@mulmoclaude/core 0.28.0 → 0.29.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 (44) hide show
  1. package/assets/helps/collection-skills.md +4 -2
  2. package/dist/collection/core/ontologyGraph.d.ts +6 -2
  3. package/dist/collection/index.cjs.map +1 -1
  4. package/dist/collection/index.js.map +1 -1
  5. package/dist/collection/registry/server/index.cjs +2 -2
  6. package/dist/collection/registry/server/index.js +2 -2
  7. package/dist/collection/server/backendAvailability.d.ts +5 -0
  8. package/dist/collection/server/index.cjs +4 -2
  9. package/dist/collection/server/index.d.ts +1 -0
  10. package/dist/collection/server/index.js +3 -3
  11. package/dist/collection/server/manageTool.d.ts +4 -0
  12. package/dist/collection/server/schemaDocs.d.ts +21 -0
  13. package/dist/collection/server/store.d.ts +37 -0
  14. package/dist/collection/server/watchFs.d.ts +17 -0
  15. package/dist/collection-watchers/index.cjs +113 -202
  16. package/dist/collection-watchers/index.cjs.map +1 -1
  17. package/dist/collection-watchers/index.d.ts +1 -1
  18. package/dist/collection-watchers/index.js +113 -201
  19. package/dist/collection-watchers/index.js.map +1 -1
  20. package/dist/collection-watchers/watcher.d.ts +8 -13
  21. package/dist/{discovery-BVdCgBFN.js → discovery-BbsJwVEq.js} +109 -8
  22. package/dist/discovery-BbsJwVEq.js.map +1 -0
  23. package/dist/{discovery-BKovdRpZ.cjs → discovery-Bklck7Ck.cjs} +119 -6
  24. package/dist/discovery-Bklck7Ck.cjs.map +1 -0
  25. package/dist/feeds/server/index.cjs +2 -2
  26. package/dist/feeds/server/index.js +2 -2
  27. package/dist/google/index.cjs +1 -1
  28. package/dist/google/index.js +1 -1
  29. package/dist/remote-view/index.cjs +8 -0
  30. package/dist/remote-view/index.cjs.map +1 -1
  31. package/dist/remote-view/index.d.ts +7 -0
  32. package/dist/remote-view/index.js +8 -1
  33. package/dist/remote-view/index.js.map +1 -1
  34. package/dist/{server-rRQkNBvO.cjs → server-CghbYZFY.cjs} +179 -14
  35. package/dist/server-CghbYZFY.cjs.map +1 -0
  36. package/dist/{server-B8mvfZSF.js → server-DlG6ydHO.js} +179 -14
  37. package/dist/server-DlG6ydHO.js.map +1 -0
  38. package/dist/whisper/client.cjs.map +1 -1
  39. package/dist/whisper/client.js.map +1 -1
  40. package/package.json +1 -1
  41. package/dist/discovery-BKovdRpZ.cjs.map +0 -1
  42. package/dist/discovery-BVdCgBFN.js.map +0 -1
  43. package/dist/server-B8mvfZSF.js.map +0 -1
  44. package/dist/server-rRQkNBvO.cjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { t as errorMessage } from "./errors-eid6Mes3.js";
2
2
  import { f as storageKindFor, l as embedTargetId, m as fieldTextOrNull, o as COMPUTED_TYPES, p as fieldText, u as isFieldDrivenEvery } from "./ids-D1M1T6KJ.js";
3
3
  import { D as rollupValue, E as projectBacklinkRow, O as actionVisible, S as itemIsDone, T as coerceNumeric, b as resolveIcon, d as parseIsoDateTime, g as deriveAll, t as defangForPrompt, u as parseIsoDate, w as backlinkRows, x as selectDynamicRecord, y as firstDateField } from "./promptSafety-cZIeiZtB.js";
4
- import { $ as isPresetSlug$1, B as writeFileAtomic, C as compileJsonlQuery, G as resolveDataDir, H as isContainedInRoot, J as safeSlugName, K as resolveTemplatePath, Q as getWorkspaceRoot, R as resolveCreateItemId, V as SCHEMA_FILE$1, Y as archiveDir, b as normalizeCsvValue, d as checkpointSqliteDatabase, et as log, h as cacheDir, j as isRegularFile, l as readOnlyRefusal, n as discoverCollections, o as CollectionSchemaZ, r as loadCollection, rt as skillsStagingDir, s as resolveMutateSet, u as storeFor, w as CollectionQueryZ, x as queryCsv } from "./discovery-BVdCgBFN.js";
4
+ import { B as resolveCreateItemId, C as compileJsonlQuery, H as writeFileAtomic, J as resolveTemplatePath, N as isRegularFile, O as isBackendUnavailable, U as SCHEMA_FILE$1, W as isContainedInRoot, X as safeSlugName, Z as archiveDir, at as skillsStagingDir, b as normalizeCsvValue, d as checkpointSqliteDatabase, et as getWorkspaceRoot, h as cacheDir, l as readOnlyRefusal, n as discoverCollections, nt as log, o as CollectionSchemaZ, q as resolveDataDir, r as loadCollection, s as resolveMutateSet, tt as isPresetSlug$1, u as storeFor, w as CollectionQueryZ, x as queryCsv } from "./discovery-BbsJwVEq.js";
5
5
  import { dataSkillDir, mirrorSkillWrite } from "./skill-bridge/index.js";
6
6
  import path from "node:path";
7
7
  import { cp, lstat, mkdir, open, readFile, readdir, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -543,7 +543,8 @@ async function countRecordFiles(dataDir, workspaceRoot) {
543
543
  async function countRecords(collection, workspaceRoot) {
544
544
  if (storageKindFor(collection.schema) !== "file") try {
545
545
  return (await storeFor(collection, { workspaceRoot }).page({ limit: 0 })).total;
546
- } catch {
546
+ } catch (err) {
547
+ if (isBackendUnavailable(err)) return null;
547
548
  return 0;
548
549
  }
549
550
  return countRecordFiles(collection.dataDir, workspaceRoot);
@@ -1124,6 +1125,151 @@ async function deleteCustomView(collection, viewId, opts = {}) {
1124
1125
  viewId
1125
1126
  };
1126
1127
  }
1128
+ /** Ceiling for ANY assembled reply — topic or default — safely inside
1129
+ * the agent's per-result limit (the full doc is what overflowed it).
1130
+ * A matched section too large to fit degrades to its own prose plus a
1131
+ * list of its subsections to fetch individually; an oversized leaf is
1132
+ * clipped outright; the default drops whole core sections into a
1133
+ * pointer note. Sized so the CURRENT bundled doc's default reply
1134
+ * (~33KB) fits without degrading. */
1135
+ var SCHEMA_DOCS_RESULT_BUDGET = 36e3;
1136
+ /** Hard truncation for a body no sectioning trick can shrink further.
1137
+ * The marker tells the agent it did NOT see everything and how to get
1138
+ * the rest, so a silent cut can't read as complete coverage. The limit
1139
+ * is clamped at zero: a negative limit would make `slice(0, limit)`
1140
+ * count from the END and return almost the whole body — the exact
1141
+ * overflow this function exists to prevent. */
1142
+ function clip(text, limit) {
1143
+ const max = Math.max(0, limit);
1144
+ if (text.length <= max) return text;
1145
+ return `${text.slice(0, max)}\n\n[…clipped ${text.length - max} chars — fetch a narrower \`topic\`, or read the doc file directly]`;
1146
+ }
1147
+ /** Slack a budget-spending assembler must reserve for what it adds
1148
+ * AROUND the budgeted pieces — clip markers, join separators, pointer
1149
+ * notes — so the final hard cap doesn't eat the reply's own tail (in
1150
+ * renderDefault's case, the TOC). */
1151
+ var ASSEMBLY_RESERVE = 500;
1152
+ /** The sections every schema author needs, matched against headings so
1153
+ * the doc can be reorganised without touching this list (an unmatched
1154
+ * pattern is simply skipped): what a collection IS, the schema DSL and
1155
+ * its field types, and the create/edit walkthroughs. Advanced sections
1156
+ * (actions, bells, views, dataSource, storage) stay TOC-only. */
1157
+ var CORE_SECTION_PATTERNS = [
1158
+ "anatomy",
1159
+ "skill.md",
1160
+ "the dsl",
1161
+ "field types",
1162
+ "end-to-end",
1163
+ "editing an existing"
1164
+ ];
1165
+ /** Backticks stripped + lowercased, so `topic: "dataSource"` matches the
1166
+ * heading "External data (CSV) collections — `dataSource`". */
1167
+ var normalize = (text) => text.toLowerCase().replace(/`/g, "");
1168
+ var sliceLines = (lines, from, until) => lines.slice(from, until).join("\n").trim();
1169
+ /** All `#`–`###` headings, skipping fenced code blocks (a `# comment`
1170
+ * inside an example must not become a section boundary). */
1171
+ function headingLines(lines) {
1172
+ const found = [];
1173
+ let fenced = false;
1174
+ lines.forEach((line, index) => {
1175
+ if (line.trimStart().startsWith("```")) fenced = !fenced;
1176
+ const match = fenced ? null : /^(#{1,3}) (.+)$/.exec(line);
1177
+ if (match) found.push({
1178
+ index,
1179
+ level: match[1].length,
1180
+ heading: match[2].trim()
1181
+ });
1182
+ });
1183
+ return found;
1184
+ }
1185
+ function parseSections(lines) {
1186
+ const heads = headingLines(lines);
1187
+ return heads.map((head, i) => {
1188
+ const closer = heads.slice(i + 1).find((other) => other.level <= head.level);
1189
+ return {
1190
+ level: head.level,
1191
+ heading: head.heading,
1192
+ start: head.index,
1193
+ ownEnd: heads[i + 1]?.index ?? lines.length,
1194
+ deepEnd: closer?.index ?? lines.length
1195
+ };
1196
+ });
1197
+ }
1198
+ /** Bounded to half the reply budget: every render path appends the TOC,
1199
+ * so a doc with thousands of headings must not blow the ceiling through
1200
+ * the TOC itself — and renderDefault subtracts the TOC's length from
1201
+ * its budget, which must stay positive. */
1202
+ function tableOfContents(sections) {
1203
+ return clip(`Sections (call schemaDocs with \`topic: "<heading>"\` for any of them; \`topic: "all"\` for the full document):\n${sections.map((section) => `${" ".repeat(section.level - 1)}- ${section.heading}`).join("\n")}`, Math.floor(SCHEMA_DOCS_RESULT_BUDGET / 2));
1204
+ }
1205
+ /** The no-topic reply: the doc's intro + the core authoring sections
1206
+ * (own prose only — a core parent's advanced subsections stay TOC-only),
1207
+ * closed by the full table of contents. Budgeted: a core section that
1208
+ * no longer fits is dropped into a pointer note (the first is clipped
1209
+ * instead, so the reply is never bodiless) — an oversized workspace
1210
+ * copy must not recreate the overflow this module exists to prevent. */
1211
+ function renderDefault(lines, sections) {
1212
+ const isCore = (section, index) => index === 0 || CORE_SECTION_PATTERNS.some((pattern) => normalize(section.heading).includes(pattern));
1213
+ const toc = tableOfContents(sections);
1214
+ const parts = [];
1215
+ const skipped = [];
1216
+ let remaining = SCHEMA_DOCS_RESULT_BUDGET - toc.length - ASSEMBLY_RESERVE;
1217
+ for (const section of sections.filter(isCore)) {
1218
+ const body = sliceLines(lines, section.start, section.ownEnd);
1219
+ if (body.length > remaining && parts.length > 0) {
1220
+ skipped.push(section.heading);
1221
+ continue;
1222
+ }
1223
+ const fitted = clip(body, remaining);
1224
+ parts.push(fitted);
1225
+ remaining -= fitted.length;
1226
+ }
1227
+ const note = skipped.length > 0 ? `\n\n[Omitted for size — fetch each with \`topic\`: ${skipped.join(" · ")}]` : "";
1228
+ return `${parts.join("\n\n")}${note}\n\n---\n\n${toc}`;
1229
+ }
1230
+ /** Case-insensitive substring match on headings, minus any match already
1231
+ * contained in another match's subtree (its parent's deep body covers it). */
1232
+ function matchSections(sections, topic) {
1233
+ const needle = normalize(topic).trim();
1234
+ const matched = sections.filter((section) => normalize(section.heading).includes(needle));
1235
+ return matched.filter((section) => !matched.some((other) => other !== section && other.start < section.start && section.deepEnd <= other.deepEnd));
1236
+ }
1237
+ /** One matched section: its whole subtree when that fits the budget,
1238
+ * otherwise its own prose (budget-clipped) + a pointer list of
1239
+ * subsections to fetch — the list clipped to what the prose left over
1240
+ * (thousands of child headings must not overflow through the list
1241
+ * itself) — and a leaf with no subsections to offer is simply clipped. */
1242
+ function renderSection(lines, sections, section, budget) {
1243
+ const deep = sliceLines(lines, section.start, section.deepEnd);
1244
+ if (deep.length <= budget) return deep;
1245
+ const children = sections.filter((child) => child.start > section.start && child.start < section.deepEnd && child.level === section.level + 1);
1246
+ const own = clip(sliceLines(lines, section.start, section.ownEnd), budget);
1247
+ if (children.length === 0) return own;
1248
+ return `${own}\n\nSubsections (too large to include together — fetch each with \`topic\`):\n${clip(children.map((child) => `- ${child.heading}`).join("\n"), budget - own.length)}`;
1249
+ }
1250
+ function renderTopic(lines, sections, topic) {
1251
+ const matched = matchSections(sections, topic);
1252
+ if (matched.length === 0) return `manageCollection: no schemaDocs section matches '${defangForPrompt(topic)}'.\n\n${tableOfContents(sections)}`;
1253
+ const perMatch = Math.floor(SCHEMA_DOCS_RESULT_BUDGET / matched.length);
1254
+ return matched.map((section) => renderSection(lines, sections, section, perMatch)).join("\n\n");
1255
+ }
1256
+ /** Render the authoring reference for one schemaDocs call. Small docs and
1257
+ * docs without headings pass through verbatim — there is nothing better
1258
+ * to key a section off. The assembled reply gets one final hard cap: the
1259
+ * inner budgeting degrades gracefully, but per-piece overheads (clip
1260
+ * markers on thousands of matches, join separators, pointer notes) add
1261
+ * up outside any single piece's budget, and the ceiling must hold no
1262
+ * matter what shape of document arrives. Only the explicit \`"all"\`
1263
+ * opt-in and the small-doc verbatim path may exceed it. */
1264
+ function renderSchemaDocs(doc, topic) {
1265
+ const requested = topic?.trim() ?? "";
1266
+ if (normalize(requested) === "all") return doc;
1267
+ if (doc.length <= 2e4) return doc;
1268
+ const lines = doc.split("\n");
1269
+ const sections = parseSections(lines);
1270
+ if (sections.length === 0) return doc;
1271
+ return clip(requested ? renderTopic(lines, sections, requested) : renderDefault(lines, sections), SCHEMA_DOCS_RESULT_BUDGET);
1272
+ }
1127
1273
  /** True for a launcher-managed preset slug (`mc-<something>`). The sync logic uses
1128
1274
  * this to bound which active skills it may refresh/prune, so user-authored skills are
1129
1275
  * never touched. */
@@ -1195,7 +1341,10 @@ async function loadRequestedItems(collection, ids, deps) {
1195
1341
  const items = [];
1196
1342
  const missing = [];
1197
1343
  for (const recordId of ids) {
1198
- const item = await store.read(recordId).catch(() => null);
1344
+ const item = await store.read(recordId).catch((err) => {
1345
+ if (isBackendUnavailable(err)) throw err;
1346
+ return null;
1347
+ });
1199
1348
  if (item) items.push(item);
1200
1349
  else missing.push(recordId);
1201
1350
  }
@@ -1256,7 +1405,8 @@ async function mergeWithExisting(collection, store, record, itemId) {
1256
1405
  let existing;
1257
1406
  try {
1258
1407
  existing = await store.read(itemId);
1259
- } catch {
1408
+ } catch (err) {
1409
+ if (isBackendUnavailable(err)) throw err;
1260
1410
  return `'${itemId}' has a malformed stored file — mode "merge" needs to read it; fix the file (Read → correct → Write) or replace it whole with "upsert"`;
1261
1411
  }
1262
1412
  if (!existing) return `'${itemId}' not found — mode "merge" updates an existing record; use "upsert" or "create" to add it`;
@@ -1388,14 +1538,17 @@ async function handleGetOntology(deps) {
1388
1538
  collections
1389
1539
  });
1390
1540
  }
1391
- /** Return the collection-authoring reference (`collection-skills.md`).
1392
- * Workspace copy first (reflects user edits), bundled asset as the
1393
- * always-present fallback. Both reads guarded; if neither resolves the
1394
- * agent still gets an actionable message instead of a thrown call. */
1395
- async function handleSchemaDocs(deps) {
1541
+ /** Return the collection-authoring reference (`collection-skills.md`),
1542
+ * rendered by `renderSchemaDocs` the full doc overflows the agent's
1543
+ * per-result limit, so the default reply is the core guide + a table of
1544
+ * contents, and `topic` fetches individual sections. Workspace copy
1545
+ * first (reflects user edits), bundled asset as the always-present
1546
+ * fallback. Both reads guarded; if neither resolves the agent still
1547
+ * gets an actionable message instead of a thrown call. */
1548
+ async function handleSchemaDocs(deps, topic) {
1396
1549
  const candidates = [path.join(resolveBase(deps), HELPS_DIR, SCHEMA_DOCS_FILE), ...deps.bundledHelpsDir ? [path.join(deps.bundledHelpsDir(), SCHEMA_DOCS_FILE)] : []];
1397
1550
  for (const candidate of candidates) try {
1398
- return await readFile(candidate, "utf-8");
1551
+ return renderSchemaDocs(await readFile(candidate, "utf-8"), topic);
1399
1552
  } catch {}
1400
1553
  return `manageCollection: could not read the collection-authoring reference (${SCHEMA_DOCS_FILE}).`;
1401
1554
  }
@@ -1474,7 +1627,7 @@ async function handlePutSchema(slug, schemaArg, deps) {
1474
1627
  written: true
1475
1628
  });
1476
1629
  }
1477
- var MANAGE_COLLECTION_PROMPT = "Use `manageCollection` instead of raw Read/Write/Edit when working with a collection's records OR its schema (raw file I/O stays available as the escape hatch). Before authoring or changing a collection's `schema.json`, call `schemaDocs` to load the field/DSL reference, then read with `getSchema` and write with `putSchema` — `putSchema` validates the whole schema before writing and returns actionable errors instead of silently failing discovery's validation. `getItems` is the only way to see computed values — `derived` fields (e.g. a portfolio's value), `toggle` projections, and `embed` records are host-computed and never present in the stored JSON files. On large collections pass `ids` and/or `fields` to keep the result small. For a question that spans collections (\"which clients have unpaid invoices?\"), start with `getOntology`: it lists every collection with its primaryKey, record count, and outbound `ref`/`embed` relations, so you know which collections to join before reading any records. `putItems` validates every row against the schema before writing (required fields, enum values, primaryKey = record id) and returns `{ written, rejected }`; fix each rejected row using its `problem` text and retry just those rows. Never include computed fields in a row you write. To update a few fields of an existing record, use `mode: \"merge\"` with a partial row ({ id, <changed fields> }) — the default upsert replaces the WHOLE record, so a partial upsert would silently erase every optional field it omits. `deleteItems` removes records by id and returns `{ deleted, rejected }`; an id that doesn't exist comes back rejected rather than counted as deleted, so check `rejected` before reporting a deletion as done. Answer aggregation questions (counts, sums, averages, group-bys) with `queryItems` on ANY collection — on a dataSource (CSV) collection it scans the whole file (getItems is row-capped, so aggregates computed from its output can be silently wrong on large files); on a file-backed collection it aggregates the enriched records, so computed fields (derived/rollup/toggle) are queryable columns.";
1630
+ var MANAGE_COLLECTION_PROMPT = "Use `manageCollection` instead of raw Read/Write/Edit when working with a collection's records OR its schema (raw file I/O stays available as the escape hatch). Before authoring or changing a collection's `schema.json`, call `schemaDocs` to load the field/DSL reference — the default reply is the core authoring guide plus a table of contents; fetch advanced sections (actions, bells, calendar/kanban views, dataSource, storage) by passing their heading as `topic` rather than dumping `topic: \"all\"`. Then read with `getSchema` and write with `putSchema` — `putSchema` validates the whole schema before writing and returns actionable errors instead of silently failing discovery's validation. `getItems` is the only way to see computed values — `derived` fields (e.g. a portfolio's value), `toggle` projections, and `embed` records are host-computed and never present in the stored JSON files. On large collections pass `ids` and/or `fields` to keep the result small. For a question that spans collections (\"which clients have unpaid invoices?\"), start with `getOntology`: it lists every collection with its primaryKey, record count, and outbound `ref`/`embed` relations, so you know which collections to join before reading any records. `putItems` validates every row against the schema before writing (required fields, enum values, primaryKey = record id) and returns `{ written, rejected }`; fix each rejected row using its `problem` text and retry just those rows. Never include computed fields in a row you write. To update a few fields of an existing record, use `mode: \"merge\"` with a partial row ({ id, <changed fields> }) — the default upsert replaces the WHOLE record, so a partial upsert would silently erase every optional field it omits. `deleteItems` removes records by id and returns `{ deleted, rejected }`; an id that doesn't exist comes back rejected rather than counted as deleted, so check `rejected` before reporting a deletion as done. Answer aggregation questions (counts, sums, averages, group-bys) with `queryItems` on ANY collection — on a dataSource (CSV) collection it scans the whole file (getItems is row-capped, so aggregates computed from its output can be silently wrong on large files); on a file-backed collection it aggregates the enriched records, so computed fields (derived/rollup/toggle) are queryable columns.";
1478
1631
  /** Validate getItems' optional `ids`/`fields` args, then delegate. */
1479
1632
  async function dispatchGetItems(collection, args, deps) {
1480
1633
  const ids = optionalStringArray(args.ids, "ids");
@@ -1508,8 +1661,16 @@ async function dispatchRecordAction(action, collection, args, deps) {
1508
1661
  return typeof parsed === "string" ? parsed : handlePutItems(collection, parsed, deps);
1509
1662
  }
1510
1663
  async function manageCollectionHandler(deps, args) {
1664
+ try {
1665
+ return await dispatchManageCollection(deps, args);
1666
+ } catch (err) {
1667
+ if (isBackendUnavailable(err)) return `manageCollection: ${err.message}`;
1668
+ throw err;
1669
+ }
1670
+ }
1671
+ async function dispatchManageCollection(deps, args) {
1511
1672
  const action = typeof args.action === "string" ? args.action : "";
1512
- if (action === "schemaDocs") return handleSchemaDocs(deps);
1673
+ if (action === "schemaDocs") return handleSchemaDocs(deps, typeof args.topic === "string" ? args.topic : void 0);
1513
1674
  if (action === "getOntology") return handleGetOntology(deps);
1514
1675
  const slug = typeof args.slug === "string" ? args.slug.trim() : "";
1515
1676
  if (!slug) return "manageCollection: `slug` is required (the collection's slug).";
@@ -1522,7 +1683,7 @@ async function manageCollectionHandler(deps, args) {
1522
1683
  }
1523
1684
  var MANAGE_COLLECTION_DEFINITION = {
1524
1685
  name: "manageCollection",
1525
- description: "Read and write a schema-driven collection through the host — both its records and its structure. getItems returns records WITH computed values (derived formulas, toggles, embeds) the stored JSON files don't contain; putItems validates each row against the schema before writing; deleteItems removes records by id. getOntology maps the whole workspace: every collection with its record count and outbound ref/embed relations — call it first for cross-collection questions. schemaDocs returns the collection-authoring reference; getSchema/putSchema read and validate-then-write the collection's schema.json. Prefer it over raw file I/O on collections.",
1686
+ description: "Read and write a schema-driven collection through the host — both its records and its structure. getItems returns records WITH computed values (derived formulas, toggles, embeds) the stored JSON files don't contain; putItems validates each row against the schema before writing; deleteItems removes records by id. getOntology maps the whole workspace: every collection with its record count and outbound ref/embed relations — call it first for cross-collection questions. schemaDocs returns the collection-authoring reference — the core guide plus a table of contents by default; pass `topic` for a specific section. getSchema/putSchema read and validate-then-write the collection's schema.json. Prefer it over raw file I/O on collections.",
1526
1687
  inputSchema: {
1527
1688
  type: "object",
1528
1689
  properties: {
@@ -1572,6 +1733,10 @@ var MANAGE_COLLECTION_DEFINITION = {
1572
1733
  type: "object",
1573
1734
  description: "putSchema: the full collection schema object (same shape as schema.json — title, icon, dataPath, primaryKey, fields, …). Call getSchema first for the current one, and schemaDocs for the field DSL."
1574
1735
  },
1736
+ topic: {
1737
+ type: "string",
1738
+ description: "schemaDocs: fetch one section of the reference by heading (case-insensitive substring — e.g. \"field types\", \"kanban\", \"calendar\", \"dataSource\"). Omit for the core authoring guide plus a table of contents of every section; \"all\" returns the full document (large — it can exceed your tool-result limit)."
1739
+ },
1575
1740
  query: {
1576
1741
  type: "object",
1577
1742
  description: "queryItems: a structured aggregation query — `{ groupBy?: [\"col\"], aggregates?: { alias: { op: \"count\"|\"sum\"|\"avg\"|\"min\"|\"max\", column? } }, where?: [{ field, op, value }], orderBy?: [{ field, dir? }], limit? }`. At least one of groupBy/aggregates. On a dataSource (CSV) collection it scans the WHOLE file uncapped (unlike getItems); on a file-backed collection it aggregates the ENRICHED records, so computed fields (derived/rollup/toggle) are queryable columns. Use it for counts / sums / averages / group-bys instead of arithmetic over getItems output."
@@ -1591,4 +1756,4 @@ function makeManageCollectionTool(deps = {}) {
1591
1756
  //#endregion
1592
1757
  export { validateRecordObject as C, enrichItems as D, runCollectionQuery as E, runQueryOverRows as O, validateCollectionRecords as S, recordFieldProblem as T, computeCollectionIcon as _, deleteCollection as a, applyMutateAction as b, computeSuccessor as c, isTriggerDue as d, maybeSpawnSuccessor as f, ONE_SECOND_MS as g, successorId as h, deleteCustomView as i, daysInMonth as l, resolveEvery as m, MAX_UNSELECTIVE_ITEMS as n, deleteCollectionRefusalMessage as o, parseCivil as p, makeManageCollectionTool as r, advanceTriggerDate as s, MAX_SCHEMA_ISSUES as t, formatCivil as u, buildWorkspaceOntology as v, compileRecordZ as w, firstMutateParamProblem as x, schemaRelations as y };
1593
1758
 
1594
- //# sourceMappingURL=server-B8mvfZSF.js.map
1759
+ //# sourceMappingURL=server-DlG6ydHO.js.map