@geraldmaron/construct 1.0.1 → 1.0.2
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/bin/construct +4 -4
- package/bin/construct-postinstall.mjs +1 -1
- package/lib/bootstrap/resources.mjs +2 -2
- package/lib/init-unified.mjs +1 -117
- package/lib/install/first-invocation.mjs +4 -4
- package/lib/intake/queue.mjs +1 -1
- package/lib/project-profile.mjs +1 -1
- package/lib/server/insights.mjs +1 -1
- package/lib/service-manager.mjs +2 -2
- package/lib/setup-prompts.mjs +2 -2
- package/lib/setup.mjs +3 -3
- package/lib/status.mjs +1 -1
- package/lib/storage/postgres-backup.mjs +1 -1
- package/lib/uninstall/uninstall.mjs +1 -1
- package/package.json +1 -1
- package/templates/docs/construct_guide.md +2 -2
- package/templates/homebrew/construct.rb +1 -1
package/.env.example
CHANGED
|
@@ -46,7 +46,7 @@ OPENROUTER_TITLE=Your App Name
|
|
|
46
46
|
# The installer can auto-configure local memory or let you skip/manualize it.
|
|
47
47
|
|
|
48
48
|
# ─── Hybrid Retrieval Backend ─────────────────────────────────────────────────
|
|
49
|
-
# `construct
|
|
49
|
+
# `construct setup --yes` writes managed defaults automatically. When Docker is
|
|
50
50
|
# available, it starts a localhost-only Postgres container on 127.0.0.1:54329.
|
|
51
51
|
#
|
|
52
52
|
# Shared Postgres store for team-ready state/querying
|
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 || path.basename(projectRoot);
|
|
145
|
+
const projectName = process.env.CX_PROJECT_NAME || require('path').basename(projectRoot);
|
|
146
146
|
println(`${COLORS.dim}Project:${COLORS.reset} ${projectName}`);
|
|
147
147
|
println('');
|
|
148
148
|
}
|
|
@@ -1789,8 +1789,8 @@ async function cmdIntakeConfig(args) {
|
|
|
1789
1789
|
async function cmdIntakeMetrics() {
|
|
1790
1790
|
try {
|
|
1791
1791
|
const { computeIntakeMetrics, pendingAge } = await import('../lib/embed/intake-metrics.mjs');
|
|
1792
|
-
const metrics = computeIntakeMetrics({ rootDir:
|
|
1793
|
-
const age = pendingAge({ rootDir:
|
|
1792
|
+
const metrics = computeIntakeMetrics({ rootDir: cwd });
|
|
1793
|
+
const age = pendingAge({ rootDir: cwd });
|
|
1794
1794
|
|
|
1795
1795
|
println(`${COLORS.bold}Intake pipeline metrics:${COLORS.reset}`);
|
|
1796
1796
|
println('');
|
|
@@ -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 setup`.
|
|
10
10
|
*
|
|
11
11
|
* The script is a no-op in three cases:
|
|
12
12
|
*
|
|
@@ -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 setup` 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 setup`
|
|
118
118
|
: '';
|
|
119
119
|
return ` ${status} ${probe.displayName}${v}${loc}${detail}${fallback}${install}`;
|
|
120
120
|
}
|
package/lib/init-unified.mjs
CHANGED
|
@@ -591,108 +591,6 @@ async function askDocumentationQuestions() {
|
|
|
591
591
|
return { lanes: selectedLanes, withArchitecture, withReadme };
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
// Intake collection — suggest directories to watch for context
|
|
595
|
-
function discoverProjectDirs(targetPath) {
|
|
596
|
-
const dirs = [];
|
|
597
|
-
let entries;
|
|
598
|
-
try { entries = fs.readdirSync(targetPath, { withFileTypes: true }); } catch { return dirs; }
|
|
599
|
-
|
|
600
|
-
for (const entry of entries) {
|
|
601
|
-
if (!entry.isDirectory()) continue;
|
|
602
|
-
const name = entry.name;
|
|
603
|
-
// Skip hidden dirs, node_modules, build artifacts
|
|
604
|
-
if (name.startsWith('.')) continue;
|
|
605
|
-
if (name === 'node_modules') continue;
|
|
606
|
-
if (name === 'dist' || name === 'build' || name === '.next' || name === '.out') continue;
|
|
607
|
-
dirs.push(name);
|
|
608
|
-
}
|
|
609
|
-
return dirs.sort();
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const INTAKE_DIR_PRESETS = [
|
|
613
|
-
{ value: 'src', label: 'src/', reason: 'Source code — most projects put code here' },
|
|
614
|
-
{ value: 'lib', label: 'lib/', reason: 'Library code' },
|
|
615
|
-
{ value: 'packages', label: 'packages/', reason: 'Monorepo packages' },
|
|
616
|
-
{ value: 'services', label: 'services/', reason: 'Microservices or backend services' },
|
|
617
|
-
{ value: 'apps', label: 'apps/', reason: 'Monorepo apps' },
|
|
618
|
-
{ value: 'docs', label: 'docs/', reason: 'Documentation source' },
|
|
619
|
-
{ value: 'tests', label: 'tests/', reason: 'Test files' },
|
|
620
|
-
{ value: 'spec', label: 'spec/', reason: 'Specifications' },
|
|
621
|
-
{ value: 'infra', label: 'infra/', reason: 'Infrastructure code (Terraform, etc.)' },
|
|
622
|
-
{ value: 'config', label: 'config/', reason: 'Configuration files' },
|
|
623
|
-
{ value: 'scripts', label: 'scripts/', reason: 'Build/ops scripts' },
|
|
624
|
-
{ value: 'tools', label: 'tools/', reason: 'Internal tooling' },
|
|
625
|
-
];
|
|
626
|
-
|
|
627
|
-
async function askIntakeCollection(targetPath, skipInteractive) {
|
|
628
|
-
if (skipInteractive) {
|
|
629
|
-
// Non-interactive: auto-detect and suggest based on what exists
|
|
630
|
-
const existingDirs = discoverProjectDirs(targetPath);
|
|
631
|
-
const selected = INTAKE_DIR_PRESETS
|
|
632
|
-
.filter(preset => existingDirs.includes(preset.value))
|
|
633
|
-
.map(p => p.value);
|
|
634
|
-
|
|
635
|
-
if (selected.length === 0) return null;
|
|
636
|
-
|
|
637
|
-
console.log('');
|
|
638
|
-
console.log('═══════════════════════════════════════════════════════════');
|
|
639
|
-
console.log(' INTAKE COLLECTION');
|
|
640
|
-
console.log('═══════════════════════════════════════════════════════════');
|
|
641
|
-
console.log('');
|
|
642
|
-
console.log(`Auto-detected ${selected.length} directory(ies) to watch for context:`);
|
|
643
|
-
for (const d of selected) console.log(` • ${d}/`);
|
|
644
|
-
console.log('');
|
|
645
|
-
console.log('The inbox watcher will scan these directories for new files');
|
|
646
|
-
console.log('and route them through R&D triage automatically.');
|
|
647
|
-
console.log('');
|
|
648
|
-
|
|
649
|
-
return { parentDirs: selected, maxDepth: 4 };
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Interactive mode
|
|
653
|
-
const existingDirs = discoverProjectDirs(targetPath);
|
|
654
|
-
const presetOptions = INTAKE_DIR_PRESETS.map(p => ({
|
|
655
|
-
label: p.label,
|
|
656
|
-
value: p.value,
|
|
657
|
-
checked: existingDirs.includes(p.value),
|
|
658
|
-
description: p.reason,
|
|
659
|
-
}));
|
|
660
|
-
|
|
661
|
-
console.log('');
|
|
662
|
-
console.log('═══════════════════════════════════════════════════════════');
|
|
663
|
-
console.log(' INTAKE COLLECTION');
|
|
664
|
-
console.log('═══════════════════════════════════════════════════════════');
|
|
665
|
-
console.log('');
|
|
666
|
-
console.log('Intake watches directories for new files and routes them');
|
|
667
|
-
console.log('through R&D triage. Select which directories to watch.');
|
|
668
|
-
console.log('(directories already found in your project are pre-checked)');
|
|
669
|
-
console.log('');
|
|
670
|
-
|
|
671
|
-
const selected = await multiSelect({
|
|
672
|
-
title: 'Directories to Watch',
|
|
673
|
-
instructions: 'Press Enter to confirm your selection',
|
|
674
|
-
options: presetOptions,
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
if (selected.length === 0) {
|
|
678
|
-
console.log('');
|
|
679
|
-
console.log('No directories selected. The inbox watcher will only scan');
|
|
680
|
-
console.log('.cx/inbox/ and docs/intake/ (built-in zones).');
|
|
681
|
-
console.log('');
|
|
682
|
-
console.log('You can always add more later with:');
|
|
683
|
-
console.log(' construct intake config set --add-dir=src');
|
|
684
|
-
console.log('');
|
|
685
|
-
return null;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
console.log('');
|
|
689
|
-
console.log(`Watching ${selected.length} directory(ies):`);
|
|
690
|
-
for (const d of selected) console.log(` • ${d}/`);
|
|
691
|
-
console.log('');
|
|
692
|
-
|
|
693
|
-
return { parentDirs: selected, maxDepth: 4 };
|
|
694
|
-
}
|
|
695
|
-
|
|
696
594
|
function buildProjectReadme(projectName) {
|
|
697
595
|
return `# ${projectName}
|
|
698
596
|
|
|
@@ -926,20 +824,6 @@ async function main() {
|
|
|
926
824
|
console.log('');
|
|
927
825
|
}
|
|
928
826
|
|
|
929
|
-
// Intake collection — ask which directories to watch for context
|
|
930
|
-
console.log('[TRACE init:intake-ask]');
|
|
931
|
-
|
|
932
|
-
const intakeConfig = await askIntakeCollection(target, skipInteractive);
|
|
933
|
-
if (intakeConfig) {
|
|
934
|
-
const { saveIntakeConfig } = await import('./intake/intake-config.mjs');
|
|
935
|
-
try {
|
|
936
|
-
saveIntakeConfig(target, intakeConfig);
|
|
937
|
-
created.push('.cx/intake-config.json');
|
|
938
|
-
} catch (err) {
|
|
939
|
-
console.warn(`⚠️ Could not write intake config: ${err.message}`);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
827
|
// Ask about documentation system
|
|
944
828
|
console.log('[TRACE init:docs-ask]');
|
|
945
829
|
|
|
@@ -1011,7 +895,7 @@ async function main() {
|
|
|
1011
895
|
// can compare new intake against established PRDs/RFCs/ADRs from day
|
|
1012
896
|
// one. Best-effort: skipped silently when Postgres + embedding model
|
|
1013
897
|
// aren't ready yet (DATABASE_URL unset, no ONNX cache). User can re-run
|
|
1014
|
-
// `construct ingest` or `construct
|
|
898
|
+
// `construct ingest` or `construct setup` later to seed manually.
|
|
1015
899
|
|
|
1016
900
|
try {
|
|
1017
901
|
const { syncFileStateToSql } = await import('./storage/sync.mjs');
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 2. Otherwise, run `probeAll()` from lib/bootstrap/resources.mjs.
|
|
11
11
|
* 3. If everything's healthy → silent, set BOOTSTRAP_CHECKED=1.
|
|
12
12
|
* 4. If anything's missing → print the status table to stderr; on TTY,
|
|
13
|
-
* prompt to run `construct
|
|
13
|
+
* prompt to run `construct setup` now. Always set BOOTSTRAP_CHECKED=1
|
|
14
14
|
* so we don't re-prompt on subsequent commands.
|
|
15
15
|
*
|
|
16
16
|
* Skipped entirely for hook invocations (would corrupt hook output), for
|
|
@@ -71,14 +71,14 @@ export async function maybeFirstInvocationProbe({
|
|
|
71
71
|
for (const probe of probes) stdout.write(formatProbeFn(probe) + '\n');
|
|
72
72
|
|
|
73
73
|
if (missingRequired.length > 0) {
|
|
74
|
-
stdout.write(`\n[construct] ${missingRequired.length} required resource(s) missing. Run \`construct
|
|
74
|
+
stdout.write(`\n[construct] ${missingRequired.length} required resource(s) missing. Run \`construct setup\` to install.\n\n`);
|
|
75
75
|
} else {
|
|
76
|
-
stdout.write(`\n[construct] ${missingOptional.length} optional resource(s) missing. Run \`construct
|
|
76
|
+
stdout.write(`\n[construct] ${missingOptional.length} optional resource(s) missing. Run \`construct setup\` to install (Postgres, telemetry, embedding model), or continue in degraded mode.\n\n`);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (stdin.isTTY && stdout.isTTY !== false) {
|
|
80
80
|
const wants = await promptYesNo({
|
|
81
|
-
question: 'Run `construct
|
|
81
|
+
question: 'Run `construct setup` now?',
|
|
82
82
|
defaultYes: missingRequired.length > 0,
|
|
83
83
|
readlineModule,
|
|
84
84
|
stdin,
|
package/lib/intake/queue.mjs
CHANGED
|
@@ -73,7 +73,7 @@ export function createIntakeQueue(rootDir, env = process.env, opts = {}) {
|
|
|
73
73
|
if (backend === 'postgres') {
|
|
74
74
|
const sql = opts.sql ?? createSqlClient(env);
|
|
75
75
|
if (!sql) {
|
|
76
|
-
throw new Error('Postgres intake queue requires a configured DATABASE_URL (or sql client). Run `construct
|
|
76
|
+
throw new Error('Postgres intake queue requires a configured DATABASE_URL (or sql client). Run `construct setup` or set CONSTRUCT_INTAKE_QUEUE_BACKEND=filesystem.');
|
|
77
77
|
}
|
|
78
78
|
const project = opts.project ?? resolveProject(rootDir, env);
|
|
79
79
|
const tenantId = opts.tenantId ?? env?.[INTAKE_TENANT_ENV_KEY] ?? null;
|
package/lib/project-profile.mjs
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* The profile is written to `<cwd>/.cx/project-profile.json` and consumed by:
|
|
10
10
|
* - `construct skills scope` — reports relevant vs irrelevant skills
|
|
11
|
-
* - `construct
|
|
11
|
+
* - `construct setup` — runs detection on bootstrap
|
|
12
12
|
* - future per-host filter generators (.claude/settings.json filter,
|
|
13
13
|
* .opencode/construct-skills.json sidecars, etc.)
|
|
14
14
|
*/
|
package/lib/server/insights.mjs
CHANGED
|
@@ -42,7 +42,7 @@ async function fetchTelemetrySummary(env, { limit = 100 } = {}) {
|
|
|
42
42
|
});
|
|
43
43
|
clearTimeout(timer);
|
|
44
44
|
if (res.status === 401 || res.status === 403) {
|
|
45
|
-
return { state: 'misconfigured', message: `Telemetry credentials rejected (HTTP ${res.status}). Run construct
|
|
45
|
+
return { state: 'misconfigured', message: `Telemetry credentials rejected (HTTP ${res.status}). Run construct setup.` };
|
|
46
46
|
}
|
|
47
47
|
if (!res.ok) return { state: 'unreachable', message: `Telemetry HTTP ${res.status}` };
|
|
48
48
|
const json = await res.json();
|
package/lib/service-manager.mjs
CHANGED
|
@@ -379,7 +379,7 @@ function startConstructPostgres({ rootDir, homeDir = os.homedir(), spawnSyncFn =
|
|
|
379
379
|
if (!composeRunner) return { status: 'unavailable', note: 'Docker not available' };
|
|
380
380
|
|
|
381
381
|
const composeFile = constructPgComposePath(rootDir);
|
|
382
|
-
if (!fs.existsSync(composeFile)) return { status: 'unavailable', note: 'Postgres compose file not found — run construct
|
|
382
|
+
if (!fs.existsSync(composeFile)) return { status: 'unavailable', note: 'Postgres compose file not found — run construct setup first' };
|
|
383
383
|
|
|
384
384
|
const args = [...composeRunner.argsPrefix, '-p', 'construct-postgres', '-f', composeFile, 'up', '-d'];
|
|
385
385
|
const r = spawnSyncFn(composeRunner.command, args, { stdio: 'pipe', encoding: 'utf8' });
|
|
@@ -563,7 +563,7 @@ export async function startServices({
|
|
|
563
563
|
results.push({ name: 'Memory (cm)', url: `http://127.0.0.1:${ports.memory}`, status: 'started' });
|
|
564
564
|
}
|
|
565
565
|
} else {
|
|
566
|
-
results.push({ name: 'Memory (cm)', status: 'unavailable', note: 'cm not installed — run: construct
|
|
566
|
+
results.push({ name: 'Memory (cm)', status: 'unavailable', note: 'cm not installed — run: construct setup or brew install dicklesworthstone/tap/cm' });
|
|
567
567
|
}
|
|
568
568
|
|
|
569
569
|
// OpenCode (optional)
|
package/lib/setup-prompts.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/setup-prompts.mjs — yes/no consent helper for `construct
|
|
2
|
+
* lib/setup-prompts.mjs — yes/no consent helper for `construct setup`.
|
|
3
3
|
*
|
|
4
4
|
* Centralises the "should we install this service?" decision so Postgres,
|
|
5
5
|
* telemetry, and any future service share the same prompt + consent-caching
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* 5. interactive → prompt [Y/n], cache the answer
|
|
15
15
|
*
|
|
16
16
|
* Consent is stored as BOOTSTRAP_<NAME>=yes|no in ~/.construct/config.env
|
|
17
|
-
* so subsequent runs of `construct
|
|
17
|
+
* so subsequent runs of `construct setup` don't re-prompt for the same
|
|
18
18
|
* services. Pass `force = true` to skip the cache and re-prompt.
|
|
19
19
|
*/
|
|
20
20
|
|
package/lib/setup.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Guides users through provider selection, API key entry, and model tier
|
|
6
6
|
* assignment. Writes the resulting config to ~/.cx/env and optionally to
|
|
7
|
-
* project-level .env. Invoked by `construct
|
|
7
|
+
* project-level .env. Invoked by `construct setup` and on first init.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import fs from 'node:fs';
|
|
@@ -43,7 +43,7 @@ function printHelp() {
|
|
|
43
43
|
console.log(`Construct setup
|
|
44
44
|
|
|
45
45
|
Usage:
|
|
46
|
-
construct
|
|
46
|
+
construct setup [--yes] [--no-docker]
|
|
47
47
|
|
|
48
48
|
What it does:
|
|
49
49
|
- creates ~/.construct/config.env
|
|
@@ -705,7 +705,7 @@ export async function runSetup({ rootDir = ROOT_DIR, args = [], homeDir = HOME }
|
|
|
705
705
|
console.log(` Trace backend: ${managedValues.CONSTRUCT_TRACE_BACKEND}${managedValues.CONSTRUCT_TELEMETRY_URL ? ` (${managedValues.CONSTRUCT_TELEMETRY_URL})` : ''}`);
|
|
706
706
|
console.log(` Pressure guard: swap ${managedValues.CONSTRUCT_PRESSURE_GUARD_SWAP_GB} GiB, opencode max ${managedValues.CONSTRUCT_PRESSURE_GUARD_MAX_OPENCODE}`);
|
|
707
707
|
console.log('\nFor unattended setup, including local Postgres when Docker is running:');
|
|
708
|
-
console.log(' construct
|
|
708
|
+
console.log(' construct setup --yes');
|
|
709
709
|
}
|
|
710
710
|
|
|
711
711
|
runConstruct(['sync']);
|
package/lib/status.mjs
CHANGED
|
@@ -330,7 +330,7 @@ async function fetchTelemetryStatus(env, { timeout = 2500 } = {}) {
|
|
|
330
330
|
try {
|
|
331
331
|
const res = await fetch(`${baseUrl}/api/public/traces?limit=25`, { headers, signal: controller.signal });
|
|
332
332
|
if (res.status === 401 || res.status === 403) {
|
|
333
|
-
return { status: 'credentials-invalid', summary: `Telemetry credentials rejected (HTTP ${res.status}) — run: construct
|
|
333
|
+
return { status: 'credentials-invalid', summary: `Telemetry credentials rejected (HTTP ${res.status}) — run: construct setup` };
|
|
334
334
|
}
|
|
335
335
|
if (!res.ok) return { status: 'degraded', summary: `Telemetry HTTP ${res.status}` };
|
|
336
336
|
const json = await res.json().catch(() => ({}));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Mirrors the telemetry backup pattern in service-manager.mjs. Entry points:
|
|
6
6
|
* - construct down → stashConstructDb (dump before container stop)
|
|
7
|
-
* - construct
|
|
7
|
+
* - construct setup → restoreConstructDb (reload after fresh container start)
|
|
8
8
|
*
|
|
9
9
|
* Dumps are stored in ~/.construct/backups/postgres/ as pg_dump custom-format
|
|
10
10
|
* files. The N most recent are kept; older ones are pruned automatically.
|
|
@@ -312,7 +312,7 @@ function printFollowups() {
|
|
|
312
312
|
console.log('Follow-ups (run by hand if you want):');
|
|
313
313
|
console.log(' npm uninstall @geraldmaron/construct # drop the package itself');
|
|
314
314
|
console.log(' docker rmi pgvector/pgvector:pg16 # remove the cached pgvector image');
|
|
315
|
-
console.log(' brew uninstall cm cass # if installed by `construct
|
|
315
|
+
console.log(' brew uninstall cm cass # if installed by `construct setup`');
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
function rel(base, target) {
|
package/package.json
CHANGED
|
@@ -80,7 +80,7 @@ construct doctor # verify everything's healthy
|
|
|
80
80
|
|
|
81
81
|
## Local services
|
|
82
82
|
|
|
83
|
-
If you ran `construct
|
|
83
|
+
If you ran `construct setup` and have Docker, you have three things running locally on Construct's reserved port block (`54329-54339`, chosen to avoid colliding with Next.js, Postgres, Redis, etc.):
|
|
84
84
|
|
|
85
85
|
**Telemetry backend** (LLM observability — see your traces, costs, and quality scores)
|
|
86
86
|
|
|
@@ -105,7 +105,7 @@ All ports bind to `127.0.0.1` only; nothing is reachable from other machines on
|
|
|
105
105
|
|
|
106
106
|
| Command | What it does |
|
|
107
107
|
|---|---|
|
|
108
|
-
| `construct
|
|
108
|
+
| `construct setup` | One-time per-machine: spins up local services, writes config |
|
|
109
109
|
| `construct config [mode <m>]` | Show active deployment mode (solo / team / enterprise) or set a new one |
|
|
110
110
|
| `construct doctor` | Health check across config, services, agents, hooks |
|
|
111
111
|
| `construct sync` | Regenerate platform adapters (Claude Code, OpenCode, Codex, Cursor) |
|
|
@@ -49,7 +49,7 @@ class Construct < Formula
|
|
|
49
49
|
def caveats
|
|
50
50
|
<<~EOS
|
|
51
51
|
To finish setup on this machine, run:
|
|
52
|
-
construct
|
|
52
|
+
construct setup
|
|
53
53
|
|
|
54
54
|
Construct uses a local Postgres container (via Docker) for hybrid
|
|
55
55
|
retrieval. If Docker is not installed, Construct falls back to a JSON
|