|
| 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> |
0 commit comments