protovibe 1.0.3 → 1.1.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.
Files changed (2) hide show
  1. package/dist/index.mjs +129 -22
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -121,7 +121,7 @@ function spawnClaude(projectPath) {
121
121
 
122
122
  // src/scaffold.ts
123
123
  import { mkdirSync, writeFileSync, existsSync as existsSync2, readdirSync, statSync } from "fs";
124
- import { join as join2, dirname } from "path";
124
+ import { join as join2, dirname, resolve } from "path";
125
125
  import { homedir as homedir2 } from "os";
126
126
  import * as clack from "@clack/prompts";
127
127
 
@@ -394,6 +394,44 @@ Rules:
394
394
  - After giving the summary, ask: "Would you like to continue where we left off?"
395
395
  `;
396
396
 
397
+ // src/templates/CLAUDE-analyse.md.ts
398
+ var CLAUDE_ANALYSE_MD = `# CLAUDE.md
399
+
400
+ IMMEDIATE INSTRUCTION \u2014 execute this before doing anything else, before reading any other file, before responding to the user in any way:
401
+
402
+ Run /analyse now.
403
+
404
+ Do not greet the user. Do not ask what they want. Do not wait for input. Do not explain what you are about to do. Just run /analyse immediately and silently.
405
+ `;
406
+
407
+ // src/templates/analyse.md.ts
408
+ var ANALYSE_MD = `# /analyse \u2014 Project Analysis
409
+
410
+ When this command is run:
411
+
412
+ 1. Read every source file, configuration file, and documentation file in the current working directory, recursively. Include: all code files, package.json / pyproject.toml / Cargo.toml or equivalent, README files, config files.
413
+
414
+ 2. Build a complete understanding of the codebase. Do not ask the user any questions during this step.
415
+
416
+ 3. Present a structured summary in exactly this format:
417
+
418
+ ---
419
+ **Project:** [inferred project name from package.json, directory name, or equivalent]
420
+ **What it does:** [1\u20132 sentences describing the project's purpose and who it is for]
421
+ **Tech Stack:** [comma-separated list of languages, frameworks, and major libraries]
422
+ **Key Features:**
423
+ [bulleted list \u2014 one sentence per feature]
424
+ **Project Structure:** [2\u20133 sentences on key directories, entry points, and separation of concerns]
425
+ **Current State:** [one sentence: scaffold, early prototype, partially built, or mature codebase \u2014 based on file count, TODOs, and feature completeness]
426
+ ---
427
+
428
+ 4. After presenting the summary, say exactly:
429
+
430
+ > "I've analysed your project. I'm ready to help \u2014 what would you like to work on?"
431
+
432
+ 5. STOP. Do not suggest tasks. Do not ask questions. Wait for the user.
433
+ `;
434
+
397
435
  // src/scaffold.ts
398
436
  function getSubdirectories(dirPath) {
399
437
  try {
@@ -440,34 +478,103 @@ async function browseForDirectory() {
440
478
  }
441
479
  }
442
480
  }
443
- async function scaffoldProject() {
444
- const projectName = await clack.text({
445
- message: "Project name:",
446
- placeholder: "my-project",
481
+ function writeProtovibeTemplates(targetDir) {
482
+ mkdirSync(join2(targetDir, ".claude", "commands"), { recursive: true });
483
+ writeFileSync(join2(targetDir, "CLAUDE.md"), CLAUDE_MD, "utf-8");
484
+ writeFileSync(join2(targetDir, ".claude", "commands", "protovibe.md"), PROTOVIBE_MD, "utf-8");
485
+ writeFileSync(join2(targetDir, ".claude", "commands", "takeover.md"), TAKEOVER_MD, "utf-8");
486
+ writeFileSync(join2(targetDir, ".claude", "commands", "summarise.md"), SUMMARISE_MD, "utf-8");
487
+ }
488
+ function writeAnalyseTemplates(targetDir) {
489
+ mkdirSync(join2(targetDir, ".claude", "commands"), { recursive: true });
490
+ writeFileSync(join2(targetDir, "CLAUDE.md"), CLAUDE_ANALYSE_MD, "utf-8");
491
+ writeFileSync(join2(targetDir, ".claude", "commands", "analyse.md"), ANALYSE_MD, "utf-8");
492
+ }
493
+ async function handleExistingProject() {
494
+ const rawPath = await clack.text({
495
+ message: "Path to your existing project:",
496
+ placeholder: "/Users/you/my-project",
447
497
  validate(value) {
448
- if (value.trim() === "/exit") return void 0;
449
- if (!value.trim()) return "Project name is required.";
450
- if (/[^a-zA-Z0-9\-_]/.test(value)) return "Use only letters, numbers, hyphens, and underscores.";
498
+ if (!value.trim()) return "Path is required.";
499
+ const resolved = resolve(value.trim().replace(/^~/, homedir2()));
500
+ if (!existsSync2(resolved)) return "That path does not exist.";
501
+ try {
502
+ if (!statSync(resolved).isDirectory()) return "That path is not a directory.";
503
+ } catch {
504
+ return "Could not read that path.";
505
+ }
451
506
  }
452
507
  });
453
- if (clack.isCancel(projectName) || String(projectName).trim() === "/exit") {
508
+ if (clack.isCancel(rawPath)) {
454
509
  clack.cancel("Exiting ProtoVibe. See you next time.");
455
510
  process.exit(0);
456
511
  }
457
- clack.log.info("Select where to create your project:");
458
- const parentDir = await browseForDirectory();
459
- const targetDir = join2(parentDir, String(projectName));
460
- if (existsSync2(targetDir)) {
461
- clack.cancel(`Directory already exists: ${targetDir}`);
462
- process.exit(1);
512
+ const existingPath = resolve(String(rawPath).trim().replace(/^~/, homedir2()));
513
+ writeAnalyseTemplates(existingPath);
514
+ clack.outro(`\u2713 Ready to analyse ${existingPath}`);
515
+ return existingPath;
516
+ }
517
+ async function handleNewProject() {
518
+ let parentDir = null;
519
+ while (true) {
520
+ const nameInput = await clack.text({
521
+ message: "Project name:",
522
+ placeholder: "my-project",
523
+ validate(value) {
524
+ if (value.trim() === "/exit") return void 0;
525
+ if (!value.trim()) return "Project name is required.";
526
+ if (/[^a-zA-Z0-9\-_]/.test(value)) return "Use only letters, numbers, hyphens, and underscores.";
527
+ }
528
+ });
529
+ if (clack.isCancel(nameInput) || String(nameInput).trim() === "/exit") {
530
+ clack.cancel("Exiting ProtoVibe. See you next time.");
531
+ process.exit(0);
532
+ }
533
+ if (parentDir === null) {
534
+ clack.log.info("Select where to create your project:");
535
+ parentDir = await browseForDirectory();
536
+ }
537
+ const targetDir = join2(parentDir, String(nameInput).trim());
538
+ if (!existsSync2(targetDir)) {
539
+ writeProtovibeTemplates(targetDir);
540
+ clack.outro(`\u2713 Project created at ${targetDir}`);
541
+ return targetDir;
542
+ }
543
+ clack.log.error(`A project named '${String(nameInput).trim()}' already exists at this location.`);
544
+ const conflictChoice = await clack.select({
545
+ message: "What would you like to do?",
546
+ options: [
547
+ { value: "rename", label: "Choose a different name for my new project" },
548
+ { value: "continue", label: "Continue with the existing project" }
549
+ ]
550
+ });
551
+ if (clack.isCancel(conflictChoice)) {
552
+ clack.cancel("Exiting ProtoVibe. See you next time.");
553
+ process.exit(0);
554
+ }
555
+ if (conflictChoice === "continue") {
556
+ writeProtovibeTemplates(targetDir);
557
+ clack.outro(`\u2713 Ready to continue at ${targetDir}`);
558
+ return targetDir;
559
+ }
463
560
  }
464
- mkdirSync(join2(targetDir, ".claude", "commands"), { recursive: true });
465
- writeFileSync(join2(targetDir, "CLAUDE.md"), CLAUDE_MD, "utf-8");
466
- writeFileSync(join2(targetDir, ".claude", "commands", "protovibe.md"), PROTOVIBE_MD, "utf-8");
467
- writeFileSync(join2(targetDir, ".claude", "commands", "takeover.md"), TAKEOVER_MD, "utf-8");
468
- writeFileSync(join2(targetDir, ".claude", "commands", "summarise.md"), SUMMARISE_MD, "utf-8");
469
- clack.outro(`\u2713 Project created at ${targetDir}`);
470
- return targetDir;
561
+ }
562
+ async function scaffoldProject() {
563
+ const modeChoice = await clack.select({
564
+ message: "What would you like to do?",
565
+ options: [
566
+ { value: "new", label: "Start a new project" },
567
+ { value: "existing", label: "Continue with an existing project" }
568
+ ]
569
+ });
570
+ if (clack.isCancel(modeChoice)) {
571
+ clack.cancel("Exiting ProtoVibe. See you next time.");
572
+ process.exit(0);
573
+ }
574
+ if (modeChoice === "existing") {
575
+ return handleExistingProject();
576
+ }
577
+ return handleNewProject();
471
578
  }
472
579
 
473
580
  // src/cli.tsx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protovibe",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "A branded CLI platform layer on top of Claude Code",
5
5
  "author": "razorgojo",
6
6
  "license": "MIT",