@geraldmaron/construct 1.2.3 → 1.3.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/README.md +7 -10
- package/bin/construct +235 -209
- 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 +55 -54
- package/lib/comment-lint.mjs +1 -5
- package/lib/completions.mjs +20 -2
- package/lib/config/legacy-config-migration.mjs +90 -24
- 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/cli.mjs +12 -2
- package/lib/doctor/source-checkout.mjs +3 -4
- package/lib/doctor/watchers/consistency.mjs +148 -43
- 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/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 +6 -26
- 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 -8
- 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 +269 -51
- 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/apps/chat/engine/ai-sdk-agent.mjs +0 -183
- package/apps/chat/engine/loop-driver.mjs +0 -211
- package/apps/chat/engine/models.mjs +0 -122
- package/apps/chat/engine/provider-adapters.mjs +0 -171
- package/apps/chat/engine/tools/permission.mjs +0 -54
- package/apps/chat/engine/tools/primitives.mjs +0 -180
- package/apps/chat/engine/tools/registry.mjs +0 -122
- package/apps/chat/engine/turn-controls.mjs +0 -70
- 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
|
@@ -24,8 +24,9 @@ import { cxDir } from '../paths.mjs';
|
|
|
24
24
|
const DEFAULT_SUPPRESS_DAYS = 7;
|
|
25
25
|
const SUPERSEDE_WITHIN_HOURS = 72;
|
|
26
26
|
|
|
27
|
-
function recommendationPaths() {
|
|
28
|
-
const
|
|
27
|
+
function recommendationPaths({ rootDir, project } = {}) {
|
|
28
|
+
const baseDir = rootDir || project;
|
|
29
|
+
const storeDir = baseDir ? join(baseDir, '.cx', 'intake') : join(cxDir(), 'intake');
|
|
29
30
|
return {
|
|
30
31
|
storeDir,
|
|
31
32
|
logFile: join(storeDir, 'recommendations.jsonl'),
|
|
@@ -36,16 +37,16 @@ function recommendationPaths() {
|
|
|
36
37
|
/**
|
|
37
38
|
* Ensure store directory exists.
|
|
38
39
|
*/
|
|
39
|
-
function ensureDir() {
|
|
40
|
-
const { storeDir } = recommendationPaths();
|
|
40
|
+
function ensureDir(opts = {}) {
|
|
41
|
+
const { storeDir } = recommendationPaths(opts);
|
|
41
42
|
if (!existsSync(storeDir)) mkdirSync(storeDir, { recursive: true });
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Read the index (fast lookup).
|
|
46
47
|
*/
|
|
47
|
-
function readIndex() {
|
|
48
|
-
const { indexFile } = recommendationPaths();
|
|
48
|
+
function readIndex(opts = {}) {
|
|
49
|
+
const { indexFile } = recommendationPaths(opts);
|
|
49
50
|
if (!existsSync(indexFile)) return {};
|
|
50
51
|
try {
|
|
51
52
|
return JSON.parse(readFileSync(indexFile, 'utf8'));
|
|
@@ -57,9 +58,9 @@ function readIndex() {
|
|
|
57
58
|
/**
|
|
58
59
|
* Write the index.
|
|
59
60
|
*/
|
|
60
|
-
function writeIndex(index) {
|
|
61
|
-
const { indexFile } = recommendationPaths();
|
|
62
|
-
ensureDir();
|
|
61
|
+
function writeIndex(index, opts = {}) {
|
|
62
|
+
const { indexFile } = recommendationPaths(opts);
|
|
63
|
+
ensureDir(opts);
|
|
63
64
|
writeFileSync(indexFile, JSON.stringify(index, null, 2) + '\n');
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -86,9 +87,10 @@ function dedupKey(type, title) {
|
|
|
86
87
|
export function createRecommendation({ type, title, reason, signalCount = 1, customerImpact = 0, recencyBonus = 0, strategicBonus = 0, sourceSignalIds = [], lane, project, env }) {
|
|
87
88
|
if (!type || !title) throw new Error('type and title are required');
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
const pathOpts = { rootDir: project };
|
|
91
|
+
ensureDir(pathOpts);
|
|
90
92
|
const key = dedupKey(type, title);
|
|
91
|
-
const index = readIndex();
|
|
93
|
+
const index = readIndex(pathOpts);
|
|
92
94
|
|
|
93
95
|
if (index[key]) {
|
|
94
96
|
const existing = index[key];
|
|
@@ -106,7 +108,7 @@ export function createRecommendation({ type, title, reason, signalCount = 1, cus
|
|
|
106
108
|
updated.score = score;
|
|
107
109
|
updated.priority = priorityTier(score);
|
|
108
110
|
index[key] = updated;
|
|
109
|
-
writeIndex(index);
|
|
111
|
+
writeIndex(index, pathOpts);
|
|
110
112
|
|
|
111
113
|
return { id: existing.id, dedupKey: key, priority: updated.priority, score, existing: true, updatedAt: updated.lastSeen };
|
|
112
114
|
}
|
|
@@ -139,10 +141,10 @@ export function createRecommendation({ type, title, reason, signalCount = 1, cus
|
|
|
139
141
|
enrichedBy: null,
|
|
140
142
|
};
|
|
141
143
|
|
|
142
|
-
appendFileSync(recommendationPaths().logFile, JSON.stringify(rec) + '\n', 'utf8');
|
|
144
|
+
appendFileSync(recommendationPaths(pathOpts).logFile, JSON.stringify(rec) + '\n', 'utf8');
|
|
143
145
|
|
|
144
146
|
index[key] = rec;
|
|
145
|
-
writeIndex(index);
|
|
147
|
+
writeIndex(index, pathOpts);
|
|
146
148
|
|
|
147
149
|
emitBestEffort('recommendation.generated', {
|
|
148
150
|
project: project || '',
|
|
@@ -202,7 +204,7 @@ export function supersedeRecommendation(dedupKey, supersedingId) {
|
|
|
202
204
|
* List active recommendations (filesystem-only).
|
|
203
205
|
*/
|
|
204
206
|
export function listActiveRecommendations({ type, priority, limit = 20, project, env } = {}) {
|
|
205
|
-
const index = readIndex();
|
|
207
|
+
const index = readIndex({ rootDir: project });
|
|
206
208
|
const now = new Date();
|
|
207
209
|
let results = Object.values(index).filter(rec => {
|
|
208
210
|
if (rec.dismissedAt) {
|
|
@@ -222,8 +224,9 @@ export function listActiveRecommendations({ type, priority, limit = 20, project,
|
|
|
222
224
|
.slice(0, limit);
|
|
223
225
|
}
|
|
224
226
|
|
|
225
|
-
export function autoSuppressStale() {
|
|
226
|
-
const
|
|
227
|
+
export function autoSuppressStale({ project } = {}) {
|
|
228
|
+
const pathOpts = { rootDir: project };
|
|
229
|
+
const index = readIndex(pathOpts);
|
|
227
230
|
const now = Date.now();
|
|
228
231
|
let suppressed = 0;
|
|
229
232
|
|
|
@@ -255,7 +258,7 @@ export function autoSuppressStale() {
|
|
|
255
258
|
}
|
|
256
259
|
}
|
|
257
260
|
|
|
258
|
-
if (suppressed) writeIndex(index);
|
|
261
|
+
if (suppressed) writeIndex(index, pathOpts);
|
|
259
262
|
return suppressed;
|
|
260
263
|
}
|
|
261
264
|
|
|
@@ -291,8 +294,8 @@ export function reviveRecommendation(dedupKey, { signalCount = 1, sourceSignalId
|
|
|
291
294
|
return { success: true };
|
|
292
295
|
}
|
|
293
296
|
|
|
294
|
-
export function recommendationStats() {
|
|
295
|
-
const index = readIndex();
|
|
297
|
+
export function recommendationStats({ project } = {}) {
|
|
298
|
+
const index = readIndex({ rootDir: project });
|
|
296
299
|
const entries = Object.values(index);
|
|
297
300
|
const byPriority = {};
|
|
298
301
|
let active = 0, dismissed = 0, superseded = 0;
|
|
@@ -314,9 +317,9 @@ export function recommendationStats() {
|
|
|
314
317
|
};
|
|
315
318
|
}
|
|
316
319
|
|
|
317
|
-
export function isRecommendationActive(type, title) {
|
|
320
|
+
export function isRecommendationActive(type, title, { project } = {}) {
|
|
318
321
|
const key = dedupKey(type, title);
|
|
319
|
-
const index = readIndex();
|
|
322
|
+
const index = readIndex({ rootDir: project });
|
|
320
323
|
const rec = index[key];
|
|
321
324
|
if (!rec) return { active: false, existing: null };
|
|
322
325
|
if (rec.dismissedAt || rec.supersededAt) return { active: false, existing: rec };
|
|
@@ -7,22 +7,17 @@
|
|
|
7
7
|
* 3. Renders a "Role Lens" section for inclusion in snapshots/roadmaps
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
import { join, dirname } from 'node:path';
|
|
12
|
-
import { fileURLToPath } from 'node:url';
|
|
13
|
-
|
|
14
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const REGISTRY_PATH = join(__dirname, '..', '..', 'specialists', 'registry.json');
|
|
10
|
+
import { loadRegistry as loadAssembledRegistry } from '../registry/loader.mjs';
|
|
16
11
|
|
|
17
12
|
let _registryCache = null;
|
|
18
13
|
|
|
19
14
|
function loadRegistry() {
|
|
20
15
|
if (_registryCache) return _registryCache;
|
|
21
16
|
try {
|
|
22
|
-
_registryCache =
|
|
17
|
+
_registryCache = loadAssembledRegistry();
|
|
23
18
|
} catch (err) {
|
|
24
19
|
process.stderr.write('[role-framing.mjs] loadRegistry: ' + (err?.message ?? String(err)) + '\n');
|
|
25
|
-
_registryCache = { specialists:
|
|
20
|
+
_registryCache = { specialists: {} };
|
|
26
21
|
}
|
|
27
22
|
return _registryCache;
|
|
28
23
|
}
|
|
@@ -35,7 +30,7 @@ function loadRegistry() {
|
|
|
35
30
|
export function getOrientation(roleName) {
|
|
36
31
|
if (!roleName) return null;
|
|
37
32
|
const registry = loadRegistry();
|
|
38
|
-
const agent = (registry.specialists ??
|
|
33
|
+
const agent = Object.values(registry.specialists ?? {}).find((a) => a.name === roleName);
|
|
39
34
|
return agent?.embedOrientation ?? null;
|
|
40
35
|
}
|
|
41
36
|
|
|
@@ -23,6 +23,7 @@ import { resolveModelTiers } from '../model-registry.mjs';
|
|
|
23
23
|
import { readSkillFrontmatter } from '../sync/skill-frontmatter.mjs';
|
|
24
24
|
import { loadPluginRegistry } from '../plugin-registry.mjs';
|
|
25
25
|
import { listWorkflowDefs } from './workflow-defs.mjs';
|
|
26
|
+
import { loadRegistry } from '../registry/loader.mjs';
|
|
26
27
|
import { APPROVAL_MODES } from './audit.mjs';
|
|
27
28
|
|
|
28
29
|
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
@@ -111,8 +112,9 @@ function buildModels(env) {
|
|
|
111
112
|
|
|
112
113
|
function buildPolicies(rootDir, warnings) {
|
|
113
114
|
try {
|
|
114
|
-
const
|
|
115
|
-
const
|
|
115
|
+
const registry = loadRegistry({ rootDir, skipValidation: true });
|
|
116
|
+
const policies = registry.policies || {};
|
|
117
|
+
const arr = Array.isArray(policies) ? policies : Object.values(policies);
|
|
116
118
|
return arr.map((p) => ({ id: p.id, enforcement: p.enforcement, mode: p.mode, description: p.description }));
|
|
117
119
|
} catch (err) {
|
|
118
120
|
warnings.push(`Policy inventory unavailable: ${err.message}`);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* CONTRACT_VERSION so an embedding application can negotiate compatibility
|
|
9
9
|
* without reading internal registries.
|
|
10
10
|
*
|
|
11
|
-
* Distinct from specialists/
|
|
11
|
+
* Distinct from specialists/org `version` (handoff contracts) and
|
|
12
12
|
* lib/plugin-registry.mjs `MANIFEST_VERSION` (plugin manifests); those version
|
|
13
13
|
* unrelated internal surfaces.
|
|
14
14
|
*/
|
package/lib/env-config.mjs
CHANGED
|
@@ -88,7 +88,7 @@ const ENV_SHADOW_IGNORE = new Set([
|
|
|
88
88
|
*/
|
|
89
89
|
const ENV_SHADOW_WARN = new Set([
|
|
90
90
|
'OPENROUTER_API_KEY', 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY',
|
|
91
|
-
'
|
|
91
|
+
'MEMORY_PORT', 'BRIDGE_PORT',
|
|
92
92
|
'CONSTRUCT_INSTANCE_ID',
|
|
93
93
|
'CONSTRUCT_DEPLOYMENT_MODE',
|
|
94
94
|
]);
|
|
@@ -155,7 +155,7 @@ export function loadConstructEnv({ rootDir, homeDir = os.homedir(), env = proces
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// process.env is the base; .env-file values override shell values so that
|
|
158
|
-
// credentials set in
|
|
158
|
+
// credentials set in config.env always win over stale shell exports.
|
|
159
159
|
// Materialized credentials from `op run` (or an explicit export) win over stored
|
|
160
160
|
// op:// references so one 1Password auth per invocation is enough.
|
|
161
161
|
const merged = { ...env, ...fileEnv };
|
package/lib/flavors/loader.mjs
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* lib/flavors/loader.mjs — Role flavor overlay loader and validator.
|
|
3
3
|
*
|
|
4
4
|
* Reads frontmatter from skills/roles/<role>.<flavor>.md files, exposes:
|
|
5
|
-
* - listFlavors(role?,
|
|
5
|
+
* - listFlavors(role?, scope?) — overlays that apply to role and scope
|
|
6
6
|
* - validateFlavor(path) — frontmatter conformance check against the schema
|
|
7
|
-
* - perRoleFlavorCount(
|
|
7
|
+
* - perRoleFlavorCount(scope) — flavor counts per role for cap enforcement
|
|
8
8
|
*
|
|
9
|
-
* The cap is enforced at sync time and in lint-
|
|
10
|
-
* Six flavors per role per
|
|
9
|
+
* The cap is enforced at sync time and in lint-scopes.
|
|
10
|
+
* Six flavors per role per scope is the current ceiling; bumping requires
|
|
11
11
|
* an ADR. Existing roles like qa already have 6, so the cap is set tight.
|
|
12
12
|
*/
|
|
13
13
|
import fs from 'node:fs';
|
|
@@ -18,7 +18,8 @@ const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
|
18
18
|
const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
19
19
|
const FLAVORS_DIR = path.join(REPO_ROOT, 'skills', 'roles');
|
|
20
20
|
|
|
21
|
-
export const
|
|
21
|
+
export const FLAVOR_CAP_PER_ROLE_PER_SCOPE = 6;
|
|
22
|
+
export const FLAVOR_CAP_PER_ROLE_PER_PROFILE = FLAVOR_CAP_PER_ROLE_PER_SCOPE;
|
|
22
23
|
|
|
23
24
|
// Files may start with an HTML comment block before the YAML frontmatter.
|
|
24
25
|
// The /m flag lets ^ match line-start so we find the YAML wherever it sits.
|
|
@@ -79,30 +80,26 @@ export function listAllFlavors() {
|
|
|
79
80
|
* Return overlays that match the given role (any-flavor for that role) and
|
|
80
81
|
* apply to the given profile. Pass undefined for either to skip that filter.
|
|
81
82
|
*/
|
|
82
|
-
export function listFlavors({ role, profile } = {}) {
|
|
83
|
+
export function listFlavors({ role, scope, profile } = {}) {
|
|
84
|
+
const scopeId = scope ?? profile;
|
|
83
85
|
return listAllFlavors().filter(({ frontmatter }) => {
|
|
84
86
|
if (role) {
|
|
85
87
|
const roleId = frontmatter.role || '';
|
|
86
88
|
const baseRole = roleId.split('.')[0];
|
|
87
89
|
if (baseRole !== role && roleId !== role) return false;
|
|
88
90
|
}
|
|
89
|
-
if (
|
|
90
|
-
const
|
|
91
|
-
if (!Array.isArray(
|
|
92
|
-
if (!
|
|
91
|
+
if (scopeId) {
|
|
92
|
+
const scopes = frontmatter.scopes ?? frontmatter.profiles;
|
|
93
|
+
if (!Array.isArray(scopes) || scopes.length === 0) return true;
|
|
94
|
+
if (!scopes.includes(scopeId)) return false;
|
|
93
95
|
}
|
|
94
96
|
return true;
|
|
95
97
|
});
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
* Count flavor overlays per base role for a given profile.
|
|
100
|
-
* Returns { [baseRole]: count }. Excludes the canonical base file (e.g.
|
|
101
|
-
* architect.md) so only true flavors count toward the cap.
|
|
102
|
-
*/
|
|
103
|
-
export function perRoleFlavorCount(profile = 'rnd') {
|
|
100
|
+
export function perRoleFlavorCount(scope = 'rnd') {
|
|
104
101
|
const counts = {};
|
|
105
|
-
for (const entry of listFlavors({
|
|
102
|
+
for (const entry of listFlavors({ scope })) {
|
|
106
103
|
const roleId = entry.frontmatter.role || '';
|
|
107
104
|
const isFlavor = roleId.includes('.');
|
|
108
105
|
if (!isFlavor) continue;
|
|
@@ -129,8 +126,13 @@ export function validateFlavor(filePath) {
|
|
|
129
126
|
if (!Array.isArray(fm.applies_to) || fm.applies_to.length === 0) {
|
|
130
127
|
errors.push(`${filePath}: applies_to must be a non-empty array`);
|
|
131
128
|
}
|
|
132
|
-
if (!Array.isArray(fm.
|
|
133
|
-
errors.push(`${filePath}:
|
|
129
|
+
if (!Array.isArray(fm.scopes) && !Array.isArray(fm.profiles)) {
|
|
130
|
+
errors.push(`${filePath}: scopes must be a non-empty array; add \`scopes: [rnd]\``);
|
|
131
|
+
} else {
|
|
132
|
+
const scopes = fm.scopes ?? fm.profiles;
|
|
133
|
+
if (!Array.isArray(scopes) || scopes.length === 0) {
|
|
134
|
+
errors.push(`${filePath}: scopes must be a non-empty array; add \`scopes: [rnd]\``);
|
|
135
|
+
}
|
|
134
136
|
}
|
|
135
137
|
return errors;
|
|
136
138
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* authoritative declarative catalogs.
|
|
4
4
|
*
|
|
5
5
|
* Ingests registry/capabilities.json, lib/embedded-contract/workflow-defs.mjs,
|
|
6
|
-
* and specialists/
|
|
6
|
+
* and specialists/org into typed nodes and directed edges:
|
|
7
7
|
* capability --embeds--> workflow (embeddedWorkflow)
|
|
8
8
|
* test --validates--> capability (verification.functional / hostEmulation / per-surface test)
|
|
9
9
|
* capability --uses--> skill | rule (skill + dependencies.skills/.rules)
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
19
|
+
import { loadRegistry } from '../registry/loader.mjs';
|
|
19
20
|
import path from 'node:path';
|
|
20
21
|
import { createHash } from 'node:crypto';
|
|
21
22
|
|
|
@@ -31,6 +32,23 @@ function existsRel(rootDir, rel) {
|
|
|
31
32
|
return !!rel && existsSync(path.join(rootDir, rel));
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function declarationImplFiles(cap, rootDir) {
|
|
36
|
+
const files = [];
|
|
37
|
+
if (cap.kind === 'workflow') {
|
|
38
|
+
files.push('lib/embedded-contract/workflow-invoke.mjs', 'lib/embedded-contract/workflow-defs.mjs');
|
|
39
|
+
}
|
|
40
|
+
if (cap.kind === 'document-type') {
|
|
41
|
+
files.push('lib/artifact-release-gate.mjs', 'lib/artifact-manifest.mjs');
|
|
42
|
+
}
|
|
43
|
+
if (cap.id === 'ingest.docling') {
|
|
44
|
+
files.push('lib/document-extract/docling-client.mjs', 'lib/ingest/docling-remote.mjs');
|
|
45
|
+
}
|
|
46
|
+
if (cap.id === 'mcp.tool-budget.trim') {
|
|
47
|
+
files.push('lib/mcp/tool-budget.mjs');
|
|
48
|
+
}
|
|
49
|
+
return [...new Set(files.filter((rel) => existsRel(rootDir, rel)))];
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
function hashFiles(rootDir, rels) {
|
|
35
53
|
const h = createHash('sha256');
|
|
36
54
|
for (const rel of rels) {
|
|
@@ -66,11 +84,11 @@ export function buildFromRegistry({ rootDir }) {
|
|
|
66
84
|
});
|
|
67
85
|
}
|
|
68
86
|
|
|
69
|
-
const contractsDoc =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
const contractsDoc = loadRegistry({ rootDir });
|
|
88
|
+
for (const { id, producer, consumer } of Object.values(contractsDoc?.contracts ?? {})) {
|
|
89
|
+
if (!id) continue;
|
|
90
|
+
node('contract', id, id, { producer: producer ?? null, consumer: consumer ?? null });
|
|
91
|
+
}
|
|
74
92
|
|
|
75
93
|
const registry = loadCapabilityRegistry({ rootDir });
|
|
76
94
|
for (const cap of registry.capabilities ?? []) {
|
|
@@ -122,11 +140,17 @@ export function buildFromRegistry({ rootDir }) {
|
|
|
122
140
|
node('contract', cid, cid, {});
|
|
123
141
|
edge(capNode, 'governed_by', nodeId('contract', cid));
|
|
124
142
|
}
|
|
143
|
+
|
|
144
|
+
for (const implRel of declarationImplFiles(cap, rootDir)) {
|
|
145
|
+
const fileNode = nodeId('file', implRel);
|
|
146
|
+
node('file', implRel, implRel, { path: implRel, exists: true, source: 'declaration' });
|
|
147
|
+
edge(fileNode, 'realizes', capNode);
|
|
148
|
+
}
|
|
125
149
|
}
|
|
126
150
|
|
|
127
151
|
const sourceHash = hashFiles(rootDir, [
|
|
128
152
|
'registry/capabilities.json',
|
|
129
|
-
'specialists/
|
|
153
|
+
'specialists/org',
|
|
130
154
|
'lib/embedded-contract/workflow-defs.mjs',
|
|
131
155
|
]);
|
|
132
156
|
|
package/lib/graph/staleness.mjs
CHANGED
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 =
|