skillgendev 0.1.0__tar.gz

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 (309) hide show
  1. skillgendev-0.1.0/.gitignore +48 -0
  2. skillgendev-0.1.0/CONTRIBUTING.md +438 -0
  3. skillgendev-0.1.0/LICENSE +201 -0
  4. skillgendev-0.1.0/PKG-INFO +796 -0
  5. skillgendev-0.1.0/README.md +766 -0
  6. skillgendev-0.1.0/claude/agents/route-analyzer.md +26 -0
  7. skillgendev-0.1.0/claude/commands/command.md +23 -0
  8. skillgendev-0.1.0/claude/skills/driver/SKILL.md +76 -0
  9. skillgendev-0.1.0/docs/decisions.md +1044 -0
  10. skillgendev-0.1.0/docs/spec.md +639 -0
  11. skillgendev-0.1.0/hintpacks/README.md +85 -0
  12. skillgendev-0.1.0/hintpacks/folders/astro-pages.yml +13 -0
  13. skillgendev-0.1.0/hintpacks/folders/nextjs-app.yml +13 -0
  14. skillgendev-0.1.0/hintpacks/folders/nextjs-pages.yml +13 -0
  15. skillgendev-0.1.0/hintpacks/folders/nuxt-pages.yml +13 -0
  16. skillgendev-0.1.0/hintpacks/folders/sveltekit-routes.yml +13 -0
  17. skillgendev-0.1.0/hintpacks/routers/aspnet-attributes.yml +16 -0
  18. skillgendev-0.1.0/hintpacks/routers/django.yml +12 -0
  19. skillgendev-0.1.0/hintpacks/routers/express.yml +9 -0
  20. skillgendev-0.1.0/hintpacks/routers/flask.yml +21 -0
  21. skillgendev-0.1.0/hintpacks/routers/laravel.yml +12 -0
  22. skillgendev-0.1.0/hintpacks/routers/phoenix.yml +13 -0
  23. skillgendev-0.1.0/hintpacks/routers/rails.yml +14 -0
  24. skillgendev-0.1.0/hintpacks/routers/react-router-jsx.yml +13 -0
  25. skillgendev-0.1.0/hintpacks/routers/route-objects-js.yml +21 -0
  26. skillgendev-0.1.0/hintpacks/routers/route-objects.yml +21 -0
  27. skillgendev-0.1.0/hintpacks/routers/spring.yml +13 -0
  28. skillgendev-0.1.0/pyproject.toml +160 -0
  29. skillgendev-0.1.0/scripts/install-local.ps1 +192 -0
  30. skillgendev-0.1.0/scripts/install-local.sh +73 -0
  31. skillgendev-0.1.0/src/skillgen/__init__.py +7 -0
  32. skillgendev-0.1.0/src/skillgen/analyze/__init__.py +1 -0
  33. skillgendev-0.1.0/src/skillgen/analyze/prompt.py +189 -0
  34. skillgendev-0.1.0/src/skillgen/analyze/runner.py +457 -0
  35. skillgendev-0.1.0/src/skillgen/assets.py +69 -0
  36. skillgendev-0.1.0/src/skillgen/cli.py +1678 -0
  37. skillgendev-0.1.0/src/skillgen/compile/__init__.py +1 -0
  38. skillgendev-0.1.0/src/skillgen/compile/assess.py +175 -0
  39. skillgendev-0.1.0/src/skillgen/compile/fields.py +320 -0
  40. skillgendev-0.1.0/src/skillgen/compile/layout.py +87 -0
  41. skillgendev-0.1.0/src/skillgen/compile/merge.py +560 -0
  42. skillgendev-0.1.0/src/skillgen/compile/unopened.py +114 -0
  43. skillgendev-0.1.0/src/skillgen/config.py +165 -0
  44. skillgendev-0.1.0/src/skillgen/drift/__init__.py +1 -0
  45. skillgendev-0.1.0/src/skillgen/drift/check.py +113 -0
  46. skillgendev-0.1.0/src/skillgen/drift/diff.py +120 -0
  47. skillgendev-0.1.0/src/skillgen/errors.py +114 -0
  48. skillgendev-0.1.0/src/skillgen/export.py +417 -0
  49. skillgendev-0.1.0/src/skillgen/generate/__init__.py +1 -0
  50. skillgendev-0.1.0/src/skillgen/generate/bundle.py +251 -0
  51. skillgendev-0.1.0/src/skillgen/generate/evals.py +144 -0
  52. skillgendev-0.1.0/src/skillgen/generate/examples.py +167 -0
  53. skillgendev-0.1.0/src/skillgen/generate/fill_engine.js +202 -0
  54. skillgendev-0.1.0/src/skillgen/generate/fill_script.py +253 -0
  55. skillgendev-0.1.0/src/skillgen/generate/folder.py +115 -0
  56. skillgendev-0.1.0/src/skillgen/generate/index_page.py +218 -0
  57. skillgendev-0.1.0/src/skillgen/generate/index_template.html +86 -0
  58. skillgendev-0.1.0/src/skillgen/generate/inline_setter.py +725 -0
  59. skillgendev-0.1.0/src/skillgen/generate/layout_reference.py +72 -0
  60. skillgendev-0.1.0/src/skillgen/generate/locators.py +270 -0
  61. skillgendev-0.1.0/src/skillgen/generate/manual_steps.py +165 -0
  62. skillgendev-0.1.0/src/skillgen/generate/skill_md.py +872 -0
  63. skillgendev-0.1.0/src/skillgen/generate/user_rules.py +129 -0
  64. skillgendev-0.1.0/src/skillgen/graph/__init__.py +1 -0
  65. skillgendev-0.1.0/src/skillgen/graph/codegraph.py +527 -0
  66. skillgendev-0.1.0/src/skillgen/graph/connections.py +335 -0
  67. skillgendev-0.1.0/src/skillgen/graph/hubs.py +84 -0
  68. skillgendev-0.1.0/src/skillgen/graph/slice.py +241 -0
  69. skillgendev-0.1.0/src/skillgen/handoff.py +217 -0
  70. skillgendev-0.1.0/src/skillgen/logs.py +121 -0
  71. skillgendev-0.1.0/src/skillgen/model/__init__.py +1 -0
  72. skillgendev-0.1.0/src/skillgen/model/confidence.py +83 -0
  73. skillgendev-0.1.0/src/skillgen/model/facts.py +66 -0
  74. skillgendev-0.1.0/src/skillgen/model/page.py +489 -0
  75. skillgendev-0.1.0/src/skillgen/model/probe.py +117 -0
  76. skillgendev-0.1.0/src/skillgen/model/route.py +183 -0
  77. skillgendev-0.1.0/src/skillgen/model/serialize.py +592 -0
  78. skillgendev-0.1.0/src/skillgen/pipeline.py +1551 -0
  79. skillgendev-0.1.0/src/skillgen/probe/__init__.py +1 -0
  80. skillgendev-0.1.0/src/skillgen/probe/capture.py +153 -0
  81. skillgendev-0.1.0/src/skillgen/probe/classify.py +136 -0
  82. skillgendev-0.1.0/src/skillgen/probe/extract.js +164 -0
  83. skillgendev-0.1.0/src/skillgen/probe/journal.py +281 -0
  84. skillgendev-0.1.0/src/skillgen/probe/measure.js +24 -0
  85. skillgendev-0.1.0/src/skillgen/probe/runner.py +1066 -0
  86. skillgendev-0.1.0/src/skillgen/py.typed +0 -0
  87. skillgendev-0.1.0/src/skillgen/routes/__init__.py +1 -0
  88. skillgendev-0.1.0/src/skillgen/routes/astgrep.py +415 -0
  89. skillgendev-0.1.0/src/skillgen/routes/folders.py +131 -0
  90. skillgendev-0.1.0/src/skillgen/routes/hintpacks.py +227 -0
  91. skillgendev-0.1.0/src/skillgen/routes/merge.py +208 -0
  92. skillgendev-0.1.0/src/skillgen/routes/naming.py +202 -0
  93. skillgendev-0.1.0/src/skillgen/routes/paths.py +150 -0
  94. skillgendev-0.1.0/src/skillgen/schema/__init__.py +97 -0
  95. skillgendev-0.1.0/src/skillgen/schema/cli/add-rule.schema.json +49 -0
  96. skillgendev-0.1.0/src/skillgen/schema/cli/answer.schema.json +43 -0
  97. skillgendev-0.1.0/src/skillgen/schema/cli/check.schema.json +61 -0
  98. skillgendev-0.1.0/src/skillgen/schema/cli/doctor.schema.json +65 -0
  99. skillgendev-0.1.0/src/skillgen/schema/cli/export-chatgpt.schema.json +52 -0
  100. skillgendev-0.1.0/src/skillgen/schema/cli/export.schema.json +53 -0
  101. skillgendev-0.1.0/src/skillgen/schema/cli/generate.schema.json +169 -0
  102. skillgendev-0.1.0/src/skillgen/schema/cli/index.schema.json +48 -0
  103. skillgendev-0.1.0/src/skillgen/schema/cli/init.schema.json +49 -0
  104. skillgendev-0.1.0/src/skillgen/schema/cli/install.schema.json +47 -0
  105. skillgendev-0.1.0/src/skillgen/schema/cli/login.schema.json +40 -0
  106. skillgendev-0.1.0/src/skillgen/schema/cli/logs.schema.json +49 -0
  107. skillgendev-0.1.0/src/skillgen/schema/cli/prepare.schema.json +68 -0
  108. skillgendev-0.1.0/src/skillgen/schema/cli/probe.schema.json +63 -0
  109. skillgendev-0.1.0/src/skillgen/schema/cli/questions.schema.json +74 -0
  110. skillgendev-0.1.0/src/skillgen/schema/cli/routes.schema.json +85 -0
  111. skillgendev-0.1.0/src/skillgen/schema/cli/validate.schema.json +66 -0
  112. skillgendev-0.1.0/src/skillgen/schema/page-analysis.schema.json +523 -0
  113. skillgendev-0.1.0/src/skillgen/schema/page.schema.json +561 -0
  114. skillgendev-0.1.0/src/skillgen/schema/route-listing.schema.json +22 -0
  115. skillgendev-0.1.0/src/skillgen/validate/__init__.py +1 -0
  116. skillgendev-0.1.0/src/skillgen/validate/model_checks.py +127 -0
  117. skillgendev-0.1.0/src/skillgen/validate/scan.py +119 -0
  118. skillgendev-0.1.0/src/skillgen/validate/spec.py +191 -0
  119. skillgendev-0.1.0/src/skillgen/workspace.py +301 -0
  120. skillgendev-0.1.0/tests/__init__.py +0 -0
  121. skillgendev-0.1.0/tests/bench/README.md +154 -0
  122. skillgendev-0.1.0/tests/bench/analysis.json +501 -0
  123. skillgendev-0.1.0/tests/bench/cases.json +295 -0
  124. skillgendev-0.1.0/tests/bench/check.py +163 -0
  125. skillgendev-0.1.0/tests/bench/files/sample-id.pdf +32 -0
  126. skillgendev-0.1.0/tests/bench/page.json +696 -0
  127. skillgendev-0.1.0/tests/bench/probe.json +604 -0
  128. skillgendev-0.1.0/tests/bench/serve.py +206 -0
  129. skillgendev-0.1.0/tests/bench/site/bench.css +47 -0
  130. skillgendev-0.1.0/tests/bench/site/bench.js +287 -0
  131. skillgendev-0.1.0/tests/bench/site/index.html +186 -0
  132. skillgendev-0.1.0/tests/bench/site/wizard.html +96 -0
  133. skillgendev-0.1.0/tests/bench/skills/bench-apply/SKILL.md +95 -0
  134. skillgendev-0.1.0/tests/bench/skills/bench-apply/evals/evals.json +53 -0
  135. skillgendev-0.1.0/tests/bench/skills/bench-apply/references/fill.md +184 -0
  136. skillgendev-0.1.0/tests/bench/skills/bench-apply/references/layout.json +203 -0
  137. skillgendev-0.1.0/tests/bench/skills/bench-apply/references/locators.md +35 -0
  138. skillgendev-0.1.0/tests/bench/skills/bench-apply/references/manual-steps.md +40 -0
  139. skillgendev-0.1.0/tests/bench/skills/bench-apply/scripts/fill.js +231 -0
  140. skillgendev-0.1.0/tests/bench/wizard-analysis.json +126 -0
  141. skillgendev-0.1.0/tests/conftest.py +55 -0
  142. skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/feedback.json +44 -0
  143. skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/home.json +1 -0
  144. skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/permits-renew.json +26 -0
  145. skillgendev-0.1.0/tests/fixtures/flask-jinja/app.py +34 -0
  146. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/generate.json +17 -0
  147. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/SKILL.md +67 -0
  148. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/evals/evals.json +53 -0
  149. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/fill.md +121 -0
  150. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/locators.md +17 -0
  151. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/manual-steps.md +16 -0
  152. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/scripts/fill.js +216 -0
  153. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/SKILL.md +54 -0
  154. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/evals/evals.json +53 -0
  155. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/fill.md +121 -0
  156. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/locators.md +14 -0
  157. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/manual-steps.md +10 -0
  158. skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/scripts/fill.js +213 -0
  159. skillgendev-0.1.0/tests/fixtures/flask-jinja/forms.py +23 -0
  160. skillgendev-0.1.0/tests/fixtures/flask-jinja/requirements.txt +1 -0
  161. skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/feedback.html +22 -0
  162. skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/index.html +1 -0
  163. skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/renew.html +14 -0
  164. skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Controllers/SignupController.cs +8 -0
  165. skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Models/SignupModel.cs +2 -0
  166. skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Pages/Contact.cshtml +3 -0
  167. skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/app/Http/Controllers/KycController.php +3 -0
  168. skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/resources/views/kyc.blade.php +1 -0
  169. skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/routes/web.php +3 -0
  170. skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/controllers/signup_controller.ex +5 -0
  171. skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/live/contact_live.ex +4 -0
  172. skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/router.ex +7 -0
  173. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/controllers/projects_controller.rb +8 -0
  174. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/controllers/registrations_controller.rb +8 -0
  175. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/models/user.rb +3 -0
  176. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/views/projects/edit.html.erb +1 -0
  177. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/views/registrations/new.html.erb +1 -0
  178. skillgendev-0.1.0/tests/fixtures/hintpacks/rails/config/routes.rb +5 -0
  179. skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/SignupController.java +10 -0
  180. skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/SignupForm.java +3 -0
  181. skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/UserService.java +2 -0
  182. skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/resources/templates/signup.html +1 -0
  183. skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/package.json +1 -0
  184. skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/src/router/index.js +8 -0
  185. skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/src/views/SignupView.vue +3 -0
  186. skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/contact.json +40 -0
  187. skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/home.json +1 -0
  188. skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/orders-return.json +32 -0
  189. skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/register.json +34 -0
  190. skillgendev-0.1.0/tests/fixtures/nextjs-app/app/(account)/register/page.tsx +20 -0
  191. skillgendev-0.1.0/tests/fixtures/nextjs-app/app/contact/page.tsx +29 -0
  192. skillgendev-0.1.0/tests/fixtures/nextjs-app/app/orders/[orderId]/return/page.tsx +29 -0
  193. skillgendev-0.1.0/tests/fixtures/nextjs-app/app/page.tsx +3 -0
  194. skillgendev-0.1.0/tests/fixtures/nextjs-app/components/CountryCombobox.tsx +24 -0
  195. skillgendev-0.1.0/tests/fixtures/nextjs-app/components/Field.tsx +10 -0
  196. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/generate.json +22 -0
  197. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/SKILL.md +63 -0
  198. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/evals/evals.json +53 -0
  199. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/fill.md +121 -0
  200. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/locators.md +16 -0
  201. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/manual-steps.md +15 -0
  202. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/scripts/fill.js +215 -0
  203. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/SKILL.md +57 -0
  204. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/evals/evals.json +53 -0
  205. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/fill.md +123 -0
  206. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/locators.md +18 -0
  207. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/manual-steps.md +13 -0
  208. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/scripts/fill.js +214 -0
  209. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/SKILL.md +55 -0
  210. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/evals/evals.json +53 -0
  211. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/fill.md +121 -0
  212. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/locators.md +15 -0
  213. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/manual-steps.md +11 -0
  214. skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/scripts/fill.js +214 -0
  215. skillgendev-0.1.0/tests/fixtures/nextjs-app/lib/schemas.ts +22 -0
  216. skillgendev-0.1.0/tests/fixtures/nextjs-app/next.config.js +1 -0
  217. skillgendev-0.1.0/tests/fixtures/nextjs-app/package.json +6 -0
  218. skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/about.json +1 -0
  219. skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/projects-edit.json +26 -0
  220. skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/settings-profile.json +37 -0
  221. skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/signup.json +47 -0
  222. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/generate.json +22 -0
  223. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/SKILL.md +54 -0
  224. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/evals/evals.json +53 -0
  225. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/fill.md +121 -0
  226. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/locators.md +14 -0
  227. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/manual-steps.md +10 -0
  228. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/scripts/fill.js +213 -0
  229. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/SKILL.md +55 -0
  230. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/evals/evals.json +53 -0
  231. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/fill.md +121 -0
  232. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/locators.md +15 -0
  233. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/manual-steps.md +11 -0
  234. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/scripts/fill.js +214 -0
  235. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/SKILL.md +65 -0
  236. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/evals/evals.json +53 -0
  237. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/fill.md +137 -0
  238. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/locators.md +17 -0
  239. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/manual-steps.md +16 -0
  240. skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/scripts/fill.js +216 -0
  241. skillgendev-0.1.0/tests/fixtures/react-router-spa/package.json +6 -0
  242. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/App.tsx +18 -0
  243. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/api/client.ts +9 -0
  244. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/components/SelectField.tsx +13 -0
  245. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/components/TextField.tsx +11 -0
  246. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/About.tsx +3 -0
  247. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/ProfileSettings.tsx +22 -0
  248. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/ProjectEdit.tsx +19 -0
  249. skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/Signup.tsx +30 -0
  250. skillgendev-0.1.0/tests/helpers.py +70 -0
  251. skillgendev-0.1.0/tests/test_analyze_runner.py +190 -0
  252. skillgendev-0.1.0/tests/test_assess.py +68 -0
  253. skillgendev-0.1.0/tests/test_assets.py +14 -0
  254. skillgendev-0.1.0/tests/test_astgrep.py +207 -0
  255. skillgendev-0.1.0/tests/test_bench.py +174 -0
  256. skillgendev-0.1.0/tests/test_boundaries.py +140 -0
  257. skillgendev-0.1.0/tests/test_bundle.py +125 -0
  258. skillgendev-0.1.0/tests/test_capture.py +15 -0
  259. skillgendev-0.1.0/tests/test_check.py +70 -0
  260. skillgendev-0.1.0/tests/test_classify.py +87 -0
  261. skillgendev-0.1.0/tests/test_cli.py +872 -0
  262. skillgendev-0.1.0/tests/test_codegraph.py +186 -0
  263. skillgendev-0.1.0/tests/test_confidence.py +42 -0
  264. skillgendev-0.1.0/tests/test_config.py +48 -0
  265. skillgendev-0.1.0/tests/test_connections.py +213 -0
  266. skillgendev-0.1.0/tests/test_diff.py +50 -0
  267. skillgendev-0.1.0/tests/test_docs.py +58 -0
  268. skillgendev-0.1.0/tests/test_errors.py +15 -0
  269. skillgendev-0.1.0/tests/test_evals.py +28 -0
  270. skillgendev-0.1.0/tests/test_examples.py +48 -0
  271. skillgendev-0.1.0/tests/test_export.py +288 -0
  272. skillgendev-0.1.0/tests/test_facts.py +15 -0
  273. skillgendev-0.1.0/tests/test_fields.py +54 -0
  274. skillgendev-0.1.0/tests/test_fill_script.py +124 -0
  275. skillgendev-0.1.0/tests/test_fixtures.py +225 -0
  276. skillgendev-0.1.0/tests/test_folder.py +57 -0
  277. skillgendev-0.1.0/tests/test_folders.py +109 -0
  278. skillgendev-0.1.0/tests/test_handoff.py +111 -0
  279. skillgendev-0.1.0/tests/test_hintpacks.py +107 -0
  280. skillgendev-0.1.0/tests/test_hubs.py +40 -0
  281. skillgendev-0.1.0/tests/test_index_page.py +71 -0
  282. skillgendev-0.1.0/tests/test_inline_setter.py +469 -0
  283. skillgendev-0.1.0/tests/test_inventory.py +138 -0
  284. skillgendev-0.1.0/tests/test_journal.py +133 -0
  285. skillgendev-0.1.0/tests/test_layout.py +96 -0
  286. skillgendev-0.1.0/tests/test_layout_reference.py +50 -0
  287. skillgendev-0.1.0/tests/test_locators.py +81 -0
  288. skillgendev-0.1.0/tests/test_logs.py +72 -0
  289. skillgendev-0.1.0/tests/test_manual_steps.py +56 -0
  290. skillgendev-0.1.0/tests/test_merge.py +291 -0
  291. skillgendev-0.1.0/tests/test_model_checks.py +69 -0
  292. skillgendev-0.1.0/tests/test_model_probe.py +12 -0
  293. skillgendev-0.1.0/tests/test_naming.py +101 -0
  294. skillgendev-0.1.0/tests/test_page.py +39 -0
  295. skillgendev-0.1.0/tests/test_paths.py +71 -0
  296. skillgendev-0.1.0/tests/test_pipeline.py +416 -0
  297. skillgendev-0.1.0/tests/test_probe_runner.py +207 -0
  298. skillgendev-0.1.0/tests/test_prompt.py +92 -0
  299. skillgendev-0.1.0/tests/test_route.py +27 -0
  300. skillgendev-0.1.0/tests/test_route_merge.py +135 -0
  301. skillgendev-0.1.0/tests/test_scan.py +54 -0
  302. skillgendev-0.1.0/tests/test_schema.py +24 -0
  303. skillgendev-0.1.0/tests/test_serialize.py +123 -0
  304. skillgendev-0.1.0/tests/test_skill_md.py +215 -0
  305. skillgendev-0.1.0/tests/test_slice.py +65 -0
  306. skillgendev-0.1.0/tests/test_spec.py +83 -0
  307. skillgendev-0.1.0/tests/test_unopened.py +124 -0
  308. skillgendev-0.1.0/tests/test_user_rules.py +67 -0
  309. skillgendev-0.1.0/tests/test_workspace.py +82 -0
@@ -0,0 +1,48 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ .python-version
9
+
10
+ # Tooling caches
11
+ .mypy_cache/
12
+ .ruff_cache/
13
+ .pytest_cache/
14
+ .coverage
15
+ htmlcov/
16
+
17
+ # Node and the docs website (website/)
18
+ node_modules/
19
+ .docusaurus/
20
+ .wrangler/
21
+ .next/
22
+ out/
23
+ *.tsbuildinfo
24
+ npm-debug.log*
25
+ yarn-debug.log*
26
+ yarn-error.log*
27
+ pnpm-debug.log*
28
+
29
+ # Graphify output when run by hand
30
+ graphify-out/
31
+
32
+ # skillgendev workspaces: secrets and caches are never committed
33
+ .skillgendev/secrets/
34
+ .skillgendev/cache/
35
+ **/.skillgendev/secrets/
36
+ **/.skillgendev/cache/
37
+ *.storage-state.json
38
+ .env
39
+ .env.*
40
+
41
+ # Bench runtime output
42
+ tests/bench/.last-submission.json
43
+
44
+ # Editors and OS
45
+ .idea/
46
+ .vscode/
47
+ .DS_Store
48
+ tests/bench/dist/
@@ -0,0 +1,438 @@
1
+ # Contributing to skillgendev
2
+
3
+ Thanks for helping. skillgendev turns an app's code into one Agent Skills folder per
4
+ page, so Claude or Cowork can fill that page's form with few tokens. This guide
5
+ explains how to set up, what we expect from a change, and how to test it, including
6
+ end to end in Claude or Cowork.
7
+
8
+ The design is in [`docs/spec.md`](docs/spec.md). Decisions that change it are in
9
+ [`docs/decisions.md`](docs/decisions.md). If the two disagree, the decision log wins.
10
+
11
+ ## Ground rules in one minute
12
+
13
+ 1. **No framework or domain knowledge in the core.** Router conventions, validation
14
+ libraries and the like go in `hintpacks/` as YAML. Deleting `hintpacks/` must
15
+ leave a working tool.
16
+ 2. **No MCP servers, no client installs.** Generated skills run in Claude or Cowork
17
+ with the built-in browser and nothing else.
18
+ 3. **Code-only generation is the default.** The Playwright probe is optional
19
+ (`pip install "skillgendev[probe]"`) and only runs when a dev URL is given.
20
+ 4. **Every fact has a source and a confidence. Evidence never reaches a skill.**
21
+ 5. **No secrets or real personal data**, anywhere: fixtures, tests, logs, examples.
22
+
23
+ ## Set up
24
+
25
+ You need Python 3.10 or newer and [uv](https://docs.astral.sh/uv/).
26
+
27
+ ```bash
28
+ git clone https://github.com/chapeee/skillprinter
29
+ cd skillprinter
30
+ uv sync --all-extras # core, probe and ast-grep extras, dev tools
31
+ uv run playwright install chromium # only for browser tests and the probe
32
+ ```
33
+
34
+ To try your working copy as a global `skillgendev` command in other projects (like
35
+ `npm link`), run the local install script. It clears uv's cache for the package,
36
+ installs your clone in editable mode and checks that `skillgendev` points at it:
37
+
38
+ ```bash
39
+ scripts/install-local.sh # macOS, Linux
40
+ .\scripts\install-local.ps1 # Windows PowerShell (add -RunTests to run all checks)
41
+ ```
42
+
43
+ Code edits apply at once. Rerun the script after changing dependencies or
44
+ `pyproject.toml`.
45
+
46
+ ## Before you open a pull request
47
+
48
+ Run all four. CI runs the same commands.
49
+
50
+ ```bash
51
+ uv run ruff check .
52
+ uv run ruff format --check .
53
+ uv run mypy
54
+ uv run pytest # unit tests, no network, no browser
55
+ uv run pytest -m browser # if you touched fill.js, probe/ or tests/bench/
56
+ ```
57
+
58
+ If you changed anything that affects generated output, also run the bench by hand
59
+ in Claude or Cowork (see [Testing in Claude and Cowork](#testing-in-claude-and-cowork)).
60
+
61
+ ## Code style
62
+
63
+ **Tooling.** Python 3.10+, uv, ruff (lint and format), mypy `--strict`, pytest.
64
+
65
+ **Layout.** Code lives in `src/skillgen/`:
66
+
67
+ | Package | What it does |
68
+ | --- | --- |
69
+ | `graph/` | Graphify adapter, page slice, change impact, likely router files (`hubs.py`). The only place that imports `graphify`. |
70
+ | `routes/` | Route probes, merge into `routes.json`, skill names |
71
+ | `probe/` | Optional Playwright probe. The only place that imports `playwright`, and only lazily. |
72
+ | `analyze/` | Prompts, `page-analysis.schema.json`, headless Claude Code runner |
73
+ | `model/` | `page.json` schema, facts, confidence |
74
+ | `compile/` | Merge facts into `page.json`; compile rules into checks |
75
+ | `generate/` | `SKILL.md`, `fill.js`, references, evals, index page |
76
+ | `validate/` | Agent Skills spec checks, model checks, secret and personal-data scan |
77
+ | `verify/` | With-skill vs without-skill runs |
78
+ | `drift/` | Stale-route check, semantic diff |
79
+ | `schema/` | JSON schemas for `page.json`, Claude's answers and every CLI `--json` output |
80
+ | `cli.py` | Argument parsing and output; the only module that prints |
81
+ | `pipeline.py` | Runs the per-route steps (files and subprocesses at the edge) |
82
+ | `handoff.py` | The `next` step a run hands to the agent or the developer, and the command to continue with |
83
+ | `logs.py` | Run logs: console level from `-v`, full debug log in `.skillgendev/logs/` |
84
+ | `export.py` | Folder, zip, Claude Code plugin and ChatGPT plugin export with `MANIFEST.sha256` |
85
+
86
+ **Naming.**
87
+
88
+ - Modules are lowercase nouns: `slice.py`, `confidence.py`.
89
+ - Functions are snake_case and start with a verb: `find_routes`, `build_page_model`.
90
+ - Classes are PascalCase nouns; constants are `UPPER_SNAKE`.
91
+ - Booleans start with `is_`, `has_` or `should_`.
92
+ - No abbreviations except `url`, `id`, `json`, `html`, `api`.
93
+ - Tests: `tests/test_<module>.py`, named `test_<function>_<behaviour>`.
94
+
95
+ **Functions.** One job each, about 40 lines at most, type hints on every argument and
96
+ return value. `model/`, `compile/` and `generate/` are pure: data in, data out. Files,
97
+ subprocesses and the network are touched only at the edges.
98
+
99
+ **Docstrings and comments.** Google-style docstrings on every public function and
100
+ class (summary, `Args`, `Returns`, `Raises`). Comments explain *why*, not *what*. No
101
+ commented-out code. A TODO names an issue: `# TODO(#42): …`.
102
+
103
+ **Errors and output.** Raise exceptions from `skillgen.errors`. Never use a bare
104
+ `except`. Log with `logging`; only `cli.py` prints. Every CLI command has `--json`,
105
+ and its output is checked against `src/skillgen/schema/cli/<command>.schema.json`.
106
+
107
+ **The page inventory.** `probe/extract.js` returns a page's fields *and* everything
108
+ clickable on it, each with a `step` that can be replayed (a selector when it names one
109
+ element, else `text:Label`). Three flags keep a reader off the wrong control, measured
110
+ on a real app where a naive click walk collected 122 fields from four different forms:
111
+ `chrome` (inside `nav`, `header`, `footer`, `aside` or a menu role), `leaves` (a link to
112
+ another path or origin) and `risky` (the label says submit, save, delete, reset, pay,
113
+ sign out and the like). On the maintainer's insurance form that turns 29 clickables into
114
+ 13 worth trying; on the onboarding form, 23 into 5. Tests: `tests/test_inventory.py`.
115
+
116
+ **Startup cost.** Every command pays for whatever `skillgen.cli` imports, so
117
+ `jsonschema` (about 320ms) and `PyYAML` (about 80ms) are imported where they are used:
118
+ `schema._validator_class`, `validate.spec.check_skill_md`, `routes.astgrep` and
119
+ `routes.hintpacks`. `tests/test_boundaries.py` fails if either reaches the startup path.
120
+ Measured: `import skillgen.cli` 1.08s to 0.68s, so every command starts about 0.15s
121
+ sooner once `uv`'s own overhead is counted.
122
+
123
+ **The name.** `skillgendev` is a working name. Use the constant `skillgen.TOOL_NAME`
124
+ instead of writing the name in strings, so the rename is one commit.
125
+
126
+ ## Commands: where each one lives
127
+
128
+ Every command is a `command_<name>` function in `src/skillgen/cli.py`, registered in
129
+ `HANDLERS`. Its `--json` output must match `src/skillgen/schema/cli/<name>.schema.json`.
130
+
131
+ | Command | Main code | Tests |
132
+ | --- | --- | --- |
133
+ | `init` | `workspace.py`, `config.py` | `test_cli.py`, `test_workspace.py`, `test_config.py` |
134
+ | `routes` | `pipeline.discover_routes`, `routes/`, `graph/hubs.py`, `pipeline.ask_claude_for_routes` | `test_folders.py`, `test_astgrep.py`, `test_hintpacks.py`, `test_hubs.py`, `test_cli.py` |
135
+ | `prepare` | `pipeline.prepare_route`, `analyze/prompt.py` | `test_cli.py`, `test_prompt.py` |
136
+ | `generate` | `pipeline.analyze_route` / `finish_route`, `compile/`, `generate/`, `validate/`; `--overwrite` in `cli.hold_existing_skills`; `--xy` in `cli._layout_measurer` → `probe.runner.measure_page_layout`; `--open` in `cli._with_open_steps` → `pipeline.apply_open_steps`; stops and `next` in `cli.find_stops_before_work`, `cli.finish_analysed`, `cli.describe_next_step` → `handoff.build_next_step`; analysis reuse in `pipeline.find_reusable_analysis` | `test_fixtures.py`, `test_merge.py`, `test_pipeline.py`, `test_handoff.py`; `--overwrite`, `next` and exit code 3 in `test_cli.py` |
137
+ | `questions`, `answer` | `cli.py`, `compile/assess.py` | `test_cli.py`, `test_assess.py` |
138
+ | `add-rule` | `generate/user_rules.py` | `test_user_rules.py`, `test_fixtures.py` |
139
+ | `validate` | `validate/spec.py`, `validate/scan.py` | `test_spec.py`, `test_scan.py` |
140
+ | `index` | `generate/index_page.py`, `pipeline.write_index` | `test_index_page.py`, `test_cli.py` |
141
+ | `check` | `drift/check.py`, `drift/diff.py` | `test_check.py`, `test_diff.py`, `test_cli.py` |
142
+ | `export` | `export.py` | `test_export.py`, `test_cli.py` |
143
+ | `export-chatgpt` | `export.export_chatgpt_plugin`: `build_chatgpt_manifest`, `retarget_skill_md`, `check_chatgpt_names` | `test_export.py`, `test_cli.py` |
144
+ | `probe`, `login` | `probe/runner.py` (Playwright, lazily imported) | `test_probe_runner.py` (`-m browser`) |
145
+ | `logs` | `logs.py` | `test_logs.py` |
146
+ | `install` | `claude/` (shipped as package data); Chromium in `cli.install_chromium`, shared with `doctor --fix` | `test_cli.py` (never downloads: `--no-chromium` or a stub) |
147
+ | `doctor` | `cli.py` | `test_cli.py` |
148
+
149
+ ### Adding or changing a command
150
+
151
+ In the same pull request:
152
+
153
+ 1. Add `command_<name>` and its parser in `cli.py`, and register it in `HANDLERS`.
154
+ 2. Add `schema/cli/<name>.schema.json` for its `--json` output.
155
+ 3. Add tests.
156
+ 4. **Document it for users** in the README's *Command reference* (every flag).
157
+ 5. **Document it for maintainers** in the table above.
158
+
159
+ `tests/test_docs.py` fails when a command or flag is missing from either document, and
160
+ `tests/test_cli.py` fails when a command has no schema.
161
+
162
+ ### Normal and `--xy`
163
+
164
+ A skill is always built from the code alone: `pipeline.write_skill` merges with no probe,
165
+ and `prepare` gives the analysis no probe either. `--xy` then measures the code-built page
166
+ (`probe.runner.measure_page_layout`: open the page, click the route's open steps, and
167
+ measure each field by the skill's own selector with `probe/measure.js`). The measuring
168
+ function returns only a `Layout`, so `--xy` can only add x/y, never change a field.
169
+ Measuring the skill's own selectors, rather than matching what the probe finds to them,
170
+ means a box always belongs to the element the layout check reads back; a run on a real
171
+ page showed the probe naming controls `input` and `select`, which matched nothing. Without `--xy` nothing is measured and
172
+ no saved capture is read. `tests/test_cli.py` holds both guarantees, and each test was
173
+ checked to fail with its fix removed.
174
+
175
+ Open steps come from two places. The code analysis returns them as `open` (prompt
176
+ version 2, `page-analysis.schema.json`), and `compile.merge.merge_open_steps` assesses
177
+ them like any fact. Steps read plainly in the code are trusted; deduced ones, and any
178
+ step naming a click that could change data (`is_risky_step`), become a question. The
179
+ developer's `--open`, saved in `routes.json`, always wins (`pipeline.write_skill`).
180
+
181
+ Open steps are a CSS selector or `text:Label`. One piece of JavaScript resolves them,
182
+ `inline_setter._STEP_TARGET`: the probe runs it through `build_open_script`, and the skill
183
+ runs it in `openForm`, which clicks only while the form's first steady field is hidden,
184
+ because an open step is often a toggle.
185
+
186
+ ### Exports: one skill per page, or one bundled skill
187
+
188
+ Each product has two ways in, and `export` covers both shapes (`tests/test_export.py`):
189
+
190
+ | Layout | How | Who takes it |
191
+ | --- | --- | --- |
192
+ | one folder per page | `export` (the default) | a skills folder on the developer's machine |
193
+ | one bundled skill at the archive root | `export --one-skill` | Claude's and ChatGPT's skill uploads |
194
+ | plugin, one folder per page | `export --plugin` | Claude Code |
195
+ | plugin, one bundled skill | `export-chatgpt` | ChatGPT and Codex, via the OpenAI portal |
196
+
197
+ A **skill upload takes one skill at a time**: its archive must hold exactly one
198
+ `SKILL.md`, the skill folder must be the archive's top level (`<skill-name>/SKILL.md`),
199
+ the folder name must equal the frontmatter `name`, and a plugin manifest makes it
200
+ refuse the archive. A **plugin** may hold many skills but needs its manifest and
201
+ `skills/<name>/`. Both products refuse a plain multi-page archive with "Zip must contain
202
+ exactly one SKILL.md file", which is why `cli.describe_export_use` warns after a plain
203
+ `export` of several pages, and why the README leads with `--one-skill`.
204
+
205
+ `generate/bundle.py` does the bundling, and `export._laid_out` chooses the layout:
206
+
207
+ - `build_entry_skill_md` writes the single `SKILL.md`: a table of forms (title, route,
208
+ what is on the page, the file to read) and three rules for using it. It stays about
209
+ 25 lines, so a fill costs it plus one page file, whatever the page count.
210
+ - `build_page_files` moves each page's `SKILL.md` body to
211
+ `references/<skill>/page.md` (frontmatter dropped), its `scripts/*` to
212
+ `scripts/<skill>/*` and its other references to `references/<skill>/*`.
213
+ `retarget_support_paths` rewrites the `scripts/…` and `references/…` paths inside
214
+ every file, or a page's own text would point at files that are not there.
215
+ `DROPPED_FOLDERS` leaves `evals/` out: developer test files are not skill content,
216
+ and they were the only text naming Claude.
217
+ - `summarize_description` keeps the middle of a page description (what is on the page),
218
+ because the table already has columns for the form and its route.
219
+ - `write_export(root_name=…)` names the archive's single folder after the skill, so
220
+ `--out` cannot break the `<skill-name>/SKILL.md` rule.
221
+ - The bundled skill is checked with `check_skill_files` after bundling, so the
222
+ frontmatter `name` matching the folder and the 500-line body limit still hold.
223
+ - `build_bundle_compatibility` writes the compatibility line from the pages' `app-url`,
224
+ with the lead naming the AI the export is for: `BUNDLE_COMPATIBILITY_LEAD` (neutral)
225
+ or `CHATGPT_COMPATIBILITY_LEAD`.
226
+
227
+ Both plugin manifests name the author `export.PLUGIN_AUTHOR` (`chapeee`), the
228
+ maintainer's decision in `docs/decisions.md`; no flag changes it. `check_chatgpt_names`
229
+ refuses an invalid name, or one too long for the `<plugin>:<skill>` identity (64
230
+ characters), before anything is written.
231
+
232
+ `--json`: `export` reports `path`, `skills`, `files`, `plugin` and `skill` (the bundled
233
+ name, empty without `--one-skill`); `export-chatgpt` the same but for its own schema.
234
+ `tests/test_bundle.py` covers the bundling, and `tests/test_export.py` checks each
235
+ layout, that only one `SKILL.md` is written when bundling, the forced archive root, the
236
+ Agent Plugins key and name rules, and that the icon is square and at least 48 px.
237
+ Nothing is uploaded in tests.
238
+
239
+ ### Size budget
240
+
241
+ `validate.spec.check_skill_md` fails a body of 500 lines or more; the 5000-token budget
242
+ (`generate.skill_md.BODY_TOKEN_BUDGET`) is only a note (`validate.spec.find_size_notes`,
243
+ in `generate` results' `problems` and `validate`'s `notes`). `generate.folder.fit_skill_text`
244
+ first moves optional fields to `references/fields.md`, four at a time.
245
+
246
+ ### Views: steps, tabs, dialogs
247
+
248
+ A page's hidden parts are `PageModel.views` (`model/page.py` `View`: name, `enter` clicks
249
+ from the part before, field `keys`, optional `leave` clicks). The analysis returns them as
250
+ `views` (prompt version 3); `compile.merge.merge_views` trusts views read in the code,
251
+ asks about deduced ones, and `find_view_candidates` stops at the first view with a risky
252
+ click (`is_risky_step`). With `--xy`, `pipeline.with_view_candidates` walks deduced views
253
+ too and `keep_proven_views` keeps those whose fields appeared.
254
+
255
+ One script does every walk. `inline_setter._VIEWS` (included only for pages with views)
256
+ reaches each part with `stepTarget`, waits for its fields, fills it, leaves it, and
257
+ reports `view` and `stopped` (with the page's `[role=alert]` and `field_error` texts).
258
+ `run(values, {"view": name})` stops in a part. The probe reaches each view with that same
259
+ call (`inline_setter.build_probe_call`), filling earlier parts with example values:
260
+ `probe.runner.plan_stages` plans one fresh load per view and per conditional field
261
+ (`choose_reveal_value`), and `measure_boxes` measures each stage. Landmarks for the
262
+ layout check come only from fields in no view. Tests: `test_inline_setter.py` and
263
+ `test_probe_runner.py` drive `tests/bench/site/wizard.html` (served at `/wizard`) in
264
+ Chromium with `tests/bench/wizard-analysis.json`; `test_merge.py`, `test_serialize.py`,
265
+ `test_pipeline.py` and `test_skill_md.py` cover the rest.
266
+
267
+ ### Commands never ask
268
+
269
+ Commands are run by an AI agent, never by a person at a terminal, so no command may
270
+ prompt or wait for input. Replacing existing output is the one decision that stops
271
+ before any work; it takes a flag for the answer. Everything else the developer must do
272
+ never throws work away: the run does all it can from the code, then reports what is
273
+ needed in both the text and the `--json` output (`next`). The agent asks the developer
274
+ in chat and runs the command again.
275
+
276
+ `generate --overwrite` is the example. Without it, a skill whose folder already holds a
277
+ `SKILL.md` is left alone and reported with status `exists` (counted in `exists` in the
278
+ schema), before any analysis or probing runs. If nothing was left to generate,
279
+ `routes.json` is restored exactly, so a `--name` rename does not stick for a run that
280
+ wrote nothing.
281
+
282
+ ### A run that stops says what comes next
283
+
284
+ When a route cannot finish, its result carries `next` (`schema/cli/generate.schema.json`)
285
+ and the exit code is `EXIT_NEEDS` (3). The pipeline only names the need
286
+ (`errors.Need` on `RouteOutcome.need`); `handoff.build_next_step` turns it into the
287
+ question and the commands, so the wording lives in one place.
288
+
289
+ | Need | Found by | `next` |
290
+ | --- | --- | --- |
291
+ | `analysis` | any `AnalysisError` except with `--analyzer replay` (`cli.finish_analysed`) | `analyse`: the agent writes `next.write` from `next.prompt` |
292
+ | `url`, `probe`, `chromium`, `server` | `probe.runner.find_xy_need`, before any work (`cli.find_xy_need_before_work`); x/y is then off for the run and the skill is written from the code (`cli.add_xy_need`) | `ask`; `run` is `doctor --fix` for Chromium |
293
+ | `login` | `measure_page_layout` when no field showed and the page has a password field or moved to another address (`Measurement.is_sign_in`) | `ask`, `run` = `login --url …`; the skill is still written |
294
+ | `open` | `measure_page_layout` when no field showed, after `pipeline.measure_page_model` tried the analysis's own open steps (`compile.merge.find_open_candidates`, never a risky one) | `ask`; `then` replaces `--open` with `--open 'text:<label>'` |
295
+ | `overwrite` | status `exists` | `ask`; `then` adds `--overwrite` |
296
+
297
+ `then` is the same command line again (`--overwrite` added when the skill was written,
298
+ `--url '<url>'` for the address). It costs little because each analysis is fingerprinted
299
+ (`pipeline.fingerprint_request`: the prompt plus every slice file's bytes) and reused
300
+ while that fingerprint holds. The fingerprint is the code only (slice files and
301
+ `PROMPT_VERSION`, not the route), so concrete pages built by one file share it.
302
+ `analysis.fingerprint` records what a saved analysis was made from; `request.fingerprint`
303
+ records the last request, so an analysis the agent wrote after it is accepted; an
304
+ analysis with no record is accepted when it is newer than every slice file. Recorded
305
+ analyses (`replay`) are never reused, so tests stay exact.
306
+
307
+ When `--xy` measures nothing, `pipeline.measure_page_model` first tries the analysis's
308
+ open steps, deduced ones included: fields appearing prove them, so they are kept and
309
+ their question dropped. When x/y could not be measured, `compile.layout.carry_layout`
310
+ keeps the last run's boxes for fields whose selector is unchanged; it only verifies, so
311
+ an outdated box can never aim a click. SKILL.md says how many fields were seen on the
312
+ live page (`skill_md.describe_checked_fields`).
313
+
314
+ `pipeline.write_skill` checks before it saves: when a check fails only the questions are
315
+ written, so `page.json` and the skill on disk keep matching.
316
+
317
+ ### Logging
318
+
319
+ Use `logger = logging.getLogger(__name__)`, never `print` (only `cli.py` prints). The
320
+ console shows warnings (`-v` for info, `-vv` for debug); `.skillgendev/logs/` always gets
321
+ everything. Log what a developer needs to debug a run: inputs, counts, timings, external
322
+ calls and their errors. Never log secrets or personal data.
323
+
324
+ ## Generated output rules
325
+
326
+ - With a measured layout, the skill also gets `references/layout.json`
327
+ (`generate/layout_reference.py`): every measured box from `page.json`, in page and
328
+ anchor-relative coordinates, with the same anchor and landmarks as the fill call's
329
+ compact check (`inline_setter.build_geometry`, which holds landmarks only). It is
330
+ read on demand, never on a normal fill. Tests: `test_layout_reference.py`.
331
+ - `SKILL.md` carries the inline setter (`generate/inline_setter.py`) with the page's
332
+ selector map, so one `javascript_tool` call fills the page and nothing else is read
333
+ first. It reports one status per key: `ok`, `partial`, `rejected`, `missing`,
334
+ `escalate`, `unknown`.
335
+ - `fill.js` is the fallback, read only when a key comes back `escalate`. Both scripts
336
+ are plain ES2019 with no dependencies and return compact JSON only. fill.js is
337
+ bounded by `MAX_SCRIPT_BYTES`, not a line count.
338
+ - Both scripts write values through the prototype's setter (`put` in the setter,
339
+ `setValue` in fill.js) and click boxes instead of setting `checked`: a
340
+ framework-controlled input ignores a direct `el.value = …`, while the read-back still
341
+ says `ok`. `test_setter_fills_inputs_a_framework_controls` (`-m browser`) rebuilds
342
+ that trap and fails on a direct write.
343
+ - Every skill carries four fill methods, and `SKILL.md`'s "Choose how to fill"
344
+ (`skill_md.render_choose_method`) tells the AI to use the first its browser tool
345
+ supports: the JavaScript call, then `references/locators.md`
346
+ (`generate/locators.py`: role, name, CSS and a Playwright-style action per field,
347
+ grouped by view), then browser actions by label (`skill_md.render_browser_actions`,
348
+ listing only the kinds of control the page has), then `references/manual-steps.md`
349
+ (`generate/manual_steps.py`: numbered steps by labels and sections). The methods are
350
+ named by ability, never by one product's tool names; locators are data, never code.
351
+ Tests: `test_locators.py`, `test_manual_steps.py`, `test_skill_md.py`, and eval 4
352
+ ("this browser cannot run JavaScript") in `test_evals.py`.
353
+ - Shipped data is read at the edge: `assets.read_fill_engine` and
354
+ `assets.read_index_template`, never inside `generate/`, which stays pure.
355
+ - The layout check is optional and only exists with a probe. `probe/extract.js`
356
+ measures each control's box from its final selector (the way the skill reads it back)
357
+ at `runner.CAPTURE_VIEWPORT`; `compile/layout.py` keeps a box only when the probe's
358
+ selector equals the field's; `inline_setter.choose_landmarks` picks up to eight, one
359
+ per row; `build_geometry` writes them relative to the first. Tests:
360
+ `tests/test_layout.py`, the layout cases in `tests/test_inline_setter.py`,
361
+ `tests/test_serialize.py` and `tests/test_skill_md.py`. The JavaScript itself was
362
+ checked live against the bench in each state (`match`, `resized`, `blocked`,
363
+ `changed`); see `docs/decisions.md` for how, and rerun those cases after changing it.
364
+ - `SKILL.md` passes the [Agent Skills spec](https://agentskills.io/specification):
365
+ the name is lowercase with single hyphens, at most 64 characters, and matches the
366
+ folder; the description is quoted in YAML and under 300 characters; the body is
367
+ under 500 lines.
368
+ - Text between `skillgendev:user:start` and `skillgendev:user:end` survives regeneration.
369
+
370
+ ## Adding framework support (hint packs)
371
+
372
+ You do not need to touch Python to teach skillgendev a new router or validation
373
+ library. Add a YAML file:
374
+
375
+ - `hintpacks/routers/<name>.yml`: an ast-grep rule that matches route declarations,
376
+ plus which metavariables hold the path and the component.
377
+ - `hintpacks/folders/<name>.yml`: a folder-routing convention (which folder, which
378
+ file names are pages, how parameters are written, and with `requires_any` which
379
+ files mark a project root, so apps nested in a monorepo are found:
380
+ `routes/folders.py` `find_project_roots`).
381
+ - `hintpacks/validators/<name>.yml`: an ast-grep rule that finds field constraints.
382
+
383
+ A route the developer gives as one concrete page (`/users/7`) is linked to the
384
+ pattern found in the code (`/users/:id`) by `routes/paths.py` `match_route` and
385
+ `routes/merge.py` `find_pattern_files`: the pattern with the most fixed segments wins,
386
+ and a tie links nothing. `pipeline.ensure_route` also fills the code files of a route
387
+ already saved in `routes.json` without any, so its slice is never empty by accident.
388
+ Tests: `tests/test_folders.py`, `tests/test_paths.py`, `tests/test_route_merge.py`,
389
+ `tests/test_pipeline.py`.
390
+
391
+ ast-grep never searches dependency or build folders (`routes.folders.SKIPPED_FOLDERS`,
392
+ passed as `--globs` and filtered again in `astgrep.is_in_skipped_folder`), and a route
393
+ that is really a Windows path (Git Bash rewrites `/forms/x`) is refused by
394
+ `routes.paths.check_route_text`. When a component declares no symbol of its own name (a
395
+ `.vue` file), `graph.slice.find_imported_file_named` links it by file name.
396
+
397
+ Each hint pack needs a matching tiny example under `tests/fixtures/hintpacks/` and a
398
+ test. See `hintpacks/README.md`.
399
+
400
+ ## Tests
401
+
402
+ - Every module has a test file. No network in tests.
403
+ - Browser tests (`-m browser`) only talk to a local server the test starts.
404
+ - Each example app in `tests/fixtures/` uses a different stack. A new stack is a
405
+ welcome contribution: add the app, its recorded analysis, and its expected skills.
406
+ - Golden files: regenerate with `uv run pytest --update-golden` and review the diff.
407
+
408
+ ## Testing in Claude and Cowork
409
+
410
+ `tests/bench/` is a one-page test bench: a form with every kind of widget skillgendev
411
+ supports (text, selects, radios, checkboxes, searchable dropdowns, dependent options,
412
+ conditional fields, a tree picker, chips, rich text, repeatable rows, file upload,
413
+ server-side errors), a tiny local server, the skill skillgendev generates for it, and a
414
+ list of test cases with expected results.
415
+
416
+ ```bash
417
+ python3 tests/bench/serve.py # http://localhost:8765, stdlib only
418
+ ```
419
+
420
+ Then follow [`tests/bench/README.md`](tests/bench/README.md): install the bench skill
421
+ into Claude or Cowork, paste a test case's prompt, and run
422
+ `python3 tests/bench/check.py <case-id>` to compare what was submitted with what
423
+ was expected. Please include the results table in your pull request when you change
424
+ generated output.
425
+
426
+ ## Commits and pull requests
427
+
428
+ - [Conventional Commits](https://www.conventionalcommits.org/): `feat:`, `fix:`,
429
+ `docs:`, `test:`, `refactor:`, `chore:`.
430
+ - Small commits, one change each.
431
+ - Never commit `.skillgendev/secrets/`, `.env` files or Playwright storage states.
432
+ - Record design decisions in `docs/decisions.md`.
433
+ - If something in the spec is unclear, open an issue and ask before building.
434
+
435
+ ## Licence
436
+
437
+ By contributing you agree that your contribution is licensed under the Apache
438
+ License 2.0 (see `LICENSE`).