@geraldmaron/construct 1.2.2 → 1.3.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/README.md +7 -10
- package/bin/construct +286 -220
- package/bin/construct-postinstall.mjs +0 -21
- package/commands/work/optimize-prompts.md +1 -1
- package/examples/distribution/sources/adr.md +2 -2
- package/examples/seed-observations/decisions.md +1 -1
- package/lib/artifact-loop-core.mjs +412 -0
- package/lib/artifact-manifest.mjs +6 -0
- package/lib/artifact-release-gate.mjs +115 -49
- package/lib/audit-rules.mjs +2 -4
- package/lib/audit-skills.mjs +32 -20
- package/lib/audit-specialists.mjs +44 -26
- package/lib/auto-docs.mjs +6 -5
- package/lib/brand-prose.mjs +3 -3
- package/lib/brand-tokens.mjs +2 -2
- package/lib/certification/artifact-fixtures.mjs +4 -1
- package/lib/certification/demo-parity.mjs +6 -32
- package/lib/certification/real-llm-scenarios.mjs +58 -13
- package/lib/certification/role-cards.mjs +7 -6
- package/lib/certification/role-overlays.mjs +4 -4
- package/lib/certification/runner.mjs +9 -2
- package/lib/certification/skill-inventory.mjs +34 -22
- package/lib/certification/skill-scenarios.mjs +21 -8
- package/lib/certification/specialist-contracts.mjs +4 -3
- package/lib/certification/specialist-scenarios.mjs +7 -6
- package/lib/certification/status.mjs +5 -4
- package/lib/cli-commands.mjs +56 -57
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +125 -0
- package/lib/config/schema.mjs +5 -5
- package/lib/config/source-targets.mjs +80 -0
- package/lib/contracts/validate.mjs +119 -51
- package/lib/decisions/golden.mjs +3 -2
- package/lib/decisions/registry.mjs +11 -6
- package/lib/demo-project.mjs +188 -0
- package/lib/demo-recording.mjs +9 -34
- package/lib/demo-script.mjs +2 -2
- package/lib/demo-surface.mjs +13 -131
- package/lib/demo-tour-renderer.mjs +92 -0
- package/lib/demo.mjs +69 -54
- package/lib/diagram-export.mjs +63 -14
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +90 -22
- package/lib/doctor/watchers/credential-parity.mjs +97 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +1 -1
- package/lib/doctor/watchers/service-health.mjs +4 -32
- package/lib/document-export.mjs +73 -9
- package/lib/document-extract.mjs +10 -198
- package/lib/embed/artifact.mjs +2 -3
- package/lib/embed/cli.mjs +1 -1
- package/lib/embed/config.mjs +1 -2
- package/lib/embed/customer-profiles.mjs +1 -1
- package/lib/embed/daemon.mjs +5 -6
- package/lib/embed/demand-fetch.mjs +114 -2
- package/lib/embed/docs-lifecycle.mjs +9 -5
- package/lib/embed/intake-metrics.mjs +2 -2
- package/lib/embed/notifications.mjs +2 -3
- package/lib/embed/recommendation-store.mjs +25 -22
- package/lib/embed/role-framing.mjs +4 -9
- package/lib/embedded-contract/capability.mjs +4 -2
- package/lib/embedded-contract/contract-version.mjs +1 -1
- package/lib/env-config.mjs +2 -2
- package/lib/flavors/loader.mjs +21 -19
- package/lib/graph/build-from-registry.mjs +31 -7
- package/lib/graph/staleness.mjs +1 -1
- package/lib/headhunt.mjs +82 -35
- package/lib/hooks/agent-tracker.mjs +2 -2
- package/lib/hooks/config-protection.mjs +1 -1
- package/lib/hooks/registry-sync.mjs +3 -3
- package/lib/host-capabilities.mjs +5 -5
- package/lib/improvement/controller.mjs +2 -2
- package/lib/init-unified.mjs +19 -21
- package/lib/intake/classify.mjs +26 -0
- package/lib/intake/daemon.mjs +2 -3
- package/lib/intake/prepare.mjs +10 -3
- package/lib/intake/session-prelude.mjs +1 -1
- package/lib/intake/tables/creative.mjs +9 -9
- package/lib/intake/tables/operations.mjs +4 -4
- package/lib/intake/tables/research.mjs +2 -2
- package/lib/integrations/intake-integrations.mjs +9 -10
- package/lib/knowledge/research-store.mjs +2 -2
- package/lib/mcp/server.mjs +104 -61
- package/lib/mcp/tool-budget.mjs +1 -46
- package/lib/mcp/tool-recovery.mjs +44 -0
- package/lib/mcp/tools/artifact-author.mjs +36 -0
- package/lib/mcp/tools/find-tool.mjs +86 -0
- package/lib/mcp/tools/orchestration-run.mjs +107 -69
- package/lib/mcp/tools/project.mjs +4 -3
- package/lib/mcp/tools/{profile.mjs → scope.mjs} +54 -52
- package/lib/mcp/tools/skills.mjs +39 -22
- package/lib/mcp/tools/telemetry.mjs +2 -1
- package/lib/mcp/tools/workflow.mjs +1 -1
- package/lib/mcp-platform-config.mjs +7 -5
- package/lib/migrations/index.mjs +4 -2
- package/lib/migrations/v2-unified-registry.mjs +35 -0
- package/lib/model-router.mjs +203 -36
- package/lib/models/catalog.mjs +25 -9
- package/lib/models/execution-capability-profile.mjs +3 -3
- package/lib/models/execution-policy.mjs +7 -6
- package/lib/models/provider-poll.mjs +18 -4
- package/lib/opencode-config.mjs +56 -1
- package/lib/opencode-runtime-plugin.mjs +13 -10
- package/lib/oracle/artifact-gate.mjs +14 -4
- package/lib/oracle/cli.mjs +2 -0
- package/lib/oracle/dispatch.mjs +18 -1
- package/lib/oracle/execute.mjs +39 -7
- package/lib/oracle/index.mjs +1 -1
- package/lib/oracle/org-graph.mjs +10 -5
- package/lib/oracle/policy.mjs +6 -0
- package/lib/oracle/read-model.mjs +103 -6
- package/lib/oracle/reconcile.mjs +31 -4
- package/lib/oracle/remediation-dispatch.mjs +53 -0
- package/lib/oracle/routing.mjs +5 -0
- package/lib/oracle/synthesize.mjs +80 -3
- package/lib/orchestration/routing-tables.mjs +44 -10
- package/lib/orchestration/runtime.mjs +2 -0
- package/lib/orchestration/worker.mjs +1 -1
- package/lib/orchestration-policy.mjs +351 -62
- package/lib/overrides/resolver.mjs +1 -12
- package/lib/parity.mjs +37 -11
- package/lib/policy/engine.mjs +80 -15
- package/lib/prompt-composer.js +51 -15
- package/lib/prompt-metadata.mjs +3 -2
- package/lib/providers/connection-probe.mjs +58 -0
- package/lib/providers/copilot-auth.mjs +1 -1
- package/lib/providers/credential-bootstrap.mjs +1 -1
- package/lib/providers/op-run.mjs +3 -4
- package/lib/providers/secret-resolver.mjs +31 -0
- package/lib/publish-tooling.mjs +3 -11
- package/lib/publish.mjs +12 -29
- package/lib/reconcile/mcp-entry-reconcile.mjs +14 -9
- package/lib/reflect.mjs +2 -2
- package/lib/registry/agent-manifest.mjs +190 -0
- package/lib/registry/assemble.mjs +133 -0
- package/lib/registry/catalog.mjs +171 -0
- package/lib/registry/cli.mjs +590 -6
- package/lib/registry/consolidation.mjs +5 -4
- package/lib/registry/docs-sync.mjs +67 -0
- package/lib/registry/loader.mjs +147 -0
- package/lib/registry/org-io.mjs +76 -0
- package/lib/registry/retired-paths.mjs +71 -0
- package/lib/registry/surface-map.mjs +5 -7
- package/lib/registry/validator.mjs +438 -0
- package/lib/research-execution-policy.mjs +173 -0
- package/lib/roles/catalog.mjs +4 -9
- package/lib/roles/fence.mjs +107 -1
- package/lib/roles/flavor-bindings.mjs +68 -0
- package/lib/roles/gateway.mjs +140 -29
- package/lib/roles/manifest.mjs +22 -13
- package/lib/roles/router.mjs +1 -1
- package/lib/runtime-env.mjs +1 -1
- package/lib/scopes/enrich.mjs +67 -0
- package/lib/scopes/hooks.mjs +12 -0
- package/lib/{profiles → scopes}/lifecycle.mjs +70 -70
- package/lib/scopes/loader.mjs +104 -0
- package/lib/scopes/rebrand.mjs +32 -0
- package/lib/scopes/research-profile.mjs +16 -0
- package/lib/scopes/teams.mjs +98 -0
- package/lib/service-manager.mjs +1 -117
- package/lib/setup-prompts.mjs +1 -1
- package/lib/setup.mjs +17 -28
- package/lib/skills/composition-graph.mjs +98 -0
- package/lib/skills/router.mjs +80 -0
- package/lib/specialist-contracts.mjs +17 -18
- package/lib/specialists/postconditions.mjs +2 -2
- package/lib/specialists/prompt-schema.mjs +6 -5
- package/lib/specialists/roster.mjs +7 -12
- package/lib/specialists/schema.mjs +9 -6
- package/lib/status.mjs +25 -90
- package/lib/storage/file-lock.mjs +6 -3
- package/lib/telemetry/skill-calls.mjs +1 -1
- package/lib/templates/doc-presentation.mjs +63 -0
- package/lib/templates/visual-requirements.mjs +35 -2
- package/lib/term-format.mjs +107 -2
- package/lib/test-env-setup.mjs +21 -0
- package/lib/ui/components.mjs +42 -0
- package/lib/ui/glyphs.mjs +58 -0
- package/lib/ui/links.mjs +115 -0
- package/lib/ui/theme.mjs +108 -0
- package/lib/uninstall/uninstall.mjs +2 -1
- package/lib/validator.mjs +45 -8
- package/lib/validators/skill-effectiveness.mjs +174 -0
- package/lib/validators/skills.mjs +1 -1
- package/package.json +12 -7
- package/personas/construct.md +12 -3
- package/platforms/claude/CLAUDE.md +1 -1
- package/rules/common/beads-hygiene.md +52 -0
- package/rules/common/neurodivergent-output.md +4 -7
- package/rules/common/research.md +2 -0
- package/scripts/sync-specialists.mjs +260 -49
- package/skills/ai/prompt-optimizer.md +3 -3
- package/skills/brand/output-vibe.md +48 -0
- package/skills/docs/adr-workflow.md +3 -0
- package/skills/docs/backlog-proposal-workflow.md +3 -0
- package/skills/docs/codebase-research-workflow.md +5 -2
- package/skills/docs/customer-profile-workflow.md +3 -0
- package/skills/docs/document-ingest-workflow.md +3 -0
- package/skills/docs/evidence-ingest-workflow.md +4 -1
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +1 -1
- package/skills/docs/prfaq-workflow.md +3 -0
- package/skills/docs/product-intelligence-workflow.md +4 -1
- package/skills/docs/product-signal-workflow.md +3 -0
- package/skills/docs/research-workflow.md +13 -6
- package/skills/docs/runbook-workflow.md +3 -0
- package/skills/docs/strategy-workflow.md +3 -0
- package/skills/docs/user-research-workflow.md +6 -3
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/{engineer.ai.md → ai-engineer.md} +2 -2
- package/skills/roles/architect.ai-systems.md +1 -1
- package/skills/roles/architect.data.md +1 -1
- package/skills/roles/architect.enterprise.md +1 -1
- package/skills/roles/architect.integration.md +1 -1
- package/skills/roles/architect.md +1 -1
- package/skills/roles/architect.platform.md +1 -1
- package/skills/roles/{product-manager.business-strategy.md → business-strategist.md} +3 -3
- package/skills/roles/data-analyst.experiment.md +1 -1
- package/skills/roles/data-analyst.md +1 -1
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/data-analyst.product.md +1 -1
- package/skills/roles/data-analyst.telemetry.md +1 -1
- package/skills/roles/{engineer.data.md → data-engineer.md} +2 -2
- package/skills/roles/data-engineer.pipeline.md +2 -2
- package/skills/roles/data-engineer.vector-retrieval.md +2 -2
- package/skills/roles/data-engineer.warehouse.md +2 -2
- package/skills/roles/debugger.md +1 -1
- package/skills/roles/designer.accessibility.md +1 -1
- package/skills/roles/designer.md +1 -1
- package/skills/roles/{reviewer.devil-advocate.md → devil-advocate.md} +2 -2
- package/skills/roles/{operator.docs.md → docs-keeper.md} +3 -3
- package/skills/roles/engineer.md +3 -7
- package/skills/roles/{reviewer.evaluator.md → evaluator.md} +2 -2
- package/skills/roles/{researcher.explorer.md → explorer.md} +2 -2
- package/skills/roles/{operator.md → operations.md} +2 -5
- package/skills/roles/orchestrator.md +1 -1
- package/skills/roles/{engineer.platform.md → platform-engineer.md} +2 -2
- package/skills/roles/product-manager.ai-product.md +1 -1
- package/skills/roles/product-manager.enterprise.md +1 -1
- package/skills/roles/product-manager.growth.md +1 -2
- package/skills/roles/product-manager.md +1 -2
- package/skills/roles/product-manager.platform.md +1 -1
- package/skills/roles/product-manager.product.md +1 -1
- package/skills/roles/qa.ai-eval.md +1 -1
- package/skills/roles/qa.api-contract.md +1 -1
- package/skills/roles/qa.data-pipeline.md +1 -1
- package/skills/roles/qa.md +1 -1
- package/skills/roles/qa.web-ui.md +1 -1
- package/skills/roles/{operator.release.md → release-manager.md} +3 -3
- package/skills/roles/researcher.md +1 -1
- package/skills/roles/reviewer.md +1 -1
- package/skills/roles/security.ai.md +1 -1
- package/skills/roles/security.appsec.md +1 -1
- package/skills/roles/security.cloud.md +1 -1
- package/skills/roles/security.legal-compliance.md +1 -1
- package/skills/roles/security.md +1 -1
- package/skills/roles/security.privacy.md +1 -1
- package/skills/roles/security.supply-chain.md +1 -1
- package/skills/roles/{operator.sre.md → sre.md} +3 -3
- package/skills/roles/{qa.test-automation.md → test-automation.md} +2 -2
- package/skills/roles/{reviewer.trace.md → trace-reviewer.md} +2 -2
- package/skills/roles/{researcher.ux.md → ux-researcher.md} +2 -2
- package/skills/routing.json +18 -0
- package/skills/routing.md +1 -1
- package/specialists/artifact-manifest.json +2 -1
- package/specialists/audit-enrichments.json +14 -14
- package/specialists/org/contracts/accessibility-to-qa.json +37 -0
- package/specialists/org/contracts/any-to-business-strategist.json +57 -0
- package/specialists/org/contracts/any-to-debugger.json +38 -0
- package/specialists/org/contracts/any-to-designer.json +33 -0
- package/specialists/org/contracts/any-to-docs-keeper.json +34 -0
- package/specialists/org/contracts/any-to-explorer.json +39 -0
- package/specialists/org/contracts/any-to-sre-incident.json +33 -0
- package/specialists/org/contracts/any-to-trace-reviewer.json +32 -0
- package/specialists/org/contracts/architect-to-ai-engineer.json +32 -0
- package/specialists/org/contracts/architect-to-data-engineer.json +52 -0
- package/specialists/org/contracts/architect-to-devil-advocate.json +38 -0
- package/specialists/org/contracts/architect-to-engineer.json +34 -0
- package/specialists/org/contracts/architect-to-evaluator.json +59 -0
- package/specialists/org/contracts/architect-to-legal-compliance.json +55 -0
- package/specialists/org/contracts/architect-to-operations.json +45 -0
- package/specialists/org/contracts/architect-to-platform-engineer.json +42 -0
- package/specialists/org/contracts/business-strategist-to-product-manager.json +39 -0
- package/specialists/org/contracts/construct-to-orchestrator.json +36 -0
- package/specialists/org/contracts/construct-to-rd-lead.json +53 -0
- package/specialists/org/contracts/data-analyst-to-product-manager.json +43 -0
- package/specialists/org/contracts/data-engineer-to-platform-engineer.json +36 -0
- package/specialists/org/contracts/designer-to-accessibility.json +36 -0
- package/specialists/org/contracts/engineer-to-qa.json +34 -0
- package/specialists/org/contracts/engineer-to-reviewer.json +52 -0
- package/specialists/org/contracts/explorer-to-engineer.json +38 -0
- package/specialists/org/contracts/legal-compliance-to-release-manager.json +43 -0
- package/specialists/org/contracts/platform-engineer-to-engineer.json +31 -0
- package/specialists/org/contracts/product-manager-to-architect.json +71 -0
- package/specialists/org/contracts/product-manager-to-data-analyst.json +49 -0
- package/specialists/org/contracts/product-manager-to-ux-researcher.json +51 -0
- package/specialists/org/contracts/qa-to-release-manager.json +42 -0
- package/specialists/org/contracts/qa-to-test-automation.json +42 -0
- package/specialists/org/contracts/rd-lead-to-architect.json +38 -0
- package/specialists/org/contracts/researcher-to-architect.json +40 -0
- package/specialists/org/contracts/researcher-to-product-manager.json +57 -0
- package/specialists/org/contracts/reviewer-to-security.json +41 -0
- package/specialists/org/contracts/sre-to-release-manager.json +36 -0
- package/specialists/org/contracts/test-automation-to-engineer.json +34 -0
- package/specialists/org/contracts/trace-reviewer-to-sre.json +41 -0
- package/specialists/org/contracts/user-to-construct.json +30 -0
- package/specialists/org/groups/engineering-group.json +43 -0
- package/specialists/org/groups/governance-group.json +38 -0
- package/specialists/org/groups/operations-group.json +41 -0
- package/specialists/org/groups/product-group.json +46 -0
- package/specialists/org/groups/quality-group.json +42 -0
- package/specialists/org/groups/strategy-group.json +41 -0
- package/specialists/org/policies/action-approval.json +13 -0
- package/specialists/org/policies/agents-routing.json +13 -0
- package/specialists/org/policies/architecture.json +20 -0
- package/specialists/org/policies/bash-safety.json +13 -0
- package/specialists/org/policies/beads-hygiene.json +13 -0
- package/specialists/org/policies/bootstrap-state.json +13 -0
- package/specialists/org/policies/code-review.json +13 -0
- package/specialists/org/policies/coding-style.json +13 -0
- package/specialists/org/policies/comments.json +13 -0
- package/specialists/org/policies/commit-approval.json +13 -0
- package/specialists/org/policies/contract-preconditions.json +13 -0
- package/specialists/org/policies/deployment.json +20 -0
- package/specialists/org/policies/description.json +14 -0
- package/specialists/org/policies/design-approval.json +19 -0
- package/specialists/org/policies/doc-ownership.json +13 -0
- package/specialists/org/policies/file-path-fence.json +13 -0
- package/specialists/org/policies/framing.json +13 -0
- package/specialists/org/policies/git-workflow.json +13 -0
- package/specialists/org/policies/incident-response.json +15 -0
- package/specialists/org/policies/intake-triage.json +15 -0
- package/specialists/org/policies/neurodivergent-output.json +13 -0
- package/specialists/org/policies/no-fabrication.json +13 -0
- package/specialists/org/policies/patterns.json +13 -0
- package/specialists/org/policies/quality-gate-approval.json +17 -0
- package/specialists/org/policies/release-gates.json +13 -0
- package/specialists/org/policies/research-evidence.json +13 -0
- package/specialists/org/policies/review-before-change.json +13 -0
- package/specialists/org/policies/rollback.json +15 -0
- package/specialists/org/policies/scope-change.json +20 -0
- package/specialists/org/policies/secret-scan.json +13 -0
- package/specialists/org/policies/security-approval.json +17 -0
- package/specialists/org/policies/security.json +13 -0
- package/specialists/org/policies/session-efficiency.json +13 -0
- package/specialists/org/policies/skill-routing.json +13 -0
- package/specialists/org/policies/strategic-prioritization.json +17 -0
- package/specialists/org/policies/testing.json +13 -0
- package/specialists/org/policies/tool-invisibility.json +14 -0
- package/specialists/org/scopes/creative.json +54 -0
- package/specialists/org/scopes/operations.json +51 -0
- package/specialists/org/scopes/research.json +60 -0
- package/specialists/org/scopes/rnd.json +81 -0
- package/specialists/org/specialists/cx-accessibility.json +69 -0
- package/specialists/org/specialists/cx-ai-engineer.json +75 -0
- package/specialists/org/specialists/cx-architect.json +89 -0
- package/specialists/org/specialists/cx-business-strategist.json +72 -0
- package/specialists/org/specialists/cx-data-analyst.json +68 -0
- package/specialists/org/specialists/cx-data-engineer.json +71 -0
- package/specialists/org/specialists/cx-debugger.json +75 -0
- package/specialists/org/specialists/cx-designer.json +85 -0
- package/specialists/org/specialists/cx-devil-advocate.json +71 -0
- package/specialists/org/specialists/cx-docs-keeper.json +82 -0
- package/specialists/org/specialists/cx-engineer.json +106 -0
- package/specialists/org/specialists/cx-evaluator.json +69 -0
- package/specialists/org/specialists/cx-explorer.json +69 -0
- package/specialists/org/specialists/cx-legal-compliance.json +74 -0
- package/specialists/org/specialists/cx-operations.json +72 -0
- package/specialists/org/specialists/cx-oracle.json +46 -0
- package/specialists/org/specialists/cx-orchestrator.json +81 -0
- package/specialists/org/specialists/cx-platform-engineer.json +80 -0
- package/specialists/org/specialists/cx-product-manager.json +102 -0
- package/specialists/org/specialists/cx-qa.json +79 -0
- package/specialists/org/specialists/cx-rd-lead.json +73 -0
- package/specialists/org/specialists/cx-release-manager.json +77 -0
- package/specialists/org/specialists/cx-researcher.json +82 -0
- package/specialists/org/specialists/cx-reviewer.json +71 -0
- package/specialists/org/specialists/cx-security.json +91 -0
- package/specialists/org/specialists/cx-sre.json +94 -0
- package/specialists/org/specialists/cx-test-automation.json +69 -0
- package/specialists/org/specialists/cx-trace-reviewer.json +69 -0
- package/specialists/org/specialists/cx-ux-researcher.json +68 -0
- package/specialists/org/teams/accessibility-team.json +39 -0
- package/specialists/org/teams/design-team.json +40 -0
- package/specialists/org/teams/engineering-team.json +50 -0
- package/specialists/org/teams/governance-team.json +41 -0
- package/specialists/org/teams/operations-team.json +46 -0
- package/specialists/org/teams/product-management-team.json +45 -0
- package/specialists/org/teams/quality-team.json +49 -0
- package/specialists/org/teams/research-team.json +39 -0
- package/specialists/org/teams/strategy-team.json +48 -0
- package/specialists/org/teams/ux-research-team.json +39 -0
- package/specialists/prompts/_shared/validation-contract.md +1 -1
- package/specialists/prompts/_team-template.md +10 -0
- package/specialists/prompts/cx-accessibility.md +1 -0
- package/specialists/prompts/cx-ai-engineer.md +1 -1
- package/specialists/prompts/cx-business-strategist.md +1 -1
- package/specialists/prompts/cx-data-engineer.md +1 -1
- package/specialists/prompts/cx-designer.md +1 -0
- package/specialists/prompts/cx-devil-advocate.md +1 -1
- package/specialists/prompts/cx-docs-keeper.md +1 -1
- package/specialists/prompts/cx-evaluator.md +1 -1
- package/specialists/prompts/cx-explorer.md +1 -1
- package/specialists/prompts/cx-operations.md +1 -1
- package/specialists/prompts/cx-oracle.md +7 -3
- package/specialists/prompts/cx-orchestrator.md +17 -1
- package/specialists/prompts/cx-platform-engineer.md +1 -1
- package/specialists/prompts/cx-product-manager.md +2 -0
- package/specialists/prompts/cx-release-manager.md +1 -1
- package/specialists/prompts/cx-researcher.md +7 -4
- package/specialists/prompts/cx-sre.md +1 -1
- package/specialists/prompts/cx-test-automation.md +2 -2
- package/specialists/prompts/cx-trace-reviewer.md +3 -3
- package/specialists/prompts/cx-ux-researcher.md +2 -1
- package/templates/demos/scripts/architecture-review-adr.json +30 -0
- package/templates/demos/scripts/capability-contract.json +25 -0
- package/templates/demos/scripts/intake-triage.json +25 -0
- package/templates/demos/scripts/profile-doctor-health.json +25 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +2 -2
- package/templates/demos/tapes/architecture-review-adr.tape +44 -0
- package/templates/demos/tapes/capability-contract.tape +39 -0
- package/templates/demos/tapes/intake-triage.tape +39 -0
- package/templates/demos/tapes/profile-doctor-health.tape +39 -0
- package/templates/distribution/construct-brand.typ +23 -18
- package/templates/distribution/construct-decision.typ +1 -1
- package/templates/distribution/construct-pdf.typ +1 -1
- package/templates/distribution/construct-prd.typ +1 -1
- package/templates/distribution/construct-research.typ +1 -1
- package/templates/distribution/mermaid-puppeteer.json +8 -0
- package/templates/docs/persona-artifact.md +1 -1
- package/templates/docs/prd.md +6 -0
- package/lib/boundary.mjs +0 -127
- package/lib/certification/dashboard-api.mjs +0 -71
- package/lib/chat/cli.mjs +0 -333
- package/lib/chat/command-suggest.mjs +0 -161
- package/lib/chat/commands.mjs +0 -215
- package/lib/chat/config.mjs +0 -142
- package/lib/chat/context-compactor.mjs +0 -250
- package/lib/chat/context-continuation.mjs +0 -253
- package/lib/chat/continuation-source.mjs +0 -58
- package/lib/chat/demo-guide.mjs +0 -61
- package/lib/chat/design-tokens.mjs +0 -91
- package/lib/chat/desktop-binary.mjs +0 -81
- package/lib/chat/desktop-build.mjs +0 -130
- package/lib/chat/desktop-launcher.mjs +0 -133
- package/lib/chat/evidence.mjs +0 -145
- package/lib/chat/export.mjs +0 -74
- package/lib/chat/harness/driver.mjs +0 -91
- package/lib/chat/list-picker.mjs +0 -112
- package/lib/chat/model-picker.mjs +0 -356
- package/lib/chat/openrouter-fallback.mjs +0 -151
- package/lib/chat/permission-prompt.mjs +0 -33
- package/lib/chat/picker-catalog.mjs +0 -45
- package/lib/chat/policy-telemetry.mjs +0 -34
- package/lib/chat/present.mjs +0 -246
- package/lib/chat/session-context.mjs +0 -39
- package/lib/chat/session-persist.mjs +0 -73
- package/lib/chat/session-restore.mjs +0 -71
- package/lib/chat/session-settings.mjs +0 -53
- package/lib/chat/system-prompt.mjs +0 -52
- package/lib/chat/transparency.mjs +0 -93
- package/lib/chat/tui/color-scheme.mjs +0 -42
- package/lib/chat/tui/markdown.mjs +0 -123
- package/lib/chat/tui/presentation.mjs +0 -100
- package/lib/chat/tui/render.mjs +0 -500
- package/lib/chat/tui/turn-block.mjs +0 -284
- package/lib/chat/tui/turn-present.mjs +0 -18
- package/lib/chat/tui/turn-state.mjs +0 -88
- package/lib/chat/tui/usage.mjs +0 -122
- package/lib/chat/web-commands.mjs +0 -146
- package/lib/chat/web-launcher.mjs +0 -63
- package/lib/chat/web-picker-keys.mjs +0 -46
- package/lib/chat/web-session.mjs +0 -159
- package/lib/config/alias.mjs +0 -57
- package/lib/dashboard-demo.mjs +0 -71
- package/lib/dashboard-static.mjs +0 -175
- package/lib/install/desktop-binary-download.mjs +0 -88
- package/lib/profiles/loader.mjs +0 -123
- package/lib/profiles/rebrand.mjs +0 -46
- package/lib/server/auth.mjs +0 -169
- package/lib/server/chat-loop.mjs +0 -622
- package/lib/server/chat.mjs +0 -336
- package/lib/server/cors.mjs +0 -77
- package/lib/server/csrf.mjs +0 -103
- package/lib/server/demo-preview.mjs +0 -63
- package/lib/server/index.mjs +0 -2641
- package/lib/server/insights.mjs +0 -780
- package/lib/server/langfuse-login.mjs +0 -58
- package/lib/server/rate-limit.mjs +0 -93
- package/lib/server/telemetry-login.mjs +0 -100
- package/lib/server/webhook.mjs +0 -512
- package/specialists/contracts.json +0 -1032
- package/specialists/contracts.schema.json +0 -83
- package/specialists/policy-inventory.json +0 -188
- package/specialists/registry.json +0 -1412
- package/specialists/role-manifests.json +0 -217
- package/specialists/teams.json +0 -94
package/lib/headhunt.mjs
CHANGED
|
@@ -13,15 +13,19 @@ import os from 'node:os';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { createInterface } from 'node:readline';
|
|
15
15
|
import { selectOption } from './tty-prompts.mjs';
|
|
16
|
+
import { resolveActiveScope } from './scopes/loader.mjs';
|
|
17
|
+
import { classifyObjectiveForScope } from './scopes/teams.mjs';
|
|
18
|
+
import { loadRegistry } from './registry/loader.mjs';
|
|
16
19
|
|
|
17
20
|
const OVERLAY_DIRNAME = 'domain-overlays';
|
|
18
21
|
const PROMOTION_DIRNAME = 'promotion-requests';
|
|
19
|
-
const TEAMS_FILE = 'specialists/teams.json';
|
|
20
22
|
|
|
21
23
|
function loadTeamTemplates(root) {
|
|
22
24
|
try {
|
|
23
|
-
const
|
|
24
|
-
return
|
|
25
|
+
const registry = loadRegistry({ rootDir: root });
|
|
26
|
+
return {
|
|
27
|
+
templates: registry.teams || {},
|
|
28
|
+
};
|
|
25
29
|
} catch {
|
|
26
30
|
return null;
|
|
27
31
|
}
|
|
@@ -63,37 +67,70 @@ function promptQuestion(rl, question) {
|
|
|
63
67
|
return new Promise((resolve) => rl.question(question, resolve));
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
function classifyOverlay(objective = '', explicitTeam = '') {
|
|
70
|
+
export function classifyOverlay(objective = '', explicitTeam = '', { cwd = process.cwd() } = {}) {
|
|
67
71
|
if (explicitTeam) {
|
|
68
72
|
return {
|
|
69
73
|
focus: 'custom',
|
|
70
74
|
attachTo: explicitTeam.split(',').map((value) => value.trim()).filter(Boolean),
|
|
75
|
+
teamFocus: 'custom-team',
|
|
71
76
|
};
|
|
72
77
|
}
|
|
73
78
|
|
|
79
|
+
const profile = resolveActiveScope(cwd);
|
|
80
|
+
const profileMatch = classifyObjectiveForScope(objective, profile);
|
|
81
|
+
if (profileMatch) return profileMatch;
|
|
82
|
+
|
|
74
83
|
const text = objective.toLowerCase();
|
|
84
|
+
|
|
85
|
+
// NEW: Team-first classification returns both team id and specialist roles
|
|
75
86
|
if (/security|policy|compliance|secrets|iam|threat|audit/.test(text)) {
|
|
76
|
-
return {
|
|
87
|
+
return {
|
|
88
|
+
focus: 'security',
|
|
89
|
+
attachTo: ['cx-security', 'cx-architect'],
|
|
90
|
+
teamFocus: 'governance-group',
|
|
91
|
+
recommendedTeam: 'governance-group',
|
|
92
|
+
};
|
|
77
93
|
}
|
|
78
94
|
if (/implement|build|module|deploy|pipeline|ci\/cd|apply|code/.test(text)) {
|
|
79
|
-
return {
|
|
95
|
+
return {
|
|
96
|
+
focus: 'implementation',
|
|
97
|
+
attachTo: ['cx-engineer', 'cx-architect'],
|
|
98
|
+
teamFocus: 'engineering-group',
|
|
99
|
+
recommendedTeam: 'engineering-group',
|
|
100
|
+
};
|
|
80
101
|
}
|
|
81
102
|
if (/runbook|docs|documentation|guide|onboard/.test(text)) {
|
|
82
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
focus: 'documentation',
|
|
105
|
+
attachTo: ['cx-docs-keeper', 'cx-researcher'],
|
|
106
|
+
teamFocus: 'operations-group',
|
|
107
|
+
recommendedTeam: 'operations-group',
|
|
108
|
+
};
|
|
83
109
|
}
|
|
84
110
|
if (/research|compare|recent|latest|best practices|upgrade|migration/.test(text)) {
|
|
85
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
focus: 'research',
|
|
113
|
+
attachTo: ['cx-researcher', 'cx-architect'],
|
|
114
|
+
teamFocus: 'strategy-group',
|
|
115
|
+
recommendedTeam: 'strategy-group',
|
|
116
|
+
};
|
|
86
117
|
}
|
|
87
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
focus: 'architecture',
|
|
120
|
+
attachTo: ['cx-architect', 'cx-researcher'],
|
|
121
|
+
teamFocus: 'engineering-group',
|
|
122
|
+
recommendedTeam: 'engineering-group',
|
|
123
|
+
};
|
|
88
124
|
}
|
|
89
125
|
|
|
90
|
-
function buildOverlayPrompt({ domain, objective, scope, permanence, focus, freshness }) {
|
|
126
|
+
function buildOverlayPrompt({ domain, objective, scope, permanence, focus, freshness, teamFocus = '' }) {
|
|
91
127
|
return [
|
|
92
128
|
`Domain overlay: ${domain}`,
|
|
93
129
|
`Objective: ${objective}`,
|
|
94
130
|
scope ? `Scope: ${scope}` : null,
|
|
95
131
|
`Permanence: ${permanence}`,
|
|
96
132
|
`Primary focus: ${focus}`,
|
|
133
|
+
teamFocus ? `Team focus: ${teamFocus}` : null,
|
|
97
134
|
`Freshness requirement: ${freshness}`,
|
|
98
135
|
'Rules:',
|
|
99
136
|
'- Treat this as an internal Construct overlay, not a permanent public role.',
|
|
@@ -102,6 +139,7 @@ function buildOverlayPrompt({ domain, objective, scope, permanence, focus, fresh
|
|
|
102
139
|
'- Separate confirmed facts from inference.',
|
|
103
140
|
'- Respect existing Construct workflow, security, and validation boundaries.',
|
|
104
141
|
'- Do not broaden tool permissions just because the domain is specialized.',
|
|
142
|
+
'- NEW: Team-first approach — work within the team structure and escalation paths defined in your organization.',
|
|
105
143
|
].filter(Boolean).join('\n');
|
|
106
144
|
}
|
|
107
145
|
|
|
@@ -342,7 +380,7 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
|
|
|
342
380
|
if (!templateName) { listTeamTemplates(cwd); return; }
|
|
343
381
|
const root = findConstructRoot(cwd);
|
|
344
382
|
const teams = loadTeamTemplates(root);
|
|
345
|
-
|
|
383
|
+
if (!teams) throw new Error('specialists/org not found. Run from the Construct root.');
|
|
346
384
|
const tpl = teams.templates[templateName];
|
|
347
385
|
if (!tpl) throw new Error(`Unknown template: ${templateName}. Run 'construct headhunt template' to list available templates.`);
|
|
348
386
|
const objective = typeof flags.for === 'string' ? flags.for.trim() : rest.slice(1).join(' ').trim();
|
|
@@ -354,29 +392,30 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
|
|
|
354
392
|
const overlayDir = getOverlayDir(root);
|
|
355
393
|
const expiresAt = permanence === 'temporary' ? new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString() : null;
|
|
356
394
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
395
|
+
const overlay = {
|
|
396
|
+
id: overlayId,
|
|
397
|
+
type: 'domain-overlay',
|
|
398
|
+
domain: templateName,
|
|
399
|
+
objective,
|
|
400
|
+
scope: typeof flags.scope === 'string' ? flags.scope.trim() : null,
|
|
401
|
+
permanence,
|
|
402
|
+
focus: tpl.focus,
|
|
403
|
+
attachTo: tpl.members,
|
|
404
|
+
freshness,
|
|
405
|
+
teamTemplate: templateName,
|
|
406
|
+
teamId: overlayId,
|
|
407
|
+
teamFocus: tpl.focus,
|
|
408
|
+
createdAt: now.toISOString(),
|
|
409
|
+
expiresAt,
|
|
410
|
+
project: path.basename(root),
|
|
411
|
+
workflowId: null,
|
|
412
|
+
taskKey: null,
|
|
413
|
+
status: 'active',
|
|
414
|
+
prompt: buildOverlayPrompt({ domain: templateName, objective, scope: typeof flags.scope === 'string' ? flags.scope : '', permanence, focus: tpl.focus, freshness, teamFocus: tpl.focus }),
|
|
415
|
+
skills: tpl.skills ?? [],
|
|
416
|
+
promotionGates: tpl.promotionGates ?? buildPromotionGates(),
|
|
417
|
+
challenge: null,
|
|
418
|
+
};
|
|
380
419
|
|
|
381
420
|
const overlayJsonPath = path.join(overlayDir, `${overlayId}.json`);
|
|
382
421
|
const overlayMdPath = path.join(overlayDir, `${overlayId}.md`);
|
|
@@ -448,7 +487,11 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
|
|
|
448
487
|
}
|
|
449
488
|
|
|
450
489
|
const freshness = typeof flags.freshness === 'string' ? flags.freshness : 'current';
|
|
451
|
-
const classification = classifyOverlay(
|
|
490
|
+
const classification = classifyOverlay(
|
|
491
|
+
objective,
|
|
492
|
+
typeof flags.team === 'string' ? flags.team : '',
|
|
493
|
+
{ cwd },
|
|
494
|
+
);
|
|
452
495
|
const now = new Date();
|
|
453
496
|
const root = findConstructRoot(cwd);
|
|
454
497
|
const overlayId = `${slugify(domain)}-${now.toISOString().replace(/[:.]/g, '-')}`;
|
|
@@ -473,6 +516,9 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
|
|
|
473
516
|
taskKey: null,
|
|
474
517
|
status: permanence === 'temporary' ? 'active' : 'promotion_requested',
|
|
475
518
|
teamId: overlayId,
|
|
519
|
+
teamFocus: classification.teamFocus || classification.focus,
|
|
520
|
+
recommendedTeam: classification.recommendedTeam || null,
|
|
521
|
+
scopeTeamSource: classification.scopeTeamSource || null,
|
|
476
522
|
sourceStrategy: [
|
|
477
523
|
'official docs',
|
|
478
524
|
'release notes/changelogs',
|
|
@@ -488,6 +534,7 @@ export async function runHeadhunt({ args = [], cwd = process.cwd(), homeDir = os
|
|
|
488
534
|
permanence,
|
|
489
535
|
focus: classification.focus,
|
|
490
536
|
freshness,
|
|
537
|
+
teamFocus: classification.teamFocus,
|
|
491
538
|
}),
|
|
492
539
|
promotionGates: buildPromotionGates(),
|
|
493
540
|
challenge: permanence === 'temporary' ? null : {
|
|
@@ -155,10 +155,10 @@ try {
|
|
|
155
155
|
if (success !== null) {
|
|
156
156
|
try {
|
|
157
157
|
const { recordOutcome } = await import('../outcomes/record.mjs');
|
|
158
|
-
const {
|
|
158
|
+
const { resolveActiveScope } = await import('../scopes/loader.mjs');
|
|
159
159
|
const roleId = agentName.replace(/^cx-/, '');
|
|
160
160
|
const projectCwd = input?.cwd || process.cwd();
|
|
161
|
-
const activeProfile =
|
|
161
|
+
const activeProfile = resolveActiveScope(projectCwd);
|
|
162
162
|
recordOutcome(projectCwd, {
|
|
163
163
|
role: roleId,
|
|
164
164
|
success,
|
|
@@ -52,7 +52,7 @@ const PROTECTED = [
|
|
|
52
52
|
];
|
|
53
53
|
|
|
54
54
|
const META_FILES = [
|
|
55
|
-
/(?:^|\/)specialists\/
|
|
55
|
+
/(?:^|\/)specialists\/org(?:\/|$)/,
|
|
56
56
|
/(?:^|\/)lib\/setup\.mjs$/,
|
|
57
57
|
/(?:^|\/)bin\/construct-postinstall\.mjs$/,
|
|
58
58
|
/(?:^|\/)claude\/settings\.template\.json$/,
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* lib/hooks/registry-sync.mjs — Registry-change reminder hook.
|
|
4
4
|
*
|
|
5
5
|
* PostToolUse on Write|Edit. Fires only when the edited file is
|
|
6
|
-
* specialists/
|
|
6
|
+
* specialists/org/ (the construct repo) or an installed
|
|
7
7
|
* agents/registry.json mirror. Emits a one-line reminder on stderr telling
|
|
8
8
|
* the developer to run `construct sync` when they're ready. Does NOT
|
|
9
9
|
* auto-execute sync — auto-execution during tests (which legitimately
|
|
10
|
-
* mutate registry
|
|
10
|
+
* mutate the registry) races test cleanup and regenerates platform state
|
|
11
11
|
* mid-suite. The dev decides when to sync, the same way ordinary code
|
|
12
12
|
* changes don't auto-build.
|
|
13
13
|
*
|
|
@@ -30,7 +30,7 @@ const input = readHookInput();
|
|
|
30
30
|
const filePath = input?.tool_input?.file_path || process.env.TOOL_INPUT_FILE_PATH || '';
|
|
31
31
|
const matchesRegistry =
|
|
32
32
|
filePath.endsWith(path.join('agents', 'registry.json')) ||
|
|
33
|
-
filePath.
|
|
33
|
+
filePath.includes(`${path.sep}specialists${path.sep}org`);
|
|
34
34
|
if (!matchesRegistry) process.exit(0);
|
|
35
35
|
|
|
36
36
|
const isNonInteractive =
|
|
@@ -153,7 +153,7 @@ export function detectActiveSessions() {
|
|
|
153
153
|
// dispatch or config-driven agents): Claude Code, OpenCode.
|
|
154
154
|
// mcp-orchestrated — no native subagent primitive, but the host calls the
|
|
155
155
|
// `orchestration_run` MCP tool to reach the same orchestrated
|
|
156
|
-
// outcome
|
|
156
|
+
// outcome in-process: Codex, VS Code, Cursor, Copilot.
|
|
157
157
|
// prompt-only — neither native dispatch nor MCP; instruction files only.
|
|
158
158
|
// No supported 2026 host is prompt-only.
|
|
159
159
|
|
|
@@ -211,7 +211,7 @@ export function detectHostCapabilities() {
|
|
|
211
211
|
sharedTaskRuntime: "tracker-plus-plan",
|
|
212
212
|
lifecycleHooks: [],
|
|
213
213
|
notes: [
|
|
214
|
-
"Run a full multi-specialist chain via the `orchestration_run` MCP tool (
|
|
214
|
+
"Run a full multi-specialist chain via the `orchestration_run` MCP tool (in-process; no daemon needed).",
|
|
215
215
|
"Use Construct profiles, MCP project/memory tools, and the active session for single-pass work.",
|
|
216
216
|
],
|
|
217
217
|
},
|
|
@@ -225,7 +225,7 @@ export function detectHostCapabilities() {
|
|
|
225
225
|
sharedTaskRuntime: "editor-session",
|
|
226
226
|
lifecycleHooks: [],
|
|
227
227
|
notes: [
|
|
228
|
-
"Run a full multi-specialist chain via the `orchestration_run` MCP tool in Copilot agent mode (
|
|
228
|
+
"Run a full multi-specialist chain via the `orchestration_run` MCP tool in Copilot agent mode (in-process; no daemon needed).",
|
|
229
229
|
"Construct's MCP servers load from `.vscode/mcp.json` (project) and the user-profile `mcp.json` (global, only when it already exists).",
|
|
230
230
|
],
|
|
231
231
|
},
|
|
@@ -239,7 +239,7 @@ export function detectHostCapabilities() {
|
|
|
239
239
|
sharedTaskRuntime: "editor-session",
|
|
240
240
|
lifecycleHooks: [],
|
|
241
241
|
notes: [
|
|
242
|
-
"Run a full multi-specialist chain via the `orchestration_run` MCP tool (
|
|
242
|
+
"Run a full multi-specialist chain via the `orchestration_run` MCP tool (in-process; no daemon needed).",
|
|
243
243
|
"Construct manages Cursor MCP registrations in `.cursor/mcp.json` (project) and `~/.cursor/mcp.json` (global, when it already exists).",
|
|
244
244
|
],
|
|
245
245
|
},
|
|
@@ -253,7 +253,7 @@ export function detectHostCapabilities() {
|
|
|
253
253
|
sharedTaskRuntime: "editor-session",
|
|
254
254
|
lifecycleHooks: [],
|
|
255
255
|
notes: [
|
|
256
|
-
"Copilot agent mode runs Construct's MCP tools — use `orchestration_run` for a real multi-specialist chain (
|
|
256
|
+
"Copilot agent mode runs Construct's MCP tools — use `orchestration_run` for a real multi-specialist chain (in-process; no daemon needed).",
|
|
257
257
|
"Construct also writes reusable prompt profiles under `.github/prompts/` for single-pass role work.",
|
|
258
258
|
],
|
|
259
259
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* The controller is the mutation boundary for the governed loop. It admits only
|
|
6
6
|
* versioned artifacts — proposal objects, dataset items, and evaluation reports,
|
|
7
|
-
* never raw
|
|
7
|
+
* never raw unversioned session history — and refuses any candidate that lacks provenance,
|
|
8
8
|
* lacks held-out evaluation evidence, failed a deterministic gate, names an unknown
|
|
9
9
|
* approver, or has an unresolved dependency. Promotability is re-derived from the
|
|
10
10
|
* report's own gate results rather than a precomputed flag, so a tampered decision
|
|
@@ -22,7 +22,7 @@ function isVersioned(artifact) {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Admit the input artifacts. Only versioned proposal/dataset/evaluation objects are
|
|
25
|
-
* accepted; an unversioned blob (such as raw
|
|
25
|
+
* accepted; an unversioned blob (such as raw session history) is refused
|
|
26
26
|
* (construct-6zga.1.5 AC1).
|
|
27
27
|
*/
|
|
28
28
|
export function admitArtifacts({ proposal = null, dataset = null, evaluationReport = null } = {}) {
|
package/lib/init-unified.mjs
CHANGED
|
@@ -69,11 +69,11 @@ const commitBootstrap = args.includes("--commit-bootstrap");
|
|
|
69
69
|
const withHostFlags = new Set(HOST_KEYS.filter((k) => args.includes(`--with-${k}`)));
|
|
70
70
|
const allHosts = args.includes("--all-hosts");
|
|
71
71
|
|
|
72
|
-
// Active
|
|
73
|
-
// project's construct.config.json so
|
|
72
|
+
// Active scope selector. `--scope=<id>` writes the field into the
|
|
73
|
+
// project's construct.config.json so resolveActiveScope picks it up
|
|
74
74
|
// on first run. Unknown ids are rejected with the available catalog.
|
|
75
|
-
const
|
|
76
|
-
const
|
|
75
|
+
const scopeArg = args.find((arg) => arg.startsWith("--scope="));
|
|
76
|
+
const scopeId = scopeArg ? scopeArg.split("=")[1] : null;
|
|
77
77
|
|
|
78
78
|
const created = [];
|
|
79
79
|
const skipped = [];
|
|
@@ -745,10 +745,10 @@ async function main() {
|
|
|
745
745
|
const { findProjectConfigPath, loadProjectConfig, writeProjectConfig, PROJECT_CONFIG_FILENAME } = await import('./config/project-config.mjs');
|
|
746
746
|
const { DEFAULT_PROJECT_CONFIG } = await import('./config/schema.mjs');
|
|
747
747
|
|
|
748
|
-
if (
|
|
749
|
-
const {
|
|
750
|
-
if (!
|
|
751
|
-
console.error(`Unknown
|
|
748
|
+
if (scopeId) {
|
|
749
|
+
const { loadScope, listScopes } = await import('./scopes/loader.mjs');
|
|
750
|
+
if (!loadScope(scopeId)) {
|
|
751
|
+
console.error(`Unknown scope: ${scopeId}. Available: ${listScopes().join(', ')}`);
|
|
752
752
|
process.exit(1);
|
|
753
753
|
}
|
|
754
754
|
}
|
|
@@ -763,29 +763,29 @@ async function main() {
|
|
|
763
763
|
const cfg = {
|
|
764
764
|
$schema: "./node_modules/@geraldmaron/construct/schemas/project-config.schema.json",
|
|
765
765
|
...DEFAULT_PROJECT_CONFIG,
|
|
766
|
-
|
|
766
|
+
scope: scopeId || 'rnd',
|
|
767
767
|
};
|
|
768
768
|
// Explicitly write the full default config object so all options are
|
|
769
769
|
// visible and discoverable to the user in the file. The $schema property
|
|
770
770
|
// provides inline documentation and validation in supported editors.
|
|
771
771
|
writeProjectConfig(cfgPath, cfg, { validate: true, silent: true });
|
|
772
772
|
created.push(PROJECT_CONFIG_FILENAME);
|
|
773
|
-
if (!quiet &&
|
|
774
|
-
} else if (
|
|
773
|
+
if (!quiet && scopeId) console.log(`Scope set to ${scopeId}.`);
|
|
774
|
+
} else if (scopeId) {
|
|
775
775
|
const loaded = loadProjectConfig(target);
|
|
776
776
|
const cfg = loaded?.raw ? { ...loaded.raw } : { version: 1 };
|
|
777
|
-
cfg.
|
|
777
|
+
cfg.scope = scopeId;
|
|
778
778
|
writeProjectConfig(cfgPath, cfg, { validate: true, silent: true });
|
|
779
|
-
if (!quiet) console.log(`
|
|
779
|
+
if (!quiet) console.log(`Scope set to ${scopeId}.`);
|
|
780
780
|
}
|
|
781
781
|
}
|
|
782
782
|
|
|
783
|
-
// Resolve active
|
|
784
|
-
// archetype, attribution stamping). Falls back to rnd when no
|
|
785
|
-
// configured — matches
|
|
783
|
+
// Resolve active scope to drive capability-gated scaffolding (intake
|
|
784
|
+
// archetype, attribution stamping). Falls back to rnd when no scope is
|
|
785
|
+
// configured — matches resolveActiveScope semantics elsewhere.
|
|
786
786
|
|
|
787
|
-
const {
|
|
788
|
-
const activeProfile =
|
|
787
|
+
const { resolveActiveScope } = await import('./scopes/loader.mjs');
|
|
788
|
+
const activeProfile = resolveActiveScope(target, scopeId) ?? null;
|
|
789
789
|
const intakeCap = activeProfile?.capabilities?.intake ?? null;
|
|
790
790
|
const { gatherAttribution } = await import('./intake/attribution.mjs');
|
|
791
791
|
const attribution = intakeCap?.attribution ? gatherAttribution() : null;
|
|
@@ -1228,12 +1228,10 @@ async function main() {
|
|
|
1228
1228
|
|
|
1229
1229
|
if (!quiet) {
|
|
1230
1230
|
console.log('\nServices:');
|
|
1231
|
-
let dashboardUrl = null;
|
|
1232
1231
|
for (const svc of results) {
|
|
1233
1232
|
if (svc.status === 'started' || svc.status === 'reused') {
|
|
1234
1233
|
const status = svc.status === 'reused' ? ' (running)' : '';
|
|
1235
1234
|
console.log(` ${svc.name}${status}${svc.url ? ` ${svc.url}` : ''}`);
|
|
1236
|
-
if (svc.name === 'Dashboard' && svc.url) dashboardUrl = svc.url;
|
|
1237
1235
|
} else if (svc.status === 'failed') {
|
|
1238
1236
|
console.log(` ${svc.name} — failed${svc.note ? ` (${svc.note})` : ''}`);
|
|
1239
1237
|
}
|
|
@@ -1241,7 +1239,7 @@ async function main() {
|
|
|
1241
1239
|
|
|
1242
1240
|
const telemetrySvc = results.find(r => r.name === 'Telemetry' && r.url?.includes('localhost'));
|
|
1243
1241
|
if (telemetrySvc) console.log(`\nTelemetry: ${telemetrySvc.url}`);
|
|
1244
|
-
|
|
1242
|
+
console.log('\nStop services with `construct stop`');
|
|
1245
1243
|
}
|
|
1246
1244
|
} catch (error) {
|
|
1247
1245
|
console.error(`Services could not be started: ${error.message}`);
|
package/lib/intake/classify.mjs
CHANGED
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import path from 'node:path';
|
|
37
|
+
import { createRequire } from 'node:module';
|
|
38
|
+
import { fileURLToPath } from 'node:url';
|
|
39
|
+
|
|
40
|
+
const require = createRequire(import.meta.url);
|
|
41
|
+
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
37
42
|
|
|
38
43
|
// Backward-compatible exports. RND defaults preserve the symbol surface
|
|
39
44
|
// referenced by intake-config, prepare, and existing tests.
|
|
@@ -72,6 +77,22 @@ const TABLES = {
|
|
|
72
77
|
research: researchTable,
|
|
73
78
|
};
|
|
74
79
|
|
|
80
|
+
const dynamicTableCache = new Map();
|
|
81
|
+
|
|
82
|
+
function loadClassificationTable(tableRef) {
|
|
83
|
+
if (!tableRef || typeof tableRef !== 'string') return null;
|
|
84
|
+
if (dynamicTableCache.has(tableRef)) return dynamicTableCache.get(tableRef);
|
|
85
|
+
const abs = path.isAbsolute(tableRef) ? tableRef : path.join(REPO_ROOT, tableRef);
|
|
86
|
+
try {
|
|
87
|
+
const mod = require(abs);
|
|
88
|
+
const table = mod.default ?? mod;
|
|
89
|
+
dynamicTableCache.set(tableRef, table);
|
|
90
|
+
return table;
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
75
96
|
// Filename patterns are explicit author-intent signals. The boost remains
|
|
76
97
|
// at +0.4 so a clear keyword majority can still override (a postmortem
|
|
77
98
|
// genuinely about a CVE escalation routes to security, not incident).
|
|
@@ -167,6 +188,11 @@ function matcherFor(entry) {
|
|
|
167
188
|
function resolveTable(profile) {
|
|
168
189
|
if (!profile) return rndTable;
|
|
169
190
|
const id = typeof profile === 'string' ? profile : profile.id;
|
|
191
|
+
const tableRef = typeof profile === 'object' ? profile?.intake?.classificationTable : null;
|
|
192
|
+
if (typeof tableRef === 'string') {
|
|
193
|
+
const fromPath = loadClassificationTable(tableRef);
|
|
194
|
+
if (fromPath) return fromPath;
|
|
195
|
+
}
|
|
170
196
|
return TABLES[id] || rndTable;
|
|
171
197
|
}
|
|
172
198
|
|
package/lib/intake/daemon.mjs
CHANGED
|
@@ -19,14 +19,13 @@ import { join, basename } from 'node:path';
|
|
|
19
19
|
|
|
20
20
|
import { createDaemon, classifyPacket } from '../daemons/contract.mjs';
|
|
21
21
|
import { memoryCapMbFor } from '../resources/process-budget.mjs';
|
|
22
|
-
import { stateDir } from '../config/xdg.mjs';
|
|
23
22
|
|
|
24
23
|
const KILLSWITCH_ENV = 'CONSTRUCT_INTAKE_DAEMON';
|
|
25
24
|
|
|
26
25
|
function inboxDir(cwd) { return join(cwd, 'inbox'); }
|
|
27
26
|
function pendingDir(cwd) { return join(cwd, '.cx', 'intake', 'pending'); }
|
|
28
27
|
function deadLetterDir(cwd) { return join(cwd, '.cx', 'intake', 'dead-letter'); }
|
|
29
|
-
function heartbeatPath() { return join(
|
|
28
|
+
function heartbeatPath(cwd) { return join(cwd, '.cx', 'runtime', 'intake-daemon.heartbeat'); }
|
|
30
29
|
|
|
31
30
|
function ensureDir(dir) {
|
|
32
31
|
try { mkdirSync(dir, { recursive: true }); } catch { /* ignore */ }
|
|
@@ -128,7 +127,7 @@ export function buildIntakeDaemon({ cwd = process.cwd(), intervalMs = 60_000, cl
|
|
|
128
127
|
name: 'intake',
|
|
129
128
|
intervalMs,
|
|
130
129
|
killswitchEnv: KILLSWITCH_ENV,
|
|
131
|
-
heartbeatPath: heartbeatPath(),
|
|
130
|
+
heartbeatPath: heartbeatPath(cwd),
|
|
132
131
|
maxRuntimeMs: 24 * 60 * 60 * 1000,
|
|
133
132
|
maxIdleTicks: 6,
|
|
134
133
|
memoryCapMb: memoryCapMbFor('intake', cwd),
|
package/lib/intake/prepare.mjs
CHANGED
|
@@ -34,7 +34,8 @@ import { suggestDocsLaneForFile } from '../docs-routing.mjs';
|
|
|
34
34
|
import { createIntakeQueue } from './queue.mjs';
|
|
35
35
|
import { classifyRdIntake, suggestTags } from './classify.mjs';
|
|
36
36
|
import { detectCustomerMentions, linkSignalToCustomer, updateCustomerProfile } from '../embed/customer-profiles.mjs';
|
|
37
|
-
import {
|
|
37
|
+
import { resolveActiveScope } from '../scopes/loader.mjs';
|
|
38
|
+
import { resolveResearchProfile } from '../scopes/research-profile.mjs';
|
|
38
39
|
import { emitBestEffort as emitRoleEvent } from '../roles/event-bus.mjs';
|
|
39
40
|
import { gatherAttribution, stampAttribution } from './attribution.mjs';
|
|
40
41
|
import { MANIFEST_REL_PATH } from './manifest.mjs';
|
|
@@ -91,13 +92,19 @@ export async function prepareIntakeForIngestedFile({
|
|
|
91
92
|
// Resolve the active profile so non-RND projects route to their own table.
|
|
92
93
|
// Falls back to rnd for any project without explicit configuration, which
|
|
93
94
|
// preserves prior behavior for every existing user.
|
|
94
|
-
const activeProfile =
|
|
95
|
-
const
|
|
95
|
+
const activeProfile = resolveActiveScope(rootDir);
|
|
96
|
+
const classified = classifyFn({
|
|
96
97
|
sourcePath: ingestedFile.sourcePath,
|
|
97
98
|
extractedText: extracted,
|
|
98
99
|
related,
|
|
99
100
|
profile: activeProfile?.id,
|
|
100
101
|
});
|
|
102
|
+
const researchProfile = resolveResearchProfile({
|
|
103
|
+
intakeType: classified?.intakeType,
|
|
104
|
+
artifactType: lane,
|
|
105
|
+
scope: activeProfile,
|
|
106
|
+
});
|
|
107
|
+
const triage = researchProfile ? { ...classified, researchProfile } : classified;
|
|
101
108
|
|
|
102
109
|
// Detect customer mentions and update profiles
|
|
103
110
|
const customerMentions = detectCustomerMentions(extracted.slice(0, 2000));
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { createIntakeQueue } from './queue.mjs';
|
|
14
14
|
import { formatTriageLine } from './classify.mjs';
|
|
15
15
|
import { isBrokered } from '../mcp/broker.mjs';
|
|
16
|
-
import { getRebrand } from '../
|
|
16
|
+
import { getRebrand } from '../scopes/rebrand.mjs';
|
|
17
17
|
import { homedir as osHomedir } from 'node:os';
|
|
18
18
|
import { readLastTick } from '../oracle/index.mjs';
|
|
19
19
|
import { listPending } from '../oracle/actions.mjs';
|
|
@@ -11,8 +11,8 @@ export const STAGES = ['brief', 'research', 'draft', 'review', 'publish', 'measu
|
|
|
11
11
|
export const UNKNOWN_TRIAGE = {
|
|
12
12
|
intakeType: 'unknown',
|
|
13
13
|
rdStage: 'unknown',
|
|
14
|
-
primaryOwner: '
|
|
15
|
-
recommendedChain: ['
|
|
14
|
+
primaryOwner: 'operations',
|
|
15
|
+
recommendedChain: ['operations'],
|
|
16
16
|
recommendedAction: 'summarize',
|
|
17
17
|
risk: 'low',
|
|
18
18
|
requiresApproval: false,
|
|
@@ -33,8 +33,8 @@ export const CLASSIFICATION_TABLE = [
|
|
|
33
33
|
intakeType: 'brief',
|
|
34
34
|
keywords: ['brief', 'campaign', 'launch plan', 'go-to-market', 'gtm', 'positioning', 'narrative', 'audience'],
|
|
35
35
|
rdStage: 'brief',
|
|
36
|
-
primaryOwner: 'product-
|
|
37
|
-
recommendedChain: ['product-
|
|
36
|
+
primaryOwner: 'product-manager',
|
|
37
|
+
recommendedChain: ['product-manager', 'researcher', 'docs-keeper'],
|
|
38
38
|
recommendedAction: 'draft-prd',
|
|
39
39
|
risk: 'medium',
|
|
40
40
|
requiresApproval: false,
|
|
@@ -43,8 +43,8 @@ export const CLASSIFICATION_TABLE = [
|
|
|
43
43
|
intakeType: 'content-request',
|
|
44
44
|
keywords: ['blog', 'article', 'landing page', 'social post', 'newsletter', 'email copy', 'video script', 'headline', 'post', 'content'],
|
|
45
45
|
rdStage: 'draft',
|
|
46
|
-
primaryOwner: '
|
|
47
|
-
recommendedChain: ['
|
|
46
|
+
primaryOwner: 'docs-keeper',
|
|
47
|
+
recommendedChain: ['docs-keeper', 'reviewer'],
|
|
48
48
|
recommendedAction: 'draft-rfc',
|
|
49
49
|
risk: 'low',
|
|
50
50
|
requiresApproval: false,
|
|
@@ -74,7 +74,7 @@ export const CLASSIFICATION_TABLE = [
|
|
|
74
74
|
keywords: ['report', 'dashboard', 'attribution', 'ctr', 'cpa', 'roas', 'conversion', 'funnel', 'engagement'],
|
|
75
75
|
rdStage: 'measure',
|
|
76
76
|
primaryOwner: 'data-analyst',
|
|
77
|
-
recommendedChain: ['data-analyst', 'product-
|
|
77
|
+
recommendedChain: ['data-analyst', 'product-manager'],
|
|
78
78
|
recommendedAction: 'evaluate',
|
|
79
79
|
risk: 'low',
|
|
80
80
|
requiresApproval: false,
|
|
@@ -83,8 +83,8 @@ export const CLASSIFICATION_TABLE = [
|
|
|
83
83
|
intakeType: 'ops',
|
|
84
84
|
keywords: ['process', 'workflow', 'template', 'sla', 'calendar', 'planning', 'editorial calendar'],
|
|
85
85
|
rdStage: 'review',
|
|
86
|
-
primaryOwner: '
|
|
87
|
-
recommendedChain: ['
|
|
86
|
+
primaryOwner: 'operations',
|
|
87
|
+
recommendedChain: ['operations', 'product-manager'],
|
|
88
88
|
recommendedAction: 'create-runbook',
|
|
89
89
|
risk: 'low',
|
|
90
90
|
requiresApproval: false,
|
|
@@ -12,8 +12,8 @@ export const STAGES = ['request', 'triage', 'resolve', 'document', 'improve'];
|
|
|
12
12
|
export const UNKNOWN_TRIAGE = {
|
|
13
13
|
intakeType: 'unknown',
|
|
14
14
|
rdStage: 'unknown',
|
|
15
|
-
primaryOwner: '
|
|
16
|
-
recommendedChain: ['
|
|
15
|
+
primaryOwner: 'operations',
|
|
16
|
+
recommendedChain: ['operations'],
|
|
17
17
|
recommendedAction: 'summarize',
|
|
18
18
|
risk: 'low',
|
|
19
19
|
requiresApproval: false,
|
|
@@ -74,8 +74,8 @@ export const CLASSIFICATION_TABLE = [
|
|
|
74
74
|
intakeType: 'request',
|
|
75
75
|
keywords: ['request', 'access', 'feature request', 'integration', 'new tool', 'change request', 'how do i', 'can someone'],
|
|
76
76
|
rdStage: 'triage',
|
|
77
|
-
primaryOwner: '
|
|
78
|
-
recommendedChain: ['
|
|
77
|
+
primaryOwner: 'operations',
|
|
78
|
+
recommendedChain: ['operations', 'engineer'],
|
|
79
79
|
recommendedAction: 'clarify',
|
|
80
80
|
risk: 'low',
|
|
81
81
|
requiresApproval: false,
|
|
@@ -74,8 +74,8 @@ export const CLASSIFICATION_TABLE = [
|
|
|
74
74
|
intakeType: 'ops',
|
|
75
75
|
keywords: ['process', 'methodology', 'template', 'protocol', 'consent form', 'data handling'],
|
|
76
76
|
rdStage: 'gather',
|
|
77
|
-
primaryOwner: '
|
|
78
|
-
recommendedChain: ['
|
|
77
|
+
primaryOwner: 'operations',
|
|
78
|
+
recommendedChain: ['operations', 'researcher'],
|
|
79
79
|
recommendedAction: 'create-runbook',
|
|
80
80
|
risk: 'low',
|
|
81
81
|
requiresApproval: false,
|