@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 +55 -5
- package/commands/board/configure.md +205 -0
- package/commands/board/create.md +496 -0
- package/commands/board/show.md +221 -0
- package/commands/board/sync.md +417 -0
- package/commands/board/views.md +230 -0
- package/commands/board.md +23 -1543
- package/commands/milestone.md +5 -38
- package/commands/review.md +222 -42
- package/commands/run/execute.md +675 -0
- package/commands/run/pr-create.md +282 -0
- package/commands/run/triage.md +510 -0
- package/commands/run/worktree.md +54 -0
- package/commands/run.md +23 -1547
- package/commands/workflows/gsd.md +1 -13
- package/dist/bin/mgw.cjs +95 -6
- package/dist/{index-BiwU0uWA.cjs → index-s7v-ifd0.cjs} +669 -48
- package/dist/lib/index.cjs +185 -142
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# MGW — My GSD Workflow
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@snipcodeit/mgw)
|
|
4
|
+
[](https://github.com/snipcodeit/mgw/actions/workflows/ci.yml)
|
|
4
5
|
[](https://www.npmjs.com/package/@snipcodeit/mgw)
|
|
5
6
|
[](https://opensource.org/licenses/MIT)
|
|
6
7
|
[](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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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>
|