@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,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/embed/capability-loader.mjs — embed-capability manifest loader (ADR-0061, LMCP-P2).
|
|
3
|
+
*
|
|
4
|
+
* An embed capability is a workflow manifest (lib/workflows/loader.mjs, D1)
|
|
5
|
+
* with `type: "embed"` and an `embed` block. This module discovers those
|
|
6
|
+
* manifests across the three D1 tiers (builtin, pack, project), validates
|
|
7
|
+
* the `embed` block against the ADR-0061 shape — including provider
|
|
8
|
+
* bindings (E4) and the ADR-0060 filter block (reused read-only from
|
|
9
|
+
* lib/providers/filter-schema.mjs / lib/providers/contract.mjs) — and
|
|
10
|
+
* exposes the per-project enable/disable lifecycle described in the ADR:
|
|
11
|
+
* pack tiers ship *available* defaults, `.cx/embed/<id>.manifest.json`
|
|
12
|
+
* (project tier) makes a capability *active*.
|
|
13
|
+
*
|
|
14
|
+
* Every validation failure returns a JSON-schema-style field path
|
|
15
|
+
* (`embed.providerBindings[1]`, `embed.filter.scope.repos`, …) so a failed
|
|
16
|
+
* `enable` names exactly what is wrong — this is a fail-closed surface, not
|
|
17
|
+
* best-effort: an invalid manifest is never silently accepted or partially
|
|
18
|
+
* applied.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { join } from 'node:path';
|
|
23
|
+
|
|
24
|
+
import { loadWorkflowManifestsFromDir, mergeWorkflowManifests, resolveWorkflowManifestDirs } from '../workflows/loader.mjs';
|
|
25
|
+
import { validateWorkflowManifest } from '../workflows/validate.mjs';
|
|
26
|
+
import { loadAllPacks } from '../packs/loader.mjs';
|
|
27
|
+
import { validateFilterConfig } from '../providers/contract.mjs';
|
|
28
|
+
import { TOP_LEVEL_KEYS as FILTER_TOP_LEVEL_KEYS, SCOPE_KEYS as FILTER_SCOPE_KEYS, PREDICATE_KEYS as FILTER_PREDICATE_KEYS } from '../providers/filter-schema.mjs';
|
|
29
|
+
|
|
30
|
+
export const EMBED_MANIFEST_TYPE = 'embed';
|
|
31
|
+
|
|
32
|
+
export const RUNTIME_VALUES = Object.freeze(['in-process', 'external', 'auto', 'none']);
|
|
33
|
+
|
|
34
|
+
export const PROPOSAL_AUTHORITY_VALUES = Object.freeze(['propose-only', 'governed-write']);
|
|
35
|
+
|
|
36
|
+
const REQUIRED_EMBED_FIELDS = Object.freeze([
|
|
37
|
+
'specialist', 'providerBindings', 'framework', 'outputContract', 'proposalAuthority', 'runtime',
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Directory holding per-project embed enable/override manifests
|
|
42
|
+
* (project tier of the D1 loader, mirrored under `.cx/embed/`).
|
|
43
|
+
*/
|
|
44
|
+
export function embedProjectDir(rootDir = process.cwd()) {
|
|
45
|
+
return join(rootDir, '.cx', 'embed');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function fieldError(filePath, fieldPath, message) {
|
|
49
|
+
const prefix = filePath ? `${filePath}: ` : '';
|
|
50
|
+
return `${prefix}${fieldPath}: ${message}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Validate the `embed` block of a `type: "embed"` workflow manifest against
|
|
55
|
+
* the ADR-0061 shape. Returns `{ valid: true }` or `{ valid: false, errors }`
|
|
56
|
+
* where every error string is prefixed with its JSON-schema-style field path
|
|
57
|
+
* (e.g. `embed.providerBindings[0]`) so a caller can point at exactly what
|
|
58
|
+
* broke. Never throws.
|
|
59
|
+
*
|
|
60
|
+
* `knownSpecialists` — merged E1 pack registry specialist ids; when supplied,
|
|
61
|
+
* an unresolvable `embed.specialist` is a hard error (fail closed rather than
|
|
62
|
+
* silently accepting a typo'd persona reference).
|
|
63
|
+
*/
|
|
64
|
+
export function validateEmbedBlock(manifest, { filePath, knownSpecialists } = {}) {
|
|
65
|
+
const errors = [];
|
|
66
|
+
|
|
67
|
+
if (manifest.type !== EMBED_MANIFEST_TYPE) {
|
|
68
|
+
return { valid: false, errors: [fieldError(filePath, 'type', `must be "${EMBED_MANIFEST_TYPE}" (got '${manifest.type}')`)] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const embed = manifest.embed;
|
|
72
|
+
if (embed === undefined || embed === null || typeof embed !== 'object' || Array.isArray(embed)) {
|
|
73
|
+
return { valid: false, errors: [fieldError(filePath, 'embed', 'must be an object present on every type:"embed" manifest')] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
for (const field of REQUIRED_EMBED_FIELDS) {
|
|
77
|
+
if (!(field in embed) || embed[field] === undefined || embed[field] === null) {
|
|
78
|
+
errors.push(fieldError(filePath, `embed.${field}`, 'missing required field'));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if ('specialist' in embed && embed.specialist !== undefined && embed.specialist !== null) {
|
|
83
|
+
if (typeof embed.specialist !== 'string' || embed.specialist.length === 0) {
|
|
84
|
+
errors.push(fieldError(filePath, 'embed.specialist', 'must be a non-empty string'));
|
|
85
|
+
} else if (Array.isArray(knownSpecialists) && knownSpecialists.length > 0 && !knownSpecialists.includes(embed.specialist)) {
|
|
86
|
+
errors.push(fieldError(filePath, 'embed.specialist', `unresolvable specialist id '${embed.specialist}' (not present in the merged pack registry)`));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ('providerBindings' in embed && embed.providerBindings !== undefined && embed.providerBindings !== null) {
|
|
91
|
+
if (!Array.isArray(embed.providerBindings)) {
|
|
92
|
+
errors.push(fieldError(filePath, 'embed.providerBindings', 'must be an array of provider ids'));
|
|
93
|
+
} else if (embed.providerBindings.length === 0) {
|
|
94
|
+
errors.push(fieldError(filePath, 'embed.providerBindings', 'must declare at least one provider id'));
|
|
95
|
+
} else {
|
|
96
|
+
embed.providerBindings.forEach((binding, idx) => {
|
|
97
|
+
if (typeof binding !== 'string' || binding.length === 0) {
|
|
98
|
+
errors.push(fieldError(filePath, `embed.providerBindings[${idx}]`, 'must be a non-empty string'));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if ('framework' in embed && embed.framework !== undefined && embed.framework !== null) {
|
|
105
|
+
if (typeof embed.framework !== 'string' || embed.framework.length === 0) {
|
|
106
|
+
errors.push(fieldError(filePath, 'embed.framework', 'must be a non-empty string'));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if ('outputContract' in embed && embed.outputContract !== undefined && embed.outputContract !== null) {
|
|
111
|
+
if (typeof embed.outputContract !== 'string' || embed.outputContract.length === 0) {
|
|
112
|
+
errors.push(fieldError(filePath, 'embed.outputContract', 'must be a non-empty string'));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if ('proposalAuthority' in embed && embed.proposalAuthority !== undefined && embed.proposalAuthority !== null) {
|
|
117
|
+
if (!PROPOSAL_AUTHORITY_VALUES.includes(embed.proposalAuthority)) {
|
|
118
|
+
errors.push(fieldError(filePath, 'embed.proposalAuthority', `must be one of: ${PROPOSAL_AUTHORITY_VALUES.join(', ')} (got '${embed.proposalAuthority}')`));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if ('runtime' in embed && embed.runtime !== undefined && embed.runtime !== null) {
|
|
123
|
+
if (!RUNTIME_VALUES.includes(embed.runtime)) {
|
|
124
|
+
errors.push(fieldError(filePath, 'embed.runtime', `must be one of: ${RUNTIME_VALUES.join(', ')} (got '${embed.runtime}')`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if ('cadence' in embed && embed.cadence !== undefined && embed.cadence !== null) {
|
|
129
|
+
if (typeof embed.cadence !== 'object' || Array.isArray(embed.cadence)) {
|
|
130
|
+
errors.push(fieldError(filePath, 'embed.cadence', 'must be an object (e.g. { "every": "PT15M" })'));
|
|
131
|
+
} else if ('every' in embed.cadence && typeof embed.cadence.every !== 'string') {
|
|
132
|
+
errors.push(fieldError(filePath, 'embed.cadence.every', 'must be a string (ISO-8601 duration)'));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if ('filter' in embed && embed.filter !== undefined && embed.filter !== null) {
|
|
137
|
+
errors.push(...validateEmbedFilterBlock(embed.filter, embed.providerBindings, filePath));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (errors.length > 0) return { valid: false, errors };
|
|
141
|
+
return { valid: true };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Validate the ADR-0060 filter block carried at `embed.filter`, field-path
|
|
146
|
+
* prefixed for the embed-manifest context. Structural checks mirror
|
|
147
|
+
* lib/providers/filter-schema.mjs; per-provider-kind scope legality is
|
|
148
|
+
* re-checked with the read-only lib/providers/contract.mjs
|
|
149
|
+
* `validateFilterConfig` against every bound provider id so a filter that
|
|
150
|
+
* is legal for one binding but not another is caught here rather than at
|
|
151
|
+
* daemon runtime.
|
|
152
|
+
*/
|
|
153
|
+
function validateEmbedFilterBlock(filter, providerBindings, filePath) {
|
|
154
|
+
const errors = [];
|
|
155
|
+
|
|
156
|
+
if (typeof filter !== 'object' || filter === null || Array.isArray(filter)) {
|
|
157
|
+
return [fieldError(filePath, 'embed.filter', 'must be an object')];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for (const key of Object.keys(filter)) {
|
|
161
|
+
if (!FILTER_TOP_LEVEL_KEYS.includes(key)) {
|
|
162
|
+
errors.push(fieldError(filePath, `embed.filter.${key}`, `unknown filter key; allowed: ${FILTER_TOP_LEVEL_KEYS.join(', ')}`));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (filter.scope !== undefined) {
|
|
167
|
+
if (typeof filter.scope !== 'object' || filter.scope === null || Array.isArray(filter.scope)) {
|
|
168
|
+
errors.push(fieldError(filePath, 'embed.filter.scope', 'must be an object'));
|
|
169
|
+
} else {
|
|
170
|
+
for (const key of Object.keys(filter.scope)) {
|
|
171
|
+
if (!FILTER_SCOPE_KEYS.includes(key)) {
|
|
172
|
+
errors.push(fieldError(filePath, `embed.filter.scope.${key}`, `unknown scope key; allowed: ${FILTER_SCOPE_KEYS.join(', ')}`));
|
|
173
|
+
} else if (!Array.isArray(filter.scope[key])) {
|
|
174
|
+
errors.push(fieldError(filePath, `embed.filter.scope.${key}`, 'must be an array'));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (filter.predicates !== undefined) {
|
|
181
|
+
if (typeof filter.predicates !== 'object' || filter.predicates === null || Array.isArray(filter.predicates)) {
|
|
182
|
+
errors.push(fieldError(filePath, 'embed.filter.predicates', 'must be an object'));
|
|
183
|
+
} else {
|
|
184
|
+
for (const key of Object.keys(filter.predicates)) {
|
|
185
|
+
if (!FILTER_PREDICATE_KEYS.includes(key)) {
|
|
186
|
+
errors.push(fieldError(filePath, `embed.filter.predicates.${key}`, `unknown predicate key; allowed: ${FILTER_PREDICATE_KEYS.join(', ')}`));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Structural errors already found: skip the per-provider re-check, its
|
|
193
|
+
// messages would only restate the same problem under a different path.
|
|
194
|
+
if (errors.length > 0) return errors;
|
|
195
|
+
|
|
196
|
+
// Fail closed per bound provider: a filter valid for "github" but not
|
|
197
|
+
// "jira" must not silently apply to jira's snapshot with github's rules.
|
|
198
|
+
for (const providerId of Array.isArray(providerBindings) ? providerBindings : []) {
|
|
199
|
+
try {
|
|
200
|
+
validateFilterConfig(providerId, filter);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
errors.push(fieldError(filePath, 'embed.filter', `invalid for bound provider '${providerId}': ${err.message}`));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return errors;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Discover embed-capability manifests across the D1 three-tier loader
|
|
211
|
+
* (builtin, every pack root's workflows/ dir, project .cx/embed/), validate
|
|
212
|
+
* the base workflow-manifest shape via lib/workflows/validate.mjs and the
|
|
213
|
+
* ADR-0061 `embed` block via validateEmbedBlock, and merge by id with
|
|
214
|
+
* project overriding pack overriding builtin. Non-`type:"embed"` workflow
|
|
215
|
+
* manifests present in the same directories are silently excluded — this
|
|
216
|
+
* loader is embed-capability-scoped, not a general workflow loader.
|
|
217
|
+
*
|
|
218
|
+
* @param {{ rootDir?: string, packRoots?: string[], knownSpecialists?: string[] }} [opts]
|
|
219
|
+
* @returns {{ capabilities: object[], errors: string[] }}
|
|
220
|
+
*/
|
|
221
|
+
export function loadEmbedCapabilities(opts = {}) {
|
|
222
|
+
const { rootDir = process.cwd() } = opts;
|
|
223
|
+
const errors = [];
|
|
224
|
+
|
|
225
|
+
let packRoots = opts.packRoots;
|
|
226
|
+
let knownSpecialists = opts.knownSpecialists;
|
|
227
|
+
if (!packRoots || !knownSpecialists) {
|
|
228
|
+
const { packs, errors: packErrors } = loadAllPacks({ rootDir });
|
|
229
|
+
errors.push(...packErrors);
|
|
230
|
+
if (!packRoots) packRoots = packs.map((p) => p._packDir).filter(Boolean);
|
|
231
|
+
if (!knownSpecialists) knownSpecialists = [...new Set(packs.flatMap((p) => p.specialists ?? []))];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const dirs = resolveWorkflowManifestDirs({ rootDir, packRoots });
|
|
235
|
+
|
|
236
|
+
// Project-tier embed manifests live under .cx/embed/ (not .cx/workflows/,
|
|
237
|
+
// which is D1's general workflow project tier) — see embedProjectDir().
|
|
238
|
+
const projectDir = embedProjectDir(rootDir);
|
|
239
|
+
|
|
240
|
+
const builtin = loadWorkflowManifestsFromDir(dirs.builtin);
|
|
241
|
+
errors.push(...builtin.errors);
|
|
242
|
+
|
|
243
|
+
const packManifests = [];
|
|
244
|
+
for (const packDir of dirs.pack) {
|
|
245
|
+
const result = loadWorkflowManifestsFromDir(packDir);
|
|
246
|
+
packManifests.push(...result.manifests);
|
|
247
|
+
errors.push(...result.errors);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const project = loadWorkflowManifestsFromDir(projectDir);
|
|
251
|
+
errors.push(...project.errors);
|
|
252
|
+
|
|
253
|
+
const merged = mergeWorkflowManifests(builtin.manifests, packManifests, project.manifests);
|
|
254
|
+
|
|
255
|
+
const capabilities = [];
|
|
256
|
+
for (const manifest of merged) {
|
|
257
|
+
if (manifest.type !== EMBED_MANIFEST_TYPE) continue;
|
|
258
|
+
|
|
259
|
+
const result = validateEmbedBlock(manifest, { filePath: manifest._filePath, knownSpecialists });
|
|
260
|
+
if (!result.valid) {
|
|
261
|
+
errors.push(...result.errors);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
capabilities.push(manifest);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return { capabilities, errors };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Validate a single candidate manifest object end to end (base workflow
|
|
272
|
+
* shape + ADR-0061 embed block) without touching disk, so an `enable` call
|
|
273
|
+
* can reject a bad manifest before any project-tier file is written.
|
|
274
|
+
*/
|
|
275
|
+
export function validateEmbedManifest(manifest, { filePath, knownSpecialists } = {}) {
|
|
276
|
+
const base = validateWorkflowManifest(manifest, { filePath });
|
|
277
|
+
if (!base.valid) return base;
|
|
278
|
+
return validateEmbedBlock(manifest, { filePath, knownSpecialists });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Read the project-tier enable/override manifest for `id`, if present.
|
|
283
|
+
* Returns null when the capability has never been enabled in this project.
|
|
284
|
+
*/
|
|
285
|
+
export function readProjectEmbedManifest(id, rootDir = process.cwd()) {
|
|
286
|
+
const filePath = join(embedProjectDir(rootDir), `${id}.manifest.json`);
|
|
287
|
+
if (!existsSync(filePath)) return null;
|
|
288
|
+
return { manifest: JSON.parse(readFileSync(filePath, 'utf8')), filePath };
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Write the project-tier enable/override manifest for `id`, creating
|
|
293
|
+
* `.cx/embed/` on demand. Callers must validate before calling — the
|
|
294
|
+
* function performs no validation, matching the D1 loader's separation
|
|
295
|
+
* between building a manifest object and validating one.
|
|
296
|
+
*/
|
|
297
|
+
export function writeProjectEmbedManifest(id, manifest, rootDir = process.cwd()) {
|
|
298
|
+
const dir = embedProjectDir(rootDir);
|
|
299
|
+
mkdirSync(dir, { recursive: true });
|
|
300
|
+
const filePath = join(dir, `${id}.manifest.json`);
|
|
301
|
+
writeFileSync(filePath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
302
|
+
return filePath;
|
|
303
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/embed/capability-runtime.mjs — ADR-0061 §3 runtime selector resolution.
|
|
3
|
+
*
|
|
4
|
+
* Resolves an embed capability's `runtime` field (`in-process` | `external`
|
|
5
|
+
* | `auto` | `none`) to the runtime that will actually execute reasoning,
|
|
6
|
+
* without performing that reasoning. `auto` resolves to `in-process` when a
|
|
7
|
+
* model provider is configured (the existing model-resolution path,
|
|
8
|
+
* lib/model-router.mjs `readCurrentModels`), else `external` when an
|
|
9
|
+
* external host is configured (`CONSTRUCT_EXTERNAL_AGENT_HOST`), else
|
|
10
|
+
* `none`. `none` is an honest, visible skip — never a fabricated result —
|
|
11
|
+
* per the workflow-invoke.mjs no-fabrication invariant this ADR preserves.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const SKIP_REASON_NO_RUNTIME = 'no-runtime';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* True when the existing model-resolution path has a model configured for
|
|
18
|
+
* any tier. Reuses lib/model-router.mjs so this module adds no new
|
|
19
|
+
* dependency — it is a read-only probe of state Construct already resolves.
|
|
20
|
+
*/
|
|
21
|
+
async function hasConfiguredModelProvider(env) {
|
|
22
|
+
try {
|
|
23
|
+
const { readCurrentModels } = await import('../model-router.mjs');
|
|
24
|
+
const models = readCurrentModels(null, {}, env);
|
|
25
|
+
return Boolean(models?.fast || models?.standard || models?.reasoning);
|
|
26
|
+
} catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasExternalRuntimeConfigured(env) {
|
|
32
|
+
return Boolean((env.CONSTRUCT_EXTERNAL_AGENT_HOST ?? '').trim());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the declared `runtime` selector to the runtime that will actually
|
|
37
|
+
* execute reasoning.
|
|
38
|
+
*
|
|
39
|
+
* @param {'in-process'|'external'|'auto'|'none'} runtime
|
|
40
|
+
* @param {object} [env] - env object (default: process.env)
|
|
41
|
+
* @returns {Promise<{ resolved: 'in-process'|'external'|'none', reason?: string }>}
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveRuntime(runtime, env = process.env) {
|
|
44
|
+
if (runtime === 'in-process') return { resolved: 'in-process' };
|
|
45
|
+
if (runtime === 'external') {
|
|
46
|
+
if (!hasExternalRuntimeConfigured(env)) {
|
|
47
|
+
return { resolved: 'none', reason: SKIP_REASON_NO_RUNTIME };
|
|
48
|
+
}
|
|
49
|
+
return { resolved: 'external' };
|
|
50
|
+
}
|
|
51
|
+
if (runtime === 'none') return { resolved: 'none', reason: SKIP_REASON_NO_RUNTIME };
|
|
52
|
+
|
|
53
|
+
// auto: in-process if a model provider is configured, else external if a
|
|
54
|
+
// host is configured, else none.
|
|
55
|
+
if (await hasConfiguredModelProvider(env)) return { resolved: 'in-process' };
|
|
56
|
+
if (hasExternalRuntimeConfigured(env)) return { resolved: 'external' };
|
|
57
|
+
return { resolved: 'none', reason: SKIP_REASON_NO_RUNTIME };
|
|
58
|
+
}
|
package/lib/embed/cli.mjs
CHANGED
|
@@ -7,14 +7,25 @@
|
|
|
7
7
|
* status Print daemon status + last snapshot summary
|
|
8
8
|
* snapshot [--config <path>] Run a one-shot snapshot and print to stdout
|
|
9
9
|
*
|
|
10
|
+
* Embed-capability lifecycle (ADR-0061, LMCP-P2) — per-specialist capability
|
|
11
|
+
* manifests, distinct from the daemon process above:
|
|
12
|
+
* list Available capabilities + per-project enabled state
|
|
13
|
+
* enable <id> Write .cx/embed/<id>.manifest.json, validate, activate
|
|
14
|
+
* disable <id> Mark a capability disabled (scheduled job stops)
|
|
15
|
+
* status <id> [--json] Bindings/filter/runtime/last-tick for one capability
|
|
16
|
+
* dry-run <id> [--json] Resolve the binding chain; no side effects
|
|
17
|
+
*
|
|
10
18
|
* Exports:
|
|
11
19
|
* resolveEmbedStatus(env, homeDir) — returns { level, label, detail }
|
|
12
20
|
* level: 'running' | 'stopped' | 'none'
|
|
13
21
|
* Consumed by session-start hook and `construct embed status`.
|
|
14
22
|
*
|
|
15
|
-
* autoStartEmbedIfNeeded(env, rootDir, homeDir) — spawns daemon when
|
|
16
|
-
* provider credentials are present but daemon is stopped
|
|
17
|
-
*
|
|
23
|
+
* autoStartEmbedIfNeeded(env, rootDir, homeDir, cwd) — spawns daemon when
|
|
24
|
+
* provider credentials are present but daemon is stopped, gated by
|
|
25
|
+
* CX_AUTO_EMBED (env, wins) or construct.config.json `autoEmbed` (config
|
|
26
|
+
* fallback, same env > config > default precedence as deployment mode).
|
|
27
|
+
* No-op if already running or no credentials. Returns
|
|
28
|
+
* { started: bool, pid?, reason? }.
|
|
18
29
|
*/
|
|
19
30
|
|
|
20
31
|
import fs from 'node:fs';
|
|
@@ -23,6 +34,7 @@ import path from 'node:path';
|
|
|
23
34
|
import { spawn } from 'node:child_process';
|
|
24
35
|
import { configDir } from '../config/xdg.mjs';
|
|
25
36
|
import { doctorRoot } from '../config/xdg.mjs';
|
|
37
|
+
import { loadProjectConfig } from '../config/project-config.mjs';
|
|
26
38
|
|
|
27
39
|
const STATE_FILE = 'embed-daemon.json';
|
|
28
40
|
const LOG_FILE = 'embed-daemon.log';
|
|
@@ -202,10 +214,17 @@ export function resolveEmbedStatus(env = process.env, homeDir = os.homedir()) {
|
|
|
202
214
|
* @param {object} [env] - Env object (default: process.env)
|
|
203
215
|
* @param {string} [rootDir] - Construct root dir (where lib/embed/worker.mjs lives)
|
|
204
216
|
* @param {string} [homeDir] - Home dir override
|
|
217
|
+
* @param {string} [cwd] - Project dir for the construct.config.json `autoEmbed` fallback
|
|
205
218
|
* @returns {{ started: boolean, pid?: number, reason?: string }}
|
|
206
219
|
*/
|
|
207
|
-
export async function autoStartEmbedIfNeeded(env = process.env, rootDir, homeDir = os.homedir()) {
|
|
208
|
-
|
|
220
|
+
export async function autoStartEmbedIfNeeded(env = process.env, rootDir, homeDir = os.homedir(), cwd = process.cwd()) {
|
|
221
|
+
let configEnabled = false;
|
|
222
|
+
try {
|
|
223
|
+
const { config } = loadProjectConfig(cwd, env);
|
|
224
|
+
configEnabled = config?.autoEmbed === true;
|
|
225
|
+
} catch { /* loader is best-effort — falls through to env-only gating */ }
|
|
226
|
+
|
|
227
|
+
if (env.CX_AUTO_EMBED !== '1' && !configEnabled) {
|
|
209
228
|
return { started: false, reason: 'CX_AUTO_EMBED not set' };
|
|
210
229
|
}
|
|
211
230
|
|
|
@@ -420,6 +439,152 @@ async function cmdEmbedSnapshot(args, { homeDir = os.homedir() } = {}) {
|
|
|
420
439
|
process.stderr.write(`\n${snapshot.summary.totalItems} items, ${snapshot.summary.errorCount} errors\n`);
|
|
421
440
|
}
|
|
422
441
|
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
// Embed-capability lifecycle subcommands (ADR-0061, LMCP-P2)
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
function jsonFlag(args) {
|
|
447
|
+
return args.includes('--json');
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function positionalId(args) {
|
|
451
|
+
return args.find((a) => !a.startsWith('--'));
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async function cmdCapabilityList(args, { rootDir = process.cwd() } = {}) {
|
|
455
|
+
const { listCapabilities } = await import('./capability-lifecycle.mjs');
|
|
456
|
+
const { capabilities, errors } = listCapabilities({ rootDir });
|
|
457
|
+
|
|
458
|
+
if (jsonFlag(args)) {
|
|
459
|
+
process.stdout.write(`${JSON.stringify({ capabilities: capabilities.map((c) => ({
|
|
460
|
+
id: c.id, available: c.available, enabled: c.enabled, source: c.source,
|
|
461
|
+
runtime: c.manifest.embed.runtime, specialist: c.manifest.embed.specialist,
|
|
462
|
+
})), errors }, null, 2)}\n`);
|
|
463
|
+
if (errors.length) process.exitCode = 1;
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (!capabilities.length) {
|
|
468
|
+
process.stdout.write('No embed capabilities discovered (pack defaults or .cx/embed/).\n');
|
|
469
|
+
}
|
|
470
|
+
for (const c of capabilities) {
|
|
471
|
+
process.stdout.write(`${c.id}${c.enabled ? ' [enabled]' : ' [available]'} — specialist=${c.manifest.embed.specialist} runtime=${c.manifest.embed.runtime}\n`);
|
|
472
|
+
}
|
|
473
|
+
for (const err of errors) {
|
|
474
|
+
process.stderr.write(`error: ${err}\n`);
|
|
475
|
+
}
|
|
476
|
+
if (errors.length) process.exitCode = 1;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
async function cmdCapabilityEnable(args, { rootDir = process.cwd() } = {}) {
|
|
480
|
+
const id = positionalId(args);
|
|
481
|
+
if (!id) {
|
|
482
|
+
process.stderr.write('Usage: construct embed enable <id>\n');
|
|
483
|
+
process.exitCode = 1;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// --dry-run resolves the binding chain and writes nothing: enable preview
|
|
488
|
+
// must never touch .cx/embed/, matching the ADR "execute nothing" rule.
|
|
489
|
+
if (args.includes('--dry-run')) {
|
|
490
|
+
return cmdCapabilityDryRun(args, { rootDir });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const { enableCapability } = await import('./capability-lifecycle.mjs');
|
|
494
|
+
const result = enableCapability(id, { rootDir });
|
|
495
|
+
|
|
496
|
+
if (!result.ok) {
|
|
497
|
+
process.stderr.write(`embed enable ${id}: invalid manifest\n`);
|
|
498
|
+
for (const err of result.errors) process.stderr.write(` ${err}\n`);
|
|
499
|
+
process.exitCode = 1;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
process.stdout.write(`embed capability '${id}' enabled → ${result.filePath}\n`);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
async function cmdCapabilityDisable(args, { rootDir = process.cwd() } = {}) {
|
|
507
|
+
const id = positionalId(args);
|
|
508
|
+
if (!id) {
|
|
509
|
+
process.stderr.write('Usage: construct embed disable <id>\n');
|
|
510
|
+
process.exitCode = 1;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const { disableCapability } = await import('./capability-lifecycle.mjs');
|
|
515
|
+
const result = disableCapability(id, { rootDir });
|
|
516
|
+
process.stdout.write(result.wasEnabled
|
|
517
|
+
? `embed capability '${id}' disabled\n`
|
|
518
|
+
: `embed capability '${id}' was not enabled (no-op)\n`);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async function cmdCapabilityStatus(args, { rootDir = process.cwd() } = {}) {
|
|
522
|
+
const id = positionalId(args);
|
|
523
|
+
const { capabilityStatus } = await import('./capability-lifecycle.mjs');
|
|
524
|
+
const result = await capabilityStatus(id, { rootDir });
|
|
525
|
+
|
|
526
|
+
if (!result.ok) {
|
|
527
|
+
if (jsonFlag(args)) {
|
|
528
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
529
|
+
} else {
|
|
530
|
+
for (const err of result.errors) process.stderr.write(`error: ${err}\n`);
|
|
531
|
+
}
|
|
532
|
+
process.exitCode = 1;
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (jsonFlag(args)) {
|
|
537
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
process.stdout.write(`embed capability '${id}': ${result.enabled ? 'enabled' : 'available (not enabled)'}\n`);
|
|
542
|
+
process.stdout.write(` specialist: ${result.chain.specialist}\n`);
|
|
543
|
+
process.stdout.write(` providers: ${result.chain.providerBindings.join(', ')}\n`);
|
|
544
|
+
process.stdout.write(` filter: ${result.chain.filter ? JSON.stringify(result.chain.filter) : '(none)'}\n`);
|
|
545
|
+
process.stdout.write(` framework: ${result.chain.framework}\n`);
|
|
546
|
+
process.stdout.write(` authority: ${result.chain.proposalAuthority}\n`);
|
|
547
|
+
process.stdout.write(` runtime: declared=${result.chain.runtime.declared} resolved=${result.chain.runtime.resolved}${result.chain.runtime.reason ? ` (${result.chain.runtime.reason})` : ''}\n`);
|
|
548
|
+
process.stdout.write(` last tick: ${result.lastTick ? `${result.lastTick.status}${result.lastTick.reason ? ` (${result.lastTick.reason})` : ''} at ${result.lastTick.tickedAt}` : '(never ticked)'}\n`);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
async function cmdCapabilityDryRun(args, { rootDir = process.cwd() } = {}) {
|
|
552
|
+
const id = positionalId(args);
|
|
553
|
+
if (!id) {
|
|
554
|
+
process.stderr.write('Usage: construct embed dry-run <id>\n');
|
|
555
|
+
process.exitCode = 1;
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const { resolveCapabilityChain } = await import('./capability-lifecycle.mjs');
|
|
560
|
+
const result = await resolveCapabilityChain(id, { rootDir });
|
|
561
|
+
|
|
562
|
+
if (!result.ok) {
|
|
563
|
+
if (jsonFlag(args)) {
|
|
564
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
565
|
+
} else {
|
|
566
|
+
for (const err of result.errors) process.stderr.write(`error: ${err}\n`);
|
|
567
|
+
}
|
|
568
|
+
process.exitCode = 1;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (jsonFlag(args)) {
|
|
573
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
process.stdout.write(`dry-run '${id}' — resolved chain (no side effects):\n`);
|
|
578
|
+
process.stdout.write(` specialist → ${result.chain.specialist}\n`);
|
|
579
|
+
process.stdout.write(` providerBindings → ${result.chain.providerBindings.join(', ')}\n`);
|
|
580
|
+
process.stdout.write(` filter → ${result.chain.filter ? JSON.stringify(result.chain.filter) : '(none)'}\n`);
|
|
581
|
+
process.stdout.write(` framework → ${result.chain.framework}\n`);
|
|
582
|
+
process.stdout.write(` outputContract → ${result.chain.outputContract}\n`);
|
|
583
|
+
process.stdout.write(` proposalAuthority → ${result.chain.proposalAuthority}\n`);
|
|
584
|
+
process.stdout.write(` cadence → ${result.chain.cadence ? JSON.stringify(result.chain.cadence) : '(event-driven only)'}\n`);
|
|
585
|
+
process.stdout.write(` runtime → declared=${result.chain.runtime.declared} resolved=${result.chain.runtime.resolved}${result.chain.runtime.reason ? ` (${result.chain.runtime.reason})` : ''}\n`);
|
|
586
|
+
}
|
|
587
|
+
|
|
423
588
|
// ---------------------------------------------------------------------------
|
|
424
589
|
// Main export
|
|
425
590
|
// ---------------------------------------------------------------------------
|
|
@@ -435,12 +600,23 @@ export async function runEmbedCli(args, opts = {}) {
|
|
|
435
600
|
const homeDir = opts.homeDir ?? os.homedir();
|
|
436
601
|
const rootDir = opts.rootDir ?? new URL('../..', import.meta.url).pathname;
|
|
437
602
|
|
|
603
|
+
// Capability-lifecycle subcommands (ADR-0061) resolve manifests against the
|
|
604
|
+
// project the operator is standing in, not the Construct package root the
|
|
605
|
+
// daemon-process subcommands above use for locating worker.mjs.
|
|
606
|
+
const capabilityRootDir = opts.capabilityRootDir ?? process.cwd();
|
|
607
|
+
|
|
438
608
|
switch (sub) {
|
|
439
609
|
case 'start': return cmdEmbedStart(subArgs, { homeDir, rootDir, _workerPath: opts._workerPath, _livenessCheckMs: opts._livenessCheckMs });
|
|
440
610
|
case 'stop': return cmdEmbedStop(subArgs, { homeDir });
|
|
441
|
-
case 'status':
|
|
611
|
+
case 'status':
|
|
612
|
+
if (positionalId(subArgs)) return cmdCapabilityStatus(subArgs, { rootDir: capabilityRootDir });
|
|
613
|
+
return cmdEmbedStatus(subArgs, { homeDir });
|
|
442
614
|
case 'snapshot': return cmdEmbedSnapshot(subArgs, { homeDir });
|
|
443
615
|
case 'migrate-model': return cmdEmbedMigrateModel(subArgs);
|
|
616
|
+
case 'list': return cmdCapabilityList(subArgs, { rootDir: capabilityRootDir });
|
|
617
|
+
case 'enable': return cmdCapabilityEnable(subArgs, { rootDir: capabilityRootDir });
|
|
618
|
+
case 'disable': return cmdCapabilityDisable(subArgs, { rootDir: capabilityRootDir });
|
|
619
|
+
case 'dry-run': return cmdCapabilityDryRun(subArgs, { rootDir: capabilityRootDir });
|
|
444
620
|
case 'supervise': {
|
|
445
621
|
const { installSupervision } = await import('./supervision.mjs');
|
|
446
622
|
const result = await installSupervision();
|
|
@@ -460,7 +636,8 @@ export async function runEmbedCli(args, opts = {}) {
|
|
|
460
636
|
}
|
|
461
637
|
default:
|
|
462
638
|
process.stderr.write(
|
|
463
|
-
`Usage: construct embed <start|stop|status|snapshot|migrate-model|supervise|unsupervise> [--config <path>]\n
|
|
639
|
+
`Usage: construct embed <start|stop|status|snapshot|migrate-model|supervise|unsupervise> [--config <path>]\n` +
|
|
640
|
+
` construct embed <list|enable|disable|status|dry-run> <id> [--json]\n`,
|
|
464
641
|
);
|
|
465
642
|
if (sub && sub !== '--help' && sub !== '-h') {
|
|
466
643
|
process.stderr.write(`Unknown subcommand: ${sub}\n`);
|
|
@@ -495,7 +672,7 @@ async function cmdEmbedMigrateModel(args) {
|
|
|
495
672
|
|
|
496
673
|
const client = createSqlClient(process.env);
|
|
497
674
|
if (!client) {
|
|
498
|
-
process.stderr.write('
|
|
675
|
+
process.stderr.write('Postgres was removed; nothing to migrate.\n');
|
|
499
676
|
process.exit(1);
|
|
500
677
|
}
|
|
501
678
|
|
package/lib/embed/config.mjs
CHANGED
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
* projects: [PROJ, INFRA] # OR list
|
|
32
32
|
* refs: [issues, sprints]
|
|
33
33
|
* jql: "project = PROJ AND sprint in openSprints()" # override JQL
|
|
34
|
+
* filter: # ADR-0060 filter block (LMCP-B11)
|
|
35
|
+
* scope: { projects: [PROJ] }
|
|
36
|
+
* predicates: { statusCategory: [in-progress], updatedSince: P14D }
|
|
37
|
+
* nativeQuery: "sprint in openSprints()" # optional passthrough
|
|
34
38
|
*
|
|
35
39
|
* outputs:
|
|
36
40
|
* - type: markdown # write snapshot to a file
|