Compare commits
14 Commits
8218a3417e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 24fde566cd | |||
| 6da54d7115 | |||
| f09da97163 | |||
| 223d9325a1 | |||
| 4cfed90f37 | |||
| f09f132220 | |||
| 579cffd874 | |||
| 1654173540 | |||
| f6e0142226 | |||
| 0962548217 | |||
| ec85b8e4d7 | |||
| 34413ffafa | |||
| de1855838e | |||
| 204e5ee64a |
@@ -3,6 +3,9 @@
|
||||
BuildPulse is a calm planning cockpit for AI-assisted product building.
|
||||
It helps capture features, park distracting ideas, log progress as Pulse events, and export clean project context for AI coding agents such as Claude Code, Codex, OpenCode, OpenClaw, or future autonomous agents.
|
||||
|
||||
Current release line:
|
||||
- v0.4.1 — preview/edit-before-copy handoffs with cleaner INTENT controls and tighter mobile UX
|
||||
|
||||
Personal runtime target:
|
||||
- `build.friborg.uk`
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+15
-11
@@ -58,21 +58,25 @@ Potential features:
|
||||
- Required vs optional features
|
||||
- Release readiness view
|
||||
|
||||
## v0.4 — Session Prompt Generator
|
||||
## v0.4 — Handoff Workflow Hardening
|
||||
|
||||
Goal:
|
||||
Generate clean prompts for AI coding agents.
|
||||
Turn a chosen feature into a sharp, target-specific AI coding brief with as little friction as possible.
|
||||
|
||||
Potential features:
|
||||
- Start 30-minute session from feature
|
||||
- Generate Claude Code/Codex prompt
|
||||
- Include do-not-touch list
|
||||
- Include acceptance criteria
|
||||
- Include recent pulse context
|
||||
- End-session summary template
|
||||
Current shipped slices:
|
||||
- v0.3.1 — focused handoff shortcuts
|
||||
- v0.3.2 — target-specific handoff presets
|
||||
- v0.4.0 — one-tap feature handoffs
|
||||
- v0.4.1 — preview/edit before copy + explicit INTENT controls
|
||||
|
||||
Note:
|
||||
BuildPulse now has a lightweight export-side session prompt generator in v0.1.x so handoff quality improves before the fuller v0.4 workflow arrives.
|
||||
Planned slices:
|
||||
- v0.4.2 — paste agent result into RESULT/BLOCKER/TEST_RESULT pulses
|
||||
- v0.4.3 — session modes (30-minute, feature-based, bugfix, QA review)
|
||||
|
||||
Core rules:
|
||||
- Keep handoff generation close to the feature decision surface.
|
||||
- Include release/phase context, blockers, parking-lot warnings, and return format.
|
||||
- Do not add live execution, telemetry, router logic, or agent streaming in this phase.
|
||||
|
||||
## v0.5 — Local/Cloud AI Assistant
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "buildpulse",
|
||||
"private": true,
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.8",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"api": "node --env-file=../.env server/index.mjs",
|
||||
|
||||
+634
-130
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,47 @@ const renderFeature = (feature: Feature) => {
|
||||
const sortPulsesNewestFirst = (pulses: PulseEvent[]) =>
|
||||
[...pulses].sort((a, b) => b.timestamp.localeCompare(a.timestamp))
|
||||
|
||||
const isFeatureDone = (feature: Feature) => feature.status === 'done' || feature.column === 'done'
|
||||
|
||||
const targetPresets: Record<string, { intro: string; workingStyle: string[]; deliverBack: string[] }> = {
|
||||
OpenClaw: {
|
||||
intro: 'Work like a proactive operator: act, verify, and keep the slice small enough to ship cleanly.',
|
||||
workingStyle: [
|
||||
'Use local repo evidence before making claims.',
|
||||
'Prefer direct fixes plus a quick verification gate.',
|
||||
'Keep follow-up notes short and operational.',
|
||||
],
|
||||
deliverBack: ['What changed', 'Files touched', 'How you verified it', 'Any blocker or parked follow-up'],
|
||||
},
|
||||
'Claude Code': {
|
||||
intro: 'Work like a careful coding pair: make the smallest coherent change, explain tradeoffs briefly, and leave the tree tidy.',
|
||||
workingStyle: [
|
||||
'Prefer narrowly scoped edits over broad rewrites.',
|
||||
'Call out any assumption that could affect correctness.',
|
||||
'End with concise verification notes and next-risk if any.',
|
||||
],
|
||||
deliverBack: ['Summary', 'Files changed', 'Verification', 'Risks or follow-ups'],
|
||||
},
|
||||
Codex: {
|
||||
intro: 'Work like a focused implementation run: minimal scope, crisp diffs, and explicit evidence before claiming success.',
|
||||
workingStyle: [
|
||||
'Bias toward direct implementation over long discussion.',
|
||||
'Preserve existing behavior unless the task explicitly changes it.',
|
||||
'Name the exact verification step you ran.',
|
||||
],
|
||||
deliverBack: ['Changes made', 'Touched files', 'Verification run', 'Remaining follow-up'],
|
||||
},
|
||||
'Generic Agent': {
|
||||
intro: 'Deliver one clean, well-bounded improvement without drifting into adjacent ideas.',
|
||||
workingStyle: [
|
||||
'Stay anchored to the focus feature.',
|
||||
'Avoid speculative architecture work.',
|
||||
'Return with concrete evidence, not just intention.',
|
||||
],
|
||||
deliverBack: ['What changed', 'Files touched', 'How you verified it', 'Any blocker or parked follow-up'],
|
||||
},
|
||||
}
|
||||
|
||||
export const createAgentSessionPrompt = (
|
||||
state: AppState,
|
||||
options?: {
|
||||
@@ -48,12 +89,36 @@ export const createAgentSessionPrompt = (
|
||||
) => {
|
||||
const grouped = groupFeatures(state.features)
|
||||
const target = options?.target || 'AI coding agent'
|
||||
const preset = targetPresets[target] || targetPresets['Generic Agent']
|
||||
const focusFeature = options?.featureId ? state.features.find((feature) => feature.id === options.featureId) ?? null : grouped.now[0] ?? null
|
||||
const focusRelease = focusFeature?.release_id ? state.releases.find((release) => release.id === focusFeature.release_id) ?? null : null
|
||||
const focusPhase = focusFeature?.phase_id ? state.phases.find((phase) => phase.id === focusFeature.phase_id) ?? null : null
|
||||
const relatedPulses = sortPulsesNewestFirst(state.pulses)
|
||||
.filter((pulse) => (focusFeature ? pulse.feature_id === focusFeature.id : true))
|
||||
.slice(0, 6)
|
||||
const releaseFeatures = focusRelease
|
||||
? state.features.filter((feature) => feature.release_id === focusRelease.id)
|
||||
: []
|
||||
const requiredReleaseFeatures = focusRelease
|
||||
? releaseFeatures.filter((feature) => focusRelease.required_feature_ids.includes(feature.id))
|
||||
: []
|
||||
const completedRequiredFeatures = requiredReleaseFeatures.filter(isFeatureDone)
|
||||
const remainingRequiredFeatures = requiredReleaseFeatures.filter((feature) => !isFeatureDone(feature))
|
||||
const releaseBlockers = sortPulsesNewestFirst(state.pulses)
|
||||
.filter((pulse) => {
|
||||
if (pulse.pulse_type !== 'BLOCKER') return false
|
||||
if (focusFeature && pulse.feature_id === focusFeature.id) return true
|
||||
if (!focusRelease) return false
|
||||
return releaseFeatures.some((feature) => feature.id === pulse.feature_id)
|
||||
})
|
||||
.slice(0, 4)
|
||||
const forbiddenWarnings = [
|
||||
...(focusRelease?.forbidden_feature_titles || []).map((title) => `${title} — forbidden in this release.`),
|
||||
...state.parking_lot.map((item) => `${item.title} — ${item.reason_parked || item.description || 'Parked for later.'}`),
|
||||
]
|
||||
const readinessSummary = focusRelease
|
||||
? `Required done: ${completedRequiredFeatures.length}/${requiredReleaseFeatures.length || focusRelease.required_feature_ids.length || 0}`
|
||||
: 'No release linked to this feature yet.'
|
||||
|
||||
const renderFeatureBlock = (feature: Feature | null) => {
|
||||
if (!feature) return '- No specific feature selected yet. Choose the smallest useful next move.'
|
||||
@@ -75,6 +140,7 @@ export const createAgentSessionPrompt = (
|
||||
return [
|
||||
`You are the ${target} for ${state.project.name}.`,
|
||||
'',
|
||||
preset.intro,
|
||||
'Read the project context below and ship the smallest high-quality improvement that satisfies the focus feature without scope creep.',
|
||||
'',
|
||||
'## Project',
|
||||
@@ -97,15 +163,28 @@ export const createAgentSessionPrompt = (
|
||||
'## Next Up',
|
||||
grouped.next.length ? grouped.next.map((feature) => `- ${feature.title} (${feature.status})`).join('\n') : '- Nothing queued yet.',
|
||||
'',
|
||||
'## Current Release Context',
|
||||
'## Current Phase + Release Context',
|
||||
focusRelease
|
||||
? [`- Release: ${focusRelease.name}`, `- Goal: ${focusRelease.goal}`, `- Status: ${focusRelease.status}`, `- Definition of done: ${focusRelease.definition_of_done.length ? focusRelease.definition_of_done.join('; ') : '—'}`].join('\n')
|
||||
: '- No release linked to this feature yet.',
|
||||
? [
|
||||
`- Phase: ${focusPhase?.title || '—'} (${focusPhase?.status || '—'})`,
|
||||
`- Release: ${focusRelease.name}`,
|
||||
`- Goal: ${focusRelease.goal}`,
|
||||
`- Status: ${focusRelease.status}`,
|
||||
`- Release readiness: ${readinessSummary}`,
|
||||
`- Remaining required features: ${remainingRequiredFeatures.length ? remainingRequiredFeatures.map((feature) => feature.title).join('; ') : 'None'}`,
|
||||
`- Definition of done: ${focusRelease.definition_of_done.length ? focusRelease.definition_of_done.join('; ') : '—'}`,
|
||||
].join('\n')
|
||||
: `- Phase: ${focusPhase?.title || '—'} (${focusPhase?.status || '—'})\n- No release linked to this feature 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.',
|
||||
'## Relevant Blockers',
|
||||
releaseBlockers.length
|
||||
? releaseBlockers.map((pulse) => `- ${formatDateTime(pulse.timestamp)} · ${getFeatureLabel(state.features, pulse.feature_id)} · ${pulse.message}`).join('\n')
|
||||
: '- No current blocker pulses for this feature or release.',
|
||||
'',
|
||||
'## Parking Lot / Forbidden Work',
|
||||
forbiddenWarnings.length
|
||||
? forbiddenWarnings.map((item) => `- ${item}`).join('\n')
|
||||
: '- Nothing parked or forbidden right now.',
|
||||
'',
|
||||
'## Recent Relevant Pulses',
|
||||
relatedPulses.length
|
||||
@@ -118,11 +197,11 @@ export const createAgentSessionPrompt = (
|
||||
'- Preserve working behavior and avoid needless rewrites.',
|
||||
'- Prefer the smallest shippable step with clear evidence.',
|
||||
'',
|
||||
'## Working Style',
|
||||
...preset.workingStyle.map((item) => `- ${item}`),
|
||||
'',
|
||||
'## Deliver back',
|
||||
'- What changed',
|
||||
'- Files touched',
|
||||
'- How you verified it',
|
||||
'- Any blocker or follow-up you intentionally parked',
|
||||
...preset.deliverBack.map((item) => `- ${item}`),
|
||||
].join('\n')
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export const createSeedState = (): AppState => ({
|
||||
one_line_pitch: 'A calm planning cockpit for AI-assisted product building.',
|
||||
description:
|
||||
'BuildPulse helps capture features, park distracting ideas, log progress as Pulse events, and export clean context for AI coding agents.',
|
||||
current_goal: 'Ship v0.3 with Phases, Releases, and clear release-readiness signals.',
|
||||
current_goal: 'Sharpen v0.4 handoff workflows so coding agents get the right context fast.',
|
||||
notes: 'First dogfood project: BuildPulse manages BuildPulse.',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
@@ -40,9 +40,9 @@ export const createSeedState = (): AppState => ({
|
||||
id: 'phase_structured_release_planning',
|
||||
title: 'Phase 3: Structured Release Planning',
|
||||
goal: 'Make releases concrete: what is required, what is forbidden, and how close the build is to ready.',
|
||||
status: 'active',
|
||||
status: 'done',
|
||||
order: 3,
|
||||
notes: 'This is the v0.3 step.',
|
||||
notes: 'v0.3 shipped with phases, releases, and readiness signals.',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
},
|
||||
@@ -50,9 +50,9 @@ export const createSeedState = (): AppState => ({
|
||||
id: 'phase_session_handoff',
|
||||
title: 'Phase 4: Session Handoff',
|
||||
goal: 'Give agents cleaner, more targeted context packages for implementation sessions.',
|
||||
status: 'upcoming',
|
||||
status: 'active',
|
||||
order: 4,
|
||||
notes: 'Planned v0.4 direction.',
|
||||
notes: 'Current v0.4 step: faster, more focused handoff workflows for coding sessions.',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
},
|
||||
@@ -118,13 +118,50 @@ export const createSeedState = (): AppState => ({
|
||||
'Local/cloud model router',
|
||||
'Session prompt generator',
|
||||
],
|
||||
status: 'shipped',
|
||||
notes: 'Shipped. Release planning structure is live; still no live integrations.',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
},
|
||||
{
|
||||
id: 'release_v040_focused_handoffs',
|
||||
phase_id: 'phase_session_handoff',
|
||||
name: 'v0.4 — Focused Session Handoffs',
|
||||
goal: 'Cut the friction between choosing a feature and giving a coding agent a sharp, minimal brief.',
|
||||
definition_of_done: [
|
||||
'Each feature detail exposes one-tap copy actions for OpenClaw, Claude Code, Codex, and a generic brief.',
|
||||
'Prompt generation includes phase, release, readiness, blockers, parking-lot, and forbidden-work guardrails.',
|
||||
'Preview/edit before copy is available with explicit INTENT pulse controls and mobile-safe actions.',
|
||||
],
|
||||
required_feature_ids: ['export_screen', 'feature_focused_handoff_shortcuts'],
|
||||
optional_feature_ids: ['pulse_log_screen'],
|
||||
forbidden_feature_titles: ['Live OpenClaw/Hermes Agent Status', 'WebSocket agent telemetry', 'GitHub / Gitea sync'],
|
||||
status: 'in_progress',
|
||||
notes: 'Keep this on planning structure only. No live integrations yet.',
|
||||
notes: 'v0.4.1 adds preview/edit before copy and cleaner INTENT controls; result capture comes next.',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
},
|
||||
],
|
||||
features: [
|
||||
{
|
||||
id: 'feature_focused_handoff_shortcuts',
|
||||
title: 'One-tap target-specific handoffs',
|
||||
description: 'Let operators copy a sharp AI handoff for OpenClaw, Claude Code, Codex, or a generic agent directly from feature detail.',
|
||||
column: 'now',
|
||||
priority: 'must',
|
||||
status: 'building',
|
||||
acceptance_criteria: [
|
||||
'Feature detail exposes separate copy actions for OpenClaw, Claude Code, Codex, and Generic.',
|
||||
'Preview/edit is available before copy, with target switching and reset-to-preset behavior.',
|
||||
'INTENT pulse logging is explicit from the preview flow instead of hidden behind a toggle.',
|
||||
],
|
||||
scope_notes: 'This is the v0.4 loop: quick-copy when you know the target, preview/edit when you want a safer brief, then let the agent work.',
|
||||
phase_id: 'phase_session_handoff',
|
||||
release_id: 'release_v040_focused_handoffs',
|
||||
release_role: 'required',
|
||||
created_at: seedDate,
|
||||
updated_at: seedDate,
|
||||
},
|
||||
{
|
||||
id: 'feature_plan_screen',
|
||||
title: 'Feature Plan screen',
|
||||
|
||||
+1392
-6
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user