@geraldmaron/construct 1.0.6 → 1.0.7
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 +19 -17
- package/agents/contracts.json +617 -2
- package/agents/contracts.schema.json +83 -0
- package/agents/prompts/cx-accessibility.md +2 -0
- package/agents/prompts/cx-ai-engineer.md +2 -0
- package/agents/prompts/cx-architect.md +2 -0
- package/agents/prompts/cx-business-strategist.md +2 -0
- package/agents/prompts/cx-data-analyst.md +2 -0
- package/agents/prompts/cx-data-engineer.md +2 -0
- package/agents/prompts/cx-debugger.md +2 -0
- package/agents/prompts/cx-designer.md +2 -0
- package/agents/prompts/cx-devil-advocate.md +2 -0
- package/agents/prompts/cx-docs-keeper.md +2 -0
- package/agents/prompts/cx-engineer.md +2 -0
- package/agents/prompts/cx-evaluator.md +2 -0
- package/agents/prompts/cx-explorer.md +2 -0
- package/agents/prompts/cx-legal-compliance.md +2 -0
- package/agents/prompts/cx-operations.md +2 -0
- package/agents/prompts/cx-orchestrator.md +2 -0
- package/agents/prompts/cx-platform-engineer.md +2 -0
- package/agents/prompts/cx-product-manager.md +2 -0
- package/agents/prompts/cx-qa.md +2 -0
- package/agents/prompts/cx-rd-lead.md +2 -0
- package/agents/prompts/cx-release-manager.md +2 -0
- package/agents/prompts/cx-researcher.md +2 -0
- package/agents/prompts/cx-reviewer.md +2 -0
- package/agents/prompts/cx-security.md +2 -0
- package/agents/prompts/cx-sre.md +2 -0
- package/agents/prompts/cx-test-automation.md +2 -0
- package/agents/prompts/cx-trace-reviewer.md +2 -0
- package/agents/prompts/cx-ux-researcher.md +2 -0
- package/agents/registry.json +8 -3
- package/bin/construct +209 -32
- package/commands/build/feature.md +1 -1
- package/commands/build/fix.md +1 -1
- package/commands/design/access.md +1 -1
- package/commands/design/flow.md +1 -1
- package/commands/design/ui.md +1 -1
- package/commands/measure/experiment.md +1 -1
- package/commands/measure/metrics.md +1 -1
- package/commands/measure/results.md +1 -1
- package/commands/plan/api.md +1 -1
- package/commands/plan/challenge.md +1 -1
- package/commands/plan/decide.md +1 -1
- package/commands/plan/feature.md +1 -1
- package/commands/plan/requirements.md +1 -1
- package/commands/remember/context.md +1 -1
- package/commands/remember/handoff.md +1 -1
- package/commands/remember/runbook.md +1 -1
- package/commands/review/code.md +1 -1
- package/commands/review/quality.md +1 -1
- package/commands/review/security.md +1 -1
- package/commands/ship/ready.md +1 -1
- package/commands/ship/release.md +1 -1
- package/commands/ship/status.md +1 -1
- package/commands/understand/docs.md +1 -1
- package/commands/understand/this.md +1 -1
- package/commands/understand/why.md +1 -1
- package/commands/work/clean.md +1 -1
- package/commands/work/drive.md +1 -1
- package/commands/work/optimize-prompts.md +1 -1
- package/commands/work/parallel-review.md +1 -1
- package/lib/auto-docs.mjs +7 -7
- package/lib/boundary.mjs +126 -0
- package/lib/cache-strategy-google.js +26 -31
- package/lib/comment-lint.mjs +134 -0
- package/lib/contracts/validate.mjs +323 -0
- package/lib/daemons/contract.mjs +210 -0
- package/lib/docs-verify.mjs +59 -6
- package/lib/doctor/cli.mjs +16 -1
- package/lib/doctor/index.mjs +3 -1
- package/lib/doctor/watchers/consistency.mjs +310 -0
- package/lib/doctor/watchers/mcp-protocol.mjs +232 -0
- package/lib/document-extract.mjs +211 -1
- package/lib/embed/cli.mjs +124 -3
- package/lib/embed/daemon.mjs +43 -4
- package/lib/embed/docs-lifecycle.mjs +1 -1
- package/lib/embed/inbox.mjs +2 -0
- package/lib/embed/scheduler.mjs +33 -5
- package/lib/evaluator-optimizer.mjs +2 -3
- package/lib/hooks/comment-lint.mjs +16 -0
- package/lib/hooks/mcp-audit.mjs +2 -1
- package/lib/hooks/proactive-activation.mjs +0 -14
- package/lib/hooks/rule-verifier.mjs +217 -0
- package/lib/hooks/session-optimize.mjs +2 -1
- package/lib/init-unified.mjs +55 -65
- package/lib/intake/classify.mjs +108 -24
- package/lib/intake/daemon.mjs +121 -0
- package/lib/intake/filesystem-queue.mjs +6 -1
- package/lib/intake/intake-config.mjs +2 -1
- package/lib/intake/prepare.mjs +0 -1
- package/lib/intake/session-prelude.mjs +7 -1
- package/lib/intake/traceability.mjs +90 -0
- package/lib/knowledge/research-store.mjs +2 -0
- package/lib/maintenance/cleanup.mjs +315 -0
- package/lib/mcp/memory-bridge.mjs +276 -0
- package/lib/mcp/server.mjs +2 -0
- package/lib/mcp/tools/workflow.mjs +25 -0
- package/lib/mcp-catalog.json +12 -8
- package/lib/mcp-platform-config.mjs +16 -8
- package/lib/migrations/index.mjs +106 -0
- package/lib/migrations/v1-baseline.mjs +33 -0
- package/lib/observation-store.mjs +9 -4
- package/lib/outcomes/record.mjs +2 -0
- package/lib/profiles/rebrand.mjs +46 -0
- package/lib/project-init-shared.mjs +12 -0
- package/lib/provider-capabilities.js +20 -7
- package/lib/providers/auth-manager.mjs +58 -17
- package/lib/reflect.mjs +49 -12
- package/lib/server/index.mjs +22 -28
- package/lib/session-store.mjs +6 -4
- package/lib/setup.mjs +14 -3
- package/lib/telemetry/client.mjs +5 -1
- package/lib/version.mjs +51 -0
- package/lib/worker/trace.mjs +5 -1
- package/package.json +4 -1
- package/personas/construct.md +3 -1
- package/rules/common/development-workflow.md +2 -1
- package/rules/common/no-fabrication.md +69 -0
- package/rules/common/review-before-change.md +58 -0
- package/scripts/sync-agents.mjs +45 -14
- package/templates/docs/construct_guide.md +6 -6
- package/templates/docs/skill-artifact.md +1 -1
package/bin/construct
CHANGED
|
@@ -292,7 +292,7 @@ async function cmdList() {
|
|
|
292
292
|
async function cmdDoctor() {
|
|
293
293
|
const args = process.argv.slice(3);
|
|
294
294
|
|
|
295
|
-
const DAEMON_SUBS = new Set(['status', 'watch', 'stop', 'logs', 'tick', 'report']);
|
|
295
|
+
const DAEMON_SUBS = new Set(['status', 'watch', 'stop', 'logs', 'tick', 'report', 'consistency']);
|
|
296
296
|
if (args.length > 0 && DAEMON_SUBS.has(args[0])) {
|
|
297
297
|
const { runCli } = await import('../lib/doctor/cli.mjs');
|
|
298
298
|
const code = await runCli(args);
|
|
@@ -1053,6 +1053,59 @@ async function cmdVersion() {
|
|
|
1053
1053
|
println(`construct v${pkg.version}`);
|
|
1054
1054
|
}
|
|
1055
1055
|
|
|
1056
|
+
async function cmdMigrate(args) {
|
|
1057
|
+
const dryRun = args.includes('--dry-run');
|
|
1058
|
+
const pathArg = args.find((a) => a.startsWith('--path='))?.split('=')[1];
|
|
1059
|
+
const fromArg = args.find((a) => a.startsWith('--from='))?.split('=')[1];
|
|
1060
|
+
const { runMigrations, planMigrations, CURRENT_SCHEMA_VERSION } = await import('../lib/migrations/index.mjs');
|
|
1061
|
+
|
|
1062
|
+
const candidates = pathArg
|
|
1063
|
+
? [pathArg]
|
|
1064
|
+
: [
|
|
1065
|
+
path.join(ROOT_DIR, 'agents', 'registry.json'),
|
|
1066
|
+
path.join(ROOT_DIR, 'agents', 'contracts.json'),
|
|
1067
|
+
path.join(ROOT_DIR, 'agents', 'role-manifests.json'),
|
|
1068
|
+
path.join(process.cwd(), '.cx', 'config.json'),
|
|
1069
|
+
];
|
|
1070
|
+
|
|
1071
|
+
let anyFailed = false;
|
|
1072
|
+
for (const artifactPath of candidates) {
|
|
1073
|
+
if (!fs.existsSync(artifactPath)) continue;
|
|
1074
|
+
let from;
|
|
1075
|
+
try {
|
|
1076
|
+
const data = JSON.parse(fs.readFileSync(artifactPath, 'utf8'));
|
|
1077
|
+
from = typeof data?.version === 'number' ? data.version : Number(fromArg) || 0;
|
|
1078
|
+
} catch {
|
|
1079
|
+
println(` ⚠ ${artifactPath}: parse error, skipping`);
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const plan = planMigrations(from, CURRENT_SCHEMA_VERSION);
|
|
1084
|
+
if (plan === null) {
|
|
1085
|
+
println(` ✗ ${artifactPath}: no migration path from v${from} to v${CURRENT_SCHEMA_VERSION}`);
|
|
1086
|
+
anyFailed = true;
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
if (plan.length === 0) {
|
|
1090
|
+
println(` ✓ ${artifactPath}: already at v${CURRENT_SCHEMA_VERSION}`);
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
println(` ${dryRun ? '·' : '→'} ${artifactPath}: ${plan.length} step(s) (v${from} → v${CURRENT_SCHEMA_VERSION})`);
|
|
1095
|
+
const result = await runMigrations({ artifactPath, fromVersion: from, dryRun });
|
|
1096
|
+
for (const step of result.applied) {
|
|
1097
|
+
println(` v${step.from}→v${step.to}: ${step.summary}`);
|
|
1098
|
+
}
|
|
1099
|
+
if (!result.ok) {
|
|
1100
|
+
println(` ✗ ${result.error}`);
|
|
1101
|
+
anyFailed = true;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
if (dryRun) println(`\n(dry-run — no files were written)`);
|
|
1106
|
+
if (anyFailed) process.exit(1);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1056
1109
|
async function cmdReview(args) {
|
|
1057
1110
|
fs.mkdirSync(path.join(HOME, '.cx', 'performance-reviews'), { recursive: true });
|
|
1058
1111
|
const sub = args[0];
|
|
@@ -1550,13 +1603,16 @@ function printActiveConfig() {
|
|
|
1550
1603
|
println(`Persisted at: ${getUserEnvPath(HOME)} (${DEPLOYMENT_MODE_ENV_KEY})`);
|
|
1551
1604
|
}
|
|
1552
1605
|
|
|
1553
|
-
function printIntakeHelp() {
|
|
1554
|
-
|
|
1606
|
+
async function printIntakeHelp() {
|
|
1607
|
+
const { getRebrand } = await import('../lib/profiles/rebrand.mjs');
|
|
1608
|
+
const { intakeQueueLabel, signalNoun } = getRebrand(process.cwd());
|
|
1609
|
+
println(`Construct intake — inspect and process the ${intakeQueueLabel}.`);
|
|
1555
1610
|
println('');
|
|
1556
1611
|
println('Usage:');
|
|
1557
|
-
println(
|
|
1612
|
+
println(` construct intake list List pending ${signalNoun}s (ID, type, stage, owner, action).`);
|
|
1558
1613
|
println(' construct intake show <id> Show a single packet: triage, source, excerpt, related docs.');
|
|
1559
|
-
println(' construct intake done <id> [--notes=…]
|
|
1614
|
+
println(' construct intake done <id> [--notes=…] [--output=<path>] [--no-output=<rejected|deferred|merged>]');
|
|
1615
|
+
println(' Mark a pending packet processed. --output stamps intake_id + confidence into the artifact frontmatter (provenance).');
|
|
1560
1616
|
println(' construct intake skip <id> [--reason=…] Mark a pending packet skipped (preserves audit trail).');
|
|
1561
1617
|
println(' construct intake reopen <id> Move a processed or skipped packet back to pending.');
|
|
1562
1618
|
println(' construct intake metrics Show pipeline volume, velocity, and processing stats.');
|
|
@@ -1580,11 +1636,12 @@ function parseKvFlags(args, keys) {
|
|
|
1580
1636
|
return out;
|
|
1581
1637
|
}
|
|
1582
1638
|
|
|
1583
|
-
function formatIntakeTable(rows) {
|
|
1639
|
+
function formatIntakeTable(rows, { intakeQueueLabel, signalNoun } = { intakeQueueLabel: 'Intake queue', signalNoun: 'signal' }) {
|
|
1584
1640
|
if (rows.length === 0) {
|
|
1585
|
-
println(
|
|
1641
|
+
println(`No pending ${signalNoun}s.`);
|
|
1586
1642
|
return;
|
|
1587
1643
|
}
|
|
1644
|
+
println(`${intakeQueueLabel} (${rows.length} pending):`);
|
|
1588
1645
|
const headers = ['ID', 'Type', 'Stage', 'Owner', 'Action'];
|
|
1589
1646
|
const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => String(r[i] || '').length)));
|
|
1590
1647
|
const fmt = (cells) => cells.map((c, i) => String(c || '').padEnd(widths[i])).join(' ');
|
|
@@ -1597,12 +1654,14 @@ async function cmdIntake(args) {
|
|
|
1597
1654
|
const sub = args[0];
|
|
1598
1655
|
|
|
1599
1656
|
if (!sub || sub === '--help' || sub === '-h') {
|
|
1600
|
-
printIntakeHelp();
|
|
1657
|
+
await printIntakeHelp();
|
|
1601
1658
|
return;
|
|
1602
1659
|
}
|
|
1603
1660
|
|
|
1604
1661
|
const { createIntakeQueue } = await import('../lib/intake/queue.mjs');
|
|
1662
|
+
const { getRebrand } = await import('../lib/profiles/rebrand.mjs');
|
|
1605
1663
|
const cwd = process.cwd();
|
|
1664
|
+
const rebrand = getRebrand(cwd);
|
|
1606
1665
|
const queue = createIntakeQueue(cwd, process.env);
|
|
1607
1666
|
|
|
1608
1667
|
if (sub === 'list') {
|
|
@@ -1611,7 +1670,7 @@ async function cmdIntake(args) {
|
|
|
1611
1670
|
const t = p.triage || {};
|
|
1612
1671
|
return [p.id, t.intakeType || 'unknown', t.rdStage || 'unknown', t.primaryOwner || '—', t.recommendedAction || '—'];
|
|
1613
1672
|
});
|
|
1614
|
-
formatIntakeTable(rows);
|
|
1673
|
+
formatIntakeTable(rows, rebrand);
|
|
1615
1674
|
return;
|
|
1616
1675
|
}
|
|
1617
1676
|
|
|
@@ -1619,8 +1678,9 @@ async function cmdIntake(args) {
|
|
|
1619
1678
|
if (sub === 'show') {
|
|
1620
1679
|
if (!id) { errorln('Usage: construct intake show <id>'); process.exit(1); }
|
|
1621
1680
|
const entry = queue.read(id);
|
|
1622
|
-
if (!entry) { errorln(`No
|
|
1681
|
+
if (!entry) { errorln(`No ${rebrand.signalNoun} with id: ${id}`); process.exit(1); }
|
|
1623
1682
|
const t = entry.triage || {};
|
|
1683
|
+
println(`${rebrand.signalNoun.charAt(0).toUpperCase() + rebrand.signalNoun.slice(1)} ${entry.id}`);
|
|
1624
1684
|
println(`ID: ${entry.id}`);
|
|
1625
1685
|
println(`Status: ${entry.status}`);
|
|
1626
1686
|
println(`Source: ${entry.intake?.sourcePath || '(none)'}`);
|
|
@@ -1712,11 +1772,59 @@ async function cmdIntake(args) {
|
|
|
1712
1772
|
}
|
|
1713
1773
|
|
|
1714
1774
|
if (sub === 'done') {
|
|
1715
|
-
if (!id) {
|
|
1716
|
-
|
|
1775
|
+
if (!id) {
|
|
1776
|
+
errorln('Usage: construct intake done <id> [--notes=…] [--output=<path>] [--no-output=<rejected|deferred|merged>]');
|
|
1777
|
+
process.exit(1);
|
|
1778
|
+
}
|
|
1779
|
+
const flags = parseKvFlags(args.slice(2), ['notes', 'output', 'no-output']);
|
|
1780
|
+
const outputPath = flags.output || null;
|
|
1781
|
+
const noOutputOutcome = flags['no-output'] || null;
|
|
1782
|
+
|
|
1783
|
+
if (outputPath && noOutputOutcome) {
|
|
1784
|
+
errorln('--output and --no-output are mutually exclusive');
|
|
1785
|
+
process.exit(1);
|
|
1786
|
+
}
|
|
1787
|
+
if (noOutputOutcome && !['rejected', 'deferred', 'merged'].includes(noOutputOutcome)) {
|
|
1788
|
+
errorln(`--no-output value must be one of: rejected, deferred, merged (got "${noOutputOutcome}")`);
|
|
1789
|
+
process.exit(1);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
let stamped = null;
|
|
1793
|
+
if (outputPath) {
|
|
1794
|
+
const packet = queue.read(id);
|
|
1795
|
+
if (!packet) {
|
|
1796
|
+
errorln(`No intake packet found: ${id}`);
|
|
1797
|
+
process.exit(1);
|
|
1798
|
+
}
|
|
1799
|
+
const triage = packet.triage || {};
|
|
1800
|
+
try {
|
|
1801
|
+
const { stampIntakeProvenance } = await import('../lib/intake/traceability.mjs');
|
|
1802
|
+
const path = (await import('node:path')).default;
|
|
1803
|
+
const absPath = path.isAbsolute(outputPath) ? outputPath : path.join(cwd, outputPath);
|
|
1804
|
+
stamped = stampIntakeProvenance(absPath, {
|
|
1805
|
+
intakeId: id,
|
|
1806
|
+
confidence: triage.confidence,
|
|
1807
|
+
rationale: triage.rationale,
|
|
1808
|
+
});
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
errorln(err.message || 'intake stamping failed');
|
|
1811
|
+
process.exit(1);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
const notes = flags.notes
|
|
1816
|
+
|| (noOutputOutcome ? `intake_outcome: ${noOutputOutcome}` : '')
|
|
1817
|
+
|| (outputPath ? `output: ${outputPath}` : '');
|
|
1818
|
+
|
|
1717
1819
|
try {
|
|
1718
|
-
const r = queue.markProcessed(id, { processedBy: 'construct-intake-cli', notes
|
|
1719
|
-
|
|
1820
|
+
const r = queue.markProcessed(id, { processedBy: 'construct-intake-cli', notes });
|
|
1821
|
+
if (stamped) {
|
|
1822
|
+
info(`Marked ${r.id} processed. Stamped ${outputPath} with intake_id=${id} (confidence=${stamped.intake_confidence ?? 'n/a'}).`);
|
|
1823
|
+
} else if (noOutputOutcome) {
|
|
1824
|
+
info(`Marked ${r.id} processed (intake_outcome: ${noOutputOutcome}).`);
|
|
1825
|
+
} else {
|
|
1826
|
+
info(`Marked ${r.id} processed.`);
|
|
1827
|
+
}
|
|
1720
1828
|
} catch (err) {
|
|
1721
1829
|
errorln(err.message || 'mark processed failed');
|
|
1722
1830
|
process.exit(1);
|
|
@@ -2741,27 +2849,55 @@ async function cmdSeedTraces(args) {
|
|
|
2741
2849
|
async function cmdCleanup() {
|
|
2742
2850
|
const force = restArgsCache.includes('--pressure-release');
|
|
2743
2851
|
const quiet = restArgsCache.includes('--quiet');
|
|
2744
|
-
const
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
const
|
|
2753
|
-
|
|
2852
|
+
const dryRun = restArgsCache.includes('--dry-run');
|
|
2853
|
+
const pressureOnly = restArgsCache.includes('--pressure-only');
|
|
2854
|
+
const diskOnly = restArgsCache.includes('--disk-only');
|
|
2855
|
+
|
|
2856
|
+
// Process pressure release: kill stale dev-agent processes.
|
|
2857
|
+
if (!diskOnly) {
|
|
2858
|
+
const report = runPressureRelease({ env: process.env, force });
|
|
2859
|
+
if (!quiet) {
|
|
2860
|
+
const swap = report.swapUsage
|
|
2861
|
+
? `${(report.swapUsage.usedBytes / (1024 ** 3)).toFixed(1)} GiB used`
|
|
2862
|
+
: 'swap unavailable';
|
|
2863
|
+
println(`Pressure guard: ${report.pressureTriggered ? 'triggered' : 'standby'} · ${swap}`);
|
|
2864
|
+
for (const entry of report.warnings ?? []) {
|
|
2865
|
+
const grace = entry.graceMinutes ? `; will terminate after ${entry.graceMinutes}m if still matched` : '';
|
|
2866
|
+
println(`Warning pid ${entry.pid} (${entry.reason})${grace}`);
|
|
2867
|
+
}
|
|
2868
|
+
if (report.killed.length) {
|
|
2869
|
+
for (const entry of report.killed) {
|
|
2870
|
+
println(`Stopped pid ${entry.pid} (${entry.reason})`);
|
|
2871
|
+
}
|
|
2872
|
+
} else {
|
|
2873
|
+
println('No stale dev-agent processes needed cleanup.');
|
|
2874
|
+
}
|
|
2754
2875
|
}
|
|
2755
2876
|
}
|
|
2756
2877
|
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2878
|
+
// Disk maintenance: rotate oversized logs, prune stale cache entries.
|
|
2879
|
+
if (!pressureOnly) {
|
|
2880
|
+
const { runFullCleanup, formatBytes } = await import('../lib/maintenance/cleanup.mjs');
|
|
2881
|
+
const summary = runFullCleanup({ homeDir: HOME, env: process.env, dryRun });
|
|
2882
|
+
if (!quiet) {
|
|
2883
|
+
const verb = dryRun ? 'Would free' : 'Freed';
|
|
2884
|
+
println(`Disk maintenance${dryRun ? ' (dry-run)' : ''}: ${verb} ${formatBytes(summary.freedBytes)} in ${summary.durationMs}ms`);
|
|
2885
|
+
const log = summary.embedLog;
|
|
2886
|
+
if (log?.truncated?.length) {
|
|
2887
|
+
for (const t of log.truncated) println(` truncated ${path.relative(HOME, t.path)} (was ${formatBytes(t.sizeWas)})`);
|
|
2888
|
+
}
|
|
2889
|
+
if (log?.removed?.length) {
|
|
2890
|
+
for (const r of log.removed) println(` removed ${path.relative(HOME, r.path)} (${formatBytes(r.size)})`);
|
|
2891
|
+
}
|
|
2892
|
+
if (summary.jsonlLogs?.truncated?.length) {
|
|
2893
|
+
for (const t of summary.jsonlLogs.truncated) println(` truncated ${path.relative(HOME, t.path)} (was ${formatBytes(t.sizeWas)} → ${formatBytes(t.sizeNow)})`);
|
|
2894
|
+
}
|
|
2895
|
+
if (summary.cacheDir?.removed?.length) {
|
|
2896
|
+
println(` pruned ${summary.cacheDir.removed.length} stale cache file(s)`);
|
|
2897
|
+
}
|
|
2898
|
+
for (const err of summary.errors ?? []) {
|
|
2899
|
+
println(` ! ${err.step}: ${err.error}`);
|
|
2900
|
+
}
|
|
2765
2901
|
}
|
|
2766
2902
|
}
|
|
2767
2903
|
}
|
|
@@ -2970,6 +3106,12 @@ async function cmdMemory(args) {
|
|
|
2970
3106
|
async function cmdLintComments(args) {
|
|
2971
3107
|
const fix = args.includes('--fix');
|
|
2972
3108
|
const staged = args.includes('--staged');
|
|
3109
|
+
// CLI invocation enforces block severity for artifact-prose lint (see
|
|
3110
|
+
// lib/comment-lint.mjs#checkArtifactBanned). Operators may opt back into
|
|
3111
|
+
// warn-only by exporting CONSTRUCT_ARTIFACT_LINT_MODE=warn explicitly.
|
|
3112
|
+
if (!process.env.CONSTRUCT_ARTIFACT_LINT_MODE) {
|
|
3113
|
+
process.env.CONSTRUCT_ARTIFACT_LINT_MODE = 'block';
|
|
3114
|
+
}
|
|
2973
3115
|
const { lintRepo: lint, lintFile, formatResults: fmt } = await import('../lib/comment-lint.mjs');
|
|
2974
3116
|
|
|
2975
3117
|
let results;
|
|
@@ -3053,6 +3195,18 @@ async function cmdLintAgents() {
|
|
|
3053
3195
|
process.exit(1);
|
|
3054
3196
|
}
|
|
3055
3197
|
|
|
3198
|
+
async function cmdLintContracts() {
|
|
3199
|
+
const { validateContractsFile } = await import('../lib/contracts/validate.mjs');
|
|
3200
|
+
const result = validateContractsFile({ repoRoot: ROOT_DIR });
|
|
3201
|
+
if (result.ok) {
|
|
3202
|
+
console.log('agents/contracts.json: 0 errors');
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
console.error(`agents/contracts.json: ${result.errors.length} error(s)`);
|
|
3206
|
+
for (const err of result.errors) console.error(` ${err}`);
|
|
3207
|
+
process.exit(1);
|
|
3208
|
+
}
|
|
3209
|
+
|
|
3056
3210
|
async function cmdBackup(args) {
|
|
3057
3211
|
const sub = args[0];
|
|
3058
3212
|
|
|
@@ -3426,6 +3580,7 @@ const handlers = new Map([
|
|
|
3426
3580
|
['evals', cmdEvals],
|
|
3427
3581
|
['cleanup', cmdCleanup],
|
|
3428
3582
|
['version', cmdVersion],
|
|
3583
|
+
['migrate', cmdMigrate],
|
|
3429
3584
|
['docs:update', cmdDocsUpdate],
|
|
3430
3585
|
['docs:check', cmdDocsCheck],
|
|
3431
3586
|
['docs:site', cmdDocsSite],
|
|
@@ -3440,6 +3595,7 @@ const handlers = new Map([
|
|
|
3440
3595
|
['provider', cmdProvider],
|
|
3441
3596
|
['lint:research', cmdLintResearch],
|
|
3442
3597
|
['lint:agents', cmdLintAgents],
|
|
3598
|
+
['lint:contracts', cmdLintContracts],
|
|
3443
3599
|
['audit', async (args) => {
|
|
3444
3600
|
const sub = args[0];
|
|
3445
3601
|
if (sub === 'skills') return runAuditSkillsCli(args.slice(1));
|
|
@@ -4331,6 +4487,27 @@ if (!handler) {
|
|
|
4331
4487
|
process.exit(1);
|
|
4332
4488
|
}
|
|
4333
4489
|
|
|
4490
|
+
// Self-maintenance on version change. Skipped for high-frequency hook calls
|
|
4491
|
+
// (the harness invokes `construct hook <name>` many times per session and
|
|
4492
|
+
// must stay fast). When the installed version differs from the stamp at
|
|
4493
|
+
// ~/.construct/.cleanup-stamp, we run a single bounded cleanup pass and
|
|
4494
|
+
// update the stamp. Subsequent invocations are a single file read.
|
|
4495
|
+
if (command !== 'hook') {
|
|
4496
|
+
try {
|
|
4497
|
+
const { maybeRunCleanupOnUpgrade, formatBytes } = await import('../lib/maintenance/cleanup.mjs');
|
|
4498
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf8'));
|
|
4499
|
+
const result = maybeRunCleanupOnUpgrade({ homeDir: HOME, env: process.env, currentVersion: pkg.version });
|
|
4500
|
+
if (result.ran && result.summary?.freedBytes > 1024 * 1024) {
|
|
4501
|
+
const previous = result.previousVersion ? ` from v${result.previousVersion}` : '';
|
|
4502
|
+
process.stderr.write(`[construct] Maintenance after upgrade${previous} to v${pkg.version}: freed ${formatBytes(result.summary.freedBytes)}.\n`);
|
|
4503
|
+
}
|
|
4504
|
+
} catch (err) {
|
|
4505
|
+
// Maintenance must never block the actual command. Surface the failure
|
|
4506
|
+
// but continue. The next invocation will retry.
|
|
4507
|
+
process.stderr.write(`[construct] startup maintenance skipped: ${err.message}\n`);
|
|
4508
|
+
}
|
|
4509
|
+
}
|
|
4510
|
+
|
|
4334
4511
|
// First-invocation resource probe — skipped for hooks, setup, uninstall,
|
|
4335
4512
|
// doctor, and once BOOTSTRAP_CHECKED=1 is cached in ~/.construct/config.env.
|
|
4336
4513
|
// On TTY: if anything's missing, prints the status table and offers to run
|
|
@@ -4,7 +4,7 @@ commands/build/feature.md (Build a feature) implement it end to end, tested and
|
|
|
4
4
|
Build a feature: implement it end to end, tested and ready to ship
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Build a feature: implement it end to end, tested and ready to ship
|
|
7
|
+
description: "Build a feature: implement it end to end, tested and ready to ship"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Build the following: $ARGUMENTS
|
package/commands/build/fix.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/build/fix.md (Fix something broken) reproduce, find the root cause, app
|
|
|
4
4
|
Fix something broken: reproduce, find the root cause, apply the smallest safe change
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Fix something broken: reproduce, find the root cause, apply the smallest safe change
|
|
7
|
+
description: "Fix something broken: reproduce, find the root cause, apply the smallest safe change"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct.
|
|
@@ -4,7 +4,7 @@ commands/design/access.md (Accessibility audit) WCAG 2.1 AA, keyboard, screen re
|
|
|
4
4
|
Accessibility audit: WCAG 2.1 AA, keyboard, screen readers, contrast, motion
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Accessibility audit: WCAG 2.1 AA, keyboard, screen readers, contrast, motion
|
|
7
|
+
description: "Accessibility audit: WCAG 2.1 AA, keyboard, screen readers, contrast, motion"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Audit: $ARGUMENTS
|
package/commands/design/flow.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/design/flow.md (Map the user flow) entry to success, friction points, j
|
|
|
4
4
|
Map the user flow: entry to success, friction points, jobs-to-be-done
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Map the user flow: entry to success, friction points, jobs-to-be-done
|
|
7
|
+
description: "Map the user flow: entry to success, friction points, jobs-to-be-done"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Map the user flow for: $ARGUMENTS
|
package/commands/design/ui.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/design/ui.md (Design or review UI) visual hierarchy, states, interactio
|
|
|
4
4
|
Design or review UI: visual hierarchy, states, interaction model, accessibility baseline
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Design or review UI: visual hierarchy, states, interaction model, accessibility baseline
|
|
7
|
+
description: "Design or review UI: visual hierarchy, states, interaction model, accessibility baseline"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Design or review the UI for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/measure/experiment.md (Design an experiment) hypothesis, test, evidence
|
|
|
4
4
|
Design an experiment: hypothesis, test, evidence threshold, go/no-go decision
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Design an experiment: hypothesis, test, evidence threshold, go/no-go decision
|
|
7
|
+
description: "Design an experiment: hypothesis, test, evidence threshold, go/no-go decision"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Design an experiment for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/measure/metrics.md (Define metrics) what to measure, baselines, success
|
|
|
4
4
|
Define metrics: what to measure, baselines, success thresholds, instrumentation needed
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Define metrics: what to measure, baselines, success thresholds, instrumentation needed
|
|
7
|
+
description: "Define metrics: what to measure, baselines, success thresholds, instrumentation needed"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Define metrics for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/measure/results.md (Analyze results) interpret data, separate signal fr
|
|
|
4
4
|
Analyze results: interpret data, separate signal from noise, produce a recommendation
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Analyze results: interpret data, separate signal from noise, produce a recommendation
|
|
7
|
+
description: "Analyze results: interpret data, separate signal from noise, produce a recommendation"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Analyze: $ARGUMENTS
|
package/commands/plan/api.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/plan/api.md (Design an API) contracts, endpoints, error model, data sch
|
|
|
4
4
|
Design an API: contracts, endpoints, error model, data schema
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Design an API: contracts, endpoints, error model, data schema
|
|
7
|
+
description: "Design an API: contracts, endpoints, error model, data schema"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Design the API for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/plan/challenge.md (Challenge a plan) stress-test assumptions, surface f
|
|
|
4
4
|
Challenge a plan: stress-test assumptions, surface failure modes and risks
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Challenge a plan: stress-test assumptions, surface failure modes and risks
|
|
7
|
+
description: "Challenge a plan: stress-test assumptions, surface failure modes and risks"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Challenge the following: $ARGUMENTS
|
package/commands/plan/decide.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/plan/decide.md (Record a decision) context, options considered, consequ
|
|
|
4
4
|
Record a decision: context, options considered, consequences, what it locks in
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Record a decision: context, options considered, consequences, what it locks in
|
|
7
|
+
description: "Record a decision: context, options considered, consequences, what it locks in"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Document the decision about: $ARGUMENTS
|
package/commands/plan/feature.md
CHANGED
|
@@ -5,7 +5,7 @@ Produces a structured spec using the canonical Construct plan format, saves it
|
|
|
5
5
|
to .cx/plans/ as a durable planning artifact linked to the active tracker slice.
|
|
6
6
|
-->
|
|
7
7
|
---
|
|
8
|
-
description: Plan a feature: produce a structured spec and link it to the active tracker-backed plan
|
|
8
|
+
description: "Plan a feature: produce a structured spec and link it to the active tracker-backed plan"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
You are Construct. Plan the following: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/plan/requirements.md (Define requirements) what needs to be true for th
|
|
|
4
4
|
Define requirements: what needs to be true for this to be done
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Define requirements: what needs to be true for this to be done
|
|
7
|
+
description: "Define requirements: what needs to be true for this to be done"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Define requirements for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/remember/context.md (Save project context) update .cx/context.md so the
|
|
|
4
4
|
Save project context: update .cx/context.md so the next session picks up where this one left off
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Save project context: update .cx/context.md so the next session picks up where this one left off
|
|
7
|
+
description: "Save project context: update .cx/context.md so the next session picks up where this one left off"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Save context for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/remember/handoff.md (Write a handoff) transfer context so the next sess
|
|
|
4
4
|
Write a handoff: transfer context so the next session or person can continue without loss
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Write a handoff: transfer context so the next session or person can continue without loss
|
|
7
|
+
description: "Write a handoff: transfer context so the next session or person can continue without loss"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Write a handoff for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/remember/runbook.md (Write a runbook) step-by-step procedure for a recu
|
|
|
4
4
|
Write a runbook: step-by-step procedure for a recurring task or incident
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Write a runbook: step-by-step procedure for a recurring task or incident
|
|
7
|
+
description: "Write a runbook: step-by-step procedure for a recurring task or incident"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Write a runbook for: $ARGUMENTS
|
package/commands/review/code.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/review/code.md (Code review) correctness, regressions, security, test c
|
|
|
4
4
|
Code review: correctness, regressions, security, test coverage
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Code review: correctness, regressions, security, test coverage
|
|
7
|
+
description: "Code review: correctness, regressions, security, test coverage"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Review: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/review/quality.md (Quality audit) complexity, naming, duplication, dead
|
|
|
4
4
|
Quality audit: complexity, naming, duplication, dead code, maintainability
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Quality audit: complexity, naming, duplication, dead code, maintainability
|
|
7
|
+
description: "Quality audit: complexity, naming, duplication, dead code, maintainability"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Audit quality for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/review/security.md (Security scan) secrets, auth, injection, data expos
|
|
|
4
4
|
Security scan: secrets, auth, injection, data exposure, dependency risk
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Security scan: secrets, auth, injection, data exposure, dependency risk
|
|
7
|
+
description: "Security scan: secrets, auth, injection, data exposure, dependency risk"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Scan: $ARGUMENTS
|
package/commands/ship/ready.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/ship/ready.md (Pre-release check) is this ready to ship?
|
|
|
4
4
|
Pre-release check: is this ready to ship?
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Pre-release check: is this ready to ship?
|
|
7
|
+
description: "Pre-release check: is this ready to ship?"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Check if the following is ready: $ARGUMENTS
|
package/commands/ship/release.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/ship/release.md (Release) plan rollout, changelog, rollback, and post-r
|
|
|
4
4
|
Release: plan rollout, changelog, rollback, and post-release verification
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Release: plan rollout, changelog, rollback, and post-release verification
|
|
7
|
+
description: "Release: plan rollout, changelog, rollback, and post-release verification"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Release the following: $ARGUMENTS
|
package/commands/ship/status.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/ship/status.md (Status) current project state, workflow, uncommitted ch
|
|
|
4
4
|
Status: current project state, workflow, uncommitted changes, recent activity
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Status: current project state, workflow, uncommitted changes, recent activity
|
|
7
|
+
description: "Status: current project state, workflow, uncommitted changes, recent activity"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Report status for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/understand/docs.md (Look up documentation) current behavior from primar
|
|
|
4
4
|
Look up documentation: current behavior from primary sources, not training memory
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Look up documentation: current behavior from primary sources, not training memory
|
|
7
|
+
description: "Look up documentation: current behavior from primary sources, not training memory"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Look up: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/understand/this.md (Explore and understand something) trace execution p
|
|
|
4
4
|
Explore and understand something: trace execution paths, map structure, gather evidence
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Explore and understand something: trace execution paths, map structure, gather evidence
|
|
7
|
+
description: "Explore and understand something: trace execution paths, map structure, gather evidence"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Explore and explain: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/understand/why.md (Investigate a failure) why is this broken, what is t
|
|
|
4
4
|
Investigate a failure: why is this broken, what is the root cause
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Investigate a failure: why is this broken, what is the root cause
|
|
7
|
+
description: "Investigate a failure: why is this broken, what is the root cause"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct. Investigate: $ARGUMENTS
|
package/commands/work/clean.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/work/clean.md (Remove AI-generated code smells) verbosity, hedging, dea
|
|
|
4
4
|
Remove AI-generated code smells: verbosity, hedging, dead comments, generic names
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Remove AI-generated code smells: verbosity, hedging, dead comments, generic names
|
|
7
|
+
description: "Remove AI-generated code smells: verbosity, hedging, dead comments, generic names"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are cx-reviewer identifying AI slop in: $ARGUMENTS
|
package/commands/work/drive.md
CHANGED
|
@@ -4,7 +4,7 @@ commands/work/drive.md (Full autonomous execution) explore, plan, implement, ver
|
|
|
4
4
|
Full autonomous execution: explore, plan, implement, verify, loop until done
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Full autonomous execution: explore, plan, implement, verify, loop until done
|
|
7
|
+
description: "Full autonomous execution: explore, plan, implement, verify, loop until done"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are Construct in drive mode. Execute $ARGUMENTS fully and autonomously without stopping for confirmation.
|
|
@@ -4,7 +4,7 @@ commands/work/optimize-prompts.md: Prompt optimization command.
|
|
|
4
4
|
Runs the closed-loop prompt optimization workflow using telemetry trace data.
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Closed-loop prompt optimization: read telemetry traces, diagnose failures, push improved version to staging
|
|
7
|
+
description: "Closed-loop prompt optimization: read telemetry traces, diagnose failures, push improved version to staging"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
You are cx-trace-reviewer running a prompt optimization cycle for: $ARGUMENTS
|
|
@@ -4,7 +4,7 @@ commands/work/parallel-review.md (Adversarial parallel review) 5 reviewers must
|
|
|
4
4
|
Adversarial parallel review: 5 reviewers must agree before output ships
|
|
5
5
|
-->
|
|
6
6
|
---
|
|
7
|
-
description: Adversarial parallel review: 5 reviewers must agree before output ships
|
|
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
|