From b9c46d362b3de5d7dd3f151bd235db08bad66f80 Mon Sep 17 00:00:00 2001 From: Jerop Kipruto Date: Tue, 17 Mar 2026 14:14:35 -0400 Subject: [PATCH] feat(sdd): guard built-in SDD with experimental.sdd flag --- docs/cli/spec-driven-development.md | 21 ++++++++++++ docs/sidebar.json | 5 +++ .../cli/src/config/extension-manager.test.ts | 32 +++++++++++++++++-- packages/cli/src/config/extension-manager.ts | 7 +++- packages/cli/src/config/settingsSchema.ts | 9 ++++++ 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/docs/cli/spec-driven-development.md b/docs/cli/spec-driven-development.md index 1e8438c3a9..011458d12e 100644 --- a/docs/cli/spec-driven-development.md +++ b/docs/cli/spec-driven-development.md @@ -36,6 +36,27 @@ SDD is designed to manage the entire lifecycle of your development tasks. It is one of the [planning workflows](./plan-mode.md#planning-workflows) supported by Gemini CLI. +### Enabling SDD + +SDD is currently an experimental feature. To use it, you must enable the +`experimental.sdd` flag in your settings: + +1. Open the settings dialog by running `/settings`. +2. Press `/` to enter search mode and type **SDD**. +3. Enable **Spec-Driven Development (SDD)**. +4. Restart the CLI for the changes to take effect. + +Alternatively, you can navigate to the **Experimental** category to find the +setting, or enable it manually in your `~/.gemini/settings.json` file: + +```json +{ + "experimental": { + "sdd": true + } +} +``` + **Note on Token Consumption:** SDD's context-driven approach involves reading and analyzing your project's context, specifications, and plans. This can lead to increased token consumption, especially in larger projects or during diff --git a/docs/sidebar.json b/docs/sidebar.json index 6cac5ec9fd..f1ce553d97 100644 --- a/docs/sidebar.json +++ b/docs/sidebar.json @@ -122,6 +122,11 @@ "slug": "docs/cli/notifications" }, { "label": "Plan mode", "slug": "docs/cli/plan-mode" }, + { + "label": "Spec-driven development (SDD)", + "badge": "🔬", + "slug": "docs/cli/spec-driven-development" + }, { "label": "Subagents", "badge": "🔬", diff --git a/packages/cli/src/config/extension-manager.test.ts b/packages/cli/src/config/extension-manager.test.ts index 7fdac7c408..b3351832df 100644 --- a/packages/cli/src/config/extension-manager.test.ts +++ b/packages/cli/src/config/extension-manager.test.ts @@ -726,8 +726,10 @@ describe('ExtensionManager', () => { const emitSpy = vi.spyOn(coreEvents, 'emitFeedback'); // Create a FRESH manager to ensure loadExtensions actually runs + const settings = createTestMergedSettings(); + settings.experimental.sdd = true; const manager = new ExtensionManager({ - settings: createTestMergedSettings(), + settings, workspaceDir: tempWorkspaceDir, requestConsent: vi.fn().mockResolvedValue(true), requestSetting: null, @@ -765,8 +767,10 @@ describe('ExtensionManager', () => { const emitSpy = vi.spyOn(coreEvents, 'emitFeedback'); // Create a FRESH manager + const settings = createTestMergedSettings(); + settings.experimental.sdd = true; const manager = new ExtensionManager({ - settings: createTestMergedSettings(), + settings, workspaceDir: tempWorkspaceDir, requestConsent: vi.fn().mockResolvedValue(true), requestSetting: null, @@ -790,5 +794,29 @@ describe('ExtensionManager', () => { expect.stringContaining('/spec setup'), ); }); + + it('should NOT load sdd builtin when experimental.sdd is false', async () => { + // Create a builtin extension named 'sdd' + createExtension({ + extensionsDir: builtinExtensionsDir, + name: 'sdd', + version: '1.0.0', + }); + + // Create a manager with default settings (experimental.sdd = false) + const settings = createTestMergedSettings(); + settings.experimental.sdd = false; + const manager = new ExtensionManager({ + settings, + workspaceDir: tempWorkspaceDir, + requestConsent: vi.fn().mockResolvedValue(true), + requestSetting: null, + }); + + const extensions = await manager.loadExtensions(builtinExtensionsDir); + + // Verify builtin was NOT loaded + expect(extensions.find((e) => e.name === 'sdd')).toBeUndefined(); + }); }); }); diff --git a/packages/cli/src/config/extension-manager.ts b/packages/cli/src/config/extension-manager.ts index f62aac6a06..a5da221df0 100644 --- a/packages/cli/src/config/extension-manager.ts +++ b/packages/cli/src/config/extension-manager.ts @@ -671,6 +671,11 @@ Would you like to attempt to install via "git clone" instead?`, for (const builtinExt of builtinResolved) { if (builtinExt) { + const isSdd = builtinExt.name === 'sdd'; + if (isSdd && !this.settings.experimental.sdd) { + continue; + } + const existingIdx = builtExtensions.findIndex( (e) => e.name === builtinExt.name, ); @@ -682,7 +687,7 @@ Would you like to attempt to install via "git clone" instead?`, builtExtensions[existingIdx] = builtinExt; } else { // Check if this is the new 'sdd' extension and if 'conductor' is installed. - if (builtinExt.name === 'sdd') { + if (isSdd) { const conductorIdx = builtExtensions.findIndex( (e) => e.name === 'conductor', ); diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 8a107c4d47..a6dd6aa781 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -1955,6 +1955,15 @@ const SETTINGS_SCHEMA = { description: 'Enable Plan Mode.', showInDialog: true, }, + sdd: { + type: 'boolean', + label: 'Spec-Driven Development (SDD)', + category: 'Experimental', + requiresRestart: true, + default: false, + description: 'Enable built-in Spec-Driven Development (SDD) workflow.', + showInDialog: true, + }, taskTracker: { type: 'boolean', label: 'Task Tracker',