exonous 0.0.0-stage → 2.10.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 (385) hide show
  1. package/README.md +100 -2
  2. package/assets/THIRD-PARTY-NOTICES.md +29 -0
  3. package/assets/adapters/opencode/exonous.js +54 -0
  4. package/assets/agents/exo-backend.md +22 -0
  5. package/assets/agents/exo-cto.md +22 -0
  6. package/assets/agents/exo-data-ai.md +22 -0
  7. package/assets/agents/exo-designer.md +22 -0
  8. package/assets/agents/exo-devops.md +22 -0
  9. package/assets/agents/exo-frontend.md +22 -0
  10. package/assets/agents/exo-infra.md +22 -0
  11. package/assets/agents/exo-mobile.md +22 -0
  12. package/assets/agents/exo-pm.md +22 -0
  13. package/assets/agents/exo-qa.md +22 -0
  14. package/assets/agents/exo-researcher.md +22 -0
  15. package/assets/agents/exo-security.md +22 -0
  16. package/assets/agents/roster.json +209 -0
  17. package/assets/agents/taxis.js +189 -0
  18. package/assets/editors/cursor/frontend-skills.mdc +27 -0
  19. package/assets/editors/hooks/cursor/hooks.json +11 -0
  20. package/assets/machine/PR-TEMPLATE.md +21 -0
  21. package/assets/machine/hooks/anamnesis.cjs +61 -0
  22. package/assets/machine/hooks/ethos.cjs +48 -0
  23. package/assets/machine/hooks/kairos.cjs +46 -0
  24. package/assets/machine/hooks/lib/cli-options.cjs +133 -0
  25. package/assets/machine/hooks/lib/memory.cjs +488 -0
  26. package/assets/machine/hooks/lib/metron.cjs +52 -0
  27. package/assets/machine/hooks/lib/paths.cjs +99 -0
  28. package/assets/machine/hooks/lib/payload.cjs +81 -0
  29. package/assets/machine/hooks/lib/pr-template-validate.cjs +103 -0
  30. package/assets/machine/hooks/lib/release.cjs +58 -0
  31. package/assets/machine/hooks/lib/repo.cjs +138 -0
  32. package/assets/machine/hooks/lib/sanitize.cjs +37 -0
  33. package/assets/machine/hooks/lib/selfupdate.cjs +354 -0
  34. package/assets/machine/hooks/lib/shell.cjs +722 -0
  35. package/assets/machine/hooks/lib/template.cjs +70 -0
  36. package/assets/machine/hooks/lib/token.cjs +110 -0
  37. package/assets/machine/hooks/lib/validate-pr-body.cjs +21 -0
  38. package/assets/machine/hooks/pyle.cjs +302 -0
  39. package/assets/rules/nomos.md +59 -0
  40. package/assets/rules/praxis.md +38 -0
  41. package/assets/skills/code-review/SKILL.md +124 -0
  42. package/assets/skills/commit-changes/SKILL.md +49 -0
  43. package/assets/skills/deliver/SKILL.md +114 -0
  44. package/assets/skills/deliver/references/parallel.md +37 -0
  45. package/assets/skills/doc/SKILL.md +140 -0
  46. package/assets/skills/doc/doc.mjs +277 -0
  47. package/assets/skills/issue-board/SKILL.md +84 -0
  48. package/assets/skills/issue-board/board.mjs +389 -0
  49. package/assets/skills/issue-board/config.example.json +9 -0
  50. package/assets/skills/issue-board/render.py +189 -0
  51. package/assets/skills/make-pr/SKILL.md +24 -0
  52. package/assets/skills/native-ui/SKILL.md +67 -0
  53. package/assets/skills/native-ui/reference/apple.md +180 -0
  54. package/assets/skills/native-ui/reference/mobile-feel.md +221 -0
  55. package/assets/skills/native-ui/reference/swift.md +289 -0
  56. package/assets/skills/project-memory/SKILL.md +59 -0
  57. package/assets/skills/project-memory/config.example.json +4 -0
  58. package/assets/skills/project-memory/project-memory.mjs +478 -0
  59. package/assets/skills/security-review/SKILL.md +102 -0
  60. package/assets/skills/session-handoff/SKILL.md +23 -0
  61. package/assets/skills/session-handoff/config.example.json +3 -0
  62. package/assets/skills/ui-design/SKILL.md +75 -0
  63. package/assets/skills/ui-design/reference/adapt.md +151 -0
  64. package/assets/skills/ui-design/reference/adapt.native.md +35 -0
  65. package/assets/skills/ui-design/reference/android.md +50 -0
  66. package/assets/skills/ui-design/reference/animate.md +87 -0
  67. package/assets/skills/ui-design/reference/audit.md +76 -0
  68. package/assets/skills/ui-design/reference/audit.native.md +126 -0
  69. package/assets/skills/ui-design/reference/bolder.md +33 -0
  70. package/assets/skills/ui-design/reference/clarify.md +84 -0
  71. package/assets/skills/ui-design/reference/colorize.md +86 -0
  72. package/assets/skills/ui-design/reference/craft-floor.md +25 -0
  73. package/assets/skills/ui-design/reference/critique.md +345 -0
  74. package/assets/skills/ui-design/reference/degraded/asset-producer.md +35 -0
  75. package/assets/skills/ui-design/reference/degraded/documenter.md +23 -0
  76. package/assets/skills/ui-design/reference/degraded/finish-reviewer.md +43 -0
  77. package/assets/skills/ui-design/reference/degraded/manual-edit-applier.md +56 -0
  78. package/assets/skills/ui-design/reference/delight.md +63 -0
  79. package/assets/skills/ui-design/reference/distill.md +98 -0
  80. package/assets/skills/ui-design/reference/doctor.md +49 -0
  81. package/assets/skills/ui-design/reference/document.md +273 -0
  82. package/assets/skills/ui-design/reference/extract.md +52 -0
  83. package/assets/skills/ui-design/reference/harden.md +122 -0
  84. package/assets/skills/ui-design/reference/hooks.md +114 -0
  85. package/assets/skills/ui-design/reference/init.md +47 -0
  86. package/assets/skills/ui-design/reference/ios.md +55 -0
  87. package/assets/skills/ui-design/reference/layout.md +81 -0
  88. package/assets/skills/ui-design/reference/live-setup.md +75 -0
  89. package/assets/skills/ui-design/reference/live.md +326 -0
  90. package/assets/skills/ui-design/reference/new-work.md +350 -0
  91. package/assets/skills/ui-design/reference/onboard.md +85 -0
  92. package/assets/skills/ui-design/reference/operate.md +29 -0
  93. package/assets/skills/ui-design/reference/optimize.md +101 -0
  94. package/assets/skills/ui-design/reference/overdrive.md +83 -0
  95. package/assets/skills/ui-design/reference/polish.md +77 -0
  96. package/assets/skills/ui-design/reference/quieter.md +95 -0
  97. package/assets/skills/ui-design/reference/routing.md +37 -0
  98. package/assets/skills/ui-design/reference/shape.md +43 -0
  99. package/assets/skills/ui-design/reference/styles/brutalist.md +45 -0
  100. package/assets/skills/ui-design/reference/styles/design-md.md +70 -0
  101. package/assets/skills/ui-design/reference/styles/direction.md +95 -0
  102. package/assets/skills/ui-design/reference/styles/full-output.md +42 -0
  103. package/assets/skills/ui-design/reference/styles/minimalist.md +64 -0
  104. package/assets/skills/ui-design/reference/styles/premium.md +65 -0
  105. package/assets/skills/ui-design/reference/styles/redesign.md +59 -0
  106. package/assets/skills/ui-design/reference/typeset.md +73 -0
  107. package/assets/skills/ui-design/reference/visualize.md +156 -0
  108. package/assets/skills/ui-design/scripts/ENGINE-LICENSE +191 -0
  109. package/assets/skills/ui-design/scripts/VERSION +1 -0
  110. package/assets/skills/ui-design/scripts/bin/darwin-arm64/impeccable +0 -0
  111. package/assets/skills/ui-design/scripts/command-metadata.json +94 -0
  112. package/assets/skills/ui-design/scripts/data/font-index-failures.json +121 -0
  113. package/assets/skills/ui-design/scripts/data/font-index.json +1 -0
  114. package/assets/skills/ui-design/scripts/impeccable +206 -0
  115. package/assets/skills/ui-design/scripts/impeccable.cmd +214 -0
  116. package/assets/skills/ui-design/scripts/live-browser-dom.js +167 -0
  117. package/assets/skills/ui-design/scripts/live-browser-ignores.js +242 -0
  118. package/assets/skills/ui-design/scripts/live-browser-session.js +144 -0
  119. package/assets/skills/ui-design/scripts/live-browser.js +12990 -0
  120. package/assets/skills/ui-design/scripts/modern-screenshot.umd.js +14 -0
  121. package/assets/skills/ui-design/scripts/ui-design +6 -0
  122. package/assets/skills/ui-design/scripts/ui-design.cmd +3 -0
  123. package/assets/skills/ui-design/scripts/ui-design.mjs +124 -0
  124. package/assets/skills/ui-mockup/SKILL.md +68 -0
  125. package/assets/skills/ui-mockup/reference/brandkit.md +101 -0
  126. package/assets/skills/ui-mockup/reference/image-to-code.md +83 -0
  127. package/assets/skills/ui-mockup/reference/mobile-comps.md +94 -0
  128. package/assets/skills/ui-mockup/reference/web-comps.md +83 -0
  129. package/assets/skills/ui-motion/SKILL.md +112 -0
  130. package/assets/skills/ui-motion/reference/expo.md +224 -0
  131. package/assets/skills/ui-motion/reference/opportunities.md +81 -0
  132. package/assets/skills/ui-motion/reference/prototype.md +238 -0
  133. package/assets/skills/ui-motion/reference/review.md +163 -0
  134. package/assets/skills/ui-motion/reference/toasts.md +144 -0
  135. package/assets/skills/ui-motion/reference/ui-libraries.md +71 -0
  136. package/assets/skills/ui-motion/reference/vocabulary.md +130 -0
  137. package/assets/skills/ui-motion/reference/web.md +294 -0
  138. package/assets/skills/ui-patterns/SKILL.md +105 -0
  139. package/assets/skills/ui-patterns/references/stacks/html-tailwind.md +40 -0
  140. package/assets/skills/ui-patterns/references/stacks/nextjs.md +52 -0
  141. package/assets/skills/ui-patterns/references/stacks/nuxt.md +100 -0
  142. package/assets/skills/ui-patterns/references/stacks/react-native.md +48 -0
  143. package/assets/skills/ui-patterns/references/stacks/react.md +49 -0
  144. package/assets/skills/ui-patterns/references/stacks/shadcn.md +57 -0
  145. package/assets/skills/ui-patterns/references/stacks/vue.md +37 -0
  146. package/assets/techne/.claude-plugin/marketplace.json +31 -0
  147. package/assets/techne/.claude-plugin/plugin.json +24 -0
  148. package/assets/techne/.mcp.json +8 -0
  149. package/assets/techne/commands/build-fix.md +48 -0
  150. package/assets/techne/commands/refactor-clean.md +46 -0
  151. package/assets/techne/commands/test-coverage.md +42 -0
  152. package/assets/techne/hooks/hooks.json +49 -0
  153. package/assets/techne/rules/angular/coding-style.md +49 -0
  154. package/assets/techne/rules/angular/hooks.md +23 -0
  155. package/assets/techne/rules/angular/patterns.md +48 -0
  156. package/assets/techne/rules/angular/security.md +26 -0
  157. package/assets/techne/rules/angular/testing.md +46 -0
  158. package/assets/techne/rules/arkts/coding-style.md +63 -0
  159. package/assets/techne/rules/arkts/hooks.md +38 -0
  160. package/assets/techne/rules/arkts/patterns.md +42 -0
  161. package/assets/techne/rules/arkts/security.md +35 -0
  162. package/assets/techne/rules/arkts/testing.md +39 -0
  163. package/assets/techne/rules/common/agents.md +6 -0
  164. package/assets/techne/rules/common/code-review.md +15 -0
  165. package/assets/techne/rules/common/coding-style.md +13 -0
  166. package/assets/techne/rules/common/development-workflow.md +5 -0
  167. package/assets/techne/rules/common/git-workflow.md +30 -0
  168. package/assets/techne/rules/common/hooks.md +7 -0
  169. package/assets/techne/rules/common/patterns.md +3 -0
  170. package/assets/techne/rules/common/performance.md +3 -0
  171. package/assets/techne/rules/common/security.md +5 -0
  172. package/assets/techne/rules/common/testing.md +3 -0
  173. package/assets/techne/rules/cpp/coding-style.md +31 -0
  174. package/assets/techne/rules/cpp/hooks.md +26 -0
  175. package/assets/techne/rules/cpp/patterns.md +31 -0
  176. package/assets/techne/rules/cpp/security.md +37 -0
  177. package/assets/techne/rules/cpp/testing.md +31 -0
  178. package/assets/techne/rules/csharp/coding-style.md +30 -0
  179. package/assets/techne/rules/csharp/hooks.md +24 -0
  180. package/assets/techne/rules/csharp/patterns.md +28 -0
  181. package/assets/techne/rules/csharp/security.md +32 -0
  182. package/assets/techne/rules/csharp/testing.md +27 -0
  183. package/assets/techne/rules/dart/coding-style.md +47 -0
  184. package/assets/techne/rules/dart/hooks.md +26 -0
  185. package/assets/techne/rules/dart/patterns.md +38 -0
  186. package/assets/techne/rules/dart/security.md +44 -0
  187. package/assets/techne/rules/dart/testing.md +39 -0
  188. package/assets/techne/rules/fsharp/coding-style.md +34 -0
  189. package/assets/techne/rules/fsharp/hooks.md +25 -0
  190. package/assets/techne/rules/fsharp/patterns.md +43 -0
  191. package/assets/techne/rules/fsharp/security.md +32 -0
  192. package/assets/techne/rules/fsharp/testing.md +27 -0
  193. package/assets/techne/rules/golang/coding-style.md +30 -0
  194. package/assets/techne/rules/golang/hooks.md +17 -0
  195. package/assets/techne/rules/golang/patterns.md +43 -0
  196. package/assets/techne/rules/golang/security.md +33 -0
  197. package/assets/techne/rules/golang/testing.md +20 -0
  198. package/assets/techne/rules/java/coding-style.md +51 -0
  199. package/assets/techne/rules/java/hooks.md +18 -0
  200. package/assets/techne/rules/java/patterns.md +55 -0
  201. package/assets/techne/rules/java/security.md +33 -0
  202. package/assets/techne/rules/java/testing.md +33 -0
  203. package/assets/techne/rules/kotlin/coding-style.md +50 -0
  204. package/assets/techne/rules/kotlin/hooks.md +17 -0
  205. package/assets/techne/rules/kotlin/patterns.md +44 -0
  206. package/assets/techne/rules/kotlin/security.md +34 -0
  207. package/assets/techne/rules/kotlin/testing.md +38 -0
  208. package/assets/techne/rules/nuxt/coding-style.md +20 -0
  209. package/assets/techne/rules/nuxt/hooks.md +16 -0
  210. package/assets/techne/rules/nuxt/patterns.md +46 -0
  211. package/assets/techne/rules/nuxt/security.md +19 -0
  212. package/assets/techne/rules/nuxt/testing.md +15 -0
  213. package/assets/techne/rules/perl/coding-style.md +33 -0
  214. package/assets/techne/rules/perl/hooks.md +22 -0
  215. package/assets/techne/rules/perl/patterns.md +43 -0
  216. package/assets/techne/rules/perl/security.md +39 -0
  217. package/assets/techne/rules/perl/testing.md +42 -0
  218. package/assets/techne/rules/php/coding-style.md +30 -0
  219. package/assets/techne/rules/php/hooks.md +24 -0
  220. package/assets/techne/rules/php/patterns.md +26 -0
  221. package/assets/techne/rules/php/security.md +27 -0
  222. package/assets/techne/rules/php/testing.md +28 -0
  223. package/assets/techne/rules/python/coding-style.md +38 -0
  224. package/assets/techne/rules/python/fastapi.md +57 -0
  225. package/assets/techne/rules/python/hooks.md +19 -0
  226. package/assets/techne/rules/python/patterns.md +37 -0
  227. package/assets/techne/rules/python/security.md +26 -0
  228. package/assets/techne/rules/python/testing.md +32 -0
  229. package/assets/techne/rules/react/coding-style.md +24 -0
  230. package/assets/techne/rules/react/hooks.md +62 -0
  231. package/assets/techne/rules/react/patterns.md +48 -0
  232. package/assets/techne/rules/react/security.md +45 -0
  233. package/assets/techne/rules/react/testing.md +42 -0
  234. package/assets/techne/rules/react-native/accessibility.md +14 -0
  235. package/assets/techne/rules/react-native/coding-style.md +16 -0
  236. package/assets/techne/rules/react-native/hooks.md +16 -0
  237. package/assets/techne/rules/react-native/patterns.md +18 -0
  238. package/assets/techne/rules/react-native/performance.md +14 -0
  239. package/assets/techne/rules/react-native/production-readiness.md +16 -0
  240. package/assets/techne/rules/react-native/security.md +16 -0
  241. package/assets/techne/rules/react-native/testing.md +14 -0
  242. package/assets/techne/rules/ruby/coding-style.md +40 -0
  243. package/assets/techne/rules/ruby/hooks.md +33 -0
  244. package/assets/techne/rules/ruby/patterns.md +33 -0
  245. package/assets/techne/rules/ruby/security.md +39 -0
  246. package/assets/techne/rules/ruby/testing.md +35 -0
  247. package/assets/techne/rules/rust/coding-style.md +41 -0
  248. package/assets/techne/rules/rust/hooks.md +16 -0
  249. package/assets/techne/rules/rust/patterns.md +42 -0
  250. package/assets/techne/rules/rust/security.md +33 -0
  251. package/assets/techne/rules/rust/testing.md +30 -0
  252. package/assets/techne/rules/swift/coding-style.md +30 -0
  253. package/assets/techne/rules/swift/hooks.md +20 -0
  254. package/assets/techne/rules/swift/patterns.md +30 -0
  255. package/assets/techne/rules/swift/security.md +22 -0
  256. package/assets/techne/rules/swift/testing.md +28 -0
  257. package/assets/techne/rules/typescript/coding-style.md +38 -0
  258. package/assets/techne/rules/typescript/hooks.md +14 -0
  259. package/assets/techne/rules/typescript/patterns.md +12 -0
  260. package/assets/techne/rules/typescript/security.md +14 -0
  261. package/assets/techne/rules/typescript/testing.md +12 -0
  262. package/assets/techne/rules/vue/coding-style.md +19 -0
  263. package/assets/techne/rules/vue/hooks.md +13 -0
  264. package/assets/techne/rules/vue/patterns.md +29 -0
  265. package/assets/techne/rules/vue/security.md +11 -0
  266. package/assets/techne/rules/vue/testing.md +11 -0
  267. package/assets/techne/rules/web/coding-style.md +25 -0
  268. package/assets/techne/rules/web/design-quality.md +21 -0
  269. package/assets/techne/rules/web/hooks.md +39 -0
  270. package/assets/techne/rules/web/patterns.md +23 -0
  271. package/assets/techne/rules/web/performance.md +25 -0
  272. package/assets/techne/rules/web/security.md +23 -0
  273. package/assets/techne/rules/web/testing.md +21 -0
  274. package/assets/techne/scripts/hooks/config-guard.js +50 -0
  275. package/assets/techne/scripts/hooks/edit-tracker.js +26 -0
  276. package/assets/techne/scripts/hooks/lib/shell.js +204 -0
  277. package/assets/techne/scripts/hooks/no-verify-guard.js +205 -0
  278. package/assets/techne/scripts/hooks/run.js +92 -0
  279. package/assets/techne/scripts/hooks/stop-checks.js +136 -0
  280. package/assets/techne/skills/angular/SKILL.md +34 -0
  281. package/assets/techne/skills/angular/reference/components-and-templates.md +63 -0
  282. package/assets/techne/skills/angular/reference/dependency-injection.md +44 -0
  283. package/assets/techne/skills/angular/reference/forms.md +97 -0
  284. package/assets/techne/skills/angular/reference/routing.md +50 -0
  285. package/assets/techne/skills/angular/reference/signals-and-state.md +59 -0
  286. package/assets/techne/skills/angular/reference/styling-motion-a11y.md +54 -0
  287. package/assets/techne/skills/angular/reference/testing.md +52 -0
  288. package/assets/techne/skills/angular/reference/tooling.md +31 -0
  289. package/assets/techne/skills/cpp/SKILL.md +27 -0
  290. package/assets/techne/skills/cpp/reference/coding-standards.md +160 -0
  291. package/assets/techne/skills/databases/SKILL.md +35 -0
  292. package/assets/techne/skills/databases/reference/clickhouse.md +102 -0
  293. package/assets/techne/skills/databases/reference/migrations.md +94 -0
  294. package/assets/techne/skills/databases/reference/mysql.md +101 -0
  295. package/assets/techne/skills/databases/reference/postgres.md +72 -0
  296. package/assets/techne/skills/databases/reference/prisma.md +105 -0
  297. package/assets/techne/skills/databases/reference/redis.md +108 -0
  298. package/assets/techne/skills/delivery/SKILL.md +35 -0
  299. package/assets/techne/skills/delivery/reference/deployment-patterns.md +58 -0
  300. package/assets/techne/skills/delivery/reference/docker-patterns.md +77 -0
  301. package/assets/techne/skills/delivery/reference/git-workflow.md +64 -0
  302. package/assets/techne/skills/delivery/reference/github-ops.md +67 -0
  303. package/assets/techne/skills/delivery/reference/jira-integration.md +53 -0
  304. package/assets/techne/skills/delivery/reference/kubernetes-patterns.md +67 -0
  305. package/assets/techne/skills/dotnet/SKILL.md +27 -0
  306. package/assets/techne/skills/dotnet/reference/dotnet-core.md +131 -0
  307. package/assets/techne/skills/flutter/SKILL.md +29 -0
  308. package/assets/techne/skills/flutter/reference/code-review.md +76 -0
  309. package/assets/techne/skills/flutter/reference/patterns.md +201 -0
  310. package/assets/techne/skills/go/SKILL.md +28 -0
  311. package/assets/techne/skills/go/reference/patterns.md +87 -0
  312. package/assets/techne/skills/go/reference/testing.md +142 -0
  313. package/assets/techne/skills/java/SKILL.md +29 -0
  314. package/assets/techne/skills/java/reference/java-standards.md +117 -0
  315. package/assets/techne/skills/java/reference/springboot.md +108 -0
  316. package/assets/techne/skills/kotlin/SKILL.md +35 -0
  317. package/assets/techne/skills/kotlin/reference/android-clean-architecture.md +115 -0
  318. package/assets/techne/skills/kotlin/reference/compose-multiplatform-patterns.md +113 -0
  319. package/assets/techne/skills/kotlin/reference/kotlin-coroutines-flows.md +93 -0
  320. package/assets/techne/skills/kotlin/reference/kotlin-patterns.md +74 -0
  321. package/assets/techne/skills/kotlin/reference/kotlin-testing.md +115 -0
  322. package/assets/techne/skills/ml/SKILL.md +34 -0
  323. package/assets/techne/skills/ml/reference/ai-regression-testing.md +99 -0
  324. package/assets/techne/skills/ml/reference/cost-aware-llm-pipeline.md +133 -0
  325. package/assets/techne/skills/ml/reference/eval-harness.md +94 -0
  326. package/assets/techne/skills/ml/reference/mle-workflow.md +177 -0
  327. package/assets/techne/skills/ml/reference/prompt-optimizer.md +107 -0
  328. package/assets/techne/skills/ml/reference/pytorch-patterns.md +164 -0
  329. package/assets/techne/skills/php/SKILL.md +27 -0
  330. package/assets/techne/skills/php/reference/laravel.md +142 -0
  331. package/assets/techne/skills/python/SKILL.md +33 -0
  332. package/assets/techne/skills/python/reference/django.md +104 -0
  333. package/assets/techne/skills/python/reference/fastapi.md +118 -0
  334. package/assets/techne/skills/python/reference/python-core.md +88 -0
  335. package/assets/techne/skills/python/reference/python-testing.md +105 -0
  336. package/assets/techne/skills/quality/SKILL.md +34 -0
  337. package/assets/techne/skills/quality/reference/benchmark.md +55 -0
  338. package/assets/techne/skills/quality/reference/browser-qa.md +60 -0
  339. package/assets/techne/skills/quality/reference/coding-standards.md +88 -0
  340. package/assets/techne/skills/quality/reference/e2e-testing.md +99 -0
  341. package/assets/techne/skills/quality/reference/tdd-workflow.md +89 -0
  342. package/assets/techne/skills/quality/reference/verification-loop.md +80 -0
  343. package/assets/techne/skills/react/SKILL.md +30 -0
  344. package/assets/techne/skills/react/reference/nextjs-turbopack.md +42 -0
  345. package/assets/techne/skills/react/reference/react-patterns.md +139 -0
  346. package/assets/techne/skills/react/reference/react-performance.md +101 -0
  347. package/assets/techne/skills/react/reference/react-testing.md +139 -0
  348. package/assets/techne/skills/react-native/SKILL.md +28 -0
  349. package/assets/techne/skills/react-native/reference/expo-patterns.md +196 -0
  350. package/assets/techne/skills/research/SKILL.md +35 -0
  351. package/assets/techne/skills/research/reference/architecture-decision-records.md +65 -0
  352. package/assets/techne/skills/research/reference/codebase-onboarding.md +44 -0
  353. package/assets/techne/skills/research/reference/deep-research.md +50 -0
  354. package/assets/techne/skills/research/reference/iterative-retrieval.md +62 -0
  355. package/assets/techne/skills/research/reference/product-lens.md +38 -0
  356. package/assets/techne/skills/research/reference/search-first.md +50 -0
  357. package/assets/techne/skills/rust/SKILL.md +28 -0
  358. package/assets/techne/skills/rust/reference/patterns.md +108 -0
  359. package/assets/techne/skills/rust/reference/testing.md +142 -0
  360. package/assets/techne/skills/services/SKILL.md +32 -0
  361. package/assets/techne/skills/services/reference/api-contracts.md +139 -0
  362. package/assets/techne/skills/services/reference/backend-layers.md +95 -0
  363. package/assets/techne/skills/services/reference/errors-and-resilience.md +146 -0
  364. package/assets/techne/skills/services/reference/hexagonal-boundaries.md +93 -0
  365. package/assets/techne/skills/services/reference/mcp-servers.md +50 -0
  366. package/assets/techne/skills/services/reference/nestjs.md +96 -0
  367. package/assets/techne/skills/swift/SKILL.md +29 -0
  368. package/assets/techne/skills/swift/reference/concurrency.md +83 -0
  369. package/assets/techne/skills/swift/reference/swiftui.md +139 -0
  370. package/assets/techne/skills/vue/SKILL.md +29 -0
  371. package/assets/techne/skills/vue/reference/nuxt4-patterns.md +90 -0
  372. package/assets/techne/skills/vue/reference/ui-to-vue.md +90 -0
  373. package/assets/techne/skills/vue/reference/vue-patterns.md +151 -0
  374. package/assets/techne/skills/web/SKILL.md +30 -0
  375. package/assets/techne/skills/web/reference/bun-runtime.md +55 -0
  376. package/assets/techne/skills/web/reference/frontend-patterns.md +54 -0
  377. package/assets/techne/skills/web/reference/seo.md +77 -0
  378. package/assets/techne/skills/web/reference/vite-patterns.md +96 -0
  379. package/bin/native.cjs +30 -0
  380. package/native/aarch64-apple-darwin/exonous +0 -0
  381. package/native/aarch64-unknown-linux-gnu/exonous +0 -0
  382. package/native/x86_64-apple-darwin/exonous +0 -0
  383. package/native/x86_64-pc-windows-msvc/exonous.exe +0 -0
  384. package/native/x86_64-unknown-linux-gnu/exonous +0 -0
  385. package/package.json +1 -6
@@ -0,0 +1,99 @@
1
+ 'use strict';
2
+
3
+ // Self-contained: copied to ~/.claude/exonous/hooks/lib/, node builtins only.
4
+ const crypto = require('crypto');
5
+ const fs = require('fs');
6
+ const { homedir, devNull } = require('os');
7
+ const path = require('path');
8
+ const { execFileSync } = require('child_process');
9
+
10
+ function home() {
11
+ return process.env.EXONOUS_HOME || homedir();
12
+ }
13
+
14
+ function claudeDir() {
15
+ return path.join(home(), '.claude');
16
+ }
17
+
18
+ function cursorDir() {
19
+ return path.join(home(), '.cursor');
20
+ }
21
+
22
+ function exoDir() {
23
+ return path.join(claudeDir(), 'exonous');
24
+ }
25
+
26
+ function stateDir() {
27
+ return path.join(exoDir(), 'state');
28
+ }
29
+
30
+ /** EXONOUS_HOME set and resolving somewhere other than the OS home. */
31
+ function redirectedHome(env = process.env) {
32
+ if (!env.EXONOUS_HOME) return false;
33
+ try {
34
+ return fs.realpathSync(env.EXONOUS_HOME) !== fs.realpathSync(homedir());
35
+ } catch {
36
+ return true;
37
+ }
38
+ }
39
+
40
+ /** Test-only overrides (release source, pm shims) need EXONOUS_TEST=1 and a redirected home. */
41
+ const testMode = (env = process.env) => env.EXONOUS_TEST === '1' && redirectedHome(env);
42
+
43
+ /** Effective uid (tests may stub it in test mode); null where uids do not exist (Windows). */
44
+ function currentUid(env = process.env) {
45
+ if (testMode(env) && /^\d+$/.test(env.EXONOUS_TEST_UID || '')) return Number(env.EXONOUS_TEST_UID);
46
+ return typeof process.getuid === 'function' ? process.getuid() : null;
47
+ }
48
+
49
+ /** Uid owning target when it differs from ours, else null: a root session must not leave root-owned files in a user's home. */
50
+ function foreignOwner(target, env = process.env) {
51
+ const uid = currentUid(env);
52
+ if (uid === null) return null;
53
+ try {
54
+ const owner = fs.statSync(target).uid;
55
+ return owner === uid ? null : owner;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ /** Env for git calls in a repo under review: repo config must not run commands (fsmonitor, hooks). */
62
+ function gitEnv(base = process.env) {
63
+ const env = { ...base, GIT_TERMINAL_PROMPT: '0' };
64
+ let n = Number.parseInt(env.GIT_CONFIG_COUNT || '0', 10);
65
+ if (!Number.isInteger(n) || n < 0) n = 0;
66
+ for (const [key, value] of [['core.fsmonitor', 'false'], ['core.hooksPath', devNull]]) {
67
+ env[`GIT_CONFIG_KEY_${n}`] = key;
68
+ env[`GIT_CONFIG_VALUE_${n}`] = value;
69
+ n += 1;
70
+ }
71
+ env.GIT_CONFIG_COUNT = String(n);
72
+ return env;
73
+ }
74
+
75
+ /** Read-only git query in cwd with the hardened env; null on failure. */
76
+ function gitOut(cwd, args) {
77
+ try {
78
+ return execFileSync('git', ['--no-optional-locks', ...args], { cwd, env: gitEnv(), encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 15000 });
79
+ } catch {
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /** Real path of the git toplevel containing cwd; throws outside a repo. */
85
+ function repoRoot(cwd) {
86
+ let out;
87
+ try {
88
+ out = execFileSync('git', ['rev-parse', '--show-toplevel'], { cwd, env: gitEnv(), encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
89
+ } catch {
90
+ throw new Error(`not inside a git repository: ${cwd}`);
91
+ }
92
+ return fs.realpathSync(out);
93
+ }
94
+
95
+ function repoKey(cwd) {
96
+ return crypto.createHash('sha1').update(repoRoot(cwd)).digest('hex');
97
+ }
98
+
99
+ module.exports = { home, claudeDir, cursorDir, exoDir, stateDir, redirectedHome, testMode, currentUid, foreignOwner, gitEnv, gitOut, repoRoot, repoKey };
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ // Per-tool hook stdin → { command, cwd }, and per-tool deny/allow output. Node builtins only.
4
+ const ADAPTERS = ['claude', 'cursor', 'gemini', 'codex', 'windsurf', 'copilot', 'opencode'];
5
+ const SAFE_WORD = /^[A-Za-z0-9_/.,:=@%+-]+$/;
6
+
7
+ const isObject = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);
8
+
9
+ function shellQuote(word) {
10
+ const text = String(word);
11
+ return SAFE_WORD.test(text) ? text : `'${text.replace(/'/g, `'"'"'`)}'`;
12
+ }
13
+
14
+ function asCommand(value) {
15
+ if (typeof value === 'string') return value;
16
+ if (Array.isArray(value) && value.length && value.every((w) => typeof w === 'string')) return value.map(shellQuote).join(' ');
17
+ return null;
18
+ }
19
+
20
+ function toolArgs(input) {
21
+ if (isObject(input.toolArgs)) return input.toolArgs;
22
+ if (typeof input.toolArgs !== 'string') return null;
23
+ try {
24
+ const parsed = JSON.parse(input.toolArgs);
25
+ return isObject(parsed) ? parsed : null;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ // Shapes are tried in order for every adapter, so a tool that changes its payload still gets gated.
32
+ function extractCommand(input) {
33
+ const sources = [input.tool_input, input.tool_info && { command: input.tool_info.command_line }, input, input.args, toolArgs(input)];
34
+ for (const source of sources) {
35
+ if (!isObject(source)) continue;
36
+ const command = asCommand(source.command);
37
+ if (command !== null) return command;
38
+ }
39
+ return null;
40
+ }
41
+
42
+ function extractCwd(input) {
43
+ const options = [input.tool_info && input.tool_info.cwd, input.cwd, Array.isArray(input.workspace_roots) ? input.workspace_roots[0] : null];
44
+ return options.find((c) => typeof c === 'string' && c) || process.cwd();
45
+ }
46
+
47
+ /** @returns {{ command: string, cwd: string } | null} null = not a shell call or unreadable: let it through. */
48
+ function parse(adapter, raw) {
49
+ let input;
50
+ try {
51
+ input = JSON.parse(raw);
52
+ } catch {
53
+ return null;
54
+ }
55
+ if (!isObject(input)) return null;
56
+ const command = extractCommand(input);
57
+ return command === null ? null : { command, cwd: extractCwd(input) };
58
+ }
59
+
60
+ /** @returns {{ code: number, stdout: string, stderr: string }} */
61
+ function deny(adapter, reason) {
62
+ if (adapter === 'cursor') {
63
+ return { code: 2, stdout: JSON.stringify({ permission: 'deny', user_message: reason, agent_message: reason }), stderr: `${reason}\n` };
64
+ }
65
+ if (adapter === 'copilot') {
66
+ const decision = { permissionDecision: 'deny', permissionDecisionReason: reason };
67
+ return { code: 0, stdout: JSON.stringify({ ...decision, hookSpecificOutput: { hookEventName: 'PreToolUse', ...decision } }), stderr: '' };
68
+ }
69
+ return { code: 2, stdout: '', stderr: `${reason}\n` };
70
+ }
71
+
72
+ /** Warnings reach Claude's model as additionalContext; other tools get them on stderr. */
73
+ function allow(adapter, warnings = []) {
74
+ const text = warnings.join('\n');
75
+ if (adapter === 'claude' && text) {
76
+ return { code: 0, stdout: JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', additionalContext: text } }), stderr: '' };
77
+ }
78
+ return { code: 0, stdout: adapter === 'cursor' ? JSON.stringify({ permission: 'allow' }) : '', stderr: text ? `${text}\n` : '' };
79
+ }
80
+
81
+ module.exports = { ADAPTERS, parse, deny, allow, shellQuote };
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Required titles come from the resolved PR template; the size cap is fixed.
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { execFileSync } = require('child_process');
8
+ const { resolveTemplate } = require('./template.cjs');
9
+ const { gitEnv } = require('./paths.cjs');
10
+
11
+ const MAX_CHANGED_FILES = 30;
12
+
13
+ function normalize(text) {
14
+ return text.normalize('NFC').toLowerCase();
15
+ }
16
+
17
+ function missingSections(text, sections) {
18
+ const haystack = normalize(text || '');
19
+ return sections.filter((section) => !haystack.includes(normalize(section)));
20
+ }
21
+
22
+ /** @param {{ path: string | null, sections: string[] }} [template] */
23
+ function validateBodyContent(content, template = resolveTemplate()) {
24
+ const missing = missingSections(content, template.sections);
25
+ if (missing.length === 0) return { ok: true };
26
+ return {
27
+ ok: false,
28
+ reason: `PR body is missing: ${missing.join(', ')}. Follow ${template.path} — all ${template.sections.length} titles must appear (write N/A under a section that does not apply).`,
29
+ };
30
+ }
31
+
32
+ function validateBodyFile(filePath, cwd = process.cwd(), template = resolveTemplate(cwd)) {
33
+ const resolved = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
34
+ if (!fs.existsSync(resolved)) {
35
+ return { ok: false, reason: `PR body file not found: ${filePath}.` };
36
+ }
37
+ return validateBodyContent(fs.readFileSync(resolved, 'utf8'), template);
38
+ }
39
+
40
+ function git(cwd, ...args) {
41
+ return execFileSync('git', args, { cwd, env: gitEnv(), encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
42
+ }
43
+
44
+ // Pure moves (R100) cost no review, so only content changes count toward the size cap.
45
+ function countContentChanges(nameStatus) {
46
+ return nameStatus.split('\n').filter((line) => line && !line.startsWith('R100')).length;
47
+ }
48
+
49
+ // Unknown base or missing refs → null; size gate must never block on git trouble.
50
+ function countChangedFiles({ base: givenBase, head: givenHead }, cwd) {
51
+ try {
52
+ const base = givenBase || git(cwd, 'symbolic-ref', '--short', 'refs/remotes/origin/HEAD').replace(/^origin\//, '');
53
+ const head = givenHead || 'HEAD';
54
+ const headRef = head === 'HEAD' ? head : `origin/${head.replace(/^[^:]+:/, '')}`;
55
+ const out = git(cwd, 'diff', '--name-status', '-M100%', `origin/${base}...${headRef}`);
56
+ return countContentChanges(out);
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+
62
+ /** Last value given for each gh pr create option that matters here. */
63
+ function prFields(parsed) {
64
+ const last = (name) => {
65
+ const opt = parsed.opts.filter((o) => o.name === name).pop();
66
+ return opt ? opt.value : null;
67
+ };
68
+ return { body: last('body'), bodyFile: last('body-file'), base: last('base'), head: last('head') };
69
+ }
70
+
71
+ /** @param {{ body: string | null, bodyFile: string | null, base: string | null, head: string | null }} fields */
72
+ function validatePrCreate(fields, cwd = process.cwd()) {
73
+ const changed = countChangedFiles(fields, cwd);
74
+ if (changed !== null && changed > MAX_CHANGED_FILES) {
75
+ return {
76
+ ok: false,
77
+ reason: `PR has ${changed} changed files (max ${MAX_CHANGED_FILES}). Split it into smaller PRs, each with one cohesive goal (stack them if they depend on each other).`,
78
+ };
79
+ }
80
+ const template = resolveTemplate(cwd);
81
+ if (fields.bodyFile === '-') return { ok: false, reason: 'A PR body read from stdin cannot be checked; write it to a file and pass --body-file.' };
82
+ if (fields.bodyFile) return validateBodyFile(fields.bodyFile, cwd, template);
83
+ return validateBodyContent(fields.body || '', template);
84
+ }
85
+
86
+ function validateGhPrCreateCommand(command, cwd = process.cwd()) {
87
+ const { analyze } = require('./shell.cjs');
88
+ const { parseOptions, GH_PR_CREATE } = require('./cli-options.cjs');
89
+ const call = command ? analyze(command, cwd).calls.find((c) => c.kind === 'pr') : null;
90
+ if (!call) return { ok: false, reason: 'Not a gh pr create command.' };
91
+ const parsed = parseOptions(call.args, GH_PR_CREATE);
92
+ if (parsed.error) return { ok: false, reason: `Cannot read the gh pr create options: ${parsed.error}.` };
93
+ return validatePrCreate(prFields(parsed), call.cwd);
94
+ }
95
+
96
+ module.exports = {
97
+ MAX_CHANGED_FILES,
98
+ validateBodyContent,
99
+ validateBodyFile,
100
+ validateGhPrCreateCommand,
101
+ validatePrCreate,
102
+ prFields,
103
+ };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ // Self-contained: copied to ~/.claude/exonous/hooks/lib/, node builtins only.
4
+ const { execFileSync } = require('child_process');
5
+ const { testMode } = require('./paths.cjs');
6
+
7
+ const RELEASE_REF = /^v(\d+)\.(\d+)\.(\d+)$/;
8
+
9
+ function compareVersions(a, b) {
10
+ for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] - b[i];
11
+ return 0;
12
+ }
13
+
14
+ const releaseSource = (url) => (testMode() && process.env.EXONOUS_RELEASE_SOURCE) || url;
15
+
16
+ /**
17
+ * Every vX.Y.Z tag on the source, oldest first; sha is the commit (annotated tags are peeled).
18
+ * @param {string} url
19
+ * @returns {{ version: string, parts: number[], sha: string }[]}
20
+ */
21
+ function listReleases(url) {
22
+ const source = releaseSource(url);
23
+ const out = execFileSync('git', ['ls-remote', '--tags', '--', source, 'v*'], {
24
+ encoding: 'utf8',
25
+ timeout: 30000,
26
+ stdio: ['ignore', 'pipe', 'ignore'],
27
+ env: { ...process.env, GIT_TERMINAL_PROMPT: '0' },
28
+ });
29
+ const tags = new Map();
30
+ for (const line of out.split('\n')) {
31
+ const [sha, ref = ''] = line.split('\t');
32
+ const name = ref.replace(/^refs\/tags\//, '');
33
+ const peeled = name.endsWith('^{}');
34
+ const tag = peeled ? name.slice(0, -3) : name;
35
+ if (!RELEASE_REF.test(tag) || !sha) continue;
36
+ // ^{} is the commit an annotated tag points to; a lightweight tag has none and is the commit itself.
37
+ if (peeled || !tags.has(tag)) tags.set(tag, sha);
38
+ }
39
+ return [...tags]
40
+ .map(([tag, sha]) => {
41
+ const m = RELEASE_REF.exec(tag);
42
+ return { sha, parts: [Number(m[1]), Number(m[2]), Number(m[3])] };
43
+ })
44
+ .sort((a, b) => compareVersions(a.parts, b.parts))
45
+ .map((r) => ({ version: r.parts.join('.'), parts: r.parts, sha: r.sha }));
46
+ }
47
+
48
+ /**
49
+ * @param {string} url
50
+ * @param {{ major?: number }} [options]
51
+ * @returns {{ version: string, sha: string } | null}
52
+ */
53
+ function latestRelease(url, { major } = {}) {
54
+ const top = listReleases(url).filter((r) => major === undefined || r.parts[0] === major).pop();
55
+ return top ? { version: top.version, sha: top.sha } : null;
56
+ }
57
+
58
+ module.exports = { RELEASE_REF, compareVersions, releaseSource, listReleases, latestRelease };
@@ -0,0 +1,138 @@
1
+ 'use strict';
2
+
3
+ // Self-contained: copied to ~/.claude/exonous/hooks/lib/, node builtins only. Repo mode = the machine runs from a copy in <repo>/node_modules.
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { execFileSync } = require('child_process');
7
+ const { gitOut } = require('./paths.cjs');
8
+
9
+ const DEPS = ['exonous', '@tron/claude-config'];
10
+ const LOCKFILES = { npm: ['package-lock.json'], pnpm: ['pnpm-lock.yaml'], bun: ['bun.lock', 'bun.lockb'] };
11
+ const RELEASE_REF = /^v\d+\.\d+\.\d+$/;
12
+
13
+ function readJson(file) {
14
+ try {
15
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
20
+
21
+ /** @returns {{ name: string, spec: string, field: string } | null} the exonous entry of the repo's package.json */
22
+ function readDep(root) {
23
+ const pkg = readJson(path.join(root, 'package.json')) || {};
24
+ for (const field of ['devDependencies', 'dependencies']) {
25
+ for (const name of DEPS) {
26
+ const spec = (pkg[field] || {})[name];
27
+ if (typeof spec === 'string' && spec) return { name, spec, field };
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+
33
+ /**
34
+ * The git repo whose node_modules holds this package copy and declares it, or null (global copies, dev checkouts).
35
+ * pnpm, yarn and bun keep global packages under a package.json of their own; only a git work tree is a repo.
36
+ */
37
+ function repoOf(packageRoot) {
38
+ if (!packageRoot) return null;
39
+ let real = packageRoot;
40
+ try {
41
+ real = fs.realpathSync(packageRoot);
42
+ } catch {
43
+ // keep the given path
44
+ }
45
+ const parts = real.split(path.sep);
46
+ const i = parts.indexOf('node_modules');
47
+ if (i <= 0) return null;
48
+ const root = parts.slice(0, i).join(path.sep) || path.sep;
49
+ if (!readDep(root)) return null;
50
+ return (gitOut(root, ['rev-parse', '--is-inside-work-tree']) || '').trim() === 'true' ? root : null;
51
+ }
52
+
53
+ /** A global install recorded in install.json whose package still exists: it takes precedence over any repo copy. */
54
+ function globalPrevails(info) {
55
+ const root = info && info.packageRoot;
56
+ return typeof root === 'string' && fs.existsSync(path.join(root, 'package.json')) && !repoOf(root);
57
+ }
58
+
59
+ /** Repo pm: `packageManager` field, then the lockfile; npm when nothing says otherwise. */
60
+ function repoPm(root) {
61
+ const field = /^(npm|pnpm|bun)@/.exec(String((readJson(path.join(root, 'package.json')) || {}).packageManager || ''));
62
+ const byLock = Object.entries(LOCKFILES).find(([, files]) => files.some((f) => fs.existsSync(path.join(root, f))));
63
+ const pm = (field && field[1]) || (byLock && byLock[0]) || 'npm';
64
+ return { pm, lockfiles: LOCKFILES[pm].filter((f) => fs.existsSync(path.join(root, f))) };
65
+ }
66
+
67
+ function splitSpec(spec) {
68
+ const at = spec.indexOf('#');
69
+ return at === -1 ? { base: spec, ref: '' } : { base: spec.slice(0, at), ref: spec.slice(at + 1) };
70
+ }
71
+
72
+ // Background updates run code from the source: only the canonical repo and its quanthub mirror qualify.
73
+ const SOURCES = ['zaqueu-1/exonous', 'quanthubbr/tron-claude-config'];
74
+
75
+ /** The release source of a GitHub spec base (`owner/repo`, `github:`, https, ssh, git+…), or null for anything else. */
76
+ function remoteUrl(base) {
77
+ const m = /^(?:github:|(?:git\+)?(?:https:\/\/|ssh:\/\/git@|git@)github\.com[/:])?([\w.-]+\/[\w.-]+?)(?:\.git)?\/?$/i.exec(base);
78
+ const slug = m && SOURCES.find((s) => s === m[1].toLowerCase());
79
+ return slug ? `https://github.com/${slug}.git` : null;
80
+ }
81
+
82
+ /**
83
+ * Where a repo-mode machine install follows releases: the source and ref of the repo's own dependency spec.
84
+ * @returns {{ root: string, dep: { name: string, spec: string, field: string }, base: string, ref: string, url: string | null } | null}
85
+ */
86
+ function repoSource(info) {
87
+ const root = repoOf(info && info.packageRoot);
88
+ const dep = root && readDep(root);
89
+ if (!dep) return null;
90
+ const { base, ref } = splitSpec(dep.spec);
91
+ return { root, dep, base, ref, url: remoteUrl(base) };
92
+ }
93
+
94
+ const pinned = (ref) => Boolean(ref) && !RELEASE_REF.test(ref);
95
+
96
+ // win32 needs a shell for .cmd shims: the pm name is fixed and every arg is a literal.
97
+ function pmRun(pm, args, cwd) {
98
+ const win = process.platform === 'win32';
99
+ return execFileSync(pm, args, {
100
+ cwd, encoding: 'utf8', timeout: 10 * 60 * 1000, stdio: ['ignore', 'pipe', 'pipe'], shell: win, windowsHide: true,
101
+ env: { ...process.env, npm_config_ignore_scripts: 'true' },
102
+ });
103
+ }
104
+
105
+ const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
106
+
107
+ /** Rewrites only the spec string of the dependency, keeping the file's formatting. */
108
+ function writeSpec(root, name, from, to) {
109
+ const file = path.join(root, 'package.json');
110
+ const text = fs.readFileSync(file, 'utf8');
111
+ const entry = new RegExp(`(${escape(JSON.stringify(name))}\\s*:\\s*)${escape(JSON.stringify(from))}`);
112
+ if (!entry.test(text)) throw new Error(`could not find ${name} in ${file}`);
113
+ fs.writeFileSync(file, text.replace(entry, (_, head) => `${head}${JSON.stringify(to)}`));
114
+ }
115
+
116
+ /**
117
+ * Moves the repo's dependency to the release tag and reinstalls with the repo's pm, scripts disabled.
118
+ * Refuses while package.json or the lockfile has uncommitted changes: the bump must be the only change there.
119
+ * @returns {string} the package root of the new copy
120
+ */
121
+ function bumpRepo(source, version) {
122
+ const { root, dep, base } = source;
123
+ const { pm, lockfiles } = repoPm(root);
124
+ const dirty = gitOut(root, ['status', '--porcelain', '--', 'package.json', ...lockfiles]);
125
+ if (dirty === null) throw new Error(`git could not read the status of ${root}`);
126
+ if (dirty.trim()) throw new Error(`${root}: package.json or its lockfile has uncommitted changes — commit them, then run \`exonous update\``);
127
+ const target = `${base}#v${version}`;
128
+ writeSpec(root, dep.name, dep.spec, target);
129
+ try {
130
+ pmRun(pm, pm === 'npm' ? ['install', '--ignore-scripts', '--no-audit', '--no-fund'] : ['install', '--ignore-scripts'], root);
131
+ } catch (err) {
132
+ writeSpec(root, dep.name, target, dep.spec);
133
+ throw err;
134
+ }
135
+ return path.join(root, 'node_modules', ...dep.name.split('/'));
136
+ }
137
+
138
+ module.exports = { DEPS, LOCKFILES, readDep, repoOf, globalPrevails, repoPm, splitSpec, remoteUrl, repoSource, pinned, bumpRepo };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ // Secret scrubber for captured session text: nothing reaches the vault without passing here. Node builtins only.
4
+ const PATTERNS = [
5
+ [/-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g, 'private_key'],
6
+ [/\bbearer\s+[A-Za-z0-9._\-+/=]{16,}/gi, 'bearer_token'],
7
+ [/\bsk-[A-Za-z0-9_-]{16,}/g, 'api_key'],
8
+ [/\b(?:sk|rk)_live_[A-Za-z0-9_-]{16,}/g, 'stripe_key'],
9
+ [/\bgh[pousr]_[A-Za-z0-9]{20,}/g, 'github_token'],
10
+ [/\bgithub_pat_[A-Za-z0-9_]{20,}/g, 'github_token'],
11
+ [/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g, 'aws_key'],
12
+ [/\bAIza[A-Za-z0-9_-]{30,}/g, 'google_api_key'],
13
+ [/\b1\/\/[0-9A-Za-z_-]{20,}/g, 'google_oauth'],
14
+ [/\bEAA[A-Za-z0-9]{20,}/g, 'meta_token'],
15
+ [/\b\d{6,10}:AA[A-Za-z0-9_-]{30,}\b/g, 'telegram_token'],
16
+ [/\bxox[abprs]-[A-Za-z0-9-]{10,}/g, 'slack_token'],
17
+ [/\bxapp-[A-Za-z0-9-]{10,}/g, 'slack_token'],
18
+ [/\beyJ[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g, 'jwt'],
19
+ [/\b[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^:/\s]+:[^@\s]+@[^\s]+/g, 'url_credentials'],
20
+ [/\b[A-Za-z0-9_-]*(?:authorization|password|passwd|secret|token|api[_-]?key)["']?\s*[:=]\s*["']?[^\s"',;]{6,}/gi, 'credential'],
21
+ ];
22
+ // Terminal escapes, NUL, bidi overrides and zero-width chars: not secrets, but they hide text or break markdown.
23
+ const STRIP = /\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)|[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f\u200b-\u200f\u202a-\u202e\u2060-\u2064\u2066-\u2069\ufeff]/g;
24
+
25
+ function sanitize(text) {
26
+ let out = String(text ?? '').replace(STRIP, '');
27
+ for (const [pattern, label] of PATTERNS) out = out.replace(pattern, `(redacted:${label})`);
28
+ return out;
29
+ }
30
+
31
+ /** Sanitized, single-line, at most max chars. */
32
+ function line(text, max = 160) {
33
+ const flat = sanitize(text).replace(/\s+/g, ' ').trim();
34
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
35
+ }
36
+
37
+ module.exports = { sanitize, line };