@fleox-agent-crm/agentcrm 0.4.0 → 0.5.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.
Files changed (46) hide show
  1. package/README.md +16 -16
  2. package/changelog.json +9 -0
  3. package/dist/admin/admin-files.json +1 -1
  4. package/dist/agent/agentcrm.mjs +133 -61
  5. package/dist/agent/chunks/{automations-DwWruxmu.mjs → automations-BqY8EwfJ.mjs} +3 -3
  6. package/dist/agent/chunks/{backup-W3CjgkAJ.mjs → backup-CO0EzdF_.mjs} +3 -3
  7. package/dist/agent/chunks/{compact-BGHI92tm.mjs → compact-qF5t42ko.mjs} +3 -3
  8. package/dist/agent/chunks/{exportCsv-C6e6XEmB.mjs → exportCsv-C-fIHLm1.mjs} +2 -2
  9. package/dist/agent/chunks/{extCheck-GgHG1dYB.mjs → extCheck-BcbUc7eB.mjs} +4 -4
  10. package/dist/agent/chunks/{files-D1daDgY0.mjs → files-C4YONVqQ.mjs} +2 -2
  11. package/dist/agent/chunks/{forms-BfuWaQjA.mjs → forms-DJDop3XY.mjs} +2 -2
  12. package/dist/agent/chunks/{importCsv-LVmMTaWU.mjs → importCsv-XjQJ9QIR.mjs} +2 -2
  13. package/dist/agent/chunks/{install-BLNflFes.mjs → install-D1lgtRIH.mjs} +1 -1
  14. package/dist/agent/chunks/{loader-MPD9iAvh.mjs → loader-Cv0UJf6b.mjs} +11 -11
  15. package/dist/agent/chunks/{migrate-CsClnnN8.mjs → migrate-Cc_8MHZv.mjs} +1 -1
  16. package/dist/agent/chunks/{pipelines-BAKpPk1w.mjs → pipelines-DiJ2dLqQ.mjs} +3 -3
  17. package/dist/agent/chunks/{records-DcHFelq5.mjs → records-CCTxxMWF.mjs} +1 -1
  18. package/dist/agent/chunks/{registry-COfJrSmg.mjs → registry-ClzNZv9Z.mjs} +10 -10
  19. package/dist/agent/chunks/{reports-BACHGccj.mjs → reports-WlI_A5Nr.mjs} +3 -3
  20. package/dist/agent/chunks/{runtime-C0Xf0Axi.mjs → runtime-BAF5vU5D.mjs} +17 -17
  21. package/dist/agent/chunks/{schema-C4lSslXv.mjs → schema-DOnnH2zL.mjs} +3 -3
  22. package/dist/agent/chunks/schemaApply-DKHipylx.mjs +164 -0
  23. package/dist/agent/chunks/{seed-6MRmQQBR.mjs → seed-Bb26Zsni.mjs} +2 -2
  24. package/dist/agent/chunks/{server-DC3t8r30.mjs → server-Bch4TQon.mjs} +3 -3
  25. package/dist/agent/chunks/{standardObjects-CheqfDqa.mjs → standardObjects-vrmqIVLH.mjs} +1 -1
  26. package/dist/agent/chunks/{stats-BfuLWhVV.mjs → stats-DfmDP3tE.mjs} +3 -3
  27. package/dist/agent/chunks/{tools-iSH-yMpl.mjs → tools-CCk24tdh.mjs} +9 -9
  28. package/dist/agent/chunks/{views-DH1n9Lrt.mjs → views-BbhmQO3n.mjs} +4 -4
  29. package/dist/version.json +1 -1
  30. package/package.json +1 -1
  31. package/plugin/.claude-plugin/plugin.json +1 -1
  32. package/plugin/skills/agentcrm-admin/SKILL.md +67 -67
  33. package/plugin/skills/agentcrm-admin/references/records.md +5 -5
  34. package/plugin/skills/agentcrm-admin/references/schema.md +19 -19
  35. package/plugin/skills/agentcrm-extensions/SKILL.md +8 -8
  36. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +2 -2
  37. package/plugin/skills/agentcrm-extensions/references/testing.md +14 -14
  38. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +3 -3
  39. package/plugin/skills/agentcrm-install/SKILL.md +13 -13
  40. package/plugin/skills/agentcrm-install/references/firebase.md +1 -1
  41. package/plugin/skills/agentcrm-install/references/github.md +1 -1
  42. package/plugin/skills/agentcrm-install/references/mcp.md +2 -2
  43. package/plugin/skills/agentcrm-install/references/plans.md +1 -1
  44. package/plugin/skills/agentcrm-install/references/s3.md +1 -1
  45. package/plugin/skills/agentcrm-manage/SKILL.md +15 -15
  46. package/plugin/skills/agentcrm-sales/SKILL.md +18 -18
@@ -142,7 +142,7 @@ const output = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
142
142
  toCliError
143
143
  }, Symbol.toStringTag, { value: "Module" }));
144
144
  const name = "@fleox-agent-crm/agentcrm";
145
- const version = "0.4.0";
145
+ const version = "0.5.0";
146
146
  const type = "module";
147
147
  const description = "The static CRM your AI agent can run: a Twenty / HubSpot-style CRM that runs entirely in the browser or headless through the agentcrm CLI, with data on storage you own.";
148
148
  const license = "SEE LICENSE IN LICENSE";
@@ -524,7 +524,7 @@ function tmpFolder() {
524
524
  return tmpBase;
525
525
  }
526
526
  async function nodeImportBundle() {
527
- await import("./chunks/runtime-C0Xf0Axi.mjs");
527
+ await import("./chunks/runtime-BAF5vU5D.mjs");
528
528
  const { createNodeImportBundle } = await import("./chunks/nodeImport-ylfRDvqt.mjs");
529
529
  return createNodeImportBundle({ runtimeDir: runtimeDir(), tmpDir: tmpFolder() });
530
530
  }
@@ -539,7 +539,7 @@ async function loadSessionExtensions(store, opts = {}) {
539
539
  if (!loadedAny && !opts.force && !hasEnabledExtensions(store)) return null;
540
540
  try {
541
541
  const importBundle = await nodeImportBundle();
542
- const { syncExtensions } = await import("./chunks/loader-MPD9iAvh.mjs");
542
+ const { syncExtensions } = await import("./chunks/loader-Cv0UJf6b.mjs");
543
543
  loadedAny = true;
544
544
  return await syncExtensions(store, { importBundle, onError: () => void 0 });
545
545
  } catch (err) {
@@ -553,7 +553,7 @@ async function loadSessionExtensions(store, opts = {}) {
553
553
  sync,
554
554
  async close() {
555
555
  if (!loadedAny) return;
556
- (await import("./chunks/registry-COfJrSmg.mjs")).deactivateAllExtensions();
556
+ (await import("./chunks/registry-ClzNZv9Z.mjs")).deactivateAllExtensions();
557
557
  }
558
558
  };
559
559
  }
@@ -681,7 +681,7 @@ async function openSession(alias, opts = {}) {
681
681
  const [{ createOpsContext }, { createPluginRegistry, plugins: defaultPlugins }, { installAutomations }] = await Promise.all([
682
682
  import("./chunks/context-CxFfqS4D.mjs"),
683
683
  import("./chunks/pluginRegistry-DyHtlkcr.mjs"),
684
- import("./chunks/automations-DwWruxmu.mjs")
684
+ import("./chunks/automations-BqY8EwfJ.mjs")
685
685
  ]);
686
686
  const own = createPluginRegistry((hook, err) => process.stderr.write(`[agentcrm] ${hook} handler failed: ${String(err?.message ?? err)}
687
687
  `));
@@ -695,7 +695,7 @@ async function openSession(alias, opts = {}) {
695
695
  const ctx = createOpsContext(store, user, { plugins, log });
696
696
  const system = createOpsContext(store, null, { plugins, log });
697
697
  if (opts.lock && (!user || user.role === "admin") && store.list("objects").length) {
698
- const { migrateSchema } = await import("./chunks/migrate-CsClnnN8.mjs").then((n) => n.c);
698
+ const { migrateSchema } = await import("./chunks/migrate-Cc_8MHZv.mjs").then((n) => n.c);
699
699
  await migrateSchema(ctx).catch((err) => warnings.push(`Schema migration skipped: ${err.message}`));
700
700
  }
701
701
  if (site.hosting) {
@@ -714,7 +714,7 @@ async function openSession(alias, opts = {}) {
714
714
  }
715
715
  }
716
716
  {
717
- const forms = await import("./chunks/forms-BfuWaQjA.mjs");
717
+ const forms = await import("./chunks/forms-DJDop3XY.mjs");
718
718
  const { buildConnector } = await import("./chunks/hosting-C_UDyMkm.mjs");
719
719
  forms.setInboxSourceResolver((_ctx, form) => {
720
720
  if (form.inbox.kind !== "s3") return null;
@@ -914,6 +914,29 @@ function table(rows, columns) {
914
914
  const SITE_OPTION = { name: "site", type: "string", description: "Site alias (default: $AGENTCRM_SITE, ./agentcrm.json, the only site, or `site use`)." };
915
915
  const AS_OPTION = { name: "as", type: "string", description: "Act as this CRM user (email or id) instead of the site's user." };
916
916
  const YES_OPTION = { name: "yes", type: "boolean", description: "Apply — only after the user confirmed the plan." };
917
+ const util = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
918
+ __proto__: null,
919
+ AS_OPTION,
920
+ SITE_OPTION,
921
+ YES_OPTION,
922
+ boolOpt,
923
+ fieldsInput,
924
+ jsonObjectOpt,
925
+ jsonOpt,
926
+ listOpt,
927
+ multiOpt,
928
+ numOpt,
929
+ parseFilters,
930
+ parseScalar,
931
+ parseSorts,
932
+ positional,
933
+ readTextValue,
934
+ requireStr,
935
+ strOpt,
936
+ table,
937
+ usage,
938
+ writeOutFile
939
+ }, Symbol.toStringTag, { value: "Module" }));
917
940
  function fileOut(f) {
918
941
  return {
919
942
  id: f.id,
@@ -936,7 +959,7 @@ function readLocalFile(path) {
936
959
  return { name: basename(full), bytes: new Uint8Array(readFileSync(full)) };
937
960
  }
938
961
  async function uploadLocalFiles(ctx, paths, targets2 = []) {
939
- const { uploadFile } = await import("./chunks/files-D1daDgY0.mjs");
962
+ const { uploadFile } = await import("./chunks/files-C4YONVqQ.mjs");
940
963
  const { guessMime, imageDimensions } = await import("./chunks/files-CjsPJSR6.mjs");
941
964
  const out = [];
942
965
  for (const p of paths) {
@@ -984,7 +1007,7 @@ const filesCommand = {
984
1007
  switch (action) {
985
1008
  case "list":
986
1009
  return withSession(io, options, {}, async ({ ctx }) => {
987
- const { listFiles } = await import("./chunks/files-D1daDgY0.mjs");
1010
+ const { listFiles } = await import("./chunks/files-C4YONVqQ.mjs");
988
1011
  const target = strOpt(options, "record") ?? strOpt(options, "activity") ?? null;
989
1012
  const items = listFiles(ctx, target, { includeRelated: boolOpt(options, "related"), kind: strOpt(options, "kind"), search: strOpt(options, "search") });
990
1013
  const limit = Math.min(1e3, numOpt(options, "limit") ?? 100);
@@ -999,7 +1022,7 @@ ${table(r.files, ["id", "name", "mime", "size", "createdAt"])}`);
999
1022
  if (record && activity) usage("Give --record or --activity, not both.");
1000
1023
  for (const p of args) readLocalFile(p);
1001
1024
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1002
- const { attachFile } = await import("./chunks/files-D1daDgY0.mjs");
1025
+ const { attachFile } = await import("./chunks/files-C4YONVqQ.mjs");
1003
1026
  const files2 = await uploadLocalFiles(ctx, args, record ? [record] : []);
1004
1027
  if (activity) for (const f of files2) await attachFile(ctx, f.id, { kind: "activity", id: activity });
1005
1028
  const { requireFile } = await import("./chunks/store-5gqRSKXg.mjs");
@@ -1012,7 +1035,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1012
1035
  const out = strOpt(options, "out");
1013
1036
  if (!out) usage("files get needs --out <path>.");
1014
1037
  return withSession(io, options, {}, async ({ ctx }) => {
1015
- const { getFile, getFileBytes } = await import("./chunks/files-D1daDgY0.mjs");
1038
+ const { getFile, getFileBytes } = await import("./chunks/files-C4YONVqQ.mjs");
1016
1039
  const meta = getFile(ctx, id);
1017
1040
  const bytes = await getFileBytes(ctx, id, { thumb: boolOpt(options, "thumb") });
1018
1041
  writeOutFile(out, bytes);
@@ -1022,7 +1045,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1022
1045
  case "delete": {
1023
1046
  const id = positional(args, 0, "file id", U$1.delete);
1024
1047
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1025
- const { getFile, deleteFile } = await import("./chunks/files-D1daDgY0.mjs");
1048
+ const { getFile, deleteFile } = await import("./chunks/files-C4YONVqQ.mjs");
1026
1049
  const meta = fileOut(getFile(ctx, id));
1027
1050
  if (options.yes !== true) return { needsConfirmation: true, plan: { delete: meta } };
1028
1051
  await deleteFile(ctx, id);
@@ -1030,7 +1053,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1030
1053
  }, (r) => "plan" in r && r.plan ? `Would delete ${r.plan.delete.name} (${r.plan.delete.id}). Re-run with --yes once the user agrees.` : `Deleted ${"deleted" in r ? r.deleted : id} (bytes purged by \`agentcrm compact\`).`);
1031
1054
  }
1032
1055
  case "usage":
1033
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-D1daDgY0.mjs")).fileUsage(ctx));
1056
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-C4YONVqQ.mjs")).fileUsage(ctx));
1034
1057
  default:
1035
1058
  usage(`Unknown files action "${action ?? ""}".`, "Use list, upload, get, delete or usage.");
1036
1059
  }
@@ -1043,7 +1066,7 @@ async function recordImageAction(io, options, args) {
1043
1066
  if (!remove && !path) usage("records image needs an image path (or --remove).");
1044
1067
  const local = remove ? null : readLocalFile(path);
1045
1068
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1046
- const { setRecordImage, clearRecordImage } = await import("./chunks/files-D1daDgY0.mjs");
1069
+ const { setRecordImage, clearRecordImage } = await import("./chunks/files-C4YONVqQ.mjs");
1047
1070
  const field = strOpt(options, "field");
1048
1071
  if (!local) {
1049
1072
  const r = await clearRecordImage(ctx, recordId, field);
@@ -1331,7 +1354,7 @@ const recordsCommand = {
1331
1354
  const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
1332
1355
  const sorts = parseSorts(multiOpt(options, "sort"));
1333
1356
  return withSession(io, options, {}, async ({ ctx }) => {
1334
- const { listRecords } = await import("./chunks/records-DcHFelq5.mjs");
1357
+ const { listRecords } = await import("./chunks/records-CCTxxMWF.mjs");
1335
1358
  const page = listRecords(ctx, objectId, {
1336
1359
  filters,
1337
1360
  sorts,
@@ -1349,7 +1372,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1349
1372
  case "get": {
1350
1373
  const id = positional(args, 0, "record id", U.get);
1351
1374
  return withSession(io, options, {}, async ({ ctx }) => {
1352
- const { getRecordDetails } = await import("./chunks/records-DcHFelq5.mjs");
1375
+ const { getRecordDetails } = await import("./chunks/records-CCTxxMWF.mjs");
1353
1376
  const d = getRecordDetails(ctx, id);
1354
1377
  return { ...await recordOut(ctx, d.record), relations: d.relations, related: d.inverse.filter((i) => i.records.length), activityCount: d.activityCount };
1355
1378
  });
@@ -1360,7 +1383,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1360
1383
  if (!Object.keys(fields).length) usage("records create needs --fields or --set.", `Usage: agentcrm ${U.create}`);
1361
1384
  const owner = strOpt(options, "owner");
1362
1385
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1363
- const { createRecord } = await import("./chunks/records-DcHFelq5.mjs");
1386
+ const { createRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1364
1387
  return recordOut(ctx, await createRecord(ctx, objectId, fields, owner ? { ownerId: owner } : {}));
1365
1388
  }, (r) => `Created ${r.object} ${r.id} (${String(r.label)})`);
1366
1389
  }
@@ -1370,7 +1393,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1370
1393
  const owner = strOpt(options, "owner");
1371
1394
  if (!Object.keys(fields).length && owner === void 0) usage("records update needs --fields, --set or --owner.");
1372
1395
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1373
- const { updateRecord } = await import("./chunks/records-DcHFelq5.mjs");
1396
+ const { updateRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1374
1397
  return recordOut(ctx, await updateRecord(ctx, id, fields, owner !== void 0 ? { ownerId: owner || null } : {}));
1375
1398
  }, (r) => `Updated ${r.id} (${String(r.label)})`);
1376
1399
  }
@@ -1378,7 +1401,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1378
1401
  if (!args.length) usage("records delete needs at least one id.", `Usage: agentcrm ${U.delete}`);
1379
1402
  const hard = boolOpt(options, "hard");
1380
1403
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1381
- const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-DcHFelq5.mjs");
1404
+ const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-CCTxxMWF.mjs");
1382
1405
  const { recordLabel } = await import("./chunks/fields-B0bitgmr.mjs");
1383
1406
  const { objectOf } = await import("./chunks/store-5gqRSKXg.mjs");
1384
1407
  const targets2 = args.map((id) => getRecord(ctx, id, { includeDeleted: true }));
@@ -1391,7 +1414,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1391
1414
  case "restore": {
1392
1415
  const id = positional(args, 0, "record id", U.restore);
1393
1416
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1394
- const { restoreRecord } = await import("./chunks/records-DcHFelq5.mjs");
1417
+ const { restoreRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1395
1418
  return recordOut(ctx, await restoreRecord(ctx, id));
1396
1419
  });
1397
1420
  }
@@ -1399,7 +1422,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1399
1422
  const query = args.join(" ").trim();
1400
1423
  if (!query) usage("records search needs a query.", `Usage: agentcrm ${U.search}`);
1401
1424
  return withSession(io, options, {}, async ({ ctx }) => {
1402
- const { searchRecords } = await import("./chunks/records-DcHFelq5.mjs");
1425
+ const { searchRecords } = await import("./chunks/records-CCTxxMWF.mjs");
1403
1426
  return searchRecords(ctx, query, { objects: listOpt(options, "object"), limit: Math.min(100, numOpt(options, "limit") ?? 20) });
1404
1427
  });
1405
1428
  }
@@ -1416,7 +1439,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1416
1439
  const [primary, ...dups] = args;
1417
1440
  if (!primary || !dups.length) usage("records merge needs <primaryId> <duplicateId>….", `Usage: agentcrm ${U.merge}`);
1418
1441
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1419
- const { getRecord } = await import("./chunks/records-DcHFelq5.mjs");
1442
+ const { getRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1420
1443
  const all = [primary, ...dups].map((id) => getRecord(ctx, id));
1421
1444
  const plan = { primary: await recordOut(ctx, all[0]), duplicates: await Promise.all(all.slice(1).map((r) => recordOut(ctx, r))) };
1422
1445
  if (options.yes !== true) return { needsConfirmation: true, plan };
@@ -1448,13 +1471,13 @@ const pipelinesCommand = {
1448
1471
  io,
1449
1472
  options,
1450
1473
  {},
1451
- async ({ ctx }) => (await import("./chunks/pipelines-BAKpPk1w.mjs")).listPipelines(ctx, strOpt(options, "object")),
1474
+ async ({ ctx }) => (await import("./chunks/pipelines-DiJ2dLqQ.mjs")).listPipelines(ctx, strOpt(options, "object")),
1452
1475
  (ps) => ps.map((p) => `${p.id} (${p.name}, ${p.object}): ${p.stages.map((s) => `${s.label} ${Math.round(s.probability * (s.probability <= 1 ? 100 : 1))}%`).join(" → ")}`).join("\n")
1453
1476
  );
1454
1477
  }
1455
1478
  if (action === "move") {
1456
1479
  if (!id || !stage.length) usage("pipelines move needs <recordId> <stage>.");
1457
- return withSession(io, options, { lock: true }, async ({ ctx }) => recordOut(ctx, await (await import("./chunks/pipelines-BAKpPk1w.mjs")).moveStage(ctx, id, stage.join(" "), { lostReason: strOpt(options, "lost-reason") })));
1480
+ return withSession(io, options, { lock: true }, async ({ ctx }) => recordOut(ctx, await (await import("./chunks/pipelines-DiJ2dLqQ.mjs")).moveStage(ctx, id, stage.join(" "), { lostReason: strOpt(options, "lost-reason") })));
1458
1481
  }
1459
1482
  usage(`Unknown pipelines action "${action}".`, "Use list or move.");
1460
1483
  }
@@ -1486,7 +1509,7 @@ const viewsCommand = {
1486
1509
  const [action = "list", id] = positionals;
1487
1510
  switch (action) {
1488
1511
  case "list":
1489
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/views-DH1n9Lrt.mjs")).listViews(ctx, strOpt(options, "object")), (vs) => table(vs, ["id", "object", "name", "type", "shared"]));
1512
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/views-BbhmQO3n.mjs")).listViews(ctx, strOpt(options, "object")), (vs) => table(vs, ["id", "object", "name", "type", "shared"]));
1490
1513
  case "create": {
1491
1514
  const type2 = strOpt(options, "type") ?? "table";
1492
1515
  if (type2 !== "table" && type2 !== "kanban") usage("--type must be table or kanban.");
@@ -1501,12 +1524,12 @@ const viewsCommand = {
1501
1524
  ...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
1502
1525
  shared: boolOpt(options, "shared")
1503
1526
  };
1504
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-DH1n9Lrt.mjs")).createView(ctx, input));
1527
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-BbhmQO3n.mjs")).createView(ctx, input));
1505
1528
  }
1506
1529
  case "delete":
1507
1530
  if (!id) usage("views delete needs a view id.");
1508
1531
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1509
- const { getView, deleteView } = await import("./chunks/views-DH1n9Lrt.mjs");
1532
+ const { getView, deleteView } = await import("./chunks/views-BbhmQO3n.mjs");
1510
1533
  const v = getView(ctx, id);
1511
1534
  if (options.yes !== true) return { needsConfirmation: true, plan: { delete: { id: v.id, name: v.name, object: v.object, shared: v.shared } } };
1512
1535
  return deleteView(ctx, id);
@@ -1833,6 +1856,10 @@ async function dryRun(ctx, work) {
1833
1856
  const copy = createOpsContext(store, ctx.user, { now: ctx.now, plugins: createPluginRegistry(() => void 0) });
1834
1857
  return { result: await work(copy), store };
1835
1858
  }
1859
+ const dryRun$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1860
+ __proto__: null,
1861
+ dryRun
1862
+ }, Symbol.toStringTag, { value: "Module" }));
1836
1863
  const CASCADE_OPTION = { name: "cascade", type: "boolean", description: "enable / disable: also enable the dependencies / disable the dependents." };
1837
1864
  function parseJsonValue(raw, name2) {
1838
1865
  const text = readTextValue(raw, name2);
@@ -2097,7 +2124,7 @@ async function check({ io, options, positionals }) {
2097
2124
  const kind = strOpt(options, "kind");
2098
2125
  if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
2099
2126
  const { readRawPackage } = await import("./chunks/extPackage-DuZ6GZhp.mjs");
2100
- const { checkExtension } = await import("./chunks/extCheck-GgHG1dYB.mjs");
2127
+ const { checkExtension } = await import("./chunks/extCheck-BcbUc7eB.mjs");
2101
2128
  const raw = await readRawPackage(target);
2102
2129
  const report = await checkExtension(raw, { kind, callTools: !boolOpt(options, "no-call") });
2103
2130
  printResult(io, report, () => {
@@ -2144,7 +2171,7 @@ async function install({ io, options, positionals }) {
2144
2171
  const apply = options.yes === true;
2145
2172
  const disabled = boolOpt(options, "disabled");
2146
2173
  if (!boolOpt(options, "force")) {
2147
- const { checkExtension } = await import("./chunks/extCheck-GgHG1dYB.mjs");
2174
+ const { checkExtension } = await import("./chunks/extCheck-BcbUc7eB.mjs");
2148
2175
  const report = await checkExtension(raw, { callTools: false });
2149
2176
  if (!report.valid) {
2150
2177
  throw new CliError(
@@ -2180,7 +2207,7 @@ async function install({ io, options, positionals }) {
2180
2207
  }
2181
2208
  const result = await ext.installExtension(s.ctx, pkg2, disabled ? { enabled: false } : {});
2182
2209
  const report = await s.extensions.sync();
2183
- const { getLoadedExtension } = await import("./chunks/registry-COfJrSmg.mjs");
2210
+ const { getLoadedExtension } = await import("./chunks/registry-ClzNZv9Z.mjs");
2184
2211
  const loaded = getLoadedExtension(m.id);
2185
2212
  const failure = report?.failed.find((f) => f.id === m.id || f.id === "*");
2186
2213
  if (failure && !loaded) return { ...result, author: m.author ?? null, load: { status: "error", error: failure.error } };
@@ -2242,7 +2269,7 @@ const extCommand = {
2242
2269
  if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
2243
2270
  return withSession(io, options, {}, async (s) => {
2244
2271
  const { listExtensions } = await import("./chunks/extensions-Bp72uZ9a.mjs");
2245
- const { getLoadedExtension } = await import("./chunks/registry-COfJrSmg.mjs");
2272
+ const { getLoadedExtension } = await import("./chunks/registry-ClzNZv9Z.mjs");
2246
2273
  return listExtensions(s.ctx, kind ? { kind } : {}).map((e) => ({ ...e, load: s.extensions.safeMode ? "skipped" : getLoadedExtension(e.id)?.status ?? (e.enabled ? "not loaded" : "disabled"), ...getLoadedExtension(e.id)?.error ? { loadError: getLoadedExtension(e.id)?.error } : {} }));
2247
2274
  }, (rows) => table(rows.map((r) => ({ ...r, enabled: r.enabled ? "yes" : "no" })), ["id", "kind", "name", "version", "enabled", "load", "author"]));
2248
2275
  }
@@ -2357,7 +2384,7 @@ const importCommand = {
2357
2384
  options,
2358
2385
  {},
2359
2386
  async ({ ctx }) => {
2360
- const plan = await (await import("./chunks/backup-W3CjgkAJ.mjs")).planBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" });
2387
+ const plan = await (await import("./chunks/backup-CO0EzdF_.mjs")).planBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" });
2361
2388
  return { needsConfirmation: true, plan: { restore: resolve(path), bytes: bytes.length, ...plan } };
2362
2389
  },
2363
2390
  (r) => [
@@ -2367,7 +2394,7 @@ const importCommand = {
2367
2394
  ].join("\n")
2368
2395
  );
2369
2396
  }
2370
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/backup-W3CjgkAJ.mjs")).importBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" }), (r) => `Restored ${r.written} document(s) and ${r.files} file(s)`);
2397
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/backup-CO0EzdF_.mjs")).importBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" }), (r) => `Restored ${r.written} document(s) and ${r.files} file(s)`);
2371
2398
  }
2372
2399
  if (kind !== "csv") usage('Only "import csv" and "import zip" are supported.', "Usage: agentcrm import csv <file> --object <id>");
2373
2400
  const source = positional(positionals, 1, "CSV file (or -)", "import csv <file|-> --object <id>");
@@ -2378,7 +2405,7 @@ const importCommand = {
2378
2405
  const apply = options.yes === true && options["dry-run"] !== true;
2379
2406
  const shown = numOpt(options, "rows") ?? 100;
2380
2407
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2381
- const { importCsv } = await import("./chunks/importCsv-LVmMTaWU.mjs");
2408
+ const { importCsv } = await import("./chunks/importCsv-XjQJ9QIR.mjs");
2382
2409
  const report = await importCsv(ctx, {
2383
2410
  object: objectId,
2384
2411
  csv,
@@ -2427,7 +2454,7 @@ const exportCommand = {
2427
2454
  const file2 = strOpt(options, "out");
2428
2455
  if (!file2) usage("export zip needs --out <file.zip>.");
2429
2456
  return withSession(io, options, {}, async ({ ctx }) => {
2430
- const { exportBackupZip } = await import("./chunks/backup-W3CjgkAJ.mjs");
2457
+ const { exportBackupZip } = await import("./chunks/backup-CO0EzdF_.mjs");
2431
2458
  const { zip, report } = await exportBackupZip(ctx, { includeFiles: options["no-files"] !== true });
2432
2459
  writeOutFile(file2, zip);
2433
2460
  return { file: resolve(file2), zipBytes: zip.length, files: report.files, missing: report.missing, fileBytes: report.bytes };
@@ -2446,7 +2473,7 @@ const exportCommand = {
2446
2473
  const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
2447
2474
  const sorts = parseSorts(multiOpt(options, "sort"));
2448
2475
  return withSession(io, options, {}, async ({ ctx }) => {
2449
- const { exportCsv } = await import("./chunks/exportCsv-C6e6XEmB.mjs");
2476
+ const { exportCsv } = await import("./chunks/exportCsv-C-fIHLm1.mjs");
2450
2477
  const csv = exportCsv(ctx, objectId, {
2451
2478
  viewId: strOpt(options, "view"),
2452
2479
  filters,
@@ -2462,7 +2489,7 @@ const exportCommand = {
2462
2489
  }
2463
2490
  if (kind === "json") {
2464
2491
  return withSession(io, options, {}, async ({ ctx }) => {
2465
- const { exportJson } = await import("./chunks/exportCsv-C6e6XEmB.mjs");
2492
+ const { exportJson } = await import("./chunks/exportCsv-C-fIHLm1.mjs");
2466
2493
  const data = exportJson(ctx, { collections: listOpt(options, "collections") });
2467
2494
  const text = JSON.stringify(data, null, 2);
2468
2495
  if (out) return deliver(text, {});
@@ -2506,7 +2533,7 @@ const reportCommand = {
2506
2533
  ...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
2507
2534
  ...strOpt(options, "owner") ? { ownerId: strOpt(options, "owner") } : {}
2508
2535
  };
2509
- return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-BACHGccj.mjs")).runReport(ctx, internal, params) }));
2536
+ return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-WlI_A5Nr.mjs")).runReport(ctx, internal, params) }));
2510
2537
  }
2511
2538
  };
2512
2539
  const statsCommand = {
@@ -2516,7 +2543,7 @@ const statsCommand = {
2516
2543
  options: [SITE_OPTION, AS_OPTION],
2517
2544
  async run({ io, options }) {
2518
2545
  return withSession(io, options, {}, async ({ ctx, store }) => {
2519
- const stats = (await import("./chunks/stats-BfuLWhVV.mjs")).getStats(ctx);
2546
+ const stats = (await import("./chunks/stats-DfmDP3tE.mjs")).getStats(ctx);
2520
2547
  const journal = store.stats;
2521
2548
  return { ...stats, ...journal ? { journal: await journal.call(store) } : {} };
2522
2549
  }, (s) => [`backend: ${s.backend}`, ...s.objects.map((o) => ` ${o.label}: ${o.count}${o.deleted ? ` (+${o.deleted} deleted)` : ""}`), `open tasks: ${s.openTasks} (${s.overdueTasks} overdue)`, `users: ${s.users}`].join("\n"));
@@ -2530,7 +2557,7 @@ const compactCommand = {
2530
2557
  async run({ io, options }) {
2531
2558
  const apply = options.yes === true;
2532
2559
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2533
- const { compactData } = await import("./chunks/compact-BGHI92tm.mjs");
2560
+ const { compactData } = await import("./chunks/compact-qF5t42ko.mjs");
2534
2561
  const report = await compactData(ctx, { olderThanDays: numOpt(options, "older-than"), keepRuns: numOpt(options, "keep-runs"), dryRun: !apply });
2535
2562
  return apply ? report : { needsConfirmation: true, plan: report };
2536
2563
  }, (r) => "plan" in r ? `Would purge ${r.plan.purgedRecords.length} record(s), ${r.plan.purgedActivities} activit(ies), ${r.plan.purgedRuns} run(s), ${r.plan.purgedFiles} file(s). Re-run with --yes once the user agrees.` : `Purged ${r.purgedRecords.length} record(s), ${r.purgedFiles} file(s); storage compacted: ${r.storageCompacted}`);
@@ -2551,19 +2578,19 @@ const automationsCommand = {
2551
2578
  const [action = "list", id] = positionals;
2552
2579
  switch (action) {
2553
2580
  case "list":
2554
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/automations-DwWruxmu.mjs")).listAutomations(ctx).map((a) => ({ id: a.id, name: a.name, enabled: a.enabled, object: a.object, trigger: a.trigger, actions: a.actions.map((x) => x.type), lastRunAt: a.lastRunAt ? new Date(a.lastRunAt).toISOString() : null })), (rows) => table(rows, ["id", "name", "enabled", "object", "lastRunAt"]));
2581
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/automations-BqY8EwfJ.mjs")).listAutomations(ctx).map((a) => ({ id: a.id, name: a.name, enabled: a.enabled, object: a.object, trigger: a.trigger, actions: a.actions.map((x) => x.type), lastRunAt: a.lastRunAt ? new Date(a.lastRunAt).toISOString() : null })), (rows) => table(rows, ["id", "name", "enabled", "object", "lastRunAt"]));
2555
2582
  case "enable":
2556
2583
  case "disable": {
2557
2584
  if (!id) usage(`automations ${action} needs an id.`);
2558
2585
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
2559
- const mod = await import("./chunks/automations-DwWruxmu.mjs");
2586
+ const mod = await import("./chunks/automations-BqY8EwfJ.mjs");
2560
2587
  return mod.setAutomationEnabled(ctx, id, action === "enable");
2561
2588
  });
2562
2589
  }
2563
2590
  case "run": {
2564
2591
  const apply = options.yes === true;
2565
2592
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2566
- const { runScheduled } = await import("./chunks/automations-DwWruxmu.mjs");
2593
+ const { runScheduled } = await import("./chunks/automations-BqY8EwfJ.mjs");
2567
2594
  const report = await runScheduled(ctx, { dryRun: !apply, automationId: strOpt(options, "id"), force: boolOpt(options, "force") });
2568
2595
  return apply ? report : { needsConfirmation: true, ...report };
2569
2596
  }, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}ran ${r.ran}, skipped ${r.skipped}, errors ${r.errors}`);
@@ -2587,11 +2614,11 @@ const formsCommand = {
2587
2614
  const [action = "list", id] = positionals;
2588
2615
  switch (action) {
2589
2616
  case "list":
2590
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-BfuWaQjA.mjs")).listForms(ctx), (fs) => table(fs, ["id", "name", "object", "publishedPath"]));
2617
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-DJDop3XY.mjs")).listForms(ctx), (fs) => table(fs, ["id", "name", "object", "publishedPath"]));
2591
2618
  case "publish": {
2592
2619
  if (!id) usage("forms publish needs a form id.");
2593
2620
  return withSession(io, options, { lock: options.yes === true }, async (s) => {
2594
- const forms = await import("./chunks/forms-BfuWaQjA.mjs");
2621
+ const forms = await import("./chunks/forms-DJDop3XY.mjs");
2595
2622
  const built = await forms.buildFormHtml(s.ctx, id);
2596
2623
  const target = s.hostingTarget();
2597
2624
  if (!target) usage(`${s.site.alias} has no hosting to publish the form to.`);
@@ -2605,7 +2632,7 @@ const formsCommand = {
2605
2632
  case "ingest": {
2606
2633
  const apply = options.yes === true;
2607
2634
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2608
- const { ingestInbox } = await import("./chunks/forms-BfuWaQjA.mjs");
2635
+ const { ingestInbox } = await import("./chunks/forms-DJDop3XY.mjs");
2609
2636
  const report = await ingestInbox(ctx, strOpt(options, "form"), { dryRun: !apply, limit: numOpt(options, "limit") });
2610
2637
  return apply ? report : { needsConfirmation: true, ...report };
2611
2638
  }, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}processed ${r.processed}: ${r.created} created, ${r.updated} updated, ${r.failed} failed`);
@@ -2845,7 +2872,7 @@ async function runDoctor(site, opts = {}) {
2845
2872
  installBrowserEnv();
2846
2873
  try {
2847
2874
  if (site.runtime.backend === "journal" && site.runtime.journal.connectorId === "git-pages") {
2848
- const { probeTarget } = await import("./chunks/install-BLNflFes.mjs");
2875
+ const { probeTarget } = await import("./chunks/install-D1lgtRIH.mjs");
2849
2876
  const { dataTargetOf: dataTargetOf2 } = await Promise.resolve().then(() => session);
2850
2877
  const probe = await probeTarget(dataTargetOf2(site), { requireSiteUrl: false, requirePrivate: true });
2851
2878
  add(probe.ok ? { id: "data-repo", status: "ok", message: probe.detail } : { id: "data-repo", status: "fail", message: probe.detail, fix: "Make the data repository private and check the token's Contents permission." });
@@ -2871,7 +2898,7 @@ async function runDoctor(site, opts = {}) {
2871
2898
  if (!settingsDoc(store)) missing.push("settings");
2872
2899
  if (!usersOf(store).some((u) => u.role === "admin" && !u.disabled)) missing.push("an active admin user");
2873
2900
  if (missing.length && opts.repair) {
2874
- const { seedCrm } = await import("./chunks/seed-6MRmQQBR.mjs");
2901
+ const { seedCrm } = await import("./chunks/seed-Bb26Zsni.mjs");
2875
2902
  repaired = await seedCrm(session$1.system, { admin: site.user ? { name: site.user.split("@")[0], email: site.user } : void 0 });
2876
2903
  add({ id: "dataset", status: "ok", message: `Repaired: seeded ${missing.join(", ")}.` });
2877
2904
  } else {
@@ -3014,7 +3041,7 @@ const installCommand = {
3014
3041
  const { resolvePlan } = await import("./chunks/plan-BmTat6-T.mjs");
3015
3042
  const plan = resolvePlan(readPlan(planPath));
3016
3043
  if (getSite(plan.alias)) throw new CliError("site-exists", `A site named "${plan.alias}" is already registered.`, "Use another alias, or `agentcrm site forget` it first.");
3017
- const { checkPlan, runInstall } = await import("./chunks/install-BLNflFes.mjs");
3044
+ const { checkPlan, runInstall } = await import("./chunks/install-D1lgtRIH.mjs");
3018
3045
  io.err("Checking access…");
3019
3046
  const checks = await checkPlan(plan);
3020
3047
  const summary = {
@@ -3448,13 +3475,13 @@ const objectsCommand = {
3448
3475
  switch (action) {
3449
3476
  case "list":
3450
3477
  return withSession(io, options, {}, async ({ ctx, store }) => {
3451
- const { listObjects } = await import("./chunks/schema-C4lSslXv.mjs");
3478
+ const { listObjects } = await import("./chunks/schema-DOnnH2zL.mjs");
3452
3479
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3453
3480
  return listObjects(ctx).map((o) => ({ id: o.id, labelSingular: o.labelSingular, labelPlural: o.labelPlural, standard: o.standard, labelField: o.labelField, fields: o.fields.length, records: recordsOf(store, o.id).length }));
3454
3481
  }, (rows) => table(rows, ["id", "labelPlural", "fields", "records", "standard"]));
3455
3482
  case "get": {
3456
3483
  const objectId = positional(positionals, 1, "object id", "objects get <id>");
3457
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).describeSchema(ctx, objectId)[0]);
3484
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).describeSchema(ctx, objectId)[0]);
3458
3485
  }
3459
3486
  case "create": {
3460
3487
  const data = jsonObjectOpt(options, "data") ?? {};
@@ -3467,7 +3494,7 @@ const objectsCommand = {
3467
3494
  ...strOpt(options, "color") ? { color: strOpt(options, "color") } : {}
3468
3495
  };
3469
3496
  if (!input.labelSingular || !input.labelPlural) usage("objects create needs --singular and --plural (or --data).");
3470
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).createObject(ctx, input));
3497
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).createObject(ctx, input));
3471
3498
  }
3472
3499
  case "update": {
3473
3500
  const objectId = positional(positionals, 1, "object id", "objects update <id> …");
@@ -3479,12 +3506,12 @@ const objectsCommand = {
3479
3506
  ...strOpt(options, "label-field") ? { labelField: strOpt(options, "label-field") } : {}
3480
3507
  };
3481
3508
  if (!Object.keys(patch).length) usage("objects update needs --singular, --plural, --icon, --color or --label-field.");
3482
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).updateObject(ctx, objectId, patch));
3509
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).updateObject(ctx, objectId, patch));
3483
3510
  }
3484
3511
  case "delete": {
3485
3512
  if (!id) usage("objects delete needs an object id.");
3486
3513
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
3487
- const { getObject, deleteObject } = await import("./chunks/schema-C4lSslXv.mjs");
3514
+ const { getObject, deleteObject } = await import("./chunks/schema-DOnnH2zL.mjs");
3488
3515
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3489
3516
  const o = getObject(ctx, id);
3490
3517
  const plan = { object: o.id, label: o.labelPlural, standard: o.standard, recordsDeleted: recordsOf(store, o.id, { includeDeleted: true }).length };
@@ -3542,7 +3569,7 @@ const fieldsCommand = {
3542
3569
  ...boolOpt(options, "unique") ? { unique: true } : {},
3543
3570
  ...strOpt(options, "description") ? { description: strOpt(options, "description") } : {}
3544
3571
  };
3545
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).createField(ctx, objectId, input));
3572
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).createField(ctx, objectId, input));
3546
3573
  }
3547
3574
  case "update": {
3548
3575
  if (!fieldId) usage("fields update needs a field id.");
@@ -3559,12 +3586,12 @@ const fieldsCommand = {
3559
3586
  ...strOpt(options, "description") !== void 0 ? { description: strOpt(options, "description") } : {}
3560
3587
  };
3561
3588
  if (!Object.keys(patch).length) usage("fields update needs at least one change.");
3562
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-C4lSslXv.mjs")).updateField(ctx, objectId, fieldId, patch));
3589
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).updateField(ctx, objectId, fieldId, patch));
3563
3590
  }
3564
3591
  case "delete": {
3565
3592
  if (!fieldId) usage("fields delete needs a field id.");
3566
3593
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
3567
- const { getObject, deleteField } = await import("./chunks/schema-C4lSslXv.mjs");
3594
+ const { getObject, deleteField } = await import("./chunks/schema-DOnnH2zL.mjs");
3568
3595
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3569
3596
  const field = getObject(ctx, objectId).fields.find((f) => f.id === fieldId);
3570
3597
  if (!field) usage(`No field "${fieldId}" on ${objectId}.`, `\`agentcrm objects get ${objectId}\` lists them.`);
@@ -3578,6 +3605,50 @@ const fieldsCommand = {
3578
3605
  }
3579
3606
  }
3580
3607
  };
3608
+ const schemaCommand = {
3609
+ name: "schema",
3610
+ usage: "schema apply <file|-> [--yes]",
3611
+ summary: "Bring the data model in line with a JSON description ({ objects: [{ id, labelSingular, labelPlural, icon, color, labelField, fields: [{ id, label, type, options, relation… }] }], pipelines: [{ id, name, object, stages }], views: [{ object, name, columns, filters, sorts, groupBy, shared }] }). Creates what is missing and updates what differs; never deletes, never changes a field type (reported as a conflict), never removes a select option or a stage; reports custom fields the description doesn't list. Prints the plan unless --yes.",
3612
+ subcommands: [{ name: "apply", usage: "schema apply <file|-> [--yes]", summary: "Apply a schema description (plan unless --yes).", writes: true }],
3613
+ options: [SITE_OPTION, AS_OPTION, YES_OPTION],
3614
+ async run({ io, options, positionals }) {
3615
+ const [action, source] = positionals;
3616
+ if (action !== "apply") usage(`Unknown schema action "${action ?? ""}".`, "Use schema apply <file|-> [--yes].");
3617
+ if (!source) usage("schema apply needs a file (or - for stdin).", "Example: agentcrm schema apply crm-schema.json");
3618
+ const { readTextValue: readTextValue2 } = await Promise.resolve().then(() => util);
3619
+ const text = readTextValue2(source === "-" ? "-" : `@${source}`, "schema");
3620
+ let raw;
3621
+ try {
3622
+ raw = JSON.parse(text);
3623
+ } catch (err) {
3624
+ usage(`The schema is not valid JSON: ${err.message}`);
3625
+ }
3626
+ const apply = options.yes === true;
3627
+ const { applySchema, parseSchemaSpec } = await import("./chunks/schemaApply-DKHipylx.mjs");
3628
+ const spec = parseSchemaSpec(raw);
3629
+ return withSession(
3630
+ io,
3631
+ options,
3632
+ { lock: apply },
3633
+ async ({ ctx }) => {
3634
+ if (apply) return applySchema(ctx, spec);
3635
+ const { dryRun: dryRun2 } = await Promise.resolve().then(() => dryRun$1);
3636
+ const { result } = await dryRun2(ctx, (copy) => applySchema(copy, spec));
3637
+ return result.changes.length ? { needsConfirmation: true, plan: result } : { ...result, upToDate: true };
3638
+ },
3639
+ (r) => {
3640
+ const report = "needsConfirmation" in r ? r.plan : r;
3641
+ const lines = report.changes.map((c) => ` ${c.action} ${c.kind} ${c.target}${c.changes?.length ? ` (${c.changes.join(", ")})` : ""}`);
3642
+ const extra = [
3643
+ ...report.conflicts.map((c) => ` conflict: ${c}`),
3644
+ ...report.extraFields.length ? [` not in the schema (kept): ${report.extraFields.join(", ")}`] : []
3645
+ ];
3646
+ if ("needsConfirmation" in r) return [`Would change ${report.changes.length} thing(s):`, ...lines, ...extra, "Nothing changed — re-run with --yes once the user agrees."].join("\n");
3647
+ return [report.changes.length ? `Applied ${report.changes.length} change(s):` : "The data model already matches the schema.", ...lines, ...extra].join("\n");
3648
+ }
3649
+ );
3650
+ }
3651
+ };
3581
3652
  const MIME = {
3582
3653
  ".html": "text/html; charset=utf-8",
3583
3654
  ".js": "text/javascript; charset=utf-8",
@@ -3871,7 +3942,7 @@ const toolsCommand = {
3871
3942
  async run({ io, options, positionals }) {
3872
3943
  const [action = "list"] = positionals;
3873
3944
  if (action === "list") {
3874
- const { listAiTools, AI_TOOLS } = await import("./chunks/tools-iSH-yMpl.mjs");
3945
+ const { listAiTools, AI_TOOLS } = await import("./chunks/tools-CCk24tdh.mjs");
3875
3946
  const builtin = new Set(AI_TOOLS.map((t) => t.name));
3876
3947
  const shape = () => listAiTools().map((t) => ({ name: t.name, description: t.description, mutating: t.mutating, source: builtin.has(t.name) ? "builtin" : "extension", inputSchema: t.inputSchema }));
3877
3948
  const human = (r) => table(r.tools, ["name", "mutating", "source", "description"]);
@@ -3891,7 +3962,7 @@ const toolsCommand = {
3891
3962
  }
3892
3963
  if (action === "call") {
3893
3964
  const name2 = positional(positionals, 1, "tool name", "tools call <name> --input <json>");
3894
- const { findTool, callTool, listAiTools } = await import("./chunks/tools-iSH-yMpl.mjs");
3965
+ const { findTool, callTool, listAiTools } = await import("./chunks/tools-CCk24tdh.mjs");
3895
3966
  const input = jsonObjectOpt(options, "input") ?? {};
3896
3967
  const builtin = findTool(name2);
3897
3968
  return withSession(io, options, { lock: builtin ? builtin.mutating : true }, async ({ ctx }) => {
@@ -3913,8 +3984,8 @@ const mcpCommand = {
3913
3984
  const log = (msg) => process.stderr.write(`${msg}
3914
3985
  `);
3915
3986
  for (const w of session2.warnings) log(`[agentcrm mcp] note: ${w}`);
3916
- const { listAiTools } = await import("./chunks/tools-iSH-yMpl.mjs");
3917
- const { createMcpServer, serveStdio } = await import("./chunks/server-DC3t8r30.mjs");
3987
+ const { listAiTools } = await import("./chunks/tools-CCk24tdh.mjs");
3988
+ const { createMcpServer, serveStdio } = await import("./chunks/server-Bch4TQon.mjs");
3918
3989
  let lastRefresh = Date.now();
3919
3990
  const server = createMcpServer({
3920
3991
  readOnly,
@@ -4016,6 +4087,7 @@ const COMMANDS = [
4016
4087
  deployAdminCommand,
4017
4088
  recordsCommand,
4018
4089
  objectsCommand,
4090
+ schemaCommand,
4019
4091
  fieldsCommand,
4020
4092
  activitiesCommand,
4021
4093
  notesCommand,
@@ -8,16 +8,16 @@ import { toIsoDate, recordLabel, formatFieldValue, normalizeEmail } from "./fiel
8
8
  import { n as newId } from "./ids-CvnmrMJX.mjs";
9
9
  import { plugins } from "./pluginRegistry-DyHtlkcr.mjs";
10
10
  import { r as resolveField, m as matchesFilter } from "./query-C5nJB_3C.mjs";
11
- import { queryContext, setRecordOwner, updateRecord } from "./records-DcHFelq5.mjs";
11
+ import { queryContext, setRecordOwner, updateRecord } from "./records-CCTxxMWF.mjs";
12
12
  import { getSettings } from "./settings-Di8nzNEL.mjs";
13
13
  import { userOf, automationRunsOf, asDoc, requireRecord, requireObject, automationsOf, objectOf, recordsOf, thaw, recordOf, usersOf } from "./store-5gqRSKXg.mjs";
14
14
  import { F as FILTER_OPS } from "../agentcrm.mjs";
15
15
  import { lastActivityAt, addNote, createTask } from "./activities-Cj_SIOay.mjs";
16
16
  import { OpsError, assertCan } from "./context-CxFfqS4D.mjs";
17
- import { pipelineOfRecord } from "./pipelines-BAKpPk1w.mjs";
17
+ import { pipelineOfRecord } from "./pipelines-DiJ2dLqQ.mjs";
18
18
  import { getAutomationAction, listAutomationActions } from "./extensionActions-D2HtpQz4.mjs";
19
19
  import { registerAutomationAction, subscribeAutomationActions } from "./extensionActions-D2HtpQz4.mjs";
20
- import "./standardObjects-CheqfDqa.mjs";
20
+ import "./standardObjects-vrmqIVLH.mjs";
21
21
  import "./relations-B9TynBmA.mjs";
22
22
  import "./runtimeConfig-CCOFg1RM.mjs";
23
23
  import "node:util";