@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/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" 2>/dev/null
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" 2>/dev/null
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 claude = require('../claude-Dk1oVsaG.cjs');
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.1.2";
13
- var require$$8 = {
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 } = claude.requireClaude();
26
- const { log, error, formatJson, verbose } = claude.requireOutput();
27
- const { getActiveDir, getCompletedDir, getMgwDir } = claude.requireState();
28
- const { getIssue, listIssues } = claude.requireGithub();
29
- const pkg = require$$8;
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 result = await invokeClaude(cmdFile, userPrompt, {
36
- model: opts.model,
37
- quiet: opts.quiet,
38
- dryRun: opts.dryRun,
39
- json: opts.json
40
- });
41
- process.exitCode = result.exitCode;
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("List open issues").option("--label <label>", "filter by label").option("--milestone <name>", "filter by milestone").option("--assignee <user>", "filter by assignee (default: @me)", "@me").option("--state <state>", "issue state: open, closed, all (default: open)", "open").action(async function() {
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
- assignee: opts.assignee || "@me",
168
- state: opts.state || "open"
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
- const pad = (s, n) => String(s).padEnd(n);
189
- log(pad("#", 6) + pad("Title", 60) + pad("State", 10) + "Labels");
190
- log("-".repeat(90));
191
- for (const issue of issues) {
192
- const labels = (issue.labels || []).map((l) => l.name || l).join(", ");
193
- log(
194
- pad(issue.number, 6) + pad((issue.title || "").substring(0, 58), 60) + pad(issue.state || "", 10) + labels
195
- );
196
- }
197
- log("\n" + issues.length + " issue(s)");
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__*/claude.getDefaultExportFromCjs(mgwExports);
359
+ var mgw = /*@__PURE__*/index.getDefaultExportFromCjs(mgwExports);
290
360
 
291
361
  module.exports = mgw;