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.
- skillgendev-0.1.0/.gitignore +48 -0
- skillgendev-0.1.0/CONTRIBUTING.md +438 -0
- skillgendev-0.1.0/LICENSE +201 -0
- skillgendev-0.1.0/PKG-INFO +796 -0
- skillgendev-0.1.0/README.md +766 -0
- skillgendev-0.1.0/claude/agents/route-analyzer.md +26 -0
- skillgendev-0.1.0/claude/commands/command.md +23 -0
- skillgendev-0.1.0/claude/skills/driver/SKILL.md +76 -0
- skillgendev-0.1.0/docs/decisions.md +1044 -0
- skillgendev-0.1.0/docs/spec.md +639 -0
- skillgendev-0.1.0/hintpacks/README.md +85 -0
- skillgendev-0.1.0/hintpacks/folders/astro-pages.yml +13 -0
- skillgendev-0.1.0/hintpacks/folders/nextjs-app.yml +13 -0
- skillgendev-0.1.0/hintpacks/folders/nextjs-pages.yml +13 -0
- skillgendev-0.1.0/hintpacks/folders/nuxt-pages.yml +13 -0
- skillgendev-0.1.0/hintpacks/folders/sveltekit-routes.yml +13 -0
- skillgendev-0.1.0/hintpacks/routers/aspnet-attributes.yml +16 -0
- skillgendev-0.1.0/hintpacks/routers/django.yml +12 -0
- skillgendev-0.1.0/hintpacks/routers/express.yml +9 -0
- skillgendev-0.1.0/hintpacks/routers/flask.yml +21 -0
- skillgendev-0.1.0/hintpacks/routers/laravel.yml +12 -0
- skillgendev-0.1.0/hintpacks/routers/phoenix.yml +13 -0
- skillgendev-0.1.0/hintpacks/routers/rails.yml +14 -0
- skillgendev-0.1.0/hintpacks/routers/react-router-jsx.yml +13 -0
- skillgendev-0.1.0/hintpacks/routers/route-objects-js.yml +21 -0
- skillgendev-0.1.0/hintpacks/routers/route-objects.yml +21 -0
- skillgendev-0.1.0/hintpacks/routers/spring.yml +13 -0
- skillgendev-0.1.0/pyproject.toml +160 -0
- skillgendev-0.1.0/scripts/install-local.ps1 +192 -0
- skillgendev-0.1.0/scripts/install-local.sh +73 -0
- skillgendev-0.1.0/src/skillgen/__init__.py +7 -0
- skillgendev-0.1.0/src/skillgen/analyze/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/analyze/prompt.py +189 -0
- skillgendev-0.1.0/src/skillgen/analyze/runner.py +457 -0
- skillgendev-0.1.0/src/skillgen/assets.py +69 -0
- skillgendev-0.1.0/src/skillgen/cli.py +1678 -0
- skillgendev-0.1.0/src/skillgen/compile/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/compile/assess.py +175 -0
- skillgendev-0.1.0/src/skillgen/compile/fields.py +320 -0
- skillgendev-0.1.0/src/skillgen/compile/layout.py +87 -0
- skillgendev-0.1.0/src/skillgen/compile/merge.py +560 -0
- skillgendev-0.1.0/src/skillgen/compile/unopened.py +114 -0
- skillgendev-0.1.0/src/skillgen/config.py +165 -0
- skillgendev-0.1.0/src/skillgen/drift/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/drift/check.py +113 -0
- skillgendev-0.1.0/src/skillgen/drift/diff.py +120 -0
- skillgendev-0.1.0/src/skillgen/errors.py +114 -0
- skillgendev-0.1.0/src/skillgen/export.py +417 -0
- skillgendev-0.1.0/src/skillgen/generate/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/generate/bundle.py +251 -0
- skillgendev-0.1.0/src/skillgen/generate/evals.py +144 -0
- skillgendev-0.1.0/src/skillgen/generate/examples.py +167 -0
- skillgendev-0.1.0/src/skillgen/generate/fill_engine.js +202 -0
- skillgendev-0.1.0/src/skillgen/generate/fill_script.py +253 -0
- skillgendev-0.1.0/src/skillgen/generate/folder.py +115 -0
- skillgendev-0.1.0/src/skillgen/generate/index_page.py +218 -0
- skillgendev-0.1.0/src/skillgen/generate/index_template.html +86 -0
- skillgendev-0.1.0/src/skillgen/generate/inline_setter.py +725 -0
- skillgendev-0.1.0/src/skillgen/generate/layout_reference.py +72 -0
- skillgendev-0.1.0/src/skillgen/generate/locators.py +270 -0
- skillgendev-0.1.0/src/skillgen/generate/manual_steps.py +165 -0
- skillgendev-0.1.0/src/skillgen/generate/skill_md.py +872 -0
- skillgendev-0.1.0/src/skillgen/generate/user_rules.py +129 -0
- skillgendev-0.1.0/src/skillgen/graph/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/graph/codegraph.py +527 -0
- skillgendev-0.1.0/src/skillgen/graph/connections.py +335 -0
- skillgendev-0.1.0/src/skillgen/graph/hubs.py +84 -0
- skillgendev-0.1.0/src/skillgen/graph/slice.py +241 -0
- skillgendev-0.1.0/src/skillgen/handoff.py +217 -0
- skillgendev-0.1.0/src/skillgen/logs.py +121 -0
- skillgendev-0.1.0/src/skillgen/model/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/model/confidence.py +83 -0
- skillgendev-0.1.0/src/skillgen/model/facts.py +66 -0
- skillgendev-0.1.0/src/skillgen/model/page.py +489 -0
- skillgendev-0.1.0/src/skillgen/model/probe.py +117 -0
- skillgendev-0.1.0/src/skillgen/model/route.py +183 -0
- skillgendev-0.1.0/src/skillgen/model/serialize.py +592 -0
- skillgendev-0.1.0/src/skillgen/pipeline.py +1551 -0
- skillgendev-0.1.0/src/skillgen/probe/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/probe/capture.py +153 -0
- skillgendev-0.1.0/src/skillgen/probe/classify.py +136 -0
- skillgendev-0.1.0/src/skillgen/probe/extract.js +164 -0
- skillgendev-0.1.0/src/skillgen/probe/journal.py +281 -0
- skillgendev-0.1.0/src/skillgen/probe/measure.js +24 -0
- skillgendev-0.1.0/src/skillgen/probe/runner.py +1066 -0
- skillgendev-0.1.0/src/skillgen/py.typed +0 -0
- skillgendev-0.1.0/src/skillgen/routes/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/routes/astgrep.py +415 -0
- skillgendev-0.1.0/src/skillgen/routes/folders.py +131 -0
- skillgendev-0.1.0/src/skillgen/routes/hintpacks.py +227 -0
- skillgendev-0.1.0/src/skillgen/routes/merge.py +208 -0
- skillgendev-0.1.0/src/skillgen/routes/naming.py +202 -0
- skillgendev-0.1.0/src/skillgen/routes/paths.py +150 -0
- skillgendev-0.1.0/src/skillgen/schema/__init__.py +97 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/add-rule.schema.json +49 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/answer.schema.json +43 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/check.schema.json +61 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/doctor.schema.json +65 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/export-chatgpt.schema.json +52 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/export.schema.json +53 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/generate.schema.json +169 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/index.schema.json +48 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/init.schema.json +49 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/install.schema.json +47 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/login.schema.json +40 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/logs.schema.json +49 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/prepare.schema.json +68 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/probe.schema.json +63 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/questions.schema.json +74 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/routes.schema.json +85 -0
- skillgendev-0.1.0/src/skillgen/schema/cli/validate.schema.json +66 -0
- skillgendev-0.1.0/src/skillgen/schema/page-analysis.schema.json +523 -0
- skillgendev-0.1.0/src/skillgen/schema/page.schema.json +561 -0
- skillgendev-0.1.0/src/skillgen/schema/route-listing.schema.json +22 -0
- skillgendev-0.1.0/src/skillgen/validate/__init__.py +1 -0
- skillgendev-0.1.0/src/skillgen/validate/model_checks.py +127 -0
- skillgendev-0.1.0/src/skillgen/validate/scan.py +119 -0
- skillgendev-0.1.0/src/skillgen/validate/spec.py +191 -0
- skillgendev-0.1.0/src/skillgen/workspace.py +301 -0
- skillgendev-0.1.0/tests/__init__.py +0 -0
- skillgendev-0.1.0/tests/bench/README.md +154 -0
- skillgendev-0.1.0/tests/bench/analysis.json +501 -0
- skillgendev-0.1.0/tests/bench/cases.json +295 -0
- skillgendev-0.1.0/tests/bench/check.py +163 -0
- skillgendev-0.1.0/tests/bench/files/sample-id.pdf +32 -0
- skillgendev-0.1.0/tests/bench/page.json +696 -0
- skillgendev-0.1.0/tests/bench/probe.json +604 -0
- skillgendev-0.1.0/tests/bench/serve.py +206 -0
- skillgendev-0.1.0/tests/bench/site/bench.css +47 -0
- skillgendev-0.1.0/tests/bench/site/bench.js +287 -0
- skillgendev-0.1.0/tests/bench/site/index.html +186 -0
- skillgendev-0.1.0/tests/bench/site/wizard.html +96 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/SKILL.md +95 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/references/fill.md +184 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/references/layout.json +203 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/references/locators.md +35 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/references/manual-steps.md +40 -0
- skillgendev-0.1.0/tests/bench/skills/bench-apply/scripts/fill.js +231 -0
- skillgendev-0.1.0/tests/bench/wizard-analysis.json +126 -0
- skillgendev-0.1.0/tests/conftest.py +55 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/feedback.json +44 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/home.json +1 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/analyses/permits-renew.json +26 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/app.py +34 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/generate.json +17 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/SKILL.md +67 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/locators.md +17 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/references/manual-steps.md +16 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-feedback/scripts/fill.js +216 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/SKILL.md +54 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/locators.md +14 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/references/manual-steps.md +10 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/expected/skills/fixture-council-permits-renew/scripts/fill.js +213 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/forms.py +23 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/requirements.txt +1 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/feedback.html +22 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/index.html +1 -0
- skillgendev-0.1.0/tests/fixtures/flask-jinja/templates/renew.html +14 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Controllers/SignupController.cs +8 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Models/SignupModel.cs +2 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/aspnet/Pages/Contact.cshtml +3 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/app/Http/Controllers/KycController.php +3 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/resources/views/kyc.blade.php +1 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/laravel/routes/web.php +3 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/controllers/signup_controller.ex +5 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/live/contact_live.ex +4 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/phoenix/lib/app_web/router.ex +7 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/controllers/projects_controller.rb +8 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/controllers/registrations_controller.rb +8 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/models/user.rb +3 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/views/projects/edit.html.erb +1 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/app/views/registrations/new.html.erb +1 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/rails/config/routes.rb +5 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/SignupController.java +10 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/SignupForm.java +3 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/java/app/UserService.java +2 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/spring/src/main/resources/templates/signup.html +1 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/package.json +1 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/src/router/index.js +8 -0
- skillgendev-0.1.0/tests/fixtures/hintpacks/vue-router-js/src/views/SignupView.vue +3 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/contact.json +40 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/home.json +1 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/orders-return.json +32 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/analyses/register.json +34 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/app/(account)/register/page.tsx +20 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/app/contact/page.tsx +29 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/app/orders/[orderId]/return/page.tsx +29 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/app/page.tsx +3 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/components/CountryCombobox.tsx +24 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/components/Field.tsx +10 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/generate.json +22 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/SKILL.md +63 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/locators.md +16 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/references/manual-steps.md +15 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-contact/scripts/fill.js +215 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/SKILL.md +57 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/fill.md +123 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/locators.md +18 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/references/manual-steps.md +13 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-orders-return/scripts/fill.js +214 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/SKILL.md +55 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/locators.md +15 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/references/manual-steps.md +11 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/expected/skills/fixture-store-register/scripts/fill.js +214 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/lib/schemas.ts +22 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/next.config.js +1 -0
- skillgendev-0.1.0/tests/fixtures/nextjs-app/package.json +6 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/about.json +1 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/projects-edit.json +26 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/settings-profile.json +37 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/analyses/signup.json +47 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/generate.json +22 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/SKILL.md +54 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/locators.md +14 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/references/manual-steps.md +10 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-projects-edit/scripts/fill.js +213 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/SKILL.md +55 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/fill.md +121 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/locators.md +15 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/references/manual-steps.md +11 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-settings-profile/scripts/fill.js +214 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/SKILL.md +65 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/evals/evals.json +53 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/fill.md +137 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/locators.md +17 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/references/manual-steps.md +16 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/expected/skills/fixture-shop-signup/scripts/fill.js +216 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/package.json +6 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/App.tsx +18 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/api/client.ts +9 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/components/SelectField.tsx +13 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/components/TextField.tsx +11 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/About.tsx +3 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/ProfileSettings.tsx +22 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/ProjectEdit.tsx +19 -0
- skillgendev-0.1.0/tests/fixtures/react-router-spa/src/pages/Signup.tsx +30 -0
- skillgendev-0.1.0/tests/helpers.py +70 -0
- skillgendev-0.1.0/tests/test_analyze_runner.py +190 -0
- skillgendev-0.1.0/tests/test_assess.py +68 -0
- skillgendev-0.1.0/tests/test_assets.py +14 -0
- skillgendev-0.1.0/tests/test_astgrep.py +207 -0
- skillgendev-0.1.0/tests/test_bench.py +174 -0
- skillgendev-0.1.0/tests/test_boundaries.py +140 -0
- skillgendev-0.1.0/tests/test_bundle.py +125 -0
- skillgendev-0.1.0/tests/test_capture.py +15 -0
- skillgendev-0.1.0/tests/test_check.py +70 -0
- skillgendev-0.1.0/tests/test_classify.py +87 -0
- skillgendev-0.1.0/tests/test_cli.py +872 -0
- skillgendev-0.1.0/tests/test_codegraph.py +186 -0
- skillgendev-0.1.0/tests/test_confidence.py +42 -0
- skillgendev-0.1.0/tests/test_config.py +48 -0
- skillgendev-0.1.0/tests/test_connections.py +213 -0
- skillgendev-0.1.0/tests/test_diff.py +50 -0
- skillgendev-0.1.0/tests/test_docs.py +58 -0
- skillgendev-0.1.0/tests/test_errors.py +15 -0
- skillgendev-0.1.0/tests/test_evals.py +28 -0
- skillgendev-0.1.0/tests/test_examples.py +48 -0
- skillgendev-0.1.0/tests/test_export.py +288 -0
- skillgendev-0.1.0/tests/test_facts.py +15 -0
- skillgendev-0.1.0/tests/test_fields.py +54 -0
- skillgendev-0.1.0/tests/test_fill_script.py +124 -0
- skillgendev-0.1.0/tests/test_fixtures.py +225 -0
- skillgendev-0.1.0/tests/test_folder.py +57 -0
- skillgendev-0.1.0/tests/test_folders.py +109 -0
- skillgendev-0.1.0/tests/test_handoff.py +111 -0
- skillgendev-0.1.0/tests/test_hintpacks.py +107 -0
- skillgendev-0.1.0/tests/test_hubs.py +40 -0
- skillgendev-0.1.0/tests/test_index_page.py +71 -0
- skillgendev-0.1.0/tests/test_inline_setter.py +469 -0
- skillgendev-0.1.0/tests/test_inventory.py +138 -0
- skillgendev-0.1.0/tests/test_journal.py +133 -0
- skillgendev-0.1.0/tests/test_layout.py +96 -0
- skillgendev-0.1.0/tests/test_layout_reference.py +50 -0
- skillgendev-0.1.0/tests/test_locators.py +81 -0
- skillgendev-0.1.0/tests/test_logs.py +72 -0
- skillgendev-0.1.0/tests/test_manual_steps.py +56 -0
- skillgendev-0.1.0/tests/test_merge.py +291 -0
- skillgendev-0.1.0/tests/test_model_checks.py +69 -0
- skillgendev-0.1.0/tests/test_model_probe.py +12 -0
- skillgendev-0.1.0/tests/test_naming.py +101 -0
- skillgendev-0.1.0/tests/test_page.py +39 -0
- skillgendev-0.1.0/tests/test_paths.py +71 -0
- skillgendev-0.1.0/tests/test_pipeline.py +416 -0
- skillgendev-0.1.0/tests/test_probe_runner.py +207 -0
- skillgendev-0.1.0/tests/test_prompt.py +92 -0
- skillgendev-0.1.0/tests/test_route.py +27 -0
- skillgendev-0.1.0/tests/test_route_merge.py +135 -0
- skillgendev-0.1.0/tests/test_scan.py +54 -0
- skillgendev-0.1.0/tests/test_schema.py +24 -0
- skillgendev-0.1.0/tests/test_serialize.py +123 -0
- skillgendev-0.1.0/tests/test_skill_md.py +215 -0
- skillgendev-0.1.0/tests/test_slice.py +65 -0
- skillgendev-0.1.0/tests/test_spec.py +83 -0
- skillgendev-0.1.0/tests/test_unopened.py +124 -0
- skillgendev-0.1.0/tests/test_user_rules.py +67 -0
- 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`).
|