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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "MahmoudMabrok Marketplace",
3
+ "description": "Custom Claude Code plugins by MahmoudMabrok",
4
+ "plugins": [
5
+ {
6
+ "name": "session-planner",
7
+ "description": "Schedule Hello! Claude Code sessions",
8
+ "source": "."
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "session-planner",
3
+ "version": "1.1.1",
4
+ "description": "Schedule Hello! Claude Code sessions to maximize session duration.",
5
+ "commands": [
6
+ "./commands/hello.md",
7
+ "./commands/hello-list.md",
8
+ "./commands/hello-remove.md"
9
+ ]
10
+ }
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
- ├── hello-scheduler.sh ← core scheduling logic
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 ~/.claude/scripts/hello-scheduler.sh --list`
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 ~/.claude/scripts/hello-scheduler.sh --remove-all`
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 ~/.claude/scripts/hello-scheduler.sh --remove $ARGUMENTS`
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 ~/.claude/scripts/hello-scheduler.sh $ARGUMENTS`
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` / `~/.claude/scripts/hello-scheduler.sh --list` / `--remove-all`
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.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 ]] && error "Hour out of range 0-23 (got: $1)"
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 ]] && usage
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
- crontab "$tmp"; rm -f "$tmp"
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
- crontab "$tmp"; rm -f "$tmp"
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 ]] && error "Please provide at least one time. E.g: hello-scheduler.sh 1am"
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
- CRON_LINE="${CRON_EXPR} claude --print \"${MSG}\" >> ${LOG} 2>&1"
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
- crontab "$tmp"
289
+ cat "$tmp" | crontab -
267
290
  rm -f "$tmp"
268
291
 
269
292
  # Summary for Claude's confirmation message
@@ -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
  ];