project-cypher 0.12.0 → 0.14.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 (2) hide show
  1. package/bin/cypher.js +30 -4
  2. package/package.json +1 -1
package/bin/cypher.js CHANGED
@@ -194,7 +194,17 @@ const cmds = {
194
194
  let id = slug(p), n = 2; if (id === 'root') id = 'root-project'; while (list.some(x => x.id === id)) id = slug(p) + '-' + n++;
195
195
  list.push({ id, name: path.basename(p), path: p, summary: '' }); writeRegistry(list); console.log(`added ${id} (${p})`);
196
196
  },
197
- remove(id) { writeRegistry(readRegistry().filter(x => x.id !== id)); console.log('removed ' + id); },
197
+ async remove(id, ...rest) {
198
+ if (id && id !== '--yes') { writeRegistry(readRegistry().filter(x => x.id !== id)); return console.log('removed project ' + id); }
199
+ console.log(`This fully removes Cypher from this computer:\n - unlinks it from your phone app\n - stops and deletes the background service\n - deletes ${CFG_DIR} (login, project list, Root notes, chat sessions)\nYour actual project folders are NOT touched.`);
200
+ if (id !== '--yes' && !rest.includes('--yes') && !yes(await ask('Continue? [y/N] '), false)) return console.log('cancelled.');
201
+ const c = rawCfg();
202
+ if (c.token) await fetch(httpOf(c.server || 'http://140.238.248.30:3300') + '/unlink', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ token: c.token }) }).then(() => console.log('[ok] unlinked from the app')).catch(() => console.log('[!!] could not reach the server; remove this computer from the app Projects list'));
203
+ SVC.remove(); console.log('[ok] background service removed');
204
+ fs.rmSync(CFG_DIR, { recursive: true, force: true }); console.log('[ok] deleted ' + CFG_DIR);
205
+ console.log('\nClean. To reinstall: npm i -g project-cypher@latest && cypher init\nTo also remove the program itself: npm uninstall -g project-cypher');
206
+ },
207
+ removeProject(id) { writeRegistry(readRegistry().filter(x => x.id !== id)); console.log('removed ' + id); },
198
208
  projects() { const c = load(); if (!c.projects.length) return console.log('none yet. cypher add <folder>'); c.projects.forEach(p => console.log(`${p.id.padEnd(20)} ${p.path}`)); },
199
209
  // Service control
200
210
  start() { const c = load(); if (!c.token) return console.log('Not linked yet. Run: cypher init'); console.log(SVC.start() ? 'Cypher is running in the background and will start automatically when this computer turns on.' : 'Could not start the background service. Run it in this window instead: cypher connect'); },
@@ -232,11 +242,26 @@ const cmds = {
232
242
  if (m.type !== 'prompt') return;
233
243
  if (!proj) return tx({ ...ctx, type: 'error', message: 'unknown project' });
234
244
  if (runs.has(proj.id)) return tx({ ...ctx, type: 'error', message: 'Claude is still working on this project.' });
235
- const cfg = load(); const args = ['-p', m.text, '--output-format', 'stream-json', '--verbose', '--permission-mode', cfg.permissionMode || 'acceptEdits'];
245
+ // Attachments from the phone: saved under ~/.cypher/uploads/<project>/, then referenced in the prompt so Claude can Read them.
246
+ let text = String(m.text || ''); const saved = []; const upDir = path.join(CFG_DIR, 'uploads', proj.id);
247
+ if (Array.isArray(m.files) && m.files.length) {
248
+ try {
249
+ fs.mkdirSync(upDir, { recursive: true });
250
+ for (const f of m.files.slice(0, 6)) {
251
+ if (!f || typeof f.data !== 'string' || f.data.length > 16 * 1024 * 1024) continue;
252
+ const nm = String(f.name || 'file').replace(/[^\w.\- ]+/g, '_').slice(-80) || 'file'; const fp = path.join(upDir, Date.now() + '-' + nm);
253
+ fs.writeFileSync(fp, Buffer.from(f.data, 'base64')); saved.push(fp);
254
+ }
255
+ } catch (e) { console.log('attachment save failed: ' + e.message); }
256
+ if (saved.length) text += '\n\n[The user attached ' + saved.length + ' file(s) from their phone. Read them with the Read tool:]\n' + saved.map(x => '- ' + x).join('\n');
257
+ }
258
+ try { for (const d of fs.readdirSync(path.join(CFG_DIR, 'uploads'))) for (const f of fs.readdirSync(path.join(CFG_DIR, 'uploads', d))) { const fp = path.join(CFG_DIR, 'uploads', d, f); if (Date.now() - fs.statSync(fp).mtimeMs > 7 * 864e5) fs.rmSync(fp, { force: true }); } } catch {}
259
+ const cfg = load(); const args = ['-p', text, '--output-format', 'stream-json', '--verbose', '--permission-mode', cfg.permissionMode || 'acceptEdits'];
236
260
  if (proj.root) { const wsd = rawCfg().workspace; args.push('--allowedTools', ROOT_TOOLS); if (wsd) args.push('--add-dir', wsd); }
261
+ if (saved.length) args.push('--add-dir', upDir);
237
262
  if (typeof m.model === 'string' && /^[a-z][a-z0-9.\-\[\]]{0,60}$/i.test(m.model) && m.model !== 'default') args.push('--model', m.model);
238
263
  if (cfg.sessions?.[proj.id]) args.push('--resume', cfg.sessions[proj.id]);
239
- console.log(`[${proj.id}] ${m.text.slice(0, 60)}`);
264
+ console.log(`[${proj.id}] ${String(m.text || '').slice(0, 60)}${saved.length ? ' (+' + saved.length + ' files)' : ''}`);
240
265
  const ch = spawn('claude', args, { cwd: proj.path, stdio: ['ignore', 'pipe', 'pipe'] }); runs.set(proj.id, ch);
241
266
  let buf = '', err = '';
242
267
  const line = l => {
@@ -266,7 +291,8 @@ const help = () => console.log(`Project Cypher
266
291
  cypher link [server-url] link this computer to your phone (QR or code), add this folder, go online
267
292
  cypher pair <server-url> <code> alternative: pair with a code made in the app
268
293
  cypher add [folder] register a project folder (or just ask Root in the app)
269
- cypher projects | remove <id> list / remove projects
294
+ cypher projects | remove <id> list projects / remove one project
295
+ cypher remove FULL removal (unlink, service, all local data) for a clean reinstall
270
296
  cypher disconnect unlink this computer from the app
271
297
  cypher connect go online in this window (no background service)
272
298
  cypher start | stop start / stop the background service (auto-starts at boot)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-cypher",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Connect your machine's Claude Code projects to the Project Cypher phone app.",
5
5
  "bin": {
6
6
  "cypher": "bin/cypher.js"