uv-suite 0.26.0 → 0.26.2
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/hooks/confirm-prompt.sh +51 -0
- package/hooks/session-label-nag.sh +63 -0
- package/hooks/session-meta.sh +121 -0
- package/hooks/session-start.sh +37 -9
- package/hooks/status-line.sh +50 -25
- package/hooks/watchtower-send.sh +44 -12
- package/install.sh +5 -3
- package/package.json +1 -1
- package/personas/auto.json +77 -19
- package/personas/professional.json +29 -7
- package/personas/spike.json +72 -18
- package/personas/sport.json +72 -18
- package/skills/checkpoint/SKILL.md +17 -5
- package/skills/confirm/SKILL.md +35 -0
- package/skills/restore/SKILL.md +5 -2
- package/skills/session-init/SKILL.md +45 -0
- package/uv.sh +84 -14
- package/watchtower/dashboard.html +137 -22
- package/watchtower/events.json +1 -22
- package/watchtower/server.js +76 -29
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
3
|
"effort": "high",
|
|
4
|
-
|
|
5
4
|
"permissions": {
|
|
6
5
|
"allow": [
|
|
7
6
|
"Read(*)",
|
|
@@ -46,7 +45,6 @@
|
|
|
46
45
|
"Bash(git reset --hard origin/*)"
|
|
47
46
|
]
|
|
48
47
|
},
|
|
49
|
-
|
|
50
48
|
"hooks": {
|
|
51
49
|
"SessionStart": [
|
|
52
50
|
{
|
|
@@ -70,6 +68,16 @@
|
|
|
70
68
|
{
|
|
71
69
|
"matcher": "*",
|
|
72
70
|
"hooks": [
|
|
71
|
+
{
|
|
72
|
+
"type": "command",
|
|
73
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/confirm-prompt.sh",
|
|
74
|
+
"timeout": 3
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "command",
|
|
78
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-label-nag.sh",
|
|
79
|
+
"timeout": 3
|
|
80
|
+
},
|
|
73
81
|
{
|
|
74
82
|
"type": "command",
|
|
75
83
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh UserPromptSubmit",
|
|
@@ -83,7 +91,12 @@
|
|
|
83
91
|
{
|
|
84
92
|
"matcher": "*",
|
|
85
93
|
"hooks": [
|
|
86
|
-
{
|
|
94
|
+
{
|
|
95
|
+
"type": "command",
|
|
96
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest",
|
|
97
|
+
"timeout": 2,
|
|
98
|
+
"async": true
|
|
99
|
+
}
|
|
87
100
|
]
|
|
88
101
|
}
|
|
89
102
|
],
|
|
@@ -91,7 +104,12 @@
|
|
|
91
104
|
{
|
|
92
105
|
"matcher": "*",
|
|
93
106
|
"hooks": [
|
|
94
|
-
{
|
|
107
|
+
{
|
|
108
|
+
"type": "command",
|
|
109
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification",
|
|
110
|
+
"timeout": 2,
|
|
111
|
+
"async": true
|
|
112
|
+
}
|
|
95
113
|
]
|
|
96
114
|
}
|
|
97
115
|
],
|
|
@@ -99,7 +117,12 @@
|
|
|
99
117
|
{
|
|
100
118
|
"matcher": "*",
|
|
101
119
|
"hooks": [
|
|
102
|
-
{
|
|
120
|
+
{
|
|
121
|
+
"type": "command",
|
|
122
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure",
|
|
123
|
+
"timeout": 2,
|
|
124
|
+
"async": true
|
|
125
|
+
}
|
|
103
126
|
]
|
|
104
127
|
}
|
|
105
128
|
],
|
|
@@ -176,9 +199,8 @@
|
|
|
176
199
|
}
|
|
177
200
|
]
|
|
178
201
|
},
|
|
179
|
-
|
|
180
202
|
"statusLine": {
|
|
181
203
|
"type": "command",
|
|
182
204
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/status-line.sh"
|
|
183
205
|
}
|
|
184
|
-
}
|
|
206
|
+
}
|
package/personas/spike.json
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
3
|
"model": "claude-opus-4-6",
|
|
4
4
|
"effort": "max",
|
|
5
|
-
|
|
6
5
|
"permissions": {
|
|
7
6
|
"allow": [
|
|
8
7
|
"Read(*)",
|
|
@@ -34,14 +33,22 @@
|
|
|
34
33
|
"Bash(npx *)"
|
|
35
34
|
]
|
|
36
35
|
},
|
|
37
|
-
|
|
38
36
|
"hooks": {
|
|
39
37
|
"SessionStart": [
|
|
40
38
|
{
|
|
41
39
|
"matcher": "*",
|
|
42
40
|
"hooks": [
|
|
43
|
-
{
|
|
44
|
-
|
|
41
|
+
{
|
|
42
|
+
"type": "command",
|
|
43
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh",
|
|
44
|
+
"timeout": 5
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"type": "command",
|
|
48
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh SessionStart",
|
|
49
|
+
"timeout": 2,
|
|
50
|
+
"async": true
|
|
51
|
+
}
|
|
45
52
|
]
|
|
46
53
|
}
|
|
47
54
|
],
|
|
@@ -49,30 +56,74 @@
|
|
|
49
56
|
{
|
|
50
57
|
"matcher": "*",
|
|
51
58
|
"hooks": [
|
|
52
|
-
{
|
|
59
|
+
{
|
|
60
|
+
"type": "command",
|
|
61
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/confirm-prompt.sh",
|
|
62
|
+
"timeout": 3
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"type": "command",
|
|
66
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-label-nag.sh",
|
|
67
|
+
"timeout": 3
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh UserPromptSubmit",
|
|
72
|
+
"timeout": 2,
|
|
73
|
+
"async": true
|
|
74
|
+
}
|
|
53
75
|
]
|
|
54
76
|
}
|
|
55
77
|
],
|
|
56
78
|
"PermissionRequest": [
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
79
|
+
{
|
|
80
|
+
"matcher": "*",
|
|
81
|
+
"hooks": [
|
|
82
|
+
{
|
|
83
|
+
"type": "command",
|
|
84
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest",
|
|
85
|
+
"timeout": 2,
|
|
86
|
+
"async": true
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
60
90
|
],
|
|
61
91
|
"Notification": [
|
|
62
|
-
{
|
|
63
|
-
|
|
64
|
-
|
|
92
|
+
{
|
|
93
|
+
"matcher": "*",
|
|
94
|
+
"hooks": [
|
|
95
|
+
{
|
|
96
|
+
"type": "command",
|
|
97
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification",
|
|
98
|
+
"timeout": 2,
|
|
99
|
+
"async": true
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
}
|
|
65
103
|
],
|
|
66
104
|
"PostToolUseFailure": [
|
|
67
|
-
{
|
|
68
|
-
|
|
69
|
-
|
|
105
|
+
{
|
|
106
|
+
"matcher": "*",
|
|
107
|
+
"hooks": [
|
|
108
|
+
{
|
|
109
|
+
"type": "command",
|
|
110
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure",
|
|
111
|
+
"timeout": 2,
|
|
112
|
+
"async": true
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
70
116
|
],
|
|
71
117
|
"PostToolUse": [
|
|
72
118
|
{
|
|
73
119
|
"matcher": "*",
|
|
74
120
|
"hooks": [
|
|
75
|
-
{
|
|
121
|
+
{
|
|
122
|
+
"type": "command",
|
|
123
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUse",
|
|
124
|
+
"timeout": 2,
|
|
125
|
+
"async": true
|
|
126
|
+
}
|
|
76
127
|
]
|
|
77
128
|
},
|
|
78
129
|
{
|
|
@@ -91,14 +142,17 @@
|
|
|
91
142
|
{
|
|
92
143
|
"matcher": "*",
|
|
93
144
|
"hooks": [
|
|
94
|
-
{
|
|
145
|
+
{
|
|
146
|
+
"type": "command",
|
|
147
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-end.sh",
|
|
148
|
+
"timeout": 10
|
|
149
|
+
}
|
|
95
150
|
]
|
|
96
151
|
}
|
|
97
152
|
]
|
|
98
153
|
},
|
|
99
|
-
|
|
100
154
|
"statusLine": {
|
|
101
155
|
"type": "command",
|
|
102
156
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/status-line.sh"
|
|
103
157
|
}
|
|
104
|
-
}
|
|
158
|
+
}
|
package/personas/sport.json
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
3
3
|
"model": "claude-sonnet-4-6",
|
|
4
4
|
"effort": "high",
|
|
5
|
-
|
|
6
5
|
"permissions": {
|
|
7
6
|
"allow": [
|
|
8
7
|
"Read(*)",
|
|
@@ -20,14 +19,22 @@
|
|
|
20
19
|
"Bash(git *)"
|
|
21
20
|
]
|
|
22
21
|
},
|
|
23
|
-
|
|
24
22
|
"hooks": {
|
|
25
23
|
"SessionStart": [
|
|
26
24
|
{
|
|
27
25
|
"matcher": "*",
|
|
28
26
|
"hooks": [
|
|
29
|
-
{
|
|
30
|
-
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh",
|
|
30
|
+
"timeout": 5
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"type": "command",
|
|
34
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh SessionStart",
|
|
35
|
+
"timeout": 2,
|
|
36
|
+
"async": true
|
|
37
|
+
}
|
|
31
38
|
]
|
|
32
39
|
}
|
|
33
40
|
],
|
|
@@ -35,30 +42,74 @@
|
|
|
35
42
|
{
|
|
36
43
|
"matcher": "*",
|
|
37
44
|
"hooks": [
|
|
38
|
-
{
|
|
45
|
+
{
|
|
46
|
+
"type": "command",
|
|
47
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/confirm-prompt.sh",
|
|
48
|
+
"timeout": 3
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "command",
|
|
52
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-label-nag.sh",
|
|
53
|
+
"timeout": 3
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"type": "command",
|
|
57
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh UserPromptSubmit",
|
|
58
|
+
"timeout": 2,
|
|
59
|
+
"async": true
|
|
60
|
+
}
|
|
39
61
|
]
|
|
40
62
|
}
|
|
41
63
|
],
|
|
42
64
|
"PermissionRequest": [
|
|
43
|
-
{
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
{
|
|
66
|
+
"matcher": "*",
|
|
67
|
+
"hooks": [
|
|
68
|
+
{
|
|
69
|
+
"type": "command",
|
|
70
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest",
|
|
71
|
+
"timeout": 2,
|
|
72
|
+
"async": true
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
46
76
|
],
|
|
47
77
|
"Notification": [
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
{
|
|
79
|
+
"matcher": "*",
|
|
80
|
+
"hooks": [
|
|
81
|
+
{
|
|
82
|
+
"type": "command",
|
|
83
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification",
|
|
84
|
+
"timeout": 2,
|
|
85
|
+
"async": true
|
|
86
|
+
}
|
|
87
|
+
]
|
|
88
|
+
}
|
|
51
89
|
],
|
|
52
90
|
"PostToolUseFailure": [
|
|
53
|
-
{
|
|
54
|
-
|
|
55
|
-
|
|
91
|
+
{
|
|
92
|
+
"matcher": "*",
|
|
93
|
+
"hooks": [
|
|
94
|
+
{
|
|
95
|
+
"type": "command",
|
|
96
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure",
|
|
97
|
+
"timeout": 2,
|
|
98
|
+
"async": true
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
56
102
|
],
|
|
57
103
|
"PostToolUse": [
|
|
58
104
|
{
|
|
59
105
|
"matcher": "*",
|
|
60
106
|
"hooks": [
|
|
61
|
-
{
|
|
107
|
+
{
|
|
108
|
+
"type": "command",
|
|
109
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUse",
|
|
110
|
+
"timeout": 2,
|
|
111
|
+
"async": true
|
|
112
|
+
}
|
|
62
113
|
]
|
|
63
114
|
},
|
|
64
115
|
{
|
|
@@ -77,14 +128,17 @@
|
|
|
77
128
|
{
|
|
78
129
|
"matcher": "*",
|
|
79
130
|
"hooks": [
|
|
80
|
-
{
|
|
131
|
+
{
|
|
132
|
+
"type": "command",
|
|
133
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-end.sh",
|
|
134
|
+
"timeout": 10
|
|
135
|
+
}
|
|
81
136
|
]
|
|
82
137
|
}
|
|
83
138
|
]
|
|
84
139
|
},
|
|
85
|
-
|
|
86
140
|
"statusLine": {
|
|
87
141
|
"type": "command",
|
|
88
142
|
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/status-line.sh"
|
|
89
143
|
}
|
|
90
|
-
}
|
|
144
|
+
}
|
|
@@ -12,23 +12,35 @@ allowed-tools:
|
|
|
12
12
|
- Bash(git status *)
|
|
13
13
|
- Bash(git diff *)
|
|
14
14
|
- Bash(git log *)
|
|
15
|
+
- Bash(git rev-parse *)
|
|
15
16
|
- Bash(date *)
|
|
16
17
|
- Bash(ls *)
|
|
18
|
+
- Bash(mkdir *)
|
|
19
|
+
- Bash(echo *)
|
|
17
20
|
---
|
|
18
21
|
|
|
19
|
-
##
|
|
22
|
+
## Resolve checkpoint directory
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
Anchor checkpoints to `$CLAUDE_PROJECT_DIR` (or the git repo root, or the current
|
|
25
|
+
working dir as a last resort) so `/checkpoint` and `/restore` always agree, no matter
|
|
26
|
+
which subdirectory the session was launched from.
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
!`DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/uv-out/checkpoints"; mkdir -p "$DIR"; echo "$DIR"`
|
|
24
29
|
|
|
25
|
-
|
|
30
|
+
Use the absolute path printed above as `<checkpoint-dir>` for every file path below.
|
|
31
|
+
|
|
32
|
+
## Write a checkpoint
|
|
33
|
+
|
|
34
|
+
Write a file named `<checkpoint-dir>/YYYY-MM-DD-HHMM.md` using the current timestamp.
|
|
35
|
+
|
|
36
|
+
Also write/overwrite `<checkpoint-dir>/latest.md` with the same content, so the next
|
|
37
|
+
session's `/restore` always finds the freshest state.
|
|
26
38
|
|
|
27
39
|
## Label
|
|
28
40
|
|
|
29
41
|
$ARGUMENTS
|
|
30
42
|
|
|
31
|
-
If a label was provided, include it in the filename:
|
|
43
|
+
If a label was provided, include it in the filename: `<checkpoint-dir>/YYYY-MM-DD-HHMM-[label].md`
|
|
32
44
|
|
|
33
45
|
## What to capture
|
|
34
46
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: confirm
|
|
3
|
+
description: >
|
|
4
|
+
Toggle confirm-mode (reframe-and-confirm long prompts) or change the word threshold.
|
|
5
|
+
When confirm-mode is on and a user prompt exceeds the threshold, the assistant will
|
|
6
|
+
restate the request and wait for confirmation before doing any work.
|
|
7
|
+
argument-hint: "[on|off|<number>|status]"
|
|
8
|
+
user-invocable: true
|
|
9
|
+
allowed-tools:
|
|
10
|
+
- Bash(mkdir *)
|
|
11
|
+
- Bash(echo *)
|
|
12
|
+
- Bash(cat *)
|
|
13
|
+
- Bash(printf *)
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Apply /confirm $ARGUMENTS
|
|
17
|
+
|
|
18
|
+
!`STATE_DIR="${CLAUDE_PROJECT_DIR:-.}/.uv-suite-state"; mkdir -p "$STATE_DIR"; MODE_FILE="$STATE_DIR/confirm-mode.txt"; THRESH_FILE="$STATE_DIR/confirm-threshold.txt"; ARG=$(printf '%s' "$ARGUMENTS" | tr -d '[:space:]'); current_mode() { cat "$MODE_FILE" 2>/dev/null || echo "on"; }; current_thresh() { cat "$THRESH_FILE" 2>/dev/null || echo "50"; }; case "$ARG" in on) echo "on" > "$MODE_FILE"; echo "Confirm mode: ON (threshold: $(current_thresh) words)";; off) echo "off" > "$MODE_FILE"; echo "Confirm mode: OFF";; ''|status) echo "Confirm mode: $(current_mode) (threshold: $(current_thresh) words)";; *) if printf '%s' "$ARG" | grep -qE '^[0-9]+$'; then echo "$ARG" > "$THRESH_FILE"; echo "Threshold set to $ARG words (mode: $(current_mode))"; else echo "Usage: /confirm [on | off | <number> | status]"; fi;; esac`
|
|
19
|
+
|
|
20
|
+
## Instructions
|
|
21
|
+
|
|
22
|
+
Show the user the line of output above as the response — that line is the confirmation
|
|
23
|
+
that the toggle took effect. Do not add commentary. The change applies to the very
|
|
24
|
+
next user prompt; no restart needed.
|
|
25
|
+
|
|
26
|
+
## What this controls
|
|
27
|
+
|
|
28
|
+
- `on` / `off` — enable or disable the reframe-and-confirm behavior driven by
|
|
29
|
+
`hooks/confirm-prompt.sh` on every UserPromptSubmit event.
|
|
30
|
+
- `<number>` — set the word-count threshold above which prompts trigger a confirmation
|
|
31
|
+
step. Slash commands (`/foo ...`) are always exempt.
|
|
32
|
+
- `status` (or no argument) — print the current mode and threshold.
|
|
33
|
+
|
|
34
|
+
State lives in `${CLAUDE_PROJECT_DIR}/.uv-suite-state/confirm-mode.txt` and
|
|
35
|
+
`confirm-threshold.txt`. Defaults if missing: mode `on`, threshold `50`.
|
package/skills/restore/SKILL.md
CHANGED
|
@@ -7,15 +7,18 @@ user-invocable: true
|
|
|
7
7
|
allowed-tools:
|
|
8
8
|
- Read(*)
|
|
9
9
|
- Bash(ls *)
|
|
10
|
+
- Bash(cat *)
|
|
11
|
+
- Bash(grep *)
|
|
12
|
+
- Bash(git rev-parse *)
|
|
10
13
|
---
|
|
11
14
|
|
|
12
15
|
## Latest checkpoint
|
|
13
16
|
|
|
14
|
-
!`
|
|
17
|
+
!`DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/uv-out/checkpoints"; if [ -f "$DIR/latest.md" ]; then cat "$DIR/latest.md"; else echo "No checkpoint found at $DIR. Run /checkpoint to create one."; fi`
|
|
15
18
|
|
|
16
19
|
## All checkpoints
|
|
17
20
|
|
|
18
|
-
!`
|
|
21
|
+
!`DIR="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/uv-out/checkpoints"; if [ -d "$DIR" ]; then matches=$(ls -la "$DIR"/ 2>/dev/null | grep '\.md$' | tail -10); if [ -n "$matches" ]; then echo "$matches"; else echo "No checkpoints in $DIR"; fi; else echo "No checkpoints directory at $DIR"; fi`
|
|
19
22
|
|
|
20
23
|
## Instructions
|
|
21
24
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: session-init
|
|
3
|
+
description: >
|
|
4
|
+
Label or relabel the current UV Suite session — sets name, kind (long-running
|
|
5
|
+
vs outcome), purpose, and priority (low/med/high). Metadata appears in the
|
|
6
|
+
watchtower dashboard. Use when you skipped the launcher prompt or want to
|
|
7
|
+
rename a session mid-flight.
|
|
8
|
+
argument-hint: "<name> | --kind long|outcome | --purpose <text> | --priority low|med|high | show | clear"
|
|
9
|
+
user-invocable: true
|
|
10
|
+
allowed-tools:
|
|
11
|
+
- Bash("$CLAUDE_PROJECT_DIR"/.claude/hooks/session-meta.sh *)
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Apply /session-init $ARGUMENTS
|
|
15
|
+
|
|
16
|
+
!`HELPER="$CLAUDE_PROJECT_DIR/.claude/hooks/session-meta.sh"; ARGS="$ARGUMENTS"; if [ -z "$ARGS" ] || [ "$ARGS" = "show" ]; then "$HELPER" show; elif [ "$ARGS" = "clear" ]; then "$HELPER" clear; else FIRST=$(printf '%s' "$ARGS" | awk '{print $1}'); REST=$(printf '%s' "$ARGS" | sed 's/^[^ ]* *//'); case "$FIRST" in --kind) "$HELPER" set-kind "$REST" ;; --priority) "$HELPER" set-priority "$REST" ;; --purpose) "$HELPER" set-purpose "$REST" ;; --name) "$HELPER" set-name "$REST" ;; *) "$HELPER" set-name "$ARGS" ;; esac; fi`
|
|
17
|
+
|
|
18
|
+
## Instructions
|
|
19
|
+
|
|
20
|
+
Show the user the output of the command above. Do not add commentary — the
|
|
21
|
+
command itself confirms what changed. The dashboard reflects changes within
|
|
22
|
+
a few seconds (next event refreshes session metadata).
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
/session-init show current values
|
|
28
|
+
/session-init show show current values
|
|
29
|
+
/session-init <free-text name> set the name (default action)
|
|
30
|
+
/session-init --kind long set kind: long-running
|
|
31
|
+
/session-init --kind outcome set kind: outcome
|
|
32
|
+
/session-init --priority high set priority: low | med | high
|
|
33
|
+
/session-init --purpose <free text> set the purpose
|
|
34
|
+
/session-init clear clear name, kind, purpose, priority
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
- The session id is generated by `uv.sh` at launch and exported as
|
|
40
|
+
`UVS_SESSION_ID`. If you launched `claude` directly, this falls back
|
|
41
|
+
to an `ad-hoc-<timestamp>` id.
|
|
42
|
+
- Metadata lives at `.uv-suite-state/sessions/<id>.json`.
|
|
43
|
+
- Persona is captured at launch time and is not editable from here —
|
|
44
|
+
re-launch with a different `uv` persona to change it.
|
|
45
|
+
- Each invocation sets one field at a time. Run multiple times to set more.
|
package/uv.sh
CHANGED
|
@@ -41,6 +41,12 @@ case "$1" in
|
|
|
41
41
|
echo " pro Production code (all hooks, all guardrails)"
|
|
42
42
|
echo " auto Fully autonomous (max effort, everything approved)"
|
|
43
43
|
echo ""
|
|
44
|
+
echo " Session metadata:"
|
|
45
|
+
echo " On launch you'll be prompted for name, kind, purpose, and"
|
|
46
|
+
echo " priority. Press Enter to skip any field; you'll be reminded"
|
|
47
|
+
echo " until the session is named. Use /session-init to relabel."
|
|
48
|
+
echo " Set UVS_NO_PROMPT=1 to suppress prompts entirely."
|
|
49
|
+
echo ""
|
|
44
50
|
exit 0
|
|
45
51
|
;;
|
|
46
52
|
"")
|
|
@@ -73,6 +79,76 @@ case "$PERSONA" in
|
|
|
73
79
|
auto) LABEL="Auto — autonomous (max, everything approved)" ;;
|
|
74
80
|
esac
|
|
75
81
|
|
|
82
|
+
# --- Session metadata: generate id and prompt for label ---
|
|
83
|
+
PROJECT_DIR="$(pwd)"
|
|
84
|
+
STATE_DIR="$PROJECT_DIR/.uv-suite-state"
|
|
85
|
+
SESSIONS_DIR="$STATE_DIR/sessions"
|
|
86
|
+
mkdir -p "$SESSIONS_DIR"
|
|
87
|
+
|
|
88
|
+
if command -v uuidgen >/dev/null 2>&1; then
|
|
89
|
+
UVS_SESSION_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
|
90
|
+
else
|
|
91
|
+
UVS_SESSION_ID="uvs-$(date +%s)-$$"
|
|
92
|
+
fi
|
|
93
|
+
export UVS_SESSION_ID
|
|
94
|
+
|
|
95
|
+
UVS_NAME=""
|
|
96
|
+
UVS_KIND=""
|
|
97
|
+
UVS_PURPOSE=""
|
|
98
|
+
UVS_PRIORITY=""
|
|
99
|
+
|
|
100
|
+
# Prompt only if attached to a TTY and not explicitly suppressed
|
|
101
|
+
if [ -t 0 ] && [ -z "$UVS_NO_PROMPT" ]; then
|
|
102
|
+
echo ""
|
|
103
|
+
echo "Label this session (Enter to skip — you'll be reminded):"
|
|
104
|
+
read -r -p " name: " UVS_NAME
|
|
105
|
+
read -r -p " kind [long/outcome]: " UVS_KIND_RAW
|
|
106
|
+
read -r -p " purpose: " UVS_PURPOSE
|
|
107
|
+
read -r -p " priority [low/med/high]: " UVS_PRIORITY_RAW
|
|
108
|
+
|
|
109
|
+
case "$UVS_KIND_RAW" in
|
|
110
|
+
l|long|long-running) UVS_KIND="long-running" ;;
|
|
111
|
+
o|outcome) UVS_KIND="outcome" ;;
|
|
112
|
+
"") UVS_KIND="" ;;
|
|
113
|
+
*) echo " (kind '$UVS_KIND_RAW' not recognized — leaving blank)"; UVS_KIND="" ;;
|
|
114
|
+
esac
|
|
115
|
+
|
|
116
|
+
case "$UVS_PRIORITY_RAW" in
|
|
117
|
+
l|low) UVS_PRIORITY="low" ;;
|
|
118
|
+
m|med|medium) UVS_PRIORITY="med" ;;
|
|
119
|
+
h|high) UVS_PRIORITY="high" ;;
|
|
120
|
+
"") UVS_PRIORITY="" ;;
|
|
121
|
+
*) echo " (priority '$UVS_PRIORITY_RAW' not recognized — leaving blank)"; UVS_PRIORITY="" ;;
|
|
122
|
+
esac
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# Write metadata as JSON. Use python3 for proper escaping of free-text fields.
|
|
126
|
+
META_FILE="$SESSIONS_DIR/$UVS_SESSION_ID.json"
|
|
127
|
+
SID="$UVS_SESSION_ID" \
|
|
128
|
+
NAME="$UVS_NAME" \
|
|
129
|
+
KIND="$UVS_KIND" \
|
|
130
|
+
PURPOSE="$UVS_PURPOSE" \
|
|
131
|
+
PRIORITY="$UVS_PRIORITY" \
|
|
132
|
+
PERSONA_VAL="$PERSONA" \
|
|
133
|
+
CWD_VAL="$PROJECT_DIR" \
|
|
134
|
+
STARTED="$(date +%s)" \
|
|
135
|
+
python3 -c '
|
|
136
|
+
import json, os
|
|
137
|
+
print(json.dumps({
|
|
138
|
+
"uvs_session_id": os.environ["SID"],
|
|
139
|
+
"name": os.environ["NAME"],
|
|
140
|
+
"kind": os.environ["KIND"],
|
|
141
|
+
"purpose": os.environ["PURPOSE"],
|
|
142
|
+
"priority": os.environ["PRIORITY"],
|
|
143
|
+
"persona": os.environ["PERSONA_VAL"],
|
|
144
|
+
"cwd": os.environ["CWD_VAL"],
|
|
145
|
+
"started_at": int(os.environ["STARTED"]),
|
|
146
|
+
}, indent=2))
|
|
147
|
+
' > "$META_FILE" 2>/dev/null
|
|
148
|
+
|
|
149
|
+
# Latest-session pointer (used by hooks that lack UVS_SESSION_ID in their env)
|
|
150
|
+
echo "$UVS_SESSION_ID" > "$STATE_DIR/current-session.txt"
|
|
151
|
+
|
|
76
152
|
SETTINGS=".claude/personas/$PERSONA.json"
|
|
77
153
|
|
|
78
154
|
if [ "$TOOL" = "claude" ]; then
|
|
@@ -87,7 +163,9 @@ if [ "$TOOL" = "claude" ]; then
|
|
|
87
163
|
exit 1
|
|
88
164
|
fi
|
|
89
165
|
|
|
166
|
+
echo ""
|
|
90
167
|
echo "UV Suite | Claude Code | $LABEL"
|
|
168
|
+
echo "Session: ${UVS_SESSION_ID:0:8}${UVS_NAME:+ — $UVS_NAME}"
|
|
91
169
|
echo ""
|
|
92
170
|
exec claude --settings "$SETTINGS" "$@"
|
|
93
171
|
|
|
@@ -99,24 +177,16 @@ elif [ "$TOOL" = "codex" ]; then
|
|
|
99
177
|
exit 1
|
|
100
178
|
fi
|
|
101
179
|
|
|
102
|
-
# Codex doesn't have --settings, but reads AGENTS.md and .codex/agents/
|
|
103
|
-
# We can pass model and approval mode based on persona
|
|
104
180
|
case "$PERSONA" in
|
|
105
|
-
spike)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
CODEX_ARGS="--approval-mode auto-edit"
|
|
110
|
-
;;
|
|
111
|
-
professional)
|
|
112
|
-
CODEX_ARGS="--approval-mode suggest"
|
|
113
|
-
;;
|
|
114
|
-
auto)
|
|
115
|
-
CODEX_ARGS="--approval-mode full-auto"
|
|
116
|
-
;;
|
|
181
|
+
spike) CODEX_ARGS="--model o3 --approval-mode suggest" ;;
|
|
182
|
+
sport) CODEX_ARGS="--approval-mode auto-edit" ;;
|
|
183
|
+
professional) CODEX_ARGS="--approval-mode suggest" ;;
|
|
184
|
+
auto) CODEX_ARGS="--approval-mode full-auto" ;;
|
|
117
185
|
esac
|
|
118
186
|
|
|
187
|
+
echo ""
|
|
119
188
|
echo "UV Suite | Codex | $LABEL"
|
|
189
|
+
echo "Session: ${UVS_SESSION_ID:0:8}${UVS_NAME:+ — $UVS_NAME}"
|
|
120
190
|
echo ""
|
|
121
191
|
exec codex $CODEX_ARGS "$@"
|
|
122
192
|
fi
|