session-planner 1.1.1 → 1.1.5
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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +10 -0
- package/README.md +15 -2
- package/{hello-list.md → commands/hello-list.md} +1 -1
- package/{hello-remove.md → commands/hello-remove.md} +2 -2
- package/{hello.md → commands/hello.md} +2 -2
- package/package.json +9 -6
- package/{hello-scheduler.sh → scripts/hello-scheduler.sh} +36 -13
- package/session-planner.js +9 -0
package/README.md
CHANGED
|
@@ -116,6 +116,17 @@ scripts/hello-scheduler.sh (bash)
|
|
|
116
116
|
| **Claude Code** | `claude` in PATH — [install](https://code.claude.com) |
|
|
117
117
|
| **cron** | Built-in on macOS; Linux: `sudo apt install cron` |
|
|
118
118
|
| **Bash** 4+ | macOS: `brew install bash` |
|
|
119
|
+
| **OS Support** | macOS and Linux (terminal use) |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 🔐 Permissions (macOS)
|
|
124
|
+
|
|
125
|
+
To ensure `session-planner` can schedule jobs and show notifications without prompting every time, grant your terminal (Terminal or iTerm) the following:
|
|
126
|
+
|
|
127
|
+
1. **Full Disk Access**: Go to `System Settings > Privacy & Security > Full Disk Access` and add your terminal. This is required for `crontab` to save your schedule.
|
|
128
|
+
2. **Notifications**: Go to `System Settings > Notifications > Script Editor` and ensure "Allow Notifications" is on.
|
|
129
|
+
|
|
119
130
|
|
|
120
131
|
Logs: `~/.claude/session-planner.log`
|
|
121
132
|
|
|
@@ -125,9 +136,11 @@ Logs: `~/.claude/session-planner.log`
|
|
|
125
136
|
|
|
126
137
|
```
|
|
127
138
|
SessionPlanner/
|
|
128
|
-
├──
|
|
139
|
+
├── scripts/
|
|
140
|
+
│ └── hello-scheduler.sh ← core scheduling logic
|
|
141
|
+
├── commands/
|
|
142
|
+
│ └── hello.md ← command definition
|
|
129
143
|
├── session-planner.js ← npx / global CLI entry
|
|
130
|
-
├── {hello,hello-list,hello-remove}.md ← command definitions
|
|
131
144
|
├── package.json
|
|
132
145
|
└── README.md
|
|
133
146
|
```
|
|
@@ -5,6 +5,6 @@ allowed-tools: Bash
|
|
|
5
5
|
|
|
6
6
|
List all scheduled hello sessions:
|
|
7
7
|
|
|
8
|
-
!`bash
|
|
8
|
+
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/hello-scheduler.sh --list`
|
|
9
9
|
|
|
10
10
|
Present the output as a clean table to the user. If no jobs are found, let them know and suggest running `/hello <time>` to schedule one.
|
|
@@ -7,9 +7,9 @@ allowed-tools: Bash
|
|
|
7
7
|
The user wants to remove a scheduled hello session. Their argument is: $ARGUMENTS
|
|
8
8
|
|
|
9
9
|
If the argument is "--all" or "all":
|
|
10
|
-
!`bash
|
|
10
|
+
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/hello-scheduler.sh --remove-all`
|
|
11
11
|
|
|
12
12
|
Otherwise treat $ARGUMENTS as a time and run:
|
|
13
|
-
!`bash
|
|
13
|
+
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/hello-scheduler.sh --remove $ARGUMENTS`
|
|
14
14
|
|
|
15
15
|
Confirm to the user what was removed.
|
|
@@ -10,7 +10,7 @@ The user wants to schedule hello sessions. Their arguments are: $ARGUMENTS
|
|
|
10
10
|
|
|
11
11
|
Execute the scheduler script:
|
|
12
12
|
|
|
13
|
-
!`bash
|
|
13
|
+
!`bash ${CLAUDE_PLUGIN_ROOT}/scripts/hello-scheduler.sh $ARGUMENTS`
|
|
14
14
|
|
|
15
15
|
## Step 2 — Parse the output and report
|
|
16
16
|
|
|
@@ -52,4 +52,4 @@ Tell the user:
|
|
|
52
52
|
2. The offset used (e.g. "4 hours before each target time")
|
|
53
53
|
3. That persistent cron jobs have been registered (survive terminal close)
|
|
54
54
|
4. That the `/loop` in-session reminder was set for the soonest session
|
|
55
|
-
5. How to list or remove jobs: `crontab -l` /
|
|
55
|
+
5. How to list or remove jobs: `crontab -l` / `${CLAUDE_PLUGIN_ROOT}/scripts/hello-scheduler.sh --list` / `--remove-all`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "session-planner",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Schedule Hello! Claude Code sessions at specific times. Opens a session N hours before your chosen time.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -25,16 +25,19 @@
|
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=14.0.0"
|
|
27
27
|
},
|
|
28
|
+
"os": [
|
|
29
|
+
"darwin",
|
|
30
|
+
"linux"
|
|
31
|
+
],
|
|
28
32
|
"scripts": {
|
|
29
33
|
"test": "bash tests/test-parse.sh"
|
|
30
34
|
},
|
|
31
35
|
"files": [
|
|
32
|
-
"hello-scheduler.sh",
|
|
36
|
+
"scripts/hello-scheduler.sh",
|
|
37
|
+
"commands/",
|
|
38
|
+
".claude-plugin/",
|
|
33
39
|
"session-planner.js",
|
|
34
|
-
"hello.md",
|
|
35
|
-
"hello-list.md",
|
|
36
|
-
"hello-remove.md",
|
|
37
40
|
"README.md",
|
|
38
41
|
"LICENSE"
|
|
39
42
|
]
|
|
40
|
-
}
|
|
43
|
+
}
|
|
@@ -103,7 +103,9 @@ parse_time() {
|
|
|
103
103
|
error "Unrecognised time format: '$1'. Try: 1am 2:30pm 14:00"
|
|
104
104
|
fi
|
|
105
105
|
|
|
106
|
-
[[ $PARSED_HOUR -gt 23 ]]
|
|
106
|
+
if [[ $PARSED_HOUR -gt 23 ]]; then
|
|
107
|
+
error "Hour out of range 0-23 (got: $1)"
|
|
108
|
+
fi
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
# 24h hour+min → pretty 12h string
|
|
@@ -117,6 +119,16 @@ pretty_time() {
|
|
|
117
119
|
printf "%d:%02d %s" $ph $m $suffix
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
send_notification() {
|
|
123
|
+
local msg="$1"
|
|
124
|
+
local title="Session Planner"
|
|
125
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
126
|
+
osascript -e "display notification \"$msg\" with title \"$title\""
|
|
127
|
+
elif command -v notify-send >/dev/null; then
|
|
128
|
+
notify-send "$title" "$msg"
|
|
129
|
+
fi
|
|
130
|
+
}
|
|
131
|
+
|
|
120
132
|
# Seconds until next occurrence of HH:MM (today or tomorrow)
|
|
121
133
|
seconds_until() {
|
|
122
134
|
local th=$1 tm=$2
|
|
@@ -130,7 +142,9 @@ seconds_until() {
|
|
|
130
142
|
|
|
131
143
|
# ── argument parsing ──────────────────────────────────────────────────────────
|
|
132
144
|
|
|
133
|
-
[[ $# -eq 0 ]]
|
|
145
|
+
if [[ $# -eq 0 ]]; then
|
|
146
|
+
usage
|
|
147
|
+
fi
|
|
134
148
|
|
|
135
149
|
while [[ $# -gt 0 ]]; do
|
|
136
150
|
case "$1" in
|
|
@@ -158,8 +172,8 @@ done
|
|
|
158
172
|
|
|
159
173
|
if $LIST_MODE; then
|
|
160
174
|
echo -e "${BOLD}Scheduled Hello jobs:${RESET}"
|
|
161
|
-
if crontab -l 2>/dev/null | grep -q "# session-planner"; then
|
|
162
|
-
crontab -l 2>/dev/null | grep "# session-planner"
|
|
175
|
+
if (((crontab -l 2>/dev/null || true) || true) || true) | grep -q "# session-planner"; then
|
|
176
|
+
(((crontab -l 2>/dev/null || true) || true) || true) | grep "# session-planner"
|
|
163
177
|
else
|
|
164
178
|
echo -e " ${YELLOW}No session-planner jobs found.${RESET}"
|
|
165
179
|
fi
|
|
@@ -170,11 +184,11 @@ fi
|
|
|
170
184
|
|
|
171
185
|
if $REMOVE_ALL; then
|
|
172
186
|
tmp=$(mktemp)
|
|
173
|
-
crontab -l 2>/dev/null \
|
|
187
|
+
((crontab -l 2>/dev/null || true) || true) \
|
|
174
188
|
| grep -v "# session-planner" \
|
|
175
189
|
| grep -v "session-planner" \
|
|
176
190
|
> "$tmp" || true
|
|
177
|
-
|
|
191
|
+
cat "$tmp" | crontab -; rm -f "$tmp"
|
|
178
192
|
echo -e "${GREEN}✓${RESET} All session-planner jobs removed."
|
|
179
193
|
exit 0
|
|
180
194
|
fi
|
|
@@ -185,28 +199,33 @@ if [[ -n "$REMOVE_TIME" ]]; then
|
|
|
185
199
|
parse_time "$REMOVE_TIME"
|
|
186
200
|
TARGET_PRETTY=$(pretty_time "$PARSED_HOUR" "$PARSED_MIN")
|
|
187
201
|
tmp=$(mktemp)
|
|
188
|
-
crontab -l 2>/dev/null \
|
|
202
|
+
((crontab -l 2>/dev/null || true) || true) \
|
|
189
203
|
| grep -v "# session-planner.*${TARGET_PRETTY}" \
|
|
190
204
|
| grep -v "Hello! It is now ${TARGET_PRETTY}" \
|
|
191
205
|
> "$tmp" || true
|
|
192
|
-
|
|
206
|
+
cat "$tmp" | crontab -; rm -f "$tmp"
|
|
193
207
|
echo -e "${GREEN}✓${RESET} Removed session-planner job for ${BOLD}${TARGET_PRETTY}${RESET}."
|
|
194
208
|
exit 0
|
|
195
209
|
fi
|
|
196
210
|
|
|
197
211
|
# ── validate at least one time was given ─────────────────────────────────────
|
|
198
212
|
|
|
199
|
-
[[ ${#TIMES[@]} -eq 0 ]]
|
|
213
|
+
if [[ ${#TIMES[@]} -eq 0 ]]; then
|
|
214
|
+
error "Please provide at least one time. E.g: hello-scheduler.sh 1am"
|
|
215
|
+
fi
|
|
200
216
|
|
|
201
217
|
# ── ensure log dir ────────────────────────────────────────────────────────────
|
|
202
218
|
|
|
203
219
|
mkdir -p "$HOME/.claude"
|
|
204
220
|
LOG="$HOME/.claude/session-planner.log"
|
|
205
221
|
|
|
222
|
+
# ── find claude path for cron ──────────────────────────────────────────────────
|
|
223
|
+
CLAUDE_PATH=$(which claude 2>/dev/null || echo "claude")
|
|
224
|
+
|
|
206
225
|
# ── load crontab, strip existing session-planner entries ─────────────────────
|
|
207
226
|
|
|
208
227
|
tmp=$(mktemp)
|
|
209
|
-
crontab -l 2>/dev/null \
|
|
228
|
+
((crontab -l 2>/dev/null || true) || true) \
|
|
210
229
|
| grep -v "# session-planner" \
|
|
211
230
|
| grep -v "session-planner" \
|
|
212
231
|
> "$tmp" || true
|
|
@@ -233,10 +252,11 @@ for T in "${TIMES[@]}"; do
|
|
|
233
252
|
TARGET_PRETTY=$(pretty_time "$TARGET_H" "$TARGET_M")
|
|
234
253
|
SCHED_PRETTY=$(pretty_time "$SCHED_H" "$SCHED_M")
|
|
235
254
|
|
|
236
|
-
# cron job: claude --print fires at session-open time
|
|
255
|
+
# cron job: claude --print fires at session-open time + notification
|
|
237
256
|
MSG="Hello! It is now ${TARGET_PRETTY}. This is your scheduled greeting."
|
|
238
257
|
CRON_EXPR="${SCHED_M} ${SCHED_H} * * *"
|
|
239
|
-
|
|
258
|
+
# We use a subshell for the notification to handle the environment/osascript better
|
|
259
|
+
CRON_LINE="${CRON_EXPR} ${CLAUDE_PATH} --print \"${MSG}\" >> ${LOG} 2>&1; if [ \"\$(uname)\" = \"Darwin\" ]; then osascript -e \"display notification \\\"${MSG}\\\" with title \\\"Session Planner\\\"\"; elif command -v notify-send >/dev/null; then notify-send \"Session Planner\" \"${MSG}\"; fi"
|
|
240
260
|
|
|
241
261
|
{
|
|
242
262
|
echo "# session-planner: session at ${SCHED_PRETTY} → greeting at ${TARGET_PRETTY} (offset: ${OFFSET_HOURS}h)"
|
|
@@ -252,6 +272,9 @@ for T in "${TIMES[@]}"; do
|
|
|
252
272
|
SOONEST_SESSION="$SCHED_PRETTY"
|
|
253
273
|
fi
|
|
254
274
|
|
|
275
|
+
# Success message for user
|
|
276
|
+
echo -e "${GREEN}✓${RESET} Scheduled Claude session to start at ${BOLD}${SCHED_PRETTY}${RESET} (greeting at ${TARGET_PRETTY})"
|
|
277
|
+
|
|
255
278
|
# Structured output for Claude to parse
|
|
256
279
|
echo "SCHEDULED: {\"target\":\"${TARGET_PRETTY}\",\"session_at\":\"${SCHED_PRETTY}\",\"cron\":\"${CRON_EXPR}\",\"loop_in_seconds\":${LOOP_SECS},\"offset_hours\":${OFFSET_HOURS}}"
|
|
257
280
|
|
|
@@ -263,7 +286,7 @@ done
|
|
|
263
286
|
JOBS_JSON+="]"
|
|
264
287
|
|
|
265
288
|
# Commit updated crontab
|
|
266
|
-
|
|
289
|
+
cat "$tmp" | crontab -
|
|
267
290
|
rm -f "$tmp"
|
|
268
291
|
|
|
269
292
|
# Summary for Claude's confirmation message
|
package/session-planner.js
CHANGED
|
@@ -21,6 +21,14 @@ const path = require('path');
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const os = require('os');
|
|
23
23
|
|
|
24
|
+
// ── OS Compatibility Check ───────────────────────────────────────────────────
|
|
25
|
+
if (process.platform === 'win32') {
|
|
26
|
+
console.error('\x1b[31mERROR:\x1b[0m session-planner currently only supports macOS and Linux.');
|
|
27
|
+
console.error('This tool relies on bash and cron, which are not natively available on Windows.');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
24
32
|
// ── colours ───────────────────────────────────────────────────────────────────
|
|
25
33
|
const isTTY = process.stdout.isTTY;
|
|
26
34
|
const c = {
|
|
@@ -38,6 +46,7 @@ const c = {
|
|
|
38
46
|
// 2. Installed as a Claude Code plugin ~/.claude/plugins/session-planner/scripts/
|
|
39
47
|
function findScript() {
|
|
40
48
|
const candidates = [
|
|
49
|
+
path.join(__dirname, 'scripts', 'hello-scheduler.sh'),
|
|
41
50
|
path.join(__dirname, 'hello-scheduler.sh'),
|
|
42
51
|
path.join(os.homedir(), '.claude', 'plugins', 'session-planner', 'scripts', 'hello-scheduler.sh'),
|
|
43
52
|
];
|