@withmata/blueprints 0.3.3 → 0.3.5

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/dist/index.js CHANGED
@@ -2,23 +2,16 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { intro as intro2, outro as outro2, log as log6 } from "@clack/prompts";
5
- import pc3 from "picocolors";
5
+ import pc4 from "picocolors";
6
6
 
7
7
  // src/constants.ts
8
8
  var API_URL = process.env["WITHMATA_API_URL"] || "https://blueprints.withmata.dev";
9
- var VERSION = "0.3.3";
10
-
11
- // src/steps/auth.ts
12
- import { log } from "@clack/prompts";
9
+ var VERSION = "0.3.5";
13
10
 
14
- // src/lib/credentials.ts
15
- import {
16
- existsSync,
17
- mkdirSync,
18
- readFileSync,
19
- writeFileSync,
20
- unlinkSync
21
- } from "fs";
11
+ // src/lib/install-state.ts
12
+ import { existsSync, readdirSync, lstatSync, readlinkSync } from "fs";
13
+ import { execSync } from "child_process";
14
+ import { join as join2 } from "path";
22
15
 
23
16
  // src/lib/paths.ts
24
17
  import { join } from "path";
@@ -41,9 +34,92 @@ var OPENCODE_COMMANDS_DIR = ".opencode/commands";
41
34
  var CURSOR_COMMANDS_DIR = ".cursor/commands";
42
35
  var BLUEPRINTS_PATH_ENV = "WITHMATA_BLUEPRINTS_PATH";
43
36
 
37
+ // src/lib/install-state.ts
38
+ function detectInstallState(targetDir) {
39
+ const previousSkillCount = countSkillDirs(SKILLS_SOURCE_DIR);
40
+ const toolStates = {
41
+ "claude-code": {
42
+ detected: hasClaudeCode(targetDir),
43
+ ...countInstalledSkills(CLAUDE_SKILLS_DIR)
44
+ },
45
+ opencode: {
46
+ detected: hasOpenCode(targetDir),
47
+ ...countInstalledSkills(OPENCODE_SKILLS_DIR)
48
+ },
49
+ cursor: {
50
+ detected: hasCursor(targetDir),
51
+ ...countInstalledSkills(CURSOR_SKILLS_DIR)
52
+ }
53
+ };
54
+ const anyToolHasSkills = Object.values(toolStates).some(
55
+ (s) => s.skillsInstalled
56
+ );
57
+ const isUpdate = anyToolHasSkills || existsSync(BLUEPRINTS_DIR) && previousSkillCount > 0;
58
+ return { isUpdate, previousSkillCount, toolStates };
59
+ }
60
+ function countSkillDirs(dir) {
61
+ if (!existsSync(dir)) return 0;
62
+ return readdirSync(dir, { withFileTypes: true }).filter(
63
+ (e) => e.isDirectory()
64
+ ).length;
65
+ }
66
+ function countInstalledSkills(skillsDir) {
67
+ if (!existsSync(skillsDir)) {
68
+ return { skillsInstalled: false, installedCount: 0 };
69
+ }
70
+ let count = 0;
71
+ for (const entry of readdirSync(skillsDir)) {
72
+ const entryPath = join2(skillsDir, entry);
73
+ try {
74
+ if (lstatSync(entryPath).isSymbolicLink()) {
75
+ const target = readlinkSync(entryPath);
76
+ if (target.includes(BLUEPRINTS_DIR)) {
77
+ count++;
78
+ }
79
+ }
80
+ } catch {
81
+ }
82
+ }
83
+ return { skillsInstalled: count > 0, installedCount: count };
84
+ }
85
+ function hasClaudeCode(targetDir) {
86
+ try {
87
+ execSync("which claude", { stdio: "ignore" });
88
+ return true;
89
+ } catch {
90
+ return existsSync(join2(targetDir, ".claude")) || existsSync(join2(targetDir, "CLAUDE.md"));
91
+ }
92
+ }
93
+ function hasOpenCode(targetDir) {
94
+ try {
95
+ execSync("which opencode", { stdio: "ignore" });
96
+ return true;
97
+ } catch {
98
+ return existsSync(join2(targetDir, ".opencode")) || existsSync(join2(targetDir, "opencode.json"));
99
+ }
100
+ }
101
+ function hasCursor(targetDir) {
102
+ try {
103
+ execSync("which cursor", { stdio: "ignore" });
104
+ return true;
105
+ } catch {
106
+ return existsSync(join2(targetDir, ".cursor"));
107
+ }
108
+ }
109
+
110
+ // src/steps/auth.ts
111
+ import { log } from "@clack/prompts";
112
+
44
113
  // src/lib/credentials.ts
114
+ import {
115
+ existsSync as existsSync2,
116
+ mkdirSync,
117
+ readFileSync,
118
+ writeFileSync,
119
+ unlinkSync
120
+ } from "fs";
45
121
  function readCredentials() {
46
- if (!existsSync(CREDENTIALS_PATH)) return null;
122
+ if (!existsSync2(CREDENTIALS_PATH)) return null;
47
123
  try {
48
124
  const raw = readFileSync(CREDENTIALS_PATH, "utf-8");
49
125
  const data = JSON.parse(raw);
@@ -65,37 +141,35 @@ async function checkAuth() {
65
141
  }
66
142
 
67
143
  // src/steps/tool-detect.ts
68
- import { existsSync as existsSync2 } from "fs";
69
- import { execSync } from "child_process";
70
- import { join as join2 } from "path";
71
144
  import { multiselect, isCancel, cancel } from "@clack/prompts";
72
- async function detectAndSelectTools(targetDir) {
73
- const claudeDetected = hasClaudeCode(targetDir);
74
- const opencodeDetected = hasOpenCode(targetDir);
75
- const cursorDetected = hasCursor(targetDir);
145
+ import pc from "picocolors";
146
+ async function detectAndSelectTools(installState) {
147
+ const { isUpdate, toolStates } = installState;
76
148
  const choices = [
77
149
  {
78
150
  value: "claude-code",
79
151
  label: "Claude Code",
80
- hint: claudeDetected ? "detected" : void 0
152
+ hint: buildHint(toolStates["claude-code"])
81
153
  },
82
154
  {
83
155
  value: "opencode",
84
156
  label: "OpenCode",
85
- hint: opencodeDetected ? "detected" : void 0
157
+ hint: buildHint(toolStates.opencode)
86
158
  },
87
159
  {
88
160
  value: "cursor",
89
161
  label: "Cursor",
90
- hint: cursorDetected ? "detected" : void 0
162
+ hint: buildHint(toolStates.cursor)
91
163
  }
92
164
  ];
93
165
  const initialValues = [];
94
- if (claudeDetected) initialValues.push("claude-code");
95
- if (opencodeDetected) initialValues.push("opencode");
96
- if (cursorDetected) initialValues.push("cursor");
166
+ for (const [tool, state] of Object.entries(toolStates)) {
167
+ if (state.detected || state.skillsInstalled) {
168
+ initialValues.push(tool);
169
+ }
170
+ }
97
171
  const tools = await multiselect({
98
- message: "Which AI coding tools do you use?",
172
+ message: isUpdate ? "Update skills for these tools?" : "Which AI coding tools do you use?",
99
173
  options: choices,
100
174
  initialValues,
101
175
  required: true
@@ -106,39 +180,27 @@ async function detectAndSelectTools(targetDir) {
106
180
  }
107
181
  return tools;
108
182
  }
109
- function hasClaudeCode(targetDir) {
110
- try {
111
- execSync("which claude", { stdio: "ignore" });
112
- return true;
113
- } catch {
114
- return existsSync2(join2(targetDir, ".claude")) || existsSync2(join2(targetDir, "CLAUDE.md"));
183
+ function buildHint(state) {
184
+ if (state.detected && state.skillsInstalled) {
185
+ return pc.green("detected \xB7 installed");
115
186
  }
116
- }
117
- function hasOpenCode(targetDir) {
118
- try {
119
- execSync("which opencode", { stdio: "ignore" });
120
- return true;
121
- } catch {
122
- return existsSync2(join2(targetDir, ".opencode")) || existsSync2(join2(targetDir, "opencode.json"));
187
+ if (state.detected) {
188
+ return "detected";
123
189
  }
124
- }
125
- function hasCursor(targetDir) {
126
- try {
127
- execSync("which cursor", { stdio: "ignore" });
128
- return true;
129
- } catch {
130
- return existsSync2(join2(targetDir, ".cursor"));
190
+ if (state.skillsInstalled) {
191
+ return "installed";
131
192
  }
193
+ return void 0;
132
194
  }
133
195
 
134
196
  // src/steps/download.ts
135
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync, rmSync, readdirSync } from "fs";
197
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, cpSync, rmSync, readdirSync as readdirSync2 } from "fs";
136
198
  import { join as join3, resolve, dirname } from "path";
137
199
  import { fileURLToPath } from "url";
138
200
  import { spinner, log as log2 } from "@clack/prompts";
139
- async function downloadBlueprints(_token) {
201
+ async function downloadBlueprints(_token, isUpdate = false) {
140
202
  const s = spinner();
141
- s.start("Resolving blueprints...");
203
+ s.start(isUpdate ? "Updating blueprints..." : "Resolving blueprints...");
142
204
  const sourcePath = resolveBlueprintsSource();
143
205
  if (!sourcePath) {
144
206
  s.stop("Could not find blueprints source");
@@ -149,7 +211,7 @@ async function downloadBlueprints(_token) {
149
211
  );
150
212
  process.exit(1);
151
213
  }
152
- s.message("Copying blueprints...");
214
+ s.message(isUpdate ? "Updating blueprints..." : "Copying blueprints...");
153
215
  rmSync(BLUEPRINTS_DIR, { recursive: true, force: true });
154
216
  mkdirSync2(BLUEPRINTS_DIR, { recursive: true });
155
217
  const blueprintsSource = join3(sourcePath, "blueprints");
@@ -169,7 +231,9 @@ async function downloadBlueprints(_token) {
169
231
  cpSync(engineeringSource, join3(BLUEPRINTS_DIR, "ENGINEERING.md"));
170
232
  }
171
233
  const blueprintIds = discoverBlueprints(join3(BLUEPRINTS_DIR, "blueprints"));
172
- s.stop(`${blueprintIds.length} blueprints installed`);
234
+ s.stop(
235
+ `${blueprintIds.length} blueprints ${isUpdate ? "updated" : "installed"}`
236
+ );
173
237
  return {
174
238
  blueprintIds,
175
239
  total: blueprintIds.length,
@@ -209,7 +273,7 @@ function discoverBlueprints(blueprintsDir) {
209
273
  for (const tier of tiers) {
210
274
  const tierDir = join3(blueprintsDir, tier);
211
275
  if (!existsSync3(tierDir)) continue;
212
- for (const entry of readdirSync(tierDir, { withFileTypes: true })) {
276
+ for (const entry of readdirSync2(tierDir, { withFileTypes: true })) {
213
277
  if (entry.isDirectory()) {
214
278
  ids.push(entry.name);
215
279
  }
@@ -223,57 +287,62 @@ import {
223
287
  existsSync as existsSync4,
224
288
  mkdirSync as mkdirSync3,
225
289
  symlinkSync,
226
- lstatSync,
290
+ lstatSync as lstatSync2,
227
291
  unlinkSync as unlinkSync2,
228
292
  renameSync,
229
- readdirSync as readdirSync2,
230
- readlinkSync
293
+ readdirSync as readdirSync3,
294
+ readlinkSync as readlinkSync2
231
295
  } from "fs";
232
296
  import { join as join4, resolve as resolve2 } from "path";
233
297
  import { spinner as spinner2, log as log3 } from "@clack/prompts";
234
- async function installSkillsGlobally(tools) {
298
+ async function installSkillsGlobally(tools, isUpdate = false) {
235
299
  let skillsInstalled = 0;
236
300
  for (const tool of tools) {
237
301
  if (tool === "claude-code") {
238
302
  skillsInstalled += installSkills(
239
303
  SKILLS_SOURCE_DIR,
240
304
  CLAUDE_SKILLS_DIR,
241
- "Claude Code"
305
+ "Claude Code",
306
+ isUpdate
242
307
  );
243
308
  }
244
309
  if (tool === "opencode") {
245
310
  skillsInstalled += installSkills(
246
311
  SKILLS_SOURCE_DIR,
247
312
  OPENCODE_SKILLS_DIR,
248
- "OpenCode"
313
+ "OpenCode",
314
+ isUpdate
249
315
  );
250
316
  }
251
317
  if (tool === "cursor") {
252
318
  skillsInstalled += installSkills(
253
319
  SKILLS_SOURCE_DIR,
254
320
  CURSOR_SKILLS_DIR,
255
- "Cursor"
321
+ "Cursor",
322
+ isUpdate
256
323
  );
257
324
  }
258
325
  }
259
326
  const legacyCommandsCleaned = cleanupLegacyCommands(process.cwd());
260
327
  return { skillsInstalled, legacyCommandsCleaned };
261
328
  }
262
- function installSkills(sourceDir, globalTargetDir, toolLabel) {
329
+ function installSkills(sourceDir, globalTargetDir, toolLabel, isUpdate) {
263
330
  const s = spinner2();
264
- s.start(`Installing skills for ${toolLabel}...`);
331
+ s.start(
332
+ isUpdate ? `Updating skills for ${toolLabel}...` : `Installing skills for ${toolLabel}...`
333
+ );
265
334
  mkdirSync3(globalTargetDir, { recursive: true });
266
335
  if (!existsSync4(sourceDir)) {
267
336
  s.stop(`No skills found for ${toolLabel}`);
268
337
  return 0;
269
338
  }
270
339
  let count = 0;
271
- for (const entry of readdirSync2(sourceDir, { withFileTypes: true })) {
340
+ for (const entry of readdirSync3(sourceDir, { withFileTypes: true })) {
272
341
  if (!entry.isDirectory()) continue;
273
342
  const source = resolve2(sourceDir, entry.name);
274
343
  const target = join4(globalTargetDir, entry.name);
275
344
  if (existsSync4(target) || lstatExists(target)) {
276
- if (lstatSync(target).isSymbolicLink()) {
345
+ if (lstatSync2(target).isSymbolicLink()) {
277
346
  unlinkSync2(target);
278
347
  } else {
279
348
  const backup = `${target}.bak`;
@@ -286,7 +355,9 @@ function installSkills(sourceDir, globalTargetDir, toolLabel) {
286
355
  symlinkSync(source, target);
287
356
  count++;
288
357
  }
289
- s.stop(`${count} skills installed for ${toolLabel}`);
358
+ s.stop(
359
+ `${count} skills ${isUpdate ? "updated" : "installed"} for ${toolLabel}`
360
+ );
290
361
  return count;
291
362
  }
292
363
  function cleanupLegacyCommands(targetDir) {
@@ -299,11 +370,11 @@ function cleanupLegacyCommands(targetDir) {
299
370
  for (const dir of legacyDirs) {
300
371
  if (!existsSync4(dir)) continue;
301
372
  let dirEmpty = true;
302
- for (const file of readdirSync2(dir)) {
373
+ for (const file of readdirSync3(dir)) {
303
374
  const filePath = join4(dir, file);
304
- if (lstatSync(filePath).isSymbolicLink()) {
375
+ if (lstatSync2(filePath).isSymbolicLink()) {
305
376
  try {
306
- const linkTarget = readlinkSync(filePath);
377
+ const linkTarget = readlinkSync2(filePath);
307
378
  if (linkTarget.includes(BLUEPRINTS_DIR)) {
308
379
  unlinkSync2(filePath);
309
380
  cleaned++;
@@ -317,10 +388,10 @@ function cleanupLegacyCommands(targetDir) {
317
388
  }
318
389
  dirEmpty = false;
319
390
  }
320
- if (dirEmpty && readdirSync2(dir).length === 0) {
391
+ if (dirEmpty && readdirSync3(dir).length === 0) {
321
392
  try {
322
- readdirSync2(dir);
323
- if (readdirSync2(dir).length === 0) {
393
+ readdirSync3(dir);
394
+ if (readdirSync3(dir).length === 0) {
324
395
  unlinkSync2(dir);
325
396
  }
326
397
  } catch {
@@ -334,7 +405,7 @@ function cleanupLegacyCommands(targetDir) {
334
405
  }
335
406
  function lstatExists(path) {
336
407
  try {
337
- lstatSync(path);
408
+ lstatSync2(path);
338
409
  return true;
339
410
  } catch {
340
411
  return false;
@@ -343,30 +414,92 @@ function lstatExists(path) {
343
414
 
344
415
  // src/steps/summary.ts
345
416
  import { log as log4, note } from "@clack/prompts";
346
- import pc from "picocolors";
347
- function showSummary({ installed, tools, blueprintCount }) {
348
- const skills = [
349
- `${pc.cyan("/new-project")} Start a new project`,
350
- `${pc.cyan("/scaffold-auth")} Add auth to this project`,
351
- `${pc.cyan("/scaffold-db")} Set up database package`,
352
- `${pc.cyan("/scaffold-foundation")} Set up monorepo skeleton`,
353
- `${pc.cyan("/discover")} Run product discovery`,
354
- `${pc.cyan("/audit")} Run a project health check`
355
- ].join("\n");
356
- note(skills, "Available skills (global)");
417
+ import pc2 from "picocolors";
418
+ var SKILL_GROUPS = [
419
+ {
420
+ title: "Getting Started",
421
+ skills: [
422
+ {
423
+ command: "/new-project",
424
+ description: "Build a new product end-to-end \u2014 from idea to running code"
425
+ },
426
+ {
427
+ command: "/discover",
428
+ description: "Define your product vision before writing any code"
429
+ }
430
+ ]
431
+ },
432
+ {
433
+ title: "Add to Your Project",
434
+ skills: [
435
+ {
436
+ command: "/scaffold-foundation",
437
+ description: "Set up a monorepo with Turborepo and shared tooling"
438
+ },
439
+ {
440
+ command: "/scaffold-db",
441
+ description: "Add a database with Drizzle ORM and PostgreSQL"
442
+ },
443
+ {
444
+ command: "/scaffold-auth",
445
+ description: "Add authentication, sessions, and organization support"
446
+ },
447
+ {
448
+ command: "/scaffold-env",
449
+ description: "Add validated environment variables with type-safe access"
450
+ },
451
+ {
452
+ command: "/scaffold-tailwind",
453
+ description: "Add a design system with Tailwind v4 tokens and animations"
454
+ },
455
+ {
456
+ command: "/scaffold-ui",
457
+ description: "Add a component library powered by shadcn/ui"
458
+ }
459
+ ]
460
+ },
461
+ {
462
+ title: "Maintain",
463
+ skills: [
464
+ {
465
+ command: "/audit",
466
+ description: "Check project health and find available upgrades"
467
+ }
468
+ ]
469
+ }
470
+ ];
471
+ function showSummary({
472
+ installed,
473
+ tools,
474
+ isUpdate
475
+ }) {
476
+ const maxCommandLen = Math.max(
477
+ ...SKILL_GROUPS.flatMap((g) => g.skills.map((s) => s.command.length))
478
+ );
479
+ const sections = SKILL_GROUPS.map((group) => {
480
+ const header = pc2.bold(group.title);
481
+ const lines = group.skills.map((skill) => {
482
+ const padded = skill.command.padEnd(maxCommandLen + 2);
483
+ return ` ${pc2.cyan(padded)}${pc2.dim(skill.description)}`;
484
+ });
485
+ return `${header}
486
+ ${lines.join("\n")}`;
487
+ });
488
+ note(sections.join("\n\n"), "Available skills");
357
489
  const toolNameMap = {
358
490
  "claude-code": "Claude Code",
359
491
  opencode: "OpenCode",
360
492
  cursor: "Cursor"
361
493
  };
362
494
  const toolNames = tools.map((t) => toolNameMap[t]).join(", ");
495
+ const verb = isUpdate ? "updated" : "installed";
363
496
  log4.info(
364
- `${pc.bold(String(installed.skillsInstalled))} skills installed globally for ${toolNames}.`
497
+ `${pc2.bold(String(installed.skillsInstalled))} skills ${verb} for ${toolNames}.`
365
498
  );
366
499
  log4.info("These skills work in any project \u2014 no per-project setup needed.");
367
500
  if (installed.legacyCommandsCleaned > 0) {
368
501
  log4.info(
369
- pc.dim(
502
+ pc2.dim(
370
503
  `Cleaned up ${installed.legacyCommandsCleaned} legacy command symlink(s).`
371
504
  )
372
505
  );
@@ -376,30 +509,30 @@ function showSummary({ installed, tools, blueprintCount }) {
376
509
  // src/steps/uninstall.ts
377
510
  import {
378
511
  existsSync as existsSync5,
379
- readdirSync as readdirSync3,
380
- lstatSync as lstatSync2,
381
- readlinkSync as readlinkSync2,
512
+ readdirSync as readdirSync4,
513
+ lstatSync as lstatSync3,
514
+ readlinkSync as readlinkSync3,
382
515
  unlinkSync as unlinkSync3,
383
516
  rmSync as rmSync2
384
517
  } from "fs";
385
518
  import { join as join5 } from "path";
386
519
  import { intro, outro, confirm, log as log5, spinner as spinner3 } from "@clack/prompts";
387
- import pc2 from "picocolors";
520
+ import pc3 from "picocolors";
388
521
  var SKILL_DIRS = [CLAUDE_SKILLS_DIR, OPENCODE_SKILLS_DIR, CURSOR_SKILLS_DIR];
389
522
  async function runUninstall() {
390
523
  intro(
391
- `${pc2.bgCyan(pc2.black(" @withmata/blueprints "))} ${pc2.dim(`v${VERSION}`)} ${pc2.yellow("uninstall")}`
524
+ `${pc3.bgCyan(pc3.black(" @withmata/blueprints "))} ${pc3.dim(`v${VERSION}`)} ${pc3.yellow("uninstall")}`
392
525
  );
393
526
  const s = spinner3();
394
527
  s.start("Scanning for withmata skill symlinks...");
395
528
  let symlinksRemoved = 0;
396
529
  for (const dir of SKILL_DIRS) {
397
530
  if (!existsSync5(dir)) continue;
398
- for (const entry of readdirSync3(dir)) {
531
+ for (const entry of readdirSync4(dir)) {
399
532
  const fullPath = join5(dir, entry);
400
533
  try {
401
- if (!lstatSync2(fullPath).isSymbolicLink()) continue;
402
- const target = readlinkSync2(fullPath);
534
+ if (!lstatSync3(fullPath).isSymbolicLink()) continue;
535
+ const target = readlinkSync3(fullPath);
403
536
  if (target.startsWith(BLUEPRINTS_DIR)) {
404
537
  unlinkSync3(fullPath);
405
538
  symlinksRemoved++;
@@ -472,22 +605,30 @@ async function main() {
472
605
  console.log(HELP);
473
606
  process.exit(1);
474
607
  }
475
- intro2(`${pc3.bgCyan(pc3.black(" @withmata/blueprints "))} ${pc3.dim(`v${VERSION}`)}`);
608
+ const installState = detectInstallState(process.cwd());
609
+ const { isUpdate } = installState;
610
+ const modeLabel = isUpdate ? ` ${pc4.dim("\xB7 update")}` : "";
611
+ intro2(
612
+ `${pc4.bgCyan(pc4.black(" @withmata/blueprints "))} ${pc4.dim(`v${VERSION}`)}${modeLabel}`
613
+ );
476
614
  const user = await checkAuth();
477
615
  if (user) {
478
616
  log6.success(`Logged in as ${user.email} (${user.tier} plan)`);
479
617
  } else {
480
618
  log6.info("Running in local mode");
481
619
  }
482
- const tools = await detectAndSelectTools(process.cwd());
483
- const blueprints = await downloadBlueprints(user?.token);
484
- const installed = await installSkillsGlobally(tools);
620
+ const tools = await detectAndSelectTools(installState);
621
+ const blueprints = await downloadBlueprints(user?.token, isUpdate);
622
+ const installed = await installSkillsGlobally(tools, isUpdate);
485
623
  showSummary({
486
624
  installed,
487
625
  tools,
488
- blueprintCount: blueprints.total
626
+ blueprintCount: blueprints.total,
627
+ isUpdate
489
628
  });
490
- outro2("Setup complete!");
629
+ outro2(
630
+ isUpdate ? "Updated! Changes take effect in your next conversation." : "Setup complete! Type /new-project in any project to get started."
631
+ );
491
632
  }
492
633
  main().catch((err) => {
493
634
  log6.error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withmata/blueprints",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Set up AI-powered project blueprints for Claude Code, OpenCode, and Cursor",
5
5
  "type": "module",
6
6
  "bin": {