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


tests/fixtures/fake-claude.js (2167 bytes)

1 // Stand-in for the Claude Code CLI: returns a verdict for every idea in the prompt and records how
2 // it was called, so tests can check flags and environment without spending tokens. It pairs an
3 // idea with a listed project when the idea names that project. It answers with the folder path,
4 // or with the project name when FAKE_CLAUDE_ANSWER=name (as Haiku did). To a question about the
5 // ideas, it answers with the first idea of the prompt.
6 import fs from 'node:fs';
7 
8 let input = '';
9 for await (const chunk of process.stdin) input += chunk;
10 
11 if (process.env.FAKE_CLAUDE_LOG) {
12   const env = {};
13   for (const key of ['CLAUDECODE', 'CLAUDE_EFFORT', 'CLAUDE_CODE_MESSAGING_SOCKET', 'ANTHROPIC_BASE_URL', 'MAX_THINKING_TOKENS']) {
14     env[key] = process.env[key] ?? null;
15   }
16   fs.writeFileSync(process.env.FAKE_CLAUDE_LOG, JSON.stringify({ args: process.argv.slice(2), env, input }));
17 }
18 
19 // Each project line is "name: folder" or "name: folder — what the README says".
20 const projects = (input.split('Projects on this machine:\n')[1] || '')
21   .split('\n\n')[0]
22   .split('\n')
23   .filter(Boolean)
24   .map((line) => ({ name: line.slice(0, line.indexOf(': ')), dir: line.slice(line.indexOf(': ') + 2).split(' — ')[0] }));
25 const answer = (p) => (p ? (process.env.FAKE_CLAUDE_ANSWER === 'name' ? p.name : p.dir) : '');
26 const ideas = [...(input.split('Ideas to triage')[1] || '').matchAll(/^#(\d+)(?: \[[^\]]*\])?: (.*)$/gm)];
27 const verdicts = ideas.map(([, id, text], i) => ({
28   id: Number(id),
29   verdict: i === 2 ? 'skip' : 'do',
30   impact: 3,
31   size: 's',
32   model: i === 1 ? 'haiku' : 'sonnet',
33   title: `Idea ${id}`,
34   why: 'test',
35   brief: `Build ${id}.`,
36   project: answer(projects.find((p) => text.toLowerCase().includes(p.name.toLowerCase()))),
37 }));
38 
39 const question = input.split('\nQuestion: ')[1];
40 const first = input.match(/\*\*#(\d+) /);
41 
42 process.stdout.write(JSON.stringify({
43   type: 'result',
44   subtype: 'success',
45   is_error: false,
46   result: question ? `#${first ? first[1] : '?'} fits "${question.trim()}".` : '',
47   structured_output: question ? undefined : { verdicts },
48   total_cost_usd: 0.0012,
49   usage: { input_tokens: 321, output_tokens: 45 },
50 }));