devlyn-cli 4.2.6 → 4.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -35,7 +35,7 @@ That's it. The installer asks:
35
35
 
36
36
  In a project, AGENTS.md readers load the skills from `.agents/skills/`; Claude Code loads only `.claude/skills/` and reads AGENTS.md when the project has no CLAUDE.md; a CLAUDE.md the installer creates imports an existing AGENTS.md (`@AGENTS.md`). Global installs skills only: `~/.agents/skills/` (omp, Pi, Grok) and `~/.codex/skills/` (Codex), plus `~/.claude/skills/` for Claude Code. Every target gets the `devlyn-ideate` skill and the `devlyn-engines` utility. In Codex / omp / Pi, invoke them as skills (`$devlyn-ideate`); in Claude Code and Grok they're slash commands (`/devlyn-ideate`). Rerunning refreshes skills and the managed instruction block while preserving project rules outside it. See [Migration from earlier versions](#migration-from-earlier-versions) for legacy migration and merge recovery.
37
37
 
38
- Without prompts, `npx devlyn-cli -y` installs AGENTS.md + `.agents/skills/` plus every target this project already has; add `--claude` for Claude Code. With `--global` it installs for every project on this machine, plus `~/.claude/skills/` with `--claude` or when it already has a devlyn install.
38
+ Without prompts, `npx devlyn-cli -y` installs AGENTS.md + `.agents/skills/` plus every target this project already has; add `--claude` for Claude Code. With `--global` it installs for every project on this machine, plus `~/.claude/skills/` with `--claude` or when it already has a devlyn install. After a project install or update, the installer names the changed files to commit so teammates and `/devlyn-ideate` drains can use them.
39
39
 
40
40
  ---
41
41
 
@@ -215,7 +215,9 @@ Selected during install. Run `npx devlyn-cli` again to add more.
215
215
 
216
216
  ---
217
217
 
218
- Installer updates preserve user work: before writing any selected target, devlyn refuses to replace or remove git-tracked skills, edited skills, or directories with extra files (including hidden files). `-y` does not bypass this check. Commit your work or move conflicting folders to a backup and migrate them manually; tracked folders must be moved out of the install path before rerunning. Unchanged installer copies and pristine untracked 4.1.0 installs upgrade automatically. Older copies without verifiable content history require manual migration.
218
+ Installer updates preserve user work: before writing any selected target, devlyn refuses to replace or remove edited skills, directories with extra files (including hidden files), links, or unknown copies. `-y` does not bypass this check. Unedited copies from every published version upgrade automatically, including committed copies, historical install-path stamps and generated Python bytecode caches. Release packaging derives the offline skill history from Git with `node scripts/update-skill-history.js`. Move conflicting folders to a backup and migrate them manually; also remove conflicting tracked paths from the Git index before rerunning.
219
+
220
+ Only `__pycache__` directories containing exclusively regular `*.pyc` files count as generated Python caches. Other entries make the directory ordinary content; links and special files are refused. Standalone `*.pyc` files count as content: none of the 113 published versions through 4.2.5 shipped them. Historical path normalization applies only to the escaped absolute assignment defaults written into Markdown by the 2.8.0–4.0.0 installers.
219
221
 
220
222
  ## Requirements
221
223
 
package/bin/devlyn.js CHANGED
@@ -1,16 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const crypto = require('crypto');
4
3
  const fs = require('fs');
5
4
  const path = require('path');
6
5
  const os = require('os');
7
- const { execSync } = require('child_process');
6
+ const { execSync, spawnSync } = require('child_process');
8
7
 
9
8
  const CONFIG_SOURCE = path.join(__dirname, '..', 'config');
10
9
  const OPTIONAL_SKILLS_SOURCE = path.join(__dirname, '..', 'optional-skills');
11
10
  const PKG = require('../package.json');
12
11
  const { updateInstructions, InstructionError, holdsDevlynDefaults, importsAgentsMd } = require('./instructions');
13
- const { contentHash, fingerprint, isOsMetadata, trackedPaths } = require('./skill-ownership');
12
+ const { contentHash, fingerprint, gitEnvironment, isIgnoredEntry, trackedPaths } = require('./skill-ownership');
14
13
  const SKILL_HISTORY = require('./skill-history.json');
15
14
 
16
15
  // The devlyn skill bundle installed into every skill-capable agent's loader
@@ -83,16 +82,15 @@ const RETIRED_SKILL_MD_SHA256 = {
83
82
  ]),
84
83
  };
85
84
 
86
- // Whether `dir` is a real folder whose only file, besides OS metadata, is a SKILL.md
85
+ // Whether `dir` is a real folder whose only file, besides generated metadata, is a SKILL.md
87
86
  // with an LF-normalized SHA-256 in `hashes`: an unedited copy devlyn-cli shipped, not something
88
87
  // the user added to or wrote.
89
88
  function isShippedCopy(dir, hashes) {
90
89
  const skill = path.join(dir, 'SKILL.md');
91
90
  return fs.lstatSync(dir, { throwIfNoEntry: false })?.isDirectory() === true
92
- && fs.readdirSync(dir).filter((name) => !isOsMetadata(path.join(dir, name))).length === 1
91
+ && fs.readdirSync(dir).filter((name) => !isIgnoredEntry(path.join(dir, name))).length === 1
93
92
  && fs.lstatSync(skill, { throwIfNoEntry: false })?.isFile() === true
94
- && hashes.has(crypto.createHash('sha256')
95
- .update(fs.readFileSync(skill, 'utf8').replace(/\r\n/g, '\n')).digest('hex'));
93
+ && hashes.has(contentHash(skill));
96
94
  }
97
95
 
98
96
  // Skill roots of each target. In a project, `.agents/skills` serves Codex, omp, Pi and Grok,
@@ -348,13 +346,14 @@ function listContents() {
348
346
  }
349
347
 
350
348
  // Old and retired skills in one skill root; nothing outside it.
351
- function cleanupDeprecated(skillsDir) {
349
+ function cleanupDeprecated(skillsDir, written) {
352
350
  let removed = 0;
353
351
  const deprecated = DEPRECATED_DIRS.flatMap((relPath) =>
354
352
  legacySkillPaths(skillsDir, path.basename(relPath)).map((fullPath) => [relPath, fullPath]));
355
353
  for (const [relPath, fullPath] of deprecated) {
356
354
  if (fs.existsSync(fullPath)) {
357
355
  fs.rmSync(fullPath, { recursive: true });
356
+ written.push(fullPath);
358
357
  log(` ✕ ${relPath}/ (removed)`, 'dim');
359
358
  removed++;
360
359
  }
@@ -363,6 +362,7 @@ function cleanupDeprecated(skillsDir) {
363
362
  const fullPath = path.join(skillsDir, name);
364
363
  if (isShippedCopy(fullPath, hashes)) {
365
364
  fs.rmSync(fullPath, { recursive: true });
365
+ written.push(fullPath);
366
366
  log(` ✕ skills/${name}/ (removed)`, 'dim');
367
367
  removed++;
368
368
  }
@@ -374,20 +374,22 @@ function cleanupDeprecated(skillsDir) {
374
374
  // are in place. An optional skill found under an old spelling is installed under its new name
375
375
  // before the old copy goes, so an interrupted migration never loses it. An unedited 0.6.0-0.7.1
376
376
  // copy is refreshed in place. Any other folder under a 4.0 name is the user's own; `-y` leaves it.
377
- function retireRenamedSkills(skillsDir) {
377
+ function retireRenamedSkills(skillsDir, written) {
378
378
  const optional = new Set(OPTIONAL_ADDONS.filter((addon) => addon.type === 'local').map((addon) => addon.name));
379
379
  const refreshed = [];
380
380
  for (const [oldName, newName] of Object.entries(RENAMED_SKILLS)) {
381
381
  const found = legacySkillPaths(skillsDir, oldName).filter((fullPath) => fs.existsSync(fullPath));
382
382
  if (found.length > 0 && optional.has(newName)) {
383
- installOptionalSkillInto(skillsDir, newName);
383
+ installOptionalSkillInto(skillsDir, newName, written);
384
384
  refreshed.push(newName);
385
385
  } else if (isShippedCopy(path.join(skillsDir, newName), PRE_STANDARD_SKILL_MD_SHA256)) {
386
386
  refreshPreStandardCopy(skillsDir, newName);
387
+ written.push(path.join(skillsDir, newName));
387
388
  refreshed.push(newName);
388
389
  }
389
390
  for (const fullPath of found) {
390
391
  fs.rmSync(fullPath, { recursive: true, force: true });
392
+ written.push(fullPath);
391
393
  log(` ✕ ${path.basename(fullPath)}/ (renamed to ${newName})`, 'dim');
392
394
  }
393
395
  }
@@ -612,40 +614,49 @@ function singleSelect(message, items, initial) {
612
614
  });
613
615
  }
614
616
 
615
- // After install(), whose preflight already cleared every selected addon before any change.
616
- function installLocalSkill(skillName, roots) {
617
+ // After preflight has cleared every selected addon before any change.
618
+ function installLocalSkill(skillName, roots, written = []) {
617
619
  if (!fs.existsSync(path.join(OPTIONAL_SKILLS_SOURCE, skillName))) {
618
620
  log(` ⚠️ Skill "${skillName}" not found`, 'yellow');
619
621
  return false;
620
622
  }
621
623
  log(`\n🛠️ Installing ${skillName}...`, 'cyan');
622
624
  for (const root of roots) {
623
- installOptionalSkillInto(root, skillName);
625
+ installOptionalSkillInto(root, skillName, written);
624
626
  writeInstallMarker(root, [skillName], readInstallManifest(root));
627
+ written.push(path.join(root, DEVLYN_INSTALL_MARKER));
625
628
  }
626
629
  return true;
627
630
  }
628
631
 
629
632
  // One optional skill into one skill-loader directory, replacing any older copy of it. A
630
633
  // copy under its name before the 4.0.0 rename goes only once the new one is complete.
631
- function installOptionalSkillInto(target, skillName) {
634
+ function installOptionalSkillInto(target, skillName, written) {
632
635
  const dest = path.join(target, skillName);
633
636
  fs.rmSync(dest, { recursive: true, force: true });
634
637
  copyRecursive(path.join(OPTIONAL_SKILLS_SOURCE, skillName), dest, target);
635
638
  assertCompleteSkillInstall(OPTIONAL_SKILLS_SOURCE, target, [skillName]);
639
+ written.push(dest);
636
640
  const oldName = Object.keys(RENAMED_SKILLS).find((name) => RENAMED_SKILLS[name] === skillName);
637
641
  for (const fullPath of oldName ? legacySkillPaths(target, oldName) : []) {
642
+ if (!fs.existsSync(fullPath)) continue;
638
643
  fs.rmSync(fullPath, { recursive: true, force: true });
644
+ written.push(fullPath);
639
645
  }
640
646
  }
641
647
 
642
- // A 0.6.0-0.7.1 copy is one SKILL.md, the file that identifies it. Renaming the new one over it is
643
- // atomic, so an interrupted run leaves the old copy, which the retry finds again, or the new.
648
+ // A 0.6.0-0.7.1 copy is one SKILL.md, the file that identifies it. A failed swap leaves
649
+ // that copy for the retry; a successful refresh discards its generated metadata too.
644
650
  function refreshPreStandardCopy(target, skillName) {
645
- const skill = path.join(target, skillName, 'SKILL.md');
651
+ const dir = path.join(target, skillName);
652
+ const skill = path.join(dir, 'SKILL.md');
646
653
  const staged = `${skill}.${process.pid}.tmp`;
647
654
  try {
648
655
  fs.copyFileSync(path.join(OPTIONAL_SKILLS_SOURCE, skillName, 'SKILL.md'), staged);
656
+ for (const name of fs.readdirSync(dir)) {
657
+ const file = path.join(dir, name);
658
+ if (isIgnoredEntry(file)) fs.rmSync(file, { recursive: true });
659
+ }
649
660
  fs.renameSync(staged, skill);
650
661
  } finally {
651
662
  fs.rmSync(staged, { force: true });
@@ -676,10 +687,7 @@ function installSkillPack(packName) {
676
687
  }
677
688
  }
678
689
 
679
- function installAddon(addon, roots) {
680
- if (addon.type === 'local') {
681
- return installLocalSkill(addon.name, roots);
682
- }
690
+ function installAddon(addon) {
683
691
  if (addon.type === 'mcp') {
684
692
  return installMcpServer(addon.name, addon.command);
685
693
  }
@@ -688,13 +696,13 @@ function installAddon(addon, roots) {
688
696
 
689
697
  // The core skills into one skill root, replacing older copies and retired skills, then the
690
698
  // marker that records a complete install.
691
- function installCoreSkills(skillsDir) {
699
+ function installCoreSkills(skillsDir, written = []) {
692
700
  const sourceSkillsDir = path.join(CONFIG_SOURCE, 'skills');
693
701
  log(`\n📁 Installing devlyn skills to ${skillsDir.replace(os.homedir(), '~')}`, 'green');
694
702
  fs.mkdirSync(skillsDir, { recursive: true });
695
703
  const previous = readInstallManifest(skillsDir);
696
704
  clearInstallMarker(skillsDir);
697
- const removed = cleanupDeprecated(skillsDir);
705
+ const removed = cleanupDeprecated(skillsDir, written);
698
706
  if (removed > 0) {
699
707
  log(`\n🧹 Cleaned up ${removed} deprecated file${removed > 1 ? 's' : ''}`, 'yellow');
700
708
  }
@@ -705,28 +713,66 @@ function installCoreSkills(skillsDir) {
705
713
  // Full replace: copyRecursive is an overlay, so stale files would otherwise persist.
706
714
  fs.rmSync(dest, { recursive: true, force: true });
707
715
  copyRecursive(src, dest, skillsDir);
716
+ written.push(dest);
708
717
  }
709
718
  assertCompleteSkillInstall(sourceSkillsDir, skillsDir, DEVLYN_CORE_SKILLS);
710
- const refreshed = retireRenamedSkills(skillsDir);
719
+ const refreshed = retireRenamedSkills(skillsDir, written);
711
720
  writeInstallMarker(skillsDir, [...DEVLYN_CORE_SKILLS, ...refreshed], previous);
721
+ written.push(path.join(skillsDir, DEVLYN_INSTALL_MARKER));
712
722
  }
713
723
 
714
- // Keep installer-managed pipeline state and install metadata out of git.
715
- function ignoreInGit(gitignoreEntries) {
724
+ function readProjectGitignore() {
716
725
  const gitignorePath = path.join(projectDir(), '.gitignore');
717
- let gitignoreContent = fs.existsSync(gitignorePath)
718
- ? fs.readFileSync(gitignorePath, 'utf8')
719
- : '';
720
- const gitignoreLines = gitignoreContent.split('\n').map((line) => line.trim());
721
- const missingGitignoreEntries = gitignoreEntries.filter((entry) =>
722
- !gitignoreLines.includes(entry) && !(entry === '.devlyn/' && gitignoreLines.includes('.devlyn')));
723
- if (missingGitignoreEntries.length > 0) {
724
- const prefix = gitignoreContent && !gitignoreContent.endsWith('\n') ? '\n' : '';
725
- const hasManagedHeader = gitignoreLines.includes('# devlyn-cli pipeline state');
726
- const header = hasManagedHeader ? '' : gitignoreContent ? '\n# devlyn-cli pipeline state\n' : '# devlyn-cli pipeline state\n';
727
- fs.writeFileSync(gitignorePath, gitignoreContent + prefix + header + missingGitignoreEntries.join('\n') + '\n');
728
- log(` → .gitignore (added ${missingGitignoreEntries.join(', ')})`, 'dim');
726
+ const stat = fs.lstatSync(gitignorePath, { throwIfNoEntry: false });
727
+ // Refuse symlinks and hardlinks even when no rules need changing, before any installation write.
728
+ if (stat && (!stat.isFile() || stat.nlink !== 1)) {
729
+ throw new InstructionError(`Cannot update ${gitignorePath}: not a regular file with a single link (links are refused).`);
729
730
  }
731
+ return stat ? fs.readFileSync(gitignorePath) : Buffer.alloc(0);
732
+ }
733
+
734
+ // Ignore runtime state; let the project's install marker travel with its skills.
735
+ function updateProjectGitignore(markerEntry) {
736
+ const gitignorePath = path.join(projectDir(), '.gitignore');
737
+ const original = readProjectGitignore();
738
+ const marker = Buffer.from(markerEntry);
739
+ const managedHeader = Buffer.from('# devlyn-cli pipeline state');
740
+ const runtimeRules = [Buffer.from('.devlyn/'), Buffer.from('.devlyn')];
741
+ const lines = [];
742
+ for (let start = 0; start < original.length;) {
743
+ const lf = original.indexOf(0x0a, start);
744
+ const end = lf === -1 ? original.length : lf + 1;
745
+ const patternEnd = lf === -1 ? end : original[lf - 1] === 0x0d ? lf - 1 : lf;
746
+ lines.push({ raw: original.subarray(start, end), pattern: original.subarray(start, patternEnd) });
747
+ start = end;
748
+ }
749
+ const kept = lines.filter(({ pattern }, index) => {
750
+ if (pattern.equals(marker)) return false;
751
+ if (!pattern.equals(managedHeader)) return true;
752
+ // A header owns contiguous patterns until a blank/comment; remove it only when marker removal empties that block.
753
+ const entries = [];
754
+ for (let next = index + 1; next < lines.length; next++) {
755
+ const entry = lines[next].pattern;
756
+ if (!entry.length || entry[0] === 0x23) break;
757
+ entries.push(entry);
758
+ }
759
+ return !entries.some((entry) => entry.equals(marker)) || entries.some((entry) => !entry.equals(marker));
760
+ });
761
+ let gitignoreContent = Buffer.concat(kept.map((line) => line.raw));
762
+ if (!kept.some(({ pattern }) => runtimeRules.some((rule) => pattern.equals(rule)))) {
763
+ const lf = original.indexOf(0x0a);
764
+ const eol = lf > 0 && original[lf - 1] === 0x0d ? '\r\n' : '\n';
765
+ const prefix = gitignoreContent.length && gitignoreContent.at(-1) !== 0x0a ? eol : '';
766
+ const header = kept.some(({ pattern }) => pattern.equals(managedHeader))
767
+ ? '' : `${gitignoreContent.length ? eol : ''}${managedHeader}${eol}`;
768
+ gitignoreContent = Buffer.concat([gitignoreContent, Buffer.from(`${prefix}${header}.devlyn/${eol}`)]);
769
+ }
770
+ if (!gitignoreContent.equals(original)) {
771
+ fs.writeFileSync(gitignorePath, gitignoreContent);
772
+ log(' → .gitignore (updated project install rules)', 'dim');
773
+ return true;
774
+ }
775
+ return false;
730
776
  }
731
777
 
732
778
  // With the Claude target in the same run, an AGENTS.md that links to this project's CLAUDE.md (a
@@ -747,16 +793,18 @@ function agentsMdIsClaudeMd() {
747
793
  }
748
794
 
749
795
  function installAgentsProject(withClaude) {
796
+ const written = [];
750
797
  if (!agentsMdIsClaudeMd()) {
751
- updateInstructions('AGENTS.md');
798
+ if (updateInstructions('AGENTS.md')) written.push('AGENTS.md');
752
799
  } else if (withClaude) {
753
800
  log(' → AGENTS.md is CLAUDE.md here, so it gets the devlyn block written there', 'dim');
754
801
  } else {
755
802
  throw new InstructionError('AGENTS.md is CLAUDE.md under another name here. Choose CLAUDE.md as well '
756
803
  + '(npx devlyn-cli -y --claude) so the devlyn block is written once, into CLAUDE.md.');
757
804
  }
758
- installCoreSkills(skillRoots('agents', false)[0]);
759
- ignoreInGit(['.devlyn/', '.agents/skills/.devlyn-install.json']);
805
+ installCoreSkills(skillRoots('agents', false)[0], written);
806
+ if (updateProjectGitignore('.agents/skills/.devlyn-install.json')) written.push('.gitignore');
807
+ return written;
760
808
  }
761
809
 
762
810
  // Whether Claude Code reads the devlyn defaults of AGENTS.md through an `@AGENTS.md` import in this CLAUDE.md text.
@@ -780,8 +828,9 @@ function installClaudeInstructions() {
780
828
  throw new InstructionError('Claude Code reads AGENTS.md here, and its devlyn defaults need an update. Codex, omp, Pi and Grok '
781
829
  + 'read them too, with .agents/skills, so choose AGENTS.md as well (npx devlyn-cli -y --claude) to update both together.');
782
830
  }
783
- updateInstructions('CLAUDE.md', initial, !imported);
831
+ const changed = updateInstructions('CLAUDE.md', initial, !imported);
784
832
  if (imported) log(' → CLAUDE.md imports AGENTS.md, which holds the devlyn block', 'dim');
833
+ return changed;
785
834
  }
786
835
 
787
836
  // Project CLAUDE.md and .claude/: skills and settings.
@@ -808,16 +857,17 @@ function installClaudeCore() {
808
857
  && (!settings.env || typeof settings.env !== 'object' || Array.isArray(settings.env))) {
809
858
  throw unmergeable('env must be a JSON object');
810
859
  }
811
- installClaudeInstructions();
860
+ const written = installClaudeInstructions() ? ['CLAUDE.md'] : [];
812
861
  for (const relPath of DEPRECATED_FILES) {
813
862
  const fullPath = path.join(targetDir, relPath);
814
863
  if (fs.existsSync(fullPath)) {
815
864
  fs.unlinkSync(fullPath);
816
865
  log(` ✕ ${relPath} (deprecated)`, 'dim');
866
+ written.push(`.claude/${relPath}`);
817
867
  }
818
868
  }
819
- installCoreSkills(skillsDir);
820
- ignoreInGit(['.devlyn/', '.claude/skills/.devlyn-install.json']);
869
+ installCoreSkills(skillsDir, written);
870
+ if (updateProjectGitignore('.claude/skills/.devlyn-install.json')) written.push('.gitignore');
821
871
 
822
872
  // One-hour foreground Bash calls and one-hour prompt caching serve a drain host that waits in the foreground.
823
873
  let settingsChanged = false;
@@ -848,6 +898,7 @@ function installClaudeCore() {
848
898
  if (settingsChanged) {
849
899
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n');
850
900
  log(' → settings.json', 'dim');
901
+ written.push('.claude/settings.json');
851
902
  }
852
903
  // Their ownership cannot be established, so the other retired settings stay; name the ones present.
853
904
  const retired = [
@@ -857,6 +908,7 @@ function installClaudeCore() {
857
908
  if (retired.length > 0) {
858
909
  log(` Retired devlyn settings may remain in .claude/settings.json: ${retired.join(', ')} — delete those you did not add yourself.`, 'yellow');
859
910
  }
911
+ return written;
860
912
  }
861
913
 
862
914
  // Installs the targets in one scope; returns the skill roots written.
@@ -871,20 +923,62 @@ function install(targets, global, optionalNames = []) {
871
923
  + 'would apply to every project. Run from a project folder, or use --global for skills only.');
872
924
  }
873
925
  const roots = targets.flatMap((target) => skillRoots(target, global));
926
+ const written = [];
927
+ if (!global) readProjectGitignore();
874
928
  preflightSkills(roots, optionalNames,
875
929
  !global && targets.includes('claude') ? path.dirname(skillRoots('claude', false)[0]) : null);
876
930
  // AGENTS.md first, whatever order the menu gave: a new CLAUDE.md holds the block only when AGENTS.md does not.
877
931
  for (const target of ['agents', 'claude'].filter((name) => targets.includes(name))) {
878
932
  if (global) skillRoots(target, true).forEach((root) => installCoreSkills(root));
879
- else if (target === 'agents') installAgentsProject(targets.includes('claude'));
880
- else installClaudeCore();
933
+ else if (target === 'agents') written.push(...installAgentsProject(targets.includes('claude')));
934
+ else written.push(...installClaudeCore());
881
935
  }
936
+ for (const name of optionalNames) installLocalSkill(name, roots, written);
882
937
  log(`\n✅ devlyn ${PKG.version} installed`, 'green');
883
938
  if (global) log('The principles block installs per project: run npx devlyn-cli in the project without --global.');
939
+ else noticeProjectChanges(written);
884
940
  noticeGlobalDrift(roots);
885
941
  return roots;
886
942
  }
887
943
 
944
+ // Advisory only: a successful install stays successful when Git is unavailable or fails.
945
+ function noticeProjectChanges(paths) {
946
+ if (!paths.length) return;
947
+ const cwd = projectDir();
948
+ paths = [...new Set(paths.map((name) => path.relative(cwd, path.resolve(cwd, name)).split(path.sep).join('/')))];
949
+ const env = { ...gitEnvironment(), GIT_OPTIONAL_LOCKS: '0' };
950
+ const git = (args) => spawnSync('git', ['-C', cwd, ...args], { encoding: 'utf8', env });
951
+ const repo = git(['rev-parse', '--show-toplevel']);
952
+ if (repo.error || repo.status !== 0) return;
953
+ const status = git(['status', '--porcelain=v1', '-z', '--untracked-files=all', '--no-renames',
954
+ '--', ...paths.map((name) => `:(icase,literal)${name}`)]);
955
+ if (status.error || status.status !== 0) return;
956
+ // Porcelain paths are repository-relative, even when installing in a nested project.
957
+ const changed = status.stdout.split('\0').filter(Boolean).map((entry) =>
958
+ path.relative(cwd, path.resolve(repo.stdout.replace(/\r?\n$/, ''), entry.slice(3))).split(path.sep).join('/'));
959
+ // Keep the installed folder/file scope, using Git's spelling on case-insensitive volumes.
960
+ const pending = [...new Set(paths.flatMap((name) => [...new Set(changed
961
+ .map((file) => file.split('/').slice(0, name.split('/').length).join('/')))]
962
+ .filter((file) => {
963
+ if (file === name) return true;
964
+ if (file.toLowerCase() !== name.toLowerCase()) return false;
965
+ // Case folding alone can include independently edited files on case-sensitive volumes.
966
+ // Removed paths have no identity left to compare, so require their exact spelling.
967
+ try {
968
+ const written = fs.lstatSync(path.resolve(cwd, name), { bigint: true, throwIfNoEntry: false });
969
+ const reported = fs.lstatSync(path.resolve(cwd, file), { bigint: true, throwIfNoEntry: false });
970
+ return written && reported && written.dev === reported.dev && written.ino === reported.ino;
971
+ } catch (error) {
972
+ log(`Cannot compare Git path ${file} with installed path ${name}: ${error.message}`, 'yellow');
973
+ return false;
974
+ }
975
+ })))];
976
+ if (!pending.length) return;
977
+ const quote = (name) => /^[a-zA-Z0-9_./-]+$/.test(name) ? name : `'${name.replace(/'/g, "'\\''")}'`;
978
+ log('Commit these changes for teammates and /devlyn-ideate drains to use them:', 'yellow');
979
+ log(` git add -- ${pending.map(quote).join(' ')} && git commit -m "chore(devlyn): update devlyn-cli to ${PKG.version}"`, 'dim');
980
+ }
981
+
888
982
  // A devlyn install in a user root this run does not refresh drifts and can shadow the
889
983
  // project's copy; name it without touching it.
890
984
  function noticeGlobalDrift(installed) {
@@ -944,11 +1038,11 @@ async function init({ yes, claude, global }) {
944
1038
  const scope = await singleSelect('Where', ['This project', 'Global — every project on this machine'], global ? 1 : 0);
945
1039
  // Ask about optional addons (local skills + external packs; MCP servers belong to Claude Code)
946
1040
  const selectedAddons = await multiSelect('Optional skills & packs', OPTIONAL_ADDONS.filter((addon) => addon.type !== 'mcp' || targets.includes('claude')));
947
- const roots = install(targets, scope === 1, selectedAddons.filter((addon) => addon.type === 'local').map((addon) => addon.name));
1041
+ install(targets, scope === 1, selectedAddons.filter((addon) => addon.type === 'local').map((addon) => addon.name));
948
1042
 
949
1043
  if (selectedAddons.length > 0) {
950
- for (const addon of selectedAddons) {
951
- installAddon(addon, roots);
1044
+ for (const addon of selectedAddons.filter((addon) => addon.type !== 'local')) {
1045
+ installAddon(addon);
952
1046
  }
953
1047
  } else {
954
1048
  log('💡 No optional addons selected', 'dim');
@@ -1,77 +1,613 @@
1
1
  {
2
- "source": "devlyn-cli@4.1.0 (npm tarball sha1 398ff07d3d689b56cc22e18ac50260bc6e4e9e85); devlyn-cli@4.2.0 (npm tarball sha1 c93c2fb1cf5630b18d2eba0085182c14fddbe2d5); devlyn-cli@4.2.4, 4.2.5 and 4.2.6 (_shared and devlyn-ideate, fingerprinted from an install of each packed tarball); whole-tree fingerprints from bin/skill-ownership.js; deprecated commands: commands/ and optional-commands/*/ files of devlyn-cli 0.0.1-0.5.8 (every published version that shipped them), LF-normalized SHA-256",
2
+ "source": "skills: config/skills/* and optional-skills/* trees from every first-parent HEAD commit touching those paths and every local tag, derived by scripts/update-skill-history.js using bin/skill-ownership.js normalization (LF, install-path stamps, OS metadata and Python bytecode); deprecated commands: commands/ and optional-commands/*/ files of devlyn-cli 0.0.1-0.5.8 (every published version that shipped them), LF-normalized SHA-256",
3
3
  "skills": {
4
4
  "_shared": [
5
- "eb3327d5e6940bd5e0459fa28c9ab323d9c58ccfdeb9426c19cd027cc4e5c4ed",
6
- "8c80926ab03b2477e52264495b35c2116f65fc51b02e8c56e441b5705872392a",
5
+ "015eea12cc07cfb9eb318c30855f6deacb5bf43bd508a511dffd2e48c9d82964",
6
+ "0544f4e90951233c6cbe99755fb5c95728d7429b8b4ce5dd6e2829dd0816524c",
7
+ "05935881380d65ba34a964592e4587534fcea034b43b13673eff4108ed9389fd",
8
+ "06ab3a4b9e1c97d9b3dcd238148d775f1165f3fc08898af2f3b76149162b78d3",
9
+ "082124278a538e4ea8b6622d767f996e3d818ec85d6e0d8ddddafe9bb03118b2",
10
+ "08b96c407cb56b0a0242b3918e1618b21eca06caf15b8a1eec00aee6b023e08e",
11
+ "0a60dd9b96442be24a571dd5fe6cac426bba7dd7a06ad17f3211f4f9fd9b1741",
12
+ "0b5c3f72d18f8eea538bfef7192b207ff5cbac25c7e0d2ce080cc725bfdaa2e2",
13
+ "0f397a4edd6b1b3063d27f6205dc6b3d069fa616624bccf661539b174535fc7e",
14
+ "110005e3c7052d35502186d907b169c9fc7c85d558ab7c7ad0ba2f0308aa7a5d",
15
+ "135f09d5382b4d375204bba3ff6fb3f6fdc34a1ec89fe88542da02d12dda0f0d",
16
+ "1504c7a41e5f4a3206288d05e66fb48847222864f7825f05633883e2c562ec77",
17
+ "161cb4f1b27a9f81ae1e96af6ff596399c8936578a11d601108a89cbdeea241d",
18
+ "196afa14871099a9afc4e484b00e8d8ccd928dba79db95a6a8776f58c8e78c39",
19
+ "1c1e5e44ff704b1b8791fabcad79a4158d8cd2f024569c1685ec3b185a98fbcc",
20
+ "20bfec10dcec9a06701827a6a39faf3495f970f659f1e941cbfb62e880a0649c",
21
+ "20e11efa7b27dcd30b5550ba5e0e2e07df9ac522a76445a594803a0e29b2e13d",
22
+ "21723943c52573124ce3c74e9090a8ae7d7c9ecdeb78fdfa52b89c45ff1b3374",
23
+ "247904cad29c1c3ed810f848e66f982e8765e5c627ef3a18a876fd95eed9c29c",
24
+ "24f73bd06d359296088e43ce2e7bf5178e3f69752b559639585563ec35fc33f5",
25
+ "27d4da7863ed4652187f6a101da0e56b1d3d18e4565c720d5f8150ad0a193402",
26
+ "2a4361bca0a9fadc4a8e5e4578c383171cd64c65296fe882990502250df44d53",
27
+ "2bcac1ab5995e474c7dd46705413982876384d8b4da704126a66c10770a1a4d2",
28
+ "2bcae17ca31adaf306d1e5d21ad0c211bbd6f57605f68168f656e078dbf524e2",
29
+ "326a71c447fe4d98f3460eda3fc541920b7cc2205b8288743e8622d90215a49d",
30
+ "34282740d421a65fc75dc95edf260aa0d0fc6b90472f7b4641cb80110df0a359",
31
+ "353b483942a62b79d1c5bf31e30060ef7ed9a67ae2d8b27450a53acb98e4e23c",
32
+ "3554398c2be144938e4837d9b60c0976f685cfc2c0dd0451cda714fd053f3c7d",
33
+ "35c7e6c069c847ff147f2bf4f3b67a296652c122aa91a3966457a00dd0597b78",
34
+ "37acccbdc76aee15311b5f47fd26c50b1652dbe7cbcbacfc37774841ab464028",
35
+ "393ad728eb0906ae61110fde3820c4fce015d8f94643a2877372b74a6b6eeb02",
36
+ "3b6193eabc83d815804533010ca7bff1f240286e7898380a5fc2adac71dcb513",
37
+ "3bc9236cf258d3fdf85fa91f70f1d3d0c98d32bd001af4a69955e733f9255238",
38
+ "3be2ec1243eb28f779b170e469dfb65555a92dc08b3d927b8973cbd49308125d",
39
+ "3c58989be2f943896810ef709fb115171420a918dfaadc43ea47940fa8ebc7ed",
7
40
  "3c81c575cc43d70488865b440204cb399091405d9b7f81dbf047a32d3d5ac045",
41
+ "3cd398df22e666fdd2e0f253c5a7464bca62bceefe3737d28a5ef95b75cb3a33",
42
+ "3e39fbe29d0bd607f4ad940f4ca40f9abf82d468e418d0362fb0fd4fa6812475",
43
+ "3e5135877e733f1dcff3b747444228f5eece7cb047956c87a3af02a485cfaad9",
44
+ "3feb469793d0101c2cd939684a9fcdebd333d0532c68455f0f2df33c4b312bfc",
45
+ "406b982f6ca0b9f5c0faa6cb50749b52aaf23545e06174ec39f831929dc929bd",
46
+ "40f50f3c3c0d030d1efeb6955c0c6f32f518e737d8dd3bec6610ff02badebbdb",
47
+ "430d156eb8c62c7edba3d660050f90b267f928976c3a3688e95d54f00dc34747",
48
+ "450d578ef177a9e8f0b693940ad64b41c1d9719975ba6305f62a7c82ac3a892f",
49
+ "4685be6a94ad5587ce53785c14a60cccf9a852d9d23391dba0a5bcd0c64931cf",
50
+ "4ecb004b24a8adfe41c7416c3fa24e62cf76baa3b276ae1ce5091caef77e57b8",
51
+ "4fed91aed4dc1cd28a9e07229dbe9b7f6d8127e5f763dde8f76bb1b6f6855cce",
52
+ "514bfaf5667af3c9d4d36f391e6567a5eb759c6dfb06a5c3ea23cf29692844ef",
53
+ "565aa53ab153a37137661a089a8f90d55ea746136e12948e082a35e9fb8bfe52",
54
+ "56c2a60ab143ca59a1d961367d0acdae8f7e24c7722a3ee43659d3f9db619b58",
55
+ "572d4b0d94d5eb1c96b624097fc8a4cb9827eca6b34d3227a08928cff42c844b",
56
+ "5882958674c44409f5dc7a9bc139c2b46473c8989b84927e87b2c45a2cb964f1",
57
+ "58af082042ac1cec7fb60dd2b6287e714db2684edeea5f96112716464f583742",
58
+ "5995fcdb203a6b98c3e7cae4fe6cc7a3fb57f06a5d32069576e58350d50eab6f",
59
+ "59f0ecc2c1fd9620e4cac06be370e0bf9965a35c20616047445dbc42da6a1677",
60
+ "5be2e643f96b6ce503ae8d631c67729f0c24b6c5d2b431c087161b520ccbe417",
61
+ "61ec4c0435a2f0b307df8e88d81d9862c3abc1bf758648f2c18039581596659b",
62
+ "628ee7de57717804cb2b4f4c9357751578276f241eb3ab53fdee49ae92d57513",
63
+ "6320c0d8a5cb5ae82fc08c9c05c9151b00366ef759c824c16d4b777ccc30c771",
64
+ "640500e3ada3d81645c293ddc956e9757f0c4ba0a06e3f914c9bdad4968cbc66",
8
65
  "6546d6b833701f2641a004802cfc17ec62442e5d0003f396cb0231b676ba9a4a",
9
- "640500e3ada3d81645c293ddc956e9757f0c4ba0a06e3f914c9bdad4968cbc66"
10
- ],
11
- "devlyn-design-ui": [
12
- "91080266232fb62b6f1d45932184f065589e12eb66f02fb6442527c6dfcd81b6"
13
- ],
14
- "devlyn-engines": [
15
- "3451d37f47e6e707cff92baa12d04b43aee94301456b8e280c09332e598a1c2c",
16
- "89f0efaa2efdfddc887b494138247980d97856f6cabe6a8dcc585b3a28e5fa88"
17
- ],
18
- "devlyn-ideate": [
19
- "8ff5148cc65b4608c35b29afd6fef20d42d8da93a094144d98cfc63e74089b6e",
20
- "5824f07e455fe1c3942bf595a12e935258d4c0a851ca1bf741db915603d1c4e0",
21
- "7f451e48e391643b34f6249244bc47d93d6303875e7b092771e3af0718343bc1",
22
- "d6433bf0f96dd9dd485c3623885255738654674b3e23cfcc1a2cfd24f3cda96e",
23
- "7fb3ceaf835230e1a75e510b046f4fe26e79815760354bec1949fa55515c6316"
24
- ],
25
- "devlyn-queue": [
26
- "4c7d988dcd154c7bf658d2d24cd7868955df9d854ed87eb784742d6d4bb229e0"
27
- ],
28
- "devlyn-resolve": [
29
- "2abd303851f085da07f6b55ee1724842ef7f8e8ad6b68c6339df5f1eaa8c69de"
66
+ "65664aafdb12ff24a44dab5f7cfbcba3cec520415901549bfc6ed0762ffb34a2",
67
+ "65c2facf362e2de58f27395a2801bbe31ec53b5fd0b313fda4f8b1e75f7655cf",
68
+ "660827ed6c68b41839f919f5ea4c7dcefb525dcdc6c12d8fbb95e7c6867a79f8",
69
+ "661d656ffb85421241a73a9fc0d0800dcccdeee7b54009be49ab1469f1f391cb",
70
+ "6801418aae194cc69778b2e9baafc86533c4199fc81384b4b62e375263ae8a1d",
71
+ "6882aca0920ee33af4f11d15a53d08dc0108df0070551b27a97a5c52717ca372",
72
+ "69a3fa5c1e7c5358cf7571936c8c09f5cc1d13999c8490ac10533af2091c4226",
73
+ "6a523474b70ba99aff5472020cdde9ca1e7c91f8ee65db03c9e712adfac6d31d",
74
+ "6d1a5458b23196b6f6fde6b7e9dc097ed173cb5914de439e512a032ca1892812",
75
+ "6fa2b5e089dcd00613c6e9800d2e763da9e5b2c80e6a7ef5d54d5ad7113e8399",
76
+ "71fb125e20420d6f3fcd3396093fcdc8d552bab9626c3cbd962351e0d19adac8",
77
+ "725395f70eec7e2ceae1f3aa122893433923b098a1e0d6202dd410979ef4eea0",
78
+ "72baab9daecfdbcafb2f9484a16c2e89fc6ffc5fc693f1a2627042f1e8197f06",
79
+ "73254fa6c8824b5d0bd0545332fafd1e946f4bc6d702909580abfb42db99e35f",
80
+ "7403aa2911ac89180c4abe2c15e4037a8023af134e2fe70ed35ae1b14c92a04c",
81
+ "74ad540635331051e8a32b6d7aa08c1926b9433298d1a1f699efe294b0c96d53",
82
+ "74d650102836e72f0ab6cec05760ced6e2e047c62c1fe403837879e91d2e1cc4",
83
+ "75cdbe50f909866a69cd7f7fa6ed104f471a6386e19fcf82e695c001edeafe13",
84
+ "7a0f6c692bd1f74759d44f1a145899c157e1787635db546ea8a192461421e353",
85
+ "7ca704d818bf9c044fed59fdd4107bd0463f9b0514d44729003e3e52aa796353",
86
+ "7e915cc7307af694e326c7f75cf831d187edd965f38d870323cd50f0f01a564b",
87
+ "800543bb9d1f8b90a7c077f072aa45364a0136d3c4e9b7acddcdbc30d63eb0c8",
88
+ "823e497e01870123d4e73c8dac7472e161b5ffa2f6181ddcba71fcb829096948",
89
+ "83b6d37fcacc086a09b9c3eddfff2263bfef23f8fabf8d8833a7c9f9fb6186c5",
90
+ "84b045f83ee58e6117ee4316499f4dcf74375942d7558d125075e995b3cc90e5",
91
+ "865e26f38b04dbaae6b0b91616110d6bd748909b7dec68f20cd7c2a240d2fa5a",
92
+ "8ab08b8f3d19e7fffc36fbc4d8c4dad83df02bcb83a9aabcb8db8b82fe12b384",
93
+ "8b34167328e88c73538b5ae2a30e452b544a44bc03f70a2c4f0278b33953afc4",
94
+ "8c80926ab03b2477e52264495b35c2116f65fc51b02e8c56e441b5705872392a",
95
+ "8fb73c0d2472b3643ac6e3f0ad87d818694afebaf68fcad596496e703893c43d",
96
+ "9162fc2fed6f4a611d299485be3a397080beb3304865ce59b95576ba7d00650e",
97
+ "94a14a1b87df1d7297d12949a24e47a0d6c16a8fe30495ee759fcabb2e1aa398",
98
+ "978e4210e49b42827c98cf6e40c868c87dc6e3533a2b779ca2fa05abfc16ad0f",
99
+ "9815b6e6963e068872a6dbeae24068143bf92042bb035f67bafd1a224d65bcb0",
100
+ "998343c148618b937556b58b83749c05d5aa3283280d751c118da6a542fb87f2",
101
+ "9f14ca629e41730b1a9fb5d0ad1a39cff558fe34758d6ceccbb9de498f9f384e",
102
+ "a11950a5880346f03257c688fd8bc6f8c29fc0b7c05c90e8f5211d5fb7665774",
103
+ "a405a99b988f6edd2eabb0504b36309bb4cf0c3b97eab26bb7ef257e4515dbf3",
104
+ "a463693773934b6de78f5326fb17acac34e8159f459b89c21aa66f3ac0874a57",
105
+ "a66080b0ff25f35e8574a24ee01d87de9f579a59796977f3cc59f1865063f6f6",
106
+ "a85c7f85bba20b95b8072242e889507b44e3bc029e082abaadaa2d9de5372428",
107
+ "a89381db5b1a7071c29c64d5293d1d248a2a658760ac2f2e9ecd99d3ab83bc86",
108
+ "a9c3acfd36a6b5c668a2b7e3de4f0408fe1ab6b83db1fb8f12e90f66709509f8",
109
+ "a9c5242608e8b6f660f3ae9daf3fdefb210b6e4603bddee3d26ba59a55be4085",
110
+ "aa0d6fad740ead835720a528f197b29e6cd0b209d3b9db1b312b9895f991b037",
111
+ "ac3d475156af89e2e887118e92b972dbfefde6f0a07bbbdb68ba88fce5f7904d",
112
+ "ac5485ee9218c7038c6d329a3877925995c5afcb2dbd16b1da4495ed769d2083",
113
+ "add41e1257ed525d6f225d9eb976a3978f126e604ca7c0f5aa1b223af78f1902",
114
+ "afb478a981c4fd91f66fbe10d04481cd4562825fd6785ba0aadc6ce3ee3da173",
115
+ "afd8966f25b601b4f6a915fa2c6591b7369f55e8a9ae370cdcb98caf886de707",
116
+ "b1fced67f40768df688e8ce34d3105dafaf6027d3e4522b77276d168bf150f32",
117
+ "b27e98e1872f450aa11c85d6aebd70d25cb5d45db13662b7ce57a1ab1736b348",
118
+ "b3ba619b28a26cb71e2d1fd45adb203cca2b0917acd44288bb1dadf300859259",
119
+ "b4414c6811d824ba16084bc1dda9b956d04055fd4ec4467e62d2d27f364389a0",
120
+ "b6b5b1952f7d2daa15af39858bb05dda9b5f55dc15c479f8fa7ca84a0ad5299e",
121
+ "ba64bf037553aafdfb6de721fff73f32ae6b0e6fc1fb54d857d347bf3e9b8952",
122
+ "bb967300f6fdee8b8461ff2a5f82acd055d969dc6cdc1040cc36a39867cad6e6",
123
+ "c3a37f7502b5e2d7108845907520cf6fef2ebcd70dd59d75f42cbf5bf5f21fbe",
124
+ "c5d2130bb864db373d36878b96dd974fee4d64b3bf3455cc722b4f6787b44c3c",
125
+ "c86770d97e721fa796925cbd64544955c8d6f88fbb696069bbf1cf74e5e907cb",
126
+ "ca5af169c57dff8d038b99376a7b984a220102c43406ccdef79c9d1eadbce6fb",
127
+ "cf13e851d0ee9b8f2f0bb44765b1e87eaec4c22a0f10aad8ad42c597c8fc0cf9",
128
+ "d023806548fe634679cee0d31639a272e6404398415c3ac72fcfa66f924d8d00",
129
+ "d07b60448cb133633f1e4e6be194d2c864fbc79fa85eb74ae36d0209155cc0b1",
130
+ "d0e1ecbb740b6e4a847b4b59a6b0370504456fcee170e34c23d65865539e03de",
131
+ "d201802016f06626bd9fa4074d61a54ab9f70b7febfccc0ea0c70605d1f1b811",
132
+ "d2273f7404df92436c4c2ebcf38bdf1e5dbc65c5d1544f95dd1332c123ef17df",
133
+ "d22bbe15dff66d4801320ea81d7952af50615a7ed57ffe7033545589b8d946bd",
134
+ "d3b40b9f0e15bf90ca557c0198a8fe5ff1d046b9aaa54b181c743d8e006f9c34",
135
+ "d424feceffc005f220e33a19cac73f33bd40d120cc14da33725b8a990704bcf6",
136
+ "d7388e36d76647a89ce52de615271928cf9fc78f980df399d2bc2e6d6675ec82",
137
+ "dac3da11ea5686382f5c9892a0b8e4ec25e625e5946283d8e2ad944aa84e1df2",
138
+ "db867d1fd98058bb20e41e942abd95f08fbaa9465928025f69f0a0308fa71514",
139
+ "dd0bfdfd2db0b5f9ddef75d1b9ce3247698739fc551d292366dccd506fef6a36",
140
+ "ddb0c8dac58e95298330ab54e4d1fe4a108c968d9af713f267b4f8f4c6808507",
141
+ "e0a305cf3bf47c8bc357195d4d50b0f6177069e870c33040c258c93edae68646",
142
+ "e115205a744fef918a59d1b3a601df09a8b6df1d3e60613b9e8b87240929f8d8",
143
+ "e41013abf5e17445ef83a86974ac1de77beaa61e484e1916577ecc64bb34c220",
144
+ "e725ec4eca42f737ee4bdaceb10c11db289414a1d4bb8be8fe103f89c89f07d5",
145
+ "e812d02ebdbdb8d82eb85842d676e21bedddb0d00154eed99b9d29b43cb8fc02",
146
+ "eb225a3ce2bfa39ddf473dff59b118a452b20fc388d64248cc67df5dfb411d32",
147
+ "eb3327d5e6940bd5e0459fa28c9ab323d9c58ccfdeb9426c19cd027cc4e5c4ed",
148
+ "ebfcaefbda2a8df64460137adf54fdac8798fced904ab484938c9174f0273aec",
149
+ "ec339b1382aeab37be103b9a6c1db96ec935f3b030c90ae0952c19c7c3b0431a",
150
+ "f08e7fed1b6ffc277d5f3b8d0e1fe6d6e8b18ee30f17baa44d0b703efca96118",
151
+ "f12077a8c5f5b6f08c1e220b6f4a191eca8de0daf35fd8ce060ec7a45bb700ff",
152
+ "f133dd1c3c40f6b4cf6879285cf282dcac436a99479717d8cb6968ad885ce0a2",
153
+ "f1b0f66a2079af7423e0889435dc3bc1e5e14c20e7d29d4d55a56217cdb036ee",
154
+ "f22d5b4ebe5f973cbbd5a1fc747c068915e0b4a5b0326754f6a750065091853c",
155
+ "f2bcfa0471aba81a10e3d0e6fd43bd6ec670601a4ba3a7d719e595edf9ab032a",
156
+ "f958bfabd2e056fea911526fba2196d2ed5a48828f576e5ce1657100b466db20",
157
+ "f9617e1034543f31ae2a3820301fbac6e4674e5d6fbc0d4b76751fe87b8e013e",
158
+ "fd68c7a74272929a14e685e447a51058ffcdd4c1d6bef83cbba18045c07aee55",
159
+ "fe386122e502410394ab60643e79aa736ddb5030257c863f4a198e23b56b4cef",
160
+ "feaa255b2b6a7b0dc79877c4eb620c0f3cac88b50d2d1407a0a8a02ebc08ed49"
30
161
  ],
31
162
  "asset-creator": [
32
163
  "e295f57eca534cbd4e27e236999f4f5412ebdc6944ac864761e922dea213f904"
33
164
  ],
34
165
  "better-auth-setup": [
166
+ "4cdc8e524f4caa806ac6899c4259cd7dfb872d0ba2226159024938607e1ce85b",
167
+ "ca927c5078ef5f6ef145a8a11203bec33e57d71d130befeb5b65ad13f4138b6f",
35
168
  "e97c39c5d669ffc1f49d57fd3a016e852727e57c3abe2f9be1ebffab9fcb19ec"
36
169
  ],
37
170
  "cloudflare-nextjs-setup": [
38
171
  "f147a28b9f6f62feb21df89bdf08869820f4871ede7a8ba4a242246509ee6e35"
39
172
  ],
40
173
  "code-health-standards": [
41
- "2056617b324bb7e1e3b85a6536bfe33151e31ef421656cade74258fb4e922c1b"
174
+ "2056617b324bb7e1e3b85a6536bfe33151e31ef421656cade74258fb4e922c1b",
175
+ "3de05d3ecb5ec9736add626674a142c5646a3470f2017403a1ba64a0286d5ce9",
176
+ "481e36637c0ee80183444bc0719aec888c64eba450d8ea0b48d38fd755866023"
42
177
  ],
43
178
  "code-review-standards": [
44
- "3fe0742825aff05fa6fda49d3e3e0fbf5457307af944eb90f2771cbf611edd69"
179
+ "3b02028ba6c62d34cff4106dfec38f0f774f3e120b5289d8216221b94d72ce43",
180
+ "3fe0742825aff05fa6fda49d3e3e0fbf5457307af944eb90f2771cbf611edd69",
181
+ "4a8bed603191eeb6834a5008691bb3942e77462abd2818340b808a752ee5a224",
182
+ "57c2481f9ae36623f59b365eae82f22d54253f8ba0fd690949a12858b5f6e308",
183
+ "b352d21d2a86def2997d89ae1ffa35b0fe428781f75cfa9e1b69a3384416ec31"
184
+ ],
185
+ "devlyn-auto-resolve": [
186
+ "0740bb91fb99953631b0c2c273f00210544576f9497af5eecfdd5fe1e58349e2",
187
+ "0ca656d6ffd54f88a51fcee1c64dd2060cf544344d9631cd201367dcf1798374",
188
+ "0e9eafb0274ebd1dbfb891234cf932439814f097b2ee0b5b38c0459f31b9e250",
189
+ "1b03511aecc98dcbab14814a761f58eba73df49b480c5caf2c4b2503a813cdd6",
190
+ "1dc8d2320580981341d64be62fe9f8d1e0e20ac432488b5f2d3531de6822b617",
191
+ "21aab65af6f8a0b985abf625dc7546f2d02520978fc0128cd81e2ff0f04bdda9",
192
+ "2662f8b1f2ccba29d471c50d989fc707e11e80a31f82fe24563ccc6890fe4a0e",
193
+ "272e7ec8f70e8dc7489c6a4b33b305587efb5ee82006ac2bb469073f0332404e",
194
+ "2fa2213cfcc119a98704614ea72d8569ed548849fde22562ac45adc92dc386c3",
195
+ "2fe20f3ce2a974e5b5653f7f12e40c4581662c912d069d8ca38215b7f1a61dc7",
196
+ "32e31fd2959971a6b0af6525e98147076beb9c390600bc8a9745a6c5c452d8a5",
197
+ "33a64c46367d829cf3660c6a5652167646e445df1b007032bf5f8d6636976c42",
198
+ "35e536027d8a251c410e2536fee4e4d2bbb8402c5653739fdcccc3d915bd544b",
199
+ "3a8d305415f0c2feba6445d25f1adbf3ebf94d54d52c30f9afdde9e265095de6",
200
+ "3b1233f5e66d9642c88956fedb3c9f9bdce52f18d56d4ebe3db3aa7cedf81d89",
201
+ "40310938b2a5eda66d699b5e0f59279b4ba941dd1d2fb8def98b61d5946f0ac2",
202
+ "4f48399c6e2b08841dfd4f1dabd1d8fc0d450c76b5b7651320fbc314dda188e3",
203
+ "53572bc42b03b223ef0c1217fea00114e339f8a629f5e0293cd46f4100463604",
204
+ "5571a978a40ab1cf5adbc00561066ad07a321a7821e51537ad177f3a2f8f47ca",
205
+ "578bed47c79a99949676122a92d262a10f42c186f1388ed34ca8c6d52aa6d0ee",
206
+ "5aaea3ae9f5e8c84a0e3ca59ffbb616e4317b55d053fb2068c75b04644d8f9d8",
207
+ "5b0aeccd4c70057c334a597b2eac8aec658132eedcf6b5d04ab89b5d020c6f30",
208
+ "657a8f5b90047ebf32d3f42627a592fd84a4aca171ff0e75b7e7982c8566802b",
209
+ "676136be835eb8d6acc3ef7e58cc10e0e8683dc7aa1ca2ba3e29ef1b00543ce5",
210
+ "6a7ec13741c4e335aa3f96c4155c3e24b30b620f8bb9ec246cf0b251285e6090",
211
+ "6e5344d95139c75e959e05340f9deeea303c010dbeea7bf10c27c865b79796f4",
212
+ "70efe263c6680576e9c9cde47c4fe5ba9704f566cd1f66c928d8231cc018e9e5",
213
+ "7233346367d51e190abb0940be3abd66a5cdaf615e87b7962e80d7a2780ba027",
214
+ "730f980ccf3e16a5bf4d54f40ed89af0902e6605e5744b5e68af6eed31ae9241",
215
+ "7680c6ffdea0afc897953f5ee1dacae5c2a393a0f2ad8d88dd2ef63bbeac2455",
216
+ "7682eaa5c201c552dbb270cf9f71083a18e08183bd4b1777a1957e3b80c9278b",
217
+ "783a2400fd45c609729689c5407f0d4e128bad9f3baaf0f2f78c9ab2ae70a768",
218
+ "7d03ba0401df41f999d33d332906c08b78ff7087ad58ea8d0e3cdaadad0cbeca",
219
+ "816ceb3db88e56e44a1b2021168e6c44290256fa13b7639f746ece07c930b811",
220
+ "818a4ac55009305466fab2d5841f87db5f59bc1ae5eedfc7db55e739794ce4e3",
221
+ "843fed2fbac8863ac1383f018c1510b47b1b012d16ca54a7525fc1c2daed5e5f",
222
+ "84c6aa074828f13d4cd1bed50793d367dac2fadf185cb446c0065aaa57274ccf",
223
+ "859f798948226b272aab6a862c32c72b721923f7008871b6c43a136a2987c885",
224
+ "86024eb8d61d502fe10663787bb7512be26f4eb4fe15dfcb8e1039ebb768dac5",
225
+ "8a3feb1e35a13406deee473e2b03366df375e458a5f2f586542b482a8ea8d295",
226
+ "8cecd8602c2f9750696d247d99cb32415bea7377d27a0618d8c15ea8580e5bc9",
227
+ "9b2d885f1d97f9d9ad76435be8ee98bad32f5264c3b96a2472270389e801cc59",
228
+ "9f980fee52abfd93ec0cb0e1aa42068560006f06dda49a25eb59384756c03644",
229
+ "ad5580897601b28f53d2ca6989e7a1d7b6f2acca0bb55ef02651bd919aa7596b",
230
+ "b19a207fc295bfe8931b16df0c3538951d3b02f9e834b976c3c9175a71ab53fc",
231
+ "b6ddc3ca15c3c59e8dd77c9bf868a66bf810b038aa86984371155397e6b1f8f7",
232
+ "c1304b5573cf45c05823369bc3bf90d93a27f9d7823f9b8a4a0598db16b4c8c8",
233
+ "cd5e1fb9b218b3c61c17db475f6bc9694c621ccba4d4ee65c6c4dcc8a438a6ad",
234
+ "cf5f81da61c37f244d6027e59d1a73be2a556acdb5a30275f655c644ac2c2e2a",
235
+ "d190658091f29c8145701d4bfac844fe08966d92c510aacda5feb6b62253154f",
236
+ "eed03933c0c89ef52a52fa6b9a65e15896995caf25225389fcbac3e811664531",
237
+ "f073b452a61e20845cb7985d0259184896dd383357cd6b6c0478944ac1012430",
238
+ "f0aeb3e9a725a9f13e598d0486c6c437814b92220f5969085ec04ccc25a3c9d3",
239
+ "f247b3c30816b6a02c9114fa69f8c478b5de7b37427d59f8dd89a558cb36ae6a",
240
+ "f47e3046c7fccd569d1c078e93ebd78b744562ea2bb334e9d9a67053b17ba0c6",
241
+ "f4832254ec1abd2f4e0be6a642c6d23c4cf9b931e074bb673f53ba6429ee29c4",
242
+ "f498d46c77b3a193d12b36b943ecf7ccec6dae7dfb6c0880421b6d4f43b6dbf2",
243
+ "f76c4018a889b4cc42eb527f5f2bc191b297919e0ffa4823d5c6564f9918887e",
244
+ "fb45a9cb28411682fe542aefc5eefd58519b0a815812246dee03c7e86e0aefdd"
245
+ ],
246
+ "devlyn-browser-validate": [
247
+ "67545c155ca84e64cfd17a06018d99541d1970188a0c0dc028f02e8498a7f396",
248
+ "8015980ce9aad60b38d31d9569045ae011051095f92ff96e1400156be109d4a7",
249
+ "809a872198bd652fbb51ffc187e1bd41ad4c907cf82ccf68874154b3393a98b8",
250
+ "b094d046a0c889818d97b31655b4d1a53f8537be82c939294f47caf2f1732955",
251
+ "b1d74b6860d7b2b505ac5b6742cf6d440fddce02a507f71303792417ee829a38",
252
+ "c2a49a84afd555205765aaa0eecce5909cf17ae36010cc6d6ce958430031866a",
253
+ "f99d74b7b81514f6bcd837aabbd634f00ed4fc5219fcaa5420fee2e158ef4eca"
254
+ ],
255
+ "devlyn-clean": [
256
+ "4066886622c6a7648b7a2c6b32fec231ba08172891fdfb6526a0d46296f6d5d7",
257
+ "5a7c26e92d7b18085f258c124904980caadae1e924cb91b38cb2ba3692176e97",
258
+ "e87b524a601db913d9897c842328bbe92270a503216eaa256a73e58c378f1b3b"
259
+ ],
260
+ "devlyn-design-system": [
261
+ "5d83c9617dc994d86409aefdfb2d5efce078a2d0c085c9a21e84d606bf3b8b33",
262
+ "bad7c516bcb097d92c1971025d574af0dc3403b7ed8424ae99993186ae5e9435"
263
+ ],
264
+ "devlyn-design-ui": [
265
+ "5b9ca3551616cf018256e22c64285d386870d93bdb4992ea4e6855310ebbb410",
266
+ "91080266232fb62b6f1d45932184f065589e12eb66f02fb6442527c6dfcd81b6",
267
+ "91d7472834e3c981003602f90e04d87b81415b042a6551755bdd35275afe0747",
268
+ "94c4271e2c4b5ff7824bba2485ffa65a98a1a64bb7a1792fc0d6a7b7cd8cf8c2",
269
+ "d75977f678c44bfc6eff9710e768cd7bb15ba75a539058bf4eba1ef6fb118978",
270
+ "e15946f45a7e5a364cf5e3a074d92c51454ff797c2c6878a17ca6461b84ce521"
271
+ ],
272
+ "devlyn-discover-product": [
273
+ "a912a91f57a9c1baa91eba090a1dd6717ce20f42d69957c819fa8064a01253eb",
274
+ "d2921bd729cd0b62b2d19d09abb3890f7768eefbcf84cdd99658589d4e7a2192"
275
+ ],
276
+ "devlyn-engines": [
277
+ "10d7cc5ce23a1e8ee89c3fac9da5ea09de571c21f06e1ec0bd5108559e43b640",
278
+ "18d84c99a2ca37e96504e50b9eb3047da08ecb87b82a423dcc8007a031ae1b13",
279
+ "3451d37f47e6e707cff92baa12d04b43aee94301456b8e280c09332e598a1c2c",
280
+ "4469daf94371ba7f34ee0af47c1eba6f318316e0ddebde47551b0495dfcf7ff2",
281
+ "519c272250b908bdb9164b198f1adc0b5098f08c6c3819118b30cc292eac5f85",
282
+ "59b030f1fed8361d47370ca9e7e6af2d0a264737eb76837589b674809134c23c",
283
+ "89f0efaa2efdfddc887b494138247980d97856f6cabe6a8dcc585b3a28e5fa88",
284
+ "9bc8113592de34a7d559d8a5db6253e472357e947010ea2304701f1f7de3643d",
285
+ "ee723ff072721cae3f5e130d72515d44414337cffadb9615902b0d578912bda2",
286
+ "fed622aa1bcf9a3b437d42c39f6b35403c8cde657be0685b93bbe864e82ca8c4"
287
+ ],
288
+ "devlyn-evaluate": [
289
+ "4d0149b7006b588cf9e7f68c19502ac2ef9b6c544a08d2857758abf95d43efae",
290
+ "5ac0f9d0af5bce26e727de0fa7a8a7c6a9ab822bfcbd049633b91acfab810066",
291
+ "b1ef3a040a29ba0b40b479289a31ec6d3f81f546ee7723f90f2a74244bb27d20",
292
+ "b4aec3f1b538839c780ab5846b1e4f2aa64adedefdc2d957b8e6cd96228dfa53",
293
+ "cd28806ec5d37e08a21cd4a19c786cedb199c20cfe314ca6290fa701dce58b13",
294
+ "f0b27178427d4aeecd62572065685cf3d6640b8041760871053fc1ebb77cc5d4"
295
+ ],
296
+ "devlyn-feature-spec": [
297
+ "85230dce315b6336e6928b9f37bff4fcd6a8b0e71b292b2ba972585738176e24",
298
+ "e8b480bac8b995441de0b9cfc1f7e9d9503c4ca317f10eaebae03e17d14f6814"
299
+ ],
300
+ "devlyn-ideate": [
301
+ "0098d266ee9d7c79c0c0a0c30474e9bf13fa0c76911668b79206985136c94f80",
302
+ "0122885c08069474ca4a0a5825f885364a62938885f0e501c09242d176c34b08",
303
+ "05e824f63a6381e02042afb1df0f1437584e25a80de8e9db0bd9456aaeb7aab8",
304
+ "0cef055580e00f9ed439eacb2dff9d238bb11619457472016095a8915162fa4b",
305
+ "0dc69c20434966015176d83baa6f95b3421f9e13d0b1bd608a22f0560eae1049",
306
+ "0fe9b270d670f70da9d2535969b6117684b7c1517806b3029ebaf9624da8006c",
307
+ "1582c202d361af927c2d0843d4bcb72ccc98a0a2a9d8344d118d680f70f7e1e4",
308
+ "1d8c3bd31c8b755acd0c3c9f416acb5024dfa99fbf5e2c14d8d6df00a24fe9b4",
309
+ "300d1af60cc53285935dc4b5b370e157707218d844a4303b96d7826eef36ed43",
310
+ "302b1a6c109087dce02ed6af184d6aeeca6cecfd40ebe638beecf8bdf21cf59c",
311
+ "33cf196ba7dd2d96df119707d874317e6005f1323fc2cae677c53e2f134f9ff4",
312
+ "36b58dbdc26e51abc05a60872c4582faeebd67e9fe5626a3933467ba07c68dc4",
313
+ "40237cd5259dbcf7c490f130e844a9e32eea48a8edabd50800edee5029bf95a4",
314
+ "5536f8391ec4442f1b793dba8dd5d7820d45f4b6dadf1111cbac9d54469a01ca",
315
+ "5824f07e455fe1c3942bf595a12e935258d4c0a851ca1bf741db915603d1c4e0",
316
+ "5954e371659ae1010757f0eec4b80dd65a899f9fcf76d4d84d9a75476a963218",
317
+ "65770e3f20c3a3afd2822e9acdcfc80b827f84a7737010b79332da70d431b05d",
318
+ "65c781764633b912e5947362eba0837c8027461a904975401d79ad4fe8714084",
319
+ "6c3e2e4b84b0b4f81aed3759704da7c14f3cbabf3beeb0d1654feda3a15a8675",
320
+ "6d9df58c117165e9d297aa6be0df8d076bac1ed49dc29ae93906af1969798ab1",
321
+ "6e7228610628fc4bca31dc0b0aaf5eb4445e1f123f2c54bd13921c5cd01129fe",
322
+ "75cecbce4d1f4adbba608cef498ae7e421ea75512c6bf9282b66cd94d821f672",
323
+ "7f451e48e391643b34f6249244bc47d93d6303875e7b092771e3af0718343bc1",
324
+ "7fb3ceaf835230e1a75e510b046f4fe26e79815760354bec1949fa55515c6316",
325
+ "83e9de10d3c439af17dc4d7a603109d35136c5c8bd468ab2be962e1fad064c3c",
326
+ "8ff5148cc65b4608c35b29afd6fef20d42d8da93a094144d98cfc63e74089b6e",
327
+ "9a0363c6a03be7aca19b8dafe33907c94aa7d1b9263216232a51c35dd596e5a8",
328
+ "9d62f726c5ac7ed8b3989c6ab3705b5c9e4b44bb70eeece0d43bd76142b9b75b",
329
+ "a12b24d2a70917b5d4e698af5478b6cce4d09ddf021f63ab7129402867433a72",
330
+ "a3b852c30898b572021b32721638f67d9f44cae98653da3362456a7b88208de7",
331
+ "a7d7d65fd5f672cb404e6069fdc42ad7077deaa80ed8500cfa8b382dab2e3e29",
332
+ "aba66b674683935e4c28c93333896dc116ffe67e7a35fe30b3599a8be507e94e",
333
+ "c1b8e3ebf38e2be8972aa003e9614a782cb4f195c69d20b95a475ebf9aaaab9e",
334
+ "ce282a55328c2d071cacc377670fbebea4da4b9a7fee81b25293fb26763f17c9",
335
+ "cf61a917a804503ef1e26fbb1cf498181e843a0758ec001f84edb7bcac837448",
336
+ "d6433bf0f96dd9dd485c3623885255738654674b3e23cfcc1a2cfd24f3cda96e",
337
+ "db8d921aabf0d04811b55465a3757c62c4b39631aebaf6eecc994ca241cfbe56",
338
+ "e25da746dc37b2e82c658a28d9481fd69741151045683eee2119582ddaf731cd",
339
+ "e929f76c15349f1b9bc1e84e0a3e9e48e916168d79270e0f4a36ea8eab9b5388",
340
+ "f2243cc4150e225961825f6097b0cf38166c291dcf58127516ceee841f7ed4c8",
341
+ "fa536198d539a3c85f54dda74d0d78716ac6e6849e733bdadecc8605511eac37",
342
+ "fffa5a47a414c9eae607aeacb6f1a35c5dd5a7f9d1f8532cccf275c79b80775c"
343
+ ],
344
+ "devlyn-implement-ui": [
345
+ "2d0b64db1e35182072e30a1f98d8037a407c5a0b7f028aefb45a10253318f793",
346
+ "3a9b45c631d6988cf18c8f153e0d36f69920ef1aa0ac35ebe8ebcc1382339ee7",
347
+ "f703cf5efd99b27ab048eeb9b94cda2840a161399d8d0c90d3a549af559d711f"
348
+ ],
349
+ "devlyn-intent": [
350
+ "9ea7429c04f897b94468bc5d2219c8779b0cc8d188ee4a8a541d633e303f0516"
45
351
  ],
46
352
  "devlyn-pencil-pull": [
47
- "65af00f88b0837ffd377ebf363da73c766ae0ae8e20716fe18f80edfa91bd6da"
353
+ "65af00f88b0837ffd377ebf363da73c766ae0ae8e20716fe18f80edfa91bd6da",
354
+ "b2a8d3ff0195c32e626cd47a7d3fc581f717991bf58c1b3f172f8f80b6090214",
355
+ "eac5eace072c22da20b71325024edda05f5c63aa259772bc79d506af9ac4147e"
48
356
  ],
49
357
  "devlyn-pencil-push": [
358
+ "27a1757e2e1bb48ff67d69a3308c10bc2006e474dad3ee8ba743f06bbf43ca59",
359
+ "451b04051c3793955e565dad01fbc6405b1930fc71ebcd716d13345b49cf43ac",
50
360
  "76955458ced72f4b0bdaf71482be6097c4c86876934b6e6a2ac7d19f850662ad"
51
361
  ],
362
+ "devlyn-preflight": [
363
+ "22233aeae7f89c4b5a23763bb996b49419e08122528387bd24cd3fcc464d9982",
364
+ "3c7c7dfab1ed3d2ef4878b03d1086e8789503295273f083ece18e4819e725d2f",
365
+ "3f6c9fa223cfff14ab91bcdb050c1112533bf8bd34b2484e9e2baaba5831d19b",
366
+ "475a8068d24e56f2cc41e08109eda28316e0f7c99284f36012286a3090966ca4",
367
+ "4a2ce421eb0d0f71945289828318a41a111f75eb4fa61ed59b62f23e6c2eab4a",
368
+ "5c172fa2ff43f0d8a676490f864fc3b8347c01eeb7ad479d4ee26dd92934e660",
369
+ "621f4c295793871994c7aa7322f1f9f06aadbef18ab5b600ae6a6498f06ec11b",
370
+ "69383f25218f6fc592d8202dd09fc2e425db6e6d0ae88c57e5c3cd14faf26918",
371
+ "8ad0dcd8b11a7d11b363cfec9b93b703b591d88750c835c438173fc5fe03d439",
372
+ "8fa8db1b09370cb39eab1e999d464087b9d6614c8125121c12907c843ab92454",
373
+ "97a65e3463ae8217f48e88aa3d172fb5845640c3f93ee35548fde157b180fec2",
374
+ "9f36c2f08160e774a3cd6fad9e7d715331eecfe2c755edca0346729467b15c1d",
375
+ "b4f6e05ee2042d4f02b0c9c41764e781243ac337996f77f23bf22783d2aabaac",
376
+ "cb527d39a8b260a36b2fba7ebe904b4c5acbe63e84859e74c89fd3c41668f81e",
377
+ "f292be6b6c74491810c485ce00a8f05b4728d97667bfc220327011873ff3fcdf"
378
+ ],
379
+ "devlyn-product-spec": [
380
+ "5e609db2d0b25a1c2bb9e64280d8e989a510ffa3c13cc143f2fe40c3ceb95b27",
381
+ "bd50c281381a9cf82b8557a627b1a71edc0998322546b94348c3914ee3a9a88f"
382
+ ],
383
+ "devlyn-queue": [
384
+ "1242612c1d223a2e1fc530264a04cf81d9798e060b295d68d6ced3e7ef194567",
385
+ "203fc2ae062b3679b7fefaf34b21da55cf7367e91b5d260b808f9e0e152e520f",
386
+ "2f1146f31cb40f2bdd907ea45c6b1c38531118bd8eecd15d4f0d9dbfccde52ed",
387
+ "48a0320262fac2ce77673c80c9eb93e66b5587bf095208234c239dd65c817578",
388
+ "49adb3b8aac1e7c303fe27b1288c7025be8190fc1d977b74ff14b533ff5dfe23",
389
+ "4c7d988dcd154c7bf658d2d24cd7868955df9d854ed87eb784742d6d4bb229e0",
390
+ "a52c0c2d286e74b9f77e42d8f505966c07c2b9c8a5dabd365f5dd62fb641f869",
391
+ "b33f5ccace0783d86bc1e80172783507ed37450e9cc9d52cb5aff7c0b5ec9710",
392
+ "ceae023b0086769168d4852a7bce91beecc30d71c20473c4277dbd06ff55da95",
393
+ "ebcc6c9ae3d2aae56f032e9e3955326f5eb85980af3fb65429517261826f6d60"
394
+ ],
52
395
  "devlyn-reap": [
53
- "77e029ab06c37fb7135a9bd4395ff84b84ab2e44cf79c5827798777e39472351"
396
+ "144b9a7ebf9e73a6aac637475e3fb872a9f73b559a15f79be846540cbbfc72dc",
397
+ "2acced1a5f198a2c2436cfb4e956443b680462790725a646c753fec52fb86110",
398
+ "60fb8b62ec932f5bbb903890197e2f0f9685251505ed080dc6de7aa14c3bfaf9",
399
+ "77e029ab06c37fb7135a9bd4395ff84b84ab2e44cf79c5827798777e39472351",
400
+ "f143265739e468a60211ed4a63eb3a10fb53951d1a42b8fad2182627ffe49116"
401
+ ],
402
+ "devlyn-recommend-features": [
403
+ "c94d0083e1f6ded1f7f812a915a67c8e0e5847a13ef1993a0fc0ece5dcc2815c",
404
+ "d6fce1f3f196d077c501c339ce3ed17a80b194ff5c4e54f8de9f9c00f2e4dc8f"
405
+ ],
406
+ "devlyn-resolve": [
407
+ "01702d31a7d5ead532698b3a5a7eff145507ec1728a0fc50295a46fb7b3b5d36",
408
+ "03d40787663dd99c3d1999d25320cec461dc8bce4135bacdf81a4deb99e0e4ca",
409
+ "09f101a0900ce3edfb4b7904e8dee61aef4ba22586c51fa2675d2c0884bf6a91",
410
+ "0d11167e5d6d0b0b34b1f717049a0576eaf5a4a3da9cf7a9ca9bd0c75ef75134",
411
+ "110aa4fbcc1fbffc5eef5ffc0dfe4b4b782d133f405da4f8b68a853416ca426c",
412
+ "15b30c632352d00e9abe060c91c2c852ccc820cadfd2e829484aae2cf77baa94",
413
+ "160fc3ddc61df74173ea31084b3d2d20f5d9e52c46714ed67afb44955a3da7ec",
414
+ "16dc1b68874d65530ee8f959a1f8244a30ff56969659d2ed1e3b02c7e186580b",
415
+ "1a7205a026d1b6b3830aa7e1bc9f8549c108cd136206b967c25e9fabaaf6daea",
416
+ "1edf4bec83bebf65a3cfa82d9c5b8d9d7e1793d4d6ac4f40aae62fb1b4b732ea",
417
+ "20b13a6e8d3c43e2d89dbc4c44f9f7b10e2466c6081c0c78a59bf8d1cb8ebaa5",
418
+ "211c32706fbf7f12ba162be62270e3a4a4a1646cec7d72bcb958c62ccf2326c0",
419
+ "23809d13f09d95093f360bfd7d699e191a4914ec23e17d705882cb4555c06328",
420
+ "25bf863cde2f8d01f8229d6722bdc7086610c522c8a89c82216b5292bb992a4c",
421
+ "27059d51a1f47c28eb30577379d443c470e19591191b091b383d477a39103ab4",
422
+ "284815f7863dbf502d30630d1027d89754d56713180a44790d65dd2ff8f5efe0",
423
+ "2abd303851f085da07f6b55ee1724842ef7f8e8ad6b68c6339df5f1eaa8c69de",
424
+ "30877650a2403ea7cb129ae92cdc120e50f32b498fff47b642b77ffd32d32a24",
425
+ "3298e41ee71e89bdc00cebca279c02ff8e12545f0b1e88c367087d235a5d39c4",
426
+ "33270cc6ab78351d513b9cf32fc462d764b61c6c4bdb4c20522323469e5237f7",
427
+ "348d98a8ff2d7c0880d4833d5e4f2eda15c22b0c9a38028905e1b05f24f3ffc8",
428
+ "36e3bc1d0ac8b0fdfa8bfbcddac76ec3ff66f8722def4b8bf88ff9ebdd53f442",
429
+ "370eeb6c9295e7d8505e78c0e80f0061aa44d07676086bbe7b99572e461b1760",
430
+ "372c8f673c962153e6022702c0a039a933baf23dbdcb1b81d4b22d834d37f4cd",
431
+ "384b24fe08291b498c41a20d7baaf9d57bec06402af11f818e280356b65cfab4",
432
+ "3fd556ebb5ef40abc3d51f91f648fc9f050d8d4ea010af8732a4d27875c7d4c9",
433
+ "400e486a28b5921e2b5db29a6e68b2c3c275ee204e1ff2d808afe91dd5cc8894",
434
+ "401fd3820214e38a9efcbd0a683128a4a266e0ffdd23006e9abcdb3d97e8ab79",
435
+ "4e8a4da4c25100b3dbd938e21fe006fdb3144c6c25d35f2f89bb50c889950632",
436
+ "52d809ca192ef52955fd1e7931c3dda75301cd0a40abd9a4f4e33cd5e9b13a57",
437
+ "54201b9c695fbbf29f76866e2679729978ca5169b42a601156793c20452cae61",
438
+ "5692babc471959cc541ef5aedaad76693bcbfb98f0da60a0b6660923dba6818b",
439
+ "5d027085a8f970a2a3688d4155ef539fbe91a8dc31e4c922a95af956275b4945",
440
+ "5f05ca509a8b547908f82a7620b93a38ba1fac165ee068bd49bb45f6840840bb",
441
+ "60d8ad24ef88f521de80abad066e8d39b8eba97684436d13192f4e67cbea112f",
442
+ "62b656b6e444b4108326099de7693eea19e2524168d6060e8fd2f6cbca74f699",
443
+ "63ffe59f616225721db4b0ce01dc98fe1158c88a016fdca5877fe9d08ed6c28b",
444
+ "66566540c47d592259b82c6514cd4e59d1f7abf09b3174a9a793ee2cb958d5b1",
445
+ "689d275f39790ec981d6fbd0486fbeef253777e5817cf71e7a81ba13d5224024",
446
+ "68f8ca1dab6bcb38092c9be61b20697ee2dd523fbd6e47ade78e6214406b3520",
447
+ "6c2cd056d4938fa9c65305ae41a5544094854e1a57774a8e156b3dd9429337cc",
448
+ "70137f2168b0bdd0d1d1ffe067872c8fe7c85774ce5a78d1a925f9a04020d61f",
449
+ "70b0a515ec8aa1d5488381430c45446f481cfe27ab3f743e6175ac4ae3a81943",
450
+ "735064a655d56cc7f5b30b00ec67fea6ac1122c8915c7ff3326a33c76c6df5e3",
451
+ "752717d1686a4ceba54a041e7a691500a74d5836d5cc8936a87da042bbbadb10",
452
+ "7a3ffdc06f55574ebef129c96b685fdffb6a1826d3549cde98fa584a84ada821",
453
+ "7c0cda80c98bd7c44ea480078b979eba6f01dade24b7e670480b6369304ff8f1",
454
+ "7dc780ceb4d6c457216dcc3b88a19b931f95cd4b5923e1151d6b819e0627cdce",
455
+ "801d3122663a340e1b2206d59c4ecf381d7a52685e0e7701027143b0ffaa6ee0",
456
+ "82ab2a86b71324bd5f84022d42d0ff95c14b2816a256eee164f22e8605983d9f",
457
+ "82bb737462606f840cc91f264327d3333bb2b605878a903da9487eed52b71450",
458
+ "833500869b08f100a4fe862039202a6d8486b93d8b3f30ddd1bcfe71fedcd7d1",
459
+ "84058c047ae3b3600a838fcab03828220968024fb9c8246baf17a2d2bfe440f9",
460
+ "84705a6affb8e81c8222a01e17eac82d42131045125a0e93e86c70cec7b194a0",
461
+ "84b134617782b8223c561ea7f1c577fa6a0ac1c2bcb37ada71ca350e28909e16",
462
+ "84f68d5096bf18ea3f3b97d41f23ba3b7cb5888198d61932cd3a10ad21bfdf80",
463
+ "87fad5c5f4267c1e72558035b33cf1f203167d0780f613b18175d49ea3dfb992",
464
+ "8b6cad6c0651eebbaf6c454362c7c7611ce3b69e380bd2bc1616a6441f6875a1",
465
+ "8cd08568d6a2283710b32dee789076b77d5f01a7d6fac373d9735d97308a74b1",
466
+ "9030fb808648dc70baa4717138fa22f24120d578b54c0c1c7b904af6158272ab",
467
+ "925ce0a3e71cbebef781874bfab22ae43ab0e4665096c23630999c2c12eebce7",
468
+ "925e801d9952f8f0add1b9e858e1e4a2786881535bb05da861ba659f26f237b2",
469
+ "93f7586931f55f4a0d8aad66c13bce6575fe2af0500d159e33994e999cddb4e6",
470
+ "942bc01ae0ed87244c152df184c44e19f265c9ed83db2199fca8c82a37fa8985",
471
+ "95b1d1d8b57cbfefbc5c0df32bf29f1c4b60dd02f75aab29b012cbfcc10f6467",
472
+ "972f68cf68bd6f525ee1f534cc959f346e2dc457ed6e8eebcd41d91b12b2eb17",
473
+ "973ddb7d1732c15dfecb9bb2b56fe60d61474be20d8e878ee69e1da05b161385",
474
+ "9758e41ec4a0ecd5fab02f010593f0e338a2cf4dfd299c0478f3d43527e7b705",
475
+ "991d22a37b5d139cc6b958cb057a010844713e879841f33dfca26d676ed0e929",
476
+ "9a49f2c92ce500ab9c0c52fd425ab5f3697151ceef88031e49c80759baabb0a3",
477
+ "9addcefbef639a101c59eb498ee544cff862af44b205b473c2333dc7ccf3cb7e",
478
+ "9c31ab93f926724f89fe36c1ec3f001166d483f72cbddd3d14b5e3152139295d",
479
+ "9e59517b8719bb6e8a715379a2439e72813e6e2b74aeea04b73bad09db6771c9",
480
+ "9fb4bc1d509d29a47a65c5aaf6de1cca6e036033f3b2cef2251ccb45a74116ca",
481
+ "a69b07abeb6b11edc7b028f30050b388e09923186690cfe6acb4e5f55f0a6ed7",
482
+ "a7278fbd93901be4ed7f51e8344341d1d9a412a6c5bd506e5bc5bfc9243bd352",
483
+ "a74485c7ec466ce52214bf030ee3f90c1f241214437b91a08401a7fc30d45a8f",
484
+ "a8b6776667014a8d4de19c6e41d5a17a66ca9090d9d9dc5941f67864f5b27713",
485
+ "ad1debd17843e0b07214b2800040acbbe19a6db3e0171fa330037073cf84037f",
486
+ "af243ffccd5355a32e7d3ee02395b541f1ba3d2264fdb4dc1bc1266ba0a87022",
487
+ "b3f01b86baaa4b90edc64da8257ae962f0348f8ed206b9b9609bc03c95f37a97",
488
+ "b5bc547941a67939891ff10e6de445a51db15a96baabe607d7f09aaa14f942e4",
489
+ "b8a88cf53c85f0837254dc69c60305d9d18e74da840ecf1ea94a912c49ebd8af",
490
+ "bd96fd79e809f5af31eba882a0884519bb5f36f2a0bf68a1a8a9e51882d98e14",
491
+ "be802f043ab3a89ac63c2cfb4b978f8567be7372f4afc2400e5d71fa1f6a38ff",
492
+ "bf56c2e9ac13b96770c02c1c7dcc27efe15805d5c02f86a0c58c2b162a3dda08",
493
+ "c125c8d084d4d9a904cbfb23575ab918dad74199b05d1b18e1e0ec7eccb42ac4",
494
+ "c24cc985f520ff9519cc0b911fa79b889bbb1d23d03ee0a873bd5298eab1e829",
495
+ "c26894b25824f18670451642f17549da5c67df7c0004ce7337e75d7df6c16fc0",
496
+ "c39efc98f2fceec9697e6980c1cb91adf21c5e2537c264c17aff12be4a0aa47c",
497
+ "c52176b119aec9acfd206ca42c5b8cbc11ae6e485801ff36dc2a946e0cf5da37",
498
+ "c57be040e3ad9d1784825c6866870f950056e53d86109c2bc7ce86bf41b496d2",
499
+ "c5d4b8122e33b20e22f37f6abc33d56e7653c502f14fb2c07fbbe5a2a56e2d13",
500
+ "ca42a46adf012ef258a0efb6f66b467cf7e0c191cc65f0f19f48574406e9b1c9",
501
+ "cb0cb013cc353aef6579d9c6d84e6c67060c28734665354c0a5a03617ae9285f",
502
+ "cb2cc49d0c4f8b7e1f42e29a0a2f228e35612dd9278832d18f5e60c704553875",
503
+ "cd4b6831710d7022c9db0d941a7a3a01b5f0092efbc864f6a74b58767a20ea47",
504
+ "cdccb2927897ebe30cb395ad31db015740acbbb9d607395efedd35ee34d30fdd",
505
+ "d037c7deb835b8ef4e3642bcd30beebcccc8d1a8b3d61b9a0694026127ce9665",
506
+ "d0772b6818f51e85a40d37ff981a2604f5d611118f20c3ed56b5b406e2d211d3",
507
+ "d0d982ca1c33abc325c250eb86623e8be292e42df73fa60eb197705cf7df005c",
508
+ "d2f54df82c7948a199e97ef08c5037f9ac3151c10a7348f7dc54c08760ddfd83",
509
+ "d7651cda916794f3d2b0516f60836d003616fe4c10305063bb10a6f985f2f9dc",
510
+ "d8dd2d8629123c0e80c3984b543b83e2b7cc98f1928b937cce2e183999c85eea",
511
+ "db0eca6c16b79f7039499ebf27dcd17cc3b159bbd7d0929f0329faaeacbb21ee",
512
+ "e07dd38be667bcfcf54c5916ab5857a8bd59062b1b49c17f9d4a1821f3a04e3f",
513
+ "ea6483ccf1535207bacf629a5f982aaf92daf1ccded1eeac103a8cc63f2a156f",
514
+ "eda8910d82aa074e91c9a5488320881d4d4c19b17b5650c282118bd16421c703",
515
+ "f09835f247165972f904a3ef2b7d176f10bf24f8f31538439dc78d7c3025c1e9",
516
+ "f4a830aca103e81efbc158fd698a832be28793b7540f802ccd7b2e086c0f3cf3",
517
+ "fd35dbb475048cdf16bd4cc4f5da8e5ba1d9dacd314627244c20c1a26dc5c6ad",
518
+ "fd367cc71a7c2fb66464091fc98b304eeb685fbb6e16b5ac55999de249f642a2"
519
+ ],
520
+ "devlyn-review": [
521
+ "1dc259e54bf71d9044f42b29e1a780df15bba9c8e30f08bfb18ef1fcf30ecc2e",
522
+ "5972f4cb670d8636e6934a920d19edb7bc9a557c348878230be0747c6feb7504",
523
+ "a11c2b961df10f8e974bcad005e34370896bdbd88cfca5c88e28dd80f2ada30f"
524
+ ],
525
+ "devlyn-team-design-ui": [
526
+ "53ac4c619aac1ca195b82d43f03399d4883d318a749cdc8618bb1d7eed0fc3b4",
527
+ "791a231ba0dce17ec626ebaf7384dee06d26954a0fb9b7b750880da2c1742315",
528
+ "839cb03838f56414ab3a6c20d03556237e8e1bfa71d9a7292f4469b4bf2a21d6"
529
+ ],
530
+ "devlyn-team-resolve": [
531
+ "1fd77e99787fc1f89c84c119b4192c01e46f80fdc9c343dcf8c3d44ef8beac5e",
532
+ "27c758e1c3bb9b4eed0da6ec07754b9baf16d5dff9e0745ab6bb1b63e4fe795a",
533
+ "3061527c5bb2c480f670ad0562cbaf06c1cc3c32703f1c2819b30fbd54f6b961",
534
+ "3d1af5cc99fbf257c35097dd981691cc767bda0ccbc934abf4f0bcfe861ad69a",
535
+ "8c2fca45205856a5fd78400ba1b418c64821dc83de4034357d6940eee5906c81",
536
+ "a7534b57054afa56b627361dd25462e2122e0767610d2a69d4b52c9487db5699",
537
+ "c0d0439a4c7cc9da982a75ccf0024c7a2f3103bdbadedc985d291696eec6bf37",
538
+ "e60e1376b90bbc39898b47f6732a64d6d7cd6547127746a8a312ae54c25b79f4",
539
+ "fa76b9a5f1d608d434753b522c81fbe7ab2665d066ef28ba262a6b466e0fcb82"
540
+ ],
541
+ "devlyn-team-review": [
542
+ "2932d54e3838f1e916cdf2ebdbd0a9d20324e432c5d909d6f0757137d314ed9b",
543
+ "47fc20ee5d909be07871a06eb23e655e6033a40edc82de9f071b77ac2c89efbb",
544
+ "5038d93be92b0975a36a4387886901bcb1992919cf6d27f4e31b520f44179c91",
545
+ "52c2d7953ae1448c1fa563e2574720a0d4bb4013bf85f7668a7201780568c023",
546
+ "5becf3b534c1e8526e99c959c9c3a4b7f8fcf8c93eae5869be3a7c50e737b735",
547
+ "dd85a9f8b1dc7d60b5859f4bad4af9e8a939720cca815dc75aaf50db8a6fef5e",
548
+ "ec556b38246f5609357a0a0cd9788acd80e40d9fad6b6be3c1e9ab046b400570"
549
+ ],
550
+ "devlyn-update-docs": [
551
+ "3aead216c40294c57745529669f033de74f19a8706aaeb768f204a9f8b267c63",
552
+ "640211292f80571ffe0a1dbfcdff40f3ad9261ad8888cb174460036f74aa72d4",
553
+ "918cf5f4c0124c882122ee714836698a435797016984e95e3f31ac205f2f2910"
54
554
  ],
55
555
  "dokkit": [
56
- "42045db4e2f70f68d0ed90a9c7c5c2660a6d5183ea641dfa9f2f1f0d0938e3fb"
556
+ "3fd2a6322cb1568ccf4c9e3dca142b35eddf1d20e7ae569942be8f0463a8e172",
557
+ "42045db4e2f70f68d0ed90a9c7c5c2660a6d5183ea641dfa9f2f1f0d0938e3fb",
558
+ "59c331161b355226ef241c95b70ee46a447999c7445c96bd1137a218707723a4"
559
+ ],
560
+ "feature-gap-analysis": [
561
+ "782fbbe850e0f846c90b97bc1df57be1c9b95be6d70ee4c8cc4b55c871e027e5"
57
562
  ],
58
563
  "generate-skill": [
59
- "16a78b867539c0bc11a44f3ecfa1762ba7fcba8841fca8558abe818fc961ff68"
564
+ "16a78b867539c0bc11a44f3ecfa1762ba7fcba8841fca8558abe818fc961ff68",
565
+ "ab9ef07595e303b1cd1dcc6f48a30831b471b77c3028f78e30e16c4b0f5aa3e2",
566
+ "f92dea36b421df6673a036c2573940f3d5bf3f9065ca157e10cd58abcbf1698c"
567
+ ],
568
+ "investigate": [
569
+ "e2de5290cbc19d37e37bf8ef470e21f18042f7dfbdb853bf9bcc38755790321d"
60
570
  ],
61
571
  "polar-billing-setup": [
62
572
  "8b6dc842903e7343ac7b1e3a1c55911a9987552196ffb27a11a86093537fe976"
63
573
  ],
64
574
  "prompt-engineering": [
575
+ "0df08f86d277f9f684e04e083023bfe539d44db38e3171250a7f18ea57fc6727",
65
576
  "d7a0718ab0fd7e0a3c0d8ef8bc4e0349edfc843648343f08f9776e1ea047573c"
66
577
  ],
67
578
  "pyx-scan": [
68
579
  "60f02fa049616e94fff4318c96dc9f417b3def7ec75a3bd0846f2bcbd99b0b69"
69
580
  ],
70
581
  "root-cause-analysis": [
582
+ "03026c720a3451226fb20c8a0af65c35e58f3653e97521c887e5bdd9e4302dfd",
583
+ "08ddd7044848cd55b9e40207e9b5f15c6eaec4a4a3cc5cb68961f0eb5a13e305",
584
+ "224b8daf20bc71a608cc378142c09d63a183f0989fd03a779d914b14ab5d9f68",
585
+ "5f538c532d2bc060453ca9ab6725030c0afbff9fecd1b8dc41c807c3a29ad3db",
586
+ "ac2b94bbfbdf30bbe8d2f154f46346f4709846f6a5325ee4134869fac2bb6e82",
71
587
  "eeb20fe09edd37b07360c3440b68bdb488abee4917d4e808e64e3889c6addf5a"
72
588
  ],
73
589
  "ui-implementation-standards": [
74
- "0fbaad2b7d77462916d9364b97b921354ffbf945a68e9e0d422b3a88d36d2569"
590
+ "0fbaad2b7d77462916d9364b97b921354ffbf945a68e9e0d422b3a88d36d2569",
591
+ "2472fc5b598a6ba3e2692cf1d9f775b27b4b6669d51c8282d0be3273c7b4a9c4",
592
+ "3cbfd9ec0ece1c196fc5aaf4558fa0b40926ac957fbc0c426732f92d7827a491",
593
+ "8c162e0d8b44a69b22b5d17e0c305ce027c7323cb535d947f767bf339c8cc054",
594
+ "a8f768fb0ac838c44221c4656eb8c3ebb12a29f13c8ee9b34aef9180c27ea775"
595
+ ],
596
+ "vercel-composition-patterns": [
597
+ "9f73c0e78ca790c0d964aba5a40aff93c1f745a3c5481e6a1e356f09f3931075"
598
+ ],
599
+ "vercel-react-best-practices": [
600
+ "18153870565cabc4f1d134820732c975694533e3f8634ad16d113795f5f562e5"
601
+ ],
602
+ "vercel-react-native-skills": [
603
+ "acfb861607a25acc76a20da9e4f26b766a3b72c5d7522de2e8f2fbabb231af1c"
604
+ ],
605
+ "web-design-guidelines": [
606
+ "288ad79d921362a232c491e2df598c5e1dd544e3f3969b0f42d605ffa81f7dad"
607
+ ],
608
+ "workflow-routing": [
609
+ "711cd2633df2062104f12d50037a07a1012a90ab18c422cb5fdf665fcb9dc987",
610
+ "d6a98c105a0d1952de90c39ca0734cb70cbdd116e6b4a4d45507d796bd2a9e88"
75
611
  ]
76
612
  },
77
613
  "commands": {
@@ -3,21 +3,33 @@ const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { spawnSync } = require('child_process');
5
5
 
6
- // A folder-metadata file the OS writes on its own (Finder, Explorer); never user content.
7
- // Only a regular file: a folder or link under that name is still the user's.
8
- function isOsMetadata(full) {
9
- return ['.DS_Store', 'Thumbs.db'].includes(path.basename(full))
10
- && fs.lstatSync(full, { throwIfNoEntry: false })?.isFile() === true;
6
+ // CPython caches contain only regular *.pyc files. A mixed directory is ordinary
7
+ // content; the fingerprint walker below still refuses links and special files.
8
+ // No published version shipped standalone *.pyc files, so those count as edits.
9
+ function isIgnoredEntry(full) {
10
+ const stat = fs.lstatSync(full, { throwIfNoEntry: false });
11
+ const name = path.basename(full);
12
+ if (stat?.isFile()) return ['.DS_Store', 'Thumbs.db'].includes(name);
13
+ if (name !== '__pycache__' || !stat?.isDirectory()) return false;
14
+ return fs.readdirSync(full).every((child) =>
15
+ child.endsWith('.pyc') && fs.lstatSync(path.join(full, child)).isFile());
11
16
  }
12
17
 
13
18
  // SHA-256 of a file with CRLF read as LF: Git for Windows checks text out with CRLF by
14
- // default, and an unedited clone must still match its shipped copy.
19
+ // default. 2.8.0-4.0.0 stamped only the exact assignment sentinel in .md files.
20
+ // Accept only shellDoubleQuoteDefault's escapes and an absolute original path.
15
21
  function contentHash(file) {
16
- const bytes = fs.readFileSync(file).toString('latin1').replace(/\r\n/g, '\n');
22
+ let bytes = fs.readFileSync(file).toString('latin1').replace(/\r\n/g, '\n');
23
+ if (path.extname(file) === '.md') {
24
+ bytes = bytes.replace(/\$\{CLAUDE_SKILL_DIR:-((?:\\[\\$"`}]|[^\\$"`}\n])+)\}/g, (match, escaped) => {
25
+ const original = escaped.replace(/\\([\\$"`}])/g, '$1');
26
+ return /^(?:\/|[A-Za-z]:\\|\\\\)/.test(original) ? '${CLAUDE_SKILL_DIR:-__DEVLYN_SKILL_DIR__}' : match;
27
+ });
28
+ }
17
29
  return crypto.createHash('sha256').update(bytes, 'latin1').digest('hex');
18
30
  }
19
31
 
20
- // Hash every entry, including empty directories and dotfiles, except OS metadata. Links and
32
+ // Hash every entry, including empty directories and dotfiles, except generated metadata. Links and
21
33
  // special files are never installer-owned.
22
34
  function fingerprint(dir) {
23
35
  const entries = [];
@@ -26,7 +38,7 @@ function fingerprint(dir) {
26
38
  if (stat.isDirectory()) {
27
39
  entries.push([relative, 'directory']);
28
40
  for (const name of fs.readdirSync(full).sort()) {
29
- if (isOsMetadata(path.join(full, name))) continue;
41
+ if (isIgnoredEntry(path.join(full, name))) continue;
30
42
  visit(path.join(full, name), relative ? `${relative}/${name}` : name);
31
43
  }
32
44
  } else if (stat.isFile()) {
@@ -40,15 +52,20 @@ function fingerprint(dir) {
40
52
  return crypto.createHash('sha256').update(JSON.stringify(entries)).digest('hex');
41
53
  }
42
54
 
55
+ function gitEnvironment() {
56
+ const env = { ...process.env, LC_ALL: 'C' };
57
+ for (const key of ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_COMMON_DIR',
58
+ 'GIT_LITERAL_PATHSPECS', 'GIT_GLOB_PATHSPECS', 'GIT_NOGLOB_PATHSPECS', 'GIT_ICASE_PATHSPECS',
59
+ 'GIT_CEILING_DIRECTORIES', 'GIT_DISCOVERY_ACROSS_FILESYSTEM']) delete env[key];
60
+ return env;
61
+ }
62
+
43
63
  // Use the index, not status: even files deleted locally need ownership checks.
44
64
  // Inspect the repository containing this root, including global dotfiles repos.
45
65
  function trackedPaths(root) {
46
66
  let cwd = root;
47
67
  while (!fs.existsSync(cwd)) cwd = path.dirname(cwd);
48
- const env = { ...process.env, LC_ALL: 'C' };
49
- for (const key of ['GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_COMMON_DIR',
50
- 'GIT_LITERAL_PATHSPECS', 'GIT_GLOB_PATHSPECS', 'GIT_NOGLOB_PATHSPECS', 'GIT_ICASE_PATHSPECS',
51
- 'GIT_CEILING_DIRECTORIES', 'GIT_DISCOVERY_ACROSS_FILESYSTEM']) delete env[key];
68
+ const env = gitEnvironment();
52
69
  const git = (args) => spawnSync('git', ['-C', cwd, ...args], { encoding: 'utf8', env });
53
70
  const repo = git(['rev-parse', '--show-toplevel']);
54
71
  if (repo.error?.code === 'ENOENT') return [];
@@ -64,4 +81,4 @@ function trackedPaths(root) {
64
81
  return files.stdout.split('\0').filter(Boolean).map((file) => path.resolve(repo.stdout.trim(), file));
65
82
  }
66
83
 
67
- module.exports = { contentHash, fingerprint, isOsMetadata, trackedPaths };
84
+ module.exports = { contentHash, fingerprint, gitEnvironment, isIgnoredEntry, trackedPaths };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devlyn-cli",
3
- "version": "4.2.6",
3
+ "version": "4.2.8",
4
4
  "description": "Loop & harness engineering for AI coding agents (Claude Code, Codex, and any AGENTS.md-driven agent) — core engineering principles for CLAUDE.md/AGENTS.md and a loop designer that splits one intent into tasks agents drain hands-free",
5
5
  "homepage": "https://github.com/fysoul17/devlyn-cli#readme",
6
6
  "bin": {