@withone/cli 1.56.1 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.js +531 -5
- package/package.json +1 -1
- package/skills/one/SKILL.md +21 -0
package/README.md
CHANGED
|
@@ -280,6 +280,18 @@ one actions knowledge shopify 67890abcdef
|
|
|
280
280
|
|
|
281
281
|
Always read the knowledge before executing. It tells you exactly what parameters are required, what format they need, and any platform-specific quirks.
|
|
282
282
|
|
|
283
|
+
In `--agent` mode the response is a **digest**: request-building sections in full (method/URL, headers, description, enforcement rules, required and optional parameters, sample request, gotchas, error handling) plus a table of contents for the rest. Response shapes, response-field tables, and worked examples — the bulk of every large doc — are loaded on demand:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
one --agent actions knowledge github <actionId> # digest + sections[] table of contents
|
|
287
|
+
one --agent actions knowledge github <actionId> --section "Response Fields" # one section, by heading or id
|
|
288
|
+
one --agent actions knowledge github <actionId> --section response,examples # several, by alias
|
|
289
|
+
one --agent actions knowledge github <actionId> --full # whole document
|
|
290
|
+
one --agent actions knowledge github <actionId> --toc # every section id, no document
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The JSON carries `truncated`, `sections[]` (the omitted sections: id, heading, chars), and `more` (the commands to load the rest); the markdown ends with the same notice so it is never mistaken for the complete doc. Small documents are returned whole. Section requests are served from the local cache. Human (non-agent) output always prints the full document; `--section` works there too.
|
|
294
|
+
|
|
283
295
|
### `one actions execute <platform> <actionId> <connectionKey>`
|
|
284
296
|
|
|
285
297
|
Execute an API action on a connected platform.
|
|
@@ -354,6 +366,7 @@ Knowledge and search commands also support cache flags:
|
|
|
354
366
|
```bash
|
|
355
367
|
one actions knowledge gmail <actionId> --no-cache # Skip cache, fetch fresh
|
|
356
368
|
one actions knowledge gmail <actionId> --cache-status # Check cache status
|
|
369
|
+
one actions knowledge gmail <actionId> --section response # Served from the cached doc, no fetch
|
|
357
370
|
one actions search gmail "send email" --no-cache # Skip cache for search
|
|
358
371
|
one actions execute gmail <actionId> <key> --no-cache # Fresh action-details lookup
|
|
359
372
|
```
|
package/dist/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
}
|
|
@@ -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
|
|
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
|
|
|
@@ -11973,7 +12499,7 @@ common mistakes to avoid. Never guess \u2014 read the skill, then act.
|
|
|
11973
12499
|
### Quick reference:
|
|
11974
12500
|
- \`one --agent list\` \u2014 See connected platforms and connection keys
|
|
11975
12501
|
- \`one --agent actions search <platform> "<query>"\` \u2014 Find actions
|
|
11976
|
-
- \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read docs (REQUIRED before execute)
|
|
12502
|
+
- \`one --agent actions knowledge <platform> <actionId>\` \u2014 Read docs (REQUIRED before execute; a digest \u2014 add \`--section <name>\` or \`--full\` for more)
|
|
11977
12503
|
- \`one --agent actions execute <platform> <actionId> <connectionKey>\` \u2014 Execute action
|
|
11978
12504
|
- \`one --agent flow create\` \u2014 Build multi-step workflows
|
|
11979
12505
|
- \`one --agent relay create\` \u2014 Set up webhook relay (receive events, forward to other platforms)
|
|
@@ -12276,7 +12802,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
|
|
|
12276
12802
|
Workflow (use these in order):
|
|
12277
12803
|
1. one list List connected platforms, keys, and your access on each
|
|
12278
12804
|
2. one actions search <platform> <q> Search for actions using natural language
|
|
12279
|
-
3. one actions knowledge <plat> <id> Get
|
|
12805
|
+
3. one actions knowledge <plat> <id> Get docs for an action (ALWAYS do this before execute; --section/--full for more)
|
|
12280
12806
|
4. one actions execute <p> <id> <key> Execute the action
|
|
12281
12807
|
|
|
12282
12808
|
Guide:
|
|
@@ -12546,7 +13072,7 @@ var actions = program.command("actions").alias("a").description("Search, explore
|
|
|
12546
13072
|
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
13073
|
await actionsSearchCommand(platform, query, options);
|
|
12548
13074
|
});
|
|
12549
|
-
actions.command("knowledge <platform> <actionId>").alias("k").description("Get
|
|
13075
|
+
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
13076
|
await actionsKnowledgeCommand(platform, actionId, options);
|
|
12551
13077
|
});
|
|
12552
13078
|
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) => {
|
package/package.json
CHANGED
package/skills/one/SKILL.md
CHANGED
|
@@ -89,6 +89,26 @@ one --agent actions knowledge <platform> <actionId>
|
|
|
89
89
|
|
|
90
90
|
This tells you exactly what parameters are required, how to structure the request, and which flags to use. Never skip this step — without it you'll guess wrong on parameters.
|
|
91
91
|
|
|
92
|
+
**You get a digest, not always the whole document.** Large docs are trimmed to the request-building sections (method/URL, headers, description, rules, required + optional parameters, sample request, gotchas, error handling). The response tells you what was left out:
|
|
93
|
+
|
|
94
|
+
- `truncated: true` — some sections are omitted. `truncated: false` — you have everything (and no `sections` list is sent).
|
|
95
|
+
- `sections[]` — the omitted sections only, each with `id`, `heading`, `chars`, and `included` (`false` / `"partial"`). Included sections are the headings you can see in the markdown.
|
|
96
|
+
- The markdown itself ends with a notice naming the omitted sections and the exact commands to load them.
|
|
97
|
+
|
|
98
|
+
Load more only when you need it (response shapes, response fields, worked examples). Served from the local cache, no network:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
one --agent actions knowledge <platform> <actionId> --section "Response Fields" # by heading
|
|
102
|
+
one --agent actions knowledge <platform> <actionId> --section response,optional # by alias, several at once
|
|
103
|
+
one --agent actions knowledge <platform> <actionId> --full # whole document
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Aliases: `response`, `fields`, `optional`, `required`, `examples`, `errors`, `success`, `body`, `query`, `path`, `notes`, `behavior`, `gotchas`. An unknown name returns an error listing every available section — retry with one of those ids.
|
|
107
|
+
|
|
108
|
+
- A `--section` response has `truncated: false` (you got the whole section) and `resolved` (which ids your names matched). It does not repeat the table of contents.
|
|
109
|
+
- If the digest says `sectionsCollapsed: true`, the doc has hundreds of headings and `sections` shows only the top omitted levels (`children` = hidden count). `--toc` lists every heading without the document.
|
|
110
|
+
- Sections named `(appendix)` in the notice are appended reference chunks or companion endpoints, not the action doc itself.
|
|
111
|
+
|
|
92
112
|
### 4. Execute
|
|
93
113
|
|
|
94
114
|
```bash
|
|
@@ -164,6 +184,7 @@ Knowledge and search responses are cached locally (`~/.one/cache/`). Subsequent
|
|
|
164
184
|
- In `--agent` mode, responses include a `_cache` field: `{"hit": true, "age": 1423, "fresh": true}`; execute responses include `"_preflight": {"cache": "hit"|"miss"}`
|
|
165
185
|
- Use `--no-cache` to force a fresh fetch: works on `knowledge`, `search`, and `execute` (refreshes execute's action-details lookup)
|
|
166
186
|
- Use `--cache-status` to check cache state without fetching
|
|
187
|
+
- `knowledge --section <name>` and `--full` read from the cached document — no extra API call once the doc is cached
|
|
167
188
|
- Manage cache: `one cache list`, `one cache clear`, `one cache update-all`
|
|
168
189
|
- Execution responses are NEVER cached — the action always runs live; only action metadata (docs, method, path, schema) is cached
|
|
169
190
|
|