eniac-slack 0.1.2 → 0.1.4
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/SPEC.md +57 -7
- package/dist/app.d.ts +1 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +77 -2
- package/dist/app.js.map +1 -1
- package/dist/cli.js +45 -8
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -0
- package/dist/handlers/mention.d.ts +21 -0
- package/dist/handlers/mention.d.ts.map +1 -1
- package/dist/handlers/mention.js +95 -20
- package/dist/handlers/mention.js.map +1 -1
- package/dist/services/git.d.ts +15 -0
- package/dist/services/git.d.ts.map +1 -1
- package/dist/services/git.js +78 -0
- package/dist/services/git.js.map +1 -1
- package/dist/services/permissions.d.ts.map +1 -1
- package/dist/services/permissions.js +2 -0
- package/dist/services/permissions.js.map +1 -1
- package/dist/services/slack-messenger.d.ts.map +1 -1
- package/dist/services/slack-messenger.js +2 -0
- package/dist/services/slack-messenger.js.map +1 -1
- package/package.json +1 -1
- package/src/app.ts +108 -2
- package/src/cli.ts +55 -8
- package/src/constants.ts +1 -0
- package/src/handlers/mention.ts +131 -21
- package/src/services/git.ts +101 -0
- package/src/services/permissions.ts +2 -0
- package/src/services/slack-messenger.ts +2 -0
package/SPEC.md
CHANGED
|
@@ -25,13 +25,15 @@ Slack 소켓모드 봇으로, 멘션을 통해 Claude Code 세션을 시작하
|
|
|
25
25
|
│ Claude │◄───────────►│ Permissions │
|
|
26
26
|
│ Service │ canUseTool │ Service │
|
|
27
27
|
│ (SDK API) │ callback │ │
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
└──┬───┬──────┘ └─────────────┘
|
|
29
|
+
│ │
|
|
30
|
+
┌──────────┘ └──────────┐
|
|
31
|
+
│ │
|
|
32
|
+
┌──────▼──────┐ ┌──────▼──────┐
|
|
33
|
+
│ Slack │ │ MCP Servers │
|
|
34
|
+
│ Messenger │ │ (optional) │
|
|
35
|
+
│ (streaming) │ └─────────────┘
|
|
36
|
+
└─────────────┘
|
|
35
37
|
```
|
|
36
38
|
|
|
37
39
|
## Modules
|
|
@@ -91,6 +93,25 @@ Slack 소켓모드 봇으로, 멘션을 통해 Claude Code 세션을 시작하
|
|
|
91
93
|
- `lastActivityAt` 매 대화마다 갱신
|
|
92
94
|
- 서버 시작 시 2주 이상 비활성 세션 자동 정리 (SDK `.jsonl` + worktree 삭제)
|
|
93
95
|
|
|
96
|
+
**MCP 서버 설정:**
|
|
97
|
+
|
|
98
|
+
- `~/.claude/.mcp.json` 파일에서 MCP 서버 설정 자동 로드
|
|
99
|
+
- `mcpServers` 항목이 있으면 Claude SDK에 전달
|
|
100
|
+
- 서버 시작 시 로드된 MCP 서버 목록 로깅
|
|
101
|
+
|
|
102
|
+
**시스템 프롬프트:**
|
|
103
|
+
|
|
104
|
+
- `claude_code` 프리셋 사용 + Slack mrkdwn 포맷팅 규칙 추가
|
|
105
|
+
- `systemPrompt: { type: "preset", preset: "claude_code", append: SLACK_FORMAT_PROMPT }`
|
|
106
|
+
- Slack 포맷팅 규칙: 헤더(`*bold*`), 볼드(단일 `*`), 링크(`<url|text>`), 테이블(box-drawing), 이모지 활용 등
|
|
107
|
+
|
|
108
|
+
**도구 입력 설명 생성:**
|
|
109
|
+
|
|
110
|
+
- `describeToolInput(toolName, input)`: 권한 요청 시 도구 입력을 사람이 읽기 쉬운 형태로 변환
|
|
111
|
+
- `Bash` → 코드블록으로 명령어 표시
|
|
112
|
+
- `Edit`, `Write`, `Read` → 파일 경로 표시
|
|
113
|
+
- 기타 → JSON 입력 (최대 500자)
|
|
114
|
+
|
|
94
115
|
**Claude SDK 호출:**
|
|
95
116
|
|
|
96
117
|
```typescript
|
|
@@ -102,6 +123,12 @@ query({
|
|
|
102
123
|
permissionMode: "bypassPermissions", // SDK 내장 권한 비활성화
|
|
103
124
|
allowDangerouslySkipPermissions: true,
|
|
104
125
|
includePartialMessages: true, // 실시간 스트리밍
|
|
126
|
+
systemPrompt: { // Slack 포맷팅 프롬프트
|
|
127
|
+
type: "preset",
|
|
128
|
+
preset: "claude_code",
|
|
129
|
+
append: SLACK_FORMAT_PROMPT,
|
|
130
|
+
},
|
|
131
|
+
mcpServers, // MCP 서버 (선택)
|
|
105
132
|
sessionId: session.sessionId, // 첫 호출
|
|
106
133
|
// 또는
|
|
107
134
|
resume: session.sessionId, // 이후 호출
|
|
@@ -126,6 +153,12 @@ type ChatEvent =
|
|
|
126
153
|
| { type: "error"; message: string };
|
|
127
154
|
```
|
|
128
155
|
|
|
156
|
+
**스트림 처리:**
|
|
157
|
+
|
|
158
|
+
- `chat()` 함수는 `AsyncGenerator<ChatEvent>` 반환
|
|
159
|
+
- `stream_event` → `content_block_delta` → `text_delta` 에서 텍스트 추출
|
|
160
|
+
- `result` 타입은 로깅만 수행 (최종 결과 확인용)
|
|
161
|
+
|
|
129
162
|
### `services/permissions.ts` — Permission Bridge
|
|
130
163
|
|
|
131
164
|
**플로우:**
|
|
@@ -189,6 +222,22 @@ Claude canUseTool 콜백 호출 (위험 도구)
|
|
|
189
222
|
4. default branch는 `HEAD` symbolic ref에서 자동 감지
|
|
190
223
|
5. 스레드마다 독립 브랜치 → PR 생성 가능
|
|
191
224
|
|
|
225
|
+
### `utils/slack-format.ts` — Markdown → Slack mrkdwn Converter
|
|
226
|
+
|
|
227
|
+
- `markdownToSlackMrkdwn(text)`: 최종 메시지를 Slack mrkdwn 포맷으로 변환
|
|
228
|
+
- 변환 규칙:
|
|
229
|
+
- 이미지: `` → `<url|alt>`
|
|
230
|
+
- 링크: `[text](url)` → `<url|text>`
|
|
231
|
+
- 헤더: `# text` → `*text*` (중첩 볼드 제거)
|
|
232
|
+
- 수평선: `---` → `───────────────────────────`
|
|
233
|
+
- 볼드: `**text**` / `__text__` → `*text*` (코드 스팬 내부는 보존)
|
|
234
|
+
- 테이블: Markdown 테이블 → box-drawing 문자를 사용한 코드블록
|
|
235
|
+
- 테이블 변환 상세:
|
|
236
|
+
- 파이프(`|`)로 시작하는 행 감지
|
|
237
|
+
- 구분선(`:---:` 등) 자동 제거
|
|
238
|
+
- `┌┬┐├┼┤└┴┘│─` 문자로 정렬된 테이블 생성
|
|
239
|
+
- 셀 너비 자동 계산 및 패딩
|
|
240
|
+
|
|
192
241
|
### `utils/parse.ts` — Message Parser
|
|
193
242
|
|
|
194
243
|
- `removeMention(text)`: `<@BOTID>` 제거
|
|
@@ -204,6 +253,7 @@ Claude canUseTool 콜백 호출 (위험 도구)
|
|
|
204
253
|
|--------|------|------|
|
|
205
254
|
| 세션 매핑 | `~/.eniac/sessions.json` | `threadTs → { sessionId, workDir, authorUserId, ... }` |
|
|
206
255
|
| SDK 대화 기록 | `~/.claude/projects/{cwd-encoded}/{sessionId}.jsonl` | 전체 대화 히스토리 (SDK 자동 관리) |
|
|
256
|
+
| MCP 설정 | `~/.claude/.mcp.json` | MCP 서버 연결 설정 (선택) |
|
|
207
257
|
| Git bare repos | `{REPOS_BASE_DIR}/{owner}/{repo}.git/` | 공유 bare clone |
|
|
208
258
|
| Git worktrees | `{REPOS_BASE_DIR}/{owner}/{repo}/worktrees/{timestamp}/` | 스레드별 작업 디렉토리 |
|
|
209
259
|
|
package/dist/app.d.ts
CHANGED
package/dist/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAalC,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CA2IvE"}
|
package/dist/app.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { App } from "@slack/bolt";
|
|
2
|
-
import { handleMention, setReposBaseDir } from "./handlers/mention.js";
|
|
2
|
+
import { handleMention, setReposBaseDir, getPendingRepoRequest, deletePendingRepoRequest, startSessionWithRepo, startSessionWithoutRepo, } from "./handlers/mention.js";
|
|
3
3
|
import { handleThreadMessage } from "./handlers/thread.js";
|
|
4
4
|
import { resolvePermission } from "./services/permissions.js";
|
|
5
|
+
import { createGithubRepo, setGithubToken } from "./services/git.js";
|
|
5
6
|
export async function createAndStartApp(config) {
|
|
6
|
-
const { slackBotToken, slackAppToken, reposBaseDir } = config;
|
|
7
|
+
const { slackBotToken, slackAppToken, reposBaseDir, githubToken } = config;
|
|
7
8
|
setReposBaseDir(reposBaseDir);
|
|
9
|
+
setGithubToken(githubToken);
|
|
8
10
|
const app = new App({
|
|
9
11
|
token: slackBotToken,
|
|
10
12
|
appToken: slackAppToken,
|
|
@@ -38,6 +40,79 @@ export async function createAndStartApp(config) {
|
|
|
38
40
|
await resolvePermission(client, permissionId, false, clickedUserId);
|
|
39
41
|
}
|
|
40
42
|
});
|
|
43
|
+
// Repo creation button handlers (private / public)
|
|
44
|
+
for (const actionId of ["create_repo_private", "create_repo_public"]) {
|
|
45
|
+
app.action(actionId, async ({ action, ack, client, body }) => {
|
|
46
|
+
await ack();
|
|
47
|
+
if (action.type !== "button" || !action.value)
|
|
48
|
+
return;
|
|
49
|
+
const requestId = action.value;
|
|
50
|
+
const pendingReq = getPendingRepoRequest(requestId);
|
|
51
|
+
if (!pendingReq)
|
|
52
|
+
return;
|
|
53
|
+
// Only the thread author can approve
|
|
54
|
+
const clickedUserId = body.user?.id ?? "someone";
|
|
55
|
+
if (pendingReq.authorUserId && clickedUserId !== pendingReq.authorUserId)
|
|
56
|
+
return;
|
|
57
|
+
deletePendingRepoRequest(requestId);
|
|
58
|
+
const { repoInfo, threadTs, channel, cleanText, authorUserId } = pendingReq;
|
|
59
|
+
const isPrivate = actionId === "create_repo_private";
|
|
60
|
+
const visibility = isPrivate ? "private" : "public";
|
|
61
|
+
// Update the message to show progress
|
|
62
|
+
const createMsg = body;
|
|
63
|
+
if (createMsg.message?.ts) {
|
|
64
|
+
await client.chat.update({
|
|
65
|
+
channel,
|
|
66
|
+
ts: createMsg.message.ts,
|
|
67
|
+
text: `:hourglass_flowing_sand: ${visibility === "private" ? "비공개" : "공개"} 리포지토리 \`${repoInfo.owner}/${repoInfo.repo}\` 생성 중...`,
|
|
68
|
+
blocks: [],
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await createGithubRepo(repoInfo.owner, repoInfo.repo, isPrivate);
|
|
73
|
+
await client.chat.postMessage({
|
|
74
|
+
channel,
|
|
75
|
+
thread_ts: threadTs,
|
|
76
|
+
text: `:white_check_mark: \`${repoInfo.owner}/${repoInfo.repo}\` ${visibility === "private" ? "비공개" : "공개"} 리포지토리가 생성되었습니다!`,
|
|
77
|
+
});
|
|
78
|
+
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
await client.chat.postMessage({
|
|
83
|
+
channel,
|
|
84
|
+
thread_ts: threadTs,
|
|
85
|
+
text: `:x: 리포지토리 생성 실패: ${message}`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
app.action("skip_repo", async ({ action, ack, client, body }) => {
|
|
91
|
+
await ack();
|
|
92
|
+
if (action.type !== "button" || !action.value)
|
|
93
|
+
return;
|
|
94
|
+
const requestId = action.value;
|
|
95
|
+
const pendingReq = getPendingRepoRequest(requestId);
|
|
96
|
+
if (!pendingReq)
|
|
97
|
+
return;
|
|
98
|
+
// Only the thread author can skip
|
|
99
|
+
const clickedUserId = body.user?.id ?? "someone";
|
|
100
|
+
if (pendingReq.authorUserId && clickedUserId !== pendingReq.authorUserId)
|
|
101
|
+
return;
|
|
102
|
+
deletePendingRepoRequest(requestId);
|
|
103
|
+
const { threadTs, channel, cleanText, authorUserId, repoInfo } = pendingReq;
|
|
104
|
+
// Update the message
|
|
105
|
+
const skipMsg = body;
|
|
106
|
+
if (skipMsg.message?.ts) {
|
|
107
|
+
await client.chat.update({
|
|
108
|
+
channel,
|
|
109
|
+
ts: skipMsg.message.ts,
|
|
110
|
+
text: `:fast_forward: \`${repoInfo.owner}/${repoInfo.repo}\` 리포지토리 없이 계속합니다.`,
|
|
111
|
+
blocks: [],
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId);
|
|
115
|
+
});
|
|
41
116
|
await app.start();
|
|
42
117
|
return app;
|
|
43
118
|
}
|
package/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -10,28 +10,65 @@ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, "../package.json"
|
|
|
10
10
|
// Load .env — try cwd first, then walk up to find it
|
|
11
11
|
dotenv.config();
|
|
12
12
|
dotenv.config({ path: path.resolve(process.cwd(), "../../.env") });
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const ENV_HELP = {
|
|
14
|
+
SLACK_BOT_TOKEN: "Bot User OAuth Token (xoxb-...).\n" +
|
|
15
|
+
" → Slack App 설정 > OAuth & Permissions 에서 확인할 수 있습니다.\n" +
|
|
16
|
+
" → https://api.slack.com/apps 에서 앱을 선택하세요.",
|
|
17
|
+
SLACK_APP_TOKEN: "App-Level Token (xapp-...).\n" +
|
|
18
|
+
" → Slack App 설정 > Basic Information > App-Level Tokens 에서 생성할 수 있습니다.\n" +
|
|
19
|
+
" → connections:write 스코프가 필요합니다.\n" +
|
|
20
|
+
" → https://api.slack.com/apps 에서 앱을 선택하세요.",
|
|
21
|
+
};
|
|
22
|
+
const REQUIRED_ENVS = ["SLACK_BOT_TOKEN", "SLACK_APP_TOKEN"];
|
|
23
|
+
function validateEnvs() {
|
|
24
|
+
const missing = REQUIRED_ENVS.filter((name) => !process.env[name]);
|
|
25
|
+
if (missing.length > 0) {
|
|
26
|
+
console.error("");
|
|
27
|
+
console.error("[eniac-slack] 필수 환경변수가 설정되지 않았습니다:");
|
|
28
|
+
console.error("");
|
|
29
|
+
for (const name of missing) {
|
|
30
|
+
console.error(` ✗ ${name}`);
|
|
31
|
+
if (ENV_HELP[name]) {
|
|
32
|
+
console.error(` ${ENV_HELP[name]}`);
|
|
33
|
+
console.error("");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.error("─".repeat(60));
|
|
37
|
+
console.error("");
|
|
38
|
+
console.error(" 해결 방법:");
|
|
39
|
+
console.error(" 1. .env.example 을 .env 로 복사한 뒤 값을 채워주세요.");
|
|
40
|
+
console.error(" cp .env.example .env");
|
|
41
|
+
console.error("");
|
|
42
|
+
console.error(" 2. 또는 셸에서 직접 export 하세요.");
|
|
43
|
+
for (const name of missing) {
|
|
44
|
+
console.error(` export ${name}=<your-token>`);
|
|
45
|
+
}
|
|
46
|
+
console.error("");
|
|
47
|
+
console.error(" Slack App 설정 페이지: https://api.slack.com/apps");
|
|
48
|
+
console.error("");
|
|
18
49
|
process.exit(1);
|
|
19
50
|
}
|
|
20
|
-
return
|
|
51
|
+
return Object.fromEntries(REQUIRED_ENVS.map((name) => [name, process.env[name]]));
|
|
21
52
|
}
|
|
22
53
|
async function main() {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
54
|
+
const envs = validateEnvs();
|
|
55
|
+
const slackBotToken = envs.SLACK_BOT_TOKEN;
|
|
56
|
+
const slackAppToken = envs.SLACK_APP_TOKEN;
|
|
57
|
+
const githubToken = process.env["GITHUB_TOKEN"];
|
|
25
58
|
const reposBaseDir = process.env["REPOS_BASE_DIR"]?.replace(/^~/, os.homedir()) ??
|
|
26
59
|
path.join(os.homedir(), ".eniac", "repos");
|
|
27
60
|
console.log(`[eniac-slack] v${pkg.version}`);
|
|
28
61
|
console.log("[eniac-slack] Starting Slack bot...");
|
|
29
62
|
console.log(`[eniac-slack] Repos base directory: ${reposBaseDir}`);
|
|
63
|
+
if (githubToken) {
|
|
64
|
+
console.log("[eniac-slack] GitHub token configured (repo creation enabled)");
|
|
65
|
+
}
|
|
30
66
|
try {
|
|
31
67
|
await createAndStartApp({
|
|
32
68
|
slackBotToken,
|
|
33
69
|
slackAppToken,
|
|
34
70
|
reposBaseDir,
|
|
71
|
+
githubToken,
|
|
35
72
|
});
|
|
36
73
|
console.log("[eniac-slack] Bot is running! Listening for events...");
|
|
37
74
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,2CAA2C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,wCAAwC,CAAC"}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { AllMiddlewareArgs, SlackEventMiddlewareArgs } from "@slack/bolt";
|
|
2
|
+
import { type GithubRepo } from "../utils/parse.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pending repo creation requests — keyed by a unique request ID.
|
|
5
|
+
*/
|
|
6
|
+
export interface PendingRepoRequest {
|
|
7
|
+
repoInfo: GithubRepo;
|
|
8
|
+
threadTs: string;
|
|
9
|
+
channel: string;
|
|
10
|
+
cleanText: string;
|
|
11
|
+
authorUserId: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function getPendingRepoRequest(requestId: string): PendingRepoRequest | undefined;
|
|
14
|
+
export declare function deletePendingRepoRequest(requestId: string): void;
|
|
2
15
|
export declare function setReposBaseDir(dir: string): void;
|
|
3
16
|
/**
|
|
4
17
|
* Handle `app_mention` events.
|
|
@@ -7,4 +20,12 @@ export declare function setReposBaseDir(dir: string): void;
|
|
|
7
20
|
* - Mention in a thread: forward to thread handler logic.
|
|
8
21
|
*/
|
|
9
22
|
export declare function handleMention(args: AllMiddlewareArgs & SlackEventMiddlewareArgs<"app_mention">): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Start a session with a GitHub repository working directory.
|
|
25
|
+
*/
|
|
26
|
+
export declare function startSessionWithRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, repoInfo: GithubRepo, cleanText: string, authorUserId: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Start a session without a repository (temp directory).
|
|
29
|
+
*/
|
|
30
|
+
export declare function startSessionWithoutRepo(client: AllMiddlewareArgs["client"], channel: string, threadTs: string, cleanText: string, authorUserId: string): Promise<void>;
|
|
10
31
|
//# sourceMappingURL=mention.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"mention.d.ts","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAStF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,UAAU,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAChB,kBAAkB,GAAG,SAAS,CAEhC;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAEhE;AAID,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD;AASD;;;;;GAKG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,iBAAiB,GAAG,wBAAwB,CAAC,aAAa,CAAC,GAChE,OAAO,CAAC,IAAI,CAAC,CA2Ff;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,EACnC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
package/dist/handlers/mention.js
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { removeMention, extractGithubRepo } from "../utils/parse.js";
|
|
2
2
|
import { createSession, getSession, chat } from "../services/claude.js";
|
|
3
|
-
import { prepareWorkDir } from "../services/git.js";
|
|
3
|
+
import { prepareWorkDir, checkRepoExists } from "../services/git.js";
|
|
4
4
|
import { postStreamingReply } from "../services/slack-messenger.js";
|
|
5
|
+
import { CLAUDE_ICON_URL } from "../constants.js";
|
|
5
6
|
import fs from "node:fs/promises";
|
|
6
7
|
import os from "node:os";
|
|
7
8
|
import path from "node:path";
|
|
9
|
+
const pendingRepoRequests = new Map();
|
|
10
|
+
let requestIdCounter = 0;
|
|
11
|
+
export function getPendingRepoRequest(requestId) {
|
|
12
|
+
return pendingRepoRequests.get(requestId);
|
|
13
|
+
}
|
|
14
|
+
export function deletePendingRepoRequest(requestId) {
|
|
15
|
+
pendingRepoRequests.delete(requestId);
|
|
16
|
+
}
|
|
8
17
|
let reposBaseDir;
|
|
9
18
|
export function setReposBaseDir(dir) {
|
|
10
19
|
reposBaseDir = dir;
|
|
@@ -38,46 +47,112 @@ export async function handleMention(args) {
|
|
|
38
47
|
channel,
|
|
39
48
|
thread_ts: threadTs,
|
|
40
49
|
text: "How can I help you? You can ask me coding questions, or mention a GitHub repo (e.g. `owner/repo`) to work with its code.",
|
|
50
|
+
icon_url: CLAUDE_ICON_URL,
|
|
41
51
|
});
|
|
42
52
|
return;
|
|
43
53
|
}
|
|
44
54
|
// Check for GitHub repo in message
|
|
45
55
|
const repoInfo = extractGithubRepo(cleanText);
|
|
46
|
-
let workDir;
|
|
47
56
|
if (repoInfo) {
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
// Check if repo exists on GitHub
|
|
58
|
+
const exists = await checkRepoExists(repoInfo.owner, repoInfo.repo);
|
|
59
|
+
if (!exists) {
|
|
60
|
+
// Repo doesn't exist — ask the user whether to create it
|
|
61
|
+
const requestId = `repo_${++requestIdCounter}`;
|
|
62
|
+
pendingRepoRequests.set(requestId, {
|
|
63
|
+
repoInfo,
|
|
64
|
+
threadTs,
|
|
50
65
|
channel,
|
|
51
|
-
|
|
52
|
-
|
|
66
|
+
cleanText,
|
|
67
|
+
authorUserId,
|
|
53
68
|
});
|
|
54
|
-
workDir = await prepareWorkDir(repoInfo, getReposBaseDir());
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
58
69
|
await client.chat.postMessage({
|
|
59
70
|
channel,
|
|
60
71
|
thread_ts: threadTs,
|
|
61
|
-
text: `:
|
|
72
|
+
text: `:warning: \`${repoInfo.owner}/${repoInfo.repo}\` 리포지토리가 존재하지 않습니다. 생성할까요?`,
|
|
73
|
+
icon_url: CLAUDE_ICON_URL,
|
|
74
|
+
blocks: [
|
|
75
|
+
{
|
|
76
|
+
type: "section",
|
|
77
|
+
text: {
|
|
78
|
+
type: "mrkdwn",
|
|
79
|
+
text: `:warning: \`${repoInfo.owner}/${repoInfo.repo}\` 리포지토리가 존재하지 않습니다.\n생성할까요?`,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "actions",
|
|
84
|
+
elements: [
|
|
85
|
+
{
|
|
86
|
+
type: "button",
|
|
87
|
+
text: { type: "plain_text", text: ":lock: 비공개 리포 생성" },
|
|
88
|
+
style: "primary",
|
|
89
|
+
action_id: "create_repo_private",
|
|
90
|
+
value: requestId,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
type: "button",
|
|
94
|
+
text: { type: "plain_text", text: ":globe_with_meridians: 공개 리포 생성" },
|
|
95
|
+
action_id: "create_repo_public",
|
|
96
|
+
value: requestId,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
text: { type: "plain_text", text: "리포 없이 계속" },
|
|
101
|
+
action_id: "skip_repo",
|
|
102
|
+
value: requestId,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
62
107
|
});
|
|
63
108
|
return;
|
|
64
109
|
}
|
|
110
|
+
// Repo exists — proceed with normal setup
|
|
111
|
+
await startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId);
|
|
65
112
|
}
|
|
66
113
|
else {
|
|
67
|
-
//
|
|
68
|
-
|
|
114
|
+
// No repo mentioned — use temp directory
|
|
115
|
+
await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId);
|
|
69
116
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Start a session with a GitHub repository working directory.
|
|
120
|
+
*/
|
|
121
|
+
export async function startSessionWithRepo(client, channel, threadTs, repoInfo, cleanText, authorUserId) {
|
|
122
|
+
let workDir;
|
|
123
|
+
try {
|
|
124
|
+
await client.chat.postMessage({
|
|
125
|
+
channel,
|
|
126
|
+
thread_ts: threadTs,
|
|
127
|
+
text: `:file_folder: Setting up repository \`${repoInfo.owner}/${repoInfo.repo}\`...`,
|
|
128
|
+
icon_url: CLAUDE_ICON_URL,
|
|
129
|
+
});
|
|
130
|
+
workDir = await prepareWorkDir(repoInfo, getReposBaseDir());
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
134
|
+
await client.chat.postMessage({
|
|
135
|
+
channel,
|
|
136
|
+
thread_ts: threadTs,
|
|
137
|
+
text: `:x: Failed to set up repository: ${message}`,
|
|
138
|
+
icon_url: CLAUDE_ICON_URL,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
76
141
|
}
|
|
77
|
-
|
|
142
|
+
createSession(threadTs, workDir, authorUserId);
|
|
143
|
+
const userMessage = `[Working directory: ${workDir} — Repository: ${repoInfo.owner}/${repoInfo.repo}]\n\n${cleanText}`;
|
|
78
144
|
const stream = chat(threadTs, userMessage, client, channel);
|
|
79
145
|
await postStreamingReply(client, channel, threadTs, stream);
|
|
80
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Start a session without a repository (temp directory).
|
|
149
|
+
*/
|
|
150
|
+
export async function startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId) {
|
|
151
|
+
const workDir = await fs.mkdtemp(path.join(os.tmpdir(), "eniac-"));
|
|
152
|
+
createSession(threadTs, workDir, authorUserId);
|
|
153
|
+
const stream = chat(threadTs, cleanText, client, channel);
|
|
154
|
+
await postStreamingReply(client, channel, threadTs, stream);
|
|
155
|
+
}
|
|
81
156
|
/**
|
|
82
157
|
* Handle a mention that occurs inside an existing thread.
|
|
83
158
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"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"}
|
package/dist/services/git.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
export declare function setGithubToken(token: string | undefined): void;
|
|
2
|
+
/**
|
|
3
|
+
* Check if a GitHub repository exists and is accessible.
|
|
4
|
+
* Uses the GitHub API when a token is available for accurate results
|
|
5
|
+
* (including private repos). Falls back to git ls-remote otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export declare function checkRepoExists(owner: string, repo: string): Promise<boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Create a new GitHub repository using the GitHub REST API.
|
|
10
|
+
* Requires a GITHUB_TOKEN with `repo` scope.
|
|
11
|
+
*
|
|
12
|
+
* Tries to create under the authenticated user first.
|
|
13
|
+
* If the owner differs from the authenticated user, creates under the org.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createGithubRepo(owner: string, repo: string, isPrivate?: boolean): Promise<void>;
|
|
1
16
|
/**
|
|
2
17
|
* Prepare a working directory for a given GitHub repository.
|
|
3
18
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/services/git.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAE9D;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,CAAC,CA0BlB;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,OAAc,GACxB,OAAO,CAAC,IAAI,CAAC,CA8Cf;AAED;;;;;;;;GAQG;AACH,wBAAsB,cAAc,CAClC,cAAc,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAC5D,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAiFjB"}
|