@iloom/cli 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +179 -41
- package/dist/{BranchNamingService-K6XNWQ6C.js → BranchNamingService-ECJHBB67.js} +2 -2
- package/dist/ClaudeContextManager-QXX6ZFST.js +14 -0
- package/dist/ClaudeService-NJNK2SUH.js +13 -0
- package/dist/{GitHubService-O7T6CFAJ.js → GitHubService-MEHKHUQP.js} +4 -4
- package/dist/IssueTrackerFactory-NG53YX5S.js +14 -0
- package/dist/{LoomLauncher-3I47SUPV.js → LoomLauncher-L64HHS3T.js} +9 -9
- package/dist/{MetadataManager-W3C54UYT.js → MetadataManager-5QZSTKNN.js} +2 -2
- package/dist/{ProjectCapabilityDetector-N5L7T4IY.js → ProjectCapabilityDetector-5KSYUTBJ.js} +3 -3
- package/dist/{PromptTemplateManager-36YLQRHP.js → PromptTemplateManager-DULSVRRE.js} +2 -2
- package/dist/README.md +179 -41
- package/dist/{SettingsManager-QR7V2IW2.js → SettingsManager-BQDQA3FK.js} +4 -2
- package/dist/agents/iloom-artifact-reviewer.md +11 -0
- package/dist/agents/iloom-code-reviewer.md +14 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +55 -12
- package/dist/agents/iloom-issue-analyzer.md +49 -6
- package/dist/agents/iloom-issue-complexity-evaluator.md +47 -6
- package/dist/agents/iloom-issue-enhancer.md +86 -7
- package/dist/agents/iloom-issue-implementer.md +48 -7
- package/dist/agents/iloom-issue-planner.md +115 -62
- package/dist/{build-IC4CJRMP.js → build-5GO3XW26.js} +9 -9
- package/dist/{chunk-USSL2X4A.js → chunk-3D7WQM7I.js} +2 -2
- package/dist/chunk-4232AHNQ.js +35 -0
- package/dist/chunk-4232AHNQ.js.map +1 -0
- package/dist/{chunk-QN47QVBX.js → chunk-4WJNIR5O.js} +1 -1
- package/dist/chunk-4WJNIR5O.js.map +1 -0
- package/dist/{chunk-2JPXGGP4.js → chunk-5MWV33NN.js} +4 -4
- package/dist/{chunk-POU2UMWN.js → chunk-6EU6TCF6.js} +10 -10
- package/dist/chunk-6EU6TCF6.js.map +1 -0
- package/dist/{chunk-Y5O2ALDZ.js → chunk-FB47TIJG.js} +29 -11
- package/dist/chunk-FB47TIJG.js.map +1 -0
- package/dist/chunk-HEXKPKCK.js +1396 -0
- package/dist/chunk-HEXKPKCK.js.map +1 -0
- package/dist/{chunk-KAYXR544.js → chunk-J5S7DFYC.js} +2 -2
- package/dist/{chunk-OK7LUTRW.js → chunk-JO2LZ6EQ.js} +476 -5
- package/dist/chunk-JO2LZ6EQ.js.map +1 -0
- package/dist/{chunk-KBEIQP4G.js → chunk-KB64WNBZ.js} +43 -3
- package/dist/chunk-KB64WNBZ.js.map +1 -0
- package/dist/{chunk-Y5HSSIK2.js → chunk-KXDRI47U.js} +71 -13
- package/dist/chunk-KXDRI47U.js.map +1 -0
- package/dist/{chunk-HZXBHMVM.js → chunk-LXLMMXXY.js} +54 -14
- package/dist/chunk-LXLMMXXY.js.map +1 -0
- package/dist/{chunk-H6ST2TGP.js → chunk-MNHZB4Z2.js} +4 -4
- package/dist/{chunk-TL72BGP6.js → chunk-MORRVYPT.js} +2 -2
- package/dist/{chunk-TGRK3CHF.js → chunk-NRSWLOAZ.js} +8 -8
- package/dist/chunk-NRSWLOAZ.js.map +1 -0
- package/dist/{chunk-FO5GGFOV.js → chunk-ONQYPICO.js} +13 -5
- package/dist/chunk-ONQYPICO.js.map +1 -0
- package/dist/{chunk-7ZEHSSUP.js → chunk-P4O6EH46.js} +4 -4
- package/dist/chunk-QZWEJVWV.js +207 -0
- package/dist/chunk-QZWEJVWV.js.map +1 -0
- package/dist/chunk-RSYT7MVI.js +202 -0
- package/dist/chunk-RSYT7MVI.js.map +1 -0
- package/dist/{chunk-OAVJR4PM.js → chunk-RYWFS37M.js} +6 -6
- package/dist/chunk-RYWFS37M.js.map +1 -0
- package/dist/{chunk-B7U6OKUR.js → chunk-SF2P22EE.js} +11 -3
- package/dist/chunk-SF2P22EE.js.map +1 -0
- package/dist/{chunk-MZPRBNYC.js → chunk-SN3SQCFK.js} +10 -8
- package/dist/{chunk-MZPRBNYC.js.map → chunk-SN3SQCFK.js.map} +1 -1
- package/dist/{chunk-4ZIHFUPN.js → chunk-UD3WJDIV.js} +145 -107
- package/dist/chunk-UD3WJDIV.js.map +1 -0
- package/dist/{chunk-3P6J4IZZ.js → chunk-UKBAJ2QQ.js} +61 -7
- package/dist/chunk-UKBAJ2QQ.js.map +1 -0
- package/dist/{chunk-RD7OPXZK.js → chunk-UVD4CZKS.js} +3 -3
- package/dist/chunk-UWGVCXRF.js +207 -0
- package/dist/chunk-UWGVCXRF.js.map +1 -0
- package/dist/{chunk-JT5LZRMI.js → chunk-VECNX6VX.js} +2 -2
- package/dist/{chunk-TRUMP4DA.js → chunk-VG45TUYK.js} +75 -6
- package/dist/chunk-VG45TUYK.js.map +1 -0
- package/dist/{chunk-4GAJJUYS.js → chunk-VGGST52X.js} +2 -2
- package/dist/{chunk-4LKGCFGG.js → chunk-WWKOVDWC.js} +2 -2
- package/dist/{chunk-2HZX6AMR.js → chunk-WY4QBK43.js} +7 -7
- package/dist/chunk-WY4QBK43.js.map +1 -0
- package/dist/chunk-Y4YZTHZE.js +73 -0
- package/dist/chunk-Y4YZTHZE.js.map +1 -0
- package/dist/{chunk-VOGGLPG5.js → chunk-YQ57ORTV.js} +14 -1
- package/dist/chunk-YQ57ORTV.js.map +1 -0
- package/dist/{chunk-XFEK2X2D.js → chunk-YYAKPQBT.js} +73 -20
- package/dist/chunk-YYAKPQBT.js.map +1 -0
- package/dist/{chunk-NTTSUAVM.js → chunk-ZEWU5PZK.js} +2 -2
- package/dist/{chunk-5LVVQGB3.js → chunk-ZHPNZC75.js} +17 -17
- package/dist/chunk-ZHPNZC75.js.map +1 -0
- package/dist/{chunk-I3HMNWQQ.js → chunk-ZW2LKWWE.js} +9 -9
- package/dist/chunk-ZW2LKWWE.js.map +1 -0
- package/dist/{claude-TP2QO3BU.js → claude-P3NQR6IJ.js} +2 -2
- package/dist/{cleanup-D3CSRBBZ.js → cleanup-6UCPVMFG.js} +81 -32
- package/dist/cleanup-6UCPVMFG.js.map +1 -0
- package/dist/cli.js +640 -350
- package/dist/cli.js.map +1 -1
- package/dist/{commit-IWGT42XN.js → commit-L3EPY5QG.js} +23 -21
- package/dist/commit-L3EPY5QG.js.map +1 -0
- package/dist/{compile-EOWJORKO.js → compile-ZS4HYRX5.js} +9 -9
- package/dist/{contribute-WSJTV2RX.js → contribute-ORDDQGSL.js} +14 -6
- package/dist/contribute-ORDDQGSL.js.map +1 -0
- package/dist/{dev-server-Q6M62ATG.js → dev-server-FYZ2AQIH.js} +29 -15
- package/dist/dev-server-FYZ2AQIH.js.map +1 -0
- package/dist/{feedback-QPNDZQRV.js → feedback-TMBXSCM5.js} +15 -15
- package/dist/{git-W3XUIFTR.js → git-ET64COO3.js} +4 -4
- package/dist/hooks/iloom-hook.js +15 -0
- package/dist/ignite-CGOV3TD4.js +1393 -0
- package/dist/ignite-CGOV3TD4.js.map +1 -0
- package/dist/index.d.ts +397 -53
- package/dist/index.js +1178 -40
- package/dist/index.js.map +1 -1
- package/dist/{init-ALYWKNWG.js → init-GFQ5W7GK.js} +57 -21
- package/dist/init-GFQ5W7GK.js.map +1 -0
- package/dist/issues-T4ZZSPEG.js +179 -0
- package/dist/issues-T4ZZSPEG.js.map +1 -0
- package/dist/{lint-IHUH45OC.js → lint-6TQXDZ3T.js} +9 -9
- package/dist/mcp/issue-management-server.js +2472 -257
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/mcp/recap-server.js +144 -21
- package/dist/mcp/recap-server.js.map +1 -1
- package/dist/{neon-helpers-VVFFTLXE.js → neon-helpers-CQN2PB4S.js} +3 -3
- package/dist/neon-helpers-CQN2PB4S.js.map +1 -0
- package/dist/{open-KWOV2OFO.js → open-5QZGXQRF.js} +15 -15
- package/dist/open-5QZGXQRF.js.map +1 -0
- package/dist/{plan-BRJBFJHF.js → plan-U7ZQWLFY.js} +41 -25
- package/dist/plan-U7ZQWLFY.js.map +1 -0
- package/dist/{projects-LH362JZQ.js → projects-2UOXFLNZ.js} +4 -4
- package/dist/prompts/CLAUDE.md +62 -0
- package/dist/prompts/init-prompt.txt +386 -47
- package/dist/prompts/issue-prompt.txt +427 -54
- package/dist/prompts/plan-prompt.txt +97 -16
- package/dist/prompts/pr-prompt.txt +44 -1
- package/dist/prompts/regular-prompt.txt +42 -1
- package/dist/prompts/session-summary-prompt.txt +14 -0
- package/dist/prompts/swarm-orchestrator-prompt.txt +437 -0
- package/dist/{rebase-AJOJOZUG.js → rebase-DWIB77KV.js} +10 -10
- package/dist/{recap-GKJXMDXW.js → recap-MX63HAKV.js} +47 -19
- package/dist/recap-MX63HAKV.js.map +1 -0
- package/dist/{run-QEUVZF7J.js → run-O3TFNQFC.js} +15 -15
- package/dist/run-O3TFNQFC.js.map +1 -0
- package/dist/schema/package-iloom.schema.json +58 -0
- package/dist/schema/settings.schema.json +130 -15
- package/dist/{shell-DAAVG4YN.js → shell-G6VC2CYR.js} +14 -7
- package/dist/shell-G6VC2CYR.js.map +1 -0
- package/dist/{summary-ZKOA35PT.js → summary-FWHAX55O.js} +27 -25
- package/dist/summary-FWHAX55O.js.map +1 -0
- package/dist/{test-5GPWWO3P.js → test-F7JNJZYP.js} +9 -9
- package/dist/{test-git-EJUKDB7F.js → test-git-BTAOIUE2.js} +4 -4
- package/dist/test-jira-CHYNV33F.js +96 -0
- package/dist/test-jira-CHYNV33F.js.map +1 -0
- package/dist/{test-prefix-23TOBUXY.js → test-prefix-Q6TFSU6F.js} +4 -4
- package/dist/{test-webserver-CKROHFBQ.js → test-webserver-EONCG7E7.js} +6 -6
- package/dist/{vscode-6TOLFCI2.js → vscode-VA5X4P25.js} +7 -7
- package/package.json +5 -1
- package/dist/ClaudeContextManager-X2Y72GRL.js +0 -14
- package/dist/ClaudeService-7P32TTES.js +0 -13
- package/dist/chunk-2HZX6AMR.js.map +0 -1
- package/dist/chunk-3P6J4IZZ.js.map +0 -1
- package/dist/chunk-4ZIHFUPN.js.map +0 -1
- package/dist/chunk-5LVVQGB3.js.map +0 -1
- package/dist/chunk-B7U6OKUR.js.map +0 -1
- package/dist/chunk-ENGCJIYQ.js +0 -520
- package/dist/chunk-ENGCJIYQ.js.map +0 -1
- package/dist/chunk-FO5GGFOV.js.map +0 -1
- package/dist/chunk-HZXBHMVM.js.map +0 -1
- package/dist/chunk-I3HMNWQQ.js.map +0 -1
- package/dist/chunk-J7FJ6PUT.js +0 -121
- package/dist/chunk-J7FJ6PUT.js.map +0 -1
- package/dist/chunk-KBEIQP4G.js.map +0 -1
- package/dist/chunk-OAVJR4PM.js.map +0 -1
- package/dist/chunk-OK7LUTRW.js.map +0 -1
- package/dist/chunk-POU2UMWN.js.map +0 -1
- package/dist/chunk-QN47QVBX.js.map +0 -1
- package/dist/chunk-TGRK3CHF.js.map +0 -1
- package/dist/chunk-TRUMP4DA.js.map +0 -1
- package/dist/chunk-VOGGLPG5.js.map +0 -1
- package/dist/chunk-XFEK2X2D.js.map +0 -1
- package/dist/chunk-Y5HSSIK2.js.map +0 -1
- package/dist/chunk-Y5O2ALDZ.js.map +0 -1
- package/dist/cleanup-D3CSRBBZ.js.map +0 -1
- package/dist/commit-IWGT42XN.js.map +0 -1
- package/dist/contribute-WSJTV2RX.js.map +0 -1
- package/dist/dev-server-Q6M62ATG.js.map +0 -1
- package/dist/ignite-OPO6EDYT.js +0 -784
- package/dist/ignite-OPO6EDYT.js.map +0 -1
- package/dist/init-ALYWKNWG.js.map +0 -1
- package/dist/issues-L7TBUPXT.js +0 -116
- package/dist/issues-L7TBUPXT.js.map +0 -1
- package/dist/open-KWOV2OFO.js.map +0 -1
- package/dist/plan-BRJBFJHF.js.map +0 -1
- package/dist/recap-GKJXMDXW.js.map +0 -1
- package/dist/run-QEUVZF7J.js.map +0 -1
- package/dist/shell-DAAVG4YN.js.map +0 -1
- package/dist/summary-ZKOA35PT.js.map +0 -1
- /package/dist/{BranchNamingService-K6XNWQ6C.js.map → BranchNamingService-ECJHBB67.js.map} +0 -0
- /package/dist/{ClaudeContextManager-X2Y72GRL.js.map → ClaudeContextManager-QXX6ZFST.js.map} +0 -0
- /package/dist/{ClaudeService-7P32TTES.js.map → ClaudeService-NJNK2SUH.js.map} +0 -0
- /package/dist/{GitHubService-O7T6CFAJ.js.map → GitHubService-MEHKHUQP.js.map} +0 -0
- /package/dist/{MetadataManager-W3C54UYT.js.map → IssueTrackerFactory-NG53YX5S.js.map} +0 -0
- /package/dist/{LoomLauncher-3I47SUPV.js.map → LoomLauncher-L64HHS3T.js.map} +0 -0
- /package/dist/{ProjectCapabilityDetector-N5L7T4IY.js.map → MetadataManager-5QZSTKNN.js.map} +0 -0
- /package/dist/{PromptTemplateManager-36YLQRHP.js.map → ProjectCapabilityDetector-5KSYUTBJ.js.map} +0 -0
- /package/dist/{SettingsManager-QR7V2IW2.js.map → PromptTemplateManager-DULSVRRE.js.map} +0 -0
- /package/dist/{claude-TP2QO3BU.js.map → SettingsManager-BQDQA3FK.js.map} +0 -0
- /package/dist/{build-IC4CJRMP.js.map → build-5GO3XW26.js.map} +0 -0
- /package/dist/{chunk-USSL2X4A.js.map → chunk-3D7WQM7I.js.map} +0 -0
- /package/dist/{chunk-2JPXGGP4.js.map → chunk-5MWV33NN.js.map} +0 -0
- /package/dist/{chunk-KAYXR544.js.map → chunk-J5S7DFYC.js.map} +0 -0
- /package/dist/{chunk-H6ST2TGP.js.map → chunk-MNHZB4Z2.js.map} +0 -0
- /package/dist/{chunk-TL72BGP6.js.map → chunk-MORRVYPT.js.map} +0 -0
- /package/dist/{chunk-7ZEHSSUP.js.map → chunk-P4O6EH46.js.map} +0 -0
- /package/dist/{chunk-RD7OPXZK.js.map → chunk-UVD4CZKS.js.map} +0 -0
- /package/dist/{chunk-JT5LZRMI.js.map → chunk-VECNX6VX.js.map} +0 -0
- /package/dist/{chunk-4GAJJUYS.js.map → chunk-VGGST52X.js.map} +0 -0
- /package/dist/{chunk-4LKGCFGG.js.map → chunk-WWKOVDWC.js.map} +0 -0
- /package/dist/{chunk-NTTSUAVM.js.map → chunk-ZEWU5PZK.js.map} +0 -0
- /package/dist/{git-W3XUIFTR.js.map → claude-P3NQR6IJ.js.map} +0 -0
- /package/dist/{compile-EOWJORKO.js.map → compile-ZS4HYRX5.js.map} +0 -0
- /package/dist/{feedback-QPNDZQRV.js.map → feedback-TMBXSCM5.js.map} +0 -0
- /package/dist/{neon-helpers-VVFFTLXE.js.map → git-ET64COO3.js.map} +0 -0
- /package/dist/{lint-IHUH45OC.js.map → lint-6TQXDZ3T.js.map} +0 -0
- /package/dist/{projects-LH362JZQ.js.map → projects-2UOXFLNZ.js.map} +0 -0
- /package/dist/{rebase-AJOJOZUG.js.map → rebase-DWIB77KV.js.map} +0 -0
- /package/dist/{test-5GPWWO3P.js.map → test-F7JNJZYP.js.map} +0 -0
- /package/dist/{test-git-EJUKDB7F.js.map → test-git-BTAOIUE2.js.map} +0 -0
- /package/dist/{test-prefix-23TOBUXY.js.map → test-prefix-Q6TFSU6F.js.map} +0 -0
- /package/dist/{test-webserver-CKROHFBQ.js.map → test-webserver-EONCG7E7.js.map} +0 -0
- /package/dist/{vscode-6TOLFCI2.js.map → vscode-VA5X4P25.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/LinearService.ts","../src/lib/IssueTrackerFactory.ts"],"sourcesContent":["/**\n * LinearService - IssueTracker implementation for Linear\n * Implements issue tracking operations using the @linear/sdk\n */\n\nimport type { Issue, PullRequest, IssueTrackerInputDetection } from '../types/index.js'\nimport type { LinearIssue } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport {\n fetchLinearIssue,\n createLinearIssue,\n updateLinearIssueState,\n getLinearChildIssues,\n} from '../utils/linear.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport type { IssueTracker } from './IssueTracker.js'\nimport { getLogger } from '../utils/logger-context.js'\n\n/**\n * Linear service configuration options\n */\nexport interface LinearServiceConfig {\n /** Linear team key (e.g., \"ENG\", \"PLAT\") */\n teamId?: string\n /** Branch naming template (e.g., \"feat/{{key}}__{{title}}\") */\n branchFormat?: string\n /** Linear API token (lin_api_...). If provided, sets process.env.LINEAR_API_TOKEN */\n apiToken?: string\n}\n\n/**\n * Linear implementation of IssueTracker interface\n */\nexport class LinearService implements IssueTracker {\n // IssueTracker interface implementation\n readonly providerName = 'linear'\n readonly supportsPullRequests = false // Linear doesn't have pull requests\n\n private config: LinearServiceConfig\n private prompter: (message: string) => Promise<boolean>\n\n constructor(\n config?: LinearServiceConfig,\n options?: { prompter?: (message: string) => Promise<boolean> },\n ) {\n this.config = config ?? {}\n this.prompter = options?.prompter ?? promptConfirmation\n\n // Set API token from config if provided (follows mcp.ts pattern)\n if (this.config.apiToken) {\n process.env.LINEAR_API_TOKEN = this.config.apiToken\n }\n }\n\n /**\n * Detect if input matches Linear identifier format (TEAM-NUMBER)\n * @param input - User input string\n * @param _repo - Repository (unused for Linear)\n * @returns Detection result with type and identifier\n */\n public async detectInputType(\n input: string,\n _repo?: string,\n ): Promise<IssueTrackerInputDetection> {\n getLogger().debug(`LinearService.detectInputType called with input: \"${input}\"`)\n\n // Pattern: TEAM-NUMBER (e.g., ENG-123, PLAT-456)\n // Requires at least 2 letters before dash to avoid conflict with PR-123 format\n const linearPattern = /^([A-Z]{2,}-\\d+)$/i\n const match = input.match(linearPattern)\n\n if (!match?.[1]) {\n getLogger().debug(`LinearService: Input \"${input}\" does not match Linear pattern`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n const identifier = match[1].toUpperCase()\n getLogger().debug(`LinearService: Matched Linear identifier: ${identifier}`)\n\n // Validate the issue exists in Linear\n getLogger().debug(`LinearService: Checking if ${identifier} is a valid Linear issue via SDK`)\n const issue = await this.isValidIssue(identifier)\n\n if (issue) {\n getLogger().debug(`LinearService: Issue ${identifier} found: \"${issue.title}\"`)\n return { type: 'issue', identifier, rawInput: input }\n }\n\n // Not found\n getLogger().debug(`LinearService: Issue ${identifier} NOT found by SDK`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n /**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (string or number)\n * @param _repo - Repository (unused for Linear)\n * @returns Generic Issue type\n * @throws LinearServiceError if issue not found\n */\n public async fetchIssue(identifier: string | number, _repo?: string): Promise<Issue> {\n const linearIssue = await fetchLinearIssue(String(identifier))\n return this.mapLinearIssueToIssue(linearIssue)\n }\n\n /**\n * Check if an issue identifier is valid (silent validation)\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue if valid, false if not found\n */\n public async isValidIssue(identifier: string | number, _repo?: string): Promise<Issue | false> {\n try {\n return await this.fetchIssue(identifier)\n } catch (error) {\n // Return false for NOT_FOUND errors (expected during detection)\n if (error instanceof LinearServiceError && error.code === 'NOT_FOUND') {\n return false\n }\n // Re-throw unexpected errors\n throw error\n }\n }\n\n /**\n * Validate issue state and prompt user if closed\n * @param issue - Issue to validate\n * @throws LinearServiceError if user cancels due to closed issue\n */\n public async validateIssueState(issue: Issue): Promise<void> {\n if (issue.state === 'closed') {\n const shouldContinue = await this.prompter(\n `Issue ${issue.number} is closed. Continue anyway?`,\n )\n\n if (!shouldContinue) {\n throw new LinearServiceError('INVALID_STATE', 'User cancelled due to closed issue')\n }\n }\n }\n\n /**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param _repository - Repository (unused for Linear)\n * @param labels - Optional label names\n * @returns Created issue identifier and URL\n * @throws LinearServiceError if teamId not configured or creation fails\n */\n public async createIssue(\n title: string,\n body: string,\n _repository?: string,\n labels?: string[],\n ): Promise<{ number: string | number; url: string }> {\n // Require teamId configuration\n if (!this.config.teamId) {\n throw new LinearServiceError(\n 'INVALID_STATE',\n 'Linear teamId not configured. Run `il init` to configure Linear settings.',\n )\n }\n\n getLogger().info(`Creating Linear issue in team ${this.config.teamId}: ${title}`)\n\n const result = await createLinearIssue(title, body, this.config.teamId, labels)\n\n return {\n number: result.identifier,\n url: result.url,\n }\n }\n\n /**\n * Get the web URL for a Linear issue\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue URL\n */\n public async getIssueUrl(identifier: string | number, _repo?: string): Promise<string> {\n const issue = await this.fetchIssue(identifier)\n return issue.url\n }\n\n /**\n * Fetch child issues of a Linear parent issue\n * @param parentIdentifier - Linear issue identifier (e.g., \"ENG-123\")\n * @param _repo - Repository (unused for Linear)\n * @returns Array of child issues\n */\n public async getChildIssues(parentIdentifier: string, _repo?: string): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n return getLinearChildIssues(parentIdentifier, this.config.apiToken ? { apiToken: this.config.apiToken } : undefined)\n }\n\n /**\n * Move a Linear issue to \"In Progress\" state\n * @param identifier - Linear issue identifier\n * @throws LinearServiceError if state update fails\n */\n public async moveIssueToInProgress(identifier: string | number): Promise<void> {\n getLogger().info(`Moving Linear issue ${identifier} to In Progress`)\n await updateLinearIssueState(String(identifier), 'In Progress')\n }\n\n /**\n * Move a Linear issue to \"In Review\" state\n * @param identifier - Linear issue identifier\n * @throws LinearServiceError if state update fails\n */\n public async moveIssueToReadyForReview(identifier: string | number): Promise<void> {\n getLogger().info(`Moving Linear issue ${identifier} to In Review`)\n await updateLinearIssueState(String(identifier), 'In Review')\n }\n\n /**\n * Normalize identifier to canonical form (uppercase for Linear keys)\n * @param identifier - Linear issue identifier (e.g., \"eng-123\" or \"ENG-123\")\n * @returns Uppercase identifier (e.g., \"ENG-123\")\n */\n public normalizeIdentifier(identifier: string | number): string {\n return String(identifier).toUpperCase()\n }\n\n /**\n * Extract issue context for AI prompts\n * @param entity - Issue (Linear doesn't have PRs)\n * @returns Formatted context string\n */\n public extractContext(entity: Issue | PullRequest): string {\n // Linear doesn't have PRs, always an issue\n const issue = entity as Issue\n return `Linear Issue ${issue.number}: ${issue.title}\\nState: ${issue.state}\\n\\n${issue.body}`\n }\n\n /**\n * Map Linear API issue to generic Issue type\n * @param linear - Linear issue from SDK\n * @returns Generic Issue type\n */\n private mapLinearIssueToIssue(linear: LinearIssue): Issue {\n return {\n number: linear.identifier, // Keep as string (e.g., \"ENG-123\")\n title: linear.title,\n body: linear.description ?? '',\n state: linear.state ? (linear.state.toLowerCase().includes('done') || linear.state.toLowerCase().includes('completed') || linear.state.toLowerCase().includes('canceled') ? 'closed' : 'open') : 'open',\n labels: [],\n assignees: [],\n url: linear.url,\n }\n }\n}\n","// IssueTrackerFactory - creates appropriate IssueTracker based on settings\n// Follows pattern from database provider instantiation\n\nimport type { IssueTracker } from './IssueTracker.js'\nimport { GitHubService } from './GitHubService.js'\nimport { LinearService, type LinearServiceConfig } from './LinearService.js'\nimport { JiraIssueTracker, type JiraTrackerConfig } from './providers/jira/index.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { getLogger } from '../utils/logger-context.js'\n\nexport type IssueTrackerProviderType = 'github' | 'linear' | 'jira'\n\n/**\n * Factory for creating IssueTracker instances based on settings\n * Provides a single point of provider instantiation\n *\n * Usage:\n * const tracker = IssueTrackerFactory.create(settings, { useClaude: true })\n * const issue = await tracker.fetchIssue(123)\n */\nexport class IssueTrackerFactory {\n\t/**\n\t * Create an IssueTracker instance based on settings configuration\n\t * Defaults to GitHub if no provider specified\n\t *\n\t * @param settings - iloom settings containing issueManagement.provider\n\t * @returns IssueTracker instance configured for the specified provider\n\t * @throws Error if provider type is not supported\n\t */\n\tstatic create(settings: IloomSettings): IssueTracker {\n\t\tconst provider = settings.issueManagement?.provider ?? 'github'\n\n\t\tgetLogger().debug(`IssueTrackerFactory: Creating tracker for provider \"${provider}\"`)\n\t\tgetLogger().debug(`IssueTrackerFactory: issueManagement settings:`, JSON.stringify(settings.issueManagement, null, 2))\n\n\t\tswitch (provider) {\n\t\t\tcase 'github':\n\t\t\t\tgetLogger().debug('IssueTrackerFactory: Creating GitHubService')\n\t\t\t\treturn new GitHubService()\n\t\t\tcase 'linear': {\n\t\t\t\tconst linearSettings = settings.issueManagement?.linear\n\t\t\t\tconst linearConfig: LinearServiceConfig = {}\n\n\t\t\t\tif (linearSettings?.teamId) {\n\t\t\t\t\tlinearConfig.teamId = linearSettings.teamId\n\t\t\t\t}\n\t\t\t\tif (linearSettings?.branchFormat) {\n\t\t\t\t\tlinearConfig.branchFormat = linearSettings.branchFormat\n\t\t\t\t}\n\t\t\t\tif (linearSettings?.apiToken) {\n\t\t\t\t\tlinearConfig.apiToken = linearSettings.apiToken\n\t\t\t\t}\n\n\t\t\t\tgetLogger().debug(`IssueTrackerFactory: Creating LinearService with config:`, JSON.stringify(linearConfig, null, 2))\n\t\t\t\treturn new LinearService(linearConfig)\n\t\t\t}\n\t\t\tcase 'jira': {\n\t\t\t\tconst jiraSettings = settings.issueManagement?.jira\n\t\t\t\t\n\t\t\t\tif (!jiraSettings?.host) {\n\t\t\t\t\tthrow new Error('Jira host is required. Configure issueManagement.jira.host in .iloom/settings.json')\n\t\t\t\t}\n\t\t\t\tif (!jiraSettings?.username) {\n\t\t\t\t\tthrow new Error('Jira username is required. Configure issueManagement.jira.username in .iloom/settings.json')\n\t\t\t\t}\n\t\t\t\tif (!jiraSettings?.apiToken) {\n\t\t\t\t\tthrow new Error('Jira API token is required. Configure issueManagement.jira.apiToken in .iloom/settings.local.json')\n\t\t\t\t}\n\t\t\t\tif (!jiraSettings?.projectKey) {\n\t\t\t\t\tthrow new Error('Jira project key is required. Configure issueManagement.jira.projectKey in .iloom/settings.json')\n\t\t\t\t}\n\n\t\t\t\tconst jiraConfig: JiraTrackerConfig = {\n\t\t\t\t\thost: jiraSettings.host,\n\t\t\t\t\tusername: jiraSettings.username,\n\t\t\t\t\tapiToken: jiraSettings.apiToken,\n\t\t\t\t\tprojectKey: jiraSettings.projectKey,\n\t\t\t\t}\n\n\t\t\t\tif (jiraSettings.transitionMappings) {\n\t\t\t\t\tjiraConfig.transitionMappings = jiraSettings.transitionMappings\n\t\t\t\t}\n\n\t\t\t\tgetLogger().debug(`IssueTrackerFactory: Creating JiraIssueTracker for host: ${jiraSettings.host}`)\n\t\t\t\treturn new JiraIssueTracker(jiraConfig)\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported issue tracker provider: ${provider}`)\n\t\t}\n\t}\n\n\t/**\n\t * Get the configured provider name from settings\n\t * Defaults to 'github' if not configured\n\t *\n\t * @param settings - iloom settings\n\t * @returns Provider type string\n\t */\n\tstatic getProviderName(settings: IloomSettings): IssueTrackerProviderType {\n\t\treturn (settings.issueManagement?.provider ?? 'github') as IssueTrackerProviderType\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-Q7POFB5Q.js";
|
|
5
5
|
import {
|
|
6
6
|
getRepoRoot
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MNHZB4Z2.js";
|
|
8
8
|
import {
|
|
9
9
|
promptConfirmation,
|
|
10
10
|
waitForKeypress
|
|
@@ -86,7 +86,7 @@ async function launchFirstRunSetup() {
|
|
|
86
86
|
logger.info("");
|
|
87
87
|
logger.info("iloom will now launch an interactive configuration session with Claude.");
|
|
88
88
|
await waitForKeypress("Press any key to start configuration...");
|
|
89
|
-
const { InitCommand } = await import("./init-
|
|
89
|
+
const { InitCommand } = await import("./init-GFQ5W7GK.js");
|
|
90
90
|
const initCommand = new InitCommand();
|
|
91
91
|
await initCommand.execute(
|
|
92
92
|
"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."
|
|
@@ -98,4 +98,4 @@ export {
|
|
|
98
98
|
needsFirstRunSetup,
|
|
99
99
|
launchFirstRunSetup
|
|
100
100
|
};
|
|
101
|
-
//# sourceMappingURL=chunk-
|
|
101
|
+
//# sourceMappingURL=chunk-UVD4CZKS.js.map
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getRepoInfo
|
|
4
|
+
} from "./chunk-VG45TUYK.js";
|
|
5
|
+
import {
|
|
6
|
+
logger
|
|
7
|
+
} from "./chunk-VT4PDUYT.js";
|
|
8
|
+
|
|
9
|
+
// src/utils/mcp.ts
|
|
10
|
+
import path from "path";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import fs from "fs-extra";
|
|
13
|
+
async function generateIssueManagementMcpConfig(contextType, repo, provider = "github", settings, draftPrNumber) {
|
|
14
|
+
var _a, _b, _c, _d, _e;
|
|
15
|
+
const effectiveContextType = draftPrNumber ? "pr" : contextType;
|
|
16
|
+
let envVars = {
|
|
17
|
+
ISSUE_PROVIDER: provider
|
|
18
|
+
};
|
|
19
|
+
if (draftPrNumber) {
|
|
20
|
+
envVars.DRAFT_PR_NUMBER = String(draftPrNumber);
|
|
21
|
+
}
|
|
22
|
+
if (provider === "github") {
|
|
23
|
+
let owner;
|
|
24
|
+
let name;
|
|
25
|
+
if (repo) {
|
|
26
|
+
const parts = repo.split("/");
|
|
27
|
+
if (parts.length !== 2 || !parts[0] || !parts[1]) {
|
|
28
|
+
throw new Error(`Invalid repo format: ${repo}. Expected "owner/repo"`);
|
|
29
|
+
}
|
|
30
|
+
owner = parts[0];
|
|
31
|
+
name = parts[1];
|
|
32
|
+
} else {
|
|
33
|
+
const repoInfo = await getRepoInfo();
|
|
34
|
+
owner = repoInfo.owner;
|
|
35
|
+
name = repoInfo.name;
|
|
36
|
+
}
|
|
37
|
+
const githubEventName = effectiveContextType === "issue" ? "issues" : effectiveContextType === "pr" ? "pull_request" : void 0;
|
|
38
|
+
envVars = {
|
|
39
|
+
...envVars,
|
|
40
|
+
REPO_OWNER: owner,
|
|
41
|
+
REPO_NAME: name,
|
|
42
|
+
GITHUB_API_URL: "https://api.github.com/",
|
|
43
|
+
...githubEventName && { GITHUB_EVENT_NAME: githubEventName }
|
|
44
|
+
};
|
|
45
|
+
logger.debug("Generated MCP config for GitHub issue management", {
|
|
46
|
+
provider,
|
|
47
|
+
repoOwner: owner,
|
|
48
|
+
repoName: name,
|
|
49
|
+
contextType: effectiveContextType ?? "auto-detect",
|
|
50
|
+
githubEventName: githubEventName ?? "auto-detect",
|
|
51
|
+
draftPrNumber: draftPrNumber ?? void 0
|
|
52
|
+
});
|
|
53
|
+
} else if (provider === "linear") {
|
|
54
|
+
const apiToken = ((_b = (_a = settings == null ? void 0 : settings.issueManagement) == null ? void 0 : _a.linear) == null ? void 0 : _b.apiToken) ?? process.env.LINEAR_API_TOKEN;
|
|
55
|
+
if (apiToken) {
|
|
56
|
+
envVars.LINEAR_API_TOKEN = apiToken;
|
|
57
|
+
}
|
|
58
|
+
const teamKey = ((_d = (_c = settings == null ? void 0 : settings.issueManagement) == null ? void 0 : _c.linear) == null ? void 0 : _d.teamId) ?? process.env.LINEAR_TEAM_KEY;
|
|
59
|
+
if (teamKey) {
|
|
60
|
+
envVars.LINEAR_TEAM_KEY = teamKey;
|
|
61
|
+
}
|
|
62
|
+
logger.debug("Generated MCP config for Linear issue management", {
|
|
63
|
+
provider,
|
|
64
|
+
hasApiToken: !!apiToken,
|
|
65
|
+
hasTeamKey: !!teamKey,
|
|
66
|
+
contextType: contextType ?? "auto-detect"
|
|
67
|
+
});
|
|
68
|
+
} else if (provider === "jira") {
|
|
69
|
+
const jiraSettings = (_e = settings == null ? void 0 : settings.issueManagement) == null ? void 0 : _e.jira;
|
|
70
|
+
if (jiraSettings == null ? void 0 : jiraSettings.host) {
|
|
71
|
+
envVars.JIRA_HOST = jiraSettings.host;
|
|
72
|
+
}
|
|
73
|
+
if (jiraSettings == null ? void 0 : jiraSettings.username) {
|
|
74
|
+
envVars.JIRA_USERNAME = jiraSettings.username;
|
|
75
|
+
}
|
|
76
|
+
if (jiraSettings == null ? void 0 : jiraSettings.apiToken) {
|
|
77
|
+
envVars.JIRA_API_TOKEN = jiraSettings.apiToken;
|
|
78
|
+
}
|
|
79
|
+
if (jiraSettings == null ? void 0 : jiraSettings.projectKey) {
|
|
80
|
+
envVars.JIRA_PROJECT_KEY = jiraSettings.projectKey;
|
|
81
|
+
}
|
|
82
|
+
if (jiraSettings == null ? void 0 : jiraSettings.transitionMappings) {
|
|
83
|
+
envVars.JIRA_TRANSITION_MAPPINGS = JSON.stringify(jiraSettings.transitionMappings);
|
|
84
|
+
}
|
|
85
|
+
if (jiraSettings == null ? void 0 : jiraSettings.defaultIssueType) {
|
|
86
|
+
envVars.JIRA_DEFAULT_ISSUE_TYPE = jiraSettings.defaultIssueType;
|
|
87
|
+
}
|
|
88
|
+
if (jiraSettings == null ? void 0 : jiraSettings.defaultSubtaskType) {
|
|
89
|
+
envVars.JIRA_DEFAULT_SUBTASK_TYPE = jiraSettings.defaultSubtaskType;
|
|
90
|
+
}
|
|
91
|
+
logger.debug("Generated MCP config for Jira issue management", {
|
|
92
|
+
provider,
|
|
93
|
+
hasApiToken: !!(jiraSettings == null ? void 0 : jiraSettings.apiToken),
|
|
94
|
+
projectKey: jiraSettings == null ? void 0 : jiraSettings.projectKey,
|
|
95
|
+
contextType: contextType ?? "auto-detect"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const serverJsPath = path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), "../dist/mcp/issue-management-server.js");
|
|
99
|
+
const resolvedServerJsPath = path.resolve(serverJsPath);
|
|
100
|
+
const mcpServerConfig = {
|
|
101
|
+
mcpServers: {
|
|
102
|
+
issue_management: {
|
|
103
|
+
transport: "stdio",
|
|
104
|
+
command: "node",
|
|
105
|
+
args: [resolvedServerJsPath],
|
|
106
|
+
env: envVars
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
return [mcpServerConfig];
|
|
111
|
+
}
|
|
112
|
+
function slugifyPath(loomPath) {
|
|
113
|
+
let slug = loomPath.replace(/[/\\]+$/, "");
|
|
114
|
+
slug = slug.replace(/[/\\]/g, "___");
|
|
115
|
+
slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
116
|
+
return `${slug}.json`;
|
|
117
|
+
}
|
|
118
|
+
function generateRecapMcpConfig(loomPath, loomMetadata) {
|
|
119
|
+
const recapsDir = path.join(os.homedir(), ".config", "iloom-ai", "recaps");
|
|
120
|
+
const recapFilePath = path.join(recapsDir, slugifyPath(loomPath));
|
|
121
|
+
const loomsDir = path.join(os.homedir(), ".config", "iloom-ai", "looms");
|
|
122
|
+
const metadataFilePath = path.join(loomsDir, slugifyPath(loomPath));
|
|
123
|
+
const envVars = {
|
|
124
|
+
RECAP_FILE_PATH: recapFilePath,
|
|
125
|
+
LOOM_METADATA_JSON: JSON.stringify(loomMetadata),
|
|
126
|
+
METADATA_FILE_PATH: metadataFilePath
|
|
127
|
+
};
|
|
128
|
+
logger.debug("Generated MCP config for recap server", {
|
|
129
|
+
loomPath,
|
|
130
|
+
recapFilePath,
|
|
131
|
+
loomMetadataDescription: loomMetadata.description
|
|
132
|
+
});
|
|
133
|
+
const recapServerJsPath = path.resolve(
|
|
134
|
+
path.join(
|
|
135
|
+
path.dirname(new globalThis.URL(import.meta.url).pathname),
|
|
136
|
+
"../dist/mcp/recap-server.js"
|
|
137
|
+
)
|
|
138
|
+
);
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
mcpServers: {
|
|
142
|
+
recap: {
|
|
143
|
+
transport: "stdio",
|
|
144
|
+
command: "node",
|
|
145
|
+
args: [recapServerJsPath],
|
|
146
|
+
env: envVars
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
function getMcpConfigsDir() {
|
|
153
|
+
return path.join(os.homedir(), ".config", "iloom-ai", "mcp-configs");
|
|
154
|
+
}
|
|
155
|
+
function getMcpConfigFilePath(loomPath) {
|
|
156
|
+
return path.join(getMcpConfigsDir(), slugifyPath(loomPath));
|
|
157
|
+
}
|
|
158
|
+
async function generateAndWriteMcpConfigFile(loomPath, loomMetadata, provider = "github", settings) {
|
|
159
|
+
var _a;
|
|
160
|
+
const mcpConfigs = [];
|
|
161
|
+
try {
|
|
162
|
+
const issueMcpConfigs = await generateIssueManagementMcpConfig(
|
|
163
|
+
"issue",
|
|
164
|
+
void 0,
|
|
165
|
+
provider,
|
|
166
|
+
settings
|
|
167
|
+
);
|
|
168
|
+
mcpConfigs.push(...issueMcpConfigs);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
logger.warn(`Failed to generate issue management MCP config for loom: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const recapMcpConfigs = generateRecapMcpConfig(loomPath, loomMetadata);
|
|
174
|
+
mcpConfigs.push(...recapMcpConfigs);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
logger.warn(`Failed to generate recap MCP config for loom: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
177
|
+
}
|
|
178
|
+
const mergedServers = {};
|
|
179
|
+
for (const config of mcpConfigs) {
|
|
180
|
+
if ("mcpServers" in config && typeof config.mcpServers === "object") {
|
|
181
|
+
Object.assign(mergedServers, config.mcpServers);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const mergedConfig = { mcpServers: mergedServers };
|
|
185
|
+
for (const [serverName, serverConfig] of Object.entries(mergedServers)) {
|
|
186
|
+
const config = serverConfig;
|
|
187
|
+
const jsPath = (_a = config.args) == null ? void 0 : _a[0];
|
|
188
|
+
if (jsPath) {
|
|
189
|
+
const exists = await fs.pathExists(jsPath);
|
|
190
|
+
if (!exists) {
|
|
191
|
+
logger.warn(`MCP server JS file not found: ${serverName} -> ${jsPath}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const configDir = getMcpConfigsDir();
|
|
196
|
+
await fs.ensureDir(configDir, { mode: 493 });
|
|
197
|
+
const configFilePath = getMcpConfigFilePath(loomPath);
|
|
198
|
+
await fs.writeFile(configFilePath, JSON.stringify(mergedConfig, null, 2), { mode: 420 });
|
|
199
|
+
return configFilePath;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export {
|
|
203
|
+
generateIssueManagementMcpConfig,
|
|
204
|
+
generateRecapMcpConfig,
|
|
205
|
+
generateAndWriteMcpConfigFile
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=chunk-UWGVCXRF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/mcp.ts"],"sourcesContent":["import path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getRepoInfo } from './github.js'\nimport { logger } from './logger.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport type { LoomMetadata } from '../lib/MetadataManager.js'\n\n/**\n * Generate MCP configuration for issue management\n * Uses a single server that can handle both issues and pull requests\n * Returns array of MCP server config objects\n * @param contextType - Optional context type (issue or pr)\n * @param repo - Optional repo in \"owner/repo\" format. If not provided, will auto-detect from git.\n * @param provider - Issue management provider (default: 'github')\n * @param settings - Optional settings to extract Linear API token from\n * @param draftPrNumber - Optional draft PR number for github-draft-pr mode (routes comments to PR)\n */\nexport async function generateIssueManagementMcpConfig(\n\tcontextType?: 'issue' | 'pr',\n\trepo?: string,\n\tprovider: 'github' | 'linear' | 'jira' = 'github',\n\tsettings?: IloomSettings,\n\tdraftPrNumber?: number\n): Promise<Record<string, unknown>[]> {\n\t// When draftPrNumber is provided (github-draft-pr mode), force contextType to 'pr'\n\t// This ensures agents route comments to the draft PR instead of the issue\n\tconst effectiveContextType = draftPrNumber ? 'pr' : contextType\n\n\t// Build provider-specific environment variables\n\tlet envVars: Record<string, string> = {\n\t\tISSUE_PROVIDER: provider,\n\t}\n\n\t// Add draft PR number to env vars if provided\n\tif (draftPrNumber) {\n\t\tenvVars.DRAFT_PR_NUMBER = String(draftPrNumber)\n\t}\n\n\tif (provider === 'github') {\n\t\t// Get repository information for GitHub - either from provided repo string or auto-detect\n\t\tlet owner: string\n\t\tlet name: string\n\n\t\tif (repo) {\n\t\t\tconst parts = repo.split('/')\n\t\t\tif (parts.length !== 2 || !parts[0] || !parts[1]) {\n\t\t\t\tthrow new Error(`Invalid repo format: ${repo}. Expected \"owner/repo\"`)\n\t\t\t}\n\t\t\towner = parts[0]\n\t\t\tname = parts[1]\n\t\t} else {\n\t\t\tconst repoInfo = await getRepoInfo()\n\t\t\towner = repoInfo.owner\n\t\t\tname = repoInfo.name\n\t\t}\n\n\t\t// Map logical types to GitHub's webhook event names (handle GitHub's naming quirk here)\n\t\t// Use effectiveContextType which may be overridden by draftPrNumber\n\t\tconst githubEventName = effectiveContextType === 'issue' ? 'issues' : effectiveContextType === 'pr' ? 'pull_request' : undefined\n\n\t\tenvVars = {\n\t\t\t...envVars,\n\t\t\tREPO_OWNER: owner,\n\t\t\tREPO_NAME: name,\n\t\t\tGITHUB_API_URL: 'https://api.github.com/',\n\t\t\t...(githubEventName && { GITHUB_EVENT_NAME: githubEventName }),\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for GitHub issue management', {\n\t\t\tprovider,\n\t\t\trepoOwner: owner,\n\t\t\trepoName: name,\n\t\t\tcontextType: effectiveContextType ?? 'auto-detect',\n\t\t\tgithubEventName: githubEventName ?? 'auto-detect',\n\t\t\tdraftPrNumber: draftPrNumber ?? undefined,\n\t\t})\n\t} else if (provider === 'linear') {\n\t\t// Linear needs API token passed through\n\t\tconst apiToken = settings?.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n\n\t\tif (apiToken) {\n\t\t\tenvVars.LINEAR_API_TOKEN = apiToken\n\t\t}\n\n\t\t// Pass through LINEAR_TEAM_KEY from settings (primary) or env var (fallback)\n\t\t// Settings teamId is the preferred source as it's configured via `il init`\n\t\tconst teamKey = settings?.issueManagement?.linear?.teamId ?? process.env.LINEAR_TEAM_KEY\n\t\tif (teamKey) {\n\t\t\tenvVars.LINEAR_TEAM_KEY = teamKey\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for Linear issue management', {\n\t\t\tprovider,\n\t\t\thasApiToken: !!apiToken,\n\t\t\thasTeamKey: !!teamKey,\n\t\t\tcontextType: contextType ?? 'auto-detect',\n\t\t})\n\t} else if (provider === 'jira') {\n\t\t// Jira configuration - pass credentials via environment variables\n\t\tconst jiraSettings = settings?.issueManagement?.jira\n\n\t\tif (jiraSettings?.host) {\n\t\t\tenvVars.JIRA_HOST = jiraSettings.host\n\t\t}\n\t\tif (jiraSettings?.username) {\n\t\t\tenvVars.JIRA_USERNAME = jiraSettings.username\n\t\t}\n\t\tif (jiraSettings?.apiToken) {\n\t\t\tenvVars.JIRA_API_TOKEN = jiraSettings.apiToken\n\t\t}\n\t\tif (jiraSettings?.projectKey) {\n\t\t\tenvVars.JIRA_PROJECT_KEY = jiraSettings.projectKey\n\t\t}\n\t\tif (jiraSettings?.transitionMappings) {\n\t\t\tenvVars.JIRA_TRANSITION_MAPPINGS = JSON.stringify(jiraSettings.transitionMappings)\n\t\t}\n\t\tif (jiraSettings?.defaultIssueType) {\n\t\t\tenvVars.JIRA_DEFAULT_ISSUE_TYPE = jiraSettings.defaultIssueType\n\t\t}\n\t\tif (jiraSettings?.defaultSubtaskType) {\n\t\t\tenvVars.JIRA_DEFAULT_SUBTASK_TYPE = jiraSettings.defaultSubtaskType\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for Jira issue management', {\n\t\t\tprovider,\n\t\t\thasApiToken: !!jiraSettings?.apiToken,\n\t\t\tprojectKey: jiraSettings?.projectKey,\n\t\t\tcontextType: contextType ?? 'auto-detect',\n\t\t})\n\t}\n\n\t// Compute absolute path to the MCP server JS file\n\tconst serverJsPath = path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), '../dist/mcp/issue-management-server.js')\n\tconst resolvedServerJsPath = path.resolve(serverJsPath)\n\n\t// Generate single MCP server config\n\tconst mcpServerConfig = {\n\t\tmcpServers: {\n\t\t\tissue_management: {\n\t\t\t\ttransport: 'stdio',\n\t\t\t\tcommand: 'node',\n\t\t\t\targs: [resolvedServerJsPath],\n\t\t\t\tenv: envVars,\n\t\t\t},\n\t\t},\n\t}\n\n\treturn [mcpServerConfig]\n}\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm for recap file naming\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nexport function slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\n/**\n * Generate MCP configuration for recap server\n *\n * The recap server captures session context (goal, decisions, insights, risks, assumptions)\n * for the VS Code Loom Context Panel.\n *\n * @param loomPath - Absolute path to the loom workspace\n * @param loomMetadata - The loom metadata object (will be stringified as JSON)\n */\nexport function generateRecapMcpConfig(\n\tloomPath: string,\n\tloomMetadata: LoomMetadata\n): Record<string, unknown>[] {\n\t// Compute recap file path using slugifyPath algorithm (same as MetadataManager)\n\tconst recapsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\tconst recapFilePath = path.join(recapsDir, slugifyPath(loomPath))\n\n\t// Compute metadata file path (same directory/naming as MetadataManager)\n\tconst loomsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'looms')\n\tconst metadataFilePath = path.join(loomsDir, slugifyPath(loomPath))\n\n\t// Pass env vars:\n\t// - RECAP_FILE_PATH: where to read/write recap data\n\t// - LOOM_METADATA_JSON: stringified loom metadata (parsed by MCP using LoomMetadata type)\n\t// - METADATA_FILE_PATH: path to loom metadata file (for state transition tools)\n\tconst envVars = {\n\t\tRECAP_FILE_PATH: recapFilePath,\n\t\tLOOM_METADATA_JSON: JSON.stringify(loomMetadata),\n\t\tMETADATA_FILE_PATH: metadataFilePath,\n\t}\n\n\tlogger.debug('Generated MCP config for recap server', {\n\t\tloomPath,\n\t\trecapFilePath,\n\t\tloomMetadataDescription: loomMetadata.description,\n\t})\n\n\t// Compute absolute path to the recap MCP server JS file\n\tconst recapServerJsPath = path.resolve(\n\t\tpath.join(\n\t\t\tpath.dirname(new globalThis.URL(import.meta.url).pathname),\n\t\t\t'../dist/mcp/recap-server.js'\n\t\t)\n\t)\n\n\treturn [\n\t\t{\n\t\t\tmcpServers: {\n\t\t\t\trecap: {\n\t\t\t\t\ttransport: 'stdio',\n\t\t\t\t\tcommand: 'node',\n\t\t\t\t\targs: [recapServerJsPath],\n\t\t\t\t\tenv: envVars,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t]\n}\n\n/**\n * Get the MCP configs directory path\n */\nexport function getMcpConfigsDir(): string {\n\treturn path.join(os.homedir(), '.config', 'iloom-ai', 'mcp-configs')\n}\n\n/**\n * Get the MCP config file path for a given loom path\n */\nexport function getMcpConfigFilePath(loomPath: string): string {\n\treturn path.join(getMcpConfigsDir(), slugifyPath(loomPath))\n}\n\n/**\n * Generate and write a per-loom MCP config file to ~/.config/iloom-ai/mcp-configs/<loom-slug>.json\n *\n * Merges issue management and recap MCP server configs into a single file.\n * Used by swarm workers to pass --mcp-config <path> to claude -p commands.\n *\n * @param loomPath - Absolute path to the loom workspace\n * @param loomMetadata - The loom metadata object\n * @param provider - Issue tracker provider name\n * @param settings - Optional settings for provider configuration\n * @returns The absolute path to the written MCP config file\n */\nexport async function generateAndWriteMcpConfigFile(\n\tloomPath: string,\n\tloomMetadata: LoomMetadata,\n\tprovider: 'github' | 'linear' | 'jira' = 'github',\n\tsettings?: IloomSettings,\n): Promise<string> {\n\tconst mcpConfigs: Record<string, unknown>[] = []\n\n\t// Generate issue management MCP config\n\ttry {\n\t\tconst issueMcpConfigs = await generateIssueManagementMcpConfig(\n\t\t\t'issue',\n\t\t\tundefined,\n\t\t\tprovider,\n\t\t\tsettings,\n\t\t)\n\t\tmcpConfigs.push(...issueMcpConfigs)\n\t} catch (error) {\n\t\tlogger.warn(`Failed to generate issue management MCP config for loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t}\n\n\t// Generate recap MCP config\n\ttry {\n\t\tconst recapMcpConfigs = generateRecapMcpConfig(loomPath, loomMetadata)\n\t\tmcpConfigs.push(...recapMcpConfigs)\n\t} catch (error) {\n\t\tlogger.warn(`Failed to generate recap MCP config for loom: ${error instanceof Error ? error.message : 'Unknown error'}`)\n\t}\n\n\t// Merge all mcpServers into a single config object\n\tconst mergedServers: Record<string, unknown> = {}\n\tfor (const config of mcpConfigs) {\n\t\tif ('mcpServers' in config && typeof config.mcpServers === 'object') {\n\t\t\tObject.assign(mergedServers, config.mcpServers)\n\t\t}\n\t}\n\n\tconst mergedConfig = { mcpServers: mergedServers }\n\n\t// Verify MCP server JS files exist before writing config\n\tfor (const [serverName, serverConfig] of Object.entries(mergedServers)) {\n\t\tconst config = serverConfig as { args?: string[] }\n\t\tconst jsPath = config.args?.[0]\n\t\tif (jsPath) {\n\t\t\tconst exists = await fs.pathExists(jsPath)\n\t\t\tif (!exists) {\n\t\t\t\tlogger.warn(`MCP server JS file not found: ${serverName} -> ${jsPath}`)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Write to file\n\tconst configDir = getMcpConfigsDir()\n\tawait fs.ensureDir(configDir, { mode: 0o755 })\n\n\tconst configFilePath = getMcpConfigFilePath(loomPath)\n\tawait fs.writeFile(configFilePath, JSON.stringify(mergedConfig, null, 2), { mode: 0o644 })\n\n\treturn configFilePath\n}"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
GitWorktreeManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-VGGST52X.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-VECNX6VX.js.map
|
|
@@ -86,7 +86,7 @@ async function fetchGhPR(prNumber, repo) {
|
|
|
86
86
|
"view",
|
|
87
87
|
String(prNumber),
|
|
88
88
|
"--json",
|
|
89
|
-
"number,title,body,state,headRefName,baseRefName,url,isDraft,mergeable,createdAt,updatedAt"
|
|
89
|
+
"number,title,body,state,headRefName,baseRefName,url,isDraft,isCrossRepository,mergeable,createdAt,updatedAt"
|
|
90
90
|
];
|
|
91
91
|
if (repo) {
|
|
92
92
|
args.push("--repo", repo);
|
|
@@ -409,10 +409,45 @@ async function removeIssueDependency(blockedIssueNumber, blockingIssueDatabaseId
|
|
|
409
409
|
apiPath
|
|
410
410
|
]);
|
|
411
411
|
}
|
|
412
|
+
async function closeGhIssue(issueNumber, repo) {
|
|
413
|
+
logger.debug("Closing GitHub issue", { issueNumber, repo });
|
|
414
|
+
const args = ["issue", "close", String(issueNumber)];
|
|
415
|
+
if (repo) {
|
|
416
|
+
args.push("--repo", repo);
|
|
417
|
+
}
|
|
418
|
+
await executeGhCommand(args);
|
|
419
|
+
}
|
|
420
|
+
async function reopenGhIssue(issueNumber, repo) {
|
|
421
|
+
logger.debug("Reopening GitHub issue", { issueNumber, repo });
|
|
422
|
+
const args = ["issue", "reopen", String(issueNumber)];
|
|
423
|
+
if (repo) {
|
|
424
|
+
args.push("--repo", repo);
|
|
425
|
+
}
|
|
426
|
+
await executeGhCommand(args);
|
|
427
|
+
}
|
|
428
|
+
async function editGhIssue(issueNumber, options, repo) {
|
|
429
|
+
logger.debug("Editing GitHub issue", { issueNumber, options, repo });
|
|
430
|
+
const args = ["issue", "edit", String(issueNumber)];
|
|
431
|
+
if (options.title !== void 0) {
|
|
432
|
+
args.push("--title", options.title);
|
|
433
|
+
}
|
|
434
|
+
if (options.body !== void 0) {
|
|
435
|
+
args.push("--body", options.body);
|
|
436
|
+
}
|
|
437
|
+
if (options.labels) {
|
|
438
|
+
if (options.labels.length > 0) {
|
|
439
|
+
args.push("--add-label", options.labels.join(","));
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
if (repo) {
|
|
443
|
+
args.push("--repo", repo);
|
|
444
|
+
}
|
|
445
|
+
await executeGhCommand(args);
|
|
446
|
+
}
|
|
412
447
|
async function fetchGitHubIssueList(options) {
|
|
413
448
|
const limit = (options == null ? void 0 : options.limit) ?? 100;
|
|
414
|
-
logger.debug("Fetching GitHub issue list", { limit, cwd: options == null ? void 0 : options.cwd });
|
|
415
|
-
const
|
|
449
|
+
logger.debug("Fetching GitHub issue list", { limit, cwd: options == null ? void 0 : options.cwd, mine: options == null ? void 0 : options.mine });
|
|
450
|
+
const args = [
|
|
416
451
|
"issue",
|
|
417
452
|
"list",
|
|
418
453
|
"--state",
|
|
@@ -423,7 +458,11 @@ async function fetchGitHubIssueList(options) {
|
|
|
423
458
|
String(limit),
|
|
424
459
|
"--search",
|
|
425
460
|
"sort:updated-desc"
|
|
426
|
-
]
|
|
461
|
+
];
|
|
462
|
+
if (options == null ? void 0 : options.mine) {
|
|
463
|
+
args.push("--assignee", "@me");
|
|
464
|
+
}
|
|
465
|
+
const result = await executeGhCommand(args, (options == null ? void 0 : options.cwd) ? { cwd: options.cwd } : void 0);
|
|
427
466
|
return (result ?? []).map((item) => ({
|
|
428
467
|
id: String(item.number),
|
|
429
468
|
title: item.title,
|
|
@@ -432,6 +471,32 @@ async function fetchGitHubIssueList(options) {
|
|
|
432
471
|
state: item.state.toLowerCase()
|
|
433
472
|
}));
|
|
434
473
|
}
|
|
474
|
+
async function fetchGitHubPRList(options) {
|
|
475
|
+
const limit = (options == null ? void 0 : options.limit) ?? 100;
|
|
476
|
+
const fetchLimit = Math.max(limit * 2, 50);
|
|
477
|
+
logger.debug("Fetching GitHub PR list", { limit, fetchLimit, cwd: options == null ? void 0 : options.cwd, mine: options == null ? void 0 : options.mine });
|
|
478
|
+
const args = [
|
|
479
|
+
"pr",
|
|
480
|
+
"list",
|
|
481
|
+
"--state",
|
|
482
|
+
"open",
|
|
483
|
+
"--json",
|
|
484
|
+
"number,title,updatedAt,url,state,isDraft",
|
|
485
|
+
"--limit",
|
|
486
|
+
String(fetchLimit)
|
|
487
|
+
];
|
|
488
|
+
if (options == null ? void 0 : options.mine) {
|
|
489
|
+
args.push("--assignee", "@me");
|
|
490
|
+
}
|
|
491
|
+
const result = await executeGhCommand(args, (options == null ? void 0 : options.cwd) ? { cwd: options.cwd } : void 0);
|
|
492
|
+
return (result ?? []).filter((item) => !item.isDraft).slice(0, limit).map((item) => ({
|
|
493
|
+
id: String(item.number),
|
|
494
|
+
title: `[PR] ${item.title}`,
|
|
495
|
+
updatedAt: item.updatedAt,
|
|
496
|
+
url: item.url,
|
|
497
|
+
state: item.state.toLowerCase()
|
|
498
|
+
}));
|
|
499
|
+
}
|
|
435
500
|
|
|
436
501
|
export {
|
|
437
502
|
executeGhCommand,
|
|
@@ -455,6 +520,10 @@ export {
|
|
|
455
520
|
getIssueDependencies,
|
|
456
521
|
createIssueDependency,
|
|
457
522
|
removeIssueDependency,
|
|
458
|
-
|
|
523
|
+
closeGhIssue,
|
|
524
|
+
reopenGhIssue,
|
|
525
|
+
editGhIssue,
|
|
526
|
+
fetchGitHubIssueList,
|
|
527
|
+
fetchGitHubPRList
|
|
459
528
|
};
|
|
460
|
-
//# sourceMappingURL=chunk-
|
|
529
|
+
//# sourceMappingURL=chunk-VG45TUYK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/github.ts"],"sourcesContent":["import { execa } from 'execa'\nimport type {\n\tGitHubIssue,\n\tGitHubPullRequest,\n\tGitHubProject,\n\tGitHubAuthStatus,\n\tProjectItem,\n\tProjectField,\n} from '../types/github.js'\nimport { logger } from './logger.js'\n\n// Core GitHub CLI execution wrapper\nexport async function executeGhCommand<T = unknown>(\n\targs: string[],\n\toptions?: { cwd?: string; timeout?: number }\n): Promise<T> {\n\tconst result = await execa('gh', args, {\n\t\tcwd: options?.cwd ?? process.cwd(),\n\t\ttimeout: options?.timeout ?? 30000,\n\t\tencoding: 'utf8',\n\t})\n\n\t// Parse JSON output if --json flag, --format json, --jq, or GraphQL was used\n\tconst isJson =\n\t\targs.includes('--json') ||\n\t\targs.includes('--jq') ||\n\t\t(args.includes('--format') && args[args.indexOf('--format') + 1] === 'json') ||\n\t\t(args[0] === 'api' && args[1] === 'graphql')\n\tconst data = isJson ? JSON.parse(result.stdout) : result.stdout\n\n\treturn data as T\n}\n\n// Authentication checking\nexport async function checkGhAuth(): Promise<GitHubAuthStatus> {\n\ttry {\n\t\tconst output = await executeGhCommand<string>(['auth', 'status'])\n\n\t\t// Parse auth status output - handle both old and new formats\n\t\t// Old format: \"Logged in to github.com as username\"\n\t\t// New format: \"✓ Logged in to github.com account username (keyring)\"\n\n\t\t// Split output into lines to find the active account\n\t\tconst lines = output.split('\\n')\n\t\tlet username: string | undefined\n\t\tlet scopes: string[] = []\n\n\t\t// Find the active account (look for \"Active account: true\" or first account if none marked)\n\t\tfor (let i = 0; i < lines.length; i++) {\n\t\t\tconst line = lines[i]\n\n\t\t\t// Match new format: \"✓ Logged in to github.com account username\"\n\t\t\tconst newFormatMatch = line?.match(/Logged in to github\\.com account ([^\\s(]+)/)\n\t\t\tif (newFormatMatch) {\n\t\t\t\tconst accountName = newFormatMatch[1]\n\n\t\t\t\t// Check if this is the active account\n\t\t\t\tconst nextFewLines = lines.slice(i + 1, i + 5).join('\\n')\n\t\t\t\tconst isActive = nextFewLines.includes('Active account: true')\n\n\t\t\t\t// If this is the active account, or we haven't found one yet and there's no \"Active account\" marker\n\t\t\t\tif (isActive || (!username && !output.includes('Active account:'))) {\n\t\t\t\t\tusername = accountName\n\n\t\t\t\t\t// Find scopes for this account\n\t\t\t\t\tconst scopeMatch = nextFewLines.match(/Token scopes: (.+)/)\n\t\t\t\t\tif (scopeMatch?.[1]) {\n\t\t\t\t\t\tscopes = scopeMatch[1].split(', ').map(scope => scope.replace(/^'|'$/g, ''))\n\t\t\t\t\t}\n\n\t\t\t\t\t// If this is the active account, we're done\n\t\t\t\t\tif (isActive) break\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fallback: match old format\n\t\t\tif (!username) {\n\t\t\t\tconst oldFormatMatch = line?.match(/Logged in to github\\.com as ([^\\s]+)/)\n\t\t\t\tif (oldFormatMatch) {\n\t\t\t\t\tusername = oldFormatMatch[1]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// If scopes not yet extracted, try the old \"Token scopes\" format\n\t\tif (scopes.length === 0) {\n\t\t\tconst scopeMatch = output.match(/Token scopes: (.+)/)\n\t\t\tscopes = scopeMatch?.[1]?.split(', ').map(scope => scope.replace(/^'|'$/g, '')) ?? []\n\t\t}\n\n\t\treturn {\n\t\t\thasAuth: true,\n\t\t\tscopes,\n\t\t\t...(username && { username }),\n\t\t}\n\t} catch (error) {\n\t\t// Only return \"no auth\" for specific authentication errors\n\t\tif (error instanceof Error && 'stderr' in error && (error as {stderr?: string}).stderr?.includes('You are not logged into any GitHub hosts')) {\n\t\t\treturn { hasAuth: false, scopes: [] }\n\t\t}\n\t\t// Re-throw unexpected errors\n\t\tthrow error\n\t}\n}\n\nexport async function hasProjectScope(): Promise<boolean> {\n\tconst auth = await checkGhAuth()\n\treturn auth.scopes.includes('project')\n}\n\n// Issue fetching\nexport async function fetchGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<GitHubIssue> {\n\tlogger.debug('Fetching GitHub issue', { issueNumber, repo })\n\n\tconst args = [\n\t\t'issue',\n\t\t'view',\n\t\tString(issueNumber),\n\t\t'--json',\n\t\t'number,title,body,state,labels,assignees,url,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubIssue>(args)\n}\n\n// PR fetching\nexport async function fetchGhPR(\n\tprNumber: number,\n\trepo?: string\n): Promise<GitHubPullRequest> {\n\tlogger.debug('Fetching GitHub PR', { prNumber, repo })\n\n\tconst args = [\n\t\t'pr',\n\t\t'view',\n\t\tString(prNumber),\n\t\t'--json',\n\t\t'number,title,body,state,headRefName,baseRefName,url,isDraft,isCrossRepository,mergeable,createdAt,updatedAt',\n\t]\n\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\treturn executeGhCommand<GitHubPullRequest>(args)\n}\n\n// Project operations\nexport async function fetchProjectList(\n\towner: string\n): Promise<GitHubProject[]> {\n\tconst result = await executeGhCommand<{ projects: GitHubProject[] }>([\n\t\t'project',\n\t\t'list',\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'100',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.projects ?? []\n}\n\nexport async function fetchProjectItems(\n\tprojectNumber: number,\n\towner: string\n): Promise<ProjectItem[]> {\n\tconst result = await executeGhCommand<{ items: ProjectItem[] }>([\n\t\t'project',\n\t\t'item-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--limit',\n\t\t'10000',\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result?.items ?? []\n}\n\nexport async function fetchProjectFields(\n\tprojectNumber: number,\n\towner: string\n): Promise<{ fields: ProjectField[] }> {\n\tconst result = await executeGhCommand<{ fields: ProjectField[] }>([\n\t\t'project',\n\t\t'field-list',\n\t\tString(projectNumber),\n\t\t'--owner',\n\t\towner,\n\t\t'--format',\n\t\t'json',\n\t])\n\n\treturn result ?? { fields: [] }\n}\n\nexport async function updateProjectItemField(\n\titemId: string,\n\tprojectId: string,\n\tfieldId: string,\n\toptionId: string\n): Promise<void> {\n\tawait executeGhCommand([\n\t\t'project',\n\t\t'item-edit',\n\t\t'--id',\n\t\titemId,\n\t\t'--project-id',\n\t\tprojectId,\n\t\t'--field-id',\n\t\tfieldId,\n\t\t'--single-select-option-id',\n\t\toptionId,\n\t\t'--format',\n\t\t'json',\n\t])\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: string | number\n\turl: string\n}\n\n/**\n * Create a new GitHub issue\n * @param title - The issue title\n * @param body - The issue body (markdown supported)\n * @param options - Optional configuration\n * @param options.repo - Repository in format \"owner/repo\" (uses current repo if not provided)\n * @param options.labels - Array of label names to add to the issue\n * @returns Issue metadata including number and URL\n */\nexport async function createIssue(\n\ttitle: string,\n\tbody: string,\n\toptions?: { repo?: string | undefined; labels?: string[] | undefined }\n): Promise<IssueCreateResponse> {\n\tconst { repo, labels } = options ?? {}\n\n\tlogger.debug('Creating GitHub issue', { title, repo, labels })\n\n\tconst args = [\n\t\t'issue',\n\t\t'create',\n\t\t'--title',\n\t\ttitle,\n\t\t'--body',\n\t\tbody,\n\t]\n\n\t// Add repo if provided\n\tif (repo) {\n\t\targs.splice(2, 0, '--repo', repo)\n\t}\n\n\t// Add labels if provided\n\tif (labels && labels.length > 0) {\n\t\targs.push('--label', labels.join(','))\n\t}\n\n\tconst execaOptions: { timeout: number; encoding: 'utf8'; cwd?: string } = {\n\t\ttimeout: 30000,\n\t\tencoding: 'utf8',\n\t}\n\n\tif (!repo) {\n\t\texecaOptions.cwd = process.cwd()\n\t}\n\n\tconst result = await execa('gh', args, execaOptions)\n\n\t// Parse the URL from the output (format: \"https://github.com/owner/repo/issues/123\")\n\tconst urlMatch = result.stdout.trim().match(/https:\\/\\/github\\.com\\/[^/]+\\/[^/]+\\/issues\\/(\\d+)/)\n\tif (!urlMatch?.[1]) {\n\t\tthrow new Error(`Failed to parse issue URL from gh output: ${result.stdout}`)\n\t}\n\n\tconst issueNumber = parseInt(urlMatch[1], 10)\n\tconst issueUrl = urlMatch[0]\n\n\treturn {\n\t\tnumber: issueNumber,\n\t\turl: issueUrl,\n\t}\n}\n\n/**\n * @deprecated Use createIssue with options.repo instead\n * Create a new GitHub issue in a specific repository\n * @param title - Issue title\n * @param body - Issue body (markdown)\n * @param repository - Repository in format \"owner/repo\"\n * @param labels - Optional array of label names to add to the issue\n * @returns Issue number and URL\n */\nexport async function createIssueInRepo(\n\ttitle: string,\n\tbody: string,\n\trepository: string,\n\tlabels?: string[]\n): Promise<IssueCreateResponse> {\n\treturn createIssue(title, body, { repo: repository, labels })\n}\n\n// GitHub Comment Operations\n\ninterface CommentResponse {\n\tid: number\n\turl: string\n\tcreated_at?: string\n\tupdated_at?: string\n}\n\ninterface RepoInfo {\n\towner: string\n\tname: string\n}\n\n/**\n * Create a comment on a GitHub issue\n * @param issueNumber - The issue number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createIssueComment(\n\tissueNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating issue comment', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/comments`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Update an existing GitHub comment\n * @param commentId - The comment ID\n * @param body - The updated comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Updated comment metadata\n */\nexport async function updateIssueComment(\n\tcommentId: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Updating issue comment', { commentId, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/comments/${commentId}`\n\t\t: `repos/:owner/:repo/issues/comments/${commentId}`\n\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-X',\n\t\t'PATCH',\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, updated_at: .updated_at}',\n\t])\n}\n\n/**\n * Create a comment on a GitHub pull request\n * Note: PR comments use the same endpoint as issue comments\n * @param prNumber - The PR number\n * @param body - The comment body (markdown supported)\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Comment metadata including ID and URL\n */\nexport async function createPRComment(\n\tprNumber: number,\n\tbody: string,\n\trepo?: string\n): Promise<CommentResponse> {\n\tlogger.debug('Creating PR comment', { prNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${prNumber}/comments`\n\t\t: `repos/:owner/:repo/issues/${prNumber}/comments`\n\n\t// PR comments use the issues endpoint\n\treturn executeGhCommand<CommentResponse>([\n\t\t'api',\n\t\tapiPath,\n\t\t'-f',\n\t\t`body=${body}`,\n\t\t'--jq',\n\t\t'{id: .id, url: .html_url, created_at: .created_at}',\n\t])\n}\n\n/**\n * Get repository owner and name from current directory\n * @returns Repository owner and name\n */\nexport async function getRepoInfo(): Promise<RepoInfo> {\n\tlogger.debug('Fetching repository info')\n\n\tconst result = await executeGhCommand<{ owner: { login: string }; name: string }>([\n\t\t'repo',\n\t\t'view',\n\t\t'--json',\n\t\t'owner,name',\n\t])\n\n\treturn {\n\t\towner: result.owner.login,\n\t\tname: result.name,\n\t}\n}\n\n// GitHub Sub-Issue Operations\n\n/**\n * Get the GraphQL node ID for a GitHub issue\n * Required for sub-issue API which uses node IDs, not issue numbers\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns GraphQL node ID (e.g., \"I_kwDOPvp_cc7...\")\n */\nexport async function getIssueNodeId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<string> {\n\tlogger.debug('Fetching GitHub issue node ID', { issueNumber, repo })\n\n\tconst args = ['issue', 'view', String(issueNumber), '--json', 'id']\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tconst result = await executeGhCommand<{ id: string }>(args)\n\treturn result.id\n}\n\n/**\n * Link a child issue to a parent issue using GitHub's sub-issue API\n * Requires GraphQL-Features: sub_issues header\n * @param parentNodeId - GraphQL node ID of the parent issue\n * @param childNodeId - GraphQL node ID of the child issue\n */\nexport async function addSubIssue(\n\tparentNodeId: string,\n\tchildNodeId: string\n): Promise<void> {\n\tlogger.debug('Linking child issue to parent', { parentNodeId, childNodeId })\n\n\tconst mutation = `\n\t\tmutation addSubIssue($parentId: ID!, $subIssueId: ID!) {\n\t\t\taddSubIssue(input: { issueId: $parentId, subIssueId: $subIssueId }) {\n\t\t\t\tissue { id }\n\t\t\t\tsubIssue { id }\n\t\t\t}\n\t\t}\n\t`\n\n\tawait executeGhCommand([\n\t\t'api', 'graphql',\n\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t'-f', `query=${mutation}`,\n\t\t'-F', `parentId=${parentNodeId}`,\n\t\t'-F', `subIssueId=${childNodeId}`,\n\t])\n}\n\n/**\n * Get sub-issues (children) of a parent GitHub issue\n * Uses GraphQL to query the sub-issue relationship\n * @param issueNumber - The parent issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of child issues with id, title, url, and state\n */\nexport async function getSubIssues(\n\tissueNumber: number,\n\trepo?: string\n): Promise<Array<{ id: string; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub sub-issues', { issueNumber, repo })\n\n\t// Get the node ID for the parent issue\n\tconst parentNodeId = await getIssueNodeId(issueNumber, repo)\n\n\t// Query sub-issues using GraphQL\n\tconst query = `\n\t\tquery getSubIssues($parentId: ID!) {\n\t\t\tnode(id: $parentId) {\n\t\t\t\t... on Issue {\n\t\t\t\t\tsubIssues(first: 100) {\n\t\t\t\t\t\tnodes {\n\t\t\t\t\t\t\tnumber\n\t\t\t\t\t\t\ttitle\n\t\t\t\t\t\t\turl\n\t\t\t\t\t\t\tstate\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t`\n\n\tinterface SubIssueNode {\n\t\tnumber: number\n\t\ttitle: string\n\t\turl: string\n\t\tstate: string\n\t}\n\n\tinterface SubIssuesResponse {\n\t\tdata: {\n\t\t\tnode: {\n\t\t\t\tsubIssues: {\n\t\t\t\t\tnodes: SubIssueNode[]\n\t\t\t\t}\n\t\t\t} | null\n\t\t}\n\t}\n\n\ttry {\n\t\tconst result = await executeGhCommand<SubIssuesResponse>([\n\t\t\t'api', 'graphql',\n\t\t\t'-H', 'GraphQL-Features: sub_issues',\n\t\t\t'-f', `query=${query}`,\n\t\t\t'-F', `parentId=${parentNodeId}`,\n\t\t])\n\n\t\tconst subIssues = result.data.node?.subIssues?.nodes ?? []\n\n\t\treturn subIssues.map(issue => ({\n\t\t\tid: String(issue.number),\n\t\t\ttitle: issue.title,\n\t\t\turl: issue.url,\n\t\t\tstate: issue.state.toLowerCase(),\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array if sub-issues feature is not available or no children\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for feature not available or empty result\n\t\t\tif (combinedError.includes('sub_issues') || combinedError.includes('null')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n// GitHub Issue Dependency Operations\n\n/**\n * GitHub dependency result from API\n */\ninterface GitHubDependency {\n\tid: number\n\tnumber: number\n\ttitle: string\n\tstate: string\n\thtml_url: string\n}\n\n/**\n * Get the internal database ID for a GitHub issue\n * Required for dependency API which uses database IDs, not node IDs\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Internal GitHub issue database ID\n */\nexport async function getIssueDatabaseId(\n\tissueNumber: number,\n\trepo?: string\n): Promise<number> {\n\tlogger.debug('Fetching GitHub issue database ID', { issueNumber, repo })\n\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}`\n\n\tconst result = await executeGhCommand<{ id: number }>([\n\t\t'api',\n\t\tapiPath,\n\t\t'--jq',\n\t\t'{id: .id}',\n\t])\n\n\treturn result.id\n}\n\n/**\n * Get dependencies for a GitHub issue\n * Uses GitHub's issue dependencies API\n * @param issueNumber - The issue number\n * @param direction - 'blocking' for issues this blocks, 'blocked_by' for issues blocking this\n * @param repo - Optional repo in \"owner/repo\" format\n * @returns Array of dependency objects with id, title, url, state\n */\nexport async function getIssueDependencies(\n\tissueNumber: number,\n\tdirection: 'blocking' | 'blocked_by',\n\trepo?: string\n): Promise<Array<{ id: string; databaseId: number; title: string; url: string; state: string }>> {\n\tlogger.debug('Fetching GitHub issue dependencies', { issueNumber, direction, repo })\n\n\t// Use the dependencies API with the appropriate direction endpoint\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${issueNumber}/dependencies/${direction}`\n\t\t: `repos/:owner/:repo/issues/${issueNumber}/dependencies/${direction}`\n\n\ttry {\n\t\tconst result = await executeGhCommand<GitHubDependency[]>([\n\t\t\t'api',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\t'--jq', '.',\n\t\t\tapiPath,\n\t\t])\n\n\t\treturn (result ?? []).map(dep => ({\n\t\t\tid: String(dep.number),\n\t\t\tdatabaseId: dep.id,\n\t\t\ttitle: dep.title,\n\t\t\turl: dep.html_url,\n\t\t\tstate: dep.state,\n\t\t}))\n\t} catch (error) {\n\t\t// Return empty array for 404 on the dependencies endpoint\n\t\t// This indicates the issue exists but has no dependencies configured\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for 404 specifically on dependencies endpoint\n\t\t\tif (combinedError.includes('404') && combinedError.includes('dependencies')) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t}\n\t\tthrow error\n\t}\n}\n\n/**\n * Create a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n * @throws Error with specific message for: dependency already exists, issue not found, or dependencies feature not enabled\n */\nexport async function createIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Creating GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// POST to the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by`\n\n\ttry {\n\t\tawait executeGhCommand([\n\t\t\t'api',\n\t\t\t'-X', 'POST',\n\t\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\t\tapiPath,\n\t\t\t'-F', `issue_id=${blockingIssueDatabaseId}`,\n\t\t])\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconst errorMessage = error.message\n\t\t\tconst stderr = 'stderr' in error ? (error as { stderr?: string }).stderr ?? '' : ''\n\t\t\tconst combinedError = `${errorMessage} ${stderr}`\n\n\t\t\t// Check for dependency already exists (422 Unprocessable Entity)\n\t\t\tif (combinedError.includes('422') || combinedError.includes('already exists') || combinedError.includes('Unprocessable Entity')) {\n\t\t\t\tthrow new Error(`Dependency already exists: issue #${blockedIssueNumber} is already blocked by the specified issue`)\n\t\t\t}\n\n\t\t\t// Check for issue not found (404)\n\t\t\tif (combinedError.includes('404') || combinedError.includes('Not Found')) {\n\t\t\t\tthrow new Error(`Issue not found: unable to create dependency for issue #${blockedIssueNumber}. The issue may not exist or you may not have access to it.`)\n\t\t\t}\n\n\t\t\t// Check for dependencies feature not enabled (403 or specific error message)\n\t\t\tif (combinedError.includes('403') || combinedError.includes('Forbidden') || combinedError.includes('not enabled')) {\n\t\t\t\tthrow new Error(`Dependencies feature not enabled: the repository may not have issue dependencies enabled. This feature requires GitHub Enterprise or specific repository settings.`)\n\t\t\t}\n\t\t}\n\n\t\t// Re-throw the original error if it doesn't match any known patterns\n\t\tthrow error\n\t}\n}\n\n/**\n * Remove a dependency between two issues (A blocks B)\n * Uses GitHub's issue dependencies API\n * @param blockedIssueNumber - The issue number that is blocked\n * @param blockingIssueDatabaseId - The database ID of the issue that blocks\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function removeIssueDependency(\n\tblockedIssueNumber: number,\n\tblockingIssueDatabaseId: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Removing GitHub issue dependency', { blockedIssueNumber, blockingIssueDatabaseId, repo })\n\n\t// DELETE from the blocked issue's blocked_by endpoint with the blocking issue's database ID\n\tconst apiPath = repo\n\t\t? `repos/${repo}/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\t\t: `repos/:owner/:repo/issues/${blockedIssueNumber}/dependencies/blocked_by/${blockingIssueDatabaseId}`\n\n\tawait executeGhCommand([\n\t\t'api',\n\t\t'-X', 'DELETE',\n\t\t'-H', 'Accept: application/vnd.github+json',\n\t\t'-H', 'X-GitHub-Api-Version: 2022-11-28',\n\t\tapiPath,\n\t])\n}\n\n// Issue State Operations\n\n/**\n * Close a GitHub issue\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function closeGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Closing GitHub issue', { issueNumber, repo })\n\n\tconst args = ['issue', 'close', String(issueNumber)]\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n/**\n * Reopen a GitHub issue\n * @param issueNumber - The issue number\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function reopenGhIssue(\n\tissueNumber: number,\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Reopening GitHub issue', { issueNumber, repo })\n\n\tconst args = ['issue', 'reopen', String(issueNumber)]\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n/**\n * Edit a GitHub issue's properties\n * @param issueNumber - The issue number\n * @param options - Fields to update\n * @param options.title - New issue title\n * @param options.body - New issue body\n * @param options.labels - Labels to add to the issue\n * @param repo - Optional repo in \"owner/repo\" format\n */\nexport async function editGhIssue(\n\tissueNumber: number,\n\toptions: { title?: string; body?: string; labels?: string[] },\n\trepo?: string\n): Promise<void> {\n\tlogger.debug('Editing GitHub issue', { issueNumber, options, repo })\n\n\tconst args = ['issue', 'edit', String(issueNumber)]\n\n\tif (options.title !== undefined) {\n\t\targs.push('--title', options.title)\n\t}\n\tif (options.body !== undefined) {\n\t\targs.push('--body', options.body)\n\t}\n\tif (options.labels) {\n\t\t// Use --add-label for each label. gh issue edit replaces with comma-separated --add-label\n\t\tif (options.labels.length > 0) {\n\t\t\targs.push('--add-label', options.labels.join(','))\n\t\t}\n\t}\n\tif (repo) {\n\t\targs.push('--repo', repo)\n\t}\n\n\tawait executeGhCommand(args)\n}\n\n// Issue List Operations (for il issues command)\n\nexport interface GitHubIssueListItem {\n\tid: string\n\ttitle: string\n\tupdatedAt: string\n\turl: string\n\tstate: string\n}\n\n/**\n * Fetch a list of open GitHub issues sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of issues to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of issues\n */\nexport async function fetchGitHubIssueList(\n\toptions?: { limit?: number; cwd?: string; mine?: boolean }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\n\tlogger.debug('Fetching GitHub issue list', { limit, cwd: options?.cwd, mine: options?.mine })\n\n\tconst args = [\n\t\t'issue',\n\t\t'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state',\n\t\t'--limit', String(limit),\n\t\t'--search', 'sort:updated-desc',\n\t]\n\n\tif (options?.mine) {\n\t\targs.push('--assignee', '@me')\n\t}\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t}>>(args, options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? []).map(item => ({\n\t\tid: String(item.number),\n\t\ttitle: item.title,\n\t\tupdatedAt: item.updatedAt,\n\t\turl: item.url,\n\t\tstate: item.state.toLowerCase(),\n\t}))\n}\n\n/**\n * Fetch a list of open, non-draft GitHub PRs sorted by recently updated\n * @param options - Fetch options\n * @param options.limit - Maximum number of PRs to return (default: 100)\n * @param options.cwd - Working directory for gh CLI (default: process.cwd())\n * @returns Array of PRs mapped to GitHubIssueListItem (with [PR] title prefix)\n */\nexport async function fetchGitHubPRList(\n\toptions?: { limit?: number; cwd?: string; mine?: boolean }\n): Promise<GitHubIssueListItem[]> {\n\tconst limit = options?.limit ?? 100\n\t// Over-fetch to account for draft PRs that will be filtered out client-side\n\t// gh pr list has no --draft=false flag\n\tconst fetchLimit = Math.max(limit * 2, 50)\n\n\tlogger.debug('Fetching GitHub PR list', { limit, fetchLimit, cwd: options?.cwd, mine: options?.mine })\n\n\tconst args = [\n\t\t'pr', 'list',\n\t\t'--state', 'open',\n\t\t'--json', 'number,title,updatedAt,url,state,isDraft',\n\t\t'--limit', String(fetchLimit),\n\t]\n\n\tif (options?.mine) {\n\t\targs.push('--assignee', '@me')\n\t}\n\n\tconst result = await executeGhCommand<Array<{\n\t\tnumber: number\n\t\ttitle: string\n\t\tupdatedAt: string\n\t\turl: string\n\t\tstate: string\n\t\tisDraft: boolean\n\t}>>(args, options?.cwd ? { cwd: options.cwd } : undefined)\n\n\treturn (result ?? [])\n\t\t.filter(item => !item.isDraft)\n\t\t.slice(0, limit)\n\t\t.map(item => ({\n\t\t\tid: String(item.number),\n\t\t\ttitle: `[PR] ${item.title}`,\n\t\t\tupdatedAt: item.updatedAt,\n\t\t\turl: item.url,\n\t\t\tstate: item.state.toLowerCase(),\n\t\t}))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
isPRBranch,
|
|
12
12
|
isValidGitRepo,
|
|
13
13
|
parseWorktreeList
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-MNHZB4Z2.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-VGGST52X.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
getPackageScripts
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YQ57ORTV.js";
|
|
5
5
|
import {
|
|
6
6
|
getLogger
|
|
7
7
|
} from "./chunk-6MLEBAYZ.js";
|
|
@@ -161,4 +161,4 @@ export {
|
|
|
161
161
|
installDependencies,
|
|
162
162
|
runScript
|
|
163
163
|
};
|
|
164
|
-
//# sourceMappingURL=chunk-
|
|
164
|
+
//# sourceMappingURL=chunk-WWKOVDWC.js.map
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import {
|
|
3
3
|
extractIssueNumber,
|
|
4
4
|
extractPRNumber
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MNHZB4Z2.js";
|
|
6
6
|
|
|
7
7
|
// src/utils/IdentifierParser.ts
|
|
8
8
|
function matchIssueIdentifier(input) {
|
|
9
9
|
const trimmed = input.trim();
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
if (
|
|
10
|
+
const projectKeyPattern = /^([A-Z]{2,}-\d+)$/i;
|
|
11
|
+
const projectKeyMatch = trimmed.match(projectKeyPattern);
|
|
12
|
+
if (projectKeyMatch == null ? void 0 : projectKeyMatch[1]) {
|
|
13
13
|
return {
|
|
14
14
|
isIssueIdentifier: true,
|
|
15
|
-
type: "
|
|
16
|
-
identifier:
|
|
15
|
+
type: "project-key",
|
|
16
|
+
identifier: projectKeyMatch[1].toUpperCase()
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
const numericPattern = /^#?(\d+)$/;
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
matchIssueIdentifier,
|
|
109
109
|
IdentifierParser
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-WY4QBK43.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/IdentifierParser.ts"],"sourcesContent":["import type { ParsedInput } from '../commands/start.js'\nimport type { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { extractIssueNumber, extractPRNumber } from './git.js'\n\n/**\n * Result of parsing an issue identifier input\n */\nexport interface IssueIdentifierMatch {\n /** Whether the input matches an issue identifier pattern */\n isIssueIdentifier: boolean\n /** The type of identifier: 'numeric' (GitHub) or 'project-key' (project key format, e.g., Linear, Jira) */\n type?: 'numeric' | 'project-key'\n /** The extracted identifier (without # prefix for numeric) */\n identifier?: string\n}\n\n/**\n * Check if a string looks like an issue identifier\n *\n * Matches:\n * - Numeric patterns: \"123\", \"#123\" (GitHub format)\n * - Project key patterns: \"ENG-123\", \"PLAT-456\" (requires at least 2 letters before dash)\n *\n * This is a pure pattern match - it does NOT validate that the issue exists.\n * Use IssueTracker.detectInputType() to validate existence.\n *\n * @param input - The input string to check\n * @returns Object with isIssueIdentifier flag and optional type/identifier\n */\nexport function matchIssueIdentifier(input: string): IssueIdentifierMatch {\n const trimmed = input.trim()\n\n // Check for project key identifier format (TEAM-NUMBER, e.g., ENG-123, PLAT-456, PROJ-789)\n // Requires at least 2 letters before dash to avoid conflict with PR-123 format\n const projectKeyPattern = /^([A-Z]{2,}-\\d+)$/i\n const projectKeyMatch = trimmed.match(projectKeyPattern)\n if (projectKeyMatch?.[1]) {\n return {\n isIssueIdentifier: true,\n type: 'project-key',\n identifier: projectKeyMatch[1].toUpperCase(),\n }\n }\n\n // Check for numeric pattern (GitHub format: 123 or #123)\n const numericPattern = /^#?(\\d+)$/\n const numericMatch = trimmed.match(numericPattern)\n if (numericMatch?.[1]) {\n return {\n isIssueIdentifier: true,\n type: 'numeric',\n identifier: numericMatch[1],\n }\n }\n\n return { isIssueIdentifier: false }\n}\n\n/**\n * IdentifierParser provides consistent identifier parsing across commands\n * using pattern-based detection without GitHub API calls.\n *\n * Detection Strategy:\n * 1. For numeric input (e.g., \"42\", \"#66\"):\n * - Check for PR worktree first (_pr_N pattern in path)\n * - Then check for issue worktree (issue-N pattern in branch)\n * 2. For alphanumeric input (e.g., \"ENG-123\"):\n * - Check for issue worktree with alphanumeric identifier\n * 3. For branch-style input (e.g., \"feat/issue-42__description\", \"pr/123\"):\n * - Find matching worktree by branch name\n * - Extract PR number from branch name if present (priority)\n * - Extract issue number from branch name if present\n * - Return as PR/issue type if number found, otherwise branch type\n *\n * This ensures:\n * - No unnecessary GitHub API calls\n * - Consistent behavior across finish/cleanup commands\n * - PR detection takes priority over issue detection\n * - Issue numbers are extracted from branch names for \"Fixes #N\" commit trailers\n */\nexport class IdentifierParser {\n\tconstructor(private gitWorktreeManager: GitWorktreeManager) {}\n\n\t/**\n\t * Parse identifier using pattern-based detection on existing worktrees.\n\t * Does NOT make GitHub API calls - only checks local worktree patterns.\n\t *\n\t * @param identifier - The identifier to parse (e.g., \"42\", \"#66\", \"ENG-123\", \"my-branch\")\n\t * @returns ParsedInput with type, number/branchName, and originalInput\n\t * @throws Error if no matching worktree is found\n\t */\n\tasync parseForPatternDetection(identifier: string): Promise<ParsedInput> {\n\t\t// Remove # prefix if present and trim whitespace\n\t\tconst cleanId = identifier.replace(/^#/, '').trim()\n\t\tconst originalInput = identifier\n\n\t\t// Check if input is numeric (GitHub-style issue/PR numbers)\n\t\tconst numericMatch = cleanId.match(/^(\\d+)$/)\n\n\t\tif (numericMatch?.[1]) {\n\t\t\tconst number = parseInt(numericMatch[1], 10)\n\n\t\t\t// Priority 1: Check for PR worktree (_pr_N pattern)\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tconst prWorktree = await this.gitWorktreeManager.findWorktreeForPR(number, '')\n\t\t\tif (prWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'pr',\n\t\t\t\t\tnumber,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Priority 2: Check for issue worktree (issue-N pattern)\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(number)\n\t\t\tif (issueWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No matching worktree found for numeric input\n\t\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Check if input is alphanumeric issue identifier (Linear/Jira-style: ABC-123, ENG-42)\n\t\tconst alphanumericMatch = cleanId.match(/^([A-Za-z]+-\\d+)$/)\n\n\t\tif (alphanumericMatch?.[1]) {\n\t\t\tconst alphanumericId = alphanumericMatch[1]\n\n\t\t\t// Check for issue worktree with alphanumeric identifier\n\t\t\tconst issueWorktree = await this.gitWorktreeManager.findWorktreeForIssue(alphanumericId)\n\t\t\tif (issueWorktree) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: alphanumericId,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// No matching worktree found for alphanumeric identifier\n\t\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t\t}\n\n\t\t// Non-numeric/non-alphanumeric input: treat as branch name\n\t\tconst branchWorktree = await this.gitWorktreeManager.findWorktreeForBranch(cleanId)\n\t\tif (branchWorktree) {\n\t\t\t// Priority 1: Check for PR pattern in the input\n\t\t\tconst prFromBranch = extractPRNumber(cleanId)\n\t\t\tif (prFromBranch !== null) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'pr',\n\t\t\t\t\tnumber: prFromBranch,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Priority 2: Try to extract issue number from branch name\n\t\t\t// This handles cases like \"feat/issue-42__description\" passed as explicit input\n\t\t\tconst issueFromBranch = extractIssueNumber(cleanId)\n\t\t\tif (issueFromBranch !== null) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: 'issue',\n\t\t\t\t\tnumber: issueFromBranch,\n\t\t\t\t\toriginalInput,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttype: 'branch',\n\t\t\t\tbranchName: cleanId,\n\t\t\t\toriginalInput,\n\t\t\t}\n\t\t}\n\n\t\t// No matching worktree found for branch name\n\t\tthrow new Error(`No worktree found for identifier: ${identifier}`)\n\t}\n}\n"],"mappings":";;;;;;;AA6BO,SAAS,qBAAqB,OAAqC;AACxE,QAAM,UAAU,MAAM,KAAK;AAI3B,QAAM,oBAAoB;AAC1B,QAAM,kBAAkB,QAAQ,MAAM,iBAAiB;AACvD,MAAI,mDAAkB,IAAI;AACxB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,gBAAgB,CAAC,EAAE,YAAY;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,iBAAiB;AACvB,QAAM,eAAe,QAAQ,MAAM,cAAc;AACjD,MAAI,6CAAe,IAAI;AACrB,WAAO;AAAA,MACL,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,aAAa,CAAC;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,EAAE,mBAAmB,MAAM;AACpC;AAwBO,IAAM,mBAAN,MAAuB;AAAA,EAC7B,YAAoB,oBAAwC;AAAxC;AAAA,EAAyC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7D,MAAM,yBAAyB,YAA0C;AAExE,UAAM,UAAU,WAAW,QAAQ,MAAM,EAAE,EAAE,KAAK;AAClD,UAAM,gBAAgB;AAGtB,UAAM,eAAe,QAAQ,MAAM,SAAS;AAE5C,QAAI,6CAAe,IAAI;AACtB,YAAM,SAAS,SAAS,aAAa,CAAC,GAAG,EAAE;AAI3C,YAAM,aAAa,MAAM,KAAK,mBAAmB,kBAAkB,QAAQ,EAAE;AAC7E,UAAI,YAAY;AACf,eAAO;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAGA,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,MAAM;AAC/E,UAAI,eAAe;AAClB,eAAO;AAAA,UACN,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAGA,YAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,IAClE;AAGA,UAAM,oBAAoB,QAAQ,MAAM,mBAAmB;AAE3D,QAAI,uDAAoB,IAAI;AAC3B,YAAM,iBAAiB,kBAAkB,CAAC;AAG1C,YAAM,gBAAgB,MAAM,KAAK,mBAAmB,qBAAqB,cAAc;AACvF,UAAI,eAAe;AAClB,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAGA,YAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,IAClE;AAGA,UAAM,iBAAiB,MAAM,KAAK,mBAAmB,sBAAsB,OAAO;AAClF,QAAI,gBAAgB;AAEnB,YAAM,eAAe,gBAAgB,OAAO;AAC5C,UAAI,iBAAiB,MAAM;AAC1B,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAIA,YAAM,kBAAkB,mBAAmB,OAAO;AAClD,UAAI,oBAAoB,MAAM;AAC7B,eAAO;AAAA,UACN,MAAM;AAAA,UACN,QAAQ;AAAA,UACR;AAAA,QACD;AAAA,MACD;AAEA,aAAO;AAAA,QACN,MAAM;AAAA,QACN,YAAY;AAAA,QACZ;AAAA,MACD;AAAA,IACD;AAGA,UAAM,IAAI,MAAM,qCAAqC,UAAU,EAAE;AAAA,EAClE;AACD;","names":[]}
|