@snipcodeit/mgw 0.1.2 → 0.2.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/commands/init.md +115 -5
- package/commands/issue.md +9 -10
- package/commands/issues.md +63 -1
- package/commands/milestone.md +92 -7
- package/commands/run.md +34 -9
- package/commands/status.md +95 -1
- package/commands/sync.md +77 -0
- package/commands/workflows/github.md +19 -4
- package/completions/mgw.bash +112 -0
- package/completions/mgw.fish +99 -0
- package/completions/mgw.zsh +142 -0
- package/dist/bin/mgw.cjs +99 -29
- package/dist/index-CXfe9U4l.cjs +1818 -0
- package/dist/lib/index.cjs +109 -8
- package/package.json +6 -1
- package/dist/claude-Dk1oVsaG.cjs +0 -622
package/commands/sync.md
CHANGED
|
@@ -464,6 +464,81 @@ Classify each issue into:
|
|
|
464
464
|
- **Unreviewed comments:** COMMENT_DELTA > 0 — new comments posted since triage that haven't been classified
|
|
465
465
|
</step>
|
|
466
466
|
|
|
467
|
+
<step name="label_drift_detection">
|
|
468
|
+
**Label Drift Detection:**
|
|
469
|
+
|
|
470
|
+
For each active issue state file in `.mgw/active/`, compare the stored `labels` field
|
|
471
|
+
against the live GitHub labels to detect MGW pipeline label drift:
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
label_drift = []
|
|
475
|
+
|
|
476
|
+
for each state_file in .mgw/active/*.json:
|
|
477
|
+
stored_labels = state['labels'] # list stored at last pipeline write
|
|
478
|
+
live_labels_json = subprocess.run(
|
|
479
|
+
['gh', 'issue', 'view', str(issue_number), '--json', 'labels', '--jq', '[.labels[].name]'],
|
|
480
|
+
capture_output=True, text=True
|
|
481
|
+
).stdout.strip()
|
|
482
|
+
live_labels = json.loads(live_labels_json or '[]')
|
|
483
|
+
|
|
484
|
+
# Find MGW labels in stored vs live
|
|
485
|
+
stored_mgw = [l for l in stored_labels if l.startswith('mgw:')]
|
|
486
|
+
live_mgw = [l for l in live_labels if l.startswith('mgw:')]
|
|
487
|
+
|
|
488
|
+
if set(stored_mgw) != set(live_mgw):
|
|
489
|
+
label_drift.append({
|
|
490
|
+
'issue': issue_number,
|
|
491
|
+
'stored': stored_mgw,
|
|
492
|
+
'live': live_mgw,
|
|
493
|
+
'pipeline_stage': state['pipeline_stage']
|
|
494
|
+
})
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
If label drift detected, include in the drift report output:
|
|
498
|
+
|
|
499
|
+
```
|
|
500
|
+
Label drift detected for ${COUNT} issue(s):
|
|
501
|
+
#N: stored=[mgw:in-progress], live=[] — pipeline_stage=${STAGE}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Offer repair:**
|
|
505
|
+
```
|
|
506
|
+
AskUserQuestion(
|
|
507
|
+
header: "Label Drift Detected",
|
|
508
|
+
question: "Live GitHub labels don't match .mgw state for ${COUNT} issue(s). Repair?",
|
|
509
|
+
options: [
|
|
510
|
+
{ label: "Repair all", description: "Re-apply correct label for each issue's pipeline_stage via remove_mgw_labels_and_apply" },
|
|
511
|
+
{ label: "Skip", description: "Log drift only — no changes" }
|
|
512
|
+
]
|
|
513
|
+
)
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
If "Repair all":
|
|
517
|
+
```bash
|
|
518
|
+
# For each drifted issue, determine correct label from pipeline_stage and re-apply
|
|
519
|
+
CORRECT_LABEL=$(python3 -c "
|
|
520
|
+
stage_to_label = {
|
|
521
|
+
'triaged': 'mgw:triaged',
|
|
522
|
+
'needs-info': 'mgw:needs-info',
|
|
523
|
+
'needs-security-review': 'mgw:needs-security-review',
|
|
524
|
+
'discussing': 'mgw:discussing',
|
|
525
|
+
'approved': 'mgw:approved',
|
|
526
|
+
'planning': 'mgw:in-progress',
|
|
527
|
+
'executing': 'mgw:in-progress',
|
|
528
|
+
'verifying': 'mgw:in-progress',
|
|
529
|
+
'blocked': 'mgw:blocked',
|
|
530
|
+
'done': '',
|
|
531
|
+
'pr-created': '',
|
|
532
|
+
'failed': '',
|
|
533
|
+
}
|
|
534
|
+
print(stage_to_label.get('${PIPELINE_STAGE}', ''))
|
|
535
|
+
")
|
|
536
|
+
remove_mgw_labels_and_apply ${ISSUE_NUMBER} "${CORRECT_LABEL}"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Log repair actions: "Repaired label for #N: applied ${CORRECT_LABEL} (pipeline_stage=${STAGE})"
|
|
540
|
+
</step>
|
|
541
|
+
|
|
467
542
|
<step name="health_check">
|
|
468
543
|
**GSD health check (if .planning/ exists):**
|
|
469
544
|
|
|
@@ -570,5 +645,7 @@ ${gsd_milestone_consistency ? 'GSD Milestone Links:\n' + gsd_milestone_consisten
|
|
|
570
645
|
- [ ] Lingering worktrees cleaned up for completed items
|
|
571
646
|
- [ ] Branch deletion offered for completed items
|
|
572
647
|
- [ ] Stale/orphaned/drift items flagged (including comment drift and milestone inconsistencies)
|
|
648
|
+
- [ ] Label drift detected between .mgw/active labels field and live GitHub labels
|
|
649
|
+
- [ ] Repair offered for drifted issues via remove_mgw_labels_and_apply
|
|
573
650
|
- [ ] Summary presented
|
|
574
651
|
</success_criteria>
|
|
@@ -95,12 +95,15 @@ Seven labels for pipeline stage tracking. Created by init.md, managed by issue.m
|
|
|
95
95
|
| `mgw:blocked` | `b60205` | Pipeline blocked by stakeholder comment |
|
|
96
96
|
|
|
97
97
|
### Remove MGW Labels and Apply New
|
|
98
|
-
Used when transitioning pipeline stages. Removes all `mgw:*` pipeline labels, then applies the target label.
|
|
98
|
+
Used when transitioning pipeline stages. Removes all `mgw:*` pipeline labels, then applies the target label. Returns non-zero if any gh operation failed.
|
|
99
99
|
```bash
|
|
100
|
-
# Remove all mgw: pipeline labels from issue, then apply new one
|
|
100
|
+
# Remove all mgw: pipeline labels from issue, then apply new one.
|
|
101
|
+
# Pass empty string as NEW_LABEL to remove all MGW labels without applying a new one.
|
|
102
|
+
# Returns non-zero if any label operation failed.
|
|
101
103
|
remove_mgw_labels_and_apply() {
|
|
102
104
|
local ISSUE_NUMBER="$1"
|
|
103
105
|
local NEW_LABEL="$2"
|
|
106
|
+
local LABEL_FAILED=0
|
|
104
107
|
|
|
105
108
|
# Get current labels
|
|
106
109
|
CURRENT_LABELS=$(gh issue view "$ISSUE_NUMBER" --json labels --jq '.labels[].name' 2>/dev/null)
|
|
@@ -109,15 +112,27 @@ remove_mgw_labels_and_apply() {
|
|
|
109
112
|
for LABEL in $CURRENT_LABELS; do
|
|
110
113
|
case "$LABEL" in
|
|
111
114
|
mgw:triaged|mgw:needs-info|mgw:needs-security-review|mgw:discussing|mgw:approved|mgw:in-progress|mgw:blocked)
|
|
112
|
-
gh issue edit "$ISSUE_NUMBER" --remove-label "$LABEL"
|
|
115
|
+
gh issue edit "$ISSUE_NUMBER" --remove-label "$LABEL"
|
|
116
|
+
RC=$?
|
|
117
|
+
if [ $RC -ne 0 ]; then
|
|
118
|
+
echo "MGW WARNING: failed to remove label $LABEL from issue $ISSUE_NUMBER (exit $RC)" >&2
|
|
119
|
+
LABEL_FAILED=1
|
|
120
|
+
fi
|
|
113
121
|
;;
|
|
114
122
|
esac
|
|
115
123
|
done
|
|
116
124
|
|
|
117
125
|
# Apply new label
|
|
118
126
|
if [ -n "$NEW_LABEL" ]; then
|
|
119
|
-
gh issue edit "$ISSUE_NUMBER" --add-label "$NEW_LABEL"
|
|
127
|
+
gh issue edit "$ISSUE_NUMBER" --add-label "$NEW_LABEL"
|
|
128
|
+
RC=$?
|
|
129
|
+
if [ $RC -ne 0 ]; then
|
|
130
|
+
echo "MGW WARNING: failed to add label $NEW_LABEL to issue $ISSUE_NUMBER (exit $RC)" >&2
|
|
131
|
+
LABEL_FAILED=1
|
|
132
|
+
fi
|
|
120
133
|
fi
|
|
134
|
+
|
|
135
|
+
return $LABEL_FAILED
|
|
121
136
|
}
|
|
122
137
|
```
|
|
123
138
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# mgw bash completion script
|
|
3
|
+
# Source this file or add to /etc/bash_completion.d/mgw
|
|
4
|
+
# Generated by bin/generate-completions.cjs
|
|
5
|
+
|
|
6
|
+
_mgw() {
|
|
7
|
+
local cur prev words cword
|
|
8
|
+
_init_completion 2>/dev/null || {
|
|
9
|
+
COMPREPLY=()
|
|
10
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
11
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
local subcommands="run init project milestone next issue update pr sync issues link help"
|
|
15
|
+
|
|
16
|
+
local global_flags="--dry-run --json --verbose -v --debug --model"
|
|
17
|
+
|
|
18
|
+
case "$prev" in
|
|
19
|
+
mgw)
|
|
20
|
+
COMPREPLY=( $(compgen -W "$subcommands $global_flags" -- "$cur") )
|
|
21
|
+
return
|
|
22
|
+
;;
|
|
23
|
+
run)
|
|
24
|
+
COMPREPLY=( $(compgen -W "--quiet --auto $global_flags" -- "$cur") )
|
|
25
|
+
return
|
|
26
|
+
;;
|
|
27
|
+
init)
|
|
28
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
29
|
+
return
|
|
30
|
+
;;
|
|
31
|
+
project)
|
|
32
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
33
|
+
return
|
|
34
|
+
;;
|
|
35
|
+
milestone)
|
|
36
|
+
COMPREPLY=( $(compgen -W "--interactive $global_flags" -- "$cur") )
|
|
37
|
+
return
|
|
38
|
+
;;
|
|
39
|
+
next)
|
|
40
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
41
|
+
return
|
|
42
|
+
;;
|
|
43
|
+
issue)
|
|
44
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
45
|
+
return
|
|
46
|
+
;;
|
|
47
|
+
update)
|
|
48
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
49
|
+
return
|
|
50
|
+
;;
|
|
51
|
+
pr)
|
|
52
|
+
COMPREPLY=( $(compgen -W "--base $global_flags" -- "$cur") )
|
|
53
|
+
return
|
|
54
|
+
;;
|
|
55
|
+
sync)
|
|
56
|
+
COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") )
|
|
57
|
+
return
|
|
58
|
+
;;
|
|
59
|
+
issues)
|
|
60
|
+
COMPREPLY=( $(compgen -W "--label --milestone --assignee --state --search -s --limit $global_flags" -- "$cur") )
|
|
61
|
+
return
|
|
62
|
+
;;
|
|
63
|
+
link)
|
|
64
|
+
COMPREPLY=( $(compgen -W "--quiet $global_flags" -- "$cur") )
|
|
65
|
+
return
|
|
66
|
+
;;
|
|
67
|
+
help)
|
|
68
|
+
COMPREPLY=()
|
|
69
|
+
return
|
|
70
|
+
;;
|
|
71
|
+
--model)
|
|
72
|
+
# Offer common Claude model names
|
|
73
|
+
COMPREPLY=( $(compgen -W "claude-opus-4-5 claude-sonnet-4-5 claude-haiku-4-5" -- "$cur") )
|
|
74
|
+
return
|
|
75
|
+
;;
|
|
76
|
+
--state)
|
|
77
|
+
COMPREPLY=( $(compgen -W "open closed all" -- "$cur") )
|
|
78
|
+
return
|
|
79
|
+
;;
|
|
80
|
+
--assignee)
|
|
81
|
+
COMPREPLY=( $(compgen -W "all @me" -- "$cur") )
|
|
82
|
+
return
|
|
83
|
+
;;
|
|
84
|
+
esac
|
|
85
|
+
|
|
86
|
+
# If cur starts with - complete flags for the current subcommand
|
|
87
|
+
if [[ "$cur" == -* ]]; then
|
|
88
|
+
# Find the subcommand in words
|
|
89
|
+
local subcmd=""
|
|
90
|
+
for word in "${words[@]}"; do
|
|
91
|
+
if [[ " $subcommands " == *" $word "* ]]; then
|
|
92
|
+
subcmd="$word"
|
|
93
|
+
break
|
|
94
|
+
fi
|
|
95
|
+
done
|
|
96
|
+
|
|
97
|
+
case "$subcmd" in
|
|
98
|
+
run) COMPREPLY=( $(compgen -W "--quiet --auto $global_flags" -- "$cur") ) ;;
|
|
99
|
+
milestone) COMPREPLY=( $(compgen -W "--interactive $global_flags" -- "$cur") ) ;;
|
|
100
|
+
pr) COMPREPLY=( $(compgen -W "--base $global_flags" -- "$cur") ) ;;
|
|
101
|
+
issues) COMPREPLY=( $(compgen -W "--label --milestone --assignee --state --search -s --limit $global_flags" -- "$cur") ) ;;
|
|
102
|
+
link) COMPREPLY=( $(compgen -W "--quiet $global_flags" -- "$cur") ) ;;
|
|
103
|
+
*) COMPREPLY=( $(compgen -W "$global_flags" -- "$cur") ) ;;
|
|
104
|
+
esac
|
|
105
|
+
return
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Default: suggest subcommands
|
|
109
|
+
COMPREPLY=( $(compgen -W "$subcommands" -- "$cur") )
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
complete -F _mgw mgw
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# mgw fish completion script
|
|
2
|
+
# Place in ~/.config/fish/completions/mgw.fish
|
|
3
|
+
# or run: fisher install (if using a fish plugin manager)
|
|
4
|
+
# Generated by bin/generate-completions.cjs
|
|
5
|
+
|
|
6
|
+
# Disable file completions by default for mgw
|
|
7
|
+
complete -c mgw -f
|
|
8
|
+
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
# Global flags (available for all subcommands)
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
complete -c mgw -n '__fish_use_subcommand' -l dry-run -d 'show what would happen without executing'
|
|
13
|
+
complete -c mgw -n '__fish_use_subcommand' -l json -d 'output structured JSON'
|
|
14
|
+
complete -c mgw -n '__fish_use_subcommand' -s v -l verbose -d 'show API calls and file writes'
|
|
15
|
+
complete -c mgw -n '__fish_use_subcommand' -l debug -d 'full payloads and timings'
|
|
16
|
+
complete -c mgw -n '__fish_use_subcommand' -l model -d 'Claude model override' -r
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Subcommands
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
complete -c mgw -n '__fish_use_subcommand' -a run -d 'Run the full pipeline for an issue'
|
|
22
|
+
complete -c mgw -n '__fish_use_subcommand' -a init -d 'Bootstrap repo for MGW (state, templates, labels)'
|
|
23
|
+
complete -c mgw -n '__fish_use_subcommand' -a project -d 'Initialize project from template (milestones, issues, ROADMAP)'
|
|
24
|
+
complete -c mgw -n '__fish_use_subcommand' -a milestone -d 'Execute milestone issues in dependency order'
|
|
25
|
+
complete -c mgw -n '__fish_use_subcommand' -a next -d 'Show next unblocked issue'
|
|
26
|
+
complete -c mgw -n '__fish_use_subcommand' -a issue -d 'Triage issue against codebase'
|
|
27
|
+
complete -c mgw -n '__fish_use_subcommand' -a update -d 'Post status comment on issue'
|
|
28
|
+
complete -c mgw -n '__fish_use_subcommand' -a pr -d 'Create PR from GSD artifacts'
|
|
29
|
+
complete -c mgw -n '__fish_use_subcommand' -a sync -d 'Reconcile .mgw/ state with GitHub'
|
|
30
|
+
complete -c mgw -n '__fish_use_subcommand' -a issues -d 'Browse open issues'
|
|
31
|
+
complete -c mgw -n '__fish_use_subcommand' -a link -d 'Cross-reference issues/PRs/branches'
|
|
32
|
+
complete -c mgw -n '__fish_use_subcommand' -a help -d 'Show command reference'
|
|
33
|
+
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# run <issue-number>
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l quiet -d 'buffer output, show summary at end'
|
|
38
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l auto -d 'phase chaining: discuss -> plan -> execute'
|
|
39
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l dry-run -d 'show what would happen without executing'
|
|
40
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l json -d 'output structured JSON'
|
|
41
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -s v -l verbose -d 'show API calls and file writes'
|
|
42
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l debug -d 'full payloads and timings'
|
|
43
|
+
complete -c mgw -n '__fish_seen_subcommand_from run' -l model -d 'Claude model override' -r
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# milestone [number]
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -l interactive -d 'pause between issues for review'
|
|
49
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -l dry-run -d 'show what would happen without executing'
|
|
50
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -l json -d 'output structured JSON'
|
|
51
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -s v -l verbose -d 'show API calls and file writes'
|
|
52
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -l debug -d 'full payloads and timings'
|
|
53
|
+
complete -c mgw -n '__fish_seen_subcommand_from milestone' -l model -d 'Claude model override' -r
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# pr [number]
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -l base -d 'custom base branch' -r
|
|
59
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -l dry-run -d 'show what would happen without executing'
|
|
60
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -l json -d 'output structured JSON'
|
|
61
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -s v -l verbose -d 'show API calls and file writes'
|
|
62
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -l debug -d 'full payloads and timings'
|
|
63
|
+
complete -c mgw -n '__fish_seen_subcommand_from pr' -l model -d 'Claude model override' -r
|
|
64
|
+
|
|
65
|
+
# ---------------------------------------------------------------------------
|
|
66
|
+
# issues [filters]
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l label -d 'filter by label' -r
|
|
69
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l milestone -d 'filter by milestone' -r
|
|
70
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l assignee -d 'filter by assignee' -r -a 'all @me'
|
|
71
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l state -d 'issue state' -r -a 'open closed all'
|
|
72
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -s s -l search -d 'pre-populate fuzzy search input' -r
|
|
73
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l limit -d 'max issues to load' -r -a '10 25 50 100'
|
|
74
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l dry-run -d 'show what would happen without executing'
|
|
75
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l json -d 'output structured JSON'
|
|
76
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -s v -l verbose -d 'show API calls and file writes'
|
|
77
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l debug -d 'full payloads and timings'
|
|
78
|
+
complete -c mgw -n '__fish_seen_subcommand_from issues' -l model -d 'Claude model override' -r
|
|
79
|
+
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# link <ref-a> <ref-b>
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -l quiet -d 'no GitHub comments'
|
|
84
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -l dry-run -d 'show what would happen without executing'
|
|
85
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -l json -d 'output structured JSON'
|
|
86
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -s v -l verbose -d 'show API calls and file writes'
|
|
87
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -l debug -d 'full payloads and timings'
|
|
88
|
+
complete -c mgw -n '__fish_seen_subcommand_from link' -l model -d 'Claude model override' -r
|
|
89
|
+
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
# Subcommands that only take global flags (no subcommand-specific flags)
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
for _mgw_cmd in init project next issue update sync help
|
|
94
|
+
complete -c mgw -n "__fish_seen_subcommand_from $_mgw_cmd" -l dry-run -d 'show what would happen without executing'
|
|
95
|
+
complete -c mgw -n "__fish_seen_subcommand_from $_mgw_cmd" -l json -d 'output structured JSON'
|
|
96
|
+
complete -c mgw -n "__fish_seen_subcommand_from $_mgw_cmd" -s v -l verbose -d 'show API calls and file writes'
|
|
97
|
+
complete -c mgw -n "__fish_seen_subcommand_from $_mgw_cmd" -l debug -d 'full payloads and timings'
|
|
98
|
+
complete -c mgw -n "__fish_seen_subcommand_from $_mgw_cmd" -l model -d 'Claude model override' -r
|
|
99
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#compdef mgw
|
|
2
|
+
# mgw zsh completion script
|
|
3
|
+
# Place in a directory on your $fpath (e.g. /usr/local/share/zsh/site-functions/_mgw)
|
|
4
|
+
# or source directly: source completions/mgw.zsh
|
|
5
|
+
# Generated by bin/generate-completions.cjs
|
|
6
|
+
|
|
7
|
+
_mgw() {
|
|
8
|
+
local context state state_descr line
|
|
9
|
+
typeset -A opt_args
|
|
10
|
+
|
|
11
|
+
local -a global_flags
|
|
12
|
+
global_flags=(
|
|
13
|
+
'--dry-run[show what would happen without executing]'
|
|
14
|
+
'--json[output structured JSON]'
|
|
15
|
+
'(-v --verbose)'{-v,--verbose}'[show API calls and file writes]'
|
|
16
|
+
'--debug[full payloads and timings]'
|
|
17
|
+
'--model[Claude model override]:model:(claude-opus-4-5 claude-sonnet-4-5 claude-haiku-4-5)'
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_arguments -C \
|
|
21
|
+
"${global_flags[@]}" \
|
|
22
|
+
'(-h --help)'{-h,--help}'[display help]' \
|
|
23
|
+
'(-V --version)'{-V,--version}'[output the version number]' \
|
|
24
|
+
'1: :_mgw_subcommands' \
|
|
25
|
+
'*:: :->subcommand_args' \
|
|
26
|
+
&& return 0
|
|
27
|
+
|
|
28
|
+
case "$state" in
|
|
29
|
+
subcommand_args)
|
|
30
|
+
case "$words[1]" in
|
|
31
|
+
run)
|
|
32
|
+
_arguments \
|
|
33
|
+
"${global_flags[@]}" \
|
|
34
|
+
'--quiet[buffer output, show summary at end]' \
|
|
35
|
+
'--auto[phase chaining: discuss -> plan -> execute]' \
|
|
36
|
+
'1:issue-number:_mgw_issue_numbers' \
|
|
37
|
+
&& return 0
|
|
38
|
+
;;
|
|
39
|
+
init)
|
|
40
|
+
_arguments "${global_flags[@]}" && return 0
|
|
41
|
+
;;
|
|
42
|
+
project)
|
|
43
|
+
_arguments "${global_flags[@]}" && return 0
|
|
44
|
+
;;
|
|
45
|
+
milestone)
|
|
46
|
+
_arguments \
|
|
47
|
+
"${global_flags[@]}" \
|
|
48
|
+
'--interactive[pause between issues for review]' \
|
|
49
|
+
'1::milestone-number:' \
|
|
50
|
+
&& return 0
|
|
51
|
+
;;
|
|
52
|
+
next)
|
|
53
|
+
_arguments "${global_flags[@]}" && return 0
|
|
54
|
+
;;
|
|
55
|
+
issue)
|
|
56
|
+
_arguments \
|
|
57
|
+
"${global_flags[@]}" \
|
|
58
|
+
'1:issue-number:_mgw_issue_numbers' \
|
|
59
|
+
&& return 0
|
|
60
|
+
;;
|
|
61
|
+
update)
|
|
62
|
+
_arguments \
|
|
63
|
+
"${global_flags[@]}" \
|
|
64
|
+
'1:issue-number:_mgw_issue_numbers' \
|
|
65
|
+
'2::message:' \
|
|
66
|
+
&& return 0
|
|
67
|
+
;;
|
|
68
|
+
pr)
|
|
69
|
+
_arguments \
|
|
70
|
+
"${global_flags[@]}" \
|
|
71
|
+
'--base[custom base branch]:branch:_git_branch_names' \
|
|
72
|
+
'1::issue-number:_mgw_issue_numbers' \
|
|
73
|
+
&& return 0
|
|
74
|
+
;;
|
|
75
|
+
sync)
|
|
76
|
+
_arguments "${global_flags[@]}" && return 0
|
|
77
|
+
;;
|
|
78
|
+
issues)
|
|
79
|
+
_arguments \
|
|
80
|
+
"${global_flags[@]}" \
|
|
81
|
+
'--label[filter by label]:label:' \
|
|
82
|
+
'--milestone[filter by milestone]:milestone:' \
|
|
83
|
+
'--assignee[filter by assignee]:assignee:(all @me)' \
|
|
84
|
+
'--state[issue state]:state:(open closed all)' \
|
|
85
|
+
'(-s --search)'{-s,--search}'[pre-populate fuzzy search input]:query:' \
|
|
86
|
+
'--limit[max issues to load]:number:(10 25 50 100)' \
|
|
87
|
+
&& return 0
|
|
88
|
+
;;
|
|
89
|
+
link)
|
|
90
|
+
_arguments \
|
|
91
|
+
"${global_flags[@]}" \
|
|
92
|
+
'--quiet[no GitHub comments]' \
|
|
93
|
+
'1:ref-a:' \
|
|
94
|
+
'2:ref-b:' \
|
|
95
|
+
&& return 0
|
|
96
|
+
;;
|
|
97
|
+
help)
|
|
98
|
+
return 0
|
|
99
|
+
;;
|
|
100
|
+
esac
|
|
101
|
+
;;
|
|
102
|
+
esac
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
_mgw_subcommands() {
|
|
106
|
+
local -a subcommands
|
|
107
|
+
subcommands=(
|
|
108
|
+
'run:Run the full pipeline for an issue'
|
|
109
|
+
'init:Bootstrap repo for MGW (state, templates, labels)'
|
|
110
|
+
'project:Initialize project from template (milestones, issues, ROADMAP)'
|
|
111
|
+
'milestone:Execute milestone issues in dependency order'
|
|
112
|
+
'next:Show next unblocked issue'
|
|
113
|
+
'issue:Triage issue against codebase'
|
|
114
|
+
'update:Post status comment on issue'
|
|
115
|
+
'pr:Create PR from GSD artifacts'
|
|
116
|
+
'sync:Reconcile .mgw/ state with GitHub'
|
|
117
|
+
'issues:Browse open issues'
|
|
118
|
+
'link:Cross-reference issues/PRs/branches'
|
|
119
|
+
'help:Show command reference'
|
|
120
|
+
)
|
|
121
|
+
_describe 'mgw subcommand' subcommands
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_mgw_issue_numbers() {
|
|
125
|
+
# Attempt to complete issue numbers from active .mgw/ state
|
|
126
|
+
local -a active_issues
|
|
127
|
+
local active_dir=".mgw/active"
|
|
128
|
+
if [[ -d "$active_dir" ]]; then
|
|
129
|
+
for f in "$active_dir"/*.json; do
|
|
130
|
+
[[ -f "$f" ]] || continue
|
|
131
|
+
local num="${${f:t}%%[-_]*}"
|
|
132
|
+
[[ "$num" =~ ^[0-9]+$ ]] && active_issues+=("$num")
|
|
133
|
+
done
|
|
134
|
+
fi
|
|
135
|
+
if (( ${#active_issues[@]} > 0 )); then
|
|
136
|
+
_describe 'issue number' active_issues
|
|
137
|
+
else
|
|
138
|
+
_message 'issue number'
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_mgw "$@"
|
package/dist/bin/mgw.cjs
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var index = require('../index-CXfe9U4l.cjs');
|
|
5
5
|
var require$$0 = require('commander');
|
|
6
6
|
var require$$1 = require('path');
|
|
7
7
|
var require$$0$2 = require('fs');
|
|
8
8
|
var require$$0$1 = require('child_process');
|
|
9
|
+
require('events');
|
|
9
10
|
|
|
10
11
|
var mgw$1 = {};
|
|
11
12
|
|
|
12
|
-
var version = "0.
|
|
13
|
-
var require$$
|
|
13
|
+
var version = "0.2.0";
|
|
14
|
+
var require$$10 = {
|
|
14
15
|
version: version};
|
|
15
16
|
|
|
16
17
|
var hasRequiredMgw;
|
|
@@ -22,26 +23,85 @@ function requireMgw () {
|
|
|
22
23
|
const path = require$$1;
|
|
23
24
|
const fs = require$$0$2;
|
|
24
25
|
const { execSync } = require$$0$1;
|
|
25
|
-
const { assertClaudeAvailable, invokeClaude, getCommandsDir } =
|
|
26
|
-
const { log, error, formatJson, verbose } =
|
|
27
|
-
const { getActiveDir, getCompletedDir, getMgwDir } =
|
|
28
|
-
const { getIssue, listIssues } =
|
|
29
|
-
const
|
|
26
|
+
const { assertClaudeAvailable, invokeClaude, getCommandsDir } = index.requireClaude();
|
|
27
|
+
const { log, error, formatJson, verbose } = index.requireOutput();
|
|
28
|
+
const { getActiveDir, getCompletedDir, getMgwDir } = index.requireState();
|
|
29
|
+
const { getIssue, listIssues } = index.requireGithub();
|
|
30
|
+
const { createIssuesBrowser } = index.requireTui();
|
|
31
|
+
const { createSpinner } = index.requireSpinner();
|
|
32
|
+
const pkg = require$$10;
|
|
30
33
|
const program = new Command();
|
|
31
34
|
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");
|
|
35
|
+
const STAGE_LABELS = {
|
|
36
|
+
run: "pipeline",
|
|
37
|
+
init: "init",
|
|
38
|
+
project: "project",
|
|
39
|
+
milestone: "milestone",
|
|
40
|
+
issue: "triage",
|
|
41
|
+
pr: "create-pr",
|
|
42
|
+
update: "update",
|
|
43
|
+
next: "next"
|
|
44
|
+
};
|
|
32
45
|
async function runAiCommand(commandName, userPrompt, opts) {
|
|
33
46
|
assertClaudeAvailable();
|
|
34
47
|
const cmdFile = path.join(getCommandsDir(), `${commandName}.md`);
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
48
|
+
const stageLabel = STAGE_LABELS[commandName] || commandName;
|
|
49
|
+
if (opts.quiet) {
|
|
50
|
+
const spinner = createSpinner(`mgw:${stageLabel}`);
|
|
51
|
+
spinner.start();
|
|
52
|
+
let result;
|
|
53
|
+
try {
|
|
54
|
+
result = await invokeClaude(cmdFile, userPrompt, {
|
|
55
|
+
model: opts.model,
|
|
56
|
+
quiet: true,
|
|
57
|
+
dryRun: opts.dryRun,
|
|
58
|
+
json: opts.json
|
|
59
|
+
});
|
|
60
|
+
} catch (err) {
|
|
61
|
+
spinner.fail(`${stageLabel} failed`);
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
if (result.exitCode === 0) {
|
|
65
|
+
spinner.succeed(`${stageLabel} complete`);
|
|
66
|
+
} else {
|
|
67
|
+
spinner.fail(`${stageLabel} failed (exit ${result.exitCode})`);
|
|
68
|
+
}
|
|
69
|
+
if (result.output) {
|
|
70
|
+
process.stdout.write(result.output);
|
|
71
|
+
}
|
|
72
|
+
process.exitCode = result.exitCode;
|
|
73
|
+
} else {
|
|
74
|
+
const spinner = createSpinner(`mgw:${stageLabel}`);
|
|
75
|
+
spinner.start();
|
|
76
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
77
|
+
spinner.stop();
|
|
78
|
+
const result = await invokeClaude(cmdFile, userPrompt, {
|
|
79
|
+
model: opts.model,
|
|
80
|
+
quiet: false,
|
|
81
|
+
dryRun: opts.dryRun,
|
|
82
|
+
json: opts.json
|
|
83
|
+
});
|
|
84
|
+
process.exitCode = result.exitCode;
|
|
85
|
+
}
|
|
42
86
|
}
|
|
87
|
+
const RUN_PIPELINE_STAGES = [
|
|
88
|
+
"validate",
|
|
89
|
+
"triage",
|
|
90
|
+
"create-worktree",
|
|
91
|
+
"execute-gsd",
|
|
92
|
+
"create-pr"
|
|
93
|
+
];
|
|
43
94
|
program.command("run <issue-number>").description("Run the full pipeline for an issue").option("--quiet", "buffer output, show summary at end").option("--auto", "phase chaining: discuss -> plan -> execute").action(async function(issueNumber) {
|
|
44
95
|
const opts = this.optsWithGlobals();
|
|
96
|
+
if (!opts.json) {
|
|
97
|
+
const { USE_COLOR, COLORS } = index.requireOutput();
|
|
98
|
+
const dim = USE_COLOR ? COLORS.dim : "";
|
|
99
|
+
const reset = USE_COLOR ? COLORS.reset : "";
|
|
100
|
+
const bold = USE_COLOR ? COLORS.bold : "";
|
|
101
|
+
const stages = RUN_PIPELINE_STAGES.join(` ${dim}\u2192${reset} `);
|
|
102
|
+
process.stdout.write(`${bold}mgw:run${reset} #${issueNumber} ${dim}${stages}${reset}
|
|
103
|
+
`);
|
|
104
|
+
}
|
|
45
105
|
await runAiCommand("run", issueNumber, opts);
|
|
46
106
|
});
|
|
47
107
|
program.command("init").description("Bootstrap repo for MGW (state, templates, labels)").action(async function() {
|
|
@@ -161,12 +221,15 @@ function requireMgw () {
|
|
|
161
221
|
log(`sync complete: ${ok} up-to-date, ${archived} archived`);
|
|
162
222
|
}
|
|
163
223
|
});
|
|
164
|
-
program.command("issues [filters...]").description("
|
|
224
|
+
program.command("issues [filters...]").description("Browse open issues \u2014 interactive TUI in TTY, static table otherwise").option("--label <label>", "filter by label").option("--milestone <name>", "filter by milestone").option("--assignee <user>", 'filter by assignee ("all" = no filter, default: all)', "all").option("--state <state>", "issue state: open, closed, all (default: open)", "open").option("-s, --search <query>", "pre-populate fuzzy search input").option("--limit <n>", "max issues to load (default: 50)", "50").action(async function() {
|
|
165
225
|
const opts = this.optsWithGlobals();
|
|
166
226
|
const ghFilters = {
|
|
167
|
-
|
|
168
|
-
|
|
227
|
+
state: opts.state || "open",
|
|
228
|
+
limit: parseInt(opts.limit, 10) || 50
|
|
169
229
|
};
|
|
230
|
+
if (opts.assignee && opts.assignee !== "all") {
|
|
231
|
+
ghFilters.assignee = opts.assignee;
|
|
232
|
+
}
|
|
170
233
|
if (opts.label) ghFilters.label = opts.label;
|
|
171
234
|
if (opts.milestone) ghFilters.milestone = opts.milestone;
|
|
172
235
|
let issues;
|
|
@@ -185,16 +248,23 @@ function requireMgw () {
|
|
|
185
248
|
log("No issues found.");
|
|
186
249
|
return;
|
|
187
250
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
|
|
251
|
+
await createIssuesBrowser({
|
|
252
|
+
issues,
|
|
253
|
+
onSelect: function(issue) {
|
|
254
|
+
log("\nSelected: #" + issue.number + " \u2014 " + issue.title);
|
|
255
|
+
log("Run: mgw issue " + issue.number);
|
|
256
|
+
process.exit(0);
|
|
257
|
+
},
|
|
258
|
+
onQuit: function() {
|
|
259
|
+
process.exit(0);
|
|
260
|
+
},
|
|
261
|
+
initialQuery: opts.search || "",
|
|
262
|
+
initialFilter: {
|
|
263
|
+
label: opts.label,
|
|
264
|
+
milestone: opts.milestone,
|
|
265
|
+
assignee: opts.assignee || "all"
|
|
266
|
+
}
|
|
267
|
+
});
|
|
198
268
|
});
|
|
199
269
|
program.command("link <ref-a> <ref-b>").description("Cross-reference issues/PRs/branches").option("--quiet", "no GitHub comments").action(async function(refA, refB) {
|
|
200
270
|
const opts = this.optsWithGlobals();
|
|
@@ -286,6 +356,6 @@ function requireMgw () {
|
|
|
286
356
|
}
|
|
287
357
|
|
|
288
358
|
var mgwExports = requireMgw();
|
|
289
|
-
var mgw = /*@__PURE__*/
|
|
359
|
+
var mgw = /*@__PURE__*/index.getDefaultExportFromCjs(mgwExports);
|
|
290
360
|
|
|
291
361
|
module.exports = mgw;
|