cc-sessions 1.0.1 → 1.1.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.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +23 -13
  4. data/bin/cc +144 -10
  5. data/bin/cc-bookmark +37 -0
  6. data/commands/bm.md +7 -35
  7. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acd63dc92da6821889b1e5c397a8ebe74aba5d571f36d859425bda641b5666e2
4
- data.tar.gz: ee05bf07a43c3bda1b42caf7a3787833a8ed28d69d182f211fdc7542e0f6fbfa
3
+ metadata.gz: bb7625607c8b67ff39212aed3d7b9bcb8a9cf5fcbae0751a554b1a09427baea1
4
+ data.tar.gz: a40ac44c23feca33a5b91eeefb4d7665a0b74a4d7e3fe54f544cb2a1dc01e5e2
5
5
  SHA512:
6
- metadata.gz: 89789ae0e1d1933496a153df188e6df4b30220fcb3ba9ae9d202949b1eacd681b562238c3fa009b0bf52fdeca723b2738dd56af2a6ec1fc2a75ce17ed7e851cf
7
- data.tar.gz: cf407c8d028ac237dcb2fea7bbd2889e94ee174ab07125de718af8bddf62a382a2d9ac7542696fec7dadb5b051f945d9906ff33ebb20594aba256f30e2d0ac10
6
+ metadata.gz: 8516229845736a7311f5d597c3218d6bff92533835b8a0b0f16851e05c503a62c98bbd2c388ee69ab42622961c57ca82efa539539b536f91cccb9d109a14133f
7
+ data.tar.gz: aa67d7f92b1d3882b10696f2b3fb4638ce902849d46c8069ce538f49d74aa9ff71ebc632c3e4864efcdd767c8c764c5a92e73b6659b39b7c1b02315118d6eb6b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2025-01-23
4
+
5
+ ### Added
6
+ - Interactive session picker with `cc -l` (no external dependencies)
7
+ - Delete bookmarks with `d` key in list (with confirmation)
8
+ - Delete bookmarks from CLI with `cc -d <tag>`
9
+ - Vim-style navigation (j/k) in addition to arrow keys
10
+
11
+ ### Changed
12
+ - `cc -l` now shows interactive list with hidden cursor
13
+ - Zero external dependencies (removed tty-prompt)
14
+
15
+ ## [1.0.2] - 2025-01-23
16
+
17
+ ### Added
18
+ - `cc-bookmark` helper script for reliable permission matching
19
+ - SVG logo
20
+
21
+ ### Changed
22
+ - `/bm` command now uses `cc-bookmark` script (auto-accepts without prompts)
23
+ - Updated README with badges and proper logo placement
24
+
3
25
  ## [1.0.1] - 2025-01-23
4
26
 
5
27
  ### Added
data/README.md CHANGED
@@ -1,32 +1,38 @@
1
1
  # CC-sessions
2
2
 
3
- A simple tool for bookmarking and resuming Claude Code sessions with tags.
3
+ ![Ruby](https://img.shields.io/badge/language-Ruby-red) [![Gem Version](https://badge.fury.io/rb/cc-sessions.svg)](https://badge.fury.io/rb/cc-sessions) ![Unlicense](https://img.shields.io/badge/license-Unlicense-green)
4
+
5
+ <img src="img/cc-sessions_logo.svg" align="left" width="150" height="150">
4
6
 
5
- ## The Problem
7
+ A simple tool for bookmarking and resuming Claude Code sessions with tags.
6
8
 
7
9
  Claude Code sessions are tied to directories, making it hard to remember where
8
10
  you were working on specific projects. This tool lets you tag sessions with
9
11
  meaningful names and quickly resume them.
10
12
 
13
+ <br clear="left"/>
14
+
11
15
  ## Features
12
16
 
13
17
  - **Bookmark sessions** with `/bm tag1 tag2` inside Claude Code
14
18
  - **Resume sessions** with `cc tag` from anywhere
15
19
  - **List bookmarks** with `cc -l`
16
- - **Auto-install** the `/bm` command on first run
20
+ - **Auto-install** the `/bm` command and permission on first run
17
21
 
18
22
  ## Installation
19
23
 
24
+ ### From RubyGems (Recommended)
25
+
20
26
  ```bash
21
- # Clone the repository
22
- git clone https://github.com/isene/CC-sessions.git
23
- cd CC-sessions
27
+ gem install cc-sessions
28
+ ```
24
29
 
25
- # Add to your PATH (add to .zshrc or .bashrc)
26
- export PATH="$HOME/path/to/CC-sessions/bin:$PATH"
30
+ ### From Source
27
31
 
28
- # Or create a symlink
29
- ln -sf /path/to/CC-sessions/bin/cc ~/bin/cc
32
+ ```bash
33
+ git clone https://github.com/isene/CC-sessions.git
34
+ cd CC-sessions
35
+ ./install.sh
30
36
  ```
31
37
 
32
38
  ## Usage
@@ -60,8 +66,11 @@ cc -h
60
66
 
61
67
  ### First Run
62
68
 
63
- On first run, `cc` automatically installs the `/bm` command to `~/.claude/commands/`.
64
- This enables the `/bm` command inside Claude Code.
69
+ On first run, `cc` automatically:
70
+ 1. Installs the `/bm` command to `~/.claude/commands/`
71
+ 2. Adds auto-accept permission to `~/.claude/settings.json`
72
+
73
+ This enables `/bm` to work without confirmation prompts.
65
74
 
66
75
  ## Files
67
76
 
@@ -69,6 +78,7 @@ This enables the `/bm` command inside Claude Code.
69
78
  |------|---------|
70
79
  | `~/.cc-sessions/bookmarks.json` | Stores your bookmarks |
71
80
  | `~/.claude/commands/bm.md` | The `/bm` command definition |
81
+ | `~/.claude/settings.json` | Permission for auto-accept |
72
82
 
73
83
  ## Example Workflow
74
84
 
@@ -91,4 +101,4 @@ cc rtfm # Instantly back in that session
91
101
 
92
102
  ## License
93
103
 
94
- MIT
104
+ This software is released into the public domain under [The Unlicense](https://unlicense.org/).
data/bin/cc CHANGED
@@ -15,13 +15,21 @@
15
15
 
16
16
  require 'json'
17
17
  require 'fileutils'
18
+ require 'io/console'
19
+
20
+ # Simple ANSI helpers
21
+ def cyan(s) = "\e[36m#{s}\e[0m"
22
+ def dim(s) = "\e[2m#{s}\e[0m"
23
+ def red(s) = "\e[31m#{s}\e[0m"
24
+ def rev(s) = "\e[7m#{s}\e[0m"
25
+ def clear_line = "\e[2K\r"
18
26
 
19
27
  CONFIG_DIR = File.expand_path('~/.cc-sessions')
20
28
  BOOKMARKS_FILE = File.join(CONFIG_DIR, 'bookmarks.json')
21
29
  COMMAND_SOURCE = File.expand_path('../commands/bm.md', __dir__)
22
30
  COMMAND_DEST = File.expand_path('~/.claude/commands/bm.md')
23
31
  SETTINGS_FILE = File.expand_path('~/.claude/settings.json')
24
- BM_PERMISSION = 'Bash(mkdir -p ~/.cc-sessions && ruby -rjson:*)'
32
+ BM_PERMISSION = 'Bash(cc-bookmark:*)'
25
33
 
26
34
  def ensure_setup
27
35
  # Create config directory
@@ -88,6 +96,43 @@ def session_exists_in_dir?(dir)
88
96
  Dir.glob(File.join(claude_dir, '**/*')).any? { |f| File.file?(f) }
89
97
  end
90
98
 
99
+ def read_key
100
+ input = $stdin.getch
101
+ if input == "\e"
102
+ begin
103
+ input << $stdin.read_nonblock(3)
104
+ rescue IO::WaitReadable
105
+ end
106
+ end
107
+ case input
108
+ when "\e[A", "k" then :up
109
+ when "\e[B", "j" then :down
110
+ when "\r", "\n" then :enter
111
+ when "q", "\e" then :quit
112
+ when "d" then :delete
113
+ else nil
114
+ end
115
+ end
116
+
117
+ def save_bookmarks(bookmarks)
118
+ File.write(BOOKMARKS_FILE, JSON.pretty_generate(bookmarks))
119
+ end
120
+
121
+ def delete_bookmark_by_tag(tag)
122
+ bookmarks = load_bookmarks
123
+ path = find_session_by_tag(tag)
124
+
125
+ if path
126
+ bookmarks.delete(path)
127
+ save_bookmarks(bookmarks)
128
+ puts "Deleted bookmark: #{path}"
129
+ puts " (was tagged: #{tag})"
130
+ else
131
+ puts "No session found with tag '#{tag}'"
132
+ exit 1
133
+ end
134
+ end
135
+
91
136
  def list_bookmarks
92
137
  bookmarks = load_bookmarks
93
138
 
@@ -98,14 +143,93 @@ def list_bookmarks
98
143
  return
99
144
  end
100
145
 
101
- puts "Bookmarked Claude Code sessions:"
102
- puts
146
+ items = bookmarks.map do |path, tags|
147
+ { path: path, tags: tags, exists: Dir.exist?(path) }
148
+ end
103
149
 
104
- bookmarks.each do |path, tags|
105
- exists = Dir.exist?(path) ? '' : ' [NOT FOUND]'
106
- puts "#{path}#{exists}"
107
- puts " Tags: #{tags.join(', ')}"
108
- puts
150
+ index = 0
151
+ puts "Select session (↑/↓/j/k move, Enter select, d delete, q quit):\n\n"
152
+
153
+ # Hide cursor
154
+ print "\e[?25l"
155
+
156
+ begin
157
+ loop do
158
+ # Render list
159
+ items.each_with_index do |item, i|
160
+ print clear_line
161
+ tag_str = cyan(item[:tags].join(', '))
162
+ path_str = dim(item[:path])
163
+ missing = item[:exists] ? '' : red(' [NOT FOUND]')
164
+ line = "#{tag_str} → #{path_str}#{missing}"
165
+
166
+ if i == index
167
+ puts "▸ #{line}"
168
+ else
169
+ puts " #{line}"
170
+ end
171
+ end
172
+
173
+ # Move cursor back to start
174
+ print "\e[#{items.size}A"
175
+
176
+ key = read_key
177
+ case key
178
+ when :up
179
+ index = (index - 1) % items.size
180
+ when :down
181
+ index = (index + 1) % items.size
182
+ when :delete
183
+ # Show confirmation
184
+ old_size = items.size
185
+ print "\e[#{old_size}B" # Move below list
186
+ print clear_line
187
+ print "Delete '#{items[index][:tags].join(', ')}'? (y/n) "
188
+ confirm = $stdin.getch
189
+ if confirm.downcase == 'y'
190
+ bookmarks.delete(items[index][:path])
191
+ save_bookmarks(bookmarks)
192
+ items.delete_at(index)
193
+ index = [index, items.size - 1].min
194
+ if items.empty?
195
+ # Clear list and confirmation line
196
+ print "\e[#{old_size}A"
197
+ (old_size + 1).times { print clear_line; puts }
198
+ print "\e[#{old_size + 1}A"
199
+ puts "All bookmarks deleted."
200
+ return
201
+ end
202
+ # Clear old list (one more line than new size)
203
+ print clear_line
204
+ print "\e[#{old_size}A"
205
+ (old_size).times { print clear_line; puts }
206
+ print "\e[#{old_size}A"
207
+ else
208
+ print clear_line
209
+ print "\e[#{old_size}A"
210
+ end
211
+ when :enter
212
+ # Clear the menu
213
+ (items.size).times { print clear_line; puts }
214
+ print "\e[#{items.size}A"
215
+
216
+ if items[index][:exists]
217
+ print "\e[?25h" # Show cursor before exec
218
+ resume_session(items[index][:path])
219
+ else
220
+ puts red("Directory not found: #{items[index][:path]}")
221
+ end
222
+ return
223
+ when :quit
224
+ # Clear the menu
225
+ (items.size).times { print clear_line; puts }
226
+ print "\e[#{items.size}A"
227
+ return
228
+ end
229
+ end
230
+ ensure
231
+ # Always show cursor
232
+ print "\e[?25h"
109
233
  end
110
234
  end
111
235
 
@@ -118,7 +242,8 @@ def show_help
118
242
  USAGE:
119
243
  cc Continue session in current directory, or start new
120
244
  cc <tag> Resume session bookmarked with <tag>
121
- cc -l, --list List all bookmarked sessions
245
+ cc -l, --list List all bookmarked sessions (interactive)
246
+ cc -d, --delete <tag> Delete bookmark matching <tag>
122
247
  cc -h, --help Show this help
123
248
 
124
249
  BOOKMARKING (inside Claude Code):
@@ -127,7 +252,8 @@ def show_help
127
252
  EXAMPLES:
128
253
  cc rtfm Resume session tagged 'rtfm'
129
254
  cc Continue in current dir or start fresh
130
- cc -l Show all bookmarked sessions
255
+ cc -l Show interactive list (d to delete, Enter to select)
256
+ cc -d rtfm Delete bookmark tagged 'rtfm'
131
257
 
132
258
  FILES:
133
259
  ~/.cc-sessions/bookmarks.json Bookmark storage
@@ -168,6 +294,14 @@ when '-h', '--help'
168
294
  show_help
169
295
  when '-l', '--list'
170
296
  list_bookmarks
297
+ when '-d', '--delete'
298
+ if ARGV[1]
299
+ delete_bookmark_by_tag(ARGV[1])
300
+ else
301
+ puts "Usage: cc -d <tag>"
302
+ puts "Delete bookmark matching the given tag."
303
+ exit 1
304
+ end
171
305
  when nil
172
306
  continue_or_start
173
307
  else
data/bin/cc-bookmark ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # CC-Bookmark - Helper script for /bm command
5
+ # Bookmarks the current directory with tags
6
+
7
+ require 'json'
8
+ require 'fileutils'
9
+
10
+ CONFIG_DIR = File.expand_path('~/.cc-sessions')
11
+ BOOKMARKS_FILE = File.join(CONFIG_DIR, 'bookmarks.json')
12
+
13
+ FileUtils.mkdir_p(CONFIG_DIR) unless Dir.exist?(CONFIG_DIR)
14
+
15
+ bookmarks = if File.exist?(BOOKMARKS_FILE)
16
+ JSON.parse(File.read(BOOKMARKS_FILE))
17
+ else
18
+ {}
19
+ end
20
+
21
+ cwd = Dir.pwd
22
+ tags = ARGV
23
+
24
+ if tags.empty?
25
+ if bookmarks[cwd]
26
+ puts "Current bookmark: #{bookmarks[cwd].join(', ')}"
27
+ else
28
+ puts "No bookmark for this directory. Usage: /bm tag1 tag2 ..."
29
+ end
30
+ else
31
+ bookmarks[cwd] = tags
32
+ File.write(BOOKMARKS_FILE, JSON.pretty_generate(bookmarks))
33
+ puts "Bookmarked: #{cwd}"
34
+ puts "Tags: #{tags.join(', ')}"
35
+ puts ""
36
+ puts "Resume later with: cc #{tags.first}"
37
+ end
data/commands/bm.md CHANGED
@@ -10,44 +10,16 @@ The user provides space-separated tags after `/bm`. For example:
10
10
 
11
11
  ## What to Do
12
12
 
13
- 1. Get the current working directory (this is the session path)
14
- 2. Parse the tags from the command arguments (the text after `/bm`)
15
- 3. Store the bookmark in `~/.cc-sessions/bookmarks.json`
16
- 4. If no tags provided, show current bookmark for this directory (if any)
17
-
18
- ## Implementation
19
-
20
- Run this bash command, replacing `$TAGS` with the actual tags provided:
13
+ Run the cc-bookmark command with the provided tags:
21
14
 
22
15
  ```bash
23
- mkdir -p ~/.cc-sessions && ruby -rjson -e '
24
- file = File.expand_path("~/.cc-sessions/bookmarks.json")
25
- bookmarks = File.exist?(file) ? JSON.parse(File.read(file)) : {}
26
- cwd = Dir.pwd
27
- tags = ARGV
28
- if tags.empty?
29
- if bookmarks[cwd]
30
- puts "Current bookmark: #{bookmarks[cwd].join(", ")}"
31
- else
32
- puts "No bookmark for this directory. Usage: /bm tag1 tag2 ..."
33
- end
34
- else
35
- bookmarks[cwd] = tags
36
- File.write(file, JSON.pretty_generate(bookmarks))
37
- puts "Bookmarked: #{cwd}"
38
- puts "Tags: #{tags.join(", ")}"
39
- puts ""
40
- puts "Resume later with: cc #{tags.first}"
41
- end
42
- ' $TAGS
16
+ cc-bookmark $TAGS
43
17
  ```
44
18
 
45
- ## Response Format
19
+ Replace `$TAGS` with the actual tags provided by the user.
46
20
 
47
- After bookmarking, confirm:
48
- ```
49
- Bookmarked: /path/to/session
50
- Tags: tag1, tag2, tag3
21
+ ## Example
51
22
 
52
- Resume later with: cc tag1
53
- ```
23
+ User types: `/bm rtfm ruby`
24
+
25
+ Run: `cc-bookmark rtfm ruby`
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cc-sessions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-01-23 00:00:00.000000000 Z
11
+ date: 2026-01-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A simple tool for bookmarking and resuming Claude Code sessions. Tag
14
14
  sessions with meaningful names using /bm inside Claude Code, then quickly resume
@@ -17,6 +17,7 @@ email:
17
17
  - g@isene.com
18
18
  executables:
19
19
  - cc
20
+ - cc-bookmark
20
21
  extensions: []
21
22
  extra_rdoc_files: []
22
23
  files:
@@ -24,6 +25,7 @@ files:
24
25
  - LICENSE
25
26
  - README.md
26
27
  - bin/cc
28
+ - bin/cc-bookmark
27
29
  - commands/bm.md
28
30
  homepage: https://github.com/isene/CC-sessions
29
31
  licenses: