feat(settings): Claude category — new-session defaults (T-457)
A Settings → Claude category sets per-user defaults for NEW sessions: model, effort, and permission mode (generic schema selects persisting app.claude.default*). The pane reads them at spawn — effort flows through the existing --effort flag (SpawnSpec.effort); model and permission mode are sent as control requests right after a fresh (non-resume, non-fork) session starts. 'default'/unset values are no-ops, leaving the CLI's own defaults. The optional "settings changed — apply to current sessions?" prompt is filed as T-470: it needs a custom control (a generic select can't prompt), so it's a clean follow-up on the T-452 escape hatch. Tests: defaultEffortFlag sentinel handling; the category contributes the three default fields. The thin pane-side applySessionDefaults is covered by the gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import 'conversation_controller.dart';
|
||||
import 'conversation_view.dart';
|
||||
import 'model_picker_card.dart';
|
||||
import 'permission_mode_control.dart';
|
||||
import 'session_defaults.dart';
|
||||
import 'prompt_card.dart';
|
||||
import 'session_index.dart';
|
||||
import 'session_naming.dart';
|
||||
@@ -294,6 +295,11 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
|
||||
Future<void> _spawn() async {
|
||||
if (!mounted) return;
|
||||
// New sessions inherit the user's Claude defaults (T-457): effort flows
|
||||
// through --effort below; model + permission mode are applied post-spawn.
|
||||
final settings = ClideKernel.of(context).settings;
|
||||
_effort ??= defaultEffortFlag(settings);
|
||||
var isNewSession = false;
|
||||
final ipc = _ipc();
|
||||
if (ipc == null || !ipc.isConnected) {
|
||||
setState(() => _error = 'Daemon not connected.');
|
||||
@@ -370,11 +376,16 @@ class _ClaudePaneState extends State<ClaudePane> {
|
||||
return;
|
||||
}
|
||||
if (!mounted) return;
|
||||
isNewSession = !resume;
|
||||
setState(() => _statusLine = resume ? 'resumed · $_sessionId' : 'new session · $_sessionId');
|
||||
}
|
||||
|
||||
_session = managed.session;
|
||||
_conversation = managed.conversation;
|
||||
// A brand-new session (not a resume or fork) starts on the user's default
|
||||
// model + permission mode (T-457). Sent as control requests; the CLI
|
||||
// applies them after init. 'default'/unset values are no-ops.
|
||||
if (isNewSession) applySessionDefaults(managed.session, settings);
|
||||
// The wire never reports effort — record what this session was spawned
|
||||
// with so the status line / sidebar can show it (T-412).
|
||||
if (_effort != null) managed.session.noteEffort(_effort!);
|
||||
|
||||
@@ -10,7 +10,9 @@ import 'package:clide/builtin/claude/src/claude_session_host.dart';
|
||||
import 'package:clide/builtin/claude/src/session_orchestrator.dart';
|
||||
import 'package:clide/builtin/claude/src/pane_context_status.dart';
|
||||
import 'package:clide/builtin/claude/src/claude_meta_sidebar.dart';
|
||||
import 'package:clide/builtin/claude/src/session_defaults.dart';
|
||||
import 'package:clide/builtin/claude/src/session_index.dart';
|
||||
import 'package:clide/builtin/claude/src/stream_json_session.dart' show kEffortLevels, kFallbackModels, kPermissionModes;
|
||||
import 'package:clide/builtin/claude/src/session_storage.dart';
|
||||
import 'package:clide/builtin/claude/src/ticket_pick_up.dart';
|
||||
import 'package:clide/builtin/claude/src/transcript_reader.dart' show ImageMessage;
|
||||
@@ -132,6 +134,49 @@ class ClaudeExtension extends ClideExtension {
|
||||
],
|
||||
),
|
||||
),
|
||||
// Claude settings category (T-457) — defaults applied to NEW sessions
|
||||
// (the pane reads these keys at spawn). Effort flows through --effort;
|
||||
// model + permission mode are sent as control requests post-spawn.
|
||||
SettingsCategoryContribution(
|
||||
id: 'claude',
|
||||
category: SettingsCategory(
|
||||
id: 'claude',
|
||||
title: 'Claude',
|
||||
iconName: 'sparkle',
|
||||
priority: 40,
|
||||
sections: [
|
||||
SettingsSection(
|
||||
label: 'New session defaults',
|
||||
fields: [
|
||||
SettingsField(
|
||||
key: kDefaultModelKey,
|
||||
kind: SettingsFieldKind.select,
|
||||
label: 'Model',
|
||||
help: 'Model for new sessions.',
|
||||
defaultValue: 'default',
|
||||
options: [for (final m in kFallbackModels) SettingsOption(value: m.value, label: m.displayName)],
|
||||
),
|
||||
SettingsField(
|
||||
key: kDefaultEffortKey,
|
||||
kind: SettingsFieldKind.select,
|
||||
label: 'Effort',
|
||||
help: 'Reasoning effort for new sessions (applied via --effort at spawn).',
|
||||
defaultValue: 'high',
|
||||
options: [for (final l in kEffortLevels) SettingsOption(value: l.value, label: l.displayName)],
|
||||
),
|
||||
SettingsField(
|
||||
key: kDefaultPermissionModeKey,
|
||||
kind: SettingsFieldKind.select,
|
||||
label: 'Permission mode',
|
||||
help: 'Starting permission mode for new sessions.',
|
||||
defaultValue: 'default',
|
||||
options: [for (final p in kPermissionModes) SettingsOption(value: p.value, label: p.displayName)],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// T-171: agent roster controls (D-6 CLI/UI parity).
|
||||
// Usage: clide claude.agent.show <sessionId>
|
||||
CommandContribution(
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:clide/builtin/claude/src/stream_json_session.dart';
|
||||
import 'package:clide/kernel/kernel.dart';
|
||||
|
||||
/// Per-user defaults applied to NEW Claude sessions (T-457). Set from the
|
||||
/// Settings → Claude category; read by the pane when it spawns a session.
|
||||
/// Effort is applied at spawn (the `--effort` flag — there's no live
|
||||
/// set_effort); model and permission mode are sent as control requests right
|
||||
/// after the session starts.
|
||||
const String kDefaultModelKey = 'app.claude.defaultModel';
|
||||
const String kDefaultEffortKey = 'app.claude.defaultEffort';
|
||||
const String kDefaultPermissionModeKey = 'app.claude.defaultPermissionMode';
|
||||
|
||||
/// The default effort for new sessions, or null to let the CLI's own default
|
||||
/// stand. `'default'` is treated as "no override" too.
|
||||
String? defaultEffortFlag(SettingsStore settings) {
|
||||
final v = settings.get<String>(kDefaultEffortKey);
|
||||
if (v == null || v.isEmpty || v == 'default') return null;
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Apply the model + permission-mode defaults to a freshly-spawned [session].
|
||||
/// A null/empty/`'default'` value is a no-op — the CLI's own default stands.
|
||||
/// (Effort is handled at spawn via [defaultEffortFlag], not here.)
|
||||
void applySessionDefaults(StreamJsonSession session, SettingsStore settings) {
|
||||
final model = settings.get<String>(kDefaultModelKey);
|
||||
if (model != null && model.isNotEmpty && model != 'default') session.setModel(model);
|
||||
final perm = settings.get<String>(kDefaultPermissionModeKey);
|
||||
if (perm != null && perm.isNotEmpty && perm != 'default') session.setPermissionMode(perm);
|
||||
}
|
||||
Reference in New Issue
Block a user