@friendlyrobot/discord-pi-agent 0.4.0 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { DiscordPiBridge, DiscordPiBridgeConfig } from "./types";
2
2
  export { buildTimeContextPrompt, type TimeContextPromptOptions, } from "./prompt-context";
3
- export { transformMarkdownTablesToCodeBlocks, transformMarkdownTablesSync, } from "./markdown-table-transformer";
3
+ export { transformMarkdownTablesToCodeBlocks } from "./markdown-table-transformer";
4
4
  export { loadDiscordPiBridgeConfigFromEnv, resolveConfig } from "./config";
5
5
  export type { AgentStatus, DiscordPiBridge, DiscordPiBridgeConfig, PromptTransform, ResolvedDiscordPiBridgeConfig, } from "./types";
6
6
  export declare function startDiscordPiBridge(config: DiscordPiBridgeConfig): Promise<DiscordPiBridge>;
package/dist/index.js CHANGED
@@ -14,82 +14,33 @@ import {
14
14
  } from "@mariozechner/pi-coding-agent";
15
15
 
16
16
  // src/markdown-table-transformer.ts
17
- var TABLE_BLOCK_REGEX = /(\|.+?(?:\|.*)+)(\n\|[-:]+(?:\|[-:]+)+\|?)(\n\|.+?(?:\|.*)+\|?)+/g;
17
+ import { Lexer } from "marked";
18
18
  var CODE_BLOCK_WRAPPER = "```\n{TABLE}\n```";
19
19
  async function transformMarkdownTablesToCodeBlocks(text) {
20
- const matches = [...text.matchAll(TABLE_BLOCK_REGEX)];
21
- if (matches.length === 0) {
22
- return text;
23
- }
24
- const formattedTables = await Promise.all(matches.map((match) => formatTableWithPrettier(match[0])));
25
- let result = text;
26
- for (let i = 0;i < matches.length; i++) {
27
- const match = matches[i];
28
- const originalTable = match[0];
29
- const formattedTable = formattedTables[i];
30
- const wrappedTable = CODE_BLOCK_WRAPPER.replace("{TABLE}", formattedTable);
31
- result = result.replace(originalTable, wrappedTable);
20
+ const formatted = await formatWithPrettier(text);
21
+ const tokens = Lexer.lex(formatted);
22
+ const result = [];
23
+ for (const token of tokens) {
24
+ if (token.type === "table") {
25
+ result.push(CODE_BLOCK_WRAPPER.replace("{TABLE}", token.raw.trimEnd()));
26
+ } else {
27
+ result.push(token.raw);
28
+ }
32
29
  }
33
- return result;
30
+ return formatWithPrettier(result.join(""));
34
31
  }
35
- function parseMarkdownTable(table) {
36
- const lines = table.trim().split(`
37
- `);
38
- return lines.map((line) => {
39
- return line.split("|").filter((cell) => cell.trim() !== "").map((cell) => cell.trim());
40
- });
41
- }
42
- async function formatTableWithPrettier(table) {
32
+ async function formatWithPrettier(text) {
43
33
  const prettier = await import("prettier");
44
34
  try {
45
- const formatted = await prettier.format(table, {
35
+ const formatted = await prettier.format(text, {
46
36
  parser: "markdown",
47
37
  printWidth: 80
48
38
  });
49
39
  return formatted.trim();
50
- } catch {
51
- return formatTableManually(table);
52
- }
53
- }
54
- function formatTableManually(table) {
55
- const rows = parseMarkdownTable(table);
56
- if (rows.length === 0) {
57
- return table;
58
- }
59
- const columnCount = Math.max(...rows.map((row) => row.length));
60
- const columnWidths = Array(columnCount).fill(0);
61
- for (const row of rows) {
62
- for (let i = 0;i < row.length; i++) {
63
- columnWidths[i] = Math.max(columnWidths[i], row[i].length);
64
- }
65
- }
66
- const formattedRows = rows.map((row, rowIndex) => {
67
- const paddedCells = row.map((cell, colIndex) => {
68
- const width = columnWidths[colIndex];
69
- return padCell(cell, width);
70
- });
71
- return "| " + paddedCells.join(" | ") + " |";
72
- });
73
- if (formattedRows.length > 1) {
74
- const separatorCells = columnWidths.map((width) => {
75
- return "-".repeat(width);
76
- });
77
- const separator = "| " + separatorCells.join(" | ") + " |";
78
- formattedRows.splice(1, 0, separator);
79
- }
80
- return formattedRows.join(`
81
- `);
82
- }
83
- function padCell(value, width) {
84
- if (value.length >= width) {
85
- return value;
40
+ } catch (error) {
41
+ console.error("[markdown-table-transformer] Prettier formatting failed:", error);
42
+ return text;
86
43
  }
87
- return value + " ".repeat(width - value.length);
88
- }
89
- function transformMarkdownTablesSync(text) {
90
- return text.replace(TABLE_BLOCK_REGEX, (tableMatch) => {
91
- return CODE_BLOCK_WRAPPER.replace("{TABLE}", tableMatch.trim());
92
- });
93
44
  }
94
45
 
95
46
  // src/reply-buffer.ts
@@ -569,13 +520,16 @@ async function handleCommand(input, agentService, promptQueue) {
569
520
  }
570
521
 
571
522
  // src/message-chunker.ts
523
+ import { Lexer as Lexer2 } from "marked";
572
524
  var DISCORD_MESSAGE_LIMIT = 2000;
573
525
  var SAFE_MESSAGE_LIMIT = 1900;
574
526
  function chunkMessage(text) {
575
527
  if (text.length <= SAFE_MESSAGE_LIMIT) {
576
528
  return [text];
577
529
  }
530
+ const codeBlockRanges = getCodeBlockRanges(text);
578
531
  const chunks = [];
532
+ let offset = 0;
579
533
  let remaining = text;
580
534
  while (remaining.length > SAFE_MESSAGE_LIMIT) {
581
535
  const candidate = remaining.slice(0, SAFE_MESSAGE_LIMIT);
@@ -583,15 +537,41 @@ function chunkMessage(text) {
583
537
 
584
538
  `), candidate.lastIndexOf(`
585
539
  `), candidate.lastIndexOf(" "));
586
- const boundary = splitIndex > 0 ? splitIndex : SAFE_MESSAGE_LIMIT;
540
+ const relBoundary = splitIndex > 0 ? splitIndex : SAFE_MESSAGE_LIMIT;
541
+ const absBoundary = adjustBoundaryForCodeBlock(offset + relBoundary, codeBlockRanges);
542
+ const boundary = absBoundary - offset;
587
543
  chunks.push(remaining.slice(0, boundary).trim());
588
544
  remaining = remaining.slice(boundary).trim();
545
+ offset = absBoundary;
589
546
  }
590
547
  if (remaining.length > 0) {
591
548
  chunks.push(remaining);
592
549
  }
593
550
  return chunks.filter((chunk) => chunk.length > 0).map((chunk) => chunk.slice(0, DISCORD_MESSAGE_LIMIT));
594
551
  }
552
+ function getCodeBlockRanges(text) {
553
+ const tokens = Lexer2.lex(text);
554
+ const ranges = [];
555
+ let pos = 0;
556
+ for (const token of tokens) {
557
+ if (token.type === "code") {
558
+ ranges.push({ start: pos, end: pos + token.raw.length });
559
+ }
560
+ pos += token.raw.length;
561
+ }
562
+ return ranges;
563
+ }
564
+ function adjustBoundaryForCodeBlock(absBoundary, codeBlockRanges) {
565
+ for (const range of codeBlockRanges) {
566
+ if (absBoundary > range.start && absBoundary < range.end) {
567
+ if (range.end <= DISCORD_MESSAGE_LIMIT) {
568
+ return range.end;
569
+ }
570
+ return absBoundary;
571
+ }
572
+ }
573
+ return absBoundary;
574
+ }
595
575
 
596
576
  // src/discord-client.ts
597
577
  async function startDiscordClient(config, agentService, promptQueue) {
@@ -840,7 +820,6 @@ function registerSignalHandlers(stop) {
840
820
  }
841
821
  export {
842
822
  transformMarkdownTablesToCodeBlocks,
843
- transformMarkdownTablesSync,
844
823
  startDiscordPiBridge,
845
824
  resolveConfig,
846
825
  loadDiscordPiBridgeConfigFromEnv,
@@ -1,18 +1,13 @@
1
1
  /**
2
2
  * Transforms markdown tables into Discord-friendly code blocks.
3
3
  * Discord doesn't support markdown tables natively, so we:
4
- * 1. Detect markdown tables in the text
5
- * 2. Wrap them in triple backticks
6
- * 3. Use Prettier to format the table with proper column alignment
4
+ * 1. Format the entire document with Prettier (tables get formatted)
5
+ * 2. Use marked Lexer to identify tables
6
+ * 3. Wrap tables in triple backticks
7
+ * 4. Run Prettier once more on the entire result (tables stay formatted in code blocks)
7
8
  */
8
9
  /**
9
10
  * Transforms markdown tables in the text to Discord-friendly code blocks.
10
- * Tables are detected by their markdown syntax and wrapped in triple backticks.
11
- * Table contents are formatted with Prettier for proper column alignment.
11
+ * Uses marked's Lexer to properly identify tables.
12
12
  */
13
13
  export declare function transformMarkdownTablesToCodeBlocks(text: string): Promise<string>;
14
- /**
15
- * Synchronous version that wraps tables in code blocks but skips Prettier formatting.
16
- * Useful when you want quick transformation without async overhead.
17
- */
18
- export declare function transformMarkdownTablesSync(text: string): string;
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friendlyrobot/discord-pi-agent",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "Reusable Discord gateway bridge for persistent pi agent sessions",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "scripts": {
28
28
  "test:watch": "vitest",
29
29
  "test": "vitest run",
30
- "update-deps": "bun add @mariozechner/pi-ai@latest @mariozechner/pi-coding-agent@latest discord.js@latest dotenv@latest prettier@latest; bun add -d @types/node@latest typescript@latest vitest@latest @vitest/ui@latest",
30
+ "update-deps": "bun add @mariozechner/pi-ai@latest @mariozechner/pi-coding-agent@latest marked@latest discord.js@latest dotenv@latest prettier@latest; bun add -d @types/node@latest typescript@latest vitest@latest @vitest/ui@latest",
31
31
  "format": "prettier --write .",
32
32
  "build": "rm -rf dist && bun build ./src/index.ts --outdir ./dist --target node --format esm --packages external && tsc -p tsconfig.json --emitDeclarationOnly --declaration --declarationMap false",
33
33
  "typecheck": "tsc --noEmit -p tsconfig.json"
@@ -37,6 +37,7 @@
37
37
  "@mariozechner/pi-coding-agent": "^0.70.0",
38
38
  "discord.js": "^14.26.3",
39
39
  "dotenv": "^17.4.2",
40
+ "marked": "^18.0.2",
40
41
  "prettier": "^3.8.3"
41
42
  },
42
43
  "devDependencies": {