@iloom/cli 0.4.1 → 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 (115) hide show
  1. package/README.md +24 -0
  2. package/dist/{ClaudeContextManager-DK77227F.js → ClaudeContextManager-DQFKIMEP.js} +5 -5
  3. package/dist/{ClaudeService-W3SA7HVG.js → ClaudeService-CJS32WG2.js} +4 -4
  4. package/dist/{LoomLauncher-S3YGJRJQ.js → LoomLauncher-4UG2E4CD.js} +19 -24
  5. package/dist/LoomLauncher-4UG2E4CD.js.map +1 -0
  6. package/dist/MetadataManager-WXUVXKUS.js +10 -0
  7. package/dist/PRManager-7DSIMCAD.js +16 -0
  8. package/dist/{PromptTemplateManager-2TDZAUC6.js → PromptTemplateManager-72FEOGT6.js} +2 -2
  9. package/dist/README.md +24 -0
  10. package/dist/{SettingsManager-FJFU6JJD.js → SettingsManager-XPR4TEQL.js} +2 -2
  11. package/dist/agents/iloom-issue-analyze-and-plan.md +41 -7
  12. package/dist/agents/iloom-issue-analyzer.md +38 -8
  13. package/dist/agents/iloom-issue-complexity-evaluator.md +45 -15
  14. package/dist/agents/iloom-issue-enhancer.md +60 -18
  15. package/dist/agents/iloom-issue-implementer.md +29 -7
  16. package/dist/agents/iloom-issue-planner.md +36 -7
  17. package/dist/agents/iloom-issue-reviewer.md +30 -7
  18. package/dist/{chunk-JC5HXN75.js → chunk-3CMGCRB5.js} +2 -2
  19. package/dist/{chunk-G6CIIJLT.js → chunk-4YTILIIH.js} +7 -8
  20. package/dist/chunk-4YTILIIH.js.map +1 -0
  21. package/dist/{chunk-55TB3FSG.js → chunk-AS2IRKLU.js} +2 -2
  22. package/dist/{chunk-POI7KLBH.js → chunk-CDQEK2WD.js} +5 -5
  23. package/dist/{chunk-74VMN2KC.js → chunk-DKQ4SUII.js} +16 -1
  24. package/dist/chunk-DKQ4SUII.js.map +1 -0
  25. package/dist/{chunk-BIIQHEXJ.js → chunk-GVRO4PWE.js} +12 -8
  26. package/dist/chunk-GVRO4PWE.js.map +1 -0
  27. package/dist/{chunk-TMZAVPGF.js → chunk-HABINPX2.js} +71 -15
  28. package/dist/{chunk-TMZAVPGF.js.map → chunk-HABINPX2.js.map} +1 -1
  29. package/dist/{chunk-2W2FBL5G.js → chunk-LN4H3A6A.js} +66 -7
  30. package/dist/chunk-LN4H3A6A.js.map +1 -0
  31. package/dist/{chunk-VWNS6DH5.js → chunk-OOU3DKNT.js} +13 -7
  32. package/dist/chunk-OOU3DKNT.js.map +1 -0
  33. package/dist/chunk-P2ZQ5LKB.js +347 -0
  34. package/dist/chunk-P2ZQ5LKB.js.map +1 -0
  35. package/dist/{chunk-HD5SUKI2.js → chunk-RFUOIUQF.js} +49 -6
  36. package/dist/{chunk-HD5SUKI2.js.map → chunk-RFUOIUQF.js.map} +1 -1
  37. package/dist/{chunk-OF7BNW4D.js → chunk-RJKMF6BC.js} +30 -4
  38. package/dist/chunk-RJKMF6BC.js.map +1 -0
  39. package/dist/{chunk-O7WHXLCB.js → chunk-RNZMHJK7.js} +18 -4
  40. package/dist/chunk-RNZMHJK7.js.map +1 -0
  41. package/dist/{chunk-UPUAQYAW.js → chunk-S65T4O6I.js} +2 -2
  42. package/dist/{chunk-IARWMDAX.js → chunk-T5IIUG4Z.js} +98 -16
  43. package/dist/chunk-T5IIUG4Z.js.map +1 -0
  44. package/dist/{chunk-IJ7IGJT3.js → chunk-YZTDGPFB.js} +18 -1
  45. package/dist/chunk-YZTDGPFB.js.map +1 -0
  46. package/dist/{cleanup-KDLVTT7M.js → cleanup-MIDJVSIU.js} +14 -14
  47. package/dist/cli.js +228 -354
  48. package/dist/cli.js.map +1 -1
  49. package/dist/{contribute-HY372S6F.js → contribute-RS3DO3WP.js} +4 -4
  50. package/dist/{dev-server-JCJGQ3PV.js → dev-server-ASH7HJVI.js} +30 -16
  51. package/dist/dev-server-ASH7HJVI.js.map +1 -0
  52. package/dist/{feedback-7PVBQNLJ.js → feedback-RVIGHBJG.js} +5 -4
  53. package/dist/{feedback-7PVBQNLJ.js.map → feedback-RVIGHBJG.js.map} +1 -1
  54. package/dist/{git-4BVOOOOV.js → git-OQAPUPLP.js} +16 -6
  55. package/dist/git-OQAPUPLP.js.map +1 -0
  56. package/dist/{ignite-3B264M7K.js → ignite-XJALWFAT.js} +57 -22
  57. package/dist/ignite-XJALWFAT.js.map +1 -0
  58. package/dist/index.d.ts +58 -7
  59. package/dist/index.js +104 -7
  60. package/dist/index.js.map +1 -1
  61. package/dist/{init-LBA6NUK2.js → init-F6PFMSU5.js} +7 -7
  62. package/dist/init-F6PFMSU5.js.map +1 -0
  63. package/dist/mcp/recap-server.js +264 -0
  64. package/dist/mcp/recap-server.js.map +1 -0
  65. package/dist/{open-OGCV32Z4.js → open-KW4NTLXH.js} +16 -17
  66. package/dist/{open-OGCV32Z4.js.map → open-KW4NTLXH.js.map} +1 -1
  67. package/dist/{projects-P55273AB.js → projects-QEAEBAT2.js} +2 -2
  68. package/dist/prompts/init-prompt.txt +31 -72
  69. package/dist/prompts/issue-prompt.txt +115 -15
  70. package/dist/prompts/pr-prompt.txt +49 -1
  71. package/dist/prompts/regular-prompt.txt +80 -20
  72. package/dist/{rebase-4T5FQHNH.js → rebase-WZHHE5LU.js} +6 -6
  73. package/dist/recap-33NPZ3ZO.js +117 -0
  74. package/dist/recap-33NPZ3ZO.js.map +1 -0
  75. package/dist/{run-HNOP6WE2.js → run-HRYQ7TR7.js} +16 -17
  76. package/dist/{run-HNOP6WE2.js.map → run-HRYQ7TR7.js.map} +1 -1
  77. package/dist/schema/settings.schema.json +13 -2
  78. package/dist/{shell-DE3HKJSM.js → shell-JMU5XTHW.js} +6 -6
  79. package/dist/{summary-GDT7DTRI.js → summary-4SSGGH7N.js} +17 -9
  80. package/dist/summary-4SSGGH7N.js.map +1 -0
  81. package/dist/{test-git-YMAE57UP.js → test-git-6SAIRBUD.js} +4 -4
  82. package/dist/{test-prefix-YCKL6CMT.js → test-prefix-RLVRK5ZD.js} +4 -4
  83. package/package.json +1 -1
  84. package/dist/LoomLauncher-S3YGJRJQ.js.map +0 -1
  85. package/dist/chunk-2W2FBL5G.js.map +0 -1
  86. package/dist/chunk-74VMN2KC.js.map +0 -1
  87. package/dist/chunk-BIIQHEXJ.js.map +0 -1
  88. package/dist/chunk-G6CIIJLT.js.map +0 -1
  89. package/dist/chunk-IARWMDAX.js.map +0 -1
  90. package/dist/chunk-IJ7IGJT3.js.map +0 -1
  91. package/dist/chunk-O7WHXLCB.js.map +0 -1
  92. package/dist/chunk-OF7BNW4D.js.map +0 -1
  93. package/dist/chunk-QRBOPFAA.js +0 -48
  94. package/dist/chunk-QRBOPFAA.js.map +0 -1
  95. package/dist/chunk-VWNS6DH5.js.map +0 -1
  96. package/dist/dev-server-JCJGQ3PV.js.map +0 -1
  97. package/dist/ignite-3B264M7K.js.map +0 -1
  98. package/dist/summary-GDT7DTRI.js.map +0 -1
  99. /package/dist/{ClaudeContextManager-DK77227F.js.map → ClaudeContextManager-DQFKIMEP.js.map} +0 -0
  100. /package/dist/{ClaudeService-W3SA7HVG.js.map → ClaudeService-CJS32WG2.js.map} +0 -0
  101. /package/dist/{PromptTemplateManager-2TDZAUC6.js.map → MetadataManager-WXUVXKUS.js.map} +0 -0
  102. /package/dist/{SettingsManager-FJFU6JJD.js.map → PRManager-7DSIMCAD.js.map} +0 -0
  103. /package/dist/{git-4BVOOOOV.js.map → PromptTemplateManager-72FEOGT6.js.map} +0 -0
  104. /package/dist/{init-LBA6NUK2.js.map → SettingsManager-XPR4TEQL.js.map} +0 -0
  105. /package/dist/{chunk-JC5HXN75.js.map → chunk-3CMGCRB5.js.map} +0 -0
  106. /package/dist/{chunk-55TB3FSG.js.map → chunk-AS2IRKLU.js.map} +0 -0
  107. /package/dist/{chunk-POI7KLBH.js.map → chunk-CDQEK2WD.js.map} +0 -0
  108. /package/dist/{chunk-UPUAQYAW.js.map → chunk-S65T4O6I.js.map} +0 -0
  109. /package/dist/{cleanup-KDLVTT7M.js.map → cleanup-MIDJVSIU.js.map} +0 -0
  110. /package/dist/{contribute-HY372S6F.js.map → contribute-RS3DO3WP.js.map} +0 -0
  111. /package/dist/{projects-P55273AB.js.map → projects-QEAEBAT2.js.map} +0 -0
  112. /package/dist/{rebase-4T5FQHNH.js.map → rebase-WZHHE5LU.js.map} +0 -0
  113. /package/dist/{shell-DE3HKJSM.js.map → shell-JMU5XTHW.js.map} +0 -0
  114. /package/dist/{test-git-YMAE57UP.js.map → test-git-6SAIRBUD.js.map} +0 -0
  115. /package/dist/{test-prefix-YCKL6CMT.js.map → test-prefix-RLVRK5ZD.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- buildDevServerCommand
4
- } from "./chunk-QRBOPFAA.js";
3
+ detectPackageManager
4
+ } from "./chunk-VBFDVGAE.js";
5
5
  import {
6
6
  ProcessManager
7
7
  } from "./chunk-VU3QMIP2.js";
@@ -12,6 +12,30 @@ import {
12
12
  // src/lib/DevServerManager.ts
13
13
  import { execa } from "execa";
14
14
  import { setTimeout } from "timers/promises";
15
+
16
+ // src/utils/dev-server.ts
17
+ async function buildDevServerCommand(workspacePath) {
18
+ const packageManager = await detectPackageManager(workspacePath);
19
+ let devCommand;
20
+ switch (packageManager) {
21
+ case "pnpm":
22
+ devCommand = "pnpm dev";
23
+ break;
24
+ case "npm":
25
+ devCommand = "npm run dev";
26
+ break;
27
+ case "yarn":
28
+ devCommand = "yarn dev";
29
+ break;
30
+ default:
31
+ logger.warn(`Unknown or unsupported package manager: ${packageManager}, defaulting to npm`);
32
+ devCommand = "npm run dev";
33
+ }
34
+ logger.debug(`Dev server command: ${devCommand}`);
35
+ return devCommand;
36
+ }
37
+
38
+ // src/lib/DevServerManager.ts
15
39
  var DevServerManager = class {
16
40
  constructor(processManager, options = {}) {
17
41
  this.runningServers = /* @__PURE__ */ new Map();
@@ -119,7 +143,7 @@ var DevServerManager = class {
119
143
  * @param onProcessStarted - Callback called immediately after process starts with PID
120
144
  * @returns Process information including PID
121
145
  */
122
- async runServerForeground(worktreePath, port, redirectToStderr = false, onProcessStarted) {
146
+ async runServerForeground(worktreePath, port, redirectToStderr = false, onProcessStarted, envOverrides) {
123
147
  const devCommand = await buildDevServerCommand(worktreePath);
124
148
  logger.debug(`Starting dev server in foreground with command: ${devCommand}`);
125
149
  const stdio = redirectToStderr ? [process.stdin, process.stderr, process.stderr] : "inherit";
@@ -127,7 +151,9 @@ var DevServerManager = class {
127
151
  cwd: worktreePath,
128
152
  env: {
129
153
  ...process.env,
154
+ ...envOverrides,
130
155
  PORT: port.toString()
156
+ // PORT always wins (explicit parameter)
131
157
  },
132
158
  // Configure stdio based on whether we want to redirect output
133
159
  stdio
@@ -161,4 +187,4 @@ var DevServerManager = class {
161
187
  export {
162
188
  DevServerManager
163
189
  };
164
- //# sourceMappingURL=chunk-OF7BNW4D.js.map
190
+ //# sourceMappingURL=chunk-RJKMF6BC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/DevServerManager.ts","../src/utils/dev-server.ts"],"sourcesContent":["import { execa, type ExecaChildProcess } from 'execa'\nimport { setTimeout } from 'timers/promises'\nimport { ProcessManager } from './process/ProcessManager.js'\nimport { buildDevServerCommand } from '../utils/dev-server.js'\nimport { logger } from '../utils/logger.js'\n\nexport interface DevServerManagerOptions {\n\t/**\n\t * Maximum time to wait for server to start (in milliseconds)\n\t * Default: 30000 (30 seconds)\n\t */\n\tstartupTimeout?: number\n\n\t/**\n\t * Interval between port checks (in milliseconds)\n\t * Default: 1000 (1 second)\n\t */\n\tcheckInterval?: number\n}\n\n/**\n * DevServerManager handles auto-starting and monitoring dev servers\n * Used by open/run commands to ensure dev server is running before opening browser\n */\nexport class DevServerManager {\n\tprivate readonly processManager: ProcessManager\n\tprivate readonly options: Required<DevServerManagerOptions>\n\tprivate runningServers: Map<number, ExecaChildProcess> = new Map()\n\n\tconstructor(\n\t\tprocessManager?: ProcessManager,\n\t\toptions: DevServerManagerOptions = {}\n\t) {\n\t\tthis.processManager = processManager ?? new ProcessManager()\n\t\tthis.options = {\n\t\t\tstartupTimeout: options.startupTimeout ?? 30000,\n\t\t\tcheckInterval: options.checkInterval ?? 1000,\n\t\t}\n\t}\n\n\t/**\n\t * Ensure dev server is running on the specified port\n\t * If not running, start it and wait for it to be ready\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param port - Port the server should run on\n\t * @returns true if server is ready, false if startup failed/timed out\n\t */\n\tasync ensureServerRunning(worktreePath: string, port: number): Promise<boolean> {\n\t\tlogger.debug(`Checking if dev server is running on port ${port}...`)\n\n\t\t// Check if already running\n\t\tconst existingProcess = await this.processManager.detectDevServer(port)\n\t\tif (existingProcess) {\n\t\t\tlogger.debug(\n\t\t\t\t`Dev server already running on port ${port} (PID: ${existingProcess.pid})`\n\t\t\t)\n\t\t\treturn true\n\t\t}\n\n\t\t// Not running - start it\n\t\tlogger.info(`Dev server not running on port ${port}, starting...`)\n\n\t\ttry {\n\t\t\tawait this.startDevServer(worktreePath, port)\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t`Failed to start dev server: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Start dev server in background and wait for it to be ready\n\t */\n\tprivate async startDevServer(worktreePath: string, port: number): Promise<void> {\n\t\t// Build dev server command\n\t\tconst devCommand = await buildDevServerCommand(worktreePath)\n\t\tlogger.debug(`Starting dev server with command: ${devCommand}`)\n\n\t\t// Start server in background\n\t\tconst serverProcess = execa('sh', ['-c', devCommand], {\n\t\t\tcwd: worktreePath,\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\tPORT: port.toString(),\n\t\t\t},\n\t\t\t// Important: Don't inherit stdio - server runs in background\n\t\t\tstdio: 'ignore',\n\t\t\t// Detach from parent process so it continues running\n\t\t\tdetached: true,\n\t\t})\n\n\t\t// Store reference to prevent cleanup\n\t\tthis.runningServers.set(port, serverProcess)\n\n\t\t// Unref so parent can exit\n\t\tserverProcess.unref()\n\n\t\t// Wait for server to be ready\n\t\tlogger.info(`Waiting for dev server to start on port ${port}...`)\n\t\tconst ready = await this.waitForServerReady(port)\n\n\t\tif (!ready) {\n\t\t\tthrow new Error(\n\t\t\t\t`Dev server failed to start within ${this.options.startupTimeout}ms timeout`\n\t\t\t)\n\t\t}\n\n\t\tlogger.success(`Dev server started successfully on port ${port}`)\n\t}\n\n\t/**\n\t * Wait for server to be ready by polling the port\n\t */\n\tprivate async waitForServerReady(port: number): Promise<boolean> {\n\t\tconst startTime = Date.now()\n\t\tlet attempts = 0\n\n\t\twhile (Date.now() - startTime < this.options.startupTimeout) {\n\t\t\tattempts++\n\n\t\t\t// Check if server is listening\n\t\t\tconst processInfo = await this.processManager.detectDevServer(port)\n\n\t\t\tif (processInfo) {\n\t\t\t\tlogger.debug(\n\t\t\t\t\t`Server detected on port ${port} after ${attempts} attempts (${Date.now() - startTime}ms)`\n\t\t\t\t)\n\t\t\t\treturn true\n\t\t\t}\n\n\t\t\t// Wait before next check\n\t\t\tawait setTimeout(this.options.checkInterval)\n\t\t}\n\n\t\t// Timeout\n\t\tlogger.warn(\n\t\t\t`Server did not start on port ${port} after ${this.options.startupTimeout}ms (${attempts} attempts)`\n\t\t)\n\t\treturn false\n\t}\n\n\t/**\n\t * Check if a dev server is running on the specified port\n\t *\n\t * @param port - Port to check\n\t * @returns true if server is running, false otherwise\n\t */\n\tasync isServerRunning(port: number): Promise<boolean> {\n\t\tconst existingProcess = await this.processManager.detectDevServer(port)\n\t\treturn existingProcess !== null\n\t}\n\n\t/**\n\t * Run dev server in foreground mode (blocking)\n\t * This method blocks until the server is stopped (e.g., via Ctrl+C)\n\t *\n\t * @param worktreePath - Path to the worktree\n\t * @param port - Port the server should run on\n\t * @param redirectToStderr - If true, redirect stdout/stderr to stderr (useful for JSON output)\n\t * @param onProcessStarted - Callback called immediately after process starts with PID\n\t * @returns Process information including PID\n\t */\n\tasync runServerForeground(\n\t\tworktreePath: string,\n\t\tport: number,\n\t\tredirectToStderr = false,\n\t\tonProcessStarted?: (pid?: number) => void,\n\t\tenvOverrides?: Record<string, string>\n\t): Promise<{ pid?: number }> {\n\t\t// Build dev server command\n\t\tconst devCommand = await buildDevServerCommand(worktreePath)\n\t\tlogger.debug(`Starting dev server in foreground with command: ${devCommand}`)\n\n\t\t// Configure stdio based on redirect option\n\t\tconst stdio = redirectToStderr ? [process.stdin, process.stderr, process.stderr] : 'inherit'\n\n\t\t// Start server in foreground (blocking with configured stdio)\n\t\tconst serverProcess = execa('sh', ['-c', devCommand], {\n\t\t\tcwd: worktreePath,\n\t\t\tenv: {\n\t\t\t\t...process.env,\n\t\t\t\t...envOverrides,\n\t\t\t\tPORT: port.toString(), // PORT always wins (explicit parameter)\n\t\t\t},\n\t\t\t// Configure stdio based on whether we want to redirect output\n\t\t\tstdio,\n\t\t})\n\n\t\t// Process info is available immediately after spawn\n\t\t// Use conditional property to satisfy exactOptionalPropertyTypes\n\t\tconst processInfo: { pid?: number } = serverProcess.pid !== undefined ? { pid: serverProcess.pid } : {}\n\n\t\t// Call the callback immediately with the PID (for JSON output)\n\t\tif (onProcessStarted) {\n\t\t\tonProcessStarted(processInfo.pid)\n\t\t}\n\n\t\t// Now wait for the process to complete (this blocks)\n\t\tawait serverProcess\n\n\t\treturn processInfo\n\t}\n\n\t/**\n\t * Clean up all running server processes\n\t * This should be called when the manager is being disposed\n\t */\n\tasync cleanup(): Promise<void> {\n\t\tfor (const [port, serverProcess] of this.runningServers.entries()) {\n\t\t\ttry {\n\t\t\t\tlogger.debug(`Cleaning up server process on port ${port}`)\n\t\t\t\tserverProcess.kill()\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Failed to kill server process on port ${port}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tthis.runningServers.clear()\n\t}\n}\n","import { detectPackageManager } from './package-manager.js'\nimport { logger } from './logger.js'\nimport type { Capability } from '../types/loom.js'\n\n/**\n * Build dev server command for workspace\n * Detects package manager and constructs appropriate command\n */\nexport async function buildDevServerCommand(\n\tworkspacePath: string\n): Promise<string> {\n\tconst packageManager = await detectPackageManager(workspacePath)\n\n\tlet devCommand: string\n\n\tswitch (packageManager) {\n\t\tcase 'pnpm':\n\t\t\tdevCommand = 'pnpm dev'\n\t\t\tbreak\n\t\tcase 'npm':\n\t\t\tdevCommand = 'npm run dev'\n\t\t\tbreak\n\t\tcase 'yarn':\n\t\t\tdevCommand = 'yarn dev'\n\t\t\tbreak\n\t\tdefault:\n\t\t\t// Fallback to npm (handles bun and other package managers)\n\t\t\tlogger.warn(`Unknown or unsupported package manager: ${packageManager}, defaulting to npm`)\n\t\t\tdevCommand = 'npm run dev'\n\t}\n\n\tlogger.debug(`Dev server command: ${devCommand}`)\n\treturn devCommand\n}\n\n/**\n * Build complete dev server launch command for terminal\n * Includes VSCode launch, echo message (only for web projects), and dev server start\n */\nexport async function getDevServerLaunchCommand(\n\tworkspacePath: string,\n\tport?: number,\n\tcapabilities: Capability[] = []\n): Promise<string> {\n\tconst devCommand = await buildDevServerCommand(workspacePath)\n\n\tconst commands: string[] = []\n\n\t// // Open VSCode\n\t// commands.push('code .')\n\n\t// Echo message (only for web projects)\n\tif (capabilities.includes('web')) {\n\t\tif (port !== undefined) {\n\t\t\tcommands.push(`echo 'Starting dev server on PORT=${port}...'`)\n\t\t} else {\n\t\t\tcommands.push(`echo 'Starting dev server...'`)\n\t\t}\n\t}\n\n\t// Start dev server\n\tcommands.push(devCommand)\n\n\treturn commands.join(' && ')\n}\n"],"mappings":"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","names":[]}
@@ -1,4 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ PromptTemplateManager
4
+ } from "./chunk-DKQ4SUII.js";
2
5
  import {
3
6
  logger
4
7
  } from "./chunk-UYVWLISQ.js";
@@ -111,7 +114,8 @@ var MarkdownAgentParser = class {
111
114
 
112
115
  // src/lib/AgentManager.ts
113
116
  var AgentManager = class {
114
- constructor(agentDir) {
117
+ constructor(agentDir, templateManager) {
118
+ this.templateManager = templateManager ?? new PromptTemplateManager();
115
119
  if (agentDir) {
116
120
  this.agentDir = agentDir;
117
121
  } else {
@@ -136,11 +140,12 @@ var AgentManager = class {
136
140
  }
137
141
  /**
138
142
  * Load all agent configuration files from markdown (.md) format
139
- * Optionally apply model overrides from settings
143
+ * Optionally apply model overrides from settings and template variable substitution
140
144
  * Throws error if agents directory doesn't exist or files are malformed
141
145
  * @param settings - Optional project settings with per-agent model overrides
146
+ * @param templateVariables - Optional variables for template substitution in agent prompts
142
147
  */
143
- async loadAgents(settings) {
148
+ async loadAgents(settings, templateVariables) {
144
149
  const { readdir } = await import("fs/promises");
145
150
  const files = await readdir(this.agentDir);
146
151
  const agentFiles = files.filter((file) => file.endsWith(".md"));
@@ -162,6 +167,15 @@ var AgentManager = class {
162
167
  );
163
168
  }
164
169
  }
170
+ if (templateVariables) {
171
+ for (const [agentName, agentConfig] of Object.entries(agents)) {
172
+ agents[agentName] = {
173
+ ...agentConfig,
174
+ prompt: this.templateManager.substituteVariables(agentConfig.prompt, templateVariables)
175
+ };
176
+ logger.debug(`Applied template substitution to agent: ${agentName}`);
177
+ }
178
+ }
165
179
  if (settings == null ? void 0 : settings.agents) {
166
180
  for (const [agentName, agentSettings] of Object.entries(settings.agents)) {
167
181
  if (agents[agentName] && agentSettings.model) {
@@ -245,4 +259,4 @@ var AgentManager = class {
245
259
  export {
246
260
  AgentManager
247
261
  };
248
- //# sourceMappingURL=chunk-O7WHXLCB.js.map
262
+ //# sourceMappingURL=chunk-RNZMHJK7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/AgentManager.ts","../src/utils/MarkdownAgentParser.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { accessSync } from 'fs'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport { MarkdownAgentParser } from '../utils/MarkdownAgentParser.js'\nimport { logger } from '../utils/logger.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { PromptTemplateManager, TemplateVariables } from './PromptTemplateManager.js'\n\n// Agent schema interface\nexport interface AgentConfig {\n\tdescription: string\n\tprompt: string\n\ttools: string[]\n\tmodel: string\n\tcolor?: string\n}\n\n// Container for all loaded agents (keyed by agent name without extension)\nexport interface AgentConfigs {\n\t[agentName: string]: AgentConfig\n}\n\nexport class AgentManager {\n\tprivate agentDir: string\n\tprivate templateManager: PromptTemplateManager\n\n\tconstructor(agentDir?: string, templateManager?: PromptTemplateManager) {\n\t\tthis.templateManager = templateManager ?? new PromptTemplateManager()\n\t\tif (agentDir) {\n\t\t\tthis.agentDir = agentDir\n\t\t} else {\n\t\t\t// Find agents relative to package installation\n\t\t\t// Same pattern as PromptTemplateManager\n\t\t\t// When running from dist/, agents are copied to dist/agents/\n\t\t\tconst currentFileUrl = import.meta.url\n\t\t\tconst currentFilePath = fileURLToPath(currentFileUrl)\n\t\t\tconst distDir = path.dirname(currentFilePath)\n\n\t\t\t// Walk up to find the agents directory\n\t\t\tlet agentDirPath = path.join(distDir, 'agents')\n\t\t\tlet currentDir = distDir\n\n\t\t\twhile (currentDir !== path.dirname(currentDir)) {\n\t\t\t\tconst candidatePath = path.join(currentDir, 'agents')\n\t\t\t\ttry {\n\t\t\t\t\taccessSync(candidatePath)\n\t\t\t\t\tagentDirPath = 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.agentDir = agentDirPath\n\t\t\tlogger.debug('AgentManager initialized', { agentDir: this.agentDir })\n\t\t}\n\t}\n\n\t/**\n\t * Load all agent configuration files from markdown (.md) format\n\t * Optionally apply model overrides from settings and template variable substitution\n\t * Throws error if agents directory doesn't exist or files are malformed\n\t * @param settings - Optional project settings with per-agent model overrides\n\t * @param templateVariables - Optional variables for template substitution in agent prompts\n\t */\n\tasync loadAgents(settings?: IloomSettings, templateVariables?: TemplateVariables): Promise<AgentConfigs> {\n\t\t// Load all .md files from the agents directory\n\t\tconst { readdir } = await import('fs/promises')\n\t\tconst files = await readdir(this.agentDir)\n\t\tconst agentFiles = files.filter(file => file.endsWith('.md'))\n\n\t\tconst agents: AgentConfigs = {}\n\n\t\tfor (const filename of agentFiles) {\n\t\t\tconst agentPath = path.join(this.agentDir, filename)\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(agentPath, 'utf-8')\n\n\t\t\t\t// Parse markdown with frontmatter\n\t\t\t\tconst parsed = this.parseMarkdownAgent(content, filename)\n\t\t\t\tconst agentConfig = parsed.config\n\t\t\t\tconst agentName = parsed.name\n\n\t\t\t\t// Validate required fields\n\t\t\t\tthis.validateAgentConfig(agentConfig, agentName)\n\n\t\t\t\tagents[agentName] = agentConfig\n\t\t\t\tlogger.debug(`Loaded agent: ${agentName}`)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(`Failed to load agent from ${filename}`, { error })\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to load agent from ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Apply template variable substitution to agent prompts if variables provided\n\t\tif (templateVariables) {\n\t\t\tfor (const [agentName, agentConfig] of Object.entries(agents)) {\n\t\t\t\tagents[agentName] = {\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tprompt: this.templateManager.substituteVariables(agentConfig.prompt, templateVariables),\n\t\t\t\t}\n\t\t\t\tlogger.debug(`Applied template substitution to agent: ${agentName}`)\n\t\t\t}\n\t\t}\n\n\t\t// Apply settings overrides if provided\n\t\tif (settings?.agents) {\n\t\t\tfor (const [agentName, agentSettings] of Object.entries(settings.agents)) {\n\t\t\t\tif (agents[agentName] && agentSettings.model) {\n\t\t\t\t\tlogger.debug(`Overriding model for ${agentName}: ${agents[agentName].model} -> ${agentSettings.model}`)\n\t\t\t\t\tagents[agentName] = {\n\t\t\t\t\t\t...agents[agentName],\n\t\t\t\t\t\tmodel: agentSettings.model,\n\t\t\t\t\t}\n\t\t\t\t} else if (!agents[agentName]) {\n\t\t\t\t\tlogger.warn(`Settings reference unknown agent: ${agentName}`)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn agents\n\t}\n\n\t/**\n\t * Validate agent configuration has required fields\n\t */\n\tprivate validateAgentConfig(config: AgentConfig, agentName: string): void {\n\t\tconst requiredFields: (keyof AgentConfig)[] = ['description', 'prompt', 'tools', 'model']\n\n\t\tfor (const field of requiredFields) {\n\t\t\tif (!config[field]) {\n\t\t\t\tthrow new Error(`Agent ${agentName} missing required field: ${field}`)\n\t\t\t}\n\t\t}\n\n\t\tif (!Array.isArray(config.tools)) {\n\t\t\tthrow new Error(`Agent ${agentName} tools must be an array`)\n\t\t}\n\t}\n\n\t/**\n\t * Parse markdown agent file with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @param filename - Original filename for error messages\n\t * @returns Parsed agent config and name\n\t */\n\tprivate parseMarkdownAgent(content: string, filename: string): { config: AgentConfig; name: string } {\n\t\ttry {\n\t\t\t// Parse frontmatter using custom parser\n\t\t\tconst { data, content: markdownBody } = MarkdownAgentParser.parse(content)\n\n\t\t\t// Validate frontmatter has required fields\n\t\t\tif (!data.name) {\n\t\t\t\tthrow new Error('Missing required field: name')\n\t\t\t}\n\t\t\tif (!data.description) {\n\t\t\t\tthrow new Error('Missing required field: description')\n\t\t\t}\n\t\t\tif (!data.tools) {\n\t\t\t\tthrow new Error('Missing required field: tools')\n\t\t\t}\n\t\t\tif (!data.model) {\n\t\t\t\tthrow new Error('Missing required field: model')\n\t\t\t}\n\n\t\t\t// Parse tools from comma-separated string to array\n\t\t\tconst tools = data.tools\n\t\t\t\t.split(',')\n\t\t\t\t.map((tool: string) => tool.trim())\n\t\t\t\t.filter((tool: string) => tool.length > 0)\n\n\t\t\t// Validate model and warn if non-standard\n\t\t\tconst validModels = ['sonnet', 'opus', 'haiku']\n\t\t\tif (!validModels.includes(data.model)) {\n\t\t\t\tlogger.warn(\n\t\t\t\t\t`Agent ${data.name} uses model \"${data.model}\" which may not be recognized by Claude CLI, and your workflow may fail or produce unexpected results. ` +\n\t\t\t\t\t\t`Valid values are: ${validModels.join(', ')}`\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// Construct AgentConfig\n\t\t\tconst config: AgentConfig = {\n\t\t\t\tdescription: data.description,\n\t\t\t\tprompt: markdownBody.trim(),\n\t\t\t\ttools,\n\t\t\t\tmodel: data.model,\n\t\t\t\t...(data.color && { color: data.color }),\n\t\t\t}\n\n\t\t\treturn { config, name: data.name }\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to parse markdown agent ${filename}: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Format loaded agents for Claude CLI --agents flag\n\t * Returns object suitable for JSON.stringify\n\t */\n\tformatForCli(agents: AgentConfigs): Record<string, unknown> {\n\t\t// The agents object is already in the correct format\n\t\t// Just return it - launchClaude will JSON.stringify it\n\t\treturn agents as Record<string, unknown>\n\t}\n}\n","/**\n * Custom YAML frontmatter parser for agent markdown files\n * Replaces gray-matter dependency with lightweight custom implementation\n */\n\ninterface ParseResult {\n\tdata: Record<string, string>\n\tcontent: string\n}\n\nexport class MarkdownAgentParser {\n\t/**\n\t * Parse markdown content with YAML frontmatter\n\t * @param content - Raw markdown file content\n\t * @returns Object with parsed frontmatter data and markdown body content\n\t * @throws Error if frontmatter is malformed or missing\n\t */\n\tstatic parse(content: string): ParseResult {\n\t\tconst lines = content.split('\\n')\n\n\t\t// Check for opening frontmatter delimiter\n\t\tif (lines[0]?.trim() !== '---') {\n\t\t\tthrow new Error('Missing opening frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Find closing frontmatter delimiter\n\t\tlet closingDelimiterIndex = -1\n\t\tfor (let i = 1; i < lines.length; i++) {\n\t\t\tif (lines[i]?.trim() === '---') {\n\t\t\t\tclosingDelimiterIndex = i\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif (closingDelimiterIndex === -1) {\n\t\t\tthrow new Error('Missing closing frontmatter delimiter (---)')\n\t\t}\n\n\t\t// Extract frontmatter lines (between the delimiters)\n\t\tconst frontmatterLines = lines.slice(1, closingDelimiterIndex)\n\n\t\t// Extract markdown body (after closing delimiter)\n\t\tconst bodyLines = lines.slice(closingDelimiterIndex + 1)\n\t\tconst markdownBody = bodyLines.join('\\n')\n\n\t\t// Parse YAML frontmatter into key-value pairs\n\t\tconst data = this.parseYaml(frontmatterLines.join('\\n'))\n\n\t\treturn {\n\t\t\tdata,\n\t\t\tcontent: markdownBody,\n\t\t}\n\t}\n\n\t/**\n\t * Parse simplified YAML into key-value object\n\t * Supports:\n\t * - Simple key: value pairs\n\t * - Multiline values with | indicator\n\t * - Values with special characters and newlines\n\t *\n\t * @param yaml - YAML string to parse\n\t * @returns Object with parsed key-value pairs\n\t */\n\tprivate static parseYaml(yaml: string): Record<string, string> {\n\t\tconst result: Record<string, string> = {}\n\t\tconst lines = yaml.split('\\n')\n\t\tlet currentKey: string | null = null\n\t\tlet currentValue: string[] = []\n\t\tlet isMultiline = false\n\n\t\tconst finalizeCurrent = (): void => {\n\t\t\tif (currentKey && currentValue.length > 0) {\n\t\t\t\tresult[currentKey] = currentValue.join('\\n').trim()\n\t\t\t\tcurrentKey = null\n\t\t\t\tcurrentValue = []\n\t\t\t}\n\t\t}\n\n\t\tfor (const line of lines) {\n\t\t\t// Skip empty lines when not in multiline mode\n\t\t\tif (!isMultiline && line.trim() === '') {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Check if this is a new key-value pair\n\t\t\tconst keyValueMatch = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\n\t\t\tif (keyValueMatch && !isMultiline) {\n\t\t\t\t// Finalize previous key if exists\n\t\t\t\tfinalizeCurrent()\n\n\t\t\t\tconst [, key, value] = keyValueMatch\n\t\t\t\tif (!key || value === undefined) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tcurrentKey = key\n\n\t\t\t\t// Check for multiline indicator\n\t\t\t\tif (value.trim() === '|') {\n\t\t\t\t\tisMultiline = true\n\t\t\t\t\tcurrentValue = []\n\t\t\t\t} else {\n\t\t\t\t\t// Single line value\n\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\t\t\t\t}\n\t\t\t} else if (isMultiline && currentKey) {\n\t\t\t\t// Continuation of multiline value\n\t\t\t\t// Check if we've returned to normal indentation (new key)\n\t\t\t\tif (line.match(/^[a-zA-Z_][a-zA-Z0-9_-]*\\s*:/) && !line.startsWith(' ')) {\n\t\t\t\t\t// End of multiline, this is a new key\n\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\tisMultiline = false\n\n\t\t\t\t\t// Process this line as a new key\n\t\t\t\t\tconst match = line.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\\s*:\\s*(.*)$/)\n\t\t\t\t\tif (match) {\n\t\t\t\t\t\tconst [, key, value] = match\n\t\t\t\t\t\tif (key && value !== undefined) {\n\t\t\t\t\t\t\tcurrentKey = key\n\t\t\t\t\t\t\tcurrentValue = [value]\n\t\t\t\t\t\t\tfinalizeCurrent()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Remove leading spaces (common indentation) from multiline values\n\t\t\t\t\tconst trimmedLine = line.replace(/^ {2}/, '')\n\t\t\t\t\tcurrentValue.push(trimmedLine)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Finalize last key\n\t\tfinalizeCurrent()\n\n\t\treturn result\n\t}\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAC3B,OAAO,UAAU;AACjB,SAAS,qBAAqB;;;ACOvB,IAAM,sBAAN,MAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhC,OAAO,MAAM,SAA8B;AAjB5C;AAkBE,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAGhC,UAAI,WAAM,CAAC,MAAP,mBAAU,YAAW,OAAO;AAC/B,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC9D;AAGA,QAAI,wBAAwB;AAC5B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACtC,YAAI,WAAM,CAAC,MAAP,mBAAU,YAAW,OAAO;AAC/B,gCAAwB;AACxB;AAAA,MACD;AAAA,IACD;AAEA,QAAI,0BAA0B,IAAI;AACjC,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC9D;AAGA,UAAM,mBAAmB,MAAM,MAAM,GAAG,qBAAqB;AAG7D,UAAM,YAAY,MAAM,MAAM,wBAAwB,CAAC;AACvD,UAAM,eAAe,UAAU,KAAK,IAAI;AAGxC,UAAM,OAAO,KAAK,UAAU,iBAAiB,KAAK,IAAI,CAAC;AAEvD,WAAO;AAAA,MACN;AAAA,MACA,SAAS;AAAA,IACV;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,OAAe,UAAU,MAAsC;AAC9D,UAAM,SAAiC,CAAC;AACxC,UAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,QAAI,aAA4B;AAChC,QAAI,eAAyB,CAAC;AAC9B,QAAI,cAAc;AAElB,UAAM,kBAAkB,MAAY;AACnC,UAAI,cAAc,aAAa,SAAS,GAAG;AAC1C,eAAO,UAAU,IAAI,aAAa,KAAK,IAAI,EAAE,KAAK;AAClD,qBAAa;AACb,uBAAe,CAAC;AAAA,MACjB;AAAA,IACD;AAEA,eAAW,QAAQ,OAAO;AAEzB,UAAI,CAAC,eAAe,KAAK,KAAK,MAAM,IAAI;AACvC;AAAA,MACD;AAGA,YAAM,gBAAgB,KAAK,MAAM,wCAAwC;AAEzE,UAAI,iBAAiB,CAAC,aAAa;AAElC,wBAAgB;AAEhB,cAAM,CAAC,EAAE,KAAK,KAAK,IAAI;AACvB,YAAI,CAAC,OAAO,UAAU,QAAW;AAChC;AAAA,QACD;AACA,qBAAa;AAGb,YAAI,MAAM,KAAK,MAAM,KAAK;AACzB,wBAAc;AACd,yBAAe,CAAC;AAAA,QACjB,OAAO;AAEN,yBAAe,CAAC,KAAK;AACrB,0BAAgB;AAChB,wBAAc;AAAA,QACf;AAAA,MACD,WAAW,eAAe,YAAY;AAGrC,YAAI,KAAK,MAAM,8BAA8B,KAAK,CAAC,KAAK,WAAW,GAAG,GAAG;AAExE,0BAAgB;AAChB,wBAAc;AAGd,gBAAM,QAAQ,KAAK,MAAM,wCAAwC;AACjE,cAAI,OAAO;AACV,kBAAM,CAAC,EAAE,KAAK,KAAK,IAAI;AACvB,gBAAI,OAAO,UAAU,QAAW;AAC/B,2BAAa;AACb,6BAAe,CAAC,KAAK;AACrB,8BAAgB;AAAA,YACjB;AAAA,UACD;AAAA,QACD,OAAO;AAEN,gBAAM,cAAc,KAAK,QAAQ,SAAS,EAAE;AAC5C,uBAAa,KAAK,WAAW;AAAA,QAC9B;AAAA,MACD;AAAA,IACD;AAGA,oBAAgB;AAEhB,WAAO;AAAA,EACR;AACD;;;ADpHO,IAAM,eAAN,MAAmB;AAAA,EAIzB,YAAY,UAAmB,iBAAyC;AACvE,SAAK,kBAAkB,mBAAmB,IAAI,sBAAsB;AACpE,QAAI,UAAU;AACb,WAAK,WAAW;AAAA,IACjB,OAAO;AAIN,YAAM,iBAAiB,YAAY;AACnC,YAAM,kBAAkB,cAAc,cAAc;AACpD,YAAM,UAAU,KAAK,QAAQ,eAAe;AAG5C,UAAI,eAAe,KAAK,KAAK,SAAS,QAAQ;AAC9C,UAAI,aAAa;AAEjB,aAAO,eAAe,KAAK,QAAQ,UAAU,GAAG;AAC/C,cAAM,gBAAgB,KAAK,KAAK,YAAY,QAAQ;AACpD,YAAI;AACH,qBAAW,aAAa;AACxB,yBAAe;AACf;AAAA,QACD,QAAQ;AACP,uBAAa,KAAK,QAAQ,UAAU;AAAA,QACrC;AAAA,MACD;AAEA,WAAK,WAAW;AAChB,aAAO,MAAM,4BAA4B,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,IACrE;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAW,UAA0B,mBAA8D;AAExG,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,aAAa;AAC9C,UAAM,QAAQ,MAAM,QAAQ,KAAK,QAAQ;AACzC,UAAM,aAAa,MAAM,OAAO,UAAQ,KAAK,SAAS,KAAK,CAAC;AAE5D,UAAM,SAAuB,CAAC;AAE9B,eAAW,YAAY,YAAY;AAClC,YAAM,YAAY,KAAK,KAAK,KAAK,UAAU,QAAQ;AAEnD,UAAI;AACH,cAAM,UAAU,MAAM,SAAS,WAAW,OAAO;AAGjD,cAAM,SAAS,KAAK,mBAAmB,SAAS,QAAQ;AACxD,cAAM,cAAc,OAAO;AAC3B,cAAM,YAAY,OAAO;AAGzB,aAAK,oBAAoB,aAAa,SAAS;AAE/C,eAAO,SAAS,IAAI;AACpB,eAAO,MAAM,iBAAiB,SAAS,EAAE;AAAA,MAC1C,SAAS,OAAO;AACf,eAAO,MAAM,6BAA6B,QAAQ,IAAI,EAAE,MAAM,CAAC;AAC/D,cAAM,IAAI;AAAA,UACT,6BAA6B,QAAQ,KAAK,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,QACnG;AAAA,MACD;AAAA,IACD;AAGA,QAAI,mBAAmB;AACtB,iBAAW,CAAC,WAAW,WAAW,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC9D,eAAO,SAAS,IAAI;AAAA,UACnB,GAAG;AAAA,UACH,QAAQ,KAAK,gBAAgB,oBAAoB,YAAY,QAAQ,iBAAiB;AAAA,QACvF;AACA,eAAO,MAAM,2CAA2C,SAAS,EAAE;AAAA,MACpE;AAAA,IACD;AAGA,QAAI,qCAAU,QAAQ;AACrB,iBAAW,CAAC,WAAW,aAAa,KAAK,OAAO,QAAQ,SAAS,MAAM,GAAG;AACzE,YAAI,OAAO,SAAS,KAAK,cAAc,OAAO;AAC7C,iBAAO,MAAM,wBAAwB,SAAS,KAAK,OAAO,SAAS,EAAE,KAAK,OAAO,cAAc,KAAK,EAAE;AACtG,iBAAO,SAAS,IAAI;AAAA,YACnB,GAAG,OAAO,SAAS;AAAA,YACnB,OAAO,cAAc;AAAA,UACtB;AAAA,QACD,WAAW,CAAC,OAAO,SAAS,GAAG;AAC9B,iBAAO,KAAK,qCAAqC,SAAS,EAAE;AAAA,QAC7D;AAAA,MACD;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoB,QAAqB,WAAyB;AACzE,UAAM,iBAAwC,CAAC,eAAe,UAAU,SAAS,OAAO;AAExF,eAAW,SAAS,gBAAgB;AACnC,UAAI,CAAC,OAAO,KAAK,GAAG;AACnB,cAAM,IAAI,MAAM,SAAS,SAAS,4BAA4B,KAAK,EAAE;AAAA,MACtE;AAAA,IACD;AAEA,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,GAAG;AACjC,YAAM,IAAI,MAAM,SAAS,SAAS,yBAAyB;AAAA,IAC5D;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,mBAAmB,SAAiB,UAAyD;AACpG,QAAI;AAEH,YAAM,EAAE,MAAM,SAAS,aAAa,IAAI,oBAAoB,MAAM,OAAO;AAGzE,UAAI,CAAC,KAAK,MAAM;AACf,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAC/C;AACA,UAAI,CAAC,KAAK,aAAa;AACtB,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACtD;AACA,UAAI,CAAC,KAAK,OAAO;AAChB,cAAM,IAAI,MAAM,+BAA+B;AAAA,MAChD;AACA,UAAI,CAAC,KAAK,OAAO;AAChB,cAAM,IAAI,MAAM,+BAA+B;AAAA,MAChD;AAGA,YAAM,QAAQ,KAAK,MACjB,MAAM,GAAG,EACT,IAAI,CAAC,SAAiB,KAAK,KAAK,CAAC,EACjC,OAAO,CAAC,SAAiB,KAAK,SAAS,CAAC;AAG1C,YAAM,cAAc,CAAC,UAAU,QAAQ,OAAO;AAC9C,UAAI,CAAC,YAAY,SAAS,KAAK,KAAK,GAAG;AACtC,eAAO;AAAA,UACN,SAAS,KAAK,IAAI,gBAAgB,KAAK,KAAK,4HACtB,YAAY,KAAK,IAAI,CAAC;AAAA,QAC7C;AAAA,MACD;AAGA,YAAM,SAAsB;AAAA,QAC3B,aAAa,KAAK;AAAA,QAClB,QAAQ,aAAa,KAAK;AAAA,QAC1B;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,GAAI,KAAK,SAAS,EAAE,OAAO,KAAK,MAAM;AAAA,MACvC;AAEA,aAAO,EAAE,QAAQ,MAAM,KAAK,KAAK;AAAA,IAClC,SAAS,OAAO;AACf,YAAM,IAAI;AAAA,QACT,kCAAkC,QAAQ,KAAK,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACxG;AAAA,IACD;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAa,QAA+C;AAG3D,WAAO;AAAA,EACR;AACD;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ClaudeService
4
- } from "./chunk-POI7KLBH.js";
4
+ } from "./chunk-CDQEK2WD.js";
5
5
  import {
6
6
  logger
7
7
  } from "./chunk-UYVWLISQ.js";
@@ -63,4 +63,4 @@ var ClaudeContextManager = class {
63
63
  export {
64
64
  ClaudeContextManager
65
65
  };
66
- //# sourceMappingURL=chunk-UPUAQYAW.js.map
66
+ //# sourceMappingURL=chunk-S65T4O6I.js.map
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- calculatePortForBranch
4
- } from "./chunk-VU3QMIP2.js";
5
2
  import {
6
3
  installDependencies,
7
4
  runScript
8
5
  } from "./chunk-VBFDVGAE.js";
6
+ import {
7
+ calculatePortForBranch
8
+ } from "./chunk-VU3QMIP2.js";
9
9
  import {
10
10
  hasScript,
11
11
  readPackageJson
@@ -23,13 +23,13 @@ import {
23
23
  hasUncommittedChanges,
24
24
  isBranchMergedIntoMain,
25
25
  isFileTrackedByGit
26
- } from "./chunk-2W2FBL5G.js";
27
- import {
28
- MetadataManager
29
- } from "./chunk-IJ7IGJT3.js";
26
+ } from "./chunk-LN4H3A6A.js";
30
27
  import {
31
28
  SettingsManager
32
- } from "./chunk-VWNS6DH5.js";
29
+ } from "./chunk-OOU3DKNT.js";
30
+ import {
31
+ MetadataManager
32
+ } from "./chunk-YZTDGPFB.js";
33
33
  import {
34
34
  calculateForegroundColor,
35
35
  generateColorFromBranchName,
@@ -315,6 +315,49 @@ var LoomManager = class {
315
315
  );
316
316
  }
317
317
  }
318
+ let draftPrNumber = void 0;
319
+ let draftPrUrl = void 0;
320
+ const mergeBehavior = settingsData.mergeBehavior ?? { mode: "local" };
321
+ if (mergeBehavior.mode === "github-draft-pr" && input.type === "issue") {
322
+ if (!this.issueTracker.supportsPullRequests) {
323
+ throw new Error(
324
+ `The 'github-draft-pr' merge mode requires a GitHub-compatible issue tracker. Your provider (${this.issueTracker.providerName}) does not support pull requests.`
325
+ );
326
+ }
327
+ getLogger().info("Creating placeholder commit for draft PR...");
328
+ const { executeGitCommand: executeGitCommand2, PLACEHOLDER_COMMIT_PREFIX, pushBranchToRemote } = await import("./git-OQAPUPLP.js");
329
+ await executeGitCommand2(
330
+ [
331
+ "commit",
332
+ "--allow-empty",
333
+ "--no-verify",
334
+ "-m",
335
+ `${PLACEHOLDER_COMMIT_PREFIX} Temporary commit for draft PR (will be removed on finish) - issue #${input.identifier}`
336
+ ],
337
+ { cwd: worktreePath }
338
+ );
339
+ getLogger().debug("Placeholder commit created");
340
+ getLogger().info("Pushing branch to remote for draft PR...");
341
+ await pushBranchToRemote(branchName, worktreePath, { dryRun: false });
342
+ const { PRManager } = await import("./PRManager-7DSIMCAD.js");
343
+ const prManager = new PRManager(settingsData);
344
+ const prTitle = (issueData == null ? void 0 : issueData.title) ?? `Work on ${branchName}`;
345
+ const prBody = `Draft PR for issue #${input.identifier}
346
+
347
+ This PR was created automatically by iloom.`;
348
+ const mainBranch = settingsData.mainBranch ?? "main";
349
+ getLogger().info("Creating draft PR...");
350
+ const prResult = await prManager.createDraftPR(
351
+ branchName,
352
+ prTitle,
353
+ prBody,
354
+ mainBranch,
355
+ worktreePath
356
+ );
357
+ draftPrNumber = prResult.number;
358
+ draftPrUrl = prResult.url;
359
+ getLogger().success(`Draft PR created: ${prResult.url}`);
360
+ }
318
361
  const allMetadata = await this.metadataManager.listAllMetadata();
319
362
  const usedHexColors = allMetadata.filter((metadata) => metadata.colorHex !== null).map((metadata) => metadata.colorHex);
320
363
  const colorData = selectDistinctColor(branchName, usedHexColors);
@@ -348,8 +391,8 @@ var LoomManager = class {
348
391
  const setArguments = (_j = input.options) == null ? void 0 : _j.setArguments;
349
392
  const executablePath = (_k = input.options) == null ? void 0 : _k.executablePath;
350
393
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
351
- const { LoomLauncher } = await import("./LoomLauncher-S3YGJRJQ.js");
352
- const { ClaudeContextManager } = await import("./ClaudeContextManager-DK77227F.js");
394
+ const { LoomLauncher } = await import("./LoomLauncher-4UG2E4CD.js");
395
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-DQFKIMEP.js");
353
396
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
354
397
  const launcher = new LoomLauncher(claudeContext, this.settings);
355
398
  await launcher.launchLoom({
@@ -373,9 +416,26 @@ var LoomManager = class {
373
416
  });
374
417
  }
375
418
  const description = (issueData == null ? void 0 : issueData.title) ?? branchName;
376
- const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
419
+ let issue_numbers = [];
420
+ let extractedIssueNum = null;
421
+ if (input.type === "issue") {
422
+ issue_numbers = [String(input.identifier)];
423
+ } else if (input.type === "pr") {
424
+ extractedIssueNum = extractIssueNumber(branchName);
425
+ if (extractedIssueNum) {
426
+ issue_numbers = [extractedIssueNum];
427
+ }
428
+ }
377
429
  const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
378
430
  const sessionId = generateDeterministicSessionId(worktreePath);
431
+ let issueUrls = {};
432
+ if (input.type === "issue" && (issueData == null ? void 0 : issueData.url)) {
433
+ issueUrls = { [String(input.identifier)]: issueData.url };
434
+ } else if (input.type === "pr" && extractedIssueNum && (issueData == null ? void 0 : issueData.url)) {
435
+ const issueUrl = issueData.url.replace(`/pull/${input.identifier}`, `/issues/${extractedIssueNum}`);
436
+ issueUrls = { [extractedIssueNum]: issueUrl };
437
+ }
438
+ const prUrls = draftPrNumber && draftPrUrl ? { [String(draftPrNumber)]: draftPrUrl } : input.type === "pr" && (issueData == null ? void 0 : issueData.url) ? { [String(input.identifier)]: issueData.url } : {};
379
439
  const metadataInput = {
380
440
  description,
381
441
  branchName,
@@ -387,6 +447,9 @@ var LoomManager = class {
387
447
  colorHex: colorData.hex,
388
448
  sessionId,
389
449
  projectPath: this.gitWorktree.workingDirectory,
450
+ issueUrls,
451
+ prUrls,
452
+ ...draftPrNumber && { draftPrNumber },
390
453
  ...input.parentLoom && { parentLoom: input.parentLoom }
391
454
  };
392
455
  await this.metadataManager.writeMetadata(worktreePath, metadataInput);
@@ -475,7 +538,7 @@ var LoomManager = class {
475
538
  async checkAndWarnChildLooms(branchName) {
476
539
  let targetBranch = branchName;
477
540
  if (!targetBranch) {
478
- const { getCurrentBranch } = await import("./git-4BVOOOOV.js");
541
+ const { getCurrentBranch } = await import("./git-OQAPUPLP.js");
479
542
  targetBranch = await getCurrentBranch();
480
543
  }
481
544
  if (!targetBranch) {
@@ -900,8 +963,8 @@ var LoomManager = class {
900
963
  const executablePath = (_i = input.options) == null ? void 0 : _i.executablePath;
901
964
  if (enableClaude || enableCode || enableDevServer || enableTerminal) {
902
965
  getLogger().info("Launching workspace components...");
903
- const { LoomLauncher } = await import("./LoomLauncher-S3YGJRJQ.js");
904
- const { ClaudeContextManager } = await import("./ClaudeContextManager-DK77227F.js");
966
+ const { LoomLauncher } = await import("./LoomLauncher-4UG2E4CD.js");
967
+ const { ClaudeContextManager } = await import("./ClaudeContextManager-DQFKIMEP.js");
905
968
  const claudeContext = new ClaudeContextManager(void 0, void 0, this.settings);
906
969
  const launcher = new LoomLauncher(claudeContext, this.settings);
907
970
  await launcher.launchLoom({
@@ -926,9 +989,26 @@ var LoomManager = class {
926
989
  }
927
990
  const description = (existingMetadata == null ? void 0 : existingMetadata.description) ?? (issueData == null ? void 0 : issueData.title) ?? branchName;
928
991
  if (!existingMetadata) {
929
- const issue_numbers = input.type === "issue" ? [String(input.identifier)] : [];
992
+ let issue_numbers = [];
993
+ let extractedIssueNum = null;
994
+ if (input.type === "issue") {
995
+ issue_numbers = [String(input.identifier)];
996
+ } else if (input.type === "pr") {
997
+ extractedIssueNum = extractIssueNumber(branchName);
998
+ if (extractedIssueNum) {
999
+ issue_numbers = [extractedIssueNum];
1000
+ }
1001
+ }
930
1002
  const pr_numbers = input.type === "pr" ? [String(input.identifier)] : [];
931
1003
  const sessionId = generateDeterministicSessionId(worktreePath);
1004
+ let issueUrls = {};
1005
+ if (input.type === "issue" && (issueData == null ? void 0 : issueData.url)) {
1006
+ issueUrls = { [String(input.identifier)]: issueData.url };
1007
+ } else if (input.type === "pr" && extractedIssueNum && (issueData == null ? void 0 : issueData.url)) {
1008
+ const issueUrl = issueData.url.replace(`/pull/${input.identifier}`, `/issues/${extractedIssueNum}`);
1009
+ issueUrls = { [extractedIssueNum]: issueUrl };
1010
+ }
1011
+ const prUrls = input.type === "pr" && (issueData == null ? void 0 : issueData.url) ? { [String(input.identifier)]: issueData.url } : {};
932
1012
  const metadataInput = {
933
1013
  description,
934
1014
  branchName,
@@ -940,6 +1020,8 @@ var LoomManager = class {
940
1020
  colorHex,
941
1021
  sessionId,
942
1022
  projectPath: this.gitWorktree.workingDirectory,
1023
+ issueUrls,
1024
+ prUrls,
943
1025
  ...input.parentLoom && { parentLoom: input.parentLoom }
944
1026
  };
945
1027
  await this.metadataManager.writeMetadata(worktreePath, metadataInput);
@@ -2288,4 +2370,4 @@ export {
2288
2370
  DatabaseManager,
2289
2371
  ResourceCleanup
2290
2372
  };
2291
- //# sourceMappingURL=chunk-IARWMDAX.js.map
2373
+ //# sourceMappingURL=chunk-T5IIUG4Z.js.map