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 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
- Slack
32
- │ Messenger │
33
- (streaming)
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
+ - 이미지: `![alt](url)` → `<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
@@ -3,6 +3,7 @@ export interface AppConfig {
3
3
  slackBotToken: string;
4
4
  slackAppToken: string;
5
5
  reposBaseDir: string;
6
+ githubToken?: string;
6
7
  }
7
8
  export declare function createAndStartApp(config: AppConfig): Promise<App>;
8
9
  //# sourceMappingURL=app.d.ts.map
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;AAKlC,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CA0CvE"}
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,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAQ9D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAiB;IACvD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAE9D,eAAe,CAAC,YAAY,CAAC,CAAC;IAE9B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC;QAClB,KAAK,EAAE,aAAa;QACpB,QAAQ,EAAE,aAAa;QACvB,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;IAEH,iBAAiB;IACjB,GAAG,CAAC,KAAK,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;IACxC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IAE1C,6BAA6B;IAC7B,GAAG,CAAC,MAAM,CAAC,oBAAoB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QACvE,MAAM,GAAG,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,kDAAkD,MAAM,CAAC,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrH,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QACrC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,SAAS,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,kCAAkC,YAAY,mBAAmB,aAAa,EAAE,CAAC,CAAC;QAC9F,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE;QACpE,MAAM,GAAG,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QAC/C,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QACrC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC;QAClC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,SAAS,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,+BAA+B,YAAY,mBAAmB,aAAa,EAAE,CAAC,CAAC;QAC3F,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;QACtE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IAClB,OAAO,GAAG,CAAC;AACb,CAAC"}
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
- function requireEnv(name) {
14
- const value = process.env[name];
15
- if (!value) {
16
- console.error(`[eniac-slack] Missing required environment variable: ${name}`);
17
- console.error(`Create a .env file or export ${name} in your shell.`);
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 value;
51
+ return Object.fromEntries(REQUIRED_ENVS.map((name) => [name, process.env[name]]));
21
52
  }
22
53
  async function main() {
23
- const slackBotToken = requireEnv("SLACK_BOT_TOKEN");
24
- const slackAppToken = requireEnv("SLACK_APP_TOKEN");
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,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
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const CLAUDE_ICON_URL = "https://claude.ai/apple-touch-icon.png";
2
+ //# sourceMappingURL=constants.d.ts.map
@@ -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,2 @@
1
+ export const CLAUDE_ICON_URL = "https://claude.ai/apple-touch-icon.png";
2
+ //# sourceMappingURL=constants.js.map
@@ -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;AAW/E,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,CAgEf"}
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"}
@@ -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
- try {
49
- await client.chat.postMessage({
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
- thread_ts: threadTs,
52
- text: `:file_folder: Setting up repository \`${repoInfo.owner}/${repoInfo.repo}\`...`,
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: `:x: Failed to set up repository: ${message}`,
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
- // Create a temp directory for non-repo conversations
68
- workDir = await fs.mkdtemp(path.join(os.tmpdir(), "eniac-"));
114
+ // No repo mentioned use temp directory
115
+ await startSessionWithoutRepo(client, channel, threadTs, cleanText, authorUserId);
69
116
  }
70
- // Create a Claude session
71
- createSession(threadTs, workDir, authorUserId);
72
- // Build context-aware first message
73
- let userMessage = cleanText;
74
- if (repoInfo) {
75
- userMessage = `[Working directory: ${workDir} — Repository: ${repoInfo.owner}/${repoInfo.repo}]\n\n${cleanText}`;
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
- // Stream the response
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":"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"}
1
+ {"version":3,"file":"mention.js","sourceRoot":"","sources":["../../src/handlers/mention.ts"],"names":[],"mappings":"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"}
@@ -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":"AAIA;;;;;;;;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"}
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"}