@withmata/blueprints 0.2.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/.claude/commands/audit.md +179 -0
- package/.claude/commands/discover.md +92 -0
- package/.claude/commands/new-blueprint.md +265 -0
- package/.claude/commands/new-project.md +230 -0
- package/.claude/commands/scaffold-auth.md +310 -0
- package/.claude/commands/scaffold-db.md +270 -0
- package/.claude/commands/scaffold-foundation.md +158 -0
- package/.cursor/commands/audit.md +179 -0
- package/.cursor/commands/discover.md +92 -0
- package/.cursor/commands/new-blueprint.md +265 -0
- package/.cursor/commands/new-project.md +230 -0
- package/.cursor/commands/scaffold-auth.md +310 -0
- package/.cursor/commands/scaffold-db.md +270 -0
- package/.cursor/commands/scaffold-foundation.md +158 -0
- package/.opencode/commands/audit.md +183 -0
- package/.opencode/commands/discover.md +96 -0
- package/.opencode/commands/new-blueprint.md +269 -0
- package/.opencode/commands/new-project.md +234 -0
- package/.opencode/commands/scaffold-auth.md +314 -0
- package/.opencode/commands/scaffold-db.md +274 -0
- package/.opencode/commands/scaffold-foundation.md +162 -0
- package/ENGINEERING.md +47 -0
- package/blueprints/discovery/product-discovery/BLUEPRINT.md +361 -0
- package/blueprints/discovery/product-discovery/templates/archetype-template.md +143 -0
- package/blueprints/discovery/product-discovery/templates/product-brief.md +65 -0
- package/blueprints/discovery/product-discovery/templates/product-thesis.md +64 -0
- package/blueprints/discovery/product-discovery/templates/research-summary.md +43 -0
- package/blueprints/features/auth-better-auth/BLUEPRINT.md +794 -0
- package/blueprints/features/auth-better-auth/files/client/auth-client.ts +31 -0
- package/blueprints/features/auth-better-auth/files/client/context/organization.ts +236 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-create-organization.ts +45 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-has-permission.ts +26 -0
- package/blueprints/features/auth-better-auth/files/client/hooks/use-organization.ts +64 -0
- package/blueprints/features/auth-better-auth/files/client/schema/auth.ts +21 -0
- package/blueprints/features/auth-better-auth/files/client/schema/organization.ts +51 -0
- package/blueprints/features/auth-better-auth/files/client/types/organization.ts +20 -0
- package/blueprints/features/auth-better-auth/files/db/auth-schema.ts +184 -0
- package/blueprints/features/auth-better-auth/files/db/drizzle.config.ts +21 -0
- package/blueprints/features/auth-better-auth/files/middleware/route-protection.ts +84 -0
- package/blueprints/features/auth-better-auth/files/server/auth-db.ts +18 -0
- package/blueprints/features/auth-better-auth/files/server/auth.ts +159 -0
- package/blueprints/features/auth-better-auth/files/server/env.ts +44 -0
- package/blueprints/features/auth-better-auth/files/server/middleware.ts +144 -0
- package/blueprints/features/db-drizzle-postgres/BLUEPRINT.md +596 -0
- package/blueprints/features/db-drizzle-postgres/files/db/drizzle.config.ts +18 -0
- package/blueprints/features/db-drizzle-postgres/files/db/env.example +3 -0
- package/blueprints/features/db-drizzle-postgres/files/db/package.json +33 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/client.ts +34 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/example-entity.ts +73 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/index.ts +8 -0
- package/blueprints/features/db-drizzle-postgres/files/db/src/scripts/seed.ts +50 -0
- package/blueprints/features/db-drizzle-postgres/files/db/tsconfig.json +13 -0
- package/blueprints/features/tailwind-v4/BLUEPRINT.md +29 -0
- package/blueprints/features/ui-shared-components/BLUEPRINT.md +35 -0
- package/blueprints/foundation/monorepo-turbo/BLUEPRINT.md +378 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/globals.css +2 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/layout.tsx +23 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/app/page.tsx +7 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/env.ts +13 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/next.config.ts +12 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/package.json +28 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/postcss.config.mjs +5 -0
- package/blueprints/foundation/monorepo-turbo/files/apps/web/tsconfig.json +18 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/package.json +14 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/postcss.config.mjs +5 -0
- package/blueprints/foundation/monorepo-turbo/files/config/tailwind-config/shared-styles.css +88 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/base.json +19 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/nextjs.json +12 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/package.json +5 -0
- package/blueprints/foundation/monorepo-turbo/files/config/typescript-config/react-library.json +7 -0
- package/blueprints/foundation/monorepo-turbo/files/root/biome.json +46 -0
- package/blueprints/foundation/monorepo-turbo/files/root/gitignore +35 -0
- package/blueprints/foundation/monorepo-turbo/files/root/package.json +25 -0
- package/blueprints/foundation/monorepo-turbo/files/root/pnpm-workspace.yaml +5 -0
- package/blueprints/foundation/monorepo-turbo/files/root/turbo.json +30 -0
- package/dist/index.js +453 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { intro, outro, log as log5 } from "@clack/prompts";
|
|
5
|
+
import pc2 from "picocolors";
|
|
6
|
+
|
|
7
|
+
// src/constants.ts
|
|
8
|
+
var API_URL = process.env["WITHMATA_API_URL"] || "https://blueprints.withmata.dev";
|
|
9
|
+
var VERSION = "0.2.0";
|
|
10
|
+
|
|
11
|
+
// src/steps/auth.ts
|
|
12
|
+
import { log } from "@clack/prompts";
|
|
13
|
+
|
|
14
|
+
// src/lib/credentials.ts
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
unlinkSync
|
|
21
|
+
} from "fs";
|
|
22
|
+
|
|
23
|
+
// src/lib/paths.ts
|
|
24
|
+
import { join } from "path";
|
|
25
|
+
import { homedir } from "os";
|
|
26
|
+
var WITHMATA_DIR = join(homedir(), ".withmata");
|
|
27
|
+
var BLUEPRINTS_DIR = join(WITHMATA_DIR, "blueprints");
|
|
28
|
+
var CREDENTIALS_PATH = join(WITHMATA_DIR, "credentials.json");
|
|
29
|
+
var CONFIG_PATH = join(WITHMATA_DIR, "config.json");
|
|
30
|
+
var CLAUDE_COMMANDS_DIR = ".claude/commands";
|
|
31
|
+
var OPENCODE_COMMANDS_DIR = ".opencode/commands";
|
|
32
|
+
var CURSOR_COMMANDS_DIR = ".cursor/commands";
|
|
33
|
+
var BLUEPRINTS_PATH_ENV = "WITHMATA_BLUEPRINTS_PATH";
|
|
34
|
+
|
|
35
|
+
// src/lib/credentials.ts
|
|
36
|
+
function readCredentials() {
|
|
37
|
+
if (!existsSync(CREDENTIALS_PATH)) return null;
|
|
38
|
+
try {
|
|
39
|
+
const raw = readFileSync(CREDENTIALS_PATH, "utf-8");
|
|
40
|
+
const data = JSON.parse(raw);
|
|
41
|
+
if (data.expiresAt && new Date(data.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
42
|
+
unlinkSync(CREDENTIALS_PATH);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/steps/auth.ts
|
|
52
|
+
async function checkAuth() {
|
|
53
|
+
const creds = readCredentials();
|
|
54
|
+
if (!creds) return null;
|
|
55
|
+
return { ...creds.user, token: creds.token };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/steps/tool-detect.ts
|
|
59
|
+
import { existsSync as existsSync2 } from "fs";
|
|
60
|
+
import { execSync } from "child_process";
|
|
61
|
+
import { join as join2 } from "path";
|
|
62
|
+
import { multiselect, isCancel, cancel } from "@clack/prompts";
|
|
63
|
+
async function detectAndSelectTools(targetDir) {
|
|
64
|
+
const claudeDetected = hasClaudeCode(targetDir);
|
|
65
|
+
const opencodeDetected = hasOpenCode(targetDir);
|
|
66
|
+
const cursorDetected = hasCursor(targetDir);
|
|
67
|
+
const choices = [
|
|
68
|
+
{
|
|
69
|
+
value: "claude-code",
|
|
70
|
+
label: "Claude Code",
|
|
71
|
+
hint: claudeDetected ? "detected" : void 0
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
value: "opencode",
|
|
75
|
+
label: "OpenCode",
|
|
76
|
+
hint: opencodeDetected ? "detected" : void 0
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
value: "cursor",
|
|
80
|
+
label: "Cursor",
|
|
81
|
+
hint: cursorDetected ? "detected" : void 0
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
const initialValues = [];
|
|
85
|
+
if (claudeDetected) initialValues.push("claude-code");
|
|
86
|
+
if (opencodeDetected) initialValues.push("opencode");
|
|
87
|
+
if (cursorDetected) initialValues.push("cursor");
|
|
88
|
+
const tools = await multiselect({
|
|
89
|
+
message: "Which AI coding tools do you use?",
|
|
90
|
+
options: choices,
|
|
91
|
+
initialValues,
|
|
92
|
+
required: true
|
|
93
|
+
});
|
|
94
|
+
if (isCancel(tools)) {
|
|
95
|
+
cancel("Setup cancelled.");
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
return tools;
|
|
99
|
+
}
|
|
100
|
+
function hasClaudeCode(targetDir) {
|
|
101
|
+
try {
|
|
102
|
+
execSync("which claude", { stdio: "ignore" });
|
|
103
|
+
return true;
|
|
104
|
+
} catch {
|
|
105
|
+
return existsSync2(join2(targetDir, ".claude")) || existsSync2(join2(targetDir, "CLAUDE.md"));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function hasOpenCode(targetDir) {
|
|
109
|
+
try {
|
|
110
|
+
execSync("which opencode", { stdio: "ignore" });
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return existsSync2(join2(targetDir, ".opencode")) || existsSync2(join2(targetDir, "opencode.json"));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function hasCursor(targetDir) {
|
|
117
|
+
try {
|
|
118
|
+
execSync("which cursor", { stdio: "ignore" });
|
|
119
|
+
return true;
|
|
120
|
+
} catch {
|
|
121
|
+
return existsSync2(join2(targetDir, ".cursor"));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/steps/download.ts
|
|
126
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync, rmSync, readdirSync } from "fs";
|
|
127
|
+
import { join as join3, resolve, dirname } from "path";
|
|
128
|
+
import { fileURLToPath } from "url";
|
|
129
|
+
import { spinner, log as log2 } from "@clack/prompts";
|
|
130
|
+
async function downloadBlueprints(_token) {
|
|
131
|
+
const s = spinner();
|
|
132
|
+
s.start("Resolving blueprints...");
|
|
133
|
+
const sourcePath = resolveBlueprintsSource();
|
|
134
|
+
if (!sourcePath) {
|
|
135
|
+
s.stop("Could not find blueprints source");
|
|
136
|
+
log2.error(
|
|
137
|
+
`Unable to locate the blueprints repository.
|
|
138
|
+
Set ${BLUEPRINTS_PATH_ENV} to the path of your my-blueprints repo,
|
|
139
|
+
or run this command from within the repo.`
|
|
140
|
+
);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
s.message("Copying blueprints...");
|
|
144
|
+
rmSync(BLUEPRINTS_DIR, { recursive: true, force: true });
|
|
145
|
+
mkdirSync2(BLUEPRINTS_DIR, { recursive: true });
|
|
146
|
+
const blueprintsSource = join3(sourcePath, "blueprints");
|
|
147
|
+
if (existsSync3(blueprintsSource)) {
|
|
148
|
+
cpSync(blueprintsSource, join3(BLUEPRINTS_DIR, "blueprints"), {
|
|
149
|
+
recursive: true
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
const claudeCommandsSource = join3(sourcePath, ".claude", "commands");
|
|
153
|
+
if (existsSync3(claudeCommandsSource)) {
|
|
154
|
+
const target = join3(BLUEPRINTS_DIR, ".claude", "commands");
|
|
155
|
+
mkdirSync2(target, { recursive: true });
|
|
156
|
+
cpSync(claudeCommandsSource, target, { recursive: true });
|
|
157
|
+
}
|
|
158
|
+
const opencodeCommandsSource = join3(sourcePath, ".opencode", "commands");
|
|
159
|
+
if (existsSync3(opencodeCommandsSource)) {
|
|
160
|
+
const target = join3(BLUEPRINTS_DIR, ".opencode", "commands");
|
|
161
|
+
mkdirSync2(target, { recursive: true });
|
|
162
|
+
cpSync(opencodeCommandsSource, target, { recursive: true });
|
|
163
|
+
}
|
|
164
|
+
const cursorCommandsSource = join3(sourcePath, ".cursor", "commands");
|
|
165
|
+
if (existsSync3(cursorCommandsSource)) {
|
|
166
|
+
const target = join3(BLUEPRINTS_DIR, ".cursor", "commands");
|
|
167
|
+
mkdirSync2(target, { recursive: true });
|
|
168
|
+
cpSync(cursorCommandsSource, target, { recursive: true });
|
|
169
|
+
}
|
|
170
|
+
const engineeringSource = join3(sourcePath, "ENGINEERING.md");
|
|
171
|
+
if (existsSync3(engineeringSource)) {
|
|
172
|
+
cpSync(engineeringSource, join3(BLUEPRINTS_DIR, "ENGINEERING.md"));
|
|
173
|
+
}
|
|
174
|
+
const blueprintIds = discoverBlueprints(join3(BLUEPRINTS_DIR, "blueprints"));
|
|
175
|
+
s.stop(`${blueprintIds.length} blueprints installed`);
|
|
176
|
+
return {
|
|
177
|
+
blueprintIds,
|
|
178
|
+
total: blueprintIds.length,
|
|
179
|
+
sourcePath
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function resolveBlueprintsSource() {
|
|
183
|
+
const envPath = process.env[BLUEPRINTS_PATH_ENV];
|
|
184
|
+
if (envPath && isBlueprintsRepo(envPath)) {
|
|
185
|
+
return resolve(envPath);
|
|
186
|
+
}
|
|
187
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
188
|
+
const cliRoot = resolve(dirname(__filename), "..");
|
|
189
|
+
if (isBlueprintsRepo(cliRoot)) {
|
|
190
|
+
return cliRoot;
|
|
191
|
+
}
|
|
192
|
+
const repoRoot = resolve(cliRoot, "..");
|
|
193
|
+
if (isBlueprintsRepo(repoRoot)) {
|
|
194
|
+
return repoRoot;
|
|
195
|
+
}
|
|
196
|
+
const cwd = process.cwd();
|
|
197
|
+
if (isBlueprintsRepo(cwd)) {
|
|
198
|
+
return cwd;
|
|
199
|
+
}
|
|
200
|
+
if (existsSync3(join3(BLUEPRINTS_DIR, "blueprints"))) {
|
|
201
|
+
return BLUEPRINTS_DIR;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
function isBlueprintsRepo(dir) {
|
|
206
|
+
return existsSync3(join3(dir, "blueprints")) && existsSync3(join3(dir, "ENGINEERING.md"));
|
|
207
|
+
}
|
|
208
|
+
function discoverBlueprints(blueprintsDir) {
|
|
209
|
+
const ids = [];
|
|
210
|
+
if (!existsSync3(blueprintsDir)) return ids;
|
|
211
|
+
const tiers = ["discovery", "foundation", "features"];
|
|
212
|
+
for (const tier of tiers) {
|
|
213
|
+
const tierDir = join3(blueprintsDir, tier);
|
|
214
|
+
if (!existsSync3(tierDir)) continue;
|
|
215
|
+
for (const entry of readdirSync(tierDir, { withFileTypes: true })) {
|
|
216
|
+
if (entry.isDirectory()) {
|
|
217
|
+
ids.push(entry.name);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return ids;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/steps/link.ts
|
|
225
|
+
import {
|
|
226
|
+
existsSync as existsSync4,
|
|
227
|
+
mkdirSync as mkdirSync3,
|
|
228
|
+
symlinkSync,
|
|
229
|
+
readFileSync as readFileSync2,
|
|
230
|
+
appendFileSync,
|
|
231
|
+
writeFileSync as writeFileSync2,
|
|
232
|
+
lstatSync,
|
|
233
|
+
unlinkSync as unlinkSync2,
|
|
234
|
+
renameSync,
|
|
235
|
+
readdirSync as readdirSync2,
|
|
236
|
+
cpSync as cpSync2
|
|
237
|
+
} from "fs";
|
|
238
|
+
import { join as join4, resolve as resolve2 } from "path";
|
|
239
|
+
import { spinner as spinner2, log as log3 } from "@clack/prompts";
|
|
240
|
+
async function linkToProject(tools) {
|
|
241
|
+
let commandsLinked = 0;
|
|
242
|
+
const rulesFilesUpdated = [];
|
|
243
|
+
const targetDir = process.cwd();
|
|
244
|
+
for (const tool of tools) {
|
|
245
|
+
if (tool === "claude-code") {
|
|
246
|
+
const result = linkCommands(
|
|
247
|
+
join4(BLUEPRINTS_DIR, CLAUDE_COMMANDS_DIR),
|
|
248
|
+
join4(targetDir, CLAUDE_COMMANDS_DIR),
|
|
249
|
+
"Claude Code"
|
|
250
|
+
);
|
|
251
|
+
commandsLinked += result;
|
|
252
|
+
if (updateRulesFile(targetDir, "CLAUDE.md")) {
|
|
253
|
+
rulesFilesUpdated.push("CLAUDE.md");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (tool === "opencode") {
|
|
257
|
+
const result = linkCommands(
|
|
258
|
+
join4(BLUEPRINTS_DIR, OPENCODE_COMMANDS_DIR),
|
|
259
|
+
join4(targetDir, OPENCODE_COMMANDS_DIR),
|
|
260
|
+
"OpenCode"
|
|
261
|
+
);
|
|
262
|
+
commandsLinked += result;
|
|
263
|
+
if (updateRulesFile(targetDir, "AGENTS.md")) {
|
|
264
|
+
if (!rulesFilesUpdated.includes("AGENTS.md")) {
|
|
265
|
+
rulesFilesUpdated.push("AGENTS.md");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (tool === "cursor") {
|
|
270
|
+
const result = linkCommands(
|
|
271
|
+
join4(BLUEPRINTS_DIR, CURSOR_COMMANDS_DIR),
|
|
272
|
+
join4(targetDir, CURSOR_COMMANDS_DIR),
|
|
273
|
+
"Cursor"
|
|
274
|
+
);
|
|
275
|
+
commandsLinked += result;
|
|
276
|
+
if (updateRulesFile(targetDir, "AGENTS.md")) {
|
|
277
|
+
if (!rulesFilesUpdated.includes("AGENTS.md")) {
|
|
278
|
+
rulesFilesUpdated.push("AGENTS.md");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
const engineeringCopied = copyEngineering(targetDir);
|
|
284
|
+
return { commandsLinked, rulesFilesUpdated, engineeringCopied };
|
|
285
|
+
}
|
|
286
|
+
function linkCommands(sourceDir, targetDir, toolLabel) {
|
|
287
|
+
const s = spinner2();
|
|
288
|
+
s.start(`Linking commands for ${toolLabel}...`);
|
|
289
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
290
|
+
let count = 0;
|
|
291
|
+
if (!existsSync4(sourceDir)) {
|
|
292
|
+
s.stop(`No commands found for ${toolLabel}`);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
for (const file of readdirSync2(sourceDir)) {
|
|
296
|
+
if (!file.endsWith(".md")) continue;
|
|
297
|
+
const source = resolve2(sourceDir, file);
|
|
298
|
+
const target = join4(targetDir, file);
|
|
299
|
+
if (existsSync4(target)) {
|
|
300
|
+
if (lstatSync(target).isSymbolicLink()) {
|
|
301
|
+
unlinkSync2(target);
|
|
302
|
+
} else {
|
|
303
|
+
renameSync(target, `${target}.bak`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
symlinkSync(source, target);
|
|
307
|
+
count++;
|
|
308
|
+
}
|
|
309
|
+
s.stop(`${count} commands linked for ${toolLabel}`);
|
|
310
|
+
return count;
|
|
311
|
+
}
|
|
312
|
+
var MARKER_START = "# --- withmata-blueprints ---";
|
|
313
|
+
var MARKER_END = "# --- /withmata-blueprints ---";
|
|
314
|
+
function buildRulesBlock() {
|
|
315
|
+
return `
|
|
316
|
+
${MARKER_START}
|
|
317
|
+
## Blueprint System (withmata.dev)
|
|
318
|
+
|
|
319
|
+
You have access to production-ready blueprints for scaffolding project infrastructure. Blueprints encode battle-tested patterns and configurations that save hours of setup. They work in both monorepo and single-repo projects.
|
|
320
|
+
|
|
321
|
+
### Commands
|
|
322
|
+
|
|
323
|
+
\`/new-project\`, \`/discover\`, \`/scaffold-foundation\`, \`/scaffold-auth\`, \`/scaffold-db\`, \`/audit\`
|
|
324
|
+
|
|
325
|
+
### Available Blueprints
|
|
326
|
+
|
|
327
|
+
| Blueprint | Tier | What it does | Dependencies | Command |
|
|
328
|
+
|-----------|------|-------------|--------------|---------|
|
|
329
|
+
| Product Discovery | Discovery | Structured session producing product thesis, user archetypes, and competitive analysis. No code. | None | \`/discover\` |
|
|
330
|
+
| Monorepo Foundation | Foundation | Turborepo + pnpm monorepo with Next.js, Biome, TypeScript strict, Tailwind v4. | None | \`/scaffold-foundation\` |
|
|
331
|
+
| Database | Feature | Drizzle ORM + PostgreSQL: schema groups, migrations, seed scripts. | None | \`/scaffold-db\` |
|
|
332
|
+
| Authentication | Feature | Better Auth + Drizzle + PostgreSQL: social login, email/password, organizations, role-based permissions. | Database | \`/scaffold-auth\` |
|
|
333
|
+
|
|
334
|
+
### When to Recommend
|
|
335
|
+
|
|
336
|
+
Suggest a blueprint when you observe these signals. Be helpful, not pushy \u2014 mention it once, then follow the user's lead.
|
|
337
|
+
|
|
338
|
+
**\`/discover\`** \u2014 User describes a new product idea, asks "what should I build", is unsure about scope or target users, is non-technical or building their first product, or project has no product documentation.
|
|
339
|
+
|
|
340
|
+
**\`/scaffold-foundation\`** \u2014 User wants to set up a monorepo, mentions Turborepo or project structure, or project has no \`turbo.json\` / \`pnpm-workspace.yaml\`. Best after discovery. Skip for single-repo apps.
|
|
341
|
+
|
|
342
|
+
**\`/scaffold-db\`** \u2014 User mentions database, Drizzle, PostgreSQL, schemas, migrations, or needs a structured db layer. Or is writing raw SQL or using an ORM without organized schemas.
|
|
343
|
+
|
|
344
|
+
**\`/scaffold-auth\`** \u2014 User mentions login, signup, users, accounts, teams, organizations, permissions, or authentication. Or is implementing auth from scratch. Recommend \`/scaffold-db\` first if not already installed.
|
|
345
|
+
|
|
346
|
+
**\`/audit\`** \u2014 User asks "what should I do next?", wants to understand their project's state, or has hand-rolled infrastructure that a blueprint could improve.
|
|
347
|
+
|
|
348
|
+
### Lifecycle & Dependencies
|
|
349
|
+
|
|
350
|
+
Discovery \u2192 Foundation \u2192 Features is the recommended flow, but blueprints can be applied at any stage.
|
|
351
|
+
|
|
352
|
+
- **New project, no code:** Start with \`/discover\` (especially for non-technical users) or \`/scaffold-foundation\` (for monorepos)
|
|
353
|
+
- **Single-repo project:** Skip foundation \u2014 go directly to feature blueprints (\`/scaffold-db\`, \`/scaffold-auth\`)
|
|
354
|
+
- **Has monorepo, missing features:** Suggest specific feature blueprints
|
|
355
|
+
- **Existing project:** Feature blueprints adapt to existing structures \u2014 no foundation required
|
|
356
|
+
- **Dependencies:** \`/scaffold-auth\` works best after \`/scaffold-db\` (auth builds on db patterns). Install dependencies first.
|
|
357
|
+
|
|
358
|
+
### Important
|
|
359
|
+
|
|
360
|
+
- Read \`.project-context.md\` first \u2014 it tracks what is already set up. Do not recommend blueprints already recorded there.
|
|
361
|
+
- Read the full BLUEPRINT.md before scaffolding. Follow ENGINEERING.md for coding conventions.
|
|
362
|
+
- Only recommend blueprints listed above. Other blueprints are in development and not yet available.
|
|
363
|
+
- Blueprints location: \`${BLUEPRINTS_DIR}\`
|
|
364
|
+
${MARKER_END}
|
|
365
|
+
`;
|
|
366
|
+
}
|
|
367
|
+
function updateRulesFile(targetDir, filename) {
|
|
368
|
+
const filepath = join4(targetDir, filename);
|
|
369
|
+
const block = buildRulesBlock();
|
|
370
|
+
if (existsSync4(filepath)) {
|
|
371
|
+
const content = readFileSync2(filepath, "utf-8");
|
|
372
|
+
if (content.includes(MARKER_START)) {
|
|
373
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
374
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
375
|
+
if (endIdx === -1) {
|
|
376
|
+
appendFileSync(filepath, block);
|
|
377
|
+
} else {
|
|
378
|
+
const before = content.slice(0, startIdx);
|
|
379
|
+
const after = content.slice(endIdx + MARKER_END.length);
|
|
380
|
+
writeFileSync2(filepath, before + block.trimStart() + after);
|
|
381
|
+
}
|
|
382
|
+
log3.success(`Updated ${filename} (refreshed blueprint catalog)`);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
appendFileSync(filepath, block);
|
|
386
|
+
} else {
|
|
387
|
+
writeFileSync2(filepath, block.trimStart());
|
|
388
|
+
}
|
|
389
|
+
log3.success(`Updated ${filename}`);
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
function copyEngineering(targetDir) {
|
|
393
|
+
const source = join4(BLUEPRINTS_DIR, "ENGINEERING.md");
|
|
394
|
+
const target = join4(targetDir, "ENGINEERING.md");
|
|
395
|
+
if (!existsSync4(source)) return false;
|
|
396
|
+
if (existsSync4(target)) {
|
|
397
|
+
const sourceContent = readFileSync2(source, "utf-8");
|
|
398
|
+
const targetContent = readFileSync2(target, "utf-8");
|
|
399
|
+
if (sourceContent === targetContent) return false;
|
|
400
|
+
}
|
|
401
|
+
cpSync2(source, target);
|
|
402
|
+
log3.success("Copied ENGINEERING.md");
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/steps/summary.ts
|
|
407
|
+
import { log as log4, note } from "@clack/prompts";
|
|
408
|
+
import pc from "picocolors";
|
|
409
|
+
function showSummary({ linked, tools, blueprintCount }) {
|
|
410
|
+
const commands = [
|
|
411
|
+
`${pc.cyan("/new-project")} Start a new project`,
|
|
412
|
+
`${pc.cyan("/scaffold-auth")} Add auth to this project`,
|
|
413
|
+
`${pc.cyan("/scaffold-db")} Set up database package`,
|
|
414
|
+
`${pc.cyan("/scaffold-foundation")} Set up monorepo skeleton`,
|
|
415
|
+
`${pc.cyan("/discover")} Run product discovery`,
|
|
416
|
+
`${pc.cyan("/audit")} Run a project health check`
|
|
417
|
+
].join("\n");
|
|
418
|
+
note(commands, "Available commands");
|
|
419
|
+
const toolNameMap = {
|
|
420
|
+
"claude-code": "Claude Code",
|
|
421
|
+
opencode: "OpenCode",
|
|
422
|
+
cursor: "Cursor"
|
|
423
|
+
};
|
|
424
|
+
const toolNames = tools.map((t) => toolNameMap[t]).join(" or ");
|
|
425
|
+
log4.info(`Open ${pc.bold(toolNames)} in this directory to use them.`);
|
|
426
|
+
log4.info(pc.dim(`Manage your account at https://withmata.dev/dashboard`));
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/index.ts
|
|
430
|
+
async function main() {
|
|
431
|
+
intro(`${pc2.bgCyan(pc2.black(" @withmata/blueprints "))} ${pc2.dim(`v${VERSION}`)}`);
|
|
432
|
+
const user = await checkAuth();
|
|
433
|
+
if (user) {
|
|
434
|
+
log5.success(`Logged in as ${user.email} (${user.tier} plan)`);
|
|
435
|
+
} else {
|
|
436
|
+
log5.info("Running in local mode");
|
|
437
|
+
}
|
|
438
|
+
const tools = await detectAndSelectTools(process.cwd());
|
|
439
|
+
const blueprints = await downloadBlueprints(user?.token);
|
|
440
|
+
const linked = await linkToProject(tools);
|
|
441
|
+
showSummary({
|
|
442
|
+
linked,
|
|
443
|
+
tools,
|
|
444
|
+
blueprintCount: blueprints.total
|
|
445
|
+
});
|
|
446
|
+
outro("Setup complete!");
|
|
447
|
+
}
|
|
448
|
+
main().catch((err) => {
|
|
449
|
+
log5.error(
|
|
450
|
+
err instanceof Error ? err.message : "An unexpected error occurred"
|
|
451
|
+
);
|
|
452
|
+
process.exit(1);
|
|
453
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@withmata/blueprints",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Set up AI-powered project blueprints for Claude Code, OpenCode, and Cursor",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"withmata-blueprints": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"blueprints",
|
|
12
|
+
".claude/commands",
|
|
13
|
+
".opencode/commands",
|
|
14
|
+
".cursor/commands",
|
|
15
|
+
"ENGINEERING.md"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsup",
|
|
19
|
+
"dev": "tsup --watch",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"copy-blueprints": "node scripts/copy-blueprints.mjs",
|
|
22
|
+
"prepublishOnly": "node scripts/copy-blueprints.mjs && tsup"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@clack/prompts": "^0.11.0",
|
|
26
|
+
"picocolors": "^1.1.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.0.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"typescript": "^5.9.0"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/With-Mata/my-blueprints.git",
|
|
42
|
+
"directory": "cli"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"blueprints",
|
|
46
|
+
"claude-code",
|
|
47
|
+
"opencode",
|
|
48
|
+
"scaffolding",
|
|
49
|
+
"ai-coding"
|
|
50
|
+
],
|
|
51
|
+
"author": "WithMata",
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|