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.
- package/dist/index.mjs +129 -22
- 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
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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()
|
|
449
|
-
|
|
450
|
-
if (
|
|
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(
|
|
508
|
+
if (clack.isCancel(rawPath)) {
|
|
454
509
|
clack.cancel("Exiting ProtoVibe. See you next time.");
|
|
455
510
|
process.exit(0);
|
|
456
511
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|