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
# D-01, D-02: --pick 토큰을 어느 위치에서든 탐지하고 제거 (positional 파싱 보존)
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
# shellcheck disable=SC2086 — intentional word splitting for positional re-set
set -- $NEW_ARGS
PHASE_RAW="${1:-}"
LENS_RAW="${2:-}"
if [ -z "$PHASE_RAW" ]; then
# --- BEGIN STATE.md Phase parsing block ---
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}')
# --- END STATE.md Phase parsing block ---
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
# D-03: --pick + positional lens 충돌 reject (silent override 금지)
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
# Phase 42 (D-02): smart default — no args → dspm+ssc, dspm first (technical core → behavior recommendation)
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
# D-05, D-06, D-17: --pick 모드 — AskUserQuestion 미지원 환경 graceful fallback
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 ordeferred to Phase Nlabel. 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 diffor 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.
- If TRANSCRIPT_FILE is empty:
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
# D-12, D-14: lens intent line (static, English, italic single-line — emitted after _Captured: line)
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"
# BODY_PRINTF — insert subheading + bullet printf lines here (confirmed content)
printf '\n### Action Items\n'
printf '| priority | item | next step |\n'
printf '|----------|------|-----------|\n'
# ACTION_ITEMS_PRINTF — insert row printf lines here
# DISPLAY-01 (D-08, D-09): P1 행은 priority 셀에 `🔴 P1` prefix로 emit. P2/P3는 prefix 없음.
# printf '| 🔴 P1 | %s | %s |\n' "$ITEM_TEXT" "$NEXT_STEP_TEXT" # P1 only
# printf '| P2 | %s | %s |\n' "$ITEM_TEXT" "$NEXT_STEP_TEXT" # P2 unchanged
# printf '| P3 | %s | %s |\n' "$ITEM_TEXT" "$NEXT_STEP_TEXT" # P3 unchanged
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
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] |
Source: gyuha/super-gsd — distributed by TomeVault.