@snipcodeit/mgw 0.2.2 → 0.3.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
@@ -1,6 +1,7 @@
1
1
  # MGW — My GSD Workflow
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@snipcodeit/mgw)](https://www.npmjs.com/package/@snipcodeit/mgw)
4
+ [![CI](https://github.com/snipcodeit/mgw/actions/workflows/ci.yml/badge.svg)](https://github.com/snipcodeit/mgw/actions/workflows/ci.yml)
4
5
  [![npm downloads](https://img.shields.io/npm/dm/@snipcodeit/mgw)](https://www.npmjs.com/package/@snipcodeit/mgw)
5
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
7
  [![node](https://img.shields.io/node/v/@snipcodeit/mgw)](https://nodejs.org)
@@ -341,18 +342,23 @@ bin/
341
342
  lib/
342
343
  index.cjs Barrel export
343
344
  claude.cjs Claude Code invocation helpers
344
- github.cjs GitHub CLI wrappers (issues, PRs, milestones, Projects v2)
345
+ errors.cjs Typed error hierarchy (MgwError, GitHubApiError, TimeoutError, etc.)
346
+ github.cjs Async GitHub CLI wrappers with retry/timeout (issues, PRs, milestones, Projects v2)
345
347
  gsd.cjs GSD integration
346
348
  gsd-adapter.cjs GSD route adapter (maps triage results to GSD spawn args)
347
- state.cjs .mgw/ state management (migrateProjectState, resolveActiveMilestoneIndex)
349
+ logger.cjs Structured JSON-lines execution logging (.mgw/logs/)
350
+ pipeline.cjs Pipeline stage constants, valid transitions, and transition hooks
351
+ state.cjs .mgw/ state management, cross-refs validation, dependency parsing
348
352
  output.cjs Logging and formatting
349
- retry.cjs Retry logic for GitHub API calls
353
+ progress.cjs Milestone progress display
354
+ retry.cjs Retry/backoff logic with failure classification
350
355
  templates.cjs Template system
351
356
  template-loader.cjs Output validation (JSON Schema) + parseRoadmap()
352
357
  commands/ Slash command source files (deployed to ~/.claude/commands/mgw/ at install time)
353
358
  ask.md Contextual question routing during milestone execution
354
359
  assign.md Claim/reassign issues; resolves GitHub noreply co-author tag
355
- board.md GitHub Projects v2 board management
360
+ board.md GitHub Projects v2 board dispatcher
361
+ board/ Board subcommands (create, show, configure, views, sync)
356
362
  help.md Command reference display
357
363
  init.md One-time repo bootstrap (state, templates, labels)
358
364
  project.md State-aware project init (Vision Cycle, alignment, drift, extend)
@@ -361,7 +367,8 @@ commands/ Slash command source files (deployed to ~/.claude/com
361
367
  next.md Next unblocked issue picker (surfaces failed issues as advisory)
362
368
  review.md Comment review and classification since last triage
363
369
  roadmap.md Milestone roadmap table; optional GitHub due-date setter and Discussion post
364
- run.md Autonomous pipeline orchestrator (cross-milestone enforcement)
370
+ run.md Autonomous pipeline orchestrator (dispatches to run/ stages)
371
+ run/ Pipeline stage files (triage, worktree, execute, pr-create)
365
372
  milestone.md Milestone execution with dependency ordering and failed-issue recovery
366
373
  update.md Structured GitHub comment templates
367
374
  pr.md PR creation from GSD artifacts with phase context + plan traceability
@@ -381,6 +388,49 @@ templates/
381
388
 
382
389
  For a detailed walkthrough of the directory structure, slash command anatomy, and CLI architecture, see the [Architecture Guide](docs/ARCHITECTURE.md#directory-structure).
383
390
 
391
+ ## Post-install Behavior
392
+
393
+ When you `npm install` MGW (globally or locally), the `postinstall` script (`bin/mgw-install.cjs`) copies all slash command `.md` files from `commands/` to `~/.claude/commands/mgw/`. This makes `/mgw:*` commands available in Claude Code.
394
+
395
+ To skip this behavior (e.g., in CI or Docker):
396
+
397
+ ```bash
398
+ npm install -g @snipcodeit/mgw --ignore-scripts
399
+ ```
400
+
401
+ To re-run manually:
402
+
403
+ ```bash
404
+ node ./bin/mgw-install.cjs
405
+ ```
406
+
407
+ ## CLI Commands
408
+
409
+ In addition to slash commands (used inside Claude Code), MGW provides standalone CLI commands:
410
+
411
+ ```bash
412
+ mgw issues # Browse GitHub issues
413
+ mgw sync # Reconcile .mgw/ state with GitHub
414
+ mgw link 42 43 # Cross-reference two issues
415
+ mgw log # View execution logs
416
+ mgw log --since 7d --metrics # Aggregated metrics for the last 7 days
417
+ mgw metrics # Pipeline metrics dashboard
418
+ mgw metrics --since 30d # Metrics over the last 30 days
419
+ ```
420
+
421
+ The `log` and `metrics` commands read from structured JSON-lines logs in `.mgw/logs/` that are written automatically during command execution.
422
+
423
+ ## Development
424
+
425
+ ```bash
426
+ git clone https://github.com/snipcodeit/mgw.git
427
+ cd mgw
428
+ npm install
429
+ npm test # 365 tests across 71 suites (Node.js built-in test runner)
430
+ npm run lint # ESLint
431
+ npm run build # pkgroll → dist/
432
+ ```
433
+
384
434
  ## Documentation
385
435
 
386
436
  | Document | Description |
@@ -0,0 +1,205 @@
1
+ ---
2
+ name: board:configure
3
+ description: Update board field options by comparing current state against canonical schema
4
+ ---
5
+
6
+ <step name="subcommand_configure">
7
+ **Execute 'configure' subcommand:**
8
+
9
+ Only run if `$SUBCOMMAND = "configure"`.
10
+
11
+ Reads current field options from GitHub and compares to the canonical schema in
12
+ docs/BOARD-SCHEMA.md / .mgw/board-schema.json. Adds any missing options.
13
+
14
+ ```bash
15
+ if [ "$SUBCOMMAND" = "configure" ]; then
16
+ if [ "$BOARD_CONFIGURED" = "false" ]; then
17
+ echo "No board configured. Run /mgw:board create first."
18
+ exit 1
19
+ fi
20
+
21
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
22
+ echo " MGW ► BOARD CONFIGURE: ${PROJECT_NAME}"
23
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
24
+ echo ""
25
+ echo "Board: #${BOARD_NUMBER} — ${BOARD_URL}"
26
+ echo ""
27
+ ```
28
+
29
+ **Fetch current field state from GitHub:**
30
+
31
+ ```bash
32
+ FIELDS_STATE=$(gh api graphql -f query='
33
+ query($owner: String!, $number: Int!) {
34
+ user(login: $owner) {
35
+ projectV2(number: $number) {
36
+ fields(first: 20) {
37
+ nodes {
38
+ ... on ProjectV2SingleSelectField {
39
+ id
40
+ name
41
+ options { id name color description }
42
+ }
43
+ ... on ProjectV2Field {
44
+ id
45
+ name
46
+ dataType
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ' -f owner="$OWNER" -F number="$BOARD_NUMBER" 2>/dev/null)
54
+
55
+ # Try org if user fails
56
+ if ! echo "$FIELDS_STATE" | python3 -c "import json,sys; d=json.load(sys.stdin); _ = d['data']['user']['projectV2']" 2>/dev/null; then
57
+ FIELDS_STATE=$(gh api graphql -f query='
58
+ query($owner: String!, $number: Int!) {
59
+ organization(login: $owner) {
60
+ projectV2(number: $number) {
61
+ fields(first: 20) {
62
+ nodes {
63
+ ... on ProjectV2SingleSelectField {
64
+ id
65
+ name
66
+ options { id name color description }
67
+ }
68
+ ... on ProjectV2Field {
69
+ id
70
+ name
71
+ dataType
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ' -f owner="$OWNER" -F number="$BOARD_NUMBER" 2>/dev/null)
79
+ fi
80
+
81
+ echo "Current fields on board:"
82
+ echo "$FIELDS_STATE" | python3 -c "
83
+ import json,sys
84
+ d = json.load(sys.stdin)
85
+ data = d.get('data', {})
86
+ proj = (data.get('user') or data.get('organization', {})).get('projectV2', {})
87
+ nodes = proj.get('fields', {}).get('nodes', [])
88
+ for node in nodes:
89
+ name = node.get('name', 'unknown')
90
+ nid = node.get('id', 'unknown')
91
+ opts = node.get('options')
92
+ if opts is not None:
93
+ print(f' {name} (SINGLE_SELECT, {len(opts)} options): {nid}')
94
+ for opt in opts:
95
+ print(f' - {opt[\"name\"]} ({opt[\"color\"]}) [{opt[\"id\"]}]')
96
+ else:
97
+ dtype = node.get('dataType', 'TEXT')
98
+ print(f' {name} ({dtype}): {nid}')
99
+ " 2>/dev/null || echo " (could not fetch field details)"
100
+
101
+ echo ""
102
+ ```
103
+
104
+ **Compare with canonical schema and identify missing options:**
105
+
106
+ ```bash
107
+ # Canonical Status options from BOARD-SCHEMA.md
108
+ CANONICAL_STATUS_OPTIONS='["New","Triaged","Needs Info","Needs Security Review","Discussing","Approved","Planning","Executing","Verifying","PR Created","Done","Failed","Blocked"]'
109
+
110
+ # Get current Status option names
111
+ CURRENT_STATUS_OPTIONS=$(echo "$FIELDS_STATE" | python3 -c "
112
+ import json,sys
113
+ d = json.load(sys.stdin)
114
+ data = d.get('data', {})
115
+ proj = (data.get('user') or data.get('organization', {})).get('projectV2', {})
116
+ nodes = proj.get('fields', {}).get('nodes', [])
117
+ for node in nodes:
118
+ if node.get('name') == 'Status' and 'options' in node:
119
+ print(json.dumps([o['name'] for o in node['options']]))
120
+ sys.exit(0)
121
+ print('[]')
122
+ " 2>/dev/null || echo "[]")
123
+
124
+ MISSING_STATUS=$(python3 -c "
125
+ import json
126
+ canonical = json.loads('${CANONICAL_STATUS_OPTIONS}')
127
+ current = json.loads('''${CURRENT_STATUS_OPTIONS}''')
128
+ missing = [o for o in canonical if o not in current]
129
+ if missing:
130
+ print('Missing Status options: ' + ', '.join(missing))
131
+ else:
132
+ print('Status field: all options present')
133
+ " 2>/dev/null)
134
+
135
+ echo "Schema comparison:"
136
+ echo " ${MISSING_STATUS}"
137
+
138
+ # Canonical GSD Route options
139
+ CANONICAL_GSD_OPTIONS='["quick","quick --full","plan-phase","new-milestone"]'
140
+
141
+ CURRENT_GSD_OPTIONS=$(echo "$FIELDS_STATE" | python3 -c "
142
+ import json,sys
143
+ d = json.load(sys.stdin)
144
+ data = d.get('data', {})
145
+ proj = (data.get('user') or data.get('organization', {})).get('projectV2', {})
146
+ nodes = proj.get('fields', {}).get('nodes', [])
147
+ for node in nodes:
148
+ if node.get('name') == 'GSD Route' and 'options' in node:
149
+ print(json.dumps([o['name'] for o in node['options']]))
150
+ sys.exit(0)
151
+ print('[]')
152
+ " 2>/dev/null || echo "[]")
153
+
154
+ MISSING_GSD=$(python3 -c "
155
+ import json
156
+ canonical = json.loads('${CANONICAL_GSD_OPTIONS}')
157
+ current = json.loads('''${CURRENT_GSD_OPTIONS}''')
158
+ missing = [o for o in canonical if o not in current]
159
+ if missing:
160
+ print('Missing GSD Route options: ' + ', '.join(missing))
161
+ else:
162
+ print('GSD Route field: all options present')
163
+ " 2>/dev/null)
164
+
165
+ echo " ${MISSING_GSD}"
166
+ echo ""
167
+
168
+ # Check for missing text fields
169
+ CURRENT_FIELD_NAMES=$(echo "$FIELDS_STATE" | python3 -c "
170
+ import json,sys
171
+ d = json.load(sys.stdin)
172
+ data = d.get('data', {})
173
+ proj = (data.get('user') or data.get('organization', {})).get('projectV2', {})
174
+ nodes = proj.get('fields', {}).get('nodes', [])
175
+ print(json.dumps([n.get('name') for n in nodes]))
176
+ " 2>/dev/null || echo "[]")
177
+
178
+ REQUIRED_TEXT_FIELDS='["AI Agent State","Milestone","Phase"]'
179
+ MISSING_TEXT=$(python3 -c "
180
+ import json
181
+ required = json.loads('${REQUIRED_TEXT_FIELDS}')
182
+ current = json.loads('''${CURRENT_FIELD_NAMES}''')
183
+ missing = [f for f in required if f not in current]
184
+ if missing:
185
+ print('Missing text fields: ' + ', '.join(missing))
186
+ else:
187
+ print('Text fields: all present')
188
+ " 2>/dev/null)
189
+
190
+ echo " ${MISSING_TEXT}"
191
+ echo ""
192
+
193
+ # Report: no automated field addition (GitHub Projects v2 API does not support
194
+ # updating existing single-select field options — must delete and recreate)
195
+ echo "Note: GitHub Projects v2 GraphQL does not support adding options to an"
196
+ echo "existing single-select field. To add new pipeline stages:"
197
+ echo " 1. Delete the existing Status field on the board UI"
198
+ echo " 2. Run /mgw:board create (idempotency check will be skipped for fields)"
199
+ echo " Or: manually add options via GitHub Projects UI at ${BOARD_URL}"
200
+ echo ""
201
+ echo "For missing text fields, run /mgw:board create (it will create missing fields)."
202
+
203
+ fi # end configure subcommand
204
+ ```
205
+ </step>