@friendlyrobot/discord-pi-agent 0.3.17 → 0.4.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 CHANGED
@@ -17,6 +17,7 @@ Reusable Discord DM bridge for persistent pi agent sessions.
17
17
  - `!status`
18
18
  - `!thinking`
19
19
  - `!compact`
20
+ - `!reload`
20
21
  - `!reset-session`
21
22
 
22
23
  Any other DM text is sent to the persistent agent session.
@@ -9,6 +9,7 @@ export declare class AgentService {
9
9
  constructor(config: ResolvedDiscordPiBridgeConfig);
10
10
  initialize(): Promise<void>;
11
11
  prompt(text: string): Promise<string>;
12
+ reloadResources(): Promise<string>;
12
13
  compact(): Promise<string>;
13
14
  resetSession(): Promise<string>;
14
15
  getStatus(): AgentStatus;
package/dist/index.d.ts CHANGED
@@ -1,5 +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
4
  export { loadDiscordPiBridgeConfigFromEnv, resolveConfig } from "./config";
4
5
  export type { AgentStatus, DiscordPiBridge, DiscordPiBridgeConfig, PromptTransform, ResolvedDiscordPiBridgeConfig, } from "./types";
5
6
  export declare function startDiscordPiBridge(config: DiscordPiBridgeConfig): Promise<DiscordPiBridge>;
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
1
4
  // src/agent-service.ts
2
5
  import fs from "node:fs/promises";
3
6
  import path from "node:path";
@@ -10,6 +13,85 @@ import {
10
13
  SettingsManager
11
14
  } from "@mariozechner/pi-coding-agent";
12
15
 
16
+ // src/markdown-table-transformer.ts
17
+ var TABLE_BLOCK_REGEX = /(\|.+?(?:\|.*)+)(\n\|[-:]+(?:\|[-:]+)+\|?)(\n\|.+?(?:\|.*)+\|?)+/g;
18
+ var CODE_BLOCK_WRAPPER = "```\n{TABLE}\n```";
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);
32
+ }
33
+ return result;
34
+ }
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) {
43
+ const prettier = await import("prettier");
44
+ try {
45
+ const formatted = await prettier.format(table, {
46
+ parser: "markdown",
47
+ printWidth: 80
48
+ });
49
+ 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;
86
+ }
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
+ }
94
+
13
95
  // src/reply-buffer.ts
14
96
  async function collectReply(session, prompt, options = {}) {
15
97
  const logPrefix = options.logPrefix ?? "[agent]";
@@ -67,7 +149,8 @@ async function collectReply(session, prompt, options = {}) {
67
149
  return errorMessage;
68
150
  }
69
151
  if (finalText) {
70
- return finalText;
152
+ const transformed = await transformMarkdownTablesToCodeBlocks(finalText);
153
+ return transformed;
71
154
  }
72
155
  return "No response generated.";
73
156
  }
@@ -147,6 +230,17 @@ class AgentService {
147
230
  logPrefix: `[agent:${session.sessionId}]`
148
231
  });
149
232
  }
233
+ async reloadResources() {
234
+ await this.resourceLoader.reload();
235
+ const extensions = this.resourceLoader.getExtensions().extensions.map((ext) => ext.path);
236
+ const agentsFiles = this.resourceLoader.getAgentsFiles().agentsFiles.map((f) => f.path);
237
+ return [
238
+ "Resources reloaded.",
239
+ `Extensions (${extensions.length}): ${extensions.join(", ") || "(none)"}`,
240
+ `AGENTS.md files (${agentsFiles.length}): ${agentsFiles.join(", ") || "(none)"}`
241
+ ].join(`
242
+ `);
243
+ }
150
244
  async compact() {
151
245
  const session = this.requireSession();
152
246
  await session.compact();
@@ -391,6 +485,7 @@ async function handleCommand(input, agentService, promptQueue) {
391
485
  "!thinking - show or set thinking/reasoning level",
392
486
  "!compact - compact the persistent session",
393
487
  "!reset-session - start a fresh persistent session",
488
+ "!reload - reload resources (AGENTS.md, extensions, skills, etc.)",
394
489
  "Any other DM text goes to the persistent agent session."
395
490
  ].join(`
396
491
  `)
@@ -451,6 +546,14 @@ async function handleCommand(input, agentService, promptQueue) {
451
546
  })
452
547
  };
453
548
  }
549
+ if (trimmed === "!reload") {
550
+ return {
551
+ handled: true,
552
+ response: await promptQueue.enqueue(async () => {
553
+ return agentService.reloadResources();
554
+ })
555
+ };
556
+ }
454
557
  if (trimmed === "!reset-session") {
455
558
  return {
456
559
  handled: true,
@@ -736,6 +839,8 @@ function registerSignalHandlers(stop) {
736
839
  });
737
840
  }
738
841
  export {
842
+ transformMarkdownTablesToCodeBlocks,
843
+ transformMarkdownTablesSync,
739
844
  startDiscordPiBridge,
740
845
  resolveConfig,
741
846
  loadDiscordPiBridgeConfigFromEnv,
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Transforms markdown tables into Discord-friendly code blocks.
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
7
+ */
8
+ /**
9
+ * 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.
12
+ */
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.3.17",
3
+ "version": "0.4.0",
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; bun add -d @types/node@latest prettier@latest typescript@latest vitest@latest @vitest/ui@latest",
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",
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"
@@ -36,12 +36,12 @@
36
36
  "@mariozechner/pi-ai": "^0.70.0",
37
37
  "@mariozechner/pi-coding-agent": "^0.70.0",
38
38
  "discord.js": "^14.26.3",
39
- "dotenv": "^17.4.2"
39
+ "dotenv": "^17.4.2",
40
+ "prettier": "^3.8.3"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@types/node": "^25.6.0",
43
44
  "@vitest/ui": "^4.1.5",
44
- "prettier": "^3.8.3",
45
45
  "typescript": "^6.0.3",
46
46
  "vitest": "^4.1.5"
47
47
  }