@geraldmaron/construct 1.0.12 → 1.0.15
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 +14 -2
- package/bin/construct +262 -14
- package/bin/construct-postinstall.mjs +23 -2
- package/commands/build/feature.md +0 -6
- package/commands/build/fix.md +0 -6
- package/commands/design/access.md +0 -6
- package/commands/design/flow.md +0 -6
- package/commands/design/ui.md +0 -6
- package/commands/measure/experiment.md +0 -6
- package/commands/measure/metrics.md +0 -6
- package/commands/measure/results.md +0 -6
- package/commands/plan/api.md +0 -6
- package/commands/plan/challenge.md +0 -6
- package/commands/plan/decide.md +0 -6
- package/commands/plan/feature.md +0 -7
- package/commands/plan/requirements.md +0 -6
- package/commands/remember/context.md +0 -6
- package/commands/remember/handoff.md +0 -6
- package/commands/remember/runbook.md +0 -6
- package/commands/review/code.md +0 -6
- package/commands/review/quality.md +0 -6
- package/commands/review/security.md +0 -6
- package/commands/ship/ready.md +0 -6
- package/commands/ship/release.md +0 -6
- package/commands/ship/status.md +0 -6
- package/commands/understand/docs.md +0 -6
- package/commands/understand/research.md +0 -6
- package/commands/understand/this.md +0 -6
- package/commands/understand/why.md +0 -6
- package/commands/work/clean.md +0 -6
- package/commands/work/drive.md +0 -6
- package/commands/work/optimize-prompts.md +0 -6
- package/commands/work/parallel-review.md +0 -6
- package/lib/auto-docs.mjs +11 -7
- package/lib/beads-client.mjs +19 -1
- package/lib/comment-lint.mjs +5 -1
- package/lib/context-state.mjs +15 -3
- package/lib/contracts/validate.mjs +54 -10
- package/lib/contracts/violation-log.mjs +154 -0
- package/lib/dashboard-static.mjs +7 -4
- package/lib/doc-stamp.mjs +16 -0
- package/lib/docs-verify.mjs +1 -8
- package/lib/doctor/watchers/bd-watch.mjs +6 -2
- package/lib/document-extract/docling-client.mjs +114 -0
- package/lib/document-extract/docling-sidecar.py +122 -0
- package/lib/document-extract/whisper-client.mjs +79 -0
- package/lib/document-extract.mjs +73 -8
- package/lib/document-ingest.mjs +26 -4
- package/lib/embed/daemon.mjs +26 -1
- package/lib/embed/docs-lifecycle.mjs +19 -0
- package/lib/embed/inbox.mjs +85 -2
- package/lib/embed/recommendation-store.mjs +29 -0
- package/lib/flavors/loader.mjs +1 -1
- package/lib/gates-audit.mjs +18 -12
- package/lib/handoffs/contract.mjs +2 -2
- package/lib/hooks/_lib/input.mjs +52 -0
- package/lib/hooks/adaptive-lint.mjs +4 -0
- package/lib/hooks/agent-tracker.mjs +59 -15
- package/lib/hooks/audit-reads.mjs +83 -42
- package/lib/hooks/audit-trail.mjs +28 -18
- package/lib/hooks/bash-output-logger.mjs +8 -2
- package/lib/hooks/block-no-verify.mjs +4 -0
- package/lib/hooks/ci-status-check.mjs +4 -0
- package/lib/hooks/comment-lint.mjs +6 -4
- package/lib/hooks/config-protection.mjs +4 -0
- package/lib/hooks/context-watch.mjs +4 -0
- package/lib/hooks/context-window-recovery.mjs +4 -0
- package/lib/hooks/dep-audit.mjs +12 -5
- package/lib/hooks/doc-coupling-check.mjs +5 -1
- package/lib/hooks/edit-accumulator.mjs +25 -10
- package/lib/hooks/edit-error-recovery.mjs +4 -0
- package/lib/hooks/edit-guard.mjs +4 -0
- package/lib/hooks/guard-bash.mjs +4 -0
- package/lib/hooks/mcp-audit.mjs +4 -0
- package/lib/hooks/mcp-health-check.mjs +4 -0
- package/lib/hooks/model-fallback.mjs +7 -11
- package/lib/hooks/policy-engine.mjs +4 -0
- package/lib/hooks/post-merge-docs-check.mjs +4 -0
- package/lib/hooks/post-merge-tracking.mjs +82 -0
- package/lib/hooks/pre-compact.mjs +4 -0
- package/lib/hooks/pre-push-gate.mjs +84 -231
- package/lib/hooks/proactive-activation.mjs +5 -2
- package/lib/hooks/readme-age-check.mjs +4 -0
- package/lib/hooks/registry-sync.mjs +35 -20
- package/lib/hooks/rule-verifier.mjs +3 -0
- package/lib/hooks/scan-secrets.mjs +4 -0
- package/lib/hooks/session-optimize.mjs +4 -0
- package/lib/hooks/session-reflect.mjs +4 -0
- package/lib/hooks/session-start.mjs +49 -2
- package/lib/hooks/session-tracking-refresh.mjs +70 -0
- package/lib/hooks/stop-notify.mjs +13 -2
- package/lib/hooks/stop-typecheck.mjs +4 -0
- package/lib/hooks/test-watch.mjs +8 -4
- package/lib/ingest/chunker.mjs +94 -0
- package/lib/ingest/pipeline.mjs +53 -0
- package/lib/ingest/store.mjs +82 -0
- package/lib/init-unified.mjs +90 -23
- package/lib/intake/attribution.mjs +77 -0
- package/lib/intake/intake-config.mjs +3 -0
- package/lib/intake/manifest.mjs +107 -0
- package/lib/intake/poll-lock.mjs +136 -0
- package/lib/intake/prepare.mjs +42 -4
- package/lib/knowledge/search.mjs +12 -0
- package/lib/logging/rotate.mjs +394 -0
- package/lib/mcp/server.mjs +142 -1
- package/lib/mcp/tools/project.mjs +2 -2
- package/lib/mcp/tools/skills.mjs +6 -3
- package/lib/mcp/tools/telemetry.mjs +1 -1
- package/lib/mcp/tools/workflow.mjs +6 -2
- package/lib/observation-store.mjs +14 -5
- package/lib/ollama-manager.mjs +32 -28
- package/lib/opencode-config.mjs +10 -3
- package/lib/orchestration/routing-tables.mjs +176 -0
- package/lib/orchestration-policy.mjs +18 -106
- package/lib/parity.mjs +48 -7
- package/lib/profiles/lifecycle.mjs +19 -1
- package/lib/project-init-shared.mjs +11 -5
- package/lib/project-root.mjs +104 -0
- package/lib/prompt-composer.js +11 -1
- package/lib/roles/catalog.mjs +1 -1
- package/lib/roles/event-bus.mjs +29 -9
- package/lib/roles/router.mjs +8 -7
- package/lib/runtime/uv-bootstrap.mjs +129 -0
- package/lib/runtime/whisper-bootstrap.mjs +102 -0
- package/lib/server/index.mjs +53 -10
- package/lib/server/static/index.html +1 -15
- package/lib/specialists/postconditions.mjs +1 -1
- package/lib/status.mjs +1 -1
- package/lib/storage/backup.mjs +2 -2
- package/lib/sync/skill-frontmatter.mjs +113 -21
- package/lib/telemetry/intent-verifications.mjs +16 -3
- package/lib/telemetry/skill-calls.mjs +12 -3
- package/lib/tracking-surfaces.mjs +377 -0
- package/lib/validators/skills.mjs +86 -54
- package/lib/worker/trace.mjs +19 -2
- package/package.json +10 -6
- package/personas/construct.md +4 -7
- package/platforms/claude/settings.template.json +29 -28
- package/rules/common/beads-hygiene.md +3 -9
- package/rules/common/code-review.md +3 -6
- package/rules/common/coding-style.md +3 -6
- package/rules/common/comments.md +3 -7
- package/rules/common/commit-approval.md +3 -6
- package/rules/common/cx-agent-routing.md +3 -7
- package/rules/common/cx-skill-routing.md +3 -3
- package/rules/common/doc-ownership.md +3 -8
- package/rules/common/efficiency.md +3 -8
- package/rules/common/framing.md +3 -8
- package/rules/common/git-workflow.md +3 -5
- package/rules/common/no-fabrication.md +4 -12
- package/rules/common/patterns.md +3 -5
- package/rules/common/release-gates.md +3 -8
- package/rules/common/research.md +3 -8
- package/rules/common/review-before-change.md +3 -9
- package/rules/common/security.md +3 -6
- package/rules/common/skill-composition.md +3 -7
- package/rules/common/testing.md +3 -6
- package/rules/golang/coding-style.md +1 -5
- package/rules/golang/hooks.md +1 -5
- package/rules/golang/patterns.md +1 -5
- package/rules/golang/security.md +1 -5
- package/rules/golang/testing.md +1 -5
- package/rules/python/coding-style.md +1 -5
- package/rules/python/hooks.md +1 -5
- package/rules/python/patterns.md +1 -5
- package/rules/python/security.md +1 -5
- package/rules/python/testing.md +1 -5
- package/rules/swift/coding-style.md +1 -5
- package/rules/swift/hooks.md +1 -5
- package/rules/swift/patterns.md +1 -5
- package/rules/swift/security.md +1 -5
- package/rules/swift/testing.md +1 -5
- package/rules/typescript/coding-style.md +1 -5
- package/rules/typescript/hooks.md +1 -5
- package/rules/typescript/patterns.md +1 -5
- package/rules/typescript/security.md +1 -5
- package/rules/typescript/testing.md +1 -5
- package/rules/web/coding-style.md +3 -5
- package/rules/web/design-quality.md +3 -5
- package/rules/web/hooks.md +3 -5
- package/rules/web/patterns.md +3 -5
- package/rules/web/performance.md +3 -5
- package/rules/web/security.md +3 -5
- package/rules/web/testing.md +3 -5
- package/scripts/sync-specialists.mjs +269 -75
- package/skills/ai/agent-dev.md +4 -5
- package/skills/ai/llm-security.md +4 -5
- package/skills/ai/ml-ops.md +4 -5
- package/skills/ai/orchestration-workflow.md +4 -5
- package/skills/ai/prompt-and-eval.md +4 -5
- package/skills/ai/prompt-optimizer.md +4 -6
- package/skills/ai/rag-system.md +4 -5
- package/skills/architecture/api-design.md +4 -5
- package/skills/architecture/caching.md +4 -5
- package/skills/architecture/cloud-native.md +4 -5
- package/skills/architecture/message-queue.md +4 -5
- package/skills/architecture/security-arch.md +4 -5
- package/skills/compliance/ai-disclosure.md +4 -5
- package/skills/compliance/data-privacy.md +4 -5
- package/skills/compliance/license-audit.md +4 -5
- package/skills/compliance/regulatory-review.md +4 -5
- package/skills/development/cpp.md +4 -5
- package/skills/development/go.md +4 -5
- package/skills/development/java.md +4 -5
- package/skills/development/kotlin.md +4 -5
- package/skills/development/mobile-crossplatform.md +4 -5
- package/skills/development/python.md +4 -5
- package/skills/development/rust.md +4 -5
- package/skills/development/shell.md +4 -5
- package/skills/development/swift.md +4 -5
- package/skills/development/typescript.md +4 -5
- package/skills/devops/ci-cd.md +4 -5
- package/skills/devops/containerization.md +4 -5
- package/skills/devops/cost-optimization.md +4 -5
- package/skills/devops/data-engineering.md +4 -5
- package/skills/devops/database.md +4 -5
- package/skills/devops/dependency-management.md +4 -5
- package/skills/devops/devsecops.md +4 -5
- package/skills/devops/git-workflow.md +4 -5
- package/skills/devops/incident-response.md +4 -5
- package/skills/devops/monorepo.md +4 -5
- package/skills/devops/observability.md +4 -5
- package/skills/devops/performance.md +4 -5
- package/skills/devops/testing.md +4 -5
- package/skills/docs/adr-workflow.md +4 -7
- package/skills/docs/backlog-proposal-workflow.md +4 -3
- package/skills/docs/customer-profile-workflow.md +4 -3
- package/skills/docs/document-ingest-workflow.md +4 -3
- package/skills/docs/evidence-ingest-workflow.md +4 -3
- package/skills/docs/init-docs.md +4 -5
- package/skills/docs/init-project.md +4 -5
- package/skills/docs/prd-workflow.md +4 -7
- package/skills/docs/prfaq-workflow.md +4 -3
- package/skills/docs/product-intelligence-review.md +4 -3
- package/skills/docs/product-intelligence-workflow.md +4 -6
- package/skills/docs/product-signal-workflow.md +4 -5
- package/skills/docs/research-workflow.md +4 -5
- package/skills/docs/runbook-workflow.md +4 -5
- package/skills/docs/strategy-workflow.md +4 -5
- package/skills/exploration/dependency-graph-reading.md +4 -7
- package/skills/exploration/repo-map.md +4 -5
- package/skills/exploration/tracer-bullet-method.md +4 -7
- package/skills/exploration/unknown-codebase-onboarding.md +4 -7
- package/skills/frameworks/django.md +4 -5
- package/skills/frameworks/nextjs.md +4 -5
- package/skills/frameworks/react.md +4 -5
- package/skills/frameworks/spring-boot.md +4 -5
- package/skills/frontend-design/accessibility.md +4 -5
- package/skills/frontend-design/component-patterns.md +4 -5
- package/skills/frontend-design/engineering.md +4 -5
- package/skills/frontend-design/state-management.md +4 -5
- package/skills/frontend-design/ui-aesthetics.md +4 -5
- package/skills/frontend-design/ux-principles.md +4 -5
- package/skills/operating/change-management.md +4 -8
- package/skills/operating/incident-response.md +4 -8
- package/skills/operating/oncall-rotation.md +4 -7
- package/skills/operating/orchestration-reference.md +4 -10
- package/skills/quality-gates/review-work.md +4 -5
- package/skills/quality-gates/verify-change.md +4 -5
- package/skills/quality-gates/verify-module.md +4 -5
- package/skills/quality-gates/verify-quality.md +4 -5
- package/skills/quality-gates/verify-security.md +4 -5
- package/skills/roles/architect.ai-systems.md +6 -9
- package/skills/roles/architect.data.md +6 -9
- package/skills/roles/architect.enterprise.md +6 -9
- package/skills/roles/architect.integration.md +6 -9
- package/skills/roles/architect.md +7 -14
- package/skills/roles/architect.platform.md +6 -9
- package/skills/roles/data-analyst.experiment.md +6 -9
- package/skills/roles/data-analyst.md +6 -9
- package/skills/roles/data-analyst.product-intelligence.md +7 -9
- package/skills/roles/data-analyst.product.md +6 -9
- package/skills/roles/data-analyst.telemetry.md +7 -9
- package/skills/roles/data-engineer.pipeline.md +6 -9
- package/skills/roles/data-engineer.vector-retrieval.md +7 -9
- package/skills/roles/data-engineer.warehouse.md +6 -9
- package/skills/roles/debugger.md +6 -9
- package/skills/roles/designer.accessibility.md +6 -9
- package/skills/roles/designer.md +7 -9
- package/skills/roles/engineer.ai.md +6 -9
- package/skills/roles/engineer.data.md +6 -9
- package/skills/roles/engineer.md +9 -9
- package/skills/roles/engineer.platform.md +6 -9
- package/skills/roles/operator.docs.md +6 -9
- package/skills/roles/operator.md +9 -9
- package/skills/roles/operator.release.md +6 -9
- package/skills/roles/operator.sre.md +6 -9
- package/skills/roles/orchestrator.md +6 -9
- package/skills/roles/product-manager.ai-product.md +6 -9
- package/skills/roles/product-manager.business-strategy.md +6 -9
- package/skills/roles/product-manager.enterprise.md +6 -9
- package/skills/roles/product-manager.growth.md +7 -9
- package/skills/roles/product-manager.md +7 -9
- package/skills/roles/product-manager.platform.md +6 -9
- package/skills/roles/product-manager.product.md +6 -9
- package/skills/roles/qa.ai-eval.md +8 -9
- package/skills/roles/qa.api-contract.md +7 -9
- package/skills/roles/qa.data-pipeline.md +7 -9
- package/skills/roles/qa.md +7 -9
- package/skills/roles/qa.test-automation.md +6 -9
- package/skills/roles/qa.web-ui.md +7 -9
- package/skills/roles/researcher.explorer.md +6 -9
- package/skills/roles/researcher.md +8 -9
- package/skills/roles/researcher.ux.md +6 -9
- package/skills/roles/reviewer.devil-advocate.md +6 -9
- package/skills/roles/reviewer.evaluator.md +6 -9
- package/skills/roles/reviewer.md +9 -9
- package/skills/roles/reviewer.trace.md +6 -9
- package/skills/roles/security.ai.md +7 -9
- package/skills/roles/security.appsec.md +6 -9
- package/skills/roles/security.cloud.md +6 -9
- package/skills/roles/security.legal-compliance.md +6 -9
- package/skills/roles/security.md +7 -9
- package/skills/roles/security.privacy.md +7 -9
- package/skills/roles/security.supply-chain.md +7 -9
- package/skills/security/blue-team.md +4 -5
- package/skills/security/code-audit.md +4 -5
- package/skills/security/pentest.md +4 -5
- package/skills/security/red-team.md +4 -5
- package/skills/security/threat-intel.md +4 -5
- package/skills/security/vuln-research.md +4 -5
- package/skills/strategy/competitive-landscape.md +4 -8
- package/skills/strategy/market-research-methods.md +4 -8
- package/skills/strategy/narrative-arc.md +4 -8
- package/skills/strategy/pricing-positioning.md +4 -8
- package/skills/utility/clean-code.md +4 -5
- package/specialists/policy-inventory.json +14 -0
- package/specialists/registry.json +158 -13
- package/lib/hooks/env-check.mjs +0 -83
- package/lib/hooks/read-tracker.mjs +0 -61
- package/lib/policy/unified-gates.mjs +0 -96
- package/lib/server/static/assets/index-ab25c707.js +0 -70
- package/lib/server/static/assets/index-f0c80a2b.css +0 -1
- package/lib/specialist-contracts-enforce.mjs +0 -158
- package/rules/common/agents.md +0 -28
- package/rules/common/development-workflow.md +0 -32
- package/rules/common/performance.md +0 -55
package/README.md
CHANGED
|
@@ -85,6 +85,12 @@ Anything dropped into `.cx/inbox/` (a bug report, a customer comment, a competit
|
|
|
85
85
|
|
|
86
86
|
Each signal gets a primary owner and a recommended handoff chain. Inspect with `construct intake list` and `construct intake show <id>`. Generate a task graph with `construct graph from-intake <id>`. The classifier runs in the daemon and is deterministic. The agent in your editor does the actual analysis. [Intake and triage](https://geraldmaron.github.io/construct/concepts/intake-and-triage).
|
|
87
87
|
|
|
88
|
+
### Document ingestion fidelity
|
|
89
|
+
|
|
90
|
+
`construct ingest <file>` extracts text from PDF, DOCX, XLSX, PPTX, HTML, plain text, email, and audio/video. High-fidelity extraction is the default and routes through a [docling](https://github.com/docling-project/docling) Python sidecar (MIT, IBM, donated to LF AI & Data) provisioned via [`uv`](https://github.com/astral-sh/uv); audio and video route through [`whisper.cpp`](https://github.com/ggml-org/whisper.cpp) (Metal-accelerated on macOS).
|
|
91
|
+
|
|
92
|
+
First run downloads `uv` and creates `.cx/runtime/docling/.venv` (~1.5 GB including PyTorch). Audio requires a system `whisper-cli` binary — `brew install whisper-cpp` on macOS. Pass `--strict` to fail on any extraction info loss; pass `--legacy-extractor` to use the pre-docling regex path. Any silent drops (image-heavy PDFs, scanned pages with low OCR yield) are surfaced as `droppedInfo` in the CLI output.
|
|
93
|
+
|
|
88
94
|
## Hard gates
|
|
89
95
|
|
|
90
96
|
Every code mutation runs through enforcement. No secrets committed, tests green, docs current, comments lint-clean, CI passes. Gates live in three places: write-time, commit-time, CI safety net. They can only be bypassed with explicit env vars so every exception leaves an audit trail. [Gates and enforcement](https://geraldmaron.github.io/construct/concepts/gates-and-enforcement).
|
|
@@ -93,6 +99,12 @@ Every code mutation runs through enforcement. No secrets committed, tests green,
|
|
|
93
99
|
|
|
94
100
|
Construct gets smarter on its own. Every session ends with an automatic capture: tools used, files touched, what the final reply said. That goes into `.cx/observations/` and is searchable from the next session. See [`docs/concepts/learning-loops.mdx`](./docs/concepts/learning-loops.mdx) for what's wired, what's coming, and how to turn pieces off.
|
|
95
101
|
|
|
102
|
+
## `.cx/` is local-only runtime state
|
|
103
|
+
|
|
104
|
+
`construct init` writes a runtime state tree at `.cx/` inside the project root: observations, sessions, vector index, intake packets, task graphs, and traces. **It's local-only and must never be committed.** `construct init` adds `.cx/` to your project's `.gitignore` automatically (idempotent: it won't double-add if you already have it). Daily trace shards (`.cx/traces/<date>.jsonl`) cap at 100 MB and rotate to `<date>.<n>.jsonl` so a stray commit never crosses GitHub's single-file limit. Override the cap with `CONSTRUCT_TRACE_MAX_MB`.
|
|
105
|
+
|
|
106
|
+
The embed daemon writes its supervisor stdout log to `~/.cx/runtime/embed-daemon.log`. That log rotates every minute at 50 MB and keeps 5 gzipped segments by default; override via `CONSTRUCT_EMBED_LOG_MAX_MB` and `CONSTRUCT_EMBED_LOG_MAX_SEGMENTS`.
|
|
107
|
+
|
|
96
108
|
## Core commands
|
|
97
109
|
|
|
98
110
|
<!-- AUTO:commands -->
|
|
@@ -215,16 +227,16 @@ Construct gets smarter on its own. Every session ends with an automatic capture:
|
|
|
215
227
|
<!-- AUTO:structure -->
|
|
216
228
|
```text
|
|
217
229
|
construct/
|
|
218
|
-
├── apps User-facing apps shipped from this repo (e.g. apps/docs/, the
|
|
230
|
+
├── apps User-facing apps shipped from this repo (e.g. apps/docs/, the Next.js docs site)
|
|
219
231
|
├── bin CLI entrypoint (`construct`)
|
|
220
232
|
├── commands Command prompt assets
|
|
221
233
|
├── config
|
|
222
|
-
├── dashboard
|
|
223
234
|
├── db
|
|
224
235
|
├── deploy
|
|
225
236
|
├── docs Architecture notes, runbooks, and documentation contract
|
|
226
237
|
├── examples
|
|
227
238
|
├── lib Core runtime: CLI, hooks, MCP, status, sync, workflow
|
|
239
|
+
├── packages
|
|
228
240
|
├── personas Persona prompt definitions
|
|
229
241
|
├── platforms
|
|
230
242
|
├── profiles
|
package/bin/construct
CHANGED
|
@@ -416,11 +416,190 @@ async function cmdDoctor() {
|
|
|
416
416
|
return;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
// --fix-* flags drive auto-remediation for legacy state left behind by
|
|
420
|
+
// pre-2.0 installs. Layer-2 update triggers (npm i -g postinstall, brew
|
|
421
|
+
// upgrade) normally handle this; --fix-* is the manual recovery path.
|
|
422
|
+
// --fix-all runs every available fix.
|
|
423
|
+
|
|
424
|
+
const wantsFixAll = args.includes('--fix-all') || args.includes('--fix');
|
|
425
|
+
const wantsFixGitignore = wantsFixAll || args.includes('--fix-gitignore');
|
|
426
|
+
const wantsFixLegacyAgents = wantsFixAll || args.includes('--fix-legacy-agents');
|
|
427
|
+
const wantsFixEmbedLog = wantsFixAll || args.includes('--fix-embed-log');
|
|
428
|
+
const wantsFixMigrateState = wantsFixAll || args.includes('--fix-migrate-state');
|
|
429
|
+
|
|
419
430
|
const checks = [];
|
|
420
431
|
const add = (label, pass, optional = false) => checks.push({ label, pass, optional });
|
|
421
432
|
add('registry.json exists', fs.existsSync(path.join(ROOT_DIR, 'specialists', 'registry.json')));
|
|
422
433
|
add('sync-specialists.mjs exists', fs.existsSync(path.join(ROOT_DIR, 'scripts', 'sync-specialists.mjs')));
|
|
423
434
|
|
|
435
|
+
// Dashboard static build output. The Next.js app under apps/dashboard
|
|
436
|
+
// emits a static export into lib/server/static/ via its postbuild script;
|
|
437
|
+
// an empty static dir means lib/server/index.mjs will serve 404s.
|
|
438
|
+
|
|
439
|
+
const dashboardIndex = path.join(ROOT_DIR, 'lib', 'server', 'static', 'index.html');
|
|
440
|
+
add('Dashboard built (lib/server/static/index.html)', fs.existsSync(dashboardIndex));
|
|
441
|
+
|
|
442
|
+
// Project .gitignore must cover .cx/. Auto-fix via --fix-gitignore.
|
|
443
|
+
// Only checks when cwd looks like a Construct project (has .cx/ or
|
|
444
|
+
// .construct/); non-project cwds get no warning.
|
|
445
|
+
|
|
446
|
+
const cwd = process.cwd();
|
|
447
|
+
const isProject = fs.existsSync(path.join(cwd, '.cx')) || fs.existsSync(path.join(cwd, '.construct'));
|
|
448
|
+
if (isProject) {
|
|
449
|
+
const giPath = path.join(cwd, '.gitignore');
|
|
450
|
+
const giContent = fs.existsSync(giPath) ? fs.readFileSync(giPath, 'utf8') : '';
|
|
451
|
+
const lines = giContent.split('\n').map((l) => l.trim());
|
|
452
|
+
const cxIgnored = lines.some((l) => l === '.cx' || l === '.cx/' || l === '.cx/**' || l === '*' || l === '**');
|
|
453
|
+
if (!cxIgnored && wantsFixGitignore) {
|
|
454
|
+
const prefix = giContent.length === 0 || giContent.endsWith('\n') ? '' : '\n';
|
|
455
|
+
fs.writeFileSync(giPath, giContent + `${prefix}\n# Construct runtime state — local-only, never source\n.cx/\n`, 'utf8');
|
|
456
|
+
println(` fix: appended .cx/ to ${path.relative(cwd, giPath)}`);
|
|
457
|
+
add('Project .gitignore covers .cx/', true);
|
|
458
|
+
} else {
|
|
459
|
+
add('Project .gitignore covers .cx/' + (cxIgnored ? '' : ' (run `construct doctor --fix-gitignore` to add)'), cxIgnored);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Legacy cx-* files at user scope. Layer-2 (npm postinstall, brew
|
|
464
|
+
// upgrade) sweeps these via `construct sync --global`; this check is
|
|
465
|
+
// the manual safety net. Counts files matching a registered specialist
|
|
466
|
+
// name across Claude / Codex / Copilot user-scope dirs.
|
|
467
|
+
|
|
468
|
+
const userHome = HOME;
|
|
469
|
+
const registry = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'specialists', 'registry.json'), 'utf8'));
|
|
470
|
+
const specialistSet = new Set((registry.specialists ?? []).map((s) => `cx-${s.name}`));
|
|
471
|
+
const legacyTargets = [
|
|
472
|
+
{ dir: path.join(userHome, '.claude', 'agents'), ext: '.md' },
|
|
473
|
+
{ dir: path.join(userHome, '.codex', 'agents'), ext: '.toml' },
|
|
474
|
+
{ dir: path.join(userHome, '.github', 'prompts'), ext: '.prompt.md' },
|
|
475
|
+
];
|
|
476
|
+
let legacyCount = 0;
|
|
477
|
+
for (const t of legacyTargets) {
|
|
478
|
+
if (!fs.existsSync(t.dir)) continue;
|
|
479
|
+
for (const f of fs.readdirSync(t.dir)) {
|
|
480
|
+
if (!f.endsWith(t.ext)) continue;
|
|
481
|
+
const base = f.slice(0, -t.ext.length);
|
|
482
|
+
if (specialistSet.has(base)) legacyCount++;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (legacyCount > 0 && wantsFixLegacyAgents) {
|
|
486
|
+
const syncScript = path.join(ROOT_DIR, 'scripts', 'sync-specialists.mjs');
|
|
487
|
+
const result = spawnSync(process.execPath, [syncScript, '--global'], { stdio: 'pipe', encoding: 'utf8' });
|
|
488
|
+
if (result.status === 0) {
|
|
489
|
+
println(` fix: swept ${legacyCount} legacy cx-* file(s) from user scope via construct sync --global`);
|
|
490
|
+
add('No legacy cx-* files at user scope', true);
|
|
491
|
+
} else {
|
|
492
|
+
add(`Legacy cx-* sweep failed: ${result.stderr?.substring(0, 200)}`, false);
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
// Soft-warn instead of hard-fail: legacy v1.0.10 cx-* files are an
|
|
496
|
+
// expected upgrade-state, not real drift. The hint is what matters;
|
|
497
|
+
// the fix is one --fix-legacy-agents run away and CI never sees this.
|
|
498
|
+
// Mirrors the parity check's legacy-install reclassification.
|
|
499
|
+
|
|
500
|
+
add(
|
|
501
|
+
legacyCount === 0
|
|
502
|
+
? 'No legacy cx-* files at user scope'
|
|
503
|
+
: `Legacy cx-* files at user scope: ${legacyCount} (run \`construct doctor --fix-legacy-agents\` to sweep)`,
|
|
504
|
+
true,
|
|
505
|
+
legacyCount !== 0,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// Pre-push bypass frequency. Every honored CONSTRUCT_SKIP_PREPUSH /
|
|
510
|
+
// CONSTRUCT_ALLOW_CLAUDE_PUSH / CONSTRUCT_SKIP_PR_LINT writes a JSONL
|
|
511
|
+
// entry to ~/.construct/audit/prepush-bypass.log. Surfacing frequent
|
|
512
|
+
// usage here is the early warning that the gate is the wrong shape —
|
|
513
|
+
// the alternative is letting the bypass quietly become the default.
|
|
514
|
+
|
|
515
|
+
const bypassLogPath = path.join(userHome, '.construct', 'audit', 'prepush-bypass.log');
|
|
516
|
+
if (fs.existsSync(bypassLogPath)) {
|
|
517
|
+
try {
|
|
518
|
+
const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
519
|
+
const recent = fs.readFileSync(bypassLogPath, 'utf8')
|
|
520
|
+
.split('\n')
|
|
521
|
+
.filter(Boolean)
|
|
522
|
+
.map((line) => { try { return JSON.parse(line); } catch { return null; } })
|
|
523
|
+
.filter((e) => e && Date.parse(e.ts) >= cutoff);
|
|
524
|
+
const BYPASS_WARN_THRESHOLD = Number(process.env.CONSTRUCT_DOCTOR_BYPASS_WARN) || 3;
|
|
525
|
+
if (recent.length >= BYPASS_WARN_THRESHOLD) {
|
|
526
|
+
add(
|
|
527
|
+
`Pre-push bypasses in last 7 days: ${recent.length} (gate may be wrong-sized — see ${bypassLogPath})`,
|
|
528
|
+
false,
|
|
529
|
+
true,
|
|
530
|
+
);
|
|
531
|
+
} else {
|
|
532
|
+
add(`Pre-push bypasses in last 7 days: ${recent.length}`, true);
|
|
533
|
+
}
|
|
534
|
+
} catch { /* audit log read is best-effort */ }
|
|
535
|
+
} else {
|
|
536
|
+
add('Pre-push bypass log not present (no bypasses honored yet)', true);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Embed daemon log size. The daemon's own scheduler rotates every
|
|
540
|
+
// minute at 50MB, but a freshly-upgraded install with a 34GB legacy
|
|
541
|
+
// log shouldn't wait 60s. --fix-embed-log forces rotation now.
|
|
542
|
+
|
|
543
|
+
const embedLogPath = path.join(userHome, '.cx', 'runtime', 'embed-daemon.log');
|
|
544
|
+
if (fs.existsSync(embedLogPath)) {
|
|
545
|
+
const sizeMb = fs.statSync(embedLogPath).size / 1024 / 1024;
|
|
546
|
+
const FORCE_THRESHOLD_MB = Number(process.env.CONSTRUCT_DOCTOR_EMBED_LOG_FORCE_MB) || 500;
|
|
547
|
+
if (sizeMb > FORCE_THRESHOLD_MB && wantsFixEmbedLog) {
|
|
548
|
+
const { rotateIfOversized } = await import('../lib/logging/rotate.mjs');
|
|
549
|
+
println(` fix: rotating ${sizeMb.toFixed(0)}MB embed-daemon.log (gzipping may take a moment)…`);
|
|
550
|
+
const start = Date.now();
|
|
551
|
+
const seg = await rotateIfOversized(embedLogPath, { maxBytes: 1, maxSegments: 5, gzip: true });
|
|
552
|
+
const elapsed = ((Date.now() - start) / 1000).toFixed(0);
|
|
553
|
+
println(` fix: rotated to ${path.basename(seg)} in ${elapsed}s`);
|
|
554
|
+
add('Embed daemon log size sane', true);
|
|
555
|
+
} else {
|
|
556
|
+
const label = sizeMb > FORCE_THRESHOLD_MB
|
|
557
|
+
? `Embed daemon log oversized: ${sizeMb.toFixed(0)}MB (run \`construct doctor --fix-embed-log\` to rotate now)`
|
|
558
|
+
: `Embed daemon log size: ${sizeMb.toFixed(1)}MB`;
|
|
559
|
+
add(label, sizeMb <= FORCE_THRESHOLD_MB);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Legacy ~/.cx/<project-scoped>.jsonl files left over from before the
|
|
564
|
+
// cross-project state isolation refactor. Four files moved from user
|
|
565
|
+
// scope to <project>/.cx/: contract-violations, audit-reads, agent-log,
|
|
566
|
+
// intent-verifications. Pre-refactor data still sits at ~/.cx/ where it
|
|
567
|
+
// mixed every project together. --fix-migrate-state archives them to
|
|
568
|
+
// ~/.cx/legacy/<name>.<ts>.jsonl so the new writers start fresh; the
|
|
569
|
+
// legacy data stays readable for forensics.
|
|
570
|
+
|
|
571
|
+
const projectScopedNames = [
|
|
572
|
+
'contract-violations.jsonl',
|
|
573
|
+
'audit-reads.jsonl',
|
|
574
|
+
'agent-log.jsonl',
|
|
575
|
+
'intent-verifications.jsonl',
|
|
576
|
+
];
|
|
577
|
+
const legacyState = projectScopedNames
|
|
578
|
+
.map((name) => ({ name, path: path.join(userHome, '.cx', name) }))
|
|
579
|
+
.filter((p) => fs.existsSync(p.path) && fs.statSync(p.path).size > 0);
|
|
580
|
+
|
|
581
|
+
if (legacyState.length > 0 && wantsFixMigrateState) {
|
|
582
|
+
const archiveDir = path.join(userHome, '.cx', 'legacy');
|
|
583
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
584
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
585
|
+
const moved = [];
|
|
586
|
+
for (const { name, path: src } of legacyState) {
|
|
587
|
+
const dest = path.join(archiveDir, `${name.replace('.jsonl', '')}.${ts}.jsonl`);
|
|
588
|
+
fs.renameSync(src, dest);
|
|
589
|
+
moved.push(path.basename(dest));
|
|
590
|
+
}
|
|
591
|
+
println(` fix: archived ${moved.length} legacy user-scope file(s) to ~/.cx/legacy/`);
|
|
592
|
+
for (const m of moved) println(` → ${m}`);
|
|
593
|
+
add('No legacy user-scope project-state files', true);
|
|
594
|
+
} else {
|
|
595
|
+
add(
|
|
596
|
+
legacyState.length === 0
|
|
597
|
+
? 'No legacy user-scope project-state files'
|
|
598
|
+
: `Legacy user-scope project-state files: ${legacyState.length} (run \`construct doctor --fix-migrate-state\` to archive)`,
|
|
599
|
+
legacyState.length === 0,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
424
603
|
// Tier model selection. Construct ships with no default — at least
|
|
425
604
|
// one tier must be configured (registry.json primary OR CX_MODEL_*
|
|
426
605
|
// env override) before any LLM-backed workflow can run.
|
|
@@ -704,7 +883,11 @@ async function cmdDoctor() {
|
|
|
704
883
|
try {
|
|
705
884
|
const { checkParity } = await import('../lib/parity.mjs');
|
|
706
885
|
const parity = checkParity({ rootDir: ROOT_DIR, homeDir: HOME });
|
|
707
|
-
|
|
886
|
+
// Legacy-install rolls up to ok (so the gate doesn't hard-fail during
|
|
887
|
+
// a v1.0.10 → v1.0.13+ upgrade) but renders as a warning so the
|
|
888
|
+
// --fix-legacy-agents hint stays visible.
|
|
889
|
+
const hasLegacy = parity.surfaces.some((s) => s.status === 'legacy-install');
|
|
890
|
+
add(`Cross-surface adapter parity (${parity.summary.join(' · ')})`, parity.ok, hasLegacy);
|
|
708
891
|
} catch {
|
|
709
892
|
add('Cross-surface adapter parity', false);
|
|
710
893
|
}
|
|
@@ -734,7 +917,7 @@ async function cmdDoctor() {
|
|
|
734
917
|
}
|
|
735
918
|
|
|
736
919
|
try {
|
|
737
|
-
const { recentViolations } = await import('../lib/
|
|
920
|
+
const { recentViolations } = await import('../lib/contracts/violation-log.mjs');
|
|
738
921
|
const violations = recentViolations({ windowMs: 24 * 60 * 60 * 1000 });
|
|
739
922
|
const label = violations.length === 0
|
|
740
923
|
? 'Contract violations (none in last 24h)'
|
|
@@ -1415,12 +1598,12 @@ async function cmdHandoffs(args) {
|
|
|
1415
1598
|
}
|
|
1416
1599
|
|
|
1417
1600
|
if (sub === 'validate') {
|
|
1418
|
-
const { parseHandoffFile,
|
|
1601
|
+
const { parseHandoffFile, validateHandoffFile } = await import('../lib/handoffs/contract.mjs');
|
|
1419
1602
|
if (summary.state === 'empty') { info('No handoffs to validate.'); return; }
|
|
1420
1603
|
let hasErrors = false;
|
|
1421
1604
|
for (const f of summary.files) {
|
|
1422
1605
|
const parsed = parseHandoffFile(f.path);
|
|
1423
|
-
const { valid, errors } =
|
|
1606
|
+
const { valid, errors } = validateHandoffFile(parsed);
|
|
1424
1607
|
if (valid) {
|
|
1425
1608
|
println(`✓ ${f.filename}`);
|
|
1426
1609
|
} else {
|
|
@@ -1643,6 +1826,10 @@ async function printIntakeHelp() {
|
|
|
1643
1826
|
println(' construct intake done <id> [--notes=…] [--output=<path>] [--no-output=<rejected|deferred|merged>]');
|
|
1644
1827
|
println(' Mark a pending packet processed. --output stamps intake_id + confidence into the artifact frontmatter (provenance).');
|
|
1645
1828
|
println(' construct intake skip <id> [--reason=…] Mark a pending packet skipped (preserves audit trail).');
|
|
1829
|
+
println(' construct intake process [--dry-run] [--wait[=<sec>]]');
|
|
1830
|
+
println(' One-shot inbox sweep — same logic the daemon runs, exits when the queue is empty.');
|
|
1831
|
+
println(' Concurrency is controlled by a poll lock — fail-fast by default, --wait[=<sec>] blocks for up to N seconds.');
|
|
1832
|
+
println(' --dry-run lists candidate inboxes without ingesting.');
|
|
1646
1833
|
println(' construct intake reopen <id> Move a processed or skipped packet back to pending.');
|
|
1647
1834
|
println(' construct intake quarantine list List packets held for human review (low confidence or close margin).');
|
|
1648
1835
|
println(' construct intake quarantine show <id> Show a quarantined packet with top-3 candidate types and margins.');
|
|
@@ -1910,6 +2097,10 @@ async function cmdIntake(args) {
|
|
|
1910
2097
|
return await cmdIntakeIntegrate(id, args.slice(2), cwd);
|
|
1911
2098
|
}
|
|
1912
2099
|
|
|
2100
|
+
if (sub === 'process') {
|
|
2101
|
+
return await cmdIntakeProcess(args.slice(1), cwd);
|
|
2102
|
+
}
|
|
2103
|
+
|
|
1913
2104
|
if (sub === 'config') {
|
|
1914
2105
|
return await cmdIntakeConfig(args.slice(1));
|
|
1915
2106
|
}
|
|
@@ -1922,10 +2113,75 @@ async function cmdIntake(args) {
|
|
|
1922
2113
|
return await cmdIntakeNeedsAsr(args.slice(1));
|
|
1923
2114
|
}
|
|
1924
2115
|
|
|
1925
|
-
errorln(`Unknown intake subcommand: ${sub}. Available: list, show, done, skip, reopen, quarantine, reroute, integrate, config, extraction-warnings, needs-asr`);
|
|
2116
|
+
errorln(`Unknown intake subcommand: ${sub}. Available: list, show, done, skip, reopen, process, quarantine, reroute, integrate, config, extraction-warnings, needs-asr`);
|
|
1926
2117
|
process.exit(1);
|
|
1927
2118
|
}
|
|
1928
2119
|
|
|
2120
|
+
async function cmdIntakeProcess(args, cwd) {
|
|
2121
|
+
const dryRun = args.includes('--dry-run');
|
|
2122
|
+
const waitFlag = args.find((a) => a === '--wait' || a.startsWith('--wait='));
|
|
2123
|
+
const { InboxWatcher, resolveInboxDirs } = await import('../lib/embed/inbox.mjs');
|
|
2124
|
+
|
|
2125
|
+
if (dryRun) {
|
|
2126
|
+
const dirs = resolveInboxDirs(cwd, process.env);
|
|
2127
|
+
if (dirs.length === 0) {
|
|
2128
|
+
println('No inbox directories configured. Run `construct intake config` to add one.');
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
println('Would scan:');
|
|
2132
|
+
for (const d of dirs) println(` ${d}`);
|
|
2133
|
+
println('');
|
|
2134
|
+
println('(dry-run — no files processed; re-run without --dry-run to actually ingest.)');
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// Concurrency is controlled by the intake poll lock inside
|
|
2139
|
+
// InboxWatcher.poll. Default behavior is fail-fast: if the daemon (or
|
|
2140
|
+
// another CLI invocation) is mid-poll, raise an actionable error rather
|
|
2141
|
+
// than serializing silently. --wait[=<seconds>] opts into blocking for
|
|
2142
|
+
// up to N seconds (default 30s when --wait is bare).
|
|
2143
|
+
|
|
2144
|
+
let waitMs = 0;
|
|
2145
|
+
if (waitFlag) {
|
|
2146
|
+
const m = /^--wait(?:=(\d+))?$/.exec(waitFlag);
|
|
2147
|
+
const seconds = m && m[1] ? Number(m[1]) : 30;
|
|
2148
|
+
waitMs = Math.max(0, seconds) * 1000;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
const watcher = new InboxWatcher({ rootDir: cwd, env: process.env, cwd });
|
|
2152
|
+
let result;
|
|
2153
|
+
try {
|
|
2154
|
+
result = await watcher.poll({ waitMs, actor: 'construct intake process' });
|
|
2155
|
+
} catch (err) {
|
|
2156
|
+
const { POLL_LOCK_BUSY } = await import('../lib/intake/poll-lock.mjs');
|
|
2157
|
+
if (err && err.code === POLL_LOCK_BUSY) {
|
|
2158
|
+
const holder = err.holder || {};
|
|
2159
|
+
errorln('Another intake poll is in progress.');
|
|
2160
|
+
errorln(` holder: ${holder.actor || 'unknown'} (pid ${holder.pid})`);
|
|
2161
|
+
errorln(` started: ${holder.startedAt || 'unknown'}`);
|
|
2162
|
+
errorln('');
|
|
2163
|
+
errorln('Choose one:');
|
|
2164
|
+
errorln(' construct intake process --wait Block until the holder finishes (default 30s).');
|
|
2165
|
+
errorln(' construct intake process --wait=<sec> Block for a custom window.');
|
|
2166
|
+
errorln(' construct down Stop background services, then retry.');
|
|
2167
|
+
process.exit(2);
|
|
2168
|
+
}
|
|
2169
|
+
throw err;
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
println(`Processed: ${result.processed.length}`);
|
|
2173
|
+
println(`Skipped (unchanged): ${result.skipped}`);
|
|
2174
|
+
if (result.errors.length) {
|
|
2175
|
+
println(`Errors: ${result.errors.length}`);
|
|
2176
|
+
for (const e of result.errors) println(` ${e.path}: ${e.error}`);
|
|
2177
|
+
}
|
|
2178
|
+
for (const item of result.processed) {
|
|
2179
|
+
const note = item.needsAsr ? ' (queued for ASR)' : '';
|
|
2180
|
+
println(` + ${item.path}${note}`);
|
|
2181
|
+
}
|
|
2182
|
+
if (result.errors.length) process.exitCode = 1;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1929
2185
|
async function cmdIntakeQuarantine(args, cwd) {
|
|
1930
2186
|
const action = args[0];
|
|
1931
2187
|
const { listQuarantine, readQuarantine } = await import('../lib/intake/quarantine.mjs');
|
|
@@ -4584,14 +4840,6 @@ const handlers = new Map([
|
|
|
4584
4840
|
process.exit(1);
|
|
4585
4841
|
}
|
|
4586
4842
|
}],
|
|
4587
|
-
['policy:list', async (args) => {
|
|
4588
|
-
const { listPolicies, formatPolicyList } = await import('../lib/policy/unified-gates.mjs');
|
|
4589
|
-
if (args.includes('--json')) {
|
|
4590
|
-
println(JSON.stringify(listPolicies(), null, 2));
|
|
4591
|
-
} else {
|
|
4592
|
-
process.stdout.write(formatPolicyList());
|
|
4593
|
-
}
|
|
4594
|
-
}],
|
|
4595
4843
|
['policy', async (args) => {
|
|
4596
4844
|
const sub = args[0];
|
|
4597
4845
|
if (!sub || sub === '--help' || sub === '-h') {
|
|
@@ -4602,7 +4850,7 @@ const handlers = new Map([
|
|
|
4602
4850
|
if (sub === 'show') {
|
|
4603
4851
|
const { readFileSync, existsSync } = await import('node:fs');
|
|
4604
4852
|
const { join } = await import('node:path');
|
|
4605
|
-
const inventoryPath = join(ROOT_DIR, '
|
|
4853
|
+
const inventoryPath = join(ROOT_DIR, 'specialists', 'policy-inventory.json');
|
|
4606
4854
|
if (!existsSync(inventoryPath)) {
|
|
4607
4855
|
errorln('specialists/policy-inventory.json not found');
|
|
4608
4856
|
process.exit(1);
|
|
@@ -58,8 +58,29 @@ try {
|
|
|
58
58
|
}
|
|
59
59
|
} catch { /* fall through */ }
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
61
|
+
// `npm i -g @geraldmaron/construct` runs the postinstall with
|
|
62
|
+
// npm_config_global=true. Wire the `construct` front-door agent into the
|
|
63
|
+
// user's home directories so it's reachable from every host (Claude Code,
|
|
64
|
+
// Codex, Copilot, OpenCode) immediately after a global install. Specialists
|
|
65
|
+
// stay project-only and land when the user runs `construct init` in a repo.
|
|
66
|
+
|
|
67
|
+
if (process.env.npm_config_global === 'true' || process.env.npm_config_global === true) {
|
|
68
|
+
const syncScript = path.join(PKG_ROOT, 'scripts', 'sync-specialists.mjs');
|
|
69
|
+
if (existsSync(syncScript)) {
|
|
70
|
+
log('global install detected; syncing front-door agent into ~/');
|
|
71
|
+
const result = spawnSync(process.execPath, [syncScript, '--global'], {
|
|
72
|
+
stdio: 'inherit',
|
|
73
|
+
});
|
|
74
|
+
if (result.status !== 0) {
|
|
75
|
+
log(`global sync failed (exit ${result.status}); run \`construct sync --global\` manually`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Project install path: require a package.json at the install target so
|
|
82
|
+
// monorepo nested installs don't accidentally trigger staging in the wrong
|
|
83
|
+
// directory.
|
|
63
84
|
|
|
64
85
|
const consumerPkgPath = path.join(initCwd, 'package.json');
|
|
65
86
|
if (!existsSync(consumerPkgPath)) {
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/build/feature.md (Build a feature) implement it end to end, tested and ready to ship
|
|
3
|
-
|
|
4
|
-
Build a feature: implement it end to end, tested and ready to ship
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Build a feature: implement it end to end, tested and ready to ship"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Build the following: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Before writing a line:
|
package/commands/build/fix.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/build/fix.md (Fix something broken) reproduce, find the root cause, apply the smallest safe change
|
|
3
|
-
|
|
4
|
-
Fix something broken: reproduce, find the root cause, apply the smallest safe change
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Fix something broken: reproduce, find the root cause, apply the smallest safe change"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct.
|
|
11
5
|
|
|
12
6
|
Debugging protocol for: $ARGUMENTS
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/design/access.md (Accessibility audit) WCAG 2.1 AA, keyboard, screen readers, contrast, motion
|
|
3
|
-
|
|
4
|
-
Accessibility audit: WCAG 2.1 AA, keyboard, screen readers, contrast, motion
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Accessibility audit: WCAG 2.1 AA, keyboard, screen readers, contrast, motion"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Audit: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
WCAG 2.1 AA baseline:
|
package/commands/design/flow.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/design/flow.md (Map the user flow) entry to success, friction points, jobs-to-be-done
|
|
3
|
-
|
|
4
|
-
Map the user flow: entry to success, friction points, jobs-to-be-done
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Map the user flow: entry to success, friction points, jobs-to-be-done"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Map the user flow for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Produce:
|
package/commands/design/ui.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/design/ui.md (Design or review UI) visual hierarchy, states, interaction model, accessibility baseline
|
|
3
|
-
|
|
4
|
-
Design or review UI: visual hierarchy, states, interaction model, accessibility baseline
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Design or review UI: visual hierarchy, states, interaction model, accessibility baseline"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Design or review the UI for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Every meaningful UI surface needs:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/measure/experiment.md (Design an experiment) hypothesis, test, evidence threshold, go/no-go decision
|
|
3
|
-
|
|
4
|
-
Design an experiment: hypothesis, test, evidence threshold, go/no-go decision
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Design an experiment: hypothesis, test, evidence threshold, go/no-go decision"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Design an experiment for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Produce:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/measure/metrics.md (Define metrics) what to measure, baselines, success thresholds, instrumentation needed
|
|
3
|
-
|
|
4
|
-
Define metrics: what to measure, baselines, success thresholds, instrumentation needed
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Define metrics: what to measure, baselines, success thresholds, instrumentation needed"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Define metrics for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
For each metric:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/measure/results.md (Analyze results) interpret data, separate signal from noise, produce a recommendation
|
|
3
|
-
|
|
4
|
-
Analyze results: interpret data, separate signal from noise, produce a recommendation
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Analyze results: interpret data, separate signal from noise, produce a recommendation"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Analyze: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
For each finding:
|
package/commands/plan/api.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/plan/api.md (Design an API) contracts, endpoints, error model, data schema
|
|
3
|
-
|
|
4
|
-
Design an API: contracts, endpoints, error model, data schema
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Design an API: contracts, endpoints, error model, data schema"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Design the API for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Produce:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/plan/challenge.md (Challenge a plan) stress-test assumptions, surface failure modes and risks
|
|
3
|
-
|
|
4
|
-
Challenge a plan: stress-test assumptions, surface failure modes and risks
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Challenge a plan: stress-test assumptions, surface failure modes and risks"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Challenge the following: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Challenge in severity order:
|
package/commands/plan/decide.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/plan/decide.md (Record a decision) context, options considered, consequences, what it locks in
|
|
3
|
-
|
|
4
|
-
Record a decision: context, options considered, consequences, what it locks in
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Record a decision: context, options considered, consequences, what it locks in"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Document the decision about: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
ADR format:
|
package/commands/plan/feature.md
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/plan/feature.md: Plan a feature and turn it into a tracker-linked implementation plan
|
|
3
|
-
|
|
4
|
-
Produces a structured spec using the canonical Construct plan format, saves it
|
|
5
|
-
to .cx/plans/ as a durable planning artifact linked to the active tracker slice.
|
|
6
|
-
-->
|
|
7
1
|
---
|
|
8
2
|
description: "Plan a feature: produce a structured spec and link it to the active tracker-backed plan"
|
|
9
3
|
---
|
|
10
|
-
|
|
11
4
|
You are Construct. Plan the following: $ARGUMENTS
|
|
12
5
|
|
|
13
6
|
Use the code-backed orchestration policy for routing/escalation decisions; this prompt only defines the plan artifact shape.
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/plan/requirements.md (Define requirements) what needs to be true for this to be done
|
|
3
|
-
|
|
4
|
-
Define requirements: what needs to be true for this to be done
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Define requirements: what needs to be true for this to be done"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Define requirements for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Produce:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/remember/context.md (Save project context) update .cx/context.md so the next session picks up where this one left off
|
|
3
|
-
|
|
4
|
-
Save project context: update .cx/context.md so the next session picks up where this one left off
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Save project context: update .cx/context.md so the next session picks up where this one left off"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Save context for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Update `.cx/context.json` as canonical state and refresh `.cx/context.md` as the readable mirror:
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/remember/handoff.md (Write a handoff) transfer context so the next session or person can continue without loss
|
|
3
|
-
|
|
4
|
-
Write a handoff: transfer context so the next session or person can continue without loss
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Write a handoff: transfer context so the next session or person can continue without loss"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Write a handoff for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
- CURRENT STATE: what is working, in progress, blocked
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
commands/remember/runbook.md (Write a runbook) step-by-step procedure for a recurring task or incident
|
|
3
|
-
|
|
4
|
-
Write a runbook: step-by-step procedure for a recurring task or incident
|
|
5
|
-
-->
|
|
6
1
|
---
|
|
7
2
|
description: "Write a runbook: step-by-step procedure for a recurring task or incident"
|
|
8
3
|
---
|
|
9
|
-
|
|
10
4
|
You are Construct. Write a runbook for: $ARGUMENTS
|
|
11
5
|
|
|
12
6
|
Structure:
|