@alexismunozdev/claude-session-topics 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -11
- package/hooks/hooks.json +39 -0
- package/package.json +2 -1
- package/scripts/auto-allow.sh +25 -0
- package/scripts/auto-setup.sh +68 -0
- package/skills/set-topic/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ Supported colors: `red`, `green`, `yellow`, `blue`, `magenta` (default), `cyan`,
|
|
|
20
20
|
|
|
21
21
|
## What it does
|
|
22
22
|
|
|
23
|
-
-
|
|
23
|
+
- A Stop hook sets the initial topic automatically after Claude's first response (no model tokens spent)
|
|
24
|
+
- The auto-topic skill refines the topic when the conversation shifts
|
|
24
25
|
- Shows the topic in the Claude Code statusline (`◆ Topic`)
|
|
25
26
|
- Change the topic anytime with `/set-topic`
|
|
26
27
|
- Composes with existing statusline plugins (doesn't overwrite)
|
|
@@ -28,10 +29,11 @@ Supported colors: `red`, `green`, `yellow`, `blue`, `magenta` (default), `cyan`,
|
|
|
28
29
|
## What the installer configures
|
|
29
30
|
|
|
30
31
|
1. Copies the statusline script to `~/.claude/session-topics/`
|
|
31
|
-
2.
|
|
32
|
-
3.
|
|
33
|
-
4.
|
|
34
|
-
5.
|
|
32
|
+
2. Installs the Stop hook (`auto-topic-hook.sh`) that sets the initial topic
|
|
33
|
+
3. Configures `statusLine` in `~/.claude/settings.json`
|
|
34
|
+
4. Adds bash permission for the script
|
|
35
|
+
5. Installs `auto-topic` and `set-topic` skills to `~/.claude/skills/`
|
|
36
|
+
6. If you already have a statusline, creates a wrapper that shows both
|
|
35
37
|
|
|
36
38
|
## Requirements
|
|
37
39
|
|
|
@@ -60,7 +62,7 @@ The default topic color is bold magenta. Three ways to change it:
|
|
|
60
62
|
|
|
61
63
|
### Auto-topic (automatic)
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
After Claude's first response, a Stop hook extracts a 2-4 word topic from your first message using lightweight heuristics (no model tokens spent). The auto-topic skill then monitors the conversation and updates the topic when you shift to a different subject.
|
|
64
66
|
|
|
65
67
|
### /set-topic (manual)
|
|
66
68
|
|
|
@@ -76,16 +78,18 @@ Change the topic at any time:
|
|
|
76
78
|
```
|
|
77
79
|
Session starts
|
|
78
80
|
|
|
|
79
|
-
|
|
81
|
+
Claude sends first response
|
|
80
82
|
|
|
|
81
|
-
|
|
83
|
+
Stop hook (auto-topic-hook.sh) extracts topic from first user message
|
|
82
84
|
|
|
|
83
|
-
|
|
85
|
+
Writes topic to ~/.claude/session-topics/${SESSION_ID}
|
|
84
86
|
|
|
|
85
|
-
|
|
87
|
+
auto-topic skill monitors for conversation shifts and updates topic
|
|
88
|
+
|
|
|
89
|
+
Statusline script reads the topic file → displays: ◆ Topic
|
|
86
90
|
```
|
|
87
91
|
|
|
88
|
-
The statusline script receives the session ID via stdin JSON, reads the corresponding topic file, and renders it with ANSI color codes.
|
|
92
|
+
The Stop hook runs after each model response and uses heuristics to extract the initial topic from the transcript. On subsequent messages, the auto-topic skill handles topic updates when the conversation shifts. The statusline script receives the session ID via stdin JSON, reads the corresponding topic file, and renders it with ANSI color codes.
|
|
89
93
|
|
|
90
94
|
## Uninstall
|
|
91
95
|
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "COLOR='${user_config.topic_color}'; [ -n \"$COLOR\" ] && mkdir -p \"$HOME/.claude/session-topics\" && echo \"$COLOR\" > \"$HOME/.claude/session-topics/.color-config\" || true"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"type": "command",
|
|
12
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/auto-setup.sh\" || true"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"PermissionRequest": [
|
|
18
|
+
{
|
|
19
|
+
"matcher": "Bash",
|
|
20
|
+
"hooks": [
|
|
21
|
+
{
|
|
22
|
+
"type": "command",
|
|
23
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/auto-allow.sh\" || true"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"Stop": [
|
|
29
|
+
{
|
|
30
|
+
"hooks": [
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "bash \"${CLAUDE_PLUGIN_ROOT}/scripts/auto-topic-hook.sh\" || true"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexismunozdev/claude-session-topics",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Session topics for Claude Code — auto-set and display a topic in the statusline, change anytime with /set-topic",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claude-session-topics": "bin/install.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"bin/",
|
|
10
|
+
"hooks/",
|
|
10
11
|
"scripts/",
|
|
11
12
|
"skills/",
|
|
12
13
|
"LICENSE",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Auto-approve Bash commands containing "session-topics" (PermissionRequest hook)
|
|
3
|
+
# Writes the allow rule to userSettings so future requests skip the prompt
|
|
4
|
+
|
|
5
|
+
input=$(cat)
|
|
6
|
+
CMD=$(echo "$input" | jq -r '.tool_input.command // ""')
|
|
7
|
+
|
|
8
|
+
if echo "$CMD" | grep -q 'session-topics'; then
|
|
9
|
+
cat <<'EOF'
|
|
10
|
+
{
|
|
11
|
+
"hookSpecificOutput": {
|
|
12
|
+
"hookEventName": "PermissionRequest",
|
|
13
|
+
"permissionDecision": "allow",
|
|
14
|
+
"updatedPermissions": [
|
|
15
|
+
{
|
|
16
|
+
"type": "addRules",
|
|
17
|
+
"rules": [{"toolName": "Bash", "ruleContent": "*session-topics*"}],
|
|
18
|
+
"behavior": "allow",
|
|
19
|
+
"destination": "userSettings"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
EOF
|
|
25
|
+
fi
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Auto-configure statusline on first run (SessionStart hook)
|
|
3
|
+
# Handles two cases:
|
|
4
|
+
# 1. No statusline configured -> set plugin's statusline.sh directly
|
|
5
|
+
# 2. Existing custom statusline -> generate a wrapper that prepends topic to original output
|
|
6
|
+
|
|
7
|
+
SETTINGS="$HOME/.claude/settings.json"
|
|
8
|
+
TOPIC_DIR="$HOME/.claude/session-topics"
|
|
9
|
+
WRAPPER="$TOPIC_DIR/wrapper-statusline.sh"
|
|
10
|
+
STABLE_SL="$TOPIC_DIR/plugin-statusline.sh"
|
|
11
|
+
ORIG_CMD_FILE="$TOPIC_DIR/.original-statusline-cmd"
|
|
12
|
+
|
|
13
|
+
[ ! -f "$SETTINGS" ] && exit 0
|
|
14
|
+
|
|
15
|
+
# Find the plugin's statusline script via CLAUDE_PLUGIN_ROOT (set by hooks system)
|
|
16
|
+
PLUGIN_SL="${CLAUDE_PLUGIN_ROOT}/scripts/statusline.sh"
|
|
17
|
+
[ ! -f "$PLUGIN_SL" ] && exit 0
|
|
18
|
+
|
|
19
|
+
mkdir -p "$TOPIC_DIR"
|
|
20
|
+
|
|
21
|
+
# Always refresh the stable copy (keeps it up-to-date across plugin updates)
|
|
22
|
+
cp "$PLUGIN_SL" "$STABLE_SL"
|
|
23
|
+
chmod +x "$STABLE_SL"
|
|
24
|
+
|
|
25
|
+
CURRENT_CMD=$(jq -r '.statusLine.command // ""' "$SETTINGS" 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
# Already integrated — skip (but the copy above still refreshes)
|
|
28
|
+
echo "$CURRENT_CMD" | grep -q 'session-topics' && exit 0
|
|
29
|
+
|
|
30
|
+
HAS_STATUSLINE=$(jq 'has("statusLine")' "$SETTINGS" 2>/dev/null)
|
|
31
|
+
|
|
32
|
+
if [ "$HAS_STATUSLINE" = "true" ] && [ -n "$CURRENT_CMD" ]; then
|
|
33
|
+
# Case 2: User has a custom statusline — generate wrapper
|
|
34
|
+
echo "$CURRENT_CMD" > "$ORIG_CMD_FILE"
|
|
35
|
+
|
|
36
|
+
cat > "$WRAPPER" << 'WRAPPER_EOF'
|
|
37
|
+
#!/bin/bash
|
|
38
|
+
input=$(cat)
|
|
39
|
+
|
|
40
|
+
# Run the plugin's topic statusline (stable copy refreshed each session)
|
|
41
|
+
TOPIC_OUTPUT=""
|
|
42
|
+
if [ -f "$HOME/.claude/session-topics/plugin-statusline.sh" ]; then
|
|
43
|
+
TOPIC_OUTPUT=$(echo "$input" | bash "$HOME/.claude/session-topics/plugin-statusline.sh" 2>/dev/null || echo "")
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Run the user's original statusline command
|
|
47
|
+
ORIG_CMD=$(cat "$HOME/.claude/session-topics/.original-statusline-cmd" 2>/dev/null || echo "")
|
|
48
|
+
ORIG_OUTPUT=""
|
|
49
|
+
if [ -n "$ORIG_CMD" ]; then
|
|
50
|
+
ORIG_OUTPUT=$(echo "$input" | bash -c "$ORIG_CMD" 2>/dev/null || echo "")
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Combine: topic | original
|
|
54
|
+
if [ -n "$TOPIC_OUTPUT" ] && [ -n "$ORIG_OUTPUT" ]; then
|
|
55
|
+
echo -e "${TOPIC_OUTPUT} | ${ORIG_OUTPUT}"
|
|
56
|
+
elif [ -n "$TOPIC_OUTPUT" ]; then
|
|
57
|
+
echo -e "${TOPIC_OUTPUT}"
|
|
58
|
+
elif [ -n "$ORIG_OUTPUT" ]; then
|
|
59
|
+
echo -e "${ORIG_OUTPUT}"
|
|
60
|
+
fi
|
|
61
|
+
WRAPPER_EOF
|
|
62
|
+
chmod +x "$WRAPPER"
|
|
63
|
+
|
|
64
|
+
jq --arg cmd "bash \"$WRAPPER\"" '.statusLine.command = $cmd' "$SETTINGS" > "${SETTINGS}.tmp" && mv "${SETTINGS}.tmp" "$SETTINGS"
|
|
65
|
+
else
|
|
66
|
+
# Case 1: No statusline at all — use stable copy directly
|
|
67
|
+
jq --arg cmd "bash \"$STABLE_SL\"" '.statusLine = {"type": "command", "command": $cmd}' "$SETTINGS" > "${SETTINGS}.tmp" && mv "${SETTINGS}.tmp" "$SETTINGS"
|
|
68
|
+
fi
|
|
@@ -28,7 +28,7 @@ if [ -z "$SESSION_ID" ]; then
|
|
|
28
28
|
echo "Error: No active session found. The statusline must run at least once before setting a topic."
|
|
29
29
|
exit 1
|
|
30
30
|
fi
|
|
31
|
-
TOPIC=$(printf '%s' "$ARGUMENTS" |
|
|
31
|
+
TOPIC=$(printf '%s' "$ARGUMENTS" | sed "s/[^a-zA-Z0-9àáâãäåèéêëìíîïòóôõöùúûüýÿñçÀÁÂÃÄÅÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝÑÇ .,:!?'-]//g" | cut -c1-100)
|
|
32
32
|
if [ -z "$TOPIC" ]; then
|
|
33
33
|
echo "Error: Topic text is empty after sanitization."
|
|
34
34
|
exit 1
|