@iloom/cli 0.3.4 → 0.4.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/README.md +13 -3
- package/dist/{BranchNamingService-A77VI6AI.js → BranchNamingService-GCCWB3LK.js} +4 -3
- package/dist/ClaudeContextManager-DK77227F.js +16 -0
- package/dist/ClaudeService-W3SA7HVG.js +15 -0
- package/dist/GitHubService-RPM27GWD.js +12 -0
- package/dist/{LoomLauncher-ZV3ZZIBA.js → LoomLauncher-S3YGJRJQ.js} +43 -27
- package/dist/LoomLauncher-S3YGJRJQ.js.map +1 -0
- package/dist/PromptTemplateManager-2TDZAUC6.js +9 -0
- package/dist/README.md +13 -3
- package/dist/{SettingsManager-I2LRCW2A.js → SettingsManager-FJFU6JJD.js} +7 -3
- package/dist/SettingsMigrationManager-EH3J2TCN.js +10 -0
- package/dist/{chunk-5Q3NDNNV.js → chunk-2W2FBL5G.js} +153 -6
- package/dist/chunk-2W2FBL5G.js.map +1 -0
- package/dist/{chunk-OXAM2WVC.js → chunk-55TB3FSG.js} +21 -1
- package/dist/chunk-55TB3FSG.js.map +1 -0
- package/dist/chunk-6UIGZD2N.js +20 -0
- package/dist/chunk-6UIGZD2N.js.map +1 -0
- package/dist/{chunk-RIEO2WML.js → chunk-74VMN2KC.js} +26 -2
- package/dist/chunk-74VMN2KC.js.map +1 -0
- package/dist/{chunk-2MAIX45J.js → chunk-BIIQHEXJ.js} +104 -43
- package/dist/chunk-BIIQHEXJ.js.map +1 -0
- package/dist/{chunk-UAN4A3YU.js → chunk-G6CIIJLT.js} +11 -11
- package/dist/{chunk-DLHA5VQ3.js → chunk-HD5SUKI2.js} +36 -179
- package/dist/chunk-HD5SUKI2.js.map +1 -0
- package/dist/{chunk-2IJEMXOB.js → chunk-IARWMDAX.js} +427 -428
- package/dist/chunk-IARWMDAX.js.map +1 -0
- package/dist/chunk-IJ7IGJT3.js +192 -0
- package/dist/chunk-IJ7IGJT3.js.map +1 -0
- package/dist/{chunk-2CXREBLZ.js → chunk-JC5HXN75.js} +8 -6
- package/dist/chunk-JC5HXN75.js.map +1 -0
- package/dist/{chunk-3RUPPQRG.js → chunk-KO2FOMHL.js} +43 -2
- package/dist/{chunk-3RUPPQRG.js.map → chunk-KO2FOMHL.js.map} +1 -1
- package/dist/{chunk-4XIDC3NF.js → chunk-MD6HA5IK.js} +2 -2
- package/dist/{chunk-OC4H6HJD.js → chunk-O7WHXLCB.js} +2 -2
- package/dist/{chunk-M7JJCX53.js → chunk-OEGECBFS.js} +20 -20
- package/dist/chunk-OEGECBFS.js.map +1 -0
- package/dist/{chunk-MKWYLDFK.js → chunk-OF7BNW4D.js} +43 -3
- package/dist/chunk-OF7BNW4D.js.map +1 -0
- package/dist/{chunk-PGPI5LR4.js → chunk-POI7KLBH.js} +7 -21
- package/dist/chunk-POI7KLBH.js.map +1 -0
- package/dist/{chunk-PA6Q6AWM.js → chunk-PSFVTBM7.js} +2 -2
- package/dist/chunk-QHA67Q7A.js +281 -0
- package/dist/chunk-QHA67Q7A.js.map +1 -0
- package/dist/{chunk-SUOXY5WJ.js → chunk-QIUJPPJQ.js} +5 -5
- package/dist/chunk-QIUJPPJQ.js.map +1 -0
- package/dist/{chunk-ZM3CFL5L.js → chunk-QRBOPFAA.js} +3 -3
- package/dist/{chunk-OYF4VIFI.js → chunk-RUC7OULH.js} +147 -22
- package/dist/chunk-RUC7OULH.js.map +1 -0
- package/dist/{chunk-CE26YH2U.js → chunk-SJ2GZ6RF.js} +48 -50
- package/dist/chunk-SJ2GZ6RF.js.map +1 -0
- package/dist/{chunk-SSCQCCJ7.js → chunk-THF25ICZ.js} +2 -2
- package/dist/chunk-TMZAVPGF.js +667 -0
- package/dist/chunk-TMZAVPGF.js.map +1 -0
- package/dist/{chunk-5VK4NRSF.js → chunk-UNXRACJ7.js} +35 -36
- package/dist/chunk-UNXRACJ7.js.map +1 -0
- package/dist/{chunk-AKUJXDNW.js → chunk-UPUAQYAW.js} +3 -3
- package/dist/{chunk-GEHQXLEI.js → chunk-UYVWLISQ.js} +18 -35
- package/dist/chunk-UYVWLISQ.js.map +1 -0
- package/dist/{chunk-OSCLCMDG.js → chunk-UYWAESOT.js} +3 -3
- package/dist/{chunk-RW54ZMBM.js → chunk-VAYGNQTE.js} +2 -2
- package/dist/{chunk-ZT3YZB4K.js → chunk-VBFDVGAE.js} +12 -12
- package/dist/chunk-VBFDVGAE.js.map +1 -0
- package/dist/{chunk-IFB4Z76W.js → chunk-VTXCGKV5.js} +13 -12
- package/dist/chunk-VTXCGKV5.js.map +1 -0
- package/dist/{chunk-CDZERT7Z.js → chunk-VWNS6DH5.js} +48 -4
- package/dist/chunk-VWNS6DH5.js.map +1 -0
- package/dist/{chunk-CFFQ2Z7A.js → chunk-WUQQNE63.js} +2 -2
- package/dist/{chunk-UJL4HI2R.js → chunk-Z5NXYJIG.js} +20 -2
- package/dist/chunk-Z5NXYJIG.js.map +1 -0
- package/dist/{claude-W52VKI6L.js → claude-ACVXNB6N.js} +8 -5
- package/dist/{cleanup-H4VXU3C3.js → cleanup-KDLVTT7M.js} +133 -122
- package/dist/cleanup-KDLVTT7M.js.map +1 -0
- package/dist/cli.js +953 -430
- package/dist/cli.js.map +1 -1
- package/dist/{color-F7RU6B6Z.js → color-ZPIIUADB.js} +3 -3
- package/dist/{contribute-Y7IQV5QY.js → contribute-HY372S6F.js} +8 -6
- package/dist/{contribute-Y7IQV5QY.js.map → contribute-HY372S6F.js.map} +1 -1
- package/dist/dev-server-JCJGQ3PV.js +298 -0
- package/dist/dev-server-JCJGQ3PV.js.map +1 -0
- package/dist/{feedback-XTUCKJNT.js → feedback-7PVBQNLJ.js} +13 -12
- package/dist/{feedback-XTUCKJNT.js.map → feedback-7PVBQNLJ.js.map} +1 -1
- package/dist/{git-IYA53VIC.js → git-4BVOOOOV.js} +16 -4
- package/dist/hooks/iloom-hook.js +258 -0
- package/dist/{ignite-T74RYXCA.js → ignite-3B264M7K.js} +245 -39
- package/dist/ignite-3B264M7K.js.map +1 -0
- package/dist/index.d.ts +461 -124
- package/dist/index.js +743 -210
- package/dist/index.js.map +1 -1
- package/dist/init-LBA6NUK2.js +21 -0
- package/dist/{installation-detector-VARGFFRZ.js → installation-detector-6R6YOFVZ.js} +3 -3
- package/dist/mcp/issue-management-server.js +2 -1
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/neon-helpers-L5CXQ5CT.js +11 -0
- package/dist/{open-UMXANW5S.js → open-OGCV32Z4.js} +15 -13
- package/dist/{open-UMXANW5S.js.map → open-OGCV32Z4.js.map} +1 -1
- package/dist/projects-P55273AB.js +73 -0
- package/dist/projects-P55273AB.js.map +1 -0
- package/dist/{prompt-QALMYTVC.js → prompt-A7GGRHSY.js} +3 -3
- package/dist/prompts/init-prompt.txt +49 -0
- package/dist/prompts/issue-prompt.txt +110 -8
- package/dist/prompts/regular-prompt.txt +90 -0
- package/dist/prompts/session-summary-prompt.txt +82 -0
- package/dist/{rebase-VJ2VKR6R.js → rebase-4T5FQHNH.js} +11 -9
- package/dist/{rebase-VJ2VKR6R.js.map → rebase-4T5FQHNH.js.map} +1 -1
- package/dist/{remote-VUNCQZ6J.js → remote-73TZ2ADI.js} +3 -3
- package/dist/{run-MJYY4PUT.js → run-HNOP6WE2.js} +15 -13
- package/dist/{run-MJYY4PUT.js.map → run-HNOP6WE2.js.map} +1 -1
- package/dist/schema/settings.schema.json +49 -0
- package/dist/shell-DE3HKJSM.js +240 -0
- package/dist/shell-DE3HKJSM.js.map +1 -0
- package/dist/summary-GDT7DTRI.js +244 -0
- package/dist/summary-GDT7DTRI.js.map +1 -0
- package/dist/{test-git-IT5EWQ5C.js → test-git-YMAE57UP.js} +6 -4
- package/dist/{test-git-IT5EWQ5C.js.map → test-git-YMAE57UP.js.map} +1 -1
- package/dist/{test-prefix-NPWDPUUH.js → test-prefix-YCKL6CMT.js} +6 -4
- package/dist/{test-prefix-NPWDPUUH.js.map → test-prefix-YCKL6CMT.js.map} +1 -1
- package/dist/{test-tabs-PRMRSHKI.js → test-tabs-3SCJWRKT.js} +4 -4
- package/dist/{test-webserver-DAHONWCS.js → test-webserver-VPNLAFZ3.js} +2 -2
- package/dist/{update-4TDDUR5K.js → update-LETF5ASC.js} +4 -4
- package/dist/{update-notifier-QEX3CJHA.js → update-notifier-H55ZK7NU.js} +3 -3
- package/package.json +6 -6
- package/dist/ClaudeContextManager-BN7RE5ZQ.js +0 -15
- package/dist/ClaudeService-DLYLJUPA.js +0 -14
- package/dist/GitHubService-FZHHBOFG.js +0 -11
- package/dist/LoomLauncher-ZV3ZZIBA.js.map +0 -1
- package/dist/PromptTemplateManager-6HH3PVXV.js +0 -9
- package/dist/SettingsMigrationManager-TJ7UWZG5.js +0 -10
- package/dist/chunk-2CXREBLZ.js.map +0 -1
- package/dist/chunk-2IJEMXOB.js.map +0 -1
- package/dist/chunk-2MAIX45J.js.map +0 -1
- package/dist/chunk-5Q3NDNNV.js.map +0 -1
- package/dist/chunk-5VK4NRSF.js.map +0 -1
- package/dist/chunk-CDZERT7Z.js.map +0 -1
- package/dist/chunk-CE26YH2U.js.map +0 -1
- package/dist/chunk-DLHA5VQ3.js.map +0 -1
- package/dist/chunk-GEHQXLEI.js.map +0 -1
- package/dist/chunk-IFB4Z76W.js.map +0 -1
- package/dist/chunk-M7JJCX53.js.map +0 -1
- package/dist/chunk-MKWYLDFK.js.map +0 -1
- package/dist/chunk-OXAM2WVC.js.map +0 -1
- package/dist/chunk-OYF4VIFI.js.map +0 -1
- package/dist/chunk-PGPI5LR4.js.map +0 -1
- package/dist/chunk-RIEO2WML.js.map +0 -1
- package/dist/chunk-SUOXY5WJ.js.map +0 -1
- package/dist/chunk-UJL4HI2R.js.map +0 -1
- package/dist/chunk-ZT3YZB4K.js.map +0 -1
- package/dist/cleanup-H4VXU3C3.js.map +0 -1
- package/dist/ignite-T74RYXCA.js.map +0 -1
- package/dist/init-4FHTAM3F.js +0 -19
- package/dist/logger-MKYH4UDV.js +0 -12
- package/dist/neon-helpers-77PBPGJ5.js +0 -10
- package/dist/update-notifier-QEX3CJHA.js.map +0 -1
- /package/dist/{BranchNamingService-A77VI6AI.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
- /package/dist/{ClaudeContextManager-BN7RE5ZQ.js.map → ClaudeContextManager-DK77227F.js.map} +0 -0
- /package/dist/{ClaudeService-DLYLJUPA.js.map → ClaudeService-W3SA7HVG.js.map} +0 -0
- /package/dist/{GitHubService-FZHHBOFG.js.map → GitHubService-RPM27GWD.js.map} +0 -0
- /package/dist/{PromptTemplateManager-6HH3PVXV.js.map → PromptTemplateManager-2TDZAUC6.js.map} +0 -0
- /package/dist/{SettingsManager-I2LRCW2A.js.map → SettingsManager-FJFU6JJD.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-TJ7UWZG5.js.map → SettingsMigrationManager-EH3J2TCN.js.map} +0 -0
- /package/dist/{chunk-UAN4A3YU.js.map → chunk-G6CIIJLT.js.map} +0 -0
- /package/dist/{chunk-4XIDC3NF.js.map → chunk-MD6HA5IK.js.map} +0 -0
- /package/dist/{chunk-OC4H6HJD.js.map → chunk-O7WHXLCB.js.map} +0 -0
- /package/dist/{chunk-PA6Q6AWM.js.map → chunk-PSFVTBM7.js.map} +0 -0
- /package/dist/{chunk-ZM3CFL5L.js.map → chunk-QRBOPFAA.js.map} +0 -0
- /package/dist/{chunk-SSCQCCJ7.js.map → chunk-THF25ICZ.js.map} +0 -0
- /package/dist/{chunk-AKUJXDNW.js.map → chunk-UPUAQYAW.js.map} +0 -0
- /package/dist/{chunk-OSCLCMDG.js.map → chunk-UYWAESOT.js.map} +0 -0
- /package/dist/{chunk-RW54ZMBM.js.map → chunk-VAYGNQTE.js.map} +0 -0
- /package/dist/{chunk-CFFQ2Z7A.js.map → chunk-WUQQNE63.js.map} +0 -0
- /package/dist/{claude-W52VKI6L.js.map → claude-ACVXNB6N.js.map} +0 -0
- /package/dist/{color-F7RU6B6Z.js.map → color-ZPIIUADB.js.map} +0 -0
- /package/dist/{git-IYA53VIC.js.map → git-4BVOOOOV.js.map} +0 -0
- /package/dist/{init-4FHTAM3F.js.map → init-LBA6NUK2.js.map} +0 -0
- /package/dist/{installation-detector-VARGFFRZ.js.map → installation-detector-6R6YOFVZ.js.map} +0 -0
- /package/dist/{logger-MKYH4UDV.js.map → neon-helpers-L5CXQ5CT.js.map} +0 -0
- /package/dist/{neon-helpers-77PBPGJ5.js.map → prompt-A7GGRHSY.js.map} +0 -0
- /package/dist/{prompt-QALMYTVC.js.map → remote-73TZ2ADI.js.map} +0 -0
- /package/dist/{test-tabs-PRMRSHKI.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
- /package/dist/{test-webserver-DAHONWCS.js.map → test-webserver-VPNLAFZ3.js.map} +0 -0
- /package/dist/{update-4TDDUR5K.js.map → update-LETF5ASC.js.map} +0 -0
- /package/dist/{remote-VUNCQZ6J.js.map → update-notifier-H55ZK7NU.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/open.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface OpenCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedOpenInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * OpenCommand - Opens workspace in browser or runs CLI tool\n * Priority: Web first, CLI fallback\n */\nexport class OpenCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: OpenCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (web first, CLI fallback)\n\t\tif (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else if (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No web or CLI capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedOpenInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach open command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in open command')\n\t\t}\n\n\t\tconst result: ParsedOpenInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedOpenInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedOpenInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedOpenInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from dotenv-flow files or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n}\n"],"mappings":"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","names":["port"]}
|
|
1
|
+
{"version":3,"sources":["../src/commands/open.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface OpenCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedOpenInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * OpenCommand - Opens workspace in browser or runs CLI tool\n * Priority: Web first, CLI fallback\n */\nexport class OpenCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: OpenCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (web first, CLI fallback)\n\t\tif (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else if (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No web or CLI capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedOpenInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach open command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in open command')\n\t\t}\n\n\t\tconst result: ParsedOpenInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedOpenInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedOpenInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedOpenInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from dotenv-flow files or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n}\n"],"mappings":"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","names":["port"]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
MetadataManager
|
|
4
|
+
} from "./chunk-IJ7IGJT3.js";
|
|
5
|
+
import {
|
|
6
|
+
getLogger
|
|
7
|
+
} from "./chunk-6UIGZD2N.js";
|
|
8
|
+
import "./chunk-UYVWLISQ.js";
|
|
9
|
+
|
|
10
|
+
// src/commands/projects.ts
|
|
11
|
+
import path from "path";
|
|
12
|
+
import os from "os";
|
|
13
|
+
import fs from "fs-extra";
|
|
14
|
+
var ProjectsCommand = class {
|
|
15
|
+
constructor(metadataManager) {
|
|
16
|
+
this.projectsDir = path.join(os.homedir(), ".config", "iloom-ai", "projects");
|
|
17
|
+
this.metadataManager = metadataManager ?? new MetadataManager();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute the projects command
|
|
21
|
+
* @param _options - Options object (json flag accepted but ignored - always returns JSON)
|
|
22
|
+
* @returns Array of project outputs
|
|
23
|
+
*/
|
|
24
|
+
async execute(_options) {
|
|
25
|
+
const results = [];
|
|
26
|
+
const logger = getLogger();
|
|
27
|
+
try {
|
|
28
|
+
if (!await fs.pathExists(this.projectsDir)) {
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
const files = await fs.readdir(this.projectsDir);
|
|
32
|
+
const allMetadata = await this.metadataManager.listAllMetadata();
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
if (file.startsWith(".")) continue;
|
|
35
|
+
try {
|
|
36
|
+
const filePath = path.join(this.projectsDir, file);
|
|
37
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
38
|
+
const marker = JSON.parse(content);
|
|
39
|
+
if (!marker.projectPath || !marker.projectName) continue;
|
|
40
|
+
if (!await fs.pathExists(marker.projectPath)) continue;
|
|
41
|
+
const activeLooms = this.countActiveLooms(marker.projectPath, allMetadata);
|
|
42
|
+
results.push({
|
|
43
|
+
configuredAt: marker.configuredAt,
|
|
44
|
+
projectPath: marker.projectPath,
|
|
45
|
+
projectName: marker.projectName,
|
|
46
|
+
activeLooms
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
logger.debug(`Skipping invalid project file: ${file}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
logger.debug(`Failed to list projects: ${error instanceof Error ? error.message : "Unknown"}`);
|
|
54
|
+
}
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Count active looms for a project
|
|
59
|
+
* Looms are counted if their worktreePath is in the project's -looms directory
|
|
60
|
+
* or if they share the same parent directory as the project.
|
|
61
|
+
*/
|
|
62
|
+
countActiveLooms(projectPath, allMetadata) {
|
|
63
|
+
return allMetadata.filter((meta) => {
|
|
64
|
+
if (!meta.worktreePath) return false;
|
|
65
|
+
const parentDir = path.dirname(meta.worktreePath);
|
|
66
|
+
return parentDir === path.dirname(projectPath) || parentDir.startsWith(projectPath + "-looms");
|
|
67
|
+
}).length;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
export {
|
|
71
|
+
ProjectsCommand
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=projects-P55273AB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/projects.ts"],"sourcesContent":["import path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getLogger } from '../utils/logger-context.js'\nimport { MetadataManager, type LoomMetadata } from '../lib/MetadataManager.js'\n\n/**\n * Project marker file structure (from FirstRunManager)\n */\ninterface ProjectMarker {\n configuredAt: string\n projectPath: string\n projectName: string\n}\n\n/**\n * Output schema for project\n */\nexport interface ProjectOutput {\n configuredAt: string\n projectPath: string\n projectName: string\n activeLooms: number\n}\n\n/**\n * ProjectsCommand: List configured iloom projects\n *\n * Returns JSON array of configured projects from ~/.config/iloom-ai/projects/\n * Only includes projects where the directory still exists.\n * Each project includes an activeLooms count.\n */\nexport class ProjectsCommand {\n private readonly projectsDir: string\n private readonly metadataManager: MetadataManager\n\n constructor(metadataManager?: MetadataManager) {\n this.projectsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'projects')\n this.metadataManager = metadataManager ?? new MetadataManager()\n }\n\n /**\n * Execute the projects command\n * @param _options - Options object (json flag accepted but ignored - always returns JSON)\n * @returns Array of project outputs\n */\n async execute(_options?: { json?: boolean }): Promise<ProjectOutput[]> {\n // Options.json is accepted but ignored - always returns structured data\n const results: ProjectOutput[] = []\n const logger = getLogger()\n\n try {\n // Check if projects directory exists\n if (!(await fs.pathExists(this.projectsDir))) {\n return results\n }\n\n // Read all files in projects directory\n const files = await fs.readdir(this.projectsDir)\n\n // Get all loom metadata for active looms lookup\n const allMetadata = await this.metadataManager.listAllMetadata()\n\n for (const file of files) {\n // Skip hidden files (like .DS_Store)\n if (file.startsWith('.')) continue\n\n try {\n const filePath = path.join(this.projectsDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const marker: ProjectMarker = JSON.parse(content)\n\n // Skip if required fields missing\n if (!marker.projectPath || !marker.projectName) continue\n\n // Filter: only include if directory exists\n if (!(await fs.pathExists(marker.projectPath))) continue\n\n // Count active looms for this project\n const activeLooms = this.countActiveLooms(marker.projectPath, allMetadata)\n\n results.push({\n configuredAt: marker.configuredAt,\n projectPath: marker.projectPath,\n projectName: marker.projectName,\n activeLooms,\n })\n } catch {\n // Skip invalid files (graceful degradation)\n logger.debug(`Skipping invalid project file: ${file}`)\n }\n }\n } catch (error) {\n // Graceful degradation on read errors\n logger.debug(`Failed to list projects: ${error instanceof Error ? error.message : 'Unknown'}`)\n }\n\n return results\n }\n\n /**\n * Count active looms for a project\n * Looms are counted if their worktreePath is in the project's -looms directory\n * or if they share the same parent directory as the project.\n */\n private countActiveLooms(projectPath: string, allMetadata: LoomMetadata[]): number {\n return allMetadata.filter((meta) => {\n if (!meta.worktreePath) return false\n const parentDir = path.dirname(meta.worktreePath)\n return (\n parentDir === path.dirname(projectPath) ||\n parentDir.startsWith(projectPath + '-looms')\n )\n }).length\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AA8BR,IAAM,kBAAN,MAAsB;AAAA,EAI3B,YAAY,iBAAmC;AAC7C,SAAK,cAAc,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY,UAAU;AAC5E,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,UAAyD;AAErE,UAAM,UAA2B,CAAC;AAClC,UAAM,SAAS,UAAU;AAEzB,QAAI;AAEF,UAAI,CAAE,MAAM,GAAG,WAAW,KAAK,WAAW,GAAI;AAC5C,eAAO;AAAA,MACT;AAGA,YAAM,QAAQ,MAAM,GAAG,QAAQ,KAAK,WAAW;AAG/C,YAAM,cAAc,MAAM,KAAK,gBAAgB,gBAAgB;AAE/D,iBAAW,QAAQ,OAAO;AAExB,YAAI,KAAK,WAAW,GAAG,EAAG;AAE1B,YAAI;AACF,gBAAM,WAAW,KAAK,KAAK,KAAK,aAAa,IAAI;AACjD,gBAAM,UAAU,MAAM,GAAG,SAAS,UAAU,MAAM;AAClD,gBAAM,SAAwB,KAAK,MAAM,OAAO;AAGhD,cAAI,CAAC,OAAO,eAAe,CAAC,OAAO,YAAa;AAGhD,cAAI,CAAE,MAAM,GAAG,WAAW,OAAO,WAAW,EAAI;AAGhD,gBAAM,cAAc,KAAK,iBAAiB,OAAO,aAAa,WAAW;AAEzE,kBAAQ,KAAK;AAAA,YACX,cAAc,OAAO;AAAA,YACrB,aAAa,OAAO;AAAA,YACpB,aAAa,OAAO;AAAA,YACpB;AAAA,UACF,CAAC;AAAA,QACH,QAAQ;AAEN,iBAAO,MAAM,kCAAkC,IAAI,EAAE;AAAA,QACvD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,aAAO,MAAM,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,SAAS,EAAE;AAAA,IAC/F;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAiB,aAAqB,aAAqC;AACjF,WAAO,YAAY,OAAO,CAAC,SAAS;AAClC,UAAI,CAAC,KAAK,aAAc,QAAO;AAC/B,YAAM,YAAY,KAAK,QAAQ,KAAK,YAAY;AAChD,aACE,cAAc,KAAK,QAAQ,WAAW,KACtC,UAAU,WAAW,cAAc,QAAQ;AAAA,IAE/C,CAAC,EAAE;AAAA,EACL;AACF;","names":[]}
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
promptConfirmation,
|
|
6
6
|
promptInput,
|
|
7
7
|
waitForKeypress
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-SJ2GZ6RF.js";
|
|
9
|
+
import "./chunk-UYVWLISQ.js";
|
|
10
10
|
export {
|
|
11
11
|
isInteractiveEnvironment,
|
|
12
12
|
promptCommitAction,
|
|
@@ -14,4 +14,4 @@ export {
|
|
|
14
14
|
promptInput,
|
|
15
15
|
waitForKeypress
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=prompt-
|
|
17
|
+
//# sourceMappingURL=prompt-A7GGRHSY.js.map
|
|
@@ -154,6 +154,11 @@ The following JSON Schema defines valid iloom settings:
|
|
|
154
154
|
"type": "boolean",
|
|
155
155
|
"default": false,
|
|
156
156
|
"description": "Launch terminal window without dev server when starting this workflow type"
|
|
157
|
+
},
|
|
158
|
+
"generateSummary": {
|
|
159
|
+
"type": "boolean",
|
|
160
|
+
"default": true,
|
|
161
|
+
"description": "Generate and post Claude session summary when finishing this workflow type"
|
|
157
162
|
}
|
|
158
163
|
},
|
|
159
164
|
"additionalProperties": false
|
|
@@ -194,6 +199,11 @@ The following JSON Schema defines valid iloom settings:
|
|
|
194
199
|
"type": "boolean",
|
|
195
200
|
"default": false,
|
|
196
201
|
"description": "Launch terminal window without dev server when starting this workflow type"
|
|
202
|
+
},
|
|
203
|
+
"generateSummary": {
|
|
204
|
+
"type": "boolean",
|
|
205
|
+
"default": true,
|
|
206
|
+
"description": "Generate and post Claude session summary when finishing this workflow type"
|
|
197
207
|
}
|
|
198
208
|
},
|
|
199
209
|
"additionalProperties": false
|
|
@@ -234,6 +244,11 @@ The following JSON Schema defines valid iloom settings:
|
|
|
234
244
|
"type": "boolean",
|
|
235
245
|
"default": false,
|
|
236
246
|
"description": "Launch terminal window without dev server when starting this workflow type"
|
|
247
|
+
},
|
|
248
|
+
"generateSummary": {
|
|
249
|
+
"type": "boolean",
|
|
250
|
+
"default": true,
|
|
251
|
+
"description": "Generate and post Claude session summary when finishing this workflow type"
|
|
237
252
|
}
|
|
238
253
|
},
|
|
239
254
|
"additionalProperties": false
|
|
@@ -275,6 +290,40 @@ The following JSON Schema defines valid iloom settings:
|
|
|
275
290
|
],
|
|
276
291
|
"description": "Per-agent configuration overrides. Available agents: iloom-issue-analyzer (analyzes issues), iloom-issue-planner (creates implementation plans), iloom-issue-analyze-and-plan (combined analysis and planning), iloom-issue-complexity-evaluator (evaluates complexity), iloom-issue-enhancer (enhances issue descriptions), iloom-issue-implementer (implements code changes), iloom-issue-reviewer (reviews code changes against requirements)"
|
|
277
292
|
},
|
|
293
|
+
"spin": {
|
|
294
|
+
"type": "object",
|
|
295
|
+
"properties": {
|
|
296
|
+
"model": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"enum": [
|
|
299
|
+
"sonnet",
|
|
300
|
+
"opus",
|
|
301
|
+
"haiku"
|
|
302
|
+
],
|
|
303
|
+
"default": "opus",
|
|
304
|
+
"description": "Claude model shorthand for spin orchestrator"
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
"additionalProperties": false,
|
|
308
|
+
"description": "Spin orchestrator configuration. Model defaults to opus when not configured."
|
|
309
|
+
},
|
|
310
|
+
"summary": {
|
|
311
|
+
"type": "object",
|
|
312
|
+
"properties": {
|
|
313
|
+
"model": {
|
|
314
|
+
"type": "string",
|
|
315
|
+
"enum": [
|
|
316
|
+
"sonnet",
|
|
317
|
+
"opus",
|
|
318
|
+
"haiku"
|
|
319
|
+
],
|
|
320
|
+
"default": "sonnet",
|
|
321
|
+
"description": "Claude model shorthand for session summary generation"
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
"additionalProperties": false,
|
|
325
|
+
"description": "Session summary generation configuration. Model defaults to sonnet when not configured."
|
|
326
|
+
},
|
|
278
327
|
"capabilities": {
|
|
279
328
|
"type": "object",
|
|
280
329
|
"properties": {
|
|
@@ -202,8 +202,26 @@ Perform ONE comprehensive scan to determine which agents need to run:
|
|
|
202
202
|
**STEP 1 - Enhancement Phase:**
|
|
203
203
|
|
|
204
204
|
Only execute if workflow plan determined NEEDS_ENHANCEMENT:
|
|
205
|
-
1. Execute: @agent-iloom-issue-enhancer ISSUE_NUMBER
|
|
205
|
+
1. Execute: @agent-iloom-issue-enhancer ISSUE_NUMBER with the following instructions about assumptions:
|
|
206
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
206
207
|
2. Upon completion: Extract issue+comment link (including comment ID) from agent output and provide link to issue comment
|
|
208
|
+
{{#IF INTERACTIVE_MODE}}
|
|
209
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
210
|
+
- Read the agent's issue comment output
|
|
211
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
212
|
+
- If assumptions/questions table found:
|
|
213
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
214
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
215
|
+
- Present each question with the agent's assumed answer shown
|
|
216
|
+
- Allow user to confirm or provide different answers for each
|
|
217
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
218
|
+
- If ANY mismatches detected:
|
|
219
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
220
|
+
- Re-run @agent-iloom-issue-enhancer with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]. Update your existing comment (comment ID: [COMMENT_ID])."
|
|
221
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
222
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
223
|
+
- If no assumptions found: Proceed to next step
|
|
224
|
+
{{/IF INTERACTIVE_MODE}}
|
|
207
225
|
3. Mark todo #2 and #3 as completed
|
|
208
226
|
4. Use AskUserQuestion tool with a single question:
|
|
209
227
|
- Question: "Enhancement complete. See results at: [issue comment URL]. How would you like to proceed?"
|
|
@@ -218,13 +236,42 @@ Only execute if workflow plan determined NEEDS_ENHANCEMENT:
|
|
|
218
236
|
|
|
219
237
|
If workflow plan determined SKIP_ENHANCEMENT:
|
|
220
238
|
1. Mark todos #2 and #3 as completed
|
|
221
|
-
|
|
239
|
+
{{#IF INTERACTIVE_MODE}}
|
|
240
|
+
2. Check for unanswered questions in the existing enhancement:
|
|
241
|
+
- Read the existing enhancement comment that was found during workflow planning
|
|
242
|
+
- Search for "Questions for Reporter" or similar question table sections
|
|
243
|
+
- Look for questions that have NO answer or are marked as "Unanswered", "TBD", "Unknown", or similar
|
|
244
|
+
- If unanswered questions found:
|
|
245
|
+
a. Use AskUserQuestion tool to present ALL unanswered questions to the user in a single batched request
|
|
246
|
+
b. After collecting all answers, update the enhancement comment with the user's answers (use mcp__issue_management__update_comment)
|
|
247
|
+
c. Proceed to next step with the newly answered questions available for subsequent agents
|
|
248
|
+
- If no unanswered questions found: Proceed to next step
|
|
249
|
+
{{/IF INTERACTIVE_MODE}}
|
|
250
|
+
3. Proceed directly to Step 1.5 (Complexity Evaluation Phase)
|
|
222
251
|
|
|
223
252
|
**STEP 1.5 - Complexity Evaluation Phase:**
|
|
224
253
|
|
|
225
254
|
Only execute if workflow plan determined NEEDS_COMPLEXITY_EVAL:
|
|
226
|
-
1. Execute: @agent-iloom-issue-complexity-evaluator ISSUE_NUMBER
|
|
255
|
+
1. Execute: @agent-iloom-issue-complexity-evaluator ISSUE_NUMBER with the following instructions about assumptions:
|
|
256
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
227
257
|
2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
|
|
258
|
+
{{#IF INTERACTIVE_MODE}}
|
|
259
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
260
|
+
- Read the agent's issue comment output
|
|
261
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
262
|
+
- If assumptions/questions table found:
|
|
263
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
264
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
265
|
+
- Present each question with the agent's assumed answer shown
|
|
266
|
+
- Allow user to confirm or provide different answers for each
|
|
267
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
268
|
+
- If ANY mismatches detected:
|
|
269
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
270
|
+
- Re-run @agent-iloom-issue-complexity-evaluator with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]. Update your existing comment (comment ID: [COMMENT_ID])."
|
|
271
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
272
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
273
|
+
- If no assumptions found: Proceed to next step
|
|
274
|
+
{{/IF INTERACTIVE_MODE}}
|
|
228
275
|
3. Extract complexity classification from evaluator output:
|
|
229
276
|
- Search the evaluator's output for the "Complexity Assessment" section
|
|
230
277
|
- Extract: Classification (TRIVIAL/SIMPLE/COMPLEX), Metrics (files, LOC, breaking changes, DB migrations, risk level), and Reasoning
|
|
@@ -272,8 +319,26 @@ If workflow plan determined SKIP_COMPLEXITY_EVAL:
|
|
|
272
319
|
**STEP 2 - Analysis Phase (COMPLEX workflow only):**
|
|
273
320
|
|
|
274
321
|
Only execute if workflow plan determined NEEDS_ANALYSIS AND complexity is COMPLEX:
|
|
275
|
-
1. Execute: @agent-iloom-issue-analyzer ISSUE_NUMBER
|
|
322
|
+
1. Execute: @agent-iloom-issue-analyzer ISSUE_NUMBER with the following instructions about assumptions:
|
|
323
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
276
324
|
2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
|
|
325
|
+
{{#IF INTERACTIVE_MODE}}
|
|
326
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
327
|
+
- Read the agent's issue comment output
|
|
328
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
329
|
+
- If assumptions/questions table found:
|
|
330
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
331
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
332
|
+
- Present each question with the agent's assumed answer shown
|
|
333
|
+
- Allow user to confirm or provide different answers for each
|
|
334
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
335
|
+
- If ANY mismatches detected:
|
|
336
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
337
|
+
- Re-run @agent-iloom-issue-analyzer with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]. Update your existing comment (comment ID: [COMMENT_ID])."
|
|
338
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
339
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
340
|
+
- If no assumptions found: Proceed to next step
|
|
341
|
+
{{/IF INTERACTIVE_MODE}}
|
|
277
342
|
3. Mark todos #10, #11, and #12 as completed (todo #9 was already marked at routing decision point)
|
|
278
343
|
4. Use AskUserQuestion tool with a single question:
|
|
279
344
|
- Question: "Analysis complete. See findings at: [issue comment URL]. How would you like to proceed?"
|
|
@@ -326,8 +391,26 @@ After STEP 1.5 completes and complexity is confirmed, determine which workflow p
|
|
|
326
391
|
|
|
327
392
|
Execute combined analyze-and-plan agent:
|
|
328
393
|
1. Display: "Executing combined analyze-and-plan agent for SIMPLE task..."
|
|
329
|
-
2. Execute: @agent-iloom-issue-analyze-and-plan ISSUE_NUMBER
|
|
394
|
+
2. Execute: @agent-iloom-issue-analyze-and-plan ISSUE_NUMBER with the following instructions about assumptions:
|
|
395
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
330
396
|
3. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
|
|
397
|
+
{{#IF INTERACTIVE_MODE}}
|
|
398
|
+
3.5. Extract and validate assumptions (batched validation):
|
|
399
|
+
- Read the agent's issue comment output
|
|
400
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
401
|
+
- If assumptions/questions table found:
|
|
402
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
403
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
404
|
+
- Present each question with the agent's assumed answer shown
|
|
405
|
+
- Allow user to confirm or provide different answers for each
|
|
406
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
407
|
+
- If ANY mismatches detected:
|
|
408
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
409
|
+
- Re-run @agent-iloom-issue-analyze-and-plan with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]. Update your existing comment (comment ID: [COMMENT_ID])."
|
|
410
|
+
- Return to step 3.5 (validate the revised assumptions again)
|
|
411
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
412
|
+
- If no assumptions found: Proceed to next step
|
|
413
|
+
{{/IF INTERACTIVE_MODE}}
|
|
331
414
|
4. Mark todo #9 as completed (COMPLEX todos #10-15 were already marked at routing decision point)
|
|
332
415
|
5. Use AskUserQuestion tool with a single question:
|
|
333
416
|
- Question: "Combined analysis and planning complete. See results at: [issue comment URL]. How would you like to proceed?"
|
|
@@ -347,8 +430,26 @@ Execute combined analyze-and-plan agent:
|
|
|
347
430
|
**IMPORTANT: Only execute this step if COMPLEX workflow is being followed (not SIMPLE)**
|
|
348
431
|
|
|
349
432
|
Only execute if workflow plan determined NEEDS_PLANNING AND complexity is COMPLEX:
|
|
350
|
-
1. Execute: @agent-iloom-issue-planner ISSUE_NUMBER
|
|
433
|
+
1. Execute: @agent-iloom-issue-planner ISSUE_NUMBER with the following instructions about assumptions:
|
|
434
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
351
435
|
2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
|
|
436
|
+
{{#IF INTERACTIVE_MODE}}
|
|
437
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
438
|
+
- Read the agent's issue comment output
|
|
439
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
440
|
+
- If assumptions/questions table found:
|
|
441
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
442
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
443
|
+
- Present each question with the agent's assumed answer shown
|
|
444
|
+
- Allow user to confirm or provide different answers for each
|
|
445
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
446
|
+
- If ANY mismatches detected:
|
|
447
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
448
|
+
- Re-run @agent-iloom-issue-planner with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]. Update your existing comment (comment ID: [COMMENT_ID])."
|
|
449
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
450
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
451
|
+
- If no assumptions found: Proceed to next step
|
|
452
|
+
{{/IF INTERACTIVE_MODE}}
|
|
352
453
|
3. Mark todos #13, #14, and #15 as completed (todos #9-12 were already marked at routing decision point and analysis phase)
|
|
353
454
|
4. Use AskUserQuestion tool with a single question:
|
|
354
455
|
- Question: "Planning complete. See implementation plan at: [issue comment URL]. How would you like to proceed?"
|
|
@@ -374,8 +475,9 @@ Only execute if workflow plan determined NEEDS_IMPLEMENTATION:
|
|
|
374
475
|
- Look back at the planning phase output (STEP 3 for COMPLEX or STEP 2-SIMPLE for SIMPLE workflows)
|
|
375
476
|
- Extract the comment ID from the issue comment URL that was provided
|
|
376
477
|
- The comment ID is the numeric value after "#issuecomment-" in the URL (e.g., https://github.com/org/repo/issues/123#issuecomment-456789 has comment ID 456789)
|
|
377
|
-
2. Execute: @agent-iloom-issue-implementer ISSUE_NUMBER with
|
|
378
|
-
-
|
|
478
|
+
2. Execute: @agent-iloom-issue-implementer ISSUE_NUMBER with the following instructions about assumptions:
|
|
479
|
+
- Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
|
|
480
|
+
Additional critical instructions:
|
|
379
481
|
- **CRITICAL**: The implementation plan in the issue comments contains exact file paths and line numbers. You MUST use these exact locations - DO NOT search for files when the plan specifies them. Extract file locations from the plan in Step 1.5 before implementing.
|
|
380
482
|
- The implementation plan is located in comment ID [COMMENT_ID] (if planning phase was executed). Read this comment first in Step 1.5 to extract file specifications.
|
|
381
483
|
3. Upon completion: Mark todo #16 as completed
|
|
@@ -169,6 +169,7 @@ You are orchestrating a set of agents through a development process, with human
|
|
|
169
169
|
* Clarify ambiguous requirements
|
|
170
170
|
* Make decisions about scope and approach
|
|
171
171
|
- Instruct the agent: "Do NOT use the issue_management MCP - there is no issue tracking backend. Return the enhanced specification directly to this conversation."
|
|
172
|
+
- Instruct the agent: "Before making assumptions, document any assumptions clearly in question tables with your own answers."
|
|
172
173
|
- The agent should output its enhanced specification directly to the conversation
|
|
173
174
|
2. Display the enhanced specification to the user:
|
|
174
175
|
```
|
|
@@ -180,6 +181,23 @@ You are orchestrating a set of agents through a development process, with human
|
|
|
180
181
|
- Constraints: [technical or business constraints]
|
|
181
182
|
- Acceptance Criteria: [how to verify completion]
|
|
182
183
|
```
|
|
184
|
+
{{#IF INTERACTIVE_MODE}}
|
|
185
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
186
|
+
- Read the agent's output
|
|
187
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
188
|
+
- If assumptions/questions table found:
|
|
189
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
190
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
191
|
+
- Present each question with the agent's assumed answer shown
|
|
192
|
+
- Allow user to confirm or provide different answers for each
|
|
193
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
194
|
+
- If ANY mismatches detected:
|
|
195
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
196
|
+
- Re-run @agent-iloom-issue-enhancer with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]."
|
|
197
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
198
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
199
|
+
- If no assumptions found: Proceed to next step
|
|
200
|
+
{{/IF INTERACTIVE_MODE}}
|
|
183
201
|
3. Mark todos #2 and #3 as completed
|
|
184
202
|
4. Use AskUserQuestion tool with a single question:
|
|
185
203
|
- Question: "Enhancement complete. Does this specification accurately capture your requirements?"
|
|
@@ -202,7 +220,25 @@ You are orchestrating a set of agents through a development process, with human
|
|
|
202
220
|
- Provide the enhanced specification directly to the agent: "This is branch mode - analyze complexity based on the following enhanced specification instead of fetching from GitHub: [ENHANCED_SPECIFICATION]"
|
|
203
221
|
- Instruct the agent to use the AskUserQuestion tool to validate any assumptions about scope or complexity
|
|
204
222
|
- Instruct the agent: "Do NOT use the issue_management MCP - there is no issue tracking backend. Return the complexity assessment directly to this conversation."
|
|
223
|
+
- Instruct the agent: "Before making assumptions, document any assumptions clearly in question tables with your own answers."
|
|
205
224
|
- The agent should analyze complexity based on the enhanced specification scope
|
|
225
|
+
{{#IF INTERACTIVE_MODE}}
|
|
226
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
227
|
+
- Read the agent's output
|
|
228
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
229
|
+
- If assumptions/questions table found:
|
|
230
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
231
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
232
|
+
- Present each question with the agent's assumed answer shown
|
|
233
|
+
- Allow user to confirm or provide different answers for each
|
|
234
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
235
|
+
- If ANY mismatches detected:
|
|
236
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
237
|
+
- Re-run @agent-iloom-issue-complexity-evaluator with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]."
|
|
238
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
239
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
240
|
+
- If no assumptions found: Proceed to next step
|
|
241
|
+
{{/IF INTERACTIVE_MODE}}
|
|
206
242
|
2. Extract complexity classification from evaluator output:
|
|
207
243
|
- Search the evaluator's output for the "Complexity Assessment" section
|
|
208
244
|
- Extract: Classification (TRIVIAL/SIMPLE/COMPLEX), Metrics (files, LOC, breaking changes, DB migrations, risk level), and Reasoning
|
|
@@ -275,8 +311,26 @@ Execute combined analyze-and-plan agent:
|
|
|
275
311
|
- Provide the enhanced specification directly to the agent: "This is branch mode - analyze and plan based on the following enhanced specification instead of fetching from GitHub: [ENHANCED_SPECIFICATION]"
|
|
276
312
|
- Instruct the agent to use the AskUserQuestion tool to validate any assumptions or make decisions about the approach
|
|
277
313
|
- Instruct the agent: "Do NOT use the issue_management MCP - there is no issue tracking backend. Return the analysis and plan directly to this conversation."
|
|
314
|
+
- Instruct the agent: "Before making assumptions, document any assumptions clearly in question tables with your own answers."
|
|
278
315
|
- The agent should output its analysis and plan directly to the conversation
|
|
279
316
|
3. Display the analysis and plan results to the user
|
|
317
|
+
{{#IF INTERACTIVE_MODE}}
|
|
318
|
+
3.5. Extract and validate assumptions (batched validation):
|
|
319
|
+
- Read the agent's output
|
|
320
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
321
|
+
- If assumptions/questions table found:
|
|
322
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
323
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
324
|
+
- Present each question with the agent's assumed answer shown
|
|
325
|
+
- Allow user to confirm or provide different answers for each
|
|
326
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
327
|
+
- If ANY mismatches detected:
|
|
328
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
329
|
+
- Re-run @agent-iloom-issue-analyze-and-plan with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]."
|
|
330
|
+
- Return to step 3.5 (validate the revised assumptions again)
|
|
331
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
332
|
+
- If no assumptions found: Proceed to next step
|
|
333
|
+
{{/IF INTERACTIVE_MODE}}
|
|
280
334
|
4. Mark todo #10 as completed (COMPLEX todos #11-16 were already marked at routing decision point)
|
|
281
335
|
5. Use AskUserQuestion tool with a single question:
|
|
282
336
|
- Question: "Combined analysis and planning complete. How would you like to proceed?"
|
|
@@ -300,8 +354,26 @@ Execute combined analyze-and-plan agent:
|
|
|
300
354
|
- Provide the enhanced specification directly to the agent: "This is branch mode - analyze the following enhanced specification instead of fetching from GitHub: [ENHANCED_SPECIFICATION]"
|
|
301
355
|
- Instruct the agent to use the AskUserQuestion tool to validate any assumptions or clarify requirements during analysis
|
|
302
356
|
- Instruct the agent: "Do NOT use the issue_management MCP - there is no issue tracking backend. Return the analysis directly to this conversation."
|
|
357
|
+
- Instruct the agent: "Before making assumptions, document any assumptions clearly in question tables with your own answers."
|
|
303
358
|
- The agent should output its analysis directly to the conversation
|
|
304
359
|
2. Display the analysis results to the user
|
|
360
|
+
{{#IF INTERACTIVE_MODE}}
|
|
361
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
362
|
+
- Read the agent's output
|
|
363
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
364
|
+
- If assumptions/questions table found:
|
|
365
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
366
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
367
|
+
- Present each question with the agent's assumed answer shown
|
|
368
|
+
- Allow user to confirm or provide different answers for each
|
|
369
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
370
|
+
- If ANY mismatches detected:
|
|
371
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
372
|
+
- Re-run @agent-iloom-issue-analyzer with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]."
|
|
373
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
374
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
375
|
+
- If no assumptions found: Proceed to next step
|
|
376
|
+
{{/IF INTERACTIVE_MODE}}
|
|
305
377
|
3. Mark todos #11 and #12 as completed
|
|
306
378
|
4. Use AskUserQuestion tool with a single question:
|
|
307
379
|
- Question: "Analysis complete. How would you like to proceed?"
|
|
@@ -326,8 +398,26 @@ Execute combined analyze-and-plan agent:
|
|
|
326
398
|
- Provide the enhanced specification AND analysis results directly to the agent: "This is branch mode - create an implementation plan based on the following enhanced specification and analysis instead of fetching from GitHub: [ENHANCED_SPECIFICATION] [ANALYSIS_RESULTS]"
|
|
327
399
|
- Instruct the agent to use the AskUserQuestion tool to validate any assumptions or make decisions about implementation approach
|
|
328
400
|
- Instruct the agent: "Do NOT use the issue_management MCP - there is no issue tracking backend. Return the implementation plan directly to this conversation."
|
|
401
|
+
- Instruct the agent: "Before making assumptions, document any assumptions clearly in question tables with your own answers."
|
|
329
402
|
- The agent should output its plan directly to the conversation
|
|
330
403
|
2. Display the implementation plan to the user
|
|
404
|
+
{{#IF INTERACTIVE_MODE}}
|
|
405
|
+
2.5. Extract and validate assumptions (batched validation):
|
|
406
|
+
- Read the agent's output
|
|
407
|
+
- Search for "Questions for Reporter", "Questions and Key Decisions", or "Assumption" table section
|
|
408
|
+
- If assumptions/questions table found:
|
|
409
|
+
a. Parse the table to extract all questions and the agent's documented assumptions/answers
|
|
410
|
+
b. Use AskUserQuestion tool to present ALL questions to the user in a single batched request:
|
|
411
|
+
- Present each question with the agent's assumed answer shown
|
|
412
|
+
- Allow user to confirm or provide different answers for each
|
|
413
|
+
c. Compare user's answers to the agent's documented assumptions:
|
|
414
|
+
- If ANY mismatches detected:
|
|
415
|
+
- Compile feedback listing each mismatch: "For [question], you assumed [agent's answer] but user wants [user's answer]"
|
|
416
|
+
- Re-run @agent-iloom-issue-planner with feedback: "User provided different answers than your assumptions. Please revise based on this feedback: [list of mismatches]."
|
|
417
|
+
- Return to step 2.5 (validate the revised assumptions again)
|
|
418
|
+
- If all user answers match agent assumptions: Proceed to next step
|
|
419
|
+
- If no assumptions found: Proceed to next step
|
|
420
|
+
{{/IF INTERACTIVE_MODE}}
|
|
331
421
|
3. Mark todos #14 and #15 as completed
|
|
332
422
|
4. Use AskUserQuestion tool with a single question:
|
|
333
423
|
- Question: "Planning complete. How would you like to proceed?"
|