bluekiwi 0.2.5 → 0.3.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.
@@ -5,7 +5,6 @@ const here = dirname(fileURLToPath(import.meta.url));
5
5
  const SKILLS_ROOT = join(here, "skills");
6
6
  export const BUNDLED_SKILLS = [
7
7
  "bk-start",
8
- "bk-next",
9
8
  "bk-status",
10
9
  "bk-rewind",
11
10
  "bk-approve",
@@ -20908,6 +20908,50 @@ var BlueKiwiClient = class {
20908
20908
  }
20909
20909
  throw new BlueKiwiNetworkError("Unreachable", lastError);
20910
20910
  }
20911
+ async requestFormData(method, path2, formData) {
20912
+ const url2 = `${this.baseUrl.replace(/\/$/, "")}${path2}`;
20913
+ const init = {
20914
+ method,
20915
+ headers: {
20916
+ Authorization: `Bearer ${this.apiKey}`
20917
+ },
20918
+ body: formData
20919
+ };
20920
+ let lastError;
20921
+ for (let attempt = 0; attempt <= RETRY_DELAYS_MS.length; attempt++) {
20922
+ try {
20923
+ const res = await fetch(url2, init);
20924
+ if (res.status === 401) {
20925
+ throw new BlueKiwiAuthError();
20926
+ }
20927
+ if (!res.ok) {
20928
+ const text2 = await res.text().catch(() => "");
20929
+ if (res.status >= 500 && attempt < RETRY_DELAYS_MS.length) {
20930
+ lastError = new BlueKiwiApiError(res.status, text2);
20931
+ await sleep(RETRY_DELAYS_MS[attempt]);
20932
+ continue;
20933
+ }
20934
+ throw new BlueKiwiApiError(res.status, text2);
20935
+ }
20936
+ const text = await res.text();
20937
+ return text ? JSON.parse(text) : null;
20938
+ } catch (err) {
20939
+ if (err instanceof BlueKiwiAuthError || err instanceof BlueKiwiApiError) {
20940
+ throw err;
20941
+ }
20942
+ lastError = err;
20943
+ if (attempt < RETRY_DELAYS_MS.length) {
20944
+ await sleep(RETRY_DELAYS_MS[attempt]);
20945
+ continue;
20946
+ }
20947
+ throw new BlueKiwiNetworkError(
20948
+ `Failed to reach ${url2} after ${RETRY_DELAYS_MS.length + 1} attempts`,
20949
+ err
20950
+ );
20951
+ }
20952
+ }
20953
+ throw new BlueKiwiNetworkError("Unreachable", lastError);
20954
+ }
20911
20955
  };
20912
20956
  function sleep(ms) {
20913
20957
  return new Promise((r) => setTimeout(r, ms));
@@ -21022,6 +21066,15 @@ var tools = [
21022
21066
  folder_id: { type: "number" }
21023
21067
  }
21024
21068
  ),
21069
+ tool(
21070
+ "list_tasks",
21071
+ "List tasks visible to the current user. Optionally filter by workflow_id, status (running/completed/failed), or search query. Returns task id, workflow_title, status, current_step, total_steps, and logs.",
21072
+ {
21073
+ workflow_id: { type: "number" },
21074
+ status: { type: "string" },
21075
+ q: { type: "string" }
21076
+ }
21077
+ ),
21025
21078
  tool(
21026
21079
  "list_workflow_versions",
21027
21080
  "List every version in the same family as the given workflow id, including active and archived ones. Returns the active_version_id and an ordered versions array.",
@@ -21135,9 +21188,10 @@ var tools = [
21135
21188
  ),
21136
21189
  tool(
21137
21190
  "get_web_response",
21138
- "Fetch the pending web response payload for a task",
21191
+ "Fetch VS response for a task. Without node_id: returns the latest response. With node_id: returns the response history for that node across all loop iterations (web_response only, no visual_html). Useful for tracking how user preferences evolved across iterations.",
21139
21192
  {
21140
- task_id: { type: "number" }
21193
+ task_id: { type: "number" },
21194
+ node_id: { type: "number" }
21141
21195
  },
21142
21196
  ["task_id"]
21143
21197
  ),
@@ -21151,6 +21205,48 @@ var tools = [
21151
21205
  },
21152
21206
  ["task_id", "node_id", "visual_html"]
21153
21207
  ),
21208
+ tool(
21209
+ "set_visual_html",
21210
+ `Submit a VS content fragment for a visual_selection=true gate node. Write HTML fragments using bk-* component classes \u2014 the frame (CSS, JS, submit button) is added automatically. Do NOT include <html>, <head>, or <body> tags.
21211
+
21212
+ DIALOG SIZE DIRECTIVE (optional, add as first line):
21213
+ <!-- @bk size=sm --> default (448px) \u2014 simple options, checklist
21214
+ <!-- @bk size=md --> medium (672px) \u2014 cards, code-compare
21215
+ <!-- @bk size=lg --> large (896px) \u2014 pros-cons, ranking, timeline
21216
+ <!-- @bk size=xl --> wide (1152px) \u2014 mockups, matrix, side-by-side wireframes
21217
+ <!-- @bk size=full --> fullscreen (95vw) \u2014 dashboard previews, complex layouts
21218
+ If omitted, defaults to sm. Use xl or full for any content that benefits from horizontal space.
21219
+
21220
+ Built-in components (use class names directly):
21221
+ SELECTION (collect choices):
21222
+ - bk-options: A/B/C cards. Wrap in .bk-options, each .bk-option with data-value. Optional data-recommended badge. Contains .bk-option-letter + .bk-option-body with h3+p.
21223
+ - bk-cards: Visual cards. Wrap in .bk-cards, each .bk-card with data-value. Contains .bk-card-image + .bk-card-body.
21224
+ - bk-checklist: Multi-select. Wrap in .bk-checklist, each .bk-check-item with data-value. Optional data-checked for defaults.
21225
+ - bk-code-compare: Code selection. Wrap in .bk-code-compare, each .bk-code-option with data-value. Contains .bk-code-label + pre.bk-code.
21226
+
21227
+ INPUT (collect values):
21228
+ - bk-slider: Numeric. data-name, data-min, data-max, data-value, data-unit on .bk-slider. Contains label.
21229
+ - bk-ranking: Drag reorder. Wrap in .bk-ranking, each .bk-rank-item with data-value.
21230
+ - bk-matrix: 2x2 placement. .bk-matrix with data-x-label, data-y-label. Each .bk-matrix-item with data-value.
21231
+
21232
+ DISPLAY (no values):
21233
+ - bk-split: Side-by-side. Two .bk-split-panel inside .bk-split.
21234
+ - bk-pros-cons: .bk-pros + .bk-cons inside .bk-pros-cons.
21235
+ - bk-mockup: .bk-mockup-header + .bk-mockup-body inside .bk-mockup.
21236
+ - bk-timeline: .bk-timeline-item with data-status="done|current|pending".
21237
+
21238
+ Layout: h2 for title, .bk-subtitle, .bk-section for breaks, .bk-label for category.
21239
+
21240
+ Response format (JSON via get_web_response):
21241
+ {selections: ["a"], values: {budget: 70}, ranking: ["security","ux"], matrix: {auth: {x:0.8,y:0.9}}}
21242
+ Only populated fields are included.`,
21243
+ {
21244
+ task_id: { type: "number" },
21245
+ node_id: { type: "number" },
21246
+ html: { type: "string" }
21247
+ },
21248
+ ["task_id", "node_id", "html"]
21249
+ ),
21154
21250
  tool(
21155
21251
  "save_artifacts",
21156
21252
  "Save artifacts for a task step",
@@ -21286,6 +21382,114 @@ var tools = [
21286
21382
  },
21287
21383
  ["workflow_id"]
21288
21384
  ),
21385
+ tool(
21386
+ "append_node",
21387
+ "Append a new node at the end of a workflow. node_type determines auto_advance automatically (action=auto, gate/loop=manual). Use hitl=true for action nodes that require human approval. For loop nodes, set loop_back_to to the target step_order (usually self). For gate nodes, set visual_selection=true for click-based HTML selection UI.",
21388
+ {
21389
+ workflow_id: { type: "number" },
21390
+ title: { type: "string" },
21391
+ instruction: { type: "string" },
21392
+ node_type: { type: "string" },
21393
+ hitl: { type: "boolean" },
21394
+ visual_selection: { type: "boolean" },
21395
+ loop_back_to: { type: "number" },
21396
+ credential_id: { type: "number" },
21397
+ instruction_id: { type: "number" }
21398
+ },
21399
+ ["workflow_id", "title", "node_type"]
21400
+ ),
21401
+ tool(
21402
+ "insert_node",
21403
+ "Insert a new node after a specific step_order in a workflow. Nodes after the insertion point are shifted by +1. For loop nodes, set loop_back_to. For gate nodes, set visual_selection=true for click-based selection.",
21404
+ {
21405
+ workflow_id: { type: "number" },
21406
+ after_step: { type: "number" },
21407
+ title: { type: "string" },
21408
+ instruction: { type: "string" },
21409
+ node_type: { type: "string" },
21410
+ hitl: { type: "boolean" },
21411
+ visual_selection: { type: "boolean" },
21412
+ loop_back_to: { type: "number" },
21413
+ credential_id: { type: "number" },
21414
+ instruction_id: { type: "number" }
21415
+ },
21416
+ ["workflow_id", "after_step", "title", "node_type"]
21417
+ ),
21418
+ tool(
21419
+ "update_node",
21420
+ "Partially update a single node. Only provided fields are changed. If node_type changes, auto_advance is re-enforced automatically.",
21421
+ {
21422
+ workflow_id: { type: "number" },
21423
+ node_id: { type: "number" },
21424
+ title: { type: "string" },
21425
+ instruction: { type: "string" },
21426
+ node_type: { type: "string" },
21427
+ hitl: { type: "boolean" },
21428
+ credential_id: { type: "number" },
21429
+ instruction_id: { type: "number" },
21430
+ loop_back_to: { type: "number" }
21431
+ },
21432
+ ["workflow_id", "node_id"]
21433
+ ),
21434
+ tool(
21435
+ "remove_node",
21436
+ "Delete a single node and reindex step_order for subsequent nodes (all nodes after the deleted one shift by -1).",
21437
+ {
21438
+ workflow_id: { type: "number" },
21439
+ node_id: { type: "number" }
21440
+ },
21441
+ ["workflow_id", "node_id"]
21442
+ ),
21443
+ tool(
21444
+ "list_attachments",
21445
+ "List file attachments for a workflow node. Returns metadata only (id, filename, mime_type, size_bytes).",
21446
+ {
21447
+ workflow_id: { type: "number" },
21448
+ node_id: { type: "number" }
21449
+ },
21450
+ ["workflow_id", "node_id"]
21451
+ ),
21452
+ tool(
21453
+ "get_attachment",
21454
+ "Download attachment content. Returns text content directly for text files. For binary files, returns metadata with binary=true.",
21455
+ {
21456
+ workflow_id: { type: "number" },
21457
+ node_id: { type: "number" },
21458
+ attachment_id: { type: "number" }
21459
+ },
21460
+ ["workflow_id", "node_id", "attachment_id"]
21461
+ ),
21462
+ tool(
21463
+ "upload_attachment",
21464
+ "Upload a text file as an attachment to a workflow node. Text-only \u2014 binary uploads must use the web UI. Use this after creating a workflow to attach scripts, reference docs, or config files that agents can download during execution.",
21465
+ {
21466
+ workflow_id: { type: "number" },
21467
+ node_id: { type: "number" },
21468
+ filename: { type: "string" },
21469
+ content: { type: "string" },
21470
+ mime_type: { type: "string" }
21471
+ },
21472
+ ["workflow_id", "node_id", "filename", "content"]
21473
+ ),
21474
+ tool(
21475
+ "delete_attachment",
21476
+ "Delete an attachment from a workflow node.",
21477
+ {
21478
+ workflow_id: { type: "number" },
21479
+ node_id: { type: "number" },
21480
+ attachment_id: { type: "number" }
21481
+ },
21482
+ ["workflow_id", "node_id", "attachment_id"]
21483
+ ),
21484
+ tool(
21485
+ "save_feedback",
21486
+ "Save post-workflow feedback for a completed task. Call this after collecting user survey responses and before calling complete_task. feedback is an array of {question, answer} objects.",
21487
+ {
21488
+ task_id: { type: "number" },
21489
+ feedback: { type: "array" }
21490
+ },
21491
+ ["task_id", "feedback"]
21492
+ ),
21289
21493
  tool(
21290
21494
  "save_findings",
21291
21495
  "Save one or more compliance findings for a task",
@@ -21323,7 +21527,7 @@ var tools = [
21323
21527
  ),
21324
21528
  tool(
21325
21529
  "share_folder",
21326
- "Share a folder with a user group at the given access level (viewer or editor). Owner or admin only.",
21530
+ "Share a folder with a user group at the given access level (reader or contributor). Owner or admin only.",
21327
21531
  {
21328
21532
  folder_id: { type: "number" },
21329
21533
  group_id: { type: "number" },
@@ -21340,6 +21544,24 @@ var tools = [
21340
21544
  },
21341
21545
  ["folder_id", "group_id"]
21342
21546
  ),
21547
+ tool(
21548
+ "update_folder",
21549
+ "Rename or change the description of a folder. Cannot rename system folders. Only the owner or an admin can edit.",
21550
+ {
21551
+ folder_id: { type: "number" },
21552
+ name: { type: "string" },
21553
+ description: { type: "string" }
21554
+ },
21555
+ ["folder_id"]
21556
+ ),
21557
+ tool(
21558
+ "delete_folder",
21559
+ "Delete an empty folder. Returns an error if the folder contains any workflows, instructions, credentials, or child folders.",
21560
+ {
21561
+ folder_id: { type: "number" }
21562
+ },
21563
+ ["folder_id"]
21564
+ ),
21343
21565
  tool(
21344
21566
  "move_workflow",
21345
21567
  "Move a workflow into a different folder. Caller must have edit permission on the destination folder.",
@@ -21405,6 +21627,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21405
21627
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
21406
21628
  return wrap(await client.request("GET", `/api/workflows${suffix}`));
21407
21629
  }
21630
+ case "list_tasks": {
21631
+ const qs = new URLSearchParams();
21632
+ if (typeof args.workflow_id === "number")
21633
+ qs.set("workflow_id", String(args.workflow_id));
21634
+ if (typeof args.status === "string") qs.set("status", args.status);
21635
+ if (typeof args.q === "string") qs.set("q", args.q);
21636
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21637
+ return wrap(await client.request("GET", `/api/tasks${suffix}`));
21638
+ }
21408
21639
  case "list_workflow_versions": {
21409
21640
  const workflowId = requireNumberArg(args, "workflow_id");
21410
21641
  return wrap(
@@ -21426,12 +21657,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21426
21657
  )
21427
21658
  );
21428
21659
  }
21429
- case "start_workflow":
21660
+ case "start_workflow": {
21661
+ const MODEL_NAME_RE = /^(claude|gpt|gemini|o\d|llama|mistral|qwen|deepseek)[-/.]/i;
21662
+ const clientName = server.getClientVersion()?.name ?? null;
21663
+ let providerSlug = null;
21664
+ let modelSlug = null;
21665
+ try {
21666
+ const meta3 = JSON.parse(
21667
+ typeof args.session_meta === "string" ? args.session_meta : "{}"
21668
+ );
21669
+ providerSlug = meta3.agent ?? null;
21670
+ modelSlug = meta3.model_id ?? null;
21671
+ } catch {
21672
+ }
21673
+ if (clientName) {
21674
+ if (MODEL_NAME_RE.test(clientName)) {
21675
+ if (!modelSlug) modelSlug = clientName;
21676
+ } else if (!providerSlug) {
21677
+ providerSlug = clientName;
21678
+ }
21679
+ }
21680
+ args.provider_slug = providerSlug;
21681
+ args.model_slug = modelSlug;
21430
21682
  return wrap(await client.request("POST", "/api/tasks/start", args));
21683
+ }
21431
21684
  case "execute_step": {
21432
21685
  const taskId = requireNumberArg(args, "task_id");
21433
21686
  const body = { ...args };
21434
21687
  delete body.task_id;
21688
+ if (body.model_id && !body.model_slug) {
21689
+ body.model_slug = body.model_id;
21690
+ }
21435
21691
  return wrap(
21436
21692
  await client.request("POST", `/api/tasks/${taskId}/execute`, body)
21437
21693
  );
@@ -21488,9 +21744,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21488
21744
  }
21489
21745
  case "get_web_response": {
21490
21746
  const taskId = requireNumberArg(args, "task_id");
21491
- return wrap(
21492
- await client.request("GET", `/api/tasks/${taskId}/respond`)
21493
- );
21747
+ const nodeId = typeof args.node_id === "number" ? args.node_id : null;
21748
+ const url2 = nodeId ? `/api/tasks/${taskId}/respond?node_id=${nodeId}` : `/api/tasks/${taskId}/respond`;
21749
+ return wrap(await client.request("GET", url2));
21494
21750
  }
21495
21751
  case "submit_visual": {
21496
21752
  const taskId = requireNumberArg(args, "task_id");
@@ -21500,6 +21756,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21500
21756
  await client.request("POST", `/api/tasks/${taskId}/visual`, body)
21501
21757
  );
21502
21758
  }
21759
+ case "set_visual_html": {
21760
+ const taskId = requireNumberArg(args, "task_id");
21761
+ const nodeId = requireNumberArg(args, "node_id");
21762
+ return wrap(
21763
+ await client.request("POST", `/api/tasks/${taskId}/visual`, {
21764
+ node_id: nodeId,
21765
+ html: args.html
21766
+ })
21767
+ );
21768
+ }
21503
21769
  case "save_artifacts": {
21504
21770
  const taskId = requireNumberArg(args, "task_id");
21505
21771
  const body = { ...args };
@@ -21581,6 +21847,146 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21581
21847
  await client.request("DELETE", `/api/workflows/${workflowId}`)
21582
21848
  );
21583
21849
  }
21850
+ case "append_node": {
21851
+ const workflowId = requireNumberArg(args, "workflow_id");
21852
+ const body = { ...args };
21853
+ delete body.workflow_id;
21854
+ return wrap(
21855
+ await client.request(
21856
+ "POST",
21857
+ `/api/workflows/${workflowId}/nodes`,
21858
+ body
21859
+ )
21860
+ );
21861
+ }
21862
+ case "insert_node": {
21863
+ const workflowId = requireNumberArg(args, "workflow_id");
21864
+ const afterStep = requireNumberArg(args, "after_step");
21865
+ const body = { ...args };
21866
+ delete body.workflow_id;
21867
+ delete body.after_step;
21868
+ return wrap(
21869
+ await client.request(
21870
+ "POST",
21871
+ `/api/workflows/${workflowId}/nodes?after=${afterStep}`,
21872
+ body
21873
+ )
21874
+ );
21875
+ }
21876
+ case "update_node": {
21877
+ const workflowId = requireNumberArg(args, "workflow_id");
21878
+ const nodeId = requireNumberArg(args, "node_id");
21879
+ const body = { ...args };
21880
+ delete body.workflow_id;
21881
+ delete body.node_id;
21882
+ return wrap(
21883
+ await client.request(
21884
+ "PATCH",
21885
+ `/api/workflows/${workflowId}/nodes/${nodeId}`,
21886
+ body
21887
+ )
21888
+ );
21889
+ }
21890
+ case "remove_node": {
21891
+ const workflowId = requireNumberArg(args, "workflow_id");
21892
+ const nodeId = requireNumberArg(args, "node_id");
21893
+ return wrap(
21894
+ await client.request(
21895
+ "DELETE",
21896
+ `/api/workflows/${workflowId}/nodes/${nodeId}`
21897
+ )
21898
+ );
21899
+ }
21900
+ case "list_attachments": {
21901
+ const workflowId = requireNumberArg(args, "workflow_id");
21902
+ const nodeId = requireNumberArg(args, "node_id");
21903
+ return wrap(
21904
+ await client.request(
21905
+ "GET",
21906
+ `/api/workflows/${workflowId}/nodes/${nodeId}/attachments`
21907
+ )
21908
+ );
21909
+ }
21910
+ case "get_attachment": {
21911
+ const workflowId = requireNumberArg(args, "workflow_id");
21912
+ const nodeId = requireNumberArg(args, "node_id");
21913
+ const attachmentId = requireNumberArg(args, "attachment_id");
21914
+ const url2 = `${apiUrl.replace(/\/$/, "")}/api/workflows/${workflowId}/nodes/${nodeId}/attachments/${attachmentId}`;
21915
+ const res = await fetch(url2, {
21916
+ method: "GET",
21917
+ headers: {
21918
+ Authorization: `Bearer ${apiKey}`
21919
+ }
21920
+ });
21921
+ if (res.status === 401) {
21922
+ throw new BlueKiwiAuthError();
21923
+ }
21924
+ if (!res.ok) {
21925
+ throw new BlueKiwiApiError(
21926
+ res.status,
21927
+ await res.text().catch(() => "")
21928
+ );
21929
+ }
21930
+ const contentType = res.headers.get("content-type") ?? "";
21931
+ if (contentType.includes("application/json")) {
21932
+ const text = await res.text();
21933
+ return wrap(text ? JSON.parse(text) : null);
21934
+ }
21935
+ const disposition = res.headers.get("content-disposition") ?? "";
21936
+ const filenameMatch = /filename\*=UTF-8''([^;]+)|filename=\"?([^\";]+)\"?/i.exec(
21937
+ disposition
21938
+ );
21939
+ const filename = decodeURIComponent(
21940
+ filenameMatch?.[1] ?? filenameMatch?.[2] ?? ""
21941
+ );
21942
+ const sizeHeader = res.headers.get("content-length");
21943
+ const sizeBytes = typeof sizeHeader === "string" ? Number(sizeHeader) : void 0;
21944
+ return wrap({
21945
+ data: {
21946
+ id: attachmentId,
21947
+ filename: filename || void 0,
21948
+ mime_type: contentType || void 0,
21949
+ size_bytes: typeof sizeBytes === "number" && Number.isFinite(sizeBytes) ? sizeBytes : void 0,
21950
+ binary: true
21951
+ }
21952
+ });
21953
+ }
21954
+ case "upload_attachment": {
21955
+ const workflowId = requireNumberArg(args, "workflow_id");
21956
+ const nodeId = requireNumberArg(args, "node_id");
21957
+ const filename = requireStringArg(args, "filename");
21958
+ const content = requireStringArg(args, "content");
21959
+ const mimeType = typeof args.mime_type === "string" && args.mime_type.length > 0 ? args.mime_type : "text/plain";
21960
+ const blob = new Blob([content], { type: mimeType });
21961
+ const formData = new FormData();
21962
+ formData.append("file", blob, filename);
21963
+ return wrap(
21964
+ await client.requestFormData(
21965
+ "POST",
21966
+ `/api/workflows/${workflowId}/nodes/${nodeId}/attachments`,
21967
+ formData
21968
+ )
21969
+ );
21970
+ }
21971
+ case "delete_attachment": {
21972
+ const workflowId = requireNumberArg(args, "workflow_id");
21973
+ const nodeId = requireNumberArg(args, "node_id");
21974
+ const attachmentId = requireNumberArg(args, "attachment_id");
21975
+ return wrap(
21976
+ await client.request(
21977
+ "DELETE",
21978
+ `/api/workflows/${workflowId}/nodes/${nodeId}/attachments/${attachmentId}`
21979
+ )
21980
+ );
21981
+ }
21982
+ case "save_feedback": {
21983
+ const taskId = requireNumberArg(args, "task_id");
21984
+ return wrap(
21985
+ await client.request("POST", `/api/tasks/${taskId}/feedback`, {
21986
+ feedback: args.feedback
21987
+ })
21988
+ );
21989
+ }
21584
21990
  case "save_findings": {
21585
21991
  const taskId = requireNumberArg(args, "task_id");
21586
21992
  const body = { findings: args.findings };
@@ -21629,6 +22035,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
21629
22035
  )
21630
22036
  );
21631
22037
  }
22038
+ case "update_folder": {
22039
+ const folderId = requireNumberArg(args, "folder_id");
22040
+ const body = {};
22041
+ if (typeof args.name === "string") body.name = args.name;
22042
+ if (typeof args.description === "string")
22043
+ body.description = args.description;
22044
+ return wrap(
22045
+ await client.request("PUT", `/api/folders/${folderId}`, body)
22046
+ );
22047
+ }
22048
+ case "delete_folder": {
22049
+ const folderId = requireNumberArg(args, "folder_id");
22050
+ return wrap(await client.request("DELETE", `/api/folders/${folderId}`));
22051
+ }
21632
22052
  case "move_workflow": {
21633
22053
  const workflowId = requireNumberArg(args, "workflow_id");
21634
22054
  const folderId = requireNumberArg(args, "folder_id");
@@ -21711,6 +22131,13 @@ function requireNumberArg(args, key) {
21711
22131
  }
21712
22132
  return value;
21713
22133
  }
22134
+ function requireStringArg(args, key) {
22135
+ const value = args[key];
22136
+ if (typeof value !== "string") {
22137
+ throw new Error(`${key} must be a string`);
22138
+ }
22139
+ return value;
22140
+ }
21714
22141
  function wrap(data) {
21715
22142
  return {
21716
22143
  content: [{ type: "text", text: JSON.stringify(data) }]
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: bk-approve
3
- description: BlueKiwi approval skill. Handles pending approvals when resuming a session that was interrupted mid-HITL, or when the user explicitly wants to approve a paused step. During normal execution, HITL approval is handled inline by bk-start/bk-next. Use this skill when the user says "/bk-approve", "approve this", "approve step", or returns to a session where a HITL step is already waiting.
3
+ description: BlueKiwi approval skill. Handles pending approvals when resuming a session that was interrupted mid-HITL, or when the user explicitly wants to approve a paused step. During normal execution, HITL approval is handled inline by bk-start. Use this skill when the user says "/bk-approve", "approve this", "approve step", or returns to a session where a HITL step is already waiting.
4
4
  user_invocable: true
5
5
  ---
6
6
 
@@ -9,7 +9,7 @@ user_invocable: true
9
9
  Handle pending approvals in a running workflow. Covers two scenarios:
10
10
 
11
11
  - **Gate step** (`node_type: gate`): Agent asked the human a question; collect the answer.
12
- - **HITL step** (`node_type: action`, `auto_advance: false`): Agent completed work and called `request_approval`; human reviews and approves before the workflow continues.
12
+ - **HITL step** (`node_type: action`, `hitl: true`): Agent completed work and called `request_approval`; human reviews and approves before the workflow continues.
13
13
 
14
14
  ## Argument Handling
15
15
 
@@ -36,11 +36,23 @@ Check `log_status` and `node_type` to determine scenario:
36
36
 
37
37
  1. Show the gate question from `instruction`.
38
38
  2. Check `get_web_response` for a pre-submitted web response. If found, show it and ask to confirm.
39
+ If the web response is a JSON object from bk-\* components, display a readable summary instead of raw JSON:
40
+
41
+ ```text
42
+ VS Response:
43
+ - Selected: [option names from selections array]
44
+ - Values: budget = 70%, confidence = 85%
45
+ - Ranking: 1. Security, 2. Performance, 3. UX
46
+ - Matrix: auth -> high urgency / high importance
47
+ ```
48
+
49
+ Map `selections` values back to the option labels shown in the VS screen. Present `values` with their units, `ranking` as a numbered list, and `matrix` positions as quadrant descriptions using high/low language for each axis.
50
+
39
51
  3. Collect decision via AskUserQuestion:
40
52
  - header: "Gate decision"
41
53
  - options: ["Approve (Recommended)", "Approve with edits", "Reject and revise", "Rewind to previous step"]
42
54
  4. Call `execute_step` with the decision as `output`, status `"success"`.
43
- 5. Call `advance` to move to the next step and follow the auto_advance loop (see `/bk-next`).
55
+ 5. Call `advance` to move to the next step and follow the auto-advance loop (see bk-start auto-advance loop).
44
56
 
45
57
  ### Step 2b: HITL Step
46
58
 
@@ -61,12 +73,12 @@ Check `log_status` and `node_type` to determine scenario:
61
73
  3. **If Approved**:
62
74
  - Call `approve_step(task_id=<id>)` MCP tool.
63
75
  - Call `advance` to move to the next step.
64
- - Follow the auto_advance loop (see `/bk-next`).
76
+ - Follow the auto-advance loop (see bk-start auto-advance loop).
65
77
 
66
78
  4. **If Rejected**:
67
79
  - Ask the user for the reason.
68
80
  - Call `rewind` to return to this step so the agent can redo it.
69
- - Tell the user: "Rewound to step {N}. Type `/bk-next` to retry."
81
+ - Tell the user: "Rewound to step {N}. Type `/bk-start` to retry."
70
82
 
71
83
  5. **If Rewind to earlier step**:
72
84
  - Switch to `/bk-rewind` flow.
@@ -74,5 +86,5 @@ Check `log_status` and `node_type` to determine scenario:
74
86
  ## Notes
75
87
 
76
88
  - `approve_step` MCP tool handles authentication automatically using the configured API key.
77
- - After approving a HITL step, always follow the auto_advance loop — the next step may auto-proceed.
89
+ - After approving a HITL step, always follow the auto-advance loop — the next step may auto-proceed.
78
90
  - If `advance` still returns 403 after approval, wait a moment and retry — the approval write may not have propagated yet.