@skillsmith/core 0.12.1 → 0.12.3

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 (398) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +2 -2
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/api/client.d.ts +5 -3
  5. package/dist/src/api/client.d.ts.map +1 -1
  6. package/dist/src/api/client.js +23 -33
  7. package/dist/src/api/client.js.map +1 -1
  8. package/dist/src/api/client.registry-sync.d.ts +28 -0
  9. package/dist/src/api/client.registry-sync.d.ts.map +1 -0
  10. package/dist/src/api/client.registry-sync.js +36 -0
  11. package/dist/src/api/client.registry-sync.js.map +1 -0
  12. package/dist/src/api/client.test.js +185 -0
  13. package/dist/src/api/client.test.js.map +1 -1
  14. package/dist/src/api/client.toSkill.d.ts +16 -0
  15. package/dist/src/api/client.toSkill.d.ts.map +1 -0
  16. package/dist/src/api/client.toSkill.js +46 -0
  17. package/dist/src/api/client.toSkill.js.map +1 -0
  18. package/dist/src/api/client.types.d.ts +22 -0
  19. package/dist/src/api/client.types.d.ts.map +1 -1
  20. package/dist/src/api/index.d.ts +1 -1
  21. package/dist/src/api/index.d.ts.map +1 -1
  22. package/dist/src/api/index.js.map +1 -1
  23. package/dist/src/api/schemas.d.ts +28 -0
  24. package/dist/src/api/schemas.d.ts.map +1 -1
  25. package/dist/src/api/schemas.js +13 -0
  26. package/dist/src/api/schemas.js.map +1 -1
  27. package/dist/src/audit/remote-audit.d.ts +85 -0
  28. package/dist/src/audit/remote-audit.d.ts.map +1 -1
  29. package/dist/src/audit/remote-audit.js +126 -3
  30. package/dist/src/audit/remote-audit.js.map +1 -1
  31. package/dist/src/audit/remote-audit.test.js +217 -1
  32. package/dist/src/audit/remote-audit.test.js.map +1 -1
  33. package/dist/src/config/index.d.ts +1 -0
  34. package/dist/src/config/index.d.ts.map +1 -1
  35. package/dist/src/config/index.js.map +1 -1
  36. package/dist/src/db/migration-runner.d.ts.map +1 -1
  37. package/dist/src/db/migration-runner.js +6 -0
  38. package/dist/src/db/migration-runner.js.map +1 -1
  39. package/dist/src/db/migrations/v18-skill-versions-real-content-hash.d.ts +36 -0
  40. package/dist/src/db/migrations/v18-skill-versions-real-content-hash.d.ts.map +1 -0
  41. package/dist/src/db/migrations/v18-skill-versions-real-content-hash.js +38 -0
  42. package/dist/src/db/migrations/v18-skill-versions-real-content-hash.js.map +1 -0
  43. package/dist/src/db/schema.d.ts +1 -1
  44. package/dist/src/db/schema.d.ts.map +1 -1
  45. package/dist/src/db/schema.js +3 -1
  46. package/dist/src/db/schema.js.map +1 -1
  47. package/dist/src/exports/services.d.ts +3 -7
  48. package/dist/src/exports/services.d.ts.map +1 -1
  49. package/dist/src/exports/services.install.d.ts +18 -0
  50. package/dist/src/exports/services.install.d.ts.map +1 -0
  51. package/dist/src/exports/services.install.js +62 -0
  52. package/dist/src/exports/services.install.js.map +1 -0
  53. package/dist/src/exports/services.js +12 -32
  54. package/dist/src/exports/services.js.map +1 -1
  55. package/dist/src/exports/types.d.ts +1 -1
  56. package/dist/src/exports/types.d.ts.map +1 -1
  57. package/dist/src/index.d.ts +6 -5
  58. package/dist/src/index.d.ts.map +1 -1
  59. package/dist/src/index.js +9 -4
  60. package/dist/src/index.js.map +1 -1
  61. package/dist/src/install/agent-config-merge.json.d.ts +5 -4
  62. package/dist/src/install/agent-config-merge.json.d.ts.map +1 -1
  63. package/dist/src/install/agent-config-merge.json.js +10 -9
  64. package/dist/src/install/agent-config-merge.json.js.map +1 -1
  65. package/dist/src/install/agent-config-merge.types.d.ts +11 -1
  66. package/dist/src/install/agent-config-merge.types.d.ts.map +1 -1
  67. package/dist/src/install/agent-config-merge.types.js.map +1 -1
  68. package/dist/src/install/agent-harness-targets.d.ts +30 -2
  69. package/dist/src/install/agent-harness-targets.d.ts.map +1 -1
  70. package/dist/src/install/agent-harness-targets.js +17 -0
  71. package/dist/src/install/agent-harness-targets.js.map +1 -1
  72. package/dist/src/install/agent-manifest-path-guard.d.ts +28 -3
  73. package/dist/src/install/agent-manifest-path-guard.d.ts.map +1 -1
  74. package/dist/src/install/agent-manifest-path-guard.js +59 -3
  75. package/dist/src/install/agent-manifest-path-guard.js.map +1 -1
  76. package/dist/src/install/agent-manifest-path-guard.test.js +27 -1
  77. package/dist/src/install/agent-manifest-path-guard.test.js.map +1 -1
  78. package/dist/src/install/agent-pack-installer.antigravity-mcp.d.ts +74 -0
  79. package/dist/src/install/agent-pack-installer.antigravity-mcp.d.ts.map +1 -0
  80. package/dist/src/install/agent-pack-installer.antigravity-mcp.js +106 -0
  81. package/dist/src/install/agent-pack-installer.antigravity-mcp.js.map +1 -0
  82. package/dist/src/install/agent-pack-installer.antigravity-mcp.test.d.ts +2 -0
  83. package/dist/src/install/agent-pack-installer.antigravity-mcp.test.d.ts.map +1 -0
  84. package/dist/src/install/agent-pack-installer.antigravity-mcp.test.js +157 -0
  85. package/dist/src/install/agent-pack-installer.antigravity-mcp.test.js.map +1 -0
  86. package/dist/src/install/agent-pack-installer.cursor-mcp.d.ts +53 -0
  87. package/dist/src/install/agent-pack-installer.cursor-mcp.d.ts.map +1 -0
  88. package/dist/src/install/agent-pack-installer.cursor-mcp.js +207 -0
  89. package/dist/src/install/agent-pack-installer.cursor-mcp.js.map +1 -0
  90. package/dist/src/install/agent-pack-installer.cursor-mcp.test.d.ts +2 -0
  91. package/dist/src/install/agent-pack-installer.cursor-mcp.test.d.ts.map +1 -0
  92. package/dist/src/install/agent-pack-installer.cursor-mcp.test.js +285 -0
  93. package/dist/src/install/agent-pack-installer.cursor-mcp.test.js.map +1 -0
  94. package/dist/src/install/agent-pack-installer.d.ts +8 -0
  95. package/dist/src/install/agent-pack-installer.d.ts.map +1 -1
  96. package/dist/src/install/agent-pack-installer.entry.d.ts +120 -3
  97. package/dist/src/install/agent-pack-installer.entry.d.ts.map +1 -1
  98. package/dist/src/install/agent-pack-installer.entry.js +166 -3
  99. package/dist/src/install/agent-pack-installer.entry.js.map +1 -1
  100. package/dist/src/install/agent-pack-installer.js +97 -3
  101. package/dist/src/install/agent-pack-installer.js.map +1 -1
  102. package/dist/src/install/agent-pack-installer.types.d.ts +27 -0
  103. package/dist/src/install/agent-pack-installer.types.d.ts.map +1 -1
  104. package/dist/src/install/agent-pack-installer.types.js +6 -0
  105. package/dist/src/install/agent-pack-installer.types.js.map +1 -1
  106. package/dist/src/install/agent-pack-installer.workspace-scope.test.d.ts +2 -0
  107. package/dist/src/install/agent-pack-installer.workspace-scope.test.d.ts.map +1 -0
  108. package/dist/src/install/agent-pack-installer.workspace-scope.test.js +148 -0
  109. package/dist/src/install/agent-pack-installer.workspace-scope.test.js.map +1 -0
  110. package/dist/src/install/agent-pack-uninstaller.d.ts +18 -7
  111. package/dist/src/install/agent-pack-uninstaller.d.ts.map +1 -1
  112. package/dist/src/install/agent-pack-uninstaller.js +59 -9
  113. package/dist/src/install/agent-pack-uninstaller.js.map +1 -1
  114. package/dist/src/install/agent-pack-uninstaller.test.js +74 -1
  115. package/dist/src/install/agent-pack-uninstaller.test.js.map +1 -1
  116. package/dist/src/install/fan-out.d.ts.map +1 -1
  117. package/dist/src/install/fan-out.js +6 -0
  118. package/dist/src/install/fan-out.js.map +1 -1
  119. package/dist/src/install/index.d.ts +2 -0
  120. package/dist/src/install/index.d.ts.map +1 -1
  121. package/dist/src/install/index.js +2 -0
  122. package/dist/src/install/index.js.map +1 -1
  123. package/dist/src/install/workspace-scope.d.ts +207 -0
  124. package/dist/src/install/workspace-scope.d.ts.map +1 -0
  125. package/dist/src/install/workspace-scope.js +275 -0
  126. package/dist/src/install/workspace-scope.js.map +1 -0
  127. package/dist/src/install/workspace-scope.test.d.ts +2 -0
  128. package/dist/src/install/workspace-scope.test.d.ts.map +1 -0
  129. package/dist/src/install/workspace-scope.test.js +378 -0
  130. package/dist/src/install/workspace-scope.test.js.map +1 -0
  131. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +9 -1
  132. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  133. package/dist/src/scripts/scan-imported-skills.d.ts +7 -1
  134. package/dist/src/scripts/scan-imported-skills.d.ts.map +1 -1
  135. package/dist/src/scripts/scan-imported-skills.js +15 -2
  136. package/dist/src/scripts/scan-imported-skills.js.map +1 -1
  137. package/dist/src/scripts/skill-scanner/allowlist.d.ts +10 -0
  138. package/dist/src/scripts/skill-scanner/allowlist.d.ts.map +1 -1
  139. package/dist/src/scripts/skill-scanner/allowlist.js +10 -0
  140. package/dist/src/scripts/skill-scanner/allowlist.js.map +1 -1
  141. package/dist/src/scripts/skill-scanner/index.d.ts +8 -0
  142. package/dist/src/scripts/skill-scanner/index.d.ts.map +1 -1
  143. package/dist/src/scripts/skill-scanner/index.js +13 -5
  144. package/dist/src/scripts/skill-scanner/index.js.map +1 -1
  145. package/dist/src/scripts/skill-scanner/trust-scorer.d.ts.map +1 -1
  146. package/dist/src/scripts/skill-scanner/trust-scorer.js +6 -0
  147. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  148. package/dist/src/security/__tests__/pathValidation.test.js +30 -1
  149. package/dist/src/security/__tests__/pathValidation.test.js.map +1 -1
  150. package/dist/src/security/scanner/SecurityScanner.action-context.d.ts +15 -0
  151. package/dist/src/security/scanner/SecurityScanner.action-context.d.ts.map +1 -0
  152. package/dist/src/security/scanner/SecurityScanner.action-context.js +209 -0
  153. package/dist/src/security/scanner/SecurityScanner.action-context.js.map +1 -0
  154. package/dist/src/security/scanner/SecurityScanner.d.ts +25 -0
  155. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  156. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +40 -1
  157. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
  158. package/dist/src/security/scanner/SecurityScanner.helpers.js +88 -51
  159. package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
  160. package/dist/src/security/scanner/SecurityScanner.js +39 -6
  161. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  162. package/dist/src/security/scanner/SecurityScanner.pii.d.ts +30 -0
  163. package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
  164. package/dist/src/security/scanner/SecurityScanner.pii.js +8 -1
  165. package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
  166. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts +60 -0
  167. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.d.ts.map +1 -0
  168. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js +228 -0
  169. package/dist/src/security/scanner/SecurityScanner.prose-lexicon.js.map +1 -0
  170. package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
  171. package/dist/src/security/scanner/SecurityScanner.scanners.js +35 -5
  172. package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
  173. package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts +49 -0
  174. package/dist/src/security/scanner/SecurityScanner.value-gate.d.ts.map +1 -0
  175. package/dist/src/security/scanner/SecurityScanner.value-gate.js +134 -0
  176. package/dist/src/security/scanner/SecurityScanner.value-gate.js.map +1 -0
  177. package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts +55 -0
  178. package/dist/src/security/scanner/SecurityScanner.weak-passwords.d.ts.map +1 -0
  179. package/dist/src/security/scanner/SecurityScanner.weak-passwords.js +313 -0
  180. package/dist/src/security/scanner/SecurityScanner.weak-passwords.js.map +1 -0
  181. package/dist/src/security/scanner/patterns.d.ts +16 -6
  182. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  183. package/dist/src/security/scanner/patterns.js +25 -61
  184. package/dist/src/security/scanner/patterns.js.map +1 -1
  185. package/dist/src/security/scanner/patterns.sensitive-path.d.ts +45 -0
  186. package/dist/src/security/scanner/patterns.sensitive-path.d.ts.map +1 -0
  187. package/dist/src/security/scanner/patterns.sensitive-path.js +137 -0
  188. package/dist/src/security/scanner/patterns.sensitive-path.js.map +1 -0
  189. package/dist/src/security/scanner/regex-utils.d.ts +31 -17
  190. package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
  191. package/dist/src/security/scanner/regex-utils.js +31 -17
  192. package/dist/src/security/scanner/regex-utils.js.map +1 -1
  193. package/dist/src/security/scanner/types.d.ts +9 -0
  194. package/dist/src/security/scanner/types.d.ts.map +1 -1
  195. package/dist/src/services/SubagentGenerator.client-profiles.d.ts +258 -0
  196. package/dist/src/services/SubagentGenerator.client-profiles.d.ts.map +1 -0
  197. package/dist/src/services/SubagentGenerator.client-profiles.js +260 -0
  198. package/dist/src/services/SubagentGenerator.client-profiles.js.map +1 -0
  199. package/dist/src/services/SubagentGenerator.d.ts +19 -41
  200. package/dist/src/services/SubagentGenerator.d.ts.map +1 -1
  201. package/dist/src/services/SubagentGenerator.helpers.d.ts +64 -0
  202. package/dist/src/services/SubagentGenerator.helpers.d.ts.map +1 -0
  203. package/dist/src/services/SubagentGenerator.helpers.js +228 -0
  204. package/dist/src/services/SubagentGenerator.helpers.js.map +1 -0
  205. package/dist/src/services/SubagentGenerator.js +66 -208
  206. package/dist/src/services/SubagentGenerator.js.map +1 -1
  207. package/dist/src/services/SubagentGenerator.types.d.ts +45 -0
  208. package/dist/src/services/SubagentGenerator.types.d.ts.map +1 -0
  209. package/dist/src/services/SubagentGenerator.types.js +14 -0
  210. package/dist/src/services/SubagentGenerator.types.js.map +1 -0
  211. package/dist/src/services/TransformationService.d.ts +18 -61
  212. package/dist/src/services/TransformationService.d.ts.map +1 -1
  213. package/dist/src/services/TransformationService.js +29 -17
  214. package/dist/src/services/TransformationService.js.map +1 -1
  215. package/dist/src/services/TransformationService.types.d.ts +74 -0
  216. package/dist/src/services/TransformationService.types.d.ts.map +1 -0
  217. package/dist/src/services/TransformationService.types.js +8 -0
  218. package/dist/src/services/TransformationService.types.js.map +1 -0
  219. package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.d.ts +5 -0
  220. package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.d.ts.map +1 -0
  221. package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.js +72 -0
  222. package/dist/src/services/__tests__/SubagentGenerator.client-profiles.test.js.map +1 -0
  223. package/dist/src/services/__tests__/SubagentGenerator.helpers.test.d.ts +2 -0
  224. package/dist/src/services/__tests__/SubagentGenerator.helpers.test.d.ts.map +1 -0
  225. package/dist/src/services/__tests__/SubagentGenerator.helpers.test.js +61 -0
  226. package/dist/src/services/__tests__/SubagentGenerator.helpers.test.js.map +1 -0
  227. package/dist/src/services/__tests__/SubagentGenerator.test.js +155 -0
  228. package/dist/src/services/__tests__/SubagentGenerator.test.js.map +1 -1
  229. package/dist/src/services/__tests__/TransformationService.test.js +48 -0
  230. package/dist/src/services/__tests__/TransformationService.test.js.map +1 -1
  231. package/dist/src/services/agent-pack/agent-pack.test.js +1 -0
  232. package/dist/src/services/agent-pack/agent-pack.test.js.map +1 -1
  233. package/dist/src/services/agent-pack/prompt-source.d.ts +1 -1
  234. package/dist/src/services/agent-pack/prompt-source.d.ts.map +1 -1
  235. package/dist/src/services/agent-pack/prompt-source.js +10 -3
  236. package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
  237. package/dist/src/services/agent-pack/types.d.ts +26 -2
  238. package/dist/src/services/agent-pack/types.d.ts.map +1 -1
  239. package/dist/src/services/agent-pack/types.js +13 -0
  240. package/dist/src/services/agent-pack/types.js.map +1 -1
  241. package/dist/src/services/agent-tool-profile.d.ts.map +1 -1
  242. package/dist/src/services/agent-tool-profile.js +7 -0
  243. package/dist/src/services/agent-tool-profile.js.map +1 -1
  244. package/dist/src/services/agent-tool-profile.test.js +2 -2
  245. package/dist/src/services/skill-content-comparison.d.ts +71 -0
  246. package/dist/src/services/skill-content-comparison.d.ts.map +1 -0
  247. package/dist/src/services/skill-content-comparison.js +90 -0
  248. package/dist/src/services/skill-content-comparison.js.map +1 -0
  249. package/dist/src/services/skill-content-comparison.test.d.ts +6 -0
  250. package/dist/src/services/skill-content-comparison.test.d.ts.map +1 -0
  251. package/dist/src/services/skill-content-comparison.test.js +83 -0
  252. package/dist/src/services/skill-content-comparison.test.js.map +1 -0
  253. package/dist/src/services/skill-identity-classification.d.ts +202 -0
  254. package/dist/src/services/skill-identity-classification.d.ts.map +1 -0
  255. package/dist/src/services/skill-identity-classification.js +295 -0
  256. package/dist/src/services/skill-identity-classification.js.map +1 -0
  257. package/dist/src/services/skill-identity-classification.test.d.ts +6 -0
  258. package/dist/src/services/skill-identity-classification.test.d.ts.map +1 -0
  259. package/dist/src/services/skill-identity-classification.test.js +314 -0
  260. package/dist/src/services/skill-identity-classification.test.js.map +1 -0
  261. package/dist/src/services/skill-installation.content.d.ts.map +1 -1
  262. package/dist/src/services/skill-installation.content.js +6 -1
  263. package/dist/src/services/skill-installation.content.js.map +1 -1
  264. package/dist/src/services/skill-installation.helpers.d.ts +8 -15
  265. package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
  266. package/dist/src/services/skill-installation.helpers.js +11 -105
  267. package/dist/src/services/skill-installation.helpers.js.map +1 -1
  268. package/dist/src/services/skill-installation.helpers.test.js +106 -1
  269. package/dist/src/services/skill-installation.helpers.test.js.map +1 -1
  270. package/dist/src/services/skill-installation.service.js +1 -1
  271. package/dist/src/services/skill-installation.service.js.map +1 -1
  272. package/dist/src/services/skill-installation.types.d.ts +33 -0
  273. package/dist/src/services/skill-installation.types.d.ts.map +1 -1
  274. package/dist/src/services/skill-installation.types.js.map +1 -1
  275. package/dist/src/services/skill-installation.uninstall.d.ts +84 -0
  276. package/dist/src/services/skill-installation.uninstall.d.ts.map +1 -0
  277. package/dist/src/services/skill-installation.uninstall.js +252 -0
  278. package/dist/src/services/skill-installation.uninstall.js.map +1 -0
  279. package/dist/src/services/skill-manifest.d.ts +41 -0
  280. package/dist/src/services/skill-manifest.d.ts.map +1 -1
  281. package/dist/src/services/skill-manifest.js +93 -0
  282. package/dist/src/services/skill-manifest.js.map +1 -1
  283. package/dist/src/services/skill-manifest.test.js +27 -0
  284. package/dist/src/services/skill-manifest.test.js.map +1 -1
  285. package/dist/src/sync/SyncEngine.d.ts.map +1 -1
  286. package/dist/src/sync/SyncEngine.js +69 -77
  287. package/dist/src/sync/SyncEngine.js.map +1 -1
  288. package/dist/src/telemetry/agent-marker.d.ts +8 -0
  289. package/dist/src/telemetry/agent-marker.d.ts.map +1 -1
  290. package/dist/src/telemetry/agent-marker.js +7 -0
  291. package/dist/src/telemetry/agent-marker.js.map +1 -1
  292. package/dist/src/telemetry/agent-marker.test.js +7 -1
  293. package/dist/src/telemetry/agent-marker.test.js.map +1 -1
  294. package/dist/src/telemetry/index.d.ts +1 -1
  295. package/dist/src/telemetry/index.d.ts.map +1 -1
  296. package/dist/src/telemetry/index.js +1 -1
  297. package/dist/src/telemetry/index.js.map +1 -1
  298. package/dist/src/telemetry/wrap.d.ts +6 -0
  299. package/dist/src/telemetry/wrap.d.ts.map +1 -1
  300. package/dist/src/telemetry/wrap.js +65 -10
  301. package/dist/src/telemetry/wrap.js.map +1 -1
  302. package/dist/src/telemetry/wrap.test.js +81 -1
  303. package/dist/src/telemetry/wrap.test.js.map +1 -1
  304. package/dist/src/telemetry/wrap.tool-call-sink.test.d.ts +26 -0
  305. package/dist/src/telemetry/wrap.tool-call-sink.test.d.ts.map +1 -0
  306. package/dist/src/telemetry/wrap.tool-call-sink.test.js +162 -0
  307. package/dist/src/telemetry/wrap.tool-call-sink.test.js.map +1 -0
  308. package/dist/src/types/skill.d.ts +9 -1
  309. package/dist/src/types/skill.d.ts.map +1 -1
  310. package/dist/src/utils/index.d.ts +2 -1
  311. package/dist/src/utils/index.d.ts.map +1 -1
  312. package/dist/src/utils/index.js +3 -1
  313. package/dist/src/utils/index.js.map +1 -1
  314. package/dist/src/utils/skill-name.d.ts +19 -0
  315. package/dist/src/utils/skill-name.d.ts.map +1 -0
  316. package/dist/src/utils/skill-name.js +26 -0
  317. package/dist/src/utils/skill-name.js.map +1 -0
  318. package/dist/src/utils/skill-name.test.d.ts +2 -0
  319. package/dist/src/utils/skill-name.test.d.ts.map +1 -0
  320. package/dist/src/utils/skill-name.test.js +34 -0
  321. package/dist/src/utils/skill-name.test.js.map +1 -0
  322. package/dist/src/utils/version-check.d.ts +61 -0
  323. package/dist/src/utils/version-check.d.ts.map +1 -1
  324. package/dist/src/utils/version-check.js +78 -1
  325. package/dist/src/utils/version-check.js.map +1 -1
  326. package/dist/src/utils/version-check.test.js +130 -1
  327. package/dist/src/utils/version-check.test.js.map +1 -1
  328. package/dist/tests/db/migration-runner-race.test.js +19 -9
  329. package/dist/tests/db/migration-runner-race.test.js.map +1 -1
  330. package/dist/tests/install/fan-out.test.js +27 -0
  331. package/dist/tests/install/fan-out.test.js.map +1 -1
  332. package/dist/tests/security/ContinuousSecurity.performance.test.js +12 -7
  333. package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
  334. package/dist/tests/security/ContinuousSecurity.test.js +14 -2
  335. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
  336. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +10 -12
  337. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -1
  338. package/dist/tests/security/ai-defence-multiline-promotion.test.js +26 -33
  339. package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -1
  340. package/dist/tests/security/nested-instruction-long-attribute.test.js +20 -10
  341. package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -1
  342. package/dist/tests/security/scanner-evidence-tiers.test.js +15 -0
  343. package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -1
  344. package/dist/tests/security/scanner-multiline-cap.test.d.ts +21 -0
  345. package/dist/tests/security/scanner-multiline-cap.test.d.ts.map +1 -0
  346. package/dist/tests/security/scanner-multiline-cap.test.js +152 -0
  347. package/dist/tests/security/scanner-multiline-cap.test.js.map +1 -0
  348. package/dist/tests/security/scanner-regression-guard.test.js +37 -0
  349. package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
  350. package/dist/tests/security/scanner-witness-sw1.test.d.ts +32 -0
  351. package/dist/tests/security/scanner-witness-sw1.test.d.ts.map +1 -0
  352. package/dist/tests/security/scanner-witness-sw1.test.js +255 -0
  353. package/dist/tests/security/scanner-witness-sw1.test.js.map +1 -0
  354. package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts +2 -0
  355. package/dist/tests/security/sensitive-path-adversarial-review.test.d.ts.map +1 -0
  356. package/dist/tests/security/sensitive-path-adversarial-review.test.js +193 -0
  357. package/dist/tests/security/sensitive-path-adversarial-review.test.js.map +1 -0
  358. package/dist/tests/security/sensitive-path-fp.test.js +248 -0
  359. package/dist/tests/security/sensitive-path-fp.test.js.map +1 -1
  360. package/dist/tests/security/smi5879-corroboration.core.test.js +44 -0
  361. package/dist/tests/security/smi5879-corroboration.core.test.js.map +1 -1
  362. package/dist/tests/security/weak-password-lexicon.test.d.ts +18 -0
  363. package/dist/tests/security/weak-password-lexicon.test.d.ts.map +1 -0
  364. package/dist/tests/security/weak-password-lexicon.test.js +122 -0
  365. package/dist/tests/security/weak-password-lexicon.test.js.map +1 -0
  366. package/dist/tests/skill-scanner/allowlist.test.js +58 -4
  367. package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
  368. package/dist/tests/skill-scanner/categorizer.test.d.ts +2 -0
  369. package/dist/tests/skill-scanner/categorizer.test.d.ts.map +1 -0
  370. package/dist/tests/skill-scanner/categorizer.test.js +71 -0
  371. package/dist/tests/skill-scanner/categorizer.test.js.map +1 -0
  372. package/dist/tests/skill-scanner/cli-guard.test.d.ts +23 -0
  373. package/dist/tests/skill-scanner/cli-guard.test.d.ts.map +1 -0
  374. package/dist/tests/skill-scanner/cli-guard.test.js +71 -0
  375. package/dist/tests/skill-scanner/cli-guard.test.js.map +1 -0
  376. package/dist/tests/sync/SyncEngine.abort.test.js +15 -11
  377. package/dist/tests/sync/SyncEngine.abort.test.js.map +1 -1
  378. package/dist/tests/sync/SyncEngine.status.test.js +1 -1
  379. package/dist/tests/sync/SyncEngine.status.test.js.map +1 -1
  380. package/dist/tests/sync/SyncEngine.test.d.ts +11 -2
  381. package/dist/tests/sync/SyncEngine.test.d.ts.map +1 -1
  382. package/dist/tests/sync/SyncEngine.test.js +164 -13
  383. package/dist/tests/sync/SyncEngine.test.js.map +1 -1
  384. package/dist/tests/unit/migrations/migration-v18.test.d.ts +16 -0
  385. package/dist/tests/unit/migrations/migration-v18.test.d.ts.map +1 -0
  386. package/dist/tests/unit/migrations/migration-v18.test.js +192 -0
  387. package/dist/tests/unit/migrations/migration-v18.test.js.map +1 -0
  388. package/dist/tests/unit/services/skill-installation.content.test.js +101 -1
  389. package/dist/tests/unit/services/skill-installation.content.test.js.map +1 -1
  390. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts +29 -0
  391. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.d.ts.map +1 -0
  392. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js +200 -0
  393. package/dist/tests/unit/services/skill-installation.service.sensitive-path-fp.test.js.map +1 -0
  394. package/dist/tests/unit/services/skill-installation.service.test.js +9 -8
  395. package/dist/tests/unit/services/skill-installation.service.test.js.map +1 -1
  396. package/dist/tests/unit/sync-engine.source-aware.test.js +5 -5
  397. package/dist/tests/unit/sync-engine.source-aware.test.js.map +1 -1
  398. package/package.json +7 -2
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @fileoverview Unit tests for the shared skill content-hash comparator
3
+ * @see SMI-6343 Wave 2 — repair the content-hash comparison
4
+ */
5
+ import { describe, it, expect } from 'vitest';
6
+ import { compareSkillContentHashes, firstNonBlankHash } from './skill-content-comparison.js';
7
+ describe('compareSkillContentHashes', () => {
8
+ it('returns current when both hashes match exactly', () => {
9
+ const result = compareSkillContentHashes('abc123', 'abc123');
10
+ expect(result.outcome).toBe('current');
11
+ expect(result.reason).toBeUndefined();
12
+ });
13
+ it('returns outdated when the hashes differ', () => {
14
+ const result = compareSkillContentHashes('abc123', 'def456');
15
+ expect(result.outcome).toBe('outdated');
16
+ expect(result.reason).toBeUndefined();
17
+ });
18
+ it('returns unknown with a reason when the installed hash is missing', () => {
19
+ const result = compareSkillContentHashes(null, 'def456');
20
+ expect(result.outcome).toBe('unknown');
21
+ expect(result.reason).toMatch(/no installed content hash/i);
22
+ });
23
+ it('returns unknown with a reason when the installed hash is undefined', () => {
24
+ const result = compareSkillContentHashes(undefined, 'def456');
25
+ expect(result.outcome).toBe('unknown');
26
+ expect(result.reason).toMatch(/no installed content hash/i);
27
+ });
28
+ it('returns unknown with a reason when the registry hash is missing', () => {
29
+ const result = compareSkillContentHashes('abc123', null);
30
+ expect(result.outcome).toBe('unknown');
31
+ expect(result.reason).toMatch(/no registry content hash/i);
32
+ });
33
+ it('returns unknown with a reason when the registry hash is undefined', () => {
34
+ const result = compareSkillContentHashes('abc123', undefined);
35
+ expect(result.outcome).toBe('unknown');
36
+ expect(result.reason).toMatch(/no registry content hash/i);
37
+ });
38
+ it('returns unknown when both hashes are missing', () => {
39
+ const result = compareSkillContentHashes(null, null);
40
+ expect(result.outcome).toBe('unknown');
41
+ expect(result.reason).toMatch(/no installed content hash and no registry content hash/i);
42
+ });
43
+ it('treats a blank/whitespace-only hash the same as missing', () => {
44
+ expect(compareSkillContentHashes(' ', 'abc123').outcome).toBe('unknown');
45
+ expect(compareSkillContentHashes('abc123', ' ').outcome).toBe('unknown');
46
+ expect(compareSkillContentHashes('', '').outcome).toBe('unknown');
47
+ });
48
+ it('is case-sensitive — differently-cased hex digests are treated as different hashes', () => {
49
+ const result = compareSkillContentHashes('ABC123', 'abc123');
50
+ expect(result.outcome).toBe('outdated');
51
+ });
52
+ it('trims surrounding whitespace before comparing', () => {
53
+ const result = compareSkillContentHashes(' abc123 ', 'abc123');
54
+ expect(result.outcome).toBe('current');
55
+ });
56
+ });
57
+ describe('firstNonBlankHash (adversarial-review addition, SMI-6343)', () => {
58
+ it('returns the first candidate when it is non-blank', () => {
59
+ expect(firstNonBlankHash('abc123', 'def456')).toBe('abc123');
60
+ });
61
+ it('falls through past null/undefined to the next candidate', () => {
62
+ expect(firstNonBlankHash(null, 'def456')).toBe('def456');
63
+ expect(firstNonBlankHash(undefined, 'def456')).toBe('def456');
64
+ });
65
+ it('falls through past a BLANK (whitespace-only) first candidate — the exact case a raw `??` chain gets wrong', () => {
66
+ // `' ' ?? 'def456'` would incorrectly return ' ' (?? only falls
67
+ // through on null/undefined) — this is the whole reason this helper
68
+ // exists instead of a raw `??` chain at each call site.
69
+ expect(firstNonBlankHash(' ', 'def456')).toBe('def456');
70
+ expect(firstNonBlankHash('', 'def456')).toBe('def456');
71
+ });
72
+ it('trims a winning candidate', () => {
73
+ expect(firstNonBlankHash(' abc123 ')).toBe('abc123');
74
+ });
75
+ it('returns null when every candidate is blank/null/undefined', () => {
76
+ expect(firstNonBlankHash(null, undefined, ' ', '')).toBeNull();
77
+ expect(firstNonBlankHash()).toBeNull();
78
+ });
79
+ it('supports three or more candidates (on-disk-hash fallback chains)', () => {
80
+ expect(firstNonBlankHash(' ', undefined, 'on-disk-hash')).toBe('on-disk-hash');
81
+ });
82
+ });
83
+ //# sourceMappingURL=skill-content-comparison.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-content-comparison.test.js","sourceRoot":"","sources":["../../../src/services/skill-content-comparison.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AAE5F,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QACxD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,MAAM,GAAG,yBAAyB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAA;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QACxD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;QAC7D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,MAAM,GAAG,yBAAyB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACpD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACtC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,yDAAyD,CAAC,CAAA;IAC1F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,yBAAyB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC1E,MAAM,CAAC,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC1E,MAAM,CAAC,yBAAyB,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,mFAAmF,EAAE,GAAG,EAAE;QAC3F,MAAM,MAAM,GAAG,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;QAC5D,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;QAChE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACxC,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACzE,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACxD,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2GAA2G,EAAE,GAAG,EAAE;QACnH,oEAAoE;QACpE,oEAAoE;QACpE,wDAAwD;QACxD,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACzD,MAAM,CAAC,iBAAiB,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAA;QAChE,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAA;IACxC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAClF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * @fileoverview Shared skill-identity contradiction-signal classification
3
+ * @module @skillsmith/core/services/skill-identity-classification
4
+ * @see SMI-6343 Wave 3 — tamper-check classification (AC#3)
5
+ *
6
+ * Three deterministic-or-best-effort "does this manifest entry's recorded
7
+ * identity contradict what's actually installed" signals, plus a
8
+ * "has this on-disk content been locally edited since install" check —
9
+ * consumed by BOTH `packages/mcp-server/src/tools/outdated.ts` (via
10
+ * `outdated.identity.ts`) and `packages/cli/src/commands/manage.update.ts`,
11
+ * so the two packages cannot drift into two independently-maintained
12
+ * implementations of the same contradiction logic (exactly the "sibling
13
+ * implementation" bug class both Wave 1 and Wave 2 of SMI-6343 already hit).
14
+ *
15
+ * Deliberately decoupled from either caller's own `RegistrySkillInfo` shape
16
+ * (mcp-server's `install.types.ts` and core's own `skill-installation.types.ts`
17
+ * both have independently-evolved versions) — callers adapt their own
18
+ * registry-lookup result into the minimal {@link RegistryLookupOutcome} shape
19
+ * this module needs.
20
+ *
21
+ * Every function here is synchronous and filesystem-free by design: signal 3
22
+ * (path containment) is a pure string check against an already-resolved
23
+ * expected root directory, not a `fs.realpath` call — the callers of this
24
+ * module (`outdated.ts`, `manage.update.ts`) only ever reach the "outdated"
25
+ * classification branch after already having successfully read the
26
+ * installed skill's SKILL.md this same iteration, so "does installPath
27
+ * exist" is already established by the time these signals run.
28
+ */
29
+ import type { ContentComparisonOutcome } from './skill-content-comparison.js';
30
+ import type { ClientId } from '../install/paths.js';
31
+ /** Which of the three contradiction signals fired. */
32
+ export type IdentitySignal = 'owner-mismatch' | 'frontmatter-contradiction' | 'path-unresolved';
33
+ /** Why a classification could not be conclusively determined. */
34
+ export type IdentityInconclusiveReason = 'offline' | 'quota-exhausted' | 'network-error' | 'no-registry-record' | 'no-history';
35
+ /** The five-state classification `outdated.ts`/`manage.update.ts` report against a divergent entry. */
36
+ export type OutdatedClassificationState = 'current' | 'outdated' | 'local-drift' | 'identity-mismatch' | 'unknown';
37
+ /** Minimal registry-record shape signal 2 needs. */
38
+ export interface IdentityRegistryRecord {
39
+ author?: string | null;
40
+ name?: string | null;
41
+ }
42
+ /**
43
+ * The outcome of a caller's OWN registry lookup for the entry's claimed
44
+ * `id`, adapted into this module's minimal shape.
45
+ *
46
+ * `attempted: false` means the caller never even tried this run (offline,
47
+ * or an earlier skill in the same batch already exhausted quota) —
48
+ * `failureReason` in that case names WHY it wasn't attempted. `attempted:
49
+ * true` with `record: null` means the lookup completed but found nothing
50
+ * for this id (a distinct case from a failed lookup — see
51
+ * `no-registry-record` below).
52
+ */
53
+ export interface RegistryLookupOutcome {
54
+ attempted: boolean;
55
+ record: IdentityRegistryRecord | null;
56
+ /** Populated when the lookup either wasn't attempted or didn't complete. */
57
+ failureReason?: 'offline' | 'quota-exhausted' | 'network-error' | null;
58
+ }
59
+ /** The subset of a manifest entry every signal needs. */
60
+ export interface ManifestEntryForIdentity {
61
+ id: string;
62
+ source: string;
63
+ installPath: string;
64
+ client?: ClientId;
65
+ contentHash?: string;
66
+ originalContentHash?: string;
67
+ }
68
+ /** Result of {@link classifyManifestEntryIdentity}. */
69
+ export interface IdentityClassificationResult {
70
+ /** Which signal fired. Null when no signal fired (regardless of `inconclusive`). */
71
+ signal: IdentitySignal | null;
72
+ /** True when signal 2 (frontmatter) could not be checked at all. */
73
+ inconclusive: boolean;
74
+ /** Populated only when `inconclusive` is true. */
75
+ inconclusiveReason: IdentityInconclusiveReason | null;
76
+ }
77
+ /** Result of {@link classifyDivergentEntry}. */
78
+ export interface DivergentEntryClassification {
79
+ state: Exclude<OutdatedClassificationState, 'current'>;
80
+ signal: IdentitySignal | null;
81
+ inconclusiveReason: IdentityInconclusiveReason | null;
82
+ }
83
+ /**
84
+ * Parse the owner segment out of a manifest `source` string. Real installs
85
+ * write `source` as `'github:' + owner + '/' + repo` (`skill-installation.
86
+ * service.ts`) — the `'github:'` prefix is optional here so a bare
87
+ * `owner/repo` (or a future non-GitHub source shape) still parses. Returns
88
+ * `null` for the `'unknown'` distrust sentinel, a raw URL, or anything else
89
+ * with no parseable `owner/name` shape.
90
+ */
91
+ export declare function parseOwnerFromSource(source: string | undefined | null): string | null;
92
+ /**
93
+ * Parse the owner segment out of a manifest entry's `id`. Returns `null`
94
+ * for anything that isn't a clean `owner/name` pair — most importantly a
95
+ * raw GitHub URL, which a direct-URL install records as `id` verbatim (see
96
+ * {@link parseOwnerFromOwnerNamePair}).
97
+ */
98
+ export declare function parseOwnerFromId(id: string | undefined | null): string | null;
99
+ /**
100
+ * Signal 1: `id` parses as `owner/name` and `source` names a DIFFERENT
101
+ * owner. Deterministic, offline, zero network dependency. Catches the real
102
+ * `linear` skill corruption (`source: "github:lobehub/lobehub"` vs
103
+ * `id: "wrsmith108/linear"`).
104
+ */
105
+ export declare function detectOwnerMismatch(entry: Pick<ManifestEntryForIdentity, 'id' | 'source'>): boolean;
106
+ /**
107
+ * Signal 3: `installPath` is absent, or resolves OUTSIDE `expectedRootDir`
108
+ * (the claimed client's native install root, or a workspace-scoped
109
+ * equivalent the caller resolves — see each caller's own scope handling).
110
+ *
111
+ * Deliberately a pure string containment check (`path.relative`), not a
112
+ * filesystem call: both callers of this module only reach this signal after
113
+ * already having successfully read the entry's SKILL.md this same
114
+ * iteration, so filesystem existence is already established. This also
115
+ * catches the exact shape of the SMI-6343 Wave 1 test-fixture leak (an
116
+ * OS-temp-dir `installPath` written into the real manifest) via simple
117
+ * string comparison, with no need to resolve symlinks.
118
+ *
119
+ * NOT a symlink-escape check (Wave 3 adversarial review, considered and
120
+ * scoped out): a real `installPath` inside `expectedRootDir` that is
121
+ * ITSELF a symlink pointing elsewhere is a local-filesystem-tampering
122
+ * threat distinct from this signal's actual purpose (catching a manifest
123
+ * entry whose *recorded* path doesn't match a legitimate install
124
+ * location — a software-bug/registry-tamper shape, not local attacker
125
+ * capability); an attacker with write access to plant such a symlink
126
+ * inside the client root already has equivalent-or-greater capability to
127
+ * edit SKILL.md content directly, making a `fs.realpath` upgrade here
128
+ * (which would also force this whole synchronous, filesystem-free module
129
+ * to become async) address a threat this signal was never meant to cover.
130
+ *
131
+ * The parent-segment check below (`rel === '..' || rel.startsWith('..' +
132
+ * sep)`) is deliberately NOT a bare `rel.startsWith('..')`: a literal
133
+ * directory name that happens to start with two dots (e.g. `..cache`) is a
134
+ * legitimate contained child — `path.relative('/r', '/r/..cache')` returns
135
+ * `'..cache'`, which a bare `startsWith('..')` would misclassify as an
136
+ * escape (Wave 3 adversarial review finding).
137
+ */
138
+ export declare function detectPathUnresolved(installPath: string | undefined | null, expectedRootDir: string): boolean;
139
+ /**
140
+ * Run all three contradiction signals against a manifest entry. Any one
141
+ * conclusively firing means `signal` is non-null — order (1, 3, 2) checks
142
+ * the two deterministic/offline signals first, only falling to the
143
+ * network-dependent signal 2 when neither of those already answered the
144
+ * question.
145
+ */
146
+ export declare function classifyManifestEntryIdentity(params: {
147
+ entry: ManifestEntryForIdentity;
148
+ localContent: string | null;
149
+ expectedRootDir: string;
150
+ registryLookup: RegistryLookupOutcome;
151
+ }): IdentityClassificationResult;
152
+ /**
153
+ * Has the on-disk content changed since the manifest's own recorded
154
+ * install/update-time hash? This is what distinguishes a benign local edit
155
+ * (`local-drift`) from a genuine registry version bump (`outdated`) once
156
+ * no identity signal has fired.
157
+ *
158
+ * When the manifest never recorded a comparable hash at all (a legacy or
159
+ * adopted entry, `contentHash`/`originalContentHash` both absent), this
160
+ * deliberately defaults to `false` (no edit evidence) rather than `true`
161
+ * (conservative) — absence of a recorded hash is a data-quality gap, not
162
+ * positive evidence of tampering, and defaulting to `true` here would
163
+ * misclassify the large population of legacy/adopted entries that never had
164
+ * a hash recorded as `local-drift` even when they are genuinely just
165
+ * outdated (SMI-6343 Wave 3 review finding).
166
+ */
167
+ export declare function hasRecordedLocalEdit(entry: Pick<ManifestEntryForIdentity, 'contentHash' | 'originalContentHash'>, localHash: string | null): boolean;
168
+ /**
169
+ * Classify an entry the caller has ALREADY determined is divergent from the
170
+ * registry (mcp-server: `compareSkillContentHashes(...).outcome ===
171
+ * 'outdated'`; CLI: `getSkillDiff()` found a version/content difference).
172
+ * Never returns `'current'` — callers own that trivial case themselves,
173
+ * since it never needs signal evaluation at all.
174
+ */
175
+ export declare function classifyDivergentEntry(params: {
176
+ entry: ManifestEntryForIdentity;
177
+ localHash: string | null;
178
+ localContent: string | null;
179
+ expectedRootDir: string;
180
+ registryLookup: RegistryLookupOutcome;
181
+ }): DivergentEntryClassification;
182
+ /**
183
+ * Full 5-state classification given an already-computed content-comparison
184
+ * outcome (from `compareSkillContentHashes`) plus the caller-derived reason
185
+ * a plain `'unknown'` comparison outcome couldn't be resolved (offline,
186
+ * quota-exhausted, network-error, or no-history — the caller already tracks
187
+ * which applies; see `outdated.ts`'s `deriveUnknownReason`).
188
+ */
189
+ export declare function classifyOutdatedState(params: {
190
+ comparisonOutcome: ContentComparisonOutcome;
191
+ unknownReasonWhenComparisonUnknown: IdentityInconclusiveReason;
192
+ entry: ManifestEntryForIdentity;
193
+ localHash: string | null;
194
+ localContent: string | null;
195
+ expectedRootDir: string;
196
+ registryLookup: RegistryLookupOutcome;
197
+ }): {
198
+ state: OutdatedClassificationState;
199
+ signal: IdentitySignal | null;
200
+ inconclusiveReason: IdentityInconclusiveReason | null;
201
+ };
202
+ //# sourceMappingURL=skill-identity-classification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-identity-classification.d.ts","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,295 @@
1
+ /**
2
+ * @fileoverview Shared skill-identity contradiction-signal classification
3
+ * @module @skillsmith/core/services/skill-identity-classification
4
+ * @see SMI-6343 Wave 3 — tamper-check classification (AC#3)
5
+ *
6
+ * Three deterministic-or-best-effort "does this manifest entry's recorded
7
+ * identity contradict what's actually installed" signals, plus a
8
+ * "has this on-disk content been locally edited since install" check —
9
+ * consumed by BOTH `packages/mcp-server/src/tools/outdated.ts` (via
10
+ * `outdated.identity.ts`) and `packages/cli/src/commands/manage.update.ts`,
11
+ * so the two packages cannot drift into two independently-maintained
12
+ * implementations of the same contradiction logic (exactly the "sibling
13
+ * implementation" bug class both Wave 1 and Wave 2 of SMI-6343 already hit).
14
+ *
15
+ * Deliberately decoupled from either caller's own `RegistrySkillInfo` shape
16
+ * (mcp-server's `install.types.ts` and core's own `skill-installation.types.ts`
17
+ * both have independently-evolved versions) — callers adapt their own
18
+ * registry-lookup result into the minimal {@link RegistryLookupOutcome} shape
19
+ * this module needs.
20
+ *
21
+ * Every function here is synchronous and filesystem-free by design: signal 3
22
+ * (path containment) is a pure string check against an already-resolved
23
+ * expected root directory, not a `fs.realpath` call — the callers of this
24
+ * module (`outdated.ts`, `manage.update.ts`) only ever reach the "outdated"
25
+ * classification branch after already having successfully read the
26
+ * installed skill's SKILL.md this same iteration, so "does installPath
27
+ * exist" is already established by the time these signals run.
28
+ */
29
+ import { relative, isAbsolute, sep } from 'node:path';
30
+ import { SkillParser } from '../indexer/SkillParser.js';
31
+ import { firstNonBlankHash } from './skill-content-comparison.js';
32
+ // ============================================================================
33
+ // Signal 1 — owner mismatch
34
+ // ============================================================================
35
+ /**
36
+ * Parse a clean `owner/name` pair (exactly one slash, no URL scheme) into
37
+ * its owner segment. Returns `null` for anything that isn't that exact
38
+ * shape — critically, a raw URL (`https://github.com/owner/repo`, a
39
+ * direct-URL install's `id`/`source`) is NOT an `owner/name` pair and must
40
+ * return `null` here rather than a garbage "owner" parsed from before the
41
+ * URL's first slash (e.g. `'https:'`). Mirrors the same 2-segment
42
+ * distinction `install.helpers.ts`'s `parseSkillId()` already draws between
43
+ * a registry id and a direct GitHub reference.
44
+ */
45
+ function parseOwnerFromOwnerNamePair(value) {
46
+ if (value.includes('://'))
47
+ return null;
48
+ const parts = value.split('/');
49
+ if (parts.length !== 2)
50
+ return null;
51
+ const [owner, name] = parts;
52
+ return owner.length > 0 && name.length > 0 ? owner : null;
53
+ }
54
+ /**
55
+ * Parse the owner segment out of a manifest `source` string. Real installs
56
+ * write `source` as `'github:' + owner + '/' + repo` (`skill-installation.
57
+ * service.ts`) — the `'github:'` prefix is optional here so a bare
58
+ * `owner/repo` (or a future non-GitHub source shape) still parses. Returns
59
+ * `null` for the `'unknown'` distrust sentinel, a raw URL, or anything else
60
+ * with no parseable `owner/name` shape.
61
+ */
62
+ export function parseOwnerFromSource(source) {
63
+ if (!source || source === 'unknown')
64
+ return null;
65
+ const withoutPrefix = source.startsWith('github:') ? source.slice('github:'.length) : source;
66
+ return parseOwnerFromOwnerNamePair(withoutPrefix);
67
+ }
68
+ /**
69
+ * Parse the owner segment out of a manifest entry's `id`. Returns `null`
70
+ * for anything that isn't a clean `owner/name` pair — most importantly a
71
+ * raw GitHub URL, which a direct-URL install records as `id` verbatim (see
72
+ * {@link parseOwnerFromOwnerNamePair}).
73
+ */
74
+ export function parseOwnerFromId(id) {
75
+ if (!id)
76
+ return null;
77
+ return parseOwnerFromOwnerNamePair(id);
78
+ }
79
+ /**
80
+ * Signal 1: `id` parses as `owner/name` and `source` names a DIFFERENT
81
+ * owner. Deterministic, offline, zero network dependency. Catches the real
82
+ * `linear` skill corruption (`source: "github:lobehub/lobehub"` vs
83
+ * `id: "wrsmith108/linear"`).
84
+ */
85
+ export function detectOwnerMismatch(entry) {
86
+ const idOwner = parseOwnerFromId(entry.id);
87
+ const sourceOwner = parseOwnerFromSource(entry.source);
88
+ if (!idOwner || !sourceOwner)
89
+ return false;
90
+ return idOwner.toLowerCase() !== sourceOwner.toLowerCase();
91
+ }
92
+ // ============================================================================
93
+ // Signal 3 — path unresolved
94
+ // ============================================================================
95
+ /**
96
+ * Signal 3: `installPath` is absent, or resolves OUTSIDE `expectedRootDir`
97
+ * (the claimed client's native install root, or a workspace-scoped
98
+ * equivalent the caller resolves — see each caller's own scope handling).
99
+ *
100
+ * Deliberately a pure string containment check (`path.relative`), not a
101
+ * filesystem call: both callers of this module only reach this signal after
102
+ * already having successfully read the entry's SKILL.md this same
103
+ * iteration, so filesystem existence is already established. This also
104
+ * catches the exact shape of the SMI-6343 Wave 1 test-fixture leak (an
105
+ * OS-temp-dir `installPath` written into the real manifest) via simple
106
+ * string comparison, with no need to resolve symlinks.
107
+ *
108
+ * NOT a symlink-escape check (Wave 3 adversarial review, considered and
109
+ * scoped out): a real `installPath` inside `expectedRootDir` that is
110
+ * ITSELF a symlink pointing elsewhere is a local-filesystem-tampering
111
+ * threat distinct from this signal's actual purpose (catching a manifest
112
+ * entry whose *recorded* path doesn't match a legitimate install
113
+ * location — a software-bug/registry-tamper shape, not local attacker
114
+ * capability); an attacker with write access to plant such a symlink
115
+ * inside the client root already has equivalent-or-greater capability to
116
+ * edit SKILL.md content directly, making a `fs.realpath` upgrade here
117
+ * (which would also force this whole synchronous, filesystem-free module
118
+ * to become async) address a threat this signal was never meant to cover.
119
+ *
120
+ * The parent-segment check below (`rel === '..' || rel.startsWith('..' +
121
+ * sep)`) is deliberately NOT a bare `rel.startsWith('..')`: a literal
122
+ * directory name that happens to start with two dots (e.g. `..cache`) is a
123
+ * legitimate contained child — `path.relative('/r', '/r/..cache')` returns
124
+ * `'..cache'`, which a bare `startsWith('..')` would misclassify as an
125
+ * escape (Wave 3 adversarial review finding).
126
+ */
127
+ export function detectPathUnresolved(installPath, expectedRootDir) {
128
+ if (!installPath)
129
+ return true;
130
+ const rel = relative(expectedRootDir, installPath);
131
+ if (rel === '')
132
+ return false;
133
+ return rel === '..' || rel.startsWith('..' + sep) || isAbsolute(rel);
134
+ }
135
+ function normalize(value) {
136
+ if (typeof value !== 'string')
137
+ return null;
138
+ const trimmed = value.trim();
139
+ return trimmed.length > 0 ? trimmed : null;
140
+ }
141
+ /**
142
+ * Signal 2: on-disk front-matter `author`/`name` contradicts the registry's
143
+ * record for the claimed `id`. Catches the real `commit` skill corruption
144
+ * (internally-consistent id/source, but content that belongs to an
145
+ * unrelated skill) — signal 1 alone does not catch this shape.
146
+ *
147
+ * H2 (fail-closed): when the registry lookup could not complete (offline,
148
+ * network error, quota-exhausted) OR completed but found no record for the
149
+ * claimed id, this returns `inconclusive: true` — NEVER "no contradiction."
150
+ * An inconclusive signal 2 still forces the overall classification to
151
+ * `unknown`, not `local-drift`/`current` — see {@link classifyDivergentEntry}.
152
+ *
153
+ * Exception, not a weakening of H2: when `id` itself doesn't parse as a
154
+ * well-formed `owner/name` registry key (a raw GitHub URL from a direct-URL
155
+ * install, an unresolved UUID) there is structurally no registry record to
156
+ * look up in the first place — a caller cannot "fail to check" something
157
+ * that was never a registry-backed identity to begin with. This is a clean
158
+ * non-fire, not an inconclusive one, exactly mirroring how an unparseable
159
+ * on-disk front-matter is treated below.
160
+ */
161
+ function evaluateFrontmatterContradiction(entryId, localContent, registryLookup) {
162
+ if (parseOwnerFromId(entryId) === null) {
163
+ return { fired: false, inconclusive: false, inconclusiveReason: null };
164
+ }
165
+ if (!registryLookup.attempted) {
166
+ return {
167
+ fired: false,
168
+ inconclusive: true,
169
+ inconclusiveReason: registryLookup.failureReason ?? 'network-error',
170
+ };
171
+ }
172
+ if (registryLookup.failureReason) {
173
+ return { fired: false, inconclusive: true, inconclusiveReason: registryLookup.failureReason };
174
+ }
175
+ if (!registryLookup.record) {
176
+ return { fired: false, inconclusive: true, inconclusiveReason: 'no-registry-record' };
177
+ }
178
+ const parsed = localContent !== null ? new SkillParser().parse(localContent) : null;
179
+ if (!parsed) {
180
+ // Nothing to contradict with — a local parse failure is a data-quality
181
+ // fact, not a network-dependent uncertainty, so this is a clean
182
+ // non-fire, not `inconclusive` (see this module's fileoverview).
183
+ return { fired: false, inconclusive: false, inconclusiveReason: null };
184
+ }
185
+ const localAuthor = normalize(parsed.author);
186
+ const localName = normalize(parsed.name);
187
+ const registryAuthor = normalize(registryLookup.record.author);
188
+ const registryName = normalize(registryLookup.record.name);
189
+ const authorContradiction = localAuthor !== null &&
190
+ registryAuthor !== null &&
191
+ localAuthor.toLowerCase() !== registryAuthor.toLowerCase();
192
+ const nameContradiction = localName !== null &&
193
+ registryName !== null &&
194
+ localName.toLowerCase() !== registryName.toLowerCase();
195
+ return {
196
+ fired: authorContradiction || nameContradiction,
197
+ inconclusive: false,
198
+ inconclusiveReason: null,
199
+ };
200
+ }
201
+ // ============================================================================
202
+ // Combined signal classification
203
+ // ============================================================================
204
+ /**
205
+ * Run all three contradiction signals against a manifest entry. Any one
206
+ * conclusively firing means `signal` is non-null — order (1, 3, 2) checks
207
+ * the two deterministic/offline signals first, only falling to the
208
+ * network-dependent signal 2 when neither of those already answered the
209
+ * question.
210
+ */
211
+ export function classifyManifestEntryIdentity(params) {
212
+ const { entry, localContent, expectedRootDir, registryLookup } = params;
213
+ if (detectOwnerMismatch(entry)) {
214
+ return { signal: 'owner-mismatch', inconclusive: false, inconclusiveReason: null };
215
+ }
216
+ if (detectPathUnresolved(entry.installPath, expectedRootDir)) {
217
+ return { signal: 'path-unresolved', inconclusive: false, inconclusiveReason: null };
218
+ }
219
+ const signal2 = evaluateFrontmatterContradiction(entry.id, localContent, registryLookup);
220
+ if (signal2.inconclusive) {
221
+ return { signal: null, inconclusive: true, inconclusiveReason: signal2.inconclusiveReason };
222
+ }
223
+ if (signal2.fired) {
224
+ return { signal: 'frontmatter-contradiction', inconclusive: false, inconclusiveReason: null };
225
+ }
226
+ return { signal: null, inconclusive: false, inconclusiveReason: null };
227
+ }
228
+ // ============================================================================
229
+ // Local-edit detection (local-drift vs outdated)
230
+ // ============================================================================
231
+ /**
232
+ * Has the on-disk content changed since the manifest's own recorded
233
+ * install/update-time hash? This is what distinguishes a benign local edit
234
+ * (`local-drift`) from a genuine registry version bump (`outdated`) once
235
+ * no identity signal has fired.
236
+ *
237
+ * When the manifest never recorded a comparable hash at all (a legacy or
238
+ * adopted entry, `contentHash`/`originalContentHash` both absent), this
239
+ * deliberately defaults to `false` (no edit evidence) rather than `true`
240
+ * (conservative) — absence of a recorded hash is a data-quality gap, not
241
+ * positive evidence of tampering, and defaulting to `true` here would
242
+ * misclassify the large population of legacy/adopted entries that never had
243
+ * a hash recorded as `local-drift` even when they are genuinely just
244
+ * outdated (SMI-6343 Wave 3 review finding).
245
+ */
246
+ export function hasRecordedLocalEdit(entry, localHash) {
247
+ const recordedHash = firstNonBlankHash(entry.contentHash, entry.originalContentHash);
248
+ if (recordedHash === null || localHash === null)
249
+ return false;
250
+ return recordedHash !== localHash;
251
+ }
252
+ // ============================================================================
253
+ // Full divergent-entry classification
254
+ // ============================================================================
255
+ /**
256
+ * Classify an entry the caller has ALREADY determined is divergent from the
257
+ * registry (mcp-server: `compareSkillContentHashes(...).outcome ===
258
+ * 'outdated'`; CLI: `getSkillDiff()` found a version/content difference).
259
+ * Never returns `'current'` — callers own that trivial case themselves,
260
+ * since it never needs signal evaluation at all.
261
+ */
262
+ export function classifyDivergentEntry(params) {
263
+ const identity = classifyManifestEntryIdentity(params);
264
+ if (identity.signal) {
265
+ return { state: 'identity-mismatch', signal: identity.signal, inconclusiveReason: null };
266
+ }
267
+ if (identity.inconclusive) {
268
+ return { state: 'unknown', signal: null, inconclusiveReason: identity.inconclusiveReason };
269
+ }
270
+ const hasLocalEdit = hasRecordedLocalEdit(params.entry, params.localHash);
271
+ return hasLocalEdit
272
+ ? { state: 'local-drift', signal: null, inconclusiveReason: null }
273
+ : { state: 'outdated', signal: null, inconclusiveReason: null };
274
+ }
275
+ /**
276
+ * Full 5-state classification given an already-computed content-comparison
277
+ * outcome (from `compareSkillContentHashes`) plus the caller-derived reason
278
+ * a plain `'unknown'` comparison outcome couldn't be resolved (offline,
279
+ * quota-exhausted, network-error, or no-history — the caller already tracks
280
+ * which applies; see `outdated.ts`'s `deriveUnknownReason`).
281
+ */
282
+ export function classifyOutdatedState(params) {
283
+ if (params.comparisonOutcome === 'current') {
284
+ return { state: 'current', signal: null, inconclusiveReason: null };
285
+ }
286
+ if (params.comparisonOutcome === 'unknown') {
287
+ return {
288
+ state: 'unknown',
289
+ signal: null,
290
+ inconclusiveReason: params.unknownReasonWhenComparisonUnknown,
291
+ };
292
+ }
293
+ return classifyDivergentEntry(params);
294
+ }
295
+ //# sourceMappingURL=skill-identity-classification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-identity-classification.js","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Unit tests for the shared skill-identity classification module
3
+ * @see SMI-6343 Wave 3 — tamper-check classification (AC#3)
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=skill-identity-classification.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-identity-classification.test.d.ts","sourceRoot":"","sources":["../../../src/services/skill-identity-classification.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}