Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

cloudctx

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cloudctx - npm Package Compare versions

Comparing version
0.1.6
to
0.1.7
+67
-25
lib/hook.js

@@ -14,5 +14,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } from 'fs';

const SAVE_PATTERN_LITERAL = /^\/cloudctx\s+save\s+(?:"([^"]+)"|'([^']+)'|(\S[^\n]*?))\s*$/im;
// /cloudctx-rename "old" "new" or /cloudctx rename "old" "new"
const RENAME_PATTERN_SLASH = /^\/cloudctx-rename\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s+(?:"([^"]+)"|'([^']+)'|(\S[^\n]*?))\s*$/im;
const RENAME_PATTERN_LITERAL = /^\/cloudctx\s+rename\s+(?:"([^"]+)"|'([^']+)'|(\S+))\s+(?:"([^"]+)"|'([^']+)'|(\S[^\n]*?))\s*$/im;
// /cloudctx-rename <args> — captures everything after the command; routing decides 1-arg vs 2-arg
const RENAME_PATTERN_SLASH = /^\/cloudctx-rename\s+(\S[^\n]*?)\s*$/im;
const RENAME_PATTERN_LITERAL = /^\/cloudctx\s+rename\s+(\S[^\n]*?)\s*$/im;
// Used inside the routing decision to try parsing as "old new"
const TWO_ARG_PARSE = /^(?:"([^"]+)"|'([^']+)'|(\S+))\s+(?:"([^"]+)"|'([^']+)'|(\S[^\n]*?))\s*$/;
const COMMANDS_DIR = join(homedir(), '.claude', 'commands');

@@ -81,5 +83,3 @@ const SLASH_COMMAND_FILE = join(COMMANDS_DIR, 'cloudctx-save.md');

if (renameMatch) {
const oldName = renameMatch[1] || renameMatch[2] || renameMatch[3];
const newName = renameMatch[4] || renameMatch[5] || renameMatch[6];
handleRenameCommand(oldName, newName);
handleRename(sessionId, renameMatch[1]);
process.exit(0);

@@ -137,30 +137,72 @@ }

function handleRenameCommand(rawOld, rawNew) {
const oldName = (rawOld || '').trim();
const newName = (rawNew || '').trim();
function stripOuterQuotes(s) {
const q = s.match(/^"(.*)"$/) || s.match(/^'(.*)'$/);
return q ? q[1] : s;
}
function reply(reason) {
console.log(JSON.stringify({ decision: 'block', reason: `[CloudCtx] ${reason}` }));
}
function handleRename(sessionId, argsStr) {
try {
if (!oldName || !newName) throw new Error('Both old and new names are required');
const args = (argsStr || '').trim();
if (!args) return reply('Usage: /cloudctx-rename <new-name> (or) /cloudctx-rename <old> <new>');
const db = getDb();
migrate(db);
const exists = db.prepare('SELECT name FROM saved_threads WHERE name = ?').get(newName);
if (exists) {
// Try to parse as two-arg form
const two = args.match(TWO_ARG_PARSE);
if (two) {
const maybeOld = two[1] || two[2] || two[3];
const maybeNew = two[4] || two[5] || two[6];
const oldExists = db.prepare('SELECT name FROM saved_threads WHERE name = ?').get(maybeOld);
if (oldExists) {
if (maybeOld === maybeNew) {
db.close();
return reply(`Already named "${maybeNew}" — no change.`);
}
const collides = db.prepare('SELECT name FROM saved_threads WHERE name = ?').get(maybeNew);
if (collides) {
db.close();
return reply(`Cannot rename: "${maybeNew}" already exists`);
}
db.prepare('UPDATE saved_threads SET name = ? WHERE name = ?').run(maybeNew, maybeOld);
db.close();
return reply(`✓ Renamed "${maybeOld}" → "${maybeNew}"`);
}
// First token wasn't a real thread — fall through to single-arg mode using the full args string
}
// Single-arg mode: rename THIS session's saved thread to the whole args string
const newName = stripOuterQuotes(args).trim();
if (!newName) {
db.close();
const out = { decision: 'block', reason: `[CloudCtx] Cannot rename: "${newName}" already exists` };
console.log(JSON.stringify(out));
return;
return reply('New name cannot be empty');
}
const result = db.prepare('UPDATE saved_threads SET name = ? WHERE name = ?').run(newName, oldName);
db.close();
if (result.changes === 0) {
const out = { decision: 'block', reason: `[CloudCtx] No saved thread named "${oldName}"` };
console.log(JSON.stringify(out));
return;
const current = db.prepare(
'SELECT name FROM saved_threads WHERE session_id = ? ORDER BY saved_at DESC LIMIT 1'
).get(sessionId);
if (!current) {
db.close();
return reply(`This session has no saved thread. Save it first with /cloudctx-save <name>`);
}
const out = { decision: 'block', reason: `[CloudCtx] ✓ Renamed "${oldName}" → "${newName}"` };
console.log(JSON.stringify(out));
if (current.name === newName) {
db.close();
return reply(`Already named "${newName}" — no change.`);
}
const collides = db.prepare('SELECT name FROM saved_threads WHERE name = ?').get(newName);
if (collides) {
db.close();
return reply(`Cannot rename: "${newName}" already exists`);
}
db.prepare('UPDATE saved_threads SET name = ? WHERE name = ?').run(newName, current.name);
db.close();
return reply(`✓ Renamed "${current.name}" → "${newName}"`);
} catch (e) {
const out = { decision: 'block', reason: `[CloudCtx] Rename failed: ${e.message}` };
console.log(JSON.stringify(out));
return reply(`Rename failed: ${e.message}`);
}

@@ -167,0 +209,0 @@ }

@@ -23,4 +23,4 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from 'fs';

const RENAME_COMMAND_BODY = `---
description: Rename a saved CloudCtx thread
argument-hint: "<old-name>" "<new-name>"
description: Rename the current session's saved thread (or an older one via two args)
argument-hint: <new-name> — or: <old-name> <new-name>
---

@@ -27,0 +27,0 @@

{
"name": "cloudctx",
"version": "0.1.6",
"version": "0.1.7",
"description": "Persistent memory for Claude Code. One command, full recall.",

@@ -5,0 +5,0 @@ "type": "module",