@iloom/cli 0.5.1 → 0.5.3
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 +2 -0
- package/dist/{BranchNamingService-GCCWB3LK.js → BranchNamingService-B5PVRR7F.js} +4 -4
- package/dist/ClaudeContextManager-PQ46VILL.js +14 -0
- package/dist/ClaudeService-6OMO552H.js +13 -0
- package/dist/GitHubService-S2OGUTDR.js +12 -0
- package/dist/{LoomLauncher-JNWBMHES.js → LoomLauncher-ZHDTPKED.js} +13 -16
- package/dist/{LoomLauncher-JNWBMHES.js.map → LoomLauncher-ZHDTPKED.js.map} +1 -1
- package/dist/MetadataManager-DFI73J3G.js +10 -0
- package/dist/PRManager-OCSB2HPT.js +14 -0
- package/dist/PromptTemplateManager-5GNF7FCP.js +9 -0
- package/dist/README.md +2 -0
- package/dist/{SettingsManager-XPR4TEQL.js → SettingsManager-CNYBGXDT.js} +3 -3
- package/dist/SettingsMigrationManager-KZKDG66H.js +10 -0
- package/dist/{chunk-OEGECBFS.js → chunk-3PT7RKL5.js} +4 -4
- package/dist/{chunk-WUQQNE63.js → chunk-433MOLAU.js} +44 -7
- package/dist/chunk-433MOLAU.js.map +1 -0
- package/dist/{chunk-LN4H3A6A.js → chunk-53OMUNUN.js} +5 -5
- package/dist/{chunk-THF25ICZ.js → chunk-5F6IWWRS.js} +2 -2
- package/dist/{chunk-P2ZQ5LKB.js → chunk-5IWU3HXE.js} +5 -5
- package/dist/{chunk-QIUJPPJQ.js → chunk-5TXLVEXT.js} +3 -3
- package/dist/{chunk-TSLKDFAF.js → chunk-66BMJ25W.js} +7 -7
- package/dist/{chunk-6UIGZD2N.js → chunk-6MLEBAYZ.js} +2 -2
- package/dist/{chunk-BVIK2P6P.js → chunk-7HIRPCKU.js} +4 -4
- package/dist/{chunk-UNXRACJ7.js → chunk-7LSSNB7Y.js} +3 -3
- package/dist/{chunk-QHA67Q7A.js → chunk-7Q66W4OH.js} +2 -2
- package/dist/{chunk-RUC7OULH.js → chunk-AEIMYF4P.js} +6 -8
- package/dist/{chunk-RUC7OULH.js.map → chunk-AEIMYF4P.js.map} +1 -1
- package/dist/{chunk-MD6HA5IK.js → chunk-B2UO6EYE.js} +2 -2
- package/dist/{chunk-YZTDGPFB.js → chunk-CFUWQHCJ.js} +2 -2
- package/dist/{chunk-UYWAESOT.js → chunk-F2PWIRV4.js} +3 -3
- package/dist/{chunk-PSFVTBM7.js → chunk-FXDYIV3K.js} +2 -2
- package/dist/{chunk-CDQEK2WD.js → chunk-FXJKNVZW.js} +5 -5
- package/dist/{chunk-3CMGCRB5.js → chunk-HMMO2LDS.js} +3 -3
- package/dist/{chunk-OOU3DKNT.js → chunk-IDUICCZY.js} +2 -2
- package/dist/{chunk-HABINPX2.js → chunk-J7GHNTYK.js} +67 -11
- package/dist/chunk-J7GHNTYK.js.map +1 -0
- package/dist/{chunk-DKQ4SUII.js → chunk-K5G5SFWY.js} +2 -2
- package/dist/chunk-K5G5SFWY.js.map +1 -0
- package/dist/{chunk-KO2FOMHL.js → chunk-LT3SGBR7.js} +2 -2
- package/dist/{chunk-VBFDVGAE.js → chunk-LVLRMP7V.js} +2 -2
- package/dist/{chunk-RNZMHJK7.js → chunk-N4ZJVATC.js} +3 -3
- package/dist/chunk-NXMDEL3F.js +54 -0
- package/dist/chunk-NXMDEL3F.js.map +1 -0
- package/dist/{chunk-CDF7ZX2B.js → chunk-O7VL5N6S.js} +2 -2
- package/dist/{chunk-S65T4O6I.js → chunk-QPS6TZUW.js} +3 -3
- package/dist/{chunk-RJKMF6BC.js → chunk-SHVB3EFE.js} +3 -3
- package/dist/chunk-VT4PDUYT.js +578 -0
- package/dist/chunk-VT4PDUYT.js.map +1 -0
- package/dist/{chunk-4YTILIIH.js → chunk-VV66DH6T.js} +8 -8
- package/dist/{chunk-GVRO4PWE.js → chunk-XNNXAAZT.js} +6 -6
- package/dist/{chunk-AS2IRKLU.js → chunk-YU5HVI6B.js} +2 -2
- package/dist/{chunk-6KB7R22U.js → chunk-Z5BM4JWB.js} +25 -24
- package/dist/chunk-Z5BM4JWB.js.map +1 -0
- package/dist/{chunk-SJ2GZ6RF.js → chunk-ZX3GTM7O.js} +2 -2
- package/dist/{claude-ACVXNB6N.js → claude-H33OQMXO.js} +4 -6
- package/dist/{cleanup-LU6NU2NZ.js → cleanup-Y5W3CNUV.js} +20 -22
- package/dist/{cleanup-LU6NU2NZ.js.map → cleanup-Y5W3CNUV.js.map} +1 -1
- package/dist/cli.js +71 -73
- package/dist/cli.js.map +1 -1
- package/dist/{color-ZPIIUADB.js → color-4TJ4P5EY.js} +5 -3
- package/dist/{contribute-RS3DO3WP.js → contribute-SMIPMWCH.js} +9 -9
- package/dist/{contribute-RS3DO3WP.js.map → contribute-SMIPMWCH.js.map} +1 -1
- package/dist/{dev-server-ASH7HJVI.js → dev-server-HNBRWGCD.js} +11 -13
- package/dist/{dev-server-ASH7HJVI.js.map → dev-server-HNBRWGCD.js.map} +1 -1
- package/dist/{feedback-OFVW22UW.js → feedback-567ZH2O7.js} +11 -13
- package/dist/{feedback-OFVW22UW.js.map → feedback-567ZH2O7.js.map} +1 -1
- package/dist/{git-OQAPUPLP.js → git-OV6ADVO7.js} +6 -6
- package/dist/{ignite-NREQ3JRM.js → ignite-3HB3ZBEW.js} +15 -17
- package/dist/{ignite-NREQ3JRM.js.map → ignite-3HB3ZBEW.js.map} +1 -1
- package/dist/index.d.ts +54 -35
- package/dist/index.js +371 -276
- package/dist/index.js.map +1 -1
- package/dist/init-CMIRHFSR.js +19 -0
- package/dist/{installation-detector-6R6YOFVZ.js → installation-detector-VXZOCL6P.js} +3 -3
- package/dist/mcp/issue-management-server.js +62 -7
- package/dist/mcp/issue-management-server.js.map +1 -1
- package/dist/neon-helpers-3KBC4A3Y.js +11 -0
- package/dist/{open-KW4NTLXH.js → open-AXE225Z5.js} +11 -13
- package/dist/{open-KW4NTLXH.js.map → open-AXE225Z5.js.map} +1 -1
- package/dist/{projects-QEAEBAT2.js → projects-GVEMCN5R.js} +4 -4
- package/dist/{prompt-A7GGRHSY.js → prompt-3SAZYRUN.js} +3 -3
- package/dist/prompts/session-summary-prompt.txt +58 -4
- package/dist/{rebase-WZHHE5LU.js → rebase-6UIHMUWS.js} +9 -11
- package/dist/{rebase-WZHHE5LU.js.map → rebase-6UIHMUWS.js.map} +1 -1
- package/dist/{recap-33NPZ3ZO.js → recap-XTBNMEMO.js} +12 -19
- package/dist/recap-XTBNMEMO.js.map +1 -0
- package/dist/{remote-73TZ2ADI.js → remote-IJAMOEAP.js} +3 -3
- package/dist/{run-HRYQ7TR7.js → run-H375EYRB.js} +11 -13
- package/dist/{run-HRYQ7TR7.js.map → run-H375EYRB.js.map} +1 -1
- package/dist/{shell-JMU5XTHW.js → shell-33FJCWJQ.js} +9 -11
- package/dist/{shell-JMU5XTHW.js.map → shell-33FJCWJQ.js.map} +1 -1
- package/dist/{summary-4SSGGH7N.js → summary-JUMOCNLR.js} +14 -15
- package/dist/{summary-4SSGGH7N.js.map → summary-JUMOCNLR.js.map} +1 -1
- package/dist/{test-git-6SAIRBUD.js → test-git-CO3BA4BV.js} +6 -6
- package/dist/{test-prefix-RLVRK5ZD.js → test-prefix-HZYSDQYT.js} +6 -6
- package/dist/{test-tabs-3SCJWRKT.js → test-tabs-D3POYOJ5.js} +3 -6
- package/dist/{test-tabs-3SCJWRKT.js.map → test-tabs-D3POYOJ5.js.map} +1 -1
- package/dist/{test-webserver-VPNLAFZ3.js → test-webserver-YVQD42W6.js} +2 -2
- package/dist/{update-LETF5ASC.js → update-5NOHT4SG.js} +4 -4
- package/dist/{update-notifier-H55ZK7NU.js → update-notifier-ARA5SPUW.js} +3 -3
- package/package.json +1 -1
- package/dist/ClaudeContextManager-DQFKIMEP.js +0 -16
- package/dist/ClaudeService-CJS32WG2.js +0 -15
- package/dist/GitHubService-RPM27GWD.js +0 -12
- package/dist/MetadataManager-WXUVXKUS.js +0 -10
- package/dist/PRManager-7DSIMCAD.js +0 -16
- package/dist/PromptTemplateManager-72FEOGT6.js +0 -9
- package/dist/SettingsMigrationManager-EH3J2TCN.js +0 -10
- package/dist/chunk-6KB7R22U.js.map +0 -1
- package/dist/chunk-DKQ4SUII.js.map +0 -1
- package/dist/chunk-HABINPX2.js.map +0 -1
- package/dist/chunk-UYVWLISQ.js +0 -113
- package/dist/chunk-UYVWLISQ.js.map +0 -1
- package/dist/chunk-VAYGNQTE.js +0 -234
- package/dist/chunk-VAYGNQTE.js.map +0 -1
- package/dist/chunk-WUQQNE63.js.map +0 -1
- package/dist/chunk-Z5NXYJIG.js +0 -207
- package/dist/chunk-Z5NXYJIG.js.map +0 -1
- package/dist/init-F6PFMSU5.js +0 -21
- package/dist/neon-helpers-L5CXQ5CT.js +0 -11
- package/dist/recap-33NPZ3ZO.js.map +0 -1
- /package/dist/{BranchNamingService-GCCWB3LK.js.map → BranchNamingService-B5PVRR7F.js.map} +0 -0
- /package/dist/{ClaudeContextManager-DQFKIMEP.js.map → ClaudeContextManager-PQ46VILL.js.map} +0 -0
- /package/dist/{ClaudeService-CJS32WG2.js.map → ClaudeService-6OMO552H.js.map} +0 -0
- /package/dist/{GitHubService-RPM27GWD.js.map → GitHubService-S2OGUTDR.js.map} +0 -0
- /package/dist/{MetadataManager-WXUVXKUS.js.map → MetadataManager-DFI73J3G.js.map} +0 -0
- /package/dist/{PRManager-7DSIMCAD.js.map → PRManager-OCSB2HPT.js.map} +0 -0
- /package/dist/{PromptTemplateManager-72FEOGT6.js.map → PromptTemplateManager-5GNF7FCP.js.map} +0 -0
- /package/dist/{SettingsManager-XPR4TEQL.js.map → SettingsManager-CNYBGXDT.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-EH3J2TCN.js.map → SettingsMigrationManager-KZKDG66H.js.map} +0 -0
- /package/dist/{chunk-OEGECBFS.js.map → chunk-3PT7RKL5.js.map} +0 -0
- /package/dist/{chunk-LN4H3A6A.js.map → chunk-53OMUNUN.js.map} +0 -0
- /package/dist/{chunk-THF25ICZ.js.map → chunk-5F6IWWRS.js.map} +0 -0
- /package/dist/{chunk-P2ZQ5LKB.js.map → chunk-5IWU3HXE.js.map} +0 -0
- /package/dist/{chunk-QIUJPPJQ.js.map → chunk-5TXLVEXT.js.map} +0 -0
- /package/dist/{chunk-TSLKDFAF.js.map → chunk-66BMJ25W.js.map} +0 -0
- /package/dist/{chunk-6UIGZD2N.js.map → chunk-6MLEBAYZ.js.map} +0 -0
- /package/dist/{chunk-BVIK2P6P.js.map → chunk-7HIRPCKU.js.map} +0 -0
- /package/dist/{chunk-UNXRACJ7.js.map → chunk-7LSSNB7Y.js.map} +0 -0
- /package/dist/{chunk-QHA67Q7A.js.map → chunk-7Q66W4OH.js.map} +0 -0
- /package/dist/{chunk-MD6HA5IK.js.map → chunk-B2UO6EYE.js.map} +0 -0
- /package/dist/{chunk-YZTDGPFB.js.map → chunk-CFUWQHCJ.js.map} +0 -0
- /package/dist/{chunk-UYWAESOT.js.map → chunk-F2PWIRV4.js.map} +0 -0
- /package/dist/{chunk-PSFVTBM7.js.map → chunk-FXDYIV3K.js.map} +0 -0
- /package/dist/{chunk-CDQEK2WD.js.map → chunk-FXJKNVZW.js.map} +0 -0
- /package/dist/{chunk-3CMGCRB5.js.map → chunk-HMMO2LDS.js.map} +0 -0
- /package/dist/{chunk-OOU3DKNT.js.map → chunk-IDUICCZY.js.map} +0 -0
- /package/dist/{chunk-KO2FOMHL.js.map → chunk-LT3SGBR7.js.map} +0 -0
- /package/dist/{chunk-VBFDVGAE.js.map → chunk-LVLRMP7V.js.map} +0 -0
- /package/dist/{chunk-RNZMHJK7.js.map → chunk-N4ZJVATC.js.map} +0 -0
- /package/dist/{chunk-CDF7ZX2B.js.map → chunk-O7VL5N6S.js.map} +0 -0
- /package/dist/{chunk-S65T4O6I.js.map → chunk-QPS6TZUW.js.map} +0 -0
- /package/dist/{chunk-RJKMF6BC.js.map → chunk-SHVB3EFE.js.map} +0 -0
- /package/dist/{chunk-4YTILIIH.js.map → chunk-VV66DH6T.js.map} +0 -0
- /package/dist/{chunk-GVRO4PWE.js.map → chunk-XNNXAAZT.js.map} +0 -0
- /package/dist/{chunk-AS2IRKLU.js.map → chunk-YU5HVI6B.js.map} +0 -0
- /package/dist/{chunk-SJ2GZ6RF.js.map → chunk-ZX3GTM7O.js.map} +0 -0
- /package/dist/{claude-ACVXNB6N.js.map → claude-H33OQMXO.js.map} +0 -0
- /package/dist/{color-ZPIIUADB.js.map → color-4TJ4P5EY.js.map} +0 -0
- /package/dist/{git-OQAPUPLP.js.map → git-OV6ADVO7.js.map} +0 -0
- /package/dist/{init-F6PFMSU5.js.map → init-CMIRHFSR.js.map} +0 -0
- /package/dist/{installation-detector-6R6YOFVZ.js.map → installation-detector-VXZOCL6P.js.map} +0 -0
- /package/dist/{neon-helpers-L5CXQ5CT.js.map → neon-helpers-3KBC4A3Y.js.map} +0 -0
- /package/dist/{projects-QEAEBAT2.js.map → projects-GVEMCN5R.js.map} +0 -0
- /package/dist/{prompt-A7GGRHSY.js.map → prompt-3SAZYRUN.js.map} +0 -0
- /package/dist/{remote-73TZ2ADI.js.map → remote-IJAMOEAP.js.map} +0 -0
- /package/dist/{test-git-6SAIRBUD.js.map → test-git-CO3BA4BV.js.map} +0 -0
- /package/dist/{test-prefix-RLVRK5ZD.js.map → test-prefix-HZYSDQYT.js.map} +0 -0
- /package/dist/{test-webserver-VPNLAFZ3.js.map → test-webserver-YVQD42W6.js.map} +0 -0
- /package/dist/{update-LETF5ASC.js.map → update-5NOHT4SG.js.map} +0 -0
- /package/dist/{update-notifier-H55ZK7NU.js.map → update-notifier-ARA5SPUW.js.map} +0 -0
|
@@ -5,23 +5,25 @@ import {
|
|
|
5
5
|
colorDistance,
|
|
6
6
|
generateColorFromBranchName,
|
|
7
7
|
getColorPalette,
|
|
8
|
+
getDarkColorPalette,
|
|
8
9
|
hexToRgb,
|
|
9
10
|
lightenColor,
|
|
10
11
|
rgbToHex,
|
|
11
12
|
saturateColor,
|
|
12
13
|
selectDistinctColor
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-433MOLAU.js";
|
|
15
|
+
import "./chunk-VT4PDUYT.js";
|
|
15
16
|
export {
|
|
16
17
|
MIN_COLOR_DISTANCE,
|
|
17
18
|
calculateForegroundColor,
|
|
18
19
|
colorDistance,
|
|
19
20
|
generateColorFromBranchName,
|
|
20
21
|
getColorPalette,
|
|
22
|
+
getDarkColorPalette,
|
|
21
23
|
hexToRgb,
|
|
22
24
|
lightenColor,
|
|
23
25
|
rgbToHex,
|
|
24
26
|
saturateColor,
|
|
25
27
|
selectDistinctColor
|
|
26
28
|
};
|
|
27
|
-
//# sourceMappingURL=color-
|
|
29
|
+
//# sourceMappingURL=color-4TJ4P5EY.js.map
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
executeGitCommand
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-53OMUNUN.js";
|
|
5
|
+
import "./chunk-IDUICCZY.js";
|
|
6
|
+
import "./chunk-CFUWQHCJ.js";
|
|
7
7
|
import {
|
|
8
8
|
checkGhAuth,
|
|
9
9
|
executeGhCommand
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-LT3SGBR7.js";
|
|
11
11
|
import {
|
|
12
12
|
promptInput
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
13
|
+
} from "./chunk-ZX3GTM7O.js";
|
|
14
|
+
import "./chunk-6MLEBAYZ.js";
|
|
15
15
|
import {
|
|
16
16
|
logger
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-VT4PDUYT.js";
|
|
18
18
|
|
|
19
19
|
// src/commands/contribute.ts
|
|
20
20
|
import { existsSync, accessSync, constants } from "fs";
|
|
@@ -248,7 +248,7 @@ Happy contributing!`);
|
|
|
248
248
|
}
|
|
249
249
|
},
|
|
250
250
|
mergeBehavior: {
|
|
251
|
-
mode: "github-pr"
|
|
251
|
+
mode: "github-draft-pr"
|
|
252
252
|
}
|
|
253
253
|
};
|
|
254
254
|
await writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
@@ -259,4 +259,4 @@ export {
|
|
|
259
259
|
validateDirectoryName,
|
|
260
260
|
validateDirectoryPath
|
|
261
261
|
};
|
|
262
|
-
//# sourceMappingURL=contribute-
|
|
262
|
+
//# sourceMappingURL=contribute-SMIPMWCH.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":";;;;;;;;;;;;;;;;;;;AAIA,SAAS,YAAY,YAAY,iBAAiB;AAClD,SAAS,WAAW,aAAa;AACjC,OAAO,UAAU;AAEjB,OAAO,WAAW;AAElB,IAAM,aAAa;AACnB,IAAM,eAAe;AAGrB,IAAM,kBAAkB;AAGxB,IAAM,iBAAiB;AAAA,EACtB;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EACrB;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAChE;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AACjE;AAIA,IAAM,wBAAwB;AAgBvB,SAAS,sBAAsB,eAAkD;AAEvF,MAAI,CAAC,iBAAiB,cAAc,KAAK,MAAM,IAAI;AAClD,WAAO,EAAE,SAAS,OAAO,OAAO,iCAAiC;AAAA,EAClE;AAEA,QAAM,UAAU,cAAc,KAAK;AACnC,QAAM,WAAW,KAAK,SAAS,OAAO;AAGtC,MAAI,sBAAsB,KAAK,QAAQ,GAAG;AACzC,WAAO,EAAE,SAAS,OAAO,OAAO,uDAAuD;AAAA,EACxF;AAGA,MAAI,eAAe,SAAS,SAAS,YAAY,CAAC,GAAG;AACpD,WAAO,EAAE,SAAS,OAAO,OAAO,IAAI,QAAQ,0CAA0C;AAAA,EACvF;AAGA,MAAI,SAAS,WAAW,GAAG,KAAK,aAAa,KAAK;AACjD,WAAO,EAAE,SAAS,OAAO,OAAO,sCAAsC;AAAA,EACvE;AACA,MAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,GAAG,GAAG;AACrD,WAAO,EAAE,SAAS,OAAO,OAAO,gDAAgD;AAAA,EACjF;AAEA,SAAO,EAAE,SAAS,KAAK;AACxB;AAOO,SAAS,sBAAsB,eAAkD;AAEvF,QAAM,iBAAiB,sBAAsB,aAAa;AAC1D,MAAI,CAAC,eAAe,SAAS;AAC5B,WAAO;AAAA,EACR;AAEA,QAAM,UAAU,cAAc,KAAK;AACnC,QAAM,eAAe,KAAK,QAAQ,OAAO;AAGzC,MAAI,aAAa,SAAS,iBAAiB;AAC1C,WAAO;AAAA,MACN,SAAS;AAAA,MACT,OAAO,qBAAqB,aAAa,MAAM,4BAA4B,eAAe;AAAA,IAC3F;AAAA,EACD;AAGA,MAAI,WAAW,YAAY,GAAG;AAC7B,WAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,OAAO,GAAG;AAAA,EACxE;AAGA,QAAM,YAAY,KAAK,QAAQ,YAAY;AAC3C,MAAI,CAAC,WAAW,SAAS,GAAG;AAC3B,WAAO,EAAE,SAAS,OAAO,OAAO,oCAAoC,SAAS,GAAG;AAAA,EACjF;AAGA,MAAI;AACH,eAAW,WAAW,UAAU,IAAI;AAAA,EACrC,QAAQ;AACP,WAAO,EAAE,SAAS,OAAO,OAAO,qCAAqC,SAAS,GAAG;AAAA,EAClF;AAEA,SAAO,EAAE,SAAS,KAAK;AACxB;AAOO,IAAM,oBAAN,MAAwB;AAAA,EAC9B,YAAY,cAA4B;AAAA,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzC,MAAa,UAAyB;AACrC,WAAO,KAAK,MAAM,KAAK,6CAA6C,CAAC;AAGrE,UAAM,WAAW,MAAM,KAAK,yBAAyB;AACrD,WAAO,QAAQ,oBAAoB,MAAM,KAAK,QAAQ,CAAC,EAAE;AAGzD,UAAM,UAAU,MAAM,KAAK,WAAW,QAAQ;AAG9C,QAAI,CAAC,SAAS;AACb,aAAO,KAAK,wCAAwC;AACpD,YAAM,KAAK,WAAW;AACtB,aAAO,QAAQ,2BAA2B;AAAA,IAC3C,OAAO;AACN,aAAO,KAAK,qBAAqB;AAAA,IAClC;AAGA,UAAM,YAAY,MAAM,KAAK,mBAAmB;AAGhD,QAAI,CAAC,WAAW;AACf,aAAO,KAAK,yBAAyB;AACrC,cAAQ,KAAK,CAAC;AAAA,IACf;AAEA,UAAM,eAAe,KAAK,QAAQ,SAAS;AAG3C,WAAO,KAAK,yBAAyB,SAAS,KAAK;AACnD,UAAM,KAAK,gBAAgB,UAAU,SAAS;AAC9C,WAAO,QAAQ,gCAAgC;AAG/C,UAAM,KAAK,kBAAkB,YAAY;AAGzC,WAAO,KAAK,+BAA+B;AAC3C,UAAM,KAAK,kBAAkB,YAAY;AACzC,WAAO,QAAQ,qBAAqB;AAEpC,WAAO,QAAQ,MAAM,KAAK,MAAM,2CAA2C,CAAC;AAC5E,WAAO,KAAK;AAAA,YAAe;AAC3B,WAAO,KAAK,WAAW,SAAS,EAAE;AAClC,WAAO,KAAK,mBAAmB;AAC/B,WAAO,KAAK,iCAAiC;AAC7C,WAAO,KAAK;AAAA,oBAAuB;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,2BAA4C;AACzD,UAAM,aAAa,MAAM,YAAY;AAErC,QAAI,CAAC,WAAW,SAAS;AACxB,YAAM,IAAI;AAAA,QACT;AAAA,MACD;AAAA,IACD;AAEA,QAAI,CAAC,WAAW,UAAU;AAEzB,UAAI;AACH,cAAM,OAAO,MAAM,iBAAoC,CAAC,OAAO,QAAQ,UAAU,OAAO,CAAC;AACzF,eAAO,KAAK;AAAA,MACb,SAAS,OAAO;AACf,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,cAAM,IAAI,MAAM,wCAAwC,OAAO,EAAE;AAAA,MAClE;AAAA,IACD;AAEA,WAAO,WAAW;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAW,UAAoC;AAC5D,QAAI;AACH,YAAM,iBAAiB,CAAC,OAAO,SAAS,QAAQ,YAAY,CAAC;AAC7D,aAAO;AAAA,IACR,SAAS,OAAO;AAEf,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,WAAW,GAAG;AAClE,eAAO;AAAA,MACR;AAEA,YAAM;AAAA,IACP;AAAA,EACD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,aAA4B;AACzC,UAAM,iBAAiB,CAAC,QAAQ,QAAQ,YAAY,eAAe,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,gBACb,UACA,WACgB;AAChB,UAAM,iBAAiB,GAAG,QAAQ;AAElC,UAAM,iBAAiB,CAAC,QAAQ,SAAS,gBAAgB,SAAS,CAAC;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAkB,WAAkC;AACjE,QAAI;AAEH,YAAM,kBAAkB,CAAC,UAAU,WAAW,UAAU,GAAG,EAAE,KAAK,UAAU,CAAC;AAC7E,aAAO,KAAK,oCAAoC;AAAA,IACjD,QAAQ;AAEP,aAAO,KAAK,2BAA2B;AACvC,YAAM;AAAA,QACL,CAAC,UAAU,OAAO,YAAY,YAAY;AAAA,QAC1C,EAAE,KAAK,UAAU;AAAA,MAClB;AACA,aAAO,QAAQ,4BAA4B;AAAA,IAC5C;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,qBAA6C;AAC1D,UAAM,aAAa;AACnB,QAAI,WAAW;AAEf,WAAO,WAAW,YAAY;AAC7B,YAAM,YAAY,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,MACD;AAGA,UAAI,CAAC,aAAa,UAAU,KAAK,MAAM,IAAI;AAC1C,eAAO;AAAA,MACR;AAEA,YAAM,UAAU,UAAU,KAAK;AAG/B,YAAM,aAAa,sBAAsB,OAAO;AAChD,UAAI,WAAW,SAAS;AACvB,eAAO;AAAA,MACR;AAGA;AACA,UAAI,WAAW,YAAY;AAC1B,eAAO,MAAM,GAAG,WAAW,KAAK,EAAE;AAClC,eAAO,KAAK,qBAAqB,aAAa,QAAQ,sBAAsB;AAAA,MAC7E,OAAO;AACN,eAAO,MAAM,GAAG,WAAW,KAAK,EAAE;AAClC,eAAO,MAAM,gCAAgC;AAC7C,cAAM,IAAI,MAAM,2BAA2B,UAAU,cAAc,WAAW,KAAK,EAAE;AAAA,MACtF;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,kBAAkB,WAAkC;AACjE,UAAM,WAAW,KAAK,KAAK,WAAW,QAAQ;AAC9C,UAAM,eAAe,KAAK,KAAK,UAAU,qBAAqB;AAG9D,UAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAGzC,UAAM,WAAW;AAAA,MAChB,iBAAiB;AAAA,QAChB,QAAQ;AAAA,UACP,QAAQ;AAAA,QACT;AAAA,MACD;AAAA,MACA,eAAe;AAAA,QACd,MAAM;AAAA,MACP;AAAA,IACD;AAEA,UAAM,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,IAAI;AAAA,EACvE;AACD;","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-draft-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,42 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
DevServerManager
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-SHVB3EFE.js";
|
|
5
|
+
import "./chunk-LVLRMP7V.js";
|
|
6
6
|
import {
|
|
7
7
|
calculatePortForBranch
|
|
8
8
|
} from "./chunk-VU3QMIP2.js";
|
|
9
9
|
import {
|
|
10
10
|
IdentifierParser
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-YU5HVI6B.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-
|
|
18
|
+
} from "./chunk-HMMO2LDS.js";
|
|
19
19
|
import {
|
|
20
20
|
extractSettingsOverrides
|
|
21
21
|
} from "./chunk-GYCR2LOU.js";
|
|
22
22
|
import {
|
|
23
23
|
extractIssueNumber
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-53OMUNUN.js";
|
|
25
25
|
import {
|
|
26
26
|
SettingsManager
|
|
27
|
-
} from "./chunk-
|
|
28
|
-
import "./chunk-
|
|
27
|
+
} from "./chunk-IDUICCZY.js";
|
|
28
|
+
import "./chunk-CFUWQHCJ.js";
|
|
29
|
+
import "./chunk-6MLEBAYZ.js";
|
|
29
30
|
import {
|
|
30
31
|
extractPort,
|
|
31
32
|
findEnvFileContainingVariable,
|
|
32
33
|
isNoEnvFilesFoundError,
|
|
33
34
|
loadWorkspaceEnv,
|
|
35
|
+
logger,
|
|
34
36
|
parseEnvFile
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-6UIGZD2N.js";
|
|
37
|
-
import {
|
|
38
|
-
logger
|
|
39
|
-
} from "./chunk-UYVWLISQ.js";
|
|
37
|
+
} from "./chunk-VT4PDUYT.js";
|
|
40
38
|
|
|
41
39
|
// src/commands/dev-server.ts
|
|
42
40
|
import path from "path";
|
|
@@ -309,4 +307,4 @@ var DevServerCommand = class {
|
|
|
309
307
|
export {
|
|
310
308
|
DevServerCommand
|
|
311
309
|
};
|
|
312
|
-
//# sourceMappingURL=dev-server-
|
|
310
|
+
//# sourceMappingURL=dev-server-HNBRWGCD.js.map
|
|
@@ -1 +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, loadWorkspaceEnv, isNoEnvFilesFoundError } 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. Load settings to check sourceEnvOnStart\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// Build environment variables\n\t\tlet envOverrides: Record<string, string> = {}\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvOverrides = envResult.parsed\n\t\t\t}\n\t\t\tif (envResult.error && !isNoEnvFilesFoundError(envResult.error)) {\n\t\t\t\tlogger.warn(`Failed to load env files: ${envResult.error.message}`)\n\t\t\t}\n\t\t}\n\n\t\t// 4. 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\tenvOverrides\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"]}
|
|
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, loadWorkspaceEnv, isNoEnvFilesFoundError } 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. Load settings to check sourceEnvOnStart\n\t\tconst settings = await this.settingsManager.loadSettings()\n\t\tconst shouldLoadEnv = settings.sourceEnvOnStart ?? false\n\n\t\t// Build environment variables\n\t\tlet envOverrides: Record<string, string> = {}\n\n\t\tif (shouldLoadEnv) {\n\t\t\tconst envResult = loadWorkspaceEnv(worktree.path)\n\t\t\tif (envResult.parsed) {\n\t\t\t\tenvOverrides = envResult.parsed\n\t\t\t}\n\t\t\tif (envResult.error && !isNoEnvFilesFoundError(envResult.error)) {\n\t\t\t\tlogger.warn(`Failed to load env files: ${envResult.error.message}`)\n\t\t\t}\n\t\t}\n\n\t\t// 4. 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\tenvOverrides\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,33 +2,31 @@
|
|
|
2
2
|
import {
|
|
3
3
|
IssueEnhancementService,
|
|
4
4
|
capitalizeFirstLetter
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7HIRPCKU.js";
|
|
6
6
|
import {
|
|
7
7
|
AgentManager
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-N4ZJVATC.js";
|
|
9
9
|
import {
|
|
10
10
|
ProjectCapabilityDetector
|
|
11
11
|
} from "./chunk-EBISESAP.js";
|
|
12
12
|
import "./chunk-2ZPFJQ3B.js";
|
|
13
13
|
import "./chunk-YETJNRQM.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-K5G5SFWY.js";
|
|
15
15
|
import {
|
|
16
16
|
SettingsManager
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-IDUICCZY.js";
|
|
18
18
|
import {
|
|
19
19
|
GitHubService
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
20
|
+
} from "./chunk-3PT7RKL5.js";
|
|
21
|
+
import "./chunk-LT3SGBR7.js";
|
|
22
|
+
import "./chunk-ZX3GTM7O.js";
|
|
23
23
|
import {
|
|
24
24
|
getClaudeVersion
|
|
25
|
-
} from "./chunk-
|
|
26
|
-
import "./chunk-
|
|
27
|
-
import "./chunk-Z5NXYJIG.js";
|
|
28
|
-
import "./chunk-6UIGZD2N.js";
|
|
25
|
+
} from "./chunk-AEIMYF4P.js";
|
|
26
|
+
import "./chunk-6MLEBAYZ.js";
|
|
29
27
|
import {
|
|
30
28
|
logger
|
|
31
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-VT4PDUYT.js";
|
|
32
30
|
|
|
33
31
|
// src/utils/diagnostics.ts
|
|
34
32
|
import { readFile } from "fs/promises";
|
|
@@ -166,4 +164,4 @@ ${userBody}`;
|
|
|
166
164
|
export {
|
|
167
165
|
FeedbackCommand
|
|
168
166
|
};
|
|
169
|
-
//# sourceMappingURL=feedback-
|
|
167
|
+
//# sourceMappingURL=feedback-567ZH2O7.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\t// When --body is provided, only require non-empty description (skip strict validation)\n\t\tconst hasBody = !!body\n\t\tif (!description || !this.enhancementService.validateDescription(description, hasBody)) {\n\t\t\tif (hasBody) {\n\t\t\t\tthrow new Error('Description is required and cannot be empty')\n\t\t\t}\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\t// When --body is provided, only require non-empty description (skip strict validation)\n\t\tconst hasBody = !!body\n\t\tif (!description || !this.enhancementService.validateDescription(description, hasBody)) {\n\t\t\tif (hasBody) {\n\t\t\t\tthrow new Error('Description is required and cannot be empty')\n\t\t\t}\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":[]}
|
|
@@ -32,11 +32,11 @@ import {
|
|
|
32
32
|
pushBranchToRemote,
|
|
33
33
|
removePlaceholderCommitFromHead,
|
|
34
34
|
removePlaceholderCommitFromHistory
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-
|
|
37
|
-
import "./chunk-
|
|
38
|
-
import "./chunk-
|
|
39
|
-
import "./chunk-
|
|
35
|
+
} from "./chunk-53OMUNUN.js";
|
|
36
|
+
import "./chunk-IDUICCZY.js";
|
|
37
|
+
import "./chunk-CFUWQHCJ.js";
|
|
38
|
+
import "./chunk-6MLEBAYZ.js";
|
|
39
|
+
import "./chunk-VT4PDUYT.js";
|
|
40
40
|
export {
|
|
41
41
|
PLACEHOLDER_COMMIT_PREFIX,
|
|
42
42
|
branchExists,
|
|
@@ -71,4 +71,4 @@ export {
|
|
|
71
71
|
removePlaceholderCommitFromHead,
|
|
72
72
|
removePlaceholderCommitFromHistory
|
|
73
73
|
};
|
|
74
|
-
//# sourceMappingURL=git-
|
|
74
|
+
//# sourceMappingURL=git-OV6ADVO7.js.map
|
|
@@ -4,42 +4,40 @@ import {
|
|
|
4
4
|
IssueTrackerFactory,
|
|
5
5
|
generateIssueManagementMcpConfig,
|
|
6
6
|
generateRecapMcpConfig
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-66BMJ25W.js";
|
|
8
|
+
import "./chunk-7Q66W4OH.js";
|
|
9
9
|
import {
|
|
10
10
|
AgentManager
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-N4ZJVATC.js";
|
|
12
12
|
import {
|
|
13
13
|
GitWorktreeManager
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-HMMO2LDS.js";
|
|
15
15
|
import {
|
|
16
16
|
PromptTemplateManager
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-K5G5SFWY.js";
|
|
18
18
|
import {
|
|
19
19
|
extractSettingsOverrides
|
|
20
20
|
} from "./chunk-GYCR2LOU.js";
|
|
21
21
|
import {
|
|
22
22
|
extractIssueNumber
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-53OMUNUN.js";
|
|
24
24
|
import {
|
|
25
25
|
SettingsManager
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-IDUICCZY.js";
|
|
27
27
|
import {
|
|
28
28
|
MetadataManager
|
|
29
|
-
} from "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import "./chunk-
|
|
29
|
+
} from "./chunk-CFUWQHCJ.js";
|
|
30
|
+
import "./chunk-3PT7RKL5.js";
|
|
31
|
+
import "./chunk-LT3SGBR7.js";
|
|
32
|
+
import "./chunk-ZX3GTM7O.js";
|
|
33
33
|
import {
|
|
34
34
|
generateDeterministicSessionId,
|
|
35
35
|
launchClaude
|
|
36
|
-
} from "./chunk-
|
|
37
|
-
import "./chunk-
|
|
38
|
-
import "./chunk-Z5NXYJIG.js";
|
|
39
|
-
import "./chunk-6UIGZD2N.js";
|
|
36
|
+
} from "./chunk-AEIMYF4P.js";
|
|
37
|
+
import "./chunk-6MLEBAYZ.js";
|
|
40
38
|
import {
|
|
41
39
|
logger
|
|
42
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-VT4PDUYT.js";
|
|
43
41
|
|
|
44
42
|
// src/commands/ignite.ts
|
|
45
43
|
import path2 from "path";
|
|
@@ -667,4 +665,4 @@ var IgniteCommand = class {
|
|
|
667
665
|
export {
|
|
668
666
|
IgniteCommand
|
|
669
667
|
};
|
|
670
|
-
//# sourceMappingURL=ignite-
|
|
668
|
+
//# sourceMappingURL=ignite-3HB3ZBEW.js.map
|