@geraldmaron/construct 1.0.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/.env.example +69 -0
- package/LICENSE +21 -0
- package/README.md +344 -0
- package/agents/prompts/cx-accessibility.md +29 -0
- package/agents/prompts/cx-ai-engineer.md +31 -0
- package/agents/prompts/cx-architect.md +40 -0
- package/agents/prompts/cx-business-strategist.md +25 -0
- package/agents/prompts/cx-data-analyst.md +30 -0
- package/agents/prompts/cx-data-engineer.md +32 -0
- package/agents/prompts/cx-debugger.md +27 -0
- package/agents/prompts/cx-designer.md +33 -0
- package/agents/prompts/cx-devil-advocate.md +36 -0
- package/agents/prompts/cx-docs-keeper.md +50 -0
- package/agents/prompts/cx-engineer.md +36 -0
- package/agents/prompts/cx-evaluator.md +26 -0
- package/agents/prompts/cx-explorer.md +38 -0
- package/agents/prompts/cx-legal-compliance.md +25 -0
- package/agents/prompts/cx-operations.md +28 -0
- package/agents/prompts/cx-orchestrator.md +30 -0
- package/agents/prompts/cx-platform-engineer.md +25 -0
- package/agents/prompts/cx-product-manager.md +30 -0
- package/agents/prompts/cx-qa.md +42 -0
- package/agents/prompts/cx-rd-lead.md +25 -0
- package/agents/prompts/cx-release-manager.md +31 -0
- package/agents/prompts/cx-researcher.md +31 -0
- package/agents/prompts/cx-reviewer.md +36 -0
- package/agents/prompts/cx-security.md +37 -0
- package/agents/prompts/cx-sre.md +26 -0
- package/agents/prompts/cx-test-automation.md +35 -0
- package/agents/prompts/cx-trace-reviewer.md +76 -0
- package/agents/prompts/cx-ux-researcher.md +30 -0
- package/agents/registry.json +879 -0
- package/agents/teams.json +94 -0
- package/bin/construct +613 -0
- package/commands/build/feature.md +21 -0
- package/commands/build/fix.md +20 -0
- package/commands/design/access.md +19 -0
- package/commands/design/flow.md +16 -0
- package/commands/design/ui.md +18 -0
- package/commands/measure/experiment.md +18 -0
- package/commands/measure/metrics.md +18 -0
- package/commands/measure/results.md +18 -0
- package/commands/plan/api.md +19 -0
- package/commands/plan/challenge.md +18 -0
- package/commands/plan/decide.md +20 -0
- package/commands/plan/feature.md +49 -0
- package/commands/plan/requirements.md +24 -0
- package/commands/remember/context.md +26 -0
- package/commands/remember/handoff.md +19 -0
- package/commands/remember/runbook.md +20 -0
- package/commands/review/code.md +21 -0
- package/commands/review/quality.md +19 -0
- package/commands/review/security.md +16 -0
- package/commands/ship/ready.md +19 -0
- package/commands/ship/release.md +19 -0
- package/commands/ship/status.md +18 -0
- package/commands/understand/docs.md +22 -0
- package/commands/understand/research.md +22 -0
- package/commands/understand/this.md +23 -0
- package/commands/understand/why.md +18 -0
- package/commands/work/clean.md +32 -0
- package/commands/work/drive.md +69 -0
- package/commands/work/optimize-prompts.md +46 -0
- package/commands/work/parallel-review.md +40 -0
- package/db/migrations/001_init.sql +38 -0
- package/langfuse/docker-compose.yml +82 -0
- package/lib/audit-skills.mjs +118 -0
- package/lib/auto-docs.mjs +293 -0
- package/lib/cli-commands.mjs +409 -0
- package/lib/codex-config.mjs +114 -0
- package/lib/comment-lint.mjs +191 -0
- package/lib/completions.mjs +170 -0
- package/lib/context-state.mjs +99 -0
- package/lib/cost.mjs +213 -0
- package/lib/distill.mjs +403 -0
- package/lib/efficiency.mjs +139 -0
- package/lib/env-config.mjs +53 -0
- package/lib/eval-harness.mjs +59 -0
- package/lib/features.mjs +209 -0
- package/lib/headhunt.mjs +597 -0
- package/lib/hooks/adaptive-lint.mjs +112 -0
- package/lib/hooks/agent-tracker.mjs +50 -0
- package/lib/hooks/bootstrap-guard.mjs +90 -0
- package/lib/hooks/comment-lint.mjs +26 -0
- package/lib/hooks/config-protection.mjs +52 -0
- package/lib/hooks/console-warn.mjs +43 -0
- package/lib/hooks/context-window-recovery.mjs +90 -0
- package/lib/hooks/continuation-enforcer.mjs +72 -0
- package/lib/hooks/dep-audit.mjs +155 -0
- package/lib/hooks/drive-guard.mjs +89 -0
- package/lib/hooks/edit-accumulator.mjs +36 -0
- package/lib/hooks/edit-error-recovery.mjs +46 -0
- package/lib/hooks/edit-guard.mjs +109 -0
- package/lib/hooks/env-check.mjs +80 -0
- package/lib/hooks/guard-bash.mjs +83 -0
- package/lib/hooks/mcp-audit.mjs +57 -0
- package/lib/hooks/mcp-health-check.mjs +51 -0
- package/lib/hooks/mcp-task-scope.mjs +47 -0
- package/lib/hooks/model-fallback.mjs +105 -0
- package/lib/hooks/pre-compact.mjs +212 -0
- package/lib/hooks/pre-push-gate.mjs +129 -0
- package/lib/hooks/read-tracker.mjs +129 -0
- package/lib/hooks/registry-sync.mjs +23 -0
- package/lib/hooks/scan-secrets.mjs +76 -0
- package/lib/hooks/session-start.mjs +95 -0
- package/lib/hooks/stop-notify.mjs +191 -0
- package/lib/hooks/stop-typecheck.mjs +83 -0
- package/lib/hooks/task-completed-guard.mjs +43 -0
- package/lib/hooks/teammate-idle-guard.mjs +54 -0
- package/lib/hooks/workflow-guard.mjs +62 -0
- package/lib/host-capabilities.mjs +123 -0
- package/lib/init-docs.mjs +382 -0
- package/lib/mcp/server.mjs +1123 -0
- package/lib/mcp-catalog.json +243 -0
- package/lib/mcp-manager.mjs +433 -0
- package/lib/mcp-platform-config.mjs +104 -0
- package/lib/model-router.mjs +810 -0
- package/lib/opencode-config.mjs +44 -0
- package/lib/opencode-runtime-plugin.mjs +909 -0
- package/lib/opencode-telemetry.mjs +433 -0
- package/lib/orchestration-policy.mjs +258 -0
- package/lib/prompt-composer.mjs +196 -0
- package/lib/prompt-metadata.mjs +68 -0
- package/lib/review.mjs +429 -0
- package/lib/role-preload.mjs +52 -0
- package/lib/schemas/decision.json +50 -0
- package/lib/schemas/implementation.json +51 -0
- package/lib/schemas/review-report.json +32 -0
- package/lib/schemas/test-report.json +43 -0
- package/lib/server/index.mjs +334 -0
- package/lib/server/static/app.js +841 -0
- package/lib/server/static/index.html +820 -0
- package/lib/service-manager.mjs +225 -0
- package/lib/setup.mjs +319 -0
- package/lib/status.mjs +707 -0
- package/lib/storage/backend.mjs +44 -0
- package/lib/storage/embeddings.mjs +70 -0
- package/lib/storage/hybrid-query.mjs +184 -0
- package/lib/storage/sql-store.mjs +55 -0
- package/lib/storage/state-source.mjs +101 -0
- package/lib/storage/sync.mjs +164 -0
- package/lib/storage/vector-store.mjs +59 -0
- package/lib/telemetry/backends/langfuse.mjs +58 -0
- package/lib/telemetry/backends/noop.mjs +16 -0
- package/lib/telemetry/langfuse-ingest.mjs +108 -0
- package/lib/telemetry/langfuse-model-sync.mjs +270 -0
- package/lib/telemetry/team-rollup.mjs +143 -0
- package/lib/toolkit-env.mjs +30 -0
- package/lib/validator.mjs +181 -0
- package/lib/workflow-state.mjs +794 -0
- package/package.json +53 -0
- package/personas/construct.md +94 -0
- package/platforms/claude/CLAUDE.md +30 -0
- package/platforms/claude/settings.template.json +472 -0
- package/platforms/opencode/config.template.json +65 -0
- package/platforms/opencode/plugins/construct-fallback.js +5 -0
- package/platforms/opencode/sync-config.mjs +69 -0
- package/rules/common/agents.md +55 -0
- package/rules/common/code-review.md +129 -0
- package/rules/common/coding-style.md +95 -0
- package/rules/common/comments.md +139 -0
- package/rules/common/cx-agent-routing.md +61 -0
- package/rules/common/cx-skill-routing.md +11 -0
- package/rules/common/development-workflow.md +49 -0
- package/rules/common/git-workflow.md +29 -0
- package/rules/common/hooks.md +35 -0
- package/rules/common/patterns.md +36 -0
- package/rules/common/performance.md +71 -0
- package/rules/common/security.md +34 -0
- package/rules/common/testing.md +62 -0
- package/rules/golang/coding-style.md +37 -0
- package/rules/golang/hooks.md +22 -0
- package/rules/golang/patterns.md +50 -0
- package/rules/golang/security.md +39 -0
- package/rules/golang/testing.md +36 -0
- package/rules/python/coding-style.md +47 -0
- package/rules/python/hooks.md +24 -0
- package/rules/python/patterns.md +44 -0
- package/rules/python/security.md +35 -0
- package/rules/python/testing.md +43 -0
- package/rules/swift/coding-style.md +52 -0
- package/rules/swift/hooks.md +25 -0
- package/rules/swift/patterns.md +71 -0
- package/rules/swift/security.md +38 -0
- package/rules/swift/testing.md +50 -0
- package/rules/typescript/coding-style.md +204 -0
- package/rules/typescript/hooks.md +27 -0
- package/rules/typescript/patterns.md +57 -0
- package/rules/typescript/security.md +33 -0
- package/rules/typescript/testing.md +23 -0
- package/rules/web/coding-style.md +101 -0
- package/rules/web/design-quality.md +68 -0
- package/rules/web/hooks.md +125 -0
- package/rules/web/patterns.md +84 -0
- package/rules/web/performance.md +69 -0
- package/rules/web/security.md +62 -0
- package/rules/web/testing.md +60 -0
- package/rules/zh/README.md +113 -0
- package/rules/zh/agents.md +55 -0
- package/rules/zh/code-review.md +129 -0
- package/rules/zh/coding-style.md +53 -0
- package/rules/zh/development-workflow.md +49 -0
- package/rules/zh/git-workflow.md +29 -0
- package/rules/zh/hooks.md +35 -0
- package/rules/zh/patterns.md +36 -0
- package/rules/zh/performance.md +60 -0
- package/rules/zh/security.md +34 -0
- package/rules/zh/testing.md +34 -0
- package/skills/ai/agent-dev.md +102 -0
- package/skills/ai/llm-security.md +105 -0
- package/skills/ai/ml-ops.md +169 -0
- package/skills/ai/orchestration-workflow.md +87 -0
- package/skills/ai/prompt-and-eval.md +114 -0
- package/skills/ai/prompt-optimizer.md +114 -0
- package/skills/ai/rag-system.md +104 -0
- package/skills/architecture/api-design.md +100 -0
- package/skills/architecture/caching.md +101 -0
- package/skills/architecture/cloud-native.md +97 -0
- package/skills/architecture/message-queue.md +100 -0
- package/skills/architecture/security-arch.md +105 -0
- package/skills/development/cpp.md +127 -0
- package/skills/development/go.md +129 -0
- package/skills/development/java.md +134 -0
- package/skills/development/kotlin.md +140 -0
- package/skills/development/mobile-crossplatform.md +144 -0
- package/skills/development/python.md +109 -0
- package/skills/development/rust.md +127 -0
- package/skills/development/shell.md +127 -0
- package/skills/development/swift.md +129 -0
- package/skills/development/typescript.md +129 -0
- package/skills/devops/ci-cd.md +108 -0
- package/skills/devops/containerization.md +106 -0
- package/skills/devops/cost-optimization.md +105 -0
- package/skills/devops/data-engineering.md +181 -0
- package/skills/devops/database.md +95 -0
- package/skills/devops/dependency-management.md +91 -0
- package/skills/devops/devsecops.md +96 -0
- package/skills/devops/git-workflow.md +100 -0
- package/skills/devops/incident-response.md +167 -0
- package/skills/devops/monorepo.md +87 -0
- package/skills/devops/observability.md +103 -0
- package/skills/devops/performance.md +104 -0
- package/skills/devops/testing.md +104 -0
- package/skills/docs/adr-workflow.md +32 -0
- package/skills/docs/backlog-proposal-workflow.md +19 -0
- package/skills/docs/customer-profile-workflow.md +22 -0
- package/skills/docs/evidence-ingest-workflow.md +23 -0
- package/skills/docs/init-docs.md +160 -0
- package/skills/docs/init-project.md +50 -0
- package/skills/docs/prd-workflow.md +57 -0
- package/skills/docs/prfaq-workflow.md +25 -0
- package/skills/docs/product-intelligence-review.md +22 -0
- package/skills/docs/product-intelligence-workflow.md +62 -0
- package/skills/docs/product-signal-workflow.md +27 -0
- package/skills/docs/research-workflow.md +28 -0
- package/skills/docs/runbook-workflow.md +24 -0
- package/skills/exploration/repo-map.md +297 -0
- package/skills/frameworks/django.md +159 -0
- package/skills/frameworks/nextjs.md +148 -0
- package/skills/frameworks/react.md +132 -0
- package/skills/frameworks/spring-boot.md +165 -0
- package/skills/frontend-design/accessibility.md +153 -0
- package/skills/frontend-design/component-patterns.md +111 -0
- package/skills/frontend-design/engineering.md +122 -0
- package/skills/frontend-design/state-management.md +118 -0
- package/skills/frontend-design/ui-aesthetics.md +102 -0
- package/skills/frontend-design/ux-principles.md +126 -0
- package/skills/quality-gates/review-work.md +90 -0
- package/skills/quality-gates/verify-change.md +94 -0
- package/skills/quality-gates/verify-module.md +96 -0
- package/skills/quality-gates/verify-quality.md +93 -0
- package/skills/quality-gates/verify-security.md +95 -0
- package/skills/roles/architect.ai-systems.md +37 -0
- package/skills/roles/architect.data.md +37 -0
- package/skills/roles/architect.enterprise.md +37 -0
- package/skills/roles/architect.integration.md +37 -0
- package/skills/roles/architect.md +68 -0
- package/skills/roles/architect.platform.md +37 -0
- package/skills/roles/data-analyst.experiment.md +37 -0
- package/skills/roles/data-analyst.md +68 -0
- package/skills/roles/data-analyst.product-intelligence.md +37 -0
- package/skills/roles/data-analyst.product.md +37 -0
- package/skills/roles/data-analyst.telemetry.md +37 -0
- package/skills/roles/data-engineer.pipeline.md +37 -0
- package/skills/roles/data-engineer.vector-retrieval.md +37 -0
- package/skills/roles/data-engineer.warehouse.md +37 -0
- package/skills/roles/debugger.md +68 -0
- package/skills/roles/designer.accessibility.md +43 -0
- package/skills/roles/designer.md +68 -0
- package/skills/roles/engineer.ai.md +49 -0
- package/skills/roles/engineer.data.md +49 -0
- package/skills/roles/engineer.md +85 -0
- package/skills/roles/engineer.platform.md +49 -0
- package/skills/roles/operator.docs.md +43 -0
- package/skills/roles/operator.md +68 -0
- package/skills/roles/operator.release.md +43 -0
- package/skills/roles/operator.sre.md +43 -0
- package/skills/roles/orchestrator.md +66 -0
- package/skills/roles/product-manager.ai-product.md +37 -0
- package/skills/roles/product-manager.business-strategy.md +43 -0
- package/skills/roles/product-manager.enterprise.md +37 -0
- package/skills/roles/product-manager.growth.md +37 -0
- package/skills/roles/product-manager.md +67 -0
- package/skills/roles/product-manager.platform.md +37 -0
- package/skills/roles/product-manager.product.md +37 -0
- package/skills/roles/qa.ai-eval.md +37 -0
- package/skills/roles/qa.api-contract.md +37 -0
- package/skills/roles/qa.data-pipeline.md +37 -0
- package/skills/roles/qa.md +68 -0
- package/skills/roles/qa.test-automation.md +49 -0
- package/skills/roles/qa.web-ui.md +37 -0
- package/skills/roles/researcher.explorer.md +43 -0
- package/skills/roles/researcher.md +68 -0
- package/skills/roles/researcher.ux.md +43 -0
- package/skills/roles/reviewer.devil-advocate.md +43 -0
- package/skills/roles/reviewer.evaluator.md +43 -0
- package/skills/roles/reviewer.md +68 -0
- package/skills/roles/reviewer.trace.md +43 -0
- package/skills/roles/security.ai.md +37 -0
- package/skills/roles/security.appsec.md +37 -0
- package/skills/roles/security.cloud.md +37 -0
- package/skills/roles/security.legal-compliance.md +49 -0
- package/skills/roles/security.md +68 -0
- package/skills/roles/security.privacy.md +37 -0
- package/skills/roles/security.supply-chain.md +37 -0
- package/skills/routing.md +139 -0
- package/skills/security/blue-team.md +83 -0
- package/skills/security/code-audit.md +94 -0
- package/skills/security/pentest.md +84 -0
- package/skills/security/red-team.md +81 -0
- package/skills/security/threat-intel.md +89 -0
- package/skills/security/vuln-research.md +87 -0
- package/skills/utility/clean-code.md +165 -0
- package/sync-agents.mjs +899 -0
- package/templates/docs/adr.md +27 -0
- package/templates/docs/backlog-proposal.md +26 -0
- package/templates/docs/customer-profile.md +34 -0
- package/templates/docs/evidence-brief.md +37 -0
- package/templates/docs/incident-report.md +46 -0
- package/templates/docs/memo.md +27 -0
- package/templates/docs/meta-prd.md +56 -0
- package/templates/docs/one-pager.md +24 -0
- package/templates/docs/prd-business.md +61 -0
- package/templates/docs/prd-platform.md +62 -0
- package/templates/docs/prd.md +47 -0
- package/templates/docs/prfaq.md +31 -0
- package/templates/docs/product-intelligence-report.md +31 -0
- package/templates/docs/research-brief.md +29 -0
- package/templates/docs/rfc-platform.md +60 -0
- package/templates/docs/rfc.md +46 -0
- package/templates/docs/runbook.md +33 -0
- package/templates/docs/signal-brief.md +25 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
commands/work/parallel-review.md — Adversarial parallel review — 5 reviewers must agree before output ships
|
|
3
|
+
|
|
4
|
+
Adversarial parallel review — 5 reviewers must agree before output ships
|
|
5
|
+
-->
|
|
6
|
+
---
|
|
7
|
+
description: Adversarial parallel review — 5 reviewers must agree before output ships
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
You are Construct running a parallel adversarial review of: $ARGUMENTS
|
|
11
|
+
|
|
12
|
+
Dispatch the following 5 review roles concurrently:
|
|
13
|
+
|
|
14
|
+
1. **cx-reviewer** — Correctness and logic: does it do what it claims? Are there off-by-ones, edge cases, or control flow bugs?
|
|
15
|
+
2. **cx-security** — Vulnerabilities and data exposure: injection, auth bypass, secret leakage, SSRF, unvalidated input
|
|
16
|
+
3. **cx-qa** — Test coverage and edge cases: what's untested? What inputs would break this?
|
|
17
|
+
4. **cx-devil-advocate** — Assumption stress-test: what are we assuming that could be wrong? What failure modes are unaddressed?
|
|
18
|
+
5. **cx-accessibility** (UI changes) or **cx-trace-reviewer** (non-UI) — Inclusive UX or performance bottlenecks
|
|
19
|
+
|
|
20
|
+
## Merge Gate
|
|
21
|
+
|
|
22
|
+
All 5 roles must return **PASS** before output ships.
|
|
23
|
+
|
|
24
|
+
| Finding severity | Action |
|
|
25
|
+
|---|---|
|
|
26
|
+
| CRITICAL | Blocks merge. Must fix before proceeding. |
|
|
27
|
+
| HIGH | Blocks merge. Must fix before proceeding. |
|
|
28
|
+
| MEDIUM | Requires explicit acknowledgment. Document why it's acceptable or fix it. |
|
|
29
|
+
| LOW | Informational only. No action required. |
|
|
30
|
+
|
|
31
|
+
## Output Format
|
|
32
|
+
|
|
33
|
+
For each role:
|
|
34
|
+
```
|
|
35
|
+
[ROLE] PASS | FAIL
|
|
36
|
+
Findings:
|
|
37
|
+
- [severity] description
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Final verdict: **MERGE READY** or **BLOCKED** (list blocking findings)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
create table if not exists construct_documents (
|
|
2
|
+
id text primary key,
|
|
3
|
+
project text not null,
|
|
4
|
+
kind text not null,
|
|
5
|
+
title text not null,
|
|
6
|
+
summary text,
|
|
7
|
+
body text not null,
|
|
8
|
+
source_path text,
|
|
9
|
+
tags jsonb not null default '[]'::jsonb,
|
|
10
|
+
content_hash text not null,
|
|
11
|
+
updated_at timestamptz not null default now(),
|
|
12
|
+
created_at timestamptz not null default now()
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
create index if not exists construct_documents_project_kind_idx on construct_documents (project, kind);
|
|
16
|
+
create index if not exists construct_documents_content_hash_idx on construct_documents (content_hash);
|
|
17
|
+
|
|
18
|
+
create table if not exists construct_embeddings (
|
|
19
|
+
document_id text primary key references construct_documents(id) on delete cascade,
|
|
20
|
+
model text not null,
|
|
21
|
+
embedding double precision[] not null,
|
|
22
|
+
content_hash text not null,
|
|
23
|
+
updated_at timestamptz not null default now(),
|
|
24
|
+
created_at timestamptz not null default now()
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
create index if not exists construct_embeddings_model_idx on construct_embeddings (model);
|
|
28
|
+
|
|
29
|
+
create table if not exists construct_sync_runs (
|
|
30
|
+
id bigserial primary key,
|
|
31
|
+
project text not null,
|
|
32
|
+
source text not null,
|
|
33
|
+
documents_synced integer not null default 0,
|
|
34
|
+
embeddings_synced integer not null default 0,
|
|
35
|
+
status text not null,
|
|
36
|
+
note text,
|
|
37
|
+
created_at timestamptz not null default now()
|
|
38
|
+
);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Construct — Langfuse Self-Hosted
|
|
2
|
+
# Auto-provisioned: keys are seeded at container startup via LANGFUSE_INIT_* vars.
|
|
3
|
+
# No browser setup required. 'construct setup' writes keys to .env automatically.
|
|
4
|
+
#
|
|
5
|
+
# Manual usage:
|
|
6
|
+
# docker-compose -f langfuse/docker-compose.yml -p construct-langfuse up -d
|
|
7
|
+
# # Keys are ready immediately — see LANGFUSE_INIT_PROJECT_* below
|
|
8
|
+
|
|
9
|
+
name: construct-langfuse
|
|
10
|
+
|
|
11
|
+
services:
|
|
12
|
+
langfuse-server:
|
|
13
|
+
image: langfuse/langfuse:2
|
|
14
|
+
container_name: construct-langfuse
|
|
15
|
+
restart: unless-stopped
|
|
16
|
+
depends_on:
|
|
17
|
+
langfuse-db:
|
|
18
|
+
condition: service_healthy
|
|
19
|
+
ports:
|
|
20
|
+
- "3000:3000"
|
|
21
|
+
environment:
|
|
22
|
+
DATABASE_URL: postgresql://langfuse:langfuse@langfuse-db:5432/langfuse
|
|
23
|
+
NEXTAUTH_SECRET: ${LANGFUSE_NEXTAUTH_SECRET:-construct-nextauth-secret}
|
|
24
|
+
SALT: ${LANGFUSE_SALT:-construct-salt-value}
|
|
25
|
+
NEXTAUTH_URL: http://localhost:3000
|
|
26
|
+
TELEMETRY_ENABLED: "false"
|
|
27
|
+
LANGFUSE_ENABLE_EXPERIMENTAL_FEATURES: "true"
|
|
28
|
+
AUTH_DISABLE_SIGNUP: "false"
|
|
29
|
+
# ── Auto-provisioning (no browser setup needed) ──────────────────
|
|
30
|
+
LANGFUSE_INIT_ORG_ID: "construct-org"
|
|
31
|
+
LANGFUSE_INIT_ORG_NAME: "Construct"
|
|
32
|
+
LANGFUSE_INIT_PROJECT_ID: "construct-project"
|
|
33
|
+
LANGFUSE_INIT_PROJECT_NAME: "Construct"
|
|
34
|
+
LANGFUSE_INIT_PROJECT_PUBLIC_KEY: "pk-lf-construct-local"
|
|
35
|
+
LANGFUSE_INIT_PROJECT_SECRET_KEY: "sk-lf-construct-local"
|
|
36
|
+
LANGFUSE_INIT_USER_EMAIL: "admin@construct.local"
|
|
37
|
+
LANGFUSE_INIT_USER_NAME: "Admin"
|
|
38
|
+
LANGFUSE_INIT_USER_PASSWORD: "construct-admin"
|
|
39
|
+
healthcheck:
|
|
40
|
+
test: ["CMD-SHELL", "curl -sf http://localhost:3000/api/public/health || exit 1"]
|
|
41
|
+
interval: 10s
|
|
42
|
+
timeout: 5s
|
|
43
|
+
retries: 10
|
|
44
|
+
start_period: 30s
|
|
45
|
+
|
|
46
|
+
langfuse-db:
|
|
47
|
+
image: postgres:15-alpine
|
|
48
|
+
container_name: construct-langfuse-db
|
|
49
|
+
restart: unless-stopped
|
|
50
|
+
environment:
|
|
51
|
+
POSTGRES_USER: langfuse
|
|
52
|
+
POSTGRES_PASSWORD: langfuse
|
|
53
|
+
POSTGRES_DB: langfuse
|
|
54
|
+
volumes:
|
|
55
|
+
- langfuse_db_data:/var/lib/postgresql/data
|
|
56
|
+
healthcheck:
|
|
57
|
+
test: ["CMD-SHELL", "pg_isready -U langfuse -d langfuse"]
|
|
58
|
+
interval: 3s
|
|
59
|
+
timeout: 3s
|
|
60
|
+
retries: 10
|
|
61
|
+
|
|
62
|
+
dspy-optimizer:
|
|
63
|
+
build:
|
|
64
|
+
context: ./dspy-optimizer
|
|
65
|
+
dockerfile: Dockerfile
|
|
66
|
+
container_name: construct-dspy-optimizer
|
|
67
|
+
restart: "no"
|
|
68
|
+
profiles:
|
|
69
|
+
- optimize
|
|
70
|
+
env_file:
|
|
71
|
+
- ../.env
|
|
72
|
+
environment:
|
|
73
|
+
LANGFUSE_BASEURL: http://langfuse-server:3000
|
|
74
|
+
depends_on:
|
|
75
|
+
langfuse-server:
|
|
76
|
+
condition: service_healthy
|
|
77
|
+
volumes:
|
|
78
|
+
- ../.cx:/app/.cx
|
|
79
|
+
|
|
80
|
+
volumes:
|
|
81
|
+
langfuse_db_data:
|
|
82
|
+
driver: local
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/audit-skills.mjs — Audit agent↔skill bindings for orphans and missing files.
|
|
3
|
+
*
|
|
4
|
+
* Reports: (a) skills with no agent owner, (b) agents with no skill bindings,
|
|
5
|
+
* (c) skill paths declared in registry but missing on disk.
|
|
6
|
+
* Called by 'construct audit skills' and incorporated into 'construct doctor'.
|
|
7
|
+
*/
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
|
|
11
|
+
function findConstructRoot(startPath = process.cwd()) {
|
|
12
|
+
let current = path.resolve(startPath);
|
|
13
|
+
while (true) {
|
|
14
|
+
if (fs.existsSync(path.join(current, 'agents', 'registry.json'))) return current;
|
|
15
|
+
const parent = path.dirname(current);
|
|
16
|
+
if (parent === current) return path.resolve(startPath);
|
|
17
|
+
current = parent;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function collectSkillFiles(skillsDir) {
|
|
22
|
+
const results = new Set();
|
|
23
|
+
function walk(dir, prefix = '') {
|
|
24
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
walk(path.join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
27
|
+
} else if (entry.name.endsWith('.md') && entry.name !== 'routing.md') {
|
|
28
|
+
const rel = prefix ? `${prefix}/${entry.name.replace(/\.md$/, '')}` : entry.name.replace(/\.md$/, '');
|
|
29
|
+
results.add(rel);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try { walk(skillsDir); } catch { /* skills dir missing */ }
|
|
34
|
+
return results;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function auditSkills({ rootDir, silent = false } = {}) {
|
|
38
|
+
const root = rootDir ?? findConstructRoot();
|
|
39
|
+
const registryPath = path.join(root, 'agents', 'registry.json');
|
|
40
|
+
const skillsDir = path.join(root, 'skills');
|
|
41
|
+
|
|
42
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
43
|
+
const allSkillFiles = collectSkillFiles(skillsDir);
|
|
44
|
+
|
|
45
|
+
const declaredSkills = new Set();
|
|
46
|
+
const agentsWithNoSkills = [];
|
|
47
|
+
const missingSkillFiles = [];
|
|
48
|
+
|
|
49
|
+
for (const agent of registry.agents ?? []) {
|
|
50
|
+
const skills = agent.skills ?? [];
|
|
51
|
+
if (skills.length === 0) {
|
|
52
|
+
agentsWithNoSkills.push(agent.name);
|
|
53
|
+
}
|
|
54
|
+
for (const skill of skills) {
|
|
55
|
+
declaredSkills.add(skill);
|
|
56
|
+
if (!allSkillFiles.has(skill)) {
|
|
57
|
+
missingSkillFiles.push({ agent: agent.name, skill });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const orphanSkills = [...allSkillFiles].filter((s) => !declaredSkills.has(s));
|
|
63
|
+
|
|
64
|
+
const issues = [];
|
|
65
|
+
if (agentsWithNoSkills.length > 0) issues.push({ kind: 'agents-no-skills', items: agentsWithNoSkills });
|
|
66
|
+
if (orphanSkills.length > 0) issues.push({ kind: 'orphan-skills', items: orphanSkills });
|
|
67
|
+
if (missingSkillFiles.length > 0) issues.push({ kind: 'missing-skill-files', items: missingSkillFiles });
|
|
68
|
+
|
|
69
|
+
if (!silent) {
|
|
70
|
+
const line = (msg) => process.stdout.write(`${msg}\n`);
|
|
71
|
+
line('Construct Skill Audit');
|
|
72
|
+
line('═════════════════════');
|
|
73
|
+
line('');
|
|
74
|
+
|
|
75
|
+
if (agentsWithNoSkills.length === 0) {
|
|
76
|
+
line(' ✓ All agents have at least one skill binding');
|
|
77
|
+
} else {
|
|
78
|
+
line(` ⚠ Agents with no skill bindings (${agentsWithNoSkills.length}):`);
|
|
79
|
+
for (const n of agentsWithNoSkills) line(` - cx-${n}`);
|
|
80
|
+
}
|
|
81
|
+
line('');
|
|
82
|
+
|
|
83
|
+
if (orphanSkills.length === 0) {
|
|
84
|
+
line(' ✓ All skills have at least one agent owner');
|
|
85
|
+
} else {
|
|
86
|
+
line(` ⚠ Skills with no agent owner (${orphanSkills.length}):`);
|
|
87
|
+
for (const s of orphanSkills) line(` - skills/${s}.md`);
|
|
88
|
+
}
|
|
89
|
+
line('');
|
|
90
|
+
|
|
91
|
+
if (missingSkillFiles.length === 0) {
|
|
92
|
+
line(' ✓ All declared skill paths exist on disk');
|
|
93
|
+
} else {
|
|
94
|
+
line(` ✗ Declared skills missing on disk (${missingSkillFiles.length}):`);
|
|
95
|
+
for (const { agent, skill } of missingSkillFiles) line(` - cx-${agent} → skills/${skill}.md`);
|
|
96
|
+
}
|
|
97
|
+
line('');
|
|
98
|
+
|
|
99
|
+
const hasErrors = missingSkillFiles.length > 0;
|
|
100
|
+
const hasWarnings = agentsWithNoSkills.length > 0 || orphanSkills.length > 0;
|
|
101
|
+
if (hasErrors) line(' Result: FAIL — fix missing skill files before syncing');
|
|
102
|
+
else if (hasWarnings) line(' Result: WARN — bindings incomplete but system is functional');
|
|
103
|
+
else line(' Result: PASS');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
agentsWithNoSkills,
|
|
108
|
+
orphanSkills,
|
|
109
|
+
missingSkillFiles,
|
|
110
|
+
pass: missingSkillFiles.length === 0,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function runAuditSkillsCli(args = []) {
|
|
115
|
+
const rootDir = args.find((a) => a.startsWith('--root='))?.split('=')[1] ?? undefined;
|
|
116
|
+
const result = auditSkills({ rootDir });
|
|
117
|
+
if (!result.pass) process.exit(1);
|
|
118
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/auto-docs.mjs — regenerate managed regions in markdown docs and build the MkDocs site tree.
|
|
3
|
+
*
|
|
4
|
+
* Managed regions are HTML comment markers in the form:
|
|
5
|
+
* <!-- AUTO:region-name -->
|
|
6
|
+
* content
|
|
7
|
+
* <!-- /AUTO:region-name -->
|
|
8
|
+
*
|
|
9
|
+
* Running regenerateDocs() is idempotent. With check:true it returns whether
|
|
10
|
+
* anything would change without writing files, used by CI to detect drift.
|
|
11
|
+
*
|
|
12
|
+
* buildSite() writes site/docs/ from the same sources feeding the AUTO regions
|
|
13
|
+
* so the GitHub Pages site never drifts from the in-repo markdown.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { execSync } from 'node:child_process';
|
|
19
|
+
|
|
20
|
+
import { CLI_COMMANDS_BY_CATEGORY, CATEGORY_ORDER } from './cli-commands.mjs';
|
|
21
|
+
|
|
22
|
+
// --- region helpers ---
|
|
23
|
+
|
|
24
|
+
function replaceRegion(content, regionName, newBody) {
|
|
25
|
+
const open = `<!-- AUTO:${regionName} -->`;
|
|
26
|
+
const close = `<!-- /AUTO:${regionName} -->`;
|
|
27
|
+
const before = content.indexOf(open);
|
|
28
|
+
const after = content.indexOf(close);
|
|
29
|
+
if (before === -1 || after === -1) return null;
|
|
30
|
+
return content.slice(0, before + open.length) + '\n' + newBody + '\n' + content.slice(after);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readFile(p) {
|
|
34
|
+
try { return fs.readFileSync(p, 'utf8'); } catch { return null; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function writeIfChanged(filePath, newContent) {
|
|
38
|
+
const existing = readFile(filePath);
|
|
39
|
+
if (existing === newContent) return false;
|
|
40
|
+
fs.writeFileSync(filePath, newContent, 'utf8');
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// --- region generators ---
|
|
45
|
+
|
|
46
|
+
function buildCommandsTable() {
|
|
47
|
+
const rows = [];
|
|
48
|
+
for (const category of CATEGORY_ORDER) {
|
|
49
|
+
const cmds = CLI_COMMANDS_BY_CATEGORY[category] ?? [];
|
|
50
|
+
if (!cmds.length) continue;
|
|
51
|
+
rows.push(`### ${category}\n`);
|
|
52
|
+
rows.push('| Command | What it does |');
|
|
53
|
+
rows.push('|---|---|');
|
|
54
|
+
for (const cmd of cmds) {
|
|
55
|
+
rows.push(`| \`construct ${cmd.name}\` | ${cmd.description} |`);
|
|
56
|
+
}
|
|
57
|
+
rows.push('');
|
|
58
|
+
}
|
|
59
|
+
return rows.join('\n').trimEnd();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildCoreDocsContract() {
|
|
63
|
+
return [
|
|
64
|
+
'## Required core documents',
|
|
65
|
+
'',
|
|
66
|
+
'| File | Purpose | Update when |',
|
|
67
|
+
'|---|---|---|',
|
|
68
|
+
'| `.cx/context.md` | Human-readable resumable project context | Active work, decisions, architecture assumptions, or open questions change |',
|
|
69
|
+
'| `.cx/context.json` | Machine-readable resumable context | Context state needs to stay in sync with `.cx/context.md` |',
|
|
70
|
+
'| `.cx/workflow.json` | Canonical workflow/task state | Non-trivial work starts, changes phase, or completes |',
|
|
71
|
+
'| `docs/README.md` | Docs index and maintenance contract | Core docs set or maintenance expectations change |',
|
|
72
|
+
'| `docs/architecture.md` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
|
|
73
|
+
'',
|
|
74
|
+
'All LLMs working in the repo, including Construct, must read these as project state and keep them current when work changes project reality.',
|
|
75
|
+
].join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const DIR_DESCRIPTIONS = {
|
|
79
|
+
agents: 'Registry and generated platform adapter chains',
|
|
80
|
+
bin: 'CLI entrypoint (`construct`)',
|
|
81
|
+
claude: 'Claude Code integration (agents, settings template)',
|
|
82
|
+
commands: 'Command prompt assets',
|
|
83
|
+
codex: 'GitHub Copilot / Codex integration',
|
|
84
|
+
docs: 'Architecture notes, runbooks, and documentation contract',
|
|
85
|
+
langfuse: 'Langfuse trace backend for agent observability',
|
|
86
|
+
lib: 'Core runtime: CLI, hooks, MCP, status, sync, workflow',
|
|
87
|
+
opencode: 'OpenCode integration config',
|
|
88
|
+
personas: 'Persona prompt definitions',
|
|
89
|
+
rules: 'Coding and quality standards',
|
|
90
|
+
site: 'MkDocs source for the GitHub Pages documentation site',
|
|
91
|
+
skills: 'Reusable domain knowledge files',
|
|
92
|
+
tests: 'Test suite',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function buildStructureSection(rootDir) {
|
|
96
|
+
let trackedDirs;
|
|
97
|
+
try {
|
|
98
|
+
const out = execSync('git ls-tree --name-only HEAD', { cwd: rootDir, encoding: 'utf8' });
|
|
99
|
+
trackedDirs = new Set(out.trim().split('\n').filter(Boolean));
|
|
100
|
+
} catch {
|
|
101
|
+
trackedDirs = null;
|
|
102
|
+
}
|
|
103
|
+
const entries = fs.readdirSync(rootDir, { withFileTypes: true })
|
|
104
|
+
.filter(e => e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules' && (trackedDirs === null || trackedDirs.has(e.name)))
|
|
105
|
+
.sort((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()));
|
|
106
|
+
|
|
107
|
+
const lines = ['```text', 'construct/'];
|
|
108
|
+
for (const entry of entries) {
|
|
109
|
+
const desc = DIR_DESCRIPTIONS[entry.name] ?? '';
|
|
110
|
+
lines.push(`├── ${entry.name.padEnd(16)} ${desc}`.trimEnd());
|
|
111
|
+
}
|
|
112
|
+
lines.push('```');
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractHookSummary(hookPath) {
|
|
117
|
+
try {
|
|
118
|
+
const src = fs.readFileSync(hookPath, 'utf8');
|
|
119
|
+
const match = src.match(/\/\*\*[\s\S]*?\*\//);
|
|
120
|
+
if (!match) return '';
|
|
121
|
+
const block = match[0].replace(/^\/\*\*|\*\/$/g, '').trim();
|
|
122
|
+
const lines = block.split('\n').map(l => l.replace(/^\s*\*\s?/, '').trim());
|
|
123
|
+
const purposeLine = lines.find(l => l.includes('—'));
|
|
124
|
+
if (purposeLine) {
|
|
125
|
+
const idx = purposeLine.indexOf('—');
|
|
126
|
+
return purposeLine.slice(idx + 1).trim();
|
|
127
|
+
}
|
|
128
|
+
return lines.find(l => l.length > 0) ?? '';
|
|
129
|
+
} catch { return ''; }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildHooksTable(rootDir) {
|
|
133
|
+
const hooksDir = path.join(rootDir, 'lib', 'hooks');
|
|
134
|
+
const files = fs.readdirSync(hooksDir)
|
|
135
|
+
.filter(f => f.endsWith('.mjs'))
|
|
136
|
+
.sort();
|
|
137
|
+
|
|
138
|
+
const rows = ['| Hook | Description |', '|---|---|'];
|
|
139
|
+
for (const f of files) {
|
|
140
|
+
const name = f.replace(/\.mjs$/, '');
|
|
141
|
+
const desc = extractHookSummary(path.join(hooksDir, f));
|
|
142
|
+
rows.push(`| \`${name}\` | ${desc} |`);
|
|
143
|
+
}
|
|
144
|
+
return rows.join('\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildAgentsTable(rootDir) {
|
|
148
|
+
const registryPath = path.join(rootDir, 'agents', 'registry.json');
|
|
149
|
+
let registry;
|
|
150
|
+
try { registry = JSON.parse(fs.readFileSync(registryPath, 'utf8')); } catch { return ''; }
|
|
151
|
+
|
|
152
|
+
const agents = registry.agents ?? registry;
|
|
153
|
+
if (!Array.isArray(agents)) return '';
|
|
154
|
+
|
|
155
|
+
const rows = ['| Agent | Tier | Purpose |', '|---|---|---|'];
|
|
156
|
+
for (const agent of agents.slice(0, 30)) {
|
|
157
|
+
const name = agent.id ?? agent.name ?? '—';
|
|
158
|
+
const tier = agent.tier ?? agent.model_tier ?? '—';
|
|
159
|
+
const purpose = (agent.description ?? agent.purpose ?? '').split('\n')[0].slice(0, 80);
|
|
160
|
+
rows.push(`| \`${name}\` | ${tier} | ${purpose} |`);
|
|
161
|
+
}
|
|
162
|
+
if (agents.length > 30) rows.push(`| *(+${agents.length - 30} more)* | | |`);
|
|
163
|
+
return rows.join('\n');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// --- public API ---
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Regenerate all AUTO regions in README.md and docs/README.md/docs/architecture.md.
|
|
170
|
+
* Returns { changed: string[], checked: boolean }.
|
|
171
|
+
* With check:true writes nothing and sets changed to files that would differ.
|
|
172
|
+
*/
|
|
173
|
+
export async function regenerateDocs({ rootDir, check = false } = {}) {
|
|
174
|
+
rootDir = rootDir ?? process.cwd();
|
|
175
|
+
const changed = [];
|
|
176
|
+
|
|
177
|
+
const jobs = [
|
|
178
|
+
{
|
|
179
|
+
file: path.join(rootDir, 'README.md'),
|
|
180
|
+
regions: {
|
|
181
|
+
commands: buildCommandsTable(),
|
|
182
|
+
structure: buildStructureSection(rootDir),
|
|
183
|
+
hooks: buildHooksTable(rootDir),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
file: path.join(rootDir, 'docs', 'architecture.md'),
|
|
188
|
+
regions: {
|
|
189
|
+
agents: buildAgentsTable(rootDir),
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
file: path.join(rootDir, 'docs', 'README.md'),
|
|
194
|
+
regions: {
|
|
195
|
+
'core-docs': buildCoreDocsContract(),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
for (const { file, regions } of jobs) {
|
|
201
|
+
let content = readFile(file);
|
|
202
|
+
if (!content) continue;
|
|
203
|
+
let modified = false;
|
|
204
|
+
for (const [regionName, body] of Object.entries(regions)) {
|
|
205
|
+
if (!body) continue;
|
|
206
|
+
const updated = replaceRegion(content, regionName, body);
|
|
207
|
+
if (updated === null) continue;
|
|
208
|
+
if (updated !== content) {
|
|
209
|
+
content = updated;
|
|
210
|
+
modified = true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (!modified) continue;
|
|
214
|
+
if (check) {
|
|
215
|
+
changed.push(file);
|
|
216
|
+
} else {
|
|
217
|
+
writeIfChanged(file, content);
|
|
218
|
+
changed.push(file);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { changed, checked: check };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Write site/docs/ from the same generators feeding the AUTO regions.
|
|
227
|
+
* Invoked by `construct docs:site` before `mkdocs build`.
|
|
228
|
+
*/
|
|
229
|
+
export async function buildSite({ rootDir } = {}) {
|
|
230
|
+
rootDir = rootDir ?? process.cwd();
|
|
231
|
+
const siteDocsDir = path.join(rootDir, 'site', 'docs');
|
|
232
|
+
fs.mkdirSync(siteDocsDir, { recursive: true });
|
|
233
|
+
fs.mkdirSync(path.join(siteDocsDir, 'templates'), { recursive: true });
|
|
234
|
+
|
|
235
|
+
const readOrEmpty = (p) => readFile(p) ?? '';
|
|
236
|
+
const writeTransformed = (src, dest, transform = (content) => content) => {
|
|
237
|
+
const content = readFile(src);
|
|
238
|
+
if (content) fs.writeFileSync(path.join(siteDocsDir, dest), transform(content));
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// index.md — strip the "For contributors" section onwards for a user-facing landing
|
|
242
|
+
const readme = readOrEmpty(path.join(rootDir, 'README.md'));
|
|
243
|
+
const cutAt = readme.indexOf('## For contributors');
|
|
244
|
+
const index = cutAt > 0 ? readme.slice(0, cutAt).trimEnd() + '\n' : readme;
|
|
245
|
+
fs.writeFileSync(path.join(siteDocsDir, 'index.md'), index);
|
|
246
|
+
|
|
247
|
+
// commands.md — full CLI reference
|
|
248
|
+
const cmdLines = [
|
|
249
|
+
'<!--\nsite/docs/commands.md — full CLI reference, generated by construct docs:site.\nSource: lib/cli-commands.mjs\n-->\n',
|
|
250
|
+
'# Command reference\n',
|
|
251
|
+
buildCommandsTable(),
|
|
252
|
+
];
|
|
253
|
+
fs.writeFileSync(path.join(siteDocsDir, 'commands.md'), cmdLines.join('\n'));
|
|
254
|
+
|
|
255
|
+
// hooks.md
|
|
256
|
+
const hooksLines = [
|
|
257
|
+
'<!--\nsite/docs/hooks.md — hook reference, generated by construct docs:site.\nSource: lib/hooks/\n-->\n',
|
|
258
|
+
'# Hooks reference\n',
|
|
259
|
+
'Hooks run in every Claude Code session. They are registered in `claude/settings.template.json`.\n',
|
|
260
|
+
buildHooksTable(rootDir),
|
|
261
|
+
];
|
|
262
|
+
fs.writeFileSync(path.join(siteDocsDir, 'hooks.md'), hooksLines.join('\n'));
|
|
263
|
+
|
|
264
|
+
// agents.md
|
|
265
|
+
const agentsTable = buildAgentsTable(rootDir);
|
|
266
|
+
if (agentsTable) {
|
|
267
|
+
const agentLines = [
|
|
268
|
+
'<!--\nsite/docs/agents.md — agent reference, generated by construct docs:site.\nSource: agents/registry.json\n-->\n',
|
|
269
|
+
'# Agent reference\n',
|
|
270
|
+
agentsTable,
|
|
271
|
+
];
|
|
272
|
+
fs.writeFileSync(path.join(siteDocsDir, 'agents.md'), agentLines.join('\n'));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// copy static docs
|
|
276
|
+
for (const [src, dest] of [
|
|
277
|
+
[path.join(rootDir, 'docs', 'architecture.md'), 'architecture.md'],
|
|
278
|
+
[path.join(rootDir, 'CONTRIBUTING.md'), 'contributing.md'],
|
|
279
|
+
[path.join(rootDir, 'CLAUDE.md'), 'claude.md'],
|
|
280
|
+
]) {
|
|
281
|
+
const content = readFile(src);
|
|
282
|
+
if (!content) continue;
|
|
283
|
+
const transformed = dest === 'contributing.md'
|
|
284
|
+
? content.replaceAll('[CLAUDE.md](CLAUDE.md)', '[CLAUDE.md](claude.md)')
|
|
285
|
+
: dest === 'claude.md'
|
|
286
|
+
? content.replaceAll('[docs/templates/README.md](docs/templates/README.md)', '[docs/templates/README.md](templates/README.md)')
|
|
287
|
+
: content;
|
|
288
|
+
fs.writeFileSync(path.join(siteDocsDir, dest), transformed);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
writeTransformed(path.join(rootDir, 'docs', 'templates', 'README.md'), path.join('templates', 'README.md'));
|
|
292
|
+
|
|
293
|
+
}
|