@iloom/cli 0.10.0 → 0.10.1
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 +1 -1
- package/README.md +2 -2
- package/dist/{BranchNamingService-ECJHBB67.js → BranchNamingService-25KSZAEM.js} +2 -2
- package/dist/ClaudeContextManager-66GR4BGM.js +14 -0
- package/dist/ClaudeService-7KM5NA5Z.js +13 -0
- package/dist/{LoomLauncher-L64HHS3T.js → LoomLauncher-TDLZSYG2.js} +6 -6
- package/dist/{PromptTemplateManager-DULSVRRE.js → PromptTemplateManager-YOE2SIPG.js} +2 -2
- package/dist/README.md +2 -2
- package/dist/{SettingsManager-BQDQA3FK.js → SettingsManager-FNKCOZMQ.js} +2 -2
- package/dist/{build-5GO3XW26.js → build-VHGEMXBA.js} +6 -6
- package/dist/chunk-4E7LCFUG.js +24 -0
- package/dist/chunk-4E7LCFUG.js.map +1 -0
- package/dist/{chunk-MNHZB4Z2.js → chunk-4FGEGQW4.js} +3 -3
- package/dist/{chunk-LXLMMXXY.js → chunk-5FJWO4IT.js} +17 -12
- package/dist/chunk-5FJWO4IT.js.map +1 -0
- package/dist/{chunk-ZHPNZC75.js → chunk-5RPBYK5Q.js} +26 -21
- package/dist/chunk-5RPBYK5Q.js.map +1 -0
- package/dist/{chunk-WY4QBK43.js → chunk-63QWFWH3.js} +2 -2
- package/dist/{chunk-YYAKPQBT.js → chunk-7VHJNVLF.js} +19 -9
- package/dist/chunk-7VHJNVLF.js.map +1 -0
- package/dist/{chunk-SF2P22EE.js → chunk-C6HNNJIV.js} +2 -2
- package/dist/{chunk-5MWV33NN.js → chunk-CVCTIDDK.js} +2 -2
- package/dist/{chunk-RYWFS37M.js → chunk-E6KOWMKA.js} +2 -2
- package/dist/{chunk-6EU6TCF6.js → chunk-EVPZFV3K.js} +5 -5
- package/dist/{chunk-ZEWU5PZK.js → chunk-G5V75JD5.js} +2 -2
- package/dist/chunk-GRISNU6G.js +651 -0
- package/dist/chunk-GRISNU6G.js.map +1 -0
- package/dist/{chunk-VGGST52X.js → chunk-I5T677EA.js} +2 -2
- package/dist/{chunk-VECNX6VX.js → chunk-KIK2ZFAL.js} +2 -2
- package/dist/{chunk-FB47TIJG.js → chunk-KKV5WH5M.js} +4 -23
- package/dist/chunk-KKV5WH5M.js.map +1 -0
- package/dist/{chunk-ZW2LKWWE.js → chunk-KVHIAWVT.js} +3 -3
- package/dist/{chunk-3D7WQM7I.js → chunk-LLHXQS3C.js} +2 -2
- package/dist/{chunk-Y4YZTHZE.js → chunk-LUKXJSRI.js} +2 -2
- package/dist/{ignite-CGOV3TD4.js → chunk-OTGH2HRS.js} +105 -71
- package/dist/chunk-OTGH2HRS.js.map +1 -0
- package/dist/{chunk-J5S7DFYC.js → chunk-QVLPWNE3.js} +2 -2
- package/dist/chunk-RJ3VBUFK.js +781 -0
- package/dist/chunk-RJ3VBUFK.js.map +1 -0
- package/dist/{chunk-SN3SQCFK.js → chunk-S7PZA6IV.js} +4 -4
- package/dist/{chunk-UWGVCXRF.js → chunk-SKSYYBCU.js} +23 -1
- package/dist/chunk-SKSYYBCU.js.map +1 -0
- package/dist/{chunk-JO2LZ6EQ.js → chunk-SWSJWA2S.js} +2 -2
- package/dist/{chunk-ONQYPICO.js → chunk-UR5DGNUO.js} +60 -6
- package/dist/chunk-UR5DGNUO.js.map +1 -0
- package/dist/{chunk-4WJNIR5O.js → chunk-UUEW5KWB.js} +1 -1
- package/dist/chunk-UUEW5KWB.js.map +1 -0
- package/dist/{chunk-NRSWLOAZ.js → chunk-WXIM2WS7.js} +4 -4
- package/dist/{chunk-UD3WJDIV.js → chunk-ZNMPGMHY.js} +11 -774
- package/dist/chunk-ZNMPGMHY.js.map +1 -0
- package/dist/{claude-P3NQR6IJ.js → claude-7GGEWVEM.js} +2 -2
- package/dist/{cleanup-6UCPVMFG.js → cleanup-6PVAC4NI.js} +19 -17
- package/dist/{cleanup-6UCPVMFG.js.map → cleanup-6PVAC4NI.js.map} +1 -1
- package/dist/cli.js +154 -614
- package/dist/cli.js.map +1 -1
- package/dist/{commit-L3EPY5QG.js → commit-FZR5XDQG.js} +12 -10
- package/dist/commit-FZR5XDQG.js.map +1 -0
- package/dist/{compile-ZS4HYRX5.js → compile-7ALJHZ4N.js} +6 -6
- package/dist/{contribute-ORDDQGSL.js → contribute-5GKLK3BQ.js} +3 -3
- package/dist/{dev-server-FYZ2AQIH.js → dev-server-7SMIB7OF.js} +8 -8
- package/dist/{feedback-TMBXSCM5.js → feedback-G2GJFN2F.js} +10 -8
- package/dist/{feedback-TMBXSCM5.js.map → feedback-G2GJFN2F.js.map} +1 -1
- package/dist/{git-ET64COO3.js → git-GTLKAZRJ.js} +3 -3
- package/dist/ignite-H2O5Y5A2.js +34 -0
- package/dist/ignite-H2O5Y5A2.js.map +1 -0
- package/dist/index.d.ts +113 -18
- package/dist/index.js +177 -12
- package/dist/index.js.map +1 -1
- package/dist/{init-GFQ5W7GK.js → init-32YOKXRL.js} +8 -8
- package/dist/{issues-T4ZZSPEG.js → issues-4UUAQ5K6.js} +3 -3
- package/dist/{lint-6TQXDZ3T.js → lint-AAN2NZWG.js} +6 -6
- package/dist/mcp/harness-server.js +140 -0
- package/dist/mcp/harness-server.js.map +1 -0
- package/dist/mcp/issue-management-server.js +140 -18
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/{open-5QZGXQRF.js → open-FXWW3VI4.js} +8 -8
- package/dist/{plan-U7ZQWLFY.js → plan-RQ5FPIGF.js} +338 -36
- package/dist/plan-RQ5FPIGF.js.map +1 -0
- package/dist/prompts/CLAUDE.md +2 -2
- package/dist/prompts/init-prompt.txt +102 -27
- package/dist/prompts/issue-prompt.txt +46 -0
- package/dist/prompts/plan-prompt.txt +59 -19
- package/dist/prompts/swarm-orchestrator-prompt.txt +107 -80
- package/dist/{rebase-DWIB77KV.js → rebase-6NVLX5V7.js} +17 -8
- package/dist/rebase-6NVLX5V7.js.map +1 -0
- package/dist/{recap-MX63HAKV.js → recap-OMBOKJST.js} +6 -6
- package/dist/{run-O3TFNQFC.js → run-BBXLRIZB.js} +8 -8
- package/dist/schema/settings.schema.json +36 -2
- package/dist/{shell-G6VC2CYR.js → shell-RF7LTND5.js} +5 -5
- package/dist/{summary-FWHAX55O.js → summary-WTQZ7XG2.js} +9 -9
- package/dist/{test-F7JNJZYP.js → test-SGO6I5Z7.js} +6 -6
- package/dist/{test-git-BTAOIUE2.js → test-git-XM4TM65W.js} +3 -3
- package/dist/{test-jira-CHYNV33F.js → test-jira-LDTOYFSD.js} +3 -3
- package/dist/{test-prefix-Q6TFSU6F.js → test-prefix-GBO37XCN.js} +3 -3
- package/dist/{test-webserver-EONCG7E7.js → test-webserver-NZ3JTVLL.js} +5 -5
- package/dist/{vscode-VA5X4P25.js → vscode-6XUGHJKL.js} +5 -5
- package/package.json +1 -1
- package/dist/ClaudeContextManager-QXX6ZFST.js +0 -14
- package/dist/ClaudeService-NJNK2SUH.js +0 -13
- package/dist/chunk-4WJNIR5O.js.map +0 -1
- package/dist/chunk-FB47TIJG.js.map +0 -1
- package/dist/chunk-LXLMMXXY.js.map +0 -1
- package/dist/chunk-ONQYPICO.js.map +0 -1
- package/dist/chunk-UD3WJDIV.js.map +0 -1
- package/dist/chunk-UVD4CZKS.js +0 -101
- package/dist/chunk-UVD4CZKS.js.map +0 -1
- package/dist/chunk-UWGVCXRF.js.map +0 -1
- package/dist/chunk-YYAKPQBT.js.map +0 -1
- package/dist/chunk-ZHPNZC75.js.map +0 -1
- package/dist/commit-L3EPY5QG.js.map +0 -1
- package/dist/ignite-CGOV3TD4.js.map +0 -1
- package/dist/plan-U7ZQWLFY.js.map +0 -1
- package/dist/rebase-DWIB77KV.js.map +0 -1
- /package/dist/{BranchNamingService-ECJHBB67.js.map → BranchNamingService-25KSZAEM.js.map} +0 -0
- /package/dist/{ClaudeContextManager-QXX6ZFST.js.map → ClaudeContextManager-66GR4BGM.js.map} +0 -0
- /package/dist/{ClaudeService-NJNK2SUH.js.map → ClaudeService-7KM5NA5Z.js.map} +0 -0
- /package/dist/{LoomLauncher-L64HHS3T.js.map → LoomLauncher-TDLZSYG2.js.map} +0 -0
- /package/dist/{PromptTemplateManager-DULSVRRE.js.map → PromptTemplateManager-YOE2SIPG.js.map} +0 -0
- /package/dist/{SettingsManager-BQDQA3FK.js.map → SettingsManager-FNKCOZMQ.js.map} +0 -0
- /package/dist/{build-5GO3XW26.js.map → build-VHGEMXBA.js.map} +0 -0
- /package/dist/{chunk-MNHZB4Z2.js.map → chunk-4FGEGQW4.js.map} +0 -0
- /package/dist/{chunk-WY4QBK43.js.map → chunk-63QWFWH3.js.map} +0 -0
- /package/dist/{chunk-SF2P22EE.js.map → chunk-C6HNNJIV.js.map} +0 -0
- /package/dist/{chunk-5MWV33NN.js.map → chunk-CVCTIDDK.js.map} +0 -0
- /package/dist/{chunk-RYWFS37M.js.map → chunk-E6KOWMKA.js.map} +0 -0
- /package/dist/{chunk-6EU6TCF6.js.map → chunk-EVPZFV3K.js.map} +0 -0
- /package/dist/{chunk-ZEWU5PZK.js.map → chunk-G5V75JD5.js.map} +0 -0
- /package/dist/{chunk-VGGST52X.js.map → chunk-I5T677EA.js.map} +0 -0
- /package/dist/{chunk-VECNX6VX.js.map → chunk-KIK2ZFAL.js.map} +0 -0
- /package/dist/{chunk-ZW2LKWWE.js.map → chunk-KVHIAWVT.js.map} +0 -0
- /package/dist/{chunk-3D7WQM7I.js.map → chunk-LLHXQS3C.js.map} +0 -0
- /package/dist/{chunk-Y4YZTHZE.js.map → chunk-LUKXJSRI.js.map} +0 -0
- /package/dist/{chunk-J5S7DFYC.js.map → chunk-QVLPWNE3.js.map} +0 -0
- /package/dist/{chunk-SN3SQCFK.js.map → chunk-S7PZA6IV.js.map} +0 -0
- /package/dist/{chunk-JO2LZ6EQ.js.map → chunk-SWSJWA2S.js.map} +0 -0
- /package/dist/{chunk-NRSWLOAZ.js.map → chunk-WXIM2WS7.js.map} +0 -0
- /package/dist/{claude-P3NQR6IJ.js.map → claude-7GGEWVEM.js.map} +0 -0
- /package/dist/{compile-ZS4HYRX5.js.map → compile-7ALJHZ4N.js.map} +0 -0
- /package/dist/{contribute-ORDDQGSL.js.map → contribute-5GKLK3BQ.js.map} +0 -0
- /package/dist/{dev-server-FYZ2AQIH.js.map → dev-server-7SMIB7OF.js.map} +0 -0
- /package/dist/{git-ET64COO3.js.map → git-GTLKAZRJ.js.map} +0 -0
- /package/dist/{init-GFQ5W7GK.js.map → init-32YOKXRL.js.map} +0 -0
- /package/dist/{issues-T4ZZSPEG.js.map → issues-4UUAQ5K6.js.map} +0 -0
- /package/dist/{lint-6TQXDZ3T.js.map → lint-AAN2NZWG.js.map} +0 -0
- /package/dist/{open-5QZGXQRF.js.map → open-FXWW3VI4.js.map} +0 -0
- /package/dist/{recap-MX63HAKV.js.map → recap-OMBOKJST.js.map} +0 -0
- /package/dist/{run-O3TFNQFC.js.map → run-BBXLRIZB.js.map} +0 -0
- /package/dist/{shell-G6VC2CYR.js.map → shell-RF7LTND5.js.map} +0 -0
- /package/dist/{summary-FWHAX55O.js.map → summary-WTQZ7XG2.js.map} +0 -0
- /package/dist/{test-F7JNJZYP.js.map → test-SGO6I5Z7.js.map} +0 -0
- /package/dist/{test-git-BTAOIUE2.js.map → test-git-XM4TM65W.js.map} +0 -0
- /package/dist/{test-jira-CHYNV33F.js.map → test-jira-LDTOYFSD.js.map} +0 -0
- /package/dist/{test-prefix-Q6TFSU6F.js.map → test-prefix-GBO37XCN.js.map} +0 -0
- /package/dist/{test-webserver-EONCG7E7.js.map → test-webserver-NZ3JTVLL.js.map} +0 -0
- /package/dist/{vscode-VA5X4P25.js.map → vscode-6XUGHJKL.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/first-run-setup.ts","../src/commands/start.ts"],"sourcesContent":["import { existsSync } from 'fs'\nimport { readFile } from 'fs/promises'\nimport path from 'path'\nimport chalk from 'chalk'\nimport { logger } from './logger.js'\nimport { FirstRunManager } from './FirstRunManager.js'\nimport { getRepoRoot } from './git.js'\nimport { promptConfirmation, waitForKeypress } from './prompt.js'\n\n/**\n * Get the project root path for first-run tracking\n * Uses git repo root if available, otherwise falls back to cwd\n * This ensures consistent path resolution regardless of where the CLI is run from\n */\nasync function getProjectRoot(): Promise<string> {\n\tconst repoRoot = await getRepoRoot()\n\tif (repoRoot) {\n\t\tlogger.debug(`getProjectRoot: Using git repo root: ${repoRoot}`)\n\t\treturn repoRoot\n\t}\n\tconst cwd = process.cwd()\n\tlogger.debug(`getProjectRoot: Not in git repo, using cwd: ${cwd}`)\n\treturn cwd\n}\n\n/**\n * Check if project needs first-run setup\n * Returns true if:\n * 1. Project is not tracked as configured globally AND\n * 2. .iloom directory is missing or settings files are empty\n *\n * Uses git repo root for path resolution to ensure consistent behavior\n * regardless of whether the CLI is run from a subdirectory or worktree\n */\nexport async function needsFirstRunSetup(): Promise<boolean> {\n\tconst projectRoot = await getProjectRoot()\n\tconst firstRunManager = new FirstRunManager()\n\n\t// Check if project is tracked as configured globally\n\tconst isConfigured = await firstRunManager.isProjectConfigured(projectRoot)\n\tif (isConfigured) {\n\t\tlogger.debug('needsFirstRunSetup: Project is tracked as configured globally')\n\t\treturn false\n\t}\n\n\tconst iloomDir = path.join(projectRoot, '.iloom')\n\n\t// Check if .iloom directory exists\n\tif (!existsSync(iloomDir)) {\n\t\treturn true\n\t}\n\n\t// Check if either settings file has meaningful content\n\tconst settingsPath = path.join(iloomDir, 'settings.json')\n\tconst settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n\n\tconst hasSettings = await hasNonEmptySettings(settingsPath)\n\tconst hasLocalSettings = await hasNonEmptySettings(settingsLocalPath)\n\n\treturn !hasSettings && !hasLocalSettings\n}\n\nasync function hasNonEmptySettings(filePath: string): Promise<boolean> {\n\tif (!existsSync(filePath)) return false\n\ttry {\n\t\tconst content = await readFile(filePath, 'utf-8')\n\t\tconst parsed = JSON.parse(content)\n\t\treturn Object.keys(parsed).length > 0\n\t} catch {\n\t\treturn false\n\t}\n}\n\n/**\n * Display default configuration values in a formatted box\n */\nfunction displayDefaultsBox(): void {\n\tlogger.info(chalk.bold('Default Configuration:'))\n\tlogger.info('')\n\tlogger.info(` ${chalk.cyan('Main Branch:')} main`)\n\tlogger.info(` ${chalk.cyan('IDE:')} vscode`)\n\tlogger.info(` ${chalk.cyan('Issue Tracker:')} GitHub Issues`)\n\tlogger.info(` ${chalk.cyan('Merge Mode:')} local ${chalk.dim('(merge locally)')}`)\n\tlogger.info(` ${chalk.cyan('Base Port:')} 3000`)\n}\n\n/**\n * Launch interactive first-run setup via InitCommand\n * Shows defaults first, allows quick acceptance or full wizard\n */\nexport async function launchFirstRunSetup(): Promise<void> {\n\tlogger.info('First-time project setup detected.')\n\tlogger.info('')\n\n\t// Display the defaults\n\tdisplayDefaultsBox()\n\n\tlogger.info('')\n\n\t// Import prompt utility\n\n\t// Ask if defaults are OK (default to Yes)\n\tconst acceptDefaults = await promptConfirmation(\n\t\t'Are these defaults OK?',\n\t\ttrue // default to true, so Enter accepts\n\t)\n\n\tif (acceptDefaults) {\n\t\t// User accepted defaults - just mark as configured\n\t\tconst projectRoot = await getProjectRoot()\n\t\tconst firstRunManager = new FirstRunManager()\n\t\tawait firstRunManager.markProjectAsConfigured(projectRoot)\n\t\tlogger.info(chalk.green('Configuration complete! Using defaults.'))\n\t\tlogger.info('You can run `il init` anytime to customize settings.')\n\t\treturn\n\t}\n\n\t// User declined - launch full wizard\n\tlogger.info('')\n\tlogger.info('iloom will now launch an interactive configuration session with Claude.')\n\n\tawait waitForKeypress('Press any key to start configuration...')\n\n\tconst { InitCommand } = await import('../commands/init.js')\n\tconst initCommand = new InitCommand()\n\tawait initCommand.execute(\n\t\t'Help me configure iloom settings for this project. This is my first time using iloom here. Note: Your iloom command will execute once we are done with configuration changes.'\n\t)\n\t// Note: InitCommand.execute() now handles markProjectAsConfigured() internally\n\t// when the guided init completes successfully\n\n\tlogger.info('Configuration complete! Continuing with your original command...')\n}\n","import path from 'path'\nimport { getLogger } from '../utils/logger-context.js'\nimport type { IssueTracker } from '../lib/IssueTracker.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { LoomManager } from '../lib/LoomManager.js'\nimport { DefaultBranchNamingService } from '../lib/BranchNamingService.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { EnvironmentManager } from '../lib/EnvironmentManager.js'\nimport { ClaudeContextManager } from '../lib/ClaudeContextManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { CLIIsolationManager } from '../lib/CLIIsolationManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { DatabaseManager } from '../lib/DatabaseManager.js'\nimport { findMainWorktreePathWithSettings } from '../utils/git.js'\nimport { matchIssueIdentifier } from '../utils/IdentifierParser.js'\nimport { loadEnvIntoProcess } from '../utils/env.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport { createNeonProviderFromSettings } from '../utils/neon-helpers.js'\nimport { getConfiguredRepoFromSettings, hasMultipleRemotes } from '../utils/remote.js'\nimport { capitalizeFirstLetter } from '../utils/text.js'\nimport type { StartOptions, StartResult } from '../types/index.js'\nimport { fetchChildIssues, fetchChildIssueDetails } from '../utils/list-children.js'\nimport { buildDependencyMap } from '../utils/dependency-map.js'\nimport { IssueTrackerFactory } from '../lib/IssueTrackerFactory.js'\nimport { launchFirstRunSetup, needsFirstRunSetup } from '../utils/first-run-setup.js'\nimport { isInteractiveEnvironment, promptConfirmation } from '../utils/prompt.js'\nimport { TelemetryService } from '../lib/TelemetryService.js'\nimport type { LoomCreatedProperties } from '../types/telemetry.js'\n\nexport interface StartCommandInput {\n\tidentifier: string\n\toptions: StartOptions\n}\n\nexport interface ParsedInput {\n\ttype: 'issue' | 'pr' | 'branch' | 'description' | 'epic'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n}\n\nexport class StartCommand {\n\tprivate issueTracker: IssueTracker\n\tprivate loomManager: LoomManager | null = null\n\tprivate settingsManager: SettingsManager\n\tprivate providedLoomManager: LoomManager | undefined\n\tprivate githubService: GitHubService | null = null\n\n\tconstructor(\n\t\tissueTracker: IssueTracker,\n\t\tloomManager?: LoomManager,\n\t\t_agentManager?: AgentManager, // Kept for API compatibility\n\t\tsettingsManager?: SettingsManager\n\t) {\n\t\tthis.issueTracker = issueTracker\n\t\tthis.settingsManager = settingsManager ?? new SettingsManager()\n\t\t// Store provided LoomManager for testing, but don't initialize yet\n\t\tthis.providedLoomManager = loomManager\n\n\t\t// Load environment variables first\n\t\tconst envResult = loadEnvIntoProcess()\n\t\tif (envResult.error) {\n\t\t\tgetLogger().debug(`Environment loading warning: ${envResult.error.message}`)\n\t\t}\n\t\tif (envResult.parsed) {\n\t\t\tgetLogger().debug(`Loaded ${Object.keys(envResult.parsed).length} environment variables`)\n\t\t}\n\t}\n\n\t/**\n\t * Get or create a GitHubService instance for PR operations\n\t * Used when the configured issue tracker doesn't support PRs (e.g., Linear)\n\t */\n\tprivate getGitHubService(): GitHubService {\n\t\tthis.githubService ??= new GitHubService()\n\t\treturn this.githubService\n\t}\n\n\t/**\n\t * Initialize LoomManager with the main worktree path\n\t * Uses lazy initialization to ensure we have the correct path\n\t */\n\tprivate async initializeLoomManager(): Promise<LoomManager> {\n\t\tif (this.loomManager) {\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\tif (this.providedLoomManager) {\n\t\t\tthis.loomManager = this.providedLoomManager\n\t\t\treturn this.loomManager\n\t\t}\n\n\t\t// Find main worktree path\n\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t// Load settings to get database configuration\n\t\tconst settings = await this.settingsManager.loadSettings()\n\n\t\t// Create DatabaseManager with NeonProvider and EnvironmentManager\n\t\tconst environmentManager = new EnvironmentManager()\n\t\tconst neonProvider = createNeonProviderFromSettings(settings)\n\t\tconst databaseUrlEnvVarName = settings.capabilities?.database?.databaseUrlEnvVarName ?? 'DATABASE_URL'\n\n\t\tconst databaseManager = new DatabaseManager(neonProvider, environmentManager, databaseUrlEnvVarName)\n\n\t\t// Create BranchNamingService (defaults to Claude-based strategy)\n\t\tconst branchNaming = new DefaultBranchNamingService({ useClaude: true })\n\n\t\tthis.loomManager = new LoomManager(\n\t\t\tnew GitWorktreeManager(mainWorktreePath),\n\t\t\tthis.issueTracker,\n\t\t\tbranchNaming, // Add branch naming service\n\t\t\tenvironmentManager, // Reuse same instance\n\t\t\tnew ClaudeContextManager(),\n\t\t\tnew ProjectCapabilityDetector(),\n\t\t\tnew CLIIsolationManager(),\n\t\t\tthis.settingsManager, // Use same instance with CLI overrides\n\t\t\tdatabaseManager // Add database manager\n\t\t)\n\n\t\treturn this.loomManager\n\t}\n\n\t/**\n\t * Main entry point for the start command\n\t */\n\tpublic async execute(input: StartCommandInput): Promise<StartResult | void> {\n\t\tconst isJsonMode = input.options.json === true\n\n\t\ttry {\n\t\t\t// Step 0: Load settings and get configured repo for GitHub operations\n\t\t\tconst initialSettings = await this.settingsManager.loadSettings()\n\n\t\t\t// Skip first-run setup in JSON mode\n\t\t\tif (!isJsonMode && (process.env.FORCE_FIRST_TIME_SETUP === \"true\" || await needsFirstRunSetup())) {\n\t\t\t\tawait launchFirstRunSetup()\n\t\t\t\t// Reload settings and recreate issueTracker if provider changed during setup\n\t\t\t\tconst newSettings = await this.settingsManager.loadSettings()\n\t\t\t\tconst newProvider = newSettings.issueManagement?.provider ?? 'github'\n\t\t\t\tif (newProvider !== this.issueTracker.providerName) {\n\t\t\t\t\tgetLogger().debug(`Reinitializing issue tracker: provider changed to \"${newProvider}\"`)\n\t\t\t\t\tthis.issueTracker = IssueTrackerFactory.create(newSettings)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet repo: string | undefined\n\n\t\t\t// Only get repo if we have multiple remotes (prehook already validated config)\n\t\t\tif (this.issueTracker.providerName === 'github' && (await hasMultipleRemotes())) {\n\t\t\t\t// Only relevant for GitHub - Linear doesn't use repo info\n\t\t\t\trepo = await getConfiguredRepoFromSettings(initialSettings)\n\t\t\t\tgetLogger().info(`Using GitHub repository: ${repo}`)\n\t\t\t}\n\n\t\t\t// Step 0.5: Initialize LoomManager with main worktree path\n\t\t\tconst loomManager = await this.initializeLoomManager()\n\n\t\t\t// Step 0.6: Detect if running from inside an existing loom (for nested loom support)\n\t\t\tlet parentLoom = await this.detectParentLoom(loomManager)\n\n\t\t\t// Step 1: Parse and validate input (pass repo to methods)\n\t\t\tconst parsed = await this.parseInput(input.identifier, repo)\n\n\t\t\t// Step 2: Validate based on type\n\t\t\tawait this.validateInput(parsed, repo)\n\n\t\t\t// Step 2.4: Handle child loom decision\n\t\t\tif (parentLoom) {\n\n\t\t\t\t// Format display message based on parent type\n\t\t\t\tconst parentDisplay = parentLoom.type === 'issue'\n\t\t\t\t\t? `issue #${parentLoom.identifier}`\n\t\t\t\t\t: parentLoom.type === 'pr'\n\t\t\t\t\t? `PR #${parentLoom.identifier}`\n\t\t\t\t\t: `branch ${parentLoom.identifier}`\n\n\t\t\t\t// Check for explicit flag first\n\t\t\t\tif (input.options.childLoom === true) {\n\t\t\t\t\t// --child-loom flag: force child loom (no prompt)\n\t\t\t\t\tgetLogger().info(`Creating as child loom of ${parentDisplay} (--child-loom flag)`)\n\t\t\t\t} else if (input.options.childLoom === false) {\n\t\t\t\t\t// --no-child-loom flag: force independent (no prompt)\n\t\t\t\t\tparentLoom = null\n\t\t\t\t\tgetLogger().info('Creating as independent loom (--no-child-loom flag)')\n\t\t\t\t} else {\n\t\t\t\t\t// No flag: use existing behavior (prompt or error if non-interactive)\n\t\t\t\t\t// JSON mode requires explicit flag\n\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --child-loom or --no-child-loom flag when running from inside a loom')\n\t\t\t\t\t}\n\t\t\t\t\tlet createAsChild = true // Default for non-interactive\n\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\tcreateAsChild = await promptConfirmation(\n\t\t\t\t\t\t\t`You are not in your main worktree. Create as a child loom of ${parentDisplay}?`,\n\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --child-loom or --no-child-loom to specify behavior')\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!createAsChild) {\n\t\t\t\t\t\tparentLoom = null // User declined, proceed as normal loom\n\t\t\t\t\t\tgetLogger().info('Creating as independent loom')\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (input.options.childLoom === true) {\n\t\t\t\t// --child-loom flag but not in a parent loom - ignore silently (per requirements)\n\t\t\t\tgetLogger().debug('--child-loom flag provided but not running from inside an existing loom (ignored)')\n\t\t\t}\n\t\t\t// Note: --no-child-loom when no parent is a no-op (already independent)\n\n\t\t\t// Step 2.5: Handle description input - create issue\n\t\t\tif (parsed.type === 'description') {\n\t\t\t\tgetLogger().info('Creating issue from description...')\n\t\t\t\t// Apply first-letter capitalization to title and body\n\t\t\t\tconst title = capitalizeFirstLetter(parsed.originalInput)\n\t\t\t\tconst body = input.options.body ? capitalizeFirstLetter(input.options.body) : \"\"\n\t\t\t\tconst result = await this.issueTracker.createIssue(\n\t\t\t\t\ttitle, // Use capitalized description as title\n\t\t\t\t\tbody // Use capitalized body or empty\n\t\t\t\t)\n\t\t\t\tgetLogger().success(`Created issue #${result.number}: ${result.url}`)\n\t\t\t\t// Update parsed to be an issue type with the new number\n\t\t\t\tparsed.type = 'issue'\n\t\t\t\tparsed.number = result.number\n\t\t\t}\n\n\t\t\t// Step 2.6: Detect epic (issue with child issues) and handle --epic/--no-epic flags\n\t\t\tlet childIssueNumbers: string[] = []\n\t\t\tlet childIssues: Array<{ number: string; title: string; body: string; url: string }> = []\n\t\t\tlet dependencyMap: Record<string, string[]> = {}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number) {\n\t\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\t\tconst epicIssueTracker = IssueTrackerFactory.create(settings)\n\t\t\t\tlet children: Awaited<ReturnType<typeof fetchChildIssues>> = []\n\t\t\t\ttry {\n\t\t\t\t\tchildren = await fetchChildIssues(String(parsed.number), epicIssueTracker, repo)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tgetLogger().warn(`Failed to check for child issues: ${error instanceof Error ? error.message : 'Unknown error'}. Proceeding as normal loom.`)\n\t\t\t\t}\n\n\t\t\t\tif (children.length > 0) {\n\t\t\t\t\tchildIssueNumbers = children.map(c => c.id)\n\t\t\t\t\tlet createAsEpic = false\n\n\t\t\t\t\tif (input.options.epic === true) {\n\t\t\t\t\t\t// --epic flag: force epic mode (no prompt)\n\t\t\t\t\t\tcreateAsEpic = true\n\t\t\t\t\t\tgetLogger().info(`Creating as epic loom with ${children.length} child issue(s) (--epic flag)`)\n\t\t\t\t\t} else if (input.options.epic === false) {\n\t\t\t\t\t\t// --no-epic flag: proceed as normal loom (no prompt)\n\t\t\t\t\t\tcreateAsEpic = false\n\t\t\t\t\t\tgetLogger().info('Creating as normal loom (--no-epic flag)')\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// No flag: prompt or error\n\t\t\t\t\t\tif (isJsonMode) {\n\t\t\t\t\t\t\tthrow new Error('JSON mode requires explicit --epic or --no-epic flag when issue has child issues')\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (isInteractiveEnvironment()) {\n\t\t\t\t\t\t\tcreateAsEpic = await promptConfirmation(\n\t\t\t\t\t\t\t\t`This issue has ${children.length} child issue(s). Create as epic loom?`,\n\t\t\t\t\t\t\t\ttrue // Default yes\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthrow new Error('Non-interactive environment detected, use either --epic or --no-epic to specify behavior')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (createAsEpic) {\n\t\t\t\t\t\tparsed.type = 'epic'\n\n\t\t\t\t\t\t// Fetch rich child issue details and dependency map for epic metadata\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst [details, depMap] = await Promise.all([\n\t\t\t\t\t\t\t\tfetchChildIssueDetails(String(parsed.number), epicIssueTracker, repo),\n\t\t\t\t\t\t\t\tbuildDependencyMap(childIssueNumbers, settings, repo),\n\t\t\t\t\t\t\t])\n\t\t\t\t\t\t\tchildIssues = details ?? []\n\t\t\t\t\t\t\tdependencyMap = depMap ?? {}\n\t\t\t\t\t\t\tgetLogger().info(`Fetched ${childIssues.length} child issue details and dependency map`)\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t// Revert to issue type since child data fetch failed\n\t\t\t\t\t\t\t// il spin needs child data to enter swarm mode, so an epic without it would be broken\n\t\t\t\t\t\t\tparsed.type = 'issue'\n\t\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t\t\tgetLogger().warn(`Failed to fetch epic child data, reverting to normal loom: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Not creating as epic, clear child issue numbers\n\t\t\t\t\t\tchildIssueNumbers = []\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// --epic or --no-epic flags are silently ignored when there are no child issues\n\t\t\t}\n\n\t\t\t// Step 2.7: Confirm bypassPermissions mode if applicable\n\t\t\t// Only prompt in interactive mode when Claude is enabled.\n\t\t\t// Skip when: --no-claude (Claude won't launch now), JSON mode (non-interactive).\n\t\t\t// The explicit --one-shot=bypassPermissions flag is sufficient intent.\n\t\t\t// The warning is shown again when Claude launches via 'il spin'.\n\t\t\tif (input.options.oneShot === 'bypassPermissions' && input.options.claude !== false && !isJsonMode) {\n\t\t\t\tconst confirmed = await promptConfirmation(\n\t\t\t\t\t'WARNING: bypassPermissions mode will allow Claude to execute all tool calls without confirmation. ' +\n\t\t\t\t\t'This can be dangerous. Do you want to proceed?'\n\t\t\t\t)\n\t\t\t\tif (!confirmed) {\n\t\t\t\t\tgetLogger().info('Operation cancelled by user')\n\t\t\t\t\tprocess.exit(0)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Step 2.8: Load workflow-specific settings with CLI overrides\n\t\t\tconst cliOverrides = extractSettingsOverrides()\n\t\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\t\tconst workflowType = parsed.type === 'branch' ? 'regular' : parsed.type === 'epic' ? 'issue' : parsed.type\n\t\t\tconst workflowConfig = settings.workflows?.[workflowType]\n\n\t\t\t// Step 2.9: Extract raw --set arguments and executable path for forwarding to spin\n\t\t\tconst { extractRawSetArguments, getExecutablePath } = await import('../utils/cli-overrides.js')\n\t\t\tconst setArguments = extractRawSetArguments()\n\t\t\tconst executablePath = getExecutablePath()\n\n\t\t\t// Step 3: Log success and create loom\n\t\t\tgetLogger().info(`Validated input: ${this.formatParsedInput(parsed)}`)\n\n\t\t\t// Step 4: Create loom using LoomManager\n\t\t\tconst identifier =\n\t\t\t\tparsed.type === 'branch'\n\t\t\t\t\t? parsed.branchName ?? ''\n\t\t\t\t\t: parsed.number ?? 0\n\n\t\t\t// Apply configuration precedence: CLI flags > workflow config > defaults (true)\n\t\t\tconst enableClaude = input.options.claude ?? workflowConfig?.startAiAgent ?? true\n\t\t\tconst enableCode = input.options.code ?? workflowConfig?.startIde ?? true\n\t\t\tconst enableDevServer = input.options.devServer ?? workflowConfig?.startDevServer ?? true\n\t\t\tconst enableTerminal = input.options.terminal ?? workflowConfig?.startTerminal ?? false\n\n\t\t\tgetLogger().debug('Final workflow config values:', {\n\t\t\t\tenableClaude,\n\t\t\t\tenableCode,\n\t\t\t\tenableDevServer,\n\t\t\t\tenableTerminal,\n\t\t\t})\n\n\t\t\tconst loom = await loomManager.createIloom({\n\t\t\t\ttype: parsed.type,\n\t\t\t\tidentifier,\n\t\t\t\toriginalInput: parsed.originalInput,\n\t\t\t\t...(parentLoom && { parentLoom }),\n\t\t\t\toptions: {\n\t\t\t\t\tenableClaude,\n\t\t\t\t\tenableCode,\n\t\t\t\t\tenableDevServer,\n\t\t\t\t\tenableTerminal,\n\t\t\t\t\t...(input.options.oneShot && { oneShot: input.options.oneShot }),\n\t\t\t\t\t...(setArguments.length > 0 && { setArguments }),\n\t\t\t\t\t...(executablePath && { executablePath }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t\t...(childIssues.length > 0 && { childIssues }),\n\t\t\t\t\t...(Object.keys(dependencyMap).length > 0 && { dependencyMap }),\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tgetLogger().success(`Created loom: ${loom.id} at ${loom.path}`)\n\n\t\t\t// Track loom.created telemetry event\n\t\t\ttry {\n\t\t\t\tconst oneShotMap: Record<string, LoomCreatedProperties['one_shot_mode']> = {\n\t\t\t\t\tnoReview: 'skip-reviews',\n\t\t\t\t\tbypassPermissions: 'yolo',\n\t\t\t\t}\n\t\t\t\tTelemetryService.getInstance().track('loom.created', {\n\t\t\t\t\tsource_type: parsed.type === 'epic' ? 'issue' : parsed.type as LoomCreatedProperties['source_type'],\n\t\t\t\t\ttracker: this.issueTracker.providerName,\n\t\t\t\t\tis_child_loom: !!parentLoom,\n\t\t\t\t\tone_shot_mode: oneShotMap[input.options.oneShot ?? ''] ?? 'default',\n\t\t\t\t})\n\t\t\t} catch (error: unknown) {\n\t\t\t\tgetLogger().debug(`Failed to track loom.created telemetry: ${error instanceof Error ? error.message : String(error)}`)\n\t\t\t}\n\n\t\t\tgetLogger().info(` Branch: ${loom.branch}`)\n\t\t\t// Only show port for web projects\n\t\t\tif (loom.capabilities?.includes('web')) {\n\t\t\t\tgetLogger().info(` Port: ${loom.port}`)\n\t\t\t}\n\t\t\tif (loom.issueData?.title) {\n\t\t\t\tgetLogger().info(` Title: ${loom.issueData.title}`)\n\t\t\t}\n\t\t\tif (parsed.type === 'epic') {\n\t\t\t\tgetLogger().info(` Epic: yes (${childIssueNumbers.length} child issue(s))`)\n\t\t\t}\n\n\t\t\t// Return StartResult in JSON mode\n\t\t\tif (isJsonMode) {\n\t\t\t\treturn {\n\t\t\t\t\tid: loom.id,\n\t\t\t\t\tpath: loom.path,\n\t\t\t\t\tbranch: loom.branch,\n\t\t\t\t\ttype: parsed.type,\n\t\t\t\t\tidentifier: loom.identifier,\n\t\t\t\t\t...(loom.port !== undefined && { port: loom.port }),\n\t\t\t\t\t...(loom.issueData?.title && { title: loom.issueData.title }),\n\t\t\t\t\t...(loom.capabilities && { capabilities: loom.capabilities }),\n\t\t\t\t\t...(childIssueNumbers.length > 0 && { childIssueNumbers }),\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) {\n\t\t\t\tgetLogger().error(`${error.message}`)\n\t\t\t} else {\n\t\t\t\tgetLogger().error('An unknown error occurred')\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Parse input to determine type and extract relevant data\n\t */\n\tprivate async parseInput(identifier: string, repo?: string): Promise<ParsedInput> {\n\t\t// Check if user wants to skip capitalization by prefixing with space\n\t\t// We preserve this for description types so capitalizeFirstLetter() can handle it\n\t\tconst hasLeadingSpace = identifier.startsWith(' ')\n\n\t\t// Handle empty input\n\t\tconst trimmedIdentifier = identifier.trim()\n\t\tif (!trimmedIdentifier) {\n\t\t\tthrow new Error('Missing required argument: identifier')\n\t\t}\n\n\t\t// Check for description: >15 chars AND has spaces (likely a natural language description)\n\t\t// Short inputs with spaces are rejected later as invalid branch names\n\t\tconst spaceCount = (trimmedIdentifier.match(/ /g) ?? []).length\n\t\tif (trimmedIdentifier.length > 15 && spaceCount >= 1) {\n\t\t\t// Preserve leading space if present so capitalizeFirstLetter() can detect the override\n\t\t\treturn {\n\t\t\t\ttype: 'description',\n\t\t\t\toriginalInput: hasLeadingSpace ? ' ' + trimmedIdentifier : trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123, Pr-123 (case-insensitive)\n\t\tconst prPattern = /^pr[/-](\\d+)$/i\n\t\tconst prMatch = trimmedIdentifier.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: parseInt(prMatch[1], 10),\n\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue identifier patterns using shared utility\n\t\t// - Project key pattern: ENG-123 (requires at least 2 letters before dash)\n\t\t// - Numeric pattern: #123 or 123 (GitHub format)\n\t\tconst identifierMatch = matchIssueIdentifier(trimmedIdentifier)\n\n\t\tif (identifierMatch.type === 'project-key' && identifierMatch.identifier) {\n\t\t\t// Use IssueTracker to validate it exists\n\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\ttrimmedIdentifier,\n\t\t\t\trepo\n\t\t\t)\n\n\t\t\tif (detection.type === 'issue' && detection.identifier) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: detection.identifier, // Keep as string for project key identifiers\n\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Project key identifier format matched but not found\n\t\t\tthrow new Error(\n\t\t\t\t`Could not find issue matching identifier ${identifierMatch.identifier}`\n\t\t\t)\n\t\t}\n\n\t\t// Check for numeric pattern (could be issue or PR)\n\t\tif (identifierMatch.type === 'numeric' && identifierMatch.identifier) {\n\t\t\tconst number = parseInt(identifierMatch.identifier, 10)\n\n\t\t\t// If issue tracker supports PRs, use it for detection\n\t\t\tif (this.issueTracker.supportsPullRequests) {\n\t\t\t\tconst detection = await this.issueTracker.detectInputType(\n\t\t\t\t\ttrimmedIdentifier,\n\t\t\t\t\trepo\n\t\t\t\t)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else if (detection.type === 'issue') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Could not find issue or PR #${number}`)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Issue tracker doesn't support PRs (e.g., Linear, Jira)\n\t\t\t\t// Check GitHub first for PR, then fall back to issue tracker for issues\n\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\tconst detection = await githubService.detectInputType(trimmedIdentifier, repo)\n\n\t\t\t\tif (detection.type === 'pr') {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'pr',\n\t\t\t\t\t\tnumber: detection.identifier ? parseInt(detection.identifier, 10) : number,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Not a GitHub PR - try the configured issue tracker\n\t\t\t\t\t// This allows future trackers with numeric IDs to work naturally\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: 'issue',\n\t\t\t\t\t\tnumber,\n\t\t\t\t\t\toriginalInput: trimmedIdentifier,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Treat as branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: trimmedIdentifier,\n\t\t\toriginalInput: trimmedIdentifier,\n\t\t}\n\t}\n\n\t/**\n\t * Validate the parsed input based on its type\n\t */\n\tprivate async validateInput(parsed: ParsedInput, repo?: string): Promise<void> {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid PR number')\n\t\t\t\t}\n\n\t\t\t\t// Determine which service to use for PR operations\n\t\t\t\tif (this.issueTracker.supportsPullRequests && this.issueTracker.fetchPR && this.issueTracker.validatePRState) {\n\t\t\t\t\t// Use issue tracker for PR operations (e.g., GitHub)\n\t\t\t\t\tconst pr = await this.issueTracker.fetchPR(parsed.number, repo)\n\t\t\t\t\tawait this.issueTracker.validatePRState(pr)\n\t\t\t\t} else {\n\t\t\t\t\t// Use GitHubService for PR operations when issue tracker doesn't support PRs (e.g., Linear)\n\t\t\t\t\tconst githubService = this.getGitHubService()\n\t\t\t\t\tconst pr = await githubService.fetchPR(parsed.number as number, repo)\n\t\t\t\t\tawait githubService.validatePRState(pr)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated PR #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'issue': {\n\t\t\t\tif (!parsed.number) {\n\t\t\t\t\tthrow new Error('Invalid issue number')\n\t\t\t\t}\n\t\t\t\t// Fetch and validate issue state\n\t\t\t\tconst issue = await this.issueTracker.fetchIssue(parsed.number, repo)\n\t\t\t\tawait this.issueTracker.validateIssueState(issue)\n\t\t\t\tgetLogger().debug(`Validated issue #${parsed.number}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'branch': {\n\t\t\t\tif (!parsed.branchName) {\n\t\t\t\t\tthrow new Error('Invalid branch name')\n\t\t\t\t}\n\t\t\t\t// Validate branch name characters (from bash script line 586)\n\t\t\t\tif (!this.isValidBranchName(parsed.branchName)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid branch name. Use only letters, numbers, hyphens, underscores, and slashes'\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tgetLogger().debug(`Validated branch name: ${parsed.branchName}`)\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tcase 'description': {\n\t\t\t\t// Description inputs are valid - they will be converted to issues\n\t\t\t\tgetLogger().debug('Detected description input', {\n\t\t\t\t\tlength: parsed.originalInput.length\n\t\t\t\t})\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tdefault: {\n\t\t\t\tconst unknownType = parsed as { type: string }\n\t\t\t\tthrow new Error(`Unknown input type: ${unknownType.type}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate branch name format\n\t */\n\tprivate isValidBranchName(branch: string): boolean {\n\t\t// Pattern from bash script line 586\n\t\treturn /^[a-zA-Z0-9/_-]+$/.test(branch)\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedInput): string {\n\t\tswitch (parsed.type) {\n\t\t\tcase 'pr':\n\t\t\t\treturn `PR #${parsed.number}`\n\t\t\tcase 'issue':\n\t\t\t\treturn `Issue #${parsed.number}`\n\t\t\tcase 'epic':\n\t\t\t\treturn `Epic #${parsed.number}`\n\t\t\tcase 'branch':\n\t\t\t\treturn `Branch '${parsed.branchName}'`\n\t\t\tcase 'description':\n\t\t\t\treturn `Description: ${parsed.originalInput.slice(0, 50)}...`\n\t\t\tdefault:\n\t\t\t\treturn 'Unknown input'\n\t\t}\n\t}\n\n\t/**\n\t * Detect if running from inside an existing loom worktree\n\t * Returns parent loom info if detected, null otherwise\n\t */\n\tprivate async detectParentLoom(loomManager: LoomManager): Promise<{\n\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\tidentifier: string | number\n\t\tbranchName: string\n\t\tworktreePath: string\n\t\tdatabaseBranch?: string\n\t} | null> {\n\t\ttry {\n\t\t\tconst cwd = process.cwd()\n\t\t\tconst looms = await loomManager.listLooms()\n\n\t\t\tif (!looms) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Get main worktree path to exclude it from valid parents\n\t\t\tconst mainWorktreePath = await findMainWorktreePathWithSettings()\n\n\t\t\t// Find loom containing current directory\n\t\t\t// Fix #2: Add path.sep check to prevent false positives (e.g., issue-123 vs issue-1234)\n\t\t\t// Exclude main worktree from being a valid parent\n\t\t\tconst parentLoom = looms.find(loom => {\n\t\t\t\t// Skip main worktree - it shouldn't be a parent for child looms\n\t\t\t\tif (loom.path === mainWorktreePath) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\t// Either exact match OR cwd starts with loom.path followed by path separator\n\t\t\t\treturn cwd === loom.path || cwd.startsWith(loom.path + path.sep)\n\t\t\t})\n\t\t\tif (!parentLoom) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tgetLogger().debug(`Detected parent loom: ${parentLoom.type} ${parentLoom.identifier} at ${parentLoom.path}`)\n\n\t\t\tconst result: {\n\t\t\t\ttype: 'issue' | 'pr' | 'branch' | 'epic'\n\t\t\t\tidentifier: string | number\n\t\t\t\tbranchName: string\n\t\t\t\tworktreePath: string\n\t\t\t\tdatabaseBranch?: string\n\t\t\t} = {\n\t\t\t\ttype: parentLoom.type,\n\t\t\t\tidentifier: parentLoom.identifier,\n\t\t\t\tbranchName: parentLoom.branch,\n\t\t\t\tworktreePath: parentLoom.path,\n\t\t\t}\n\n\t\t\t// Only include databaseBranch if it exists (exactOptionalPropertyTypes compatibility)\n\t\t\tif (parentLoom.databaseBranch) {\n\t\t\t\tresult.databaseBranch = parentLoom.databaseBranch\n\t\t\t}\n\n\t\t\t// Try to get database branch from parent's .env file via reverse lookup\n\t\t\tif (!result.databaseBranch) {\n\t\t\t\tconst databaseBranch = await loomManager.getDatabaseBranchForLoom(parentLoom.path)\n\t\t\t\tif (databaseBranch) {\n\t\t\t\t\tresult.databaseBranch = databaseBranch\n\t\t\t\t\tgetLogger().debug(`Detected parent database branch: ${databaseBranch}`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result\n\t\t} catch (error) {\n\t\t\t// If detection fails for any reason, just return null (don't break the start workflow)\n\t\t\tgetLogger().debug(`Failed to detect parent loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn null\n\t\t}\n\t}\n\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,gBAAgB;AACzB,OAAO,UAAU;AACjB,OAAO,WAAW;AAWlB,eAAe,iBAAkC;AAChD,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,UAAU;AACb,WAAO,MAAM,wCAAwC,QAAQ,EAAE;AAC/D,WAAO;AAAA,EACR;AACA,QAAM,MAAM,QAAQ,IAAI;AACxB,SAAO,MAAM,+CAA+C,GAAG,EAAE;AACjE,SAAO;AACR;AAWA,eAAsB,qBAAuC;AAC5D,QAAM,cAAc,MAAM,eAAe;AACzC,QAAM,kBAAkB,IAAI,gBAAgB;AAG5C,QAAM,eAAe,MAAM,gBAAgB,oBAAoB,WAAW;AAC1E,MAAI,cAAc;AACjB,WAAO,MAAM,+DAA+D;AAC5E,WAAO;AAAA,EACR;AAEA,QAAM,WAAW,KAAK,KAAK,aAAa,QAAQ;AAGhD,MAAI,CAAC,WAAW,QAAQ,GAAG;AAC1B,WAAO;AAAA,EACR;AAGA,QAAM,eAAe,KAAK,KAAK,UAAU,eAAe;AACxD,QAAM,oBAAoB,KAAK,KAAK,UAAU,qBAAqB;AAEnE,QAAM,cAAc,MAAM,oBAAoB,YAAY;AAC1D,QAAM,mBAAmB,MAAM,oBAAoB,iBAAiB;AAEpE,SAAO,CAAC,eAAe,CAAC;AACzB;AAEA,eAAe,oBAAoB,UAAoC;AACtE,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAClC,MAAI;AACH,UAAM,UAAU,MAAM,SAAS,UAAU,OAAO;AAChD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,WAAO,OAAO,KAAK,MAAM,EAAE,SAAS;AAAA,EACrC,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAKA,SAAS,qBAA2B;AACnC,SAAO,KAAK,MAAM,KAAK,wBAAwB,CAAC;AAChD,SAAO,KAAK,EAAE;AACd,SAAO,KAAK,KAAK,MAAM,KAAK,cAAc,CAAC,WAAW;AACtD,SAAO,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC,qBAAqB;AACxD,SAAO,KAAK,KAAK,MAAM,KAAK,gBAAgB,CAAC,kBAAkB;AAC/D,SAAO,KAAK,KAAK,MAAM,KAAK,aAAa,CAAC,eAAe,MAAM,IAAI,iBAAiB,CAAC,EAAE;AACvF,SAAO,KAAK,KAAK,MAAM,KAAK,YAAY,CAAC,aAAa;AACvD;AAMA,eAAsB,sBAAqC;AAC1D,SAAO,KAAK,oCAAoC;AAChD,SAAO,KAAK,EAAE;AAGd,qBAAmB;AAEnB,SAAO,KAAK,EAAE;AAKd,QAAM,iBAAiB,MAAM;AAAA,IAC5B;AAAA,IACA;AAAA;AAAA,EACD;AAEA,MAAI,gBAAgB;AAEnB,UAAM,cAAc,MAAM,eAAe;AACzC,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,gBAAgB,wBAAwB,WAAW;AACzD,WAAO,KAAK,MAAM,MAAM,yCAAyC,CAAC;AAClE,WAAO,KAAK,sDAAsD;AAClE;AAAA,EACD;AAGA,SAAO,KAAK,EAAE;AACd,SAAO,KAAK,yEAAyE;AAErF,QAAM,gBAAgB,yCAAyC;AAE/D,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,oBAAqB;AAC1D,QAAM,cAAc,IAAI,YAAY;AACpC,QAAM,YAAY;AAAA,IACjB;AAAA,EACD;AAIA,SAAO,KAAK,kEAAkE;AAC/E;;;ACpIA,OAAOA,WAAU;AA0CV,IAAM,eAAN,MAAmB;AAAA,EAOzB,YACC,cACA,aACA,eACA,iBACC;AAVF,SAAQ,cAAkC;AAG1C,SAAQ,gBAAsC;AAQ7C,SAAK,eAAe;AACpB,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAE9D,SAAK,sBAAsB;AAG3B,UAAM,YAAY,mBAAmB;AACrC,QAAI,UAAU,OAAO;AACpB,gBAAU,EAAE,MAAM,gCAAgC,UAAU,MAAM,OAAO,EAAE;AAAA,IAC5E;AACA,QAAI,UAAU,QAAQ;AACrB,gBAAU,EAAE,MAAM,UAAU,OAAO,KAAK,UAAU,MAAM,EAAE,MAAM,wBAAwB;AAAA,IACzF;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,mBAAkC;AACzC,SAAK,kBAAkB,IAAI,cAAc;AACzC,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,wBAA8C;AAnF7D;AAoFE,QAAI,KAAK,aAAa;AACrB,aAAO,KAAK;AAAA,IACb;AAEA,QAAI,KAAK,qBAAqB;AAC7B,WAAK,cAAc,KAAK;AACxB,aAAO,KAAK;AAAA,IACb;AAGA,UAAM,mBAAmB,MAAM,iCAAiC;AAGhE,UAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa;AAGzD,UAAM,qBAAqB,IAAI,mBAAmB;AAClD,UAAM,eAAe,+BAA+B,QAAQ;AAC5D,UAAM,0BAAwB,oBAAS,iBAAT,mBAAuB,aAAvB,mBAAiC,0BAAyB;AAExF,UAAM,kBAAkB,IAAI,gBAAgB,cAAc,oBAAoB,qBAAqB;AAGnG,UAAM,eAAe,IAAI,2BAA2B,EAAE,WAAW,KAAK,CAAC;AAEvE,SAAK,cAAc,IAAI;AAAA,MACtB,IAAI,mBAAmB,gBAAgB;AAAA,MACvC,KAAK;AAAA,MACL;AAAA;AAAA,MACA;AAAA;AAAA,MACA,IAAI,qBAAqB;AAAA,MACzB,IAAI,0BAA0B;AAAA,MAC9B,IAAI,oBAAoB;AAAA,MACxB,KAAK;AAAA;AAAA,MACL;AAAA;AAAA,IACD;AAEA,WAAO,KAAK;AAAA,EACb;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,QAAQ,OAAuD;AA/H7E;AAgIE,UAAM,aAAa,MAAM,QAAQ,SAAS;AAE1C,QAAI;AAEH,YAAM,kBAAkB,MAAM,KAAK,gBAAgB,aAAa;AAGhE,UAAI,CAAC,eAAe,QAAQ,IAAI,2BAA2B,UAAU,MAAM,mBAAmB,IAAI;AACjG,cAAM,oBAAoB;AAE1B,cAAM,cAAc,MAAM,KAAK,gBAAgB,aAAa;AAC5D,cAAM,gBAAc,iBAAY,oBAAZ,mBAA6B,aAAY;AAC7D,YAAI,gBAAgB,KAAK,aAAa,cAAc;AACnD,oBAAU,EAAE,MAAM,sDAAsD,WAAW,GAAG;AACtF,eAAK,eAAe,oBAAoB,OAAO,WAAW;AAAA,QAC3D;AAAA,MACD;AAEA,UAAI;AAGJ,UAAI,KAAK,aAAa,iBAAiB,YAAa,MAAM,mBAAmB,GAAI;AAEhF,eAAO,MAAM,8BAA8B,eAAe;AAC1D,kBAAU,EAAE,KAAK,4BAA4B,IAAI,EAAE;AAAA,MACpD;AAGA,YAAM,cAAc,MAAM,KAAK,sBAAsB;AAGrD,UAAI,aAAa,MAAM,KAAK,iBAAiB,WAAW;AAGxD,YAAM,SAAS,MAAM,KAAK,WAAW,MAAM,YAAY,IAAI;AAG3D,YAAM,KAAK,cAAc,QAAQ,IAAI;AAGrC,UAAI,YAAY;AAGf,cAAM,gBAAgB,WAAW,SAAS,UACvC,UAAU,WAAW,UAAU,KAC/B,WAAW,SAAS,OACpB,OAAO,WAAW,UAAU,KAC5B,UAAU,WAAW,UAAU;AAGlC,YAAI,MAAM,QAAQ,cAAc,MAAM;AAErC,oBAAU,EAAE,KAAK,6BAA6B,aAAa,sBAAsB;AAAA,QAClF,WAAW,MAAM,QAAQ,cAAc,OAAO;AAE7C,uBAAa;AACb,oBAAU,EAAE,KAAK,qDAAqD;AAAA,QACvE,OAAO;AAGN,cAAI,YAAY;AACf,kBAAM,IAAI,MAAM,kGAAkG;AAAA,UACnH;AACA,cAAI,gBAAgB;AACpB,cAAI,yBAAyB,GAAG;AAC/B,4BAAgB,MAAM;AAAA,cACrB,gEAAgE,aAAa;AAAA,cAC7E;AAAA;AAAA,YACD;AAAA,UACD,OAAO;AACN,kBAAM,IAAI,MAAM,sGAAsG;AAAA,UACvH;AAEA,cAAI,CAAC,eAAe;AACnB,yBAAa;AACb,sBAAU,EAAE,KAAK,8BAA8B;AAAA,UAChD;AAAA,QACD;AAAA,MACD,WAAW,MAAM,QAAQ,cAAc,MAAM;AAE5C,kBAAU,EAAE,MAAM,mFAAmF;AAAA,MACtG;AAIA,UAAI,OAAO,SAAS,eAAe;AAClC,kBAAU,EAAE,KAAK,oCAAoC;AAErD,cAAM,QAAQ,sBAAsB,OAAO,aAAa;AACxD,cAAM,OAAO,MAAM,QAAQ,OAAO,sBAAsB,MAAM,QAAQ,IAAI,IAAI;AAC9E,cAAM,SAAS,MAAM,KAAK,aAAa;AAAA,UACtC;AAAA;AAAA,UACA;AAAA;AAAA,QACD;AACA,kBAAU,EAAE,QAAQ,kBAAkB,OAAO,MAAM,KAAK,OAAO,GAAG,EAAE;AAEpE,eAAO,OAAO;AACd,eAAO,SAAS,OAAO;AAAA,MACxB;AAGA,UAAI,oBAA8B,CAAC;AACnC,UAAI,cAAmF,CAAC;AACxF,UAAI,gBAA0C,CAAC;AAE/C,UAAI,OAAO,SAAS,WAAW,OAAO,QAAQ;AAC7C,cAAMC,YAAW,MAAM,KAAK,gBAAgB,aAAa;AACzD,cAAM,mBAAmB,oBAAoB,OAAOA,SAAQ;AAC5D,YAAI,WAAyD,CAAC;AAC9D,YAAI;AACH,qBAAW,MAAM,iBAAiB,OAAO,OAAO,MAAM,GAAG,kBAAkB,IAAI;AAAA,QAChF,SAAS,OAAO;AACf,oBAAU,EAAE,KAAK,qCAAqC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,8BAA8B;AAAA,QAC7I;AAEA,YAAI,SAAS,SAAS,GAAG;AACxB,8BAAoB,SAAS,IAAI,OAAK,EAAE,EAAE;AAC1C,cAAI,eAAe;AAEnB,cAAI,MAAM,QAAQ,SAAS,MAAM;AAEhC,2BAAe;AACf,sBAAU,EAAE,KAAK,8BAA8B,SAAS,MAAM,+BAA+B;AAAA,UAC9F,WAAW,MAAM,QAAQ,SAAS,OAAO;AAExC,2BAAe;AACf,sBAAU,EAAE,KAAK,0CAA0C;AAAA,UAC5D,OAAO;AAEN,gBAAI,YAAY;AACf,oBAAM,IAAI,MAAM,kFAAkF;AAAA,YACnG;AAEA,gBAAI,yBAAyB,GAAG;AAC/B,6BAAe,MAAM;AAAA,gBACpB,kBAAkB,SAAS,MAAM;AAAA,gBACjC;AAAA;AAAA,cACD;AAAA,YACD,OAAO;AACN,oBAAM,IAAI,MAAM,0FAA0F;AAAA,YAC3G;AAAA,UACD;AAEA,cAAI,cAAc;AACjB,mBAAO,OAAO;AAGd,gBAAI;AACH,oBAAM,CAAC,SAAS,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,gBAC3C,uBAAuB,OAAO,OAAO,MAAM,GAAG,kBAAkB,IAAI;AAAA,gBACpE,mBAAmB,mBAAmBA,WAAU,IAAI;AAAA,cACrD,CAAC;AACD,4BAAc,WAAW,CAAC;AAC1B,8BAAgB,UAAU,CAAC;AAC3B,wBAAU,EAAE,KAAK,WAAW,YAAY,MAAM,yCAAyC;AAAA,YACxF,SAAS,OAAO;AAGf,qBAAO,OAAO;AACd,kCAAoB,CAAC;AACrB,wBAAU,EAAE,KAAK,8DAA8D,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,YACxI;AAAA,UACD,OAAO;AAEN,gCAAoB,CAAC;AAAA,UACtB;AAAA,QACD;AAAA,MAED;AAOA,UAAI,MAAM,QAAQ,YAAY,uBAAuB,MAAM,QAAQ,WAAW,SAAS,CAAC,YAAY;AACnG,cAAM,YAAY,MAAM;AAAA,UACvB;AAAA,QAED;AACA,YAAI,CAAC,WAAW;AACf,oBAAU,EAAE,KAAK,6BAA6B;AAC9C,kBAAQ,KAAK,CAAC;AAAA,QACf;AAAA,MACD;AAGA,YAAM,eAAe,yBAAyB;AAC9C,YAAM,WAAW,MAAM,KAAK,gBAAgB,aAAa,QAAW,YAAY;AAChF,YAAM,eAAe,OAAO,SAAS,WAAW,YAAY,OAAO,SAAS,SAAS,UAAU,OAAO;AACtG,YAAM,kBAAiB,cAAS,cAAT,mBAAqB;AAG5C,YAAM,EAAE,wBAAwB,kBAAkB,IAAI,MAAM,OAAO,6BAA2B;AAC9F,YAAM,eAAe,uBAAuB;AAC5C,YAAM,iBAAiB,kBAAkB;AAGzC,gBAAU,EAAE,KAAK,oBAAoB,KAAK,kBAAkB,MAAM,CAAC,EAAE;AAGrE,YAAM,aACL,OAAO,SAAS,WACb,OAAO,cAAc,KACrB,OAAO,UAAU;AAGrB,YAAM,eAAe,MAAM,QAAQ,WAAU,iDAAgB,iBAAgB;AAC7E,YAAM,aAAa,MAAM,QAAQ,SAAQ,iDAAgB,aAAY;AACrE,YAAM,kBAAkB,MAAM,QAAQ,cAAa,iDAAgB,mBAAkB;AACrF,YAAM,iBAAiB,MAAM,QAAQ,aAAY,iDAAgB,kBAAiB;AAElF,gBAAU,EAAE,MAAM,iCAAiC;AAAA,QAClD;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD,CAAC;AAED,YAAM,OAAO,MAAM,YAAY,YAAY;AAAA,QAC1C,MAAM,OAAO;AAAA,QACb;AAAA,QACA,eAAe,OAAO;AAAA,QACtB,GAAI,cAAc,EAAE,WAAW;AAAA,QAC/B,SAAS;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,MAAM,QAAQ,WAAW,EAAE,SAAS,MAAM,QAAQ,QAAQ;AAAA,UAC9D,GAAI,aAAa,SAAS,KAAK,EAAE,aAAa;AAAA,UAC9C,GAAI,kBAAkB,EAAE,eAAe;AAAA,UACvC,GAAI,kBAAkB,SAAS,KAAK,EAAE,kBAAkB;AAAA,UACxD,GAAI,YAAY,SAAS,KAAK,EAAE,YAAY;AAAA,UAC5C,GAAI,OAAO,KAAK,aAAa,EAAE,SAAS,KAAK,EAAE,cAAc;AAAA,QAC9D;AAAA,MACD,CAAC;AAED,gBAAU,EAAE,QAAQ,iBAAiB,KAAK,EAAE,OAAO,KAAK,IAAI,EAAE;AAG9D,UAAI;AACH,cAAM,aAAqE;AAAA,UAC1E,UAAU;AAAA,UACV,mBAAmB;AAAA,QACpB;AACA,yBAAiB,YAAY,EAAE,MAAM,gBAAgB;AAAA,UACpD,aAAa,OAAO,SAAS,SAAS,UAAU,OAAO;AAAA,UACvD,SAAS,KAAK,aAAa;AAAA,UAC3B,eAAe,CAAC,CAAC;AAAA,UACjB,eAAe,WAAW,MAAM,QAAQ,WAAW,EAAE,KAAK;AAAA,QAC3D,CAAC;AAAA,MACF,SAAS,OAAgB;AACxB,kBAAU,EAAE,MAAM,2CAA2C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,MACtH;AAEA,gBAAU,EAAE,KAAK,cAAc,KAAK,MAAM,EAAE;AAE5C,WAAI,UAAK,iBAAL,mBAAmB,SAAS,QAAQ;AACvC,kBAAU,EAAE,KAAK,YAAY,KAAK,IAAI,EAAE;AAAA,MACzC;AACA,WAAI,UAAK,cAAL,mBAAgB,OAAO;AAC1B,kBAAU,EAAE,KAAK,aAAa,KAAK,UAAU,KAAK,EAAE;AAAA,MACrD;AACA,UAAI,OAAO,SAAS,QAAQ;AAC3B,kBAAU,EAAE,KAAK,iBAAiB,kBAAkB,MAAM,kBAAkB;AAAA,MAC7E;AAGA,UAAI,YAAY;AACf,eAAO;AAAA,UACN,IAAI,KAAK;AAAA,UACT,MAAM,KAAK;AAAA,UACX,QAAQ,KAAK;AAAA,UACb,MAAM,OAAO;AAAA,UACb,YAAY,KAAK;AAAA,UACjB,GAAI,KAAK,SAAS,UAAa,EAAE,MAAM,KAAK,KAAK;AAAA,UACjD,KAAI,UAAK,cAAL,mBAAgB,UAAS,EAAE,OAAO,KAAK,UAAU,MAAM;AAAA,UAC3D,GAAI,KAAK,gBAAgB,EAAE,cAAc,KAAK,aAAa;AAAA,UAC3D,GAAI,kBAAkB,SAAS,KAAK,EAAE,kBAAkB;AAAA,QACzD;AAAA,MACD;AAAA,IACD,SAAS,OAAO;AACf,UAAI,iBAAiB,OAAO;AAC3B,kBAAU,EAAE,MAAM,GAAG,MAAM,OAAO,EAAE;AAAA,MACrC,OAAO;AACN,kBAAU,EAAE,MAAM,2BAA2B;AAAA,MAC9C;AACA,YAAM;AAAA,IACP;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAW,YAAoB,MAAqC;AAGjF,UAAM,kBAAkB,WAAW,WAAW,GAAG;AAGjD,UAAM,oBAAoB,WAAW,KAAK;AAC1C,QAAI,CAAC,mBAAmB;AACvB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACxD;AAIA,UAAM,cAAc,kBAAkB,MAAM,IAAI,KAAK,CAAC,GAAG;AACzD,QAAI,kBAAkB,SAAS,MAAM,cAAc,GAAG;AAErD,aAAO;AAAA,QACN,MAAM;AAAA,QACN,eAAe,kBAAkB,MAAM,oBAAoB;AAAA,MAC5D;AAAA,IACD;AAGA,UAAM,YAAY;AAClB,UAAM,UAAU,kBAAkB,MAAM,SAAS;AACjD,QAAI,mCAAU,IAAI;AACjB,aAAO;AAAA,QACN,MAAM;AAAA,QACN,QAAQ,SAAS,QAAQ,CAAC,GAAG,EAAE;AAAA,QAC/B,eAAe;AAAA,MAChB;AAAA,IACD;AAKA,UAAM,kBAAkB,qBAAqB,iBAAiB;AAE9D,QAAI,gBAAgB,SAAS,iBAAiB,gBAAgB,YAAY;AAEzE,YAAM,YAAY,MAAM,KAAK,aAAa;AAAA,QACzC;AAAA,QACA;AAAA,MACD;AAEA,UAAI,UAAU,SAAS,WAAW,UAAU,YAAY;AACvD,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ,UAAU;AAAA;AAAA,UAClB,eAAe;AAAA,QAChB;AAAA,MACD;AAGA,YAAM,IAAI;AAAA,QACT,4CAA4C,gBAAgB,UAAU;AAAA,MACvE;AAAA,IACD;AAGA,QAAI,gBAAgB,SAAS,aAAa,gBAAgB,YAAY;AACrE,YAAM,SAAS,SAAS,gBAAgB,YAAY,EAAE;AAGtD,UAAI,KAAK,aAAa,sBAAsB;AAC3C,cAAM,YAAY,MAAM,KAAK,aAAa;AAAA,UACzC;AAAA,UACA;AAAA,QACD;AAEA,YAAI,UAAU,SAAS,MAAM;AAC5B,iBAAO;AAAA,YACN,MAAM;AAAA,YACN,QAAQ,UAAU,aAAa,SAAS,UAAU,YAAY,EAAE,IAAI;AAAA,YACpE,eAAe;AAAA,UAChB;AAAA,QACD,WAAW,UAAU,SAAS,SAAS;AACtC,iBAAO;AAAA,YACN,MAAM;AAAA,YACN,QAAQ,UAAU,aAAa,SAAS,UAAU,YAAY,EAAE,IAAI;AAAA,YACpE,eAAe;AAAA,UAChB;AAAA,QACD,OAAO;AACN,gBAAM,IAAI,MAAM,+BAA+B,MAAM,EAAE;AAAA,QACxD;AAAA,MACD,OAAO;AAGN,cAAM,gBAAgB,KAAK,iBAAiB;AAC5C,cAAM,YAAY,MAAM,cAAc,gBAAgB,mBAAmB,IAAI;AAE7E,YAAI,UAAU,SAAS,MAAM;AAC5B,iBAAO;AAAA,YACN,MAAM;AAAA,YACN,QAAQ,UAAU,aAAa,SAAS,UAAU,YAAY,EAAE,IAAI;AAAA,YACpE,eAAe;AAAA,UAChB;AAAA,QACD,OAAO;AAGN,iBAAO;AAAA,YACN,MAAM;AAAA,YACN;AAAA,YACA,eAAe;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAGA,WAAO;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,eAAe;AAAA,IAChB;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAc,QAAqB,MAA8B;AAC9E,YAAQ,OAAO,MAAM;AAAA,MACpB,KAAK,MAAM;AACV,YAAI,CAAC,OAAO,QAAQ;AACnB,gBAAM,IAAI,MAAM,mBAAmB;AAAA,QACpC;AAGA,YAAI,KAAK,aAAa,wBAAwB,KAAK,aAAa,WAAW,KAAK,aAAa,iBAAiB;AAE7G,gBAAM,KAAK,MAAM,KAAK,aAAa,QAAQ,OAAO,QAAQ,IAAI;AAC9D,gBAAM,KAAK,aAAa,gBAAgB,EAAE;AAAA,QAC3C,OAAO;AAEN,gBAAM,gBAAgB,KAAK,iBAAiB;AAC5C,gBAAM,KAAK,MAAM,cAAc,QAAQ,OAAO,QAAkB,IAAI;AACpE,gBAAM,cAAc,gBAAgB,EAAE;AAAA,QACvC;AACA,kBAAU,EAAE,MAAM,iBAAiB,OAAO,MAAM,EAAE;AAClD;AAAA,MACD;AAAA,MAEA,KAAK,SAAS;AACb,YAAI,CAAC,OAAO,QAAQ;AACnB,gBAAM,IAAI,MAAM,sBAAsB;AAAA,QACvC;AAEA,cAAM,QAAQ,MAAM,KAAK,aAAa,WAAW,OAAO,QAAQ,IAAI;AACpE,cAAM,KAAK,aAAa,mBAAmB,KAAK;AAChD,kBAAU,EAAE,MAAM,oBAAoB,OAAO,MAAM,EAAE;AACrD;AAAA,MACD;AAAA,MAEA,KAAK,UAAU;AACd,YAAI,CAAC,OAAO,YAAY;AACvB,gBAAM,IAAI,MAAM,qBAAqB;AAAA,QACtC;AAEA,YAAI,CAAC,KAAK,kBAAkB,OAAO,UAAU,GAAG;AAC/C,gBAAM,IAAI;AAAA,YACT;AAAA,UACD;AAAA,QACD;AACA,kBAAU,EAAE,MAAM,0BAA0B,OAAO,UAAU,EAAE;AAC/D;AAAA,MACD;AAAA,MAEA,KAAK,eAAe;AAEnB,kBAAU,EAAE,MAAM,8BAA8B;AAAA,UAC/C,QAAQ,OAAO,cAAc;AAAA,QAC9B,CAAC;AACD;AAAA,MACD;AAAA,MAEA,SAAS;AACR,cAAM,cAAc;AACpB,cAAM,IAAI,MAAM,uBAAuB,YAAY,IAAI,EAAE;AAAA,MAC1D;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAAyB;AAElD,WAAO,oBAAoB,KAAK,MAAM;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,QAA6B;AACtD,YAAQ,OAAO,MAAM;AAAA,MACpB,KAAK;AACJ,eAAO,OAAO,OAAO,MAAM;AAAA,MAC5B,KAAK;AACJ,eAAO,UAAU,OAAO,MAAM;AAAA,MAC/B,KAAK;AACJ,eAAO,SAAS,OAAO,MAAM;AAAA,MAC9B,KAAK;AACJ,eAAO,WAAW,OAAO,UAAU;AAAA,MACpC,KAAK;AACJ,eAAO,gBAAgB,OAAO,cAAc,MAAM,GAAG,EAAE,CAAC;AAAA,MACzD;AACC,eAAO;AAAA,IACT;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,iBAAiB,aAMrB;AACT,QAAI;AACH,YAAM,MAAM,QAAQ,IAAI;AACxB,YAAM,QAAQ,MAAM,YAAY,UAAU;AAE1C,UAAI,CAAC,OAAO;AACX,eAAO;AAAA,MACR;AAGA,YAAM,mBAAmB,MAAM,iCAAiC;AAKhE,YAAM,aAAa,MAAM,KAAK,UAAQ;AAErC,YAAI,KAAK,SAAS,kBAAkB;AACnC,iBAAO;AAAA,QACR;AAEA,eAAO,QAAQ,KAAK,QAAQ,IAAI,WAAW,KAAK,OAAOC,MAAK,GAAG;AAAA,MAChE,CAAC;AACD,UAAI,CAAC,YAAY;AAChB,eAAO;AAAA,MACR;AAEA,gBAAU,EAAE,MAAM,yBAAyB,WAAW,IAAI,IAAI,WAAW,UAAU,OAAO,WAAW,IAAI,EAAE;AAE3G,YAAM,SAMF;AAAA,QACH,MAAM,WAAW;AAAA,QACjB,YAAY,WAAW;AAAA,QACvB,YAAY,WAAW;AAAA,QACvB,cAAc,WAAW;AAAA,MAC1B;AAGA,UAAI,WAAW,gBAAgB;AAC9B,eAAO,iBAAiB,WAAW;AAAA,MACpC;AAGA,UAAI,CAAC,OAAO,gBAAgB;AAC3B,cAAM,iBAAiB,MAAM,YAAY,yBAAyB,WAAW,IAAI;AACjF,YAAI,gBAAgB;AACnB,iBAAO,iBAAiB;AACxB,oBAAU,EAAE,MAAM,oCAAoC,cAAc,EAAE;AAAA,QACvE;AAAA,MACD;AAEA,aAAO;AAAA,IACR,SAAS,OAAO;AAEf,gBAAU,EAAE,MAAM,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,EAAE;AAC7G,aAAO;AAAA,IACR;AAAA,EACD;AAED;","names":["path","settings","path"]}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isPRBranch,
|
|
12
12
|
isValidGitRepo,
|
|
13
13
|
parseWorktreeList
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-4FGEGQW4.js";
|
|
15
15
|
import {
|
|
16
16
|
getLogger
|
|
17
17
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -388,4 +388,4 @@ var GitWorktreeManager = class {
|
|
|
388
388
|
export {
|
|
389
389
|
GitWorktreeManager
|
|
390
390
|
};
|
|
391
|
-
//# sourceMappingURL=chunk-
|
|
391
|
+
//# sourceMappingURL=chunk-I5T677EA.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
GitWorktreeManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-I5T677EA.js";
|
|
5
5
|
import {
|
|
6
6
|
logger
|
|
7
7
|
} from "./chunk-VT4PDUYT.js";
|
|
@@ -299,4 +299,4 @@ Please consult your shell's documentation for setting up custom completions.
|
|
|
299
299
|
export {
|
|
300
300
|
ShellCompletion
|
|
301
301
|
};
|
|
302
|
-
//# sourceMappingURL=chunk-
|
|
302
|
+
//# sourceMappingURL=chunk-KIK2ZFAL.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
generateIssueManagementMcpConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SKSYYBCU.js";
|
|
5
5
|
import {
|
|
6
6
|
openBrowser
|
|
7
7
|
} from "./chunk-YETJNRQM.js";
|
|
8
8
|
import {
|
|
9
9
|
launchClaude
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UR5DGNUO.js";
|
|
11
11
|
import {
|
|
12
12
|
getLogger
|
|
13
13
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -240,26 +240,7 @@ IMPORTANT: When you create your analysis comment, tag @${author} in the "Questio
|
|
|
240
240
|
}
|
|
241
241
|
};
|
|
242
242
|
|
|
243
|
-
// src/utils/text.ts
|
|
244
|
-
function capitalizeFirstLetter(str) {
|
|
245
|
-
if (!str || str.length === 0) {
|
|
246
|
-
return str;
|
|
247
|
-
}
|
|
248
|
-
if (str.startsWith(" ")) {
|
|
249
|
-
return str.slice(1);
|
|
250
|
-
}
|
|
251
|
-
const firstChar = str.charAt(0);
|
|
252
|
-
const upperChar = firstChar.toUpperCase();
|
|
253
|
-
if (upperChar !== firstChar.toLowerCase() || new RegExp("\\p{L}", "u").test(firstChar)) {
|
|
254
|
-
if (upperChar !== firstChar) {
|
|
255
|
-
return upperChar + str.slice(1);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return str;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
243
|
export {
|
|
262
|
-
IssueEnhancementService
|
|
263
|
-
capitalizeFirstLetter
|
|
244
|
+
IssueEnhancementService
|
|
264
245
|
};
|
|
265
|
-
//# sourceMappingURL=chunk-
|
|
246
|
+
//# sourceMappingURL=chunk-KKV5WH5M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/IssueEnhancementService.ts"],"sourcesContent":["import type { IssueTracker } from './IssueTracker.js'\nimport type { AgentManager } from './AgentManager.js'\nimport type { SettingsManager } from './SettingsManager.js'\nimport type { TemplateVariables } from './PromptTemplateManager.js'\nimport { launchClaude } from '../utils/claude.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { waitForKeypress } from '../utils/prompt.js'\nimport { getLogger } from '../utils/logger-context.js'\nimport { generateIssueManagementMcpConfig } from '../utils/mcp.js'\n\n/**\n * Options for enhancing an existing issue\n */\nexport interface EnhanceExistingIssueOptions {\n\t/** GitHub username of issue author for tagging in questions */\n\tauthor?: string\n\t/** Repository in \"owner/repo\" format */\n\trepo?: string\n}\n\n/**\n * Result of enhancing an existing issue\n */\nexport interface EnhanceExistingIssueResult {\n\t/** Whether the issue was enhanced */\n\tenhanced: boolean\n\t/** URL of the comment if enhancement occurred */\n\turl?: string\n}\n\n/**\n * Service for enhancing and creating issues with AI assistance.\n * Extracts reusable issue enhancement logic from StartCommand.\n */\nexport class IssueEnhancementService {\n\tconstructor(\n\t\tprivate issueTrackerService: IssueTracker,\n\t\tprivate agentManager: AgentManager,\n\t\tprivate settingsManager: SettingsManager\n\t) {\n\t\t// No-op - logger now uses AsyncLocalStorage context\n\t}\n\n\t/**\n\t * Expose issue tracker for provider checks\n\t */\n\tpublic get issueTracker(): IssueTracker {\n\t\treturn this.issueTrackerService\n\t}\n\n\t/**\n\t * Validates that a description meets minimum requirements.\n\t *\n\t * When hasBody is false (default): Strict validation - >30 characters AND >2 spaces\n\t * When hasBody is true: Relaxed validation - only requires non-empty description\n\t *\n\t * @param description - The description text to validate\n\t * @param hasBody - If true, skip strict validation (only require non-empty)\n\t */\n\tpublic validateDescription(description: string, hasBody = false): boolean {\n\t\tconst trimmedDescription = description.trim()\n\n\t\t// When --body is provided, only require non-empty description\n\t\tif (hasBody) {\n\t\t\treturn trimmedDescription.length > 0\n\t\t}\n\n\t\t// Standard validation: >30 chars AND >2 spaces\n\t\tconst spaceCount = (trimmedDescription.match(/ /g) ?? []).length\n\t\treturn trimmedDescription.length > 30 && spaceCount > 2\n\t}\n\n\t/**\n\t * Enhances a description using Claude Code in headless mode.\n\t * Falls back to original description if enhancement fails.\n\t */\n\tpublic async enhanceDescription(description: string): Promise<string> {\n\t\ttry {\n\t\t\tgetLogger().info('Enhancing description with Claude Code. This may take a moment...')\n\n\t\t\t// Load only the enhancer agent with template variables so Handlebars expressions resolve\n\t\t\tconst settings = await this.settingsManager.loadSettings()\n\t\t\tconst templateVariables: TemplateVariables = {\n\t\t\t\tSTANDARD_ISSUE_MODE: true,\n\t\t\t\tDIRECT_PROMPT_MODE: true,\n\t\t\t}\n\t\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\t\tsettings,\n\t\t\t\ttemplateVariables,\n\t\t\t\t['iloom-issue-enhancer.md']\n\t\t\t)\n\t\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t\t// Call Claude in headless mode with issue enhancer agent\n\t\t\tconst prompt = `@agent-iloom-issue-enhancer\n\nTASK: Enhance the following issue description for the issue tracker.\n\nINPUT:\n${description}\n\nOUTPUT REQUIREMENTS:\n- Return ONLY the enhanced description markdown text\n- Use GitHub-Flavored Markdown syntax ONLY\n- NEVER use Jira Wiki format (e.g., {code}, h1., *bold*, {quote}, [link|url])\n- NO meta-commentary (no \"Here is...\", \"The enhanced...\", \"I have...\", etc)\n- NO code block markers (\\`\\`\\`)\n- NO conversational framing or acknowledgments\n- NO explanations of your work\n- Start your response immediately with the enhanced content\n\nYour response should be the raw markdown that will become the issue body.`\n\n\t\t\tconst enhanced = await launchClaude(prompt, {\n\t\t\t\theadless: true,\n\t\t\t\tmodel: 'sonnet',\n\t\t\t\tagents,\n\t\t\t\tnoSessionPersistence: true, // Utility operation - don't persist session\n\t\t\t})\n\n\t\t\tif (enhanced && typeof enhanced === 'string') {\n\t\t\t\tgetLogger().success('Description enhanced successfully')\n\t\t\t\treturn enhanced\n\t\t\t}\n\n\t\t\t// Fallback to original description\n\t\t\tgetLogger().warn('Claude enhancement returned empty result, using original description')\n\t\t\treturn description\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to enhance description: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t\treturn description\n\t\t}\n\t}\n\n\t/**\n\t * Creates a GitHub issue with title and enhanced body.\n\t * @param originalDescription - Used as the issue title\n\t * @param enhancedDescription - Used as the issue body\n\t * @param repository - Optional repository override (format: \"owner/repo\")\n\t * @param labels - Optional array of label names to add to the issue\n\t * @returns Issue number and URL\n\t */\n\tpublic async createEnhancedIssue(\n\t\toriginalDescription: string,\n\t\tenhancedDescription: string,\n\t\trepository?: string,\n\t\tlabels?: string[]\n\t): Promise<{ number: string | number; url: string }> {\n\t\tgetLogger().info('Creating issue from description...')\n\n\t\tconst result = await this.issueTrackerService.createIssue(\n\t\t\toriginalDescription, // Use original description as title\n\t\t\tenhancedDescription, // Use enhanced description as body\n\t\t\trepository,\n\t\t\tlabels\n\t\t)\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Waits for user keypress and opens issue in browser for review.\n\t * @param issueNumber - Issue number to open for review\n\t * @param confirm - If true, wait for additional keypress after opening browser before returning\n\t * @param repository - Optional repository to fetch issue from (format: \"owner/repo\")\n\t */\n\tpublic async waitForReviewAndOpen(issueNumber: string | number, confirm = false, repository?: string): Promise<void> {\n\t\t// Check if running in non-interactive environment (CI or no TTY)\n\t\tconst isCI = process.env.CI === 'true'\n\t\tconst isNonInteractive = isCI || !process.stdin.isTTY\n\n\t\tif (isNonInteractive) {\n\t\t\t// In non-interactive environment: Skip all interactive operations\n\t\t\tgetLogger().info(`Running in non-interactive environment - skipping interactive prompts for issue #${issueNumber}`)\n\t\t\treturn\n\t\t}\n\n\t\t// Get issue URL\n\t\tconst issueUrl = await this.issueTrackerService.getIssueUrl(issueNumber, repository)\n\n\t\t// Display message and wait for first keypress\n\t\tconst message = `Created issue #${issueNumber}.\nReview and edit the issue in your browser if needed.\nPress any key to open issue for editing...`\n\t\tawait waitForKeypress(message)\n\n\t\t// Open issue in browser\n\t\tawait openBrowser(issueUrl)\n\n\t\t// If confirmation required, wait for second keypress\n\t\tif (confirm) {\n\t\t\tawait waitForKeypress('Press any key to continue with loom creation...')\n\t\t}\n\t}\n\n\t/**\n\t * Enhances an existing issue using the issue enhancer agent.\n\t * This method encapsulates the Claude invocation, MCP config generation,\n\t * and response parsing for enhancing existing issues.\n\t *\n\t * @param issueNumber - The issue number to enhance\n\t * @param options - Optional enhancement options (author, repo)\n\t * @returns Result indicating whether enhancement occurred and the comment URL if so\n\t */\n\tpublic async enhanceExistingIssue(\n\t\tissueNumber: string | number,\n\t\toptions?: EnhanceExistingIssueOptions\n\t): Promise<EnhanceExistingIssueResult> {\n\t\tconst { author, repo } = options ?? {}\n\n\t\t// Load only the enhancer agent with template variables so Handlebars expressions resolve\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst templateVariables: TemplateVariables = {\n\t\t\tISSUE_NUMBER: issueNumber,\n\t\t\tSTANDARD_ISSUE_MODE: true,\n\t\t}\n\t\tconst loadedAgents = await this.agentManager.loadAgents(\n\t\t\tsettings,\n\t\t\ttemplateVariables,\n\t\t\t['iloom-issue-enhancer.md']\n\t\t)\n\t\tconst agents = this.agentManager.formatForCli(loadedAgents)\n\n\t\t// Generate MCP config and tool filtering for issue management\n\t\tlet mcpConfig: Record<string, unknown>[] | undefined\n\t\tlet allowedTools: string[] | undefined\n\t\tlet disallowedTools: string[] | undefined\n\n\t\ttry {\n\t\t\tconst provider = this.issueTrackerService.providerName as 'github' | 'linear'\n\t\t\tmcpConfig = await generateIssueManagementMcpConfig('issue', repo, provider, settings)\n\t\t\tgetLogger().debug('Generated MCP configuration for issue management:', { mcpConfig })\n\n\t\t\t// Configure tool filtering for issue workflows\n\t\t\tallowedTools = [\n\t\t\t\t'mcp__issue_management__get_issue',\n\t\t\t\t'mcp__issue_management__get_comment',\n\t\t\t\t'mcp__issue_management__create_comment',\n\t\t\t\t'mcp__issue_management__update_comment',\n\t\t\t\t'mcp__issue_management__create_issue',\n\t\t\t]\n\t\t\tdisallowedTools = ['Bash(gh api:*)']\n\n\t\t\tgetLogger().debug('Configured tool filtering for issue workflow', { allowedTools, disallowedTools })\n\t\t} catch (error) {\n\t\t\t// Log warning but continue without MCP\n\t\t\tgetLogger().warn(`Failed to generate MCP config: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t\t}\n\n\t\t// Construct prompt for the orchestrating Claude instance\n\t\tconst prompt = this.constructEnhancerPrompt(issueNumber, author)\n\n\t\t// Invoke Claude CLI with enhancer agent\n\t\tconst response = await launchClaude(prompt, {\n\t\t\theadless: true,\n\t\t\tmodel: 'sonnet',\n\t\t\tagents,\n\t\t\tnoSessionPersistence: true, // Headless operation - no session persistence needed\n\t\t\t...(mcpConfig && { mcpConfig }),\n\t\t\t...(allowedTools && { allowedTools }),\n\t\t\t...(disallowedTools && { disallowedTools }),\n\t\t})\n\n\t\t// Parse response to determine outcome\n\t\treturn this.parseEnhancerResponse(response)\n\t}\n\n\t/**\n\t * Construct the prompt for the orchestrating Claude instance.\n\t * This prompt is very clear about expected output format to ensure reliable parsing.\n\t */\n\tprivate constructEnhancerPrompt(issueNumber: string | number, author?: string): string {\n\t\tconst authorInstruction = author\n\t\t\t? `\\nIMPORTANT: When you create your analysis comment, tag @${author} in the \"Questions for Reporter\" section if you have questions.\\n`\n\t\t\t: ''\n\n\t\treturn `Execute @agent-iloom-issue-enhancer ${issueNumber}${authorInstruction}\n\n## OUTPUT REQUIREMENTS\n* If the issue was not enhanced, return ONLY: \"No enhancement needed\"\n* If the issue WAS enhanced, return ONLY: <FULL URL OF THE COMMENT INCLUDING COMMENT ID>\n* If you encounter permission/authentication/access errors, return ONLY: \"Permission denied: <specific error description>\"\n* IMPORTANT: Return ONLY one of the above - DO NOT include commentary such as \"I created a comment at <URL>\" or \"I examined the issue and found no enhancement was necessary\"\n* CONTEXT: Your output is going to be parsed programmatically, so adherence to the output requirements is CRITICAL.`\n\t}\n\n\t/**\n\t * Parse the response from the enhancer agent.\n\t * Returns either { enhanced: false } or { enhanced: true, url: \"...\" }\n\t * Throws specific errors for permission issues.\n\t */\n\tprivate parseEnhancerResponse(response: string | void): EnhanceExistingIssueResult {\n\t\t// Handle empty or void response\n\t\tif (!response || typeof response !== 'string') {\n\t\t\tthrow new Error('No response from enhancer agent')\n\t\t}\n\n\t\tconst trimmed = response.trim()\n\n\t\tgetLogger().debug(`RESPONSE FROM ENHANCER AGENT: '${trimmed}'`)\n\n\t\t// Check for permission denied errors (case-insensitive)\n\t\tif (trimmed.toLowerCase().startsWith('permission denied:')) {\n\t\t\tconst errorMessage = trimmed.substring('permission denied:'.length).trim()\n\t\t\tthrow new Error(`Permission denied: ${errorMessage}`)\n\t\t}\n\n\t\t// Check for \"No enhancement needed\" (case-insensitive)\n\t\tif (trimmed.toLowerCase().includes('no enhancement needed')) {\n\t\t\treturn { enhanced: false }\n\t\t}\n\n\t\t// Check if response looks like a GitHub comment URL\n\t\tconst urlPattern = /https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/\\d+#issuecomment-\\d+/\n\t\tconst match = trimmed.match(urlPattern)\n\n\t\tif (match) {\n\t\t\treturn { enhanced: true, url: match[0] }\n\t\t}\n\n\t\t// Unexpected response format\n\t\tthrow new Error(`Unexpected response from enhancer agent: ${trimmed}`)\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
IssueManagementProviderFactory
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-SWSJWA2S.js";
|
|
5
5
|
import {
|
|
6
6
|
openBrowser
|
|
7
7
|
} from "./chunk-YETJNRQM.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
detectClaudeCli,
|
|
15
15
|
launchClaude
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-UR5DGNUO.js";
|
|
17
17
|
import {
|
|
18
18
|
executeGhCommand
|
|
19
19
|
} from "./chunk-VG45TUYK.js";
|
|
@@ -359,4 +359,4 @@ Then retry: il start`
|
|
|
359
359
|
export {
|
|
360
360
|
PRManager
|
|
361
361
|
};
|
|
362
|
-
//# sourceMappingURL=chunk-
|
|
362
|
+
//# sourceMappingURL=chunk-KVHIAWVT.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
extractIssueNumber
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4FGEGQW4.js";
|
|
5
5
|
import {
|
|
6
6
|
extractPort,
|
|
7
7
|
findEnvFileContainingVariable,
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
calculatePortFromIdentifier,
|
|
109
109
|
getWorkspacePort
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-LLHXQS3C.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
findMainWorktreePathWithSettings
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-4FGEGQW4.js";
|
|
5
5
|
import {
|
|
6
6
|
MetadataManager
|
|
7
7
|
} from "./chunk-KB64WNBZ.js";
|
|
@@ -70,4 +70,4 @@ export {
|
|
|
70
70
|
archiveRecap,
|
|
71
71
|
findArchivedRecap
|
|
72
72
|
};
|
|
73
|
-
//# sourceMappingURL=chunk-
|
|
73
|
+
//# sourceMappingURL=chunk-LUKXJSRI.js.map
|