ideabox 1.0.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.
package/bin/cli.mjs ADDED
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from "node:child_process";
4
+ import {
5
+ cpSync,
6
+ existsSync,
7
+ mkdirSync,
8
+ readFileSync,
9
+ writeFileSync,
10
+ rmSync,
11
+ } from "node:fs";
12
+ import { homedir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = dirname(__filename);
18
+ const ROOT = resolve(__dirname, "..");
19
+
20
+ const SKILLS = ["ideabox", "research", "backlog", "profile"];
21
+ const MARKER_START = "<!-- ideabox:start -->";
22
+ const MARKER_END = "<!-- ideabox:end -->";
23
+ const HOME = homedir();
24
+ const DATA_DIR = join(HOME, ".ideabox");
25
+
26
+ function hasCommand(cmd) {
27
+ try {
28
+ const check = process.platform === "win32" ? `where ${cmd}` : `command -v ${cmd}`;
29
+ execSync(check, { stdio: "ignore" });
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ function autoDetect() {
37
+ const targets = [];
38
+ if (hasCommand("claude")) targets.push("claude");
39
+ if (hasCommand("codex")) targets.push("codex");
40
+ if (targets.length === 0) {
41
+ console.log("No supported CLI detected. Installing to .claude/skills/ (default).");
42
+ targets.push("claude");
43
+ }
44
+ return targets;
45
+ }
46
+
47
+ function getSkillsDir(target) {
48
+ switch (target) {
49
+ case "claude":
50
+ return join(process.cwd(), ".claude", "skills");
51
+ case "codex":
52
+ return join(HOME, ".codex", "skills");
53
+ case "codex-project":
54
+ return join(process.cwd(), ".codex", "skills");
55
+ default:
56
+ return join(process.cwd(), ".claude", "skills");
57
+ }
58
+ }
59
+
60
+ function getInstructionFile(target) {
61
+ if (target === "codex" || target === "codex-project") {
62
+ return join(process.cwd(), "AGENTS.md");
63
+ }
64
+ return join(process.cwd(), "CLAUDE.md");
65
+ }
66
+
67
+ function getInstructionSource(target) {
68
+ if (target === "codex" || target === "codex-project") {
69
+ return join(ROOT, "AGENTS.md");
70
+ }
71
+ return join(ROOT, "CLAUDE.md");
72
+ }
73
+
74
+ function stripSection(content) {
75
+ const startIdx = content.indexOf(MARKER_START);
76
+ const endIdx = content.indexOf(MARKER_END);
77
+ if (startIdx === -1 || endIdx === -1) return content;
78
+ const before = content.slice(0, startIdx).trimEnd();
79
+ const after = content.slice(endIdx + MARKER_END.length).trimStart();
80
+ return before + (after ? "\n\n" + after : "") + "\n";
81
+ }
82
+
83
+ function mergeInstructionFile(target) {
84
+ const destPath = getInstructionFile(target);
85
+ const sourcePath = getInstructionSource(target);
86
+
87
+ if (!existsSync(sourcePath)) return;
88
+
89
+ const sourceContent = readFileSync(sourcePath, "utf-8").trim();
90
+ const section = `${MARKER_START}\n${sourceContent}\n${MARKER_END}`;
91
+
92
+ if (existsSync(destPath)) {
93
+ let existing = readFileSync(destPath, "utf-8");
94
+ existing = stripSection(existing);
95
+ writeFileSync(destPath, existing.trimEnd() + "\n\n" + section + "\n");
96
+ } else {
97
+ writeFileSync(destPath, section + "\n");
98
+ }
99
+ }
100
+
101
+ function installSkills(target) {
102
+ const skillsDir = getSkillsDir(target);
103
+ const srcSkillsDir = join(ROOT, "skills");
104
+
105
+ for (const skill of SKILLS) {
106
+ const src = join(srcSkillsDir, skill);
107
+ const dest = join(skillsDir, skill);
108
+
109
+ if (!existsSync(src)) {
110
+ console.log(` Skipping ${skill} (not found in plugin)`);
111
+ continue;
112
+ }
113
+
114
+ // Copy skill files
115
+ mkdirSync(dest, { recursive: true });
116
+ cpSync(src, dest, { recursive: true });
117
+
118
+ // For skills that reference phases/references, copy them locally
119
+ // so cross-skill references work without depending on sibling paths
120
+ if (skill !== "ideabox" && skill !== "profile") {
121
+ const ideasRefs = join(srcSkillsDir, "ideabox", "references");
122
+ const ideasPhases = join(srcSkillsDir, "ideabox", "phases");
123
+ if (existsSync(ideasRefs)) {
124
+ const destRefs = join(dest, "references");
125
+ mkdirSync(destRefs, { recursive: true });
126
+ cpSync(ideasRefs, destRefs, { recursive: true });
127
+ }
128
+ if (existsSync(ideasPhases)) {
129
+ const destPhases = join(dest, "phases");
130
+ mkdirSync(destPhases, { recursive: true });
131
+ cpSync(ideasPhases, destPhases, { recursive: true });
132
+ }
133
+ }
134
+
135
+ console.log(` Installed ${skill}`);
136
+ }
137
+ }
138
+
139
+ function ensureDataDir() {
140
+ if (!existsSync(DATA_DIR)) {
141
+ mkdirSync(DATA_DIR, { recursive: true });
142
+ console.log(` Created ${DATA_DIR}`);
143
+ }
144
+ }
145
+
146
+ function install(targets) {
147
+ console.log("\nIdeaBox -- Installing...\n");
148
+ ensureDataDir();
149
+
150
+ for (const target of targets) {
151
+ console.log(`Target: ${target}`);
152
+ installSkills(target);
153
+ mergeInstructionFile(target);
154
+ console.log(` Merged instruction file for ${target}`);
155
+ }
156
+
157
+ console.log("\nDone! Run /ideabox to get started.\n");
158
+ }
159
+
160
+ function uninstall(targets) {
161
+ console.log("\nIdeaBox -- Uninstalling...\n");
162
+
163
+ for (const target of targets) {
164
+ const skillsDir = getSkillsDir(target);
165
+
166
+ for (const skill of SKILLS) {
167
+ const dest = join(skillsDir, skill);
168
+ if (existsSync(dest)) {
169
+ rmSync(dest, { recursive: true });
170
+ console.log(` Removed ${skill}`);
171
+ }
172
+ }
173
+
174
+ const instrPath = getInstructionFile(target);
175
+ if (existsSync(instrPath)) {
176
+ const content = readFileSync(instrPath, "utf-8");
177
+ if (content.includes(MARKER_START)) {
178
+ writeFileSync(instrPath, stripSection(content));
179
+ console.log(` Cleaned instruction file for ${target}`);
180
+ }
181
+ }
182
+ }
183
+
184
+ console.log(`\nNote: ${DATA_DIR} was kept. Delete it manually if you want to remove all data.\n`);
185
+ }
186
+
187
+ function doctor() {
188
+ console.log("\nIdeaBox -- Health Check\n");
189
+ let ok = true;
190
+
191
+ // Check 1: Node version
192
+ const nodeVersion = parseInt(process.version.slice(1));
193
+ if (nodeVersion >= 18) {
194
+ console.log(` ✓ Node.js ${process.version}`);
195
+ } else {
196
+ console.log(` ✗ Node.js ${process.version} (need >= 18)`);
197
+ ok = false;
198
+ }
199
+
200
+ // Check 2: Data directory
201
+ if (existsSync(DATA_DIR)) {
202
+ console.log(` ✓ Data directory exists (${DATA_DIR})`);
203
+ } else {
204
+ console.log(` ✗ Data directory missing (${DATA_DIR})`);
205
+ ok = false;
206
+ }
207
+
208
+ // Check 3: Profile
209
+ const profilePath = join(DATA_DIR, "profile.json");
210
+ if (existsSync(profilePath)) {
211
+ console.log(` ✓ Profile configured`);
212
+ } else {
213
+ console.log(` ○ No profile yet (will be created on first /ideas run)`);
214
+ }
215
+
216
+ // Check 4: Skills installed (check both Claude and Codex paths)
217
+ const claudeSkills = join(process.cwd(), ".claude", "skills");
218
+ const codexSkills = join(HOME, ".codex", "skills");
219
+ const claudeInstalled = SKILLS.filter((s) => existsSync(join(claudeSkills, s)));
220
+ const codexInstalled = SKILLS.filter((s) => existsSync(join(codexSkills, s)));
221
+ const installed = claudeInstalled.length >= codexInstalled.length ? claudeInstalled : codexInstalled;
222
+ const installTarget = claudeInstalled.length >= codexInstalled.length ? "Claude" : "Codex";
223
+ if (installed.length === SKILLS.length) {
224
+ console.log(` ✓ All ${SKILLS.length} skills installed (${installTarget})`);
225
+ } else if (installed.length > 0) {
226
+ console.log(` △ ${installed.length}/${SKILLS.length} skills installed (${installed.join(", ")})`);
227
+ } else {
228
+ console.log(` ✗ No skills installed in current project`);
229
+ ok = false;
230
+ }
231
+
232
+ // Check 5: Claude Code / Codex available
233
+ if (hasCommand("claude")) {
234
+ console.log(` ✓ Claude Code CLI detected`);
235
+ } else if (hasCommand("codex")) {
236
+ console.log(` ✓ Codex CLI detected`);
237
+ } else {
238
+ console.log(` ○ No supported CLI found (optional)`);
239
+ }
240
+
241
+ console.log(ok ? "\n All checks passed!\n" : "\n Some issues found.\n");
242
+ }
243
+
244
+ // --- Main ---
245
+ function parseTargets(args) {
246
+ const explicit = args.slice(1).filter((a) => !a.startsWith("-") && a !== "--");
247
+ return explicit.length > 0 ? explicit : autoDetect();
248
+ }
249
+
250
+ const args = process.argv.slice(2);
251
+ const command = args[0] || "init";
252
+
253
+ switch (command) {
254
+ case "init":
255
+ install(parseTargets(args));
256
+ break;
257
+ case "uninstall":
258
+ uninstall(parseTargets(args));
259
+ break;
260
+ case "doctor":
261
+ doctor();
262
+ break;
263
+ default:
264
+ console.log(`Unknown command: ${command}`);
265
+ console.log("Usage: ideabox-agent [init|uninstall|doctor]");
266
+ process.exit(1);
267
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "ideabox",
3
+ "version": "1.0.0",
4
+ "description": "Data-driven project idea engine for coding agents. Research, brainstorm, plan, and build — all from one command.",
5
+ "type": "module",
6
+ "bin": {
7
+ "ideabox": "bin/cli.mjs"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "skills/",
12
+ "CLAUDE.md",
13
+ "AGENTS.md",
14
+ "README.md"
15
+ ],
16
+ "keywords": [
17
+ "ideabox",
18
+ "ideas",
19
+ "project-ideas",
20
+ "claude-code",
21
+ "codex-cli"
22
+ ],
23
+ "author": "Pawan Paudel",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/pawanpaudel93/ideabox.git"
28
+ },
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "devDependencies": {
33
+ "bumpp": "^10.4.1"
34
+ },
35
+ "scripts": {
36
+ "version": "node --input-type=commonjs -e \"const fs=require('fs');const p=JSON.parse(fs.readFileSync('.claude-plugin/plugin.json'));p.version=require('./package.json').version;fs.writeFileSync('.claude-plugin/plugin.json',JSON.stringify(p,null,2)+'\\n');\"",
37
+ "release": "bumpp && pnpm publish"
38
+ }
39
+ }
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: backlog
3
+ description: >
4
+ View your IdeaBox idea backlog — saved, dismissed, planned, and built ideas.
5
+ Use when asked to "show ideas", "idea backlog", "saved ideas", "what ideas do I have",
6
+ "ideabox backlog", or "list ideas".
7
+ ---
8
+
9
+ # IdeaBox Backlog
10
+
11
+ Display and manage the user's idea history from `~/.ideabox/ideas.jsonl`.
12
+
13
+ ## Reading the Backlog
14
+
15
+ Read `~/.ideabox/ideas.jsonl`. Each line is a JSON object. There are two record types:
16
+ - **Idea records:** have fields `id`, `title`, `problem`, `scores`, `status`, `evidence`, `monetization`, `tech_stack`, `complexity`
17
+ - **Status update records:** have `type: "status_update"`, `idea_id`, `old_status`, `new_status`, `timestamp`
18
+
19
+ To get the current status of an idea: find the latest `status_update` for that `idea_id`. If no status_update exists, use the idea record's `status` field.
20
+
21
+ Parse arguments from the user's command:
22
+ - `/ideas backlog` — show all ideas grouped by status
23
+ - `/ideas backlog saved` — show only saved ideas
24
+ - `/ideas backlog dismissed` — show only dismissed ideas
25
+ - `/ideas backlog built` — show only built ideas
26
+
27
+ ## Display Format
28
+
29
+ ### If ideas.jsonl does not exist or is empty
30
+ "No ideas in your backlog yet. Run `/ideas` to research your first batch!"
31
+
32
+ ### If ideas exist
33
+
34
+ Group by current status and display:
35
+
36
+ ```
37
+ ## IdeaBox Backlog
38
+
39
+ ### Saved (ready to build)
40
+ | # | Idea | Score | Monetization | Added |
41
+ |---|------|-------|-------------|-------|
42
+ | 1 | MCP Server Testing Framework | 48/60 | Freemium $29/mo | Mar 26 |
43
+
44
+ ### Planned (in progress)
45
+ | # | Idea | Score | Status | Started |
46
+ |---|------|-------|--------|---------|
47
+
48
+ ### Built
49
+ | # | Idea | Score | Shipped |
50
+ |---|------|-------|---------|
51
+
52
+ ### Dismissed
53
+ Showing {count} dismissed ideas. Use `/ideas backlog dismissed` for details.
54
+ ```
55
+
56
+ ## Actions
57
+
58
+ After displaying, offer:
59
+ - "Pick a number to brainstorm and build that idea"
60
+ - "Or run `/ideas` for fresh research"
61
+
62
+ If the user picks a saved idea by number:
63
+ 1. Read the full idea record from `ideas.jsonl`
64
+ 2. Present the complete idea card (problem, evidence, monetization, tech stack, score)
65
+ 3. Ask "Ready to brainstorm this one?"
66
+ 4. If yes:
67
+ a. Append a status update to `~/.ideabox/ideas.jsonl`:
68
+ ```json
69
+ {"type":"status_update","idea_id":"{id}","old_status":"saved","new_status":"planned","timestamp":"{ISO}"}
70
+ ```
71
+ b. Create `.ideabox/` and `.ideabox/session/` directories if they don't exist
72
+ c. Initialize `.ideabox/state.json`:
73
+ ```json
74
+ {
75
+ "session_id": "sess_YYYYMMDD_HHMMSS",
76
+ "current_phase": "02-brainstorm",
77
+ "phases_completed": [],
78
+ "idea": {"id": "{id}", "title": "{title}", "problem": "{problem}"},
79
+ "artifacts": {},
80
+ "started_at": "{ISO}"
81
+ }
82
+ ```
83
+ d. Write the idea details to `.ideabox/session/01-research.md` (so phase 02 has context)
84
+ e. Read `${CLAUDE_SKILL_DIR}/phases/02-brainstorm.md` and follow it
85
+
86
+ ## Comparison Mode
87
+
88
+ If the user says `/ideas backlog compare N M` (e.g., `compare 1 3`):
89
+
90
+ 1. Load both idea records from `ideas.jsonl`
91
+ 2. Present using the visual comparison format from `${CLAUDE_SKILL_DIR}/references/scoring-rubric.md` (Head-to-Head section) — with score bars, evidence, and a recommendation
92
+ 3. Ask: "Build which one? (N / M / neither)"
93
+
94
+ ## Updating Idea Status
95
+
96
+ To update an idea's status, append a NEW line to `~/.ideabox/ideas.jsonl`:
97
+ ```json
98
+ {"type":"status_update","idea_id":"idea_20260326_001","old_status":"saved","new_status":"planned","timestamp":"2026-03-26T17:00:00Z"}
99
+ ```
100
+
101
+ **Never modify existing lines.** Append-only. The latest status_update for an idea_id is the current status.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: ideabox
3
+ description: >
4
+ Full idea-to-code pipeline. Researches real market demand, presents data-backed project ideas,
5
+ then brainstorms, plans, builds, tests, polishes, and ships the one you pick — 9 phases, fully self-contained.
6
+ Use when asked to "give me ideas", "what should I build", "project ideas", "ideabox",
7
+ "find me something to build", "I need a project", "ideas", or "suggest a project".
8
+ argument-hint: "[research|backlog|profile]"
9
+ ---
10
+
11
+ # IdeaBox — Full Pipeline
12
+
13
+ Research -> Present -> Pick -> Brainstorm -> Plan -> Build -> QA -> Polish -> Ship -> Post-Ship -> Learn
14
+
15
+ ## Subcommand Routing
16
+
17
+ Check if the user passed a subcommand:
18
+ - `/ideabox research` -> invoke `ideabox:research` skill
19
+ - `/ideabox backlog` -> invoke `ideabox:backlog` skill
20
+ - `/ideabox profile` -> invoke `ideabox:profile` skill
21
+ - `/ideabox` (no subcommand) -> continue with pipeline below
22
+ - `/ideabox [anything else]` -> respond: "Unknown subcommand. Available: `/ideabox`, `/ideabox research`, `/ideabox backlog`, `/ideabox profile`"
23
+
24
+ ## Pipeline State
25
+
26
+ Check if `.ideabox/state.json` exists in the current project directory.
27
+
28
+ **If state.json exists and `current_phase` is not null:**
29
+ Ask: "You have an in-progress session (Phase: {current_phase}, Idea: {idea.title}). Resume or start fresh?"
30
+ - Resume: read state, load the current phase file, continue from where you left off
31
+ - Start fresh: delete `.ideabox/state.json` and `.ideabox/session/`, proceed as new
32
+
33
+ **If no state.json or starting fresh:**
34
+ 1. Create `.ideabox/` and `.ideabox/session/` directories
35
+ 2. Add `.ideabox/` to the project's `.gitignore` if not already present (session state should not be committed)
36
+ 3. Check for existing artifacts (phase skipping):
37
+ - If `~/.ideabox/research/` has a file from today -> ask: "You already researched today. Use those results or research fresh?"
38
+ - If `.ideabox/session/02-brainstorm-spec.md` exists -> ask: "Found an existing spec. Resume from planning?"
39
+ - If `.ideabox/session/03-plan.md` exists -> ask: "Found an existing plan. Resume from building?"
40
+ 4. Initialize `.ideabox/state.json` with the appropriate starting phase:
41
+ ```json
42
+ {
43
+ "session_id": "sess_YYYYMMDD_HHMMSS",
44
+ "current_phase": "01-research",
45
+ "phases_completed": [],
46
+ "idea": null,
47
+ "artifacts": {},
48
+ "started_at": "{ISO timestamp}"
49
+ }
50
+ ```
51
+
52
+ ## Phase Execution
53
+
54
+ For the current phase, read the corresponding phase file and follow it completely:
55
+
56
+ | Phase | File to Read | Gate Condition |
57
+ |-------|-------------|---------------|
58
+ | 01-research | `phases/01-research.md` | >=3 ideas with >=3 evidence sources each |
59
+ | 02-brainstorm | `phases/02-brainstorm.md` | Spec document written and user-approved |
60
+ | 03-plan | `phases/03-plan.md` | Plan passes self-review (no placeholders) |
61
+ | 04-build | `phases/04-build.md` | All tests pass, two-stage review complete |
62
+ | 05-qa | `phases/05-qa.md` | Health score above threshold, no critical bugs |
63
+ | 06-polish | `phases/06-polish.md` | Before/after evidence, no AI slop detected |
64
+ | 07-ship | `phases/07-ship.md` | PR created, CI passes |
65
+ | 08-post-ship | `phases/08-post-ship.md` | Canary passes or skipped |
66
+ | 09-learn | `phases/09-learn.md` | Preferences updated |
67
+
68
+ **Conditional loading (save tokens):**
69
+ - Phase 06 (polish): If the project has no visual UI (library, API, backend-only), skip directly to phase 07 without reading the phase file
70
+ - Phase 08 (post-ship): If no deployment URL exists and no documentation files exist, skip directly to phase 09
71
+
72
+ **To execute a phase:**
73
+ ```
74
+ Read `${CLAUDE_SKILL_DIR}/phases/{phase_file}` and follow it completely.
75
+ ```
76
+
77
+ ## Phase Transition Protocol
78
+
79
+ After each phase completes and its gate condition is met:
80
+
81
+ 1. **Save artifact path** in state.json `artifacts` field
82
+ 2. **Move phase** to `phases_completed` array
83
+ 3. **Advance** `current_phase` to next phase
84
+ 4. **Write state** to `.ideabox/state.json`
85
+ 5. **Present summary** to user: "Phase {N} complete. {one-line summary of output}"
86
+ 6. **Ask at gate:** "Continue to {next phase name}? (yes / revise / skip remaining / abort)"
87
+ - **yes**: read next phase file and follow it
88
+ - **revise**: stay in current phase, user explains what to change
89
+ - **skip**: jump to phase 09 (learn) to save progress, then end
90
+ - **abort**: save state, end session (can resume later with `/ideas`)
91
+
92
+ ## Phase Rules
93
+
94
+ - **Phases 01-04 are MANDATORY** (research, brainstorm, plan, build) — cannot be skipped
95
+ - **Phases 05-08 can be skipped** if user says "skip" — for CLI tools without UI, skip polish; for unpublished tools, skip ship
96
+ - **Phase 09 (learn) always runs** after any end point — tracks what happened for self-improvement
97
+
98
+ ## Context Management
99
+
100
+ - **Re-read state** before every phase transition to prevent drift
101
+ - **Each phase writes output** to `.ideabox/session/` (e.g., `01-research.md`, `02-brainstorm-spec.md`, `03-plan.md`, `05-qa-report.md`)
102
+ - **Next phase reads** only its own instructions + the previous phase's output (not all phases)
103
+ - **If context is running low:** save state to `.ideabox/state.json`, tell user: "Context getting full. Run `/ideas` in a new session to resume from phase {current_phase}."
104
+
105
+ ## Error Handling
106
+
107
+ - **Phase gate fails:** stay in current phase, explain what's needed to pass
108
+ - **User aborts:** save state, they resume later with `/ideas`
109
+ - **Subagent fails:** note the failure, offer to retry or skip
110
+ - **No profile:** run profile setup inline (don't tell user to run another command)