()
| 80 | } |
| 81 | |
| 82 | async function checkAssets() { |
| 83 | const drift = []; |
| 84 | |
| 85 | for (const dir of dirsToSync) { |
| 86 | const sourceDir = join(sourceRoot, dir); |
| 87 | const targetDir = join(assetRoot, dir); |
| 88 | |
| 89 | if (!(await exists(sourceDir))) { |
| 90 | drift.push(`missing source directory: ${relative(repoRoot, sourceDir)}`); |
| 91 | continue; |
| 92 | } |
| 93 | if (!(await exists(targetDir))) { |
| 94 | drift.push(`missing asset directory: ${relative(repoRoot, targetDir)}`); |
| 95 | continue; |
| 96 | } |
| 97 | |
| 98 | const sourceFiles = (await listFiles(sourceDir)).filter((f) => !isExcludedAssetFile(f)); |
| 99 | const targetFiles = (await listFiles(targetDir)).filter((f) => !isExcludedAssetFile(f)); |
| 100 | const allFiles = [...new Set([...sourceFiles, ...targetFiles])].sort(); |
| 101 | |
| 102 | for (const file of allFiles) { |
| 103 | const sourcePath = join(sourceDir, file); |
| 104 | const targetPath = join(targetDir, file); |
| 105 | |
| 106 | if (!sourceFiles.includes(file)) { |
| 107 | drift.push(`extra asset file: ${dir}/${file}`); |
| 108 | } else if (!targetFiles.includes(file)) { |
| 109 | drift.push(`missing asset file: ${dir}/${file}`); |
| 110 | } else if ((await fileHash(sourcePath)) !== (await fileHash(targetPath))) { |
| 111 | drift.push(`stale asset file: ${dir}/${file}`); |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | // Sub-skills (text content only; fonts/binaries intentionally excluded) |
| 117 | for (const name of subSkills) { |
| 118 | const sourceDir = join(skillsSourceRoot, name); |
| 119 | const targetDir = join(skillsAssetRoot, name); |
| 120 | |
| 121 | if (!(await exists(sourceDir))) { |
| 122 | drift.push(`missing source sub-skill: ${relative(repoRoot, sourceDir)}`); |
| 123 | continue; |
| 124 | } |
| 125 | if (!(await exists(targetDir))) { |
| 126 | drift.push(`missing asset sub-skill: skills/${name}`); |
| 127 | continue; |
| 128 | } |
| 129 | |
| 130 | const sourceFiles = (await listFiles(sourceDir)).filter((f) => !isExcludedAssetFile(f)); |
| 131 | const targetFiles = (await listFiles(targetDir)).filter((f) => !isExcludedAssetFile(f)); |
| 132 | const allFiles = [...new Set([...sourceFiles, ...targetFiles])].sort(); |
| 133 | |
| 134 | for (const file of allFiles) { |
| 135 | const sourcePath = join(sourceDir, file); |
| 136 | const targetPath = join(targetDir, file); |
| 137 | |
| 138 | if (!sourceFiles.includes(file)) { |
| 139 | drift.push(`extra asset file: skills/${name}/${file}`); |
no test coverage detected