@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 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 init --yes` writes managed defaults automatically. When Docker is
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('../lib/config/project-config.mjs');
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: process.cwd() });
1793
- const age = pendingAge({ rootDir: process.cwd() });
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 init`.
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 init` walks every resource, asks consent, installs
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 init`
117
+ ? `\n installable via construct setup`
118
118
  : '';
119
119
  return ` ${status} ${probe.displayName}${v}${loc}${detail}${fallback}${install}`;
120
120
  }
@@ -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 init` later to seed manually.
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 init` now. Always set BOOTSTRAP_CHECKED=1
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 init\` to install.\n\n`);
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 init\` to install (Postgres, telemetry, embedding model), or continue in degraded mode.\n\n`);
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 init` now?',
81
+ question: 'Run `construct setup` now?',
82
82
  defaultYes: missingRequired.length > 0,
83
83
  readlineModule,
84
84
  stdin,
@@ -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 init` or set CONSTRUCT_INTAKE_QUEUE_BACKEND=filesystem.');
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;
@@ -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 init` — runs detection on bootstrap
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
  */
@@ -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 init.` };
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();
@@ -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 init first' };
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 init or brew install dicklesworthstone/tap/cm' });
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)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * lib/setup-prompts.mjs — yes/no consent helper for `construct init`.
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 init` don't re-prompt for the same
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 init` and on first init.
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 init [--yes] [--no-docker]
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 init --yes');
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 init` };
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 init → restoreConstructDb (reload after fresh container start)
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 init`');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geraldmaron/construct",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Construct — agent orchestration layer for OpenCode, Claude Code, and other coding surfaces",
6
6
  "bin": {
@@ -80,7 +80,7 @@ construct doctor # verify everything's healthy
80
80
 
81
81
  ## Local services
82
82
 
83
- If you ran `construct init` 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.):
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 init` | One-time per-machine: spins up local services, writes config |
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 init
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