| name | sg-retro |
| description | Run a structured retrospective on a GSD phase with one of three lenses (ssc, dspm, analyze) — select multiple lenses in one call or omit lens argument for smart default (dspm+ssc) — and append results to .planning/lessons/{NN}-{YYYY-MM-DD}.md. `--pick` flag is rejected with a graceful exit explaining positional alternative (no AskUserQuestion in this environment). AskUserQuestion-free version for Codex/Gemini CLI/Antigravity CLI. |
| argument-hint | [phase] [lens...|--pick] - e.g. '14 ssc' or '14 --pick'. lens: ssc|dspm|analyze. Omit lens for smart default (dspm+ssc). --pick is not supported in this environment (AskUserQuestion required) — use positional args instead. |
Detect the user's input language and respond in that language throughout this skill's output.
- Korean input → respond in Korean
- English input → respond in English
- Mixed input → match the dominant language
Run a structured retrospective on a GSD phase. Auto-collect phase artifacts and git context. Let the user pick one or more of three lenses — Start/Stop/Continue (ssc), Decisions/Surprises/Patterns/Mistakes (dspm), Conversation Analyzer (analyze) — or omit the lens argument to apply the smart default (dspm+ssc). The `--pick` flag is accepted for argument compatibility but triggers a graceful early-exit since AskUserQuestion is unavailable in this environment. Facilitate each lens (artifact-grounded for ssc/dspm; transcript-native for analyze), then append all results sequentially to `.planning/lessons/{NN}-{YYYY-MM-DD}.md`.
## Platform Constraints (Codex / Gemini CLI / Antigravity CLI)
- AskUserQuestion not supported: smart default (dspm+ssc) applies when no lens argument is provided; explicit lens arguments (ssc/dspm/analyze) are accepted directly. The `--pick` flag triggers a graceful early-exit (stderr message + exit 1) since the interactive picker requires AskUserQuestion.
- SubagentStop not supported: no automatic trigger on stage completion
- Superpowers integration unavailable: this skill is fully self-contained
<execution_context>
Self-contained. Reads .planning/STATE.md, .planning/phases/{NN}-*/{NN}-CONTEXT.md, all {NN}-*-PLAN.md, all {NN}-*-SUMMARY.md, and git log/git diff. Writes only .planning/lessons/{NN}-{YYYY-MM-DD}.md (and creates .planning/lessons/ if missing).
</execution_context>
Step 1 — Argument parsing + phase resolve.
Parse $ARGUMENTS into PHASE_RAW and LENS_RAW. Detect --pick token anywhere in the argument list and strip it before positional parsing (D-01, D-02). If PHASE_RAW is empty, fall back to .planning/STATE.md ^Phase: line using the multi-line sed pattern below. Do not introduce a single-token regex shortcut — preserve the full grep + sed + awk pipeline as-is.
set -- $ARGUMENTS
PICK_MODE=false
NEW_ARGS=""
for ARG in "$@"; do
case "$ARG" in
--pick)
PICK_MODE=true
;;
*)
NEW_ARGS="${NEW_ARGS}${NEW_ARGS:+ }${ARG}"
;;
esac
done
set -- $NEW_ARGS
PHASE_RAW="${1:-}"
LENS_RAW="${2:-}"
if [ -z "$PHASE_RAW" ]; then
PHASE_RAW=$(grep -E '^Phase:' .planning/STATE.md 2>/dev/null | head -1 \
| sed -E 's/^Phase:[[:space:]]*//' \
| sed -E 's/[[:space:]]+$//' \
| awk '{print $1}')
fi
if ! printf '%s' "$PHASE_RAW" | grep -qE '^[0-9]+(\.[0-9]+)?$'; then
echo "Phase token must be a number (integer or decimal like 7.1). Got: '${PHASE_RAW}'." >&2
if [ -d .planning/phases ]; then
echo "Available phases:" >&2
ls .planning/phases/ 2>/dev/null >&2 || echo " (no phases yet)" >&2
fi
exit 1
fi
if echo "$PHASE_RAW" | grep -qE '\.'; then
PHASE_PAD="$PHASE_RAW"
else
PHASE_PAD=$(printf "%02d" "$PHASE_RAW")
fi
PHASE_DIR=$(ls -d .planning/phases/${PHASE_PAD}-*/ 2>/dev/null | head -1)
if [ -z "$PHASE_DIR" ]; then
echo "Phase ${PHASE_RAW} not found. Available phases:" >&2
ls .planning/phases/ 2>/dev/null >&2 || echo " (no phases yet)" >&2
exit 1
fi
PHASE_DIR="${PHASE_DIR%/}"
EXTRA_LENS_CODES=""
if [ -n "${3:-}" ]; then
shift 2
EXTRA_LENS_CODES="$@"
fi
LENS_CODES_ARRAY=""
Step 2 — Lens code mapping with smart default.
Map LENS_RAW to one of ssc/dspm/analyze (case-insensitive). Removed lens codes (4ls/sail/5why) or any unknown code emit a stderr error message and exit 1 without creating or appending to a lessons file. When LENS_RAW and EXTRA_LENS_CODES are both empty, the smart default applies automatically (no AskUserQuestion, no numbered list).
LENS_CODE=""
if [ -n "$LENS_RAW" ]; then
LENS_LC=$(printf '%s' "$LENS_RAW" | tr '[:upper:]' '[:lower:]')
case "$LENS_LC" in
ssc) LENS_CODE="ssc" ;;
dspm) LENS_CODE="dspm" ;;
analyze) LENS_CODE="analyze" ;;
4ls|sail|5why)
printf "Lens '%s' is no longer supported (removed in v2.9).\nAvailable lenses: ssc, dspm, analyze.\nRun without lens argument to use smart default (dspm+ssc).\n" "$LENS_LC" >&2
exit 1
;;
*)
printf "Lens '%s' is no longer supported (removed in v2.9).\nAvailable lenses: ssc, dspm, analyze.\nRun without lens argument to use smart default (dspm+ssc).\n" "$LENS_LC" >&2
exit 1
;;
esac
fi
Single-lens argument path:
VALID_EXTRAS=""
if [ -n "$EXTRA_LENS_CODES" ]; then
for EC in $EXTRA_LENS_CODES; do
EC_LC=$(printf '%s' "$EC" | tr '[:upper:]' '[:lower:]')
case "$EC_LC" in
ssc|dspm|analyze) VALID_EXTRAS="${VALID_EXTRAS} ${EC_LC}" ;;
4ls|sail|5why)
printf "Lens '%s' is no longer supported (removed in v2.9).\nAvailable lenses: ssc, dspm, analyze.\nRun without lens argument to use smart default (dspm+ssc).\n" "$EC_LC" >&2
exit 1
;;
*)
printf "Lens '%s' is no longer supported (removed in v2.9).\nAvailable lenses: ssc, dspm, analyze.\nRun without lens argument to use smart default (dspm+ssc).\n" "$EC_LC" >&2
exit 1
;;
esac
done
fi
if [ -n "$LENS_CODE" ]; then
LENS_CODES_ARRAY="${LENS_CODE}${VALID_EXTRAS}"
elif [ -n "$VALID_EXTRAS" ]; then
LENS_CODES_ARRAY="$VALID_EXTRAS"
fi
if [ "$PICK_MODE" = "true" ] && { [ -n "$LENS_RAW" ] || [ -n "$EXTRA_LENS_CODES" ]; }; then
printf 'Cannot combine --pick with positional lens argument.\nUse either: sg-retro {phase} {lens...} (explicit args)\nOr: sg-retro {phase} --pick (interactive picker)\n' >&2
exit 1
fi
if [ -z "$LENS_CODES_ARRAY" ] && [ -z "$LENS_RAW" ] && [ -z "$EXTRA_LENS_CODES" ]; then
LENS_CODES_ARRAY="dspm ssc"
fi
Step 3 — Collect target artifacts.
CONTEXT_FILE="${PHASE_DIR}/${PHASE_PAD}-CONTEXT.md"
PLAN_FILES=$(ls -1 ${PHASE_DIR}/${PHASE_PAD}-*-PLAN.md 2>/dev/null)
SUMMARY_FILES=$(ls -1 ${PHASE_DIR}/${PHASE_PAD}-*-SUMMARY.md 2>/dev/null)
echo "Collected artifacts:" >&2
[ -f "$CONTEXT_FILE" ] && echo " $CONTEXT_FILE" >&2
[ -n "$PLAN_FILES" ] && printf ' %s\n' $PLAN_FILES >&2
[ -n "$SUMMARY_FILES" ] && printf ' %s\n' $SUMMARY_FILES >&2
Step 3b — Collect session transcript (for analyze lens).
PROJECT_SLUG=$(pwd | tr '/' '-')
TRANSCRIPT_DIR="$HOME/.claude/projects/${PROJECT_SLUG}"
TRANSCRIPT_FILE=$(ls -t "${TRANSCRIPT_DIR}"/*.jsonl 2>/dev/null | head -1)
if [ -z "$TRANSCRIPT_FILE" ]; then
echo "[Conversation Analyzer] No transcript found at ${TRANSCRIPT_DIR}." >&2
TRANSCRIPT_FILE=""
fi
Step 4 — Collect git context.
BASE=$(git log -1 --format=%H -- .planning/phases/${PHASE_PAD}-*/ 2>/dev/null)
if [ -z "$BASE" ]; then
RANGE="HEAD~10..HEAD"
else
RANGE="${BASE}..HEAD"
fi
GIT_LOG=$(git log ${RANGE} --oneline 2>/dev/null)
DIFF=$(git diff ${RANGE} 2>/dev/null)
LINES=$(printf '%s\n' "$DIFF" | wc -l | tr -d ' ')
if [ "$LINES" -gt 1000 ]; then
GIT_DIFF=$(printf '[diff truncated — %s lines exceeded 1000-line cap; showing --stat summary]\n' "$LINES"; git diff --stat ${RANGE} 2>/dev/null)
else
GIT_DIFF="$DIFF"
fi
echo "Git range: ${RANGE}" >&2
Step 5 — Multi-lens execution loop + lens facilitation (artifact-grounded draft-then-confirm).
ANALYZE_LENS_RAN=false
if [ "$PICK_MODE" = "true" ]; then
printf -- '--pick is not supported in this environment (AskUserQuestion required).\nUse positional lens args (e.g. '\''sg-retro %s ssc dspm'\'') or omit lens for smart default (dspm+ssc).\n' "${PHASE_RAW}" >&2
exit 1
fi
Execute each lens in LENS_CODES_ARRAY sequentially. For each iteration, set LENS_CODE to the current code and run the matching sub-block. After the sub-block completes, run Step 6 append for that lens.
for LENS_CODE in $LENS_CODES_ARRAY; do
[run sub-block for LENS_CODE]
[run Step 6 append for this LENS_CODE]
done
Common flow for ssc/dspm lenses:
- Read all three artifact types (CONTEXT, PLAN(s), SUMMARY(s)) and capture key signals from
${GIT_LOG} and ${GIT_DIFF}.
- For each lens-specific subheading, propose 2–5 draft bullet items grounded in the artifacts above. Each bullet must cite the source (file path or commit hash). Present the full draft as a single markdown block.
- Ask the user to confirm/edit/add/delete each subheading's items. Output the draft as text and wait for user input. Single round-trip preferred — present all subheadings at once.
- After items are finalized, propose 2–4 Action Items rows: priority (
P1/P2/P3) | one-sentence item | concrete next step or deferred to Phase N label. No owner column. User confirms/edits.
- Assemble the final lens section (header +
_Captured: {NOW_ISO}_ + _Intent: ..._ + subheadings + Action Items) and append to ${LESSONS_FILE} using the bash block in Step 6.
Sub-block ssc (Start/Stop/Continue):
- Fixed subheadings:
### Start / ### Stop / ### Continue.
- Facilitation: For Start, propose practices/tools/conventions to begin. For Stop, propose anti-patterns visible in
git diff or CONTEXT. For Continue, propose practices that delivered observed value.
Sub-block dspm (Decisions/Surprises/Patterns/Mistakes):
- Fixed subheadings:
### Decisions / ### Surprises / ### Patterns / ### Mistakes.
- Explicit guard: Derives strictly from collected phase artifacts +
git log/git diff. Do NOT read or analyze session transcript.
Sub-block analyze (Conversation Analyzer):
- Fixed subheadings:
### Analysis Findings (table) / ### Draft sg-rules / ### Action Items.
- Facilitation:
- If TRANSCRIPT_FILE is empty:
echo "No transcript found — skipping Conversation Analyzer." and skip this lens.
- Read TRANSCRIPT_FILE using the Read tool (not bash grep).
- Scan recent 20-30 messages for 4 categories:
frustration, correction, repeated, validated-success.
- Output 5-column findings table:
| category | tool/event | pattern | context | severity |
- Generate Draft sg-rules for high/medium severity items.
- Confirm Action Items (text output) then append.
- Set
ANALYZE_LENS_RAN=true.
Step 6 — Append to lessons file (per lens iteration).
TODAY=$(date -u +%Y-%m-%d)
NOW_ISO=$(date -u +%Y-%m-%dT%H:%M:%SZ)
LESSONS_DIR=".planning/lessons"
LESSONS_FILE="${LESSONS_DIR}/${PHASE_PAD}-${TODAY}.md"
mkdir -p "$LESSONS_DIR"
case "$LENS_CODE" in
ssc) LENS_NAME="Start/Stop/Continue" ;;
dspm) LENS_NAME="Decisions/Surprises/Patterns/Mistakes" ;;
analyze) LENS_NAME="Conversation Analyzer" ;;
esac
case "$LENS_CODE" in
ssc) INTENT_LINE='_Intent: surface behavior changes — what to start, stop, or continue doing next phase._' ;;
dspm) INTENT_LINE='_Intent: capture technical decisions, unexpected outcomes, recurring techniques, and verification failures from this phase._' ;;
analyze) INTENT_LINE='_Intent: scan session transcript for frustration, correction, repetition, and validated-success signals; propose sg-rule drafts._' ;;
esac
RUN_SUFFIX=""
if [ -f "$LESSONS_FILE" ]; then
COUNT=$(grep -cE "^## Lens: ${LENS_NAME}( \(run [0-9]+\))?\$" "$LESSONS_FILE" 2>/dev/null)
COUNT=${COUNT:-0}
if [ "$COUNT" -gt 0 ]; then
RUN_SUFFIX=" (run $((COUNT + 1)))"
fi
fi
LENS_HEADER="## Lens: ${LENS_NAME}${RUN_SUFFIX}"
if [ -f "$LESSONS_FILE" ]; then
BEFORE=$(wc -l < "$LESSONS_FILE" | tr -d ' ')
else
BEFORE=0
fi
if [ ! -f "$LESSONS_FILE" ]; then
{
printf '# Lessons: Phase %s (%s)\n\n' "$PHASE_RAW" "$TODAY"
} > "$LESSONS_FILE"
fi
{
printf '%s\n' "$LENS_HEADER"
printf '_Captured: %s_\n' "$NOW_ISO"
printf '%s\n\n' "$INTENT_LINE"
printf '\n### Action Items\n'
printf '| priority | item | next step |\n'
printf '|----------|------|-----------|\n'
printf '\n'
} >> "$LESSONS_FILE"
AFTER=$(wc -l < "$LESSONS_FILE" | tr -d ' ')
DELTA=$((AFTER - BEFORE))
echo "lessons file: ${LESSONS_FILE} +${DELTA} lines" >&2
After the multi-lens loop completes, auto-suggest sg-rule drafts once:
if [ "${ANALYZE_LENS_RAN:-false}" = "true" ]; then
echo "sg-rule drafts were included in the Conversation Analyzer output above." >&2
else
echo "[Auto-suggest] Review the Action Items above and consider creating sg-rules for repeated patterns." >&2
fi
<lens_templates>
Start/Stop/Continue (ssc):
## Lens: Start/Stop/Continue
_Captured: {ISO-8601 UTC}_
_Intent: surface behavior changes — what to start, stop, or continue doing next phase._
### Start
- [item]
### Stop
- [item]
### Continue
- [item]
### Action Items
| priority | item | next step |
|----------|------|-----------|
| 🔴 P1 | [summary] | [concrete step] |
Decisions/Surprises/Patterns/Mistakes (dspm):
## Lens: Decisions/Surprises/Patterns/Mistakes
_Captured: {ISO-8601 UTC}_
_Intent: capture technical decisions, unexpected outcomes, recurring techniques, and verification failures from this phase._
### Decisions
- [item]
### Surprises
- [item]
### Patterns
- [item]
### Mistakes
- [item]
### Action Items
| priority | item | next step |
|----------|------|-----------|
| 🔴 P1 | [summary] | [concrete step] |
Conversation Analyzer (analyze):
## Lens: Conversation Analyzer
_Captured: {ISO-8601 UTC}_
_Intent: scan session transcript for frustration, correction, repetition, and validated-success signals; propose sg-rule drafts._
### Analysis Findings
| category | tool/event | pattern | context | severity |
|----------|------------|---------|---------|----------|
| frustration | Bash | `rm -rf` | User said "why delete" | high |
### Draft sg-rules
- `warn-dangerous-rm` — Event: bash, Pattern: `rm\s+-rf`, Severity: high
### Action Items
| priority | item | next step |
|----------|------|-----------|
| 🔴 P1 | [summary] | [concrete step] |
</lens_templates>
<success_criteria>
- The Phase argument must be a number, and the corresponding
.planning/phases/{NN}-*/ directory must exist.
- Second argument (if provided) is one of
ssc/dspm/analyze (case-insensitive). When omitted along with extra-lens arguments, smart default LENS_CODES_ARRAY="dspm ssc" applies automatically (dspm first, then ssc) without prompting.
- No argument → smart default (dspm+ssc) applies without prompting. Removed lens codes (
4ls/sail/5why) or any unknown code emit a stderr error message containing "no longer supported (removed in v2.9)" and exit 1 without creating or appending to a lessons file. Multi-lens invocations reject on the first dropped code encountered — no partial execution (D-07).
- Each lens output:
## Lens: {name} header + _Captured: {ISO}_ + _Intent: ..._ italic line + lens-specific fixed subheadings + ### Action Items 3-column table.
- Results are appended to
.planning/lessons/{NN}-{YYYY-MM-DD}.md.
- DSPM lens references only phase artifacts +
git log/git diff. No transcript scan.
- The analyze lens gracefully skips if TRANSCRIPT_FILE is not found.
- The
--pick flag (long form, lowercase, hyphenated) is detected anywhere in the argument list and triggers a graceful early-exit with a stderr message and exit 1, since AskUserQuestion is unavailable in this environment (LENS-03, D-01, D-02, D-05, D-17). Combining --pick with a positional lens argument (e.g. 43 ssc --pick) emits a stderr conflict error and exits 1 (D-03). Smart default (dspm+ssc) and positional lens arguments continue to work without AskUserQuestion.
- Lessons file
### Action Items rows whose priority is P1 are emitted with a 🔴 P1 prefix in the priority cell; P2 and P3 rows have no prefix (DISPLAY-01, D-08, D-09). The single 3-column table schema (priority | item | next step) is preserved.
- Each lens section emits an italic single-line lens intent statement directly after the
_Captured: {ISO}_ line, sourced from the static INTENT_LINE case mapping in Step 6 (one fixed English sentence per lens — ssc/dspm/analyze) (DISPLAY-02, D-12, D-13, D-14, D-15).
</success_criteria>