Skip to content

Commit 3d4979e

Browse files
committed
feat: add auto-install support for the Junie agent
Signed-off-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
1 parent b075f05 commit 3d4979e

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
@@ -1143,6 +1143,10 @@ cbm_detected_agents_t cbm_detect_agents(const char *home_dir) {
11431143
snprintf(path, sizeof(path), "%s/.kiro", home_dir);
11441144
agents.kiro = dir_exists(path);
11451145

1146+
/* Junie (JetBrains): ~/.junie/ */
1147+
snprintf(path, sizeof(path), "%s/.junie", home_dir);
1148+
agents.junie = dir_exists(path);
1149+
11461150
return agents;
11471151
}
11481152

@@ -1679,6 +1683,17 @@ int cbm_remove_antigravity_mcp(const char *config_path) {
16791683
return cbm_remove_editor_mcp(config_path);
16801684
}
16811685

1686+
/* ── Junie MCP config (JSON, same mcpServers format) ──────────── */
1687+
1688+
int cbm_upsert_junie_mcp(const char *binary_path, const char *config_path) {
1689+
/* Junie (JetBrains) uses same mcpServers format as Cursor/Antigravity */
1690+
return cbm_install_editor_mcp(binary_path, config_path);
1691+
}
1692+
1693+
int cbm_remove_junie_mcp(const char *config_path) {
1694+
return cbm_remove_editor_mcp(config_path);
1695+
}
1696+
16821697
/* ── Claude Code pre-tool hooks ───────────────────────────────── */
16831698

16841699
/* Matcher intentionally excludes Read: gating Read breaks Claude Code's
@@ -2934,6 +2949,7 @@ static void print_detected_agents(const cbm_detected_agents_t *a) {
29342949
{a->cursor, "Cursor"},
29352950
{a->openclaw, "OpenClaw"},
29362951
{a->kiro, "Kiro"},
2952+
{a->junie, "Junie"},
29372953
};
29382954
printf("Detected agents:");
29392955
bool any = false;
@@ -3247,6 +3263,16 @@ static void install_editor_agent_configs(const cbm_detected_agents_t *agents, co
32473263
install_generic_agent_config("Kiro", binary_path, cp, NULL, dry_run,
32483264
cbm_install_editor_mcp);
32493265
}
3266+
if (agents->junie) {
3267+
char cp[CLI_BUF_1K];
3268+
char sd[CLI_BUF_1K];
3269+
snprintf(cp, sizeof(cp), "%s/.junie/mcp/mcp.json", home);
3270+
snprintf(sd, sizeof(sd), "%s/.junie/mcp", home);
3271+
if (!dry_run) {
3272+
cbm_mkdir_p(sd, CLI_OCTAL_PERM);
3273+
}
3274+
install_generic_agent_config("Junie", binary_path, cp, NULL, dry_run, cbm_upsert_junie_mcp);
3275+
}
32503276
}
32513277

32523278
static void cbm_install_agent_configs(const char *home, const char *binary_path, bool force,
@@ -3346,6 +3372,7 @@ char *cbm_build_install_plan_json(const char *home, const char *binary_path) {
33463372
{det.cursor, "cursor"},
33473373
{det.openclaw, "openclaw"},
33483374
{det.kiro, "kiro"},
3375+
{det.junie, "junie"},
33493376
};
33503377

33513378
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
@@ -3720,6 +3747,12 @@ static void uninstall_editor_agents(const cbm_detected_agents_t *agents, const c
37203747
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Kiro", cp, NULL}, dry_run,
37213748
cbm_remove_editor_mcp);
37223749
}
3750+
if (agents->junie) {
3751+
char cp[CLI_BUF_1K];
3752+
snprintf(cp, sizeof(cp), "%s/.junie/mcp/mcp.json", home);
3753+
uninstall_agent_mcp_instr((mcp_uninstall_args_t){"Junie", cp, NULL}, dry_run,
3754+
cbm_remove_junie_mcp);
3755+
}
37233756
}
37243757

37253758
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
@@ -128,6 +128,7 @@ typedef struct {
128128
bool cursor; /* ~/.cursor/ exists */
129129
bool openclaw; /* ~/.openclaw/ exists */
130130
bool kiro; /* ~/.kiro/ exists */
131+
bool junie; /* ~/.junie/ exists */
131132
} cbm_detected_agents_t;
132133

133134
/* Detect which coding agents are installed.
@@ -155,6 +156,13 @@ int cbm_upsert_antigravity_mcp(const char *binary_path, const char *config_path)
155156
/* Remove CMM MCP entry from antigravity mcp_config.json. Returns 0 on success. */
156157
int cbm_remove_antigravity_mcp(const char *config_path);
157158

159+
/* Junie (JetBrains): upsert MCP entry in ~/.junie/mcp/mcp.json (mcpServers format).
160+
* Returns 0 on success. */
161+
int cbm_upsert_junie_mcp(const char *binary_path, const char *config_path);
162+
163+
/* Remove CMM MCP entry from Junie mcp.json. Returns 0 on success. */
164+
int cbm_remove_junie_mcp(const char *config_path);
165+
158166
/* ── Instructions file upsert ─────────────────────────────────── */
159167

160168
/* 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
@@ -695,6 +695,48 @@ TEST(cli_editor_mcp_uninstall) {
695695
PASS();
696696
}
697697

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

1819+
/* issue #651: Junie (~/.junie/) must be detected so install registers the
1820+
* MCP server in ~/.junie/mcp/mcp.json. */
1821+
TEST(cli_detect_agents_finds_junie_issue651) {
1822+
char tmpdir[256];
1823+
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
1824+
if (!cbm_mkdtemp(tmpdir))
1825+
FAIL("cbm_mkdtemp failed");
1826+
char dir[512];
1827+
snprintf(dir, sizeof(dir), "%s/.junie", tmpdir);
1828+
test_mkdirp(dir);
1829+
cbm_detected_agents_t agents = cbm_detect_agents(tmpdir);
1830+
ASSERT_TRUE(agents.junie);
1831+
test_rmdir_r(tmpdir);
1832+
PASS();
1833+
}
1834+
17771835
TEST(cli_detect_agents_none_found) {
17781836
char tmpdir[256];
17791837
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-detect-XXXXXX");
@@ -2692,6 +2750,7 @@ SUITE(cli) {
26922750
RUN_TEST(cli_editor_mcp_idempotent);
26932751
RUN_TEST(cli_editor_mcp_preserves_others);
26942752
RUN_TEST(cli_editor_mcp_uninstall);
2753+
RUN_TEST(cli_junie_mcp_install_issue651);
26952754
RUN_TEST(cli_gemini_mcp_install);
26962755

26972756
/* VS Code MCP (2 tests — install_test.go) */
@@ -2756,6 +2815,7 @@ SUITE(cli) {
27562815
RUN_TEST(cli_detect_agents_finds_antigravity);
27572816
RUN_TEST(cli_detect_agents_finds_kilocode);
27582817
RUN_TEST(cli_detect_agents_finds_kiro);
2818+
RUN_TEST(cli_detect_agents_finds_junie_issue651);
27592819
RUN_TEST(cli_detect_agents_none_found);
27602820

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

0 commit comments

Comments
 (0)