voidmachine 0.0.0 → 4.0.0
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.
- package/LICENSE +21 -0
- package/README.md +123 -4
- package/bin/void-harness.mjs +12 -0
- package/bin/void-machine.mjs +12 -0
- package/core-assets/.claude-plugin/plugin.json +253 -0
- package/core-assets/.gitkeep +0 -0
- package/core-assets/PHILOSOPHY.md +218 -0
- package/core-assets/PROJECT-DOCTRINE.template.md +14 -0
- package/core-assets/adapters/security/manifest.yaml +96 -0
- package/core-assets/agents/.gitkeep +0 -0
- package/core-assets/agents/accessibility-specialist.md +59 -0
- package/core-assets/agents/api-integration-engineer.md +59 -0
- package/core-assets/agents/code-explorer.md +94 -0
- package/core-assets/agents/code-explorer.source +7 -0
- package/core-assets/agents/data-migration-engineer.md +59 -0
- package/core-assets/agents/devex-docs-engineer.md +57 -0
- package/core-assets/agents/doctrine-critic.md +91 -0
- package/core-assets/agents/doctrine-critic.source +11 -0
- package/core-assets/agents/domain-architect.md +56 -0
- package/core-assets/agents/experience-designer.md +59 -0
- package/core-assets/agents/frontend-engineer.md +60 -0
- package/core-assets/agents/independent-code-reviewer.md +61 -0
- package/core-assets/agents/migration-planner.md +97 -0
- package/core-assets/agents/migration-planner.source +7 -0
- package/core-assets/agents/observability-sre-engineer.md +59 -0
- package/core-assets/agents/pdf-specialist.md +58 -0
- package/core-assets/agents/performance-engineer.md +58 -0
- package/core-assets/agents/product-challenger.md +55 -0
- package/core-assets/agents/security-engineer.md +66 -0
- package/core-assets/agents/silent-failure-hunter.md +94 -0
- package/core-assets/agents/silent-failure-hunter.source +8 -0
- package/core-assets/agents/solution-architect.md +57 -0
- package/core-assets/agents/test-qa-engineer.md +60 -0
- package/core-assets/agents/type-design-analyzer.md +93 -0
- package/core-assets/agents/type-design-analyzer.source +8 -0
- package/core-assets/agents/visual-craft-director.md +62 -0
- package/core-assets/codex/hooks.json +157 -0
- package/core-assets/contract-history/visual-craft-director/v2.yaml +43 -0
- package/core-assets/data/catalog.v3.json +10479 -0
- package/core-assets/data/certification.json +1800 -0
- package/core-assets/data/identity.json +8 -0
- package/core-assets/data/model.json +4226 -0
- package/core-assets/enforce/ci-enforce.sh +183 -0
- package/core-assets/hooks/_checks.sh +145 -0
- package/core-assets/hooks/_hooklib.sh +163 -0
- package/core-assets/hooks/_syntax-worker.cjs +503 -0
- package/core-assets/hooks/_void-hook.mjs +5459 -0
- package/core-assets/hooks/activation-meter.sh +13 -0
- package/core-assets/hooks/anti-ai-slop-grep.sh +38 -0
- package/core-assets/hooks/auto-format.sh +9 -0
- package/core-assets/hooks/axe-precommit.sh +35 -0
- package/core-assets/hooks/block-dangerous-bash.sh +10 -0
- package/core-assets/hooks/block-protected-push.sh +81 -0
- package/core-assets/hooks/boundary-direction-check.sh +10 -0
- package/core-assets/hooks/control-character-grep.sh +10 -0
- package/core-assets/hooks/fowler-refactors.txt +71 -0
- package/core-assets/hooks/large-cl-grep.sh +9 -0
- package/core-assets/hooks/llm-cost-precommit.sh +55 -0
- package/core-assets/hooks/migration-lint.sh +58 -0
- package/core-assets/hooks/migration-pr-template.sh +55 -0
- package/core-assets/hooks/no-ai-design-slop.sh +10 -0
- package/core-assets/hooks/no-any-grep.sh +10 -0
- package/core-assets/hooks/no-as-cast-grep.sh +10 -0
- package/core-assets/hooks/no-console-log-grep.sh +10 -0
- package/core-assets/hooks/no-null-grep.sh +10 -0
- package/core-assets/hooks/no-only-no-skip.sh +10 -0
- package/core-assets/hooks/outcome-meter.sh +13 -0
- package/core-assets/hooks/pre-PR-review-evidence.sh +47 -0
- package/core-assets/hooks/protect-sensitive-files.sh +10 -0
- package/core-assets/hooks/refactor-named-grep.sh +49 -0
- package/core-assets/hooks/secret-in-content.sh +10 -0
- package/core-assets/hooks/sessionstart-context.sh +9 -0
- package/core-assets/hooks/stop-typecheck.sh +9 -0
- package/core-assets/hooks/tdd-guard.md +90 -0
- package/core-assets/hooks/tdd-guard.sh +10 -0
- package/core-assets/hooks/test-name-lint.sh +10 -0
- package/core-assets/hooks/tidying-commit-prefix.sh +37 -0
- package/core-assets/hooks/trim-large-output.sh +9 -0
- package/core-assets/hooks/tsc-noemit-precommit.sh +22 -0
- package/core-assets/hooks/ubiquitous-language-lint.sh +58 -0
- package/core-assets/hooks/viewport-screenshot-gate.sh +48 -0
- package/core-assets/modules/.gitkeep +0 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/SKILL.md +164 -0
- package/core-assets/packs/pack-mobile/skills/void-eas-build-profile/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/SKILL.md +178 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-config-plugins/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/.source +8 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/SKILL.md +209 -0
- package/core-assets/packs/pack-mobile/skills/void-expo-router-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/.source +7 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/SKILL.md +194 -0
- package/core-assets/packs/pack-mobile/skills/void-ota-update-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/SKILL.md +160 -0
- package/core-assets/packs/pack-monorepo/skills/void-dependency-direction/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/SKILL.md +70 -0
- package/core-assets/packs/pack-monorepo/skills/void-package-extraction/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/.source +5 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/SKILL.md +122 -0
- package/core-assets/packs/pack-monorepo/skills/void-service-package/harness.yaml +14 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/.source +7 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/SKILL.md +116 -0
- package/core-assets/packs/pack-monorepo/skills/void-turbo-pipeline-tuning/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/SKILL.md +162 -0
- package/core-assets/packs/pack-nextjs/skills/void-cache-component-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/SKILL.md +161 -0
- package/core-assets/packs/pack-nextjs/skills/void-instrumentation-setup/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/SKILL.md +176 -0
- package/core-assets/packs/pack-nextjs/skills/void-loading-error-boundaries/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/SKILL.md +129 -0
- package/core-assets/packs/pack-nextjs/skills/void-parallel-routes-slots/harness.yaml +14 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/.source +5 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/SKILL.md +107 -0
- package/core-assets/packs/pack-nextjs/skills/void-route-group-decision/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/SKILL.md +153 -0
- package/core-assets/packs/pack-pwa/skills/void-install-prompt-ux/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/SKILL.md +204 -0
- package/core-assets/packs/pack-pwa/skills/void-manifest-checklist/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/.source +8 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/SKILL.md +266 -0
- package/core-assets/packs/pack-pwa/skills/void-offline-first-mutation/harness.yaml +14 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/.source +5 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/SKILL.md +178 -0
- package/core-assets/packs/pack-pwa/skills/void-service-worker-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/.source +8 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/SKILL.md +136 -0
- package/core-assets/packs/pack-react/skills/void-accessibility-check/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/.source +5 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/SKILL.md +116 -0
- package/core-assets/packs/pack-react/skills/void-client-vs-server-component/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/.source +7 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/SKILL.md +157 -0
- package/core-assets/packs/pack-react/skills/void-form-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/.source +5 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/SKILL.md +160 -0
- package/core-assets/packs/pack-react/skills/void-state-architecture/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/SKILL.md +214 -0
- package/core-assets/packs/pack-server/skills/void-background-job-pattern/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/SKILL.md +168 -0
- package/core-assets/packs/pack-server/skills/void-drizzle-migration-safe/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/SKILL.md +131 -0
- package/core-assets/packs/pack-server/skills/void-env-validation/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/.source +6 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/SKILL.md +149 -0
- package/core-assets/packs/pack-server/skills/void-rate-limit-strategy/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-server-action/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-server-action/SKILL.md +181 -0
- package/core-assets/packs/pack-server/skills/void-server-action/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/SKILL.md +73 -0
- package/core-assets/packs/pack-server/skills/void-testing-server-modules/harness.yaml +14 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/.source +7 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/SKILL.md +205 -0
- package/core-assets/packs/pack-server/skills/void-webhook-handler-pattern/harness.yaml +14 -0
- package/core-assets/policies/core.yaml +95 -0
- package/core-assets/policies/ui.yaml +23 -0
- package/core-assets/profiles/base.yaml +29 -0
- package/core-assets/profiles/expo-config.yaml +36 -0
- package/core-assets/profiles/expo.yaml +35 -0
- package/core-assets/profiles/monorepo.yaml +33 -0
- package/core-assets/profiles/nextjs-config.yaml +31 -0
- package/core-assets/profiles/nextjs.yaml +31 -0
- package/core-assets/profiles/node-server.yaml +31 -0
- package/core-assets/profiles/pwa.yaml +33 -0
- package/core-assets/profiles/react.yaml +39 -0
- package/core-assets/profiles/sql.yaml +39 -0
- package/core-assets/profiles/typescript.yaml +39 -0
- package/core-assets/skills/.gitkeep +0 -0
- package/core-assets/skills/void-accessibility/.source +15 -0
- package/core-assets/skills/void-accessibility/SKILL.md +275 -0
- package/core-assets/skills/void-accessibility/harness.yaml +17 -0
- package/core-assets/skills/void-api-and-interface-design/.source +13 -0
- package/core-assets/skills/void-api-and-interface-design/SKILL.md +231 -0
- package/core-assets/skills/void-api-and-interface-design/harness.yaml +16 -0
- package/core-assets/skills/void-async-safety/.source +13 -0
- package/core-assets/skills/void-async-safety/SKILL.md +287 -0
- package/core-assets/skills/void-async-safety/harness.yaml +16 -0
- package/core-assets/skills/void-audit/.source +15 -0
- package/core-assets/skills/void-audit/SKILL.md +83 -0
- package/core-assets/skills/void-audit/harness.yaml +14 -0
- package/core-assets/skills/void-autopilot/.source +21 -0
- package/core-assets/skills/void-autopilot/SKILL.md +358 -0
- package/core-assets/skills/void-autopilot/harness.yaml +14 -0
- package/core-assets/skills/void-brainstorm/.source +14 -0
- package/core-assets/skills/void-brainstorm/SKILL.md +271 -0
- package/core-assets/skills/void-brainstorm/harness.yaml +14 -0
- package/core-assets/skills/void-checkpoint/.source +39 -0
- package/core-assets/skills/void-checkpoint/SKILL.md +227 -0
- package/core-assets/skills/void-checkpoint/harness.yaml +14 -0
- package/core-assets/skills/void-claude-md/.source +15 -0
- package/core-assets/skills/void-claude-md/SKILL.md +180 -0
- package/core-assets/skills/void-claude-md/harness.yaml +14 -0
- package/core-assets/skills/void-code-review/.source +14 -0
- package/core-assets/skills/void-code-review/SKILL.md +232 -0
- package/core-assets/skills/void-code-review/harness.yaml +15 -0
- package/core-assets/skills/void-commit-discipline/.source +9 -0
- package/core-assets/skills/void-commit-discipline/SKILL.md +211 -0
- package/core-assets/skills/void-commit-discipline/harness.yaml +15 -0
- package/core-assets/skills/void-context/.source +15 -0
- package/core-assets/skills/void-context/SKILL.md +167 -0
- package/core-assets/skills/void-context/harness.yaml +14 -0
- package/core-assets/skills/void-debug/.source +11 -0
- package/core-assets/skills/void-debug/SKILL.md +211 -0
- package/core-assets/skills/void-debug/harness.yaml +14 -0
- package/core-assets/skills/void-decide/.source +9 -0
- package/core-assets/skills/void-decide/SKILL.md +147 -0
- package/core-assets/skills/void-decide/harness.yaml +14 -0
- package/core-assets/skills/void-devex-audit/.source +8 -0
- package/core-assets/skills/void-devex-audit/SKILL.md +113 -0
- package/core-assets/skills/void-devex-audit/harness.yaml +15 -0
- package/core-assets/skills/void-doctor/.source +15 -0
- package/core-assets/skills/void-doctor/SKILL.md +83 -0
- package/core-assets/skills/void-doctor/harness.yaml +14 -0
- package/core-assets/skills/void-domain-driven-design/.source +13 -0
- package/core-assets/skills/void-domain-driven-design/SKILL.md +292 -0
- package/core-assets/skills/void-domain-driven-design/harness.yaml +15 -0
- package/core-assets/skills/void-frontend-design/.source +19 -0
- package/core-assets/skills/void-frontend-design/SKILL.md +274 -0
- package/core-assets/skills/void-frontend-design/harness.yaml +17 -0
- package/core-assets/skills/void-functional/.source +14 -0
- package/core-assets/skills/void-functional/SKILL.md +312 -0
- package/core-assets/skills/void-functional/harness.yaml +15 -0
- package/core-assets/skills/void-graph/.source +15 -0
- package/core-assets/skills/void-graph/SKILL.md +80 -0
- package/core-assets/skills/void-graph/harness.yaml +14 -0
- package/core-assets/skills/void-hexagonal-architecture/.source +12 -0
- package/core-assets/skills/void-hexagonal-architecture/SKILL.md +276 -0
- package/core-assets/skills/void-hexagonal-architecture/harness.yaml +15 -0
- package/core-assets/skills/void-implement/.source +39 -0
- package/core-assets/skills/void-implement/SKILL.md +183 -0
- package/core-assets/skills/void-implement/harness.yaml +14 -0
- package/core-assets/skills/void-learn/.source +13 -0
- package/core-assets/skills/void-learn/SKILL.md +226 -0
- package/core-assets/skills/void-learn/harness.yaml +14 -0
- package/core-assets/skills/void-llm-cost-discipline/.source +13 -0
- package/core-assets/skills/void-llm-cost-discipline/SKILL.md +299 -0
- package/core-assets/skills/void-llm-cost-discipline/harness.yaml +15 -0
- package/core-assets/skills/void-make-pdf/.source +9 -0
- package/core-assets/skills/void-make-pdf/SKILL.md +52 -0
- package/core-assets/skills/void-make-pdf/harness.yaml +15 -0
- package/core-assets/skills/void-merge/.source +8 -0
- package/core-assets/skills/void-merge/SKILL.md +111 -0
- package/core-assets/skills/void-merge/harness.yaml +14 -0
- package/core-assets/skills/void-migrations/.source +13 -0
- package/core-assets/skills/void-migrations/SKILL.md +264 -0
- package/core-assets/skills/void-migrations/harness.yaml +17 -0
- package/core-assets/skills/void-observability/.source +12 -0
- package/core-assets/skills/void-observability/SKILL.md +245 -0
- package/core-assets/skills/void-observability/harness.yaml +15 -0
- package/core-assets/skills/void-plan/.source +15 -0
- package/core-assets/skills/void-plan/SKILL.md +239 -0
- package/core-assets/skills/void-plan/harness.yaml +14 -0
- package/core-assets/skills/void-plan-review/.source +12 -0
- package/core-assets/skills/void-plan-review/SKILL.md +128 -0
- package/core-assets/skills/void-plan-review/harness.yaml +15 -0
- package/core-assets/skills/void-qa/.source +15 -0
- package/core-assets/skills/void-qa/SKILL.md +103 -0
- package/core-assets/skills/void-qa/harness.yaml +15 -0
- package/core-assets/skills/void-refactor/.source +9 -0
- package/core-assets/skills/void-refactor/SKILL.md +220 -0
- package/core-assets/skills/void-refactor/harness.yaml +15 -0
- package/core-assets/skills/void-retrospective/.source +9 -0
- package/core-assets/skills/void-retrospective/SKILL.md +71 -0
- package/core-assets/skills/void-retrospective/harness.yaml +15 -0
- package/core-assets/skills/void-security-audit/.source +12 -0
- package/core-assets/skills/void-security-audit/SKILL.md +138 -0
- package/core-assets/skills/void-security-audit/harness.yaml +15 -0
- package/core-assets/skills/void-security-guidance/.source +12 -0
- package/core-assets/skills/void-security-guidance/SKILL.md +255 -0
- package/core-assets/skills/void-security-guidance/harness.yaml +15 -0
- package/core-assets/skills/void-source-driven-development/.source +12 -0
- package/core-assets/skills/void-source-driven-development/SKILL.md +158 -0
- package/core-assets/skills/void-source-driven-development/harness.yaml +15 -0
- package/core-assets/skills/void-tdd/.source +15 -0
- package/core-assets/skills/void-tdd/SKILL.md +394 -0
- package/core-assets/skills/void-tdd/harness.yaml +15 -0
- package/core-assets/skills/void-testing/.source +19 -0
- package/core-assets/skills/void-testing/SKILL.md +301 -0
- package/core-assets/skills/void-testing/harness.yaml +17 -0
- package/core-assets/skills/void-ticket/.source +18 -0
- package/core-assets/skills/void-ticket/SKILL.md +239 -0
- package/core-assets/skills/void-ticket/harness.yaml +14 -0
- package/core-assets/skills/void-typescript-strict/.source +10 -0
- package/core-assets/skills/void-typescript-strict/SKILL.md +294 -0
- package/core-assets/skills/void-typescript-strict/harness.yaml +17 -0
- package/core-assets/skills/void-ui-review/.source +12 -0
- package/core-assets/skills/void-ui-review/SKILL.md +97 -0
- package/core-assets/skills/void-ui-review/harness.yaml +15 -0
- package/core-assets/skills/void-verify/.source +12 -0
- package/core-assets/skills/void-verify/SKILL.md +184 -0
- package/core-assets/skills/void-verify/harness.yaml +15 -0
- package/core-assets/specialists/accessibility-specialist.source +11 -0
- package/core-assets/specialists/accessibility-specialist.yaml +30 -0
- package/core-assets/specialists/api-integration-engineer.source +11 -0
- package/core-assets/specialists/api-integration-engineer.yaml +30 -0
- package/core-assets/specialists/data-migration-engineer.source +11 -0
- package/core-assets/specialists/data-migration-engineer.yaml +30 -0
- package/core-assets/specialists/devex-docs-engineer.source +11 -0
- package/core-assets/specialists/devex-docs-engineer.yaml +29 -0
- package/core-assets/specialists/domain-architect.source +11 -0
- package/core-assets/specialists/domain-architect.yaml +28 -0
- package/core-assets/specialists/experience-designer.source +15 -0
- package/core-assets/specialists/experience-designer.yaml +38 -0
- package/core-assets/specialists/frontend-engineer.source +11 -0
- package/core-assets/specialists/frontend-engineer.yaml +32 -0
- package/core-assets/specialists/independent-code-reviewer.source +10 -0
- package/core-assets/specialists/independent-code-reviewer.yaml +37 -0
- package/core-assets/specialists/migrations.json +15 -0
- package/core-assets/specialists/observability-sre-engineer.source +11 -0
- package/core-assets/specialists/observability-sre-engineer.yaml +30 -0
- package/core-assets/specialists/pdf-specialist.source +11 -0
- package/core-assets/specialists/pdf-specialist.yaml +29 -0
- package/core-assets/specialists/performance-engineer.source +11 -0
- package/core-assets/specialists/performance-engineer.yaml +29 -0
- package/core-assets/specialists/product-challenger.source +10 -0
- package/core-assets/specialists/product-challenger.yaml +27 -0
- package/core-assets/specialists/security-engineer.source +13 -0
- package/core-assets/specialists/security-engineer.yaml +41 -0
- package/core-assets/specialists/solution-architect.source +13 -0
- package/core-assets/specialists/solution-architect.yaml +32 -0
- package/core-assets/specialists/test-qa-engineer.source +14 -0
- package/core-assets/specialists/test-qa-engineer.yaml +35 -0
- package/core-assets/specialists/visual-craft-director.source +17 -0
- package/core-assets/specialists/visual-craft-director.yaml +60 -0
- package/core-assets/templates/github/dependency-audit.mjs +54 -0
- package/core-assets/templates/github/void-dependency-audit.yml +35 -0
- package/core-assets/templates/github/void-security.yml +85 -0
- package/dist/main.js +57001 -0
- package/package.json +66 -4
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-parallel-routes-slots
|
|
3
|
+
description: Use Next.js parallel routes (@slot) and intercepting routes ((.)foo) — when they're the right tool, when they're an anti-pattern. The "modal that's also a deep-linkable page" archetype.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# parallel-routes-slots
|
|
7
|
+
|
|
8
|
+
Use when you need to render **multiple independent UI pieces in the same layout that can navigate independently**, or to **show a modal that's also a deep-linkable route**. Parallel routes and intercepting routes are powerful but easy to mis-apply.
|
|
9
|
+
|
|
10
|
+
If you only need a different page at a different URL → use a normal route. No slots needed.
|
|
11
|
+
|
|
12
|
+
## Parallel routes (`@slot`)
|
|
13
|
+
|
|
14
|
+
A `@slot` folder is a **named layout slot** that can be filled by any of its child pages.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
app/dashboard/
|
|
18
|
+
├── @analytics/
|
|
19
|
+
│ ├── page.tsx # default content for the slot
|
|
20
|
+
│ └── audience/page.tsx # different content at the slot
|
|
21
|
+
├── @team/
|
|
22
|
+
│ ├── page.tsx
|
|
23
|
+
│ └── invite/page.tsx
|
|
24
|
+
└── layout.tsx # consumes both slots
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/dashboard/layout.tsx
|
|
29
|
+
export default function Layout({
|
|
30
|
+
children,
|
|
31
|
+
analytics,
|
|
32
|
+
team,
|
|
33
|
+
}: {
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
analytics: React.ReactNode;
|
|
36
|
+
team: React.ReactNode;
|
|
37
|
+
}) {
|
|
38
|
+
return (
|
|
39
|
+
<div className="grid grid-cols-3 gap-4">
|
|
40
|
+
<main className="col-span-2">{children}</main>
|
|
41
|
+
<aside>{analytics}</aside>
|
|
42
|
+
<aside>{team}</aside>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Each slot can independently navigate. The URL `/dashboard/audience/invite` could render `@analytics/audience` AND `@team/invite` AND the main `children` page simultaneously.
|
|
49
|
+
|
|
50
|
+
## When parallel routes WIN
|
|
51
|
+
|
|
52
|
+
- **Dashboard layouts** with widgets that load independently and can be refreshed/navigated without rerouting the main view
|
|
53
|
+
- **Admin panels** with side panels showing different details based on selection
|
|
54
|
+
- **Layouts where streaming partial content matters**: each slot has its own loading.tsx, so the user sees content as each piece arrives
|
|
55
|
+
|
|
56
|
+
## When parallel routes LOSE
|
|
57
|
+
|
|
58
|
+
- A single page with sections → just compose components in the page, no slots needed
|
|
59
|
+
- Conditional rendering based on user role → handle in the page, not via slots
|
|
60
|
+
- Modals (use intercepting routes — different mechanism, below)
|
|
61
|
+
- Mobile-first apps where slot layouts collapse to stack anyway → adds complexity for no gain on mobile
|
|
62
|
+
|
|
63
|
+
## Default content (`page.tsx`) per slot
|
|
64
|
+
|
|
65
|
+
Every slot **must** have a `page.tsx` rendering its default empty state. Without it, you get a 404 when the slot is "empty" but the parent route renders. Common forgetting → cryptic Next errors.
|
|
66
|
+
|
|
67
|
+
## Intercepting routes — the modal pattern
|
|
68
|
+
|
|
69
|
+
`(.)foo`, `(..)foo`, `(..)(..)foo`, `(...)foo` intercept a URL and render a different component **based on how the user arrived**.
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
app/
|
|
73
|
+
├── photos/
|
|
74
|
+
│ ├── [id]/
|
|
75
|
+
│ │ └── page.tsx # full-page photo view
|
|
76
|
+
│ └── page.tsx # photo grid
|
|
77
|
+
└── @modal/
|
|
78
|
+
└── (.)photos/[id]/
|
|
79
|
+
└── page.tsx # modal photo view
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
When the user is on `/photos` and clicks a photo, the URL changes to `/photos/123` but they see a MODAL on top of the grid (the intercepting route fires). If they refresh or share the link, they get the full-page view.
|
|
83
|
+
|
|
84
|
+
This is the **deep-linkable modal** pattern — modal for in-app navigation, full page for direct link.
|
|
85
|
+
|
|
86
|
+
## Intercepting route syntax
|
|
87
|
+
|
|
88
|
+
| Pattern | Means |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `(.)foo` | Intercept `foo` from the same level |
|
|
91
|
+
| `(..)foo` | Intercept `foo` from one level up |
|
|
92
|
+
| `(..)(..)foo` | Intercept from two levels up |
|
|
93
|
+
| `(...)foo` | Intercept from the root |
|
|
94
|
+
|
|
95
|
+
The dots count is **route-segment** counts, not file-path. Easy to miscount.
|
|
96
|
+
|
|
97
|
+
## When intercepting routes WIN
|
|
98
|
+
|
|
99
|
+
- Photo viewer, image gallery (the canonical use case)
|
|
100
|
+
- "Edit" / "Quick view" overlays that should be linkable
|
|
101
|
+
- Onboarding wizards that take over the UI but stay on a route
|
|
102
|
+
|
|
103
|
+
## When intercepting routes LOSE
|
|
104
|
+
|
|
105
|
+
- Truly transient modals (delete confirmation, settings) — use state, not routes
|
|
106
|
+
- Mobile drawers / sheets — usually trigger different UX patterns, intercepting is overkill
|
|
107
|
+
- Modals that aren't shareable — adds URL complexity for no benefit
|
|
108
|
+
|
|
109
|
+
## Anti-patterns
|
|
110
|
+
|
|
111
|
+
- ✗ **Slots without default `page.tsx`** — runtime errors when slot is "empty"
|
|
112
|
+
- ✗ **Nested intercepting routes** — debugging becomes impossible
|
|
113
|
+
- ✗ **Mixing slots with `useState` modal management** in the same surface — pick one
|
|
114
|
+
- ✗ **Slot for what should be a component** — if the content doesn't navigate independently, it's a component, not a slot
|
|
115
|
+
- ✗ **Intercepting routes that don't have a non-intercepting fallback** — refresh shows 404 instead of the full-page view
|
|
116
|
+
|
|
117
|
+
## Workflow
|
|
118
|
+
|
|
119
|
+
1. **Default**: simple route. No slots. No interception.
|
|
120
|
+
2. **Need independent loading + navigation** of UI pieces in same layout? → parallel routes
|
|
121
|
+
3. **Need a modal that's also a deep link** (refresh works, share link works)? → intercepting routes
|
|
122
|
+
4. **Otherwise**: state-managed modal/sheet via `useState` + Radix Dialog.
|
|
123
|
+
|
|
124
|
+
## Composition
|
|
125
|
+
|
|
126
|
+
- `void-route-group-decision` — slots and intercepting routes live within route groups.
|
|
127
|
+
- `void-cache-component-pattern` — each slot has its own cache scope.
|
|
128
|
+
- `void-loading-error-boundaries` — slots can have their own `loading.tsx`, `error.tsx`.
|
|
129
|
+
- `void-state-architecture` — transient modals use `useState`, not routes.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Sources for `void-route-group-decision` skill (see `docs/plans/skill-audits/void-route-group-decision.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- No external source listed in the audit. Original void-machine convention: group route groups (name) by trust boundary, not visual section, behind a 3-condition gate.
|
|
4
|
+
|
|
5
|
+
Strategy: native. Authored from scratch as a void-machine convention; no external source.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-route-group-decision
|
|
3
|
+
description: Decide when to use a (route-group), what to name it, and how to share layouts. The void-machine convention groups by trust boundary, not by visual section.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# route-group-decision
|
|
7
|
+
|
|
8
|
+
Use when adding any new route in `app/`, or when refactor an existing flat `app/` into groups. Route groups (folders named `(name)`) don't affect URL but DO affect layout and convention scope.
|
|
9
|
+
|
|
10
|
+
## The void-machine convention
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
app/
|
|
14
|
+
├── (api|actions)/ # trust boundaries (Server Actions, route handlers)
|
|
15
|
+
│ ├── (actions)/<feature>/
|
|
16
|
+
│ └── (api)/<resource>/route.ts
|
|
17
|
+
├── (marketing)/ # public, statically rendered, no auth
|
|
18
|
+
│ ├── layout.tsx # marketing nav + footer
|
|
19
|
+
│ ├── page.tsx # /
|
|
20
|
+
│ ├── pricing/page.tsx # /pricing
|
|
21
|
+
│ └── blog/[slug]/page.tsx
|
|
22
|
+
├── (app)/ # authenticated app
|
|
23
|
+
│ ├── layout.tsx # auth gate + app shell
|
|
24
|
+
│ ├── dashboard/page.tsx
|
|
25
|
+
│ └── settings/page.tsx
|
|
26
|
+
└── api/webhooks/<source>/route.ts # webhooks NOT in (api|actions) — different lifecycle
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The principle: **group by trust boundary and rendering model, not by visual section**.
|
|
30
|
+
|
|
31
|
+
## Why these specific groups
|
|
32
|
+
|
|
33
|
+
- `(api|actions)` — every file inside is a trust boundary. The skill `void-server-action` applies. The "no fetch in components" rule applies in reverse: these files MUST fetch / mutate.
|
|
34
|
+
- `(marketing)` — statically renderable (Cache Components opted in by default), no auth, indexable by search engines. Different layout (marketing nav vs app shell).
|
|
35
|
+
- `(app)` — authenticated. Layout checks session, redirects to login if absent. All routes inside trust the layout's auth gate.
|
|
36
|
+
- `api/webhooks/<source>/` — NOT in a route group because the convention is path-stable for external systems. Stripe sends to `/api/webhooks/stripe`, period.
|
|
37
|
+
|
|
38
|
+
## When to add a new group
|
|
39
|
+
|
|
40
|
+
You need ALL three:
|
|
41
|
+
|
|
42
|
+
1. **A distinct layout** the new routes share (not just a different page)
|
|
43
|
+
2. **A distinct trust posture** OR rendering model (cached vs dynamic, auth vs public)
|
|
44
|
+
3. **≥ 2 routes** that fit the group
|
|
45
|
+
|
|
46
|
+
If you'd only put one route inside, don't add the group. Put the route in the closest existing group or at top level.
|
|
47
|
+
|
|
48
|
+
## Naming the group
|
|
49
|
+
|
|
50
|
+
- Use parentheses: `(name)` — they're invisible in URL
|
|
51
|
+
- Lowercase, single word, plural if it represents a category
|
|
52
|
+
- Conventional names: `(api|actions)`, `(marketing)`, `(app)`, `(admin)`, `(public)`, `(authed)`
|
|
53
|
+
- Avoid: `(misc)`, `(stuff)`, `(my-new-feature)` — groups are coarse, not feature-scoped
|
|
54
|
+
|
|
55
|
+
## Sharing layouts across groups
|
|
56
|
+
|
|
57
|
+
You can't directly — groups are siblings, each owns its layout. To share a header across `(marketing)` and `(app)`:
|
|
58
|
+
|
|
59
|
+
- Option A: put the shared header in `app/layout.tsx` (the root)
|
|
60
|
+
- Option B: extract the header as a component, import in each group's layout
|
|
61
|
+
|
|
62
|
+
A is cleaner if the header is truly shared by every group. B is right if only some groups share it.
|
|
63
|
+
|
|
64
|
+
## The `app/layout.tsx` (root)
|
|
65
|
+
|
|
66
|
+
ONE root layout, contains:
|
|
67
|
+
|
|
68
|
+
- `<html>` and `<body>` tags (mandatory — only the root layout has these)
|
|
69
|
+
- Providers needed everywhere (Theme, i18n, Sentry, query client if RQ used)
|
|
70
|
+
- Global stylesheets
|
|
71
|
+
|
|
72
|
+
Should NOT contain:
|
|
73
|
+
|
|
74
|
+
- Navigation (lives in group layouts)
|
|
75
|
+
- Auth check (lives in `(app)/layout.tsx`)
|
|
76
|
+
- Marketing-specific markup
|
|
77
|
+
|
|
78
|
+
If root layout grows past 40 lines, you have layout-creep — push down.
|
|
79
|
+
|
|
80
|
+
## Parallel routes and intercepting routes — separate concept
|
|
81
|
+
|
|
82
|
+
`@modal`, `(.)`, `(..)`, `(..)(..)`, `(...)` are **NOT** route groups. They're parallel/intercepting routes. See `void-parallel-routes-slots` for that pattern.
|
|
83
|
+
|
|
84
|
+
A route group is `(name)` (parentheses around a folder). Don't confuse them.
|
|
85
|
+
|
|
86
|
+
## Anti-patterns
|
|
87
|
+
|
|
88
|
+
- ✗ **`(feature-name)` group for one route** — premature. Put the route at top level until you have a second.
|
|
89
|
+
- ✗ **Auth check in every page** instead of the `(app)/layout.tsx` — repetition + miss risk
|
|
90
|
+
- ✗ **Mixing public + authed routes in the same group** — the layout can't enforce a uniform auth posture
|
|
91
|
+
- ✗ **Group nesting**: `(app)/(admin)/...` — usually a smell. Flatten to `(admin)/` and put the auth check in its layout
|
|
92
|
+
- ✗ **Renaming a group after launch** — the convention is propagated through skill references and CLAUDE.md modules; rename is cheap technically but expensive culturally
|
|
93
|
+
|
|
94
|
+
## Workflow
|
|
95
|
+
|
|
96
|
+
1. **List the routes you're adding.** 3 routes? They probably go in one group.
|
|
97
|
+
2. **Identify the trust posture.** Public? Authed? Trust boundary? That picks the group.
|
|
98
|
+
3. **Check existing groups.** Does one fit? If yes, use it. Don't create new groups for routes that fit.
|
|
99
|
+
4. **Layout content:** what's truly shared between these routes? That's the layout. Everything else is page-specific.
|
|
100
|
+
5. **If creating a new group**, write an ADR (`void-decide`). Group convention drift is annoying to undo.
|
|
101
|
+
|
|
102
|
+
## Composition
|
|
103
|
+
|
|
104
|
+
- `void-cache-component-pattern` — `(marketing)` is cache-by-default; `(app)` mostly `'use no cache'`.
|
|
105
|
+
- `void-server-action` — `(actions)/` is where Server Actions live.
|
|
106
|
+
- `void-decide` — adding/renaming groups is ADR-worthy.
|
|
107
|
+
- `void-security-guidance` — `(app)/layout.tsx` is the auth boundary; redirects centralized here.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Sources for `void-install-prompt-ux` skill (see `docs/plans/skill-audits/void-install-prompt-ux.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- No external source audited — native VoidCorp skill (beforeinstallprompt capture, engagement-signal threshold, dismiss cooldown, iOS fallback).
|
|
4
|
+
|
|
5
|
+
Strategy: native. Original VoidCorp authoring, not derived from an external source.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-install-prompt-ux
|
|
3
|
+
description: Surface the PWA install prompt at the right moment (engagement signal) with the right UI (custom button, not auto-prompt). Capture beforeinstallprompt, defer, recover declined.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# install-prompt-ux
|
|
7
|
+
|
|
8
|
+
Use when adding "Install app" UX to a PWA, or when figuring out why the default browser prompt doesn't appear / appears at the wrong time. Default browser behavior is broken for most apps — too early, too generic, no recovery.
|
|
9
|
+
|
|
10
|
+
This skill is the void-machine pattern: capture the event, defer it, surface a custom button at a meaningful moment.
|
|
11
|
+
|
|
12
|
+
## The browser default is bad
|
|
13
|
+
|
|
14
|
+
Without custom code, Chrome (etc.) decides when to show a small browser-styled "Add to Home Screen" toast. The decision is opaque (engagement heuristic), the UI is generic, and once dismissed it doesn't reappear soon. Users either miss it or click "Cancel" reflexively.
|
|
15
|
+
|
|
16
|
+
Solution: hijack the prompt, defer it, show your own button.
|
|
17
|
+
|
|
18
|
+
## The `beforeinstallprompt` flow
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
// apps/web/src/components/InstallPrompt.tsx
|
|
22
|
+
'use client';
|
|
23
|
+
|
|
24
|
+
import { useEffect, useState } from 'react';
|
|
25
|
+
|
|
26
|
+
interface BIPEvent extends Event {
|
|
27
|
+
prompt: () => Promise<void>;
|
|
28
|
+
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function InstallPrompt() {
|
|
32
|
+
const [deferred, setDeferred] = useState<BIPEvent | null>(null);
|
|
33
|
+
const [isInstalled, setIsInstalled] = useState(false);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
// 1. Capture the event when the browser fires it
|
|
37
|
+
const onBIP = (e: Event) => {
|
|
38
|
+
e.preventDefault(); // don't show browser default
|
|
39
|
+
setDeferred(e as BIPEvent);
|
|
40
|
+
};
|
|
41
|
+
window.addEventListener('beforeinstallprompt', onBIP);
|
|
42
|
+
|
|
43
|
+
// 2. Detect already-installed (PWA opened from home screen)
|
|
44
|
+
const onInstalled = () => {
|
|
45
|
+
setIsInstalled(true);
|
|
46
|
+
setDeferred(null);
|
|
47
|
+
};
|
|
48
|
+
window.addEventListener('appinstalled', onInstalled);
|
|
49
|
+
|
|
50
|
+
// 3. Sanity check: are we already in standalone mode?
|
|
51
|
+
if (window.matchMedia('(display-mode: standalone)').matches) {
|
|
52
|
+
setIsInstalled(true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener('beforeinstallprompt', onBIP);
|
|
57
|
+
window.removeEventListener('appinstalled', onInstalled);
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
if (isInstalled || !deferred) return null;
|
|
62
|
+
|
|
63
|
+
async function handleInstall() {
|
|
64
|
+
if (!deferred) return;
|
|
65
|
+
await deferred.prompt(); // shows the browser native prompt NOW
|
|
66
|
+
const { outcome } = await deferred.userChoice;
|
|
67
|
+
setDeferred(null); // one shot per page load; can't re-prompt
|
|
68
|
+
if (outcome === 'accepted') {
|
|
69
|
+
// Tracked, navigated to standalone — onInstalled fires shortly
|
|
70
|
+
} else {
|
|
71
|
+
// User dismissed — store decision, don't pester
|
|
72
|
+
localStorage.setItem('install-dismissed-at', String(Date.now()));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<button onClick={handleInstall}>Installer l'app</button>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## When to show your custom button
|
|
83
|
+
|
|
84
|
+
NOT immediately. Show only after an engagement signal — proof the user finds the app useful:
|
|
85
|
+
|
|
86
|
+
| Signal | Threshold (default) |
|
|
87
|
+
|---|---|
|
|
88
|
+
| Sessions completed | ≥ 2 |
|
|
89
|
+
| Time in-app this session | ≥ 30 seconds |
|
|
90
|
+
| Key action completed | 1 (e.g., created their first contact in Solaar) |
|
|
91
|
+
| Days since first visit | ≥ 1 |
|
|
92
|
+
|
|
93
|
+
Combine: "shown after 2 sessions AND ≥ 30s AND not dismissed in the last 7 days". The combination kills the "saw an empty app, prompted to install, declined forever" failure.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
const ENGAGEMENT_THRESHOLD = { sessions: 2, secondsThisSession: 30 };
|
|
97
|
+
|
|
98
|
+
function shouldShow(state) {
|
|
99
|
+
const dismissedAt = Number(localStorage.getItem('install-dismissed-at') ?? 0);
|
|
100
|
+
const daysSinceDismiss = (Date.now() - dismissedAt) / (24 * 60 * 60 * 1000);
|
|
101
|
+
if (dismissedAt && daysSinceDismiss < 7) return false;
|
|
102
|
+
return state.sessions >= ENGAGEMENT_THRESHOLD.sessions &&
|
|
103
|
+
state.secondsThisSession >= ENGAGEMENT_THRESHOLD.secondsThisSession;
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Recovery: dismissed → can we re-prompt?
|
|
108
|
+
|
|
109
|
+
Once `beforeinstallprompt` is dismissed, Chrome won't refire for at least 90 days (per their heuristic, not contractual). Strategies:
|
|
110
|
+
|
|
111
|
+
- **Cooldown**: store `install-dismissed-at`, don't show button for 7-14 days. After cooldown, if `deferred` is again non-null (Chrome decided to re-fire), show.
|
|
112
|
+
- **Fallback for unsupported browsers** (Safari iOS): provide instructions in a help page ("Tap Share → Add to Home Screen").
|
|
113
|
+
|
|
114
|
+
iOS Safari doesn't fire `beforeinstallprompt` at all. The button can still be useful as a "How to install on iPhone" link.
|
|
115
|
+
|
|
116
|
+
## Placement of the install button
|
|
117
|
+
|
|
118
|
+
Three patterns; pick one based on app type:
|
|
119
|
+
|
|
120
|
+
- **A. In the settings/profile menu**: discoverable for power users, low-friction. Default.
|
|
121
|
+
- **B. As a dismissible banner**: more aggressive; for content-heavy apps where install is the conversion goal
|
|
122
|
+
- **C. After a key action**: e.g., "Just created your first contact! Install the app to access it offline." Highest conversion when timed right.
|
|
123
|
+
|
|
124
|
+
Avoid: modal pop-ups on first visit (worst conversion + user trust).
|
|
125
|
+
|
|
126
|
+
## Anti-patterns
|
|
127
|
+
|
|
128
|
+
- ✗ **Calling `deferred.prompt()` without user gesture** — browsers reject; the prompt won't fire
|
|
129
|
+
- ✗ **Showing the install button before SW registers** — install prompt requires manifest + SW; show only after `serviceWorker.ready`
|
|
130
|
+
- ✗ **No recovery for dismissed users** — silent forever
|
|
131
|
+
- ✗ **Pestering on every page** — kills trust; one button in the settings menu is enough for most apps
|
|
132
|
+
- ✗ **No handling for iOS** — Safari doesn't fire BIP; without a fallback "how to install" page, iOS users have no path
|
|
133
|
+
|
|
134
|
+
## Anatomy of a great PWA install flow (Solaar archetype)
|
|
135
|
+
|
|
136
|
+
1. New visitor → no install UX
|
|
137
|
+
2. Visit 2, 30s in → small "Installer" button appears in settings menu (subtle)
|
|
138
|
+
3. After creating first contact → contextual prompt: "Vos contacts dans la poche. Installer ?"
|
|
139
|
+
4. User dismisses → 7-day cooldown
|
|
140
|
+
5. After 7 days, if still using the app → re-prompt in the contextual spot, not in face
|
|
141
|
+
|
|
142
|
+
## Verification
|
|
143
|
+
|
|
144
|
+
DevTools → Application → Manifest → "Add to home screen" button (forces install in dev).
|
|
145
|
+
Lighthouse → PWA category → "Installable" should be green; otherwise read the failure reasons (usually missing icon, missing start_url, etc.).
|
|
146
|
+
|
|
147
|
+
## Composition
|
|
148
|
+
|
|
149
|
+
- `void-manifest-checklist` — installability requires a valid manifest first
|
|
150
|
+
- `void-service-worker-strategy` — installability requires SW registered
|
|
151
|
+
- `void-accessibility-check` — install button is interactive; needs label, focus ring
|
|
152
|
+
- `void-observability` — track install events (`window.addEventListener('appinstalled', ...)`) for conversion measurement
|
|
153
|
+
- `void-frontend-design` — when to show, where to place — UX trade-offs covered here
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Sources for `void-manifest-checklist` skill (see `docs/plans/skill-audits/void-manifest-checklist.md` for the full audit):
|
|
2
|
+
|
|
3
|
+
- No external source audited — native VoidCorp skill (field-by-field manifest checklist with the WHY for each, anti-pattern list, optional-fields-worth-adding section).
|
|
4
|
+
|
|
5
|
+
Strategy: native. Original VoidCorp authoring, not derived from an external source.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: void-manifest-checklist
|
|
3
|
+
description: "Get manifest.webmanifest right the first time — required fields, icon sizes (192, 512, maskable), display modes, scope, start_url. Single artifact: every field motivated, no dead options."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# manifest-checklist
|
|
7
|
+
|
|
8
|
+
Use when creating or updating the web app manifest. The manifest is the document that tells browsers "this is a PWA, install it like an app". A wrong manifest is the difference between "no install prompt ever" and "installable on all platforms".
|
|
9
|
+
|
|
10
|
+
If you have no manifest yet, this skill is the green-field checklist. If you have one and "install" doesn't work somewhere, this skill is the debug.
|
|
11
|
+
|
|
12
|
+
## File location and reference
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
apps/<app>/public/manifest.webmanifest # the file
|
|
16
|
+
apps/<app>/app/layout.tsx # references via <link>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Reference in `<head>`:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
// app/layout.tsx
|
|
23
|
+
export const metadata = {
|
|
24
|
+
manifest: '/manifest.webmanifest',
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or for Next 16's typed metadata.ts:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// app/manifest.ts
|
|
32
|
+
import type { MetadataRoute } from 'next';
|
|
33
|
+
|
|
34
|
+
export default function manifest(): MetadataRoute.Manifest {
|
|
35
|
+
return { /* ... */ };
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use the TS file if your stack supports it — typo-safe.
|
|
40
|
+
|
|
41
|
+
## Required fields
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"name": "Solaar",
|
|
46
|
+
"short_name": "Solaar",
|
|
47
|
+
"description": "Relationship intelligence companion.",
|
|
48
|
+
"start_url": "/",
|
|
49
|
+
"scope": "/",
|
|
50
|
+
"display": "standalone",
|
|
51
|
+
"background_color": "#0a0a0a",
|
|
52
|
+
"theme_color": "#0a0a0a",
|
|
53
|
+
"lang": "fr",
|
|
54
|
+
"dir": "ltr",
|
|
55
|
+
"icons": [
|
|
56
|
+
{
|
|
57
|
+
"src": "/icons/icon-192.png",
|
|
58
|
+
"sizes": "192x192",
|
|
59
|
+
"type": "image/png",
|
|
60
|
+
"purpose": "any"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"src": "/icons/icon-512.png",
|
|
64
|
+
"sizes": "512x512",
|
|
65
|
+
"type": "image/png",
|
|
66
|
+
"purpose": "any"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"src": "/icons/icon-maskable-192.png",
|
|
70
|
+
"sizes": "192x192",
|
|
71
|
+
"type": "image/png",
|
|
72
|
+
"purpose": "maskable"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"src": "/icons/icon-maskable-512.png",
|
|
76
|
+
"sizes": "512x512",
|
|
77
|
+
"type": "image/png",
|
|
78
|
+
"purpose": "maskable"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Every field above has a reason. Skipping ANY breaks installability on some platform.
|
|
85
|
+
|
|
86
|
+
## Field-by-field
|
|
87
|
+
|
|
88
|
+
### `name` and `short_name`
|
|
89
|
+
|
|
90
|
+
- `name`: full name shown in the install prompt and OS app list
|
|
91
|
+
- `short_name`: home screen label (truncated platforms). ≤ 12 chars ideal
|
|
92
|
+
|
|
93
|
+
Distinct values when the full name is long: `name: "Solaar by VoidCorp"`, `short_name: "Solaar"`.
|
|
94
|
+
|
|
95
|
+
### `start_url` and `scope`
|
|
96
|
+
|
|
97
|
+
- `start_url`: where the app opens on launch from home screen. Almost always `/`.
|
|
98
|
+
- `scope`: paths that "belong to" the app. Outside scope, the OS opens browser instead of in-app. Almost always `/`.
|
|
99
|
+
|
|
100
|
+
If you scope to `/app/` and the user visits `/marketing/`, they'll bounce out to browser. Usually undesired.
|
|
101
|
+
|
|
102
|
+
### `display`
|
|
103
|
+
|
|
104
|
+
| Value | Effect |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `standalone` | Full-screen app, no browser UI. **Default for PWAs.** |
|
|
107
|
+
| `fullscreen` | Even hides the status bar (for games) |
|
|
108
|
+
| `minimal-ui` | Some browser controls visible |
|
|
109
|
+
| `browser` | Normal browser tab (no PWA-ness) |
|
|
110
|
+
|
|
111
|
+
Use `standalone`. The others are niche.
|
|
112
|
+
|
|
113
|
+
### `theme_color` and `background_color`
|
|
114
|
+
|
|
115
|
+
- `theme_color`: status bar / title bar color in standalone mode
|
|
116
|
+
- `background_color`: splash screen color before the app's CSS loads
|
|
117
|
+
|
|
118
|
+
Set both to your app's primary background. Mismatch causes a flash on launch.
|
|
119
|
+
|
|
120
|
+
### `lang` and `dir`
|
|
121
|
+
|
|
122
|
+
- `lang`: primary language ISO code (`fr`, `en`, `pt-BR`)
|
|
123
|
+
- `dir`: `ltr` or `rtl`
|
|
124
|
+
|
|
125
|
+
Don't omit. AT and OS use them for accessibility hints.
|
|
126
|
+
|
|
127
|
+
### `icons`
|
|
128
|
+
|
|
129
|
+
The minimum: **4 icons** — 192 and 512 in both `any` and `maskable` purposes.
|
|
130
|
+
|
|
131
|
+
- `any` purpose: standard square icons (used on browsers, older Android)
|
|
132
|
+
- `maskable`: safe-zone respected (used by Android adaptive icons, where the OS clips to circle/squircle/etc.). MUST have 20% padding around the actual logo
|
|
133
|
+
|
|
134
|
+
Larger sizes (1024) are nice but not required. iOS uses its own `apple-touch-icon.png` (set separately in `<head>`).
|
|
135
|
+
|
|
136
|
+
Generate from one SVG via:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npx pwa-asset-generator logo.svg public/icons/ --maskable-only false --padding "20%"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Optional fields worth adding
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"categories": ["productivity", "business"],
|
|
147
|
+
"screenshots": [
|
|
148
|
+
{
|
|
149
|
+
"src": "/screenshots/home-narrow.png",
|
|
150
|
+
"sizes": "390x844",
|
|
151
|
+
"form_factor": "narrow",
|
|
152
|
+
"label": "Accueil sur mobile"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"src": "/screenshots/home-wide.png",
|
|
156
|
+
"sizes": "1280x720",
|
|
157
|
+
"form_factor": "wide",
|
|
158
|
+
"label": "Accueil sur desktop"
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"shortcuts": [
|
|
162
|
+
{
|
|
163
|
+
"name": "Nouveau contact",
|
|
164
|
+
"url": "/contacts/new",
|
|
165
|
+
"icons": [{ "src": "/icons/new-contact.png", "sizes": "96x96" }]
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"share_target": {
|
|
169
|
+
"action": "/share",
|
|
170
|
+
"method": "POST",
|
|
171
|
+
"enctype": "multipart/form-data",
|
|
172
|
+
"params": { "title": "title", "text": "text", "url": "url" }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
- `categories`: helps Play Store / OS app categorization
|
|
178
|
+
- `screenshots`: shown in install prompt on Android (Chrome). **Set form_factor** so each is shown on the right platform
|
|
179
|
+
- `shortcuts`: appear on long-press of the app icon (Android)
|
|
180
|
+
- `share_target`: receive shared content from other apps
|
|
181
|
+
|
|
182
|
+
## Anti-patterns
|
|
183
|
+
|
|
184
|
+
- ✗ **Same icon for `any` and `maskable`** — maskable needs 20% padding; non-padded icon gets clipped on Android
|
|
185
|
+
- ✗ **Both `manifest.webmanifest` and `manifest.json`** — confuse browsers; pick `.webmanifest` (correct mime type)
|
|
186
|
+
- ✗ **`start_url: "/?source=pwa"`** with tracking query — breaks back-navigation, surprises the user
|
|
187
|
+
- ✗ **`scope: "."`** — relative scope is fragile; use `/`
|
|
188
|
+
- ✗ **Missing maskable icon** — Android adaptive icons render with broken padding
|
|
189
|
+
- ✗ **Theme color set on `<meta>` but missing in manifest** — inconsistent splash screen
|
|
190
|
+
|
|
191
|
+
## Verification
|
|
192
|
+
|
|
193
|
+
Browser DevTools → Application → Manifest:
|
|
194
|
+
|
|
195
|
+
- Shows parsed manifest with warnings
|
|
196
|
+
- "Installability" badge red? Read the reasons listed
|
|
197
|
+
- Use Chrome DevTools Lighthouse → PWA category for a comprehensive audit (target 100)
|
|
198
|
+
|
|
199
|
+
## Composition
|
|
200
|
+
|
|
201
|
+
- `void-install-prompt-ux` — installability depends on manifest being valid
|
|
202
|
+
- `void-service-worker-strategy` — SW + manifest are the two PWA artifacts; both required for install prompt
|
|
203
|
+
- `void-accessibility-check` — `lang` + `dir` are a11y signals
|
|
204
|
+
- `void-frontend-design` — theme_color and icon design should match the brand
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Harness metadata for this skill. Not part of the Agent Skills
|
|
2
|
+
# specification, and never copied into a consumer project: the SKILL.md
|
|
3
|
+
# beside it stays portable and validates against the official six fields.
|
|
4
|
+
# Read by this repository's graph, and by the installer for `runtimes`.
|
|
5
|
+
kind: standard
|
|
6
|
+
owner: folpe
|
|
7
|
+
runtimes: [claude, codex]
|
|
8
|
+
enforcement:
|
|
9
|
+
floor: ci
|
|
10
|
+
inline:
|
|
11
|
+
claude: active
|
|
12
|
+
codex: active
|
|
13
|
+
hermes: ci-only
|
|
14
|
+
eval_targets: [claude/anthropic/opus]
|