multi-project-gateway 0.2.0 → 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/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/router.ts","../src/claude-cli.ts","../src/session-manager.ts","../src/session-store.ts","../src/discord.ts","../src/init.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { config as loadEnv } from 'dotenv';\nimport { loadConfig } from './config.js';\nimport { createRouter } from './router.js';\nimport { createSessionManager } from './session-manager.js';\nimport { createFileSessionStore } from './session-store.js';\nimport { createDiscordBot } from './discord.js';\nimport { runInit } from './init.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0] ?? 'start';\n\nasync function main() {\n switch (command) {\n case 'start':\n return start();\n case 'init':\n return runInit();\n case 'status':\n return status();\n case 'help':\n case '--help':\n case '-h':\n return help();\n case '--version':\n case '-v':\n return version();\n default:\n console.error(`Unknown command: ${command}`);\n help();\n process.exit(1);\n }\n}\n\nfunction help() {\n console.log(`\nmpg — multi-project gateway for Claude Code\n\nUsage: mpg <command>\n\nCommands:\n start Start the gateway (default)\n init Interactive setup wizard\n status Show session status\n help Show this message\n\nOptions:\n -v, --version Show version\n -h, --help Show this message\n`.trim());\n}\n\nfunction version() {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));\n console.log(`mpg v${pkg.version}`);\n}\n\nfunction start() {\n const configDir = process.cwd();\n const envPath = resolve(configDir, '.env');\n if (existsSync(envPath)) {\n loadEnv({ path: envPath });\n }\n\n const token = process.env.DISCORD_BOT_TOKEN;\n if (!token) {\n console.error('DISCORD_BOT_TOKEN is not set. Run `mpg init` or set it in .env');\n process.exit(1);\n }\n\n const configPath = resolve(configDir, 'config.json');\n if (!existsSync(configPath)) {\n console.error('config.json not found. Run `mpg init` to create one.');\n process.exit(1);\n }\n\n const rawConfig = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(rawConfig);\n\n const projectCount = Object.keys(config.projects).length;\n if (projectCount === 0) {\n console.error('No projects configured in config.json');\n process.exit(1);\n }\n\n // Validate project directories exist\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (!existsSync(project.directory)) {\n console.warn(`Warning: directory not found for ${project.name}: ${project.directory}`);\n }\n }\n\n console.log(`Loaded ${projectCount} project(s) from config`);\n\n const router = createRouter(config);\n const sessionStore = createFileSessionStore(resolve(configDir, '.sessions.json'));\n const sessionManager = createSessionManager(config.defaults, sessionStore);\n const bot = createDiscordBot(router, sessionManager, config);\n\n function shutdown() {\n console.log('Shutting down...');\n sessionManager.shutdown();\n bot.stop();\n process.exit(0);\n }\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n bot.start(token).catch((err) => {\n console.error('Failed to start bot:', err);\n process.exit(1);\n });\n}\n\nfunction status() {\n const configDir = process.cwd();\n const sessionsPath = resolve(configDir, '.sessions.json');\n\n if (!existsSync(sessionsPath)) {\n console.log('No sessions file found. Is the gateway running?');\n return;\n }\n\n const configPath = resolve(configDir, 'config.json');\n let projectNames: Record<string, string> = {};\n if (existsSync(configPath)) {\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(raw);\n for (const [channelId, project] of Object.entries(config.projects)) {\n projectNames[channelId] = project.name;\n }\n } catch {\n // ignore config errors for status\n }\n }\n\n const sessions = JSON.parse(readFileSync(sessionsPath, 'utf-8')) as Array<{\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n }>;\n\n if (sessions.length === 0) {\n console.log('No active sessions.');\n return;\n }\n\n console.log(`Sessions (${sessions.length}):\\n`);\n for (const s of sessions) {\n const name = projectNames[s.projectKey] ?? s.projectKey;\n const ago = Math.floor((Date.now() - s.lastActivity) / 60000);\n console.log(` ${name}`);\n console.log(` Session: ${s.sessionId}`);\n console.log(` Dir: ${s.cwd}`);\n console.log(` Idle: ${ago}m`);\n console.log();\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","export interface ProjectConfig {\n name: string;\n directory: string;\n idleTimeoutMs?: number;\n claudeArgs?: string[];\n}\n\nexport interface GatewayDefaults {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}\n\nexport interface GatewayConfig {\n defaults: GatewayDefaults;\n projects: Record<string, ProjectConfig>;\n}\n\nexport function loadConfig(raw: unknown): GatewayConfig {\n if (!raw || typeof raw !== 'object') {\n throw new Error('Config must be an object');\n }\n\n const obj = raw as Record<string, unknown>;\n\n if (!obj.projects || typeof obj.projects !== 'object') {\n throw new Error('Config must have a \"projects\" object');\n }\n\n const projects = obj.projects as Record<string, unknown>;\n const validated: Record<string, ProjectConfig> = {};\n\n for (const [channelId, project] of Object.entries(projects)) {\n if (!project || typeof project !== 'object') {\n throw new Error(`Project for channel ${channelId} must be an object`);\n }\n const p = project as Record<string, unknown>;\n if (typeof p.directory !== 'string' || !p.directory) {\n throw new Error(`Project for channel ${channelId} must have a \"directory\" string`);\n }\n validated[channelId] = {\n name: typeof p.name === 'string' ? p.name : channelId,\n directory: p.directory,\n ...(p.idleTimeoutMs !== undefined && { idleTimeoutMs: Number(p.idleTimeoutMs) }),\n ...(Array.isArray(p.claudeArgs) && { claudeArgs: p.claudeArgs as string[] }),\n };\n }\n\n const defaults = (obj.defaults ?? {}) as Record<string, unknown>;\n\n return {\n defaults: {\n idleTimeoutMs: typeof defaults.idleTimeoutMs === 'number' ? defaults.idleTimeoutMs : 1800000,\n maxConcurrentSessions: typeof defaults.maxConcurrentSessions === 'number' ? defaults.maxConcurrentSessions : 4,\n claudeArgs: Array.isArray(defaults.claudeArgs) ? (defaults.claudeArgs as string[]) : ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: validated,\n };\n}\n","import type { GatewayConfig, ProjectConfig } from './config.js';\n\nexport interface ResolvedProject {\n channelId: string;\n name: string;\n directory: string;\n}\n\nexport interface Router {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null;\n}\n\nexport function createRouter(config: GatewayConfig): Router {\n return {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null {\n const project = config.projects[channelId];\n if (project) {\n return { channelId, name: project.name, directory: project.directory };\n }\n\n if (parentChannelId) {\n const parentProject = config.projects[parentChannelId];\n if (parentProject) {\n return { channelId: parentChannelId, name: parentProject.name, directory: parentProject.directory };\n }\n }\n\n return null;\n },\n };\n}\n","import { spawn } from 'node:child_process';\n\nexport interface ClaudeResult {\n text: string;\n sessionId: string;\n isError: boolean;\n}\n\nexport function parseClaudeJsonOutput(raw: string): ClaudeResult {\n const data = JSON.parse(raw);\n return {\n text: data.result ?? '',\n sessionId: data.session_id ?? '',\n isError: Boolean(data.is_error),\n };\n}\n\nexport function buildClaudeArgs(\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): string[] {\n const args = ['--print', ...baseArgs];\n if (sessionId) {\n args.push('--resume', sessionId);\n }\n args.push(prompt);\n return args;\n}\n\nexport function runClaude(\n cwd: string,\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): Promise<ClaudeResult> {\n return new Promise((resolve, reject) => {\n const args = buildClaudeArgs(baseArgs, prompt, sessionId);\n const proc = spawn('claude', args, {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on('close', (code) => {\n if (code !== 0) {\n reject(new Error(`claude exited with code ${code}: ${stderr}`));\n return;\n }\n try {\n const result = parseClaudeJsonOutput(stdout.trim());\n resolve(result);\n } catch (err) {\n reject(new Error(`Failed to parse claude output: ${stdout.slice(0, 200)}`));\n }\n });\n\n proc.on('error', (err) => {\n reject(new Error(`Failed to spawn claude: ${err.message}`));\n });\n });\n}\n","import { runClaude, type ClaudeResult } from './claude-cli.js';\nimport type { SessionStore, PersistedSession } from './session-store.js';\n\nexport interface SessionInfo {\n sessionId: string;\n projectKey: string;\n lastActivity: number;\n queueLength: number;\n}\n\nexport interface SessionManager {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult>;\n getSession(projectKey: string): SessionInfo | undefined;\n listSessions(): SessionInfo[];\n clearSession(projectKey: string): boolean;\n shutdown(): void;\n}\n\ninterface InternalSession {\n sessionId: string | undefined;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n processing: boolean;\n queue: Array<{\n prompt: string;\n resolve: (result: ClaudeResult) => void;\n reject: (error: Error) => void;\n }>;\n idleTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport function createSessionManager(defaults: {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}, store?: SessionStore): SessionManager {\n const sessions = new Map<string, InternalSession>();\n\n let activeProcesses = 0;\n const waiters: Array<() => void> = [];\n\n function persistSessions(): void {\n if (!store) return;\n // Merge in-memory sessions with existing persisted data.\n // In-memory sessions take precedence; persisted-only entries are preserved.\n const persisted = store.load();\n for (const [key, s] of sessions) {\n if (s.sessionId) {\n persisted.set(key, {\n sessionId: s.sessionId,\n projectKey: s.projectKey,\n cwd: s.cwd,\n lastActivity: s.lastActivity,\n });\n }\n }\n store.save(persisted);\n }\n\n async function acquireSlot(): Promise<void> {\n if (activeProcesses < defaults.maxConcurrentSessions) {\n activeProcesses++;\n return;\n }\n return new Promise<void>((resolve) => {\n waiters.push(() => {\n activeProcesses++;\n resolve();\n });\n });\n }\n\n function releaseSlot(): void {\n activeProcesses--;\n const next = waiters.shift();\n if (next) next();\n }\n\n function resetIdleTimer(session: InternalSession) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n session.idleTimer = setTimeout(() => {\n // Remove from memory only; session ID stays on disk for later resume\n sessions.delete(session.projectKey);\n }, defaults.idleTimeoutMs);\n }\n\n async function processQueue(session: InternalSession): Promise<void> {\n if (session.processing || session.queue.length === 0) return;\n session.processing = true;\n\n while (session.queue.length > 0) {\n const item = session.queue.shift()!;\n await acquireSlot();\n try {\n const result = await runClaude(\n session.cwd,\n defaults.claudeArgs,\n item.prompt,\n session.sessionId,\n );\n session.sessionId = result.sessionId || session.sessionId;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (err) {\n if (session.sessionId) {\n session.sessionId = undefined;\n try {\n const result = await runClaude(session.cwd, defaults.claudeArgs, item.prompt, undefined);\n session.sessionId = result.sessionId || undefined;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (retryErr) {\n item.reject(retryErr instanceof Error ? retryErr : new Error(String(retryErr)));\n }\n } else {\n item.reject(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n releaseSlot();\n }\n }\n\n session.processing = false;\n }\n\n function getOrCreateSession(projectKey: string, cwd: string): InternalSession {\n let session = sessions.get(projectKey);\n if (!session) {\n // Check store for a previously persisted session ID\n let restoredSessionId: string | undefined;\n if (store) {\n const persisted = store.load();\n const entry = persisted.get(projectKey);\n if (entry?.sessionId) {\n restoredSessionId = entry.sessionId;\n }\n }\n\n session = {\n sessionId: restoredSessionId,\n projectKey,\n cwd,\n lastActivity: Date.now(),\n processing: false,\n queue: [],\n idleTimer: null,\n };\n sessions.set(projectKey, session);\n resetIdleTimer(session);\n }\n return session;\n }\n\n // Restore persisted sessions into memory at startup\n if (store) {\n const persisted = store.load();\n for (const [key, entry] of persisted) {\n sessions.set(key, {\n sessionId: entry.sessionId,\n projectKey: entry.projectKey,\n cwd: entry.cwd,\n lastActivity: entry.lastActivity,\n processing: false,\n queue: [],\n idleTimer: null,\n });\n }\n for (const session of sessions.values()) {\n resetIdleTimer(session);\n }\n if (persisted.size > 0) {\n console.log(`Restored ${persisted.size} session(s) from disk`);\n }\n }\n\n return {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult> {\n const session = getOrCreateSession(projectKey, cwd);\n return new Promise<ClaudeResult>((resolve, reject) => {\n session.queue.push({ prompt, resolve, reject });\n processQueue(session);\n });\n },\n\n getSession(projectKey: string): SessionInfo | undefined {\n const session = sessions.get(projectKey);\n if (!session) return undefined;\n return {\n sessionId: session.sessionId ?? '',\n projectKey: session.projectKey,\n lastActivity: session.lastActivity,\n queueLength: session.queue.length,\n };\n },\n\n listSessions(): SessionInfo[] {\n return Array.from(sessions.values()).map((s) => ({\n sessionId: s.sessionId ?? '',\n projectKey: s.projectKey,\n lastActivity: s.lastActivity,\n queueLength: s.queue.length,\n }));\n },\n\n clearSession(projectKey: string): boolean {\n const session = sessions.get(projectKey);\n if (!session) return false;\n if (session.idleTimer) clearTimeout(session.idleTimer);\n sessions.delete(projectKey);\n persistSessions();\n return true;\n },\n\n shutdown() {\n persistSessions();\n for (const session of sessions.values()) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n }\n sessions.clear();\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface PersistedSession {\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n}\n\nexport interface SessionStore {\n load(): Map<string, PersistedSession>;\n save(sessions: Map<string, PersistedSession>): void;\n}\n\nexport function createFileSessionStore(filePath: string): SessionStore {\n return {\n load(): Map<string, PersistedSession> {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entries: PersistedSession[] = JSON.parse(raw);\n const map = new Map<string, PersistedSession>();\n for (const entry of entries) {\n if (entry.sessionId && entry.projectKey) {\n map.set(entry.projectKey, entry);\n }\n }\n return map;\n } catch {\n return new Map();\n }\n },\n\n save(sessions: Map<string, PersistedSession>): void {\n const entries = Array.from(sessions.values());\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(entries, null, 2) + '\\n');\n } catch (err) {\n console.error('Failed to persist sessions:', err);\n }\n },\n };\n}\n","import { Client, GatewayIntentBits, Events, type Message } from 'discord.js';\nimport type { Router } from './router.js';\nimport type { SessionManager } from './session-manager.js';\nimport type { GatewayConfig } from './config.js';\n\nexport function chunkMessage(text: string, limit: number): string[] {\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const lines = text.split('\\n');\n let current = '';\n\n for (const line of lines) {\n if (line.length > limit) {\n if (current) {\n chunks.push(current);\n current = '';\n }\n for (let i = 0; i < line.length; i += limit) {\n chunks.push(line.slice(i, i + limit));\n }\n continue;\n }\n\n const candidate = current ? `${current}\\n${line}` : line;\n if (candidate.length > limit) {\n chunks.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n\n if (current || chunks.length === 0) {\n chunks.push(current);\n }\n\n return chunks;\n}\n\nexport interface DiscordBot {\n start(token: string): Promise<void>;\n stop(): void;\n}\n\nfunction resolveProjectName(config: GatewayConfig, channelId: string): string {\n return config.projects[channelId]?.name ?? channelId;\n}\n\nfunction findProjectByName(config: GatewayConfig, name: string): { channelId: string; name: string } | null {\n const lower = name.toLowerCase();\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (project.name.toLowerCase() === lower) {\n return { channelId, name: project.name };\n }\n }\n return null;\n}\n\nfunction formatTimeSince(timestamp: number): string {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ago`;\n}\n\nexport function handleCommand(\n command: string,\n config: GatewayConfig,\n sessionManager: SessionManager,\n): string | null {\n const parts = command.trim().split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n\n if (cmd === '!sessions') {\n const allSessions = sessionManager.listSessions();\n if (allSessions.length === 0) {\n return 'No active sessions.';\n }\n const lines = allSessions.map((s) => {\n const name = resolveProjectName(config, s.projectKey);\n const idle = formatTimeSince(s.lastActivity);\n const queue = s.queueLength > 0 ? ` | queue: ${s.queueLength}` : '';\n const sid = s.sessionId ? ` | \\`${s.sessionId.slice(0, 8)}…\\`` : '';\n return `- **${name}** — last active ${idle}${queue}${sid}`;\n });\n return `**Active sessions (${allSessions.length})**\\n${lines.join('\\n')}`;\n }\n\n if (cmd === '!session') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!session <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const info = sessionManager.getSession(project.channelId);\n if (!info) return `**${project.name}** — no active session.`;\n const idle = formatTimeSince(info.lastActivity);\n const sid = info.sessionId || 'none';\n return [\n `**${project.name}**`,\n `Session ID: \\`${sid}\\``,\n `Last active: ${idle}`,\n `Queue depth: ${info.queueLength}`,\n ].join('\\n');\n }\n\n if (cmd === '!kill') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!kill <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const cleared = sessionManager.clearSession(project.channelId);\n if (cleared) return `Session for **${project.name}** cleared.`;\n return `**${project.name}** — no active session to clear.`;\n }\n\n if (cmd === '!help') {\n return [\n '**Gateway commands**',\n '`!sessions` — list all active sessions',\n '`!session <name>` — inspect a specific project session',\n '`!kill <name>` — force-close a project session',\n '`!help` — show this message',\n ].join('\\n');\n }\n\n return null;\n}\n\nexport function createDiscordBot(router: Router, sessionManager: SessionManager, config: GatewayConfig): DiscordBot {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n client.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n if (!('send' in message.channel)) return;\n\n // Handle gateway commands from any mapped channel\n if (message.content.startsWith('!')) {\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (resolved) {\n const response = handleCommand(message.content, config, sessionManager);\n if (response) {\n await message.channel.send(response);\n return;\n }\n }\n }\n\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (!resolved) return;\n\n try {\n await message.react('👀');\n } catch {\n // Reaction may fail if permissions are missing — non-critical\n }\n\n try {\n const result = await sessionManager.send(\n resolved.channelId,\n resolved.directory,\n message.content,\n );\n\n const chunks = chunkMessage(result.text, 2000);\n for (const chunk of chunks) {\n await message.channel.send(chunk);\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n await message.channel.send(\n `**Error** (${resolved.name}): ${errorMsg.slice(0, 1800)}`,\n );\n }\n });\n\n return {\n async start(token: string) {\n await client.login(token);\n console.log(`Gateway connected as ${client.user?.tag}`);\n },\n stop() {\n client.destroy();\n },\n };\n}\n","import { createInterface } from 'node:readline';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction createPrompt(): (question: string) => Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return (question: string) =>\n new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));\n}\n\nexport async function runInit() {\n const ask = createPrompt();\n const configDir = process.cwd();\n\n console.log('\\nmpg init — set up multi-project gateway\\n');\n\n // Check for claude CLI\n try {\n execSync('claude --version', { stdio: 'pipe' });\n console.log('Claude CLI found.');\n } catch {\n console.warn('Warning: `claude` not found on PATH. Make sure it is installed before starting the gateway.');\n }\n\n // Discord bot token\n let token = process.env.DISCORD_BOT_TOKEN ?? '';\n const inputToken = await ask(`Discord bot token${token ? ' (press Enter to keep existing)' : ''}: `);\n if (inputToken) token = inputToken;\n if (!token) {\n console.error('A Discord bot token is required. Create one at https://discord.com/developers/applications');\n process.exit(1);\n }\n\n // Write .env\n const envPath = resolve(configDir, '.env');\n writeFileSync(envPath, `DISCORD_BOT_TOKEN=${token}\\n`);\n console.log(`Wrote ${envPath}`);\n\n // Collect projects\n interface ProjectEntry {\n name: string;\n directory: string;\n channelId: string;\n }\n\n const projects: ProjectEntry[] = [];\n\n // Load existing config if present\n const configPath = resolve(configDir, 'config.json');\n if (existsSync(configPath)) {\n try {\n const existing = JSON.parse(\n (await import('node:fs')).readFileSync(configPath, 'utf-8'),\n );\n if (existing.projects) {\n for (const [channelId, project] of Object.entries(existing.projects)) {\n const p = project as { name?: string; directory: string };\n projects.push({ name: p.name ?? channelId, directory: p.directory, channelId });\n }\n if (projects.length > 0) {\n console.log(`\\nExisting projects (${projects.length}):`);\n for (const p of projects) {\n console.log(` ${p.name} → ${p.directory} (${p.channelId})`);\n }\n }\n }\n } catch {\n // ignore parse errors\n }\n }\n\n console.log('\\nAdd projects (empty name to finish):\\n');\n\n while (true) {\n const name = await ask('Project name: ');\n if (!name) break;\n\n const directory = await ask('Project directory (absolute path): ');\n if (!directory) {\n console.log('Directory is required, skipping.');\n continue;\n }\n if (!existsSync(directory)) {\n console.warn(`Warning: ${directory} does not exist.`);\n }\n\n const channelId = await ask('Discord channel ID: ');\n if (!channelId) {\n console.log('Channel ID is required, skipping.');\n continue;\n }\n\n projects.push({ name, directory, channelId });\n console.log(`Added ${name}\\n`);\n }\n\n if (projects.length === 0) {\n console.log('No projects configured. You can edit config.json later.');\n }\n\n // Build config\n const config = {\n defaults: {\n idleTimeoutMs: 1800000,\n maxConcurrentSessions: 4,\n claudeArgs: ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: Object.fromEntries(\n projects.map((p) => [p.channelId, { name: p.name, directory: p.directory }]),\n ),\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n console.log(`Wrote ${configPath}`);\n\n console.log('\\nSetup complete! Run `mpg start` to launch the gateway.');\n}\n"],"mappings":";;;AAAA,SAAS,WAAAA,gBAAe;AACxB,SAAS,cAAAC,aAAY,gBAAAC,qBAAoB;AACzC,SAAS,UAAU,eAAe;;;ACgB3B,SAAS,WAAW,KAA6B;AACtD,MAAI,CAAC,OAAO,OAAO,QAAQ,UAAU;AACnC,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAEA,QAAM,MAAM;AAEZ,MAAI,CAAC,IAAI,YAAY,OAAO,IAAI,aAAa,UAAU;AACrD,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AAEA,QAAM,WAAW,IAAI;AACrB,QAAM,YAA2C,CAAC;AAElD,aAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC3D,QAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,YAAM,IAAI,MAAM,uBAAuB,SAAS,oBAAoB;AAAA,IACtE;AACA,UAAM,IAAI;AACV,QAAI,OAAO,EAAE,cAAc,YAAY,CAAC,EAAE,WAAW;AACnD,YAAM,IAAI,MAAM,uBAAuB,SAAS,iCAAiC;AAAA,IACnF;AACA,cAAU,SAAS,IAAI;AAAA,MACrB,MAAM,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,MAC5C,WAAW,EAAE;AAAA,MACb,GAAI,EAAE,kBAAkB,UAAa,EAAE,eAAe,OAAO,EAAE,aAAa,EAAE;AAAA,MAC9E,GAAI,MAAM,QAAQ,EAAE,UAAU,KAAK,EAAE,YAAY,EAAE,WAAuB;AAAA,IAC5E;AAAA,EACF;AAEA,QAAM,WAAY,IAAI,YAAY,CAAC;AAEnC,SAAO;AAAA,IACL,UAAU;AAAA,MACR,eAAe,OAAO,SAAS,kBAAkB,WAAW,SAAS,gBAAgB;AAAA,MACrF,uBAAuB,OAAO,SAAS,0BAA0B,WAAW,SAAS,wBAAwB;AAAA,MAC7G,YAAY,MAAM,QAAQ,SAAS,UAAU,IAAK,SAAS,aAA0B,CAAC,qBAAqB,eAAe,mBAAmB,MAAM;AAAA,IACrJ;AAAA,IACA,UAAU;AAAA,EACZ;AACF;;;AC9CO,SAAS,aAAa,QAA+B;AAC1D,SAAO;AAAA,IACL,QAAQ,WAAmB,iBAAkD;AAC3E,YAAM,UAAU,OAAO,SAAS,SAAS;AACzC,UAAI,SAAS;AACX,eAAO,EAAE,WAAW,MAAM,QAAQ,MAAM,WAAW,QAAQ,UAAU;AAAA,MACvE;AAEA,UAAI,iBAAiB;AACnB,cAAM,gBAAgB,OAAO,SAAS,eAAe;AACrD,YAAI,eAAe;AACjB,iBAAO,EAAE,WAAW,iBAAiB,MAAM,cAAc,MAAM,WAAW,cAAc,UAAU;AAAA,QACpG;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC9BA,SAAS,aAAa;AAQf,SAAS,sBAAsB,KAA2B;AAC/D,QAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAO;AAAA,IACL,MAAM,KAAK,UAAU;AAAA,IACrB,WAAW,KAAK,cAAc;AAAA,IAC9B,SAAS,QAAQ,KAAK,QAAQ;AAAA,EAChC;AACF;AAEO,SAAS,gBACd,UACA,QACA,WACU;AACV,QAAMC,QAAO,CAAC,WAAW,GAAG,QAAQ;AACpC,MAAI,WAAW;AACb,IAAAA,MAAK,KAAK,YAAY,SAAS;AAAA,EACjC;AACA,EAAAA,MAAK,KAAK,MAAM;AAChB,SAAOA;AACT;AAEO,SAAS,UACd,KACA,UACA,QACA,WACuB;AACvB,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAMD,QAAO,gBAAgB,UAAU,QAAQ,SAAS;AACxD,UAAM,OAAO,MAAM,UAAUA,OAAM;AAAA,MACjC;AAAA,MACA,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AAED,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,SAAS;AACzB,UAAI,SAAS,GAAG;AACd,eAAO,IAAI,MAAM,2BAA2B,IAAI,KAAK,MAAM,EAAE,CAAC;AAC9D;AAAA,MACF;AACA,UAAI;AACF,cAAM,SAAS,sBAAsB,OAAO,KAAK,CAAC;AAClD,QAAAC,SAAQ,MAAM;AAAA,MAChB,SAAS,KAAK;AACZ,eAAO,IAAI,MAAM,kCAAkC,OAAO,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;AAAA,MAC5E;AAAA,IACF,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,aAAO,IAAI,MAAM,2BAA2B,IAAI,OAAO,EAAE,CAAC;AAAA,IAC5D,CAAC;AAAA,EACH,CAAC;AACH;;;ACvCO,SAAS,qBAAqB,UAIlC,OAAsC;AACvC,QAAM,WAAW,oBAAI,IAA6B;AAElD,MAAI,kBAAkB;AACtB,QAAM,UAA6B,CAAC;AAEpC,WAAS,kBAAwB;AAC/B,QAAI,CAAC,MAAO;AAGZ,UAAM,YAAY,MAAM,KAAK;AAC7B,eAAW,CAAC,KAAK,CAAC,KAAK,UAAU;AAC/B,UAAI,EAAE,WAAW;AACf,kBAAU,IAAI,KAAK;AAAA,UACjB,WAAW,EAAE;AAAA,UACb,YAAY,EAAE;AAAA,UACd,KAAK,EAAE;AAAA,UACP,cAAc,EAAE;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AACA,UAAM,KAAK,SAAS;AAAA,EACtB;AAEA,iBAAe,cAA6B;AAC1C,QAAI,kBAAkB,SAAS,uBAAuB;AACpD;AACA;AAAA,IACF;AACA,WAAO,IAAI,QAAc,CAACC,aAAY;AACpC,cAAQ,KAAK,MAAM;AACjB;AACA,QAAAA,SAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAEA,WAAS,cAAoB;AAC3B;AACA,UAAM,OAAO,QAAQ,MAAM;AAC3B,QAAI,KAAM,MAAK;AAAA,EACjB;AAEA,WAAS,eAAe,SAA0B;AAChD,QAAI,QAAQ,UAAW,cAAa,QAAQ,SAAS;AACrD,YAAQ,YAAY,WAAW,MAAM;AAEnC,eAAS,OAAO,QAAQ,UAAU;AAAA,IACpC,GAAG,SAAS,aAAa;AAAA,EAC3B;AAEA,iBAAe,aAAa,SAAyC;AACnE,QAAI,QAAQ,cAAc,QAAQ,MAAM,WAAW,EAAG;AACtD,YAAQ,aAAa;AAErB,WAAO,QAAQ,MAAM,SAAS,GAAG;AAC/B,YAAM,OAAO,QAAQ,MAAM,MAAM;AACjC,YAAM,YAAY;AAClB,UAAI;AACF,cAAM,SAAS,MAAM;AAAA,UACnB,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,KAAK;AAAA,UACL,QAAQ;AAAA,QACV;AACA,gBAAQ,YAAY,OAAO,aAAa,QAAQ;AAChD,gBAAQ,eAAe,KAAK,IAAI;AAChC,uBAAe,OAAO;AACtB,wBAAgB;AAChB,aAAK,QAAQ,MAAM;AAAA,MACrB,SAAS,KAAK;AACZ,YAAI,QAAQ,WAAW;AACrB,kBAAQ,YAAY;AACpB,cAAI;AACF,kBAAM,SAAS,MAAM,UAAU,QAAQ,KAAK,SAAS,YAAY,KAAK,QAAQ,MAAS;AACvF,oBAAQ,YAAY,OAAO,aAAa;AACxC,oBAAQ,eAAe,KAAK,IAAI;AAChC,2BAAe,OAAO;AACtB,4BAAgB;AAChB,iBAAK,QAAQ,MAAM;AAAA,UACrB,SAAS,UAAU;AACjB,iBAAK,OAAO,oBAAoB,QAAQ,WAAW,IAAI,MAAM,OAAO,QAAQ,CAAC,CAAC;AAAA,UAChF;AAAA,QACF,OAAO;AACL,eAAK,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,QACjE;AAAA,MACF,UAAE;AACA,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,YAAQ,aAAa;AAAA,EACvB;AAEA,WAAS,mBAAmB,YAAoB,KAA8B;AAC5E,QAAI,UAAU,SAAS,IAAI,UAAU;AACrC,QAAI,CAAC,SAAS;AAEZ,UAAI;AACJ,UAAI,OAAO;AACT,cAAM,YAAY,MAAM,KAAK;AAC7B,cAAM,QAAQ,UAAU,IAAI,UAAU;AACtC,YAAI,OAAO,WAAW;AACpB,8BAAoB,MAAM;AAAA,QAC5B;AAAA,MACF;AAEA,gBAAU;AAAA,QACR,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA,cAAc,KAAK,IAAI;AAAA,QACvB,YAAY;AAAA,QACZ,OAAO,CAAC;AAAA,QACR,WAAW;AAAA,MACb;AACA,eAAS,IAAI,YAAY,OAAO;AAChC,qBAAe,OAAO;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AAGA,MAAI,OAAO;AACT,UAAM,YAAY,MAAM,KAAK;AAC7B,eAAW,CAAC,KAAK,KAAK,KAAK,WAAW;AACpC,eAAS,IAAI,KAAK;AAAA,QAChB,WAAW,MAAM;AAAA,QACjB,YAAY,MAAM;AAAA,QAClB,KAAK,MAAM;AAAA,QACX,cAAc,MAAM;AAAA,QACpB,YAAY;AAAA,QACZ,OAAO,CAAC;AAAA,QACR,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AACA,eAAW,WAAW,SAAS,OAAO,GAAG;AACvC,qBAAe,OAAO;AAAA,IACxB;AACA,QAAI,UAAU,OAAO,GAAG;AACtB,cAAQ,IAAI,YAAY,UAAU,IAAI,uBAAuB;AAAA,IAC/D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK,YAAoB,KAAa,QAAuC;AAC3E,YAAM,UAAU,mBAAmB,YAAY,GAAG;AAClD,aAAO,IAAI,QAAsB,CAACA,UAAS,WAAW;AACpD,gBAAQ,MAAM,KAAK,EAAE,QAAQ,SAAAA,UAAS,OAAO,CAAC;AAC9C,qBAAa,OAAO;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,IAEA,WAAW,YAA6C;AACtD,YAAM,UAAU,SAAS,IAAI,UAAU;AACvC,UAAI,CAAC,QAAS,QAAO;AACrB,aAAO;AAAA,QACL,WAAW,QAAQ,aAAa;AAAA,QAChC,YAAY,QAAQ;AAAA,QACpB,cAAc,QAAQ;AAAA,QACtB,aAAa,QAAQ,MAAM;AAAA,MAC7B;AAAA,IACF;AAAA,IAEA,eAA8B;AAC5B,aAAO,MAAM,KAAK,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO;AAAA,QAC/C,WAAW,EAAE,aAAa;AAAA,QAC1B,YAAY,EAAE;AAAA,QACd,cAAc,EAAE;AAAA,QAChB,aAAa,EAAE,MAAM;AAAA,MACvB,EAAE;AAAA,IACJ;AAAA,IAEA,aAAa,YAA6B;AACxC,YAAM,UAAU,SAAS,IAAI,UAAU;AACvC,UAAI,CAAC,QAAS,QAAO;AACrB,UAAI,QAAQ,UAAW,cAAa,QAAQ,SAAS;AACrD,eAAS,OAAO,UAAU;AAC1B,sBAAgB;AAChB,aAAO;AAAA,IACT;AAAA,IAEA,WAAW;AACT,sBAAgB;AAChB,iBAAW,WAAW,SAAS,OAAO,GAAG;AACvC,YAAI,QAAQ,UAAW,cAAa,QAAQ,SAAS;AAAA,MACvD;AACA,eAAS,MAAM;AAAA,IACjB;AAAA,EACF;AACF;;;AClOA,SAAS,cAAc,eAAe,iBAAiB;AACvD,SAAS,eAAe;AAcjB,SAAS,uBAAuB,UAAgC;AACrE,SAAO;AAAA,IACL,OAAsC;AACpC,UAAI;AACF,cAAM,MAAM,aAAa,UAAU,OAAO;AAC1C,cAAM,UAA8B,KAAK,MAAM,GAAG;AAClD,cAAM,MAAM,oBAAI,IAA8B;AAC9C,mBAAW,SAAS,SAAS;AAC3B,cAAI,MAAM,aAAa,MAAM,YAAY;AACvC,gBAAI,IAAI,MAAM,YAAY,KAAK;AAAA,UACjC;AAAA,QACF;AACA,eAAO;AAAA,MACT,QAAQ;AACN,eAAO,oBAAI,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,IAEA,KAAK,UAA+C;AAClD,YAAM,UAAU,MAAM,KAAK,SAAS,OAAO,CAAC;AAC5C,UAAI;AACF,kBAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,sBAAc,UAAU,KAAK,UAAU,SAAS,MAAM,CAAC,IAAI,IAAI;AAAA,MACjE,SAAS,KAAK;AACZ,gBAAQ,MAAM,+BAA+B,GAAG;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AACF;;;AC3CA,SAAS,QAAQ,mBAAmB,cAA4B;AAKzD,SAAS,aAAa,MAAc,OAAyB;AAClE,MAAI,KAAK,UAAU,MAAO,QAAO,CAAC,IAAI;AAEtC,QAAM,SAAmB,CAAC;AAC1B,QAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,MAAI,UAAU;AAEd,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,OAAO;AACvB,UAAI,SAAS;AACX,eAAO,KAAK,OAAO;AACnB,kBAAU;AAAA,MACZ;AACA,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,OAAO;AAC3C,eAAO,KAAK,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC;AAAA,MACtC;AACA;AAAA,IACF;AAEA,UAAM,YAAY,UAAU,GAAG,OAAO;AAAA,EAAK,IAAI,KAAK;AACpD,QAAI,UAAU,SAAS,OAAO;AAC5B,aAAO,KAAK,OAAO;AACnB,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,WAAW,OAAO,WAAW,GAAG;AAClC,WAAO,KAAK,OAAO;AAAA,EACrB;AAEA,SAAO;AACT;AAOA,SAAS,mBAAmB,QAAuB,WAA2B;AAC5E,SAAO,OAAO,SAAS,SAAS,GAAG,QAAQ;AAC7C;AAEA,SAAS,kBAAkB,QAAuB,MAA0D;AAC1G,QAAM,QAAQ,KAAK,YAAY;AAC/B,aAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAClE,QAAI,QAAQ,KAAK,YAAY,MAAM,OAAO;AACxC,aAAO,EAAE,WAAW,MAAM,QAAQ,KAAK;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,WAA2B;AAClD,QAAM,UAAU,KAAK,OAAO,KAAK,IAAI,IAAI,aAAa,GAAI;AAC1D,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AACrC,SAAO,GAAG,KAAK,KAAK,UAAU,EAAE;AAClC;AAEO,SAAS,cACdC,UACA,QACA,gBACe;AACf,QAAM,QAAQA,SAAQ,KAAK,EAAE,MAAM,KAAK;AACxC,QAAM,MAAM,MAAM,CAAC,GAAG,YAAY;AAElC,MAAI,QAAQ,aAAa;AACvB,UAAM,cAAc,eAAe,aAAa;AAChD,QAAI,YAAY,WAAW,GAAG;AAC5B,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,YAAY,IAAI,CAAC,MAAM;AACnC,YAAM,OAAO,mBAAmB,QAAQ,EAAE,UAAU;AACpD,YAAM,OAAO,gBAAgB,EAAE,YAAY;AAC3C,YAAM,QAAQ,EAAE,cAAc,IAAI,aAAa,EAAE,WAAW,KAAK;AACjE,YAAM,MAAM,EAAE,YAAY,QAAQ,EAAE,UAAU,MAAM,GAAG,CAAC,CAAC,aAAQ;AACjE,aAAO,OAAO,IAAI,yBAAoB,IAAI,GAAG,KAAK,GAAG,GAAG;AAAA,IAC1D,CAAC;AACD,WAAO,sBAAsB,YAAY,MAAM;AAAA,EAAQ,MAAM,KAAK,IAAI,CAAC;AAAA,EACzE;AAEA,MAAI,QAAQ,YAAY;AACtB,UAAM,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AACpC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,UAAU,kBAAkB,QAAQ,IAAI;AAC9C,QAAI,CAAC,QAAS,QAAO,8BAA8B,IAAI;AACvD,UAAM,OAAO,eAAe,WAAW,QAAQ,SAAS;AACxD,QAAI,CAAC,KAAM,QAAO,KAAK,QAAQ,IAAI;AACnC,UAAM,OAAO,gBAAgB,KAAK,YAAY;AAC9C,UAAM,MAAM,KAAK,aAAa;AAC9B,WAAO;AAAA,MACL,KAAK,QAAQ,IAAI;AAAA,MACjB,iBAAiB,GAAG;AAAA,MACpB,gBAAgB,IAAI;AAAA,MACpB,gBAAgB,KAAK,WAAW;AAAA,IAClC,EAAE,KAAK,IAAI;AAAA,EACb;AAEA,MAAI,QAAQ,SAAS;AACnB,UAAM,OAAO,MAAM,MAAM,CAAC,EAAE,KAAK,GAAG;AACpC,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,UAAU,kBAAkB,QAAQ,IAAI;AAC9C,QAAI,CAAC,QAAS,QAAO,8BAA8B,IAAI;AACvD,UAAM,UAAU,eAAe,aAAa,QAAQ,SAAS;AAC7D,QAAI,QAAS,QAAO,iBAAiB,QAAQ,IAAI;AACjD,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AAEA,MAAI,QAAQ,SAAS;AACnB,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AAEA,SAAO;AACT;AAEO,SAAS,iBAAiB,QAAgB,gBAAgC,QAAmC;AAClH,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,SAAS;AAAA,MACP,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,MAClB,kBAAkB;AAAA,IACpB;AAAA,EACF,CAAC;AAED,SAAO,GAAG,OAAO,eAAe,OAAO,YAAqB;AAC1D,QAAI,QAAQ,OAAO,IAAK;AAExB,QAAI,EAAE,UAAU,QAAQ,SAAU;AAGlC,QAAI,QAAQ,QAAQ,WAAW,GAAG,GAAG;AACnC,YAAMC,YAAW,QAAQ,QAAQ,SAAS,IAAI,QAAQ,QAAQ,YAAY,SAAY;AACtF,YAAMC,YAAW,OAAO,QAAQ,QAAQ,WAAWD,SAAQ;AAC3D,UAAIC,WAAU;AACZ,cAAM,WAAW,cAAc,QAAQ,SAAS,QAAQ,cAAc;AACtE,YAAI,UAAU;AACZ,gBAAM,QAAQ,QAAQ,KAAK,QAAQ;AACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ,QAAQ,SAAS,IAAI,QAAQ,QAAQ,YAAY,SAAY;AACtF,UAAM,WAAW,OAAO,QAAQ,QAAQ,WAAW,QAAQ;AAC3D,QAAI,CAAC,SAAU;AAEf,QAAI;AACF,YAAM,QAAQ,MAAM,WAAI;AAAA,IAC1B,QAAQ;AAAA,IAER;AAEA,QAAI;AACF,YAAM,SAAS,MAAM,eAAe;AAAA,QAClC,SAAS;AAAA,QACT,SAAS;AAAA,QACT,QAAQ;AAAA,MACV;AAEA,YAAM,SAAS,aAAa,OAAO,MAAM,GAAI;AAC7C,iBAAW,SAAS,QAAQ;AAC1B,cAAM,QAAQ,QAAQ,KAAK,KAAK;AAAA,MAClC;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,WAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAChE,YAAM,QAAQ,QAAQ;AAAA,QACpB,cAAc,SAAS,IAAI,MAAM,SAAS,MAAM,GAAG,IAAI,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL,MAAM,MAAM,OAAe;AACzB,YAAM,OAAO,MAAM,KAAK;AACxB,cAAQ,IAAI,wBAAwB,OAAO,MAAM,GAAG,EAAE;AAAA,IACxD;AAAA,IACA,OAAO;AACL,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;;;ACpMA,SAAS,uBAAuB;AAChC,SAAS,iBAAAC,gBAAe,kBAAkB;AAC1C,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAEzB,SAAS,eAAsD;AAC7D,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,SAAO,CAAC,aACN,IAAI,QAAQ,CAACC,aAAY,GAAG,SAAS,UAAU,CAAC,WAAWA,SAAQ,OAAO,KAAK,CAAC,CAAC,CAAC;AACtF;AAEA,eAAsB,UAAU;AAC9B,QAAM,MAAM,aAAa;AACzB,QAAM,YAAY,QAAQ,IAAI;AAE9B,UAAQ,IAAI,kDAA6C;AAGzD,MAAI;AACF,aAAS,oBAAoB,EAAE,OAAO,OAAO,CAAC;AAC9C,YAAQ,IAAI,mBAAmB;AAAA,EACjC,QAAQ;AACN,YAAQ,KAAK,6FAA6F;AAAA,EAC5G;AAGA,MAAI,QAAQ,QAAQ,IAAI,qBAAqB;AAC7C,QAAM,aAAa,MAAM,IAAI,oBAAoB,QAAQ,oCAAoC,EAAE,IAAI;AACnG,MAAI,WAAY,SAAQ;AACxB,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,4FAA4F;AAC1G,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,UAAU,QAAQ,WAAW,MAAM;AACzC,EAAAD,eAAc,SAAS,qBAAqB,KAAK;AAAA,CAAI;AACrD,UAAQ,IAAI,SAAS,OAAO,EAAE;AAS9B,QAAM,WAA2B,CAAC;AAGlC,QAAM,aAAa,QAAQ,WAAW,aAAa;AACnD,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,YAAM,WAAW,KAAK;AAAA,SACnB,MAAM,OAAO,IAAS,GAAG,aAAa,YAAY,OAAO;AAAA,MAC5D;AACA,UAAI,SAAS,UAAU;AACrB,mBAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,SAAS,QAAQ,GAAG;AACpE,gBAAM,IAAI;AACV,mBAAS,KAAK,EAAE,MAAM,EAAE,QAAQ,WAAW,WAAW,EAAE,WAAW,UAAU,CAAC;AAAA,QAChF;AACA,YAAI,SAAS,SAAS,GAAG;AACvB,kBAAQ,IAAI;AAAA,qBAAwB,SAAS,MAAM,IAAI;AACvD,qBAAW,KAAK,UAAU;AACxB,oBAAQ,IAAI,KAAK,EAAE,IAAI,WAAM,EAAE,SAAS,KAAK,EAAE,SAAS,GAAG;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,UAAQ,IAAI,0CAA0C;AAEtD,SAAO,MAAM;AACX,UAAM,OAAO,MAAM,IAAI,gBAAgB;AACvC,QAAI,CAAC,KAAM;AAEX,UAAM,YAAY,MAAM,IAAI,qCAAqC;AACjE,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,kCAAkC;AAC9C;AAAA,IACF;AACA,QAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,cAAQ,KAAK,YAAY,SAAS,kBAAkB;AAAA,IACtD;AAEA,UAAM,YAAY,MAAM,IAAI,sBAAsB;AAClD,QAAI,CAAC,WAAW;AACd,cAAQ,IAAI,mCAAmC;AAC/C;AAAA,IACF;AAEA,aAAS,KAAK,EAAE,MAAM,WAAW,UAAU,CAAC;AAC5C,YAAQ,IAAI,SAAS,IAAI;AAAA,CAAI;AAAA,EAC/B;AAEA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,yDAAyD;AAAA,EACvE;AAGA,QAAM,SAAS;AAAA,IACb,UAAU;AAAA,MACR,eAAe;AAAA,MACf,uBAAuB;AAAA,MACvB,YAAY,CAAC,qBAAqB,eAAe,mBAAmB,MAAM;AAAA,IAC5E;AAAA,IACA,UAAU,OAAO;AAAA,MACf,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,WAAW,EAAE,UAAU,CAAC,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,EAAAA,eAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,IAAI;AAChE,UAAQ,IAAI,SAAS,UAAU,EAAE;AAEjC,UAAQ,IAAI,0DAA0D;AACxE;;;AP3GA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UAAU,KAAK,CAAC,KAAK;AAE3B,eAAe,OAAO;AACpB,UAAQ,SAAS;AAAA,IACf,KAAK;AACH,aAAO,MAAM;AAAA,IACf,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB,KAAK;AACH,aAAO,OAAO;AAAA,IAChB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,KAAK;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AACH,aAAO,QAAQ;AAAA,IACjB;AACE,cAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,WAAK;AACL,cAAQ,KAAK,CAAC;AAAA,EAClB;AACF;AAEA,SAAS,OAAO;AACd,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcZ,KAAK,CAAC;AACR;AAEA,SAAS,UAAU;AACjB,QAAM,MAAM,KAAK,MAAME,cAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,OAAO,CAAC;AACzF,UAAQ,IAAI,QAAQ,IAAI,OAAO,EAAE;AACnC;AAEA,SAAS,QAAQ;AACf,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,UAAUC,SAAQ,WAAW,MAAM;AACzC,MAAIC,YAAW,OAAO,GAAG;AACvB,YAAQ,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC3B;AAEA,QAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,gEAAgE;AAC9E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,aAAaD,SAAQ,WAAW,aAAa;AACnD,MAAI,CAACC,YAAW,UAAU,GAAG;AAC3B,YAAQ,MAAM,sDAAsD;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,YAAY,KAAK,MAAMF,cAAa,YAAY,OAAO,CAAC;AAC9D,QAAM,SAAS,WAAW,SAAS;AAEnC,QAAM,eAAe,OAAO,KAAK,OAAO,QAAQ,EAAE;AAClD,MAAI,iBAAiB,GAAG;AACtB,YAAQ,MAAM,uCAAuC;AACrD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,aAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAClE,QAAI,CAACE,YAAW,QAAQ,SAAS,GAAG;AAClC,cAAQ,KAAK,oCAAoC,QAAQ,IAAI,KAAK,QAAQ,SAAS,EAAE;AAAA,IACvF;AAAA,EACF;AAEA,UAAQ,IAAI,UAAU,YAAY,yBAAyB;AAE3D,QAAM,SAAS,aAAa,MAAM;AAClC,QAAM,eAAe,uBAAuBD,SAAQ,WAAW,gBAAgB,CAAC;AAChF,QAAM,iBAAiB,qBAAqB,OAAO,UAAU,YAAY;AACzE,QAAM,MAAM,iBAAiB,QAAQ,gBAAgB,MAAM;AAE3D,WAAS,WAAW;AAClB,YAAQ,IAAI,kBAAkB;AAC9B,mBAAe,SAAS;AACxB,QAAI,KAAK;AACT,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,QAAQ;AAC7B,UAAQ,GAAG,WAAW,QAAQ;AAE9B,MAAI,MAAM,KAAK,EAAE,MAAM,CAAC,QAAQ;AAC9B,YAAQ,MAAM,wBAAwB,GAAG;AACzC,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACH;AAEA,SAAS,SAAS;AAChB,QAAM,YAAY,QAAQ,IAAI;AAC9B,QAAM,eAAeA,SAAQ,WAAW,gBAAgB;AAExD,MAAI,CAACC,YAAW,YAAY,GAAG;AAC7B,YAAQ,IAAI,iDAAiD;AAC7D;AAAA,EACF;AAEA,QAAM,aAAaD,SAAQ,WAAW,aAAa;AACnD,MAAI,eAAuC,CAAC;AAC5C,MAAIC,YAAW,UAAU,GAAG;AAC1B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMF,cAAa,YAAY,OAAO,CAAC;AACxD,YAAM,SAAS,WAAW,GAAG;AAC7B,iBAAW,CAAC,WAAW,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAClE,qBAAa,SAAS,IAAI,QAAQ;AAAA,MACpC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,WAAW,KAAK,MAAMA,cAAa,cAAc,OAAO,CAAC;AAO/D,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,qBAAqB;AACjC;AAAA,EACF;AAEA,UAAQ,IAAI,aAAa,SAAS,MAAM;AAAA,CAAM;AAC9C,aAAW,KAAK,UAAU;AACxB,UAAM,OAAO,aAAa,EAAE,UAAU,KAAK,EAAE;AAC7C,UAAM,MAAM,KAAK,OAAO,KAAK,IAAI,IAAI,EAAE,gBAAgB,GAAK;AAC5D,YAAQ,IAAI,KAAK,IAAI,EAAE;AACvB,YAAQ,IAAI,gBAAgB,EAAE,SAAS,EAAE;AACzC,YAAQ,IAAI,gBAAgB,EAAE,GAAG,EAAE;AACnC,YAAQ,IAAI,gBAAgB,GAAG,GAAG;AAClC,YAAQ,IAAI;AAAA,EACd;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["resolve","existsSync","readFileSync","args","resolve","resolve","command","parentId","resolved","writeFileSync","resolve","readFileSync","resolve","existsSync"]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/persona-presets.ts","../src/config.ts","../src/router.ts","../src/claude-cli.ts","../src/worktree.ts","../src/session-manager.ts","../src/session-store.ts","../src/discord.ts","../src/agent-dispatch.ts","../src/embed-format.ts","../src/health-server.ts","../src/turn-counter.ts","../src/init.ts","../src/resolve-home.ts","../src/health.ts"],"sourcesContent":["import { resolve, dirname } from 'node:path';\nimport { existsSync, readFileSync, copyFileSync, mkdirSync } from 'node:fs';\nimport { config as loadEnv } from 'dotenv';\nimport { loadConfig } from './config.js';\nimport { createRouter } from './router.js';\nimport { createSessionManager } from './session-manager.js';\nimport { createFileSessionStore } from './session-store.js';\nimport { createDiscordBot } from './discord.js';\nimport { createHealthServer, type HealthServer } from './health-server.js';\nimport { createTurnCounter } from './turn-counter.js';\nimport { runInit } from './init.js';\nimport { runHealthChecks } from './health.js';\nimport { reconcileWorktrees } from './worktree.js';\nimport {\n resolveEnvPath,\n resolveConfigPath,\n resolveSessionsPath,\n resolveMpgHome,\n resolveProfileDir,\n parseFlags,\n} from './resolve-home.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0] ?? 'start';\nconst flags = parseFlags(args.slice(1));\n\nasync function main() {\n switch (command) {\n case 'start':\n return start();\n case 'init':\n if (flags.migrate) {\n return migrate();\n }\n return runInit(flags.profileFlag);\n case 'status':\n return status();\n case 'help':\n case '--help':\n case '-h':\n return help();\n case '--version':\n case '-v':\n return version();\n default:\n console.error(`Unknown command: ${command}`);\n help();\n process.exit(1);\n }\n}\n\nfunction help() {\n console.log(`\nmpg — multi-project gateway for Claude Code\n\nUsage: mpg <command>\n\nCommands:\n start Start the gateway (default)\n init Interactive setup wizard\n status Show session status\n help Show this message\n\nOptions:\n --profile <name> Use a named profile (default: \"default\")\n --config <path> Use a specific config.json path\n --migrate Copy CWD config files into ~/.mpg/profiles/default/\n -v, --version Show version\n -h, --help Show this message\n\nEnvironment:\n MPG_HOME Override config home (default: ~/.mpg)\n`.trim());\n}\n\nfunction version() {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));\n console.log(`mpg v${pkg.version}`);\n}\n\nfunction start() {\n // Load .env using resolution order\n const envPath = resolveEnvPath();\n if (envPath) {\n loadEnv({ path: envPath });\n }\n\n const token = process.env.DISCORD_BOT_TOKEN;\n if (!token) {\n console.error('DISCORD_BOT_TOKEN is not set. Run `mpg init` or set it in .env');\n process.exit(1);\n }\n\n const configPath = resolveConfigPath({\n configFlag: flags.configFlag,\n profileFlag: flags.profileFlag,\n });\n if (!configPath || !existsSync(configPath)) {\n console.error('config.json not found. Run `mpg init` to create one.');\n process.exit(1);\n }\n\n const rawConfig = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(rawConfig);\n\n const projectCount = Object.keys(config.projects).length;\n if (projectCount === 0) {\n console.error('No projects configured in config.json');\n process.exit(1);\n }\n\n runHealthChecks(config);\n\n console.log(`Loaded ${projectCount} project(s) from ${configPath}`);\n\n const router = createRouter(config);\n const sessionsPath = resolveSessionsPath(configPath);\n const sessionStore = createFileSessionStore(sessionsPath);\n const sessionManager = createSessionManager(config.defaults, sessionStore);\n\n // Reconcile orphaned worktrees from crashed sessions\n const persistedSessions = sessionStore.load();\n const knownKeysByProject = new Map<string, Set<string>>();\n for (const [key, entry] of persistedSessions) {\n if (entry.projectDir) {\n let keys = knownKeysByProject.get(entry.projectDir);\n if (!keys) {\n keys = new Set();\n knownKeysByProject.set(entry.projectDir, keys);\n }\n keys.add(key);\n }\n }\n for (const [projectDir, keys] of knownKeysByProject) {\n reconcileWorktrees(projectDir, keys);\n }\n\n const turnCounter = createTurnCounter();\n const bot = createDiscordBot(router, sessionManager, config, turnCounter);\n\n let healthServer: HealthServer | undefined;\n\n function shutdown() {\n console.log('Shutting down...');\n if (healthServer) {\n healthServer.close().catch(() => {});\n }\n sessionManager.shutdown();\n bot.stop();\n process.exit(0);\n }\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n bot.start(token)\n .then(async () => {\n if (config.defaults.httpPort !== false) {\n try {\n healthServer = await createHealthServer(config.defaults.httpPort, sessionManager, bot);\n } catch (err) {\n console.warn(`Health server failed to start on port ${config.defaults.httpPort}:`, err);\n }\n }\n })\n .catch((err) => {\n console.error('Failed to start bot:', err);\n process.exit(1);\n });\n}\n\nfunction status() {\n const configPath = resolveConfigPath({\n configFlag: flags.configFlag,\n profileFlag: flags.profileFlag,\n });\n\n const sessionsPath = configPath\n ? resolveSessionsPath(configPath)\n : resolve(process.cwd(), '.sessions.json');\n\n if (!existsSync(sessionsPath)) {\n console.log('No sessions file found. Is the gateway running?');\n return;\n }\n\n let projectNames: Record<string, string> = {};\n if (configPath && existsSync(configPath)) {\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(raw);\n for (const [channelId, project] of Object.entries(config.projects)) {\n projectNames[channelId] = project.name;\n }\n } catch {\n // ignore config errors for status\n }\n }\n\n const sessions = JSON.parse(readFileSync(sessionsPath, 'utf-8')) as Array<{\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n }>;\n\n if (sessions.length === 0) {\n console.log('No active sessions.');\n return;\n }\n\n console.log(`Sessions (${sessions.length}):\\n`);\n for (const s of sessions) {\n const name = projectNames[s.projectKey] ?? s.projectKey;\n const ago = Math.floor((Date.now() - s.lastActivity) / 60000);\n console.log(` ${name}`);\n console.log(` Session: ${s.sessionId}`);\n console.log(` Dir: ${s.cwd}`);\n console.log(` Idle: ${ago}m`);\n console.log();\n }\n}\n\nfunction migrate() {\n const mpgHome = resolveMpgHome();\n const profileDir = resolveProfileDir('default');\n\n const cwdEnv = resolve(process.cwd(), '.env');\n const cwdConfig = resolve(process.cwd(), 'config.json');\n const cwdSessions = resolve(process.cwd(), '.sessions.json');\n\n const copied: string[] = [];\n\n // Create directories\n mkdirSync(profileDir, { recursive: true });\n\n // Copy .env to MPG_HOME root\n if (existsSync(cwdEnv)) {\n const dest = resolve(mpgHome, '.env');\n copyFileSync(cwdEnv, dest);\n copied.push(` ${cwdEnv} → ${dest}`);\n }\n\n // Copy config.json to profile dir\n if (existsSync(cwdConfig)) {\n const dest = resolve(profileDir, 'config.json');\n copyFileSync(cwdConfig, dest);\n copied.push(` ${cwdConfig} → ${dest}`);\n }\n\n // Copy sessions.json to profile dir\n if (existsSync(cwdSessions)) {\n const dest = resolve(profileDir, 'sessions.json');\n copyFileSync(cwdSessions, dest);\n copied.push(` ${cwdSessions} → ${dest}`);\n }\n\n if (copied.length === 0) {\n console.log('No config files found in current directory. Nothing to migrate.');\n return;\n }\n\n console.log(`Migrated files to ${mpgHome}:\\n`);\n for (const line of copied) {\n console.log(line);\n }\n console.log(`\\nProfile directory: ${profileDir}`);\n console.log('You can now run `mpg start` from any directory.');\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","import type { AgentConfig } from './config.js';\n\nexport const PERSONA_PRESETS: Record<string, AgentConfig> = {\n pm: {\n role: 'Product Manager',\n prompt: [\n 'You are a Product Manager.',\n 'Your responsibilities:',\n '- Clarify requirements and acceptance criteria before handing work to engineers.',\n '- Break down features into concrete, actionable tasks.',\n '- Prioritize work based on user impact and feasibility.',\n '- Ask clarifying questions when requirements are ambiguous.',\n '- Summarize decisions and next steps clearly.',\n '',\n 'Communication style: concise, structured, and action-oriented.',\n 'When a task is ready for implementation, mention the appropriate engineer agent.',\n ].join('\\n'),\n },\n\n engineer: {\n role: 'Software Engineer',\n prompt: [\n 'You are a Software Engineer.',\n 'Your responsibilities:',\n '- Write clean, well-tested code that meets the requirements.',\n '- Follow existing project conventions and patterns.',\n '- Consider edge cases, error handling, and performance.',\n '- Explain technical trade-offs when relevant.',\n '- Ask for clarification when requirements are unclear rather than guessing.',\n '',\n 'Communication style: precise and technical, but accessible to non-engineers.',\n ].join('\\n'),\n },\n\n qa: {\n role: 'QA Engineer',\n prompt: [\n 'You are a QA Engineer.',\n 'Your responsibilities:',\n '- Review code and features for correctness, edge cases, and regressions.',\n '- Write and suggest test cases covering happy paths and failure modes.',\n '- Verify that acceptance criteria from the PM are met.',\n '- Report issues clearly with steps to reproduce.',\n '- Think adversarially — try to break things.',\n '',\n 'Communication style: thorough, detail-oriented, and evidence-based.',\n ].join('\\n'),\n },\n\n designer: {\n role: 'Designer',\n prompt: [\n 'You are a Designer.',\n 'Your responsibilities:',\n '- Propose UI/UX solutions that are intuitive and consistent.',\n '- Consider accessibility, responsiveness, and user flows.',\n '- Provide clear specifications for engineers to implement.',\n '- Challenge assumptions about user needs when appropriate.',\n '',\n 'Communication style: visual-thinking, user-centric, and practical.',\n ].join('\\n'),\n },\n\n devops: {\n role: 'DevOps Engineer',\n prompt: [\n 'You are a DevOps Engineer.',\n 'Your responsibilities:',\n '- Manage infrastructure, CI/CD pipelines, and deployment processes.',\n '- Ensure reliability, monitoring, and observability.',\n '- Advise on architecture decisions that affect operability.',\n '- Automate repetitive operational tasks.',\n '',\n 'Communication style: systematic, risk-aware, and automation-focused.',\n ].join('\\n'),\n },\n};\n\nexport function resolvePreset(presetName: string): AgentConfig | undefined {\n return PERSONA_PRESETS[presetName.toLowerCase()];\n}\n","import { resolvePreset } from './persona-presets.js';\n\nexport interface AgentConfig {\n role: string;\n prompt: string;\n}\n\nexport interface AgentInputConfig {\n preset?: string;\n role?: string;\n prompt?: string;\n}\n\nexport interface ProjectConfig {\n name: string;\n directory: string;\n idleTimeoutMs?: number;\n claudeArgs?: string[];\n agents?: Record<string, AgentConfig>;\n}\n\nexport interface GatewayDefaults {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n sessionTtlMs: number;\n maxPersistedSessions: number;\n claudeArgs: string[];\n maxTurnsPerAgent: number;\n agentTimeoutMs: number;\n httpPort: number | false;\n}\n\nexport interface GatewayConfig {\n defaults: GatewayDefaults;\n projects: Record<string, ProjectConfig>;\n}\n\nexport function loadConfig(raw: unknown): GatewayConfig {\n if (!raw || typeof raw !== 'object') {\n throw new Error('Config must be an object');\n }\n\n const obj = raw as Record<string, unknown>;\n\n if (!obj.projects || typeof obj.projects !== 'object') {\n throw new Error('Config must have a \"projects\" object');\n }\n\n const projects = obj.projects as Record<string, unknown>;\n const validated: Record<string, ProjectConfig> = {};\n\n for (const [channelId, project] of Object.entries(projects)) {\n if (!project || typeof project !== 'object') {\n throw new Error(`Project for channel ${channelId} must be an object`);\n }\n const p = project as Record<string, unknown>;\n if (typeof p.directory !== 'string' || !p.directory) {\n throw new Error(`Project for channel ${channelId} must have a \"directory\" string`);\n }\n let agents: Record<string, AgentConfig> | undefined;\n if (Array.isArray(p.agents)) {\n // Shorthand: [\"pm\", \"engineer\"] — resolve each as a preset\n agents = {};\n for (const entry of p.agents) {\n if (typeof entry === 'string') {\n const name = entry.toLowerCase();\n const preset = resolvePreset(name);\n if (preset) {\n agents[name] = { ...preset };\n }\n }\n }\n if (Object.keys(agents).length === 0) agents = undefined;\n } else if (p.agents && typeof p.agents === 'object') {\n agents = {};\n for (const [agentName, agentCfg] of Object.entries(p.agents as Record<string, unknown>)) {\n const ac = agentCfg as Record<string, unknown>;\n const name = agentName.toLowerCase();\n\n if (typeof ac.preset === 'string') {\n // Preset-based: resolve preset, then merge overrides\n const preset = resolvePreset(ac.preset);\n if (preset) {\n const role = typeof ac.role === 'string' ? ac.role : preset.role;\n const basePrompt = preset.prompt;\n const extra = typeof ac.prompt === 'string' ? ac.prompt : '';\n const prompt = extra ? `${basePrompt}\\n\\n${extra}` : basePrompt;\n agents[name] = { role, prompt };\n }\n } else if (typeof ac.role === 'string' && typeof ac.prompt === 'string') {\n // Inline: original behavior\n agents[name] = { role: ac.role, prompt: ac.prompt };\n }\n }\n if (Object.keys(agents).length === 0) agents = undefined;\n }\n\n validated[channelId] = {\n name: typeof p.name === 'string' ? p.name : channelId,\n directory: p.directory,\n ...(p.idleTimeoutMs !== undefined && { idleTimeoutMs: Number(p.idleTimeoutMs) }),\n ...(Array.isArray(p.claudeArgs) && { claudeArgs: p.claudeArgs as string[] }),\n ...(agents && { agents }),\n };\n }\n\n const defaults = (obj.defaults ?? {}) as Record<string, unknown>;\n\n return {\n defaults: {\n idleTimeoutMs: typeof defaults.idleTimeoutMs === 'number' ? defaults.idleTimeoutMs : 1800000,\n maxConcurrentSessions: typeof defaults.maxConcurrentSessions === 'number' ? defaults.maxConcurrentSessions : 4,\n sessionTtlMs: typeof defaults.sessionTtlMs === 'number' ? defaults.sessionTtlMs : 7 * 24 * 60 * 60 * 1000,\n maxPersistedSessions: typeof defaults.maxPersistedSessions === 'number' ? defaults.maxPersistedSessions : 50,\n claudeArgs: Array.isArray(defaults.claudeArgs) ? (defaults.claudeArgs as string[]) : ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n maxTurnsPerAgent: typeof defaults.maxTurnsPerAgent === 'number' ? defaults.maxTurnsPerAgent : 5,\n agentTimeoutMs: typeof defaults.agentTimeoutMs === 'number' ? defaults.agentTimeoutMs : 3 * 60 * 1000,\n httpPort: defaults.httpPort === false ? false : (typeof defaults.httpPort === 'number' ? defaults.httpPort : 3100),\n },\n projects: validated,\n };\n}\n","import type { GatewayConfig, ProjectConfig } from './config.js';\n\nexport interface ResolvedProject {\n channelId: string;\n name: string;\n directory: string;\n isThread: boolean;\n}\n\nexport interface Router {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null;\n}\n\nexport function createRouter(config: GatewayConfig): Router {\n return {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null {\n const project = config.projects[channelId];\n if (project) {\n return { channelId, name: project.name, directory: project.directory, isThread: false };\n }\n\n if (parentChannelId) {\n const parentProject = config.projects[parentChannelId];\n if (parentProject) {\n return { channelId, name: parentProject.name, directory: parentProject.directory, isThread: true };\n }\n }\n\n return null;\n },\n };\n}\n","import { spawn } from 'node:child_process';\n\nexport interface ClaudeResult {\n text: string;\n sessionId: string;\n isError: boolean;\n sessionReset?: boolean;\n sessionChanged?: boolean;\n}\n\nexport function parseClaudeJsonOutput(raw: string): ClaudeResult {\n const data = JSON.parse(raw);\n return {\n text: data.result ?? '',\n sessionId: data.session_id ?? '',\n isError: Boolean(data.is_error),\n };\n}\n\nexport function buildClaudeArgs(\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n systemPrompt?: string,\n): string[] {\n const args = ['--print', ...baseArgs];\n if (sessionId) {\n args.push('--resume', sessionId);\n }\n if (systemPrompt) {\n args.push('--append-system-prompt', systemPrompt);\n }\n args.push(prompt);\n return args;\n}\n\nexport function friendlyError(stderr: string): string {\n const combined = stderr.toLowerCase();\n if (combined.includes('rate limit') || combined.includes('rate_limit_error')) {\n return 'Claude usage limit reached — please wait a few minutes and try again.';\n }\n if (combined.includes('overloaded') || combined.includes('overloaded_error')) {\n return 'Claude API is temporarily overloaded — please try again shortly.';\n }\n if (combined.includes('invalid api key') || combined.includes('authentication_error') || combined.includes('authentication failed')) {\n return 'Claude authentication failed — check your API key or CLI login.';\n }\n if (combined.includes('no messages returned')) {\n return 'Claude returned an empty response — try sending your message again.';\n }\n return `Claude error: ${stderr.slice(0, 500)}`;\n}\n\nconst DEFAULT_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes\n\nexport function runClaude(\n cwd: string,\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n systemPrompt?: string,\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<ClaudeResult> {\n return new Promise((resolve, reject) => {\n const args = buildClaudeArgs(baseArgs, prompt, sessionId, systemPrompt);\n const proc = spawn('claude', args, {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n let settled = false;\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n proc.kill('SIGTERM');\n reject(new Error(`Claude CLI timed out after ${timeoutMs / 1000}s`));\n }\n }, timeoutMs);\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on('close', (code) => {\n clearTimeout(timer);\n if (settled) return;\n settled = true;\n if (code !== 0) {\n reject(new Error(friendlyError(stderr)));\n return;\n }\n try {\n const result = parseClaudeJsonOutput(stdout.trim());\n resolve(result);\n } catch (err) {\n reject(new Error(`Failed to parse claude output: ${stdout.slice(0, 200)}`));\n }\n });\n\n proc.on('error', (err) => {\n clearTimeout(timer);\n if (settled) return;\n settled = true;\n reject(new Error(`Failed to spawn claude: ${err.message}`));\n });\n });\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\n\nconst WORKTREE_DIR = '.worktrees';\nconst BRANCH_PREFIX = 'mpg/';\nconst TIMEOUT = 10_000;\n\n/** Sanitize project key for use in filesystem paths and git branch names. */\nfunction sanitizeKey(key: string): string {\n return key.replace(/:/g, '-');\n}\n\nexport function worktreePath(projectDir: string, projectKey: string): string {\n return join(projectDir, WORKTREE_DIR, sanitizeKey(projectKey));\n}\n\nexport function createWorktree(projectDir: string, projectKey: string): string {\n const safeKey = sanitizeKey(projectKey);\n if (!/^[\\w-]+$/.test(safeKey)) {\n throw new Error(`Invalid projectKey for worktree: ${projectKey}`);\n }\n const wtPath = worktreePath(projectDir, projectKey);\n if (existsSync(wtPath)) return wtPath;\n const branch = `${BRANCH_PREFIX}${safeKey}`;\n try {\n execFileSync('git', ['worktree', 'add', '-b', branch, wtPath], {\n cwd: projectDir,\n timeout: TIMEOUT,\n });\n } catch (err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!msg.includes('already exists')) throw err;\n // Branch already exists (previous session) — attach without creating branch\n execFileSync('git', ['worktree', 'add', wtPath, branch], {\n cwd: projectDir,\n timeout: TIMEOUT,\n });\n }\n return wtPath;\n}\n\nexport function removeWorktree(projectDir: string, projectKey: string): void {\n const wtPath = worktreePath(projectDir, projectKey);\n try {\n execFileSync('git', ['worktree', 'remove', '--force', wtPath], {\n cwd: projectDir,\n timeout: TIMEOUT,\n });\n } catch {\n // Already removed or not a worktree — safe to ignore\n }\n}\n\nexport interface WorktreeInfo {\n path: string;\n branch: string;\n}\n\nexport function listWorktrees(projectDir: string): WorktreeInfo[] {\n try {\n const raw = execFileSync('git', ['worktree', 'list', '--porcelain'], {\n cwd: projectDir,\n timeout: TIMEOUT,\n }).toString();\n\n const entries: WorktreeInfo[] = [];\n let currentPath = '';\n let currentBranch = '';\n\n for (const line of raw.split('\\n')) {\n if (line.startsWith('worktree ')) {\n currentPath = line.slice('worktree '.length);\n } else if (line.startsWith('branch ')) {\n currentBranch = line.slice('branch '.length);\n } else if (line === '') {\n if (currentPath) {\n entries.push({ path: currentPath, branch: currentBranch });\n }\n currentPath = '';\n currentBranch = '';\n }\n }\n return entries;\n } catch {\n return [];\n }\n}\n\nexport function reconcileWorktrees(projectDir: string, knownKeys: Set<string>): void {\n const worktrees = listWorktrees(projectDir);\n const sanitizedKnown = new Set([...knownKeys].map(sanitizeKey));\n let removed = 0;\n for (const wt of worktrees) {\n if (!wt.branch.startsWith(`refs/heads/${BRANCH_PREFIX}`)) continue;\n const key = wt.branch.slice(`refs/heads/${BRANCH_PREFIX}`.length);\n if (!sanitizedKnown.has(key)) {\n removeWorktree(projectDir, key);\n removed++;\n }\n }\n if (removed > 0) {\n console.log(`Reconciled ${removed} orphaned worktree(s) in ${projectDir}`);\n }\n}\n","import { runClaude, type ClaudeResult } from './claude-cli.js';\nimport type { SessionStore, PersistedSession } from './session-store.js';\nimport { createWorktree as gitCreateWorktree, removeWorktree as gitRemoveWorktree } from './worktree.js';\n\nexport interface SessionInfo {\n sessionId: string;\n projectKey: string;\n lastActivity: number;\n queueLength: number;\n}\n\nexport interface SessionManager {\n send(projectKey: string, cwd: string, prompt: string, opts?: { worktree?: boolean; systemPrompt?: string; timeoutMs?: number }): Promise<ClaudeResult>;\n getSession(projectKey: string): SessionInfo | undefined;\n listSessions(): SessionInfo[];\n clearSession(projectKey: string): boolean;\n restartSession(projectKey: string): boolean;\n shutdown(): void;\n}\n\ninterface InternalSession {\n sessionId: string | undefined;\n projectKey: string;\n cwd: string;\n projectDir: string | undefined;\n worktreePath: string | undefined;\n lastActivity: number;\n processing: boolean;\n queue: Array<{\n prompt: string;\n systemPrompt?: string;\n timeoutMs?: number;\n resolve: (result: ClaudeResult) => void;\n reject: (error: Error) => void;\n }>;\n idleTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport function createSessionManager(defaults: {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n sessionTtlMs?: number;\n maxPersistedSessions?: number;\n claudeArgs: string[];\n}, store?: SessionStore): SessionManager {\n const sessions = new Map<string, InternalSession>();\n const sessionTtlMs = defaults.sessionTtlMs ?? 7 * 24 * 60 * 60 * 1000;\n const maxPersistedSessions = defaults.maxPersistedSessions ?? 50;\n\n let activeProcesses = 0;\n const waiters: Array<() => void> = [];\n\n function pruneSessions(persisted: Map<string, PersistedSession>): number {\n const now = Date.now();\n let pruned = 0;\n\n // Remove sessions older than TTL\n for (const [key, entry] of persisted) {\n if (now - entry.lastActivity > sessionTtlMs) {\n persisted.delete(key);\n pruned++;\n }\n }\n\n // Enforce cap: evict oldest if over limit\n if (persisted.size > maxPersistedSessions) {\n const sorted = Array.from(persisted.entries())\n .sort((a, b) => a[1].lastActivity - b[1].lastActivity);\n const toRemove = sorted.slice(0, persisted.size - maxPersistedSessions);\n for (const [key] of toRemove) {\n persisted.delete(key);\n pruned++;\n }\n }\n\n return pruned;\n }\n\n function persistSessions(): void {\n if (!store) return;\n // Merge in-memory sessions with existing persisted data.\n // In-memory sessions take precedence; persisted-only entries are preserved.\n const persisted = store.load();\n for (const [key, s] of sessions) {\n if (s.sessionId) {\n persisted.set(key, {\n sessionId: s.sessionId,\n projectKey: s.projectKey,\n cwd: s.cwd,\n lastActivity: s.lastActivity,\n worktreePath: s.worktreePath,\n projectDir: s.projectDir,\n });\n }\n }\n pruneSessions(persisted);\n store.save(persisted);\n }\n\n async function acquireSlot(): Promise<void> {\n if (activeProcesses < defaults.maxConcurrentSessions) {\n activeProcesses++;\n return;\n }\n return new Promise<void>((resolve) => {\n waiters.push(() => {\n activeProcesses++;\n resolve();\n });\n });\n }\n\n function releaseSlot(): void {\n activeProcesses--;\n const next = waiters.shift();\n if (next) next();\n }\n\n function resetIdleTimer(session: InternalSession) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n // Don't start idle timer while session has queued work waiting.\n if (session.queue.length > 0) return;\n session.idleTimer = setTimeout(() => {\n // Remove from memory only; session ID and worktree stay on disk for later resume.\n // Worktrees persist on idle intentionally — cleaned up on !kill or startup reconciliation.\n sessions.delete(session.projectKey);\n }, defaults.idleTimeoutMs);\n }\n\n async function processQueue(session: InternalSession): Promise<void> {\n if (session.processing || session.queue.length === 0) return;\n session.processing = true;\n\n while (session.queue.length > 0) {\n const item = session.queue.shift()!;\n await acquireSlot();\n try {\n const result = await runClaude(\n session.cwd,\n defaults.claudeArgs,\n item.prompt,\n session.sessionId,\n item.systemPrompt,\n item.timeoutMs,\n );\n const sessionChanged = !!(\n session.sessionId &&\n result.sessionId &&\n result.sessionId !== session.sessionId\n );\n session.sessionId = result.sessionId || session.sessionId;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n if (sessionChanged) {\n item.resolve({ ...result, sessionChanged: true });\n } else {\n item.resolve(result);\n }\n } catch (err) {\n if (session.sessionId) {\n session.sessionId = undefined;\n try {\n const result = await runClaude(session.cwd, defaults.claudeArgs, item.prompt, undefined, item.systemPrompt, item.timeoutMs);\n session.sessionId = result.sessionId || undefined;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve({ ...result, sessionReset: true });\n } catch (retryErr) {\n item.reject(retryErr instanceof Error ? retryErr : new Error(String(retryErr)));\n }\n } else {\n item.reject(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n releaseSlot();\n }\n }\n\n session.processing = false;\n }\n\n function getOrCreateSession(projectKey: string, cwd: string, useWorktree?: boolean): InternalSession {\n let session = sessions.get(projectKey);\n if (!session) {\n // Check store for a previously persisted session ID\n let restoredSessionId: string | undefined;\n let restoredWorktreePath: string | undefined;\n if (store) {\n const persisted = store.load();\n const entry = persisted.get(projectKey);\n if (entry?.sessionId) {\n restoredSessionId = entry.sessionId;\n restoredWorktreePath = entry.worktreePath;\n }\n }\n\n let effectiveCwd = cwd;\n let worktreePath: string | undefined = restoredWorktreePath;\n let projectDir: string | undefined;\n\n if (useWorktree && !worktreePath) {\n worktreePath = gitCreateWorktree(cwd, projectKey);\n }\n if (worktreePath) {\n projectDir = cwd;\n effectiveCwd = worktreePath;\n }\n\n session = {\n sessionId: restoredSessionId,\n projectKey,\n cwd: effectiveCwd,\n projectDir,\n worktreePath,\n lastActivity: Date.now(),\n processing: false,\n queue: [],\n idleTimer: null,\n };\n sessions.set(projectKey, session);\n resetIdleTimer(session);\n }\n return session;\n }\n\n // Restore persisted sessions into memory at startup, pruning stale entries\n if (store) {\n const persisted = store.load();\n const pruned = pruneSessions(persisted);\n if (pruned > 0) {\n console.log(`Pruned ${pruned} expired session(s)`);\n store.save(persisted);\n }\n for (const [key, entry] of persisted) {\n sessions.set(key, {\n sessionId: entry.sessionId,\n projectKey: entry.projectKey,\n cwd: entry.cwd,\n projectDir: entry.projectDir,\n worktreePath: entry.worktreePath,\n lastActivity: entry.lastActivity,\n processing: false,\n queue: [],\n idleTimer: null,\n });\n }\n for (const session of sessions.values()) {\n resetIdleTimer(session);\n }\n if (persisted.size > 0) {\n console.log(`Restored ${persisted.size} session(s) from disk`);\n }\n }\n\n return {\n send(projectKey: string, cwd: string, prompt: string, opts?: { worktree?: boolean; systemPrompt?: string; timeoutMs?: number }): Promise<ClaudeResult> {\n const session = getOrCreateSession(projectKey, cwd, opts?.worktree);\n return new Promise<ClaudeResult>((resolve, reject) => {\n session.queue.push({ prompt, systemPrompt: opts?.systemPrompt, timeoutMs: opts?.timeoutMs, resolve, reject });\n processQueue(session);\n });\n },\n\n getSession(projectKey: string): SessionInfo | undefined {\n const session = sessions.get(projectKey);\n if (!session) return undefined;\n return {\n sessionId: session.sessionId ?? '',\n projectKey: session.projectKey,\n lastActivity: session.lastActivity,\n queueLength: session.queue.length,\n };\n },\n\n listSessions(): SessionInfo[] {\n return Array.from(sessions.values()).map((s) => ({\n sessionId: s.sessionId ?? '',\n projectKey: s.projectKey,\n lastActivity: s.lastActivity,\n queueLength: s.queue.length,\n }));\n },\n\n clearSession(projectKey: string): boolean {\n const session = sessions.get(projectKey);\n if (!session) return false;\n if (session.idleTimer) clearTimeout(session.idleTimer);\n if (session.worktreePath && session.projectDir) {\n gitRemoveWorktree(session.projectDir, session.projectKey);\n }\n sessions.delete(projectKey);\n persistSessions();\n return true;\n },\n\n restartSession(projectKey: string): boolean {\n const session = sessions.get(projectKey);\n if (!session) return false;\n session.sessionId = undefined;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n return true;\n },\n\n shutdown() {\n persistSessions();\n for (const session of sessions.values()) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n }\n sessions.clear();\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface PersistedSession {\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n worktreePath?: string;\n projectDir?: string;\n}\n\nexport interface SessionStore {\n load(): Map<string, PersistedSession>;\n save(sessions: Map<string, PersistedSession>): void;\n}\n\nexport function createFileSessionStore(filePath: string): SessionStore {\n return {\n load(): Map<string, PersistedSession> {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entries: PersistedSession[] = JSON.parse(raw);\n const map = new Map<string, PersistedSession>();\n for (const entry of entries) {\n if (entry.sessionId && entry.projectKey) {\n map.set(entry.projectKey, entry);\n }\n }\n return map;\n } catch {\n return new Map();\n }\n },\n\n save(sessions: Map<string, PersistedSession>): void {\n const entries = Array.from(sessions.values());\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(entries, null, 2) + '\\n');\n } catch (err) {\n console.error('Failed to persist sessions:', err);\n }\n },\n };\n}\n","import { Client, GatewayIntentBits, Events, Status, type Message, type TextChannel, type ThreadChannel } from 'discord.js';\nimport type { Router } from './router.js';\nimport type { SessionManager } from './session-manager.js';\nimport type { GatewayConfig } from './config.js';\nimport { parseAgentMention } from './agent-dispatch.js';\nimport { sendAgentMessage } from './embed-format.js';\nimport type { TurnCounter } from './turn-counter.js';\n\nexport function chunkMessage(text: string, limit: number): string[] {\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const lines = text.split('\\n');\n let current = '';\n\n for (const line of lines) {\n if (line.length > limit) {\n if (current) {\n chunks.push(current);\n current = '';\n }\n for (let i = 0; i < line.length; i += limit) {\n chunks.push(line.slice(i, i + limit));\n }\n continue;\n }\n\n const candidate = current ? `${current}\\n${line}` : line;\n if (candidate.length > limit) {\n chunks.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n\n if (current || chunks.length === 0) {\n chunks.push(current);\n }\n\n return chunks;\n}\n\nexport interface DiscordBot {\n start(token: string): Promise<void>;\n stop(): void;\n getStatus(): string;\n}\n\nfunction resolveProjectName(config: GatewayConfig, channelId: string): string {\n return config.projects[channelId]?.name ?? channelId;\n}\n\nfunction findProjectByName(config: GatewayConfig, name: string): { channelId: string; name: string } | null {\n const lower = name.toLowerCase();\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (project.name.toLowerCase() === lower) {\n return { channelId, name: project.name };\n }\n }\n return null;\n}\n\nfunction formatTimeSince(timestamp: number): string {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ago`;\n}\n\nexport function handleCommand(\n command: string,\n config: GatewayConfig,\n sessionManager: SessionManager,\n context?: { channelId: string; projectName: string; isThread: boolean },\n): string | null {\n const parts = command.trim().split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n\n if (cmd === '!sessions') {\n const allSessions = sessionManager.listSessions();\n if (allSessions.length === 0) {\n return 'No active sessions.';\n }\n const lines = allSessions.map((s) => {\n const name = resolveProjectName(config, s.projectKey);\n const idle = formatTimeSince(s.lastActivity);\n const queue = s.queueLength > 0 ? ` | queue: ${s.queueLength}` : '';\n const sid = s.sessionId ? ` | \\`${s.sessionId.slice(0, 8)}…\\`` : '';\n return `- **${name}** — last active ${idle}${queue}${sid}`;\n });\n return `**Active sessions (${allSessions.length})**\\n${lines.join('\\n')}`;\n }\n\n if (cmd === '!session') {\n const name = parts.slice(1).join(' ');\n\n // No arguments: show session for the current thread (or project channel)\n if (!name && context) {\n const info = sessionManager.getSession(context.channelId);\n if (!info) return `**${context.projectName}** — no active session in this ${context.isThread ? 'thread' : 'channel'}.`;\n const idle = formatTimeSince(info.lastActivity);\n const sid = info.sessionId || 'none';\n return [\n `**${context.projectName}**${context.isThread ? ' (thread)' : ''}`,\n `Session ID: \\`${sid}\\``,\n `Last active: ${idle}`,\n `Queue depth: ${info.queueLength}`,\n ].join('\\n');\n }\n\n if (!name) return 'Usage: `!session <project name>` or run `!session` in a thread';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const info = sessionManager.getSession(project.channelId);\n if (!info) return `**${project.name}** — no active session.`;\n const idle = formatTimeSince(info.lastActivity);\n const sid = info.sessionId || 'none';\n return [\n `**${project.name}**`,\n `Session ID: \\`${sid}\\``,\n `Last active: ${idle}`,\n `Queue depth: ${info.queueLength}`,\n ].join('\\n');\n }\n\n if (cmd === '!kill') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!kill <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const cleared = sessionManager.clearSession(project.channelId);\n if (cleared) return `Session for **${project.name}** cleared.`;\n return `**${project.name}** — no active session to clear.`;\n }\n\n if (cmd === '!restart') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!restart <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const restarted = sessionManager.restartSession(project.channelId);\n if (restarted) return `Session for **${project.name}** restarted — next message will start fresh context.`;\n return `**${project.name}** — no active session to restart.`;\n }\n\n if (cmd === '!agents') {\n if (!context) return 'Run `!agents` in a project channel or thread.';\n // context.channelId may be a thread ID; look up the project by name\n const match = findProjectByName(config, context.projectName);\n const project = match ? config.projects[match.channelId] : undefined;\n if (!project?.agents || Object.keys(project.agents).length === 0) {\n return `**${context.projectName}** — No agents configured. Messages go to the default session.`;\n }\n const lines = Object.entries(project.agents).map(([name, agent]) =>\n `- \\`@${name}\\` — ${agent.role}`\n );\n return `**${context.projectName} agents**\\n${lines.join('\\n')}\\n\\nMention an agent to dispatch: \\`@pm review this\\``;\n }\n\n if (cmd === '!help') {\n return [\n '**Gateway commands**',\n '`!sessions` — list all active sessions',\n '`!session` — show session for the current thread (or use `!session <name>`)',\n '`!restart <name>` — reset a session (fresh context, keeps worktree)',\n '`!kill <name>` — force-close a project session',\n '`!agents` — list available agents for the current project',\n '`!help` — show this message',\n ].join('\\n');\n }\n\n return null;\n}\n\nconst THREAD_HISTORY_LIMIT = 20;\n\n/** Fetch recent thread messages and format as a conversation log. */\nasync function fetchThreadHistory(channel: TextChannel | ThreadChannel, beforeMessageId: string): Promise<string | null> {\n if (!channel.isThread()) return null;\n try {\n const messages = await channel.messages.fetch({ limit: THREAD_HISTORY_LIMIT, before: beforeMessageId });\n if (messages.size === 0) return null;\n const lines = [...messages.values()]\n .reverse()\n .map((m) => `[${m.author.bot ? 'agent' : m.author.username}]: ${m.content}`);\n return `<thread-history>\\n${lines.join('\\n')}\\n</thread-history>\\n\\n`;\n } catch {\n return null;\n }\n}\n\nexport function createDiscordBot(router: Router, sessionManager: SessionManager, config: GatewayConfig, turnCounter?: TurnCounter): DiscordBot {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n // Track last active agent per thread for routing plain replies (#48)\n const lastActiveAgent = new Map<string, { agentName: string; agent: import('./config.js').AgentConfig }>();\n\n client.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n if (!('send' in message.channel)) return;\n\n // Handle gateway commands from any mapped channel\n if (message.content.startsWith('!')) {\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (resolved) {\n const response = handleCommand(message.content, config, sessionManager, {\n channelId: resolved.channelId,\n projectName: resolved.name,\n isThread: resolved.isThread,\n });\n if (response) {\n await message.channel.send(response);\n return;\n }\n }\n }\n\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (!resolved) return;\n\n try {\n await message.react('👀');\n } catch {\n // Reaction may fail if permissions are missing — non-critical\n }\n\n // If the message is in a main channel, create a thread for the response.\n // If already in a thread, reply there directly.\n let replyChannel: TextChannel | ThreadChannel;\n if (message.channel.isThread()) {\n replyChannel = message.channel;\n } else {\n try {\n replyChannel = await message.startThread({\n name: message.content.slice(0, 100) || 'Claude response',\n autoArchiveDuration: 1440,\n });\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n console.error(`Failed to create thread in ${resolved.name}: ${errMsg}`);\n await message.reply('⚠️ Could not create a thread — check bot permissions (Create Public Threads).');\n return;\n }\n }\n\n // Show typing indicator while Claude is processing\n const typingInterval = setInterval(() => {\n replyChannel.sendTyping().catch(() => {});\n }, 7_000);\n replyChannel.sendTyping().catch(() => {});\n\n // Look up agents for the project (use parent channel ID for threads)\n const projectChannelId = parentId || resolved.channelId;\n const project = config.projects[projectChannelId];\n const agents = project?.agents;\n\n // Reset turn counter on human messages\n if (turnCounter) turnCounter.reset(replyChannel.id);\n\n // Check for @agent mention, fall back to last active agent in this thread (#48)\n const mention = agents ? parseAgentMention(message.content, agents) : null;\n const activeAgent = mention ?? (message.channel.isThread() ? lastActiveAgent.get(replyChannel.id) ?? null : null);\n\n // Use thread ID for session keys so each thread gets its own agent sessions.\n // For main-channel messages, replyChannel is the newly created thread.\n const threadId = replyChannel.id;\n\n // Build session key and system prompt\n const sessionKey = activeAgent\n ? `${threadId}:${activeAgent.agentName}`\n : threadId;\n const systemPrompt = activeAgent\n ? `Your role: ${activeAgent.agent.role}\\n\\n${activeAgent.agent.prompt}`\n : undefined;\n\n try {\n // Prepend thread history when dispatching to an agent in a thread (#49)\n let userPrompt = mention ? mention.prompt : message.content;\n if (activeAgent && message.channel.isThread()) {\n const history = await fetchThreadHistory(replyChannel, message.id);\n if (history) userPrompt = `${history}${userPrompt}`;\n }\n\n const result = await sessionManager.send(\n sessionKey,\n resolved.directory,\n userPrompt,\n {\n worktree: replyChannel.isThread() ? true : undefined,\n systemPrompt,\n },\n );\n\n if (result.sessionReset) {\n await replyChannel.send('⚠️ Previous session expired — starting fresh.');\n } else if (result.sessionChanged) {\n await replyChannel.send('⚠️ Claude started a new session — previous conversation context may be lost.');\n }\n\n await sendAgentMessage(\n replyChannel,\n result.text,\n activeAgent?.agentName,\n activeAgent?.agent.role,\n );\n\n // Track last active agent for plain reply routing (#48)\n if (activeAgent) {\n lastActiveAgent.set(threadId, { agentName: activeAgent.agentName, agent: activeAgent.agent });\n }\n\n // Auto-handoff loop: check if agent response mentions another agent\n if (agents && turnCounter) {\n let responseText = result.text;\n let currentAgentName = activeAgent?.agentName;\n const maxTurns = config.defaults.maxTurnsPerAgent;\n\n while (true) {\n const handoff = parseAgentMention(responseText, agents);\n if (!handoff || handoff.agentName === currentAgentName) break;\n\n turnCounter.increment(replyChannel.id);\n const turn = turnCounter.getTurns(replyChannel.id);\n console.log(`[handoff] thread=${replyChannel.id} turn=${turn}/${maxTurns} ${currentAgentName ?? 'user'} → ${handoff.agentName}`);\n\n if (turnCounter.isOverLimit(replyChannel.id, maxTurns)) {\n console.log(`[handoff] thread=${replyChannel.id} turn limit reached, stopping`);\n await replyChannel.send(\n `⚠️ Agent turn limit reached (${maxTurns}) — send a message to reset.`\n );\n break;\n }\n\n const handoffKey = `${threadId}:${handoff.agentName}`;\n const handoffPrompt = `Your role: ${handoff.agent.role}\\n\\n${handoff.agent.prompt}`;\n\n replyChannel.sendTyping().catch(() => {});\n\n console.log(`[handoff] thread=${replyChannel.id} sending to ${handoff.agentName} (key=${handoffKey}, prompt length=${responseText.length})`);\n const sendStart = Date.now();\n\n let handoffResult;\n try {\n handoffResult = await sessionManager.send(\n handoffKey,\n resolved.directory,\n responseText,\n { worktree: replyChannel.isThread() ? true : undefined, systemPrompt: handoffPrompt, timeoutMs: config.defaults.agentTimeoutMs },\n );\n } catch (handoffErr) {\n const msg = handoffErr instanceof Error ? handoffErr.message : String(handoffErr);\n console.log(`[handoff] thread=${replyChannel.id} ${handoff.agentName} failed: ${msg}`);\n await replyChannel.send(\n `⚠️ Agent \\`@${handoff.agentName}\\` failed: ${msg.slice(0, 1800)}`\n );\n break;\n }\n\n const elapsed = ((Date.now() - sendStart) / 1000).toFixed(1);\n console.log(`[handoff] thread=${replyChannel.id} ${handoff.agentName} responded in ${elapsed}s (${handoffResult.text.length} chars)`);\n\n await sendAgentMessage(\n replyChannel,\n handoffResult.text,\n handoff.agentName,\n handoff.agent.role,\n );\n\n responseText = handoffResult.text;\n currentAgentName = handoff.agentName;\n lastActiveAgent.set(threadId, { agentName: handoff.agentName, agent: handoff.agent });\n }\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n await replyChannel.send(\n `**Error** (${resolved.name}): ${errorMsg.slice(0, 1800)}`,\n );\n } finally {\n clearInterval(typingInterval);\n }\n });\n\n return {\n async start(token: string) {\n await client.login(token);\n console.log(`Gateway connected as ${client.user?.tag}`);\n },\n stop() {\n client.destroy();\n },\n getStatus(): string {\n const ws = client.ws;\n const statusMap: Record<number, string> = {\n [Status.Ready]: 'connected',\n [Status.Connecting]: 'connecting',\n [Status.Reconnecting]: 'reconnecting',\n [Status.Idle]: 'idle',\n [Status.Nearly]: 'nearly',\n [Status.Disconnected]: 'disconnected',\n [Status.WaitingForGuilds]: 'waiting_for_guilds',\n [Status.Identifying]: 'identifying',\n [Status.Resuming]: 'resuming',\n };\n return statusMap[ws.status] ?? 'unknown';\n },\n };\n}\n","// src/agent-dispatch.ts\nimport type { AgentConfig } from './config.js';\n\nexport type { AgentConfig };\n\nexport interface AgentMention {\n agentName: string;\n agent: AgentConfig;\n prompt: string;\n}\n\nexport function parseAgentMention(\n text: string,\n agents: Record<string, AgentConfig>,\n): AgentMention | null {\n // Build a pattern that matches @agentName (case-insensitive)\n const agentNames = Object.keys(agents);\n if (agentNames.length === 0) return null;\n\n const escaped = agentNames.map(n => n.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`@(${escaped.join('|')})\\\\b`, 'i');\n const match = text.match(pattern);\n if (!match) return null;\n\n const matchedName = match[1].toLowerCase();\n const agent = agents[matchedName];\n if (!agent) return null;\n\n // If mention is at the start, strip it from the prompt\n let prompt: string;\n if (match.index === 0) {\n prompt = text.slice(match[0].length).trim();\n } else {\n prompt = text;\n }\n\n return { agentName: matchedName, agent, prompt };\n}\n","// src/embed-format.ts\nimport { EmbedBuilder, type TextChannel, type ThreadChannel } from 'discord.js';\nimport { chunkMessage } from './discord.js';\n\n/** 10 high-contrast colors for light and dark Discord themes. */\nexport const PALETTE: readonly number[] = [\n 0x3498db, // blue\n 0xe74c3c, // red\n 0x2ecc71, // green\n 0x9b59b6, // purple\n 0xf39c12, // orange\n 0x1abc9c, // teal\n 0xe91e63, // pink\n 0xff9800, // amber\n 0x00bcd4, // cyan\n 0x8bc34a, // lime\n];\n\n/** Deterministic color for an agent key (djb2 hash mod palette length). */\nexport function agentColor(agentKey: string): number {\n let hash = 0;\n for (const ch of agentKey) hash = ((hash << 5) - hash + ch.charCodeAt(0)) | 0;\n return PALETTE[Math.abs(hash) % PALETTE.length];\n}\n\nconst EMBED_DESCRIPTION_LIMIT = 4096;\n\n/** Build Discord embeds for an agent response, chunking at 4096 chars. */\nexport function buildAgentEmbeds(text: string, agentName: string, agentRole: string): EmbedBuilder[] {\n const color = agentColor(agentName);\n const chunks = chunkMessage(text, EMBED_DESCRIPTION_LIMIT);\n\n return chunks.map((chunk, i) => {\n const authorName = i === 0 ? agentRole : `${agentRole} (cont.)`;\n const embed = new EmbedBuilder()\n .setAuthor({ name: authorName })\n .setColor(color);\n if (chunk) {\n embed.setDescription(chunk);\n } else {\n embed.data.description = '';\n }\n return embed;\n });\n}\n\nconst PLAIN_TEXT_LIMIT = 2000;\n\n/** Send a message as embeds (if agent) or plain text (if not). */\nexport async function sendAgentMessage(\n channel: { send(content: unknown): Promise<unknown> },\n text: string,\n agentName?: string,\n agentRole?: string,\n): Promise<void> {\n if (agentName && agentRole) {\n const embeds = buildAgentEmbeds(text, agentName, agentRole);\n for (const embed of embeds) {\n await channel.send({ embeds: [embed] });\n }\n } else {\n const chunks = chunkMessage(text, PLAIN_TEXT_LIMIT);\n for (const chunk of chunks) {\n await channel.send(chunk);\n }\n }\n}\n","import { createServer, type Server } from 'node:http';\nimport type { SessionManager } from './session-manager.js';\nimport type { DiscordBot } from './discord.js';\n\nexport interface HealthServer {\n close(): Promise<void>;\n}\n\nexport function createHealthServer(\n port: number,\n sessionManager: SessionManager,\n bot: DiscordBot,\n): Promise<HealthServer> {\n const startTime = Date.now();\n\n const server: Server = createServer((req, res) => {\n const { pathname } = new URL(req.url ?? '/', `http://localhost`);\n if (req.method === 'GET' && pathname === '/health') {\n const sessions = sessionManager.listSessions();\n\n const body = JSON.stringify({\n status: 'ok',\n uptime: Math.floor((Date.now() - startTime) / 1000),\n sessions: {\n active: sessions.length,\n queued: sessions.reduce((sum, s) => sum + s.queueLength, 0),\n },\n discord: bot.getStatus(),\n });\n\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(body);\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'Not Found' }));\n });\n\n return new Promise<HealthServer>((resolve, reject) => {\n server.on('error', reject);\n server.listen(port, () => {\n console.log(`Health endpoint listening on http://localhost:${port}/health`);\n resolve({\n close() {\n return new Promise<void>((res, rej) => {\n server.close((err) => (err ? rej(err) : res()));\n });\n },\n });\n });\n });\n}\n","// src/turn-counter.ts\nexport interface TurnCounter {\n increment(threadId: string): void;\n getTurns(threadId: string): number;\n isOverLimit(threadId: string, limit: number): boolean;\n reset(threadId: string): void;\n}\n\nexport function createTurnCounter(): TurnCounter {\n const turns = new Map<string, number>();\n\n return {\n increment(threadId: string): void {\n turns.set(threadId, (turns.get(threadId) ?? 0) + 1);\n },\n\n getTurns(threadId: string): number {\n return turns.get(threadId) ?? 0;\n },\n\n isOverLimit(threadId: string, limit: number): boolean {\n return (turns.get(threadId) ?? 0) >= limit;\n },\n\n reset(threadId: string): void {\n turns.delete(threadId);\n },\n };\n}\n","import { createInterface } from 'node:readline';\nimport { writeFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { resolveMpgHome, resolveProfileDir } from './resolve-home.js';\n\nfunction createPrompt(): (question: string) => Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return (question: string) =>\n new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));\n}\n\nexport async function runInit(profile?: string) {\n const ask = createPrompt();\n\n // Determine output directory: if --profile is given, use MPG_HOME/profiles/<name>;\n // otherwise, use CWD for backward compat (quick single-instance setup).\n let configDir: string;\n let envDir: string;\n if (profile) {\n configDir = resolveProfileDir(profile);\n envDir = resolveMpgHome();\n mkdirSync(configDir, { recursive: true });\n mkdirSync(envDir, { recursive: true });\n console.log(`\\nmpg init — set up profile \"${profile}\"\\n`);\n console.log(`Profile directory: ${configDir}`);\n console.log(`Secrets directory: ${envDir}\\n`);\n } else {\n configDir = process.cwd();\n envDir = process.cwd();\n console.log('\\nmpg init — set up multi-project gateway\\n');\n }\n\n // Check for claude CLI\n try {\n execSync('claude --version', { stdio: 'pipe' });\n console.log('Claude CLI found.');\n } catch {\n console.warn('Warning: `claude` not found on PATH. Make sure it is installed before starting the gateway.');\n }\n\n // Discord bot token\n let token = process.env.DISCORD_BOT_TOKEN ?? '';\n const inputToken = await ask(`Discord bot token${token ? ' (press Enter to keep existing)' : ''}: `);\n if (inputToken) token = inputToken;\n if (!token) {\n console.error('A Discord bot token is required. Create one at https://discord.com/developers/applications');\n process.exit(1);\n }\n\n // Write .env\n const envPath = resolve(envDir, '.env');\n writeFileSync(envPath, `DISCORD_BOT_TOKEN=${token}\\n`);\n console.log(`Wrote ${envPath}`);\n\n // Collect projects\n interface ProjectEntry {\n name: string;\n directory: string;\n channelId: string;\n }\n\n const projects: ProjectEntry[] = [];\n\n // Load existing config if present\n const configPath = resolve(configDir, 'config.json');\n if (existsSync(configPath)) {\n try {\n const existing = JSON.parse(\n (await import('node:fs')).readFileSync(configPath, 'utf-8'),\n );\n if (existing.projects) {\n for (const [channelId, project] of Object.entries(existing.projects)) {\n const p = project as { name?: string; directory: string };\n projects.push({ name: p.name ?? channelId, directory: p.directory, channelId });\n }\n if (projects.length > 0) {\n console.log(`\\nExisting projects (${projects.length}):`);\n for (const p of projects) {\n console.log(` ${p.name} → ${p.directory} (${p.channelId})`);\n }\n }\n }\n } catch {\n // ignore parse errors\n }\n }\n\n console.log('\\nAdd projects (empty name to finish):\\n');\n\n while (true) {\n const name = await ask('Project name: ');\n if (!name) break;\n\n const directory = await ask('Project directory (absolute path): ');\n if (!directory) {\n console.log('Directory is required, skipping.');\n continue;\n }\n if (!existsSync(directory)) {\n console.warn(`Warning: ${directory} does not exist.`);\n }\n\n const channelId = await ask('Discord channel ID: ');\n if (!channelId) {\n console.log('Channel ID is required, skipping.');\n continue;\n }\n\n projects.push({ name, directory, channelId });\n console.log(`Added ${name}\\n`);\n }\n\n if (projects.length === 0) {\n console.log('No projects configured. You can edit config.json later.');\n }\n\n // Build config\n const config = {\n defaults: {\n idleTimeoutMs: 1800000,\n maxConcurrentSessions: 4,\n claudeArgs: ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: Object.fromEntries(\n projects.map((p) => [p.channelId, { name: p.name, directory: p.directory }]),\n ),\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n console.log(`Wrote ${configPath}`);\n\n console.log('\\nSetup complete! Run `mpg start` to launch the gateway.');\n}\n","import { resolve, dirname } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\n\n/**\n * Returns the MPG home directory.\n * Priority: MPG_HOME env var > ~/.mpg\n */\nexport function resolveMpgHome(): string {\n return process.env.MPG_HOME ?? resolve(homedir(), '.mpg');\n}\n\n/**\n * Returns the profile directory for a given profile name.\n */\nexport function resolveProfileDir(profile: string): string {\n return resolve(resolveMpgHome(), 'profiles', profile);\n}\n\nexport interface ResolvedPaths {\n envPath: string | undefined;\n configPath: string;\n sessionsPath: string;\n}\n\n/**\n * Resolve .env path using the resolution order:\n * 1. Environment variables (already set) — highest priority (returns undefined, already loaded)\n * 2. $MPG_HOME/.env\n * 3. $CWD/.env — backward compat\n */\nexport function resolveEnvPath(): string | undefined {\n const mpgHome = resolveMpgHome();\n const mpgEnv = resolve(mpgHome, '.env');\n if (existsSync(mpgEnv)) {\n return mpgEnv;\n }\n\n const cwdEnv = resolve(process.cwd(), '.env');\n if (existsSync(cwdEnv)) {\n return cwdEnv;\n }\n\n return undefined;\n}\n\n/**\n * Resolve config.json path using the resolution order:\n * 1. --config <path> CLI flag (explicit path)\n * 2. --profile <name> → $MPG_HOME/profiles/<name>/config.json\n * 3. $MPG_HOME/profiles/default/config.json\n * 4. $CWD/config.json — backward compat fallback\n *\n * Returns the resolved config path, or undefined if none found.\n */\nexport function resolveConfigPath(options?: {\n configFlag?: string;\n profileFlag?: string;\n}): string | undefined {\n // 1. Explicit --config flag\n if (options?.configFlag) {\n const explicit = resolve(options.configFlag);\n if (existsSync(explicit)) {\n return explicit;\n }\n return explicit; // Return even if missing — let caller handle error\n }\n\n // 2. --profile flag\n if (options?.profileFlag) {\n const profileConfig = resolve(\n resolveProfileDir(options.profileFlag),\n 'config.json',\n );\n if (existsSync(profileConfig)) {\n return profileConfig;\n }\n return profileConfig; // Return even if missing — let caller handle error\n }\n\n // 3. MPG_HOME/profiles/default/config.json\n const mpgHome = resolveMpgHome();\n const defaultConfig = resolve(mpgHome, 'profiles', 'default', 'config.json');\n if (existsSync(defaultConfig)) {\n return defaultConfig;\n }\n\n // 4. CWD/config.json — backward compat\n const cwdConfig = resolve(process.cwd(), 'config.json');\n if (existsSync(cwdConfig)) {\n return cwdConfig;\n }\n\n return undefined;\n}\n\n/**\n * Resolve sessions.json path — always co-located with the resolved config.json.\n */\nexport function resolveSessionsPath(configPath: string): string {\n return resolve(dirname(configPath), 'sessions.json');\n}\n\n/**\n * Parse --config, --profile flags from argv.\n */\nexport function parseFlags(argv: string[]): {\n configFlag?: string;\n profileFlag?: string;\n migrate?: boolean;\n} {\n const result: { configFlag?: string; profileFlag?: string; migrate?: boolean } = {};\n\n for (let i = 0; i < argv.length; i++) {\n if (argv[i] === '--config' && i + 1 < argv.length) {\n result.configFlag = argv[i + 1];\n i++;\n } else if (argv[i] === '--profile' && i + 1 < argv.length) {\n result.profileFlag = argv[i + 1];\n i++;\n } else if (argv[i] === '--migrate') {\n result.migrate = true;\n }\n }\n\n return result;\n}\n","import { statSync } from 'node:fs';\nimport { execFileSync } from 'node:child_process';\nimport type { GatewayConfig } from './config.js';\n\nexport function runHealthChecks(config: GatewayConfig): void {\n // 1. Check claude CLI is on PATH\n try {\n execFileSync('claude', ['--version'], { timeout: 5000, stdio: 'ignore' });\n } catch {\n console.error(\n 'Health check failed:\\n' +\n ' ✗ \"claude\" CLI not found on PATH. Install: https://docs.anthropic.com/en/docs/claude-code'\n );\n process.exit(1);\n return;\n }\n\n // 2. Check all project directories exist and are directories\n const missing: string[] = [];\n for (const project of Object.values(config.projects)) {\n try {\n if (!statSync(project.directory).isDirectory()) {\n missing.push(` ✗ Project \"${project.name}\" path is not a directory: ${project.directory}`);\n }\n } catch {\n missing.push(` ✗ Project \"${project.name}\" directory not found: ${project.directory}`);\n }\n }\n\n if (missing.length > 0) {\n console.error('Health check failed:\\n' + missing.join('\\n'));\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["resolve","existsSync","readFileSync","mkdirSync","args","resolve","resolve","worktreePath","command","info","idle","sid","parentId","resolved","resolve","writeFileSync","existsSync","mkdirSync","resolve","dirname","existsSync","resolve","mkdirSync","writeFileSync","existsSync","execFileSync","readFileSync","existsSync","resolve","mkdirSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "multi-project-gateway",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Route Discord messages to per-project Claude Code CLI sessions",
6
6
  "bin": {