Skip to content

Commit 9a03ed3

Browse files
authored
Merge pull request #664 from mvanhorn/feat/651-feat-auto-install-support-for-the-junie-
feat: auto-install support for the Junie agent
2 parents 59da01e + 3d4979e commit 9a03ed3

3 files changed

Lines changed: 101 additions & 0 deletions

File tree

src/cli/cli.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,10 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
12351235
snprintf(path, sizeof(path), "%s/.kiro", home_dir);
12361236
agents.kiro = dir_exists(path);
12371237

1238+
/* Junie (JetBrains): ~/.junie/ */
1239+
snprintf(path, sizeof(path), "%s/.junie", home_dir);
1240+
agents.junie = dir_exists(path);
1241+
12381242
return agents;
12391243
}
12401244

@@ -1771,6 +1775,17 @@ int cbm_remove_antigravity_mcp(const char *config_path) {
17711775
return cbm_remove_editor_mcp(config_path);
17721776
}
17731777

1778+
/* ── Junie MCP config (JSON, same mcpServers format) ──────────── */
1779+
1780+
int cbm_upsert_junie_mcp(const char *binary_path, const char *config_path) {
1781+
/* Junie (JetBrains) uses same mcpServers format as Cursor/Antigravity */
1782+
return cbm_install_editor_mcp(binary_path, config_path);
1783+
}
1784+
1785+
int cbm_remove_junie_mcp(const char *config_path) {
1786+
return cbm_remove_editor_mcp(config_path);
1787+
}
1788+
17741789
/* ── Claude Code pre-tool hooks ───────────────────────────────── */
17751790

17761791
/* Matcher intentionally excludes Read: gating Read breaks Claude Code's
@@ -3148,6 +3163,7 @@ static void print_detected_agents(const cbm_detected_agents_t *a) {
31483163
{a->cursor, "Cursor"},
31493164
{a->openclaw, "OpenClaw"},
31503165
{a->kiro, "Kiro"},
3166+
{a->junie, "Junie"},
31513167
};
31523168
printf("Detected agents:");
31533169
bool any = false;
@@ -3516,6 +3532,16 @@ static void install_editor_agent_configs(const cbm_detected_agents_t *agents, co
35163532
install_generic_agent_config("Kiro", binary_path, cp, NULL, dry_run,
35173533
cbm_install_editor_mcp);
35183534
}
3535+
if (agents->junie) {
3536+
char cp[CLI_BUF_1K];
3537+
char sd[CLI_BUF_1K];
3538+
snprintf(cp, sizeof(cp), "%s/.junie/mcp/mcp.json", home);
3539+
snprintf(sd, sizeof(sd), "%s/.junie/mcp", home);
3540+
if (!dry_run) {
3541+
cbm_mkdir_p(sd, CLI_OCTAL_PERM);
3542+
}
3543+
install_generic_agent_config("Junie", binary_path, cp, NULL, dry_run, cbm_upsert_junie_mcp);
3544+
}
35193545
}
35203546

35213547
static void cbm_install_agent_configs(const char *home, const char *binary_path, bool force,
@@ -3668,6 +3694,7 @@ char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
36683694
{det.cursor, "cursor"},
36693695
{det.openclaw, "openclaw"},
36703696
{det.kiro, "kiro"},
3697+
{det.junie, "junie"},
36713698
};
36723699

36733700
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
@@ -4041,6 +4068,12 @@ static void uninstall_editor_agents(const cbm_detected_agents_t *agents, const c
40414068
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Kiro", cp, NULL}, dry_run,
40424069
cbm_remove_editor_mcp);
40434070
}
4071+
if (agents->junie) {
4072+
char cp[CLI_BUF_1K];
4073+
snprintf(cp, sizeof(cp), "%s/.junie/mcp/mcp.json", home);
4074+
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Junie", cp, NULL}, dry_run,
4075+
cbm_remove_junie_mcp);
4076+
}
40444077
}
40454078

40464079
int cbm_cmd_uninstall(int argc, char **argv) {

src/cli/cli.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ typedef struct {
154154
bool cursor; /* ~/.cursor/ exists */
155155
bool openclaw; /* ~/.openclaw/ exists */
156156
bool kiro; /* ~/.kiro/ exists */
157+
bool junie; /* ~/.junie/ exists */
157158
} cbm_detected_agents_t;
158159

159160
/* Detect which coding agents are installed.
@@ -181,6 +182,13 @@ int cbm_upsert_antigravity_mcp(const char *binary_path, const char *config_path)
181182
/* Remove CMM MCP entry from antigravity mcp_config.json. Returns 0 on success. */
182183
int cbm_remove_antigravity_mcp(const char *config_path);
183184

185+
/* Junie (JetBrains): upsert MCP entry in ~/.junie/mcp/mcp.json (mcpServers format).
186+
* Returns 0 on success. */
187+
int cbm_upsert_junie_mcp(const char *binary_path, const char *config_path);
188+
189+
/* Remove CMM MCP entry from Junie mcp.json. Returns 0 on success. */
190+
int cbm_remove_junie_mcp(const char *config_path);
191+
184192
/* ── Instructions file upsert ─────────────────────────────────── */
185193

186194
/* Upsert a codebase-memory-mcp instruction section in a markdown file.

tests/test_cli.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,48 @@ TEST(cli_editor_mcp_uninstall) {
696696
PASS();
697697
}
698698

699+
TEST(cli_junie_mcp_install_issue651) {
700+
char tmpdir[256];
701+
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-mcp-XXXXXX");
702+
if (!cbm_mkdtemp(tmpdir))
703+
FAIL("cbm_mkdtemp failed");
704+
705+
char configpath[512];
706+
snprintf(configpath, sizeof(configpath), "%s/.junie/mcp/mcp.json", tmpdir);
707+
708+
int rc = cbm_upsert_junie_mcp("/usr/local/bin/codebase-memory-mcp", configpath);
709+
ASSERT_EQ(rc, 0);
710+
711+
const char *data = read_test_file(configpath);
712+
ASSERT_NOT_NULL(data);
713+
ASSERT(strstr(data, "mcpServers") != NULL);
714+
ASSERT(strstr(data, "codebase-memory-mcp") != NULL);
715+
ASSERT(strstr(data, "/usr/local/bin/codebase-memory-mcp") != NULL);
716+
717+
rc = cbm_upsert_junie_mcp("/usr/local/bin/codebase-memory-mcp", configpath);
718+
ASSERT_EQ(rc, 0);
719+
720+
data = read_test_file(configpath);
721+
ASSERT_NOT_NULL(data);
722+
int count = 0;
723+
const char *p = data;
724+
while ((p = strstr(p, "\"codebase-memory-mcp\"")) != NULL) {
725+
count++;
726+
p += 20;
727+
}
728+
ASSERT_EQ(count, 1);
729+
730+
rc = cbm_remove_junie_mcp(configpath);
731+
ASSERT_EQ(rc, 0);
732+
733+
data = read_test_file(configpath);
734+
ASSERT_NOT_NULL(data);
735+
ASSERT(strstr(data, "\"codebase-memory-mcp\"") == NULL);
736+
737+
test_rmdir_r(tmpdir);
738+
PASS();
739+
}
740+
699741
TEST(cli_gemini_mcp_install) {
700742
/* Port of TestGeminiMCPInstall */
701743
char tmpdir[256];
@@ -1962,6 +2004,22 @@ TEST(cli_detect_agents_finds_kiro) {
19622004
PASS();
19632005
}
19642006

2007+
/* issue #651: Junie (~/.junie/) must be detected so install registers the
2008+
* MCP server in ~/.junie/mcp/mcp.json. */
2009+
TEST(cli_detect_agents_finds_junie_issue651) {
2010+
char tmpdir[256];
2011+
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
2012+
if (!cbm_mkdtemp(tmpdir))
2013+
FAIL("cbm_mkdtemp failed");
2014+
char dir[512];
2015+
snprintf(dir, sizeof(dir), "%s/.junie", tmpdir);
2016+
test_mkdirp(dir);
2017+
cbm_detected_agents_t agents = cbm_detect_agents(tmpdir);
2018+
ASSERT_TRUE(agents.junie);
2019+
test_rmdir_r(tmpdir);
2020+
PASS();
2021+
}
2022+
19652023
TEST(cli_detect_agents_none_found) {
19662024
char tmpdir[256];
19672025
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
@@ -2990,6 +3048,7 @@ SUITE(cli) {
29903048
RUN_TEST(cli_editor_mcp_idempotent);
29913049
RUN_TEST(cli_editor_mcp_preserves_others);
29923050
RUN_TEST(cli_editor_mcp_uninstall);
3051+
RUN_TEST(cli_junie_mcp_install_issue651);
29933052
RUN_TEST(cli_gemini_mcp_install);
29943053
RUN_TEST(cli_openclaw_mcp_install_uses_nested_servers);
29953054
RUN_TEST(cli_openclaw_mcp_preserves_existing_config);
@@ -3060,6 +3119,7 @@ SUITE(cli) {
30603119
RUN_TEST(cli_detect_agents_finds_antigravity);
30613120
RUN_TEST(cli_detect_agents_finds_kilocode);
30623121
RUN_TEST(cli_detect_agents_finds_kiro);
3122+
RUN_TEST(cli_detect_agents_finds_junie_issue651);
30633123
RUN_TEST(cli_detect_agents_none_found);
30643124

30653125
/* Codex MCP config upsert (3 tests — group B) */

0 commit comments

Comments
 (0)