Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cd349a5
[recipes] Add Repo Learning Coach recipe (#175)
justfinethanku Apr 13, 2026
3285d04
fix link to recipes/content-fingerprint-dedup (#167)
markhu Apr 13, 2026
bd12888
[dashboards] Add Workflow kanban board with status tracking (#146)
csakzozo Apr 13, 2026
06b5b6c
[skills] Add autodream-brain-sync skill for cross-client memory shari…
rumbitopi Apr 13, 2026
0ed8a64
[recipes] Clean up Life Engine: add state table, harden permissions, …
air-captain Apr 13, 2026
48c6d16
[recipes] Add Claude Code scheduled task variant for daily digest (#125)
matthallett1 Apr 13, 2026
f3eae3c
[recipes] Vercel + Neon + Telegram alternative architecture (#37)
geoff-price Apr 13, 2026
8c5d506
[recipes] ChatGPT import v2: multi-thought knowledge extraction (#171)
dgourlay Apr 13, 2026
979e96d
[recipes] Local Ollama embeddings, zero-cost alternative to OpenRoute…
snapsynapse Apr 13, 2026
35705bf
[recipes] Fix claudeception: single-line YAML descriptions (#150)
jaredirish Apr 13, 2026
12a9e8b
[extensions] Fix professional-crm Accept handling for Streamable HTTP…
Promithius-DR Apr 13, 2026
de3b350
[recipes] Add adaptive capture classification with confidence gating …
mjh756 Apr 13, 2026
1da39c9
[extensions] Add update_professional_contact tool to CRM extension (#…
swhitt Apr 13, 2026
465c214
[docs] Fix pre-existing markdownlint errors across 15 files (#161)
alanshurafa Apr 13, 2026
19a1c42
[recipes] Infographic Generator: turn research docs into visual infog…
jaredirish Apr 13, 2026
84e7f62
[recipes] Add OB-Graph knowledge graph layer (#158)
justfinethanku Apr 13, 2026
b9ef650
[primitives] Fix Cursor MCP connection docs (#123)
matthallett1 Apr 13, 2026
c651588
[skills] Add weekly signal diff skill pack (#178)
justfinethanku Apr 14, 2026
2c933e4
[skills] Add install prompt to weekly signal diff README (#179)
justfinethanku Apr 14, 2026
0a970ef
[recipes] Add work operating model activation workflow (#180)
justfinethanku Apr 14, 2026
6c673f0
[recipes] Add Bring Your Own Context composition recipe (#181)
justfinethanku Apr 16, 2026
500076e
[repo] Sweep fix-now backlog issues (#185)
justfinethanku Apr 16, 2026
7679129
[recipes] Operational monitoring and brain health
alanshurafa Apr 6, 2026
e7a9bff
[recipes] Fix REVIEW-HIGH-1 (SQL): guard optional views behind to_reg…
alanshurafa Apr 18, 2026
fabcb3e
[recipes] Fix REVIEW-HIGH-1 (README): correct companion schema paths
alanshurafa Apr 18, 2026
39bbe93
[recipes] Fix REVIEW-MEDIUM-1: filter restricted thoughts from previe…
alanshurafa Apr 18, 2026
96377dc
[docs] Fix pre-existing markdownlint errors across 8 files
alanshurafa Apr 22, 2026
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
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,15 @@ Standalone capabilities that make your Open Brain smarter.
| ------ | ------------ | ----------- |
| [Auto-Capture Protocol](recipes/auto-capture/) | Stores ACT NOW items and session summaries in Open Brain at session close using the reusable Auto-Capture skill | [@jaredirish](https://github.com/jaredirish) |
| [Panning for Gold](recipes/panning-for-gold/) | Mine brain dumps and voice transcripts for actionable ideas — battle-tested across 13+ sessions | [@jaredirish](https://github.com/jaredirish) |
| [Claudeception](recipes/claudeception/) | Self-improving system that creates new skills from work sessions — skills that create other skills | [@jaredirish](https://github.com/jaredirish) |
| [Aiception (formerly Claudeception)](recipes/claudeception/) | Self-improving system that creates new skills from work sessions — skills that create other skills | [@jaredirish](https://github.com/jaredirish) |
| [Schema-Aware Routing](recipes/schema-aware-routing/) | LLM-powered routing that distributes unstructured text across multiple database tables | [@claydunker-yalc](https://github.com/claydunker-yalc) |
| [Fingerprint Dedup Backfill](recipes/fingerprint-dedup-backfill/) | Backfill content fingerprints and safely remove duplicate thoughts | [@alanshurafa](https://github.com/alanshurafa) |
| [Source Filtering](recipes/source-filtering/) | Filter thoughts by source and backfill missing metadata for early imports | [@matthallett1](https://github.com/matthallett1) |
| [Life Engine](recipes/life-engine/) | Self-improving personal assistant — calendar, habits, health, proactive briefings via Telegram or Discord | [@justfinethanku](https://github.com/justfinethanku) |
| [Life Engine Video](recipes/life-engine-video/) | Add-on that renders Life Engine briefings as short animated videos with voiceover | [@justfinethanku](https://github.com/justfinethanku) |
| [Daily Digest](recipes/daily-digest/) | Automated daily summary of recent thoughts delivered via email or Slack | OB1 Team |
| [Bring Your Own Context](recipes/bring-your-own-context/) | Portable context workflow that packages extraction prompts, profile generation, and remote MCP deployment into one entrypoint | [@jonathanedwards](https://github.com/jonathanedwards) |
| [Work Operating Model Activation](recipes/work-operating-model-activation/) | Conversation-first workflow that turns tacit work patterns into structured Open Brain records and agent-ready operating files | [@jonathanedwards](https://github.com/jonathanedwards) |
| [Research-to-Decision Workflow](recipes/research-to-decision-workflow/) | Composition recipe that chains canonical skills into operator and investor research, synthesis, meeting, and memo workflows | [@NateBJones](https://github.com/NateBJones) |

### [`/skills`](skills/) — Agent Skills
Expand All @@ -105,7 +107,8 @@ Plain-text skill packs you can drop into Claude Code, Codex, or other AI clients
| [Research Synthesis Skill Pack](skills/research-synthesis/) | Synthesizes source sets into findings, contradictions, confidence markers, and next questions | [@NateBJones](https://github.com/NateBJones) |
| [Meeting Synthesis Skill Pack](skills/meeting-synthesis/) | Converts meeting notes or transcripts into decisions, action items, risks, and follow-up artifacts | [@NateBJones](https://github.com/NateBJones) |
| [Panning for Gold Skill Pack](skills/panning-for-gold/) | Turns brain dumps and transcripts into evaluated idea inventories | [@jaredirish](https://github.com/jaredirish) |
| [Claudeception Skill Pack](skills/claudeception/) | Extracts reusable lessons from work sessions into new skills | [@jaredirish](https://github.com/jaredirish) |
| [Aiception Skill Pack (formerly Claudeception)](skills/claudeception/) | Extracts reusable lessons from work sessions into new skills | [@jaredirish](https://github.com/jaredirish) |
| [Work Operating Model Skill Pack](skills/work-operating-model/) | Runs a five-layer elicitation interview and saves the approved operating model into Open Brain | [@jonathanedwards](https://github.com/jonathanedwards) |

### [`/dashboards`](dashboards/) — Frontend Templates

Expand Down
1 change: 1 addition & 0 deletions dashboards/open-brain-dashboard-next/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules/
.env.local.example
next-env.d.ts
tsconfig.tsbuildinfo
.vercel
45 changes: 43 additions & 2 deletions dashboards/open-brain-dashboard-next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ A full-featured web dashboard for your Open Brain second brain. Browse, search,

## What It Does

Provides 8 pages for managing your thoughts:
Provides 9 pages for managing your thoughts:

| Page | Description |
|------|-------------|
| **Dashboard** | Stats overview (total thoughts, type distribution, top topics), recent activity, quick capture |
| **Dashboard** | Stats overview (total thoughts, type distribution, top topics), recent activity, quick capture, workflow summary widget |
| **Workflow** | Kanban board for tasks and ideas with drag-and-drop status management (New → Planning → Active → Review → Done → Archived) |
| **Browse** | Paginated thought table with filters for type, source, and importance |
| **Detail** | Full thought view with inline editing, delete, linked reflections, and related connections |
| **Search** | Semantic (vector similarity) and full-text search with match scores and pagination |
Expand Down Expand Up @@ -103,6 +104,43 @@ When working correctly:
- **Add to Brain** auto-routes short text (< 500 chars, single paragraph) to single capture, and long/structured text to extraction with dry-run preview
- **Detail page** shows full thought content with metadata, inline edit for content/type/importance, and linked reflections

## Workflow Board

The Workflow page adds a visual kanban board for managing `task` and `idea` thoughts through status stages.

### Features

- **Drag-and-drop** between status columns using @dnd-kit (touch-friendly with 200ms hold delay)
- **Collapsible columns** — click the arrow to collapse any column to a slim vertical bar (persisted in localStorage)
- **Auto-adjusting widths** — expanded columns share available space equally, no horizontal scrollbar
- **Inline editing** — tap a card to open the edit modal (status, priority, type, content)
- **Priority dots** — click to change priority (Critical/High/Medium/Low mapped from importance 0-100)
- **Dashboard widget** — summary of active workflow items on the main dashboard
- **Mobile-first** — responsive layout, pinch-to-zoom enabled, full-screen edit modal on small screens

### Status Flow

```
New → Planning → Active → Review → Done → (Archived)
```

Cards auto-archive from Done after 30 days. Archived cards are hidden by default (toggle with "Show archived").

### Database Requirements

The Workflow board requires two additional columns on the `thoughts` table. See the [workflow-status schema](../../schemas/workflow-status/) for the migration SQL.

### MCP Integration

The `progress_task` tool in the Open Brain MCP server allows AI assistants to update task status and priority conversationally:

```
"Move the API redesign task to active"
"Set priority on thought 42 to high"
```

When a new task or idea is captured, the MCP server auto-assigns `status: "new"`.

## REST API Endpoints Required

The dashboard calls these endpoints on your Open Brain REST API:
Expand All @@ -121,6 +159,8 @@ The dashboard calls these endpoints on your Open Brain REST API:
| `/ingest` | POST | Smart ingest (extraction) |
| `/ingestion-jobs` | GET | Ingest page (job history) |
| `/duplicates` | GET | Duplicates page |
| `/thoughts?type=task` | GET | Workflow board (filtered by type) |
| `/thought/:id` | PUT | Workflow board (status/priority updates) |

> [!NOTE]
> If your Open Brain instance doesn't have all these endpoints (e.g., no smart-ingest or duplicates), those pages will show errors but the core pages (dashboard, browse, search, detail) will still work.
Expand Down Expand Up @@ -154,6 +194,7 @@ No API key is stored in environment variables or exposed to the browser.
- **React 19** with TypeScript
- **Tailwind CSS 4** (dark theme)
- **iron-session 8** (encrypted cookies)
- **@dnd-kit** (drag-and-drop for workflow board)
- Zero external runtime dependencies beyond these

## Troubleshooting
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextRequest, NextResponse } from "next/server";
import { requireSession, AuthError } from "@/lib/auth";
import { deleteThought } from "@/lib/api";

export async function POST(request: NextRequest) {
let apiKey: string;
try {
({ apiKey } = await requireSession());
} catch (err) {
if (err instanceof AuthError)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
throw err;
}

try {
const body = await request.json();
const { thoughtId } = body;

if (!thoughtId || typeof thoughtId !== "number") {
return NextResponse.json(
{ error: "thoughtId (number) is required" },
{ status: 400 }
);
}

await deleteThought(apiKey, thoughtId);
return NextResponse.json({ success: true });
} catch (err) {
return NextResponse.json(
{ error: err instanceof Error ? err.message : "Delete failed" },
{ status: 500 }
);
}
}
40 changes: 40 additions & 0 deletions dashboards/open-brain-dashboard-next/app/api/kanban/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from "next/server";
import { requireSession, AuthError } from "@/lib/auth";
import { getSession } from "@/lib/auth";
import { fetchKanbanThoughts } from "@/lib/api";

export async function GET(request: NextRequest) {
let apiKey: string;
try {
({ apiKey } = await requireSession());
} catch (err) {
if (err instanceof AuthError)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
throw err;
}

const session = await getSession();
const excludeRestricted = session.restrictedUnlocked !== true;
const includeArchived =
request.nextUrl.searchParams.get("archived") === "true";

const statusFilter = includeArchived
? "new,planning,active,review,done,archived"
: "new,planning,active,review,done";

try {
const thoughts = await fetchKanbanThoughts(apiKey, {
status: statusFilter,
exclude_restricted: excludeRestricted,
});
return NextResponse.json({ thoughts });
} catch (err) {
return NextResponse.json(
{
error:
err instanceof Error ? err.message : "Failed to fetch kanban data",
},
{ status: 500 }
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NextRequest, NextResponse } from "next/server";
import { requireSession, AuthError } from "@/lib/auth";
import { updateThought } from "@/lib/api";

const VALID_STATUSES = [
"new",
"planning",
"active",
"review",
"done",
"archived",
];

export async function POST(request: NextRequest) {
let apiKey: string;
try {
({ apiKey } = await requireSession());
} catch (err) {
if (err instanceof AuthError)
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
throw err;
}

try {
const body = await request.json();
const { thoughtId, status, importance, content, type } = body;

if (!thoughtId || typeof thoughtId !== "number") {
return NextResponse.json(
{ error: "thoughtId (number) is required" },
{ status: 400 }
);
}

if (status !== undefined && status !== null && !VALID_STATUSES.includes(status)) {
return NextResponse.json(
{ error: `Invalid status. Must be one of: ${VALID_STATUSES.join(", ")}` },
{ status: 400 }
);
}

const updates: Record<string, unknown> = {};
if (status !== undefined) updates.status = status;
if (importance !== undefined) updates.importance = importance;
if (content !== undefined) updates.content = content;
if (type !== undefined) updates.type = type;

if (Object.keys(updates).length === 0) {
return NextResponse.json(
{ error: "No fields to update" },
{ status: 400 }
);
}

const result = await updateThought(apiKey, thoughtId, updates);
return NextResponse.json(result);
} catch (err) {
return NextResponse.json(
{ error: err instanceof Error ? err.message : "Update failed" },
{ status: 500 }
);
}
}
15 changes: 15 additions & 0 deletions dashboards/open-brain-dashboard-next/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,18 @@ body {
background: var(--color-violet-glow);
color: var(--color-text-primary);
}

/* Phone landscape: hide desktop sidebar, keep mobile topbar, remove sidebar margin */
@media (orientation: landscape) and (max-height: 600px) {
aside {
display: none !important;
}
main {
margin-left: 0 !important;
padding-top: 48px !important;
}
/* Force mobile topbar visible in landscape */
.md\:hidden {
display: flex !important;
}
}
21 changes: 21 additions & 0 deletions dashboards/open-brain-dashboard-next/app/kanban/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { requireSessionOrRedirect } from "@/lib/auth";
import { KanbanBoard } from "@/components/KanbanBoard";

export const dynamic = "force-dynamic";


export default async function KanbanPage() {
await requireSessionOrRedirect();

return (
<div className="space-y-4">
<div>
<h1 className="text-2xl font-semibold mb-1">Workflow</h1>
<p className="text-text-secondary text-sm">
Track tasks and ideas through your workflow
</p>
</div>
<KanbanBoard />
</div>
);
}
9 changes: 5 additions & 4 deletions dashboards/open-brain-dashboard-next/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Sidebar } from "@/components/Sidebar";
import { SidebarShell } from "@/components/SidebarShell";

const geistSans = Geist({
variable: "--font-geist-sans",
Expand All @@ -27,11 +27,12 @@ export default function RootLayout({
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
suppressHydrationWarning
>
<body className="min-h-screen flex bg-bg-primary text-text-primary">
<Sidebar />
<main className="flex-1 ml-56 min-h-screen">
<div className="max-w-6xl mx-auto px-8 py-8">
<SidebarShell />
<main className="flex-1 md:ml-56 min-h-screen pt-12 md:pt-0">
<div className="max-w-6xl mx-auto px-4 py-4 md:px-8 md:py-8">
{children}
</div>
</main>
Expand Down
3 changes: 3 additions & 0 deletions dashboards/open-brain-dashboard-next/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { fetchStats, fetchThoughts } from "@/lib/api";
import { requireSessionOrRedirect, getSession } from "@/lib/auth";
import { StatsWidget } from "@/components/StatsWidget";
import { KanbanSummary } from "@/components/KanbanSummary";
import { ThoughtCard } from "@/components/ThoughtCard";
import { AddToBrain } from "@/components/AddToBrain";

Expand Down Expand Up @@ -43,6 +44,8 @@ export default async function DashboardPage() {

<StatsWidget stats={stats} />

<KanbanSummary />

{/* Add to Brain */}
<div>
<h2 className="text-lg font-medium mb-1">Add to Brain</h2>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { notFound } from "next/navigation";
import { notFound, redirect } from "next/navigation";
import {
fetchThought,
updateThought,
Expand Down Expand Up @@ -77,6 +77,7 @@ export default async function ThoughtDetailPage({
"use server";
const { apiKey } = await requireSessionOrRedirect();
await deleteThought(apiKey, thoughtId);
redirect("/thoughts");
}

return (
Expand All @@ -86,6 +87,11 @@ export default async function ThoughtDetailPage({
<div>
<div className="flex items-center gap-3 mb-2 flex-wrap">
<TypeBadge type={thought.type} />
{thought.status && (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium border bg-violet/15 text-violet border-violet/20">
{thought.status}
</span>
)}
<span className="text-xs text-text-muted font-mono">
ID: {thought.id}
</span>
Expand Down
Loading
Loading