@geraldmaron/construct 1.4.1 → 1.5.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/.env.example +36 -0
- package/README.md +41 -7
- package/bin/construct +1029 -65
- package/bin/construct-postinstall.mjs +27 -2
- package/config/tag-vocabulary.json +264 -0
- package/examples/distribution/sources/deck-one-pager.md +1 -1
- package/lib/acp/server.mjs +21 -7
- package/lib/adapters-sync.mjs +2 -1
- package/lib/audit-trail.mjs +185 -2
- package/lib/beads/auto-close.mjs +2 -1
- package/lib/beads/drift.mjs +2 -1
- package/lib/bridges/copilot-proxy.mjs +20 -5
- package/lib/certification/skill-inventory.mjs +10 -1
- package/lib/cli/approvals.mjs +147 -0
- package/lib/cli-commands.mjs +105 -14
- package/lib/comment-lint.mjs +127 -8
- package/lib/config/schema.mjs +7 -30
- package/lib/config/source-target-registry.mjs +70 -0
- package/lib/config/source-targets.mjs +179 -205
- package/lib/contracts/coverage.mjs +77 -0
- package/lib/contracts/validate.mjs +102 -13
- package/lib/contracts/violation-log.mjs +50 -4
- package/lib/db/migrate.mjs +69 -0
- package/lib/db/migrations/001_orchestration_runs.sql +9 -0
- package/lib/db/migrations/002_queue_provider.sql +50 -0
- package/lib/db/migrations/003_worker_registry.sql +17 -0
- package/lib/db/migrations/004_trace_events.sql +16 -0
- package/lib/db/migrations/005_shared_memory.sql +15 -0
- package/lib/db/migrations/006_orchestration_runs_tenant.sql +5 -0
- package/lib/decisions/enforced-baseline.json +0 -2
- package/lib/decisions/registry.mjs +3 -2
- package/lib/deployment/parity-contract.mjs +1 -1
- package/lib/deployment-mode.mjs +78 -2
- package/lib/diagram-export.mjs +10 -1
- package/lib/distill.mjs +5 -2
- package/lib/docs-verify.mjs +2 -1
- package/lib/doctor/cli.mjs +1 -0
- package/lib/doctor/command-on-path.mjs +24 -0
- package/lib/doctor/diagnosis.mjs +109 -0
- package/lib/doctor/embedding-health.mjs +50 -0
- package/lib/doctor/engine-health.mjs +93 -0
- package/lib/doctor/graph-validate.mjs +47 -0
- package/lib/doctor/index.mjs +23 -4
- package/lib/doctor/sidecar-providers.mjs +56 -0
- package/lib/doctor/watchers/consistency.mjs +93 -1
- package/lib/doctor/watchers/cx-budget.mjs +1 -1
- package/lib/doctor/watchers/graph-staleness.mjs +1 -1
- package/lib/doctor/watchers/mcp-protocol.mjs +17 -0
- package/lib/doctor/watchers/oracle-liveness.mjs +92 -0
- package/lib/doctor/watchers/orchestration-runs.mjs +108 -0
- package/lib/doctor/watchers/provider-breaker.mjs +78 -0
- package/lib/document-export.mjs +52 -27
- package/lib/document-extract/docling-client.mjs +9 -5
- package/lib/document-extract/docling-sidecar.py +30 -0
- package/lib/document-extract.mjs +1 -1
- package/lib/document-ingest.mjs +9 -0
- package/lib/embed/approval-queue.mjs +184 -88
- package/lib/embed/authority-guard.mjs +65 -2
- package/lib/embed/capability-jobs.mjs +365 -0
- package/lib/embed/capability-lifecycle.mjs +219 -0
- package/lib/embed/capability-loader.mjs +303 -0
- package/lib/embed/capability-runtime.mjs +58 -0
- package/lib/embed/cli.mjs +185 -8
- package/lib/embed/config.mjs +4 -0
- package/lib/embed/daemon.mjs +125 -6
- package/lib/embed/demand-fetch.mjs +190 -54
- package/lib/embed/inbox.mjs +12 -10
- package/lib/embed/presets/ops-triage.mjs +236 -0
- package/lib/embed/presets/pm-feedback.mjs +341 -0
- package/lib/embed/presets/tpm.mjs +401 -0
- package/lib/embed/providers/jira.mjs +72 -1
- package/lib/embed/providers/registry.mjs +140 -33
- package/lib/embed/worker.mjs +5 -0
- package/lib/embedded-contract/capability.mjs +4 -0
- package/lib/embedded-contract/execution.mjs +1 -1
- package/lib/embedded-contract/model-resolve.mjs +53 -3
- package/lib/embedded-contract/workflow-defs.mjs +48 -73
- package/lib/embedded-contract/workflow-invoke.mjs +144 -4
- package/lib/embedded-contract/workflows/architecture-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/data-structure.manifest.json +14 -0
- package/lib/embedded-contract/workflows/evidence-ingest.manifest.json +14 -0
- package/lib/embedded-contract/workflows/memo-draft.manifest.json +14 -0
- package/lib/embedded-contract/workflows/operations-triage.manifest.json +18 -0
- package/lib/embedded-contract/workflows/operations.manifest.json +18 -0
- package/lib/embedded-contract/workflows/pm-feedback.manifest.json +18 -0
- package/lib/embedded-contract/workflows/prd-draft.manifest.json +15 -0
- package/lib/embedded-contract/workflows/proposal-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/research-synthesis.manifest.json +14 -0
- package/lib/embedded-contract/workflows/risk-review.manifest.json +15 -0
- package/lib/embedded-contract/workflows/structure-notes.manifest.json +14 -0
- package/lib/embedded-contract/workflows/transcript-process.manifest.json +14 -0
- package/lib/embedded-contract/workflows/triage.manifest.json +14 -0
- package/lib/env-config.mjs +60 -11
- package/lib/export-validate.mjs +34 -2
- package/lib/extensions/index.mjs +27 -0
- package/lib/extensions/loader.mjs +120 -0
- package/lib/extensions/manifest-schema.mjs +141 -0
- package/lib/extensions/manifests/anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-confluence.manifest.json +12 -0
- package/lib/extensions/manifests/atlassian-jira.manifest.json +53 -0
- package/lib/extensions/manifests/directory.manifest.json +12 -0
- package/lib/extensions/manifests/docling.manifest.json +37 -0
- package/lib/extensions/manifests/echo.manifest.json +8 -0
- package/lib/extensions/manifests/feedback.manifest.json +12 -0
- package/lib/extensions/manifests/github-copilot.manifest.json +12 -0
- package/lib/extensions/manifests/github.manifest.json +52 -0
- package/lib/extensions/manifests/linear.manifest.json +50 -0
- package/lib/extensions/manifests/local.manifest.json +12 -0
- package/lib/extensions/manifests/ollama.manifest.json +12 -0
- package/lib/extensions/manifests/openai.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-anthropic.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-deepseek.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-google.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-llama.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter-qwen.manifest.json +12 -0
- package/lib/extensions/manifests/openrouter.manifest.json +12 -0
- package/lib/extensions/manifests/postgres.manifest.json +12 -0
- package/lib/extensions/manifests/salesforce.manifest.json +12 -0
- package/lib/extensions/manifests/slack.manifest.json +58 -0
- package/lib/extensions/manifests/whisper.manifest.json +36 -0
- package/lib/extensions/validate.mjs +175 -0
- package/lib/features.mjs +13 -9
- package/lib/flows/checkpoint.mjs +184 -0
- package/lib/flows/constants.mjs +27 -0
- package/lib/flows/define.mjs +146 -0
- package/lib/flows/engine.mjs +249 -0
- package/lib/flows/errors.mjs +19 -0
- package/lib/flows/index.mjs +27 -0
- package/lib/flows/joins.mjs +18 -0
- package/lib/flows/schema.mjs +90 -0
- package/lib/flows/state.mjs +31 -0
- package/lib/frameworks/loader.mjs +99 -0
- package/lib/frameworks/schema.mjs +157 -0
- package/lib/graph/build-from-corpus.mjs +67 -0
- package/lib/graph/build-from-embed.mjs +82 -0
- package/lib/graph/build-from-registry.mjs +164 -3
- package/lib/graph/cli.mjs +456 -12
- package/lib/graph/gap-queries.mjs +156 -0
- package/lib/graph/gaps.mjs +41 -0
- package/lib/graph/impacted.mjs +129 -0
- package/lib/graph/runtime-evidence.mjs +177 -0
- package/lib/graph/security-coverage.mjs +113 -0
- package/lib/graph/staleness.mjs +241 -10
- package/lib/graph/store.mjs +24 -7
- package/lib/graph/validate.mjs +161 -0
- package/lib/headhunt.mjs +9 -8
- package/lib/health-check.mjs +15 -7
- package/lib/hook-health.mjs +1 -1
- package/lib/hooks/agent-tracker.mjs +10 -4
- package/lib/hooks/edit-guard.mjs +3 -3
- package/lib/hooks/graph-impact-advisory.mjs +2 -2
- package/lib/hooks/guard-bash.mjs +41 -15
- package/lib/hooks/mcp-health-check.mjs +11 -4
- package/lib/hooks/model-fallback.mjs +50 -6
- package/lib/hooks/orchestration-dispatch-guard.mjs +14 -3
- package/lib/hooks/pre-compact.mjs +181 -173
- package/lib/hooks/rule-verifier.mjs +2 -1
- package/lib/hooks/session-reflect.mjs +2 -1
- package/lib/hooks/session-start.mjs +3 -3
- package/lib/host/readiness.mjs +109 -0
- package/lib/host-capabilities.mjs +13 -2
- package/lib/host-disposition.mjs +29 -8
- package/lib/identity.mjs +92 -0
- package/lib/ingest/degraded-extract.mjs +96 -0
- package/lib/ingest/docling-remote.mjs +2 -1
- package/lib/ingest/provider-extract.mjs +7 -19
- package/lib/ingest/sidecar-providers.mjs +196 -0
- package/lib/ingest-tooling.mjs +11 -5
- package/lib/init-unified.mjs +55 -38
- package/lib/init-update.mjs +2 -1
- package/lib/install/legacy-global-cleanup.mjs +25 -0
- package/lib/install/stage-project.mjs +41 -4
- package/lib/intake/daemon.mjs +99 -8
- package/lib/intake/git-queue.mjs +110 -38
- package/lib/intake/prepare.mjs +2 -0
- package/lib/intake/queue-registry.mjs +50 -0
- package/lib/intake/queue.mjs +133 -17
- package/lib/intake/session-prelude.mjs +57 -8
- package/lib/integrations/intake-integrations.mjs +61 -111
- package/lib/intent-classifier.mjs +43 -5
- package/lib/libreoffice-export.mjs +8 -8
- package/lib/logging/rotate.mjs +5 -4
- package/lib/mcp/broker.mjs +332 -17
- package/lib/mcp/denied-store.mjs +95 -0
- package/lib/mcp/destructive-approval.mjs +57 -0
- package/lib/mcp/destructive-gate.mjs +30 -0
- package/lib/mcp/dispatch-envelope.mjs +199 -0
- package/lib/mcp/memory-bridge.mjs +2 -1
- package/lib/mcp/server.mjs +185 -1268
- package/lib/mcp/tool-definitions-memory.mjs +376 -0
- package/lib/mcp/tool-definitions-project.mjs +271 -0
- package/lib/mcp/tool-definitions-skills.mjs +311 -0
- package/lib/mcp/tool-definitions-workflow.mjs +398 -0
- package/lib/mcp/tool-definitions.mjs +25 -0
- package/lib/mcp/tool-rate-limit.mjs +47 -0
- package/lib/mcp/tool-registry.mjs +107 -0
- package/lib/mcp/tool-safety.mjs +95 -0
- package/lib/mcp/tool-surface-parity.mjs +60 -0
- package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
- package/lib/mcp/tools/orchestration-run.mjs +197 -19
- package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
- package/lib/mcp/tools/project.mjs +25 -8
- package/lib/mcp/tools/provider-write.mjs +187 -0
- package/lib/mcp/tools/scope.mjs +0 -3
- package/lib/mcp/tools/skills.mjs +1 -1
- package/lib/mcp/tools/storage.mjs +2 -2
- package/lib/mcp/tools/web-search-governance.mjs +96 -0
- package/lib/mcp/tools/web-search.mjs +5 -76
- package/lib/mcp/transport/auth.mjs +238 -0
- package/lib/mcp/transport/http.mjs +136 -0
- package/lib/mcp/transport/mode.mjs +31 -0
- package/lib/mcp/transport/stdio.mjs +22 -0
- package/lib/mcp-catalog.json +4 -4
- package/lib/mcp-manager.mjs +34 -23
- package/lib/mcp-platform-config.mjs +53 -20
- package/lib/mode-capabilities.mjs +109 -0
- package/lib/model-router.mjs +42 -9
- package/lib/models/catalog.mjs +40 -1
- package/lib/net-guard.mjs +247 -0
- package/lib/observation-store.mjs +6 -2
- package/lib/ollama/provision-context.mjs +2 -1
- package/lib/opencode-config.mjs +22 -3
- package/lib/opencode-runtime-plugin.mjs +120 -2
- package/lib/oracle/actions.mjs +204 -10
- package/lib/oracle/cli.mjs +24 -3
- package/lib/oracle/daemon-entry.mjs +6 -0
- package/lib/oracle/dispatch.mjs +2 -1
- package/lib/oracle/execute.mjs +2 -2
- package/lib/oracle/gaps.mjs +3 -3
- package/lib/oracle/heartbeat.mjs +53 -0
- package/lib/oracle/read-model.mjs +69 -13
- package/lib/oracle/reconcile.mjs +3 -46
- package/lib/oracle/routing.mjs +37 -31
- package/lib/oracle/synthesize.mjs +1 -1
- package/lib/orchestration/classification.mjs +434 -0
- package/lib/orchestration/delegation-flow.mjs +132 -0
- package/lib/orchestration/flow-selection.mjs +418 -0
- package/lib/orchestration/gates.mjs +244 -0
- package/lib/orchestration/host-sampling.mjs +121 -0
- package/lib/orchestration/policy-constants.mjs +48 -0
- package/lib/orchestration/provider-outcome.mjs +197 -0
- package/lib/orchestration/readiness.mjs +114 -13
- package/lib/orchestration/run-store-postgres.mjs +32 -26
- package/lib/orchestration/run-store-sqlite.mjs +8 -14
- package/lib/orchestration/run-store.mjs +25 -12
- package/lib/orchestration/runtime.mjs +508 -62
- package/lib/orchestration/store.mjs +87 -12
- package/lib/orchestration/trace-store.mjs +125 -0
- package/lib/orchestration/web-capability.mjs +60 -0
- package/lib/orchestration/worker-runtime.mjs +162 -0
- package/lib/orchestration/worker.mjs +763 -80
- package/lib/orchestration-policy.mjs +67 -1111
- package/lib/output-quality.mjs +61 -2
- package/lib/packs/cli.mjs +144 -0
- package/lib/packs/core-pack.mjs +112 -0
- package/lib/packs/enablement.mjs +187 -0
- package/lib/packs/index.mjs +23 -0
- package/lib/packs/loader.mjs +176 -0
- package/lib/packs/manifest-schema.mjs +58 -0
- package/lib/packs/prompts.mjs +120 -0
- package/lib/packs/validate.mjs +208 -0
- package/lib/path-policy.mjs +56 -0
- package/lib/plugin-registry.mjs +4 -5
- package/lib/policy/audit-gate.mjs +42 -0
- package/lib/policy/consumption-budget.mjs +149 -0
- package/lib/policy/engine.mjs +81 -6
- package/lib/policy/role-authority.mjs +89 -0
- package/lib/prompt-composer.js +19 -3
- package/lib/providers/contract/adapters/confluence/governed-write.mjs +215 -0
- package/lib/providers/contract/adapters/confluence/manifest.json +17 -0
- package/lib/providers/contract/adapters/confluence/transport.mjs +135 -0
- package/lib/providers/contract/adapters/github/governed-write.mjs +121 -0
- package/lib/providers/contract/adapters/github/index.mjs +38 -3
- package/lib/providers/contract/adapters/jira/adf.mjs +151 -0
- package/lib/providers/contract/adapters/jira/createmeta.mjs +143 -0
- package/lib/providers/contract/adapters/jira/governed-write.mjs +182 -0
- package/lib/providers/contract/adapters/jira/manifest.json +17 -0
- package/lib/providers/contract/adapters/jira/transport.mjs +119 -0
- package/lib/providers/contract.mjs +207 -0
- package/lib/providers/credential-bootstrap.mjs +7 -4
- package/lib/providers/credential-sources.mjs +14 -2
- package/lib/providers/directory/index.mjs +261 -0
- package/lib/providers/feedback/index.mjs +392 -0
- package/lib/providers/filter-audit.mjs +68 -0
- package/lib/providers/filter-schema.mjs +54 -0
- package/lib/providers/github/index.mjs +31 -0
- package/lib/providers/instance-config.mjs +215 -0
- package/lib/providers/op-locate.mjs +96 -0
- package/lib/providers/op-run.mjs +64 -9
- package/lib/providers/registry.mjs +26 -3
- package/lib/providers/secret-audit-wiring.mjs +41 -18
- package/lib/providers/secret-resolver.mjs +263 -31
- package/lib/publish-template.mjs +6 -3
- package/lib/publish-tooling.mjs +4 -0
- package/lib/publish.mjs +32 -4
- package/lib/queue/pg-queue.mjs +395 -0
- package/lib/reflect.mjs +2 -1
- package/lib/registry/assemble.mjs +28 -2
- package/lib/registry/catalog.mjs +6 -0
- package/lib/registry/consolidation.mjs +8 -1
- package/lib/registry/custom-scaffold.mjs +200 -0
- package/lib/registry/custom-schema.mjs +137 -0
- package/lib/registry/loader.mjs +15 -4
- package/lib/registry/manifests/format-engines.default.json +15 -0
- package/lib/registry/manifests/surface-map.default.json +46 -0
- package/lib/registry/retired-paths.mjs +1 -0
- package/lib/registry/surface-map.mjs +100 -50
- package/lib/registry/validate.mjs +3 -3
- package/lib/render-visual-check.mjs +240 -0
- package/lib/resources/budget.mjs +40 -17
- package/lib/roles/flavor-bindings.mjs +36 -14
- package/lib/roles/gateway.mjs +15 -8
- package/lib/roots.mjs +107 -0
- package/lib/runtime/uv-bootstrap.mjs +32 -6
- package/lib/runtime/whisper-bootstrap.mjs +11 -3
- package/lib/runtime-env.mjs +5 -2
- package/lib/sandbox.mjs +1 -1
- package/lib/scheduler/index.mjs +8 -20
- package/lib/schema-infer.mjs +31 -2
- package/lib/scopes/lifecycle.mjs +29 -25
- package/lib/scopes/loader.mjs +49 -12
- package/lib/scopes/teams.mjs +1 -1
- package/lib/security/ingest-boundary.mjs +80 -0
- package/lib/security/recall-wrapper.mjs +99 -0
- package/lib/security/trust.mjs +132 -0
- package/lib/service-manager.mjs +93 -24
- package/lib/setup.mjs +41 -23
- package/lib/skills-apply.mjs +4 -2
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/state-root.mjs +147 -0
- package/lib/status.mjs +597 -6
- package/lib/storage/admin.mjs +84 -7
- package/lib/storage/backend-registry.mjs +73 -0
- package/lib/storage/backend.mjs +63 -9
- package/lib/storage/embeddings-openai.mjs +12 -2
- package/lib/storage/hybrid-query.mjs +11 -3
- package/lib/storage/retrieval-hardening.mjs +384 -0
- package/lib/storage/shared-memory.mjs +158 -0
- package/lib/storage/vector-client.mjs +69 -2
- package/lib/team/health.mjs +72 -0
- package/lib/telemetry/backends/local.mjs +9 -3
- package/lib/telemetry/client.mjs +3 -7
- package/lib/template-registry.mjs +10 -12
- package/lib/tenant/context.mjs +82 -0
- package/lib/tenant/isolation.mjs +129 -0
- package/lib/test-corpus-inventory.mjs +103 -2
- package/lib/uninstall/uninstall.mjs +78 -12
- package/lib/validator.mjs +4 -6
- package/lib/worker/entrypoint.mjs +2 -1
- package/lib/worker/run.mjs +2 -2
- package/lib/worker/trace.mjs +5 -11
- package/lib/workflow-state.mjs +52 -8
- package/lib/workflows/liveness.mjs +203 -0
- package/lib/workflows/loader.mjs +177 -0
- package/lib/workflows/manifest-schema.mjs +71 -0
- package/lib/workflows/surface-parity.mjs +118 -0
- package/lib/workflows/validate.mjs +127 -0
- package/lib/writes/control-plane.mjs +140 -0
- package/lib/writes/envelope.mjs +206 -0
- package/lib/writes/sent-log.mjs +86 -0
- package/lib/writes/write-intent.mjs +109 -0
- package/package.json +20 -7
- package/personas/construct.md +12 -3
- package/registry/agent-manifest.json +117 -0
- package/registry/capabilities.json +1987 -0
- package/rules/common/comments.md +1 -0
- package/schemas/brand-voice.schema.json +24 -0
- package/schemas/capability-registry.schema.json +72 -0
- package/schemas/certification-run.schema.json +130 -0
- package/schemas/demo-recording.schema.json +46 -0
- package/schemas/eval-dataset.schema.json +79 -0
- package/schemas/execution-capability-profile.schema.json +46 -0
- package/schemas/execution-policy.schema.json +114 -0
- package/schemas/improvement-proposal.schema.json +65 -0
- package/schemas/mcp-tool-output.schema.json +61 -0
- package/schemas/platform-capabilities.schema.json +83 -0
- package/schemas/project-config.schema.json +215 -0
- package/schemas/project-demo.schema.json +60 -0
- package/schemas/provider-behavior-matrix.schema.json +91 -0
- package/schemas/scope.schema.json +197 -0
- package/schemas/specialist-trace.schema.json +107 -0
- package/schemas/team.schema.json +99 -0
- package/schemas/unified-registry.schema.json +546 -0
- package/scripts/sync-specialists.mjs +336 -106
- package/skills/docs/adr-workflow.md +1 -1
- package/skills/docs/codebase-research-workflow.md +3 -3
- package/skills/docs/prd-workflow.md +5 -6
- package/skills/docs/runbook-workflow.md +2 -2
- package/skills/docs/user-research-workflow.md +3 -3
- package/skills/operating/fleet-health-routing.md +77 -0
- package/skills/roles/ai-engineer.md +1 -1
- package/skills/roles/architect.md +0 -1
- package/skills/roles/business-strategist.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/data-engineer.md +1 -1
- package/skills/roles/data-engineer.pipeline.md +1 -1
- package/skills/roles/data-engineer.vector-retrieval.md +1 -2
- package/skills/roles/data-engineer.warehouse.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +0 -1
- package/skills/roles/devil-advocate.md +1 -1
- package/skills/roles/docs-keeper.md +1 -1
- package/skills/roles/evaluator.md +1 -1
- package/skills/roles/explorer.md +1 -1
- package/skills/roles/platform-engineer.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -2
- package/skills/roles/qa.api-contract.md +0 -1
- package/skills/roles/qa.data-pipeline.md +0 -1
- package/skills/roles/qa.md +0 -1
- package/skills/roles/qa.web-ui.md +0 -1
- package/skills/roles/release-manager.md +1 -1
- package/skills/roles/researcher.md +0 -2
- package/skills/roles/reviewer.md +0 -3
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +0 -1
- package/skills/roles/security.privacy.md +0 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/sre.md +1 -1
- package/skills/roles/test-automation.md +1 -1
- package/skills/roles/trace-reviewer.md +1 -1
- package/skills/roles/ux-researcher.md +1 -1
- package/specialists/artifact-manifest.json +36 -36
- package/specialists/org/contracts/accessibility-to-qa.json +11 -3
- package/specialists/org/contracts/any-to-business-strategist.json +19 -7
- package/specialists/org/contracts/any-to-debugger.json +7 -1
- package/specialists/org/contracts/any-to-designer.json +7 -1
- package/specialists/org/contracts/any-to-docs-keeper.json +18 -4
- package/specialists/org/contracts/any-to-explorer.json +13 -4
- package/specialists/org/contracts/any-to-sre-incident.json +6 -2
- package/specialists/org/contracts/any-to-trace-reviewer.json +16 -4
- package/specialists/org/contracts/architect-to-ai-engineer.json +6 -2
- package/specialists/org/contracts/architect-to-data-engineer.json +17 -5
- package/specialists/org/contracts/architect-to-devil-advocate.json +11 -3
- package/specialists/org/contracts/architect-to-engineer.json +20 -4
- package/specialists/org/contracts/architect-to-evaluator.json +15 -5
- package/specialists/org/contracts/architect-to-legal-compliance.json +15 -5
- package/specialists/org/contracts/architect-to-operations.json +17 -4
- package/specialists/org/contracts/architect-to-platform-engineer.json +20 -4
- package/specialists/org/contracts/construct-to-orchestrator.json +10 -2
- package/specialists/org/contracts/data-analyst-to-product-manager.json +11 -2
- package/specialists/org/contracts/engineer-to-qa.json +20 -4
- package/specialists/org/contracts/engineer-to-reviewer.json +29 -5
- package/specialists/org/contracts/explorer-to-engineer.json +11 -3
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +12 -4
- package/specialists/org/contracts/operations-to-user.json +53 -0
- package/specialists/org/contracts/operations-triage-output.json +53 -0
- package/specialists/org/contracts/pm-requirements-candidates.json +53 -0
- package/specialists/org/contracts/product-manager-to-architect.json +30 -10
- package/specialists/org/contracts/product-manager-to-data-analyst.json +13 -3
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +15 -5
- package/specialists/org/contracts/qa-to-release-manager.json +11 -3
- package/specialists/org/contracts/researcher-to-architect.json +10 -2
- package/specialists/org/contracts/researcher-to-product-manager.json +16 -5
- package/specialists/org/contracts/reviewer-to-security.json +17 -3
- package/specialists/org/contracts/test-automation-to-engineer.json +11 -3
- package/specialists/org/contracts/trace-reviewer-to-sre.json +12 -4
- package/specialists/org/contracts/user-to-construct.json +11 -4
- package/specialists/org/frameworks/cx-architect-constraint-option-failure.md +66 -0
- package/specialists/org/frameworks/cx-engineer-feasibility-blast-radius.md +66 -0
- package/specialists/org/frameworks/cx-ops-dependency-sequencing.md +74 -0
- package/specialists/org/frameworks/cx-pm-value-tradeoff.md +66 -0
- package/specialists/org/frameworks/cx-qa-risk-based-coverage.md +69 -0
- package/specialists/org/groups/engineering-group.json +2 -6
- package/specialists/org/groups/governance-group.json +2 -3
- package/specialists/org/groups/operations-group.json +5 -8
- package/specialists/org/groups/product-group.json +4 -8
- package/specialists/org/groups/quality-group.json +3 -7
- package/specialists/org/groups/strategy-group.json +6 -9
- package/specialists/org/models.json.example +8 -0
- package/specialists/org/scopes/creative.json +6 -1
- package/specialists/org/scopes/operations.json +7 -1
- package/specialists/org/scopes/research.json +6 -1
- package/specialists/org/scopes/rnd.json +7 -1
- package/specialists/org/specialists/cx-architect.json +27 -8
- package/specialists/org/specialists/cx-designer.json +22 -8
- package/specialists/org/specialists/cx-engineer.json +71 -7
- package/specialists/org/specialists/cx-operations.json +89 -15
- package/specialists/org/specialists/cx-orchestrator.json +16 -4
- package/specialists/org/specialists/cx-product-manager.json +28 -9
- package/specialists/org/specialists/cx-qa.json +16 -6
- package/specialists/org/specialists/cx-researcher.json +41 -7
- package/specialists/org/specialists/cx-reviewer.json +61 -11
- package/specialists/org/specialists/cx-security.json +30 -10
- package/specialists/org/teams/design-team.json +3 -4
- package/specialists/org/teams/engineering-team.json +3 -10
- package/specialists/org/teams/governance-team.json +3 -5
- package/specialists/org/teams/operations-team.json +6 -12
- package/specialists/org/teams/product-management-team.json +2 -3
- package/specialists/org/teams/quality-team.json +4 -12
- package/specialists/org/teams/research-team.json +3 -3
- package/specialists/org/teams/strategy-team.json +7 -14
- package/specialists/prompts/cx-architect.md +20 -1
- package/specialists/prompts/cx-data-analyst.md +1 -1
- package/specialists/prompts/cx-designer.md +12 -4
- package/specialists/prompts/cx-engineer.md +15 -1
- package/specialists/prompts/cx-operations.md +14 -2
- package/specialists/prompts/cx-orchestrator.md +9 -5
- package/specialists/prompts/cx-product-manager.md +5 -1
- package/specialists/prompts/cx-qa.md +6 -2
- package/specialists/prompts/cx-researcher.md +27 -31
- package/specialists/prompts/cx-reviewer.md +19 -1
- package/specialists/prompts/cx-security.md +5 -1
- package/templates/distribution/construct-brand.typ +123 -59
- package/templates/distribution/construct-decision.typ +6 -3
- package/templates/distribution/construct-pdf.typ +11 -4
- package/templates/distribution/construct-prd.typ +6 -3
- package/templates/distribution/construct-research.typ +7 -4
- package/vendor/pandoc-ext/README.md +3 -0
- package/vendor/pandoc-ext/diagram.lua +687 -0
- package/lib/providers/contract/adapters/git/index.mjs +0 -115
- package/lib/providers/contract/adapters/slack/index.mjs +0 -175
- package/specialists/org/contracts/business-strategist-to-product-manager.json +0 -39
- package/specialists/org/contracts/construct-to-rd-lead.json +0 -53
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +0 -36
- package/specialists/org/contracts/designer-to-accessibility.json +0 -36
- package/specialists/org/contracts/platform-engineer-to-engineer.json +0 -31
- package/specialists/org/contracts/qa-to-test-automation.json +0 -42
- package/specialists/org/contracts/rd-lead-to-architect.json +0 -38
- package/specialists/org/contracts/sre-to-release-manager.json +0 -36
- package/specialists/org/specialists/cx-accessibility.json +0 -69
- package/specialists/org/specialists/cx-ai-engineer.json +0 -75
- package/specialists/org/specialists/cx-business-strategist.json +0 -72
- package/specialists/org/specialists/cx-data-engineer.json +0 -71
- package/specialists/org/specialists/cx-devil-advocate.json +0 -71
- package/specialists/org/specialists/cx-docs-keeper.json +0 -82
- package/specialists/org/specialists/cx-evaluator.json +0 -69
- package/specialists/org/specialists/cx-explorer.json +0 -69
- package/specialists/org/specialists/cx-legal-compliance.json +0 -74
- package/specialists/org/specialists/cx-oracle.json +0 -46
- package/specialists/org/specialists/cx-platform-engineer.json +0 -80
- package/specialists/org/specialists/cx-rd-lead.json +0 -73
- package/specialists/org/specialists/cx-release-manager.json +0 -77
- package/specialists/org/specialists/cx-sre.json +0 -94
- package/specialists/org/specialists/cx-test-automation.json +0 -69
- package/specialists/org/specialists/cx-trace-reviewer.json +0 -69
- package/specialists/org/specialists/cx-ux-researcher.json +0 -68
- package/specialists/org/teams/accessibility-team.json +0 -39
- package/specialists/org/teams/ux-research-team.json +0 -39
- package/specialists/prompts/cx-accessibility.md +0 -55
- package/specialists/prompts/cx-ai-engineer.md +0 -124
- package/specialists/prompts/cx-business-strategist.md +0 -58
- package/specialists/prompts/cx-data-engineer.md +0 -55
- package/specialists/prompts/cx-devil-advocate.md +0 -59
- package/specialists/prompts/cx-docs-keeper.md +0 -164
- package/specialists/prompts/cx-evaluator.md +0 -49
- package/specialists/prompts/cx-explorer.md +0 -71
- package/specialists/prompts/cx-legal-compliance.md +0 -58
- package/specialists/prompts/cx-oracle.md +0 -98
- package/specialists/prompts/cx-platform-engineer.md +0 -97
- package/specialists/prompts/cx-rd-lead.md +0 -59
- package/specialists/prompts/cx-release-manager.md +0 -54
- package/specialists/prompts/cx-sre.md +0 -111
- package/specialists/prompts/cx-test-automation.md +0 -55
- package/specialists/prompts/cx-trace-reviewer.md +0 -101
- package/specialists/prompts/cx-ux-researcher.md +0 -53
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/packs/loader.mjs — pack manifest directory loader and merger.
|
|
3
|
+
*
|
|
4
|
+
* Three manifest tiers exist: builtin (shipped with Construct), user (in
|
|
5
|
+
* ~/.config/construct/packs/), and project (in .cx/packs/). Project packs
|
|
6
|
+
* take highest precedence; user packs override builtin. The core pack is
|
|
7
|
+
* programmatically injected at the builtin tier via loadCorePack().
|
|
8
|
+
*
|
|
9
|
+
* `loadPacksFromDir` reads every subdirectory containing pack.manifest.json
|
|
10
|
+
* and validates each one. `mergePackTiers` merges the three tiers by id.
|
|
11
|
+
* `resolvePackDirs` returns the canonical paths for each tier.
|
|
12
|
+
* `loadAllPacks` is a convenience that loads from all tiers.
|
|
13
|
+
*
|
|
14
|
+
* Prompt validation (LMCP-E2): in team/enterprise deployment mode a pack whose
|
|
15
|
+
* declared prompt files are missing or lack valid frontmatter is rejected at
|
|
16
|
+
* load time — the error names the missing file — rather than letting the
|
|
17
|
+
* worker discover the gap mid-run and silently substitute a generic persona.
|
|
18
|
+
* Solo mode does not hard-fail here; the worker records the miss as a visible
|
|
19
|
+
* degraded fallback instead (lib/orchestration/worker.mjs).
|
|
20
|
+
*
|
|
21
|
+
* embedBindings validation (LMCP-E4): every pack's `embedBindings` block is
|
|
22
|
+
* cross-checked against the builtin extension manifests (lib/extensions) so
|
|
23
|
+
* a binding naming an unknown provider id or an undeclared capability fails
|
|
24
|
+
* pack validation with a path, rather than the authority guard discovering
|
|
25
|
+
* the gap at proposal time.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
29
|
+
import { dirname, join, resolve } from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
|
|
32
|
+
import { validatePackManifest } from './validate.mjs';
|
|
33
|
+
import { validatePackPrompts } from './prompts.mjs';
|
|
34
|
+
import { loadCorePack } from './core-pack.mjs';
|
|
35
|
+
import { getDeploymentMode } from '../deployment-mode.mjs';
|
|
36
|
+
import { loadManifestsFromDir, resolveManifestDirs } from '../extensions/loader.mjs';
|
|
37
|
+
|
|
38
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
const PACKAGE_ROOT = resolve(__dirname, '..', '..');
|
|
40
|
+
|
|
41
|
+
const PROMPT_HARD_FAIL_MODES = new Set(['team', 'enterprise']);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load builtin extension manifests keyed by id, for embedBindings
|
|
45
|
+
* cross-validation. Only the builtin tier is consulted — the same set every
|
|
46
|
+
* `construct doctor` / pack loader run sees, independent of project overrides.
|
|
47
|
+
*/
|
|
48
|
+
function loadKnownProviderManifests(packageRoot) {
|
|
49
|
+
const { builtin } = resolveManifestDirs({ rootDir: packageRoot });
|
|
50
|
+
const { manifests } = loadManifestsFromDir(builtin);
|
|
51
|
+
const byId = {};
|
|
52
|
+
for (const m of manifests) byId[m.id] = m;
|
|
53
|
+
return byId;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function loadPacksFromDir(packsBaseDir, opts = {}) {
|
|
57
|
+
const packs = [];
|
|
58
|
+
const errors = [];
|
|
59
|
+
|
|
60
|
+
if (!existsSync(packsBaseDir)) {
|
|
61
|
+
return { packs, errors };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let entries;
|
|
65
|
+
try {
|
|
66
|
+
entries = readdirSync(packsBaseDir, { withFileTypes: true });
|
|
67
|
+
} catch (err) {
|
|
68
|
+
errors.push(`${packsBaseDir}: failed to read directory (${err.message})`);
|
|
69
|
+
return { packs, errors };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const packDirs = entries.filter(e => e.isDirectory()).map(e => join(packsBaseDir, e.name)).sort();
|
|
73
|
+
const deploymentMode = opts.deploymentMode || getDeploymentMode(opts.env);
|
|
74
|
+
const packageRoot = opts.packageRoot || PACKAGE_ROOT;
|
|
75
|
+
const knownProviders = opts.knownProviders || loadKnownProviderManifests(packageRoot);
|
|
76
|
+
|
|
77
|
+
for (const packDir of packDirs) {
|
|
78
|
+
const manifestPath = join(packDir, 'pack.manifest.json');
|
|
79
|
+
if (!existsSync(manifestPath)) continue;
|
|
80
|
+
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
84
|
+
} catch (err) {
|
|
85
|
+
errors.push(`${manifestPath}: failed to parse JSON (${err.message})`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = validatePackManifest(parsed, { filePath: manifestPath, strict: opts.strict, knownProviders });
|
|
90
|
+
if (!result.valid) {
|
|
91
|
+
errors.push(...result.errors);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (PROMPT_HARD_FAIL_MODES.has(deploymentMode)) {
|
|
96
|
+
const promptResult = validatePackPrompts({ ...parsed, _packDir: packDir }, { packageRoot });
|
|
97
|
+
if (!promptResult.valid) {
|
|
98
|
+
errors.push(...promptResult.errors.map((e) => `${manifestPath}: ${e}`));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
packs.push({ ...parsed, _tier: opts.tier || 'unknown', _manifestPath: manifestPath, _packDir: packDir });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { packs, errors };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function mergePackTiers(builtin = [], user = [], project = []) {
|
|
110
|
+
const map = new Map();
|
|
111
|
+
|
|
112
|
+
for (const p of builtin) map.set(p.id, p);
|
|
113
|
+
for (const p of user) map.set(p.id, p);
|
|
114
|
+
for (const p of project) map.set(p.id, p);
|
|
115
|
+
|
|
116
|
+
return [...map.values()];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function resolvePackDirs({ rootDir, homeDir } = {}) {
|
|
120
|
+
const home = homeDir ?? (process.env.HOME || process.env.USERPROFILE || '');
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
builtin: join(PACKAGE_ROOT, 'lib', 'packs', 'manifests'),
|
|
124
|
+
user: join(home, '.config', 'construct', 'packs'),
|
|
125
|
+
project: join(rootDir || process.cwd(), '.cx', 'packs'),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function loadAllPacks(opts = {}) {
|
|
130
|
+
const dirs = resolvePackDirs(opts);
|
|
131
|
+
const allErrors = [];
|
|
132
|
+
const deploymentMode = opts.deploymentMode || getDeploymentMode(opts.env);
|
|
133
|
+
const packageRoot = opts.packageRoot || PACKAGE_ROOT;
|
|
134
|
+
const knownProviders = opts.knownProviders || loadKnownProviderManifests(packageRoot);
|
|
135
|
+
|
|
136
|
+
const builtinResult = loadPacksFromDir(dirs.builtin, { ...opts, tier: 'builtin', deploymentMode, packageRoot, knownProviders });
|
|
137
|
+
allErrors.push(...builtinResult.errors);
|
|
138
|
+
|
|
139
|
+
const userResult = loadPacksFromDir(dirs.user, { ...opts, tier: 'user', deploymentMode, packageRoot, knownProviders });
|
|
140
|
+
allErrors.push(...userResult.errors);
|
|
141
|
+
|
|
142
|
+
const projectResult = loadPacksFromDir(dirs.project, { ...opts, tier: 'project', deploymentMode, packageRoot, knownProviders });
|
|
143
|
+
allErrors.push(...projectResult.errors);
|
|
144
|
+
|
|
145
|
+
const corePack = loadCorePack(packageRoot);
|
|
146
|
+
let builtinPacks = [...builtinResult.packs, corePack];
|
|
147
|
+
|
|
148
|
+
// The core pack ships with Construct and is not read from a manifest file,
|
|
149
|
+
// bypassing loadPacksFromDir's validation loop above; the same hard-fail
|
|
150
|
+
// check still applies under team/enterprise, naming the missing file. The
|
|
151
|
+
// same is true of embedBindings — validated explicitly here so an unknown
|
|
152
|
+
// provider id or undeclared capability in the core pack's own bindings
|
|
153
|
+
// fails loudly instead of silently granting excess authority.
|
|
154
|
+
if (PROMPT_HARD_FAIL_MODES.has(deploymentMode)) {
|
|
155
|
+
const coreResult = validatePackPrompts(corePack, { packageRoot });
|
|
156
|
+
if (!coreResult.valid) {
|
|
157
|
+
allErrors.push(...coreResult.errors.map((e) => `${corePack._sourceDir || '@construct/core'}: ${e}`));
|
|
158
|
+
builtinPacks = builtinResult.packs;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (corePack.embedBindings) {
|
|
163
|
+
const coreBindingResult = validatePackManifest(
|
|
164
|
+
{ id: corePack.id, version: corePack.version, compatVersion: corePack.compatVersion, embedBindings: corePack.embedBindings },
|
|
165
|
+
{ filePath: corePack._sourceDir || '@construct/core', knownProviders }
|
|
166
|
+
);
|
|
167
|
+
if (!coreBindingResult.valid) {
|
|
168
|
+
allErrors.push(...coreBindingResult.errors);
|
|
169
|
+
builtinPacks = builtinPacks.filter((p) => p.id !== corePack.id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const merged = mergePackTiers(builtinPacks, userResult.packs, projectResult.packs);
|
|
174
|
+
|
|
175
|
+
return { packs: merged, errors: allErrors };
|
|
176
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/packs/manifest-schema.mjs — canonical pack manifest schema descriptor.
|
|
3
|
+
*
|
|
4
|
+
* Defines the vocabulary for pack manifests as per ADR-0055 (A5 pack schema).
|
|
5
|
+
* A plain JS descriptor, not JSON Schema — the validator in validate.mjs
|
|
6
|
+
* consumes it directly. The schema is intentionally additive: new optional
|
|
7
|
+
* fields may be added in a minor version bump; new required fields are
|
|
8
|
+
* breaking and require a PACK_COMPAT_VERSION increment.
|
|
9
|
+
*
|
|
10
|
+
* `embedBindings` (LMCP-E4) is one such additive field: per-specialist
|
|
11
|
+
* provider read/search grants and external-write proposal grants, closing
|
|
12
|
+
* the excessive-agency gap where an embedded specialist could read any
|
|
13
|
+
* provider and propose any write with no declared authorization.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const PACK_REQUIRED_FIELDS = ['id', 'version', 'compatVersion'];
|
|
17
|
+
|
|
18
|
+
export const PACK_OPTIONAL_FIELDS = [
|
|
19
|
+
'teams', 'specialists', 'prompts', 'frameworks', 'perspectives',
|
|
20
|
+
'modelTierHints', 'toolGrantsRequested', 'workflowContributions',
|
|
21
|
+
'handoffContracts', 'outputContracts', 'gates', 'tests', 'docs',
|
|
22
|
+
'installConditions', 'enableConditions', 'deprecation', 'embedBindings',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
export const PACK_COMPAT_VERSION = 1;
|
|
26
|
+
export const PACK_ID_RE = /^[a-z0-9\-./@]+$/;
|
|
27
|
+
export const PACK_SOURCE_TIERS = ['builtin', 'user', 'project'];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* embedBindings shape (LMCP-E4) — per-specialist read/propose grant.
|
|
31
|
+
*
|
|
32
|
+
* `embedBindings` is a pack-level map keyed by specialist id:
|
|
33
|
+
*
|
|
34
|
+
* {
|
|
35
|
+
* "<specialistId>": {
|
|
36
|
+
* "providers": [
|
|
37
|
+
* { "id": "<extension-manifest-id>", "capabilities": ["read"], "filters": { ... ADR-0060 filter block ... } }
|
|
38
|
+
* ],
|
|
39
|
+
* "proposals": ["jira.createIssue"]
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* `providers[].capabilities` values must be drawn from EMBED_BINDING_CAPABILITIES
|
|
44
|
+
* and must each be declared by the referenced extension manifest's own
|
|
45
|
+
* `capabilities` array — the binding cannot grant a capability the provider
|
|
46
|
+
* itself never advertises. `providers[].filters` reuses the ADR-0060 provider
|
|
47
|
+
* filter block verbatim (scope/predicates/nativeQuery); it is optional and,
|
|
48
|
+
* when present, validated with the same rules poll-time filters use.
|
|
49
|
+
* `proposals` is a flat list of `<providerId>.<writeKind>` tokens the
|
|
50
|
+
* specialist may propose (never execute autonomously — see authority-guard.mjs).
|
|
51
|
+
*/
|
|
52
|
+
export const EMBED_BINDING_CAPABILITIES = ['read', 'search'];
|
|
53
|
+
|
|
54
|
+
export const EMBED_BINDING_FIELDS = ['providers', 'proposals'];
|
|
55
|
+
|
|
56
|
+
export const EMBED_BINDING_PROVIDER_FIELDS = ['id', 'capabilities', 'filters'];
|
|
57
|
+
|
|
58
|
+
export const EMBED_BINDING_PROPOSAL_RE = /^[a-z0-9-]+\.[a-zA-Z0-9]+$/;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/packs/prompts.mjs — pack prompt-file validation and registry resolution.
|
|
3
|
+
*
|
|
4
|
+
* A pack manifest's `prompts` map declares `{ specialistId: relativePath }`,
|
|
5
|
+
* relative to that pack's own directory (`_packDir`, set by
|
|
6
|
+
* lib/packs/loader.mjs for every manifest-loaded pack) — a project pack's
|
|
7
|
+
* prompt paths are relative to the project, not to the Construct install.
|
|
8
|
+
* The core pack (lib/packs/core-pack.mjs) has no `_packDir`; its prompts map
|
|
9
|
+
* uses paths relative to `packageRoot` (the Construct install), the fallback
|
|
10
|
+
* root used whenever a pack carries no `_packDir` of its own.
|
|
11
|
+
*
|
|
12
|
+
* validatePackPrompts() confirms every declared file exists AND carries
|
|
13
|
+
* parseable YAML frontmatter with a `role` field — the two things worker.mjs
|
|
14
|
+
* needs before it can trust a persona prompt. In team/enterprise deployment
|
|
15
|
+
* mode a miss is a hard load-time error (the pack is rejected, naming the
|
|
16
|
+
* missing file) per LMCP-E2: a governed pack must never let a specialist run
|
|
17
|
+
* under the wrong persona.
|
|
18
|
+
*
|
|
19
|
+
* resolvePersonaPrompt() is the ONLY path worker.mjs uses to read a persona
|
|
20
|
+
* body: a walk of the pack list in the order given (the caller sorts by tier
|
|
21
|
+
* precedence — project before user before builtin, per ADR-0055), returning
|
|
22
|
+
* the first pack that declares the role, or `{ found: false }` when no pack
|
|
23
|
+
* in the registry declares it. No fallback to reading specialists/prompts/
|
|
24
|
+
* directly exists — that would defeat the pack boundary the registry is
|
|
25
|
+
* meant to enforce.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
29
|
+
import { join } from 'node:path';
|
|
30
|
+
import yaml from 'js-yaml';
|
|
31
|
+
|
|
32
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
33
|
+
|
|
34
|
+
function parseFrontmatter(content) {
|
|
35
|
+
const match = content.match(FRONTMATTER_RE);
|
|
36
|
+
if (!match) return null;
|
|
37
|
+
try {
|
|
38
|
+
return yaml.load(match[1]) || {};
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function packPromptRoot(pack, fallbackRoot) {
|
|
45
|
+
return pack?._packDir || fallbackRoot;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Validate every prompt file a pack declares.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} pack a manifest object carrying `prompts: {id: relPath}`
|
|
52
|
+
* @param {object} opts
|
|
53
|
+
* @param {string} opts.packageRoot fallback root for packs with no `_packDir` (e.g. the core pack)
|
|
54
|
+
* @returns {{valid: boolean, errors: string[]}}
|
|
55
|
+
*/
|
|
56
|
+
export function validatePackPrompts(pack, { packageRoot } = {}) {
|
|
57
|
+
const errors = [];
|
|
58
|
+
const prompts = pack?.prompts || {};
|
|
59
|
+
const root = packPromptRoot(pack, packageRoot);
|
|
60
|
+
|
|
61
|
+
for (const [specId, relPath] of Object.entries(prompts)) {
|
|
62
|
+
if (typeof relPath !== 'string' || !relPath) {
|
|
63
|
+
errors.push(`pack '${pack.id}': prompt entry for '${specId}' has no file path`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const absPath = join(root, relPath);
|
|
67
|
+
if (!existsSync(absPath)) {
|
|
68
|
+
errors.push(`pack '${pack.id}': declared prompt file missing for '${specId}': ${relPath}`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
let raw;
|
|
72
|
+
try {
|
|
73
|
+
raw = readFileSync(absPath, 'utf8');
|
|
74
|
+
} catch (err) {
|
|
75
|
+
errors.push(`pack '${pack.id}': declared prompt file unreadable for '${specId}': ${relPath} (${err.message})`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const fm = parseFrontmatter(raw);
|
|
79
|
+
if (!fm) {
|
|
80
|
+
errors.push(`pack '${pack.id}': prompt file missing or invalid frontmatter for '${specId}': ${relPath}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (!fm.role || typeof fm.role !== 'string') {
|
|
84
|
+
errors.push(`pack '${pack.id}': prompt frontmatter missing 'role' for '${specId}': ${relPath}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { valid: errors.length === 0, errors };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Resolve one specialist's persona prompt through the pack registry only.
|
|
93
|
+
*
|
|
94
|
+
* Packs are tried in the order given (the caller sorts by tier precedence;
|
|
95
|
+
* see the module header) — the first pack declaring the role wins.
|
|
96
|
+
*
|
|
97
|
+
* @param {string} role specialist id, e.g. 'cx-engineer' (accepts 'engineer' too)
|
|
98
|
+
* @param {object} opts
|
|
99
|
+
* @param {object[]} opts.packs pack list, tier-precedence ordered (see loadAllPacks)
|
|
100
|
+
* @param {string} opts.packageRoot fallback root for packs with no `_packDir` (e.g. the core pack)
|
|
101
|
+
* @returns {{found: true, content: string, packId: string} | {found: false}}
|
|
102
|
+
*/
|
|
103
|
+
export function resolvePersonaPrompt(role, { packs, packageRoot }) {
|
|
104
|
+
const slug = String(role || '').replace(/^cx-/, '');
|
|
105
|
+
const specId = `cx-${slug}`;
|
|
106
|
+
|
|
107
|
+
for (const pack of packs || []) {
|
|
108
|
+
const relPath = pack?.prompts?.[specId];
|
|
109
|
+
if (!relPath) continue;
|
|
110
|
+
const absPath = join(packPromptRoot(pack, packageRoot), relPath);
|
|
111
|
+
if (!existsSync(absPath)) continue;
|
|
112
|
+
try {
|
|
113
|
+
return { found: true, content: readFileSync(absPath, 'utf8'), packId: pack.id };
|
|
114
|
+
} catch {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { found: false };
|
|
120
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/packs/validate.mjs — pack manifest validator.
|
|
3
|
+
*
|
|
4
|
+
* Validates a plain-JS pack manifest object against the canonical schema
|
|
5
|
+
* defined in manifest-schema.mjs. All errors are returned in a structured
|
|
6
|
+
* result; this function never throws. Error messages are single-line,
|
|
7
|
+
* actionable, and prefixed with the filePath (when provided).
|
|
8
|
+
*
|
|
9
|
+
* `embedBindings` validation (LMCP-E4) additionally cross-checks each bound
|
|
10
|
+
* provider id and capability against `knownProviders` — a map of provider id
|
|
11
|
+
* to its loaded extension manifest (lib/extensions/loader.mjs). A binding
|
|
12
|
+
* naming a provider id absent from that map, or granting a capability the
|
|
13
|
+
* provider's own manifest does not declare, fails validation with a path.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
PACK_REQUIRED_FIELDS, PACK_OPTIONAL_FIELDS, PACK_COMPAT_VERSION,
|
|
18
|
+
EMBED_BINDING_CAPABILITIES, EMBED_BINDING_FIELDS, EMBED_BINDING_PROVIDER_FIELDS,
|
|
19
|
+
EMBED_BINDING_PROPOSAL_RE,
|
|
20
|
+
} from './manifest-schema.mjs';
|
|
21
|
+
import { validateFilterConfig } from '../providers/contract.mjs';
|
|
22
|
+
|
|
23
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+].+)?$/;
|
|
24
|
+
|
|
25
|
+
const PACK_ID_RE = /^[a-z0-9\-./@]+$/;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validate the `embedBindings` block (LMCP-E4) against known extension
|
|
29
|
+
* manifests. `knownProviders` is a map of providerId → manifest (as loaded
|
|
30
|
+
* by lib/extensions/loader.mjs); each bound provider must exist in that map
|
|
31
|
+
* and must itself declare every capability the binding grants — a pack
|
|
32
|
+
* cannot hand a specialist a read/search grant the provider never advertises.
|
|
33
|
+
* Every error names the JSON path of the offending field.
|
|
34
|
+
*/
|
|
35
|
+
function validateEmbedBindings(embedBindings, { prefix, knownProviders = {} }) {
|
|
36
|
+
const errors = [];
|
|
37
|
+
|
|
38
|
+
if (typeof embedBindings !== 'object' || embedBindings === null || Array.isArray(embedBindings)) {
|
|
39
|
+
return [`${prefix}embedBindings must be an object keyed by specialist id`];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const [specialistId, binding] of Object.entries(embedBindings)) {
|
|
43
|
+
const bindingPath = `embedBindings.${specialistId}`;
|
|
44
|
+
|
|
45
|
+
if (typeof binding !== 'object' || binding === null || Array.isArray(binding)) {
|
|
46
|
+
errors.push(`${prefix}${bindingPath} must be an object`);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const key of Object.keys(binding)) {
|
|
51
|
+
if (!EMBED_BINDING_FIELDS.includes(key)) {
|
|
52
|
+
errors.push(`${prefix}${bindingPath}.${key} is not a recognized embedBindings field (allowed: ${EMBED_BINDING_FIELDS.join(', ')})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const providers = binding.providers;
|
|
57
|
+
if (providers !== undefined) {
|
|
58
|
+
if (!Array.isArray(providers)) {
|
|
59
|
+
errors.push(`${prefix}${bindingPath}.providers must be an array`);
|
|
60
|
+
} else {
|
|
61
|
+
providers.forEach((entry, idx) => {
|
|
62
|
+
const entryPath = `${bindingPath}.providers[${idx}]`;
|
|
63
|
+
|
|
64
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
65
|
+
errors.push(`${prefix}${entryPath} must be an object`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const key of Object.keys(entry)) {
|
|
70
|
+
if (!EMBED_BINDING_PROVIDER_FIELDS.includes(key)) {
|
|
71
|
+
errors.push(`${prefix}${entryPath}.${key} is not a recognized provider binding field (allowed: ${EMBED_BINDING_PROVIDER_FIELDS.join(', ')})`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const providerId = entry.id;
|
|
76
|
+
if (typeof providerId !== 'string' || providerId.length === 0) {
|
|
77
|
+
errors.push(`${prefix}${entryPath}.id must be a non-empty string`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const providerManifest = knownProviders[providerId];
|
|
82
|
+
if (!providerManifest) {
|
|
83
|
+
errors.push(`${prefix}${entryPath}.id references unknown provider "${providerId}" (no extension manifest declares this id)`);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const declaredCapabilities = new Set(providerManifest.capabilities || []);
|
|
88
|
+
|
|
89
|
+
if (!Array.isArray(entry.capabilities) || entry.capabilities.length === 0) {
|
|
90
|
+
errors.push(`${prefix}${entryPath}.capabilities must be a non-empty array`);
|
|
91
|
+
} else {
|
|
92
|
+
entry.capabilities.forEach((cap, capIdx) => {
|
|
93
|
+
const capPath = `${entryPath}.capabilities[${capIdx}]`;
|
|
94
|
+
if (!EMBED_BINDING_CAPABILITIES.includes(cap)) {
|
|
95
|
+
errors.push(`${prefix}${capPath} "${cap}" is not a recognized embed capability (allowed: ${EMBED_BINDING_CAPABILITIES.join(', ')})`);
|
|
96
|
+
} else if (!declaredCapabilities.has(cap)) {
|
|
97
|
+
errors.push(`${prefix}${capPath} "${cap}" is not declared by provider "${providerId}"'s manifest capabilities (${[...declaredCapabilities].join(', ') || '(none)'})`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (entry.filters !== undefined) {
|
|
103
|
+
try {
|
|
104
|
+
validateFilterConfig(providerId, entry.filters);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
errors.push(`${prefix}${entryPath}.filters: ${err.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const proposals = binding.proposals;
|
|
114
|
+
if (proposals !== undefined) {
|
|
115
|
+
if (!Array.isArray(proposals)) {
|
|
116
|
+
errors.push(`${prefix}${bindingPath}.proposals must be an array`);
|
|
117
|
+
} else {
|
|
118
|
+
const boundProviderIds = new Set((Array.isArray(providers) ? providers : []).map((p) => p?.id).filter(Boolean));
|
|
119
|
+
proposals.forEach((token, idx) => {
|
|
120
|
+
const tokenPath = `${bindingPath}.proposals[${idx}]`;
|
|
121
|
+
if (typeof token !== 'string' || !EMBED_BINDING_PROPOSAL_RE.test(token)) {
|
|
122
|
+
errors.push(`${prefix}${tokenPath} "${token}" must be a "<providerId>.<writeKind>" token (e.g. "jira.createIssue")`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const [providerId] = token.split('.');
|
|
126
|
+
if (!knownProviders[providerId]) {
|
|
127
|
+
errors.push(`${prefix}${tokenPath} "${token}" references unknown provider "${providerId}" (no extension manifest declares this id)`);
|
|
128
|
+
} else if (boundProviderIds.size > 0 && !boundProviderIds.has(providerId)) {
|
|
129
|
+
errors.push(`${prefix}${tokenPath} "${token}" proposes against provider "${providerId}" which is not in this specialist's providers[] grant`);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return errors;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function validatePackManifest(manifest, { filePath, strict = false, knownProviders = {} } = {}) {
|
|
140
|
+
const prefix = filePath ? `${filePath}: ` : '';
|
|
141
|
+
const errors = [];
|
|
142
|
+
|
|
143
|
+
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
144
|
+
return { valid: false, errors: [`${prefix}manifest must be a JSON object`] };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const field of PACK_REQUIRED_FIELDS) {
|
|
148
|
+
if (!(field in manifest) || manifest[field] === undefined || manifest[field] === null) {
|
|
149
|
+
errors.push(`${prefix}missing required field: ${field}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if ('id' in manifest && manifest.id !== undefined && manifest.id !== null) {
|
|
154
|
+
if (typeof manifest.id !== 'string' || manifest.id.length === 0) {
|
|
155
|
+
errors.push(`${prefix}id must be a non-empty string`);
|
|
156
|
+
} else if (!PACK_ID_RE.test(manifest.id)) {
|
|
157
|
+
errors.push(`${prefix}id must match [a-z0-9-./@]+ (got '${manifest.id}')`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if ('version' in manifest && manifest.version !== undefined && manifest.version !== null) {
|
|
162
|
+
if (typeof manifest.version !== 'string' || !SEMVER_RE.test(manifest.version)) {
|
|
163
|
+
errors.push(`${prefix}version must be a semver string`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (manifest.compatVersion !== undefined && manifest.compatVersion !== null) {
|
|
168
|
+
if (typeof manifest.compatVersion !== 'number' || !Number.isInteger(manifest.compatVersion)) {
|
|
169
|
+
errors.push(`${prefix}compatVersion must be an integer`);
|
|
170
|
+
} else if (manifest.compatVersion > PACK_COMPAT_VERSION) {
|
|
171
|
+
errors.push(
|
|
172
|
+
`${prefix}compatVersion ${manifest.compatVersion} exceeds supported version ${PACK_COMPAT_VERSION}; upgrade Construct to use this pack`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (manifest.deprecation !== undefined && manifest.deprecation !== null) {
|
|
178
|
+
if (typeof manifest.deprecation !== 'object' || Array.isArray(manifest.deprecation)) {
|
|
179
|
+
errors.push(`${prefix}deprecation must be an object`);
|
|
180
|
+
} else {
|
|
181
|
+
if (!('since' in manifest.deprecation)) {
|
|
182
|
+
errors.push(`${prefix}deprecation must include 'since' field`);
|
|
183
|
+
}
|
|
184
|
+
if (!('message' in manifest.deprecation)) {
|
|
185
|
+
errors.push(`${prefix}deprecation must include 'message' field`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (manifest.embedBindings !== undefined && manifest.embedBindings !== null) {
|
|
191
|
+
errors.push(...validateEmbedBindings(manifest.embedBindings, { prefix, knownProviders }));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (strict) {
|
|
195
|
+
const knownFields = new Set([...PACK_REQUIRED_FIELDS, ...PACK_OPTIONAL_FIELDS]);
|
|
196
|
+
for (const key of Object.keys(manifest)) {
|
|
197
|
+
if (key.startsWith('_')) continue;
|
|
198
|
+
if (!knownFields.has(key)) {
|
|
199
|
+
errors.push(`${prefix}unknown field '${key}' in strict mode`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (errors.length > 0) {
|
|
205
|
+
return { valid: false, errors };
|
|
206
|
+
}
|
|
207
|
+
return { valid: true };
|
|
208
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/path-policy.mjs — root containment for model-supplied file paths.
|
|
3
|
+
*
|
|
4
|
+
* MCP file tools take paths from model arguments. resolveWithinRoot pins each path to a
|
|
5
|
+
* declared root: it rejects backslash/UNC separators, out-of-root absolute or ../ targets,
|
|
6
|
+
* and symlinks whose real target escapes the root. Containment is checked on the realpath
|
|
7
|
+
* of the longest existing ancestor, so a not-yet-existing target is still anchored through
|
|
8
|
+
* real directories. On any escape it throws PathContainmentError whose message names the
|
|
9
|
+
* denial; otherwise it returns the canonical absolute path.
|
|
10
|
+
*/
|
|
11
|
+
import { realpathSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
|
|
14
|
+
export class PathContainmentError extends Error {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'PathContainmentError';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// A target may not exist yet (a delete of an already-gone file, a path with a missing
|
|
22
|
+
// tail). Resolve symlinks on the longest existing prefix, then re-append the remaining
|
|
23
|
+
// segments, so the escape check still sees through real ancestors.
|
|
24
|
+
|
|
25
|
+
function canonical(p) {
|
|
26
|
+
const abs = path.resolve(p);
|
|
27
|
+
let cur = abs;
|
|
28
|
+
const tail = [];
|
|
29
|
+
while (true) {
|
|
30
|
+
try {
|
|
31
|
+
const real = realpathSync(cur);
|
|
32
|
+
return tail.length ? path.join(real, ...tail) : real;
|
|
33
|
+
} catch {
|
|
34
|
+
const parent = path.dirname(cur);
|
|
35
|
+
if (parent === cur) return abs;
|
|
36
|
+
tail.unshift(path.basename(cur));
|
|
37
|
+
cur = parent;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveWithinRoot(root, candidate) {
|
|
43
|
+
if (typeof candidate !== 'string' || candidate.length === 0) {
|
|
44
|
+
throw new PathContainmentError('path denied: empty or non-string file path');
|
|
45
|
+
}
|
|
46
|
+
if (candidate.includes('\\')) {
|
|
47
|
+
throw new PathContainmentError('path denied: backslash/UNC separators escape the allowed root');
|
|
48
|
+
}
|
|
49
|
+
const base = canonical(root);
|
|
50
|
+
const requested = path.isAbsolute(candidate) ? candidate : path.join(path.resolve(root), candidate);
|
|
51
|
+
const resolved = canonical(requested);
|
|
52
|
+
if (resolved !== base && !resolved.startsWith(base + path.sep)) {
|
|
53
|
+
throw new PathContainmentError(`path denied: ${candidate} resolves outside the allowed root`);
|
|
54
|
+
}
|
|
55
|
+
return resolved;
|
|
56
|
+
}
|
package/lib/plugin-registry.mjs
CHANGED
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
import {
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { extname, join, resolve } from "node:path";
|
|
11
10
|
import { configDir } from "./config/xdg.mjs";
|
|
11
|
+
import { packageRoot } from "./roots.mjs";
|
|
12
12
|
|
|
13
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
13
|
export const MANIFEST_VERSION = 1;
|
|
15
14
|
|
|
16
15
|
function readJson(filePath) {
|
|
@@ -97,7 +96,7 @@ export function validatePluginManifest(manifest, { manifestPath = "<memory>" } =
|
|
|
97
96
|
|
|
98
97
|
function loadBuiltinPlugin(rootDir) {
|
|
99
98
|
const rootCatalogPath = join(rootDir, "lib", "mcp-catalog.json");
|
|
100
|
-
const catalogPath = existsSync(rootCatalogPath) ? rootCatalogPath : join(
|
|
99
|
+
const catalogPath = existsSync(rootCatalogPath) ? rootCatalogPath : join(packageRoot, "lib", "mcp-catalog.json");
|
|
101
100
|
const catalog = readJson(catalogPath);
|
|
102
101
|
const plugin = {
|
|
103
102
|
id: "construct-builtins",
|
|
@@ -142,7 +141,7 @@ function findManifestFiles(pluginDirs) {
|
|
|
142
141
|
return files.sort();
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
export function loadPluginRegistry({ cwd = process.cwd(), homeDir = homedir(), rootDir =
|
|
144
|
+
export function loadPluginRegistry({ cwd = process.cwd(), homeDir = homedir(), rootDir = packageRoot, env = process.env } = {}) {
|
|
146
145
|
const errors = [];
|
|
147
146
|
const plugins = [loadBuiltinPlugin(rootDir)];
|
|
148
147
|
const pluginDirs = resolvePluginDirs({ cwd, homeDir, env });
|