@geraldmaron/construct 1.1.1 โ 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 +29 -19
- package/bin/construct +311 -81
- 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 +1 -1
- 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 +129 -15
- 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 +598 -140
- package/lib/diagram-export.mjs +192 -0
- package/lib/diagram.mjs +13 -11
- package/lib/docs-verify.mjs +25 -7
- package/lib/doctor/index.mjs +3 -1
- 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/document-export.mjs +360 -36
- package/lib/document-extract.mjs +44 -4
- package/lib/document-ingest.mjs +29 -20
- package/lib/embed/cli.mjs +2 -2
- package/lib/embed/daemon.mjs +18 -46
- 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/health-check.mjs +90 -76
- package/lib/hooks/artifact-release-gate.mjs +43 -0
- 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 +373 -92
- 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/playwright-demo.mjs +252 -0
- package/lib/project-init-shared.mjs +4 -1
- package/lib/prompt-composer.js +7 -3
- 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 +228 -7
- package/lib/service-manager.mjs +60 -8
- package/lib/setup.mjs +70 -7
- 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/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 +28 -14
- package/personas/construct.md +2 -2
- 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 +41 -28
- 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 +7 -2
- 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 +8 -8
- 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
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lib/hooks/artifact-release-gate.mjs โ PostToolUse advisory structure/visual gate for typed docs.
|
|
4
|
+
*
|
|
5
|
+
* Runs after Write/Edit/MultiEdit on docs/** and .cx/research/** markdown artifacts.
|
|
6
|
+
* Surfaces manifest structure and visual violations on stderr during drafting; does not
|
|
7
|
+
* block the edit (full release gate runs via `construct artifact validate` and CI).
|
|
8
|
+
*
|
|
9
|
+
* Notice-only: suppressed when stderr is not a TTY, CI=true, or NODE_ENV=test.
|
|
10
|
+
*
|
|
11
|
+
* @lifecycle PostToolUse
|
|
12
|
+
* @matcher Write|Edit|MultiEdit
|
|
13
|
+
* @p95ms 80
|
|
14
|
+
* @maxBlockingScope none (PostToolUse, non-blocking)
|
|
15
|
+
* @exits 0 = pass
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { checkArtifactGateNotice, formatArtifactGateNotice } from '../artifact-gate-notice.mjs';
|
|
20
|
+
|
|
21
|
+
function shouldEmitNotice() {
|
|
22
|
+
if (process.env.CI === 'true') return false;
|
|
23
|
+
if (process.env.NODE_ENV === 'test') return false;
|
|
24
|
+
if (!process.stderr.isTTY) return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let filePath = process.env.TOOL_INPUT_FILE_PATH;
|
|
29
|
+
let cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
30
|
+
if (!filePath) {
|
|
31
|
+
try {
|
|
32
|
+
const input = JSON.parse(readFileSync(0, 'utf8'));
|
|
33
|
+
filePath = input?.tool_input?.file_path || input?.tool_input?.path;
|
|
34
|
+
if (input?.cwd) cwd = input.cwd;
|
|
35
|
+
} catch { /* no stdin */ }
|
|
36
|
+
}
|
|
37
|
+
if (!filePath) process.exit(0);
|
|
38
|
+
|
|
39
|
+
const result = checkArtifactGateNotice(filePath, { cwd });
|
|
40
|
+
if (!result || !shouldEmitNotice()) process.exit(0);
|
|
41
|
+
|
|
42
|
+
process.stderr.write(formatArtifactGateNotice(result));
|
|
43
|
+
process.exit(0);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lib/hooks/brand-prose-lint.mjs โ PostToolUse hook: block brand drift at write time.
|
|
4
|
+
*
|
|
5
|
+
* Enforces marketing voice, retired typography, and Construct/cli naming on governed
|
|
6
|
+
* markdown and template surfaces. Mirrors scripts/audit/03d-brand.mjs scope.
|
|
7
|
+
*
|
|
8
|
+
* @lifecycle PostToolUse
|
|
9
|
+
* @matcher Write|Edit|MultiEdit
|
|
10
|
+
* @p95ms 40
|
|
11
|
+
* @maxBlockingScope PostToolUse
|
|
12
|
+
* @exits 0 = pass | 2 = block tool call
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync } from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { formatBrandViolations, lintFile } from '../brand-prose.mjs';
|
|
19
|
+
|
|
20
|
+
let filePath = process.env.TOOL_INPUT_FILE_PATH;
|
|
21
|
+
if (!filePath) {
|
|
22
|
+
try {
|
|
23
|
+
const input = JSON.parse(readFileSync(0, 'utf8'));
|
|
24
|
+
filePath = input?.tool_input?.file_path || input?.tool_input?.path;
|
|
25
|
+
} catch {}
|
|
26
|
+
}
|
|
27
|
+
if (!filePath) process.exit(0);
|
|
28
|
+
|
|
29
|
+
const rootDir = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
30
|
+
const result = lintFile(filePath, { rootDir });
|
|
31
|
+
if (!result.violations.length) process.exit(0);
|
|
32
|
+
|
|
33
|
+
const output = formatBrandViolations([result]);
|
|
34
|
+
process.stderr.write(`${output}\n`);
|
|
35
|
+
process.stderr.write(
|
|
36
|
+
'\nBrand policy blocked this edit (marketing voice, retired fonts, or Construct/cli naming). See docs/reference/branding.md and docs/STYLE.md.\n',
|
|
37
|
+
);
|
|
38
|
+
process.exit(2);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lib/hooks/graph-impact-advisory.mjs โ PostToolUse hook: test-impact advisory on code edits.
|
|
4
|
+
*
|
|
5
|
+
* On Write|Edit under lib/, bin/, tests/, or src/, runs forward change-impact
|
|
6
|
+
* over the dependency graph and surfaces affected tests on stderr. Non-blocking;
|
|
7
|
+
* complements `construct impact` CLI and the doctor graph-staleness watcher.
|
|
8
|
+
*
|
|
9
|
+
* @p95ms 50
|
|
10
|
+
* @maxBlockingScope none (PostToolUse, non-blocking)
|
|
11
|
+
*
|
|
12
|
+
* @lifecycle PostToolUse
|
|
13
|
+
* @matcher Write|Edit|MultiEdit
|
|
14
|
+
* @exits 0 = pass
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { readFileSync } from 'node:fs';
|
|
18
|
+
|
|
19
|
+
import { computeImpact } from '../graph/impact.mjs';
|
|
20
|
+
import { checkGraphStaleness } from '../graph/staleness.mjs';
|
|
21
|
+
|
|
22
|
+
const CODE_RE = /^(lib|bin|src|tests)\/.*\.(?:m?js|jsx?|tsx?|cjs)$/;
|
|
23
|
+
|
|
24
|
+
let filePath = process.env.TOOL_INPUT_FILE_PATH;
|
|
25
|
+
let cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
26
|
+
if (!filePath) {
|
|
27
|
+
try {
|
|
28
|
+
const input = JSON.parse(readFileSync(0, 'utf8'));
|
|
29
|
+
filePath = input?.tool_input?.file_path || input?.tool_input?.path;
|
|
30
|
+
if (input?.cwd) cwd = input.cwd;
|
|
31
|
+
} catch { /* stdin optional */ }
|
|
32
|
+
}
|
|
33
|
+
if (!filePath) process.exit(0);
|
|
34
|
+
|
|
35
|
+
const rel = filePath.startsWith(cwd)
|
|
36
|
+
? filePath.slice(cwd.length).replace(/^\/+/, '')
|
|
37
|
+
: filePath;
|
|
38
|
+
|
|
39
|
+
if (!CODE_RE.test(rel)) process.exit(0);
|
|
40
|
+
|
|
41
|
+
const stale = checkGraphStaleness(cwd);
|
|
42
|
+
if (stale.stale) {
|
|
43
|
+
process.stderr.write(
|
|
44
|
+
`[graph-impact] dependency graph stale (${stale.staleReason}). Run \`construct graph build\` before trusting impact output.\n`,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const impact = computeImpact({ rootDir: cwd, changedFiles: [rel] });
|
|
49
|
+
if (!impact.graphPresent) {
|
|
50
|
+
process.stderr.write('[graph-impact] no .cx/graph/ present โ run `construct graph build` to enable test-impact advisories.\n');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (impact.affectedTests.length > 0) {
|
|
55
|
+
const preview = impact.affectedTests.slice(0, 5).map((t) => t.replace(/^test:/, '')).join(', ');
|
|
56
|
+
const more = impact.affectedTests.length > 5 ? ` (+${impact.affectedTests.length - 5} more)` : '';
|
|
57
|
+
process.stderr.write(`[graph-impact] ${rel} may affect: ${preview}${more}\n`);
|
|
58
|
+
} else if (impact.coverageGaps.length > 0) {
|
|
59
|
+
process.stderr.write(`[graph-impact] ${rel} has no capability edge in the dependency graph (coverage gap).\n`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
process.exit(0);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* lib/hooks/session-optimize.mjs โ Session end optimization hook โ triggers agent optimization for low-performers.
|
|
4
4
|
*
|
|
5
5
|
* Runs as a Stop hook at session end. Checks recent performance reviews for agents with avgScore < 0.7
|
|
6
|
-
* and
|
|
6
|
+
* and schedules `construct optimize <agent> --dry-run` detached so the Stop hook returns within budget.
|
|
7
7
|
*
|
|
8
8
|
* @p95ms 300
|
|
9
9
|
* @maxBlockingScope Stop (non-blocking, async)
|
|
@@ -12,110 +12,102 @@
|
|
|
12
12
|
* @matcher *
|
|
13
13
|
* @exits 0 = pass
|
|
14
14
|
*/
|
|
15
|
-
import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'fs';
|
|
15
|
+
import { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
16
16
|
import { join, dirname } from 'path';
|
|
17
17
|
import { homedir } from 'os';
|
|
18
|
-
import {
|
|
18
|
+
import { spawn } from 'child_process';
|
|
19
19
|
import { fileURLToPath } from 'url';
|
|
20
20
|
|
|
21
21
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
22
|
const CONSTRUCT_BIN = join(__dirname, '..', '..', 'bin', 'construct');
|
|
23
23
|
|
|
24
|
-
const OPTIMIZATION_THRESHOLD = 0.7;
|
|
25
|
-
const MIN_TRACES_PER_AGENT = 3;
|
|
26
|
-
const OPTIMIZATION_DAYS = 7;
|
|
24
|
+
const OPTIMIZATION_THRESHOLD = 0.7;
|
|
25
|
+
const MIN_TRACES_PER_AGENT = 3;
|
|
26
|
+
const OPTIMIZATION_DAYS = 7;
|
|
27
27
|
const OPTIMIZATION_LOG_DIR = join(homedir(), '.cx', 'optimization-logs');
|
|
28
|
+
const REVIEWS_DIR = join(homedir(), '.cx', 'performance-reviews');
|
|
28
29
|
|
|
29
|
-
// Ensure optimization log directory exists
|
|
30
30
|
mkdirSync(OPTIMIZATION_LOG_DIR, { recursive: true });
|
|
31
31
|
|
|
32
32
|
let input = {};
|
|
33
33
|
try { input = JSON.parse(readFileSync(0, 'utf8')); } catch { process.exit(0); }
|
|
34
34
|
|
|
35
|
-
// Only run for Construct sessions
|
|
36
35
|
const cwd = input?.cwd || process.cwd();
|
|
37
36
|
const projectName = cwd.split('/').pop() || 'project';
|
|
38
37
|
|
|
39
|
-
// Skip if this is not a Construct project or in global mode
|
|
40
38
|
if (projectName !== 'construct' && !existsSync(join(cwd, '.cx'))) {
|
|
41
39
|
process.exit(0);
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
// Reviews older than this trigger an auto-refresh on the next session-stop.
|
|
45
|
-
// Six hours keeps optimization signal current without re-running review on
|
|
46
|
-
// every short session. Operators can still run `construct review` manually.
|
|
47
42
|
const REVIEW_REFRESH_MS = 6 * 60 * 60 * 1000;
|
|
48
43
|
|
|
44
|
+
function spawnDetached(args, { cwd: workDir = cwd } = {}) {
|
|
45
|
+
try {
|
|
46
|
+
const child = spawn(process.execPath, args, {
|
|
47
|
+
cwd: workDir,
|
|
48
|
+
stdio: 'ignore',
|
|
49
|
+
detached: true,
|
|
50
|
+
env: process.env,
|
|
51
|
+
});
|
|
52
|
+
child.unref();
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function listReviewJsonFiles(dir) {
|
|
60
|
+
if (!existsSync(dir)) return [];
|
|
61
|
+
return readdirSync(dir)
|
|
62
|
+
.filter((name) => name.endsWith('.json'))
|
|
63
|
+
.map((name) => join(dir, name));
|
|
64
|
+
}
|
|
65
|
+
|
|
49
66
|
function maybeRefreshReviews() {
|
|
50
|
-
const reviewsDir = join(homedir(), '.cx', 'performance-reviews');
|
|
51
67
|
let needsRefresh = false;
|
|
52
68
|
try {
|
|
53
|
-
|
|
69
|
+
const files = listReviewJsonFiles(REVIEWS_DIR);
|
|
70
|
+
if (files.length === 0) {
|
|
54
71
|
needsRefresh = true;
|
|
55
72
|
} else {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const m = Number(spawnSync('stat', ['-f', '%m', f], { encoding: 'utf8' }).stdout.trim());
|
|
65
|
-
return Number.isFinite(m) ? Math.max(acc, m * 1000) : acc;
|
|
66
|
-
} catch { return acc; }
|
|
67
|
-
}, 0);
|
|
68
|
-
if (Date.now() - newestMs > REVIEW_REFRESH_MS) needsRefresh = true;
|
|
69
|
-
}
|
|
73
|
+
const newestMs = files.reduce((acc, filePath) => {
|
|
74
|
+
try {
|
|
75
|
+
return Math.max(acc, statSync(filePath).mtimeMs);
|
|
76
|
+
} catch {
|
|
77
|
+
return acc;
|
|
78
|
+
}
|
|
79
|
+
}, 0);
|
|
80
|
+
if (Date.now() - newestMs > REVIEW_REFRESH_MS) needsRefresh = true;
|
|
70
81
|
}
|
|
71
|
-
} catch {
|
|
82
|
+
} catch {
|
|
83
|
+
/* refresh is best-effort */
|
|
84
|
+
}
|
|
72
85
|
|
|
73
86
|
if (!needsRefresh) return;
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
try {
|
|
77
|
-
spawnSync(process.execPath, [CONSTRUCT_BIN, 'review', '--quiet'], {
|
|
78
|
-
stdio: 'ignore',
|
|
79
|
-
timeout: 60_000,
|
|
80
|
-
detached: true,
|
|
81
|
-
});
|
|
82
|
-
} catch { /* refresh is best-effort; manual `construct review` is always available */ }
|
|
88
|
+
spawnDetached([CONSTRUCT_BIN, 'review', '--quiet']);
|
|
83
89
|
}
|
|
84
90
|
|
|
85
|
-
// Read the most recent performance review
|
|
86
91
|
function getLatestPerformanceReview() {
|
|
87
|
-
const reviewsDir = join(homedir(), '.cx', 'performance-reviews');
|
|
88
|
-
if (!existsSync(reviewsDir)) return null;
|
|
89
|
-
|
|
90
92
|
try {
|
|
91
|
-
const files =
|
|
92
|
-
encoding: 'utf8',
|
|
93
|
-
timeout: 5000
|
|
94
|
-
}).stdout.trim().split('\n').filter(Boolean);
|
|
95
|
-
|
|
93
|
+
const files = listReviewJsonFiles(REVIEWS_DIR);
|
|
96
94
|
if (files.length === 0) return null;
|
|
97
|
-
|
|
98
|
-
// Sort by modification time, newest first
|
|
95
|
+
|
|
99
96
|
files.sort((a, b) => {
|
|
100
97
|
try {
|
|
101
|
-
|
|
102
|
-
const statB = spawnSync('stat', ['-f', '%m', b], { encoding: 'utf8' }).stdout.trim();
|
|
103
|
-
return parseInt(statB) - parseInt(statA);
|
|
98
|
+
return statSync(b).mtimeMs - statSync(a).mtimeMs;
|
|
104
99
|
} catch {
|
|
105
100
|
return 0;
|
|
106
101
|
}
|
|
107
102
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const content = readFileSync(latestFile, 'utf8');
|
|
111
|
-
return JSON.parse(content);
|
|
103
|
+
|
|
104
|
+
return JSON.parse(readFileSync(files[0], 'utf8'));
|
|
112
105
|
} catch (error) {
|
|
113
106
|
console.error(`Error reading performance reviews: ${error.message}`);
|
|
114
107
|
return null;
|
|
115
108
|
}
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
// Check if agent needs optimization
|
|
119
111
|
function needsOptimization(agentStats) {
|
|
120
112
|
return (
|
|
121
113
|
agentStats.scoredInvocations >= MIN_TRACES_PER_AGENT &&
|
|
@@ -124,101 +116,25 @@ function needsOptimization(agentStats) {
|
|
|
124
116
|
);
|
|
125
117
|
}
|
|
126
118
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const duration = Date.now() - startTime;
|
|
145
|
-
|
|
146
|
-
// Log the optimization attempt
|
|
147
|
-
const logEntry = {
|
|
148
|
-
timestamp: new Date().toISOString(),
|
|
149
|
-
agent: agentName,
|
|
150
|
-
durationMs: duration,
|
|
151
|
-
success: result.status === 0,
|
|
152
|
-
stdout: result.stdout,
|
|
153
|
-
stderr: result.stderr,
|
|
154
|
-
exitCode: result.status,
|
|
155
|
-
signal: result.signal
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
writeFileSync(logFile, JSON.stringify(logEntry, null, 2) + '\n');
|
|
159
|
-
|
|
160
|
-
if (result.status === 0) {
|
|
161
|
-
console.log(`โ
Optimization for ${agentName} completed in ${duration}ms`);
|
|
162
|
-
|
|
163
|
-
// Record observation about the optimization
|
|
164
|
-
recordOptimizationObservation(agentName, true, duration);
|
|
165
|
-
} else {
|
|
166
|
-
console.log(`โ Optimization for ${agentName} failed with code ${result.status}`);
|
|
167
|
-
console.log(`Stderr: ${result.stderr.slice(0, 500)}...`);
|
|
168
|
-
|
|
169
|
-
// Record observation about the failure
|
|
170
|
-
recordOptimizationObservation(agentName, false, duration, result.stderr);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return result.status === 0;
|
|
174
|
-
} catch (error) {
|
|
175
|
-
console.error(`โ Optimization for ${agentName} threw error: ${error.message}`);
|
|
176
|
-
|
|
177
|
-
const logEntry = {
|
|
178
|
-
timestamp: new Date().toISOString(),
|
|
179
|
-
agent: agentName,
|
|
180
|
-
durationMs: Date.now() - startTime,
|
|
181
|
-
success: false,
|
|
182
|
-
error: error.message,
|
|
183
|
-
stack: error.stack
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
writeFileSync(logFile, JSON.stringify(logEntry, null, 2) + '\n');
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// Record optimization observation for learning
|
|
192
|
-
function recordOptimizationObservation(agentName, success, durationMs, error = null) {
|
|
193
|
-
const observation = {
|
|
194
|
-
role: 'construct',
|
|
195
|
-
category: success ? 'pattern' : 'anti-pattern',
|
|
196
|
-
summary: success
|
|
197
|
-
? `Auto-optimized ${agentName} in ${durationMs}ms`
|
|
198
|
-
: `Auto-optimization failed for ${agentName}: ${error?.slice(0, 200) || 'unknown error'}`,
|
|
199
|
-
content: success
|
|
200
|
-
? `Automatic optimization triggered for ${agentName} after performance review showed avgScore < ${OPTIMIZATION_THRESHOLD}. Optimization completed successfully in ${durationMs}ms.`
|
|
201
|
-
: `Automatic optimization failed for ${agentName}. Error: ${error || 'Unknown'}. System should notify maintainer.`,
|
|
202
|
-
tags: ['auto-optimization', 'performance-review', agentName],
|
|
203
|
-
confidence: 0.9
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
// Use existing observation recording if available
|
|
208
|
-
const observationFile = join(homedir(), '.cx', 'observations', `${Date.now()}-${agentName}-optimization.json`);
|
|
209
|
-
mkdirSync(dirname(observationFile), { recursive: true });
|
|
210
|
-
writeFileSync(observationFile, JSON.stringify(observation, null, 2) + '\n');
|
|
211
|
-
} catch (error) {
|
|
212
|
-
// Non-critical - observation recording failed
|
|
213
|
-
console.error(`Failed to record optimization observation: ${error.message}`);
|
|
214
|
-
}
|
|
119
|
+
function scheduleAgentOptimization(agentName) {
|
|
120
|
+
const logFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-${agentName}-scheduled.json`);
|
|
121
|
+
const scheduled = spawnDetached([
|
|
122
|
+
CONSTRUCT_BIN,
|
|
123
|
+
'optimize',
|
|
124
|
+
agentName,
|
|
125
|
+
`--days=${OPTIMIZATION_DAYS}`,
|
|
126
|
+
'--dry-run',
|
|
127
|
+
]);
|
|
128
|
+
writeFileSync(logFile, `${JSON.stringify({
|
|
129
|
+
timestamp: new Date().toISOString(),
|
|
130
|
+
agent: agentName,
|
|
131
|
+
scheduled,
|
|
132
|
+
mode: 'detached-dry-run',
|
|
133
|
+
})}\n`);
|
|
134
|
+
return scheduled;
|
|
215
135
|
}
|
|
216
136
|
|
|
217
|
-
// Main optimization logic
|
|
218
137
|
function main() {
|
|
219
|
-
// If the most recent review is missing or stale (>6h), kick off a refresh
|
|
220
|
-
// detached so the Stop hook returns promptly. The refresh runs on the next
|
|
221
|
-
// session's Stop if it's still in flight; nothing here blocks user work.
|
|
222
138
|
maybeRefreshReviews();
|
|
223
139
|
|
|
224
140
|
const review = getLatestPerformanceReview();
|
|
@@ -226,92 +142,53 @@ function main() {
|
|
|
226
142
|
console.log('๐ No performance review data found. Run `construct review` first.');
|
|
227
143
|
process.exit(0);
|
|
228
144
|
}
|
|
229
|
-
|
|
230
|
-
console.log(`๐ Checking ${review.agentStats.length} agents from performance review ${review.generated}`);
|
|
231
|
-
|
|
145
|
+
|
|
232
146
|
const agentsNeedingOptimization = review.agentStats.filter(needsOptimization);
|
|
233
|
-
|
|
147
|
+
|
|
234
148
|
if (agentsNeedingOptimization.length === 0) {
|
|
235
149
|
console.log(`โ
All ${review.agentStats.length} agents have scores >= ${OPTIMIZATION_THRESHOLD} or insufficient data`);
|
|
236
150
|
process.exit(0);
|
|
237
151
|
}
|
|
238
|
-
|
|
239
|
-
console.log(`๐ง
|
|
240
|
-
|
|
241
|
-
console.log(` โข ${agent.name}: avgScore=${agent.avgScore}, invocations=${agent.invocations}`);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Sequential optimization keeps prompt-tuning deterministic and avoids
|
|
245
|
-
// colliding writes to per-agent prompt files.
|
|
246
|
-
let optimizedCount = 0;
|
|
247
|
-
let failedCount = 0;
|
|
248
|
-
|
|
152
|
+
|
|
153
|
+
console.log(`๐ง Scheduling ${agentsNeedingOptimization.length} detached optimization preview(s):`);
|
|
154
|
+
let scheduledCount = 0;
|
|
249
155
|
for (const agent of agentsNeedingOptimization) {
|
|
250
|
-
|
|
251
|
-
if (
|
|
252
|
-
optimizedCount++;
|
|
253
|
-
} else {
|
|
254
|
-
failedCount++;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Small delay between optimizations to avoid rate limits
|
|
258
|
-
if (agentsNeedingOptimization.length > 1) {
|
|
259
|
-
spawnSync('sleep', ['10'], { encoding: 'utf8' });
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Summary
|
|
264
|
-
console.log(`\n๐ Optimization Summary:`);
|
|
265
|
-
console.log(` โข Agents checked: ${review.agentStats.length}`);
|
|
266
|
-
console.log(` โข Needing optimization: ${agentsNeedingOptimization.length}`);
|
|
267
|
-
console.log(` โข Successfully optimized: ${optimizedCount}`);
|
|
268
|
-
console.log(` โข Failed: ${failedCount}`);
|
|
269
|
-
|
|
270
|
-
if (optimizedCount > 0) {
|
|
271
|
-
console.log(`\n๐ก Optimized agents will be evaluated in the next performance review.`);
|
|
272
|
-
console.log(` Run \`construct review\` after next session to see improvements.`);
|
|
156
|
+
console.log(` โข ${agent.name}: avgScore=${agent.avgScore}, invocations=${agent.invocations}`);
|
|
157
|
+
if (scheduleAgentOptimization(agent.name)) scheduledCount += 1;
|
|
273
158
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
159
|
+
|
|
160
|
+
const summaryFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-summary.json`);
|
|
161
|
+
writeFileSync(summaryFile, `${JSON.stringify({
|
|
277
162
|
timestamp: new Date().toISOString(),
|
|
278
163
|
project: projectName,
|
|
279
164
|
reviewDate: review.generated,
|
|
280
165
|
agentsChecked: review.agentStats.length,
|
|
281
166
|
agentsNeedingOptimization: agentsNeedingOptimization.length,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
agents: agentsNeedingOptimization.map(a => ({
|
|
167
|
+
scheduledCount,
|
|
168
|
+
agents: agentsNeedingOptimization.map((a) => ({
|
|
285
169
|
name: a.name,
|
|
286
170
|
avgScore: a.avgScore,
|
|
287
171
|
invocations: a.invocations,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const summaryFile = join(OPTIMIZATION_LOG_DIR, `${new Date().toISOString().split('T')[0]}-summary.json`);
|
|
293
|
-
writeFileSync(summaryFile, JSON.stringify(summary, null, 2) + '\n');
|
|
172
|
+
})),
|
|
173
|
+
}, null, 2)}\n`);
|
|
174
|
+
|
|
175
|
+
console.log(`\n๐ Optimization previews scheduled: ${scheduledCount}/${agentsNeedingOptimization.length}`);
|
|
294
176
|
}
|
|
295
177
|
|
|
296
|
-
// Run main logic
|
|
297
178
|
try {
|
|
298
179
|
main();
|
|
299
180
|
} catch (error) {
|
|
300
181
|
console.error(`โ Session optimization hook crashed: ${error.message}`);
|
|
301
|
-
console.error(error.stack);
|
|
302
|
-
|
|
303
|
-
// Log the crash
|
|
304
182
|
const crashLog = join(OPTIMIZATION_LOG_DIR, 'crashes', `${Date.now()}-crash.json`);
|
|
305
183
|
mkdirSync(dirname(crashLog), { recursive: true });
|
|
306
|
-
writeFileSync(crashLog, JSON.stringify({
|
|
184
|
+
writeFileSync(crashLog, `${JSON.stringify({
|
|
307
185
|
timestamp: new Date().toISOString(),
|
|
308
186
|
error: error.message,
|
|
309
187
|
stack: error.stack,
|
|
310
188
|
cwd,
|
|
311
|
-
projectName
|
|
312
|
-
}, null, 2)
|
|
313
|
-
|
|
189
|
+
projectName,
|
|
190
|
+
}, null, 2)}\n`);
|
|
314
191
|
process.exit(1);
|
|
315
192
|
}
|
|
316
193
|
|
|
317
|
-
process.exit(0);
|
|
194
|
+
process.exit(0);
|
|
@@ -319,11 +319,10 @@ try {
|
|
|
319
319
|
// plugin so both platforms surface the same prelude on session start.
|
|
320
320
|
|
|
321
321
|
let intakeReviewNote = '';
|
|
322
|
-
let brokerStatusNote = '';
|
|
323
322
|
try {
|
|
324
|
-
const {
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
const { buildSessionPrelude } = await import('../intake/session-prelude.mjs');
|
|
324
|
+
const prelude = buildSessionPrelude({ cwd, env: process.env });
|
|
325
|
+
intakeReviewNote = prelude ? `\n${prelude}\n` : '';
|
|
327
326
|
} catch { /* best effort */ }
|
|
328
327
|
|
|
329
328
|
// Missing-env-vars notice. Reads .env.example from the project root,
|
|
@@ -383,7 +382,7 @@ try {
|
|
|
383
382
|
// that pollutes the caller's output contract, so the resolved output mode routes
|
|
384
383
|
// the payload to stdout (interactive), stderr, or a debug log (silent).
|
|
385
384
|
|
|
386
|
-
const payload = `${header}\n${body}${stateNote}${efficiencyNote}${observationsNote}${concurrencyNote}${skillScopeNote}${dropNote}${embedStatusNote}${sourcesNote}${selfKnowledgeNote}${rolesNote}${intakeReviewNote}${
|
|
385
|
+
const payload = `${header}\n${body}${stateNote}${efficiencyNote}${observationsNote}${concurrencyNote}${skillScopeNote}${dropNote}${embedStatusNote}${sourcesNote}${selfKnowledgeNote}${rolesNote}${intakeReviewNote}${permissionPostureNote}${envCheckNote}\n${footer}${pendingNote}\n`;
|
|
387
386
|
try {
|
|
388
387
|
const { mode } = resolveHookOutputMode({ cwd, env: process.env });
|
|
389
388
|
writeHookContext({ payload, mode });
|
package/lib/host-disposition.mjs
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* lib/host-disposition.mjs โ single source of truth for the disposition of
|
|
3
3
|
* every artifact Construct creates in a host project (ADR-0027 ยง1).
|
|
4
4
|
*
|
|
5
|
+
* Scope model (see docs/concepts/project-scopes.md):
|
|
6
|
+
* templates/, docs/ โ package/repo durable sources (committed)
|
|
7
|
+
* .construct/ โ per-project launcher (ignored)
|
|
8
|
+
* .cx/ โ per-project runtime: intake, oracle, observations, demos (ignored)
|
|
9
|
+
* ~/.construct/ โ user credentials and doctor state
|
|
10
|
+
* ~/.cx/ โ user-scope telemetry fallback
|
|
11
|
+
*
|
|
5
12
|
* `IGNORED_PATTERNS` enumerates machine-specific, sync-regenerated artifacts
|
|
6
13
|
* that must never be committed; the init `.gitignore` writer and the
|
|
7
14
|
* `gitignore-coverage` reconciliation both read from here, and uninstall /
|