Multi-run with configurable prompt templates (#1111)
* Multi-run with configurable prompt templates * Fixes * Fix handleDuplicate fire-and-forget: await createTemplate and handle failure * Add Polish translations for prompt template and multirun group keys * fix: migrate remaining Remix icons to Icon component in MultiRunLauncher --------- Signed-off-by: Tom Rochette <roctom@gmail.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import {
|
||||
readConfigLayers,
|
||||
writeConfig,
|
||||
getJsonWriteTarget,
|
||||
CONFIG_FILE,
|
||||
} from './shared.js';
|
||||
|
||||
const SECTION_KEY = 'promptTemplates';
|
||||
|
||||
const DEFAULT_TEMPLATES = {
|
||||
simple: {
|
||||
name: 'Simple',
|
||||
body: 'Implement this task using the simplest possible approach. Prefer readability and straightforward solutions over clever abstractions. Keep the code easy to understand and maintain.',
|
||||
isDefault: true,
|
||||
},
|
||||
fast: {
|
||||
name: 'Fast',
|
||||
body: 'Implement this task as quickly as possible. Optimize for speed of development. Use the most direct path to a working solution. Favor existing libraries and proven patterns.',
|
||||
isDefault: true,
|
||||
},
|
||||
'memory-efficient': {
|
||||
name: 'Memory Efficient',
|
||||
body: 'Implement this task with memory efficiency in mind. Minimize memory allocations, use streaming where possible, avoid holding large data structures in memory, and prefer lazy evaluation.',
|
||||
isDefault: true,
|
||||
},
|
||||
'cpu-efficient': {
|
||||
name: 'CPU Efficient',
|
||||
body: 'Implement this task with CPU efficiency in mind. Optimize algorithms, minimize unnecessary computations, use efficient data structures, and avoid redundant work.',
|
||||
isDefault: true,
|
||||
},
|
||||
'tests-first': {
|
||||
name: 'Tests First',
|
||||
body: 'Implement this task using a test-driven approach. Write tests first, then implement the minimum code to pass them. Ensure comprehensive test coverage including edge cases.',
|
||||
isDefault: true,
|
||||
},
|
||||
'spec-first': {
|
||||
name: 'Spec First',
|
||||
body: 'Implement this task by first creating a detailed specification, then implementing according to the spec. Start by documenting the requirements, interfaces, and expected behavior before writing any implementation code.',
|
||||
isDefault: true,
|
||||
},
|
||||
};
|
||||
|
||||
function slugify(name) {
|
||||
return name
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.substring(0, 60);
|
||||
}
|
||||
|
||||
function ensureDefaults(templates) {
|
||||
if (!templates || typeof templates !== 'object') {
|
||||
return { ...DEFAULT_TEMPLATES };
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
const result = { ...templates };
|
||||
|
||||
for (const [id, template] of Object.entries(DEFAULT_TEMPLATES)) {
|
||||
if (!(id in result)) {
|
||||
result[id] = { ...template };
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed ? result : templates;
|
||||
}
|
||||
|
||||
function readTemplatesFromConfig(workingDirectory) {
|
||||
const layers = readConfigLayers(workingDirectory);
|
||||
const merged = layers.mergedConfig || {};
|
||||
const raw = merged[SECTION_KEY];
|
||||
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return ensureDefaults(null);
|
||||
}
|
||||
|
||||
return ensureDefaults(raw);
|
||||
}
|
||||
|
||||
function writeTemplatesToConfig(templates, workingDirectory) {
|
||||
const layers = readConfigLayers(workingDirectory);
|
||||
const target = getJsonWriteTarget(layers, 'user');
|
||||
const config = { ...target.config };
|
||||
config[SECTION_KEY] = templates;
|
||||
writeConfig(config, target.path || CONFIG_FILE);
|
||||
}
|
||||
|
||||
export function listPromptTemplates(workingDirectory) {
|
||||
const templates = readTemplatesFromConfig(workingDirectory);
|
||||
return Object.entries(templates).map(([id, value]) => ({
|
||||
id,
|
||||
name: value.name || id,
|
||||
body: value.body || '',
|
||||
isDefault: value.isDefault === true,
|
||||
}));
|
||||
}
|
||||
|
||||
export function getPromptTemplate(id, workingDirectory) {
|
||||
const templates = readTemplatesFromConfig(workingDirectory);
|
||||
const entry = templates[id];
|
||||
if (!entry) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id,
|
||||
name: entry.name || id,
|
||||
body: entry.body || '',
|
||||
isDefault: entry.isDefault === true,
|
||||
};
|
||||
}
|
||||
|
||||
export function createPromptTemplate(id, config, workingDirectory) {
|
||||
const templates = readTemplatesFromConfig(workingDirectory);
|
||||
|
||||
if (templates[id]) {
|
||||
throw new Error(`Prompt template "${id}" already exists`);
|
||||
}
|
||||
|
||||
templates[id] = {
|
||||
name: config.name || id,
|
||||
body: config.body || '',
|
||||
isDefault: false,
|
||||
};
|
||||
|
||||
writeTemplatesToConfig(templates, workingDirectory);
|
||||
return getPromptTemplate(id, workingDirectory);
|
||||
}
|
||||
|
||||
export function updatePromptTemplate(id, updates, workingDirectory) {
|
||||
const templates = readTemplatesFromConfig(workingDirectory);
|
||||
const existing = templates[id];
|
||||
|
||||
if (!existing) {
|
||||
throw new Error(`Prompt template "${id}" not found`);
|
||||
}
|
||||
|
||||
templates[id] = {
|
||||
...existing,
|
||||
...(updates.name !== undefined ? { name: updates.name } : {}),
|
||||
...(updates.body !== undefined ? { body: updates.body } : {}),
|
||||
};
|
||||
|
||||
writeTemplatesToConfig(templates, workingDirectory);
|
||||
return getPromptTemplate(id, workingDirectory);
|
||||
}
|
||||
|
||||
export function deletePromptTemplate(id, workingDirectory) {
|
||||
const templates = readTemplatesFromConfig(workingDirectory);
|
||||
|
||||
if (!templates[id]) {
|
||||
throw new Error(`Prompt template "${id}" not found`);
|
||||
}
|
||||
|
||||
delete templates[id];
|
||||
writeTemplatesToConfig(templates, workingDirectory);
|
||||
}
|
||||
|
||||
export { slugify };
|
||||
Reference in New Issue
Block a user