@construct-space/cli 1.9.2 → 1.9.3

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/dist/index.js CHANGED
@@ -2965,7 +2965,7 @@ function load2() {
2965
2965
  const fromProfile = loadFromActiveProfile();
2966
2966
  if (fromProfile)
2967
2967
  return fromProfile;
2968
- throw new Error("not logged in \u2014 run 'construct login' first");
2968
+ throw new Error("not logged in -- run 'construct login' first");
2969
2969
  }
2970
2970
  function loadFromActiveProfile() {
2971
2971
  try {
@@ -3124,7 +3124,7 @@ async function orgStatus() {
3124
3124
  process.exit(1);
3125
3125
  }
3126
3126
  if (s.scope === "org" && s.org) {
3127
- console.log(source_default.cyan("Scope: ") + source_default.bold(`org \u2014 ${s.org.name || s.org.slug || s.org.id}`));
3127
+ console.log(source_default.cyan("Scope: ") + source_default.bold(`org -- ${s.org.name || s.org.slug || s.org.id}`));
3128
3128
  if (s.org.slug)
3129
3129
  console.log(source_default.dim(` slug: ${s.org.slug}`));
3130
3130
  if (s.org.id)
@@ -3148,13 +3148,13 @@ async function orgStatus() {
3148
3148
  const expected = s.scope === "org" ? "org" : "user";
3149
3149
  const matches = kind === expected;
3150
3150
  console.log(` ${source_default.bold(creds.publisherName || "(unnamed)")} ${source_default.dim(`[${kind}]`)}`);
3151
- console.log(source_default.dim(` key: ${creds.publisherKey.slice(0, 14)}\u2026`));
3151
+ console.log(source_default.dim(` key: ${creds.publisherKey.slice(0, 14)}...`));
3152
3152
  if (!matches) {
3153
3153
  console.log(source_default.yellow(` \u26A0 publisher kind (${kind}) doesn't match active scope (${expected}).`));
3154
3154
  console.log(source_default.dim(" Restart the desktop app or run 'construct login' to re-sync."));
3155
3155
  }
3156
3156
  } else {
3157
- console.log(source_default.dim(" (none \u2014 enroll as a developer to get a publisher key)"));
3157
+ console.log(source_default.dim(" (none -- enroll as a developer to get a publisher key)"));
3158
3158
  }
3159
3159
  if (s.developer) {
3160
3160
  console.log();
@@ -3255,7 +3255,7 @@ async function spacesList(opts = {}) {
3255
3255
  const rows = spaces.map((s) => ({
3256
3256
  id: s.id,
3257
3257
  version: s.latest_version || "0.0.0",
3258
- bundle: s.bundle_id || source_default.dim("\u2014"),
3258
+ bundle: s.bundle_id || source_default.dim("--"),
3259
3259
  distribution: colorizeDistribution(s.distribution),
3260
3260
  installs: String(s.install_count)
3261
3261
  }));
@@ -3342,7 +3342,7 @@ async function bundlesList(opts = {}) {
3342
3342
  }
3343
3343
  console.log(source_default.bold(`Bundles for org ${orgId}:`));
3344
3344
  for (const b of bundles) {
3345
- console.log(` ${source_default.cyan(b.id)} \u2014 ${b.name}`);
3345
+ console.log(` ${source_default.cyan(b.id)} -- ${b.name}`);
3346
3346
  }
3347
3347
  } catch (err) {
3348
3348
  fail(err);
@@ -3365,7 +3365,7 @@ async function bundleCreate(id, name, opts = {}) {
3365
3365
  console.log(JSON.stringify(resp, null, 2));
3366
3366
  return;
3367
3367
  }
3368
- console.log(source_default.green(`\u2713 Bundle created: ${source_default.cyan(resp.id)} \u2014 ${resp.name}`));
3368
+ console.log(source_default.green(`\u2713 Bundle created: ${source_default.cyan(resp.id)} -- ${resp.name}`));
3369
3369
  console.log(source_default.dim(` Next: add bundle_id: "${resp.id}" to your data.manifest.json and publish.`));
3370
3370
  } catch (err) {
3371
3371
  fail(err);
@@ -3572,7 +3572,7 @@ async function allowlistRemove(spaceId, targetOrgId, opts = {}) {
3572
3572
  return;
3573
3573
  }
3574
3574
  console.log(source_default.green(`\u2713 Removed ${source_default.cyan(targetOrgId)} from ${spaceId} allowlist`));
3575
- console.log(source_default.dim(" Existing installs by this org are preserved \u2014 uninstall separately if needed."));
3575
+ console.log(source_default.dim(" Existing installs by this org are preserved -- uninstall separately if needed."));
3576
3576
  } catch (err) {
3577
3577
  fail3(err);
3578
3578
  }
@@ -8310,7 +8310,7 @@ function generate(root, m) {
8310
8310
  const hasActions = existsSync4(actionsPath);
8311
8311
  console.log(`[entry] root=${root} actionsPath=${actionsPath} hasActions=${hasActions}`);
8312
8312
  const lines = [
8313
- "// Auto-generated entry \u2014 do not edit manually",
8313
+ "// Auto-generated entry -- do not edit manually",
8314
8314
  "// Generated from space.manifest.json"
8315
8315
  ];
8316
8316
  if (existsSync4(join4(root, "src", "style.css"))) {
@@ -10270,7 +10270,7 @@ async function dev() {
10270
10270
  }
10271
10271
  const m = read(root);
10272
10272
  const rt = detect();
10273
- console.log(source_default.blue(`Dev mode \u2014 ${m.id} (${rt.name} ${rt.version})`));
10273
+ console.log(source_default.blue(`Dev mode -- ${m.id} (${rt.name} ${rt.version})`));
10274
10274
  ensureDeps(root, rt);
10275
10275
  writeEntry(root, m);
10276
10276
  const vite = watchCmd(root, rt);
@@ -10293,10 +10293,10 @@ async function dev() {
10293
10293
  entryWatcher.on("all", (_, changedPath) => {
10294
10294
  regenerateEntry();
10295
10295
  if (changedPath.endsWith(MANIFEST_FILE)) {
10296
- console.log(source_default.blue("Manifest changed \u2014 entry regenerated"));
10296
+ console.log(source_default.blue("Manifest changed -- entry regenerated"));
10297
10297
  return;
10298
10298
  }
10299
- console.log(source_default.blue("Actions changed \u2014 entry regenerated"));
10299
+ console.log(source_default.blue("Actions changed -- entry regenerated"));
10300
10300
  });
10301
10301
  const distDir = join9(root, "dist");
10302
10302
  const bundleFile = join9(distDir, `space-${m.id}.iife.js`);
@@ -10317,7 +10317,7 @@ async function dev() {
10317
10317
  hostApiVersion: "0.5.0",
10318
10318
  builtAt: new Date().toISOString()
10319
10319
  });
10320
- console.log(source_default.green(`Built \u2192 dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
10320
+ console.log(source_default.green(`Built -> dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
10321
10321
  });
10322
10322
  console.log(source_default.green("Watching for changes... (Ctrl+C to stop)"));
10323
10323
  console.log(source_default.dim("Use the Preview button in Construct to open the Space Runner"));
@@ -10371,7 +10371,7 @@ function install() {
10371
10371
  mkdirSync3(installDir, { recursive: true });
10372
10372
  cpSync2(distDir, installDir, { recursive: true, verbatimSymlinks: true, force: true });
10373
10373
  ensureBinExecutable(installDir);
10374
- console.log(source_default.green(`Installed ${m.name} \u2192 ${installDir}`));
10374
+ console.log(source_default.green(`Installed ${m.name} -> ${installDir}`));
10375
10375
  console.log(source_default.dim(" Restart Construct to load the updated space."));
10376
10376
  }
10377
10377
 
@@ -10489,7 +10489,7 @@ async function uploadSource(portalURL, identityToken, publisherKey, tarballPath,
10489
10489
  });
10490
10490
  const result = await resp.json();
10491
10491
  if (resp.status === 401) {
10492
- throw new Error("authentication failed \u2014 run 'construct login' to re-authenticate");
10492
+ throw new Error("authentication failed -- run 'construct login' to re-authenticate");
10493
10493
  }
10494
10494
  if (resp.status === 403) {
10495
10495
  let msg = result.error || "You are not the owner of this space";
@@ -10644,7 +10644,7 @@ async function publish(options) {
10644
10644
  console.log(source_default.yellow("No publisher key in active profile."));
10645
10645
  console.log(source_default.dim(" Spaces will be attributed to your personal user identity."));
10646
10646
  console.log(source_default.dim(" To publish as an org: enroll the org from the desktop app"));
10647
- console.log(source_default.dim(" (Org Settings \u2192 Developer), then re-run this command."));
10647
+ console.log(source_default.dim(" (Org Settings -> Developer), then re-run this command."));
10648
10648
  console.log();
10649
10649
  }
10650
10650
  if (wantUserScope && creds.publisherKind === "org" && !options?.apiKey) {
@@ -10659,7 +10659,7 @@ async function publish(options) {
10659
10659
  console.error(source_default.red("--private requires an org publisher key."));
10660
10660
  console.error(source_default.dim(" Personal publishes are always public."));
10661
10661
  console.error(source_default.dim(" Pass --scope=org to publish as your active org, or enrol an org"));
10662
- console.error(source_default.dim(" from the desktop app (Org Settings \u2192 Developer) first."));
10662
+ console.error(source_default.dim(" from the desktop app (Org Settings -> Developer) first."));
10663
10663
  process.exit(1);
10664
10664
  }
10665
10665
  console.log();
@@ -10706,7 +10706,7 @@ async function publish(options) {
10706
10706
  const explicitKey = options?.apiKey || process.env.CONSTRUCT_PUBLISHER_KEY;
10707
10707
  let initialKey = explicitKey || creds.publisherKey;
10708
10708
  if (wantOrgScope && !explicitKey && !initialKey) {
10709
- uploadSpinner.text = "Fetching org publisher key\u2026";
10709
+ uploadSpinner.text = "Fetching org publisher key...";
10710
10710
  const orgKey = await fetchOrgPublisherKey(creds.portal, creds.token);
10711
10711
  if (!orgKey) {
10712
10712
  uploadSpinner.fail("No org publisher available for --scope=org");
@@ -10722,11 +10722,11 @@ async function publish(options) {
10722
10722
  result = await uploadSource(creds.portal, creds.token, initialKey, tarballPath, m, { private: wantPrivate, public: wantPublic });
10723
10723
  } catch (e) {
10724
10724
  if (e?.ownerKind === "org" && !creds.publisherKey && !wantUserScope) {
10725
- uploadSpinner.text = "Fetching org publisher key\u2026";
10725
+ uploadSpinner.text = "Fetching org publisher key...";
10726
10726
  const orgKey = await fetchOrgPublisherKey(creds.portal, creds.token);
10727
10727
  if (!orgKey)
10728
10728
  throw e;
10729
- uploadSpinner.text = "Uploading & building (as org)\u2026";
10729
+ uploadSpinner.text = "Uploading & building (as org)...";
10730
10730
  result = await uploadSource(creds.portal, creds.token, orgKey, tarballPath, m, { private: wantPrivate, public: wantPublic });
10731
10731
  } else {
10732
10732
  throw e;
@@ -10738,7 +10738,7 @@ async function publish(options) {
10738
10738
  if (result.status === "approved" || result.status === "pending_review") {
10739
10739
  uploadSpinner.succeed(`Published ${m.name} v${m.version}`);
10740
10740
  if (result.status === "pending_review") {
10741
- console.log(source_default.dim(" Status: pending review \u2014 your space will be available after approval."));
10741
+ console.log(source_default.dim(" Status: pending review -- your space will be available after approval."));
10742
10742
  }
10743
10743
  } else if (result.status === "build_failed") {
10744
10744
  uploadSpinner.fail("Build failed on server");
@@ -10886,7 +10886,7 @@ function check() {
10886
10886
  process.exit(1);
10887
10887
  }
10888
10888
  console.log();
10889
- console.log(source_default.green(`\u2713 ${m.name} v${m.version} \u2014 all checks passed`));
10889
+ console.log(source_default.green(`\u2713 ${m.name} v${m.version} -- all checks passed`));
10890
10890
  }
10891
10891
 
10892
10892
  // src/commands/clean.ts
@@ -11192,7 +11192,7 @@ function generateFieldCode(field) {
11192
11192
  if (MODIFIERS[mod]) {
11193
11193
  code += MODIFIERS[mod];
11194
11194
  } else {
11195
- console.log(source_default.yellow(` \u26A0 Unknown modifier '${mod}' \u2014 skipped`));
11195
+ console.log(source_default.yellow(` \u26A0 Unknown modifier '${mod}' -- skipped`));
11196
11196
  }
11197
11197
  }
11198
11198
  return code;
@@ -11405,7 +11405,7 @@ async function graphPush() {
11405
11405
  }
11406
11406
  console.error(source_default.dim(" Fork to a new space_id to publish your own version."));
11407
11407
  } catch {
11408
- console.error(source_default.red(` 403: Forbidden \u2014 ownership check failed`));
11408
+ console.error(source_default.red(` 403: Forbidden -- ownership check failed`));
11409
11409
  }
11410
11410
  process.exit(1);
11411
11411
  }
@@ -11419,7 +11419,7 @@ async function graphPush() {
11419
11419
  spinner.succeed("Models registered");
11420
11420
  console.log();
11421
11421
  for (const model of models) {
11422
- console.log(` ${source_default.cyan(model.name)} \u2014 ${model.fields.length} field(s)`);
11422
+ console.log(` ${source_default.cyan(model.name)} -- ${model.fields.length} field(s)`);
11423
11423
  }
11424
11424
  console.log();
11425
11425
  console.log(source_default.dim(` GraphQL endpoint: ${graphURL}/graphql`));
@@ -11582,25 +11582,25 @@ async function graphMigrate(options) {
11582
11582
  const localFields = local.fields.map((f) => f.name);
11583
11583
  for (const sf of serverFields) {
11584
11584
  if (!localFields.includes(sf)) {
11585
- console.log(source_default.red(` - ${server.name}.${sf}`), source_default.dim("(on server, not in local model \u2014 can drop)"));
11585
+ console.log(source_default.red(` - ${server.name}.${sf}`), source_default.dim("(on server, not in local model -- can drop)"));
11586
11586
  hasChanges = true;
11587
11587
  }
11588
11588
  }
11589
11589
  for (const lf of localFields) {
11590
11590
  if (!serverFields.includes(lf)) {
11591
- console.log(source_default.green(` + ${server.name}.${lf}`), source_default.dim("(new \u2014 will be added on push)"));
11591
+ console.log(source_default.green(` + ${server.name}.${lf}`), source_default.dim("(new -- will be added on push)"));
11592
11592
  hasChanges = true;
11593
11593
  }
11594
11594
  }
11595
11595
  }
11596
11596
  for (const local of localModels) {
11597
11597
  if (!serverModels.find((m2) => m2.name === local.name)) {
11598
- console.log(source_default.green(` + Model "${local.name}"`), source_default.dim("(new \u2014 will be created on push)"));
11598
+ console.log(source_default.green(` + Model "${local.name}"`), source_default.dim("(new -- will be created on push)"));
11599
11599
  hasChanges = true;
11600
11600
  }
11601
11601
  }
11602
11602
  if (!hasChanges) {
11603
- console.log(source_default.green(" Schema is in sync \u2014 no changes needed"));
11603
+ console.log(source_default.green(" Schema is in sync -- no changes needed"));
11604
11604
  return;
11605
11605
  }
11606
11606
  console.log();
@@ -11700,7 +11700,7 @@ function graphFork(newSpaceID) {
11700
11700
  // package.json
11701
11701
  var package_default = {
11702
11702
  name: "@construct-space/cli",
11703
- version: "1.9.2",
11703
+ version: "1.9.3",
11704
11704
  description: "Construct CLI \u2014 scaffold, build, develop, and publish spaces",
11705
11705
  type: "module",
11706
11706
  bin: {
@@ -11747,12 +11747,12 @@ var package_default = {
11747
11747
  // src/index.ts
11748
11748
  var VERSION = package_default.version;
11749
11749
  var program2 = new Command;
11750
- program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
11750
+ program2.name("construct").description("Construct CLI -- scaffold, build, develop, and publish spaces").version(VERSION);
11751
11751
  program2.command("scaffold [name]").alias("new").alias("create").description("Create a new Construct space project").option("--with-tests", "Include E2E testing boilerplate").option("--full", "Full preset: multiple pages, extra skills, widget templates").action(async (name, opts) => scaffold(name, opts));
11752
11752
  program2.command("build").description("Build the space (generate entry + run Vite)").option("--entry-only", "Only generate src/entry.ts").action(async (opts) => build(opts));
11753
11753
  program2.command("dev").description("Start dev mode with file watching and live reload").action(async () => dev());
11754
11754
  program2.command("install").alias("run").description("Install built space to Construct spaces directory").action(() => install());
11755
- program2.command("publish").description("Publish a space to the Construct registry").option("-y, --yes", "Skip all confirmation prompts").option("--bump <type>", "Auto-bump version (patch, minor, major)").option("--private", "Publish as org-private (catalog-listed only inside the owning org). Requires an org publisher key.").option("--public", "Flip a previously-private space back to the public catalog on this publish. Without --private or --public, visibility is preserved on update (and defaults to public on first publish).").option("--scope <scope>", "Publish as 'user' (personal) or 'org'. Without this flag, the active publisher in auth.json decides.").option("--api-key <key>", "Publisher API key (csk_live_\u2026). Overrides any key stored in the active profile. Also reads CONSTRUCT_PUBLISHER_KEY.").action(async (opts) => publish(opts));
11755
+ program2.command("publish").description("Publish a space to the Construct registry").option("-y, --yes", "Skip all confirmation prompts").option("--bump <type>", "Auto-bump version (patch, minor, major)").option("--private", "Publish as org-private (catalog-listed only inside the owning org). Requires an org publisher key.").option("--public", "Flip a previously-private space back to the public catalog on this publish. Without --private or --public, visibility is preserved on update (and defaults to public on first publish).").option("--scope <scope>", "Publish as 'user' (personal) or 'org'. Without this flag, the active publisher in auth.json decides.").option("--api-key <key>", "Publisher API key (csk_live_...). Overrides any key stored in the active profile. Also reads CONSTRUCT_PUBLISHER_KEY.").action(async (opts) => publish(opts));
11756
11756
  program2.command("validate").description("Validate space.manifest.json").action(() => validate3());
11757
11757
  program2.command("check").description("Run type-check (vue-tsc) and linter (eslint)").action(() => check());
11758
11758
  program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
@@ -11762,7 +11762,7 @@ program2.command("update").description("Update the CLI to the latest version").a
11762
11762
  program2.command("whoami").alias("status").description("Show the signed-in user + current org scope").action(async () => (await Promise.resolve().then(() => (init_whoami(), exports_whoami))).whoami());
11763
11763
  var org = program2.command("org").description("Inspect the active organization context");
11764
11764
  org.command("status").description("Show active scope, publisher, and roles").action(async () => (await Promise.resolve().then(() => (init_org(), exports_org))).orgStatus());
11765
- var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
11765
+ var graph = program2.command("graph").description("Construct Graph -- data models and GraphQL");
11766
11766
  graph.command("init").description("Initialize Graph in a space project").action(() => graphInit());
11767
11767
  graph.command("generate <model> [fields...]").alias("g").description("Generate a data model").option("--access <rules>", "Access rules (e.g. read:member,create:member,update:owner,delete:admin)").action((model, fields, opts) => generate2(model, fields, opts));
11768
11768
  graph.command("push").description("Register models with the Graph service").action(async () => graphPush());
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Space Actions exposed to the AI agent via space_run_action.
2
+ * Space Actions -- exposed to the AI agent via space_run_action.
3
3
  *
4
4
  * Each action: { description, params, run, tier? }.
5
5
  * - `params` JSON-schema-ish input shape; each: { type, description?, required? }.
6
6
  * - `run` receives the validated payload, returns any JSON-serialisable value.
7
7
  * - `tier` (optional) default model bucket for useBrain() calls inside `run`:
8
- * 'small' fast/cheap summarisation, classification, short Q&A
9
- * 'medium' balanced general help, edits, structured output
10
- * 'large' reasoning long-form writing, deep code, planning
11
- * The host resolves tier provider+model via the user's tier config
12
- * (Settings LLM Providers). Per-call `brain.complete({ tier })`
8
+ * 'small' fast/cheap -- summarisation, classification, short Q&A
9
+ * 'medium' balanced -- general help, edits, structured output
10
+ * 'large' reasoning -- long-form writing, deep code, planning
11
+ * The host resolves tier -> provider+model via the user's tier config
12
+ * (Settings -> LLM Providers). Per-call `brain.complete({ tier })`
13
13
  * wins over the action default.
14
14
  *
15
15
  * Permission for useBrain():
@@ -31,7 +31,7 @@ import type { SpaceActions } from '@construct-space/sdk'
31
31
 
32
32
  export const actions: SpaceActions = {
33
33
  ping: {
34
- description: 'Health check returns pong with the space id.',
34
+ description: 'Health check -- returns pong with the space id.',
35
35
  params: {},
36
36
  run: () => ({ pong: true, space: '{{.ID}}' }),
37
37
  },
@@ -21,7 +21,7 @@ Use the listed tools above first. If you need an action that isn't whitelisted,
21
21
  call space_list_actions to discover what else this space exposes, then
22
22
  space_run_action to invoke it.
23
23
 
24
- Do NOT call get_project_context you work with space content, not project files.
24
+ Do NOT call get_project_context -- you work with space content, not project files.
25
25
 
26
26
  ## Behavior
27
27
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  id: construct-stack
3
3
  name: Construct Stack Guide
4
- description: How to build and extend a Construct space CLI, UI, SDK, Graph, manifest, agent actions
4
+ description: How to build and extend a Construct space -- CLI, UI, SDK, Graph, manifest, agent actions
5
5
  trigger: construct|space|graph|ui|sdk|manifest|action
6
6
  category: skill
7
7
  tools: [read_file, list_dir, glob, grep, write_file, edit_file]
8
8
  ---
9
9
 
10
- # Construct.md agent guide for `{{.DisplayName}}`
10
+ # Construct.md -- agent guide for `{{.DisplayName}}`
11
11
 
12
12
  This file briefs an AI coding agent on the Construct stack so it can build, extend, and ship this space without external lookups.
13
13
 
@@ -23,7 +23,7 @@ This file briefs an AI coding agent on the Construct stack so it can build, exte
23
23
  | Icons | `lucide-vue-next` | Icon set |
24
24
  | Tooling | `@construct-space/cli` | Build / dev / publish |
25
25
 
26
- All four are **host-provided externals** import normally, never bundle. Vite externalises them via `vite.config.ts`.
26
+ All four are **host-provided externals** -- import normally, never bundle. Vite externalises them via `vite.config.ts`.
27
27
 
28
28
  ---
29
29
 
@@ -35,7 +35,7 @@ All four are **host-provided externals** — import normally, never bundle. Vite
35
35
  src/
36
36
  entry.ts auto-generated; do not hand-edit
37
37
  actions.ts agent-callable actions (space_run_action)
38
- pages/ filesystem routing (e.g. settings.vue /settings)
38
+ pages/ filesystem routing (e.g. settings.vue -> /settings)
39
39
  components/ local Vue components
40
40
  composables/ local composables (use* convention)
41
41
  models/ graph models (defineModel)
@@ -52,7 +52,7 @@ All four are **host-provided externals** — import normally, never bundle. Vite
52
52
 
53
53
  ```bash
54
54
  construct dev hot-reload dev (regenerates entry.ts on change)
55
- construct build production build dist/
55
+ construct build production build -> dist/
56
56
  construct check manifest + typecheck + lint
57
57
  construct install install to active profile
58
58
  construct publish publish to registry (requires auth)
@@ -79,11 +79,11 @@ construct graph install <id> install a published space for current org
79
79
  }
80
80
  ```
81
81
 
82
- `scopes: ['org']` makes everything multi-tenant graph schemas auto-partition per org. Use `useOrg()` to read current org context. `projectAware: true` is the orthogonal flag for spaces that also live inside a project.
82
+ `scopes: ['org']` makes everything multi-tenant -- graph schemas auto-partition per org. Use `useOrg()` to read current org context. `projectAware: true` is the orthogonal flag for spaces that also live inside a project.
83
83
 
84
84
  ---
85
85
 
86
- ## `@construct-space/ui` component cheatsheet
86
+ ## `@construct-space/ui` -- component cheatsheet
87
87
 
88
88
  All exported from the package root:
89
89
 
@@ -91,7 +91,7 @@ All exported from the package root:
91
91
 
92
92
  **Inputs**: `Input`, `Textarea`, `Select` (options: `{label, value}[]`), `MultiSelect`, `Checkbox`, `Switch`, `RadioGroup`, `DatePicker`, `ColorPicker`, `FileInput`, `Slider`, `Autocomplete`, `FormField` (label/hint wrapper)
93
93
 
94
- **Display**: `Badge` (color: primary|neutral|success|warning|error|info; variant: solid|soft|subtle|outline), `Chip`, `Avatar` (`fallback` shows text pass initials), `Empty`, `Skeleton`, `Progress`, `Tooltip`, `Kbd`, `Alert`, `Notification`/`Toast`
94
+ **Display**: `Badge` (color: primary|neutral|success|warning|error|info; variant: solid|soft|subtle|outline), `Chip`, `Avatar` (`fallback` shows text -- pass initials), `Empty`, `Skeleton`, `Progress`, `Tooltip`, `Kbd`, `Alert`, `Notification`/`Toast`
95
95
 
96
96
  **Actions**: `Button` (variant: solid|ghost|outline; color: primary|neutral|error|...; size: xs|sm|md|lg), `Dropdown`, `DropdownMenu`, `ContextMenu`, `Popover`, `ToggleGroup` (segmented control)
97
97
 
@@ -104,13 +104,13 @@ All exported from the package root:
104
104
  ### Component gotchas
105
105
 
106
106
  - **`Avatar` sizes**: only `sm | md | lg` (no `xs`).
107
- - **`Select`** doesn't accept `multiple` render a chip-toggle list yourself for multi-select.
107
+ - **`Select`** doesn't accept `multiple` -- render a chip-toggle list yourself for multi-select.
108
108
  - **`Table`** column `align` literals must be `as const` to typecheck (`align: 'right' as const`).
109
109
  - **`Table` rowKey** is a string field name on the row object, not a function.
110
110
  - **`Modal`** uses `v-model:open` (not `v-model`); slots: `header | body | footer | accessory`.
111
111
  - **`Card`** slots: `header | accessory | default | footer | footer-end`. Use `title`/`description` props for the header to get standard styling; put right-side actions in `accessory`.
112
- - **`ConfirmationModal`** emits `@confirm` and `@cancel` wire both, otherwise dismissal does nothing.
113
- - **Settings pages**: never raw `<select>`/`<input>`/`<textarea>` use `Select`, `Switch`, `Input`, `Textarea` with `FormField` labels. Persist with `localStorage` (key prefix per space).
112
+ - **`ConfirmationModal`** emits `@confirm` and `@cancel` -- wire both, otherwise dismissal does nothing.
113
+ - **Settings pages**: never raw `<select>`/`<input>`/`<textarea>` -- use `Select`, `Switch`, `Input`, `Textarea` with `FormField` labels. Persist with `localStorage` (key prefix per space).
114
114
 
115
115
  ```vue
116
116
  <script setup lang="ts">
@@ -124,13 +124,13 @@ const open = ref(false)
124
124
  <Button variant="solid" size="sm" @click="open = true">Open</Button>
125
125
  </template>
126
126
  </Card>
127
- <Modal v-model:open="open" title="Demo">…</Modal>
127
+ <Modal v-model:open="open" title="Demo">...</Modal>
128
128
  </template>
129
129
  ```
130
130
 
131
131
  ---
132
132
 
133
- ## `@construct-space/sdk` host composables
133
+ ## `@construct-space/sdk` -- host composables
134
134
 
135
135
  ```ts
136
136
  import { useOrg, useOrgMembers, useToast, useAuth } from '@construct-space/sdk'
@@ -145,7 +145,7 @@ Use these for org-aware UX (assignee pickers, "shared with N" badges, avatars, m
145
145
 
146
146
  ---
147
147
 
148
- ## `@construct-space/graph` data layer
148
+ ## `@construct-space/graph` -- data layer
149
149
 
150
150
  Define typed models, get a typed reactive client, multi-tenant by default.
151
151
 
@@ -186,12 +186,12 @@ await tasks.remove(id)
186
186
 
187
187
  **Field types**: `string`, `int`, `number`, `boolean`, `date`, `json`, `enum([...])`. Chain `.required()`, `.index()`, `.unique()`, `.default(v)`.
188
188
 
189
- **Access helpers**: `authenticated()`, `owner()`, `admin()`, `member()`. `member()` requires org context use `authenticated()` for org-scoped models that allow any logged-in member.
189
+ **Access helpers**: `authenticated()`, `owner()`, `admin()`, `member()`. `member()` requires org context -- use `authenticated()` for org-scoped models that allow any logged-in member.
190
190
 
191
191
  **Scopes** (must match `scopes` in space.manifest.json):
192
- - `'app'` per-user bucket (personal install)
193
- - `'org'` shared across the active organization (org install)
194
- - both: `scopes: ['app', 'org']` host picks bucket at install time
192
+ - `'app'` -- per-user bucket (personal install)
193
+ - `'org'` -- shared across the active organization (org install)
194
+ - both: `scopes: ['app', 'org']` -- host picks bucket at install time
195
195
 
196
196
  `'project'` was removed in SDK 1.0. Older docs that show it are stale.
197
197
 
@@ -209,16 +209,16 @@ await tasks.remove(id)
209
209
  return []
210
210
  }
211
211
  ```
212
- - **Topological sort on push**: avoid `relation.belongsTo()` if push fails use a `field.string()` foreign-key column instead and join manually in composables.
212
+ - **Topological sort on push**: avoid `relation.belongsTo()` if push fails -- use a `field.string()` foreign-key column instead and join manually in composables.
213
213
  - **Lazy provisioning**: first query for a new tenant auto-creates the schema; expect a one-time slower request.
214
214
  - **Schema ownership is sticky**: the first profile to `construct graph push` becomes the schema owner. Subsequent pushes from a different profile fail with "Ownership check failed". Bump the space `id` to fork, or push from the original owner profile.
215
- - **`access.member()` requires org context** use `access.authenticated()` for org-scoped models that any logged-in member should be able to read/write. `member()` is for stricter membership checks.
215
+ - **`access.member()` requires org context** -- use `access.authenticated()` for org-scoped models that any logged-in member should be able to read/write. `member()` is for stricter membership checks.
216
216
 
217
217
  ---
218
218
 
219
219
  ## Actions (agent surface)
220
220
 
221
- `src/actions.ts` exports an `actions` object each entry is exposed to the space's agent as a first-class tool.
221
+ `src/actions.ts` exports an `actions` object -- each entry is exposed to the space's agent as a first-class tool.
222
222
 
223
223
  ```ts
224
224
  import type { SpaceActions } from '@construct-space/sdk'
@@ -239,7 +239,7 @@ export const actions: SpaceActions = {
239
239
 
240
240
  Actions can call the model mid-execution via `useBrain()`. The action picks
241
241
  the **cost bucket** (small / medium / large), the **user** picks the slot
242
- in Settings LLM Providers. The host maps tier provider + model.
242
+ in Settings -> LLM Providers. The host maps tier -> provider + model.
243
243
 
244
244
  ```ts
245
245
  import { useBrain } from '@construct-space/sdk'
@@ -261,7 +261,7 @@ export const actions: SpaceActions = {
261
261
 
262
262
  composeReply: {
263
263
  description: 'Draft a polished reply to the given message.',
264
- tier: 'large', // long-form writing opus-class
264
+ tier: 'large', // long-form writing -> opus-class
265
265
  params: {
266
266
  original: { type: 'string', required: true },
267
267
  style: { type: 'string', required: false, description: 'e.g. "warm", "formal"' },
@@ -304,7 +304,7 @@ to it under `permissions.actions`. The user grants this at install time.
304
304
  Without the grant, `useBrain()` throws `BrainPermissionDenied` synchronously
305
305
  on the first method call so the action can fall back gracefully.
306
306
 
307
- ### Wiring (REQUIRED easy to miss)
307
+ ### Wiring (REQUIRED -- easy to miss)
308
308
 
309
309
  Each action becomes a tool named **`{spaceID}.{actionName}`** (dot, not underscore). The operator only pre-registers actions that are **explicitly whitelisted** in the agent config. An empty `tools: []` means the agent sees zero action tools and will report "unknown tool".
310
310
 
@@ -319,15 +319,15 @@ tools:
319
319
  ---
320
320
  ```
321
321
 
322
- Fallback: agents can also discover/invoke actions via the generic `space_list_actions` + `space_run_action` bridge tools. Prefer the whitelist it gives the model proper tool definitions instead of stringly-typed action names.
322
+ Fallback: agents can also discover/invoke actions via the generic `space_list_actions` + `space_run_action` bridge tools. Prefer the whitelist -- it gives the model proper tool definitions instead of stringly-typed action names.
323
323
 
324
- Keep action descriptions tight and specific they're the only docstring the agent sees.
324
+ Keep action descriptions tight and specific -- they're the only docstring the agent sees.
325
325
 
326
326
  ---
327
327
 
328
328
  ## Drag and drop (HTML5)
329
329
 
330
- Tauri intercepts native drag-drop by default. The Construct host sets `dragDropEnabled: false` so HTML5 events fire normally use `draggable="true"`, `@dragstart`, `@dragover.prevent`, `@drop`. No extra deps required; reach for `vuedraggable` only if reordering needs auto-scroll/animation.
330
+ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropEnabled: false` so HTML5 events fire normally -- use `draggable="true"`, `@dragstart`, `@dragover.prevent`, `@drop`. No extra deps required; reach for `vuedraggable` only if reordering needs auto-scroll/animation.
331
331
 
332
332
  ---
333
333
 
@@ -335,7 +335,7 @@ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropE
335
335
 
336
336
  - **Imports**: `@construct-space/*` packages always external. Local imports use relative paths (no `@/` alias unless you add one).
337
337
  - **Composables**: `use*` prefix, keep in `src/composables/`.
338
- - **Pages**: filename = route path. `index.vue` `/`, `settings.vue` `/settings`, `[id].vue` param.
338
+ - **Pages**: filename = route path. `index.vue` -> `/`, `settings.vue` -> `/settings`, `[id].vue` -> param.
339
339
  - **Styling**: Tailwind utilities + CSS vars `--app-foreground`, `--app-background`, `--app-muted`, `--app-border`, `--app-accent`. Don't hard-code colors; the host themes via these variables.
340
340
  - **Comments**: explain *why*, not *what*. Skip TODOs in committed code.
341
341
  - **No emojis** in source unless the user requests them.
@@ -345,7 +345,7 @@ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropE
345
345
  ## Build pipeline reminder
346
346
 
347
347
  `construct build` writes:
348
- - `dist/space-{{.ID}}.iife.js` the bundled space
349
- - `dist/manifest.json` manifest + `build` block (checksum, size, **`hostApiVersion`**, builtAt)
348
+ - `dist/space-{{.ID}}.iife.js` -- the bundled space
349
+ - `dist/manifest.json` -- manifest + `build` block (checksum, size, **`hostApiVersion`**, builtAt)
350
350
 
351
351
  The runtime SpaceLoader compares `hostApiVersion` to its own; mismatches log a warning. Bump the CLI to keep them aligned.
@@ -1,4 +1,4 @@
1
- // Space entry exports pages, widgets, and actions for the host loader.
1
+ // Space entry -- exports pages, widgets, and actions for the host loader.
2
2
  // `construct dev` regenerates this from space.manifest.json on changes.
3
3
  import './style.css'
4
4
  import IndexPage from './pages/index.vue'
@@ -1,4 +1,4 @@
1
- // Space entry exports pages, widgets, and actions for the host loader.
1
+ // Space entry -- exports pages, widgets, and actions for the host loader.
2
2
  // `construct dev` regenerates this from space.manifest.json on changes.
3
3
  import './style.css'
4
4
  import IndexPage from './pages/index.vue'
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Settings page
3
+ * {{.DisplayName}} -- Settings page
4
4
  */
5
5
  import { ref } from 'vue'
6
6
 
@@ -1,14 +1,14 @@
1
- # {{.DisplayName}} data
1
+ # {{.DisplayName}} -- data
2
2
 
3
3
  Reference doc for the agent when working with {{.DisplayName}} data.
4
4
 
5
5
  ## Storage model
6
6
 
7
- Describe where {{.DisplayName}} data lives (Graph models, local storage, server API). Edit this section to match the actual model.
7
+ Describe where {{.DisplayName}} data lives (Graph models, local storage, server API...). Edit this section to match the actual model.
8
8
 
9
9
  ## CRUD conventions
10
10
 
11
11
  - Validate inputs before writing
12
12
  - Prefer the space's actions API over raw file or DB writes
13
13
  - Batch bulk changes when possible
14
- - Read existing rows before modifying changes should be additive when in doubt
14
+ - Read existing rows before modifying -- changes should be additive when in doubt
@@ -1,4 +1,4 @@
1
- # {{.DisplayName}} UI
1
+ # {{.DisplayName}} -- UI
2
2
 
3
3
  Reference doc for the agent when adjusting {{.DisplayName}}'s UI.
4
4
 
@@ -12,8 +12,8 @@ Use Construct CSS variables instead of hardcoded values:
12
12
 
13
13
  ## Layout
14
14
 
15
- - Live inside the host shell avoid full-bleed layouts that conflict with the sidebar/header
16
- - Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, ) for consistency
15
+ - Live inside the host shell -- avoid full-bleed layouts that conflict with the sidebar/header
16
+ - Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, ...) for consistency
17
17
  - Prefer flex/grid over absolute positioning
18
18
 
19
19
  ## Accessibility
@@ -1,12 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Home page
3
+ * {{.DisplayName}} -- Home page
4
4
  *
5
5
  * Built-in libraries available at runtime (do not bundle):
6
- * @construct-space/ui Vue 3 components (Button, Card, Modal, Table, Badge, ...)
7
- * @construct-space/sdk useOrg, useOrgMembers, useToast, useAuth
8
- * @construct-space/graph defineModel, useGraph (multi-tenant data layer)
9
- * lucide-vue-next icons
6
+ * @construct-space/ui -- Vue 3 components (Button, Card, Modal, Table, Badge, ...)
7
+ * @construct-space/sdk -- useOrg, useOrgMembers, useToast, useAuth
8
+ * @construct-space/graph -- defineModel, useGraph (multi-tenant data layer)
9
+ * lucide-vue-next -- icons
10
10
  */
11
11
  import { ref } from 'vue'
12
12
  import { Button, Card, Empty } from '@construct-space/ui'
@@ -26,6 +26,6 @@ Help the user with tasks inside the {{.DisplayName}} space.
26
26
 
27
27
  The agent loads these on demand:
28
28
 
29
- - `references/` domain-specific docs (data model, UI conventions, )
30
- - `scripts/` supporting executables
31
- - `assets/` templates and static resources
29
+ - `references/` -- domain-specific docs (data model, UI conventions, ...)
30
+ - `scripts/` -- supporting executables
31
+ - `assets/` -- templates and static resources
@@ -4,19 +4,19 @@ import vue from '@vitejs/plugin-vue'
4
4
  import { resolve } from 'path'
5
5
 
6
6
  /**
7
- * Host-provided externals these are supplied by Construct at runtime
7
+ * Host-provided externals -- these are supplied by Construct at runtime
8
8
  * via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
9
9
  *
10
10
  * Source of truth: construct-app/frontend/lib/spaceHost.ts
11
11
  *
12
- * IMPORTANT only add a package here AFTER confirming spaceHost.ts
12
+ * IMPORTANT -- only add a package here AFTER confirming spaceHost.ts
13
13
  * actually puts it on window.__CONSTRUCT__. Externalising a package the
14
14
  * host doesn't expose silently substitutes `undefined`; you'll see
15
15
  * "X is not a function" at runtime. When in doubt, bundle it (omit from
16
- * this list) bundle bloat is cheaper than a broken space.
16
+ * this list) -- bundle bloat is cheaper than a broken space.
17
17
  *
18
18
  * Notably NOT externalised:
19
- * @construct-space/graph host does not expose this. Bundle it.
19
+ * @construct-space/graph -- host does not expose this. Bundle it.
20
20
  */
21
21
  const hostExternals = [
22
22
  'vue',
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Summary Widget 2×1 compact view
3
+ * {{.DisplayName}} Summary Widget -- 2x1 compact view
4
4
  *
5
5
  * Widgets run inside a closed Shadow DOM sandbox.
6
- * Use the injected widgetApi for theme and actions do not access
6
+ * Use the injected widgetApi for theme and actions -- do not access
7
7
  * window, document, or global stores directly.
8
8
  */
9
9
  import { inject } from 'vue'
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Summary Widget 4×1 wide view
3
+ * {{.DisplayName}} Summary Widget -- 4x1 wide view
4
4
  *
5
5
  * Widgets run inside a closed Shadow DOM sandbox.
6
- * Use the injected widgetApi for theme and actions do not access
6
+ * Use the injected widgetApi for theme and actions -- do not access
7
7
  * window, document, or global stores directly.
8
8
  */
9
9
  import { inject } from 'vue'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@construct-space/cli",
3
- "version": "1.9.2",
3
+ "version": "1.9.3",
4
4
  "description": "Construct CLI — scaffold, build, develop, and publish spaces",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Space Actions exposed to the AI agent via space_run_action.
2
+ * Space Actions -- exposed to the AI agent via space_run_action.
3
3
  *
4
4
  * Each action: { description, params, run, tier? }.
5
5
  * - `params` JSON-schema-ish input shape; each: { type, description?, required? }.
6
6
  * - `run` receives the validated payload, returns any JSON-serialisable value.
7
7
  * - `tier` (optional) default model bucket for useBrain() calls inside `run`:
8
- * 'small' fast/cheap summarisation, classification, short Q&A
9
- * 'medium' balanced general help, edits, structured output
10
- * 'large' reasoning long-form writing, deep code, planning
11
- * The host resolves tier provider+model via the user's tier config
12
- * (Settings LLM Providers). Per-call `brain.complete({ tier })`
8
+ * 'small' fast/cheap -- summarisation, classification, short Q&A
9
+ * 'medium' balanced -- general help, edits, structured output
10
+ * 'large' reasoning -- long-form writing, deep code, planning
11
+ * The host resolves tier -> provider+model via the user's tier config
12
+ * (Settings -> LLM Providers). Per-call `brain.complete({ tier })`
13
13
  * wins over the action default.
14
14
  *
15
15
  * Permission for useBrain():
@@ -31,7 +31,7 @@ import type { SpaceActions } from '@construct-space/sdk'
31
31
 
32
32
  export const actions: SpaceActions = {
33
33
  ping: {
34
- description: 'Health check returns pong with the space id.',
34
+ description: 'Health check -- returns pong with the space id.',
35
35
  params: {},
36
36
  run: () => ({ pong: true, space: '{{.ID}}' }),
37
37
  },
@@ -21,7 +21,7 @@ Use the listed tools above first. If you need an action that isn't whitelisted,
21
21
  call space_list_actions to discover what else this space exposes, then
22
22
  space_run_action to invoke it.
23
23
 
24
- Do NOT call get_project_context you work with space content, not project files.
24
+ Do NOT call get_project_context -- you work with space content, not project files.
25
25
 
26
26
  ## Behavior
27
27
 
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  id: construct-stack
3
3
  name: Construct Stack Guide
4
- description: How to build and extend a Construct space CLI, UI, SDK, Graph, manifest, agent actions
4
+ description: How to build and extend a Construct space -- CLI, UI, SDK, Graph, manifest, agent actions
5
5
  trigger: construct|space|graph|ui|sdk|manifest|action
6
6
  category: skill
7
7
  tools: [read_file, list_dir, glob, grep, write_file, edit_file]
8
8
  ---
9
9
 
10
- # Construct.md agent guide for `{{.DisplayName}}`
10
+ # Construct.md -- agent guide for `{{.DisplayName}}`
11
11
 
12
12
  This file briefs an AI coding agent on the Construct stack so it can build, extend, and ship this space without external lookups.
13
13
 
@@ -23,7 +23,7 @@ This file briefs an AI coding agent on the Construct stack so it can build, exte
23
23
  | Icons | `lucide-vue-next` | Icon set |
24
24
  | Tooling | `@construct-space/cli` | Build / dev / publish |
25
25
 
26
- All four are **host-provided externals** import normally, never bundle. Vite externalises them via `vite.config.ts`.
26
+ All four are **host-provided externals** -- import normally, never bundle. Vite externalises them via `vite.config.ts`.
27
27
 
28
28
  ---
29
29
 
@@ -35,7 +35,7 @@ All four are **host-provided externals** — import normally, never bundle. Vite
35
35
  src/
36
36
  entry.ts auto-generated; do not hand-edit
37
37
  actions.ts agent-callable actions (space_run_action)
38
- pages/ filesystem routing (e.g. settings.vue /settings)
38
+ pages/ filesystem routing (e.g. settings.vue -> /settings)
39
39
  components/ local Vue components
40
40
  composables/ local composables (use* convention)
41
41
  models/ graph models (defineModel)
@@ -52,7 +52,7 @@ All four are **host-provided externals** — import normally, never bundle. Vite
52
52
 
53
53
  ```bash
54
54
  construct dev hot-reload dev (regenerates entry.ts on change)
55
- construct build production build dist/
55
+ construct build production build -> dist/
56
56
  construct check manifest + typecheck + lint
57
57
  construct install install to active profile
58
58
  construct publish publish to registry (requires auth)
@@ -79,11 +79,11 @@ construct graph install <id> install a published space for current org
79
79
  }
80
80
  ```
81
81
 
82
- `scopes: ['org']` makes everything multi-tenant graph schemas auto-partition per org. Use `useOrg()` to read current org context. `projectAware: true` is the orthogonal flag for spaces that also live inside a project.
82
+ `scopes: ['org']` makes everything multi-tenant -- graph schemas auto-partition per org. Use `useOrg()` to read current org context. `projectAware: true` is the orthogonal flag for spaces that also live inside a project.
83
83
 
84
84
  ---
85
85
 
86
- ## `@construct-space/ui` component cheatsheet
86
+ ## `@construct-space/ui` -- component cheatsheet
87
87
 
88
88
  All exported from the package root:
89
89
 
@@ -91,7 +91,7 @@ All exported from the package root:
91
91
 
92
92
  **Inputs**: `Input`, `Textarea`, `Select` (options: `{label, value}[]`), `MultiSelect`, `Checkbox`, `Switch`, `RadioGroup`, `DatePicker`, `ColorPicker`, `FileInput`, `Slider`, `Autocomplete`, `FormField` (label/hint wrapper)
93
93
 
94
- **Display**: `Badge` (color: primary|neutral|success|warning|error|info; variant: solid|soft|subtle|outline), `Chip`, `Avatar` (`fallback` shows text pass initials), `Empty`, `Skeleton`, `Progress`, `Tooltip`, `Kbd`, `Alert`, `Notification`/`Toast`
94
+ **Display**: `Badge` (color: primary|neutral|success|warning|error|info; variant: solid|soft|subtle|outline), `Chip`, `Avatar` (`fallback` shows text -- pass initials), `Empty`, `Skeleton`, `Progress`, `Tooltip`, `Kbd`, `Alert`, `Notification`/`Toast`
95
95
 
96
96
  **Actions**: `Button` (variant: solid|ghost|outline; color: primary|neutral|error|...; size: xs|sm|md|lg), `Dropdown`, `DropdownMenu`, `ContextMenu`, `Popover`, `ToggleGroup` (segmented control)
97
97
 
@@ -104,13 +104,13 @@ All exported from the package root:
104
104
  ### Component gotchas
105
105
 
106
106
  - **`Avatar` sizes**: only `sm | md | lg` (no `xs`).
107
- - **`Select`** doesn't accept `multiple` render a chip-toggle list yourself for multi-select.
107
+ - **`Select`** doesn't accept `multiple` -- render a chip-toggle list yourself for multi-select.
108
108
  - **`Table`** column `align` literals must be `as const` to typecheck (`align: 'right' as const`).
109
109
  - **`Table` rowKey** is a string field name on the row object, not a function.
110
110
  - **`Modal`** uses `v-model:open` (not `v-model`); slots: `header | body | footer | accessory`.
111
111
  - **`Card`** slots: `header | accessory | default | footer | footer-end`. Use `title`/`description` props for the header to get standard styling; put right-side actions in `accessory`.
112
- - **`ConfirmationModal`** emits `@confirm` and `@cancel` wire both, otherwise dismissal does nothing.
113
- - **Settings pages**: never raw `<select>`/`<input>`/`<textarea>` use `Select`, `Switch`, `Input`, `Textarea` with `FormField` labels. Persist with `localStorage` (key prefix per space).
112
+ - **`ConfirmationModal`** emits `@confirm` and `@cancel` -- wire both, otherwise dismissal does nothing.
113
+ - **Settings pages**: never raw `<select>`/`<input>`/`<textarea>` -- use `Select`, `Switch`, `Input`, `Textarea` with `FormField` labels. Persist with `localStorage` (key prefix per space).
114
114
 
115
115
  ```vue
116
116
  <script setup lang="ts">
@@ -124,13 +124,13 @@ const open = ref(false)
124
124
  <Button variant="solid" size="sm" @click="open = true">Open</Button>
125
125
  </template>
126
126
  </Card>
127
- <Modal v-model:open="open" title="Demo">…</Modal>
127
+ <Modal v-model:open="open" title="Demo">...</Modal>
128
128
  </template>
129
129
  ```
130
130
 
131
131
  ---
132
132
 
133
- ## `@construct-space/sdk` host composables
133
+ ## `@construct-space/sdk` -- host composables
134
134
 
135
135
  ```ts
136
136
  import { useOrg, useOrgMembers, useToast, useAuth } from '@construct-space/sdk'
@@ -145,7 +145,7 @@ Use these for org-aware UX (assignee pickers, "shared with N" badges, avatars, m
145
145
 
146
146
  ---
147
147
 
148
- ## `@construct-space/graph` data layer
148
+ ## `@construct-space/graph` -- data layer
149
149
 
150
150
  Define typed models, get a typed reactive client, multi-tenant by default.
151
151
 
@@ -186,12 +186,12 @@ await tasks.remove(id)
186
186
 
187
187
  **Field types**: `string`, `int`, `number`, `boolean`, `date`, `json`, `enum([...])`. Chain `.required()`, `.index()`, `.unique()`, `.default(v)`.
188
188
 
189
- **Access helpers**: `authenticated()`, `owner()`, `admin()`, `member()`. `member()` requires org context use `authenticated()` for org-scoped models that allow any logged-in member.
189
+ **Access helpers**: `authenticated()`, `owner()`, `admin()`, `member()`. `member()` requires org context -- use `authenticated()` for org-scoped models that allow any logged-in member.
190
190
 
191
191
  **Scopes** (must match `scopes` in space.manifest.json):
192
- - `'app'` per-user bucket (personal install)
193
- - `'org'` shared across the active organization (org install)
194
- - both: `scopes: ['app', 'org']` host picks bucket at install time
192
+ - `'app'` -- per-user bucket (personal install)
193
+ - `'org'` -- shared across the active organization (org install)
194
+ - both: `scopes: ['app', 'org']` -- host picks bucket at install time
195
195
 
196
196
  `'project'` was removed in SDK 1.0. Older docs that show it are stale.
197
197
 
@@ -209,16 +209,16 @@ await tasks.remove(id)
209
209
  return []
210
210
  }
211
211
  ```
212
- - **Topological sort on push**: avoid `relation.belongsTo()` if push fails use a `field.string()` foreign-key column instead and join manually in composables.
212
+ - **Topological sort on push**: avoid `relation.belongsTo()` if push fails -- use a `field.string()` foreign-key column instead and join manually in composables.
213
213
  - **Lazy provisioning**: first query for a new tenant auto-creates the schema; expect a one-time slower request.
214
214
  - **Schema ownership is sticky**: the first profile to `construct graph push` becomes the schema owner. Subsequent pushes from a different profile fail with "Ownership check failed". Bump the space `id` to fork, or push from the original owner profile.
215
- - **`access.member()` requires org context** use `access.authenticated()` for org-scoped models that any logged-in member should be able to read/write. `member()` is for stricter membership checks.
215
+ - **`access.member()` requires org context** -- use `access.authenticated()` for org-scoped models that any logged-in member should be able to read/write. `member()` is for stricter membership checks.
216
216
 
217
217
  ---
218
218
 
219
219
  ## Actions (agent surface)
220
220
 
221
- `src/actions.ts` exports an `actions` object each entry is exposed to the space's agent as a first-class tool.
221
+ `src/actions.ts` exports an `actions` object -- each entry is exposed to the space's agent as a first-class tool.
222
222
 
223
223
  ```ts
224
224
  import type { SpaceActions } from '@construct-space/sdk'
@@ -239,7 +239,7 @@ export const actions: SpaceActions = {
239
239
 
240
240
  Actions can call the model mid-execution via `useBrain()`. The action picks
241
241
  the **cost bucket** (small / medium / large), the **user** picks the slot
242
- in Settings LLM Providers. The host maps tier provider + model.
242
+ in Settings -> LLM Providers. The host maps tier -> provider + model.
243
243
 
244
244
  ```ts
245
245
  import { useBrain } from '@construct-space/sdk'
@@ -261,7 +261,7 @@ export const actions: SpaceActions = {
261
261
 
262
262
  composeReply: {
263
263
  description: 'Draft a polished reply to the given message.',
264
- tier: 'large', // long-form writing opus-class
264
+ tier: 'large', // long-form writing -> opus-class
265
265
  params: {
266
266
  original: { type: 'string', required: true },
267
267
  style: { type: 'string', required: false, description: 'e.g. "warm", "formal"' },
@@ -304,7 +304,7 @@ to it under `permissions.actions`. The user grants this at install time.
304
304
  Without the grant, `useBrain()` throws `BrainPermissionDenied` synchronously
305
305
  on the first method call so the action can fall back gracefully.
306
306
 
307
- ### Wiring (REQUIRED easy to miss)
307
+ ### Wiring (REQUIRED -- easy to miss)
308
308
 
309
309
  Each action becomes a tool named **`{spaceID}.{actionName}`** (dot, not underscore). The operator only pre-registers actions that are **explicitly whitelisted** in the agent config. An empty `tools: []` means the agent sees zero action tools and will report "unknown tool".
310
310
 
@@ -319,15 +319,15 @@ tools:
319
319
  ---
320
320
  ```
321
321
 
322
- Fallback: agents can also discover/invoke actions via the generic `space_list_actions` + `space_run_action` bridge tools. Prefer the whitelist it gives the model proper tool definitions instead of stringly-typed action names.
322
+ Fallback: agents can also discover/invoke actions via the generic `space_list_actions` + `space_run_action` bridge tools. Prefer the whitelist -- it gives the model proper tool definitions instead of stringly-typed action names.
323
323
 
324
- Keep action descriptions tight and specific they're the only docstring the agent sees.
324
+ Keep action descriptions tight and specific -- they're the only docstring the agent sees.
325
325
 
326
326
  ---
327
327
 
328
328
  ## Drag and drop (HTML5)
329
329
 
330
- Tauri intercepts native drag-drop by default. The Construct host sets `dragDropEnabled: false` so HTML5 events fire normally use `draggable="true"`, `@dragstart`, `@dragover.prevent`, `@drop`. No extra deps required; reach for `vuedraggable` only if reordering needs auto-scroll/animation.
330
+ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropEnabled: false` so HTML5 events fire normally -- use `draggable="true"`, `@dragstart`, `@dragover.prevent`, `@drop`. No extra deps required; reach for `vuedraggable` only if reordering needs auto-scroll/animation.
331
331
 
332
332
  ---
333
333
 
@@ -335,7 +335,7 @@ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropE
335
335
 
336
336
  - **Imports**: `@construct-space/*` packages always external. Local imports use relative paths (no `@/` alias unless you add one).
337
337
  - **Composables**: `use*` prefix, keep in `src/composables/`.
338
- - **Pages**: filename = route path. `index.vue` `/`, `settings.vue` `/settings`, `[id].vue` param.
338
+ - **Pages**: filename = route path. `index.vue` -> `/`, `settings.vue` -> `/settings`, `[id].vue` -> param.
339
339
  - **Styling**: Tailwind utilities + CSS vars `--app-foreground`, `--app-background`, `--app-muted`, `--app-border`, `--app-accent`. Don't hard-code colors; the host themes via these variables.
340
340
  - **Comments**: explain *why*, not *what*. Skip TODOs in committed code.
341
341
  - **No emojis** in source unless the user requests them.
@@ -345,7 +345,7 @@ Tauri intercepts native drag-drop by default. The Construct host sets `dragDropE
345
345
  ## Build pipeline reminder
346
346
 
347
347
  `construct build` writes:
348
- - `dist/space-{{.ID}}.iife.js` the bundled space
349
- - `dist/manifest.json` manifest + `build` block (checksum, size, **`hostApiVersion`**, builtAt)
348
+ - `dist/space-{{.ID}}.iife.js` -- the bundled space
349
+ - `dist/manifest.json` -- manifest + `build` block (checksum, size, **`hostApiVersion`**, builtAt)
350
350
 
351
351
  The runtime SpaceLoader compares `hostApiVersion` to its own; mismatches log a warning. Bump the CLI to keep them aligned.
@@ -1,4 +1,4 @@
1
- // Space entry exports pages, widgets, and actions for the host loader.
1
+ // Space entry -- exports pages, widgets, and actions for the host loader.
2
2
  // `construct dev` regenerates this from space.manifest.json on changes.
3
3
  import './style.css'
4
4
  import IndexPage from './pages/index.vue'
@@ -1,4 +1,4 @@
1
- // Space entry exports pages, widgets, and actions for the host loader.
1
+ // Space entry -- exports pages, widgets, and actions for the host loader.
2
2
  // `construct dev` regenerates this from space.manifest.json on changes.
3
3
  import './style.css'
4
4
  import IndexPage from './pages/index.vue'
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Settings page
3
+ * {{.DisplayName}} -- Settings page
4
4
  */
5
5
  import { ref } from 'vue'
6
6
 
@@ -1,14 +1,14 @@
1
- # {{.DisplayName}} data
1
+ # {{.DisplayName}} -- data
2
2
 
3
3
  Reference doc for the agent when working with {{.DisplayName}} data.
4
4
 
5
5
  ## Storage model
6
6
 
7
- Describe where {{.DisplayName}} data lives (Graph models, local storage, server API). Edit this section to match the actual model.
7
+ Describe where {{.DisplayName}} data lives (Graph models, local storage, server API...). Edit this section to match the actual model.
8
8
 
9
9
  ## CRUD conventions
10
10
 
11
11
  - Validate inputs before writing
12
12
  - Prefer the space's actions API over raw file or DB writes
13
13
  - Batch bulk changes when possible
14
- - Read existing rows before modifying changes should be additive when in doubt
14
+ - Read existing rows before modifying -- changes should be additive when in doubt
@@ -1,4 +1,4 @@
1
- # {{.DisplayName}} UI
1
+ # {{.DisplayName}} -- UI
2
2
 
3
3
  Reference doc for the agent when adjusting {{.DisplayName}}'s UI.
4
4
 
@@ -12,8 +12,8 @@ Use Construct CSS variables instead of hardcoded values:
12
12
 
13
13
  ## Layout
14
14
 
15
- - Live inside the host shell avoid full-bleed layouts that conflict with the sidebar/header
16
- - Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, ) for consistency
15
+ - Live inside the host shell -- avoid full-bleed layouts that conflict with the sidebar/header
16
+ - Use the `@construct-space/ui` primitives (`Card`, `Button`, `Badge`, ...) for consistency
17
17
  - Prefer flex/grid over absolute positioning
18
18
 
19
19
  ## Accessibility
@@ -1,12 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Home page
3
+ * {{.DisplayName}} -- Home page
4
4
  *
5
5
  * Built-in libraries available at runtime (do not bundle):
6
- * @construct-space/ui Vue 3 components (Button, Card, Modal, Table, Badge, ...)
7
- * @construct-space/sdk useOrg, useOrgMembers, useToast, useAuth
8
- * @construct-space/graph defineModel, useGraph (multi-tenant data layer)
9
- * lucide-vue-next icons
6
+ * @construct-space/ui -- Vue 3 components (Button, Card, Modal, Table, Badge, ...)
7
+ * @construct-space/sdk -- useOrg, useOrgMembers, useToast, useAuth
8
+ * @construct-space/graph -- defineModel, useGraph (multi-tenant data layer)
9
+ * lucide-vue-next -- icons
10
10
  */
11
11
  import { ref } from 'vue'
12
12
  import { Button, Card, Empty } from '@construct-space/ui'
@@ -26,6 +26,6 @@ Help the user with tasks inside the {{.DisplayName}} space.
26
26
 
27
27
  The agent loads these on demand:
28
28
 
29
- - `references/` domain-specific docs (data model, UI conventions, )
30
- - `scripts/` supporting executables
31
- - `assets/` templates and static resources
29
+ - `references/` -- domain-specific docs (data model, UI conventions, ...)
30
+ - `scripts/` -- supporting executables
31
+ - `assets/` -- templates and static resources
@@ -4,19 +4,19 @@ import vue from '@vitejs/plugin-vue'
4
4
  import { resolve } from 'path'
5
5
 
6
6
  /**
7
- * Host-provided externals these are supplied by Construct at runtime
7
+ * Host-provided externals -- these are supplied by Construct at runtime
8
8
  * via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
9
9
  *
10
10
  * Source of truth: construct-app/frontend/lib/spaceHost.ts
11
11
  *
12
- * IMPORTANT only add a package here AFTER confirming spaceHost.ts
12
+ * IMPORTANT -- only add a package here AFTER confirming spaceHost.ts
13
13
  * actually puts it on window.__CONSTRUCT__. Externalising a package the
14
14
  * host doesn't expose silently substitutes `undefined`; you'll see
15
15
  * "X is not a function" at runtime. When in doubt, bundle it (omit from
16
- * this list) bundle bloat is cheaper than a broken space.
16
+ * this list) -- bundle bloat is cheaper than a broken space.
17
17
  *
18
18
  * Notably NOT externalised:
19
- * @construct-space/graph host does not expose this. Bundle it.
19
+ * @construct-space/graph -- host does not expose this. Bundle it.
20
20
  */
21
21
  const hostExternals = [
22
22
  'vue',
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Summary Widget 2×1 compact view
3
+ * {{.DisplayName}} Summary Widget -- 2x1 compact view
4
4
  *
5
5
  * Widgets run inside a closed Shadow DOM sandbox.
6
- * Use the injected widgetApi for theme and actions do not access
6
+ * Use the injected widgetApi for theme and actions -- do not access
7
7
  * window, document, or global stores directly.
8
8
  */
9
9
  import { inject } from 'vue'
@@ -1,9 +1,9 @@
1
1
  <script setup lang="ts">
2
2
  /**
3
- * {{.DisplayName}} Summary Widget 4×1 wide view
3
+ * {{.DisplayName}} Summary Widget -- 4x1 wide view
4
4
  *
5
5
  * Widgets run inside a closed Shadow DOM sandbox.
6
- * Use the injected widgetApi for theme and actions do not access
6
+ * Use the injected widgetApi for theme and actions -- do not access
7
7
  * window, document, or global stores directly.
8
8
  */
9
9
  import { inject } from 'vue'