@snipcodeit/mgw 0.1.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mgw:init
3
- description: Bootstrap current repo for MGW integration — creates .mgw/ state, GitHub templates, gitignore entries
4
- argument-hint: ""
3
+ description: Bootstrap current repo for MGW integration — creates .mgw/ state, GitHub templates, gitignore entries, installs shell completions, and runs config wizard
4
+ argument-hint: "[--no-config]"
5
5
  allowed-tools:
6
6
  - Bash
7
7
  - Read
@@ -12,8 +12,9 @@ allowed-tools:
12
12
 
13
13
  <objective>
14
14
  One-time setup for a repo to work with MGW. Creates the .mgw/ state directory,
15
- GitHub issue/PR templates, and ensures gitignore entries. Safe to re-run — skips
16
- anything that already exists.
15
+ GitHub issue/PR templates, ensures gitignore entries, and runs an interactive
16
+ config wizard for first-time setup preferences. Safe to re-run — skips anything
17
+ that already exists. Pass --no-config to skip the wizard.
17
18
  </objective>
18
19
 
19
20
  <execution_context>
@@ -213,6 +214,109 @@ gh label create "mgw:blocked" --description "Pipeline blocked by stakeholder com
213
214
  `--force` updates existing labels without error.
214
215
  </step>
215
216
 
217
+ <step name="install_completions">
218
+ **Offer to install shell completions (opt-in):**
219
+
220
+ Locate the completions directory bundled with the MGW package:
221
+ ```bash
222
+ MGW_PKG_DIR=$(node -e "const path = require('path'); console.log(path.resolve(__dirname, '..', '..'))" 2>/dev/null || echo "")
223
+ COMPLETIONS_DIR="${MGW_PKG_DIR}/completions"
224
+ ```
225
+
226
+ If completions are not found (package not globally installed or completions dir absent) → skip silently, note "Shell completions not available (mgw not installed globally)" in report.
227
+
228
+ If completions are found, detect the user's shell and determine the install target:
229
+ ```bash
230
+ CURRENT_SHELL=$(basename "${SHELL:-}")
231
+ ```
232
+
233
+ Shell → target directory mapping:
234
+ - `bash` → `~/.local/share/bash-completion/completions/` (source file: `mgw.bash`)
235
+ - `zsh` → `~/.zsh/completions/` (source file: `mgw.zsh`)
236
+ - `fish` → `~/.config/fish/completions/` (source file: `mgw.fish`)
237
+
238
+ If shell is unrecognized or `SHELL` is unset → show all three install commands and skip auto-install.
239
+
240
+ **Interactive mode (default):** Ask the user:
241
+ ```
242
+ Shell completions are available for ${CURRENT_SHELL}.
243
+
244
+ Install to ${COMPLETION_TARGET_DIR}? [Y/n]
245
+ ```
246
+
247
+ If the user answers yes (or presses Enter for the default):
248
+ ```bash
249
+ mkdir -p "${COMPLETION_TARGET_DIR}"
250
+ cp "${COMPLETIONS_DIR}/mgw.${SHELL_EXT}" "${COMPLETION_TARGET_DIR}/mgw.${SHELL_EXT}"
251
+ ```
252
+
253
+ Then show the source/activation line appropriate for the shell:
254
+ - bash: `# Reload with: source ~/.local/share/bash-completion/completions/mgw.bash`
255
+ (or add to ~/.bashrc: `source ~/.local/share/bash-completion/completions/mgw.bash`)
256
+ - zsh: Add to ~/.zshrc (required — ~/.zsh/completions is not in default $fpath):
257
+ ```
258
+ fpath=(~/.zsh/completions $fpath)
259
+ autoload -Uz compinit
260
+ compinit
261
+ ```
262
+ Then reload: `source ~/.zshrc`
263
+ - fish: `# Completions loaded automatically from ~/.config/fish/completions/`
264
+
265
+ If user answers no → skip, note "Shell completions: skipped" in report.
266
+
267
+ If the completion file already exists at the target → overwrite (idempotent re-run).
268
+
269
+ **Non-interactive mode** (stdin is not a TTY): Skip the prompt entirely, print the install command as a hint:
270
+ ```
271
+ Shell completions available. Install manually:
272
+ cp ${COMPLETIONS_DIR}/mgw.${SHELL_EXT} ${COMPLETION_TARGET_DIR}/mgw.${SHELL_EXT}
273
+ ```
274
+ </step>
275
+
276
+ <step name="run_config_wizard">
277
+ **Run interactive config wizard for first-time setup (skip if --no-config or config already exists):**
278
+
279
+ Check whether the wizard should run:
280
+ ```bash
281
+ # Skip if --no-config flag is present
282
+ # Skip if stdin is not a TTY (CI/piped context)
283
+ # Skip if .mgw/config.json already exists
284
+ ```
285
+
286
+ Use `lib/config-wizard.cjs` to determine and execute:
287
+ ```javascript
288
+ const { runWizard, shouldRunWizard } = require('./lib/config-wizard.cjs');
289
+ const mgwDir = path.join(REPO_ROOT, '.mgw');
290
+
291
+ if (shouldRunWizard(mgwDir, process.argv)) {
292
+ await runWizard(mgwDir);
293
+ } else if (fs.existsSync(path.join(mgwDir, 'config.json'))) {
294
+ // report: ".mgw/config.json exists, skipping wizard"
295
+ } else {
296
+ // report: ".mgw/config.json skipped (--no-config)"
297
+ }
298
+ ```
299
+
300
+ The wizard asks the user four questions in order:
301
+ 1. **GitHub username** — auto-detected from `gh api user -q .login`; user may accept or override
302
+ 2. **Default issue state filter** — `open` (default) or `all`
303
+ 3. **Default issue limit** — `10`, `25` (default), or `50`
304
+ 4. **Default assignee filter** — `me` (default) or `all`
305
+
306
+ Answers are written to `${REPO_ROOT}/.mgw/config.json`:
307
+ ```json
308
+ {
309
+ "github_username": "...",
310
+ "default_issue_state": "open",
311
+ "default_issue_limit": 25,
312
+ "default_assignee": "me",
313
+ "created_at": "<ISO timestamp>"
314
+ }
315
+ ```
316
+
317
+ If the wizard errors or is interrupted, report the failure but do not abort the overall init — config is optional.
318
+ </step>
319
+
216
320
  <step name="report">
217
321
  **Report setup status:**
218
322
 
@@ -223,11 +327,13 @@ gh label create "mgw:blocked" --description "Pipeline blocked by stakeholder com
223
327
 
224
328
  .mgw/ ${created|exists}
225
329
  .mgw/cross-refs.json ${created|exists}
330
+ .mgw/config.json ${written|exists|skipped}
226
331
  .gitignore entries ${added|exists}
227
332
  Issue templates ${created|exists}
228
333
  PR template ${created|exists}
229
334
  GitHub labels synced
230
335
  MGW pipeline labels synced (7 labels)
336
+ Shell completions ${installed (bash|zsh|fish)|skipped|not available}
231
337
 
232
338
  Ready to use:
233
339
  /mgw:issues Browse issues
@@ -242,9 +348,13 @@ Ready to use:
242
348
  - [ ] .mgw/ directory structure created
243
349
  - [ ] .mgw/ and .worktrees/ in .gitignore
244
350
  - [ ] cross-refs.json initialized
351
+ - [ ] Config wizard run (or skipped via --no-config / pre-existing config.json)
352
+ - [ ] .mgw/config.json written with user preferences (unless skipped)
245
353
  - [ ] Issue templates created (bug + enhancement)
246
354
  - [ ] PR template created
247
355
  - [ ] GitHub labels ensured (bug, enhancement)
248
356
  - [ ] MGW pipeline labels ensured (7 mgw:* labels)
249
- - [ ] Setup report shown
357
+ - [ ] Shell completion install offered (interactive) or hint printed (non-interactive)
358
+ - [ ] Completion install skipped gracefully if completions dir not found
359
+ - [ ] Setup report shown with completion status line
250
360
  </success_criteria>
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mgw:issues
3
3
  description: List and filter GitHub issues, pick one to triage
4
- argument-hint: "[--label &lt;label&gt;] [--milestone &lt;name&gt;] [--assignee &lt;user&gt;] [--state open|closed|all]"
4
+ argument-hint: "[--label &lt;label&gt;] [--milestone &lt;name&gt;] [--assignee &lt;user&gt;] [--state open|closed|all] [--search &lt;query&gt;]"
5
5
  allowed-tools:
6
6
  - Bash
7
7
  - Read
@@ -14,6 +14,10 @@ Browse GitHub issues for the current repo. Presents a scannable table filtered b
14
14
  assignment (defaults to @me), labels, milestone, or state. Pick an issue to route
15
15
  into triage via /mgw:issue.
16
16
 
17
+ This is the Claude Code slash-command variant (table + AskUserQuestion). When running
18
+ from the CLI (`mgw issues`), an interactive TUI browser launches instead — see
19
+ `docs/TUI-DESIGN.md` and `lib/tui/index.cjs` for the TUI implementation.
20
+
17
21
  No side effects — read-only GitHub access. Safe to run anytime.
18
22
  </objective>
19
23
 
@@ -107,3 +111,61 @@ If invalid → re-prompt.
107
111
  - [ ] User can pick an issue number
108
112
  - [ ] Routes to /mgw:issue <number>
109
113
  </success_criteria>
114
+
115
+ ## TUI Mode (CLI only)
116
+
117
+ When `mgw issues` runs from the CLI entry point (`bin/mgw.cjs`) in an interactive
118
+ terminal, it launches a full TUI browser instead of a static table.
119
+
120
+ **Entry point:** `lib/tui/index.cjs` — `createIssuesBrowser(options)`
121
+
122
+ **CLI options:**
123
+ ```
124
+ mgw issues [options]
125
+ -l, --label <label> Filter by label
126
+ -m, --milestone <name> Filter by milestone
127
+ -a, --assignee <user> Assignee filter (default: @me, 'all' = no filter)
128
+ -s, --search <query> Pre-populate the fuzzy search input
129
+ --state <state> Issue state: open|closed|all (default: open)
130
+ --limit <n> Max issues to load (default: 50)
131
+ ```
132
+
133
+ **TUI keyboard shortcuts:**
134
+ | Key | Action |
135
+ |-----|--------|
136
+ | `j` / `↓` | Scroll down |
137
+ | `k` / `↑` | Scroll up |
138
+ | `/` | Focus search input |
139
+ | `Enter` | Select issue → prints `#N — Title` and exits |
140
+ | `q` / `Esc` | Quit |
141
+ | `Tab` | Cycle focus (list → detail → filter) |
142
+ | `g` / `Home` | Jump to top |
143
+ | `G` / `End` | Jump to bottom |
144
+ | `?` | Toggle keyboard help |
145
+
146
+ **Non-interactive fallback:**
147
+ When stdout is not a TTY (piped, CI, `MGW_NO_TUI=1`), the static table is printed
148
+ to stdout. Pipe-friendly — no ANSI codes, no interactive elements.
149
+
150
+ ```bash
151
+ mgw issues | grep "auth"
152
+ ```
153
+
154
+ **Implementation modules:**
155
+ ```
156
+ lib/tui/
157
+ index.cjs — createIssuesBrowser(options) — entry point
158
+ search.cjs — FuzzySearch class — pure, no UI dependency
159
+ keyboard.cjs — KeyboardHandler (EventEmitter)
160
+ renderer.cjs — createRenderer() — blessed/neo-blessed adapter
161
+ graceful.cjs — isInteractive(), renderStaticTable()
162
+ ```
163
+
164
+ **Design document:** `docs/TUI-DESIGN.md` — library selection rationale, wireframe, full interface contracts.
165
+
166
+ **Rendering library:** `neo-blessed` (optional dependency). Renderer is swappable via `lib/tui/renderer.cjs`.
167
+
168
+ **Slash command vs CLI:**
169
+ This slash command (`/mgw:issues`) uses the static table + `AskUserQuestion` pattern
170
+ because Claude Code sessions don't have raw TTY access. The TUI is CLI-only (`mgw issues`).
171
+ Both paths route to `/mgw:issue <number>` for triage.
@@ -141,6 +141,28 @@ Display:
141
141
  Issues: ${TOTAL_ISSUES}
142
142
  Mode: ${INTERACTIVE ? "Interactive" : "Autonomous"}
143
143
  ```
144
+
145
+ Then print the initial milestone progress bar (0 done, TOTAL_ISSUES total):
146
+ ```bash
147
+ ISSUES_WITH_STAGES=$(echo "$ISSUES_JSON" | python3 -c "
148
+ import json,sys
149
+ issues = json.load(sys.stdin)
150
+ result = [{'number': i['github_number'], 'pipeline_stage': i.get('pipeline_stage', 'new')} for i in issues]
151
+ print(json.dumps(result))
152
+ ")
153
+
154
+ node -e "
155
+ const { printMilestoneProgress } = require('./lib/progress.cjs');
156
+ const issues = JSON.parse(process.env.ISSUES_WITH_STAGES || '[]');
157
+ const doneCount = issues.filter(i => i.pipeline_stage === 'done' || i.pipeline_stage === 'pr-created').length;
158
+ printMilestoneProgress({
159
+ done: doneCount,
160
+ total: issues.length,
161
+ label: process.env.MILESTONE_NAME,
162
+ issues
163
+ });
164
+ " MILESTONE_NAME="$MILESTONE_NAME" ISSUES_WITH_STAGES="$ISSUES_WITH_STAGES"
165
+ ```
144
166
  </step>
145
167
 
146
168
  <step name="resolve_execution_order">
@@ -723,6 +745,27 @@ writeProjectState(state);
723
745
 
724
746
  ISSUES_RUN=$((ISSUES_RUN + 1))
725
747
 
748
+ # Update and print milestone progress bar after each issue completes
749
+ ISSUES_WITH_STAGES=$(node -e "
750
+ const { loadProjectState, resolveActiveMilestoneIndex } = require('./lib/state.cjs');
751
+ const state = loadProjectState();
752
+ const idx = resolveActiveMilestoneIndex(state);
753
+ if (idx < 0) { console.log('[]'); process.exit(0); }
754
+ const issues = state.milestones[idx].issues || [];
755
+ console.log(JSON.stringify(issues.map(i => ({ number: i.github_number, pipeline_stage: i.pipeline_stage || 'new' }))));
756
+ " 2>/dev/null || echo "[]")
757
+
758
+ node -e "
759
+ const { printMilestoneProgress } = require('./lib/progress.cjs');
760
+ const issues = JSON.parse(process.env.ISSUES_WITH_STAGES || '[]');
761
+ const doneCount = issues.filter(i => i.pipeline_stage === 'done' || i.pipeline_stage === 'pr-created').length;
762
+ printMilestoneProgress({
763
+ done: doneCount,
764
+ total: issues.length,
765
+ issues
766
+ });
767
+ " ISSUES_WITH_STAGES="$ISSUES_WITH_STAGES"
768
+
726
769
  # If --interactive: pause between issues
727
770
  if [ "$INTERACTIVE" = true ]; then
728
771
  AskUserQuestion(
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mgw:status
3
3
  description: Project status dashboard — milestone progress, issue pipeline stages, open PRs
4
- argument-hint: "[milestone_number] [--json] [--board]"
4
+ argument-hint: "[milestone_number] [--json] [--board] [--watch [--interval N]]"
5
5
  allowed-tools:
6
6
  - Bash
7
7
  - Read
@@ -13,6 +13,11 @@ pipeline stages, open PRs, and next milestone preview. Pure read-only — no sta
13
13
  mutations, no agent spawns, no GitHub writes.
14
14
 
15
15
  Falls back gracefully when no project.json exists (lists active issues only via GitHub API).
16
+
17
+ When `--watch` is passed, enters live-refresh mode: clears the terminal and redraws the
18
+ dashboard every N seconds (default 30). Displays a "Last refreshed" timestamp. User exits
19
+ by pressing 'q' or Ctrl+C. If both `--watch` and `--json` are supplied, print an error and
20
+ exit 1.
16
21
  </objective>
17
22
 
18
23
  <execution_context>
@@ -35,11 +40,21 @@ Repo detected via: gh repo view --json nameWithOwner -q .nameWithOwner
35
40
  MILESTONE_NUM=""
36
41
  JSON_OUTPUT=false
37
42
  OPEN_BOARD=false
43
+ WATCH_MODE=false
44
+ WATCH_INTERVAL=30
45
+ NEXT_IS_INTERVAL=false
38
46
 
39
47
  for ARG in $ARGUMENTS; do
48
+ if [ "$NEXT_IS_INTERVAL" = true ]; then
49
+ WATCH_INTERVAL="$ARG"
50
+ NEXT_IS_INTERVAL=false
51
+ continue
52
+ fi
40
53
  case "$ARG" in
41
54
  --json) JSON_OUTPUT=true ;;
42
55
  --board) OPEN_BOARD=true ;;
56
+ --watch) WATCH_MODE=true ;;
57
+ --interval) NEXT_IS_INTERVAL=true ;;
43
58
  [0-9]*) MILESTONE_NUM="$ARG" ;;
44
59
  esac
45
60
  done
@@ -418,6 +433,77 @@ Rendering rules:
418
433
  - If no open PRs matched to milestone, show "No open PRs for this milestone."
419
434
  - If no next milestone, show "No more milestones planned."
420
435
  - If `TARGET_MILESTONE != CURRENT_MILESTONE`, add "(viewing milestone ${TARGET_MILESTONE})" to header
436
+ - In watch mode, append the footer: `[ Refreshing every ${WATCH_INTERVAL}s — last refreshed HH:MM:SS | press q to quit ]`
437
+ </step>
438
+
439
+ <step name="watch_mode">
440
+ **If --watch flag: enter live-refresh loop:**
441
+
442
+ `--watch` is incompatible with `--json`. If both are passed, print an error and exit 1.
443
+
444
+ The watch loop is implemented as a Node.js one-shot script executed via `node -e` (or saved
445
+ to a temp file). It wraps the full dashboard render cycle with `setInterval`, clears the
446
+ terminal before each redraw, and uses `process.stdin.setRawMode(true)` to detect a 'q'
447
+ keypress for clean exit.
448
+
449
+ ```javascript
450
+ // watch-mode runner (pseudocode — shows the pattern)
451
+ const { execSync } = require('child_process');
452
+ const INTERVAL = parseInt(process.env.WATCH_INTERVAL || '30', 10) * 1000;
453
+ const REPO_ROOT = process.env.REPO_ROOT;
454
+
455
+ function renderDashboard() {
456
+ // Re-run all data collection and dashboard build steps synchronously
457
+ // (same logic as the non-watch single-shot path above, but called in a loop)
458
+ const output = buildDashboardOutput(); // all the python/gh calls assembled into a string
459
+ // Implementation note: buildDashboardOutput() is a pseudocode placeholder.
460
+ // The executor must refactor the full `display_dashboard` step into a reusable
461
+ // function that collects all GitHub and project.json data and returns the rendered
462
+ // dashboard string, then call it from both the single-shot path and the watch loop.
463
+ const now = new Date().toLocaleTimeString();
464
+ process.stdout.write('\x1B[2J\x1B[H'); // clear terminal, cursor home
465
+ process.stdout.write(output);
466
+ process.stdout.write(`\n[ Refreshing every ${INTERVAL / 1000}s — last refreshed ${now} | press q to quit ]\n`);
467
+ }
468
+
469
+ // Initial render
470
+ renderDashboard();
471
+
472
+ // Poll on interval
473
+ const timer = setInterval(renderDashboard, INTERVAL);
474
+
475
+ // Detect 'q' to exit
476
+ if (process.stdin.isTTY) {
477
+ process.stdin.setRawMode(true);
478
+ process.stdin.resume();
479
+ process.stdin.setEncoding('utf8');
480
+ process.stdin.on('data', (key) => {
481
+ if (key === 'q' || key === '\u0003') { // 'q' or Ctrl+C
482
+ clearInterval(timer);
483
+ process.stdin.setRawMode(false);
484
+ process.stdout.write('\nWatch mode exited.\n');
485
+ process.exit(0);
486
+ }
487
+ });
488
+ }
489
+ ```
490
+
491
+ Implementation notes for the executor:
492
+ - The watch runner re-executes the full data fetch on each interval tick (re-reads
493
+ project.json, re-calls `gh pr list`, etc.) so the display reflects live GitHub state.
494
+ - `process.stdout.write('\x1B[2J\x1B[H')` clears the screen without spawning `clear`.
495
+ - The footer line shows "last refreshed HH:MM:SS" only. No countdown timer.
496
+ - SIGINT (Ctrl+C) should also trigger clean exit — the `'\u0003'` check above handles it,
497
+ but also register `process.on('SIGINT', ...)` as a fallback when `setRawMode` is false
498
+ (non-TTY or piped stdin).
499
+ - If `--watch` and `--json` are both supplied, print the error message and exit 1 before
500
+ entering the watch loop.
501
+
502
+ If both flags are supplied, emit:
503
+ ```
504
+ Error: --watch and --json cannot be used together.
505
+ ```
506
+ and exit 1.
421
507
  </step>
422
508
 
423
509
  <step name="json_output">
@@ -523,4 +609,12 @@ The JSON structure:
523
609
  - [ ] Velocity computed from .mgw/active/ and .mgw/completed/ file mtimes
524
610
  - [ ] --json output includes board_url and milestone.health object
525
611
  - [ ] Board URL line omitted when board_url is not set in project.json
612
+ - [ ] --watch flag enters live-refresh loop, refreshing every N seconds (default 30)
613
+ - [ ] --interval N overrides the default 30s refresh interval
614
+ - [ ] Watch mode clears terminal before each redraw
615
+ - [ ] Watch mode footer shows last refresh time
616
+ - [ ] 'q' keypress exits watch mode cleanly (stdin raw mode)
617
+ - [ ] Ctrl+C (SIGINT) exits watch mode cleanly
618
+ - [ ] --watch and --json are mutually exclusive — error + exit 1 if both supplied
619
+ - [ ] Watch mode re-fetches all data on each tick (live GitHub state)
526
620
  </success_criteria>
@@ -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