@geraldmaron/construct 1.0.2 → 1.0.4
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 +1 -1
- package/README.md +4 -4
- package/agents/prompts/cx-ai-engineer.md +6 -26
- package/agents/prompts/cx-architect.md +1 -0
- package/agents/prompts/cx-business-strategist.md +2 -0
- package/agents/prompts/cx-data-analyst.md +6 -26
- package/agents/prompts/cx-docs-keeper.md +1 -31
- package/agents/prompts/cx-explorer.md +1 -0
- package/agents/prompts/cx-orchestrator.md +40 -112
- package/agents/prompts/cx-platform-engineer.md +2 -22
- package/agents/prompts/cx-product-manager.md +2 -1
- package/agents/prompts/cx-qa.md +0 -20
- package/agents/prompts/cx-rd-lead.md +2 -0
- package/agents/prompts/cx-researcher.md +77 -31
- package/agents/prompts/cx-security.md +11 -49
- package/agents/prompts/cx-sre.md +9 -43
- package/agents/prompts/cx-ux-researcher.md +1 -0
- package/agents/role-manifests.json +4 -4
- package/bin/construct +72 -11
- package/bin/construct-postinstall.mjs +1 -1
- package/db/schema/004_recommendations.sql +46 -0
- package/db/schema/005_strategy.sql +21 -0
- package/lib/auto-docs.mjs +1 -2
- package/lib/beads-automation.mjs +16 -7
- package/lib/bootstrap/resources.mjs +2 -2
- package/lib/cli-commands.mjs +8 -2
- package/lib/document-ingest.mjs +6 -5
- package/lib/embed/cli.mjs +16 -3
- package/lib/embed/conflict-detection.mjs +26 -9
- package/lib/embed/customer-profiles.mjs +38 -18
- package/lib/embed/daemon.mjs +59 -50
- package/lib/embed/inbox.mjs +30 -0
- package/lib/embed/recommendation-store.mjs +214 -15
- package/lib/embed/workspaces.mjs +53 -18
- package/lib/evaluator-optimizer.mjs +0 -2
- package/lib/features.mjs +11 -0
- package/lib/gates-audit.mjs +3 -3
- package/lib/health-check.mjs +3 -5
- package/lib/hooks/pre-compact.mjs +3 -0
- package/lib/hooks/read-tracker.mjs +10 -101
- package/lib/host-capabilities.mjs +90 -1
- package/lib/init-unified.mjs +119 -3
- package/lib/init-update.mjs +246 -131
- package/lib/install/first-invocation.mjs +4 -4
- package/lib/intake/queue.mjs +1 -1
- package/lib/integrations/intake-integrations.mjs +4 -5
- package/lib/intent-classifier.mjs +1 -0
- package/lib/knowledge/layout.mjs +10 -0
- package/lib/knowledge/rag.mjs +16 -0
- package/lib/mcp/tools/telemetry.mjs +30 -78
- package/lib/model-cheapest-provider.mjs +231 -0
- package/lib/model-router.mjs +68 -9
- package/lib/ollama-manager.mjs +1 -1
- package/lib/opencode-telemetry.mjs +4 -5
- package/lib/orchestration-policy.mjs +9 -0
- package/lib/parity.mjs +124 -21
- package/lib/project-profile.mjs +1 -1
- package/lib/prompt-composer.js +106 -29
- package/lib/read-tracker-store.mjs +149 -0
- package/lib/roles/catalog.mjs +133 -0
- package/lib/roles/preference.mjs +74 -0
- package/lib/server/index.mjs +109 -47
- package/lib/server/insights.mjs +1 -1
- package/lib/server/telemetry-login.mjs +17 -25
- package/lib/service-manager.mjs +32 -24
- package/lib/services/local-postgres.mjs +15 -0
- package/lib/services/telemetry-backend.mjs +2 -3
- package/lib/setup-prompts.mjs +2 -2
- package/lib/setup.mjs +55 -46
- package/lib/status.mjs +56 -8
- package/lib/storage/backend.mjs +12 -2
- package/lib/storage/postgres-backup.mjs +1 -1
- package/lib/strategy-store.mjs +371 -0
- package/lib/telemetry/backends/local.mjs +6 -4
- package/lib/telemetry/client.mjs +185 -0
- package/lib/telemetry/ingest.mjs +13 -5
- package/lib/telemetry/team-rollup.mjs +9 -2
- package/lib/uninstall/uninstall.mjs +1 -1
- package/lib/worker/trace.mjs +17 -27
- package/package.json +5 -2
- package/rules/common/research.md +44 -12
- package/skills/docs/backlog-proposal-workflow.md +2 -2
- package/skills/docs/customer-profile-workflow.md +1 -1
- package/skills/docs/evidence-ingest-workflow.md +5 -5
- package/skills/docs/prfaq-workflow.md +1 -1
- package/skills/docs/product-intelligence-review.md +1 -1
- package/skills/docs/product-intelligence-workflow.md +3 -3
- package/skills/docs/product-signal-workflow.md +48 -18
- package/skills/docs/research-workflow.md +26 -14
- package/skills/docs/strategy-workflow.md +36 -0
- package/skills/roles/data-analyst.product-intelligence.md +1 -1
- package/skills/roles/researcher.md +28 -15
- package/skills/routing.md +8 -1
- package/templates/docs/construct_guide.md +2 -2
- package/templates/docs/research-brief.md +63 -9
- package/templates/docs/strategy.md +36 -0
- package/templates/homebrew/construct.rb +7 -7
|
@@ -1,46 +1,92 @@
|
|
|
1
|
-
You have been burned enough times by stale
|
|
1
|
+
You have been burned enough times by stale, uncited, or hallucinated sources to treat every unverified claim as a liability. Training knowledge has a cutoff; the world does not. You operate at the standard of a principal researcher or senior academic: every load-bearing claim is traceable to a verifiable primary source with a date, every inference is labeled as such, and every URL has been fetched and confirmed to exist.
|
|
2
|
+
|
|
3
|
+
**Scope boundary** — you handle: external technical evidence, market and competitive research, academic literature, vendor documentation, security advisories, and quantitative benchmarks. For user behavioral research, handoff to `cx-ux-researcher`. For hypothesis design and experiment planning, handoff to `cx-rd-lead`.
|
|
2
4
|
|
|
3
5
|
**What you're instinctively suspicious of:**
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
- Blog posts
|
|
8
|
-
-
|
|
6
|
+
- Any claim without a publication date on a fast-moving topic
|
|
7
|
+
- URLs included but not fetched — an unfetched URL is an unverified claim
|
|
8
|
+
- Stopping research when the first plausible result appears
|
|
9
|
+
- Blog posts or summaries cited in place of the underlying paper, spec, or changelog
|
|
10
|
+
- "Everyone knows" or "the standard approach" as a substitute for a citation
|
|
11
|
+
- Research that confirms the original hypothesis without seriously engaging counter-evidence
|
|
9
12
|
|
|
10
|
-
**Your productive tension**: cx-rd-lead — R&D lead has hypotheses; you
|
|
13
|
+
**Your productive tension**: cx-rd-lead — R&D lead has hypotheses; you supply primary-source evidence before those hypotheses are treated as validated
|
|
11
14
|
|
|
12
|
-
**Your opening question**: What is the
|
|
15
|
+
**Your opening question**: What is the specific falsifiable claim, what is the most recent authoritative source for it, and what does the strongest counter-evidence say?
|
|
13
16
|
|
|
14
|
-
**Failure mode warning**: If
|
|
17
|
+
**Failure mode warning**: If your sources are secondhand, undated, or unfetched, the research is not complete. A confident-sounding synthesis of weak sources is worse than an honest "insufficient evidence."
|
|
15
18
|
|
|
16
19
|
**Role guidance**: call `get_skill("roles/researcher")` before drafting.
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
## Research protocol
|
|
22
|
+
|
|
23
|
+
Follow `rules/common/research.md` as the binding policy. The steps below operationalize it.
|
|
24
|
+
|
|
25
|
+
### Step 1 — Establish recency baseline
|
|
26
|
+
|
|
27
|
+
Start from the most recent available evidence. For 2026 work, search 2026 sources first; step back to 2025 only when 2026 sources are insufficient. For any fast-moving domain (AI tools, LLM behavior, cloud APIs, security advisories, market data), treat anything older than 12 months as presumptively stale until a newer source confirms it is still current.
|
|
28
|
+
|
|
29
|
+
When querying search engines or paper indexes, always filter or sort by date — never by relevance alone.
|
|
30
|
+
|
|
31
|
+
### Step 2 — Use domain-specific authoritative starting points
|
|
32
|
+
|
|
33
|
+
| Domain | Authoritative starting points (most-recent-first) |
|
|
34
|
+
|---|---|
|
|
35
|
+
| AI tools, LLM behavior, multi-agent systems | arXiv (cs.AI, cs.SE, cs.CL, cs.HC); ACL Anthology; NeurIPS / ICML / ICLR / HICSS proceedings; then vendor research blogs |
|
|
36
|
+
| Developer tools, IDE, editor, adoption | Stack Overflow Developer Survey (current year); JetBrains Developer Ecosystem Report; GitHub blog; editor/tool changelogs and release notes |
|
|
37
|
+
| Security, CVEs, supply chain | NVD; GitHub Security Advisories; OWASP; Google Project Zero; Microsoft Security Response Center; vendor advisories; then ProjectDiscovery / Snyk reports |
|
|
38
|
+
| Market data, ARR, funding, adoption | Primary company announcements, press releases, SEC filings; then TechCrunch / Bloomberg / WSJ (where citing named company sources) |
|
|
39
|
+
| Cloud, APIs, SDKs, frameworks | Official vendor docs for the exact version in use; changelog; migration guides |
|
|
40
|
+
| Regulatory, compliance, privacy | Primary regulation or standard text; then official agency guidance; then qualified legal analysis |
|
|
41
|
+
| Academic / research literature | arXiv (preprints); ACM Digital Library; IEEE Xplore; Google Scholar (filter by year) |
|
|
42
|
+
|
|
43
|
+
### Step 3 — Source hierarchy
|
|
44
|
+
|
|
45
|
+
1. **Primary**: peer-reviewed papers, official docs for the exact version, published standards, raw source code, SEC filings, primary company announcements
|
|
46
|
+
2. **Secondary**: changelogs, migration guides, tracked GitHub issues, maintainer posts, conference talks by the authors
|
|
47
|
+
3. **Tertiary**: blog posts, forums, Q&A, analyst summaries, AI-generated overviews — used only to locate primaries, never as evidence
|
|
48
|
+
|
|
49
|
+
### Step 4 — Check internal evidence
|
|
50
|
+
|
|
51
|
+
Before going external, search: `.cx/research/`, `.cx/knowledge/`, `docs/prd/`, `docs/meta-prd/`, ADRs, runbooks, and ingested artifacts. If a prior research brief exists for the topic, cite and extend it rather than redoing the search from scratch.
|
|
52
|
+
|
|
53
|
+
### Step 5 — Verify every URL
|
|
54
|
+
|
|
55
|
+
Fetch every URL you include. Confirm it resolves and that the content matches the cited claim. Do not include aggregate or index pages (arxiv.org/search, Google Scholar listings) for quantitative claims — cite the specific document URL. If a URL returns a 404, paywall, or redirect loop, find the canonical source or replace the citation. Mark unconfirmed URLs `[unverified]` until fetched.
|
|
56
|
+
|
|
57
|
+
### Step 6 — Evidence requirements per claim
|
|
58
|
+
|
|
59
|
+
- Prefer two independent primary sources per load-bearing claim
|
|
60
|
+
- One source is acceptable only when it is the authoritative primary source for that exact fact (e.g., the author's own paper reporting their measurement)
|
|
61
|
+
- Separate observation from inference — these are different things and must be labeled differently
|
|
62
|
+
- Name the strongest counter-evidence; do not smooth contradictions away
|
|
63
|
+
- State the evidence threshold that would change the recommendation
|
|
64
|
+
|
|
65
|
+
### Termination rule
|
|
66
|
+
|
|
67
|
+
Stop at 2–3 confirmed primary sources per finding. If a primary source is confirmed, do not continue searching for corroboration unless the claim is contested. Tertiary sources are search tools, not evidence.
|
|
68
|
+
|
|
69
|
+
## Output format
|
|
70
|
+
|
|
71
|
+
Produce a research brief using the structure from `get_template("research-brief")`. Minimum required sections:
|
|
72
|
+
|
|
73
|
+
**QUESTION** — the specific falsifiable question being answered
|
|
74
|
+
|
|
75
|
+
**METHOD** — search terms, systems queried, date filters applied, domain starting points used, internal paths checked; enough detail to reproduce
|
|
76
|
+
|
|
77
|
+
**SOURCES** — structured table: title/path | class (primary/secondary/tertiary) | date | URL | verified (yes/no) | relevance
|
|
78
|
+
|
|
79
|
+
**FINDINGS** — each finding labeled: what the source says (observation) | what is inferred (inference) | confidence (high/medium/low) | supporting source(s)
|
|
23
80
|
|
|
24
|
-
|
|
25
|
-
1. Primary: official documentation for the exact version in use, published standards, source code
|
|
26
|
-
2. Secondary: release notes, changelogs, migration guides, tracked issues, maintainer posts
|
|
27
|
-
3. Tertiary: forums, blog posts, Q&A — use as leads, not evidence
|
|
81
|
+
**COUNTER-EVIDENCE** — strongest disconfirming evidence; how it was addressed
|
|
28
82
|
|
|
29
|
-
|
|
83
|
+
**GAPS** — what the research did not resolve; what evidence would change the recommendation
|
|
30
84
|
|
|
31
|
-
|
|
32
|
-
- prefer two independent sources unless one authoritative primary source is sufficient
|
|
33
|
-
- separate observation from inference
|
|
34
|
-
- call out contradictions instead of smoothing them away
|
|
35
|
-
- state the evidence threshold that would change the recommendation
|
|
85
|
+
**CONFIDENCE SUMMARY** — overall confidence across findings; key uncertainties
|
|
36
86
|
|
|
37
|
-
|
|
87
|
+
**RECOMMENDATION** — what the evidence supports; the evidence threshold at which the recommendation flips
|
|
38
88
|
|
|
39
|
-
|
|
40
|
-
FINDINGS: key facts with citations
|
|
41
|
-
INFERENCES: conclusions drawn from evidence (clearly labeled)
|
|
42
|
-
GAPS: missing evidence that would change the recommendation
|
|
43
|
-
RECOMMENDATION: what the evidence supports
|
|
89
|
+
Write to `.cx/research/{topic-slug}.md` via `cx-docs-keeper`. Reference by path in the requesting agent's output.
|
|
44
90
|
|
|
45
91
|
## When invoked via the role framework
|
|
46
92
|
|
|
@@ -48,4 +94,4 @@ Construct may dispatch you in response to a `handoff.received`, `research.reques
|
|
|
48
94
|
|
|
49
95
|
**Fence** (agents/role-manifests.json → researcher): allowed paths `docs/research/**`, `.cx/research/**`, `docs/evidence-briefs/**`, `docs/signal-briefs/**`; allowed bd labels `research`, `evidence`, `investigation`; approval required for code/commit/push.
|
|
50
96
|
|
|
51
|
-
You produce research briefs, evidence briefs, signal briefs, and product-intelligence reports inside the fence. **Must not** edit code without user approval. **Handoff syntax**: `next:cx-product-manager` (requirements impact), `next:cx-architect` (design impact), `next:cx-engineer` (implementation question).
|
|
97
|
+
You produce research briefs, evidence briefs, signal briefs, and product-intelligence reports inside the fence. **Must not** edit code without user approval per `rules/common/commit-approval.md`. **Handoff syntax**: `next:cx-product-manager` (requirements impact), `next:cx-architect` (design impact), `next:cx-engineer` (implementation question), `next:cx-ux-researcher` (user behavioral question).
|
|
@@ -36,55 +36,17 @@ Check in this order:
|
|
|
36
36
|
|
|
37
37
|
Provide: severity, location (file:line), description, trigger condition, and concrete fix. For CVE checks, delegate to cx-researcher. Hand all findings to cx-engineer — CRITICAL findings block shipping until fixed.
|
|
38
38
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- **Errors:** INCOMPLETE_FLOW, MISSING_VALIDATION
|
|
51
|
-
- **Rate:** 10/min
|
|
52
|
-
|
|
53
|
-
### check_injection
|
|
54
|
-
- **Input:** `{ sinks: string[], sources: string[], sanitizers?: string[] }`
|
|
55
|
-
- **Output:** `{ injectionPoints: InjectionPoint[], severity: Severity, fix: string }`
|
|
56
|
-
- **Errors:** UNREACHABLE_SINK, FALSE_POSITIVE
|
|
57
|
-
- **Rate:** 15/min
|
|
58
|
-
|
|
59
|
-
### assess_dependencies
|
|
60
|
-
- **Input:** `{ dependencies: Dependency[], ecosystem: string, severityThreshold: string }`
|
|
61
|
-
- **Output:** `{ cves: CVE[], upgrades: UpgradeRecommendation[], sbom: SBOM }`
|
|
62
|
-
- **Errors:** UNKNOWN_PACKAGE, RATE_LIMITED
|
|
63
|
-
- **Rate:** 5/min
|
|
64
|
-
|
|
65
|
-
## Parallel Execution
|
|
66
|
-
|
|
67
|
-
When auditing code or reviewing changes, these checks run in parallel:
|
|
68
|
-
|
|
69
|
-
- **Secrets scan** (always runs — fast, non-blocking)
|
|
70
|
-
- **Auth/authorization audit** (if auth logic, JWT, sessions touched)
|
|
71
|
-
- **Injection path analysis** (if user input reaches sinks)
|
|
72
|
-
- **Data exposure check** (if logging, errors, APIs return data)
|
|
73
|
-
- **Dependency CVE scan** (if package.json or lock files changed)
|
|
74
|
-
|
|
75
|
-
All checks are independent — run concurrently and aggregate findings.
|
|
76
|
-
|
|
77
|
-
### Execution Pattern
|
|
78
|
-
```javascript
|
|
79
|
-
// Parallel security checks
|
|
80
|
-
const [secrets, auth, injection, exposure, deps] = await Promise.all([
|
|
81
|
-
scan_secrets({ files }),
|
|
82
|
-
audit_auth({ authFlow }),
|
|
83
|
-
check_injection({ sinks, sources }),
|
|
84
|
-
assess_data_exposure({ logs, errors }),
|
|
85
|
-
assess_dependencies({ dependencies })
|
|
86
|
-
]);
|
|
87
|
-
```
|
|
39
|
+
## Parallel audit discipline
|
|
40
|
+
|
|
41
|
+
Run these checks concurrently — they are independent and can be grep-driven in parallel:
|
|
42
|
+
|
|
43
|
+
- **Secrets scan** (always — fast, covers all files in scope)
|
|
44
|
+
- **Auth/authorization audit** (if auth logic, JWT, sessions, or privilege paths touched)
|
|
45
|
+
- **Injection path analysis** (if user input reaches sinks — exec, eval, query, template)
|
|
46
|
+
- **Data exposure check** (if logging, error responses, or APIs return data)
|
|
47
|
+
- **Dependency CVE scan** (if package.json or lock files changed — delegate to cx-researcher for CVE lookups)
|
|
48
|
+
|
|
49
|
+
Aggregate findings by severity before reporting. Do not report each category separately.
|
|
88
50
|
|
|
89
51
|
## Learning Capture
|
|
90
52
|
|
package/agents/prompts/cx-sre.md
CHANGED
|
@@ -25,49 +25,15 @@ RUNBOOK for each alert:
|
|
|
25
25
|
|
|
26
26
|
Review code changes for: missing error handling on request paths, N+1 queries, unbounded operations, missing timeouts, operations that don't degrade gracefully.
|
|
27
27
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- **
|
|
33
|
-
- **
|
|
34
|
-
- **
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- **Input:** `{ alertName: string, triggerCondition: string, triageSteps: Step[] }`
|
|
38
|
-
- **Output:** `{ runbook: Runbook, escalationPath: string[], rollbackProcedure: Rollback }`
|
|
39
|
-
- **Errors:** MISSING_TRIGGER, INCOMPLETE_TRIAGE
|
|
40
|
-
- **Rate:** 10/min
|
|
41
|
-
|
|
42
|
-
### review_reliability
|
|
43
|
-
- **Input:** `{ codeChanges: Diff[], statefulOps: boolean, degradationPlan?: string }`
|
|
44
|
-
- **Output:** `{ findings: ReliabilityFinding[], missingAlerts: string[], rollbackGaps: string[] }`
|
|
45
|
-
- **Errors:** MISSING_DEGRADATION_PLAN, UNBOUNDED_OPERATION
|
|
46
|
-
- **Rate:** 15/min
|
|
47
|
-
|
|
48
|
-
## Parallel Execution
|
|
49
|
-
|
|
50
|
-
When reviewing changes for production readiness, these checks run in parallel:
|
|
51
|
-
|
|
52
|
-
- **SLO definition check** (if new service or changed behavior)
|
|
53
|
-
- **Alerting coverage** (if error paths or failure modes exist)
|
|
54
|
-
- **Rollback procedure** (if stateful or irreversible operation)
|
|
55
|
-
- **Error handling audit** (if request paths or external calls)
|
|
56
|
-
- **Resource limits** (if N+1 queries, unbounded operations, missing timeouts)
|
|
57
|
-
|
|
58
|
-
All checks are independent — run concurrently and aggregate findings.
|
|
59
|
-
|
|
60
|
-
### Execution Pattern
|
|
61
|
-
```javascript
|
|
62
|
-
// Parallel SRE checks
|
|
63
|
-
const [slo, alerts, rollback, errors, resources] = await Promise.all([
|
|
64
|
-
define_slo({ service, metric, target }),
|
|
65
|
-
check_alerting_coverage({ failureModes }),
|
|
66
|
-
verify_rollback({ statefulOps }),
|
|
67
|
-
audit_error_handling({ requestPaths }),
|
|
68
|
-
check_resource_limits({ queries, operations })
|
|
69
|
-
]);
|
|
70
|
-
```
|
|
28
|
+
## Production readiness checklist
|
|
29
|
+
|
|
30
|
+
For each change review, check these independently and aggregate before reporting:
|
|
31
|
+
|
|
32
|
+
- **SLO definition** — is there a measurable target with an error budget for this service or behavior?
|
|
33
|
+
- **Alerting coverage** — is every meaningful failure mode covered by an alert with a runbook?
|
|
34
|
+
- **Rollback procedure** — is there a tested, documented path back from this change?
|
|
35
|
+
- **Error handling** — do request paths and external calls fail gracefully and within timeouts?
|
|
36
|
+
- **Resource bounds** — are there N+1 queries, unbounded loops, or missing timeouts?
|
|
71
37
|
|
|
72
38
|
## Learning Capture
|
|
73
39
|
|
|
@@ -14,6 +14,7 @@ You have watched enough users fail to know that what they say they want and what
|
|
|
14
14
|
**Failure mode warning**: If your brief has no friction points, you haven't talked to users. Every product has places where users get stuck.
|
|
15
15
|
|
|
16
16
|
**Role guidance**: call `get_skill("roles/researcher.ux")` before drafting.
|
|
17
|
+
**Evidence policy**: for any external claims (benchmark data, published studies, platform statistics), follow `rules/common/research.md` — most-recent-first, primary sources, verified URLs. UX findings based on direct user observation are primary evidence; stated preferences and self-reported data are secondary.
|
|
17
18
|
|
|
18
19
|
Produce a UX brief:
|
|
19
20
|
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"events": ["handoff.received", "backlog.stale", "prd.requested"],
|
|
126
126
|
"severityImmediate": [],
|
|
127
127
|
"fence": {
|
|
128
|
-
"allowedPaths": ["docs/prd/**", "docs/meta-prd/**", "docs/prfaq/**", "docs/one-pager/**", ".cx/
|
|
128
|
+
"allowedPaths": ["docs/prd/**", "docs/meta-prd/**", "docs/prfaq/**", "docs/one-pager/**", ".cx/knowledge/**"],
|
|
129
129
|
"allowedBdLabels": ["product", "prd", "backlog", "feature"],
|
|
130
130
|
"allowedCommands": ["bd create", "bd note", "bd update", "bd label", "bd priority"],
|
|
131
131
|
"approvalRequired": ["commit", "push", "edit:lib/**", "edit:bin/**"]
|
|
@@ -192,7 +192,7 @@
|
|
|
192
192
|
"trace-reviewer": { "events": ["handoff.received", "trace.anomaly"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/traces/**", ".cx/traces/**"], "allowedBdLabels": ["trace", "observability"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["trace"], "docTypes": ["memo"] }, "handoffCandidates": ["sre", "ai-engineer", "engineer"], "killSwitchEnv": "CONSTRUCT_ROLE_TRACE_REVIEWER" },
|
|
193
193
|
"test-automation": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["tests/**", "docs/test-automation/**"], "allowedBdLabels": ["test-automation", "ci", "test-infra"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:lib/**", "edit:bin/**"] }, "outputs": { "bdLabels": ["test-automation"], "docTypes": ["memo"] }, "handoffCandidates": ["qa", "engineer", "platform-engineer"], "killSwitchEnv": "CONSTRUCT_ROLE_TEST_AUTOMATION" },
|
|
194
194
|
"ai-engineer": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/ai/**", "docs/agents/**", "agents/**", "skills/ai/**"], "allowedBdLabels": ["ai", "agent", "model"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:lib/**", "edit:bin/**"] }, "outputs": { "bdLabels": ["ai"], "docTypes": ["memo"] }, "handoffCandidates": ["engineer", "architect", "evaluator"], "killSwitchEnv": "CONSTRUCT_ROLE_AI_ENGINEER" },
|
|
195
|
-
"business-strategist": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/strategy/**", ".cx/strategy/**"], "allowedBdLabels": ["strategy", "business"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["strategy"], "docTypes": ["memo"] }, "handoffCandidates": ["product-manager", "researcher"], "killSwitchEnv": "CONSTRUCT_ROLE_BUSINESS_STRATEGIST" },
|
|
195
|
+
"business-strategist": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/strategy/**", ".cx/knowledge/decisions/strategy/**"], "allowedBdLabels": ["strategy", "business"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["strategy"], "docTypes": ["memo", "strategy"] }, "handoffCandidates": ["product-manager", "researcher"], "killSwitchEnv": "CONSTRUCT_ROLE_BUSINESS_STRATEGIST" },
|
|
196
196
|
"researcher": {
|
|
197
197
|
"events": ["handoff.received", "research.requested", "evidence.requested"],
|
|
198
198
|
"severityImmediate": [],
|
|
@@ -209,9 +209,9 @@
|
|
|
209
209
|
"ux-researcher": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/ux-research/**", ".cx/ux-research/**"], "allowedBdLabels": ["ux-research", "user-research"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["ux-research"], "docTypes": ["memo"] }, "handoffCandidates": ["product-manager", "designer", "researcher"], "killSwitchEnv": "CONSTRUCT_ROLE_UX_RESEARCHER" },
|
|
210
210
|
"explorer": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/explorations/**", ".cx/explorations/**"], "allowedBdLabels": ["exploration", "spike"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["exploration"], "docTypes": ["memo"] }, "handoffCandidates": ["engineer", "architect", "researcher"], "killSwitchEnv": "CONSTRUCT_ROLE_EXPLORER" },
|
|
211
211
|
"operations": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/ops/**", "docs/operations/**"], "allowedBdLabels": ["ops", "operations"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["ops"], "docTypes": ["memo", "runbook"] }, "handoffCandidates": ["sre", "platform-engineer", "release-manager"], "killSwitchEnv": "CONSTRUCT_ROLE_OPERATIONS" },
|
|
212
|
-
"orchestrator": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs
|
|
212
|
+
"orchestrator": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/**", "plan.md", ".cx/context.md"], "allowedBdLabels": ["orchestration", "workflow", "dispatch"], "allowedCommands": ["bd create", "bd note", "bd update", "bd label", "bd link"], "approvalRequired": ["commit", "push", "edit:lib/**", "edit:bin/**", "edit:agents/**"] }, "outputs": { "bdLabels": ["orchestration", "dispatch"], "docTypes": ["memo"] }, "handoffCandidates": ["product-manager", "architect", "engineer", "security", "qa", "sre"], "killSwitchEnv": "CONSTRUCT_ROLE_ORCHESTRATOR" },
|
|
213
213
|
"rd-lead": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/research/**", "docs/experiments/**", ".cx/experiments/**"], "allowedBdLabels": ["rd", "research", "experiment"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["rd"], "docTypes": ["memo", "research-brief"] }, "handoffCandidates": ["researcher", "architect", "ai-engineer"], "killSwitchEnv": "CONSTRUCT_ROLE_RD_LEAD" },
|
|
214
|
-
"legal-compliance": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/legal/**", "docs/compliance/**"], "allowedBdLabels": ["legal", "compliance", "privacy"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["legal", "compliance"], "docTypes": ["memo"] }, "handoffCandidates": ["security", "product-manager"], "killSwitchEnv": "CONSTRUCT_ROLE_LEGAL_COMPLIANCE" },
|
|
214
|
+
"legal-compliance": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/legal/**", "docs/compliance/**", "docs/security/**", ".cx/knowledge/**"], "allowedBdLabels": ["legal", "compliance", "privacy"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["legal", "compliance"], "docTypes": ["memo"] }, "handoffCandidates": ["security", "product-manager"], "killSwitchEnv": "CONSTRUCT_ROLE_LEGAL_COMPLIANCE" },
|
|
215
215
|
"devil-advocate": { "events": ["handoff.received"], "severityImmediate": [], "fence": { "allowedPaths": ["docs/critiques/**"], "allowedBdLabels": ["critique", "devil-advocate"], "allowedCommands": ["bd create", "bd note", "bd update"], "approvalRequired": ["commit", "push", "edit:**"] }, "outputs": { "bdLabels": ["critique"], "docTypes": ["memo"] }, "handoffCandidates": ["architect", "product-manager", "engineer"], "killSwitchEnv": "CONSTRUCT_ROLE_DEVIL_ADVOCATE" }
|
|
216
216
|
}
|
|
217
217
|
}
|
package/bin/construct
CHANGED
|
@@ -130,7 +130,7 @@ function usage() {
|
|
|
130
130
|
|
|
131
131
|
/** Show interactive context-aware menu when construct is run without arguments. */
|
|
132
132
|
async function showInteractiveMenu() {
|
|
133
|
-
const { loadProjectConfig } = await import('
|
|
133
|
+
const { loadProjectConfig } = await import('../lib/config/project-config.mjs');
|
|
134
134
|
const { existsSync } = await import('node:fs');
|
|
135
135
|
const { join } = await import('node:path');
|
|
136
136
|
|
|
@@ -142,7 +142,7 @@ async function showInteractiveMenu() {
|
|
|
142
142
|
println('');
|
|
143
143
|
|
|
144
144
|
if (isConstructProject) {
|
|
145
|
-
const projectName = process.env.CX_PROJECT_NAME ||
|
|
145
|
+
const projectName = process.env.CX_PROJECT_NAME || path.basename(projectRoot);
|
|
146
146
|
println(`${COLORS.dim}Project:${COLORS.reset} ${projectName}`);
|
|
147
147
|
println('');
|
|
148
148
|
}
|
|
@@ -160,7 +160,7 @@ async function showInteractiveMenu() {
|
|
|
160
160
|
|
|
161
161
|
// Show context-aware suggestions
|
|
162
162
|
if (isConstructProject) {
|
|
163
|
-
const { readDashboardState } = await import('
|
|
163
|
+
const { readDashboardState } = await import('../lib/service-manager.mjs');
|
|
164
164
|
const dashboard = readDashboardState(HOME);
|
|
165
165
|
|
|
166
166
|
if (!dashboard) {
|
|
@@ -288,12 +288,12 @@ async function cmdDoctor() {
|
|
|
288
288
|
for (const varName of vars) {
|
|
289
289
|
const sources = [];
|
|
290
290
|
|
|
291
|
-
//
|
|
291
|
+
// process.env
|
|
292
292
|
if (process.env[varName]) {
|
|
293
293
|
sources.push(`process.env (${process.env[varName].slice(0, 8)}…)`);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
//
|
|
296
|
+
// .env files
|
|
297
297
|
for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.env'), join(homedir(), '.construct', 'config.env')]) {
|
|
298
298
|
if (fs.existsSync(envPath)) {
|
|
299
299
|
try {
|
|
@@ -304,7 +304,7 @@ async function cmdDoctor() {
|
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
//
|
|
307
|
+
// Shell rc files
|
|
308
308
|
const shellFiles = [join(homedir(), '.zshrc'), join(homedir(), '.bashrc'), join(homedir(), '.bash_profile'), join(homedir(), '.profile')];
|
|
309
309
|
for (const rcPath of shellFiles) {
|
|
310
310
|
if (!fs.existsSync(rcPath)) continue;
|
|
@@ -421,6 +421,22 @@ async function cmdDoctor() {
|
|
|
421
421
|
add('Claude Code agents dir', fs.existsSync(path.join(HOME, '.claude', 'agents')), true);
|
|
422
422
|
add('Codex agents dir', fs.existsSync(path.join(HOME, '.codex', 'agents')), true);
|
|
423
423
|
add('Copilot prompts dir', fs.existsSync(path.join(HOME, '.github', 'prompts')), true);
|
|
424
|
+
add('Cursor MCP config', fs.existsSync(path.join(HOME, '.cursor', 'mcp.json')), true);
|
|
425
|
+
const vscodeSettingsPaths = process.platform === 'darwin'
|
|
426
|
+
? [
|
|
427
|
+
path.join(HOME, 'Library', 'Application Support', 'Code', 'User', 'settings.json'),
|
|
428
|
+
path.join(HOME, 'Library', 'Application Support', 'Code - Insiders', 'User', 'settings.json'),
|
|
429
|
+
]
|
|
430
|
+
: process.platform === 'linux'
|
|
431
|
+
? [
|
|
432
|
+
path.join(HOME, '.config', 'Code', 'User', 'settings.json'),
|
|
433
|
+
path.join(HOME, '.config', 'Code - Insiders', 'User', 'settings.json'),
|
|
434
|
+
]
|
|
435
|
+
: [
|
|
436
|
+
path.join(process.env.APPDATA ?? path.join(HOME, 'AppData', 'Roaming'), 'Code', 'User', 'settings.json'),
|
|
437
|
+
path.join(process.env.APPDATA ?? path.join(HOME, 'AppData', 'Roaming'), 'Code - Insiders', 'User', 'settings.json'),
|
|
438
|
+
];
|
|
439
|
+
add('VS Code settings file', vscodeSettingsPaths.some((candidate) => fs.existsSync(candidate)), true);
|
|
424
440
|
add('User config ready', fs.existsSync(getUserEnvPath(HOME)) || fs.existsSync(path.join(HOME, '.construct')), true);
|
|
425
441
|
add('skills/ directory', fs.existsSync(path.join(ROOT_DIR, 'skills')));
|
|
426
442
|
add('rules/common/', fs.existsSync(path.join(ROOT_DIR, 'rules', 'common')));
|
|
@@ -904,9 +920,9 @@ async function cmdUp() {
|
|
|
904
920
|
if (embedConfigExists && autoEmbed) {
|
|
905
921
|
try {
|
|
906
922
|
const { runEmbedCli } = await import('../lib/embed/cli.mjs');
|
|
907
|
-
await runEmbedCli(['start']);
|
|
908
|
-
} catch {
|
|
909
|
-
|
|
923
|
+
await runEmbedCli(['start'], { rootDir: ROOT_DIR });
|
|
924
|
+
} catch (err) {
|
|
925
|
+
warn(`embed start failed: ${err.message} — check ~/.cx/runtime/embed-daemon.log`);
|
|
910
926
|
}
|
|
911
927
|
}
|
|
912
928
|
|
|
@@ -1789,8 +1805,8 @@ async function cmdIntakeConfig(args) {
|
|
|
1789
1805
|
async function cmdIntakeMetrics() {
|
|
1790
1806
|
try {
|
|
1791
1807
|
const { computeIntakeMetrics, pendingAge } = await import('../lib/embed/intake-metrics.mjs');
|
|
1792
|
-
const metrics = computeIntakeMetrics({ rootDir: cwd });
|
|
1793
|
-
const age = pendingAge({ rootDir: cwd });
|
|
1808
|
+
const metrics = computeIntakeMetrics({ rootDir: process.cwd() });
|
|
1809
|
+
const age = pendingAge({ rootDir: process.cwd() });
|
|
1794
1810
|
|
|
1795
1811
|
println(`${COLORS.bold}Intake pipeline metrics:${COLORS.reset}`);
|
|
1796
1812
|
println('');
|
|
@@ -2449,6 +2465,44 @@ async function cmdModels(args) {
|
|
|
2449
2465
|
await cmdSync([]);
|
|
2450
2466
|
return;
|
|
2451
2467
|
}
|
|
2468
|
+
if (args.includes('--cheapest')) {
|
|
2469
|
+
const { selectCheapestProvider, rankConfiguredProvidersByCost, formatCheapestProviderMessage, isCheapestProviderEnabled } =
|
|
2470
|
+
await import('../lib/model-cheapest-provider.mjs');
|
|
2471
|
+
const cheapestTier = args.find((arg) => arg.startsWith('--tier='))?.split('=')[1] ?? 'standard';
|
|
2472
|
+
const verbose = args.includes('--verbose');
|
|
2473
|
+
const enabled = isCheapestProviderEnabled(envPath, { env: process.env });
|
|
2474
|
+
const result = await selectCheapestProvider(cheapestTier, { env: process.env });
|
|
2475
|
+
const msg = formatCheapestProviderMessage(result, { showRanking: verbose });
|
|
2476
|
+
println(msg);
|
|
2477
|
+
if (enabled) println('\nCheapest provider mode: enabled');
|
|
2478
|
+
return;
|
|
2479
|
+
}
|
|
2480
|
+
if (args.includes('--apply-cheapest')) {
|
|
2481
|
+
const { selectCheapestForAllTiers, setCheapestProviderPreference } =
|
|
2482
|
+
await import('../lib/model-cheapest-provider.mjs');
|
|
2483
|
+
const allTiers = args.includes('--all-tiers') || !args.find((arg) => arg.startsWith('--tier='));
|
|
2484
|
+
const tierArg = args.find((arg) => arg.startsWith('--tier='))?.split('=')[1];
|
|
2485
|
+
const tiers = allTiers ? ['reasoning', 'standard', 'fast'] : (tierArg ? [tierArg] : ['standard']);
|
|
2486
|
+
const selections = {};
|
|
2487
|
+
for (const currentTier of tiers) {
|
|
2488
|
+
const { selectCheapestProvider } = await import('../lib/model-cheapest-provider.mjs');
|
|
2489
|
+
const result = await selectCheapestProvider(currentTier, { env: process.env });
|
|
2490
|
+
if (result.modelId) selections[currentTier] = result.modelId;
|
|
2491
|
+
}
|
|
2492
|
+
if (Object.keys(selections).length === 0) {
|
|
2493
|
+
errorln('No configured providers found. Set API keys or install Ollama first.');
|
|
2494
|
+
process.exit(1);
|
|
2495
|
+
}
|
|
2496
|
+
applyToEnv(envPath, selections);
|
|
2497
|
+
println('Applying cheapest models:');
|
|
2498
|
+
for (const [tier, model] of Object.entries(selections)) {
|
|
2499
|
+
println(` ${tier.padEnd(11)} ${model}`);
|
|
2500
|
+
}
|
|
2501
|
+
println('Written to ~/.construct/config.env. Running construct sync...');
|
|
2502
|
+
setCheapestProviderPreference(envPath, true);
|
|
2503
|
+
await cmdSync([]);
|
|
2504
|
+
return;
|
|
2505
|
+
}
|
|
2452
2506
|
const registry = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'agents', 'registry.json'), 'utf8'));
|
|
2453
2507
|
const result = readCurrentModels(envPath, registry.models ?? {});
|
|
2454
2508
|
const nothingSelected = ['reasoning', 'standard', 'fast'].every((t) => !result[t]);
|
|
@@ -3796,6 +3850,13 @@ if (command === '--help' || command === '-h' || command === 'help') {
|
|
|
3796
3850
|
process.exit(0);
|
|
3797
3851
|
}
|
|
3798
3852
|
|
|
3853
|
+
// Version flags → print version and exit
|
|
3854
|
+
if (command === '--version' || command === '-V') {
|
|
3855
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf8'));
|
|
3856
|
+
println(`construct v${pkg.version}`);
|
|
3857
|
+
process.exit(0);
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3799
3860
|
const handler = handlers.get(command);
|
|
3800
3861
|
if (!handler) {
|
|
3801
3862
|
errorln(`Unknown command: ${command}`);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* and runs `npm install`, npm fetches Construct into the project's
|
|
7
7
|
* `node_modules/` and then runs this script. Its job: regenerate the project's
|
|
8
8
|
* `.claude/agents/` and `.claude/settings.json` from the bundled registry so
|
|
9
|
-
* the project clone is fully runnable without a manual `construct
|
|
9
|
+
* the project clone is fully runnable without a manual `construct init`.
|
|
10
10
|
*
|
|
11
11
|
* The script is a no-op in three cases:
|
|
12
12
|
*
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
-- 004_recommendations.sql — Recommendations table for team and enterprise mode.
|
|
2
|
+
-- Solo mode uses the JSONL store under .cx/intake/ instead.
|
|
3
|
+
--
|
|
4
|
+
-- Dedup key is enforced at the (project, dedup_key) level — matches the in-memory
|
|
5
|
+
-- dedup contract in lib/embed/recommendation-store.mjs.
|
|
6
|
+
-- Active recommendations: dismissed_at IS NULL AND superseded_at IS NULL.
|
|
7
|
+
|
|
8
|
+
create table if not exists construct_recommendations (
|
|
9
|
+
id text primary key,
|
|
10
|
+
project text not null,
|
|
11
|
+
dedup_key text not null,
|
|
12
|
+
type text not null,
|
|
13
|
+
title text not null,
|
|
14
|
+
reason text,
|
|
15
|
+
lane text,
|
|
16
|
+
signal_count int not null default 1,
|
|
17
|
+
total_signal_count int not null default 1,
|
|
18
|
+
customer_impact int not null default 0,
|
|
19
|
+
recency_bonus int not null default 0,
|
|
20
|
+
strategic_bonus int not null default 0,
|
|
21
|
+
score float not null default 0,
|
|
22
|
+
priority text not null default 'P3',
|
|
23
|
+
source_signal_ids jsonb not null default '[]'::jsonb,
|
|
24
|
+
first_seen timestamptz not null default now(),
|
|
25
|
+
last_seen timestamptz not null default now(),
|
|
26
|
+
dismissed_at timestamptz,
|
|
27
|
+
dismiss_reason text,
|
|
28
|
+
superseded_at timestamptz,
|
|
29
|
+
superseded_by_id text,
|
|
30
|
+
suppressed_until timestamptz,
|
|
31
|
+
suppress_reason text,
|
|
32
|
+
updated_at timestamptz not null default now()
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
create unique index if not exists construct_recommendations_project_dedup_key_idx
|
|
36
|
+
on construct_recommendations (project, dedup_key);
|
|
37
|
+
|
|
38
|
+
create index if not exists construct_recommendations_priority_score_idx
|
|
39
|
+
on construct_recommendations (project, priority, score desc);
|
|
40
|
+
|
|
41
|
+
create index if not exists construct_recommendations_last_seen_idx
|
|
42
|
+
on construct_recommendations (project, last_seen desc);
|
|
43
|
+
|
|
44
|
+
create index if not exists construct_recommendations_active_idx
|
|
45
|
+
on construct_recommendations (project, dismissed_at)
|
|
46
|
+
where dismissed_at is null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- 005_strategy.sql — Product strategy store for team and enterprise mode.
|
|
2
|
+
-- Solo mode uses ~/.cx/strategy.md instead.
|
|
3
|
+
--
|
|
4
|
+
-- Each row is an immutable version snapshot. The latest version for a project
|
|
5
|
+
-- is the row with the highest version number (or max updated_at when equal).
|
|
6
|
+
-- Callers insert a new row on each write; old versions are retained for history.
|
|
7
|
+
|
|
8
|
+
create table if not exists construct_strategy (
|
|
9
|
+
id bigserial primary key,
|
|
10
|
+
project text not null,
|
|
11
|
+
content text not null,
|
|
12
|
+
version int not null default 1,
|
|
13
|
+
updated_at timestamptz not null default now(),
|
|
14
|
+
updated_by text
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
create unique index if not exists construct_strategy_project_version_idx
|
|
18
|
+
on construct_strategy (project, version);
|
|
19
|
+
|
|
20
|
+
create index if not exists construct_strategy_project_updated_at_idx
|
|
21
|
+
on construct_strategy (project, updated_at desc);
|
package/lib/auto-docs.mjs
CHANGED
|
@@ -88,7 +88,7 @@ const DIR_DESCRIPTIONS = {
|
|
|
88
88
|
commands: 'Command prompt assets',
|
|
89
89
|
codex: 'GitHub Copilot / Codex integration',
|
|
90
90
|
docs: 'Architecture notes, runbooks, and documentation contract',
|
|
91
|
-
telemetry: '
|
|
91
|
+
telemetry: 'Local trace capture and optional telemetry export',
|
|
92
92
|
lib: 'Core runtime: CLI, hooks, MCP, status, sync, workflow',
|
|
93
93
|
opencode: 'OpenCode integration config',
|
|
94
94
|
personas: 'Persona prompt definitions',
|
|
@@ -470,4 +470,3 @@ export function buildFumadocsReference({ rootDir } = {}) {
|
|
|
470
470
|
|
|
471
471
|
return { written };
|
|
472
472
|
}
|
|
473
|
-
|
package/lib/beads-automation.mjs
CHANGED
|
@@ -22,6 +22,11 @@ const HANDOFFS_DIR = '.cx/handoffs';
|
|
|
22
22
|
const CONFIRM_TIMEOUT = 60000; // 60 second timeout for confirmation prompts
|
|
23
23
|
const POSITIVE_INTENT = /^(y|yes|yeah|yep|yup|sure|ok|okay|go|proceed|do it)\b/i;
|
|
24
24
|
|
|
25
|
+
function beadsConfirmEnabled(env = process.env) {
|
|
26
|
+
const raw = String(env.CONSTRUCT_BEADS_CONFIRM ?? '').trim().toLowerCase();
|
|
27
|
+
return raw === '1' || raw === 'true' || raw === 'yes' || raw === 'on';
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
// ---------------------------------------------------------------------------
|
|
26
31
|
// Confirmation Utility
|
|
27
32
|
// ---------------------------------------------------------------------------
|
|
@@ -148,14 +153,18 @@ export async function confirmAndCommit({
|
|
|
148
153
|
}
|
|
149
154
|
console.error('');
|
|
150
155
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
if (yes || beadsConfirmEnabled()) {
|
|
157
|
+
const confirmed = await confirm(
|
|
158
|
+
'Commit these changes?',
|
|
159
|
+
{ yes, default: 'n' }
|
|
160
|
+
);
|
|
155
161
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
if (!confirmed) {
|
|
163
|
+
console.error('[beads] Commit cancelled');
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
console.error('[beads] CONSTRUCT_BEADS_CONFIRM is off — committing without interactive prompt.');
|
|
159
168
|
}
|
|
160
169
|
|
|
161
170
|
runGit(['commit', '-m', message], { cwd });
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* downloadSize approximate install size in bytes (informational)
|
|
16
16
|
*
|
|
17
17
|
* The registry is consulted by:
|
|
18
|
-
* - `construct
|
|
18
|
+
* - `construct init` walks every resource, asks consent, installs
|
|
19
19
|
* - `construct doctor --bootstrap` re-probes every resource verbosely
|
|
20
20
|
* - lazy-install paths in hooks (consult the cached consent silently)
|
|
21
21
|
*
|
|
@@ -114,7 +114,7 @@ export function formatProbe(probe) {
|
|
|
114
114
|
? `\n fallback: ${probe.fallback}`
|
|
115
115
|
: '';
|
|
116
116
|
const install = !probe.present && probe.installable
|
|
117
|
-
? `\n installable via construct
|
|
117
|
+
? `\n installable via construct init`
|
|
118
118
|
: '';
|
|
119
119
|
return ` ${status} ${probe.displayName}${v}${loc}${detail}${fallback}${install}`;
|
|
120
120
|
}
|