@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,542 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/beads-automation.mjs — Automation around beads issue tracking.
|
|
3
|
+
*
|
|
4
|
+
* 1. Sync plan.md with bead status changes
|
|
5
|
+
* 2. Auto‑create handoffs when beads change state
|
|
6
|
+
* 3. Track verification notes
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import { spawnSync } from 'node:child_process';
|
|
13
|
+
|
|
14
|
+
import { acquireMergeSlot, releaseMergeSlot, runBd, runBdJson } from './beads-client.mjs';
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Constants
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
const PLAN_MD_PATH = 'plan.md';
|
|
21
|
+
const HANDOFFS_DIR = '.cx/handoffs';
|
|
22
|
+
const CONFIRM_TIMEOUT = 60000; // 60 second timeout for confirmation prompts
|
|
23
|
+
const POSITIVE_INTENT = /^(y|yes|yeah|yep|yup|sure|ok|okay|go|proceed|do it)\b/i;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Confirmation Utility
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Ask the user for confirmation before proceeding with a critical operation.
|
|
31
|
+
* Follows established patterns from the codebase.
|
|
32
|
+
* @param {string} message - The message to display to the user
|
|
33
|
+
* @param {Object} options - Options for the confirmation
|
|
34
|
+
* @param {boolean} options.yes - If true, skip confirmation (--yes flag)
|
|
35
|
+
* @param {string} options.default - Default answer ('y' or 'n')
|
|
36
|
+
* @returns {Promise<boolean>} True if confirmed, false otherwise
|
|
37
|
+
*/
|
|
38
|
+
export async function confirm(message, { yes = false, default: defaultAnswer = 'n' } = {}) {
|
|
39
|
+
if (yes) {
|
|
40
|
+
console.error(`[beads] Confirmation skipped: --yes flag provided`);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!process.stdin.isTTY) {
|
|
45
|
+
console.error(`[beads] Non-TTY environment detected. Proceeding with operation.`);
|
|
46
|
+
console.error(`[beads] To explicitly confirm, run with --yes flag.`);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { stdin, stdout } = process;
|
|
51
|
+
const defaultMarker = defaultAnswer === 'y' ? '[Y/n]' : '[y/N]';
|
|
52
|
+
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
let timeoutId;
|
|
55
|
+
const cleanup = () => {
|
|
56
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
57
|
+
stdin.pause();
|
|
58
|
+
stdin.removeListener('data', onData);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const onData = (chunk) => {
|
|
62
|
+
cleanup();
|
|
63
|
+
const answer = String(chunk).trim();
|
|
64
|
+
if (answer === '') {
|
|
65
|
+
resolve(defaultAnswer === 'y');
|
|
66
|
+
} else {
|
|
67
|
+
resolve(POSITIVE_INTENT.test(answer));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
timeoutId = setTimeout(() => {
|
|
72
|
+
cleanup();
|
|
73
|
+
console.error('\n[beads] Confirmation timeout. Operation cancelled.');
|
|
74
|
+
resolve(false);
|
|
75
|
+
}, CONFIRM_TIMEOUT);
|
|
76
|
+
|
|
77
|
+
stdin.setEncoding('utf8');
|
|
78
|
+
stdin.resume();
|
|
79
|
+
stdin.on('data', onData);
|
|
80
|
+
|
|
81
|
+
stdout.write(`[beads] ${message} ${defaultMarker} `);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// Git Operations
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
|
|
89
|
+
function runGit(args, { cwd = process.cwd() } = {}) {
|
|
90
|
+
const result = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
91
|
+
if (result.status !== 0) {
|
|
92
|
+
throw new Error(`git ${args.join(' ')} failed: ${result.stderr || result.stdout}`);
|
|
93
|
+
}
|
|
94
|
+
return result.stdout.trim();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function getGitStatus({ cwd = process.cwd() } = {}) {
|
|
98
|
+
try {
|
|
99
|
+
return runGit(['status', '--porcelain'], { cwd });
|
|
100
|
+
} catch {
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getGitDiffStat({ cwd = process.cwd() } = {}) {
|
|
106
|
+
try {
|
|
107
|
+
return runGit(['diff', '--stat'], { cwd });
|
|
108
|
+
} catch {
|
|
109
|
+
return '';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getStagedDiffStat({ cwd = process.cwd() } = {}) {
|
|
114
|
+
try {
|
|
115
|
+
return runGit(['diff', '--cached', '--stat'], { cwd });
|
|
116
|
+
} catch {
|
|
117
|
+
return '';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function confirmAndCommit({
|
|
122
|
+
message,
|
|
123
|
+
beadId,
|
|
124
|
+
cwd = process.cwd(),
|
|
125
|
+
yes = false,
|
|
126
|
+
} = {}) {
|
|
127
|
+
const status = getGitStatus({ cwd });
|
|
128
|
+
const staged = getStagedDiffStat({ cwd });
|
|
129
|
+
|
|
130
|
+
if (!status && !staged) {
|
|
131
|
+
console.error('[beads] No changes to commit');
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.error('');
|
|
136
|
+
console.error('[beads] Proposed commit:');
|
|
137
|
+
console.error('═══════════════════════════════════════════════');
|
|
138
|
+
console.error(message);
|
|
139
|
+
console.error('═══════════════════════════════════════════════');
|
|
140
|
+
|
|
141
|
+
if (staged) {
|
|
142
|
+
console.error('[beads] Staged changes:');
|
|
143
|
+
console.error(staged);
|
|
144
|
+
}
|
|
145
|
+
if (status) {
|
|
146
|
+
console.error('[beads] Unstaged changes:');
|
|
147
|
+
console.error(status);
|
|
148
|
+
}
|
|
149
|
+
console.error('');
|
|
150
|
+
|
|
151
|
+
const confirmed = await confirm(
|
|
152
|
+
'Commit these changes?',
|
|
153
|
+
{ yes, default: 'n' }
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (!confirmed) {
|
|
157
|
+
console.error('[beads] Commit cancelled');
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
runGit(['commit', '-m', message], { cwd });
|
|
162
|
+
console.error('[beads] Committed');
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Plan.md sync
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Find all bead IDs mentioned in plan.md and their current status.
|
|
172
|
+
* Returns [ [lineNumber, construct-xxx, currentStatus], ... ]
|
|
173
|
+
*/
|
|
174
|
+
export function extractBeadsFromPlan(planContent) {
|
|
175
|
+
const lines = planContent.split('\n');
|
|
176
|
+
const beadLines = [];
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < lines.length; i++) {
|
|
179
|
+
const line = lines[i];
|
|
180
|
+
|
|
181
|
+
// Look for construct-xxx patterns
|
|
182
|
+
const beadMatches = line.match(/(construct-[a-z0-9]+)/g);
|
|
183
|
+
if (!beadMatches) continue;
|
|
184
|
+
|
|
185
|
+
// Parse table row status (if in a table)
|
|
186
|
+
let status = 'unknown';
|
|
187
|
+
const tableMatch = line.match(/\|.*\|.*\|.*\|\s*([^|]+)\s*\|/);
|
|
188
|
+
if (tableMatch) {
|
|
189
|
+
const statusCell = tableMatch[1].trim();
|
|
190
|
+
if (statusCell.match(/^(done|in_progress|pending)$/i)) {
|
|
191
|
+
status = statusCell.toLowerCase();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Look for P0/P1/P2/P3 as well
|
|
196
|
+
const priorityMatch = line.match(/\bP([0-3])\b/);
|
|
197
|
+
const priority = priorityMatch ? `P${priorityMatch[1]}` : null;
|
|
198
|
+
|
|
199
|
+
for (const beadId of beadMatches) {
|
|
200
|
+
beadLines.push({
|
|
201
|
+
line: i + 1,
|
|
202
|
+
content: line,
|
|
203
|
+
beadId,
|
|
204
|
+
status,
|
|
205
|
+
priority,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return beadLines;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Update plan.md based on actual bead status.
|
|
215
|
+
* Returns true if plan.md changed, false otherwise.
|
|
216
|
+
*/
|
|
217
|
+
export async function syncPlanWithBeads({ cwd = process.cwd(), dryRun = false } = {}) {
|
|
218
|
+
const planPath = path.join(cwd, PLAN_MD_PATH);
|
|
219
|
+
if (!fs.existsSync(planPath)) {
|
|
220
|
+
console.error(`[beads] No ${PLAN_MD_PATH} found in ${cwd}`);
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const content = fs.readFileSync(planPath, 'utf8');
|
|
225
|
+
const beadLines = extractBeadsFromPlan(content);
|
|
226
|
+
if (!beadLines.length) return false;
|
|
227
|
+
|
|
228
|
+
const lines = content.split('\n');
|
|
229
|
+
let changed = false;
|
|
230
|
+
|
|
231
|
+
for (const bl of beadLines) {
|
|
232
|
+
const { beadId, line: lineIndex } = bl;
|
|
233
|
+
try {
|
|
234
|
+
// Get current bead status
|
|
235
|
+
const bead = await runBdJson(['show', beadId], { cwd, silent: true });
|
|
236
|
+
const actualStatus = bead?.status || 'unknown';
|
|
237
|
+
|
|
238
|
+
// Update line if needed
|
|
239
|
+
const currentLine = lines[lineIndex - 1];
|
|
240
|
+
|
|
241
|
+
// Table format: | task | status | notes |
|
|
242
|
+
if (currentLine.includes('|') && currentLine.includes(beadId)) {
|
|
243
|
+
// Find status column (assume second column for simplicity)
|
|
244
|
+
const parts = currentLine.split('|').map(p => p.trim());
|
|
245
|
+
if (parts.length >= 3) {
|
|
246
|
+
const oldStatus = parts[2].trim();
|
|
247
|
+
if (oldStatus.toLowerCase() !== actualStatus.toLowerCase()) {
|
|
248
|
+
parts[2] = ` ${actualStatus} `;
|
|
249
|
+
const newLine = `|${parts.slice(1).join('|')}|`;
|
|
250
|
+
lines[lineIndex - 1] = newLine;
|
|
251
|
+
changed = true;
|
|
252
|
+
console.error(`[beads] Updated ${beadId}: ${oldStatus} → ${actualStatus}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// Inline format: - construct-xxx (in progress)
|
|
257
|
+
else {
|
|
258
|
+
const statusMatch = currentLine.match(/\(([^)]+)\)/);
|
|
259
|
+
const oldStatus = statusMatch ? statusMatch[1].trim() : '';
|
|
260
|
+
if (oldStatus !== actualStatus) {
|
|
261
|
+
const newLine = currentLine.replace(
|
|
262
|
+
/\([^)]*\)/,
|
|
263
|
+
`(${actualStatus})`
|
|
264
|
+
) || `${currentLine} (${actualStatus})`;
|
|
265
|
+
lines[lineIndex - 1] = newLine;
|
|
266
|
+
changed = true;
|
|
267
|
+
console.error(`[beads] Updated ${beadId}: ${oldStatus} → ${actualStatus}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error(`[beads] Could not fetch ${beadId}: ${error.message}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (changed && !dryRun) {
|
|
276
|
+
fs.writeFileSync(planPath, lines.join('\n'), 'utf8');
|
|
277
|
+
console.error(`[beads] Updated ${PLAN_MD_PATH}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return changed;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
// Handoff automation
|
|
285
|
+
// ---------------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Create a handoff file for active beads.
|
|
289
|
+
* Returns the path to the created handoff.
|
|
290
|
+
*/
|
|
291
|
+
export async function createHandoff({
|
|
292
|
+
beads = [],
|
|
293
|
+
actor = 'construct',
|
|
294
|
+
summary = 'Session handoff',
|
|
295
|
+
decisions = [],
|
|
296
|
+
nextActions = [],
|
|
297
|
+
risks = [],
|
|
298
|
+
doNotTouch = [],
|
|
299
|
+
cwd = process.cwd(),
|
|
300
|
+
}) {
|
|
301
|
+
const date = new Date().toISOString().split('T')[0];
|
|
302
|
+
const slug = summary
|
|
303
|
+
.toLowerCase()
|
|
304
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
305
|
+
.replace(/^-|-$/g, '')
|
|
306
|
+
.slice(0, 50);
|
|
307
|
+
|
|
308
|
+
const handoffFileName = `${date}-${slug}.md`;
|
|
309
|
+
const handoffDir = path.join(cwd, HANDOFFS_DIR);
|
|
310
|
+
const handoffPath = path.join(handoffDir, handoffFileName);
|
|
311
|
+
|
|
312
|
+
fs.mkdirSync(handoffDir, { recursive: true });
|
|
313
|
+
|
|
314
|
+
// Gather bead details
|
|
315
|
+
const beadDetails = [];
|
|
316
|
+
for (const beadId of beads) {
|
|
317
|
+
try {
|
|
318
|
+
const bead = await runBdJson(['show', beadId], { cwd, silent: true });
|
|
319
|
+
beadDetails.push({
|
|
320
|
+
id: beadId,
|
|
321
|
+
subject: bead.subject || 'unknown',
|
|
322
|
+
status: bead.status || 'unknown',
|
|
323
|
+
priority: bead.priority || 'P3',
|
|
324
|
+
summary: `${beadId}: ${bead.subject} (${bead.status}, ${bead.priority})`,
|
|
325
|
+
});
|
|
326
|
+
} catch {
|
|
327
|
+
beadDetails.push({ id: beadId, summary: `${beadId}: unknown` });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Build markdown
|
|
332
|
+
const parts = [
|
|
333
|
+
`# Handoff: ${summary}`,
|
|
334
|
+
'',
|
|
335
|
+
`Date: ${date}`,
|
|
336
|
+
`Actor: ${actor}`,
|
|
337
|
+
beads.length ? `Beads: ${beads.join(', ')}` : null,
|
|
338
|
+
'',
|
|
339
|
+
'## Current state',
|
|
340
|
+
'',
|
|
341
|
+
...beadDetails.map(b => `- ${b.summary}`),
|
|
342
|
+
beadDetails.length === 0 ? '(No beads active)' : '',
|
|
343
|
+
'',
|
|
344
|
+
decisions.length ? '## Recent decisions' : '',
|
|
345
|
+
...decisions.map(d => d.includes('|') ? d : `- ${d}`),
|
|
346
|
+
'',
|
|
347
|
+
nextActions.length ? '## Next actions' : '',
|
|
348
|
+
...nextActions.map(a => a.includes('|') ? a : `1. ${a}`),
|
|
349
|
+
'',
|
|
350
|
+
risks.length ? '## Risks' : '',
|
|
351
|
+
...risks.map(r => r.includes('|') ? r : `- ${r}`),
|
|
352
|
+
'',
|
|
353
|
+
doNotTouch.length ? '## Do not touch' : '',
|
|
354
|
+
...doNotTouch.map(f => f.includes('|') ? f : `- ${f}`),
|
|
355
|
+
'',
|
|
356
|
+
'## Verification',
|
|
357
|
+
'',
|
|
358
|
+
`- Session completed by ${actor}`,
|
|
359
|
+
`- Beads status synced with ${PLAN_MD_PATH}`,
|
|
360
|
+
beads.length ? `- Handoff attached to ${beads.length} bead(s)` : '',
|
|
361
|
+
].filter(Boolean);
|
|
362
|
+
|
|
363
|
+
const content = parts.join('\n');
|
|
364
|
+
fs.writeFileSync(handoffPath, content, 'utf8');
|
|
365
|
+
|
|
366
|
+
// Attach handoff to each bead as a note
|
|
367
|
+
for (const beadId of beads) {
|
|
368
|
+
try {
|
|
369
|
+
spawnSync('bd', ['note', beadId, '--message', `Handoff: ${handoffFileName}`], {
|
|
370
|
+
cwd,
|
|
371
|
+
stdio: 'ignore',
|
|
372
|
+
});
|
|
373
|
+
} catch {
|
|
374
|
+
// Ignore note failures
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
console.error(`[beads] Handoff written to ${path.relative(cwd, handoffPath)}`);
|
|
379
|
+
return handoffPath;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Auto-create handoff based on beads that were worked on this session.
|
|
384
|
+
* Scans `bd list` for beads that were touched within the last hour.
|
|
385
|
+
*/
|
|
386
|
+
export async function autoCreateHandoff({ actor = 'unknown', cwd = process.cwd() } = {}) {
|
|
387
|
+
try {
|
|
388
|
+
const beads = await runBdJson(['list', '--json'], { cwd, silent: true });
|
|
389
|
+
const oneHourAgo = new Date(Date.now() - 3600 * 1000).toISOString();
|
|
390
|
+
|
|
391
|
+
const recent = beads.filter(b => {
|
|
392
|
+
const updated = b.updated_at || b.created_at;
|
|
393
|
+
return updated && new Date(updated) > oneHourAgo;
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
if (!recent.length) {
|
|
397
|
+
console.error('[beads] No recent beads to create handoff for');
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const beadIds = recent.slice(0, 5).map(b => b.id);
|
|
402
|
+
const summary = `Session for ${recent[0].subject?.split(' ')[0] || 'work'}`;
|
|
403
|
+
|
|
404
|
+
const decisions = [
|
|
405
|
+
`Used beads client with locking to avoid conflicts`,
|
|
406
|
+
recent.length > 1 ? `Worked on ${recent.length} beads` : 'Focused on single bead',
|
|
407
|
+
];
|
|
408
|
+
|
|
409
|
+
const nextActions = [
|
|
410
|
+
'Continue with next ready bead from `bd ready`',
|
|
411
|
+
'Sync plan.md after bead status changes',
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
const risks = [
|
|
415
|
+
'Parallel agent access still requires merge-slot coordination for batch operations',
|
|
416
|
+
];
|
|
417
|
+
|
|
418
|
+
const handoffPath = await createHandoff({
|
|
419
|
+
beads: beadIds,
|
|
420
|
+
actor,
|
|
421
|
+
summary,
|
|
422
|
+
decisions,
|
|
423
|
+
nextActions,
|
|
424
|
+
risks,
|
|
425
|
+
cwd,
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
return handoffPath;
|
|
429
|
+
} catch (error) {
|
|
430
|
+
console.error(`[beads] Failed to auto‑create handoff: ${error.message}`);
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// ---------------------------------------------------------------------------
|
|
436
|
+
// Hooks
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Post‑bead‑update hook that syncs plan.md if bead status changed.
|
|
441
|
+
* Call this after `bd update`, `bd close`, etc.
|
|
442
|
+
*/
|
|
443
|
+
export async function postBeadUpdateHook(beadId, { cwd = process.cwd() } = {}) {
|
|
444
|
+
try {
|
|
445
|
+
const changed = await syncPlanWithBeads({ cwd, dryRun: false });
|
|
446
|
+
if (changed) {
|
|
447
|
+
console.error(`[beads] plan.md updated for ${beadId}`);
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error(`[beads] Plan sync failed for ${beadId}: ${error.message}`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* End‑of‑session hook.
|
|
456
|
+
*/
|
|
457
|
+
export async function endOfSessionHook({ actor = 'unknown', cwd = process.cwd(), yes = false } = {}) {
|
|
458
|
+
console.error('[beads] Running end‑of‑session hooks…');
|
|
459
|
+
|
|
460
|
+
await syncPlanWithBeads({ cwd });
|
|
461
|
+
|
|
462
|
+
const handoffPath = await autoCreateHandoff({ actor, cwd });
|
|
463
|
+
|
|
464
|
+
let mergeSlotHeld = false;
|
|
465
|
+
try {
|
|
466
|
+
const mergeSlot = await acquireMergeSlot({ cwd, actor });
|
|
467
|
+
mergeSlotHeld = Boolean(mergeSlot?.success);
|
|
468
|
+
|
|
469
|
+
const confirmed = await confirm(
|
|
470
|
+
'About to push beads changes to remote. Continue?',
|
|
471
|
+
{ yes, default: 'n' }
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (!confirmed) {
|
|
475
|
+
console.error('[beads] Beads push cancelled by user');
|
|
476
|
+
return handoffPath;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
await runBd(['dolt', 'push'], { cwd, silent: true, actor });
|
|
480
|
+
console.error('[beads] Beads changes pushed');
|
|
481
|
+
} catch (pushError) {
|
|
482
|
+
console.error(`[beads] dolt push failed: ${pushError.message}`);
|
|
483
|
+
} finally {
|
|
484
|
+
if (mergeSlotHeld) {
|
|
485
|
+
await releaseMergeSlot({ cwd, actor });
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return handoffPath;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// ---------------------------------------------------------------------------
|
|
493
|
+
// CLI entry point (for direct invocation)
|
|
494
|
+
// ---------------------------------------------------------------------------
|
|
495
|
+
|
|
496
|
+
export async function runBeadsAutomationCli(args) {
|
|
497
|
+
const sub = args[0] || 'status';
|
|
498
|
+
const subArgs = args.slice(1);
|
|
499
|
+
const cwd = process.cwd();
|
|
500
|
+
const yes = subArgs.includes('--yes') || subArgs.includes('-y');
|
|
501
|
+
|
|
502
|
+
if (sub === 'sync-plan') {
|
|
503
|
+
const dryRun = subArgs.includes('--dry-run');
|
|
504
|
+
const changed = await syncPlanWithBeads({ cwd, dryRun });
|
|
505
|
+
console.log(changed ? 'plan.md updated' : 'plan.md unchanged');
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (sub === 'create-handoff') {
|
|
510
|
+
const actor = process.env.USER || 'unknown';
|
|
511
|
+
const beads = subArgs.filter(arg => !arg.startsWith('--'));
|
|
512
|
+
const summary = beads.length ? `Working on ${beads[0]}` : 'Session handoff';
|
|
513
|
+
|
|
514
|
+
const handoffPath = await createHandoff({
|
|
515
|
+
beads,
|
|
516
|
+
actor,
|
|
517
|
+
summary,
|
|
518
|
+
cwd,
|
|
519
|
+
});
|
|
520
|
+
console.log(`Created: ${handoffPath}`);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (sub === 'end-session') {
|
|
525
|
+
const actor = process.env.USER || 'unknown';
|
|
526
|
+
const handoffPath = await endOfSessionHook({ actor, cwd, yes });
|
|
527
|
+
if (handoffPath) {
|
|
528
|
+
console.log(`Session ended. Handoff: ${handoffPath}`);
|
|
529
|
+
} else {
|
|
530
|
+
console.log('Session ended (no handoff generated)');
|
|
531
|
+
}
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
console.error('Usage:');
|
|
536
|
+
console.error(' sync-plan Sync plan.md with bead status');
|
|
537
|
+
console.error(' create-handoff Create a handoff for given beads');
|
|
538
|
+
console.error(' end-session Run all end‑of‑session hooks');
|
|
539
|
+
console.error('');
|
|
540
|
+
console.error('Options:');
|
|
541
|
+
console.error(' --yes, -y Skip confirmation prompts (for automation)');
|
|
542
|
+
}
|