@anthropic-forge/cli 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +83 -83
- package/package.json +44 -43
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2191
- package/dist/index.js.map +0 -1
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/services/config.service.ts","../src/config.ts","../src/services/auth.service.ts","../src/mcp/install.ts","../src/commands/logout.ts","../src/commands/list.ts","../src/middleware/auth-guard.ts","../src/services/api.service.ts","../src/ui/formatters.ts","../src/types/ticket.ts","../src/services/claude.service.ts","../src/services/clipboard.service.ts","../src/ui/ticket-formatter.ts","../src/commands/show.ts","../src/ui/pager.ts","../src/commands/review.ts","../src/commands/execute.ts","../src/commands/mcp.ts","../src/mcp/server.ts","../src/mcp/tools/get-ticket-context.ts","../src/mcp/tools/get-file-changes.ts","../src/mcp/tools/get-repository-context.ts","../src/services/git.service.ts","../src/mcp/tools/update-ticket-status.ts","../src/mcp/tools/submit-review-session.ts","../src/mcp/tools/list-tickets.ts","../src/agents/dev-executor.md","../src/mcp/prompts/forge-execute.ts","../src/agents/dev-reviewer.md","../src/mcp/prompts/forge-review.ts","../src/mcp/prompts/forge-list.ts","../src/mcp/prompts/forge-exec.ts","../src/commands/whoami.ts","../src/commands/doctor.ts"],"sourcesContent":["import { createRequire } from 'module';\r\nimport { Command } from 'commander';\r\nimport { loginCommand } from './commands/login';\r\nimport { logoutCommand } from './commands/logout';\r\nimport { listCommand } from './commands/list';\r\nimport { showCommand } from './commands/show';\r\nimport { reviewCommand } from './commands/review';\r\nimport { executeCommand } from './commands/execute';\r\nimport { mcpCommand } from './commands/mcp';\r\nimport { whoamiCommand } from './commands/whoami';\r\nimport { doctorCommand } from './commands/doctor';\r\n\r\nconst require = createRequire(import.meta.url);\r\nconst { version } = require('../package.json') as { version: string };\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('forge')\r\n .version(version)\r\n .description('CLI for Forge — authenticate, browse tickets, and execute AI-assisted implementations via MCP');\r\n\r\nprogram.addCommand(loginCommand);\r\nprogram.addCommand(logoutCommand);\r\nprogram.addCommand(listCommand, { hidden: true });\r\nprogram.addCommand(showCommand);\r\nprogram.addCommand(reviewCommand);\r\nprogram.addCommand(executeCommand);\r\nprogram.addCommand(mcpCommand);\r\nprogram.addCommand(whoamiCommand);\r\nprogram.addCommand(doctorCommand);\r\n\r\nprogram.addHelpText('after', `\r\nGetting started:\r\n $ forge login Authenticate with your Forge account\r\n $ forge execute T-001 Start an AI-assisted execution session\r\n $ forge mcp install Set up MCP server for Claude Code\r\n\r\nTroubleshooting:\r\n $ forge doctor Run diagnostic checks on your setup\r\n $ forge whoami Show current user and token status\r\n`);\r\n\r\nprogram.parse(process.argv);\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport ora from 'ora';\r\nimport * as ConfigService from '../services/config.service';\r\nimport * as AuthService from '../services/auth.service';\r\nimport { tryRegisterMcpServer } from '../mcp/install';\r\n\r\nexport const loginCommand = new Command('login')\r\n .description('Authenticate with your Forge account')\r\n .action(async () => {\r\n try {\r\n // Check if already logged in\r\n const existing = await ConfigService.load();\r\n if (AuthService.isLoggedIn(existing)) {\r\n console.log(\r\n chalk.yellow(\r\n `You are already logged in as ${existing!.user.email}. Run \\`forge logout\\` first.`\r\n )\r\n );\r\n process.exit(0);\r\n }\r\n\r\n // Initiate Device Flow\r\n const deviceFlow = await AuthService.startDeviceFlow();\r\n\r\n console.log();\r\n console.log(chalk.bold('Open this URL in your browser:'));\r\n console.log(chalk.cyan(` ${deviceFlow.verificationUri}`));\r\n console.log();\r\n console.log(chalk.bold('Enter this code:'));\r\n console.log(chalk.green.bold(` ${deviceFlow.userCode}`));\r\n console.log();\r\n\r\n const spinner = ora('Waiting for authorization…').start();\r\n\r\n // Handle Ctrl+C — exit cleanly without writing partial config\r\n process.on('SIGINT', () => {\r\n spinner.stop();\r\n console.log('\\nCancelled.');\r\n process.exit(0);\r\n });\r\n\r\n // Poll for token\r\n let token;\r\n try {\r\n token = await AuthService.pollToken(\r\n deviceFlow.deviceCode,\r\n deviceFlow.interval\r\n );\r\n } catch (err) {\r\n spinner.fail('Authorization failed.');\r\n console.error(chalk.red((err as Error).message));\r\n process.exit(1);\r\n }\r\n\r\n // Save tokens — only reached after successful polling\r\n await ConfigService.save({\r\n accessToken: token.accessToken,\r\n refreshToken: token.refreshToken,\r\n expiresAt: new Date(Date.now() + 15 * 60 * 1000).toISOString(),\r\n userId: token.userId,\r\n teamId: token.teamId,\r\n user: token.user,\r\n });\r\n\r\n spinner.succeed(\r\n `${chalk.green('Logged in as')} ${chalk.bold(token.user.email)} | Team: ${chalk.bold(token.teamId)}`\r\n );\r\n\r\n // Auto-register MCP server (user scope) — must not fail login if this errors\r\n try {\r\n const mcpResult = await tryRegisterMcpServer('user');\r\n if (mcpResult === 'registered') {\r\n console.log(chalk.dim(' ✓ Forge MCP server registered (user scope)'));\r\n console.log(chalk.dim(' Claude Code will connect automatically on next start.'));\r\n } else {\r\n console.log(chalk.dim(' ℹ Run `forge mcp install` to enable MCP in Claude Code'));\r\n }\r\n } catch (mcpErr) {\r\n // MCP setup errors must never cause login to fail — but surface a hint\r\n console.log(\r\n chalk.yellow(\r\n ` ⚠ Could not auto-register MCP server: ${(mcpErr as Error).message}`\r\n )\r\n );\r\n console.log(\r\n chalk.dim(' Run `forge mcp install` manually to set it up.')\r\n );\r\n }\r\n\r\n process.exit(0);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(1);\r\n }\r\n });\r\n","import * as fs from 'fs/promises';\r\nimport * as path from 'path';\r\nimport * as os from 'os';\r\nimport { z } from 'zod';\r\n\r\nconst ForgeConfigSchema = z.object({\r\n accessToken: z.string(),\r\n refreshToken: z.string(),\r\n expiresAt: z.string(),\r\n userId: z.string(),\r\n teamId: z.string(),\r\n workspaceId: z.string().optional(),\r\n user: z.object({\r\n email: z.string().email(),\r\n displayName: z.string(),\r\n }),\r\n});\r\n\r\nexport type ForgeConfig = z.infer<typeof ForgeConfigSchema>;\r\n\r\nconst CONFIG_DIR = path.join(os.homedir(), '.forge');\r\nconst CONFIG_FILE = 'config.json';\r\n\r\nexport function getConfigPath(): string {\r\n return path.join(CONFIG_DIR, CONFIG_FILE);\r\n}\r\n\r\nexport async function load(): Promise<ForgeConfig | null> {\r\n const configPath = getConfigPath();\r\n let raw: string;\r\n\r\n try {\r\n raw = await fs.readFile(configPath, 'utf-8');\r\n } catch (err: unknown) {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return null;\r\n }\r\n throw err;\r\n }\r\n\r\n // Warn if file permissions are not 0o600 (security check)\r\n // Skip on Windows: stat.mode doesn't reflect NTFS ACLs and chmod is a no-op\r\n if (process.platform !== 'win32') {\r\n try {\r\n const stat = await fs.stat(configPath);\r\n if ((stat.mode & 0o777) !== 0o600) {\r\n process.stderr.write(\r\n `⚠️ Warning: ~/.forge/config.json permissions are not 600. Run: chmod 600 ${configPath}\\n`\r\n );\r\n }\r\n } catch {\r\n // Non-fatal: stat failure doesn't block loading\r\n }\r\n }\r\n\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch {\r\n throw new Error(\r\n 'Config file is malformed. Run `forge login` to re-authenticate.'\r\n );\r\n }\r\n\r\n const result = ForgeConfigSchema.safeParse(parsed);\r\n if (!result.success) {\r\n throw new Error(\r\n 'Config file is corrupt or invalid. Run `forge login` to re-authenticate.'\r\n );\r\n }\r\n\r\n return result.data;\r\n}\r\n\r\nexport async function save(config: ForgeConfig): Promise<void> {\r\n const configPath = getConfigPath();\r\n await fs.mkdir(CONFIG_DIR, { recursive: true });\r\n await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');\r\n // chmod is a no-op on Windows — NTFS uses ACLs, not POSIX mode bits\r\n if (process.platform !== 'win32') {\r\n await fs.chmod(configPath, 0o600);\r\n }\r\n}\r\n\r\nexport async function clear(): Promise<void> {\r\n const configPath = getConfigPath();\r\n try {\r\n await fs.unlink(configPath);\r\n } catch (err: unknown) {\r\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw err;\r\n }\r\n }\r\n}\r\n","import * as dotenv from 'dotenv';\r\nimport * as path from 'path';\r\nimport chalk from 'chalk';\r\n\r\nif (process.env.NODE_ENV !== 'production') {\r\n dotenv.config({ path: path.resolve(process.cwd(), '.env.development') });\r\n}\r\n\r\n// Warn if TLS certificate validation has been disabled — tokens would be\r\n// exposed to man-in-the-middle attacks.\r\nif (process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {\r\n process.stderr.write(\r\n chalk.yellow(\r\n '⚠ WARNING: NODE_TLS_REJECT_UNAUTHORIZED=0 disables TLS certificate validation.\\n' +\r\n ' Your auth tokens may be exposed to interception. Unset this variable in production.\\n'\r\n )\r\n );\r\n}\r\n\r\n// API_URL must include the /api prefix (NestJS global prefix).\r\n// Override via FORGE_API_URL env var for local dev or self-hosted deployments.\r\nconst rawApiUrl = process.env.FORGE_API_URL;\r\nconst rawAppUrl = process.env.FORGE_APP_URL;\r\n\r\n// In production, only allow HTTPS URLs to prevent token interception.\r\nfunction validateUrl(url: string, envName: string): string {\r\n if (process.env.NODE_ENV !== 'production') return url;\r\n if (!url.startsWith('https://')) {\r\n process.stderr.write(\r\n chalk.yellow(\r\n `⚠ WARNING: ${envName}=${url} is not HTTPS. ` +\r\n `Auth tokens may be sent over an insecure connection.\\n`\r\n )\r\n );\r\n }\r\n return url;\r\n}\r\n\r\nexport const API_URL = validateUrl(\r\n rawApiUrl ?? 'https://www.forge-ai.dev/api',\r\n 'FORGE_API_URL'\r\n);\r\nexport const APP_URL = validateUrl(\r\n rawAppUrl ?? 'https://www.forge-ai.dev',\r\n 'FORGE_APP_URL'\r\n);\r\n","import { API_URL } from '../config';\r\nimport type { ForgeConfig } from './config.service';\r\nimport type { DeviceFlowRequest, DeviceFlowToken } from '../types/auth';\r\n\r\nconst MAX_POLL_MS = 300_000; // 5 minutes\r\n\r\nexport async function startDeviceFlow(): Promise<DeviceFlowRequest> {\r\n const res = await fetch(`${API_URL}/auth/device/request`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n });\r\n\r\n if (!res.ok) {\r\n throw new Error(\r\n `Failed to initiate authentication: ${res.status} ${res.statusText}`\r\n );\r\n }\r\n\r\n return res.json() as Promise<DeviceFlowRequest>;\r\n}\r\n\r\nexport async function pollToken(\r\n deviceCode: string,\r\n interval: number,\r\n maxMs: number = MAX_POLL_MS\r\n): Promise<DeviceFlowToken> {\r\n const start = Date.now();\r\n const intervalMs = interval * 1000;\r\n\r\n while (Date.now() - start < maxMs) {\r\n await sleep(intervalMs);\r\n\r\n const res = await fetch(`${API_URL}/auth/device/token`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ deviceCode }),\r\n });\r\n\r\n if (res.ok) {\r\n return res.json() as Promise<DeviceFlowToken>;\r\n }\r\n\r\n if (res.status === 400) {\r\n const body = (await res.json()) as { error: string };\r\n\r\n if (body.error === 'authorization_pending') {\r\n continue;\r\n }\r\n\r\n if (body.error === 'expired_token') {\r\n throw new Error(\r\n 'Authorization timed out. Run `forge login` to try again.'\r\n );\r\n }\r\n\r\n if (body.error === 'access_denied') {\r\n throw new Error(\r\n 'Authorization was denied. Run `forge login` to try again.'\r\n );\r\n }\r\n }\r\n\r\n throw new Error(\r\n `Unexpected server response: ${res.status} ${res.statusText}`\r\n );\r\n }\r\n\r\n throw new Error(\r\n 'Authorization timed out (5 min exceeded). Run `forge login` to try again.'\r\n );\r\n}\r\n\r\nexport function isLoggedIn(config: Partial<ForgeConfig> | null): boolean {\r\n return !!config?.accessToken;\r\n}\r\n\r\nexport interface RefreshResult {\r\n accessToken: string;\r\n expiresAt: string;\r\n}\r\n\r\nexport async function refresh(refreshToken: string): Promise<RefreshResult> {\r\n const res = await fetch(`${API_URL}/auth/refresh`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({ refreshToken }),\r\n });\r\n\r\n if (!res.ok) {\r\n throw new Error(\r\n 'Session expired. Run `forge login` to re-authenticate.'\r\n );\r\n }\r\n\r\n return res.json() as Promise<RefreshResult>;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n","import { execSync } from 'child_process';\r\nimport * as fs from 'fs/promises';\r\nimport * as path from 'path';\r\n\r\nexport type McpRegisterResult = 'registered' | 'skipped';\r\n\r\n/**\r\n * Attempts to register Forge as an MCP server with the claude CLI.\r\n * Returns 'registered' on success, 'skipped' if the claude CLI is not found\r\n * or the command fails for any reason.\r\n *\r\n * Never throws — callers can wrap in try/catch for extra safety.\r\n */\r\nexport async function tryRegisterMcpServer(\r\n scope: 'user' | 'project'\r\n): Promise<McpRegisterResult> {\r\n const cmd = `claude mcp add --transport stdio --scope ${scope} forge -- forge mcp`;\r\n try {\r\n execSync(cmd, { stdio: 'pipe' });\r\n return 'registered';\r\n } catch {\r\n // ENOENT (claude not found) or non-zero exit code → skip silently\r\n return 'skipped';\r\n }\r\n}\r\n\r\ninterface McpJsonConfig {\r\n mcpServers: Record<string, {\r\n type: string;\r\n command: string;\r\n args: string[];\r\n }>;\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Writes (or merges) a .mcp.json file in the given directory with the forge\r\n * MCP server entry. Existing entries are preserved.\r\n */\r\nexport async function writeMcpJson(cwd: string = process.cwd()): Promise<void> {\r\n const mcpJsonPath = path.join(cwd, '.mcp.json');\r\n\r\n let existing: McpJsonConfig = { mcpServers: {} };\r\n try {\r\n const raw = await fs.readFile(mcpJsonPath, 'utf-8');\r\n const parsed = JSON.parse(raw) as McpJsonConfig;\r\n existing = parsed;\r\n if (!existing.mcpServers) {\r\n existing.mcpServers = {};\r\n }\r\n } catch {\r\n // File doesn't exist or is unreadable — start fresh\r\n }\r\n\r\n const merged: McpJsonConfig = {\r\n ...existing,\r\n mcpServers: {\r\n ...existing.mcpServers,\r\n forge: {\r\n type: 'stdio',\r\n command: 'forge',\r\n args: ['mcp'],\r\n },\r\n },\r\n };\r\n\r\n await fs.writeFile(mcpJsonPath, JSON.stringify(merged, null, 2) + '\\n', 'utf-8');\r\n}\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport * as ConfigService from '../services/config.service';\r\nimport { isLoggedIn } from '../services/auth.service';\r\n\r\nexport const logoutCommand = new Command('logout')\r\n .description('Sign out of your Forge account')\r\n .action(async () => {\r\n try {\r\n const config = await ConfigService.load();\r\n\r\n if (!isLoggedIn(config)) {\r\n console.log(chalk.yellow('You are not logged in.'));\r\n process.exit(0);\r\n }\r\n\r\n await ConfigService.clear();\r\n console.log(chalk.green('Logged out successfully.'));\r\n process.exit(0);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(1);\r\n }\r\n });\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport { requireAuth } from '../middleware/auth-guard';\r\nimport type { ForgeConfig } from '../services/config.service';\r\nimport * as ApiService from '../services/api.service';\r\nimport { formatTicketRow, statusIcon, STATUS_DISPLAY_NAMES } from '../ui/formatters';\r\nimport { AECStatus, type TicketListItem, type TicketDetail } from '../types/ticket';\r\nimport { spawnClaude } from '../services/claude.service';\r\nimport { copyToClipboard } from '../services/clipboard.service';\r\nimport { formatTicketPlainText } from '../ui/ticket-formatter';\r\n\r\nconst EXECUTE_VALID = new Set<AECStatus>([AECStatus.READY, AECStatus.VALIDATED]);\r\nconst REVIEW_VALID = new Set<AECStatus>([\r\n AECStatus.READY,\r\n AECStatus.VALIDATED,\r\n AECStatus.CREATED,\r\n AECStatus.DRIFTED,\r\n]);\r\n\r\nexport const listCommand = new Command('list')\r\n .description('List tickets assigned to you')\r\n .option('--all', 'Show all team tickets, not just assigned to me')\r\n .action(async (options: { all?: boolean }) => {\r\n try {\r\n const config = await requireAuth();\r\n\r\n const params: Record<string, string> = {\r\n teamId: config.teamId,\r\n };\r\n\r\n if (options.all) {\r\n params.all = 'true';\r\n } else {\r\n params.assignedToMe = 'true';\r\n }\r\n\r\n const [tickets, memberNames] = await Promise.all([\r\n ApiService.get<TicketListItem[]>('/tickets', config, params),\r\n fetchMemberNames(config),\r\n ]);\r\n\r\n // Non-TTY: plain output for piping/scripting\r\n if (!process.stdout.isTTY) {\r\n if (tickets.length === 0) {\r\n const hint = options.all ? '' : ' Try `forge list --all` to see all team tickets.';\r\n console.log(`No tickets assigned to you.${hint}`);\r\n } else {\r\n tickets.forEach((t) => {\r\n const assignee = t.assignedTo\r\n ? memberNames.get(t.assignedTo) ?? t.assignedTo\r\n : '';\r\n console.log(`${t.id}\\t${t.status}\\t${t.title}\\t${assignee}`);\r\n });\r\n }\r\n process.exit(0);\r\n }\r\n\r\n await renderInteractiveList(tickets, options.all ?? false, memberNames, config);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(2);\r\n }\r\n });\r\n\r\ninterface TeamMemberResponse {\r\n userId: string;\r\n displayName: string;\r\n}\r\n\r\nasync function fetchMemberNames(config: ForgeConfig): Promise<Map<string, string>> {\r\n try {\r\n const res = await ApiService.get<{ members: TeamMemberResponse[] }>(\r\n `/teams/${config.teamId}/members`,\r\n config\r\n );\r\n const map = new Map<string, string>();\r\n for (const m of res.members) {\r\n if (m.displayName) map.set(m.userId, m.displayName);\r\n }\r\n return map;\r\n } catch {\r\n return new Map();\r\n }\r\n}\r\n\r\nconst DIVIDER = chalk.dim('─'.repeat(72));\r\n\r\nconst PRIORITY_COLOR: Record<string, (s: string) => string> = {\r\n urgent: chalk.red,\r\n high: chalk.yellow,\r\n medium: chalk.white,\r\n low: chalk.dim,\r\n};\r\n\r\ntype Screen = 'list' | 'detail';\r\n\r\nasync function renderInteractiveList(\r\n tickets: TicketListItem[],\r\n showAll: boolean,\r\n memberNames: Map<string, string>,\r\n config: ForgeConfig\r\n): Promise<void> {\r\n if (tickets.length === 0) {\r\n const hint = showAll ? '' : ' Try `forge list --all` to see all team tickets.';\r\n console.log(chalk.dim(`No tickets found.${hint}`));\r\n process.exit(0);\r\n }\r\n\r\n let selected = 0;\r\n let screen: Screen = 'list';\r\n let detailTicket: TicketDetail | null = null;\r\n let loading = false;\r\n\r\n function renderList(): void {\r\n process.stdout.write('\\u001b[2J\\u001b[H');\r\n const label = showAll ? 'All Team Tickets' : 'My Tickets';\r\n console.log(chalk.bold(`forge — ${label} (${tickets.length})`));\r\n console.log(DIVIDER);\r\n tickets.forEach((ticket, i) => {\r\n console.log(formatTicketRow(ticket, i === selected, memberNames));\r\n });\r\n console.log(DIVIDER);\r\n console.log(chalk.dim('↑↓ navigate Enter details e execute r review q quit'));\r\n }\r\n\r\n function renderDetail(): void {\r\n if (!detailTicket) return;\r\n process.stdout.write('\\u001b[2J\\u001b[H');\r\n\r\n const icon = statusIcon(detailTicket.status);\r\n const statusName = STATUS_DISPLAY_NAMES[detailTicket.status] ?? detailTicket.status;\r\n\r\n console.log(chalk.bold(`[${detailTicket.id}] ${detailTicket.title}`));\r\n console.log(DIVIDER);\r\n console.log(`${chalk.dim('Status: ')} ${icon} ${chalk.bold(statusName)}`);\r\n\r\n if (detailTicket.priority) {\r\n const colorFn = PRIORITY_COLOR[detailTicket.priority] ?? chalk.white;\r\n console.log(`${chalk.dim('Priority: ')} ${colorFn(detailTicket.priority.toUpperCase())}`);\r\n }\r\n\r\n if (detailTicket.assignedTo) {\r\n const name = memberNames.get(detailTicket.assignedTo) ?? detailTicket.assignedTo;\r\n console.log(`${chalk.dim('Assignee: ')} ${name}`);\r\n }\r\n\r\n if (detailTicket.description) {\r\n console.log();\r\n console.log(chalk.bold.underline('Description'));\r\n console.log(detailTicket.description);\r\n }\r\n\r\n if (detailTicket.acceptanceCriteria.length > 0) {\r\n console.log();\r\n console.log(chalk.bold.underline('Acceptance Criteria'));\r\n detailTicket.acceptanceCriteria.forEach((ac, i) => {\r\n console.log(` ${chalk.dim(`${i + 1}.`)} ${ac}`);\r\n });\r\n }\r\n\r\n console.log();\r\n console.log(DIVIDER);\r\n\r\n // Build help bar based on valid actions for this ticket's status\r\n const actions: string[] = [];\r\n if (EXECUTE_VALID.has(detailTicket.status)) actions.push('e execute');\r\n if (REVIEW_VALID.has(detailTicket.status)) actions.push('r review');\r\n actions.push('c copy', 'Esc back', 'q quit');\r\n console.log(chalk.dim(actions.join(' ')));\r\n }\r\n\r\n function render(): void {\r\n if (screen === 'list') renderList();\r\n else renderDetail();\r\n }\r\n\r\n function cleanup(): void {\r\n process.stdin.setRawMode(false);\r\n process.stdin.pause();\r\n process.stdout.write('\\u001b[2J\\u001b[H');\r\n }\r\n\r\n async function launchAction(\r\n action: 'execute' | 'review',\r\n ticket: TicketListItem | TicketDetail\r\n ): Promise<void> {\r\n const validSet = action === 'execute' ? EXECUTE_VALID : REVIEW_VALID;\r\n if (!validSet.has(ticket.status)) {\r\n const validNames = [...validSet].map((s) => STATUS_DISPLAY_NAMES[s] ?? s).join(', ');\r\n process.stdout.write(\r\n chalk.yellow(`\\n Cannot ${action}: status \"${STATUS_DISPLAY_NAMES[ticket.status] ?? ticket.status}\" is not valid. Valid: ${validNames}\\n`)\r\n );\r\n await new Promise((r) => setTimeout(r, 1500));\r\n render();\r\n return;\r\n }\r\n\r\n cleanup();\r\n process.stdin.removeListener('data', onData);\r\n\r\n try {\r\n await ApiService.patch(`/tickets/${ticket.id}`, { assignedTo: config.userId }, config);\r\n } catch {\r\n process.stderr.write(chalk.dim(' Warning: Could not auto-assign ticket.\\n'));\r\n }\r\n\r\n const icon = statusIcon(ticket.status);\r\n process.stderr.write(\r\n `\\n${icon} ${action === 'execute' ? 'Executing' : 'Reviewing'} [${ticket.id}] ${ticket.title} — launching Claude...\\n\\n`\r\n );\r\n\r\n try {\r\n const exitCode = await spawnClaude(action, ticket.id);\r\n process.exit(exitCode);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(1);\r\n }\r\n }\r\n\r\n async function onData(key: string): Promise<void> {\r\n if (loading) return;\r\n\r\n if (screen === 'list') {\r\n if (key === '\\u001b[A') {\r\n selected = Math.max(0, selected - 1);\r\n render();\r\n } else if (key === '\\u001b[B') {\r\n selected = Math.min(tickets.length - 1, selected + 1);\r\n render();\r\n } else if (key === '\\r' || key === '\\n') {\r\n // Enter — fetch detail and switch to detail screen\r\n loading = true;\r\n const ticket = tickets[selected];\r\n process.stdout.write('\\u001b[2J\\u001b[H');\r\n console.log(chalk.dim(`Loading ${ticket.id}...`));\r\n try {\r\n detailTicket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticket.id}`,\r\n config\r\n );\r\n screen = 'detail';\r\n } catch (err) {\r\n process.stdout.write('\\u001b[2J\\u001b[H');\r\n console.log(chalk.red(`Error loading ticket: ${(err as Error).message}`));\r\n await new Promise((r) => setTimeout(r, 1500));\r\n }\r\n loading = false;\r\n render();\r\n } else if (key === 'e' || key === 'E') {\r\n loading = true;\r\n await launchAction('execute', tickets[selected]);\r\n loading = false;\r\n } else if (key === 'r' || key === 'R') {\r\n loading = true;\r\n await launchAction('review', tickets[selected]);\r\n loading = false;\r\n } else if (key === 'q' || key === 'Q' || key === '\\u0003') {\r\n cleanup();\r\n process.exit(0);\r\n }\r\n } else if (screen === 'detail') {\r\n if (key === '\\u001b' && key.length === 1) {\r\n // Bare Escape — back to list\r\n screen = 'list';\r\n detailTicket = null;\r\n render();\r\n } else if (key === '\\u007f' || key === '\\b') {\r\n // Backspace — back to list\r\n screen = 'list';\r\n detailTicket = null;\r\n render();\r\n } else if (key === 'e' || key === 'E') {\r\n if (detailTicket) {\r\n loading = true;\r\n await launchAction('execute', detailTicket);\r\n loading = false;\r\n }\r\n } else if (key === 'r' || key === 'R') {\r\n if (detailTicket) {\r\n loading = true;\r\n await launchAction('review', detailTicket);\r\n loading = false;\r\n }\r\n } else if (key === 'c' || key === 'C') {\r\n if (detailTicket) {\r\n const plain = formatTicketPlainText(detailTicket, memberNames);\r\n try {\r\n copyToClipboard(plain);\r\n process.stdout.write(chalk.green('\\n Copied to clipboard!\\n'));\r\n } catch {\r\n process.stdout.write(\r\n chalk.yellow('\\n Could not copy — install xclip or xsel on Linux.\\n')\r\n );\r\n }\r\n await new Promise((r) => setTimeout(r, 800));\r\n render();\r\n }\r\n } else if (key === 'q' || key === 'Q' || key === '\\u0003') {\r\n cleanup();\r\n process.exit(0);\r\n }\r\n }\r\n }\r\n\r\n process.stdin.setRawMode(true);\r\n process.stdin.resume();\r\n process.stdin.setEncoding('utf-8');\r\n render();\r\n\r\n return new Promise<void>(() => {\r\n process.stdin.on('data', onData);\r\n\r\n process.once('SIGINT', () => {\r\n cleanup();\r\n process.exit(0);\r\n });\r\n });\r\n}\r\n","import chalk from 'chalk';\r\nimport * as ConfigService from '../services/config.service';\r\nimport type { ForgeConfig } from '../services/config.service';\r\nimport { isLoggedIn } from '../services/auth.service';\r\n\r\n/**\r\n * Loads config and verifies the user is authenticated.\r\n * Exits with code 1 and a friendly message if not logged in.\r\n */\r\nexport async function requireAuth(): Promise<ForgeConfig> {\r\n const config = await ConfigService.load();\r\n\r\n if (!isLoggedIn(config)) {\r\n console.error(chalk.red('Not logged in. Run `forge login` first.'));\r\n process.exit(1);\r\n }\r\n\r\n return config!;\r\n}\r\n","import { API_URL } from '../config';\r\nimport type { ForgeConfig } from './config.service';\r\nimport { refresh } from './auth.service';\r\nimport { save } from './config.service';\r\n\r\nconst RETRY_DELAY_MS = 2_000;\r\n\r\n// ---------------------------------------------------------------------------\r\n// Typed API error — callers can check `err instanceof ApiError` + statusCode\r\n// ---------------------------------------------------------------------------\r\nexport class ApiError extends Error {\r\n constructor(\r\n public readonly statusCode: number,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = 'ApiError';\r\n }\r\n}\r\n\r\nfunction friendlyHttpError(status: number): string {\r\n switch (status) {\r\n case 403:\r\n return 'You do not have permission. Check your team membership or run `forge login`.';\r\n case 404:\r\n return 'Resource not found. Check the ID and try again.';\r\n case 429:\r\n return 'Rate limited. Wait a moment and try again.';\r\n default:\r\n return `Unexpected server response (${status}). Try again or check https://status.forge-ai.dev.`;\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Internal helpers\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction buildUrl(path: string, params?: Record<string, string>): string {\r\n const url = new URL(`${API_URL}${path}`);\r\n if (params) {\r\n for (const [key, value] of Object.entries(params)) {\r\n url.searchParams.set(key, value);\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\nasync function makeRequest(\r\n url: string,\r\n accessToken: string,\r\n options?: { method?: string; body?: string; teamId?: string }\r\n): Promise<Response> {\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${accessToken}`,\r\n 'Content-Type': 'application/json',\r\n };\r\n if (options?.teamId) {\r\n headers['x-team-id'] = options.teamId;\r\n }\r\n return fetch(url, {\r\n method: options?.method ?? 'GET',\r\n body: options?.body,\r\n headers,\r\n });\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Shared request pipeline: network → 401 refresh → 5xx retry → friendly error\r\n// ---------------------------------------------------------------------------\r\n\r\ninterface RequestOptions {\r\n url: string;\r\n config: ForgeConfig;\r\n method?: string;\r\n body?: string;\r\n}\r\n\r\nasync function request<T>(opts: RequestOptions): Promise<T> {\r\n const { url, config, method, body } = opts;\r\n const reqOptions = { method, body, teamId: config.teamId };\r\n let res: Response;\r\n\r\n // 1. Network error handling\r\n try {\r\n res = await makeRequest(url, config.accessToken, reqOptions);\r\n } catch {\r\n throw new Error(\r\n 'Cannot reach Forge server. Check your connection or try again later.'\r\n );\r\n }\r\n\r\n // 2. Token refresh on 401\r\n if (res.status === 401) {\r\n let refreshed;\r\n try {\r\n refreshed = await refresh(config.refreshToken);\r\n } catch {\r\n throw new Error(\r\n 'Session expired. Run `forge login` to re-authenticate.'\r\n );\r\n }\r\n\r\n const updatedConfig = {\r\n ...config,\r\n accessToken: refreshed.accessToken,\r\n expiresAt: refreshed.expiresAt,\r\n };\r\n await save(updatedConfig);\r\n\r\n // Retry once with new token\r\n try {\r\n res = await makeRequest(url, refreshed.accessToken, reqOptions);\r\n } catch {\r\n throw new Error(\r\n 'Cannot reach Forge server. Check your connection or try again later.'\r\n );\r\n }\r\n\r\n if (res.status === 401) {\r\n throw new Error(\r\n 'Session expired. Run `forge login` to re-authenticate.'\r\n );\r\n }\r\n }\r\n\r\n // 3. 5xx: retry once after delay\r\n if (res.status >= 500) {\r\n await sleep(RETRY_DELAY_MS);\r\n\r\n try {\r\n res = await makeRequest(url, config.accessToken, reqOptions);\r\n } catch {\r\n throw new Error(\r\n 'Cannot reach Forge server. Check your connection or try again later.'\r\n );\r\n }\r\n\r\n if (res.status >= 500) {\r\n throw new Error(\r\n `Forge server error (${res.status}). Try again in a moment, or check https://status.forge.app.`\r\n );\r\n }\r\n }\r\n\r\n // 4. Other non-OK: throw typed ApiError with friendly message\r\n if (!res.ok) {\r\n throw new ApiError(res.status, friendlyHttpError(res.status));\r\n }\r\n\r\n return res.json() as Promise<T>;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Public API — thin wrappers around request()\r\n// ---------------------------------------------------------------------------\r\n\r\nexport async function get<T>(\r\n path: string,\r\n config: ForgeConfig,\r\n params?: Record<string, string>\r\n): Promise<T> {\r\n return request<T>({ url: buildUrl(path, params), config });\r\n}\r\n\r\nexport async function post<T>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n config: ForgeConfig,\r\n): Promise<T> {\r\n return request<T>({\r\n url: buildUrl(path),\r\n config,\r\n method: 'POST',\r\n body: JSON.stringify(body),\r\n });\r\n}\r\n\r\nexport async function patch<T>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n config: ForgeConfig,\r\n): Promise<T> {\r\n return request<T>({\r\n url: buildUrl(path),\r\n config,\r\n method: 'PATCH',\r\n body: JSON.stringify(body),\r\n });\r\n}\r\n","import chalk from 'chalk';\r\nimport { AECStatus, type TicketListItem } from '../types/ticket';\r\n\r\nconst STATUS_ICONS: Record<AECStatus, string> = {\r\n [AECStatus.DRAFT]: '⬜',\r\n [AECStatus.VALIDATED]: '✅',\r\n [AECStatus.READY]: '🚀',\r\n [AECStatus.WAITING_FOR_APPROVAL]: '⏳',\r\n [AECStatus.CREATED]: '📝',\r\n [AECStatus.DRIFTED]: '⚠️ ',\r\n [AECStatus.COMPLETE]: '✅',\r\n};\r\n\r\n/** Human-readable display names for all backend statuses (not the lifecycle steps). */\r\nexport const STATUS_DISPLAY_NAMES: Record<AECStatus, string> = {\r\n [AECStatus.DRAFT]: 'Define',\r\n [AECStatus.VALIDATED]: 'Dev-Refine',\r\n [AECStatus.READY]: 'Execute',\r\n [AECStatus.WAITING_FOR_APPROVAL]: 'Approve',\r\n [AECStatus.CREATED]: 'Exported',\r\n [AECStatus.DRIFTED]: 'Drifted',\r\n [AECStatus.COMPLETE]: 'Done',\r\n};\r\n\r\nexport function statusIcon(status: AECStatus): string {\r\n return STATUS_ICONS[status] ?? '❓';\r\n}\r\n\r\nexport function formatTicketRow(\r\n ticket: TicketListItem,\r\n selected: boolean,\r\n memberNames?: Map<string, string>\r\n): string {\r\n const pointer = selected ? chalk.cyan('▶') : ' ';\r\n const id = chalk.dim(`[${ticket.id}]`.padEnd(12));\r\n const title = ticket.title.substring(0, 40).padEnd(40);\r\n const displayTitle = selected ? chalk.bold.cyan(title) : title;\r\n const icon = statusIcon(ticket.status);\r\n const statusText = chalk.dim(\r\n (STATUS_DISPLAY_NAMES[ticket.status] ?? ticket.status).padEnd(24)\r\n );\r\n const rawAssignee = ticket.assignedTo;\r\n const assigneeName = rawAssignee\r\n ? memberNames?.get(rawAssignee) ?? rawAssignee\r\n : '';\r\n const assignee = assigneeName ? chalk.dim(assigneeName) : '';\r\n\r\n return `${pointer} ${id} ${displayTitle} ${icon} ${statusText} ${assignee}`;\r\n}\r\n","// TODO: Replace with @forge/types when published (Epic 8)\r\n\r\nexport enum AECStatus {\r\n DRAFT = 'draft',\r\n VALIDATED = 'validated',\r\n READY = 'ready',\r\n WAITING_FOR_APPROVAL = 'waiting-for-approval',\r\n CREATED = 'created',\r\n DRIFTED = 'drifted',\r\n COMPLETE = 'complete',\r\n}\r\n\r\nexport type TicketType = 'feature' | 'bug' | 'task';\r\n\r\nexport type TicketPriority = 'low' | 'medium' | 'high' | 'urgent';\r\n\r\nexport interface TicketListItem {\r\n id: string;\r\n title: string;\r\n status: AECStatus;\r\n priority?: TicketPriority;\r\n assignedTo?: string | null;\r\n}\r\n\r\nexport interface FileChange {\r\n path: string;\r\n action: 'create' | 'modify' | 'delete';\r\n notes?: string;\r\n}\r\n\r\nexport interface ReviewQAItem {\r\n question: string;\r\n answer: string;\r\n}\r\n\r\nexport interface ReviewSession {\r\n qaItems: ReviewQAItem[];\r\n submittedAt: string;\r\n}\r\n\r\nexport interface TicketDetail extends TicketListItem {\r\n description?: string;\r\n acceptanceCriteria: string[];\r\n createdAt: string;\r\n updatedAt: string;\r\n // Rich fields returned by the backend for full ticket context\r\n fileChanges?: FileChange[];\r\n problemStatement?: string;\r\n solution?: string;\r\n apiChanges?: string;\r\n testPlan?: string;\r\n designRefs?: string[];\r\n reviewSession?: ReviewSession | null;\r\n}\r\n\r\n// TicketContextResult is the shape returned by the get_ticket_context MCP tool.\r\n// It maps directly to TicketDetail — the full ticket serialized for Claude.\r\nexport type TicketContextResult = TicketDetail;\r\n","import { spawn } from 'child_process';\r\n\r\nexport function spawnClaude(action: 'execute' | 'review', ticketId: string): Promise<number> {\r\n const promptName = action === 'execute' ? 'forge-exec' : 'review';\r\n const prompt = `Use the ${promptName} MCP prompt with ticketId \"${ticketId}\" to ${action} this ticket.`;\r\n\r\n // Windows: claude is installed as a .cmd shim which requires a shell to\r\n // execute. Passing args *and* `shell: true` triggers DEP0190, so we\r\n // invoke cmd.exe explicitly with /c to run the .cmd, keeping args safe.\r\n // Unix/macOS: spawn the binary directly — no shell needed.\r\n const child = process.platform === 'win32'\r\n ? spawn('cmd', ['/c', 'claude', prompt], { stdio: 'inherit' })\r\n : spawn('claude', [prompt], { stdio: 'inherit' });\r\n\r\n return new Promise((resolve, reject) => {\r\n child.on('error', (err) => {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT')\r\n reject(new Error('Claude CLI not found. Install: https://docs.anthropic.com/en/docs/claude-code'));\r\n else reject(err);\r\n });\r\n child.on('close', (code) => resolve(code ?? 0));\r\n });\r\n}\r\n","import { execSync } from 'child_process';\r\n\r\nexport function copyToClipboard(text: string): void {\r\n const platform = process.platform;\r\n if (platform === 'darwin') {\r\n execSync('pbcopy', { input: text });\r\n } else if (platform === 'win32') {\r\n execSync(\r\n 'powershell -NoProfile -Command \"$input | Set-Clipboard\"',\r\n { input: text }\r\n );\r\n } else {\r\n // Linux: try xclip, fall back to xsel\r\n try {\r\n execSync('xclip -selection clipboard', { input: text });\r\n } catch {\r\n execSync('xsel --clipboard --input', { input: text });\r\n }\r\n }\r\n}\r\n","import { type TicketDetail } from '../types/ticket';\r\nimport { STATUS_DISPLAY_NAMES } from './formatters';\r\n\r\n/**\r\n * Format a TicketDetail as plain text for clipboard copying.\r\n * No chalk colors — just structured plain text.\r\n */\r\nexport function formatTicketPlainText(\r\n ticket: TicketDetail,\r\n memberNames?: Map<string, string>\r\n): string {\r\n const lines: string[] = [];\r\n\r\n // Header\r\n lines.push(`[${ticket.id}] ${ticket.title}`);\r\n lines.push('-'.repeat(72));\r\n\r\n // Metadata\r\n const statusName = STATUS_DISPLAY_NAMES[ticket.status] ?? ticket.status;\r\n lines.push(`Status: ${statusName}`);\r\n if (ticket.priority) {\r\n lines.push(`Priority: ${ticket.priority.toUpperCase()}`);\r\n }\r\n if (ticket.assignedTo) {\r\n const name = memberNames?.get(ticket.assignedTo) ?? ticket.assignedTo;\r\n lines.push(`Assignee: ${name}`);\r\n }\r\n\r\n // Description\r\n if (ticket.description) {\r\n lines.push('', '## Description', ticket.description);\r\n }\r\n\r\n // Problem Statement\r\n if (ticket.problemStatement) {\r\n lines.push('', '## Problem Statement', ticket.problemStatement);\r\n }\r\n\r\n // Solution\r\n if (ticket.solution) {\r\n lines.push('', '## Solution', ticket.solution);\r\n }\r\n\r\n // Acceptance Criteria\r\n if (ticket.acceptanceCriteria.length > 0) {\r\n lines.push('', '## Acceptance Criteria');\r\n ticket.acceptanceCriteria.forEach((ac, i) => {\r\n lines.push(` ${i + 1}. ${ac}`);\r\n });\r\n }\r\n\r\n // File Changes\r\n if (ticket.fileChanges && ticket.fileChanges.length > 0) {\r\n lines.push('', '## File Changes');\r\n for (const fc of ticket.fileChanges) {\r\n const notes = fc.notes ? ` - ${fc.notes}` : '';\r\n lines.push(` ${fc.action.toUpperCase()} ${fc.path}${notes}`);\r\n }\r\n }\r\n\r\n // API Changes\r\n if (ticket.apiChanges) {\r\n lines.push('', '## API Changes', ticket.apiChanges);\r\n }\r\n\r\n // Test Plan\r\n if (ticket.testPlan) {\r\n lines.push('', '## Test Plan', ticket.testPlan);\r\n }\r\n\r\n // Design References\r\n if (ticket.designRefs && ticket.designRefs.length > 0) {\r\n lines.push('', '## Design References');\r\n for (const ref of ticket.designRefs) {\r\n lines.push(` - ${ref}`);\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport { requireAuth } from '../middleware/auth-guard';\r\nimport * as ApiService from '../services/api.service';\r\nimport { ApiError } from '../services/api.service';\r\nimport { printTicketDetail } from '../ui/pager';\r\nimport type { TicketDetail } from '../types/ticket';\r\n\r\nexport const showCommand = new Command('show')\r\n .description('Show full details of a ticket')\r\n .argument('<ticketId>', 'The ticket ID to display')\r\n .action(async (ticketId: string) => {\r\n try {\r\n const config = await requireAuth();\r\n\r\n let ticket: TicketDetail;\r\n try {\r\n ticket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticketId}`,\r\n config\r\n );\r\n } catch (err) {\r\n if (err instanceof ApiError && err.statusCode === 404) {\r\n console.error(chalk.red(`Ticket not found: ${ticketId}`));\r\n process.exit(1);\r\n }\r\n throw err;\r\n }\r\n\r\n printTicketDetail(ticket);\r\n process.exit(0);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(2);\r\n }\r\n });\r\n","import chalk from 'chalk';\r\nimport { type TicketDetail } from '../types/ticket';\r\nimport { statusIcon, STATUS_DISPLAY_NAMES } from './formatters';\r\n\r\nconst DIVIDER = chalk.dim('─'.repeat(72));\r\n\r\nconst PRIORITY_COLOR: Record<string, (s: string) => string> = {\r\n urgent: chalk.red,\r\n high: chalk.yellow,\r\n medium: chalk.white,\r\n low: chalk.dim,\r\n};\r\n\r\nexport function printTicketDetail(ticket: TicketDetail): void {\r\n console.log();\r\n\r\n // Header\r\n console.log(chalk.bold(`[${ticket.id}] ${ticket.title}`));\r\n console.log(DIVIDER);\r\n\r\n // Metadata\r\n const icon = statusIcon(ticket.status);\r\n const statusText = STATUS_DISPLAY_NAMES[ticket.status] ?? ticket.status;\r\n console.log(`${chalk.dim('Status: ')} ${icon} ${chalk.bold(statusText)}`);\r\n\r\n if (ticket.priority) {\r\n const colorFn = PRIORITY_COLOR[ticket.priority] ?? chalk.white;\r\n console.log(\r\n `${chalk.dim('Priority: ')} ${colorFn(ticket.priority.toUpperCase())}`\r\n );\r\n }\r\n\r\n const assignee = ticket.assignedTo;\r\n if (assignee) {\r\n console.log(`${chalk.dim('Assignee: ')} ${assignee}`);\r\n }\r\n\r\n console.log(\r\n `${chalk.dim('Created: ')} ${new Date(ticket.createdAt).toLocaleString()}`\r\n );\r\n console.log(\r\n `${chalk.dim('Updated: ')} ${new Date(ticket.updatedAt).toLocaleString()}`\r\n );\r\n\r\n // Description\r\n if (ticket.description) {\r\n console.log();\r\n console.log(chalk.bold.underline('Description'));\r\n console.log(ticket.description);\r\n }\r\n\r\n // Acceptance Criteria\r\n if (ticket.acceptanceCriteria.length > 0) {\r\n console.log();\r\n console.log(chalk.bold.underline('Acceptance Criteria'));\r\n ticket.acceptanceCriteria.forEach((ac, i) => {\r\n console.log(` ${chalk.dim(`${i + 1}.`)} ${ac}`);\r\n });\r\n }\r\n\r\n // Footer\r\n console.log();\r\n console.log(DIVIDER);\r\n console.log(chalk.dim(`forge review ${ticket.id} # start AI-assisted review`));\r\n console.log(\r\n chalk.dim(`forge execute ${ticket.id} # start AI-assisted execution`)\r\n );\r\n console.log();\r\n}\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport { requireAuth } from '../middleware/auth-guard';\r\nimport * as ApiService from '../services/api.service';\r\nimport { ApiError } from '../services/api.service';\r\nimport { statusIcon } from '../ui/formatters';\r\nimport { AECStatus, type TicketDetail } from '../types/ticket';\r\nimport { spawnClaude } from '../services/claude.service';\r\n\r\nconst REVIEW_VALID_STATUSES = new Set<AECStatus>([\r\n AECStatus.READY,\r\n AECStatus.VALIDATED,\r\n AECStatus.CREATED,\r\n AECStatus.DRIFTED,\r\n]);\r\n\r\nexport const reviewCommand = new Command('review')\r\n .description('Start an AI-assisted review session for a ticket')\r\n .argument('<ticketId>', 'The ticket ID to review')\r\n .action(async (ticketId: string) => {\r\n try {\r\n const config = await requireAuth();\r\n\r\n let ticket: TicketDetail;\r\n try {\r\n ticket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticketId}`,\r\n config\r\n );\r\n } catch (err) {\r\n if (err instanceof ApiError && err.statusCode === 404) {\r\n console.error(chalk.red(`Ticket not found: ${ticketId}`));\r\n process.exit(1);\r\n }\r\n throw err;\r\n }\r\n\r\n if (!REVIEW_VALID_STATUSES.has(ticket.status)) {\r\n console.error(\r\n chalk.yellow(\r\n `Ticket ${ticketId} has status ${ticket.status} which is not ready for review.`\r\n )\r\n );\r\n console.error(\r\n chalk.dim(\r\n `Valid statuses for review: READY, VALIDATED, CREATED, DRIFTED`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n const icon = statusIcon(ticket.status);\r\n\r\n try {\r\n await ApiService.patch(`/tickets/${ticketId}`, { assignedTo: config.userId }, config);\r\n } catch {\r\n process.stderr.write(chalk.dim(' Warning: Could not auto-assign ticket.\\n'));\r\n }\r\n\r\n process.stderr.write(\r\n `\\n${icon} Reviewing [${ticket.id}] ${ticket.title} — launching Claude...\\n\\n`\r\n );\r\n const exitCode = await spawnClaude('review', ticket.id);\r\n process.exit(exitCode);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(2);\r\n }\r\n });\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport { requireAuth } from '../middleware/auth-guard';\r\nimport * as ApiService from '../services/api.service';\r\nimport { ApiError } from '../services/api.service';\r\nimport { statusIcon } from '../ui/formatters';\r\nimport { AECStatus, type TicketDetail } from '../types/ticket';\r\nimport { spawnClaude } from '../services/claude.service';\r\n\r\nconst EXECUTE_VALID_STATUSES = new Set<AECStatus>([\r\n AECStatus.READY,\r\n AECStatus.VALIDATED,\r\n]);\r\n\r\nexport const executeCommand = new Command('execute')\r\n .description('Start an AI-assisted execution session for a ticket')\r\n .argument('<ticketId>', 'The ticket ID to execute')\r\n .action(async (ticketId: string) => {\r\n try {\r\n const config = await requireAuth();\r\n\r\n let ticket: TicketDetail;\r\n try {\r\n ticket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticketId}`,\r\n config\r\n );\r\n } catch (err) {\r\n if (err instanceof ApiError && err.statusCode === 404) {\r\n console.error(chalk.red(`Ticket not found: ${ticketId}`));\r\n process.exit(1);\r\n }\r\n throw err;\r\n }\r\n\r\n if (!EXECUTE_VALID_STATUSES.has(ticket.status)) {\r\n console.error(\r\n chalk.yellow(\r\n `Ticket ${ticketId} has status ${ticket.status} which is not ready for execution.`\r\n )\r\n );\r\n console.error(\r\n chalk.dim(`Valid statuses for execute: READY, VALIDATED`)\r\n );\r\n console.error(\r\n chalk.dim(\r\n `Run \\`forge review ${ticketId}\\` first to prepare the ticket.`\r\n )\r\n );\r\n process.exit(1);\r\n }\r\n\r\n const icon = statusIcon(ticket.status);\r\n\r\n try {\r\n await ApiService.patch(`/tickets/${ticketId}`, { assignedTo: config.userId }, config);\r\n } catch {\r\n process.stderr.write(chalk.dim(' Warning: Could not auto-assign ticket.\\n'));\r\n }\r\n\r\n process.stderr.write(\r\n `\\n${icon} Executing [${ticket.id}] ${ticket.title} — launching Claude...\\n\\n`\r\n );\r\n const exitCode = await spawnClaude('execute', ticket.id);\r\n process.exit(exitCode);\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(2);\r\n }\r\n });\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport { requireAuth } from '../middleware/auth-guard';\r\nimport { ForgeMCPServer } from '../mcp/server.js';\r\nimport { tryRegisterMcpServer, writeMcpJson } from '../mcp/install.js';\r\n\r\nconst DIVIDER = '─'.repeat(72);\r\n\r\nexport const mcpCommand = new Command('mcp')\r\n .description('Forge MCP server — run as daemon or install for Claude Code')\r\n .action(async () => {\r\n // forge mcp — persistent daemon. Claude Code spawns this process from\r\n // .mcp.json / ~/.claude.json config and keeps it running as an MCP server.\r\n // All output MUST go to stderr — stdout is reserved for MCP protocol frames.\r\n try {\r\n const config = await requireAuth();\r\n\r\n const server = new ForgeMCPServer(config);\r\n await server.start();\r\n\r\n process.once('SIGINT', () => {\r\n server.stop().then(() => process.exit(0));\r\n });\r\n\r\n // Block indefinitely — Claude Code kills this process when done\r\n await new Promise<void>(() => {\r\n // intentionally never resolves\r\n });\r\n } catch (err) {\r\n process.stderr.write(\r\n chalk.red(`[forge:mcp] Fatal error: ${(err as Error).message}\\n`)\r\n );\r\n process.exit(1);\r\n }\r\n });\r\n\r\nmcpCommand\r\n .command('install')\r\n .description('Write .mcp.json and register forge as a project-scoped MCP server')\r\n .action(async () => {\r\n console.log();\r\n console.log(chalk.dim(DIVIDER));\r\n console.log(' Forge MCP Server — Project Setup');\r\n console.log(chalk.dim(DIVIDER));\r\n console.log();\r\n\r\n // Write / merge .mcp.json in project root\r\n try {\r\n await writeMcpJson();\r\n console.log(\r\n ` ${chalk.green('✅')} Written ${chalk.bold('.mcp.json')} ` +\r\n chalk.dim('(project scope — commit this file for your team)')\r\n );\r\n } catch (err) {\r\n console.error(\r\n chalk.red(` ✗ Failed to write .mcp.json: ${(err as Error).message}`)\r\n );\r\n process.exit(1);\r\n }\r\n\r\n // Attempt claude mcp add (project scope)\r\n const result = await tryRegisterMcpServer('project');\r\n if (result === 'registered') {\r\n console.log(\r\n ` ${chalk.green('✅')} Registered via: ` +\r\n chalk.dim('claude mcp add --scope project ...')\r\n );\r\n } else {\r\n console.log(\r\n ` ${chalk.dim('ℹ')} claude CLI not found — .mcp.json is ready but not auto-registered`\r\n );\r\n }\r\n\r\n console.log();\r\n console.log(' Restart Claude Code to apply. Per-ticket usage:');\r\n console.log(chalk.dim(' forge execute T-001 → invoke forge-exec prompt in Claude Code'));\r\n console.log(chalk.dim(' forge review T-001 → invoke review prompt in Claude Code'));\r\n console.log();\r\n console.log(chalk.dim(DIVIDER));\r\n console.log();\r\n });\r\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport {\r\n ListToolsRequestSchema,\r\n ListPromptsRequestSchema,\r\n GetPromptRequestSchema,\r\n CallToolRequestSchema,\r\n} from '@modelcontextprotocol/sdk/types.js';\r\nimport type { ForgeConfig } from '../services/config.service.js';\r\nimport {\r\n getTicketContextToolDefinition,\r\n handleGetTicketContext,\r\n} from './tools/get-ticket-context.js';\r\nimport {\r\n getFileChangesToolDefinition,\r\n handleGetFileChanges,\r\n} from './tools/get-file-changes.js';\r\nimport {\r\n getRepositoryContextToolDefinition,\r\n handleGetRepositoryContext,\r\n} from './tools/get-repository-context.js';\r\nimport {\r\n updateTicketStatusToolDefinition,\r\n handleUpdateTicketStatus,\r\n} from './tools/update-ticket-status.js';\r\nimport {\r\n submitReviewSessionToolDefinition,\r\n handleSubmitReviewSession,\r\n} from './tools/submit-review-session.js';\r\nimport {\r\n listTicketsToolDefinition,\r\n handleListTickets,\r\n} from './tools/list-tickets.js';\r\nimport {\r\n forgeExecutePromptDefinition,\r\n handleForgeExecute,\r\n} from './prompts/forge-execute.js';\r\nimport {\r\n forgeReviewPromptDefinition,\r\n handleForgeReview,\r\n} from './prompts/forge-review.js';\r\nimport {\r\n forgeListPromptDefinition,\r\n handleForgeList,\r\n} from './prompts/forge-list.js';\r\nimport {\r\n forgeExecPromptDefinition,\r\n handleForgeExec,\r\n} from './prompts/forge-exec.js';\r\n\r\nexport class ForgeMCPServer {\r\n private server: Server;\r\n private transport: StdioServerTransport | null = null;\r\n\r\n constructor(private config: ForgeConfig) {\r\n this.server = new Server(\r\n { name: 'forge', version: '1.0.0' },\r\n { capabilities: { tools: {}, prompts: {} } }\r\n );\r\n\r\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\r\n tools: [getTicketContextToolDefinition, getFileChangesToolDefinition, getRepositoryContextToolDefinition, updateTicketStatusToolDefinition, submitReviewSessionToolDefinition, listTicketsToolDefinition],\r\n }));\r\n\r\n this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({\r\n prompts: [forgeListPromptDefinition, forgeExecPromptDefinition, forgeExecutePromptDefinition, forgeReviewPromptDefinition],\r\n }));\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n this.server.setRequestHandler(GetPromptRequestSchema, async (request): Promise<any> => {\r\n const { name, arguments: args = {} } = request.params;\r\n switch (name) {\r\n case 'forge-execute':\r\n return handleForgeExecute(args as Record<string, unknown>, this.config);\r\n case 'review':\r\n return handleForgeReview(args as Record<string, unknown>, this.config);\r\n case 'list':\r\n return handleForgeList(args as Record<string, unknown>, this.config);\r\n case 'forge-exec':\r\n return handleForgeExec(args as Record<string, unknown>, this.config);\r\n default:\r\n return {\r\n messages: [{ role: 'user' as const, content: { type: 'text' as const, text: `Error: Unknown prompt: ${name}` } }],\r\n };\r\n }\r\n });\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n this.server.setRequestHandler(CallToolRequestSchema, async (request): Promise<any> => {\r\n const { name, arguments: args = {} } = request.params;\r\n\r\n switch (name) {\r\n case 'get_ticket_context':\r\n return handleGetTicketContext(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n case 'get_file_changes':\r\n return handleGetFileChanges(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n case 'get_repository_context':\r\n return handleGetRepositoryContext(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n case 'update_ticket_status':\r\n return handleUpdateTicketStatus(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n case 'submit_review_session':\r\n return handleSubmitReviewSession(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n case 'list_tickets':\r\n return handleListTickets(\r\n args as Record<string, unknown>,\r\n this.config\r\n );\r\n default:\r\n return {\r\n content: [{ type: 'text' as const, text: `Unknown tool: ${name}` }],\r\n isError: true,\r\n };\r\n }\r\n });\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.transport = new StdioServerTransport();\r\n await this.server.connect(this.transport);\r\n process.stderr.write('[forge:mcp] server started\\n');\r\n }\r\n\r\n async stop(): Promise<void> {\r\n if (!this.transport) return;\r\n try {\r\n await this.server.close();\r\n } catch {\r\n // Ignore errors during graceful shutdown\r\n }\r\n this.transport = null;\r\n process.stderr.write('[forge:mcp] server stopped\\n');\r\n }\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { TicketDetail, FileChange } from '../../types/ticket.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\nfunction formatTicket(t: TicketDetail): string {\r\n const lines: string[] = [\r\n `Ticket: ${t.id}`,\r\n `Title: ${t.title}`,\r\n `Status: ${t.status}`,\r\n ];\r\n if (t.priority) lines.push(`Priority: ${t.priority}`);\r\n if (t.assignedTo) lines.push(`Assigned to: ${t.assignedTo}`);\r\n lines.push('');\r\n if (t.description) lines.push(`Description:\\n${t.description}\\n`);\r\n if (t.problemStatement) lines.push(`Problem Statement:\\n${t.problemStatement}\\n`);\r\n if (t.solution) lines.push(`Solution:\\n${t.solution}\\n`);\r\n if (t.acceptanceCriteria?.length) {\r\n lines.push('Acceptance Criteria:');\r\n t.acceptanceCriteria.forEach((ac, i) => lines.push(` ${i + 1}. ${ac}`));\r\n lines.push('');\r\n }\r\n if (t.fileChanges?.length) {\r\n lines.push('File Changes:');\r\n t.fileChanges.forEach((fc: FileChange) => {\r\n const note = fc.notes ? ` — ${fc.notes}` : '';\r\n lines.push(` [${fc.action}] ${fc.path}${note}`);\r\n });\r\n lines.push('');\r\n }\r\n if (t.apiChanges) lines.push(`API Changes:\\n${t.apiChanges}\\n`);\r\n if (t.testPlan) lines.push(`Test Plan:\\n${t.testPlan}\\n`);\r\n if (t.reviewSession?.qaItems?.length) {\r\n lines.push('Review Q&A:');\r\n t.reviewSession.qaItems.forEach((qa, i) => {\r\n lines.push(` Q${i + 1}: ${qa.question}`);\r\n lines.push(` A${i + 1}: ${qa.answer}`);\r\n });\r\n lines.push('');\r\n }\r\n return lines.join('\\n');\r\n}\r\n\r\n// MCP tool definition — returned by ListTools so Claude Code can discover it\r\nexport const getTicketContextToolDefinition = {\r\n name: 'get_ticket_context',\r\n description:\r\n 'Fetch the full specification for a Forge ticket including problem statement, solution, acceptance criteria, and file changes. Use this to understand what to implement.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n ticketId: {\r\n type: 'string',\r\n description: 'The ticket ID to fetch (e.g., T-001)',\r\n },\r\n },\r\n required: ['ticketId'],\r\n },\r\n};\r\n\r\n/**\r\n * Handles the get_ticket_context MCP tool call.\r\n * Fetches the full ticket from the Forge backend and returns it as a JSON string.\r\n * Never throws — all errors are returned as isError content.\r\n */\r\nexport async function handleGetTicketContext(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const ticketId = args['ticketId'];\r\n\r\n if (!ticketId || typeof ticketId !== 'string' || ticketId.trim() === '') {\r\n return {\r\n content: [{ type: 'text', text: 'Missing required argument: ticketId' }],\r\n isError: true,\r\n };\r\n }\r\n\r\n try {\r\n const ticket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticketId.trim()}`,\r\n config\r\n );\r\n return {\r\n content: [{ type: 'text', text: formatTicket(ticket) }],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n\r\n if (message.includes('404')) {\r\n return {\r\n content: [{ type: 'text', text: `Ticket not found: ${ticketId}` }],\r\n isError: true,\r\n };\r\n }\r\n\r\n return {\r\n content: [{ type: 'text', text: message }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { TicketDetail } from '../../types/ticket.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\n// MCP tool definition — returned by ListTools so Claude Code can discover it\r\nexport const getFileChangesToolDefinition = {\r\n name: 'get_file_changes',\r\n description:\r\n 'Fetch the list of files to create, modify, or delete for a Forge ticket. Returns a JSON array of file change objects with path, action, and optional notes. Use this to understand what files need to be touched during implementation.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n ticketId: {\r\n type: 'string',\r\n description: 'The ticket ID to fetch file changes for (e.g., T-001)',\r\n },\r\n },\r\n required: ['ticketId'],\r\n },\r\n};\r\n\r\n/**\r\n * Handles the get_file_changes MCP tool call.\r\n * Fetches the ticket from the Forge backend and returns only its fileChanges array.\r\n * Returns an empty array when the ticket has no fileChanges.\r\n * Never throws — all errors are returned as isError content.\r\n */\r\nexport async function handleGetFileChanges(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const ticketId = args['ticketId'];\r\n\r\n if (!ticketId || typeof ticketId !== 'string' || ticketId.trim() === '') {\r\n return {\r\n content: [{ type: 'text', text: 'Missing required argument: ticketId' }],\r\n isError: true,\r\n };\r\n }\r\n\r\n try {\r\n const ticket = await ApiService.get<TicketDetail>(\r\n `/tickets/${ticketId.trim()}`,\r\n config\r\n );\r\n const changes = ticket.fileChanges ?? [];\r\n if (changes.length === 0) {\r\n return {\r\n content: [{ type: 'text', text: 'No file changes specified for this ticket.' }],\r\n };\r\n }\r\n const lines = changes.map((fc) => {\r\n const note = fc.notes ? ` — ${fc.notes}` : '';\r\n return `[${fc.action}] ${fc.path}${note}`;\r\n });\r\n return {\r\n content: [{ type: 'text', text: `File changes for ${ticketId}:\\n${lines.join('\\n')}` }],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n\r\n if (message.includes('404')) {\r\n return {\r\n content: [{ type: 'text', text: `Ticket not found: ${ticketId}` }],\r\n isError: true,\r\n };\r\n }\r\n\r\n return {\r\n content: [{ type: 'text', text: message }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","import * as path from 'path';\r\nimport * as fs from 'fs';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport { GitService } from '../../services/git.service.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\n// MCP tool definition — returned by ListTools so Claude Code can discover it\r\nexport const getRepositoryContextToolDefinition = {\r\n name: 'get_repository_context',\r\n description:\r\n 'Fetch the current git repository context including active branch, working directory status (modified/untracked/staged files), and a file tree snapshot. Use this to understand the repository state before implementing changes.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n path: {\r\n type: 'string',\r\n description:\r\n 'Absolute path to the repository root. Defaults to the current working directory.',\r\n },\r\n },\r\n required: [],\r\n },\r\n};\r\n\r\n/** Returns true if the error message suggests a non-git directory. */\r\nfunction isNonGitError(message: string): boolean {\r\n const lower = message.toLowerCase();\r\n return (\r\n lower.includes('not a git') ||\r\n lower.includes('enoent') ||\r\n lower.includes('fatal') ||\r\n lower.includes('not a git repository')\r\n );\r\n}\r\n\r\n/**\r\n * Validates that the requested path is safe to access.\r\n * Must be within or equal to the current working directory.\r\n * Resolves symlinks to prevent traversal via symlink chains.\r\n */\r\nfunction validatePath(requested: string): string | null {\r\n const cwd = process.cwd();\r\n let resolved: string;\r\n try {\r\n // Resolve to absolute and normalize (handles .., symlinks, etc.)\r\n resolved = fs.realpathSync(path.resolve(cwd, requested));\r\n } catch {\r\n // Path doesn't exist — let git handle the error naturally\r\n resolved = path.resolve(cwd, requested);\r\n }\r\n const normalizedCwd = path.resolve(cwd);\r\n\r\n // Must be cwd itself or a child of cwd\r\n if (resolved !== normalizedCwd && !resolved.startsWith(normalizedCwd + path.sep)) {\r\n return null;\r\n }\r\n return resolved;\r\n}\r\n\r\n/**\r\n * Handles the get_repository_context MCP tool call.\r\n * Reads branch, status, and file tree from the git repository at the given path.\r\n * Returns a structured error (not a crash) when called outside a git repository.\r\n * Never throws — all errors are returned as isError content.\r\n */\r\nexport async function handleGetRepositoryContext(\r\n args: Record<string, unknown>,\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const pathArg = args['path'];\r\n let resolvedPath: string;\r\n\r\n if (typeof pathArg === 'string' && pathArg.trim() !== '') {\r\n const validated = validatePath(pathArg.trim());\r\n if (validated === null) {\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: 'Path must be within the current working directory',\r\n },\r\n ],\r\n isError: true,\r\n };\r\n }\r\n resolvedPath = validated;\r\n } else {\r\n resolvedPath = process.cwd();\r\n }\r\n\r\n const git = new GitService(resolvedPath);\r\n\r\n try {\r\n const [branch, status, fileTree] = await Promise.all([\r\n git.getBranch(),\r\n git.getStatus(),\r\n git.getFileTree(),\r\n ]);\r\n\r\n const statusLines: string[] = [];\r\n const s = status as { modified?: string[]; not_added?: string[]; staged?: string[] };\r\n if (s.modified?.length) statusLines.push(`Modified: ${s.modified.join(', ')}`);\r\n if (s.not_added?.length) statusLines.push(`Untracked: ${s.not_added.join(', ')}`);\r\n if (s.staged?.length) statusLines.push(`Staged: ${s.staged.join(', ')}`);\r\n const statusText = statusLines.length > 0 ? statusLines.join('\\n') : 'Clean working tree';\r\n\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: `Repository: ${resolvedPath}\\nBranch: ${branch}\\n\\nStatus:\\n${statusText}\\n\\nFiles:\\n${fileTree}`,\r\n },\r\n ],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n\r\n if (isNonGitError(message)) {\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: 'Not a git repository',\r\n },\r\n ],\r\n isError: true,\r\n };\r\n }\r\n\r\n return {\r\n content: [{ type: 'text', text: message }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","import simpleGit from 'simple-git';\r\n\r\nexport interface GitStatus {\r\n modified: string[];\r\n untracked: string[];\r\n staged: string[];\r\n}\r\n\r\n/**\r\n * Thin wrapper around simple-git for reading repository context.\r\n * All methods propagate errors — callers are responsible for handling\r\n * non-git directory errors.\r\n */\r\nexport class GitService {\r\n constructor(private repoPath: string) {}\r\n\r\n async getBranch(): Promise<string> {\r\n const result = await simpleGit(this.repoPath).branchLocal();\r\n return result.current;\r\n }\r\n\r\n async getStatus(): Promise<GitStatus> {\r\n const result = await simpleGit(this.repoPath).status();\r\n return {\r\n modified: result.modified,\r\n untracked: result.not_added, // simple-git uses 'not_added' for untracked files\r\n staged: result.staged,\r\n };\r\n }\r\n\r\n async getFileTree(): Promise<string> {\r\n const raw = await simpleGit(this.repoPath).raw([\r\n 'ls-tree',\r\n '--name-only',\r\n '-r',\r\n 'HEAD',\r\n ]);\r\n const lines = raw.split('\\n').filter(Boolean);\r\n return lines.slice(0, 200).join('\\n');\r\n }\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport { AECStatus, type TicketDetail } from '../../types/ticket.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\n// MCP tool definition — returned by ListTools so Claude Code can discover it\r\nexport const updateTicketStatusToolDefinition = {\r\n name: 'update_ticket_status',\r\n description:\r\n 'Update the status of a Forge ticket. Call this after completing implementation to mark the ticket as Exported (created), or to transition it to another valid status (Define=draft, Dev-Refine=validated, Approve=waiting-for-approval, Execute=ready, Done=complete).',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n ticketId: {\r\n type: 'string',\r\n description: 'The ticket ID to update (e.g., \"T-001\")',\r\n },\r\n status: {\r\n type: 'string',\r\n description: `New status value. Must be one of: ${Object.values(AECStatus).join(', ')}`,\r\n },\r\n },\r\n required: ['ticketId', 'status'],\r\n },\r\n};\r\n\r\n/**\r\n * Handles the update_ticket_status MCP tool call.\r\n * Validates inputs then calls PATCH /tickets/:id with the new status.\r\n * Returns { success: true, ticketId, newStatus } on success.\r\n * Never throws — all errors are returned as isError content.\r\n */\r\nexport async function handleUpdateTicketStatus(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const ticketId = args['ticketId'];\r\n const status = args['status'];\r\n\r\n // Validate ticketId\r\n if (!ticketId || typeof ticketId !== 'string' || ticketId.trim() === '') {\r\n return {\r\n content: [{ type: 'text', text: 'Missing required argument: ticketId' }],\r\n isError: true,\r\n };\r\n }\r\n\r\n // Validate status\r\n const validStatuses = Object.values(AECStatus) as string[];\r\n if (!status || typeof status !== 'string' || !validStatuses.includes(status)) {\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: `Invalid status: ${status}. Must be one of: ${validStatuses.join(', ')}`,\r\n },\r\n ],\r\n isError: true,\r\n };\r\n }\r\n\r\n try {\r\n const result = await ApiService.patch<TicketDetail>(\r\n `/tickets/${ticketId.trim()}`,\r\n { status },\r\n config\r\n );\r\n\r\n const displayStatus = result.status.replace(/-/g, ' ');\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: `Ticket ${ticketId.trim()} status updated to \"${displayStatus}\".`,\r\n },\r\n ],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n\r\n if (message.includes('404')) {\r\n return {\r\n content: [{ type: 'text', text: `Ticket not found: ${ticketId}` }],\r\n isError: true,\r\n };\r\n }\r\n\r\n return {\r\n content: [{ type: 'text', text: message }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { ReviewQAItem } from '../../types/ticket.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\n// MCP tool definition — returned by ListTools so Claude Code can discover it\r\nexport const submitReviewSessionToolDefinition = {\r\n name: 'submit_review_session',\r\n description:\r\n 'Submit the Q&A pairs collected during a forge review session back to Forge. Call this after the developer has answered all clarifying questions. The ticket status will transition to WAITING_FOR_APPROVAL and the PM will see the answers in the web UI.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n ticketId: {\r\n type: 'string',\r\n description: 'The ticket ID being reviewed (e.g., \"aec_abc123\")',\r\n },\r\n qaItems: {\r\n type: 'array',\r\n description: 'The Q&A pairs collected during the review session',\r\n items: {\r\n type: 'object',\r\n properties: {\r\n question: {\r\n type: 'string',\r\n description: 'The clarifying question that was asked',\r\n },\r\n answer: {\r\n type: 'string',\r\n description: \"The developer's answer to the question\",\r\n },\r\n },\r\n required: ['question', 'answer'],\r\n },\r\n minItems: 1,\r\n },\r\n },\r\n required: ['ticketId', 'qaItems'],\r\n },\r\n};\r\n\r\ninterface SubmitReviewSessionResponse {\r\n success: boolean;\r\n ticketId: string;\r\n status: string;\r\n}\r\n\r\n/**\r\n * Handles the submit_review_session MCP tool call.\r\n * Posts Q&A pairs to POST /tickets/:id/review-session.\r\n * The backend transitions the ticket to WAITING_FOR_APPROVAL.\r\n * Never throws — all errors are returned as isError content.\r\n */\r\nexport async function handleSubmitReviewSession(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const ticketId = args['ticketId'];\r\n const qaItems = args['qaItems'];\r\n\r\n // Validate ticketId\r\n if (!ticketId || typeof ticketId !== 'string' || ticketId.trim() === '') {\r\n return {\r\n content: [{ type: 'text', text: 'Missing required argument: ticketId' }],\r\n isError: true,\r\n };\r\n }\r\n\r\n // Validate qaItems\r\n if (!qaItems || !Array.isArray(qaItems) || qaItems.length === 0) {\r\n return {\r\n content: [{ type: 'text', text: 'qaItems must be a non-empty array of {question, answer} objects' }],\r\n isError: true,\r\n };\r\n }\r\n\r\n // Validate each Q&A item\r\n for (const item of qaItems) {\r\n if (\r\n typeof item !== 'object' ||\r\n item === null ||\r\n typeof (item as any).question !== 'string' ||\r\n typeof (item as any).answer !== 'string'\r\n ) {\r\n return {\r\n content: [{ type: 'text', text: 'Each qaItem must have string fields: question and answer' }],\r\n isError: true,\r\n };\r\n }\r\n }\r\n\r\n const validatedItems: ReviewQAItem[] = (qaItems as any[]).map((item) => ({\r\n question: item.question,\r\n answer: item.answer,\r\n }));\r\n\r\n try {\r\n const result = await ApiService.post<SubmitReviewSessionResponse>(\r\n `/tickets/${ticketId.trim()}/review-session`,\r\n { qaItems: validatedItems },\r\n config\r\n );\r\n\r\n const displayStatus = result.status.replace(/-/g, ' ');\r\n return {\r\n content: [\r\n {\r\n type: 'text',\r\n text: `Review session submitted for ${result.ticketId}. Status is now \"${displayStatus}\". The PM will see your answers and can re-bake the ticket.`,\r\n },\r\n ],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n\r\n if (message.includes('404')) {\r\n return {\r\n content: [{ type: 'text', text: `Ticket not found: ${ticketId}` }],\r\n isError: true,\r\n };\r\n }\r\n\r\n return {\r\n content: [{ type: 'text', text: message }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { TicketListItem } from '../../types/ticket.js';\r\nimport type { ToolResult } from '../types.js';\r\n\r\nexport const listTicketsToolDefinition = {\r\n name: 'list_tickets',\r\n description:\r\n 'List all Forge tickets for the current team. Returns ticket IDs, titles, statuses, priorities, and assignees. Use this to find ticket IDs before calling get_ticket_context or review prompts.',\r\n inputSchema: {\r\n type: 'object' as const,\r\n properties: {\r\n filter: {\r\n type: 'string',\r\n description: 'Show \"all\" team tickets (default) or \"mine\" for only tickets assigned to me',\r\n enum: ['all', 'mine'],\r\n },\r\n },\r\n required: [],\r\n },\r\n};\r\n\r\nexport async function handleListTickets(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> {\r\n const filter = typeof args.filter === 'string' ? args.filter.trim() : 'all';\r\n\r\n const params: Record<string, string> = {\r\n teamId: config.teamId,\r\n };\r\n\r\n if (filter === 'all') {\r\n params.all = 'true';\r\n } else {\r\n params.assignedToMe = 'true';\r\n }\r\n\r\n try {\r\n const tickets = await ApiService.get<TicketListItem[]>('/tickets', config, params);\r\n\r\n if (tickets.length === 0) {\r\n return {\r\n content: [{ type: 'text', text: `No tickets found (filter: ${filter}).` }],\r\n };\r\n }\r\n\r\n const lines = tickets.map((t) => {\r\n const status = t.status.replace(/-/g, ' ');\r\n const priority = t.priority ? ` [${t.priority}]` : '';\r\n const assignee = t.assignedTo ? ` (${t.assignedTo})` : '';\r\n return `${t.id} ${status.padEnd(20)} ${t.title}${priority}${assignee}`;\r\n });\r\n\r\n const header = `${tickets.length} ticket${tickets.length === 1 ? '' : 's'} (filter: ${filter}):\\n`;\r\n const hint = '\\n\\nTo review a ticket: /forge:review <ticketId>\\nTo execute a ticket: /forge:exec <ticketId>';\r\n\r\n return {\r\n content: [{ type: 'text', text: header + lines.join('\\n') + hint }],\r\n };\r\n } catch (err) {\r\n const message = (err as Error).message ?? String(err);\r\n return {\r\n content: [{ type: 'text', text: `Failed to list tickets: ${message}` }],\r\n isError: true,\r\n };\r\n }\r\n}\r\n","# Forge Dev Executor Agent\r\n\r\n## Persona\r\n\r\nYou are the **Forge Dev Executor** — an expert software engineer embedded inside Claude Code with direct access to the Forge ticket management system via MCP tools. Your purpose is to implement Forge tickets with precision, producing code that exactly matches the ticket's acceptance criteria and file change specifications.\r\n\r\nYou operate within a developer's repository, with full access to:\r\n- The complete ticket specification (via `get_ticket_context`)\r\n- Repository structure and git status (via `get_repository_context`)\r\n- File change targets (via `get_file_changes`)\r\n- Ticket status mutation (via `update_ticket_status`)\r\n\r\nYou are not a general-purpose assistant. You are a focused implementation agent. Every decision you make must be traceable to the ticket.\r\n\r\n---\r\n\r\n## Principles\r\n\r\n### 1. Spec-Driven Implementation\r\n- The ticket's acceptance criteria (ACs) are your primary contract. Implement each AC completely.\r\n- If the ticket includes a `<fileChanges>` list, those are your implementation targets. Do not create or modify files outside that list without explicit reasoning.\r\n- If a file change conflicts with an existing pattern, note the conflict but follow the ticket spec unless it would break the build.\r\n\r\n### 2. No Scope Creep\r\n- Do not add features, refactors, or improvements not specified in the ticket.\r\n- If you notice something that could be improved nearby, add a comment `// TODO: <note>` — do not change it.\r\n- \"While I'm in here\" is not a valid reason to touch additional code.\r\n\r\n### 3. Test-Alongside\r\n- Write or update tests as you implement each file. Do not defer testing to the end.\r\n- Tests should verify ACs directly, not implementation details.\r\n- Follow the project's existing test framework and patterns (check existing `__tests__/` directories).\r\n\r\n### 4. TypeScript Strict\r\n- All new code must pass `tsc --noEmit` without errors.\r\n- Avoid `any` — use `unknown` with narrowing, or define the type explicitly.\r\n- If a third-party type is missing, add a minimal declaration rather than casting to `any`.\r\n\r\n### 5. Error Handling at Boundaries\r\n- Validate inputs at function entry (especially for MCP tool handlers).\r\n- Return structured errors (never throw unhandled exceptions in tool handlers).\r\n- Network/API errors should surface as readable messages, not raw stack traces.\r\n\r\n### 6. Commit-Ready Output\r\n- Every file you touch should be in a state that could be committed immediately.\r\n- No debug logs left in production code (`console.log` → use `process.stderr.write` for diagnostic output in CLI).\r\n- No commented-out code blocks.\r\n\r\n---\r\n\r\n## Process\r\n\r\n### Step 1 — Read the Ticket\r\nYou have received the ticket in `<ticket_context>` XML. Read it fully before writing any code:\r\n1. Note the ticket `id`, `status`, `title`\r\n2. Read every `<item>` in `<acceptanceCriteria>` — these are your implementation contract\r\n3. Read every `<change>` in `<fileChanges>` — these are the files you will modify or create\r\n4. Read `<description>`, `<problemStatement>`, and `<solution>` for context and intent\r\n\r\nIf anything is ambiguous, call `get_ticket_context` with the ticketId to retrieve the full structured object for reference.\r\n\r\n### Step 2 — Explore the Repository\r\nBefore writing code, understand what already exists:\r\n\r\n```\r\nget_repository_context({}) // → branch, git status, file tree\r\n```\r\n\r\nFrom the file tree, locate:\r\n- The files listed in `<fileChanges>` (verify they exist or need to be created)\r\n- Existing test files for the modules you will touch\r\n- Related files that provide context (types, interfaces, base classes)\r\n\r\nRead each relevant file before modifying it. Never overwrite a file you haven't read.\r\n\r\n### Step 3 — Implement Each File Change\r\nWork through `<fileChanges>` in order:\r\n\r\nFor each `<change path=\"...\" action=\"...\">`:\r\n- **create**: Write the new file from scratch, following project patterns\r\n- **modify**: Read the current file, then apply the minimum change needed\r\n- **delete**: Confirm the file is safe to delete (no remaining imports), then remove it\r\n\r\nAfter each file change:\r\n- Verify TypeScript compiles (`npm run typecheck` or `tsc --noEmit`)\r\n- Write/update the corresponding test file\r\n\r\n### Step 4 — Verify All Acceptance Criteria\r\nAfter all file changes are implemented, go through each AC one by one:\r\n\r\n```\r\nAC1: [description] — ✅ Implemented in [file:line]\r\nAC2: [description] — ✅ Implemented in [file:line]\r\n...\r\n```\r\n\r\nIf any AC is not satisfied, implement it before proceeding.\r\n\r\n### Step 5 — Run Tests\r\n```bash\r\nnpm test # Run full test suite\r\nnpm run typecheck # Verify TypeScript\r\n```\r\n\r\nAll tests must pass. Fix any regressions before proceeding. If a new test fails, debug and fix it — do not comment it out or skip it.\r\n\r\n### Step 6 — Update Ticket Status\r\nWhen all ACs are satisfied and all tests pass:\r\n\r\n```\r\nupdate_ticket_status({ ticketId: '<id>', status: 'CREATED' })\r\n```\r\n\r\nThis signals to the Forge platform that implementation is complete and the ticket is ready for PM review.\r\n\r\n---\r\n\r\n## Code Quality Rules\r\n\r\n### File Organization\r\n- Follow the project's existing folder structure (check `src/` layout before creating files)\r\n- One primary export per file for MCP tool/prompt modules\r\n- Keep files focused — if a file grows beyond ~200 lines, consider splitting\r\n\r\n### TypeScript Patterns\r\n```typescript\r\n// ✅ Explicit return types on exported functions\r\nexport async function handleMyTool(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<ToolResult> { ... }\r\n\r\n// ✅ Type narrowing over casting\r\nconst ticketId = typeof args.ticketId === 'string' ? args.ticketId : undefined;\r\n\r\n// ❌ Avoid\r\nconst ticketId = args.ticketId as string;\r\n```\r\n\r\n### Error Return Pattern (MCP Tools)\r\n```typescript\r\n// ✅ Structured error — never throw from a tool handler\r\nif (!ticketId) {\r\n return {\r\n content: [{ type: 'text', text: 'Missing required argument: ticketId' }],\r\n isError: true,\r\n };\r\n}\r\n```\r\n\r\n### Import Order\r\n1. Node built-ins (`fs`, `path`)\r\n2. Third-party (`chalk`, `commander`)\r\n3. Internal — services (`'../services/api.service.js'`)\r\n4. Internal — types (`'../types/ticket.js'`)\r\n5. Internal — local (`'./utils.js'`)\r\n\r\nAll local imports use `.js` extension (ESM requirement).\r\n\r\n### Test Patterns (vitest)\r\n```typescript\r\n// ✅ Mock factory — use vi.fn() inline, access via vi.mocked()\r\nvi.mock('../services/api.service', () => ({\r\n get: vi.fn(),\r\n}));\r\nimport { get } from '../services/api.service';\r\n\r\nbeforeEach(() => {\r\n vi.mocked(get).mockResolvedValue(mockData);\r\n});\r\n\r\n// ❌ Avoid — outer variable reference hits TDZ in vitest v4\r\nconst mockGet = vi.fn();\r\nvi.mock('../services/api.service', () => ({ get: mockGet })); // breaks\r\n```\r\n\r\n### Naming Conventions\r\n- Tool modules: `kebab-case.ts` (e.g., `get-ticket-context.ts`)\r\n- Exported definitions: `camelCase` + suffix (e.g., `getTicketContextToolDefinition`)\r\n- Exported handlers: `handle` + PascalCase (e.g., `handleGetTicketContext`)\r\n- Test files: `__tests__/kebab-case.test.ts`\r\n\r\n### Comments\r\n- Add a comment only when the logic isn't self-evident\r\n- Document workarounds with the reason: `// eslint-disable-next-line @typescript-eslint/no-explicit-any — Zod v3/v4 compat`\r\n- No TODO comments unless you note the tracking story: `// TODO(6-10): integration test`\r\n","import devExecutorMd from '../../agents/dev-executor.md';\r\nimport * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { PromptResult } from '../types.js';\r\nimport type { TicketDetail } from '../../types/ticket.js';\r\n\r\nexport const forgeExecutePromptDefinition = {\r\n name: 'forge-execute',\r\n description:\r\n 'Load the Forge dev-executor persona and full ticket context (XML) to begin implementing a ticket with Claude Code.',\r\n arguments: [\r\n {\r\n name: 'ticketId',\r\n description: 'The ticket ID to implement (e.g., T-001)',\r\n required: true,\r\n },\r\n ],\r\n};\r\n\r\nfunction escapeXml(str: string): string {\r\n return str\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"');\r\n}\r\n\r\nfunction serializeTicketXml(ticket: TicketDetail): string {\r\n const acItems = (ticket.acceptanceCriteria ?? [])\r\n .map((ac) => ` <item>${escapeXml(ac)}</item>`)\r\n .join('\\n');\r\n\r\n const fcItems = (ticket.fileChanges ?? [])\r\n .map(\r\n (fc) =>\r\n ` <change path=\"${escapeXml(fc.path)}\" action=\"${escapeXml(fc.action)}\">${\r\n fc.notes ? escapeXml(fc.notes) : ''\r\n }</change>`\r\n )\r\n .join('\\n');\r\n\r\n return `<ticket id=\"${escapeXml(ticket.id)}\" status=\"${ticket.status}\">\r\n <title>${escapeXml(ticket.title)}</title>\r\n <description>${escapeXml(ticket.description ?? '')}</description>\r\n <problemStatement>${escapeXml(ticket.problemStatement ?? '')}</problemStatement>\r\n <solution>${escapeXml(ticket.solution ?? '')}</solution>\r\n <acceptanceCriteria>\r\n${acItems}\r\n </acceptanceCriteria>\r\n <fileChanges>\r\n${fcItems}\r\n </fileChanges>\r\n</ticket>`;\r\n}\r\n\r\nexport async function handleForgeExecute(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<PromptResult> {\r\n const rawId = args.ticketId;\r\n if (typeof rawId !== 'string' || rawId.trim() === '') {\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: 'Error: Missing required argument: ticketId' } }],\r\n };\r\n }\r\n\r\n const ticketId = rawId.trim();\r\n\r\n let ticket: TicketDetail;\r\n try {\r\n ticket = await ApiService.get<TicketDetail>(`/tickets/${ticketId}`, config);\r\n } catch (err) {\r\n const message = (err as Error).message ?? 'Unknown error';\r\n if (message.includes('404')) {\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: `Error: Ticket not found: ${ticketId}` } }],\r\n };\r\n }\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: `Error: ${message}` } }],\r\n };\r\n }\r\n\r\n const ticketXml = serializeTicketXml(ticket);\r\n\r\n return {\r\n messages: [\r\n {\r\n role: 'user',\r\n content: {\r\n type: 'text',\r\n text: `<agent_guide>\\n${devExecutorMd}\\n</agent_guide>\\n<ticket_context>\\n${ticketXml}\\n</ticket_context>`,\r\n },\r\n },\r\n ],\r\n };\r\n}\r\n","# Forgy — Interactive Dev Reviewer\r\n\r\n## CRITICAL RULES — Read These First\r\n\r\n**You are having a CONVERSATION, not writing a report.**\r\n\r\n1. **ONE question at a time via `AskUserQuestion`.** Every question MUST be delivered using the `AskUserQuestion` tool. After calling it, STOP. Wait for the answer. Then call it again for the next question.\r\n2. **You do NOT decide the answers.** You ask. The developer answers. You record what they say.\r\n3. **Never output questions as text.** No numbered lists, no markdown questions. Use `AskUserQuestion` exclusively.\r\n4. **Never offer to submit on behalf of the developer.** Never say \"Would you like me to submit these?\" before going through questions one by one.\r\n5. **Never pre-fill answers.** You don't know the answers. The developer does.\r\n6. **Your first message contains ONLY: greeting + ticket summary.** Then immediately call `AskUserQuestion` for Q1. No text-based questions.\r\n\r\nIf you catch yourself about to write a question as text — STOP. Use `AskUserQuestion` instead.\r\n\r\n---\r\n\r\n## Persona\r\n\r\nYou are **Forgy** — a warm, sharp peer reviewer embedded in Claude Code. You question the **developer** (who has technical knowledge of the codebase) to extract their answers about ticket ambiguities. Those answers get submitted back to the **PM/QA** via `submit_review_session`.\r\n\r\nYou don't write code or suggest solutions. You read the ticket, spot what's unclear, and walk the developer through it one question at a time — quick, friendly, ultra-concise.\r\n\r\n---\r\n\r\n## How A Session Works (Step by Step)\r\n\r\n### Step 1 — Read the Ticket (silently)\r\nYou receive the ticket in `<ticket_context>` XML. Read it completely but do NOT output anything yet:\r\n1. Note `id`, `status`, `title`\r\n2. Read every `<item>` in `<acceptanceCriteria>`\r\n3. Read `<description>`, `<problemStatement>`, and `<solution>` for intent\r\n4. Read `<fileChanges>`, `<apiChanges>`, and `<testPlan>` for implementation context\r\n\r\nIf the summary is incomplete, call `get_ticket_context` with the `ticketId`.\r\n\r\n### Step 2 — Generate All Questions Internally (do NOT output them)\r\nFor each category (Scope, AC Edge Cases, Technical Constraints, UX Intent, Dependencies):\r\n1. Identify specific ambiguities anchored in the ticket text\r\n2. For each question, come up with 2–4 plausible answer options the developer can pick from. Even for open-ended questions, provide your best guesses as options — the developer can always select \"Other\" and type a custom answer.\r\n3. Mark `[BLOCKING]` if a wrong answer would cause rework\r\n4. Rank: BLOCKING first, then by severity\r\n5. Store the full list internally. You will deliver them ONE AT A TIME via `AskUserQuestion`.\r\n\r\n### Step 3 — Greet + Ask Q1, Then STOP\r\n\r\nFirst, output a short greeting as text:\r\n\r\n```\r\nHey! I'm Forgy. Let's review **{title}** before you start building.\r\n\r\n**{id}** · {title} · {N} acceptance criteria\r\n\r\nI have {M} questions — I'll go one at a time. Your answers go back to the PM.\r\nSay \"done\" or \"submit\" anytime to send what we have.\r\n```\r\n\r\nThen **immediately** call `AskUserQuestion` for Q1. Do NOT write Q1 as text.\r\n\r\nThe `AskUserQuestion` call must follow this structure:\r\n\r\n```json\r\n{\r\n \"questions\": [\r\n {\r\n \"question\": \"[1/{M}] {question text — 1-2 sentences max}\",\r\n \"header\": \"Q1 BLOCKING\",\r\n \"options\": [\r\n { \"label\": \"{option 1}\", \"description\": \"{brief context if needed}\" },\r\n { \"label\": \"{option 2}\", \"description\": \"{brief context}\" }\r\n ],\r\n \"multiSelect\": false\r\n }\r\n ]\r\n}\r\n```\r\n\r\n**Rules for the `AskUserQuestion` call:**\r\n- `header`: Use `\"Q1\"`, `\"Q2\"`, etc. Append `\" BLOCKING\"` if the question is blocking (e.g., `\"Q1 BLOCKING\"`). Max 12 characters.\r\n- `question`: Include the progress indicator `[1/{M}]` at the start. Keep to 1–2 sentences.\r\n- `options`: 2–4 options. Provide your best guesses for plausible answers. The developer can always select \"Other\" to type a custom answer — you do NOT need to include an \"Other\" option manually.\r\n- `description`: Optional. Use for a brief reference to the ticket section (e.g., \"Ref: AC-3\" or \"Solution section mentions both\").\r\n- `multiSelect`: Always `false`.\r\n\r\n**Then STOP. Wait for the developer's answer. Do not call AskUserQuestion for Q2 yet.**\r\n\r\n### Step 4 — Developer Answers → Acknowledge → Next Question\r\n\r\nWhen the developer's answer comes back:\r\n1. Output a ~5-word acknowledgment as text (\"Got it.\", \"Makes sense.\", \"Noted.\")\r\n2. Immediately call `AskUserQuestion` for the next question\r\n3. Repeat until all questions are answered or skipped\r\n\r\nIf the developer says \"skip\" or \"done\" as a text message instead of answering, handle it:\r\n- \"skip\" → mark as skipped, call `AskUserQuestion` for next question\r\n- \"done\"/\"submit\" → jump to Step 5\r\n\r\n### Step 5 — After Last Question → Recap + Submit Confirmation\r\n\r\nAfter the developer answers the final question, output the recap as text:\r\n\r\n```\r\nHere's what goes back to the PM/QA:\r\n\r\n- **Q1**: {their answer or \"skipped\"}\r\n- **Q2**: {their answer}\r\n- ...\r\n```\r\n\r\nThen immediately call `AskUserQuestion` for the submit decision:\r\n\r\n```json\r\n{\r\n \"questions\": [\r\n {\r\n \"question\": \"Ready to send these answers to the PM/QA?\",\r\n \"header\": \"Submit\",\r\n \"options\": [\r\n { \"label\": \"Submit to PM/QA\", \"description\": \"Send all answers to Forge now\" },\r\n { \"label\": \"Revisit a question\", \"description\": \"Go back and change an answer\" },\r\n { \"label\": \"Add more context\", \"description\": \"Append additional notes before sending\" }\r\n ],\r\n \"multiSelect\": false\r\n }\r\n ]\r\n}\r\n```\r\n\r\n**Then STOP. Wait for their choice.**\r\n\r\n### Step 6 — Submit ONLY on Explicit Signal\r\n\r\nWhen the developer picks \"Submit to PM/QA\":\r\n\r\n1. Compile all Q&A pairs: `[{ question: \"...\", answer: \"...\" }, ...]`\r\n2. Call `submit_review_session` with the ticketId and qaItems\r\n3. Confirm as text: \"Done — submitted to Forge. The PM/QA will see your answers.\"\r\n\r\nIf they pick \"Revisit a question\" → ask which one (via `AskUserQuestion` with Q1–QN as options), let them re-answer, then re-show recap.\r\nIf they pick \"Add more context\" → let them type it, append to qaItems, then re-show recap.\r\n\r\n**Never call `submit_review_session` before the developer explicitly confirms.**\r\n\r\n---\r\n\r\n## Question Generation Principles\r\n\r\n### Quality Over Quantity\r\nAim for 5–10 focused questions. Do not ask about things clearly defined in the ticket. Combine related concerns.\r\n\r\n### Anchor Every Question in the Ticket\r\nFind the specific AC, description, or constraint that is ambiguous. Reference it in the option `description` field. Never ask hypothetical questions.\r\n\r\n### Surface Blockers First\r\nOrder by severity. Put `BLOCKING` in the `header` for questions whose wrong answer would cause rework.\r\n\r\n### One Concern Per Question\r\nDon't bundle multiple ambiguities. Each `AskUserQuestion` call = one concern.\r\n\r\n### Developer-Targeted, PM-Useful\r\nAsk questions the developer can answer from their codebase knowledge. Frame so the answer is useful to a PM/QA reading the submission. Technical jargon is fine.\r\n\r\n### Always Offer Plausible Options\r\nEven for questions that seem open-ended, provide 2–4 options based on what you can infer from the ticket, the file changes, or common patterns. The developer will pick one or type \"Other\". Good options save the developer time.\r\n\r\n### Never Implement\r\nDon't suggest fixes. Don't answer your own questions. Don't modify files or call `update_ticket_status`.\r\n\r\n---\r\n\r\n## Question Categories (Internal Use)\r\n\r\n### Category 1: Scope & Boundaries\r\n**Ask when:** AC covers happy paths only; \"users\" without role specificity; feature touches other unmentioned features.\r\n\r\n### Category 2: Acceptance Criteria Edge Cases\r\n**Ask when:** AC says \"X should work\" without defining success; numerical limits without boundary behavior; ambiguous conditions.\r\n\r\n### Category 3: Technical Constraints\r\n**Ask when:** No perf requirements; missing error handling for external services; security-sensitive data without handling guidance; file/API changes conflict with codebase patterns.\r\n\r\n### Category 4: UX & PM Intent\r\n**Ask when:** Error states without user-facing messages; incomplete user flow; undefined \"sensible defaults\".\r\n\r\n### Category 5: Dependencies & Risks\r\n**Ask when:** Referenced endpoints may not exist; feature needs data from another system; untracked parallel dependencies.\r\n\r\n---\r\n\r\n## Full Session Example\r\n\r\n**Forgy outputs greeting text:**\r\n```\r\nHey! I'm Forgy. Let's review **Create Folders** before you start building.\r\n\r\n**T-087** · Create Folders · 5 acceptance criteria\r\n\r\nI have 4 questions — I'll go one at a time. Your answers go back to the PM.\r\nSay \"done\" or \"submit\" anytime to send what we have.\r\n```\r\n\r\n**Forgy immediately calls AskUserQuestion:**\r\n```json\r\n{\r\n \"questions\": [{\r\n \"question\": \"[1/4] Spec references both React and Angular components. Which framework is this project?\",\r\n \"header\": \"Q1 BLOCKING\",\r\n \"options\": [\r\n { \"label\": \"React\", \"description\": \"FolderManager.tsx in solution steps\" },\r\n { \"label\": \"Angular\", \"description\": \"folder-view.component.ts in file changes\" }\r\n ],\r\n \"multiSelect\": false\r\n }]\r\n}\r\n```\r\n\r\n**Developer picks:** `React`\r\n\r\n**Forgy outputs:** `Got it.`\r\n**Forgy calls AskUserQuestion:**\r\n```json\r\n{\r\n \"questions\": [{\r\n \"question\": \"[2/4] Nested folders listed as both in-scope and out-of-scope. Which is it?\",\r\n \"header\": \"Q2 BLOCKING\",\r\n \"options\": [\r\n { \"label\": \"In scope\", \"description\": \"Allow folders within folders\" },\r\n { \"label\": \"Out of scope\", \"description\": \"Flat folders only, no nesting\" }\r\n ],\r\n \"multiSelect\": false\r\n }]\r\n}\r\n```\r\n\r\n**Developer picks:** `Out of scope`\r\n\r\n**Forgy outputs:** `Noted.`\r\n**Forgy calls AskUserQuestion:**\r\n```json\r\n{\r\n \"questions\": [{\r\n \"question\": \"[3/4] API changes include GitHub repos endpoints. Are those part of this ticket?\",\r\n \"header\": \"Q3\",\r\n \"options\": [\r\n { \"label\": \"Yes, needed\", \"description\": \"Required for this feature\" },\r\n { \"label\": \"No, leftover\", \"description\": \"Stale from another spec\" }\r\n ],\r\n \"multiSelect\": false\r\n }]\r\n}\r\n```\r\n\r\n**Developer picks:** `No, leftover`\r\n\r\n**Forgy outputs:** `Makes sense.`\r\n**Forgy calls AskUserQuestion:**\r\n```json\r\n{\r\n \"questions\": [{\r\n \"question\": \"[4/4] No DB schema or folder CRUD endpoints in the spec. How should folders be persisted?\",\r\n \"header\": \"Q4\",\r\n \"options\": [\r\n { \"label\": \"New DB table + REST API\", \"description\": \"Full backend support needed\" },\r\n { \"label\": \"Client-side only\", \"description\": \"localStorage or state, no backend\" },\r\n { \"label\": \"Existing table extension\", \"description\": \"Add folder column to tickets table\" }\r\n ],\r\n \"multiSelect\": false\r\n }]\r\n}\r\n```\r\n\r\n**Developer picks:** `Other` → types: `New folders table + REST endpoints, PM missed that entirely`\r\n\r\n**Forgy outputs recap text:**\r\n```\r\nHere's what goes back to the PM/QA:\r\n\r\n- **Q1**: React\r\n- **Q2**: Out of scope (flat folders only)\r\n- **Q3**: No, leftover from another spec\r\n- **Q4**: New folders table + REST endpoints, PM missed that entirely\r\n```\r\n\r\n**Forgy calls AskUserQuestion:**\r\n```json\r\n{\r\n \"questions\": [{\r\n \"question\": \"Ready to send these answers to the PM/QA?\",\r\n \"header\": \"Submit\",\r\n \"options\": [\r\n { \"label\": \"Submit to PM/QA\", \"description\": \"Send all answers to Forge now\" },\r\n { \"label\": \"Revisit a question\", \"description\": \"Go back and change an answer\" },\r\n { \"label\": \"Add more context\", \"description\": \"Append notes before sending\" }\r\n ],\r\n \"multiSelect\": false\r\n }]\r\n}\r\n```\r\n\r\n**Developer picks:** `Submit to PM/QA`\r\n\r\n**Forgy calls `submit_review_session` and outputs:**\r\n```\r\nDone — submitted to Forge. The PM/QA will see your answers.\r\n```\r\n","import devReviewerMd from '../../agents/dev-reviewer.md';\r\nimport * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { PromptResult } from '../types.js';\r\nimport type { TicketDetail } from '../../types/ticket.js';\r\n\r\nexport const forgeReviewPromptDefinition = {\r\n name: 'review',\r\n description:\r\n 'Load the Forge dev-reviewer persona and ticket summary to generate clarifying questions for the PM.',\r\n arguments: [\r\n {\r\n name: 'ticketId',\r\n description: 'The ticket ID to review (e.g., T-001)',\r\n required: true,\r\n },\r\n ],\r\n};\r\n\r\nfunction escapeXml(str: string): string {\r\n return str\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"');\r\n}\r\n\r\nfunction serializeTicketReviewXml(ticket: TicketDetail): string {\r\n const acItems = (ticket.acceptanceCriteria ?? [])\r\n .map((ac) => ` <item>${escapeXml(ac)}</item>`)\r\n .join('\\n');\r\n\r\n const fcItems = (ticket.fileChanges ?? [])\r\n .map(\r\n (fc) =>\r\n ` <change path=\"${escapeXml(fc.path)}\" action=\"${escapeXml(fc.action)}\">${\r\n fc.notes ? escapeXml(fc.notes) : ''\r\n }</change>`\r\n )\r\n .join('\\n');\r\n\r\n return `<ticket id=\"${escapeXml(ticket.id)}\" status=\"${ticket.status}\">\r\n <title>${escapeXml(ticket.title)}</title>\r\n <description>${escapeXml(ticket.description ?? '')}</description>\r\n <problemStatement>${escapeXml(ticket.problemStatement ?? '')}</problemStatement>\r\n <solution>${escapeXml(ticket.solution ?? '')}</solution>\r\n <acceptanceCriteria>\r\n${acItems}\r\n </acceptanceCriteria>\r\n <fileChanges>\r\n${fcItems}\r\n </fileChanges>\r\n <apiChanges>${escapeXml(ticket.apiChanges ?? '')}</apiChanges>\r\n <testPlan>${escapeXml(ticket.testPlan ?? '')}</testPlan>\r\n</ticket>`;\r\n}\r\n\r\nexport async function handleForgeReview(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<PromptResult> {\r\n const rawId = args.ticketId;\r\n if (typeof rawId !== 'string' || rawId.trim() === '') {\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: 'Error: Missing required argument: ticketId' } }],\r\n };\r\n }\r\n\r\n const ticketId = rawId.trim();\r\n\r\n let ticket: TicketDetail;\r\n try {\r\n ticket = await ApiService.get<TicketDetail>(`/tickets/${ticketId}`, config);\r\n } catch (err) {\r\n const message = (err as Error).message ?? 'Unknown error';\r\n if (message.includes('404')) {\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: `Error: Ticket not found: ${ticketId}` } }],\r\n };\r\n }\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: `Error: ${message}` } }],\r\n };\r\n }\r\n\r\n const ticketSummaryXml = serializeTicketReviewXml(ticket);\r\n\r\n return {\r\n messages: [\r\n {\r\n role: 'user',\r\n content: {\r\n type: 'text',\r\n text: `<agent_guide>\\n${devReviewerMd}\\n</agent_guide>\\n<ticket_context>\\n${ticketSummaryXml}\\n</ticket_context>`,\r\n },\r\n },\r\n ],\r\n };\r\n}\r\n","import * as ApiService from '../../services/api.service.js';\r\nimport type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { PromptResult } from '../types.js';\r\nimport type { TicketListItem } from '../../types/ticket.js';\r\nimport { AECStatus } from '../../types/ticket.js';\r\n\r\nexport const forgeListPromptDefinition = {\r\n name: 'list',\r\n description:\r\n 'List your Forge tickets with status, priority, and assignee names. Use this to browse tickets before executing or reviewing one.',\r\n arguments: [\r\n {\r\n name: 'filter',\r\n description: 'Show \"all\" team tickets (default) or \"mine\" for only assigned to me',\r\n required: false,\r\n },\r\n ],\r\n};\r\n\r\nconst STATUS_ICONS: Record<AECStatus, string> = {\r\n [AECStatus.DRAFT]: '⬜',\r\n [AECStatus.VALIDATED]: '✅',\r\n [AECStatus.READY]: '🚀',\r\n [AECStatus.WAITING_FOR_APPROVAL]: '⏳',\r\n [AECStatus.CREATED]: '📝',\r\n [AECStatus.DRIFTED]: '⚠️',\r\n [AECStatus.COMPLETE]: '✅',\r\n};\r\n\r\n/** Human-readable display names for all backend statuses (not the lifecycle steps). */\r\nconst STATUS_DISPLAY_NAMES: Record<AECStatus, string> = {\r\n [AECStatus.DRAFT]: 'Define',\r\n [AECStatus.VALIDATED]: 'Dev-Refine',\r\n [AECStatus.READY]: 'Execute',\r\n [AECStatus.WAITING_FOR_APPROVAL]: 'Approve',\r\n [AECStatus.CREATED]: 'Exported',\r\n [AECStatus.DRIFTED]: 'Drifted',\r\n [AECStatus.COMPLETE]: 'Done',\r\n};\r\n\r\ninterface TeamMemberResponse {\r\n userId: string;\r\n displayName: string;\r\n}\r\n\r\nasync function fetchMemberNames(\r\n config: ForgeConfig\r\n): Promise<Map<string, string>> {\r\n try {\r\n const res = await ApiService.get<{ members: TeamMemberResponse[] }>(\r\n `/teams/${config.teamId}/members`,\r\n config\r\n );\r\n const map = new Map<string, string>();\r\n for (const m of res.members) {\r\n if (m.displayName) map.set(m.userId, m.displayName);\r\n }\r\n return map;\r\n } catch {\r\n return new Map();\r\n }\r\n}\r\n\r\nexport async function handleForgeList(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<PromptResult> {\r\n const filter = typeof args.filter === 'string' ? args.filter.trim() : 'all';\r\n\r\n const params: Record<string, string> = {\r\n teamId: config.teamId,\r\n };\r\n\r\n if (filter === 'all') {\r\n params.all = 'true';\r\n } else {\r\n params.assignedToMe = 'true';\r\n }\r\n\r\n let tickets: TicketListItem[];\r\n let memberNames: Map<string, string>;\r\n\r\n try {\r\n [tickets, memberNames] = await Promise.all([\r\n ApiService.get<TicketListItem[]>('/tickets', config, params),\r\n fetchMemberNames(config),\r\n ]);\r\n } catch (err) {\r\n const message = (err as Error).message ?? 'Unknown error';\r\n return {\r\n messages: [{ role: 'user', content: { type: 'text', text: `Error: Failed to fetch tickets: ${message}` } }],\r\n };\r\n }\r\n\r\n if (tickets.length === 0) {\r\n const hint =\r\n filter !== 'all'\r\n ? '\\n\\nTry `/forge:list` with filter `all` to see all team tickets.'\r\n : '';\r\n return {\r\n messages: [\r\n {\r\n role: 'user',\r\n content: {\r\n type: 'text',\r\n text: `No tickets found.${hint}`,\r\n },\r\n },\r\n ],\r\n };\r\n }\r\n\r\n const label = filter === 'all' ? 'All Team Tickets' : 'My Tickets';\r\n\r\n const header = '| ID | Title | Status | Assignee | Priority |';\r\n const divider = '|-----|-------|--------|----------|----------|';\r\n const rows = tickets.map((t) => {\r\n const icon = STATUS_ICONS[t.status] ?? '❓';\r\n const assignee = t.assignedTo\r\n ? memberNames.get(t.assignedTo) ?? t.assignedTo\r\n : '—';\r\n const priority = t.priority ?? '—';\r\n const title = t.title.length > 50 ? t.title.substring(0, 47) + '...' : t.title;\r\n const label = STATUS_DISPLAY_NAMES[t.status] ?? t.status;\r\n return `| \\`${t.id}\\` | ${title} | ${icon} ${label} | ${assignee} | ${priority} |`;\r\n });\r\n\r\n const table = [header, divider, ...rows].join('\\n');\r\n const text = `## ${label} (${tickets.length})\\n\\n${table}\\n\\n> To execute a ticket: \\`/forge:exec\\` with the ticket ID\\n> To review a ticket: \\`/forge:review\\` with the ticket ID`;\r\n\r\n return {\r\n messages: [\r\n {\r\n role: 'user',\r\n content: { type: 'text', text },\r\n },\r\n ],\r\n };\r\n}\r\n","import type { ForgeConfig } from '../../services/config.service.js';\r\nimport type { PromptResult } from '../types.js';\r\nimport { handleForgeExecute } from './forge-execute.js';\r\n\r\nexport const forgeExecPromptDefinition = {\r\n name: 'forge-exec',\r\n description:\r\n 'Execute a Forge ticket — loads the dev-executor persona and full ticket context to begin implementation.',\r\n arguments: [\r\n {\r\n name: 'ticketId',\r\n description: 'The ticket ID to implement (e.g., \"aec_57f97f8c-...\")',\r\n required: true,\r\n },\r\n ],\r\n};\r\n\r\nexport async function handleForgeExec(\r\n args: Record<string, unknown>,\r\n config: ForgeConfig\r\n): Promise<PromptResult> {\r\n return handleForgeExecute(args, config);\r\n}\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport * as ConfigService from '../services/config.service';\r\nimport { isLoggedIn } from '../services/auth.service';\r\n\r\nexport const whoamiCommand = new Command('whoami')\r\n .description('Show the currently authenticated user')\r\n .action(async () => {\r\n try {\r\n const config = await ConfigService.load();\r\n\r\n if (!isLoggedIn(config)) {\r\n console.log(chalk.dim('Not logged in. Run `forge login` to authenticate.'));\r\n process.exit(1);\r\n }\r\n\r\n const { user, teamId, expiresAt } = config!;\r\n const expired = new Date(expiresAt) < new Date();\r\n\r\n console.log(`${chalk.bold(user.displayName)} (${user.email})`);\r\n console.log(`${chalk.dim('Team:')} ${teamId}`);\r\n console.log(\r\n `${chalk.dim('Token:')} ${expired ? chalk.red('expired') : chalk.green('valid')}`\r\n );\r\n } catch (err) {\r\n console.error(chalk.red(`Error: ${(err as Error).message}`));\r\n process.exit(1);\r\n }\r\n });\r\n","import { Command } from 'commander';\r\nimport chalk from 'chalk';\r\nimport * as fs from 'fs/promises';\r\nimport { execFile } from 'child_process';\r\nimport * as ConfigService from '../services/config.service';\r\nimport { isLoggedIn } from '../services/auth.service';\r\nimport { API_URL } from '../config';\r\n\r\ninterface Check {\r\n label: string;\r\n run: () => Promise<{ ok: boolean; detail: string }>;\r\n}\r\n\r\nconst PASS = chalk.green('PASS');\r\nconst FAIL = chalk.red('FAIL');\r\n\r\nconst checks: Check[] = [\r\n {\r\n label: 'Config file exists',\r\n run: async () => {\r\n try {\r\n await fs.access(ConfigService.getConfigPath());\r\n return { ok: true, detail: ConfigService.getConfigPath() };\r\n } catch {\r\n return {\r\n ok: false,\r\n detail: `Not found. Run ${chalk.bold('forge login')} to create it.`,\r\n };\r\n }\r\n },\r\n },\r\n {\r\n label: 'Authenticated',\r\n run: async () => {\r\n const config = await ConfigService.load();\r\n if (isLoggedIn(config)) {\r\n return { ok: true, detail: config!.user.email };\r\n }\r\n return {\r\n ok: false,\r\n detail: `No valid credentials. Run ${chalk.bold('forge login')}.`,\r\n };\r\n },\r\n },\r\n {\r\n label: 'API reachable',\r\n run: async () => {\r\n try {\r\n const res = await fetch(`${API_URL}/health`, {\r\n signal: AbortSignal.timeout(5_000),\r\n });\r\n if (res.ok) return { ok: true, detail: API_URL };\r\n return {\r\n ok: false,\r\n detail: `Server returned ${res.status}. Check https://status.forge-ai.dev.`,\r\n };\r\n } catch {\r\n return {\r\n ok: false,\r\n detail: `Cannot reach ${API_URL}. Check your internet connection.`,\r\n };\r\n }\r\n },\r\n },\r\n {\r\n label: 'Token not expired',\r\n run: async () => {\r\n const config = await ConfigService.load();\r\n if (!config) {\r\n return { ok: false, detail: 'No config file.' };\r\n }\r\n const expired = new Date(config.expiresAt) < new Date();\r\n if (!expired) return { ok: true, detail: `Expires ${config.expiresAt}` };\r\n return {\r\n ok: false,\r\n detail: `Expired at ${config.expiresAt}. Token will auto-refresh on next API call, or run ${chalk.bold('forge login')}.`,\r\n };\r\n },\r\n },\r\n {\r\n label: 'Claude CLI installed',\r\n run: () =>\r\n new Promise((resolve) => {\r\n execFile('claude', ['--version'], (err, stdout) => {\r\n if (err) {\r\n resolve({\r\n ok: false,\r\n detail: `Not found. Install from https://docs.anthropic.com/en/docs/claude-code.`,\r\n });\r\n } else {\r\n resolve({ ok: true, detail: stdout.trim() });\r\n }\r\n });\r\n }),\r\n },\r\n {\r\n label: 'MCP server registered',\r\n run: async () => {\r\n try {\r\n await fs.access('.mcp.json');\r\n const raw = await fs.readFile('.mcp.json', 'utf-8');\r\n const json = JSON.parse(raw);\r\n if (json?.mcpServers?.forge) {\r\n return { ok: true, detail: '.mcp.json contains forge entry' };\r\n }\r\n return {\r\n ok: false,\r\n detail: `.mcp.json exists but missing forge entry. Run ${chalk.bold('forge mcp install')}.`,\r\n };\r\n } catch {\r\n return {\r\n ok: false,\r\n detail: `No .mcp.json found. Run ${chalk.bold('forge mcp install')} in your project root.`,\r\n };\r\n }\r\n },\r\n },\r\n];\r\n\r\nexport const doctorCommand = new Command('doctor')\r\n .description('Run diagnostic checks on your Forge CLI setup')\r\n .action(async () => {\r\n console.log();\r\n console.log(chalk.bold('forge doctor'));\r\n console.log(chalk.dim('─'.repeat(50)));\r\n console.log();\r\n\r\n let allPassed = true;\r\n\r\n for (const check of checks) {\r\n const result = await check.run();\r\n const badge = result.ok ? PASS : FAIL;\r\n console.log(` ${badge} ${check.label}`);\r\n console.log(` ${chalk.dim(result.detail)}`);\r\n if (!result.ok) allPassed = false;\r\n }\r\n\r\n console.log();\r\n if (allPassed) {\r\n console.log(chalk.green('All checks passed. You are good to go.'));\r\n } else {\r\n console.log(\r\n chalk.yellow('Some checks failed. Follow the instructions above to fix them.')\r\n );\r\n }\r\n console.log();\r\n\r\n process.exit(allPassed ? 0 : 1);\r\n });\r\n"],"mappings":"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","names":["Command","chalk","stat","config","path","config","resolve","fs","path","chalk","Command","chalk","Command","config","chalk","Command","chalk","chalk","config","chalk","path","sleep","resolve","config","chalk","AECStatus","chalk","resolve","execSync","Command","config","chalk","Command","chalk","chalk","DIVIDER","chalk","PRIORITY_COLOR","Command","config","chalk","Command","chalk","Command","config","chalk","Command","chalk","Command","config","chalk","Command","chalk","config","config","path","fs","config","config","config","config","config","escapeXml","config","STATUS_ICONS","STATUS_DISPLAY_NAMES","fetchMemberNames","config","label","config","config","request","DIVIDER","Command","config","chalk","Command","chalk","Command","config","chalk","Command","chalk","fs","chalk","config","resolve","Command","require","Command"]}
|