@iloom/cli 0.3.4 → 0.4.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.
- package/README.md +13 -3
- package/dist/{BranchNamingService-A77VI6AI.js → BranchNamingService-GCCWB3LK.js} +4 -3
- package/dist/ClaudeContextManager-DK77227F.js +16 -0
- package/dist/ClaudeService-W3SA7HVG.js +15 -0
- package/dist/GitHubService-RPM27GWD.js +12 -0
- package/dist/{LoomLauncher-ZV3ZZIBA.js → LoomLauncher-S3YGJRJQ.js} +43 -27
- package/dist/LoomLauncher-S3YGJRJQ.js.map +1 -0
- package/dist/PromptTemplateManager-2TDZAUC6.js +9 -0
- package/dist/README.md +13 -3
- package/dist/{SettingsManager-I2LRCW2A.js → SettingsManager-FJFU6JJD.js} +7 -3
- package/dist/SettingsMigrationManager-EH3J2TCN.js +10 -0
- package/dist/{chunk-5Q3NDNNV.js → chunk-2W2FBL5G.js} +153 -6
- package/dist/chunk-2W2FBL5G.js.map +1 -0
- package/dist/{chunk-OXAM2WVC.js → chunk-55TB3FSG.js} +21 -1
- package/dist/chunk-55TB3FSG.js.map +1 -0
- package/dist/chunk-6UIGZD2N.js +20 -0
- package/dist/chunk-6UIGZD2N.js.map +1 -0
- package/dist/{chunk-RIEO2WML.js → chunk-74VMN2KC.js} +26 -2
- package/dist/chunk-74VMN2KC.js.map +1 -0
- package/dist/{chunk-2MAIX45J.js → chunk-BIIQHEXJ.js} +104 -43
- package/dist/chunk-BIIQHEXJ.js.map +1 -0
- package/dist/{chunk-UAN4A3YU.js → chunk-G6CIIJLT.js} +11 -11
- package/dist/{chunk-DLHA5VQ3.js → chunk-HD5SUKI2.js} +36 -179
- package/dist/chunk-HD5SUKI2.js.map +1 -0
- package/dist/{chunk-2IJEMXOB.js → chunk-IARWMDAX.js} +427 -428
- package/dist/chunk-IARWMDAX.js.map +1 -0
- package/dist/chunk-IJ7IGJT3.js +192 -0
- package/dist/chunk-IJ7IGJT3.js.map +1 -0
- package/dist/{chunk-2CXREBLZ.js → chunk-JC5HXN75.js} +8 -6
- package/dist/chunk-JC5HXN75.js.map +1 -0
- package/dist/{chunk-3RUPPQRG.js → chunk-KO2FOMHL.js} +43 -2
- package/dist/{chunk-3RUPPQRG.js.map → chunk-KO2FOMHL.js.map} +1 -1
- package/dist/{chunk-4XIDC3NF.js → chunk-MD6HA5IK.js} +2 -2
- package/dist/{chunk-OC4H6HJD.js → chunk-O7WHXLCB.js} +2 -2
- package/dist/{chunk-M7JJCX53.js → chunk-OEGECBFS.js} +20 -20
- package/dist/chunk-OEGECBFS.js.map +1 -0
- package/dist/{chunk-MKWYLDFK.js → chunk-OF7BNW4D.js} +43 -3
- package/dist/chunk-OF7BNW4D.js.map +1 -0
- package/dist/{chunk-PGPI5LR4.js → chunk-POI7KLBH.js} +7 -21
- package/dist/chunk-POI7KLBH.js.map +1 -0
- package/dist/{chunk-PA6Q6AWM.js → chunk-PSFVTBM7.js} +2 -2
- package/dist/chunk-QHA67Q7A.js +281 -0
- package/dist/chunk-QHA67Q7A.js.map +1 -0
- package/dist/{chunk-SUOXY5WJ.js → chunk-QIUJPPJQ.js} +5 -5
- package/dist/chunk-QIUJPPJQ.js.map +1 -0
- package/dist/{chunk-ZM3CFL5L.js → chunk-QRBOPFAA.js} +3 -3
- package/dist/{chunk-OYF4VIFI.js → chunk-RUC7OULH.js} +147 -22
- package/dist/chunk-RUC7OULH.js.map +1 -0
- package/dist/{chunk-CE26YH2U.js → chunk-SJ2GZ6RF.js} +48 -50
- package/dist/chunk-SJ2GZ6RF.js.map +1 -0
- package/dist/{chunk-SSCQCCJ7.js → chunk-THF25ICZ.js} +2 -2
- package/dist/chunk-TMZAVPGF.js +667 -0
- package/dist/chunk-TMZAVPGF.js.map +1 -0
- package/dist/{chunk-5VK4NRSF.js → chunk-UNXRACJ7.js} +35 -36
- package/dist/chunk-UNXRACJ7.js.map +1 -0
- package/dist/{chunk-AKUJXDNW.js → chunk-UPUAQYAW.js} +3 -3
- package/dist/{chunk-GEHQXLEI.js → chunk-UYVWLISQ.js} +18 -35
- package/dist/chunk-UYVWLISQ.js.map +1 -0
- package/dist/{chunk-OSCLCMDG.js → chunk-UYWAESOT.js} +3 -3
- package/dist/{chunk-RW54ZMBM.js → chunk-VAYGNQTE.js} +2 -2
- package/dist/{chunk-ZT3YZB4K.js → chunk-VBFDVGAE.js} +12 -12
- package/dist/chunk-VBFDVGAE.js.map +1 -0
- package/dist/{chunk-IFB4Z76W.js → chunk-VTXCGKV5.js} +13 -12
- package/dist/chunk-VTXCGKV5.js.map +1 -0
- package/dist/{chunk-CDZERT7Z.js → chunk-VWNS6DH5.js} +48 -4
- package/dist/chunk-VWNS6DH5.js.map +1 -0
- package/dist/{chunk-CFFQ2Z7A.js → chunk-WUQQNE63.js} +2 -2
- package/dist/{chunk-UJL4HI2R.js → chunk-Z5NXYJIG.js} +20 -2
- package/dist/chunk-Z5NXYJIG.js.map +1 -0
- package/dist/{claude-W52VKI6L.js → claude-ACVXNB6N.js} +8 -5
- package/dist/{cleanup-H4VXU3C3.js → cleanup-KDLVTT7M.js} +133 -122
- package/dist/cleanup-KDLVTT7M.js.map +1 -0
- package/dist/cli.js +953 -430
- package/dist/cli.js.map +1 -1
- package/dist/{color-F7RU6B6Z.js → color-ZPIIUADB.js} +3 -3
- package/dist/{contribute-Y7IQV5QY.js → contribute-HY372S6F.js} +8 -6
- package/dist/{contribute-Y7IQV5QY.js.map → contribute-HY372S6F.js.map} +1 -1
- package/dist/dev-server-JCJGQ3PV.js +298 -0
- package/dist/dev-server-JCJGQ3PV.js.map +1 -0
- package/dist/{feedback-XTUCKJNT.js → feedback-7PVBQNLJ.js} +13 -12
- package/dist/{feedback-XTUCKJNT.js.map → feedback-7PVBQNLJ.js.map} +1 -1
- package/dist/{git-IYA53VIC.js → git-4BVOOOOV.js} +16 -4
- package/dist/hooks/iloom-hook.js +258 -0
- package/dist/{ignite-T74RYXCA.js → ignite-3B264M7K.js} +245 -39
- package/dist/ignite-3B264M7K.js.map +1 -0
- package/dist/index.d.ts +461 -124
- package/dist/index.js +743 -210
- package/dist/index.js.map +1 -1
- package/dist/init-LBA6NUK2.js +21 -0
- package/dist/{installation-detector-VARGFFRZ.js → installation-detector-6R6YOFVZ.js} +3 -3
- package/dist/mcp/issue-management-server.js +2 -1
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/neon-helpers-L5CXQ5CT.js +11 -0
- package/dist/{open-UMXANW5S.js → open-OGCV32Z4.js} +15 -13
- package/dist/{open-UMXANW5S.js.map → open-OGCV32Z4.js.map} +1 -1
- package/dist/projects-P55273AB.js +73 -0
- package/dist/projects-P55273AB.js.map +1 -0
- package/dist/{prompt-QALMYTVC.js → prompt-A7GGRHSY.js} +3 -3
- package/dist/prompts/init-prompt.txt +49 -0
- package/dist/prompts/issue-prompt.txt +110 -8
- package/dist/prompts/regular-prompt.txt +90 -0
- package/dist/prompts/session-summary-prompt.txt +82 -0
- package/dist/{rebase-VJ2VKR6R.js → rebase-4T5FQHNH.js} +11 -9
- package/dist/{rebase-VJ2VKR6R.js.map → rebase-4T5FQHNH.js.map} +1 -1
- package/dist/{remote-VUNCQZ6J.js → remote-73TZ2ADI.js} +3 -3
- package/dist/{run-MJYY4PUT.js → run-HNOP6WE2.js} +15 -13
- package/dist/{run-MJYY4PUT.js.map → run-HNOP6WE2.js.map} +1 -1
- package/dist/schema/settings.schema.json +49 -0
- package/dist/shell-DE3HKJSM.js +240 -0
- package/dist/shell-DE3HKJSM.js.map +1 -0
- package/dist/summary-GDT7DTRI.js +244 -0
- package/dist/summary-GDT7DTRI.js.map +1 -0
- package/dist/{test-git-IT5EWQ5C.js → test-git-YMAE57UP.js} +6 -4
- package/dist/{test-git-IT5EWQ5C.js.map → test-git-YMAE57UP.js.map} +1 -1
- package/dist/{test-prefix-NPWDPUUH.js → test-prefix-YCKL6CMT.js} +6 -4
- package/dist/{test-prefix-NPWDPUUH.js.map → test-prefix-YCKL6CMT.js.map} +1 -1
- package/dist/{test-tabs-PRMRSHKI.js → test-tabs-3SCJWRKT.js} +4 -4
- package/dist/{test-webserver-DAHONWCS.js → test-webserver-VPNLAFZ3.js} +2 -2
- package/dist/{update-4TDDUR5K.js → update-LETF5ASC.js} +4 -4
- package/dist/{update-notifier-QEX3CJHA.js → update-notifier-H55ZK7NU.js} +3 -3
- package/package.json +6 -6
- package/dist/ClaudeContextManager-BN7RE5ZQ.js +0 -15
- package/dist/ClaudeService-DLYLJUPA.js +0 -14
- package/dist/GitHubService-FZHHBOFG.js +0 -11
- package/dist/LoomLauncher-ZV3ZZIBA.js.map +0 -1
- package/dist/PromptTemplateManager-6HH3PVXV.js +0 -9
- package/dist/SettingsMigrationManager-TJ7UWZG5.js +0 -10
- package/dist/chunk-2CXREBLZ.js.map +0 -1
- package/dist/chunk-2IJEMXOB.js.map +0 -1
- package/dist/chunk-2MAIX45J.js.map +0 -1
- package/dist/chunk-5Q3NDNNV.js.map +0 -1
- package/dist/chunk-5VK4NRSF.js.map +0 -1
- package/dist/chunk-CDZERT7Z.js.map +0 -1
- package/dist/chunk-CE26YH2U.js.map +0 -1
- package/dist/chunk-DLHA5VQ3.js.map +0 -1
- package/dist/chunk-GEHQXLEI.js.map +0 -1
- package/dist/chunk-IFB4Z76W.js.map +0 -1
- package/dist/chunk-M7JJCX53.js.map +0 -1
- package/dist/chunk-MKWYLDFK.js.map +0 -1
- package/dist/chunk-OXAM2WVC.js.map +0 -1
- package/dist/chunk-OYF4VIFI.js.map +0 -1
- package/dist/chunk-PGPI5LR4.js.map +0 -1
- package/dist/chunk-RIEO2WML.js.map +0 -1
- package/dist/chunk-SUOXY5WJ.js.map +0 -1
- package/dist/chunk-UJL4HI2R.js.map +0 -1
- package/dist/chunk-ZT3YZB4K.js.map +0 -1
- package/dist/cleanup-H4VXU3C3.js.map +0 -1
- package/dist/ignite-T74RYXCA.js.map +0 -1
- package/dist/init-4FHTAM3F.js +0 -19
- package/dist/logger-MKYH4UDV.js +0 -12
- package/dist/neon-helpers-77PBPGJ5.js +0 -10
- package/dist/update-notifier-QEX3CJHA.js.map +0 -1
- /package/dist/{BranchNamingService-A77VI6AI.js.map → BranchNamingService-GCCWB3LK.js.map} +0 -0
- /package/dist/{ClaudeContextManager-BN7RE5ZQ.js.map → ClaudeContextManager-DK77227F.js.map} +0 -0
- /package/dist/{ClaudeService-DLYLJUPA.js.map → ClaudeService-W3SA7HVG.js.map} +0 -0
- /package/dist/{GitHubService-FZHHBOFG.js.map → GitHubService-RPM27GWD.js.map} +0 -0
- /package/dist/{PromptTemplateManager-6HH3PVXV.js.map → PromptTemplateManager-2TDZAUC6.js.map} +0 -0
- /package/dist/{SettingsManager-I2LRCW2A.js.map → SettingsManager-FJFU6JJD.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-TJ7UWZG5.js.map → SettingsMigrationManager-EH3J2TCN.js.map} +0 -0
- /package/dist/{chunk-UAN4A3YU.js.map → chunk-G6CIIJLT.js.map} +0 -0
- /package/dist/{chunk-4XIDC3NF.js.map → chunk-MD6HA5IK.js.map} +0 -0
- /package/dist/{chunk-OC4H6HJD.js.map → chunk-O7WHXLCB.js.map} +0 -0
- /package/dist/{chunk-PA6Q6AWM.js.map → chunk-PSFVTBM7.js.map} +0 -0
- /package/dist/{chunk-ZM3CFL5L.js.map → chunk-QRBOPFAA.js.map} +0 -0
- /package/dist/{chunk-SSCQCCJ7.js.map → chunk-THF25ICZ.js.map} +0 -0
- /package/dist/{chunk-AKUJXDNW.js.map → chunk-UPUAQYAW.js.map} +0 -0
- /package/dist/{chunk-OSCLCMDG.js.map → chunk-UYWAESOT.js.map} +0 -0
- /package/dist/{chunk-RW54ZMBM.js.map → chunk-VAYGNQTE.js.map} +0 -0
- /package/dist/{chunk-CFFQ2Z7A.js.map → chunk-WUQQNE63.js.map} +0 -0
- /package/dist/{claude-W52VKI6L.js.map → claude-ACVXNB6N.js.map} +0 -0
- /package/dist/{color-F7RU6B6Z.js.map → color-ZPIIUADB.js.map} +0 -0
- /package/dist/{git-IYA53VIC.js.map → git-4BVOOOOV.js.map} +0 -0
- /package/dist/{init-4FHTAM3F.js.map → init-LBA6NUK2.js.map} +0 -0
- /package/dist/{installation-detector-VARGFFRZ.js.map → installation-detector-6R6YOFVZ.js.map} +0 -0
- /package/dist/{logger-MKYH4UDV.js.map → neon-helpers-L5CXQ5CT.js.map} +0 -0
- /package/dist/{neon-helpers-77PBPGJ5.js.map → prompt-A7GGRHSY.js.map} +0 -0
- /package/dist/{prompt-QALMYTVC.js.map → remote-73TZ2ADI.js.map} +0 -0
- /package/dist/{test-tabs-PRMRSHKI.js.map → test-tabs-3SCJWRKT.js.map} +0 -0
- /package/dist/{test-webserver-DAHONWCS.js.map → test-webserver-VPNLAFZ3.js.map} +0 -0
- /package/dist/{update-4TDDUR5K.js.map → update-LETF5ASC.js.map} +0 -0
- /package/dist/{remote-VUNCQZ6J.js.map → update-notifier-H55ZK7NU.js.map} +0 -0
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
rgbToHex,
|
|
11
11
|
saturateColor,
|
|
12
12
|
selectDistinctColor
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-WUQQNE63.js";
|
|
14
|
+
import "./chunk-UYVWLISQ.js";
|
|
15
15
|
export {
|
|
16
16
|
MIN_COLOR_DISTANCE,
|
|
17
17
|
calculateForegroundColor,
|
|
@@ -24,4 +24,4 @@ export {
|
|
|
24
24
|
saturateColor,
|
|
25
25
|
selectDistinctColor
|
|
26
26
|
};
|
|
27
|
-
//# sourceMappingURL=color-
|
|
27
|
+
//# sourceMappingURL=color-ZPIIUADB.js.map
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
executeGitCommand
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-2W2FBL5G.js";
|
|
5
|
+
import "./chunk-IJ7IGJT3.js";
|
|
6
|
+
import "./chunk-VWNS6DH5.js";
|
|
6
7
|
import {
|
|
7
8
|
checkGhAuth,
|
|
8
9
|
executeGhCommand
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KO2FOMHL.js";
|
|
10
11
|
import {
|
|
11
12
|
promptInput
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-SJ2GZ6RF.js";
|
|
14
|
+
import "./chunk-6UIGZD2N.js";
|
|
13
15
|
import {
|
|
14
16
|
logger
|
|
15
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-UYVWLISQ.js";
|
|
16
18
|
|
|
17
19
|
// src/commands/contribute.ts
|
|
18
20
|
import { existsSync, accessSync, constants } from "fs";
|
|
@@ -257,4 +259,4 @@ export {
|
|
|
257
259
|
validateDirectoryName,
|
|
258
260
|
validateDirectoryPath
|
|
259
261
|
};
|
|
260
|
-
//# sourceMappingURL=contribute-
|
|
262
|
+
//# sourceMappingURL=contribute-HY372S6F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/contribute.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { checkGhAuth, executeGhCommand } from '../utils/github.js'\nimport { executeGitCommand } from '../utils/git.js'\nimport { promptInput } from '../utils/prompt.js'\nimport { existsSync, accessSync, constants } from 'fs'\nimport { writeFile, mkdir } from 'fs/promises'\nimport path from 'path'\nimport { InitCommand } from './init.js'\nimport chalk from 'chalk'\n\nconst ILOOM_REPO = 'iloom-ai/iloom-cli'\nconst UPSTREAM_URL = 'https://github.com/iloom-ai/iloom-cli.git'\n\n// Maximum path length for most file systems\nconst MAX_PATH_LENGTH = 255\n\n// Reserved names on Windows (also avoid on all platforms for portability)\nconst RESERVED_NAMES = [\n\t'CON', 'PRN', 'AUX', 'NUL',\n\t'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',\n\t'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9',\n]\n\n// Invalid characters for directory names (cross-platform)\n// eslint-disable-next-line no-control-regex\nconst INVALID_CHARS_PATTERN = /[<>:\"|?*\\x00-\\x1f]/\n\n\n/**\n * Validation result for directory input\n */\ninterface DirectoryValidationResult {\n\tisValid: boolean\n\terror?: string\n}\n\n/**\n * Validate directory name format\n * @param directoryName - The directory name (not full path)\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryName(directoryName: string): DirectoryValidationResult {\n\t// Check for empty or whitespace-only\n\tif (!directoryName || directoryName.trim() === '') {\n\t\treturn { isValid: false, error: 'Directory name cannot be empty' }\n\t}\n\n\tconst trimmed = directoryName.trim()\n\tconst baseName = path.basename(trimmed)\n\n\t// Check for invalid characters\n\tif (INVALID_CHARS_PATTERN.test(baseName)) {\n\t\treturn { isValid: false, error: 'Directory name contains invalid characters (<>:\"|?*)' }\n\t}\n\n\t// Check for reserved names (case-insensitive)\n\tif (RESERVED_NAMES.includes(baseName.toUpperCase())) {\n\t\treturn { isValid: false, error: `\"${baseName}\" is a reserved name and cannot be used` }\n\t}\n\n\t// Check for names that start/end with dots or spaces (problematic on some systems)\n\tif (baseName.startsWith('.') && baseName === '.') {\n\t\treturn { isValid: false, error: 'Directory name cannot be just a dot' }\n\t}\n\tif (baseName.endsWith('.') || baseName.endsWith(' ')) {\n\t\treturn { isValid: false, error: 'Directory name cannot end with a dot or space' }\n\t}\n\n\treturn { isValid: true }\n}\n\n/**\n * Validate full directory path\n * @param directoryPath - The full directory path\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryPath(directoryPath: string): DirectoryValidationResult {\n\t// First validate the directory name component\n\tconst nameValidation = validateDirectoryName(directoryPath)\n\tif (!nameValidation.isValid) {\n\t\treturn nameValidation\n\t}\n\n\tconst trimmed = directoryPath.trim()\n\tconst absolutePath = path.resolve(trimmed)\n\n\t// Check path length\n\tif (absolutePath.length > MAX_PATH_LENGTH) {\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\terror: `Path is too long (${absolutePath.length} characters). Maximum is ${MAX_PATH_LENGTH} characters.`\n\t\t}\n\t}\n\n\t// Check if directory already exists\n\tif (existsSync(absolutePath)) {\n\t\treturn { isValid: false, error: `Directory already exists: ${trimmed}` }\n\t}\n\n\t// Check if parent directory exists\n\tconst parentDir = path.dirname(absolutePath)\n\tif (!existsSync(parentDir)) {\n\t\treturn { isValid: false, error: `Parent directory does not exist: ${parentDir}` }\n\t}\n\n\t// Check if parent directory is writable\n\ttry {\n\t\taccessSync(parentDir, constants.W_OK)\n\t} catch {\n\t\treturn { isValid: false, error: `Parent directory is not writable: ${parentDir}` }\n\t}\n\n\treturn { isValid: true }\n}\n\n\n/**\n * ContributeCommand - Set up local development environment for contributing to iloom\n * Implements issue #220: streamlined contributor onboarding workflow\n */\nexport class ContributeCommand {\n\tconstructor(_initCommand?: InitCommand) {}\n\n\t/**\n\t * Main entry point for the contribute command\n\t * Automates fork creation, cloning, and upstream configuration\n\t */\n\tpublic async execute(): Promise<void> {\n\t\tlogger.info(chalk.bold('Setting up iloom contributor environment...'))\n\n\t\t// Step 1: Verify gh CLI authenticated\n\t\tconst username = await this.getAuthenticatedUsername()\n\t\tlogger.success(`Authenticated as ${chalk.cyan(username)}`)\n\n\t\t// Step 2: Check for existing fork\n\t\tconst hasFork = await this.forkExists(username)\n\n\t\t// Step 3: Create fork if needed\n\t\tif (!hasFork) {\n\t\t\tlogger.info('Creating fork of iloom-ai/iloom-cli...')\n\t\t\tawait this.createFork()\n\t\t\tlogger.success('Fork created successfully')\n\t\t} else {\n\t\t\tlogger.info('Using existing fork')\n\t\t}\n\n\t\t// Step 4: Prompt for directory with validation and retry loop\n\t\tconst directory = await this.promptForDirectory()\n\n\t\t// Handle cancelled input\n\t\tif (!directory) {\n\t\t\tlogger.info('Setup cancelled by user')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tconst absolutePath = path.resolve(directory)\n\n\t\t// Step 5: Clone repository (gh CLI handles SSH/HTTPS automatically based on git config)\n\t\tlogger.info(`Cloning repository to ${directory}...`)\n\t\tawait this.cloneRepository(username, directory)\n\t\tlogger.success('Repository cloned successfully')\n\n\t\t// Step 6: Add upstream remote if it doesn't exist\n\t\tawait this.addUpstreamRemote(absolutePath)\n\n\t\t// Step 7: Configure settings\n\t\tlogger.info('Configuring iloom settings...')\n\t\tawait this.configureSettings(absolutePath)\n\t\tlogger.success('Settings configured')\n\n\t\tlogger.success(chalk.bold.green('\\nContributor environment setup complete!'))\n\t\tlogger.info(`\\nNext steps:`)\n\t\tlogger.info(` 1. cd ${directory}`)\n\t\tlogger.info(` 2. pnpm install`)\n\t\tlogger.info(` 3. iloom start <issue_number>`)\n\t\tlogger.info(`\\nHappy contributing!`)\n\t}\n\n\t/**\n\t * Get authenticated GitHub username\n\t * @throws Error if not authenticated\n\t */\n\tprivate async getAuthenticatedUsername(): Promise<string> {\n\t\tconst authStatus = await checkGhAuth()\n\n\t\tif (!authStatus.hasAuth) {\n\t\t\tthrow new Error(\n\t\t\t\t'GitHub CLI is not authenticated. Please run: gh auth login'\n\t\t\t)\n\t\t}\n\n\t\tif (!authStatus.username) {\n\t\t\t// Try to fetch username from gh api if not in auth status\n\t\t\ttry {\n\t\t\t\tconst user = await executeGhCommand<{ login: string }>(['api', 'user', '--json', 'login'])\n\t\t\t\treturn user.login\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\tthrow new Error(`Unable to determine GitHub username: ${message}`)\n\t\t\t}\n\t\t}\n\n\t\treturn authStatus.username\n\t}\n\n\t/**\n\t * Check if user already has a fork of iloom-cli\n\t */\n\tprivate async forkExists(username: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait executeGhCommand(['api', `repos/${username}/iloom-cli`])\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\t// 404 means no fork exists\n\t\t\tif (error instanceof Error && error.message.includes('Not Found')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Create a fork of iloom-cli without cloning\n\t */\n\tprivate async createFork(): Promise<void> {\n\t\tawait executeGhCommand(['repo', 'fork', ILOOM_REPO, '--clone=false'])\n\t}\n\n\n\t/**\n\t * Clone the repository using simplified gh CLI approach\n\t */\n\tprivate async cloneRepository(\n\t\tusername: string,\n\t\tdirectory: string\n\t): Promise<void> {\n\t\tconst repoIdentifier = `${username}/iloom-cli`\n\t\t// Always use gh repo clone - it handles SSH/HTTPS based on user's git config\n\t\tawait executeGhCommand(['repo', 'clone', repoIdentifier, directory])\n\t}\n\n\t/**\n\t * Add upstream remote if it doesn't already exist\n\t */\n\tprivate async addUpstreamRemote(directory: string): Promise<void> {\n\t\ttry {\n\t\t\t// Check if upstream remote exists\n\t\t\tawait executeGitCommand(['remote', 'get-url', 'upstream'], { cwd: directory })\n\t\t\tlogger.info('Upstream remote already configured')\n\t\t} catch {\n\t\t\t// Upstream doesn't exist, add it\n\t\t\tlogger.info('Adding upstream remote...')\n\t\t\tawait executeGitCommand(\n\t\t\t\t['remote', 'add', 'upstream', UPSTREAM_URL],\n\t\t\t\t{ cwd: directory }\n\t\t\t)\n\t\t\tlogger.success('Upstream remote configured')\n\t\t}\n\t}\n\n\t/**\n\t * Prompt for directory with validation and retry loop\n\t * @returns The validated directory path, or null if user cancels\n\t */\n\tprivate async promptForDirectory(): Promise<string | null> {\n\t\tconst maxRetries = 3\n\t\tlet attempts = 0\n\n\t\twhile (attempts < maxRetries) {\n\t\t\tconst directory = await promptInput(\n\t\t\t\t'Where should the repository be cloned?',\n\t\t\t\t'./iloom-cli'\n\t\t\t)\n\n\t\t\t// Handle empty input (user cancelled by entering empty string after exhausting default)\n\t\t\tif (!directory || directory.trim() === '') {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst trimmed = directory.trim()\n\n\t\t\t// Validate the directory path\n\t\t\tconst validation = validateDirectoryPath(trimmed)\n\t\t\tif (validation.isValid) {\n\t\t\t\treturn trimmed\n\t\t\t}\n\n\t\t\t// Show error and increment attempts\n\t\t\tattempts++\n\t\t\tif (attempts < maxRetries) {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.info(`Please try again (${maxRetries - attempts} attempts remaining)`)\n\t\t\t} else {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.error('Maximum retry attempts reached')\n\t\t\t\tthrow new Error(`Invalid directory after ${maxRetries} attempts: ${validation.error}`)\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n\n\n\t/**\n\t * Configure .iloom/settings.json with upstream remote\n\t */\n\tprivate async configureSettings(directory: string): Promise<void> {\n\t\tconst iloomDir = path.join(directory, '.iloom')\n\t\tconst settingsPath = path.join(iloomDir, 'settings.local.json')\n\n\t\t// Create .iloom directory\n\t\tawait mkdir(iloomDir, { recursive: true })\n\n\t\t// Create settings.json with upstream remote configuration and github-pr mode\n\t\tconst settings = {\n\t\t\tissueManagement: {\n\t\t\t\tgithub: {\n\t\t\t\t\tremote: 'upstream',\n\t\t\t\t},\n\t\t\t},\n\t\t\tmergeBehavior: {\n\t\t\t\tmode: 'github-pr',\n\t\t\t},\n\t\t}\n\n\t\tawait writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\\n')\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/commands/contribute.ts"],"sourcesContent":["import { logger } from '../utils/logger.js'\nimport { checkGhAuth, executeGhCommand } from '../utils/github.js'\nimport { executeGitCommand } from '../utils/git.js'\nimport { promptInput } from '../utils/prompt.js'\nimport { existsSync, accessSync, constants } from 'fs'\nimport { writeFile, mkdir } from 'fs/promises'\nimport path from 'path'\nimport { InitCommand } from './init.js'\nimport chalk from 'chalk'\n\nconst ILOOM_REPO = 'iloom-ai/iloom-cli'\nconst UPSTREAM_URL = 'https://github.com/iloom-ai/iloom-cli.git'\n\n// Maximum path length for most file systems\nconst MAX_PATH_LENGTH = 255\n\n// Reserved names on Windows (also avoid on all platforms for portability)\nconst RESERVED_NAMES = [\n\t'CON', 'PRN', 'AUX', 'NUL',\n\t'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',\n\t'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9',\n]\n\n// Invalid characters for directory names (cross-platform)\n// eslint-disable-next-line no-control-regex\nconst INVALID_CHARS_PATTERN = /[<>:\"|?*\\x00-\\x1f]/\n\n\n/**\n * Validation result for directory input\n */\ninterface DirectoryValidationResult {\n\tisValid: boolean\n\terror?: string\n}\n\n/**\n * Validate directory name format\n * @param directoryName - The directory name (not full path)\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryName(directoryName: string): DirectoryValidationResult {\n\t// Check for empty or whitespace-only\n\tif (!directoryName || directoryName.trim() === '') {\n\t\treturn { isValid: false, error: 'Directory name cannot be empty' }\n\t}\n\n\tconst trimmed = directoryName.trim()\n\tconst baseName = path.basename(trimmed)\n\n\t// Check for invalid characters\n\tif (INVALID_CHARS_PATTERN.test(baseName)) {\n\t\treturn { isValid: false, error: 'Directory name contains invalid characters (<>:\"|?*)' }\n\t}\n\n\t// Check for reserved names (case-insensitive)\n\tif (RESERVED_NAMES.includes(baseName.toUpperCase())) {\n\t\treturn { isValid: false, error: `\"${baseName}\" is a reserved name and cannot be used` }\n\t}\n\n\t// Check for names that start/end with dots or spaces (problematic on some systems)\n\tif (baseName.startsWith('.') && baseName === '.') {\n\t\treturn { isValid: false, error: 'Directory name cannot be just a dot' }\n\t}\n\tif (baseName.endsWith('.') || baseName.endsWith(' ')) {\n\t\treturn { isValid: false, error: 'Directory name cannot end with a dot or space' }\n\t}\n\n\treturn { isValid: true }\n}\n\n/**\n * Validate full directory path\n * @param directoryPath - The full directory path\n * @returns Validation result with error message if invalid\n */\nexport function validateDirectoryPath(directoryPath: string): DirectoryValidationResult {\n\t// First validate the directory name component\n\tconst nameValidation = validateDirectoryName(directoryPath)\n\tif (!nameValidation.isValid) {\n\t\treturn nameValidation\n\t}\n\n\tconst trimmed = directoryPath.trim()\n\tconst absolutePath = path.resolve(trimmed)\n\n\t// Check path length\n\tif (absolutePath.length > MAX_PATH_LENGTH) {\n\t\treturn {\n\t\t\tisValid: false,\n\t\t\terror: `Path is too long (${absolutePath.length} characters). Maximum is ${MAX_PATH_LENGTH} characters.`\n\t\t}\n\t}\n\n\t// Check if directory already exists\n\tif (existsSync(absolutePath)) {\n\t\treturn { isValid: false, error: `Directory already exists: ${trimmed}` }\n\t}\n\n\t// Check if parent directory exists\n\tconst parentDir = path.dirname(absolutePath)\n\tif (!existsSync(parentDir)) {\n\t\treturn { isValid: false, error: `Parent directory does not exist: ${parentDir}` }\n\t}\n\n\t// Check if parent directory is writable\n\ttry {\n\t\taccessSync(parentDir, constants.W_OK)\n\t} catch {\n\t\treturn { isValid: false, error: `Parent directory is not writable: ${parentDir}` }\n\t}\n\n\treturn { isValid: true }\n}\n\n\n/**\n * ContributeCommand - Set up local development environment for contributing to iloom\n * Implements issue #220: streamlined contributor onboarding workflow\n */\nexport class ContributeCommand {\n\tconstructor(_initCommand?: InitCommand) {}\n\n\t/**\n\t * Main entry point for the contribute command\n\t * Automates fork creation, cloning, and upstream configuration\n\t */\n\tpublic async execute(): Promise<void> {\n\t\tlogger.info(chalk.bold('Setting up iloom contributor environment...'))\n\n\t\t// Step 1: Verify gh CLI authenticated\n\t\tconst username = await this.getAuthenticatedUsername()\n\t\tlogger.success(`Authenticated as ${chalk.cyan(username)}`)\n\n\t\t// Step 2: Check for existing fork\n\t\tconst hasFork = await this.forkExists(username)\n\n\t\t// Step 3: Create fork if needed\n\t\tif (!hasFork) {\n\t\t\tlogger.info('Creating fork of iloom-ai/iloom-cli...')\n\t\t\tawait this.createFork()\n\t\t\tlogger.success('Fork created successfully')\n\t\t} else {\n\t\t\tlogger.info('Using existing fork')\n\t\t}\n\n\t\t// Step 4: Prompt for directory with validation and retry loop\n\t\tconst directory = await this.promptForDirectory()\n\n\t\t// Handle cancelled input\n\t\tif (!directory) {\n\t\t\tlogger.info('Setup cancelled by user')\n\t\t\tprocess.exit(0)\n\t\t}\n\n\t\tconst absolutePath = path.resolve(directory)\n\n\t\t// Step 5: Clone repository (gh CLI handles SSH/HTTPS automatically based on git config)\n\t\tlogger.info(`Cloning repository to ${directory}...`)\n\t\tawait this.cloneRepository(username, directory)\n\t\tlogger.success('Repository cloned successfully')\n\n\t\t// Step 6: Add upstream remote if it doesn't exist\n\t\tawait this.addUpstreamRemote(absolutePath)\n\n\t\t// Step 7: Configure settings\n\t\tlogger.info('Configuring iloom settings...')\n\t\tawait this.configureSettings(absolutePath)\n\t\tlogger.success('Settings configured')\n\n\t\tlogger.success(chalk.bold.green('\\nContributor environment setup complete!'))\n\t\tlogger.info(`\\nNext steps:`)\n\t\tlogger.info(` 1. cd ${directory}`)\n\t\tlogger.info(` 2. pnpm install`)\n\t\tlogger.info(` 3. iloom start <issue_number>`)\n\t\tlogger.info(`\\nHappy contributing!`)\n\t}\n\n\t/**\n\t * Get authenticated GitHub username\n\t * @throws Error if not authenticated\n\t */\n\tprivate async getAuthenticatedUsername(): Promise<string> {\n\t\tconst authStatus = await checkGhAuth()\n\n\t\tif (!authStatus.hasAuth) {\n\t\t\tthrow new Error(\n\t\t\t\t'GitHub CLI is not authenticated. Please run: gh auth login'\n\t\t\t)\n\t\t}\n\n\t\tif (!authStatus.username) {\n\t\t\t// Try to fetch username from gh api if not in auth status\n\t\t\ttry {\n\t\t\t\tconst user = await executeGhCommand<{ login: string }>(['api', 'user', '--json', 'login'])\n\t\t\t\treturn user.login\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error)\n\t\t\t\tthrow new Error(`Unable to determine GitHub username: ${message}`)\n\t\t\t}\n\t\t}\n\n\t\treturn authStatus.username\n\t}\n\n\t/**\n\t * Check if user already has a fork of iloom-cli\n\t */\n\tprivate async forkExists(username: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait executeGhCommand(['api', `repos/${username}/iloom-cli`])\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\t// 404 means no fork exists\n\t\t\tif (error instanceof Error && error.message.includes('Not Found')) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// Re-throw unexpected errors\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Create a fork of iloom-cli without cloning\n\t */\n\tprivate async createFork(): Promise<void> {\n\t\tawait executeGhCommand(['repo', 'fork', ILOOM_REPO, '--clone=false'])\n\t}\n\n\n\t/**\n\t * Clone the repository using simplified gh CLI approach\n\t */\n\tprivate async cloneRepository(\n\t\tusername: string,\n\t\tdirectory: string\n\t): Promise<void> {\n\t\tconst repoIdentifier = `${username}/iloom-cli`\n\t\t// Always use gh repo clone - it handles SSH/HTTPS based on user's git config\n\t\tawait executeGhCommand(['repo', 'clone', repoIdentifier, directory])\n\t}\n\n\t/**\n\t * Add upstream remote if it doesn't already exist\n\t */\n\tprivate async addUpstreamRemote(directory: string): Promise<void> {\n\t\ttry {\n\t\t\t// Check if upstream remote exists\n\t\t\tawait executeGitCommand(['remote', 'get-url', 'upstream'], { cwd: directory })\n\t\t\tlogger.info('Upstream remote already configured')\n\t\t} catch {\n\t\t\t// Upstream doesn't exist, add it\n\t\t\tlogger.info('Adding upstream remote...')\n\t\t\tawait executeGitCommand(\n\t\t\t\t['remote', 'add', 'upstream', UPSTREAM_URL],\n\t\t\t\t{ cwd: directory }\n\t\t\t)\n\t\t\tlogger.success('Upstream remote configured')\n\t\t}\n\t}\n\n\t/**\n\t * Prompt for directory with validation and retry loop\n\t * @returns The validated directory path, or null if user cancels\n\t */\n\tprivate async promptForDirectory(): Promise<string | null> {\n\t\tconst maxRetries = 3\n\t\tlet attempts = 0\n\n\t\twhile (attempts < maxRetries) {\n\t\t\tconst directory = await promptInput(\n\t\t\t\t'Where should the repository be cloned?',\n\t\t\t\t'./iloom-cli'\n\t\t\t)\n\n\t\t\t// Handle empty input (user cancelled by entering empty string after exhausting default)\n\t\t\tif (!directory || directory.trim() === '') {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst trimmed = directory.trim()\n\n\t\t\t// Validate the directory path\n\t\t\tconst validation = validateDirectoryPath(trimmed)\n\t\t\tif (validation.isValid) {\n\t\t\t\treturn trimmed\n\t\t\t}\n\n\t\t\t// Show error and increment attempts\n\t\t\tattempts++\n\t\t\tif (attempts < maxRetries) {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.info(`Please try again (${maxRetries - attempts} attempts remaining)`)\n\t\t\t} else {\n\t\t\t\tlogger.error(`${validation.error}`)\n\t\t\t\tlogger.error('Maximum retry attempts reached')\n\t\t\t\tthrow new Error(`Invalid directory after ${maxRetries} attempts: ${validation.error}`)\n\t\t\t}\n\t\t}\n\n\t\treturn null\n\t}\n\n\n\t/**\n\t * Configure .iloom/settings.json with upstream remote\n\t */\n\tprivate async configureSettings(directory: string): Promise<void> {\n\t\tconst iloomDir = path.join(directory, '.iloom')\n\t\tconst settingsPath = path.join(iloomDir, 'settings.local.json')\n\n\t\t// Create .iloom directory\n\t\tawait mkdir(iloomDir, { recursive: true })\n\n\t\t// Create settings.json with upstream remote configuration and github-pr mode\n\t\tconst settings = {\n\t\t\tissueManagement: {\n\t\t\t\tgithub: {\n\t\t\t\t\tremote: 'upstream',\n\t\t\t\t},\n\t\t\t},\n\t\t\tmergeBehavior: {\n\t\t\t\tmode: 'github-pr',\n\t\t\t},\n\t\t}\n\n\t\tawait writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\\n')\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DevServerManager
|
|
4
|
+
} from "./chunk-OF7BNW4D.js";
|
|
5
|
+
import "./chunk-QRBOPFAA.js";
|
|
6
|
+
import {
|
|
7
|
+
IdentifierParser
|
|
8
|
+
} from "./chunk-55TB3FSG.js";
|
|
9
|
+
import {
|
|
10
|
+
calculatePortForBranch
|
|
11
|
+
} from "./chunk-VU3QMIP2.js";
|
|
12
|
+
import {
|
|
13
|
+
GitWorktreeManager
|
|
14
|
+
} from "./chunk-JC5HXN75.js";
|
|
15
|
+
import "./chunk-VBFDVGAE.js";
|
|
16
|
+
import {
|
|
17
|
+
extractSettingsOverrides
|
|
18
|
+
} from "./chunk-GYCR2LOU.js";
|
|
19
|
+
import {
|
|
20
|
+
ProjectCapabilityDetector
|
|
21
|
+
} from "./chunk-EBISESAP.js";
|
|
22
|
+
import "./chunk-2ZPFJQ3B.js";
|
|
23
|
+
import {
|
|
24
|
+
extractIssueNumber
|
|
25
|
+
} from "./chunk-2W2FBL5G.js";
|
|
26
|
+
import "./chunk-IJ7IGJT3.js";
|
|
27
|
+
import {
|
|
28
|
+
SettingsManager
|
|
29
|
+
} from "./chunk-VWNS6DH5.js";
|
|
30
|
+
import {
|
|
31
|
+
extractPort,
|
|
32
|
+
findEnvFileContainingVariable,
|
|
33
|
+
parseEnvFile
|
|
34
|
+
} from "./chunk-Z5NXYJIG.js";
|
|
35
|
+
import "./chunk-6UIGZD2N.js";
|
|
36
|
+
import {
|
|
37
|
+
logger
|
|
38
|
+
} from "./chunk-UYVWLISQ.js";
|
|
39
|
+
|
|
40
|
+
// src/commands/dev-server.ts
|
|
41
|
+
import path from "path";
|
|
42
|
+
import fs from "fs-extra";
|
|
43
|
+
var DevServerCommand = class {
|
|
44
|
+
constructor(gitWorktreeManager = new GitWorktreeManager(), capabilityDetector = new ProjectCapabilityDetector(), identifierParser = new IdentifierParser(new GitWorktreeManager()), devServerManager = new DevServerManager(), settingsManager = new SettingsManager()) {
|
|
45
|
+
this.gitWorktreeManager = gitWorktreeManager;
|
|
46
|
+
this.capabilityDetector = capabilityDetector;
|
|
47
|
+
this.identifierParser = identifierParser;
|
|
48
|
+
this.devServerManager = devServerManager;
|
|
49
|
+
this.settingsManager = settingsManager;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Output JSON to stdout (used for --json flag)
|
|
53
|
+
*/
|
|
54
|
+
outputJson(data) {
|
|
55
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
56
|
+
}
|
|
57
|
+
async execute(input) {
|
|
58
|
+
const parsed = input.identifier ? await this.parseExplicitInput(input.identifier) : await this.autoDetectFromCurrentDirectory();
|
|
59
|
+
logger.debug(`Parsed input: ${JSON.stringify(parsed)}`);
|
|
60
|
+
const worktree = await this.findWorktreeForIdentifier(parsed);
|
|
61
|
+
logger.debug(`Found worktree at: ${worktree.path}`);
|
|
62
|
+
const { capabilities } = await this.capabilityDetector.detectCapabilities(worktree.path);
|
|
63
|
+
logger.debug(`Detected capabilities: ${capabilities.join(", ")}`);
|
|
64
|
+
if (!capabilities.includes("web")) {
|
|
65
|
+
const message2 = "No web capability detected in this workspace. Dev server not started.";
|
|
66
|
+
if (input.json) {
|
|
67
|
+
this.outputJson({
|
|
68
|
+
status: "no_web_capability",
|
|
69
|
+
message: message2
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
logger.info(message2);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
status: "no_web_capability",
|
|
76
|
+
message: message2
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const port = await this.getWorkspacePort(worktree.path);
|
|
80
|
+
const url = `http://localhost:${port}`;
|
|
81
|
+
const isRunning = await this.devServerManager.isServerRunning(port);
|
|
82
|
+
if (isRunning) {
|
|
83
|
+
const message2 = `Dev server already running at ${url}`;
|
|
84
|
+
if (input.json) {
|
|
85
|
+
this.outputJson({
|
|
86
|
+
status: "already_running",
|
|
87
|
+
url,
|
|
88
|
+
port,
|
|
89
|
+
message: message2
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
logger.info(message2);
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
status: "already_running",
|
|
96
|
+
url,
|
|
97
|
+
port,
|
|
98
|
+
message: message2
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const message = `Starting dev server at ${url}`;
|
|
102
|
+
if (!input.json) {
|
|
103
|
+
logger.info(message);
|
|
104
|
+
}
|
|
105
|
+
let finalResult = {
|
|
106
|
+
status: "started",
|
|
107
|
+
url,
|
|
108
|
+
port,
|
|
109
|
+
message
|
|
110
|
+
};
|
|
111
|
+
const processInfo = await this.devServerManager.runServerForeground(
|
|
112
|
+
worktree.path,
|
|
113
|
+
port,
|
|
114
|
+
!!input.json,
|
|
115
|
+
// Callback called immediately when process starts (for JSON output)
|
|
116
|
+
(pid) => {
|
|
117
|
+
if (input.json && pid) {
|
|
118
|
+
finalResult.pid = pid;
|
|
119
|
+
this.outputJson(finalResult);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
if (processInfo.pid) {
|
|
124
|
+
finalResult.pid = processInfo.pid;
|
|
125
|
+
}
|
|
126
|
+
return finalResult;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Parse explicit identifier input
|
|
130
|
+
*/
|
|
131
|
+
async parseExplicitInput(identifier) {
|
|
132
|
+
const parsed = await this.identifierParser.parseForPatternDetection(identifier);
|
|
133
|
+
if (parsed.type === "description") {
|
|
134
|
+
throw new Error("Description input type is not supported in dev-server command");
|
|
135
|
+
}
|
|
136
|
+
const result = {
|
|
137
|
+
type: parsed.type,
|
|
138
|
+
originalInput: parsed.originalInput,
|
|
139
|
+
autoDetected: false
|
|
140
|
+
};
|
|
141
|
+
if (parsed.number !== void 0) {
|
|
142
|
+
result.number = parsed.number;
|
|
143
|
+
}
|
|
144
|
+
if (parsed.branchName !== void 0) {
|
|
145
|
+
result.branchName = parsed.branchName;
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Auto-detect identifier from current directory
|
|
151
|
+
*/
|
|
152
|
+
async autoDetectFromCurrentDirectory() {
|
|
153
|
+
const currentDir = path.basename(process.cwd());
|
|
154
|
+
const prPattern = /_pr_(\d+)$/;
|
|
155
|
+
const prMatch = currentDir.match(prPattern);
|
|
156
|
+
if (prMatch == null ? void 0 : prMatch[1]) {
|
|
157
|
+
const prNumber = parseInt(prMatch[1], 10);
|
|
158
|
+
logger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`);
|
|
159
|
+
return {
|
|
160
|
+
type: "pr",
|
|
161
|
+
number: prNumber,
|
|
162
|
+
originalInput: currentDir,
|
|
163
|
+
autoDetected: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const issueNumber = extractIssueNumber(currentDir);
|
|
167
|
+
if (issueNumber !== null) {
|
|
168
|
+
logger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`);
|
|
169
|
+
return {
|
|
170
|
+
type: "issue",
|
|
171
|
+
number: issueNumber,
|
|
172
|
+
originalInput: currentDir,
|
|
173
|
+
autoDetected: true
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const repoInfo = await this.gitWorktreeManager.getRepoInfo();
|
|
177
|
+
const currentBranch = repoInfo.currentBranch;
|
|
178
|
+
if (!currentBranch) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\nExpected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix"
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const branchIssueNumber = extractIssueNumber(currentBranch);
|
|
184
|
+
if (branchIssueNumber !== null) {
|
|
185
|
+
logger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`);
|
|
186
|
+
return {
|
|
187
|
+
type: "issue",
|
|
188
|
+
number: branchIssueNumber,
|
|
189
|
+
originalInput: currentBranch,
|
|
190
|
+
autoDetected: true
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
type: "branch",
|
|
195
|
+
branchName: currentBranch,
|
|
196
|
+
originalInput: currentBranch,
|
|
197
|
+
autoDetected: true
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Find worktree for the given identifier
|
|
202
|
+
*/
|
|
203
|
+
async findWorktreeForIdentifier(parsed) {
|
|
204
|
+
let worktree = null;
|
|
205
|
+
if (parsed.type === "issue" && parsed.number !== void 0) {
|
|
206
|
+
worktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number);
|
|
207
|
+
} else if (parsed.type === "pr" && parsed.number !== void 0) {
|
|
208
|
+
const prNumber = typeof parsed.number === "number" ? parsed.number : Number(parsed.number);
|
|
209
|
+
if (isNaN(prNumber) || !isFinite(prNumber)) {
|
|
210
|
+
throw new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`);
|
|
211
|
+
}
|
|
212
|
+
worktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, "");
|
|
213
|
+
} else if (parsed.type === "branch" && parsed.branchName) {
|
|
214
|
+
worktree = await this.gitWorktreeManager.findWorktreeForBranch(
|
|
215
|
+
parsed.branchName
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (!worktree) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`No worktree found for ${this.formatParsedInput(parsed)}. Run 'il start ${parsed.originalInput}' to create one.`
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
return worktree;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Format parsed input for display
|
|
227
|
+
*/
|
|
228
|
+
formatParsedInput(parsed) {
|
|
229
|
+
const autoLabel = parsed.autoDetected ? " (auto-detected)" : "";
|
|
230
|
+
if (parsed.type === "issue") {
|
|
231
|
+
return `issue #${parsed.number}${autoLabel}`;
|
|
232
|
+
}
|
|
233
|
+
if (parsed.type === "pr") {
|
|
234
|
+
return `PR #${parsed.number}${autoLabel}`;
|
|
235
|
+
}
|
|
236
|
+
return `branch "${parsed.branchName}"${autoLabel}`;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get port for workspace - reads from dotenv-flow files or calculates based on workspace type
|
|
240
|
+
*/
|
|
241
|
+
async getWorkspacePort(worktreePath) {
|
|
242
|
+
var _a, _b;
|
|
243
|
+
const cliOverrides = extractSettingsOverrides();
|
|
244
|
+
const settings = await this.settingsManager.loadSettings(void 0, cliOverrides);
|
|
245
|
+
const basePort = ((_b = (_a = settings.capabilities) == null ? void 0 : _a.web) == null ? void 0 : _b.basePort) ?? 3e3;
|
|
246
|
+
const envFile = await findEnvFileContainingVariable(
|
|
247
|
+
worktreePath,
|
|
248
|
+
"PORT",
|
|
249
|
+
async (p) => fs.pathExists(p),
|
|
250
|
+
async (p, varName) => {
|
|
251
|
+
const content = await fs.readFile(p, "utf8");
|
|
252
|
+
const envMap = parseEnvFile(content);
|
|
253
|
+
return envMap.get(varName) ?? null;
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
if (envFile) {
|
|
257
|
+
const envPath = path.join(worktreePath, envFile);
|
|
258
|
+
const envContent = await fs.readFile(envPath, "utf8");
|
|
259
|
+
const envMap = parseEnvFile(envContent);
|
|
260
|
+
const port2 = extractPort(envMap);
|
|
261
|
+
if (port2) {
|
|
262
|
+
logger.debug(`Using PORT from ${envFile}: ${port2}`);
|
|
263
|
+
return port2;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
logger.debug("PORT not found in any dotenv-flow file, calculating from workspace identifier");
|
|
267
|
+
const worktrees = await this.gitWorktreeManager.listWorktrees();
|
|
268
|
+
const worktree = worktrees.find((wt) => wt.path === worktreePath);
|
|
269
|
+
if (!worktree) {
|
|
270
|
+
throw new Error(`Could not find worktree for path: ${worktreePath}`);
|
|
271
|
+
}
|
|
272
|
+
const dirName = path.basename(worktreePath);
|
|
273
|
+
const prPattern = /_pr_(\d+)$/;
|
|
274
|
+
const prMatch = dirName.match(prPattern);
|
|
275
|
+
if (prMatch == null ? void 0 : prMatch[1]) {
|
|
276
|
+
const prNumber = parseInt(prMatch[1], 10);
|
|
277
|
+
const port2 = basePort + prNumber;
|
|
278
|
+
logger.debug(`Calculated PORT for PR #${prNumber}: ${port2}`);
|
|
279
|
+
return port2;
|
|
280
|
+
}
|
|
281
|
+
const issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch);
|
|
282
|
+
if (issueId !== null) {
|
|
283
|
+
const issueNumber = parseInt(issueId, 10);
|
|
284
|
+
if (!isNaN(issueNumber)) {
|
|
285
|
+
const port2 = basePort + issueNumber;
|
|
286
|
+
logger.debug(`Calculated PORT for issue #${issueId}: ${port2}`);
|
|
287
|
+
return port2;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const port = calculatePortForBranch(worktree.branch, basePort);
|
|
291
|
+
logger.debug(`Calculated PORT for branch "${worktree.branch}": ${port}`);
|
|
292
|
+
return port;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
export {
|
|
296
|
+
DevServerCommand
|
|
297
|
+
};
|
|
298
|
+
//# sourceMappingURL=dev-server-JCJGQ3PV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/dev-server.ts"],"sourcesContent":["import path from 'path'\nimport fs from 'fs-extra'\nimport { GitWorktreeManager } from '../lib/GitWorktreeManager.js'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport { DevServerManager } from '../lib/DevServerManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { IdentifierParser } from '../utils/IdentifierParser.js'\nimport { parseEnvFile, extractPort, findEnvFileContainingVariable } from '../utils/env.js'\nimport { calculatePortForBranch } from '../utils/port.js'\nimport { extractIssueNumber } from '../utils/git.js'\nimport { logger } from '../utils/logger.js'\nimport { extractSettingsOverrides } from '../utils/cli-overrides.js'\nimport type { GitWorktree } from '../types/worktree.js'\n\nexport interface DevServerCommandInput {\n\tidentifier?: string | undefined\n\tjson?: boolean | undefined\n}\n\nexport interface DevServerResult {\n\tstatus: 'started' | 'already_running' | 'no_web_capability'\n\turl?: string\n\tport?: number\n\tpid?: number\n\tmessage: string\n}\n\ninterface ParsedDevServerInput {\n\ttype: 'issue' | 'pr' | 'branch'\n\tnumber?: string | number\n\tbranchName?: string\n\toriginalInput: string\n\tautoDetected: boolean\n}\n\n/**\n * DevServerCommand - Start dev server for workspace in foreground mode\n * Runs in foreground (blocking terminal until user stops it)\n */\nexport class DevServerCommand {\n\tconstructor(\n\t\tprivate gitWorktreeManager = new GitWorktreeManager(),\n\t\tprivate capabilityDetector = new ProjectCapabilityDetector(),\n\t\tprivate identifierParser = new IdentifierParser(new GitWorktreeManager()),\n\t\tprivate devServerManager = new DevServerManager(),\n\t\tprivate settingsManager = new SettingsManager()\n\t) {}\n\n\t/**\n\t * Output JSON to stdout (used for --json flag)\n\t */\n\tprivate outputJson(data: DevServerResult | Record<string, unknown>): void {\n\t\tprocess.stdout.write(JSON.stringify(data, null, 2) + '\\n')\n\t}\n\n\tasync execute(input: DevServerCommandInput): Promise<DevServerResult> {\n\t\t// 1. Parse or auto-detect identifier\n\t\tconst parsed = input.identifier\n\t\t\t? await this.parseExplicitInput(input.identifier)\n\t\t\t: await this.autoDetectFromCurrentDirectory()\n\n\t\tlogger.debug(`Parsed input: ${JSON.stringify(parsed)}`)\n\n\t\t// 2. Find worktree path based on identifier\n\t\tconst worktree = await this.findWorktreeForIdentifier(parsed)\n\n\t\tlogger.debug(`Found worktree at: ${worktree.path}`)\n\n\t\t// 3. Detect project capabilities\n\t\tconst { capabilities } =\n\t\t\tawait this.capabilityDetector.detectCapabilities(worktree.path)\n\n\t\tlogger.debug(`Detected capabilities: ${capabilities.join(', ')}`)\n\n\t\t// 4. If no web capability, return gracefully with info message\n\t\tif (!capabilities.includes('web')) {\n\t\t\tconst message = 'No web capability detected in this workspace. Dev server not started.'\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'no_web_capability',\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 5. Get port for workspace\n\t\tconst port = await this.getWorkspacePort(worktree.path)\n\t\tconst url = `http://localhost:${port}`\n\n\t\t// 6. Check if server already running\n\t\tconst isRunning = await this.devServerManager.isServerRunning(port)\n\n\t\tif (isRunning) {\n\t\t\tconst message = `Dev server already running at ${url}`\n\t\t\tif (input.json) {\n\t\t\t\tthis.outputJson({\n\t\t\t\t\tstatus: 'already_running',\n\t\t\t\t\turl,\n\t\t\t\t\tport,\n\t\t\t\t\tmessage,\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlogger.info(message)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tstatus: 'already_running',\n\t\t\t\turl,\n\t\t\t\tport,\n\t\t\t\tmessage,\n\t\t\t}\n\t\t}\n\n\t\t// 7. Start server in foreground\n\t\tconst message = `Starting dev server at ${url}`\n\t\tif (!input.json) {\n\t\t\tlogger.info(message)\n\t\t}\n\n\t\tlet finalResult: DevServerResult = {\n\t\t\tstatus: 'started',\n\t\t\turl,\n\t\t\tport,\n\t\t\tmessage,\n\t\t}\n\n\t\t// This will block until user stops the server (Ctrl+C)\n\t\t// In JSON mode, redirect npm output to stderr so JSON can go to stdout\n\t\tconst processInfo = await this.devServerManager.runServerForeground(\n\t\t\tworktree.path,\n\t\t\tport,\n\t\t\t!!input.json,\n\t\t\t// Callback called immediately when process starts (for JSON output)\n\t\t\t(pid) => {\n\t\t\t\tif (input.json && pid) {\n\t\t\t\t\tfinalResult.pid = pid\n\t\t\t\t\tthis.outputJson(finalResult)\n\t\t\t\t}\n\t\t\t}\n\t\t)\n\n\t\tif (processInfo.pid) {\n\t\t\tfinalResult.pid = processInfo.pid\n\t\t}\n\n\t\treturn finalResult\n\t}\n\n\t/**\n\t * Parse explicit identifier input\n\t */\n\tprivate async parseExplicitInput(identifier: string): Promise<ParsedDevServerInput> {\n\t\tconst parsed = await this.identifierParser.parseForPatternDetection(identifier)\n\n\t\t// Description type should never reach dev-server command\n\t\tif (parsed.type === 'description') {\n\t\t\tthrow new Error('Description input type is not supported in dev-server command')\n\t\t}\n\n\t\tconst result: ParsedDevServerInput = {\n\t\t\ttype: parsed.type,\n\t\t\toriginalInput: parsed.originalInput,\n\t\t\tautoDetected: false,\n\t\t}\n\n\t\tif (parsed.number !== undefined) {\n\t\t\tresult.number = parsed.number\n\t\t}\n\t\tif (parsed.branchName !== undefined) {\n\t\t\tresult.branchName = parsed.branchName\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * Auto-detect identifier from current directory\n\t */\n\tprivate async autoDetectFromCurrentDirectory(): Promise<ParsedDevServerInput> {\n\t\tconst currentDir = path.basename(process.cwd())\n\n\t\t// Check for PR worktree pattern: _pr_N suffix\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = currentDir.match(prPattern)\n\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tlogger.debug(`Auto-detected PR #${prNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'pr',\n\t\t\t\tnumber: prNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Check for issue pattern in directory\n\t\tconst issueNumber = extractIssueNumber(currentDir)\n\n\t\tif (issueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${issueNumber} from directory: ${currentDir}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: issueNumber,\n\t\t\t\toriginalInput: currentDir,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Fallback: get current branch name\n\t\tconst repoInfo = await this.gitWorktreeManager.getRepoInfo()\n\t\tconst currentBranch = repoInfo.currentBranch\n\n\t\tif (!currentBranch) {\n\t\t\tthrow new Error(\n\t\t\t\t'Could not auto-detect identifier. Please provide an issue number, PR number, or branch name.\\n' +\n\t\t\t\t\t'Expected directory pattern: feat/issue-XX-description OR worktree with _pr_N suffix'\n\t\t\t)\n\t\t}\n\n\t\t// Try to extract issue from branch name\n\t\tconst branchIssueNumber = extractIssueNumber(currentBranch)\n\t\tif (branchIssueNumber !== null) {\n\t\t\tlogger.debug(`Auto-detected issue #${branchIssueNumber} from branch: ${currentBranch}`)\n\t\t\treturn {\n\t\t\t\ttype: 'issue',\n\t\t\t\tnumber: branchIssueNumber,\n\t\t\t\toriginalInput: currentBranch,\n\t\t\t\tautoDetected: true,\n\t\t\t}\n\t\t}\n\n\t\t// Last resort: use branch name\n\t\treturn {\n\t\t\ttype: 'branch',\n\t\t\tbranchName: currentBranch,\n\t\t\toriginalInput: currentBranch,\n\t\t\tautoDetected: true,\n\t\t}\n\t}\n\n\t/**\n\t * Find worktree for the given identifier\n\t */\n\tprivate async findWorktreeForIdentifier(parsed: ParsedDevServerInput): Promise<GitWorktree> {\n\t\tlet worktree: GitWorktree | null = null\n\n\t\tif (parsed.type === 'issue' && parsed.number !== undefined) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForIssue(parsed.number)\n\t\t} else if (parsed.type === 'pr' && parsed.number !== undefined) {\n\t\t\tconst prNumber = typeof parsed.number === 'number' ? parsed.number : Number(parsed.number)\n\t\t\tif (isNaN(prNumber) || !isFinite(prNumber)) {\n\t\t\t\tthrow new Error(`Invalid PR number: ${parsed.number}. PR numbers must be numeric.`)\n\t\t\t}\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForPR(prNumber, '')\n\t\t} else if (parsed.type === 'branch' && parsed.branchName) {\n\t\t\tworktree = await this.gitWorktreeManager.findWorktreeForBranch(\n\t\t\t\tparsed.branchName\n\t\t\t)\n\t\t}\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(\n\t\t\t\t`No worktree found for ${this.formatParsedInput(parsed)}. ` +\n\t\t\t\t\t`Run 'il start ${parsed.originalInput}' to create one.`\n\t\t\t)\n\t\t}\n\n\t\treturn worktree\n\t}\n\n\t/**\n\t * Format parsed input for display\n\t */\n\tprivate formatParsedInput(parsed: ParsedDevServerInput): string {\n\t\tconst autoLabel = parsed.autoDetected ? ' (auto-detected)' : ''\n\n\t\tif (parsed.type === 'issue') {\n\t\t\treturn `issue #${parsed.number}${autoLabel}`\n\t\t}\n\t\tif (parsed.type === 'pr') {\n\t\t\treturn `PR #${parsed.number}${autoLabel}`\n\t\t}\n\t\treturn `branch \"${parsed.branchName}\"${autoLabel}`\n\t}\n\n\t/**\n\t * Get port for workspace - reads from dotenv-flow files or calculates based on workspace type\n\t */\n\tprivate async getWorkspacePort(worktreePath: string): Promise<number> {\n\t\t// Load base port from settings with CLI overrides\n\t\tconst cliOverrides = extractSettingsOverrides()\n\t\tconst settings = await this.settingsManager.loadSettings(undefined, cliOverrides)\n\t\tconst basePort = settings.capabilities?.web?.basePort ?? 3000\n\n\t\t// Try to read PORT from any dotenv-flow file (as override)\n\t\tconst envFile = await findEnvFileContainingVariable(\n\t\t\tworktreePath,\n\t\t\t'PORT',\n\t\t\tasync (p) => fs.pathExists(p),\n\t\t\tasync (p, varName) => {\n\t\t\t\tconst content = await fs.readFile(p, 'utf8')\n\t\t\t\tconst envMap = parseEnvFile(content)\n\t\t\t\treturn envMap.get(varName) ?? null\n\t\t\t}\n\t\t)\n\n\t\tif (envFile) {\n\t\t\tconst envPath = path.join(worktreePath, envFile)\n\t\t\tconst envContent = await fs.readFile(envPath, 'utf8')\n\t\t\tconst envMap = parseEnvFile(envContent)\n\t\t\tconst port = extractPort(envMap)\n\n\t\t\tif (port) {\n\t\t\t\tlogger.debug(`Using PORT from ${envFile}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t}\n\n\t\t// PORT not in any dotenv-flow file, calculate based on workspace identifier\n\t\tlogger.debug('PORT not found in any dotenv-flow file, calculating from workspace identifier')\n\n\t\t// Get worktree to determine type\n\t\tconst worktrees = await this.gitWorktreeManager.listWorktrees()\n\t\tconst worktree = worktrees.find(wt => wt.path === worktreePath)\n\n\t\tif (!worktree) {\n\t\t\tthrow new Error(`Could not find worktree for path: ${worktreePath}`)\n\t\t}\n\n\t\t// Extract identifier from worktree path/branch\n\t\tconst dirName = path.basename(worktreePath)\n\n\t\t// Check for PR pattern: _pr_N\n\t\tconst prPattern = /_pr_(\\d+)$/\n\t\tconst prMatch = dirName.match(prPattern)\n\t\tif (prMatch?.[1]) {\n\t\t\tconst prNumber = parseInt(prMatch[1], 10)\n\t\t\tconst port = basePort + prNumber\n\t\t\tlogger.debug(`Calculated PORT for PR #${prNumber}: ${port}`)\n\t\t\treturn port\n\t\t}\n\n\t\t// Check for issue pattern: issue-N\n\t\tconst issueId = extractIssueNumber(dirName) ?? extractIssueNumber(worktree.branch)\n\t\tif (issueId !== null) {\n\t\t\tconst issueNumber = parseInt(issueId, 10)\n\t\t\tif (!isNaN(issueNumber)) {\n\t\t\t\tconst port = basePort + issueNumber\n\t\t\t\tlogger.debug(`Calculated PORT for issue #${issueId}: ${port}`)\n\t\t\t\treturn port\n\t\t\t}\n\t\t\t// For alphanumeric IDs, fall through to branch-based hash\n\t\t}\n\n\t\t// Branch-based workspace - use deterministic hash\n\t\tconst port = calculatePortForBranch(worktree.branch, basePort)\n\t\tlogger.debug(`Calculated PORT for branch \"${worktree.branch}\": ${port}`)\n\t\treturn port\n\t}\n}\n"],"mappings":"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","names":["message","port"]}
|
|
@@ -2,31 +2,32 @@
|
|
|
2
2
|
import {
|
|
3
3
|
IssueEnhancementService,
|
|
4
4
|
capitalizeFirstLetter
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-YETJNRQM.js";
|
|
5
|
+
} from "./chunk-VTXCGKV5.js";
|
|
7
6
|
import {
|
|
8
7
|
AgentManager
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-O7WHXLCB.js";
|
|
9
|
+
import "./chunk-YETJNRQM.js";
|
|
10
10
|
import {
|
|
11
11
|
ProjectCapabilityDetector
|
|
12
12
|
} from "./chunk-EBISESAP.js";
|
|
13
13
|
import "./chunk-2ZPFJQ3B.js";
|
|
14
14
|
import {
|
|
15
15
|
SettingsManager
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VWNS6DH5.js";
|
|
17
17
|
import {
|
|
18
18
|
GitHubService
|
|
19
|
-
} from "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
19
|
+
} from "./chunk-OEGECBFS.js";
|
|
20
|
+
import "./chunk-KO2FOMHL.js";
|
|
21
|
+
import "./chunk-SJ2GZ6RF.js";
|
|
22
22
|
import {
|
|
23
23
|
getClaudeVersion
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-RUC7OULH.js";
|
|
25
|
+
import "./chunk-VAYGNQTE.js";
|
|
26
|
+
import "./chunk-Z5NXYJIG.js";
|
|
27
|
+
import "./chunk-6UIGZD2N.js";
|
|
27
28
|
import {
|
|
28
29
|
logger
|
|
29
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-UYVWLISQ.js";
|
|
30
31
|
|
|
31
32
|
// src/utils/diagnostics.ts
|
|
32
33
|
import { readFile } from "fs/promises";
|
|
@@ -160,4 +161,4 @@ ${userBody}`;
|
|
|
160
161
|
export {
|
|
161
162
|
FeedbackCommand
|
|
162
163
|
};
|
|
163
|
-
//# sourceMappingURL=feedback-
|
|
164
|
+
//# sourceMappingURL=feedback-7PVBQNLJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/diagnostics.ts","../src/commands/feedback.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { join, dirname } from 'path'\nimport { fileURLToPath } from 'url'\nimport { platform, release, arch } from 'os'\nimport { logger } from './logger'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport type { ProjectCapability } from '../types/loom.js'\nimport { getClaudeVersion } from './claude.js'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * Diagnostic information gathered from the system\n */\nexport interface DiagnosticInfo {\n\tcliVersion: string\n\tnodeVersion: string\n\tosType: string\n\tosVersion: string\n\tarchitecture: string\n\tcapabilities: ProjectCapability[]\n\tclaudeVersion: string | null\n}\n\n/**\n * Gathers diagnostic information about the CLI environment.\n * Fails gracefully with fallback messages if any information cannot be gathered.\n */\nexport async function gatherDiagnosticInfo(): Promise<DiagnosticInfo> {\n\t// Instantiate detector for current directory\n\tconst detector = new ProjectCapabilityDetector()\n\tconst { capabilities } = await detector.detectCapabilities(process.cwd())\n\n\t// Get Claude CLI version (returns null if not available)\n\tconst claudeVersion = await getClaudeVersion()\n\n\tconst diagnostics: DiagnosticInfo = {\n\t\tcliVersion: await getCliVersion(),\n\t\tnodeVersion: getNodeVersion(),\n\t\tosType: getOsType(),\n\t\tosVersion: getOsVersion(),\n\t\tarchitecture: getArchitecture(),\n\t\tcapabilities,\n\t\tclaudeVersion,\n\t}\n\n\treturn diagnostics\n}\n\n/**\n * Formats diagnostic information as markdown for inclusion in GitHub issues\n */\nexport function formatDiagnosticsAsMarkdown(diagnostics: DiagnosticInfo, includeMarker = true): string {\n\tconst marker = includeMarker ? `<!-- CLI GENERATED FEEDBACK v${diagnostics.cliVersion} -->\\n` : ''\n\n\t// Format capabilities as comma-separated string or \"none\"\n\tconst capabilitiesDisplay = diagnostics.capabilities.length > 0\n\t\t? diagnostics.capabilities.join(', ')\n\t\t: 'none'\n\n\t// Format Claude version with fallback for null\n\tconst claudeVersionDisplay = diagnostics.claudeVersion ?? 'not available'\n\n\treturn `${marker}\n<details>\n<summary>Diagnostic Information</summary>\n\n| Property | Value |\n|----------|-------|\n| CLI Version | ${diagnostics.cliVersion} |\n| Node.js Version | ${diagnostics.nodeVersion} |\n| OS | ${diagnostics.osType} |\n| OS Version | ${diagnostics.osVersion} |\n| Architecture | ${diagnostics.architecture} |\n| Capabilities | ${capabilitiesDisplay} |\n| Claude CLI Version | ${claudeVersionDisplay} |\n\n</details>\n`\n}\n\n/**\n * Gets the CLI version from package.json.\n * Falls back to \"unknown\" if package.json cannot be read.\n */\nasync function getCliVersion(): Promise<string> {\n\ttry {\n\t\t// Navigate up from dist/ to root directory (same as cli.ts does)\n\t\tconst packageJsonPath = join(__dirname, '..', 'package.json')\n\t\tconst packageJson = await readFile(packageJsonPath, 'utf-8')\n\t\tconst parsed = JSON.parse(packageJson)\n\t\treturn parsed.version ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read CLI version from package.json: ', error)\n\t\treturn 'unknown (failed to read package.json)'\n\t}\n}\n\n/**\n * Gets the Node.js version.\n * Falls back to \"unknown\" if process.version is not available.\n */\nfunction getNodeVersion(): string {\n\ttry {\n\t\treturn process.version ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read Node.js version:', error)\n\t\treturn 'unknown (failed to read Node.js version)'\n\t}\n}\n\n/**\n * Gets the operating system type.\n * Falls back to \"unknown\" if os.platform() fails.\n */\nfunction getOsType(): string {\n\ttry {\n\t\treturn platform() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read OS type:', error)\n\t\treturn 'unknown (failed to detect OS)'\n\t}\n}\n\n/**\n * Gets the operating system version.\n * Falls back to \"unknown\" if os.release() fails.\n */\nfunction getOsVersion(): string {\n\ttry {\n\t\treturn release() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read OS version:', error)\n\t\treturn 'unknown (failed to detect OS version)'\n\t}\n}\n\n/**\n * Gets the system architecture.\n * Falls back to \"unknown\" if os.arch() fails.\n */\nfunction getArchitecture(): string {\n\ttry {\n\t\treturn arch() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read system architecture:', error)\n\t\treturn 'unknown (failed to detect architecture)'\n\t}\n}\n","import type { FeedbackOptions } from '../types/index.js'\nimport { IssueEnhancementService } from '../lib/IssueEnhancementService.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { gatherDiagnosticInfo, formatDiagnosticsAsMarkdown } from '../utils/diagnostics.js'\nimport { capitalizeFirstLetter } from '../utils/text.js'\n\n// Hardcoded target repository for feedback\nconst FEEDBACK_REPOSITORY = 'iloom-ai/iloom-cli'\n\n/**\n * Input structure for FeedbackCommand\n */\nexport interface FeedbackCommandInput {\n\tdescription: string\n\toptions: FeedbackOptions\n}\n\n/**\n * Command to submit feedback/bug reports to the iloom-cli repository.\n * Mirrors add-issue command but targets iloom-ai/iloom-cli repo.\n */\nexport class FeedbackCommand {\n\tprivate enhancementService: IssueEnhancementService\n\n\tconstructor(enhancementService?: IssueEnhancementService) {\n\t\t// Use provided service or create default\n\t\tthis.enhancementService = enhancementService ?? new IssueEnhancementService(\n\t\t\tnew GitHubService(),\n\t\t\tnew AgentManager(),\n\t\t\tnew SettingsManager()\n\t\t)\n\t}\n\n\t/**\n\t * Execute the feedback command workflow:\n\t * 1. Validate description format\n\t * 2. Gather diagnostic information\n\t * 3. Create GitHub issue in iloom-ai/iloom-cli with CLI marker and diagnostics\n\t * 4. Wait for keypress and open browser for review\n\t * 5. Return issue number\n\t */\n\tpublic async execute(input: FeedbackCommandInput): Promise<string | number> {\n\t\t// Apply first-letter capitalization to title (description) and body\n\t\tconst description = capitalizeFirstLetter(input.description)\n\t\tconst body = input.options.body ? capitalizeFirstLetter(input.options.body) : undefined\n\n\t\t// Step 1: Validate description format\n\t\tif (!description || !this.enhancementService.validateDescription(description)) {\n\t\t\tthrow new Error('Description is required and must be more than 30 characters with at least 3 words')\n\t\t}\n\n\t\t// Step 2: Gather diagnostic information\n\t\tconst diagnostics = await gatherDiagnosticInfo()\n\t\tconst diagnosticsMarkdown = formatDiagnosticsAsMarkdown(diagnostics)\n\n\t\t// Step 3: Create enhanced issue body with marker and diagnostics\n\t\tconst userBody = body ?? description\n\t\tconst enhancedBody = `${diagnosticsMarkdown}\n\n${userBody}`\n\n\t\t// Step 4: Create GitHub issue in iloom-cli repo (no label needed)\n\t\t// The GitHub Action workflow will detect the CLI marker and enhance the issue\n\t\tconst result = await this.enhancementService.createEnhancedIssue(\n\t\t\tdescription,\n\t\t\tenhancedBody,\n\t\t\tFEEDBACK_REPOSITORY,\n\t\t\tundefined // No labels needed\n\t\t)\n\n\t\t// Step 5: Wait for keypress and open issue in browser for review\n\t\tawait this.enhancementService.waitForReviewAndOpen(result.number, false, FEEDBACK_REPOSITORY)\n\n\t\t// Step 6: Return issue number for reference\n\t\treturn result.number\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/diagnostics.ts","../src/commands/feedback.ts"],"sourcesContent":["import { readFile } from 'fs/promises'\nimport { join, dirname } from 'path'\nimport { fileURLToPath } from 'url'\nimport { platform, release, arch } from 'os'\nimport { logger } from './logger'\nimport { ProjectCapabilityDetector } from '../lib/ProjectCapabilityDetector.js'\nimport type { ProjectCapability } from '../types/loom.js'\nimport { getClaudeVersion } from './claude.js'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\n/**\n * Diagnostic information gathered from the system\n */\nexport interface DiagnosticInfo {\n\tcliVersion: string\n\tnodeVersion: string\n\tosType: string\n\tosVersion: string\n\tarchitecture: string\n\tcapabilities: ProjectCapability[]\n\tclaudeVersion: string | null\n}\n\n/**\n * Gathers diagnostic information about the CLI environment.\n * Fails gracefully with fallback messages if any information cannot be gathered.\n */\nexport async function gatherDiagnosticInfo(): Promise<DiagnosticInfo> {\n\t// Instantiate detector for current directory\n\tconst detector = new ProjectCapabilityDetector()\n\tconst { capabilities } = await detector.detectCapabilities(process.cwd())\n\n\t// Get Claude CLI version (returns null if not available)\n\tconst claudeVersion = await getClaudeVersion()\n\n\tconst diagnostics: DiagnosticInfo = {\n\t\tcliVersion: await getCliVersion(),\n\t\tnodeVersion: getNodeVersion(),\n\t\tosType: getOsType(),\n\t\tosVersion: getOsVersion(),\n\t\tarchitecture: getArchitecture(),\n\t\tcapabilities,\n\t\tclaudeVersion,\n\t}\n\n\treturn diagnostics\n}\n\n/**\n * Formats diagnostic information as markdown for inclusion in GitHub issues\n */\nexport function formatDiagnosticsAsMarkdown(diagnostics: DiagnosticInfo, includeMarker = true): string {\n\tconst marker = includeMarker ? `<!-- CLI GENERATED FEEDBACK v${diagnostics.cliVersion} -->\\n` : ''\n\n\t// Format capabilities as comma-separated string or \"none\"\n\tconst capabilitiesDisplay = diagnostics.capabilities.length > 0\n\t\t? diagnostics.capabilities.join(', ')\n\t\t: 'none'\n\n\t// Format Claude version with fallback for null\n\tconst claudeVersionDisplay = diagnostics.claudeVersion ?? 'not available'\n\n\treturn `${marker}\n<details>\n<summary>Diagnostic Information</summary>\n\n| Property | Value |\n|----------|-------|\n| CLI Version | ${diagnostics.cliVersion} |\n| Node.js Version | ${diagnostics.nodeVersion} |\n| OS | ${diagnostics.osType} |\n| OS Version | ${diagnostics.osVersion} |\n| Architecture | ${diagnostics.architecture} |\n| Capabilities | ${capabilitiesDisplay} |\n| Claude CLI Version | ${claudeVersionDisplay} |\n\n</details>\n`\n}\n\n/**\n * Gets the CLI version from package.json.\n * Falls back to \"unknown\" if package.json cannot be read.\n */\nasync function getCliVersion(): Promise<string> {\n\ttry {\n\t\t// Navigate up from dist/ to root directory (same as cli.ts does)\n\t\tconst packageJsonPath = join(__dirname, '..', 'package.json')\n\t\tconst packageJson = await readFile(packageJsonPath, 'utf-8')\n\t\tconst parsed = JSON.parse(packageJson)\n\t\treturn parsed.version ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read CLI version from package.json: ', error)\n\t\treturn 'unknown (failed to read package.json)'\n\t}\n}\n\n/**\n * Gets the Node.js version.\n * Falls back to \"unknown\" if process.version is not available.\n */\nfunction getNodeVersion(): string {\n\ttry {\n\t\treturn process.version ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read Node.js version:', error)\n\t\treturn 'unknown (failed to read Node.js version)'\n\t}\n}\n\n/**\n * Gets the operating system type.\n * Falls back to \"unknown\" if os.platform() fails.\n */\nfunction getOsType(): string {\n\ttry {\n\t\treturn platform() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read OS type:', error)\n\t\treturn 'unknown (failed to detect OS)'\n\t}\n}\n\n/**\n * Gets the operating system version.\n * Falls back to \"unknown\" if os.release() fails.\n */\nfunction getOsVersion(): string {\n\ttry {\n\t\treturn release() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read OS version:', error)\n\t\treturn 'unknown (failed to detect OS version)'\n\t}\n}\n\n/**\n * Gets the system architecture.\n * Falls back to \"unknown\" if os.arch() fails.\n */\nfunction getArchitecture(): string {\n\ttry {\n\t\treturn arch() ?? 'unknown'\n\t} catch (error) {\n\t\tlogger.debug('Failed to read system architecture:', error)\n\t\treturn 'unknown (failed to detect architecture)'\n\t}\n}\n","import type { FeedbackOptions } from '../types/index.js'\nimport { IssueEnhancementService } from '../lib/IssueEnhancementService.js'\nimport { GitHubService } from '../lib/GitHubService.js'\nimport { AgentManager } from '../lib/AgentManager.js'\nimport { SettingsManager } from '../lib/SettingsManager.js'\nimport { gatherDiagnosticInfo, formatDiagnosticsAsMarkdown } from '../utils/diagnostics.js'\nimport { capitalizeFirstLetter } from '../utils/text.js'\n\n// Hardcoded target repository for feedback\nconst FEEDBACK_REPOSITORY = 'iloom-ai/iloom-cli'\n\n/**\n * Input structure for FeedbackCommand\n */\nexport interface FeedbackCommandInput {\n\tdescription: string\n\toptions: FeedbackOptions\n}\n\n/**\n * Command to submit feedback/bug reports to the iloom-cli repository.\n * Mirrors add-issue command but targets iloom-ai/iloom-cli repo.\n */\nexport class FeedbackCommand {\n\tprivate enhancementService: IssueEnhancementService\n\n\tconstructor(enhancementService?: IssueEnhancementService) {\n\t\t// Use provided service or create default\n\t\tthis.enhancementService = enhancementService ?? new IssueEnhancementService(\n\t\t\tnew GitHubService(),\n\t\t\tnew AgentManager(),\n\t\t\tnew SettingsManager()\n\t\t)\n\t}\n\n\t/**\n\t * Execute the feedback command workflow:\n\t * 1. Validate description format\n\t * 2. Gather diagnostic information\n\t * 3. Create GitHub issue in iloom-ai/iloom-cli with CLI marker and diagnostics\n\t * 4. Wait for keypress and open browser for review\n\t * 5. Return issue number\n\t */\n\tpublic async execute(input: FeedbackCommandInput): Promise<string | number> {\n\t\t// Apply first-letter capitalization to title (description) and body\n\t\tconst description = capitalizeFirstLetter(input.description)\n\t\tconst body = input.options.body ? capitalizeFirstLetter(input.options.body) : undefined\n\n\t\t// Step 1: Validate description format\n\t\tif (!description || !this.enhancementService.validateDescription(description)) {\n\t\t\tthrow new Error('Description is required and must be more than 30 characters with at least 3 words')\n\t\t}\n\n\t\t// Step 2: Gather diagnostic information\n\t\tconst diagnostics = await gatherDiagnosticInfo()\n\t\tconst diagnosticsMarkdown = formatDiagnosticsAsMarkdown(diagnostics)\n\n\t\t// Step 3: Create enhanced issue body with marker and diagnostics\n\t\tconst userBody = body ?? description\n\t\tconst enhancedBody = `${diagnosticsMarkdown}\n\n${userBody}`\n\n\t\t// Step 4: Create GitHub issue in iloom-cli repo (no label needed)\n\t\t// The GitHub Action workflow will detect the CLI marker and enhance the issue\n\t\tconst result = await this.enhancementService.createEnhancedIssue(\n\t\t\tdescription,\n\t\t\tenhancedBody,\n\t\t\tFEEDBACK_REPOSITORY,\n\t\t\tundefined // No labels needed\n\t\t)\n\n\t\t// Step 5: Wait for keypress and open issue in browser for review\n\t\tawait this.enhancementService.waitForReviewAndOpen(result.number, false, FEEDBACK_REPOSITORY)\n\n\t\t// Step 6: Return issue number for reference\n\t\treturn result.number\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
branchExists,
|
|
4
|
+
checkRemoteBranchStatus,
|
|
4
5
|
ensureRepositoryHasCommits,
|
|
5
6
|
executeGitCommand,
|
|
6
7
|
extractIssueNumber,
|
|
@@ -8,25 +9,32 @@ import {
|
|
|
8
9
|
findAllBranchesForIssue,
|
|
9
10
|
findMainWorktreePath,
|
|
10
11
|
findMainWorktreePathWithSettings,
|
|
12
|
+
findWorktreeForBranch,
|
|
11
13
|
generateWorktreePath,
|
|
12
14
|
getCurrentBranch,
|
|
13
15
|
getDefaultBranch,
|
|
16
|
+
getMergeTargetBranch,
|
|
14
17
|
getRepoRoot,
|
|
15
18
|
getWorktreeRoot,
|
|
16
19
|
hasUncommittedChanges,
|
|
20
|
+
isBranchMergedIntoMain,
|
|
17
21
|
isEmptyRepository,
|
|
18
22
|
isFileGitignored,
|
|
19
23
|
isFileTrackedByGit,
|
|
20
24
|
isPRBranch,
|
|
25
|
+
isRemoteBranchUpToDate,
|
|
21
26
|
isValidGitRepo,
|
|
22
27
|
isWorktreePath,
|
|
23
28
|
parseWorktreeList,
|
|
24
29
|
pushBranchToRemote
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-
|
|
30
|
+
} from "./chunk-2W2FBL5G.js";
|
|
31
|
+
import "./chunk-IJ7IGJT3.js";
|
|
32
|
+
import "./chunk-VWNS6DH5.js";
|
|
33
|
+
import "./chunk-6UIGZD2N.js";
|
|
34
|
+
import "./chunk-UYVWLISQ.js";
|
|
28
35
|
export {
|
|
29
36
|
branchExists,
|
|
37
|
+
checkRemoteBranchStatus,
|
|
30
38
|
ensureRepositoryHasCommits,
|
|
31
39
|
executeGitCommand,
|
|
32
40
|
extractIssueNumber,
|
|
@@ -34,19 +42,23 @@ export {
|
|
|
34
42
|
findAllBranchesForIssue,
|
|
35
43
|
findMainWorktreePath,
|
|
36
44
|
findMainWorktreePathWithSettings,
|
|
45
|
+
findWorktreeForBranch,
|
|
37
46
|
generateWorktreePath,
|
|
38
47
|
getCurrentBranch,
|
|
39
48
|
getDefaultBranch,
|
|
49
|
+
getMergeTargetBranch,
|
|
40
50
|
getRepoRoot,
|
|
41
51
|
getWorktreeRoot,
|
|
42
52
|
hasUncommittedChanges,
|
|
53
|
+
isBranchMergedIntoMain,
|
|
43
54
|
isEmptyRepository,
|
|
44
55
|
isFileGitignored,
|
|
45
56
|
isFileTrackedByGit,
|
|
46
57
|
isPRBranch,
|
|
58
|
+
isRemoteBranchUpToDate,
|
|
47
59
|
isValidGitRepo,
|
|
48
60
|
isWorktreePath,
|
|
49
61
|
parseWorktreeList,
|
|
50
62
|
pushBranchToRemote
|
|
51
63
|
};
|
|
52
|
-
//# sourceMappingURL=git-
|
|
64
|
+
//# sourceMappingURL=git-4BVOOOOV.js.map
|