devlyn-cli 2.5.7 → 2.6.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/README.md CHANGED
@@ -27,7 +27,7 @@ If devlyn-cli saved you time, [give it a star](https://github.com/fysoul17/devly
27
27
  npx devlyn-cli
28
28
  ```
29
29
 
30
- That's it. The interactive installer handles everything. Claude Code config is installed by default; optional AI CLI instructions can be selected during install. Choose **Codex CLI (OpenAI)** to install `AGENTS.md` and the `devlyn:resolve`, `devlyn:ideate`, and `devlyn:design-ui` skills into `~/.codex/skills/`. In Codex, invoke them as skills with `$devlyn:resolve`, `$devlyn:ideate`, or `$devlyn:design-ui` rather than Claude Code slash commands. Run it again anytime to update.
30
+ That's it. The installer opens with a single **agent selector** — pick any combination of **Claude Code**, **Codex CLI**, **oh-my-pi (omp)**, **Pi**, Gemini, Cursor, Copilot, or Windsurf, and devlyn installs into every one you choose in a single pass. Claude Code and any agent already present on your machine are pre-checked, so the common case is just Enter. Skill-capable agents receive the `devlyn:resolve`, `devlyn:ideate`, and `devlyn:design-ui` skills in the directory each one loads from: Codex → `~/.codex/skills/`, while **omp and Pi share `~/.agents/skills/`** — the cross-agent standard both read — so the bundle is written there once, not duplicated per agent. In Codex / omp / Pi, invoke them as skills (`$devlyn:resolve`, `$devlyn:ideate`, `$devlyn:design-ui`); in Claude Code they're slash commands (`/devlyn:resolve`). Run it again anytime to update. (`npx devlyn-cli -y` installs the Claude core non-interactively; `npx devlyn-cli agents <cli>` adds one agent later.)
31
31
 
32
32
  ---
33
33
 
@@ -39,7 +39,7 @@ devlyn-cli turns Claude Code into a hands-free development pipeline. The pipelin
39
39
  ideate (optional) → resolve → ship
40
40
  ```
41
41
 
42
- Codex note: when the optional Codex install is selected, these workflows are installed as Codex skills. Use `$devlyn:ideate`, `$devlyn:resolve`, or `$devlyn:design-ui` in Codex; the `/devlyn:*` slash-command form is for Claude Code.
42
+ Non-Claude agents (Codex / omp / Pi): when one of these is selected, the workflows install as that agent's skills. Use `$devlyn:ideate`, `$devlyn:resolve`, or `$devlyn:design-ui`; the `/devlyn:*` slash-command form is for Claude Code.
43
43
 
44
44
  ### Step 1 (optional) — Plan with `/devlyn:ideate`
45
45
 
package/bin/devlyn.js CHANGED
@@ -11,6 +11,19 @@ const AGENTS_SOURCE = path.join(__dirname, '..', 'agents-config');
11
11
  const OPTIONAL_SKILLS_SOURCE = path.join(__dirname, '..', 'optional-skills');
12
12
  const PKG = require('../package.json');
13
13
 
14
+ // The devlyn skill bundle installed into every skill-capable agent's loader
15
+ // directory. Single source of truth so codex/omp/pi stay in lockstep — adding a
16
+ // skill here installs it everywhere. Standards skills (code-*, root-cause-*,
17
+ // ui-*) stay Claude-core-only, matching the pre-existing Codex bundle.
18
+ const DEVLYN_CORE_SKILLS = ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', '_shared'];
19
+
20
+ // Cross-agent shared skills directory read by BOTH oh-my-pi and Pi. Verified
21
+ // from the omp binary's skill-provider strings ("skills from .agents/skills —
22
+ // project walk-up + user home") and Pi's docs ("~/.agents/skills/"). Installing
23
+ // the bundle here once covers both agents — the de-dup driver writes it a single
24
+ // time even when both are selected.
25
+ const SHARED_AGENTS_SKILLS_DIR = path.join(os.homedir(), '.agents', 'skills');
26
+
14
27
  // Cross-CLI agent installation targets
15
28
  // Each entry maps a CLI tool to where its agent instructions should be placed
16
29
  const CLI_TARGETS = {
@@ -22,9 +35,32 @@ const CLI_TARGETS = {
22
35
  // Codex auto-loads skills from ~/.codex/skills/ (user-global). Same
23
36
  // SKILL.md format as Claude Code; descriptions must stay ≤1024 chars.
24
37
  skillsDir: path.join(os.homedir(), '.codex', 'skills'),
25
- skillsToInstall: ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', '_shared'],
38
+ skillsToInstall: DEVLYN_CORE_SKILLS,
26
39
  detect: () => fs.existsSync(path.join(process.cwd(), 'AGENTS.md')) || fs.existsSync(path.join(process.cwd(), '.codex')),
27
40
  },
41
+ omp: {
42
+ name: 'oh-my-pi (omp)',
43
+ instructionsFile: 'AGENTS.md',
44
+ baseInstructionsFile: 'AGENTS.md',
45
+ configDir: null, // omp discovers AGENTS.md at project root + user home
46
+ // omp loads skills from ~/.agents/skills (user home) — shared with Pi.
47
+ skillsDir: SHARED_AGENTS_SKILLS_DIR,
48
+ skillsToInstall: DEVLYN_CORE_SKILLS,
49
+ // Project-scoped only: machine-level ~/.omp must not auto-trigger a project
50
+ // AGENTS.md write via `npx devlyn-cli agents` in an unrelated repo.
51
+ detect: () => fs.existsSync(path.join(process.cwd(), '.omp')) || fs.existsSync(path.join(process.cwd(), '.agents')),
52
+ },
53
+ pi: {
54
+ name: 'Pi (earendil-works)',
55
+ instructionsFile: 'AGENTS.md',
56
+ baseInstructionsFile: 'AGENTS.md',
57
+ configDir: null, // Pi reads AGENTS.md / CLAUDE.md walking up from cwd
58
+ // Pi loads skills from ~/.agents/skills — shared with oh-my-pi.
59
+ skillsDir: SHARED_AGENTS_SKILLS_DIR,
60
+ skillsToInstall: DEVLYN_CORE_SKILLS,
61
+ // Project-scoped only (see omp): machine-level ~/.pi must not auto-trigger.
62
+ detect: () => fs.existsSync(path.join(process.cwd(), '.pi')) || fs.existsSync(path.join(process.cwd(), '.agents')),
63
+ },
28
64
  gemini: {
29
65
  name: 'Gemini CLI (Google)',
30
66
  instructionsFile: 'GEMINI.md',
@@ -363,9 +399,9 @@ function cleanManagedSkillDirs(sourceSkillsDir, targetSkillsDir) {
363
399
  return cleaned;
364
400
  }
365
401
 
366
- function multiSelect(items) {
402
+ function multiSelect(items, preselectedIndices = []) {
367
403
  return new Promise((resolve) => {
368
- const selected = new Set();
404
+ const selected = new Set(preselectedIndices.filter((i) => i >= 0 && i < items.length));
369
405
  let cursor = 0;
370
406
  let firstRender = true;
371
407
 
@@ -470,11 +506,15 @@ function installLocalSkill(skillName) {
470
506
  copyRecursive(src, dest, targetDir);
471
507
 
472
508
  // Mirror to every CLI skill-loader directory that already exists so optional
473
- // skills are picked up by Codex (and any future CLI with a skillsDir) the
474
- // same way required skills are. Existing dir, not new dir — we don't create
475
- // a Codex install just because someone opted into a Claude-side skill.
509
+ // skills are picked up by Codex/omp/Pi (and any future CLI with a skillsDir)
510
+ // the same way required skills are. Existing dir, not new dir — we don't
511
+ // create an agent install just because someone opted into a Claude-side skill.
512
+ // De-dup by directory: omp and Pi share ~/.agents/skills, so the mirror runs
513
+ // once per unique destination.
514
+ const mirrored = new Set();
476
515
  for (const cli of Object.values(CLI_TARGETS)) {
477
- if (!cli.skillsDir || !fs.existsSync(cli.skillsDir)) continue;
516
+ if (!cli.skillsDir || mirrored.has(cli.skillsDir) || !fs.existsSync(cli.skillsDir)) continue;
517
+ mirrored.add(cli.skillsDir);
478
518
  const cliDest = path.join(cli.skillsDir, skillName);
479
519
  if (fs.existsSync(cliDest)) fs.rmSync(cliDest, { recursive: true, force: true });
480
520
  copyRecursive(src, cliDest, cli.skillsDir);
@@ -556,94 +596,136 @@ function installSkillsForCLI(cliKey) {
556
596
  return copied;
557
597
  }
558
598
 
559
- function installAgentsForCLI(cliKey) {
599
+ // Strip devlyn's previously-appended "# Devlyn Agent Instructions" block from an
600
+ // instruction file (or the packaged base) before re-appending, so installs stay
601
+ // idempotent and never duplicate the block. Anchors on the LAST marker, not the
602
+ // first: the packaged AGENTS.md ships its own "# Devlyn Agent Instructions"
603
+ // section, so indexOf would truncate that real content — lastIndexOf targets the
604
+ // devlyn-appended block (always last) and preserves everything above it.
605
+ function stripManagedBlock(content) {
606
+ const marker = '# Devlyn Agent Instructions';
607
+ const markerIdx = content.lastIndexOf(marker);
608
+ if (markerIdx > 0) {
609
+ const sepIdx = content.lastIndexOf('\n---', markerIdx);
610
+ return content.slice(0, sepIdx > 0 ? sepIdx : markerIdx).trimEnd();
611
+ }
612
+ return content.trimEnd();
613
+ }
614
+
615
+ // One-line description of exactly what selecting a CLI target installs, so the
616
+ // unified selector stays honest — "Gemini" (instructions only) must not look
617
+ // identical to "Codex" (instructions + skills).
618
+ function targetDesc(cli) {
619
+ if (cli.skillsDir) {
620
+ return `${cli.instructionsFile} + devlyn skills → ${cli.skillsDir.replace(os.homedir(), '~')}`;
621
+ }
622
+ if (cli.configDir) return `Agent instructions → ${cli.configDir}/`;
623
+ return `${cli.instructionsFile} instructions only`;
624
+ }
625
+
626
+ // Install only the instruction file(s) for a CLI (no skills). Returns true when
627
+ // instruction content was written. Split from skills so the de-dup driver can
628
+ // write a shared destination (e.g. project AGENTS.md for codex/omp/pi) once.
629
+ function installInstructionsForCLI(cliKey) {
560
630
  const cli = CLI_TARGETS[cliKey];
561
631
  if (!cli) return false;
562
632
 
563
633
  const agents = fs.existsSync(AGENTS_SOURCE)
564
634
  ? fs.readdirSync(AGENTS_SOURCE).filter((f) => f.endsWith('.md'))
565
635
  : [];
636
+ if (agents.length === 0) return false;
566
637
 
567
- if (agents.length > 0) {
568
- log(`\n🤖 Installing agents for ${cli.name}...`, 'cyan');
638
+ log(`\n🤖 Installing agent instructions for ${cli.name}...`, 'cyan');
569
639
 
570
- if (cli.configDir) {
571
- // CLI supports an agents directory — copy agent files there
572
- const destDir = path.join(process.cwd(), cli.configDir);
573
- if (!fs.existsSync(destDir)) {
574
- fs.mkdirSync(destDir, { recursive: true });
575
- }
576
- for (const file of agents) {
577
- const src = path.join(AGENTS_SOURCE, file);
578
- const dest = path.join(destDir, file);
579
- fs.copyFileSync(src, dest);
580
- log(` → ${cli.configDir}/${file}`, 'dim');
581
- }
582
- } else {
583
- // CLI uses a single instructions file — append agent content
584
- const destFile = path.join(process.cwd(), cli.instructionsFile);
585
- const separator = '\n\n---\n\n# Devlyn Agent Instructions\n\n';
586
- const agentContent = agents.map((file) => {
587
- return fs.readFileSync(path.join(AGENTS_SOURCE, file), 'utf8');
588
- }).join('\n\n---\n\n');
589
-
590
- let existing = '';
591
- if (fs.existsSync(destFile)) {
592
- existing = fs.readFileSync(destFile, 'utf8');
593
- // Remove previous devlyn agent section if present
594
- const devlynMarker = '# Devlyn Agent Instructions';
595
- const markerIdx = existing.indexOf(devlynMarker);
596
- if (markerIdx > 0) {
597
- // Find the separator before the marker (---\n\n)
598
- const sepIdx = existing.lastIndexOf('---', markerIdx);
599
- existing = existing.slice(0, sepIdx > 0 ? sepIdx : markerIdx).trimEnd();
600
- }
601
- } else if (cli.baseInstructionsFile) {
602
- const baseInstructionsSrc = path.join(__dirname, '..', cli.baseInstructionsFile);
603
- if (fs.existsSync(baseInstructionsSrc)) {
604
- existing = fs.readFileSync(baseInstructionsSrc, 'utf8').trimEnd();
605
- }
640
+ if (cli.configDir) {
641
+ // CLI supports an agents directory — copy agent files there
642
+ const destDir = path.join(process.cwd(), cli.configDir);
643
+ if (!fs.existsSync(destDir)) {
644
+ fs.mkdirSync(destDir, { recursive: true });
645
+ }
646
+ for (const file of agents) {
647
+ const src = path.join(AGENTS_SOURCE, file);
648
+ const dest = path.join(destDir, file);
649
+ fs.copyFileSync(src, dest);
650
+ log(` → ${cli.configDir}/${file}`, 'dim');
651
+ }
652
+ } else {
653
+ // CLI uses a single instructions file — append agent content
654
+ const destFile = path.join(process.cwd(), cli.instructionsFile);
655
+ const separator = '\n\n---\n\n# Devlyn Agent Instructions\n\n';
656
+ const agentContent = agents.map((file) => {
657
+ return fs.readFileSync(path.join(AGENTS_SOURCE, file), 'utf8');
658
+ }).join('\n\n---\n\n');
659
+
660
+ let existing = '';
661
+ if (fs.existsSync(destFile)) {
662
+ existing = stripManagedBlock(fs.readFileSync(destFile, 'utf8'));
663
+ } else if (cli.baseInstructionsFile) {
664
+ const baseInstructionsSrc = path.join(__dirname, '..', cli.baseInstructionsFile);
665
+ if (fs.existsSync(baseInstructionsSrc)) {
666
+ existing = stripManagedBlock(fs.readFileSync(baseInstructionsSrc, 'utf8'));
606
667
  }
607
-
608
- fs.writeFileSync(destFile, existing + separator + agentContent + '\n');
609
- log(` → ${cli.instructionsFile} (agent instructions appended)`, 'dim');
610
668
  }
669
+
670
+ fs.writeFileSync(destFile, existing + separator + agentContent + '\n');
671
+ log(` → ${cli.instructionsFile} (agent instructions appended)`, 'dim');
611
672
  }
673
+ return true;
674
+ }
612
675
 
613
- // Always install global skills (Codex-style skill loader) regardless of
614
- // whether agent files were present. Agent install and skill install are
615
- // independent capabilities.
676
+ // Install both instructions and skills for a single CLI. Used by the
677
+ // `agents <cli>` command; multi-target paths use installSelectedCLITargets.
678
+ function installAgentsForCLI(cliKey) {
679
+ const instr = installInstructionsForCLI(cliKey);
616
680
  const skillsCopied = installSkillsForCLI(cliKey);
617
681
  if (skillsCopied > 0) {
618
682
  log(` → ${skillsCopied} skill${skillsCopied > 1 ? 's' : ''} installed (devlyn:resolve / devlyn:ideate / devlyn:design-ui / _shared)`, 'dim');
619
683
  }
684
+ return instr || skillsCopied > 0;
685
+ }
620
686
 
621
- return agents.length > 0 || skillsCopied > 0;
687
+ // Install instructions + skills for a set of selected CLI targets, writing each
688
+ // unique destination exactly once. omp and Pi share project AGENTS.md and the
689
+ // ~/.agents/skills dir, so selecting both does not duplicate writes — this is
690
+ // the researched "group the common" model: targets are the user's selection,
691
+ // destinations are the unit of work.
692
+ function installSelectedCLITargets(cliKeys) {
693
+ const instrDone = new Set();
694
+ const skillsDone = new Set();
695
+ let count = 0;
696
+ for (const cliKey of cliKeys) {
697
+ const cli = CLI_TARGETS[cliKey];
698
+ if (!cli) continue;
699
+ const instrKey = cli.configDir ? `dir:${cli.configDir}` : `file:${cli.instructionsFile}`;
700
+ if (!instrDone.has(instrKey)) {
701
+ installInstructionsForCLI(cliKey);
702
+ instrDone.add(instrKey);
703
+ }
704
+ if (cli.skillsDir && !skillsDone.has(cli.skillsDir)) {
705
+ const copied = installSkillsForCLI(cliKey);
706
+ if (copied > 0) {
707
+ log(` → ${copied} skill${copied > 1 ? 's' : ''} → ${cli.skillsDir.replace(os.homedir(), '~')}`, 'dim');
708
+ }
709
+ skillsDone.add(cli.skillsDir);
710
+ }
711
+ count++;
712
+ }
713
+ return count;
622
714
  }
623
715
 
624
716
  function installAgentsForAllDetected() {
625
717
  const detected = detectOtherCLIs();
626
718
  if (detected.length === 0) return 0;
627
-
628
- let installed = 0;
629
- for (const cliKey of detected) {
630
- if (installAgentsForCLI(cliKey)) installed++;
631
- }
632
- return installed;
719
+ return installSelectedCLITargets(detected);
633
720
  }
634
721
 
635
- async function init(skipPrompts = false) {
636
- showLogo();
637
- log('─'.repeat(44), 'dim');
638
-
639
- if (!fs.existsSync(CONFIG_SOURCE)) {
640
- log('❌ Config source not found', 'yellow');
641
- process.exit(1);
642
- }
643
-
644
- // Install core config
722
+ // Install the Claude Code core config: project .claude/ (skills, templates,
723
+ // settings, CLAUDE.md, .gitignore) plus global ~/.claude/settings.json tweaks.
724
+ // Extracted so the unified target selector installs it only when "Claude Code"
725
+ // is chosen. Behavior is unchanged from the previous unconditional core install.
726
+ function installClaudeCore() {
645
727
  const targetDir = getTargetDir();
646
- log('\n📁 Installing core config to .claude/', 'green');
728
+ log('\n📁 Installing Claude Code config to .claude/', 'green');
647
729
  const refreshed = cleanManagedSkillDirs(
648
730
  path.join(CONFIG_SOURCE, 'skills'),
649
731
  path.join(targetDir, 'skills'),
@@ -747,40 +829,55 @@ async function init(skipPrompts = false) {
747
829
  log(' → ~/.claude/settings.json (disabled adaptive thinking, enabled 1h prompt caching)', 'dim');
748
830
  }
749
831
 
750
- log('\n✅ Core config installed!', 'green');
832
+ log('\n✅ Claude Code config installed!', 'green');
833
+ }
834
+
835
+ async function init(skipPrompts = false) {
836
+ showLogo();
837
+ log('─'.repeat(44), 'dim');
751
838
 
752
- // Skip prompts if -y flag or non-interactive
839
+ if (!fs.existsSync(CONFIG_SOURCE)) {
840
+ log('❌ Config source not found', 'yellow');
841
+ process.exit(1);
842
+ }
843
+
844
+ // Non-interactive / -y: preserve historical behavior — install Claude core
845
+ // only. Multi-target selection needs a TTY; scripted runs add other agents
846
+ // via `npx devlyn-cli agents <cli>`.
753
847
  if (skipPrompts || !process.stdin.isTTY) {
848
+ installClaudeCore();
754
849
  log('\n💡 Add optional addons later: run `npx devlyn-cli` without -y', 'dim');
755
- log(' Add Codex instructions + skills later: run `npx devlyn-cli agents codex`', 'dim');
850
+ log(' Add another agent (Codex / omp / Pi / …) later: `npx devlyn-cli agents <cli>`', 'dim');
756
851
  log(`\n${COLORS.dim} Enjoying devlyn? Star it on GitHub — it helps others find it:${COLORS.reset}`);
757
852
  log(` ${COLORS.purple}→ https://github.com/fysoul17/devlyn-cli${COLORS.reset}\n`);
758
853
  return;
759
854
  }
760
855
 
761
- // Ask which non-Claude CLIs should receive instruction files.
762
- log('\n🤖 Optional AI CLI instructions:\n', 'blue');
763
- const cliOptions = Object.entries(CLI_TARGETS).map(([key, cli]) => {
764
- let desc;
765
- if (cli.configDir) {
766
- desc = `Install agents into ${cli.configDir}/`;
767
- } else if (cli.skillsDir) {
768
- desc = `Install ${cli.instructionsFile} + devlyn:resolve/devlyn:ideate/devlyn:design-ui skills (~/.codex/skills/; use $devlyn:* in Codex)`;
769
- } else {
770
- desc = `Install ${cli.instructionsFile}`;
771
- }
772
- return { key, name: cli.name, desc, type: 'cli' };
773
- });
774
- const selectedClis = await multiSelect(cliOptions);
775
- if (selectedClis.length > 0) {
776
- let agentsInstalled = 0;
777
- for (const selectedCli of selectedClis) {
778
- if (installAgentsForCLI(selectedCli.key)) agentsInstalled++;
779
- }
780
- log(` ✅ Agent instructions installed for ${agentsInstalled} CLI${agentsInstalled !== 1 ? 's' : ''}`, 'green');
781
- } else {
782
- log('💡 No additional CLI instructions selected', 'dim');
783
- log(' Run `npx devlyn-cli agents codex` later to install Codex AGENTS.md + devlyn skills', 'dim');
856
+ // Pick every agent to install devlyn into, up front. Claude Code is
857
+ // pre-checked (the happy path); every other agent is an explicit opt-in so a
858
+ // bare Enter never mutates a project's AGENTS.md or a shared skills dir for an
859
+ // agent the user didn't choose.
860
+ log('\n🎯 Select the agents to install devlyn into:\n', 'blue');
861
+ const targetOptions = [
862
+ { key: 'claude', name: 'Claude Code', desc: '.claude/ config — skills, templates, settings, CLAUDE.md', type: 'cli' },
863
+ ...Object.entries(CLI_TARGETS).map(([key, cli]) => ({ key, name: cli.name, desc: targetDesc(cli), type: 'cli' })),
864
+ ];
865
+ const preselected = [targetOptions.findIndex((o) => o.key === 'claude')];
866
+ const selectedKeys = (await multiSelect(targetOptions, preselected)).map((t) => t.key);
867
+
868
+ if (selectedKeys.length === 0) {
869
+ log('\n💡 No agents selected — nothing installed.', 'yellow');
870
+ log(' Run `npx devlyn-cli` again and pick at least one agent.\n', 'dim');
871
+ return;
872
+ }
873
+
874
+ if (selectedKeys.includes('claude')) {
875
+ installClaudeCore();
876
+ }
877
+ const cliKeys = selectedKeys.filter((k) => k !== 'claude');
878
+ if (cliKeys.length > 0) {
879
+ const installed = installSelectedCLITargets(cliKeys);
880
+ log(`\n ✅ Installed devlyn into ${installed} agent${installed !== 1 ? 's' : ''}`, 'green');
784
881
  }
785
882
 
786
883
  // Ask about optional addons (local skills + external packs)
@@ -806,9 +903,9 @@ async function init(skipPrompts = false) {
806
903
  function showHelp() {
807
904
  showLogo();
808
905
  log('Usage:', 'green');
809
- log(' npx devlyn-cli Install/update .claude config');
906
+ log(' npx devlyn-cli Install/update devlyn (pick agents: Claude / Codex / omp / Pi / …)');
810
907
  log(' npx devlyn-cli list List available skills & templates');
811
- log(' npx devlyn-cli -y Install without prompts');
908
+ log(' npx devlyn-cli -y Install Claude core without prompts');
812
909
  log(' npx devlyn-cli agents Install agents for detected CLIs');
813
910
  log(' npx devlyn-cli agents all Install agents for all supported CLIs');
814
911
  log(' npx devlyn-cli benchmark Run the resolve benchmark suite');
@@ -1065,12 +1162,10 @@ switch (command) {
1065
1162
  log('─'.repeat(44), 'dim');
1066
1163
  const subArg = args[1];
1067
1164
  if (subArg === 'all') {
1068
- // Install for all supported CLIs regardless of detection
1165
+ // Install for all supported CLIs regardless of detection. De-duped so the
1166
+ // shared ~/.agents/skills dir (omp + Pi) and shared AGENTS.md write once.
1069
1167
  log('\n🤖 Installing agents for all supported CLIs...', 'blue');
1070
- let count = 0;
1071
- for (const cliKey of Object.keys(CLI_TARGETS)) {
1072
- if (installAgentsForCLI(cliKey)) count++;
1073
- }
1168
+ const count = installSelectedCLITargets(Object.keys(CLI_TARGETS));
1074
1169
  log(`\n✅ Agents installed for ${count} CLI${count !== 1 ? 's' : ''}`, 'green');
1075
1170
  } else if (subArg && CLI_TARGETS[subArg]) {
1076
1171
  // Install for a specific CLI
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlyn-cli",
3
- "version": "2.5.7",
3
+ "version": "2.6.0",
4
4
  "description": "AI development toolkit for Claude Code — ideate, resolve, and ship with context engineering and agent orchestration",
5
5
  "homepage": "https://github.com/fysoul17/devlyn-cli#readme",
6
6
  "bin": {
@@ -242,10 +242,11 @@ if [ ! -e "optional-skills/devlyn:design-ui" ]; then
242
242
  else
243
243
  bad "devlyn:design-ui must not be installed as an optional addon"
244
244
  fi
245
- if grep -Fq "skillsToInstall: ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', '_shared']" bin/devlyn.js; then
246
- ok "Codex install includes devlyn:design-ui"
245
+ if grep -Fq "const DEVLYN_CORE_SKILLS = ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', '_shared'];" bin/devlyn.js \
246
+ && grep -Fq "skillsToInstall: DEVLYN_CORE_SKILLS" bin/devlyn.js; then
247
+ ok "Codex install includes devlyn:design-ui (via shared DEVLYN_CORE_SKILLS bundle)"
247
248
  else
248
- bad "Codex skillsToInstall must include devlyn:design-ui"
249
+ bad "Codex skillsToInstall must include devlyn:design-ui (shared DEVLYN_CORE_SKILLS bundle)"
249
250
  fi
250
251
  if ! grep -F "name: 'devlyn:design-ui'" bin/devlyn.js >/dev/null 2>&1; then
251
252
  ok "devlyn:design-ui is absent from OPTIONAL_ADDONS"