Recently Written · git

ideamine

An idea inbox for Claude Code: /idea saves ideas at zero tokens; Claude triages them and routes each to the cheapest model that can build it.

git clone https://github.com/equwal/ideamine

Log | Files | Refs


src/archive.js (2645 bytes)

1 // Each change of the archive goes through here: to the archive on this machine, or to the ideamine
2 // server when sync is on. Callers await each function in both cases. When the server does not
3 // answer, a change resolves to { queued: true, reason }: it waits in the outbox and goes later.
4 
5 import os from 'node:os';
6 import { absolute, addIdeas, applyTriage, findIdea, load, removeIdeas, updateIdea } from './store.js';
7 import * as sync from './sync.js';
8 
9 let offline = null;
10 
11 /**
12  * The archive, fresh from the server when sync is on. When the server does not answer, the copy on
13  * this machine, and offline() tells why.
14  */
15 export async function fresh({ timeoutMs = 1500 } = {}) {
16   offline = null;
17   if (sync.enabled()) {
18     try {
19       await sync.pull({ timeoutMs });
20     } catch (e) {
21       if (!(e instanceof sync.SyncError)) throw e;
22       offline = e.message;
23     }
24   }
25   return load();
26 }
27 
28 /** Why the last fresh() gave the copy on this machine, or null. */
29 export const offlineReason = () => offline;
30 
31 export async function add(texts, { source = 'mcp', project = null, session = null, tags = [] } = {}) {
32   const host = os.hostname();
33   if (!sync.enabled()) return addIdeas(texts, { source, project, session, tags, host });
34   const items = (Array.isArray(texts) ? texts : [texts]).map((t) => String(t ?? '').trim()).filter(Boolean);
35   if (!items.length) throw new Error('idea text is empty');
36   const out = await sync.change({ op: 'add', texts: items, source, project: project ? absolute(project) : null, session, tags, host });
37   if (out.queued) return out;
38   const db = load();
39   return out.value.map((r) => ({ idea: findIdea(db, r.id), similar: r.similar }));
40 }
41 
42 export async function update(id, patch = {}) {
43   if (!sync.enabled()) return updateIdea(id, patch);
44   const clean = patch.project ? { ...patch, project: absolute(patch.project) } : patch;
45   const out = await sync.change({ op: 'update', id, patch: clean });
46   return out.queued ? out : findIdea(load(), out.value.id);
47 }
48 
49 export async function remove(ids) {
50   if (!sync.enabled()) return removeIdeas(ids);
51   const out = await sync.change({ op: 'remove', ids });
52   return out.queued ? out : out.value;
53 }
54 
55 export async function triage(verdicts, { by = null, projects = null } = {}) {
56   if (!sync.enabled()) return applyTriage(verdicts, { by, projects });
57   const out = await sync.change({ op: 'triage', verdicts, by, projects });
58   return out.queued ? out : out.value;
59 }
60 
61 /** The text for a change that waits in the outbox. */
62 export const queuedText = (out) => `The ideamine server does not answer (${out.reason}). The change waits here and goes to the server later.`;