From a952391b33ab210c2dd77a8c360e603b651738c5 Mon Sep 17 00:00:00 2001 From: Iouri Goussev Date: Thu, 26 Mar 2026 18:09:59 -0400 Subject: [PATCH] chore: rename preferences.md to PREFERENCES.md for consistency (#2700) (#2738) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other .gsd/ state files use uppercase naming (DECISIONS.md, REQUIREMENTS.md, PROJECT.md, etc). This renames the canonical preferences file to PREFERENCES.md while keeping a migration fallback — the loader checks PREFERENCES.md first, then falls back to lowercase preferences.md for existing installations. Closes #2700 Co-authored-by: Claude Opus 4.6 --- .plans/issue-575-dynamic-model-routing.md | 2 +- .plans/onboarding-detection-wizard.md | 4 +-- .plans/preferences-wizard-completeness.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 8 +++--- docs/configuration.md | 10 +++---- docs/parallel-orchestration.md | 2 +- docs/remote-questions.md | 4 +-- docs/token-optimization.md | 2 +- docs/working-in-teams.md | 8 +++--- mintlify-docs/guides/configuration.mdx | 6 ++--- mintlify-docs/guides/working-in-teams.mdx | 4 +-- src/remote-questions-config.ts | 2 +- .../extensions/gsd/commands-prefs-wizard.ts | 2 +- src/resources/extensions/gsd/detection.ts | 12 ++++----- .../gsd/docs/preferences-reference.md | 6 ++--- src/resources/extensions/gsd/gitignore.ts | 14 +++++----- src/resources/extensions/gsd/init-wizard.ts | 4 +-- .../extensions/gsd/preferences-models.ts | 2 +- src/resources/extensions/gsd/preferences.ts | 26 +++++++++---------- .../extensions/gsd/prompts/system.md | 2 +- src/resources/extensions/gsd/rule-registry.ts | 2 +- .../{preferences.md => PREFERENCES.md} | 0 .../gsd/tests/claude-import-tui.test.ts | 2 +- .../extensions/gsd/tests/detection.test.ts | 2 +- .../extensions/gsd/tests/doctor-git.test.ts | 8 +++--- .../gsd/tests/doctor-proactive.test.ts | 2 +- .../gsd/tests/doctor-providers.test.ts | 4 +-- .../extensions/gsd/tests/git-service.test.ts | 2 +- .../extensions/gsd/tests/init-wizard.test.ts | 2 +- .../gsd/tests/none-mode-gates.test.ts | 14 +++++----- .../extensions/gsd/tests/preferences.test.ts | 2 +- .../gsd/tests/token-cost-display.test.ts | 4 +-- .../search-the-web/native-search.ts | 2 +- .../extensions/search-the-web/provider.ts | 2 +- src/web/hooks-service.ts | 2 +- web/app/api/experimental/route.ts | 2 +- web/app/api/remote-questions/route.ts | 2 +- web/components/gsd/settings-panels.tsx | 2 +- 39 files changed, 90 insertions(+), 90 deletions(-) rename src/resources/extensions/gsd/templates/{preferences.md => PREFERENCES.md} (100%) diff --git a/.plans/issue-575-dynamic-model-routing.md b/.plans/issue-575-dynamic-model-routing.md index c68eab6bf..b32190405 100644 --- a/.plans/issue-575-dynamic-model-routing.md +++ b/.plans/issue-575-dynamic-model-routing.md @@ -11,7 +11,7 @@ Users on capped plans (e.g., Claude Pro) exhaust weekly token limits in 15-20 ho ## Current Architecture ### What Exists -- **Phase-based model config:** Users can set different models per phase via `preferences.md` (research, planning, execution, completion) +- **Phase-based model config:** Users can set different models per phase via `PREFERENCES.md` (research, planning, execution, completion) - **Fallback chains:** Each phase supports `fallbacks: [model1, model2]` for error recovery - **Pre-dispatch hooks:** `PreDispatchResult` has a `model` field but it's **never applied** in `auto.ts` — this is a ready-made extension point - **Model registry:** `ModelRegistry.getAvailable()` provides all configured models with metadata diff --git a/.plans/onboarding-detection-wizard.md b/.plans/onboarding-detection-wizard.md index 0f6d0044f..5d1e5a2e2 100644 --- a/.plans/onboarding-detection-wizard.md +++ b/.plans/onboarding-detection-wizard.md @@ -134,7 +134,7 @@ Quick filesystem scan (no heavy reads): ### Task 1.4: `isFirstEverLaunch(): boolean` -Returns `true` if `~/.gsd/` doesn't exist or has no `preferences.md`. +Returns `true` if `~/.gsd/` doesn't exist or has no `PREFERENCES.md`. --- @@ -298,7 +298,7 @@ Step 8: Advanced (collapsed by default, expandable) Step 9: Bootstrap .gsd/ structure - Creates .gsd/milestones/ - - Creates .gsd/preferences.md (from wizard answers) + - Creates .gsd/PREFERENCES.md (from wizard answers) - Creates .gitignore entries - Seeds CONTEXT.md with detected project signals - Commits "chore: init gsd" (if commit_docs enabled) diff --git a/.plans/preferences-wizard-completeness.md b/.plans/preferences-wizard-completeness.md index 5709d7f21..bb6a353d0 100644 --- a/.plans/preferences-wizard-completeness.md +++ b/.plans/preferences-wizard-completeness.md @@ -42,7 +42,7 @@ The `/gsd prefs wizard` currently only configures 6 of 18+ preference fields. Us - Added missing keys to `orderedKeys` in `serializePreferencesToFrontmatter()` ### Group 6: Update Template & Docs ✓ -- Updated `templates/preferences.md` with new fields +- Updated `templates/PREFERENCES.md` with new fields - Updated `docs/preferences-reference.md` with budget, notifications, git, hooks ### Group 7: Tests ✓ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1aa93fe5a..a0f0db894 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -53,7 +53,7 @@ git rebase origin/main GSD uses worktree-based isolation for multi-developer work. If you're contributing with GSD running, enable team mode in your project preferences: ```yaml -# .gsd/preferences.md +# .gsd/PREFERENCES.md --- version: 1 mode: team diff --git a/README.md b/README.md index d7c624552..9ef22069d 100644 --- a/README.md +++ b/README.md @@ -521,7 +521,7 @@ An auto-generated `index.html` shows all reports with progression metrics across ### Preferences -GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project). Manage with `/gsd prefs`. +GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project). Manage with `/gsd prefs`. ```yaml --- @@ -672,7 +672,7 @@ The best practice for working in teams is to ensure unique milestone names acros ### Unique Milestone Names -Create or amend your `.gsd/preferences.md` file within the repo to include `unique_milestone_ids: true` e.g. +Create or amend your `.gsd/PREFERENCES.md` file within the repo to include `unique_milestone_ids: true` e.g. ```markdown --- @@ -681,7 +681,7 @@ unique_milestone_ids: true --- ``` -With the above `.gitignore` set up, the `.gsd/preferences.md` file is checked into the repo ensuring all teammates use unique milestone names to avoid collisions. +With the above `.gitignore` set up, the `.gsd/PREFERENCES.md` file is checked into the repo ensuring all teammates use unique milestone names to avoid collisions. Milestone names will now be generated with a 6 char random string appended e.g. instead of `M001` you'll get something like `M001-ush8s3` @@ -689,7 +689,7 @@ Milestone names will now be generated with a 6 char random string appended e.g. 1. Ensure you are not in the middle of any milestones (clean state) 2. Update the `.gsd/` related entries in your `.gitignore` to follow the `Suggested .gitignore setup` section under `Working in teams` (ensure you are no longer blanket ignoring the whole `.gsd/` directory) -3. Update your `.gsd/preferences.md` file within the repo as per section `Unique Milestone Names` +3. Update your `.gsd/PREFERENCES.md` file within the repo as per section `Unique Milestone Names` 4. If you want to update all your existing milestones use this prompt in GSD: `I have turned on unique milestone ids, please update all old milestone ids to use this new format e.g. M001-abc123 where abc123 is a random 6 char lowercase alpha numeric string. Update all references in all .gsd file contents, file names and directory names. Validate your work once done to ensure referential integrity.` 5. Commit to git diff --git a/docs/configuration.md b/docs/configuration.md index 01a8f3194..d632e8315 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,14 +1,14 @@ # Configuration -GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project-local). Manage interactively with `/gsd prefs`. +GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project-local). Manage interactively with `/gsd prefs`. ## `/gsd prefs` Commands | Command | Description | |---------|-------------| | `/gsd prefs` | Open the global preferences wizard (default) | -| `/gsd prefs global` | Interactive wizard for global preferences (`~/.gsd/preferences.md`) | -| `/gsd prefs project` | Interactive wizard for project preferences (`.gsd/preferences.md`) | +| `/gsd prefs global` | Interactive wizard for global preferences (`~/.gsd/PREFERENCES.md`) | +| `/gsd prefs project` | Interactive wizard for project preferences (`.gsd/PREFERENCES.md`) | | `/gsd prefs status` | Show current preference files, merged values, and skill resolution status | | `/gsd prefs wizard` | Alias for `/gsd prefs global` | | `/gsd prefs setup` | Alias for `/gsd prefs wizard` — creates preferences file if missing | @@ -42,8 +42,8 @@ token_profile: balanced | Scope | Path | Applies to | |-------|------|-----------| -| Global | `~/.gsd/preferences.md` | All projects | -| Project | `.gsd/preferences.md` | Current project only | +| Global | `~/.gsd/PREFERENCES.md` | All projects | +| Project | `.gsd/PREFERENCES.md` | Current project only | **Merge behavior:** - **Scalar fields** (`skill_discovery`, `budget_ceiling`): project wins if defined diff --git a/docs/parallel-orchestration.md b/docs/parallel-orchestration.md index 6b611291d..40463fa95 100644 --- a/docs/parallel-orchestration.md +++ b/docs/parallel-orchestration.md @@ -126,7 +126,7 @@ File overlaps are warnings, not blockers. Both milestones work in separate workt ## Configuration -Add to `~/.gsd/preferences.md` or `.gsd/preferences.md`: +Add to `~/.gsd/PREFERENCES.md` or `.gsd/PREFERENCES.md`: ```yaml --- diff --git a/docs/remote-questions.md b/docs/remote-questions.md index 8e4ce3555..8078a9c56 100644 --- a/docs/remote-questions.md +++ b/docs/remote-questions.md @@ -16,7 +16,7 @@ The setup wizard: 3. Lists servers the bot belongs to (or lets you pick) 4. Lists text channels in the selected server 5. Sends a test message to confirm permissions -6. Saves the configuration to `~/.gsd/preferences.md` +6. Saves the configuration to `~/.gsd/PREFERENCES.md` **Bot requirements:** - A Discord bot application with a token (from [Discord Developer Portal](https://discord.com/developers/applications)) @@ -65,7 +65,7 @@ The setup wizard: ## Configuration -Remote questions are configured in `~/.gsd/preferences.md`: +Remote questions are configured in `~/.gsd/PREFERENCES.md`: ```yaml remote_questions: diff --git a/docs/token-optimization.md b/docs/token-optimization.md index a622869d1..5c5ea3466 100644 --- a/docs/token-optimization.md +++ b/docs/token-optimization.md @@ -257,7 +257,7 @@ models: ## How the Pieces Fit Together ``` -preferences.md +PREFERENCES.md └─ token_profile: balanced ├─ resolveProfileDefaults() → model defaults + phase skip defaults ├─ resolveInlineLevel() → standard diff --git a/docs/working-in-teams.md b/docs/working-in-teams.md index 71956d5ff..fd5476813 100644 --- a/docs/working-in-teams.md +++ b/docs/working-in-teams.md @@ -9,7 +9,7 @@ GSD supports multi-user workflows where several developers work on the same repo The simplest way to configure GSD for team use is to set `mode: team` in your project preferences. This enables unique milestone IDs, push branches, and pre-merge checks in one setting: ```yaml -# .gsd/preferences.md (project-level, committed to git) +# .gsd/PREFERENCES.md (project-level, committed to git) --- version: 1 mode: team @@ -38,7 +38,7 @@ Share planning artifacts (milestones, roadmaps, decisions) while keeping runtime ``` **What gets shared** (committed to git): -- `.gsd/preferences.md` — project preferences +- `.gsd/PREFERENCES.md` — project preferences - `.gsd/PROJECT.md` — living project description - `.gsd/REQUIREMENTS.md` — requirement contract - `.gsd/DECISIONS.md` — architectural decisions @@ -50,7 +50,7 @@ Share planning artifacts (milestones, roadmaps, decisions) while keeping runtime ### 3. Commit the Preferences ```bash -git add .gsd/preferences.md +git add .gsd/PREFERENCES.md git commit -m "chore: enable GSD team workflow" ``` @@ -71,7 +71,7 @@ If you have an existing project with `.gsd/` blanket-ignored: 1. Ensure no milestones are in progress (clean state) 2. Update `.gitignore` to use the selective pattern above -3. Add `unique_milestone_ids: true` to `.gsd/preferences.md` +3. Add `unique_milestone_ids: true` to `.gsd/PREFERENCES.md` 4. Optionally rename existing milestones to use unique IDs: ``` I have turned on unique milestone ids, please update all old milestone diff --git a/mintlify-docs/guides/configuration.mdx b/mintlify-docs/guides/configuration.mdx index cd74a40a0..4961d66b9 100644 --- a/mintlify-docs/guides/configuration.mdx +++ b/mintlify-docs/guides/configuration.mdx @@ -3,7 +3,7 @@ title: "Configuration" description: "Preferences, model selection, MCP servers, hooks, and all settings." --- -GSD preferences live in `~/.gsd/preferences.md` (global) or `.gsd/preferences.md` (project-local). Manage interactively with `/gsd prefs`. +GSD preferences live in `~/.gsd/PREFERENCES.md` (global) or `.gsd/PREFERENCES.md` (project-local). Manage interactively with `/gsd prefs`. ## Preferences commands @@ -40,8 +40,8 @@ token_profile: balanced | Scope | Path | Applies to | |-------|------|-----------| -| Global | `~/.gsd/preferences.md` | All projects | -| Project | `.gsd/preferences.md` | Current project only | +| Global | `~/.gsd/PREFERENCES.md` | All projects | +| Project | `.gsd/PREFERENCES.md` | Current project only | **Merge behavior:** - **Scalar fields** — project wins if defined diff --git a/mintlify-docs/guides/working-in-teams.mdx b/mintlify-docs/guides/working-in-teams.mdx index 17f6f0c1d..72baa19e2 100644 --- a/mintlify-docs/guides/working-in-teams.mdx +++ b/mintlify-docs/guides/working-in-teams.mdx @@ -10,7 +10,7 @@ GSD supports multi-user workflows where several developers work on the same repo ### 1. Set team mode ```yaml -# .gsd/preferences.md (project-level, committed to git) +# .gsd/PREFERENCES.md (project-level, committed to git) --- version: 1 mode: team @@ -43,7 +43,7 @@ Share planning artifacts while keeping runtime files local: ### 3. Commit ```bash -git add .gsd/preferences.md +git add .gsd/PREFERENCES.md git commit -m "chore: enable GSD team workflow" ``` diff --git a/src/remote-questions-config.ts b/src/remote-questions-config.ts index e7f0d8cae..7a66543a4 100644 --- a/src/remote-questions-config.ts +++ b/src/remote-questions-config.ts @@ -16,7 +16,7 @@ import { appRoot } from "./app-paths.js"; // boundary — this file is compiled by tsc, but preferences.ts is loaded // via jiti at runtime. Importing it as .js fails because no .js exists // in dist/. See #592, #1110. -const GLOBAL_PREFERENCES_PATH = join(appRoot, "preferences.md"); +const GLOBAL_PREFERENCES_PATH = join(appRoot, "PREFERENCES.md"); export function saveRemoteQuestionsConfig(channel: "slack" | "discord" | "telegram", channelId: string): void { const prefsPath = GLOBAL_PREFERENCES_PATH; diff --git a/src/resources/extensions/gsd/commands-prefs-wizard.ts b/src/resources/extensions/gsd/commands-prefs-wizard.ts index 46e4b0a37..f006cca61 100644 --- a/src/resources/extensions/gsd/commands-prefs-wizard.ts +++ b/src/resources/extensions/gsd/commands-prefs-wizard.ts @@ -771,7 +771,7 @@ export async function ensurePreferencesFile( scope: "global" | "project", ): Promise { if (!existsSync(path)) { - const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "preferences.md")); + const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "PREFERENCES.md")); if (!template) { ctx.ui.notify("Could not load GSD preferences template.", "error"); return; diff --git a/src/resources/extensions/gsd/detection.ts b/src/resources/extensions/gsd/detection.ts index 7507d427d..0bf69ddc9 100644 --- a/src/resources/extensions/gsd/detection.ts +++ b/src/resources/extensions/gsd/detection.ts @@ -359,8 +359,8 @@ function detectV2Gsd(basePath: string): V2Detection | null { if (!existsSync(gsdPath)) return null; const hasPreferences = - existsSync(join(gsdPath, "preferences.md")) || - existsSync(join(gsdPath, "PREFERENCES.md")); + existsSync(join(gsdPath, "PREFERENCES.md")) || + existsSync(join(gsdPath, "preferences.md")); const hasContext = existsSync(join(gsdPath, "CONTEXT.md")); @@ -714,8 +714,8 @@ function detectVerificationCommands( */ export function hasGlobalSetup(): boolean { return ( - existsSync(join(gsdHome, "preferences.md")) || - existsSync(join(gsdHome, "PREFERENCES.md")) + existsSync(join(gsdHome, "PREFERENCES.md")) || + existsSync(join(gsdHome, "preferences.md")) ); } @@ -728,8 +728,8 @@ export function isFirstEverLaunch(): boolean { // If we have preferences, not first launch if ( - existsSync(join(gsdHome, "preferences.md")) || - existsSync(join(gsdHome, "PREFERENCES.md")) + existsSync(join(gsdHome, "PREFERENCES.md")) || + existsSync(join(gsdHome, "preferences.md")) ) { return false; } diff --git a/src/resources/extensions/gsd/docs/preferences-reference.md b/src/resources/extensions/gsd/docs/preferences-reference.md index 5afeff2bd..27a994a37 100644 --- a/src/resources/extensions/gsd/docs/preferences-reference.md +++ b/src/resources/extensions/gsd/docs/preferences-reference.md @@ -1,6 +1,6 @@ # GSD Preferences Reference -Full documentation for `~/.gsd/preferences.md` (global) and `.gsd/preferences.md` (project). +Full documentation for `~/.gsd/PREFERENCES.md` (global) and `.gsd/PREFERENCES.md` (project). --- @@ -51,8 +51,8 @@ skill_rules: [] Preferences are loaded from two locations and merged: -1. **Global:** `~/.gsd/preferences.md` — applies to all projects -2. **Project:** `.gsd/preferences.md` — applies to the current project only +1. **Global:** `~/.gsd/PREFERENCES.md` — applies to all projects +2. **Project:** `.gsd/PREFERENCES.md` — applies to the current project only **Merge behavior** (see `mergePreferences()` in `preferences.ts`): diff --git a/src/resources/extensions/gsd/gitignore.ts b/src/resources/extensions/gsd/gitignore.ts index 71cf7c2ab..da4b2ee91 100644 --- a/src/resources/extensions/gsd/gitignore.ts +++ b/src/resources/extensions/gsd/gitignore.ts @@ -1,8 +1,8 @@ /** - * GSD bootstrappers for .gitignore and preferences.md + * GSD bootstrappers for .gitignore and PREFERENCES.md * * Ensures baseline .gitignore exists with universally-correct patterns. - * Creates an empty preferences.md template if it doesn't exist. + * Creates an empty PREFERENCES.md template if it doesn't exist. * Both idempotent — non-destructive if already present. */ @@ -216,16 +216,16 @@ export function untrackRuntimeFiles(basePath: string): void { } /** - * Ensure basePath/.gsd/preferences.md exists as an empty template. + * Ensure basePath/.gsd/PREFERENCES.md exists as an empty template. * Creates the file with frontmatter only if it doesn't exist. * Returns true if created, false if already exists. * - * Checks both lowercase (canonical) and uppercase (legacy) to avoid - * creating a duplicate when an uppercase file already exists. + * Checks both uppercase (canonical) and lowercase (legacy) to avoid + * creating a duplicate when a lowercase file already exists. */ export function ensurePreferences(basePath: string): boolean { - const preferencesPath = join(gsdRoot(basePath), "preferences.md"); - const legacyPath = join(gsdRoot(basePath), "PREFERENCES.md"); + const preferencesPath = join(gsdRoot(basePath), "PREFERENCES.md"); + const legacyPath = join(gsdRoot(basePath), "preferences.md"); if (existsSync(preferencesPath) || existsSync(legacyPath)) { return false; diff --git a/src/resources/extensions/gsd/init-wizard.ts b/src/resources/extensions/gsd/init-wizard.ts index de634ce99..f1a077dd8 100644 --- a/src/resources/extensions/gsd/init-wizard.ts +++ b/src/resources/extensions/gsd/init-wizard.ts @@ -422,9 +422,9 @@ function bootstrapGsdDirectory( const gsd = gsdRoot(basePath); mkdirSync(join(gsd, "milestones"), { recursive: true }); - // Write preferences.md from wizard answers + // Write PREFERENCES.md from wizard answers const preferencesContent = buildPreferencesFile(prefs); - writeFileSync(join(gsd, "preferences.md"), preferencesContent, "utf-8"); + writeFileSync(join(gsd, "PREFERENCES.md"), preferencesContent, "utf-8"); // Seed CONTEXT.md with detected project signals const contextContent = buildContextSeed(signals); diff --git a/src/resources/extensions/gsd/preferences-models.ts b/src/resources/extensions/gsd/preferences-models.ts index 303c43470..2100b16e4 100644 --- a/src/resources/extensions/gsd/preferences-models.ts +++ b/src/resources/extensions/gsd/preferences-models.ts @@ -308,7 +308,7 @@ export function resolveContextSelection(): import("./types.js").ContextSelection } /** - * Resolve the search provider preference from preferences.md. + * Resolve the search provider preference from PREFERENCES.md. * Returns undefined if not configured (caller falls back to existing behavior). */ export function resolveSearchProviderFromPreferences(): GSDPreferences["search_provider"] | undefined { diff --git a/src/resources/extensions/gsd/preferences.ts b/src/resources/extensions/gsd/preferences.ts index 0b0b82927..7e25ede0a 100644 --- a/src/resources/extensions/gsd/preferences.ts +++ b/src/resources/extensions/gsd/preferences.ts @@ -87,7 +87,7 @@ function gsdHome(): string { } function globalPreferencesPath(): string { - return join(gsdHome(), "preferences.md"); + return join(gsdHome(), "PREFERENCES.md"); } function legacyGlobalPreferencesPath(): string { @@ -95,16 +95,16 @@ function legacyGlobalPreferencesPath(): string { } function projectPreferencesPath(): string { - return join(gsdRoot(process.cwd()), "preferences.md"); -} -// Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake. -// Check uppercase as a fallback so those files aren't silently ignored. -function globalPreferencesPathUppercase(): string { - return join(gsdHome(), "PREFERENCES.md"); -} -function projectPreferencesPathUppercase(): string { return join(gsdRoot(process.cwd()), "PREFERENCES.md"); } +// Legacy: older versions used lowercase preferences.md. +// Check lowercase as a fallback so those files aren't silently ignored. +function globalPreferencesPathLegacy(): string { + return join(gsdHome(), "preferences.md"); +} +function projectPreferencesPathLegacy(): string { + return join(gsdRoot(process.cwd()), "preferences.md"); +} export function getGlobalGSDPreferencesPath(): string { return globalPreferencesPath(); @@ -122,13 +122,13 @@ export function getProjectGSDPreferencesPath(): string { export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null { return loadPreferencesFile(globalPreferencesPath(), "global") - ?? loadPreferencesFile(globalPreferencesPathUppercase(), "global") + ?? loadPreferencesFile(globalPreferencesPathLegacy(), "global") ?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global"); } export function loadProjectGSDPreferences(): LoadedGSDPreferences | null { return loadPreferencesFile(projectPreferencesPath(), "project") - ?? loadPreferencesFile(projectPreferencesPathUppercase(), "project"); + ?? loadPreferencesFile(projectPreferencesPathLegacy(), "project"); } export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null { @@ -223,7 +223,7 @@ export function parsePreferencesMarkdown(content: string): GSDPreferences | null if (!_warnedUnrecognizedFormat) { _warnedUnrecognizedFormat = true; - console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping."); + console.warn("[parsePreferencesMarkdown] PREFERENCES.md exists but uses an unrecognized format — skipping."); } return null; } @@ -502,7 +502,7 @@ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] { * Resolve the effective git isolation mode from preferences. * Returns "none" (default), "worktree", or "branch". * - * Default is "none" so GSD works out of the box without preferences.md. + * Default is "none" so GSD works out of the box without PREFERENCES.md. * Worktree isolation requires explicit opt-in because it depends on git * branch infrastructure that must be set up before use. */ diff --git a/src/resources/extensions/gsd/prompts/system.md b/src/resources/extensions/gsd/prompts/system.md index 44671a14f..0d1eb0ada 100644 --- a/src/resources/extensions/gsd/prompts/system.md +++ b/src/resources/extensions/gsd/prompts/system.md @@ -92,7 +92,7 @@ Titles live inside file content (headings, frontmatter), not in file or director ### Isolation Model -Auto-mode supports three isolation modes (configured in `.gsd/preferences.md` under `taskIsolation.mode`): +Auto-mode supports three isolation modes (configured in `.gsd/PREFERENCES.md` under `taskIsolation.mode`): - **worktree** (default): Work happens in `.gsd/worktrees//`, a full git worktree on the `milestone/` branch. Each worktree has its own working copy and `.gsd/` directory. Squash-merged back to the integration branch on milestone completion. - **branch**: Work happens in the project root on a `milestone/` branch. No worktree directory — files are checked out in-place. diff --git a/src/resources/extensions/gsd/rule-registry.ts b/src/resources/extensions/gsd/rule-registry.ts index e8ac7c13e..e61893606 100644 --- a/src/resources/extensions/gsd/rule-registry.ts +++ b/src/resources/extensions/gsd/rule-registry.ts @@ -524,7 +524,7 @@ export class RuleRegistry { formatHookStatus(): string { const entries = this.getHookStatus(); if (entries.length === 0) { - return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md"; + return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/PREFERENCES.md"; } const lines: string[] = ["Configured Hooks:", ""]; diff --git a/src/resources/extensions/gsd/templates/preferences.md b/src/resources/extensions/gsd/templates/PREFERENCES.md similarity index 100% rename from src/resources/extensions/gsd/templates/preferences.md rename to src/resources/extensions/gsd/templates/PREFERENCES.md diff --git a/src/resources/extensions/gsd/tests/claude-import-tui.test.ts b/src/resources/extensions/gsd/tests/claude-import-tui.test.ts index c3728cbce..53a4284fa 100644 --- a/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +++ b/src/resources/extensions/gsd/tests/claude-import-tui.test.ts @@ -126,7 +126,7 @@ describe( before(() => { tempDir = mkdtempSync(join(tmpdir(), 'gsd-tui-test-')); - prefsPath = join(tempDir, 'preferences.md'); + prefsPath = join(tempDir, 'PREFERENCES.md'); prefs = { version: 1 }; }); diff --git a/src/resources/extensions/gsd/tests/detection.test.ts b/src/resources/extensions/gsd/tests/detection.test.ts index b1a1647dc..c1efd9d0f 100644 --- a/src/resources/extensions/gsd/tests/detection.test.ts +++ b/src/resources/extensions/gsd/tests/detection.test.ts @@ -99,7 +99,7 @@ test("detectProjectState: detects preferences in .gsd/", (t) => { t.after(() => cleanup(dir)); mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true }); - writeFileSync(join(dir, ".gsd", "preferences.md"), "---\nversion: 1\n---\n", "utf-8"); + writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---\n", "utf-8"); const result = detectProjectState(dir); assert.ok(result.v2); assert.equal(result.v2!.hasPreferences, true); diff --git a/src/resources/extensions/gsd/tests/doctor-git.test.ts b/src/resources/extensions/gsd/tests/doctor-git.test.ts index cdffe17ae..9b87d2714 100644 --- a/src/resources/extensions/gsd/tests/doctor-git.test.ts +++ b/src/resources/extensions/gsd/tests/doctor-git.test.ts @@ -64,11 +64,11 @@ _None_ return dir; } -/** Write a .gsd/preferences.md with the given git isolation mode. */ +/** Write a .gsd/PREFERENCES.md with the given git isolation mode. */ function writePreferencesFile(dir: string, isolation: "none" | "worktree" | "branch"): void { const gsdDir = join(dir, ".gsd"); mkdirSync(gsdDir, { recursive: true }); - writeFileSync(join(gsdDir, "preferences.md"), `---\ngit:\n isolation: "${isolation}"\n---\n`); + writeFileSync(join(gsdDir, "PREFERENCES.md"), `---\ngit:\n isolation: "${isolation}"\n---\n`); } /** Create a repo with an in-progress milestone. */ @@ -302,7 +302,7 @@ describe('doctor-git', async () => { // ─── Test 7: none-mode skips orphaned worktree check ─────────────── // NOTE: loadEffectiveGSDPreferences() resolves PROJECT_PREFERENCES_PATH // at module load time from process.cwd(). We write the prefs file to - // the test runner's cwd .gsd/preferences.md and clean up afterwards. + // the test runner's cwd .gsd/PREFERENCES.md and clean up afterwards. if (process.platform !== "win32") { test('none-mode skips orphaned worktree', async () => { const dir = createRepoWithCompletedMilestone(); @@ -409,7 +409,7 @@ describe('doctor-git', async () => { cleanups.push(dir); run("git branch trunk", dir); - writeFileSync(join(dir, ".gsd", "preferences.md"), `---\ngit:\n isolation: "worktree"\n main_branch: "trunk"\n---\n`); + writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n isolation: "worktree"\n main_branch: "trunk"\n---\n`); const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json"); writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feat/does-not-exist" }, null, 2)); diff --git a/src/resources/extensions/gsd/tests/doctor-proactive.test.ts b/src/resources/extensions/gsd/tests/doctor-proactive.test.ts index 217769f68..29be69b33 100644 --- a/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +++ b/src/resources/extensions/gsd/tests/doctor-proactive.test.ts @@ -297,7 +297,7 @@ describe('doctor-proactive', async () => { cleanups.push(dir); run("git branch trunk", dir); - writeFileSync(join(dir, ".gsd", "preferences.md"), `---\ngit:\n main_branch: "trunk"\n---\n`); + writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), `---\ngit:\n main_branch: "trunk"\n---\n`); const metaPath = join(dir, ".gsd", "milestones", "M001", "M001-META.json"); writeFileSync(metaPath, JSON.stringify({ integrationBranch: "feature/missing" }, null, 2)); diff --git a/src/resources/extensions/gsd/tests/doctor-providers.test.ts b/src/resources/extensions/gsd/tests/doctor-providers.test.ts index c27d92e17..96f6abd3e 100644 --- a/src/resources/extensions/gsd/tests/doctor-providers.test.ts +++ b/src/resources/extensions/gsd/tests/doctor-providers.test.ts @@ -419,7 +419,7 @@ test("runProviderChecks uses provider-qualified anthropic-vertex model IDs", () const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-prefix-repo-"))); mkdirSync(join(repo, ".gsd"), { recursive: true }); writeFileSync( - join(repo, ".gsd", "preferences.md"), + join(repo, ".gsd", "PREFERENCES.md"), [ "---", "models:", @@ -454,7 +454,7 @@ test("runProviderChecks uses object provider field for anthropic-vertex models", const repo = realpathSync(mkdtempSync(join(tmpdir(), "gsd-providers-vertex-provider-repo-"))); mkdirSync(join(repo, ".gsd"), { recursive: true }); writeFileSync( - join(repo, ".gsd", "preferences.md"), + join(repo, ".gsd", "PREFERENCES.md"), [ "---", "models:", diff --git a/src/resources/extensions/gsd/tests/git-service.test.ts b/src/resources/extensions/gsd/tests/git-service.test.ts index 3e4b3ffda..d6c0f3b8f 100644 --- a/src/resources/extensions/gsd/tests/git-service.test.ts +++ b/src/resources/extensions/gsd/tests/git-service.test.ts @@ -1142,7 +1142,7 @@ describe('git-service', async () => { mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true }); mkdirSync(join(repo, ".gsd", "activity"), { recursive: true }); writeFileSync(join(repo, ".gsd", "milestones", "M001", "ROADMAP.md"), "# Roadmap"); - writeFileSync(join(repo, ".gsd", "preferences.md"), "---\nversion: 1\n---"); + writeFileSync(join(repo, ".gsd", "PREFERENCES.md"), "---\nversion: 1\n---"); writeFileSync(join(repo, ".gsd", "STATE.md"), "# State"); writeFileSync(join(repo, ".gsd", "runtime", "units.json"), "{}"); writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}"); diff --git a/src/resources/extensions/gsd/tests/init-wizard.test.ts b/src/resources/extensions/gsd/tests/init-wizard.test.ts index c3350a5a4..c17300682 100644 --- a/src/resources/extensions/gsd/tests/init-wizard.test.ts +++ b/src/resources/extensions/gsd/tests/init-wizard.test.ts @@ -123,7 +123,7 @@ test("init-wizard: v2 .gsd/ preferences detected", (t) => { const dir = makeTempDir("prefs-detect"); try { mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true }); - writeFileSync(join(dir, ".gsd", "preferences.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8"); + writeFileSync(join(dir, ".gsd", "PREFERENCES.md"), "---\nversion: 1\nmode: solo\n---\n", "utf-8"); const detection = detectProjectState(dir); assert.ok(detection.v2); diff --git a/src/resources/extensions/gsd/tests/none-mode-gates.test.ts b/src/resources/extensions/gsd/tests/none-mode-gates.test.ts index bdadcfc1d..0a002556d 100644 --- a/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +++ b/src/resources/extensions/gsd/tests/none-mode-gates.test.ts @@ -8,7 +8,7 @@ * Uses the writeRunnerPreferences pattern from doctor-git.test.ts: * PROJECT_PREFERENCES_PATH is a module-level constant frozen at import * time, so process.chdir() won't redirect preference loading. We write - * prefs to the runner's cwd .gsd/preferences.md and clean up in finally. + * prefs to the runner's cwd .gsd/PREFERENCES.md and clean up in finally. */ import { mkdirSync, writeFileSync, rmSync, existsSync } from "node:fs"; @@ -24,7 +24,7 @@ import assert from 'node:assert/strict'; // --- Preferences helpers (same pattern as doctor-git.test.ts K001) --- -const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "preferences.md"); +const RUNNER_PREFS_PATH = join(process.cwd(), ".gsd", "PREFERENCES.md"); function writeRunnerPreferences(isolation: "none" | "worktree" | "branch"): void { mkdirSync(join(process.cwd(), ".gsd"), { recursive: true }); @@ -72,12 +72,12 @@ try { // Test 4: shouldUseWorktreeIsolation returns false for no prefs (default: none) // Worktree isolation requires explicit opt-in — default is "none" so GSD -// works out of the box without preferences.md (#2480). +// works out of the box without PREFERENCES.md (#2480). // Skip if global prefs exist — they override the default and this test -// cannot control ~/.gsd/preferences.md. +// cannot control ~/.gsd/PREFERENCES.md. test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () => { - const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md")) + const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md")) || existsSync(join(homedir(), ".gsd", "PREFERENCES.md")); if (!globalPrefsExist) { try { @@ -91,9 +91,9 @@ test('shouldUseWorktreeIsolation returns false for no prefs (default: none)', () } }); -// Test 5: getIsolationMode returns "none" when no preferences.md exists (#2480) +// Test 5: getIsolationMode returns "none" when no PREFERENCES.md exists (#2480) test('getIsolationMode returns "none" with no prefs (default)', () => { - const globalPrefsExist = existsSync(join(homedir(), ".gsd", "preferences.md")) + const globalPrefsExist = existsSync(join(homedir(), ".gsd", "PREFERENCES.md")) || existsSync(join(homedir(), ".gsd", "PREFERENCES.md")); if (!globalPrefsExist) { try { diff --git a/src/resources/extensions/gsd/tests/preferences.test.ts b/src/resources/extensions/gsd/tests/preferences.test.ts index f2c033784..1b337a9d3 100644 --- a/src/resources/extensions/gsd/tests/preferences.test.ts +++ b/src/resources/extensions/gsd/tests/preferences.test.ts @@ -45,7 +45,7 @@ test("getIsolationMode defaults to none when preferences have no isolation setti // Validate the default via validatePreferences: when no isolation is set, // preferences.git.isolation is undefined, and getIsolationMode returns "none". // Default changed from "worktree" to "none" so GSD works out of the box - // without preferences.md (#2480). + // without PREFERENCES.md (#2480). const { preferences } = validatePreferences({}); assert.equal(preferences.git?.isolation, undefined, "no isolation in empty prefs"); const isolation = preferences.git?.isolation; diff --git a/src/resources/extensions/gsd/tests/token-cost-display.test.ts b/src/resources/extensions/gsd/tests/token-cost-display.test.ts index e12d9e4db..bbd7afc50 100644 --- a/src/resources/extensions/gsd/tests/token-cost-display.test.ts +++ b/src/resources/extensions/gsd/tests/token-cost-display.test.ts @@ -63,13 +63,13 @@ test("show_token_cost defaults to undefined (disabled) when not set", () => { assert.equal(preferences.show_token_cost, undefined); }); -test("empty preferences.md does not enable show_token_cost", () => { +test("empty PREFERENCES.md does not enable show_token_cost", () => { const prefs = parsePreferencesMarkdown("---\nversion: 1\n---\n"); assert.ok(prefs); assert.equal(prefs.show_token_cost, undefined); }); -test("preferences.md with show_token_cost: true enables the preference", () => { +test("PREFERENCES.md with show_token_cost: true enables the preference", () => { const prefs = parsePreferencesMarkdown("---\nshow_token_cost: true\n---\n"); assert.ok(prefs); assert.equal(prefs.show_token_cost, true); diff --git a/src/resources/extensions/search-the-web/native-search.ts b/src/resources/extensions/search-the-web/native-search.ts index 0f7805528..5debc2b1b 100644 --- a/src/resources/extensions/search-the-web/native-search.ts +++ b/src/resources/extensions/search-the-web/native-search.ts @@ -28,7 +28,7 @@ export const MAX_NATIVE_SEARCHES_PER_SESSION = 15; /** When true, skip native web search injection and keep Brave/custom tools active on Anthropic. */ export function preferBraveSearch(): boolean { - // preferences.md takes priority over env var + // PREFERENCES.md takes priority over env var const prefsPref = resolveSearchProviderFromPreferences(); if (prefsPref === "brave" || prefsPref === "tavily" || prefsPref === "ollama") return true; if (prefsPref === "native") return false; diff --git a/src/resources/extensions/search-the-web/provider.ts b/src/resources/extensions/search-the-web/provider.ts index e1f8b2312..cf7ae5b98 100644 --- a/src/resources/extensions/search-the-web/provider.ts +++ b/src/resources/extensions/search-the-web/provider.ts @@ -105,7 +105,7 @@ export function resolveSearchProvider(overridePreference?: string): SearchProvid if (overridePreference && VALID_PREFERENCES.has(overridePreference)) { pref = overridePreference as SearchProviderPreference } else { - // preferences.md takes priority over auth.json + // PREFERENCES.md takes priority over auth.json const mdPref = resolveSearchProviderFromPreferences() if (mdPref && mdPref !== 'auto' && mdPref !== 'native') { pref = mdPref as SearchProviderPreference diff --git a/src/web/hooks-service.ts b/src/web/hooks-service.ts index b8142dda4..9eeac1276 100644 --- a/src/web/hooks-service.ts +++ b/src/web/hooks-service.ts @@ -38,7 +38,7 @@ export async function collectHooksData(projectCwdOverride?: string): Promise; body: string } { diff --git a/web/app/api/remote-questions/route.ts b/web/app/api/remote-questions/route.ts index ae6e1cf4e..0215e08b3 100644 --- a/web/app/api/remote-questions/route.ts +++ b/web/app/api/remote-questions/route.ts @@ -84,7 +84,7 @@ function maskToken(token: string): string { // ─── Helpers ────────────────────────────────────────────────────────────────── function getPreferencesPath(): string { - return join(homedir(), ".gsd", "preferences.md") + return join(homedir(), ".gsd", "PREFERENCES.md") } function clamp(value: number | undefined, defaultVal: number, min: number, max: number): number { diff --git a/web/components/gsd/settings-panels.tsx b/web/components/gsd/settings-panels.tsx index c80bf7d8a..ea64dda5f 100644 --- a/web/components/gsd/settings-panels.tsx +++ b/web/components/gsd/settings-panels.tsx @@ -1200,7 +1200,7 @@ export function ExperimentalPanel() { {data && (

Changes are written to{" "} - {prefs?.path ?? "~/.gsd/preferences.md"} + {prefs?.path ?? "~/.gsd/PREFERENCES.md"} {" "}and take effect on the next session.

)}