@schneiderjoseph/devia 0.1.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/AGENTS.md +149 -0
- package/CHANGELOG.md +78 -0
- package/GOVERNANCE.md +51 -0
- package/LEVELS.md +78 -0
- package/LICENSE +21 -0
- package/MATURITY.md +79 -0
- package/MEMORY.md +122 -0
- package/MIGRATION.md +62 -0
- package/PRINCIPLES.md +110 -0
- package/README.md +125 -0
- package/REFERENCES.md +30 -0
- package/VERSION +5 -0
- package/bin/devia.mjs +13 -0
- package/checklists/README.md +39 -0
- package/checklists/design/accessibility-review.md +11 -0
- package/checklists/design/design-to-code-review.md +10 -0
- package/checklists/design/responsive-review.md +7 -0
- package/checklists/design/ui-review.md +8 -0
- package/checklists/design/ux-review.md +10 -0
- package/checklists/engineering/accessibility.md +8 -0
- package/checklists/engineering/api.md +12 -0
- package/checklists/engineering/architecture.md +8 -0
- package/checklists/engineering/database.md +11 -0
- package/checklists/engineering/devops.md +9 -0
- package/checklists/engineering/disaster-recovery.md +9 -0
- package/checklists/engineering/frontend.md +9 -0
- package/checklists/engineering/incident.md +9 -0
- package/checklists/engineering/performance.md +7 -0
- package/checklists/engineering/production.md +26 -0
- package/checklists/engineering/release.md +17 -0
- package/checklists/engineering/security.md +33 -0
- package/checklists/engineering/testing.md +8 -0
- package/compliance/COVERAGE.md +46 -0
- package/compliance/REQUIREMENTS.md +8 -0
- package/compliance/TRACEABILITY.md +160 -0
- package/compliance/WAIVERS.md +13 -0
- package/package.json +74 -0
- package/rules/LIFECYCLE.md +16 -0
- package/rules/README.md +166 -0
- package/rules/accessibility/A11Y-001.md +42 -0
- package/rules/accessibility/A11Y-002.md +41 -0
- package/rules/accessibility/A11Y-003.md +40 -0
- package/rules/accessibility/A11Y-004.md +40 -0
- package/rules/accessibility/A11Y-005.md +39 -0
- package/rules/accessibility/A11Y-006.md +42 -0
- package/rules/accessibility/A11Y-007.md +39 -0
- package/rules/accessibility/A11Y-008.md +40 -0
- package/rules/accessibility/A11Y-009.md +41 -0
- package/rules/accessibility/A11Y-010.md +40 -0
- package/rules/accessibility/A11Y-011.md +39 -0
- package/rules/accessibility/A11Y-012.md +39 -0
- package/rules/agent/AGT-001.md +42 -0
- package/rules/agent/AGT-002.md +39 -0
- package/rules/agent/AGT-003.md +38 -0
- package/rules/agent/AGT-004.md +39 -0
- package/rules/agent/AGT-005.md +38 -0
- package/rules/agent/AGT-006.md +38 -0
- package/rules/agent/AGT-007.md +38 -0
- package/rules/agent/AGT-008.md +38 -0
- package/rules/agent/AGT-009.md +39 -0
- package/rules/agent/AGT-010.md +38 -0
- package/rules/agent/AGT-011.md +38 -0
- package/rules/ai/AI-001.md +44 -0
- package/rules/ai/AI-002.md +39 -0
- package/rules/ai/AI-003.md +40 -0
- package/rules/ai/AI-004.md +39 -0
- package/rules/ai/AI-005.md +38 -0
- package/rules/api/API-001.md +39 -0
- package/rules/api/API-002.md +39 -0
- package/rules/api/API-003.md +39 -0
- package/rules/api/API-004.md +38 -0
- package/rules/api/API-005.md +39 -0
- package/rules/api/API-006.md +40 -0
- package/rules/architecture/ARC-001.md +41 -0
- package/rules/architecture/ARC-002.md +43 -0
- package/rules/architecture/ARC-003.md +43 -0
- package/rules/architecture/ARC-004.md +40 -0
- package/rules/architecture/ARC-005.md +39 -0
- package/rules/architecture/ARC-006.md +39 -0
- package/rules/components/CMP-001.md +39 -0
- package/rules/components/CMP-002.md +38 -0
- package/rules/components/CMP-003.md +39 -0
- package/rules/components/CMP-004.md +40 -0
- package/rules/components/CMP-005.md +39 -0
- package/rules/content/CNT-001.md +39 -0
- package/rules/data-display/DATA-001.md +39 -0
- package/rules/data-display/DATA-002.md +39 -0
- package/rules/data-display/DATA-003.md +40 -0
- package/rules/data-display/DATA-004.md +40 -0
- package/rules/database/DB-001.md +43 -0
- package/rules/database/DB-002.md +38 -0
- package/rules/database/DB-003.md +40 -0
- package/rules/database/DB-004.md +44 -0
- package/rules/database/DB-005.md +39 -0
- package/rules/database/DB-006.md +38 -0
- package/rules/database/DB-007.md +40 -0
- package/rules/database/DB-008.md +40 -0
- package/rules/design-system/DS-001.md +39 -0
- package/rules/design-system/DS-002.md +40 -0
- package/rules/design-system/DS-003.md +38 -0
- package/rules/devops/OPS-001.md +43 -0
- package/rules/devops/OPS-002.md +39 -0
- package/rules/devops/OPS-003.md +38 -0
- package/rules/devops/OPS-004.md +40 -0
- package/rules/devops/OPS-005.md +43 -0
- package/rules/devops/OPS-006.md +38 -0
- package/rules/devops/OPS-007.md +40 -0
- package/rules/interaction/INT-001.md +39 -0
- package/rules/interaction/INT-002.md +39 -0
- package/rules/localization/I18N-001.md +38 -0
- package/rules/localization/I18N-002.md +40 -0
- package/rules/localization/I18N-003.md +39 -0
- package/rules/memory/MEM-001.md +44 -0
- package/rules/memory/MEM-002.md +39 -0
- package/rules/memory/MEM-003.md +38 -0
- package/rules/memory/MEM-004.md +38 -0
- package/rules/memory/MEM-005.md +38 -0
- package/rules/memory/MEM-006.md +38 -0
- package/rules/memory/MEM-007.md +38 -0
- package/rules/memory/MEM-008.md +38 -0
- package/rules/memory/MEM-009.md +39 -0
- package/rules/memory/MEM-010.md +38 -0
- package/rules/memory/MEM-011.md +38 -0
- package/rules/motion/MOT-001.md +38 -0
- package/rules/observability/OBS-001.md +40 -0
- package/rules/observability/OBS-002.md +40 -0
- package/rules/observability/OBS-003.md +38 -0
- package/rules/observability/OBS-004.md +38 -0
- package/rules/privacy/PRIV-001.md +38 -0
- package/rules/privacy/PRIV-002.md +44 -0
- package/rules/privacy/PRIV-003.md +40 -0
- package/rules/privacy/PRIV-004.md +41 -0
- package/rules/responsive/RWD-001.md +40 -0
- package/rules/responsive/RWD-002.md +39 -0
- package/rules/responsive/RWD-003.md +39 -0
- package/rules/security/SEC-001.md +44 -0
- package/rules/security/SEC-002.md +44 -0
- package/rules/security/SEC-003.md +41 -0
- package/rules/security/SEC-004.md +40 -0
- package/rules/security/SEC-005.md +42 -0
- package/rules/security/SEC-006.md +40 -0
- package/rules/security/SEC-007.md +44 -0
- package/rules/security/SEC-008.md +39 -0
- package/rules/security/SEC-009.md +40 -0
- package/rules/security/SEC-010.md +40 -0
- package/rules/security/SEC-011.md +40 -0
- package/rules/security/SEC-012.md +40 -0
- package/rules/states/STATE-001.md +38 -0
- package/rules/states/STATE-002.md +39 -0
- package/rules/states/STATE-003.md +39 -0
- package/rules/states/STATE-004.md +38 -0
- package/rules/testing/TST-001.md +39 -0
- package/rules/testing/TST-002.md +39 -0
- package/rules/testing/TST-003.md +39 -0
- package/rules/testing/TST-004.md +38 -0
- package/rules/testing/TST-005.md +40 -0
- package/rules/testing/TST-006.md +39 -0
- package/rules/ui/UI-001.md +40 -0
- package/rules/ui/UI-002.md +38 -0
- package/rules/ui/UI-003.md +41 -0
- package/rules/ui/UI-004.md +41 -0
- package/rules/ui/UI-005.md +41 -0
- package/rules/ui/UI-006.md +40 -0
- package/rules/ui/UI-007.md +41 -0
- package/rules/ux/UX-001.md +41 -0
- package/rules/ux/UX-002.md +41 -0
- package/rules/ux/UX-003.md +39 -0
- package/rules/ux/UX-004.md +39 -0
- package/rules/ux/UX-005.md +40 -0
- package/rules/ux/UX-006.md +40 -0
- package/rules/ux/UX-007.md +44 -0
- package/rules/ux/UX-008.md +41 -0
- package/rules/ux/UX-009.md +39 -0
- package/rules/ux/UX-010.md +40 -0
- package/rules/ux/UX-011.md +40 -0
- package/rules/ux/UX-012.md +40 -0
- package/rules/ux/UX-013.md +39 -0
- package/schema/README.md +19 -0
- package/schema/checklist.schema.json +34 -0
- package/schema/component.schema.json +37 -0
- package/schema/project-config.schema.json +70 -0
- package/schema/rule.schema.json +104 -0
- package/schema/token.schema.json +15 -0
- package/schema/waiver.schema.json +33 -0
- package/scripts/build-index.mjs +178 -0
- package/scripts/validate-links.mjs +43 -0
- package/scripts/validate-rules.mjs +42 -0
- package/skills/devia/SKILL.md +114 -0
- package/src/cli.mjs +107 -0
- package/src/commands/check.mjs +483 -0
- package/src/commands/doctor.mjs +139 -0
- package/src/commands/init.mjs +186 -0
- package/src/commands/registry.mjs +154 -0
- package/src/commands/rules.mjs +101 -0
- package/src/commands/skills.mjs +97 -0
- package/src/commands/sync.mjs +83 -0
- package/src/commands/validate.mjs +194 -0
- package/src/lib/fs.mjs +89 -0
- package/src/lib/git.mjs +31 -0
- package/src/lib/rules.mjs +85 -0
- package/src/lib/ui.mjs +52 -0
- package/src/lib/vendor.mjs +74 -0
- package/src/lib/version.mjs +19 -0
- package/src/lib/yaml.mjs +152 -0
- package/standard/README.md +31 -0
- package/standard/design/README.md +51 -0
- package/standard/design/REFERENCES.md +39 -0
- package/standard/design/accessibility/aria.md +7 -0
- package/standard/design/accessibility/contrast.md +6 -0
- package/standard/design/accessibility/focus.md +6 -0
- package/standard/design/accessibility/forms.md +3 -0
- package/standard/design/accessibility/keyboard.md +6 -0
- package/standard/design/accessibility/reduced-motion.md +3 -0
- package/standard/design/accessibility/reflow.md +3 -0
- package/standard/design/accessibility/screen-readers.md +3 -0
- package/standard/design/accessibility/target-size.md +3 -0
- package/standard/design/accessibility/testing.md +3 -0
- package/standard/design/accessibility/wcag-2.2.md +16 -0
- package/standard/design/accessibility/zoom.md +3 -0
- package/standard/design/ai/ai-design-rules.md +8 -0
- package/standard/design/ai/anti-patterns.md +1 -0
- package/standard/design/ai/compliance-format.md +22 -0
- package/standard/design/ai/design-review.md +10 -0
- package/standard/design/ai/evals/README.md +1 -0
- package/standard/design/ai/instructions.md +7 -0
- package/standard/design/ai/prompts/accessibility-review.md +5 -0
- package/standard/design/ai/prompts/design-system-review.md +5 -0
- package/standard/design/ai/prompts/ui-review.md +5 -0
- package/standard/design/ai/prompts/ux-review.md +5 -0
- package/standard/design/ai/review-protocol.md +11 -0
- package/standard/design/anti-patterns/accessibility.md +7 -0
- package/standard/design/anti-patterns/ai-generated.md +6 -0
- package/standard/design/anti-patterns/dashboards.md +5 -0
- package/standard/design/anti-patterns/forms.md +5 -0
- package/standard/design/anti-patterns/responsive.md +5 -0
- package/standard/design/anti-patterns/ui.md +6 -0
- package/standard/design/anti-patterns/ux.md +5 -0
- package/standard/design/components/buttons.md +6 -0
- package/standard/design/components/dialogs.md +5 -0
- package/standard/design/components/feedback.md +3 -0
- package/standard/design/components/forms.md +3 -0
- package/standard/design/components/navigation.md +3 -0
- package/standard/design/components/tables.md +6 -0
- package/standard/design/content/error-messages.md +4 -0
- package/standard/design/content/ux-writing.md +5 -0
- package/standard/design/data-display/README.md +5 -0
- package/standard/design/data-display/currency.md +3 -0
- package/standard/design/data-display/dates.md +3 -0
- package/standard/design/data-display/density.md +3 -0
- package/standard/design/data-display/filtering.md +3 -0
- package/standard/design/data-display/localization.md +3 -0
- package/standard/design/data-display/numbers.md +3 -0
- package/standard/design/data-display/pagination.md +3 -0
- package/standard/design/data-display/percentages.md +3 -0
- package/standard/design/data-display/sorting.md +3 -0
- package/standard/design/data-display/tables.md +3 -0
- package/standard/design/data-display/time.md +3 -0
- package/standard/design/decisions/ADR-001-token-naming.md +13 -0
- package/standard/design/decisions/ADR-002-radius-scale.md +9 -0
- package/standard/design/decisions/ADR-003-color-system.md +9 -0
- package/standard/design/decisions/ADR-004-component-versioning.md +9 -0
- package/standard/design/decisions/README.md +3 -0
- package/standard/design/design-system/components.md +9 -0
- package/standard/design/design-system/naming.md +19 -0
- package/standard/design/design-system/tokens.md +7 -0
- package/standard/design/design-system/variants.md +5 -0
- package/standard/design/design-system/versioning.md +5 -0
- package/standard/design/health/coverage.md +1 -0
- package/standard/design/health/dashboard.md +14 -0
- package/standard/design/health/maturity-model.md +9 -0
- package/standard/design/health/score.md +14 -0
- package/standard/design/interaction/README.md +10 -0
- package/standard/design/interaction/keyboard.md +3 -0
- package/standard/design/interaction/mouse.md +2 -0
- package/standard/design/interaction/pen.md +1 -0
- package/standard/design/interaction/touch.md +3 -0
- package/standard/design/interaction/touchscreen.md +6 -0
- package/standard/design/interaction/voice.md +2 -0
- package/standard/design/localization/README.md +5 -0
- package/standard/design/localization/currency.md +3 -0
- package/standard/design/localization/date-time.md +3 -0
- package/standard/design/localization/i18n.md +3 -0
- package/standard/design/localization/locale-testing.md +3 -0
- package/standard/design/localization/number-formatting.md +3 -0
- package/standard/design/localization/pluralization.md +3 -0
- package/standard/design/localization/rtl.md +3 -0
- package/standard/design/localization/text-expansion.md +3 -0
- package/standard/design/motion/animation.md +6 -0
- package/standard/design/penpot/WORKFLOW.md +27 -0
- package/standard/design/platforms/README.md +12 -0
- package/standard/design/platforms/desktop/README.md +5 -0
- package/standard/design/platforms/mobile/android.md +5 -0
- package/standard/design/platforms/mobile/ios.md +5 -0
- package/standard/design/platforms/tablet/README.md +5 -0
- package/standard/design/platforms/web/README.md +1 -0
- package/standard/design/principles/design-principles.md +9 -0
- package/standard/design/principles/ui-principles.md +7 -0
- package/standard/design/principles/ux-principles.md +14 -0
- package/standard/design/references/atlassian.md +3 -0
- package/standard/design/references/carbon.md +3 -0
- package/standard/design/references/figma-sds.md +5 -0
- package/standard/design/references/govuk.md +3 -0
- package/standard/design/references/material.md +3 -0
- package/standard/design/references/nng.md +3 -0
- package/standard/design/references/openui.md +3 -0
- package/standard/design/references/primer.md +5 -0
- package/standard/design/references/storybook.md +3 -0
- package/standard/design/references/uswds.md +3 -0
- package/standard/design/references/w3c.md +3 -0
- package/standard/design/responsive/desktop.md +4 -0
- package/standard/design/responsive/mobile.md +6 -0
- package/standard/design/responsive/tablet.md +3 -0
- package/standard/design/states/async.md +3 -0
- package/standard/design/states/conflict.md +3 -0
- package/standard/design/states/empty.md +3 -0
- package/standard/design/states/error.md +3 -0
- package/standard/design/states/interaction.md +5 -0
- package/standard/design/states/loading.md +3 -0
- package/standard/design/states/offline.md +3 -0
- package/standard/design/states/optimistic.md +3 -0
- package/standard/design/states/partial.md +3 -0
- package/standard/design/states/permission-denied.md +3 -0
- package/standard/design/states/rate-limited.md +3 -0
- package/standard/design/states/read-only.md +3 -0
- package/standard/design/states/retry.md +3 -0
- package/standard/design/states/skeleton.md +3 -0
- package/standard/design/states/stale.md +3 -0
- package/standard/design/states/success.md +3 -0
- package/standard/design/states/timeout.md +3 -0
- package/standard/design/states/unsaved-changes.md +3 -0
- package/standard/design/ui/color.md +6 -0
- package/standard/design/ui/grids.md +5 -0
- package/standard/design/ui/icons.md +5 -0
- package/standard/design/ui/imagery.md +5 -0
- package/standard/design/ui/layout.md +5 -0
- package/standard/design/ui/spacing.md +5 -0
- package/standard/design/ui/typography.md +6 -0
- package/standard/design/ui/visual-design.md +7 -0
- package/standard/design/ux/cognitive-load.md +5 -0
- package/standard/design/ux/errors.md +6 -0
- package/standard/design/ux/feedback.md +5 -0
- package/standard/design/ux/forms.md +7 -0
- package/standard/design/ux/heuristics.md +14 -0
- package/standard/design/ux/information-architecture.md +6 -0
- package/standard/design/ux/navigation.md +6 -0
- package/standard/design/ux/usability.md +6 -0
- package/standard/engineering/README.md +29 -0
- package/standard/engineering/REFERENCES.md +42 -0
- package/standard/engineering/ai/AI_SECURITY.md +15 -0
- package/standard/engineering/ai/PROMPT_SECURITY.md +6 -0
- package/standard/engineering/architecture/ADR.md +11 -0
- package/standard/engineering/architecture/ARCHITECTURE.md +25 -0
- package/standard/engineering/architecture/PRINCIPLES.md +17 -0
- package/standard/engineering/architecture/THREAT_MODEL.md +14 -0
- package/standard/engineering/backend/API.md +29 -0
- package/standard/engineering/backend/BACKGROUND_JOBS.md +12 -0
- package/standard/engineering/backend/ERROR_HANDLING.md +26 -0
- package/standard/engineering/backend/LOGGING.md +12 -0
- package/standard/engineering/compliance/DATA_RETENTION.md +10 -0
- package/standard/engineering/compliance/PRIVACY.md +11 -0
- package/standard/engineering/database/BACKUPS.md +19 -0
- package/standard/engineering/database/DATABASE.md +30 -0
- package/standard/engineering/database/MIGRATIONS.md +12 -0
- package/standard/engineering/devops/BRANCH_PROTECTION.md +17 -0
- package/standard/engineering/devops/CI_CD.md +48 -0
- package/standard/engineering/devops/DEPLOYMENT.md +12 -0
- package/standard/engineering/devops/DOCKER.md +9 -0
- package/standard/engineering/devops/ENVIRONMENTS.md +12 -0
- package/standard/engineering/devops/RELEASE.md +6 -0
- package/standard/engineering/devops/ROLLBACK.md +9 -0
- package/standard/engineering/frontend/ACCESSIBILITY.md +16 -0
- package/standard/engineering/frontend/DESIGN_SYSTEM.md +10 -0
- package/standard/engineering/frontend/FRONTEND.md +17 -0
- package/standard/engineering/frontend/UX.md +15 -0
- package/standard/engineering/observability/ALERTING.md +11 -0
- package/standard/engineering/observability/INCIDENTS.md +10 -0
- package/standard/engineering/observability/MONITORING.md +17 -0
- package/standard/engineering/payments/PAYMENTS.md +15 -0
- package/standard/engineering/performance/PERFORMANCE.md +19 -0
- package/standard/engineering/security/API_SECURITY.md +22 -0
- package/standard/engineering/security/ASVS_5.0/V10_MALICIOUS_CODE.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V11_BUSINESS_LOGIC.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V12_FILES.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V13_API.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V14_CONFIGURATION.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V1_ARCHITECTURE.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V2_AUTHENTICATION.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V3_SESSION.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V4_ACCESS_CONTROL.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V5_VALIDATION.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V6_CRYPTOGRAPHY.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V7_ERROR_HANDLING.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V8_DATA_PROTECTION.md +31 -0
- package/standard/engineering/security/ASVS_5.0/V9_COMMUNICATION.md +31 -0
- package/standard/engineering/security/ASVS_REGISTRY.md +31 -0
- package/standard/engineering/security/AUTHENTICATION.md +28 -0
- package/standard/engineering/security/AUTHORIZATION.md +27 -0
- package/standard/engineering/security/DATA_SECURITY.md +14 -0
- package/standard/engineering/security/OWASP_ASVS.md +31 -0
- package/standard/engineering/security/SECRETS.md +28 -0
- package/standard/engineering/security/SECURITY.md +28 -0
- package/standard/engineering/testing/TESTING.md +27 -0
- package/templates/agents/AGENTS.md +34 -0
- package/templates/agents/CLAUDE.md +33 -0
- package/templates/agents/copilot-instructions.md +15 -0
- package/templates/agents/cursor.mdc +27 -0
- package/templates/agents/windsurfrules.md +17 -0
- package/templates/docs/ADR.md +16 -0
- package/templates/docs/FEATURE.md +19 -0
- package/templates/docs/SECURITY_REPORT.md +15 -0
- package/templates/docs/accessibility-review.md +9 -0
- package/templates/docs/component.md +9 -0
- package/templates/docs/decision-record.md +9 -0
- package/templates/docs/design-review.md +9 -0
- package/templates/docs/exception.md +9 -0
- package/templates/docs/page.md +9 -0
- package/templates/docs/pattern.md +9 -0
- package/templates/docs/user-flow.md +9 -0
- package/templates/github/workflows/app-ci.yml +91 -0
- package/templates/project/00_OVERVIEW.md +51 -0
- package/templates/project/01_ARCHITECTURE.md +37 -0
- package/templates/project/02_SURFACES.md +30 -0
- package/templates/project/03_DATA_MODEL.md +31 -0
- package/templates/project/04_PERMISSIONS.md +32 -0
- package/templates/project/05_FLOWS.md +20 -0
- package/templates/project/06_INTEGRATIONS.md +21 -0
- package/templates/project/07_DESIGN.md +33 -0
- package/templates/project/10_NEVER_ALWAYS.md +26 -0
- package/templates/project/11_GAPS.md +18 -0
- package/templates/project/12_DEBT.md +19 -0
- package/templates/project/13_RECIPES.md +35 -0
- package/templates/project/14_INDEX.md +50 -0
- package/templates/project/AGENTS.md +51 -0
- package/templates/project/README.md +49 -0
- package/templates/project/impact-map.yaml +41 -0
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { packageRoot, findProjectRoot } from "./lib/fs.mjs";
|
|
4
|
+
import { cliVersion, standardVersion } from "./lib/version.mjs";
|
|
5
|
+
import { color } from "./lib/ui.mjs";
|
|
6
|
+
|
|
7
|
+
const COMMANDS = {
|
|
8
|
+
init: () => import("./commands/init.mjs"),
|
|
9
|
+
validate: () => import("./commands/validate.mjs"),
|
|
10
|
+
check: () => import("./commands/check.mjs"),
|
|
11
|
+
doctor: () => import("./commands/doctor.mjs"),
|
|
12
|
+
rules: () => import("./commands/rules.mjs"),
|
|
13
|
+
sync: () => import("./commands/sync.mjs"),
|
|
14
|
+
skills: () => import("./commands/skills.mjs"),
|
|
15
|
+
gap: () => import("./commands/registry.mjs"),
|
|
16
|
+
debt: () => import("./commands/registry.mjs"),
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const HELP = `
|
|
20
|
+
${color.bold("devia")} — one standard, one memory
|
|
21
|
+
|
|
22
|
+
${color.bold("devia init")} create .devia/ and the agent adapters in this repository
|
|
23
|
+
${color.bold("devia validate")} check memory integrity (structure, registries, placeholders)
|
|
24
|
+
${color.bold("devia check")} readiness gates — P0 failures exit non-zero
|
|
25
|
+
${color.bold("devia doctor")} adoption, drift and staleness diagnosis
|
|
26
|
+
${color.bold("devia rules")} list or show rules from the registry
|
|
27
|
+
${color.bold("devia sync")} refresh the vendored standard after upgrading devia
|
|
28
|
+
${color.bold("devia skills")} install the agent adapters (install --agent all)
|
|
29
|
+
${color.bold("devia gap")} add or close a line in 11_GAPS.md
|
|
30
|
+
${color.bold("devia debt")} add or close a line in 12_DEBT.md
|
|
31
|
+
|
|
32
|
+
Common flags
|
|
33
|
+
|
|
34
|
+
--root <dir> operate on another directory (default: the repository root)
|
|
35
|
+
--json machine-readable output where supported
|
|
36
|
+
--help command help
|
|
37
|
+
--version print the devia and standard versions
|
|
38
|
+
|
|
39
|
+
Docs: AGENTS.md · MEMORY.md · rules/README.md
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
export function parseArgs(argv) {
|
|
43
|
+
const args = { _: [], flags: {} };
|
|
44
|
+
for (let i = 0; i < argv.length; i++) {
|
|
45
|
+
const a = argv[i];
|
|
46
|
+
if (a.startsWith("--")) {
|
|
47
|
+
const [key, inline] = a.slice(2).split("=");
|
|
48
|
+
const next = argv[i + 1];
|
|
49
|
+
if (inline !== undefined) args.flags[key] = inline;
|
|
50
|
+
else if (next && !next.startsWith("--")) args.flags[key] = argv[++i];
|
|
51
|
+
else args.flags[key] = true;
|
|
52
|
+
} else if (a.startsWith("-") && a.length > 1) {
|
|
53
|
+
args.flags[a.slice(1)] = true;
|
|
54
|
+
} else {
|
|
55
|
+
args._.push(a);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return args;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function context(args) {
|
|
62
|
+
const cwd = path.resolve(process.cwd());
|
|
63
|
+
const given = args.flags.root ? path.resolve(String(args.flags.root)) : null;
|
|
64
|
+
const root = given || findProjectRoot(cwd);
|
|
65
|
+
return {
|
|
66
|
+
root,
|
|
67
|
+
cwd,
|
|
68
|
+
// The root was inferred by walking up, and it is not where the user is standing. A command
|
|
69
|
+
// that writes asks before acting on it; commands that only read do not care.
|
|
70
|
+
rootAwayFromCwd: !given && root !== cwd,
|
|
71
|
+
deviaDir: path.join(root, ".devia"),
|
|
72
|
+
packageRoot,
|
|
73
|
+
args,
|
|
74
|
+
flags: args.flags,
|
|
75
|
+
json: Boolean(args.flags.json),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function versions() {
|
|
80
|
+
return { cli: cliVersion(), standard: standardVersion() };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function run(argv) {
|
|
84
|
+
const args = parseArgs(argv);
|
|
85
|
+
const command = args._[0];
|
|
86
|
+
|
|
87
|
+
if (args.flags.version || command === "version") {
|
|
88
|
+
const { cli, standard } = versions();
|
|
89
|
+
console.log(`devia ${cli} · standard ${standard}`);
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
if (!command || command === "help" || (args.flags.help && !command)) {
|
|
93
|
+
console.log(HELP.trim());
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const load = COMMANDS[command];
|
|
98
|
+
if (!load) {
|
|
99
|
+
console.error(`unknown command: ${command}`);
|
|
100
|
+
console.log(HELP.trim());
|
|
101
|
+
return 2;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const mod = await load();
|
|
105
|
+
const code = await mod.default(context(args), command);
|
|
106
|
+
return typeof code === "number" ? code : 0;
|
|
107
|
+
}
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { exists, read, readJSON, walk } from "../lib/fs.mjs";
|
|
3
|
+
import { trackedFiles } from "../lib/git.mjs";
|
|
4
|
+
import { color, heading, status, line } from "../lib/ui.mjs";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Readiness gates (LEVELS.md level 2).
|
|
8
|
+
*
|
|
9
|
+
* Every check answers from evidence in the repository. A check that cannot answer returns SKIP
|
|
10
|
+
* with the reason — it never rounds up to PASS, because "I did not check" is reported, not
|
|
11
|
+
* assumed (AGT-006).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const SKIP_DIRS = new Set([
|
|
15
|
+
".git",
|
|
16
|
+
"node_modules",
|
|
17
|
+
"dist",
|
|
18
|
+
"build",
|
|
19
|
+
"out",
|
|
20
|
+
"coverage",
|
|
21
|
+
"vendor",
|
|
22
|
+
".next",
|
|
23
|
+
".venv",
|
|
24
|
+
"__pycache__",
|
|
25
|
+
".devia",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const SECRET_PATTERNS = [
|
|
29
|
+
[/AKIA[0-9A-Z]{16}/, "AWS access key id"],
|
|
30
|
+
[/-----BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----/, "private key"],
|
|
31
|
+
[/sk_live_[0-9a-zA-Z]{16,}/, "live secret key"],
|
|
32
|
+
[/gh[pousr]_[0-9A-Za-z]{30,}/, "GitHub token"],
|
|
33
|
+
[/xox[baprs]-[0-9A-Za-z-]{10,}/, "Slack token"],
|
|
34
|
+
[/AIza[0-9A-Za-z_-]{35}/, "Google API key"],
|
|
35
|
+
[/eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}/, "JWT"],
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A line carrying this marker is exempt from the secret and bypass scanners. It exists so an
|
|
40
|
+
* exemption is visible in the diff and reviewable — never as a silent allowlist elsewhere.
|
|
41
|
+
*/
|
|
42
|
+
const ALLOW = "devia:allow";
|
|
43
|
+
|
|
44
|
+
/** Where a bypass can actually be wired in: scripts, hooks and pipeline configuration. */
|
|
45
|
+
const EXECUTABLE_SURFACE =
|
|
46
|
+
/(^|\/)(package\.json|Makefile|Justfile|\.husky\/|\.github\/workflows\/|\.gitlab-ci\.yml|azure-pipelines\.yml|Jenkinsfile)|\.(sh|bash|ps1|cmd|bat|mk)$/;
|
|
47
|
+
|
|
48
|
+
const TEXT_EXT = new Set([
|
|
49
|
+
".js", ".mjs", ".cjs", ".ts", ".tsx", ".jsx", ".py", ".rb", ".go", ".rs", ".java", ".php",
|
|
50
|
+
".json", ".yml", ".yaml", ".toml", ".env", ".sh", ".ps1", ".sql", ".md", ".txt", ".html",
|
|
51
|
+
".css", ".ini", ".cfg", ".conf",
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The files this repository actually carries: what git tracks, plus what is untracked and not
|
|
56
|
+
* ignored — the content a push would publish. An ignored path is a local artefact, and failing a
|
|
57
|
+
* P0 gate on someone's build cache or downloaded fixture is a false positive, not vigilance.
|
|
58
|
+
* Without git the tree is walked instead, which is wider: SKIP is not an option here, because a
|
|
59
|
+
* secret scan that silently scanned nothing is worse than one that over-reports.
|
|
60
|
+
*/
|
|
61
|
+
function sourceFiles(root) {
|
|
62
|
+
const skipped = (rel) => rel.split(/[\/]/).some((seg) => SKIP_DIRS.has(seg));
|
|
63
|
+
const wanted = (rel) => TEXT_EXT.has(path.extname(rel).toLowerCase());
|
|
64
|
+
|
|
65
|
+
const tracked = trackedFiles(root);
|
|
66
|
+
if (tracked) {
|
|
67
|
+
return tracked
|
|
68
|
+
.filter((rel) => !skipped(rel) && wanted(rel))
|
|
69
|
+
.map((rel) => rel.split("/").join(path.sep))
|
|
70
|
+
.sort();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return walk(root, {
|
|
74
|
+
skip: (rel) => skipped(rel),
|
|
75
|
+
filter: (rel) => wanted(rel),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function anyExists(root, candidates) {
|
|
80
|
+
return candidates.find((c) => exists(path.join(root, c))) || null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function makeChecks(root, ctx) {
|
|
84
|
+
const pkg = readJSON(path.join(root, "package.json"));
|
|
85
|
+
const scripts = pkg?.scripts || {};
|
|
86
|
+
const deps = { ...(pkg?.dependencies || {}), ...(pkg?.devDependencies || {}) };
|
|
87
|
+
const config = readJSON(path.join(root, ".devia", "devia.json"));
|
|
88
|
+
const files = sourceFiles(root);
|
|
89
|
+
const rel = (p) => p.split(path.sep).join("/");
|
|
90
|
+
|
|
91
|
+
const hasDep = (...names) => names.some((n) => n in deps);
|
|
92
|
+
const grepFiles = (re, limit = 40) => {
|
|
93
|
+
const hits = [];
|
|
94
|
+
for (const f of files) {
|
|
95
|
+
const text = read(path.join(root, f));
|
|
96
|
+
if (text && re.test(text)) {
|
|
97
|
+
hits.push(rel(f));
|
|
98
|
+
if (hits.length >= limit) break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return hits;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return [
|
|
105
|
+
{
|
|
106
|
+
id: "MEM-PRESENT",
|
|
107
|
+
priority: "P0",
|
|
108
|
+
rule: "AGT-002",
|
|
109
|
+
title: "Project memory exists",
|
|
110
|
+
run: () =>
|
|
111
|
+
exists(path.join(root, ".devia"))
|
|
112
|
+
? { kind: "PASS" }
|
|
113
|
+
: { kind: "FAIL", detail: "run `devia init` before working here" },
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "MEM-FILLED",
|
|
117
|
+
priority: "P1",
|
|
118
|
+
rule: "MEM-009",
|
|
119
|
+
title: "Overview is filled in",
|
|
120
|
+
run: () => {
|
|
121
|
+
const text = read(path.join(root, ".devia", "00_OVERVIEW.md"));
|
|
122
|
+
if (text === null) return { kind: "SKIP", detail: "no .devia/00_OVERVIEW.md" };
|
|
123
|
+
const n = (text.match(/TODO\(devia\)/g) || []).length;
|
|
124
|
+
return n
|
|
125
|
+
? { kind: "WARN", detail: `${n} placeholders left` }
|
|
126
|
+
: { kind: "PASS" };
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: "MEM-DEBT-P0",
|
|
131
|
+
priority: "P0",
|
|
132
|
+
rule: "MEM-002",
|
|
133
|
+
title: "No P0 debt recorded as unbuilt",
|
|
134
|
+
run: () => {
|
|
135
|
+
const text = read(path.join(root, ".devia", "12_DEBT.md"));
|
|
136
|
+
if (text === null) return { kind: "SKIP", detail: "no debt registry" };
|
|
137
|
+
const rows = (text.match(/^\|\s*D\d+\s*\|.*$/gm) || []).filter(
|
|
138
|
+
(r) => /\bP0\b/.test(r) && !/TODO\(devia\)/.test(r)
|
|
139
|
+
);
|
|
140
|
+
return rows.length
|
|
141
|
+
? { kind: "FAIL", detail: `${rows.length} P0 debt line(s) open` }
|
|
142
|
+
: { kind: "PASS" };
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "MEM-WAIVERS",
|
|
147
|
+
priority: "P1",
|
|
148
|
+
rule: "GOVERNANCE",
|
|
149
|
+
title: "No expired waiver",
|
|
150
|
+
run: () => {
|
|
151
|
+
const waivers = config?.waivers || [];
|
|
152
|
+
if (!waivers.length) return { kind: "PASS", detail: "none" };
|
|
153
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
154
|
+
const expired = waivers.filter((w) => !w.expires || w.expires < today);
|
|
155
|
+
return expired.length
|
|
156
|
+
? { kind: "FAIL", detail: `${expired.length} expired or undated` }
|
|
157
|
+
: { kind: "PASS", detail: `${waivers.length} active` };
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "CI-PRESENT",
|
|
162
|
+
priority: "P0",
|
|
163
|
+
rule: "OPS-001",
|
|
164
|
+
title: "CI runs on pull requests",
|
|
165
|
+
run: () => {
|
|
166
|
+
const wf = path.join(root, ".github", "workflows");
|
|
167
|
+
const found =
|
|
168
|
+
(exists(wf) && walk(wf, { filter: (f) => /\.ya?ml$/.test(f) }).length) ||
|
|
169
|
+
anyExists(root, [".gitlab-ci.yml", "azure-pipelines.yml", "Jenkinsfile", ".circleci"]);
|
|
170
|
+
return found
|
|
171
|
+
? { kind: "PASS" }
|
|
172
|
+
: { kind: "FAIL", detail: "no CI configuration found" };
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: "CI-GATES",
|
|
177
|
+
priority: "P1",
|
|
178
|
+
rule: "OPS-001",
|
|
179
|
+
title: "CI runs tests and static checks",
|
|
180
|
+
run: () => {
|
|
181
|
+
const wf = path.join(root, ".github", "workflows");
|
|
182
|
+
if (!exists(wf)) return { kind: "SKIP", detail: "no GitHub workflows to read" };
|
|
183
|
+
const text = walk(wf, { filter: (f) => /\.ya?ml$/.test(f) })
|
|
184
|
+
.map((f) => read(path.join(wf, f)) || "")
|
|
185
|
+
.join("\n");
|
|
186
|
+
const has = (re) => re.test(text);
|
|
187
|
+
const missing = [];
|
|
188
|
+
if (!has(/\btest\b/i)) missing.push("test");
|
|
189
|
+
if (!has(/\blint\b|eslint|ruff|flake8/i)) missing.push("lint");
|
|
190
|
+
if (!has(/audit|snyk|dependabot|osv/i)) missing.push("dependency audit");
|
|
191
|
+
return missing.length
|
|
192
|
+
? { kind: "WARN", detail: `not referenced: ${missing.join(", ")}` }
|
|
193
|
+
: { kind: "PASS" };
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "SEC-ENV",
|
|
198
|
+
priority: "P0",
|
|
199
|
+
rule: "SEC-002",
|
|
200
|
+
title: "No environment file committed",
|
|
201
|
+
run: () => {
|
|
202
|
+
const ignore = read(path.join(root, ".gitignore")) || "";
|
|
203
|
+
const envs = [".env", ".env.local", ".env.production"].filter((f) =>
|
|
204
|
+
exists(path.join(root, f))
|
|
205
|
+
);
|
|
206
|
+
if (!envs.length) return { kind: "PASS" };
|
|
207
|
+
const ignored = /^\s*\.env/m.test(ignore);
|
|
208
|
+
return ignored
|
|
209
|
+
? { kind: "WARN", detail: `${envs.join(", ")} present locally but gitignored` }
|
|
210
|
+
: { kind: "FAIL", detail: `${envs.join(", ")} is not gitignored` };
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: "SEC-SECRETS",
|
|
215
|
+
priority: "P0",
|
|
216
|
+
rule: "SEC-002",
|
|
217
|
+
title: "No secret-shaped strings in the tree",
|
|
218
|
+
run: () => {
|
|
219
|
+
const hits = [];
|
|
220
|
+
let exempted = 0;
|
|
221
|
+
for (const f of files) {
|
|
222
|
+
if (/(^|\/)(\.env\.example|.*\.lock|package-lock\.json)$/.test(rel(f))) continue;
|
|
223
|
+
const text = read(path.join(root, f));
|
|
224
|
+
if (!text) continue;
|
|
225
|
+
const lines = text.split(/\r?\n/);
|
|
226
|
+
for (let i = 0; i < lines.length; i++) {
|
|
227
|
+
const hit = SECRET_PATTERNS.find(([re]) => re.test(lines[i]));
|
|
228
|
+
if (!hit) continue;
|
|
229
|
+
if (lines[i].includes(ALLOW)) {
|
|
230
|
+
exempted++;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
hits.push(`${rel(f)}:${i + 1} (${hit[1]})`);
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
if (hits.length >= 5) break;
|
|
237
|
+
}
|
|
238
|
+
return hits.length
|
|
239
|
+
? { kind: "FAIL", detail: hits.join("; ") }
|
|
240
|
+
: {
|
|
241
|
+
kind: "PASS",
|
|
242
|
+
detail: `${files.length} files scanned${exempted ? `, ${exempted} exempted` : ""}`,
|
|
243
|
+
};
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
id: "OPS-BYPASS",
|
|
248
|
+
priority: "P0",
|
|
249
|
+
rule: "OPS-003",
|
|
250
|
+
title: "No check bypass wired into the repository",
|
|
251
|
+
run: () => {
|
|
252
|
+
// Only scripts, hooks and pipeline configuration can wire a bypass in. Prose that
|
|
253
|
+
// forbids `--no-verify` is not a bypass, so documentation is out of scope here.
|
|
254
|
+
const hits = [];
|
|
255
|
+
for (const f of files) {
|
|
256
|
+
if (!EXECUTABLE_SURFACE.test(rel(f))) continue;
|
|
257
|
+
const text = read(path.join(root, f));
|
|
258
|
+
if (!text) continue;
|
|
259
|
+
const lines = text.split(/\r?\n/);
|
|
260
|
+
for (let i = 0; i < lines.length; i++) {
|
|
261
|
+
if (!/--no-verify|SKIP_HOOKS=1|HUSKY=0|\[skip ci\]/.test(lines[i])) continue;
|
|
262
|
+
if (lines[i].includes(ALLOW)) continue;
|
|
263
|
+
hits.push(`${rel(f)}:${i + 1}`);
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
if (hits.length >= 5) break;
|
|
267
|
+
}
|
|
268
|
+
return hits.length ? { kind: "FAIL", detail: hits.join(", ") } : { kind: "PASS" };
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "TST-PRESENT",
|
|
273
|
+
priority: "P0",
|
|
274
|
+
rule: "TST-001",
|
|
275
|
+
title: "Automated tests exist",
|
|
276
|
+
run: () => {
|
|
277
|
+
const testFiles = files.filter((f) =>
|
|
278
|
+
/(^|\/)(tests?|spec|__tests__)\//.test(rel(f)) ||
|
|
279
|
+
/\.(test|spec)\.[a-z]+$/.test(rel(f)) ||
|
|
280
|
+
/(^|\/)test_[^/]+\.py$/.test(rel(f))
|
|
281
|
+
);
|
|
282
|
+
return testFiles.length
|
|
283
|
+
? { kind: "PASS", detail: `${testFiles.length} test files` }
|
|
284
|
+
: { kind: "FAIL", detail: "no test files found" };
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: "TST-SKIPPED",
|
|
289
|
+
priority: "P1",
|
|
290
|
+
rule: "TST-003",
|
|
291
|
+
title: "No disabled tests",
|
|
292
|
+
run: () => {
|
|
293
|
+
const hits = grepFiles(
|
|
294
|
+
/\b(it|test|describe)\.skip\(|\bxit\(|\bxdescribe\(|@pytest\.mark\.skip|t\.Skip\(/,
|
|
295
|
+
20
|
|
296
|
+
);
|
|
297
|
+
return hits.length
|
|
298
|
+
? { kind: "WARN", detail: `${hits.length} file(s): ${hits.slice(0, 3).join(", ")}` }
|
|
299
|
+
: { kind: "PASS" };
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
id: "TST-SCRIPT",
|
|
304
|
+
priority: "P1",
|
|
305
|
+
rule: "TST-001",
|
|
306
|
+
title: "A test command exists",
|
|
307
|
+
run: () => {
|
|
308
|
+
if (!pkg) return { kind: "SKIP", detail: "no package.json" };
|
|
309
|
+
const t = scripts.test;
|
|
310
|
+
if (!t) return { kind: "WARN", detail: "no npm test script" };
|
|
311
|
+
return /no test specified/.test(t)
|
|
312
|
+
? { kind: "FAIL", detail: "test script is the npm placeholder" }
|
|
313
|
+
: { kind: "PASS" };
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
id: "OPS-LOCKFILE",
|
|
318
|
+
priority: "P1",
|
|
319
|
+
rule: "OPS-004",
|
|
320
|
+
title: "Dependency lockfile committed",
|
|
321
|
+
run: () => {
|
|
322
|
+
const manifests = ["package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile"];
|
|
323
|
+
if (!anyExists(root, manifests)) return { kind: "SKIP", detail: "no manifest" };
|
|
324
|
+
const lock = anyExists(root, [
|
|
325
|
+
"package-lock.json",
|
|
326
|
+
"pnpm-lock.yaml",
|
|
327
|
+
"yarn.lock",
|
|
328
|
+
"poetry.lock",
|
|
329
|
+
"uv.lock",
|
|
330
|
+
"requirements.txt",
|
|
331
|
+
"go.sum",
|
|
332
|
+
"Cargo.lock",
|
|
333
|
+
"Gemfile.lock",
|
|
334
|
+
]);
|
|
335
|
+
return lock
|
|
336
|
+
? { kind: "PASS", detail: lock }
|
|
337
|
+
: { kind: "FAIL", detail: "no lockfile" };
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "DB-MIGRATIONS",
|
|
342
|
+
priority: "P1",
|
|
343
|
+
rule: "DB-001",
|
|
344
|
+
title: "Schema changes are versioned migrations",
|
|
345
|
+
run: () => {
|
|
346
|
+
const usesDb =
|
|
347
|
+
hasDep("pg", "mysql2", "prisma", "@prisma/client", "typeorm", "sequelize", "knex",
|
|
348
|
+
"drizzle-orm", "mongoose", "sqlalchemy") ||
|
|
349
|
+
files.some((f) => /\.sql$/.test(rel(f)));
|
|
350
|
+
if (!usesDb) return { kind: "SKIP", detail: "no database detected" };
|
|
351
|
+
const dir = anyExists(root, [
|
|
352
|
+
"migrations",
|
|
353
|
+
"db/migrate",
|
|
354
|
+
"prisma/migrations",
|
|
355
|
+
"alembic",
|
|
356
|
+
path.join("src", "migrations"),
|
|
357
|
+
]);
|
|
358
|
+
return dir
|
|
359
|
+
? { kind: "PASS", detail: dir }
|
|
360
|
+
: { kind: "FAIL", detail: "database in use, no migrations directory" };
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: "AGT-CONTRACT",
|
|
365
|
+
priority: "P1",
|
|
366
|
+
rule: "AGT-001",
|
|
367
|
+
title: "Agent contract at the repository root",
|
|
368
|
+
run: () =>
|
|
369
|
+
exists(path.join(root, "AGENTS.md"))
|
|
370
|
+
? { kind: "PASS" }
|
|
371
|
+
: { kind: "WARN", detail: "run `devia skills install`" },
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "DOC-README",
|
|
375
|
+
priority: "P2",
|
|
376
|
+
rule: "—",
|
|
377
|
+
title: "README present",
|
|
378
|
+
run: () =>
|
|
379
|
+
anyExists(root, ["README.md", "README.rst", "readme.md"])
|
|
380
|
+
? { kind: "PASS" }
|
|
381
|
+
: { kind: "WARN", detail: "no README" },
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: "UI-A11Y-TOOLING",
|
|
385
|
+
priority: "P2",
|
|
386
|
+
rule: "A11Y-001",
|
|
387
|
+
title: "Accessibility tooling available",
|
|
388
|
+
run: () => {
|
|
389
|
+
if (config && config.modules && config.modules.design === false)
|
|
390
|
+
return { kind: "SKIP", detail: "design module disabled" };
|
|
391
|
+
if (!pkg) return { kind: "SKIP", detail: "no package.json" };
|
|
392
|
+
const found = hasDep(
|
|
393
|
+
"axe-core", "@axe-core/react", "@axe-core/playwright", "jest-axe",
|
|
394
|
+
"eslint-plugin-jsx-a11y", "pa11y", "@storybook/addon-a11y", "lighthouse"
|
|
395
|
+
);
|
|
396
|
+
return found
|
|
397
|
+
? { kind: "PASS" }
|
|
398
|
+
: { kind: "WARN", detail: "no automated accessibility check configured" };
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
id: "OBS-ERRORS",
|
|
403
|
+
priority: "P2",
|
|
404
|
+
rule: "OBS-002",
|
|
405
|
+
title: "Errors reach something a human watches",
|
|
406
|
+
run: () => {
|
|
407
|
+
const profile = config?.project?.profile;
|
|
408
|
+
if (["cli", "library", "docs"].includes(profile))
|
|
409
|
+
return { kind: "SKIP", detail: `${profile} does not run as a watched service` };
|
|
410
|
+
if (!pkg) return { kind: "SKIP", detail: "no package.json" };
|
|
411
|
+
const found = hasDep("@sentry/node", "@sentry/browser", "@sentry/nextjs", "bugsnag",
|
|
412
|
+
"rollbar", "datadog-lambda-js", "dd-trace", "@opentelemetry/api");
|
|
413
|
+
return found
|
|
414
|
+
? { kind: "PASS" }
|
|
415
|
+
: { kind: "WARN", detail: "no error tracker detected — verify manually" };
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export default async function check(ctx) {
|
|
422
|
+
const { root, flags } = ctx;
|
|
423
|
+
|
|
424
|
+
if (flags.help) {
|
|
425
|
+
line(`
|
|
426
|
+
${color.bold("devia check")} — readiness gates
|
|
427
|
+
|
|
428
|
+
--root <dir> repository to check
|
|
429
|
+
--json machine-readable output
|
|
430
|
+
--all show passing checks too
|
|
431
|
+
|
|
432
|
+
Exit code is 1 when a P0 gate fails.
|
|
433
|
+
`.trim());
|
|
434
|
+
return 0;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const checks = makeChecks(root, ctx);
|
|
438
|
+
const results = checks.map((c) => ({ ...c, ...c.run(), run: undefined }));
|
|
439
|
+
const counts = results.reduce((a, r) => ((a[r.kind] = (a[r.kind] || 0) + 1), a), {});
|
|
440
|
+
const p0Failures = results.filter((r) => r.kind === "FAIL" && r.priority === "P0");
|
|
441
|
+
const failures = results.filter((r) => r.kind === "FAIL");
|
|
442
|
+
|
|
443
|
+
if (ctx.json) {
|
|
444
|
+
console.log(
|
|
445
|
+
JSON.stringify(
|
|
446
|
+
{ ok: p0Failures.length === 0, counts, blocking: p0Failures.map((r) => r.id), results },
|
|
447
|
+
null,
|
|
448
|
+
2
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
return p0Failures.length ? 1 : 0;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
heading(`devia check — ${path.basename(root)}`);
|
|
455
|
+
for (const r of results) {
|
|
456
|
+
if (!flags.all && r.kind === "PASS") continue;
|
|
457
|
+
status(r.kind, `${r.priority} ${r.title}`, [r.detail, r.rule !== "—" ? r.rule : ""]
|
|
458
|
+
.filter(Boolean)
|
|
459
|
+
.join(" · "));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
line("");
|
|
463
|
+
const parts = ["PASS", "WARN", "FAIL", "SKIP"]
|
|
464
|
+
.filter((k) => counts[k])
|
|
465
|
+
.map((k) => `${counts[k]} ${k.toLowerCase()}`)
|
|
466
|
+
.join(" · ");
|
|
467
|
+
line(` ${parts}`);
|
|
468
|
+
|
|
469
|
+
if (p0Failures.length) {
|
|
470
|
+
line(
|
|
471
|
+
` ${color.red("BLOCKED")} — ${p0Failures.length} P0 gate(s) failing: ${p0Failures
|
|
472
|
+
.map((r) => r.id)
|
|
473
|
+
.join(", ")}`
|
|
474
|
+
);
|
|
475
|
+
line(color.dim(" Not production ready. Do not report done (AGT-005)."));
|
|
476
|
+
} else if (failures.length) {
|
|
477
|
+
line(` ${color.yellow("PASSES P0")} — ${failures.length} non-blocking failure(s) to schedule`);
|
|
478
|
+
} else {
|
|
479
|
+
line(` ${color.green("P0 clear")} — list the warnings for the human before claiming done`);
|
|
480
|
+
}
|
|
481
|
+
line("");
|
|
482
|
+
return p0Failures.length ? 1 : 0;
|
|
483
|
+
}
|