Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions server/workflow-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export interface ReviewRepoConfig {
/** Workflow kind to run. Defaults to 'code-review.daily'. */
kind?: string
customPrompt?: string
/** Claude model to use for this workflow (e.g. 'claude-sonnet-4-6'). Omit for system default. */
model?: string
}

export interface WorkflowConfig {
Expand Down
5 changes: 5 additions & 0 deletions server/workflow-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* outputDir: .codekin/reports/code-review
* filenameSuffix: _code-review-daily.md
* commitMessage: chore: code review
* model: claude-sonnet-4-6 # optional — defaults to system default
* ---
* You are performing a daily automated code review...
*
Expand Down Expand Up @@ -54,6 +55,7 @@ export interface WorkflowDef {
outputDir: string
filenameSuffix: string
commitMessage: string
model?: string
prompt: string
}

Expand Down Expand Up @@ -88,6 +90,7 @@ function parseMdWorkflow(content: string, sourcePath: string): WorkflowDef {
outputDir: meta.outputDir,
filenameSuffix: meta.filenameSuffix,
commitMessage: meta.commitMessage,
model: meta.model,
prompt,
}
}
Expand Down Expand Up @@ -233,9 +236,11 @@ function registerWorkflow(engine: WorkflowEngine, sessions: SessionManager, def:
const repoPath = input.repoPath as string
const repoName = (input.repoName as string) || repoPath.split('/').pop() || 'unknown'

const model = (input.model as string | undefined) || def.model
const session = sessions.create(`${def.sessionPrefix}:${repoName}`, repoPath, {
source: 'workflow',
groupDir: repoPath,
model,
})

console.log(`[workflow:${def.kind}] Created session ${session.id} for ${repoName} (run ${ctx.runId})`)
Expand Down
5 changes: 3 additions & 2 deletions server/workflow-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function syncSchedules(sessions?: SessionManager) {
id: repo.id,
kind: repo.kind ?? 'code-review.daily',
cronExpression: repo.cronExpression,
input: { repoPath: repo.repoPath, repoName: repo.name, customPrompt: repo.customPrompt },
input: { repoPath: repo.repoPath, repoName: repo.name, customPrompt: repo.customPrompt, model: repo.model },
enabled: repo.enabled,
})
}
Expand Down Expand Up @@ -248,7 +248,7 @@ export function createWorkflowRouter(verifyToken: VerifyFn, extractToken: Extrac
})

router.post('/config/repos', (req, res) => {
const { id, name, repoPath, cronExpression, enabled, customPrompt, kind } = req.body as Partial<ReviewRepoConfig>
const { id, name, repoPath, cronExpression, enabled, customPrompt, kind, model } = req.body as Partial<ReviewRepoConfig>
if (!id || !name || !repoPath || !cronExpression) {
return res.status(400).json({ error: 'Missing required fields: id, name, repoPath, cronExpression' })
}
Expand All @@ -268,6 +268,7 @@ export function createWorkflowRouter(verifyToken: VerifyFn, extractToken: Extrac
enabled: enabled !== false,
kind,
customPrompt,
model,
})

// Re-sync schedules with updated config
Expand Down
26 changes: 25 additions & 1 deletion src/components/AddWorkflowModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useRepos } from '../hooks/useRepos'
import { listKinds } from '../lib/workflowApi'
import type { ReviewRepoConfig, WorkflowKindInfo } from '../lib/workflowApi'
import {
WORKFLOW_KINDS, DAY_PRESETS, DAY_INDIVIDUAL,
WORKFLOW_KINDS, DAY_PRESETS, DAY_INDIVIDUAL, MODEL_OPTIONS,
buildCron, describeCron, slugify, kindCategory,
isBiweeklyDow,
} from '../lib/workflowHelpers'
Expand All @@ -31,6 +31,7 @@ interface FormState {
cronMinute: number
cronDow: string
customPrompt: string
model: string
}

interface Props {
Expand Down Expand Up @@ -337,6 +338,27 @@ function StepSchedule({
</div>
</div>

{/* Model selection */}
<div>
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Model</label>
<div className="flex gap-1.5">
{MODEL_OPTIONS.map(m => (
<button
key={m.value}
type="button"
onClick={() => onChange({ model: m.value })}
className={`rounded-md border px-3 py-1.5 text-[13px] font-medium transition-colors ${
form.model === m.value
? 'border-accent-6 bg-accent-9/40 text-accent-2'
: 'border-neutral-7 bg-neutral-10 text-neutral-3 hover:border-neutral-6 hover:text-neutral-2'
}`}
>
{m.label}
</button>
))}
</div>
</div>

<div>
<label className="block text-[13px] font-medium text-neutral-3 mb-1.5">
Focus areas <span className="text-neutral-5 font-normal">(optional)</span>
Expand Down Expand Up @@ -369,6 +391,7 @@ export function AddWorkflowModal({ token, onClose, onAdd }: Props) {
cronMinute: 0,
cronDow: '*',
customPrompt: '',
model: '',
})
const [saving, setSaving] = useState(false)
const [formError, setFormError] = useState<string | null>(null)
Expand Down Expand Up @@ -412,6 +435,7 @@ export function AddWorkflowModal({ token, onClose, onAdd }: Props) {
kind: form.kind,
enabled: true,
customPrompt: form.customPrompt.trim() || undefined,
model: form.model || undefined,
})
onClose()
} catch (err) {
Expand Down
21 changes: 20 additions & 1 deletion src/components/EditWorkflowModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useState } from 'react'
import { IconX, IconLoader2 } from '@tabler/icons-react'
import type { ReviewRepoConfig } from '../lib/workflowApi'
import {
WORKFLOW_KINDS, DAY_PRESETS, DAY_INDIVIDUAL, isBiweeklyDow,
WORKFLOW_KINDS, DAY_PRESETS, DAY_INDIVIDUAL, MODEL_OPTIONS, isBiweeklyDow,
buildCron, parseCron, describeCron, kindLabel,
} from '../lib/workflowHelpers'
import { CategoryBadge } from './WorkflowBadges'
Expand Down Expand Up @@ -36,6 +36,7 @@ export function EditWorkflowModal({ repo, onClose, onSave }: Props) {
cronMinute: parsed.minute,
cronDow: parsed.dow,
customPrompt: repo.customPrompt ?? '',
model: repo.model ?? '',
})
const [saving, setSaving] = useState(false)
const [formError, setFormError] = useState<string | null>(null)
Expand All @@ -49,6 +50,7 @@ export function EditWorkflowModal({ repo, onClose, onSave }: Props) {
kind: form.kind,
cronExpression: buildCron(form.cronHour, form.cronDow, form.cronMinute),
customPrompt: form.customPrompt.trim() || undefined,
model: form.model || undefined,
})
onClose()
} catch (err) {
Expand Down Expand Up @@ -162,6 +164,23 @@ export function EditWorkflowModal({ repo, onClose, onSave }: Props) {
</div>
</div>

{/* Model selection */}
<div>
<label className="block text-[13px] font-medium text-neutral-3 mb-2">Model</label>
<div className="flex gap-1.5">
{MODEL_OPTIONS.map(m => (
<button
key={m.value}
type="button"
onClick={() => setForm(f => ({ ...f, model: m.value }))}
className={btnClass(form.model === m.value)}
>
{m.label}
</button>
))}
</div>
</div>

{/* Custom prompt */}
<div>
<label className="block text-[13px] font-medium text-neutral-3 mb-1.5">
Expand Down
1 change: 1 addition & 0 deletions src/lib/workflowApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface ReviewRepoConfig {
enabled: boolean
kind?: string
customPrompt?: string
model?: string
}

export interface WorkflowConfig {
Expand Down
6 changes: 6 additions & 0 deletions src/lib/workflowHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const WORKFLOW_KINDS = [
{ value: 'complexity.weekly', label: 'Complexity Report', category: 'assessment' as WorkflowCategory },
]

export const MODEL_OPTIONS = [
{ value: '', label: 'Default (Opus)' },
{ value: 'claude-sonnet-4-6', label: 'Sonnet 4.6' },
{ value: 'claude-haiku-4-5-20251001', label: 'Haiku 4.5' },
]

export const DAY_PRESETS = [
{ label: 'Daily', dow: '*' },
{ label: 'Weekdays', dow: '1-5' },
Expand Down