@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
|
@@ -2,41 +2,83 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* lib/hooks/session-start.mjs — Session start hook — emits resumable project context at the start of each session.
|
|
4
4
|
*
|
|
5
|
-
* Runs at session start. Reads .cx/context.json,
|
|
5
|
+
* Runs at session start. Reads .cx/context.json, git status, and efficiency log to produce a structured resume message. Non-blocking — always exits 0.
|
|
6
|
+
*
|
|
7
|
+
* @p95ms 300
|
|
8
|
+
* @maxBlockingScope SessionStart
|
|
6
9
|
*/
|
|
7
|
-
import { readFileSync, existsSync } from 'fs';
|
|
10
|
+
import { readFileSync, existsSync, statSync, writeFileSync } from 'fs';
|
|
8
11
|
import { execSync } from 'child_process';
|
|
9
12
|
import { homedir } from 'os';
|
|
10
13
|
import { dirname, join, resolve } from 'path';
|
|
11
14
|
import { fileURLToPath } from 'url';
|
|
12
|
-
import {
|
|
15
|
+
import { loadConstructEnv } from '../env-config.mjs';
|
|
13
16
|
import { buildCompactEfficiencyDigest, readEfficiencyLog } from '../efficiency.mjs';
|
|
14
17
|
import { readContextState, contextSummaryLine } from '../context-state.mjs';
|
|
18
|
+
import { createSession, lastSession, buildResumeContext } from '../session-store.mjs';
|
|
19
|
+
import { listObservations, searchObservations } from '../observation-store.mjs';
|
|
20
|
+
import { countEntities } from '../entity-store.mjs';
|
|
21
|
+
import { logHookFailure } from './_lib/log.mjs';
|
|
15
22
|
|
|
16
23
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
24
|
const CONSTRUCT_BIN = resolve(MODULE_DIR, '..', '..', 'bin', 'construct');
|
|
18
25
|
|
|
19
26
|
let input = {};
|
|
20
|
-
try { input = JSON.parse(readFileSync(0, 'utf8')); }
|
|
27
|
+
try { input = JSON.parse(readFileSync(0, 'utf8')); }
|
|
28
|
+
catch (err) { logHookFailure({ hook: 'session-start', err, phase: 'parse' }); process.exit(0); }
|
|
29
|
+
|
|
30
|
+
// Merge config.env into process.env so provider source hints and embed status
|
|
31
|
+
// reflect the operator's actual configuration, not just the shell environment.
|
|
32
|
+
try {
|
|
33
|
+
const merged = loadConstructEnv({ warn: false });
|
|
34
|
+
for (const [k, v] of Object.entries(merged)) {
|
|
35
|
+
if (!(k in process.env)) process.env[k] = v;
|
|
36
|
+
}
|
|
37
|
+
} catch { /* best effort — non-blocking */ }
|
|
21
38
|
|
|
22
39
|
const cwd = input?.cwd || process.cwd();
|
|
23
40
|
const projectName = cwd.split('/').pop() || 'project';
|
|
24
41
|
const date = new Date().toISOString().slice(0, 10);
|
|
25
42
|
|
|
26
|
-
//
|
|
43
|
+
// Tiered injection model:
|
|
44
|
+
// Tier 1 — always inject (header, branch, status)
|
|
45
|
+
// Tier 2 — inject only when fresh and meaningful (context.md if <7d old,
|
|
46
|
+
// skill scope, recent drops)
|
|
47
|
+
// Tier 3 — surface as a one-line hint pointing at an MCP tool
|
|
48
|
+
// (memory_recent, efficiency_snapshot) instead of injecting the
|
|
49
|
+
// full payload every session.
|
|
50
|
+
const FRESHNESS_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
51
|
+
function fileIsFresh(path) {
|
|
52
|
+
try {
|
|
53
|
+
if (!existsSync(path)) return false;
|
|
54
|
+
return Date.now() - statSync(path).mtimeMs < FRESHNESS_WINDOW_MS;
|
|
55
|
+
} catch { return false; }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Load context — project-local first, global fallback. Gate by 7-day freshness:
|
|
59
|
+
// stale context files inject only the digest line as fallback (Tier 2 → Tier 1
|
|
60
|
+
// degradation), full body only when modified within the freshness window.
|
|
27
61
|
const localCtx = join(cwd, '.cx', 'context.md');
|
|
28
62
|
const globalCtx = join(homedir(), '.cx', 'context.md');
|
|
29
63
|
let context = '';
|
|
64
|
+
let contextStale = false;
|
|
30
65
|
const localState = readContextState(cwd);
|
|
31
66
|
const globalState = localState ? null : readContextState(homedir());
|
|
32
67
|
const stateContext = localState || globalState;
|
|
68
|
+
const stateContextPath = localState ? localCtx : globalCtx;
|
|
33
69
|
if (stateContext?.markdown) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
70
|
+
if (fileIsFresh(stateContextPath) || !existsSync(stateContextPath)) {
|
|
71
|
+
const raw = String(stateContext.markdown);
|
|
72
|
+
context = raw.length > 1800 ? `${raw.slice(0, 400)}\n…\n${raw.slice(-1400)}` : raw;
|
|
73
|
+
} else {
|
|
74
|
+
contextStale = true;
|
|
75
|
+
}
|
|
76
|
+
} else if (existsSync(localCtx) && fileIsFresh(localCtx)) {
|
|
37
77
|
try { const raw = readFileSync(localCtx, 'utf8'); context = raw.length > 1800 ? `${raw.slice(0, 400)}\n…\n${raw.slice(-1400)}` : raw; } catch { /* best effort */ }
|
|
38
|
-
} else if (existsSync(globalCtx)) {
|
|
78
|
+
} else if (existsSync(globalCtx) && fileIsFresh(globalCtx)) {
|
|
39
79
|
try { const raw = readFileSync(globalCtx, 'utf8'); context = raw.length > 1800 ? `${raw.slice(0, 400)}\n…\n${raw.slice(-1400)}` : raw; } catch { /* best effort */ }
|
|
80
|
+
} else if (existsSync(localCtx) || existsSync(globalCtx)) {
|
|
81
|
+
contextStale = true;
|
|
40
82
|
}
|
|
41
83
|
const contextSummary = contextSummaryLine(stateContext);
|
|
42
84
|
|
|
@@ -52,6 +94,68 @@ try {
|
|
|
52
94
|
recentCommits = execSync(`git -C "${cwd}" log --oneline -3 2>/dev/null`, { timeout: 5000 }).toString().trim();
|
|
53
95
|
} catch { /* best effort */ }
|
|
54
96
|
|
|
97
|
+
|
|
98
|
+
// Session persistence — create session and load last for resume context.
|
|
99
|
+
// Cap resume note at 600 chars; the full record is available via session_load.
|
|
100
|
+
let sessionResumeNote = '';
|
|
101
|
+
try {
|
|
102
|
+
createSession(cwd, { project: projectName, platform: 'claude-code' });
|
|
103
|
+
const prev = lastSession(cwd, projectName);
|
|
104
|
+
if (prev && prev.status !== 'active') {
|
|
105
|
+
const resumeCtx = buildResumeContext(prev);
|
|
106
|
+
if (resumeCtx) {
|
|
107
|
+
const capped = resumeCtx.length > 600
|
|
108
|
+
? resumeCtx.slice(0, 580) + '\n…(truncated — call `session_load` for full record)'
|
|
109
|
+
: resumeCtx;
|
|
110
|
+
sessionResumeNote = '\n## Last session context\n' + capped + '\n';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch { /* best effort — session store is non-blocking */ }
|
|
114
|
+
|
|
115
|
+
// Tier 2 — inject top observations that are relevant to the current project.
|
|
116
|
+
// Filter out trivial placeholder summaries. Show at most 2 inline; rest are
|
|
117
|
+
// available on demand via memory_search / memory_recent.
|
|
118
|
+
// Skipped when CONSTRUCT_MEMORY=off (enables A/B comparison with memory disabled).
|
|
119
|
+
let observationsNote = '';
|
|
120
|
+
try {
|
|
121
|
+
if (process.env.CONSTRUCT_MEMORY !== 'off') {
|
|
122
|
+
const PLACEHOLDER_RE = /^(implement|done|completed|session completed|in_progress)[\s:.]*(done|ok|completed)?$/i;
|
|
123
|
+
const searched = searchObservations(cwd, projectName, { project: projectName, limit: 10 });
|
|
124
|
+
const allObs = searched.length > 0 ? searched : listObservations(cwd, { project: projectName, limit: 20 });
|
|
125
|
+
const meaningful = allObs.filter((o) =>
|
|
126
|
+
o.summary && o.summary.length > 10 && !PLACEHOLDER_RE.test(o.summary.trim())
|
|
127
|
+
);
|
|
128
|
+
const entityCount = countEntities(cwd, { project: projectName });
|
|
129
|
+
|
|
130
|
+
if (meaningful.length > 0) {
|
|
131
|
+
const top2 = meaningful.slice(0, 2).map((o) => `- [${o.category}] ${o.summary}`).join('\n');
|
|
132
|
+
const rest = meaningful.length > 2
|
|
133
|
+
? ` · ${meaningful.length - 2} more via \`memory_search\``
|
|
134
|
+
: '';
|
|
135
|
+
const entitySuffix = entityCount > 0 ? ` · ${entityCount} entit${entityCount === 1 ? 'y' : 'ies'} tracked` : '';
|
|
136
|
+
observationsNote = `\n## Prior observations\n${top2}\n${meaningful.length} total${rest}${entitySuffix}\n`;
|
|
137
|
+
} else {
|
|
138
|
+
const total = allObs.length;
|
|
139
|
+
if (total > 0) {
|
|
140
|
+
const entitySuffix = entityCount > 0 ? ` · ${entityCount} entit${entityCount === 1 ? 'y' : 'ies'} tracked` : '';
|
|
141
|
+
observationsNote = `\n## Prior observations\n${total} observation${total === 1 ? '' : 's'} available — \`memory_recent\` for recency order · \`memory_search\` for semantic lookup${entitySuffix}.\n`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} // end CONSTRUCT_MEMORY check
|
|
145
|
+
} catch { /* best effort */ }
|
|
146
|
+
|
|
147
|
+
// Record memory injection stats for the Stop hook to persist into .cx/memory-stats.jsonl.
|
|
148
|
+
try {
|
|
149
|
+
const memStatsPath = join(homedir(), '.cx', 'session-memory-stats.json');
|
|
150
|
+
const injected = observationsNote ? (observationsNote.match(/^- /gm) || []).length : 0;
|
|
151
|
+
writeFileSync(memStatsPath, JSON.stringify({
|
|
152
|
+
project: projectName,
|
|
153
|
+
observationsInjected: injected,
|
|
154
|
+
memoryEnabled: process.env.CONSTRUCT_MEMORY !== 'off',
|
|
155
|
+
at: new Date().toISOString(),
|
|
156
|
+
}));
|
|
157
|
+
} catch { /* best effort */ }
|
|
158
|
+
|
|
55
159
|
// Pending typecheck warning
|
|
56
160
|
const tcPath = join(homedir(), '.cx', 'pending-typecheck.txt');
|
|
57
161
|
let pendingNote = '';
|
|
@@ -68,28 +172,183 @@ const recentLine = recentCommits
|
|
|
68
172
|
? `Recent: ${recentCommits.split('\n').join(' · ')}`
|
|
69
173
|
: '';
|
|
70
174
|
|
|
71
|
-
|
|
175
|
+
// Working branch — show prominently at session start so it is visible
|
|
176
|
+
// before any mutating operation is even proposed.
|
|
177
|
+
let workingBranch = '(unknown)';
|
|
178
|
+
try {
|
|
179
|
+
workingBranch = execSync(`git -C "${cwd}" rev-parse --abbrev-ref HEAD 2>/dev/null`, { timeout: 5000 }).toString().trim() || '(unknown)';
|
|
180
|
+
} catch { /* not a git repo */ }
|
|
181
|
+
// Branch + project anchor mutating operations; the read-only/destructive
|
|
182
|
+
// policy lives in CLAUDE.md and the persona, so the prelude stays compact.
|
|
183
|
+
|
|
184
|
+
const header = `## Resuming — ${projectName} · ${date}
|
|
185
|
+
## Working branch: **${workingBranch}**`;
|
|
72
186
|
const footer = [statusLine, recentLine].filter(Boolean).join(' · ');
|
|
73
187
|
|
|
74
|
-
|
|
188
|
+
const body = context || (contextStale
|
|
189
|
+
? '## Stale context — last context.md update >7 days old. Run `construct context refresh` or rely on `memory_recent`.\n'
|
|
190
|
+
: '## Fresh start — no prior context found.\n');
|
|
191
|
+
// Tier 3 hint — efficiency snapshot fires only when status is 'degraded'
|
|
192
|
+
// (repeated-read ratio or byte threshold exceeded). 'configured' (large reads
|
|
193
|
+
// but within budget) is advisory and not worth a session-start interrupt.
|
|
194
|
+
const efficiency = buildCompactEfficiencyDigest(readEfficiencyLog(homedir()));
|
|
195
|
+
const efficiencyNote = (efficiency?.compact && efficiency?.status === 'degraded')
|
|
196
|
+
? `\n## Session efficiency\n${efficiency.compact}\nFull snapshot via \`efficiency_snapshot\` tool.\n`
|
|
197
|
+
: '';
|
|
198
|
+
// Context digest is redundant when the full context.md body was already
|
|
199
|
+
// rendered above. Only surface the digest line as a *fallback* when no
|
|
200
|
+
// context body was available — it's a shorter substitute, not an addition.
|
|
201
|
+
const stateNote = (contextSummary && !context) ? `\n## Context digest\n${contextSummary}\n` : '';
|
|
202
|
+
|
|
203
|
+
// Single-line coordination reminder. The full rule (one writer per file,
|
|
204
|
+
// coordinate via tracker + plan.md) is in the persona prompt.
|
|
205
|
+
|
|
206
|
+
const concurrencyNote = '\nCoordination: one writer per file — see tracker and `plan.md` for ownership.\n';
|
|
207
|
+
|
|
208
|
+
// Tier 3 hint — skill scope lists can be large (20+ entries). The model
|
|
209
|
+
// doesn't need the full list on every session; a one-line hint is enough to
|
|
210
|
+
// surface that a classification exists. Full list via `construct skills scope`.
|
|
211
|
+
let skillScopeNote = '';
|
|
75
212
|
try {
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
213
|
+
const profilePath = join(cwd, '.cx', 'skills-profile.json');
|
|
214
|
+
if (existsSync(profilePath)) {
|
|
215
|
+
const profile = JSON.parse(readFileSync(profilePath, 'utf8'));
|
|
216
|
+
const irrelevant = profile?.recommendedDisable || profile?.decision?.irrelevant || [];
|
|
217
|
+
const tags = profile?.profile?.tags || [];
|
|
218
|
+
if (irrelevant.length > 0) {
|
|
219
|
+
skillScopeNote =
|
|
220
|
+
'\n## Project skill scope\n' +
|
|
221
|
+
`Stack: ${tags.join(', ') || 'unspecified'} · ${irrelevant.length} installed skill${irrelevant.length === 1 ? '' : 's'} out of scope — run \`construct skills scope\` for the full list.\n`;
|
|
222
|
+
}
|
|
82
223
|
}
|
|
83
|
-
} catch {
|
|
84
|
-
|
|
85
|
-
|
|
224
|
+
} catch { /* best effort */ }
|
|
225
|
+
|
|
226
|
+
// Recent drop-zone files — surface very recent downloads so the user can
|
|
227
|
+
// ingest them with `construct drop` instead of manually referencing paths.
|
|
228
|
+
// Honors CONSTRUCT_DROP_DIRS; default watches Downloads/Desktop/Documents.
|
|
229
|
+
let dropNote = '';
|
|
230
|
+
try {
|
|
231
|
+
const { collectCandidates } = await import('../drop.mjs');
|
|
232
|
+
const { homedir } = await import('node:os');
|
|
233
|
+
const { join: pathJoin } = await import('node:path');
|
|
234
|
+
const { existsSync: fsExists } = await import('node:fs');
|
|
235
|
+
const watchDirs = (process.env.CONSTRUCT_DROP_DIRS
|
|
236
|
+
? process.env.CONSTRUCT_DROP_DIRS.split(':').map((s) => s.trim()).filter(Boolean)
|
|
237
|
+
: [pathJoin(homedir(), 'Downloads'), pathJoin(homedir(), 'Desktop'), pathJoin(homedir(), 'Documents')]
|
|
238
|
+
).filter((d) => fsExists(d));
|
|
239
|
+
const recent = collectCandidates({
|
|
240
|
+
dirs: watchDirs,
|
|
241
|
+
sinceMs: 60 * 60 * 1000, // 1 hour — only very recent drops
|
|
242
|
+
limit: 3,
|
|
243
|
+
});
|
|
244
|
+
if (recent.length > 0) {
|
|
245
|
+
const items = recent.map((r) => `- ${r.name} (${r.ext || 'file'}, ${Math.round((Date.now() - r.mtimeMs) / 60000)}m ago)`).join('\n');
|
|
246
|
+
dropNote = '\n## Recent drop-zone files\n' +
|
|
247
|
+
'Files saved in the last hour that you may want to reference:\n' +
|
|
248
|
+
items + '\n' +
|
|
249
|
+
'Run `construct drop` to ingest the most recent, or `construct drop --list` to see more.\n';
|
|
250
|
+
}
|
|
251
|
+
} catch { /* best effort */ }
|
|
252
|
+
|
|
253
|
+
// Single-line recall trigger so Construct-specific questions go through
|
|
254
|
+
// `knowledge_search` instead of training-data recall. Full policy in persona.
|
|
255
|
+
|
|
256
|
+
const selfKnowledgeNote = '\nSelf-knowledge: call `knowledge_search` for any Construct-specific question.\n';
|
|
257
|
+
|
|
258
|
+
// Configured provider sources hint — tell Construct what repos/projects are
|
|
259
|
+
// wired so it knows to call `provider_fetch` instead of saying "no context".
|
|
260
|
+
let sourcesNote = '';
|
|
261
|
+
let embedStatusNote = '';
|
|
262
|
+
try {
|
|
263
|
+
const repos = (process.env.GITHUB_REPOS ?? '').split(',').map(r => r.trim()).filter(Boolean);
|
|
264
|
+
const jiraConfigured = !!(process.env.JIRA_BASE_URL);
|
|
265
|
+
const linearConfigured = !!(process.env.LINEAR_API_KEY);
|
|
266
|
+
const hasSources = repos.length > 0 || jiraConfigured || linearConfigured;
|
|
267
|
+
if (hasSources) {
|
|
268
|
+
const parts = [];
|
|
269
|
+
if (repos.length > 0) parts.push(`GitHub repos: ${repos.join(', ')}`);
|
|
270
|
+
if (jiraConfigured) parts.push(`Jira: ${process.env.JIRA_BASE_URL}`);
|
|
271
|
+
if (linearConfigured) parts.push('Linear: configured');
|
|
272
|
+
// Surface which sources are wired so `provider_fetch` is the obvious
|
|
273
|
+
// tool for questions about them. The "fetch first, don't answer from
|
|
274
|
+
// memory" policy lives in the persona.
|
|
275
|
+
sourcesNote = '\nProvider sources wired: ' + parts.join(' · ') +
|
|
276
|
+
'. Use `provider_fetch` for any question about them.\n';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Embed daemon status — surface as a one-liner so operator always knows state
|
|
280
|
+
const { resolveEmbedStatus, autoStartEmbedIfNeeded } = await import('../embed/cli.mjs');
|
|
281
|
+
const embedStatus = resolveEmbedStatus(process.env);
|
|
282
|
+
if (embedStatus.level !== 'none') {
|
|
283
|
+
embedStatusNote = `\n## Embed daemon\n${embedStatus.label} · ${embedStatus.detail}\n`;
|
|
284
|
+
// Auto-start if CX_AUTO_EMBED=1 and daemon is stopped
|
|
285
|
+
if (embedStatus.level === 'stopped' && process.env.CX_AUTO_EMBED === '1') {
|
|
286
|
+
const result = await autoStartEmbedIfNeeded(process.env);
|
|
287
|
+
if (result.started) {
|
|
288
|
+
embedStatusNote = `\n## Embed daemon\nembed: auto-started (pid ${result.pid}) · background polling active\n`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
} catch { /* best effort */ }
|
|
293
|
+
|
|
294
|
+
// Role framework: drain queued events into bd issues and surface pending
|
|
295
|
+
// invocations. Skipped when CONSTRUCT_ROLES=off; bounded so session start
|
|
296
|
+
// stays fast.
|
|
297
|
+
|
|
298
|
+
let rolesNote = '';
|
|
299
|
+
try {
|
|
300
|
+
if (process.env.CONSTRUCT_ROLES !== 'off') {
|
|
301
|
+
const { processBacklog, listPending } = await import('../roles/gateway.mjs');
|
|
302
|
+
await processBacklog({ sinceMs: 60 * 60 * 1000, maxProcess: 5 });
|
|
303
|
+
const pendingRoles = listPending({ unresolved: true });
|
|
304
|
+
if (pendingRoles.length > 0) {
|
|
305
|
+
const lines = pendingRoles.slice(-5).map(
|
|
306
|
+
(p) => `- ${p.cxId} · ${p.bdIssueId || '(no-bd)'} · ${p.eventType} — ${p.summary || ''}`
|
|
307
|
+
);
|
|
308
|
+
rolesNote = `\n## Pending role invocations (${pendingRoles.length})\n${lines.join('\n')}\nDispatch via Task: see \`construct role latest\` for the brief.\n`;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
} catch { /* best effort */ }
|
|
312
|
+
|
|
313
|
+
// R&D intake queue + MCP broker status. Same builders feed the opencode
|
|
314
|
+
// plugin so both platforms surface the same prelude on session start.
|
|
315
|
+
|
|
316
|
+
let intakeReviewNote = '';
|
|
317
|
+
let brokerStatusNote = '';
|
|
318
|
+
try {
|
|
319
|
+
const { buildIntakePrelude, buildBrokerStatusLine } = await import('../intake/session-prelude.mjs');
|
|
320
|
+
intakeReviewNote = buildIntakePrelude({ cwd, env: process.env });
|
|
321
|
+
brokerStatusNote = `\n${buildBrokerStatusLine({ env: process.env })}\n`;
|
|
322
|
+
} catch { /* best effort */ }
|
|
323
|
+
|
|
324
|
+
// Permission posture: surface gaps in ~/.claude/settings.json permissions.allow
|
|
325
|
+
// so the agent can ask the user to close them instead of stumbling into the
|
|
326
|
+
// classifier mid-task. Empty string when no gaps — keeps the banner small.
|
|
327
|
+
|
|
328
|
+
let permissionPostureNote = '';
|
|
329
|
+
try {
|
|
330
|
+
const { buildPermissionPostureLine } = await import('../claude-allow.mjs');
|
|
331
|
+
permissionPostureNote = buildPermissionPostureLine({ cwd });
|
|
332
|
+
} catch { /* best effort */ }
|
|
333
|
+
|
|
334
|
+
process.stdout.write(`${header}\n${body}${stateNote}${efficiencyNote}${observationsNote}${concurrencyNote}${skillScopeNote}${dropNote}${embedStatusNote}${sourcesNote}${selfKnowledgeNote}${rolesNote}${intakeReviewNote}${brokerStatusNote}${permissionPostureNote}\n${footer}${pendingNote}\n`);
|
|
335
|
+
// Auto-bootstrap the policy-engine gate. session-start has just emitted
|
|
336
|
+
// branch + recent commits + prior observations + context-state — that is
|
|
337
|
+
// already grounding; mark the session bootstrapped so the PreToolUse rule
|
|
338
|
+
// stops asking for redundant ceremony before the first mutating tool call.
|
|
339
|
+
try {
|
|
340
|
+
const sessionId = input?.session_id || input?.sessionId;
|
|
341
|
+
if (sessionId) {
|
|
342
|
+
const bootstrapPath = join(homedir(), ".cx", "bootstrap-state.json");
|
|
343
|
+
let state = {};
|
|
344
|
+
try { state = JSON.parse(readFileSync(bootstrapPath, "utf8")) || {}; } catch {}
|
|
345
|
+
const now = Date.now();
|
|
346
|
+
for (const [k, v] of Object.entries(state)) {
|
|
347
|
+
if (!v?.ts || now - v.ts > 24 * 60 * 60 * 1000) delete state[k];
|
|
348
|
+
}
|
|
349
|
+
state[sessionId] = { ts: now, reads: 0, bootstrap: ["session-start"], done: true };
|
|
350
|
+
try { writeFileSync(bootstrapPath, JSON.stringify(state)); } catch {}
|
|
351
|
+
}
|
|
352
|
+
} catch {}
|
|
86
353
|
|
|
87
|
-
const body = context || '## Fresh start — no prior context found.\n';
|
|
88
|
-
const efficiency = buildCompactEfficiencyDigest(readEfficiencyLog(homedir()));
|
|
89
|
-
const efficiencyNote = efficiency?.compact
|
|
90
|
-
? `\n## Session efficiency\n${efficiency.compact}\n`
|
|
91
|
-
: '';
|
|
92
|
-
const stateNote = contextSummary ? `\n## Context digest\n${contextSummary}\n` : '';
|
|
93
|
-
const memoryDirective = `\n## Required first actions\nBefore responding: call memory_search("${projectName}") to retrieve prior session context and user preferences. Apply any relevant results immediately. If context is degraded, compact before broad rereads.\n`;
|
|
94
|
-
process.stdout.write(`${header}\n${body}${stateNote}${workflowNote}${efficiencyNote}${memoryDirective}\n${footer}${pendingNote}\n`);
|
|
95
354
|
process.exit(0);
|