@crypto512/jicon-mcp 2.2.1 → 2.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/PROMPT.md +10 -28
  2. package/README.md +39 -6
  3. package/TOOL_LIST.md +542 -407
  4. package/dist/config/constants.d.ts +28 -0
  5. package/dist/config/constants.d.ts.map +1 -1
  6. package/dist/config/constants.js +34 -0
  7. package/dist/config/constants.js.map +1 -1
  8. package/dist/config/loader.d.ts.map +1 -1
  9. package/dist/config/loader.js +12 -2
  10. package/dist/config/loader.js.map +1 -1
  11. package/dist/config/types.d.ts +49 -6
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/config/types.js +18 -0
  14. package/dist/config/types.js.map +1 -1
  15. package/dist/confluence/client.d.ts.map +1 -1
  16. package/dist/confluence/client.js +7 -2
  17. package/dist/confluence/client.js.map +1 -1
  18. package/dist/confluence/formatters.d.ts +20 -0
  19. package/dist/confluence/formatters.d.ts.map +1 -1
  20. package/dist/confluence/formatters.js +74 -8
  21. package/dist/confluence/formatters.js.map +1 -1
  22. package/dist/confluence/tools.d.ts +16 -20
  23. package/dist/confluence/tools.d.ts.map +1 -1
  24. package/dist/confluence/tools.js +71 -68
  25. package/dist/confluence/tools.js.map +1 -1
  26. package/dist/credentials/extractor.d.ts +15 -5
  27. package/dist/credentials/extractor.d.ts.map +1 -1
  28. package/dist/credentials/extractor.js +99 -12
  29. package/dist/credentials/extractor.js.map +1 -1
  30. package/dist/credentials/index.d.ts +4 -3
  31. package/dist/credentials/index.d.ts.map +1 -1
  32. package/dist/credentials/index.js +3 -2
  33. package/dist/credentials/index.js.map +1 -1
  34. package/dist/credentials/types.d.ts +22 -0
  35. package/dist/credentials/types.d.ts.map +1 -1
  36. package/dist/credentials/types.js.map +1 -1
  37. package/dist/index.js +211 -176
  38. package/dist/index.js.map +1 -1
  39. package/dist/jira/activity-tools.d.ts +8 -15
  40. package/dist/jira/activity-tools.d.ts.map +1 -1
  41. package/dist/jira/activity-tools.js +131 -90
  42. package/dist/jira/activity-tools.js.map +1 -1
  43. package/dist/jira/client.d.ts +24 -0
  44. package/dist/jira/client.d.ts.map +1 -1
  45. package/dist/jira/client.js +65 -6
  46. package/dist/jira/client.js.map +1 -1
  47. package/dist/jira/formatters.d.ts +61 -0
  48. package/dist/jira/formatters.d.ts.map +1 -1
  49. package/dist/jira/formatters.js +83 -11
  50. package/dist/jira/formatters.js.map +1 -1
  51. package/dist/jira/tools.d.ts +78 -26
  52. package/dist/jira/tools.d.ts.map +1 -1
  53. package/dist/jira/tools.js +293 -130
  54. package/dist/jira/tools.js.map +1 -1
  55. package/dist/permissions/filter.d.ts.map +1 -1
  56. package/dist/permissions/filter.js +8 -4
  57. package/dist/permissions/filter.js.map +1 -1
  58. package/dist/permissions/tool-registry.d.ts +15 -13
  59. package/dist/permissions/tool-registry.d.ts.map +1 -1
  60. package/dist/permissions/tool-registry.js +19 -10
  61. package/dist/permissions/tool-registry.js.map +1 -1
  62. package/dist/session/context.d.ts +81 -0
  63. package/dist/session/context.d.ts.map +1 -0
  64. package/dist/session/context.js +107 -0
  65. package/dist/session/context.js.map +1 -0
  66. package/dist/session/index.d.ts +12 -0
  67. package/dist/session/index.d.ts.map +1 -0
  68. package/dist/session/index.js +22 -0
  69. package/dist/session/index.js.map +1 -0
  70. package/dist/session/manager.d.ts +186 -0
  71. package/dist/session/manager.d.ts.map +1 -0
  72. package/dist/session/manager.js +383 -0
  73. package/dist/session/manager.js.map +1 -0
  74. package/dist/tempo/client.d.ts +14 -0
  75. package/dist/tempo/client.d.ts.map +1 -1
  76. package/dist/tempo/client.js +57 -0
  77. package/dist/tempo/client.js.map +1 -1
  78. package/dist/tempo/formatters.d.ts +13 -0
  79. package/dist/tempo/formatters.d.ts.map +1 -1
  80. package/dist/tempo/formatters.js +106 -20
  81. package/dist/tempo/formatters.js.map +1 -1
  82. package/dist/tempo/tools.d.ts +14 -13
  83. package/dist/tempo/tools.d.ts.map +1 -1
  84. package/dist/tempo/tools.js +203 -33
  85. package/dist/tempo/tools.js.map +1 -1
  86. package/dist/tempo/types.d.ts +20 -6
  87. package/dist/tempo/types.d.ts.map +1 -1
  88. package/dist/transport/http.d.ts +21 -5
  89. package/dist/transport/http.d.ts.map +1 -1
  90. package/dist/transport/http.js +193 -22
  91. package/dist/transport/http.js.map +1 -1
  92. package/dist/transport/index.d.ts +7 -2
  93. package/dist/transport/index.d.ts.map +1 -1
  94. package/dist/transport/index.js +10 -4
  95. package/dist/transport/index.js.map +1 -1
  96. package/dist/utils/buffer-tools.d.ts +48 -724
  97. package/dist/utils/buffer-tools.d.ts.map +1 -1
  98. package/dist/utils/buffer-tools.js +337 -170
  99. package/dist/utils/buffer-tools.js.map +1 -1
  100. package/dist/utils/content-buffer.d.ts +10 -31
  101. package/dist/utils/content-buffer.d.ts.map +1 -1
  102. package/dist/utils/content-buffer.js +12 -86
  103. package/dist/utils/content-buffer.js.map +1 -1
  104. package/dist/utils/http-client.d.ts.map +1 -1
  105. package/dist/utils/http-client.js +99 -2
  106. package/dist/utils/http-client.js.map +1 -1
  107. package/dist/utils/jicon-help.d.ts +3 -3
  108. package/dist/utils/jicon-help.d.ts.map +1 -1
  109. package/dist/utils/jicon-help.js +164 -312
  110. package/dist/utils/jicon-help.js.map +1 -1
  111. package/dist/utils/logger.d.ts +43 -0
  112. package/dist/utils/logger.d.ts.map +1 -0
  113. package/dist/utils/logger.js +102 -0
  114. package/dist/utils/logger.js.map +1 -0
  115. package/dist/utils/plantuml/tools.d.ts.map +1 -1
  116. package/dist/utils/plantuml/tools.js +10 -9
  117. package/dist/utils/plantuml/tools.js.map +1 -1
  118. package/dist/utils/response-formatter.d.ts +20 -2
  119. package/dist/utils/response-formatter.d.ts.map +1 -1
  120. package/dist/utils/response-formatter.js +147 -17
  121. package/dist/utils/response-formatter.js.map +1 -1
  122. package/dist/utils/sandbox/formatters.d.ts +25 -0
  123. package/dist/utils/sandbox/formatters.d.ts.map +1 -0
  124. package/dist/utils/sandbox/formatters.js +690 -0
  125. package/dist/utils/sandbox/formatters.js.map +1 -0
  126. package/dist/utils/sandbox/helpers.d.ts +16 -0
  127. package/dist/utils/sandbox/helpers.d.ts.map +1 -0
  128. package/dist/utils/sandbox/helpers.js +252 -0
  129. package/dist/utils/sandbox/helpers.js.map +1 -0
  130. package/dist/utils/sandbox/index.d.ts +19 -0
  131. package/dist/utils/sandbox/index.d.ts.map +1 -0
  132. package/dist/utils/sandbox/index.js +269 -0
  133. package/dist/utils/sandbox/index.js.map +1 -0
  134. package/dist/utils/sandbox/schema.d.ts +55 -0
  135. package/dist/utils/sandbox/schema.d.ts.map +1 -0
  136. package/dist/utils/sandbox/schema.js +39 -0
  137. package/dist/utils/sandbox/schema.js.map +1 -0
  138. package/dist/utils/sandbox/types.d.ts +179 -0
  139. package/dist/utils/sandbox/types.d.ts.map +1 -0
  140. package/dist/utils/sandbox/types.js +8 -0
  141. package/dist/utils/sandbox/types.js.map +1 -0
  142. package/dist/utils/schemas/confluence.d.ts +41 -0
  143. package/dist/utils/schemas/confluence.d.ts.map +1 -0
  144. package/dist/utils/schemas/confluence.js +105 -0
  145. package/dist/utils/schemas/confluence.js.map +1 -0
  146. package/dist/utils/schemas/index.d.ts +77 -0
  147. package/dist/utils/schemas/index.d.ts.map +1 -0
  148. package/dist/utils/schemas/index.js +107 -0
  149. package/dist/utils/schemas/index.js.map +1 -0
  150. package/dist/utils/schemas/jira.d.ts +49 -0
  151. package/dist/utils/schemas/jira.d.ts.map +1 -0
  152. package/dist/utils/schemas/jira.js +153 -0
  153. package/dist/utils/schemas/jira.js.map +1 -0
  154. package/dist/utils/schemas/tempo.d.ts +29 -0
  155. package/dist/utils/schemas/tempo.d.ts.map +1 -0
  156. package/dist/utils/schemas/tempo.js +72 -0
  157. package/dist/utils/schemas/tempo.js.map +1 -0
  158. package/dist/utils/whoami-tools.d.ts +17 -0
  159. package/dist/utils/whoami-tools.d.ts.map +1 -0
  160. package/dist/utils/whoami-tools.js +90 -0
  161. package/dist/utils/whoami-tools.js.map +1 -0
  162. package/dist/utils/xhtml/error-locator.js +5 -5
  163. package/dist/utils/xhtml/error-locator.js.map +1 -1
  164. package/package.json +10 -9
  165. package/dist/credentials/client-factory.d.ts +0 -64
  166. package/dist/credentials/client-factory.d.ts.map +0 -1
  167. package/dist/credentials/client-factory.js +0 -110
  168. package/dist/credentials/client-factory.js.map +0 -1
  169. package/dist/credentials/context.d.ts +0 -25
  170. package/dist/credentials/context.d.ts.map +0 -1
  171. package/dist/credentials/context.js +0 -35
  172. package/dist/credentials/context.js.map +0 -1
  173. package/dist/utils/buffer-pipeline/index.d.ts +0 -30
  174. package/dist/utils/buffer-pipeline/index.d.ts.map +0 -1
  175. package/dist/utils/buffer-pipeline/index.js +0 -317
  176. package/dist/utils/buffer-pipeline/index.js.map +0 -1
  177. package/dist/utils/buffer-pipeline/output/csv.d.ts +0 -20
  178. package/dist/utils/buffer-pipeline/output/csv.d.ts.map +0 -1
  179. package/dist/utils/buffer-pipeline/output/csv.js +0 -117
  180. package/dist/utils/buffer-pipeline/output/csv.js.map +0 -1
  181. package/dist/utils/buffer-pipeline/output/json.d.ts +0 -16
  182. package/dist/utils/buffer-pipeline/output/json.d.ts.map +0 -1
  183. package/dist/utils/buffer-pipeline/output/json.js +0 -48
  184. package/dist/utils/buffer-pipeline/output/json.js.map +0 -1
  185. package/dist/utils/buffer-pipeline/output/markdown.d.ts +0 -15
  186. package/dist/utils/buffer-pipeline/output/markdown.d.ts.map +0 -1
  187. package/dist/utils/buffer-pipeline/output/markdown.js +0 -105
  188. package/dist/utils/buffer-pipeline/output/markdown.js.map +0 -1
  189. package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts +0 -16
  190. package/dist/utils/buffer-pipeline/output/xhtml-list.d.ts.map +0 -1
  191. package/dist/utils/buffer-pipeline/output/xhtml-list.js +0 -81
  192. package/dist/utils/buffer-pipeline/output/xhtml-list.js.map +0 -1
  193. package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts +0 -15
  194. package/dist/utils/buffer-pipeline/output/xhtml-table.d.ts.map +0 -1
  195. package/dist/utils/buffer-pipeline/output/xhtml-table.js +0 -176
  196. package/dist/utils/buffer-pipeline/output/xhtml-table.js.map +0 -1
  197. package/dist/utils/buffer-pipeline/schema.d.ts +0 -1878
  198. package/dist/utils/buffer-pipeline/schema.d.ts.map +0 -1
  199. package/dist/utils/buffer-pipeline/schema.js +0 -168
  200. package/dist/utils/buffer-pipeline/schema.js.map +0 -1
  201. package/dist/utils/buffer-pipeline/stages/filter.d.ts +0 -32
  202. package/dist/utils/buffer-pipeline/stages/filter.d.ts.map +0 -1
  203. package/dist/utils/buffer-pipeline/stages/filter.js +0 -208
  204. package/dist/utils/buffer-pipeline/stages/filter.js.map +0 -1
  205. package/dist/utils/buffer-pipeline/stages/format.d.ts +0 -45
  206. package/dist/utils/buffer-pipeline/stages/format.d.ts.map +0 -1
  207. package/dist/utils/buffer-pipeline/stages/format.js +0 -160
  208. package/dist/utils/buffer-pipeline/stages/format.js.map +0 -1
  209. package/dist/utils/buffer-pipeline/stages/group-by.d.ts +0 -25
  210. package/dist/utils/buffer-pipeline/stages/group-by.d.ts.map +0 -1
  211. package/dist/utils/buffer-pipeline/stages/group-by.js +0 -190
  212. package/dist/utils/buffer-pipeline/stages/group-by.js.map +0 -1
  213. package/dist/utils/buffer-pipeline/stages/select.d.ts +0 -54
  214. package/dist/utils/buffer-pipeline/stages/select.d.ts.map +0 -1
  215. package/dist/utils/buffer-pipeline/stages/select.js +0 -228
  216. package/dist/utils/buffer-pipeline/stages/select.js.map +0 -1
  217. package/dist/utils/buffer-pipeline/stages/sort.d.ts +0 -20
  218. package/dist/utils/buffer-pipeline/stages/sort.d.ts.map +0 -1
  219. package/dist/utils/buffer-pipeline/stages/sort.js +0 -96
  220. package/dist/utils/buffer-pipeline/stages/sort.js.map +0 -1
  221. package/dist/utils/buffer-pipeline/types.d.ts +0 -277
  222. package/dist/utils/buffer-pipeline/types.d.ts.map +0 -1
  223. package/dist/utils/buffer-pipeline/types.js +0 -8
  224. package/dist/utils/buffer-pipeline/types.js.map +0 -1
  225. package/dist/utils/plantuml/docker-manager.d.ts +0 -37
  226. package/dist/utils/plantuml/docker-manager.d.ts.map +0 -1
  227. package/dist/utils/plantuml/docker-manager.js +0 -284
  228. package/dist/utils/plantuml/docker-manager.js.map +0 -1
@@ -3,11 +3,15 @@
3
3
  *
4
4
  * Provides tools to retrieve chunks from buffered content,
5
5
  * list active buffers, clear buffers, search content, and edit content.
6
+ *
7
+ * All buffer operations use the session-scoped buffer from getSessionBuffer()
8
+ * which provides isolation between MCP sessions.
6
9
  */
7
10
  import { z } from "zod";
8
- import { contentBuffer } from "./content-buffer.js";
11
+ import { getSessionBuffer } from "../session/context.js";
9
12
  import { formatSuccess, formatSuccessDirect, formatError, getMaxOutputSize } from "./response-formatter.js";
10
- import { executePipelineWithOutput, validatePipeline, BufferPipelineInputSchema, } from "./buffer-pipeline/index.js";
13
+ import { getSchema, extractSamples } from "./schemas/index.js";
14
+ import { executeTransform, BufferTransformInputSchema, } from "./sandbox/index.js";
11
15
  import { createJsonStructure, replaceInJsonArray, setByKeys, appendToJsonArray } from "./json-structure.js";
12
16
  import { parseXhtml, serializeXhtml, validateXhtmlAsync, buildPlantUmlMacro, detectRawPlantUml, detectPlantUmlInContent,
13
17
  // Element ID-based operations
@@ -63,7 +67,7 @@ function formatGrepOutput(result, showLineNumbers) {
63
67
  * Type-safe buffer access: tools should only accept buffers of the type they understand.
64
68
  */
65
69
  function validateBufferType(bufferId, expectedTypes, toolName) {
66
- const bufferInfo = contentBuffer.getInfo(bufferId);
70
+ const bufferInfo = getSessionBuffer().getInfo(bufferId);
67
71
  if (!bufferInfo) {
68
72
  return {
69
73
  error: true,
@@ -74,7 +78,7 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
74
78
  details: {
75
79
  code: "BUFFER_NOT_FOUND",
76
80
  bufferId,
77
- hint: "Use buffer_list() to see active buffers",
81
+ hint: 'Use buffer_list() to see active buffers. Call help(topic="buffers") for buffer workflow guide.',
78
82
  },
79
83
  }),
80
84
  };
@@ -82,11 +86,11 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
82
86
  const actualType = bufferInfo.metadata?.contentType;
83
87
  if (!expectedTypes.includes(actualType ?? "")) {
84
88
  const typeHints = {
85
- json: "Use buffer_pipeline for JSON data, or buffer_create(contentType='json') to create JSON buffers.",
89
+ json: "Use buffer_transform for JSON data, or buffer_create(contentType='json') to create JSON buffers.",
86
90
  xhtml: "Use buffer_create(contentType='xhtml') to create XHTML buffers, or confluence_get_page/confluence_edit to load Confluence content.",
87
91
  };
88
92
  const suggestedTools = {
89
- json: "buffer_pipeline, buffer_get_items, buffer_set_items",
93
+ json: "buffer_transform, buffer_get_items, buffer_set_items",
90
94
  xhtml: "buffer_edit, buffer_get_structure, buffer_get_element, confluence_draft_create",
91
95
  };
92
96
  const hint = actualType ? typeHints[actualType] || "" : "";
@@ -104,6 +108,7 @@ function validateBufferType(bufferId, expectedTypes, toolName) {
104
108
  actualType: actualType || "unknown",
105
109
  hint: hint || `Create a ${expectedTypes[0]} buffer first.`,
106
110
  suggestedTools: suggested ? `For ${actualType} buffers, use: ${suggested}` : undefined,
111
+ tip: 'Call help(topic="buffers") for buffer types and workflow guide.',
107
112
  },
108
113
  }),
109
114
  };
@@ -153,7 +158,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
153
158
  const op = operations[opIndex];
154
159
  if (op.fromBufferId) {
155
160
  // Validate source buffer exists
156
- const sourceInfo = contentBuffer.getInfo(op.fromBufferId);
161
+ const sourceInfo = getSessionBuffer().getInfo(op.fromBufferId);
157
162
  if (!sourceInfo) {
158
163
  return formatError({
159
164
  error: true,
@@ -162,7 +167,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
162
167
  });
163
168
  }
164
169
  // Get source buffer content
165
- const sourceChunk = contentBuffer.getChunk(op.fromBufferId, 0, sourceInfo.bufferSizeBytes);
170
+ const sourceChunk = getSessionBuffer().getChunk(op.fromBufferId, 0, sourceInfo.bufferSizeBytes);
166
171
  if (!sourceChunk) {
167
172
  return formatError({
168
173
  error: true,
@@ -183,7 +188,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
183
188
  }
184
189
  }
185
190
  // Get buffer content
186
- const chunk = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
191
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
187
192
  if (!chunk) {
188
193
  return formatError({
189
194
  error: true,
@@ -366,7 +371,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
366
371
  // Rebuild structure
367
372
  const newStructure = parseStructure(parseResult.document);
368
373
  // Update buffer
369
- contentBuffer.update(args.bufferId, newContent, { contentType: "xhtml" }, newStructure.structure, newStructure.nextId);
374
+ getSessionBuffer().update(args.bufferId, newContent, { contentType: "xhtml" }, newStructure.structure, newStructure.nextId);
370
375
  return formatSuccessDirect({
371
376
  bufferId: args.bufferId,
372
377
  success: true,
@@ -385,7 +390,7 @@ async function handleXhtmlEdit(args, bufferInfo) {
385
390
  function handlePlainTextEdit(args) {
386
391
  // Handle append for plain/json buffers
387
392
  if (args.append && args.content) {
388
- const result = contentBuffer.append(args.bufferId, args.content);
393
+ const result = getSessionBuffer().append(args.bufferId, args.content);
389
394
  if (!result) {
390
395
  return formatError({
391
396
  error: true,
@@ -412,7 +417,7 @@ function handlePlainTextEdit(args) {
412
417
  },
413
418
  });
414
419
  }
415
- const result = contentBuffer.edit(args.bufferId, args.oldString, args.newString, args.replaceAll ?? false);
420
+ const result = getSessionBuffer().edit(args.bufferId, args.oldString, args.newString, args.replaceAll ?? false);
416
421
  if (!result) {
417
422
  return formatError({
418
423
  error: true,
@@ -435,7 +440,7 @@ export function createBufferTools() {
435
440
  buffer_create: {
436
441
  description: `Create a new buffer with initial content. Returns bufferId and structure.
437
442
 
438
- Types: xhtml (Confluence storage, returns element IDs), json (data, use buffer_get_items or buffer_pipeline).
443
+ Types: xhtml (Confluence storage, returns element IDs), json (data, use buffer_get_items or buffer_transform).
439
444
  JSON accepts string or native object/array (auto-stringified).
440
445
  Run help(topic="storage") for XHTML syntax.`,
441
446
  inputSchema: z.object({
@@ -511,7 +516,7 @@ Run help(topic="storage") for XHTML syntax.`,
511
516
  nextId = structureResult.nextId;
512
517
  // Serialize back (now with data-jicon-id attributes)
513
518
  const contentWithIds = serializeXhtml(parseResult.document);
514
- bufferId = contentBuffer.storeWithStructure(contentWithIds, structure, nextId, mergedMetadata);
519
+ bufferId = getSessionBuffer().storeWithStructure(contentWithIds, structure, nextId, mergedMetadata);
515
520
  }
516
521
  else if (args.contentType === "json") {
517
522
  // Parse JSON and create structure with item IDs (for arrays)
@@ -529,14 +534,14 @@ Run help(topic="storage") for XHTML syntax.`,
529
534
  // Create JSON structure (handles arrays with IDs, objects with keys)
530
535
  const { content: contentWithIds, structure: jsonStruct } = createJsonStructure(data);
531
536
  jsonStructure = jsonStruct;
532
- bufferId = contentBuffer.storeJsonWithStructure(contentWithIds, jsonStruct, mergedMetadata);
537
+ bufferId = getSessionBuffer().storeJsonWithStructure(contentWithIds, jsonStruct, mergedMetadata);
533
538
  }
534
539
  else {
535
540
  // TypeScript exhaustiveness check - should never reach here
536
541
  const _exhaustiveCheck = args.contentType;
537
542
  throw new Error(`Unsupported content type: ${_exhaustiveCheck}`);
538
543
  }
539
- const info = contentBuffer.getInfo(bufferId);
544
+ const info = getSessionBuffer().getInfo(bufferId);
540
545
  // Build response based on content type
541
546
  const isArray = jsonStructure?.rootType === "array";
542
547
  const isObject = jsonStructure?.rootType === "object";
@@ -548,7 +553,7 @@ Run help(topic="storage") for XHTML syntax.`,
548
553
  : undefined;
549
554
  // Build hint for JSON buffers
550
555
  const hint = args.contentType === "json" && isArray
551
- ? `Use buffer_pipeline(sourceBufferId='${bufferId}', pipeline={...}) to filter, transform, and output as XHTML/CSV/JSON.`
556
+ ? `Use buffer_transform(inputs={data:'${bufferId}'}, code='...') to filter, transform, and output.`
552
557
  : args.contentType === "json"
553
558
  ? `Use buffer_get_items(bufferId='${bufferId}', keys=[...]) to access values, or buffer_grep to search.`
554
559
  : undefined;
@@ -557,7 +562,6 @@ Run help(topic="storage") for XHTML syntax.`,
557
562
  bufferSizeBytes: info?.bufferSizeBytes ?? contentStr.length,
558
563
  contentType: args.contentType,
559
564
  createdAt: info ? new Date(info.createdAt).toISOString() : new Date().toISOString(),
560
- expiresAt: info ? new Date(info.expiresAt).toISOString() : undefined,
561
565
  metadata: mergedMetadata,
562
566
  // XHTML structure
563
567
  ...(structure && { structure, nextId }),
@@ -573,7 +577,7 @@ Run help(topic="storage") for XHTML syntax.`,
573
577
  ...(hint && { hint }),
574
578
  message: args.contentType === "xhtml"
575
579
  ? "Buffer created with element IDs. Use buffer_edit with after/before/replace to modify."
576
- : "JSON buffer created. Use buffer_get_items for access or buffer_pipeline for transformation.",
580
+ : "JSON buffer created. Use buffer_get_items for access or buffer_transform for transformation.",
577
581
  });
578
582
  }
579
583
  catch (error) {
@@ -614,7 +618,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
614
618
  if (typeError)
615
619
  return typeError.result;
616
620
  // Get full buffer content
617
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
621
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
618
622
  if (!bufferInfo) {
619
623
  return formatError({
620
624
  error: true,
@@ -622,7 +626,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
622
626
  statusCode: 404,
623
627
  });
624
628
  }
625
- const chunk = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
629
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
626
630
  if (!chunk) {
627
631
  return formatError({
628
632
  error: true,
@@ -653,7 +657,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
653
657
  if (parsed.length > 0 && typeof parsed[0] === "object" && parsed[0] !== null) {
654
658
  itemSchema = Object.keys(parsed[0]);
655
659
  }
656
- return formatSuccess({
660
+ return formatSuccessDirect({
657
661
  bufferId: args.bufferId,
658
662
  type: "array",
659
663
  items,
@@ -663,7 +667,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
663
667
  hasMore,
664
668
  nextStart: hasMore ? start + count : undefined,
665
669
  itemSchema: itemSchema ? itemSchema.slice(0, 20) : undefined,
666
- });
670
+ }, { truncateHint: "Too many items. Use smaller count parameter." });
667
671
  }
668
672
  // Handle objects (new behavior)
669
673
  if (typeof parsed === "object" && parsed !== null) {
@@ -683,7 +687,7 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
683
687
  missingKeys.push(key);
684
688
  }
685
689
  }
686
- return formatSuccess({
690
+ return formatSuccessDirect({
687
691
  bufferId: args.bufferId,
688
692
  type: "object",
689
693
  data: result,
@@ -691,20 +695,20 @@ Returns: For arrays: items, totalItems, hasMore. For objects: data, keys.`,
691
695
  foundKeys,
692
696
  ...(missingKeys.length > 0 && { missingKeys }),
693
697
  availableKeys: allKeys,
694
- });
698
+ }, { truncateHint: "Response truncated. Request fewer keys." });
695
699
  }
696
700
  else {
697
701
  // Return all keys
698
- return formatSuccess({
702
+ return formatSuccessDirect({
699
703
  bufferId: args.bufferId,
700
704
  type: "object",
701
705
  data,
702
706
  keys: allKeys,
703
- });
707
+ }, { truncateHint: "Response truncated. Use keys parameter to request specific fields." });
704
708
  }
705
709
  }
706
710
  // Handle primitives (edge case)
707
- return formatSuccess({
711
+ return formatSuccessDirect({
708
712
  bufferId: args.bufferId,
709
713
  type: "primitive",
710
714
  data: parsed,
@@ -749,7 +753,7 @@ Returns: Updated buffer info with changes summary.`,
749
753
  if (typeError)
750
754
  return typeError.result;
751
755
  // Get buffer info
752
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
756
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
753
757
  if (!bufferInfo) {
754
758
  return formatError({
755
759
  error: true,
@@ -776,7 +780,7 @@ Returns: Updated buffer info with changes summary.`,
776
780
  },
777
781
  });
778
782
  }
779
- const chunk = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
783
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
780
784
  if (!chunk) {
781
785
  return formatError({
782
786
  error: true,
@@ -801,7 +805,7 @@ Returns: Updated buffer info with changes summary.`,
801
805
  });
802
806
  }
803
807
  // Update buffer with new content
804
- const updated = contentBuffer.updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
808
+ const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
805
809
  if (!updated) {
806
810
  return formatError({
807
811
  error: true,
@@ -846,7 +850,7 @@ Returns: Updated buffer info with changes summary.`,
846
850
  });
847
851
  }
848
852
  // Update buffer with new content
849
- const updated = contentBuffer.updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
853
+ const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
850
854
  if (!updated) {
851
855
  return formatError({
852
856
  error: true,
@@ -890,7 +894,7 @@ Returns: Updated buffer info with changes summary.`,
890
894
  });
891
895
  }
892
896
  // Update buffer with new content
893
- const updated = contentBuffer.updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
897
+ const updated = getSessionBuffer().updateJsonStructure(args.bufferId, result.content, result.structure, { contentType: "json" });
894
898
  if (!updated) {
895
899
  return formatError({
896
900
  error: true,
@@ -920,18 +924,17 @@ Returns: Updated buffer info with changes summary.`,
920
924
  },
921
925
  },
922
926
  buffer_list: {
923
- description: `List all active buffers with metadata. Buffers expire after 1 hour (24h if modified).
924
- Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost buffer IDs.`,
927
+ description: `List all active buffers with metadata. Buffers are cleaned up when session ends.
928
+ Max buffers per session configurable via JICON_MAX_BUFFERS (default: 10); oldest unmodified evicted first when full.`,
925
929
  inputSchema: z.object({}),
926
930
  handler: async () => {
927
931
  try {
928
- const buffers = contentBuffer.list();
932
+ const buffers = getSessionBuffer().list();
929
933
  return formatSuccess({
930
934
  count: buffers.length,
931
935
  buffers: buffers.map((b) => ({
932
936
  ...b,
933
937
  createdAt: new Date(b.createdAt).toISOString(),
934
- expiresAt: new Date(b.expiresAt).toISOString(),
935
938
  })),
936
939
  });
937
940
  }
@@ -940,6 +943,113 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
940
943
  }
941
944
  },
942
945
  },
946
+ buffer_get_schema: {
947
+ description: `Get the schema for a JSON buffer. Returns field names, types, and sample values.
948
+
949
+ Use this to understand buffer structure before writing buffer_transform code.
950
+ The schema shows all available fields with their types and example values from the actual data.
951
+
952
+ Returns:
953
+ - schemaType: Named schema type (e.g., "jira_issue", "tempo_worklog")
954
+ - fields: Object with field definitions (type, required, description, sample)
955
+ - itemCount: Number of items (for arrays)
956
+
957
+ Example: buffer_get_schema(bufferId="buf_xxx")`,
958
+ inputSchema: z.object({
959
+ bufferId: z.string().describe("Buffer ID to get schema for"),
960
+ }),
961
+ handler: async (args) => {
962
+ try {
963
+ // Type-safe buffer access: buffer_get_schema requires JSON buffer
964
+ const typeError = validateBufferType(args.bufferId, ["json"], "buffer_get_schema");
965
+ if (typeError)
966
+ return typeError.result;
967
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
968
+ if (!bufferInfo) {
969
+ return formatError({
970
+ error: true,
971
+ message: `Buffer not found or expired: ${args.bufferId}`,
972
+ statusCode: 404,
973
+ });
974
+ }
975
+ const schemaType = bufferInfo.schemaType;
976
+ // If buffer has a schemaType, get the schema definition and samples
977
+ if (schemaType) {
978
+ const schemaDef = getSchema(schemaType);
979
+ if (schemaDef) {
980
+ // Get actual data to extract samples
981
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
982
+ if (chunk) {
983
+ try {
984
+ const data = JSON.parse(chunk.chunk);
985
+ const schemaWithSamples = extractSamples(schemaType, data);
986
+ if (schemaWithSamples) {
987
+ return formatSuccessDirect({
988
+ bufferId: args.bufferId,
989
+ schemaType,
990
+ description: schemaDef.description,
991
+ itemCount: bufferInfo.jsonStructure?.itemCount,
992
+ legend: {
993
+ required: "Always present with a non-null value",
994
+ optional: "May be null when not set — use `== null` to check",
995
+ searchable: "Can be used in JQL/CQL queries",
996
+ writable: "Can be set via update API",
997
+ jqlName: "JQL clause name when different from field name (e.g., type → issuetype)",
998
+ },
999
+ fields: schemaWithSamples.fields,
1000
+ }, { truncateHint: "Schema truncated." });
1001
+ }
1002
+ }
1003
+ catch {
1004
+ // Fall through to return schema without samples
1005
+ }
1006
+ }
1007
+ // Return schema without samples if we couldn't extract them
1008
+ return formatSuccessDirect({
1009
+ bufferId: args.bufferId,
1010
+ schemaType,
1011
+ description: schemaDef.description,
1012
+ itemCount: bufferInfo.jsonStructure?.itemCount,
1013
+ legend: {
1014
+ required: "Always present with a non-null value",
1015
+ optional: "May be null when not set — use `== null` to check",
1016
+ searchable: "Can be used in JQL/CQL queries",
1017
+ writable: "Can be set via update API",
1018
+ jqlName: "JQL clause name when different from field name (e.g., type → issuetype)",
1019
+ },
1020
+ fields: schemaDef.fields,
1021
+ }, { truncateHint: "Schema truncated." });
1022
+ }
1023
+ }
1024
+ // No schemaType - return structure info from buffer
1025
+ const jsonStructure = bufferInfo.jsonStructure;
1026
+ if (!jsonStructure) {
1027
+ return formatError({
1028
+ error: true,
1029
+ message: "Buffer does not have JSON structure",
1030
+ statusCode: 400,
1031
+ details: {
1032
+ hint: "This buffer may not be a JSON buffer or was created without structure tracking",
1033
+ },
1034
+ });
1035
+ }
1036
+ // For untyped buffers, return what we know
1037
+ return formatSuccessDirect({
1038
+ bufferId: args.bufferId,
1039
+ schemaType: null,
1040
+ note: "Buffer has no schema type. Use buffer_get_items to inspect data structure.",
1041
+ structure: {
1042
+ type: jsonStructure.rootType,
1043
+ itemCount: jsonStructure.itemCount,
1044
+ keys: jsonStructure.keys,
1045
+ },
1046
+ });
1047
+ }
1048
+ catch (error) {
1049
+ return formatError(error instanceof Error ? error : new Error(String(error)));
1050
+ }
1051
+ },
1052
+ },
943
1053
  buffer_clear: {
944
1054
  description: `Clear a specific buffer or all buffers. Omit bufferId to clear all.`,
945
1055
  inputSchema: z.object({
@@ -951,7 +1061,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
951
1061
  handler: async (args) => {
952
1062
  try {
953
1063
  if (args.bufferId) {
954
- const deleted = contentBuffer.clear(args.bufferId);
1064
+ const deleted = getSessionBuffer().clear(args.bufferId);
955
1065
  if (!deleted) {
956
1066
  return formatError({
957
1067
  error: true,
@@ -964,8 +1074,8 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
964
1074
  cleared: 1,
965
1075
  });
966
1076
  }
967
- const count = contentBuffer.size();
968
- contentBuffer.clear();
1077
+ const count = getSessionBuffer().size();
1078
+ getSessionBuffer().clear();
969
1079
  return formatSuccess({
970
1080
  message: "All buffers cleared",
971
1081
  cleared: count,
@@ -1027,7 +1137,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
1027
1137
  // Handle -C overriding -A/-B
1028
1138
  const before = args["-C"] ?? args["-B"] ?? 0;
1029
1139
  const after = args["-C"] ?? args["-A"] ?? 0;
1030
- const result = contentBuffer.grep(args.bufferId, args.pattern, {
1140
+ const result = getSessionBuffer().grep(args.bufferId, args.pattern, {
1031
1141
  caseInsensitive: args["-i"] ?? false,
1032
1142
  before,
1033
1143
  after,
@@ -1067,7 +1177,7 @@ Max 20 buffers; oldest unmodified evicted first when full. Use to recover lost b
1067
1177
  if (fullText.length > maxSize) {
1068
1178
  const wrapperData = { content: fullText, type: "grep_result" };
1069
1179
  const { content: jsonContent, structure } = createJsonStructure(wrapperData);
1070
- const newBufferId = contentBuffer.storeJsonWithStructure(jsonContent, structure, {
1180
+ const newBufferId = getSessionBuffer().storeJsonWithStructure(jsonContent, structure, {
1071
1181
  contentType: "json",
1072
1182
  type: "grep_result",
1073
1183
  sourceBufferId: args.bufferId,
@@ -1144,7 +1254,7 @@ Run help(topic="buffers") for editing guide and examples.`,
1144
1254
  }),
1145
1255
  handler: async (args) => {
1146
1256
  try {
1147
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
1257
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
1148
1258
  if (!bufferInfo) {
1149
1259
  return formatError({
1150
1260
  error: true,
@@ -1181,12 +1291,12 @@ Each element has:
1181
1291
  const typeError = validateBufferType(args.bufferId, ["xhtml"], "buffer_get_structure");
1182
1292
  if (typeError)
1183
1293
  return typeError.result;
1184
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
1185
- return formatSuccess({
1294
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
1295
+ return formatSuccessDirect({
1186
1296
  bufferId: args.bufferId,
1187
1297
  structure: bufferInfo?.structure ?? [],
1188
1298
  nextId: bufferInfo?.nextId ?? 1,
1189
- });
1299
+ }, { truncateHint: "Structure truncated. Document has many elements." });
1190
1300
  }
1191
1301
  catch (error) {
1192
1302
  return formatError(error instanceof Error ? error : new Error(String(error)));
@@ -1211,7 +1321,7 @@ Example: After a Confluence error at element 12, use this to see the content:
1211
1321
  const typeError = validateBufferType(args.bufferId, ["xhtml"], "buffer_get_element");
1212
1322
  if (typeError)
1213
1323
  return typeError.result;
1214
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
1324
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
1215
1325
  if (!bufferInfo) {
1216
1326
  return formatError({
1217
1327
  error: true,
@@ -1220,7 +1330,7 @@ Example: After a Confluence error at element 12, use this to see the content:
1220
1330
  });
1221
1331
  }
1222
1332
  // Get full buffer content
1223
- const chunk = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
1333
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
1224
1334
  if (!chunk) {
1225
1335
  return formatError({
1226
1336
  error: true,
@@ -1263,13 +1373,13 @@ Example: After a Confluence error at element 12, use this to see the content:
1263
1373
  if (elementType === "ac:structured-macro") {
1264
1374
  elementType = element.getAttribute("ac:name") || "macro";
1265
1375
  }
1266
- return formatSuccess({
1376
+ return formatSuccessDirect({
1267
1377
  bufferId: args.bufferId,
1268
1378
  elementId: args.elementId,
1269
1379
  elementType,
1270
1380
  lineCount,
1271
1381
  content,
1272
- });
1382
+ }, { truncateHint: "Element content truncated. Element is very large." });
1273
1383
  }
1274
1384
  catch (error) {
1275
1385
  return formatError(error instanceof Error ? error : new Error(String(error)));
@@ -1294,7 +1404,7 @@ Use before confluence_draft_create to catch errors early.`,
1294
1404
  if (typeError)
1295
1405
  return typeError.result;
1296
1406
  // Get buffer content
1297
- const bufferInfo = contentBuffer.getInfo(args.bufferId);
1407
+ const bufferInfo = getSessionBuffer().getInfo(args.bufferId);
1298
1408
  if (!bufferInfo) {
1299
1409
  return formatError({
1300
1410
  error: true,
@@ -1302,7 +1412,7 @@ Use before confluence_draft_create to catch errors early.`,
1302
1412
  statusCode: 404,
1303
1413
  });
1304
1414
  }
1305
- const chunk = contentBuffer.getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
1415
+ const chunk = getSessionBuffer().getChunk(args.bufferId, 0, bufferInfo.bufferSizeBytes);
1306
1416
  if (!chunk) {
1307
1417
  return formatError({
1308
1418
  error: true,
@@ -1352,147 +1462,172 @@ Use before confluence_draft_create to catch errors early.`,
1352
1462
  },
1353
1463
  },
1354
1464
  // =========================================================================
1355
- // Buffer Pipeline - Unified data transformation tool
1465
+ // Buffer Transform - JavaScript sandbox for data transformation
1356
1466
  // =========================================================================
1357
- buffer_pipeline: {
1358
- description: `Transform JSON array with declarative pipeline (server-side, 98% token reduction).
1467
+ buffer_transform: {
1468
+ description: `Execute JavaScript code in a secure sandbox to transform buffer data.
1469
+
1470
+ IMPORTANT: Call buffer_get_schema(bufferId) BEFORE writing transform code to discover field names, types, and which fields are optional (nullable).
1359
1471
 
1360
- STAGES: select → filter → groupBy → sort → limit → format → output (required)
1361
- OUTPUT: xhtml_table | xhtml_list | json | csv | markdown
1362
- NOTE: xhtml/csv/md outputs are NOT iterable (buffer_get_items won't work). json IS iterable.
1363
- RETURNS: bufferId, contentType, itemCount
1472
+ FLAT FIELD ACCESS:
1473
+ Buffer data uses FLAT fields (not nested). Use the field names from the schema.
1474
+ CORRECT: i.status, i.assignee, i.priority
1475
+ WRONG: i.status.name, i.fields.status.name, i.assignee?.displayName
1364
1476
 
1365
- Example: pipeline={filter:[{field:"status.name",operator:"ne",value:"Done"}],output:{type:"xhtml_table",table:{columns:[{field:"key"},{field:"summary"}]}}}
1477
+ NULL HANDLING: Optional fields (priority, assignee, time fields) are null when not set.
1478
+ Filter unset: i.timeoriginalestimate == null (catches both null and undefined)
1479
+ Filter zero: i.timeoriginalestimate === 0 (only explicit zero)
1366
1480
 
1367
- IMPORTANT: pipeline must be object (not string). Use "sort" (not "sortBy"). output is REQUIRED.
1368
- Run help(topic="buffers") for full schema, operators, and examples.`,
1369
- inputSchema: BufferPipelineInputSchema,
1481
+ INPUTS: Map of variable names to buffer IDs. Buffers are loaded as globals.
1482
+ CODE: ES2020 JavaScript (synchronous). Return value becomes output buffer(s).
1483
+
1484
+ OUTPUT FORMAT:
1485
+ - For CONFLUENCE → toTable(), toList() → XHTML
1486
+ - For DISPLAY → toMarkdownTable(), toMarkdownList() → Markdown
1487
+
1488
+ HELPERS: groupBy, sum, avg, min, max, count, unique, get, pick, omit, parseDate, formatDate, daysBetween
1489
+
1490
+ Example:
1491
+ inputs: { issues: "buf_xxx" }
1492
+ code: \`
1493
+ const open = issues.filter(i => i.status !== 'Done');
1494
+ return toMarkdownTable(open, {
1495
+ columns: [
1496
+ { field: 'key', header: 'Issue', link: { type: 'jira' } },
1497
+ { field: 'summary', header: 'Summary' },
1498
+ { field: 'status', header: 'Status' },
1499
+ { field: 'assignee', header: 'Assignee' }
1500
+ ]
1501
+ });
1502
+ \``,
1503
+ inputSchema: BufferTransformInputSchema,
1370
1504
  handler: async (args) => {
1371
1505
  try {
1372
- // Type-safe buffer access: buffer_pipeline requires JSON buffer
1373
- const typeError = validateBufferType(args.sourceBufferId, ["json"], "buffer_pipeline");
1374
- if (typeError)
1375
- return typeError.result;
1376
- // 1. Get buffer info
1377
- const bufferInfo = contentBuffer.getInfo(args.sourceBufferId);
1378
- if (!bufferInfo) {
1379
- return formatError({
1380
- error: true,
1381
- message: `Buffer not found or expired: ${args.sourceBufferId}`,
1382
- statusCode: 404,
1383
- });
1384
- }
1385
- // 2. Get buffer content
1386
- const chunk = contentBuffer.getChunk(args.sourceBufferId, 0, bufferInfo.bufferSizeBytes);
1387
- if (!chunk) {
1388
- return formatError({
1389
- error: true,
1390
- message: `Failed to read buffer content: ${args.sourceBufferId}`,
1391
- statusCode: 500,
1392
- });
1393
- }
1394
- // 3. Parse JSON array
1395
- let data;
1396
- try {
1397
- const parsed = JSON.parse(chunk.chunk);
1398
- if (!Array.isArray(parsed)) {
1506
+ // 1. Load all input buffers
1507
+ const inputData = {};
1508
+ for (const [name, bufferId] of Object.entries(args.inputs)) {
1509
+ // Type-safe buffer access: buffer_transform requires JSON buffers
1510
+ const typeError = validateBufferType(bufferId, ["json"], "buffer_transform");
1511
+ if (typeError) {
1399
1512
  return formatError({
1400
1513
  error: true,
1401
- message: "Buffer content is not a JSON array. buffer_pipeline requires array data.",
1514
+ message: `Input "${name}": ${typeError.result.content[0].type === "text" ? JSON.parse(typeError.result.content[0].text).message : "Invalid buffer"}`,
1402
1515
  statusCode: 400,
1403
- details: { type: typeof parsed },
1516
+ details: { inputName: name, bufferId },
1517
+ });
1518
+ }
1519
+ const bufferInfo = getSessionBuffer().getInfo(bufferId);
1520
+ if (!bufferInfo) {
1521
+ return formatError({
1522
+ error: true,
1523
+ message: `Input "${name}": Buffer not found or expired: ${bufferId}`,
1524
+ statusCode: 404,
1525
+ details: { inputName: name, bufferId },
1526
+ });
1527
+ }
1528
+ const chunk = getSessionBuffer().getChunk(bufferId, 0, bufferInfo.bufferSizeBytes);
1529
+ if (!chunk) {
1530
+ return formatError({
1531
+ error: true,
1532
+ message: `Input "${name}": Failed to read buffer: ${bufferId}`,
1533
+ statusCode: 500,
1534
+ details: { inputName: name, bufferId },
1535
+ });
1536
+ }
1537
+ try {
1538
+ inputData[name] = JSON.parse(chunk.chunk);
1539
+ }
1540
+ catch (e) {
1541
+ return formatError({
1542
+ error: true,
1543
+ message: `Input "${name}": Failed to parse as JSON: ${e.message}`,
1544
+ statusCode: 400,
1545
+ details: { inputName: name, bufferId },
1404
1546
  });
1405
1547
  }
1406
- data = parsed;
1407
- }
1408
- catch (e) {
1409
- return formatError({
1410
- error: true,
1411
- message: `Failed to parse buffer as JSON: ${e.message}`,
1412
- statusCode: 400,
1413
- });
1414
1548
  }
1415
- // 4. Build pipeline context
1549
+ // 2. Build context
1416
1550
  const context = {
1417
- sourceBufferId: args.sourceBufferId,
1418
1551
  jiraUrl: process.env.JIRA_URL,
1419
1552
  confluenceUrl: process.env.CONFLUENCE_URL,
1420
- metadata: bufferInfo.metadata,
1421
1553
  };
1422
- // 5. Dry run - validate only
1423
- if (args.dryRun) {
1424
- const validation = validatePipeline(data, args.pipeline);
1554
+ // 3. Execute in sandbox
1555
+ const result = await executeTransform({ inputs: args.inputs, code: args.code, limits: args.limits }, context, inputData);
1556
+ // 4. Handle errors
1557
+ if ("error" in result && result.error) {
1558
+ return formatError({
1559
+ error: true,
1560
+ message: result.message,
1561
+ statusCode: result.code === "TIMEOUT" || result.code === "MEMORY_LIMIT" ? 408 : 400,
1562
+ details: {
1563
+ code: result.code,
1564
+ ...result.details,
1565
+ tip: 'Call help(topic="buffers") for transform syntax and helper functions.',
1566
+ },
1567
+ });
1568
+ }
1569
+ // 5. Store output(s) as buffer(s)
1570
+ const transformResult = result;
1571
+ // Handle multiple outputs
1572
+ if (transformResult.multipleOutputs) {
1573
+ const buffers = {};
1574
+ for (const [outputName, output] of Object.entries(transformResult.multipleOutputs)) {
1575
+ const bufferId = await storeTransformOutput(output.contentType, output.content, { outputName });
1576
+ buffers[outputName] = bufferId;
1577
+ }
1425
1578
  return formatSuccessDirect({
1426
- dryRun: true,
1427
- valid: validation.valid,
1428
- errors: validation.errors,
1429
- warnings: validation.warnings,
1430
- availableFields: validation.availableFields,
1431
- itemCount: data.length,
1579
+ buffers,
1580
+ outputCount: Object.keys(buffers).length,
1581
+ hint: "Multiple buffers created. Use buffer_get_items(bufferId) to access each.",
1432
1582
  });
1433
1583
  }
1434
- // 6. Execute pipeline
1435
- const pipelineResult = executePipelineWithOutput(data, args.pipeline, context);
1436
- if ("error" in pipelineResult) {
1437
- return formatError(pipelineResult.error);
1584
+ // Handle single XHTML output
1585
+ if (transformResult.xhtmlContent) {
1586
+ const bufferId = await storeTransformOutput("xhtml", transformResult.xhtmlContent, {});
1587
+ return formatSuccessDirect({
1588
+ bufferId,
1589
+ contentType: "xhtml",
1590
+ hint: "Use confluence_draft_create(bufferId) to publish, or buffer_edit to modify.",
1591
+ });
1438
1592
  }
1439
- // 7. Store output in buffer
1440
- const { result, output } = pipelineResult;
1441
- const outputType = args.pipeline.output.type;
1442
- // Determine content type for buffer (csv/markdown stored as JSON wrapper)
1443
- const contentType = outputType.startsWith("xhtml")
1444
- ? "xhtml"
1445
- : "json";
1446
- const bufferMetadata = {
1447
- contentType,
1448
- resourceType: "pipeline_output",
1449
- sourceBufferId: args.sourceBufferId,
1450
- pipelineStages: result.pipelineStages,
1451
- outputType,
1452
- };
1453
- let newBufferId;
1454
- if (contentType === "xhtml") {
1455
- // Parse XHTML to get structure with element IDs
1456
- const parseResult = parseXhtml(output);
1457
- if (parseResult.document) {
1458
- const structureResult = parseStructure(parseResult.document);
1459
- // Re-serialize to include data-jicon-id attributes
1460
- const contentWithIds = serializeXhtml(parseResult.document);
1461
- newBufferId = contentBuffer.storeWithStructure(contentWithIds, structureResult.structure, structureResult.nextId, bufferMetadata);
1462
- }
1463
- else {
1464
- // Fallback: store without structure if parsing fails
1465
- newBufferId = contentBuffer.store(output, bufferMetadata);
1593
+ // Handle single Markdown output - return content directly (displayable)
1594
+ if (transformResult.markdownContent) {
1595
+ const markdownContent = transformResult.markdownContent;
1596
+ const bufferId = await storeTransformOutput("markdown", markdownContent, {});
1597
+ const maxContentSize = Math.floor(getMaxOutputSize() * 0.9);
1598
+ if (markdownContent.length <= maxContentSize) {
1599
+ // Return content directly - no extra tool call needed
1600
+ return formatSuccessDirect({
1601
+ bufferId,
1602
+ format: "markdown",
1603
+ content: markdownContent,
1604
+ });
1466
1605
  }
1606
+ // Truncate large content
1607
+ return formatSuccessDirect({
1608
+ bufferId,
1609
+ format: "markdown",
1610
+ content: markdownContent.substring(0, maxContentSize) + "\n\n...[truncated]",
1611
+ truncated: true,
1612
+ fullSize: markdownContent.length,
1613
+ note: "Content truncated. Full content available via buffer_get_items(bufferId, keys=['content']).",
1614
+ });
1467
1615
  }
1468
- else {
1469
- // JSON content type - handles json, csv, and markdown outputs
1470
- if (outputType === "json") {
1471
- // Pure JSON output - parse and store with structure
1472
- try {
1473
- const data = JSON.parse(output);
1474
- const { content: contentWithIds, structure: jsonStruct } = createJsonStructure(data);
1475
- newBufferId = contentBuffer.storeJsonWithStructure(contentWithIds, jsonStruct, bufferMetadata);
1476
- }
1477
- catch {
1478
- // Fallback: wrap as JSON object if parsing fails
1479
- const wrapperData = { content: output };
1480
- const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapperData);
1481
- newBufferId = contentBuffer.storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
1482
- }
1483
- }
1484
- else {
1485
- // CSV/Markdown output - wrap in JSON object for accessor support
1486
- const wrapperData = { content: output, format: outputType };
1487
- const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapperData);
1488
- newBufferId = contentBuffer.storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
1489
- }
1616
+ // Handle single JSON output
1617
+ if (transformResult.jsonContent !== undefined) {
1618
+ const bufferId = await storeTransformOutput("json", transformResult.jsonContent, {});
1619
+ return formatSuccessDirect({
1620
+ bufferId,
1621
+ contentType: "json",
1622
+ itemCount: transformResult.itemCount,
1623
+ hint: "Use buffer_get_items(bufferId) to access data, or buffer_transform for further processing.",
1624
+ });
1490
1625
  }
1491
- // 8. Return result with bufferId
1492
- return formatSuccessDirect({
1493
- ...result,
1494
- bufferId: newBufferId,
1495
- contentType,
1626
+ // Should not reach here
1627
+ return formatError({
1628
+ error: true,
1629
+ message: "Unexpected transform result: no output generated",
1630
+ statusCode: 500,
1496
1631
  });
1497
1632
  }
1498
1633
  catch (error) {
@@ -1502,4 +1637,36 @@ Run help(topic="buffers") for full schema, operators, and examples.`,
1502
1637
  },
1503
1638
  };
1504
1639
  }
1640
+ /**
1641
+ * Store transform output as a buffer
1642
+ */
1643
+ async function storeTransformOutput(contentType, content, metadata) {
1644
+ const bufferMetadata = {
1645
+ contentType,
1646
+ resourceType: "transform_output",
1647
+ ...metadata,
1648
+ };
1649
+ if (contentType === "xhtml") {
1650
+ const xhtmlContent = content;
1651
+ const parseResult = parseXhtml(xhtmlContent);
1652
+ if (parseResult.document) {
1653
+ const structureResult = parseStructure(parseResult.document);
1654
+ const contentWithIds = serializeXhtml(parseResult.document);
1655
+ return getSessionBuffer().storeWithStructure(contentWithIds, structureResult.structure, structureResult.nextId, bufferMetadata);
1656
+ }
1657
+ return getSessionBuffer().store(xhtmlContent, bufferMetadata);
1658
+ }
1659
+ if (contentType === "markdown") {
1660
+ // Store markdown as a JSON wrapper for easy access via buffer_get_items
1661
+ const markdownContent = content;
1662
+ const wrapper = { content: markdownContent, format: "markdown" };
1663
+ const { content: jsonContent, structure: jsonStruct } = createJsonStructure(wrapper);
1664
+ // Override contentType to "json" so buffer_get_items can read it
1665
+ const markdownMetadata = { ...bufferMetadata, contentType: "json", originalType: "markdown" };
1666
+ return getSessionBuffer().storeJsonWithStructure(jsonContent, jsonStruct, markdownMetadata);
1667
+ }
1668
+ // JSON content
1669
+ const { content: jsonContent, structure: jsonStruct } = createJsonStructure(content);
1670
+ return getSessionBuffer().storeJsonWithStructure(jsonContent, jsonStruct, bufferMetadata);
1671
+ }
1505
1672
  //# sourceMappingURL=buffer-tools.js.map