@iloom/cli 0.1.17 → 0.1.19

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.
Files changed (118) hide show
  1. package/README.md +51 -6
  2. package/dist/ClaudeContextManager-JKR4WGNU.js +13 -0
  3. package/dist/ClaudeService-55DQGB7T.js +12 -0
  4. package/dist/{GitHubService-F7Z3XJOS.js → GitHubService-LWP4GKGH.js} +3 -3
  5. package/dist/{LoomLauncher-MODG2SEM.js → LoomLauncher-UMMLPIZO.js} +7 -7
  6. package/dist/{PromptTemplateManager-7FINLRDE.js → PromptTemplateManager-WII75TKH.js} +2 -2
  7. package/dist/README.md +755 -0
  8. package/dist/{SettingsManager-VAZF26S2.js → SettingsManager-SKLUVE3K.js} +6 -2
  9. package/dist/{add-issue-22JBNOML.js → add-issue-X56V3XPB.js} +23 -8
  10. package/dist/add-issue-X56V3XPB.js.map +1 -0
  11. package/dist/{chunk-Y7SAGNUT.js → chunk-DEPYQRRB.js} +2 -2
  12. package/dist/{chunk-WKEWRSDB.js → chunk-ELFT36PV.js} +3 -3
  13. package/dist/chunk-FXV24OYZ.js +83 -0
  14. package/dist/chunk-FXV24OYZ.js.map +1 -0
  15. package/dist/{chunk-HPJJSYNS.js → chunk-H5LDRGVK.js} +6 -8
  16. package/dist/{chunk-HPJJSYNS.js.map → chunk-H5LDRGVK.js.map} +1 -1
  17. package/dist/{chunk-QEPVTTHD.js → chunk-IO4WFTL2.js} +17 -11
  18. package/dist/chunk-IO4WFTL2.js.map +1 -0
  19. package/dist/{chunk-KQDEK2ZW.js → chunk-JXQXSC45.js} +41 -9
  20. package/dist/chunk-JXQXSC45.js.map +1 -0
  21. package/dist/{chunk-JQ7VOSTC.js → chunk-KOCQAD2E.js} +3 -3
  22. package/dist/{chunk-YYSKGAZT.js → chunk-LAPY6NAE.js} +17 -8
  23. package/dist/chunk-LAPY6NAE.js.map +1 -0
  24. package/dist/{chunk-O2QWO64Z.js → chunk-PV3GAXQO.js} +56 -3
  25. package/dist/chunk-PV3GAXQO.js.map +1 -0
  26. package/dist/chunk-PVAVNJKS.js +188 -0
  27. package/dist/chunk-PVAVNJKS.js.map +1 -0
  28. package/dist/{chunk-CP2NU2JC.js → chunk-Q2KYPAH2.js} +7 -7
  29. package/dist/{chunk-CP2NU2JC.js.map → chunk-Q2KYPAH2.js.map} +1 -1
  30. package/dist/{chunk-W3DQTW63.js → chunk-USVVV3FP.js} +4 -4
  31. package/dist/{chunk-SSR5AVRJ.js → chunk-VCMMAFXQ.js} +21 -8
  32. package/dist/chunk-VCMMAFXQ.js.map +1 -0
  33. package/dist/chunk-VVH3ANF2.js +307 -0
  34. package/dist/chunk-VVH3ANF2.js.map +1 -0
  35. package/dist/{chunk-JBH2ZYYZ.js → chunk-VYQLLHZ7.js} +22 -3
  36. package/dist/chunk-VYQLLHZ7.js.map +1 -0
  37. package/dist/{chunk-SJUQ2NDR.js → chunk-ZMNQBJUI.js} +24 -19
  38. package/dist/chunk-ZMNQBJUI.js.map +1 -0
  39. package/dist/{chunk-T7QPXANZ.js → chunk-ZWXJBSUW.js} +17 -17
  40. package/dist/chunk-ZWXJBSUW.js.map +1 -0
  41. package/dist/{cleanup-3LUWPSM7.js → cleanup-ZHROIBSQ.js} +12 -16
  42. package/dist/cleanup-ZHROIBSQ.js.map +1 -0
  43. package/dist/cli.js +107 -49
  44. package/dist/cli.js.map +1 -1
  45. package/dist/contribute-3MQJ3XAQ.js +256 -0
  46. package/dist/contribute-3MQJ3XAQ.js.map +1 -0
  47. package/dist/{enhance-XJIQHVPD.js → enhance-VGWUX474.js} +18 -8
  48. package/dist/enhance-VGWUX474.js.map +1 -0
  49. package/dist/{feedback-23CLXKFT.js → feedback-ZOUCCHN4.js} +8 -8
  50. package/dist/{finish-3CQZIULO.js → finish-QJSK6Z7J.js} +36 -313
  51. package/dist/finish-QJSK6Z7J.js.map +1 -0
  52. package/dist/{git-LVRZ57GJ.js → git-OUYMVYJX.js} +2 -2
  53. package/dist/{ignite-WXEF2ID5.js → ignite-HICLZEYU.js} +124 -9
  54. package/dist/ignite-HICLZEYU.js.map +1 -0
  55. package/dist/index.d.ts +794 -712
  56. package/dist/index.js +169 -36
  57. package/dist/index.js.map +1 -1
  58. package/dist/init-UMKNHNV5.js +339 -0
  59. package/dist/init-UMKNHNV5.js.map +1 -0
  60. package/dist/mcp/github-comment-server.js +12 -9
  61. package/dist/mcp/github-comment-server.js.map +1 -1
  62. package/dist/neon-helpers-ZVIRPKCI.js +10 -0
  63. package/dist/{open-X6BTENPV.js → open-ETZUFSE4.js} +15 -17
  64. package/dist/{open-X6BTENPV.js.map → open-ETZUFSE4.js.map} +1 -1
  65. package/dist/prompts/init-prompt.txt +748 -0
  66. package/dist/prompts/issue-prompt.txt +141 -9
  67. package/dist/rebase-KBWFDZCN.js +95 -0
  68. package/dist/rebase-KBWFDZCN.js.map +1 -0
  69. package/dist/remote-GJEZWRCC.js +14 -0
  70. package/dist/{run-2JCPQAX3.js → run-4SVQ3WEU.js} +15 -17
  71. package/dist/{run-2JCPQAX3.js.map → run-4SVQ3WEU.js.map} +1 -1
  72. package/dist/schema/settings.schema.json +51 -1
  73. package/dist/{start-LWVRBJ6S.js → start-CT2ZEFP2.js} +54 -53
  74. package/dist/{start-LWVRBJ6S.js.map → start-CT2ZEFP2.js.map} +1 -1
  75. package/dist/{test-git-XPF4SZXJ.js → test-git-MKZATGZN.js} +3 -3
  76. package/dist/{test-prefix-XGFXFAYN.js → test-prefix-ZNLWDI3K.js} +3 -3
  77. package/dist/{update-DN3FSNKY.js → update-4TDDUR5K.js} +10 -4
  78. package/dist/{update-DN3FSNKY.js.map → update-4TDDUR5K.js.map} +1 -1
  79. package/package.json +3 -2
  80. package/dist/ClaudeContextManager-XOSXQ67R.js +0 -13
  81. package/dist/ClaudeService-YSZ6EXWP.js +0 -12
  82. package/dist/NeonProvider-PAGPUH7F.js +0 -12
  83. package/dist/add-issue-22JBNOML.js.map +0 -1
  84. package/dist/chunk-37DYYFVK.js +0 -29
  85. package/dist/chunk-37DYYFVK.js.map +0 -1
  86. package/dist/chunk-F3XBU2R7.js +0 -110
  87. package/dist/chunk-F3XBU2R7.js.map +0 -1
  88. package/dist/chunk-JBH2ZYYZ.js.map +0 -1
  89. package/dist/chunk-KQDEK2ZW.js.map +0 -1
  90. package/dist/chunk-O2QWO64Z.js.map +0 -1
  91. package/dist/chunk-QEPVTTHD.js.map +0 -1
  92. package/dist/chunk-SJUQ2NDR.js.map +0 -1
  93. package/dist/chunk-SSR5AVRJ.js.map +0 -1
  94. package/dist/chunk-T7QPXANZ.js.map +0 -1
  95. package/dist/chunk-YYSKGAZT.js.map +0 -1
  96. package/dist/cleanup-3LUWPSM7.js.map +0 -1
  97. package/dist/enhance-XJIQHVPD.js.map +0 -1
  98. package/dist/env-MDFL4ZXL.js +0 -23
  99. package/dist/finish-3CQZIULO.js.map +0 -1
  100. package/dist/ignite-WXEF2ID5.js.map +0 -1
  101. package/dist/init-RHACUR4E.js +0 -123
  102. package/dist/init-RHACUR4E.js.map +0 -1
  103. /package/dist/{ClaudeContextManager-XOSXQ67R.js.map → ClaudeContextManager-JKR4WGNU.js.map} +0 -0
  104. /package/dist/{ClaudeService-YSZ6EXWP.js.map → ClaudeService-55DQGB7T.js.map} +0 -0
  105. /package/dist/{GitHubService-F7Z3XJOS.js.map → GitHubService-LWP4GKGH.js.map} +0 -0
  106. /package/dist/{LoomLauncher-MODG2SEM.js.map → LoomLauncher-UMMLPIZO.js.map} +0 -0
  107. /package/dist/{NeonProvider-PAGPUH7F.js.map → PromptTemplateManager-WII75TKH.js.map} +0 -0
  108. /package/dist/{PromptTemplateManager-7FINLRDE.js.map → SettingsManager-SKLUVE3K.js.map} +0 -0
  109. /package/dist/{chunk-Y7SAGNUT.js.map → chunk-DEPYQRRB.js.map} +0 -0
  110. /package/dist/{chunk-WKEWRSDB.js.map → chunk-ELFT36PV.js.map} +0 -0
  111. /package/dist/{chunk-JQ7VOSTC.js.map → chunk-KOCQAD2E.js.map} +0 -0
  112. /package/dist/{chunk-W3DQTW63.js.map → chunk-USVVV3FP.js.map} +0 -0
  113. /package/dist/{feedback-23CLXKFT.js.map → feedback-ZOUCCHN4.js.map} +0 -0
  114. /package/dist/{SettingsManager-VAZF26S2.js.map → git-OUYMVYJX.js.map} +0 -0
  115. /package/dist/{env-MDFL4ZXL.js.map → neon-helpers-ZVIRPKCI.js.map} +0 -0
  116. /package/dist/{git-LVRZ57GJ.js.map → remote-GJEZWRCC.js.map} +0 -0
  117. /package/dist/{test-git-XPF4SZXJ.js.map → test-git-MKZATGZN.js.map} +0 -0
  118. /package/dist/{test-prefix-XGFXFAYN.js.map → test-prefix-ZNLWDI3K.js.map} +0 -0
@@ -19,12 +19,39 @@ async function checkGhAuth() {
19
19
  var _a, _b;
20
20
  try {
21
21
  const output = await executeGhCommand(["auth", "status"]);
22
- const scopeMatch = output.match(/Token scopes: (.+)/);
23
- const userMatch = output.match(/Logged in to github\.com as ([^\s]+)/);
24
- const username = userMatch == null ? void 0 : userMatch[1];
22
+ const lines = output.split("\n");
23
+ let username;
24
+ let scopes = [];
25
+ for (let i = 0; i < lines.length; i++) {
26
+ const line = lines[i];
27
+ const newFormatMatch = line == null ? void 0 : line.match(/Logged in to github\.com account ([^\s(]+)/);
28
+ if (newFormatMatch) {
29
+ const accountName = newFormatMatch[1];
30
+ const nextFewLines = lines.slice(i + 1, i + 5).join("\n");
31
+ const isActive = nextFewLines.includes("Active account: true");
32
+ if (isActive || !username && !output.includes("Active account:")) {
33
+ username = accountName;
34
+ const scopeMatch = nextFewLines.match(/Token scopes: (.+)/);
35
+ if (scopeMatch == null ? void 0 : scopeMatch[1]) {
36
+ scopes = scopeMatch[1].split(", ").map((scope) => scope.replace(/^'|'$/g, ""));
37
+ }
38
+ if (isActive) break;
39
+ }
40
+ }
41
+ if (!username) {
42
+ const oldFormatMatch = line == null ? void 0 : line.match(/Logged in to github\.com as ([^\s]+)/);
43
+ if (oldFormatMatch) {
44
+ username = oldFormatMatch[1];
45
+ }
46
+ }
47
+ }
48
+ if (scopes.length === 0) {
49
+ const scopeMatch = output.match(/Token scopes: (.+)/);
50
+ scopes = ((_a = scopeMatch == null ? void 0 : scopeMatch[1]) == null ? void 0 : _a.split(", ").map((scope) => scope.replace(/^'|'$/g, ""))) ?? [];
51
+ }
25
52
  return {
26
53
  hasAuth: true,
27
- scopes: ((_a = scopeMatch == null ? void 0 : scopeMatch[1]) == null ? void 0 : _a.split(", ").map((scope) => scope.replace(/^'|'$/g, ""))) ?? [],
54
+ scopes,
28
55
  ...username && { username }
29
56
  };
30
57
  } catch (error) {
@@ -52,15 +79,19 @@ async function fetchGhIssue(issueNumber, repo) {
52
79
  }
53
80
  return executeGhCommand(args);
54
81
  }
55
- async function fetchGhPR(prNumber) {
56
- logger.debug("Fetching GitHub PR", { prNumber });
57
- return executeGhCommand([
82
+ async function fetchGhPR(prNumber, repo) {
83
+ logger.debug("Fetching GitHub PR", { prNumber, repo });
84
+ const args = [
58
85
  "pr",
59
86
  "view",
60
87
  String(prNumber),
61
88
  "--json",
62
89
  "number,title,body,state,headRefName,baseRefName,url,isDraft,mergeable,createdAt,updatedAt"
63
- ]);
90
+ ];
91
+ if (repo) {
92
+ args.push("--repo", repo);
93
+ }
94
+ return executeGhCommand(args);
64
95
  }
65
96
  async function fetchProjectList(owner) {
66
97
  const result = await executeGhCommand([
@@ -184,6 +215,7 @@ async function getRepoInfo() {
184
215
 
185
216
  export {
186
217
  executeGhCommand,
218
+ checkGhAuth,
187
219
  hasProjectScope,
188
220
  fetchGhIssue,
189
221
  fetchGhPR,
@@ -196,4 +228,4 @@ export {
196
228
  createIssue,
197
229
  getRepoInfo
198
230
  };
199
- //# sourceMappingURL=chunk-KQDEK2ZW.js.map
231
+ //# sourceMappingURL=chunk-JXQXSC45.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\tBranchNameStrategy,\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, or --jq 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\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,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// Branch name generation strategies\nexport class SimpleBranchNameStrategy implements BranchNameStrategy {\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Create a simple slug from the title\n\t\tconst slug = title\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 20) // Keep it short for the simple strategy\n\n\t\treturn `feat/issue-${issueNumber}-${slug}`\n\t}\n}\n\nexport class ClaudeBranchNameStrategy implements BranchNameStrategy {\n\tconstructor(private claudeModel = 'haiku') {}\n\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Import dynamically to avoid circular dependency\n\t\tconst { generateBranchName } = await import('../utils/claude.js')\n\n\t\t// Delegate to the shared implementation\n\t\treturn generateBranchName(title, issueNumber, this.claudeModel)\n\t}\n}\n\n// Template-based strategy for custom patterns\nexport class TemplateBranchNameStrategy implements BranchNameStrategy {\n\tconstructor(private template = '{prefix}/issue-{number}-{slug}') {}\n\n\tasync generate(issueNumber: number, title: string): Promise<string> {\n\t\t// Determine prefix based on title\n\t\tconst prefix = this.determinePrefix(title)\n\n\t\t// Create slug from title\n\t\tconst slug = title\n\t\t\t.toLowerCase()\n\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t.replace(/^-|-$/g, '')\n\t\t\t.substring(0, 30)\n\n\t\treturn this.template\n\t\t\t.replace('{prefix}', prefix)\n\t\t\t.replace('{number}', String(issueNumber))\n\t\t\t.replace('{slug}', slug)\n\t}\n\n\tprivate determinePrefix(title: string): string {\n\t\tconst lowerTitle = title.toLowerCase()\n\t\tif (lowerTitle.includes('fix') || lowerTitle.includes('bug')) return 'fix'\n\t\tif (lowerTitle.includes('doc')) return 'docs'\n\t\tif (lowerTitle.includes('test')) return 'test'\n\t\tif (lowerTitle.includes('refactor')) return 'refactor'\n\t\treturn 'feat'\n\t}\n}\n\n// GitHub Issue Operations\n\ninterface IssueCreateResponse {\n\tnumber: 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}"],"mappings":"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","names":[]}
@@ -261,7 +261,7 @@ async function findMainWorktreePath(path2 = process.cwd(), options) {
261
261
  }
262
262
  async function findMainWorktreePathWithSettings(path2, settingsManager) {
263
263
  if (!settingsManager) {
264
- const { SettingsManager: SM } = await import("./SettingsManager-VAZF26S2.js");
264
+ const { SettingsManager: SM } = await import("./SettingsManager-SKLUVE3K.js");
265
265
  settingsManager = new SM();
266
266
  }
267
267
  const settings = await settingsManager.loadSettings(path2);
@@ -296,7 +296,7 @@ async function getDefaultBranch(path2 = process.cwd()) {
296
296
  }
297
297
  async function findAllBranchesForIssue(issueNumber, path2 = process.cwd(), settingsManager) {
298
298
  if (!settingsManager) {
299
- const { SettingsManager: SM } = await import("./SettingsManager-VAZF26S2.js");
299
+ const { SettingsManager: SM } = await import("./SettingsManager-SKLUVE3K.js");
300
300
  settingsManager = new SM();
301
301
  }
302
302
  const protectedBranches = await settingsManager.getProtectedBranches(path2);
@@ -434,4 +434,4 @@ export {
434
434
  ensureRepositoryHasCommits,
435
435
  pushBranchToRemote
436
436
  };
437
- //# sourceMappingURL=chunk-JQ7VOSTC.js.map
437
+ //# sourceMappingURL=chunk-KOCQAD2E.js.map
@@ -13,19 +13,19 @@ function validateNeonConfig(config) {
13
13
  if (!config.projectId) {
14
14
  return {
15
15
  valid: false,
16
- error: "NEON_PROJECT_ID is required"
16
+ error: "Neon projectId is required. Configure in .iloom/settings.json under databaseProviders.neon"
17
17
  };
18
18
  }
19
19
  if (!config.parentBranch) {
20
20
  return {
21
21
  valid: false,
22
- error: "NEON_PARENT_BRANCH is required"
22
+ error: "Neon parentBranch is required. Configure in .iloom/settings.json under databaseProviders.neon"
23
23
  };
24
24
  }
25
25
  if (!/^[a-zA-Z0-9-]+$/.test(config.projectId)) {
26
26
  return {
27
27
  valid: false,
28
- error: "NEON_PROJECT_ID contains invalid characters"
28
+ error: "Neon projectId contains invalid characters"
29
29
  };
30
30
  }
31
31
  return { valid: true };
@@ -51,7 +51,7 @@ var NeonProvider = class {
51
51
  }
52
52
  /**
53
53
  * Check if provider is properly configured
54
- * Returns true if NEON_PROJECT_ID and NEON_PARENT_BRANCH are set
54
+ * Returns true if projectId and parentBranch are valid in settings
55
55
  */
56
56
  isConfigured() {
57
57
  return this._isConfigured;
@@ -65,7 +65,7 @@ var NeonProvider = class {
65
65
  */
66
66
  async executeNeonCommand(args, cwd) {
67
67
  if (!this._isConfigured) {
68
- throw new Error("NeonProvider is not configured. Check NEON_PROJECT_ID and NEON_PARENT_BRANCH environment variables.");
68
+ throw new Error("NeonProvider is not configured. Check databaseProviders.neon configuration in .iloom/settings.json");
69
69
  }
70
70
  const command = `neon ${args.join(" ")}`;
71
71
  logger.debug(`Executing Neon CLI command: ${command}`);
@@ -377,8 +377,17 @@ var NeonProvider = class {
377
377
  }
378
378
  };
379
379
 
380
+ // src/utils/neon-helpers.ts
381
+ function createNeonProviderFromSettings(settings) {
382
+ var _a;
383
+ const neonConfig = (_a = settings.databaseProviders) == null ? void 0 : _a.neon;
384
+ return new NeonProvider({
385
+ projectId: (neonConfig == null ? void 0 : neonConfig.projectId) ?? "",
386
+ parentBranch: (neonConfig == null ? void 0 : neonConfig.parentBranch) ?? ""
387
+ });
388
+ }
389
+
380
390
  export {
381
- validateNeonConfig,
382
- NeonProvider
391
+ createNeonProviderFromSettings
383
392
  };
384
- //# sourceMappingURL=chunk-YYSKGAZT.js.map
393
+ //# sourceMappingURL=chunk-LAPY6NAE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/providers/NeonProvider.ts","../src/utils/neon-helpers.ts"],"sourcesContent":["import { execa, type ExecaError } from 'execa'\nimport type { DatabaseProvider } from '../../types/index.js'\nimport { createLogger } from '../../utils/logger.js'\nimport { promptConfirmation } from '../../utils/prompt.js'\n\nconst logger = createLogger({ prefix: '🗂️' })\n\ninterface NeonBranch {\n name: string\n id: string\n [key: string]: unknown\n}\n\nexport interface NeonConfig {\n projectId: string\n parentBranch: string\n}\n\n/**\n * Validate Neon configuration\n * Checks that required configuration values are present\n */\nexport function validateNeonConfig(config: {\n projectId?: string\n parentBranch?: string\n}): { valid: boolean; error?: string } {\n if (!config.projectId) {\n return {\n valid: false,\n error: 'Neon projectId is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n if (!config.parentBranch) {\n return {\n valid: false,\n error: 'Neon parentBranch is required. Configure in .iloom/settings.json under databaseProviders.neon',\n }\n }\n\n // Basic validation for project ID format (should start with appropriate prefix)\n if (!/^[a-zA-Z0-9-]+$/.test(config.projectId)) {\n return {\n valid: false,\n error: 'Neon projectId contains invalid characters',\n }\n }\n\n return { valid: true }\n}\n\n/**\n * Neon database provider implementation\n * Ports functionality from bash/utils/neon-utils.sh\n */\nexport class NeonProvider implements DatabaseProvider {\n private _isConfigured: boolean = false\n\n constructor(private config: NeonConfig) {\n logger.debug('NeonProvider initialized with config:', {\n projectId: config.projectId,\n parentBranch: config.parentBranch,\n hasProjectId: !!config.projectId,\n hasParentBranch: !!config.parentBranch,\n })\n\n // Validate config but don't throw - just mark as not configured\n // This allows the provider to be instantiated even when Neon is not being used\n const validation = validateNeonConfig(config)\n if (!validation.valid) {\n logger.debug(`NeonProvider not configured: ${validation.error}`)\n logger.debug('Neon database branching will not be used')\n this._isConfigured = false\n } else {\n this._isConfigured = true\n }\n }\n\n /**\n * Check if provider is properly configured\n * Returns true if projectId and parentBranch are valid in settings\n */\n isConfigured(): boolean {\n return this._isConfigured\n }\n\n /**\n * Execute a Neon CLI command and return stdout\n * Throws an error if the command fails\n *\n * @param args - Command arguments to pass to neon CLI\n * @param cwd - Optional working directory to run the command from (defaults to current directory)\n */\n private async executeNeonCommand(args: string[], cwd?: string): Promise<string> {\n // Check if provider is properly configured\n if (!this._isConfigured) {\n throw new Error('NeonProvider is not configured. Check databaseProviders.neon configuration in .iloom/settings.json')\n }\n\n // Log the exact command being executed for debugging\n const command = `neon ${args.join(' ')}`\n logger.debug(`Executing Neon CLI command: ${command}`)\n logger.debug(`Project ID being used: ${this.config.projectId}`)\n if (cwd) {\n logger.debug(`Working directory: ${cwd}`)\n }\n\n const result = await execa('neon', args, {\n timeout: 30000,\n encoding: 'utf8',\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return result.stdout\n }\n\n /**\n * Check if neon CLI is available\n * Ports: check_neon_cli() from bash/utils/neon-utils.sh:18-23\n */\n async isCliAvailable(): Promise<boolean> {\n try {\n await execa('command', ['-v', 'neon'], {\n timeout: 5000,\n shell: true,\n })\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Check if user is authenticated with Neon CLI\n * Ports: check_neon_auth() from bash/utils/neon-utils.sh:25-36\n *\n * @param cwd - Optional working directory to run the command from (prevents issues with deleted directories)\n * @throws Error if authentication check fails for reasons other than not being authenticated\n */\n async isAuthenticated(cwd?: string): Promise<boolean> {\n const cliAvailable = await this.isCliAvailable()\n if (!cliAvailable) {\n return false\n }\n\n try {\n await execa('neon', ['me'], {\n timeout: 10000,\n stdio: 'pipe',\n ...(cwd && { cwd }),\n })\n return true\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr?.trim() ?? ''\n\n // Check for authentication failure patterns (should return false, not throw)\n const isAuthError =\n stderr.toLowerCase().includes('not authenticated') ||\n stderr.toLowerCase().includes('not logged in') ||\n stderr.toLowerCase().includes('authentication required') ||\n stderr.toLowerCase().includes('login required')\n\n if (isAuthError) {\n return false\n }\n\n // For any other error, let it bubble up\n throw error\n }\n }\n\n /**\n * Sanitize branch name for Neon (replace slashes with underscores)\n * Ports: sanitize_neon_branch_name() from bash/utils/neon-utils.sh:11-15\n */\n sanitizeBranchName(branchName: string): string {\n return branchName.replace(/\\//g, '_')\n }\n\n /**\n * Extract endpoint ID from Neon connection string\n * Pattern matches: ep-abc-123 or ep-abc-123-pooler\n * Returns: ep-abc-123 (without -pooler suffix)\n * Used by: get_neon_branch_name() from bash/utils/neon-utils.sh:294\n */\n private extractEndpointId(connectionString: string): string | null {\n // First, extract the full host part between @ and first dot\n // Examples:\n // @ep-abc123.us-east-1.neon.tech -> ep-abc123\n // @ep-abc123-pooler.us-east-1.neon.tech -> ep-abc123-pooler\n const hostMatch = connectionString.match(/@(ep-[a-z0-9-]+)\\./)\n if (!hostMatch?.[1]) {\n return null\n }\n\n const fullEndpoint = hostMatch[1]\n // Remove -pooler suffix if present\n return fullEndpoint.replace(/-pooler$/, '')\n }\n\n /**\n * List all branches in the Neon project\n * Ports: list_neon_branches() from bash/utils/neon-utils.sh:63-74\n *\n * @param cwd - Optional working directory to run commands from\n */\n async listBranches(cwd?: string): Promise<string[]> {\n const output = await this.executeNeonCommand([\n 'branches',\n 'list',\n '--project-id',\n this.config.projectId,\n '--output',\n 'json',\n ], cwd)\n\n const branches: NeonBranch[] = JSON.parse(output)\n return branches.map(branch => branch.name)\n }\n\n /**\n * Check if a branch exists\n * Ports: check_neon_branch_exists() from bash/utils/neon-utils.sh:38-61\n *\n * @param name - Branch name to check\n * @param cwd - Optional working directory to run commands from\n */\n async branchExists(name: string, cwd?: string): Promise<boolean> {\n const branches = await this.listBranches(cwd)\n return branches.includes(name)\n }\n\n /**\n * Get connection string for a specific branch\n * Ports: get_neon_connection_string() from bash/utils/neon-utils.sh:76-90\n *\n * @param branch - Branch name to get connection string for\n * @param cwd - Optional working directory to run commands from\n */\n async getConnectionString(branch: string, cwd?: string): Promise<string> {\n const connectionString = await this.executeNeonCommand([\n 'connection-string',\n '--branch',\n branch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n return connectionString.trim()\n }\n\n /**\n * Find Vercel preview database branch\n * Checks for both patterns: preview/<branch> and preview_<sanitized-branch>\n * Ports: find_preview_database_branch() from bash/utils/neon-utils.sh:92-124\n *\n * @param branchName - Branch name to find preview for\n * @param cwd - Optional working directory to run commands from\n */\n async findPreviewBranch(branchName: string, cwd?: string): Promise<string | null> {\n // Check for exact preview branch match with slash pattern\n const slashPattern = `preview/${branchName}`\n if (await this.branchExists(slashPattern, cwd)) {\n logger.info(`Found Vercel preview database: ${slashPattern}`)\n return slashPattern\n }\n\n // Check for underscore pattern variation\n const sanitized = this.sanitizeBranchName(branchName)\n const underscorePattern = `preview_${sanitized}`\n if (await this.branchExists(underscorePattern, cwd)) {\n logger.info(`Found Vercel preview database: ${underscorePattern}`)\n return underscorePattern\n }\n\n return null\n }\n\n /**\n * Create a new database branch\n * ALWAYS checks for Vercel preview database first\n * Returns connection string for the branch\n * Ports: create_neon_database_branch() from bash/utils/neon-utils.sh:126-187\n *\n * @param name - Name for the new branch\n * @param fromBranch - Parent branch to create from (defaults to config.parentBranch)\n * @param cwd - Optional working directory to run commands from\n */\n async createBranch(name: string, fromBranch?: string, cwd?: string): Promise<string> {\n // Always check for existing Vercel preview database first (lines 149-158)\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n const connectionString = await this.getConnectionString(previewBranch, cwd)\n logger.success(`Using existing Vercel preview database: ${previewBranch}`)\n return connectionString\n }\n\n // Sanitize branch name for Neon (replace slashes with underscores)\n const sanitizedName = this.sanitizeBranchName(name)\n const parentBranch = fromBranch ?? this.config.parentBranch\n\n logger.info('Creating Neon database branch...')\n logger.info(` Source branch: ${parentBranch}`)\n logger.info(` New branch: ${sanitizedName}`)\n\n // Create the database branch\n await this.executeNeonCommand([\n 'branches',\n 'create',\n '--name',\n sanitizedName,\n '--parent',\n parentBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n\n logger.success('Database branch created successfully')\n\n // Get the connection string for the new branch\n logger.info('Getting connection string for new database branch...')\n const connectionString = await this.getConnectionString(sanitizedName, cwd)\n\n return connectionString\n }\n\n /**\n * Delete a database branch\n * Includes preview database protection with user confirmation\n * Ports: delete_neon_database_branch() from bash/utils/neon-utils.sh:204-259\n *\n * @param name - Name of the branch to delete\n * @param isPreview - Whether this is a preview database branch\n * @param cwd - Optional working directory to run commands from (prevents issues with deleted directories)\n */\n async deleteBranch(name: string, isPreview: boolean = false, cwd?: string): Promise<import('../../types/index.js').DatabaseDeletionResult> {\n // Sanitize branch name for Neon\n const sanitizedName = this.sanitizeBranchName(name)\n\n // For preview contexts, check for preview databases first\n if (isPreview) {\n const previewBranch = await this.findPreviewBranch(name, cwd)\n if (previewBranch) {\n logger.warn(`Found Vercel preview database: ${previewBranch}`)\n logger.warn('Preview databases are managed by Vercel and will be cleaned up automatically')\n logger.warn('Manual deletion may interfere with Vercel\\'s preview deployments')\n\n const confirmed = await promptConfirmation(\n 'Delete preview database anyway?',\n false\n )\n\n if (confirmed) {\n // User confirmed - delete preview branch\n try {\n logger.info(`Deleting Vercel preview database: ${previewBranch}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n previewBranch,\n '--project-id',\n this.config.projectId,\n ], cwd)\n logger.success('Preview database deleted successfully')\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: previewBranch\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.error(`Failed to delete preview database: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: previewBranch\n }\n }\n } else {\n // User declined deletion\n logger.info('Skipping preview database deletion')\n return {\n success: true,\n deleted: false,\n notFound: false,\n userDeclined: true,\n branchName: previewBranch\n }\n }\n }\n // If no preview database found, fall through to check regular branch\n }\n\n // Check for regular branch\n logger.info(`Checking for Neon database branch: ${sanitizedName}`)\n\n try {\n const exists = await this.branchExists(sanitizedName, cwd)\n\n if (!exists) {\n logger.info(`No database branch found for '${name}'`)\n return {\n success: true,\n deleted: false,\n notFound: true,\n branchName: sanitizedName\n }\n }\n\n // Branch exists - delete it\n logger.info(`Deleting Neon database branch: ${sanitizedName}`)\n await this.executeNeonCommand([\n 'branches',\n 'delete',\n sanitizedName,\n '--project-id',\n this.config.projectId,\n ], cwd)\n logger.success('Database branch deleted successfully')\n\n return {\n success: true,\n deleted: true,\n notFound: false,\n branchName: sanitizedName\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n logger.error(`Failed to delete database branch: ${errorMessage}`)\n return {\n success: false,\n deleted: false,\n notFound: false,\n error: errorMessage,\n branchName: sanitizedName\n }\n }\n }\n\n /**\n * Get branch name from endpoint ID (reverse lookup)\n * Searches all branches to find one with matching endpoint\n * Ports: get_neon_branch_name() from bash/utils/neon-utils.sh:262-308\n *\n * @param endpointId - Endpoint ID to search for\n * @param cwd - Optional working directory to run commands from\n */\n async getBranchNameFromEndpoint(endpointId: string, cwd?: string): Promise<string | null> {\n const branches = await this.listBranches(cwd)\n\n for (const branch of branches) {\n try {\n const connectionString = await this.getConnectionString(branch, cwd)\n const branchEndpointId = this.extractEndpointId(connectionString)\n\n if (branchEndpointId === endpointId) {\n return branch\n }\n } catch {\n // Skip branches that fail to get connection string\n continue\n }\n }\n\n return null\n }\n}\n","import { NeonProvider } from '../lib/providers/NeonProvider.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\n\n/**\n * Create NeonProvider from settings configuration\n * Returns provider with isConfigured() = false if neon settings missing\n */\nexport function createNeonProviderFromSettings(settings: IloomSettings): NeonProvider {\n\tconst neonConfig = settings.databaseProviders?.neon\n\n\treturn new NeonProvider({\n\t\tprojectId: neonConfig?.projectId ?? '',\n\t\tparentBranch: neonConfig?.parentBranch ?? '',\n\t})\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,aAA8B;AAKvC,IAAM,SAAS,aAAa,EAAE,QAAQ,kBAAM,CAAC;AAiBtC,SAAS,mBAAmB,QAGI;AACrC,MAAI,CAAC,OAAO,WAAW;AACrB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,cAAc;AACxB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,CAAC,kBAAkB,KAAK,OAAO,SAAS,GAAG;AAC7C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,KAAK;AACvB;AAMO,IAAM,eAAN,MAA+C;AAAA,EAGpD,YAAoB,QAAoB;AAApB;AAFpB,SAAQ,gBAAyB;AAG/B,WAAO,MAAM,yCAAyC;AAAA,MACpD,WAAW,OAAO;AAAA,MAClB,cAAc,OAAO;AAAA,MACrB,cAAc,CAAC,CAAC,OAAO;AAAA,MACvB,iBAAiB,CAAC,CAAC,OAAO;AAAA,IAC5B,CAAC;AAID,UAAM,aAAa,mBAAmB,MAAM;AAC5C,QAAI,CAAC,WAAW,OAAO;AACrB,aAAO,MAAM,gCAAgC,WAAW,KAAK,EAAE;AAC/D,aAAO,MAAM,0CAA0C;AACvD,WAAK,gBAAgB;AAAA,IACvB,OAAO;AACL,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,eAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,mBAAmB,MAAgB,KAA+B;AAE9E,QAAI,CAAC,KAAK,eAAe;AACvB,YAAM,IAAI,MAAM,oGAAoG;AAAA,IACtH;AAGA,UAAM,UAAU,QAAQ,KAAK,KAAK,GAAG,CAAC;AACtC,WAAO,MAAM,+BAA+B,OAAO,EAAE;AACrD,WAAO,MAAM,0BAA0B,KAAK,OAAO,SAAS,EAAE;AAC9D,QAAI,KAAK;AACP,aAAO,MAAM,sBAAsB,GAAG,EAAE;AAAA,IAC1C;AAEA,UAAM,SAAS,MAAM,MAAM,QAAQ,MAAM;AAAA,MACvC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO;AAAA,MACP,GAAI,OAAO,EAAE,IAAI;AAAA,IACnB,CAAC;AACD,WAAO,OAAO;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBAAmC;AACvC,QAAI;AACF,YAAM,MAAM,WAAW,CAAC,MAAM,MAAM,GAAG;AAAA,QACrC,SAAS;AAAA,QACT,OAAO;AAAA,MACT,CAAC;AACD,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,gBAAgB,KAAgC;AA3IxD;AA4II,UAAM,eAAe,MAAM,KAAK,eAAe;AAC/C,QAAI,CAAC,cAAc;AACjB,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,MAAM,QAAQ,CAAC,IAAI,GAAG;AAAA,QAC1B,SAAS;AAAA,QACT,OAAO;AAAA,QACP,GAAI,OAAO,EAAE,IAAI;AAAA,MACnB,CAAC;AACD,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,aAAa;AACnB,YAAM,WAAS,gBAAW,WAAX,mBAAmB,WAAU;AAG5C,YAAM,cACJ,OAAO,YAAY,EAAE,SAAS,mBAAmB,KACjD,OAAO,YAAY,EAAE,SAAS,eAAe,KAC7C,OAAO,YAAY,EAAE,SAAS,yBAAyB,KACvD,OAAO,YAAY,EAAE,SAAS,gBAAgB;AAEhD,UAAI,aAAa;AACf,eAAO;AAAA,MACT;AAGA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAmB,YAA4B;AAC7C,WAAO,WAAW,QAAQ,OAAO,GAAG;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,kBAAyC;AAKjE,UAAM,YAAY,iBAAiB,MAAM,oBAAoB;AAC7D,QAAI,EAAC,uCAAY,KAAI;AACnB,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,UAAU,CAAC;AAEhC,WAAO,aAAa,QAAQ,YAAY,EAAE;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,aAAa,KAAiC;AAClD,UAAM,SAAS,MAAM,KAAK,mBAAmB;AAAA,MAC3C;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,IACF,GAAG,GAAG;AAEN,UAAM,WAAyB,KAAK,MAAM,MAAM;AAChD,WAAO,SAAS,IAAI,YAAU,OAAO,IAAI;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,aAAa,MAAc,KAAgC;AAC/D,UAAM,WAAW,MAAM,KAAK,aAAa,GAAG;AAC5C,WAAO,SAAS,SAAS,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,oBAAoB,QAAgB,KAA+B;AACvE,UAAM,mBAAmB,MAAM,KAAK,mBAAmB;AAAA,MACrD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,IACd,GAAG,GAAG;AACN,WAAO,iBAAiB,KAAK;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,kBAAkB,YAAoB,KAAsC;AAEhF,UAAM,eAAe,WAAW,UAAU;AAC1C,QAAI,MAAM,KAAK,aAAa,cAAc,GAAG,GAAG;AAC9C,aAAO,KAAK,kCAAkC,YAAY,EAAE;AAC5D,aAAO;AAAA,IACT;AAGA,UAAM,YAAY,KAAK,mBAAmB,UAAU;AACpD,UAAM,oBAAoB,WAAW,SAAS;AAC9C,QAAI,MAAM,KAAK,aAAa,mBAAmB,GAAG,GAAG;AACnD,aAAO,KAAK,kCAAkC,iBAAiB,EAAE;AACjE,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,aAAa,MAAc,YAAqB,KAA+B;AAEnF,UAAM,gBAAgB,MAAM,KAAK,kBAAkB,MAAM,GAAG;AAC5D,QAAI,eAAe;AACjB,YAAMA,oBAAmB,MAAM,KAAK,oBAAoB,eAAe,GAAG;AAC1E,aAAO,QAAQ,2CAA2C,aAAa,EAAE;AACzE,aAAOA;AAAA,IACT;AAGA,UAAM,gBAAgB,KAAK,mBAAmB,IAAI;AAClD,UAAM,eAAe,cAAc,KAAK,OAAO;AAE/C,WAAO,KAAK,kCAAkC;AAC9C,WAAO,KAAK,oBAAoB,YAAY,EAAE;AAC9C,WAAO,KAAK,iBAAiB,aAAa,EAAE;AAG5C,UAAM,KAAK,mBAAmB;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,IACd,GAAG,GAAG;AAEN,WAAO,QAAQ,sCAAsC;AAGrD,WAAO,KAAK,sDAAsD;AAClE,UAAM,mBAAmB,MAAM,KAAK,oBAAoB,eAAe,GAAG;AAE1E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,aAAa,MAAc,YAAqB,OAAO,KAA8E;AAEzI,UAAM,gBAAgB,KAAK,mBAAmB,IAAI;AAGlD,QAAI,WAAW;AACb,YAAM,gBAAgB,MAAM,KAAK,kBAAkB,MAAM,GAAG;AAC5D,UAAI,eAAe;AACjB,eAAO,KAAK,kCAAkC,aAAa,EAAE;AAC7D,eAAO,KAAK,8EAA8E;AAC1F,eAAO,KAAK,iEAAkE;AAE9E,cAAM,YAAY,MAAM;AAAA,UACtB;AAAA,UACA;AAAA,QACF;AAEA,YAAI,WAAW;AAEb,cAAI;AACF,mBAAO,KAAK,qCAAqC,aAAa,EAAE;AAChE,kBAAM,KAAK,mBAAmB;AAAA,cAC5B;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA,KAAK,OAAO;AAAA,YACd,GAAG,GAAG;AACN,mBAAO,QAAQ,uCAAuC;AACtD,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS;AAAA,cACT,UAAU;AAAA,cACV,YAAY;AAAA,YACd;AAAA,UACF,SAAS,OAAO;AACd,kBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,mBAAO,MAAM,sCAAsC,YAAY,EAAE;AACjE,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS;AAAA,cACT,UAAU;AAAA,cACV,OAAO;AAAA,cACP,YAAY;AAAA,YACd;AAAA,UACF;AAAA,QACF,OAAO;AAEL,iBAAO,KAAK,oCAAoC;AAChD,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS;AAAA,YACT,UAAU;AAAA,YACV,cAAc;AAAA,YACd,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IAEF;AAGA,WAAO,KAAK,sCAAsC,aAAa,EAAE;AAEjE,QAAI;AACF,YAAM,SAAS,MAAM,KAAK,aAAa,eAAe,GAAG;AAEzD,UAAI,CAAC,QAAQ;AACX,eAAO,KAAK,iCAAiC,IAAI,GAAG;AACpD,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS;AAAA,UACT,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,MACF;AAGA,aAAO,KAAK,kCAAkC,aAAa,EAAE;AAC7D,YAAM,KAAK,mBAAmB;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,KAAK,OAAO;AAAA,MACd,GAAG,GAAG;AACN,aAAO,QAAQ,sCAAsC;AAErD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,UAAU;AAAA,QACV,YAAY;AAAA,MACd;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,aAAO,MAAM,qCAAqC,YAAY,EAAE;AAChE,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS;AAAA,QACT,UAAU;AAAA,QACV,OAAO;AAAA,QACP,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,0BAA0B,YAAoB,KAAsC;AACxF,UAAM,WAAW,MAAM,KAAK,aAAa,GAAG;AAE5C,eAAW,UAAU,UAAU;AAC7B,UAAI;AACF,cAAM,mBAAmB,MAAM,KAAK,oBAAoB,QAAQ,GAAG;AACnE,cAAM,mBAAmB,KAAK,kBAAkB,gBAAgB;AAEhE,YAAI,qBAAqB,YAAY;AACnC,iBAAO;AAAA,QACT;AAAA,MACF,QAAQ;AAEN;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AC9cO,SAAS,+BAA+B,UAAuC;AAPtF;AAQC,QAAM,cAAa,cAAS,sBAAT,mBAA4B;AAE/C,SAAO,IAAI,aAAa;AAAA,IACvB,YAAW,yCAAY,cAAa;AAAA,IACpC,eAAc,yCAAY,iBAAgB;AAAA,EAC3C,CAAC;AACF;","names":["connectionString"]}
@@ -1,19 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  GitWorktreeManager
4
- } from "./chunk-QEPVTTHD.js";
4
+ } from "./chunk-IO4WFTL2.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-GEHQXLEI.js";
8
8
 
9
9
  // src/lib/ShellCompletion.ts
10
10
  import omelette from "omelette";
11
+ import { readFile } from "fs/promises";
12
+ import { existsSync } from "fs";
13
+ import path from "path";
14
+ import os from "os";
11
15
  var ShellCompletion = class {
12
16
  constructor(commandName) {
13
17
  // omelette instance - no types available
14
18
  this.COMPLETION_TIMEOUT = 1e3;
15
19
  this.commandName = commandName ?? this.detectCommandName();
16
- this.completion = omelette(`${this.commandName} <command> <arg>`);
20
+ this.completion = omelette("iloom|il <command> <arg>");
17
21
  this.setupHandlers();
18
22
  }
19
23
  detectCommandName() {
@@ -171,9 +175,58 @@ Please consult your shell's documentation for setting up custom completions.
171
175
  process.exit(1);
172
176
  }
173
177
  }
178
+ /**
179
+ * Get the shell configuration file path for the given shell type
180
+ */
181
+ getShellConfigPath(shell) {
182
+ const homeDir = os.homedir();
183
+ switch (shell) {
184
+ case "bash": {
185
+ const bashrcPath = path.join(homeDir, ".bashrc");
186
+ const bashProfilePath = path.join(homeDir, ".bash_profile");
187
+ if (existsSync(bashrcPath)) {
188
+ return bashrcPath;
189
+ } else if (existsSync(bashProfilePath)) {
190
+ return bashProfilePath;
191
+ }
192
+ return bashrcPath;
193
+ }
194
+ case "zsh":
195
+ return path.join(homeDir, ".zshrc");
196
+ case "fish":
197
+ return path.join(homeDir, ".config", "fish", "config.fish");
198
+ default:
199
+ return null;
200
+ }
201
+ }
202
+ /**
203
+ * Read the shell configuration file contents
204
+ */
205
+ async readShellConfig(shell) {
206
+ const configPath = this.getShellConfigPath(shell);
207
+ if (!configPath) {
208
+ return null;
209
+ }
210
+ try {
211
+ let content = "";
212
+ if (existsSync(configPath)) {
213
+ content = await readFile(configPath, "utf-8");
214
+ }
215
+ return {
216
+ path: configPath,
217
+ content
218
+ };
219
+ } catch (error) {
220
+ logger.debug(`Failed to read shell config file ${configPath}: ${error}`);
221
+ return {
222
+ path: configPath,
223
+ content: ""
224
+ };
225
+ }
226
+ }
174
227
  };
175
228
 
176
229
  export {
177
230
  ShellCompletion
178
231
  };
179
- //# sourceMappingURL=chunk-O2QWO64Z.js.map
232
+ //# sourceMappingURL=chunk-PV3GAXQO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/ShellCompletion.ts"],"sourcesContent":["import omelette from 'omelette'\nimport { GitWorktreeManager } from './GitWorktreeManager.js'\nimport { logger } from '../utils/logger.js'\nimport { readFile } from 'fs/promises'\nimport { existsSync } from 'fs'\nimport path from 'path'\nimport os from 'os'\n\nexport type ShellType = 'bash' | 'zsh' | 'fish' | 'unknown'\n\n/**\n * Manages shell autocomplete functionality for the iloom CLI\n * Uses omelette to provide tab-completion for commands in bash/zsh/fish\n */\nexport class ShellCompletion {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private completion: any // omelette instance - no types available\n private readonly COMPLETION_TIMEOUT = 1000 // ms - prevent blocking\n private readonly commandName: string\n\n constructor(commandName?: string) {\n // Detect command name from process.argv[1] if not provided\n this.commandName = commandName ?? this.detectCommandName()\n\n // Initialize omelette with both command names using pipe syntax\n // This registers completion for both 'iloom' and 'il' aliases\n // Template covers: <commandName> <command> <arg>\n // This allows for two-level completion: command completion + argument completion\n this.completion = omelette('iloom|il <command> <arg>')\n this.setupHandlers()\n }\n\n private detectCommandName(): string {\n // Get the actual command name used to invoke this script\n const scriptPath = process.argv[1] ?? 'il'\n const baseName = scriptPath.split('/').pop() ?? 'il'\n\n // Remove .js extension if present\n return baseName.replace(/\\.js$/, '')\n }\n\n private setupHandlers(): void {\n // Handler for command-level completion\n // When user types: il <TAB>\n this.completion.on('command', ({ reply }: { reply: (suggestions: string[]) => void }) => {\n reply([\n 'start',\n 'finish',\n 'spin',\n 'ignite',\n 'open',\n 'run',\n 'cleanup',\n 'list',\n 'init',\n // Intentionally exclude test-* commands from autocomplete\n ])\n })\n\n // Handler for argument-level completion\n // When user types: il <command> <TAB>\n this.completion.on('arg', async ({ line, reply }: { line: string; reply: (suggestions: string[]) => void }) => {\n // Check if the command is 'cleanup' to provide dynamic branch suggestions\n if (line.includes('cleanup')) {\n // Use timeout to prevent blocking if worktree listing is slow\n const suggestions = await this.getBranchSuggestionsWithTimeout()\n reply(suggestions)\n } else {\n // For other commands, no argument suggestions\n reply([])\n }\n })\n }\n\n /**\n * Get branch suggestions with timeout to prevent blocking\n */\n private async getBranchSuggestionsWithTimeout(): Promise<string[]> {\n try {\n return await Promise.race([\n this.getBranchSuggestions(),\n this.timeout(this.COMPLETION_TIMEOUT, []),\n ])\n } catch (error) {\n logger.debug(`Autocomplete branch suggestions failed: ${error}`)\n return []\n }\n }\n\n private async timeout<T>(ms: number, defaultValue: T): Promise<T> {\n return new Promise((resolve) => {\n // eslint-disable-next-line no-undef\n setTimeout(() => resolve(defaultValue), ms)\n })\n }\n\n async getBranchSuggestions(): Promise<string[]> {\n // Retrieve worktree branches for dynamic completion\n // Used by cleanup command autocomplete\n try {\n const manager = new GitWorktreeManager()\n const worktrees = await manager.listWorktrees({ porcelain: true })\n const repoInfo = await manager.getRepoInfo()\n\n // Filter out:\n // 1. Main worktree (at repo root) - can't be cleaned up\n // 2. Current worktree (where we're working) - shouldn't clean up current location\n const repoRoot = repoInfo.root\n const currentBranch = repoInfo.currentBranch\n\n return worktrees\n .filter((wt) => wt.path !== repoRoot) // Not the main worktree\n .filter((wt) => wt.branch !== currentBranch) // Not current worktree\n .map((wt) => wt.branch)\n } catch (error) {\n // Silently fail - autocomplete should never break the CLI\n logger.debug(`Failed to get branch suggestions: ${error}`)\n return []\n }\n }\n\n /**\n * Initialize completion - must be called before program.parseAsync()\n */\n init(): void {\n this.completion.init()\n }\n\n /**\n * Detect user's current shell\n */\n detectShell(): ShellType {\n const shell = process.env.SHELL ?? ''\n\n if (shell.includes('bash')) return 'bash'\n if (shell.includes('zsh')) return 'zsh'\n if (shell.includes('fish')) return 'fish'\n\n return 'unknown'\n }\n\n /**\n * Get completion script for a specific shell\n */\n getCompletionScript(shell: ShellType): string {\n switch (shell) {\n case 'bash':\n return this.completion.setupShellInitFile('bash')\n case 'zsh':\n return this.completion.setupShellInitFile('zsh')\n case 'fish':\n return this.completion.setupShellInitFile('fish')\n default:\n throw new Error(`Unsupported shell type: ${shell}`)\n }\n }\n\n /**\n * Get setup instructions for manual installation\n */\n getSetupInstructions(shell: ShellType): string {\n const binaryName = this.commandName\n\n switch (shell) {\n case 'bash':\n return `\nAdd the following to your ~/.bashrc or ~/.bash_profile:\n\n eval \"$(${binaryName} --completion)\"\n\nThen reload your shell:\n\n source ~/.bashrc\n`\n case 'zsh':\n return `\nAdd the following to your ~/.zshrc:\n\n eval \"$(${binaryName} --completion)\"\n\nThen reload your shell:\n\n source ~/.zshrc\n`\n case 'fish':\n return `\nAdd the following to your ~/.config/fish/config.fish:\n\n ${binaryName} --completion | source\n\nThen reload your shell:\n\n source ~/.config/fish/config.fish\n`\n default:\n return `\nShell autocomplete is supported for bash, zsh, and fish.\nYour current shell (${shell}) may not be supported.\n\nPlease consult your shell's documentation for setting up custom completions.\n`\n }\n }\n\n /**\n * Generate completion script and print to stdout\n * Used by: il --completion\n */\n printCompletionScript(shell?: ShellType): void {\n const detectedShell = shell ?? this.detectShell()\n\n if (detectedShell === 'unknown') {\n logger.error('Could not detect shell type. Please specify --shell bash|zsh|fish')\n process.exit(1)\n }\n\n try {\n const script = this.getCompletionScript(detectedShell)\n // eslint-disable-next-line no-console\n console.log(script)\n } catch (error) {\n logger.error(`Failed to generate completion script: ${error}`)\n process.exit(1)\n }\n }\n\n /**\n * Get the shell configuration file path for the given shell type\n */\n getShellConfigPath(shell: ShellType): string | null {\n const homeDir = os.homedir()\n\n switch (shell) {\n case 'bash': {\n // Prefer .bashrc, fall back to .bash_profile\n const bashrcPath = path.join(homeDir, '.bashrc')\n const bashProfilePath = path.join(homeDir, '.bash_profile')\n\n if (existsSync(bashrcPath)) {\n return bashrcPath\n } else if (existsSync(bashProfilePath)) {\n return bashProfilePath\n }\n // Return .bashrc path even if it doesn't exist (for creation)\n return bashrcPath\n }\n\n case 'zsh':\n return path.join(homeDir, '.zshrc')\n\n case 'fish':\n return path.join(homeDir, '.config', 'fish', 'config.fish')\n\n default:\n return null\n }\n }\n\n /**\n * Read the shell configuration file contents\n */\n async readShellConfig(shell: ShellType): Promise<{ path: string; content: string } | null> {\n const configPath = this.getShellConfigPath(shell)\n\n if (!configPath) {\n return null\n }\n\n try {\n let content = ''\n if (existsSync(configPath)) {\n content = await readFile(configPath, 'utf-8')\n }\n // Return the path and content (empty string if file doesn't exist)\n return {\n path: configPath,\n content\n }\n } catch (error) {\n logger.debug(`Failed to read shell config file ${configPath}: ${error}`)\n return {\n path: configPath,\n content: ''\n }\n }\n }\n\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-GEHQXLEI.js";
5
+
6
+ // src/lib/PromptTemplateManager.ts
7
+ import { readFile } from "fs/promises";
8
+ import { accessSync } from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ var PromptTemplateManager = class {
12
+ constructor(templateDir) {
13
+ if (templateDir) {
14
+ this.templateDir = templateDir;
15
+ } else {
16
+ const currentFileUrl = import.meta.url;
17
+ const currentFilePath = fileURLToPath(currentFileUrl);
18
+ const distDir = path.dirname(currentFilePath);
19
+ let templateDir2 = path.join(distDir, "prompts");
20
+ let currentDir = distDir;
21
+ while (currentDir !== path.dirname(currentDir)) {
22
+ const candidatePath = path.join(currentDir, "prompts");
23
+ try {
24
+ accessSync(candidatePath);
25
+ templateDir2 = candidatePath;
26
+ break;
27
+ } catch {
28
+ currentDir = path.dirname(currentDir);
29
+ }
30
+ }
31
+ this.templateDir = templateDir2;
32
+ logger.debug("PromptTemplateManager initialized", {
33
+ currentFilePath,
34
+ distDir,
35
+ templateDir: this.templateDir
36
+ });
37
+ }
38
+ }
39
+ /**
40
+ * Load a template file by name
41
+ */
42
+ async loadTemplate(templateName) {
43
+ const templatePath = path.join(this.templateDir, `${templateName}-prompt.txt`);
44
+ logger.debug("Loading template", {
45
+ templateName,
46
+ templateDir: this.templateDir,
47
+ templatePath
48
+ });
49
+ try {
50
+ return await readFile(templatePath, "utf-8");
51
+ } catch (error) {
52
+ logger.error("Failed to load template", { templateName, templatePath, error });
53
+ throw new Error(`Template not found: ${templatePath}`);
54
+ }
55
+ }
56
+ /**
57
+ * Substitute variables in a template string
58
+ */
59
+ substituteVariables(template, variables) {
60
+ let result = template;
61
+ result = this.processConditionalSections(result, variables);
62
+ if (variables.ISSUE_NUMBER !== void 0) {
63
+ result = result.replace(/ISSUE_NUMBER/g, String(variables.ISSUE_NUMBER));
64
+ }
65
+ if (variables.PR_NUMBER !== void 0) {
66
+ result = result.replace(/PR_NUMBER/g, String(variables.PR_NUMBER));
67
+ }
68
+ if (variables.ISSUE_TITLE !== void 0) {
69
+ result = result.replace(/ISSUE_TITLE/g, variables.ISSUE_TITLE);
70
+ }
71
+ if (variables.PR_TITLE !== void 0) {
72
+ result = result.replace(/PR_TITLE/g, variables.PR_TITLE);
73
+ }
74
+ if (variables.WORKSPACE_PATH !== void 0) {
75
+ result = result.replace(/WORKSPACE_PATH/g, variables.WORKSPACE_PATH);
76
+ }
77
+ if (variables.PORT !== void 0) {
78
+ result = result.replace(/PORT/g, String(variables.PORT));
79
+ }
80
+ if (variables.SETTINGS_SCHEMA !== void 0) {
81
+ result = result.replace(/SETTINGS_SCHEMA/g, variables.SETTINGS_SCHEMA);
82
+ }
83
+ if (variables.SETTINGS_JSON !== void 0) {
84
+ result = result.replace(/SETTINGS_JSON/g, variables.SETTINGS_JSON);
85
+ }
86
+ if (variables.SETTINGS_LOCAL_JSON !== void 0) {
87
+ result = result.replace(/SETTINGS_LOCAL_JSON/g, variables.SETTINGS_LOCAL_JSON);
88
+ }
89
+ if (variables.SHELL_TYPE !== void 0) {
90
+ result = result.replace(/SHELL_TYPE/g, variables.SHELL_TYPE);
91
+ }
92
+ if (variables.SHELL_CONFIG_PATH !== void 0) {
93
+ result = result.replace(/SHELL_CONFIG_PATH/g, variables.SHELL_CONFIG_PATH);
94
+ }
95
+ if (variables.SHELL_CONFIG_CONTENT !== void 0) {
96
+ result = result.replace(/SHELL_CONFIG_CONTENT/g, variables.SHELL_CONFIG_CONTENT);
97
+ }
98
+ if (variables.REMOTES_INFO !== void 0) {
99
+ result = result.replace(/REMOTES_INFO/g, variables.REMOTES_INFO);
100
+ }
101
+ if (variables.MULTIPLE_REMOTES !== void 0) {
102
+ result = result.replace(/MULTIPLE_REMOTES/g, variables.MULTIPLE_REMOTES);
103
+ }
104
+ if (variables.SINGLE_REMOTE !== void 0) {
105
+ result = result.replace(/SINGLE_REMOTE/g, variables.SINGLE_REMOTE);
106
+ }
107
+ if (variables.SINGLE_REMOTE_NAME !== void 0) {
108
+ result = result.replace(/SINGLE_REMOTE_NAME/g, variables.SINGLE_REMOTE_NAME);
109
+ }
110
+ if (variables.SINGLE_REMOTE_URL !== void 0) {
111
+ result = result.replace(/SINGLE_REMOTE_URL/g, variables.SINGLE_REMOTE_URL);
112
+ }
113
+ if (variables.NO_REMOTES !== void 0) {
114
+ result = result.replace(/NO_REMOTES/g, variables.NO_REMOTES);
115
+ }
116
+ if (variables.README_CONTENT !== void 0) {
117
+ result = result.replace(/README_CONTENT/g, variables.README_CONTENT);
118
+ }
119
+ if (variables.SETTINGS_SCHEMA_CONTENT !== void 0) {
120
+ result = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT);
121
+ }
122
+ return result;
123
+ }
124
+ /**
125
+ * Process conditional sections in template
126
+ * Format: {{#IF ONE_SHOT_MODE}}content{{/IF ONE_SHOT_MODE}}
127
+ *
128
+ * Note: /s flag allows . to match newlines
129
+ */
130
+ processConditionalSections(template, variables) {
131
+ let result = template;
132
+ const oneShotRegex = /\{\{#IF ONE_SHOT_MODE\}\}(.*?)\{\{\/IF ONE_SHOT_MODE\}\}/gs;
133
+ if (variables.ONE_SHOT_MODE === true) {
134
+ result = result.replace(oneShotRegex, "$1");
135
+ } else {
136
+ result = result.replace(oneShotRegex, "");
137
+ }
138
+ const settingsJsonRegex = /\{\{#IF SETTINGS_JSON\}\}(.*?)\{\{\/IF SETTINGS_JSON\}\}/gs;
139
+ if (variables.SETTINGS_JSON !== void 0 && variables.SETTINGS_JSON !== "") {
140
+ result = result.replace(settingsJsonRegex, "$1");
141
+ } else {
142
+ result = result.replace(settingsJsonRegex, "");
143
+ }
144
+ const settingsLocalJsonRegex = /\{\{#IF SETTINGS_LOCAL_JSON\}\}(.*?)\{\{\/IF SETTINGS_LOCAL_JSON\}\}/gs;
145
+ if (variables.SETTINGS_LOCAL_JSON !== void 0 && variables.SETTINGS_LOCAL_JSON !== "") {
146
+ result = result.replace(settingsLocalJsonRegex, "$1");
147
+ } else {
148
+ result = result.replace(settingsLocalJsonRegex, "");
149
+ }
150
+ const multipleRemotesRegex = /\{\{#IF MULTIPLE_REMOTES\}\}(.*?)\{\{\/IF MULTIPLE_REMOTES\}\}/gs;
151
+ if (variables.MULTIPLE_REMOTES !== void 0 && variables.MULTIPLE_REMOTES !== "") {
152
+ result = result.replace(multipleRemotesRegex, "$1");
153
+ } else {
154
+ result = result.replace(multipleRemotesRegex, "");
155
+ }
156
+ const singleRemoteRegex = /\{\{#IF SINGLE_REMOTE\}\}(.*?)\{\{\/IF SINGLE_REMOTE\}\}/gs;
157
+ if (variables.SINGLE_REMOTE !== void 0 && variables.SINGLE_REMOTE !== "") {
158
+ result = result.replace(singleRemoteRegex, "$1");
159
+ } else {
160
+ result = result.replace(singleRemoteRegex, "");
161
+ }
162
+ const noRemotesRegex = /\{\{#IF NO_REMOTES\}\}(.*?)\{\{\/IF NO_REMOTES\}\}/gs;
163
+ if (variables.NO_REMOTES !== void 0 && variables.NO_REMOTES !== "") {
164
+ result = result.replace(noRemotesRegex, "$1");
165
+ } else {
166
+ result = result.replace(noRemotesRegex, "");
167
+ }
168
+ const firstTimeUserRegex = /\{\{#IF FIRST_TIME_USER\}\}(.*?)\{\{\/IF FIRST_TIME_USER\}\}/gs;
169
+ if (variables.FIRST_TIME_USER === true) {
170
+ result = result.replace(firstTimeUserRegex, "$1");
171
+ } else {
172
+ result = result.replace(firstTimeUserRegex, "");
173
+ }
174
+ return result;
175
+ }
176
+ /**
177
+ * Get a fully processed prompt for a workflow type
178
+ */
179
+ async getPrompt(type, variables) {
180
+ const template = await this.loadTemplate(type);
181
+ return this.substituteVariables(template, variables);
182
+ }
183
+ };
184
+
185
+ export {
186
+ PromptTemplateManager
187
+ };
188
+ //# sourceMappingURL=chunk-PVAVNJKS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/PromptTemplateManager.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { logger } from '../utils/logger.js'\n\nexport interface TemplateVariables {\n\tISSUE_NUMBER?: number\n\tPR_NUMBER?: number\n\tISSUE_TITLE?: string\n\tPR_TITLE?: string\n\tWORKSPACE_PATH?: string\n\tPORT?: number\n\tONE_SHOT_MODE?: boolean\n\tSETTINGS_SCHEMA?: string\n\tSETTINGS_JSON?: string\n\tSETTINGS_LOCAL_JSON?: string\n\tSHELL_TYPE?: string\n\tSHELL_CONFIG_PATH?: string\n\tSHELL_CONFIG_CONTENT?: string\n\tREMOTES_INFO?: string\n\tMULTIPLE_REMOTES?: string\n\tSINGLE_REMOTE?: string\n\tSINGLE_REMOTE_NAME?: string\n\tSINGLE_REMOTE_URL?: string\n\tNO_REMOTES?: string\n\tREADME_CONTENT?: string\n\tSETTINGS_SCHEMA_CONTENT?: string\n\tFIRST_TIME_USER?: boolean\n}\n\nexport class PromptTemplateManager {\n\tprivate templateDir: string\n\n\tconstructor(templateDir?: string) {\n\t\tif (templateDir) {\n\t\t\tthis.templateDir = templateDir\n\t\t} else {\n\t\t\t// Find templates relative to the package installation\n\t\t\t// When running from dist/, templates are copied to dist/prompts/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath) // dist directory (may be chunked file location)\n\n\t\t\t// Walk up to find the dist directory (in case of chunked files)\n\t\t\tlet templateDir = path.join(distDir, 'prompts')\n\t\t\tlet currentDir = distDir\n\n\t\t\t// Try to find the prompts directory by walking up\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'prompts')\n\t\t\t\ttry {\n\t\t\t\t\t// Check if this directory exists (sync check for constructor)\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\ttemplateDir = candidatePath\n\t\t\t\t\tbreak\n\t\t\t\t} catch {\n\t\t\t\t\tcurrentDir = path.dirname(currentDir)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.templateDir = templateDir\n\t\t\tlogger.debug('PromptTemplateManager initialized', {\n\t\t\t\tcurrentFilePath,\n\t\t\t\tdistDir,\n\t\t\t\ttemplateDir: this.templateDir\n\t\t\t})\n\t\t}\n\t}\n\n\t/**\n\t * Load a template file by name\n\t */\n\tasync loadTemplate(templateName: 'issue' | 'pr' | 'regular' | 'init'): Promise<string> {\n\t\tconst templatePath = path.join(this.templateDir, `${templateName}-prompt.txt`)\n\n\t\tlogger.debug('Loading template', {\n\t\t\ttemplateName,\n\t\t\ttemplateDir: this.templateDir,\n\t\t\ttemplatePath\n\t\t})\n\n\t\ttry {\n\t\t\treturn await readFile(templatePath, 'utf-8')\n\t\t} catch (error) {\n\t\t\tlogger.error('Failed to load template', { templateName, templatePath, error })\n\t\t\tthrow new Error(`Template not found: ${templatePath}`)\n\t\t}\n\t}\n\n\t/**\n\t * Substitute variables in a template string\n\t */\n\tsubstituteVariables(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process conditional sections first\n\t\tresult = this.processConditionalSections(result, variables)\n\n\t\t// Replace each variable if it exists\n\t\tif (variables.ISSUE_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_NUMBER/g, String(variables.ISSUE_NUMBER))\n\t\t}\n\n\t\tif (variables.PR_NUMBER !== undefined) {\n\t\t\tresult = result.replace(/PR_NUMBER/g, String(variables.PR_NUMBER))\n\t\t}\n\n\t\tif (variables.ISSUE_TITLE !== undefined) {\n\t\t\tresult = result.replace(/ISSUE_TITLE/g, variables.ISSUE_TITLE)\n\t\t}\n\n\t\tif (variables.PR_TITLE !== undefined) {\n\t\t\tresult = result.replace(/PR_TITLE/g, variables.PR_TITLE)\n\t\t}\n\n\t\tif (variables.WORKSPACE_PATH !== undefined) {\n\t\t\tresult = result.replace(/WORKSPACE_PATH/g, variables.WORKSPACE_PATH)\n\t\t}\n\n\t\tif (variables.PORT !== undefined) {\n\t\t\tresult = result.replace(/PORT/g, String(variables.PORT))\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA/g, variables.SETTINGS_SCHEMA)\n\t\t}\n\n\t\tif (variables.SETTINGS_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_JSON/g, variables.SETTINGS_JSON)\n\t\t}\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_LOCAL_JSON/g, variables.SETTINGS_LOCAL_JSON)\n\t\t}\n\n\t\tif (variables.SHELL_TYPE !== undefined) {\n\t\t\tresult = result.replace(/SHELL_TYPE/g, variables.SHELL_TYPE)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_PATH !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_PATH/g, variables.SHELL_CONFIG_PATH)\n\t\t}\n\n\t\tif (variables.SHELL_CONFIG_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SHELL_CONFIG_CONTENT/g, variables.SHELL_CONFIG_CONTENT)\n\t\t}\n\n\t\tif (variables.REMOTES_INFO !== undefined) {\n\t\t\tresult = result.replace(/REMOTES_INFO/g, variables.REMOTES_INFO)\n\t\t}\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/MULTIPLE_REMOTES/g, variables.MULTIPLE_REMOTES)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE/g, variables.SINGLE_REMOTE)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_NAME !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_NAME/g, variables.SINGLE_REMOTE_NAME)\n\t\t}\n\n\t\tif (variables.SINGLE_REMOTE_URL !== undefined) {\n\t\t\tresult = result.replace(/SINGLE_REMOTE_URL/g, variables.SINGLE_REMOTE_URL)\n\t\t}\n\n\t\tif (variables.NO_REMOTES !== undefined) {\n\t\t\tresult = result.replace(/NO_REMOTES/g, variables.NO_REMOTES)\n\t\t}\n\n\t\tif (variables.README_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/README_CONTENT/g, variables.README_CONTENT)\n\t\t}\n\n\t\tif (variables.SETTINGS_SCHEMA_CONTENT !== undefined) {\n\t\t\tresult = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT)\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Process conditional sections in template\n\t * Format: {{#IF ONE_SHOT_MODE}}content{{/IF ONE_SHOT_MODE}}\n\t *\n\t * Note: /s flag allows . to match newlines\n\t */\n\tprivate processConditionalSections(template: string, variables: TemplateVariables): string {\n\t\tlet result = template\n\n\t\t// Process ONE_SHOT_MODE conditionals\n\t\tconst oneShotRegex = /\\{\\{#IF ONE_SHOT_MODE\\}\\}(.*?)\\{\\{\\/IF ONE_SHOT_MODE\\}\\}/gs\n\n\t\tif (variables.ONE_SHOT_MODE === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(oneShotRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(oneShotRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_JSON conditionals\n\t\tconst settingsJsonRegex = /\\{\\{#IF SETTINGS_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_JSON !== undefined && variables.SETTINGS_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsJsonRegex, '')\n\t\t}\n\n\t\t// Process SETTINGS_LOCAL_JSON conditionals\n\t\tconst settingsLocalJsonRegex = /\\{\\{#IF SETTINGS_LOCAL_JSON\\}\\}(.*?)\\{\\{\\/IF SETTINGS_LOCAL_JSON\\}\\}/gs\n\n\t\tif (variables.SETTINGS_LOCAL_JSON !== undefined && variables.SETTINGS_LOCAL_JSON !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(settingsLocalJsonRegex, '')\n\t\t}\n\n\t\t// Process MULTIPLE_REMOTES conditionals\n\t\tconst multipleRemotesRegex = /\\{\\{#IF MULTIPLE_REMOTES\\}\\}(.*?)\\{\\{\\/IF MULTIPLE_REMOTES\\}\\}/gs\n\n\t\tif (variables.MULTIPLE_REMOTES !== undefined && variables.MULTIPLE_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(multipleRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(multipleRemotesRegex, '')\n\t\t}\n\n\t\t// Process SINGLE_REMOTE conditionals\n\t\tconst singleRemoteRegex = /\\{\\{#IF SINGLE_REMOTE\\}\\}(.*?)\\{\\{\\/IF SINGLE_REMOTE\\}\\}/gs\n\n\t\tif (variables.SINGLE_REMOTE !== undefined && variables.SINGLE_REMOTE !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(singleRemoteRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(singleRemoteRegex, '')\n\t\t}\n\n\t\t// Process NO_REMOTES conditionals\n\t\tconst noRemotesRegex = /\\{\\{#IF NO_REMOTES\\}\\}(.*?)\\{\\{\\/IF NO_REMOTES\\}\\}/gs\n\n\t\tif (variables.NO_REMOTES !== undefined && variables.NO_REMOTES !== '') {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(noRemotesRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(noRemotesRegex, '')\n\t\t}\n\n\t\t// Process FIRST_TIME_USER conditionals\n\t\tconst firstTimeUserRegex = /\\{\\{#IF FIRST_TIME_USER\\}\\}(.*?)\\{\\{\\/IF FIRST_TIME_USER\\}\\}/gs\n\n\t\tif (variables.FIRST_TIME_USER === true) {\n\t\t\t// Include the content, remove the conditional markers\n\t\t\tresult = result.replace(firstTimeUserRegex, '$1')\n\t\t} else {\n\t\t\t// Remove the entire conditional block\n\t\t\tresult = result.replace(firstTimeUserRegex, '')\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Get a fully processed prompt for a workflow type\n\t */\n\tasync getPrompt(\n\t\ttype: 'issue' | 'pr' | 'regular' | 'init',\n\t\tvariables: TemplateVariables\n\t): Promise<string> {\n\t\tconst template = await this.loadTemplate(type)\n\t\treturn this.substituteVariables(template, variables)\n\t}\n}\n"],"mappings":"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","names":["templateDir"]}