busa-cli 0.52.0 → 0.53.0

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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as runCli, r as pkgVersion, t as checkForUpdate } from "./update-check-CNYcPsfm.js";
2
+ import { i as runCli, r as pkgVersion, t as checkForUpdate } from "./update-check-Rqylvhw0.js";
3
3
  //#region src/cli.ts
4
4
  const exitCode = await runCli(process.argv.slice(2));
5
5
  const notice = await checkForUpdate("busabase-cli", pkgVersion());
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import { a as render, i as runCli, n as HELP, r as pkgVersion, t as checkForUpdate } from "./update-check-CNYcPsfm.js";
1
+ import { a as render, i as runCli, n as HELP, r as pkgVersion, t as checkForUpdate } from "./update-check-Rqylvhw0.js";
2
2
  import { DEFAULT_BASE_URL, createBusabaseClient, normalizeBaseUrl } from "busabase-sdk";
3
3
  export { DEFAULT_BASE_URL, HELP, checkForUpdate, createBusabaseClient, normalizeBaseUrl, pkgVersion, render, runCli };
@@ -160,12 +160,20 @@ const customPromptBodySchema = iStringSchema.refine((value) => iStringLocaleValu
160
160
  * One custom scenario prompt. `body`'s `{target}` placeholder is substituted at
161
161
  * render time with the same target string `PromptDef.body(target)` receives
162
162
  * today (see `node-agent-prompts.ts`) — this schema does not interpolate it.
163
+ *
164
+ * The placeholder is OPTIONAL and chooses placement only: a `body` that never
165
+ * mentions `{target}` gets the target line prepended as its first paragraph, so
166
+ * a custom prompt can never reach an agent without naming the node it acts on.
167
+ * That is why the schema does not require it — forgetting it is not an error to
168
+ * reject, it is a default to supply.
163
169
  */
164
170
  const customPromptDefSchema = z.object({
165
171
  /** Stable id, unique within this node's custom list. */
166
172
  key: z.string().trim().min(1, { message: "key must not be empty" }),
167
173
  /** Defaults to `change` (same default the curated prompts use) so a prompt
168
- * cannot silently bypass the approval-first policy by omission. */
174
+ * cannot silently opt out of the change-request path by omission — whether that
175
+ * path then merges immediately or waits for review is the permission layer's
176
+ * call, not the prompt's. */
169
177
  intent: customPromptIntentSchema.optional(),
170
178
  /** Short title shown in the dialog's left list. */
171
179
  label: customPromptLabelSchema,
@@ -1236,6 +1244,7 @@ const baseNodeType = {
1236
1244
  capabilities: {
1237
1245
  hasDetail: true,
1238
1246
  creatable: true,
1247
+ commonlyCreated: true,
1239
1248
  publicAccess: "detail"
1240
1249
  },
1241
1250
  operations: [
@@ -1340,6 +1349,7 @@ const docNodeType = {
1340
1349
  capabilities: {
1341
1350
  hasDetail: true,
1342
1351
  creatable: true,
1352
+ commonlyCreated: true,
1343
1353
  publicAccess: "detail"
1344
1354
  },
1345
1355
  operations: [{
@@ -1371,6 +1381,7 @@ const fileNodeType = {
1371
1381
  capabilities: {
1372
1382
  hasDetail: true,
1373
1383
  creatable: true,
1384
+ commonlyCreated: true,
1374
1385
  publicAccess: "detail"
1375
1386
  },
1376
1387
  operations: []
@@ -1386,6 +1397,7 @@ const folderNodeType = {
1386
1397
  capabilities: {
1387
1398
  container: true,
1388
1399
  creatable: true,
1400
+ commonlyCreated: true,
1389
1401
  hasDetail: true,
1390
1402
  publicAccess: "detail"
1391
1403
  },
@@ -1396,9 +1408,10 @@ const folderNodeType = {
1396
1408
  /**
1397
1409
  * Form node: an agent-authored, sandboxed web page bound to a Base via an
1398
1410
  * explicit field-binding contract. A submission does NOT write a record
1399
- * directly — it produces a review-first record-create ChangeRequest on the
1400
- * target Base (so the submitting act reuses the base's `record_create` op, not a
1401
- * form-specific one). The form's own config (bindings/page/share) is owner-
1411
+ * directly — it produces a record-create ChangeRequest on the target Base (so
1412
+ * the submitting act reuses the base's `record_create` op, not a form-specific
1413
+ * one), which then merges immediately or waits for review according to the
1414
+ * submitter's permission on that Base. The form's own config (bindings/page/share) is owner-
1402
1415
  * managed and edited directly, so this node contributes no CR operations of its
1403
1416
  * own for now.
1404
1417
  */
@@ -1406,10 +1419,23 @@ const formNodeType = {
1406
1419
  type: "form",
1407
1420
  label: "Form",
1408
1421
  icon: "form",
1422
+ /**
1423
+ * `hidden` until a Form can be created from a create surface at all.
1424
+ *
1425
+ * `busabase_forms.target_base_id` is NOT NULL and `form` registers no
1426
+ * `node_create` materializer, so a Form built through the generic New-item
1427
+ * flow (which only collects name/slug/description) is a node row with no form
1428
+ * config behind it — it opens to a dead end, every time, for everyone. The
1429
+ * type stays fully `creatable` so `forms.create` (which does take a target
1430
+ * Base) and the REST/MCP surface are untouched; it just no longer offers an
1431
+ * entry point that cannot succeed. Drop this once the New-item flow asks for
1432
+ * the target Base and a materializer writes the config row.
1433
+ */
1409
1434
  capabilities: {
1410
1435
  hasDetail: true,
1411
1436
  creatable: true,
1412
- publicAccess: "submit"
1437
+ publicAccess: "submit",
1438
+ hidden: true
1413
1439
  },
1414
1440
  operations: []
1415
1441
  };
@@ -5703,6 +5729,19 @@ const TemplateManifestSchema = z.object({
5703
5729
  /** Card/detail screenshots, package-relative (`assets/screenshots/overview.webp`). */
5704
5730
  screenshots: z.array(z.string()).default([]),
5705
5731
  /**
5732
+ * Optional demo clip, package-relative (`assets/recordings/busa-crm.mp4`).
5733
+ *
5734
+ * No companion poster field on purpose: the detail page uses
5735
+ * `screenshots[0]`, which the catalog already requires to be the cover. One
5736
+ * declared path instead of two that can disagree with each other.
5737
+ *
5738
+ * Note for anyone adding a sibling field here: this is a plain `z.object`, so
5739
+ * an unrecognized key in `busabase.json` is silently stripped rather than
5740
+ * rejected. A template cannot declare a field ahead of the schema landing —
5741
+ * it just vanishes, with no error from `busabase-cli check`.
5742
+ */
5743
+ video: z.string().optional(),
5744
+ /**
5706
5745
  * Ready-made prompts shown after install ("Ask agent" prefills the first).
5707
5746
  *
5708
5747
  * They are the difference between a folder of tables and something a user can
@@ -6047,7 +6086,26 @@ const updateFieldChangeRequestInputSchema = z.object({
6047
6086
  patch: z.object({
6048
6087
  name: fieldNameSchema.optional(),
6049
6088
  required: z.boolean().optional(),
6050
- options: fieldOptionsSchema.optional()
6089
+ options: fieldOptionsSchema.optional(),
6090
+ /**
6091
+ * Not a patch key — rejected on purpose, and the only key here that is.
6092
+ *
6093
+ * `update` cannot change a field's type; `convert` does, after
6094
+ * `previewFieldConversion` has shown what happens to the stored values.
6095
+ * But `patch` is a plain (non-strict) object, so `{ type: "markdown" }`
6096
+ * used to be stripped silently: the request validated, the change request
6097
+ * merged, `ok: true` came back, and the field was still whatever it was.
6098
+ * A caller reaching for the obvious-but-wrong shape got a successful
6099
+ * no-op, which reads exactly like a successful conversion.
6100
+ *
6101
+ * Blanket `.strict()` is not the fix here — see `contract/auto-merge.ts`
6102
+ * on why these schemas stay open: the SDK ships on its own cadence
6103
+ * against self-hosted servers, so a newer client sending a newer optional
6104
+ * key is normal traffic, and strictness would 400 all of them to catch
6105
+ * this one. Naming the single key that will never be legitimate keeps
6106
+ * that forward compatibility intact.
6107
+ */
6108
+ type: z.never({ message: "A field type cannot be changed with `update`. Use operation: \"convert\" with `newType`, and call `previewFieldConversion` first to see how many stored values survive it." }).optional()
6051
6109
  }),
6052
6110
  message: z.string().optional(),
6053
6111
  submittedBy: z.string().optional().default("local-editor"),
@@ -6321,7 +6379,14 @@ z.object({
6321
6379
  name: z.string(),
6322
6380
  slug: z.string(),
6323
6381
  path: z.string(),
6324
- updatedAt: z.string()
6382
+ updatedAt: z.string(),
6383
+ /**
6384
+ * The node's own custom avatar, same shape as `NodeVO.icon`. Optional so an
6385
+ * older server that predates this field is still a valid response — a
6386
+ * caller that doesn't know it falls back to the type icon exactly as it
6387
+ * always has.
6388
+ */
6389
+ icon: NodeIconSchema.nullable().optional()
6325
6390
  });
6326
6391
  const userRefSchema = z.object({
6327
6392
  id: z.string(),
@@ -7584,6 +7649,8 @@ const auditPackage = (tree, files, options = {}) => {
7584
7649
  const present = new Set(paths);
7585
7650
  if (options.directoryName !== void 0 && options.directoryName !== tree.manifest.name) error("package/identity", `Directory is "${options.directoryName}" but busabase.json declares "${tree.manifest.name}". The directory name is the package's identity, not a label beside it.`);
7586
7651
  for (const screenshot of tree.manifest.template?.screenshots ?? []) if (!present.has(screenshot)) error("package/screenshot-missing", `busabase.json declares screenshot "${screenshot}", which is not in the package. The Template Center card would render a broken image.`);
7652
+ const video = tree.manifest.template?.video;
7653
+ if (video && !present.has(video)) error("package/video-missing", `busabase.json declares video "${video}", which is not in the package. The Template Center detail page would show a play button that leads nowhere.`);
7587
7654
  for (const relative of paths) {
7588
7655
  const base = relative.slice(relative.lastIndexOf("/") + 1);
7589
7656
  if (/^\.env(?!\.example$)(?:\.|$)/.test(base)) error("package/dotenv", `${relative} is part of the package. Credentials belong in Vault, never in something people install.`);
@@ -9317,7 +9384,7 @@ const toCreatableFields = (node, fields, recordIdsByKey) => {
9317
9384
  return result;
9318
9385
  };
9319
9386
  const createRecords = async (client, base, recordsToCreate, plan, options, state, recordIdsByKey, app) => {
9320
- const mergeRecords = options.autoMerge || (app?.mergeSampleRecords ?? false);
9387
+ const mergeRecords = app ? app.mergeSampleRecords : options.autoMerge;
9321
9388
  const records = [...recordsToCreate].sort((a, b) => a.key.localeCompare(b.key, "en"));
9322
9389
  for (let offset = 0; offset < records.length; offset += 200) {
9323
9390
  const batch = records.slice(offset, offset + 200);
@@ -10058,12 +10125,17 @@ const hasUnlinkableRelationValues = (node) => {
10058
10125
  };
10059
10126
  const findCollisions = (tree, targetFolderSlug, target) => {
10060
10127
  const collisions = [];
10061
- for (const node of walkNodes(tree.nodes)) if (target.existingNodeSlugsByType.get(node.type)?.has(node.slug)) collisions.push({
10062
- kind: node.type === "base" ? "base" : "node",
10063
- nodeType: node.type,
10064
- slug: node.slug,
10065
- path: `${targetFolderSlug}/…/${node.slug} (${node.type} slugs are unique per space)`
10066
- });
10128
+ const check = (type, slug) => {
10129
+ if (!target.existingNodeSlugsByType.get(type)?.has(slug)) return;
10130
+ collisions.push({
10131
+ kind: type === "base" ? "base" : "node",
10132
+ nodeType: type,
10133
+ slug,
10134
+ path: `${targetFolderSlug}/…/${slug} (${type} slugs are unique per space)`
10135
+ });
10136
+ };
10137
+ for (const node of walkNodes(tree.nodes)) check(node.type, node.slug);
10138
+ if (tree.rootSkill) check("skill", tree.rootSkill.slug);
10067
10139
  return collisions;
10068
10140
  };
10069
10141
  const nodeIdentityKey = (node) => `${node.type}\0${node.slug}`;
@@ -10102,7 +10174,22 @@ const applyRenames = (tree, collisions, existingNodeSlugsByType) => {
10102
10174
  }
10103
10175
  const nodeRenames = /* @__PURE__ */ new Map();
10104
10176
  const baseRenames = /* @__PURE__ */ new Map();
10177
+ if (tree.rootSkill) {
10178
+ const taken = takenByType.get("skill") ?? /* @__PURE__ */ new Set();
10179
+ taken.add(tree.rootSkill.slug);
10180
+ takenByType.set("skill", taken);
10181
+ }
10182
+ let rootSkillRename;
10105
10183
  for (const collision of collisions) {
10184
+ if (tree.rootSkill && collision.nodeType === "skill" && collision.slug === tree.rootSkill.slug) {
10185
+ const taken = takenByType.get("skill") ?? /* @__PURE__ */ new Set();
10186
+ const renamed = nextFreeSlug(collision.slug, taken);
10187
+ taken.add(renamed);
10188
+ takenByType.set("skill", taken);
10189
+ rootSkillRename = renamed;
10190
+ collision.renamedTo = renamed;
10191
+ continue;
10192
+ }
10106
10193
  const node = [...walkNodes(tree.nodes)].find((candidate) => candidate.type === collision.nodeType && candidate.slug === collision.slug);
10107
10194
  if (!node) continue;
10108
10195
  const taken = takenByType.get(node.type) ?? /* @__PURE__ */ new Set();
@@ -10148,7 +10235,11 @@ const applyRenames = (tree, collisions, existingNodeSlugsByType) => {
10148
10235
  };
10149
10236
  return {
10150
10237
  ...tree,
10151
- nodes: tree.nodes.map(rewriteNode)
10238
+ nodes: tree.nodes.map(rewriteNode),
10239
+ ...tree.rootSkill && rootSkillRename ? { rootSkill: {
10240
+ ...tree.rootSkill,
10241
+ slug: rootSkillRename
10242
+ } } : {}
10152
10243
  };
10153
10244
  };
10154
10245
  const collectWarnings = (tree, warnings) => {
@@ -10337,6 +10428,7 @@ const discoverPackages = (files) => {
10337
10428
  ...tree.manifest.template?.category ? { category: tree.manifest.template.category } : {},
10338
10429
  ...validation.risk ? { risk: validation.risk } : {},
10339
10430
  screenshots: tree.manifest.template?.screenshots ?? [],
10431
+ ...tree.manifest.template?.video ? { video: tree.manifest.template.video } : {},
10340
10432
  tags: [.../* @__PURE__ */ new Set([...tree.manifest.tags, ...tree.manifest.template?.tags ?? []])],
10341
10433
  agentPrompts: tree.manifest.template?.agentPrompts ?? [],
10342
10434
  ...tree.manifest.version ? { version: tree.manifest.version } : {},
@@ -10563,6 +10655,7 @@ const toEntry = (found, airapps) => ({
10563
10655
  ...found.risk ? { risk: found.risk } : {},
10564
10656
  tags: found.tags ?? [],
10565
10657
  screenshots: found.screenshots,
10658
+ ...found.video ? { video: found.video } : {},
10566
10659
  agentPrompts: found.agentPrompts ?? [],
10567
10660
  ...found.version ? { version: found.version } : {},
10568
10661
  ...found.author ? { author: found.author } : {},
@@ -11371,6 +11464,7 @@ const PROCEDURE_PERMISSION_POLICY = {
11371
11464
  "webhooks.testFire": workspace("manage"),
11372
11465
  "vault.get": workspace("manage"),
11373
11466
  "vault.update": workspace("manage"),
11467
+ "vault.updatePreviewFileCredential": workspace("manage"),
11374
11468
  "vault.clear": workspace("manage"),
11375
11469
  "dump.exportTables": workspace("manage"),
11376
11470
  "dump.exportAssetText": workspace("manage"),
@@ -11407,6 +11501,8 @@ const PROCEDURE_PERMISSION_POLICY = {
11407
11501
  "bases.previewFieldConversion": node("read"),
11408
11502
  "fileTrees.listFiles": node("read"),
11409
11503
  "fileTrees.readFile": node("read"),
11504
+ "fileTrees.previewConfig": workspace("read"),
11505
+ "fileTrees.preparePreview": node("read"),
11410
11506
  "airapps.runLocal": node("write"),
11411
11507
  "airapps.stopLocal": node("write"),
11412
11508
  "nodes.readLines": node("read"),
@@ -11421,6 +11517,7 @@ const PROCEDURE_PERMISSION_POLICY = {
11421
11517
  "assets.readTextLines": node("read"),
11422
11518
  "install.planFromGithub": workspace("read"),
11423
11519
  "install.fromGithub": workspace("manage"),
11520
+ "install.fromGithubStream": workspace("manage"),
11424
11521
  "templates.list": workspace("read"),
11425
11522
  "guides.list": workspace("read"),
11426
11523
  "guides.read": workspace("read"),
@@ -11430,6 +11527,7 @@ const PROCEDURE_PERMISSION_POLICY = {
11430
11527
  "agents.sessions.list": workspace("manage"),
11431
11528
  "agents.sessions.create": workspace("manage"),
11432
11529
  "agents.sessions.prompt": workspace("manage"),
11530
+ "agents.sessions.setConfigOption": workspace("manage"),
11433
11531
  "agents.sessions.cancel": workspace("manage"),
11434
11532
  "agents.sessions.close": workspace("manage"),
11435
11533
  "agents.sessions.subscribe": workspace("manage"),
@@ -11732,9 +11830,10 @@ const wrapGuidance = (text, width = 78) => {
11732
11830
  /**
11733
11831
  * `--wait`: block until a submitted proposal is actually decided.
11734
11832
  *
11735
- * Review-first is the product's whole point, so the useful unit of work for a
11736
- * caller is not "the change request was filed" — it is "the change request was
11737
- * merged, or it wasn't". Without this every caller writes the same poll loop
11833
+ * A write can still land as a pending proposal — a proposal-only credential, no
11834
+ * write access on the target, or an explicit `--require-review` — so the useful
11835
+ * unit of work for a caller is not "the change request was filed" but "the
11836
+ * change request was merged, or it wasn't". Without this every caller writes the same poll loop
11738
11837
  * against `change-requests get`, and an agent that skips it reports a proposal
11739
11838
  * nobody has looked at as a completed task.
11740
11839
  */
@@ -12831,7 +12930,7 @@ Examples:
12831
12930
  submittedBy: opts.submittedBy
12832
12931
  });
12833
12932
  }));
12834
- addGlobalFlags(bases.command("create-change-request")).description("Propose a new record via a Change Request — review-first by default").requiredOption("--base-id <id>", "target Base id").requiredOption("--fields-json <json|@file>", "record fields as JSON, or @file.json").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option("--auto-merge", "skip review and create the record immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)").option("--require-review", "always propose a pending Change Request, even if you have write access").addHelpText("after", `
12933
+ addGlobalFlags(bases.command("create-change-request")).description("Create a record via a Change Request — merged immediately if you have write access, otherwise proposed for review").requiredOption("--base-id <id>", "target Base id").requiredOption("--fields-json <json|@file>", "record fields as JSON, or @file.json").option("--message <text>", "reviewer-facing Change Request message").option("--submitted-by <name>", "producer label").option("--auto-merge", "skip review and create the record immediately if you have write access (default: merge immediately if you have write access, otherwise propose a pending Change Request)").option("--require-review", "always propose a pending Change Request, even if you have write access").addHelpText("after", `
12835
12934
  Examples:
12836
12935
  busabase-cli bases create-change-request --base-id bse_123 --fields-json '{"title":"Hello","status":"draft"}'
12837
12936
  busabase-cli bases create-change-request --base-id bse_123 --fields-json @record.json
@@ -12918,7 +13017,7 @@ Examples:
12918
13017
  startLine: opts.startLine,
12919
13018
  endLine: opts.endLine
12920
13019
  })));
12921
- addGlobalFlags(program.command("install")).description("Install a Busabase package from a GitHub repo or a local directory into this space").argument("<source>", "GitHub repo URL (optionally /tree/<ref>[/<subdir>]), or a local directory / file:// URL written by `busabase-cli export`").option("--into-folder <name>", "target folder slug (default: the package's manifest name)").option("--dry-run", "print the plan (tree, record counts, collisions) and create nothing").option("--auto-merge", "merge the package's records and docs on the spot instead of leaving them as change requests to review — this TRUSTS THE PACKAGE AUTHOR, since skills and AirApps carry code your agents will run (default: review first)").option("--rename", "install colliding items under suffixed slugs (-2, -3, …) instead of failing").option("--skill <name>", "which package to install when the URL is a repository holding several (e.g. busabase/skills)").option("--no-sample-records", "propose a template's sample rows for review instead of merging them (default: merged, so the app is not empty on first open)").option("--no-rollback", "keep whatever a failed install created (default: it is moved to Trash)").addHelpText("after", `
13020
+ addGlobalFlags(program.command("install")).description("Install a Busabase package from a GitHub repo or a local directory into this space").argument("<source>", "GitHub repo URL (optionally /tree/<ref>[/<subdir>]), or a local directory / file:// URL written by `busabase-cli export`").option("--into-folder <name>", "target folder slug (default: the package's manifest name)").option("--dry-run", "print the plan (tree, record counts, collisions) and create nothing").option("--auto-merge", "merge the package's records and docs on the spot (the default; the flag is kept so scripts that pass it keep working)").option("--require-review", "leave the package's records and docs as change requests for you to approve, instead of merging them").option("--rename", "install colliding items under suffixed slugs (-2, -3, …) instead of failing").option("--skill <name>", "which package to install when the URL is a repository holding several (e.g. busabase/skills)").option("--no-sample-records", "propose a template's sample rows for review instead of merging them (default: merged, so the app is not empty on first open)").option("--no-rollback", "keep whatever a failed install created (default: it is moved to Trash)").addHelpText("after", `
12922
13021
  The URL's git ref is the version pin — a tag installs that tag's content forever,
12923
13022
  even after the branch moves on. GITHUB_TOKEN is honored for private repos.
12924
13023
 
@@ -12931,12 +13030,15 @@ Examples:
12931
13030
  busabase-cli install https://github.com/acme/support-kb-template
12932
13031
  busabase-cli install https://github.com/acme/packages/tree/v1.2.0/skills/pdf-summarizer
12933
13032
  busabase-cli install https://github.com/acme/support-kb-template --dry-run
12934
- busabase-cli install https://github.com/acme/support-kb-template --into-folder support --auto-merge
13033
+ busabase-cli install https://github.com/acme/support-kb-template --into-folder support --require-review
12935
13034
  busabase-cli install ./support-kb-template --dry-run
12936
13035
 
12937
13036
  Folders, Bases, their fields and their views are structure and are always created
12938
- immediately. Records are content: by default they land as change requests for you
12939
- to review, and --auto-merge merges them on the spot instead.
13037
+ immediately. Records, docs and app code are content: they merge on the spot too if
13038
+ your credential can write to the space, and --require-review leaves them as change
13039
+ requests for you to approve instead. Installing is already restricted to a space
13040
+ owner/admin, so choosing to install IS the trust decision — remember that a package
13041
+ can carry skills and AirApps, i.e. code your agents will run.
12940
13042
 
12941
13043
  A TEMPLATE (a package that also carries a SKILL.md) installs as an app: its
12942
13044
  manual lands as a Skill node, its nodes are stamped as belonging to that app, its
@@ -12948,13 +13050,13 @@ still wait for your review.
12948
13050
  If the URL is a repository that holds several packages rather than being one,
12949
13051
  install lists them and you pick with --skill <name>.
12950
13052
 
12951
- A package whose records carry relation values requires --auto-merge — a relation
12952
- stores the ids of the records it points at, and those exist only once the records
12953
- are merged, so review-first would install every relation empty. Defining a relation
12954
- field with nothing linked yet does not trigger this.`).action(runArgAction(state, (repoUrl, client, opts, config) => runInstall(client, repoUrl, {
13053
+ A package whose records carry relation values cannot be installed with
13054
+ --require-review — a relation stores the ids of the records it points at, and those
13055
+ exist only once the records are merged, so review would install every relation
13056
+ empty. Defining a relation field with nothing linked yet does not trigger this.`).action(runArgAction(state, (repoUrl, client, opts, config) => runInstall(client, repoUrl, {
12955
13057
  intoFolder: opts.intoFolder,
12956
13058
  dryRun: Boolean(opts.dryRun),
12957
- autoMerge: Boolean(opts.autoMerge),
13059
+ autoMerge: opts.requireReview !== true,
12958
13060
  rename: Boolean(opts.rename),
12959
13061
  skill: opts.skill,
12960
13062
  noSampleRecords: opts.sampleRecords === false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "busa-cli",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "Command-line client for the Busabase OpenAPI REST API. Talks to a local or remote `busabase server`. Short-name alias for busabase-cli.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/busabase/busabase/tree/main/apps/busabase-cli",
@@ -33,7 +33,7 @@
33
33
  "dependencies": {
34
34
  "@zip.js/zip.js": "^2.8.20",
35
35
  "busabase-package": "workspace:*",
36
- "busabase-sdk": "0.52.0",
36
+ "busabase-sdk": "0.53.0",
37
37
  "commander": "^14.0.0",
38
38
  "qrcode": "^1.5.4",
39
39
  "tar-stream": "^3.1.7",