@geraldmaron/construct 1.0.4 → 1.0.6
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 +57 -44
- package/agents/prompts/cx-accessibility.md +3 -3
- package/agents/prompts/cx-ai-engineer.md +7 -7
- package/agents/prompts/cx-architect.md +6 -6
- package/agents/prompts/cx-business-strategist.md +6 -6
- package/agents/prompts/cx-data-analyst.md +7 -7
- package/agents/prompts/cx-data-engineer.md +3 -3
- package/agents/prompts/cx-debugger.md +6 -6
- package/agents/prompts/cx-designer.md +5 -5
- package/agents/prompts/cx-devil-advocate.md +4 -4
- package/agents/prompts/cx-docs-keeper.md +5 -5
- package/agents/prompts/cx-engineer.md +5 -5
- package/agents/prompts/cx-evaluator.md +2 -2
- package/agents/prompts/cx-explorer.md +8 -8
- package/agents/prompts/cx-legal-compliance.md +2 -2
- package/agents/prompts/cx-operations.md +5 -5
- package/agents/prompts/cx-orchestrator.md +10 -10
- package/agents/prompts/cx-platform-engineer.md +5 -5
- package/agents/prompts/cx-product-manager.md +4 -4
- package/agents/prompts/cx-qa.md +8 -8
- package/agents/prompts/cx-rd-lead.md +9 -9
- package/agents/prompts/cx-release-manager.md +5 -5
- package/agents/prompts/cx-researcher.md +22 -22
- package/agents/prompts/cx-reviewer.md +7 -7
- package/agents/prompts/cx-security.md +10 -10
- package/agents/prompts/cx-sre.md +7 -7
- package/agents/prompts/cx-test-automation.md +3 -3
- package/agents/prompts/cx-trace-reviewer.md +8 -8
- package/agents/prompts/cx-ux-researcher.md +3 -3
- package/bin/construct +470 -4
- package/commands/build/feature.md +4 -4
- package/commands/build/fix.md +8 -8
- package/commands/design/access.md +3 -3
- package/commands/design/flow.md +3 -3
- package/commands/design/ui.md +4 -4
- package/commands/measure/experiment.md +5 -5
- package/commands/measure/metrics.md +3 -3
- package/commands/measure/results.md +4 -4
- package/commands/plan/api.md +3 -3
- package/commands/plan/challenge.md +3 -3
- package/commands/plan/decide.md +3 -3
- package/commands/plan/feature.md +7 -7
- package/commands/plan/requirements.md +3 -3
- package/commands/remember/context.md +5 -5
- package/commands/remember/handoff.md +3 -3
- package/commands/remember/runbook.md +3 -3
- package/commands/review/code.md +8 -8
- package/commands/review/quality.md +4 -4
- package/commands/review/security.md +3 -3
- package/commands/ship/ready.md +3 -3
- package/commands/ship/release.md +3 -3
- package/commands/ship/status.md +4 -4
- package/commands/understand/docs.md +3 -3
- package/commands/understand/research.md +5 -3
- package/commands/understand/this.md +4 -4
- package/commands/understand/why.md +8 -8
- package/commands/work/clean.md +14 -14
- package/commands/work/drive.md +10 -10
- package/commands/work/optimize-prompts.md +9 -9
- package/commands/work/parallel-review.md +8 -8
- package/db/schema/006_graph.sql +24 -0
- package/examples/provider-plugin/README.md +7 -7
- package/examples/seed-observations/README.md +6 -6
- package/examples/seed-observations/anti-patterns.md +14 -14
- package/examples/seed-observations/decisions.md +4 -4
- package/examples/seed-observations/patterns.md +14 -14
- package/lib/auto-docs.mjs +10 -5
- package/lib/cli-commands.mjs +45 -1
- package/lib/comment-lint.mjs +7 -1
- package/lib/config/schema.mjs +3 -0
- package/lib/flavors/loader.mjs +136 -0
- package/lib/hooks/agent-tracker.mjs +22 -3
- package/lib/hooks/pre-push-gate.mjs +14 -1
- package/lib/hooks/session-optimize.mjs +3 -2
- package/lib/hooks/session-reflect.mjs +68 -0
- package/lib/init-unified.mjs +25 -2
- package/lib/intake/classify.mjs +61 -183
- package/lib/intake/prepare.mjs +7 -0
- package/lib/intake/tables/creative.mjs +94 -0
- package/lib/intake/tables/operations.mjs +85 -0
- package/lib/intake/tables/research.mjs +85 -0
- package/lib/intake/tables/rnd.mjs +175 -0
- package/lib/knowledge/graph.mjs +213 -0
- package/lib/knowledge/research-store.mjs +109 -0
- package/lib/mcp/server.mjs +187 -1
- package/lib/mcp/tools/profile.mjs +270 -0
- package/lib/observation-store.mjs +19 -0
- package/lib/outcomes/aggregate.mjs +104 -0
- package/lib/outcomes/record.mjs +115 -0
- package/lib/parity.mjs +6 -9
- package/lib/profiles/lifecycle.mjs +388 -0
- package/lib/profiles/loader.mjs +123 -0
- package/lib/profiles/validate-custom.mjs +114 -0
- package/lib/reflect/extractor.mjs +193 -0
- package/lib/reflect.mjs +89 -2
- package/lib/sandbox.mjs +102 -0
- package/package.json +6 -1
- package/personas/construct.md +20 -20
- package/platforms/claude/CLAUDE.md +6 -6
- package/platforms/claude/settings.template.json +13 -0
- package/rules/common/agents.md +2 -2
- package/rules/common/beads-hygiene.md +11 -11
- package/rules/common/code-review.md +1 -1
- package/rules/common/coding-style.md +1 -1
- package/rules/common/comments.md +8 -8
- package/rules/common/commit-approval.md +4 -4
- package/rules/common/cx-agent-routing.md +2 -2
- package/rules/common/cx-skill-routing.md +2 -2
- package/rules/common/development-workflow.md +1 -1
- package/rules/common/doc-ownership.md +2 -2
- package/rules/common/efficiency.md +3 -3
- package/rules/common/framing.md +1 -1
- package/rules/common/git-workflow.md +1 -1
- package/rules/common/patterns.md +1 -1
- package/rules/common/performance.md +1 -1
- package/rules/common/release-gates.md +7 -7
- package/rules/common/research.md +4 -4
- package/rules/common/security.md +1 -1
- package/rules/common/skill-composition.md +8 -8
- package/rules/common/testing.md +1 -1
- package/rules/golang/coding-style.md +2 -2
- package/rules/golang/hooks.md +1 -1
- package/rules/golang/patterns.md +1 -1
- package/rules/golang/security.md +1 -1
- package/rules/golang/testing.md +1 -1
- package/rules/python/coding-style.md +1 -1
- package/rules/python/hooks.md +1 -1
- package/rules/python/patterns.md +1 -1
- package/rules/python/security.md +1 -1
- package/rules/python/testing.md +1 -1
- package/rules/swift/coding-style.md +3 -3
- package/rules/swift/hooks.md +2 -2
- package/rules/swift/patterns.md +2 -2
- package/rules/swift/security.md +4 -4
- package/rules/swift/testing.md +2 -2
- package/rules/typescript/coding-style.md +1 -1
- package/rules/typescript/hooks.md +1 -1
- package/rules/typescript/patterns.md +1 -1
- package/rules/typescript/security.md +1 -1
- package/rules/typescript/testing.md +1 -1
- package/rules/web/coding-style.md +1 -1
- package/rules/web/design-quality.md +1 -1
- package/rules/web/hooks.md +1 -1
- package/rules/web/patterns.md +1 -1
- package/rules/web/performance.md +1 -1
- package/rules/web/security.md +1 -1
- package/rules/web/testing.md +1 -1
- package/scripts/sync-agents.mjs +11 -0
- package/skills/ai/agent-dev.md +1 -1
- package/skills/ai/llm-security.md +1 -1
- package/skills/ai/ml-ops.md +6 -6
- package/skills/ai/orchestration-workflow.md +1 -1
- package/skills/ai/prompt-and-eval.md +1 -1
- package/skills/ai/prompt-optimizer.md +13 -13
- package/skills/ai/rag-system.md +1 -1
- package/skills/architecture/api-design.md +1 -1
- package/skills/architecture/caching.md +1 -1
- package/skills/architecture/cloud-native.md +1 -1
- package/skills/architecture/message-queue.md +1 -1
- package/skills/architecture/security-arch.md +1 -1
- package/skills/compliance/ai-disclosure.md +1 -1
- package/skills/compliance/data-privacy.md +1 -1
- package/skills/compliance/license-audit.md +2 -2
- package/skills/compliance/regulatory-review.md +1 -1
- package/skills/development/cpp.md +1 -1
- package/skills/development/go.md +1 -1
- package/skills/development/java.md +1 -1
- package/skills/development/kotlin.md +9 -9
- package/skills/development/mobile-crossplatform.md +13 -13
- package/skills/development/python.md +1 -1
- package/skills/development/rust.md +1 -1
- package/skills/development/shell.md +1 -1
- package/skills/development/swift.md +6 -6
- package/skills/development/typescript.md +1 -1
- package/skills/devops/ci-cd.md +5 -5
- package/skills/devops/containerization.md +9 -9
- package/skills/devops/cost-optimization.md +1 -1
- package/skills/devops/data-engineering.md +2 -2
- package/skills/devops/database.md +1 -1
- package/skills/devops/dependency-management.md +3 -3
- package/skills/devops/devsecops.md +1 -1
- package/skills/devops/git-workflow.md +1 -1
- package/skills/devops/incident-response.md +18 -18
- package/skills/devops/monorepo.md +5 -5
- package/skills/devops/observability.md +1 -1
- package/skills/devops/performance.md +1 -1
- package/skills/devops/testing.md +1 -1
- package/skills/docs/adr-workflow.md +2 -2
- package/skills/docs/backlog-proposal-workflow.md +1 -1
- package/skills/docs/customer-profile-workflow.md +1 -1
- package/skills/docs/document-ingest-workflow.md +1 -1
- package/skills/docs/evidence-ingest-workflow.md +1 -1
- package/skills/docs/init-docs.md +15 -15
- package/skills/docs/init-project.md +1 -1
- package/skills/docs/prd-workflow.md +3 -3
- package/skills/docs/prfaq-workflow.md +1 -1
- package/skills/docs/product-intelligence-review.md +1 -1
- package/skills/docs/product-intelligence-workflow.md +1 -1
- package/skills/docs/product-signal-workflow.md +9 -9
- package/skills/docs/research-workflow.md +10 -10
- package/skills/docs/runbook-workflow.md +2 -2
- package/skills/docs/strategy-workflow.md +3 -3
- package/skills/exploration/repo-map.md +11 -11
- package/skills/frameworks/django.md +15 -15
- package/skills/frameworks/nextjs.md +16 -16
- package/skills/frameworks/react.md +12 -12
- package/skills/frameworks/spring-boot.md +12 -12
- package/skills/frontend-design/accessibility.md +6 -6
- package/skills/frontend-design/component-patterns.md +1 -1
- package/skills/frontend-design/engineering.md +1 -1
- package/skills/frontend-design/state-management.md +1 -1
- package/skills/frontend-design/ui-aesthetics.md +1 -1
- package/skills/frontend-design/ux-principles.md +1 -1
- package/skills/operating/orchestration-reference.md +27 -27
- package/skills/quality-gates/review-work.md +3 -3
- package/skills/quality-gates/verify-change.md +1 -1
- package/skills/quality-gates/verify-module.md +1 -1
- package/skills/quality-gates/verify-quality.md +1 -1
- package/skills/quality-gates/verify-security.md +1 -1
- package/skills/roles/architect.ai-systems.md +4 -2
- package/skills/roles/architect.data.md +4 -2
- package/skills/roles/architect.enterprise.md +4 -2
- package/skills/roles/architect.integration.md +4 -2
- package/skills/roles/architect.md +7 -5
- package/skills/roles/architect.platform.md +4 -2
- package/skills/roles/data-analyst.experiment.md +4 -2
- package/skills/roles/data-analyst.md +9 -7
- package/skills/roles/data-analyst.product-intelligence.md +4 -2
- package/skills/roles/data-analyst.product.md +4 -2
- package/skills/roles/data-analyst.telemetry.md +4 -2
- package/skills/roles/data-engineer.pipeline.md +4 -2
- package/skills/roles/data-engineer.vector-retrieval.md +4 -2
- package/skills/roles/data-engineer.warehouse.md +4 -2
- package/skills/roles/debugger.md +7 -5
- package/skills/roles/designer.accessibility.md +4 -2
- package/skills/roles/designer.md +10 -8
- package/skills/roles/engineer.ai.md +4 -2
- package/skills/roles/engineer.data.md +5 -3
- package/skills/roles/engineer.md +14 -12
- package/skills/roles/engineer.platform.md +5 -3
- package/skills/roles/operator.docs.md +6 -4
- package/skills/roles/operator.md +6 -4
- package/skills/roles/operator.release.md +4 -2
- package/skills/roles/operator.sre.md +5 -3
- package/skills/roles/orchestrator.md +5 -3
- package/skills/roles/product-manager.ai-product.md +4 -2
- package/skills/roles/product-manager.business-strategy.md +4 -2
- package/skills/roles/product-manager.enterprise.md +4 -2
- package/skills/roles/product-manager.growth.md +4 -2
- package/skills/roles/product-manager.md +6 -4
- package/skills/roles/product-manager.platform.md +4 -2
- package/skills/roles/product-manager.product.md +4 -2
- package/skills/roles/qa.ai-eval.md +4 -2
- package/skills/roles/qa.api-contract.md +4 -2
- package/skills/roles/qa.data-pipeline.md +4 -2
- package/skills/roles/qa.md +7 -5
- package/skills/roles/qa.test-automation.md +5 -3
- package/skills/roles/qa.web-ui.md +4 -2
- package/skills/roles/researcher.explorer.md +4 -2
- package/skills/roles/researcher.md +11 -9
- package/skills/roles/researcher.ux.md +4 -2
- package/skills/roles/reviewer.devil-advocate.md +4 -2
- package/skills/roles/reviewer.evaluator.md +4 -2
- package/skills/roles/reviewer.md +14 -12
- package/skills/roles/reviewer.trace.md +4 -2
- package/skills/roles/security.ai.md +4 -2
- package/skills/roles/security.appsec.md +4 -2
- package/skills/roles/security.cloud.md +4 -2
- package/skills/roles/security.legal-compliance.md +4 -2
- package/skills/roles/security.md +7 -5
- package/skills/roles/security.privacy.md +4 -2
- package/skills/roles/security.supply-chain.md +4 -2
- package/skills/routing.md +14 -14
- package/skills/security/blue-team.md +1 -1
- package/skills/security/code-audit.md +1 -1
- package/skills/security/pentest.md +1 -1
- package/skills/security/red-team.md +1 -1
- package/skills/security/threat-intel.md +1 -1
- package/skills/security/vuln-research.md +1 -1
- package/skills/utility/clean-code.md +2 -2
- package/templates/docs/changelog-entry.md +1 -1
- package/templates/docs/construct_guide.md +13 -13
- package/templates/docs/meta-prd.md +16 -16
- package/templates/docs/one-pager.md +1 -1
- package/templates/docs/persona-artifact.md +36 -0
- package/templates/docs/prd-business.md +1 -1
- package/templates/docs/prd-platform.md +1 -1
- package/templates/docs/prd.md +17 -17
- package/templates/docs/research-brief.md +8 -8
- package/templates/docs/research-finding.md +26 -0
- package/templates/docs/rfc.md +1 -1
- package/templates/docs/skill-artifact.md +27 -0
- package/templates/docs/strategy.md +1 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
commands/work/parallel-review.md
|
|
2
|
+
commands/work/parallel-review.md (Adversarial parallel review) 5 reviewers must agree before output ships
|
|
3
3
|
|
|
4
|
-
Adversarial parallel review
|
|
4
|
+
Adversarial parallel review: 5 reviewers must agree before output ships
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Adversarial parallel review
|
|
7
|
+
description: Adversarial parallel review: 5 reviewers must agree before output ships
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct running a parallel adversarial review of: $ARGUMENTS
|
|
11
11
|
|
|
12
12
|
Dispatch the following 5 review roles concurrently:
|
|
13
13
|
|
|
14
|
-
1. **cx-reviewer
|
|
15
|
-
2. **cx-security
|
|
16
|
-
3. **cx-qa
|
|
17
|
-
4. **cx-devil-advocate
|
|
18
|
-
5. **cx-accessibility** (UI changes) or **cx-trace-reviewer** (non-UI)
|
|
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
19
|
|
|
20
20
|
## Merge Gate
|
|
21
21
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- 006_graph.sql. GraphRAG community columns for entities.
|
|
2
|
+
--
|
|
3
|
+
-- Phase C9 foundations. Adds community_id (label propagation output) and a
|
|
4
|
+
-- community summary table so the Pg-backed deployment can query communities
|
|
5
|
+
-- without a JSONL scan. Solo-mode JSONL remains the source of truth; this
|
|
6
|
+
-- table is the projection.
|
|
7
|
+
|
|
8
|
+
ALTER TABLE construct_entities
|
|
9
|
+
ADD COLUMN IF NOT EXISTS community_id text,
|
|
10
|
+
ADD COLUMN IF NOT EXISTS community_size int;
|
|
11
|
+
|
|
12
|
+
CREATE INDEX IF NOT EXISTS idx_entities_community ON construct_entities(project, community_id);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE IF NOT EXISTS construct_entity_communities (
|
|
15
|
+
community_id text NOT NULL,
|
|
16
|
+
project text NOT NULL,
|
|
17
|
+
size int NOT NULL DEFAULT 0,
|
|
18
|
+
top_members jsonb DEFAULT '[]'::jsonb,
|
|
19
|
+
summary text,
|
|
20
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
21
|
+
PRIMARY KEY (project, community_id)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_communities_size ON construct_entity_communities(project, size DESC);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Hello World
|
|
1
|
+
# Hello World: Construct provider plugin example
|
|
2
2
|
|
|
3
3
|
A minimal reference implementation of the [Construct provider contract](../../docs/providers/authoring.md). Returns canned data; no external credentials required.
|
|
4
4
|
|
|
@@ -18,16 +18,16 @@ construct provider test hello-world --query "greet"
|
|
|
18
18
|
## What this shows
|
|
19
19
|
|
|
20
20
|
- The factory function signature (`create({ env })`)
|
|
21
|
-
- `meta
|
|
22
|
-
- `configSchema
|
|
23
|
-
- `health()
|
|
24
|
-
- `read()
|
|
25
|
-
- `search()
|
|
21
|
+
- `meta`: id, displayName, capabilities
|
|
22
|
+
- `configSchema`: JSON Schema for provider settings
|
|
23
|
+
- `health()`: always-healthy health check
|
|
24
|
+
- `read()`: look up a single item by id
|
|
25
|
+
- `search()`: full-text filter over canned items
|
|
26
26
|
|
|
27
27
|
## Adapting for real providers
|
|
28
28
|
|
|
29
29
|
1. Replace the `ITEMS` constant with real API calls.
|
|
30
|
-
2. Add auth logic in `create()
|
|
30
|
+
2. Add auth logic in `create()`: read tokens from `env` or `config`.
|
|
31
31
|
3. Add `watch()` and/or `webhook()` if your source supports push.
|
|
32
32
|
4. Publish as an npm package and register via `construct provider plugins add <package>`.
|
|
33
33
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
examples/seed-observations/README.md
|
|
2
|
+
examples/seed-observations/README.md: seed corpus for Construct's in-tree memory layer.
|
|
3
3
|
|
|
4
4
|
Run `construct bootstrap` to import these files into the local observation and entity stores.
|
|
5
5
|
The seed corpus gives the hybrid BM25 + cosine retrieval a meaningful starting signal
|
|
@@ -25,14 +25,14 @@ This corpus seeds the Construct memory layer with high-value starting knowledge.
|
|
|
25
25
|
construct bootstrap
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Imports all three files into the local observation store. Safe to re-run
|
|
28
|
+
Imports all three files into the local observation store. Safe to re-run: duplicate
|
|
29
29
|
observations are deduplicated by content hash.
|
|
30
30
|
|
|
31
31
|
## Payoff Timeline
|
|
32
32
|
|
|
33
|
-
- **0 sessions
|
|
34
|
-
- **5 sessions
|
|
35
|
-
- **20 sessions
|
|
36
|
-
- **50+ sessions
|
|
33
|
+
- **0 sessions**: retrieval is cold; seed corpus provides baseline recall
|
|
34
|
+
- **5 sessions**: personal patterns start surfacing; seed corpus still dominant
|
|
35
|
+
- **20 sessions**: personal patterns dominate; seed corpus recedes to backstop
|
|
36
|
+
- **50+ sessions**: retrieval is fully personalized; seed corpus rarely surfaces
|
|
37
37
|
|
|
38
38
|
Run `construct memory stats` to see whether memory is paying off.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
examples/seed-observations/anti-patterns.md
|
|
2
|
+
examples/seed-observations/anti-patterns.md: recurring mistakes for Construct memory seed corpus.
|
|
3
3
|
|
|
4
4
|
Each entry becomes one observation in the store with category "anti-pattern". Imported via
|
|
5
5
|
`construct bootstrap`.
|
|
@@ -9,36 +9,36 @@ Each entry becomes one observation in the store with category "anti-pattern". Im
|
|
|
9
9
|
|
|
10
10
|
## Context Waste
|
|
11
11
|
|
|
12
|
-
**Reading the whole file to find one function
|
|
12
|
+
**Reading the whole file to find one function**: Use Grep to locate the line number, then Read with offset/limit to read only that section. Reading a 500-line file to find a 10-line function burns ~5k tokens.
|
|
13
13
|
|
|
14
|
-
**Re-reading already-loaded files
|
|
14
|
+
**Re-reading already-loaded files**: If the file content is already in context, reading it again is pure waste. Check context before issuing another Read.
|
|
15
15
|
|
|
16
|
-
**Sequential tool calls that could be parallel
|
|
16
|
+
**Sequential tool calls that could be parallel**: Issuing Glob, then Read, then Grep one at a time when they're independent triples the latency. Batch independent calls in one message.
|
|
17
17
|
|
|
18
18
|
## Code Authoring
|
|
19
19
|
|
|
20
|
-
**Guessing a function signature
|
|
20
|
+
**Guessing a function signature**: Never guess an API or function signature. Read the source or check the docs first. Wrong signatures produce runtime errors that cost a full round-trip to fix.
|
|
21
21
|
|
|
22
|
-
**Editing without reading
|
|
22
|
+
**Editing without reading**: Issuing an Edit without a prior Read produces "oldString not found" failures when the file differs from assumptions. Always read first.
|
|
23
23
|
|
|
24
|
-
**Creating new files instead of editing existing ones
|
|
24
|
+
**Creating new files instead of editing existing ones**: Adding a new file when the logic belongs in an existing one fragments the codebase. Check for the right home first.
|
|
25
25
|
|
|
26
|
-
**Commenting what the code already says
|
|
26
|
+
**Commenting what the code already says**: `// increment counter` above `count++` adds noise. Comments earn their place by explaining non-obvious constraints, not restating syntax.
|
|
27
27
|
|
|
28
28
|
## Testing
|
|
29
29
|
|
|
30
|
-
**Testing the mock instead of the behavior
|
|
30
|
+
**Testing the mock instead of the behavior**: A test that only verifies that a mock was called doesn't confirm the real behavior. Prefer integration-level assertions over spy counts.
|
|
31
31
|
|
|
32
|
-
**Skipping the failing test to make CI green
|
|
32
|
+
**Skipping the failing test to make CI green**: Skipping tests hides regressions. Fix the failure or delete the test with a comment explaining why it no longer applies.
|
|
33
33
|
|
|
34
34
|
## Git
|
|
35
35
|
|
|
36
|
-
**Amending a pushed commit
|
|
36
|
+
**Amending a pushed commit**: `git commit --amend` after a push requires force-push, which rewrites shared history. Use a new commit instead.
|
|
37
37
|
|
|
38
|
-
**Committing without running tests
|
|
38
|
+
**Committing without running tests**: Tests exist to catch regressions before they land on main. A green build is the only signal that a change is safe to ship.
|
|
39
39
|
|
|
40
40
|
## Agent Behavior
|
|
41
41
|
|
|
42
|
-
**Stopping without surfacing incomplete tasks
|
|
42
|
+
**Stopping without surfacing incomplete tasks**: An agent that stops while tasks are `in_progress` or have unmet acceptance criteria leaves the user without visibility. Surface the state before stopping.
|
|
43
43
|
|
|
44
|
-
**Making assumptions about user intent
|
|
44
|
+
**Making assumptions about user intent**: When a requirement is ambiguous, ask one targeted question rather than guessing and building the wrong thing.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
examples/seed-observations/decisions.md
|
|
2
|
+
examples/seed-observations/decisions.md: key architectural decisions for Construct memory seed corpus.
|
|
3
3
|
|
|
4
4
|
Each entry becomes one observation in the store with category "decision". Imported via
|
|
5
5
|
`construct bootstrap`.
|
|
@@ -13,7 +13,7 @@ Each entry becomes one observation in the store with category "decision". Import
|
|
|
13
13
|
|
|
14
14
|
## Two-Phase Sync with Lockfile
|
|
15
15
|
|
|
16
|
-
`construct sync` writes to `.cx/sync-staging/` first, then atomically renames into place. A lockfile at `.cx/sync.lock` prevents concurrent runs from corrupting the output. This makes partial syncs impossible
|
|
16
|
+
`construct sync` writes to `.cx/sync-staging/` first, then atomically renames into place. A lockfile at `.cx/sync.lock` prevents concurrent runs from corrupting the output. This makes partial syncs impossible: either the full sync lands or nothing does.
|
|
17
17
|
|
|
18
18
|
## Hook Ceiling: 30
|
|
19
19
|
|
|
@@ -25,7 +25,7 @@ The memory layer uses a hashing bag-of-words cosine similarity for dense recall
|
|
|
25
25
|
|
|
26
26
|
## Tiered Session-Start Injection
|
|
27
27
|
|
|
28
|
-
Session-start injects context in three tiers: Tier 1 (always
|
|
28
|
+
Session-start injects context in three tiers: Tier 1 (always (header, branch, status), Tier 2 (when fresh and meaningful) workflow, observations, context.md), Tier 3 (hint-only: efficiency snapshot, skill scope). This caps injection size and avoids loading the model with stale or irrelevant context on every session.
|
|
29
29
|
|
|
30
30
|
## Declarative Policy Rules
|
|
31
31
|
|
|
@@ -33,4 +33,4 @@ Hook guards for workflow, drive, bootstrap, and task behavior are expressed as Y
|
|
|
33
33
|
|
|
34
34
|
## Registry as Single Source of Truth
|
|
35
35
|
|
|
36
|
-
`agents/registry.json` is the canonical definition for all agents on all platforms (Claude Code, OpenCode, Codex, Copilot). `construct sync` regenerates all platform-specific files from the registry. Never edit generated files directly
|
|
36
|
+
`agents/registry.json` is the canonical definition for all agents on all platforms (Claude Code, OpenCode, Codex, Copilot). `construct sync` regenerates all platform-specific files from the registry. Never edit generated files directly: edits are overwritten on the next sync.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
examples/seed-observations/patterns.md
|
|
2
|
+
examples/seed-observations/patterns.md: engineering patterns for Construct memory seed corpus.
|
|
3
3
|
|
|
4
4
|
Each entry becomes one observation in the store. Format: ## Category, then pattern entries
|
|
5
5
|
with **Pattern name** and description. Imported via `construct bootstrap`.
|
|
@@ -9,34 +9,34 @@ with **Pattern name** and description. Imported via `construct bootstrap`.
|
|
|
9
9
|
|
|
10
10
|
## Code Quality
|
|
11
11
|
|
|
12
|
-
**Read before edit
|
|
12
|
+
**Read before edit**: Always read the target file with the Read tool before writing or editing. Editing without reading produces stale edits that fail with "oldString not found".
|
|
13
13
|
|
|
14
|
-
**Parallel tool calls for independent work
|
|
14
|
+
**Parallel tool calls for independent work**: When two tool calls don't depend on each other's output, issue them in the same message. Cuts wall time roughly in half for multi-file operations.
|
|
15
15
|
|
|
16
|
-
**Probe before bulk read
|
|
16
|
+
**Probe before bulk read**: Use Glob or Grep to identify which files are relevant before reading them. Bulk-reading 10 files to find the one that matters burns context and slows response.
|
|
17
17
|
|
|
18
|
-
**Edit smallest possible diff
|
|
18
|
+
**Edit smallest possible diff**: Target the exact lines that need to change. Large oldString blocks with unchanged surrounding context are fragile when the file is modified concurrently.
|
|
19
19
|
|
|
20
20
|
## Testing
|
|
21
21
|
|
|
22
|
-
**Run targeted test first
|
|
22
|
+
**Run targeted test first**: Before running the full suite, run only the affected test file. Faster feedback loop; full suite confirms no regressions.
|
|
23
23
|
|
|
24
|
-
**Assert the contract, not the implementation
|
|
24
|
+
**Assert the contract, not the implementation**: Tests that check internal state (private variables, call counts) break on refactors that don't change behavior. Test the public output.
|
|
25
25
|
|
|
26
|
-
**Table-driven tests for edge cases
|
|
26
|
+
**Table-driven tests for edge cases**: When a function has many input/output pairs to verify, a single table-driven test is more maintainable than N separate `it()` blocks.
|
|
27
27
|
|
|
28
28
|
## Git Discipline
|
|
29
29
|
|
|
30
|
-
**Branch, test, merge
|
|
30
|
+
**Branch, test, merge**: Never commit directly to main. Branch for every feature or fix, verify tests pass, then merge.
|
|
31
31
|
|
|
32
|
-
**Commit message: why, not what
|
|
32
|
+
**Commit message: why, not what**: The diff already shows what changed. The commit message should explain why. "fix dry-run bypass" is less useful than "fix: dry-run skipped mkdirp calls, causing 31 phantom files in test home".
|
|
33
33
|
|
|
34
|
-
**Atomic commits
|
|
34
|
+
**Atomic commits**: One logical change per commit. Mixing a bug fix with a refactor makes bisect and revert painful.
|
|
35
35
|
|
|
36
36
|
## Agent Dispatch
|
|
37
37
|
|
|
38
|
-
**Route through Construct
|
|
38
|
+
**Route through Construct**: Construct is the single dispatch point. Invoking specialist agents directly bypasses context injection and workflow tracking.
|
|
39
39
|
|
|
40
|
-
**Subagent for isolated subtasks
|
|
40
|
+
**Subagent for isolated subtasks**: Use Task tool dispatch when a subtask has a clear input/output contract and doesn't need the parent conversation history. Keeps the main context lean.
|
|
41
41
|
|
|
42
|
-
**Timeout specialist calls
|
|
42
|
+
**Timeout specialist calls**: Agent dispatch without a timeout can block indefinitely. Set `timeout: 120_000` as a floor; use 300_000 for heavy analysis tasks.
|
package/lib/auto-docs.mjs
CHANGED
|
@@ -71,7 +71,7 @@ function buildCoreDocsContract() {
|
|
|
71
71
|
'| `docs/README.md` | Docs index and maintenance contract | Core docs set or maintenance expectations change |',
|
|
72
72
|
'| `docs/concepts/architecture.md` | Canonical architecture and invariants | Runtime shape, contracts, boundaries, or major dependencies change |',
|
|
73
73
|
'',
|
|
74
|
-
'`plan.md` is a local working document. `construct init` creates it for the active session, but it is gitignored and not committed
|
|
74
|
+
'`plan.md` is a local working document. `construct init` creates it for the active session, but it is gitignored and not committed; durable work belongs in the tracker (Beads or external).',
|
|
75
75
|
'',
|
|
76
76
|
'Tracker hierarchy: external tracker (prefer Beads) for durable work, `plan.md` for the local working plan, and cass-memory via MCP `memory` for cross-session recall.',
|
|
77
77
|
'',
|
|
@@ -82,7 +82,7 @@ function buildCoreDocsContract() {
|
|
|
82
82
|
|
|
83
83
|
const DIR_DESCRIPTIONS = {
|
|
84
84
|
agents: 'Registry and generated platform adapter chains',
|
|
85
|
-
apps: 'User-facing apps shipped from this repo (e.g
|
|
85
|
+
apps: 'User-facing apps shipped from this repo (e.g. apps/docs/, the Fumadocs docs site)',
|
|
86
86
|
bin: 'CLI entrypoint (`construct`)',
|
|
87
87
|
claude: 'Claude Code integration (agents, settings template)',
|
|
88
88
|
commands: 'Command prompt assets',
|
|
@@ -160,9 +160,14 @@ function buildAgentsTable(rootDir) {
|
|
|
160
160
|
|
|
161
161
|
const rows = ['| Agent | Tier | Purpose |', '|---|---|---|'];
|
|
162
162
|
for (const agent of agents.slice(0, 30)) {
|
|
163
|
-
const name = agent.id ?? agent.name ?? '
|
|
164
|
-
const tier = agent.tier ?? agent.model_tier ?? '
|
|
165
|
-
|
|
163
|
+
const name = agent.id ?? agent.name ?? 'n/a';
|
|
164
|
+
const tier = agent.tier ?? agent.model_tier ?? 'n/a';
|
|
165
|
+
// Strip em-dashes from descriptions so the prose lint stays clean across
|
|
166
|
+
// AUTO regen. Em-dashes are allowed in the registry's source descriptions
|
|
167
|
+
// but never in the rendered markdown.
|
|
168
|
+
const purpose = (agent.description ?? agent.purpose ?? '')
|
|
169
|
+
.split('\n')[0].slice(0, 80)
|
|
170
|
+
.replace(/ — /g, '. ').replace(/—/g, ',');
|
|
166
171
|
rows.push(`| \`${name}\` | ${tier} | ${purpose} |`);
|
|
167
172
|
}
|
|
168
173
|
if (agents.length > 30) rows.push(`| *(+${agents.length - 30} more)* | | |`);
|
package/lib/cli-commands.mjs
CHANGED
|
@@ -92,7 +92,7 @@ export const CLI_COMMANDS = [
|
|
|
92
92
|
emoji: '📥',
|
|
93
93
|
category: 'Core',
|
|
94
94
|
core: true,
|
|
95
|
-
description: 'View and process
|
|
95
|
+
description: 'View and process the active profile\'s intake queue (queue label varies by profile)',
|
|
96
96
|
usage: 'construct intake list|show|done|skip',
|
|
97
97
|
},
|
|
98
98
|
{
|
|
@@ -128,6 +128,50 @@ export const CLI_COMMANDS = [
|
|
|
128
128
|
{ name: 'search <query>', desc: 'Search customer profiles by name/alias' },
|
|
129
129
|
],
|
|
130
130
|
},
|
|
131
|
+
{
|
|
132
|
+
name: 'knowledge',
|
|
133
|
+
emoji: '🧠',
|
|
134
|
+
category: 'Work',
|
|
135
|
+
core: false,
|
|
136
|
+
description: 'Query, index, or add to the project knowledge base',
|
|
137
|
+
usage: 'construct knowledge trends|index|add',
|
|
138
|
+
subcommands: [
|
|
139
|
+
{ name: 'trends', desc: 'Show trend report across observations and artifacts' },
|
|
140
|
+
{ name: 'index', desc: 'Rebuild the local RAG corpus over .cx/ artifacts' },
|
|
141
|
+
{ name: 'add --source=research --slug=<id> --topic="..." [--source-url=<url>]', desc: 'Persist a research finding into .cx/knowledge/external/research/' },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'sandbox',
|
|
146
|
+
emoji: '🧪',
|
|
147
|
+
category: 'Core',
|
|
148
|
+
core: true,
|
|
149
|
+
description: 'Isolated tmpdir-based environment for QA / specialist dry-runs',
|
|
150
|
+
usage: 'construct sandbox create|list|delete|prune [--profile=<id>]',
|
|
151
|
+
subcommands: [
|
|
152
|
+
{ name: 'create [--profile=<id>]', desc: 'Create a new sandbox under ~/.cx/sandboxes/' },
|
|
153
|
+
{ name: 'list', desc: 'List existing sandboxes, newest first' },
|
|
154
|
+
{ name: 'delete <id>', desc: 'Remove one sandbox by id' },
|
|
155
|
+
{ name: 'prune [--days=N]', desc: 'Remove sandboxes older than N days (default 7)' },
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'profile',
|
|
160
|
+
emoji: '🧭',
|
|
161
|
+
category: 'Core',
|
|
162
|
+
core: true,
|
|
163
|
+
description: 'Manage the active org profile and its lifecycle (draft, promote, archive, health)',
|
|
164
|
+
usage: 'construct profile show|list|set|create|drafts|archive|health',
|
|
165
|
+
subcommands: [
|
|
166
|
+
{ name: 'show', desc: 'Show the active profile' },
|
|
167
|
+
{ name: 'list', desc: 'List curated profiles' },
|
|
168
|
+
{ name: 'set <id>', desc: 'Switch the active profile (writes construct.config.json)' },
|
|
169
|
+
{ name: 'create <id> [--display=…] [--role=…] [--department=…] [--yes|--dry-run]', desc: 'Scaffold a draft profile; previews and confirms by default, prompts interactively when no flags' },
|
|
170
|
+
{ name: 'drafts', desc: 'List in-progress draft profiles' },
|
|
171
|
+
{ name: 'archive <id> --reason="..."', desc: 'Move a curated profile into archive/profiles/<id>/' },
|
|
172
|
+
{ name: 'health <id> [--days=N]', desc: 'Per-profile observation + outcome rollup' },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
131
175
|
{
|
|
132
176
|
name: 'workspace',
|
|
133
177
|
emoji: '🏢',
|
package/lib/comment-lint.mjs
CHANGED
|
@@ -42,7 +42,13 @@ function requiresHeader(rel) {
|
|
|
42
42
|
if (rel.startsWith('lib/server/static/')) {
|
|
43
43
|
return { required: false, type: null };
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
// Markdown files always get markdown-style headers, even if the directory
|
|
46
|
+
// glob (e.g. ^tests/) primarily targets JS sources. Without this, .md docs
|
|
47
|
+
// co-located with .mjs tests were forced into /** */ format.
|
|
48
|
+
let type;
|
|
49
|
+
if (ext === '.md') type = mdMatch || jsMatch ? 'md' : null;
|
|
50
|
+
else if (jsMatch && ext !== '.html') type = 'js';
|
|
51
|
+
else type = (jsMatch || mdMatch) ? 'md' : null;
|
|
46
52
|
return { required: jsMatch || mdMatch, type };
|
|
47
53
|
}
|
|
48
54
|
|
package/lib/config/schema.mjs
CHANGED
|
@@ -20,6 +20,7 @@ export const CONFIG_SCHEMA_VERSION = 1;
|
|
|
20
20
|
|
|
21
21
|
export const DEPLOYMENT_MODES = ['solo', 'team', 'enterprise'];
|
|
22
22
|
export const MCP_BROKER_VALUES = ['auto', 'on', 'off'];
|
|
23
|
+
export const DEFAULT_PROFILE_ID = 'rnd';
|
|
23
24
|
|
|
24
25
|
export const DEFAULT_PROJECT_CONFIG = Object.freeze({
|
|
25
26
|
version: CONFIG_SCHEMA_VERSION,
|
|
@@ -31,6 +32,7 @@ export const DEFAULT_PROJECT_CONFIG = Object.freeze({
|
|
|
31
32
|
tenantId: null,
|
|
32
33
|
}),
|
|
33
34
|
providers: Object.freeze({}),
|
|
35
|
+
profile: DEFAULT_PROFILE_ID,
|
|
34
36
|
autoEmbed: false,
|
|
35
37
|
telemetry: Object.freeze({
|
|
36
38
|
enabled: true,
|
|
@@ -77,6 +79,7 @@ export const FIELD_RULES = {
|
|
|
77
79
|
},
|
|
78
80
|
},
|
|
79
81
|
providers: { type: 'object', required: false },
|
|
82
|
+
profile: { type: 'string', required: false, maxLength: 40 },
|
|
80
83
|
autoEmbed: { type: 'boolean', required: false },
|
|
81
84
|
telemetry: {
|
|
82
85
|
type: 'object',
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lib/flavors/loader.mjs — Role flavor overlay loader and validator.
|
|
3
|
+
*
|
|
4
|
+
* Reads frontmatter from skills/roles/<role>.<flavor>.md files, exposes:
|
|
5
|
+
* - listFlavors(role?, profile?) — overlays that apply to role and profile
|
|
6
|
+
* - validateFlavor(path) — frontmatter conformance check against the schema
|
|
7
|
+
* - perRoleFlavorCount(profile) — flavor counts per role for cap enforcement
|
|
8
|
+
*
|
|
9
|
+
* The cap is enforced at sync time and in lint-prose's sibling lint-flavors.
|
|
10
|
+
* Six flavors per role per profile is the current ceiling; bumping requires
|
|
11
|
+
* an ADR. Existing roles like qa already have 6, so the cap is set tight.
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const REPO_ROOT = path.resolve(MODULE_DIR, '..', '..');
|
|
19
|
+
const FLAVORS_DIR = path.join(REPO_ROOT, 'skills', 'roles');
|
|
20
|
+
|
|
21
|
+
export const FLAVOR_CAP_PER_ROLE_PER_PROFILE = 6;
|
|
22
|
+
|
|
23
|
+
// Files may start with an HTML comment block before the YAML frontmatter.
|
|
24
|
+
// The /m flag lets ^ match line-start so we find the YAML wherever it sits.
|
|
25
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---/m;
|
|
26
|
+
|
|
27
|
+
function parseFrontmatter(content) {
|
|
28
|
+
const m = FRONTMATTER_RE.exec(content);
|
|
29
|
+
if (!m) return null;
|
|
30
|
+
const out = {};
|
|
31
|
+
let currentArrayKey = null;
|
|
32
|
+
for (const rawLine of m[1].split('\n')) {
|
|
33
|
+
const line = rawLine.replace(/\r$/, '');
|
|
34
|
+
if (!line.trim()) continue;
|
|
35
|
+
if (currentArrayKey && /^\s*-\s+/.test(line)) {
|
|
36
|
+
out[currentArrayKey].push(line.replace(/^\s*-\s+/, '').trim());
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
currentArrayKey = null;
|
|
40
|
+
const kv = /^([a-z_]+):\s*(.*)$/.exec(line);
|
|
41
|
+
if (!kv) continue;
|
|
42
|
+
const [, key, valueRaw] = kv;
|
|
43
|
+
const value = valueRaw.trim();
|
|
44
|
+
if (value === '') {
|
|
45
|
+
out[key] = [];
|
|
46
|
+
currentArrayKey = key;
|
|
47
|
+
} else if (value.startsWith('[') && value.endsWith(']')) {
|
|
48
|
+
const items = value.slice(1, -1).split(',').map((s) => s.trim()).filter(Boolean);
|
|
49
|
+
out[key] = items;
|
|
50
|
+
} else if (value === 'null') {
|
|
51
|
+
out[key] = null;
|
|
52
|
+
} else if (/^-?\d+$/.test(value)) {
|
|
53
|
+
out[key] = Number(value);
|
|
54
|
+
} else {
|
|
55
|
+
out[key] = value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Read and parse every overlay in skills/roles/. Returns { path, frontmatter }
|
|
63
|
+
* tuples. Files without parseable frontmatter are skipped silently.
|
|
64
|
+
*/
|
|
65
|
+
export function listAllFlavors() {
|
|
66
|
+
if (!fs.existsSync(FLAVORS_DIR)) return [];
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const f of fs.readdirSync(FLAVORS_DIR)) {
|
|
69
|
+
if (!f.endsWith('.md') || f === 'README.md') continue;
|
|
70
|
+
const full = path.join(FLAVORS_DIR, f);
|
|
71
|
+
const content = fs.readFileSync(full, 'utf8');
|
|
72
|
+
const fm = parseFrontmatter(content);
|
|
73
|
+
if (fm) out.push({ path: full, file: f, frontmatter: fm });
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Return overlays that match the given role (any-flavor for that role) and
|
|
80
|
+
* apply to the given profile. Pass undefined for either to skip that filter.
|
|
81
|
+
*/
|
|
82
|
+
export function listFlavors({ role, profile } = {}) {
|
|
83
|
+
return listAllFlavors().filter(({ frontmatter }) => {
|
|
84
|
+
if (role) {
|
|
85
|
+
const roleId = frontmatter.role || '';
|
|
86
|
+
const baseRole = roleId.split('.')[0];
|
|
87
|
+
if (baseRole !== role && roleId !== role) return false;
|
|
88
|
+
}
|
|
89
|
+
if (profile) {
|
|
90
|
+
const profiles = frontmatter.profiles;
|
|
91
|
+
if (!Array.isArray(profiles) || profiles.length === 0) return true;
|
|
92
|
+
if (!profiles.includes(profile)) return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Count flavor overlays per base role for a given profile.
|
|
100
|
+
* Returns { [baseRole]: count }. Excludes the canonical base file (e.g.
|
|
101
|
+
* architect.md) so only true flavors count toward the cap.
|
|
102
|
+
*/
|
|
103
|
+
export function perRoleFlavorCount(profile = 'rnd') {
|
|
104
|
+
const counts = {};
|
|
105
|
+
for (const entry of listFlavors({ profile })) {
|
|
106
|
+
const roleId = entry.frontmatter.role || '';
|
|
107
|
+
const isFlavor = roleId.includes('.');
|
|
108
|
+
if (!isFlavor) continue;
|
|
109
|
+
const base = roleId.split('.')[0];
|
|
110
|
+
counts[base] = (counts[base] || 0) + 1;
|
|
111
|
+
}
|
|
112
|
+
return counts;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Validate one overlay's frontmatter against the schema invariants. Returns
|
|
117
|
+
* an array of error strings; empty array means valid.
|
|
118
|
+
*/
|
|
119
|
+
export function validateFlavor(filePath) {
|
|
120
|
+
const errors = [];
|
|
121
|
+
if (!fs.existsSync(filePath)) return [`file not found: ${filePath}`];
|
|
122
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
123
|
+
const fm = parseFrontmatter(content);
|
|
124
|
+
if (!fm) return [`${filePath}: missing or unparseable frontmatter`];
|
|
125
|
+
|
|
126
|
+
if (!fm.role || !/^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)?$/.test(fm.role)) {
|
|
127
|
+
errors.push(`${filePath}: invalid role "${fm.role}"`);
|
|
128
|
+
}
|
|
129
|
+
if (!Array.isArray(fm.applies_to) || fm.applies_to.length === 0) {
|
|
130
|
+
errors.push(`${filePath}: applies_to must be a non-empty array`);
|
|
131
|
+
}
|
|
132
|
+
if (!Array.isArray(fm.profiles) || fm.profiles.length === 0) {
|
|
133
|
+
errors.push(`${filePath}: profiles must be a non-empty array; add a frontmatter line like \`profiles: [rnd]\``);
|
|
134
|
+
}
|
|
135
|
+
return errors;
|
|
136
|
+
}
|
|
@@ -119,7 +119,7 @@ try {
|
|
|
119
119
|
if (success !== null) {
|
|
120
120
|
const learningDir = join(cxDir, 'learning', agentName);
|
|
121
121
|
mkdirSync(learningDir, { recursive: true });
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
const learningFile = join(learningDir, `${Date.now()}-${success ? 'success' : 'failure'}.json`);
|
|
124
124
|
const learningEntry = {
|
|
125
125
|
agent: agentName,
|
|
@@ -127,13 +127,32 @@ try {
|
|
|
127
127
|
description,
|
|
128
128
|
outcome,
|
|
129
129
|
timestamp: new Date().toISOString(),
|
|
130
|
-
// Extract potential patterns from description
|
|
131
130
|
keywords: extractKeywords(description),
|
|
132
131
|
taskType: classifyTaskType(description)
|
|
133
132
|
};
|
|
134
|
-
|
|
133
|
+
|
|
135
134
|
writeFileSync(learningFile, JSON.stringify(learningEntry, null, 2) + '\n');
|
|
136
135
|
}
|
|
136
|
+
|
|
137
|
+
// A3 outcome capture: record the per-project outcome JSONL that feeds
|
|
138
|
+
// .cx/outcomes/_summary.json and the classifier's tiebreaker. Best-effort;
|
|
139
|
+
// any failure leaves the existing telemetry paths above untouched.
|
|
140
|
+
if (success !== null) {
|
|
141
|
+
try {
|
|
142
|
+
const { recordOutcome } = await import('../outcomes/record.mjs');
|
|
143
|
+
const { resolveActiveProfile } = await import('../profiles/loader.mjs');
|
|
144
|
+
const roleId = agentName.replace(/^cx-/, '');
|
|
145
|
+
const projectCwd = input?.cwd || process.cwd();
|
|
146
|
+
const activeProfile = resolveActiveProfile(projectCwd);
|
|
147
|
+
recordOutcome(projectCwd, {
|
|
148
|
+
role: roleId,
|
|
149
|
+
success,
|
|
150
|
+
notes: outcome,
|
|
151
|
+
source: 'agent-tracker',
|
|
152
|
+
profile: activeProfile?.id ?? null,
|
|
153
|
+
});
|
|
154
|
+
} catch { /* best effort */ }
|
|
155
|
+
}
|
|
137
156
|
}
|
|
138
157
|
|
|
139
158
|
} catch (error) {
|
|
@@ -189,13 +189,26 @@ function runNpmGate(projectDir) {
|
|
|
189
189
|
const jobs = [];
|
|
190
190
|
if (hasTest || hasCi) jobs.push({ label: 'tests', cmd: runner, args: ['run', testScript], timeout: 90_000 });
|
|
191
191
|
if (hasBuild) jobs.push({ label: 'build', cmd: runner, args: ['run', 'build'], timeout: 120_000 });
|
|
192
|
-
|
|
192
|
+
// Use the project's package manager for audit so the lockfile format matches.
|
|
193
|
+
const auditArgs = runner === 'pnpm'
|
|
194
|
+
? ['audit', '--prod', '--audit-level=high']
|
|
195
|
+
: runner === 'yarn'
|
|
196
|
+
? ['npm-audit', '--groups', 'dependencies', '--level', 'high']
|
|
197
|
+
: ['audit', '--omit=dev', '--audit-level=high'];
|
|
198
|
+
jobs.push({ label: 'audit', cmd: runner, args: auditArgs, timeout: 30_000 });
|
|
193
199
|
|
|
194
200
|
if (existsSync(join(projectDir, 'bin/construct'))) {
|
|
195
201
|
jobs.push({ label: 'evals', cmd: 'node', args: ['bin/construct', 'evals', 'retrieval'], timeout: 60_000 });
|
|
196
202
|
jobs.push({ label: 'docs', cmd: 'node', args: ['bin/construct', 'docs:verify'], timeout: 15_000 });
|
|
197
203
|
}
|
|
198
204
|
|
|
205
|
+
if (existsSync(join(projectDir, 'scripts/lint-prose.mjs'))) {
|
|
206
|
+
jobs.push({ label: 'prose', cmd: 'node', args: ['scripts/lint-prose.mjs'], timeout: 15_000 });
|
|
207
|
+
}
|
|
208
|
+
if (existsSync(join(projectDir, 'scripts/lint-profiles.mjs'))) {
|
|
209
|
+
jobs.push({ label: 'profiles', cmd: 'node', args: ['scripts/lint-profiles.mjs', '--quiet'], timeout: 15_000 });
|
|
210
|
+
}
|
|
211
|
+
|
|
199
212
|
return { projectDir, jobs };
|
|
200
213
|
}
|
|
201
214
|
|
|
@@ -128,8 +128,9 @@ function optimizeAgent(agentName) {
|
|
|
128
128
|
const startTime = Date.now();
|
|
129
129
|
|
|
130
130
|
try {
|
|
131
|
-
//
|
|
132
|
-
|
|
131
|
+
// Preview only. The new contract requires an explicit --apply from the
|
|
132
|
+
// operator; hook-driven runs must never modify prompt files.
|
|
133
|
+
const result = spawnSync(CONSTRUCT_BIN, ['optimize', agentName, `--days=${OPTIMIZATION_DAYS}`, '--dry-run'], {
|
|
133
134
|
cwd,
|
|
134
135
|
encoding: 'utf8',
|
|
135
136
|
timeout: 300000, // 5 minutes timeout for optimization
|