@iloom/cli 0.5.5 → 0.6.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 (97) hide show
  1. package/README.md +100 -4
  2. package/dist/{ClaudeContextManager-7WX7DUNI.js → ClaudeContextManager-6J2EB4QU.js} +4 -4
  3. package/dist/{ClaudeService-LQLN2GP4.js → ClaudeService-O2PB22GX.js} +3 -3
  4. package/dist/{LoomLauncher-5QU42LOM.js → LoomLauncher-5LFM4LXB.js} +4 -4
  5. package/dist/{PRManager-OCSB2HPT.js → PRManager-GB3FOJ2W.js} +2 -2
  6. package/dist/ProjectCapabilityDetector-S5FLNCFI.js +11 -0
  7. package/dist/{PromptTemplateManager-5GNF7FCP.js → PromptTemplateManager-C3DK6XZL.js} +2 -2
  8. package/dist/README.md +100 -4
  9. package/dist/agents/iloom-framework-detector.md +366 -0
  10. package/dist/agents/iloom-issue-analyze-and-plan.md +0 -10
  11. package/dist/agents/iloom-issue-implementer.md +1 -1
  12. package/dist/agents/iloom-issue-planner.md +0 -10
  13. package/dist/build-FJVYP7EV.js +27 -0
  14. package/dist/build-FJVYP7EV.js.map +1 -0
  15. package/dist/{chunk-WIJWIKAN.js → chunk-2A7WQKBE.js} +10 -2
  16. package/dist/{chunk-WIJWIKAN.js.map → chunk-2A7WQKBE.js.map} +1 -1
  17. package/dist/{chunk-SHVB3EFE.js → chunk-64O2UIWO.js} +44 -19
  18. package/dist/chunk-64O2UIWO.js.map +1 -0
  19. package/dist/{chunk-FO2H3YXI.js → chunk-6U6VI4SZ.js} +2 -2
  20. package/dist/{chunk-VXMY22TP.js → chunk-7WANFUIK.js} +2 -2
  21. package/dist/{chunk-LVLRMP7V.js → chunk-AXX3QIKK.js} +58 -14
  22. package/dist/chunk-AXX3QIKK.js.map +1 -0
  23. package/dist/chunk-BXCPJJYM.js +133 -0
  24. package/dist/chunk-BXCPJJYM.js.map +1 -0
  25. package/dist/{chunk-46JVEGUW.js → chunk-I75JMBNB.js} +14 -16
  26. package/dist/chunk-I75JMBNB.js.map +1 -0
  27. package/dist/{chunk-SJIMVKK7.js → chunk-K7SEEHKO.js} +2 -2
  28. package/dist/{chunk-WZHBRKLN.js → chunk-LVBRMTE6.js} +39 -56
  29. package/dist/chunk-LVBRMTE6.js.map +1 -0
  30. package/dist/{chunk-NKRQNER7.js → chunk-Q7POFB5Q.js} +1 -55
  31. package/dist/chunk-Q7POFB5Q.js.map +1 -0
  32. package/dist/{chunk-N4ZJVATC.js → chunk-SN3Z6EZO.js} +11 -7
  33. package/dist/chunk-SN3Z6EZO.js.map +1 -0
  34. package/dist/chunk-TRQ76ISK.js +159 -0
  35. package/dist/chunk-TRQ76ISK.js.map +1 -0
  36. package/dist/{chunk-5IWU3HXE.js → chunk-VDA5JMB4.js} +3 -4
  37. package/dist/{chunk-5IWU3HXE.js.map → chunk-VDA5JMB4.js.map} +1 -1
  38. package/dist/{chunk-K5G5SFWY.js → chunk-W6WVRHJ6.js} +13 -1
  39. package/dist/chunk-W6WVRHJ6.js.map +1 -0
  40. package/dist/{chunk-EBISESAP.js → chunk-ZPSTA5PR.js} +16 -6
  41. package/dist/chunk-ZPSTA5PR.js.map +1 -0
  42. package/dist/{cleanup-WTZZ74VS.js → cleanup-BRUAINKE.js} +6 -6
  43. package/dist/cli.js +268 -65
  44. package/dist/cli.js.map +1 -1
  45. package/dist/compile-ULNO5F7Q.js +57 -0
  46. package/dist/compile-ULNO5F7Q.js.map +1 -0
  47. package/dist/{contribute-T7ENST5N.js → contribute-Q6GX6AXK.js} +95 -27
  48. package/dist/contribute-Q6GX6AXK.js.map +1 -0
  49. package/dist/{dev-server-S5QG5SBZ.js → dev-server-4RCDJ5MU.js} +5 -5
  50. package/dist/{feedback-PDMCKYOT.js → feedback-O4Q55SVS.js} +7 -7
  51. package/dist/{ignite-YF4Q5RA7.js → ignite-VHV65WEZ.js} +9 -5
  52. package/dist/ignite-VHV65WEZ.js.map +1 -0
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.js +12 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/{init-XQQGC6DN.js → init-UTYRHNJJ.js} +5 -4
  57. package/dist/lint-5JMCWE4Y.js +27 -0
  58. package/dist/lint-5JMCWE4Y.js.map +1 -0
  59. package/dist/{open-AIZG5756.js → open-WHVUYGPY.js} +5 -5
  60. package/dist/prompts/init-prompt.txt +101 -8
  61. package/dist/prompts/issue-prompt.txt +12 -12
  62. package/dist/{rebase-5EY3Q6XP.js → rebase-Y4AS6LQW.js} +2 -2
  63. package/dist/{run-CMZUYZVG.js → run-NCRK5NPR.js} +5 -5
  64. package/dist/{summary-5YXUGPRN.js → summary-CVFAMDOJ.js} +3 -3
  65. package/dist/test-3KIVXI6J.js +27 -0
  66. package/dist/test-3KIVXI6J.js.map +1 -0
  67. package/package.json +1 -1
  68. package/dist/ProjectCapabilityDetector-34LU7JJ4.js +0 -9
  69. package/dist/chunk-2ZPFJQ3B.js +0 -63
  70. package/dist/chunk-2ZPFJQ3B.js.map +0 -1
  71. package/dist/chunk-46JVEGUW.js.map +0 -1
  72. package/dist/chunk-EBISESAP.js.map +0 -1
  73. package/dist/chunk-K5G5SFWY.js.map +0 -1
  74. package/dist/chunk-LVLRMP7V.js.map +0 -1
  75. package/dist/chunk-N4ZJVATC.js.map +0 -1
  76. package/dist/chunk-NKRQNER7.js.map +0 -1
  77. package/dist/chunk-SHVB3EFE.js.map +0 -1
  78. package/dist/chunk-WZHBRKLN.js.map +0 -1
  79. package/dist/contribute-T7ENST5N.js.map +0 -1
  80. package/dist/ignite-YF4Q5RA7.js.map +0 -1
  81. /package/dist/{ClaudeContextManager-7WX7DUNI.js.map → ClaudeContextManager-6J2EB4QU.js.map} +0 -0
  82. /package/dist/{ClaudeService-LQLN2GP4.js.map → ClaudeService-O2PB22GX.js.map} +0 -0
  83. /package/dist/{LoomLauncher-5QU42LOM.js.map → LoomLauncher-5LFM4LXB.js.map} +0 -0
  84. /package/dist/{PRManager-OCSB2HPT.js.map → PRManager-GB3FOJ2W.js.map} +0 -0
  85. /package/dist/{ProjectCapabilityDetector-34LU7JJ4.js.map → ProjectCapabilityDetector-S5FLNCFI.js.map} +0 -0
  86. /package/dist/{PromptTemplateManager-5GNF7FCP.js.map → PromptTemplateManager-C3DK6XZL.js.map} +0 -0
  87. /package/dist/{chunk-FO2H3YXI.js.map → chunk-6U6VI4SZ.js.map} +0 -0
  88. /package/dist/{chunk-VXMY22TP.js.map → chunk-7WANFUIK.js.map} +0 -0
  89. /package/dist/{chunk-SJIMVKK7.js.map → chunk-K7SEEHKO.js.map} +0 -0
  90. /package/dist/{cleanup-WTZZ74VS.js.map → cleanup-BRUAINKE.js.map} +0 -0
  91. /package/dist/{dev-server-S5QG5SBZ.js.map → dev-server-4RCDJ5MU.js.map} +0 -0
  92. /package/dist/{feedback-PDMCKYOT.js.map → feedback-O4Q55SVS.js.map} +0 -0
  93. /package/dist/{init-XQQGC6DN.js.map → init-UTYRHNJJ.js.map} +0 -0
  94. /package/dist/{open-AIZG5756.js.map → open-WHVUYGPY.js.map} +0 -0
  95. /package/dist/{rebase-5EY3Q6XP.js.map → rebase-Y4AS6LQW.js.map} +0 -0
  96. /package/dist/{run-CMZUYZVG.js.map → run-NCRK5NPR.js.map} +0 -0
  97. /package/dist/{summary-5YXUGPRN.js.map → summary-CVFAMDOJ.js.map} +0 -0
package/dist/cli.js CHANGED
@@ -1,28 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionSummaryService
4
- } from "./chunk-SJIMVKK7.js";
4
+ } from "./chunk-K7SEEHKO.js";
5
5
  import "./chunk-NXMDEL3F.js";
6
- import {
7
- IssueTrackerFactory,
8
- generateIssueManagementMcpConfig
9
- } from "./chunk-6YSFTPKW.js";
10
- import "./chunk-7Q66W4OH.js";
11
6
  import {
12
7
  CLIIsolationManager,
13
8
  DatabaseManager,
14
9
  EnvironmentManager,
15
10
  LoomManager,
16
11
  ResourceCleanup
17
- } from "./chunk-46JVEGUW.js";
12
+ } from "./chunk-I75JMBNB.js";
18
13
  import {
19
- detectPackageManager,
20
- installDependencies,
21
- runScript
22
- } from "./chunk-LVLRMP7V.js";
14
+ IssueTrackerFactory,
15
+ generateIssueManagementMcpConfig
16
+ } from "./chunk-6YSFTPKW.js";
17
+ import "./chunk-7Q66W4OH.js";
23
18
  import {
24
19
  ProcessManager
25
20
  } from "./chunk-VU3QMIP2.js";
21
+ import {
22
+ detectPackageManager,
23
+ installDependencies,
24
+ runScript
25
+ } from "./chunk-AXX3QIKK.js";
26
26
  import {
27
27
  IdentifierParser
28
28
  } from "./chunk-UQIXZ3BA.js";
@@ -32,34 +32,34 @@ import {
32
32
  import {
33
33
  InitCommand,
34
34
  ShellCompletion
35
- } from "./chunk-WZHBRKLN.js";
35
+ } from "./chunk-LVBRMTE6.js";
36
36
  import {
37
37
  FirstRunManager
38
- } from "./chunk-NKRQNER7.js";
38
+ } from "./chunk-Q7POFB5Q.js";
39
39
  import "./chunk-F2PWIRV4.js";
40
40
  import {
41
41
  IssueEnhancementService,
42
42
  capitalizeFirstLetter
43
43
  } from "./chunk-7HIRPCKU.js";
44
- import {
45
- AgentManager
46
- } from "./chunk-N4ZJVATC.js";
47
44
  import {
48
45
  ProjectCapabilityDetector
49
- } from "./chunk-EBISESAP.js";
46
+ } from "./chunk-ZPSTA5PR.js";
50
47
  import {
51
- hasScript,
52
- readPackageJson
53
- } from "./chunk-2ZPFJQ3B.js";
48
+ getPackageConfig,
49
+ hasScript
50
+ } from "./chunk-BXCPJJYM.js";
51
+ import {
52
+ AgentManager
53
+ } from "./chunk-SN3Z6EZO.js";
54
54
  import {
55
55
  MergeManager
56
- } from "./chunk-WIJWIKAN.js";
56
+ } from "./chunk-2A7WQKBE.js";
57
57
  import {
58
58
  GitWorktreeManager
59
59
  } from "./chunk-EK3XCAAS.js";
60
60
  import {
61
61
  PRManager
62
- } from "./chunk-5IWU3HXE.js";
62
+ } from "./chunk-VDA5JMB4.js";
63
63
  import {
64
64
  openBrowser
65
65
  } from "./chunk-YETJNRQM.js";
@@ -74,9 +74,9 @@ import {
74
74
  } from "./chunk-O7VL5N6S.js";
75
75
  import {
76
76
  ClaudeContextManager
77
- } from "./chunk-VXMY22TP.js";
78
- import "./chunk-FO2H3YXI.js";
79
- import "./chunk-K5G5SFWY.js";
77
+ } from "./chunk-7WANFUIK.js";
78
+ import "./chunk-6U6VI4SZ.js";
79
+ import "./chunk-W6WVRHJ6.js";
80
80
  import {
81
81
  extractSettingsOverrides
82
82
  } from "./chunk-GYCR2LOU.js";
@@ -148,7 +148,7 @@ async function getProjectRoot() {
148
148
  async function needsFirstRunSetup() {
149
149
  const projectRoot = await getProjectRoot();
150
150
  const firstRunManager = new FirstRunManager();
151
- const { isConfigured } = await firstRunManager.fixupLegacyProject(projectRoot);
151
+ const isConfigured = await firstRunManager.isProjectConfigured(projectRoot);
152
152
  if (isConfigured) {
153
153
  logger.debug("needsFirstRunSetup: Project is tracked as configured globally");
154
154
  return false;
@@ -867,7 +867,7 @@ var ValidationRunner = class {
867
867
  const stepStartTime = Date.now();
868
868
  let scriptToRun = null;
869
869
  try {
870
- const pkgJson = await readPackageJson(worktreePath);
870
+ const pkgJson = await getPackageConfig(worktreePath);
871
871
  const hasCompileScript = hasScript(pkgJson, "compile");
872
872
  const hasTypecheckScript = hasScript(pkgJson, "typecheck");
873
873
  if (hasCompileScript) {
@@ -947,7 +947,7 @@ Run '${runCommand}' to see detailed errors.`
947
947
  async runLint(worktreePath, dryRun) {
948
948
  const stepStartTime = Date.now();
949
949
  try {
950
- const pkgJson = await readPackageJson(worktreePath);
950
+ const pkgJson = await getPackageConfig(worktreePath);
951
951
  const hasLintScript = hasScript(pkgJson, "lint");
952
952
  if (!hasLintScript) {
953
953
  getLogger().debug("Skipping lint - no lint script found");
@@ -1020,7 +1020,7 @@ Run '${runCommand}' to see detailed errors.`
1020
1020
  async runTests(worktreePath, dryRun) {
1021
1021
  const stepStartTime = Date.now();
1022
1022
  try {
1023
- const pkgJson = await readPackageJson(worktreePath);
1023
+ const pkgJson = await getPackageConfig(worktreePath);
1024
1024
  const hasTestScript = hasScript(pkgJson, "test");
1025
1025
  if (!hasTestScript) {
1026
1026
  getLogger().debug("Skipping tests - no test script found");
@@ -1134,12 +1134,10 @@ Run '${runCommand}' to see detailed errors.`
1134
1134
  }
1135
1135
  /**
1136
1136
  * Get validation command string for prompts
1137
+ * Uses il commands for multi-language project support
1137
1138
  */
1138
- getValidationCommand(validationType, packageManager) {
1139
- if (packageManager === "npm") {
1140
- return `npm run ${validationType}`;
1141
- }
1142
- return `${packageManager} ${validationType}`;
1139
+ getValidationCommand(validationType, _packageManager) {
1140
+ return `il ${validationType}`;
1143
1141
  }
1144
1142
  /**
1145
1143
  * Get Claude prompt for specific validation type
@@ -1606,24 +1604,24 @@ var BuildRunner = class {
1606
1604
  async runBuild(buildPath, options = {}) {
1607
1605
  const startTime = Date.now();
1608
1606
  try {
1609
- const pkgJson = await readPackageJson(buildPath);
1607
+ const pkgJson = await getPackageConfig(buildPath);
1610
1608
  const hasBuildScript = hasScript(pkgJson, "build");
1611
1609
  if (!hasBuildScript) {
1612
1610
  getLogger().debug("Skipping build - no build script found");
1613
1611
  return {
1614
1612
  success: true,
1615
1613
  skipped: true,
1616
- reason: "No build script found in package.json",
1614
+ reason: "No build script found in package configuration",
1617
1615
  duration: Date.now() - startTime
1618
1616
  };
1619
1617
  }
1620
1618
  } catch (error) {
1621
1619
  if (error instanceof Error && error.message.includes("package.json not found")) {
1622
- getLogger().debug("Skipping build - no package.json found (non-Node.js project)");
1620
+ getLogger().debug("Skipping build - no package configuration found");
1623
1621
  return {
1624
1622
  success: true,
1625
1623
  skipped: true,
1626
- reason: "No package.json found in project",
1624
+ reason: "No package configuration found in project",
1627
1625
  duration: Date.now() - startTime
1628
1626
  };
1629
1627
  }
@@ -1769,7 +1767,7 @@ var FinishCommand = class {
1769
1767
  * Main entry point for finish command
1770
1768
  */
1771
1769
  async execute(input) {
1772
- var _a, _b;
1770
+ var _a, _b, _c, _d;
1773
1771
  const isJsonMode = input.options.json === true;
1774
1772
  const result = {
1775
1773
  success: false,
@@ -1780,13 +1778,13 @@ var FinishCommand = class {
1780
1778
  };
1781
1779
  if (isJsonMode) {
1782
1780
  const settings2 = await this.settingsManager.loadSettings();
1783
- if (((_a = settings2.mergeBehavior) == null ? void 0 : _a.mode) === "github-pr" && input.options.cleanup === void 0) {
1784
- throw new Error("JSON mode with github-pr workflow requires --cleanup or --no-cleanup flag. Use: il finish --json --cleanup <identifier>");
1781
+ if ((((_a = settings2.mergeBehavior) == null ? void 0 : _a.mode) === "github-pr" || ((_b = settings2.mergeBehavior) == null ? void 0 : _b.mode) === "github-draft-pr") && input.options.cleanup === void 0) {
1782
+ throw new Error('JSON mode with "github-pr"/"github-draft-pr" workflow requires --cleanup or --no-cleanup flag. Use: il finish --json --cleanup <identifier>');
1785
1783
  }
1786
1784
  }
1787
1785
  const settings = await this.settingsManager.loadSettings();
1788
1786
  let repo;
1789
- const needsRepo = ((_b = settings.mergeBehavior) == null ? void 0 : _b.mode) === "github-pr" || this.issueTracker.providerName === "github";
1787
+ const needsRepo = ((_c = settings.mergeBehavior) == null ? void 0 : _c.mode) === "github-pr" || ((_d = settings.mergeBehavior) == null ? void 0 : _d.mode) === "github-draft-pr" || this.issueTracker.providerName === "github";
1790
1788
  if (needsRepo && await hasMultipleRemotes()) {
1791
1789
  repo = await getConfiguredRepoFromSettings(settings);
1792
1790
  getLogger().info(`Using GitHub repository: ${repo}`);
@@ -2780,12 +2778,12 @@ function determineLoomType(worktree) {
2780
2778
  }
2781
2779
  return "branch";
2782
2780
  }
2783
- function extractPRNumbers(path4) {
2784
- if (!path4) {
2781
+ function extractPRNumbers(path6) {
2782
+ if (!path6) {
2785
2783
  return [];
2786
2784
  }
2787
2785
  const prPathPattern = /_pr_(\d+)$/;
2788
- const match = path4.match(prPathPattern);
2786
+ const match = path6.match(prPathPattern);
2789
2787
  if (match == null ? void 0 : match[1]) {
2790
2788
  return [match[1]];
2791
2789
  }
@@ -2839,6 +2837,165 @@ function formatLoomsForJson(worktrees, mainWorktreePath, metadata) {
2839
2837
  return worktrees.map((wt) => formatLoomForJson(wt, mainWorktreePath, metadata == null ? void 0 : metadata.get(wt.path)));
2840
2838
  }
2841
2839
 
2840
+ // src/lib/VersionMigrationManager.ts
2841
+ import fs2 from "fs-extra";
2842
+ import path5 from "path";
2843
+ import os2 from "os";
2844
+
2845
+ // src/migrations/index.ts
2846
+ import fs from "fs-extra";
2847
+ import path4 from "path";
2848
+ import os from "os";
2849
+ var migrations = [
2850
+ // v0.6.0 is the baseline - no migrations needed
2851
+ {
2852
+ version: "0.6.1",
2853
+ description: "Add global gitignore for .iloom/settings.local.json",
2854
+ migrate: async () => {
2855
+ const globalIgnorePath = path4.join(os.homedir(), ".config", "git", "ignore");
2856
+ const pattern = "**/.iloom/settings.local.json";
2857
+ await fs.ensureDir(path4.dirname(globalIgnorePath));
2858
+ let content = "";
2859
+ try {
2860
+ content = await fs.readFile(globalIgnorePath, "utf-8");
2861
+ } catch {
2862
+ }
2863
+ if (content.includes(pattern)) {
2864
+ return;
2865
+ }
2866
+ const separator = content.endsWith("\n") || content === "" ? "" : "\n";
2867
+ const newContent = content + separator + "\n# Added by iloom CLI\n" + pattern + "\n";
2868
+ await fs.writeFile(globalIgnorePath, newContent, "utf-8");
2869
+ }
2870
+ }
2871
+ ];
2872
+
2873
+ // src/lib/VersionMigrationManager.ts
2874
+ var VersionMigrationManager = class {
2875
+ constructor() {
2876
+ this.DEFAULT_VERSION = "0.6.0";
2877
+ this.VERSION_OVERRIDE_ENV = "ILOOM_VERSION_OVERRIDE";
2878
+ }
2879
+ // Return path to migration state file
2880
+ getMigrationStatePath() {
2881
+ return path5.join(os2.homedir(), ".config", "iloom-ai", "migration-state.json");
2882
+ }
2883
+ // Get effective version, respecting ILOOM_VERSION_OVERRIDE env var
2884
+ // packageVersion is the version from package.json passed by caller
2885
+ getEffectiveVersion(packageVersion) {
2886
+ const override = process.env[this.VERSION_OVERRIDE_ENV];
2887
+ if (override && override.trim() !== "") {
2888
+ logger.debug(`[VersionMigrationManager] Using version override: ${override} (package.json: ${packageVersion})`);
2889
+ return override.trim();
2890
+ }
2891
+ return packageVersion;
2892
+ }
2893
+ // Load full migration state from file
2894
+ // Returns state with DEFAULT_VERSION if file missing/invalid
2895
+ async loadFullMigrationState() {
2896
+ const statePath = this.getMigrationStatePath();
2897
+ try {
2898
+ const content = await fs2.readFile(statePath, "utf-8");
2899
+ const state = JSON.parse(content);
2900
+ if (typeof state.lastMigratedVersion === "string") {
2901
+ return state;
2902
+ }
2903
+ } catch {
2904
+ logger.debug(`[VersionMigrationManager] Migration state not found, using default version: ${this.DEFAULT_VERSION}`);
2905
+ }
2906
+ return {
2907
+ lastMigratedVersion: this.DEFAULT_VERSION,
2908
+ migratedAt: (/* @__PURE__ */ new Date()).toISOString()
2909
+ };
2910
+ }
2911
+ // Load last migrated version from state file
2912
+ // Returns DEFAULT_VERSION if file missing/invalid
2913
+ async loadMigrationState() {
2914
+ const state = await this.loadFullMigrationState();
2915
+ return state.lastMigratedVersion;
2916
+ }
2917
+ // Save current version to state file, preserving and appending to existing failures
2918
+ async saveMigrationState(version, newFailures = []) {
2919
+ const statePath = this.getMigrationStatePath();
2920
+ try {
2921
+ await fs2.ensureDir(path5.dirname(statePath));
2922
+ const existingState = await this.loadFullMigrationState();
2923
+ const existingFailures = existingState.failedMigrations ?? [];
2924
+ const allFailures = [...existingFailures];
2925
+ for (const newFailure of newFailures) {
2926
+ const existingIndex = allFailures.findIndex((f) => f.version === newFailure.version);
2927
+ if (existingIndex >= 0) {
2928
+ allFailures[existingIndex] = newFailure;
2929
+ } else {
2930
+ allFailures.push(newFailure);
2931
+ }
2932
+ }
2933
+ const state = {
2934
+ lastMigratedVersion: version,
2935
+ migratedAt: (/* @__PURE__ */ new Date()).toISOString(),
2936
+ ...allFailures.length > 0 && { failedMigrations: allFailures }
2937
+ };
2938
+ await fs2.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
2939
+ } catch (error) {
2940
+ logger.warn(`[VersionMigrationManager] Failed to save migration state: ${error}`);
2941
+ }
2942
+ }
2943
+ // Compare semver versions: returns negative if v1 < v2, positive if v1 > v2, 0 if equal
2944
+ // Pattern from update-notifier.ts:190-221
2945
+ compareVersions(v1, v2) {
2946
+ try {
2947
+ const parts1 = v1.split(".").map((p) => parseInt(p, 10));
2948
+ const parts2 = v2.split(".").map((p) => parseInt(p, 10));
2949
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
2950
+ const p1 = parts1[i] ?? 0;
2951
+ const p2 = parts2[i] ?? 0;
2952
+ if (p1 !== p2) return p1 - p2;
2953
+ }
2954
+ return 0;
2955
+ } catch {
2956
+ return 0;
2957
+ }
2958
+ }
2959
+ // Get migrations that need to run (version > lastMigrated, version <= current)
2960
+ getPendingMigrations(lastMigratedVersion, currentVersion) {
2961
+ return migrations.filter(
2962
+ (m) => this.compareVersions(m.version, lastMigratedVersion) > 0 && this.compareVersions(m.version, currentVersion) <= 0
2963
+ ).sort((a, b) => this.compareVersions(a.version, b.version));
2964
+ }
2965
+ // Main entry point - run any pending migrations
2966
+ // packageVersion is the version from package.json; may be overridden by ILOOM_VERSION_OVERRIDE
2967
+ async runMigrationsIfNeeded(packageVersion) {
2968
+ const currentVersion = this.getEffectiveVersion(packageVersion);
2969
+ const lastMigratedVersion = await this.loadMigrationState();
2970
+ if (this.compareVersions(lastMigratedVersion, currentVersion) >= 0) {
2971
+ return;
2972
+ }
2973
+ const pending = this.getPendingMigrations(lastMigratedVersion, currentVersion);
2974
+ if (pending.length === 0) {
2975
+ logger.debug(`[VersionMigrationManager] No migrations to run, updating state to ${currentVersion}`);
2976
+ await this.saveMigrationState(currentVersion);
2977
+ return;
2978
+ }
2979
+ const failedMigrations = [];
2980
+ for (const migration of pending) {
2981
+ logger.debug(`[VersionMigrationManager] Running migration to ${migration.version}: ${migration.description}`);
2982
+ try {
2983
+ await migration.migrate();
2984
+ logger.debug(`[VersionMigrationManager] Migration to ${migration.version} completed`);
2985
+ } catch (error) {
2986
+ logger.warn(`[VersionMigrationManager] Migration to ${migration.version} failed: ${error}`);
2987
+ failedMigrations.push({
2988
+ version: migration.version,
2989
+ description: migration.description,
2990
+ failedAt: (/* @__PURE__ */ new Date()).toISOString(),
2991
+ error: error instanceof Error ? error.message : String(error)
2992
+ });
2993
+ }
2994
+ }
2995
+ await this.saveMigrationState(currentVersion, failedMigrations);
2996
+ }
2997
+ };
2998
+
2842
2999
  // src/cli.ts
2843
3000
  var __filename = fileURLToPath2(import.meta.url);
2844
3001
  var packageJson = getPackageInfo(__filename);
@@ -2870,6 +3027,12 @@ program.name("iloom").description(packageJson.description).version(packageJson.v
2870
3027
  } catch (error) {
2871
3028
  logger.debug(`Settings migration failed: ${error instanceof Error ? error.message : "Unknown"}`);
2872
3029
  }
3030
+ try {
3031
+ const versionMigrationManager = new VersionMigrationManager();
3032
+ await versionMigrationManager.runMigrationsIfNeeded(packageJson.version);
3033
+ } catch (error) {
3034
+ logger.warn(`Version migration failed: ${error instanceof Error ? error.message : "Unknown"}`);
3035
+ }
2873
3036
  await validateSettingsForCommand(actionCommand);
2874
3037
  await validateGhCliForCommand(actionCommand);
2875
3038
  await validateIdeForStartCommand(thisCommand);
@@ -2917,14 +3080,14 @@ async function validateGhCliForCommand(command) {
2917
3080
  const settings = await settingsManager.loadSettings();
2918
3081
  const provider = IssueTrackerFactory.getProviderName(settings);
2919
3082
  const mergeBehaviorMode = (_a = settings.mergeBehavior) == null ? void 0 : _a.mode;
2920
- needsGhCli = provider === "github" || mergeBehaviorMode === "github-pr";
3083
+ needsGhCli = provider === "github" || mergeBehaviorMode === "github-pr" || mergeBehaviorMode === "github-draft-pr";
2921
3084
  } catch {
2922
3085
  needsGhCli = true;
2923
3086
  }
2924
3087
  }
2925
3088
  if (!ghAvailable) {
2926
3089
  if (needsGhCli) {
2927
- const errorMessage = alwaysRequireGh.includes(commandName) ? `The "${commandName}" command requires GitHub CLI (gh) to be installed.` : `GitHub CLI (gh) is required when using GitHub as the issue tracker or "github-pr" merge mode.`;
3090
+ const errorMessage = alwaysRequireGh.includes(commandName) ? `The "${commandName}" command requires GitHub CLI (gh) to be installed.` : `GitHub CLI (gh) is required when using GitHub as the issue tracker or "github-pr"/"github-draft-pr" merge mode.`;
2928
3091
  logger.error(errorMessage);
2929
3092
  logger.info("");
2930
3093
  logger.info("To install GitHub CLI:");
@@ -2940,10 +3103,10 @@ async function validateGhCliForCommand(command) {
2940
3103
  const settings = await settingsManager.loadSettings();
2941
3104
  const provider = IssueTrackerFactory.getProviderName(settings);
2942
3105
  const mergeBehaviorMode = (_b = settings.mergeBehavior) == null ? void 0 : _b.mode;
2943
- if (provider === "github" || mergeBehaviorMode === "github-pr") {
3106
+ if (provider === "github" || mergeBehaviorMode === "github-pr" || mergeBehaviorMode === "github-draft-pr") {
2944
3107
  logger.warn("GitHub CLI (gh) is not installed.");
2945
3108
  logger.warn(
2946
- "Some features may not work correctly with your current configuration (GitHub provider or github-pr merge mode)."
3109
+ 'Some features may not work correctly with your current configuration (GitHub provider or "github-pr"/"github-draft-pr" merge mode).'
2947
3110
  );
2948
3111
  logger.info("To install: brew install gh (macOS) or see https://github.com/cli/cli#installation");
2949
3112
  logger.info("");
@@ -2999,7 +3162,7 @@ async function autoLaunchInitForMultipleRemotes() {
2999
3162
  await waitForKeypress2("Press any key to start configuration...");
3000
3163
  logger.info("");
3001
3164
  try {
3002
- const { InitCommand: InitCommand2 } = await import("./init-XQQGC6DN.js");
3165
+ const { InitCommand: InitCommand2 } = await import("./init-UTYRHNJJ.js");
3003
3166
  const initCommand = new InitCommand2();
3004
3167
  const customInitialMessage = "Help me configure which git remote iloom should use for GitHub operations. I have multiple remotes and need to select the correct one.";
3005
3168
  await initCommand.execute(customInitialMessage);
@@ -3100,7 +3263,7 @@ program.command("add-issue").alias("a").description("Create and enhance GitHub i
3100
3263
  });
3101
3264
  program.command("feedback").alias("f").description("Submit feedback/bug report to iloom-cli repository").argument("<description>", "Feedback title (>30 chars, >2 spaces; or any non-empty text when --body provided)").option("--body <text>", "Body text for feedback (added after diagnostics)").action(async (description, options) => {
3102
3265
  try {
3103
- const { FeedbackCommand } = await import("./feedback-PDMCKYOT.js");
3266
+ const { FeedbackCommand } = await import("./feedback-O4Q55SVS.js");
3104
3267
  const command = new FeedbackCommand();
3105
3268
  const feedbackOptions = {};
3106
3269
  if (options.body !== void 0) {
@@ -3183,7 +3346,7 @@ program.command("finish").alias("dn").description("Merge work and cleanup worksp
3183
3346
  });
3184
3347
  program.command("rebase").description("Rebase current branch on main with Claude-assisted conflict resolution").option("-f, --force", "Skip confirmation prompts").option("-n, --dry-run", "Preview actions without executing").action(async (options) => {
3185
3348
  try {
3186
- const { RebaseCommand } = await import("./rebase-5EY3Q6XP.js");
3349
+ const { RebaseCommand } = await import("./rebase-Y4AS6LQW.js");
3187
3350
  const command = new RebaseCommand();
3188
3351
  await command.execute(options);
3189
3352
  } catch (error) {
@@ -3195,7 +3358,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3195
3358
  new Option("--one-shot <mode>", "One-shot automation mode").choices(["default", "noReview", "bypassPermissions"]).default("default")
3196
3359
  ).action(async (options) => {
3197
3360
  try {
3198
- const { IgniteCommand } = await import("./ignite-YF4Q5RA7.js");
3361
+ const { IgniteCommand } = await import("./ignite-VHV65WEZ.js");
3199
3362
  const command = new IgniteCommand();
3200
3363
  await command.execute(options.oneShot ?? "default");
3201
3364
  } catch (error) {
@@ -3206,7 +3369,7 @@ program.command("spin").alias("ignite").description("Launch Claude with auto-det
3206
3369
  program.command("open").description("Open workspace in browser or run CLI tool").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3207
3370
  try {
3208
3371
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3209
- const { OpenCommand } = await import("./open-AIZG5756.js");
3372
+ const { OpenCommand } = await import("./open-WHVUYGPY.js");
3210
3373
  const cmd = new OpenCommand();
3211
3374
  const input = identifier ? { identifier, args } : { args };
3212
3375
  await cmd.execute(input);
@@ -3218,7 +3381,7 @@ program.command("open").description("Open workspace in browser or run CLI tool")
3218
3381
  program.command("run").description("Run CLI tool or open workspace in browser").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").allowUnknownOption().action(async (identifier, _options, command) => {
3219
3382
  try {
3220
3383
  const args = (command == null ? void 0 : command.args) ? command.args.slice(identifier ? 1 : 0) : [];
3221
- const { RunCommand } = await import("./run-CMZUYZVG.js");
3384
+ const { RunCommand } = await import("./run-NCRK5NPR.js");
3222
3385
  const cmd = new RunCommand();
3223
3386
  const input = identifier ? { identifier, args } : { args };
3224
3387
  await cmd.execute(input);
@@ -3229,7 +3392,7 @@ program.command("run").description("Run CLI tool or open workspace in browser").
3229
3392
  });
3230
3393
  program.command("dev-server").alias("dev").description("Start dev server for workspace (foreground)").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").option("--json", "Output as JSON").action(async (identifier, options) => {
3231
3394
  try {
3232
- const { DevServerCommand } = await import("./dev-server-S5QG5SBZ.js");
3395
+ const { DevServerCommand } = await import("./dev-server-4RCDJ5MU.js");
3233
3396
  const cmd = new DevServerCommand();
3234
3397
  await cmd.execute({ identifier, json: options == null ? void 0 : options.json });
3235
3398
  } catch (error) {
@@ -3247,10 +3410,50 @@ program.command("shell").alias("terminal").description("Open interactive shell w
3247
3410
  process.exit(1);
3248
3411
  }
3249
3412
  });
3413
+ program.command("build").description("Run the build script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3414
+ try {
3415
+ const { BuildCommand } = await import("./build-FJVYP7EV.js");
3416
+ const cmd = new BuildCommand();
3417
+ await cmd.execute(identifier ? { identifier } : {});
3418
+ } catch (error) {
3419
+ logger.error(`Build failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3420
+ process.exit(1);
3421
+ }
3422
+ });
3423
+ program.command("lint").description("Run the lint script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3424
+ try {
3425
+ const { LintCommand } = await import("./lint-5JMCWE4Y.js");
3426
+ const cmd = new LintCommand();
3427
+ await cmd.execute(identifier ? { identifier } : {});
3428
+ } catch (error) {
3429
+ logger.error(`Lint failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3430
+ process.exit(1);
3431
+ }
3432
+ });
3433
+ program.command("test").description("Run the test script").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3434
+ try {
3435
+ const { TestCommand } = await import("./test-3KIVXI6J.js");
3436
+ const cmd = new TestCommand();
3437
+ await cmd.execute(identifier ? { identifier } : {});
3438
+ } catch (error) {
3439
+ logger.error(`Test failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3440
+ process.exit(1);
3441
+ }
3442
+ });
3443
+ program.command("compile").alias("typecheck").description("Run the compile or typecheck script (prefers compile if both exist)").argument("[identifier]", "Issue number, PR number, or branch name (auto-detected if omitted)").action(async (identifier) => {
3444
+ try {
3445
+ const { CompileCommand } = await import("./compile-ULNO5F7Q.js");
3446
+ const cmd = new CompileCommand();
3447
+ await cmd.execute(identifier ? { identifier } : {});
3448
+ } catch (error) {
3449
+ logger.error(`Compile failed: ${error instanceof Error ? error.message : "Unknown error"}`);
3450
+ process.exit(1);
3451
+ }
3452
+ });
3250
3453
  program.command("cleanup").alias("remove").alias("clean").description("Remove workspaces").argument("[identifier]", "Branch name or issue number to cleanup (auto-detected)").option("-l, --list", "List all worktrees").option("-a, --all", "Remove all worktrees (interactive confirmation)").option("-i, --issue <number>", "Cleanup by issue number", parseInt).option("-f, --force", "Skip confirmations and force removal").option("--dry-run", "Show what would be done without doing it").option("--json", "Output result as JSON").action(async (identifier, options) => {
3251
3454
  const executeAction = async () => {
3252
3455
  try {
3253
- const { CleanupCommand } = await import("./cleanup-WTZZ74VS.js");
3456
+ const { CleanupCommand } = await import("./cleanup-BRUAINKE.js");
3254
3457
  const command = new CleanupCommand();
3255
3458
  const input = {
3256
3459
  options: options ?? {}
@@ -3339,7 +3542,7 @@ program.command("projects").description("List configured iloom projects").option
3339
3542
  });
3340
3543
  program.command("init").alias("config").description("Initialize iloom configuration").argument("[prompt]", 'Custom initial message to send to Claude (defaults to "Help me configure iloom settings.")').action(async (prompt) => {
3341
3544
  try {
3342
- const { InitCommand: InitCommand2 } = await import("./init-XQQGC6DN.js");
3545
+ const { InitCommand: InitCommand2 } = await import("./init-UTYRHNJJ.js");
3343
3546
  const command = new InitCommand2();
3344
3547
  const trimmedPrompt = prompt == null ? void 0 : prompt.trim();
3345
3548
  const customPrompt = trimmedPrompt && trimmedPrompt.length > 0 ? trimmedPrompt : void 0;
@@ -3349,11 +3552,11 @@ program.command("init").alias("config").description("Initialize iloom configurat
3349
3552
  process.exit(1);
3350
3553
  }
3351
3554
  });
3352
- program.command("contribute").description("Set up local development environment for contributing to iloom").action(async () => {
3555
+ program.command("contribute").description("Set up local development environment for contributing to a GitHub project").argument("[repository]", "GitHub repository (owner/repo, github.com/owner/repo, or full URL). Defaults to iloom-ai/iloom-cli").action(async (repository) => {
3353
3556
  try {
3354
- const { ContributeCommand } = await import("./contribute-T7ENST5N.js");
3557
+ const { ContributeCommand } = await import("./contribute-Q6GX6AXK.js");
3355
3558
  const command = new ContributeCommand();
3356
- await command.execute();
3559
+ await command.execute(repository);
3357
3560
  } catch (error) {
3358
3561
  logger.error(`Failed to set up contributor environment: ${error instanceof Error ? error.message : "Unknown error"}`);
3359
3562
  process.exit(1);
@@ -3431,9 +3634,9 @@ program.command("test-github").description("Test GitHub integration (Issue #3)")
3431
3634
  program.command("test-claude").description("Test Claude integration (Issue #10)").option("--detect", "Test Claude CLI detection").option("--version", "Get Claude CLI version").option("--branch <title>", "Test branch name generation with given title").option("--issue <number>", "Issue number for branch generation", "123").option("--launch <prompt>", "Launch Claude with a prompt (headless)").option("--interactive", "Launch Claude interactively (requires --launch)").option("--template <name>", "Test template loading").action(async (options) => {
3432
3635
  try {
3433
3636
  const { detectClaudeCli: detectClaudeCli2, getClaudeVersion, generateBranchName, launchClaude: launchClaude2 } = await import("./claude-H33OQMXO.js");
3434
- const { PromptTemplateManager } = await import("./PromptTemplateManager-5GNF7FCP.js");
3435
- const { ClaudeService } = await import("./ClaudeService-LQLN2GP4.js");
3436
- const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-7WX7DUNI.js");
3637
+ const { PromptTemplateManager } = await import("./PromptTemplateManager-C3DK6XZL.js");
3638
+ const { ClaudeService } = await import("./ClaudeService-O2PB22GX.js");
3639
+ const { ClaudeContextManager: ClaudeContextManager2 } = await import("./ClaudeContextManager-6J2EB4QU.js");
3437
3640
  logger.info("Testing Claude Integration\n");
3438
3641
  if (options.detect) {
3439
3642
  logger.info("Detecting Claude CLI...");
@@ -3621,7 +3824,7 @@ program.command("test-prefix").description("Test worktree prefix configuration -
3621
3824
  program.command("summary").description("Generate Claude session summary for a loom").argument("[identifier]", "Issue number, PR number (pr/123), or branch name (auto-detected if omitted)").option("--with-comment", "Post summary as a comment to the issue/PR").option("--json", "Output result as JSON").action(async (identifier, options) => {
3622
3825
  const executeAction = async () => {
3623
3826
  try {
3624
- const { SummaryCommand } = await import("./summary-5YXUGPRN.js");
3827
+ const { SummaryCommand } = await import("./summary-CVFAMDOJ.js");
3625
3828
  const command = new SummaryCommand();
3626
3829
  const result = await command.execute({ identifier, options });
3627
3830
  if (options.json && result) {