From 1f8ac2cf349965c6f456aa5314a27a2e7c2757cd Mon Sep 17 00:00:00 2001 From: gagan Date: Thu, 18 Jun 2026 22:56:43 -0700 Subject: [PATCH] =?UTF-8?q?feat(bg-tasks):=20coding-from-meetings=20?= =?UTF-8?q?=E2=80=94=20auto-implement=20coding=20action=20items=20(#630)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(bg-tasks): coding-from-meetings — auto-implement coding action items A background-task flavor that watches for meeting notes, scans them for actionable coding items, and autonomously implements them in isolated git worktrees, summarizing results in the task's index.md. - Emit `meeting.notes_ready` when Fireflies/Granola first write a meeting note - Add optional `projectId` to BackgroundTask (pins a coding task to a repo) - New `launch-code-task` builtin tool: per group of items, create a worktree-isolated, yolo, direct code session, wrap the prompt in an autonomous scaffold, run async, and finalize a per-session row in index.md - Group code sessions under their meeting heading in index.md - Summary from the code agent's `## Summary` section; file counts from `git diff` vs the worktree fork point (counts committed work, not just dirty) - Guardrails: self-heal projectId across runs, cap launches per run, and bar the bg-task agent from managing/spawning tasks - UI: "View available templates" -> Coding-from-meetings preset (repo picker, prefilled trigger + instructions) See plan.md for the full design. * let the copilot able to configure a coding background agent * Delete plan.md --------- Co-authored-by: Arjun <6592213+arkml@users.noreply.github.com> --- apps/x/apps/main/src/ipc.ts | 1 + .../renderer/src/components/bg-tasks-view.tsx | 179 +++++++++- .../core/src/application/lib/builtin-tools.ts | 93 ++++- .../core/src/background-tasks/agent.ts | 22 +- .../src/background-tasks/code-sessions.ts | 333 ++++++++++++++++++ .../core/src/background-tasks/fileops.ts | 3 + .../core/src/background-tasks/runner.ts | 48 ++- .../core/src/code-mode/git/service.ts | 61 ++++ .../core/src/knowledge/granola/sync.ts | 9 + .../core/src/knowledge/meeting-events.ts | 37 ++ .../core/src/knowledge/sync_fireflies.ts | 10 + apps/x/packages/shared/src/background-task.ts | 9 + apps/x/packages/shared/src/ipc.ts | 1 + 13 files changed, 795 insertions(+), 11 deletions(-) create mode 100644 apps/x/packages/core/src/background-tasks/code-sessions.ts create mode 100644 apps/x/packages/core/src/knowledge/meeting-events.ts diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index 9f7233b0..c7192143 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -1606,6 +1606,7 @@ export function setupIpcHandlers() { name: args.name, instructions: args.instructions, ...(args.triggers ? { triggers: args.triggers } : {}), + ...(args.projectId ? { projectId: args.projectId } : {}), ...(args.model ? { model: args.model } : {}), ...(args.provider ? { provider: args.provider } : {}), }); diff --git a/apps/x/apps/renderer/src/components/bg-tasks-view.tsx b/apps/x/apps/renderer/src/components/bg-tasks-view.tsx index 0641bc13..c48df79e 100644 --- a/apps/x/apps/renderer/src/components/bg-tasks-view.tsx +++ b/apps/x/apps/renderer/src/components/bg-tasks-view.tsx @@ -4,6 +4,7 @@ import { ListChecks, Play, Square, Loader2, Trash2, Plus, X, AlertCircle, Repeat, Clock, Zap, ChevronLeft, ChevronDown, ChevronRight, Pencil, Check, PanelRightClose, PanelRightOpen, Sparkles, + Code2, FolderOpen, LayoutTemplate, } from 'lucide-react' import type { z } from 'zod' import type { BackgroundTask, BackgroundTaskSummary, Triggers } from '@x/shared/dist/background-task.js' @@ -271,7 +272,16 @@ function TriggersEditor({ // New Task dialog // --------------------------------------------------------------------------- -type DialogMode = 'describe' | 'manual' +type DialogMode = 'describe' | 'manual' | 'templates' | 'coding' + +// Prefills for the "Coding from meetings" preset. +const CODING_PRESET = { + name: 'Implement coding items from meetings', + instructions: `After a meeting's notes are ready, scan them for coding action items (bugs to fix, features to build, concrete changes requested) for me or my team. + +Conservatively implement the clearly-scoped, self-contained ones in the configured repo using the launch-code-task tool — group related items into one session, split unrelated ones. Note ambiguous, large/architectural, or other-repo items as "needs review" instead of coding them. If nothing is actionable, do nothing.`, + eventMatchCriteria: `A meeting's notes or transcript just became available (engineering standup, planning, sprint, or technical discussion) that may contain coding action items, bugs to fix, or features to build.`, +} function NewTaskDialog({ open, @@ -295,6 +305,9 @@ function NewTaskDialog({ const [name, setName] = useState('') const [instructions, setInstructions] = useState('') const [triggers, setTriggers] = useState(undefined) + const [projectId, setProjectId] = useState(undefined) + const [projectName, setProjectName] = useState(undefined) + const [addingProject, setAddingProject] = useState(false) const [submitting, setSubmitting] = useState(false) useEffect(() => { @@ -304,11 +317,64 @@ function NewTaskDialog({ setName('') setInstructions('') setTriggers(undefined) + setProjectId(undefined) + setProjectName(undefined) } }, [open, copilotEnabled]) + // Switch into the coding preset: prefill name/instructions/trigger once. + const enterCodingMode = () => { + setMode('coding') + setName(CODING_PRESET.name) + setInstructions(CODING_PRESET.instructions) + setTriggers({ eventMatchCriteria: CODING_PRESET.eventMatchCriteria }) + } + + const pickRepo = async () => { + setAddingProject(true) + try { + const res = await window.ipc.invoke('dialog:openDirectory', { title: 'Choose the repository for this task' }) + const dir = res.path + if (!dir) return + const added = await window.ipc.invoke('codeProject:add', { path: dir }) + if (!added.git?.isGitRepo) { + toast('That folder is not a git repository — coding tasks need one.', 'error') + return + } + setProjectId(added.project.id) + setProjectName(added.project.name) + } catch (err) { + toast(err instanceof Error ? err.message : String(err), 'error') + } finally { + setAddingProject(false) + } + } + const canSubmitDescribe = description.trim().length > 0 && !submitting const canSubmitManual = name.trim().length > 0 && instructions.trim().length > 0 && !submitting + const canSubmitCoding = name.trim().length > 0 && instructions.trim().length > 0 && !!projectId && !submitting + + const submitCoding = async () => { + if (!canSubmitCoding) return + setSubmitting(true) + try { + const result = await window.ipc.invoke('bg-task:create', { + name: name.trim(), + instructions: instructions.trim(), + ...(triggers ? { triggers } : {}), + ...(projectId ? { projectId } : {}), + }) + if (result.success && result.slug) { + onCreated(result.slug) + } else { + toast(result.error ?? 'Failed to create task', 'error') + } + } catch (err) { + toast(err instanceof Error ? err.message : String(err), 'error') + } finally { + setSubmitting(false) + } + } const submitDescribe = () => { if (!canSubmitDescribe || !onCreateWithCopilot) return @@ -359,7 +425,116 @@ function NewTaskDialog({ - {mode === 'describe' ? ( + {(mode === 'describe' || mode === 'manual') && ( + + )} + + {mode === 'templates' ? ( + <> +
+ {[ + { + id: 'coding-from-meetings', + title: 'Coding from meetings', + description: "When a meeting's notes are ready, scan them for coding action items and auto-implement them in a repo — each on its own isolated branch, with a summary.", + icon: Code2, + onSelect: enterCodingMode, + }, + ].map(preset => ( + + ))} +
+ +
+ + +
+ + ) : mode === 'coding' ? ( + <> +
+
+ + {projectName ? ( +
+ + + {projectName} + + +
+ ) : ( + + )} +

+ Code changes run full-auto in an isolated git worktree — your working checkout is never touched. +

+
+
+ + setName(e.target.value)} /> +
+
+ +