Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>io.github.nbclaudecodegui</groupId>
<artifactId>netbeans-claude-code-gui</artifactId>
<version>0.20.6-SNAPSHOT</version>
<version>0.20.7-SNAPSHOT</version>
<packaging>nbm</packaging>
<name>Netbeans Claude Code GUI</name>
<description>NetBeans plugin that provides a GUI for Claude Code CLI — run claude code sessions directly inside the IDE.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClaudeProfile>> LIST_TYPE =
Expand All @@ -56,7 +59,19 @@ private ClaudeProfileStore() {}
public static List<ClaudeProfile> getProfiles() {
List<ClaudeProfile> 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)
Expand Down Expand Up @@ -117,10 +132,12 @@ public static ClaudeProfile findByName(String name) {
*/
public static void saveProfiles(List<ClaudeProfile> profiles) {
List<ClaudeProfile> 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);
}
Expand All @@ -130,6 +147,11 @@ public static void saveProfiles(List<ClaudeProfile> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ClaudeProfile> 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
// -------------------------------------------------------------------------
Expand Down
Loading