feat: add AI session prompt export
This commit is contained in:
@@ -39,6 +39,83 @@ const renderFeature = (feature: Feature) => {
|
||||
const sortPulsesNewestFirst = (pulses: PulseEvent[]) =>
|
||||
[...pulses].sort((a, b) => b.timestamp.localeCompare(a.timestamp))
|
||||
|
||||
export const createAgentSessionPrompt = (
|
||||
state: AppState,
|
||||
options?: {
|
||||
featureId?: string
|
||||
target?: string
|
||||
},
|
||||
) => {
|
||||
const grouped = groupFeatures(state.features)
|
||||
const target = options?.target || 'AI coding agent'
|
||||
const focusFeature = options?.featureId ? state.features.find((feature) => feature.id === options.featureId) ?? null : grouped.now[0] ?? null
|
||||
const relatedPulses = sortPulsesNewestFirst(state.pulses)
|
||||
.filter((pulse) => (focusFeature ? pulse.feature_id === focusFeature.id : true))
|
||||
.slice(0, 6)
|
||||
|
||||
const renderFeatureBlock = (feature: Feature | null) => {
|
||||
if (!feature) return '- No specific feature selected yet. Choose the smallest useful next move.'
|
||||
|
||||
return [
|
||||
`- Title: ${feature.title}`,
|
||||
`- Description: ${feature.description || '—'}`,
|
||||
`- Column: ${columnLabels[feature.column]}`,
|
||||
`- Priority: ${feature.priority}`,
|
||||
`- Status: ${feature.status}`,
|
||||
`- Acceptance criteria: ${feature.acceptance_criteria.length ? feature.acceptance_criteria.join('; ') : 'None yet'}`,
|
||||
`- Scope notes: ${feature.scope_notes || '—'}`,
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
return [
|
||||
`You are the ${target} for ${state.project.name}.`,
|
||||
'',
|
||||
'Read the project context below and ship the smallest high-quality improvement that satisfies the focus feature without scope creep.',
|
||||
'',
|
||||
'## Project',
|
||||
`- Name: ${state.project.name}`,
|
||||
`- Pitch: ${state.project.one_line_pitch || '—'}`,
|
||||
`- Current goal: ${state.project.current_goal || '—'}`,
|
||||
`- Notes: ${state.project.notes || '—'}`,
|
||||
'',
|
||||
'## Focus Feature',
|
||||
renderFeatureBlock(focusFeature),
|
||||
'',
|
||||
'## Other Active Features',
|
||||
grouped.now.filter((feature) => feature.id !== focusFeature?.id).length
|
||||
? grouped.now
|
||||
.filter((feature) => feature.id !== focusFeature?.id)
|
||||
.map((feature) => `- ${feature.title} (${feature.status}, ${feature.priority})`)
|
||||
.join('\n')
|
||||
: '- None beyond the focus feature.',
|
||||
'',
|
||||
'## Next Up',
|
||||
grouped.next.length ? grouped.next.map((feature) => `- ${feature.title} (${feature.status})`).join('\n') : '- Nothing queued yet.',
|
||||
'',
|
||||
'## Parking Lot / Do Not Implement Yet',
|
||||
state.parking_lot.length
|
||||
? state.parking_lot.map((item) => `- ${item.title} — ${item.reason_parked || item.description || 'Parked for later.'}`).join('\n')
|
||||
: '- Nothing parked yet.',
|
||||
'',
|
||||
'## Recent Relevant Pulses',
|
||||
relatedPulses.length
|
||||
? relatedPulses.map((pulse) => `- ${formatDateTime(pulse.timestamp)} · ${pulse.pulse_type} · ${pulse.message}`).join('\n')
|
||||
: '- No relevant pulse events yet.',
|
||||
'',
|
||||
'## Rules',
|
||||
'- Stay inside the focus feature unless a tiny supporting fix is required.',
|
||||
'- Do not implement Parking Lot items.',
|
||||
'- Preserve working behavior and avoid needless rewrites.',
|
||||
'- Prefer the smallest shippable step with clear evidence.',
|
||||
'',
|
||||
'## Deliver back',
|
||||
'- What changed',
|
||||
'- Files touched',
|
||||
'- How you verified it',
|
||||
'- Any blocker or follow-up you intentionally parked',
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
export const createJsonExport = (state: AppState) =>
|
||||
JSON.stringify(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user