fleetlens 0.7.1 → 0.10.0
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/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/api/ask/route.js +1 -1
- package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +3 -3
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +3 -3
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +3 -3
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/events/route.js +1 -1
- package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js +4 -4
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__07qxzx4._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0fu2xfy._.js +5 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gfd42k._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0i.m8y0._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0iy5aqy._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nh7-nf._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nvdffv._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0s2~c3g._.js +3 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +3 -3
- package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_0o6.gpk._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +13 -13
- package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_122oe0-.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0d~2~le._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0s4dg1y._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0tejjy_._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +3 -3
- package/app/apps/web/.next/server/chunks/ssr/_0jw2sm7._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_usage-charts-dashboard_tsx_0bgr_uv._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0m67-xi._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_10_oegc._.js +1 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/04a36uhu~p_z-.css +1 -0
- package/app/apps/web/.next/static/chunks/{0q2vd~aj.1hpz.js → 09--oejbzw3q-.js} +3 -3
- package/app/apps/web/.next/static/chunks/0l3ck.awv~4t7.js +1 -0
- package/app/apps/web/.next/static/chunks/{171za22fv684..js → 0lxuac2v0_gdl.js} +2 -2
- package/app/apps/web/.next/static/chunks/0n0n.0279ot6c.js +1 -0
- package/app/apps/web/.next/static/chunks/0qcu452dr~edz.js +14 -0
- package/app/apps/web/.next/static/chunks/0uev~g~qvbjv9.js +4 -0
- package/app/apps/web/components/previous-cycles-trend.tsx +26 -11
- package/app/apps/web/components/usage-chart.tsx +35 -37
- package/app/apps/web/components/usage-gauges.tsx +18 -12
- package/app/apps/web/components/usage-sidebar.tsx +23 -12
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/utilization-tone.ts +74 -0
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +694 -67
- package/dist/index.js +1449 -823
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/[externals]__0fpbr8-._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0-vgrh1._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0.z48ue._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_8zh-i._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0dctov1._.js +0 -5
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e8n_x4._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ej_p3s._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jprdks._.js +0 -9
- package/app/apps/web/.next/server/chunks/packages_09hmrqx._.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_118y1lp._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ytdqxt._.js +0 -3
- package/app/apps/web/.next/static/chunks/05m.oz3kmf1p7.js +0 -1
- package/app/apps/web/.next/static/chunks/07-fq4im3pmjp.js +0 -4
- package/app/apps/web/.next/static/chunks/0o9ayt8sknubd.js +0 -1
- package/app/apps/web/.next/static/chunks/0wfbj5zr~az.z.css +0 -1
- package/app/apps/web/.next/static/chunks/179_t_igg8ghh.js +0 -14
- /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_ssgManifest.js +0 -0
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type { UsageSnapshot, UsageWindow } from "@/lib/usage-data";
|
|
2
|
+
import { paceToneForCycle, toneVar } from "@/lib/utilization-tone";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
const HOUR = 3_600_000;
|
|
5
|
+
const FIVE_HOURS_MS = 5 * HOUR;
|
|
6
|
+
const SEVEN_DAYS_MS = 7 * 24 * HOUR;
|
|
7
|
+
|
|
8
|
+
type Row = { label: string; window: UsageWindow | null; windowMs: number };
|
|
4
9
|
|
|
5
10
|
export function UsageGauges({ snapshot }: { snapshot: UsageSnapshot }) {
|
|
6
11
|
const rows: Row[] = [
|
|
7
|
-
{ label: "5 hour", window: snapshot.five_hour },
|
|
8
|
-
{ label: "7 day (all)", window: snapshot.seven_day },
|
|
9
|
-
{ label: "7 day Sonnet", window: snapshot.seven_day_sonnet },
|
|
12
|
+
{ label: "5 hour", window: snapshot.five_hour, windowMs: FIVE_HOURS_MS },
|
|
13
|
+
{ label: "7 day (all)", window: snapshot.seven_day, windowMs: SEVEN_DAYS_MS },
|
|
14
|
+
{ label: "7 day Sonnet", window: snapshot.seven_day_sonnet, windowMs: SEVEN_DAYS_MS },
|
|
10
15
|
];
|
|
11
16
|
|
|
12
17
|
return (
|
|
@@ -24,16 +29,17 @@ export function UsageGauges({ snapshot }: { snapshot: UsageSnapshot }) {
|
|
|
24
29
|
);
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
function Gauge({ label, window }: Row) {
|
|
32
|
+
function Gauge({ label, window, windowMs }: Row) {
|
|
28
33
|
const pct = window?.utilization ?? null;
|
|
29
34
|
const hasData = pct !== null;
|
|
30
35
|
const clamped = hasData ? Math.max(0, Math.min(100, pct!)) : 0;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
// Live gauges color by pace, not absolute %. Without resets_at we have
|
|
37
|
+
// no cycle to anchor against — drop to a neutral border tone so the bar
|
|
38
|
+
// still renders rather than picking a misleading pace color.
|
|
39
|
+
const cycleEndMs = window?.resets_at ? new Date(window.resets_at).getTime() : null;
|
|
40
|
+
const fillColor = hasData && cycleEndMs
|
|
41
|
+
? toneVar(paceToneForCycle(clamped, cycleEndMs, windowMs))
|
|
42
|
+
: "var(--af-border-subtle)";
|
|
37
43
|
|
|
38
44
|
return (
|
|
39
45
|
<div className="af-card" style={{ padding: "16px 18px" }}>
|
|
@@ -77,7 +83,7 @@ function Gauge({ label, window }: Row) {
|
|
|
77
83
|
style={{
|
|
78
84
|
height: "100%",
|
|
79
85
|
width: hasData ? `${clamped}%` : "0%",
|
|
80
|
-
background:
|
|
86
|
+
background: fillColor,
|
|
81
87
|
borderRadius: 999,
|
|
82
88
|
transition: "width 0.24s ease",
|
|
83
89
|
}}
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
import Link from "next/link";
|
|
4
4
|
import type { UsageSnapshot, UsageWindow } from "@/lib/usage-data";
|
|
5
5
|
import { usePersistentBoolean } from "@/lib/use-persistent-boolean";
|
|
6
|
+
import { paceToneForCycle, toneVar } from "@/lib/utilization-tone";
|
|
7
|
+
|
|
8
|
+
const HOUR = 3_600_000;
|
|
9
|
+
const FIVE_HOURS_MS = 5 * HOUR;
|
|
10
|
+
const SEVEN_DAYS_MS = 7 * 24 * HOUR;
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Compact current-usage widget for the sidebar. Always visible on every page.
|
|
@@ -38,14 +43,14 @@ export function UsageSidebar({ snapshot }: { snapshot: UsageSnapshot | null }) {
|
|
|
38
43
|
);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
const rows: { label: string; window: UsageWindow | null }[] = [
|
|
42
|
-
{ label: "5h", window: snapshot.five_hour },
|
|
43
|
-
{ label: "7d", window: snapshot.seven_day },
|
|
46
|
+
const rows: { label: string; window: UsageWindow | null; windowMs: number }[] = [
|
|
47
|
+
{ label: "5h", window: snapshot.five_hour, windowMs: FIVE_HOURS_MS },
|
|
48
|
+
{ label: "7d", window: snapshot.seven_day, windowMs: SEVEN_DAYS_MS },
|
|
44
49
|
];
|
|
45
50
|
// Only include Sonnet after hydration to avoid a layout flash where
|
|
46
51
|
// the SSR pass renders it, then the client removes it on hydrate.
|
|
47
52
|
if (sonnetHydrated && showSonnet) {
|
|
48
|
-
rows.push({ label: "Sonnet 7d", window: snapshot.seven_day_sonnet });
|
|
53
|
+
rows.push({ label: "Sonnet 7d", window: snapshot.seven_day_sonnet, windowMs: SEVEN_DAYS_MS });
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
return (
|
|
@@ -93,16 +98,22 @@ export function UsageSidebar({ snapshot }: { snapshot: UsageSnapshot | null }) {
|
|
|
93
98
|
);
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
function UsageRow({
|
|
101
|
+
function UsageRow({
|
|
102
|
+
label,
|
|
103
|
+
window,
|
|
104
|
+
windowMs,
|
|
105
|
+
}: {
|
|
106
|
+
label: string;
|
|
107
|
+
window: UsageWindow | null;
|
|
108
|
+
windowMs: number;
|
|
109
|
+
}) {
|
|
97
110
|
const pct = window?.utilization ?? null;
|
|
98
111
|
const hasData = pct !== null;
|
|
99
112
|
const clamped = hasData ? Math.max(0, Math.min(100, pct!)) : 0;
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
? "var(--af-warning)"
|
|
105
|
-
: "var(--af-success)";
|
|
113
|
+
const cycleEndMs = window?.resets_at ? new Date(window.resets_at).getTime() : null;
|
|
114
|
+
const fillColor = hasData && cycleEndMs
|
|
115
|
+
? toneVar(paceToneForCycle(clamped, cycleEndMs, windowMs))
|
|
116
|
+
: "var(--af-border-subtle)";
|
|
106
117
|
|
|
107
118
|
return (
|
|
108
119
|
<div>
|
|
@@ -150,7 +161,7 @@ function UsageRow({ label, window }: { label: string; window: UsageWindow | null
|
|
|
150
161
|
style={{
|
|
151
162
|
height: "100%",
|
|
152
163
|
width: hasData ? `${clamped}%` : "0%",
|
|
153
|
-
background:
|
|
164
|
+
background: fillColor,
|
|
154
165
|
borderRadius: 999,
|
|
155
166
|
transition: "width 0.24s ease",
|
|
156
167
|
}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// AUTOGENERATED by scripts/build-changelog.mjs from CHANGELOG.md.
|
|
2
2
|
// Do not edit by hand — run `pnpm changelog:sync`.
|
|
3
|
-
export const RAW_CHANGELOG = "# Changelog\n\nAll notable user-facing changes to the Fleetlens CLI (`fleetlens` on npm) are\nrecorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\nThe team-server has its own log at `packages/team-server/CHANGELOG.md`.\n\n## [0.7.1] — 2026-05-07\n\n### Fixed\n- Cycle-peaks trend no longer renders two bars labeled the same day. When Anthropic's rolling 7-day window slid its anchor between consecutive polls, the daemon's bucketer (hour-rounded) recorded both reset boundaries as distinct cycles. They're now merged within a 12 h tolerance — same fix in `apps/web/lib/cycle-peaks.ts` (used by `/usage`) and in `packages/cli/src/team/sync.ts` (the daemon's team-server push), so both the personal `/usage` chart and the team-server's plan-utilization view stay consistent. 5-hour cycles keep no merge tolerance (their anchor is stable; merging would collapse legitimate distinct cycles).\n\n## [0.6.4] — 2026-05-04\n\n### Fixed\n- Cached calibration events per-file so `/usage` warm renders complete in well under a second on large session histories.\n\n## [0.6.3] — 2026-05-04\n\n### Fixed\n- Dropped poisoned auto-week locks left behind by interrupted runs; backfill now gates on the live pipeline lock instead.\n\n## [0.6.2] — 2026-05-04\n\n### Added\n- Last week's digest auto-generates on launch when AI features are on. Disable in Settings → AI features.\n\n## [0.6.1] — 2026-05-04\n\n### Changed\n- Smaller install size — published bundle no longer drags in unused workspace metadata.\n\n## [0.6.0] — 2026-04-30\n\n### Added\n- V2 perception layer. Daily, weekly, and monthly digests synthesized from per-session entries via a strict hierarchical pipeline (entry → day → week → month). Past-period digests are immutable on disk; current-period digests use a 10-min in-memory TTL.\n- `/insights` route with on-demand week and month digest generation, picker history, and force-regen.\n- `fleetlens digest day | week | month [--json]` — CLI surface that reproduces the same digests served at `/insights`.\n- `fleetlens entries [--day | --session | --all] [--json]` — inspect the per-session entry primitive.\n\n### Changed\n- Renamed \"active time\" to \"agent time\" across the dashboard and CLI for clarity.\n\n## [0.4.1] — 2026-04-23\n\n### Added\n- Concurrency view on `/parallelism` page. Bursts colored teal for same-project and purple for cross-project (the genuinely interesting fleet-work signal).\n- Plan utilization sparkline on the sidebar, fed by the usage daemon's 5-min snapshots.\n\n### Fixed\n- Auto-update no longer leaves a zombie web server on the old binary; both server and daemon are torn down before re-exec.\n\n## [0.4.0] — 2026-04-18\n\n### Added\n- Usage daemon (`fleetlens daemon …`) polls `/api/oauth/usage` every 5 min and appends snapshots to `~/.cclens/usage.jsonl`. `fleetlens start` and `fleetlens stop` manage server + daemon together.\n- `fleetlens usage --history` for inspecting past plan utilization.\n\n## [0.3.0] — 2026-04-10\n\n### Added\n- Project rollups with worktree aggregation. Sessions inside `<project>/.worktrees/<name>` roll up under their parent canonical project with a `+N wt` badge.\n- Pinned projects in the sidebar (per-browser via localStorage).\n\n## [0.2.0] — 2026-04-01\n\n### Changed\n- Active-segment computation now uses all timestamped events (system + summary + sidechain + conversational) instead of conversational-only. Earlier filter undercounted by up to 100x on tool-heavy sessions.\n\n## [0.1.0] — 2026-03-15\n\n### Added\n- Initial release. Local-only dashboard for Claude Code sessions: timeline, projects, daily activity bucketing, parallelism detection, per-model cost estimation. Reads JSONL transcripts from `~/.claude/projects/`.\n";
|
|
3
|
+
export const RAW_CHANGELOG = "# Changelog\n\nAll notable user-facing changes to the Fleetlens CLI (`fleetlens` on npm) are\nrecorded here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).\nThe team-server has its own log at `packages/team-server/CHANGELOG.md`.\n\n## [0.10.0] — 2026-05-08\n\n### Changed\n- **Plan utilization reframed.** High utilization is now \"getting plan value\" (green); low utilization is \"paying for unused headroom\" (red). Mirrors how an operator actually looks at the dashboard: the wasteful outcome is paying for a plan and barely touching it, not running near the cap. Applied across `/usage` burndown, gauges, sidebar, and previous-cycles strip.\n- **In-progress cycles colored by pace, not peak.** A 50% peak halfway through a 7-day cycle reads on-pace green, not amber — the chart no longer cries wolf on a fresh cycle that just hasn't burnt yet. Completed cycles still use peak tone.\n- **Pace label ±15pp band.** \"On pace\" now covers a ±15pp window around ideal; outside that, label is \"below pace\" (under-burning) or \"may exhaust early\" (over-burning). The previous +5/-50 thresholds flipped amber every time the user took a night off.\n- **Pace label collapses to three states.** \"May exhaust early\" / \"below pace\" / \"on pace\" — same color tones, one fewer qualifier to read.\n- **Wording softened across the utilization views** to describe the pattern rather than judge the user. \"Behind schedule\" → \"below pace\"; \"on track (+x%)\" → \"on pace (+xpp vs ideal)\".\n\n### Added\n- **Conductor multi-agent workspace support.** Sessions inside Conductor's worktree-style agent workspaces (`~/conductor/workspaces/<repo>/<agent>/...`) now group correctly under their parent project, with each agent shown as a separate worktree. Mirrors the existing `.worktrees/` handling.\n\n### Fixed\n- Hydration mismatch on `/usage` when pace labels rendered before client mount.\n\n### Notes for operators\n- The CLI's team push now ships **only Claude Code usage snapshots** to the team server. Codex snapshots stay local until `plan_utilization` gets a per-agent partition — without one, the team-server's \"latest snapshot\" pick flips between agents and miscolors the in-progress cycle. Existing rows in `plan_utilization` stay; new pushes are claude-code only.\n\n## [0.9.0] — 2026-05-07\n\n### Added\n- **Gemini CLI** as the third agent source. Sessions written to `~/.gemini/tmp/<slug>/chats/session-*.jsonl` are now read, parsed, and rendered alongside Claude Code and Codex. Tool calls (`read_file`, `list_directory`, `glob`, `run_shell_command`, `update_topic`, …) and their results show in the session timeline; thinking blocks, multi-write status replay, `$set` / `$rewindTo` semantics, and the legacy single-JSON file shape all handled. Slug → cwd inversion via `~/.gemini/projects.json`. `✺` glyph + purple accent in the UI.\n\n### Changed\n- Session detail timeline drops in-turn idle hatching. Within a turn, every wait — model loading, tool execution, network — is the agent's active time. Only between-turn idle (user reading + composing the next message) renders as hatched.\n- Gemini parser anchors `firstTimestamp`, `lastTimestamp`, and `activeSegments` on conversational events only. Login/OAuth `info` records and `metadata.startTime` (file-creation moment) no longer stretch the session bar back to before the first user message.\n\n## [0.7.1] — 2026-05-07\n\n### Fixed\n- Cycle-peaks trend no longer renders two bars labeled the same day. When Anthropic's rolling 7-day window slid its anchor between consecutive polls, the daemon's bucketer (hour-rounded) recorded both reset boundaries as distinct cycles. They're now merged within a 12 h tolerance — same fix in `apps/web/lib/cycle-peaks.ts` (used by `/usage`) and in `packages/cli/src/team/sync.ts` (the daemon's team-server push), so both the personal `/usage` chart and the team-server's plan-utilization view stay consistent. 5-hour cycles keep no merge tolerance (their anchor is stable; merging would collapse legitimate distinct cycles).\n\n## [0.6.4] — 2026-05-04\n\n### Fixed\n- Cached calibration events per-file so `/usage` warm renders complete in well under a second on large session histories.\n\n## [0.6.3] — 2026-05-04\n\n### Fixed\n- Dropped poisoned auto-week locks left behind by interrupted runs; backfill now gates on the live pipeline lock instead.\n\n## [0.6.2] — 2026-05-04\n\n### Added\n- Last week's digest auto-generates on launch when AI features are on. Disable in Settings → AI features.\n\n## [0.6.1] — 2026-05-04\n\n### Changed\n- Smaller install size — published bundle no longer drags in unused workspace metadata.\n\n## [0.6.0] — 2026-04-30\n\n### Added\n- V2 perception layer. Daily, weekly, and monthly digests synthesized from per-session entries via a strict hierarchical pipeline (entry → day → week → month). Past-period digests are immutable on disk; current-period digests use a 10-min in-memory TTL.\n- `/insights` route with on-demand week and month digest generation, picker history, and force-regen.\n- `fleetlens digest day | week | month [--json]` — CLI surface that reproduces the same digests served at `/insights`.\n- `fleetlens entries [--day | --session | --all] [--json]` — inspect the per-session entry primitive.\n\n### Changed\n- Renamed \"active time\" to \"agent time\" across the dashboard and CLI for clarity.\n\n## [0.4.1] — 2026-04-23\n\n### Added\n- Concurrency view on `/parallelism` page. Bursts colored teal for same-project and purple for cross-project (the genuinely interesting fleet-work signal).\n- Plan utilization sparkline on the sidebar, fed by the usage daemon's 5-min snapshots.\n\n### Fixed\n- Auto-update no longer leaves a zombie web server on the old binary; both server and daemon are torn down before re-exec.\n\n## [0.4.0] — 2026-04-18\n\n### Added\n- Usage daemon (`fleetlens daemon …`) polls `/api/oauth/usage` every 5 min and appends snapshots to `~/.cclens/usage.jsonl`. `fleetlens start` and `fleetlens stop` manage server + daemon together.\n- `fleetlens usage --history` for inspecting past plan utilization.\n\n## [0.3.0] — 2026-04-10\n\n### Added\n- Project rollups with worktree aggregation. Sessions inside `<project>/.worktrees/<name>` roll up under their parent canonical project with a `+N wt` badge.\n- Pinned projects in the sidebar (per-browser via localStorage).\n\n## [0.2.0] — 2026-04-01\n\n### Changed\n- Active-segment computation now uses all timestamped events (system + summary + sidechain + conversational) instead of conversational-only. Earlier filter undercounted by up to 100x on tool-heavy sessions.\n\n## [0.1.0] — 2026-03-15\n\n### Added\n- Initial release. Local-only dashboard for Claude Code sessions: timeline, projects, daily activity bucketing, parallelism detection, per-model cost estimation. Reads JSONL transcripts from `~/.claude/projects/`.\n";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilization tone — the "high usage = good" framing.
|
|
3
|
+
*
|
|
4
|
+
* Paying for a plan and barely touching it is the wasteful outcome,
|
|
5
|
+
* not the safe one. ≥70% peaks = getting value (green). <40% = paying
|
|
6
|
+
* for headroom you don't need (red). Throttling (real wall hits) is
|
|
7
|
+
* tracked separately and stays red regardless.
|
|
8
|
+
*
|
|
9
|
+
* SYNC: packages/team-server/src/lib/utilization-tone.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export type Tone = "success" | "warning" | "danger";
|
|
13
|
+
|
|
14
|
+
export const UTILIZATION_THRESHOLDS = { good: 70, low: 40 } as const;
|
|
15
|
+
|
|
16
|
+
export function utilizationTone(pct: number): Tone {
|
|
17
|
+
if (pct >= UTILIZATION_THRESHOLDS.good) return "success";
|
|
18
|
+
if (pct >= UTILIZATION_THRESHOLDS.low) return "warning";
|
|
19
|
+
return "danger";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function toneVar(tone: Tone): string {
|
|
23
|
+
switch (tone) {
|
|
24
|
+
case "success":
|
|
25
|
+
return "var(--af-success)";
|
|
26
|
+
case "warning":
|
|
27
|
+
return "#b58400";
|
|
28
|
+
case "danger":
|
|
29
|
+
return "var(--af-danger)";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function utilizationVar(pct: number): string {
|
|
34
|
+
return toneVar(utilizationTone(pct));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Pace label for the burndown chart. Three states only — describing
|
|
39
|
+
* the pattern, not judging it. ±15pp band around ideal is "on pace";
|
|
40
|
+
* outside that, we name what's happening.
|
|
41
|
+
*
|
|
42
|
+
* delta = currentRemaining − idealRemaining
|
|
43
|
+
* delta < -15 → "may exhaust early" (danger — over-burning)
|
|
44
|
+
* delta > 15 → "below pace" (warning — under-burn)
|
|
45
|
+
* else → "on pace" (success)
|
|
46
|
+
*
|
|
47
|
+
* The band is wider than feels intuitive on purpose: a 7d cycle has
|
|
48
|
+
* ~14pp/day of ideal burn, so anything tighter flips amber every time
|
|
49
|
+
* the user takes a night off.
|
|
50
|
+
*/
|
|
51
|
+
export function paceLabel(delta: number): { tone: Tone; label: string } {
|
|
52
|
+
if (delta < -15) return { tone: "danger", label: "may exhaust early" };
|
|
53
|
+
if (delta > 15) return { tone: "warning", label: "below pace" };
|
|
54
|
+
return { tone: "success", label: "on pace" };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Tone for an in-progress cycle/gauge — derived from pace (utilization vs
|
|
59
|
+
* elapsed-fraction-of-cycle), not from absolute peak. A 50% peak halfway
|
|
60
|
+
* through a cycle is on pace = green; the same 50% peak in a completed
|
|
61
|
+
* cycle is moderate-use = amber. Use `utilizationTone()` for completed
|
|
62
|
+
* cycles, this for live ones.
|
|
63
|
+
*/
|
|
64
|
+
export function paceToneForCycle(
|
|
65
|
+
utilizationPct: number,
|
|
66
|
+
cycleEndsAtMs: number,
|
|
67
|
+
windowMs: number,
|
|
68
|
+
nowMs: number = Date.now(),
|
|
69
|
+
): Tone {
|
|
70
|
+
const cycleStart = cycleEndsAtMs - windowMs;
|
|
71
|
+
const elapsed = Math.max(0, Math.min(1, (nowMs - cycleStart) / windowMs));
|
|
72
|
+
const idealUsedPct = elapsed * 100;
|
|
73
|
+
return paceLabel(idealUsedPct - utilizationPct).tone;
|
|
74
|
+
}
|