Files
msd-core/get-shit-done/bin/lib/model-profiles.cjs
Tom Boucher d59d635560 feat: add gsd-pattern-mapper agent for codebase pattern analysis (#1861)
Add a new pattern mapper agent that analyzes the codebase for existing
patterns before planning, producing PATTERNS.md with per-file analog
assignments and code excerpts. Integrated into plan-phase workflow as
Step 7.8 (between research and planning), controlled by the
workflow.pattern_mapper config key (default: true).

Changes:
- New agent: agents/gsd-pattern-mapper.md
- New config key: workflow.pattern_mapper in VALID_CONFIG_KEYS and CONFIG_DEFAULTS
- init plan-phase: patterns_path field in JSON output
- plan-phase.md: Step 7.8 spawns pattern mapper, PATTERNS_PATH in planner files_to_read
- gsd-plan-checker.md: Dimension 12 (Pattern Compliance)
- model-profiles.cjs: gsd-pattern-mapper profile entry
- Tests: tests/pattern-mapper.test.cjs (5 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-11 14:25:02 -04:00

72 lines
3.8 KiB
JavaScript

/**
* Mapping of GSD agent to model for each profile.
*
* Should be in sync with the profiles table in `get-shit-done/references/model-profiles.md`. But
* possibly worth making this the single source of truth at some point, and removing the markdown
* reference table in favor of programmatically determining the model to use for an agent (which
* would be faster, use fewer tokens, and be less error-prone).
*/
const MODEL_PROFILES = {
'gsd-planner': { quality: 'opus', balanced: 'opus', budget: 'sonnet', adaptive: 'opus' },
'gsd-roadmapper': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet', adaptive: 'sonnet' },
'gsd-executor': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet', adaptive: 'sonnet' },
'gsd-phase-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' },
'gsd-project-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' },
'gsd-research-synthesizer': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-debugger': { quality: 'opus', balanced: 'sonnet', budget: 'sonnet', adaptive: 'opus' },
'gsd-codebase-mapper': { quality: 'sonnet', balanced: 'haiku', budget: 'haiku', adaptive: 'haiku' },
'gsd-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' },
'gsd-plan-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-integration-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-nyquist-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-pattern-mapper': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-ui-researcher': { quality: 'opus', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' },
'gsd-ui-checker': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-ui-auditor': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
'gsd-doc-writer': { quality: 'opus', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' },
'gsd-doc-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' },
};
const VALID_PROFILES = Object.keys(MODEL_PROFILES['gsd-planner']);
/**
* Formats the agent-to-model mapping as a human-readable table (in string format).
*
* @param {Object<string, string>} agentToModelMap - A mapping from agent to model
* @returns {string} A formatted table string
*/
function formatAgentToModelMapAsTable(agentToModelMap) {
const agentWidth = Math.max('Agent'.length, ...Object.keys(agentToModelMap).map((a) => a.length));
const modelWidth = Math.max(
'Model'.length,
...Object.values(agentToModelMap).map((m) => m.length)
);
const sep = '─'.repeat(agentWidth + 2) + '┼' + '─'.repeat(modelWidth + 2);
const header = ' ' + 'Agent'.padEnd(agentWidth) + ' │ ' + 'Model'.padEnd(modelWidth);
let agentToModelTable = header + '\n' + sep + '\n';
for (const [agent, model] of Object.entries(agentToModelMap)) {
agentToModelTable += ' ' + agent.padEnd(agentWidth) + ' │ ' + model.padEnd(modelWidth) + '\n';
}
return agentToModelTable;
}
/**
* Returns a mapping from agent to model for the given model profile.
*
* @param {string} normalizedProfile - The normalized (lowercase and trimmed) profile name
* @returns {Object<string, string>} A mapping from agent to model for the given profile
*/
function getAgentToModelMapForProfile(normalizedProfile) {
const agentToModelMap = {};
for (const [agent, profileToModelMap] of Object.entries(MODEL_PROFILES)) {
agentToModelMap[agent] = profileToModelMap[normalizedProfile];
}
return agentToModelMap;
}
module.exports = {
MODEL_PROFILES,
VALID_PROFILES,
formatAgentToModelMapAsTable,
getAgentToModelMapForProfile,
};