From cd0565dc481922646ce544f2f36c8284648ff070 Mon Sep 17 00:00:00 2001 From: Oleg Samarin Date: Tue, 14 Apr 2026 10:15:00 +0300 Subject: [PATCH] =?UTF-8?q?Fixed=20Default=20profile=20settings=20not=20sa?= =?UTF-8?q?ving=20in=20Tools=20=E2=86=92=20Options=20=E2=86=92=20Profiles?= =?UTF-8?q?=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backport of 166921d to 0.20. Serialises the full Default profile to a dedicated NbPreferences key (KEY_DEFAULT_PROFILE) so that all fields (API key, proxy, Extra CLI args, etc.) survive a save/load cycle. Back-compat: the legacy KEY_DEFAULT_EXTRA_CLI_ARGS key is still written and read as a fallback. Resolves: #31 Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 1 + pom.xml | 2 +- .../settings/ClaudeProfileStore.java | 26 ++++++++++- .../settings/ClaudeProfileStoreTest.java | 44 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 897340e3..74379d5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fixed Default profile settings not saving in Tools → Options → Profiles (https://github.com/nbclaudecodegui/NetbeansClaudeCodeGui/issues/31) - Fixed "Open with Claude Code" context menu not applying Default profile settings (API key, proxy, Extra CLI args, etc.) (https://github.com/nbclaudecodegui/NetbeansClaudeCodeGui/issues/23) - 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) - Fixed process start errors and hang timeouts now logged to the IDE log file at SEVERE level diff --git a/pom.xml b/pom.xml index ec59dc01..91d144ab 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.nbclaudecodegui netbeans-claude-code-gui - 0.20.6-SNAPSHOT + 0.20.7-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 eda7dd48..659af658 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,19 @@ private ClaudeProfileStore() {} public static List getProfiles() { List result = new ArrayList<>(); ClaudeProfile def = ClaudeProfile.createDefault(); - def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs()); + String defJson = NbPreferences.forModule(ClaudeProfileStore.class) + .get(KEY_DEFAULT_PROFILE, null); + if (defJson != null) { + try { + def = MAPPER.readValue(defJson, ClaudeProfile.class); + } catch (Exception e) { + LOG.warning("Could not parse stored Default profile JSON, using defaults. Cause: " + e); + def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs()); + } + } else { + // back-compat: read only extraCliArgs from old key + def.setExtraCliArgs(ClaudeCodePreferences.getDefaultExtraCliArgs()); + } result.add(def); String json = NbPreferences.forModule(ClaudeProfileStore.class) @@ -117,10 +132,12 @@ public static ClaudeProfile findByName(String name) { */ public static void saveProfiles(List profiles) { List toSave = new ArrayList<>(); + ClaudeProfile defaultProfile = null; if (profiles != null) { for (ClaudeProfile p : profiles) { if (p.isDefault()) { - ClaudeCodePreferences.setDefaultExtraCliArgs(p.getExtraCliArgs()); + defaultProfile = p; + ClaudeCodePreferences.setDefaultExtraCliArgs(p.getExtraCliArgs()); // back-compat } else { toSave.add(p); } @@ -130,6 +147,11 @@ public static void saveProfiles(List profiles) { String json = MAPPER.writeValueAsString(toSave); NbPreferences.forModule(ClaudeProfileStore.class) .put(KEY_PROFILES, json); + if (defaultProfile != null) { + String defJson = MAPPER.writeValueAsString(defaultProfile); + NbPreferences.forModule(ClaudeProfileStore.class) + .put(KEY_DEFAULT_PROFILE, defJson); + } LOG.fine(() -> "Saved " + toSave.size() + " profile(s): " + profileNames(toSave)); } catch (Exception e) { LOG.severe("Could not serialise profiles: " + e.getMessage()); diff --git a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java index 3428db72..ca70cbe6 100644 --- a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java +++ b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeProfileStoreTest.java @@ -228,6 +228,50 @@ void defaultProfile_extraCliArgs_roundTrip() { } } + // ------------------------------------------------------------------------- + // Default profile all-fields round-trip (issue #31) + // ------------------------------------------------------------------------- + + @Test + void defaultProfile_allFields_roundTrip() { + try { + ClaudeProfile def = ClaudeProfile.createDefault(); + def.setApiKey("sk-ant-default"); + def.setExtraCliArgs("--verbose"); + ClaudeProfileStore.saveProfiles(java.util.List.of(def)); + + ClaudeProfile loaded = ClaudeProfileStore.getDefaultProfile(); + assertTrue(loaded.isDefault()); + assertEquals("sk-ant-default", loaded.getApiKey(), + "Default profile apiKey must survive save/load"); + assertEquals("--verbose", loaded.getExtraCliArgs(), + "Default profile extraCliArgs must survive save/load"); + } 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-ant-work"); + p.setExtraCliArgs("--model claude-3-5-sonnet-20241022"); + ClaudeProfileStore.saveProfiles(java.util.List.of(p)); + + List loaded = ClaudeProfileStore.getProfiles(); + assertEquals(2, loaded.size(), "Expected Default + Work"); + ClaudeProfile work = loaded.get(1); + assertEquals("Work", work.getName()); + assertEquals("sk-ant-work", work.getApiKey(), + "Named profile apiKey must survive save/load"); + assertEquals("--model claude-3-5-sonnet-20241022", work.getExtraCliArgs(), + "Named profile extraCliArgs must survive save/load"); + } catch (Exception e) { + org.junit.jupiter.api.Assumptions.assumeTrue(false, "NbPreferences not available"); + } + } + // ------------------------------------------------------------------------- // extraCliArgs serialization round-trip // -------------------------------------------------------------------------