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,722 @@
1
+ 'use strict';
2
+
3
+ // Static shell-line analysis for the gates: finds git commit/push and gh pr create, and flags what it cannot resolve.
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { home, gitOut } = require('./paths.cjs');
7
+
8
+ const MAX_DEPTH = 4;
9
+ const RESERVED = new Set(['if', 'then', 'else', 'elif', 'fi', 'for', 'while', 'until', 'do', 'done', 'case', 'esac', 'select', 'function', '{', '}', '!', '[[', ']]', 'coproc']);
10
+ const WRAPPERS = {
11
+ time: [], nohup: [], command: [], builtin: [], exec: ['-a'], nice: ['-n'], ionice: ['-c', '-n', '-t'], stdbuf: ['-i', '-o', '-e'],
12
+ caffeinate: ['-t', '-w'], timeout: ['-s', '-k', '--signal', '--kill-after'], chrt: [], taskset: [], unbuffer: [],
13
+ sudo: ['-u', '-g', '-C', '-D', '-h', '-p', '-r', '-t', '-U'], doas: ['-u', '-C'],
14
+ };
15
+ const SHELLS = new Set(['bash', 'sh', 'zsh', 'dash', 'ksh', 'mksh', 'ash', 'fish']);
16
+ const ENV_MUTATORS = new Set(['export', 'declare', 'typeset', 'readonly', 'local', 'set', 'unset', 'alias', 'unalias', 'source', '.', 'shopt', 'enable', 'hash']);
17
+ const INDIRECT = new Set(['xargs', 'parallel', 'watch', 'ssh', 'su', 'runuser', 'flock', 'sg', 'chroot', 'nsenter', 'unshare', 'setsid', 'strace', 'ltrace', 'gdb', 'dbus-run-session', 'busybox', 'sem']);
18
+ const PRINTERS = new Set(['cat', 'printf', 'echo']);
19
+ const INTERPRETERS = /^(python[\d.]*|node|nodejs|deno|bun|perl|ruby|php|lua|osascript|pwsh|powershell|tclsh|awk|gawk|jq)$/;
20
+ const GIT_BUILTINS = new Set(`add am annotate apply archive bisect blame branch bugreport bundle cat-file check-attr check-ignore check-mailmap
21
+ check-ref-format checkout checkout-index cherry cherry-pick citool clean clone column commit commit-graph commit-tree config count-objects
22
+ credential describe diagnose diff diff-files diff-index diff-tree difftool fast-export fast-import fetch fetch-pack filter-branch fmt-merge-msg
23
+ for-each-ref for-each-repo format-patch fsck gc get-tar-commit-id grep gui hash-object help hook index-pack init instaweb interpret-trailers
24
+ log ls-files ls-remote ls-tree mailinfo mailsplit maintenance merge merge-base merge-file merge-index merge-tree mergetool mktag mktree
25
+ multi-pack-index mv name-rev notes pack-objects pack-redundant pack-refs patch-id prune prune-packed pull push range-diff read-tree rebase
26
+ reflog remote repack replace replay request-pull rerere reset restore rev-list rev-parse revert rm send-email send-pack shortlog show
27
+ show-branch show-index show-ref sparse-checkout stash status stripspace submodule switch symbolic-ref tag unpack-file unpack-objects
28
+ update-index update-ref update-server-info var verify-commit verify-pack verify-tag version whatchanged worktree write-tree lfs`.split(/\s+/));
29
+ const GH_COMMANDS = new Set(`agent-task alias api attestation auth browse cache codespace completion config copilot extension gist gpg-key help
30
+ issue label org pr preview project release repo ruleset run search secret ssh-key status variable version workflow`.split(/\s+/));
31
+
32
+ // ── Lexer ────────────────────────────────────────────────────────────────────
33
+
34
+ function newWord() {
35
+ return { t: 'w', v: '', unq: '', dyn: false, dynUnq: false, dynAt: -1, dynEnd: -1, glob: false, quoted: false, subs: [] };
36
+ }
37
+
38
+ function markDyn(w, quoted) {
39
+ if (!w.dyn) w.dynAt = w.v.length;
40
+ w.dyn = true;
41
+ if (!quoted) w.dynUnq = true;
42
+ }
43
+
44
+ function heredocDelim(text, i) {
45
+ let j = i;
46
+ if (text[j] === '-') j += 1;
47
+ while (text[j] === ' ' || text[j] === '\t') j += 1;
48
+ const m = /^(?:'([^']*)'|"([^"]*)"|\\?([^\s;&|<>()'"]+))/.exec(text.slice(j));
49
+ if (!m) return null;
50
+ return { delim: m[1] ?? m[2] ?? m[3], quoted: m[1] !== undefined || m[2] !== undefined || m[0].startsWith('\\'), end: j + m[0].length };
51
+ }
52
+
53
+ function skipHeredocBody(text, i, delim) {
54
+ let j = i;
55
+ const lines = [];
56
+ while (j < text.length) {
57
+ const eol = text.indexOf('\n', j);
58
+ const line = text.slice(j, eol === -1 ? text.length : eol);
59
+ j = eol === -1 ? text.length : eol + 1;
60
+ if (line.replace(/^\t+/, '') === delim) return { body: lines.join('\n'), end: j, closed: true };
61
+ lines.push(line);
62
+ }
63
+ return { body: lines.join('\n'), end: j, closed: false };
64
+ }
65
+
66
+ /** Index just past the matching close; heredoc bodies and quotes do not count. */
67
+ function readNested(text, start, open, close) {
68
+ let depth = 1;
69
+ let j = start;
70
+ let quote = null;
71
+ const pending = [];
72
+ while (j < text.length) {
73
+ const ch = text[j];
74
+ if (quote) {
75
+ if (ch === '\\' && quote === '"') j += 1;
76
+ else if (ch === quote) quote = null;
77
+ j += 1;
78
+ continue;
79
+ }
80
+ if (ch === '\\') {
81
+ j += 2;
82
+ continue;
83
+ }
84
+ if (ch === '\n' && pending.length) {
85
+ j += 1;
86
+ for (const delim of pending.splice(0)) j = skipHeredocBody(text, j, delim).end;
87
+ continue;
88
+ }
89
+ if (open !== close && ch === '<' && text[j + 1] === '<' && text[j + 2] !== '<') {
90
+ const h = heredocDelim(text, j + 2);
91
+ if (h) {
92
+ pending.push(h.delim);
93
+ j = h.end;
94
+ continue;
95
+ }
96
+ }
97
+ if (open !== close && (ch === '"' || ch === "'")) quote = ch;
98
+ else if (ch === open && open !== close) depth += 1;
99
+ else if (ch === close) {
100
+ depth -= 1;
101
+ if (!depth) return { inner: text.slice(start, j), end: j + 1, closed: true };
102
+ }
103
+ j += 1;
104
+ }
105
+ return { inner: text.slice(start), end: text.length, closed: false };
106
+ }
107
+
108
+ /** @returns {{ tokens: object[], notes: string[] }} */
109
+ function lex(text) {
110
+ const tokens = [];
111
+ const notes = [];
112
+ const pending = [];
113
+ let w = null;
114
+ const word = () => {
115
+ if (!w) w = newWord();
116
+ return w;
117
+ };
118
+ const end = () => {
119
+ if (!w) return;
120
+ if (/\{[^{}]*(,|\.\.)[^{}]*\}/.test(w.unq)) w.glob = true;
121
+ tokens.push(w);
122
+ w = null;
123
+ };
124
+ const op = (v) => {
125
+ end();
126
+ tokens.push({ t: 'op', v });
127
+ };
128
+ const nested = (start, open, close, quoted) => {
129
+ const r = readNested(text, start, open, close);
130
+ if (!r.closed) notes.push('an unterminated command substitution');
131
+ const wd = word();
132
+ markDyn(wd, quoted);
133
+ wd.subs.push(r.inner);
134
+ return r.end;
135
+ };
136
+
137
+ function readDollar(i, quoted) {
138
+ const nx = text[i + 1];
139
+ const wd = word();
140
+ if (nx === '(') {
141
+ const end2 = nested(i + 2, '(', ')', quoted);
142
+ wd.v += text.slice(i, end2);
143
+ wd.dynEnd = wd.v.length;
144
+ return end2;
145
+ }
146
+ if (nx === '{') {
147
+ const r = readNested(text, i + 2, '{', '}');
148
+ markDyn(wd, quoted);
149
+ if (/\$\(|`|<\(/.test(r.inner)) wd.subs.push(r.inner.replace(/^[^$`<]*/, ''));
150
+ wd.v += text.slice(i, r.end);
151
+ wd.dynEnd = wd.v.length;
152
+ return r.end;
153
+ }
154
+ if (nx === "'" && !quoted) {
155
+ let j = i + 2;
156
+ while (j < text.length && text[j] !== "'") j += text[j] === '\\' ? 2 : 1;
157
+ markDyn(wd, false);
158
+ wd.v += text.slice(i, j + 1);
159
+ wd.dynEnd = wd.v.length;
160
+ return j + 1;
161
+ }
162
+ if (nx === '"' && !quoted) {
163
+ markDyn(wd, true);
164
+ const after = readDouble(i + 2);
165
+ wd.dynEnd = wd.v.length;
166
+ return after;
167
+ }
168
+ const m = /^(?:[A-Za-z_][A-Za-z0-9_]*|[0-9?#@*!$-])/.exec(text.slice(i + 1));
169
+ if (m) {
170
+ markDyn(wd, quoted);
171
+ wd.v += `$${m[0]}`;
172
+ wd.dynEnd = wd.v.length;
173
+ return i + 1 + m[0].length;
174
+ }
175
+ wd.v += '$';
176
+ if (!quoted) wd.unq += '$';
177
+ return i + 1;
178
+ }
179
+
180
+ function readDouble(start) {
181
+ const wd = word();
182
+ wd.quoted = true;
183
+ let i = start;
184
+ while (i < text.length && text[i] !== '"') {
185
+ const ch = text[i];
186
+ if (ch === '\\' && i + 1 < text.length) {
187
+ const nx = text[i + 1];
188
+ if (nx === '\n') {
189
+ i += 2;
190
+ } else {
191
+ wd.v += '$`"\\'.includes(nx) ? nx : `\\${nx}`;
192
+ i += 2;
193
+ }
194
+ } else if (ch === '$') {
195
+ i = readDollar(i, true);
196
+ } else if (ch === '`') {
197
+ const end2 = nested(i + 1, '`', '`', true);
198
+ wd.v += text.slice(i, end2);
199
+ wd.dynEnd = wd.v.length;
200
+ i = end2;
201
+ } else {
202
+ wd.v += ch;
203
+ i += 1;
204
+ }
205
+ }
206
+ if (i >= text.length) notes.push('an unterminated quote');
207
+ return i + 1;
208
+ }
209
+
210
+ let i = 0;
211
+ while (i < text.length) {
212
+ const ch = text[i];
213
+ const nx = text[i + 1];
214
+ if (ch === '\n') {
215
+ op('\n');
216
+ i += 1;
217
+ for (const tok of pending.splice(0)) {
218
+ const body = skipHeredocBody(text, i, tok.heredoc.delim);
219
+ if (!body.closed) notes.push('an unterminated heredoc');
220
+ tok.heredoc.body = body.body;
221
+ if (!tok.heredoc.quoted && /\$\(|`/.test(body.body)) tok.heredoc.expands = true;
222
+ i = body.end;
223
+ }
224
+ continue;
225
+ }
226
+ if (ch === ' ' || ch === '\t' || ch === '\r') {
227
+ end();
228
+ i += 1;
229
+ } else if (ch === '#' && !w) {
230
+ while (i < text.length && text[i] !== '\n') i += 1;
231
+ } else if (ch === '&' && nx === '&') {
232
+ op('&&');
233
+ i += 2;
234
+ } else if (ch === '|' && nx === '|') {
235
+ op('||');
236
+ i += 2;
237
+ } else if (ch === ';' && (nx === ';' || nx === '&')) {
238
+ op(`;${nx}`);
239
+ i += 2;
240
+ } else if (ch === '|' && nx === '&') {
241
+ op('|&');
242
+ i += 2;
243
+ } else if ((ch === '<' || ch === '>') && nx === '(') {
244
+ const end2 = nested(i + 2, '(', ')', false);
245
+ word().v += text.slice(i, end2);
246
+ w.dynEnd = w.v.length;
247
+ i = end2;
248
+ } else if (ch === '&' && nx === '>') {
249
+ end();
250
+ i += text[i + 2] === '>' ? 3 : 2;
251
+ tokens.push({ t: 'r', v: '&>' });
252
+ } else if (ch === '<' || ch === '>') {
253
+ if (w && /^\d+$/.test(w.v) && !w.quoted && !w.dyn) w = null;
254
+ else end();
255
+ if (ch === '<' && nx === '<' && text[i + 2] === '<') {
256
+ tokens.push({ t: 'r', v: '<<<' });
257
+ i += 3;
258
+ continue;
259
+ }
260
+ if (ch === '<' && nx === '<') {
261
+ const h = heredocDelim(text, i + 2);
262
+ const tok = { t: 'r', v: '<<', heredoc: { delim: h ? h.delim : '', quoted: h ? h.quoted : false, body: '', expands: false } };
263
+ if (!h) notes.push('an unreadable heredoc');
264
+ tokens.push(tok);
265
+ pending.push(tok);
266
+ i = h ? h.end : i + 2;
267
+ continue;
268
+ }
269
+ let v = ch;
270
+ i += 1;
271
+ if (text[i] === '>' || text[i] === '&' || text[i] === '|' || (ch === '<' && text[i] === '>')) {
272
+ v += text[i];
273
+ i += 1;
274
+ }
275
+ const dup = v.endsWith('&') ? /^(\d+|-)/.exec(text.slice(i)) : null;
276
+ if (dup) i += dup[0].length;
277
+ tokens.push({ t: 'r', v, dup: Boolean(dup) });
278
+ } else if (';&|()'.includes(ch)) {
279
+ op(ch);
280
+ i += 1;
281
+ } else if (ch === "'") {
282
+ const wd = word();
283
+ wd.quoted = true;
284
+ const e = text.indexOf("'", i + 1);
285
+ if (e === -1) notes.push('an unterminated quote');
286
+ wd.v += text.slice(i + 1, e === -1 ? text.length : e);
287
+ i = e === -1 ? text.length : e + 1;
288
+ } else if (ch === '"') {
289
+ i = readDouble(i + 1);
290
+ } else if (ch === '\\') {
291
+ if (nx !== '\n') {
292
+ const wd = word();
293
+ wd.quoted = true;
294
+ wd.v += nx ?? '';
295
+ }
296
+ i += 2;
297
+ } else if (ch === '`') {
298
+ const end2 = nested(i + 1, '`', '`', false);
299
+ word().v += text.slice(i, end2);
300
+ w.dynEnd = w.v.length;
301
+ i = end2;
302
+ } else if (ch === '$') {
303
+ i = readDollar(i, false);
304
+ } else {
305
+ const wd = word();
306
+ if ('*?['.includes(ch)) wd.glob = true;
307
+ wd.v += ch;
308
+ wd.unq += ch;
309
+ i += 1;
310
+ }
311
+ }
312
+ end();
313
+ if (pending.length) notes.push('an unterminated heredoc');
314
+ return { tokens, notes };
315
+ }
316
+
317
+ // ── Simple commands ──────────────────────────────────────────────────────────
318
+
319
+ const ASSIGNMENT = /^([A-Za-z_][A-Za-z0-9_]*)(\[[^\]]*\])?\+?=/;
320
+
321
+ function commands(tokens) {
322
+ const cmds = [];
323
+ const ops = [];
324
+ let cur = null;
325
+ const blank = () => ({ words: [], assigns: [], redirs: [] });
326
+ cur = blank();
327
+ const flush = () => {
328
+ if (cur.words.length || cur.assigns.length || cur.redirs.length) cmds.push(cur);
329
+ cur = blank();
330
+ };
331
+ for (let k = 0; k < tokens.length; k += 1) {
332
+ const tk = tokens[k];
333
+ if (tk.t === 'op') {
334
+ flush();
335
+ ops.push({ v: tk.v, after: cmds.length - 1 });
336
+ } else if (tk.t === 'r') {
337
+ const r = { op: tk.v, heredoc: tk.heredoc || null, target: null };
338
+ if (!tk.dup && !tk.heredoc && tokens[k + 1] && tokens[k + 1].t === 'w') {
339
+ k += 1;
340
+ r.target = tokens[k];
341
+ }
342
+ cur.redirs.push(r);
343
+ } else if (!cur.words.length && ASSIGNMENT.test(tk.unq) && tk.v.startsWith(ASSIGNMENT.exec(tk.unq)[1])) {
344
+ cur.assigns.push(tk);
345
+ } else {
346
+ cur.words.push(tk);
347
+ }
348
+ }
349
+ flush();
350
+ return { cmds, ops };
351
+ }
352
+
353
+ const subsOf = (cmd) => [...cmd.words, ...cmd.assigns, ...cmd.redirs.map((r) => r.target).filter(Boolean)].flatMap((w) => w.subs);
354
+ const commandName = (word) => path.basename(word).replace(/\.exe$/i, '');
355
+
356
+ // `$HOME/bin/tool` keeps a static basename; anything computed after the last slash is unknowable.
357
+ function staticName(word) {
358
+ if (word.glob) return null;
359
+ if (!word.dyn) return commandName(word.v);
360
+ const slash = word.v.lastIndexOf('/');
361
+ return slash !== -1 && word.dynEnd <= slash ? commandName(word.v.slice(slash + 1)) : null;
362
+ }
363
+ const assignName = (w) => ASSIGNMENT.exec(w.unq)[1];
364
+
365
+ function expandHome(dir) {
366
+ const base = process.env.HOME || home();
367
+ if (dir === '~') return base;
368
+ return dir.startsWith('~/') ? path.join(base, dir.slice(2)) : dir;
369
+ }
370
+
371
+ function resolveCommand(cmd, scan) {
372
+ let words = cmd.words;
373
+ let wrapped = false;
374
+ let compound = false;
375
+ const assigns = [...cmd.assigns];
376
+ for (let guard = 0; guard < 16 && words.length; guard += 1) {
377
+ const w0 = words[0];
378
+ if (w0.dyn || w0.glob) break;
379
+ if (!w0.quoted && RESERVED.has(w0.v)) {
380
+ words = words.slice(1);
381
+ compound = true;
382
+ continue;
383
+ }
384
+ const name = commandName(w0.v);
385
+ if (name === 'env') {
386
+ wrapped = true;
387
+ let k = 1;
388
+ while (k < words.length) {
389
+ const a = words[k];
390
+ if (ASSIGNMENT.test(a.unq)) {
391
+ assigns.push(a);
392
+ k += 1;
393
+ } else if (/^(-[a-zA-Z]*S|--split-string)/.test(a.v)) {
394
+ scan.notes.push('`env -S` builds a command from a string');
395
+ return { words: [], wrapped, compound, assigns };
396
+ } else if (['-u', '--unset', '-C', '--chdir'].includes(a.v)) {
397
+ if (a.v === '-C' || a.v === '--chdir') scan.notes.push('`env -C` changes directory');
398
+ k += 2;
399
+ } else if (a.v.startsWith('-')) {
400
+ k += 1;
401
+ } else {
402
+ break;
403
+ }
404
+ }
405
+ words = words.slice(k);
406
+ continue;
407
+ }
408
+ if (WRAPPERS[name]) {
409
+ wrapped = true;
410
+ let k = 1;
411
+ while (k < words.length && words[k].v.startsWith('-') && words[k].v !== '-') k += WRAPPERS[name].includes(words[k].v) ? 2 : 1;
412
+ if (name === 'timeout' && words[k] && /^\d/.test(words[k].v)) k += 1;
413
+ words = words.slice(k);
414
+ continue;
415
+ }
416
+ break;
417
+ }
418
+ return { words, wrapped, compound, assigns };
419
+ }
420
+
421
+ // ── Walk ─────────────────────────────────────────────────────────────────────
422
+
423
+ function gitCall(cmd, res, args, dir, dirKnown, depth, scan) {
424
+ let i = 0;
425
+ let cwd = dir;
426
+ let known = dirKnown;
427
+ const redirect = [];
428
+ const configs = [];
429
+ while (i < args.length) {
430
+ const a = args[i];
431
+ if (a.dyn || a.glob) {
432
+ scan.forced.push('a git subcommand or option is computed at run time');
433
+ return;
434
+ }
435
+ if (!a.v.startsWith('-')) break;
436
+ const eq = a.v.indexOf('=');
437
+ const flag = eq === -1 ? a.v : a.v.slice(0, eq);
438
+ const value = eq === -1 ? args[i + 1] && args[i + 1].v : a.v.slice(eq + 1);
439
+ const step = eq === -1 ? 2 : 1;
440
+ if (a.v === '-C') {
441
+ const t = args[i + 1];
442
+ if (!t || t.dyn || t.glob) known = false;
443
+ else cwd = path.resolve(cwd, expandHome(t.v));
444
+ i += 2;
445
+ } else if (a.v === '-c' || flag === '--config-env') {
446
+ const t = args[i + (eq === -1 ? 1 : 0)];
447
+ if (!t || t.dyn || /^\s*(alias\.|include)/i.test(value || '')) scan.forced.push('inline git config that defines an alias or include');
448
+ configs.push(String(value || '').split('=')[0].trim());
449
+ i += step;
450
+ } else if (flag === '--git-dir' || flag === '--work-tree') {
451
+ redirect.push(flag);
452
+ i += step;
453
+ } else if (['--namespace', '--super-prefix', '--attr-source'].includes(flag)) {
454
+ i += step;
455
+ } else if (flag === '--exec-path' && eq !== -1) {
456
+ scan.forced.push('a custom git exec path');
457
+ i += 1;
458
+ } else {
459
+ i += 1;
460
+ }
461
+ }
462
+ const sub = args[i];
463
+ if (!sub) return;
464
+ const rest = args.slice(i + 1);
465
+ const call = { args: rest, cwd, dirKnown: known, cmd, depth, wrapped: res.wrapped, assigns: res.assigns, redirect, configs };
466
+ if (sub.v === 'commit' || sub.v === 'push') scan.calls.push({ kind: sub.v, ...call });
467
+ else if (sub.v === 'config' && rest.some((a) => a.dyn || /^(alias\.|include)/i.test(a.v))) scan.forced.push('a git alias or include change');
468
+ else if (!GIT_BUILTINS.has(sub.v)) scan.nonBuiltin.push({ tool: 'git', sub: sub.v, dir: cwd, known, changesEnv: res.assigns.length > 0 || res.wrapped });
469
+ }
470
+
471
+ function ghCall(cmd, res, args, dir, dirKnown, depth, scan) {
472
+ let i = 0;
473
+ while (i < args.length && args[i].v.startsWith('-') && !args[i].dyn) i += ['-R', '--repo', '--hostname'].includes(args[i].v) ? 2 : 1;
474
+ const a = args[i];
475
+ const b = args[i + 1];
476
+ if (!a) return;
477
+ if (a.dyn || a.glob || (a.v === 'pr' && b && (b.dyn || b.glob))) {
478
+ scan.forced.push('a gh subcommand is computed at run time');
479
+ return;
480
+ }
481
+ if (a.v === 'pr' && b && ['create', 'new'].includes(b.v)) {
482
+ scan.calls.push({ kind: 'pr', args: args.slice(i + 2), cwd: dir, dirKnown, cmd, depth, wrapped: res.wrapped, assigns: res.assigns, redirect: [], configs: [] });
483
+ } else if (a.v === 'alias' && b && ['set', 'import'].includes(b.v)) {
484
+ scan.forced.push('a gh alias change');
485
+ } else if (a.v === 'api' && args.some((x) => /pulls|graphql/.test(x.v))) {
486
+ scan.forced.push('`gh api` can open pull requests without the gate');
487
+ } else if (!GH_COMMANDS.has(a.v)) {
488
+ scan.nonBuiltin.push({ tool: 'gh', sub: a.v, dir, known: dirKnown, changesEnv: res.assigns.length > 0 || res.wrapped });
489
+ }
490
+ }
491
+
492
+ function shellCall(cmd, args, dir, depth, scan) {
493
+ let i = 0;
494
+ let hasC = false;
495
+ while (i < args.length) {
496
+ const a = args[i];
497
+ if (a.v === '--') {
498
+ i += 1;
499
+ break;
500
+ }
501
+ if (!/^[-+]/.test(a.v) || a.dyn) break;
502
+ if (/^-[a-zA-Z]*c[a-zA-Z]*$/.test(a.v) || a.v === '--command') hasC = true;
503
+ i += ['-o', '+o', '-O', '+O', '--rcfile', '--init-file'].includes(a.v) ? 2 : 1;
504
+ }
505
+ if (!hasC) {
506
+ scan.notes.push('a shell runs commands the gate cannot read');
507
+ return;
508
+ }
509
+ const script = args[i];
510
+ if (!script) return;
511
+ if (script.dyn || script.glob) {
512
+ scan.dynamicScripts.push('a shell script is computed at run time');
513
+ return;
514
+ }
515
+ cmd.shellScript = walk(script.v, dir, depth + 1, scan);
516
+ cmd.shellPlain = i === args.length - 1;
517
+ }
518
+
519
+ function walk(text, cwd, depth, scan) {
520
+ if (depth > MAX_DEPTH) {
521
+ scan.notes.push('commands are nested too deeply to check');
522
+ return null;
523
+ }
524
+ const { tokens, notes } = lex(text);
525
+ scan.notes.push(...notes);
526
+ for (const tk of tokens) {
527
+ if (tk.t === 'w') scan.words.push(tk.v);
528
+ if (tk.heredoc) scan.words.push(tk.heredoc.body);
529
+ }
530
+ const { cmds, ops } = commands(tokens);
531
+ const structure = { cmds, ops, compound: false, depth };
532
+ let dir = cwd;
533
+ let dirKnown = true;
534
+ for (const cmd of cmds) {
535
+ for (const sub of subsOf(cmd)) walk(sub, dir, depth + 1, scan);
536
+ if (cmd.redirs.some((r) => r.heredoc && r.heredoc.expands)) scan.notes.push('a heredoc runs a command substitution');
537
+ const res = resolveCommand(cmd, scan);
538
+ if (res.compound) structure.compound = true;
539
+ cmd.wrapped = res.wrapped;
540
+ if (!res.words.length) {
541
+ if (cmd.assigns.length) scan.mutatesEnv = true;
542
+ continue;
543
+ }
544
+ const head = res.words[0];
545
+ const name = staticName(head);
546
+ const args = res.words.slice(1);
547
+ if (name === null) {
548
+ scan.dynamicHeads.push({ argsStatic: args.every((a) => !a.dyn && !a.glob), first: args[0] ? args[0].v : null, dir, known: dirKnown });
549
+ continue;
550
+ }
551
+ if (name === 'cd' || name === 'pushd' || name === 'popd') {
552
+ const target = args.find((a) => !a.v.startsWith('-') || a.v === '-');
553
+ const opts = args.some((a) => a.v.startsWith('-') && a.v !== '-');
554
+ if (name === 'popd' || opts || args.length > 1 || (target && (target.dyn || target.glob || target.v === '-'))) {
555
+ dirKnown = false;
556
+ } else {
557
+ dir = path.resolve(dir, expandHome(target ? target.v : '~'));
558
+ cmd.cdTarget = dir;
559
+ }
560
+ } else if (ENV_MUTATORS.has(name)) {
561
+ scan.mutatesEnv = true;
562
+ if (name === 'source' || name === '.') scan.notes.push('a sourced script cannot be read');
563
+ } else if (SHELLS.has(name)) {
564
+ shellCall(cmd, args, dir, depth, scan);
565
+ } else if (name === 'eval') {
566
+ if (args.some((a) => a.dyn || a.glob)) scan.dynamicScripts.push('eval of a string computed at run time');
567
+ else walk(args.map((a) => a.v).join(' '), dir, depth + 1, scan);
568
+ } else if (INDIRECT.has(name)
569
+ || (['find', 'fd', 'fdfind'].includes(name) && args.some((a) => /^-(exec|execdir|ok|okdir|x|X|-exec|-exec-batch)$/.test(a.v)))
570
+ || (name === 'script' && args.some((a) => /^(-[a-zA-Z]*c|--command)/.test(a.v)))) {
571
+ scan.notes.push(`\`${name}\` runs a command the gate cannot see`);
572
+ } else if (INTERPRETERS.test(name)) {
573
+ scan.notes.push(`\`${name}\` runs code the gate cannot read`);
574
+ } else if (name === 'git') {
575
+ gitCall(cmd, res, args, dir, dirKnown, depth, scan);
576
+ } else if (name === 'gh') {
577
+ ghCall(cmd, res, args, dir, dirKnown, depth, scan);
578
+ }
579
+ }
580
+ return structure;
581
+ }
582
+
583
+ // ── Aliases ──────────────────────────────────────────────────────────────────
584
+
585
+ function gitAliases(dir) {
586
+ const out = gitOut(dir, ['config', '--get-regexp', '^alias\\.']) || '';
587
+ const map = new Map();
588
+ for (const line of out.split('\n')) {
589
+ const m = /^alias\.(\S+)\s+([\s\S]*)$/.exec(line);
590
+ if (m) map.set(m[1], m[2]);
591
+ }
592
+ return map;
593
+ }
594
+
595
+ // Same lookup order as gh's own ConfigDir: Windows keeps it under %APPDATA%.
596
+ function ghConfigDir(env = process.env, platform = process.platform) {
597
+ if (env.GH_CONFIG_DIR) return env.GH_CONFIG_DIR;
598
+ if (env.XDG_CONFIG_HOME) return path.join(env.XDG_CONFIG_HOME, 'gh');
599
+ if (platform === 'win32' && env.APPDATA) return path.win32.join(env.APPDATA, 'GitHub CLI');
600
+ return path.join(env.HOME || home(), '.config', 'gh');
601
+ }
602
+
603
+ function ghAliases() {
604
+ const dir = ghConfigDir();
605
+ const map = new Map();
606
+ let text = '';
607
+ try {
608
+ text = fs.readFileSync(path.join(dir, 'config.yml'), 'utf8');
609
+ } catch {
610
+ return map;
611
+ }
612
+ const block = /^aliases:\s*\n((?:[ \t]+.*\n?)*)/m.exec(text);
613
+ for (const line of (block ? block[1] : '').split('\n')) {
614
+ const m = /^\s+([^:\s]+):\s*(.*)$/.exec(line);
615
+ if (m) map.set(m[1], m[2].replace(/^(['"])(.*)\1$/, '$2'));
616
+ }
617
+ return map;
618
+ }
619
+
620
+ function resolveAliases(scan) {
621
+ const cache = new Map();
622
+ // A computed command name could be git or gh: its first argument must not be a gated alias either.
623
+ for (const head of scan.dynamicHeads) {
624
+ if (head.argsStatic && head.first && head.known) {
625
+ for (const tool of ['git', 'gh']) scan.nonBuiltin.push({ tool, sub: head.first, dir: head.dir, known: true, changesEnv: false, viaDynamic: true });
626
+ }
627
+ }
628
+ for (const nb of scan.nonBuiltin) {
629
+ if (!nb.viaDynamic && (nb.changesEnv || scan.mutatesEnv || !nb.known)) {
630
+ scan.forced.push(`\`${nb.tool} ${nb.sub}\` may be an alias and the line changes its environment`);
631
+ continue;
632
+ }
633
+ const key = `${nb.tool}:${nb.dir}`;
634
+ if (!cache.has(key)) cache.set(key, nb.tool === 'git' ? gitAliases(nb.dir) : ghAliases());
635
+ const map = cache.get(key);
636
+ const gated = nb.tool === 'git' ? /\b(commit|push|send-pack|commit-tree)\b/ : /\b(pr\s+(create|new)|api)\b/;
637
+ let name = nb.sub;
638
+ for (let hop = 0; hop < 10 && map.has(name); hop += 1) {
639
+ const value = map.get(name);
640
+ if (value.trim().startsWith('!')) {
641
+ scan.forced.push(`\`${nb.tool} ${nb.sub}\` is a shell alias`);
642
+ break;
643
+ }
644
+ if (gated.test(value)) {
645
+ scan.forced.push(`\`${nb.tool} ${nb.sub}\` is an alias for \`${nb.tool} ${value.trim()}\``);
646
+ break;
647
+ }
648
+ name = value.trim().split(/\s+/)[0];
649
+ }
650
+ }
651
+ }
652
+
653
+ // ── Public ───────────────────────────────────────────────────────────────────
654
+
655
+ const trailingOnly = (structure) => structure.ops.every((o) => (o.v === ';' || o.v === '\n') && o.after === structure.cmds.length - 1);
656
+
657
+ /** Text of a command substitution that only prints (cat/printf/echo, maybe a heredoc). */
658
+ function isPrinter(text) {
659
+ const { tokens, notes } = lex(text);
660
+ if (notes.length) return false;
661
+ const structure = commands(tokens);
662
+ if (structure.cmds.length !== 1 || !trailingOnly(structure)) return false;
663
+ const [cmd] = structure.cmds;
664
+ const [head] = cmd.words;
665
+ return Boolean(head) && !head.dyn && !head.glob && PRINTERS.has(head.v) && !cmd.assigns.length
666
+ && cmd.words.every((w) => !w.subs.length) && cmd.redirs.every((r) => (r.heredoc ? !r.heredoc.expands : !r.target || !r.target.subs.length));
667
+ }
668
+
669
+ /**
670
+ * @returns {{ calls: object[], notes: string[], forced: string[], words: string[], top: object | null }}
671
+ */
672
+ function analyze(command, cwd) {
673
+ const scan = { calls: [], notes: [], forced: [], words: [], nonBuiltin: [], dynamicHeads: [], dynamicScripts: [], mutatesEnv: false };
674
+ let top = walk(command, cwd, 0, scan);
675
+ // `sh -c '<script>'` alone on the line is judged as the script itself.
676
+ while (top && top.cmds.length === 1 && trailingOnly(top) && !top.compound) {
677
+ const [cmd] = top.cmds;
678
+ if (!cmd.shellScript || !cmd.shellPlain || cmd.assigns.length || cmd.redirs.length || cmd.wrapped) break;
679
+ top = cmd.shellScript;
680
+ }
681
+ resolveAliases(scan);
682
+ return { ...scan, top };
683
+ }
684
+
685
+ /** Why `call` is not alone on the line, or null when it is (optionally after one `cd <dir> &&`). */
686
+ function isolationProblem(result, call) {
687
+ const { top } = result;
688
+ if (result.calls.length !== 1) return 'it shares the line with another gated command';
689
+ if (!top || top.compound) return 'it is inside a compound command';
690
+ if (call.wrapped) return 'it is wrapped in another command';
691
+ if (call.depth !== top.depth) return 'it runs inside another command';
692
+ const last = top.cmds[top.cmds.length - 1];
693
+ if (last !== call.cmd) return 'other commands run with it';
694
+ if (top.cmds.length === 1) {
695
+ if (!trailingOnly(top)) return 'other commands run with it';
696
+ } else if (top.cmds.length === 2) {
697
+ const [cd] = top.cmds;
698
+ const plainCd = cd.cdTarget && cd.words.length === 2 && !cd.assigns.length && !cd.redirs.length && !cd.wrapped && !subsOf(cd).length;
699
+ const ops = top.ops.filter((o) => !(o.after === 1 && (o.v === ';' || o.v === '\n')));
700
+ if (!plainCd || ops.length !== 1 || ops[0].v !== '&&' || ops[0].after !== 0) return 'only one `cd <dir> &&` may come before it';
701
+ } else {
702
+ return 'other commands run with it';
703
+ }
704
+ if (!call.dirKnown) return 'its working directory cannot be determined';
705
+ if (!subsOf(call.cmd).every(isPrinter)) return 'a command substitution in it runs another command';
706
+ return null;
707
+ }
708
+
709
+ const GH_CONFIG_ENV = /^(GH_CONFIG_DIR|XDG_CONFIG_HOME|HOME|APPDATA|USERPROFILE)$/;
710
+
711
+ /** GIT_DIR-style redirection on a gated call, or a gh config dir the alias check never read. */
712
+ function redirectionProblem(call) {
713
+ const names = call.assigns.map(assignName);
714
+ const ghEnv = call.kind === 'pr' ? names.filter((n) => GH_CONFIG_ENV.test(n)) : [];
715
+ if (ghEnv.length) return `${ghEnv.join(', ')} points gh at a config the gate never read`;
716
+ const env = names.filter((n) => /^GIT_/.test(n));
717
+ if (env.length) return `${env.join(', ')} points git somewhere the token never saw`;
718
+ if (call.redirect.length) return `${call.redirect.join(', ')} points git somewhere the token never saw`;
719
+ return null;
720
+ }
721
+
722
+ module.exports = { lex, analyze, isolationProblem, redirectionProblem, isPrinter, ghConfigDir };