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,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit Tests: Doctor Orchestrator
|
|
3
|
+
* Story INS-4.1: aios doctor rewrite
|
|
4
|
+
*
|
|
5
|
+
* Tests for options forwarding, output format, and fix/dry-run behavior.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
// Use real modules for orchestrator testing (checks will hit real filesystem)
|
|
11
|
+
const { runDoctorChecks, DOCTOR_VERSION } = require('../../../../../.aios-core/core/doctor');
|
|
12
|
+
|
|
13
|
+
const projectRoot = path.resolve(__dirname, '..', '..', '..', '..', '..');
|
|
14
|
+
|
|
15
|
+
describe('Doctor Orchestrator', () => {
|
|
16
|
+
describe('version', () => {
|
|
17
|
+
it('should export DOCTOR_VERSION as 2.0.0', () => {
|
|
18
|
+
expect(DOCTOR_VERSION).toBe('2.0.0');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('options forwarding (AC1)', () => {
|
|
23
|
+
it('should accept and use options object', async () => {
|
|
24
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
25
|
+
expect(result).toHaveProperty('formatted');
|
|
26
|
+
expect(result).toHaveProperty('data');
|
|
27
|
+
expect(result.data).toHaveProperty('version', '2.0.0');
|
|
28
|
+
expect(result.data).toHaveProperty('summary');
|
|
29
|
+
expect(result.data).toHaveProperty('checks');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should produce JSON when json option is true', async () => {
|
|
33
|
+
const result = await runDoctorChecks({ json: true, projectRoot });
|
|
34
|
+
expect(result.formatted).toBeTruthy();
|
|
35
|
+
|
|
36
|
+
// Should be valid JSON
|
|
37
|
+
const parsed = JSON.parse(result.formatted);
|
|
38
|
+
expect(parsed).toHaveProperty('version');
|
|
39
|
+
expect(parsed).toHaveProperty('summary');
|
|
40
|
+
expect(parsed).toHaveProperty('checks');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should produce text when json option is false', async () => {
|
|
44
|
+
const result = await runDoctorChecks({ json: false, projectRoot });
|
|
45
|
+
expect(result.formatted).toContain('AIOS Doctor');
|
|
46
|
+
expect(result.formatted).toContain('Summary:');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('15 checks (AC2 + INS-4.8)', () => {
|
|
51
|
+
it('should run exactly 15 checks', async () => {
|
|
52
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
53
|
+
expect(result.data.checks).toHaveLength(15);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should return valid status for each check', async () => {
|
|
57
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
58
|
+
const validStatuses = ['PASS', 'WARN', 'FAIL', 'INFO'];
|
|
59
|
+
|
|
60
|
+
for (const check of result.data.checks) {
|
|
61
|
+
expect(validStatuses).toContain(check.status);
|
|
62
|
+
expect(check).toHaveProperty('check');
|
|
63
|
+
expect(check).toHaveProperty('message');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should include all expected check names', async () => {
|
|
68
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
69
|
+
const checkNames = result.data.checks.map((c) => c.check);
|
|
70
|
+
|
|
71
|
+
expect(checkNames).toContain('settings-json');
|
|
72
|
+
expect(checkNames).toContain('rules-files');
|
|
73
|
+
expect(checkNames).toContain('agent-memory');
|
|
74
|
+
expect(checkNames).toContain('entity-registry');
|
|
75
|
+
expect(checkNames).toContain('git-hooks');
|
|
76
|
+
expect(checkNames).toContain('core-config');
|
|
77
|
+
expect(checkNames).toContain('claude-md');
|
|
78
|
+
expect(checkNames).toContain('ide-sync');
|
|
79
|
+
expect(checkNames).toContain('graph-dashboard');
|
|
80
|
+
expect(checkNames).toContain('code-intel');
|
|
81
|
+
expect(checkNames).toContain('node-version');
|
|
82
|
+
expect(checkNames).toContain('npm-packages');
|
|
83
|
+
expect(checkNames).toContain('skills-count');
|
|
84
|
+
expect(checkNames).toContain('commands-count');
|
|
85
|
+
expect(checkNames).toContain('hooks-claude-count');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('summary (AC3)', () => {
|
|
90
|
+
it('should have pass/warn/fail/info counts that sum to 15', async () => {
|
|
91
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
92
|
+
const { pass, warn, fail, info } = result.data.summary;
|
|
93
|
+
expect(pass + warn + fail + info).toBe(15);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should include Summary line in text output', async () => {
|
|
97
|
+
const result = await runDoctorChecks({ projectRoot });
|
|
98
|
+
expect(result.formatted).toMatch(/Summary: \d+ PASS \| \d+ WARN \| \d+ FAIL \| \d+ INFO/);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('--dry-run (AC4)', () => {
|
|
103
|
+
it('should include fixResults when dryRun is true', async () => {
|
|
104
|
+
const result = await runDoctorChecks({ dryRun: true, projectRoot });
|
|
105
|
+
expect(result.data).toHaveProperty('fixResults');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should not produce file changes with dryRun', async () => {
|
|
109
|
+
const result = await runDoctorChecks({ dryRun: true, projectRoot });
|
|
110
|
+
if (result.data.fixResults) {
|
|
111
|
+
for (const fr of result.data.fixResults) {
|
|
112
|
+
expect(fr.applied).toBe(false);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('JSON output schema (AC3)', () => {
|
|
119
|
+
it('should match expected JSON schema', async () => {
|
|
120
|
+
const result = await runDoctorChecks({ json: true, projectRoot });
|
|
121
|
+
const parsed = JSON.parse(result.formatted);
|
|
122
|
+
|
|
123
|
+
expect(parsed).toHaveProperty('version');
|
|
124
|
+
expect(parsed).toHaveProperty('timestamp');
|
|
125
|
+
expect(parsed).toHaveProperty('summary');
|
|
126
|
+
expect(parsed.summary).toHaveProperty('pass');
|
|
127
|
+
expect(parsed.summary).toHaveProperty('warn');
|
|
128
|
+
expect(parsed.summary).toHaveProperty('fail');
|
|
129
|
+
expect(parsed.summary).toHaveProperty('info');
|
|
130
|
+
expect(parsed).toHaveProperty('checks');
|
|
131
|
+
expect(Array.isArray(parsed.checks)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Entity Registry Bootstrap Tests (Story INS-4.6)
|
|
5
|
+
*
|
|
6
|
+
* Validates that the installer calls populate-entity-registry.js during install,
|
|
7
|
+
* handles failures gracefully, and that aios doctor can verify the registry.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const WIZARD_PATH = path.join(__dirname, '..', '..', 'src', 'wizard', 'index.js');
|
|
14
|
+
const POPULATE_SCRIPT = path.join(
|
|
15
|
+
__dirname, '..', '..', '..', '..', '.aios-core', 'development', 'scripts', 'populate-entity-registry.js'
|
|
16
|
+
);
|
|
17
|
+
const DOCTOR_CHECK = path.join(
|
|
18
|
+
__dirname, '..', '..', '..', '..', '.aios-core', 'core', 'doctor', 'checks', 'entity-registry.js'
|
|
19
|
+
);
|
|
20
|
+
const REGISTRY_PATH = path.join(
|
|
21
|
+
__dirname, '..', '..', '..', '..', '.aios-core', 'data', 'entity-registry.yaml'
|
|
22
|
+
);
|
|
23
|
+
const PRE_PUSH_HOOK = path.join(__dirname, '..', '..', '..', '..', '.husky', 'pre-push');
|
|
24
|
+
const IDS_PRE_PUSH = path.join(
|
|
25
|
+
__dirname, '..', '..', '..', '..', '.aios-core', 'hooks', 'ids-pre-push.js'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
describe('Entity Registry Bootstrap (Story INS-4.6)', () => {
|
|
29
|
+
let wizardSource;
|
|
30
|
+
|
|
31
|
+
beforeAll(() => {
|
|
32
|
+
wizardSource = fs.readFileSync(WIZARD_PATH, 'utf8');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('AC1: Bootstrap called during install', () => {
|
|
36
|
+
test('wizard calls populate-entity-registry.js', () => {
|
|
37
|
+
expect(wizardSource).toContain('populate-entity-registry.js');
|
|
38
|
+
expect(wizardSource).toContain('Bootstrapping entity registry');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('bootstrap runs after .aios-core/ copy (injection order)', () => {
|
|
42
|
+
const aiosCoreIdx = wizardSource.indexOf('AIOS core installed');
|
|
43
|
+
const bootstrapIdx = wizardSource.indexOf('Bootstrapping entity registry');
|
|
44
|
+
const envConfigIdx = wizardSource.indexOf('Configuring environment');
|
|
45
|
+
|
|
46
|
+
// Bootstrap must come after aios-core install
|
|
47
|
+
expect(bootstrapIdx).toBeGreaterThan(aiosCoreIdx);
|
|
48
|
+
// Bootstrap must come before environment configuration
|
|
49
|
+
expect(bootstrapIdx).toBeLessThan(envConfigIdx);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('bootstrap has try/catch — failure does not abort install', () => {
|
|
53
|
+
// Verify the bootstrap block is wrapped in try/catch
|
|
54
|
+
expect(wizardSource).toContain("answers.entityRegistryStatus = 'failed'");
|
|
55
|
+
expect(wizardSource).toContain('Entity registry bootstrap failed');
|
|
56
|
+
// Should warn, not throw
|
|
57
|
+
expect(wizardSource).toContain("run 'aios doctor' post-install");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('bootstrap uses 30s timeout guard', () => {
|
|
61
|
+
expect(wizardSource).toContain('timeout: 30000');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('bootstrap reports entity count on success', () => {
|
|
65
|
+
expect(wizardSource).toContain("answers.entityRegistryStatus = 'populated'");
|
|
66
|
+
expect(wizardSource).toContain('answers.entityRegistryCount');
|
|
67
|
+
expect(wizardSource).toContain('answers.entityRegistryMs');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('bootstrap handles missing script gracefully', () => {
|
|
71
|
+
expect(wizardSource).toContain("answers.entityRegistryStatus = 'skipped'");
|
|
72
|
+
expect(wizardSource).toContain('Entity registry script not found');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('AC2: Sanity check (relative threshold)', () => {
|
|
77
|
+
test('aios doctor entity-registry check exists', () => {
|
|
78
|
+
expect(fs.existsSync(DOCTOR_CHECK)).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('doctor check uses relative validation (file exists + non-empty), not fixed threshold', () => {
|
|
82
|
+
const checkSource = fs.readFileSync(DOCTOR_CHECK, 'utf8');
|
|
83
|
+
|
|
84
|
+
// Should check file existence
|
|
85
|
+
expect(checkSource).toContain('existsSync');
|
|
86
|
+
// Should NOT have hardcoded threshold like >= 500
|
|
87
|
+
expect(checkSource).not.toMatch(/>= ?\d{3}/);
|
|
88
|
+
// Should report line count (relative measure)
|
|
89
|
+
expect(checkSource).toContain('lineCount');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('doctor check validates recency (mtime)', () => {
|
|
93
|
+
const checkSource = fs.readFileSync(DOCTOR_CHECK, 'utf8');
|
|
94
|
+
expect(checkSource).toContain('mtimeMs');
|
|
95
|
+
expect(checkSource).toContain('MAX_AGE_MS');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('AC3: No duplication with pre-push hook', () => {
|
|
100
|
+
test('pre-push hook calls ids-pre-push.js (incremental), not populate script', () => {
|
|
101
|
+
const hookContent = fs.readFileSync(PRE_PUSH_HOOK, 'utf8');
|
|
102
|
+
expect(hookContent).toContain('ids-pre-push.js');
|
|
103
|
+
expect(hookContent).not.toContain('populate-entity-registry.js');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('ids-pre-push.js uses RegistryUpdater.processChanges (incremental)', () => {
|
|
107
|
+
const idsSource = fs.readFileSync(IDS_PRE_PUSH, 'utf8');
|
|
108
|
+
expect(idsSource).toContain('RegistryUpdater');
|
|
109
|
+
expect(idsSource).toContain('processChanges');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('bootstrap uses populate-entity-registry.js (full scan), distinct from incremental', () => {
|
|
113
|
+
// Wizard calls populate-entity-registry.js
|
|
114
|
+
expect(wizardSource).toContain('populate-entity-registry.js');
|
|
115
|
+
// Wizard does NOT call ids-pre-push.js
|
|
116
|
+
expect(wizardSource).not.toContain('ids-pre-push.js');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('AC4: Performance guard', () => {
|
|
121
|
+
test('populate-entity-registry.js exists and is executable', () => {
|
|
122
|
+
expect(fs.existsSync(POPULATE_SCRIPT)).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('populate script has programmatic API (module.exports)', () => {
|
|
126
|
+
const scriptSource = fs.readFileSync(POPULATE_SCRIPT, 'utf8');
|
|
127
|
+
expect(scriptSource).toContain('module.exports');
|
|
128
|
+
expect(scriptSource).toContain('populate');
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('measured runtime is well under 15s threshold', () => {
|
|
132
|
+
// Run the actual script and measure time
|
|
133
|
+
const { execSync } = require('child_process');
|
|
134
|
+
const projectRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
135
|
+
const start = Date.now();
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
execSync(`node "${POPULATE_SCRIPT}"`, {
|
|
139
|
+
cwd: projectRoot,
|
|
140
|
+
encoding: 'utf8',
|
|
141
|
+
timeout: 30000,
|
|
142
|
+
stdio: 'pipe',
|
|
143
|
+
});
|
|
144
|
+
} catch {
|
|
145
|
+
// Script may fail in some environments — that's ok for timing test
|
|
146
|
+
console.log('SKIP: populate script execution failed — timing not measured');
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const elapsed = Date.now() - start;
|
|
151
|
+
// Must be under 15s (AC4 threshold)
|
|
152
|
+
expect(elapsed).toBeLessThan(15000);
|
|
153
|
+
console.log(`Measured runtime: ${(elapsed / 1000).toFixed(2)}s`);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('AC5: Post-bootstrap verification', () => {
|
|
158
|
+
test('entity-registry.yaml exists after bootstrap', () => {
|
|
159
|
+
expect(fs.existsSync(REGISTRY_PATH)).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('entity-registry.yaml has at least 1 entity (non-empty)', () => {
|
|
163
|
+
const content = fs.readFileSync(REGISTRY_PATH, 'utf8');
|
|
164
|
+
const match = content.match(/entityCount:\s*(\d+)/);
|
|
165
|
+
expect(match).not.toBeNull();
|
|
166
|
+
const count = parseInt(match[1], 10);
|
|
167
|
+
expect(count).toBeGreaterThan(0);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// NOTE: This test depends on AC4 "measured runtime" test running first,
|
|
171
|
+
// which executes populate-entity-registry.js and refreshes the file mtime.
|
|
172
|
+
test('entity-registry.yaml updatedAt is recent (within 5 minutes)', () => {
|
|
173
|
+
const stat = fs.statSync(REGISTRY_PATH);
|
|
174
|
+
const ageMs = Date.now() - stat.mtimeMs;
|
|
175
|
+
const FIVE_MINUTES = 5 * 60 * 1000;
|
|
176
|
+
expect(ageMs).toBeLessThan(FIVE_MINUTES);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('aios doctor entity-registry check passes on current registry', async () => {
|
|
180
|
+
const { run } = require(DOCTOR_CHECK);
|
|
181
|
+
const projectRoot = path.join(__dirname, '..', '..', '..', '..');
|
|
182
|
+
const result = await run({ projectRoot });
|
|
183
|
+
expect(result.status).toBe('PASS');
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
generate,
|
|
9
|
+
validateBoundaryPath,
|
|
10
|
+
readBoundaryConfig,
|
|
11
|
+
expandProtectedPaths,
|
|
12
|
+
expandExceptionPaths,
|
|
13
|
+
generatePermissions,
|
|
14
|
+
writeSettingsJson,
|
|
15
|
+
} = require('../../../../../.aios-core/infrastructure/scripts/generate-settings-json');
|
|
16
|
+
|
|
17
|
+
function createTempProject(boundary, existingSettings) {
|
|
18
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gen-settings-'));
|
|
19
|
+
|
|
20
|
+
// Create core-config.yaml with boundary section
|
|
21
|
+
const aiosCoreDir = path.join(tmpDir, '.aios-core');
|
|
22
|
+
fs.mkdirSync(aiosCoreDir, { recursive: true });
|
|
23
|
+
|
|
24
|
+
const yamlContent = [
|
|
25
|
+
'boundary:',
|
|
26
|
+
` frameworkProtection: ${boundary.frameworkProtection}`,
|
|
27
|
+
' protected:',
|
|
28
|
+
...boundary.protected.map(p => ` - ${p}`),
|
|
29
|
+
' exceptions:',
|
|
30
|
+
...boundary.exceptions.map(p => ` - ${p}`),
|
|
31
|
+
].join('\n') + '\n';
|
|
32
|
+
|
|
33
|
+
fs.writeFileSync(path.join(tmpDir, '.aios-core', 'core-config.yaml'), yamlContent, 'utf8');
|
|
34
|
+
|
|
35
|
+
// Create directory structure for expansion tests
|
|
36
|
+
if (boundary.protected.includes('.aios-core/core/**')) {
|
|
37
|
+
const coreDir = path.join(tmpDir, '.aios-core', 'core');
|
|
38
|
+
fs.mkdirSync(coreDir, { recursive: true });
|
|
39
|
+
fs.mkdirSync(path.join(coreDir, 'utils'), { recursive: true });
|
|
40
|
+
fs.mkdirSync(path.join(coreDir, 'events'), { recursive: true });
|
|
41
|
+
fs.writeFileSync(path.join(coreDir, 'index.js'), '', 'utf8');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Create .claude directory and optionally existing settings
|
|
45
|
+
const claudeDir = path.join(tmpDir, '.claude');
|
|
46
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
if (existingSettings) {
|
|
49
|
+
fs.writeFileSync(
|
|
50
|
+
path.join(claudeDir, 'settings.json'),
|
|
51
|
+
JSON.stringify(existingSettings, null, 2) + '\n',
|
|
52
|
+
'utf8'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return tmpDir;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function cleanupTempProject(tmpDir) {
|
|
60
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('generate-settings-json', () => {
|
|
64
|
+
describe('readBoundaryConfig', () => {
|
|
65
|
+
test('reads boundary config from core-config.yaml', () => {
|
|
66
|
+
const tmpDir = createTempProject({
|
|
67
|
+
frameworkProtection: true,
|
|
68
|
+
protected: ['.aios-core/core/**', 'bin/aios.js'],
|
|
69
|
+
exceptions: ['.aios-core/data/**'],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const config = readBoundaryConfig(tmpDir);
|
|
74
|
+
|
|
75
|
+
expect(config.frameworkProtection).toBe(true);
|
|
76
|
+
expect(config.protected).toContain('.aios-core/core/**');
|
|
77
|
+
expect(config.protected).toContain('bin/aios.js');
|
|
78
|
+
expect(config.exceptions).toContain('.aios-core/data/**');
|
|
79
|
+
} finally {
|
|
80
|
+
cleanupTempProject(tmpDir);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('throws when core-config.yaml not found', () => {
|
|
85
|
+
expect(() => readBoundaryConfig('/nonexistent/path')).toThrow('core-config.yaml not found');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('rejects path traversal in protected paths', () => {
|
|
89
|
+
const tmpDir = createTempProject({
|
|
90
|
+
frameworkProtection: true,
|
|
91
|
+
protected: ['../../etc/passwd'],
|
|
92
|
+
exceptions: [],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
expect(() => readBoundaryConfig(tmpDir)).toThrow('Path traversal detected');
|
|
97
|
+
} finally {
|
|
98
|
+
cleanupTempProject(tmpDir);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('rejects absolute paths in boundary config', () => {
|
|
103
|
+
const tmpDir = createTempProject({
|
|
104
|
+
frameworkProtection: true,
|
|
105
|
+
protected: ['/etc/passwd'],
|
|
106
|
+
exceptions: [],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
expect(() => readBoundaryConfig(tmpDir)).toThrow('Absolute path not allowed');
|
|
111
|
+
} finally {
|
|
112
|
+
cleanupTempProject(tmpDir);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('generatePermissions — frameworkProtection: true', () => {
|
|
118
|
+
test('generates deny rules covering all protected paths', () => {
|
|
119
|
+
const tmpDir = createTempProject({
|
|
120
|
+
frameworkProtection: true,
|
|
121
|
+
protected: ['.aios-core/core/**', '.aios-core/infrastructure/**', 'bin/aios.js'],
|
|
122
|
+
exceptions: ['.aios-core/data/**'],
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Create infrastructure dir (no expansion for non-core paths)
|
|
126
|
+
fs.mkdirSync(path.join(tmpDir, '.aios-core', 'infrastructure'), { recursive: true });
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const boundary = readBoundaryConfig(tmpDir);
|
|
130
|
+
const permissions = generatePermissions(boundary, tmpDir);
|
|
131
|
+
|
|
132
|
+
// Should have deny rules for core subdirs (events/**, utils/**, index.js) + infrastructure/** + bin/aios.js
|
|
133
|
+
expect(permissions.deny.length).toBeGreaterThan(0);
|
|
134
|
+
|
|
135
|
+
// Core expansion: events/**, utils/**, index.js → 6 deny rules (3 paths x 2 tools)
|
|
136
|
+
expect(permissions.deny).toContain('Edit(.aios-core/core/events/**)');
|
|
137
|
+
expect(permissions.deny).toContain('Write(.aios-core/core/events/**)');
|
|
138
|
+
expect(permissions.deny).toContain('Edit(.aios-core/core/utils/**)');
|
|
139
|
+
expect(permissions.deny).toContain('Write(.aios-core/core/utils/**)');
|
|
140
|
+
expect(permissions.deny).toContain('Edit(.aios-core/core/index.js)');
|
|
141
|
+
expect(permissions.deny).toContain('Write(.aios-core/core/index.js)');
|
|
142
|
+
|
|
143
|
+
// Non-core paths stay as globs
|
|
144
|
+
expect(permissions.deny).toContain('Edit(.aios-core/infrastructure/**)');
|
|
145
|
+
expect(permissions.deny).toContain('Write(.aios-core/infrastructure/**)');
|
|
146
|
+
expect(permissions.deny).toContain('Edit(bin/aios.js)');
|
|
147
|
+
expect(permissions.deny).toContain('Write(bin/aios.js)');
|
|
148
|
+
|
|
149
|
+
// Allow rules from exceptions
|
|
150
|
+
expect(permissions.allow).toContain('Edit(.aios-core/data/**)');
|
|
151
|
+
expect(permissions.allow).toContain('Write(.aios-core/data/**)');
|
|
152
|
+
expect(permissions.allow).toContain('Read(.aios-core/**)');
|
|
153
|
+
} finally {
|
|
154
|
+
cleanupTempProject(tmpDir);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('all 9 protected paths from core-config produce deny rules', () => {
|
|
159
|
+
const projectRoot = path.resolve(__dirname, '../../../../..');
|
|
160
|
+
const boundary = readBoundaryConfig(projectRoot);
|
|
161
|
+
// Force frameworkProtection: true for this test (core-config may have it disabled for contributor mode)
|
|
162
|
+
boundary.frameworkProtection = true;
|
|
163
|
+
const permissions = generatePermissions(boundary, projectRoot);
|
|
164
|
+
|
|
165
|
+
// Verify all 9 config paths are covered
|
|
166
|
+
const protectedRoots = [
|
|
167
|
+
'.aios-core/core/',
|
|
168
|
+
'.aios-core/development/tasks/',
|
|
169
|
+
'.aios-core/development/templates/',
|
|
170
|
+
'.aios-core/development/checklists/',
|
|
171
|
+
'.aios-core/development/workflows/',
|
|
172
|
+
'.aios-core/infrastructure/',
|
|
173
|
+
'.aios-core/constitution.md',
|
|
174
|
+
'bin/aios.js',
|
|
175
|
+
'bin/aios-init.js',
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
for (const root of protectedRoots) {
|
|
179
|
+
const hasDenyRule = permissions.deny.some(r => r.includes(root));
|
|
180
|
+
expect(hasDenyRule).toBe(true);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Verify deny rules use only Edit and Write (no MultiEdit)
|
|
184
|
+
for (const rule of permissions.deny) {
|
|
185
|
+
expect(rule).toMatch(/^(Edit|Write)\(/);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('generatePermissions — frameworkProtection: false', () => {
|
|
191
|
+
test('produces no boundary deny rules', () => {
|
|
192
|
+
const boundary = {
|
|
193
|
+
frameworkProtection: false,
|
|
194
|
+
protected: ['.aios-core/core/**', '.aios-core/infrastructure/**'],
|
|
195
|
+
exceptions: ['.aios-core/data/**'],
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const permissions = generatePermissions(boundary, '/tmp');
|
|
199
|
+
|
|
200
|
+
expect(permissions.deny).toEqual([]);
|
|
201
|
+
expect(permissions.allow).toEqual([]);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('idempotency', () => {
|
|
206
|
+
test('running generator twice produces identical output', () => {
|
|
207
|
+
const tmpDir = createTempProject({
|
|
208
|
+
frameworkProtection: true,
|
|
209
|
+
protected: ['.aios-core/core/**', 'bin/aios.js'],
|
|
210
|
+
exceptions: ['.aios-core/data/**'],
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
try {
|
|
214
|
+
// First run
|
|
215
|
+
generate(tmpDir);
|
|
216
|
+
const firstRun = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
217
|
+
|
|
218
|
+
// Second run
|
|
219
|
+
generate(tmpDir);
|
|
220
|
+
const secondRun = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
221
|
+
|
|
222
|
+
expect(firstRun).toBe(secondRun);
|
|
223
|
+
} finally {
|
|
224
|
+
cleanupTempProject(tmpDir);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test('JSON output is valid and parseable', () => {
|
|
229
|
+
const tmpDir = createTempProject({
|
|
230
|
+
frameworkProtection: true,
|
|
231
|
+
protected: ['.aios-core/core/**'],
|
|
232
|
+
exceptions: ['.aios-core/data/**'],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
generate(tmpDir);
|
|
237
|
+
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
238
|
+
const parsed = JSON.parse(content);
|
|
239
|
+
|
|
240
|
+
expect(parsed).toHaveProperty('permissions');
|
|
241
|
+
expect(parsed.permissions).toHaveProperty('deny');
|
|
242
|
+
expect(parsed.permissions).toHaveProperty('allow');
|
|
243
|
+
expect(Array.isArray(parsed.permissions.deny)).toBe(true);
|
|
244
|
+
expect(Array.isArray(parsed.permissions.allow)).toBe(true);
|
|
245
|
+
} finally {
|
|
246
|
+
cleanupTempProject(tmpDir);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('section preservation', () => {
|
|
252
|
+
test('preserves user-set language key after generator run', () => {
|
|
253
|
+
const tmpDir = createTempProject(
|
|
254
|
+
{
|
|
255
|
+
frameworkProtection: true,
|
|
256
|
+
protected: ['bin/aios.js'],
|
|
257
|
+
exceptions: [],
|
|
258
|
+
},
|
|
259
|
+
{ language: 'pt', customSetting: true }
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
generate(tmpDir);
|
|
264
|
+
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
265
|
+
const parsed = JSON.parse(content);
|
|
266
|
+
|
|
267
|
+
expect(parsed.language).toBe('pt');
|
|
268
|
+
expect(parsed.customSetting).toBe(true);
|
|
269
|
+
expect(parsed.permissions).toBeDefined();
|
|
270
|
+
expect(parsed.permissions.deny.length).toBeGreaterThan(0);
|
|
271
|
+
} finally {
|
|
272
|
+
cleanupTempProject(tmpDir);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('frameworkProtection false preserves user settings and removes permissions', () => {
|
|
277
|
+
const tmpDir = createTempProject(
|
|
278
|
+
{
|
|
279
|
+
frameworkProtection: false,
|
|
280
|
+
protected: ['bin/aios.js'],
|
|
281
|
+
exceptions: [],
|
|
282
|
+
},
|
|
283
|
+
{ language: 'pt', permissions: { deny: ['old-rule'], allow: [] } }
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
generate(tmpDir);
|
|
288
|
+
const content = fs.readFileSync(path.join(tmpDir, '.claude', 'settings.json'), 'utf8');
|
|
289
|
+
const parsed = JSON.parse(content);
|
|
290
|
+
|
|
291
|
+
expect(parsed.language).toBe('pt');
|
|
292
|
+
expect(parsed.permissions).toBeUndefined();
|
|
293
|
+
} finally {
|
|
294
|
+
cleanupTempProject(tmpDir);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe('CLI entry point', () => {
|
|
300
|
+
test('module exports required functions', () => {
|
|
301
|
+
expect(typeof generate).toBe('function');
|
|
302
|
+
expect(typeof readBoundaryConfig).toBe('function');
|
|
303
|
+
expect(typeof expandProtectedPaths).toBe('function');
|
|
304
|
+
expect(typeof expandExceptionPaths).toBe('function');
|
|
305
|
+
expect(typeof generatePermissions).toBe('function');
|
|
306
|
+
expect(typeof writeSettingsJson).toBe('function');
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|