Skip to content

Commit a7db5ea

Browse files
authored
Merge pull request #42 from snipcodeit/issue/38-feat-mgw-ask-question-routing
feat: add /mgw:ask contextual question routing command
2 parents 80d61bc + 6877ff0 commit a7db5ea

8 files changed

Lines changed: 788 additions & 6 deletions

File tree

.claude/commands/mgw/ask.md

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
---
2+
name: mgw:ask
3+
description: Route a question/observation — classify as in-scope, adjacent, separate, duplicate, or out-of-scope
4+
argument-hint: "<question>"
5+
allowed-tools:
6+
- Bash
7+
- Read
8+
- Task
9+
- AskUserQuestion
10+
---
11+
12+
<objective>
13+
During milestone execution, observations arise that need classification: is this
14+
in-scope for the current issue, adjacent to a different issue, a separate concern,
15+
a duplicate, or out-of-scope entirely?
16+
17+
/mgw:ask spawns a general-purpose agent with full project context (milestone, all
18+
issues, active state, recent git diff) that classifies the question and recommends
19+
an action. Read-only for the orchestrator — the agent reads state and code, MGW
20+
presents the structured result.
21+
</objective>
22+
23+
<execution_context>
24+
@~/.claude/commands/mgw/workflows/state.md
25+
@~/.claude/commands/mgw/workflows/github.md
26+
@~/.claude/commands/mgw/workflows/gsd.md
27+
@~/.claude/commands/mgw/workflows/validation.md
28+
</execution_context>
29+
30+
<context>
31+
Question text: $ARGUMENTS
32+
33+
Active issues context: .mgw/active/ (current work state)
34+
Project context: .mgw/project.json (milestone + all issues)
35+
</context>
36+
37+
<process>
38+
39+
<step name="validate_input">
40+
**Validate question text provided:**
41+
42+
Parse $ARGUMENTS for the question text. If missing or empty:
43+
```
44+
AskUserQuestion(
45+
header: "Question Required",
46+
question: "What question or observation do you want to classify?",
47+
followUp: "Enter a question or observation (e.g., 'The slug generation doesn't handle unicode characters')"
48+
)
49+
```
50+
Store as $QUESTION.
51+
</step>
52+
53+
<step name="load_project_context">
54+
**Load project.json for milestone and issue context:**
55+
56+
```bash
57+
REPO_ROOT=$(git rev-parse --show-toplevel)
58+
MGW_DIR="${REPO_ROOT}/.mgw"
59+
60+
# Load project state
61+
PROJECT_JSON=""
62+
MILESTONE_CONTEXT=""
63+
ALL_ISSUES_CONTEXT=""
64+
65+
if [ -f "${MGW_DIR}/project.json" ]; then
66+
PROJECT_JSON=$(cat "${MGW_DIR}/project.json")
67+
68+
# Extract current milestone info
69+
MILESTONE_CONTEXT=$(echo "$PROJECT_JSON" | python3 -c "
70+
import json, sys
71+
p = json.load(sys.stdin)
72+
idx = p['current_milestone'] - 1
73+
if idx < len(p['milestones']):
74+
m = p['milestones'][idx]
75+
print(f\"Milestone: {m['name']}\")
76+
if m.get('description'):
77+
print(f\"Description: {m['description']}\")
78+
else:
79+
print('No active milestone')
80+
" 2>/dev/null || echo "No project initialized")
81+
82+
# Extract all issues in current milestone with titles, bodies, scopes, labels
83+
ALL_ISSUES_CONTEXT=$(echo "$PROJECT_JSON" | python3 -c "
84+
import json, sys
85+
p = json.load(sys.stdin)
86+
idx = p['current_milestone'] - 1
87+
if idx >= len(p['milestones']):
88+
print('No issues found')
89+
sys.exit(0)
90+
m = p['milestones'][idx]
91+
for issue in m.get('issues', []):
92+
num = issue.get('github_number', '?')
93+
title = issue.get('title', 'untitled')
94+
stage = issue.get('pipeline_stage', 'new')
95+
labels = ', '.join(issue.get('labels', []))
96+
phase = issue.get('phase_name', '')
97+
scope = issue.get('scope', '')
98+
print(f'#{num} [{stage}] {title}')
99+
if phase:
100+
print(f' Phase: {phase}')
101+
if labels:
102+
print(f' Labels: {labels}')
103+
if scope:
104+
print(f' Scope: {scope}')
105+
print()
106+
" 2>/dev/null || echo "")
107+
else
108+
MILESTONE_CONTEXT="No project initialized"
109+
ALL_ISSUES_CONTEXT=""
110+
fi
111+
```
112+
</step>
113+
114+
<step name="load_active_state">
115+
**Load active issue state from .mgw/active/:**
116+
117+
```bash
118+
ACTIVE_STATE=""
119+
ACTIVE_ISSUE_NUMBER=""
120+
121+
ACTIVE_FILES=$(ls "${MGW_DIR}/active/"*.json 2>/dev/null)
122+
if [ -n "$ACTIVE_FILES" ]; then
123+
for f in ${ACTIVE_FILES}; do
124+
ISSUE_DATA=$(cat "$f")
125+
ISSUE_NUM=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin).get('issue',{}).get('number','?'))")
126+
ISSUE_TITLE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin).get('issue',{}).get('title','untitled'))")
127+
PIPELINE_STAGE=$(echo "$ISSUE_DATA" | python3 -c "import json,sys; print(json.load(sys.stdin).get('pipeline_stage','unknown'))")
128+
ACTIVE_STATE="${ACTIVE_STATE}#${ISSUE_NUM} [${PIPELINE_STAGE}] ${ISSUE_TITLE}\n"
129+
# Track the most recently active issue (last in list)
130+
ACTIVE_ISSUE_NUMBER="$ISSUE_NUM"
131+
done
132+
else
133+
ACTIVE_STATE="No active issues"
134+
fi
135+
```
136+
</step>
137+
138+
<step name="gather_git_diff">
139+
**Gather recent git diff for change context:**
140+
141+
```bash
142+
# Get recent changes (staged + unstaged, limited to keep prompt reasonable)
143+
RECENT_DIFF=$(git diff HEAD --stat 2>/dev/null | head -30 || echo "No changes")
144+
CURRENT_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
145+
```
146+
</step>
147+
148+
<step name="fetch_issue_details">
149+
**Fetch full issue bodies from GitHub for matching context:**
150+
151+
If project.json has issues, fetch their bodies for the agent to compare against:
152+
153+
```bash
154+
ISSUE_BODIES=""
155+
if [ -n "$PROJECT_JSON" ]; then
156+
ISSUE_NUMBERS=$(echo "$PROJECT_JSON" | python3 -c "
157+
import json, sys
158+
p = json.load(sys.stdin)
159+
idx = p['current_milestone'] - 1
160+
if idx < len(p['milestones']):
161+
m = p['milestones'][idx]
162+
for issue in m.get('issues', []):
163+
print(issue.get('github_number', ''))
164+
" 2>/dev/null)
165+
166+
for NUM in $ISSUE_NUMBERS; do
167+
if [ -n "$NUM" ]; then
168+
BODY=$(gh issue view "$NUM" --json number,title,body -q '"\(.number)|\(.title)|\(.body)"' 2>/dev/null || echo "")
169+
if [ -n "$BODY" ]; then
170+
ISSUE_BODIES="${ISSUE_BODIES}${BODY}\n---\n"
171+
fi
172+
fi
173+
done
174+
fi
175+
```
176+
</step>
177+
178+
<step name="spawn_classification_agent">
179+
**Spawn general-purpose agent for question classification:**
180+
181+
```
182+
Task(
183+
prompt="
184+
<files_to_read>
185+
- ./CLAUDE.md (Project instructions — if exists, follow all guidelines)
186+
- .agents/skills/ (Project skills — if dir exists, list skills, read SKILL.md for each, follow relevant rules)
187+
</files_to_read>
188+
189+
You are a question classification agent for the MGW pipeline. Your job is to
190+
classify a question or observation against the current project context and
191+
recommend an action.
192+
193+
<question>
194+
${QUESTION}
195+
</question>
196+
197+
<current_milestone>
198+
${MILESTONE_CONTEXT}
199+
</current_milestone>
200+
201+
<milestone_issues>
202+
${ALL_ISSUES_CONTEXT}
203+
</milestone_issues>
204+
205+
<issue_bodies>
206+
${ISSUE_BODIES}
207+
</issue_bodies>
208+
209+
<active_work>
210+
Current branch: ${CURRENT_BRANCH}
211+
Active issues in .mgw/:
212+
${ACTIVE_STATE}
213+
</active_work>
214+
215+
<recent_changes>
216+
${RECENT_DIFF}
217+
</recent_changes>
218+
219+
<classification_rules>
220+
221+
Classify the question into exactly ONE of these categories:
222+
223+
| Category | Criteria | Action |
224+
|----------|----------|--------|
225+
| In-scope | Directly relates to the current active issue being worked on | Include in current work — no new issue needed |
226+
| Adjacent | Relates to a DIFFERENT issue in the same milestone | Note it on that issue — suggest posting a comment |
227+
| Separate | Doesn't match any open issue in the milestone | Suggest filing a new issue with a title |
228+
| Duplicate | Matches an existing issue (same root cause or fix) | Point to the existing issue |
229+
| Out-of-scope | Beyond the current milestone entirely | Note for future planning |
230+
231+
Decision process:
232+
1. Read the question carefully
233+
2. Compare against each issue title, body, and scope in the milestone
234+
3. Check if it relates to current active work (branch, diff, active state)
235+
4. Look for keyword/concept overlap with existing issues
236+
5. If no match, determine if it fits the milestone's theme or is out-of-scope
237+
238+
</classification_rules>
239+
240+
<output_format>
241+
Return a structured classification report:
242+
243+
## Classification: ${CATEGORY}
244+
245+
### Analysis
246+
- What the question is about (1-2 sentences)
247+
- Why this classification was chosen (1-2 sentences)
248+
249+
### Related Issue
250+
- Issue number and title (if adjacent or duplicate)
251+
- Or 'N/A — current work' (if in-scope)
252+
- Or 'No matching issue' (if separate or out-of-scope)
253+
254+
### Recommendation
255+
- Specific actionable next step
256+
- If 'separate': suggest an issue title and brief body
257+
- If 'adjacent': suggest a comment to post on the related issue
258+
- If 'in-scope': note what to include in current work
259+
- If 'duplicate': point to the matching issue
260+
- If 'out-of-scope': note for future milestone planning
261+
</output_format>
262+
",
263+
subagent_type="general-purpose",
264+
description="Classify question: ${QUESTION}"
265+
)
266+
```
267+
</step>
268+
269+
<step name="present_result">
270+
**Present the classification result to the user:**
271+
272+
Display the agent's report, then present the MGW action banner:
273+
274+
```
275+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
276+
MGW ► QUESTION ROUTING
277+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
278+
279+
${classification_report_from_agent}
280+
281+
───────────────────────────────────────────────────────
282+
```
283+
</step>
284+
285+
<step name="offer_actions">
286+
**Offer follow-up actions based on classification:**
287+
288+
**If "Separate":**
289+
```
290+
AskUserQuestion(
291+
header: "File New Issue?",
292+
question: "Create a new issue from this observation?",
293+
options: [
294+
{ label: "Yes", description: "File the suggested issue via /mgw:issue" },
295+
{ label: "No", description: "Note it and continue current work" }
296+
]
297+
)
298+
```
299+
300+
If user says "Yes":
301+
```
302+
Suggested issue ready. To file it:
303+
304+
gh issue create --title "${suggested_title}" --body "${suggested_body}"
305+
306+
Or use /mgw:project to add it to a future milestone.
307+
```
308+
309+
**If "Adjacent":**
310+
```
311+
AskUserQuestion(
312+
header: "Post Comment?",
313+
question: "Post a note on the related issue #${related_number}?",
314+
options: [
315+
{ label: "Yes", description: "Post observation as a comment on #${related_number}" },
316+
{ label: "No", description: "Note it and continue current work" }
317+
]
318+
)
319+
```
320+
321+
If user says "Yes":
322+
```bash
323+
gh issue comment ${related_number} --body "> **MGW** · \`observation\` · $(node ~/.claude/get-shit-done/bin/gsd-tools.cjs current-timestamp --raw)
324+
325+
Observation noted during work on #${ACTIVE_ISSUE_NUMBER}:
326+
327+
${QUESTION}"
328+
```
329+
330+
Report: "Comment posted on #${related_number}."
331+
332+
**If "In-scope":**
333+
```
334+
This relates to your current work on #${ACTIVE_ISSUE_NUMBER}.
335+
No action needed — include it in your current implementation.
336+
```
337+
338+
**If "Duplicate":**
339+
```
340+
This appears to duplicate #${duplicate_number} — ${duplicate_title}.
341+
No new issue needed. Check that issue for existing progress.
342+
```
343+
344+
**If "Out-of-scope":**
345+
```
346+
This is beyond the current milestone scope.
347+
Consider adding it to a future milestone or filing it for backlog:
348+
349+
gh issue create --title "${suggested_title}" --label "backlog"
350+
```
351+
</step>
352+
353+
</process>
354+
355+
<success_criteria>
356+
- [ ] Question text parsed from $ARGUMENTS (or prompted)
357+
- [ ] project.json loaded for milestone + issue context
358+
- [ ] .mgw/active/ state loaded for current work context
359+
- [ ] Recent git diff gathered for change context
360+
- [ ] Issue bodies fetched from GitHub for comparison
361+
- [ ] Classification agent spawned with full context
362+
- [ ] Classification returned: in-scope, adjacent, separate, duplicate, or out-of-scope
363+
- [ ] Related issue identified (if adjacent or duplicate)
364+
- [ ] Actionable recommendation provided
365+
- [ ] Follow-up action offered (file issue, post comment, etc.)
366+
- [ ] Delegation boundary respected: agent reads code/state, MGW presents results
367+
</success_criteria>
368+
</output>

.claude/commands/mgw/help.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ COMMANDS
5353
/mgw:pr [number] [--base b] Create PR from GSD artifacts + issue context
5454
/mgw:link <ref> <ref> Cross-reference issues/PRs/branches
5555
56+
Query
57+
─────
58+
/mgw:ask <question> Route a question — in-scope, adjacent, separate, duplicate, out-of-scope
59+
5660
Maintenance
5761
───────────
5862
/mgw:sync Reconcile .mgw/ state with GitHub

.claude/commands/mgw/workflows/github.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,9 +259,9 @@ Release is created as draft — user reviews and publishes manually.
259259

260260
| Section | Referenced By |
261261
|---------|-------------|
262-
| Issue Operations | issue.md, run.md, issues.md, sync.md, milestone.md, next.md |
262+
| Issue Operations | issue.md, run.md, issues.md, sync.md, milestone.md, next.md, ask.md |
263263
| Comment Operations | issue.md (triage snapshot), run.md (pre-flight check), sync.md (drift), review.md |
264-
| Issue Mutations | issue.md, update.md, run.md, init.md, milestone.md |
264+
| Issue Mutations | issue.md, update.md, run.md, init.md, milestone.md, ask.md |
265265
| Milestone Operations | project.md, milestone.md |
266266
| Dependency Labels | project.md |
267267
| Phase Labels | project.md |

0 commit comments

Comments
 (0)