@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,168 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lib/init-update.mjs — update existing projects with latest AGENTS.md template.
|
|
4
|
+
*
|
|
5
|
+
* For projects that already have AGENTS.md but need to sync with the latest
|
|
6
|
+
* Construct operating rules and CI enforcement.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node lib/init-update.mjs [--dry-run] [--cwd=path]
|
|
10
|
+
* construct init:update [--dry-run]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
16
|
+
|
|
17
|
+
const args = process.argv.slice(2);
|
|
18
|
+
const dryRun = args.includes("--dry-run");
|
|
19
|
+
const cwdArg = args.find(arg => arg.startsWith("--cwd="));
|
|
20
|
+
const cwd = cwdArg ? path.resolve(cwdArg.split("=")[1]) : process.cwd();
|
|
21
|
+
|
|
22
|
+
console.log(`📝 Updating project documentation in ${cwd}`);
|
|
23
|
+
|
|
24
|
+
// Load the AGENTS.md template from project-init-shared
|
|
25
|
+
async function loadAgentsTemplate(projectName) {
|
|
26
|
+
const modulePath = path.join(import.meta.dirname, 'project-init-shared.mjs');
|
|
27
|
+
try {
|
|
28
|
+
// Import the module and call the buildAgentsGuide function
|
|
29
|
+
const module = await import(modulePath);
|
|
30
|
+
if (module.buildAgentsGuide) {
|
|
31
|
+
return module.buildAgentsGuide(projectName);
|
|
32
|
+
}
|
|
33
|
+
throw new Error('Could not find buildAgentsGuide function in project-init-shared.mjs');
|
|
34
|
+
} catch (error) {
|
|
35
|
+
console.error(`❌ Failed to load AGENTS.md template: ${error.message}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function updateAgentsMd(existingContent, newTemplate) {
|
|
41
|
+
// We'll do a simple replacement for now
|
|
42
|
+
// In a real implementation, we'd parse the existing file and merge sections
|
|
43
|
+
|
|
44
|
+
console.log(`🔄 Updating AGENTS.md`);
|
|
45
|
+
|
|
46
|
+
if (dryRun) {
|
|
47
|
+
console.log(`📋 Dry run - would update AGENTS.md`);
|
|
48
|
+
console.log(`--- Current AGENTS.md (first 200 chars) ---`);
|
|
49
|
+
console.log(existingContent.substring(0, 200) + '...');
|
|
50
|
+
console.log(`--- New AGENTS.md (first 200 chars) ---`);
|
|
51
|
+
console.log(newTemplate.substring(0, 200) + '...');
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Backup existing file
|
|
56
|
+
const backupPath = path.join(cwd, 'AGENTS.md.backup');
|
|
57
|
+
writeFileSync(backupPath, existingContent, 'utf8');
|
|
58
|
+
console.log(`📦 Created backup at ${backupPath}`);
|
|
59
|
+
|
|
60
|
+
// Write new template
|
|
61
|
+
const agentsPath = path.join(cwd, 'AGENTS.md');
|
|
62
|
+
writeFileSync(agentsPath, newTemplate, 'utf8');
|
|
63
|
+
console.log(`✅ Updated AGENTS.md`);
|
|
64
|
+
|
|
65
|
+
return newTemplate;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function updateCiWorkflow(cwd) {
|
|
69
|
+
const workflowPath = path.join(cwd, '.github', 'workflows', 'ci.yml');
|
|
70
|
+
if (!existsSync(workflowPath)) {
|
|
71
|
+
console.log(`⚠️ No CI workflow found at ${workflowPath}`);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let content = readFileSync(workflowPath, 'utf8');
|
|
76
|
+
|
|
77
|
+
// Check if docs:verify is already in the workflow
|
|
78
|
+
if (content.includes('construct docs:verify')) {
|
|
79
|
+
console.log(`✅ CI workflow already has docs:verify check`);
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Find the test job and add docs:verify after doctor
|
|
84
|
+
if (content.includes('node bin/construct doctor')) {
|
|
85
|
+
const updatedContent = content.replace(
|
|
86
|
+
/(\s+- run: node bin\/construct doctor\s*)/,
|
|
87
|
+
'$1 - run: node bin/construct docs:verify\n'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
if (dryRun) {
|
|
91
|
+
console.log(`📋 Dry run - would update CI workflow to include docs:verify`);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const backupPath = workflowPath + '.backup';
|
|
96
|
+
writeFileSync(backupPath, content, 'utf8');
|
|
97
|
+
writeFileSync(workflowPath, updatedContent, 'utf8');
|
|
98
|
+
console.log(`✅ Updated CI workflow to include docs:verify check`);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`⚠️ Could not find 'construct doctor' in CI workflow to add docs:verify`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function main() {
|
|
107
|
+
console.log(`\n🔍 Checking project structure...`);
|
|
108
|
+
|
|
109
|
+
// Check for AGENTS.md
|
|
110
|
+
const agentsPath = path.join(cwd, 'AGENTS.md');
|
|
111
|
+
if (!existsSync(agentsPath)) {
|
|
112
|
+
console.log(`❌ AGENTS.md not found. Run 'construct init' first.`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const existingAgents = readFileSync(agentsPath, 'utf8');
|
|
117
|
+
|
|
118
|
+
// Get project name from package.json or directory name
|
|
119
|
+
let projectName = path.basename(cwd);
|
|
120
|
+
const packagePath = path.join(cwd, 'package.json');
|
|
121
|
+
if (existsSync(packagePath)) {
|
|
122
|
+
try {
|
|
123
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
124
|
+
if (pkg.name) projectName = pkg.name;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.log(`⚠️ Could not read package.json: ${error.message}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const template = await loadAgentsTemplate(projectName);
|
|
131
|
+
|
|
132
|
+
console.log(`\n📊 Analysis:`);
|
|
133
|
+
console.log(` • AGENTS.md found (${existingAgents.length} chars)`);
|
|
134
|
+
console.log(` • Latest template loaded (${template.length} chars)`);
|
|
135
|
+
console.log(` • Project name: ${projectName}`);
|
|
136
|
+
|
|
137
|
+
// Check if AGENTS.md has documentation enforcement section
|
|
138
|
+
const hasDocEnforcement = existingAgents.includes('Documentation Rules');
|
|
139
|
+
console.log(` • Has documentation enforcement: ${hasDocEnforcement ? '✅' : '❌'}`);
|
|
140
|
+
|
|
141
|
+
// Check for CI workflow
|
|
142
|
+
const ciUpdated = updateCiWorkflow(cwd);
|
|
143
|
+
|
|
144
|
+
// Update AGENTS.md if it doesn't have the latest sections
|
|
145
|
+
if (!hasDocEnforcement) {
|
|
146
|
+
updateAgentsMd(existingAgents, template);
|
|
147
|
+
} else {
|
|
148
|
+
console.log(`✅ AGENTS.md already has documentation enforcement rules`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log(`\n🎉 Update complete!`);
|
|
152
|
+
console.log(`\nNext steps:`);
|
|
153
|
+
console.log(` 1. Review the updated AGENTS.md`);
|
|
154
|
+
console.log(` 2. Run 'construct docs:verify' to test documentation checks`);
|
|
155
|
+
console.log(` 3. Commit changes: git add AGENTS.md .github/workflows/ci.yml`);
|
|
156
|
+
console.log(` 4. Push and verify CI passes`);
|
|
157
|
+
|
|
158
|
+
if (dryRun) {
|
|
159
|
+
console.log(`\n⚠️ This was a dry run. Use 'construct init:update' without --dry-run to apply changes.`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
164
|
+
main().catch(error => {
|
|
165
|
+
console.error(`❌ Error: ${error.message}`);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
});
|
|
168
|
+
}
|
package/lib/init.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lib/init.mjs — bootstrap Construct project state in an existing repo.
|
|
4
|
+
*
|
|
5
|
+
* Intentionally separate from `init-docs`: `construct init` creates the
|
|
6
|
+
* baseline agent/workflow context for a repo, while `construct init-docs`
|
|
7
|
+
* stands up a documentation system and lane-specific templates.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node lib/init.mjs [target-path]
|
|
11
|
+
* construct init [path]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
buildAgentsGuide,
|
|
19
|
+
buildContextJson,
|
|
20
|
+
buildContextMarkdown,
|
|
21
|
+
buildPlanTemplate,
|
|
22
|
+
writeStampedIfMissing,
|
|
23
|
+
} from "./project-init-shared.mjs";
|
|
24
|
+
|
|
25
|
+
const args = process.argv.slice(2);
|
|
26
|
+
const targetArg = args.find((arg) => !arg.startsWith("--"));
|
|
27
|
+
const target = path.resolve(targetArg ?? process.cwd());
|
|
28
|
+
|
|
29
|
+
const created = [];
|
|
30
|
+
const skipped = [];
|
|
31
|
+
|
|
32
|
+
function inferProjectName(targetPath) {
|
|
33
|
+
const packageJsonPath = path.join(targetPath, "package.json");
|
|
34
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
35
|
+
try {
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
37
|
+
return pkg.name || path.basename(targetPath);
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
return path.basename(targetPath);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function main() {
|
|
44
|
+
const projectName = inferProjectName(target);
|
|
45
|
+
|
|
46
|
+
writeStampedIfMissing({
|
|
47
|
+
targetRoot: target,
|
|
48
|
+
created,
|
|
49
|
+
skipped,
|
|
50
|
+
filePath: path.join(target, "AGENTS.md"),
|
|
51
|
+
content: buildAgentsGuide(projectName),
|
|
52
|
+
generator: "construct/init",
|
|
53
|
+
});
|
|
54
|
+
writeStampedIfMissing({
|
|
55
|
+
targetRoot: target,
|
|
56
|
+
created,
|
|
57
|
+
skipped,
|
|
58
|
+
filePath: path.join(target, "plan.md"),
|
|
59
|
+
content: buildPlanTemplate(),
|
|
60
|
+
generator: "construct/init",
|
|
61
|
+
});
|
|
62
|
+
writeStampedIfMissing({
|
|
63
|
+
targetRoot: target,
|
|
64
|
+
created,
|
|
65
|
+
skipped,
|
|
66
|
+
filePath: path.join(target, ".cx", "context.json"),
|
|
67
|
+
content: buildContextJson(projectName),
|
|
68
|
+
generator: "construct/init",
|
|
69
|
+
});
|
|
70
|
+
writeStampedIfMissing({
|
|
71
|
+
targetRoot: target,
|
|
72
|
+
created,
|
|
73
|
+
skipped,
|
|
74
|
+
filePath: path.join(target, ".cx", "context.md"),
|
|
75
|
+
content: buildContextMarkdown(),
|
|
76
|
+
generator: "construct/init",
|
|
77
|
+
});
|
|
78
|
+
writeStampedIfMissing({
|
|
79
|
+
targetRoot: target,
|
|
80
|
+
created,
|
|
81
|
+
skipped,
|
|
82
|
+
filePath: path.join(target, ".cx", ".gitkeep"),
|
|
83
|
+
content: "",
|
|
84
|
+
generator: "construct/init",
|
|
85
|
+
});
|
|
86
|
+
writeStampedIfMissing({
|
|
87
|
+
targetRoot: target,
|
|
88
|
+
created,
|
|
89
|
+
skipped,
|
|
90
|
+
filePath: path.join(target, ".cx", "inbox", ".gitkeep"),
|
|
91
|
+
content: "",
|
|
92
|
+
generator: "construct/init",
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
process.stdout.write(`\nConstruct init → ${target}\n\n`);
|
|
96
|
+
if (created.length) {
|
|
97
|
+
process.stdout.write("Created:\n");
|
|
98
|
+
for (const file of created) process.stdout.write(` + ${file}\n`);
|
|
99
|
+
}
|
|
100
|
+
if (skipped.length) {
|
|
101
|
+
process.stdout.write("\nSkipped (already exist):\n");
|
|
102
|
+
for (const file of skipped) process.stdout.write(` ~ ${file}\n`);
|
|
103
|
+
}
|
|
104
|
+
process.stdout.write(`\n${created.length} created, ${skipped.length} skipped.\n`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
main();
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/install/first-invocation.mjs — one-shot resource probe on first use.
|
|
3
|
+
*
|
|
4
|
+
* Construct's postinstall is silent and idempotent (per the husky / prisma /
|
|
5
|
+
* supabase pattern — npm swallows postinstall output, so prompting from there
|
|
6
|
+
* is unreliable). Instead, the first time a user invokes ANY `construct`
|
|
7
|
+
* command on a machine that hasn't been bootstrapped, this probe runs:
|
|
8
|
+
* 1. Quick check whether `BOOTSTRAP_CHECKED=1` is cached in config.env.
|
|
9
|
+
* If yes → silent, return immediately.
|
|
10
|
+
* 2. Otherwise, run `probeAll()` from lib/bootstrap/resources.mjs.
|
|
11
|
+
* 3. If everything's healthy → silent, set BOOTSTRAP_CHECKED=1.
|
|
12
|
+
* 4. If anything's missing → print the status table to stderr; on TTY,
|
|
13
|
+
* prompt to run `construct init` now. Always set BOOTSTRAP_CHECKED=1
|
|
14
|
+
* so we don't re-prompt on subsequent commands.
|
|
15
|
+
*
|
|
16
|
+
* Skipped entirely for hook invocations (would corrupt hook output), for
|
|
17
|
+
* `setup`/`uninstall`/`version` (those handle their own flow), and on
|
|
18
|
+
* non-TTY runs (CI / scripts — keep them silent).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import readline from 'node:readline';
|
|
22
|
+
|
|
23
|
+
import { parseEnvFile, writeEnvValues, getUserEnvPath } from '../env-config.mjs';
|
|
24
|
+
import { probeAll, formatProbe } from '../bootstrap/resources.mjs';
|
|
25
|
+
|
|
26
|
+
const BOOTSTRAP_CHECKED_KEY = 'BOOTSTRAP_CHECKED';
|
|
27
|
+
const SKIP_COMMANDS = new Set([
|
|
28
|
+
'hook', 'setup', 'uninstall', 'version', 'help', 'completions', 'doctor',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export function shouldSkipProbe({ command, env = process.env, stdin = process.stdin } = {}) {
|
|
32
|
+
if (!command) return true;
|
|
33
|
+
if (SKIP_COMMANDS.has(command)) return true;
|
|
34
|
+
if (command.startsWith('-')) return true;
|
|
35
|
+
if (env[BOOTSTRAP_CHECKED_KEY] === '1') return true;
|
|
36
|
+
if (env.CONSTRUCT_SKIP_BOOTSTRAP_PROBE === '1') return true;
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function maybeFirstInvocationProbe({
|
|
41
|
+
command,
|
|
42
|
+
homeDir,
|
|
43
|
+
env = process.env,
|
|
44
|
+
stdin = process.stdin,
|
|
45
|
+
stdout = process.stderr,
|
|
46
|
+
readlineModule = readline,
|
|
47
|
+
probeAllFn = probeAll,
|
|
48
|
+
formatProbeFn = formatProbe,
|
|
49
|
+
writeEnvFn = writeEnvValues,
|
|
50
|
+
} = {}) {
|
|
51
|
+
if (shouldSkipProbe({ command, env, stdin })) {
|
|
52
|
+
return { ran: false, reason: 'skipped' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const envPath = getUserEnvPath(homeDir);
|
|
56
|
+
const persisted = parseEnvFile(envPath);
|
|
57
|
+
if (persisted[BOOTSTRAP_CHECKED_KEY] === '1') {
|
|
58
|
+
return { ran: false, reason: 'already-checked' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const probes = await probeAllFn();
|
|
62
|
+
const missingRequired = probes.filter((p) => p.required && !p.present);
|
|
63
|
+
const missingOptional = probes.filter((p) => !p.required && !p.present);
|
|
64
|
+
|
|
65
|
+
if (missingRequired.length === 0 && missingOptional.length === 0) {
|
|
66
|
+
writeEnvFn(envPath, { [BOOTSTRAP_CHECKED_KEY]: '1' });
|
|
67
|
+
return { ran: true, reason: 'all-healthy', missingRequired: 0, missingOptional: 0 };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
stdout.write('\n[construct] First-run Resource check\n');
|
|
71
|
+
for (const probe of probes) stdout.write(formatProbeFn(probe) + '\n');
|
|
72
|
+
|
|
73
|
+
if (missingRequired.length > 0) {
|
|
74
|
+
stdout.write(`\n[construct] ${missingRequired.length} required resource(s) missing. Run \`construct init\` to install.\n\n`);
|
|
75
|
+
} else {
|
|
76
|
+
stdout.write(`\n[construct] ${missingOptional.length} optional resource(s) missing. Run \`construct init\` to install (Postgres, telemetry, embedding model), or continue in degraded mode.\n\n`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (stdin.isTTY && stdout.isTTY !== false) {
|
|
80
|
+
const wants = await promptYesNo({
|
|
81
|
+
question: 'Run `construct init` now?',
|
|
82
|
+
defaultYes: missingRequired.length > 0,
|
|
83
|
+
readlineModule,
|
|
84
|
+
stdin,
|
|
85
|
+
stdout,
|
|
86
|
+
});
|
|
87
|
+
writeEnvFn(envPath, { [BOOTSTRAP_CHECKED_KEY]: '1' });
|
|
88
|
+
return {
|
|
89
|
+
ran: true,
|
|
90
|
+
reason: 'prompted',
|
|
91
|
+
missingRequired: missingRequired.length,
|
|
92
|
+
missingOptional: missingOptional.length,
|
|
93
|
+
runSetup: wants,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
writeEnvFn(envPath, { [BOOTSTRAP_CHECKED_KEY]: '1' });
|
|
98
|
+
return {
|
|
99
|
+
ran: true,
|
|
100
|
+
reason: 'reported-non-tty',
|
|
101
|
+
missingRequired: missingRequired.length,
|
|
102
|
+
missingOptional: missingOptional.length,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function promptYesNo({ question, defaultYes, readlineModule, stdin, stdout }) {
|
|
107
|
+
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
108
|
+
return new Promise((resolve) => {
|
|
109
|
+
const rl = readlineModule.createInterface({ input: stdin, output: stdout });
|
|
110
|
+
rl.question(`${question} ${suffix} `, (raw) => {
|
|
111
|
+
rl.close();
|
|
112
|
+
const trimmed = String(raw || '').trim().toLowerCase();
|
|
113
|
+
if (!trimmed) return resolve(defaultYes);
|
|
114
|
+
if (['y', 'yes'].includes(trimmed)) return resolve(true);
|
|
115
|
+
if (['n', 'no'].includes(trimmed)) return resolve(false);
|
|
116
|
+
resolve(defaultYes);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/install/stage-project.mjs — shared staging logic for a consumer project.
|
|
3
|
+
*
|
|
4
|
+
* Same work as `bin/construct-postinstall.mjs` but callable from both the
|
|
5
|
+
* npm postinstall hook and `construct init`. Stages `.construct/{run.mjs,
|
|
6
|
+
* bootstrap.sh, bootstrap.ps1, version}` and runs `sync-agents.mjs --project`
|
|
7
|
+
* to materialise `.claude/agents/` and `.claude/settings.json` from the
|
|
8
|
+
* bundled registry. Idempotent — re-runs overwrite the launcher files but
|
|
9
|
+
* leave existing `.construct/version` and project state untouched beyond
|
|
10
|
+
* what `sync-agents.mjs --project` writes itself.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from 'node:child_process';
|
|
14
|
+
import { existsSync, copyFileSync, writeFileSync, mkdirSync, chmodSync } from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
|
|
17
|
+
export function stageProjectAdapters({ projectRoot, packageRoot, pkgVersion, log }) {
|
|
18
|
+
if (!projectRoot) throw new Error('stageProjectAdapters: projectRoot is required');
|
|
19
|
+
if (!packageRoot) throw new Error('stageProjectAdapters: packageRoot is required');
|
|
20
|
+
const emit = typeof log === 'function' ? log : () => {};
|
|
21
|
+
|
|
22
|
+
const templateDir = path.join(packageRoot, 'templates', 'distribution');
|
|
23
|
+
const syncScript = path.join(packageRoot, 'scripts', 'sync-agents.mjs');
|
|
24
|
+
|
|
25
|
+
ensureProjectLauncher({ projectRoot, templateDir, pkgVersion });
|
|
26
|
+
emit(`staged .construct/ launcher in ${projectRoot}`);
|
|
27
|
+
|
|
28
|
+
if (!existsSync(syncScript)) {
|
|
29
|
+
emit(`sync-agents.mjs not found at ${syncScript}; skipping adapter sync`);
|
|
30
|
+
return { staged: true, synced: false };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
emit(`syncing project adapters into ${projectRoot}/.claude/`);
|
|
34
|
+
const result = spawnSync(process.execPath, [syncScript, '--project'], {
|
|
35
|
+
cwd: projectRoot,
|
|
36
|
+
stdio: 'inherit',
|
|
37
|
+
env: { ...process.env, CONSTRUCT_PROJECT_ROOT: projectRoot },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (result.status !== 0) {
|
|
41
|
+
emit(`sync failed (exit ${result.status}); project left in a clean state`);
|
|
42
|
+
return { staged: true, synced: false };
|
|
43
|
+
}
|
|
44
|
+
return { staged: true, synced: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function ensureProjectLauncher({ projectRoot, templateDir, pkgVersion }) {
|
|
48
|
+
const dotConstruct = path.join(projectRoot, '.construct');
|
|
49
|
+
mkdirSync(dotConstruct, { recursive: true });
|
|
50
|
+
mkdirSync(path.join(dotConstruct, 'cache', 'bin'), { recursive: true });
|
|
51
|
+
|
|
52
|
+
const versionPath = path.join(dotConstruct, 'version');
|
|
53
|
+
if (!existsSync(versionPath) && pkgVersion) {
|
|
54
|
+
writeFileSync(versionPath, pkgVersion + '\n');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const copies = [
|
|
58
|
+
['run.mjs', 0o644],
|
|
59
|
+
['bootstrap.sh', 0o755],
|
|
60
|
+
['bootstrap.ps1', 0o644],
|
|
61
|
+
];
|
|
62
|
+
for (const [name, mode] of copies) {
|
|
63
|
+
const src = path.join(templateDir, name);
|
|
64
|
+
const dst = path.join(dotConstruct, name);
|
|
65
|
+
if (!existsSync(src)) continue;
|
|
66
|
+
copyFileSync(src, dst);
|
|
67
|
+
try { chmodSync(dst, mode); } catch { /* mode set is best-effort on Windows */ }
|
|
68
|
+
}
|
|
69
|
+
}
|