worclaude 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -20,6 +20,7 @@ Worclaude scaffolds a complete Claude Code workflow into any project in seconds.
20
20
  `worclaude init` installs a production-ready Claude Code workflow:
21
21
 
22
22
  **Agents (23 total)**
23
+
23
24
  - 5 universal: plan-reviewer, code-simplifier, test-writer, build-validator, verify-app
24
25
  - 18 optional across 6 categories: Backend, Frontend, DevOps, Quality, Documentation, Data/AI
25
26
 
@@ -27,15 +28,18 @@ Worclaude scaffolds a complete Claude Code workflow into any project in seconds.
27
28
  `/start` `/end` `/commit-push-pr` `/review-plan` `/techdebt` `/verify` `/compact-safe` `/status` `/update-claude-md` `/setup`
28
29
 
29
30
  **Skills (12)**
31
+
30
32
  - 9 universal knowledge files (testing, git conventions, context management, and more)
31
33
  - 3 project-specific templates filled in by `/setup`
32
34
 
33
35
  **Hooks**
36
+
34
37
  - PostToolUse formatter (auto-formats on every write)
35
38
  - PostCompact re-injection (re-reads key files after compaction)
36
39
  - Stop notifications (desktop alert when Claude finishes)
37
40
 
38
41
  **Configuration**
42
+
39
43
  - Pre-configured permissions per tech stack (Node.js, Python, Go, Rust, and more)
40
44
  - CLAUDE.md template with progressive disclosure
41
45
  - Sandbox, effort, and output defaults ready out of the box
@@ -58,14 +62,14 @@ For parallel tasks, run Claude with worktrees: `claude --worktree --tmux`
58
62
 
59
63
  ## Commands
60
64
 
61
- | Command | Description |
62
- |---------|-------------|
63
- | `worclaude init` | Scaffold workflow into new or existing project |
64
- | `worclaude upgrade` | Update universal components to latest version |
65
- | `worclaude status` | Show current workflow state and version |
66
- | `worclaude backup` | Create timestamped backup of workflow files |
67
- | `worclaude restore` | Restore from a previous backup |
68
- | `worclaude diff` | Compare current setup vs latest version |
65
+ | Command | Description |
66
+ | ------------------- | ---------------------------------------------- |
67
+ | `worclaude init` | Scaffold workflow into new or existing project |
68
+ | `worclaude upgrade` | Update universal components to latest version |
69
+ | `worclaude status` | Show current workflow state and version |
70
+ | `worclaude backup` | Create timestamped backup of workflow files |
71
+ | `worclaude restore` | Restore from a previous backup |
72
+ | `worclaude diff` | Compare current setup vs latest version |
69
73
 
70
74
  The `init` command detects existing setups and merges intelligently — no data is overwritten without your confirmation. Use `upgrade` to pull in new features while preserving your customizations.
71
75
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worclaude",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "CLI tool that scaffolds a comprehensive Claude Code workflow into any project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,8 +19,8 @@ export async function backupCommand() {
19
19
  }
20
20
 
21
21
  display.newline();
22
- display.success(`Backed up to ${path.basename(backupDir)}/`);
23
- display.newline();
22
+ display.success(`Backup created!`);
23
+ display.success(`${display.dimColor(path.basename(backupDir) + '/')}`);
24
24
 
25
25
  // Summarize contents
26
26
  const contents = [];
@@ -35,7 +35,6 @@ export async function backupCommand() {
35
35
  const commands = await listFiles(path.join(claudeBackup, 'commands'));
36
36
  const skills = await listFiles(path.join(claudeBackup, 'skills'));
37
37
  const parts = [];
38
- if (await fileExists(path.join(claudeBackup, 'settings.json'))) parts.push('settings.json');
39
38
  if (agents.length > 0) parts.push(`${agents.length} agents`);
40
39
  if (commands.length > 0) parts.push(`${commands.length} commands`);
41
40
  if (skills.length > 0) parts.push(`${skills.length} skills`);
@@ -47,9 +46,6 @@ export async function backupCommand() {
47
46
  }
48
47
 
49
48
  if (contents.length > 0) {
50
- display.info('Contents:');
51
- for (const item of contents) {
52
- display.dim(` ${item}`);
53
- }
49
+ display.dim(` ${contents.join(' · ')}`);
54
50
  }
55
51
  }
@@ -17,52 +17,52 @@ export async function diffCommand() {
17
17
  }
18
18
 
19
19
  const categories = await categorizeFiles(projectRoot, meta);
20
+ const cliVersion = await getPackageVersion();
20
21
 
21
- display.header('Worclaude Diff');
22
- display.newline();
23
- display.dim(` Comparing current setup to workflow v${meta.version}:`);
22
+ display.sectionHeader(`WORCLAUDE DIFF (v${meta.version} → v${cliVersion})`);
24
23
  display.newline();
25
24
 
26
25
  let hasChanges = false;
27
26
 
28
27
  if (categories.modified.length > 0) {
29
28
  hasChanges = true;
30
- display.info('Modified (your changes):');
29
+ display.barLine(`${display.yellow('~')} Modified (your changes):`);
31
30
  for (const { key } of categories.modified) {
32
- display.dim(` ~ .claude/${key}`);
31
+ display.barLine(` ${display.yellow(key)}`);
33
32
  }
34
33
  display.newline();
35
34
  }
36
35
 
37
36
  if (categories.deleted.length > 0) {
38
37
  hasChanges = true;
39
- display.info('Deleted (removed since install):');
38
+ display.barLine(`${display.red('-')} Deleted (removed since install):`);
40
39
  for (const { key } of categories.deleted) {
41
- display.dim(` - .claude/${key}`);
40
+ display.barLine(` ${display.red(key)}`);
42
41
  }
43
42
  display.newline();
44
43
  }
45
44
 
46
45
  if (categories.userAdded.length > 0) {
47
46
  hasChanges = true;
48
- display.info('Extra (you added):');
47
+ display.barLine(`${display.green('+')} Extra (you added):`);
49
48
  for (const { key } of categories.userAdded) {
50
- display.dim(` + .claude/${key}`);
49
+ display.barLine(` ${display.green(key)}`);
51
50
  }
52
51
  display.newline();
53
52
  }
54
53
 
55
54
  if (categories.outdated.length > 0) {
56
55
  hasChanges = true;
57
- const cliVersion = await getPackageVersion();
58
- display.info(`Outdated (newer version available in CLI v${cliVersion}):`);
56
+ display.barLine(`${display.blue('↑')} Outdated (newer version available):`);
59
57
  for (const { key } of categories.outdated) {
60
- display.dim(`.claude/${key}`);
58
+ display.barLine(` ${display.blue(key)}`);
61
59
  }
62
60
  display.newline();
63
61
  }
64
62
 
65
- display.dim(` Unchanged: ${categories.unchanged.length} files`);
63
+ display.barLine(
64
+ `${display.dimColor('=')} Unchanged: ${display.dimColor(`${categories.unchanged.length} files`)}`
65
+ );
66
66
 
67
67
  if (!hasChanges) {
68
68
  display.newline();
@@ -71,6 +71,6 @@ export async function diffCommand() {
71
71
 
72
72
  if (categories.outdated.length > 0) {
73
73
  display.newline();
74
- display.info('Run `worclaude upgrade` to update outdated files.');
74
+ display.barLine(`Run ${display.purple('worclaude upgrade')} to apply changes.`);
75
75
  }
76
76
  }
@@ -204,18 +204,27 @@ async function showConfirmation(selections) {
204
204
  stackLabels.push('Other / None');
205
205
  }
206
206
  if (selections.useDocker) stackLabels.push('Docker');
207
- const stackText = stackLabels.join(', ') || 'None specified';
208
207
 
209
208
  const universalCount = UNIVERSAL_AGENTS.length;
210
209
  const optionalCount = selections.selectedAgents.length;
211
210
  const totalCount = universalCount + optionalCount;
212
211
 
213
- display.reviewBox([
214
- `Project: ${selections.projectName} — ${selections.description || 'No description'}`,
215
- `Type: ${selections.projectTypes.join(', ')}`,
216
- `Stack: ${stackText}`,
217
- `Agents: ${universalCount} universal + ${optionalCount} optional (${totalCount} total)`,
218
- ]);
212
+ display.newline();
213
+ display.divider('REVIEW');
214
+ display.newline();
215
+ console.log(
216
+ ` ${'Project'.padEnd(10)}${display.white(selections.projectName)}${selections.description ? display.dimColor(` — ${selections.description}`) : ''}`
217
+ );
218
+ console.log(
219
+ ` ${'Type'.padEnd(10)}${display.renderBadgeList(selections.projectTypes, display.TYPE_BADGES)}`
220
+ );
221
+ console.log(
222
+ ` ${'Stack'.padEnd(10)}${display.renderBadgeList(stackLabels, display.STACK_BADGES)}`
223
+ );
224
+ console.log(
225
+ ` ${'Agents'.padEnd(10)}${display.white(`${universalCount} universal + ${optionalCount} optional`)} ${display.dimColor(`(${totalCount} total)`)}`
226
+ );
227
+ display.newline();
219
228
 
220
229
  const { confirmation } = await inquirer.prompt([
221
230
  {
@@ -318,15 +327,11 @@ function buildTemplateVariables(selections) {
318
327
  techStackTableItems.push('Not specified');
319
328
  }
320
329
  const techStackTable = techStackTableItems.join(', ');
321
- const dockerRow = useDocker
322
- ? '\n| Containers | Docker |'
323
- : '';
330
+ const dockerRow = useDocker ? '\n| Containers | Docker |' : '';
324
331
 
325
332
  const commandsText = buildCommandsBlock(languages, useDocker);
326
333
 
327
- const skillsLines = TEMPLATE_SKILLS.map(
328
- (s) => `- ${s}.md — Run /setup to fill automatically`
329
- );
334
+ const skillsLines = TEMPLATE_SKILLS.map((s) => `- ${s}.md — Run /setup to fill automatically`);
330
335
  const skillsText = skillsLines.join('\n');
331
336
 
332
337
  return {
@@ -346,7 +351,10 @@ async function computeAndWriteWorkflowMeta(projectRoot, selections, version) {
346
351
  const fileHashes = {};
347
352
  const claudeFiles = await listFilesRecursive(path.join(projectRoot, '.claude'));
348
353
  for (const filePath of claudeFiles) {
349
- const relativePath = path.relative(path.join(projectRoot, '.claude'), filePath).split(path.sep).join('/');
354
+ const relativePath = path
355
+ .relative(path.join(projectRoot, '.claude'), filePath)
356
+ .split(path.sep)
357
+ .join('/');
350
358
  if (relativePath !== 'workflow-meta.json' && relativePath !== 'settings.json') {
351
359
  fileHashes[relativePath] = await hashFile(filePath);
352
360
  }
@@ -448,7 +456,12 @@ async function scaffoldFresh(projectRoot, selections, variables, settingsStr, ve
448
456
  if (!(await fileExists(specPath))) {
449
457
  const primaryType = projectTypes[0];
450
458
  const specTemplate = SPEC_MD_TEMPLATE_MAP[primaryType] || 'spec-md.md';
451
- await scaffoldFile(specTemplate, path.join('docs', 'spec', 'SPEC.md'), variables, projectRoot);
459
+ await scaffoldFile(
460
+ specTemplate,
461
+ path.join('docs', 'spec', 'SPEC.md'),
462
+ variables,
463
+ projectRoot
464
+ );
452
465
  } else {
453
466
  skipped.specMd = true;
454
467
  }
@@ -467,67 +480,66 @@ async function scaffoldFresh(projectRoot, selections, variables, settingsStr, ve
467
480
  }
468
481
 
469
482
  function displayFreshSuccess(selections, skipped) {
483
+ const totalAgents = UNIVERSAL_AGENTS.length + selections.selectedAgents.length;
484
+ const totalSkills = UNIVERSAL_SKILLS.length + TEMPLATE_SKILLS.length;
485
+
470
486
  display.newline();
471
487
  display.success('CLAUDE.md');
472
488
  display.success('.claude/settings.json');
473
489
  display.success('.claude/workflow-meta.json');
474
- display.success(
475
- `.claude/agents/ (${UNIVERSAL_AGENTS.length} universal + ${selections.selectedAgents.length} optional)`
476
- );
477
- display.success(`.claude/commands/ (${COMMAND_FILES.length})`);
478
- display.success(
479
- `.claude/skills/ (${UNIVERSAL_SKILLS.length} universal + ${TEMPLATE_SKILLS.length} templates)`
480
- );
490
+ display.success(`.claude/agents/${display.dimColor(` ${totalAgents} agents`)}`);
491
+ display.success(`.claude/commands/${display.dimColor(` ${COMMAND_FILES.length} commands`)}`);
492
+ display.success(`.claude/skills/${display.dimColor(` ${totalSkills} skills`)}`);
481
493
  display.success('.mcp.json');
482
494
  if (skipped.progressMd) {
483
495
  display.dim(' docs/spec/PROGRESS.md — already exists, skipped');
496
+ } else if (skipped.specMd) {
497
+ display.success(`docs/spec/${display.dimColor(' PROGRESS.md, SPEC.md')}`);
484
498
  } else {
485
- display.success('docs/spec/PROGRESS.md');
486
- }
487
- if (skipped.specMd) {
488
- display.dim(' docs/spec/SPEC.md — already exists, skipped');
489
- } else {
490
- display.success('docs/spec/SPEC.md');
499
+ display.success(`docs/spec/${display.dimColor(' PROGRESS.md, SPEC.md')}`);
491
500
  }
501
+
492
502
  display.newline();
493
- display.info('What to do next:');
503
+ display.divider('NEXT');
494
504
  display.newline();
495
- display.dim(' 1. Start a Claude Code session in this project');
496
- display.dim(' 2. Run /setup — Claude will interview you about your project');
497
- display.dim(' and fill in all configuration files automatically');
498
- display.dim(' 3. Review CLAUDE.md and adjust if needed');
499
- display.dim(' 4. Start building!');
505
+ console.log(` ${display.white('1.')} Start a Claude Code session in this project`);
506
+ console.log(
507
+ ` ${display.white('2.')} Run ${display.purple('/setup')} Claude will interview you about your project`
508
+ );
509
+ console.log(` and fill in all configuration files automatically`);
510
+ console.log(` ${display.white('3.')} Review CLAUDE.md and adjust if needed`);
511
+ console.log(` ${display.white('4.')} Start building!`);
500
512
  display.newline();
501
- display.info('Tip: The /setup command is the fastest way to configure');
502
- display.dim(' your project. It takes about 5 minutes.');
513
+ console.log(
514
+ ` ${display.yellow('TIP')} ${display.dimColor(`${display.purple('/setup')} is the fastest way to configure. ~5 minutes.`)}`
515
+ );
503
516
  display.newline();
504
517
  }
505
518
 
506
519
  // --- Scenario B: Detection report and merge report ---
507
520
 
508
521
  function displayDetectionReport(scan) {
509
- display.info('Detected existing Claude Code setup:');
510
- display.newline();
522
+ display.sectionHeader('DETECTED SETUP');
511
523
 
512
524
  const dot = (label, width = 26) => label + ' ' + '.'.repeat(width - label.length) + ' ';
513
525
 
514
- display.dim(
515
- ` ${dot('CLAUDE.md')}${scan.hasClaudeMd ? `exists (${scan.claudeMdLineCount} lines)` : 'not found'}`
526
+ display.barLine(
527
+ `${display.dimColor(dot('CLAUDE.md'))}${scan.hasClaudeMd ? display.white(`exists (${scan.claudeMdLineCount} lines)`) : display.dimColor('not found')}`
516
528
  );
517
- display.dim(
518
- ` ${dot('.claude/settings.json')}${scan.hasSettingsJson ? 'exists' : 'not found'}`
529
+ display.barLine(
530
+ `${display.dimColor(dot('.claude/settings.json'))}${scan.hasSettingsJson ? display.white('exists') : display.dimColor('not found')}`
519
531
  );
520
- display.dim(
521
- ` ${dot('.claude/skills/')}${scan.existingSkills.length > 0 ? `${scan.existingSkills.length} files found` : 'not found'}`
532
+ display.barLine(
533
+ `${display.dimColor(dot('.claude/skills/'))}${scan.existingSkills.length > 0 ? display.white(`${scan.existingSkills.length} files found`) : display.dimColor('not found')}`
522
534
  );
523
- display.dim(
524
- ` ${dot('.claude/agents/')}${scan.existingAgents.length > 0 ? `${scan.existingAgents.length} files found` : 'not found'}`
535
+ display.barLine(
536
+ `${display.dimColor(dot('.claude/agents/'))}${scan.existingAgents.length > 0 ? display.white(`${scan.existingAgents.length} files found`) : display.dimColor('not found')}`
525
537
  );
526
- display.dim(
527
- ` ${dot('.claude/commands/')}${scan.existingCommands.length > 0 ? `${scan.existingCommands.length} files found` : 'not found'}`
538
+ display.barLine(
539
+ `${display.dimColor(dot('.claude/commands/'))}${scan.existingCommands.length > 0 ? display.white(`${scan.existingCommands.length} files found`) : display.dimColor('not found')}`
528
540
  );
529
- display.dim(
530
- ` ${dot('.mcp.json')}${scan.hasMcpJson ? 'exists' : 'not found'}`
541
+ display.barLine(
542
+ `${display.dimColor(dot('.mcp.json'))}${scan.hasMcpJson ? display.white('exists') : display.dimColor('not found')}`
531
543
  );
532
544
  display.newline();
533
545
  display.info('A backup will be created before any changes.');
@@ -543,23 +555,25 @@ function displayMergeReport(report, backupPath) {
543
555
  report.added.commands.length > 0 ||
544
556
  report.added.skills.length > 0
545
557
  ) {
546
- display.info('Added automatically:');
558
+ display.barLine(`${display.green('+')} Added automatically:`);
547
559
  if (report.added.agents.length > 0) {
548
- display.success(`${report.added.agents.length} agents added`);
560
+ display.barLine(` ${display.green('✓')} ${report.added.agents.length} agents added`);
549
561
  }
550
562
  if (report.added.commands.length > 0) {
551
- display.success(`${report.added.commands.length} commands added`);
563
+ display.barLine(` ${display.green('✓')} ${report.added.commands.length} commands added`);
552
564
  }
553
565
  if (report.added.skills.length > 0) {
554
- display.success(
555
- `${report.added.skills.length} skills added${report.conflicts.skills.length > 0 ? ` (${report.conflicts.skills.length} conflicts saved as .workflow-ref.md)` : ''}`
566
+ display.barLine(
567
+ ` ${display.green('✓')} ${report.added.skills.length} skills added${report.conflicts.skills.length > 0 ? ` (${report.conflicts.skills.length} conflicts saved as .workflow-ref.md)` : ''}`
556
568
  );
557
569
  }
558
570
  if (report.added.permissions > 0) {
559
- display.success(`${report.added.permissions} permission rules appended to settings.json`);
571
+ display.barLine(
572
+ ` ${display.green('✓')} ${report.added.permissions} permission rules appended to settings.json`
573
+ );
560
574
  }
561
575
  if (report.added.hooks > 0) {
562
- display.success(`${report.added.hooks} hooks added to settings.json`);
576
+ display.barLine(` ${display.green('✓')} ${report.added.hooks} hooks added to settings.json`);
563
577
  }
564
578
  display.newline();
565
579
  }
@@ -571,19 +585,19 @@ function displayMergeReport(report, backupPath) {
571
585
  ...report.conflicts.commands,
572
586
  ];
573
587
  if (allConflicts.length > 0) {
574
- display.info('Conflicts (saved alongside for review):');
588
+ display.barLine(`${display.yellow('~')} Conflicts (saved alongside for review):`);
575
589
  for (const file of allConflicts) {
576
590
  const refName = file.replace('.md', '.workflow-ref.md');
577
- display.warn(`${file} → ${refName}`);
591
+ display.barLine(` ${display.yellow('⚠')} ${file} → ${refName}`);
578
592
  }
579
593
  display.newline();
580
594
  }
581
595
 
582
596
  // Tier 3 — Hook conflicts
583
597
  if (report.hookConflicts.length > 0) {
584
- display.info('Hook conflicts resolved:');
598
+ display.barLine(`Hook conflicts resolved:`);
585
599
  for (const desc of report.hookConflicts) {
586
- display.dim(` ${desc}`);
600
+ display.barLine(` ${display.dimColor(desc)}`);
587
601
  }
588
602
  display.newline();
589
603
  }
@@ -610,17 +624,22 @@ function displayMergeReport(report, backupPath) {
610
624
  if (backupPath) {
611
625
  display.dim(` Backup: ${path.basename(backupPath)}/`);
612
626
  }
627
+
613
628
  display.newline();
614
- display.info('What to do next:');
629
+ display.divider('NEXT');
615
630
  display.newline();
616
631
  if (allConflicts.length > 0) {
617
- display.dim(' 1. Review .workflow-ref.md files and merge what\'s useful');
632
+ console.log(` ${display.white('1.')} Review .workflow-ref.md files and merge what's useful`);
618
633
  }
619
634
  if (report.claudeMdHandling === 'suggestions-generated') {
620
- display.dim(' 2. Review CLAUDE.md.workflow-suggestions');
621
- display.dim(' 3. Delete .workflow-ref.md and .workflow-suggestions files when done');
635
+ console.log(` ${display.white('2.')} Review CLAUDE.md.workflow-suggestions`);
636
+ console.log(
637
+ ` ${display.white('3.')} Delete .workflow-ref.md and .workflow-suggestions files when done`
638
+ );
622
639
  }
623
- display.dim(' Run /setup in Claude Code for project-specific configuration');
640
+ console.log(
641
+ ` Run ${display.purple('/setup')} in Claude Code for project-specific configuration`
642
+ );
624
643
  display.newline();
625
644
  }
626
645
 
@@ -634,17 +653,14 @@ export async function initCommand() {
634
653
 
635
654
  if (scenario === 'upgrade') {
636
655
  const meta = await readWorkflowMeta(projectRoot);
637
- display.info(
638
- `This project was initialized with Worclaude v${meta?.version || 'unknown'}.`
639
- );
656
+ display.info(`This project was initialized with Worclaude v${meta?.version || 'unknown'}.`);
640
657
  display.info('Use `worclaude upgrade` to update.');
641
658
  return;
642
659
  }
643
660
 
644
661
  // Step 2: Welcome
645
662
  const version = await getPackageVersion();
646
- display.header(`Worclaude v${version}`);
647
- display.newline();
663
+ display.banner(version);
648
664
 
649
665
  // Step 3: If existing project, show detection report and confirm
650
666
  let existingScan = null;
@@ -692,7 +708,9 @@ export async function initCommand() {
692
708
  // Scenario B: merge
693
709
  const spinner = ora('Merging workflow...').start();
694
710
  try {
695
- const report = await performMerge(projectRoot, existingScan, selections, variables, { spinner });
711
+ const report = await performMerge(projectRoot, existingScan, selections, variables, {
712
+ spinner,
713
+ });
696
714
  await computeAndWriteWorkflowMeta(projectRoot, selections, version);
697
715
  spinner.succeed('Workflow merged successfully!');
698
716
  displayMergeReport(report, backupPath);
@@ -15,7 +15,7 @@ export async function restoreCommand() {
15
15
  return;
16
16
  }
17
17
 
18
- display.header('Available Backups');
18
+ display.sectionHeader('AVAILABLE BACKUPS');
19
19
  display.newline();
20
20
 
21
21
  const choices = backups.map((b, i) => ({
@@ -77,7 +77,8 @@ export async function restoreCommand() {
77
77
  // Show what was restored
78
78
  const restored = [];
79
79
  if (await fileExists(path.join(projectRoot, 'CLAUDE.md'))) restored.push('CLAUDE.md');
80
- if (await dirExists(path.join(projectRoot, '.claude'))) restored.push('.claude/ (full directory)');
80
+ if (await dirExists(path.join(projectRoot, '.claude')))
81
+ restored.push('.claude/ (full directory)');
81
82
  if (await fileExists(path.join(projectRoot, '.mcp.json'))) restored.push('.mcp.json');
82
83
 
83
84
  if (restored.length > 0) {
@@ -88,8 +89,6 @@ export async function restoreCommand() {
88
89
  }
89
90
 
90
91
  display.newline();
91
- display.info(
92
- 'Note: workflow-meta.json has been restored to its backup state.'
93
- );
92
+ display.info('Note: workflow-meta.json has been restored to its backup state.');
94
93
  display.dim(' Run `worclaude upgrade` if you want to update to the latest version.');
95
94
  }
@@ -25,40 +25,41 @@ export async function statusCommand() {
25
25
  return;
26
26
  }
27
27
 
28
- display.header('Worclaude Status');
29
- display.newline();
28
+ display.sectionHeader('WORCLAUDE STATUS');
30
29
 
31
- // Version and dates
32
- display.dim(` Version: ${meta.version}`);
33
- display.dim(` Installed: ${meta.installedAt?.split('T')[0] || 'unknown'}`);
34
- display.dim(` Last updated: ${meta.lastUpdated?.split('T')[0] || 'unknown'}`);
35
- display.newline();
30
+ // Version
31
+ display.barLine(
32
+ `${'Version'.padEnd(11)}${display.green(`v${meta.version}`)} ${display.dimColor('(up to date)')}`
33
+ );
36
34
 
37
35
  // Project info
38
- const projectTypes = (meta.projectTypes || []).join(', ');
39
- if (projectTypes) display.dim(` Project type: ${projectTypes}`);
36
+ const projectTypes = meta.projectTypes || [];
37
+ if (projectTypes.length > 0) {
38
+ display.barLine(`${'Project'.padEnd(11)}${display.white(projectTypes.join(', '))}`);
39
+ display.barLine(
40
+ `${'Type'.padEnd(11)}${display.renderBadgeList(projectTypes, display.TYPE_BADGES)}`
41
+ );
42
+ }
40
43
 
41
- const techNames = (meta.techStack || [])
42
- .map((t) => TECH_DISPLAY_NAMES[t] || t)
43
- .join(', ');
44
- if (techNames) display.dim(` Tech stack: ${techNames}`);
45
- display.newline();
44
+ const techNames = (meta.techStack || []).map((t) => TECH_DISPLAY_NAMES[t] || t);
45
+ if (techNames.length > 0) {
46
+ display.barLine(
47
+ `${'Stack'.padEnd(11)}${display.renderBadgeList(techNames, display.STACK_BADGES)}`
48
+ );
49
+ }
46
50
 
47
51
  // Agents
48
52
  const universalCount = (meta.universalAgents || []).length;
49
53
  const optionalCount = (meta.optionalAgents || []).length;
50
- const totalAgents = universalCount + optionalCount;
51
- display.dim(` Agents: ${universalCount} universal + ${optionalCount} optional (${totalAgents} total)`);
52
- if (optionalCount > 0) {
53
- display.dim(` Optional: ${meta.optionalAgents.join(', ')}`);
54
- }
55
- display.newline();
54
+ display.barLine(
55
+ `${'Agents'.padEnd(11)}${display.white(`${universalCount} universal + ${optionalCount} optional`)}`
56
+ );
56
57
 
57
58
  // Commands and skills counts
58
59
  const commandCount = countByPrefix(meta.fileHashes || {}, 'commands/');
59
60
  const skillCount = countByPrefix(meta.fileHashes || {}, 'skills/');
60
- display.dim(` Commands: ${commandCount} installed`);
61
- display.dim(` Skills: ${skillCount} installed`);
61
+ display.barLine(`${'Commands'.padEnd(11)}${display.white(String(commandCount))}`);
62
+ display.barLine(`${'Skills'.padEnd(11)}${display.white(String(skillCount))}`);
62
63
  display.newline();
63
64
 
64
65
  // Customized files
@@ -127,10 +128,7 @@ export async function statusCommand() {
127
128
  const permCount = allow.filter((p) => !p.trim().startsWith('//')).length;
128
129
 
129
130
  const hooks = settings.hooks || {};
130
- const hookCount = Object.values(hooks).reduce(
131
- (sum, entries) => sum + entries.length,
132
- 0
133
- );
131
+ const hookCount = Object.values(hooks).reduce((sum, entries) => sum + entries.length, 0);
134
132
 
135
133
  display.dim(` Hooks: ${hookCount} active`);
136
134
  display.dim(` Permissions: ${permCount} rules`);
@@ -45,51 +45,52 @@ export async function upgradeCommand() {
45
45
  const categories = await categorizeFiles(projectRoot, meta);
46
46
 
47
47
  // 4. Preview
48
- display.header('Worclaude Upgrade');
49
- display.newline();
50
- display.dim(` Current version: ${installedVersion}`);
51
- display.dim(` New version: ${currentVersion}`);
48
+ display.sectionHeader(`WORCLAUDE UPGRADE (v${installedVersion} → v${currentVersion})`);
52
49
  display.newline();
53
50
 
54
- display.info('Changes:');
51
+ display.barLine('Changes:');
55
52
 
56
53
  if (categories.autoUpdate.length > 0) {
57
- display.dim(' Auto-update (unchanged since install):');
54
+ display.barLine(`${display.green('✓')} Auto-update (unchanged since install):`);
58
55
  const showCount = Math.min(categories.autoUpdate.length, 3);
59
56
  for (let i = 0; i < showCount; i++) {
60
- display.dim(` ✓ ${categories.autoUpdate[i].key}`);
57
+ display.barLine(` ${display.green('')} ${categories.autoUpdate[i].key}`);
61
58
  }
62
59
  if (categories.autoUpdate.length > 3) {
63
- display.dim(` ✓ ${categories.autoUpdate.length - 3} more files`);
60
+ display.barLine(` ${display.green('')} ${categories.autoUpdate.length - 3} more files`);
64
61
  }
65
62
  display.newline();
66
63
  }
67
64
 
68
65
  if (categories.conflict.length > 0) {
69
- display.dim(' Needs review (you\'ve customized these):');
66
+ display.barLine(`${display.yellow('~')} Needs review (you've customized these):`);
70
67
  for (const { key } of categories.conflict) {
71
- display.dim(` ~ ${key} (modified since install)`);
68
+ display.barLine(
69
+ ` ${display.yellow('~')} ${key} ${display.dimColor('(modified since install)')}`
70
+ );
72
71
  }
73
72
  display.newline();
74
73
  }
75
74
 
76
75
  if (categories.newFiles.length > 0) {
77
- display.dim(' New in this version:');
76
+ display.barLine(`${display.green('+')} New in this version:`);
78
77
  for (const { key } of categories.newFiles) {
79
- display.dim(` + ${key}`);
78
+ display.barLine(` ${display.green('+')} ${key}`);
80
79
  }
81
80
  display.newline();
82
81
  }
83
82
 
84
83
  if (categories.unchanged.length > 0) {
85
- display.dim(` Unchanged (no updates needed): ${categories.unchanged.length} files`);
84
+ display.barLine(
85
+ `${display.dimColor('=')} Unchanged: ${display.dimColor(`${categories.unchanged.length} files`)}`
86
+ );
86
87
  display.newline();
87
88
  }
88
89
 
89
90
  if (categories.modified.length > 0) {
90
- display.dim(' Your customizations (no workflow updates available):');
91
+ display.barLine(`${display.yellow('~')} Your customizations (no workflow updates available):`);
91
92
  for (const { key } of categories.modified) {
92
- display.dim(` ~ ${key}`);
93
+ display.barLine(` ${display.yellow('~')} ${key}`);
93
94
  }
94
95
  display.newline();
95
96
  }
@@ -182,24 +183,28 @@ export async function upgradeCommand() {
182
183
  // 7. Display report
183
184
  display.newline();
184
185
  if (categories.autoUpdate.length > 0) {
185
- display.dim(` Updated: ${categories.autoUpdate.length} files`);
186
+ display.barLine(`Updated: ${categories.autoUpdate.length} files`);
186
187
  }
187
188
  if (categories.conflict.length > 0) {
188
- display.dim(` Conflicts: ${categories.conflict.length} files (saved as .workflow-ref.md)`);
189
+ display.barLine(
190
+ `Conflicts: ${categories.conflict.length} files ${display.dimColor('(saved as .workflow-ref.md)')}`
191
+ );
189
192
  }
190
193
  if (categories.newFiles.length > 0) {
191
- display.dim(` New: ${categories.newFiles.length} files added`);
194
+ display.barLine(`New: ${categories.newFiles.length} files added`);
192
195
  }
193
- display.dim(` Unchanged: ${categories.unchanged.length} files`);
196
+ display.barLine(`Unchanged: ${categories.unchanged.length} files`);
194
197
  if (categories.modified.length > 0) {
195
- display.dim(` Customized: ${categories.modified.length} files (no updates needed)`);
198
+ display.barLine(
199
+ `Customized: ${categories.modified.length} files ${display.dimColor('(no updates needed)')}`
200
+ );
196
201
  }
197
202
  display.newline();
198
- display.dim(` Backup: ${path.basename(backupDir)}/`);
203
+ display.barLine(display.dimColor(`Backup: ${path.basename(backupDir)}/`));
199
204
 
200
205
  if (categories.conflict.length > 0) {
201
206
  display.newline();
202
- display.info('Review .workflow-ref.md files and merge what\'s useful.');
207
+ display.barLine(`Review .workflow-ref.md files and merge what's useful.`);
203
208
  }
204
209
  } catch (err) {
205
210
  spinner.fail('Upgrade failed.');
@@ -1,12 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import os from 'node:os';
3
3
  import { readFile, writeFile } from '../utils/file.js';
4
- import {
5
- readTemplate,
6
- substituteVariables,
7
- scaffoldFile,
8
- mergeSettings,
9
- } from './scaffolder.js';
4
+ import { readTemplate, substituteVariables, scaffoldFile, mergeSettings } from './scaffolder.js';
10
5
  import { promptHookConflict } from '../prompts/conflict-resolution.js';
11
6
  import {
12
7
  detectMissingSections,
@@ -102,8 +97,16 @@ function parseUserJson(raw, filename) {
102
97
 
103
98
  async function mergeSkills(projectRoot, existingScan, variables, report) {
104
99
  const allSkills = [
105
- ...UNIVERSAL_SKILLS.map((s) => ({ name: s, templatePath: `skills/universal/${s}.md`, vars: {} })),
106
- ...TEMPLATE_SKILLS.map((s) => ({ name: s, templatePath: `skills/templates/${s}.md`, vars: variables })),
100
+ ...UNIVERSAL_SKILLS.map((s) => ({
101
+ name: s,
102
+ templatePath: `skills/universal/${s}.md`,
103
+ vars: {},
104
+ })),
105
+ ...TEMPLATE_SKILLS.map((s) => ({
106
+ name: s,
107
+ templatePath: `skills/templates/${s}.md`,
108
+ vars: variables,
109
+ })),
107
110
  ];
108
111
 
109
112
  for (const skill of allSkills) {
@@ -251,13 +254,9 @@ export async function mergeSettingsPermissionsAndHooks(projectRoot, workflowSett
251
254
  },
252
255
  ],
253
256
  };
254
- report.hookConflicts.push(
255
- `${category} "${workflowEntry.matcher}": chained both hooks`
256
- );
257
+ report.hookConflicts.push(`${category} "${workflowEntry.matcher}": chained both hooks`);
257
258
  } else {
258
- report.hookConflicts.push(
259
- `${category} "${workflowEntry.matcher}": kept existing hook`
260
- );
259
+ report.hookConflicts.push(`${category} "${workflowEntry.matcher}": kept existing hook`);
261
260
  }
262
261
  } else {
263
262
  // Tier 1: no conflict — append
@@ -291,9 +290,7 @@ async function mergeSettingsJson(projectRoot, existingScan, selections, report)
291
290
  try {
292
291
  await mergeSettingsPermissionsAndHooks(projectRoot, workflowSettings, report);
293
292
  } catch {
294
- display.warn(
295
- 'Existing settings.json contains invalid JSON — creating fresh settings instead.'
296
- );
293
+ display.warn('Existing settings.json contains invalid JSON — creating fresh settings instead.');
297
294
  await writeFile(path.join(projectRoot, '.claude', 'settings.json'), settingsStr);
298
295
  report.added.permissions = workflowSettings.permissions?.allow?.length || 0;
299
296
  report.added.hooks = countHooks(workflowSettings.hooks);
@@ -383,7 +380,13 @@ async function handleClaudeMd(projectRoot, existingScan, variables, report) {
383
380
 
384
381
  // --- Main merge function ---
385
382
 
386
- export async function performMerge(projectRoot, existingScan, selections, variables, { spinner } = {}) {
383
+ export async function performMerge(
384
+ projectRoot,
385
+ existingScan,
386
+ selections,
387
+ variables,
388
+ { spinner } = {}
389
+ ) {
387
390
  const report = {
388
391
  added: { skills: [], agents: [], commands: [], permissions: 0, hooks: 0 },
389
392
  conflicts: { skills: [], agents: [], commands: [] },
@@ -7,34 +7,154 @@ export const UNIVERSAL_AGENTS = [
7
7
  ];
8
8
 
9
9
  export const AGENT_CATALOG = {
10
- 'ui-reviewer': { model: 'sonnet', isolation: 'none', category: 'frontend', description: 'Reviews UI for consistency and accessibility' },
11
- 'style-enforcer': { model: 'haiku', isolation: 'none', category: 'frontend', description: 'Ensures design system compliance' },
12
- 'api-designer': { model: 'opus', isolation: 'none', category: 'backend', description: 'Reviews API design for RESTful conventions' },
13
- 'database-analyst': { model: 'sonnet', isolation: 'none', category: 'backend', description: 'Reviews database schemas and queries' },
14
- 'auth-auditor': { model: 'opus', isolation: 'none', category: 'backend', description: 'Audits authentication and authorization' },
15
- 'security-reviewer': { model: 'opus', isolation: 'none', category: 'quality', description: 'Reviews code for security vulnerabilities' },
16
- 'performance-auditor': { model: 'sonnet', isolation: 'none', category: 'quality', description: 'Analyzes code for performance issues' },
17
- 'bug-fixer': { model: 'sonnet', isolation: 'worktree', category: 'quality', description: 'Diagnoses and fixes bugs' },
18
- 'refactorer': { model: 'sonnet', isolation: 'worktree', category: 'quality', description: 'Refactors code to improve maintainability' },
19
- 'dependency-manager': { model: 'haiku', isolation: 'none', category: 'devops', description: 'Reviews dependency health and updates' },
20
- 'ci-fixer': { model: 'sonnet', isolation: 'worktree', category: 'devops', description: 'Diagnoses and fixes CI/CD failures' },
21
- 'docker-helper': { model: 'sonnet', isolation: 'none', category: 'devops', description: 'Reviews Docker configs for best practices' },
22
- 'deploy-validator': { model: 'sonnet', isolation: 'none', category: 'devops', description: 'Validates deployment readiness' },
23
- 'doc-writer': { model: 'sonnet', isolation: 'worktree', category: 'docs', description: 'Writes and updates documentation' },
24
- 'changelog-generator': { model: 'haiku', isolation: 'none', category: 'docs', description: 'Generates changelog from commits' },
25
- 'data-pipeline-reviewer': { model: 'sonnet', isolation: 'none', category: 'data', description: 'Reviews data pipeline correctness' },
26
- 'ml-experiment-tracker': { model: 'sonnet', isolation: 'none', category: 'data', description: 'Reviews ML experiment reproducibility' },
27
- 'prompt-engineer': { model: 'opus', isolation: 'none', category: 'data', description: 'Reviews and improves LLM prompts' },
10
+ 'ui-reviewer': {
11
+ model: 'sonnet',
12
+ isolation: 'none',
13
+ category: 'frontend',
14
+ description: 'Reviews UI for consistency and accessibility',
15
+ },
16
+ 'style-enforcer': {
17
+ model: 'haiku',
18
+ isolation: 'none',
19
+ category: 'frontend',
20
+ description: 'Ensures design system compliance',
21
+ },
22
+ 'api-designer': {
23
+ model: 'opus',
24
+ isolation: 'none',
25
+ category: 'backend',
26
+ description: 'Reviews API design for RESTful conventions',
27
+ },
28
+ 'database-analyst': {
29
+ model: 'sonnet',
30
+ isolation: 'none',
31
+ category: 'backend',
32
+ description: 'Reviews database schemas and queries',
33
+ },
34
+ 'auth-auditor': {
35
+ model: 'opus',
36
+ isolation: 'none',
37
+ category: 'backend',
38
+ description: 'Audits authentication and authorization',
39
+ },
40
+ 'security-reviewer': {
41
+ model: 'opus',
42
+ isolation: 'none',
43
+ category: 'quality',
44
+ description: 'Reviews code for security vulnerabilities',
45
+ },
46
+ 'performance-auditor': {
47
+ model: 'sonnet',
48
+ isolation: 'none',
49
+ category: 'quality',
50
+ description: 'Analyzes code for performance issues',
51
+ },
52
+ 'bug-fixer': {
53
+ model: 'sonnet',
54
+ isolation: 'worktree',
55
+ category: 'quality',
56
+ description: 'Diagnoses and fixes bugs',
57
+ },
58
+ refactorer: {
59
+ model: 'sonnet',
60
+ isolation: 'worktree',
61
+ category: 'quality',
62
+ description: 'Refactors code to improve maintainability',
63
+ },
64
+ 'dependency-manager': {
65
+ model: 'haiku',
66
+ isolation: 'none',
67
+ category: 'devops',
68
+ description: 'Reviews dependency health and updates',
69
+ },
70
+ 'ci-fixer': {
71
+ model: 'sonnet',
72
+ isolation: 'worktree',
73
+ category: 'devops',
74
+ description: 'Diagnoses and fixes CI/CD failures',
75
+ },
76
+ 'docker-helper': {
77
+ model: 'sonnet',
78
+ isolation: 'none',
79
+ category: 'devops',
80
+ description: 'Reviews Docker configs for best practices',
81
+ },
82
+ 'deploy-validator': {
83
+ model: 'sonnet',
84
+ isolation: 'none',
85
+ category: 'devops',
86
+ description: 'Validates deployment readiness',
87
+ },
88
+ 'doc-writer': {
89
+ model: 'sonnet',
90
+ isolation: 'worktree',
91
+ category: 'docs',
92
+ description: 'Writes and updates documentation',
93
+ },
94
+ 'changelog-generator': {
95
+ model: 'haiku',
96
+ isolation: 'none',
97
+ category: 'docs',
98
+ description: 'Generates changelog from commits',
99
+ },
100
+ 'data-pipeline-reviewer': {
101
+ model: 'sonnet',
102
+ isolation: 'none',
103
+ category: 'data',
104
+ description: 'Reviews data pipeline correctness',
105
+ },
106
+ 'ml-experiment-tracker': {
107
+ model: 'sonnet',
108
+ isolation: 'none',
109
+ category: 'data',
110
+ description: 'Reviews ML experiment reproducibility',
111
+ },
112
+ 'prompt-engineer': {
113
+ model: 'opus',
114
+ isolation: 'none',
115
+ category: 'data',
116
+ description: 'Reviews and improves LLM prompts',
117
+ },
28
118
  };
29
119
 
30
120
  export const CATEGORY_RECOMMENDATIONS = {
31
- 'Full-stack web application': ['ui-reviewer', 'api-designer', 'database-analyst', 'security-reviewer', 'bug-fixer', 'doc-writer'],
32
- 'Backend / API': ['api-designer', 'database-analyst', 'security-reviewer', 'auth-auditor', 'bug-fixer', 'performance-auditor'],
121
+ 'Full-stack web application': [
122
+ 'ui-reviewer',
123
+ 'api-designer',
124
+ 'database-analyst',
125
+ 'security-reviewer',
126
+ 'bug-fixer',
127
+ 'doc-writer',
128
+ ],
129
+ 'Backend / API': [
130
+ 'api-designer',
131
+ 'database-analyst',
132
+ 'security-reviewer',
133
+ 'auth-auditor',
134
+ 'bug-fixer',
135
+ 'performance-auditor',
136
+ ],
33
137
  'Frontend / UI': ['ui-reviewer', 'style-enforcer', 'performance-auditor', 'bug-fixer'],
34
138
  'CLI tool': ['bug-fixer', 'doc-writer', 'dependency-manager'],
35
- 'Data / ML / AI': ['data-pipeline-reviewer', 'ml-experiment-tracker', 'prompt-engineer', 'database-analyst'],
36
- 'Library / Package': ['doc-writer', 'dependency-manager', 'performance-auditor', 'refactorer', 'changelog-generator'],
37
- 'DevOps / Infrastructure': ['ci-fixer', 'docker-helper', 'deploy-validator', 'dependency-manager'],
139
+ 'Data / ML / AI': [
140
+ 'data-pipeline-reviewer',
141
+ 'ml-experiment-tracker',
142
+ 'prompt-engineer',
143
+ 'database-analyst',
144
+ ],
145
+ 'Library / Package': [
146
+ 'doc-writer',
147
+ 'dependency-manager',
148
+ 'performance-auditor',
149
+ 'refactorer',
150
+ 'changelog-generator',
151
+ ],
152
+ 'DevOps / Infrastructure': [
153
+ 'ci-fixer',
154
+ 'docker-helper',
155
+ 'deploy-validator',
156
+ 'dependency-manager',
157
+ ],
38
158
  };
39
159
 
40
160
  export const COMMAND_FILES = [
package/src/index.js CHANGED
@@ -38,10 +38,7 @@ program
38
38
  .description('Create a backup of current Claude setup')
39
39
  .action(backupCommand);
40
40
 
41
- program
42
- .command('restore')
43
- .description('Restore Claude setup from a backup')
44
- .action(restoreCommand);
41
+ program.command('restore').description('Restore Claude setup from a backup').action(restoreCommand);
45
42
 
46
43
  program
47
44
  .command('diff')
@@ -10,9 +10,9 @@ import * as display from '../utils/display.js';
10
10
  export async function promptAgentSelection(projectTypes) {
11
11
  // Show universal agents (informational)
12
12
  display.newline();
13
- display.info('Universal agents (always installed):');
13
+ display.sectionHeader('UNIVERSAL AGENTS');
14
14
  for (const agent of UNIVERSAL_AGENTS) {
15
- display.success(agent);
15
+ display.barLine(`${display.green('✓')} ${display.renderAgentWithBadges(agent)}`);
16
16
  }
17
17
  display.newline();
18
18
 
@@ -68,7 +68,7 @@ export async function promptAgentSelection(projectTypes) {
68
68
  {
69
69
  type: 'checkbox',
70
70
  name: 'additionalCategories',
71
- message: 'Any other agent categories you\'d like to add? (space to toggle, enter to skip)',
71
+ message: "Any other agent categories you'd like to add? (space to toggle, enter to skip)",
72
72
  choices: unselectedCategories.map((cat) => ({
73
73
  name: `${cat} — ${AGENT_CATEGORIES[cat].description}`,
74
74
  value: cat,
@@ -58,7 +58,7 @@ export function generateWorkflowSuggestions(existingContent, renderedTemplate) {
58
58
  '# Worclaude — Suggested CLAUDE.md Additions',
59
59
  '',
60
60
  'The following sections are recommended based on the Claude Code',
61
- 'workflow system. Review and merge what\'s useful into your CLAUDE.md.',
61
+ "workflow system. Review and merge what's useful into your CLAUDE.md.",
62
62
  '',
63
63
  ];
64
64
 
@@ -1,5 +1,9 @@
1
1
  import inquirer from 'inquirer';
2
- import { PROJECT_TYPES, PROJECT_TYPE_DESCRIPTIONS, CATEGORY_RECOMMENDATIONS } from '../data/agents.js';
2
+ import {
3
+ PROJECT_TYPES,
4
+ PROJECT_TYPE_DESCRIPTIONS,
5
+ CATEGORY_RECOMMENDATIONS,
6
+ } from '../data/agents.js';
3
7
  import * as display from '../utils/display.js';
4
8
 
5
9
  export async function promptProjectType() {
@@ -8,7 +12,7 @@ export async function promptProjectType() {
8
12
  type: 'checkbox',
9
13
  name: 'projectTypes',
10
14
  message:
11
- 'What type of project is this? (space to toggle, enter to confirm)\n ℹ Not sure? Pick what\'s closest. You can add/remove agents later.',
15
+ "What type of project is this? (space to toggle, enter to confirm)\n ℹ Not sure? Pick what's closest. You can add/remove agents later.",
12
16
  choices: PROJECT_TYPES.map((t) => ({
13
17
  name: `${t} — ${PROJECT_TYPE_DESCRIPTIONS[t]}`,
14
18
  value: t,
@@ -1,41 +1,186 @@
1
1
  import chalk from 'chalk';
2
+ import { AGENT_CATALOG } from '../data/agents.js';
2
3
 
3
- export function header(text) {
4
+ // ── Color Palette ──────────────────────────────────────────────
5
+
6
+ // Brand colors
7
+ export const purple = chalk.hex('#c4b5fd');
8
+ // purpleDim covered by dimColor
9
+
10
+ // Semantic colors
11
+ export const green = chalk.hex('#6ee7b7');
12
+ export const yellow = chalk.hex('#fbbf24');
13
+ export const white = chalk.hex('#e2e0f0');
14
+ export const dimColor = chalk.hex('#6b6590');
15
+ export const blue = chalk.hex('#7dd3fc');
16
+ // teal and pink are used via badge styles below
17
+ export const red = chalk.hex('#fca5a5');
18
+
19
+ // ── Badge Styles ───────────────────────────────────────────────
20
+
21
+ export const badges = {
22
+ // Models
23
+ opus: chalk.bgHex('#2d2455').hex('#c4b5fd'),
24
+ sonnet: chalk.bgHex('#1a2a3a').hex('#7dd3fc'),
25
+ haiku: chalk.bgHex('#2a2520').hex('#fbbf24'),
26
+
27
+ // Isolation
28
+ worktree: chalk.bgHex('#1a3a2a').hex('#6ee7b7'),
29
+
30
+ // Categories
31
+ backend: chalk.bgHex('#2d2455').hex('#c4b5fd'),
32
+ frontend: chalk.bgHex('#2a1a2a').hex('#f9a8d4'),
33
+ devops: chalk.bgHex('#1a2a2a').hex('#5eead4'),
34
+ quality: chalk.bgHex('#2a2520').hex('#fbbf24'),
35
+ docs: chalk.bgHex('#1a2520').hex('#6ee7b7'),
36
+ dataai: chalk.bgHex('#1a1a2a').hex('#818cf8'),
37
+
38
+ // Tech stacks
39
+ python: chalk.bgHex('#1a3a2a').hex('#6ee7b7'),
40
+ node: chalk.bgHex('#1a3a2a').hex('#6ee7b7'),
41
+ java: chalk.bgHex('#2a2520').hex('#fbbf24'),
42
+ csharp: chalk.bgHex('#2d2455').hex('#c4b5fd'),
43
+ cpp: chalk.bgHex('#1a2a3a').hex('#7dd3fc'),
44
+ go: chalk.bgHex('#1a2a2a').hex('#5eead4'),
45
+ rust: chalk.bgHex('#2a1a1a').hex('#fca5a5'),
46
+ php: chalk.bgHex('#1a1a2a').hex('#818cf8'),
47
+ docker: chalk.bgHex('#1a2a2a').hex('#5eead4'),
48
+
49
+ // Project types
50
+ backendApi: chalk.bgHex('#2d2455').hex('#c4b5fd'),
51
+ frontendUi: chalk.bgHex('#2a1a2a').hex('#f9a8d4'),
52
+ fullstack: chalk.bgHex('#1a2a3a').hex('#7dd3fc'),
53
+ library: chalk.bgHex('#1a3a2a').hex('#6ee7b7'),
54
+ cli: chalk.bgHex('#1a2a2a').hex('#5eead4'),
55
+ dataml: chalk.bgHex('#2a2520').hex('#fbbf24'),
56
+ devopsInfra: chalk.bgHex('#1a1a2a').hex('#818cf8'),
57
+ };
58
+
59
+ // ── Badge-to-Data Lookup Maps ──────────────────────────────────
60
+
61
+ export const MODEL_BADGES = {
62
+ opus: badges.opus,
63
+ sonnet: badges.sonnet,
64
+ haiku: badges.haiku,
65
+ };
66
+
67
+ export const CATEGORY_BADGES = {
68
+ Backend: badges.backend,
69
+ Frontend: badges.frontend,
70
+ DevOps: badges.devops,
71
+ Quality: badges.quality,
72
+ Documentation: badges.docs,
73
+ 'Data / AI': badges.dataai,
74
+ };
75
+
76
+ export const STACK_BADGES = {
77
+ Python: badges.python,
78
+ 'Node.js / TypeScript': badges.node,
79
+ Java: badges.java,
80
+ 'C# / .NET': badges.csharp,
81
+ 'C / C++': badges.cpp,
82
+ Go: badges.go,
83
+ Rust: badges.rust,
84
+ PHP: badges.php,
85
+ Docker: badges.docker,
86
+ };
87
+
88
+ export const TYPE_BADGES = {
89
+ 'Backend / API': badges.backendApi,
90
+ 'Frontend / UI': badges.frontendUi,
91
+ 'Full-stack web application': badges.fullstack,
92
+ 'Library / Package': badges.library,
93
+ 'CLI tool': badges.cli,
94
+ 'Data / ML / AI': badges.dataml,
95
+ 'DevOps / Infrastructure': badges.devopsInfra,
96
+ };
97
+
98
+ // ── Universal Agent Metadata ───────────────────────────────────
99
+
100
+ const UNIVERSAL_AGENT_META = {
101
+ 'plan-reviewer': { model: 'opus', isolation: 'none' },
102
+ 'code-simplifier': { model: 'sonnet', isolation: 'worktree' },
103
+ 'test-writer': { model: 'sonnet', isolation: 'worktree' },
104
+ 'build-validator': { model: 'haiku', isolation: 'none' },
105
+ 'verify-app': { model: 'sonnet', isolation: 'worktree' },
106
+ };
107
+
108
+ // ── Badge Rendering ────────────────────────────────────────────
109
+
110
+ function capitalize(s) {
111
+ return s.charAt(0).toUpperCase() + s.slice(1);
112
+ }
113
+
114
+ export function badge(text, style) {
115
+ return style(` ${text} `);
116
+ }
117
+
118
+ export function renderBadge(text, mapping) {
119
+ const style = mapping[text];
120
+ if (style) return badge(text, style);
121
+ return text;
122
+ }
123
+
124
+ export function renderBadgeList(items, mapping) {
125
+ return items.map((item) => renderBadge(item, mapping)).join(' ');
126
+ }
127
+
128
+ export function renderAgentWithBadges(name) {
129
+ const meta = UNIVERSAL_AGENT_META[name] || AGENT_CATALOG[name];
130
+ if (!meta) return name;
131
+
132
+ let line = name.padEnd(20);
133
+ line += badge(capitalize(meta.model), MODEL_BADGES[meta.model]);
134
+ if (meta.isolation === 'worktree') {
135
+ line += ' ' + badge('worktree', badges.worktree);
136
+ }
137
+ return line;
138
+ }
139
+
140
+ // ── Structural Elements ────────────────────────────────────────
141
+
142
+ export const bar = dimColor('│');
143
+
144
+ export function banner(version) {
145
+ console.log();
146
+ console.log(` ${purple('▌')} ${white('WORCLAUDE')} ${dimColor(`v${version}`)}`);
4
147
  console.log();
5
- console.log(chalk.bold(text));
6
- console.log(chalk.dim('─'.repeat(text.length)));
7
148
  }
8
149
 
150
+ export function sectionHeader(title) {
151
+ console.log(` ${purple('▌')} ${white(title)}`);
152
+ }
153
+
154
+ export function divider(label) {
155
+ console.log(` ${dimColor(`─── ${label} ───`)}`);
156
+ }
157
+
158
+ export function barLine(text) {
159
+ console.log(` ${bar} ${text}`);
160
+ }
161
+
162
+ // ── Display Functions (backward-compatible signatures) ─────────
163
+
9
164
  export function success(text) {
10
- console.log(chalk.green(' ') + text);
165
+ console.log(` ${green('✓')} ${text}`);
11
166
  }
12
167
 
13
168
  export function error(text) {
14
- console.log(chalk.red(' ') + text);
169
+ console.log(` ${red('✗')} ${text}`);
15
170
  }
16
171
 
17
172
  export function info(text) {
18
- console.log(chalk.blue(' ') + text);
173
+ console.log(` ${blue('ℹ')} ${text}`);
19
174
  }
20
175
 
21
176
  export function warn(text) {
22
- console.log(chalk.yellow(' ') + text);
177
+ console.log(` ${yellow('⚠')} ${text}`);
23
178
  }
24
179
 
25
180
  export function dim(text) {
26
- console.log(chalk.dim(' ' + text));
181
+ console.log(dimColor(' ' + text));
27
182
  }
28
183
 
29
184
  export function newline() {
30
185
  console.log();
31
186
  }
32
-
33
- export function reviewBox(lines) {
34
- console.log();
35
- console.log(chalk.dim(' ─── Review Your Selections ───'));
36
- console.log();
37
- for (const line of lines) {
38
- console.log(' ' + line);
39
- }
40
- console.log();
41
- }