uv-suite 0.26.1 → 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.
@@ -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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure", "timeout": 2, "async": true }
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
+ }
@@ -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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh", "timeout": 5 },
44
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh SessionStart", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh UserPromptSubmit", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
58
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
63
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
68
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUse", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-end.sh", "timeout": 10 }
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
+ }
@@ -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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-start.sh", "timeout": 5 },
30
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh SessionStart", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh UserPromptSubmit", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
44
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PermissionRequest", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
49
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh Notification", "timeout": 2, "async": true }
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
- { "matcher": "*", "hooks": [
54
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUseFailure", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/watchtower-send.sh PostToolUse", "timeout": 2, "async": true }
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
- { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/session-end.sh", "timeout": 10 }
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
- ## Write a checkpoint to uv-out/checkpoints/
22
+ ## Resolve checkpoint directory
20
23
 
21
- Create the directory `uv-out/checkpoints/` if it doesn't exist.
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
- Write a file named `uv-out/checkpoints/YYYY-MM-DD-HHMM.md` (using the current timestamp).
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
- Also write/overwrite `uv-out/checkpoints/latest.md` with the same content (so the next session can always find the most recent checkpoint).
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: `uv-out/checkpoints/YYYY-MM-DD-HHMM-[label].md`
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`.
@@ -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
- !`cat uv-out/checkpoints/latest.md 2>/dev/null || echo "No checkpoint found. Run /checkpoint to create one."`
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
- !`ls -la uv-out/checkpoints/*.md 2>/dev/null | tail -10 || echo "No checkpoints directory"`
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
- CODEX_ARGS="--model o3 --approval-mode suggest"
107
- ;;
108
- sport)
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