@geraldmaron/construct 1.4.2 → 1.5.1
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 +1027 -64
- 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 +6 -29
- 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 +89 -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 +18 -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/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 +50 -9
- 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-capabilities.mjs +13 -2
- package/lib/host-disposition.mjs +19 -7
- 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 +57 -45
- package/lib/init-update.mjs +2 -1
- package/lib/install/legacy-global-cleanup.mjs +25 -0
- package/lib/intake/daemon.mjs +99 -8
- package/lib/intake/git-queue.mjs +110 -38
- 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-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 +154 -1411
- 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-registry.mjs +107 -0
- package/lib/mcp/tool-safety.mjs +1 -0
- package/lib/mcp/tools/orchestration-delegation-next.tool.mjs +68 -0
- package/lib/mcp/tools/orchestration-run.mjs +165 -25
- package/lib/mcp/tools/orchestration-task-result.tool.mjs +77 -0
- 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 +0 -8
- 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 +31 -7
- 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/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 +446 -39
- package/lib/orchestration/store.mjs +87 -12
- package/lib/orchestration/trace-store.mjs +125 -0
- package/lib/orchestration/web-capability.mjs +3 -2
- package/lib/orchestration/worker-runtime.mjs +162 -0
- package/lib/orchestration/worker.mjs +528 -89
- package/lib/orchestration-policy.mjs +67 -1111
- 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/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 +273 -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 +6 -0
- package/lib/providers/secret-resolver.mjs +240 -23
- 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/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 +8 -3
- 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/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/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 +38 -19
- 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 +77 -5
- 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 +104 -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 +16 -8
- package/personas/construct.md +12 -3
- package/registry/capabilities.json +143 -36
- package/rules/common/comments.md +1 -0
- package/schemas/project-config.schema.json +0 -12
- package/schemas/unified-registry.schema.json +2 -4
- package/scripts/sync-specialists.mjs +284 -81
- 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 +40 -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 +25 -30
- 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/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
package/lib/service-manager.mjs
CHANGED
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
* spawns whatever is available and returns a result per service.
|
|
7
7
|
*/
|
|
8
8
|
import { spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import { randomBytes } from 'node:crypto';
|
|
9
10
|
import fs from 'node:fs';
|
|
10
11
|
import net from 'node:net';
|
|
11
12
|
import os from 'node:os';
|
|
12
13
|
import path from 'node:path';
|
|
13
14
|
import { fileURLToPath } from 'node:url';
|
|
14
15
|
|
|
15
|
-
import { findAvailablePort } from './host-capabilities.mjs';
|
|
16
|
+
import { findAvailablePort, detectActiveSessions } from './host-capabilities.mjs';
|
|
16
17
|
import { wrapWithOpRun } from './providers/op-run.mjs';
|
|
17
18
|
import { getUserEnvPath, loadConstructEnv, parseEnvFile, writeEnvValues } from './env-config.mjs';
|
|
18
19
|
import { runPressureRelease } from './runtime-pressure.mjs';
|
|
@@ -22,6 +23,7 @@ import { readHeartbeat } from './daemons/contract.mjs';
|
|
|
22
23
|
import { heartbeatPath } from './oracle/index.mjs';
|
|
23
24
|
import { stateDir } from './config/xdg.mjs';
|
|
24
25
|
import { doctorRoot } from './config/xdg.mjs';
|
|
26
|
+
import { resolveStateDir } from './state-root.mjs';
|
|
25
27
|
|
|
26
28
|
const INSTALL_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
29
|
|
|
@@ -163,13 +165,19 @@ async function isCopilotBridgeRunning(port) {
|
|
|
163
165
|
return probeRuntimePort(port);
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
// Opt-in 1Password resolution (ADR-0049 Design A):
|
|
167
|
-
// under `op run --env-file
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
168
|
+
// Opt-in 1Password resolution (ADR-0049 Design A): under `construct dev` the process
|
|
169
|
+
// re-execs once under a single `op run --env-file` (maybeReExecUnderOpRun), so op://
|
|
170
|
+
// references resolve one time at that stable parent and every daemon here inherits the
|
|
171
|
+
// resolved keys through liveEnv — the CONSTRUCT_OP_RUN_ACTIVE sentinel then makes
|
|
172
|
+
// wrapWithOpRun a no-op so no daemon nests a second op run. wrapWithOpRun stays the
|
|
173
|
+
// fallback for daemons started outside that parent (an opencode/copilot-bridge restart
|
|
174
|
+
// from a hook), wrapping them individually. It returns the command unchanged when
|
|
175
|
+
// CONSTRUCT_OP_ENV_FILE is unset, the file is missing, `op` is absent, or the sentinel
|
|
176
|
+
// is set, so the un-opted path is byte-for-byte the prior behavior. doctor, oracle, and
|
|
177
|
+
// cm consume no provider API keys (construct-0wmj) and are spawned unwrapped; every
|
|
178
|
+
// service is still spawned with the merged liveEnv, so config.env-only vars (including
|
|
179
|
+
// CONSTRUCT_OP_ENV_FILE and an OP_SERVICE_ACCOUNT_TOKEN) reach the child rather than
|
|
180
|
+
// only the wrapped op-run subset.
|
|
173
181
|
|
|
174
182
|
function wrapServiceSpawn(command, args, homeDir, options = {}) {
|
|
175
183
|
const env = options.env || process.env;
|
|
@@ -225,9 +233,9 @@ export function startDoctor({ rootDir, homeDir = os.homedir() } = {}) {
|
|
|
225
233
|
if (existing) return { started: false, reused: true, pid: existing.pid };
|
|
226
234
|
const indexPath = path.join(INSTALL_ROOT, 'lib', 'doctor', 'index.mjs');
|
|
227
235
|
if (!fs.existsSync(indexPath)) return { started: false, reason: 'missing-binary' };
|
|
236
|
+
// doctor consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
228
237
|
const doctorEnv = { ...process.env };
|
|
229
|
-
const
|
|
230
|
-
const { logPath } = spawnDetached(launch.command, launch.args, homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
|
|
238
|
+
const { logPath } = spawnDetached('node', [indexPath], homeDir, 'doctor.log', { cwd: rootDir, env: doctorEnv });
|
|
231
239
|
return { started: true, logPath };
|
|
232
240
|
}
|
|
233
241
|
|
|
@@ -244,8 +252,8 @@ export function startOracle({ rootDir, homeDir = os.homedir(), projectDir = root
|
|
|
244
252
|
CONSTRUCT_ORACLE_ROOT: rootDir,
|
|
245
253
|
CONSTRUCT_ORACLE_PROJECT: projectDir,
|
|
246
254
|
};
|
|
247
|
-
|
|
248
|
-
const { logPath } = spawnDetached(
|
|
255
|
+
// oracle consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
256
|
+
const { logPath } = spawnDetached('node', [entry], homeDir, 'oracle-daemon.log', {
|
|
249
257
|
cwd: projectDir,
|
|
250
258
|
env: oracleEnv,
|
|
251
259
|
});
|
|
@@ -338,6 +346,17 @@ export async function startServices({
|
|
|
338
346
|
const operatorKeepAlive = resolveOllamaKeepAlive(liveEnv);
|
|
339
347
|
if (operatorKeepAlive) liveEnv.OLLAMA_KEEP_ALIVE = operatorKeepAlive;
|
|
340
348
|
|
|
349
|
+
// The Copilot bridge fronts the user's Copilot entitlement over loopback HTTP, so it
|
|
350
|
+
// requires a bearer token. Generate a stable token once, persist it to the 0600
|
|
351
|
+
// config.env, and set it on liveEnv before OpenCode (the only caller) spawns so both
|
|
352
|
+
// the bridge and OpenCode inherit it; writeOpenCodeConfig wires it as an {env:} auth
|
|
353
|
+
// header. Reusing the persisted value keeps a restarted bridge and OpenCode in sync.
|
|
354
|
+
if (wants('copilot-bridge') && support.gh && detectActiveSessions().includes('github-copilot') && !liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN) {
|
|
355
|
+
const copilotBridgeToken = randomBytes(24).toString('hex');
|
|
356
|
+
liveEnv.CONSTRUCT_COPILOT_BRIDGE_TOKEN = copilotBridgeToken;
|
|
357
|
+
writeEnvValues(envPath, { CONSTRUCT_COPILOT_BRIDGE_TOKEN: copilotBridgeToken });
|
|
358
|
+
}
|
|
359
|
+
|
|
341
360
|
const pressureReport = runPressureReleaseFn({ env: { ...liveEnv, HOME: homeDir } });
|
|
342
361
|
if (pressureReport?.killed?.length) {
|
|
343
362
|
results.push({
|
|
@@ -354,14 +373,14 @@ export async function startServices({
|
|
|
354
373
|
if (traceBackend === 'local') {
|
|
355
374
|
results.push({
|
|
356
375
|
name: 'Telemetry',
|
|
357
|
-
url:
|
|
376
|
+
url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
|
|
358
377
|
status: 'configured',
|
|
359
378
|
note: 'local JSONL traces; remote export not configured',
|
|
360
379
|
});
|
|
361
380
|
} else if (traceBackend === 'none') {
|
|
362
381
|
results.push({
|
|
363
382
|
name: 'Telemetry',
|
|
364
|
-
url:
|
|
383
|
+
url: resolveStateDir(rootDir, 'traces', { ensureDir: false }),
|
|
365
384
|
status: 'configured',
|
|
366
385
|
note: 'remote export disabled; local JSONL traces preserved',
|
|
367
386
|
});
|
|
@@ -390,12 +409,12 @@ export async function startServices({
|
|
|
390
409
|
if (await memoryProbeFn(ports.memory)) {
|
|
391
410
|
results.push({ name: 'Memory (cm)', url: `http://127.0.0.1:${ports.memory}`, status: 'reused' });
|
|
392
411
|
} else {
|
|
393
|
-
|
|
394
|
-
const cmSpawn = spawnDetachedFn(cm
|
|
412
|
+
// cm consumes no provider API keys (construct-0wmj) — no op-run wrap needed.
|
|
413
|
+
const cmSpawn = spawnDetachedFn('cm', ['serve', '--port', String(ports.memory)], homeDir, 'cm.log', { env: liveEnv });
|
|
395
414
|
try {
|
|
396
415
|
if (cmSpawn?.child?.pid) {
|
|
397
416
|
writeJson(portOwnershipPath(ports.memory, homeDir), {
|
|
398
|
-
pid: cmSpawn.child.pid, command:
|
|
417
|
+
pid: cmSpawn.child.pid, command: `cm serve --port ${ports.memory}`,
|
|
399
418
|
cwd: process.cwd(), marker: 'construct', constructManaged: true,
|
|
400
419
|
startedAt: new Date().toISOString(),
|
|
401
420
|
});
|
|
@@ -413,7 +432,7 @@ export async function startServices({
|
|
|
413
432
|
results.push({ name: 'OpenCode', url: `http://127.0.0.1:${ports.bridge}`, status: 'reused' });
|
|
414
433
|
} else {
|
|
415
434
|
const opencode = wrapServiceSpawn('opencode', ['serve', '--port', String(ports.bridge)], homeDir, { env: liveEnv });
|
|
416
|
-
const ocSpawn = spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log');
|
|
435
|
+
const ocSpawn = spawnDetachedFn(opencode.command, opencode.args, homeDir, 'opencode.log', { env: liveEnv });
|
|
417
436
|
try {
|
|
418
437
|
if (ocSpawn?.child?.pid) {
|
|
419
438
|
writeJson(portOwnershipPath(ports.bridge, homeDir), {
|
|
@@ -438,7 +457,7 @@ export async function startServices({
|
|
|
438
457
|
const proxyPath = path.join(INSTALL_ROOT, 'lib', 'bridges', 'copilot-proxy.mjs');
|
|
439
458
|
if (fs.existsSync(proxyPath)) {
|
|
440
459
|
const copilot = wrapServiceSpawn('node', [proxyPath, '--port', String(ports.copilotBridge)], homeDir, { env: liveEnv });
|
|
441
|
-
const copilotSpawn = spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log');
|
|
460
|
+
const copilotSpawn = spawnDetachedFn(copilot.command, copilot.args, homeDir, 'copilot-bridge.log', { env: liveEnv });
|
|
442
461
|
try {
|
|
443
462
|
if (copilotSpawn?.child?.pid) {
|
|
444
463
|
writeJson(portOwnershipPath(ports.copilotBridge, homeDir), {
|
package/lib/setup.mjs
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* lib/setup.mjs — Machine-scoped first-run setup wizard for Construct.
|
|
4
4
|
*
|
|
5
|
-
* Installs cm/cass, configures managed defaults,
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Installs cm/cass, configures managed defaults, wires MCP integrations, and
|
|
6
|
+
* writes the XDG user config (~/.config/construct/config.env). Invoked by
|
|
7
|
+
* `construct install` and by lib/install/first-invocation.mjs when a TTY
|
|
8
|
+
* user is missing resources.
|
|
9
|
+
*
|
|
10
|
+
* The embedding model and LanceDB vector store provision lazily by default
|
|
11
|
+
* (construct-rf26.17) — pass --with-embeddings to warm both up during
|
|
12
|
+
* install instead of at first semantic-search use, mirroring --with-docling.
|
|
9
13
|
*
|
|
10
14
|
* Project-scoped scaffolding (`.cx/`, AGENTS.md, plan.md, adapters) lives
|
|
11
15
|
* in lib/init-unified.mjs — invoked by `construct init`. These two flows
|
|
@@ -35,6 +39,7 @@ import { getEmbeddingModelInfo, warmupEmbeddingModel } from './storage/embedding
|
|
|
35
39
|
import { buildPressureGuardValues, installPressureGuardLaunchAgent, loadPressureGuardLaunchAgent } from './runtime-pressure.mjs';
|
|
36
40
|
import { consentToInstall } from './setup-prompts.mjs';
|
|
37
41
|
import { cleanupLegacyGlobalConstruct } from './install/legacy-global-cleanup.mjs';
|
|
42
|
+
import { isMainModule } from './roots.mjs';
|
|
38
43
|
|
|
39
44
|
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
|
|
40
45
|
const HOME = os.homedir();
|
|
@@ -59,6 +64,9 @@ Flags:
|
|
|
59
64
|
--with-docling eagerly provision the docling document-extraction venv now
|
|
60
65
|
(heavy, ~10 min via uv; otherwise provisioned lazily on
|
|
61
66
|
first document ingest)
|
|
67
|
+
--with-embeddings warm the embedding model + LanceDB vector store dir now
|
|
68
|
+
(loads from local cache only, offline-safe; otherwise
|
|
69
|
+
both provision lazily on first semantic-search use)
|
|
62
70
|
--cleanup-legacy-global
|
|
63
71
|
remove legacy user-global Construct adapters/MCP/history
|
|
64
72
|
before setup; does not uninstall a global construct CLI
|
|
@@ -330,7 +338,7 @@ function runLegacyGlobalCleanup({ homeDir, dryRun }) {
|
|
|
330
338
|
// network call, or service registration that the real run would perform — and
|
|
331
339
|
// returns before touching disk so the preview is provably side-effect-free.
|
|
332
340
|
|
|
333
|
-
function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupLegacyGlobal }) {
|
|
341
|
+
function printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent, cleanupLegacyGlobal }) {
|
|
334
342
|
console.log('Construct install — dry-run (no changes written)');
|
|
335
343
|
console.log('────────────────────────────────────────────────');
|
|
336
344
|
console.log(`Scope: ${scope}`);
|
|
@@ -350,11 +358,11 @@ function printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent, cleanupL
|
|
|
350
358
|
console.log(` · ${getCanonicalOpenCodeConfigPath(homeDir)} (OpenCode config)`);
|
|
351
359
|
console.log(` · ${path.join(configDir(homeDir), 'lib')} → ${path.join(ROOT_DIR, 'lib')} (hook lib symlink)`);
|
|
352
360
|
console.log(` · ${path.join(stateDir(homeDir), 'workspace')} (workspace docs scaffold)`);
|
|
353
|
-
console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
|
|
361
|
+
if (withEmbeddings) console.log(` · ${path.dirname(defaultVectorIndexPath(homeDir))} (LanceDB vector store dir)`);
|
|
354
362
|
console.log('\nWould check / install runtime tooling:');
|
|
355
363
|
console.log(' · cm (memory CLI) and cass (session search) when available on PATH');
|
|
356
|
-
console.log('\nWould reach the network / external services:');
|
|
357
|
-
console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
|
|
364
|
+
if (withEmbeddings || withDocling) console.log('\nWould reach the network / external services:');
|
|
365
|
+
if (withEmbeddings) console.log(' · embedding model warmup (loads from local cache; offline-safe, no download)');
|
|
358
366
|
if (withDocling) console.log(' · docling Python venv provisioning via uv (~10 min)');
|
|
359
367
|
console.log(' · construct sync --quiet, construct sync --global, construct doctor');
|
|
360
368
|
if (process.platform === 'darwin' && !noLaunchAgent) {
|
|
@@ -375,9 +383,10 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
375
383
|
return;
|
|
376
384
|
}
|
|
377
385
|
|
|
378
|
-
const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--cleanup-legacy-global', '--help', '-h']);
|
|
386
|
+
const KNOWN_FLAGS = new Set(['--yes', '--dry-run', '--no-launch-agent', '--reconfigure', '--with-docling', '--with-embeddings', '--cleanup-legacy-global', '--help', '-h']);
|
|
379
387
|
const dryRun = argSet.has('--dry-run');
|
|
380
388
|
const withDocling = argSet.has('--with-docling');
|
|
389
|
+
const withEmbeddings = argSet.has('--with-embeddings');
|
|
381
390
|
const unknownFlags = args.filter((a) => {
|
|
382
391
|
if (!a.startsWith('-')) return false;
|
|
383
392
|
if (a.startsWith('--scope=') || a === '--scope') return false;
|
|
@@ -402,7 +411,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
402
411
|
printProjectScopeGuidance();
|
|
403
412
|
return;
|
|
404
413
|
}
|
|
405
|
-
printInstallPlan({ scope, homeDir, withDocling, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
|
|
414
|
+
printInstallPlan({ scope, homeDir, withDocling, withEmbeddings, noLaunchAgent: argSet.has('--no-launch-agent'), cleanupLegacyGlobal });
|
|
406
415
|
return;
|
|
407
416
|
}
|
|
408
417
|
|
|
@@ -470,20 +479,29 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
470
479
|
|
|
471
480
|
const telemetryResult = process.env.CONSTRUCT_TELEMETRY_URL
|
|
472
481
|
? { status: 'configured', note: `remote export configured (${process.env.CONSTRUCT_TELEMETRY_URL})` }
|
|
473
|
-
: { status: 'local', note: 'local JSONL traces in
|
|
482
|
+
: { status: 'local', note: 'local JSONL traces in the machine-scoped state root; remote export optional' };
|
|
474
483
|
console.log(`Telemetry: ${telemetryResult.note}`);
|
|
475
484
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
485
|
+
// The embedding model and LanceDB vector store dir are provisioned lazily
|
|
486
|
+
// by default (construct-rf26.17): a project that never runs semantic
|
|
487
|
+
// search never pays for the ONNX model cache or an index directory. Both
|
|
488
|
+
// provision on first real use (`construct ingest`, first observation
|
|
489
|
+
// store/search) exactly like docling below. --with-embeddings opts into
|
|
490
|
+
// warming both up now instead of at first query.
|
|
491
|
+
if (withEmbeddings) {
|
|
492
|
+
fs.mkdirSync(path.dirname(defaultVectorIndexPath(homeDir)), { recursive: true });
|
|
493
|
+
try {
|
|
494
|
+
const warm = await warmupEmbeddingModel({ env: process.env });
|
|
495
|
+
if (warm.degraded) {
|
|
496
|
+
console.log(`Embeddings: degraded fallback (${warm.fallbackReason || 'model unavailable'}) — semantic search will use the hashing backend`);
|
|
497
|
+
} else {
|
|
498
|
+
console.log(`Embeddings: ${warm.model} ready (${warm.dimensions}d, warmed in ${warm.durationMs}ms)`);
|
|
499
|
+
}
|
|
500
|
+
} catch (err) {
|
|
501
|
+
console.log(`Embeddings: warmup skipped (${err?.message || 'unknown error'}) — model will load on first use`);
|
|
484
502
|
}
|
|
485
|
-
}
|
|
486
|
-
console.log(
|
|
503
|
+
} else {
|
|
504
|
+
console.log('Embeddings: will provision on first semantic-search use (pass --with-embeddings to warm up now)');
|
|
487
505
|
}
|
|
488
506
|
|
|
489
507
|
// Docling (document/PDF extraction) provisions a pinned Python venv via uv,
|
|
@@ -592,7 +610,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
592
610
|
console.log('Setup complete.');
|
|
593
611
|
|
|
594
612
|
console.log('\nLocal services:');
|
|
595
|
-
console.log(' Traces: local JSONL (
|
|
613
|
+
console.log(' Traces: local JSONL (machine-scoped state root)');
|
|
596
614
|
console.log(` Vector: LanceDB (embedded)`);
|
|
597
615
|
console.log(` ${managedValues.CONSTRUCT_LANCEDB_PATH}`);
|
|
598
616
|
console.log(` Credentials are saved to ${getUserEnvPath(homeDir)} for later reference.`);
|
|
@@ -623,6 +641,6 @@ export function ensureWorkspace(homeDir = HOME) {
|
|
|
623
641
|
return wsPath;
|
|
624
642
|
}
|
|
625
643
|
|
|
626
|
-
if (import.meta.url
|
|
644
|
+
if (isMainModule(import.meta.url)) {
|
|
627
645
|
runSetup({ args: process.argv.slice(2) });
|
|
628
646
|
}
|
package/lib/skills-apply.mjs
CHANGED
|
@@ -87,8 +87,10 @@ export function writeConstructManifest(cwd, profile, classification) {
|
|
|
87
87
|
* that records the recommended per-project scoping for Claude Code.
|
|
88
88
|
*
|
|
89
89
|
* Why a sidecar and not `settings.json`? Claude Code today only reads
|
|
90
|
-
* `permissions`, `hooks`, and
|
|
91
|
-
* `
|
|
90
|
+
* `permissions`, `hooks`, and policy keys (`allowedMcpServers`,
|
|
91
|
+
* `deniedMcpServers`, etc.) from settings.json — MCP server definitions
|
|
92
|
+
* live in `~/.claude.json` or `.mcp.json` instead (construct-ranh). A
|
|
93
|
+
* `disabledPlugins` key would be ignored silently either way. Writing a sidecar is
|
|
92
94
|
* honest — it documents the recommendation without pretending the harness
|
|
93
95
|
* enforces it. The construct-mcp session-start path can read this sidecar
|
|
94
96
|
* and inject a "skills out of scope for this project" note.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - cx-reviewer prevents silent rubber-stamp reviews
|
|
19
19
|
* - cx-security prevents post-hoc threat models
|
|
20
20
|
* - cx-debugger prevents symptom-only fixes
|
|
21
|
-
* - cx-
|
|
21
|
+
* - cx-operations prevents stale-doc PRs (docs-keeper folded in, construct-rf26.11)
|
|
22
22
|
* - cx-designer prevents post-hoc accessibility
|
|
23
23
|
*/
|
|
24
24
|
|
|
@@ -75,7 +75,7 @@ export const POSTCONDITIONS = {
|
|
|
75
75
|
reason: `rootCauseConfirmedVia must be one of: ${[...ROOT_CAUSE_SOURCES].join(', ')}.`,
|
|
76
76
|
},
|
|
77
77
|
],
|
|
78
|
-
'cx-
|
|
78
|
+
'cx-operations': [
|
|
79
79
|
{
|
|
80
80
|
id: 'docs-keeper.cross-doc-coherence-check-ran',
|
|
81
81
|
description: 'Docs-keeper must run the cross-doc coherence check and attach a named diff.',
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/state-root.mjs — machine-scoped state roots for heavy runtime state (ADR-0066).
|
|
3
|
+
*
|
|
4
|
+
* Two distinct scopes live under `constructDir()` (`~/.construct/` or
|
|
5
|
+
* `$CX_TOOLKIT_DIR`), and this module is the only place either is resolved:
|
|
6
|
+
*
|
|
7
|
+
* - Per-project state (`resolveStateRoot`/`resolveStateDir`/`resolveStatePath`):
|
|
8
|
+
* `~/.construct/projects/<key>/` — traces, observations, the vector index,
|
|
9
|
+
* task graphs. Keyed by `deriveProjectKey`, a derivation stable across
|
|
10
|
+
* clones of the same git remote so two checkouts of one repository share
|
|
11
|
+
* accumulated state. A project with no remote (a fresh local-only repo)
|
|
12
|
+
* falls back to a hash of its resolved absolute path.
|
|
13
|
+
*
|
|
14
|
+
* - Machine-shared state (`resolveSharedRuntimeDir`): `~/.construct/runtime/`
|
|
15
|
+
* — not keyed per project at all. Reserved for state whose cost scales with
|
|
16
|
+
* the binary itself, not with any one project (the docling venv,
|
|
17
|
+
* construct-rf26.16): provisioning it per-project multiplies a genuinely
|
|
18
|
+
* large cost for content that isn't project-specific.
|
|
19
|
+
*
|
|
20
|
+
* `.beads/` is a deliberate exception (ADR-0026): issue history travels with
|
|
21
|
+
* the repository and is never resolved through this module.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import fs from 'node:fs';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { createHash } from 'node:crypto';
|
|
27
|
+
import { execFileSync } from 'node:child_process';
|
|
28
|
+
|
|
29
|
+
import { constructDir } from './paths.mjs';
|
|
30
|
+
|
|
31
|
+
function normalizeRemote(url) {
|
|
32
|
+
let s = url.trim();
|
|
33
|
+
|
|
34
|
+
// `git@host:owner/repo.git` (scp-like shorthand) has no `scheme://` prefix,
|
|
35
|
+
// so it needs its own split before the generic scheme/credential strip.
|
|
36
|
+
const scp = s.match(/^[^@/]+@([^:]+):(.+)$/);
|
|
37
|
+
if (scp && !/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(s)) {
|
|
38
|
+
s = `${scp[1]}/${scp[2]}`;
|
|
39
|
+
} else {
|
|
40
|
+
s = s.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//, '');
|
|
41
|
+
s = s.replace(/^[^@/]+@/, '');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return s.replace(/\.git$/, '').replace(/\/+$/, '').toLowerCase();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function readOriginRemote(projectRoot) {
|
|
48
|
+
try {
|
|
49
|
+
const out = execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
50
|
+
cwd: projectRoot,
|
|
51
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
52
|
+
timeout: 5000,
|
|
53
|
+
}).toString().trim();
|
|
54
|
+
return out || null;
|
|
55
|
+
} catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The path-hash fallback (no remote) must be symlink-invariant: a caller that
|
|
61
|
+
// reaches the same directory through a symlinked path (e.g. macOS's /var/folders
|
|
62
|
+
// tmp symlink, or a home-directory alias) must still resolve to the same key.
|
|
63
|
+
// realpath is best-effort — a not-yet-created path falls back to path.resolve.
|
|
64
|
+
|
|
65
|
+
function canonicalPath(projectRoot) {
|
|
66
|
+
const resolved = path.resolve(projectRoot);
|
|
67
|
+
try {
|
|
68
|
+
return fs.realpathSync(resolved);
|
|
69
|
+
} catch {
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Derive a stable key for a project: the normalized git origin remote when
|
|
76
|
+
* one exists (so every clone of the same repository resolves to the same
|
|
77
|
+
* key), otherwise a hash of the canonical (symlink-resolved) absolute project
|
|
78
|
+
* path.
|
|
79
|
+
*/
|
|
80
|
+
export function deriveProjectKey(projectRoot) {
|
|
81
|
+
const resolved = path.resolve(projectRoot);
|
|
82
|
+
const remote = readOriginRemote(resolved);
|
|
83
|
+
const material = remote ? `remote:${normalizeRemote(remote)}` : `path:${canonicalPath(projectRoot)}`;
|
|
84
|
+
return createHash('sha256').update(material).digest('hex').slice(0, 24);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A trailing plain-object argument is an options bag ({ ensureDir }), never a
|
|
88
|
+
// path segment — path segments are always strings.
|
|
89
|
+
|
|
90
|
+
function popOptions(segments) {
|
|
91
|
+
const last = segments[segments.length - 1];
|
|
92
|
+
if (last !== null && typeof last === 'object' && !Array.isArray(last)) {
|
|
93
|
+
return [segments.slice(0, -1), last];
|
|
94
|
+
}
|
|
95
|
+
return [segments, {}];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Resolve the machine-scoped state root for a project. Creates the directory
|
|
100
|
+
* (mkdir -p) on this access by default rather than up front — pass
|
|
101
|
+
* `{ ensureDir: false }` for a caller that only wants the path (e.g. a probe
|
|
102
|
+
* that may never actually write). Built on constructDir() so the existing
|
|
103
|
+
* CX_TOOLKIT_DIR override relocates per-project state along with the rest of
|
|
104
|
+
* `~/.construct/`.
|
|
105
|
+
*/
|
|
106
|
+
export function resolveStateRoot(projectRoot, { ensureDir = true } = {}) {
|
|
107
|
+
const dir = path.join(constructDir(), 'projects', deriveProjectKey(projectRoot));
|
|
108
|
+
if (ensureDir) fs.mkdirSync(dir, { recursive: true });
|
|
109
|
+
return dir;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Resolve a subdirectory under a project's state root, creating it by
|
|
114
|
+
* default. Pass a trailing `{ ensureDir: false }` to skip creation.
|
|
115
|
+
*/
|
|
116
|
+
export function resolveStateDir(projectRoot, ...segments) {
|
|
117
|
+
const [parts, { ensureDir = true }] = popOptions(segments);
|
|
118
|
+
const dir = path.join(resolveStateRoot(projectRoot, { ensureDir: false }), ...parts);
|
|
119
|
+
if (ensureDir) fs.mkdirSync(dir, { recursive: true });
|
|
120
|
+
return dir;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Resolve a file path under a project's state root, creating the parent
|
|
125
|
+
* directory (not the file itself) by default. Pass a trailing
|
|
126
|
+
* `{ ensureDir: false }` to skip creation.
|
|
127
|
+
*/
|
|
128
|
+
export function resolveStatePath(projectRoot, ...segments) {
|
|
129
|
+
const [parts, { ensureDir = true }] = popOptions(segments);
|
|
130
|
+
const filePath = path.join(resolveStateRoot(projectRoot, { ensureDir: false }), ...parts);
|
|
131
|
+
if (ensureDir) fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
132
|
+
return filePath;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Resolve a machine-shared runtime directory: not project-keyed, unlike
|
|
137
|
+
* resolveStateRoot/resolveStateDir. Every project on this machine — and every
|
|
138
|
+
* clone or worktree of every project — resolves the same segments to the
|
|
139
|
+
* identical directory. Use for state whose provisioning cost is per-machine,
|
|
140
|
+
* per-binary (a shared uv venv), never per-project.
|
|
141
|
+
*/
|
|
142
|
+
export function resolveSharedRuntimeDir(...segments) {
|
|
143
|
+
const [parts, { ensureDir = true }] = popOptions(segments);
|
|
144
|
+
const dir = path.join(constructDir(), 'runtime', ...parts);
|
|
145
|
+
if (ensureDir) fs.mkdirSync(dir, { recursive: true });
|
|
146
|
+
return dir;
|
|
147
|
+
}
|