devlyn-cli 3.3.0 → 4.0.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 (46) hide show
  1. package/AGENTS.md +8 -8
  2. package/CLAUDE.md +17 -17
  3. package/README.md +47 -34
  4. package/bin/devlyn.js +104 -67
  5. package/bin/instruction-templates.json +26 -0
  6. package/config/skills/_shared/adapters/README.md +1 -1
  7. package/config/skills/_shared/adapters/omp.md +2 -2
  8. package/config/skills/_shared/archive_run.py +2 -2
  9. package/config/skills/_shared/codex-config.md +3 -3
  10. package/config/skills/_shared/engine-doctor.sh +1 -1
  11. package/config/skills/_shared/engine-preflight.md +5 -5
  12. package/config/skills/_shared/finish-gate.py +1 -1
  13. package/config/skills/_shared/resolve-bootstrap.py +2 -2
  14. package/config/skills/_shared/runtime-principles.md +6 -6
  15. package/config/skills/_shared/spec-verify-check.py +3 -3
  16. package/config/skills/_shared/task-complete.py +32 -14
  17. package/config/skills/{devlyn:design-ui → devlyn-design-ui}/SKILL.md +1 -1
  18. package/config/skills/{devlyn:engines → devlyn-engines}/SKILL.md +2 -2
  19. package/config/skills/{devlyn:ideate → devlyn-ideate}/SKILL.md +10 -10
  20. package/config/skills/{devlyn:ideate → devlyn-ideate}/references/elicitation.md +2 -2
  21. package/config/skills/{devlyn:ideate → devlyn-ideate}/references/from-spec-mode.md +2 -2
  22. package/config/skills/{devlyn:ideate → devlyn-ideate}/references/project-mode.md +5 -5
  23. package/config/skills/{devlyn:ideate → devlyn-ideate}/references/spec-template.md +1 -1
  24. package/config/skills/{devlyn:queue → devlyn-queue}/SKILL.md +6 -6
  25. package/config/skills/{devlyn:resolve → devlyn-resolve}/SKILL.md +4 -4
  26. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/free-form-mode.md +3 -3
  27. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/outer-loop.md +1 -1
  28. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/state-schema.md +1 -1
  29. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/task-completion.md +5 -5
  30. package/config/templates/prompt-templates.md +2 -2
  31. package/optional-skills/{devlyn:pencil-pull → devlyn-pencil-pull}/SKILL.md +1 -1
  32. package/optional-skills/{devlyn:pencil-push → devlyn-pencil-push}/SKILL.md +1 -1
  33. package/optional-skills/{devlyn:reap → devlyn-reap}/SKILL.md +5 -5
  34. package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/reap.sh +1 -1
  35. package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/scan.sh +1 -1
  36. package/package.json +3 -1
  37. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/decision.md +0 -0
  38. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/roadmap.md +0 -0
  39. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/vision.md +0 -0
  40. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/build-gate.md +0 -0
  41. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/cleanup.md +0 -0
  42. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/implement.md +0 -0
  43. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/plan.md +0 -0
  44. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/probe-derive.md +0 -0
  45. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/surface-close.md +0 -0
  46. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/verify.md +0 -0
package/bin/devlyn.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const crypto = require('crypto');
3
4
  const fs = require('fs');
4
5
  const path = require('path');
5
6
  const os = require('os');
@@ -14,35 +15,44 @@ const { updateInstructions, InstructionError } = require('./instructions');
14
15
  // The devlyn skill bundle installed into every skill-capable agent's loader
15
16
  // directory. Single source of truth so codex/omp/pi stay in lockstep — adding a
16
17
  // skill here installs it everywhere.
17
- const DEVLYN_CORE_SKILLS = ['devlyn:resolve', 'devlyn:ideate', 'devlyn:design-ui', 'devlyn:engines', 'devlyn:queue', '_shared'];
18
+ const DEVLYN_CORE_SKILLS = ['devlyn-resolve', 'devlyn-ideate', 'devlyn-design-ui', 'devlyn-engines', 'devlyn-queue', '_shared'];
19
+ // 4.0.0 renamed the colon-named skills to the Agent Skills standard (`[a-z0-9-]`, name ==
20
+ // folder): Git for Windows cannot check out a folder with ':' in its name. Installs from 3.x
21
+ // and earlier keep the old folder under either spelling (':' or npm's U+F03A extraction
22
+ // alias). Both are removed, and an optional skill that was installed is installed again
23
+ // under its new name in the same place.
24
+ const RENAMED_SKILLS = {
25
+ 'devlyn:resolve': 'devlyn-resolve',
26
+ 'devlyn:ideate': 'devlyn-ideate',
27
+ 'devlyn:design-ui': 'devlyn-design-ui',
28
+ 'devlyn:engines': 'devlyn-engines',
29
+ 'devlyn:queue': 'devlyn-queue',
30
+ 'devlyn:pencil-pull': 'devlyn-pencil-pull',
31
+ 'devlyn:pencil-push': 'devlyn-pencil-push',
32
+ 'devlyn:reap': 'devlyn-reap',
33
+ };
18
34
  const DEVLYN_SKILL_DIR_STAMP = '__DEVLYN_SKILL_DIR__';
19
35
  const DEVLYN_INSTALL_MARKER = '.devlyn-install.json';
20
36
 
21
- function logicalSkillName(name) {
22
- return name.replace(/\uF03A/g, ':');
23
- }
24
-
25
- function physicalSkillName(name) {
26
- return process.platform === 'win32' ? name.replace(/:/g, '\uF03A') : name;
37
+ // Every spelling a colon name can have on disk: its own and npm's U+F03A extraction alias.
38
+ function legacySkillPaths(root, name) {
39
+ return [...new Set([name, name.replace(/:/g, '\uF03A')])].map((spelling) => path.join(root, spelling));
27
40
  }
28
41
 
29
- function skillPath(root, name) {
30
- name = logicalSkillName(name);
31
- const aliases = [...new Set([name, name.replace(/:/g, '\uF03A')])];
32
- const entries = fs.existsSync(root) ? fs.readdirSync(root) : [];
33
- const found = aliases.filter((alias) => entries.includes(alias));
34
- if (found.length > 1) throw new Error(`Ambiguous skill aliases in ${root}: ${found.join(', ')}`);
35
- return path.join(root, found[0] || physicalSkillName(name));
36
- }
42
+ // SKILL.md of the pencil skills 0.6.0-0.7.1 installed under today's names, without the
43
+ // frontmatter the standard requires, hashed with LF line endings (a Windows checkout may hold
44
+ // CRLF). 3.x deleted these copies; 4.0 replaces them.
45
+ const PRE_STANDARD_SKILL_MD_SHA256 = new Set([
46
+ 'dfdd3d19ca676558bfa2bb3ce398181b0a081ab88d1feaf69c54c706a9671631', // devlyn-pencil-pull
47
+ '5f2e8b29609cbb2af72941579823bec598265ebf29cd3a375d6aa47d95b74354', // devlyn-pencil-push
48
+ ]);
37
49
 
38
- function preflightSkillDirs(source, target, names) {
39
- for (const name of names) {
40
- skillPath(source, name);
41
- skillPath(target, name);
42
- }
43
- for (const relative of DEPRECATED_DIRS) {
44
- skillPath(target, path.basename(relative));
45
- }
50
+ function isPreStandardCopy(dir) {
51
+ const skill = path.join(dir, 'SKILL.md');
52
+ return fs.lstatSync(dir, { throwIfNoEntry: false })?.isDirectory() === true
53
+ && fs.lstatSync(skill, { throwIfNoEntry: false })?.isFile() === true
54
+ && PRE_STANDARD_SKILL_MD_SHA256.has(crypto.createHash('sha256')
55
+ .update(fs.readFileSync(skill, 'utf8').replace(/\r\n/g, '\n')).digest('hex'));
46
56
  }
47
57
 
48
58
  // Cross-agent shared skills directory read by BOTH oh-my-pi and Pi. Verified
@@ -134,21 +144,17 @@ const DEPRECATED_DIRS = [
134
144
  // v0.7.x rename: devlyn-* → devlyn:*
135
145
  'skills/devlyn-clean',
136
146
  'skills/devlyn-design-system',
137
- 'skills/devlyn-design-ui',
138
147
  'skills/devlyn-discover-product',
139
148
  'skills/devlyn-evaluate',
140
149
  'skills/devlyn-feature-spec',
141
150
  'skills/devlyn-implement-ui',
142
151
  'skills/devlyn-product-spec',
143
152
  'skills/devlyn-recommend-features',
144
- 'skills/devlyn-resolve',
145
153
  'skills/devlyn-review',
146
154
  'skills/devlyn-team-design-ui',
147
155
  'skills/devlyn-team-resolve',
148
156
  'skills/devlyn-team-review',
149
157
  'skills/devlyn-update-docs',
150
- 'skills/devlyn-pencil-pull',
151
- 'skills/devlyn-pencil-push',
152
158
  // iter-0034 Phase 4 cutover: deleted user skills
153
159
  'skills/devlyn:auto-resolve',
154
160
  'skills/devlyn:browser-validate',
@@ -164,10 +170,6 @@ const DEPRECATED_DIRS = [
164
170
  'skills/devlyn:team-resolve',
165
171
  'skills/devlyn:team-review',
166
172
  'skills/devlyn:update-docs',
167
- // iter-0034 Phase 4 cutover: moved to optional-skills/. Force-removed on
168
- // upgrade so users only have them if they opt in via the interactive
169
- // installer (matches the pencil-pull / pencil-push pattern).
170
- 'skills/devlyn:reap',
171
173
  // 0221 Session 1: standards skills moved to optional-skills/ (opt-in).
172
174
  'skills/code-health-standards',
173
175
  'skills/code-review-standards',
@@ -238,9 +240,9 @@ const OPTIONAL_ADDONS = [
238
240
  { name: 'polar-billing-setup', desc: 'Polar usage-based / metered billing — correct setup + diagnose silent $0-billing failures', type: 'local' },
239
241
  { name: 'pyx-scan', desc: 'Check whether an AI agent skill is safe before installing', type: 'local' },
240
242
  { name: 'dokkit', desc: 'Document template filling for DOCX/HWPX — ingest, fill, review, export', type: 'local' },
241
- { name: 'devlyn:pencil-pull', desc: 'Pull Pencil designs into code with exact visual fidelity', type: 'local' },
242
- { name: 'devlyn:pencil-push', desc: 'Push codebase UI to Pencil canvas for design sync', type: 'local' },
243
- { name: 'devlyn:reap', desc: 'Safely reap orphaned MCP / codex / Superset child processes left behind by long Claude sessions', type: 'local' },
243
+ { name: 'devlyn-pencil-pull', desc: 'Pull Pencil designs into code with exact visual fidelity', type: 'local' },
244
+ { name: 'devlyn-pencil-push', desc: 'Push codebase UI to Pencil canvas for design sync', type: 'local' },
245
+ { name: 'devlyn-reap', desc: 'Safely reap orphaned MCP / codex / Superset child processes left behind by long Claude sessions', type: 'local' },
244
246
  { name: 'code-health-standards', desc: 'Maintainability standards — dead code, dependencies, complexity, naming, hygiene', type: 'local' },
245
247
  { name: 'code-review-standards', desc: 'Severity framework and approval criteria for reviewing a change', type: 'local' },
246
248
  { name: 'root-cause-analysis', desc: 'Evidence-first why-chain debugging to the actionable root cause', type: 'local' },
@@ -255,7 +257,7 @@ const OPTIONAL_ADDONS = [
255
257
  // Note: the Codex integration uses the local `codex` CLI binary (not MCP).
256
258
  // Install the CLI separately per https://platform.openai.com/docs/codex — the
257
259
  // pair/risk-probe routes fail closed when Codex is required but unavailable.
258
- { name: 'playwright', desc: 'Playwright MCP for browser testing — powers /devlyn:resolve BUILD_GATE browser tier', type: 'mcp', command: 'npx -y @playwright/mcp@latest' },
260
+ { name: 'playwright', desc: 'Playwright MCP for browser testing — powers /devlyn-resolve BUILD_GATE browser tier', type: 'mcp', command: 'npx -y @playwright/mcp@latest' },
259
261
  ];
260
262
 
261
263
  function log(msg, color = 'reset') {
@@ -349,8 +351,8 @@ function listContents() {
349
351
 
350
352
  function cleanupDeprecated(targetDir) {
351
353
  let removed = 0;
352
- const deprecated = DEPRECATED_DIRS.map((relPath) =>
353
- [relPath, skillPath(path.join(targetDir, 'skills'), path.basename(relPath))]);
354
+ const deprecated = DEPRECATED_DIRS.flatMap((relPath) =>
355
+ legacySkillPaths(path.join(targetDir, 'skills'), path.basename(relPath)).map((fullPath) => [relPath, fullPath]));
354
356
  for (const relPath of DEPRECATED_FILES) {
355
357
  const fullPath = path.join(targetDir, relPath);
356
358
  if (fs.existsSync(fullPath)) {
@@ -369,19 +371,35 @@ function cleanupDeprecated(targetDir) {
369
371
  return removed;
370
372
  }
371
373
 
374
+ // Remove every old spelling of the renamed skills in `skillsDir`, after the new core skills
375
+ // are in place. An optional skill found under an old spelling is installed under its new name
376
+ // before the old copy goes, so an interrupted migration never loses it. An unedited 0.6.0-0.7.1
377
+ // copy is refreshed in place. Any other folder under a 4.0 name is the user's own; `-y` leaves it.
378
+ function retireRenamedSkills(skillsDir) {
379
+ const optional = new Set(OPTIONAL_ADDONS.filter((addon) => addon.type === 'local').map((addon) => addon.name));
380
+ for (const [oldName, newName] of Object.entries(RENAMED_SKILLS)) {
381
+ const found = legacySkillPaths(skillsDir, oldName).filter((fullPath) => fs.existsSync(fullPath));
382
+ if (found.length > 0 && optional.has(newName)) installOptionalSkillInto(skillsDir, newName);
383
+ else if (isPreStandardCopy(path.join(skillsDir, newName))) refreshPreStandardCopy(skillsDir, newName);
384
+ for (const fullPath of found) {
385
+ fs.rmSync(fullPath, { recursive: true, force: true });
386
+ log(` ✕ ${path.basename(fullPath)}/ (renamed to ${newName})`, 'dim');
387
+ }
388
+ }
389
+ }
390
+
372
391
  function copyRecursive(src, dest, baseDir) {
373
392
  const stats = fs.statSync(src);
374
393
 
375
394
  if (stats.isDirectory()) {
376
395
  // Never install dev workspaces, even when running from source repo.
377
- if (UNSHIPPED_SKILL_DIRS.has(logicalSkillName(path.basename(src)))) return;
396
+ if (UNSHIPPED_SKILL_DIRS.has(path.basename(src))) return;
378
397
  if (!fs.existsSync(dest)) {
379
398
  fs.mkdirSync(dest, { recursive: true });
380
399
  }
381
400
 
382
401
  for (const item of fs.readdirSync(src)) {
383
- const name = src === path.join(CONFIG_SOURCE, 'skills') ? physicalSkillName(logicalSkillName(item)) : item;
384
- copyRecursive(path.join(src, item), path.join(dest, name), baseDir);
402
+ copyRecursive(path.join(src, item), path.join(dest, item), baseDir);
385
403
  }
386
404
  } else {
387
405
  const destDir = path.dirname(dest);
@@ -407,8 +425,8 @@ function assertCompleteSkillInstall(sourceSkillsDir, skillsDir, skillNames) {
407
425
  return fs.statSync(dest).isFile();
408
426
  }
409
427
  const missing = skillNames.filter((name) => {
410
- const src = skillPath(sourceSkillsDir, name);
411
- const dest = skillPath(skillsDir, name);
428
+ const src = path.join(sourceSkillsDir, name);
429
+ const dest = path.join(skillsDir, name);
412
430
  return !complete(src, dest) || (name !== '_shared' && !fs.existsSync(path.join(src, 'SKILL.md')));
413
431
  });
414
432
  if (missing.length > 0) {
@@ -475,6 +493,7 @@ function stampInstalledSkillDir(rootDir, skillDir) {
475
493
  const UNSHIPPED_SKILL_DIRS = new Set([
476
494
  'devlyn:auto-resolve-workspace',
477
495
  'devlyn:ideate-workspace',
496
+ 'devlyn-ideate-workspace',
478
497
  'preflight-workspace',
479
498
  'roadmap-archival-workspace',
480
499
  ]);
@@ -488,12 +507,11 @@ const UNSHIPPED_SKILL_DIRS = new Set([
488
507
  // no counterpart in source. Dev workspaces are skipped entirely.
489
508
  function cleanManagedSkillDirs(sourceSkillsDir, targetSkillsDir) {
490
509
  if (!fs.existsSync(sourceSkillsDir) || !fs.existsSync(targetSkillsDir)) return 0;
491
- preflightSkillDirs(sourceSkillsDir, targetSkillsDir, fs.readdirSync(sourceSkillsDir));
492
510
  let cleaned = 0;
493
511
  for (const entry of fs.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
494
512
  if (!entry.isDirectory()) continue;
495
- if (UNSHIPPED_SKILL_DIRS.has(logicalSkillName(entry.name))) continue;
496
- const targetPath = skillPath(targetSkillsDir, entry.name);
513
+ if (UNSHIPPED_SKILL_DIRS.has(entry.name)) continue;
514
+ const targetPath = path.join(targetSkillsDir, entry.name);
497
515
  if (fs.existsSync(targetPath)) {
498
516
  fs.rmSync(targetPath, { recursive: true, force: true });
499
517
  cleaned++;
@@ -596,11 +614,10 @@ function multiSelect(items, preselectedIndices = []) {
596
614
  }
597
615
 
598
616
  function installLocalSkill(skillName) {
599
- const src = skillPath(OPTIONAL_SKILLS_SOURCE, skillName);
617
+ const src = path.join(OPTIONAL_SKILLS_SOURCE, skillName);
600
618
  const targetDir = getTargetDir();
601
619
  const targets = new Set([path.join(targetDir, 'skills'), ...Object.values(CLI_TARGETS)
602
620
  .map((cli) => cli.skillsDir).filter((dir) => dir && fs.existsSync(dir))]);
603
- for (const target of targets) skillPath(target, skillName);
604
621
 
605
622
  if (!fs.existsSync(src)) {
606
623
  log(` ⚠️ Skill "${skillName}" not found`, 'yellow');
@@ -615,16 +632,38 @@ function installLocalSkill(skillName) {
615
632
  // create an agent install just because someone opted into a Claude-side skill.
616
633
  // De-dup by directory: omp and Pi share ~/.agents/skills, so the mirror runs
617
634
  // once per unique destination.
618
- for (const target of targets) {
619
- fs.rmSync(skillPath(target, skillName), { recursive: true, force: true });
620
- const dest = path.join(target, physicalSkillName(skillName));
621
- copyRecursive(src, dest, target);
622
- stampInstalledSkillDir(dest, dest);
623
- assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
624
- }
635
+ for (const target of targets) installOptionalSkillInto(target, skillName);
625
636
  return true;
626
637
  }
627
638
 
639
+ // One optional skill into one skill-loader directory, replacing any older copy of it. A
640
+ // copy under its name before the 4.0.0 rename goes only once the new one is complete.
641
+ function installOptionalSkillInto(target, skillName) {
642
+ const dest = path.join(target, skillName);
643
+ fs.rmSync(dest, { recursive: true, force: true });
644
+ copyRecursive(path.join(OPTIONAL_SKILLS_SOURCE, skillName), dest, target);
645
+ stampInstalledSkillDir(dest, dest);
646
+ assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
647
+ const oldName = Object.keys(RENAMED_SKILLS).find((name) => RENAMED_SKILLS[name] === skillName);
648
+ for (const fullPath of oldName ? legacySkillPaths(target, oldName) : []) {
649
+ fs.rmSync(fullPath, { recursive: true, force: true });
650
+ }
651
+ }
652
+
653
+ // A 0.6.0-0.7.1 copy is one SKILL.md, the file that identifies it. Renaming the new one over it is
654
+ // atomic, so an interrupted run leaves the old copy, which the retry finds again, or the new.
655
+ function refreshPreStandardCopy(target, skillName) {
656
+ const skill = path.join(target, skillName, 'SKILL.md');
657
+ const staged = `${skill}.${process.pid}.tmp`;
658
+ try {
659
+ fs.copyFileSync(path.join(OPTIONAL_SKILLS_SOURCE, skillName, 'SKILL.md'), staged);
660
+ fs.renameSync(staged, skill);
661
+ } finally {
662
+ fs.rmSync(staged, { force: true });
663
+ }
664
+ assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
665
+ }
666
+
628
667
  function installMcpServer(name, command) {
629
668
  try {
630
669
  log(`\n🔌 Installing MCP server: ${name}...`, 'cyan');
@@ -681,7 +720,6 @@ function installSkillsForCLI(cliKey) {
681
720
  fs.mkdirSync(cli.skillsDir, { recursive: true });
682
721
  }
683
722
  clearInstallMarker(cli.skillsDir);
684
- preflightSkillDirs(sourceSkillsDir, cli.skillsDir, cli.skillsToInstall);
685
723
 
686
724
  const removed = cleanupDeprecated(path.dirname(cli.skillsDir));
687
725
  if (removed > 0) {
@@ -690,14 +728,13 @@ function installSkillsForCLI(cliKey) {
690
728
 
691
729
  let copied = 0;
692
730
  for (const skillName of cli.skillsToInstall) {
693
- const src = skillPath(sourceSkillsDir, skillName);
694
- const previous = skillPath(cli.skillsDir, skillName);
695
- const dest = path.join(cli.skillsDir, physicalSkillName(skillName));
731
+ const src = path.join(sourceSkillsDir, skillName);
732
+ const dest = path.join(cli.skillsDir, skillName);
696
733
  if (!fs.existsSync(src)) continue;
697
734
  // Full replace per cleanManagedSkillDirs semantics: stale files in the
698
735
  // installed mirror would otherwise persist forever.
699
- if (fs.existsSync(previous)) {
700
- fs.rmSync(previous, { recursive: true, force: true });
736
+ if (fs.existsSync(dest)) {
737
+ fs.rmSync(dest, { recursive: true, force: true });
701
738
  }
702
739
  copyRecursive(src, dest, cli.skillsDir);
703
740
  stampInstalledSkillDir(dest, dest);
@@ -705,6 +742,7 @@ function installSkillsForCLI(cliKey) {
705
742
  log(` → ${cli.skillsDir.replace(os.homedir(), '~')}/${skillName}`, 'dim');
706
743
  }
707
744
  assertCompleteSkillInstall(sourceSkillsDir, cli.skillsDir, cli.skillsToInstall);
745
+ retireRenamedSkills(cli.skillsDir);
708
746
  writeInstallMarker(cli.skillsDir);
709
747
  return copied;
710
748
  }
@@ -803,6 +841,10 @@ function installClaudeCore() {
803
841
  log('\n📁 Installing Claude Code config to .claude/', 'green');
804
842
  if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
805
843
  clearInstallMarker(skillsDir);
844
+ const removed = cleanupDeprecated(targetDir);
845
+ if (removed > 0) {
846
+ log(`\n🧹 Cleaned up ${removed} deprecated file${removed > 1 ? 's' : ''}`, 'yellow');
847
+ }
806
848
  const refreshed = cleanManagedSkillDirs(
807
849
  path.join(CONFIG_SOURCE, 'skills'),
808
850
  skillsDir,
@@ -813,17 +855,12 @@ function installClaudeCore() {
813
855
  copyRecursive(CONFIG_SOURCE, targetDir, targetDir);
814
856
  assertCompleteSkillInstall(path.join(CONFIG_SOURCE, 'skills'), skillsDir, DEVLYN_CORE_SKILLS);
815
857
  for (const name of DEVLYN_CORE_SKILLS) {
816
- const dest = skillPath(skillsDir, name);
858
+ const dest = path.join(skillsDir, name);
817
859
  stampInstalledSkillDir(dest, dest);
818
860
  }
861
+ retireRenamedSkills(skillsDir);
819
862
  writeInstallMarker(skillsDir);
820
863
 
821
- // Remove deprecated files from previous versions
822
- const removed = cleanupDeprecated(targetDir);
823
- if (removed > 0) {
824
- log(`\n🧹 Cleaned up ${removed} deprecated file${removed > 1 ? 's' : ''}`, 'yellow');
825
- }
826
-
827
864
  // Keep installer-managed pipeline state and install metadata out of git.
828
865
  const gitignorePath = path.join(process.cwd(), '.gitignore');
829
866
  const gitignoreEntries = ['.devlyn/', '.claude/skills/.devlyn-install.json'];
@@ -149,10 +149,13 @@
149
149
  "4bbcb7fefd5366ed4151ce588e8482b747bdd85a7ecf795862a1c243218a162f",
150
150
  "4c3c4295f7cb794d0466f93886bf89b316382feafec68606385eb6b4c36eebbc",
151
151
  "4c4ee4b0948bc84b130c39b9846a5317ec76a6e3a8d95281a78e740192081662",
152
+ "4cae665b0a714cd7b20b59ef356b53e21dec1cf4679458de2a23b5077bceafff",
153
+ "4d42f91b8ff88de40921f825ec3a9aeeba30e85c7442ea2f8876b7c68555c39c",
152
154
  "4d756bc5c6a7c7ca1589efc6c340c75fb389699fe14d114aa211a29921e7effe",
153
155
  "4dcba526b49c91411d98925a7f638d990acea0f904ba8f143f195f7f99f85e33",
154
156
  "4e114313a6fbf27b89bebe7847f9aebb4ca2c61d23d167997efd459fa091e602",
155
157
  "4f6bf3fa8b8b706d234a7d5a3dda34e52ce1fcba68770aadf606c1717543bd5a",
158
+ "50fe2edc6da214c0ae4bfce43d8d210f98ff15da2d2f9bca88269dab9a4ed179",
156
159
  "53a8f3196d2320ba46bf1299bfabb38dff495218bb47b35a43ac5072308f9deb",
157
160
  "54cdf7a05afb97642d06a0d8284f17d51fed5ce0fdcdac949655c47734802893",
158
161
  "55ff3f39ef7341cfb1e8b50271821d574eeddc455461d6df317caa9c5b55827f",
@@ -200,6 +203,7 @@
200
203
  "8574b17a21f98a1cf558a7b88780eda07e8c39943168d0be49cbf4575edc23f1",
201
204
  "8694f41265b0f74734ce6801434fe255a4c2261501bcf13d37fe2e5b85a859e9",
202
205
  "870810ca9060fa45ef2eafd41c21282a21b9493cbec9632e2893556d07f3301f",
206
+ "890e3bdec4212b404e6c3675021922215d96705bb8def099a0d04ee65286585c",
203
207
  "892c26f1d54958622f5615734666f3b1aedb07e7a3ac225c416a5d3f1508312c",
204
208
  "8954f0209ecdac5a48f68c9ba5da95840ba745aabfb3751c6c216a06d809d1c9",
205
209
  "897d0d57a5ab67b082a3fbaf078ac3e9c724acb033b6df35b8959ae28c26bc93",
@@ -221,12 +225,15 @@
221
225
  "9e65019acb60433529160c929b840398fe6e5d72dcfd219cb79e69ba2d3cd5e9",
222
226
  "9f62c5b2a94e8b5e81528ccbdb6f78d147d14882108568b2cb3b7f85212694c0",
223
227
  "a02eb2d8d77553327fb77377dbfedeab059b2e68e0a33c469b9be54a5d55182b",
228
+ "a27ec7f5f0f0a2798e53695f20dc43fe28378d551fc060e2c772d0b522a336e5",
224
229
  "a3a15249968f64085b0cec8c0826688a4c3a1e4cf09b4879054baa086d5cbca1",
230
+ "a4da26a46ab83815d8ca5d747c68a907ad67dad485adbc6c863b982ee23b307e",
225
231
  "a5d6ff585d696254d6727cd1e6d7f8825881f90210eff744265e211ced715ea3",
226
232
  "a7ff913df40945a8a16b6632214020a368119ff665953d4a07ec58330b39f101",
227
233
  "aafc680a7ac73e100f4a17d2c4070190e68563f78614c39f4c2f58f1e291d9a9",
228
234
  "ab7f6d08d95cf1c22b7ec6e2e113158b7f767b73d8dde04fd1c3b33788922c93",
229
235
  "ac432d9179f245fec0beaf4010355613dcd6d476d8c9268c9cd430c98e73ff35",
236
+ "af3a498714fecb29ebdc1e43e5cc0425efaf1c290130e6913cabd75fe4c9cc67",
230
237
  "af77f01317665c50815eb3cfefe481519efcc9c8b81bac06fc110abf723c7868",
231
238
  "b3921b2317558010f99625aff30f3ed7a7a0dd4f37c586a6c4f9e1001e100134",
232
239
  "b3ac898e73c5b7923c1050bdbc36144dc6f4ad8bd9b83c8515394c418ef447ed",
@@ -265,12 +272,14 @@
265
272
  "d189a2002b4ef0e858acb734c4ed14f6af6f0b7bb250b178f65d439369a9149a",
266
273
  "d41692aa7577bbd85144fe73d111947f98e1304f4aceb891d712daa5907d550d",
267
274
  "d486a0b267c9425bde2cba737d802d2412a9b4b4b339e925500c1643327d2a3d",
275
+ "d5d8d02a1fb53cd28ed1c33e7184067b76defbfcc6cab9498b365dee35579013",
268
276
  "d75bdfc9cf08cfcb46214f69dac4f20b1a0a30b86d91127007fe587de9eaaeb0",
269
277
  "d768a149b5842e0d50eda941872188c8b29bcfb4b1aa0f4190fd4299f237fce0",
270
278
  "d8c50cb04441e6b568236bc6cd494b4de296dfc7e8b33cf96e7aeb94612df3b8",
271
279
  "da019f3edfb762b9216016a3ea9cf7211b2ba698a55ddda5cec2e478218b0288",
272
280
  "ddb200267b43942de3c061b449d08cc3f73441258b74816cfe13da091377b428",
273
281
  "dff43f9976c68267baf13f6a460f24af8e76be233c8440ac4684dc664106deae",
282
+ "e05f671dbf45aef8e7e4211195d46be8554c188d8df8899093359c414ffef2fe",
274
283
  "e237f12ed08800ccc4c6eb4ae57cdd59d61198711f6a3b02dcb668aa08fddb93",
275
284
  "e289dbab99dadb30d8315b32b8243154148c82955f238936bce4c1fe1c3ae525",
276
285
  "e2bac40f3ff0b39726f95caaa9b19aa3b21596f165ac7a9dc68ebf42681d58cb",
@@ -327,6 +336,7 @@
327
336
  "07d5c3147813efd7b5cf262709d4ee1643f962e5442f1f6e0f374d12d8d8c346",
328
337
  "07ec9e6c76821368b71110413acf43edd6e3c91b93e380eebed8f363a1915c7a",
329
338
  "080051ed34915b572971d9fc734b95ba7a7984f6e799a7286055d41a7aeaa3b6",
339
+ "0845abb4d59bce13c33c80b3292ba829bace0abe26d67a100a3bbc07e5db2b7d",
330
340
  "086b65c51d3342970968f4c3c6554a605b8740ed82809d26587cbd08f87d19f8",
331
341
  "0ab05e7ec571a8c899901514ab0655622817e8a77254ec2ec763fd78dad7f5bd",
332
342
  "0b45db686d97f33be4dc2b091ed259de534c2e3f97f8787574e722d2e86a0c1d",
@@ -366,6 +376,7 @@
366
376
  "18b3b8a84421d45d2f1cfd2d3bc936bb1635eb9fb426587ffb8b10ab224f5562",
367
377
  "18d96415b31677a8efe1d8e5d3246c62456fdee9f6371e8bc7817de6c062eb50",
368
378
  "1a7c6046a588a2608c146e5cb4403d943ef1d70f3779be1920a245442b48d7af",
379
+ "1af26e8d5dc8268e6372c1f2429aa2531a5780ae1956530a5b2978451121b4eb",
369
380
  "1b0c98cba21a4d616442124ed1e29259006398777a40fac41ba6b1894727c31f",
370
381
  "1c12577abd7db4345386fc7f24148dcbf6dabfe2ac89de3089fd27cca797aa0c",
371
382
  "1c14e388ba39b4953aa51b2b73a92d287027f6849e25ee8725d9e3ea51f78fa1",
@@ -400,6 +411,7 @@
400
411
  "2712e0f66046a30d80b9127688c67c73e809521dddedef1c10fad373d9a22114",
401
412
  "278bed8b8219c30412f09523678c050173875c14aa8545062e718669a4e76ee8",
402
413
  "27fe72a818b59177a7e692df7b02048eb7b8dd3b34e6e5a7717f2898df66ea00",
414
+ "284c050b55f96ae3c30d7f006aedd815f6b9601ceab010d569bc8622ee389fc3",
403
415
  "287fa116a8235756a8b0e28caa951a9ad981667443434e13dbfde3c23f1590e6",
404
416
  "28c5b42a58dca1a093576c8c35369c346caaf3963e14a4702ab3330d0d93e4d4",
405
417
  "2939f7a22fd12f3ca3fdd0a08fc52535cf25dae38b9b445aceed09dc331fc1de",
@@ -451,6 +463,7 @@
451
463
  "3ed5bcfca2cccfb1da4c181b27f9f44b6e53bd9c47c3b12990dd2e0c0ea51c32",
452
464
  "3ee9206330fd3a17b04c5a5d136b92c2abf4792831c3c706bca463db3668fbcc",
453
465
  "3ef2dde760ee9b50380364f83f368268a7c7a91cbdda6e0f629ebb09258cffaf",
466
+ "40464d8ac0f0395c447e7e502f262d5b05398716259b109e9ee7929fa1fa0105",
454
467
  "40cc672b94e87300a3b317348d99f4c939e38316b5b9aa7f81e95f2ccf848f10",
455
468
  "40fc22e1faeaa5a3be482334a312717b259f384e991c70e962ebef12d16120e7",
456
469
  "41323b1c12be256f604edf8c49ff505acbaa75b6a1b938b76286d5eebec1ce95",
@@ -474,6 +487,7 @@
474
487
  "477c7b699d1d2f66b63f04b30c3f78ddb4208b6346af58ed3dfa48d0568191cf",
475
488
  "47bac4155f4e8f3d8e9eedce9dc7fc8039d74bc18c23213979fbe55e786d71a1",
476
489
  "480420a6bcf31ae06cf1d4fcf34c24d949be485d2e13c2827b54331bec129053",
490
+ "4846658fe0742e27b3e2fccbbf222dbb4b23f47b5a31792249fc818e4ab4a659",
477
491
  "484dc70c61110ccc7f4ba15e1a3e010b6653920e1bd852b9de61625942f7b056",
478
492
  "485f3e37ba8a27b5027fd887f7baeea98a9099c6c7421d0e67e848d99905dc54",
479
493
  "4923c02dff4a34a20d2f104ec3b17311cf269330932b7cf29addb2f4369dd23c",
@@ -493,6 +507,7 @@
493
507
  "4dda6aecfc27e1a2889a30e12310abdc986d59cb8a7034ee9da3ee43c274dbf6",
494
508
  "4e9eb8208ccd7fc7ec25010de2d9fa3df043452f3fa808ec520e283b7ebfa156",
495
509
  "500e680bebe35b368080d6a1f6d39183323f519612fc9a776509ed0a2effcaf7",
510
+ "50bdeff1ac1044668a9692a076ff3bb504a42fdcb5abc6b41ccc4f05d57e9acd",
496
511
  "5111747b387a61c55834df35d588156d2ef1e72b5530f4c33453754e4d35fcf1",
497
512
  "5111bcf0edbbdb07d24a854086303f0f76a489fae171e7e441c1ec5dd62dd109",
498
513
  "51ac33809c5f2a830a22d1f67f0f38f3b1e26acb47673fb3f691deb93d7e42b4",
@@ -521,6 +536,7 @@
521
536
  "5b29b4f80abac3c4f6748a16f38b3bb72c61a639f65c845d0569a87f8f8a0cf0",
522
537
  "5b79e4d8b7b75485b9be3341ec0d49ef4754c31d527bfb170d5cec1af92539e8",
523
538
  "5b82f9020d952e89ccf8803bce90dd5b9c38b39fa59a629349c9d15cc14c9597",
539
+ "5d1183e08f92cb76c9fb41131742771101daebd65199b32050818a554e1ca185",
524
540
  "5d56b35a8b69aabc401bb42cf93f38d0eebcb1219f3a8a032e124c45d4e4b5d8",
525
541
  "5dec4f3f015629b0c29c802b30a9e373c570fca85bb3e260c0dac9662d2685cd",
526
542
  "5dfd01e48b8b3eb9d6eec85d68624b2af0dd0eeb483307865ef88b0fc916bc97",
@@ -571,11 +587,13 @@
571
587
  "76fdf7a80d7948c3007f8c54d3d710e8855a4227cefc915b8b07c0cdb299ceb0",
572
588
  "77071010460d4aa8c89841d31a6f18600b804496de948710953dbd6f6fffc816",
573
589
  "7766a3152ac33c155748ef3a7c9f500a7927870409100f55e1070f32f3667c54",
590
+ "793c5984744c21dfd4e8720e6319d61ff51ed30117c633b410024aaffa5ba048",
574
591
  "7aaf7227bd67650944c709f4143b6b9161834d391e5cfd4aaeea105f5e840594",
575
592
  "7af6e9be693d906ba1f942430f46b288176e418d35eefb869d3cc35570e3daff",
576
593
  "7b47b07e3062f3dc839f11397bc3265120e01a35b0fda094d738e28a6729e6c3",
577
594
  "7c0dacd603b4381539d6d3cf21f5fe625cf54b05eb4aa750fce658529b11d81d",
578
595
  "7c79750557c7d7156df6f135fb582ec09cc5f4987ac8e2af0f5113e42c117224",
596
+ "7d083db061c678113c12453eb73299caa9e207dd6418ad63406154fd330f3309",
579
597
  "7d2239e2cd4893c57ac0bf1373a2ef46419f614d0ce18a12df51222bc7795327",
580
598
  "7e529bc76ca67f2023890da254ef0b09596f7d924d1a4eb93c72262783dc6c18",
581
599
  "7eab506921c339be748106aa49468dba61ebd5a93aabc111621ccc8a9df52cc9",
@@ -608,6 +626,7 @@
608
626
  "8b417f12b79e7328ab8998bde222da856470a9fe2a5eff838c710d5fc66c850e",
609
627
  "8c89d0106c4051f7400670c2e83e6e3cfd95533b6d4d5f715124f28dbf41c37e",
610
628
  "8cff7c530e70102f9506012e91f305c306c56a6e68073f44aba74a827c87f679",
629
+ "8d0079bdee4cf4dc86c1cca826f81584fe2c97db04514fdd666167df8b509979",
611
630
  "8d3788c5b6eb17b9c58873208bbd36df9e4d3f969563165095a9dbb17d6c73fe",
612
631
  "8db63678d5615054312a1b20b4383358fb0ec689e6ba6cb0d650bb133a2e3452",
613
632
  "9101242a1f901484fc6110064e8e3ff4d4935c0ed69ee76003b57f8faf020765",
@@ -646,9 +665,11 @@
646
665
  "a292850ee0ceb0549d2e0cd50e48c8596594a509597f7210f59acbad7b45b475",
647
666
  "a2b7ab33386255547ecd6fce85b574d0354ef8e93d2bafb58e0cb77d7fee46b7",
648
667
  "a2cb37971755afd2b67d7f91f0857ce9e0dee7464e8e16d79c72070bc0e5a5ef",
668
+ "a309b4f27f5733300e22b7b04a044b6c30df42b7ad51c9d03369e8d8d3da2f79",
649
669
  "a35c41356dc27962435aafef3aaad9060a987aa83ea01992da6f7c26612468c9",
650
670
  "a53c30ff3a184f3192f7c528b6cfe50dd917e8185edf31d66c5c1454abcbea9d",
651
671
  "a5700a94e3e29b246bd9e2e29b2292287687cedbb69902f0b87ffbdcf382915c",
672
+ "a5de90a81e905134db282ef1cd50e17a684c8ea3217630147a47e2d5058edc3f",
652
673
  "a624d3a28050dba8ceaf70f1e940c8a2d090c22a63118eee78fc1df154a88e2b",
653
674
  "a6bc0c56d741f39aea2dbceb9bdd671326f12cebf7468c9c0f1c2d8ae733acc4",
654
675
  "a880b449c712d425187e6119469d719a96f01f7a603de858022b385f47f84602",
@@ -700,6 +721,7 @@
700
721
  "bf636ba39e22c5f06aa4e4850e7e4c16e87eab4439b1c18d7bc5313d6c402cc6",
701
722
  "bfd30b5a63ea49031fefd4a7c7804c72731ea35ea47884d0aa154e9aeb83e805",
702
723
  "bfe131637d915e23907418c2ea412a31e4a0e3e90da24c96b3d23380d7f3ceaa",
724
+ "c115b2ba9ec7abe2fa5261a5894e3c4899794b527f01ec682ae7fd2479199c08",
703
725
  "c150c40a04099d7b726eb8aecc8eec8e92f9f9248a6a5c15556c08000c4bd231",
704
726
  "c16ff4fe013ee2cef905d767a09305d9c1b2f778a06b439f0bc4645ac05bc82b",
705
727
  "c1c7e45fedafb97770b6726539694eb0668c70677c2031fe2db2bc8b5339423a",
@@ -750,6 +772,7 @@
750
772
  "d606733e95736b3e28e4dce4847b9775effc0944c4cd54c733400074991934ef",
751
773
  "d660cb0c81a7032782da6910fa05eafa83d36520ca5fdb95e7077da96216a3ca",
752
774
  "d6df42e35e82f71f257f5e79d038bf60a96e4965dbdb6bf08bc95c250754e5fe",
775
+ "d905ca576280d8a3d774bcadebfa57a9e22bdf7c1678ca9a548a1c25d643bf17",
753
776
  "d94088aedb09a1feb3ad388a69e242043b4a921ca8d76b4660d94e18e2836546",
754
777
  "d9547a0d7d9a97bffa84178d8ab73f3556f22c34120759d50eaa165c3ef0c318",
755
778
  "d9683236273b7f1759c34a892267b1c57d59dec57415bb762201a50b5925c408",
@@ -771,10 +794,12 @@
771
794
  "e031aef2a9adb0d0900e8269cf84c892f77b9938984e26df54826d1771861629",
772
795
  "e0cecf4838b7ba38afa78577855d5bf7fab251d3f1b1ba01c51c0f76b97aea90",
773
796
  "e160cfba0b71557be31005767ee845cab16d8c3a9bafde3d12ec31e8af0a1339",
797
+ "e16ebeb4c7803de28999a926918b42bec8d548691719827a636d84bc47941d80",
774
798
  "e2070548ec9a6244c569ef0b9a43c2e5dd99cf9c0fa4540a21fd0595b291e444",
775
799
  "e29fec7d54a144ea4867a3764fccdee6849d58d0f32eacdd56b2c317863fa1c6",
776
800
  "e2e0fc585321ee2013cccaed95d47c49fbca71b54c67abad9080d19d70e3c1a5",
777
801
  "e3b2f7be15a793f07cbe8e51aa2feeac51d4c67077b94e5e20bad2e14be07fc5",
802
+ "e468c242cc38c390f9679fca61f355848f58d51570249dfbb65b4f2e26a9dfb1",
778
803
  "e4bafa0f553e899b65f05feabf4cbe86d8246c57cbba745eb79e6aa858f40a6b",
779
804
  "e5160f4da789914e7f93694a55b9f80b8d06c3b0a126ff60f7627b0b418ae292",
780
805
  "e55a62fa1a993f5df1b44d1c55ef27915938d58a1c357604f3dc150f795672df",
@@ -789,6 +814,7 @@
789
814
  "e97387270720794226b6f1c657a0d5a961dbe06f8b873d6150b2f6246e54891f",
790
815
  "eabe96ee6140792fb6d7c188c3ce031cba5e2c6663aecdeb019d9d51d5a90a43",
791
816
  "eaf6d28a8d55fb98dcd4ddb84ab4645b4bf6d328a26811ddfcc1423b0b8ea604",
817
+ "ed4a68efc0af88b2192a06ae6b208992b617aae54b768d125b95442b987d517b",
792
818
  "edb34ab257304ae32df0fd5b8d4b41e982ff5e956231e56b05c7bbc1e8ae44ca",
793
819
  "edd212c7b0941068f6fd2d2051ad28651acd16ee12d9400f060553af6274ed2e",
794
820
  "eddcbf50ba05f1f633fea0ad0a384300670be2ec6b45f8cc4f622aaa7a431b88",
@@ -46,7 +46,7 @@ Keep each section to ≤ 8 lines. Adapters are deltas, not full prompts. If an a
46
46
 
47
47
  Adapters may add two more sections beyond the base format:
48
48
 
49
- - **`## Role eligibility`** — fixed ASCII fields, one per line: `executor: yes|no`, `pair_judge: yes|no`. Absent section = eligible for both roles (every adapter shipped before iter-0051). Present only when a backend is structurally unable or not certified for a role — e.g. a completion-only local model has no tool/file-edit access, so it declares `executor: no`. `_shared/engine-preflight.md` and `/devlyn:engines` read this section as a structured marker, not prose, so it validates mechanically.
49
+ - **`## Role eligibility`** — fixed ASCII fields, one per line: `executor: yes|no`, `pair_judge: yes|no`. Absent section = eligible for both roles (every adapter shipped before iter-0051). Present only when a backend is structurally unable or not certified for a role — e.g. a completion-only local model has no tool/file-edit access, so it declares `executor: no`. `_shared/engine-preflight.md` and `/devlyn-engines` read this section as a structured marker, not prose, so it validates mechanically.
50
50
  - **`## Invocation`** — for backends called over an API or CLI backends with non-default invocation contracts: the concrete request shape (endpoint, JSON body, timeout) and the adapter-declared availability probe (`_shared/engine-preflight.md#role-resolution`'s "an adapter may declare a different probe" hook).
51
51
 
52
52
  ## When to add a new adapter
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Identity
4
4
 
5
- You are the model running behind the `omp` CLI. Follow the canonical phase prompt exactly; this adapter exists so oh-my-pi can be selected as a first-class native task worker by `/devlyn:resolve`.
5
+ You are the model running behind the `omp` CLI. Follow the canonical phase prompt exactly; this adapter exists so oh-my-pi can be selected as a first-class native task worker by `/devlyn-resolve`.
6
6
 
7
7
  ## Output Discipline
8
8
 
@@ -10,4 +10,4 @@ Keep phase output machine-consumable and concise. Write the artifacts the canoni
10
10
 
11
11
  ## Fresh Context
12
12
 
13
- Each native `task` tool invocation with a fresh `context` is a fresh phase worker. Do not call `/devlyn:resolve` recursively from inside a phase prompt; execute only the phase body you received.
13
+ Each native `task` tool invocation with a fresh `context` is a fresh phase worker. Do not call `/devlyn-resolve` recursively from inside a phase prompt; execute only the phase body you received.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Archive devlyn:resolve run artifacts per references/pipeline-state.md#archive-contract.
2
+ """Archive devlyn-resolve run artifacts per references/pipeline-state.md#archive-contract.
3
3
 
4
4
  Usage:
5
5
  python3 scripts/archive_run.py [--devlyn-dir .devlyn]
@@ -67,7 +67,7 @@ PER_RUN_PATTERNS = (
67
67
  "verify.primary.timeout.json",
68
68
  "verify.pair.timeout.json",
69
69
  "finish-gate.summary.json",
70
- # iter-0033a/2026-04-30 archive-fix iter: NEW /devlyn:resolve emits
70
+ # iter-0033a/2026-04-30 archive-fix iter: NEW /devlyn-resolve emits
71
71
  # plan.md (PLAN output) + final-report.md (PHASE 6 render) +
72
72
  # cumulative.patch (cumulative diff). Smoke 2's archive listing
73
73
  # captured all three; archive_run.py was missing them because the
@@ -22,7 +22,7 @@ if [ ! -f "$CODEX_MONITORED_PATH" ]; then
22
22
  fi
23
23
  ```
24
24
 
25
- **Read-only critique / adversarial review / debate** (`/devlyn:resolve` VERIFY pair-mode, plus any future ideate read-only critique). Codex returns findings on stdout; the orchestrator writes files.
25
+ **Read-only critique / adversarial review / debate** (`/devlyn-resolve` VERIFY pair-mode, plus any future ideate read-only critique). Codex returns findings on stdout; the orchestrator writes files.
26
26
 
27
27
  ```bash
28
28
  DEVLYN_CODEX_PROMPT_FILE="<prompt-file>" CODEX_MONITORED_ISOLATED=1 CODEX_MONITORED_TIMEOUT_SEC=600 bash "$CODEX_MONITORED_PATH" \
@@ -32,7 +32,7 @@ DEVLYN_CODEX_PROMPT_FILE="<prompt-file>" CODEX_MONITORED_ISOLATED=1 CODEX_MONITO
32
32
  -
33
33
  ```
34
34
 
35
- **Workspace-write implementation** (`/devlyn:resolve` IMPLEMENT phase when `--engine codex` or `--engine auto` routes to Codex, plus codex-routed `/devlyn:ideate` phases):
35
+ **Workspace-write implementation** (`/devlyn-resolve` IMPLEMENT phase when `--engine codex` or `--engine auto` routes to Codex, plus codex-routed `/devlyn-ideate` phases):
36
36
 
37
37
  ```bash
38
38
  DEVLYN_CODEX_PROMPT_FILE="<prompt-file>" bash "$CODEX_MONITORED_PATH" \
@@ -76,6 +76,6 @@ The local Codex CLI, fronted by `codex-monitored.sh`, is the integration boundar
76
76
 
77
77
  ## Invocation from inside a skill prompt
78
78
 
79
- Skills write the invocation as a Bash command the runtime executes. Example shape from `/devlyn:resolve` PHASE 2 IMPLEMENT when routed to Codex:
79
+ Skills write the invocation as a Bash command the runtime executes. Example shape from `/devlyn-resolve` PHASE 2 IMPLEMENT when routed to Codex:
80
80
 
81
81
  > Run `DEVLYN_CODEX_PROMPT_FILE="<IMPLEMENT-prompt-file>" bash "$CODEX_MONITORED_PATH" --json -C <state.base_ref.repo_root> -s workspace-write -m <model_requested> -c sandbox_workspace_write.network_access=false -c model_reasoning_effort=xhigh -`. Capture stdout as the IMPLEMENT reply; non-zero exit → treat as subagent failure. The wrapper emits `[codex-monitored]` heartbeat and lifecycle lines on **stderr** — stdout stays clean for Codex output, so the orchestrator can parse the reply without filtering. Heartbeat-on-stderr keeps the orchestrator's combined-output stream non-silent (defeats the iter-0008 byte-watchdog kill) without polluting the codex-reply view of stdout. Do not pipe the wrapper; direct capture or file redirection preserves streaming and avoids the pipe-refusal exit.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env bash
2
- # engine-doctor.sh — read-only detection for /devlyn:engines' no-arg output.
2
+ # engine-doctor.sh — read-only detection for /devlyn-engines' no-arg output.
3
3
  #
4
4
  # WHY (iter-0050): users need to see what's actually on the machine, not
5
5
  # just what's pinned. This script never writes .devlyn/engines.json, never
@@ -1,10 +1,10 @@
1
1
  # Shared — Engine Pre-flight
2
2
 
3
- Used by `/devlyn:resolve` and `/devlyn:ideate`. One shared availability rule so every skill routes identically.
3
+ Used by `/devlyn-resolve` and `/devlyn-ideate`. One shared availability rule so every skill routes identically.
4
4
 
5
5
  ## Rule
6
6
 
7
- Each skill resolves the effective engine from its own SKILL.md default plus any explicit `--engine` flag passed by the user. `/devlyn:resolve` also computes the default VERIFY-pair route and conditional risk-probe requirements before the phase that needs the OTHER engine.
7
+ Each skill resolves the effective engine from its own SKILL.md default plus any explicit `--engine` flag passed by the user. `/devlyn-resolve` also computes the default VERIFY-pair route and conditional risk-probe requirements before the phase that needs the OTHER engine.
8
8
 
9
9
  Engine requirements have two classes:
10
10
 
@@ -15,11 +15,11 @@ Engine requirements have two classes:
15
15
 
16
16
  The orchestrator is the CLI the user opened. PLAN remains orchestrator-fixed. Resolve the legacy executor as explicit `--engine` > `cwd/.devlyn/engines.json` `executor` > the invoking skill's supported default. BUILD_GATE runs commands in the orchestrator without a separate model; risk-probe derivation keeps its existing route.
17
17
 
18
- The role profiles and `--role-config` below apply only to `/devlyn:resolve`; `/devlyn:ideate` keeps its existing `--engine` / legacy executor selection and never inherits worker/judge profiles. `role-config.py` is resolve’s deterministic resolver. Optional project `roles.worker`, `roles.primary_judge`, and `roles.pair_judge` each require `engine` and accept exact `model`/`effort`; `/devlyn:resolve --role-config <path>` supplies the same `{"roles": {...}}` object for one run. Each entry replaces the entire lower entry. Worker/primary precedence is run role > `--engine` engine-only > project role > legacy executor. Pair precedence is run role > project role > existing `pair_judge_priority`/binary complement. An absent primary profile follows the legacy executor, not the worker profile. No parent/global devlyn config lookup.
18
+ The role profiles and `--role-config` below apply only to `/devlyn-resolve`; `/devlyn-ideate` keeps its existing `--engine` / legacy executor selection and never inherits worker/judge profiles. `role-config.py` is resolve’s deterministic resolver. Optional project `roles.worker`, `roles.primary_judge`, and `roles.pair_judge` each require `engine` and accept exact `model`/`effort`; `/devlyn-resolve --role-config <path>` supplies the same `{"roles": {...}}` object for one run. Each entry replaces the entire lower entry. Worker/primary precedence is run role > `--engine` engine-only > project role > legacy executor. Pair precedence is run role > project role > existing `pair_judge_priority`/binary complement. An absent primary profile follows the legacy executor, not the worker profile. No parent/global devlyn config lookup.
19
19
 
20
20
  Worker controls IMPLEMENT, its included code/doc cleanup, and their repair rounds only; owner CLEANUP performs artifact/invariant checks without a worker dispatch; primary/pair control VERIFY only. OTHER must differ from the resolved primary engine. Explicit same-engine pair is invalid; a legacy priority list skips the primary. `--no-pair` does not dispatch or check availability of an unused pair. Configured pins remain explicit promises, while an unconfigured unavailable automatic OTHER records its solo reason.
21
21
 
22
- Before the first phase, `/devlyn:resolve` freezes resolution with `state-phase-write.py --freeze-roles`; subsequent status/dispatch reads that state instead of mutable config. `state.engine` stays the legacy executor. The resolver rejects statically unsupported selected channel fields before PLAN, and validates adapter names and the existing ASCII `executor: yes|no` / `pair_judge: yes|no` eligibility markers; absent eligibility section means both. Malformed configuration fails with `BLOCKED:invalid-engine-config`.
22
+ Before the first phase, `/devlyn-resolve` freezes resolution with `state-phase-write.py --freeze-roles`; subsequent status/dispatch reads that state instead of mutable config. `state.engine` stays the legacy executor. The resolver rejects statically unsupported selected channel fields before PLAN, and validates adapter names and the existing ASCII `executor: yes|no` / `pair_judge: yes|no` eligibility markers; absent eligibility section means both. Malformed configuration fails with `BLOCKED:invalid-engine-config`.
23
23
 
24
24
  Before each affected worker/judge, read `role-config.py --state .devlyn/pipeline.state.json --role <role> [--resolved-model <inherited-exact-model>]`. It returns source/channel, requested values and argv additions. Explicit Codex options require current version-matching native model metadata; refresh the native CLI's cache when missing/stale. Explicit Claude judge effort requires the adapter's version/exact-model capability declaration; model-only selection passes the exact ID without that effort declaration and validates the native result. No model/effort fields means unchanged phase defaults. Unknown support fails with `BLOCKED:unsupported-role-option`; no clamp, fallback or invented model identity. Native Agent workers reject unsupported explicit Claude fields without switching to a CLI worker. Show inherited/unknown values honestly; availability is not role fitness.
25
25
 
@@ -33,7 +33,7 @@ When a run or phase requires engine `<name>` (claude, codex, omp, or any adapter
33
33
 
34
34
  Never prompt the user mid-pipeline. Missing engines for explicit routes are BLOCKED states, not silent fallbacks. Missing OTHER engines for automatic escalations are reported solo-skips, not fallbacks — the auto route was never selected.
35
35
 
36
- Per-skill defaults: `/devlyn:resolve` uses Claude for IMPLEMENT when the orchestrator has Claude Code's native Agent primitive; Codex CLI and oh-my-pi orchestrators use their own fresh worker as the default route (Codex child process, omp native `task`) so implementation and independent verification retain structural isolation. PLAN uses the owner context; BUILD_GATE/CLEANUP use owner commands. VERIFY invokes the OTHER engine by default when available unless `--no-pair` was passed. `/devlyn:ideate` defaults to Claude; `--engine` selects the elicitation/normalization adapter, not an automatic cross-model challenge phase. Any future ideate read-only critique must follow `_shared/codex-config.md` isolation rules. Each SKILL.md flag block is source of truth for that skill's default.
36
+ Per-skill defaults: `/devlyn-resolve` uses Claude for IMPLEMENT when the orchestrator has Claude Code's native Agent primitive; Codex CLI and oh-my-pi orchestrators use their own fresh worker as the default route (Codex child process, omp native `task`) so implementation and independent verification retain structural isolation. PLAN uses the owner context; BUILD_GATE/CLEANUP use owner commands. VERIFY invokes the OTHER engine by default when available unless `--no-pair` was passed. `/devlyn-ideate` defaults to Claude; `--engine` selects the elicitation/normalization adapter, not an automatic cross-model challenge phase. Any future ideate read-only critique must follow `_shared/codex-config.md` isolation rules. Each SKILL.md flag block is source of truth for that skill's default.
37
37
 
38
38
  ## What a skill must report after a BLOCKED engine check
39
39