@geraldmaron/construct 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -18
- package/bin/construct +392 -91
- package/bin/construct-postinstall.mjs +30 -2
- package/examples/distribution/README.md +42 -0
- package/examples/distribution/manifest.json +53 -0
- package/examples/distribution/sources/adr.md +84 -0
- package/examples/distribution/sources/deck-one-pager.md +65 -0
- package/examples/distribution/sources/prd-platform.md +161 -0
- package/examples/distribution/sources/research-brief.md +80 -0
- package/examples/distribution/sources/rfc-platform.md +82 -0
- package/examples/distribution/sources/runbook.md +103 -0
- package/examples/distribution/sources/strategy.md +88 -0
- package/lib/adapters-sync.mjs +67 -0
- package/lib/artifact-gate-notice.mjs +38 -0
- package/lib/artifact-manifest.mjs +112 -0
- package/lib/artifact-release-gate.mjs +171 -0
- package/lib/artifact-reviewers.mjs +68 -0
- package/lib/artifact-type-from-path.mjs +79 -0
- package/lib/audit-skills.mjs +4 -0
- package/lib/audit-specialists.mjs +285 -0
- package/lib/audit-trail.mjs +77 -10
- package/lib/auto-docs.mjs +87 -18
- package/lib/beads/drift.mjs +27 -6
- package/lib/beads-client.mjs +49 -121
- package/lib/beads-optimistic.mjs +5 -12
- package/lib/brand-fonts.mjs +93 -0
- package/lib/brand-prose.mjs +214 -0
- package/lib/brand-tokens.mjs +92 -0
- package/lib/bridges/copilot-proxy.mjs +13 -26
- package/lib/capability-ledger.mjs +156 -0
- package/lib/certification/artifact-fixtures.mjs +132 -0
- package/lib/certification/artifact-gates.mjs +63 -0
- package/lib/certification/artifact-provenance.mjs +97 -0
- package/lib/certification/canonical-scenarios.mjs +78 -0
- package/lib/certification/cli.mjs +191 -0
- package/lib/certification/dashboard-api.mjs +71 -0
- package/lib/certification/demo-parity.mjs +116 -0
- package/lib/certification/document-io-fixtures.mjs +246 -0
- package/lib/certification/document-workflow.mjs +97 -0
- package/lib/certification/eval-bridge.mjs +77 -0
- package/lib/certification/model-routing.mjs +149 -0
- package/lib/certification/prompt-budget.mjs +119 -0
- package/lib/certification/rc-gate.mjs +206 -0
- package/lib/certification/real-llm-scenarios.mjs +303 -0
- package/lib/certification/role-cards.mjs +113 -0
- package/lib/certification/role-overlays.mjs +117 -0
- package/lib/certification/run.mjs +122 -0
- package/lib/certification/runner.mjs +323 -0
- package/lib/certification/scenarios.mjs +51 -0
- package/lib/certification/skill-inventory.mjs +289 -0
- package/lib/certification/skill-scenarios.mjs +147 -0
- package/lib/certification/specialist-contracts.mjs +85 -0
- package/lib/certification/specialist-scenarios.mjs +175 -0
- package/lib/certification/stale-impact.mjs +146 -0
- package/lib/certification/status.mjs +252 -0
- package/lib/certification/store.mjs +77 -0
- package/lib/chat/cli.mjs +333 -0
- package/lib/chat/command-suggest.mjs +161 -0
- package/lib/chat/commands.mjs +215 -0
- package/lib/chat/config.mjs +142 -0
- package/lib/chat/context-compactor.mjs +250 -0
- package/lib/chat/context-continuation.mjs +253 -0
- package/lib/chat/continuation-source.mjs +58 -0
- package/lib/chat/demo-guide.mjs +61 -0
- package/lib/chat/design-tokens.mjs +91 -0
- package/lib/chat/desktop-binary.mjs +79 -0
- package/lib/chat/desktop-build.mjs +130 -0
- package/lib/chat/desktop-launcher.mjs +133 -0
- package/lib/chat/evidence.mjs +145 -0
- package/lib/chat/export.mjs +74 -0
- package/lib/chat/harness/driver.mjs +91 -0
- package/lib/chat/list-picker.mjs +112 -0
- package/lib/chat/model-picker.mjs +356 -0
- package/lib/chat/openrouter-fallback.mjs +151 -0
- package/lib/chat/permission-prompt.mjs +33 -0
- package/lib/chat/picker-catalog.mjs +45 -0
- package/lib/chat/policy-telemetry.mjs +34 -0
- package/lib/chat/present.mjs +245 -0
- package/lib/chat/session-context.mjs +39 -0
- package/lib/chat/session-persist.mjs +73 -0
- package/lib/chat/session-restore.mjs +71 -0
- package/lib/chat/session-settings.mjs +53 -0
- package/lib/chat/system-prompt.mjs +52 -0
- package/lib/chat/transparency.mjs +93 -0
- package/lib/chat/tui/color-scheme.mjs +42 -0
- package/lib/chat/tui/markdown.mjs +123 -0
- package/lib/chat/tui/presentation.mjs +100 -0
- package/lib/chat/tui/render.mjs +500 -0
- package/lib/chat/tui/turn-block.mjs +284 -0
- package/lib/chat/tui/turn-present.mjs +18 -0
- package/lib/chat/tui/turn-state.mjs +88 -0
- package/lib/chat/tui/usage.mjs +122 -0
- package/lib/chat/web-commands.mjs +146 -0
- package/lib/chat/web-launcher.mjs +63 -0
- package/lib/chat/web-picker-keys.mjs +46 -0
- package/lib/chat/web-session.mjs +159 -0
- package/lib/cli-commands.mjs +232 -11
- package/lib/comment-lint.mjs +6 -4
- package/lib/config/schema.mjs +36 -5
- package/lib/contract-schemas/decision.json +50 -0
- package/lib/contract-schemas/implementation.json +51 -0
- package/lib/contract-schemas/review-report.json +32 -0
- package/lib/contract-schemas/test-report.json +43 -0
- package/lib/contracts/construct-handoff.mjs +60 -0
- package/lib/contracts/validate.mjs +32 -3
- package/lib/contracts/violation-log.mjs +58 -5
- package/lib/dashboard-demo.mjs +71 -0
- package/lib/dashboard-static.mjs +19 -5
- package/lib/decisions/registry.mjs +5 -3
- package/lib/deck-export-pptx.mjs +1152 -0
- package/lib/demo-recording.mjs +142 -0
- package/lib/demo-script.mjs +114 -0
- package/lib/demo-surface.mjs +249 -0
- package/lib/demo.mjs +703 -0
- package/lib/diagram-export.mjs +192 -0
- package/lib/diagram.mjs +302 -0
- package/lib/docs-verify.mjs +25 -7
- package/lib/doctor/index.mjs +4 -2
- package/lib/doctor/project-adapters.mjs +44 -0
- package/lib/doctor/watchers/cx-budget.mjs +98 -0
- package/lib/doctor/watchers/graph-staleness.mjs +52 -0
- package/lib/doctor/watchers/process-pressure.mjs +4 -3
- package/lib/doctor/watchers/service-health.mjs +1 -1
- package/lib/document-export.mjs +360 -36
- package/lib/document-extract.mjs +88 -0
- package/lib/document-ingest.mjs +32 -18
- package/lib/embed/cli.mjs +2 -2
- package/lib/embed/daemon.mjs +18 -46
- package/lib/embed/semantic.mjs +5 -2
- package/lib/embed/worker.mjs +2 -2
- package/lib/embedded-contract/model-resolve.mjs +10 -9
- package/lib/env-config.mjs +26 -0
- package/lib/evals/dataset.mjs +137 -0
- package/lib/evals/gates.mjs +175 -0
- package/lib/graph/build-co-change.mjs +54 -0
- package/lib/graph/build-from-registry.mjs +136 -0
- package/lib/graph/build-import-graph.mjs +153 -0
- package/lib/graph/cli.mjs +110 -0
- package/lib/graph/impact-cli.mjs +89 -0
- package/lib/graph/impact.mjs +108 -0
- package/lib/graph/staleness.mjs +44 -0
- package/lib/graph/store.mjs +172 -0
- package/lib/handoffs/cleanup.mjs +1 -1
- package/lib/health-check.mjs +90 -76
- package/lib/hooks/artifact-release-gate.mjs +43 -0
- package/lib/hooks/audit-trail.mjs +14 -28
- package/lib/hooks/brand-prose-lint.mjs +38 -0
- package/lib/hooks/graph-impact-advisory.mjs +62 -0
- package/lib/hooks/session-optimize.mjs +84 -207
- package/lib/hooks/session-start.mjs +4 -5
- package/lib/host-disposition.mjs +7 -0
- package/lib/improvement/cli.mjs +189 -0
- package/lib/improvement/controller.mjs +137 -0
- package/lib/improvement/proposal.mjs +120 -0
- package/lib/improvement/specialist-loop.mjs +192 -0
- package/lib/improvement/store.mjs +89 -0
- package/lib/improvement/surface.mjs +219 -0
- package/lib/ingest-tooling.mjs +97 -0
- package/lib/init/doc-lanes.mjs +165 -0
- package/lib/init-docs.mjs +31 -178
- package/lib/init-unified.mjs +27 -97
- package/lib/init-update-guide.mjs +102 -0
- package/lib/init-update.mjs +32 -1
- package/lib/init.mjs +8 -0
- package/lib/install/desktop-binary-download.mjs +85 -0
- package/lib/install/legacy-global-cleanup.mjs +189 -0
- package/lib/intake/daemon.mjs +2 -0
- package/lib/intake/git-queue.mjs +9 -8
- package/lib/intake/queue.mjs +2 -1
- package/lib/intake/session-prelude.mjs +90 -1
- package/lib/libreoffice-export.mjs +97 -0
- package/lib/logging/rotate.mjs +9 -0
- package/lib/maintenance/docker-reclaim.mjs +206 -0
- package/lib/mcp/external-schema-cost.mjs +74 -0
- package/lib/mcp/server.mjs +84 -9
- package/lib/mcp/stdio-mcp-probe.mjs +188 -0
- package/lib/mcp/tool-budget.mjs +55 -4
- package/lib/mcp/tools/document.mjs +18 -4
- package/lib/mcp/tools/skills.mjs +32 -3
- package/lib/mcp/tools/workflow.mjs +13 -1
- package/lib/model-free-selector.mjs +18 -0
- package/lib/model-registry.mjs +13 -229
- package/lib/model-router.mjs +425 -93
- package/lib/models/behavior-matrix.mjs +289 -0
- package/lib/models/catalog.mjs +209 -0
- package/lib/models/execution-capability-profile.mjs +196 -0
- package/lib/models/execution-policy.mjs +307 -0
- package/lib/models/provider-poll.mjs +383 -0
- package/lib/npm-spawn-env.mjs +17 -0
- package/lib/ollama/installed-models.mjs +129 -0
- package/lib/oracle/actions.mjs +187 -0
- package/lib/oracle/artifact-gate.mjs +99 -0
- package/lib/oracle/cli.mjs +204 -0
- package/lib/oracle/daemon-entry.mjs +14 -0
- package/lib/oracle/dispatch.mjs +81 -0
- package/lib/oracle/execute.mjs +143 -0
- package/lib/oracle/gaps.mjs +76 -0
- package/lib/oracle/index.mjs +84 -0
- package/lib/oracle/issues.mjs +164 -0
- package/lib/oracle/org-graph.mjs +170 -0
- package/lib/oracle/policy.mjs +80 -0
- package/lib/oracle/read-model.mjs +398 -0
- package/lib/oracle/reconcile.mjs +191 -0
- package/lib/oracle/routing.mjs +89 -0
- package/lib/oracle/synthesize.mjs +384 -0
- package/lib/oracle/verdicts.mjs +51 -0
- package/lib/orchestration/worker.mjs +88 -27
- package/lib/orchestration-policy.mjs +50 -0
- package/lib/parity.mjs +96 -2
- package/lib/persona-sections.mjs +66 -0
- package/lib/playwright-demo.mjs +252 -0
- package/lib/project-init-shared.mjs +4 -1
- package/lib/prompt-composer.js +9 -4
- package/lib/prompt-validation-contract.mjs +24 -0
- package/lib/provider-capabilities.js +57 -11
- package/lib/providers/contract/adapters/confluence/index.mjs +181 -0
- package/lib/providers/contract/adapters/git/index.mjs +115 -0
- package/lib/providers/contract/adapters/github/index.mjs +166 -0
- package/lib/providers/contract/adapters/jira/index.mjs +187 -0
- package/lib/providers/contract/adapters/slack/index.mjs +175 -0
- package/lib/providers/contract/contract-tests.mjs +57 -0
- package/lib/providers/contract/errors.mjs +48 -0
- package/lib/providers/contract/interface.mjs +50 -0
- package/lib/providers/contract/registry.mjs +102 -0
- package/lib/providers/copilot-auth.mjs +297 -0
- package/lib/providers/credential-bootstrap.mjs +178 -0
- package/lib/providers/credential-catalog.mjs +46 -0
- package/lib/providers/credential-sources.mjs +63 -0
- package/lib/providers/creds.mjs +5 -2
- package/lib/providers/op-run.mjs +59 -0
- package/lib/providers/secret-resolver.mjs +159 -0
- package/lib/publish-template.mjs +163 -0
- package/lib/publish-tooling.mjs +119 -0
- package/lib/publish.mjs +305 -0
- package/lib/registry/cli.mjs +82 -0
- package/lib/registry/consolidation.mjs +147 -0
- package/lib/registry/generate-docs.mjs +75 -0
- package/lib/registry/skill-verification.mjs +57 -0
- package/lib/registry/surface-map.mjs +76 -0
- package/lib/registry/validate.mjs +135 -0
- package/lib/resources/budget.mjs +82 -0
- package/lib/resources/process-budget.mjs +45 -0
- package/lib/rules-delivery.mjs +9 -2
- package/lib/rules-read.mjs +26 -0
- package/lib/runtime-env.mjs +23 -0
- package/lib/runtime-pressure.mjs +51 -7
- package/lib/schema-infer.mjs +13 -2
- package/lib/server/chat-loop.mjs +622 -0
- package/lib/server/demo-preview.mjs +63 -0
- package/lib/server/index.mjs +229 -8
- package/lib/server/langfuse-login.mjs +3 -3
- package/lib/service-manager.mjs +61 -9
- package/lib/session-store.mjs +1 -1
- package/lib/setup.mjs +102 -2
- package/lib/specialists/prompt-schema.mjs +19 -10
- package/lib/specialists/roster.mjs +43 -0
- package/lib/specialists/scaffold.mjs +56 -0
- package/lib/storage/backend.mjs +6 -6
- package/lib/storage/embeddings-local.mjs +6 -3
- package/lib/storage/file-lock.mjs +18 -11
- package/lib/storage/hybrid-query.mjs +7 -4
- package/lib/storage/state-source.mjs +5 -5
- package/lib/storage/sync.mjs +2 -3
- package/lib/telemetry/rule-calls.mjs +52 -0
- package/lib/template-registry.mjs +2 -2
- package/lib/templates/visual-requirements.mjs +27 -51
- package/lib/test-corpus-inventory.mjs +313 -0
- package/lib/uninstall/uninstall.mjs +19 -7
- package/lib/update.mjs +12 -0
- package/lib/upgrade.mjs +14 -0
- package/lib/wireframe.mjs +20 -14
- package/lib/worker/run.mjs +17 -4
- package/lib/worker/trace.mjs +11 -3
- package/package.json +29 -13
- package/personas/construct.md +13 -13
- package/platforms/claude/settings.template.json +36 -0
- package/rules/common/patterns.md +1 -1
- package/rules/common/release-gates.md +4 -3
- package/scripts/sync-specialists.mjs +187 -60
- package/skills/devops/data-engineering.md +1 -1
- package/skills/docs/adr-workflow.md +1 -0
- package/skills/docs/backlog-proposal-workflow.md +1 -0
- package/skills/docs/codebase-research-workflow.md +40 -0
- package/skills/docs/customer-profile-workflow.md +1 -0
- package/skills/docs/document-ingest-workflow.md +1 -0
- package/skills/docs/evidence-ingest-workflow.md +1 -0
- package/skills/docs/init-docs.md +2 -2
- package/skills/docs/init-project.md +8 -3
- package/skills/docs/prd-workflow.md +24 -1
- package/skills/docs/prfaq-workflow.md +1 -0
- package/skills/docs/product-intelligence-workflow.md +1 -0
- package/skills/docs/product-signal-workflow.md +1 -0
- package/skills/docs/research-workflow.md +54 -37
- package/skills/docs/runbook-workflow.md +1 -0
- package/skills/docs/strategy-workflow.md +1 -0
- package/skills/docs/user-research-workflow.md +40 -0
- package/skills/operating/orchestration-reference.md +1 -1
- package/skills/roles/architect.md +5 -0
- package/skills/roles/operator.docs.md +4 -0
- package/skills/routing.md +4 -2
- package/specialists/artifact-manifest.json +480 -0
- package/specialists/artifact-manifest.schema.json +53 -0
- package/specialists/audit-enrichments.json +454 -0
- package/specialists/contracts.json +37 -25
- package/specialists/prompts/_shared/validation-contract.md +26 -0
- package/specialists/prompts/cx-accessibility.md +21 -2
- package/specialists/prompts/cx-ai-engineer.md +24 -1
- package/specialists/prompts/cx-architect.md +4 -1
- package/specialists/prompts/cx-business-strategist.md +23 -2
- package/specialists/prompts/cx-data-analyst.md +22 -1
- package/specialists/prompts/cx-data-engineer.md +23 -2
- package/specialists/prompts/cx-debugger.md +21 -2
- package/specialists/prompts/cx-designer.md +26 -3
- package/specialists/prompts/cx-devil-advocate.md +19 -2
- package/specialists/prompts/cx-docs-keeper.md +28 -1
- package/specialists/prompts/cx-engineer.md +22 -1
- package/specialists/prompts/cx-evaluator.md +18 -1
- package/specialists/prompts/cx-explorer.md +21 -2
- package/specialists/prompts/cx-legal-compliance.md +21 -2
- package/specialists/prompts/cx-operations.md +21 -2
- package/specialists/prompts/cx-oracle.md +94 -0
- package/specialists/prompts/cx-orchestrator.md +20 -1
- package/specialists/prompts/cx-platform-engineer.md +25 -2
- package/specialists/prompts/cx-product-manager.md +32 -1
- package/specialists/prompts/cx-qa.md +24 -2
- package/specialists/prompts/cx-rd-lead.md +23 -2
- package/specialists/prompts/cx-release-manager.md +23 -2
- package/specialists/prompts/cx-researcher.md +22 -2
- package/specialists/prompts/cx-reviewer.md +18 -1
- package/specialists/prompts/cx-security.md +22 -2
- package/specialists/prompts/cx-sre.md +30 -3
- package/specialists/prompts/cx-test-automation.md +7 -1
- package/specialists/prompts/cx-trace-reviewer.md +22 -3
- package/specialists/prompts/cx-ux-researcher.md +21 -2
- package/specialists/registry.json +52 -173
- package/specialists/tone-profiles.json +42 -0
- package/templates/demos/playwright/demo-recording.config.mjs +47 -0
- package/templates/demos/recordings/agentic-platforms-prd.json +19 -0
- package/templates/demos/scripts/agentic-platforms-prd.json +44 -0
- package/templates/demos/specs/_helpers/scroll-artifact.ts +89 -0
- package/templates/demos/tapes/agentic-platforms-prd.tape +49 -0
- package/templates/demos/tapes/resource-guard-rails.tape +49 -0
- package/templates/demos/vhs/construct-cockpit.json +24 -0
- package/templates/distribution/construct-brand.typ +446 -0
- package/templates/distribution/construct-decision.typ +38 -0
- package/templates/distribution/construct-deck.html +95 -0
- package/templates/distribution/construct-pdf.typ +38 -0
- package/templates/distribution/construct-prd.typ +38 -0
- package/templates/distribution/construct-research.typ +38 -0
- package/templates/distribution/construct-web.html +92 -0
- package/templates/distribution/fonts/Geist-Bold.ttf +0 -0
- package/templates/distribution/fonts/Geist-Medium.ttf +0 -0
- package/templates/distribution/fonts/Geist-Regular.ttf +0 -0
- package/templates/distribution/fonts/Geist-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/GeistMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Medium.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-Regular.ttf +0 -0
- package/templates/distribution/fonts/JetBrainsMono-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Bold.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Medium.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-Regular.ttf +0 -0
- package/templates/distribution/fonts/PlusJakartaSans-SemiBold.ttf +0 -0
- package/templates/distribution/fonts/README.md +36 -0
- package/templates/distribution/fonts/SpaceGrotesk-Variable.ttf +0 -0
- package/templates/distribution/fonts/handwritten/Caveat.ttf +0 -0
- package/templates/distribution/fonts/legacy/IBMPlexMono-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Medium.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/Inter-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/InterDisplay-SemiBold.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Regular.otf +0 -0
- package/templates/distribution/fonts/legacy/SourceSerif4-Semibold.otf +0 -0
- package/templates/distribution/icons/activity.svg +15 -0
- package/templates/distribution/icons/alert-triangle.svg +17 -0
- package/templates/distribution/icons/book-open.svg +16 -0
- package/templates/distribution/icons/bot.svg +20 -0
- package/templates/distribution/icons/brain.svg +22 -0
- package/templates/distribution/icons/circle-check.svg +16 -0
- package/templates/distribution/icons/clipboard-check.svg +17 -0
- package/templates/distribution/icons/cpu.svg +28 -0
- package/templates/distribution/icons/database.svg +17 -0
- package/templates/distribution/icons/eye.svg +16 -0
- package/templates/distribution/icons/file-text.svg +19 -0
- package/templates/distribution/icons/gauge.svg +16 -0
- package/templates/distribution/icons/git-branch.svg +17 -0
- package/templates/distribution/icons/key.svg +17 -0
- package/templates/distribution/icons/layers.svg +17 -0
- package/templates/distribution/icons/list-checks.svg +19 -0
- package/templates/distribution/icons/lock.svg +16 -0
- package/templates/distribution/icons/message-square.svg +15 -0
- package/templates/distribution/icons/network.svg +19 -0
- package/templates/distribution/icons/route.svg +17 -0
- package/templates/distribution/icons/scale.svg +19 -0
- package/templates/distribution/icons/search.svg +16 -0
- package/templates/distribution/icons/send.svg +16 -0
- package/templates/distribution/icons/server.svg +18 -0
- package/templates/distribution/icons/shield-check.svg +16 -0
- package/templates/distribution/icons/users.svg +18 -0
- package/templates/distribution/icons/webhook.svg +17 -0
- package/templates/distribution/icons/workflow.svg +17 -0
- package/templates/distribution/icons/wrench.svg +15 -0
- package/templates/distribution/run.mjs +18 -1
- package/templates/docs/adr.md +7 -0
- package/templates/docs/construct_guide.md +10 -10
- package/templates/docs/customer-profile.md +4 -0
- package/templates/docs/one-pager.md +4 -0
- package/templates/docs/postmortem.md +31 -0
- package/templates/docs/prd-platform.md +19 -0
- package/templates/docs/prd.md +15 -0
- package/templates/docs/prfaq.md +7 -0
- package/templates/docs/qa-strategy.md +103 -0
- package/templates/docs/research-brief.md +8 -0
- package/templates/docs/rfc-platform.md +12 -0
- package/templates/docs/test-plan.md +7 -0
- package/lib/ingest/chunker.mjs +0 -94
- package/lib/ingest/pipeline.mjs +0 -53
- package/lib/ingest/store.mjs +0 -82
- package/lib/mode-commands.mjs +0 -122
- package/lib/policy/unified-gates.mjs +0 -96
- package/lib/profiles/validate-custom.mjs +0 -114
- package/lib/services/telemetry-backend.mjs +0 -177
- package/lib/storage/fusion.mjs +0 -95
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/registry/surface-map.mjs — ADR-0039 primary interaction surface per command group.
|
|
3
|
+
*
|
|
4
|
+
* Declarative map from CLI command name to primary surface tier. The CLI registry
|
|
5
|
+
* remains the substrate; this file governs discovery posture without removing verbs.
|
|
6
|
+
*
|
|
7
|
+
* Surfaces:
|
|
8
|
+
* agent-mcp — MCP tool canonical; CLI --json twin
|
|
9
|
+
* thin-cli — human types this at a prompt
|
|
10
|
+
* tui — interactive loop emphasized
|
|
11
|
+
* dashboard — visual/telemetry emphasized
|
|
12
|
+
* internal — CI/harness only
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const SURFACE_TIERS = ['agent-mcp', 'thin-cli', 'tui', 'dashboard', 'internal'];
|
|
16
|
+
|
|
17
|
+
/** @type {Record<string, string>} */
|
|
18
|
+
export const COMMAND_SURFACE = {
|
|
19
|
+
install: 'thin-cli',
|
|
20
|
+
init: 'thin-cli',
|
|
21
|
+
dev: 'thin-cli',
|
|
22
|
+
dashboard: 'thin-cli',
|
|
23
|
+
stop: 'thin-cli',
|
|
24
|
+
status: 'thin-cli',
|
|
25
|
+
doctor: 'thin-cli',
|
|
26
|
+
sync: 'thin-cli',
|
|
27
|
+
ingest: 'agent-mcp',
|
|
28
|
+
drop: 'agent-mcp',
|
|
29
|
+
distill: 'agent-mcp',
|
|
30
|
+
ask: 'agent-mcp',
|
|
31
|
+
search: 'agent-mcp',
|
|
32
|
+
knowledge: 'agent-mcp',
|
|
33
|
+
memory: 'agent-mcp',
|
|
34
|
+
reflect: 'agent-mcp',
|
|
35
|
+
intake: 'tui',
|
|
36
|
+
workflow: 'agent-mcp',
|
|
37
|
+
graph: 'agent-mcp',
|
|
38
|
+
capability: 'agent-mcp',
|
|
39
|
+
execution: 'agent-mcp',
|
|
40
|
+
orchestrate: 'agent-mcp',
|
|
41
|
+
models: 'agent-mcp',
|
|
42
|
+
profile: 'tui',
|
|
43
|
+
sandbox: 'tui',
|
|
44
|
+
review: 'dashboard',
|
|
45
|
+
telemetry: 'dashboard',
|
|
46
|
+
evals: 'dashboard',
|
|
47
|
+
improvement: 'dashboard',
|
|
48
|
+
ci: 'thin-cli',
|
|
49
|
+
docs: 'thin-cli',
|
|
50
|
+
export: 'agent-mcp',
|
|
51
|
+
diagram: 'agent-mcp',
|
|
52
|
+
demo: 'agent-mcp',
|
|
53
|
+
beads: 'thin-cli',
|
|
54
|
+
hook: 'internal',
|
|
55
|
+
'lint:comments': 'internal',
|
|
56
|
+
'lint:agents': 'internal',
|
|
57
|
+
'registry:status': 'internal',
|
|
58
|
+
'registry:validate': 'internal',
|
|
59
|
+
'registry:generate-docs': 'internal',
|
|
60
|
+
rules: 'thin-cli',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function surfaceForCommand(name) {
|
|
64
|
+
if (COMMAND_SURFACE[name]) return COMMAND_SURFACE[name];
|
|
65
|
+
if (name.includes(':')) return 'internal';
|
|
66
|
+
return 'thin-cli';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function commandsBySurface(commands) {
|
|
70
|
+
const grouped = Object.fromEntries(SURFACE_TIERS.map((t) => [t, []]));
|
|
71
|
+
for (const cmd of commands) {
|
|
72
|
+
const surface = cmd.surface ?? surfaceForCommand(cmd.name);
|
|
73
|
+
grouped[surface]?.push(cmd.name);
|
|
74
|
+
}
|
|
75
|
+
return grouped;
|
|
76
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/registry/validate.mjs — validate registry/capabilities.json against repo reality.
|
|
3
|
+
*
|
|
4
|
+
* Checks that declared skills, templates, workflow types, and verification paths
|
|
5
|
+
* resolve on disk; flags stale lastValidated entries; ensures every embedded
|
|
6
|
+
* workflow type has a registry row. Consumed by `construct registry validate`
|
|
7
|
+
* and `construct doctor`.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
import { WORKFLOW_TYPES } from '../embedded-contract/workflow-defs.mjs';
|
|
15
|
+
import { STRUCTURE_REQUIREMENTS } from '../templates/visual-requirements.mjs';
|
|
16
|
+
|
|
17
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
19
|
+
const REGISTRY_PATH = path.join(REPO_ROOT, 'registry', 'capabilities.json');
|
|
20
|
+
const STALE_MS = 90 * 24 * 60 * 60 * 1000;
|
|
21
|
+
|
|
22
|
+
function exists(rel) {
|
|
23
|
+
return fs.existsSync(path.join(REPO_ROOT, rel));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveSkillPath(skillId) {
|
|
27
|
+
return path.join(REPO_ROOT, 'skills', `${skillId}.md`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveTemplate(name) {
|
|
31
|
+
const shipped = path.join(REPO_ROOT, 'templates', 'docs', `${name}.md`);
|
|
32
|
+
if (exists(path.relative(REPO_ROOT, shipped))) return true;
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function loadCapabilityRegistry({ rootDir = REPO_ROOT } = {}) {
|
|
37
|
+
const file = path.join(rootDir, 'registry', 'capabilities.json');
|
|
38
|
+
if (!fs.existsSync(file)) {
|
|
39
|
+
return { version: 0, capabilities: [], error: 'registry/capabilities.json missing' };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return { version: 0, capabilities: [], error: err?.message || String(err) };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function validateCapabilityRegistry({ rootDir = REPO_ROOT, now = Date.now() } = {}) {
|
|
49
|
+
const raw = loadCapabilityRegistry({ rootDir });
|
|
50
|
+
const errors = [];
|
|
51
|
+
const warnings = [];
|
|
52
|
+
const ids = new Set();
|
|
53
|
+
|
|
54
|
+
if (raw.error) errors.push(raw.error);
|
|
55
|
+
|
|
56
|
+
for (const cap of raw.capabilities ?? []) {
|
|
57
|
+
if (!cap.id) {
|
|
58
|
+
errors.push('capability missing id');
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (ids.has(cap.id)) errors.push(`duplicate capability id: ${cap.id}`);
|
|
62
|
+
ids.add(cap.id);
|
|
63
|
+
|
|
64
|
+
if (cap.skill && !fs.existsSync(resolveSkillPath(cap.skill))) {
|
|
65
|
+
errors.push(`${cap.id}: skill file missing: skills/${cap.skill}.md`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (cap.embeddedWorkflow && !WORKFLOW_TYPES.includes(cap.embeddedWorkflow)) {
|
|
69
|
+
errors.push(`${cap.id}: unknown embeddedWorkflow "${cap.embeddedWorkflow}"`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (cap.template && !resolveTemplate(cap.template)) {
|
|
73
|
+
errors.push(`${cap.id}: template missing: templates/docs/${cap.template}.md`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ver = cap.verification ?? {};
|
|
77
|
+
if (ver.functional && !exists(ver.functional)) {
|
|
78
|
+
errors.push(`${cap.id}: verification.functional missing: ${ver.functional}`);
|
|
79
|
+
}
|
|
80
|
+
if (ver.hostEmulation && !exists(ver.hostEmulation)) {
|
|
81
|
+
errors.push(`${cap.id}: verification.hostEmulation missing: ${ver.hostEmulation}`);
|
|
82
|
+
}
|
|
83
|
+
if (ver.structureRequirements && !STRUCTURE_REQUIREMENTS[ver.structureRequirements]) {
|
|
84
|
+
errors.push(`${cap.id}: unknown structureRequirements "${ver.structureRequirements}"`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const hasTest = ver.functional || ver.hostEmulation || ver.untestableRationale;
|
|
88
|
+
if (!hasTest && (cap.criticality === 'P0' || cap.criticality === 'P1')) {
|
|
89
|
+
warnings.push(`${cap.id}: P0/P1 capability has no functional test or untestableRationale`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const surfaces = cap.surfaces ?? {};
|
|
93
|
+
for (const [surface, status] of Object.entries(surfaces)) {
|
|
94
|
+
if (!status?.supported) continue;
|
|
95
|
+
if (status.primary) {
|
|
96
|
+
const tierPath = path.join('tests', 'capabilities', cap.id, `${surface}.test.mjs`);
|
|
97
|
+
const hasSurfaceTest = exists(tierPath);
|
|
98
|
+
const hasFunctional = !!(ver.functional || ver.hostEmulation);
|
|
99
|
+
if (!hasSurfaceTest && !hasFunctional && !ver.untestableRationale) {
|
|
100
|
+
warnings.push(`${cap.id}: primary surface "${surface}" has no tests/capabilities/${cap.id}/${surface}.test.mjs`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (cap.lastValidated) {
|
|
106
|
+
const age = now - Date.parse(cap.lastValidated);
|
|
107
|
+
if (Number.isFinite(age) && age > STALE_MS) {
|
|
108
|
+
warnings.push(`${cap.id}: lastValidated older than 90 days (${cap.lastValidated})`);
|
|
109
|
+
}
|
|
110
|
+
} else if (cap.criticality === 'P0') {
|
|
111
|
+
warnings.push(`${cap.id}: P0 capability never validated (lastValidated null)`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const dep of cap.dependencies?.skills ?? []) {
|
|
115
|
+
if (!fs.existsSync(resolveSkillPath(dep))) {
|
|
116
|
+
errors.push(`${cap.id}: dependency skill missing: skills/${dep}.md`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
for (const wf of WORKFLOW_TYPES) {
|
|
122
|
+
const covered = (raw.capabilities ?? []).some((c) => c.embeddedWorkflow === wf);
|
|
123
|
+
if (!covered) {
|
|
124
|
+
warnings.push(`embedded workflow "${wf}" has no registry entry yet`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
valid: errors.length === 0,
|
|
130
|
+
errors,
|
|
131
|
+
warnings,
|
|
132
|
+
count: (raw.capabilities ?? []).length,
|
|
133
|
+
registryPath: path.join(rootDir, 'registry', 'capabilities.json'),
|
|
134
|
+
};
|
|
135
|
+
}
|
package/lib/resources/budget.mjs
CHANGED
|
@@ -82,6 +82,16 @@ export function measureUsage(projectRoot, env = process.env) {
|
|
|
82
82
|
return result;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
let lastBudgetWarnAt = 0;
|
|
86
|
+
|
|
87
|
+
export function warnBudgetOnce(message, env = process.env) {
|
|
88
|
+
if (env.NODE_ENV === 'test' && env.CONSTRUCT_BUDGET_WARN_IN_TEST !== '1') return;
|
|
89
|
+
const now = Date.now();
|
|
90
|
+
if (now - lastBudgetWarnAt < 60_000) return;
|
|
91
|
+
lastBudgetWarnAt = now;
|
|
92
|
+
process.stderr.write(`[construct:budget] ${message}\n`);
|
|
93
|
+
}
|
|
94
|
+
|
|
85
95
|
export function reserveOrReject(projectRoot, category, sizeBytes, env = process.env) {
|
|
86
96
|
if (!CATEGORY_PATHS[category]) {
|
|
87
97
|
return { ok: true, source: 'unknown-category' };
|
|
@@ -110,6 +120,78 @@ export function reserveOrReject(projectRoot, category, sizeBytes, env = process.
|
|
|
110
120
|
return { ok: true };
|
|
111
121
|
}
|
|
112
122
|
|
|
123
|
+
function listHardRejectFiles(projectRoot) {
|
|
124
|
+
const files = [];
|
|
125
|
+
for (const cat of HARD_REJECT_CATEGORIES) {
|
|
126
|
+
const rel = CATEGORY_PATHS[cat];
|
|
127
|
+
if (!rel) continue;
|
|
128
|
+
const dir = path.join(projectRoot, rel);
|
|
129
|
+
if (!fs.existsSync(dir)) continue;
|
|
130
|
+
const walk = (current) => {
|
|
131
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
132
|
+
const full = path.join(current, entry.name);
|
|
133
|
+
if (entry.isDirectory()) {
|
|
134
|
+
walk(full);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const stat = fs.statSync(full);
|
|
139
|
+
files.push({
|
|
140
|
+
category: cat,
|
|
141
|
+
path: full,
|
|
142
|
+
mtimeMs: stat.mtimeMs,
|
|
143
|
+
bytes: stat.size,
|
|
144
|
+
});
|
|
145
|
+
} catch { /* race */ }
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
walk(dir);
|
|
149
|
+
}
|
|
150
|
+
files.sort((a, b) => a.mtimeMs - b.mtimeMs);
|
|
151
|
+
return files;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function planEmergencyReclaim(projectRoot, bytesNeeded, env = process.env) {
|
|
155
|
+
const usage = measureUsage(projectRoot, env);
|
|
156
|
+
const bytesToFree = Math.max(0, usage.totalCxBytes + bytesNeeded - usage.totalCxCap);
|
|
157
|
+
if (bytesToFree <= 0) return [];
|
|
158
|
+
|
|
159
|
+
const actions = [];
|
|
160
|
+
let freed = 0;
|
|
161
|
+
for (const file of listHardRejectFiles(projectRoot)) {
|
|
162
|
+
if (freed >= bytesToFree) break;
|
|
163
|
+
actions.push({
|
|
164
|
+
category: file.category,
|
|
165
|
+
path: file.path,
|
|
166
|
+
reason: 'emergency-reclaim',
|
|
167
|
+
bytes: file.bytes,
|
|
168
|
+
});
|
|
169
|
+
freed += file.bytes;
|
|
170
|
+
}
|
|
171
|
+
return actions;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function ensureDiskWrite(projectRoot, category, sizeBytes, env = process.env) {
|
|
175
|
+
let check = reserveOrReject(projectRoot, category, sizeBytes, env);
|
|
176
|
+
if (check.ok) return check;
|
|
177
|
+
|
|
178
|
+
if (!HARD_REJECT_CATEGORIES.has(category)) {
|
|
179
|
+
return check;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const actions = planEmergencyReclaim(projectRoot, sizeBytes, env);
|
|
183
|
+
let reclaimed = { removed: [], bytesFreed: 0 };
|
|
184
|
+
if (actions.length) {
|
|
185
|
+
reclaimed = executePrune(actions);
|
|
186
|
+
check = reserveOrReject(projectRoot, category, sizeBytes, env);
|
|
187
|
+
if (check.ok) {
|
|
188
|
+
return { ...check, reclaimed };
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { ...check, reclaimed };
|
|
193
|
+
}
|
|
194
|
+
|
|
113
195
|
export function planPrune(projectRoot, env = process.env, opts = {}) {
|
|
114
196
|
const budgets = loadResourceBudgets(projectRoot, env);
|
|
115
197
|
const disk = budgets?.disk ?? {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/resources/process-budget.mjs — RSS caps from construct.config.json.
|
|
3
|
+
*
|
|
4
|
+
* Maps long-running Construct processes to `resources.process.*` limits so
|
|
5
|
+
* daemon runners and embed workers share one config surface with doctor.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadProjectConfig } from '../config/project-config.mjs';
|
|
9
|
+
import { DEFAULT_MEMORY_CAP_MB } from '../daemons/contract.mjs';
|
|
10
|
+
|
|
11
|
+
export function loadProcessBudgets(projectRoot, env = process.env) {
|
|
12
|
+
const { config } = loadProjectConfig(projectRoot, env);
|
|
13
|
+
const process = config?.resources?.process ?? {};
|
|
14
|
+
return {
|
|
15
|
+
embedDaemonMaxRssMb: process.embedDaemonMaxRssMb ?? 800,
|
|
16
|
+
mcpServerMaxRssMb: process.mcpServerMaxRssMb ?? 250,
|
|
17
|
+
workerReplicaMaxRssMb: process.workerReplicaMaxRssMb ?? DEFAULT_MEMORY_CAP_MB,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function memoryCapMbFor(daemonName, projectRoot, env = process.env) {
|
|
22
|
+
const budgets = loadProcessBudgets(projectRoot, env);
|
|
23
|
+
switch (daemonName) {
|
|
24
|
+
case 'embed':
|
|
25
|
+
case 'embed-daemon':
|
|
26
|
+
return budgets.embedDaemonMaxRssMb;
|
|
27
|
+
case 'mcp':
|
|
28
|
+
case 'construct-mcp':
|
|
29
|
+
return budgets.mcpServerMaxRssMb;
|
|
30
|
+
case 'intake':
|
|
31
|
+
case 'oracle':
|
|
32
|
+
case 'worker':
|
|
33
|
+
return budgets.workerReplicaMaxRssMb;
|
|
34
|
+
default:
|
|
35
|
+
return DEFAULT_MEMORY_CAP_MB;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function currentRssMb() {
|
|
40
|
+
return process.memoryUsage().rss / (1024 * 1024);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function rssOverCap(capMb) {
|
|
44
|
+
return currentRssMb() > capMb;
|
|
45
|
+
}
|
package/lib/rules-delivery.mjs
CHANGED
|
@@ -15,11 +15,18 @@
|
|
|
15
15
|
import fs from 'node:fs';
|
|
16
16
|
import path from 'node:path';
|
|
17
17
|
|
|
18
|
+
import { logRuleCall } from './telemetry/rule-calls.mjs';
|
|
19
|
+
|
|
18
20
|
const SKIP_DIRS = new Set(['node_modules', '.git', '.cx', 'dist', 'build', '.next', 'vendor', '.beads']);
|
|
19
21
|
const MAX_FILES = 5000;
|
|
20
22
|
|
|
21
|
-
function parseRule(absPath) {
|
|
23
|
+
function parseRule(absPath, ruleRel) {
|
|
22
24
|
const raw = fs.readFileSync(absPath, 'utf8');
|
|
25
|
+
logRuleCall({
|
|
26
|
+
rulePath: `rules/${ruleRel.replace(/\.md$/, '')}.md`,
|
|
27
|
+
source: 'sync',
|
|
28
|
+
callerContext: 'rules-delivery',
|
|
29
|
+
});
|
|
23
30
|
const fm = raw.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
24
31
|
if (!fm) return null;
|
|
25
32
|
const globs = [...fm[1].matchAll(/^\s*-\s+"([^"]+)"\s*$/gm)].map((m) => m[1]);
|
|
@@ -38,7 +45,7 @@ export function collectGlobScopedRules(rulesDir) {
|
|
|
38
45
|
const childRel = rel ? `${rel}/${e.name}` : e.name;
|
|
39
46
|
if (e.isDirectory()) walk(full, childRel);
|
|
40
47
|
else if (e.name.endsWith('.md')) {
|
|
41
|
-
const parsed = parseRule(full);
|
|
48
|
+
const parsed = parseRule(full, childRel);
|
|
42
49
|
if (parsed) out.push({ rule: childRel.replace(/\.md$/, ''), ...parsed });
|
|
43
50
|
}
|
|
44
51
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/rules-read.mjs — read rule files with reference telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Central read path for enforcement and sync tooling so `construct rules usage`
|
|
5
|
+
* can roll up which rules are actually loaded at runtime.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
|
|
12
|
+
import { logRuleCall } from './telemetry/rule-calls.mjs';
|
|
13
|
+
|
|
14
|
+
const REPO_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
15
|
+
|
|
16
|
+
export function normalizeRulePath(rel) {
|
|
17
|
+
const trimmed = String(rel || '').replace(/^\.\//, '');
|
|
18
|
+
if (trimmed.startsWith('rules/')) return trimmed;
|
|
19
|
+
return `rules/${trimmed.replace(/\.md$/, '')}.md`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readRuleFile(rel, { rootDir = REPO_ROOT, source = 'validation', callerContext, env } = {}) {
|
|
23
|
+
const rulePath = normalizeRulePath(rel);
|
|
24
|
+
logRuleCall({ rulePath, source, callerContext }, { env });
|
|
25
|
+
return fs.readFileSync(path.join(rootDir, rulePath), 'utf8');
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/runtime-env.mjs — Construct runtime environment with credential bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* prepareConstructEnv merges ~/.construct/config.env into the process environment.
|
|
5
|
+
* Credential auto-link from 1Password runs only when autoLink:true (setup-credentials).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadConstructEnv } from './env-config.mjs';
|
|
9
|
+
import { ensureConstructCredentials } from './providers/credential-bootstrap.mjs';
|
|
10
|
+
|
|
11
|
+
export { loadConstructEnv } from './env-config.mjs';
|
|
12
|
+
|
|
13
|
+
export function prepareConstructEnv({
|
|
14
|
+
rootDir, homeDir, env = process.env, warn = true, cwd, autoLink = false,
|
|
15
|
+
} = {}) {
|
|
16
|
+
ensureConstructCredentials({
|
|
17
|
+
env,
|
|
18
|
+
cwd: cwd || rootDir || process.cwd(),
|
|
19
|
+
home: homeDir,
|
|
20
|
+
autoLink,
|
|
21
|
+
});
|
|
22
|
+
return loadConstructEnv({ rootDir, homeDir, env, warn });
|
|
23
|
+
}
|
package/lib/runtime-pressure.mjs
CHANGED
|
@@ -91,6 +91,19 @@ function isHelperCommand(command) {
|
|
|
91
91
|
return /(context7-mcp|playwright-mcp|mcp-server-sequential-thinking)/.test(command);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// VHS demo recorder spawns ttyd with a fixed flag set. Orphaned instances retain
|
|
95
|
+
// resources after VHS exits without teardown.
|
|
96
|
+
|
|
97
|
+
function isVhsTtydCommand(command) {
|
|
98
|
+
return /\bttyd\b/.test(command)
|
|
99
|
+
&& /--once\b/.test(command)
|
|
100
|
+
&& /rendererType canvas/.test(command);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isVhsRecorderCommand(command) {
|
|
104
|
+
return /(?:^|\/)vhs(?:\s|$)/.test(command);
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
function isCassIndexCommand(command) {
|
|
95
108
|
return /\bcass index\b/.test(command);
|
|
96
109
|
}
|
|
@@ -166,6 +179,28 @@ export function buildCleanupPlan(processEntries = [], thresholds = {}) {
|
|
|
166
179
|
}
|
|
167
180
|
}
|
|
168
181
|
|
|
182
|
+
const leakedVhsTtyd = processEntries
|
|
183
|
+
.filter((entry) => isVhsTtydCommand(entry.command)
|
|
184
|
+
&& (entry.ppid === 1 || entry.elapsedSeconds >= 3600))
|
|
185
|
+
.sort((a, b) => b.elapsedSeconds - a.elapsedSeconds);
|
|
186
|
+
for (const entry of leakedVhsTtyd) {
|
|
187
|
+
if (!seen.has(entry.pid)) {
|
|
188
|
+
seen.add(entry.pid);
|
|
189
|
+
terminate.push({ ...entry, reason: 'leaked-vhs-ttyd' });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const staleVhs = processEntries
|
|
194
|
+
.filter((entry) => isVhsRecorderCommand(entry.command)
|
|
195
|
+
&& entry.elapsedSeconds >= maxHelperAgeSeconds)
|
|
196
|
+
.sort((a, b) => b.elapsedSeconds - a.elapsedSeconds);
|
|
197
|
+
for (const entry of staleVhs) {
|
|
198
|
+
if (!seen.has(entry.pid)) {
|
|
199
|
+
seen.add(entry.pid);
|
|
200
|
+
terminate.push({ ...entry, reason: 'stale-vhs-recorder' });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
169
204
|
const cassCandidates = processEntries
|
|
170
205
|
.filter((entry) => isCassIndexCommand(entry.command) && entry.elapsedSeconds >= maxCassIndexAgeSeconds)
|
|
171
206
|
.sort((a, b) => b.elapsedSeconds - a.elapsedSeconds)
|
|
@@ -179,18 +214,27 @@ export function buildCleanupPlan(processEntries = [], thresholds = {}) {
|
|
|
179
214
|
}
|
|
180
215
|
|
|
181
216
|
function parsePsLine(line) {
|
|
182
|
-
const match = line.match(/^\s*(\d+)\s+(\d+)\s+([0-9:-]+)\s+(.*)$/);
|
|
217
|
+
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+([0-9:-]+)\s+(.*)$/);
|
|
183
218
|
if (!match) return null;
|
|
184
219
|
return {
|
|
185
220
|
pid: Number(match[1]),
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
221
|
+
ppid: Number(match[2]),
|
|
222
|
+
rssKb: Number(match[3]),
|
|
223
|
+
elapsedSeconds: parseElapsedSeconds(match[4]),
|
|
224
|
+
command: match[5],
|
|
189
225
|
};
|
|
190
226
|
}
|
|
191
227
|
|
|
228
|
+
function isPressureManagedEntry(entry) {
|
|
229
|
+
return isOpencodeCommand(entry.command)
|
|
230
|
+
|| isHelperCommand(entry.command)
|
|
231
|
+
|| isCassIndexCommand(entry.command)
|
|
232
|
+
|| isVhsTtydCommand(entry.command)
|
|
233
|
+
|| isVhsRecorderCommand(entry.command);
|
|
234
|
+
}
|
|
235
|
+
|
|
192
236
|
export function listPressureProcesses({ spawnSyncFn = spawnSync } = {}) {
|
|
193
|
-
const result = spawnSyncFn('ps', ['-ax', '-o', 'pid=,rss=,etime=,command='], {
|
|
237
|
+
const result = spawnSyncFn('ps', ['-ax', '-o', 'pid=,ppid=,rss=,etime=,command='], {
|
|
194
238
|
encoding: 'utf8',
|
|
195
239
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
196
240
|
});
|
|
@@ -199,7 +243,7 @@ export function listPressureProcesses({ spawnSyncFn = spawnSync } = {}) {
|
|
|
199
243
|
.split(/\r?\n/)
|
|
200
244
|
.map(parsePsLine)
|
|
201
245
|
.filter(Boolean)
|
|
202
|
-
.filter(
|
|
246
|
+
.filter(isPressureManagedEntry);
|
|
203
247
|
}
|
|
204
248
|
|
|
205
249
|
export function readSwapUsage({ spawnSyncFn = spawnSync } = {}) {
|
|
@@ -270,7 +314,7 @@ export function runPressureRelease({
|
|
|
270
314
|
for (const entry of targets) {
|
|
271
315
|
try {
|
|
272
316
|
killFn(entry.pid, 'SIGTERM');
|
|
273
|
-
killed.push({ pid: entry.pid, reason: entry.reason });
|
|
317
|
+
killed.push({ pid: entry.pid, reason: entry.reason, command: entry.command });
|
|
274
318
|
} catch {
|
|
275
319
|
// Process may already be gone or we lack permission — pressure relief
|
|
276
320
|
// continues regardless; the loop is opportunistic per-pid, not transactional.
|
package/lib/schema-infer.mjs
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* { name, type, description, required, pattern, example, enum, format, nested_fields }
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import { extractDocumentTextNodeNative, extractDocumentTextAsync } from './document-extract.mjs';
|
|
25
25
|
import { readCurrentModels, readOpenRouterApiKeyFromOpenCodeConfig } from './model-router.mjs';
|
|
26
26
|
import { getUserEnvPath } from './env-config.mjs';
|
|
27
27
|
import { pollFreeModels, topForTier } from './model-free-selector.mjs';
|
|
@@ -348,6 +348,17 @@ function buildUserPrompt(extracted) {
|
|
|
348
348
|
].join('\n');
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
async function extractForSchemaInfer(filePath, { maxChars }) {
|
|
352
|
+
try {
|
|
353
|
+
return await extractDocumentTextNodeNative(filePath, { maxChars });
|
|
354
|
+
} catch (err) {
|
|
355
|
+
if (err.code === 'OFFICE_REQUIRES_DOCLING' || err.code === 'PDF_REQUIRES_DOCLING') {
|
|
356
|
+
return extractDocumentTextAsync(filePath, { maxChars });
|
|
357
|
+
}
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
351
362
|
/**
|
|
352
363
|
* Infer a structured schema from a document file.
|
|
353
364
|
*
|
|
@@ -372,7 +383,7 @@ export async function inferDocumentSchema(filePath, { maxChars = 40_000 } = {})
|
|
|
372
383
|
environment,
|
|
373
384
|
});
|
|
374
385
|
|
|
375
|
-
const extracted =
|
|
386
|
+
const extracted = await extractForSchemaInfer(filePath, { maxChars });
|
|
376
387
|
if (!extracted.text || extracted.text.trim().length === 0) {
|
|
377
388
|
void lfUpdateTrace({ id: traceId, output: { error: 'no-text-extracted' }, metadata: { status: 'error' } });
|
|
378
389
|
throw new Error(`No text could be extracted from: ${filePath}`);
|