@fleox-agent-crm/agentcrm 0.5.0 → 0.7.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 (53) hide show
  1. package/README.md +16 -16
  2. package/changelog.json +18 -0
  3. package/dist/admin/admin-files.json +7 -7
  4. package/dist/admin/assets/{chartsImpl-zjsXDhuy.js → chartsImpl-Cx0bQmYg.js} +1 -1
  5. package/dist/admin/assets/{firebase-C3h9mF8O.js → firebase-DyAT9e1o.js} +2 -2
  6. package/dist/admin/assets/{gitJson-nrOYgX0x.js → gitJson-BeyPO6eJ.js} +1 -1
  7. package/dist/admin/assets/{index-0ejjkW0z.js → index-Bp5AxImK.js} +172 -172
  8. package/dist/admin/assets/{lucide-react.min-CI5dCsSm.js → lucide-react.min-BXJHmaO2.js} +1 -1
  9. package/dist/admin/assets/{sdk-mepKXtVl.js → sdk-hs3heWPk.js} +1 -1
  10. package/dist/admin/index.html +1 -1
  11. package/dist/agent/agentcrm.mjs +113 -73
  12. package/dist/agent/chunks/{activities-Cj_SIOay.mjs → activities-be260oPg.mjs} +1 -1
  13. package/dist/agent/chunks/{automations-BqY8EwfJ.mjs → automations-gCDYNS88.mjs} +4 -4
  14. package/dist/agent/chunks/{backup-CO0EzdF_.mjs → backup-DDqTRbuW.mjs} +3 -3
  15. package/dist/agent/chunks/{compact-qF5t42ko.mjs → compact-BqTG5cvC.mjs} +4 -4
  16. package/dist/agent/chunks/{dedupe-BepmsaHv.mjs → dedupe-isAmKHQV.mjs} +1 -1
  17. package/dist/agent/chunks/{exportCsv-C-fIHLm1.mjs → exportCsv-9ZMyqTdH.mjs} +2 -2
  18. package/dist/agent/chunks/{extCheck-BcbUc7eB.mjs → extCheck-Bu8C87OV.mjs} +3 -3
  19. package/dist/agent/chunks/{files-C4YONVqQ.mjs → files-SMLUpCPs.mjs} +10 -6
  20. package/dist/agent/chunks/{forms-DJDop3XY.mjs → forms-Bb0zztBM.mjs} +3 -3
  21. package/dist/agent/chunks/{importCsv-XjQJ9QIR.mjs → importCsv-BuSYG4ja.mjs} +2 -2
  22. package/dist/agent/chunks/{loader-Cv0UJf6b.mjs → loader-CGIek5YG.mjs} +13 -13
  23. package/dist/agent/chunks/{pipelines-DiJ2dLqQ.mjs → pipelines-G-c23x-s.mjs} +2 -2
  24. package/dist/agent/chunks/{records-CCTxxMWF.mjs → records-BJLSmv0O.mjs} +1 -1
  25. package/dist/agent/chunks/{registry-ClzNZv9Z.mjs → registry-Bg8kGteS.mjs} +12 -12
  26. package/dist/agent/chunks/{relations-B9TynBmA.mjs → relations-bmUKPT0G.mjs} +4 -4
  27. package/dist/agent/chunks/{reports-WlI_A5Nr.mjs → reports-ogUa41jr.mjs} +4 -4
  28. package/dist/agent/chunks/{runtime-BAF5vU5D.mjs → runtime-Bmwgx2hK.mjs} +23 -20
  29. package/dist/agent/chunks/{schema-DOnnH2zL.mjs → schema-Cwi7iFp2.mjs} +4 -4
  30. package/dist/agent/chunks/{schemaApply-DKHipylx.mjs → schemaApply-tEIr5lFK.mjs} +15 -6
  31. package/dist/agent/chunks/{server-Bch4TQon.mjs → server-3awvGTvL.mjs} +3 -3
  32. package/dist/agent/chunks/{stats-DfmDP3tE.mjs → stats-bInarNLK.mjs} +3 -3
  33. package/dist/agent/chunks/syncRecords-69hcCgOK.mjs +102 -0
  34. package/dist/agent/chunks/{tools-CCk24tdh.mjs → tools-BR3X1Uvm.mjs} +11 -11
  35. package/dist/agent/chunks/{views-BbhmQO3n.mjs → views-zRdsphZl.mjs} +2 -2
  36. package/dist/version.json +1 -1
  37. package/package.json +1 -1
  38. package/plugin/.claude-plugin/plugin.json +1 -1
  39. package/plugin/skills/agentcrm-admin/SKILL.md +67 -67
  40. package/plugin/skills/agentcrm-admin/references/records.md +5 -5
  41. package/plugin/skills/agentcrm-admin/references/schema.md +19 -19
  42. package/plugin/skills/agentcrm-extensions/SKILL.md +8 -8
  43. package/plugin/skills/agentcrm-extensions/references/module-recipes.md +2 -2
  44. package/plugin/skills/agentcrm-extensions/references/testing.md +14 -14
  45. package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +3 -3
  46. package/plugin/skills/agentcrm-install/SKILL.md +13 -13
  47. package/plugin/skills/agentcrm-install/references/firebase.md +1 -1
  48. package/plugin/skills/agentcrm-install/references/github.md +1 -1
  49. package/plugin/skills/agentcrm-install/references/mcp.md +2 -2
  50. package/plugin/skills/agentcrm-install/references/plans.md +1 -1
  51. package/plugin/skills/agentcrm-install/references/s3.md +1 -1
  52. package/plugin/skills/agentcrm-manage/SKILL.md +15 -15
  53. 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.5.0";
145
+ const version = "0.7.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-BAF5vU5D.mjs");
527
+ await import("./chunks/runtime-Bmwgx2hK.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-Cv0UJf6b.mjs");
542
+ const { syncExtensions } = await import("./chunks/loader-CGIek5YG.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-ClzNZv9Z.mjs")).deactivateAllExtensions();
556
+ (await import("./chunks/registry-Bg8kGteS.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-BqY8EwfJ.mjs")
684
+ import("./chunks/automations-gCDYNS88.mjs")
685
685
  ]);
686
686
  const own = createPluginRegistry((hook, err) => process.stderr.write(`[agentcrm] ${hook} handler failed: ${String(err?.message ?? err)}
687
687
  `));
@@ -714,7 +714,7 @@ async function openSession(alias, opts = {}) {
714
714
  }
715
715
  }
716
716
  {
717
- const forms = await import("./chunks/forms-DJDop3XY.mjs");
717
+ const forms = await import("./chunks/forms-Bb0zztBM.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;
@@ -959,7 +959,7 @@ function readLocalFile(path) {
959
959
  return { name: basename(full), bytes: new Uint8Array(readFileSync(full)) };
960
960
  }
961
961
  async function uploadLocalFiles(ctx, paths, targets2 = []) {
962
- const { uploadFile } = await import("./chunks/files-C4YONVqQ.mjs");
962
+ const { uploadFile } = await import("./chunks/files-SMLUpCPs.mjs");
963
963
  const { guessMime, imageDimensions } = await import("./chunks/files-CjsPJSR6.mjs");
964
964
  const out = [];
965
965
  for (const p of paths) {
@@ -1007,7 +1007,7 @@ const filesCommand = {
1007
1007
  switch (action) {
1008
1008
  case "list":
1009
1009
  return withSession(io, options, {}, async ({ ctx }) => {
1010
- const { listFiles } = await import("./chunks/files-C4YONVqQ.mjs");
1010
+ const { listFiles } = await import("./chunks/files-SMLUpCPs.mjs");
1011
1011
  const target = strOpt(options, "record") ?? strOpt(options, "activity") ?? null;
1012
1012
  const items = listFiles(ctx, target, { includeRelated: boolOpt(options, "related"), kind: strOpt(options, "kind"), search: strOpt(options, "search") });
1013
1013
  const limit = Math.min(1e3, numOpt(options, "limit") ?? 100);
@@ -1022,7 +1022,7 @@ ${table(r.files, ["id", "name", "mime", "size", "createdAt"])}`);
1022
1022
  if (record && activity) usage("Give --record or --activity, not both.");
1023
1023
  for (const p of args) readLocalFile(p);
1024
1024
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1025
- const { attachFile } = await import("./chunks/files-C4YONVqQ.mjs");
1025
+ const { attachFile } = await import("./chunks/files-SMLUpCPs.mjs");
1026
1026
  const files2 = await uploadLocalFiles(ctx, args, record ? [record] : []);
1027
1027
  if (activity) for (const f of files2) await attachFile(ctx, f.id, { kind: "activity", id: activity });
1028
1028
  const { requireFile } = await import("./chunks/store-5gqRSKXg.mjs");
@@ -1035,7 +1035,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1035
1035
  const out = strOpt(options, "out");
1036
1036
  if (!out) usage("files get needs --out <path>.");
1037
1037
  return withSession(io, options, {}, async ({ ctx }) => {
1038
- const { getFile, getFileBytes } = await import("./chunks/files-C4YONVqQ.mjs");
1038
+ const { getFile, getFileBytes } = await import("./chunks/files-SMLUpCPs.mjs");
1039
1039
  const meta = getFile(ctx, id);
1040
1040
  const bytes = await getFileBytes(ctx, id, { thumb: boolOpt(options, "thumb") });
1041
1041
  writeOutFile(out, bytes);
@@ -1045,7 +1045,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1045
1045
  case "delete": {
1046
1046
  const id = positional(args, 0, "file id", U$1.delete);
1047
1047
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1048
- const { getFile, deleteFile } = await import("./chunks/files-C4YONVqQ.mjs");
1048
+ const { getFile, deleteFile } = await import("./chunks/files-SMLUpCPs.mjs");
1049
1049
  const meta = fileOut(getFile(ctx, id));
1050
1050
  if (options.yes !== true) return { needsConfirmation: true, plan: { delete: meta } };
1051
1051
  await deleteFile(ctx, id);
@@ -1053,7 +1053,7 @@ ${table(r.uploaded, ["id", "name", "size"])}`);
1053
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\`).`);
1054
1054
  }
1055
1055
  case "usage":
1056
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-C4YONVqQ.mjs")).fileUsage(ctx));
1056
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/files-SMLUpCPs.mjs")).fileUsage(ctx));
1057
1057
  default:
1058
1058
  usage(`Unknown files action "${action ?? ""}".`, "Use list, upload, get, delete or usage.");
1059
1059
  }
@@ -1066,7 +1066,7 @@ async function recordImageAction(io, options, args) {
1066
1066
  if (!remove && !path) usage("records image needs an image path (or --remove).");
1067
1067
  const local = remove ? null : readLocalFile(path);
1068
1068
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1069
- const { setRecordImage, clearRecordImage } = await import("./chunks/files-C4YONVqQ.mjs");
1069
+ const { setRecordImage, clearRecordImage } = await import("./chunks/files-SMLUpCPs.mjs");
1070
1070
  const field = strOpt(options, "field");
1071
1071
  if (!local) {
1072
1072
  const r = await clearRecordImage(ctx, recordId, field);
@@ -1160,7 +1160,7 @@ const activitiesCommand = {
1160
1160
  const recs = multiOpt(options, "record");
1161
1161
  if (!recs.length && type2 !== "task") usage("activities add needs --record <id> (repeatable).");
1162
1162
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1163
- const { addActivity } = await import("./chunks/activities-Cj_SIOay.mjs");
1163
+ const { addActivity } = await import("./chunks/activities-be260oPg.mjs");
1164
1164
  return activityOut(
1165
1165
  await addActivity(ctx, {
1166
1166
  type: type2,
@@ -1177,7 +1177,7 @@ const activitiesCommand = {
1177
1177
  }
1178
1178
  case "list":
1179
1179
  return withSession(io, options, {}, async ({ ctx }) => {
1180
- const { listActivities } = await import("./chunks/activities-Cj_SIOay.mjs");
1180
+ const { listActivities } = await import("./chunks/activities-be260oPg.mjs");
1181
1181
  const types = listOpt(options, "type");
1182
1182
  const page = listActivities(ctx, { target: multiOpt(options, "record")[0], types, includeRelated: boolOpt(options, "related"), limit: Math.min(500, numOpt(options, "limit") ?? 50) });
1183
1183
  return { total: page.total, activities: page.items.map(activityOut) };
@@ -1185,7 +1185,7 @@ const activitiesCommand = {
1185
1185
  ${table(r.activities, ["id", "type", "title", "createdAt"])}`);
1186
1186
  case "done": {
1187
1187
  if (!id) usage("activities done needs an id.");
1188
- return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-Cj_SIOay.mjs")).completeTask(ctx, id, !boolOpt(options, "undo"))));
1188
+ return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-be260oPg.mjs")).completeTask(ctx, id, !boolOpt(options, "undo"))));
1189
1189
  }
1190
1190
  default:
1191
1191
  usage(`Unknown activities action "${action ?? ""}".`, "Use add, list or done.");
@@ -1206,7 +1206,7 @@ const notesCommand = {
1206
1206
  if (!text?.trim()) usage("notes add needs --body.");
1207
1207
  const files2 = attachPaths(options);
1208
1208
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1209
- const ops = await import("./chunks/activities-Cj_SIOay.mjs");
1209
+ const ops = await import("./chunks/activities-be260oPg.mjs");
1210
1210
  const { recordOf } = await import("./chunks/store-5gqRSKXg.mjs");
1211
1211
  const r = recordOf(ctx.store, id);
1212
1212
  if (!files2.length || !r) return activityOut(await ops.addNote(ctx, id, text, strOpt(options, "title")));
@@ -1249,7 +1249,7 @@ const tasksCommand = {
1249
1249
  switch (action) {
1250
1250
  case "list":
1251
1251
  return withSession(io, options, {}, async ({ ctx }) => {
1252
- const { listTasks } = await import("./chunks/activities-Cj_SIOay.mjs");
1252
+ const { listTasks } = await import("./chunks/activities-be260oPg.mjs");
1253
1253
  const status = strOpt(options, "status") ?? "open";
1254
1254
  if (!["open", "done", "all"].includes(status)) usage("--status must be open, done or all.");
1255
1255
  const page = listTasks(ctx, { assigneeId: await assignee(ctx), target: multiOpt(options, "record")[0], status, overdue: boolOpt(options, "overdue"), dueWithinDays: numOpt(options, "due-within"), limit: Math.min(500, numOpt(options, "limit") ?? 50) });
@@ -1260,17 +1260,17 @@ ${table(r.tasks, ["id", "title", "dueAt", "assigneeId"])}`);
1260
1260
  const title = requireStr(options, "title");
1261
1261
  const files2 = attachPaths(options);
1262
1262
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1263
- const { addActivity } = await import("./chunks/activities-Cj_SIOay.mjs");
1263
+ const { addActivity } = await import("./chunks/activities-be260oPg.mjs");
1264
1264
  return activityOut(await addActivity(ctx, { type: "task", title, body: body(options), dueAt: strOpt(options, "due"), assigneeId: await assignee(ctx) ?? ctx.user?.id, targets: targets(multiOpt(options, "record")), attachments: await attachments(ctx, files2) }));
1265
1265
  });
1266
1266
  }
1267
1267
  case "done":
1268
1268
  if (!id) usage("tasks done needs a task id.");
1269
- return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-Cj_SIOay.mjs")).completeTask(ctx, id, !boolOpt(options, "undo"))));
1269
+ return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-be260oPg.mjs")).completeTask(ctx, id, !boolOpt(options, "undo"))));
1270
1270
  case "reassign":
1271
1271
  if (!id) usage("tasks reassign needs a task id.");
1272
1272
  requireStr(options, "assignee");
1273
- return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-Cj_SIOay.mjs")).updateActivity(ctx, id, { assigneeId: await assignee(ctx) ?? null })));
1273
+ return withSession(io, options, { lock: true }, async ({ ctx }) => activityOut(await (await import("./chunks/activities-be260oPg.mjs")).updateActivity(ctx, id, { assigneeId: await assignee(ctx) ?? null })));
1274
1274
  default:
1275
1275
  usage(`Unknown tasks action "${action ?? ""}".`, "Use list, add, done or reassign.");
1276
1276
  }
@@ -1301,11 +1301,12 @@ const U = {
1301
1301
  search: "records search <query> [--object <id>]… [--limit n]",
1302
1302
  duplicates: "records duplicates --object <id> [--by email,domain,name,phone]",
1303
1303
  merge: "records merge <primaryId> <duplicateId>… [--yes]",
1304
- image: "records image <recordId> <path> | --remove [--field <imageFieldId>]"
1304
+ image: "records image <recordId> <path> | --remove [--field <imageFieldId>]",
1305
+ sync: "records sync --object <id> --key <fieldId> --rows <json|@file|-> [--archive-field <fieldId>] [--ref-key <fieldId>] [--yes]"
1305
1306
  };
1306
1307
  const recordsCommand = {
1307
1308
  name: "records",
1308
- usage: "records <list|get|create|update|delete|restore|search|duplicates|merge|image> …",
1309
+ usage: "records <list|get|create|update|delete|restore|search|duplicates|merge|image|sync> …",
1309
1310
  summary: "Records of any object (companies, people, deals, custom objects): list with filters / sorts / pagination, get with relations and timeline counts, create, update (only the given fields), soft delete / restore, full-text search, find and merge duplicates.",
1310
1311
  subcommands: [
1311
1312
  { name: "list", usage: U.list, summary: `List records. Filter ops: ${FILTER_OPS.join(", ")}. Values: in/notIn comma-separated; dates YYYY-MM-DD, today, +30d.` },
@@ -1317,7 +1318,13 @@ const recordsCommand = {
1317
1318
  { name: "search", usage: U.search, summary: "Full-text search (names, emails, domains, phones)." },
1318
1319
  { name: "duplicates", usage: U.duplicates, summary: "Groups of likely duplicates (same email / domain / name / phone)." },
1319
1320
  { name: "merge", usage: U.merge, summary: "Merge duplicates into the primary (empty fields filled, relations and activities moved). Plan unless --yes.", writes: true, destructive: true },
1320
- { name: "image", usage: U.image, summary: "Set the record's avatar / logo from a local image (the object's image field), or --remove it.", writes: true }
1321
+ { name: "image", usage: U.image, summary: "Set the record's avatar / logo from a local image (the object's image field), or --remove it.", writes: true },
1322
+ {
1323
+ name: "sync",
1324
+ usage: U.sync,
1325
+ summary: 'Mirror an external source into one object: rows [{ key, fields }] matched on the --key field, created / updated only where a value differs; relation values reference other mirrored records by their key ({ "$key": "12" } or { "$key": ["12","13"] }); records whose key is missing get --archive-field = true (never deleted). No automation fires. Plan unless --yes.',
1326
+ writes: true
1327
+ }
1321
1328
  ],
1322
1329
  options: [
1323
1330
  SITE_OPTION,
@@ -1338,7 +1345,11 @@ const recordsCommand = {
1338
1345
  { name: "hard", type: "boolean", description: "delete: remove immediately (no restore)." },
1339
1346
  { name: "by", type: "string", description: "duplicates: email,domain,name,phone." },
1340
1347
  { name: "remove", type: "boolean", description: "image: remove the record's image." },
1341
- { name: "field", type: "string", description: "image: image field id (default: the object's image field)." }
1348
+ { name: "field", type: "string", description: "image: image field id (default: the object's image field)." },
1349
+ { name: "key", type: "string", description: "sync: field holding the source id (match key)." },
1350
+ { name: "rows", type: "string", description: "sync: rows as a JSON array [{ key, fields }], @file.json or - (stdin)." },
1351
+ { name: "archive-field", type: "string", description: "sync: boolean field set to true on records whose key is missing from the rows." },
1352
+ { name: "ref-key", type: "string", description: 'sync: key field of related objects for { "$key": … } (default: --key).' }
1342
1353
  ],
1343
1354
  async run({ io, options, positionals }) {
1344
1355
  const [action, ...args] = positionals;
@@ -1354,7 +1365,7 @@ const recordsCommand = {
1354
1365
  const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
1355
1366
  const sorts = parseSorts(multiOpt(options, "sort"));
1356
1367
  return withSession(io, options, {}, async ({ ctx }) => {
1357
- const { listRecords } = await import("./chunks/records-CCTxxMWF.mjs");
1368
+ const { listRecords } = await import("./chunks/records-BJLSmv0O.mjs");
1358
1369
  const page = listRecords(ctx, objectId, {
1359
1370
  filters,
1360
1371
  sorts,
@@ -1372,7 +1383,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1372
1383
  case "get": {
1373
1384
  const id = positional(args, 0, "record id", U.get);
1374
1385
  return withSession(io, options, {}, async ({ ctx }) => {
1375
- const { getRecordDetails } = await import("./chunks/records-CCTxxMWF.mjs");
1386
+ const { getRecordDetails } = await import("./chunks/records-BJLSmv0O.mjs");
1376
1387
  const d = getRecordDetails(ctx, id);
1377
1388
  return { ...await recordOut(ctx, d.record), relations: d.relations, related: d.inverse.filter((i) => i.records.length), activityCount: d.activityCount };
1378
1389
  });
@@ -1383,7 +1394,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1383
1394
  if (!Object.keys(fields).length) usage("records create needs --fields or --set.", `Usage: agentcrm ${U.create}`);
1384
1395
  const owner = strOpt(options, "owner");
1385
1396
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1386
- const { createRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1397
+ const { createRecord } = await import("./chunks/records-BJLSmv0O.mjs");
1387
1398
  return recordOut(ctx, await createRecord(ctx, objectId, fields, owner ? { ownerId: owner } : {}));
1388
1399
  }, (r) => `Created ${r.object} ${r.id} (${String(r.label)})`);
1389
1400
  }
@@ -1393,7 +1404,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1393
1404
  const owner = strOpt(options, "owner");
1394
1405
  if (!Object.keys(fields).length && owner === void 0) usage("records update needs --fields, --set or --owner.");
1395
1406
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1396
- const { updateRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1407
+ const { updateRecord } = await import("./chunks/records-BJLSmv0O.mjs");
1397
1408
  return recordOut(ctx, await updateRecord(ctx, id, fields, owner !== void 0 ? { ownerId: owner || null } : {}));
1398
1409
  }, (r) => `Updated ${r.id} (${String(r.label)})`);
1399
1410
  }
@@ -1401,7 +1412,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1401
1412
  if (!args.length) usage("records delete needs at least one id.", `Usage: agentcrm ${U.delete}`);
1402
1413
  const hard = boolOpt(options, "hard");
1403
1414
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1404
- const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-CCTxxMWF.mjs");
1415
+ const { getRecord, deleteRecord, bulkDelete } = await import("./chunks/records-BJLSmv0O.mjs");
1405
1416
  const { recordLabel } = await import("./chunks/fields-B0bitgmr.mjs");
1406
1417
  const { objectOf } = await import("./chunks/store-5gqRSKXg.mjs");
1407
1418
  const targets2 = args.map((id) => getRecord(ctx, id, { includeDeleted: true }));
@@ -1414,7 +1425,7 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1414
1425
  case "restore": {
1415
1426
  const id = positional(args, 0, "record id", U.restore);
1416
1427
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
1417
- const { restoreRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1428
+ const { restoreRecord } = await import("./chunks/records-BJLSmv0O.mjs");
1418
1429
  return recordOut(ctx, await restoreRecord(ctx, id));
1419
1430
  });
1420
1431
  }
@@ -1422,14 +1433,14 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1422
1433
  const query = args.join(" ").trim();
1423
1434
  if (!query) usage("records search needs a query.", `Usage: agentcrm ${U.search}`);
1424
1435
  return withSession(io, options, {}, async ({ ctx }) => {
1425
- const { searchRecords } = await import("./chunks/records-CCTxxMWF.mjs");
1436
+ const { searchRecords } = await import("./chunks/records-BJLSmv0O.mjs");
1426
1437
  return searchRecords(ctx, query, { objects: listOpt(options, "object"), limit: Math.min(100, numOpt(options, "limit") ?? 20) });
1427
1438
  });
1428
1439
  }
1429
1440
  case "duplicates": {
1430
1441
  const objectId = object();
1431
1442
  return withSession(io, options, {}, async ({ ctx }) => {
1432
- const { findDuplicates } = await import("./chunks/dedupe-BepmsaHv.mjs");
1443
+ const { findDuplicates } = await import("./chunks/dedupe-isAmKHQV.mjs");
1433
1444
  const by = listOpt(options, "by");
1434
1445
  const groups = findDuplicates(ctx, objectId, by ? { by } : {});
1435
1446
  return { object: objectId, groups: groups.length, duplicates: groups };
@@ -1439,19 +1450,48 @@ ${table(r.records, ["id", "label", "updatedAt"])}`);
1439
1450
  const [primary, ...dups] = args;
1440
1451
  if (!primary || !dups.length) usage("records merge needs <primaryId> <duplicateId>….", `Usage: agentcrm ${U.merge}`);
1441
1452
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1442
- const { getRecord } = await import("./chunks/records-CCTxxMWF.mjs");
1453
+ const { getRecord } = await import("./chunks/records-BJLSmv0O.mjs");
1443
1454
  const all = [primary, ...dups].map((id) => getRecord(ctx, id));
1444
1455
  const plan = { primary: await recordOut(ctx, all[0]), duplicates: await Promise.all(all.slice(1).map((r) => recordOut(ctx, r))) };
1445
1456
  if (options.yes !== true) return { needsConfirmation: true, plan };
1446
- const { mergeRecords } = await import("./chunks/dedupe-BepmsaHv.mjs");
1457
+ const { mergeRecords } = await import("./chunks/dedupe-isAmKHQV.mjs");
1447
1458
  const res = await mergeRecords(ctx, primary, dups);
1448
1459
  return { record: await recordOut(ctx, res.record), merged: res.merged, movedActivities: res.movedActivities };
1449
1460
  }, (r) => "plan" in r && r.plan ? `Would merge ${r.plan.duplicates.length} duplicate(s) into ${r.plan.primary.id}. Re-run with --yes once the user agrees.` : "merged" in r ? `Merged ${r.merged.length} into ${r.record.id}` : "");
1450
1461
  }
1451
1462
  case "image":
1452
1463
  return (await Promise.resolve().then(() => files)).recordImageAction(io, options, args);
1464
+ case "sync": {
1465
+ const objectId = Array.isArray(options.object) ? options.object[0] : options.object;
1466
+ const key = strOpt(options, "key");
1467
+ const rowsRaw = strOpt(options, "rows");
1468
+ if (!objectId || !key || !rowsRaw) usage("records sync needs --object, --key and --rows.", `Usage: agentcrm ${U.sync}`);
1469
+ const { readTextValue: readTextValue2 } = await Promise.resolve().then(() => util);
1470
+ let rows;
1471
+ try {
1472
+ rows = JSON.parse(readTextValue2(rowsRaw, "rows"));
1473
+ } catch (err) {
1474
+ usage(`--rows is not valid JSON: ${err.message}`);
1475
+ }
1476
+ if (!Array.isArray(rows)) usage("--rows must be a JSON array of { key, fields }.");
1477
+ const spec = { object: objectId, key, rows, archiveField: strOpt(options, "archive-field"), refKey: strOpt(options, "ref-key") };
1478
+ const apply = options.yes === true;
1479
+ return withSession(io, options, { lock: apply }, async ({ ctx }) => {
1480
+ const { syncRecords } = await import("./chunks/syncRecords-69hcCgOK.mjs");
1481
+ if (apply) return syncRecords(ctx, spec);
1482
+ const { dryRun: dryRun2 } = await Promise.resolve().then(() => dryRun$1);
1483
+ const { result } = await dryRun2(ctx, (copy) => syncRecords(copy, spec));
1484
+ const pending = result.created + result.updated + result.archived;
1485
+ return pending ? { needsConfirmation: true, plan: result } : result;
1486
+ }, (r) => {
1487
+ const s = "needsConfirmation" in r ? r.plan : r;
1488
+ const line = `${s.object}: ${s.rows} rows → ${s.created} new, ${s.updated} updated, ${s.unchanged} unchanged, ${s.archived} archived${s.unresolved ? `, ${s.unresolved} unresolved references` : ""}${s.errors.length ? `, ${s.errors.length} errors (first: ${s.errors[0].key}: ${s.errors[0].message})` : ""}`;
1489
+ return "needsConfirmation" in r ? `${line}
1490
+ Nothing changed — re-run with --yes once the user agrees.` : line;
1491
+ });
1492
+ }
1453
1493
  default:
1454
- usage(`Unknown records action "${action ?? ""}".`, "Use list, get, create, update, delete, restore, search, duplicates, merge or image.");
1494
+ usage(`Unknown records action "${action ?? ""}".`, "Use list, get, create, update, delete, restore, search, duplicates, merge, image or sync.");
1455
1495
  }
1456
1496
  }
1457
1497
  };
@@ -1471,13 +1511,13 @@ const pipelinesCommand = {
1471
1511
  io,
1472
1512
  options,
1473
1513
  {},
1474
- async ({ ctx }) => (await import("./chunks/pipelines-DiJ2dLqQ.mjs")).listPipelines(ctx, strOpt(options, "object")),
1514
+ async ({ ctx }) => (await import("./chunks/pipelines-G-c23x-s.mjs")).listPipelines(ctx, strOpt(options, "object")),
1475
1515
  (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")
1476
1516
  );
1477
1517
  }
1478
1518
  if (action === "move") {
1479
1519
  if (!id || !stage.length) usage("pipelines move needs <recordId> <stage>.");
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") })));
1520
+ return withSession(io, options, { lock: true }, async ({ ctx }) => recordOut(ctx, await (await import("./chunks/pipelines-G-c23x-s.mjs")).moveStage(ctx, id, stage.join(" "), { lostReason: strOpt(options, "lost-reason") })));
1481
1521
  }
1482
1522
  usage(`Unknown pipelines action "${action}".`, "Use list or move.");
1483
1523
  }
@@ -1509,7 +1549,7 @@ const viewsCommand = {
1509
1549
  const [action = "list", id] = positionals;
1510
1550
  switch (action) {
1511
1551
  case "list":
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"]));
1552
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/views-zRdsphZl.mjs")).listViews(ctx, strOpt(options, "object")), (vs) => table(vs, ["id", "object", "name", "type", "shared"]));
1513
1553
  case "create": {
1514
1554
  const type2 = strOpt(options, "type") ?? "table";
1515
1555
  if (type2 !== "table" && type2 !== "kanban") usage("--type must be table or kanban.");
@@ -1524,12 +1564,12 @@ const viewsCommand = {
1524
1564
  ...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
1525
1565
  shared: boolOpt(options, "shared")
1526
1566
  };
1527
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-BbhmQO3n.mjs")).createView(ctx, input));
1567
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/views-zRdsphZl.mjs")).createView(ctx, input));
1528
1568
  }
1529
1569
  case "delete":
1530
1570
  if (!id) usage("views delete needs a view id.");
1531
1571
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx }) => {
1532
- const { getView, deleteView } = await import("./chunks/views-BbhmQO3n.mjs");
1572
+ const { getView, deleteView } = await import("./chunks/views-zRdsphZl.mjs");
1533
1573
  const v = getView(ctx, id);
1534
1574
  if (options.yes !== true) return { needsConfirmation: true, plan: { delete: { id: v.id, name: v.name, object: v.object, shared: v.shared } } };
1535
1575
  return deleteView(ctx, id);
@@ -2124,7 +2164,7 @@ async function check({ io, options, positionals }) {
2124
2164
  const kind = strOpt(options, "kind");
2125
2165
  if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
2126
2166
  const { readRawPackage } = await import("./chunks/extPackage-DuZ6GZhp.mjs");
2127
- const { checkExtension } = await import("./chunks/extCheck-BcbUc7eB.mjs");
2167
+ const { checkExtension } = await import("./chunks/extCheck-Bu8C87OV.mjs");
2128
2168
  const raw = await readRawPackage(target);
2129
2169
  const report = await checkExtension(raw, { kind, callTools: !boolOpt(options, "no-call") });
2130
2170
  printResult(io, report, () => {
@@ -2171,7 +2211,7 @@ async function install({ io, options, positionals }) {
2171
2211
  const apply = options.yes === true;
2172
2212
  const disabled = boolOpt(options, "disabled");
2173
2213
  if (!boolOpt(options, "force")) {
2174
- const { checkExtension } = await import("./chunks/extCheck-BcbUc7eB.mjs");
2214
+ const { checkExtension } = await import("./chunks/extCheck-Bu8C87OV.mjs");
2175
2215
  const report = await checkExtension(raw, { callTools: false });
2176
2216
  if (!report.valid) {
2177
2217
  throw new CliError(
@@ -2207,7 +2247,7 @@ async function install({ io, options, positionals }) {
2207
2247
  }
2208
2248
  const result = await ext.installExtension(s.ctx, pkg2, disabled ? { enabled: false } : {});
2209
2249
  const report = await s.extensions.sync();
2210
- const { getLoadedExtension } = await import("./chunks/registry-ClzNZv9Z.mjs");
2250
+ const { getLoadedExtension } = await import("./chunks/registry-Bg8kGteS.mjs");
2211
2251
  const loaded = getLoadedExtension(m.id);
2212
2252
  const failure = report?.failed.find((f) => f.id === m.id || f.id === "*");
2213
2253
  if (failure && !loaded) return { ...result, author: m.author ?? null, load: { status: "error", error: failure.error } };
@@ -2269,7 +2309,7 @@ const extCommand = {
2269
2309
  if (kind && kind !== "module" && kind !== "theme") usage("--kind is module or theme.");
2270
2310
  return withSession(io, options, {}, async (s) => {
2271
2311
  const { listExtensions } = await import("./chunks/extensions-Bp72uZ9a.mjs");
2272
- const { getLoadedExtension } = await import("./chunks/registry-ClzNZv9Z.mjs");
2312
+ const { getLoadedExtension } = await import("./chunks/registry-Bg8kGteS.mjs");
2273
2313
  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 } : {} }));
2274
2314
  }, (rows) => table(rows.map((r) => ({ ...r, enabled: r.enabled ? "yes" : "no" })), ["id", "kind", "name", "version", "enabled", "load", "author"]));
2275
2315
  }
@@ -2384,7 +2424,7 @@ const importCommand = {
2384
2424
  options,
2385
2425
  {},
2386
2426
  async ({ ctx }) => {
2387
- const plan = await (await import("./chunks/backup-CO0EzdF_.mjs")).planBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" });
2427
+ const plan = await (await import("./chunks/backup-DDqTRbuW.mjs")).planBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" });
2388
2428
  return { needsConfirmation: true, plan: { restore: resolve(path), bytes: bytes.length, ...plan } };
2389
2429
  },
2390
2430
  (r) => [
@@ -2394,7 +2434,7 @@ const importCommand = {
2394
2434
  ].join("\n")
2395
2435
  );
2396
2436
  }
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)`);
2437
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/backup-DDqTRbuW.mjs")).importBackupZip(ctx, bytes, { mode: options.replace === true ? "replace" : "merge" }), (r) => `Restored ${r.written} document(s) and ${r.files} file(s)`);
2398
2438
  }
2399
2439
  if (kind !== "csv") usage('Only "import csv" and "import zip" are supported.', "Usage: agentcrm import csv <file> --object <id>");
2400
2440
  const source = positional(positionals, 1, "CSV file (or -)", "import csv <file|-> --object <id>");
@@ -2405,7 +2445,7 @@ const importCommand = {
2405
2445
  const apply = options.yes === true && options["dry-run"] !== true;
2406
2446
  const shown = numOpt(options, "rows") ?? 100;
2407
2447
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2408
- const { importCsv } = await import("./chunks/importCsv-XjQJ9QIR.mjs");
2448
+ const { importCsv } = await import("./chunks/importCsv-BuSYG4ja.mjs");
2409
2449
  const report = await importCsv(ctx, {
2410
2450
  object: objectId,
2411
2451
  csv,
@@ -2454,7 +2494,7 @@ const exportCommand = {
2454
2494
  const file2 = strOpt(options, "out");
2455
2495
  if (!file2) usage("export zip needs --out <file.zip>.");
2456
2496
  return withSession(io, options, {}, async ({ ctx }) => {
2457
- const { exportBackupZip } = await import("./chunks/backup-CO0EzdF_.mjs");
2497
+ const { exportBackupZip } = await import("./chunks/backup-DDqTRbuW.mjs");
2458
2498
  const { zip, report } = await exportBackupZip(ctx, { includeFiles: options["no-files"] !== true });
2459
2499
  writeOutFile(file2, zip);
2460
2500
  return { file: resolve(file2), zipBytes: zip.length, files: report.files, missing: report.missing, fileBytes: report.bytes };
@@ -2473,7 +2513,7 @@ const exportCommand = {
2473
2513
  const filters = parseFilters(multiOpt(options, "filter"), FILTER_OPS);
2474
2514
  const sorts = parseSorts(multiOpt(options, "sort"));
2475
2515
  return withSession(io, options, {}, async ({ ctx }) => {
2476
- const { exportCsv } = await import("./chunks/exportCsv-C-fIHLm1.mjs");
2516
+ const { exportCsv } = await import("./chunks/exportCsv-9ZMyqTdH.mjs");
2477
2517
  const csv = exportCsv(ctx, objectId, {
2478
2518
  viewId: strOpt(options, "view"),
2479
2519
  filters,
@@ -2489,7 +2529,7 @@ const exportCommand = {
2489
2529
  }
2490
2530
  if (kind === "json") {
2491
2531
  return withSession(io, options, {}, async ({ ctx }) => {
2492
- const { exportJson } = await import("./chunks/exportCsv-C-fIHLm1.mjs");
2532
+ const { exportJson } = await import("./chunks/exportCsv-9ZMyqTdH.mjs");
2493
2533
  const data = exportJson(ctx, { collections: listOpt(options, "collections") });
2494
2534
  const text = JSON.stringify(data, null, 2);
2495
2535
  if (out) return deliver(text, {});
@@ -2533,7 +2573,7 @@ const reportCommand = {
2533
2573
  ...strOpt(options, "pipeline") ? { pipelineId: strOpt(options, "pipeline") } : {},
2534
2574
  ...strOpt(options, "owner") ? { ownerId: strOpt(options, "owner") } : {}
2535
2575
  };
2536
- return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-WlI_A5Nr.mjs")).runReport(ctx, internal, params) }));
2576
+ return withSession(io, options, {}, async ({ ctx }) => ({ report: name2, data: (await import("./chunks/reports-ogUa41jr.mjs")).runReport(ctx, internal, params) }));
2537
2577
  }
2538
2578
  };
2539
2579
  const statsCommand = {
@@ -2543,7 +2583,7 @@ const statsCommand = {
2543
2583
  options: [SITE_OPTION, AS_OPTION],
2544
2584
  async run({ io, options }) {
2545
2585
  return withSession(io, options, {}, async ({ ctx, store }) => {
2546
- const stats = (await import("./chunks/stats-DfmDP3tE.mjs")).getStats(ctx);
2586
+ const stats = (await import("./chunks/stats-bInarNLK.mjs")).getStats(ctx);
2547
2587
  const journal = store.stats;
2548
2588
  return { ...stats, ...journal ? { journal: await journal.call(store) } : {} };
2549
2589
  }, (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"));
@@ -2557,7 +2597,7 @@ const compactCommand = {
2557
2597
  async run({ io, options }) {
2558
2598
  const apply = options.yes === true;
2559
2599
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2560
- const { compactData } = await import("./chunks/compact-qF5t42ko.mjs");
2600
+ const { compactData } = await import("./chunks/compact-BqTG5cvC.mjs");
2561
2601
  const report = await compactData(ctx, { olderThanDays: numOpt(options, "older-than"), keepRuns: numOpt(options, "keep-runs"), dryRun: !apply });
2562
2602
  return apply ? report : { needsConfirmation: true, plan: report };
2563
2603
  }, (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}`);
@@ -2578,19 +2618,19 @@ const automationsCommand = {
2578
2618
  const [action = "list", id] = positionals;
2579
2619
  switch (action) {
2580
2620
  case "list":
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"]));
2621
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/automations-gCDYNS88.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"]));
2582
2622
  case "enable":
2583
2623
  case "disable": {
2584
2624
  if (!id) usage(`automations ${action} needs an id.`);
2585
2625
  return withSession(io, options, { lock: true }, async ({ ctx }) => {
2586
- const mod = await import("./chunks/automations-BqY8EwfJ.mjs");
2626
+ const mod = await import("./chunks/automations-gCDYNS88.mjs");
2587
2627
  return mod.setAutomationEnabled(ctx, id, action === "enable");
2588
2628
  });
2589
2629
  }
2590
2630
  case "run": {
2591
2631
  const apply = options.yes === true;
2592
2632
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2593
- const { runScheduled } = await import("./chunks/automations-BqY8EwfJ.mjs");
2633
+ const { runScheduled } = await import("./chunks/automations-gCDYNS88.mjs");
2594
2634
  const report = await runScheduled(ctx, { dryRun: !apply, automationId: strOpt(options, "id"), force: boolOpt(options, "force") });
2595
2635
  return apply ? report : { needsConfirmation: true, ...report };
2596
2636
  }, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}ran ${r.ran}, skipped ${r.skipped}, errors ${r.errors}`);
@@ -2614,11 +2654,11 @@ const formsCommand = {
2614
2654
  const [action = "list", id] = positionals;
2615
2655
  switch (action) {
2616
2656
  case "list":
2617
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-DJDop3XY.mjs")).listForms(ctx), (fs) => table(fs, ["id", "name", "object", "publishedPath"]));
2657
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/forms-Bb0zztBM.mjs")).listForms(ctx), (fs) => table(fs, ["id", "name", "object", "publishedPath"]));
2618
2658
  case "publish": {
2619
2659
  if (!id) usage("forms publish needs a form id.");
2620
2660
  return withSession(io, options, { lock: options.yes === true }, async (s) => {
2621
- const forms = await import("./chunks/forms-DJDop3XY.mjs");
2661
+ const forms = await import("./chunks/forms-Bb0zztBM.mjs");
2622
2662
  const built = await forms.buildFormHtml(s.ctx, id);
2623
2663
  const target = s.hostingTarget();
2624
2664
  if (!target) usage(`${s.site.alias} has no hosting to publish the form to.`);
@@ -2632,7 +2672,7 @@ const formsCommand = {
2632
2672
  case "ingest": {
2633
2673
  const apply = options.yes === true;
2634
2674
  return withSession(io, options, { lock: apply }, async ({ ctx }) => {
2635
- const { ingestInbox } = await import("./chunks/forms-DJDop3XY.mjs");
2675
+ const { ingestInbox } = await import("./chunks/forms-Bb0zztBM.mjs");
2636
2676
  const report = await ingestInbox(ctx, strOpt(options, "form"), { dryRun: !apply, limit: numOpt(options, "limit") });
2637
2677
  return apply ? report : { needsConfirmation: true, ...report };
2638
2678
  }, (r) => `${"needsConfirmation" in r ? "DRY RUN — " : ""}processed ${r.processed}: ${r.created} created, ${r.updated} updated, ${r.failed} failed`);
@@ -3475,13 +3515,13 @@ const objectsCommand = {
3475
3515
  switch (action) {
3476
3516
  case "list":
3477
3517
  return withSession(io, options, {}, async ({ ctx, store }) => {
3478
- const { listObjects } = await import("./chunks/schema-DOnnH2zL.mjs");
3518
+ const { listObjects } = await import("./chunks/schema-Cwi7iFp2.mjs");
3479
3519
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3480
3520
  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 }));
3481
3521
  }, (rows) => table(rows, ["id", "labelPlural", "fields", "records", "standard"]));
3482
3522
  case "get": {
3483
3523
  const objectId = positional(positionals, 1, "object id", "objects get <id>");
3484
- return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).describeSchema(ctx, objectId)[0]);
3524
+ return withSession(io, options, {}, async ({ ctx }) => (await import("./chunks/schema-Cwi7iFp2.mjs")).describeSchema(ctx, objectId)[0]);
3485
3525
  }
3486
3526
  case "create": {
3487
3527
  const data = jsonObjectOpt(options, "data") ?? {};
@@ -3494,7 +3534,7 @@ const objectsCommand = {
3494
3534
  ...strOpt(options, "color") ? { color: strOpt(options, "color") } : {}
3495
3535
  };
3496
3536
  if (!input.labelSingular || !input.labelPlural) usage("objects create needs --singular and --plural (or --data).");
3497
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).createObject(ctx, input));
3537
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-Cwi7iFp2.mjs")).createObject(ctx, input));
3498
3538
  }
3499
3539
  case "update": {
3500
3540
  const objectId = positional(positionals, 1, "object id", "objects update <id> …");
@@ -3506,12 +3546,12 @@ const objectsCommand = {
3506
3546
  ...strOpt(options, "label-field") ? { labelField: strOpt(options, "label-field") } : {}
3507
3547
  };
3508
3548
  if (!Object.keys(patch).length) usage("objects update needs --singular, --plural, --icon, --color or --label-field.");
3509
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).updateObject(ctx, objectId, patch));
3549
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-Cwi7iFp2.mjs")).updateObject(ctx, objectId, patch));
3510
3550
  }
3511
3551
  case "delete": {
3512
3552
  if (!id) usage("objects delete needs an object id.");
3513
3553
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
3514
- const { getObject, deleteObject } = await import("./chunks/schema-DOnnH2zL.mjs");
3554
+ const { getObject, deleteObject } = await import("./chunks/schema-Cwi7iFp2.mjs");
3515
3555
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3516
3556
  const o = getObject(ctx, id);
3517
3557
  const plan = { object: o.id, label: o.labelPlural, standard: o.standard, recordsDeleted: recordsOf(store, o.id, { includeDeleted: true }).length };
@@ -3569,7 +3609,7 @@ const fieldsCommand = {
3569
3609
  ...boolOpt(options, "unique") ? { unique: true } : {},
3570
3610
  ...strOpt(options, "description") ? { description: strOpt(options, "description") } : {}
3571
3611
  };
3572
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).createField(ctx, objectId, input));
3612
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-Cwi7iFp2.mjs")).createField(ctx, objectId, input));
3573
3613
  }
3574
3614
  case "update": {
3575
3615
  if (!fieldId) usage("fields update needs a field id.");
@@ -3586,12 +3626,12 @@ const fieldsCommand = {
3586
3626
  ...strOpt(options, "description") !== void 0 ? { description: strOpt(options, "description") } : {}
3587
3627
  };
3588
3628
  if (!Object.keys(patch).length) usage("fields update needs at least one change.");
3589
- return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-DOnnH2zL.mjs")).updateField(ctx, objectId, fieldId, patch));
3629
+ return withSession(io, options, { lock: true }, async ({ ctx }) => (await import("./chunks/schema-Cwi7iFp2.mjs")).updateField(ctx, objectId, fieldId, patch));
3590
3630
  }
3591
3631
  case "delete": {
3592
3632
  if (!fieldId) usage("fields delete needs a field id.");
3593
3633
  return withSession(io, options, { lock: options.yes === true }, async ({ ctx, store }) => {
3594
- const { getObject, deleteField } = await import("./chunks/schema-DOnnH2zL.mjs");
3634
+ const { getObject, deleteField } = await import("./chunks/schema-Cwi7iFp2.mjs");
3595
3635
  const { recordsOf } = await import("./chunks/store-5gqRSKXg.mjs");
3596
3636
  const field = getObject(ctx, objectId).fields.find((f) => f.id === fieldId);
3597
3637
  if (!field) usage(`No field "${fieldId}" on ${objectId}.`, `\`agentcrm objects get ${objectId}\` lists them.`);
@@ -3624,7 +3664,7 @@ const schemaCommand = {
3624
3664
  usage(`The schema is not valid JSON: ${err.message}`);
3625
3665
  }
3626
3666
  const apply = options.yes === true;
3627
- const { applySchema, parseSchemaSpec } = await import("./chunks/schemaApply-DKHipylx.mjs");
3667
+ const { applySchema, parseSchemaSpec } = await import("./chunks/schemaApply-tEIr5lFK.mjs");
3628
3668
  const spec = parseSchemaSpec(raw);
3629
3669
  return withSession(
3630
3670
  io,
@@ -3942,7 +3982,7 @@ const toolsCommand = {
3942
3982
  async run({ io, options, positionals }) {
3943
3983
  const [action = "list"] = positionals;
3944
3984
  if (action === "list") {
3945
- const { listAiTools, AI_TOOLS } = await import("./chunks/tools-CCk24tdh.mjs");
3985
+ const { listAiTools, AI_TOOLS } = await import("./chunks/tools-BR3X1Uvm.mjs");
3946
3986
  const builtin = new Set(AI_TOOLS.map((t) => t.name));
3947
3987
  const shape = () => listAiTools().map((t) => ({ name: t.name, description: t.description, mutating: t.mutating, source: builtin.has(t.name) ? "builtin" : "extension", inputSchema: t.inputSchema }));
3948
3988
  const human = (r) => table(r.tools, ["name", "mutating", "source", "description"]);
@@ -3962,7 +4002,7 @@ const toolsCommand = {
3962
4002
  }
3963
4003
  if (action === "call") {
3964
4004
  const name2 = positional(positionals, 1, "tool name", "tools call <name> --input <json>");
3965
- const { findTool, callTool, listAiTools } = await import("./chunks/tools-CCk24tdh.mjs");
4005
+ const { findTool, callTool, listAiTools } = await import("./chunks/tools-BR3X1Uvm.mjs");
3966
4006
  const input = jsonObjectOpt(options, "input") ?? {};
3967
4007
  const builtin = findTool(name2);
3968
4008
  return withSession(io, options, { lock: builtin ? builtin.mutating : true }, async ({ ctx }) => {
@@ -3984,8 +4024,8 @@ const mcpCommand = {
3984
4024
  const log = (msg) => process.stderr.write(`${msg}
3985
4025
  `);
3986
4026
  for (const w of session2.warnings) log(`[agentcrm mcp] note: ${w}`);
3987
- const { listAiTools } = await import("./chunks/tools-CCk24tdh.mjs");
3988
- const { createMcpServer, serveStdio } = await import("./chunks/server-Bch4TQon.mjs");
4027
+ const { listAiTools } = await import("./chunks/tools-BR3X1Uvm.mjs");
4028
+ const { createMcpServer, serveStdio } = await import("./chunks/server-3awvGTvL.mjs");
3989
4029
  let lastRefresh = Date.now();
3990
4030
  const server = createMcpServer({
3991
4031
  readOnly,