From 771259597b358dd9df386e95ce81bf9aef3d50b8 Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Sat, 4 Apr 2026 15:17:09 -0500 Subject: [PATCH] refactor: deduplicate config defaults into CONFIG_DEFAULTS constant (#1708) Fixes #1707 Extracts config defaults from loadConfig() into an exported CONFIG_DEFAULTS constant in core.cjs. config.cjs and verify.cjs now reference CONFIG_DEFAULTS instead of duplicating values, preventing future divergence. --- get-shit-done/bin/lib/config.cjs | 18 +++++------ get-shit-done/bin/lib/core.cjs | 54 ++++++++++++++++++-------------- get-shit-done/bin/lib/verify.cjs | 28 ++++++++--------- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/get-shit-done/bin/lib/config.cjs b/get-shit-done/bin/lib/config.cjs index db3e4e80d..38d245fd5 100644 --- a/get-shit-done/bin/lib/config.cjs +++ b/get-shit-done/bin/lib/config.cjs @@ -4,7 +4,7 @@ const fs = require('fs'); const path = require('path'); -const { output, error, planningRoot } = require('./core.cjs'); +const { output, error, planningRoot, CONFIG_DEFAULTS } = require('./core.cjs'); const { VALID_PROFILES, getAgentToModelMapForProfile, @@ -106,18 +106,18 @@ function buildNewProjectConfig(userChoices) { } const hardcoded = { - model_profile: 'balanced', - commit_docs: true, - parallelization: true, - search_gitignored: false, + model_profile: CONFIG_DEFAULTS.model_profile, + commit_docs: CONFIG_DEFAULTS.commit_docs, + parallelization: CONFIG_DEFAULTS.parallelization, + search_gitignored: CONFIG_DEFAULTS.search_gitignored, brave_search: hasBraveSearch, firecrawl: hasFirecrawl, exa_search: hasExaSearch, git: { - branching_strategy: 'none', - phase_branch_template: 'gsd/phase-{phase}-{slug}', - milestone_branch_template: 'gsd/{milestone}-{slug}', - quick_branch_template: null, + branching_strategy: CONFIG_DEFAULTS.branching_strategy, + phase_branch_template: CONFIG_DEFAULTS.phase_branch_template, + milestone_branch_template: CONFIG_DEFAULTS.milestone_branch_template, + quick_branch_template: CONFIG_DEFAULTS.quick_branch_template, }, workflow: { research: true, diff --git a/get-shit-done/bin/lib/core.cjs b/get-shit-done/bin/lib/core.cjs index e8ad18b9e..7477741db 100644 --- a/get-shit-done/bin/lib/core.cjs +++ b/get-shit-done/bin/lib/core.cjs @@ -214,32 +214,37 @@ function safeReadFile(filePath) { } } +/** + * Canonical config defaults. Single source of truth — imported by config.cjs and verify.cjs. + */ +const CONFIG_DEFAULTS = { + model_profile: 'balanced', + commit_docs: true, + search_gitignored: false, + branching_strategy: 'none', + phase_branch_template: 'gsd/phase-{phase}-{slug}', + milestone_branch_template: 'gsd/{milestone}-{slug}', + quick_branch_template: null, + research: true, + plan_checker: true, + verifier: true, + nyquist_validation: true, + parallelization: true, + brave_search: false, + firecrawl: false, + exa_search: false, + text_mode: false, // when true, use plain-text numbered lists instead of AskUserQuestion menus + sub_repos: [], + resolve_model_ids: false, // false: return alias as-is | true: map to full Claude model ID | "omit": return '' (runtime uses its default) + context_window: 200000, // default 200k; set to 1000000 for Opus/Sonnet 4.6 1M models + phase_naming: 'sequential', // 'sequential' (default, auto-increment) or 'custom' (arbitrary string IDs) + project_code: null, // optional short prefix for phase dirs (e.g., 'CK' → 'CK-01-foundation') + subagent_timeout: 300000, // 5 min default; increase for large codebases or slower models (ms) +}; + function loadConfig(cwd) { const configPath = path.join(planningDir(cwd), 'config.json'); - const defaults = { - model_profile: 'balanced', - commit_docs: true, - search_gitignored: false, - branching_strategy: 'none', - phase_branch_template: 'gsd/phase-{phase}-{slug}', - milestone_branch_template: 'gsd/{milestone}-{slug}', - quick_branch_template: null, - research: true, - plan_checker: true, - verifier: true, - nyquist_validation: true, - parallelization: true, - brave_search: false, - firecrawl: false, - exa_search: false, - text_mode: false, // when true, use plain-text numbered lists instead of AskUserQuestion menus - sub_repos: [], - resolve_model_ids: false, // false: return alias as-is | true: map to full Claude model ID | "omit": return '' (runtime uses its default) - context_window: 200000, // default 200k; set to 1000000 for Opus/Sonnet 4.6 1M models - phase_naming: 'sequential', // 'sequential' (default, auto-increment) or 'custom' (arbitrary string IDs) - project_code: null, // optional short prefix for phase dirs (e.g., 'CK' → 'CK-01-foundation') - subagent_timeout: 300000, // 5 min default; increase for large codebases or slower models (ms) - }; + const defaults = CONFIG_DEFAULTS; try { const raw = fs.readFileSync(configPath, 'utf-8'); @@ -1484,6 +1489,7 @@ module.exports = { detectSubRepos, reapStaleTempFiles, MODEL_ALIAS_MAP, + CONFIG_DEFAULTS, planningDir, planningRoot, planningPaths, diff --git a/get-shit-done/bin/lib/verify.cjs b/get-shit-done/bin/lib/verify.cjs index b3ce3c582..081dd3a6f 100644 --- a/get-shit-done/bin/lib/verify.cjs +++ b/get-shit-done/bin/lib/verify.cjs @@ -5,7 +5,7 @@ const fs = require('fs'); const path = require('path'); const os = require('os'); -const { safeReadFile, loadConfig, normalizePhaseName, escapeRegex, execGit, findPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, planningDir, planningRoot, output, error, checkAgentsInstalled } = require('./core.cjs'); +const { safeReadFile, loadConfig, normalizePhaseName, escapeRegex, execGit, findPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, planningDir, planningRoot, output, error, checkAgentsInstalled, CONFIG_DEFAULTS } = require('./core.cjs'); const { extractFrontmatter, parseMustHavesBlock } = require('./frontmatter.cjs'); const { writeStateMd } = require('./state.cjs'); @@ -831,21 +831,21 @@ function cmdValidateHealth(cwd, options, raw) { case 'createConfig': case 'resetConfig': { const defaults = { - model_profile: 'balanced', - commit_docs: true, - search_gitignored: false, - branching_strategy: 'none', - phase_branch_template: 'gsd/phase-{phase}-{slug}', - milestone_branch_template: 'gsd/{milestone}-{slug}', - quick_branch_template: null, + model_profile: CONFIG_DEFAULTS.model_profile, + commit_docs: CONFIG_DEFAULTS.commit_docs, + search_gitignored: CONFIG_DEFAULTS.search_gitignored, + branching_strategy: CONFIG_DEFAULTS.branching_strategy, + phase_branch_template: CONFIG_DEFAULTS.phase_branch_template, + milestone_branch_template: CONFIG_DEFAULTS.milestone_branch_template, + quick_branch_template: CONFIG_DEFAULTS.quick_branch_template, workflow: { - research: true, - plan_check: true, - verifier: true, - nyquist_validation: true, + research: CONFIG_DEFAULTS.research, + plan_check: CONFIG_DEFAULTS.plan_checker, + verifier: CONFIG_DEFAULTS.verifier, + nyquist_validation: CONFIG_DEFAULTS.nyquist_validation, }, - parallelization: true, - brave_search: false, + parallelization: CONFIG_DEFAULTS.parallelization, + brave_search: CONFIG_DEFAULTS.brave_search, }; fs.writeFileSync(configPath, JSON.stringify(defaults, null, 2), 'utf-8'); repairActions.push({ action: repair, success: true, path: 'config.json' });