diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3515a80c..35569262 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+- Fixed Default profile settings (API key, connection type, proxy, etc.) not being saved when edited in Tools → Options → Claude Code → Profiles
- Fixed MCP config not working on Windows due to quote stripping in CreateProcess (https://github.com/nbclaudecodegui/NetbeansClaudeCodeGui/issues/13)
- Improved error panel on process start failure: shows Working Directory and CLAUDE_CONFIG_DIR fields (before Command and Error) for easier diagnosis; all fields have a right-click Copy menu; fixed "Restart Advanced…" not dismissing the error panel
- Added Advanced tab in Tools → Options → Claude Code with Debug mode, Hang timeout, and a new "Enable MCP integration" toggle (disabling it skips the --mcp-config flag while hooks remain active)
diff --git a/pom.xml b/pom.xml
index 7a1e19f9..76c0ac7c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
io.github.nbclaudecodegui
netbeans-claude-code-gui
- 0.21.39-SNAPSHOT
+ 0.21.40-SNAPSHOT
nbm
Netbeans Claude Code GUI
NetBeans plugin that provides a GUI for Claude Code CLI — run claude code sessions directly inside the IDE.
diff --git a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeProfileStore.java b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeProfileStore.java
index 6b559969..ccfa8a60 100644
--- a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeProfileStore.java
+++ b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeProfileStore.java
@@ -34,6 +34,9 @@ public final class ClaudeProfileStore {
/** NbPreferences key under which the JSON array is stored. */
public static final String KEY_PROFILES = "profiles";
+ /** NbPreferences key under which the full Default profile JSON is stored. */
+ private static final String KEY_DEFAULT_PROFILE = "defaultProfile";
+
private static final ObjectMapper MAPPER = new ObjectMapper()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
private static final TypeReference> LIST_TYPE =
@@ -56,7 +59,32 @@ private ClaudeProfileStore() {}
public static List getProfiles() {
List result = new ArrayList<>();
ClaudeProfile def = ClaudeProfile.createDefault();
- def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs());
+
+ String defaultJson = NbPreferences.forModule(ClaudeProfileStore.class)
+ .get(KEY_DEFAULT_PROFILE, null);
+ if (defaultJson != null) {
+ try {
+ ClaudeProfile stored = MAPPER.readValue(defaultJson, ClaudeProfile.class);
+ def.setToken(stored.getToken());
+ def.setApiKey(stored.getApiKey());
+ def.setBaseUrl(stored.getBaseUrl());
+ def.setProxyMode(stored.getProxyMode());
+ def.setHttpProxy(stored.getHttpProxy());
+ def.setHttpsProxy(stored.getHttpsProxy());
+ def.setNoProxy(stored.getNoProxy());
+ def.setExtraEnvVars(stored.getExtraEnvVars());
+ def.setExtraCliArgs(stored.getExtraCliArgs());
+ def.setModelAliases(stored.getModelAliases());
+ def.setCustomModels(stored.getCustomModels());
+ def.setStorageDir(stored.getStorageDir());
+ } catch (Exception e) {
+ LOG.warning("Could not parse stored Default profile JSON, using defaults. Cause: " + e);
+ def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs());
+ }
+ } else {
+ def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs());
+ }
+
result.add(def);
String json = NbPreferences.forModule(ClaudeProfileStore.class)
@@ -108,7 +136,14 @@ public static void saveProfiles(List profiles) {
if (profiles != null) {
for (ClaudeProfile p : profiles) {
if (p.isDefault()) {
- ClaudeCodePreferences.setDefaultExtraCliArgs(p.getExtraCliArgs());
+ ClaudeCodePreferences.setDefaultExtraCliArgs(p.getExtraCliArgs()); // back-compat
+ try {
+ String defaultJson = MAPPER.writeValueAsString(p);
+ NbPreferences.forModule(ClaudeProfileStore.class)
+ .put(KEY_DEFAULT_PROFILE, defaultJson);
+ } catch (Exception e) {
+ LOG.severe("Could not serialise Default profile: " + e.getMessage());
+ }
} else {
toSave.add(p);
}
diff --git a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java
index 35d1bba1..063060b5 100644
--- a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java
+++ b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java
@@ -203,6 +203,83 @@ void extraCliArgs_jacksonRoundTrip() throws Exception {
assertEquals("--verbose --model foo", loaded.get(0).getExtraCliArgs());
}
+ // -------------------------------------------------------------------------
+ // Default profile full round-trip (bug: all fields except extraCliArgs were lost)
+ // -------------------------------------------------------------------------
+
+ @Test
+ void defaultProfile_allFields_roundTrip() {
+ try {
+ ClaudeProfile def = ClaudeProfile.createDefault();
+ def.setApiKey("sk-api-key-123");
+ def.setToken("oauth-token-xyz");
+ def.setBaseUrl("https://custom.api.example.com");
+ def.setProxyMode(ClaudeProfile.ProxyMode.CUSTOM);
+ def.setHttpProxy("http://proxy.example.com:8080");
+ def.setHttpsProxy("https://proxy.example.com:8443");
+ def.setNoProxy("localhost,127.0.0.1");
+ java.util.List defEnvVars = new java.util.ArrayList<>();
+ defEnvVars.add(new String[]{"FOO", "bar"});
+ def.setExtraEnvVars(defEnvVars);
+ def.setExtraCliArgs("--verbose");
+
+ ClaudeProfileStore.saveProfiles(java.util.List.of(def));
+
+ List loaded = ClaudeProfileStore.getProfiles();
+ ClaudeProfile loadedDef = loaded.get(0);
+ assertTrue(loadedDef.isDefault());
+ assertEquals("sk-api-key-123", loadedDef.getApiKey());
+ assertEquals("oauth-token-xyz", loadedDef.getToken());
+ assertEquals("https://custom.api.example.com", loadedDef.getBaseUrl());
+ assertEquals(ClaudeProfile.ProxyMode.CUSTOM, loadedDef.getProxyMode());
+ assertEquals("http://proxy.example.com:8080", loadedDef.getHttpProxy());
+ assertEquals("https://proxy.example.com:8443", loadedDef.getHttpsProxy());
+ assertEquals("localhost,127.0.0.1", loadedDef.getNoProxy());
+ assertEquals("--verbose", loadedDef.getExtraCliArgs());
+ assertEquals(1, loadedDef.getExtraEnvVars().size());
+ assertArrayEquals(new String[]{"FOO", "bar"}, (Object[]) loadedDef.getExtraEnvVars().get(0));
+ } catch (Exception e) {
+ org.junit.jupiter.api.Assumptions.assumeTrue(false, "NbPreferences not available");
+ }
+ }
+
+ @Test
+ void namedProfile_allFields_roundTrip() {
+ try {
+ ClaudeProfile p = ClaudeProfile.createNamed("Work");
+ p.setApiKey("sk-named-key");
+ p.setToken("named-token");
+ p.setBaseUrl("https://api.work.example.com");
+ p.setProxyMode(ClaudeProfile.ProxyMode.CUSTOM);
+ p.setHttpProxy("http://work-proxy:8080");
+ p.setHttpsProxy("https://work-proxy:8443");
+ p.setNoProxy("internal.example.com");
+ java.util.List namedEnvVars = new java.util.ArrayList<>();
+ namedEnvVars.add(new String[]{"ENV_VAR", "value1"});
+ p.setExtraEnvVars(namedEnvVars);
+ p.setExtraCliArgs("--model claude-opus");
+
+ ClaudeProfileStore.saveProfiles(java.util.List.of(ClaudeProfile.createDefault(), p));
+
+ List loaded = ClaudeProfileStore.getProfiles();
+ assertEquals(2, loaded.size());
+ ClaudeProfile loadedP = loaded.get(1);
+ assertEquals("Work", loadedP.getName());
+ assertEquals("sk-named-key", loadedP.getApiKey());
+ assertEquals("named-token", loadedP.getToken());
+ assertEquals("https://api.work.example.com", loadedP.getBaseUrl());
+ assertEquals(ClaudeProfile.ProxyMode.CUSTOM, loadedP.getProxyMode());
+ assertEquals("http://work-proxy:8080", loadedP.getHttpProxy());
+ assertEquals("https://work-proxy:8443", loadedP.getHttpsProxy());
+ assertEquals("internal.example.com", loadedP.getNoProxy());
+ assertEquals("--model claude-opus", loadedP.getExtraCliArgs());
+ assertEquals(1, loadedP.getExtraEnvVars().size());
+ assertArrayEquals(new String[]{"ENV_VAR", "value1"}, (Object[]) loadedP.getExtraEnvVars().get(0));
+ } catch (Exception e) {
+ org.junit.jupiter.api.Assumptions.assumeTrue(false, "NbPreferences not available");
+ }
+ }
+
// -------------------------------------------------------------------------
// resolveStorageDir
// -------------------------------------------------------------------------