@iloom/cli 0.5.2 → 0.5.4

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 (58) hide show
  1. package/README.md +2 -0
  2. package/dist/README.md +2 -0
  3. package/dist/{chunk-66BMJ25W.js → chunk-6YSFTPKW.js} +8 -192
  4. package/dist/chunk-6YSFTPKW.js.map +1 -0
  5. package/dist/{chunk-XNNXAAZT.js → chunk-E4F7KASE.js} +2 -2
  6. package/dist/{chunk-HMMO2LDS.js → chunk-ESP2FF52.js} +2 -2
  7. package/dist/chunk-NKRQNER7.js +197 -0
  8. package/dist/chunk-NKRQNER7.js.map +1 -0
  9. package/dist/{chunk-Z5BM4JWB.js → chunk-NRDY6XO3.js} +4 -4
  10. package/dist/{chunk-53OMUNUN.js → chunk-TKL7RBEF.js} +7 -2
  11. package/dist/chunk-TKL7RBEF.js.map +1 -0
  12. package/dist/{chunk-YU5HVI6B.js → chunk-YPOG7WY4.js} +2 -2
  13. package/dist/{chunk-VV66DH6T.js → chunk-ZXWTOJXA.js} +26 -7
  14. package/dist/chunk-ZXWTOJXA.js.map +1 -0
  15. package/dist/{cleanup-Y5W3CNUV.js → cleanup-H5QUWBWE.js} +6 -6
  16. package/dist/cli.js +30 -30
  17. package/dist/cli.js.map +1 -1
  18. package/dist/{contribute-K7UXBOML.js → contribute-VP73TPAL.js} +3 -3
  19. package/dist/{contribute-K7UXBOML.js.map → contribute-VP73TPAL.js.map} +1 -1
  20. package/dist/{dev-server-HNBRWGCD.js → dev-server-H5FFXIVX.js} +4 -4
  21. package/dist/{git-OV6ADVO7.js → git-UHUNQZBA.js} +2 -2
  22. package/dist/{ignite-3HB3ZBEW.js → ignite-VPP4PMF4.js} +7 -5
  23. package/dist/{ignite-3HB3ZBEW.js.map → ignite-VPP4PMF4.js.map} +1 -1
  24. package/dist/index.js +6 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/{init-CMIRHFSR.js → init-4ZR2XXZA.js} +5 -4
  27. package/dist/{open-AXE225Z5.js → open-6HBQHPUL.js} +4 -4
  28. package/dist/{projects-GVEMCN5R.js → projects-SA76I4TZ.js} +14 -7
  29. package/dist/projects-SA76I4TZ.js.map +1 -0
  30. package/dist/prompts/issue-prompt.txt +12 -6
  31. package/dist/{rebase-6UIHMUWS.js → rebase-SRBOVC4M.js} +4 -4
  32. package/dist/{recap-XTBNMEMO.js → recap-X7FTTKPP.js} +4 -4
  33. package/dist/{run-H375EYRB.js → run-5FU76FFE.js} +4 -4
  34. package/dist/{shell-33FJCWJQ.js → shell-UQJDI36V.js} +4 -4
  35. package/dist/{summary-JUMOCNLR.js → summary-MOKN7RM2.js} +3 -3
  36. package/dist/{test-git-CO3BA4BV.js → test-git-GB3B6QNT.js} +2 -2
  37. package/dist/{test-prefix-HZYSDQYT.js → test-prefix-YQNNTCY3.js} +2 -2
  38. package/package.json +1 -1
  39. package/dist/chunk-53OMUNUN.js.map +0 -1
  40. package/dist/chunk-66BMJ25W.js.map +0 -1
  41. package/dist/chunk-VV66DH6T.js.map +0 -1
  42. package/dist/projects-GVEMCN5R.js.map +0 -1
  43. /package/dist/{chunk-XNNXAAZT.js.map → chunk-E4F7KASE.js.map} +0 -0
  44. /package/dist/{chunk-HMMO2LDS.js.map → chunk-ESP2FF52.js.map} +0 -0
  45. /package/dist/{chunk-Z5BM4JWB.js.map → chunk-NRDY6XO3.js.map} +0 -0
  46. /package/dist/{chunk-YU5HVI6B.js.map → chunk-YPOG7WY4.js.map} +0 -0
  47. /package/dist/{cleanup-Y5W3CNUV.js.map → cleanup-H5QUWBWE.js.map} +0 -0
  48. /package/dist/{dev-server-HNBRWGCD.js.map → dev-server-H5FFXIVX.js.map} +0 -0
  49. /package/dist/{git-OV6ADVO7.js.map → git-UHUNQZBA.js.map} +0 -0
  50. /package/dist/{init-CMIRHFSR.js.map → init-4ZR2XXZA.js.map} +0 -0
  51. /package/dist/{open-AXE225Z5.js.map → open-6HBQHPUL.js.map} +0 -0
  52. /package/dist/{rebase-6UIHMUWS.js.map → rebase-SRBOVC4M.js.map} +0 -0
  53. /package/dist/{recap-XTBNMEMO.js.map → recap-X7FTTKPP.js.map} +0 -0
  54. /package/dist/{run-H375EYRB.js.map → run-5FU76FFE.js.map} +0 -0
  55. /package/dist/{shell-33FJCWJQ.js.map → shell-UQJDI36V.js.map} +0 -0
  56. /package/dist/{summary-JUMOCNLR.js.map → summary-MOKN7RM2.js.map} +0 -0
  57. /package/dist/{test-git-CO3BA4BV.js.map → test-git-GB3B6QNT.js.map} +0 -0
  58. /package/dist/{test-prefix-HZYSDQYT.js.map → test-prefix-YQNNTCY3.js.map} +0 -0
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  InitCommand
4
- } from "./chunk-VV66DH6T.js";
4
+ } from "./chunk-ZXWTOJXA.js";
5
+ import "./chunk-NKRQNER7.js";
5
6
  import "./chunk-F2PWIRV4.js";
6
- import "./chunk-HMMO2LDS.js";
7
+ import "./chunk-ESP2FF52.js";
7
8
  import "./chunk-FXDYIV3K.js";
8
9
  import "./chunk-K5G5SFWY.js";
9
- import "./chunk-53OMUNUN.js";
10
+ import "./chunk-TKL7RBEF.js";
10
11
  import "./chunk-IDUICCZY.js";
11
12
  import "./chunk-CFUWQHCJ.js";
12
13
  import "./chunk-ZX3GTM7O.js";
@@ -16,4 +17,4 @@ import "./chunk-VT4PDUYT.js";
16
17
  export {
17
18
  InitCommand
18
19
  };
19
- //# sourceMappingURL=init-CMIRHFSR.js.map
20
+ //# sourceMappingURL=init-4ZR2XXZA.js.map
@@ -8,14 +8,14 @@ import {
8
8
  } from "./chunk-VU3QMIP2.js";
9
9
  import {
10
10
  IdentifierParser
11
- } from "./chunk-YU5HVI6B.js";
11
+ } from "./chunk-YPOG7WY4.js";
12
12
  import {
13
13
  ProjectCapabilityDetector
14
14
  } from "./chunk-EBISESAP.js";
15
15
  import "./chunk-2ZPFJQ3B.js";
16
16
  import {
17
17
  GitWorktreeManager
18
- } from "./chunk-HMMO2LDS.js";
18
+ } from "./chunk-ESP2FF52.js";
19
19
  import {
20
20
  openBrowser
21
21
  } from "./chunk-YETJNRQM.js";
@@ -24,7 +24,7 @@ import {
24
24
  } from "./chunk-GYCR2LOU.js";
25
25
  import {
26
26
  extractIssueNumber
27
- } from "./chunk-53OMUNUN.js";
27
+ } from "./chunk-TKL7RBEF.js";
28
28
  import {
29
29
  SettingsManager
30
30
  } from "./chunk-IDUICCZY.js";
@@ -288,4 +288,4 @@ Make sure the project is built (run 'il start' first)`
288
288
  export {
289
289
  OpenCommand
290
290
  };
291
- //# sourceMappingURL=open-AXE225Z5.js.map
291
+ //# sourceMappingURL=open-6HBQHPUL.js.map
@@ -38,7 +38,7 @@ var ProjectsCommand = class {
38
38
  const marker = JSON.parse(content);
39
39
  if (!marker.projectPath || !marker.projectName) continue;
40
40
  if (!await fs.pathExists(marker.projectPath)) continue;
41
- const activeLooms = this.countActiveLooms(marker.projectPath, allMetadata);
41
+ const activeLooms = await this.countActiveLooms(marker.projectPath, allMetadata);
42
42
  results.push({
43
43
  configuredAt: marker.configuredAt,
44
44
  projectPath: marker.projectPath,
@@ -58,16 +58,23 @@ var ProjectsCommand = class {
58
58
  * Count active looms for a project
59
59
  * Looms are counted if their worktreePath is in the project's -looms directory
60
60
  * or if they share the same parent directory as the project.
61
+ * Only counts looms where the worktree is a valid git worktree (.git exists).
61
62
  */
62
- countActiveLooms(projectPath, allMetadata) {
63
- return allMetadata.filter((meta) => {
64
- if (!meta.worktreePath) return false;
63
+ async countActiveLooms(projectPath, allMetadata) {
64
+ let count = 0;
65
+ for (const meta of allMetadata) {
66
+ if (!meta.worktreePath) continue;
65
67
  const parentDir = path.dirname(meta.worktreePath);
66
- return parentDir === path.dirname(projectPath) || parentDir.startsWith(projectPath + "-looms");
67
- }).length;
68
+ const isProjectLoom = parentDir === path.dirname(projectPath) || parentDir.startsWith(projectPath + "-looms");
69
+ const isValidWorktree = await fs.pathExists(path.join(meta.worktreePath, ".git"));
70
+ if (isProjectLoom && isValidWorktree) {
71
+ count++;
72
+ }
73
+ }
74
+ return count;
68
75
  }
69
76
  };
70
77
  export {
71
78
  ProjectsCommand
72
79
  };
73
- //# sourceMappingURL=projects-GVEMCN5R.js.map
80
+ //# sourceMappingURL=projects-SA76I4TZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/projects.ts"],"sourcesContent":["import path from 'path'\nimport os from 'os'\nimport fs from 'fs-extra'\nimport { getLogger } from '../utils/logger-context.js'\nimport { MetadataManager, type LoomMetadata } from '../lib/MetadataManager.js'\n\n/**\n * Project marker file structure (from FirstRunManager)\n */\ninterface ProjectMarker {\n configuredAt: string\n projectPath: string\n projectName: string\n}\n\n/**\n * Output schema for project\n */\nexport interface ProjectOutput {\n configuredAt: string\n projectPath: string\n projectName: string\n activeLooms: number\n}\n\n/**\n * ProjectsCommand: List configured iloom projects\n *\n * Returns JSON array of configured projects from ~/.config/iloom-ai/projects/\n * Only includes projects where the directory still exists.\n * Each project includes an activeLooms count.\n */\nexport class ProjectsCommand {\n private readonly projectsDir: string\n private readonly metadataManager: MetadataManager\n\n constructor(metadataManager?: MetadataManager) {\n this.projectsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'projects')\n this.metadataManager = metadataManager ?? new MetadataManager()\n }\n\n /**\n * Execute the projects command\n * @param _options - Options object (json flag accepted but ignored - always returns JSON)\n * @returns Array of project outputs\n */\n async execute(_options?: { json?: boolean }): Promise<ProjectOutput[]> {\n // Options.json is accepted but ignored - always returns structured data\n const results: ProjectOutput[] = []\n const logger = getLogger()\n\n try {\n // Check if projects directory exists\n if (!(await fs.pathExists(this.projectsDir))) {\n return results\n }\n\n // Read all files in projects directory\n const files = await fs.readdir(this.projectsDir)\n\n // Get all loom metadata for active looms lookup\n const allMetadata = await this.metadataManager.listAllMetadata()\n\n for (const file of files) {\n // Skip hidden files (like .DS_Store)\n if (file.startsWith('.')) continue\n\n try {\n const filePath = path.join(this.projectsDir, file)\n const content = await fs.readFile(filePath, 'utf8')\n const marker: ProjectMarker = JSON.parse(content)\n\n // Skip if required fields missing\n if (!marker.projectPath || !marker.projectName) continue\n\n // Filter: only include if directory exists\n if (!(await fs.pathExists(marker.projectPath))) continue\n\n // Count active looms for this project\n const activeLooms = await this.countActiveLooms(marker.projectPath, allMetadata)\n\n results.push({\n configuredAt: marker.configuredAt,\n projectPath: marker.projectPath,\n projectName: marker.projectName,\n activeLooms,\n })\n } catch {\n // Skip invalid files (graceful degradation)\n logger.debug(`Skipping invalid project file: ${file}`)\n }\n }\n } catch (error) {\n // Graceful degradation on read errors\n logger.debug(`Failed to list projects: ${error instanceof Error ? error.message : 'Unknown'}`)\n }\n\n return results\n }\n\n /**\n * Count active looms for a project\n * Looms are counted if their worktreePath is in the project's -looms directory\n * or if they share the same parent directory as the project.\n * Only counts looms where the worktree is a valid git worktree (.git exists).\n */\n private async countActiveLooms(projectPath: string, allMetadata: LoomMetadata[]): Promise<number> {\n let count = 0\n for (const meta of allMetadata) {\n if (!meta.worktreePath) continue\n const parentDir = path.dirname(meta.worktreePath)\n const isProjectLoom =\n parentDir === path.dirname(projectPath) ||\n parentDir.startsWith(projectPath + '-looms')\n // Check for .git (file for worktrees, directory for main repo) to verify it's a valid git worktree\n const isValidWorktree = await fs.pathExists(path.join(meta.worktreePath, '.git'))\n if (isProjectLoom && isValidWorktree) {\n count++\n }\n }\n return count\n }\n}\n"],"mappings":";;;;;;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AA8BR,IAAM,kBAAN,MAAsB;AAAA,EAI3B,YAAY,iBAAmC;AAC7C,SAAK,cAAc,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,YAAY,UAAU;AAC5E,SAAK,kBAAkB,mBAAmB,IAAI,gBAAgB;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,UAAyD;AAErE,UAAM,UAA2B,CAAC;AAClC,UAAM,SAAS,UAAU;AAEzB,QAAI;AAEF,UAAI,CAAE,MAAM,GAAG,WAAW,KAAK,WAAW,GAAI;AAC5C,eAAO;AAAA,MACT;AAGA,YAAM,QAAQ,MAAM,GAAG,QAAQ,KAAK,WAAW;AAG/C,YAAM,cAAc,MAAM,KAAK,gBAAgB,gBAAgB;AAE/D,iBAAW,QAAQ,OAAO;AAExB,YAAI,KAAK,WAAW,GAAG,EAAG;AAE1B,YAAI;AACF,gBAAM,WAAW,KAAK,KAAK,KAAK,aAAa,IAAI;AACjD,gBAAM,UAAU,MAAM,GAAG,SAAS,UAAU,MAAM;AAClD,gBAAM,SAAwB,KAAK,MAAM,OAAO;AAGhD,cAAI,CAAC,OAAO,eAAe,CAAC,OAAO,YAAa;AAGhD,cAAI,CAAE,MAAM,GAAG,WAAW,OAAO,WAAW,EAAI;AAGhD,gBAAM,cAAc,MAAM,KAAK,iBAAiB,OAAO,aAAa,WAAW;AAE/E,kBAAQ,KAAK;AAAA,YACX,cAAc,OAAO;AAAA,YACrB,aAAa,OAAO;AAAA,YACpB,aAAa,OAAO;AAAA,YACpB;AAAA,UACF,CAAC;AAAA,QACH,QAAQ;AAEN,iBAAO,MAAM,kCAAkC,IAAI,EAAE;AAAA,QACvD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,aAAO,MAAM,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,SAAS,EAAE;AAAA,IAC/F;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,iBAAiB,aAAqB,aAA8C;AAChG,QAAI,QAAQ;AACZ,eAAW,QAAQ,aAAa;AAC9B,UAAI,CAAC,KAAK,aAAc;AACxB,YAAM,YAAY,KAAK,QAAQ,KAAK,YAAY;AAChD,YAAM,gBACJ,cAAc,KAAK,QAAQ,WAAW,KACtC,UAAU,WAAW,cAAc,QAAQ;AAE7C,YAAM,kBAAkB,MAAM,GAAG,WAAW,KAAK,KAAK,KAAK,cAAc,MAAM,CAAC;AAChF,UAAI,iBAAiB,iBAAiB;AACpC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -267,7 +267,8 @@ Perform ONE comprehensive scan to determine which agents need to run:
267
267
 
268
268
  Only execute if workflow plan determined NEEDS_ENHANCEMENT:
269
269
  1. Execute: @agent-iloom-issue-enhancer ISSUE_NUMBER with the following instructions about assumptions:
270
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
270
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
271
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
271
272
  2. Upon completion: Extract issue+comment link (including comment ID) from agent output and provide link to issue comment
272
273
  {{#IF INTERACTIVE_MODE}}
273
274
  2.5. Extract and validate assumptions (batched validation):
@@ -317,7 +318,8 @@ If workflow plan determined SKIP_ENHANCEMENT:
317
318
 
318
319
  Only execute if workflow plan determined NEEDS_COMPLEXITY_EVAL:
319
320
  1. Execute: @agent-iloom-issue-complexity-evaluator ISSUE_NUMBER with the following instructions about assumptions:
320
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
321
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
322
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
321
323
  2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
322
324
  {{#IF INTERACTIVE_MODE}}
323
325
  2.5. Extract and validate assumptions (batched validation):
@@ -384,7 +386,8 @@ If workflow plan determined SKIP_COMPLEXITY_EVAL:
384
386
 
385
387
  Only execute if workflow plan determined NEEDS_ANALYSIS AND complexity is COMPLEX:
386
388
  1. Execute: @agent-iloom-issue-analyzer ISSUE_NUMBER with the following instructions about assumptions:
387
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
389
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
390
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
388
391
  2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
389
392
  - Use `recap.add_entry` to log significant technical findings (type: "insight" for dependencies/constraints, type: "risk" for problems identified)
390
393
  {{#IF INTERACTIVE_MODE}}
@@ -462,7 +465,8 @@ After STEP 1.5 completes and complexity is confirmed, determine which workflow p
462
465
  Execute combined analyze-and-plan agent:
463
466
  1. Display: "Executing combined analyze-and-plan agent for SIMPLE task..."
464
467
  2. Execute: @agent-iloom-issue-analyze-and-plan ISSUE_NUMBER with the following instructions about assumptions:
465
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
468
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
469
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
466
470
  3. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
467
471
  - Use `recap.add_entry` to log key technical discoveries (type: "insight") and design decisions (type: "decision")
468
472
  {{#IF INTERACTIVE_MODE}}
@@ -502,7 +506,8 @@ Execute combined analyze-and-plan agent:
502
506
 
503
507
  Only execute if workflow plan determined NEEDS_PLANNING AND complexity is COMPLEX:
504
508
  1. Execute: @agent-iloom-issue-planner ISSUE_NUMBER with the following instructions about assumptions:
505
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
509
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
510
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
506
511
  2. Upon completion: Extract issue+comment link from agent output and provide link to issue comment (including comment ID)
507
512
  - Use `recap.add_entry` to log key architectural decisions (e.g., type: "decision", content: "Chose approach A over B due to X constraint")
508
513
  {{#IF INTERACTIVE_MODE}}
@@ -548,7 +553,8 @@ Only execute if workflow plan determined NEEDS_IMPLEMENTATION:
548
553
  - Extract the comment ID from the issue comment URL that was provided
549
554
  - The comment ID is the numeric value after "#issuecomment-" in the URL (e.g., https://github.com/org/repo/issues/123#issuecomment-456789 has comment ID 456789)
550
555
  2. Execute: @agent-iloom-issue-implementer ISSUE_NUMBER with the following instructions about assumptions:
551
- - Before making assumptions, scan ALL issue comments for previously asked questions and their answers. Document any assumptions clearly in question tables with your own answers.
556
+ - Before making assumptions, scan ALL issue comments for previously asked questions and their answers.
557
+ - When creating question tables in your issue comments, fill in your own assumed answers to each question. This documents assumptions made during execution.
552
558
  Additional critical instructions:
553
559
  - **CRITICAL**: The implementation plan in the issue comments contains exact file paths and line numbers. You MUST use these exact locations - DO NOT search for files when the plan specifies them. Extract file locations from the plan in Step 1.5 before implementing.
554
560
  - The implementation plan is located in comment ID [COMMENT_ID] (if planning phase was executed). Read this comment first in Step 1.5 to extract file specifications.
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MergeManager
4
- } from "./chunk-XNNXAAZT.js";
4
+ } from "./chunk-E4F7KASE.js";
5
5
  import {
6
6
  GitWorktreeManager
7
- } from "./chunk-HMMO2LDS.js";
7
+ } from "./chunk-ESP2FF52.js";
8
8
  import {
9
9
  getWorktreeRoot,
10
10
  isValidGitRepo
11
- } from "./chunk-53OMUNUN.js";
11
+ } from "./chunk-TKL7RBEF.js";
12
12
  import {
13
13
  SettingsManager
14
14
  } from "./chunk-IDUICCZY.js";
@@ -94,4 +94,4 @@ export {
94
94
  RebaseCommand,
95
95
  WorktreeValidationError
96
96
  };
97
- //# sourceMappingURL=rebase-6UIHMUWS.js.map
97
+ //# sourceMappingURL=rebase-SRBOVC4M.js.map
@@ -4,11 +4,11 @@ import {
4
4
  } from "./chunk-NXMDEL3F.js";
5
5
  import {
6
6
  IdentifierParser
7
- } from "./chunk-YU5HVI6B.js";
7
+ } from "./chunk-YPOG7WY4.js";
8
8
  import {
9
9
  GitWorktreeManager
10
- } from "./chunk-HMMO2LDS.js";
11
- import "./chunk-53OMUNUN.js";
10
+ } from "./chunk-ESP2FF52.js";
11
+ import "./chunk-TKL7RBEF.js";
12
12
  import "./chunk-IDUICCZY.js";
13
13
  import "./chunk-CFUWQHCJ.js";
14
14
  import "./chunk-6MLEBAYZ.js";
@@ -107,4 +107,4 @@ var RecapCommand = class {
107
107
  export {
108
108
  RecapCommand
109
109
  };
110
- //# sourceMappingURL=recap-XTBNMEMO.js.map
110
+ //# sourceMappingURL=recap-X7FTTKPP.js.map
@@ -8,14 +8,14 @@ import {
8
8
  } from "./chunk-VU3QMIP2.js";
9
9
  import {
10
10
  IdentifierParser
11
- } from "./chunk-YU5HVI6B.js";
11
+ } from "./chunk-YPOG7WY4.js";
12
12
  import {
13
13
  ProjectCapabilityDetector
14
14
  } from "./chunk-EBISESAP.js";
15
15
  import "./chunk-2ZPFJQ3B.js";
16
16
  import {
17
17
  GitWorktreeManager
18
- } from "./chunk-HMMO2LDS.js";
18
+ } from "./chunk-ESP2FF52.js";
19
19
  import {
20
20
  openBrowser
21
21
  } from "./chunk-YETJNRQM.js";
@@ -24,7 +24,7 @@ import {
24
24
  } from "./chunk-GYCR2LOU.js";
25
25
  import {
26
26
  extractIssueNumber
27
- } from "./chunk-53OMUNUN.js";
27
+ } from "./chunk-TKL7RBEF.js";
28
28
  import {
29
29
  SettingsManager
30
30
  } from "./chunk-IDUICCZY.js";
@@ -288,4 +288,4 @@ Make sure the project is built (run 'il start' first)`
288
288
  export {
289
289
  RunCommand
290
290
  };
291
- //# sourceMappingURL=run-H375EYRB.js.map
291
+ //# sourceMappingURL=run-5FU76FFE.js.map
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  IdentifierParser
4
- } from "./chunk-YU5HVI6B.js";
4
+ } from "./chunk-YPOG7WY4.js";
5
5
  import {
6
6
  GitWorktreeManager
7
- } from "./chunk-HMMO2LDS.js";
7
+ } from "./chunk-ESP2FF52.js";
8
8
  import {
9
9
  extractIssueNumber
10
- } from "./chunk-53OMUNUN.js";
10
+ } from "./chunk-TKL7RBEF.js";
11
11
  import {
12
12
  SettingsManager
13
13
  } from "./chunk-IDUICCZY.js";
@@ -235,4 +235,4 @@ var ShellCommand = class {
235
235
  export {
236
236
  ShellCommand
237
237
  };
238
- //# sourceMappingURL=shell-33FJCWJQ.js.map
238
+ //# sourceMappingURL=shell-UQJDI36V.js.map
@@ -6,12 +6,12 @@ import "./chunk-NXMDEL3F.js";
6
6
  import "./chunk-7Q66W4OH.js";
7
7
  import {
8
8
  GitWorktreeManager
9
- } from "./chunk-HMMO2LDS.js";
9
+ } from "./chunk-ESP2FF52.js";
10
10
  import "./chunk-FXDYIV3K.js";
11
11
  import "./chunk-K5G5SFWY.js";
12
12
  import {
13
13
  extractIssueNumber
14
- } from "./chunk-53OMUNUN.js";
14
+ } from "./chunk-TKL7RBEF.js";
15
15
  import "./chunk-IDUICCZY.js";
16
16
  import {
17
17
  MetadataManager
@@ -248,4 +248,4 @@ Please provide an issue number, PR number, or branch name.`
248
248
  export {
249
249
  SummaryCommand
250
250
  };
251
- //# sourceMappingURL=summary-JUMOCNLR.js.map
251
+ //# sourceMappingURL=summary-MOKN7RM2.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  findMainWorktreePath
4
- } from "./chunk-53OMUNUN.js";
4
+ } from "./chunk-TKL7RBEF.js";
5
5
  import {
6
6
  SettingsManager
7
7
  } from "./chunk-IDUICCZY.js";
@@ -51,4 +51,4 @@ var TestGitCommand = class {
51
51
  export {
52
52
  TestGitCommand
53
53
  };
54
- //# sourceMappingURL=test-git-CO3BA4BV.js.map
54
+ //# sourceMappingURL=test-git-GB3B6QNT.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  generateWorktreePath
4
- } from "./chunk-53OMUNUN.js";
4
+ } from "./chunk-TKL7RBEF.js";
5
5
  import {
6
6
  SettingsManager
7
7
  } from "./chunk-IDUICCZY.js";
@@ -67,4 +67,4 @@ var TestPrefixCommand = class {
67
67
  export {
68
68
  TestPrefixCommand
69
69
  };
70
- //# sourceMappingURL=test-prefix-HZYSDQYT.js.map
70
+ //# sourceMappingURL=test-prefix-YQNNTCY3.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iloom/cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Control plane for maintaining alignment between you and Claude Code as you work across multiple issues using isolated environments, visible context, and multi-agent workflows to scale understanding, not just output",
5
5
  "keywords": [
6
6
  "ai",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/utils/git.ts"],"sourcesContent":["import path from 'path'\nimport { execa, type ExecaError } from 'execa'\nimport { type GitWorktree } from '../types/worktree.js'\nimport { SettingsManager, type SettingsManager as SettingsManagerType } from '../lib/SettingsManager.js'\nimport { MetadataManager } from '../lib/MetadataManager.js'\nimport { logger } from './logger.js'\n\n/**\n * Execute a Git command and return the stdout result\n * Throws an error if the command fails\n */\nexport async function executeGitCommand(\n args: string[],\n options?: { cwd?: string; timeout?: number; stdio?: 'inherit' | 'pipe'; env?: NodeJS.ProcessEnv }\n): Promise<string> {\n try {\n const result = await execa('git', args, {\n cwd: options?.cwd ?? process.cwd(),\n timeout: options?.timeout ?? 30000,\n encoding: 'utf8',\n stdio: options?.stdio ?? 'pipe',\n verbose: logger.isDebugEnabled(),\n // Spread env conditionally - only include if defined\n ...(options?.env && { env: options.env }),\n })\n\n return result.stdout\n } catch (error) {\n const execaError = error as ExecaError\n const stderr = execaError.stderr ?? execaError.message ?? 'Unknown Git error'\n throw new Error(`Git command failed: ${stderr}`)\n }\n}\n\n/**\n * Parse git worktree list output into structured data\n * @param output - The output from git worktree list --porcelain\n * @param defaultBranch - Default branch name to use for bare repositories (defaults to 'main')\n */\nexport function parseWorktreeList(output: string, defaultBranch?: string): GitWorktree[] {\n const worktrees: GitWorktree[] = []\n const lines = output.trim().split('\\n')\n\n let i = 0\n while (i < lines.length) {\n const pathLine = lines[i]\n if (!pathLine?.startsWith('worktree ')) {\n i++\n continue\n }\n\n // Parse path line: \"worktree /path/to/worktree\"\n const pathMatch = pathLine.match(/^worktree (.+)$/)\n if (!pathMatch) {\n i++\n continue\n }\n\n let branch = ''\n let commit = ''\n let detached = false\n let bare = false\n let locked = false\n let lockReason: string | undefined\n\n // Process subsequent lines for this worktree\n i++\n while (i < lines.length && !lines[i]?.startsWith('worktree ')) {\n const line = lines[i]?.trim()\n if (!line) {\n i++\n continue\n }\n\n if (line === 'bare') {\n bare = true\n branch = defaultBranch ?? 'main' // Default assumption for bare repo\n } else if (line === 'detached') {\n detached = true\n branch = 'HEAD'\n } else if (line.startsWith('locked')) {\n locked = true\n const lockMatch = line.match(/^locked (.+)$/)\n lockReason = lockMatch?.[1]\n branch = branch || 'unknown'\n } else if (line.startsWith('HEAD ')) {\n // Parse commit line: \"HEAD abc123def456...\"\n const commitMatch = line.match(/^HEAD ([a-f0-9]+)/)\n if (commitMatch) {\n commit = commitMatch[1] ?? ''\n }\n } else if (line.startsWith('branch ')) {\n // Parse branch line: \"branch refs/heads/feature-branch\"\n const branchMatch = line.match(/^branch refs\\/heads\\/(.+)$/)\n branch = branchMatch?.[1] ?? line.replace('branch ', '')\n }\n\n i++\n }\n\n const worktree: GitWorktree = {\n path: pathMatch[1] ?? '',\n branch,\n commit,\n bare,\n detached,\n locked,\n }\n\n if (lockReason !== undefined) {\n worktree.lockReason = lockReason\n }\n\n worktrees.push(worktree)\n }\n\n return worktrees\n}\n\n/**\n * Check if a branch name follows PR naming patterns\n */\nexport function isPRBranch(branchName: string): boolean {\n const prPatterns = [\n /^pr\\/\\d+/i, // pr/123, pr/123-feature-name\n /^pull\\/\\d+/i, // pull/123\n /^\\d+[-_]/, // 123-feature-name, 123_feature_name\n /^feature\\/pr[-_]?\\d+/i, // feature/pr123, feature/pr-123\n /^hotfix\\/pr[-_]?\\d+/i, // hotfix/pr123\n ]\n\n return prPatterns.some(pattern => pattern.test(branchName))\n}\n\n/**\n * Extract PR number from branch name\n */\nexport function extractPRNumber(branchName: string): number | null {\n const patterns = [\n /^pr\\/(\\d+)/i, // pr/123\n /^pull\\/(\\d+)/i, // pull/123\n /^(\\d+)[-_]/, // 123-feature-name\n /^feature\\/pr[-_]?(\\d+)/i, // feature/pr123\n /^hotfix\\/pr[-_]?(\\d+)/i, // hotfix/pr123\n /pr[-_]?(\\d+)/i, // anywhere with pr123 or pr-123\n ]\n\n for (const pattern of patterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) {\n const num = parseInt(match[1], 10)\n if (!isNaN(num)) return num\n }\n }\n\n return null\n}\n\n/**\n * Extract issue number from branch name\n * Supports both new format (issue-{issueId}__{slug}) and old format (issue-{number}-{slug})\n * @returns string issue ID (alphanumeric) or null if not found\n */\nexport function extractIssueNumber(branchName: string): string | null {\n // Priority 1: New format - issue-{issueId}__ (alphanumeric ID with double underscore)\n const newFormatPattern = /issue-([^_]+)__/i\n const newMatch = branchName.match(newFormatPattern)\n if (newMatch?.[1]) return newMatch[1]\n\n // Priority 2: Old format - issue-{number}- or issue-{number}$ (numeric only, dash or end)\n const oldFormatPattern = /issue-(\\d+)(?:-|$)/i\n const oldMatch = branchName.match(oldFormatPattern)\n if (oldMatch?.[1]) return oldMatch[1]\n\n // Priority 3: Alphanumeric ID at end (either format without description)\n const alphanumericEndPattern = /issue-([^_\\s/]+)$/i\n const alphanumericMatch = branchName.match(alphanumericEndPattern)\n if (alphanumericMatch?.[1]) return alphanumericMatch[1]\n\n // Priority 4: Legacy patterns (issue_N, leading number)\n const legacyPatterns = [\n /issue_(\\d+)/i, // issue_42\n /^(\\d+)-/, // 42-feature-name\n ]\n for (const pattern of legacyPatterns) {\n const match = branchName.match(pattern)\n if (match?.[1]) return match[1]\n }\n\n return null\n}\n\n/**\n * Check if a path follows worktree naming patterns\n */\nexport function isWorktreePath(path: string): boolean {\n const worktreePatterns = [\n /\\/worktrees?\\//i, // Contains /worktree/ or /worktrees/\n /\\/workspace[-_]?\\d+/i, // workspace123, workspace-123\n /\\/issue[-_]?\\d+/i, // issue123, issue-123\n /\\/pr[-_]?\\d+/i, // pr123, pr-123\n /-worktree$/i, // ends with -worktree\n /\\.worktree$/i, // ends with .worktree\n ]\n\n return worktreePatterns.some(pattern => pattern.test(path))\n}\n\n/**\n * Generate a worktree path based on branch name and root directory\n * For PRs, adds _pr_<PR_NUM> suffix to distinguish from issue branches\n */\nexport function generateWorktreePath(\n branchName: string,\n rootDir: string = process.cwd(),\n options?: { isPR?: boolean; prNumber?: number; prefix?: string }\n): string {\n // Replace slashes with dashes (matches bash line 593)\n let sanitized = branchName.replace(/\\//g, '-')\n\n // Add PR suffix if this is a PR (matches bash lines 595-597)\n if (options?.isPR && options?.prNumber) {\n sanitized = `${sanitized}_pr_${options.prNumber}`\n }\n\n const parentDir = path.dirname(rootDir)\n\n // Handle prefix logic\n let prefix: string\n\n if (options?.prefix === undefined) {\n // No prefix in options - calculate default: <basename>-looms\n const mainFolderName = path.basename(rootDir)\n prefix = mainFolderName ? `${mainFolderName}-looms/` : 'looms/'\n } else if (options.prefix === '') {\n // Empty string = no prefix mode\n prefix = ''\n } else {\n // Custom prefix provided\n prefix = options.prefix\n\n // Check if prefix contains forward slashes (nested directory structure)\n const hasNestedPath = prefix.includes('/')\n\n if (hasNestedPath) {\n // Check if it ends with a separator character (dash, underscore, or slash)\n const endsWithSeparator = /[-_/]$/.test(prefix)\n\n if (!endsWithSeparator) {\n // Has nested path but no trailing separator: auto-append hyphen\n // Example: \"temp/looms\" becomes \"temp/looms-\"\n prefix = `${prefix}-`\n }\n // If it already ends with -, _, or /, keep as-is\n } else {\n // Single-level prefix: auto-append separator if it doesn't end with one\n const endsWithSeparator = /[-_]$/.test(prefix)\n if (!endsWithSeparator) {\n prefix = `${prefix}-`\n }\n }\n }\n\n // Apply prefix (or not, if empty)\n if (prefix === '') {\n return path.join(parentDir, sanitized)\n } else if (prefix.endsWith('/')) {\n // Forward slash = nested directory, use path.join for proper handling\n return path.join(parentDir, prefix, sanitized)\n } else if (prefix.includes('/')) {\n // Contains slash but doesn't end with slash = nested with separator (e.g., \"looms/myprefix-\")\n // Split and handle: last part is prefix with separator, rest is directory path\n const lastSlashIndex = prefix.lastIndexOf('/')\n const dirPath = prefix.substring(0, lastSlashIndex)\n const prefixWithSeparator = prefix.substring(lastSlashIndex + 1)\n return path.join(parentDir, dirPath, `${prefixWithSeparator}${sanitized}`)\n } else {\n // Dash/underscore separator = single directory name\n return path.join(parentDir, `${prefix}${sanitized}`)\n }\n}\n\n/**\n * Validate that a directory is a valid Git repository\n */\nexport async function isValidGitRepo(path: string): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--git-dir'], { cwd: path })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Get the current branch name for a repository\n */\nexport async function getCurrentBranch(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['branch', '--show-current'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Check if a branch exists (local or remote)\n */\nexport async function branchExists(\n branchName: string,\n path: string = process.cwd(),\n includeRemote = true\n): Promise<boolean> {\n try {\n // Check local branches\n const localResult = await executeGitCommand(['branch', '--list', branchName], { cwd: path })\n if (localResult.trim()) {\n return true\n }\n\n // Check remote branches if requested\n if (includeRemote) {\n const remoteResult = await executeGitCommand(['branch', '-r', '--list', `*/${branchName}`], {\n cwd: path,\n })\n if (remoteResult.trim()) {\n return true\n }\n }\n\n return false\n } catch {\n return false\n }\n}\n\n/**\n * Get the root directory of the current worktree\n * Returns the worktree root when in a linked worktree, or main repo root when in main worktree\n */\nexport async function getWorktreeRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n const result = await executeGitCommand(['rev-parse', '--show-toplevel'], { cwd: path })\n return result.trim()\n } catch {\n return null\n }\n}\n\n/**\n * Get the main repository root directory\n * Returns the main repo root even when called from a linked worktree\n */\nexport async function getRepoRoot(path: string = process.cwd()): Promise<string | null> {\n try {\n // Get the common git directory (shared by all worktrees)\n const gitCommonDir = await executeGitCommand(\n ['rev-parse', '--path-format=absolute', '--git-common-dir'],\n { cwd: path }\n )\n const trimmedPath = gitCommonDir.trim()\n\n // Handle linked worktree: /path/to/repo/.git/worktrees/worktree-name -> /path/to/repo\n // Handle main worktree: /path/to/repo/.git -> /path/to/repo\n const repoRoot = trimmedPath\n .replace(/\\/\\.git\\/worktrees\\/[^/]+$/, '') // Remove /.git/worktrees/name suffix\n .replace(/\\/\\.git$/, '') // Remove /.git suffix\n\n return repoRoot\n } catch(error) {\n logger.warn(`Failed to determine repo root from git-common-dir: ${path}`, error instanceof Error ? error.message : String(error))\n return null\n }\n}\n\n/**\n * Find the worktree path where main branch is checked out\n * Copies bash script approach: parse git worktree list to find main\n */\nexport async function findMainWorktreePath(\n path: string = process.cwd(),\n options?: { mainBranch?: string }\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, options?.mainBranch)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Tier 1: Check for specified mainBranch in options\n if (options?.mainBranch) {\n const specified = worktrees.find(wt => wt.branch === options.mainBranch)\n if (!specified?.path) {\n throw new Error(\n `No worktree found with branch '${options.mainBranch}' (specified in settings). Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return specified.path\n }\n\n // Tier 2: Look for \"main\" branch\n const mainBranch = worktrees.find(wt => wt.branch === 'main')\n if (mainBranch?.path) {\n return mainBranch.path\n }\n\n // Tier 3: Use first worktree (primary worktree)\n const firstWorktree = worktrees[0]\n if (!firstWorktree?.path) {\n throw new Error('Failed to determine primary worktree path')\n }\n return firstWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found') ||\n error.message.includes('Failed to determine primary worktree'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find main worktree: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Find main worktree path with automatic settings loading\n *\n * This is a convenience wrapper that:\n * 1. Loads project settings from .iloom/settings.json\n * 2. Extracts mainBranch configuration if present\n * 3. Calls findMainWorktreePath with appropriate options\n *\n * @param path - Path to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n * @returns Path to main worktree\n * @throws Error if main worktree cannot be found\n */\nexport async function findMainWorktreePathWithSettings(\n path?: string,\n settingsManager?: SettingsManagerType\n): Promise<string> {\n // Lazy load SettingsManager to avoid circular dependencies\n settingsManager ??= new SettingsManager()\n\n const settings = await settingsManager.loadSettings(path)\n const findOptions = settings.mainBranch ? { mainBranch: settings.mainBranch } : undefined\n return findMainWorktreePath(path, findOptions)\n}\n\n/**\n * Find the worktree path where a specific branch is checked out\n *\n * Used by MergeManager to find the correct worktree for child loom merges.\n * When finishing a child loom, we need to find where the PARENT branch is\n * checked out (the merge target), not where settings.mainBranch is checked out.\n *\n * @param branchName - The branch name to find\n * @param path - Path to search from (defaults to process.cwd())\n * @returns Path to worktree where the branch is checked out\n * @throws Error if no worktree has the specified branch checked out\n */\nexport async function findWorktreeForBranch(\n branchName: string,\n path: string = process.cwd()\n): Promise<string> {\n try {\n const output = await executeGitCommand(['worktree', 'list', '--porcelain'], { cwd: path })\n const worktrees = parseWorktreeList(output, branchName)\n\n // Guard: empty worktree list\n if (worktrees.length === 0) {\n throw new Error('No worktrees found in repository')\n }\n\n // Find the worktree with the specified branch\n const targetWorktree = worktrees.find(wt => wt.branch === branchName)\n if (!targetWorktree?.path) {\n throw new Error(\n `No worktree found with branch '${branchName}' checked out. ` +\n `Available worktrees: ${worktrees.map(wt => `${wt.path} (${wt.branch})`).join(', ')}`\n )\n }\n return targetWorktree.path\n } catch (error) {\n if (\n error instanceof Error &&\n (error.message.includes('No worktree found with branch') ||\n error.message.includes('No worktrees found'))\n ) {\n // Re-throw our specific errors\n throw error\n }\n throw new Error(`Failed to find worktree for branch '${branchName}': ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Check if there are uncommitted changes in a repository\n */\nexport async function hasUncommittedChanges(path: string = process.cwd()): Promise<boolean> {\n try {\n const result = await executeGitCommand(['status', '--porcelain'], { cwd: path })\n return result.trim().length > 0\n } catch {\n return false\n }\n}\n\n/**\n * Get the default branch name for a repository\n */\nexport async function getDefaultBranch(path: string = process.cwd()): Promise<string> {\n try {\n // Try to get from remote\n const remoteResult = await executeGitCommand(['symbolic-ref', 'refs/remotes/origin/HEAD'], {\n cwd: path,\n })\n const match = remoteResult.match(/refs\\/remotes\\/origin\\/(.+)/)\n if (match) return match[1] ?? 'main'\n\n // Fallback to common default branch names\n const commonDefaults = ['main', 'master', 'develop']\n for (const branch of commonDefaults) {\n if (await branchExists(branch, path)) {\n return branch\n }\n }\n\n return 'main' // Final fallback\n } catch {\n return 'main'\n }\n}\n\n/**\n * Find all branches related to a GitHub issue or PR number\n * Matches patterns like:\n * - Issue patterns: issue-25, issue/25, 25-feature, feat-25, feat/issue-25\n * - PR patterns: pr/25, pull/25, pr-25, feature/pr-25\n *\n * Based on bash cleanup-worktree.sh find_issue_branches() (lines 133-154)\n *\n * @param issueNumber - The issue or PR number to search for\n * @param path - Working directory to search from (defaults to process.cwd())\n * @param settingsManager - Optional SettingsManager instance (for DI/testing)\n */\nexport async function findAllBranchesForIssue(\n issueNumber: string | number,\n path: string = process.cwd(),\n settingsManager?: SettingsManagerType\n): Promise<string[]> {\n // Lazy load SettingsManager to avoid circular dependencies\n if (!settingsManager) {\n const { SettingsManager: SM } = await import('../lib/SettingsManager.js')\n settingsManager = new SM()\n }\n\n // Get protected branches list from centralized method\n const protectedBranches = await settingsManager.getProtectedBranches(path)\n\n // Get all branches (local and remote)\n const output = await executeGitCommand(['branch', '-a'], { cwd: path })\n\n const branches: string[] = []\n const lines = output.split('\\n').filter(Boolean)\n\n for (const line of lines) {\n // Skip remotes/origin/HEAD pointer\n if (line.includes('remotes/origin/HEAD')) {\n continue\n }\n\n // Clean the branch name:\n // 1. Remove git status markers (* + spaces at start)\n let cleanBranch = line.replace(/^[*+ ]+/, '')\n\n // 2. Remove 'origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^origin\\//, '')\n\n // 3. Remove 'remotes/origin/' prefix if present\n cleanBranch = cleanBranch.replace(/^remotes\\/origin\\//, '')\n\n // 4. Trim any remaining whitespace\n cleanBranch = cleanBranch.trim()\n\n // Skip protected branches\n if (protectedBranches.includes(cleanBranch)) {\n continue\n }\n\n // Check if branch contains issue number with strict word boundary pattern\n // The issue number must NOT be:\n // - Part of a larger number (preceded or followed by a digit)\n // - After an unknown word (like \"tissue-25\")\n // The issue number CAN be:\n // - At start: \"25-feature\"\n // - After known prefix + separator: \"issue-25\", \"feat-25\", \"fix-25\", \"pr-25\"\n // - After just a separator with no prefix: test_25 (separator at start)\n\n // First check: not part of a larger number\n const notPartOfNumber = new RegExp(`(?<!\\\\d)${issueNumber}(?!\\\\d)`)\n if (!notPartOfNumber.test(cleanBranch)) {\n continue\n }\n\n // Second check: if preceded by letters, validate they're known issue-related prefixes\n // This prevents \"tissue-25\" but allows \"issue-25\", \"feat-25\", etc.\n const beforeNumber = cleanBranch.substring(0, cleanBranch.indexOf(String(issueNumber)))\n\n if (beforeNumber) {\n // Extract the last word (letters) before the number\n const lastWord = beforeNumber.match(/([a-zA-Z]+)[-_/\\s]*$/)\n if (lastWord?.[1]) {\n const word = lastWord[1].toLowerCase()\n // Known prefixes for issue-related branches\n const knownPrefixes = [\n 'issue', 'issues',\n 'feat', 'feature', 'features',\n 'fix', 'fixes', 'bugfix', 'hotfix',\n 'pr', 'pull',\n 'test', 'tests',\n 'chore',\n 'docs',\n 'refactor',\n 'perf',\n 'style',\n 'ci',\n 'build',\n 'revert'\n ]\n\n // If we found a word and it's NOT in the known list, skip this branch\n if (!knownPrefixes.includes(word)) {\n continue\n }\n }\n }\n\n // Passed all checks - add to results\n if (!branches.includes(cleanBranch)) {\n branches.push(cleanBranch)\n }\n }\n\n return branches\n}\n\n/**\n * Check if a repository is empty (has no commits yet)\n * @param path - Repository path to check (defaults to process.cwd())\n * @returns true if repository has no commits, false otherwise\n */\nexport async function isEmptyRepository(path: string = process.cwd()): Promise<boolean> {\n try {\n await executeGitCommand(['rev-parse', '--verify', 'HEAD'], { cwd: path })\n return false // HEAD exists, repo has commits\n } catch {\n return true // HEAD doesn't exist, repo is empty\n }\n}\n\n/**\n * Ensure repository has at least one commit\n * Creates an initial empty commit if repository is empty\n * @param path - Repository path (defaults to process.cwd())\n */\nexport async function ensureRepositoryHasCommits(path: string = process.cwd()): Promise<void> {\n const isEmpty = await isEmptyRepository(path)\n if (isEmpty) {\n await executeGitCommand(['commit', '--no-verify', '--allow-empty', '-m', 'Initial commit'], { cwd: path })\n }\n}\n\n/**\n * Push a branch to remote repository\n * Used for PR workflow to push changes to remote without merging locally\n *\n * @param branchName - The branch name to push\n * @param worktreePath - The worktree path where the branch is checked out\n * @param options - Push options\n * @throws Error if push fails\n */\nexport async function pushBranchToRemote(\n branchName: string,\n worktreePath: string,\n options?: { dryRun?: boolean }\n): Promise<void> {\n if (options?.dryRun) {\n // In dry-run mode, just log what would be done\n return\n }\n\n try {\n // Execute: git push origin <branch-name>\n // This matches the bash script behavior (merge-and-clean.sh line 359)\n await executeGitCommand(['push', 'origin', branchName], {\n cwd: worktreePath,\n timeout: 120000, // 120 second timeout for push operations\n })\n } catch (error) {\n // Provide helpful error message based on common push failures\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n // Check for common error patterns and provide context, but ALWAYS include original error\n if (errorMessage.includes('failed to push') || errorMessage.includes('rejected')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Possible causes:\\n` +\n ` • Remote branch was deleted\\n` +\n ` • Push was rejected (non-fast-forward)\\n` +\n ` • Network connectivity issues\\n\\n` +\n ` To retry: il finish --pr <number>\\n` +\n ` To force push: git push origin ${branchName} --force`\n )\n }\n\n if (errorMessage.includes('Could not resolve host') || errorMessage.includes('network')) {\n throw new Error(\n `Failed to push changes to origin/${branchName}: Network connectivity issues\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Check your internet connection and try again.`\n )\n }\n\n if (errorMessage.includes('No such remote')) {\n throw new Error(\n `Failed to push changes: Remote 'origin' not found\\n\\n` +\n ` Git error: ${errorMessage}\\n\\n` +\n ` Configure remote: git remote add origin <url>`\n )\n }\n\n // For other errors, re-throw with original message\n throw new Error(`Failed to push to remote: ${errorMessage}`)\n }\n}\n\n/**\n * Check if a file is tracked by git\n * Uses git ls-files to check if file is in the index\n * @param filePath - Absolute or relative path to the file\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file is tracked, false otherwise\n */\nexport async function isFileTrackedByGit(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n const result = await executeGitCommand(\n ['ls-files', '--error-unmatch', filePath],\n { cwd }\n )\n return result.trim().length > 0\n } catch (error) {\n // Only return false if it's the specific \"pathspec did not match\" error\n const errorMessage = error instanceof Error ? error.message : String(error)\n if (errorMessage.includes('pathspec') && errorMessage.includes('did not match')) {\n return false\n }\n // Re-throw other errors\n throw error\n }\n}\n\n/**\n * Check if a file is gitignored\n * Uses `git check-ignore` which handles nested gitignore files and global patterns\n *\n * @param filePath - Path to file to check (relative to repo root)\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if file IS ignored, false if NOT ignored or on error\n */\nexport async function isFileGitignored(\n filePath: string,\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n await executeGitCommand(['check-ignore', '-q', filePath], { cwd })\n return true // Exit 0 = file IS ignored\n } catch {\n return false // Exit 1 = NOT ignored (or error)\n }\n}\n\n/**\n * Check if a branch is merged into the main branch\n *\n * Uses `git merge-base --is-ancestor` which is more reliable than `git branch -d`'s check.\n * The `-d` flag checks against current HEAD, which can give false positives when:\n * - Running from a worktree where main isn't checked out\n * - Squash or rebase merges were used\n *\n * This function explicitly checks if the branch tip is an ancestor of the main branch,\n * providing consistent results regardless of which worktree the command runs from.\n *\n * @param branchName - The branch to check\n * @param mainBranch - The main branch to check against (defaults to 'main')\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if branch is merged into main, false otherwise\n */\nexport async function isBranchMergedIntoMain(\n branchName: string,\n mainBranch: string = 'main',\n cwd: string = process.cwd()\n): Promise<boolean> {\n try {\n // git merge-base --is-ancestor exits 0 if branchName is ancestor of mainBranch, 1 if not\n await executeGitCommand(['merge-base', '--is-ancestor', branchName, mainBranch], { cwd })\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if a branch exists on the remote (origin) and is up-to-date with local\n * Useful for GitHub-PR workflows to ensure branch has been pushed and is current\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<boolean> - true if remote branch exists and matches local HEAD, false otherwise\n */\nexport async function isRemoteBranchUpToDate(\n branchName: string,\n cwd: string\n): Promise<boolean> {\n try {\n // First, check if remote branch exists and get its commit hash\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return false\n }\n\n // Extract the commit hash from ls-remote output (format: \"hash\\trefs/heads/branchname\")\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n\n // Both must exist and match\n return localCommit.trim() === remoteCommit\n } catch {\n return false\n }\n}\n\n/**\n * Result of checking remote branch status for safety validation\n */\nexport interface RemoteBranchStatus {\n /** Whether the remote branch exists */\n exists: boolean\n /** Whether the remote is ahead of local (has commits not present locally) */\n remoteAhead: boolean\n /** Whether local is ahead of remote (has unpushed commits) */\n localAhead: boolean\n /** Whether a network error occurred during the check */\n networkError: boolean\n /** Error message if network error occurred */\n errorMessage?: string\n}\n\n/**\n * Check the status of a remote branch for safety validation during cleanup\n * This function provides detailed status needed for the 5-point safety check:\n *\n * The key insight: we care about DATA LOSS, not about remote state\n * - Remote ahead of local is SAFE (commits exist on remote, no data loss)\n * - Local ahead of remote is DANGEROUS (unpushed commits would be lost)\n *\n * 5-point safety logic:\n * 1. Network error -> BLOCK (can't verify safety)\n * 2. Remote ahead of local -> OK (no data loss - commits exist on remote)\n * 3. Local ahead of remote (unpushed commits) -> BLOCK (data loss risk)\n * 4. No remote, merged to main -> OK (work is in main)\n * 5. No remote, NOT merged to main -> BLOCK (unmerged work would be lost)\n *\n * @param branchName - Name of the branch to check\n * @param cwd - Working directory to run git command in\n * @returns Promise<RemoteBranchStatus> - Detailed status of the remote branch\n */\nexport async function checkRemoteBranchStatus(\n branchName: string,\n cwd: string\n): Promise<RemoteBranchStatus> {\n try {\n // First, fetch to ensure we have the latest remote refs\n // This is important to accurately detect if remote is ahead\n try {\n await executeGitCommand(['fetch', 'origin', branchName], { cwd, timeout: 30000 })\n } catch (fetchError) {\n // Fetch failing for a specific branch is OK - branch might not exist on remote\n // We'll detect this in the ls-remote call\n const fetchErrorMessage = fetchError instanceof Error ? fetchError.message : String(fetchError)\n\n // Check if this is a network error vs branch not found\n if (fetchErrorMessage.includes('Could not resolve host') ||\n fetchErrorMessage.includes('unable to access') ||\n fetchErrorMessage.includes('network') ||\n fetchErrorMessage.includes('Connection refused') ||\n fetchErrorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage: fetchErrorMessage\n }\n }\n // Otherwise continue - branch might just not exist on remote\n }\n\n // Check if remote branch exists using ls-remote\n const remoteResult = await executeGitCommand(['ls-remote', '--heads', 'origin', branchName], { cwd })\n\n if (remoteResult.trim().length === 0) {\n // Remote branch doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Remote branch exists - check if it's ahead of local\n // Extract the remote commit hash\n const remoteCommit = remoteResult.trim().split('\\t')[0]\n\n // Guard against undefined (shouldn't happen but TypeScript wants it)\n if (!remoteCommit) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Get the local branch's HEAD commit\n const localCommit = await executeGitCommand(['rev-parse', branchName], { cwd })\n const localCommitTrimmed = localCommit.trim()\n\n if (remoteCommit === localCommitTrimmed) {\n // Remote and local are at the same commit - safe (no unpushed commits)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n\n // Commits differ - check if remote is ahead of local\n // Use merge-base to find common ancestor, then compare\n try {\n // Check if localCommit is an ancestor of remoteCommit (meaning remote is ahead)\n await executeGitCommand(['merge-base', '--is-ancestor', localCommitTrimmed, remoteCommit], { cwd })\n // If we get here, local IS an ancestor of remote, meaning remote is ahead\n // This is SAFE - no data loss because commits exist on remote\n return {\n exists: true,\n remoteAhead: true,\n localAhead: false,\n networkError: false\n }\n } catch {\n // Local is NOT an ancestor of remote\n // This means local is ahead or branches have diverged\n // Either way, local has unpushed commits - this is DANGEROUS (data loss risk)\n return {\n exists: true,\n remoteAhead: false,\n localAhead: true,\n networkError: false\n }\n }\n } catch (error) {\n // Check if this is a network error\n const errorMessage = error instanceof Error ? error.message : String(error)\n\n if (errorMessage.includes('Could not resolve host') ||\n errorMessage.includes('unable to access') ||\n errorMessage.includes('network') ||\n errorMessage.includes('Connection refused') ||\n errorMessage.includes('Connection timed out')) {\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: true,\n errorMessage\n }\n }\n\n // For other errors, assume remote doesn't exist\n return {\n exists: false,\n remoteAhead: false,\n localAhead: false,\n networkError: false\n }\n }\n}\n\n/**\n * Get the merge target branch for a loom\n * Priority: parent loom metadata (parentLoom.branchName) > configured main branch > 'main'\n *\n * This is the shared utility for determining where a branch should merge to.\n * Child looms merge to their parent branch, standalone looms merge to main.\n *\n * @param worktreePath - Path to load metadata/settings from (defaults to process.cwd())\n * @param options - Optional dependency injection for testing\n * @returns The branch name to merge into\n */\nexport async function getMergeTargetBranch(\n worktreePath: string = process.cwd(),\n options?: {\n settingsManager?: SettingsManagerType\n metadataManager?: MetadataManager\n }\n): Promise<string> {\n const settingsManager = options?.settingsManager ?? new SettingsManager()\n const metadataManager = options?.metadataManager ?? new MetadataManager()\n\n // Check for parent loom metadata first (child looms merge to parent)\n logger.debug(`Checking for parent loom metadata at: ${worktreePath}`)\n const metadata = await metadataManager.readMetadata(worktreePath)\n if (metadata?.parentLoom?.branchName) {\n logger.debug(`Using parent branch as merge target: ${metadata.parentLoom.branchName}`)\n return metadata.parentLoom.branchName\n }\n logger.debug('No parent loom metadata found, falling back to settings')\n\n // Fall back to configured main branch\n const settings = await settingsManager.loadSettings(worktreePath)\n const mainBranch = settings.mainBranch ?? 'main'\n logger.debug(`Using configured main branch as merge target: ${mainBranch}`)\n return mainBranch\n}\n\n/**\n * Placeholder commit prefix used by github-draft-pr mode.\n * Created during il start to enable draft PR creation (GitHub requires at least one commit ahead of base).\n * Removed during il finish before the final push to maintain clean commit history.\n */\nexport const PLACEHOLDER_COMMIT_PREFIX = '[iloom-temp]'\n\n/**\n * Check if HEAD commit is a placeholder commit\n * @param cwd - Working directory (defaults to process.cwd())\n * @returns true if HEAD is a placeholder commit\n */\nexport async function isPlaceholderCommit(cwd: string = process.cwd()): Promise<boolean> {\n try {\n const subject = await executeGitCommand(['log', '-1', '--format=%s', 'HEAD'], { cwd })\n return subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)\n } catch {\n // No HEAD (empty repo) or other error - not a placeholder\n return false\n }\n}\n\n/**\n * Find placeholder commit SHA in history using git log --grep\n * @param worktreePath - Working directory\n * @returns SHA of placeholder commit if found, null otherwise\n */\nexport async function findPlaceholderCommitSha(worktreePath: string): Promise<string | null> {\n try {\n // Search commit history for placeholder prefix\n // Use --fixed-strings to treat the pattern literally (brackets are regex special chars)\n const log = await executeGitCommand(\n ['log', '--format=%H', '--fixed-strings', '--grep', PLACEHOLDER_COMMIT_PREFIX, '-n', '1'],\n { cwd: worktreePath }\n )\n const sha = log.trim()\n if (sha.length === 0) {\n return null\n }\n\n // Verify the found commit actually has the placeholder prefix in its subject\n // This guards against git grep matching in commit body instead of subject\n const subject = await executeGitCommand(\n ['log', '-1', '--format=%s', sha],\n { cwd: worktreePath }\n )\n if (!subject.trim().startsWith(PLACEHOLDER_COMMIT_PREFIX)) {\n return null\n }\n\n return sha\n } catch {\n return null\n }\n}\n\n/**\n * Remove placeholder commit when it's HEAD\n * Uses soft reset to preserve any staged changes\n * @param worktreePath - Working directory\n * @returns true if placeholder was removed\n */\nexport async function removePlaceholderCommitFromHead(worktreePath: string): Promise<boolean> {\n if (!await isPlaceholderCommit(worktreePath)) {\n return false\n }\n await executeGitCommand(['reset', '--soft', 'HEAD~1'], { cwd: worktreePath })\n return true\n}\n\n/**\n * Remove placeholder commit from history using rebase\n * Used when user has made commits on top of placeholder\n * @param worktreePath - Working directory\n * @param placeholderSha - SHA of the placeholder commit to remove\n * @throws Error if rebase fails\n */\nexport async function removePlaceholderCommitFromHistory(\n worktreePath: string,\n placeholderSha: string\n): Promise<void> {\n // Get parent of placeholder commit\n const parentSha = await executeGitCommand(\n ['rev-parse', `${placeholderSha}^`],\n { cwd: worktreePath }\n )\n\n // Rebase to drop the placeholder: rebase --onto parent^ placeholder\n await executeGitCommand(\n ['rebase', '--onto', parentSha.trim(), placeholderSha],\n { cwd: worktreePath }\n )\n}\n"],"mappings":"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","names":["path"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/LinearService.ts","../src/lib/IssueTrackerFactory.ts","../src/utils/FirstRunManager.ts","../src/utils/mcp.ts"],"sourcesContent":["/**\n * LinearService - IssueTracker implementation for Linear\n * Implements issue tracking operations using the @linear/sdk\n */\n\nimport type { Issue, PullRequest, IssueTrackerInputDetection } from '../types/index.js'\nimport type { LinearIssue } from '../types/linear.js'\nimport { LinearServiceError } from '../types/linear.js'\nimport {\n fetchLinearIssue,\n createLinearIssue,\n updateLinearIssueState,\n} from '../utils/linear.js'\nimport { promptConfirmation } from '../utils/prompt.js'\nimport type { IssueTracker } from './IssueTracker.js'\nimport { logger } from '../utils/logger.js'\n\n/**\n * Linear service configuration options\n */\nexport interface LinearServiceConfig {\n /** Linear team key (e.g., \"ENG\", \"PLAT\") */\n teamId?: string\n /** Branch naming template (e.g., \"feat/{{key}}__{{title}}\") */\n branchFormat?: string\n /** Linear API token (lin_api_...). If provided, sets process.env.LINEAR_API_TOKEN */\n apiToken?: string\n}\n\n/**\n * Linear implementation of IssueTracker interface\n */\nexport class LinearService implements IssueTracker {\n // IssueTracker interface implementation\n readonly providerName = 'linear'\n readonly supportsPullRequests = false // Linear doesn't have pull requests\n\n private config: LinearServiceConfig\n private prompter: (message: string) => Promise<boolean>\n\n constructor(\n config?: LinearServiceConfig,\n options?: { prompter?: (message: string) => Promise<boolean> },\n ) {\n this.config = config ?? {}\n this.prompter = options?.prompter ?? promptConfirmation\n\n // Set API token from config if provided (follows mcp.ts pattern)\n if (this.config.apiToken) {\n process.env.LINEAR_API_TOKEN = this.config.apiToken\n }\n }\n\n /**\n * Detect if input matches Linear identifier format (TEAM-NUMBER)\n * @param input - User input string\n * @param _repo - Repository (unused for Linear)\n * @returns Detection result with type and identifier\n */\n public async detectInputType(\n input: string,\n _repo?: string,\n ): Promise<IssueTrackerInputDetection> {\n logger.debug(`LinearService.detectInputType called with input: \"${input}\"`)\n\n // Pattern: TEAM-NUMBER (e.g., ENG-123, PLAT-456)\n // Requires at least 2 letters before dash to avoid conflict with PR-123 format\n const linearPattern = /^([A-Z]{2,}-\\d+)$/i\n const match = input.match(linearPattern)\n\n if (!match?.[1]) {\n logger.debug(`LinearService: Input \"${input}\" does not match Linear pattern`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n const identifier = match[1].toUpperCase()\n logger.debug(`LinearService: Matched Linear identifier: ${identifier}`)\n\n // Validate the issue exists in Linear\n logger.debug(`LinearService: Checking if ${identifier} is a valid Linear issue via SDK`)\n const issue = await this.isValidIssue(identifier)\n\n if (issue) {\n logger.debug(`LinearService: Issue ${identifier} found: \"${issue.title}\"`)\n return { type: 'issue', identifier, rawInput: input }\n }\n\n // Not found\n logger.debug(`LinearService: Issue ${identifier} NOT found by SDK`)\n return { type: 'unknown', identifier: null, rawInput: input }\n }\n\n /**\n * Fetch a Linear issue by identifier\n * @param identifier - Linear issue identifier (string or number)\n * @param _repo - Repository (unused for Linear)\n * @returns Generic Issue type\n * @throws LinearServiceError if issue not found\n */\n public async fetchIssue(identifier: string | number, _repo?: string): Promise<Issue> {\n const linearIssue = await fetchLinearIssue(String(identifier))\n return this.mapLinearIssueToIssue(linearIssue)\n }\n\n /**\n * Check if an issue identifier is valid (silent validation)\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue if valid, false if not found\n */\n public async isValidIssue(identifier: string | number, _repo?: string): Promise<Issue | false> {\n try {\n return await this.fetchIssue(identifier)\n } catch (error) {\n // Return false for NOT_FOUND errors (expected during detection)\n if (error instanceof LinearServiceError && error.code === 'NOT_FOUND') {\n return false\n }\n // Re-throw unexpected errors\n throw error\n }\n }\n\n /**\n * Validate issue state and prompt user if closed\n * @param issue - Issue to validate\n * @throws LinearServiceError if user cancels due to closed issue\n */\n public async validateIssueState(issue: Issue): Promise<void> {\n if (issue.state === 'closed') {\n const shouldContinue = await this.prompter(\n `Issue ${issue.number} is closed. Continue anyway?`,\n )\n\n if (!shouldContinue) {\n throw new LinearServiceError('INVALID_STATE', 'User cancelled due to closed issue')\n }\n }\n }\n\n /**\n * Create a new Linear issue\n * @param title - Issue title\n * @param body - Issue description (markdown)\n * @param _repository - Repository (unused for Linear)\n * @param labels - Optional label names\n * @returns Created issue identifier and URL\n * @throws LinearServiceError if teamId not configured or creation fails\n */\n public async createIssue(\n title: string,\n body: string,\n _repository?: string,\n labels?: string[],\n ): Promise<{ number: string | number; url: string }> {\n // Require teamId configuration\n if (!this.config.teamId) {\n throw new LinearServiceError(\n 'INVALID_STATE',\n 'Linear teamId not configured. Run `il init` to configure Linear settings.',\n )\n }\n\n logger.info(`Creating Linear issue in team ${this.config.teamId}: ${title}`)\n\n const result = await createLinearIssue(title, body, this.config.teamId, labels)\n\n return {\n number: result.identifier,\n url: result.url,\n }\n }\n\n /**\n * Get the web URL for a Linear issue\n * @param identifier - Linear issue identifier\n * @param _repo - Repository (unused for Linear)\n * @returns Issue URL\n */\n public async getIssueUrl(identifier: string | number, _repo?: string): Promise<string> {\n const issue = await this.fetchIssue(identifier)\n return issue.url\n }\n\n /**\n * Move a Linear issue to \"In Progress\" state\n * @param identifier - Linear issue identifier\n * @throws LinearServiceError if state update fails\n */\n public async moveIssueToInProgress(identifier: string | number): Promise<void> {\n logger.info(`Moving Linear issue ${identifier} to In Progress`)\n await updateLinearIssueState(String(identifier), 'In Progress')\n }\n\n /**\n * Extract issue context for AI prompts\n * @param entity - Issue (Linear doesn't have PRs)\n * @returns Formatted context string\n */\n public extractContext(entity: Issue | PullRequest): string {\n // Linear doesn't have PRs, always an issue\n const issue = entity as Issue\n return `Linear Issue ${issue.number}: ${issue.title}\\nState: ${issue.state}\\n\\n${issue.body}`\n }\n\n /**\n * Map Linear API issue to generic Issue type\n * @param linear - Linear issue from SDK\n * @returns Generic Issue type\n */\n private mapLinearIssueToIssue(linear: LinearIssue): Issue {\n return {\n number: linear.identifier, // Keep as string (e.g., \"ENG-123\")\n title: linear.title,\n body: linear.description ?? '',\n state: linear.state ? (linear.state.toLowerCase().includes('done') || linear.state.toLowerCase().includes('completed') || linear.state.toLowerCase().includes('canceled') ? 'closed' : 'open') : 'open',\n labels: [],\n assignees: [],\n url: linear.url,\n }\n }\n}\n","// IssueTrackerFactory - creates appropriate IssueTracker based on settings\n// Follows pattern from database provider instantiation\n\nimport type { IssueTracker } from './IssueTracker.js'\nimport { GitHubService } from './GitHubService.js'\nimport { LinearService, type LinearServiceConfig } from './LinearService.js'\nimport type { IloomSettings } from './SettingsManager.js'\nimport { getLogger } from '../utils/logger-context.js'\n\nexport type IssueTrackerProviderType = 'github' | 'linear'\n\n/**\n * Factory for creating IssueTracker instances based on settings\n * Provides a single point of provider instantiation\n *\n * Usage:\n * const tracker = IssueTrackerFactory.create(settings, { useClaude: true })\n * const issue = await tracker.fetchIssue(123)\n */\nexport class IssueTrackerFactory {\n\t/**\n\t * Create an IssueTracker instance based on settings configuration\n\t * Defaults to GitHub if no provider specified\n\t *\n\t * @param settings - iloom settings containing issueManagement.provider\n\t * @returns IssueTracker instance configured for the specified provider\n\t * @throws Error if provider type is not supported\n\t */\n\tstatic create(settings: IloomSettings): IssueTracker {\n\t\tconst provider = settings.issueManagement?.provider ?? 'github'\n\n\t\tgetLogger().debug(`IssueTrackerFactory: Creating tracker for provider \"${provider}\"`)\n\t\tgetLogger().debug(`IssueTrackerFactory: issueManagement settings:`, JSON.stringify(settings.issueManagement, null, 2))\n\n\t\tswitch (provider) {\n\t\t\tcase 'github':\n\t\t\t\tgetLogger().debug('IssueTrackerFactory: Creating GitHubService')\n\t\t\t\treturn new GitHubService()\n\t\t\tcase 'linear': {\n\t\t\t\tconst linearSettings = settings.issueManagement?.linear\n\t\t\t\tconst linearConfig: LinearServiceConfig = {}\n\n\t\t\t\tif (linearSettings?.teamId) {\n\t\t\t\t\tlinearConfig.teamId = linearSettings.teamId\n\t\t\t\t}\n\t\t\t\tif (linearSettings?.branchFormat) {\n\t\t\t\t\tlinearConfig.branchFormat = linearSettings.branchFormat\n\t\t\t\t}\n\t\t\t\tif (linearSettings?.apiToken) {\n\t\t\t\t\tlinearConfig.apiToken = linearSettings.apiToken\n\t\t\t\t}\n\n\t\t\t\tgetLogger().debug(`IssueTrackerFactory: Creating LinearService with config:`, JSON.stringify(linearConfig, null, 2))\n\t\t\t\treturn new LinearService(linearConfig)\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`Unsupported issue tracker provider: ${provider}`)\n\t\t}\n\t}\n\n\t/**\n\t * Get the configured provider name from settings\n\t * Defaults to 'github' if not configured\n\t *\n\t * @param settings - iloom settings\n\t * @returns Provider type string\n\t */\n\tstatic getProviderName(settings: IloomSettings): IssueTrackerProviderType {\n\t\treturn (settings.issueManagement?.provider ?? 'github') as IssueTrackerProviderType\n\t}\n}\n","import os from 'os'\nimport path from 'path'\nimport fs from 'fs-extra'\nimport { logger } from './logger.js'\n\n/**\n * FirstRunManager: Detect and track first-time spin usage via marker file\n *\n * Follows the same pattern as UpdateNotifier for file-based state tracking\n * in ~/.config/iloom-ai/ directory.\n *\n * Also supports project-level tracking for config wizard completion using\n * individual marker files per project in ~/.config/iloom-ai/projects/\n */\nexport class FirstRunManager {\n\tprivate markerFilePath: string\n\tprivate configDir: string\n\n\tconstructor(feature: string = 'spin') {\n\t\tthis.configDir = path.join(os.homedir(), '.config', 'iloom-ai')\n\t\tthis.markerFilePath = path.join(this.configDir, `${feature}-first-run`)\n\t\tlogger.debug('FirstRunManager initialized', {\n\t\t\tfeature,\n\t\t\tmarkerFilePath: this.markerFilePath\n\t\t})\n\t}\n\n\t/**\n\t * Get the directory for project marker files\n\t */\n\tprivate getProjectsDir(): string {\n\t\treturn path.join(this.configDir, 'projects')\n\t}\n\n\t/**\n\t * Resolve symlinks in project path to get canonical path\n\t * Falls back to original path on errors (broken symlinks, permissions, etc.)\n\t */\n\tprivate async resolveProjectPath(projectPath: string): Promise<string> {\n\t\ttry {\n\t\t\treturn await fs.realpath(projectPath)\n\t\t} catch {\n\t\t\tlogger.debug('resolveProjectPath: Failed to resolve symlink, using original path', { projectPath })\n\t\t\treturn projectPath\n\t\t}\n\t}\n\n\t/**\n\t * Convert a project path to a readable filename\n\t * /Users/adam/Projects/my-app -> Users__adam__Projects__my-app\n\t */\n\tprivate projectPathToFileName(projectPath: string): string {\n\t\tconst normalized = path.normalize(projectPath)\n\t\treturn normalized.replace(/^[/\\\\]+/, '').replace(/[/\\\\]+/g, '__')\n\t}\n\n\t/**\n\t * Get full path to a project's marker file\n\t */\n\tprivate getProjectMarkerPath(projectPath: string): string {\n\t\treturn path.join(this.getProjectsDir(), this.projectPathToFileName(projectPath))\n\t}\n\n\t/**\n\t * Extract project name from path\n\t */\n\tprivate getProjectName(projectPath: string): string {\n\t\treturn path.basename(projectPath)\n\t}\n\n\t/**\n\t * Check if a project has been configured\n\t * Returns true if project marker file exists\n\t */\n\tasync isProjectConfigured(projectPath?: string): Promise<boolean> {\n\t\tconst inputPath = projectPath ?? process.cwd()\n\t\tconst resolvedPath = await this.resolveProjectPath(inputPath)\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\t\tlogger.debug('isProjectConfigured: Checking for marker file', { markerPath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(markerPath)\n\t\t\tlogger.debug(`isProjectConfigured: Marker file exists=${exists}`)\n\t\t\treturn exists\n\t\t} catch (error) {\n\t\t\t// On error, treat as not configured to allow wizard to run\n\t\t\tlogger.debug(`isProjectConfigured: Error checking marker file, treating as not configured: ${error}`)\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Check if a project has local .iloom settings files\n\t * Returns true if .iloom/settings.json or .iloom/settings.local.json exists with content\n\t */\n\tprivate async hasLocalSettings(projectPath: string): Promise<boolean> {\n\t\tconst iloomDir = path.join(projectPath, '.iloom')\n\t\tconst settingsPath = path.join(iloomDir, 'settings.json')\n\t\tconst settingsLocalPath = path.join(iloomDir, 'settings.local.json')\n\n\t\tconst hasSettings = await this.hasNonEmptySettingsFile(settingsPath)\n\t\tconst hasLocalSettings = await this.hasNonEmptySettingsFile(settingsLocalPath)\n\n\t\treturn hasSettings || hasLocalSettings\n\t}\n\n\t/**\n\t * Check if a settings file exists and has non-empty content\n\t */\n\tprivate async hasNonEmptySettingsFile(filePath: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(filePath)\n\t\t\tif (!exists) return false\n\t\t\tconst content = await fs.readFile(filePath, 'utf8')\n\t\t\tconst parsed = JSON.parse(content)\n\t\t\treturn Object.keys(parsed).length > 0\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Fixup legacy projects that have local config but lack the global marker file\n\t * This handles projects configured before global project tracking was implemented\n\t *\n\t * Returns object with:\n\t * - isConfigured: true if project is configured (either already had marker or fixup created one)\n\t * - wasFixedUp: true if fixup was performed (marker was created by this call)\n\t *\n\t * This combined return avoids duplicate isProjectConfigured() calls in needsFirstRunSetup()\n\t */\n\tasync fixupLegacyProject(projectPath?: string): Promise<{ isConfigured: boolean; wasFixedUp: boolean }> {\n\t\tconst inputPath = projectPath ?? process.cwd()\n\t\tconst resolvedPath = await this.resolveProjectPath(inputPath)\n\t\tlogger.debug('fixupLegacyProject: Checking for legacy project', { projectPath: resolvedPath })\n\n\t\t// Check if already has global marker - no fixup needed, but project IS configured\n\t\tconst hasMarker = await this.isProjectConfigured(resolvedPath)\n\t\tif (hasMarker) {\n\t\t\tlogger.debug('fixupLegacyProject: Project already has global marker')\n\t\t\treturn { isConfigured: true, wasFixedUp: false }\n\t\t}\n\n\t\t// Check if has local settings files - this indicates a legacy configured project\n\t\tconst hasLocal = await this.hasLocalSettings(resolvedPath)\n\t\tif (!hasLocal) {\n\t\t\tlogger.debug('fixupLegacyProject: No local settings found, not a legacy project')\n\t\t\treturn { isConfigured: false, wasFixedUp: false }\n\t\t}\n\n\t\t// Legacy project found - create the missing global marker\n\t\tlogger.debug('fixupLegacyProject: Legacy project detected, creating global marker')\n\t\tawait this.markProjectAsConfigured(resolvedPath)\n\t\treturn { isConfigured: true, wasFixedUp: true }\n\t}\n\n\t/**\n\t * Mark a project as configured\n\t * Creates a marker file with project metadata\n\t */\n\tasync markProjectAsConfigured(projectPath?: string): Promise<void> {\n\t\tconst inputPath = projectPath ?? process.cwd()\n\t\tconst resolvedPath = await this.resolveProjectPath(inputPath)\n\t\tconst markerPath = this.getProjectMarkerPath(resolvedPath)\n\t\tlogger.debug('markProjectAsConfigured: Creating marker file', { markerPath })\n\t\ttry {\n\t\t\tawait fs.ensureDir(this.getProjectsDir())\n\t\t\tconst markerContent = {\n\t\t\t\tconfiguredAt: new Date().toISOString(),\n\t\t\t\tprojectPath: resolvedPath,\n\t\t\t\tprojectName: this.getProjectName(resolvedPath)\n\t\t\t}\n\t\t\tawait fs.writeFile(markerPath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markProjectAsConfigured: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\tlogger.debug(`markProjectAsConfigured: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n\n\t/**\n\t * Check if this is the first run of the feature\n\t * Returns true if marker file doesn't exist\n\t * Handles errors gracefully by returning true (treat as first-run on error)\n\t */\n\tasync isFirstRun(): Promise<boolean> {\n\t\tlogger.debug('isFirstRun: Checking for marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\tconst exists = await fs.pathExists(this.markerFilePath)\n\t\t\tlogger.debug(`isFirstRun: Marker file exists=${exists}`)\n\t\t\treturn !exists\n\t\t} catch (error) {\n\t\t\t// On error, gracefully degrade by treating as first-run\n\t\t\tlogger.debug(`isFirstRun: Error checking marker file, treating as first-run: ${error}`)\n\t\t\treturn true\n\t\t}\n\t}\n\n\t/**\n\t * Mark the feature as having been run\n\t * Creates the marker file in config directory\n\t * Handles errors gracefully without throwing\n\t */\n\tasync markAsRun(): Promise<void> {\n\t\tlogger.debug('markAsRun: Attempting to create marker file', { markerFilePath: this.markerFilePath })\n\t\ttry {\n\t\t\t// Ensure directory exists\n\t\t\tconst configDir = path.dirname(this.markerFilePath)\n\t\t\tlogger.debug(`markAsRun: Ensuring config directory exists: ${configDir}`)\n\t\t\tawait fs.ensureDir(configDir)\n\n\t\t\t// Write marker file with timestamp for debugging\n\t\t\tconst markerContent = {\n\t\t\t\tfirstRun: new Date().toISOString(),\n\t\t\t}\n\t\t\tawait fs.writeFile(this.markerFilePath, JSON.stringify(markerContent, null, 2), 'utf8')\n\t\t\tlogger.debug('markAsRun: Marker file created successfully')\n\t\t} catch (error) {\n\t\t\t// Don't throw on errors - just log debug message\n\t\t\t// Failing to write marker shouldn't break the workflow\n\t\t\tlogger.debug(`markAsRun: Failed to create marker file: ${error}`)\n\t\t}\n\t}\n}\n","import path from 'path'\nimport os from 'os'\nimport { getRepoInfo } from './github.js'\nimport { logger } from './logger.js'\nimport type { IloomSettings } from '../lib/SettingsManager.js'\nimport type { LoomMetadata } from '../lib/MetadataManager.js'\n\n/**\n * Generate MCP configuration for issue management\n * Uses a single server that can handle both issues and pull requests\n * Returns array of MCP server config objects\n * @param contextType - Optional context type (issue or pr)\n * @param repo - Optional repo in \"owner/repo\" format. If not provided, will auto-detect from git.\n * @param provider - Issue management provider (default: 'github')\n * @param settings - Optional settings to extract Linear API token from\n * @param draftPrNumber - Optional draft PR number for github-draft-pr mode (routes comments to PR)\n */\nexport async function generateIssueManagementMcpConfig(\n\tcontextType?: 'issue' | 'pr',\n\trepo?: string,\n\tprovider: 'github' | 'linear' = 'github',\n\tsettings?: IloomSettings,\n\tdraftPrNumber?: number\n): Promise<Record<string, unknown>[]> {\n\t// When draftPrNumber is provided (github-draft-pr mode), force contextType to 'pr'\n\t// This ensures agents route comments to the draft PR instead of the issue\n\tconst effectiveContextType = draftPrNumber ? 'pr' : contextType\n\n\t// Build provider-specific environment variables\n\tlet envVars: Record<string, string> = {\n\t\tISSUE_PROVIDER: provider,\n\t}\n\n\t// Add draft PR number to env vars if provided\n\tif (draftPrNumber) {\n\t\tenvVars.DRAFT_PR_NUMBER = String(draftPrNumber)\n\t}\n\n\tif (provider === 'github') {\n\t\t// Get repository information for GitHub - either from provided repo string or auto-detect\n\t\tlet owner: string\n\t\tlet name: string\n\n\t\tif (repo) {\n\t\t\tconst parts = repo.split('/')\n\t\t\tif (parts.length !== 2 || !parts[0] || !parts[1]) {\n\t\t\t\tthrow new Error(`Invalid repo format: ${repo}. Expected \"owner/repo\"`)\n\t\t\t}\n\t\t\towner = parts[0]\n\t\t\tname = parts[1]\n\t\t} else {\n\t\t\tconst repoInfo = await getRepoInfo()\n\t\t\towner = repoInfo.owner\n\t\t\tname = repoInfo.name\n\t\t}\n\n\t\t// Map logical types to GitHub's webhook event names (handle GitHub's naming quirk here)\n\t\t// Use effectiveContextType which may be overridden by draftPrNumber\n\t\tconst githubEventName = effectiveContextType === 'issue' ? 'issues' : effectiveContextType === 'pr' ? 'pull_request' : undefined\n\n\t\tenvVars = {\n\t\t\t...envVars,\n\t\t\tREPO_OWNER: owner,\n\t\t\tREPO_NAME: name,\n\t\t\tGITHUB_API_URL: 'https://api.github.com/',\n\t\t\t...(githubEventName && { GITHUB_EVENT_NAME: githubEventName }),\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for GitHub issue management', {\n\t\t\tprovider,\n\t\t\trepoOwner: owner,\n\t\t\trepoName: name,\n\t\t\tcontextType: effectiveContextType ?? 'auto-detect',\n\t\t\tgithubEventName: githubEventName ?? 'auto-detect',\n\t\t\tdraftPrNumber: draftPrNumber ?? undefined,\n\t\t})\n\t} else {\n\t\t// Linear needs API token passed through\n\t\tconst apiToken = settings?.issueManagement?.linear?.apiToken ?? process.env.LINEAR_API_TOKEN\n\n\t\tif (apiToken) {\n\t\t\tenvVars.LINEAR_API_TOKEN = apiToken\n\t\t}\n\n\t\tlogger.debug('Generated MCP config for Linear issue management', {\n\t\t\tprovider,\n\t\t\thasApiToken: !!apiToken,\n\t\t\tcontextType: contextType ?? 'auto-detect',\n\t\t})\n\t}\n\n\t// Generate single MCP server config\n\tconst mcpServerConfig = {\n\t\tmcpServers: {\n\t\t\tissue_management: {\n\t\t\t\ttransport: 'stdio',\n\t\t\t\tcommand: 'node',\n\t\t\t\targs: [path.join(path.dirname(new globalThis.URL(import.meta.url).pathname), '../dist/mcp/issue-management-server.js')],\n\t\t\t\tenv: envVars,\n\t\t\t},\n\t\t},\n\t}\n\n\treturn [mcpServerConfig]\n}\n\n/**\n * Reuse MetadataManager.slugifyPath() algorithm for recap file naming\n *\n * Algorithm:\n * 1. Trim trailing slashes\n * 2. Replace all path separators (/ or \\) with ___ (triple underscore)\n * 3. Replace any other non-alphanumeric characters (except _ and -) with -\n * 4. Append .json\n */\nfunction slugifyPath(loomPath: string): string {\n\tlet slug = loomPath.replace(/[/\\\\]+$/, '')\n\tslug = slug.replace(/[/\\\\]/g, '___')\n\tslug = slug.replace(/[^a-zA-Z0-9_-]/g, '-')\n\treturn `${slug}.json`\n}\n\n/**\n * Generate MCP configuration for recap server\n *\n * The recap server captures session context (goal, decisions, insights, risks, assumptions)\n * for the VS Code Loom Context Panel.\n *\n * @param loomPath - Absolute path to the loom workspace\n * @param loomMetadata - The loom metadata object (will be stringified as JSON)\n */\nexport function generateRecapMcpConfig(\n\tloomPath: string,\n\tloomMetadata: LoomMetadata\n): Record<string, unknown>[] {\n\t// Compute recap file path using slugifyPath algorithm (same as MetadataManager)\n\tconst recapsDir = path.join(os.homedir(), '.config', 'iloom-ai', 'recaps')\n\tconst recapFilePath = path.join(recapsDir, slugifyPath(loomPath))\n\n\t// Pass both env vars:\n\t// - RECAP_FILE_PATH: where to read/write recap data\n\t// - LOOM_METADATA_JSON: stringified loom metadata (parsed by MCP using LoomMetadata type)\n\tconst envVars = {\n\t\tRECAP_FILE_PATH: recapFilePath,\n\t\tLOOM_METADATA_JSON: JSON.stringify(loomMetadata),\n\t}\n\n\tlogger.debug('Generated MCP config for recap server', {\n\t\tloomPath,\n\t\trecapFilePath,\n\t\tloomMetadataDescription: loomMetadata.description,\n\t})\n\n\treturn [\n\t\t{\n\t\t\tmcpServers: {\n\t\t\t\trecap: {\n\t\t\t\t\ttransport: 'stdio',\n\t\t\t\t\tcommand: 'node',\n\t\t\t\t\targs: [\n\t\t\t\t\t\tpath.join(\n\t\t\t\t\t\t\tpath.dirname(new globalThis.URL(import.meta.url).pathname),\n\t\t\t\t\t\t\t'../dist/mcp/recap-server.js'\n\t\t\t\t\t\t),\n\t\t\t\t\t],\n\t\t\t\t\tenv: envVars,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t]\n}"],"mappings":"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","names":["path","os","path","os"]}