@geraldmaron/construct 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +10 -7
- package/LICENSE +201 -21
- package/README.md +132 -257
- package/agents/contracts.json +387 -0
- package/agents/prompts/cx-accessibility.md +8 -0
- package/agents/prompts/cx-ai-engineer.md +92 -0
- package/agents/prompts/cx-architect.md +10 -2
- package/agents/prompts/cx-business-strategist.md +13 -0
- package/agents/prompts/cx-data-analyst.md +80 -0
- package/agents/prompts/cx-data-engineer.md +4 -0
- package/agents/prompts/cx-debugger.md +8 -0
- package/agents/prompts/cx-designer.md +19 -0
- package/agents/prompts/cx-devil-advocate.md +4 -0
- package/agents/prompts/cx-docs-keeper.md +128 -0
- package/agents/prompts/cx-engineer.md +13 -0
- package/agents/prompts/cx-evaluator.md +4 -0
- package/agents/prompts/cx-explorer.md +12 -0
- package/agents/prompts/cx-legal-compliance.md +13 -0
- package/agents/prompts/cx-operations.md +13 -0
- package/agents/prompts/cx-orchestrator.md +107 -4
- package/agents/prompts/cx-platform-engineer.md +75 -0
- package/agents/prompts/cx-product-manager.md +8 -0
- package/agents/prompts/cx-qa.md +100 -0
- package/agents/prompts/cx-rd-lead.md +13 -0
- package/agents/prompts/cx-release-manager.md +8 -0
- package/agents/prompts/cx-researcher.md +21 -1
- package/agents/prompts/cx-reviewer.md +8 -0
- package/agents/prompts/cx-security.md +104 -0
- package/agents/prompts/cx-sre.md +104 -0
- package/agents/prompts/cx-test-automation.md +4 -0
- package/agents/prompts/cx-trace-reviewer.md +7 -3
- package/agents/prompts/cx-ux-researcher.md +4 -0
- package/agents/registry.json +365 -90
- package/agents/role-manifests.json +217 -0
- package/bin/construct +3345 -141
- package/bin/construct-postinstall.mjs +87 -0
- package/commands/build/feature.md +6 -6
- package/commands/plan/feature.md +6 -5
- package/commands/plan/requirements.md +1 -1
- package/commands/ship/status.md +1 -1
- package/commands/work/drive.md +3 -3
- package/commands/work/optimize-prompts.md +3 -3
- package/db/{migrations → schema}/001_init.sql +2 -0
- package/db/schema/002_pgvector.sql +182 -0
- package/db/schema/003_intake.sql +47 -0
- package/examples/README.md +85 -0
- package/examples/internal/roles/architect/bad/clever-plan-without-contracts.md +30 -0
- package/examples/internal/roles/architect/golden/explicit-tradeoff-before-plan.md +23 -0
- package/examples/internal/roles/engineer/bad/speculative-abstraction.md +30 -0
- package/examples/internal/roles/engineer/golden/read-before-write.md +28 -0
- package/examples/internal/roles/orchestrator/bad/everything-becomes-multi-agent.md +29 -0
- package/examples/internal/roles/orchestrator/golden/minimal-dispatch.md +22 -0
- package/examples/internal/roles/qa/bad/coverage-theater.md +30 -0
- package/examples/internal/roles/qa/golden/regression-gate.md +23 -0
- package/examples/internal/roles/reviewer/bad/lgtm-without-verification.md +29 -0
- package/examples/internal/roles/reviewer/golden/find-structural-risk-first.md +28 -0
- package/examples/personas/construct/adversarial/ignore-instruction-to-skip-approval.md +22 -0
- package/examples/personas/construct/bad/commit-without-approval.md +30 -0
- package/examples/personas/construct/boundary/blocked-needs-main-input.md +29 -0
- package/examples/personas/construct/golden/branch-approval-before-mutation.md +36 -0
- package/examples/personas/construct/golden/focused-direct-answer.md +28 -0
- package/examples/provider-plugin/README.md +34 -0
- package/examples/provider-plugin/index.mjs +74 -0
- package/examples/provider-plugin/package.json +15 -0
- package/examples/seed-observations/README.md +38 -0
- package/examples/seed-observations/anti-patterns.md +44 -0
- package/examples/seed-observations/decisions.md +36 -0
- package/examples/seed-observations/patterns.md +42 -0
- package/lib/agent-contracts-enforce.mjs +158 -0
- package/lib/agent-contracts.mjs +231 -0
- package/lib/agents/postconditions.mjs +126 -0
- package/lib/agents/schema.mjs +124 -0
- package/lib/artifact-capture.mjs +183 -0
- package/lib/audit-trail.mjs +149 -0
- package/lib/auto-docs.mjs +245 -65
- package/lib/beads/auto-close.mjs +126 -0
- package/lib/beads/drift.mjs +171 -0
- package/lib/beads-automation.mjs +542 -0
- package/lib/beads-client.mjs +518 -0
- package/lib/beads-lock.mjs +377 -0
- package/lib/beads-optimistic.mjs +365 -0
- package/lib/bootstrap/built-ins.mjs +136 -0
- package/lib/bootstrap/lazy-install.mjs +161 -0
- package/lib/bootstrap/resources.mjs +120 -0
- package/lib/bootstrap.mjs +105 -0
- package/lib/cache-governor.js +213 -0
- package/lib/cache-strategy-anthropic.js +62 -0
- package/lib/cache-strategy-google.js +79 -0
- package/lib/cache-strategy-none.js +30 -0
- package/lib/cache-strategy-openai.js +48 -0
- package/lib/cache-strategy.js +91 -0
- package/lib/claude-allow.mjs +149 -0
- package/lib/cli-commands.mjs +413 -258
- package/lib/codex-config.mjs +3 -1
- package/lib/comment-lint.mjs +55 -6
- package/lib/completions.mjs +21 -6
- package/lib/config/alias.mjs +56 -0
- package/lib/config/project-config.mjs +335 -0
- package/lib/config/schema.mjs +159 -0
- package/lib/context-router.mjs +308 -0
- package/lib/cost-ledger.mjs +177 -0
- package/lib/cost.mjs +171 -10
- package/lib/dashboard-static.mjs +158 -0
- package/lib/deployment-mode.mjs +86 -0
- package/lib/deprecate.mjs +49 -0
- package/lib/dispatch-batch.js +183 -0
- package/lib/distill.mjs +21 -8
- package/lib/doc-stamp.mjs +164 -0
- package/lib/doc-verify.mjs +119 -0
- package/lib/docs-routing.mjs +89 -0
- package/lib/docs-verify.mjs +417 -0
- package/lib/doctor/audit.mjs +71 -0
- package/lib/doctor/cli.mjs +99 -0
- package/lib/doctor/escalate.mjs +29 -0
- package/lib/doctor/index.mjs +140 -0
- package/lib/doctor/report.mjs +170 -0
- package/lib/doctor/watchers/bd-watch.mjs +117 -0
- package/lib/doctor/watchers/cost.mjs +130 -0
- package/lib/doctor/watchers/disk.mjs +122 -0
- package/lib/doctor/watchers/handoffs.mjs +33 -0
- package/lib/doctor/watchers/process-pressure.mjs +60 -0
- package/lib/doctor/watchers/service-health.mjs +188 -0
- package/lib/document-extract.mjs +288 -0
- package/lib/document-ingest.mjs +230 -0
- package/lib/drop.mjs +282 -0
- package/lib/embed/approval-queue.mjs +176 -0
- package/lib/embed/artifact.mjs +349 -0
- package/lib/embed/authority-guard.mjs +155 -0
- package/lib/embed/cli.mjs +408 -0
- package/lib/embed/config.mjs +355 -0
- package/lib/embed/conflict-detection.mjs +264 -0
- package/lib/embed/customer-profiles.mjs +480 -0
- package/lib/embed/daemon.mjs +1309 -0
- package/lib/embed/demand-fetch.mjs +449 -0
- package/lib/embed/docs-lifecycle.mjs +349 -0
- package/lib/embed/inbox-live-watcher.mjs +119 -0
- package/lib/embed/inbox.mjs +343 -0
- package/lib/embed/intake-metrics.mjs +190 -0
- package/lib/embed/jobs/vector-sync.mjs +198 -0
- package/lib/embed/notifications.mjs +75 -0
- package/lib/embed/output.mjs +79 -0
- package/lib/embed/providers/github.mjs +295 -0
- package/lib/embed/providers/jira.mjs +192 -0
- package/lib/embed/providers/linear.mjs +186 -0
- package/lib/embed/providers/registry.mjs +115 -0
- package/lib/embed/providers/slack.mjs +203 -0
- package/lib/embed/recommendation-store.mjs +378 -0
- package/lib/embed/roadmap.mjs +374 -0
- package/lib/embed/role-framing.mjs +110 -0
- package/lib/embed/scheduler.mjs +99 -0
- package/lib/embed/semantic.mjs +325 -0
- package/lib/embed/snapshot.mjs +191 -0
- package/lib/embed/supervision.mjs +235 -0
- package/lib/embed/target-resolver.mjs +186 -0
- package/lib/embed/worker.mjs +62 -0
- package/lib/embed/workspaces.mjs +297 -0
- package/lib/engine/chunker-headings.mjs +110 -0
- package/lib/engine/compressor-heuristic.mjs +100 -0
- package/lib/engine/consolidate.mjs +287 -0
- package/lib/engine/contracts.mjs +126 -0
- package/lib/engine/defaults.mjs +129 -0
- package/lib/engine/eval-retrieval.mjs +148 -0
- package/lib/engine/fuser-rrf.mjs +62 -0
- package/lib/engine/index.mjs +37 -0
- package/lib/engine/registry.mjs +146 -0
- package/lib/engine/reranker-mmr.mjs +90 -0
- package/lib/engine/tokens.mjs +77 -0
- package/lib/entity-store.mjs +280 -0
- package/lib/env-config.mjs +69 -4
- package/lib/evals/retrieval-bench.mjs +159 -0
- package/lib/evaluator-optimizer.mjs +317 -0
- package/lib/features.mjs +159 -29
- package/lib/gates-audit.mjs +236 -0
- package/lib/git-hooks/prepare-commit-msg +58 -0
- package/lib/handoffs/cleanup.mjs +159 -0
- package/lib/handoffs/contract.mjs +162 -0
- package/lib/handoffs/inventory.mjs +120 -0
- package/lib/headhunt.mjs +28 -47
- package/lib/health-check.mjs +399 -0
- package/lib/hook-health.mjs +442 -0
- package/lib/hooks/_lib/log.mjs +82 -0
- package/lib/hooks/adaptive-lint.mjs +26 -2
- package/lib/hooks/agent-tracker.mjs +171 -14
- package/lib/hooks/audit-reads.mjs +109 -0
- package/lib/hooks/audit-trail.mjs +153 -0
- package/lib/hooks/bash-output-logger.mjs +71 -0
- package/lib/hooks/block-no-verify.mjs +41 -0
- package/lib/hooks/ci-status-check.mjs +82 -0
- package/lib/hooks/comment-lint.mjs +29 -7
- package/lib/hooks/config-protection.mjs +39 -18
- package/lib/hooks/context-watch.mjs +137 -0
- package/lib/hooks/context-window-recovery.mjs +4 -20
- package/lib/hooks/dep-audit.mjs +16 -0
- package/lib/hooks/doc-coupling-check.mjs +80 -0
- package/lib/hooks/edit-accumulator.mjs +3 -0
- package/lib/hooks/edit-error-recovery.mjs +3 -0
- package/lib/hooks/edit-guard.mjs +45 -4
- package/lib/hooks/env-check.mjs +3 -0
- package/lib/hooks/guard-bash.mjs +58 -1
- package/lib/hooks/mcp-audit.mjs +18 -20
- package/lib/hooks/mcp-health-check.mjs +36 -0
- package/lib/hooks/model-fallback.mjs +42 -56
- package/lib/hooks/policy-engine.mjs +209 -0
- package/lib/hooks/post-merge-docs-check.mjs +63 -0
- package/lib/hooks/pre-compact.mjs +4 -24
- package/lib/hooks/pre-push-gate.mjs +200 -37
- package/lib/hooks/proactive-activation.mjs +284 -0
- package/lib/hooks/read-tracker.mjs +27 -4
- package/lib/hooks/readme-age-check.mjs +77 -0
- package/lib/hooks/registry-sync.mjs +12 -5
- package/lib/hooks/scan-secrets.mjs +50 -7
- package/lib/hooks/session-optimize.mjs +311 -0
- package/lib/hooks/session-start.mjs +287 -28
- package/lib/hooks/stop-notify.mjs +236 -96
- package/lib/hooks/stop-typecheck.mjs +13 -1
- package/lib/hooks/test-watch.mjs +68 -0
- package/lib/host-capabilities.mjs +31 -10
- package/lib/init-docs.mjs +731 -291
- package/lib/init-unified.mjs +1116 -0
- package/lib/init-update.mjs +168 -0
- package/lib/init.mjs +107 -0
- package/lib/install/first-invocation.mjs +119 -0
- package/lib/install/stage-project.mjs +69 -0
- package/lib/intake/classify.mjs +278 -0
- package/lib/intake/feedback.mjs +273 -0
- package/lib/intake/filesystem-queue.mjs +158 -0
- package/lib/intake/intake-config.mjs +132 -0
- package/lib/intake/postgres-queue.mjs +198 -0
- package/lib/intake/prepare.mjs +139 -0
- package/lib/intake/queue.mjs +83 -0
- package/lib/intake/session-prelude.mjs +50 -0
- package/lib/integrations/intake-integrations.mjs +740 -0
- package/lib/intent-classifier.mjs +253 -0
- package/lib/knowledge/layout.mjs +72 -0
- package/lib/knowledge/rag.mjs +331 -0
- package/lib/knowledge/search.mjs +315 -0
- package/lib/knowledge/trends.mjs +261 -0
- package/lib/logger.mjs +85 -0
- package/lib/mcp/broker.mjs +124 -0
- package/lib/mcp/server.mjs +554 -854
- package/lib/mcp/tools/document.mjs +132 -0
- package/lib/mcp/tools/memory.mjs +209 -0
- package/lib/mcp/tools/project.mjs +349 -0
- package/lib/mcp/tools/skills.mjs +409 -0
- package/lib/mcp/tools/storage.mjs +60 -0
- package/lib/mcp/tools/telemetry.mjs +315 -0
- package/lib/mcp/tools/workflow.mjs +112 -0
- package/lib/mcp-catalog.json +54 -3
- package/lib/mcp-manager.mjs +96 -48
- package/lib/mcp-platform-config.mjs +22 -22
- package/lib/memory-stats.mjs +121 -0
- package/lib/mode-commands.mjs +124 -0
- package/lib/model-free-selector.mjs +184 -0
- package/lib/model-pricing.mjs +152 -0
- package/lib/model-registry.mjs +226 -0
- package/lib/model-router.mjs +362 -386
- package/lib/observation-store.mjs +391 -0
- package/lib/ollama-manager.mjs +428 -0
- package/lib/opencode-config.mjs +13 -3
- package/lib/opencode-runtime-plugin.mjs +70 -38
- package/lib/opencode-telemetry.mjs +64 -6
- package/lib/orchestration-policy.mjs +509 -6
- package/lib/overrides/resolver.mjs +207 -0
- package/lib/parity.mjs +147 -0
- package/lib/paths.mjs +33 -0
- package/lib/performance/generate.mjs +212 -0
- package/lib/plugin-registry.mjs +268 -0
- package/lib/policy/engine.mjs +130 -0
- package/lib/policy/unified-gates.mjs +96 -0
- package/lib/project-detection.mjs +129 -0
- package/lib/project-init-shared.mjs +272 -0
- package/lib/project-profile.mjs +447 -0
- package/lib/prompt-composer.js +434 -0
- package/lib/prompt-metadata.mjs +1 -1
- package/lib/provider-capabilities-anthropic.js +44 -0
- package/lib/provider-capabilities-deepseek.js +37 -0
- package/lib/provider-capabilities-generic.js +26 -0
- package/lib/provider-capabilities-google.js +47 -0
- package/lib/provider-capabilities-openai.js +45 -0
- package/lib/provider-capabilities.js +142 -0
- package/lib/providers/atlassian-confluence/index.mjs +103 -0
- package/lib/providers/atlassian-jira/index.mjs +100 -0
- package/lib/providers/auth-manager.mjs +126 -0
- package/lib/providers/circuit-breaker.mjs +124 -0
- package/lib/providers/contract.mjs +93 -0
- package/lib/providers/github/index.mjs +126 -0
- package/lib/providers/registry.mjs +184 -0
- package/lib/providers/salesforce/index.mjs +100 -0
- package/lib/providers/slack/index.mjs +80 -0
- package/lib/reflect.mjs +137 -0
- package/lib/research-lint.mjs +164 -0
- package/lib/resources/budget.mjs +259 -0
- package/lib/role-preload.mjs +21 -6
- package/lib/roles/approval-surface.mjs +54 -0
- package/lib/roles/cli.mjs +118 -0
- package/lib/roles/event-bus.mjs +79 -0
- package/lib/roles/fence.mjs +84 -0
- package/lib/roles/gateway.mjs +260 -0
- package/lib/roles/hook-emit.mjs +37 -0
- package/lib/roles/manifest.mjs +48 -0
- package/lib/roles/router.mjs +27 -0
- package/lib/runtime-pressure.mjs +360 -0
- package/lib/schema-artifact.mjs +134 -0
- package/lib/schema-infer.mjs +551 -0
- package/lib/server/auth.mjs +168 -0
- package/lib/server/chat.mjs +336 -0
- package/lib/server/cors.mjs +77 -0
- package/lib/server/csrf.mjs +91 -0
- package/lib/server/index.mjs +1927 -78
- package/lib/server/insights.mjs +765 -0
- package/lib/server/rate-limit.mjs +91 -0
- package/lib/server/static/assets/index-ab25c707.js +70 -0
- package/lib/server/static/assets/index-f0c80a2b.css +1 -0
- package/lib/server/static/index.html +12 -817
- package/lib/server/telemetry-login.mjs +108 -0
- package/lib/server/webhook.mjs +510 -0
- package/lib/service-manager.mjs +522 -58
- package/lib/services/pattern-promotion-service.mjs +167 -0
- package/lib/services/telemetry-backend.mjs +178 -0
- package/lib/session-store.mjs +374 -0
- package/lib/setup-prompts.mjs +96 -0
- package/lib/setup.mjs +525 -38
- package/lib/skills-apply.mjs +280 -0
- package/lib/skills-scope.mjs +118 -0
- package/lib/status.mjs +261 -70
- package/lib/storage/admin.mjs +355 -0
- package/lib/storage/backend.mjs +2 -1
- package/lib/storage/backup.mjs +347 -0
- package/lib/storage/embeddings-engine.mjs +133 -0
- package/lib/storage/embeddings-legacy.mjs +85 -0
- package/lib/storage/embeddings-local.mjs +108 -0
- package/lib/storage/embeddings-ollama.mjs +78 -0
- package/lib/storage/embeddings-openai.mjs +85 -0
- package/lib/storage/embeddings.mjs +92 -33
- package/lib/storage/file-lock.mjs +130 -0
- package/lib/storage/fusion.mjs +95 -0
- package/lib/storage/hybrid-query.mjs +34 -27
- package/lib/storage/migrations.mjs +187 -0
- package/lib/storage/postgres-backup.mjs +124 -0
- package/lib/storage/sql-store.mjs +5 -15
- package/lib/storage/state-source.mjs +12 -13
- package/lib/storage/store-version.mjs +115 -0
- package/lib/storage/sync.mjs +144 -35
- package/lib/storage/unified-storage.mjs +550 -0
- package/lib/storage/vector-client.mjs +286 -0
- package/lib/storage/vector-store.mjs +71 -30
- package/lib/task-graph/generate.mjs +135 -0
- package/lib/task-graph/schema.mjs +81 -0
- package/lib/task-graph/store.mjs +71 -0
- package/lib/telemetry/backends/local.mjs +62 -0
- package/lib/telemetry/backends/{langfuse.mjs → remote.mjs} +27 -14
- package/lib/telemetry/backfill.mjs +180 -0
- package/lib/telemetry/eval-datasets.mjs +203 -0
- package/lib/telemetry/{langfuse-ingest.mjs → ingest.mjs} +26 -20
- package/lib/telemetry/intent-verifications.mjs +86 -0
- package/lib/telemetry/llm-judge.mjs +350 -0
- package/lib/telemetry/model-pricing-catalog.mjs +557 -0
- package/lib/telemetry/setup.mjs +151 -0
- package/lib/telemetry/skill-calls.mjs +78 -0
- package/lib/telemetry/team-rollup.mjs +4 -4
- package/lib/token-engine.js +117 -0
- package/lib/token-estimator-anthropic.js +15 -0
- package/lib/token-estimator-deepseek.js +13 -0
- package/lib/token-estimator-default.js +13 -0
- package/lib/token-estimator-google.js +13 -0
- package/lib/token-estimator-openai.js +13 -0
- package/lib/toolkit-env.mjs +1 -1
- package/lib/tty-prompts.mjs +211 -0
- package/lib/uninstall/uninstall.mjs +423 -0
- package/lib/update.mjs +115 -0
- package/lib/upgrade.mjs +141 -0
- package/lib/validator.mjs +51 -2
- package/lib/validators/skills.mjs +142 -0
- package/lib/wireframe.mjs +422 -0
- package/lib/worker/entrypoint.mjs +241 -0
- package/lib/worker/evidence.mjs +107 -0
- package/lib/worker/run.mjs +154 -0
- package/lib/worker/trace.mjs +182 -0
- package/lib/workflow-state.mjs +14 -18
- package/package.json +21 -8
- package/personas/construct.md +53 -51
- package/platforms/claude/CLAUDE.md +1 -1
- package/platforms/claude/settings.template.json +115 -68
- package/platforms/opencode/config.template.json +3 -7
- package/rules/common/agents.md +11 -38
- package/rules/common/beads-hygiene.md +75 -0
- package/rules/common/code-review.md +11 -100
- package/rules/common/coding-style.md +5 -3
- package/rules/common/comments.md +30 -111
- package/rules/common/commit-approval.md +53 -0
- package/rules/common/cx-agent-routing.md +1 -0
- package/rules/common/development-workflow.md +23 -41
- package/rules/common/doc-ownership.md +54 -0
- package/rules/common/efficiency.md +57 -0
- package/rules/common/framing.md +76 -0
- package/rules/common/git-workflow.md +2 -4
- package/rules/common/patterns.md +3 -7
- package/rules/common/performance.md +15 -31
- package/rules/common/release-gates.md +69 -0
- package/rules/common/research.md +107 -0
- package/rules/common/security.md +6 -6
- package/rules/common/skill-composition.md +67 -0
- package/rules/common/testing.md +15 -27
- package/rules/golang/hooks.md +0 -4
- package/rules/policy/bootstrap.yaml +11 -0
- package/rules/policy/drive.yaml +8 -0
- package/rules/policy/task.yaml +9 -0
- package/rules/policy/workflow.yaml +9 -0
- package/rules/python/hooks.md +0 -4
- package/rules/swift/hooks.md +0 -4
- package/rules/typescript/hooks.md +0 -4
- package/rules/web/hooks.md +0 -2
- package/{sync-agents.mjs → scripts/sync-agents.mjs} +306 -61
- package/skills/ai/prompt-optimizer.md +7 -7
- package/skills/compliance/ai-disclosure.md +58 -0
- package/skills/compliance/data-privacy.md +46 -0
- package/skills/compliance/license-audit.md +40 -0
- package/skills/compliance/regulatory-review.md +61 -0
- package/skills/docs/document-ingest-workflow.md +52 -0
- package/skills/docs/evidence-ingest-workflow.md +9 -0
- package/skills/docs/init-docs.md +51 -18
- package/skills/docs/product-intelligence-workflow.md +12 -0
- package/skills/docs/product-signal-workflow.md +4 -0
- package/skills/docs/research-workflow.md +23 -8
- package/skills/docs/runbook-workflow.md +1 -1
- package/skills/operating/orchestration-reference.md +151 -0
- package/skills/roles/architect.md +5 -0
- package/skills/roles/engineer.md +32 -0
- package/skills/roles/operator.md +12 -0
- package/skills/roles/reviewer.md +23 -0
- package/skills/routing.md +1 -1
- package/templates/devcontainer/Dockerfile.devcontainer +38 -0
- package/templates/devcontainer/devcontainer.json +31 -0
- package/templates/distribution/bootstrap.ps1 +142 -0
- package/templates/distribution/bootstrap.sh +196 -0
- package/templates/distribution/run.mjs +187 -0
- package/templates/docs/adr.md +44 -8
- package/templates/docs/changelog-entry.md +43 -0
- package/templates/docs/construct_guide.md +149 -0
- package/templates/docs/evidence-brief.md +2 -2
- package/templates/docs/meta-prd.md +140 -19
- package/templates/docs/onboarding.md +57 -0
- package/templates/docs/prd.md +159 -15
- package/templates/docs/research-brief.md +4 -4
- package/templates/homebrew/construct.rb +67 -0
- package/langfuse/docker-compose.yml +0 -82
- package/lib/eval-harness.mjs +0 -59
- package/lib/hooks/bootstrap-guard.mjs +0 -90
- package/lib/hooks/console-warn.mjs +0 -43
- package/lib/hooks/continuation-enforcer.mjs +0 -72
- package/lib/hooks/drive-guard.mjs +0 -89
- package/lib/hooks/mcp-task-scope.mjs +0 -47
- package/lib/hooks/task-completed-guard.mjs +0 -43
- package/lib/hooks/teammate-idle-guard.mjs +0 -54
- package/lib/hooks/workflow-guard.mjs +0 -62
- package/lib/prompt-composer.mjs +0 -196
- package/lib/review.mjs +0 -429
- package/lib/server/static/app.js +0 -841
- package/lib/telemetry/langfuse-model-sync.mjs +0 -270
- package/rules/common/hooks.md +0 -35
- package/rules/zh/README.md +0 -113
- package/rules/zh/agents.md +0 -55
- package/rules/zh/code-review.md +0 -129
- package/rules/zh/coding-style.md +0 -53
- package/rules/zh/development-workflow.md +0 -49
- package/rules/zh/git-workflow.md +0 -29
- package/rules/zh/hooks.md +0 -35
- package/rules/zh/patterns.md +0 -36
- package/rules/zh/performance.md +0 -60
- package/rules/zh/security.md +0 -34
- package/rules/zh/testing.md +0 -34
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/tools/skills.mjs — Skills, templates, agent contracts, orchestration policy, and team tools.
|
|
3
|
+
*
|
|
4
|
+
* Exposes listSkills, getSkill, searchSkills, getTemplate, listTemplates, agentContract,
|
|
5
|
+
* orchestrationPolicy, listTeams, and getTeam. All synchronous except agentContract (dynamic import).
|
|
6
|
+
* Requires ROOT_DIR injected via the opts argument on each call.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
11
|
+
import { routeRequest, routeRequestVerified, requiresExecutiveApproval, TERMINAL_STATES } from '../../orchestration-policy.mjs';
|
|
12
|
+
import { buildTaskPacketFromIntent } from '../../workflow-state.mjs';
|
|
13
|
+
import { logSkillCall } from '../../telemetry/skill-calls.mjs';
|
|
14
|
+
|
|
15
|
+
export function listSkills({ ROOT_DIR }) {
|
|
16
|
+
const skillsDir = join(ROOT_DIR, 'skills');
|
|
17
|
+
if (!existsSync(skillsDir)) return { error: 'Skills directory not found.' };
|
|
18
|
+
|
|
19
|
+
const listDirRecursive = (dir, prefix = '') => {
|
|
20
|
+
let results = [];
|
|
21
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
if (entry.isDirectory()) {
|
|
24
|
+
results = results.concat(listDirRecursive(join(dir, entry.name), `${prefix}${entry.name}/`));
|
|
25
|
+
} else if (entry.name.endsWith('.md') && entry.name !== 'SKILL.md') {
|
|
26
|
+
results.push(`${prefix}${entry.name.replace('.md', '')}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const skills = listDirRecursive(skillsDir).sort();
|
|
33
|
+
return { skills };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getSkill(args, { ROOT_DIR }) {
|
|
37
|
+
const { path: skillPath } = args;
|
|
38
|
+
if (!skillPath) return { error: 'Missing path argument' };
|
|
39
|
+
|
|
40
|
+
const fullPath = join(ROOT_DIR, 'skills', `${skillPath}.md`);
|
|
41
|
+
|
|
42
|
+
if (!existsSync(fullPath)) {
|
|
43
|
+
return { error: `Skill not found: ${skillPath}` };
|
|
44
|
+
}
|
|
45
|
+
const content = readFileSync(fullPath, 'utf8');
|
|
46
|
+
logSkillCall({ skillId: skillPath, source: 'mcp' });
|
|
47
|
+
return { content };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function searchSkills(args, { ROOT_DIR }) {
|
|
51
|
+
const { pattern } = args;
|
|
52
|
+
if (!pattern) return { error: 'Missing pattern argument' };
|
|
53
|
+
const skillsDir = join(ROOT_DIR, 'skills');
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const output = execSync(`rg -i "${pattern.replace(/"/g, '\\"')}" "${skillsDir}"`, { encoding: 'utf8' });
|
|
57
|
+
return { results: output.split('\n').filter(Boolean) };
|
|
58
|
+
} catch {
|
|
59
|
+
try {
|
|
60
|
+
const output = execSync(`grep -ri "${pattern.replace(/"/g, '\\"')}" "${skillsDir}"`, { encoding: 'utf8' });
|
|
61
|
+
return { results: output.split('\n').filter(Boolean) };
|
|
62
|
+
} catch {
|
|
63
|
+
return { results: [], note: 'No matches found or grep error' };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function listTemplatesRaw({ ROOT_DIR }) {
|
|
69
|
+
const shipped = [];
|
|
70
|
+
const override = [];
|
|
71
|
+
const shippedDir = join(ROOT_DIR, 'templates', 'docs');
|
|
72
|
+
if (existsSync(shippedDir)) {
|
|
73
|
+
for (const f of readdirSync(shippedDir)) {
|
|
74
|
+
if (f.endsWith('.md')) shipped.push(f.replace(/\.md$/, ''));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const overrideDir = join(process.cwd(), '.cx', 'templates', 'docs');
|
|
78
|
+
if (existsSync(overrideDir)) {
|
|
79
|
+
for (const f of readdirSync(overrideDir)) {
|
|
80
|
+
if (f.endsWith('.md')) override.push(f.replace(/\.md$/, ''));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { shipped: shipped.sort(), overridden: override.sort() };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function getTemplate(args, { ROOT_DIR }) {
|
|
87
|
+
const { name } = args;
|
|
88
|
+
if (!name) return { error: 'Missing name argument' };
|
|
89
|
+
const safe = String(name).replace(/[^a-zA-Z0-9._-]/g, '');
|
|
90
|
+
if (!safe) return { error: 'Invalid template name' };
|
|
91
|
+
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
const candidates = [
|
|
94
|
+
{ source: 'project-override', path: join(cwd, '.cx', 'templates', 'docs', `${safe}.md`) },
|
|
95
|
+
{ source: 'shipped-default', path: join(ROOT_DIR, 'templates', 'docs', `${safe}.md`) },
|
|
96
|
+
];
|
|
97
|
+
for (const { source, path: p } of candidates) {
|
|
98
|
+
if (existsSync(p)) {
|
|
99
|
+
return { source, path: p, content: readFileSync(p, 'utf8') };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { error: `Template not found: ${safe}`, available: listTemplatesRaw({ ROOT_DIR }) };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function listTemplates({ ROOT_DIR }) {
|
|
106
|
+
return listTemplatesRaw({ ROOT_DIR });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function agentContract(args = {}) {
|
|
110
|
+
const {
|
|
111
|
+
getAllContracts,
|
|
112
|
+
getContractById,
|
|
113
|
+
getContract,
|
|
114
|
+
getOutgoingContracts,
|
|
115
|
+
getIncomingContracts,
|
|
116
|
+
summarize,
|
|
117
|
+
} = await import('../../agent-contracts.mjs');
|
|
118
|
+
const { describePostconditions } = await import('../../agents/postconditions.mjs');
|
|
119
|
+
const { id, producer, consumer } = args;
|
|
120
|
+
// Attach binary postconditions to every returned contract so a specialist
|
|
121
|
+
// calling this tool at handoff time sees the hard checks their output
|
|
122
|
+
// packet must pass — not just the free-text postconditions field.
|
|
123
|
+
function attachPostconditions(c) {
|
|
124
|
+
if (!c?.producer) return c;
|
|
125
|
+
const binary = describePostconditions(c.producer);
|
|
126
|
+
return binary.length > 0 ? { ...c, binaryPostconditions: binary } : c;
|
|
127
|
+
}
|
|
128
|
+
if (id) {
|
|
129
|
+
const match = getContractById(String(id));
|
|
130
|
+
return match ? { contract: attachPostconditions(match) } : { error: `Contract not found: ${id}` };
|
|
131
|
+
}
|
|
132
|
+
if (producer && consumer) {
|
|
133
|
+
const match = getContract(String(producer), String(consumer));
|
|
134
|
+
return match ? { contract: attachPostconditions(match) } : { error: `No contract for ${producer} → ${consumer}` };
|
|
135
|
+
}
|
|
136
|
+
if (producer) return { producer: String(producer), contracts: getOutgoingContracts(String(producer)).map(attachPostconditions) };
|
|
137
|
+
if (consumer) return { consumer: String(consumer), contracts: getIncomingContracts(String(consumer)).map(attachPostconditions) };
|
|
138
|
+
return { summary: summarize(), contracts: getAllContracts().map(attachPostconditions) };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export async function orchestrationPolicy(args) {
|
|
142
|
+
// Use the LLM-verified router by default; it falls back to keyword-only
|
|
143
|
+
// when no model is configured or the verifier errors. Callers that need
|
|
144
|
+
// strictly synchronous behavior can import routeRequest directly.
|
|
145
|
+
const route = await routeRequestVerified(args || {});
|
|
146
|
+
const approvalRequired = requiresExecutiveApproval(args?.approval || {});
|
|
147
|
+
const draftTask = route.track !== 'immediate'
|
|
148
|
+
? buildTaskPacketFromIntent(args?.request || '', { fileCount: args?.fileCount, moduleCount: args?.moduleCount })
|
|
149
|
+
: null;
|
|
150
|
+
|
|
151
|
+
// Per-specialist context packets: when the caller supplied a candidate
|
|
152
|
+
// artifact list, build the role-filtered bundle each specialist should
|
|
153
|
+
// receive. Each packet carries its own omitted-with-reason list so the
|
|
154
|
+
// caller can see exactly what was dropped per role. The router lookup
|
|
155
|
+
// strips the `cx-` prefix to match ROLE_POLICIES keys (which are
|
|
156
|
+
// unprefixed, e.g. `engineer`, `product-manager`).
|
|
157
|
+
let contextPackets = null;
|
|
158
|
+
const candidates = Array.isArray(args?.candidates) ? args.candidates : null;
|
|
159
|
+
if (candidates && route.specialists?.length > 0) {
|
|
160
|
+
const { buildContextPacket } = await import('../../context-router.mjs');
|
|
161
|
+
contextPackets = {};
|
|
162
|
+
for (const specialist of route.specialists) {
|
|
163
|
+
const role = specialist.replace(/^cx-/, '');
|
|
164
|
+
contextPackets[specialist] = buildContextPacket({
|
|
165
|
+
request: args?.request || '',
|
|
166
|
+
triage: args?.triage || null,
|
|
167
|
+
role,
|
|
168
|
+
budget: args?.budget || {},
|
|
169
|
+
candidates,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Auto-generate a task graph when the caller names an intake packet.
|
|
175
|
+
// The graph mirrors the triage's recommendedChain one-to-one and is
|
|
176
|
+
// persisted to .cx/task-graphs/. Both intake.received and task_graph.created
|
|
177
|
+
// trace events share the same traceId so the lineage is queryable end-to-end.
|
|
178
|
+
let taskGraph = null;
|
|
179
|
+
let intakeError = null;
|
|
180
|
+
const intakeId = args?.intakeId;
|
|
181
|
+
if (intakeId) {
|
|
182
|
+
try {
|
|
183
|
+
const { createIntakeQueue } = await import('../../intake/queue.mjs');
|
|
184
|
+
const { generateTaskGraphFromTriage } = await import('../../task-graph/generate.mjs');
|
|
185
|
+
const { FilesystemTaskGraphStore } = await import('../../task-graph/store.mjs');
|
|
186
|
+
const { emitTraceEvent, newTraceId } = await import('../../worker/trace.mjs');
|
|
187
|
+
const cwd = process.cwd();
|
|
188
|
+
const queue = createIntakeQueue(cwd, process.env);
|
|
189
|
+
const intake = queue.read(intakeId);
|
|
190
|
+
if (!intake) {
|
|
191
|
+
intakeError = `intake packet not found: ${intakeId}`;
|
|
192
|
+
} else if (!intake.triage) {
|
|
193
|
+
intakeError = `intake packet ${intakeId} has no triage block`;
|
|
194
|
+
} else {
|
|
195
|
+
const traceId = args?.traceId || newTraceId();
|
|
196
|
+
const graph = generateTaskGraphFromTriage({
|
|
197
|
+
triage: intake.triage,
|
|
198
|
+
project: args?.project || (cwd.split('/').pop() || 'construct'),
|
|
199
|
+
request: args?.request || intake.excerpt || '',
|
|
200
|
+
intake: intake.intake,
|
|
201
|
+
});
|
|
202
|
+
new FilesystemTaskGraphStore(cwd).save(graph);
|
|
203
|
+
try {
|
|
204
|
+
emitTraceEvent({
|
|
205
|
+
rootDir: cwd,
|
|
206
|
+
eventType: 'task_graph.created',
|
|
207
|
+
traceId,
|
|
208
|
+
project: graph.project,
|
|
209
|
+
intakeId,
|
|
210
|
+
metadata: {
|
|
211
|
+
graphId: graph.id,
|
|
212
|
+
nodes: graph.nodes.length,
|
|
213
|
+
chain: graph.nodes.map((n) => n.owner),
|
|
214
|
+
intakeType: intake.triage.intakeType,
|
|
215
|
+
rdStage: intake.triage.rdStage,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
} catch { /* observability never breaks the caller */ }
|
|
219
|
+
taskGraph = { ...graph, traceId };
|
|
220
|
+
}
|
|
221
|
+
} catch (err) {
|
|
222
|
+
intakeError = err?.message || String(err);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
...route,
|
|
228
|
+
approvalRequired,
|
|
229
|
+
terminalStates: TERMINAL_STATES,
|
|
230
|
+
draftTask,
|
|
231
|
+
...(contextPackets ? { contextPackets } : {}),
|
|
232
|
+
...(taskGraph ? { taskGraph } : {}),
|
|
233
|
+
...(intakeError ? { intakeError } : {}),
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* broker_check — query the MCP broker's policy decision for a pending action.
|
|
239
|
+
*
|
|
240
|
+
* Agents call this BEFORE attempting a high-risk action. Returns the same
|
|
241
|
+
* shape `Broker.invoke` would, minus the execute step: `{allowed, reason,
|
|
242
|
+
* approvalRequired, source, brokerActive}`. Solo mode returns
|
|
243
|
+
* `brokerActive: false` with `allowed: true` so agents skip the prompt
|
|
244
|
+
* overhead when no broker is configured. Always emits a `tool.called`
|
|
245
|
+
* trace event so the audit trail captures the consultation even when no
|
|
246
|
+
* underlying tool is invoked.
|
|
247
|
+
*/
|
|
248
|
+
export async function brokerCheck(args = {}) {
|
|
249
|
+
const { policyDecision } = await import('../../policy/engine.mjs');
|
|
250
|
+
const { isBrokered } = await import('../broker.mjs');
|
|
251
|
+
const { emitTraceEvent } = await import('../../worker/trace.mjs');
|
|
252
|
+
const brokerActive = isBrokered(process.env);
|
|
253
|
+
|
|
254
|
+
const baseEvent = {
|
|
255
|
+
rootDir: process.cwd(),
|
|
256
|
+
eventType: 'tool.called',
|
|
257
|
+
traceId: args?.traceId,
|
|
258
|
+
project: args?.project || null,
|
|
259
|
+
role: args?.role || null,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
if (!brokerActive) {
|
|
263
|
+
try {
|
|
264
|
+
emitTraceEvent({
|
|
265
|
+
...baseEvent,
|
|
266
|
+
metadata: {
|
|
267
|
+
tool: args?.tool || null,
|
|
268
|
+
action: args?.action || null,
|
|
269
|
+
risk: args?.risk || 'low',
|
|
270
|
+
allowed: true,
|
|
271
|
+
approvalRequired: false,
|
|
272
|
+
source: 'broker-off',
|
|
273
|
+
brokerActive: false,
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
} catch { /* observability never breaks the caller */ }
|
|
277
|
+
return {
|
|
278
|
+
allowed: true,
|
|
279
|
+
reason: 'broker inactive (solo mode default; set CONSTRUCT_MCP_BROKER=on to engage)',
|
|
280
|
+
approvalRequired: false,
|
|
281
|
+
source: 'broker-off',
|
|
282
|
+
brokerActive: false,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const decision = policyDecision({
|
|
287
|
+
role: args?.role,
|
|
288
|
+
project: args?.project,
|
|
289
|
+
tool: args?.tool,
|
|
290
|
+
action: args?.action,
|
|
291
|
+
risk: args?.risk,
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
emitTraceEvent({
|
|
296
|
+
...baseEvent,
|
|
297
|
+
metadata: {
|
|
298
|
+
tool: args?.tool || null,
|
|
299
|
+
action: args?.action || null,
|
|
300
|
+
risk: args?.risk || 'low',
|
|
301
|
+
allowed: decision.allowed,
|
|
302
|
+
approvalRequired: decision.approvalRequired,
|
|
303
|
+
source: decision.source,
|
|
304
|
+
brokerActive: true,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
} catch { /* swallow */ }
|
|
308
|
+
|
|
309
|
+
return { ...decision, brokerActive: true };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* worker_run — bounded command execution with optional task-graph evidence linkage.
|
|
314
|
+
*
|
|
315
|
+
* Wraps lib/worker/run.mjs `runJob`. Records evidence on the named task
|
|
316
|
+
* graph node when both `graphId` and `nodeId` are provided; otherwise
|
|
317
|
+
* returns the result without writing to a graph. Emits `worker.started`
|
|
318
|
+
* / `worker.completed` trace events from inside runJob, plus an
|
|
319
|
+
* `evidence.recorded` event when evidence is appended.
|
|
320
|
+
*
|
|
321
|
+
* Path policy: `allowedPaths` defaults to [cwd] so jobs can't reach
|
|
322
|
+
* outside the project. Set explicitly to broaden, e.g. when staging
|
|
323
|
+
* artifacts in a sibling worktree.
|
|
324
|
+
*/
|
|
325
|
+
export async function workerRun(args = {}) {
|
|
326
|
+
const { runJob } = await import('../../worker/run.mjs');
|
|
327
|
+
const { evidenceFromJobResult, recordEvidence } = await import('../../worker/evidence.mjs');
|
|
328
|
+
const { FilesystemTaskGraphStore } = await import('../../task-graph/store.mjs');
|
|
329
|
+
|
|
330
|
+
const cwd = process.cwd();
|
|
331
|
+
const jobId = args?.jobId || `worker-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
332
|
+
const allowedPaths = Array.isArray(args?.allowedPaths) && args.allowedPaths.length > 0
|
|
333
|
+
? args.allowedPaths
|
|
334
|
+
: [cwd];
|
|
335
|
+
|
|
336
|
+
let result;
|
|
337
|
+
try {
|
|
338
|
+
result = await runJob({
|
|
339
|
+
rootDir: cwd,
|
|
340
|
+
job: {
|
|
341
|
+
jobId,
|
|
342
|
+
taskId: args?.nodeId || args?.taskId || null,
|
|
343
|
+
project: args?.project || null,
|
|
344
|
+
workspaceRef: args?.workspaceRef || cwd,
|
|
345
|
+
command: args?.command,
|
|
346
|
+
args: Array.isArray(args?.args) ? args.args : [],
|
|
347
|
+
timeoutSeconds: args?.timeoutSeconds || 300,
|
|
348
|
+
envPolicy: args?.envPolicy || 'restricted',
|
|
349
|
+
allowedPaths,
|
|
350
|
+
allowedEnvKeys: Array.isArray(args?.allowedEnvKeys) ? args.allowedEnvKeys : [],
|
|
351
|
+
traceId: args?.traceId,
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
} catch (err) {
|
|
355
|
+
return { error: err?.message || String(err) };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// When the caller named a specific task graph node, append an evidence
|
|
359
|
+
// record. The result includes the evidence shape so the caller can show
|
|
360
|
+
// the user what was attached.
|
|
361
|
+
let evidence = null;
|
|
362
|
+
if (args?.graphId && args?.nodeId) {
|
|
363
|
+
try {
|
|
364
|
+
const store = new FilesystemTaskGraphStore(cwd);
|
|
365
|
+
evidence = evidenceFromJobResult(result, {
|
|
366
|
+
evidenceType: args?.evidenceType || 'test-result',
|
|
367
|
+
summary: args?.evidenceSummary,
|
|
368
|
+
});
|
|
369
|
+
recordEvidence({
|
|
370
|
+
store,
|
|
371
|
+
graphId: args.graphId,
|
|
372
|
+
nodeId: args.nodeId,
|
|
373
|
+
evidence,
|
|
374
|
+
rootDir: cwd,
|
|
375
|
+
});
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return { ...result, evidence: null, evidenceError: err?.message || String(err) };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return { ...result, ...(evidence ? { evidence } : {}) };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export function listTeams({ ROOT_DIR }) {
|
|
385
|
+
const teamsPath = join(ROOT_DIR, 'agents', 'teams.json');
|
|
386
|
+
if (!existsSync(teamsPath)) return { error: 'agents/teams.json not found' };
|
|
387
|
+
const { templates } = JSON.parse(readFileSync(teamsPath, 'utf8'));
|
|
388
|
+
return {
|
|
389
|
+
teams: Object.entries(templates).map(([name, t]) => ({
|
|
390
|
+
name,
|
|
391
|
+
description: t.description,
|
|
392
|
+
members: t.members,
|
|
393
|
+
focus: t.focus,
|
|
394
|
+
skills: t.skills,
|
|
395
|
+
promotionGates: t.promotionGates,
|
|
396
|
+
})),
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function getTeam(args, { ROOT_DIR }) {
|
|
401
|
+
const { name } = args;
|
|
402
|
+
if (!name) return { error: 'Missing name argument' };
|
|
403
|
+
const teamsPath = join(ROOT_DIR, 'agents', 'teams.json');
|
|
404
|
+
if (!existsSync(teamsPath)) return { error: 'agents/teams.json not found' };
|
|
405
|
+
const { templates } = JSON.parse(readFileSync(teamsPath, 'utf8'));
|
|
406
|
+
const team = templates[name];
|
|
407
|
+
if (!team) return { error: `Team not found: ${name}`, available: Object.keys(templates) };
|
|
408
|
+
return { name, ...team };
|
|
409
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/mcp/tools/storage.mjs — Storage MCP tools: status, sync, reset, and artifact deletion.
|
|
3
|
+
*
|
|
4
|
+
* All functions are async. Wraps lib/storage/admin.mjs and lib/storage/sync.mjs.
|
|
5
|
+
* Requires confirm=true guards on destructive operations (reset, delete).
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { deleteIngestedArtifacts, getStorageStatus, inferProjectName, purgeExpiredData, resetStorage } from '../../storage/admin.mjs';
|
|
9
|
+
import { syncFileStateToSql } from '../../storage/sync.mjs';
|
|
10
|
+
|
|
11
|
+
export async function storageStatus(args) {
|
|
12
|
+
const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
13
|
+
const project = args.project ? String(args.project) : inferProjectName(cwd);
|
|
14
|
+
return getStorageStatus(cwd, { env: process.env, project });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function storageSync(args) {
|
|
18
|
+
const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
19
|
+
const project = args.project ? String(args.project) : inferProjectName(cwd);
|
|
20
|
+
const syncResult = await syncFileStateToSql(cwd, { env: process.env, project });
|
|
21
|
+
|
|
22
|
+
// Run retention purge on each sync so expired data is cleaned automatically.
|
|
23
|
+
let purge = null;
|
|
24
|
+
try {
|
|
25
|
+
purge = await purgeExpiredData(cwd, { env: process.env, project });
|
|
26
|
+
} catch { /* best effort */ }
|
|
27
|
+
|
|
28
|
+
return purge && purge.status !== 'skipped' ? { ...syncResult, retention: purge } : syncResult;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function storageReset(args) {
|
|
32
|
+
const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
33
|
+
const project = args.project ? String(args.project) : inferProjectName(cwd);
|
|
34
|
+
if (args.confirm !== true) {
|
|
35
|
+
return { error: 'storage_reset requires confirm=true' };
|
|
36
|
+
}
|
|
37
|
+
return resetStorage(cwd, {
|
|
38
|
+
env: process.env,
|
|
39
|
+
project,
|
|
40
|
+
resetSql: args.reset_sql !== false,
|
|
41
|
+
resetVector: args.reset_vector !== false,
|
|
42
|
+
resetIngested: args.reset_ingested === true,
|
|
43
|
+
confirm: true,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function deleteIngestedArtifactsTool(args) {
|
|
48
|
+
const cwd = args.cwd ? resolve(String(args.cwd)) : process.cwd();
|
|
49
|
+
if (args.confirm !== true) {
|
|
50
|
+
return { error: 'delete_ingested_artifacts requires confirm=true' };
|
|
51
|
+
}
|
|
52
|
+
const files = Array.isArray(args.files)
|
|
53
|
+
? args.files.map((value) => String(value))
|
|
54
|
+
: [];
|
|
55
|
+
try {
|
|
56
|
+
return deleteIngestedArtifacts(cwd, { files, confirm: true });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return { error: error.message ?? String(error) };
|
|
59
|
+
}
|
|
60
|
+
}
|