aios-core 4.2.15 → 4.4.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/.aios-core/cli/commands/validate/index.js +1 -1
- package/.aios-core/core/code-intel/code-intel-client.js +19 -5
- package/.aios-core/core/code-intel/helpers/creation-helper.js +183 -0
- package/.aios-core/core/code-intel/helpers/devops-helper.js +166 -0
- package/.aios-core/core/code-intel/helpers/planning-helper.js +248 -0
- package/.aios-core/core/code-intel/helpers/qa-helper.js +187 -0
- package/.aios-core/core/code-intel/helpers/story-helper.js +146 -0
- package/.aios-core/core/code-intel/hook-runtime.js +186 -0
- package/.aios-core/core/code-intel/index.js +2 -0
- package/.aios-core/core/code-intel/providers/code-graph-provider.js +8 -0
- package/.aios-core/core/code-intel/providers/provider-interface.js +9 -0
- package/.aios-core/core/code-intel/providers/registry-provider.js +515 -0
- package/.aios-core/core/config/schemas/framework-config.schema.json +155 -7
- package/.aios-core/core/config/schemas/project-config.schema.json +329 -15
- package/.aios-core/core/config/template-overrides.js +84 -0
- package/.aios-core/core/docs/troubleshooting-guide.md +1 -1
- package/.aios-core/core/doctor/checks/agent-memory.js +63 -0
- package/.aios-core/core/doctor/checks/claude-md.js +56 -0
- package/.aios-core/core/doctor/checks/code-intel.js +131 -0
- package/.aios-core/core/doctor/checks/commands-count.js +81 -0
- package/.aios-core/core/doctor/checks/core-config.js +53 -0
- package/.aios-core/core/doctor/checks/entity-registry.js +53 -0
- package/.aios-core/core/doctor/checks/git-hooks.js +50 -0
- package/.aios-core/core/doctor/checks/graph-dashboard.js +48 -0
- package/.aios-core/core/doctor/checks/hooks-claude-count.js +118 -0
- package/.aios-core/core/doctor/checks/ide-sync.js +85 -0
- package/.aios-core/core/doctor/checks/index.js +46 -0
- package/.aios-core/core/doctor/checks/node-version.js +33 -0
- package/.aios-core/core/doctor/checks/npm-packages.js +35 -0
- package/.aios-core/core/doctor/checks/rules-files.js +61 -0
- package/.aios-core/core/doctor/checks/settings-json.js +121 -0
- package/.aios-core/core/doctor/checks/skills-count.js +72 -0
- package/.aios-core/core/doctor/fix-handler.js +165 -0
- package/.aios-core/core/doctor/formatters/json.js +14 -0
- package/.aios-core/core/doctor/formatters/text.js +59 -0
- package/.aios-core/core/doctor/index.js +94 -0
- package/.aios-core/core/graph-dashboard/cli.js +361 -0
- package/.aios-core/core/graph-dashboard/data-sources/code-intel-source.js +234 -0
- package/.aios-core/core/graph-dashboard/data-sources/metrics-source.js +95 -0
- package/.aios-core/core/graph-dashboard/data-sources/registry-source.js +106 -0
- package/.aios-core/core/graph-dashboard/formatters/dot-formatter.js +45 -0
- package/.aios-core/core/graph-dashboard/formatters/html-formatter.js +1437 -0
- package/.aios-core/core/graph-dashboard/formatters/json-formatter.js +13 -0
- package/.aios-core/core/graph-dashboard/formatters/mermaid-formatter.js +59 -0
- package/.aios-core/core/graph-dashboard/index.js +21 -0
- package/.aios-core/core/graph-dashboard/renderers/stats-renderer.js +217 -0
- package/.aios-core/core/graph-dashboard/renderers/status-renderer.js +125 -0
- package/.aios-core/core/graph-dashboard/renderers/tree-renderer.js +119 -0
- package/.aios-core/core/health-check/base-check.js +1 -1
- package/.aios-core/core/health-check/check-registry.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/build-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/ci-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/deployment-readiness.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/docker-config.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/env-file.js +1 -1
- package/.aios-core/core/health-check/checks/deployment/index.js +1 -1
- package/.aios-core/core/health-check/checks/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/disk-space.js +1 -1
- package/.aios-core/core/health-check/checks/local/environment-vars.js +1 -1
- package/.aios-core/core/health-check/checks/local/git-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/ide-detection.js +1 -1
- package/.aios-core/core/health-check/checks/local/index.js +1 -1
- package/.aios-core/core/health-check/checks/local/memory.js +1 -1
- package/.aios-core/core/health-check/checks/local/network.js +1 -1
- package/.aios-core/core/health-check/checks/local/npm-install.js +1 -1
- package/.aios-core/core/health-check/checks/local/shell-environment.js +1 -1
- package/.aios-core/core/health-check/checks/project/agent-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/aios-directory.js +1 -1
- package/.aios-core/core/health-check/checks/project/dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/project/framework-config.js +1 -1
- package/.aios-core/core/health-check/checks/project/index.js +1 -1
- package/.aios-core/core/health-check/checks/project/node-version.js +1 -1
- package/.aios-core/core/health-check/checks/project/package-json.js +1 -1
- package/.aios-core/core/health-check/checks/project/task-definitions.js +1 -1
- package/.aios-core/core/health-check/checks/project/workflow-dependencies.js +1 -1
- package/.aios-core/core/health-check/checks/repository/branch-protection.js +1 -1
- package/.aios-core/core/health-check/checks/repository/commit-history.js +1 -1
- package/.aios-core/core/health-check/checks/repository/conflicts.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-repo.js +1 -1
- package/.aios-core/core/health-check/checks/repository/git-status.js +1 -1
- package/.aios-core/core/health-check/checks/repository/gitignore.js +1 -1
- package/.aios-core/core/health-check/checks/repository/index.js +1 -1
- package/.aios-core/core/health-check/checks/repository/large-files.js +1 -1
- package/.aios-core/core/health-check/checks/repository/lockfile-integrity.js +1 -1
- package/.aios-core/core/health-check/checks/services/api-endpoints.js +1 -1
- package/.aios-core/core/health-check/checks/services/claude-code.js +1 -1
- package/.aios-core/core/health-check/checks/services/gemini-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/github-cli.js +1 -1
- package/.aios-core/core/health-check/checks/services/index.js +1 -1
- package/.aios-core/core/health-check/checks/services/mcp-integration.js +1 -1
- package/.aios-core/core/health-check/engine.js +1 -1
- package/.aios-core/core/health-check/healers/backup-manager.js +1 -1
- package/.aios-core/core/health-check/healers/index.js +1 -1
- package/.aios-core/core/health-check/index.js +9 -2
- package/.aios-core/core/health-check/reporters/console.js +1 -1
- package/.aios-core/core/health-check/reporters/index.js +1 -1
- package/.aios-core/core/health-check/reporters/json.js +1 -1
- package/.aios-core/core/health-check/reporters/markdown.js +1 -1
- package/.aios-core/core/ids/layer-classifier.js +65 -0
- package/.aios-core/core/ids/registry-updater.js +49 -0
- package/.aios-core/core/index.esm.js +1 -1
- package/.aios-core/core/index.js +1 -1
- package/.aios-core/core/session/context-detector.js +2 -7
- package/.aios-core/core/synapse/context/context-tracker.js +9 -1
- package/.aios-core/core/synapse/engine.js +33 -13
- package/.aios-core/core/synapse/memory/memory-bridge.js +17 -43
- package/.aios-core/core/synapse/memory/synapse-memory-provider.js +201 -0
- package/.aios-core/core/synapse/runtime/hook-runtime.js +40 -2
- package/.aios-core/core/synapse/session/session-manager.js +3 -2
- package/.aios-core/core/synapse/utils/atomic-write.js +79 -0
- package/.aios-core/core-config.yaml +34 -1
- package/.aios-core/data/aios-kb.md +2 -2
- package/.aios-core/data/capability-detection.js +290 -0
- package/.aios-core/data/entity-registry.yaml +10450 -2129
- package/.aios-core/data/mcp-discipline.js +166 -0
- package/.aios-core/data/mcp-tool-examples.yaml +215 -0
- package/.aios-core/data/tok2-validation.js +168 -0
- package/.aios-core/data/tok3-token-comparison.js +123 -0
- package/.aios-core/data/tool-registry.yaml +648 -0
- package/.aios-core/data/tool-search-validation.js +174 -0
- package/.aios-core/data/workflow-chains.yaml +156 -0
- package/.aios-core/development/agents/aios-master.md +17 -10
- package/.aios-core/development/agents/analyst/MEMORY.md +33 -0
- package/.aios-core/development/agents/analyst.md +17 -10
- package/.aios-core/development/agents/architect/MEMORY.md +39 -0
- package/.aios-core/development/agents/architect.md +17 -10
- package/.aios-core/development/agents/data-engineer/MEMORY.md +32 -0
- package/.aios-core/development/agents/data-engineer.md +17 -10
- package/.aios-core/development/agents/dev/MEMORY.md +46 -0
- package/.aios-core/development/agents/dev.md +18 -11
- package/.aios-core/development/agents/devops/MEMORY.md +39 -0
- package/.aios-core/development/agents/devops.md +44 -10
- package/.aios-core/development/agents/pm/MEMORY.md +38 -0
- package/.aios-core/development/agents/pm.md +17 -10
- package/.aios-core/development/agents/po/MEMORY.md +45 -0
- package/.aios-core/development/agents/po.md +17 -10
- package/.aios-core/development/agents/qa/MEMORY.md +42 -0
- package/.aios-core/development/agents/qa.md +18 -11
- package/.aios-core/development/agents/sm/MEMORY.md +31 -0
- package/.aios-core/development/agents/sm.md +17 -10
- package/.aios-core/development/agents/squad-creator.md +18 -9
- package/.aios-core/development/agents/ux/MEMORY.md +31 -0
- package/.aios-core/development/agents/ux-design-expert.md +16 -9
- package/.aios-core/development/checklists/issue-triage-checklist.md +35 -0
- package/.aios-core/development/checklists/memory-audit-checklist.md +53 -0
- package/.aios-core/development/scripts/issue-triage.js +171 -0
- package/.aios-core/development/scripts/populate-entity-registry.js +412 -19
- package/.aios-core/development/scripts/unified-activation-pipeline.js +31 -10
- package/.aios-core/development/tasks/analyze-project-structure.md +48 -0
- package/.aios-core/development/tasks/apply-qa-fixes.md +7 -0
- package/.aios-core/development/tasks/architect-analyze-impact.md +8 -1
- package/.aios-core/development/tasks/brownfield-create-epic.md +41 -0
- package/.aios-core/development/tasks/brownfield-create-story.md +7 -0
- package/.aios-core/development/tasks/build-autonomous.md +7 -0
- package/.aios-core/development/tasks/create-deep-research-prompt.md +7 -0
- package/.aios-core/development/tasks/create-doc.md +44 -0
- package/.aios-core/development/tasks/create-next-story.md +17 -0
- package/.aios-core/development/tasks/create-suite.md +7 -0
- package/.aios-core/development/tasks/dev-develop-story.md +9 -1
- package/.aios-core/development/tasks/execute-checklist.md +7 -0
- package/.aios-core/development/tasks/github-devops-github-pr-automation.md +56 -0
- package/.aios-core/development/tasks/github-devops-pre-push-quality-gate.md +70 -0
- package/.aios-core/development/tasks/github-issue-triage.md +118 -0
- package/.aios-core/development/tasks/health-check.yaml +206 -171
- package/.aios-core/development/tasks/kb-mode-interaction.md +3 -3
- package/.aios-core/development/tasks/plan-create-context.md +47 -1
- package/.aios-core/development/tasks/plan-create-implementation.md +55 -0
- package/.aios-core/development/tasks/po-close-story.md +7 -0
- package/.aios-core/development/tasks/pr-automation.md +5 -5
- package/.aios-core/development/tasks/qa-create-fix-request.md +7 -0
- package/.aios-core/development/tasks/qa-fix-issues.md +7 -0
- package/.aios-core/development/tasks/qa-gate.md +56 -0
- package/.aios-core/development/tasks/qa-review-story.md +32 -1
- package/.aios-core/development/tasks/release-management.md +7 -0
- package/.aios-core/development/tasks/resolve-github-issue.md +608 -0
- package/.aios-core/development/tasks/review-contributor-pr.md +152 -0
- package/.aios-core/development/tasks/setup-llm-routing.md +1 -1
- package/.aios-core/development/tasks/spec-critique.md +8 -0
- package/.aios-core/development/tasks/spec-gather-requirements.md +7 -0
- package/.aios-core/development/tasks/spec-research-dependencies.md +4 -0
- package/.aios-core/development/tasks/spec-write-spec.md +5 -0
- package/.aios-core/development/tasks/triage-github-issues.md +356 -0
- package/.aios-core/development/tasks/validate-agents.md +4 -0
- package/.aios-core/development/tasks/validate-next-story.md +17 -0
- package/.aios-core/development/templates/agent-handoff-tmpl.yaml +48 -0
- package/.aios-core/development/templates/code-intel-integration-pattern.md +199 -0
- package/.aios-core/development/templates/ptc-entity-validation.md +113 -0
- package/.aios-core/development/templates/ptc-qa-gate.md +100 -0
- package/.aios-core/development/templates/ptc-research-aggregation.md +94 -0
- package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
- package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
- package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
- package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
- package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
- package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
- package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
- package/.aios-core/development/templates/squad/agent-template.md +11 -0
- package/.aios-core/development/templates/squad/task-template.md +21 -0
- package/.aios-core/development/templates/squad-template/LICENSE +21 -21
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-COMPLETE.md +1 -1
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.2-SUMMARY.md +1 -1
- package/.aios-core/framework-config.yaml +8 -0
- package/.aios-core/index.esm.js +1 -1
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/integrations/ai-providers/index.js +1 -1
- package/.aios-core/infrastructure/schemas/task-v3-schema.json +6 -0
- package/.aios-core/infrastructure/scripts/collect-tool-usage.js +311 -0
- package/.aios-core/infrastructure/scripts/generate-optimization-report.js +497 -0
- package/.aios-core/infrastructure/scripts/generate-settings-json.js +300 -0
- package/.aios-core/infrastructure/scripts/git-config-detector.js +65 -9
- package/.aios-core/infrastructure/scripts/ide-sync/index.js +3 -1
- package/.aios-core/infrastructure/scripts/ide-sync/transformers/github-copilot.js +184 -0
- package/.aios-core/infrastructure/scripts/repository-detector.js +3 -3
- package/.aios-core/infrastructure/templates/aios-sync.yaml.template +182 -182
- package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
- package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
- package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
- package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
- package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
- package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
- package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
- package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
- package/.aios-core/install-manifest.yaml +613 -305
- package/.aios-core/lib/build.json +1 -0
- package/.aios-core/local-config.yaml.template +71 -71
- package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
- package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
- package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
- package/.aios-core/monitor/hooks/notification.py +29 -29
- package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
- package/.aios-core/monitor/hooks/pre_compact.py +29 -29
- package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
- package/.aios-core/monitor/hooks/stop.py +29 -29
- package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
- package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
- package/.aios-core/product/templates/adr.hbs +125 -125
- package/.aios-core/product/templates/dbdr.hbs +241 -241
- package/.aios-core/product/templates/epic.hbs +212 -212
- package/.aios-core/product/templates/ide-rules/claude-rules.md +125 -0
- package/.aios-core/product/templates/pmdr.hbs +186 -186
- package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
- package/.aios-core/product/templates/prd.hbs +201 -201
- package/.aios-core/product/templates/story.hbs +263 -263
- package/.aios-core/product/templates/task.hbs +170 -170
- package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
- package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
- package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
- package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
- package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
- package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
- package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
- package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
- package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
- package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
- package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
- package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
- package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
- package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
- package/.aios-core/product/templates/tmpl-view.sql +177 -177
- package/.aios-core/scripts/pm.sh +0 -0
- package/.aios-core/user-guide.md +15 -15
- package/.aios-core/utils/filters/constants.js +10 -0
- package/.aios-core/utils/filters/content-filter.js +223 -0
- package/.aios-core/utils/filters/field-filter.js +126 -0
- package/.aios-core/utils/filters/index.js +180 -0
- package/.aios-core/utils/filters/schema-filter.js +157 -0
- package/.claude/CLAUDE.md +62 -0
- package/.claude/hooks/enforce-architecture-first.py +196 -196
- package/.claude/hooks/enforce-git-push-authority.sh +33 -0
- package/.claude/hooks/mind-clone-governance.py +192 -192
- package/.claude/hooks/read-protection.py +151 -151
- package/.claude/hooks/slug-validation.py +176 -176
- package/.claude/hooks/sql-governance.py +182 -182
- package/.claude/hooks/synapse-engine.cjs +28 -5
- package/.claude/hooks/write-path-validation.py +194 -194
- package/.claude/rules/agent-authority.md +105 -0
- package/.claude/rules/agent-handoff.md +97 -0
- package/.claude/rules/agent-memory-imports.md +15 -0
- package/.claude/rules/coderabbit-integration.md +101 -0
- package/.claude/rules/ids-principles.md +119 -0
- package/.claude/rules/story-lifecycle.md +145 -0
- package/.claude/rules/tool-examples.md +64 -0
- package/.claude/rules/tool-response-filtering.md +57 -0
- package/.claude/rules/workflow-execution.md +150 -0
- package/LICENSE +33 -33
- package/bin/aios-graph.js +9 -0
- package/bin/aios-init.js +2 -2
- package/bin/aios-minimal.js +0 -0
- package/bin/aios.js +17 -221
- package/bin/utils/detect-fsmonitor.js +70 -0
- package/bin/utils/framework-guard.js +238 -0
- package/bin/utils/validate-publish.js +108 -0
- package/package.json +6 -3
- package/packages/aios-install/bin/aios-install.js +0 -0
- package/packages/aios-install/bin/edmcp.js +0 -0
- package/packages/aios-pro-cli/bin/aios-pro.js +2 -0
- package/packages/installer/src/config/templates/core-config-template.js +25 -0
- package/packages/installer/src/installer/brownfield-upgrader.js +68 -5
- package/packages/installer/src/merger/index.js +3 -0
- package/packages/installer/src/merger/strategies/index.js +6 -0
- package/packages/installer/src/merger/strategies/yaml-merger.js +181 -0
- package/packages/installer/src/updater/index.js +4 -4
- package/packages/installer/src/wizard/i18n.js +321 -3
- package/packages/installer/src/wizard/ide-config-generator.js +173 -25
- package/packages/installer/src/wizard/index.js +119 -1
- package/packages/installer/src/wizard/pro-setup.js +137 -121
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +271 -0
- package/packages/installer/tests/unit/claude-md-template-v5/claude-md-template-v5.test.js +192 -0
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +610 -0
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +134 -0
- package/packages/installer/tests/unit/entity-registry-bootstrap.test.js +186 -0
- package/packages/installer/tests/unit/generate-settings-json/generate-settings-json.test.js +309 -0
- package/packages/installer/tests/unit/ide-sync-integration/ide-sync-integration.test.js +230 -0
- package/packages/installer/tests/unit/merger/strategies.test.js +2 -2
- package/packages/installer/tests/unit/merger/yaml-merger.test.js +327 -0
- package/scripts/check-markdown-links.py +352 -352
- package/scripts/dashboard-parallel-dev.sh +0 -0
- package/scripts/dashboard-parallel-phase3.sh +0 -0
- package/scripts/dashboard-parallel-phase4.sh +0 -0
- package/scripts/install-monitor-hooks.sh +0 -0
- package/scripts/package-synapse.js +2 -1
- package/pro/README.md +0 -66
- package/pro/license/degradation.js +0 -220
- package/pro/license/errors.js +0 -450
- package/pro/license/feature-gate.js +0 -354
- package/pro/license/index.js +0 -181
- package/pro/license/license-api.js +0 -651
- package/pro/license/license-cache.js +0 -523
- package/pro/license/license-crypto.js +0 -303
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Tests: Doctor Check Modules
|
|
3
|
+
* Story INS-4.1: aios doctor rewrite
|
|
4
|
+
* Story INS-4.8: 3 new checks (skills-count, commands-count, hooks-claude-count)
|
|
5
|
+
*
|
|
6
|
+
* Tests all 15 check modules individually with mocked filesystem.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
// Mock fs for controlled test scenarios
|
|
13
|
+
jest.mock('fs');
|
|
14
|
+
|
|
15
|
+
const nodeVersionCheck = require('../../../../../.aios-core/core/doctor/checks/node-version');
|
|
16
|
+
const npmPackagesCheck = require('../../../../../.aios-core/core/doctor/checks/npm-packages');
|
|
17
|
+
const settingsJsonCheck = require('../../../../../.aios-core/core/doctor/checks/settings-json');
|
|
18
|
+
const rulesFilesCheck = require('../../../../../.aios-core/core/doctor/checks/rules-files');
|
|
19
|
+
const agentMemoryCheck = require('../../../../../.aios-core/core/doctor/checks/agent-memory');
|
|
20
|
+
const entityRegistryCheck = require('../../../../../.aios-core/core/doctor/checks/entity-registry');
|
|
21
|
+
const gitHooksCheck = require('../../../../../.aios-core/core/doctor/checks/git-hooks');
|
|
22
|
+
const coreConfigCheck = require('../../../../../.aios-core/core/doctor/checks/core-config');
|
|
23
|
+
const claudeMdCheck = require('../../../../../.aios-core/core/doctor/checks/claude-md');
|
|
24
|
+
const graphDashboardCheck = require('../../../../../.aios-core/core/doctor/checks/graph-dashboard');
|
|
25
|
+
const codeIntelCheck = require('../../../../../.aios-core/core/doctor/checks/code-intel');
|
|
26
|
+
const ideSyncCheck = require('../../../../../.aios-core/core/doctor/checks/ide-sync');
|
|
27
|
+
const skillsCountCheck = require('../../../../../.aios-core/core/doctor/checks/skills-count');
|
|
28
|
+
const commandsCountCheck = require('../../../../../.aios-core/core/doctor/checks/commands-count');
|
|
29
|
+
const hooksClaudeCountCheck = require('../../../../../.aios-core/core/doctor/checks/hooks-claude-count');
|
|
30
|
+
const { loadChecks } = require('../../../../../.aios-core/core/doctor/checks');
|
|
31
|
+
|
|
32
|
+
const mockContext = {
|
|
33
|
+
projectRoot: '/mock/project',
|
|
34
|
+
frameworkRoot: '/mock/framework',
|
|
35
|
+
options: { fix: false, json: false, dryRun: false, quiet: false },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('node-version check', () => {
|
|
43
|
+
it('should PASS for current Node.js version (>=18)', async () => {
|
|
44
|
+
const result = await nodeVersionCheck.run(mockContext);
|
|
45
|
+
expect(result.check).toBe('node-version');
|
|
46
|
+
expect(result.status).toBe('PASS');
|
|
47
|
+
expect(result.message).toContain('Node.js');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('npm-packages check', () => {
|
|
52
|
+
it('should PASS when node_modules exists', async () => {
|
|
53
|
+
fs.existsSync.mockReturnValue(true);
|
|
54
|
+
const result = await npmPackagesCheck.run(mockContext);
|
|
55
|
+
expect(result.status).toBe('PASS');
|
|
56
|
+
expect(result.message).toBe('node_modules present');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should FAIL when node_modules missing', async () => {
|
|
60
|
+
fs.existsSync.mockReturnValue(false);
|
|
61
|
+
const result = await npmPackagesCheck.run(mockContext);
|
|
62
|
+
expect(result.status).toBe('FAIL');
|
|
63
|
+
expect(result.fixCommand).toBe('npm install');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('settings-json check', () => {
|
|
68
|
+
it('should PASS with valid settings and sufficient deny rules', async () => {
|
|
69
|
+
fs.existsSync.mockReturnValue(true);
|
|
70
|
+
const mockSettings = {
|
|
71
|
+
permissions: {
|
|
72
|
+
deny: new Array(50).fill('Edit(.aios-core/core/)'),
|
|
73
|
+
allow: ['Edit(docs/)'],
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
const coreConfig = 'boundary:\n protected:\n - .aios-core/core/**\n exceptions:\n - agents/MEMORY.md';
|
|
77
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
78
|
+
if (p.includes('settings.json')) return JSON.stringify(mockSettings);
|
|
79
|
+
if (p.includes('core-config')) return coreConfig;
|
|
80
|
+
return '';
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = await settingsJsonCheck.run(mockContext);
|
|
84
|
+
expect(result.status).toBe('PASS');
|
|
85
|
+
expect(result.message).toContain('50 rules');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should FAIL when settings.json not found', async () => {
|
|
89
|
+
fs.existsSync.mockReturnValue(false);
|
|
90
|
+
const result = await settingsJsonCheck.run(mockContext);
|
|
91
|
+
expect(result.status).toBe('FAIL');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should WARN when deny rules below threshold', async () => {
|
|
95
|
+
fs.existsSync.mockReturnValue(true);
|
|
96
|
+
const mockSettings = { permissions: { deny: ['one'], allow: [] } };
|
|
97
|
+
fs.readFileSync.mockReturnValue(JSON.stringify(mockSettings));
|
|
98
|
+
|
|
99
|
+
const result = await settingsJsonCheck.run(mockContext);
|
|
100
|
+
expect(result.status).toBe('WARN');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should WARN when boundary paths not covered by deny rules', async () => {
|
|
104
|
+
fs.existsSync.mockReturnValue(true);
|
|
105
|
+
const mockSettings = {
|
|
106
|
+
permissions: {
|
|
107
|
+
deny: new Array(50).fill('Edit(docs/)'),
|
|
108
|
+
allow: [],
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
const coreConfig = 'boundary:\n protected:\n - .aios-core/core/**\n - bin/aios.js\n exceptions:\n - test';
|
|
112
|
+
fs.readFileSync.mockImplementation((p) => {
|
|
113
|
+
if (p.includes('settings.json')) return JSON.stringify(mockSettings);
|
|
114
|
+
if (p.includes('core-config')) return coreConfig;
|
|
115
|
+
return '';
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const result = await settingsJsonCheck.run(mockContext);
|
|
119
|
+
expect(result.status).toBe('WARN');
|
|
120
|
+
expect(result.message).toContain('boundary coverage');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('rules-files check', () => {
|
|
125
|
+
it('should PASS when all 7 rules files exist', async () => {
|
|
126
|
+
fs.existsSync.mockReturnValue(true);
|
|
127
|
+
const result = await rulesFilesCheck.run(mockContext);
|
|
128
|
+
expect(result.status).toBe('PASS');
|
|
129
|
+
expect(result.message).toContain('7');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should FAIL when rules directory missing', async () => {
|
|
133
|
+
fs.existsSync.mockReturnValue(false);
|
|
134
|
+
const result = await rulesFilesCheck.run(mockContext);
|
|
135
|
+
expect(result.status).toBe('FAIL');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should WARN when some rules missing', async () => {
|
|
139
|
+
fs.existsSync.mockImplementation((p) => {
|
|
140
|
+
// Directory exists
|
|
141
|
+
if (p.endsWith('rules')) return true;
|
|
142
|
+
// Most files exist except 2
|
|
143
|
+
if (p.includes('agent-authority') || p.includes('workflow-execution')) return false;
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const result = await rulesFilesCheck.run(mockContext);
|
|
148
|
+
expect(result.status).toBe('WARN');
|
|
149
|
+
expect(result.message).toContain('Missing 2');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('agent-memory check', () => {
|
|
154
|
+
it('should PASS when all 10 MEMORY.md files exist', async () => {
|
|
155
|
+
fs.existsSync.mockReturnValue(true);
|
|
156
|
+
const result = await agentMemoryCheck.run(mockContext);
|
|
157
|
+
expect(result.status).toBe('PASS');
|
|
158
|
+
expect(result.message).toContain('10/10');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should WARN when some MEMORY.md files missing', async () => {
|
|
162
|
+
fs.existsSync.mockImplementation((p) => {
|
|
163
|
+
if (p.endsWith('agents')) return true;
|
|
164
|
+
if (p.includes('analyst') || p.includes('ux')) return false;
|
|
165
|
+
return true;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const result = await agentMemoryCheck.run(mockContext);
|
|
169
|
+
expect(result.status).toBe('WARN');
|
|
170
|
+
expect(result.message).toContain('8/10');
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('entity-registry check', () => {
|
|
175
|
+
it('should PASS when registry exists and is fresh', async () => {
|
|
176
|
+
fs.existsSync.mockReturnValue(true);
|
|
177
|
+
fs.statSync.mockReturnValue({ mtimeMs: Date.now() - 1000 });
|
|
178
|
+
fs.readFileSync.mockReturnValue('line1\nline2\nline3');
|
|
179
|
+
|
|
180
|
+
const result = await entityRegistryCheck.run(mockContext);
|
|
181
|
+
expect(result.status).toBe('PASS');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should FAIL when registry not found', async () => {
|
|
185
|
+
fs.existsSync.mockReturnValue(false);
|
|
186
|
+
const result = await entityRegistryCheck.run(mockContext);
|
|
187
|
+
expect(result.status).toBe('FAIL');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should WARN when registry is stale (>48h)', async () => {
|
|
191
|
+
fs.existsSync.mockReturnValue(true);
|
|
192
|
+
fs.statSync.mockReturnValue({ mtimeMs: Date.now() - 72 * 60 * 60 * 1000 });
|
|
193
|
+
fs.readFileSync.mockReturnValue('line1\nline2');
|
|
194
|
+
|
|
195
|
+
const result = await entityRegistryCheck.run(mockContext);
|
|
196
|
+
expect(result.status).toBe('WARN');
|
|
197
|
+
expect(result.message).toContain('72h');
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('git-hooks check', () => {
|
|
202
|
+
it('should PASS when both hooks exist', async () => {
|
|
203
|
+
fs.existsSync.mockReturnValue(true);
|
|
204
|
+
const result = await gitHooksCheck.run(mockContext);
|
|
205
|
+
expect(result.status).toBe('PASS');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should WARN when .husky directory missing', async () => {
|
|
209
|
+
fs.existsSync.mockReturnValue(false);
|
|
210
|
+
const result = await gitHooksCheck.run(mockContext);
|
|
211
|
+
expect(result.status).toBe('WARN');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe('core-config check', () => {
|
|
216
|
+
it('should PASS when config has all required sections', async () => {
|
|
217
|
+
fs.existsSync.mockReturnValue(true);
|
|
218
|
+
fs.readFileSync.mockReturnValue('boundary:\n test: true\nproject:\n name: test\nide:\n sync: true');
|
|
219
|
+
|
|
220
|
+
const result = await coreConfigCheck.run(mockContext);
|
|
221
|
+
expect(result.status).toBe('PASS');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('should FAIL when config missing', async () => {
|
|
225
|
+
fs.existsSync.mockReturnValue(false);
|
|
226
|
+
const result = await coreConfigCheck.run(mockContext);
|
|
227
|
+
expect(result.status).toBe('FAIL');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should FAIL when missing required sections', async () => {
|
|
231
|
+
fs.existsSync.mockReturnValue(true);
|
|
232
|
+
fs.readFileSync.mockReturnValue('project:\n name: test');
|
|
233
|
+
|
|
234
|
+
const result = await coreConfigCheck.run(mockContext);
|
|
235
|
+
expect(result.status).toBe('FAIL');
|
|
236
|
+
expect(result.message).toContain('boundary');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
describe('claude-md check', () => {
|
|
241
|
+
it('should PASS when all sections present', async () => {
|
|
242
|
+
fs.existsSync.mockReturnValue(true);
|
|
243
|
+
fs.readFileSync.mockReturnValue(
|
|
244
|
+
'## Constitution\n## Framework vs Project Boundary\n## Sistema de Agentes',
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const result = await claudeMdCheck.run(mockContext);
|
|
248
|
+
expect(result.status).toBe('PASS');
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should WARN when sections missing', async () => {
|
|
252
|
+
fs.existsSync.mockReturnValue(true);
|
|
253
|
+
fs.readFileSync.mockReturnValue('## Constitution\nSome content');
|
|
254
|
+
|
|
255
|
+
const result = await claudeMdCheck.run(mockContext);
|
|
256
|
+
expect(result.status).toBe('WARN');
|
|
257
|
+
expect(result.message).toContain('Missing sections');
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('graph-dashboard check', () => {
|
|
262
|
+
it('should PASS when directory has .js files', async () => {
|
|
263
|
+
fs.existsSync.mockReturnValue(true);
|
|
264
|
+
fs.readdirSync.mockReturnValue(['index.js', 'cli.js']);
|
|
265
|
+
|
|
266
|
+
const result = await graphDashboardCheck.run(mockContext);
|
|
267
|
+
expect(result.status).toBe('PASS');
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should WARN when directory missing', async () => {
|
|
271
|
+
fs.existsSync.mockReturnValue(false);
|
|
272
|
+
const result = await graphDashboardCheck.run(mockContext);
|
|
273
|
+
expect(result.status).toBe('WARN');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('code-intel check', () => {
|
|
278
|
+
// The code-intel check does a real require() of index.js and triggers
|
|
279
|
+
// provider auto-detection. Tests that need provider detection must use
|
|
280
|
+
// the real projectRoot and real fs (jest.requireActual).
|
|
281
|
+
const realFs = jest.requireActual('fs');
|
|
282
|
+
const realProjectRoot = path.join(__dirname, '..', '..', '..', '..', '..');
|
|
283
|
+
|
|
284
|
+
it('should return INFO when code-intel dir does not exist', async () => {
|
|
285
|
+
fs.existsSync.mockReturnValue(false);
|
|
286
|
+
const result = await codeIntelCheck.run(mockContext);
|
|
287
|
+
expect(result.status).toBe('INFO');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should WARN when index.js missing but dir exists', async () => {
|
|
291
|
+
fs.existsSync.mockImplementation((p) => {
|
|
292
|
+
// Dir exists, but index.js does not
|
|
293
|
+
if (p.includes('index.js')) return false;
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
const result = await codeIntelCheck.run(mockContext);
|
|
297
|
+
expect(result.status).toBe('WARN');
|
|
298
|
+
expect(result.message).toContain('index.js not found');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should PASS with RegistryProvider when entity-registry exists', async () => {
|
|
302
|
+
// Use real fs + real project root so require() resolves the actual module
|
|
303
|
+
// and RegistryProvider can load the real entity-registry.yaml
|
|
304
|
+
const realContext = {
|
|
305
|
+
...mockContext,
|
|
306
|
+
projectRoot: realProjectRoot,
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// Temporarily restore real fs for this test
|
|
310
|
+
fs.existsSync.mockImplementation(realFs.existsSync);
|
|
311
|
+
fs.readFileSync.mockImplementation(realFs.readFileSync);
|
|
312
|
+
fs.statSync.mockImplementation(realFs.statSync);
|
|
313
|
+
|
|
314
|
+
// Only run if entity-registry actually exists (skip in CI without registry)
|
|
315
|
+
const registryPath = path.join(realProjectRoot, '.aios-core', 'data', 'entity-registry.yaml');
|
|
316
|
+
if (!realFs.existsSync(registryPath)) {
|
|
317
|
+
return; // skip — no registry available
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const result = await codeIntelCheck.run(realContext);
|
|
321
|
+
expect(result.status).toBe('PASS');
|
|
322
|
+
expect(result.message).toContain('RegistryProvider');
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('should WARN when provider detection fails (no valid registry)', async () => {
|
|
326
|
+
// Use real project root so require() works, but mock registry as empty
|
|
327
|
+
const realContext = {
|
|
328
|
+
...mockContext,
|
|
329
|
+
projectRoot: realProjectRoot,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// Real existsSync for module resolution, mocked readFileSync for empty registry
|
|
333
|
+
fs.existsSync.mockImplementation((p) => {
|
|
334
|
+
if (p.includes('entity-registry.yaml')) return true;
|
|
335
|
+
return realFs.existsSync(p);
|
|
336
|
+
});
|
|
337
|
+
fs.statSync.mockImplementation((p) => {
|
|
338
|
+
if (p.includes('entity-registry.yaml')) return { mtimeMs: Date.now(), size: 10 };
|
|
339
|
+
return realFs.statSync(p);
|
|
340
|
+
});
|
|
341
|
+
fs.readFileSync.mockImplementation((p, enc) => {
|
|
342
|
+
if (typeof p === 'string' && p.includes('entity-registry.yaml')) {
|
|
343
|
+
return 'metadata:\n entityCount: 0';
|
|
344
|
+
}
|
|
345
|
+
return realFs.readFileSync(p, enc);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const result = await codeIntelCheck.run(realContext);
|
|
349
|
+
// Without valid entities, provider won't be available → WARN or INFO
|
|
350
|
+
expect(['WARN', 'INFO']).toContain(result.status);
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe('ide-sync check', () => {
|
|
355
|
+
it('should PASS when counts match', async () => {
|
|
356
|
+
fs.existsSync.mockReturnValue(true);
|
|
357
|
+
fs.readdirSync.mockImplementation((p) => {
|
|
358
|
+
if (p.includes('commands')) return ['dev.md', 'qa.md'];
|
|
359
|
+
return ['dev.md', 'qa.md'];
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const result = await ideSyncCheck.run(mockContext);
|
|
363
|
+
expect(result.status).toBe('PASS');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should WARN when counts mismatch', async () => {
|
|
367
|
+
fs.existsSync.mockReturnValue(true);
|
|
368
|
+
fs.readdirSync.mockImplementation((p) => {
|
|
369
|
+
if (p.includes('commands')) return ['dev.md', 'qa.md', 'pm.md'];
|
|
370
|
+
return ['dev.md', 'qa.md'];
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const result = await ideSyncCheck.run(mockContext);
|
|
374
|
+
expect(result.status).toBe('WARN');
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// === INS-4.8: New checks ===
|
|
379
|
+
|
|
380
|
+
describe('skills-count check', () => {
|
|
381
|
+
it('should PASS when >=7 skills directories with SKILL.md', async () => {
|
|
382
|
+
fs.existsSync.mockReturnValue(true);
|
|
383
|
+
const dirs = Array.from({ length: 8 }, (_, i) => ({
|
|
384
|
+
name: `skill-${i}`,
|
|
385
|
+
isDirectory: () => true,
|
|
386
|
+
isFile: () => false,
|
|
387
|
+
}));
|
|
388
|
+
fs.readdirSync.mockReturnValue(dirs);
|
|
389
|
+
|
|
390
|
+
const result = await skillsCountCheck.run(mockContext);
|
|
391
|
+
expect(result.check).toBe('skills-count');
|
|
392
|
+
expect(result.status).toBe('PASS');
|
|
393
|
+
expect(result.message).toContain('8');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should WARN when <7 skills found', async () => {
|
|
397
|
+
fs.existsSync.mockImplementation((p) => {
|
|
398
|
+
if (p.includes('skills') && !p.includes('SKILL.md')) return true;
|
|
399
|
+
if (p.includes('SKILL.md')) return true;
|
|
400
|
+
return true;
|
|
401
|
+
});
|
|
402
|
+
const dirs = Array.from({ length: 3 }, (_, i) => ({
|
|
403
|
+
name: `skill-${i}`,
|
|
404
|
+
isDirectory: () => true,
|
|
405
|
+
isFile: () => false,
|
|
406
|
+
}));
|
|
407
|
+
fs.readdirSync.mockReturnValue(dirs);
|
|
408
|
+
|
|
409
|
+
const result = await skillsCountCheck.run(mockContext);
|
|
410
|
+
expect(result.status).toBe('WARN');
|
|
411
|
+
expect(result.message).toContain('3/7');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should FAIL when 0 skills found', async () => {
|
|
415
|
+
fs.existsSync.mockImplementation((p) => {
|
|
416
|
+
if (p.includes('SKILL.md')) return false;
|
|
417
|
+
return true;
|
|
418
|
+
});
|
|
419
|
+
const dirs = [{ name: 'empty', isDirectory: () => true, isFile: () => false }];
|
|
420
|
+
fs.readdirSync.mockReturnValue(dirs);
|
|
421
|
+
|
|
422
|
+
const result = await skillsCountCheck.run(mockContext);
|
|
423
|
+
expect(result.status).toBe('FAIL');
|
|
424
|
+
expect(result.fixCommand).toBe('npx aios-core install --force');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
it('should FAIL when skills directory missing', async () => {
|
|
428
|
+
fs.existsSync.mockReturnValue(false);
|
|
429
|
+
const result = await skillsCountCheck.run(mockContext);
|
|
430
|
+
expect(result.status).toBe('FAIL');
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe('commands-count check', () => {
|
|
435
|
+
it('should PASS when >=20 command files', async () => {
|
|
436
|
+
fs.existsSync.mockReturnValue(true);
|
|
437
|
+
const files = Array.from({ length: 22 }, (_, i) => ({
|
|
438
|
+
name: `cmd-${i}.md`,
|
|
439
|
+
isDirectory: () => false,
|
|
440
|
+
isFile: () => true,
|
|
441
|
+
}));
|
|
442
|
+
fs.readdirSync.mockReturnValue(files);
|
|
443
|
+
|
|
444
|
+
const result = await commandsCountCheck.run(mockContext);
|
|
445
|
+
expect(result.check).toBe('commands-count');
|
|
446
|
+
expect(result.status).toBe('PASS');
|
|
447
|
+
expect(result.message).toContain('22');
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should WARN when 12-19 command files', async () => {
|
|
451
|
+
fs.existsSync.mockReturnValue(true);
|
|
452
|
+
const files = Array.from({ length: 15 }, (_, i) => ({
|
|
453
|
+
name: `cmd-${i}.md`,
|
|
454
|
+
isDirectory: () => false,
|
|
455
|
+
isFile: () => true,
|
|
456
|
+
}));
|
|
457
|
+
fs.readdirSync.mockReturnValue(files);
|
|
458
|
+
|
|
459
|
+
const result = await commandsCountCheck.run(mockContext);
|
|
460
|
+
expect(result.status).toBe('WARN');
|
|
461
|
+
expect(result.message).toContain('15/20');
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('should FAIL when <12 command files', async () => {
|
|
465
|
+
fs.existsSync.mockReturnValue(true);
|
|
466
|
+
const files = Array.from({ length: 5 }, (_, i) => ({
|
|
467
|
+
name: `cmd-${i}.md`,
|
|
468
|
+
isDirectory: () => false,
|
|
469
|
+
isFile: () => true,
|
|
470
|
+
}));
|
|
471
|
+
fs.readdirSync.mockReturnValue(files);
|
|
472
|
+
|
|
473
|
+
const result = await commandsCountCheck.run(mockContext);
|
|
474
|
+
expect(result.status).toBe('FAIL');
|
|
475
|
+
expect(result.message).toContain('5');
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it('should FAIL when commands directory missing', async () => {
|
|
479
|
+
fs.existsSync.mockReturnValue(false);
|
|
480
|
+
const result = await commandsCountCheck.run(mockContext);
|
|
481
|
+
expect(result.status).toBe('FAIL');
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('hooks-claude-count check', () => {
|
|
486
|
+
it('should PASS when >=2 hook files and registered', async () => {
|
|
487
|
+
fs.existsSync.mockReturnValue(true);
|
|
488
|
+
const hookFiles = [
|
|
489
|
+
{ name: 'enforce-git-push.cjs', isFile: () => true, isDirectory: () => false },
|
|
490
|
+
{ name: 'pre-commit-check.cjs', isFile: () => true, isDirectory: () => false },
|
|
491
|
+
];
|
|
492
|
+
fs.readdirSync.mockReturnValue(hookFiles);
|
|
493
|
+
const settingsLocal = {
|
|
494
|
+
hooks: {
|
|
495
|
+
PreToolUse: [{ command: 'node .claude/hooks/enforce-git-push.cjs' }],
|
|
496
|
+
PostToolUse: [{ command: 'node .claude/hooks/pre-commit-check.cjs' }],
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
fs.readFileSync.mockReturnValue(JSON.stringify(settingsLocal));
|
|
500
|
+
|
|
501
|
+
const result = await hooksClaudeCountCheck.run(mockContext);
|
|
502
|
+
expect(result.check).toBe('hooks-claude-count');
|
|
503
|
+
expect(result.status).toBe('PASS');
|
|
504
|
+
expect(result.message).toContain('2');
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it('should WARN when hooks present but not registered', async () => {
|
|
508
|
+
fs.existsSync.mockImplementation((p) => {
|
|
509
|
+
if (p.includes('settings.local.json')) return true;
|
|
510
|
+
return true;
|
|
511
|
+
});
|
|
512
|
+
const hookFiles = [
|
|
513
|
+
{ name: 'hook-a.cjs', isFile: () => true, isDirectory: () => false },
|
|
514
|
+
{ name: 'hook-b.cjs', isFile: () => true, isDirectory: () => false },
|
|
515
|
+
];
|
|
516
|
+
fs.readdirSync.mockReturnValue(hookFiles);
|
|
517
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({ hooks: {} }));
|
|
518
|
+
|
|
519
|
+
const result = await hooksClaudeCountCheck.run(mockContext);
|
|
520
|
+
expect(result.status).toBe('WARN');
|
|
521
|
+
expect(result.message).toContain('not registered');
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('should WARN when <2 hook files', async () => {
|
|
525
|
+
fs.existsSync.mockReturnValue(true);
|
|
526
|
+
const hookFiles = [
|
|
527
|
+
{ name: 'single-hook.cjs', isFile: () => true, isDirectory: () => false },
|
|
528
|
+
];
|
|
529
|
+
fs.readdirSync.mockReturnValue(hookFiles);
|
|
530
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({ hooks: {} }));
|
|
531
|
+
|
|
532
|
+
const result = await hooksClaudeCountCheck.run(mockContext);
|
|
533
|
+
expect(result.status).toBe('WARN');
|
|
534
|
+
expect(result.message).toContain('1/2');
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('should FAIL when no hook files found', async () => {
|
|
538
|
+
fs.existsSync.mockReturnValue(true);
|
|
539
|
+
fs.readdirSync.mockReturnValue([]);
|
|
540
|
+
|
|
541
|
+
const result = await hooksClaudeCountCheck.run(mockContext);
|
|
542
|
+
expect(result.status).toBe('FAIL');
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should FAIL when hooks directory missing', async () => {
|
|
546
|
+
fs.existsSync.mockReturnValue(false);
|
|
547
|
+
const result = await hooksClaudeCountCheck.run(mockContext);
|
|
548
|
+
expect(result.status).toBe('FAIL');
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// === INS-4.8: Registry and task validation ===
|
|
553
|
+
|
|
554
|
+
describe('check registry (INS-4.8)', () => {
|
|
555
|
+
it('should load 15 checks total', () => {
|
|
556
|
+
// loadChecks is the real function (not mocked) — verifies registration
|
|
557
|
+
const checks = loadChecks();
|
|
558
|
+
expect(checks).toHaveLength(15);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should include all 3 new checks', () => {
|
|
562
|
+
const checks = loadChecks();
|
|
563
|
+
const names = checks.map((c) => c.name);
|
|
564
|
+
expect(names).toContain('skills-count');
|
|
565
|
+
expect(names).toContain('commands-count');
|
|
566
|
+
expect(names).toContain('hooks-claude-count');
|
|
567
|
+
});
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
describe('health-check.yaml task (INS-4.8)', () => {
|
|
571
|
+
it('should NOT have *doctor alias', () => {
|
|
572
|
+
const realFs = jest.requireActual('fs');
|
|
573
|
+
const yaml = realFs.readFileSync(
|
|
574
|
+
path.join(__dirname, '..', '..', '..', '..', '..', '.aios-core', 'development', 'tasks', 'health-check.yaml'),
|
|
575
|
+
'utf8',
|
|
576
|
+
);
|
|
577
|
+
// Verify *doctor is not in the aliases list (only *hc should be)
|
|
578
|
+
const aliasMatch = yaml.match(/aliases:\s*\n((?:\s+-\s+.*\n)*)/);
|
|
579
|
+
expect(aliasMatch).toBeTruthy();
|
|
580
|
+
expect(aliasMatch[1]).not.toContain('*doctor');
|
|
581
|
+
expect(aliasMatch[1]).toContain('*hc');
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
it('should reference aios doctor --json in instructions', () => {
|
|
585
|
+
const realFs = jest.requireActual('fs');
|
|
586
|
+
const yaml = realFs.readFileSync(
|
|
587
|
+
path.join(__dirname, '..', '..', '..', '..', '..', '.aios-core', 'development', 'tasks', 'health-check.yaml'),
|
|
588
|
+
'utf8',
|
|
589
|
+
);
|
|
590
|
+
expect(yaml).toContain('aios doctor --json');
|
|
591
|
+
expect(yaml).toContain('node bin/aios.js doctor --json');
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should have governance_map with all 15 checks', () => {
|
|
595
|
+
const realFs = jest.requireActual('fs');
|
|
596
|
+
const yaml = realFs.readFileSync(
|
|
597
|
+
path.join(__dirname, '..', '..', '..', '..', '..', '.aios-core', 'development', 'tasks', 'health-check.yaml'),
|
|
598
|
+
'utf8',
|
|
599
|
+
);
|
|
600
|
+
const expectedChecks = [
|
|
601
|
+
'settings-json', 'rules-files', 'agent-memory', 'entity-registry',
|
|
602
|
+
'git-hooks', 'core-config', 'claude-md', 'ide-sync', 'graph-dashboard',
|
|
603
|
+
'code-intel', 'node-version', 'npm-packages', 'skills-count',
|
|
604
|
+
'commands-count', 'hooks-claude-count',
|
|
605
|
+
];
|
|
606
|
+
for (const check of expectedChecks) {
|
|
607
|
+
expect(yaml).toContain(`${check}:`);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
});
|