devlyn-cli 3.3.1 → 4.0.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.
Files changed (59) hide show
  1. package/AGENTS.md +8 -8
  2. package/CLAUDE.md +17 -17
  3. package/README.md +43 -30
  4. package/bin/devlyn.js +123 -105
  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 +15 -15
  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 +1 -1
  17. package/config/skills/{devlyn:design-ui → devlyn-design-ui}/SKILL.md +1 -1
  18. package/config/skills/{devlyn:engines → devlyn-engines}/SKILL.md +37 -11
  19. package/config/skills/{devlyn:ideate → devlyn-ideate}/SKILL.md +42 -24
  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 +39 -16
  25. package/config/skills/devlyn-queue/scripts/append.py +94 -0
  26. package/config/skills/{devlyn:resolve → devlyn-resolve}/SKILL.md +38 -19
  27. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/free-form-mode.md +3 -3
  28. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/outer-loop.md +1 -1
  29. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/state-schema.md +1 -1
  30. package/config/skills/{devlyn:resolve → devlyn-resolve}/references/task-completion.md +9 -2
  31. package/config/templates/prompt-templates.md +2 -2
  32. package/optional-skills/better-auth-setup/SKILL.md +24 -18
  33. package/optional-skills/{devlyn:pencil-pull → devlyn-pencil-pull}/SKILL.md +1 -1
  34. package/optional-skills/{devlyn:pencil-push → devlyn-pencil-push}/SKILL.md +1 -1
  35. package/optional-skills/{devlyn:reap → devlyn-reap}/SKILL.md +36 -5
  36. package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/reap.sh +1 -1
  37. package/optional-skills/{devlyn:reap → devlyn-reap}/scripts/scan.sh +1 -1
  38. package/optional-skills/dokkit/COMMANDS.md +4 -4
  39. package/optional-skills/dokkit/EXPORT.md +3 -3
  40. package/optional-skills/dokkit/FILLING.md +4 -4
  41. package/optional-skills/dokkit/IMAGE-SOURCING.md +2 -2
  42. package/optional-skills/dokkit/INGESTION.md +3 -3
  43. package/optional-skills/dokkit/PIPELINE.md +5 -4
  44. package/optional-skills/dokkit/SKILL.md +35 -5
  45. package/optional-skills/dokkit/STATE.md +1 -1
  46. package/optional-skills/dokkit/references/image-opportunity-heuristics.md +1 -1
  47. package/optional-skills/generate-skill/REFERENCE.md +10 -13
  48. package/optional-skills/generate-skill/SKILL.md +13 -5
  49. package/package.json +3 -1
  50. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/decision.md +0 -0
  51. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/roadmap.md +0 -0
  52. /package/config/skills/{devlyn:ideate → devlyn-ideate}/references/templates/vision.md +0 -0
  53. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/build-gate.md +0 -0
  54. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/cleanup.md +0 -0
  55. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/implement.md +0 -0
  56. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/plan.md +0 -0
  57. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/probe-derive.md +0 -0
  58. /package/config/skills/{devlyn:resolve → devlyn-resolve}/references/phases/surface-close.md +0 -0
  59. /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,57 @@ 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_SKILL_DIR_STAMP = '__DEVLYN_SKILL_DIR__';
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
+ };
19
34
  const DEVLYN_INSTALL_MARKER = '.devlyn-install.json';
20
35
 
21
- function logicalSkillName(name) {
22
- return name.replace(/\uF03A/g, ':');
36
+ // Every spelling a colon name can have on disk: its own and npm's U+F03A extraction alias.
37
+ function legacySkillPaths(root, name) {
38
+ return [...new Set([name, name.replace(/:/g, '\uF03A')])].map((spelling) => path.join(root, spelling));
23
39
  }
24
40
 
25
- function physicalSkillName(name) {
26
- return process.platform === 'win32' ? name.replace(/:/g, '\uF03A') : name;
27
- }
41
+ // SKILL.md of the pencil skills 0.6.0-0.7.1 installed under today's names, without the
42
+ // frontmatter the standard requires, hashed with LF line endings (a Windows checkout may hold
43
+ // CRLF). 3.x deleted these copies; 4.0 replaces them.
44
+ const PRE_STANDARD_SKILL_MD_SHA256 = new Set([
45
+ 'dfdd3d19ca676558bfa2bb3ce398181b0a081ab88d1feaf69c54c706a9671631', // devlyn-pencil-pull
46
+ '5f2e8b29609cbb2af72941579823bec598265ebf29cd3a375d6aa47d95b74354', // devlyn-pencil-push
47
+ ]);
28
48
 
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
- }
49
+ // Skills devlyn-cli shipped and later deleted without listing them, by SHA-256 of their only
50
+ // file, SKILL.md (LF endings): a user's own folder of the same name is never removed.
51
+ const RETIRED_SKILL_MD_SHA256 = {
52
+ // 0.2.0-1.15.0; deleted in iter-0034, it still routes agents to retired commands.
53
+ 'workflow-routing': new Set([
54
+ '7fec2bb20d808a873a975d64a6223e404bc5b328c800760bb77257ae6b2f467a',
55
+ '8bfdaeff2ce48d5a79b5653de3bbe0d56a61a7fd750ed3ad11149fec5a502ecf',
56
+ ]),
57
+ };
37
58
 
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
- }
59
+ // Whether `dir` is a real folder whose only file (dotfiles such as .DS_Store aside) is a SKILL.md
60
+ // with an LF-normalized SHA-256 in `hashes`: an unedited copy devlyn-cli shipped, not something
61
+ // the user added to or wrote.
62
+ function isShippedCopy(dir, hashes) {
63
+ const skill = path.join(dir, 'SKILL.md');
64
+ return fs.lstatSync(dir, { throwIfNoEntry: false })?.isDirectory() === true
65
+ && fs.readdirSync(dir).filter((name) => !name.startsWith('.')).length === 1
66
+ && fs.lstatSync(skill, { throwIfNoEntry: false })?.isFile() === true
67
+ && hashes.has(crypto.createHash('sha256')
68
+ .update(fs.readFileSync(skill, 'utf8').replace(/\r\n/g, '\n')).digest('hex'));
46
69
  }
47
70
 
48
71
  // Cross-agent shared skills directory read by BOTH oh-my-pi and Pi. Verified
@@ -134,21 +157,17 @@ const DEPRECATED_DIRS = [
134
157
  // v0.7.x rename: devlyn-* → devlyn:*
135
158
  'skills/devlyn-clean',
136
159
  'skills/devlyn-design-system',
137
- 'skills/devlyn-design-ui',
138
160
  'skills/devlyn-discover-product',
139
161
  'skills/devlyn-evaluate',
140
162
  'skills/devlyn-feature-spec',
141
163
  'skills/devlyn-implement-ui',
142
164
  'skills/devlyn-product-spec',
143
165
  'skills/devlyn-recommend-features',
144
- 'skills/devlyn-resolve',
145
166
  'skills/devlyn-review',
146
167
  'skills/devlyn-team-design-ui',
147
168
  'skills/devlyn-team-resolve',
148
169
  'skills/devlyn-team-review',
149
170
  'skills/devlyn-update-docs',
150
- 'skills/devlyn-pencil-pull',
151
- 'skills/devlyn-pencil-push',
152
171
  // iter-0034 Phase 4 cutover: deleted user skills
153
172
  'skills/devlyn:auto-resolve',
154
173
  'skills/devlyn:browser-validate',
@@ -164,10 +183,6 @@ const DEPRECATED_DIRS = [
164
183
  'skills/devlyn:team-resolve',
165
184
  'skills/devlyn:team-review',
166
185
  '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
186
  // 0221 Session 1: standards skills moved to optional-skills/ (opt-in).
172
187
  'skills/code-health-standards',
173
188
  'skills/code-review-standards',
@@ -238,9 +253,9 @@ const OPTIONAL_ADDONS = [
238
253
  { name: 'polar-billing-setup', desc: 'Polar usage-based / metered billing — correct setup + diagnose silent $0-billing failures', type: 'local' },
239
254
  { name: 'pyx-scan', desc: 'Check whether an AI agent skill is safe before installing', type: 'local' },
240
255
  { 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' },
256
+ { name: 'devlyn-pencil-pull', desc: 'Pull Pencil designs into code with exact visual fidelity', type: 'local' },
257
+ { name: 'devlyn-pencil-push', desc: 'Push codebase UI to Pencil canvas for design sync', type: 'local' },
258
+ { name: 'devlyn-reap', desc: 'Safely reap orphaned MCP / codex / Superset child processes left behind by long Claude sessions', type: 'local' },
244
259
  { name: 'code-health-standards', desc: 'Maintainability standards — dead code, dependencies, complexity, naming, hygiene', type: 'local' },
245
260
  { name: 'code-review-standards', desc: 'Severity framework and approval criteria for reviewing a change', type: 'local' },
246
261
  { name: 'root-cause-analysis', desc: 'Evidence-first why-chain debugging to the actionable root cause', type: 'local' },
@@ -255,7 +270,7 @@ const OPTIONAL_ADDONS = [
255
270
  // Note: the Codex integration uses the local `codex` CLI binary (not MCP).
256
271
  // Install the CLI separately per https://platform.openai.com/docs/codex — the
257
272
  // 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' },
273
+ { name: 'playwright', desc: 'Playwright MCP for browser testing — powers /devlyn-resolve BUILD_GATE browser tier', type: 'mcp', command: 'npx -y @playwright/mcp@latest' },
259
274
  ];
260
275
 
261
276
  function log(msg, color = 'reset') {
@@ -349,8 +364,8 @@ function listContents() {
349
364
 
350
365
  function cleanupDeprecated(targetDir) {
351
366
  let removed = 0;
352
- const deprecated = DEPRECATED_DIRS.map((relPath) =>
353
- [relPath, skillPath(path.join(targetDir, 'skills'), path.basename(relPath))]);
367
+ const deprecated = DEPRECATED_DIRS.flatMap((relPath) =>
368
+ legacySkillPaths(path.join(targetDir, 'skills'), path.basename(relPath)).map((fullPath) => [relPath, fullPath]));
354
369
  for (const relPath of DEPRECATED_FILES) {
355
370
  const fullPath = path.join(targetDir, relPath);
356
371
  if (fs.existsSync(fullPath)) {
@@ -366,22 +381,46 @@ function cleanupDeprecated(targetDir) {
366
381
  removed++;
367
382
  }
368
383
  }
384
+ for (const [name, hashes] of Object.entries(RETIRED_SKILL_MD_SHA256)) {
385
+ const fullPath = path.join(targetDir, 'skills', name);
386
+ if (isShippedCopy(fullPath, hashes)) {
387
+ fs.rmSync(fullPath, { recursive: true });
388
+ log(` ✕ skills/${name}/ (removed)`, 'dim');
389
+ removed++;
390
+ }
391
+ }
369
392
  return removed;
370
393
  }
371
394
 
395
+ // Remove every old spelling of the renamed skills in `skillsDir`, after the new core skills
396
+ // are in place. An optional skill found under an old spelling is installed under its new name
397
+ // before the old copy goes, so an interrupted migration never loses it. An unedited 0.6.0-0.7.1
398
+ // copy is refreshed in place. Any other folder under a 4.0 name is the user's own; `-y` leaves it.
399
+ function retireRenamedSkills(skillsDir) {
400
+ const optional = new Set(OPTIONAL_ADDONS.filter((addon) => addon.type === 'local').map((addon) => addon.name));
401
+ for (const [oldName, newName] of Object.entries(RENAMED_SKILLS)) {
402
+ const found = legacySkillPaths(skillsDir, oldName).filter((fullPath) => fs.existsSync(fullPath));
403
+ if (found.length > 0 && optional.has(newName)) installOptionalSkillInto(skillsDir, newName);
404
+ else if (isShippedCopy(path.join(skillsDir, newName), PRE_STANDARD_SKILL_MD_SHA256)) refreshPreStandardCopy(skillsDir, newName);
405
+ for (const fullPath of found) {
406
+ fs.rmSync(fullPath, { recursive: true, force: true });
407
+ log(` ✕ ${path.basename(fullPath)}/ (renamed to ${newName})`, 'dim');
408
+ }
409
+ }
410
+ }
411
+
372
412
  function copyRecursive(src, dest, baseDir) {
373
413
  const stats = fs.statSync(src);
374
414
 
375
415
  if (stats.isDirectory()) {
376
416
  // Never install dev workspaces, even when running from source repo.
377
- if (UNSHIPPED_SKILL_DIRS.has(logicalSkillName(path.basename(src)))) return;
417
+ if (UNSHIPPED_SKILL_DIRS.has(path.basename(src))) return;
378
418
  if (!fs.existsSync(dest)) {
379
419
  fs.mkdirSync(dest, { recursive: true });
380
420
  }
381
421
 
382
422
  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);
423
+ copyRecursive(path.join(src, item), path.join(dest, item), baseDir);
385
424
  }
386
425
  } else {
387
426
  const destDir = path.dirname(dest);
@@ -407,8 +446,8 @@ function assertCompleteSkillInstall(sourceSkillsDir, skillsDir, skillNames) {
407
446
  return fs.statSync(dest).isFile();
408
447
  }
409
448
  const missing = skillNames.filter((name) => {
410
- const src = skillPath(sourceSkillsDir, name);
411
- const dest = skillPath(skillsDir, name);
449
+ const src = path.join(sourceSkillsDir, name);
450
+ const dest = path.join(skillsDir, name);
412
451
  return !complete(src, dest) || (name !== '_shared' && !fs.existsSync(path.join(src, 'SKILL.md')));
413
452
  });
414
453
  if (missing.length > 0) {
@@ -436,45 +475,12 @@ function writeInstallMarker(skillsDir) {
436
475
  }
437
476
  }
438
477
 
439
- function shellDoubleQuoteDefault(value) {
440
- if (value.includes('\n')) {
441
- throw new Error(`Cannot stamp skill path with newline: ${value}`);
442
- }
443
- return value.replace(/[\\$"`}]/g, '\\$&');
444
- }
445
-
446
- function stampInstalledSkillDir(rootDir, skillDir) {
447
- // Stamp ONLY the assignment-default occurrence. The sentinel comparison
448
- // literal on the guard line must stay intact: stamping it makes the guard
449
- // compare the stamped default to itself and false-positive
450
- // BLOCKED:shared-dir-unresolved on every codex/omp run that executes the
451
- // block (CLAUDE_SKILL_DIR unset) — iter-0040 R3 latent finding.
452
- const assignmentStamp = '${CLAUDE_SKILL_DIR:-' + DEVLYN_SKILL_DIR_STAMP + '}';
453
- const stampedAssignment = '${CLAUDE_SKILL_DIR:-' + shellDoubleQuoteDefault(skillDir) + '}';
454
-
455
- function visit(current) {
456
- const stats = fs.statSync(current);
457
- if (stats.isDirectory()) {
458
- for (const item of fs.readdirSync(current)) {
459
- visit(path.join(current, item));
460
- }
461
- return;
462
- }
463
- if (path.extname(current) !== '.md') return;
464
-
465
- const content = fs.readFileSync(current, 'utf8');
466
- if (!content.includes(assignmentStamp)) return;
467
- fs.writeFileSync(current, content.split(assignmentStamp).join(stampedAssignment));
468
- }
469
-
470
- visit(rootDir);
471
- }
472
-
473
478
  // Dev artifacts that live under config/skills/ but must never ship or install.
474
479
  // Mirrors the `!` exclusions in package.json files[].
475
480
  const UNSHIPPED_SKILL_DIRS = new Set([
476
481
  'devlyn:auto-resolve-workspace',
477
482
  'devlyn:ideate-workspace',
483
+ 'devlyn-ideate-workspace',
478
484
  'preflight-workspace',
479
485
  'roadmap-archival-workspace',
480
486
  ]);
@@ -488,12 +494,11 @@ const UNSHIPPED_SKILL_DIRS = new Set([
488
494
  // no counterpart in source. Dev workspaces are skipped entirely.
489
495
  function cleanManagedSkillDirs(sourceSkillsDir, targetSkillsDir) {
490
496
  if (!fs.existsSync(sourceSkillsDir) || !fs.existsSync(targetSkillsDir)) return 0;
491
- preflightSkillDirs(sourceSkillsDir, targetSkillsDir, fs.readdirSync(sourceSkillsDir));
492
497
  let cleaned = 0;
493
498
  for (const entry of fs.readdirSync(sourceSkillsDir, { withFileTypes: true })) {
494
499
  if (!entry.isDirectory()) continue;
495
- if (UNSHIPPED_SKILL_DIRS.has(logicalSkillName(entry.name))) continue;
496
- const targetPath = skillPath(targetSkillsDir, entry.name);
500
+ if (UNSHIPPED_SKILL_DIRS.has(entry.name)) continue;
501
+ const targetPath = path.join(targetSkillsDir, entry.name);
497
502
  if (fs.existsSync(targetPath)) {
498
503
  fs.rmSync(targetPath, { recursive: true, force: true });
499
504
  cleaned++;
@@ -596,11 +601,10 @@ function multiSelect(items, preselectedIndices = []) {
596
601
  }
597
602
 
598
603
  function installLocalSkill(skillName) {
599
- const src = skillPath(OPTIONAL_SKILLS_SOURCE, skillName);
604
+ const src = path.join(OPTIONAL_SKILLS_SOURCE, skillName);
600
605
  const targetDir = getTargetDir();
601
606
  const targets = new Set([path.join(targetDir, 'skills'), ...Object.values(CLI_TARGETS)
602
607
  .map((cli) => cli.skillsDir).filter((dir) => dir && fs.existsSync(dir))]);
603
- for (const target of targets) skillPath(target, skillName);
604
608
 
605
609
  if (!fs.existsSync(src)) {
606
610
  log(` ⚠️ Skill "${skillName}" not found`, 'yellow');
@@ -615,16 +619,37 @@ function installLocalSkill(skillName) {
615
619
  // create an agent install just because someone opted into a Claude-side skill.
616
620
  // De-dup by directory: omp and Pi share ~/.agents/skills, so the mirror runs
617
621
  // 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
- }
622
+ for (const target of targets) installOptionalSkillInto(target, skillName);
625
623
  return true;
626
624
  }
627
625
 
626
+ // One optional skill into one skill-loader directory, replacing any older copy of it. A
627
+ // copy under its name before the 4.0.0 rename goes only once the new one is complete.
628
+ function installOptionalSkillInto(target, skillName) {
629
+ const dest = path.join(target, skillName);
630
+ fs.rmSync(dest, { recursive: true, force: true });
631
+ copyRecursive(path.join(OPTIONAL_SKILLS_SOURCE, skillName), dest, target);
632
+ assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
633
+ const oldName = Object.keys(RENAMED_SKILLS).find((name) => RENAMED_SKILLS[name] === skillName);
634
+ for (const fullPath of oldName ? legacySkillPaths(target, oldName) : []) {
635
+ fs.rmSync(fullPath, { recursive: true, force: true });
636
+ }
637
+ }
638
+
639
+ // A 0.6.0-0.7.1 copy is one SKILL.md, the file that identifies it. Renaming the new one over it is
640
+ // atomic, so an interrupted run leaves the old copy, which the retry finds again, or the new.
641
+ function refreshPreStandardCopy(target, skillName) {
642
+ const skill = path.join(target, skillName, 'SKILL.md');
643
+ const staged = `${skill}.${process.pid}.tmp`;
644
+ try {
645
+ fs.copyFileSync(path.join(OPTIONAL_SKILLS_SOURCE, skillName, 'SKILL.md'), staged);
646
+ fs.renameSync(staged, skill);
647
+ } finally {
648
+ fs.rmSync(staged, { force: true });
649
+ }
650
+ assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
651
+ }
652
+
628
653
  function installMcpServer(name, command) {
629
654
  try {
630
655
  log(`\n🔌 Installing MCP server: ${name}...`, 'cyan');
@@ -681,7 +706,6 @@ function installSkillsForCLI(cliKey) {
681
706
  fs.mkdirSync(cli.skillsDir, { recursive: true });
682
707
  }
683
708
  clearInstallMarker(cli.skillsDir);
684
- preflightSkillDirs(sourceSkillsDir, cli.skillsDir, cli.skillsToInstall);
685
709
 
686
710
  const removed = cleanupDeprecated(path.dirname(cli.skillsDir));
687
711
  if (removed > 0) {
@@ -690,21 +714,20 @@ function installSkillsForCLI(cliKey) {
690
714
 
691
715
  let copied = 0;
692
716
  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));
717
+ const src = path.join(sourceSkillsDir, skillName);
718
+ const dest = path.join(cli.skillsDir, skillName);
696
719
  if (!fs.existsSync(src)) continue;
697
720
  // Full replace per cleanManagedSkillDirs semantics: stale files in the
698
721
  // installed mirror would otherwise persist forever.
699
- if (fs.existsSync(previous)) {
700
- fs.rmSync(previous, { recursive: true, force: true });
722
+ if (fs.existsSync(dest)) {
723
+ fs.rmSync(dest, { recursive: true, force: true });
701
724
  }
702
725
  copyRecursive(src, dest, cli.skillsDir);
703
- stampInstalledSkillDir(dest, dest);
704
726
  copied++;
705
727
  log(` → ${cli.skillsDir.replace(os.homedir(), '~')}/${skillName}`, 'dim');
706
728
  }
707
729
  assertCompleteSkillInstall(sourceSkillsDir, cli.skillsDir, cli.skillsToInstall);
730
+ retireRenamedSkills(cli.skillsDir);
708
731
  writeInstallMarker(cli.skillsDir);
709
732
  return copied;
710
733
  }
@@ -803,6 +826,10 @@ function installClaudeCore() {
803
826
  log('\n📁 Installing Claude Code config to .claude/', 'green');
804
827
  if (!fs.existsSync(skillsDir)) fs.mkdirSync(skillsDir, { recursive: true });
805
828
  clearInstallMarker(skillsDir);
829
+ const removed = cleanupDeprecated(targetDir);
830
+ if (removed > 0) {
831
+ log(`\n🧹 Cleaned up ${removed} deprecated file${removed > 1 ? 's' : ''}`, 'yellow');
832
+ }
806
833
  const refreshed = cleanManagedSkillDirs(
807
834
  path.join(CONFIG_SOURCE, 'skills'),
808
835
  skillsDir,
@@ -812,18 +839,9 @@ function installClaudeCore() {
812
839
  }
813
840
  copyRecursive(CONFIG_SOURCE, targetDir, targetDir);
814
841
  assertCompleteSkillInstall(path.join(CONFIG_SOURCE, 'skills'), skillsDir, DEVLYN_CORE_SKILLS);
815
- for (const name of DEVLYN_CORE_SKILLS) {
816
- const dest = skillPath(skillsDir, name);
817
- stampInstalledSkillDir(dest, dest);
818
- }
842
+ retireRenamedSkills(skillsDir);
819
843
  writeInstallMarker(skillsDir);
820
844
 
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
845
  // Keep installer-managed pipeline state and install metadata out of git.
828
846
  const gitignorePath = path.join(process.cwd(), '.gitignore');
829
847
  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",
@@ -193,6 +196,7 @@
193
196
  "76517a747d47a3986b7181db4bbe5898ad3d6d82644ce39d8bf62edc998cecb9",
194
197
  "766c776b750cb84f084c49679fa377899693acf5f8d4e00751ca0fab97b4f030",
195
198
  "78690fc75b54ee634413fd0d5f6892692b2557e8838fbb8453f7bdd4b6519cc5",
199
+ "7a0d897737f705b0276b829df5b62fd9541e3897b872b2a2668f54e08b88dfff",
196
200
  "7aab816bfa43d73b4eb8bd9136242e8a4938f3610e7ad3c32e033b7ee3d28a1e",
197
201
  "7fde023e25658ff48c67b6708c5b6f383a6497a509946cb3bdc5da7d35e4f3f6",
198
202
  "8211bb3dcc9d7df40c393a4ef8e620526d84ded9faec25655cfa74801458515d",
@@ -200,6 +204,7 @@
200
204
  "8574b17a21f98a1cf558a7b88780eda07e8c39943168d0be49cbf4575edc23f1",
201
205
  "8694f41265b0f74734ce6801434fe255a4c2261501bcf13d37fe2e5b85a859e9",
202
206
  "870810ca9060fa45ef2eafd41c21282a21b9493cbec9632e2893556d07f3301f",
207
+ "890e3bdec4212b404e6c3675021922215d96705bb8def099a0d04ee65286585c",
203
208
  "892c26f1d54958622f5615734666f3b1aedb07e7a3ac225c416a5d3f1508312c",
204
209
  "8954f0209ecdac5a48f68c9ba5da95840ba745aabfb3751c6c216a06d809d1c9",
205
210
  "897d0d57a5ab67b082a3fbaf078ac3e9c724acb033b6df35b8959ae28c26bc93",
@@ -221,6 +226,7 @@
221
226
  "9e65019acb60433529160c929b840398fe6e5d72dcfd219cb79e69ba2d3cd5e9",
222
227
  "9f62c5b2a94e8b5e81528ccbdb6f78d147d14882108568b2cb3b7f85212694c0",
223
228
  "a02eb2d8d77553327fb77377dbfedeab059b2e68e0a33c469b9be54a5d55182b",
229
+ "a27ec7f5f0f0a2798e53695f20dc43fe28378d551fc060e2c772d0b522a336e5",
224
230
  "a3a15249968f64085b0cec8c0826688a4c3a1e4cf09b4879054baa086d5cbca1",
225
231
  "a4da26a46ab83815d8ca5d747c68a907ad67dad485adbc6c863b982ee23b307e",
226
232
  "a5d6ff585d696254d6727cd1e6d7f8825881f90210eff744265e211ced715ea3",
@@ -228,6 +234,7 @@
228
234
  "aafc680a7ac73e100f4a17d2c4070190e68563f78614c39f4c2f58f1e291d9a9",
229
235
  "ab7f6d08d95cf1c22b7ec6e2e113158b7f767b73d8dde04fd1c3b33788922c93",
230
236
  "ac432d9179f245fec0beaf4010355613dcd6d476d8c9268c9cd430c98e73ff35",
237
+ "af3a498714fecb29ebdc1e43e5cc0425efaf1c290130e6913cabd75fe4c9cc67",
231
238
  "af77f01317665c50815eb3cfefe481519efcc9c8b81bac06fc110abf723c7868",
232
239
  "b3921b2317558010f99625aff30f3ed7a7a0dd4f37c586a6c4f9e1001e100134",
233
240
  "b3ac898e73c5b7923c1050bdbc36144dc6f4ad8bd9b83c8515394c418ef447ed",
@@ -266,12 +273,14 @@
266
273
  "d189a2002b4ef0e858acb734c4ed14f6af6f0b7bb250b178f65d439369a9149a",
267
274
  "d41692aa7577bbd85144fe73d111947f98e1304f4aceb891d712daa5907d550d",
268
275
  "d486a0b267c9425bde2cba737d802d2412a9b4b4b339e925500c1643327d2a3d",
276
+ "d5d8d02a1fb53cd28ed1c33e7184067b76defbfcc6cab9498b365dee35579013",
269
277
  "d75bdfc9cf08cfcb46214f69dac4f20b1a0a30b86d91127007fe587de9eaaeb0",
270
278
  "d768a149b5842e0d50eda941872188c8b29bcfb4b1aa0f4190fd4299f237fce0",
271
279
  "d8c50cb04441e6b568236bc6cd494b4de296dfc7e8b33cf96e7aeb94612df3b8",
272
280
  "da019f3edfb762b9216016a3ea9cf7211b2ba698a55ddda5cec2e478218b0288",
273
281
  "ddb200267b43942de3c061b449d08cc3f73441258b74816cfe13da091377b428",
274
282
  "dff43f9976c68267baf13f6a460f24af8e76be233c8440ac4684dc664106deae",
283
+ "e05f671dbf45aef8e7e4211195d46be8554c188d8df8899093359c414ffef2fe",
275
284
  "e237f12ed08800ccc4c6eb4ae57cdd59d61198711f6a3b02dcb668aa08fddb93",
276
285
  "e289dbab99dadb30d8315b32b8243154148c82955f238936bce4c1fe1c3ae525",
277
286
  "e2bac40f3ff0b39726f95caaa9b19aa3b21596f165ac7a9dc68ebf42681d58cb",
@@ -328,6 +337,7 @@
328
337
  "07d5c3147813efd7b5cf262709d4ee1643f962e5442f1f6e0f374d12d8d8c346",
329
338
  "07ec9e6c76821368b71110413acf43edd6e3c91b93e380eebed8f363a1915c7a",
330
339
  "080051ed34915b572971d9fc734b95ba7a7984f6e799a7286055d41a7aeaa3b6",
340
+ "0845abb4d59bce13c33c80b3292ba829bace0abe26d67a100a3bbc07e5db2b7d",
331
341
  "086b65c51d3342970968f4c3c6554a605b8740ed82809d26587cbd08f87d19f8",
332
342
  "0ab05e7ec571a8c899901514ab0655622817e8a77254ec2ec763fd78dad7f5bd",
333
343
  "0b45db686d97f33be4dc2b091ed259de534c2e3f97f8787574e722d2e86a0c1d",
@@ -367,6 +377,7 @@
367
377
  "18b3b8a84421d45d2f1cfd2d3bc936bb1635eb9fb426587ffb8b10ab224f5562",
368
378
  "18d96415b31677a8efe1d8e5d3246c62456fdee9f6371e8bc7817de6c062eb50",
369
379
  "1a7c6046a588a2608c146e5cb4403d943ef1d70f3779be1920a245442b48d7af",
380
+ "1af26e8d5dc8268e6372c1f2429aa2531a5780ae1956530a5b2978451121b4eb",
370
381
  "1b0c98cba21a4d616442124ed1e29259006398777a40fac41ba6b1894727c31f",
371
382
  "1c12577abd7db4345386fc7f24148dcbf6dabfe2ac89de3089fd27cca797aa0c",
372
383
  "1c14e388ba39b4953aa51b2b73a92d287027f6849e25ee8725d9e3ea51f78fa1",
@@ -401,6 +412,7 @@
401
412
  "2712e0f66046a30d80b9127688c67c73e809521dddedef1c10fad373d9a22114",
402
413
  "278bed8b8219c30412f09523678c050173875c14aa8545062e718669a4e76ee8",
403
414
  "27fe72a818b59177a7e692df7b02048eb7b8dd3b34e6e5a7717f2898df66ea00",
415
+ "284c050b55f96ae3c30d7f006aedd815f6b9601ceab010d569bc8622ee389fc3",
404
416
  "287fa116a8235756a8b0e28caa951a9ad981667443434e13dbfde3c23f1590e6",
405
417
  "28c5b42a58dca1a093576c8c35369c346caaf3963e14a4702ab3330d0d93e4d4",
406
418
  "2939f7a22fd12f3ca3fdd0a08fc52535cf25dae38b9b445aceed09dc331fc1de",
@@ -444,6 +456,7 @@
444
456
  "3ab5f6021b62f21624e3eed505cc6849c66972d01ac67337533cff0e81dc9dc8",
445
457
  "3afe3870751373b6059ebddfb6c179ab70b7a1f3a9299555ecf5ea47c2970422",
446
458
  "3b5bf36be5ad797443dc5c1c407684c6f86b28894ca9a975a3fb5c18b3066fbb",
459
+ "3ba1747fbe0662a0aa9c42e6e442748f013a8e90febc330e2854ab9ad841dd77",
447
460
  "3cfc73fecd13c54fd400f5b04b2c4113cc776c102f6f09eaa401252276818e17",
448
461
  "3d321af4f48efc92735cc8772cf092f15789ac63e1e20f857ba20c0532453eac",
449
462
  "3def30362c96b6238021ab82e6f2ebd2974005ca94a84008f43528aaa7eb435f",
@@ -452,6 +465,7 @@
452
465
  "3ed5bcfca2cccfb1da4c181b27f9f44b6e53bd9c47c3b12990dd2e0c0ea51c32",
453
466
  "3ee9206330fd3a17b04c5a5d136b92c2abf4792831c3c706bca463db3668fbcc",
454
467
  "3ef2dde760ee9b50380364f83f368268a7c7a91cbdda6e0f629ebb09258cffaf",
468
+ "40464d8ac0f0395c447e7e502f262d5b05398716259b109e9ee7929fa1fa0105",
455
469
  "40cc672b94e87300a3b317348d99f4c939e38316b5b9aa7f81e95f2ccf848f10",
456
470
  "40fc22e1faeaa5a3be482334a312717b259f384e991c70e962ebef12d16120e7",
457
471
  "41323b1c12be256f604edf8c49ff505acbaa75b6a1b938b76286d5eebec1ce95",
@@ -475,6 +489,7 @@
475
489
  "477c7b699d1d2f66b63f04b30c3f78ddb4208b6346af58ed3dfa48d0568191cf",
476
490
  "47bac4155f4e8f3d8e9eedce9dc7fc8039d74bc18c23213979fbe55e786d71a1",
477
491
  "480420a6bcf31ae06cf1d4fcf34c24d949be485d2e13c2827b54331bec129053",
492
+ "4846658fe0742e27b3e2fccbbf222dbb4b23f47b5a31792249fc818e4ab4a659",
478
493
  "484dc70c61110ccc7f4ba15e1a3e010b6653920e1bd852b9de61625942f7b056",
479
494
  "485f3e37ba8a27b5027fd887f7baeea98a9099c6c7421d0e67e848d99905dc54",
480
495
  "4923c02dff4a34a20d2f104ec3b17311cf269330932b7cf29addb2f4369dd23c",
@@ -494,6 +509,7 @@
494
509
  "4dda6aecfc27e1a2889a30e12310abdc986d59cb8a7034ee9da3ee43c274dbf6",
495
510
  "4e9eb8208ccd7fc7ec25010de2d9fa3df043452f3fa808ec520e283b7ebfa156",
496
511
  "500e680bebe35b368080d6a1f6d39183323f519612fc9a776509ed0a2effcaf7",
512
+ "50bdeff1ac1044668a9692a076ff3bb504a42fdcb5abc6b41ccc4f05d57e9acd",
497
513
  "5111747b387a61c55834df35d588156d2ef1e72b5530f4c33453754e4d35fcf1",
498
514
  "5111bcf0edbbdb07d24a854086303f0f76a489fae171e7e441c1ec5dd62dd109",
499
515
  "51ac33809c5f2a830a22d1f67f0f38f3b1e26acb47673fb3f691deb93d7e42b4",
@@ -522,6 +538,7 @@
522
538
  "5b29b4f80abac3c4f6748a16f38b3bb72c61a639f65c845d0569a87f8f8a0cf0",
523
539
  "5b79e4d8b7b75485b9be3341ec0d49ef4754c31d527bfb170d5cec1af92539e8",
524
540
  "5b82f9020d952e89ccf8803bce90dd5b9c38b39fa59a629349c9d15cc14c9597",
541
+ "5d1183e08f92cb76c9fb41131742771101daebd65199b32050818a554e1ca185",
525
542
  "5d56b35a8b69aabc401bb42cf93f38d0eebcb1219f3a8a032e124c45d4e4b5d8",
526
543
  "5dec4f3f015629b0c29c802b30a9e373c570fca85bb3e260c0dac9662d2685cd",
527
544
  "5dfd01e48b8b3eb9d6eec85d68624b2af0dd0eeb483307865ef88b0fc916bc97",
@@ -572,11 +589,13 @@
572
589
  "76fdf7a80d7948c3007f8c54d3d710e8855a4227cefc915b8b07c0cdb299ceb0",
573
590
  "77071010460d4aa8c89841d31a6f18600b804496de948710953dbd6f6fffc816",
574
591
  "7766a3152ac33c155748ef3a7c9f500a7927870409100f55e1070f32f3667c54",
592
+ "793c5984744c21dfd4e8720e6319d61ff51ed30117c633b410024aaffa5ba048",
575
593
  "7aaf7227bd67650944c709f4143b6b9161834d391e5cfd4aaeea105f5e840594",
576
594
  "7af6e9be693d906ba1f942430f46b288176e418d35eefb869d3cc35570e3daff",
577
595
  "7b47b07e3062f3dc839f11397bc3265120e01a35b0fda094d738e28a6729e6c3",
578
596
  "7c0dacd603b4381539d6d3cf21f5fe625cf54b05eb4aa750fce658529b11d81d",
579
597
  "7c79750557c7d7156df6f135fb582ec09cc5f4987ac8e2af0f5113e42c117224",
598
+ "7d083db061c678113c12453eb73299caa9e207dd6418ad63406154fd330f3309",
580
599
  "7d2239e2cd4893c57ac0bf1373a2ef46419f614d0ce18a12df51222bc7795327",
581
600
  "7e529bc76ca67f2023890da254ef0b09596f7d924d1a4eb93c72262783dc6c18",
582
601
  "7eab506921c339be748106aa49468dba61ebd5a93aabc111621ccc8a9df52cc9",
@@ -609,6 +628,7 @@
609
628
  "8b417f12b79e7328ab8998bde222da856470a9fe2a5eff838c710d5fc66c850e",
610
629
  "8c89d0106c4051f7400670c2e83e6e3cfd95533b6d4d5f715124f28dbf41c37e",
611
630
  "8cff7c530e70102f9506012e91f305c306c56a6e68073f44aba74a827c87f679",
631
+ "8d0079bdee4cf4dc86c1cca826f81584fe2c97db04514fdd666167df8b509979",
612
632
  "8d3788c5b6eb17b9c58873208bbd36df9e4d3f969563165095a9dbb17d6c73fe",
613
633
  "8db63678d5615054312a1b20b4383358fb0ec689e6ba6cb0d650bb133a2e3452",
614
634
  "9101242a1f901484fc6110064e8e3ff4d4935c0ed69ee76003b57f8faf020765",
@@ -647,9 +667,11 @@
647
667
  "a292850ee0ceb0549d2e0cd50e48c8596594a509597f7210f59acbad7b45b475",
648
668
  "a2b7ab33386255547ecd6fce85b574d0354ef8e93d2bafb58e0cb77d7fee46b7",
649
669
  "a2cb37971755afd2b67d7f91f0857ce9e0dee7464e8e16d79c72070bc0e5a5ef",
670
+ "a309b4f27f5733300e22b7b04a044b6c30df42b7ad51c9d03369e8d8d3da2f79",
650
671
  "a35c41356dc27962435aafef3aaad9060a987aa83ea01992da6f7c26612468c9",
651
672
  "a53c30ff3a184f3192f7c528b6cfe50dd917e8185edf31d66c5c1454abcbea9d",
652
673
  "a5700a94e3e29b246bd9e2e29b2292287687cedbb69902f0b87ffbdcf382915c",
674
+ "a5de90a81e905134db282ef1cd50e17a684c8ea3217630147a47e2d5058edc3f",
653
675
  "a624d3a28050dba8ceaf70f1e940c8a2d090c22a63118eee78fc1df154a88e2b",
654
676
  "a6bc0c56d741f39aea2dbceb9bdd671326f12cebf7468c9c0f1c2d8ae733acc4",
655
677
  "a880b449c712d425187e6119469d719a96f01f7a603de858022b385f47f84602",
@@ -701,6 +723,7 @@
701
723
  "bf636ba39e22c5f06aa4e4850e7e4c16e87eab4439b1c18d7bc5313d6c402cc6",
702
724
  "bfd30b5a63ea49031fefd4a7c7804c72731ea35ea47884d0aa154e9aeb83e805",
703
725
  "bfe131637d915e23907418c2ea412a31e4a0e3e90da24c96b3d23380d7f3ceaa",
726
+ "c115b2ba9ec7abe2fa5261a5894e3c4899794b527f01ec682ae7fd2479199c08",
704
727
  "c150c40a04099d7b726eb8aecc8eec8e92f9f9248a6a5c15556c08000c4bd231",
705
728
  "c16ff4fe013ee2cef905d767a09305d9c1b2f778a06b439f0bc4645ac05bc82b",
706
729
  "c1c7e45fedafb97770b6726539694eb0668c70677c2031fe2db2bc8b5339423a",
@@ -751,6 +774,7 @@
751
774
  "d606733e95736b3e28e4dce4847b9775effc0944c4cd54c733400074991934ef",
752
775
  "d660cb0c81a7032782da6910fa05eafa83d36520ca5fdb95e7077da96216a3ca",
753
776
  "d6df42e35e82f71f257f5e79d038bf60a96e4965dbdb6bf08bc95c250754e5fe",
777
+ "d905ca576280d8a3d774bcadebfa57a9e22bdf7c1678ca9a548a1c25d643bf17",
754
778
  "d94088aedb09a1feb3ad388a69e242043b4a921ca8d76b4660d94e18e2836546",
755
779
  "d9547a0d7d9a97bffa84178d8ab73f3556f22c34120759d50eaa165c3ef0c318",
756
780
  "d9683236273b7f1759c34a892267b1c57d59dec57415bb762201a50b5925c408",
@@ -772,6 +796,7 @@
772
796
  "e031aef2a9adb0d0900e8269cf84c892f77b9938984e26df54826d1771861629",
773
797
  "e0cecf4838b7ba38afa78577855d5bf7fab251d3f1b1ba01c51c0f76b97aea90",
774
798
  "e160cfba0b71557be31005767ee845cab16d8c3a9bafde3d12ec31e8af0a1339",
799
+ "e16ebeb4c7803de28999a926918b42bec8d548691719827a636d84bc47941d80",
775
800
  "e2070548ec9a6244c569ef0b9a43c2e5dd99cf9c0fa4540a21fd0595b291e444",
776
801
  "e29fec7d54a144ea4867a3764fccdee6849d58d0f32eacdd56b2c317863fa1c6",
777
802
  "e2e0fc585321ee2013cccaed95d47c49fbca71b54c67abad9080d19d70e3c1a5",
@@ -791,6 +816,7 @@
791
816
  "e97387270720794226b6f1c657a0d5a961dbe06f8b873d6150b2f6246e54891f",
792
817
  "eabe96ee6140792fb6d7c188c3ce031cba5e2c6663aecdeb019d9d51d5a90a43",
793
818
  "eaf6d28a8d55fb98dcd4ddb84ab4645b4bf6d328a26811ddfcc1423b0b8ea604",
819
+ "ed4a68efc0af88b2192a06ae6b208992b617aae54b768d125b95442b987d517b",
794
820
  "edb34ab257304ae32df0fd5b8d4b41e982ff5e956231e56b05c7bbc1e8ae44ca",
795
821
  "edd212c7b0941068f6fd2d2051ad28651acd16ee12d9400f060553af6274ed2e",
796
822
  "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