| name | tutor-path |
| description | Show the learning path for an active or completed tutoring topic — the ordered list of prerequisite concepts with progress markers and dependency annotations. Internal-by-default; the path is generated by /tutor-start but kept hidden unless the user explicitly asks to see it. Invoke with no arg to show the current in-progress topic, or pass a slug (/tutor-path python-decorators) for a specific one. Triggers: '/tutor-path', 'show me the path', 'what are we covering', 'show me the curriculum', 'how many concepts left'. |
Tutor Path
This skill renders the learning path for a tutoring topic — the ordered list of prerequisite concepts with progress markers and dependency annotations. Per design decision D2, the path is generated internally by /tutor-start and kept hidden unless the user explicitly asks to see it; this skill is how they ask.
The format is the "with dependencies" format chosen in D2:
Path for python-decorators (6 concepts):
[✓] 1. closures
[✓] 2. first-class functions
[→] 3. wrapper functions (needs: 1, 2)
[ ] 4. @ syntax (needs: 3)
[ ] 5. decorators with arguments (needs: 3, 4)
[ ] 6. real-world patterns (needs: 4, 5)
State markers:
[✓] — acquired (passed the two-application rule)
[→] — currently being taught
[ ] — pending (not yet started)
Step 0 — Resolve the target topic
STATE="$CLAUDE_PLUGIN_ROOT/scripts/state.sh"
0a. Subject resolution
If the user provided an argument (e.g., /tutor-path python-decorators), use it as the slug.
If no argument, prefer the in-progress topic. If multiple, ask:
ACTIVE=$(bash "$STATE" get '[.topics | to_entries[] | select(.value.status == "in_progress") | .key]')
ACTIVE_COUNT=$(echo "$ACTIVE" | jq 'length')
- 0 in-progress, 0 completed: "No topics yet. Run
/tutor-start <subject> to begin one." Exit.
- 0 in-progress, ≥1 completed: ask which completed topic, or suggest
/tutor-status for the overview. Exit.
- 1 in-progress: use that slug.
- multiple in-progress: ask "Which topic's path? [list slugs]"
0b. Validate
EXISTS=$(bash "$STATE" get ".topics[\"$SLUG\"] // null")
If EXISTS is null, tell user: "No topic named $SLUG. Run /tutor-status to see what you've started." Exit.
Step 1 — Render the path
Read the concepts array and render each one with its status marker and dependency annotation. The rendering is a bash loop — clearer than one giant jq expression and easier to debug if any field is missing.
SLUG="python-decorators"
CONCEPTS=$(bash "$STATE" get ".topics[\"$SLUG\"].concepts")
N=$(echo "$CONCEPTS" | jq 'length')
echo "Path for $SLUG ($N concepts):"
echo
for i in $(seq 0 $((N - 1))); do
NAME=$(echo "$CONCEPTS" | jq -r ".[$i].name")
STATUS=$(echo "$CONCEPTS" | jq -r ".[$i].status")
POS=$((i + 1))
case "$STATUS" in
"acquired") MARKER="✓" ;;
"in_progress") MARKER="→" ;;
*) MARKER=" " ;;
esac
IS_ALL_ABOVE=$(echo "$CONCEPTS" | jq -r --argjson i $i \
'.[$i].depends_on // [] | . == [range(1; $i + 1)]')
DEPS_LEN=$(echo "$CONCEPTS" | jq -r ".[$i].depends_on // [] | length")
if [ "$DEPS_LEN" -eq 0 ]; then
printf " [%s] %d. %s\n" "$MARKER" "$POS" "$NAME"
elif [ "$IS_ALL_ABOVE" = "true" ] && [ "$DEPS_LEN" -gt 1 ]; then
printf " [%s] %d. %s (needs: all above)\n" "$MARKER" "$POS" "$NAME"
else
DEPS=$(echo "$CONCEPTS" | jq -r ".[$i].depends_on | map(tostring) | join(\", \")")
printf " [%s] %d. %s (needs: %s)\n" "$MARKER" "$POS" "$NAME" "$DEPS"
fi
done
The "all above" cosmetic kicks in only when:
- The dependency list is exactly
[1, 2, ..., POS-1] (every prior concept), AND
- There are at least 2 dependencies (otherwise "needs: 1" is clearer than "needs: all above")
Step 2 — Print
The output of the loop in Step 1 goes straight to the user, unmodified. Don't add a header about "here's your learning path" — the table is self-explanatory.
After the table, a single closing line if the topic has progress:
ACQUIRED=$(echo "$CONCEPTS" | jq '[.[] | select(.status == "acquired")] | length')
echo
echo "Progress: $ACQUIRED/$N concepts acquired."
For a completed topic, the line is Progress: $N/$N concepts acquired (topic complete).
Hard rules
- Don't print this proactively. This skill only renders when the user explicitly asks. The path being internal-by-default is a deliberate design call from D2 — surfacing it without being asked frames the session as a checklist and undermines the discovery framing of the personas.
- Don't editorialize the path. Render the table; don't add "this looks good, you're making progress" or other persona-voice commentary. That belongs in the dialogue, not in this state reader.
- Show the table even if 0 concepts are acquired. The user might be asking right at the start of a topic. That's fine — they'll see all
[ ] markers, which is honest.
- Don't infer dependencies that aren't in the state. Render exactly what's in
depends_on. If a concept has an empty depends_on, show no annotation — don't make one up.
- Don't sort or rearrange. The array's order IS the teaching order. Render in array order, period.
Edge cases
- Concept names with special characters. The rendering uses
printf "%s" so any name renders as-is. No escaping needed.
- A concept with
depends_on: [N] where N is itself. Shouldn't happen if /tutor-start is well-behaved, but if it does, render literally — don't try to recover.
- A topic with 0 concepts. Could happen if
/tutor-start aborted partway. Tell user: "$SLUG has no concepts yet — the path generation may have failed. Try /tutor-start $SLUG again."
- The
depends_on field is missing entirely (old state from before this field existed). Treat as [] — render with no annotation. The // [] fallback in jq handles this.