agentfit 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +111 -0
- package/README.md +41 -38
- package/app/(dashboard)/daily/page.tsx +1 -1
- package/app/(dashboard)/data-management/page.tsx +180 -0
- package/app/(dashboard)/flow/page.tsx +17 -0
- package/app/(dashboard)/layout.tsx +2 -0
- package/app/(dashboard)/page.tsx +24 -5
- package/app/(dashboard)/reports/[id]/page.tsx +72 -0
- package/app/(dashboard)/reports/page.tsx +132 -0
- package/app/(dashboard)/sessions/[id]/page.tsx +167 -0
- package/app/api/backup/route.ts +215 -0
- package/app/api/check/route.ts +11 -1
- package/app/api/command-insights/route.ts +13 -0
- package/app/api/commands/route.ts +55 -1
- package/app/api/images-analysis/route.ts +3 -4
- package/app/api/reports/[id]/route.ts +23 -0
- package/app/api/reports/route.ts +50 -0
- package/app/api/reset/route.ts +21 -0
- package/app/api/session/route.ts +40 -0
- package/app/api/usage/route.ts +26 -1
- package/app/layout.tsx +1 -1
- package/bin/agentfit.mjs +2 -2
- package/components/agent-coach.tsx +256 -129
- package/components/app-sidebar.tsx +45 -10
- package/components/backup-section.tsx +236 -0
- package/components/daily-chart.tsx +447 -83
- package/components/dashboard-shell.tsx +29 -31
- package/components/data-provider.tsx +88 -8
- package/components/fitness-score.tsx +95 -54
- package/components/overview-cards.tsx +148 -41
- package/components/report-view.tsx +307 -0
- package/components/screenshots-analysis.tsx +51 -46
- package/components/session-chatlog.tsx +124 -0
- package/components/session-timeline.tsx +184 -0
- package/components/session-workflow.tsx +183 -0
- package/components/sessions-table.tsx +9 -1
- package/components/tool-flow-graph.tsx +144 -0
- package/components/ui/carousel.tsx +242 -0
- package/components/ui/sidebar.tsx +1 -1
- package/components/ui/sonner.tsx +51 -0
- package/electron/entitlements.mac.plist +16 -0
- package/electron/init-db.mjs +37 -0
- package/electron/main.mjs +203 -0
- package/generated/prisma/browser.ts +5 -0
- package/generated/prisma/client.ts +5 -0
- package/generated/prisma/internal/class.ts +14 -4
- package/generated/prisma/internal/prismaNamespace.ts +97 -2
- package/generated/prisma/internal/prismaNamespaceBrowser.ts +21 -1
- package/generated/prisma/models/Report.ts +1219 -0
- package/generated/prisma/models/Session.ts +221 -1
- package/generated/prisma/models.ts +1 -0
- package/lib/coach.ts +571 -211
- package/lib/command-insights.ts +231 -0
- package/lib/db.ts +2 -2
- package/lib/parse-codex.ts +6 -0
- package/lib/parse-logs.ts +80 -1
- package/lib/queries-codex.ts +24 -0
- package/lib/queries.ts +45 -0
- package/lib/report.ts +156 -0
- package/lib/session-detail.ts +382 -0
- package/lib/sync.ts +87 -0
- package/lib/tool-flow.ts +71 -0
- package/next.config.mjs +6 -1
- package/package.json +17 -2
- package/plugins/cost-heatmap/component.tsx +72 -50
- package/prisma/migrations/20260401144555_add_system_prompt_edits/migration.sql +80 -0
- package/prisma/schema.prisma +18 -0
- package/prisma/schema.sql +81 -0
- package/.claude/settings.local.json +0 -26
- package/CONTRIBUTING.md +0 -209
- package/prisma/migrations/20260328152517_init/migration.sql +0 -41
- package/prisma/migrations/20260328153801_add_image_model/migration.sql +0 -18
- package/prisma.config.ts +0 -14
- package/setup.sh +0 -73
package/CONTRIBUTING.md
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# Contributing a Community Plugin
|
|
2
|
-
|
|
3
|
-
AgentFit has a plugin system that lets anyone contribute new analysis views. Your plugin appears in the **Community** sidebar section and receives the same usage data as built-in pages.
|
|
4
|
-
|
|
5
|
-
## Quick Start
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# 1. Fork & clone
|
|
9
|
-
git clone https://github.com/<you>/agentfit && cd agentfit
|
|
10
|
-
npm install
|
|
11
|
-
|
|
12
|
-
# 2. Scaffold your plugin
|
|
13
|
-
mkdir -p plugins/my-analysis
|
|
14
|
-
|
|
15
|
-
# 3. Create the two required files (see below)
|
|
16
|
-
# 4. Register it in plugins/index.ts
|
|
17
|
-
# 5. Run dev server & tests
|
|
18
|
-
npm run dev
|
|
19
|
-
npm test
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Plugin Structure
|
|
23
|
-
|
|
24
|
-
Every plugin lives in its own folder under `plugins/` and has exactly **two files** plus a test:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
plugins/
|
|
28
|
-
my-analysis/
|
|
29
|
-
manifest.ts # metadata (name, slug, icon, etc.)
|
|
30
|
-
component.tsx # React component that renders the analysis
|
|
31
|
-
component.test.tsx # tests (required for PR acceptance)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### manifest.ts
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
import type { PluginManifest } from '@/lib/plugins'
|
|
38
|
-
|
|
39
|
-
const manifest: PluginManifest = {
|
|
40
|
-
slug: 'my-analysis', // URL-safe, lowercase, hyphens only
|
|
41
|
-
name: 'My Analysis', // shown in sidebar
|
|
42
|
-
description: 'One-line summary of what this shows',
|
|
43
|
-
author: 'your-github-handle',
|
|
44
|
-
icon: 'ChartArea', // any lucide-react icon name
|
|
45
|
-
version: '1.0.0',
|
|
46
|
-
tags: ['cost', 'productivity'], // optional, for discoverability
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default manifest
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**Slug rules:** lowercase letters, numbers, hyphens only (e.g. `my-analysis`). Must be unique across all plugins.
|
|
53
|
-
|
|
54
|
-
### component.tsx
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
'use client'
|
|
58
|
-
|
|
59
|
-
import { useMemo } from 'react'
|
|
60
|
-
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
61
|
-
import type { PluginProps } from '@/lib/plugins'
|
|
62
|
-
|
|
63
|
-
export default function MyAnalysis({ data }: PluginProps) {
|
|
64
|
-
const stats = useMemo(() => {
|
|
65
|
-
// Compute your analysis from data.sessions, data.daily, etc.
|
|
66
|
-
return { total: data.sessions.length }
|
|
67
|
-
}, [data])
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<Card>
|
|
71
|
-
<CardHeader>
|
|
72
|
-
<CardTitle>My Analysis</CardTitle>
|
|
73
|
-
</CardHeader>
|
|
74
|
-
<CardContent>
|
|
75
|
-
<p>Found {stats.total} sessions</p>
|
|
76
|
-
</CardContent>
|
|
77
|
-
</Card>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Registration
|
|
83
|
-
|
|
84
|
-
Open `plugins/index.ts` and add your import + registration call:
|
|
85
|
-
|
|
86
|
-
```ts
|
|
87
|
-
// ─── Community plugins ──────────────────────────────────────────────
|
|
88
|
-
import myAnalysisManifest from './my-analysis/manifest'
|
|
89
|
-
import MyAnalysis from './my-analysis/component'
|
|
90
|
-
registerPlugin(myAnalysisManifest, MyAnalysis)
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
That's it. Your plugin now appears in the sidebar at `/community/my-analysis`.
|
|
94
|
-
|
|
95
|
-
## Data Available to Plugins
|
|
96
|
-
|
|
97
|
-
Your component receives `PluginProps`:
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
interface PluginProps {
|
|
101
|
-
data: UsageData
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
`UsageData` contains:
|
|
106
|
-
|
|
107
|
-
| Field | Type | Description |
|
|
108
|
-
|-------|------|-------------|
|
|
109
|
-
| `overview` | `OverviewStats` | Aggregated metrics (totals for sessions, tokens, cost, etc.) |
|
|
110
|
-
| `sessions` | `SessionSummary[]` | Every session with tokens, cost, duration, tool calls |
|
|
111
|
-
| `projects` | `ProjectSummary[]` | Per-project aggregations |
|
|
112
|
-
| `daily` | `DailyUsage[]` | Per-day aggregations |
|
|
113
|
-
| `toolUsage` | `Record<string, number>` | Tool invocation counts |
|
|
114
|
-
|
|
115
|
-
All data is already filtered by the user's selected time range and agent type. You don't need to filter again.
|
|
116
|
-
|
|
117
|
-
See `lib/parse-logs.ts` for the full type definitions.
|
|
118
|
-
|
|
119
|
-
## UI Guidelines
|
|
120
|
-
|
|
121
|
-
- Use **shadcn UI** components from `components/ui/` (Card, Badge, Table, etc.)
|
|
122
|
-
- Use **Recharts** (already installed) for charts, wrapped in `ChartContainer`
|
|
123
|
-
- Use `lib/format.ts` helpers: `formatCost()`, `formatTokens()`, `formatDuration()`
|
|
124
|
-
- Use the existing CSS chart color variables: `--chart-1` through `--chart-10`
|
|
125
|
-
- Mark your component as `'use client'` at the top
|
|
126
|
-
- Wrap computed values in `useMemo` for performance
|
|
127
|
-
|
|
128
|
-
## Writing Tests
|
|
129
|
-
|
|
130
|
-
Every plugin must include tests. Use the provided test helpers:
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
// plugins/my-analysis/component.test.tsx
|
|
134
|
-
import { describe, it, expect } from 'vitest'
|
|
135
|
-
import { screen } from '@testing-library/react'
|
|
136
|
-
import { renderPlugin } from '@/tests/plugin-helpers'
|
|
137
|
-
import { validateManifest } from '@/lib/plugins'
|
|
138
|
-
import MyAnalysis from './component'
|
|
139
|
-
import manifest from './manifest'
|
|
140
|
-
|
|
141
|
-
describe('my-analysis plugin', () => {
|
|
142
|
-
it('manifest passes validation', () => {
|
|
143
|
-
expect(validateManifest(manifest)).toEqual([])
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('renders without crashing', () => {
|
|
147
|
-
const { container } = renderPlugin(MyAnalysis)
|
|
148
|
-
expect(container).toBeTruthy()
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('shows expected content', () => {
|
|
152
|
-
renderPlugin(MyAnalysis)
|
|
153
|
-
expect(screen.getByText('My Analysis')).toBeInTheDocument()
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('handles empty data', () => {
|
|
157
|
-
renderPlugin(MyAnalysis, { sessions: [], daily: [], projects: [] })
|
|
158
|
-
// Should not crash — show an empty state instead
|
|
159
|
-
})
|
|
160
|
-
})
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### Test utilities
|
|
164
|
-
|
|
165
|
-
| Helper | Import | Description |
|
|
166
|
-
|--------|--------|-------------|
|
|
167
|
-
| `renderPlugin(Component, dataOverrides?)` | `@/tests/plugin-helpers` | Renders your plugin with mock `UsageData` |
|
|
168
|
-
| `createMockData(overrides?)` | `@/tests/fixtures` | Creates a realistic `UsageData` object |
|
|
169
|
-
| `createMockSession(overrides?)` | `@/tests/fixtures` | Creates a single `SessionSummary` |
|
|
170
|
-
| `createMockDaily(overrides?)` | `@/tests/fixtures` | Creates a single `DailyUsage` |
|
|
171
|
-
| `validateManifest(manifest)` | `@/lib/plugins` | Returns array of validation errors (empty = valid) |
|
|
172
|
-
|
|
173
|
-
### Running tests
|
|
174
|
-
|
|
175
|
-
```bash
|
|
176
|
-
npm test # run all tests once
|
|
177
|
-
npm run test:watch # watch mode during development
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
## PR Checklist
|
|
181
|
-
|
|
182
|
-
Before submitting your pull request:
|
|
183
|
-
|
|
184
|
-
- [ ] Plugin folder is in `plugins/<your-slug>/`
|
|
185
|
-
- [ ] `manifest.ts` passes `validateManifest()` with zero errors
|
|
186
|
-
- [ ] `component.tsx` exports a default React component accepting `PluginProps`
|
|
187
|
-
- [ ] Plugin is registered in `plugins/index.ts`
|
|
188
|
-
- [ ] Tests exist in `component.test.tsx` and all pass (`npm test`)
|
|
189
|
-
- [ ] Component handles empty data gracefully (no crashes)
|
|
190
|
-
- [ ] No new dependencies added (use existing recharts, shadcn, lucide-react)
|
|
191
|
-
- [ ] `npm run typecheck` passes
|
|
192
|
-
- [ ] `npm run lint` passes
|
|
193
|
-
|
|
194
|
-
## Advanced: Custom Data Sources
|
|
195
|
-
|
|
196
|
-
If your plugin fetches its own data (e.g., from a custom API route), set `customDataSource: true` in your manifest. This hides the time-range filter when your plugin is active.
|
|
197
|
-
|
|
198
|
-
```ts
|
|
199
|
-
const manifest: PluginManifest = {
|
|
200
|
-
// ...
|
|
201
|
-
customDataSource: true,
|
|
202
|
-
}
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
You can still use the `data` prop as a fallback, but you're free to `fetch()` additional data in a `useEffect`.
|
|
206
|
-
|
|
207
|
-
## Example Plugin
|
|
208
|
-
|
|
209
|
-
See `plugins/cost-heatmap/` for a complete working example with manifest, component, and tests.
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
CREATE TABLE "Session" (
|
|
3
|
-
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
-
"sessionId" TEXT NOT NULL,
|
|
5
|
-
"project" TEXT NOT NULL,
|
|
6
|
-
"projectPath" TEXT NOT NULL,
|
|
7
|
-
"startTime" DATETIME NOT NULL,
|
|
8
|
-
"endTime" DATETIME NOT NULL,
|
|
9
|
-
"durationMinutes" REAL NOT NULL,
|
|
10
|
-
"userMessages" INTEGER NOT NULL,
|
|
11
|
-
"assistantMessages" INTEGER NOT NULL,
|
|
12
|
-
"totalMessages" INTEGER NOT NULL,
|
|
13
|
-
"inputTokens" INTEGER NOT NULL,
|
|
14
|
-
"outputTokens" INTEGER NOT NULL,
|
|
15
|
-
"cacheCreationTokens" INTEGER NOT NULL,
|
|
16
|
-
"cacheReadTokens" INTEGER NOT NULL,
|
|
17
|
-
"totalTokens" INTEGER NOT NULL,
|
|
18
|
-
"costUSD" REAL NOT NULL,
|
|
19
|
-
"model" TEXT NOT NULL,
|
|
20
|
-
"toolCallsTotal" INTEGER NOT NULL,
|
|
21
|
-
"toolCallsJson" TEXT NOT NULL,
|
|
22
|
-
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
-- CreateTable
|
|
26
|
-
CREATE TABLE "SyncLog" (
|
|
27
|
-
"id" TEXT NOT NULL PRIMARY KEY,
|
|
28
|
-
"syncedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
29
|
-
"filesProcessed" INTEGER NOT NULL,
|
|
30
|
-
"sessionsAdded" INTEGER NOT NULL,
|
|
31
|
-
"sessionsSkipped" INTEGER NOT NULL
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
-- CreateIndex
|
|
35
|
-
CREATE UNIQUE INDEX "Session_sessionId_key" ON "Session"("sessionId");
|
|
36
|
-
|
|
37
|
-
-- CreateIndex
|
|
38
|
-
CREATE INDEX "Session_project_idx" ON "Session"("project");
|
|
39
|
-
|
|
40
|
-
-- CreateIndex
|
|
41
|
-
CREATE INDEX "Session_startTime_idx" ON "Session"("startTime");
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
CREATE TABLE "Image" (
|
|
3
|
-
"id" TEXT NOT NULL PRIMARY KEY,
|
|
4
|
-
"sessionId" TEXT NOT NULL,
|
|
5
|
-
"messageId" TEXT NOT NULL,
|
|
6
|
-
"filename" TEXT NOT NULL,
|
|
7
|
-
"mediaType" TEXT NOT NULL,
|
|
8
|
-
"sizeBytes" INTEGER NOT NULL,
|
|
9
|
-
"timestamp" DATETIME NOT NULL,
|
|
10
|
-
"role" TEXT NOT NULL,
|
|
11
|
-
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
-- CreateIndex
|
|
15
|
-
CREATE INDEX "Image_sessionId_idx" ON "Image"("sessionId");
|
|
16
|
-
|
|
17
|
-
-- CreateIndex
|
|
18
|
-
CREATE UNIQUE INDEX "Image_sessionId_messageId_filename_key" ON "Image"("sessionId", "messageId", "filename");
|
package/prisma.config.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
// This file was generated by Prisma, and assumes you have installed the following:
|
|
2
|
-
// npm install --save-dev prisma dotenv
|
|
3
|
-
import "dotenv/config";
|
|
4
|
-
import { defineConfig } from "prisma/config";
|
|
5
|
-
|
|
6
|
-
export default defineConfig({
|
|
7
|
-
schema: "prisma/schema.prisma",
|
|
8
|
-
migrations: {
|
|
9
|
-
path: "prisma/migrations",
|
|
10
|
-
},
|
|
11
|
-
datasource: {
|
|
12
|
-
url: process.env["DATABASE_URL"],
|
|
13
|
-
},
|
|
14
|
-
});
|
package/setup.sh
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# ─── AgentFit Installer ─────────────────────────────────────────────
|
|
5
|
-
# Usage:
|
|
6
|
-
# curl -fsSL https://raw.githubusercontent.com/harrywang/agentfit/main/setup.sh | bash
|
|
7
|
-
#
|
|
8
|
-
# Or clone manually:
|
|
9
|
-
# git clone https://github.com/harrywang/agentfit.git && cd agentfit && ./setup.sh
|
|
10
|
-
# ─────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
REPO="https://github.com/harrywang/agentfit.git"
|
|
13
|
-
DIR="agentfit"
|
|
14
|
-
PORT="${AGENTFIT_PORT:-3000}"
|
|
15
|
-
|
|
16
|
-
info() { printf "\033[1;34m==>\033[0m %s\n" "$1"; }
|
|
17
|
-
ok() { printf "\033[1;32m==>\033[0m %s\n" "$1"; }
|
|
18
|
-
error() { printf "\033[1;31m==>\033[0m %s\n" "$1" >&2; }
|
|
19
|
-
|
|
20
|
-
# ─── Prerequisites ───────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
command -v node >/dev/null 2>&1 || { error "Node.js is required. Install it from https://nodejs.org"; exit 1; }
|
|
23
|
-
command -v npm >/dev/null 2>&1 || { error "npm is required. It ships with Node.js."; exit 1; }
|
|
24
|
-
|
|
25
|
-
NODE_MAJOR=$(node -e "process.stdout.write(String(process.versions.node.split('.')[0]))")
|
|
26
|
-
if [ "$NODE_MAJOR" -lt 20 ]; then
|
|
27
|
-
error "Node.js 20+ is required (found v$(node -v)). Please upgrade."
|
|
28
|
-
exit 1
|
|
29
|
-
fi
|
|
30
|
-
|
|
31
|
-
# ─── Clone (skip if already inside the repo) ────────────────────────
|
|
32
|
-
|
|
33
|
-
if [ ! -f "package.json" ] || ! grep -q '"agentfit"' package.json 2>/dev/null; then
|
|
34
|
-
if [ -d "$DIR" ]; then
|
|
35
|
-
info "Directory '$DIR' already exists — pulling latest..."
|
|
36
|
-
cd "$DIR"
|
|
37
|
-
git pull --ff-only
|
|
38
|
-
else
|
|
39
|
-
info "Cloning AgentFit..."
|
|
40
|
-
git clone "$REPO" "$DIR"
|
|
41
|
-
cd "$DIR"
|
|
42
|
-
fi
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
# ─── Install ─────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
info "Installing dependencies..."
|
|
48
|
-
npm install
|
|
49
|
-
|
|
50
|
-
# ─── Database ────────────────────────────────────────────────────────
|
|
51
|
-
|
|
52
|
-
info "Setting up database..."
|
|
53
|
-
echo 'DATABASE_URL="file:./dev.db"' > .env
|
|
54
|
-
npx prisma generate
|
|
55
|
-
npx prisma migrate deploy
|
|
56
|
-
|
|
57
|
-
# ─── Build ───────────────────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
info "Building production bundle..."
|
|
60
|
-
npm run build
|
|
61
|
-
|
|
62
|
-
# ─── Done ────────────────────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
ok "AgentFit is ready!"
|
|
65
|
-
echo ""
|
|
66
|
-
echo " Start the dashboard:"
|
|
67
|
-
echo " cd ${DIR} && npm start"
|
|
68
|
-
echo ""
|
|
69
|
-
echo " Or run in dev mode:"
|
|
70
|
-
echo " cd ${DIR} && npm run dev"
|
|
71
|
-
echo ""
|
|
72
|
-
echo " Then open http://localhost:${PORT}"
|
|
73
|
-
echo ""
|