@iloom/cli 0.2.0 → 0.3.0
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 +274 -30
- package/dist/BranchNamingService-3OQPRSWT.js +13 -0
- package/dist/ClaudeContextManager-MUQSDY2E.js +13 -0
- package/dist/ClaudeService-HG4VQ7AW.js +12 -0
- package/dist/GitHubService-EBOETDIW.js +11 -0
- package/dist/{LoomLauncher-CTSWJL35.js → LoomLauncher-FLEMBCSQ.js} +63 -32
- package/dist/LoomLauncher-FLEMBCSQ.js.map +1 -0
- package/dist/ProjectCapabilityDetector-34LU7JJ4.js +9 -0
- package/dist/{PromptTemplateManager-WII75TKH.js → PromptTemplateManager-A52RUAMS.js} +2 -2
- package/dist/README.md +274 -30
- package/dist/{SettingsManager-XOYCLH3D.js → SettingsManager-WHHFGSL7.js} +12 -4
- package/dist/SettingsMigrationManager-AGIIIPDQ.js +10 -0
- package/dist/agents/iloom-issue-analyze-and-plan.md +125 -35
- package/dist/agents/iloom-issue-analyzer.md +284 -32
- package/dist/agents/iloom-issue-complexity-evaluator.md +40 -21
- package/dist/agents/iloom-issue-enhancer.md +69 -48
- package/dist/agents/iloom-issue-implementer.md +36 -25
- package/dist/agents/iloom-issue-planner.md +35 -24
- package/dist/agents/iloom-issue-reviewer.md +62 -9
- package/dist/chunk-3KATJIKO.js +55 -0
- package/dist/chunk-3KATJIKO.js.map +1 -0
- package/dist/{chunk-SWCRXDZC.js → chunk-3RUPPQRG.js} +1 -18
- package/dist/chunk-3RUPPQRG.js.map +1 -0
- package/dist/{chunk-RF2YI2XJ.js → chunk-47KSHUCR.js} +3 -3
- package/dist/chunk-47KSHUCR.js.map +1 -0
- package/dist/{chunk-VETG35MF.js → chunk-4HHRTA7Q.js} +3 -3
- package/dist/{chunk-VETG35MF.js.map → chunk-4HHRTA7Q.js.map} +1 -1
- package/dist/chunk-5EF7Z346.js +1987 -0
- package/dist/chunk-5EF7Z346.js.map +1 -0
- package/dist/{chunk-4IV6W4U5.js → chunk-AWOFAD5O.js} +12 -12
- package/dist/chunk-AWOFAD5O.js.map +1 -0
- package/dist/{chunk-2PLUQT6J.js → chunk-C5QCTEQK.js} +2 -2
- package/dist/{chunk-CWR2SANQ.js → chunk-EBISESAP.js} +1 -1
- package/dist/{chunk-LHP6ROUM.js → chunk-FIAT22G7.js} +4 -16
- package/dist/chunk-FIAT22G7.js.map +1 -0
- package/dist/{chunk-TS6DL67T.js → chunk-G2IEYOLQ.js} +11 -38
- package/dist/chunk-G2IEYOLQ.js.map +1 -0
- package/dist/{chunk-ZMNQBJUI.js → chunk-IP7SMKIF.js} +61 -22
- package/dist/chunk-IP7SMKIF.js.map +1 -0
- package/dist/{chunk-JNKJ7NJV.js → chunk-JKXJ7BGL.js} +6 -2
- package/dist/{chunk-JNKJ7NJV.js.map → chunk-JKXJ7BGL.js.map} +1 -1
- package/dist/{chunk-LAPY6NAE.js → chunk-JQFO7QQN.js} +68 -12
- package/dist/{chunk-LAPY6NAE.js.map → chunk-JQFO7QQN.js.map} +1 -1
- package/dist/{SettingsMigrationManager-MTQIMI54.js → chunk-KLBYVHPK.js} +3 -2
- package/dist/{chunk-HBVFXN7R.js → chunk-MAVL6PJF.js} +26 -3
- package/dist/chunk-MAVL6PJF.js.map +1 -0
- package/dist/{chunk-USVVV3FP.js → chunk-MKWYLDFK.js} +5 -5
- package/dist/chunk-ML3NRPNB.js +396 -0
- package/dist/chunk-ML3NRPNB.js.map +1 -0
- package/dist/{chunk-DJUGYNQE.js → chunk-PA6Q6AWM.js} +16 -3
- package/dist/chunk-PA6Q6AWM.js.map +1 -0
- package/dist/chunk-RO26VS3W.js +444 -0
- package/dist/chunk-RO26VS3W.js.map +1 -0
- package/dist/{chunk-6LEQW46Y.js → chunk-VAYCCUXW.js} +72 -2
- package/dist/{chunk-6LEQW46Y.js.map → chunk-VAYCCUXW.js.map} +1 -1
- package/dist/{chunk-SPYPLHMK.js → chunk-VU3QMIP2.js} +34 -2
- package/dist/chunk-VU3QMIP2.js.map +1 -0
- package/dist/{chunk-PVAVNJKS.js → chunk-WEN5C5DM.js} +10 -1
- package/dist/chunk-WEN5C5DM.js.map +1 -0
- package/dist/{chunk-MFU53H6J.js → chunk-XXV3UFZL.js} +3 -3
- package/dist/{chunk-MFU53H6J.js.map → chunk-XXV3UFZL.js.map} +1 -1
- package/dist/{chunk-GZP4UGGM.js → chunk-ZM3CFL5L.js} +2 -2
- package/dist/{chunk-BLCTGFZN.js → chunk-ZT3YZB4K.js} +3 -4
- package/dist/chunk-ZT3YZB4K.js.map +1 -0
- package/dist/{claude-ZIWDG4XG.js → claude-GOP6PFC7.js} +2 -2
- package/dist/{cleanup-FEIVZSIV.js → cleanup-7RWLBSLE.js} +86 -25
- package/dist/cleanup-7RWLBSLE.js.map +1 -0
- package/dist/cli.js +2511 -62
- package/dist/cli.js.map +1 -1
- package/dist/{contribute-EMZKCAC6.js → contribute-BS2L4FZR.js} +6 -6
- package/dist/{feedback-LFNMQBAZ.js → feedback-N4ECWIPF.js} +15 -14
- package/dist/{feedback-LFNMQBAZ.js.map → feedback-N4ECWIPF.js.map} +1 -1
- package/dist/{git-WC6HZLOT.js → git-TDXKRTXM.js} +4 -2
- package/dist/{ignite-MQWVJEAB.js → ignite-VM64QO3J.js} +32 -27
- package/dist/ignite-VM64QO3J.js.map +1 -0
- package/dist/index.d.ts +359 -45
- package/dist/index.js +1266 -502
- package/dist/index.js.map +1 -1
- package/dist/{init-GJDYN2IK.js → init-G3T64SC4.js} +104 -40
- package/dist/init-G3T64SC4.js.map +1 -0
- package/dist/mcp/issue-management-server.js +934 -0
- package/dist/mcp/issue-management-server.js.map +1 -0
- package/dist/{neon-helpers-ZVIRPKCI.js → neon-helpers-WPUACUVC.js} +3 -3
- package/dist/neon-helpers-WPUACUVC.js.map +1 -0
- package/dist/{open-NXSN7XOC.js → open-KXDXEKRZ.js} +39 -36
- package/dist/open-KXDXEKRZ.js.map +1 -0
- package/dist/{prompt-ANTQWHUF.js → prompt-7INJ7YRU.js} +4 -2
- package/dist/prompt-7INJ7YRU.js.map +1 -0
- package/dist/prompts/init-prompt.txt +538 -95
- package/dist/prompts/issue-prompt.txt +27 -27
- package/dist/{rebase-DUNFOJVS.js → rebase-Q7GMM7EI.js} +6 -6
- package/dist/{remote-ZCXJVVNW.js → remote-VUNCQZ6J.js} +3 -2
- package/dist/remote-VUNCQZ6J.js.map +1 -0
- package/dist/{run-O7ZK7CKA.js → run-PAWJJCSX.js} +39 -36
- package/dist/run-PAWJJCSX.js.map +1 -0
- package/dist/schema/settings.schema.json +56 -0
- package/dist/{test-git-T76HOTIA.js → test-git-3WDLNQCA.js} +3 -3
- package/dist/{test-prefix-6HJUVQMH.js → test-prefix-EVGAWAJW.js} +3 -3
- package/dist/{test-webserver-M2I3EV4J.js → test-webserver-DAHONWCS.js} +4 -4
- package/dist/test-webserver-DAHONWCS.js.map +1 -0
- package/package.json +2 -1
- package/dist/ClaudeContextManager-LVCYRM6Q.js +0 -13
- package/dist/ClaudeService-WVTWB3DK.js +0 -12
- package/dist/GitHubService-7E2S5NNZ.js +0 -11
- package/dist/LoomLauncher-CTSWJL35.js.map +0 -1
- package/dist/add-issue-OBI325W7.js +0 -69
- package/dist/add-issue-OBI325W7.js.map +0 -1
- package/dist/chunk-4IV6W4U5.js.map +0 -1
- package/dist/chunk-BLCTGFZN.js.map +0 -1
- package/dist/chunk-CVLAZRNB.js +0 -54
- package/dist/chunk-CVLAZRNB.js.map +0 -1
- package/dist/chunk-DJUGYNQE.js.map +0 -1
- package/dist/chunk-H4E4THUZ.js +0 -55
- package/dist/chunk-H4E4THUZ.js.map +0 -1
- package/dist/chunk-H5LDRGVK.js +0 -642
- package/dist/chunk-H5LDRGVK.js.map +0 -1
- package/dist/chunk-HBVFXN7R.js.map +0 -1
- package/dist/chunk-LHP6ROUM.js.map +0 -1
- package/dist/chunk-PVAVNJKS.js.map +0 -1
- package/dist/chunk-RF2YI2XJ.js.map +0 -1
- package/dist/chunk-SPYPLHMK.js.map +0 -1
- package/dist/chunk-SWCRXDZC.js.map +0 -1
- package/dist/chunk-SYOSCMIT.js +0 -545
- package/dist/chunk-SYOSCMIT.js.map +0 -1
- package/dist/chunk-T3KEIB4D.js +0 -243
- package/dist/chunk-T3KEIB4D.js.map +0 -1
- package/dist/chunk-TS6DL67T.js.map +0 -1
- package/dist/chunk-ZMNQBJUI.js.map +0 -1
- package/dist/cleanup-FEIVZSIV.js.map +0 -1
- package/dist/enhance-MNA4ZGXW.js +0 -176
- package/dist/enhance-MNA4ZGXW.js.map +0 -1
- package/dist/finish-TX5CJICB.js +0 -1749
- package/dist/finish-TX5CJICB.js.map +0 -1
- package/dist/ignite-MQWVJEAB.js.map +0 -1
- package/dist/init-GJDYN2IK.js.map +0 -1
- package/dist/mcp/chunk-6SDFJ42P.js +0 -62
- package/dist/mcp/chunk-6SDFJ42P.js.map +0 -1
- package/dist/mcp/claude-NDFOCQQQ.js +0 -249
- package/dist/mcp/claude-NDFOCQQQ.js.map +0 -1
- package/dist/mcp/color-QS5BFCNN.js +0 -168
- package/dist/mcp/color-QS5BFCNN.js.map +0 -1
- package/dist/mcp/github-comment-server.js +0 -168
- package/dist/mcp/github-comment-server.js.map +0 -1
- package/dist/mcp/terminal-OMNRFWB3.js +0 -227
- package/dist/mcp/terminal-OMNRFWB3.js.map +0 -1
- package/dist/open-NXSN7XOC.js.map +0 -1
- package/dist/run-O7ZK7CKA.js.map +0 -1
- package/dist/start-73I5W7WW.js +0 -983
- package/dist/start-73I5W7WW.js.map +0 -1
- package/dist/test-webserver-M2I3EV4J.js.map +0 -1
- /package/dist/{ClaudeContextManager-LVCYRM6Q.js.map → BranchNamingService-3OQPRSWT.js.map} +0 -0
- /package/dist/{ClaudeService-WVTWB3DK.js.map → ClaudeContextManager-MUQSDY2E.js.map} +0 -0
- /package/dist/{GitHubService-7E2S5NNZ.js.map → ClaudeService-HG4VQ7AW.js.map} +0 -0
- /package/dist/{PromptTemplateManager-WII75TKH.js.map → GitHubService-EBOETDIW.js.map} +0 -0
- /package/dist/{SettingsManager-XOYCLH3D.js.map → ProjectCapabilityDetector-34LU7JJ4.js.map} +0 -0
- /package/dist/{claude-ZIWDG4XG.js.map → PromptTemplateManager-A52RUAMS.js.map} +0 -0
- /package/dist/{git-WC6HZLOT.js.map → SettingsManager-WHHFGSL7.js.map} +0 -0
- /package/dist/{neon-helpers-ZVIRPKCI.js.map → SettingsMigrationManager-AGIIIPDQ.js.map} +0 -0
- /package/dist/{chunk-2PLUQT6J.js.map → chunk-C5QCTEQK.js.map} +0 -0
- /package/dist/{chunk-CWR2SANQ.js.map → chunk-EBISESAP.js.map} +0 -0
- /package/dist/{SettingsMigrationManager-MTQIMI54.js.map → chunk-KLBYVHPK.js.map} +0 -0
- /package/dist/{chunk-USVVV3FP.js.map → chunk-MKWYLDFK.js.map} +0 -0
- /package/dist/{chunk-GZP4UGGM.js.map → chunk-ZM3CFL5L.js.map} +0 -0
- /package/dist/{prompt-ANTQWHUF.js.map → claude-GOP6PFC7.js.map} +0 -0
- /package/dist/{contribute-EMZKCAC6.js.map → contribute-BS2L4FZR.js.map} +0 -0
- /package/dist/{remote-ZCXJVVNW.js.map → git-TDXKRTXM.js.map} +0 -0
- /package/dist/{rebase-DUNFOJVS.js.map → rebase-Q7GMM7EI.js.map} +0 -0
- /package/dist/{test-git-T76HOTIA.js.map → test-git-3WDLNQCA.js.map} +0 -0
- /package/dist/{test-prefix-6HJUVQMH.js.map → test-prefix-EVGAWAJW.js.map} +0 -0
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
logger
|
|
4
|
-
} from "./chunk-6SDFJ42P.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/claude.ts
|
|
7
|
-
import { execa } from "execa";
|
|
8
|
-
import { existsSync } from "fs";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
async function detectClaudeCli() {
|
|
11
|
-
try {
|
|
12
|
-
await execa("command", ["-v", "claude"], {
|
|
13
|
-
shell: true,
|
|
14
|
-
timeout: 5e3
|
|
15
|
-
});
|
|
16
|
-
return true;
|
|
17
|
-
} catch (error) {
|
|
18
|
-
logger.debug("Claude CLI not available", { error });
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
async function getClaudeVersion() {
|
|
23
|
-
try {
|
|
24
|
-
const result = await execa("claude", ["--version"], {
|
|
25
|
-
timeout: 5e3
|
|
26
|
-
});
|
|
27
|
-
return result.stdout.trim();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
logger.warn("Failed to get Claude version", { error });
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function parseJsonStreamOutput(output) {
|
|
34
|
-
try {
|
|
35
|
-
const lines = output.split("\n").filter((line) => line.trim());
|
|
36
|
-
let lastResult = "";
|
|
37
|
-
for (const line of lines) {
|
|
38
|
-
try {
|
|
39
|
-
const jsonObj = JSON.parse(line);
|
|
40
|
-
if (jsonObj && typeof jsonObj === "object" && jsonObj.type === "result" && "result" in jsonObj) {
|
|
41
|
-
lastResult = jsonObj.result;
|
|
42
|
-
}
|
|
43
|
-
} catch {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return lastResult || output;
|
|
48
|
-
} catch {
|
|
49
|
-
return output;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
async function launchClaude(prompt, options = {}) {
|
|
53
|
-
const { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents } = options;
|
|
54
|
-
const args = [];
|
|
55
|
-
if (headless) {
|
|
56
|
-
args.push("-p");
|
|
57
|
-
args.push("--output-format", "stream-json");
|
|
58
|
-
args.push("--verbose");
|
|
59
|
-
}
|
|
60
|
-
if (model) {
|
|
61
|
-
args.push("--model", model);
|
|
62
|
-
}
|
|
63
|
-
if (permissionMode && permissionMode !== "default") {
|
|
64
|
-
args.push("--permission-mode", permissionMode);
|
|
65
|
-
}
|
|
66
|
-
if (addDir) {
|
|
67
|
-
args.push("--add-dir", addDir);
|
|
68
|
-
}
|
|
69
|
-
args.push("--add-dir", "/tmp");
|
|
70
|
-
if (appendSystemPrompt) {
|
|
71
|
-
args.push("--append-system-prompt", appendSystemPrompt);
|
|
72
|
-
}
|
|
73
|
-
if (mcpConfig && mcpConfig.length > 0) {
|
|
74
|
-
for (const config of mcpConfig) {
|
|
75
|
-
args.push("--mcp-config", JSON.stringify(config));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (allowedTools && allowedTools.length > 0) {
|
|
79
|
-
args.push("--allowed-tools", ...allowedTools);
|
|
80
|
-
}
|
|
81
|
-
if (disallowedTools && disallowedTools.length > 0) {
|
|
82
|
-
args.push("--disallowed-tools", ...disallowedTools);
|
|
83
|
-
}
|
|
84
|
-
if (agents) {
|
|
85
|
-
args.push("--agents", JSON.stringify(agents));
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
if (headless) {
|
|
89
|
-
const isDebugMode = logger.isDebugEnabled();
|
|
90
|
-
const execaOptions = {
|
|
91
|
-
input: prompt,
|
|
92
|
-
timeout: 0,
|
|
93
|
-
// Disable timeout for long responses
|
|
94
|
-
...addDir && { cwd: addDir },
|
|
95
|
-
// Run Claude in the worktree directory
|
|
96
|
-
verbose: isDebugMode,
|
|
97
|
-
...isDebugMode && { stdio: ["pipe", "pipe", "pipe"] }
|
|
98
|
-
// Enable streaming in debug mode
|
|
99
|
-
};
|
|
100
|
-
const subprocess = execa("claude", args, execaOptions);
|
|
101
|
-
const isJsonStreamFormat = args.includes("--output-format") && args.includes("stream-json");
|
|
102
|
-
let outputBuffer = "";
|
|
103
|
-
let isStreaming = false;
|
|
104
|
-
let isFirstProgress = true;
|
|
105
|
-
if (subprocess.stdout && typeof subprocess.stdout.on === "function") {
|
|
106
|
-
isStreaming = true;
|
|
107
|
-
subprocess.stdout.on("data", (chunk) => {
|
|
108
|
-
const text = chunk.toString();
|
|
109
|
-
outputBuffer += text;
|
|
110
|
-
if (isDebugMode) {
|
|
111
|
-
process.stdout.write(text);
|
|
112
|
-
} else {
|
|
113
|
-
if (isFirstProgress) {
|
|
114
|
-
process.stdout.write("\u{1F916} .");
|
|
115
|
-
isFirstProgress = false;
|
|
116
|
-
} else {
|
|
117
|
-
process.stdout.write(".");
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
const result = await subprocess;
|
|
123
|
-
if (isStreaming) {
|
|
124
|
-
const rawOutput = outputBuffer.trim();
|
|
125
|
-
if (!isDebugMode) {
|
|
126
|
-
process.stdout.write("\n");
|
|
127
|
-
}
|
|
128
|
-
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
129
|
-
} else {
|
|
130
|
-
if (isDebugMode) {
|
|
131
|
-
process.stdout.write(result.stdout);
|
|
132
|
-
if (result.stdout && !result.stdout.endsWith("\n")) {
|
|
133
|
-
process.stdout.write("\n");
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
process.stdout.write("\u{1F916} .");
|
|
137
|
-
process.stdout.write("\n");
|
|
138
|
-
}
|
|
139
|
-
const rawOutput = result.stdout.trim();
|
|
140
|
-
return isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput;
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
await execa("claude", [...args, "--", prompt], {
|
|
144
|
-
...addDir && { cwd: addDir },
|
|
145
|
-
stdio: "inherit",
|
|
146
|
-
// Let user interact directly in current terminal
|
|
147
|
-
timeout: 0,
|
|
148
|
-
// Disable timeout
|
|
149
|
-
verbose: logger.isDebugEnabled()
|
|
150
|
-
});
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
} catch (error) {
|
|
154
|
-
const execaError = error;
|
|
155
|
-
const errorMessage = execaError.stderr ?? execaError.message ?? "Unknown Claude CLI error";
|
|
156
|
-
throw new Error(`Claude CLI error: ${errorMessage}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
async function launchClaudeInNewTerminalWindow(_prompt, options) {
|
|
160
|
-
const { workspacePath, branchName, oneShot = "default", port, setArguments, executablePath } = options;
|
|
161
|
-
if (!workspacePath) {
|
|
162
|
-
throw new Error("workspacePath is required for terminal window launch");
|
|
163
|
-
}
|
|
164
|
-
const { openTerminalWindow } = await import("./terminal-OMNRFWB3.js");
|
|
165
|
-
const executable = executablePath ?? "iloom";
|
|
166
|
-
let launchCommand = `${executable} spin`;
|
|
167
|
-
if (oneShot !== "default") {
|
|
168
|
-
launchCommand += ` --one-shot=${oneShot}`;
|
|
169
|
-
}
|
|
170
|
-
if (setArguments && setArguments.length > 0) {
|
|
171
|
-
for (const setArg of setArguments) {
|
|
172
|
-
launchCommand += ` --set ${setArg}`;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
let backgroundColor;
|
|
176
|
-
if (branchName) {
|
|
177
|
-
try {
|
|
178
|
-
const { generateColorFromBranchName } = await import("./color-QS5BFCNN.js");
|
|
179
|
-
const colorData = generateColorFromBranchName(branchName);
|
|
180
|
-
backgroundColor = colorData.rgb;
|
|
181
|
-
} catch (error) {
|
|
182
|
-
logger.warn(
|
|
183
|
-
`Failed to generate terminal color: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
const hasEnvFile = existsSync(join(workspacePath, ".env"));
|
|
188
|
-
await openTerminalWindow({
|
|
189
|
-
workspacePath,
|
|
190
|
-
command: launchCommand,
|
|
191
|
-
...backgroundColor && { backgroundColor },
|
|
192
|
-
includeEnvSetup: hasEnvFile,
|
|
193
|
-
// source .env only if it exists
|
|
194
|
-
...port !== void 0 && { port, includePortExport: true }
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
async function generateBranchName(issueTitle, issueNumber, model = "haiku") {
|
|
198
|
-
try {
|
|
199
|
-
const isAvailable = await detectClaudeCli();
|
|
200
|
-
if (!isAvailable) {
|
|
201
|
-
logger.warn("Claude CLI not available, using fallback branch name");
|
|
202
|
-
return `feat/issue-${issueNumber}`;
|
|
203
|
-
}
|
|
204
|
-
logger.debug("Generating branch name with Claude", { issueNumber, issueTitle });
|
|
205
|
-
const prompt = `<Task>
|
|
206
|
-
Generate a git branch name for the following issue:
|
|
207
|
-
<Issue>
|
|
208
|
-
<IssueNumber>${issueNumber}</IssueNumber>
|
|
209
|
-
<IssueTitle>${issueTitle}</IssueTitle>
|
|
210
|
-
</Issue>
|
|
211
|
-
|
|
212
|
-
<Requirements>
|
|
213
|
-
<IssueNumber>Must use this exact issue number: ${issueNumber}</IssueNumber>
|
|
214
|
-
<Format>Format must be: {prefix}/issue-${issueNumber}-{description}</Format>
|
|
215
|
-
<Prefix>Prefix must be one of: feat, fix, docs, refactor, test, chore</Prefix>
|
|
216
|
-
<MaxLength>Maximum 50 characters total</MaxLength>
|
|
217
|
-
<Characters>Only lowercase letters, numbers, and hyphens allowed</Characters>
|
|
218
|
-
<Output>Reply with ONLY the branch name, nothing else</Output>
|
|
219
|
-
</Requirements>
|
|
220
|
-
</Task>`;
|
|
221
|
-
logger.debug("Sending prompt to Claude", { prompt });
|
|
222
|
-
const result = await launchClaude(prompt, {
|
|
223
|
-
model,
|
|
224
|
-
headless: true
|
|
225
|
-
});
|
|
226
|
-
const branchName = result.trim();
|
|
227
|
-
logger.debug("Claude returned branch name", { branchName, issueNumber });
|
|
228
|
-
if (!branchName || !isValidBranchName(branchName, issueNumber)) {
|
|
229
|
-
logger.warn("Invalid branch name from Claude, using fallback", { branchName });
|
|
230
|
-
return `feat/issue-${issueNumber}`;
|
|
231
|
-
}
|
|
232
|
-
return branchName;
|
|
233
|
-
} catch (error) {
|
|
234
|
-
logger.warn("Failed to generate branch name with Claude", { error });
|
|
235
|
-
return `feat/issue-${issueNumber}`;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
function isValidBranchName(name, issueNumber) {
|
|
239
|
-
const pattern = new RegExp(`^(feat|fix|docs|refactor|test|chore)/issue-${issueNumber}-[a-z0-9-]+$`);
|
|
240
|
-
return pattern.test(name) && name.length <= 50;
|
|
241
|
-
}
|
|
242
|
-
export {
|
|
243
|
-
detectClaudeCli,
|
|
244
|
-
generateBranchName,
|
|
245
|
-
getClaudeVersion,
|
|
246
|
-
launchClaude,
|
|
247
|
-
launchClaudeInNewTerminalWindow
|
|
248
|
-
};
|
|
249
|
-
//# sourceMappingURL=claude-NDFOCQQQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/claude.ts"],"sourcesContent":["import { execa } from 'execa'\nimport { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { logger } from './logger.js'\n\nexport interface ClaudeCliOptions {\n\tmodel?: string\n\tpermissionMode?: 'plan' | 'acceptEdits' | 'bypassPermissions' | 'default'\n\taddDir?: string\n\theadless?: boolean\n\tbranchName?: string // Optional branch name for terminal coloring\n\tport?: number // Optional port for terminal window export\n\ttimeout?: number // Timeout in milliseconds\n\tappendSystemPrompt?: string // System instructions to append to system prompt\n\tmcpConfig?: Record<string, unknown>[] // Array of MCP server configurations\n\tallowedTools?: string[] // Tools to allow via --allowed-tools flag\n\tdisallowedTools?: string[] // Tools to disallow via --disallowed-tools flag\n\tagents?: Record<string, unknown> // Agent configurations for --agents flag\n\toneShot?: import('../types/index.js').OneShotMode // One-shot automation mode\n\tsetArguments?: string[] // Raw --set arguments to forward (e.g., ['workflows.issue.startIde=false'])\n\texecutablePath?: string // Executable path to use for spin command (e.g., 'il', 'il-125', or '/path/to/dist/cli.js')\n}\n\n/**\n * Detect if Claude CLI is available on the system\n */\nexport async function detectClaudeCli(): Promise<boolean> {\n\ttry {\n\t\t// Use 'command -v' for cross-platform compatibility (works on macOS/Linux)\n\t\tawait execa('command', ['-v', 'claude'], {\n\t\t\tshell: true,\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn true\n\t} catch (error) {\n\t\t// Claude CLI not found\n\t\tlogger.debug('Claude CLI not available', { error })\n\t\treturn false\n\t}\n}\n\n/**\n * Get Claude CLI version\n */\nexport async function getClaudeVersion(): Promise<string | null> {\n\ttry {\n\t\tconst result = await execa('claude', ['--version'], {\n\t\t\ttimeout: 5000,\n\t\t})\n\t\treturn result.stdout.trim()\n\t} catch (error) {\n\t\tlogger.warn('Failed to get Claude version', { error })\n\t\treturn null\n\t}\n}\n\n/**\n * Parse JSON stream output and extract result from last JSON object with type:\"result\"\n */\nfunction parseJsonStreamOutput(output: string): string {\n\ttry {\n\t\t// Split by newlines and filter out empty lines\n\t\tconst lines = output.split('\\n').filter(line => line.trim())\n\n\t\t// Find the last valid JSON object with type:\"result\"\n\t\tlet lastResult = ''\n\t\tfor (const line of lines) {\n\t\t\ttry {\n\t\t\t\tconst jsonObj = JSON.parse(line)\n\t\t\t\tif (jsonObj && typeof jsonObj === 'object' && jsonObj.type === 'result' && 'result' in jsonObj) {\n\t\t\t\t\tlastResult = jsonObj.result\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Skip invalid JSON lines\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\treturn lastResult || output // Fallback to original output if no valid result found\n\t} catch {\n\t\t// If parsing fails completely, return original output\n\t\treturn output\n\t}\n}\n\n/**\n * Launch Claude CLI with specified options\n * In headless mode, returns stdout. In interactive mode, returns void.\n */\nexport async function launchClaude(\n\tprompt: string,\n\toptions: ClaudeCliOptions = {}\n): Promise<string | void> {\n\tconst { model, permissionMode, addDir, headless = false, appendSystemPrompt, mcpConfig, allowedTools, disallowedTools, agents } = options\n\n\t// Build command arguments\n\tconst args: string[] = []\n\n\tif (headless) {\n\t\targs.push('-p')\n\n\t\t// Add JSON streaming output for progress tracking\n\t\targs.push('--output-format', 'stream-json')\n\t\targs.push('--verbose')\n\t}\n\n\tif (model) {\n\t\targs.push('--model', model)\n\t}\n\n\tif (permissionMode && permissionMode !== 'default') {\n\t\targs.push('--permission-mode', permissionMode)\n\t}\n\n\tif (addDir) {\n\t\targs.push('--add-dir', addDir)\n\t}\n\n\targs.push('--add-dir', '/tmp') //TODO: Won't work on Windows\n\n\t// Add --append-system-prompt flag if provided\n\tif (appendSystemPrompt) {\n\t\targs.push('--append-system-prompt', appendSystemPrompt)\n\t}\n\n\t// Add --mcp-config flags for each MCP server configuration\n\tif (mcpConfig && mcpConfig.length > 0) {\n\t\tfor (const config of mcpConfig) {\n\t\t\targs.push('--mcp-config', JSON.stringify(config))\n\t\t}\n\t}\n\n\t// Add --allowed-tools flags if provided\n\tif (allowedTools && allowedTools.length > 0) {\n\t\targs.push('--allowed-tools', ...allowedTools)\n\t}\n\n\t// Add --disallowed-tools flags if provided\n\tif (disallowedTools && disallowedTools.length > 0) {\n\t\targs.push('--disallowed-tools', ...disallowedTools)\n\t}\n\n\t// Add --agents flag if provided\n\tif (agents) {\n\t\targs.push('--agents', JSON.stringify(agents))\n\t}\n\n\ttry {\n\t\tif (headless) {\n\t\t\t// Headless mode: capture and return output\n\t\t\tconst isDebugMode = logger.isDebugEnabled()\n\n\t\t\t// Set up execa options based on debug mode\n\t\t\tconst execaOptions = {\n\t\t\t\tinput: prompt,\n\t\t\t\ttimeout: 0, // Disable timeout for long responses\n\t\t\t\t...(addDir && { cwd: addDir }), // Run Claude in the worktree directory\n\t\t\t\tverbose: isDebugMode,\n\t\t\t\t...(isDebugMode && { stdio: ['pipe', 'pipe', 'pipe'] as const }), // Enable streaming in debug mode\n\t\t\t}\n\n\t\t\tconst subprocess = execa('claude', args, execaOptions)\n\n\t\t\t// Check if JSON streaming format is enabled (always true in headless mode)\n\t\t\tconst isJsonStreamFormat = args.includes('--output-format') && args.includes('stream-json')\n\n\t\t\t// Handle real-time streaming (enabled for progress tracking)\n\t\t\tlet outputBuffer = ''\n\t\t\tlet isStreaming = false\n\t\t\tlet isFirstProgress = true\n\t\t\tif (subprocess.stdout && typeof subprocess.stdout.on === 'function') {\n\t\t\t\tisStreaming = true\n\t\t\t\tsubprocess.stdout.on('data', (chunk: Buffer) => {\n\t\t\t\t\tconst text = chunk.toString()\n\t\t\t\t\toutputBuffer += text\n\n\t\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t\tprocess.stdout.write(text) // Full JSON streaming in debug mode\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Progress dots in non-debug mode with robot emoji prefix\n\t\t\t\t\t\tif (isFirstProgress) {\n\t\t\t\t\t\t\tprocess.stdout.write('🤖 .')\n\t\t\t\t\t\t\tisFirstProgress = false\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tprocess.stdout.write('.')\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tconst result = await subprocess\n\n\t\t\t// Return streamed output if we were streaming, otherwise use result.stdout\n\t\t\tif (isStreaming) {\n\t\t\t\tconst rawOutput = outputBuffer.trim()\n\n\t\t\t\t// Clean up progress dots with newline in non-debug mode\n\t\t\t\tif (!isDebugMode) {\n\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t}\n\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t} else {\n\t\t\t\t// Fallback for mocked tests or when streaming not available\n\t\t\t\tif (isDebugMode) {\n\t\t\t\t\t// In debug mode, write to stdout even if not streaming (old behavior for tests)\n\t\t\t\t\tprocess.stdout.write(result.stdout)\n\t\t\t\t\tif (result.stdout && !result.stdout.endsWith('\\n')) {\n\t\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// In non-debug mode, show a single progress dot even without streaming (for tests)\n\t\t\t\t\tprocess.stdout.write('🤖 .')\n\t\t\t\t\tprocess.stdout.write('\\n')\n\t\t\t\t}\n\t\t\t\tconst rawOutput = result.stdout.trim()\n\t\t\t\treturn isJsonStreamFormat ? parseJsonStreamOutput(rawOutput) : rawOutput\n\t\t\t}\n\t\t} else {\n\t\t\t// Simple interactive mode: run Claude in current terminal with stdio inherit\n\t\t\t// Used for conflict resolution, error fixing, etc.\n\t\t\t// This is the simple approach: claude -- \"prompt\"\n\n\t\t\t// Execute in current terminal (blocking, inherits stdio)\n\t\t\tawait execa('claude', [...args, '--', prompt], {\n\t\t\t\t...(addDir && { cwd: addDir }),\n\t\t\t\tstdio: 'inherit', // Let user interact directly in current terminal\n\t\t\t\ttimeout: 0, // Disable timeout\n\t\t\t\tverbose: logger.isDebugEnabled(),\n\t\t\t})\n\n\t\t\treturn\n\t\t}\n\t} catch (error) {\n\t\t// Check for specific Claude CLI errors\n\t\tconst execaError = error as {\n\t\t\tstderr?: string\n\t\t\tmessage?: string\n\t\t\texitCode?: number\n\t\t}\n\n\t\t// Re-throw with more context\n\t\tconst errorMessage = execaError.stderr ?? execaError.message ?? 'Unknown Claude CLI error'\n\t\tthrow new Error(`Claude CLI error: ${errorMessage}`)\n\t}\n}\n\n/**\n * Launch Claude in a new terminal window with rich context\n * This is specifically for \"end of il start\" workflow\n * Ports the terminal window opening, coloring, and .env sourcing behavior\n */\nexport async function launchClaudeInNewTerminalWindow(\n\t_prompt: string,\n\toptions: ClaudeCliOptions & {\n\t\tworkspacePath: string // Required for terminal window launch\n\t}\n): Promise<void> {\n\tconst { workspacePath, branchName, oneShot = 'default', port, setArguments, executablePath } = options\n\n\t// Verify required parameter\n\tif (!workspacePath) {\n\t\tthrow new Error('workspacePath is required for terminal window launch')\n\t}\n\n\t// Import terminal launcher for new terminal window creation\n\tconst { openTerminalWindow } = await import('./terminal.js')\n\n\t// Build launch command with optional --one-shot flag\n\t// Use provided executable path or fallback to 'il'\n\tconst executable = executablePath ?? 'iloom'\n\tlet launchCommand = `${executable} spin`\n\tif (oneShot !== 'default') {\n\t\tlaunchCommand += ` --one-shot=${oneShot}`\n\t}\n\n\t// Append --set arguments if provided\n\tif (setArguments && setArguments.length > 0) {\n\t\tfor (const setArg of setArguments) {\n\t\t\tlaunchCommand += ` --set ${setArg}`\n\t\t}\n\t}\n\n\t// Apply terminal background color if branch name available\n\tlet backgroundColor: { r: number; g: number; b: number } | undefined\n\tif (branchName) {\n\t\ttry {\n\t\t\tconst { generateColorFromBranchName } = await import('./color.js')\n\t\t\tconst colorData = generateColorFromBranchName(branchName)\n\t\t\tbackgroundColor = colorData.rgb\n\t\t} catch (error) {\n\t\t\tlogger.warn(\n\t\t\t\t`Failed to generate terminal color: ${error instanceof Error ? error.message : 'Unknown error'}`\n\t\t\t)\n\t\t}\n\t}\n\n\t// Check if .env file exists in workspace\n\tconst hasEnvFile = existsSync(join(workspacePath, '.env'))\n\n\t// Open new terminal window with Claude\n\tawait openTerminalWindow({\n\t\tworkspacePath,\n\t\tcommand: launchCommand,\n\t\t...(backgroundColor && { backgroundColor }),\n\t\tincludeEnvSetup: hasEnvFile, // source .env only if it exists\n\t\t...(port !== undefined && { port, includePortExport: true }),\n\t})\n}\n\n/**\n * Generate a branch name using Claude with fallback\n * This matches the implementation that was working in ClaudeBranchNameStrategy\n */\nexport async function generateBranchName(\n\tissueTitle: string,\n\tissueNumber: number,\n\tmodel: string = 'haiku'\n): Promise<string> {\n\ttry {\n\t\t// Check if Claude CLI is available\n\t\tconst isAvailable = await detectClaudeCli()\n\t\tif (!isAvailable) {\n\t\t\tlogger.warn('Claude CLI not available, using fallback branch name')\n\t\t\treturn `feat/issue-${issueNumber}`\n\t\t}\n\n\t\tlogger.debug('Generating branch name with Claude', { issueNumber, issueTitle })\n\n\t\t// Use the proven prompt format from ClaudeBranchNameStrategy\n\t\tconst prompt = `<Task>\nGenerate a git branch name for the following issue:\n<Issue>\n<IssueNumber>${issueNumber}</IssueNumber>\n<IssueTitle>${issueTitle}</IssueTitle>\n</Issue>\n\n<Requirements>\n<IssueNumber>Must use this exact issue number: ${issueNumber}</IssueNumber>\n<Format>Format must be: {prefix}/issue-${issueNumber}-{description}</Format>\n<Prefix>Prefix must be one of: feat, fix, docs, refactor, test, chore</Prefix>\n<MaxLength>Maximum 50 characters total</MaxLength>\n<Characters>Only lowercase letters, numbers, and hyphens allowed</Characters>\n<Output>Reply with ONLY the branch name, nothing else</Output>\n</Requirements>\n</Task>`\n\n\t\tlogger.debug('Sending prompt to Claude', { prompt })\n\n\t\tconst result = (await launchClaude(prompt, {\n\t\t\tmodel,\n\t\t\theadless: true,\n\t\t})) as string\n\n\t\tconst branchName = result.trim()\n\t\tlogger.debug('Claude returned branch name', { branchName, issueNumber })\n\n\t\t// Validate generated name using same validation as ClaudeBranchNameStrategy\n\t\tif (!branchName || !isValidBranchName(branchName, issueNumber)) {\n\t\t\tlogger.warn('Invalid branch name from Claude, using fallback', { branchName })\n\t\t\treturn `feat/issue-${issueNumber}`\n\t\t}\n\n\t\treturn branchName\n\t} catch (error) {\n\t\tlogger.warn('Failed to generate branch name with Claude', { error })\n\t\treturn `feat/issue-${issueNumber}`\n\t}\n}\n\n/**\n * Validate branch name format\n * Check format: {prefix}/issue-{number}-{description}\n */\nfunction isValidBranchName(name: string, issueNumber: number): boolean {\n\tconst pattern = new RegExp(`^(feat|fix|docs|refactor|test|chore)/issue-${issueNumber}-[a-z0-9-]+$`)\n\treturn pattern.test(name) && name.length <= 50\n}\n"],"mappings":";;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAwBrB,eAAsB,kBAAoC;AACzD,MAAI;AAEH,UAAM,MAAM,WAAW,CAAC,MAAM,QAAQ,GAAG;AAAA,MACxC,OAAO;AAAA,MACP,SAAS;AAAA,IACV,CAAC;AACD,WAAO;AAAA,EACR,SAAS,OAAO;AAEf,WAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC;AAClD,WAAO;AAAA,EACR;AACD;AAKA,eAAsB,mBAA2C;AAChE,MAAI;AACH,UAAM,SAAS,MAAM,MAAM,UAAU,CAAC,WAAW,GAAG;AAAA,MACnD,SAAS;AAAA,IACV,CAAC;AACD,WAAO,OAAO,OAAO,KAAK;AAAA,EAC3B,SAAS,OAAO;AACf,WAAO,KAAK,gCAAgC,EAAE,MAAM,CAAC;AACrD,WAAO;AAAA,EACR;AACD;AAKA,SAAS,sBAAsB,QAAwB;AACtD,MAAI;AAEH,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,UAAQ,KAAK,KAAK,CAAC;AAG3D,QAAI,aAAa;AACjB,eAAW,QAAQ,OAAO;AACzB,UAAI;AACH,cAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,YAAI,WAAW,OAAO,YAAY,YAAY,QAAQ,SAAS,YAAY,YAAY,SAAS;AAC/F,uBAAa,QAAQ;AAAA,QACtB;AAAA,MACD,QAAQ;AAEP;AAAA,MACD;AAAA,IACD;AAEA,WAAO,cAAc;AAAA,EACtB,QAAQ;AAEP,WAAO;AAAA,EACR;AACD;AAMA,eAAsB,aACrB,QACA,UAA4B,CAAC,GACJ;AACzB,QAAM,EAAE,OAAO,gBAAgB,QAAQ,WAAW,OAAO,oBAAoB,WAAW,cAAc,iBAAiB,OAAO,IAAI;AAGlI,QAAM,OAAiB,CAAC;AAExB,MAAI,UAAU;AACb,SAAK,KAAK,IAAI;AAGd,SAAK,KAAK,mBAAmB,aAAa;AAC1C,SAAK,KAAK,WAAW;AAAA,EACtB;AAEA,MAAI,OAAO;AACV,SAAK,KAAK,WAAW,KAAK;AAAA,EAC3B;AAEA,MAAI,kBAAkB,mBAAmB,WAAW;AACnD,SAAK,KAAK,qBAAqB,cAAc;AAAA,EAC9C;AAEA,MAAI,QAAQ;AACX,SAAK,KAAK,aAAa,MAAM;AAAA,EAC9B;AAEA,OAAK,KAAK,aAAa,MAAM;AAG7B,MAAI,oBAAoB;AACvB,SAAK,KAAK,0BAA0B,kBAAkB;AAAA,EACvD;AAGA,MAAI,aAAa,UAAU,SAAS,GAAG;AACtC,eAAW,UAAU,WAAW;AAC/B,WAAK,KAAK,gBAAgB,KAAK,UAAU,MAAM,CAAC;AAAA,IACjD;AAAA,EACD;AAGA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC5C,SAAK,KAAK,mBAAmB,GAAG,YAAY;AAAA,EAC7C;AAGA,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AAClD,SAAK,KAAK,sBAAsB,GAAG,eAAe;AAAA,EACnD;AAGA,MAAI,QAAQ;AACX,SAAK,KAAK,YAAY,KAAK,UAAU,MAAM,CAAC;AAAA,EAC7C;AAEA,MAAI;AACH,QAAI,UAAU;AAEb,YAAM,cAAc,OAAO,eAAe;AAG1C,YAAM,eAAe;AAAA,QACpB,OAAO;AAAA,QACP,SAAS;AAAA;AAAA,QACT,GAAI,UAAU,EAAE,KAAK,OAAO;AAAA;AAAA,QAC5B,SAAS;AAAA,QACT,GAAI,eAAe,EAAE,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAW;AAAA;AAAA,MAC/D;AAEA,YAAM,aAAa,MAAM,UAAU,MAAM,YAAY;AAGrD,YAAM,qBAAqB,KAAK,SAAS,iBAAiB,KAAK,KAAK,SAAS,aAAa;AAG1F,UAAI,eAAe;AACnB,UAAI,cAAc;AAClB,UAAI,kBAAkB;AACtB,UAAI,WAAW,UAAU,OAAO,WAAW,OAAO,OAAO,YAAY;AACpE,sBAAc;AACd,mBAAW,OAAO,GAAG,QAAQ,CAAC,UAAkB;AAC/C,gBAAM,OAAO,MAAM,SAAS;AAC5B,0BAAgB;AAEhB,cAAI,aAAa;AAChB,oBAAQ,OAAO,MAAM,IAAI;AAAA,UAC1B,OAAO;AAEN,gBAAI,iBAAiB;AACpB,sBAAQ,OAAO,MAAM,aAAM;AAC3B,gCAAkB;AAAA,YACnB,OAAO;AACN,sBAAQ,OAAO,MAAM,GAAG;AAAA,YACzB;AAAA,UACD;AAAA,QACD,CAAC;AAAA,MACF;AAEA,YAAM,SAAS,MAAM;AAGrB,UAAI,aAAa;AAChB,cAAM,YAAY,aAAa,KAAK;AAGpC,YAAI,CAAC,aAAa;AACjB,kBAAQ,OAAO,MAAM,IAAI;AAAA,QAC1B;AAEA,eAAO,qBAAqB,sBAAsB,SAAS,IAAI;AAAA,MAChE,OAAO;AAEN,YAAI,aAAa;AAEhB,kBAAQ,OAAO,MAAM,OAAO,MAAM;AAClC,cAAI,OAAO,UAAU,CAAC,OAAO,OAAO,SAAS,IAAI,GAAG;AACnD,oBAAQ,OAAO,MAAM,IAAI;AAAA,UAC1B;AAAA,QACD,OAAO;AAEN,kBAAQ,OAAO,MAAM,aAAM;AAC3B,kBAAQ,OAAO,MAAM,IAAI;AAAA,QAC1B;AACA,cAAM,YAAY,OAAO,OAAO,KAAK;AACrC,eAAO,qBAAqB,sBAAsB,SAAS,IAAI;AAAA,MAChE;AAAA,IACD,OAAO;AAMN,YAAM,MAAM,UAAU,CAAC,GAAG,MAAM,MAAM,MAAM,GAAG;AAAA,QAC9C,GAAI,UAAU,EAAE,KAAK,OAAO;AAAA,QAC5B,OAAO;AAAA;AAAA,QACP,SAAS;AAAA;AAAA,QACT,SAAS,OAAO,eAAe;AAAA,MAChC,CAAC;AAED;AAAA,IACD;AAAA,EACD,SAAS,OAAO;AAEf,UAAM,aAAa;AAOnB,UAAM,eAAe,WAAW,UAAU,WAAW,WAAW;AAChE,UAAM,IAAI,MAAM,qBAAqB,YAAY,EAAE;AAAA,EACpD;AACD;AAOA,eAAsB,gCACrB,SACA,SAGgB;AAChB,QAAM,EAAE,eAAe,YAAY,UAAU,WAAW,MAAM,cAAc,eAAe,IAAI;AAG/F,MAAI,CAAC,eAAe;AACnB,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACvE;AAGA,QAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,wBAAe;AAI3D,QAAM,aAAa,kBAAkB;AACrC,MAAI,gBAAgB,GAAG,UAAU;AACjC,MAAI,YAAY,WAAW;AAC1B,qBAAiB,eAAe,OAAO;AAAA,EACxC;AAGA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC5C,eAAW,UAAU,cAAc;AAClC,uBAAiB,UAAU,MAAM;AAAA,IAClC;AAAA,EACD;AAGA,MAAI;AACJ,MAAI,YAAY;AACf,QAAI;AACH,YAAM,EAAE,4BAA4B,IAAI,MAAM,OAAO,qBAAY;AACjE,YAAM,YAAY,4BAA4B,UAAU;AACxD,wBAAkB,UAAU;AAAA,IAC7B,SAAS,OAAO;AACf,aAAO;AAAA,QACN,sCAAsC,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC/F;AAAA,IACD;AAAA,EACD;AAGA,QAAM,aAAa,WAAW,KAAK,eAAe,MAAM,CAAC;AAGzD,QAAM,mBAAmB;AAAA,IACxB;AAAA,IACA,SAAS;AAAA,IACT,GAAI,mBAAmB,EAAE,gBAAgB;AAAA,IACzC,iBAAiB;AAAA;AAAA,IACjB,GAAI,SAAS,UAAa,EAAE,MAAM,mBAAmB,KAAK;AAAA,EAC3D,CAAC;AACF;AAMA,eAAsB,mBACrB,YACA,aACA,QAAgB,SACE;AAClB,MAAI;AAEH,UAAM,cAAc,MAAM,gBAAgB;AAC1C,QAAI,CAAC,aAAa;AACjB,aAAO,KAAK,sDAAsD;AAClE,aAAO,cAAc,WAAW;AAAA,IACjC;AAEA,WAAO,MAAM,sCAAsC,EAAE,aAAa,WAAW,CAAC;AAG9E,UAAM,SAAS;AAAA;AAAA;AAAA,eAGF,WAAW;AAAA,cACZ,UAAU;AAAA;AAAA;AAAA;AAAA,iDAIyB,WAAW;AAAA,yCACnB,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlD,WAAO,MAAM,4BAA4B,EAAE,OAAO,CAAC;AAEnD,UAAM,SAAU,MAAM,aAAa,QAAQ;AAAA,MAC1C;AAAA,MACA,UAAU;AAAA,IACX,CAAC;AAED,UAAM,aAAa,OAAO,KAAK;AAC/B,WAAO,MAAM,+BAA+B,EAAE,YAAY,YAAY,CAAC;AAGvE,QAAI,CAAC,cAAc,CAAC,kBAAkB,YAAY,WAAW,GAAG;AAC/D,aAAO,KAAK,mDAAmD,EAAE,WAAW,CAAC;AAC7E,aAAO,cAAc,WAAW;AAAA,IACjC;AAEA,WAAO;AAAA,EACR,SAAS,OAAO;AACf,WAAO,KAAK,8CAA8C,EAAE,MAAM,CAAC;AACnE,WAAO,cAAc,WAAW;AAAA,EACjC;AACD;AAMA,SAAS,kBAAkB,MAAc,aAA8B;AACtE,QAAM,UAAU,IAAI,OAAO,8CAA8C,WAAW,cAAc;AAClG,SAAO,QAAQ,KAAK,IAAI,KAAK,KAAK,UAAU;AAC7C;","names":[]}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
logger_default
|
|
4
|
-
} from "./chunk-6SDFJ42P.js";
|
|
5
|
-
|
|
6
|
-
// src/utils/color.ts
|
|
7
|
-
import { createHash } from "crypto";
|
|
8
|
-
function getColorPalette() {
|
|
9
|
-
return [
|
|
10
|
-
// First 10 colors preserved for backward compatibility
|
|
11
|
-
{ r: 220, g: 235, b: 248 },
|
|
12
|
-
// 0: Soft blue
|
|
13
|
-
{ r: 248, g: 220, b: 235 },
|
|
14
|
-
// 1: Soft pink
|
|
15
|
-
{ r: 220, g: 248, b: 235 },
|
|
16
|
-
// 2: Soft green
|
|
17
|
-
{ r: 248, g: 240, b: 220 },
|
|
18
|
-
// 3: Soft cream
|
|
19
|
-
{ r: 240, g: 220, b: 248 },
|
|
20
|
-
// 4: Soft lavender
|
|
21
|
-
{ r: 220, g: 240, b: 248 },
|
|
22
|
-
// 5: Soft cyan
|
|
23
|
-
{ r: 235, g: 235, b: 235 },
|
|
24
|
-
// 6: Soft grey
|
|
25
|
-
{ r: 228, g: 238, b: 248 },
|
|
26
|
-
// 7: Soft ice blue
|
|
27
|
-
{ r: 248, g: 228, b: 238 },
|
|
28
|
-
// 8: Soft rose
|
|
29
|
-
{ r: 228, g: 248, b: 238 },
|
|
30
|
-
// 9: Soft mint
|
|
31
|
-
// 30 new colors (indices 10-39)
|
|
32
|
-
{ r: 235, g: 245, b: 250 },
|
|
33
|
-
// 10: Pale sky blue
|
|
34
|
-
{ r: 250, g: 235, b: 245 },
|
|
35
|
-
// 11: Pale orchid
|
|
36
|
-
{ r: 235, g: 250, b: 245 },
|
|
37
|
-
// 12: Pale seafoam
|
|
38
|
-
{ r: 250, g: 245, b: 235 },
|
|
39
|
-
// 13: Pale peach
|
|
40
|
-
{ r: 245, g: 235, b: 250 },
|
|
41
|
-
// 14: Pale periwinkle
|
|
42
|
-
{ r: 235, g: 245, b: 235 },
|
|
43
|
-
// 15: Pale sage
|
|
44
|
-
{ r: 245, g: 250, b: 235 },
|
|
45
|
-
// 16: Pale lemon
|
|
46
|
-
{ r: 245, g: 235, b: 235 },
|
|
47
|
-
// 17: Pale blush
|
|
48
|
-
{ r: 235, g: 235, b: 250 },
|
|
49
|
-
// 18: Pale lavender blue
|
|
50
|
-
{ r: 250, g: 235, b: 235 },
|
|
51
|
-
// 19: Pale coral
|
|
52
|
-
{ r: 235, g: 250, b: 250 },
|
|
53
|
-
// 20: Pale aqua
|
|
54
|
-
{ r: 240, g: 248, b: 255 },
|
|
55
|
-
// 21: Alice blue
|
|
56
|
-
{ r: 255, g: 240, b: 248 },
|
|
57
|
-
// 22: Lavender blush
|
|
58
|
-
{ r: 240, g: 255, b: 248 },
|
|
59
|
-
// 23: Honeydew tint
|
|
60
|
-
{ r: 255, g: 248, b: 240 },
|
|
61
|
-
// 24: Antique white
|
|
62
|
-
{ r: 248, g: 240, b: 255 },
|
|
63
|
-
// 25: Magnolia
|
|
64
|
-
{ r: 240, g: 248, b: 240 },
|
|
65
|
-
// 26: Mint cream tint
|
|
66
|
-
{ r: 248, g: 255, b: 240 },
|
|
67
|
-
// 27: Ivory tint
|
|
68
|
-
{ r: 248, g: 240, b: 240 },
|
|
69
|
-
// 28: Misty rose tint
|
|
70
|
-
{ r: 240, g: 240, b: 255 },
|
|
71
|
-
// 29: Ghost white tint
|
|
72
|
-
{ r: 255, g: 245, b: 238 },
|
|
73
|
-
// 30: Seashell
|
|
74
|
-
{ r: 245, g: 255, b: 250 },
|
|
75
|
-
// 31: Azure mist
|
|
76
|
-
{ r: 250, g: 245, b: 255 },
|
|
77
|
-
// 32: Lilac mist
|
|
78
|
-
{ r: 255, g: 250, b: 245 },
|
|
79
|
-
// 33: Snow peach
|
|
80
|
-
{ r: 238, g: 245, b: 255 },
|
|
81
|
-
// 34: Powder blue
|
|
82
|
-
{ r: 255, g: 238, b: 245 },
|
|
83
|
-
// 35: Pink lace
|
|
84
|
-
{ r: 245, g: 255, b: 238 },
|
|
85
|
-
// 36: Pale lime
|
|
86
|
-
{ r: 238, g: 255, b: 245 },
|
|
87
|
-
// 37: Pale turquoise
|
|
88
|
-
{ r: 245, g: 238, b: 255 },
|
|
89
|
-
// 38: Pale violet
|
|
90
|
-
{ r: 255, g: 245, b: 255 }
|
|
91
|
-
// 39: Pale magenta
|
|
92
|
-
];
|
|
93
|
-
}
|
|
94
|
-
function rgbToHex(r, g, b) {
|
|
95
|
-
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
|
|
96
|
-
throw new Error("RGB values must be between 0 and 255");
|
|
97
|
-
}
|
|
98
|
-
const rHex = r.toString(16).padStart(2, "0");
|
|
99
|
-
const gHex = g.toString(16).padStart(2, "0");
|
|
100
|
-
const bHex = b.toString(16).padStart(2, "0");
|
|
101
|
-
return `#${rHex}${gHex}${bHex}`;
|
|
102
|
-
}
|
|
103
|
-
function hexToRgb(hex) {
|
|
104
|
-
const cleanHex = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
105
|
-
if (cleanHex.length !== 6 || !/^[0-9a-fA-F]{6}$/.test(cleanHex)) {
|
|
106
|
-
throw new Error("Invalid hex color format. Expected format: #RRGGBB or RRGGBB");
|
|
107
|
-
}
|
|
108
|
-
const r = parseInt(cleanHex.slice(0, 2), 16);
|
|
109
|
-
const g = parseInt(cleanHex.slice(2, 4), 16);
|
|
110
|
-
const b = parseInt(cleanHex.slice(4, 6), 16);
|
|
111
|
-
return { r, g, b };
|
|
112
|
-
}
|
|
113
|
-
function generateColorFromBranchName(branchName) {
|
|
114
|
-
const hash = createHash("sha256").update(branchName).digest("hex");
|
|
115
|
-
const hashPrefix = hash.slice(0, 8);
|
|
116
|
-
const palette = getColorPalette();
|
|
117
|
-
const hashAsInt = parseInt(hashPrefix, 16);
|
|
118
|
-
const index = hashAsInt % palette.length;
|
|
119
|
-
logger_default.debug(`[generateColorFromBranchName] Branch name: ${branchName}, Hash: ${hash}, Hash prefix: ${hashPrefix}, Hash as int: ${hashAsInt}, Index: ${index}`);
|
|
120
|
-
const rgb = palette[index];
|
|
121
|
-
if (!rgb) {
|
|
122
|
-
throw new Error(`Invalid color index: ${index}`);
|
|
123
|
-
}
|
|
124
|
-
const hex = rgbToHex(rgb.r, rgb.g, rgb.b);
|
|
125
|
-
return {
|
|
126
|
-
rgb,
|
|
127
|
-
hex,
|
|
128
|
-
index
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function lightenColor(rgb, amount) {
|
|
132
|
-
const clamp = (value) => Math.min(255, Math.max(0, Math.round(value)));
|
|
133
|
-
return {
|
|
134
|
-
r: clamp(rgb.r + (255 - rgb.r) * amount),
|
|
135
|
-
g: clamp(rgb.g + (255 - rgb.g) * amount),
|
|
136
|
-
b: clamp(rgb.b + (255 - rgb.b) * amount)
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function saturateColor(rgb, amount) {
|
|
140
|
-
const clamp = (value) => Math.min(255, Math.max(0, Math.round(value)));
|
|
141
|
-
const avg = (rgb.r + rgb.g + rgb.b) / 3;
|
|
142
|
-
return {
|
|
143
|
-
r: clamp(rgb.r + (rgb.r - avg) * amount),
|
|
144
|
-
g: clamp(rgb.g + (rgb.g - avg) * amount),
|
|
145
|
-
b: clamp(rgb.b + (rgb.b - avg) * amount)
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
function calculateForegroundColor(rgb) {
|
|
149
|
-
const toLinear = (channel) => {
|
|
150
|
-
const c = channel / 255;
|
|
151
|
-
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
152
|
-
};
|
|
153
|
-
const r = toLinear(rgb.r);
|
|
154
|
-
const g = toLinear(rgb.g);
|
|
155
|
-
const b = toLinear(rgb.b);
|
|
156
|
-
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
157
|
-
return luminance > 0.5 ? "#000000" : "#ffffff";
|
|
158
|
-
}
|
|
159
|
-
export {
|
|
160
|
-
calculateForegroundColor,
|
|
161
|
-
generateColorFromBranchName,
|
|
162
|
-
getColorPalette,
|
|
163
|
-
hexToRgb,
|
|
164
|
-
lightenColor,
|
|
165
|
-
rgbToHex,
|
|
166
|
-
saturateColor
|
|
167
|
-
};
|
|
168
|
-
//# sourceMappingURL=color-QS5BFCNN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/color.ts"],"sourcesContent":["import { createHash } from 'crypto'\nimport logger from './logger'\n\n/**\n * RGB color representation\n */\nexport interface RgbColor {\n\tr: number\n\tg: number\n\tb: number\n}\n\n/**\n * Complete color data with RGB, hex, and palette index\n */\nexport interface ColorData {\n\trgb: RgbColor\n\thex: string\n\tindex: number\n}\n\n/**\n * Get the predefined color palette (40 subtle, professional colors)\n * Matches the terminal color palette from bash/new-branch-workflow.sh\n *\n * @returns Array of 40 RGB colors\n */\nexport function getColorPalette(): RgbColor[] {\n\treturn [\n\t\t// First 10 colors preserved for backward compatibility\n\t\t{ r: 220, g: 235, b: 248 }, // 0: Soft blue\n\t\t{ r: 248, g: 220, b: 235 }, // 1: Soft pink\n\t\t{ r: 220, g: 248, b: 235 }, // 2: Soft green\n\t\t{ r: 248, g: 240, b: 220 }, // 3: Soft cream\n\t\t{ r: 240, g: 220, b: 248 }, // 4: Soft lavender\n\t\t{ r: 220, g: 240, b: 248 }, // 5: Soft cyan\n\t\t{ r: 235, g: 235, b: 235 }, // 6: Soft grey\n\t\t{ r: 228, g: 238, b: 248 }, // 7: Soft ice blue\n\t\t{ r: 248, g: 228, b: 238 }, // 8: Soft rose\n\t\t{ r: 228, g: 248, b: 238 }, // 9: Soft mint\n\t\t// 30 new colors (indices 10-39)\n\t\t{ r: 235, g: 245, b: 250 }, // 10: Pale sky blue\n\t\t{ r: 250, g: 235, b: 245 }, // 11: Pale orchid\n\t\t{ r: 235, g: 250, b: 245 }, // 12: Pale seafoam\n\t\t{ r: 250, g: 245, b: 235 }, // 13: Pale peach\n\t\t{ r: 245, g: 235, b: 250 }, // 14: Pale periwinkle\n\t\t{ r: 235, g: 245, b: 235 }, // 15: Pale sage\n\t\t{ r: 245, g: 250, b: 235 }, // 16: Pale lemon\n\t\t{ r: 245, g: 235, b: 235 }, // 17: Pale blush\n\t\t{ r: 235, g: 235, b: 250 }, // 18: Pale lavender blue\n\t\t{ r: 250, g: 235, b: 235 }, // 19: Pale coral\n\t\t{ r: 235, g: 250, b: 250 }, // 20: Pale aqua\n\t\t{ r: 240, g: 248, b: 255 }, // 21: Alice blue\n\t\t{ r: 255, g: 240, b: 248 }, // 22: Lavender blush\n\t\t{ r: 240, g: 255, b: 248 }, // 23: Honeydew tint\n\t\t{ r: 255, g: 248, b: 240 }, // 24: Antique white\n\t\t{ r: 248, g: 240, b: 255 }, // 25: Magnolia\n\t\t{ r: 240, g: 248, b: 240 }, // 26: Mint cream tint\n\t\t{ r: 248, g: 255, b: 240 }, // 27: Ivory tint\n\t\t{ r: 248, g: 240, b: 240 }, // 28: Misty rose tint\n\t\t{ r: 240, g: 240, b: 255 }, // 29: Ghost white tint\n\t\t{ r: 255, g: 245, b: 238 }, // 30: Seashell\n\t\t{ r: 245, g: 255, b: 250 }, // 31: Azure mist\n\t\t{ r: 250, g: 245, b: 255 }, // 32: Lilac mist\n\t\t{ r: 255, g: 250, b: 245 }, // 33: Snow peach\n\t\t{ r: 238, g: 245, b: 255 }, // 34: Powder blue\n\t\t{ r: 255, g: 238, b: 245 }, // 35: Pink lace\n\t\t{ r: 245, g: 255, b: 238 }, // 36: Pale lime\n\t\t{ r: 238, g: 255, b: 245 }, // 37: Pale turquoise\n\t\t{ r: 245, g: 238, b: 255 }, // 38: Pale violet\n\t\t{ r: 255, g: 245, b: 255 }, // 39: Pale magenta\n\t]\n}\n\n/**\n * Convert RGB values to hex color format\n *\n * @param r - Red value (0-255)\n * @param g - Green value (0-255)\n * @param b - Blue value (0-255)\n * @returns Hex color string (e.g., \"#dcebf8\")\n * @throws Error if RGB values are out of range\n */\nexport function rgbToHex(r: number, g: number, b: number): string {\n\t// Validate RGB values\n\tif (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {\n\t\tthrow new Error('RGB values must be between 0 and 255')\n\t}\n\n\t// Convert to hex and pad with zeros\n\tconst rHex = r.toString(16).padStart(2, '0')\n\tconst gHex = g.toString(16).padStart(2, '0')\n\tconst bHex = b.toString(16).padStart(2, '0')\n\n\treturn `#${rHex}${gHex}${bHex}`\n}\n\n/**\n * Convert hex color format to RGB values\n *\n * @param hex - Hex color string (with or without # prefix)\n * @returns RGB color object\n * @throws Error if hex format is invalid\n */\nexport function hexToRgb(hex: string): RgbColor {\n\t// Remove # prefix if present\n\tconst cleanHex = hex.startsWith('#') ? hex.slice(1) : hex\n\n\t// Validate format (must be exactly 6 hex characters)\n\tif (cleanHex.length !== 6 || !/^[0-9a-fA-F]{6}$/.test(cleanHex)) {\n\t\tthrow new Error('Invalid hex color format. Expected format: #RRGGBB or RRGGBB')\n\t}\n\n\t// Parse hex values\n\tconst r = parseInt(cleanHex.slice(0, 2), 16)\n\tconst g = parseInt(cleanHex.slice(2, 4), 16)\n\tconst b = parseInt(cleanHex.slice(4, 6), 16)\n\n\treturn { r, g, b }\n}\n\n/**\n * Generate deterministic color from branch name using SHA256 hash\n * Matches the bash implementation in bash/new-branch-workflow.sh\n *\n * @param branchName - Branch name to generate color from\n * @returns ColorData with RGB, hex, and palette index\n */\nexport function generateColorFromBranchName(branchName: string): ColorData {\n\t// Generate SHA256 hash of branch name\n\tconst hash = createHash('sha256').update(branchName).digest('hex')\n\n\t// Take first 8 hex characters and convert to index (0-39)\n\t// Matches bash: local index=$(( 0x$hash % ${#colors[@]} ))\n\tconst hashPrefix = hash.slice(0, 8)\n\tconst palette = getColorPalette()\n\tconst hashAsInt = parseInt(hashPrefix, 16)\n\tconst index = hashAsInt % palette.length\n\tlogger.debug(`[generateColorFromBranchName] Branch name: ${branchName}, Hash: ${hash}, Hash prefix: ${hashPrefix}, Hash as int: ${hashAsInt}, Index: ${index}`)\n\n\t// Get color from palette\n\tconst rgb = palette[index]\n\n\t// This should never happen as index is always in range [0, palette.length)\n\tif (!rgb) {\n\t\tthrow new Error(`Invalid color index: ${index}`)\n\t}\n\n\t// Convert to hex format\n\tconst hex = rgbToHex(rgb.r, rgb.g, rgb.b)\n\n\treturn {\n\t\trgb,\n\t\thex,\n\t\tindex,\n\t}\n}\n\n/**\n * Lighten a color by a given amount\n * Useful for creating slightly lighter variants for hover states\n *\n * @param rgb - RGB color to lighten\n * @param amount - Amount to lighten (0-1, where 0.1 = 10% lighter)\n * @returns Lightened RGB color\n */\nexport function lightenColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\treturn {\n\t\tr: clamp(rgb.r + (255 - rgb.r) * amount),\n\t\tg: clamp(rgb.g + (255 - rgb.g) * amount),\n\t\tb: clamp(rgb.b + (255 - rgb.b) * amount),\n\t}\n}\n\n/**\n * Saturate a color by pushing it away from grey towards its dominant hue\n * Makes subtle colors more vivid while maintaining their hue\n *\n * @param rgb - RGB color to saturate\n * @param amount - Amount to saturate (0-1, where 0.4 = 40% more saturated)\n * @returns Saturated RGB color\n */\nexport function saturateColor(rgb: RgbColor, amount: number): RgbColor {\n\tconst clamp = (value: number): number => Math.min(255, Math.max(0, Math.round(value)))\n\n\t// Calculate average (grey point)\n\tconst avg = (rgb.r + rgb.g + rgb.b) / 3\n\n\t// Push each channel away from grey\n\treturn {\n\t\tr: clamp(rgb.r + (rgb.r - avg) * amount),\n\t\tg: clamp(rgb.g + (rgb.g - avg) * amount),\n\t\tb: clamp(rgb.b + (rgb.b - avg) * amount),\n\t}\n}\n\n/**\n * Calculate appropriate foreground color (black or white) for a given background\n * Uses relative luminance formula from WCAG 2.0\n *\n * @param rgb - Background RGB color\n * @returns '#000000' for light backgrounds, '#ffffff' for dark backgrounds\n */\nexport function calculateForegroundColor(rgb: RgbColor): string {\n\t// Convert RGB to relative luminance (WCAG 2.0 formula)\n\tconst toLinear = (channel: number): number => {\n\t\tconst c = channel / 255\n\t\treturn c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)\n\t}\n\n\tconst r = toLinear(rgb.r)\n\tconst g = toLinear(rgb.g)\n\tconst b = toLinear(rgb.b)\n\n\tconst luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b\n\n\t// Use black text for light backgrounds (luminance > 0.5)\n\t// Use white text for dark backgrounds\n\treturn luminance > 0.5 ? '#000000' : '#ffffff'\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
logger
|
|
4
|
-
} from "./chunk-6SDFJ42P.js";
|
|
5
|
-
|
|
6
|
-
// src/mcp/github-comment-server.ts
|
|
7
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
|
|
11
|
-
// src/utils/github.ts
|
|
12
|
-
import { execa } from "execa";
|
|
13
|
-
async function executeGhCommand(args, options) {
|
|
14
|
-
const result = await execa("gh", args, {
|
|
15
|
-
cwd: (options == null ? void 0 : options.cwd) ?? process.cwd(),
|
|
16
|
-
timeout: (options == null ? void 0 : options.timeout) ?? 3e4,
|
|
17
|
-
encoding: "utf8"
|
|
18
|
-
});
|
|
19
|
-
const isJson = args.includes("--json") || args.includes("--jq") || args.includes("--format") && args[args.indexOf("--format") + 1] === "json";
|
|
20
|
-
const data = isJson ? JSON.parse(result.stdout) : result.stdout;
|
|
21
|
-
return data;
|
|
22
|
-
}
|
|
23
|
-
async function createIssueComment(issueNumber, body, repo) {
|
|
24
|
-
logger.debug("Creating issue comment", { issueNumber, repo });
|
|
25
|
-
const apiPath = repo ? `repos/${repo}/issues/${issueNumber}/comments` : `repos/:owner/:repo/issues/${issueNumber}/comments`;
|
|
26
|
-
return executeGhCommand([
|
|
27
|
-
"api",
|
|
28
|
-
apiPath,
|
|
29
|
-
"-f",
|
|
30
|
-
`body=${body}`,
|
|
31
|
-
"--jq",
|
|
32
|
-
"{id: .id, url: .html_url, created_at: .created_at}"
|
|
33
|
-
]);
|
|
34
|
-
}
|
|
35
|
-
async function updateIssueComment(commentId, body, repo) {
|
|
36
|
-
logger.debug("Updating issue comment", { commentId, repo });
|
|
37
|
-
const apiPath = repo ? `repos/${repo}/issues/comments/${commentId}` : `repos/:owner/:repo/issues/comments/${commentId}`;
|
|
38
|
-
return executeGhCommand([
|
|
39
|
-
"api",
|
|
40
|
-
apiPath,
|
|
41
|
-
"-X",
|
|
42
|
-
"PATCH",
|
|
43
|
-
"-f",
|
|
44
|
-
`body=${body}`,
|
|
45
|
-
"--jq",
|
|
46
|
-
"{id: .id, url: .html_url, updated_at: .updated_at}"
|
|
47
|
-
]);
|
|
48
|
-
}
|
|
49
|
-
async function createPRComment(prNumber, body, repo) {
|
|
50
|
-
logger.debug("Creating PR comment", { prNumber, repo });
|
|
51
|
-
const apiPath = repo ? `repos/${repo}/issues/${prNumber}/comments` : `repos/:owner/:repo/issues/${prNumber}/comments`;
|
|
52
|
-
return executeGhCommand([
|
|
53
|
-
"api",
|
|
54
|
-
apiPath,
|
|
55
|
-
"-f",
|
|
56
|
-
`body=${body}`,
|
|
57
|
-
"--jq",
|
|
58
|
-
"{id: .id, url: .html_url, created_at: .created_at}"
|
|
59
|
-
]);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// src/mcp/github-comment-server.ts
|
|
63
|
-
function validateEnvironment() {
|
|
64
|
-
const required = ["REPO_OWNER", "REPO_NAME"];
|
|
65
|
-
const missing = required.filter((key) => !process.env[key]);
|
|
66
|
-
if (missing.length > 0) {
|
|
67
|
-
console.error(
|
|
68
|
-
`Missing required environment variables: ${missing.join(", ")}`
|
|
69
|
-
);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
var server = new McpServer({
|
|
74
|
-
name: "github-comment-broker",
|
|
75
|
-
version: "0.1.0"
|
|
76
|
-
});
|
|
77
|
-
server.registerTool(
|
|
78
|
-
"create_comment",
|
|
79
|
-
{
|
|
80
|
-
title: "Create GitHub Comment",
|
|
81
|
-
description: "Create a new comment on a GitHub issue or pull request. Use this to start tracking a workflow phase.",
|
|
82
|
-
inputSchema: {
|
|
83
|
-
number: z.number().describe("The issue or PR number"),
|
|
84
|
-
body: z.string().describe("The comment body (markdown supported)"),
|
|
85
|
-
type: z.enum(["issue", "pr"]).describe("Type of entity to comment on (issue or pr)")
|
|
86
|
-
},
|
|
87
|
-
outputSchema: {
|
|
88
|
-
id: z.number(),
|
|
89
|
-
url: z.string(),
|
|
90
|
-
created_at: z.string().optional()
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
async ({ number, body, type }) => {
|
|
94
|
-
console.error(`Creating ${type} comment on #${number}`);
|
|
95
|
-
try {
|
|
96
|
-
const result = type === "issue" ? await createIssueComment(number, body) : await createPRComment(number, body);
|
|
97
|
-
console.error(
|
|
98
|
-
`Comment created successfully: ${result.id} at ${result.url}`
|
|
99
|
-
);
|
|
100
|
-
return {
|
|
101
|
-
content: [
|
|
102
|
-
{
|
|
103
|
-
type: "text",
|
|
104
|
-
text: JSON.stringify(result)
|
|
105
|
-
}
|
|
106
|
-
],
|
|
107
|
-
structuredContent: result
|
|
108
|
-
};
|
|
109
|
-
} catch (error) {
|
|
110
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
111
|
-
console.error(`Failed to create comment: ${errorMessage}`);
|
|
112
|
-
throw new Error(`Failed to create ${type} comment: ${errorMessage}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
);
|
|
116
|
-
server.registerTool(
|
|
117
|
-
"update_comment",
|
|
118
|
-
{
|
|
119
|
-
title: "Update GitHub Comment",
|
|
120
|
-
description: "Update an existing GitHub comment. Use this to update progress during a workflow phase.",
|
|
121
|
-
inputSchema: {
|
|
122
|
-
commentId: z.number().describe("The GitHub comment ID to update"),
|
|
123
|
-
body: z.string().describe("The updated comment body (markdown supported)")
|
|
124
|
-
},
|
|
125
|
-
outputSchema: {
|
|
126
|
-
id: z.number(),
|
|
127
|
-
url: z.string(),
|
|
128
|
-
updated_at: z.string().optional()
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
async ({ commentId, body }) => {
|
|
132
|
-
console.error(`Updating comment ${commentId}`);
|
|
133
|
-
try {
|
|
134
|
-
const result = await updateIssueComment(commentId, body);
|
|
135
|
-
console.error(
|
|
136
|
-
`Comment updated successfully: ${result.id} at ${result.url}`
|
|
137
|
-
);
|
|
138
|
-
return {
|
|
139
|
-
content: [
|
|
140
|
-
{
|
|
141
|
-
type: "text",
|
|
142
|
-
text: JSON.stringify(result)
|
|
143
|
-
}
|
|
144
|
-
],
|
|
145
|
-
structuredContent: result
|
|
146
|
-
};
|
|
147
|
-
} catch (error) {
|
|
148
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
149
|
-
console.error(`Failed to update comment: ${errorMessage}`);
|
|
150
|
-
throw new Error(`Failed to update comment: ${errorMessage}`);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
);
|
|
154
|
-
async function main() {
|
|
155
|
-
console.error("Starting GitHub Comment MCP Server...");
|
|
156
|
-
validateEnvironment();
|
|
157
|
-
console.error("Environment validated");
|
|
158
|
-
console.error(`Repository: ${process.env.REPO_OWNER}/${process.env.REPO_NAME}`);
|
|
159
|
-
console.error(`Event type: ${process.env.GITHUB_EVENT_NAME ?? "not specified"}`);
|
|
160
|
-
const transport = new StdioServerTransport();
|
|
161
|
-
await server.connect(transport);
|
|
162
|
-
console.error("GitHub Comment MCP Server running on stdio transport");
|
|
163
|
-
}
|
|
164
|
-
main().catch((error) => {
|
|
165
|
-
console.error("Fatal error starting MCP server:", error);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
});
|
|
168
|
-
//# sourceMappingURL=github-comment-server.js.map
|