eniac-slack 0.1.0 → 0.1.2
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/cli.js +5 -0
- package/dist/cli.js.map +1 -1
- package/dist/handlers/thread.d.ts.map +1 -1
- package/dist/handlers/thread.js +6 -7
- package/dist/handlers/thread.js.map +1 -1
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +49 -0
- package/dist/services/claude.js.map +1 -1
- package/dist/services/slack-messenger.d.ts.map +1 -1
- package/dist/services/slack-messenger.js +7 -3
- package/dist/services/slack-messenger.js.map +1 -1
- package/dist/utils/slack-format.d.ts +18 -0
- package/dist/utils/slack-format.d.ts.map +1 -0
- package/dist/utils/slack-format.js +135 -0
- package/dist/utils/slack-format.js.map +1 -0
- package/package.json +1 -1
- package/src/cli.ts +8 -0
- package/src/handlers/thread.ts +7 -8
- package/src/services/claude.ts +50 -0
- package/src/services/slack-messenger.ts +7 -6
- package/src/utils/slack-format.ts +161 -0
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import os from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
4
6
|
import dotenv from "dotenv";
|
|
5
7
|
import { createAndStartApp } from "./app.js";
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"));
|
|
6
10
|
// Load .env — try cwd first, then walk up to find it
|
|
7
11
|
dotenv.config();
|
|
8
12
|
dotenv.config({ path: path.resolve(process.cwd(), "../../.env") });
|
|
@@ -20,6 +24,7 @@ async function main() {
|
|
|
20
24
|
const slackAppToken = requireEnv("SLACK_APP_TOKEN");
|
|
21
25
|
const reposBaseDir = process.env["REPOS_BASE_DIR"]?.replace(/^~/, os.homedir()) ??
|
|
22
26
|
path.join(os.homedir(), ".eniac", "repos");
|
|
27
|
+
console.log(`[eniac-slack] v${pkg.version}`);
|
|
23
28
|
console.log("[eniac-slack] Starting Slack bot...");
|
|
24
29
|
console.log(`[eniac-slack] Repos base directory: ${reposBaseDir}`);
|
|
25
30
|
try {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,qDAAqD;AACrD,MAAM,CAAC,MAAM,EAAE,CAAC;AAChB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;AAEnE,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,wDAAwD,IAAI,EAAE,CAAC,CAAC;QAC9E,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,iBAAiB,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,aAAa,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAEpD,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,uCAAuC,YAAY,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC;QACH,MAAM,iBAAiB,CAAC;YACtB,aAAa;YACb,aAAa;YACb,YAAY;SACb,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,OAAO,CAAC,CACrE,CAAC;AAEF,qDAAqD;AACrD,MAAM,CAAC,MAAM,EAAE,CAAC;AAChB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,EAAE,CAAC,CAAC;AAEnE,SAAS,UAAU,CAAC,IAAY;IAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,wDAAwD,IAAI,EAAE,CAAC,CAAC;QAC9E,OAAO,CAAC,KAAK,CAAC,gCAAgC,IAAI,iBAAiB,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,aAAa,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAEpD,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1D,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE7C,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,uCAAuC,YAAY,EAAE,CAAC,CAAC;IAEnE,IAAI,CAAC;QACH,MAAM,iBAAiB,CAAC;YACtB,aAAa;YACb,aAAa;YACb,YAAY;SACb,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;IACvE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAI/E;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,CAAC,SAAS,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAI/E;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,CAAC,SAAS,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC,CAgDf"}
|
package/dist/handlers/thread.js
CHANGED
|
@@ -7,18 +7,17 @@ import { postStreamingReply } from "../services/slack-messenger.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export async function handleThreadMessage(args) {
|
|
9
9
|
const { event, client, context } = args;
|
|
10
|
-
|
|
11
|
-
const threadTs = "thread_ts" in event ? event.thread_ts : undefined;
|
|
12
|
-
const botId = "bot_id" in event ? event.bot_id : undefined;
|
|
13
|
-
const userId = "user" in event ? event.user : undefined;
|
|
14
|
-
console.log(`[thread] message event: subtype=${subtype}, thread_ts=${threadTs ?? "none"}, bot_id=${botId ?? "none"}, user=${userId ?? "none"}`);
|
|
15
|
-
// Skip non-user subtypes (message_changed, bot_message, etc.)
|
|
10
|
+
// Silently skip non-user subtypes (message_changed, bot_message, etc.)
|
|
16
11
|
// But allow file_share (image/file attachments) — those are real user messages
|
|
17
12
|
const allowedSubtypes = new Set(["file_share"]);
|
|
18
13
|
if (event.subtype !== undefined && !allowedSubtypes.has(event.subtype)) {
|
|
19
|
-
console.log(`[thread] skipped: subtype=${subtype}`);
|
|
20
14
|
return;
|
|
21
15
|
}
|
|
16
|
+
const subtype = event.subtype ?? "none";
|
|
17
|
+
const threadTs = "thread_ts" in event ? event.thread_ts : undefined;
|
|
18
|
+
const botId = "bot_id" in event ? event.bot_id : undefined;
|
|
19
|
+
const userId = "user" in event ? event.user : undefined;
|
|
20
|
+
console.log(`[thread] message event: subtype=${subtype}, thread_ts=${threadTs ?? "none"}, bot_id=${botId ?? "none"}, user=${userId ?? "none"}`);
|
|
22
21
|
// Must be in a thread
|
|
23
22
|
if (!threadTs) {
|
|
24
23
|
console.log(`[thread] skipped: not in a thread`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAA6D;IAE7D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAExC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC;IACxC,MAAM,QAAQ,GAAG,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,KAAK,GAAG,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,mCAAmC,OAAO,eAAe,QAAQ,IAAI,MAAM,YAAY,KAAK,IAAI,MAAM,UAAU,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;IAEhJ,
|
|
1
|
+
{"version":3,"file":"thread.js","sourceRoot":"","sources":["../../src/handlers/thread.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,IAA6D;IAE7D,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAExC,uEAAuE;IACvE,+EAA+E;IAC/E,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACvE,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC;IACxC,MAAM,QAAQ,GAAG,WAAW,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,MAAM,KAAK,GAAG,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,OAAO,CAAC,GAAG,CAAC,mCAAmC,OAAO,eAAe,QAAQ,IAAI,MAAM,YAAY,KAAK,IAAI,MAAM,UAAU,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;IAEhJ,sBAAsB;IACtB,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,OAAO;IACT,CAAC;IAED,sCAAsC;IACtC,IAAI,KAAK,IAAI,MAAM,KAAK,OAAO,CAAC,SAAS,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IACtD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAE9B,4DAA4D;IAC5D,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,8CAA8C,QAAQ,EAAE,CAAC,CAAC;QACtE,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,qCAAqC,OAAO,CAAC,SAAS,UAAU,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAElG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE9C,+EAA+E;IAC/E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,SAAS;QAAE,OAAO;IAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1D,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,UAAU,OAAO;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,UAAU,OAAO;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAoJvC,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAaT;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAEhE;AAkBD;;;;;GAKG;AACH,wBAAuB,IAAI,CACzB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,SAAS,EACtB,OAAO,EAAE,MAAM,GACd,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAmI1C"}
|
package/dist/services/claude.js
CHANGED
|
@@ -44,6 +44,50 @@ function loadMcpServers() {
|
|
|
44
44
|
return undefined;
|
|
45
45
|
}
|
|
46
46
|
const mcpServers = loadMcpServers();
|
|
47
|
+
// --- Slack formatting system prompt ---
|
|
48
|
+
const SLACK_FORMAT_PROMPT = `
|
|
49
|
+
# Slack Formatting Rules
|
|
50
|
+
|
|
51
|
+
You are responding inside a Slack conversation. Follow these formatting rules strictly:
|
|
52
|
+
|
|
53
|
+
## Text Formatting
|
|
54
|
+
- Use *bold* (single asterisk) instead of **bold** (double asterisk)
|
|
55
|
+
- Use _italic_ (underscore) for emphasis
|
|
56
|
+
- Use ~strikethrough~ for deleted text
|
|
57
|
+
- Use \`inline code\` with backticks (same as Markdown)
|
|
58
|
+
- Use \`\`\`code blocks\`\`\` with triple backticks (same as Markdown)
|
|
59
|
+
- Use > for blockquotes (same as Markdown)
|
|
60
|
+
|
|
61
|
+
## Structure
|
|
62
|
+
- Use bullet lists with • or - for unordered items
|
|
63
|
+
- Use numbered lists with 1. 2. 3. for ordered items
|
|
64
|
+
- Do NOT use # headers. Instead, use *bold text* on its own line as a section title
|
|
65
|
+
- Use ─────── (horizontal line with box-drawing characters) instead of --- for dividers
|
|
66
|
+
|
|
67
|
+
## Tables
|
|
68
|
+
- Slack does not support Markdown tables
|
|
69
|
+
- When you need to display tabular data, use a code block with box-drawing characters:
|
|
70
|
+
\`\`\`
|
|
71
|
+
┌──────┬───────┬──────┐
|
|
72
|
+
│ Name │ Value │ Note │
|
|
73
|
+
├──────┼───────┼──────┤
|
|
74
|
+
│ foo │ 123 │ ok │
|
|
75
|
+
│ bar │ 456 │ ok │
|
|
76
|
+
└──────┴───────┴──────┘
|
|
77
|
+
\`\`\`
|
|
78
|
+
|
|
79
|
+
## Links
|
|
80
|
+
- Use <URL|display text> format instead of [display text](URL)
|
|
81
|
+
|
|
82
|
+
## Emoji
|
|
83
|
+
- Use emoji actively to make messages more readable and engaging :rocket: :white_check_mark: :warning: :bulb: :memo:
|
|
84
|
+
- Use emoji as visual indicators for status, categories, or emphasis
|
|
85
|
+
|
|
86
|
+
## General
|
|
87
|
+
- Keep responses concise and scannable
|
|
88
|
+
- Use line breaks generously for readability
|
|
89
|
+
- Never use HTML tags
|
|
90
|
+
`.trim();
|
|
47
91
|
const SESSION_TTL_MS = 14 * 24 * 60 * 60 * 1000; // 2 weeks
|
|
48
92
|
function cleanupExpiredSessions() {
|
|
49
93
|
const now = Date.now();
|
|
@@ -172,6 +216,11 @@ export async function* chat(threadTs, userMessage, slackClient, channel) {
|
|
|
172
216
|
permissionMode: "bypassPermissions",
|
|
173
217
|
allowDangerouslySkipPermissions: true,
|
|
174
218
|
includePartialMessages: true,
|
|
219
|
+
systemPrompt: {
|
|
220
|
+
type: "preset",
|
|
221
|
+
preset: "claude_code",
|
|
222
|
+
append: SLACK_FORMAT_PROMPT,
|
|
223
|
+
},
|
|
175
224
|
...(mcpServers && { mcpServers }),
|
|
176
225
|
...(session.hasStarted
|
|
177
226
|
? { resume: session.sessionId }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/services/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-messenger.d.ts","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"slack-messenger.d.ts","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,cAAc,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,GACpD,OAAO,CAAC,MAAM,CAAC,CAoFjB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markdownToSlackMrkdwn } from "../utils/slack-format.js";
|
|
1
2
|
const THROTTLE_MS = 500;
|
|
2
3
|
/**
|
|
3
4
|
* Post a streaming reply in a Slack thread, handling both text and tool events.
|
|
@@ -40,7 +41,7 @@ export async function postStreamingReply(client, channel, threadTs, eventStream)
|
|
|
40
41
|
pendingUpdate = false;
|
|
41
42
|
lastUpdateTime = Date.now();
|
|
42
43
|
// For intermediate updates, trim to word boundary to prevent garbled display
|
|
43
|
-
const displayText = isFinal ? text : safeSlice(text);
|
|
44
|
+
const displayText = isFinal ? text : safeSlice(text) + "\n(생각 중)";
|
|
44
45
|
try {
|
|
45
46
|
await client.chat.update({
|
|
46
47
|
channel,
|
|
@@ -64,9 +65,12 @@ export async function postStreamingReply(client, channel, threadTs, eventStream)
|
|
|
64
65
|
break;
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
|
-
// Final flush
|
|
68
|
+
// Final flush — apply Slack mrkdwn conversion on the complete message
|
|
68
69
|
if (pendingUpdate || accumulated) {
|
|
69
|
-
|
|
70
|
+
const finalText = accumulated
|
|
71
|
+
? markdownToSlackMrkdwn(accumulated)
|
|
72
|
+
: ":warning: No response generated.";
|
|
73
|
+
await doUpdate(finalText, true);
|
|
70
74
|
}
|
|
71
75
|
return messageTs;
|
|
72
76
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack-messenger.js","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack-messenger.js","sourceRoot":"","sources":["../../src/services/slack-messenger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert standard Markdown to Slack mrkdwn format.
|
|
3
|
+
*
|
|
4
|
+
* Handles the differences between GitHub-flavored Markdown and Slack's mrkdwn:
|
|
5
|
+
* - Headers (# → *bold*)
|
|
6
|
+
* - Bold (**text** → *text*)
|
|
7
|
+
* - Italic (_text_ stays, *text* needs care since Slack uses * for bold)
|
|
8
|
+
* - Links [text](url) → <url|text>
|
|
9
|
+
* - Images  → <url|alt>
|
|
10
|
+
* - Horizontal rules (--- → ───────)
|
|
11
|
+
* - Tables → special character aligned format
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Convert a complete Markdown string to Slack mrkdwn.
|
|
15
|
+
* Applied as post-processing on the final message before sending to Slack.
|
|
16
|
+
*/
|
|
17
|
+
export declare function markdownToSlackMrkdwn(text: string): string;
|
|
18
|
+
//# sourceMappingURL=slack-format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-format.d.ts","sourceRoot":"","sources":["../../src/utils/slack-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAuD1D"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert standard Markdown to Slack mrkdwn format.
|
|
3
|
+
*
|
|
4
|
+
* Handles the differences between GitHub-flavored Markdown and Slack's mrkdwn:
|
|
5
|
+
* - Headers (# → *bold*)
|
|
6
|
+
* - Bold (**text** → *text*)
|
|
7
|
+
* - Italic (_text_ stays, *text* needs care since Slack uses * for bold)
|
|
8
|
+
* - Links [text](url) → <url|text>
|
|
9
|
+
* - Images  → <url|alt>
|
|
10
|
+
* - Horizontal rules (--- → ───────)
|
|
11
|
+
* - Tables → special character aligned format
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Convert a complete Markdown string to Slack mrkdwn.
|
|
15
|
+
* Applied as post-processing on the final message before sending to Slack.
|
|
16
|
+
*/
|
|
17
|
+
export function markdownToSlackMrkdwn(text) {
|
|
18
|
+
let result = text;
|
|
19
|
+
// Convert images  → <url|alt> (must come before links)
|
|
20
|
+
result = result.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
21
|
+
// Convert links [text](url) → <url|text>
|
|
22
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
23
|
+
// Process line-by-line for block-level elements
|
|
24
|
+
const lines = result.split("\n");
|
|
25
|
+
const output = [];
|
|
26
|
+
let i = 0;
|
|
27
|
+
while (i < lines.length) {
|
|
28
|
+
const line = lines[i];
|
|
29
|
+
// Detect markdown table (line with pipes)
|
|
30
|
+
if (isTableRow(line) && i + 1 < lines.length && isTableSeparator(lines[i + 1])) {
|
|
31
|
+
const tableLines = [line];
|
|
32
|
+
i++; // skip separator
|
|
33
|
+
i++; // move to first data row
|
|
34
|
+
while (i < lines.length && isTableRow(lines[i])) {
|
|
35
|
+
tableLines.push(lines[i]);
|
|
36
|
+
i++;
|
|
37
|
+
}
|
|
38
|
+
output.push(convertTable(tableLines));
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
// Headers: # text → *text*
|
|
42
|
+
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
43
|
+
if (headerMatch) {
|
|
44
|
+
const content = headerMatch[2].replace(/\*\*/g, "").replace(/\*/g, "");
|
|
45
|
+
output.push(`*${content}*`);
|
|
46
|
+
i++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
// Horizontal rules
|
|
50
|
+
if (/^(-{3,}|_{3,}|\*{3,})$/.test(line.trim())) {
|
|
51
|
+
output.push("───────────────────────────");
|
|
52
|
+
i++;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
// Bold: **text** → *text* (Slack uses single * for bold)
|
|
56
|
+
// But preserve code spans first
|
|
57
|
+
const processed = convertInlineFormatting(line);
|
|
58
|
+
output.push(processed);
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
return output.join("\n");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Convert inline formatting: **bold** → *bold*, __bold__ → *bold*
|
|
65
|
+
* Preserves code spans (`code`) from conversion.
|
|
66
|
+
*/
|
|
67
|
+
function convertInlineFormatting(line) {
|
|
68
|
+
// Split by code spans to avoid converting inside them
|
|
69
|
+
const parts = line.split(/(`[^`]+`)/);
|
|
70
|
+
return parts
|
|
71
|
+
.map((part, idx) => {
|
|
72
|
+
// Odd indices are code spans — leave them alone
|
|
73
|
+
if (idx % 2 === 1)
|
|
74
|
+
return part;
|
|
75
|
+
// Convert **bold** or __bold__ to *bold*
|
|
76
|
+
let converted = part.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
77
|
+
converted = converted.replace(/__(.+?)__/g, "*$1*");
|
|
78
|
+
return converted;
|
|
79
|
+
})
|
|
80
|
+
.join("");
|
|
81
|
+
}
|
|
82
|
+
function isTableRow(line) {
|
|
83
|
+
return line.includes("|") && line.trim().startsWith("|");
|
|
84
|
+
}
|
|
85
|
+
function isTableSeparator(line) {
|
|
86
|
+
return /^\|[\s:-]+\|/.test(line.trim());
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Convert a Markdown table to a Slack-friendly format using special characters.
|
|
90
|
+
* Input: array of table rows (header + data, separator already removed)
|
|
91
|
+
*/
|
|
92
|
+
function convertTable(rows) {
|
|
93
|
+
// Parse cells
|
|
94
|
+
const parsed = rows.map((row) => row
|
|
95
|
+
.split("|")
|
|
96
|
+
.slice(1, -1) // remove empty first/last from leading/trailing |
|
|
97
|
+
.map((cell) => cell.trim()));
|
|
98
|
+
if (parsed.length === 0)
|
|
99
|
+
return rows.join("\n");
|
|
100
|
+
const header = parsed[0];
|
|
101
|
+
// Calculate column widths
|
|
102
|
+
const colCount = header.length;
|
|
103
|
+
const colWidths = Array(colCount).fill(0);
|
|
104
|
+
for (const row of parsed) {
|
|
105
|
+
for (let c = 0; c < colCount; c++) {
|
|
106
|
+
const cellLen = (row[c] ?? "").length;
|
|
107
|
+
if (cellLen > colWidths[c])
|
|
108
|
+
colWidths[c] = cellLen;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const padCell = (text, width) => text.padEnd(width);
|
|
112
|
+
const makeLine = (left, mid, right, fill) => left + colWidths.map((w) => fill.repeat(w + 2)).join(mid) + right;
|
|
113
|
+
const topBorder = makeLine("┌", "┬", "┐", "─");
|
|
114
|
+
const midBorder = makeLine("├", "┼", "┤", "─");
|
|
115
|
+
const bottomBorder = makeLine("└", "┴", "┘", "─");
|
|
116
|
+
const formatRow = (cells) => "│" +
|
|
117
|
+
cells
|
|
118
|
+
.map((cell, c) => ` ${padCell(cell, colWidths[c])} `)
|
|
119
|
+
.join("│") +
|
|
120
|
+
"│";
|
|
121
|
+
const lines = [];
|
|
122
|
+
lines.push("```");
|
|
123
|
+
lines.push(topBorder);
|
|
124
|
+
lines.push(formatRow(header));
|
|
125
|
+
if (parsed.length > 1) {
|
|
126
|
+
lines.push(midBorder);
|
|
127
|
+
for (let r = 1; r < parsed.length; r++) {
|
|
128
|
+
lines.push(formatRow(parsed[r]));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
lines.push(bottomBorder);
|
|
132
|
+
lines.push("```");
|
|
133
|
+
return lines.join("\n");
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=slack-format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack-format.js","sourceRoot":"","sources":["../../src/utils/slack-format.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import os from "node:os";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
5
7
|
import dotenv from "dotenv";
|
|
6
8
|
import { createAndStartApp } from "./app.js";
|
|
7
9
|
|
|
10
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const pkg = JSON.parse(
|
|
12
|
+
fs.readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"),
|
|
13
|
+
);
|
|
14
|
+
|
|
8
15
|
// Load .env — try cwd first, then walk up to find it
|
|
9
16
|
dotenv.config();
|
|
10
17
|
dotenv.config({ path: path.resolve(process.cwd(), "../../.env") });
|
|
@@ -27,6 +34,7 @@ async function main(): Promise<void> {
|
|
|
27
34
|
process.env["REPOS_BASE_DIR"]?.replace(/^~/, os.homedir()) ??
|
|
28
35
|
path.join(os.homedir(), ".eniac", "repos");
|
|
29
36
|
|
|
37
|
+
console.log(`[eniac-slack] v${pkg.version}`);
|
|
30
38
|
console.log("[eniac-slack] Starting Slack bot...");
|
|
31
39
|
console.log(`[eniac-slack] Repos base directory: ${reposBaseDir}`);
|
|
32
40
|
|
package/src/handlers/thread.ts
CHANGED
|
@@ -12,20 +12,19 @@ export async function handleThreadMessage(
|
|
|
12
12
|
): Promise<void> {
|
|
13
13
|
const { event, client, context } = args;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const threadTs = "thread_ts" in event ? event.thread_ts : undefined;
|
|
17
|
-
const botId = "bot_id" in event ? event.bot_id : undefined;
|
|
18
|
-
const userId = "user" in event ? event.user : undefined;
|
|
19
|
-
console.log(`[thread] message event: subtype=${subtype}, thread_ts=${threadTs ?? "none"}, bot_id=${botId ?? "none"}, user=${userId ?? "none"}`);
|
|
20
|
-
|
|
21
|
-
// Skip non-user subtypes (message_changed, bot_message, etc.)
|
|
15
|
+
// Silently skip non-user subtypes (message_changed, bot_message, etc.)
|
|
22
16
|
// But allow file_share (image/file attachments) — those are real user messages
|
|
23
17
|
const allowedSubtypes = new Set(["file_share"]);
|
|
24
18
|
if (event.subtype !== undefined && !allowedSubtypes.has(event.subtype)) {
|
|
25
|
-
console.log(`[thread] skipped: subtype=${subtype}`);
|
|
26
19
|
return;
|
|
27
20
|
}
|
|
28
21
|
|
|
22
|
+
const subtype = event.subtype ?? "none";
|
|
23
|
+
const threadTs = "thread_ts" in event ? event.thread_ts : undefined;
|
|
24
|
+
const botId = "bot_id" in event ? event.bot_id : undefined;
|
|
25
|
+
const userId = "user" in event ? event.user : undefined;
|
|
26
|
+
console.log(`[thread] message event: subtype=${subtype}, thread_ts=${threadTs ?? "none"}, bot_id=${botId ?? "none"}, user=${userId ?? "none"}`);
|
|
27
|
+
|
|
29
28
|
// Must be in a thread
|
|
30
29
|
if (!threadTs) {
|
|
31
30
|
console.log(`[thread] skipped: not in a thread`);
|
package/src/services/claude.ts
CHANGED
|
@@ -75,6 +75,51 @@ function loadMcpServers(): Record<string, McpServerConfig> | undefined {
|
|
|
75
75
|
|
|
76
76
|
const mcpServers = loadMcpServers();
|
|
77
77
|
|
|
78
|
+
// --- Slack formatting system prompt ---
|
|
79
|
+
const SLACK_FORMAT_PROMPT = `
|
|
80
|
+
# Slack Formatting Rules
|
|
81
|
+
|
|
82
|
+
You are responding inside a Slack conversation. Follow these formatting rules strictly:
|
|
83
|
+
|
|
84
|
+
## Text Formatting
|
|
85
|
+
- Use *bold* (single asterisk) instead of **bold** (double asterisk)
|
|
86
|
+
- Use _italic_ (underscore) for emphasis
|
|
87
|
+
- Use ~strikethrough~ for deleted text
|
|
88
|
+
- Use \`inline code\` with backticks (same as Markdown)
|
|
89
|
+
- Use \`\`\`code blocks\`\`\` with triple backticks (same as Markdown)
|
|
90
|
+
- Use > for blockquotes (same as Markdown)
|
|
91
|
+
|
|
92
|
+
## Structure
|
|
93
|
+
- Use bullet lists with • or - for unordered items
|
|
94
|
+
- Use numbered lists with 1. 2. 3. for ordered items
|
|
95
|
+
- Do NOT use # headers. Instead, use *bold text* on its own line as a section title
|
|
96
|
+
- Use ─────── (horizontal line with box-drawing characters) instead of --- for dividers
|
|
97
|
+
|
|
98
|
+
## Tables
|
|
99
|
+
- Slack does not support Markdown tables
|
|
100
|
+
- When you need to display tabular data, use a code block with box-drawing characters:
|
|
101
|
+
\`\`\`
|
|
102
|
+
┌──────┬───────┬──────┐
|
|
103
|
+
│ Name │ Value │ Note │
|
|
104
|
+
├──────┼───────┼──────┤
|
|
105
|
+
│ foo │ 123 │ ok │
|
|
106
|
+
│ bar │ 456 │ ok │
|
|
107
|
+
└──────┴───────┴──────┘
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
## Links
|
|
111
|
+
- Use <URL|display text> format instead of [display text](URL)
|
|
112
|
+
|
|
113
|
+
## Emoji
|
|
114
|
+
- Use emoji actively to make messages more readable and engaging :rocket: :white_check_mark: :warning: :bulb: :memo:
|
|
115
|
+
- Use emoji as visual indicators for status, categories, or emphasis
|
|
116
|
+
|
|
117
|
+
## General
|
|
118
|
+
- Keep responses concise and scannable
|
|
119
|
+
- Use line breaks generously for readability
|
|
120
|
+
- Never use HTML tags
|
|
121
|
+
`.trim();
|
|
122
|
+
|
|
78
123
|
const SESSION_TTL_MS = 14 * 24 * 60 * 60 * 1000; // 2 weeks
|
|
79
124
|
|
|
80
125
|
function cleanupExpiredSessions(): void {
|
|
@@ -256,6 +301,11 @@ export async function* chat(
|
|
|
256
301
|
permissionMode: "bypassPermissions",
|
|
257
302
|
allowDangerouslySkipPermissions: true,
|
|
258
303
|
includePartialMessages: true,
|
|
304
|
+
systemPrompt: {
|
|
305
|
+
type: "preset",
|
|
306
|
+
preset: "claude_code",
|
|
307
|
+
append: SLACK_FORMAT_PROMPT,
|
|
308
|
+
},
|
|
259
309
|
...(mcpServers && { mcpServers }),
|
|
260
310
|
...(session.hasStarted
|
|
261
311
|
? { resume: session.sessionId }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { WebClient } from "@slack/web-api";
|
|
2
2
|
import type { ChatEvent } from "./claude.js";
|
|
3
|
+
import { markdownToSlackMrkdwn } from "../utils/slack-format.js";
|
|
3
4
|
|
|
4
5
|
const THROTTLE_MS = 500;
|
|
5
6
|
|
|
@@ -60,7 +61,7 @@ export async function postStreamingReply(
|
|
|
60
61
|
lastUpdateTime = Date.now();
|
|
61
62
|
|
|
62
63
|
// For intermediate updates, trim to word boundary to prevent garbled display
|
|
63
|
-
const displayText = isFinal ? text : safeSlice(text);
|
|
64
|
+
const displayText = isFinal ? text : safeSlice(text) + "\n(생각 중)";
|
|
64
65
|
|
|
65
66
|
try {
|
|
66
67
|
await client.chat.update({
|
|
@@ -90,12 +91,12 @@ export async function postStreamingReply(
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
// Final flush
|
|
94
|
+
// Final flush — apply Slack mrkdwn conversion on the complete message
|
|
94
95
|
if (pendingUpdate || accumulated) {
|
|
95
|
-
|
|
96
|
-
accumulated
|
|
97
|
-
|
|
98
|
-
);
|
|
96
|
+
const finalText = accumulated
|
|
97
|
+
? markdownToSlackMrkdwn(accumulated)
|
|
98
|
+
: ":warning: No response generated.";
|
|
99
|
+
await doUpdate(finalText, true);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
return messageTs;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert standard Markdown to Slack mrkdwn format.
|
|
3
|
+
*
|
|
4
|
+
* Handles the differences between GitHub-flavored Markdown and Slack's mrkdwn:
|
|
5
|
+
* - Headers (# → *bold*)
|
|
6
|
+
* - Bold (**text** → *text*)
|
|
7
|
+
* - Italic (_text_ stays, *text* needs care since Slack uses * for bold)
|
|
8
|
+
* - Links [text](url) → <url|text>
|
|
9
|
+
* - Images  → <url|alt>
|
|
10
|
+
* - Horizontal rules (--- → ───────)
|
|
11
|
+
* - Tables → special character aligned format
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Convert a complete Markdown string to Slack mrkdwn.
|
|
16
|
+
* Applied as post-processing on the final message before sending to Slack.
|
|
17
|
+
*/
|
|
18
|
+
export function markdownToSlackMrkdwn(text: string): string {
|
|
19
|
+
let result = text;
|
|
20
|
+
|
|
21
|
+
// Convert images  → <url|alt> (must come before links)
|
|
22
|
+
result = result.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
23
|
+
|
|
24
|
+
// Convert links [text](url) → <url|text>
|
|
25
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "<$2|$1>");
|
|
26
|
+
|
|
27
|
+
// Process line-by-line for block-level elements
|
|
28
|
+
const lines = result.split("\n");
|
|
29
|
+
const output: string[] = [];
|
|
30
|
+
let i = 0;
|
|
31
|
+
|
|
32
|
+
while (i < lines.length) {
|
|
33
|
+
const line = lines[i]!;
|
|
34
|
+
|
|
35
|
+
// Detect markdown table (line with pipes)
|
|
36
|
+
if (isTableRow(line) && i + 1 < lines.length && isTableSeparator(lines[i + 1]!)) {
|
|
37
|
+
const tableLines: string[] = [line];
|
|
38
|
+
i++; // skip separator
|
|
39
|
+
i++; // move to first data row
|
|
40
|
+
while (i < lines.length && isTableRow(lines[i]!)) {
|
|
41
|
+
tableLines.push(lines[i]!);
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
output.push(convertTable(tableLines));
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Headers: # text → *text*
|
|
49
|
+
const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
50
|
+
if (headerMatch) {
|
|
51
|
+
const content = headerMatch[2]!.replace(/\*\*/g, "").replace(/\*/g, "");
|
|
52
|
+
output.push(`*${content}*`);
|
|
53
|
+
i++;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Horizontal rules
|
|
58
|
+
if (/^(-{3,}|_{3,}|\*{3,})$/.test(line.trim())) {
|
|
59
|
+
output.push("───────────────────────────");
|
|
60
|
+
i++;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Bold: **text** → *text* (Slack uses single * for bold)
|
|
65
|
+
// But preserve code spans first
|
|
66
|
+
const processed = convertInlineFormatting(line);
|
|
67
|
+
|
|
68
|
+
output.push(processed);
|
|
69
|
+
i++;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return output.join("\n");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Convert inline formatting: **bold** → *bold*, __bold__ → *bold*
|
|
77
|
+
* Preserves code spans (`code`) from conversion.
|
|
78
|
+
*/
|
|
79
|
+
function convertInlineFormatting(line: string): string {
|
|
80
|
+
// Split by code spans to avoid converting inside them
|
|
81
|
+
const parts = line.split(/(`[^`]+`)/);
|
|
82
|
+
|
|
83
|
+
return parts
|
|
84
|
+
.map((part, idx) => {
|
|
85
|
+
// Odd indices are code spans — leave them alone
|
|
86
|
+
if (idx % 2 === 1) return part;
|
|
87
|
+
|
|
88
|
+
// Convert **bold** or __bold__ to *bold*
|
|
89
|
+
let converted = part.replace(/\*\*(.+?)\*\*/g, "*$1*");
|
|
90
|
+
converted = converted.replace(/__(.+?)__/g, "*$1*");
|
|
91
|
+
|
|
92
|
+
return converted;
|
|
93
|
+
})
|
|
94
|
+
.join("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function isTableRow(line: string): boolean {
|
|
98
|
+
return line.includes("|") && line.trim().startsWith("|");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isTableSeparator(line: string): boolean {
|
|
102
|
+
return /^\|[\s:-]+\|/.test(line.trim());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Convert a Markdown table to a Slack-friendly format using special characters.
|
|
107
|
+
* Input: array of table rows (header + data, separator already removed)
|
|
108
|
+
*/
|
|
109
|
+
function convertTable(rows: string[]): string {
|
|
110
|
+
// Parse cells
|
|
111
|
+
const parsed = rows.map((row) =>
|
|
112
|
+
row
|
|
113
|
+
.split("|")
|
|
114
|
+
.slice(1, -1) // remove empty first/last from leading/trailing |
|
|
115
|
+
.map((cell) => cell.trim())
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
if (parsed.length === 0) return rows.join("\n");
|
|
119
|
+
|
|
120
|
+
const header = parsed[0]!;
|
|
121
|
+
|
|
122
|
+
// Calculate column widths
|
|
123
|
+
const colCount = header.length;
|
|
124
|
+
const colWidths: number[] = Array<number>(colCount).fill(0);
|
|
125
|
+
for (const row of parsed) {
|
|
126
|
+
for (let c = 0; c < colCount; c++) {
|
|
127
|
+
const cellLen = (row[c] ?? "").length;
|
|
128
|
+
if (cellLen > colWidths[c]!) colWidths[c] = cellLen;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const padCell = (text: string, width: number) => text.padEnd(width);
|
|
133
|
+
const makeLine = (left: string, mid: string, right: string, fill: string) =>
|
|
134
|
+
left + colWidths.map((w) => fill.repeat(w + 2)).join(mid) + right;
|
|
135
|
+
|
|
136
|
+
const topBorder = makeLine("┌", "┬", "┐", "─");
|
|
137
|
+
const midBorder = makeLine("├", "┼", "┤", "─");
|
|
138
|
+
const bottomBorder = makeLine("└", "┴", "┘", "─");
|
|
139
|
+
|
|
140
|
+
const formatRow = (cells: string[]) =>
|
|
141
|
+
"│" +
|
|
142
|
+
cells
|
|
143
|
+
.map((cell, c) => ` ${padCell(cell, colWidths[c]!)} `)
|
|
144
|
+
.join("│") +
|
|
145
|
+
"│";
|
|
146
|
+
|
|
147
|
+
const lines: string[] = [];
|
|
148
|
+
lines.push("```");
|
|
149
|
+
lines.push(topBorder);
|
|
150
|
+
lines.push(formatRow(header));
|
|
151
|
+
if (parsed.length > 1) {
|
|
152
|
+
lines.push(midBorder);
|
|
153
|
+
for (let r = 1; r < parsed.length; r++) {
|
|
154
|
+
lines.push(formatRow(parsed[r]!));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
lines.push(bottomBorder);
|
|
158
|
+
lines.push("```");
|
|
159
|
+
|
|
160
|
+
return lines.join("\n");
|
|
161
|
+
}
|