diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-08 01:02:41 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-08 06:31:50 +0000 |
| commit | 560f42ba271ffc06eae1f93bccb26593cf93b150 (patch) | |
| tree | 2c61732110dd2faf4eddbcd52ad6dbb98f7b431c /web | |
| parent | 73d0d0a7fd49dee324c2cfc8816c6be93b07dac1 (diff) | |
feat(web): support agent type selection and rename Claude to Agent in UI
Diffstat (limited to 'web')
| -rw-r--r-- | web/app.js | 105 | ||||
| -rw-r--r-- | web/index.html | 43 |
2 files changed, 94 insertions, 54 deletions
@@ -439,7 +439,7 @@ async function updateTask(taskId, body) { } function createEditForm(task) { - const c = task.claude || {}; + const a = task.agent || {}; const form = document.createElement('div'); form.className = 'task-inline-edit'; @@ -460,10 +460,25 @@ function createEditForm(task) { form.appendChild(makeField('Name', 'input', { type: 'text', name: 'name', value: task.name || '' })); form.appendChild(makeField('Description', 'textarea', { name: 'description', rows: '2', value: task.description || '' })); - form.appendChild(makeField('Instructions', 'textarea', { name: 'instructions', rows: '4', value: c.instructions || '' })); - form.appendChild(makeField('Model', 'input', { type: 'text', name: 'model', value: c.model || 'sonnet' })); - form.appendChild(makeField('Working Directory', 'input', { type: 'text', name: 'working_dir', value: c.working_dir || '', placeholder: '/path/to/repo' })); - form.appendChild(makeField('Max Budget (USD)', 'input', { type: 'number', name: 'max_budget_usd', step: '0.01', value: c.max_budget_usd != null ? String(c.max_budget_usd) : '1.00' })); + form.appendChild(makeField('Instructions', 'textarea', { name: 'instructions', rows: '4', value: a.instructions || '' })); + + const typeLabel = document.createElement('label'); + typeLabel.textContent = 'Agent Type'; + const typeSel = document.createElement('select'); + typeSel.name = 'type'; + for (const val of ['claude', 'gemini']) { + const opt = document.createElement('option'); + opt.value = val; + opt.textContent = val.charAt(0).toUpperCase() + val.slice(1); + if (val === (a.type || 'claude')) opt.selected = true; + typeSel.appendChild(opt); + } + typeLabel.appendChild(typeSel); + form.appendChild(typeLabel); + + form.appendChild(makeField('Model', 'input', { type: 'text', name: 'model', value: a.model || 'sonnet' })); + form.appendChild(makeField('Working Directory', 'input', { type: 'text', name: 'working_dir', value: a.working_dir || '', placeholder: '/path/to/repo' })); + form.appendChild(makeField('Max Budget (USD)', 'input', { type: 'number', name: 'max_budget_usd', step: '0.01', value: a.max_budget_usd != null ? String(a.max_budget_usd) : '1.00' })); form.appendChild(makeField('Timeout', 'input', { type: 'text', name: 'timeout', value: formatDurationForInput(task.timeout) || '15m', placeholder: '15m' })); const prioLabel = document.createElement('label'); @@ -511,7 +526,8 @@ async function handleEditSave(taskId, form, saveBtn) { const body = { name: get('name'), description: get('description'), - claude: { + agent: { + type: get('type'), model: get('model'), instructions: get('instructions'), working_dir: get('working_dir'), @@ -986,11 +1002,12 @@ function buildValidatePayload() { const instructions = f.querySelector('[name="instructions"]').value; const working_dir = f.querySelector('[name="working_dir"]').value; const model = f.querySelector('[name="model"]').value; + const type = f.querySelector('[name="type"]').value; const allowedToolsEl = f.querySelector('[name="allowed_tools"]'); const allowed_tools = allowedToolsEl ? allowedToolsEl.value.split(',').map(s => s.trim()).filter(Boolean) : []; - return { name, claude: { instructions, working_dir, model, allowed_tools } }; + return { name, agent: { type, instructions, working_dir, model, allowed_tools } }; } function renderValidationResult(result) { @@ -1079,6 +1096,7 @@ function initProjectSelect() { const select = document.getElementById('project-select'); const newRow = document.getElementById('new-project-row'); const newInput = document.getElementById('new-project-input'); + if (!select) return; select.addEventListener('change', () => { if (select.value === '__new__') { newRow.hidden = false; @@ -1110,7 +1128,8 @@ async function createTask(formData) { const body = { name: formData.get('name'), description: '', - claude: { + agent: { + type: formData.get('type'), model: formData.get('model'), instructions: formData.get('instructions'), working_dir: workingDir, @@ -1154,7 +1173,8 @@ async function saveTemplate(formData) { const body = { name: formData.get('name'), description: formData.get('description'), - claude: { + agent: { + type: formData.get('type'), model: formData.get('model'), instructions: formData.get('instructions'), working_dir: formData.get('working_dir'), @@ -1329,31 +1349,32 @@ function renderTaskPanel(task, executions) { overview.appendChild(overviewGrid); content.appendChild(overview); - // ── Claude Config ── - const c = task.claude || {}; - const claudeSection = makeSection('Claude Config'); - const claudeGrid = document.createElement('div'); - claudeGrid.className = 'meta-grid'; - claudeGrid.append( - makeMetaItem('Model', c.model), - makeMetaItem('Max Budget', c.max_budget_usd != null ? `$${c.max_budget_usd.toFixed(2)}` : '—'), - makeMetaItem('Working Dir', c.working_dir), - makeMetaItem('Permission Mode', c.permission_mode || 'default'), + // ── Agent Config ── + const a = task.agent || {}; + const agentSection = makeSection('Agent Config'); + const agentGrid = document.createElement('div'); + agentGrid.className = 'meta-grid'; + agentGrid.append( + makeMetaItem('Type', a.type || 'claude'), + makeMetaItem('Model', a.model), + makeMetaItem('Max Budget', a.max_budget_usd != null ? `$${a.max_budget_usd.toFixed(2)}` : '—'), + makeMetaItem('Working Dir', a.working_dir), + makeMetaItem('Permission Mode', a.permission_mode || 'default'), ); - if (c.allowed_tools && c.allowed_tools.length > 0) { - claudeGrid.append(makeMetaItem('Allowed Tools', c.allowed_tools.join(', '), { fullWidth: true })); + if (a.allowed_tools && a.allowed_tools.length > 0) { + agentGrid.append(makeMetaItem('Allowed Tools', a.allowed_tools.join(', '), { fullWidth: true })); } - if (c.disallowed_tools && c.disallowed_tools.length > 0) { - claudeGrid.append(makeMetaItem('Disallowed Tools', c.disallowed_tools.join(', '), { fullWidth: true })); + if (a.disallowed_tools && a.disallowed_tools.length > 0) { + agentGrid.append(makeMetaItem('Disallowed Tools', a.disallowed_tools.join(', '), { fullWidth: true })); } - if (c.instructions) { - claudeGrid.append(makeMetaItem('Instructions', c.instructions, { fullWidth: true, code: true })); + if (a.instructions) { + agentGrid.append(makeMetaItem('Instructions', a.instructions, { fullWidth: true, code: true })); } - if (c.system_prompt_append) { - claudeGrid.append(makeMetaItem('System Prompt Append', c.system_prompt_append, { fullWidth: true, code: true })); + if (a.system_prompt_append) { + agentGrid.append(makeMetaItem('System Prompt Append', a.system_prompt_append, { fullWidth: true, code: true })); } - claudeSection.appendChild(claudeGrid); - content.appendChild(claudeSection); + agentSection.appendChild(agentGrid); + content.appendChild(agentSection); // ── Execution Settings ── const settingsSection = makeSection('Execution Settings'); @@ -1709,23 +1730,25 @@ if (typeof document !== 'undefined') document.addEventListener('DOMContentLoaded const f = document.getElementById('task-form'); if (result.name) f.querySelector('[name="name"]').value = result.name; - if (result.claude && result.claude.instructions) - f.querySelector('[name="instructions"]').value = result.claude.instructions; - if (result.claude && result.claude.working_dir) { - const sel = document.getElementById('project-select'); - const exists = [...sel.options].some(o => o.value === result.claude.working_dir); + if (result.agent && result.agent.instructions) + f.querySelector('[name="instructions"]').value = result.agent.instructions; + if (result.agent && result.agent.working_dir) { + const pSel = document.getElementById('project-select'); + const exists = [...pSel.options].some(o => o.value === result.agent.working_dir); if (exists) { - sel.value = result.claude.working_dir; + pSel.value = result.agent.working_dir; } else { - sel.value = '__new__'; + pSel.value = '__new__'; document.getElementById('new-project-row').hidden = false; - document.getElementById('new-project-input').value = result.claude.working_dir; + document.getElementById('new-project-input').value = result.agent.working_dir; } } - if (result.claude && result.claude.model) - f.querySelector('[name="model"]').value = result.claude.model; - if (result.claude && result.claude.max_budget_usd != null) - f.querySelector('[name="max_budget_usd"]').value = result.claude.max_budget_usd; + if (result.agent && result.agent.model) + f.querySelector('[name="model"]').value = result.agent.model; + if (result.agent && result.agent.type) + f.querySelector('[name="type"]').value = result.agent.type; + if (result.agent && result.agent.max_budget_usd != null) + f.querySelector('[name="max_budget_usd"]').value = result.agent.max_budget_usd; if (result.timeout) f.querySelector('[name="timeout"]').value = result.timeout; if (result.priority) { diff --git a/web/index.html b/web/index.html index 8bfa6bb..d7fb591 100644 --- a/web/index.html +++ b/web/index.html @@ -11,7 +11,6 @@ <body> <header> <h1>Claudomator</h1> - <button id="btn-start-next" class="btn-secondary">Start Next</button> <button id="btn-new-task" class="btn-primary">New Task</button> </header> <nav class="tab-bar"> @@ -25,6 +24,7 @@ <button class="filter-tab active" data-filter="active">Active</button> <button class="filter-tab" data-filter="done">Done</button> <button class="filter-tab" data-filter="all">All</button> + <button id="btn-start-next" class="btn-secondary btn-sm">Start Next</button> </div> <div class="task-list"> <div id="loading">Loading tasks…</div> @@ -46,24 +46,16 @@ <form id="task-form" method="dialog"> <h2>New Task</h2> <div class="elaborate-section"> - <label>Describe what you want Claude to do + <label>Describe what you want the agent to do <textarea id="elaborate-prompt" rows="3" placeholder="e.g. run tests with race detector and check coverage"></textarea> </label> <button type="button" id="btn-elaborate" class="btn-secondary"> Draft with AI ✦ </button> - <p class="elaborate-hint">Claude will fill in the form fields below. You can edit before submitting.</p> + <p class="elaborate-hint">AI will fill in the form fields below. You can edit before submitting.</p> </div> <hr class="form-divider"> - <label>Project - <select name="working_dir" id="project-select"> - <option value="/workspace/claudomator" selected>/workspace/claudomator</option> - </select> - </label> - <div id="new-project-row" hidden> - <label>New project path <input id="new-project-input" placeholder="/workspace/my-project"></label> - </div> <label>Name <input name="name" required></label> <label>Instructions <textarea name="instructions" rows="6" required></textarea></label> <div class="validate-section"> @@ -72,7 +64,24 @@ </button> <div id="validate-result" hidden></div> </div> - <label>Model <input name="model" value="sonnet"></label> + <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;"> + <label>Agent Type + <select name="type"> + <option value="claude" selected>Claude</option> + <option value="gemini">Gemini</option> + </select> + </label> + <label>Model <input name="model" value="sonnet" placeholder="e.g. sonnet, gemini-2.0-flash"></label> + </div> + <label>Project + <select id="project-select" name="working_dir"> + <option value="/workspace/claudomator" selected>/workspace/claudomator</option> + <option value="__new__">Create new project…</option> + </select> + </label> + <div id="new-project-row" hidden> + <label>New Project Path <input id="new-project-input" placeholder="/workspace/my-new-app"></label> + </div> <label>Max Budget (USD) <input name="max_budget_usd" type="number" step="0.01" value="1.00"></label> <label>Timeout <input name="timeout" value="15m"></label> <label>Priority @@ -94,7 +103,15 @@ <h2>New Template</h2> <label>Name <input name="name" required></label> <label>Description <textarea name="description" rows="2"></textarea></label> - <label>Model <input name="model" value="sonnet"></label> + <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;"> + <label>Agent Type + <select name="type"> + <option value="claude" selected>Claude</option> + <option value="gemini">Gemini</option> + </select> + </label> + <label>Model <input name="model" value="sonnet" placeholder="e.g. sonnet, gemini-2.0-flash"></label> + </div> <label>Instructions <textarea name="instructions" rows="6" required></textarea></label> <label>Working Directory <input name="working_dir" placeholder="/path/to/repo"></label> <label>Max Budget (USD) <input name="max_budget_usd" type="number" step="0.01" value="1.00"></label> |
