vibe-ideas 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/LICENSE +21 -0
- package/README.md +275 -0
- package/data/projects.json +3117 -0
- package/dist/cli.js +206 -0
- package/package.json +47 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// apps/cli.ts
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { z as z3 } from "zod";
|
|
7
|
+
|
|
8
|
+
// apps/data.ts
|
|
9
|
+
import { readFile } from "fs/promises";
|
|
10
|
+
import { dirname, resolve } from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
// apps/filter.ts
|
|
15
|
+
function filterProjects(projects, filters = {}) {
|
|
16
|
+
const level = filters.level?.toLowerCase();
|
|
17
|
+
const category = filters.category?.toLowerCase();
|
|
18
|
+
const language = filters.language?.toLowerCase();
|
|
19
|
+
return projects.filter((project) => {
|
|
20
|
+
if (level && project.level.toLowerCase() !== level) return false;
|
|
21
|
+
if (category && project.category.toLowerCase() !== category) return false;
|
|
22
|
+
if (language && project.language.toLowerCase() !== language) return false;
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// apps/data.ts
|
|
28
|
+
var ProjectSchema = z.object({
|
|
29
|
+
id: z.string(),
|
|
30
|
+
title: z.string(),
|
|
31
|
+
description: z.string(),
|
|
32
|
+
level: z.string(),
|
|
33
|
+
category: z.string(),
|
|
34
|
+
language: z.string(),
|
|
35
|
+
techStack: z.array(z.string()),
|
|
36
|
+
source: z.string()
|
|
37
|
+
});
|
|
38
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
var __dirname = dirname(__filename);
|
|
40
|
+
var projectsPath = resolve(__dirname, "../data/projects.json");
|
|
41
|
+
var cachedProjects = null;
|
|
42
|
+
async function loadProjects() {
|
|
43
|
+
if (cachedProjects) return cachedProjects;
|
|
44
|
+
const raw = await readFile(projectsPath, "utf8");
|
|
45
|
+
const parsed = ProjectSchema.array().parse(JSON.parse(raw));
|
|
46
|
+
cachedProjects = parsed;
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
async function getProjectById(id) {
|
|
50
|
+
const projects = await loadProjects();
|
|
51
|
+
return projects.find((project) => project.id === id);
|
|
52
|
+
}
|
|
53
|
+
async function getFilteredProjects(filters = {}) {
|
|
54
|
+
const projects = await loadProjects();
|
|
55
|
+
return filterProjects(projects, filters);
|
|
56
|
+
}
|
|
57
|
+
async function getFilterValues() {
|
|
58
|
+
const projects = await loadProjects();
|
|
59
|
+
const unique = (values) => [...new Set(values)].sort((a, b) => a.localeCompare(b));
|
|
60
|
+
return {
|
|
61
|
+
level: unique(projects.map((project) => project.level)),
|
|
62
|
+
category: unique(projects.map((project) => project.category)),
|
|
63
|
+
language: unique(projects.map((project) => project.language))
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// apps/format.ts
|
|
68
|
+
import { z as z2 } from "zod";
|
|
69
|
+
var FormatSchema = z2.enum(["json", "human", "text"]);
|
|
70
|
+
function normalizeFormat(format) {
|
|
71
|
+
return FormatSchema.parse(format ?? "json");
|
|
72
|
+
}
|
|
73
|
+
function humanOne(project) {
|
|
74
|
+
const boxLine = "\u2501".repeat(Math.max(16, project.title.length + 2));
|
|
75
|
+
return [
|
|
76
|
+
`\u{1F4E6} ${project.title}`,
|
|
77
|
+
boxLine,
|
|
78
|
+
`Level: ${project.level}`,
|
|
79
|
+
`Category: ${project.category}`,
|
|
80
|
+
`Language: ${project.language}`,
|
|
81
|
+
`Source: ${project.source}`,
|
|
82
|
+
"",
|
|
83
|
+
project.description
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
86
|
+
function textOne(project) {
|
|
87
|
+
return [
|
|
88
|
+
`id: ${project.id}`,
|
|
89
|
+
`title: ${project.title}`,
|
|
90
|
+
`description: ${project.description}`,
|
|
91
|
+
`level: ${project.level}`,
|
|
92
|
+
`category: ${project.category}`,
|
|
93
|
+
`language: ${project.language}`,
|
|
94
|
+
`techStack: ${project.techStack.join(", ")}`,
|
|
95
|
+
`source: ${project.source}`
|
|
96
|
+
].join("\n");
|
|
97
|
+
}
|
|
98
|
+
function formatProjects(projects, format = "json") {
|
|
99
|
+
const normalized = normalizeFormat(format);
|
|
100
|
+
const list = Array.isArray(projects) ? projects : [projects];
|
|
101
|
+
if (normalized === "json") return JSON.stringify(Array.isArray(projects) ? projects : projects, null, 2);
|
|
102
|
+
if (normalized === "human") return list.map(humanOne).join("\n\n");
|
|
103
|
+
return list.map(textOne).join("\n\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// apps/random.ts
|
|
107
|
+
function mulberry32(seed) {
|
|
108
|
+
let t = seed >>> 0;
|
|
109
|
+
return function() {
|
|
110
|
+
t += 1831565813;
|
|
111
|
+
let r = Math.imul(t ^ t >>> 15, 1 | t);
|
|
112
|
+
r ^= r + Math.imul(r ^ r >>> 7, 61 | r);
|
|
113
|
+
return ((r ^ r >>> 14) >>> 0) / 4294967296;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function pickRandomItems(items, count = 1, seed) {
|
|
117
|
+
if (items.length === 0) return [];
|
|
118
|
+
const rng = seed === void 0 ? Math.random : mulberry32(seed);
|
|
119
|
+
const pool = [...items];
|
|
120
|
+
const selected = [];
|
|
121
|
+
const limit = Math.min(count, pool.length);
|
|
122
|
+
for (let i = 0; i < limit; i++) {
|
|
123
|
+
const index = Math.floor(rng() * pool.length);
|
|
124
|
+
selected.push(pool.splice(index, 1)[0]);
|
|
125
|
+
}
|
|
126
|
+
return selected;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// apps/cli.ts
|
|
130
|
+
var program = new Command();
|
|
131
|
+
var filterSchema = z3.object({
|
|
132
|
+
level: z3.string().optional(),
|
|
133
|
+
category: z3.string().optional(),
|
|
134
|
+
language: z3.string().optional()
|
|
135
|
+
});
|
|
136
|
+
function handleError(error) {
|
|
137
|
+
if (error instanceof z3.ZodError) {
|
|
138
|
+
console.error(JSON.stringify({ error: error.message, code: "ERR_INVALID_INPUT", issues: error.issues }, null, 2));
|
|
139
|
+
process.exit(2);
|
|
140
|
+
}
|
|
141
|
+
if (error instanceof Error) {
|
|
142
|
+
console.error(JSON.stringify({ error: error.message, code: "ERR_INVALID_INPUT" }, null, 2));
|
|
143
|
+
process.exit(2);
|
|
144
|
+
}
|
|
145
|
+
console.error(JSON.stringify({ error: "Invalid input", code: "ERR_INVALID_INPUT" }, null, 2));
|
|
146
|
+
process.exit(2);
|
|
147
|
+
}
|
|
148
|
+
async function runFiltered(format, count, seed, filters) {
|
|
149
|
+
const projects = await getFilteredProjects(filters);
|
|
150
|
+
if (projects.length === 0) {
|
|
151
|
+
console.error(JSON.stringify({ error: "No projects match filters", code: "ERR_NO_MATCH", filters }, null, 2));
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
const selected = count > 0 ? pickRandomItems(projects, count, seed) : projects;
|
|
155
|
+
process.stdout.write(formatProjects(selected.length === 1 ? selected[0] : selected, format) + "\n");
|
|
156
|
+
}
|
|
157
|
+
program.name("vibe").description("Vibe CLI - random coding project ideas").option("-f, --format <format>", "output format", "json");
|
|
158
|
+
program.command("random").option("--level <level>").option("--category <category>").option("--language <language>").option("--count <count>", "number of projects", "1").option("--seed <seed>").action(async (options) => {
|
|
159
|
+
try {
|
|
160
|
+
const parsed = filterSchema.parse(options);
|
|
161
|
+
const count = Number.parseInt(options.count, 10);
|
|
162
|
+
const seed = options.seed === void 0 ? void 0 : Number.parseInt(options.seed, 10);
|
|
163
|
+
if (Number.isNaN(count) || count < 1) throw new Error("count must be a positive integer");
|
|
164
|
+
if (options.seed !== void 0 && Number.isNaN(seed)) throw new Error("seed must be a number");
|
|
165
|
+
await runFiltered(program.opts().format, count, seed, parsed);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
handleError(error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
program.command("list").option("--level <level>").option("--category <category>").option("--language <language>").option("--count <count>", "number of projects", "0").action(async (options) => {
|
|
171
|
+
try {
|
|
172
|
+
const parsed = filterSchema.parse(options);
|
|
173
|
+
const count = Number.parseInt(options.count, 10);
|
|
174
|
+
if (Number.isNaN(count) || count < 0) throw new Error("count must be a non-negative integer");
|
|
175
|
+
const projects = await getFilteredProjects(parsed);
|
|
176
|
+
if (projects.length === 0) {
|
|
177
|
+
console.error(JSON.stringify({ error: "No projects match filters", code: "ERR_NO_MATCH", filters: parsed }, null, 2));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
const selected = count > 0 ? projects.slice(0, count) : projects;
|
|
181
|
+
process.stdout.write(formatProjects(selected.length === 1 ? selected[0] : selected, program.opts().format) + "\n");
|
|
182
|
+
} catch (error) {
|
|
183
|
+
handleError(error);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
program.command("get").argument("<id>").action(async (id) => {
|
|
187
|
+
try {
|
|
188
|
+
const project = await getProjectById(id);
|
|
189
|
+
if (!project) {
|
|
190
|
+
console.error(JSON.stringify({ error: "No projects match filters", code: "ERR_NO_MATCH", filters: { id } }, null, 2));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
process.stdout.write(formatProjects(project, program.opts().format) + "\n");
|
|
194
|
+
} catch (error) {
|
|
195
|
+
handleError(error);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
program.command("filters").action(async () => {
|
|
199
|
+
try {
|
|
200
|
+
const values = await getFilterValues();
|
|
201
|
+
process.stdout.write(JSON.stringify(values, null, 2) + "\n");
|
|
202
|
+
} catch (error) {
|
|
203
|
+
handleError(error);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
program.parseAsync(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vibe-ideas",
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/evolv3-ai/vibe-ideas.git"
|
|
6
|
+
},
|
|
7
|
+
"homepage": "https://github.com/evolv3-ai/vibe-ideas#readme",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"url": "https://github.com/evolv3-ai/vibe-ideas/issues"
|
|
10
|
+
},
|
|
11
|
+
"version": "1.0.0",
|
|
12
|
+
"description": "Random coding project ideas for AI agents and developers",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"bin": {
|
|
15
|
+
"vibe-ideas": "dist/cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"data/projects.json"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"build-data": "bun run scripts/build-data.ts",
|
|
24
|
+
"dev": "bun run apps/cli.ts",
|
|
25
|
+
"prepublishOnly": "npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"cli",
|
|
29
|
+
"project-ideas",
|
|
30
|
+
"coding",
|
|
31
|
+
"developer-tools",
|
|
32
|
+
"ai-agents",
|
|
33
|
+
"random"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"commander": "^12.1.0",
|
|
41
|
+
"zod": "^3.25.67"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"tsup": "^8.0.0",
|
|
45
|
+
"typescript": "^5.8.0"
|
|
46
|
+
}
|
|
47
|
+
}
|