@withone/cli 1.56.1 → 1.57.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -35,7 +35,7 @@ import {
35
35
  } from "./chunk-QCXFSOTS.js";
36
36
  import {
37
37
  memSqlCommand
38
- } from "./chunk-KAF5SVJS.js";
38
+ } from "./chunk-GKAIRI4Y.js";
39
39
  import {
40
40
  collectIdentityKeys,
41
41
  countRecords,
@@ -66,7 +66,7 @@ import {
66
66
  writeDraftProfile,
67
67
  writePageToMemory,
68
68
  writeProfile
69
- } from "./chunk-XWTV7FA3.js";
69
+ } from "./chunk-YTM6HJKA.js";
70
70
  import {
71
71
  getByDotPath
72
72
  } from "./chunk-44CV5IMX.js";
@@ -100,7 +100,7 @@ import {
100
100
  listBackendPlugins,
101
101
  loadBackendFromConfig,
102
102
  updateRecord
103
- } from "./chunk-VH46MAE4.js";
103
+ } from "./chunk-IMU2X3ZV.js";
104
104
  import {
105
105
  DEFAULT_MEMORY_CONFIG,
106
106
  defaultSearchableText,
@@ -3109,6 +3109,391 @@ async function platformsCommand(options) {
3109
3109
  // src/commands/actions.ts
3110
3110
  import * as p6 from "@clack/prompts";
3111
3111
  import pc8 from "picocolors";
3112
+
3113
+ // src/lib/knowledge-sections.ts
3114
+ var DEFAULT_WHOLE_DOC_THRESHOLD = 8e3;
3115
+ var DEFAULT_DIGEST_BUDGET = 8e3;
3116
+ var DEFAULT_SECTION_CAP = 6e3;
3117
+ var HEADING_RE = /^(#{1,6})[ \t]+(.+?)[ \t]*#*[ \t]*$/;
3118
+ var FENCE_RE = /^\s*(```|~~~)/;
3119
+ function slugify(heading) {
3120
+ return heading.toLowerCase().replace(/`/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "section";
3121
+ }
3122
+ function parseSections(markdown) {
3123
+ const normalized = markdown.replace(/\r\n?/g, "\n");
3124
+ const lines = normalized.split("\n");
3125
+ const prefaceLines = [];
3126
+ const blocks = [];
3127
+ let inFence = false;
3128
+ let fenceMarker = "";
3129
+ for (const line of lines) {
3130
+ const fence = FENCE_RE.exec(line);
3131
+ if (fence) {
3132
+ if (!inFence) {
3133
+ inFence = true;
3134
+ fenceMarker = fence[1];
3135
+ } else if (fence[1] === fenceMarker) {
3136
+ inFence = false;
3137
+ }
3138
+ }
3139
+ const m = !inFence ? HEADING_RE.exec(line) : null;
3140
+ if (m) {
3141
+ blocks.push({ level: m[1].length, heading: m[2].trim(), lines: [line] });
3142
+ continue;
3143
+ }
3144
+ if (blocks.length === 0) prefaceLines.push(line);
3145
+ else blocks[blocks.length - 1].lines.push(line);
3146
+ }
3147
+ const usedIds = /* @__PURE__ */ new Set();
3148
+ const nodes = blocks.map((b) => {
3149
+ const base = slugify(b.heading);
3150
+ let id = base;
3151
+ for (let n = 2; usedIds.has(id); n++) id = `${base}-${n}`;
3152
+ usedIds.add(id);
3153
+ return {
3154
+ id,
3155
+ heading: b.heading,
3156
+ level: b.level,
3157
+ text: b.lines.join("\n"),
3158
+ chars: 0,
3159
+ tier: classifyHeading(b.heading),
3160
+ children: []
3161
+ };
3162
+ });
3163
+ const root = [];
3164
+ const stack = [];
3165
+ let title = "";
3166
+ let titleNode = null;
3167
+ for (const node of nodes) {
3168
+ while (stack.length && stack[stack.length - 1].level >= node.level) stack.pop();
3169
+ if (stack.length) stack[stack.length - 1].children.push(node);
3170
+ else root.push(node);
3171
+ stack.push(node);
3172
+ }
3173
+ if (root.length && nodes[0].level === 1 && root[0] === nodes[0]) {
3174
+ titleNode = root.shift();
3175
+ title = titleNode.heading;
3176
+ root.unshift(...titleNode.children);
3177
+ titleNode.children = [];
3178
+ }
3179
+ let preface = prefaceLines.join("\n").trim();
3180
+ if (titleNode) {
3181
+ const own = titleNode.text.split("\n").slice(1).join("\n").trim();
3182
+ if (own) preface = preface ? `${preface}
3183
+
3184
+ ${own}` : own;
3185
+ }
3186
+ const measure = (n) => {
3187
+ n.chars = n.text.length + n.children.reduce((sum, c) => sum + measure(c), 0);
3188
+ return n.chars;
3189
+ };
3190
+ root.forEach(measure);
3191
+ return { title, preface, sections: root, chars: normalized.length };
3192
+ }
3193
+ function sectionText(section) {
3194
+ const parts = [section.text, ...section.children.map(sectionText)];
3195
+ return parts.join("\n").replace(/\n+$/, "");
3196
+ }
3197
+ function flattenSections(sections) {
3198
+ const out = [];
3199
+ const walk = (list) => {
3200
+ for (const s of list) {
3201
+ out.push(s);
3202
+ walk(s.children);
3203
+ }
3204
+ };
3205
+ walk(sections);
3206
+ return out;
3207
+ }
3208
+ function canonicalHeading(heading) {
3209
+ return heading.toLowerCase().replace(/\s*\([^)]*\)\s*$/, "").replace(/[`*_]/g, "").replace(/\s+/g, " ").trim();
3210
+ }
3211
+ var ESSENTIAL_EXACT = /* @__PURE__ */ new Set([
3212
+ "prerequisites",
3213
+ "method",
3214
+ "url",
3215
+ "endpoint",
3216
+ "headers",
3217
+ "description",
3218
+ "enforcement rules",
3219
+ "valid examples",
3220
+ "invalid examples",
3221
+ "request body",
3222
+ "sample request",
3223
+ "gotchas",
3224
+ "error handling",
3225
+ "graphql operation",
3226
+ "graphql arguments",
3227
+ "request variables",
3228
+ "variables",
3229
+ "path parameters",
3230
+ "query parameters",
3231
+ "parameters",
3232
+ "arguments"
3233
+ ]);
3234
+ var ESSENTIAL_PREFIX = ["required "];
3235
+ function classifyHeading(heading) {
3236
+ const c = canonicalHeading(heading);
3237
+ if (ESSENTIAL_EXACT.has(c)) return "essential";
3238
+ if (ESSENTIAL_PREFIX.some((p10) => c.startsWith(p10))) return "essential";
3239
+ return "deferred";
3240
+ }
3241
+ function deferredPriority(heading) {
3242
+ const c = canonicalHeading(heading);
3243
+ if (c.startsWith("optional ")) return 0;
3244
+ if (c === "behavior" || c === "behaviour") return 1;
3245
+ if (c === "notes") return 2;
3246
+ if (c === "example usage" || c.startsWith("example")) return 3;
3247
+ if (c === "response fields") return 5;
3248
+ if (c === "response" || c.startsWith("success response") || c.startsWith("error response")) return 6;
3249
+ return 4;
3250
+ }
3251
+ function truncateText(text5, cap, id) {
3252
+ const cut = text5.slice(0, cap);
3253
+ const nl = cut.lastIndexOf("\n");
3254
+ const head = nl > cap * 0.5 ? cut.slice(0, nl) : cut;
3255
+ const remaining = text5.length - head.length;
3256
+ return `${head}
3257
+
3258
+ _[truncated \u2014 ${remaining.toLocaleString("en-US")} more chars. Load the full section with --section ${id}]_`;
3259
+ }
3260
+ function buildDigest(doc, opts = {}) {
3261
+ const wholeThreshold = opts.wholeDocThreshold ?? DEFAULT_WHOLE_DOC_THRESHOLD;
3262
+ const budget = opts.budget ?? DEFAULT_DIGEST_BUDGET;
3263
+ const cap = opts.sectionCap ?? DEFAULT_SECTION_CAP;
3264
+ const flat = flattenSections(doc.sections);
3265
+ const whole = () => ({
3266
+ markdown: renderWhole(doc),
3267
+ truncated: false,
3268
+ sections: flat.map((s) => summary(s, true)),
3269
+ omitted: 0,
3270
+ omittedChars: 0
3271
+ });
3272
+ if (flat.length === 0 || doc.chars <= wholeThreshold) return whole();
3273
+ const decisions = /* @__PURE__ */ new Map();
3274
+ let used = doc.title ? doc.title.length + 4 : 0;
3275
+ used += doc.preface.length;
3276
+ const hasEssentialDescendant = (n) => n.children.some((c) => c.tier === "essential" || hasEssentialDescendant(c));
3277
+ const walk = (list) => {
3278
+ for (const node of list) {
3279
+ if (node.tier === "essential" || node.level >= 3 && hasEssentialDescendant(node)) {
3280
+ const own = node.text;
3281
+ if (own.length > cap) {
3282
+ const render = truncateText(own, cap, node.id);
3283
+ decisions.set(node.id, { node, included: "partial", render });
3284
+ used += render.length;
3285
+ } else {
3286
+ decisions.set(node.id, { node, included: true, render: own });
3287
+ used += own.length;
3288
+ }
3289
+ walk(node.children);
3290
+ } else {
3291
+ decisions.set(node.id, { node, included: false, render: "" });
3292
+ for (const c of flattenSections(node.children)) decisions.set(c.id, { node: c, included: false, render: "" });
3293
+ }
3294
+ }
3295
+ };
3296
+ walk(doc.sections);
3297
+ const candidates = flat.filter((s) => decisions.get(s.id)?.included === false).filter((s) => s.level >= 2).filter((s) => {
3298
+ const parent = parentOf(doc.sections, s);
3299
+ return !parent || decisions.get(parent.id)?.included !== false;
3300
+ }).sort((a, b) => deferredPriority(a.heading) - deferredPriority(b.heading) || a.chars - b.chars);
3301
+ for (const s of candidates) {
3302
+ if (used + s.chars > budget) continue;
3303
+ for (const n of [s, ...flattenSections(s.children)]) {
3304
+ decisions.set(n.id, { node: n, included: true, render: n.text });
3305
+ }
3306
+ used += s.chars;
3307
+ }
3308
+ const allIn = flat.every((s) => decisions.get(s.id)?.included === true);
3309
+ if (allIn) return whole();
3310
+ const out = [];
3311
+ if (doc.title) out.push(`# ${doc.title}`);
3312
+ if (doc.preface) out.push(doc.preface);
3313
+ for (const s of flat) {
3314
+ const d = decisions.get(s.id);
3315
+ if (d.included !== false) out.push(d.render.replace(/\n+$/, ""));
3316
+ }
3317
+ const sections = flat.map((s) => summary(s, decisions.get(s.id).included));
3318
+ const topOmitted = flat.filter((s) => {
3319
+ if (decisions.get(s.id).included !== false) return false;
3320
+ const parent = parentOf(doc.sections, s);
3321
+ return !parent || decisions.get(parent.id).included !== false;
3322
+ });
3323
+ const omittedChars = topOmitted.reduce((sum, s) => sum + s.chars, 0) + flat.filter((s) => decisions.get(s.id).included === "partial").reduce((sum, s) => sum + (s.text.length - decisions.get(s.id).render.length), 0);
3324
+ return {
3325
+ markdown: out.join("\n\n"),
3326
+ truncated: true,
3327
+ sections,
3328
+ omitted: topOmitted.length + flat.filter((s) => decisions.get(s.id).included === "partial").length,
3329
+ omittedChars: Math.max(0, omittedChars)
3330
+ };
3331
+ }
3332
+ function summary(s, included) {
3333
+ return { id: s.id, heading: s.heading, level: s.level, chars: s.chars, included };
3334
+ }
3335
+ function parentOf(roots, target) {
3336
+ const find = (list, parent) => {
3337
+ for (const n of list) {
3338
+ if (n === target) return parent;
3339
+ const r = find(n.children, n);
3340
+ if (r !== void 0) return r;
3341
+ }
3342
+ return void 0;
3343
+ };
3344
+ return find(roots, null) ?? null;
3345
+ }
3346
+ function renderWhole(doc) {
3347
+ const out = [];
3348
+ if (doc.title) out.push(`# ${doc.title}`);
3349
+ if (doc.preface) out.push(doc.preface);
3350
+ for (const s of doc.sections) out.push(sectionText(s));
3351
+ return out.join("\n\n");
3352
+ }
3353
+ var DEFAULT_TOC_MAX_ENTRIES = 60;
3354
+ function collapseSections(sections, max = DEFAULT_TOC_MAX_ENTRIES) {
3355
+ if (sections.length <= max) return { sections, collapsed: false };
3356
+ const firstH1 = sections.findIndex((s) => s.level === 1);
3357
+ const canonEnd = firstH1 === -1 ? sections.length : firstH1;
3358
+ for (const depth of [3, 2]) {
3359
+ const keep = (s, i) => i < canonEnd ? s.level <= depth : s.level <= 1;
3360
+ const count = sections.filter(keep).length;
3361
+ if (count > max && depth !== 2) continue;
3362
+ const out = [];
3363
+ let owner = null;
3364
+ sections.forEach((s, i) => {
3365
+ if (keep(s, i)) {
3366
+ owner = { ...s };
3367
+ out.push(owner);
3368
+ } else if (owner) {
3369
+ owner.children = (owner.children ?? 0) + 1;
3370
+ }
3371
+ });
3372
+ return { sections: out, collapsed: true };
3373
+ }
3374
+ return { sections, collapsed: false };
3375
+ }
3376
+ function omittedSections(digest, max = DEFAULT_TOC_MAX_ENTRIES) {
3377
+ return collapseSections(digest.sections.filter((s) => s.included !== true), max);
3378
+ }
3379
+ function renderDigestNotice(digest, platform, actionId) {
3380
+ if (!digest.truncated) return "";
3381
+ const topMost = digest.sections.filter((s, idx) => {
3382
+ if (s.included === true) return false;
3383
+ for (let i = idx - 1; i >= 0; i--) {
3384
+ const prev = digest.sections[i];
3385
+ if (prev.level < s.level) return prev.included !== false;
3386
+ }
3387
+ return true;
3388
+ });
3389
+ const names = topMost.map((s) => {
3390
+ if (s.included === "partial") return `${s.heading} (partial)`;
3391
+ if (s.level === 1) return `${s.heading} (appendix)`;
3392
+ return s.heading;
3393
+ });
3394
+ const base = `one --agent actions knowledge ${platform} ${actionId}`;
3395
+ const MAX_NAMES = 12;
3396
+ const shown = names.slice(0, MAX_NAMES);
3397
+ const rest = names.length - shown.length;
3398
+ const nameList = rest > 0 ? `${shown.join(", ")}, and ${rest} more (see --toc)` : shown.join(", ");
3399
+ return [
3400
+ "---",
3401
+ `**This is a digest, not the full document.** ${topMost.length} section${topMost.length === 1 ? "" : "s"} omitted (${digest.omittedChars.toLocaleString("en-US")} chars): ${nameList}.`,
3402
+ `Load one or more by name or id: ${base} --section "${topMost[0]?.heading ?? "Response"}"`,
3403
+ `Load everything: ${base} --full`
3404
+ ].join("\n");
3405
+ }
3406
+ function renderDigestBanner(digest) {
3407
+ if (!digest.truncated) return "";
3408
+ return `> **Digest.** ${digest.omitted} section${digest.omitted === 1 ? "" : "s"} (${digest.omittedChars.toLocaleString("en-US")} chars) omitted: response shapes, reference schemas, or worked examples. The notice at the end names them and shows how to load them; the JSON \`sections\` array marks them included:false.`;
3409
+ }
3410
+ var ALIASES = {
3411
+ response: (c) => c === "response",
3412
+ responses: (c) => c === "response",
3413
+ "response fields": (c) => c === "response fields",
3414
+ fields: (c) => c === "response fields",
3415
+ optional: (c) => c.startsWith("optional "),
3416
+ required: (c) => c.startsWith("required "),
3417
+ examples: (c) => c === "example usage" || c === "examples",
3418
+ example: (c) => c === "example usage" || c === "examples",
3419
+ errors: (c) => c.startsWith("error response") || c === "error handling",
3420
+ success: (c) => c.startsWith("success response"),
3421
+ body: (c) => c === "request body" || c.endsWith("request body fields"),
3422
+ query: (c) => c.endsWith("query parameters"),
3423
+ path: (c) => c.endsWith("path parameters"),
3424
+ notes: (c) => c === "notes",
3425
+ behavior: (c) => c === "behavior" || c === "behaviour",
3426
+ gotchas: (c) => c === "gotchas"
3427
+ };
3428
+ function normalizeQuery(q) {
3429
+ return q.trim().replace(/^["'`]+|["'`]+$/g, "").toLowerCase().replace(/\s+/g, " ");
3430
+ }
3431
+ function findSections(doc, query) {
3432
+ const flat = flattenSections(doc.sections);
3433
+ const q = normalizeQuery(query);
3434
+ const qSlug = slugify(q);
3435
+ const miss = (reason, candidates) => ({
3436
+ ok: false,
3437
+ reason,
3438
+ query,
3439
+ candidates: candidates.map((s) => summary(s, false))
3440
+ });
3441
+ if (!q) return miss("not-found", flat);
3442
+ const exact = flat.filter(
3443
+ (s) => s.id === qSlug || canonicalHeading(s.heading) === q || s.heading.toLowerCase() === q
3444
+ );
3445
+ if (exact.length) return { ok: true, sections: exact };
3446
+ const alias = ALIASES[q];
3447
+ if (alias) {
3448
+ const hits = flat.filter((s) => alias(canonicalHeading(s.heading)));
3449
+ if (hits.length) return { ok: true, sections: dropNested(hits) };
3450
+ }
3451
+ const byPrefix = flat.filter(
3452
+ (s) => s.id.startsWith(qSlug) || canonicalHeading(s.heading).startsWith(q)
3453
+ );
3454
+ if (byPrefix.length === 1) return { ok: true, sections: byPrefix };
3455
+ if (byPrefix.length > 1) {
3456
+ const sameCanon = new Set(byPrefix.map((s) => canonicalHeading(s.heading).replace(/\s*\d.*$/, "")));
3457
+ if (sameCanon.size === 1) return { ok: true, sections: dropNested(byPrefix) };
3458
+ return miss("ambiguous", byPrefix);
3459
+ }
3460
+ const bySubstring = flat.filter(
3461
+ (s) => s.id.includes(qSlug) || canonicalHeading(s.heading).includes(q)
3462
+ );
3463
+ if (bySubstring.length === 1) return { ok: true, sections: bySubstring };
3464
+ if (bySubstring.length > 1) return miss("ambiguous", bySubstring);
3465
+ return miss("not-found", flat);
3466
+ }
3467
+ function dropNested(hits) {
3468
+ const inside = /* @__PURE__ */ new Set();
3469
+ for (const h of hits) for (const c of flattenSections(h.children)) inside.add(c.id);
3470
+ return hits.filter((h) => !inside.has(h.id));
3471
+ }
3472
+ function selectSections(doc, names) {
3473
+ const picked = [];
3474
+ const seen = /* @__PURE__ */ new Set();
3475
+ for (const name of names) {
3476
+ const r = findSections(doc, name);
3477
+ if (!r.ok) return r;
3478
+ for (const s of dropNested(r.sections)) {
3479
+ if (!seen.has(s.id)) {
3480
+ seen.add(s.id);
3481
+ picked.push(s);
3482
+ }
3483
+ }
3484
+ }
3485
+ const order = new Map(flattenSections(doc.sections).map((s, i) => [s.id, i]));
3486
+ const final = dropNested(picked).sort((a, b) => order.get(a.id) - order.get(b.id));
3487
+ const parts = [doc.title ? `# ${doc.title}` : "", ...final.map(sectionText)].filter(Boolean);
3488
+ return { ok: true, sections: final, markdown: parts.join("\n\n") };
3489
+ }
3490
+ function parseSectionFlag(value) {
3491
+ if (!value) return [];
3492
+ const raw = Array.isArray(value) ? value : [value];
3493
+ return raw.flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
3494
+ }
3495
+
3496
+ // src/commands/actions.ts
3112
3497
  function getConfig() {
3113
3498
  const apiKey = getApiKey();
3114
3499
  if (!apiKey) {
@@ -3306,8 +3691,90 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
3306
3691
  knowledge: details.knowledge || "No knowledge was found",
3307
3692
  method: details.method || "No method was found"
3308
3693
  };
3694
+ const doc = parseSections(knowledgeData.knowledge);
3695
+ const sectionNames = parseSectionFlag(options.section);
3696
+ const wantFull = options.full === true || sectionNames.some((n) => n.toLowerCase() === "all");
3697
+ const base = `one --agent actions knowledge ${platform} ${actionId}`;
3698
+ const fullToc = flattenSections(doc.sections).map((s) => ({ id: s.id, heading: s.heading, level: s.level, chars: s.chars }));
3699
+ if (options.toc) {
3700
+ if (isAgentMode()) {
3701
+ json({
3702
+ title: doc.title,
3703
+ method: knowledgeData.method,
3704
+ chars: doc.chars,
3705
+ sections: fullToc,
3706
+ more: { section: `${base} --section <id or heading>[,<id or heading>...]`, full: `${base} --full` },
3707
+ _cache: buildCacheMeta(entry, cacheHit)
3708
+ });
3709
+ return;
3710
+ }
3711
+ spinner4.stop("Sections");
3712
+ console.log();
3713
+ for (const t of fullToc) console.log(`${" ".repeat(Math.max(0, t.level - 1))}${t.heading} ${pc8.dim(`[${t.id}] ${t.chars} chars`)}`);
3714
+ console.log();
3715
+ return;
3716
+ }
3717
+ if (sectionNames.length > 0 && !wantFull) {
3718
+ const picked = selectSections(doc, sectionNames);
3719
+ if (!picked.ok) {
3720
+ const toc = collapseSections(picked.candidates);
3721
+ const list = toc.sections.map((c) => `${" ".repeat(Math.max(0, c.level - 2))}${c.heading} [${c.id}] (${c.chars} chars${c.children ? `, +${c.children} nested` : ""})`).join("\n");
3722
+ if (isAgentMode()) {
3723
+ json({
3724
+ error: picked.reason === "ambiguous" ? `Section "${picked.query}" matches several sections; pick one by id or full heading.` : `No section named "${picked.query}". Use one of the ids or headings below, or --full.`,
3725
+ query: picked.query,
3726
+ reason: picked.reason,
3727
+ sections: toc.sections.map(({ id, heading, level, chars, children }) => children ? { id, heading, level, chars, children } : { id, heading, level, chars }),
3728
+ ...toc.collapsed ? { sectionsCollapsed: true, sectionCount: picked.candidates.length } : {},
3729
+ hint: `${base} --section <id or heading>`,
3730
+ toc: `${base} --toc`
3731
+ });
3732
+ process.exit(1);
3733
+ }
3734
+ spinner4.stop("Section not found");
3735
+ error(`${picked.reason === "ambiguous" ? "Ambiguous" : "Unknown"} section "${picked.query}". Available:
3736
+ ${list}`);
3737
+ }
3738
+ if (isAgentMode()) {
3739
+ json({
3740
+ knowledge: picked.markdown,
3741
+ method: knowledgeData.method,
3742
+ title: doc.title,
3743
+ // The requested sections are returned whole; nothing here was cut.
3744
+ truncated: false,
3745
+ requested: sectionNames,
3746
+ // What each name matched — fuzzy and alias matches are visible here.
3747
+ resolved: picked.sections.map((s) => ({ id: s.id, heading: s.heading, chars: s.chars })),
3748
+ sectionCount: fullToc.length,
3749
+ more: {
3750
+ note: "Only the requested sections are included. The digest call listed the others; `--toc` lists all of them.",
3751
+ section: `${base} --section <id or heading>[,<id or heading>...]`,
3752
+ toc: `${base} --toc`,
3753
+ full: `${base} --full`
3754
+ },
3755
+ _cache: buildCacheMeta(entry, cacheHit)
3756
+ });
3757
+ return;
3758
+ }
3759
+ spinner4.stop("Sections loaded");
3760
+ console.log();
3761
+ console.log(picked.markdown);
3762
+ console.log();
3763
+ return;
3764
+ }
3765
+ const digest = buildDigest(
3766
+ doc,
3767
+ isAgentMode() && !wantFull ? {} : { wholeDocThreshold: Number.POSITIVE_INFINITY }
3768
+ );
3769
+ const body = doc.sections.length === 0 ? knowledgeData.knowledge : digest.markdown;
3770
+ const notice = renderDigestNotice(digest, platform, actionId);
3771
+ const banner = renderDigestBanner(digest);
3309
3772
  const knowledgeWithGuidance = buildActionKnowledgeWithGuidance(
3310
- knowledgeData.knowledge,
3773
+ notice ? `${banner}
3774
+
3775
+ ${body}
3776
+
3777
+ ${notice}` : body,
3311
3778
  knowledgeData.method,
3312
3779
  platform,
3313
3780
  actionId
@@ -3316,8 +3783,26 @@ async function actionsKnowledgeCommand(platform, actionId, options) {
3316
3783
  const response = {
3317
3784
  knowledge: knowledgeWithGuidance,
3318
3785
  method: knowledgeData.method,
3786
+ title: doc.title || void 0,
3787
+ truncated: digest.truncated,
3319
3788
  _cache: buildCacheMeta(entry, cacheHit)
3320
3789
  };
3790
+ if (digest.truncated) {
3791
+ const toc = omittedSections(digest);
3792
+ response.omitted = digest.omitted;
3793
+ response.omittedChars = digest.omittedChars;
3794
+ response.sections = toc.sections;
3795
+ if (toc.collapsed) {
3796
+ response.sectionsCollapsed = true;
3797
+ response.sectionCount = digest.sections.length;
3798
+ }
3799
+ response.more = {
3800
+ note: toc.collapsed ? `This is a digest. \`sections\` lists what was omitted, collapsed to ${toc.sections.length} entries (\`children\` counts nested ones); request any by id or heading, or --toc for all ${digest.sections.length} headings.` : "This is a digest. `sections` lists what was omitted; request any by id or heading.",
3801
+ section: `${base} --section <id or heading>[,<id or heading>...]`,
3802
+ toc: `${base} --toc`,
3803
+ full: `${base} --full`
3804
+ };
3805
+ }
3321
3806
  json(response);
3322
3807
  return;
3323
3808
  }
@@ -6746,7 +7231,7 @@ async function syncModel(api, profile, options) {
6746
7231
  updateModelState(platform, model, { status: "failed", pagesProcessed, lastCursor }),
6747
7232
  (async () => {
6748
7233
  try {
6749
- const { getBackend: getBackend2 } = await import("./runtime-65IPTPVD.js");
7234
+ const { getBackend: getBackend2 } = await import("./runtime-SX36HGBA.js");
6750
7235
  const backend = await getBackend2();
6751
7236
  await Promise.race([
6752
7237
  backend.close(),
@@ -7112,7 +7597,7 @@ async function syncModel(api, profile, options) {
7112
7597
  db.exec(`DROP TABLE IF EXISTS _seen_ids`);
7113
7598
  }
7114
7599
  if (options.toMemory !== false) {
7115
- const backend = await (await import("./runtime-65IPTPVD.js")).getBackend();
7600
+ const backend = await (await import("./runtime-SX36HGBA.js")).getBackend();
7116
7601
  const type = `${platform}/${model}`;
7117
7602
  const existing = await backend.listKeysByType(type);
7118
7603
  const sourcePrefix = `${type}:`;
@@ -7213,7 +7698,7 @@ async function syncModel(api, profile, options) {
7213
7698
  let statusCounts;
7214
7699
  if (options.toMemory !== false) {
7215
7700
  try {
7216
- const backend = await (await import("./runtime-65IPTPVD.js")).getBackend();
7701
+ const backend = await (await import("./runtime-SX36HGBA.js")).getBackend();
7217
7702
  const typeName = `${platform}/${model}`;
7218
7703
  const [active, archived] = await Promise.all([
7219
7704
  backend.count(typeName, { status: "active" }),
@@ -8935,7 +9420,7 @@ ${result.total} results`);
8935
9420
  }
8936
9421
  }
8937
9422
  async function syncSqlCommand(platformModel, sql) {
8938
- const { syncSqlCommand: runSyncSql } = await import("./sql-QIVDWTL3.js");
9423
+ const { syncSqlCommand: runSyncSql } = await import("./sql-SYCWUPNP.js");
8939
9424
  await runSyncSql(platformModel, sql);
8940
9425
  }
8941
9426
  async function syncDeleteCommand(platformModel, options) {
@@ -9013,7 +9498,7 @@ async function syncDeleteCommand(platformModel, options) {
9013
9498
  async function maybeAutoMigrateLegacy(platform, models) {
9014
9499
  const dbSize = getDatabaseSize(platform);
9015
9500
  if (!dbSize || dbSize === "0 B") return;
9016
- const { getBackend: getBackend2 } = await import("./runtime-65IPTPVD.js");
9501
+ const { getBackend: getBackend2 } = await import("./runtime-SX36HGBA.js");
9017
9502
  const backend = await getBackend2();
9018
9503
  let memoryHasData = false;
9019
9504
  for (const model of models) {
@@ -9029,7 +9514,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
9029
9514
  ` detected legacy .one/sync/data/${platform}.db (${dbSize}) \u2014 auto-migrating into memory before sync.
9030
9515
  `
9031
9516
  );
9032
- const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-IJI3BTWL.js");
9517
+ const { memMigrateCommand: memMigrateCommand3 } = await import("./migrate-TACDLGPD.js");
9033
9518
  await memMigrateCommand3({ platform, yes: true });
9034
9519
  return;
9035
9520
  }
@@ -9038,7 +9523,7 @@ async function maybeAutoMigrateLegacy(platform, models) {
9038
9523
  initialValue: true
9039
9524
  });
9040
9525
  if (p7.isCancel(shouldMigrate) || !shouldMigrate) return;
9041
- const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-IJI3BTWL.js");
9526
+ const { memMigrateCommand: memMigrateCommand2 } = await import("./migrate-TACDLGPD.js");
9042
9527
  await memMigrateCommand2({ platform, yes: true });
9043
9528
  }
9044
9529
  async function syncSuggestSearchableCommand(platformModel, options = {}) {
@@ -9099,7 +9584,7 @@ async function syncSuggestSearchableCommand(platformModel, options = {}) {
9099
9584
  async function syncListCommand(platform) {
9100
9585
  const profiles = listProfiles(platform);
9101
9586
  const state = await readSyncState();
9102
- const { getBackend: getBackend2 } = await import("./runtime-65IPTPVD.js");
9587
+ const { getBackend: getBackend2 } = await import("./runtime-SX36HGBA.js");
9103
9588
  const backend = await getBackend2();
9104
9589
  const syncs = await Promise.all(profiles.map(async (p10) => {
9105
9590
  const modelState = state[p10.platform]?.[p10.model];
@@ -9375,7 +9860,7 @@ function registerSyncSubcommands(sync) {
9375
9860
  await syncSqlCommand(platformModel, sql);
9376
9861
  });
9377
9862
  sync.command("schema <platform/model>").description("Inspect the JSON structure of synced records (field paths, types, examples) \u2014 useful before writing `sync sql` queries").action(async (platformModel) => {
9378
- const { syncSchemaCommand } = await import("./schema-OO4WK5ND.js");
9863
+ const { syncSchemaCommand } = await import("./schema-RJC2WQQ6.js");
9379
9864
  await syncSchemaCommand(platformModel);
9380
9865
  });
9381
9866
  sync.command("delete <platform/model>").description('Delete records from local sync data (e.g. one sync delete notion/pages --id "abc-123")').option("--id <value>", "Delete record by ID").option("--where <conditions>", 'Delete records matching conditions (e.g. "status=archived")').option("--where-sql <predicate>", `Delete using a raw SQL WHERE clause (e.g. "json_extract(data, '$.type') = 'promotion'")`).option("--yes", "Skip confirmation prompt").action(async (platformModel, options) => {
@@ -10833,7 +11318,43 @@ one --agent actions search <platform> "<query>" -t execute
10833
11318
  one --agent actions knowledge <platform> <actionId>
10834
11319
  \`\`\`
10835
11320
 
10836
- Returns full API docs: required fields, validation rules, request structure. **REQUIRED before execute.** The output includes a CLI PARAMETER MAPPING section showing which flags to use.
11321
+ Returns the API docs: required fields, validation rules, request structure. **REQUIRED before execute.** The output includes a CLI PARAMETER MAPPING section showing which flags to use.
11322
+
11323
+ **In \`--agent\` mode this is a digest, not the whole document.** Large docs (most of their size is response shapes and optional-field tables) are trimmed to the sections needed to build a correct request: method/URL, headers, description, enforcement rules, required and optional parameters, sample request, gotchas, error handling. Everything else is listed in a table of contents so you can pull it on demand:
11324
+
11325
+ \`\`\`json
11326
+ {
11327
+ "knowledge": "...digest markdown, ends with a notice naming the omitted sections...",
11328
+ "method": "POST",
11329
+ "title": "Create an Issue",
11330
+ "truncated": true,
11331
+ "sections": [
11332
+ { "id": "response", "heading": "Response", "level": 2, "chars": 16483, "included": false },
11333
+ { "id": "response-fields", "heading": "Response Fields", "level": 2, "chars": 3353, "included": false }
11334
+ ],
11335
+ "omitted": 2,
11336
+ "omittedChars": 19836,
11337
+ "more": { "section": "one --agent actions knowledge <platform> <actionId> --section <id>[,<id>...]", "full": "..." }
11338
+ }
11339
+ \`\`\`
11340
+
11341
+ - \`truncated: false\` means you have the whole document \u2014 small docs are never trimmed, and no \`sections\` list is sent.
11342
+ - \`sections\` lists only what was omitted. \`included\` is \`false\` or \`"partial"\` (an oversized essential section was cut; its text ends with a truncation marker). Included sections are simply the headings in the markdown.
11343
+ - Load more by heading, id, or alias \u2014 several at once, comma-separated or repeated:
11344
+
11345
+ \`\`\`bash
11346
+ one --agent actions knowledge <platform> <actionId> --section "Response Fields"
11347
+ one --agent actions knowledge <platform> <actionId> --section response,optional
11348
+ one --agent actions knowledge <platform> <actionId> --full
11349
+ \`\`\`
11350
+
11351
+ Aliases: \`response\`, \`fields\`, \`optional\`, \`required\`, \`examples\`, \`errors\`, \`success\`, \`body\`, \`query\`, \`path\`, \`notes\`, \`behavior\`, \`gotchas\`. A parent section brings its subsections. Section requests are served from the local cache \u2014 no network call. An unknown name returns an error that lists every available section, so retry with one of those ids.
11352
+
11353
+ A \`--section\` response carries \`requested\`, \`resolved\` (the ids each name matched \u2014 alias and prefix matches are visible here), and \`truncated: false\` (the requested sections are whole). It does not repeat the table of contents. On scraped mega-docs with hundreds of headings the digest's \`sections\` list is collapsed to the top levels (\`sectionsCollapsed: true\`, each entry's \`children\` counts the hidden ones); \`--toc\` returns the complete list without the document:
11354
+
11355
+ \`\`\`bash
11356
+ one --agent actions knowledge <platform> <actionId> --toc
11357
+ \`\`\`
10837
11358
 
10838
11359
  ### 4. Execute
10839
11360
 
@@ -11038,6 +11559,8 @@ In \`--agent\` mode, knowledge and search responses include a \`_cache\` field:
11038
11559
  {
11039
11560
  "knowledge": "...",
11040
11561
  "method": "POST",
11562
+ "truncated": true,
11563
+ "sections": [ ... ],
11041
11564
  "_cache": {
11042
11565
  "hit": true,
11043
11566
  "age": 1423,
@@ -11057,6 +11580,9 @@ one --agent actions knowledge <platform> <actionId> --no-cache
11057
11580
  # Check cache status without fetching
11058
11581
  one --agent actions knowledge <platform> <actionId> --cache-status
11059
11582
 
11583
+ # Sections and --full are served from the cache once the doc is cached
11584
+ one --agent actions knowledge <platform> <actionId> --section "Response Fields"
11585
+
11060
11586
  # Same for search
11061
11587
  one --agent actions search <platform> "<query>" --no-cache
11062
11588
 
@@ -11101,6 +11627,8 @@ The unified memory store is a local, pluggable database (\`embedded-postgres\` b
11101
11627
  The first \`one mem\` call on a fresh install auto-initializes:
11102
11628
  - Backend: \`embedded-postgres\` (Postgres 18 via pgserve; downloads ~52MB of binaries on first use, ~2s on later boots)
11103
11629
  - Cluster path: \`~/.one/pg/cluster/\`, daemon PID/port at \`~/.one/pg/.pgserve.json\`
11630
+ - Port: \`5434\` by default. If another program already holds it (a database tunnel, another Postgres), the daemon starts on the next free port and records it in \`.pgserve.json\`. A port pinned with \`mem config set embedded-postgres.port <n>\` is never moved \u2014 a conflict there is an error naming the port.
11631
+ - Self-healing: a running daemon is reused only when the Postgres on its port reports this cluster's data directory. A Postgres left holding the cluster by a dead or stuck pgserve is stopped and a fresh daemon started. \`pgserve.log\` is trimmed to its last 1 MB once it passes 10 MB.
11104
11632
  - Embedding provider: \`openai\` if a key is already resolvable AND pgvector is loadable; else \`none\`
11105
11633
 
11106
11634
  If pgvector isn't installed in the bundled Postgres (the default binary distribution doesn't ship it yet), the schema applies cleanly without an \`embedding\` column, all FTS paths work, and \`mem doctor\` emits an \`_upgrade\` block pointing at \`brew install pgvector\` when an OpenAI key is present.
@@ -11751,8 +12279,9 @@ Every \`sync X\` command is also exposed as \`mem sync X\` \u2014 same handlers,
11751
12279
  logs/{platform}.log # cron run logs
11752
12280
  locks/{platform}_{model}/ # cross-process sync locks
11753
12281
  ~/.one/pg/cluster/ # unified memory store \u2014 embedded Postgres data dir
11754
- ~/.one/pg/.pgserve.json # daemon PID/port file (cleared on stop)
11755
- ~/.one/pg/pgserve.log # daemon stdout/stderr
12282
+ ~/.one/pg/.pgserve.json # daemon PID/port file
12283
+ ~/.one/pg/.pgserve.lock # held while a CLI process starts the daemon
12284
+ ~/.one/pg/pgserve.log # daemon stdout/stderr (trimmed to 1 MB past 10 MB)
11756
12285
  ~/.one/config.json # apiKey + openaiApiKey + memory config block
11757
12286
  ~/.one/sync/
11758
12287
  schedules.json # global schedule registry
@@ -11973,7 +12502,7 @@ common mistakes to avoid. Never guess \u2014 read the skill, then act.
11973
12502
  ### Quick reference:
11974
12503
  - \`one --agent list\` \u2014 See connected platforms and connection keys
11975
12504
  - \`one --agent actions search <platform> "<query>"\` \u2014 Find actions
11976
- - \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read docs (REQUIRED before execute)
12505
+ - \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read docs (REQUIRED before execute; a digest \u2014 add \`--section <name>\` or \`--full\` for more)
11977
12506
  - \`one --agent actions execute <platform> <actionId> <connectionKey>\` \u2014 Execute action
11978
12507
  - \`one --agent flow create\` \u2014 Build multi-step workflows
11979
12508
  - \`one --agent relay create\` \u2014 Set up webhook relay (receive events, forward to other platforms)
@@ -12276,7 +12805,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
12276
12805
  Workflow (use these in order):
12277
12806
  1. one list List connected platforms, keys, and your access on each
12278
12807
  2. one actions search <platform> <q> Search for actions using natural language
12279
- 3. one actions knowledge <plat> <id> Get full docs for an action (ALWAYS do this before execute)
12808
+ 3. one actions knowledge <plat> <id> Get docs for an action (ALWAYS do this before execute; --section/--full for more)
12280
12809
  4. one actions execute <p> <id> <key> Execute the action
12281
12810
 
12282
12811
  Guide:
@@ -12546,7 +13075,7 @@ var actions = program.command("actions").alias("a").description("Search, explore
12546
13075
  actions.command("search <platform> <query>").description('Search for actions on a platform (e.g. one actions search gmail "send email")').option("-t, --type <type>", "execute (to run it) or knowledge (to learn about it). Default: knowledge").option("--no-cache", "Bypass the cache and re-fetch from the API (the fresh response still refreshes the cache)").action(async (platform, query, options) => {
12547
13076
  await actionsSearchCommand(platform, query, options);
12548
13077
  });
12549
- actions.command("knowledge <platform> <actionId>").alias("k").description("Get full docs for an action \u2014 MUST call before execute to know required params").option("--no-cache", "Bypass the cache and re-fetch from the API (the fresh response still refreshes the cache)").option("--cache-status", "Print cache metadata without fetching").action(async (platform, actionId, options) => {
13078
+ actions.command("knowledge <platform> <actionId>").alias("k").description("Get docs for an action \u2014 MUST call before execute to know required params. In --agent mode returns a digest (request-building sections + a table of contents); load the rest with --section or --full").option("--section <name>", 'Return only the named section(s) \u2014 by heading, id, or alias (e.g. "Response Fields", response, optional). Repeatable or comma-separated', collect, []).option("--full", "Return the whole document instead of the digest").option("--toc", "List every section (id, heading, size) without the document").option("--no-cache", "Bypass the cache and re-fetch from the API (the fresh response still refreshes the cache)").option("--cache-status", "Print cache metadata without fetching").action(async (platform, actionId, options) => {
12550
13079
  await actionsKnowledgeCommand(platform, actionId, options);
12551
13080
  });
12552
13081
  actions.command("execute [platform] [actionId] [connectionKey]").alias("x").allowUnknownOption(true).allowExcessArguments(true).description("Execute an action (or multiple with --parallel, separated by --)").option("-d, --data <json>", "Request body as JSON").option("--path-vars <json>", "Path variables as JSON").option("--query-params <json>", "Query parameters as JSON").option("--headers <json>", "Additional headers as JSON").option("--form-data", "Send as multipart/form-data").option("--form-url-encoded", "Send as application/x-www-form-urlencoded").option("--dry-run", "Show request that would be sent without executing").option("--mock", "Return example response without making an API call").option("--skip-validation", "Skip input validation against the action schema").option("--no-cache", "Bypass the cached action details and re-fetch them (the fresh details still refresh the cache; execution itself is never cached)").option("--output <path>", "Save binary response to a file (for non-JSON responses like file downloads)").option("--parallel", "Execute multiple actions concurrently (separate actions with --)").option("--max-concurrency <n>", "Max concurrent actions when using --parallel (default: 5)", "5").action(async (platform, actionId, connectionKey, options) => {