+4
-0
@@ -484,2 +484,6 @@ #!/usr/bin/env node | ||
| } | ||
| if (key === 'launch_sort' && !['time', 'alpha'].includes(value)) { | ||
| console.error(`launch_sort must be 'time' or 'alpha' — got: ${value}`); | ||
| process.exit(1); | ||
| } | ||
| storeValue = value; | ||
@@ -486,0 +490,0 @@ } else { |
+3
-1
@@ -10,2 +10,3 @@ import { readFileSync, writeFileSync, existsSync } from 'fs'; | ||
| statusline_color: 'cyan', | ||
| launch_sort: 'time', | ||
| }; | ||
@@ -18,6 +19,7 @@ | ||
| statusline_color: 'Color for statusline text (see: cloudctx config colors)', | ||
| launch_sort: 'Default sort order for launch picker: time or alpha', | ||
| }; | ||
| const BOOL_KEYS = new Set(['statusline']); | ||
| const STRING_KEYS = new Set(['statusline_color']); | ||
| const STRING_KEYS = new Set(['statusline_color', 'launch_sort']); | ||
@@ -24,0 +26,0 @@ export const STATUSLINE_COLORS = { |
+46
-10
@@ -6,2 +6,3 @@ import { getReadonlyDb, getDb, dbExists, migrate } from './db.js'; | ||
| import { homedir } from 'os'; | ||
| import { getConfigValue, setConfig } from './config.js'; | ||
@@ -103,5 +104,8 @@ const CLAUDE_PROJECTS = join(homedir(), '.claude', 'projects'); | ||
| function fetchThreadsWithActivity() { | ||
| function fetchThreadsWithActivity(sort = 'time') { | ||
| const db = getDb(); | ||
| migrate(db); | ||
| const orderBy = sort === 'alpha' | ||
| ? 'ORDER BY LOWER(st.name) ASC' | ||
| : 'ORDER BY last_active DESC'; | ||
| const threads = db.prepare(` | ||
@@ -118,3 +122,3 @@ SELECT | ||
| FROM saved_threads st | ||
| ORDER BY last_active DESC | ||
| ${orderBy} | ||
| `).all(); | ||
@@ -145,3 +149,4 @@ | ||
| const threads = fetchThreadsWithActivity(); | ||
| const sort = getConfigValue('launch_sort') === 'alpha' ? 'alpha' : 'time'; | ||
| const threads = fetchThreadsWithActivity(sort); | ||
@@ -173,3 +178,4 @@ if (!threads.length) { | ||
| let threads = fetchThreadsWithActivity(); | ||
| const initialSort = getConfigValue('launch_sort') === 'alpha' ? 'alpha' : 'time'; | ||
| let threads = fetchThreadsWithActivity(initialSort); | ||
@@ -181,3 +187,3 @@ if (!threads.length) { | ||
| const selected = await arrowKeyPicker(threads); | ||
| const selected = await arrowKeyPicker(threads, initialSort); | ||
| if (!selected) return; | ||
@@ -202,6 +208,7 @@ | ||
| async function arrowKeyPicker(threads) { | ||
| async function arrowKeyPicker(threads, initialSort = 'time') { | ||
| let cursor = 0; | ||
| let confirmingDelete = null; // holds the thread name being deleted, if any | ||
| let renaming = null; // {oldName, buffer, error} when renaming | ||
| let sort = initialSort === 'alpha' ? 'alpha' : 'time'; | ||
| const visibleCount = () => Math.min(threads.length, process.stdout.rows ? process.stdout.rows - 8 : 20); | ||
@@ -226,4 +233,5 @@ | ||
| console.log(''); | ||
| console.log(' \x1b[1mCloudCtx — Select a thread to resume\x1b[0m'); | ||
| console.log(' \x1b[2m↑↓ navigate ⏎ select r rename d delete q quit\x1b[0m'); | ||
| const sortLabel = sort === 'alpha' ? 'alpha' : 'time'; | ||
| console.log(` \x1b[1mCloudCtx — Select a thread to resume\x1b[0m \x1b[2m(sort: ${sortLabel})\x1b[0m`); | ||
| console.log(' \x1b[2m↑↓ navigate ⏎ select T time A alpha r rename d delete q quit\x1b[0m'); | ||
| console.log(''); | ||
@@ -304,3 +312,3 @@ | ||
| db.close(); | ||
| threads = fetchThreadsWithActivity(); | ||
| threads = fetchThreadsWithActivity(sort); | ||
| // Keep cursor on the renamed thread | ||
@@ -344,3 +352,3 @@ const idx = threads.findIndex(t => t.name === newName); | ||
| // Refresh threads list | ||
| threads = fetchThreadsWithActivity(); | ||
| threads = fetchThreadsWithActivity(sort); | ||
| confirmingDelete = null; | ||
@@ -394,2 +402,30 @@ | ||
| // T — sort by time (newest first), persist | ||
| if (key === 't' || key === 'T') { | ||
| if (sort !== 'time') { | ||
| sort = 'time'; | ||
| try { setConfig('launch_sort', 'time'); } catch {} | ||
| const currentName = threads[cursor]?.name; | ||
| threads = fetchThreadsWithActivity('time'); | ||
| const idx = currentName ? threads.findIndex(t => t.name === currentName) : -1; | ||
| cursor = idx >= 0 ? idx : 0; | ||
| render(); | ||
| } | ||
| return; | ||
| } | ||
| // A — sort alphabetical (A→Z), persist | ||
| if (key === 'a' || key === 'A') { | ||
| if (sort !== 'alpha') { | ||
| sort = 'alpha'; | ||
| try { setConfig('launch_sort', 'alpha'); } catch {} | ||
| const currentName = threads[cursor]?.name; | ||
| threads = fetchThreadsWithActivity('alpha'); | ||
| const idx = currentName ? threads.findIndex(t => t.name === currentName) : -1; | ||
| cursor = idx >= 0 ? idx : 0; | ||
| render(); | ||
| } | ||
| return; | ||
| } | ||
| // Arrow keys | ||
@@ -396,0 +432,0 @@ if (key === '\x1b[A' || key === 'k') { |
+1
-1
| { | ||
| "name": "cloudctx", | ||
| "version": "0.1.5", | ||
| "version": "0.1.6", | ||
| "description": "Persistent memory for Claude Code. One command, full recall.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
83163
2.27%2125
1.92%