@skillsmith/core 0.12.2 → 0.12.4

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 (285) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/README.md +2 -2
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/config/owned-lock.acquire.d.ts +8 -0
  5. package/dist/src/config/owned-lock.acquire.d.ts.map +1 -1
  6. package/dist/src/config/owned-lock.acquire.js +19 -4
  7. package/dist/src/config/owned-lock.acquire.js.map +1 -1
  8. package/dist/src/config/owned-lock.claim.d.ts +4 -2
  9. package/dist/src/config/owned-lock.claim.d.ts.map +1 -1
  10. package/dist/src/config/owned-lock.claim.js +42 -8
  11. package/dist/src/config/owned-lock.claim.js.map +1 -1
  12. package/dist/src/config/owned-lock.d.ts.map +1 -1
  13. package/dist/src/config/owned-lock.js +1 -0
  14. package/dist/src/config/owned-lock.js.map +1 -1
  15. package/dist/src/config/owned-lock.test.js +126 -2
  16. package/dist/src/config/owned-lock.test.js.map +1 -1
  17. package/dist/src/config/owned-lock.types.d.ts +9 -0
  18. package/dist/src/config/owned-lock.types.d.ts.map +1 -1
  19. package/dist/src/db/createDatabase.d.ts.map +1 -1
  20. package/dist/src/db/createDatabase.js +0 -2
  21. package/dist/src/db/createDatabase.js.map +1 -1
  22. package/dist/src/exports/services.install.d.ts +1 -0
  23. package/dist/src/exports/services.install.d.ts.map +1 -1
  24. package/dist/src/exports/services.install.js +5 -0
  25. package/dist/src/exports/services.install.js.map +1 -1
  26. package/dist/src/index.d.ts +2 -1
  27. package/dist/src/index.d.ts.map +1 -1
  28. package/dist/src/index.js +3 -1
  29. package/dist/src/index.js.map +1 -1
  30. package/dist/src/install/fan-out.cleanup.d.ts +37 -0
  31. package/dist/src/install/fan-out.cleanup.d.ts.map +1 -0
  32. package/dist/src/install/fan-out.cleanup.js +75 -0
  33. package/dist/src/install/fan-out.cleanup.js.map +1 -0
  34. package/dist/src/install/fan-out.d.ts +41 -31
  35. package/dist/src/install/fan-out.d.ts.map +1 -1
  36. package/dist/src/install/fan-out.js +213 -112
  37. package/dist/src/install/fan-out.js.map +1 -1
  38. package/dist/src/install/fan-out.leftovers.d.ts +60 -0
  39. package/dist/src/install/fan-out.leftovers.d.ts.map +1 -0
  40. package/dist/src/install/fan-out.leftovers.js +251 -0
  41. package/dist/src/install/fan-out.leftovers.js.map +1 -0
  42. package/dist/src/install/fan-out.locks.d.ts +12 -0
  43. package/dist/src/install/fan-out.locks.d.ts.map +1 -0
  44. package/dist/src/install/fan-out.locks.js +114 -0
  45. package/dist/src/install/fan-out.locks.js.map +1 -0
  46. package/dist/src/install/fan-out.manifest.d.ts +64 -0
  47. package/dist/src/install/fan-out.manifest.d.ts.map +1 -0
  48. package/dist/src/install/fan-out.manifest.js +130 -0
  49. package/dist/src/install/fan-out.manifest.js.map +1 -0
  50. package/dist/src/install/fan-out.overwrite.d.ts +51 -0
  51. package/dist/src/install/fan-out.overwrite.d.ts.map +1 -0
  52. package/dist/src/install/fan-out.overwrite.js +343 -0
  53. package/dist/src/install/fan-out.overwrite.js.map +1 -0
  54. package/dist/src/install/remove-if-same.d.ts +58 -0
  55. package/dist/src/install/remove-if-same.d.ts.map +1 -0
  56. package/dist/src/install/remove-if-same.js +222 -0
  57. package/dist/src/install/remove-if-same.js.map +1 -0
  58. package/dist/src/provenance/backfill.d.ts.map +1 -1
  59. package/dist/src/provenance/backfill.js +15 -2
  60. package/dist/src/provenance/backfill.js.map +1 -1
  61. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +9 -1
  62. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  63. package/dist/src/scripts/scan-imported-skills.d.ts +7 -1
  64. package/dist/src/scripts/scan-imported-skills.d.ts.map +1 -1
  65. package/dist/src/scripts/scan-imported-skills.js +15 -2
  66. package/dist/src/scripts/scan-imported-skills.js.map +1 -1
  67. package/dist/src/scripts/skill-scanner/allowlist.d.ts +10 -0
  68. package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -1
  69. package/dist/src/scripts/skill-scanner/allowlist.js +10 -0
  70. package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -1
  71. package/dist/src/scripts/skill-scanner/index.d.ts +8 -0
  72. package/dist/src/scripts/skill-scanner/index.d.ts.map +1 -1
  73. package/dist/src/scripts/skill-scanner/index.js +13 -5
  74. package/dist/src/scripts/skill-scanner/index.js.map +1 -1
  75. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
  76. package/dist/src/scripts/skill-scanner/trust-scorer.js +6 -0
  77. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  78. package/dist/src/security/scanner/SecurityScanner.action-context.d.ts +15 -0
  79. package/dist/src/security/scanner/SecurityScanner.action-context.d.ts.map +1 -0
  80. package/dist/src/security/scanner/SecurityScanner.action-context.js +209 -0
  81. package/dist/src/security/scanner/SecurityScanner.action-context.js.map +1 -0
  82. package/dist/src/security/scanner/SecurityScanner.d.ts +25 -0
  83. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  84. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +40 -1
  85. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
  86. package/dist/src/security/scanner/SecurityScanner.helpers.js +88 -51
  87. package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
  88. package/dist/src/security/scanner/SecurityScanner.js +39 -6
  89. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  90. package/dist/src/security/scanner/SecurityScanner.pii.d.ts +30 -0
  91. package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
  92. package/dist/src/security/scanner/SecurityScanner.pii.js +8 -1
  93. package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
  94. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts +60 -0
  95. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts.map +1 -0
  96. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js +228 -0
  97. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js.map +1 -0
  98. package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
  99. package/dist/src/security/scanner/SecurityScanner.scanners.js +42 -5
  100. package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
  101. package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts +69 -0
  102. package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts.map +1 -0
  103. package/dist/src/security/scanner/SecurityScanner.value-gate.js +271 -0
  104. package/dist/src/security/scanner/SecurityScanner.value-gate.js.map +1 -0
  105. package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts +55 -0
  106. package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts.map +1 -0
  107. package/dist/src/security/scanner/SecurityScanner.weak-passwords.js +308 -0
  108. package/dist/src/security/scanner/SecurityScanner.weak-passwords.js.map +1 -0
  109. package/dist/src/security/scanner/patterns.d.ts +61 -6
  110. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  111. package/dist/src/security/scanner/patterns.js +70 -61
  112. package/dist/src/security/scanner/patterns.js.map +1 -1
  113. package/dist/src/security/scanner/patterns.sensitive-path.d.ts +73 -0
  114. package/dist/src/security/scanner/patterns.sensitive-path.d.ts.map +1 -0
  115. package/dist/src/security/scanner/patterns.sensitive-path.js +192 -0
  116. package/dist/src/security/scanner/patterns.sensitive-path.js.map +1 -0
  117. package/dist/src/security/scanner/regex-utils.d.ts +31 -17
  118. package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
  119. package/dist/src/security/scanner/regex-utils.js +31 -17
  120. package/dist/src/security/scanner/regex-utils.js.map +1 -1
  121. package/dist/src/security/scanner/types.d.ts +9 -0
  122. package/dist/src/security/scanner/types.d.ts.map +1 -1
  123. package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -1
  124. package/dist/src/services/agent-pack/prompt-source.js +3 -2
  125. package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
  126. package/dist/src/services/skill-installation.content.d.ts +7 -2
  127. package/dist/src/services/skill-installation.content.d.ts.map +1 -1
  128. package/dist/src/services/skill-installation.content.js +75 -28
  129. package/dist/src/services/skill-installation.content.js.map +1 -1
  130. package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
  131. package/dist/src/services/skill-installation.helpers.js +8 -0
  132. package/dist/src/services/skill-installation.helpers.js.map +1 -1
  133. package/dist/src/services/skill-installation.io.d.ts +1 -29
  134. package/dist/src/services/skill-installation.io.d.ts.map +1 -1
  135. package/dist/src/services/skill-installation.io.dirs.d.ts +15 -0
  136. package/dist/src/services/skill-installation.io.dirs.d.ts.map +1 -0
  137. package/dist/src/services/skill-installation.io.dirs.js +84 -0
  138. package/dist/src/services/skill-installation.io.dirs.js.map +1 -0
  139. package/dist/src/services/skill-installation.io.js +6 -176
  140. package/dist/src/services/skill-installation.io.js.map +1 -1
  141. package/dist/src/services/skill-installation.io.rollback.d.ts +138 -0
  142. package/dist/src/services/skill-installation.io.rollback.d.ts.map +1 -0
  143. package/dist/src/services/skill-installation.io.rollback.js +218 -0
  144. package/dist/src/services/skill-installation.io.rollback.js.map +1 -0
  145. package/dist/src/services/skill-installation.io.write.d.ts +69 -0
  146. package/dist/src/services/skill-installation.io.write.d.ts.map +1 -0
  147. package/dist/src/services/skill-installation.io.write.js +417 -0
  148. package/dist/src/services/skill-installation.io.write.js.map +1 -0
  149. package/dist/src/services/skill-installation.service.d.ts.map +1 -1
  150. package/dist/src/services/skill-installation.service.finalize.d.ts +62 -0
  151. package/dist/src/services/skill-installation.service.finalize.d.ts.map +1 -0
  152. package/dist/src/services/skill-installation.service.finalize.js +136 -0
  153. package/dist/src/services/skill-installation.service.finalize.js.map +1 -0
  154. package/dist/src/services/skill-installation.service.js +66 -80
  155. package/dist/src/services/skill-installation.service.js.map +1 -1
  156. package/dist/src/services/skill-installation.target-guard.d.ts +82 -0
  157. package/dist/src/services/skill-installation.target-guard.d.ts.map +1 -0
  158. package/dist/src/services/skill-installation.target-guard.js +410 -0
  159. package/dist/src/services/skill-installation.target-guard.js.map +1 -0
  160. package/dist/src/services/skill-installation.types.d.ts +13 -1
  161. package/dist/src/services/skill-installation.types.d.ts.map +1 -1
  162. package/dist/src/services/skill-installation.types.js.map +1 -1
  163. package/dist/src/services/skill-installation.uninstall.d.ts +3 -1
  164. package/dist/src/services/skill-installation.uninstall.d.ts.map +1 -1
  165. package/dist/src/services/skill-installation.uninstall.helpers.d.ts +59 -0
  166. package/dist/src/services/skill-installation.uninstall.helpers.d.ts.map +1 -0
  167. package/dist/src/services/skill-installation.uninstall.helpers.js +124 -0
  168. package/dist/src/services/skill-installation.uninstall.helpers.js.map +1 -0
  169. package/dist/src/services/skill-installation.uninstall.js +148 -25
  170. package/dist/src/services/skill-installation.uninstall.js.map +1 -1
  171. package/dist/src/utils/index.d.ts +1 -0
  172. package/dist/src/utils/index.d.ts.map +1 -1
  173. package/dist/src/utils/index.js +2 -0
  174. package/dist/src/utils/index.js.map +1 -1
  175. package/dist/src/utils/safe-fs.d.ts +69 -0
  176. package/dist/src/utils/safe-fs.d.ts.map +1 -1
  177. package/dist/src/utils/safe-fs.js +113 -2
  178. package/dist/src/utils/safe-fs.js.map +1 -1
  179. package/dist/src/utils/skill-name.d.ts +19 -0
  180. package/dist/src/utils/skill-name.d.ts.map +1 -0
  181. package/dist/src/utils/skill-name.js +26 -0
  182. package/dist/src/utils/skill-name.js.map +1 -0
  183. package/dist/src/utils/skill-name.test.d.ts +2 -0
  184. package/dist/src/utils/skill-name.test.d.ts.map +1 -0
  185. package/dist/src/utils/skill-name.test.js +34 -0
  186. package/dist/src/utils/skill-name.test.js.map +1 -0
  187. package/dist/tests/install/fan-out.test.js +1951 -13
  188. package/dist/tests/install/fan-out.test.js.map +1 -1
  189. package/dist/tests/install/remove-if-same.test.d.ts +2 -0
  190. package/dist/tests/install/remove-if-same.test.d.ts.map +1 -0
  191. package/dist/tests/install/remove-if-same.test.js +298 -0
  192. package/dist/tests/install/remove-if-same.test.js.map +1 -0
  193. package/dist/tests/integration/fan-out-lock-contention.test.d.ts +11 -0
  194. package/dist/tests/integration/fan-out-lock-contention.test.d.ts.map +1 -0
  195. package/dist/tests/integration/fan-out-lock-contention.test.js +53 -0
  196. package/dist/tests/integration/fan-out-lock-contention.test.js.map +1 -0
  197. package/dist/tests/provenance/backfill.test.js +43 -0
  198. package/dist/tests/provenance/backfill.test.js.map +1 -1
  199. package/dist/tests/security/ContinuousSecurity.performance.test.js +12 -7
  200. package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
  201. package/dist/tests/security/ContinuousSecurity.test.js +14 -2
  202. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
  203. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +10 -12
  204. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -1
  205. package/dist/tests/security/ai-defence-multiline-promotion.test.js +26 -33
  206. package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -1
  207. package/dist/tests/security/nested-instruction-long-attribute.test.js +20 -10
  208. package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -1
  209. package/dist/tests/security/scanner-evidence-tiers.test.js +15 -0
  210. package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -1
  211. package/dist/tests/security/scanner-multiline-cap.test.d.ts +21 -0
  212. package/dist/tests/security/scanner-multiline-cap.test.d.ts.map +1 -0
  213. package/dist/tests/security/scanner-multiline-cap.test.js +152 -0
  214. package/dist/tests/security/scanner-multiline-cap.test.js.map +1 -0
  215. package/dist/tests/security/scanner-regression-guard.test.js +66 -1
  216. package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
  217. package/dist/tests/security/scanner-witness-sw1.test.d.ts +32 -0
  218. package/dist/tests/security/scanner-witness-sw1.test.d.ts.map +1 -0
  219. package/dist/tests/security/scanner-witness-sw1.test.js +255 -0
  220. package/dist/tests/security/scanner-witness-sw1.test.js.map +1 -0
  221. package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts +2 -0
  222. package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts.map +1 -0
  223. package/dist/tests/security/sensitive-path-adversarial-review.test.js +313 -0
  224. package/dist/tests/security/sensitive-path-adversarial-review.test.js.map +1 -0
  225. package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts +2 -0
  226. package/dist/tests/security/sensitive-path-boolean-flag.test.d.ts.map +1 -0
  227. package/dist/tests/security/sensitive-path-boolean-flag.test.js +151 -0
  228. package/dist/tests/security/sensitive-path-boolean-flag.test.js.map +1 -0
  229. package/dist/tests/security/sensitive-path-fp.test.js +314 -0
  230. package/dist/tests/security/sensitive-path-fp.test.js.map +1 -1
  231. package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts +2 -0
  232. package/dist/tests/security/sensitive-path-prefixed-secrets.test.d.ts.map +1 -0
  233. package/dist/tests/security/sensitive-path-prefixed-secrets.test.js +176 -0
  234. package/dist/tests/security/sensitive-path-prefixed-secrets.test.js.map +1 -0
  235. package/dist/tests/security/smi5879-corroboration.core.test.js +44 -0
  236. package/dist/tests/security/smi5879-corroboration.core.test.js.map +1 -1
  237. package/dist/tests/security/weak-password-lexicon.test.d.ts +18 -0
  238. package/dist/tests/security/weak-password-lexicon.test.d.ts.map +1 -0
  239. package/dist/tests/security/weak-password-lexicon.test.js +132 -0
  240. package/dist/tests/security/weak-password-lexicon.test.js.map +1 -0
  241. package/dist/tests/skill-scanner/allowlist.test.js +58 -4
  242. package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
  243. package/dist/tests/skill-scanner/categorizer.test.d.ts +2 -0
  244. package/dist/tests/skill-scanner/categorizer.test.d.ts.map +1 -0
  245. package/dist/tests/skill-scanner/categorizer.test.js +71 -0
  246. package/dist/tests/skill-scanner/categorizer.test.js.map +1 -0
  247. package/dist/tests/skill-scanner/cli-guard.test.d.ts +23 -0
  248. package/dist/tests/skill-scanner/cli-guard.test.d.ts.map +1 -0
  249. package/dist/tests/skill-scanner/cli-guard.test.js +71 -0
  250. package/dist/tests/skill-scanner/cli-guard.test.js.map +1 -0
  251. package/dist/tests/unit/services/skill-installation.content.test.js +59 -0
  252. package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -1
  253. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js +35 -14
  254. package/dist/tests/unit/services/skill-installation.io.client-agent-path.test.js.map +1 -1
  255. package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts +2 -0
  256. package/dist/tests/unit/services/skill-installation.io.rollback.test.d.ts.map +1 -0
  257. package/dist/tests/unit/services/skill-installation.io.rollback.test.js +984 -0
  258. package/dist/tests/unit/services/skill-installation.io.rollback.test.js.map +1 -0
  259. package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts +2 -0
  260. package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.d.ts.map +1 -0
  261. package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js +148 -0
  262. package/dist/tests/unit/services/skill-installation.io.rollback.unit.test.js.map +1 -0
  263. package/dist/tests/unit/services/skill-installation.service.error-codes.test.js +19 -0
  264. package/dist/tests/unit/services/skill-installation.service.error-codes.test.js.map +1 -1
  265. package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts +2 -0
  266. package/dist/tests/unit/services/skill-installation.service.finalize.test.d.ts.map +1 -0
  267. package/dist/tests/unit/services/skill-installation.service.finalize.test.js +147 -0
  268. package/dist/tests/unit/services/skill-installation.service.finalize.test.js.map +1 -0
  269. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts +29 -0
  270. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts.map +1 -0
  271. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js +200 -0
  272. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js.map +1 -0
  273. package/dist/tests/unit/services/skill-installation.service.test.js +43 -0
  274. package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
  275. package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts +2 -0
  276. package/dist/tests/unit/services/skill-installation.target-guard.test.d.ts.map +1 -0
  277. package/dist/tests/unit/services/skill-installation.target-guard.test.js +590 -0
  278. package/dist/tests/unit/services/skill-installation.target-guard.test.js.map +1 -0
  279. package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts +2 -0
  280. package/dist/tests/unit/services/skill-installation.uninstall.guard.test.d.ts.map +1 -0
  281. package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js +473 -0
  282. package/dist/tests/unit/services/skill-installation.uninstall.guard.test.js.map +1 -0
  283. package/dist/tests/utils/safe-fs.test.js +129 -2
  284. package/dist/tests/utils/safe-fs.test.js.map +1 -1
  285. package/package.json +6 -1
@@ -20,9 +20,40 @@ import { open, lstat, constants } from 'fs/promises';
20
20
  * O_NOFOLLOW: refuse to open symlinks
21
21
  * Prefer runtime constant from fs.constants, fall back to platform-specific values
22
22
  * if unavailable (e.g., Windows where O_NOFOLLOW is not defined).
23
+ *
24
+ * Exported (SMI-6529 F2) so other symlink-safe fd-based writers — e.g.
25
+ * writeInstallFiles' own rollback restore — share this exact platform
26
+ * -fallback resolution instead of a second, driftable copy.
23
27
  */
24
- const O_NOFOLLOW = constants.O_NOFOLLOW ??
28
+ export const O_NOFOLLOW = constants.O_NOFOLLOW ??
25
29
  (process.platform === 'darwin' ? 0x100 : 0x20000);
30
+ /**
31
+ * SMI-6529 N3 (round 4): `FileHandle.write()` is permitted by POSIX (and
32
+ * measured live — 2048/10000 bytes under `RLIMIT_FSIZE`) to write FEWER
33
+ * bytes than requested WITHOUT throwing. Every fd-based writer in this
34
+ * module family used a single unchecked `await fd.write(data)` call, so a
35
+ * short write silently truncated content with no error — for
36
+ * `restoreSnapshots()` specifically, that meant a "successful" restore that
37
+ * left the file smaller than the true original with no `InstallRestoreError`
38
+ * ever raised. Loops until every byte is accounted for, or throws if a call
39
+ * makes zero progress (a short write that returns 0 can otherwise spin
40
+ * forever).
41
+ *
42
+ * @param position - Explicit starting file offset, or `null` to write
43
+ * sequentially from the handle's own current position (which auto-advances
44
+ * after each underlying `write()` call, short or not).
45
+ */
46
+ export async function writeFullBuffer(fd, data, position = null) {
47
+ let written = 0;
48
+ while (written < data.length) {
49
+ const pos = position === null ? null : position + written;
50
+ const { bytesWritten } = await fd.write(data, written, data.length - written, pos);
51
+ if (bytesWritten <= 0) {
52
+ throw new Error(`Short write: made no progress after ${written}/${data.length} bytes written`);
53
+ }
54
+ written += bytesWritten;
55
+ }
56
+ }
26
57
  /**
27
58
  * Error thrown when a symlink is detected at a write destination.
28
59
  */
@@ -110,7 +141,87 @@ export async function safeWriteFile(filePath, content, options) {
110
141
  : typeof content === 'string'
111
142
  ? Buffer.from(content, 'utf-8')
112
143
  : content;
113
- await fd.write(data);
144
+ await writeFullBuffer(fd, data);
145
+ }
146
+ finally {
147
+ await fd?.close();
148
+ }
149
+ }
150
+ /**
151
+ * Error thrown when `safeCreateFile`'s exclusive create finds the destination
152
+ * already occupied — created by something else between the caller's own
153
+ * pre-write classification and this call (a race, not a symlink/hardlink).
154
+ * SMI-6529 L14(b).
155
+ */
156
+ export class ExclusiveCreateRaceError extends Error {
157
+ filePath;
158
+ constructor(filePath) {
159
+ super(`Target appeared during install (race): ${filePath}`);
160
+ this.name = 'ExclusiveCreateRaceError';
161
+ this.filePath = filePath;
162
+ }
163
+ }
164
+ /**
165
+ * SMI-6529 L14(b): create a NEW file exclusively — refuses (throws
166
+ * {@link ExclusiveCreateRaceError}) if anything already exists at `filePath`,
167
+ * symlink or otherwise, instead of silently truncating/overwriting it. Only
168
+ * appropriate for a write target the caller has just classified as `absent`
169
+ * (`classifyPreWrite` in skill-installation.io.rollback.ts) — the atomicity
170
+ * of `O_CREAT|O_EXCL` means "did this call create the file" is knowable
171
+ * without needing `safeWriteFile`'s "record BEFORE writing" hedge (F1, round
172
+ * 1), which exists specifically to survive `O_TRUNC`'s non-atomicity on an
173
+ * OVERWRITE — a case this function is never used for.
174
+ *
175
+ * @param filePath - Absolute path to create (must not already exist)
176
+ * @param content - File content (string or Buffer)
177
+ * @param options - Optional fs.writeFile options (encoding, mode) or encoding string
178
+ * @param onCreated - SMI-6529 N9 (round 4): fired the MOMENT the exclusive
179
+ * `open()` succeeds — i.e. the instant the file is KNOWN to exist on disk —
180
+ * BEFORE any content byte is written. Lets a caller (`writeInstallFiles`)
181
+ * record its rollback "created fresh, safe to unlink" candidate at the
182
+ * earliest correct moment: without this, a write that later fails (e.g.
183
+ * ENOSPC) after `open()` already created the file left a 0-byte/partial
184
+ * orphan that rollback never knew to remove, since the caller previously
185
+ * only recorded it AFTER this whole function resolved.
186
+ * @throws {ExclusiveCreateRaceError} if the destination already exists
187
+ * @throws {SymlinkError} technically unreachable (O_EXCL fails before O_NOFOLLOW
188
+ * would matter) but kept out of the public contract intentionally — this
189
+ * function never follows anything, by construction.
190
+ */
191
+ export async function safeCreateFile(filePath, content, options, onCreated) {
192
+ const mode = typeof options === 'string' ? 0o644 : (options?.mode ?? 0o644);
193
+ const encoding = typeof options === 'string'
194
+ ? options
195
+ : (options?.encoding ?? undefined);
196
+ let fd;
197
+ try {
198
+ fd = await open(filePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | O_NOFOLLOW, mode);
199
+ // SMI-6529 R5 (round 5): pass the new file's (dev, ino) so a rollback can
200
+ // confirm the path still names THIS file before unlinking it, and never
201
+ // removes a file another process renamed over ours in between. If the
202
+ // stat fails, the identity is omitted and the caller falls back to
203
+ // unlinking by path.
204
+ let identity;
205
+ try {
206
+ const created = await fd.stat();
207
+ identity = { dev: created.dev, ino: created.ino };
208
+ }
209
+ catch {
210
+ identity = undefined;
211
+ }
212
+ onCreated?.(identity);
213
+ const data = typeof content === 'string' && encoding
214
+ ? Buffer.from(content, encoding)
215
+ : typeof content === 'string'
216
+ ? Buffer.from(content, 'utf-8')
217
+ : content;
218
+ await writeFullBuffer(fd, data);
219
+ }
220
+ catch (error) {
221
+ if (error.code === 'EEXIST') {
222
+ throw new ExclusiveCreateRaceError(filePath);
223
+ }
224
+ throw error;
114
225
  }
115
226
  finally {
116
227
  await fd?.close();
@@ -1 +1 @@
1
- {"version":3,"file":"safe-fs.js","sourceRoot":"","sources":["../../../src/utils/safe-fs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"safe-fs.js","sourceRoot":"","sources":["../../../src/utils/safe-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Shared skill name validation utilities.
3
+ *
4
+ * Used by both `skillsmith author init` and `skillsmith create` to enforce
5
+ * a consistent, registry-safe naming convention across all scaffolding paths.
6
+ */
7
+ /**
8
+ * Valid skill names: lowercase letters, digits, and hyphens only.
9
+ * Must start with a lowercase letter.
10
+ * Matches the Skillsmith registry slug format.
11
+ */
12
+ export declare const VALID_SKILL_NAME_RE: RegExp;
13
+ /**
14
+ * Validate a skill name against the canonical Skillsmith naming convention.
15
+ *
16
+ * @returns `true` if valid, or a string error message if invalid.
17
+ */
18
+ export declare function validateSkillName(name: string): true | string;
19
+ //# sourceMappingURL=skill-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-name.d.ts","sourceRoot":"","sources":["../../../src/utils/skill-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAsB,CAAA;AAEtD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAM7D"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared skill name validation utilities.
3
+ *
4
+ * Used by both `skillsmith author init` and `skillsmith create` to enforce
5
+ * a consistent, registry-safe naming convention across all scaffolding paths.
6
+ */
7
+ /**
8
+ * Valid skill names: lowercase letters, digits, and hyphens only.
9
+ * Must start with a lowercase letter.
10
+ * Matches the Skillsmith registry slug format.
11
+ */
12
+ export const VALID_SKILL_NAME_RE = /^[a-z][a-z0-9-]*$/;
13
+ /**
14
+ * Validate a skill name against the canonical Skillsmith naming convention.
15
+ *
16
+ * @returns `true` if valid, or a string error message if invalid.
17
+ */
18
+ export function validateSkillName(name) {
19
+ if (!name.trim())
20
+ return 'Skill name is required';
21
+ if (!VALID_SKILL_NAME_RE.test(name)) {
22
+ return 'Skill name must start with a lowercase letter and contain only lowercase letters, digits, and hyphens (e.g. my-skill)';
23
+ }
24
+ return true;
25
+ }
26
+ //# sourceMappingURL=skill-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-name.js","sourceRoot":"","sources":["../../../src/utils/skill-name.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAA;AAEtD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,wBAAwB,CAAA;IACjD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,OAAO,uHAAuH,CAAA;IAChI,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=skill-name.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-name.test.d.ts","sourceRoot":"","sources":["../../../src/utils/skill-name.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * SMI-6472: validateSkillName tests.
3
+ *
4
+ * Ported from packages/cli/tests/create.test.ts:38-71 — @skillsmith/core is
5
+ * now the canonical source (packages/cli/src/utils/skill-name.ts re-exports
6
+ * from here), so this is where the direct unit coverage lives.
7
+ */
8
+ import { describe, it, expect } from 'vitest';
9
+ import { validateSkillName } from './skill-name.js';
10
+ describe('validateSkillName', () => {
11
+ it('accepts valid lowercase-hyphen names', () => {
12
+ expect(validateSkillName('my-skill')).toBe(true);
13
+ expect(validateSkillName('skill')).toBe(true);
14
+ expect(validateSkillName('a1b2-c3')).toBe(true);
15
+ });
16
+ it('rejects names with uppercase letters', () => {
17
+ const result = validateSkillName('My-Skill');
18
+ expect(result).not.toBe(true);
19
+ expect(typeof result).toBe('string');
20
+ });
21
+ it('rejects names with spaces', () => {
22
+ expect(validateSkillName('my skill')).not.toBe(true);
23
+ });
24
+ it('rejects names starting with a digit', () => {
25
+ expect(validateSkillName('1skill')).not.toBe(true);
26
+ });
27
+ it('rejects empty string', () => {
28
+ expect(validateSkillName('')).not.toBe(true);
29
+ });
30
+ it('rejects names with underscores', () => {
31
+ expect(validateSkillName('my_skill')).not.toBe(true);
32
+ });
33
+ });
34
+ //# sourceMappingURL=skill-name.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-name.test.js","sourceRoot":"","sources":["../../../src/utils/skill-name.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAEnD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAChD,MAAM,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7C,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACjD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAA;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC7B,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACtC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACpD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}