Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
200583d
feat(core): add all wow features infrastructure types
Manish0Jha Apr 7, 2026
a313463
feat(storage): add wow features tables to SchemaManager
Manish0Jha Apr 7, 2026
0382b36
feat(storage): implement SqliteDeadEndStore, GetSessionObservations, …
Manish0Jha Apr 7, 2026
f3381e2
fix(agents): real cron parsing, dead-end persistence, prompt migration
Manish0Jha Apr 7, 2026
42b4610
feat: wire infrastructure, implement DecisionChainAgent
Manish0Jha Apr 7, 2026
aa41851
fix: address code review findings across foundation
Manish0Jha Apr 7, 2026
a6280cf
feat: add IAlertStore interface and SqliteAlertStore implementation
Manish0Jha Apr 7, 2026
0f8dd41
feat(agents): implement DejaVuAgent with file overlap matching
Manish0Jha Apr 7, 2026
59aee58
feat(api): add AlertChannel, alert endpoints, wire DejaVuAgent
Manish0Jha Apr 7, 2026
1aa1b24
feat(cli): add alerts command with dismiss and history subcommands
Manish0Jha Apr 7, 2026
4bf0129
feat(dashboard): add Alerts page, AlertBanner, and navigation
Manish0Jha Apr 7, 2026
dd13a04
fix: address Déjà Vu code review findings
Manish0Jha Apr 7, 2026
8bd0c5f
feat: add Session Storytelling — ISessionStore, SqliteSessionStore, S…
Manish0Jha Apr 7, 2026
c84b18d
feat: complete Session Storytelling — API, CLI, dashboard
Manish0Jha Apr 7, 2026
c2f3c38
fix: address Session Storytelling review findings
Manish0Jha Apr 7, 2026
34b619a
feat: add Decision Replay — query service, API, CLI, dashboard
Manish0Jha Apr 7, 2026
535c1ac
fix: address Decision Replay review findings
Manish0Jha Apr 7, 2026
fd8affe
feat: add Blast Radius Prediction — calculator, API, CLI, dashboard
Manish0Jha Apr 7, 2026
507b339
fix: address Blast Radius review findings
Manish0Jha Apr 7, 2026
dc4ef2b
feat: add Developer Growth Tracker — store, agent, API, CLI, dashboard
Manish0Jha Apr 7, 2026
4244ee8
fix: address Growth Tracker review findings
Manish0Jha Apr 7, 2026
d9e3652
docs: add single-click packaging design spec
Manish0Jha Apr 8, 2026
40d23de
docs: add single-click packaging implementation plan
Manish0Jha Apr 8, 2026
3d6d45f
feat(packaging): scaffold Electron tray app with npm workspaces
Manish0Jha Apr 8, 2026
ac88bd8
feat(packaging): add platform path resolution module
Manish0Jha Apr 8, 2026
db330b3
feat(packaging): add OS notification helpers
Manish0Jha Apr 8, 2026
36c19b8
feat(packaging): add daemon health monitor with TDD
Manish0Jha Apr 8, 2026
7ef79c9
feat(packaging): add daemon lifecycle manager with auto-restart
Manish0Jha Apr 8, 2026
070e42e
feat(packaging): add first-run bootstrap orchestrator
Manish0Jha Apr 8, 2026
7ab0d3f
feat(packaging): add Electron main entry with tray icon and context menu
Manish0Jha Apr 8, 2026
9906a1f
feat(packaging): add CLI/tray coordination via tray.lock + stopped se…
Manish0Jha Apr 8, 2026
a2becc3
feat(packaging): add electron-builder config for Windows/macOS/Linux
Manish0Jha Apr 8, 2026
8943a9e
feat(packaging): add Homebrew, winget, and APT package manifests
Manish0Jha Apr 8, 2026
c644678
feat(packaging): add CI/CD for Electron build + package manager publi…
Manish0Jha Apr 8, 2026
aedeec8
fix(packaging): address code review findings
Manish0Jha Apr 8, 2026
8c3ce26
fix(packaging): address critical and high review findings
Manish0Jha Apr 8, 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
165 changes: 165 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
name: Package & Publish

on:
workflow_run:
workflows: ["Build & Test"]
types: [completed]

jobs:
check:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success'
outputs:
is_tag: ${{ steps.check_tag.outputs.is_tag }}
steps:
- id: check_tag
run: |
# workflow_run.head_branch contains the tag name for tag-triggered runs
# Build & Test triggers on tags: ['v*'], so check if the triggering ref was a tag
if [[ "${{ github.event.workflow_run.event }}" == "push" ]] && \
[[ "${{ github.event.workflow_run.head_branch }}" =~ ^v[0-9] ]]; then
echo "is_tag=true" >> $GITHUB_OUTPUT
else
echo "is_tag=false" >> $GITHUB_OUTPUT
fi

electron-build:
needs: check
strategy:
matrix:
include:
- os: windows-latest
rid: win-x64
electron-args: --win --x64
artifact-pattern: "*.exe"
- os: macos-latest
rid: osx-x64
electron-args: --mac --x64
artifact-pattern: "*.dmg"
- os: ubuntu-latest
rid: linux-x64
electron-args: --linux --x64
artifact-pattern: "*.deb"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"

- uses: actions/download-artifact@v4
with:
name: devbrain-${{ matrix.rid }}
path: packages/tray/resources/bin/
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/download-artifact@v4
with:
name: dashboard-dist
path: packages/tray/resources/wwwroot/
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Extract .NET binaries (Unix)
if: runner.os != 'Windows'
run: |
cd packages/tray/resources/bin
for f in *.tar.gz; do [ -f "$f" ] && tar xzf "$f" && rm "$f"; done
chmod +x devbrain devbrain-daemon 2>/dev/null || true

- name: Extract .NET binaries (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
cd packages/tray/resources/bin
for f in *.zip; do [ -f "$f" ] && 7z x "$f" -y && rm "$f"; done

- name: Install dependencies
run: cd packages/tray && npm ci

- name: Build TypeScript
run: cd packages/tray && npm run build

- name: Build Electron package
run: cd packages/tray && npx electron-builder ${{ matrix.electron-args }} --publish never

- uses: actions/upload-artifact@v4
with:
name: electron-${{ matrix.rid }}
path: packages/tray/release/${{ matrix.artifact-pattern }}

publish-homebrew:
needs: [check, electron-build]
if: needs.check.outputs.is_tag == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update Homebrew formula
uses: Justintime50/homebrew-releaser@v1
with:
homebrew_owner: devbrain
homebrew_tap: homebrew-tap
formula_folder: Formula
github_token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
commit_owner: devbrain-bot
commit_email: bot@devbrain.dev
install: |
bin.install "devbrain"
bin.install "devbrain-daemon"
test: |
assert_match "devbrain", shell_output("#{bin}/devbrain --version")

publish-winget:
needs: [check, electron-build]
if: needs.check.outputs.is_tag == 'true'
runs-on: windows-latest
steps:
- uses: actions/download-artifact@v4
with:
name: electron-win-x64
path: installer/

- name: Submit to winget
uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: DevBrain.DevBrain
installers-regex: '\.exe$'
token: ${{ secrets.WINGET_TOKEN }}

publish-apt:
needs: [check, electron-build]
if: needs.check.outputs.is_tag == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/download-artifact@v4
with:
name: electron-linux-x64
path: deb-package/

- name: Setup GPG
run: |
GPG_KEY_FILE="$(mktemp)"
echo "${{ secrets.APT_GPG_PRIVATE_KEY }}" > "$GPG_KEY_FILE"
chmod 600 "$GPG_KEY_FILE"
gpg --batch --import "$GPG_KEY_FILE"
rm -f "$GPG_KEY_FILE"

- name: Update APT repository
run: |
mkdir -p apt-repo/pool/main
cp deb-package/*.deb apt-repo/pool/main/
cd apt-repo
dpkg-scanpackages pool/main /dev/null | gzip -9c > Packages.gz
apt-ftparchive release . > Release
gpg --batch --yes --armor --detach-sign -o Release.gpg Release
gpg --batch --yes --clearsign -o InRelease Release

- name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./apt-repo
destination_dir: apt
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ wwwroot/
.env.local
*.pem
docs/superpowers/
packages/tray/dist/
packages/tray/node_modules/
packages/tray/resources/bin/
packages/tray/resources/wwwroot/
20 changes: 20 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ tests/
dashboard/ # React + TypeScript SPA (Vite), 7 pages
```

## Build & Toolchain

When working on Rust projects on Windows, prefer MSVC toolchain in release mode. Debug mode causes Wasmtime stack overflows. Avoid GNU toolchain as it requires gcc/dlltool which are often missing. Always check toolchain before attempting builds.

## Build & Test Commands

```bash
Expand All @@ -38,6 +42,10 @@ dotnet run --project src/DevBrain.Api/ # Run daemon (localhost:37800)
dotnet run --project src/DevBrain.Cli/ -- status # Run CLI
```

## Development Workflow

After implementing any feature, run a full build/compile check before committing. Do not batch multiple features without intermediate build verification. For Rust: `cargo check` after each module. For .NET: `dotnet build` after each project change.

## Architecture Rules

**Dependency direction is strictly enforced by project references:**
Expand Down Expand Up @@ -135,6 +143,18 @@ refactor: extract graph traversal into helper
- **LLM daily counter** resets at midnight UTC but has no persistence across daemon restarts.
- **No ICaptureAdapter interface** in Core — adapter contract is defined inline in the Capture project.

## Code Quality

For code reviews: always check for division-by-zero, SQL injection, race conditions, bounds validation, and correct API signatures before marking a feature complete. Do not wait for user to catch these in review.

## Deployment

When working with Azure deployments, always verify: 1) Environment variables are set before deploy, 2) Database migrations are idempotent, 3) DNS and health check endpoints are configured. Never assume previous deploy state is clean.

## Windows Development

When running background services or daemons on Windows, use UseShellExecute approach rather than stream draining for process management. Always kill processes on conflicting ports before restart.

## Security Notes

- Daemon binds to `127.0.0.1` ONLY — never `0.0.0.0`
Expand Down
12 changes: 12 additions & 0 deletions dashboard/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Navigation from './components/Navigation';
import AlertBanner from './components/AlertBanner';
import Timeline from './pages/Timeline';
import Briefings from './pages/Briefings';
import DeadEnds from './pages/DeadEnds';
Expand All @@ -9,11 +10,17 @@ import SettingsPage from './pages/SettingsPage';
import Health from './pages/Health';
import Database from './pages/Database';
import Setup from './pages/Setup';
import Alerts from './pages/Alerts';
import Sessions from './pages/Sessions';
import Replay from './pages/Replay';
import BlastRadius from './pages/BlastRadius';
import Growth from './pages/Growth';

export default function App() {
return (
<BrowserRouter>
<Navigation />
<AlertBanner />
<main>
<Routes>
<Route path="/" element={<Timeline />} />
Expand All @@ -25,6 +32,11 @@ export default function App() {
<Route path="/health" element={<Health />} />
<Route path="/database" element={<Database />} />
<Route path="/setup" element={<Setup />} />
<Route path="/alerts" element={<Alerts />} />
<Route path="/sessions" element={<Sessions />} />
<Route path="/replay" element={<Replay />} />
<Route path="/blast-radius" element={<BlastRadius />} />
<Route path="/growth" element={<Growth />} />
</Routes>
</main>
</BrowserRouter>
Expand Down
128 changes: 128 additions & 0 deletions dashboard/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,95 @@ export interface DeadEnd {
createdAt: string;
}

// DejaVuAlert model
export interface DejaVuAlert {
id: string;
threadId: string;
matchedDeadEndId: string;
confidence: number;
message: string;
strategy: string;
dismissed: boolean;
createdAt: string;
}

// BlastRadius model
export interface BlastRadiusEntry {
filePath: string;
riskScore: number;
chainLength: number;
reason: string;
linkedDecisionId?: string;
}

export interface BlastRadius {
sourceFile: string;
affectedFiles: BlastRadiusEntry[];
deadEndsAtRisk: string[];
summary?: string;
generatedAt: string;
}

// DecisionChain model
export interface DecisionStep {
observationId: string;
summary: string;
timestamp: string;
stepType: string;
filesInvolved: string[];
}

export interface DecisionChain {
id: string;
rootNodeId: string;
narrative: string;
steps: DecisionStep[];
generatedAt: string;
}

// SessionSummary model
export interface SessionSummary {
id: string;
sessionId: string;
narrative: string;
outcome: string;
duration: string;
observationCount: number;
filesTouched: number;
deadEndsHit: number;
phases: string[];
createdAt: string;
}

// Growth Tracker models
export interface DeveloperMetric {
id: string;
dimension: string;
value: number;
periodStart: string;
periodEnd: string;
createdAt: string;
}

export interface GrowthMilestone {
id: string;
type: string;
description: string;
achievedAt: string;
observationId?: string;
createdAt: string;
}

export interface GrowthReport {
id: string;
periodStart: string;
periodEnd: string;
metrics: DeveloperMetric[];
milestones: GrowthMilestone[];
narrative?: string;
generatedAt: string;
}

// Database explorer types
export interface DbTableInfo {
name: string;
Expand Down Expand Up @@ -331,6 +420,45 @@ export const api = {
return fetchJson<DeadEnd[]>(`/dead-ends${qs ? `?${qs}` : ''}`);
},

// Alerts
alerts: () => fetchJson<DejaVuAlert[]>('/alerts'),

alertsAll: () => fetchJson<DejaVuAlert[]>('/alerts/all'),

alertDismiss: async (id: string) => {
const res = await fetch(`${BASE_URL}/alerts/${encodeURIComponent(id)}/dismiss`, {
method: 'POST',
});
if (!res.ok) throw new Error(`API error ${res.status}: ${res.statusText}`);
},

// Blast Radius
blastRadius: (path: string, hops = 3) =>
fetchJson<BlastRadius>(`/blast-radius/${encodeURIComponent(path)}?hops=${hops}`),

// Decision Replay
replayFile: (path: string) =>
fetchJson<DecisionChain>(`/replay/file/${encodeURIComponent(path)}`),

replayDecision: (nodeId: string) =>
fetchJson<DecisionChain>(`/replay/decision/${encodeURIComponent(nodeId)}`),

// Growth
growth: () => fetchJson<GrowthReport>('/growth'),

growthHistory: (dimension: string, weeks = 12) =>
fetchJson<DeveloperMetric[]>(`/growth/history?dimension=${encodeURIComponent(dimension)}&weeks=${weeks}`),

growthMilestones: (limit = 50) =>
fetchJson<GrowthMilestone[]>(`/growth/milestones?limit=${limit}`),

// Sessions
sessions: (limit = 50) =>
fetchJson<SessionSummary[]>(`/sessions?limit=${limit}`),

sessionStory: (id: string) =>
fetchJson<SessionSummary>(`/sessions/${encodeURIComponent(id)}/story`),

// Context
fileContext: (path: string) =>
fetchJson<FileContext>(`/context/file/${encodeURIComponent(path)}`),
Expand Down
Loading
Loading