@iloom/cli 0.6.1 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +35 -18
  3. package/dist/{BranchNamingService-B5PVRR7F.js → BranchNamingService-FLPUUFOB.js} +2 -2
  4. package/dist/ClaudeContextManager-KE5TBZVZ.js +14 -0
  5. package/dist/ClaudeService-CRSETT3A.js +13 -0
  6. package/dist/{GitHubService-S2OGUTDR.js → GitHubService-O7U4UQ7N.js} +3 -3
  7. package/dist/{LoomLauncher-5LFM4LXB.js → LoomLauncher-NL65LSKP.js} +6 -6
  8. package/dist/{MetadataManager-DFI73J3G.js → MetadataManager-XJ2YB762.js} +2 -2
  9. package/dist/PRManager-2ABCWXHW.js +16 -0
  10. package/dist/{ProjectCapabilityDetector-S5FLNCFI.js → ProjectCapabilityDetector-IA56AUE6.js} +3 -3
  11. package/dist/{PromptTemplateManager-C3DK6XZL.js → PromptTemplateManager-7L3HJQQU.js} +2 -2
  12. package/dist/README.md +35 -18
  13. package/dist/{SettingsManager-35F5RUJH.js → SettingsManager-YU4VYPTW.js} +2 -2
  14. package/dist/agents/iloom-framework-detector.md +78 -9
  15. package/dist/agents/iloom-issue-analyze-and-plan.md +42 -17
  16. package/dist/agents/iloom-issue-analyzer.md +14 -14
  17. package/dist/agents/iloom-issue-complexity-evaluator.md +38 -15
  18. package/dist/agents/iloom-issue-enhancer.md +15 -15
  19. package/dist/agents/iloom-issue-implementer.md +44 -15
  20. package/dist/agents/iloom-issue-planner.md +121 -17
  21. package/dist/agents/iloom-issue-reviewer.md +15 -15
  22. package/dist/{build-FJVYP7EV.js → build-HQ5HGA3T.js} +9 -9
  23. package/dist/{chunk-VU3QMIP2.js → chunk-453NC377.js} +91 -15
  24. package/dist/chunk-453NC377.js.map +1 -0
  25. package/dist/{chunk-UQIXZ3BA.js → chunk-5V74K5ZA.js} +2 -2
  26. package/dist/{chunk-7WANFUIK.js → chunk-6TL3BYH6.js} +2 -2
  27. package/dist/{chunk-ZPSTA5PR.js → chunk-7GLZVDPQ.js} +2 -2
  28. package/dist/{chunk-64O2UIWO.js → chunk-AFRICMSW.js} +4 -4
  29. package/dist/{chunk-5TXLVEXT.js → chunk-C3AKFAIR.js} +2 -2
  30. package/dist/{chunk-K7SEEHKO.js → chunk-CNSTXBJ3.js} +7 -419
  31. package/dist/chunk-CNSTXBJ3.js.map +1 -0
  32. package/dist/{chunk-2A7WQKBE.js → chunk-DAOS6EC3.js} +96 -6
  33. package/dist/chunk-DAOS6EC3.js.map +1 -0
  34. package/dist/{chunk-TRQ76ISK.js → chunk-ELJKYFSH.js} +9 -9
  35. package/dist/{chunk-VDA5JMB4.js → chunk-EPPPDVHD.js} +21 -8
  36. package/dist/chunk-EPPPDVHD.js.map +1 -0
  37. package/dist/{chunk-LVBRMTE6.js → chunk-FEAJR6PN.js} +6 -6
  38. package/dist/{chunk-6YSFTPKW.js → chunk-FM4KBPVA.js} +18 -13
  39. package/dist/chunk-FM4KBPVA.js.map +1 -0
  40. package/dist/{chunk-AEIMYF4P.js → chunk-FP7G7DG3.js} +6 -2
  41. package/dist/chunk-FP7G7DG3.js.map +1 -0
  42. package/dist/{chunk-LT3SGBR7.js → chunk-GCPAZSGV.js} +36 -2
  43. package/dist/{chunk-LT3SGBR7.js.map → chunk-GCPAZSGV.js.map} +1 -1
  44. package/dist/chunk-GJMEKEI5.js +517 -0
  45. package/dist/chunk-GJMEKEI5.js.map +1 -0
  46. package/dist/{chunk-7Q66W4OH.js → chunk-HBJITKSZ.js} +37 -1
  47. package/dist/chunk-HBJITKSZ.js.map +1 -0
  48. package/dist/{chunk-7HIRPCKU.js → chunk-HVQNVRAF.js} +2 -2
  49. package/dist/{chunk-6U6VI4SZ.js → chunk-KVS4XGBQ.js} +4 -4
  50. package/dist/{chunk-SN3Z6EZO.js → chunk-N7FVXZNI.js} +2 -2
  51. package/dist/{chunk-I75JMBNB.js → chunk-QQFBMCAH.js} +54 -43
  52. package/dist/chunk-QQFBMCAH.js.map +1 -0
  53. package/dist/{chunk-AXX3QIKK.js → chunk-RD7I2Q2F.js} +2 -2
  54. package/dist/chunk-TIYJEEVO.js +79 -0
  55. package/dist/chunk-TIYJEEVO.js.map +1 -0
  56. package/dist/{chunk-EK3XCAAS.js → chunk-UDRZY65Y.js} +2 -2
  57. package/dist/{chunk-3PT7RKL5.js → chunk-USJSNHGG.js} +2 -2
  58. package/dist/{chunk-CFUWQHCJ.js → chunk-VWGKGNJP.js} +114 -35
  59. package/dist/chunk-VWGKGNJP.js.map +1 -0
  60. package/dist/{chunk-F6WVM437.js → chunk-WFQ5CLTR.js} +6 -3
  61. package/dist/chunk-WFQ5CLTR.js.map +1 -0
  62. package/dist/{chunk-BXCPJJYM.js → chunk-XPKN3QWY.js} +24 -6
  63. package/dist/chunk-XPKN3QWY.js.map +1 -0
  64. package/dist/chunk-YQNSZKKT.js +822 -0
  65. package/dist/chunk-YQNSZKKT.js.map +1 -0
  66. package/dist/{chunk-GEXP5IOF.js → chunk-ZA575VLF.js} +21 -8
  67. package/dist/chunk-ZA575VLF.js.map +1 -0
  68. package/dist/{claude-H33OQMXO.js → claude-6H36IBHO.js} +4 -2
  69. package/dist/{cleanup-BRUAINKE.js → cleanup-77U5ATYI.js} +20 -16
  70. package/dist/cleanup-77U5ATYI.js.map +1 -0
  71. package/dist/cli.js +361 -954
  72. package/dist/cli.js.map +1 -1
  73. package/dist/commit-ONRXU67O.js +237 -0
  74. package/dist/commit-ONRXU67O.js.map +1 -0
  75. package/dist/{compile-ULNO5F7Q.js → compile-CT7IR7O2.js} +9 -9
  76. package/dist/{contribute-Q6GX6AXK.js → contribute-GXKOIA42.js} +5 -5
  77. package/dist/{dev-server-4RCDJ5MU.js → dev-server-UKAPBGUR.js} +22 -74
  78. package/dist/dev-server-UKAPBGUR.js.map +1 -0
  79. package/dist/{feedback-O4Q55SVS.js → feedback-K3A4QUSG.js} +10 -10
  80. package/dist/{git-FVMGBHC2.js → git-ENLT2VNI.js} +6 -4
  81. package/dist/hooks/iloom-hook.js +30 -2
  82. package/dist/{ignite-VHV65WEZ.js → ignite-YUAOJ5PP.js} +20 -20
  83. package/dist/ignite-YUAOJ5PP.js.map +1 -0
  84. package/dist/index.d.ts +71 -27
  85. package/dist/index.js +196 -266
  86. package/dist/index.js.map +1 -1
  87. package/dist/init-XQQMFDM6.js +21 -0
  88. package/dist/{lint-5JMCWE4Y.js → lint-HAVU4U34.js} +9 -9
  89. package/dist/mcp/issue-management-server.js +359 -13
  90. package/dist/mcp/issue-management-server.js.map +1 -1
  91. package/dist/mcp/recap-server.js +13 -4
  92. package/dist/mcp/recap-server.js.map +1 -1
  93. package/dist/{open-WHVUYGPY.js → open-QI63XQ4F.js} +25 -76
  94. package/dist/open-QI63XQ4F.js.map +1 -0
  95. package/dist/{projects-SA76I4TZ.js → projects-TWY4RT2Z.js} +11 -4
  96. package/dist/projects-TWY4RT2Z.js.map +1 -0
  97. package/dist/prompts/init-prompt.txt +119 -51
  98. package/dist/prompts/issue-prompt.txt +132 -63
  99. package/dist/prompts/pr-prompt.txt +3 -3
  100. package/dist/prompts/regular-prompt.txt +16 -18
  101. package/dist/prompts/session-summary-prompt.txt +13 -13
  102. package/dist/{rebase-Y4AS6LQW.js → rebase-QYCRF7JG.js} +53 -8
  103. package/dist/rebase-QYCRF7JG.js.map +1 -0
  104. package/dist/{recap-VOOUXOGP.js → recap-ZKGHZCX6.js} +6 -6
  105. package/dist/{run-NCRK5NPR.js → run-YDVYORT2.js} +25 -76
  106. package/dist/run-YDVYORT2.js.map +1 -0
  107. package/dist/schema/settings.schema.json +14 -3
  108. package/dist/{shell-SBLXVOVJ.js → shell-2NNSIU34.js} +6 -6
  109. package/dist/{summary-CVFAMDOJ.js → summary-G6L3VAKK.js} +11 -10
  110. package/dist/{summary-CVFAMDOJ.js.map → summary-G6L3VAKK.js.map} +1 -1
  111. package/dist/{test-3KIVXI6J.js → test-75WAA6DU.js} +9 -9
  112. package/dist/{test-git-ZB6AGGRW.js → test-git-E2BLXR6M.js} +4 -4
  113. package/dist/{test-prefix-FBGXKMPA.js → test-prefix-A7JGGYAA.js} +4 -4
  114. package/dist/{test-webserver-YVQD42W6.js → test-webserver-NRMGT2HB.js} +29 -8
  115. package/dist/test-webserver-NRMGT2HB.js.map +1 -0
  116. package/package.json +3 -1
  117. package/dist/ClaudeContextManager-6J2EB4QU.js +0 -14
  118. package/dist/ClaudeService-O2PB22GX.js +0 -13
  119. package/dist/PRManager-GB3FOJ2W.js +0 -14
  120. package/dist/chunk-2A7WQKBE.js.map +0 -1
  121. package/dist/chunk-6YSFTPKW.js.map +0 -1
  122. package/dist/chunk-7Q66W4OH.js.map +0 -1
  123. package/dist/chunk-AEIMYF4P.js.map +0 -1
  124. package/dist/chunk-BXCPJJYM.js.map +0 -1
  125. package/dist/chunk-CFUWQHCJ.js.map +0 -1
  126. package/dist/chunk-F6WVM437.js.map +0 -1
  127. package/dist/chunk-GEXP5IOF.js.map +0 -1
  128. package/dist/chunk-I75JMBNB.js.map +0 -1
  129. package/dist/chunk-K7SEEHKO.js.map +0 -1
  130. package/dist/chunk-VDA5JMB4.js.map +0 -1
  131. package/dist/chunk-VU3QMIP2.js.map +0 -1
  132. package/dist/chunk-W6WVRHJ6.js +0 -251
  133. package/dist/chunk-W6WVRHJ6.js.map +0 -1
  134. package/dist/cleanup-BRUAINKE.js.map +0 -1
  135. package/dist/dev-server-4RCDJ5MU.js.map +0 -1
  136. package/dist/ignite-VHV65WEZ.js.map +0 -1
  137. package/dist/init-UTYRHNJJ.js +0 -21
  138. package/dist/open-WHVUYGPY.js.map +0 -1
  139. package/dist/projects-SA76I4TZ.js.map +0 -1
  140. package/dist/rebase-Y4AS6LQW.js.map +0 -1
  141. package/dist/run-NCRK5NPR.js.map +0 -1
  142. package/dist/test-webserver-YVQD42W6.js.map +0 -1
  143. /package/dist/{BranchNamingService-B5PVRR7F.js.map → BranchNamingService-FLPUUFOB.js.map} +0 -0
  144. /package/dist/{ClaudeContextManager-6J2EB4QU.js.map → ClaudeContextManager-KE5TBZVZ.js.map} +0 -0
  145. /package/dist/{ClaudeService-O2PB22GX.js.map → ClaudeService-CRSETT3A.js.map} +0 -0
  146. /package/dist/{GitHubService-S2OGUTDR.js.map → GitHubService-O7U4UQ7N.js.map} +0 -0
  147. /package/dist/{LoomLauncher-5LFM4LXB.js.map → LoomLauncher-NL65LSKP.js.map} +0 -0
  148. /package/dist/{MetadataManager-DFI73J3G.js.map → MetadataManager-XJ2YB762.js.map} +0 -0
  149. /package/dist/{PRManager-GB3FOJ2W.js.map → PRManager-2ABCWXHW.js.map} +0 -0
  150. /package/dist/{ProjectCapabilityDetector-S5FLNCFI.js.map → ProjectCapabilityDetector-IA56AUE6.js.map} +0 -0
  151. /package/dist/{PromptTemplateManager-C3DK6XZL.js.map → PromptTemplateManager-7L3HJQQU.js.map} +0 -0
  152. /package/dist/{SettingsManager-35F5RUJH.js.map → SettingsManager-YU4VYPTW.js.map} +0 -0
  153. /package/dist/{build-FJVYP7EV.js.map → build-HQ5HGA3T.js.map} +0 -0
  154. /package/dist/{chunk-UQIXZ3BA.js.map → chunk-5V74K5ZA.js.map} +0 -0
  155. /package/dist/{chunk-7WANFUIK.js.map → chunk-6TL3BYH6.js.map} +0 -0
  156. /package/dist/{chunk-ZPSTA5PR.js.map → chunk-7GLZVDPQ.js.map} +0 -0
  157. /package/dist/{chunk-64O2UIWO.js.map → chunk-AFRICMSW.js.map} +0 -0
  158. /package/dist/{chunk-5TXLVEXT.js.map → chunk-C3AKFAIR.js.map} +0 -0
  159. /package/dist/{chunk-TRQ76ISK.js.map → chunk-ELJKYFSH.js.map} +0 -0
  160. /package/dist/{chunk-LVBRMTE6.js.map → chunk-FEAJR6PN.js.map} +0 -0
  161. /package/dist/{chunk-7HIRPCKU.js.map → chunk-HVQNVRAF.js.map} +0 -0
  162. /package/dist/{chunk-6U6VI4SZ.js.map → chunk-KVS4XGBQ.js.map} +0 -0
  163. /package/dist/{chunk-SN3Z6EZO.js.map → chunk-N7FVXZNI.js.map} +0 -0
  164. /package/dist/{chunk-AXX3QIKK.js.map → chunk-RD7I2Q2F.js.map} +0 -0
  165. /package/dist/{chunk-EK3XCAAS.js.map → chunk-UDRZY65Y.js.map} +0 -0
  166. /package/dist/{chunk-3PT7RKL5.js.map → chunk-USJSNHGG.js.map} +0 -0
  167. /package/dist/{claude-H33OQMXO.js.map → claude-6H36IBHO.js.map} +0 -0
  168. /package/dist/{compile-ULNO5F7Q.js.map → compile-CT7IR7O2.js.map} +0 -0
  169. /package/dist/{contribute-Q6GX6AXK.js.map → contribute-GXKOIA42.js.map} +0 -0
  170. /package/dist/{feedback-O4Q55SVS.js.map → feedback-K3A4QUSG.js.map} +0 -0
  171. /package/dist/{git-FVMGBHC2.js.map → git-ENLT2VNI.js.map} +0 -0
  172. /package/dist/{init-UTYRHNJJ.js.map → init-XQQMFDM6.js.map} +0 -0
  173. /package/dist/{lint-5JMCWE4Y.js.map → lint-HAVU4U34.js.map} +0 -0
  174. /package/dist/{recap-VOOUXOGP.js.map → recap-ZKGHZCX6.js.map} +0 -0
  175. /package/dist/{shell-SBLXVOVJ.js.map → shell-2NNSIU34.js.map} +0 -0
  176. /package/dist/{test-3KIVXI6J.js.map → test-75WAA6DU.js.map} +0 -0
  177. /package/dist/{test-git-ZB6AGGRW.js.map → test-git-E2BLXR6M.js.map} +0 -0
  178. /package/dist/{test-prefix-FBGXKMPA.js.map → test-prefix-A7JGGYAA.js.map} +0 -0
package/dist/index.js CHANGED
@@ -494,11 +494,12 @@ import path2 from "path";
494
494
  import os from "os";
495
495
  import { z } from "zod";
496
496
  import deepmerge from "deepmerge";
497
- var AgentSettingsSchema, SpinAgentSettingsSchema, SummarySettingsSchema, WorkflowPermissionSchema, WorkflowPermissionSchemaNoDefaults, WorkflowsSettingsSchema, WorkflowsSettingsSchemaNoDefaults, CapabilitiesSettingsSchema, CapabilitiesSettingsSchemaNoDefaults, NeonSettingsSchema, DatabaseProvidersSettingsSchema, IloomSettingsSchema, IloomSettingsSchemaNoDefaults, SettingsManager;
497
+ var PROJECT_CAPABILITIES, AgentSettingsSchema, SpinAgentSettingsSchema, SummarySettingsSchema, WorkflowPermissionSchema, WorkflowPermissionSchemaNoDefaults, WorkflowsSettingsSchema, WorkflowsSettingsSchemaNoDefaults, CapabilitiesSettingsSchema, CapabilitiesSettingsSchemaNoDefaults, NeonSettingsSchema, DatabaseProvidersSettingsSchema, IloomSettingsSchema, IloomSettingsSchemaNoDefaults, SettingsManager;
498
498
  var init_SettingsManager = __esm({
499
499
  "src/lib/SettingsManager.ts"() {
500
500
  "use strict";
501
501
  init_logger();
502
+ PROJECT_CAPABILITIES = ["cli", "web"];
502
503
  AgentSettingsSchema = z.object({
503
504
  model: z.enum(["sonnet", "opus", "haiku"]).optional().describe("Claude model shorthand: sonnet, opus, or haiku")
504
505
  // Future: could add other per-agent overrides
@@ -511,7 +512,7 @@ var init_SettingsManager = __esm({
511
512
  });
512
513
  WorkflowPermissionSchema = z.object({
513
514
  permissionMode: z.enum(["plan", "acceptEdits", "bypassPermissions", "default"]).optional().describe("Permission mode for Claude CLI in this workflow type"),
514
- noVerify: z.boolean().optional().describe("Skip pre-commit hooks (--no-verify) when committing during finish workflow"),
515
+ noVerify: z.boolean().optional().describe("Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows"),
515
516
  startIde: z.boolean().default(true).describe("Launch IDE (code) when starting this workflow type"),
516
517
  startDevServer: z.boolean().default(true).describe("Launch development server when starting this workflow type"),
517
518
  startAiAgent: z.boolean().default(true).describe("Launch Claude Code agent when starting this workflow type"),
@@ -520,7 +521,7 @@ var init_SettingsManager = __esm({
520
521
  });
521
522
  WorkflowPermissionSchemaNoDefaults = z.object({
522
523
  permissionMode: z.enum(["plan", "acceptEdits", "bypassPermissions", "default"]).optional().describe("Permission mode for Claude CLI in this workflow type"),
523
- noVerify: z.boolean().optional().describe("Skip pre-commit hooks (--no-verify) when committing during finish workflow"),
524
+ noVerify: z.boolean().optional().describe("Skip pre-commit hooks (--no-verify) when committing during commit and finish workflows"),
524
525
  startIde: z.boolean().optional().describe("Launch IDE (code) when starting this workflow type"),
525
526
  startDevServer: z.boolean().optional().describe("Launch development server when starting this workflow type"),
526
527
  startAiAgent: z.boolean().optional().describe("Launch Claude Code agent when starting this workflow type"),
@@ -538,6 +539,7 @@ var init_SettingsManager = __esm({
538
539
  regular: WorkflowPermissionSchemaNoDefaults.optional()
539
540
  }).optional();
540
541
  CapabilitiesSettingsSchema = z.object({
542
+ capabilities: z.array(z.enum(PROJECT_CAPABILITIES)).optional().describe("Explicitly declared project capabilities (auto-detected if not specified)"),
541
543
  web: z.object({
542
544
  basePort: z.number().min(1, "Base port must be >= 1").max(65535, "Base port must be <= 65535").optional().describe("Base port for web workspace port calculations (default: 3000)")
543
545
  }).optional(),
@@ -546,6 +548,7 @@ var init_SettingsManager = __esm({
546
548
  }).optional()
547
549
  }).optional();
548
550
  CapabilitiesSettingsSchemaNoDefaults = z.object({
551
+ capabilities: z.array(z.enum(PROJECT_CAPABILITIES)).optional().describe("Explicitly declared project capabilities (auto-detected if not specified)"),
549
552
  web: z.object({
550
553
  basePort: z.number().min(1, "Base port must be >= 1").max(65535, "Base port must be <= 65535").optional().describe("Base port for web workspace port calculations (default: 3000)")
551
554
  }).optional(),
@@ -1171,6 +1174,30 @@ function getLogger() {
1171
1174
  var MetadataManager = class {
1172
1175
  constructor() {
1173
1176
  this.loomsDir = path3.join(os2.homedir(), ".config", "iloom-ai", "looms");
1177
+ this.finishedDir = path3.join(this.loomsDir, "finished");
1178
+ }
1179
+ /**
1180
+ * Convert MetadataFile to LoomMetadata with default values for optional fields
1181
+ */
1182
+ toMetadata(data) {
1183
+ return {
1184
+ description: data.description,
1185
+ created_at: data.created_at ?? null,
1186
+ branchName: data.branchName ?? null,
1187
+ worktreePath: data.worktreePath ?? null,
1188
+ issueType: data.issueType ?? null,
1189
+ issue_numbers: data.issue_numbers ?? [],
1190
+ pr_numbers: data.pr_numbers ?? [],
1191
+ issueTracker: data.issueTracker ?? null,
1192
+ colorHex: data.colorHex ?? null,
1193
+ sessionId: data.sessionId ?? null,
1194
+ projectPath: data.projectPath ?? null,
1195
+ issueUrls: data.issueUrls ?? {},
1196
+ prUrls: data.prUrls ?? {},
1197
+ draftPrNumber: data.draftPrNumber ?? null,
1198
+ capabilities: data.capabilities ?? [],
1199
+ parentLoom: data.parentLoom ?? null
1200
+ };
1174
1201
  }
1175
1202
  /**
1176
1203
  * Convert worktree path to filename slug per spec section 2.2
@@ -1230,6 +1257,7 @@ var MetadataManager = class {
1230
1257
  projectPath: input.projectPath,
1231
1258
  issueUrls: input.issueUrls,
1232
1259
  prUrls: input.prUrls,
1260
+ capabilities: input.capabilities,
1233
1261
  ...input.draftPrNumber && { draftPrNumber: input.draftPrNumber },
1234
1262
  ...input.parentLoom && { parentLoom: input.parentLoom }
1235
1263
  };
@@ -1259,23 +1287,7 @@ var MetadataManager = class {
1259
1287
  if (!data.description) {
1260
1288
  return null;
1261
1289
  }
1262
- return {
1263
- description: data.description,
1264
- created_at: data.created_at ?? null,
1265
- branchName: data.branchName ?? null,
1266
- worktreePath: data.worktreePath ?? null,
1267
- issueType: data.issueType ?? null,
1268
- issue_numbers: data.issue_numbers ?? [],
1269
- pr_numbers: data.pr_numbers ?? [],
1270
- issueTracker: data.issueTracker ?? null,
1271
- colorHex: data.colorHex ?? null,
1272
- sessionId: data.sessionId ?? null,
1273
- projectPath: data.projectPath ?? null,
1274
- issueUrls: data.issueUrls ?? {},
1275
- prUrls: data.prUrls ?? {},
1276
- draftPrNumber: data.draftPrNumber ?? null,
1277
- parentLoom: data.parentLoom ?? null
1278
- };
1290
+ return this.toMetadata(data);
1279
1291
  } catch (error) {
1280
1292
  getLogger().debug(
1281
1293
  `Could not read metadata for worktree ${worktreePath}: ${error instanceof Error ? error.message : String(error)}`
@@ -1309,23 +1321,7 @@ var MetadataManager = class {
1309
1321
  if (!data.description) {
1310
1322
  continue;
1311
1323
  }
1312
- results.push({
1313
- description: data.description,
1314
- created_at: data.created_at ?? null,
1315
- branchName: data.branchName ?? null,
1316
- worktreePath: data.worktreePath ?? null,
1317
- issueType: data.issueType ?? null,
1318
- issue_numbers: data.issue_numbers ?? [],
1319
- pr_numbers: data.pr_numbers ?? [],
1320
- issueTracker: data.issueTracker ?? null,
1321
- colorHex: data.colorHex ?? null,
1322
- sessionId: data.sessionId ?? null,
1323
- projectPath: data.projectPath ?? null,
1324
- issueUrls: data.issueUrls ?? {},
1325
- prUrls: data.prUrls ?? {},
1326
- draftPrNumber: data.draftPrNumber ?? null,
1327
- parentLoom: data.parentLoom ?? null
1328
- });
1324
+ results.push(this.toMetadata(data));
1329
1325
  } catch (error) {
1330
1326
  getLogger().debug(
1331
1327
  `Skipping metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`
@@ -1362,10 +1358,104 @@ var MetadataManager = class {
1362
1358
  );
1363
1359
  }
1364
1360
  }
1361
+ /**
1362
+ * Archive metadata for a finished worktree
1363
+ *
1364
+ * Moves the metadata file to the finished/ subdirectory and adds
1365
+ * status: 'finished' and finishedAt timestamp fields.
1366
+ *
1367
+ * Idempotent: silently succeeds if source file doesn't exist
1368
+ * Non-fatal: logs warning on errors but doesn't throw
1369
+ *
1370
+ * @param worktreePath - Absolute path to the worktree
1371
+ */
1372
+ async archiveMetadata(worktreePath) {
1373
+ try {
1374
+ const filename = this.slugifyPath(worktreePath);
1375
+ const sourcePath = path3.join(this.loomsDir, filename);
1376
+ if (!await fs.pathExists(sourcePath)) {
1377
+ getLogger().debug(`No metadata file to archive for worktree: ${worktreePath}`);
1378
+ return;
1379
+ }
1380
+ const content = await fs.readFile(sourcePath, "utf8");
1381
+ const data = JSON.parse(content);
1382
+ const finishedData = {
1383
+ ...data,
1384
+ status: "finished",
1385
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString()
1386
+ };
1387
+ await fs.ensureDir(this.finishedDir, { mode: 493 });
1388
+ const destPath = path3.join(this.finishedDir, filename);
1389
+ await fs.writeFile(destPath, JSON.stringify(finishedData, null, 2), { mode: 420 });
1390
+ await fs.unlink(sourcePath);
1391
+ getLogger().debug(`Metadata archived for worktree: ${worktreePath}`);
1392
+ } catch (error) {
1393
+ getLogger().warn(
1394
+ `Failed to archive metadata for worktree: ${error instanceof Error ? error.message : String(error)}`
1395
+ );
1396
+ }
1397
+ }
1398
+ /**
1399
+ * List all finished loom metadata files
1400
+ *
1401
+ * Returns an array of LoomMetadata objects for all finished looms
1402
+ * in the finished/ subdirectory, sorted by finishedAt in descending order
1403
+ * (most recently finished first).
1404
+ *
1405
+ * @returns Array of LoomMetadata objects from finished files, sorted by finishedAt desc
1406
+ */
1407
+ async listFinishedMetadata() {
1408
+ const results = [];
1409
+ try {
1410
+ if (!await fs.pathExists(this.finishedDir)) {
1411
+ return results;
1412
+ }
1413
+ const files = await fs.readdir(this.finishedDir);
1414
+ for (const file of files) {
1415
+ if (!file.endsWith(".json")) {
1416
+ continue;
1417
+ }
1418
+ try {
1419
+ const filePath = path3.join(this.finishedDir, file);
1420
+ const content = await fs.readFile(filePath, "utf8");
1421
+ const data = JSON.parse(content);
1422
+ if (!data.description) {
1423
+ continue;
1424
+ }
1425
+ const metadata = this.toMetadata(data);
1426
+ metadata.status = data.status ?? "finished";
1427
+ metadata.finishedAt = data.finishedAt ?? null;
1428
+ results.push(metadata);
1429
+ } catch (error) {
1430
+ getLogger().warn(
1431
+ `Skipping finished metadata file ${file}: ${error instanceof Error ? error.message : String(error)}`
1432
+ );
1433
+ }
1434
+ }
1435
+ results.sort((a, b) => {
1436
+ const aTime = a.finishedAt ? new Date(a.finishedAt).getTime() : 0;
1437
+ const bTime = b.finishedAt ? new Date(b.finishedAt).getTime() : 0;
1438
+ return bTime - aTime;
1439
+ });
1440
+ } catch (error) {
1441
+ getLogger().warn(
1442
+ `Could not list finished metadata files: ${error instanceof Error ? error.message : String(error)}`
1443
+ );
1444
+ }
1445
+ return results;
1446
+ }
1365
1447
  };
1366
1448
 
1367
1449
  // src/utils/git.ts
1368
1450
  init_logger();
1451
+ var GitCommandError = class extends Error {
1452
+ constructor(message, exitCode, stderr) {
1453
+ super(message);
1454
+ this.exitCode = exitCode;
1455
+ this.stderr = stderr;
1456
+ this.name = "GitCommandError";
1457
+ }
1458
+ };
1369
1459
  async function executeGitCommand(args, options) {
1370
1460
  try {
1371
1461
  const result = await execa2("git", args, {
@@ -1381,7 +1471,11 @@ async function executeGitCommand(args, options) {
1381
1471
  } catch (error) {
1382
1472
  const execaError = error;
1383
1473
  const stderr = execaError.stderr ?? execaError.message ?? "Unknown Git error";
1384
- throw new Error(`Git command failed: ${stderr}`);
1474
+ throw new GitCommandError(
1475
+ `Git command failed: ${stderr}`,
1476
+ execaError.exitCode,
1477
+ stderr
1478
+ );
1385
1479
  }
1386
1480
  }
1387
1481
  function parseWorktreeList(output, defaultBranch) {
@@ -1621,10 +1715,10 @@ async function getRepoRoot(path7 = process.cwd()) {
1621
1715
  const repoRoot = trimmedPath.replace(/\/\.git\/worktrees\/[^/]+$/, "").replace(/\/\.git$/, "");
1622
1716
  return repoRoot;
1623
1717
  } catch (error) {
1624
- const errorMessage = error instanceof Error ? error.message : String(error);
1625
- if (errorMessage.includes("not a git repository")) {
1718
+ if (error instanceof GitCommandError && (error.exitCode === 128 || /fatal: not a git repository/i.test(error.stderr))) {
1626
1719
  logger.info(`Note: No git repository detected: ${path7}`);
1627
1720
  } else {
1721
+ const errorMessage = error instanceof Error ? error.message : String(error);
1628
1722
  logger.warn(`Failed to determine repo root from git-common-dir: ${path7}`, errorMessage);
1629
1723
  }
1630
1724
  return null;
@@ -1979,7 +2073,7 @@ async function getMergeTargetBranch(worktreePath = process.cwd(), options) {
1979
2073
  logger.debug(`Using configured main branch as merge target: ${mainBranch}`);
1980
2074
  return mainBranch;
1981
2075
  }
1982
- var PLACEHOLDER_COMMIT_PREFIX = "[iloom-temp]";
2076
+ var PLACEHOLDER_COMMIT_PREFIX = "[iloom]";
1983
2077
  async function isPlaceholderCommit(cwd = process.cwd()) {
1984
2078
  try {
1985
2079
  const subject = await executeGitCommand(["log", "-1", "--format=%s", "HEAD"], { cwd });
@@ -3058,7 +3152,6 @@ async function updateLinearIssueState(identifier, stateName) {
3058
3152
  }
3059
3153
 
3060
3154
  // src/lib/LinearService.ts
3061
- init_logger();
3062
3155
  var LinearService = class {
3063
3156
  constructor(config, options) {
3064
3157
  // IssueTracker interface implementation
@@ -3077,22 +3170,22 @@ var LinearService = class {
3077
3170
  * @returns Detection result with type and identifier
3078
3171
  */
3079
3172
  async detectInputType(input, _repo) {
3080
- logger.debug(`LinearService.detectInputType called with input: "${input}"`);
3173
+ getLogger().debug(`LinearService.detectInputType called with input: "${input}"`);
3081
3174
  const linearPattern = /^([A-Z]{2,}-\d+)$/i;
3082
3175
  const match = input.match(linearPattern);
3083
3176
  if (!(match == null ? void 0 : match[1])) {
3084
- logger.debug(`LinearService: Input "${input}" does not match Linear pattern`);
3177
+ getLogger().debug(`LinearService: Input "${input}" does not match Linear pattern`);
3085
3178
  return { type: "unknown", identifier: null, rawInput: input };
3086
3179
  }
3087
3180
  const identifier = match[1].toUpperCase();
3088
- logger.debug(`LinearService: Matched Linear identifier: ${identifier}`);
3089
- logger.debug(`LinearService: Checking if ${identifier} is a valid Linear issue via SDK`);
3181
+ getLogger().debug(`LinearService: Matched Linear identifier: ${identifier}`);
3182
+ getLogger().debug(`LinearService: Checking if ${identifier} is a valid Linear issue via SDK`);
3090
3183
  const issue = await this.isValidIssue(identifier);
3091
3184
  if (issue) {
3092
- logger.debug(`LinearService: Issue ${identifier} found: "${issue.title}"`);
3185
+ getLogger().debug(`LinearService: Issue ${identifier} found: "${issue.title}"`);
3093
3186
  return { type: "issue", identifier, rawInput: input };
3094
3187
  }
3095
- logger.debug(`LinearService: Issue ${identifier} NOT found by SDK`);
3188
+ getLogger().debug(`LinearService: Issue ${identifier} NOT found by SDK`);
3096
3189
  return { type: "unknown", identifier: null, rawInput: input };
3097
3190
  }
3098
3191
  /**
@@ -3153,7 +3246,7 @@ var LinearService = class {
3153
3246
  "Linear teamId not configured. Run `il init` to configure Linear settings."
3154
3247
  );
3155
3248
  }
3156
- logger.info(`Creating Linear issue in team ${this.config.teamId}: ${title}`);
3249
+ getLogger().info(`Creating Linear issue in team ${this.config.teamId}: ${title}`);
3157
3250
  const result = await createLinearIssue(title, body, this.config.teamId, labels);
3158
3251
  return {
3159
3252
  number: result.identifier,
@@ -3176,7 +3269,7 @@ var LinearService = class {
3176
3269
  * @throws LinearServiceError if state update fails
3177
3270
  */
3178
3271
  async moveIssueToInProgress(identifier) {
3179
- logger.info(`Moving Linear issue ${identifier} to In Progress`);
3272
+ getLogger().info(`Moving Linear issue ${identifier} to In Progress`);
3180
3273
  await updateLinearIssueState(String(identifier), "In Progress");
3181
3274
  }
3182
3275
  /**
@@ -3262,11 +3355,25 @@ var IssueTrackerFactory = class {
3262
3355
  };
3263
3356
 
3264
3357
  // src/lib/EnvironmentManager.ts
3265
- import fs3 from "fs-extra";
3358
+ import fs4 from "fs-extra";
3266
3359
  init_env();
3267
3360
 
3268
3361
  // src/utils/port.ts
3269
3362
  import { createHash } from "crypto";
3363
+ init_env();
3364
+ import fs3 from "fs-extra";
3365
+ init_logger();
3366
+ function wrapPort(rawPort, basePort) {
3367
+ if (rawPort <= 65535) return rawPort;
3368
+ const range = 65535 - basePort;
3369
+ return (rawPort - basePort - 1) % range + basePort + 1;
3370
+ }
3371
+ function extractNumericSuffix(issueId) {
3372
+ const match = issueId.match(/[-_]?(\d+)$/);
3373
+ const digits = match == null ? void 0 : match[1];
3374
+ if (digits === void 0) return null;
3375
+ return parseInt(digits, 10);
3376
+ }
3270
3377
  function generatePortOffsetFromBranchName(branchName) {
3271
3378
  if (!branchName || branchName.trim().length === 0) {
3272
3379
  throw new Error("Branch name cannot be empty");
@@ -3280,12 +3387,21 @@ function generatePortOffsetFromBranchName(branchName) {
3280
3387
  function calculatePortForBranch(branchName, basePort = 3e3) {
3281
3388
  const offset = generatePortOffsetFromBranchName(branchName);
3282
3389
  const port = basePort + offset;
3283
- if (port > 65535) {
3284
- throw new Error(
3285
- `Calculated port ${port} exceeds maximum (65535). Use a lower base port (current: ${basePort}).`
3286
- );
3390
+ return wrapPort(port, basePort);
3391
+ }
3392
+ function calculatePortFromIdentifier(identifier, basePort = 3e3) {
3393
+ if (typeof identifier === "number") {
3394
+ return wrapPort(basePort + identifier, basePort);
3395
+ }
3396
+ const numericValue = parseInt(identifier, 10);
3397
+ if (!isNaN(numericValue) && String(numericValue) === identifier) {
3398
+ return wrapPort(basePort + numericValue, basePort);
3287
3399
  }
3288
- return port;
3400
+ const numericSuffix = extractNumericSuffix(identifier);
3401
+ if (numericSuffix !== null) {
3402
+ return wrapPort(basePort + numericSuffix, basePort);
3403
+ }
3404
+ return calculatePortForBranch(`issue-${identifier}`, basePort);
3289
3405
  }
3290
3406
 
3291
3407
  // src/lib/EnvironmentManager.ts
@@ -3303,15 +3419,15 @@ var EnvironmentManager = class {
3303
3419
  if (!validation.valid) {
3304
3420
  throw new Error(validation.error ?? "Invalid variable name");
3305
3421
  }
3306
- const fileExists = await fs3.pathExists(filePath);
3422
+ const fileExists = await fs4.pathExists(filePath);
3307
3423
  if (!fileExists) {
3308
3424
  getLogger().info(`Creating ${filePath} with ${key}...`);
3309
3425
  const content = formatEnvLine(key, value);
3310
- await fs3.writeFile(filePath, content, "utf8");
3426
+ await fs4.writeFile(filePath, content, "utf8");
3311
3427
  getLogger().success(`${filePath} created with ${key}`);
3312
3428
  return;
3313
3429
  }
3314
- const existingContent = await fs3.readFile(filePath, "utf8");
3430
+ const existingContent = await fs4.readFile(filePath, "utf8");
3315
3431
  const envMap = parseEnvFile(existingContent);
3316
3432
  let backupPath;
3317
3433
  if (backup) {
@@ -3348,7 +3464,7 @@ var EnvironmentManager = class {
3348
3464
  getLogger().success(`${key} updated successfully`);
3349
3465
  }
3350
3466
  const newContent = newLines.join("\n");
3351
- await fs3.writeFile(filePath, newContent, "utf8");
3467
+ await fs4.writeFile(filePath, newContent, "utf8");
3352
3468
  return backupPath;
3353
3469
  }
3354
3470
  /**
@@ -3356,7 +3472,7 @@ var EnvironmentManager = class {
3356
3472
  */
3357
3473
  async readEnvFile(filePath) {
3358
3474
  try {
3359
- const content = await fs3.readFile(filePath, "utf8");
3475
+ const content = await fs4.readFile(filePath, "utf8");
3360
3476
  return parseEnvFile(content);
3361
3477
  } catch (error) {
3362
3478
  getLogger().debug(
@@ -3379,12 +3495,12 @@ var EnvironmentManager = class {
3379
3495
  * @private
3380
3496
  */
3381
3497
  async copyIfExists(source, destination) {
3382
- const sourceExists = await fs3.pathExists(source);
3498
+ const sourceExists = await fs4.pathExists(source);
3383
3499
  if (!sourceExists) {
3384
3500
  getLogger().debug(`Source file ${source} does not exist, skipping copy`);
3385
3501
  return;
3386
3502
  }
3387
- await fs3.copy(source, destination, { overwrite: false });
3503
+ await fs4.copy(source, destination, { overwrite: false });
3388
3504
  getLogger().success(`Copied ${source} to ${destination}`);
3389
3505
  }
3390
3506
  /**
@@ -3392,30 +3508,15 @@ var EnvironmentManager = class {
3392
3508
  * Implements:
3393
3509
  * - Issue/PR: 3000 + issue/PR number
3394
3510
  * - Branch: 3000 + deterministic hash offset (1-999)
3511
+ * Delegates to calculatePortFromIdentifier for the core calculation.
3395
3512
  */
3396
3513
  calculatePort(options) {
3397
3514
  const basePort = options.basePort ?? 3e3;
3398
3515
  if (options.issueNumber !== void 0) {
3399
- const numericIssue = typeof options.issueNumber === "number" ? options.issueNumber : parseInt(String(options.issueNumber), 10);
3400
- if (!isNaN(numericIssue) && String(numericIssue) === String(options.issueNumber)) {
3401
- const port = basePort + numericIssue;
3402
- if (port > 65535) {
3403
- throw new Error(
3404
- `Calculated port ${port} exceeds maximum (65535). Use a lower base port or issue number.`
3405
- );
3406
- }
3407
- return port;
3408
- }
3409
- return calculatePortForBranch(String(options.issueNumber), basePort);
3516
+ return calculatePortFromIdentifier(options.issueNumber, basePort);
3410
3517
  }
3411
3518
  if (options.prNumber !== void 0) {
3412
- const port = basePort + options.prNumber;
3413
- if (port > 65535) {
3414
- throw new Error(
3415
- `Calculated port ${port} exceeds maximum (65535). Use a lower base port or PR number.`
3416
- );
3417
- }
3418
- return port;
3519
+ return calculatePortFromIdentifier(options.prNumber, basePort);
3419
3520
  }
3420
3521
  if (options.branchName !== void 0) {
3421
3522
  return calculatePortForBranch(options.branchName, basePort);
@@ -3445,7 +3546,7 @@ var EnvironmentManager = class {
3445
3546
  */
3446
3547
  async validateEnvFile(filePath) {
3447
3548
  try {
3448
- const content = await fs3.readFile(filePath, "utf8");
3549
+ const content = await fs4.readFile(filePath, "utf8");
3449
3550
  const envMap = parseEnvFile(content);
3450
3551
  const errors = [];
3451
3552
  for (const [key, value] of envMap.entries()) {
@@ -3473,7 +3574,7 @@ var EnvironmentManager = class {
3473
3574
  async createBackup(filePath) {
3474
3575
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3475
3576
  const backupPath = `${filePath}${this.backupSuffix}-${timestamp}`;
3476
- await fs3.copy(filePath, backupPath);
3577
+ await fs4.copy(filePath, backupPath);
3477
3578
  getLogger().debug(`Created backup at ${backupPath}`);
3478
3579
  return backupPath;
3479
3580
  }
@@ -3481,7 +3582,7 @@ var EnvironmentManager = class {
3481
3582
 
3482
3583
  // src/lib/DatabaseManager.ts
3483
3584
  init_env();
3484
- import fs4 from "fs-extra";
3585
+ import fs5 from "fs-extra";
3485
3586
  var DatabaseManager = class {
3486
3587
  constructor(provider, environment, databaseUrlEnvVarName = "DATABASE_URL") {
3487
3588
  this.provider = provider;
@@ -3670,7 +3771,7 @@ var DatabaseManager = class {
3670
3771
  const hasConfiguredVar = await hasVariableInAnyEnvFile(
3671
3772
  workspacePath,
3672
3773
  this.databaseUrlEnvVarName,
3673
- async (p) => fs4.pathExists(p),
3774
+ async (p) => fs5.pathExists(p),
3674
3775
  async (p, v) => this.environment.getEnvVariable(p, v)
3675
3776
  );
3676
3777
  if (hasConfiguredVar) {
@@ -3690,7 +3791,7 @@ var DatabaseManager = class {
3690
3791
  const hasDefaultVar = await hasVariableInAnyEnvFile(
3691
3792
  workspacePath,
3692
3793
  "DATABASE_URL",
3693
- async (p) => fs4.pathExists(p),
3794
+ async (p) => fs5.pathExists(p),
3694
3795
  async (p, v) => this.environment.getEnvVariable(p, v)
3695
3796
  );
3696
3797
  if (hasDefaultVar) {
@@ -3713,7 +3814,7 @@ init_logger();
3713
3814
  import { execa as execa4 } from "execa";
3714
3815
  import { existsSync as existsSync2 } from "fs";
3715
3816
  import { join } from "path";
3716
- import { createHash as createHash3 } from "crypto";
3817
+ import { createHash as createHash3, randomUUID } from "crypto";
3717
3818
  init_terminal();
3718
3819
  async function detectClaudeCli() {
3719
3820
  try {
@@ -4002,6 +4103,10 @@ import { readFile as readFile2 } from "fs/promises";
4002
4103
  import { accessSync } from "fs";
4003
4104
  import path6 from "path";
4004
4105
  import { fileURLToPath } from "url";
4106
+ import Handlebars from "handlebars";
4107
+ Handlebars.registerHelper("raw", function(options) {
4108
+ return options.fn(this);
4109
+ });
4005
4110
  var PromptTemplateManager = class {
4006
4111
  constructor(templateDir) {
4007
4112
  if (templateDir) {
@@ -4048,187 +4153,11 @@ var PromptTemplateManager = class {
4048
4153
  }
4049
4154
  }
4050
4155
  /**
4051
- * Substitute variables in a template string
4156
+ * Substitute variables in a template string using Handlebars
4052
4157
  */
4053
4158
  substituteVariables(template, variables) {
4054
- let result = template;
4055
- result = this.processConditionalSections(result, variables);
4056
- if (variables.ISSUE_NUMBER !== void 0) {
4057
- result = result.replace(/ISSUE_NUMBER/g, String(variables.ISSUE_NUMBER));
4058
- }
4059
- if (variables.PR_NUMBER !== void 0) {
4060
- result = result.replace(/PR_NUMBER/g, String(variables.PR_NUMBER));
4061
- }
4062
- if (variables.ISSUE_TITLE !== void 0) {
4063
- result = result.replace(/ISSUE_TITLE/g, variables.ISSUE_TITLE);
4064
- }
4065
- if (variables.PR_TITLE !== void 0) {
4066
- result = result.replace(/PR_TITLE/g, variables.PR_TITLE);
4067
- }
4068
- if (variables.WORKSPACE_PATH !== void 0) {
4069
- result = result.replace(/WORKSPACE_PATH/g, variables.WORKSPACE_PATH);
4070
- }
4071
- if (variables.PORT !== void 0) {
4072
- result = result.replace(/PORT/g, String(variables.PORT));
4073
- }
4074
- if (variables.SETTINGS_SCHEMA !== void 0) {
4075
- result = result.replace(/SETTINGS_SCHEMA/g, variables.SETTINGS_SCHEMA);
4076
- }
4077
- if (variables.SETTINGS_GLOBAL_JSON !== void 0) {
4078
- result = result.replace(/SETTINGS_GLOBAL_JSON/g, variables.SETTINGS_GLOBAL_JSON);
4079
- }
4080
- if (variables.SETTINGS_JSON !== void 0) {
4081
- result = result.replace(/SETTINGS_JSON/g, variables.SETTINGS_JSON);
4082
- }
4083
- if (variables.SETTINGS_LOCAL_JSON !== void 0) {
4084
- result = result.replace(/SETTINGS_LOCAL_JSON/g, variables.SETTINGS_LOCAL_JSON);
4085
- }
4086
- if (variables.SHELL_TYPE !== void 0) {
4087
- result = result.replace(/SHELL_TYPE/g, variables.SHELL_TYPE);
4088
- }
4089
- if (variables.SHELL_CONFIG_PATH !== void 0) {
4090
- result = result.replace(/SHELL_CONFIG_PATH/g, variables.SHELL_CONFIG_PATH);
4091
- }
4092
- if (variables.SHELL_CONFIG_CONTENT !== void 0) {
4093
- result = result.replace(/SHELL_CONFIG_CONTENT/g, variables.SHELL_CONFIG_CONTENT);
4094
- }
4095
- if (variables.REMOTES_INFO !== void 0) {
4096
- result = result.replace(/REMOTES_INFO/g, variables.REMOTES_INFO);
4097
- }
4098
- if (variables.MULTIPLE_REMOTES !== void 0) {
4099
- result = result.replace(/MULTIPLE_REMOTES/g, variables.MULTIPLE_REMOTES);
4100
- }
4101
- if (variables.SINGLE_REMOTE !== void 0) {
4102
- result = result.replace(/SINGLE_REMOTE/g, variables.SINGLE_REMOTE);
4103
- }
4104
- if (variables.SINGLE_REMOTE_NAME !== void 0) {
4105
- result = result.replace(/SINGLE_REMOTE_NAME/g, variables.SINGLE_REMOTE_NAME);
4106
- }
4107
- if (variables.SINGLE_REMOTE_URL !== void 0) {
4108
- result = result.replace(/SINGLE_REMOTE_URL/g, variables.SINGLE_REMOTE_URL);
4109
- }
4110
- if (variables.NO_REMOTES !== void 0) {
4111
- result = result.replace(/NO_REMOTES/g, variables.NO_REMOTES);
4112
- }
4113
- if (variables.README_CONTENT !== void 0) {
4114
- result = result.replace(/README_CONTENT/g, variables.README_CONTENT);
4115
- }
4116
- if (variables.SETTINGS_SCHEMA_CONTENT !== void 0) {
4117
- result = result.replace(/SETTINGS_SCHEMA_CONTENT/g, variables.SETTINGS_SCHEMA_CONTENT);
4118
- }
4119
- if (variables.VSCODE_SETTINGS_GITIGNORED !== void 0) {
4120
- result = result.replace(/VSCODE_SETTINGS_GITIGNORED/g, variables.VSCODE_SETTINGS_GITIGNORED);
4121
- }
4122
- if (variables.SESSION_CONTEXT !== void 0) {
4123
- result = result.replace(/SESSION_CONTEXT/g, variables.SESSION_CONTEXT);
4124
- }
4125
- if (variables.BRANCH_NAME !== void 0) {
4126
- result = result.replace(/BRANCH_NAME/g, variables.BRANCH_NAME);
4127
- }
4128
- if (variables.LOOM_TYPE !== void 0) {
4129
- result = result.replace(/LOOM_TYPE/g, variables.LOOM_TYPE);
4130
- }
4131
- if (variables.COMPACT_SUMMARIES !== void 0) {
4132
- result = result.replace(/COMPACT_SUMMARIES/g, variables.COMPACT_SUMMARIES);
4133
- }
4134
- if (variables.DRAFT_PR_NUMBER !== void 0) {
4135
- result = result.replace(/DRAFT_PR_NUMBER/g, String(variables.DRAFT_PR_NUMBER));
4136
- }
4137
- return result;
4138
- }
4139
- /**
4140
- * Process conditional sections in template
4141
- * Format: {{#IF ONE_SHOT_MODE}}content{{/IF ONE_SHOT_MODE}}
4142
- *
4143
- * Note: /s flag allows . to match newlines
4144
- */
4145
- processConditionalSections(template, variables) {
4146
- let result = template;
4147
- const oneShotRegex = /\{\{#IF ONE_SHOT_MODE\}\}(.*?)\{\{\/IF ONE_SHOT_MODE\}\}/gs;
4148
- if (variables.ONE_SHOT_MODE === true) {
4149
- result = result.replace(oneShotRegex, "$1");
4150
- } else {
4151
- result = result.replace(oneShotRegex, "");
4152
- }
4153
- const settingsJsonRegex = /\{\{#IF SETTINGS_JSON\}\}(.*?)\{\{\/IF SETTINGS_JSON\}\}/gs;
4154
- if (variables.SETTINGS_JSON !== void 0 && variables.SETTINGS_JSON !== "") {
4155
- result = result.replace(settingsJsonRegex, "$1");
4156
- } else {
4157
- result = result.replace(settingsJsonRegex, "");
4158
- }
4159
- const settingsGlobalJsonRegex = /\{\{#IF SETTINGS_GLOBAL_JSON\}\}(.*?)\{\{\/IF SETTINGS_GLOBAL_JSON\}\}/gs;
4160
- if (variables.SETTINGS_GLOBAL_JSON !== void 0 && variables.SETTINGS_GLOBAL_JSON !== "") {
4161
- result = result.replace(settingsGlobalJsonRegex, "$1");
4162
- } else {
4163
- result = result.replace(settingsGlobalJsonRegex, "");
4164
- }
4165
- const settingsLocalJsonRegex = /\{\{#IF SETTINGS_LOCAL_JSON\}\}(.*?)\{\{\/IF SETTINGS_LOCAL_JSON\}\}/gs;
4166
- if (variables.SETTINGS_LOCAL_JSON !== void 0 && variables.SETTINGS_LOCAL_JSON !== "") {
4167
- result = result.replace(settingsLocalJsonRegex, "$1");
4168
- } else {
4169
- result = result.replace(settingsLocalJsonRegex, "");
4170
- }
4171
- const multipleRemotesRegex = /\{\{#IF MULTIPLE_REMOTES\}\}(.*?)\{\{\/IF MULTIPLE_REMOTES\}\}/gs;
4172
- if (variables.MULTIPLE_REMOTES !== void 0 && variables.MULTIPLE_REMOTES !== "") {
4173
- result = result.replace(multipleRemotesRegex, "$1");
4174
- } else {
4175
- result = result.replace(multipleRemotesRegex, "");
4176
- }
4177
- const singleRemoteRegex = /\{\{#IF SINGLE_REMOTE\}\}(.*?)\{\{\/IF SINGLE_REMOTE\}\}/gs;
4178
- if (variables.SINGLE_REMOTE !== void 0 && variables.SINGLE_REMOTE !== "") {
4179
- result = result.replace(singleRemoteRegex, "$1");
4180
- } else {
4181
- result = result.replace(singleRemoteRegex, "");
4182
- }
4183
- const noRemotesRegex = /\{\{#IF NO_REMOTES\}\}(.*?)\{\{\/IF NO_REMOTES\}\}/gs;
4184
- if (variables.NO_REMOTES !== void 0 && variables.NO_REMOTES !== "") {
4185
- result = result.replace(noRemotesRegex, "$1");
4186
- } else {
4187
- result = result.replace(noRemotesRegex, "");
4188
- }
4189
- const firstTimeUserRegex = /\{\{#IF FIRST_TIME_USER\}\}(.*?)\{\{\/IF FIRST_TIME_USER\}\}/gs;
4190
- if (variables.FIRST_TIME_USER === true) {
4191
- result = result.replace(firstTimeUserRegex, "$1");
4192
- } else {
4193
- result = result.replace(firstTimeUserRegex, "");
4194
- }
4195
- const interactiveModeRegex = /\{\{#IF INTERACTIVE_MODE\}\}(.*?)\{\{\/IF INTERACTIVE_MODE\}\}/gs;
4196
- if (variables.INTERACTIVE_MODE === true) {
4197
- result = result.replace(interactiveModeRegex, "$1");
4198
- } else {
4199
- result = result.replace(interactiveModeRegex, "");
4200
- }
4201
- const compactSummariesRegex = /\{\{#IF COMPACT_SUMMARIES\}\}(.*?)\{\{\/IF COMPACT_SUMMARIES\}\}/gs;
4202
- if (variables.COMPACT_SUMMARIES !== void 0 && variables.COMPACT_SUMMARIES !== "") {
4203
- result = result.replace(compactSummariesRegex, "$1");
4204
- } else {
4205
- result = result.replace(compactSummariesRegex, "");
4206
- }
4207
- const draftPrModeRegex = /\{\{#IF DRAFT_PR_MODE\}\}(.*?)\{\{\/IF DRAFT_PR_MODE\}\}/gs;
4208
- if (variables.DRAFT_PR_MODE === true) {
4209
- result = result.replace(draftPrModeRegex, "$1");
4210
- } else {
4211
- result = result.replace(draftPrModeRegex, "");
4212
- }
4213
- const standardIssueModeRegex = /\{\{#IF STANDARD_ISSUE_MODE\}\}(.*?)\{\{\/IF STANDARD_ISSUE_MODE\}\}/gs;
4214
- if (variables.STANDARD_ISSUE_MODE === true) {
4215
- result = result.replace(standardIssueModeRegex, "$1");
4216
- } else {
4217
- result = result.replace(standardIssueModeRegex, "");
4218
- }
4219
- const hasPackageJsonRegex = /\{\{#IF HAS_PACKAGE_JSON\}\}(.*?)\{\{\/IF HAS_PACKAGE_JSON\}\}/gs;
4220
- if (variables.HAS_PACKAGE_JSON === true) {
4221
- result = result.replace(hasPackageJsonRegex, "$1");
4222
- } else {
4223
- result = result.replace(hasPackageJsonRegex, "");
4224
- }
4225
- const noPackageJsonRegex = /\{\{#IF NO_PACKAGE_JSON\}\}(.*?)\{\{\/IF NO_PACKAGE_JSON\}\}/gs;
4226
- if (variables.NO_PACKAGE_JSON === true) {
4227
- result = result.replace(noPackageJsonRegex, "$1");
4228
- } else {
4229
- result = result.replace(noPackageJsonRegex, "");
4230
- }
4231
- return result;
4159
+ const compiled = Handlebars.compile(template, { noEscape: true });
4160
+ return compiled(variables);
4232
4161
  }
4233
4162
  /**
4234
4163
  * Get a fully processed prompt for a workflow type
@@ -4716,6 +4645,7 @@ export {
4716
4645
  ClaudeContextManager,
4717
4646
  DatabaseManager,
4718
4647
  EnvironmentManager,
4648
+ GitCommandError,
4719
4649
  GitHubService,
4720
4650
  GitWorktreeManager,
4721
4651
  IssueTrackerFactory,