@iloom/cli 0.4.0 → 0.5.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.
Files changed (135) hide show
  1. package/README.md +24 -0
  2. package/dist/{BranchNamingService-TOM2KAUT.js → BranchNamingService-GCCWB3LK.js} +2 -2
  3. package/dist/ClaudeContextManager-DQFKIMEP.js +16 -0
  4. package/dist/ClaudeService-CJS32WG2.js +15 -0
  5. package/dist/{LoomLauncher-SJBZFZXE.js → LoomLauncher-4UG2E4CD.js} +40 -32
  6. package/dist/LoomLauncher-4UG2E4CD.js.map +1 -0
  7. package/dist/MetadataManager-WXUVXKUS.js +10 -0
  8. package/dist/PRManager-7DSIMCAD.js +16 -0
  9. package/dist/{PromptTemplateManager-2TDZAUC6.js → PromptTemplateManager-72FEOGT6.js} +2 -2
  10. package/dist/README.md +24 -0
  11. package/dist/{SettingsManager-FJFU6JJD.js → SettingsManager-XPR4TEQL.js} +2 -2
  12. package/dist/agents/iloom-issue-analyze-and-plan.md +41 -7
  13. package/dist/agents/iloom-issue-analyzer.md +38 -8
  14. package/dist/agents/iloom-issue-complexity-evaluator.md +45 -15
  15. package/dist/agents/iloom-issue-enhancer.md +60 -18
  16. package/dist/agents/iloom-issue-implementer.md +29 -7
  17. package/dist/agents/iloom-issue-planner.md +36 -7
  18. package/dist/agents/iloom-issue-reviewer.md +30 -7
  19. package/dist/{chunk-M5XUCTTJ.js → chunk-3CMGCRB5.js} +2 -2
  20. package/dist/{chunk-KM3W7YQX.js → chunk-4YTILIIH.js} +8 -9
  21. package/dist/chunk-4YTILIIH.js.map +1 -0
  22. package/dist/{chunk-HVGQP44L.js → chunk-AS2IRKLU.js} +2 -2
  23. package/dist/{chunk-ADDNFQJ4.js → chunk-CDQEK2WD.js} +6 -6
  24. package/dist/{chunk-74VMN2KC.js → chunk-DKQ4SUII.js} +16 -1
  25. package/dist/chunk-DKQ4SUII.js.map +1 -0
  26. package/dist/{chunk-LTNDJMTH.js → chunk-GVRO4PWE.js} +13 -9
  27. package/dist/chunk-GVRO4PWE.js.map +1 -0
  28. package/dist/{chunk-HHDSIE72.js → chunk-HABINPX2.js} +72 -16
  29. package/dist/{chunk-HHDSIE72.js.map → chunk-HABINPX2.js.map} +1 -1
  30. package/dist/{chunk-TR5MC2U6.js → chunk-LN4H3A6A.js} +66 -7
  31. package/dist/chunk-LN4H3A6A.js.map +1 -0
  32. package/dist/{chunk-VWNS6DH5.js → chunk-OOU3DKNT.js} +13 -7
  33. package/dist/chunk-OOU3DKNT.js.map +1 -0
  34. package/dist/chunk-P2ZQ5LKB.js +347 -0
  35. package/dist/chunk-P2ZQ5LKB.js.map +1 -0
  36. package/dist/{chunk-P2WZIDF3.js → chunk-QIUJPPJQ.js} +2 -2
  37. package/dist/{chunk-HD5SUKI2.js → chunk-RFUOIUQF.js} +49 -6
  38. package/dist/{chunk-HD5SUKI2.js.map → chunk-RFUOIUQF.js.map} +1 -1
  39. package/dist/{chunk-OF7BNW4D.js → chunk-RJKMF6BC.js} +30 -4
  40. package/dist/chunk-RJKMF6BC.js.map +1 -0
  41. package/dist/{chunk-O7WHXLCB.js → chunk-RNZMHJK7.js} +18 -4
  42. package/dist/chunk-RNZMHJK7.js.map +1 -0
  43. package/dist/{chunk-75B2HZZ5.js → chunk-RUC7OULH.js} +2 -2
  44. package/dist/{chunk-F4J6KEL6.js → chunk-S65T4O6I.js} +2 -2
  45. package/dist/{chunk-NFVFVYAP.js → chunk-T5IIUG4Z.js} +109 -20
  46. package/dist/chunk-T5IIUG4Z.js.map +1 -0
  47. package/dist/{chunk-JJUPY5MM.js → chunk-VAYGNQTE.js} +2 -2
  48. package/dist/{chunk-S44CHE3G.js → chunk-VTXCGKV5.js} +2 -2
  49. package/dist/{chunk-MLS5FAV7.js → chunk-YZTDGPFB.js} +21 -1
  50. package/dist/chunk-YZTDGPFB.js.map +1 -0
  51. package/dist/{chunk-3NFBZRPR.js → chunk-Z5NXYJIG.js} +19 -1
  52. package/dist/chunk-Z5NXYJIG.js.map +1 -0
  53. package/dist/{claude-X7EBJRB2.js → claude-ACVXNB6N.js} +4 -4
  54. package/dist/{cleanup-7QVPYBJJ.js → cleanup-MIDJVSIU.js} +18 -18
  55. package/dist/cli.js +293 -372
  56. package/dist/cli.js.map +1 -1
  57. package/dist/{contribute-RZYCYUDX.js → contribute-RS3DO3WP.js} +4 -4
  58. package/dist/{dev-server-LOY7YWCP.js → dev-server-ASH7HJVI.js} +28 -14
  59. package/dist/dev-server-ASH7HJVI.js.map +1 -0
  60. package/dist/{feedback-562KPG5U.js → feedback-RVIGHBJG.js} +9 -8
  61. package/dist/{feedback-562KPG5U.js.map → feedback-RVIGHBJG.js.map} +1 -1
  62. package/dist/{git-OXJACVAU.js → git-OQAPUPLP.js} +16 -6
  63. package/dist/git-OQAPUPLP.js.map +1 -0
  64. package/dist/{ignite-VSIPGKKG.js → ignite-XJALWFAT.js} +60 -25
  65. package/dist/ignite-XJALWFAT.js.map +1 -0
  66. package/dist/index.d.ts +60 -7
  67. package/dist/index.js +107 -7
  68. package/dist/index.js.map +1 -1
  69. package/dist/init-F6PFMSU5.js +21 -0
  70. package/dist/init-F6PFMSU5.js.map +1 -0
  71. package/dist/mcp/recap-server.js +264 -0
  72. package/dist/mcp/recap-server.js.map +1 -0
  73. package/dist/{open-CX7HUE26.js → open-KW4NTLXH.js} +15 -16
  74. package/dist/{open-CX7HUE26.js.map → open-KW4NTLXH.js.map} +1 -1
  75. package/dist/{projects-6DTNDVLH.js → projects-QEAEBAT2.js} +2 -2
  76. package/dist/prompts/init-prompt.txt +31 -72
  77. package/dist/prompts/issue-prompt.txt +115 -15
  78. package/dist/prompts/pr-prompt.txt +49 -1
  79. package/dist/prompts/regular-prompt.txt +80 -20
  80. package/dist/{rebase-55URTXZC.js → rebase-WZHHE5LU.js} +9 -9
  81. package/dist/recap-33NPZ3ZO.js +117 -0
  82. package/dist/recap-33NPZ3ZO.js.map +1 -0
  83. package/dist/{run-DP2U2CA2.js → run-HRYQ7TR7.js} +15 -16
  84. package/dist/{run-DP2U2CA2.js.map → run-HRYQ7TR7.js.map} +1 -1
  85. package/dist/schema/settings.schema.json +13 -2
  86. package/dist/shell-JMU5XTHW.js +240 -0
  87. package/dist/shell-JMU5XTHW.js.map +1 -0
  88. package/dist/{summary-J3CJSM7L.js → summary-4SSGGH7N.js} +20 -12
  89. package/dist/summary-4SSGGH7N.js.map +1 -0
  90. package/dist/{test-git-QLAIBJLX.js → test-git-6SAIRBUD.js} +4 -4
  91. package/dist/{test-prefix-6YM2ZOON.js → test-prefix-RLVRK5ZD.js} +4 -4
  92. package/dist/{test-tabs-JGO3VOXJ.js → test-tabs-3SCJWRKT.js} +3 -3
  93. package/package.json +1 -1
  94. package/dist/ClaudeContextManager-VEGJTS5E.js +0 -16
  95. package/dist/ClaudeService-ICSHJMQ5.js +0 -15
  96. package/dist/LoomLauncher-SJBZFZXE.js.map +0 -1
  97. package/dist/chunk-3NFBZRPR.js.map +0 -1
  98. package/dist/chunk-74VMN2KC.js.map +0 -1
  99. package/dist/chunk-KM3W7YQX.js.map +0 -1
  100. package/dist/chunk-LTNDJMTH.js.map +0 -1
  101. package/dist/chunk-MLS5FAV7.js.map +0 -1
  102. package/dist/chunk-NFVFVYAP.js.map +0 -1
  103. package/dist/chunk-O7WHXLCB.js.map +0 -1
  104. package/dist/chunk-OF7BNW4D.js.map +0 -1
  105. package/dist/chunk-QRBOPFAA.js +0 -48
  106. package/dist/chunk-QRBOPFAA.js.map +0 -1
  107. package/dist/chunk-TR5MC2U6.js.map +0 -1
  108. package/dist/chunk-VWNS6DH5.js.map +0 -1
  109. package/dist/dev-server-LOY7YWCP.js.map +0 -1
  110. package/dist/ignite-VSIPGKKG.js.map +0 -1
  111. package/dist/init-SCR2LQ4A.js +0 -21
  112. package/dist/summary-J3CJSM7L.js.map +0 -1
  113. /package/dist/{BranchNamingService-TOM2KAUT.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
  114. /package/dist/{ClaudeContextManager-VEGJTS5E.js.map → ClaudeContextManager-DQFKIMEP.js.map} +0 -0
  115. /package/dist/{ClaudeService-ICSHJMQ5.js.map → ClaudeService-CJS32WG2.js.map} +0 -0
  116. /package/dist/{PromptTemplateManager-2TDZAUC6.js.map → MetadataManager-WXUVXKUS.js.map} +0 -0
  117. /package/dist/{SettingsManager-FJFU6JJD.js.map → PRManager-7DSIMCAD.js.map} +0 -0
  118. /package/dist/{claude-X7EBJRB2.js.map → PromptTemplateManager-72FEOGT6.js.map} +0 -0
  119. /package/dist/{git-OXJACVAU.js.map → SettingsManager-XPR4TEQL.js.map} +0 -0
  120. /package/dist/{chunk-M5XUCTTJ.js.map → chunk-3CMGCRB5.js.map} +0 -0
  121. /package/dist/{chunk-HVGQP44L.js.map → chunk-AS2IRKLU.js.map} +0 -0
  122. /package/dist/{chunk-ADDNFQJ4.js.map → chunk-CDQEK2WD.js.map} +0 -0
  123. /package/dist/{chunk-P2WZIDF3.js.map → chunk-QIUJPPJQ.js.map} +0 -0
  124. /package/dist/{chunk-75B2HZZ5.js.map → chunk-RUC7OULH.js.map} +0 -0
  125. /package/dist/{chunk-F4J6KEL6.js.map → chunk-S65T4O6I.js.map} +0 -0
  126. /package/dist/{chunk-JJUPY5MM.js.map → chunk-VAYGNQTE.js.map} +0 -0
  127. /package/dist/{chunk-S44CHE3G.js.map → chunk-VTXCGKV5.js.map} +0 -0
  128. /package/dist/{init-SCR2LQ4A.js.map → claude-ACVXNB6N.js.map} +0 -0
  129. /package/dist/{cleanup-7QVPYBJJ.js.map → cleanup-MIDJVSIU.js.map} +0 -0
  130. /package/dist/{contribute-RZYCYUDX.js.map → contribute-RS3DO3WP.js.map} +0 -0
  131. /package/dist/{projects-6DTNDVLH.js.map → projects-QEAEBAT2.js.map} +0 -0
  132. /package/dist/{rebase-55URTXZC.js.map → rebase-WZHHE5LU.js.map} +0 -0
  133. /package/dist/{test-git-QLAIBJLX.js.map → test-git-6SAIRBUD.js.map} +0 -0
  134. /package/dist/{test-prefix-6YM2ZOON.js.map → test-prefix-RLVRK5ZD.js.map} +0 -0
  135. /package/dist/{test-tabs-JGO3VOXJ.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ IdentifierParser
4
+ } from "./chunk-AS2IRKLU.js";
5
+ import {
6
+ GitWorktreeManager
7
+ } from "./chunk-3CMGCRB5.js";
8
+ import "./chunk-LN4H3A6A.js";
9
+ import "./chunk-OOU3DKNT.js";
10
+ import "./chunk-YZTDGPFB.js";
11
+ import "./chunk-6UIGZD2N.js";
12
+ import "./chunk-UYVWLISQ.js";
13
+
14
+ // src/commands/recap.ts
15
+ import path from "path";
16
+ import os from "os";
17
+ import fs from "fs-extra";
18
+ var RECAPS_DIR = path.join(os.homedir(), ".config", "iloom-ai", "recaps");
19
+ function slugifyPath(loomPath) {
20
+ let slug = loomPath.replace(/[/\\]+$/, "");
21
+ slug = slug.replace(/[/\\]/g, "___");
22
+ slug = slug.replace(/[^a-zA-Z0-9_-]/g, "-");
23
+ return `${slug}.json`;
24
+ }
25
+ var RecapCommand = class {
26
+ /**
27
+ * Execute the recap command
28
+ * Returns RecapOutput in JSON mode, void otherwise
29
+ */
30
+ async execute(input) {
31
+ const loomPath = await this.resolveLoomPath(input.identifier);
32
+ const filePath = path.join(RECAPS_DIR, slugifyPath(loomPath));
33
+ let recap = {};
34
+ try {
35
+ if (await fs.pathExists(filePath)) {
36
+ const content = await fs.readFile(filePath, "utf8");
37
+ recap = JSON.parse(content);
38
+ }
39
+ } catch {
40
+ }
41
+ const goal = recap.goal ?? null;
42
+ const complexity = recap.complexity ?? null;
43
+ const entries = recap.entries ?? [];
44
+ const artifacts = recap.artifacts ?? [];
45
+ const result = { filePath, goal, complexity, entries, artifacts };
46
+ if (input.json) {
47
+ return result;
48
+ }
49
+ console.log(`Recap file: ${filePath}`);
50
+ console.log(`Goal: ${goal ?? "(not set)"}`);
51
+ console.log(`Complexity: ${complexity ? `${complexity.level}${complexity.reason ? ` - ${complexity.reason}` : ""}` : "(not set)"}`);
52
+ console.log(`Entries: ${entries.length}`);
53
+ for (const entry of entries) {
54
+ console.log(` [${entry.type}] ${entry.content}`);
55
+ }
56
+ console.log(`Artifacts: ${artifacts.length}`);
57
+ for (const artifact of artifacts) {
58
+ console.log(` [${artifact.type}] ${artifact.description} - ${artifact.primaryUrl}`);
59
+ }
60
+ }
61
+ /**
62
+ * Resolve identifier to loom path
63
+ * Falls back to cwd when no identifier is provided (backward compatible)
64
+ */
65
+ async resolveLoomPath(identifier) {
66
+ if (!(identifier == null ? void 0 : identifier.trim())) {
67
+ return process.cwd();
68
+ }
69
+ const trimmedId = identifier.trim();
70
+ const gitWorktreeManager = new GitWorktreeManager();
71
+ const identifierParser = new IdentifierParser(gitWorktreeManager);
72
+ const prPattern = /^(?:pr|PR)[/-](\d+)$/;
73
+ const prMatch = trimmedId.match(prPattern);
74
+ if (prMatch == null ? void 0 : prMatch[1]) {
75
+ const prNumber = parseInt(prMatch[1], 10);
76
+ const worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, "");
77
+ if (worktree) {
78
+ return worktree.path;
79
+ }
80
+ throw new Error(`No worktree found for PR #${prNumber}`);
81
+ }
82
+ try {
83
+ const parsed = await identifierParser.parseForPatternDetection(trimmedId);
84
+ if (parsed.type === "pr" && typeof parsed.number === "number") {
85
+ const worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, "");
86
+ if (worktree) {
87
+ return worktree.path;
88
+ }
89
+ throw new Error(`No worktree found for PR #${parsed.number}`);
90
+ }
91
+ if (parsed.type === "issue" && parsed.number !== void 0) {
92
+ const worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number);
93
+ if (worktree) {
94
+ return worktree.path;
95
+ }
96
+ throw new Error(`No worktree found for issue #${parsed.number}`);
97
+ }
98
+ if (parsed.type === "branch" && parsed.branchName) {
99
+ const worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName);
100
+ if (worktree) {
101
+ return worktree.path;
102
+ }
103
+ throw new Error(`No worktree found for branch: ${parsed.branchName}`);
104
+ }
105
+ } catch (error) {
106
+ if (error instanceof Error) {
107
+ throw new Error(`Could not resolve identifier '${identifier}': ${error.message}`);
108
+ }
109
+ throw error;
110
+ }
111
+ throw new Error(`Could not resolve identifier: ${identifier}`);
112
+ }
113
+ };
114
+ export {
115
+ RecapCommand
116
+ };
117
+ //# sourceMappingURL=recap-33NPZ3ZO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/recap.ts"],"sourcesContent":["/**\n * RecapCommand - Fast read-only command for VS Code extension\n *\n * Reads ~/.config/iloom-ai/recaps/{current-loom}.json and outputs it.\n * Skips config validation for fast startup.\n * Includes filePath in output so extension can set up file watcher.\n */\nimport path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport type { RecapFile, RecapOutput } from '../mcp/recap-types.js'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\n\nconst RECAPS_DIR = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm\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 */\nfunction 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\nexport interface RecapCommandInput {\n\tidentifier?: string | undefined // Optional identifier (issue number, PR number, branch name)\n\tjson?: boolean | undefined\n}\n\nexport class RecapCommand {\n\t/**\n\t * Execute the recap command\n\t * Returns RecapOutput in JSON mode, void otherwise\n\t */\n\tasync execute(input: RecapCommandInput): Promise<RecapOutput | void> {\n\t\t// Resolve loom path from identifier or fall back to cwd\n\t\tconst loomPath = await this.resolveLoomPath(input.identifier)\n\t\tconst filePath = path.join(RECAPS_DIR, slugifyPath(loomPath))\n\n\t\t// Read recap file (return empty object if not found)\n\t\tlet recap: RecapFile = {}\n\t\ttry {\n\t\t\tif (await fs.pathExists(filePath)) {\n\t\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\t\trecap = JSON.parse(content) as RecapFile\n\t\t\t}\n\t\t} catch {\n\t\t\t// Graceful degradation - return empty recap on read error\n\t\t\t// This is intentional for fast startup\n\t\t}\n\n\t\t// Build output with filePath for file watching (provide defaults for optional fields)\n\t\tconst goal = recap.goal ?? null\n\t\tconst complexity = recap.complexity ?? null\n\t\tconst entries = recap.entries ?? []\n\t\tconst artifacts = recap.artifacts ?? []\n\t\tconst result: RecapOutput = { filePath, goal, complexity, entries, artifacts }\n\n\t\tif (input.json) {\n\t\t\treturn result\n\t\t}\n\n\t\t// Non-JSON mode: print human-readable format (intentionally using console.log for piping/redirection)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Recap file: ${filePath}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Goal: ${goal ?? '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Complexity: ${complexity ? `${complexity.level}${complexity.reason ? ` - ${complexity.reason}` : ''}` : '(not set)'}`)\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Entries: ${entries.length}`)\n\t\tfor (const entry of entries) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${entry.type}] ${entry.content}`)\n\t\t}\n\t\t// eslint-disable-next-line no-console\n\t\tconsole.log(`Artifacts: ${artifacts.length}`)\n\t\tfor (const artifact of artifacts) {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.log(` [${artifact.type}] ${artifact.description} - ${artifact.primaryUrl}`)\n\t\t}\n\t}\n\n\t/**\n\t * Resolve identifier to loom path\n\t * Falls back to cwd when no identifier is provided (backward compatible)\n\t */\n\tprivate async resolveLoomPath(identifier: string | undefined): Promise<string> {\n\t\t// Default: use current working directory\n\t\tif (!identifier?.trim()) {\n\t\t\treturn process.cwd()\n\t\t}\n\n\t\tconst trimmedId = identifier.trim()\n\t\tconst gitWorktreeManager = new GitWorktreeManager()\n\t\tconst identifierParser = new IdentifierParser(gitWorktreeManager)\n\n\t\t// Check for PR-specific formats: pr/123, PR-123, PR/123\n\t\tconst prPattern = /^(?:pr|PR)[/-](\\d+)$/\n\t\tconst prMatch = trimmedId.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t\tif (worktree) {\n\t\t\t\treturn worktree.path\n\t\t\t}\n\t\t\tthrow new Error(`No worktree found for PR #${prNumber}`)\n\t\t}\n\n\t\t// Use IdentifierParser for pattern-based detection\n\t\ttry {\n\t\t\tconst parsed = await identifierParser.parseForPatternDetection(trimmedId)\n\n\t\t\t// Find worktree based on parsed type\n\t\t\tif (parsed.type === 'pr' && typeof parsed.number === 'number') {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForPR(parsed.number, '')\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for PR #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for issue #${parsed.number}`)\n\t\t\t}\n\n\t\t\tif (parsed.type === 'branch' && parsed.branchName) {\n\t\t\t\tconst worktree = await gitWorktreeManager.findWorktreeForBranch(parsed.branchName)\n\t\t\t\tif (worktree) {\n\t\t\t\t\treturn worktree.path\n\t\t\t\t}\n\t\t\t\tthrow new Error(`No worktree found for branch: ${parsed.branchName}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Re-throw IdentifierParser errors with context\n\t\t\tif (error instanceof Error) {\n\t\t\t\tthrow new Error(`Could not resolve identifier '${identifier}': ${error.message}`)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\t// Should not reach here, but provide a fallback error\n\t\tthrow new Error(`Could not resolve identifier: ${identifier}`)\n\t}\n}\n"],"mappings":"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","names":[]}
@@ -1,40 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DevServerManager
4
- } from "./chunk-OF7BNW4D.js";
5
- import "./chunk-QRBOPFAA.js";
6
- import {
7
- IdentifierParser
8
- } from "./chunk-HVGQP44L.js";
4
+ } from "./chunk-RJKMF6BC.js";
5
+ import "./chunk-VBFDVGAE.js";
9
6
  import {
10
7
  calculatePortForBranch
11
8
  } from "./chunk-VU3QMIP2.js";
12
9
  import {
13
- openBrowser
14
- } from "./chunk-YETJNRQM.js";
15
- import {
16
- GitWorktreeManager
17
- } from "./chunk-M5XUCTTJ.js";
18
- import "./chunk-VBFDVGAE.js";
10
+ IdentifierParser
11
+ } from "./chunk-AS2IRKLU.js";
19
12
  import {
20
13
  ProjectCapabilityDetector
21
14
  } from "./chunk-EBISESAP.js";
22
15
  import "./chunk-2ZPFJQ3B.js";
16
+ import {
17
+ GitWorktreeManager
18
+ } from "./chunk-3CMGCRB5.js";
19
+ import {
20
+ openBrowser
21
+ } from "./chunk-YETJNRQM.js";
23
22
  import {
24
23
  extractSettingsOverrides
25
24
  } from "./chunk-GYCR2LOU.js";
26
25
  import {
27
26
  extractIssueNumber
28
- } from "./chunk-TR5MC2U6.js";
29
- import "./chunk-MLS5FAV7.js";
27
+ } from "./chunk-LN4H3A6A.js";
30
28
  import {
31
29
  SettingsManager
32
- } from "./chunk-VWNS6DH5.js";
30
+ } from "./chunk-OOU3DKNT.js";
31
+ import "./chunk-YZTDGPFB.js";
33
32
  import {
34
33
  extractPort,
35
34
  findEnvFileContainingVariable,
36
35
  parseEnvFile
37
- } from "./chunk-3NFBZRPR.js";
36
+ } from "./chunk-Z5NXYJIG.js";
38
37
  import "./chunk-6UIGZD2N.js";
39
38
  import {
40
39
  logger
@@ -291,4 +290,4 @@ Make sure the project is built (run 'il start' first)`
291
290
  export {
292
291
  RunCommand
293
292
  };
294
- //# sourceMappingURL=run-DP2U2CA2.js.map
293
+ //# sourceMappingURL=run-HRYQ7TR7.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from dotenv-flow files or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n}\n"],"mappings":"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","names":["port"]}
1
+ {"version":3,"sources":["../src/commands/run.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { execa } from 'execa'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { openBrowser } from '../utils/browser.js'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface RunCommandInput {\n\tidentifier?: string\n\targs?: string[]\n}\n\ninterface ParsedRunInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number // For issues and PRs\n\tbranchName?: string // For branches\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * RunCommand - Runs CLI tool or opens workspace in browser\n * Priority: CLI first, Web fallback\n */\nexport class RunCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: RunCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.info(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities, binEntries } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. Execute based on capabilities (CLI first, web fallback)\n\t\tif (capabilities.includes('cli')) {\n\t\t\tawait this.runCLITool(worktree.path, binEntries, input.args ?? [])\n\t\t} else if (capabilities.includes('web')) {\n\t\t\tawait this.openWebBrowser(worktree.path)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`No CLI or web capabilities detected for workspace at ${worktree.path}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedRunInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach run command (converted in start)\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in run command')\n\t\t}\n\n\t\tconst result: ParsedRunInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t * Same logic as FinishCommand.autoDetectFromCurrentDirectory()\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedRunInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedRunInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\t// For PRs, ensure the number is numeric (PRs are always numeric per GitHub)\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\t// Pass empty string for branch name since we don't know it yet\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedRunInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Run CLI tool directly from worktree bin path (NO SYMLINKS!)\n\t */\n\tprivate async runCLITool(\n\t\tworktreePath: string,\n\t\tbinEntries: Record<string, string>,\n\t\targs: string[]\n\t): Promise<void> {\n\t\t// Validate binEntries exist\n\t\tif (Object.keys(binEntries).length === 0) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\n\t\t// Get first bin entry (deterministic)\n\t\tconst firstEntry = Object.entries(binEntries)[0]\n\t\tif (!firstEntry) {\n\t\t\tthrow new Error('No bin entries found in package.json')\n\t\t}\n\t\tconst [binName, binPath] = firstEntry\n\t\tlogger.debug(`Using bin entry: ${binName} -> ${binPath}`)\n\n\t\t// CRITICAL: Construct absolute path (NO SYMLINKS!)\n\t\tconst binFilePath = path.resolve(worktreePath, binPath)\n\t\tlogger.debug(`Resolved bin file path: ${binFilePath}`)\n\n\t\t// Verify file exists\n\t\tif (!(await fs.pathExists(binFilePath))) {\n\t\t\tthrow new Error(\n\t\t\t\t`CLI executable not found: ${binFilePath}\\n` +\n\t\t\t\t\t`Make sure the project is built (run 'il start' first)`\n\t\t\t)\n\t\t}\n\n\t\t// Execute with Node.js\n\t\tlogger.info(`Running CLI: node ${binFilePath} ${args.join(' ')}`)\n\t\tawait execa('node', [binFilePath, ...args], {\n\t\t\tstdio: 'inherit', // Allow interactive CLIs (prompts, colors, etc.)\n\t\t\tcwd: worktreePath, // Execute in worktree context\n\t\t\tenv: process.env, // Inherit environment\n\t\t})\n\t}\n\n\t/**\n\t * Open web browser with workspace URL\n\t * Auto-starts dev server if not already running\n\t */\n\tprivate async openWebBrowser(worktreePath: string): Promise<void> {\n\t\tconst port = await this.getWorkspacePort(worktreePath)\n\n\t\t// Ensure dev server is running on the port\n\t\tconst serverReady = await this.devServerManager.ensureServerRunning(\n\t\t\tworktreePath,\n\t\t\tport\n\t\t)\n\n\t\tif (!serverReady) {\n\t\t\tlogger.warn(\n\t\t\t\t`Dev server failed to start on port ${port}. Opening browser anyway...`\n\t\t\t)\n\t\t}\n\n\t\t// Construct URL and open browser\n\t\tconst url = `http://localhost:${port}`\n\t\tlogger.info(`Opening browser: ${url}`)\n\t\tawait openBrowser(url)\n\t\tlogger.success('Browser opened')\n\t}\n\n\t/**\n\t * Get port for workspace - reads from dotenv-flow files or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n}\n"],"mappings":"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","names":["port"]}
@@ -355,7 +355,8 @@
355
355
  "type": "string",
356
356
  "enum": [
357
357
  "local",
358
- "github-pr"
358
+ "github-pr",
359
+ "github-draft-pr"
359
360
  ],
360
361
  "default": "local"
361
362
  },
@@ -364,7 +365,7 @@
364
365
  }
365
366
  },
366
367
  "additionalProperties": false,
367
- "description": "Merge behavior configuration: local (merge locally) or github-pr (create PR)"
368
+ "description": "Merge behavior configuration: local (merge locally), github-pr (create PR), or github-draft-pr (create draft PR at start, mark ready on finish)"
368
369
  },
369
370
  "ide": {
370
371
  "type": "object",
@@ -403,6 +404,16 @@
403
404
  },
404
405
  "additionalProperties": false,
405
406
  "description": "Color synchronization settings for workspace identification"
407
+ },
408
+ "attribution": {
409
+ "type": "string",
410
+ "enum": [
411
+ "off",
412
+ "upstreamOnly",
413
+ "on"
414
+ ],
415
+ "default": "upstreamOnly",
416
+ "description": "Controls when iloom attribution appears in session summaries. \"off\" - never show attribution. \"upstreamOnly\" - only show for contributions to external repositories (e.g., open source). \"on\" - always show attribution."
406
417
  }
407
418
  },
408
419
  "additionalProperties": false
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ IdentifierParser
4
+ } from "./chunk-AS2IRKLU.js";
5
+ import {
6
+ GitWorktreeManager
7
+ } from "./chunk-3CMGCRB5.js";
8
+ import {
9
+ extractIssueNumber
10
+ } from "./chunk-LN4H3A6A.js";
11
+ import {
12
+ SettingsManager
13
+ } from "./chunk-OOU3DKNT.js";
14
+ import "./chunk-YZTDGPFB.js";
15
+ import {
16
+ getDotenvFlowFiles,
17
+ loadWorkspaceEnv
18
+ } from "./chunk-Z5NXYJIG.js";
19
+ import "./chunk-6UIGZD2N.js";
20
+ import {
21
+ logger
22
+ } from "./chunk-UYVWLISQ.js";
23
+
24
+ // src/commands/shell.ts
25
+ import path from "path";
26
+ import { execa } from "execa";
27
+ import fs from "fs-extra";
28
+ var ShellCommand = class {
29
+ constructor(gitWorktreeManager = new GitWorktreeManager(), identifierParser = new IdentifierParser(new GitWorktreeManager()), settingsManager = new SettingsManager()) {
30
+ this.gitWorktreeManager = gitWorktreeManager;
31
+ this.identifierParser = identifierParser;
32
+ this.settingsManager = settingsManager;
33
+ }
34
+ async execute(input) {
35
+ const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
36
+ logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
37
+ const worktree = await this.findWorktreeForIdentifier(parsed);
38
+ logger.debug(`Found worktree at: ${worktree.path}`);
39
+ const settings = await this.settingsManager.loadSettings();
40
+ const shouldLoadEnv = settings.sourceEnvOnStart ?? false;
41
+ let envVars = { ...process.env };
42
+ let loadedEnvFiles = [];
43
+ if (shouldLoadEnv) {
44
+ const envResult = loadWorkspaceEnv(worktree.path);
45
+ if (envResult.parsed) {
46
+ envVars = { ...envVars, ...envResult.parsed };
47
+ }
48
+ loadedEnvFiles = await this.getExistingEnvFiles(worktree.path);
49
+ }
50
+ const loomIdentifier = this.formatLoomIdentifier(parsed);
51
+ envVars.ILOOM_LOOM = loomIdentifier;
52
+ const shell = this.detectShell();
53
+ this.printSummary(worktree.path, shell, loadedEnvFiles, envVars, shouldLoadEnv);
54
+ await execa(shell, [], {
55
+ cwd: worktree.path,
56
+ env: envVars,
57
+ stdio: "inherit"
58
+ });
59
+ }
60
+ /**
61
+ * Parse explicit identifier input
62
+ */
63
+ async parseExplicitInput(identifier) {
64
+ const parsed = await this.identifierParser.parseForPatternDetection(identifier);
65
+ if (parsed.type === "description") {
66
+ throw new Error("Description input type is not supported in terminal command");
67
+ }
68
+ const result = {
69
+ type: parsed.type,
70
+ originalInput: parsed.originalInput,
71
+ autoDetected: false
72
+ };
73
+ if (parsed.number !== void 0) {
74
+ result.number = parsed.number;
75
+ }
76
+ if (parsed.branchName !== void 0) {
77
+ result.branchName = parsed.branchName;
78
+ }
79
+ return result;
80
+ }
81
+ /**
82
+ * Auto-detect identifier from current directory
83
+ */
84
+ async autoDetectFromCurrentDirectory() {
85
+ const currentDir = path.basename(process.cwd());
86
+ const prPattern = /_pr_(\d+)$/;
87
+ const prMatch = currentDir.match(prPattern);
88
+ if (prMatch == null ? void 0 : prMatch[1]) {
89
+ const prNumber = parseInt(prMatch[1], 10);
90
+ logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
91
+ return {
92
+ type: "pr",
93
+ number: prNumber,
94
+ originalInput: currentDir,
95
+ autoDetected: true
96
+ };
97
+ }
98
+ const issueNumber = extractIssueNumber(currentDir);
99
+ if (issueNumber !== null) {
100
+ logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
101
+ return {
102
+ type: "issue",
103
+ number: issueNumber,
104
+ originalInput: currentDir,
105
+ autoDetected: true
106
+ };
107
+ }
108
+ const repoInfo = await this.gitWorktreeManager.getRepoInfo();
109
+ const currentBranch = repoInfo.currentBranch;
110
+ if (!currentBranch) {
111
+ throw new Error(
112
+ "Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\nExpected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix"
113
+ );
114
+ }
115
+ const branchIssueNumber = extractIssueNumber(currentBranch);
116
+ if (branchIssueNumber !== null) {
117
+ logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
118
+ return {
119
+ type: "issue",
120
+ number: branchIssueNumber,
121
+ originalInput: currentBranch,
122
+ autoDetected: true
123
+ };
124
+ }
125
+ return {
126
+ type: "branch",
127
+ branchName: currentBranch,
128
+ originalInput: currentBranch,
129
+ autoDetected: true
130
+ };
131
+ }
132
+ /**
133
+ * Find worktree for the given identifier
134
+ */
135
+ async findWorktreeForIdentifier(parsed) {
136
+ let worktree = null;
137
+ if (parsed.type === "issue" && parsed.number !== void 0) {
138
+ worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
139
+ } else if (parsed.type === "pr" && parsed.number !== void 0) {
140
+ const prNumber = typeof parsed.number === "number" ? parsed.number : Number(parsed.number);
141
+ if (isNaN(prNumber) || !isFinite(prNumber)) {
142
+ throw new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`);
143
+ }
144
+ worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, "");
145
+ } else if (parsed.type === "branch" && parsed.branchName) {
146
+ worktree = await this.gitWorktreeManager.findWorktreeForBranch(
147
+ parsed.branchName
148
+ );
149
+ }
150
+ if (!worktree) {
151
+ throw new Error(
152
+ `No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
153
+ );
154
+ }
155
+ return worktree;
156
+ }
157
+ /**
158
+ * Format parsed input for display
159
+ */
160
+ formatParsedInput(parsed) {
161
+ const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
162
+ if (parsed.type === "issue") {
163
+ return `issue #${parsed.number}${autoLabel}`;
164
+ }
165
+ if (parsed.type === "pr") {
166
+ return `PR #${parsed.number}${autoLabel}`;
167
+ }
168
+ return `branch "${parsed.branchName}"${autoLabel}`;
169
+ }
170
+ /**
171
+ * Format loom identifier for ILOOM_LOOM env var
172
+ */
173
+ formatLoomIdentifier(parsed) {
174
+ if (parsed.type === "issue") {
175
+ return `issue-${parsed.number}`;
176
+ }
177
+ if (parsed.type === "pr") {
178
+ return `pr-${parsed.number}`;
179
+ }
180
+ return parsed.branchName ?? parsed.originalInput;
181
+ }
182
+ /**
183
+ * Detect shell based on platform and environment variables
184
+ */
185
+ detectShell() {
186
+ if (process.env.ILOOM_SHELL) {
187
+ return process.env.ILOOM_SHELL;
188
+ }
189
+ if (process.platform === "win32") {
190
+ return process.env.COMSPEC ?? "cmd.exe";
191
+ }
192
+ return process.env.SHELL ?? "/bin/bash";
193
+ }
194
+ /**
195
+ * Get list of existing dotenv-flow files in workspace
196
+ */
197
+ async getExistingEnvFiles(workspacePath) {
198
+ const files = getDotenvFlowFiles();
199
+ const existing = [];
200
+ for (const file of files) {
201
+ const fullPath = path.join(workspacePath, file);
202
+ if (await fs.pathExists(fullPath)) {
203
+ existing.push(file);
204
+ }
205
+ }
206
+ return existing;
207
+ }
208
+ /**
209
+ * Print summary of shell session
210
+ */
211
+ printSummary(workspacePath, shell, loadedEnvFiles, envVars, envEnabled) {
212
+ logger.info("Opening interactive shell");
213
+ logger.info(` Workspace: ${workspacePath}`);
214
+ logger.info(` Shell: ${shell}`);
215
+ if (envEnabled) {
216
+ if (loadedEnvFiles.length > 0) {
217
+ logger.info(` Env files: ${loadedEnvFiles.join(", ")}`);
218
+ } else {
219
+ logger.info(" Env files: (none found)");
220
+ }
221
+ const keyVars = ["PORT", "DATABASE_URL", "NODE_ENV"];
222
+ const presentVars = keyVars.filter((v) => envVars[v]);
223
+ if (presentVars.length > 0) {
224
+ const varSummary = presentVars.map((v) => {
225
+ const value = envVars[v];
226
+ const displayValue = value && value.length > 40 ? value.substring(0, 37) + "..." : value;
227
+ return `${v}=${displayValue}`;
228
+ }).join(", ");
229
+ logger.info(` Key vars: ${varSummary}`);
230
+ }
231
+ } else {
232
+ logger.info(" Env loading: disabled (set sourceEnvOnStart in settings to enable)");
233
+ }
234
+ logger.info("");
235
+ }
236
+ };
237
+ export {
238
+ ShellCommand
239
+ };
240
+ //# sourceMappingURL=shell-JMU5XTHW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/shell.ts"],"sourcesContent":["import path from 'path'\nimport { execa } from 'execa'\nimport fs from 'fs-extra'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { loadWorkspaceEnv, getDotenvFlowFiles } from '../utils/env.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface ShellCommandInput {\n\tidentifier?: string | undefined\n}\n\ninterface ParsedShellInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * ShellCommand - Open interactive shell with workspace environment\n * Loads dotenv-flow files when sourceEnvOnStart is configured\n */\nexport class ShellCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\tasync execute(input: ShellCommandInput): Promise<void> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.debug(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Load settings to check sourceEnvOnStart\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// 4. Build environment variables\n\t\tlet envVars: Record<string, string> = { ...process.env as Record<string, string> }\n\t\tlet loadedEnvFiles: string[] = []\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvVars = { ...envVars, ...envResult.parsed }\n\t\t\t}\n\t\t\t// Determine which files were actually loaded\n\t\t\tloadedEnvFiles = await this.getExistingEnvFiles(worktree.path)\n\t\t}\n\n\t\t// 5. Set ILOOM_LOOM for PS1 customization\n\t\tconst loomIdentifier = this.formatLoomIdentifier(parsed)\n\t\tenvVars.ILOOM_LOOM = loomIdentifier\n\n\t\t// 6. Detect shell\n\t\tconst shell = this.detectShell()\n\n\t\t// 7. Print summary\n\t\tthis.printSummary(worktree.path, shell, loadedEnvFiles, envVars, shouldLoadEnv)\n\n\t\t// 8. Launch interactive shell\n\t\tawait execa(shell, [], {\n\t\t\tcwd: worktree.path,\n\t\t\tenv: envVars,\n\t\t\tstdio: 'inherit',\n\t\t})\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedShellInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach terminal command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in terminal command')\n\t\t}\n\n\t\tconst result: ParsedShellInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedShellInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedShellInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedShellInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Format loom identifier for ILOOM_LOOM env var\n\t */\n\tprivate formatLoomIdentifier(parsed: ParsedShellInput): string {\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue-${parsed.number}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `pr-${parsed.number}`\n\t\t}\n\t\treturn parsed.branchName ?? parsed.originalInput\n\t}\n\n\t/**\n\t * Detect shell based on platform and environment variables\n\t */\n\tprivate detectShell(): string {\n\t\t// ILOOM_SHELL takes highest priority\n\t\tif (process.env.ILOOM_SHELL) {\n\t\t\treturn process.env.ILOOM_SHELL\n\t\t}\n\n\t\t// Platform-specific detection\n\t\tif (process.platform === 'win32') {\n\t\t\t// Windows: prefer PowerShell, fall back to COMSPEC (cmd.exe)\n\t\t\treturn process.env.COMSPEC ?? 'cmd.exe'\n\t\t}\n\n\t\t// Unix/macOS: use SHELL or fall back to /bin/bash\n\t\treturn process.env.SHELL ?? '/bin/bash'\n\t}\n\n\t/**\n\t * Get list of existing dotenv-flow files in workspace\n\t */\n\tprivate async getExistingEnvFiles(workspacePath: string): Promise<string[]> {\n\t\tconst files = getDotenvFlowFiles()\n\t\tconst existing: string[] = []\n\n\t\tfor (const file of files) {\n\t\t\tconst fullPath = path.join(workspacePath, file)\n\t\t\tif (await fs.pathExists(fullPath)) {\n\t\t\t\texisting.push(file)\n\t\t\t}\n\t\t}\n\n\t\treturn existing\n\t}\n\n\t/**\n\t * Print summary of shell session\n\t */\n\tprivate printSummary(\n\t\tworkspacePath: string,\n\t\tshell: string,\n\t\tloadedEnvFiles: string[],\n\t\tenvVars: Record<string, string>,\n\t\tenvEnabled: boolean\n\t): void {\n\t\tlogger.info('Opening interactive shell')\n\t\tlogger.info(` Workspace: ${workspacePath}`)\n\t\tlogger.info(` Shell: ${shell}`)\n\n\t\tif (envEnabled) {\n\t\t\tif (loadedEnvFiles.length > 0) {\n\t\t\t\tlogger.info(` Env files: ${loadedEnvFiles.join(', ')}`)\n\t\t\t} else {\n\t\t\t\tlogger.info(' Env files: (none found)')\n\t\t\t}\n\n\t\t\t// Print key environment variables if present\n\t\t\tconst keyVars = ['PORT', 'DATABASE_URL', 'NODE_ENV']\n\t\t\tconst presentVars = keyVars.filter(v => envVars[v])\n\t\t\tif (presentVars.length > 0) {\n\t\t\t\tconst varSummary = presentVars.map(v => {\n\t\t\t\t\tconst value = envVars[v]\n\t\t\t\t\t// Truncate long values like DATABASE_URL\n\t\t\t\t\tconst displayValue = value && value.length > 40\n\t\t\t\t\t\t? value.substring(0, 37) + '...'\n\t\t\t\t\t\t: value\n\t\t\t\t\treturn `${v}=${displayValue}`\n\t\t\t\t}).join(', ')\n\t\t\t\tlogger.info(` Key vars: ${varSummary}`)\n\t\t\t}\n\t\t} else {\n\t\t\tlogger.info(' Env loading: disabled (set sourceEnvOnStart in settings to enable)')\n\t\t}\n\n\t\tlogger.info('')\n\t}\n}\n"],"mappings":"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","names":[]}