cli-five 0.2.7 → 0.2.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-five",
3
- "version": "0.2.7",
3
+ "version": "0.2.8",
4
4
  "description": "Code Like I'm Five — scaffold a 5-agent VS Code Copilot team into any repo.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "LICENSE"
17
17
  ],
18
18
  "scripts": {
19
- "test": "node --test tests/",
19
+ "test": "node --test tests/*.test.mjs",
20
20
  "smoke": "node bin/cli-five.mjs --help"
21
21
  },
22
22
  "engines": {
@@ -9,6 +9,7 @@ import { log } from '../util/log.mjs';
9
9
  // ── Known-good skill repos (verified working) ────────────────────────
10
10
  // awesome-copilot skills are discovered dynamically via `skills find`.
11
11
  const AWESOME_COPILOT_REPO = 'github/awesome-copilot';
12
+ const BREADCRUMB_BOX_WIDTH = 66;
12
13
 
13
14
  // Well-known skill repos matched by stack term → repo + suggested skill names (skills.sh)
14
15
  const SKILL_CATALOG = [
@@ -56,20 +57,17 @@ function projectUsesPnpm(cwd) {
56
57
  */
57
58
  function detectEnv(cwd) {
58
59
  const bundledBin = resolveSkillsBin();
60
+ const npmVer = whichVersion('npm');
59
61
  const pnpmVer = whichVersion('pnpm');
60
62
  const npxVer = whichVersion('npx');
61
63
  const projectPnpm = projectUsesPnpm(cwd);
62
64
 
63
- // ── Terse status lines ────────────────────────────────────────────
64
- log.info(`skills CLI (bundled) ${bundledBin ? '✓' : '✗ not resolved'}`);
65
- log.info(`pnpm ${pnpmVer ? `✓ ${pnpmVer}` : '✗ not found'}${projectPnpm ? ' (project uses pnpm)' : ''}`);
66
- log.info(`npx ${npxVer ? `✓ ${npxVer}` : '✗ not found'}`);
67
-
68
65
  // ── Priority: bundled > pnpm (if project or system) > npx ─────────
69
66
  if (bundledBin) {
70
67
  return {
71
68
  label: 'bundled skills CLI',
72
69
  runner: (skillsArgs) => ({ cmd: process.execPath, args: [bundledBin, ...skillsArgs] }),
70
+ npmVer,
73
71
  pnpmVer,
74
72
  npxVer,
75
73
  projectPnpm,
@@ -79,6 +77,7 @@ function detectEnv(cwd) {
79
77
  return {
80
78
  label: 'pnpm dlx',
81
79
  runner: (skillsArgs) => ({ cmd: 'pnpm', args: ['dlx', 'skills', ...skillsArgs] }),
80
+ npmVer,
82
81
  pnpmVer,
83
82
  npxVer,
84
83
  projectPnpm,
@@ -88,6 +87,7 @@ function detectEnv(cwd) {
88
87
  return {
89
88
  label: 'npx',
90
89
  runner: (skillsArgs) => ({ cmd: 'npx', args: ['-y', 'skills', ...skillsArgs] }),
90
+ npmVer,
91
91
  pnpmVer,
92
92
  npxVer,
93
93
  projectPnpm,
@@ -109,7 +109,7 @@ export async function skillDiscovery({ cwd, answers, args }) {
109
109
  }
110
110
 
111
111
  // ── Environment checks ──────────────────────────────────────────────
112
- const env = detectEnv(cwd);
112
+ let env = detectEnv(cwd);
113
113
 
114
114
  if (!env) {
115
115
  log.warn('Cannot run skills CLI: no bundled binary, pnpm, or npx found.');
@@ -119,7 +119,7 @@ export async function skillDiscovery({ cwd, answers, args }) {
119
119
  }
120
120
 
121
121
  // Offer pnpm if the user doesn't have it
122
- if (!env.pnpmVer) {
122
+ if (shouldOfferPnpmInstall(env)) {
123
123
  const { wantPnpm } = await prompts({
124
124
  type: 'confirm',
125
125
  name: 'wantPnpm',
@@ -130,10 +130,13 @@ export async function skillDiscovery({ cwd, answers, args }) {
130
130
  try {
131
131
  execFileSync('npm', ['install', '-g', 'pnpm'], { stdio: 'inherit' });
132
132
  log.ok('pnpm installed.');
133
+ env = detectEnv(cwd) || env;
133
134
  } catch (err) {
134
135
  log.warn(`pnpm install failed: ${err.message}`);
135
136
  }
136
137
  }
138
+ } else if (env.projectPnpm && !env.pnpmVer) {
139
+ log.dim(`pnpm not found. Using ${env.label} for skill discovery.`);
137
140
  }
138
141
 
139
142
  log.ok(`Running skills via ${env.label}`);
@@ -345,21 +348,9 @@ function printInstallSummary(results) {
345
348
 
346
349
  function printBreadcrumbs() {
347
350
  log.raw('');
348
- log.raw(kleur.bold().green(' ┌──────────────────────────────────────────────────────────────────┐'));
349
- log.raw(kleur.bold().green(' │') + kleur.bold(' 🎯 KEEP DISCOVERING — paste into VS Code / Copilot Chat: ') + kleur.bold().green('│'));
350
- log.raw(kleur.bold().green(' ├──────────────────────────────────────────────────────────────────┤'));
351
- log.raw(kleur.bold().green(' │') + ' ' + kleur.bold().green('│'));
352
- log.raw(kleur.bold().green(' │') + ' Add awesome-copilot MCP for ongoing search: ' + kleur.bold().green('│'));
353
- log.raw(kleur.bold().green(' │') + kleur.white(' Add to .vscode/mcp.json: ') + kleur.bold().green('│'));
354
- log.raw(kleur.bold().green(' │') + kleur.dim(' "awesome-copilot": { ') + kleur.bold().green('│'));
355
- log.raw(kleur.bold().green(' │') + kleur.dim(' "command": "npx", ') + kleur.bold().green('│'));
356
- log.raw(kleur.bold().green(' │') + kleur.dim(' "args": ["-y", "awesome-copilot-mcp"] ') + kleur.bold().green('│'));
357
- log.raw(kleur.bold().green(' │') + kleur.dim(' } ') + kleur.bold().green('│'));
358
- log.raw(kleur.bold().green(' │') + ' ' + kleur.bold().green('│'));
359
- log.raw(kleur.bold().green(' │') + ' Browse skills anytime: ' + kleur.bold().green('│'));
360
- log.raw(kleur.bold().green(' │') + kleur.white(' npx skills find ') + kleur.bold().green('│'));
361
- log.raw(kleur.bold().green(' │') + ' ' + kleur.bold().green('│'));
362
- log.raw(kleur.bold().green(' └──────────────────────────────────────────────────────────────────┘'));
351
+ for (const line of buildBreadcrumbBox()) {
352
+ log.raw(line);
353
+ }
363
354
  log.raw('');
364
355
  }
365
356
 
@@ -539,6 +530,44 @@ function pad(s, n) {
539
530
  return (s || '').padEnd(n);
540
531
  }
541
532
 
533
+ /**
534
+ * Offer pnpm installation only for pnpm workspaces that are currently missing pnpm
535
+ * and can bootstrap it via npm on PATH.
536
+ */
537
+ function shouldOfferPnpmInstall(env) {
538
+ return Boolean(env && env.projectPnpm && !env.pnpmVer && env.npmVer);
539
+ }
540
+
541
+ function buildBreadcrumbBox() {
542
+ const horizontalDivider = kleur.bold().green(` ├${'─'.repeat(BREADCRUMB_BOX_WIDTH + 2)}┤`);
543
+ const lines = [
544
+ formatBoxLine(kleur.bold(' KEEP DISCOVERING -- paste into VS Code / Copilot Chat:'), BREADCRUMB_BOX_WIDTH),
545
+ horizontalDivider,
546
+ formatBoxLine('', BREADCRUMB_BOX_WIDTH),
547
+ formatBoxLine(' Add awesome-copilot MCP for ongoing search:', BREADCRUMB_BOX_WIDTH),
548
+ formatBoxLine(kleur.white(' Add to .vscode/mcp.json:'), BREADCRUMB_BOX_WIDTH),
549
+ formatBoxLine(kleur.dim(' "awesome-copilot": {'), BREADCRUMB_BOX_WIDTH),
550
+ formatBoxLine(kleur.dim(' "command": "npx",'), BREADCRUMB_BOX_WIDTH),
551
+ formatBoxLine(kleur.dim(' "args": ["-y", "awesome-copilot-mcp"]'), BREADCRUMB_BOX_WIDTH),
552
+ formatBoxLine(kleur.dim(' }'), BREADCRUMB_BOX_WIDTH),
553
+ formatBoxLine('', BREADCRUMB_BOX_WIDTH),
554
+ formatBoxLine(' Browse skills anytime:', BREADCRUMB_BOX_WIDTH),
555
+ formatBoxLine(kleur.white(' npx skills find'), BREADCRUMB_BOX_WIDTH),
556
+ formatBoxLine('', BREADCRUMB_BOX_WIDTH),
557
+ ];
558
+
559
+ return [
560
+ kleur.bold().green(` ┌${'─'.repeat(BREADCRUMB_BOX_WIDTH + 2)}┐`),
561
+ ...lines,
562
+ kleur.bold().green(` └${'─'.repeat(BREADCRUMB_BOX_WIDTH + 2)}┘`),
563
+ ];
564
+ }
565
+
566
+ function formatBoxLine(content = '', width = BREADCRUMB_BOX_WIDTH) {
567
+ const paddingNeeded = Math.max(0, width - stripAnsi(content).length);
568
+ return `${kleur.bold().green(' │ ')}${content}${' '.repeat(paddingNeeded)}${kleur.bold().green(' │')}`;
569
+ }
570
+
542
571
  function runInteractive(cmd, args, cwd) {
543
572
  return new Promise((resolve) => {
544
573
  const proc = spawn(cmd, args, { cwd, stdio: 'inherit' });
@@ -567,3 +596,11 @@ function parseSkillRefs(output) {
567
596
  }
568
597
  return results;
569
598
  }
599
+
600
+ export const __testables = {
601
+ buildBreadcrumbBox,
602
+ detectEnv,
603
+ formatBoxLine,
604
+ shouldOfferPnpmInstall,
605
+ stripAnsi,
606
+ };