claude-code-session-manager 0.94.0 → 0.96.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.
Files changed (122) hide show
  1. package/dist/assets/{DataModel-B0LDnnSL.js → DataModel-T0D00BpK.js} +1 -1
  2. package/dist/assets/History-DuAovTr7.js +2 -0
  3. package/dist/assets/{Hooks-CwLnp0Z_.js → Hooks-CgP9jsAN.js} +3 -3
  4. package/dist/assets/{HostBilko-CwEHEKYk.js → HostBilko-DHfz3Mxa.js} +1 -1
  5. package/dist/assets/{Library-BU9np05E.js → Library--poVP-d1.js} +1 -1
  6. package/dist/assets/{MarkdownEditor-B74ogK2f.js → MarkdownEditor-C56-Pc4c.js} +1 -1
  7. package/dist/assets/{McpServers-BLM3ftzP.js → McpServers-D-ykP4Vi.js} +2 -2
  8. package/dist/assets/Memory-BrkJjRL8.js +8 -0
  9. package/dist/assets/{Permissions-3pGLJmxQ.js → Permissions-BMpAaxJu.js} +3 -3
  10. package/dist/assets/Plugins-OuEe5S_T.js +2 -0
  11. package/dist/assets/{ProvenanceBadge-BnjBQiV9.js → ProvenanceBadge-4eNbDYe8.js} +1 -1
  12. package/dist/assets/{SaveBar-Zq2NfcPf.js → SaveBar-BQ8y9Lkv.js} +1 -1
  13. package/dist/assets/Scheduler-DcMT5sT_.js +16 -0
  14. package/dist/assets/{ScopeSwitcher-Cnojv22D.js → ScopeSwitcher-XdxtVKP-.js} +1 -1
  15. package/dist/assets/Settings-FdaPUq2t.js +3 -0
  16. package/dist/assets/{SkillReferenceGraph-BRDiE6Hv.js → SkillReferenceGraph-CJzvvc0_.js} +1 -1
  17. package/dist/assets/Skills-Wvm9OE1e.js +3 -0
  18. package/dist/assets/{SystemPrompt-tFr19Od6.js → SystemPrompt--LYw_69j.js} +1 -1
  19. package/dist/assets/TagLibrary-Be6HbMMx.js +1 -0
  20. package/dist/assets/{TiptapBody-DGzSg3BO.js → TiptapBody-DzFlWMgO.js} +1 -1
  21. package/dist/assets/{Toggle-DzoROibf.js → Toggle-BxtAFE1i.js} +1 -1
  22. package/dist/assets/index-XkrLB67D.js +3079 -0
  23. package/dist/assets/{settingsSchema-BYKVe1WM.js → settingsSchema-BIvDZ1Cj.js} +1 -1
  24. package/dist/index.html +1 -1
  25. package/package.json +10 -2
  26. package/plugins/CLAUDE.md +3 -3
  27. package/plugins/session-manager-dev/skills/develop/SKILL.md +83 -67
  28. package/plugins/session-manager-dev/skills/develop/standards.md +5 -11
  29. package/plugins/session-manager-dev/skills/requesting-code-review/SKILL.md +5 -15
  30. package/scripts/README.md +4 -0
  31. package/scripts/hooks/guard-destructive-git.cjs +9 -374
  32. package/scripts/hooks/guard-inline-implementation.cjs +9 -128
  33. package/scripts/hooks/guard-prd-writes.cjs +9 -74
  34. package/scripts/hooks/guard-self-schedule.cjs +9 -61
  35. package/scripts/hooks/lib/guard-destructive-git-policy.cjs +410 -0
  36. package/scripts/hooks/lib/guard-inline-implementation-policy.cjs +159 -0
  37. package/scripts/hooks/lib/guard-prd-writes-policy.cjs +100 -0
  38. package/scripts/hooks/lib/guard-self-schedule-policy.cjs +89 -0
  39. package/scripts/scheduler-mcp-server.cjs +8 -1
  40. package/src/main/__tests__/configWriteBoundaryOwners.test.cjs +59 -0
  41. package/src/main/__tests__/exchanges.test.cjs +41 -96
  42. package/src/main/__tests__/exchangesPromptId.test.cjs +16 -25
  43. package/src/main/__tests__/files-reject-credentials.test.cjs +1 -1
  44. package/src/main/__tests__/health-credentials.test.cjs +81 -0
  45. package/src/main/__tests__/health-tick-liveness.test.cjs +12 -0
  46. package/src/main/__tests__/memoryAggregate.test.cjs +40 -1
  47. package/src/main/__tests__/opsErrorLogTelemetryTap.test.cjs +1 -1
  48. package/src/main/__tests__/planValidator.test.cjs +71 -0
  49. package/src/main/__tests__/prdCreate.test.cjs +3 -1
  50. package/src/main/__tests__/prdSizing.test.cjs +106 -0
  51. package/src/main/__tests__/queue-starvation-dispatch-driver.test.cjs +46 -0
  52. package/src/main/__tests__/reconcileFlatPrdSweep.test.cjs +10 -0
  53. package/src/main/__tests__/scheduler-looks-done.test.cjs +40 -0
  54. package/src/main/__tests__/scheduler-notify-originating-tab.test.cjs +81 -0
  55. package/src/main/__tests__/scheduler-rate-limit-pause.test.cjs +15 -0
  56. package/src/main/__tests__/scheduler-shard-quarantine.test.cjs +6 -1
  57. package/src/main/__tests__/scheduler-utilization-hold.test.cjs +89 -0
  58. package/src/main/__tests__/schedulerStateSidecarRestore.test.cjs +110 -0
  59. package/src/main/__tests__/seedAgentPersonas.test.cjs +19 -1
  60. package/src/main/__tests__/seedValidatorPersona.test.cjs +116 -0
  61. package/src/main/__tests__/telemetryClient.test.cjs +2 -2
  62. package/src/main/__tests__/telemetryContract.test.cjs +2 -2
  63. package/src/main/__tests__/usageSingleFlight.test.cjs +10 -0
  64. package/src/main/__tests__/validationSentinels.test.cjs +84 -0
  65. package/src/main/build-info.json +4 -4
  66. package/src/main/config.cjs +26 -14
  67. package/src/main/crashDiagnostics.cjs +9 -0
  68. package/src/main/exchanges.cjs +26 -21
  69. package/src/main/health.cjs +91 -5
  70. package/src/main/index.cjs +1 -1
  71. package/src/main/ipcSchemas.cjs +5 -1
  72. package/src/main/lib/__tests__/crashTelemetry.test.cjs +1 -1
  73. package/src/main/lib/__tests__/credentials-futile-refresh.test.cjs +115 -0
  74. package/src/main/lib/__tests__/epicWorktreeProjectConfig.test.cjs +85 -27
  75. package/src/main/lib/__tests__/gitWorktree.test.cjs +21 -27
  76. package/src/main/lib/__tests__/guardShims.test.cjs +10 -3
  77. package/src/main/lib/__tests__/loadGate.test.cjs +44 -1
  78. package/src/main/lib/__tests__/loopDelay.test.cjs +68 -0
  79. package/src/main/lib/__tests__/opsOwnership.test.cjs +14 -0
  80. package/src/main/lib/__tests__/telemetryBacklog.test.cjs +4 -4
  81. package/src/main/lib/__tests__/telemetryConsent.test.cjs +2 -2
  82. package/src/main/lib/__tests__/telemetryCountersMetadataColumn.test.cjs +1 -1
  83. package/src/main/lib/__tests__/usageCircuit.test.cjs +86 -17
  84. package/src/main/lib/agentModelResolve.cjs +1 -0
  85. package/src/main/lib/credentials.cjs +62 -0
  86. package/src/main/lib/cwdClassify.cjs +15 -0
  87. package/src/main/lib/definitionOfDone.cjs +9 -13
  88. package/src/main/lib/effectiveModelInfo.cjs +2 -9
  89. package/src/main/lib/epicWorktreeProjectConfig.cjs +75 -31
  90. package/src/main/lib/loadGate.cjs +29 -5
  91. package/src/main/lib/loopDelay.cjs +44 -0
  92. package/src/main/lib/opsOwnership.cjs +23 -0
  93. package/src/main/lib/planValidator.cjs +28 -0
  94. package/src/main/lib/prdCreate.cjs +8 -4
  95. package/src/main/lib/prdSizing.cjs +83 -0
  96. package/src/main/lib/runLogRetention.cjs +2 -8
  97. package/src/main/lib/telemetryBacklog.cjs +9 -3
  98. package/src/main/lib/telemetryClient.cjs +12 -3
  99. package/src/main/lib/telemetrySettings.cjs +8 -2
  100. package/src/main/lib/usageCircuit.cjs +40 -12
  101. package/src/main/lib/validationSentinels.cjs +29 -0
  102. package/src/main/memoryAggregate.cjs +36 -11
  103. package/src/main/otelSettings.cjs +8 -2
  104. package/src/main/scheduler.cjs +264 -34
  105. package/src/main/seedAgentPersonas.cjs +1 -1
  106. package/src/main/sessionsStore.cjs +8 -2
  107. package/src/main/supervisor.cjs +3 -3
  108. package/src/main/templates/PRD_AUTHORING.md +9 -9
  109. package/src/main/usage.cjs +9 -1
  110. package/src/main/voiceSettings.cjs +8 -2
  111. package/src/preload/api.d.ts +20 -5
  112. package/src/preload/index.cjs +5 -5
  113. package/src/seed/agents/dev-lead.md +24 -15
  114. package/src/seed/agents/validator.md +33 -0
  115. package/dist/assets/History-C685Kytt.js +0 -2
  116. package/dist/assets/Memory-DAP1t8bA.js +0 -8
  117. package/dist/assets/Plugins-Q1KoLEzn.js +0 -2
  118. package/dist/assets/Scheduler-D488ebKm.js +0 -16
  119. package/dist/assets/Settings-D5Lhj7ga.js +0 -3
  120. package/dist/assets/Skills-BQDqp-EN.js +0 -3
  121. package/dist/assets/TagLibrary-BjyrnaRE.js +0 -1
  122. package/dist/assets/index-CCl4tz-u.js +0 -3079
@@ -1,4 +1,4 @@
1
- <!-- PRD_AUTHORING.md v2 -->
1
+ <!-- PRD_AUTHORING.md v3 -->
2
2
  # PRD Authoring Guide — Scheduler Safety Rules
3
3
 
4
4
  This guide codifies lessons from two stuck-job incidents (fizzpop poll-hang, etch-engine post-AC overrun) into enforceable rules every PRD MUST follow. Violating these rules costs real money and wastes hours waiting at a terminal.
@@ -122,14 +122,14 @@ curl -sf https://bilko.run/projects/<slug>/ > /dev/null || { echo "SMOKE TEST FA
122
122
 
123
123
  ## §6 Frontmatter rules
124
124
 
125
- **Summary:** Required keys are `title`, `cwd` (absolute path), `estimateMinutes`. Default to letting the filename `NN-` prefix drive grouping; include `parallelGroup` ONLY when you are deliberately interleaving across project streams.
125
+ **Summary:** Required keys are `title`, `cwd` (absolute path), `estimateMinutes`. Default to letting the filename `NN-` prefix drive grouping.
126
126
 
127
127
  **Required frontmatter:**
128
128
  ```yaml
129
129
  ---
130
130
  title: <one line, plain English>
131
131
  cwd: ~/Projects/<target-repo>
132
- estimateMinutes: 60
132
+ estimateMinutes: 10
133
133
  ---
134
134
  ```
135
135
 
@@ -139,7 +139,7 @@ estimateMinutes: 60
139
139
  - `cwd` MUST point to the target project. Prefer `~/...` for portability; only use an absolute path if you have a specific reason to pin to one machine.
140
140
  - **`cwd` MUST already exist on disk at queue time.** The scheduler runs a dead-cwd guard (`fs.accessSync(cwd, fs.constants.X_OK)` in `src/main/scheduler.cjs:669-680`) *before* spawning the child, so a PRD whose `cwd` references a not-yet-created directory will exit with `-1: cwd no longer exists` and the body will never run — even if the first step of the body would have created the directory. If the PRD's purpose is to create a brand-new sibling project at `~/Projects/<new-slug>/`, point `cwd` at the parent (`~/Projects`) and make the first executable step `mkdir -p ~/Projects/<new-slug> && cd ~/Projects/<new-slug>`.
141
141
  - `estimateMinutes` is used for ETA display; include a realistic estimate (note: empirical median is ~10 min, p90 ~20 min — avoid wildly inflated estimates that hide real outliers).
142
- - `parallelGroup` in frontmatter, when present, IS honored by the scheduler (`pickNextBatch` reads `parallelGroup ?? 99` and overrides the filename NN). Use this only for cross-stream interleaving — e.g., the cellar series `122-`, `123-`, `124-` overrides to groups `113`, `114`, `115` so cellar steps fire alongside the parallel etch steps. Do NOT use it to reorder within a single stream; rename the file instead.
142
+ - `parallelGroup` is DEPRECATED and ignored (PRD 832) — `dependsOn: [<slug>, …]` is the only ordering primitive; numbers are unique per project.
143
143
 
144
144
  ### `planId` (API-owned — never pass it)
145
145
 
@@ -168,11 +168,11 @@ Incident: sigma PRD `816-prepare-157-copy-citation-extras-patch` (2026-09-20) wr
168
168
 
169
169
  ---
170
170
 
171
- ## §8 Scope sizing — target ~15 min, ceiling 30 (data-driven, 2026-06)
171
+ ## §8 Scope sizing — target ≤10 min, ceiling 15 (data-driven, 2026-09)
172
172
 
173
- **Summary:** One PRD ≈ **~15 wall-clock minutes** of work. Empirically (400+ runs) median real run = **~7 min**, p90 = **21 min**; authored estimates ran 5–8× too high. **If you project >30 min, SPLIT.**
173
+ **Summary:** One PRD ≈ **≤10 wall-clock minutes** of work. Empirically (2026-09 calibration) wall p50 = **7.8 min**, 60% of runs ≤ 10 min; authored estimates ran 4× too high. **If you project >15 min, SPLIT.**
174
174
 
175
- **Rule:** Split larger work into sequential PRDs; reference the dependency in `# Implementation notes`. PRDs in the same `NN-` group run in parallel — don't put dependent work in the same group. e2e/publish work is the failure tail: **shard test suites to one spec per PRD; never run a full suite or an endpoint-polling publish in a single PRD** (§1/§5).
175
+ **Rule:** Split larger work into sequential PRDs; reference the dependency in `# Implementation notes`. e2e/publish work is the failure tail: **shard test suites to one spec per PRD; never run a full suite or an endpoint-polling publish in a single PRD** (§1/§5).
176
176
 
177
177
  ---
178
178
 
@@ -202,9 +202,9 @@ Before queueing a new PRD, verify each of these:
202
202
  - [ ] **§3 Smoke tests + verify-before-done:** Every deploy/migration step is followed by a test command that exits 1 on failure. Run the AC test command once before declaring done; never end the run on a red test.
203
203
  - [ ] **§4 Bounded generators:** Any search/seed loop has an explicit `MAX_ATTEMPTS` constant and surfaces failure on exhaustion.
204
204
  - [ ] **§5 Render deploys:** Deploy waits use a live URL check, not uptime/restart signals.
205
- - [ ] **§6 Frontmatter:** `title`, `cwd` (`~/Projects/<name>` preferred; path MUST exist on this machine), `estimateMinutes` present. `parallelGroup` only if intentionally interleaving cross-stream.
205
+ - [ ] **§6 Frontmatter:** `title`, `cwd` (`~/Projects/<name>` preferred; path MUST exist on this machine), `estimateMinutes` present. `parallelGroup` is deprecated — use `dependsOn` for ordering.
206
206
  - [ ] **§7 Self-contained:** No references to "the conversation" or external context. Paths and identifiers are inline. Body is clean UTF-8 — **no NUL/control bytes** (paste-from-PDF crashes the spawn). Quick check: `grep -qP '\x00' file && echo BAD`.
207
- - [ ] **§8 Scope:** Targets ~15 min, ceiling 30. If projected larger, split. e2e/publish sharded to one spec per PRD.
207
+ - [ ] **§8 Scope:** Targets ≤10 min, ceiling 15. If projected larger, split. e2e/publish sharded to one spec per PRD.
208
208
  - [ ] **§9 Failure surfacing:** Errors exit 1 with a diagnostic line. No silent `|| true` swallows. (`rateLimited` exit-1 is benign auto-pause, not a failure.)
209
209
  - [ ] **§11 Negative-assertion checks:** Any "this should produce NO output / NO match" check (a `grep` that should find nothing, a "no leftover X" guard) is written as an inverted conditional that exits 0 on the clean case. A bare `grep` whose success is "no match" exits 1 and trips the verifier `transcript_errors` downgrade even when the run is perfect.
210
210
  - [ ] **§12 End green:** The acceptance/test gate is the LAST thing the run does; any intentionally-failing step (TDD red test, expected-nonzero probe) runs EARLY, never after the gate, and is captured (`2>&1 | tail` inside a conditional) so it doesn't surface as a bare `is_error`/`Traceback` in the final portion of the transcript.
@@ -280,8 +280,16 @@ async function fetchUsage() {
280
280
  if (kind === 'meter_rate_limited') return { kind: 'meter_rate_limited', message: 'e2e stub', httpStatus: 429 };
281
281
  if (kind === 'transient') return { kind: 'transient', message: 'e2e stub', httpStatus: 503 };
282
282
  if (kind === 'auth') return { kind: 'auth', message: 'e2e stub', httpStatus: 401 };
283
+ // 'ok_weekly_binding': real-shape limits[] where the WEEKLY window binds (64%) while the
284
+ // session window reads 12% and a scoped entry reads 90% (must be ignored). Distinct values
285
+ // let an e2e prove the KPI was derived via bindingWindow(), not a flat five_hour fallback.
286
+ if (kind === 'ok_weekly_binding') {
287
+ const sessionReset = new Date(Date.now() + 3 * 3_600_000).toISOString();
288
+ const weeklyReset = new Date(Date.now() + (2 * 24 + 5) * 3_600_000).toISOString();
289
+ return { kind: 'ok', data: { usage: { five_hour: { utilization: 12, resets_at: sessionReset }, seven_day: { utilization: 64, resets_at: weeklyReset }, limits: [{ kind: 'session', group: 'session', percent: 12, severity: 'normal', resets_at: sessionReset, scope: null, is_active: false }, { kind: 'weekly_all', group: 'weekly', percent: 64, severity: 'normal', resets_at: weeklyReset, scope: null, is_active: true }, { kind: 'weekly_scoped', group: 'weekly', percent: 90, severity: 'warning', resets_at: weeklyReset, scope: { model: 'opus' }, is_active: false }], seven_day_sonnet: null, seven_day_opus: null, extra_usage: null }, subscriptionType: null, rateLimitTier: null, credentialsExpiresAt: null, fetchedAt: Date.now() } };
290
+ }
283
291
  // 'ok' stub returns a minimal valid payload.
284
- return { kind: 'ok', data: { usage: { five_hour: { utilization: 10, resets_at: null }, seven_day: { utilization: 10, resets_at: null }, seven_day_sonnet: null, seven_day_opus: null, extra_usage: null }, subscriptionType: null, rateLimitTier: null, credentialsExpiresAt: null, fetchedAt: Date.now() } };
292
+ return { kind: 'ok', data: { usage: { five_hour: { utilization: 10, resets_at: null }, seven_day: { utilization: 10, resets_at: null }, limits: [{ kind: 'session', group: 'session', percent: 10, severity: 'normal', resets_at: null, scope: null, is_active: false }, { kind: 'weekly_all', group: 'weekly', percent: 10, severity: 'normal', resets_at: null, scope: null, is_active: true }], seven_day_sonnet: null, seven_day_opus: null, extra_usage: null }, subscriptionType: null, rateLimitTier: null, credentialsExpiresAt: null, fetchedAt: Date.now() } };
285
293
  }
286
294
 
287
295
  await ensureHydrated();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * VoiceSettings — persists voice hotkey configuration to disk.
3
3
  *
4
- * Storage: ~/.config/session-manager/voice.json
4
+ * Storage: ~/.claude/session-manager/voice.json
5
5
  * Shape: {
6
6
  * accelerator: string, mode: 'hold'|'toggle', global: boolean, schemaVersion: 1,
7
7
  * // F5 device picker (optional, additive):
@@ -44,8 +44,14 @@ function defaultsForPlatform() {
44
44
  return { accelerator: 'Ctrl+Shift+Space', mode: 'hold', global: false, schemaVersion: SCHEMA_VERSION };
45
45
  }
46
46
 
47
+ let migrated = false;
47
48
  function storePath() {
48
- return path.join(os.homedir(), '.config', 'session-manager', 'voice.json');
49
+ const p = path.join(os.homedir(), '.claude', 'session-manager', 'voice.json');
50
+ if (!migrated) {
51
+ migrated = true;
52
+ config.migrateLegacyHomeFile(path.join(os.homedir(), '.config', 'session-manager', 'voice.json'), p);
53
+ }
54
+ return p;
49
55
  }
50
56
 
51
57
  // PRD F1 v2 §Chord schema. Anchored, requires ≥1 modifier so a bare
@@ -66,7 +66,7 @@ export interface ImportRef {
66
66
  /** Declared owner ids for the single-writer law over a project's
67
67
  * session-manager-operations/ root. Mirrors OWNERS in
68
68
  * src/main/lib/opsOwnership.cjs — one writer per namespace; everyone reads. */
69
- export type OpsWriter = 'epics' | 'scheduler' | 'project-home';
69
+ export type OpsWriter = 'epics' | 'scheduler' | 'project-home' | 'ui-prefs';
70
70
 
71
71
  export interface WriteResult {
72
72
  ok: boolean;
@@ -154,12 +154,23 @@ export interface UsageWindow {
154
154
  resets_at: string | null;
155
155
  }
156
156
 
157
+ export interface UsageLimitEntry {
158
+ kind: string;
159
+ group: string;
160
+ percent: number | null;
161
+ severity: string | null;
162
+ resets_at: string | null;
163
+ scope: { model?: { display_name?: string } } | null;
164
+ is_active: boolean;
165
+ }
166
+
157
167
  export interface UsageSnapshot {
158
168
  five_hour: UsageWindow | null;
159
169
  seven_day: UsageWindow | null;
160
170
  seven_day_sonnet: UsageWindow | null;
161
171
  seven_day_opus: UsageWindow | null;
162
172
  seven_day_oauth_apps: UsageWindow | null;
173
+ limits?: UsageLimitEntry[] | null;
163
174
  extra_usage: {
164
175
  is_enabled: boolean;
165
176
  monthly_limit: number | null;
@@ -329,7 +340,7 @@ export interface VoiceMarkWizardCompleteResult {
329
340
  /**
330
341
  * F8 — semantic turn-detection settings. MVP shape only; the actual
331
342
  * smart-turn-v3 ONNX model is not loaded in v1. Persisted at
332
- * ~/.config/session-manager/voice.json under `turnDetector`. See PRD F8 v2.
343
+ * ~/.claude/session-manager/voice.json under `turnDetector`. See PRD F8 v2.
333
344
  */
334
345
  export interface VoiceTurnDetectorState {
335
346
  enabled: boolean;
@@ -504,7 +515,7 @@ export interface ScheduleConfig {
504
515
  defaultCwd: string;
505
516
  /** Auto-fire policy. Default 'when-available'. */
506
517
  firePolicy: ScheduleFirePolicy;
507
- /** When firePolicy='when-available', fire only if five_hour utilization is
518
+ /** When firePolicy='when-available', fire only if binding-window utilization is
508
519
  * strictly below this percent. 0–100. Default 90. */
509
520
  utilizationThreshold: number;
510
521
  schemaVersion: 1;
@@ -858,8 +869,12 @@ export interface ScheduleStateSnapshot {
858
869
  launchBlocks?: Record<string, ScheduleLaunchBlock>;
859
870
  /** Degraded-mode launch env in force per persona. Empty when nothing is degraded. */
860
871
  launchMitigations?: Record<string, ScheduleLaunchMitigation>;
861
- /** Latest five_hour utilization percent (0–100) cached from billing.fetchUsage. null if unknown. */
872
+ /** Latest BINDING-window utilization percent (0–100) cached from billing.fetchUsage. null if unknown. */
862
873
  utilization: number | null;
874
+ /** Name/kind of the window `utilization` reads (e.g. 'five_hour', 'weekly_all'). null if unknown. */
875
+ utilizationWindow?: string | null;
876
+ /** Non-null while the when-available gate is holding the queue on utilization: which window, and its reset. */
877
+ utilizationHold?: { window: string | null; percent: number; threshold: number; resetsAt: string | null } | null;
863
878
  /** Poll health — last billing poll result; used to detect stale utilization. */
864
879
  pollHealth?: SchedulePollHealth;
865
880
  /** Effective concurrency cap and whether it's env-pinned or config-driven. */
@@ -1929,7 +1944,7 @@ export interface SessionManagerAPI {
1929
1944
  /** Create a new memory entry with starter frontmatter + body. */
1930
1945
  create: (name: string, description?: string, workspace?: string) => Promise<MemoryMutationResult>;
1931
1946
  /** Aggregate workspace memories into semantic clusters. `refresh:true` fires a cost-gated `claude -p` pass; otherwise returns the cache only. */
1932
- aggregate: (workspace: string, refresh?: boolean) => Promise<MemoryAggregateResult>;
1947
+ aggregate: (workspace: string, refresh?: boolean, cwd?: string) => Promise<MemoryAggregateResult>;
1933
1948
  /** Deterministic, zero-LLM-cost staleness report. `cwd` (optional) scopes the dead-repo-ref check. */
1934
1949
  stale: (workspace?: string, cwd?: string) => Promise<MemoryStaleResult>;
1935
1950
  };
@@ -158,13 +158,13 @@ contextBridge.exposeInMainWorld('api', {
158
158
  getHotkeyConfig: () => ipcRenderer.invoke('voice:get-hotkey-config'),
159
159
  setHotkeyConfig: (cfg) => ipcRenderer.invoke('voice:set-hotkey', cfg),
160
160
  setRecording: (recording) => ipcRenderer.send('voice:set-recording', !!recording),
161
- // F5 device picker prefs (~/.config/session-manager/voice.json `device` key).
161
+ // F5 device picker prefs (~/.claude/session-manager/voice.json `device` key).
162
162
  getDevicePref: () => ipcRenderer.invoke('voice:get-device-pref'),
163
163
  setDevicePref: (pref) => ipcRenderer.invoke('voice:set-device-pref', pref),
164
- // F7 first-run wizard state (~/.config/session-manager/voice.json `wizard` key).
164
+ // F7 first-run wizard state (~/.claude/session-manager/voice.json `wizard` key).
165
165
  getWizardState: () => ipcRenderer.invoke('voice:wizard-state'),
166
166
  markWizardComplete: () => ipcRenderer.invoke('voice:wizard-complete'),
167
- // F8 turn-detector settings (~/.config/session-manager/voice.json `turnDetector` key).
167
+ // F8 turn-detector settings (~/.claude/session-manager/voice.json `turnDetector` key).
168
168
  // MVP: settings persistence + kill switch only; no model is actually loaded in v1.
169
169
  getTurnDetector: () => ipcRenderer.invoke('voice:get-turn-detector'),
170
170
  setTurnDetector: (state) => ipcRenderer.invoke('voice:set-turn-detector', state),
@@ -315,8 +315,8 @@ contextBridge.exposeInMainWorld('api', {
315
315
  if (workspace) payload.workspace = workspace;
316
316
  return ipcRenderer.invoke('memory:create', payload);
317
317
  },
318
- aggregate: (workspace, refresh) =>
319
- ipcRenderer.invoke('memory:aggregate', refresh ? { workspace, refresh: true } : { workspace }),
318
+ aggregate: (workspace, refresh, cwd) =>
319
+ ipcRenderer.invoke('memory:aggregate', { workspace, ...(refresh ? { refresh: true } : {}), ...(cwd ? { cwd } : {}) }),
320
320
  stale: (workspace, cwd) =>
321
321
  ipcRenderer.invoke('memory:stale', { ...(workspace ? { workspace } : {}), ...(cwd ? { cwd } : {}) }),
322
322
  },
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: dev-lead
3
- description: Executes exactly one already-scoped PRD at a time, headless, start to finish — reads the PRD's Goal/Acceptance Criteria/Implementation notes and standards.md, implements it, verifies against its own AC, and reports. Has no visibility into the overall plan — that's architect's job. Not currently wired to run automatically; a PRD must name this persona explicitly (e.g. in its Implementation notes) for an executor to adopt it.
3
+ description: Executes exactly one already-scoped PRD at a time, headless, start to finish — reads the PRD's Goal/Acceptance Criteria/Implementation notes and standards.md, implements it, verifies against its own AC, and reports. Has no visibility into the overall plan — that's architect's job. This is the default persona a scheduled PRD runs as: the scheduler resolves a PRD's `agentType` frontmatter field (default `dev-lead`) to this file and launches the headless executor AS this persona via `--append-system-prompt`.
4
4
  tools: Read, Grep, Glob, Bash, Edit, Write
5
- model: fable
5
+ model: sonnet
6
6
  title: Engineering — Software Engineer
7
7
  ---
8
8
 
@@ -18,25 +18,34 @@ points you at), and the project's own files.
18
18
  notes, Out of scope — in that order. The Implementation notes exist so you don't have to
19
19
  re-derive file paths or signatures from scratch; use them, but verify by reading the actual
20
20
  code before relying on a claim in the PRD that might have drifted since it was authored.
21
- 2. **Read `standards.md`** (the PRD names its absolute path) before writing any code — Performance,
22
- Debugging, API-reuse, TDD, and Execution discipline all live there. Don't guess at these rules
23
- from memory; the file is the single source of truth and is meant to be read fresh each run.
24
- 3. **Build exactly what the PRD asks — no more, no less.** "Out of scope" is not a suggestion;
25
- resist the urge to also fix an adjacent thing you noticed. If something the PRD depends on
26
- turns out to be wrong or missing, say so in your final report rather than silently expanding
27
- scope to route around it.
28
- 4. **Verify against the PRD's own Acceptance Criteria before reporting done** — run the named test
29
- command, don't just assert it "should" pass. A PRD that ends on red is not done; follow
30
- `standards.md`'s Execution discipline for what to do instead of quietly giving up.
31
- 5. **Report what you actually did**, not what the PRD asked for — file paths touched, the test
21
+ 2. **Report what you actually did**, not what the PRD asked for — file paths touched, the test
32
22
  command's real output, anything you couldn't complete and why. `architect` (or whoever queued
33
23
  this PRD) reads this report to decide what happens next; a report that just restates the PRD is
34
24
  not useful.
35
25
 
26
+ ## Run contract (execute in this order — the scheduler grades you on it)
27
+
28
+ 0. ORIENT (≤ 8 tool calls): read the PRD's `Read first:` files and ONLY those. Do not survey the repo.
29
+ 1. RED: write or extend the named test so it fails for the right reason. Run it once, captured: `<test cmd> 2>&1 | tail -20 || true`.
30
+ 2. BUILD: make exactly the changes the AC names. Touch no file the PRD does not name unless the build breaks without it — then say so in the report.
31
+ 3. GATE: run the PRD's single gate line, `timeout`-wrapped, in the foreground, as the LAST command of the run. Red → fix it or `exit 1`; never continue on red.
32
+ 4. COMMIT: `git add <exact paths>` then `git commit -m "<type>(<scope>): <summary>"`. Never `git add -A` / `git add .`; never stash, reset, checkout or clean anything you did not create.
33
+ 5. VERDICT: last line `SCHEDULER_VERDICT: PASS` only if step 3 was green AND step 4 landed; otherwise `SCHEDULER_VERDICT: FAIL <reason>` and `exit 1`.
34
+
35
+ Verifier traps — each has parked a green run before:
36
+ - Absence checks: `if grep …; then echo HALT; exit 1; fi; echo clean` — a bare no-match grep exits 1 and reads as an error.
37
+ - Expected failures (a red test, a probe) run EARLY and captured; nothing may error after the gate.
38
+ - A probe that errors: re-run it corrected at once, or print `# expected/handled: <why>` on the next line.
39
+ - A `timeout` exit 124 you expected: branch on 124 and print OK; never leave a bare 124.
40
+ - Never background a verification, never call ScheduleWakeup/Monitor/agents, never invoke /develop or queue work — this process has no next turn.
41
+ - `gh pr checks` exits 8 while pending: wrap it and annotate; poll with the identical command each time.
42
+ - Retry a transient failure with the SAME command text so the verifier pairs the recovery with the failure.
43
+
44
+ Out of contract: anything past the AC list. If the PRD looks wrong, do the AC as written and say why in the report.
45
+ Reasoning for every line above: `standards.md` (the PRD names its path) — consult one section when a line here is unclear; do not re-read it every run.
46
+
36
47
  ## What you don't do
37
48
 
38
49
  - Don't decompose new PRDs, re-plan the sequence, or second-guess whether this PRD should exist —
39
50
  if scope looks wrong, say so in your report; don't unilaterally expand or split the work.
40
- - Don't assume you're running because a human picked you as an Epic's Actor — PRD execution has no
41
- persona-selection mechanism today. You're running because a PRD's own text named you explicitly.
42
51
  - Don't fork `standards.md`'s rules into this file — reference it, don't restate it.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: validator
3
+ description: Validates a finished PLAN (the PRDs that share one planId) once, after its last PRD lands — re-runs each PRD's gate, checks every acceptance criterion against the real tree, reviews the plan's combined diff, and reports one VERIFIED/REFUTED verdict per PRD via sentinel lines. Runs headless as a scheduled PRD (agentType: validator); never edits product code and never queues work.
4
+ tools: Read, Grep, Glob, Bash
5
+ model: sonnet
6
+ title: Engineering — Plan Validator
7
+ ---
8
+
9
+ You are validator. You judge whether a plan's PRDs actually landed what they promised. You do not fix anything, you do not re-implement, you do not queue PRDs — your output is evidence and verdicts, read by the architect who owns the plan.
10
+
11
+ ## Inputs
12
+
13
+ Your PRD's `# Acceptance criteria` lists the plan's PRD slugs and where each PRD file lives (`session-manager-operations/scheduler/epics/<epic>/prds/` while queued, `prds-archived/` once terminal — locate by slug in either). Everything else comes from the working tree and `git log`.
14
+
15
+ ## Procedure (in this order)
16
+
17
+ 1. For each PRD slug, find its landed commit(s): `git log --oneline --since=<plan start> --grep=<slug>`, then the paths its AC names. No commit and no diff on an implementation PRD → REFUTED ("nothing landed").
18
+ 2. For each AC line, produce ONE line of evidence: the `file:line` you read, or the captured output of the command it names. Re-run each PRD's gate command exactly as written (foreground, `timeout`-wrapped).
19
+ 3. Review the plan's combined diff once: `git diff <base>..HEAD --stat`, then the diff. Run `/code-review` and `/security-review` on it if available in this environment — synchronously, never as a background agent; otherwise self-review for correctness, unsafe input handling, secrets, path traversal, and duplication of an existing helper.
20
+ 4. Write the review record to the path your PRD names (Markdown: one section per PRD with verdict + evidence, then a Findings section ranked Critical / Important / Minor with `file:line`), `git add` that ONE file, commit it.
21
+ 5. Finish with the sentinel block — one line per PRD, then the scheduler's own verdict line:
22
+ VALIDATION: <slug> VERIFIED
23
+ VALIDATION: <slug> REFUTED — <one-line reason>
24
+ SCHEDULER_VERDICT: PASS
25
+ PASS means the validation itself completed and the record is committed — a REFUTED PRD is still a successful validation. FAIL only if you could not run the procedure.
26
+
27
+ ## Rules
28
+
29
+ - Exit 0, a green queue row, or a confident run report are not evidence; only the tree and the commands are.
30
+ - Never edit files outside the review record. Never `git stash`, `git reset`, `git checkout --`, or `git clean`.
31
+ - Bound every command with `timeout`; run nothing in the background; do not call ScheduleWakeup, Monitor, or /develop.
32
+ - Absence checks must exit 0 when clean (`if grep …; then echo HALT; exit 1; fi; echo clean`).
33
+ - Do the review once; do not loop on findings — report them.
@@ -1,2 +0,0 @@
1
- import{at as ue,ak as e,az as f,au as F,av as I,aU as Be,b0 as ge,ao as _e,aL as Oe,aT as He,m as Ie,v as Le}from"./index-CCl4tz-u.js";import"./monaco-editor-BW5C4Iv1.js";function ce(){return{promptCount:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,toolCallCount:0,sessionCount:0,errorCount:0,activeMinutes:0,estimatedCostUsd:0}}function ke(t,s){t.promptCount+=s.promptCount,t.inputTokens+=s.inputTokens,t.outputTokens+=s.outputTokens,t.cacheReadTokens+=s.cacheReadTokens,t.cacheCreationTokens+=s.cacheCreationTokens,t.toolCallCount+=s.toolCallCount,t.sessionCount+=s.sessionCount,t.errorCount+=s.errorCount,t.activeMinutes+=s.activeMinutes,t.estimatedCostUsd+=s.estimatedCostUsd}function Ke(t){return t.promptCount>0||t.sessionCount>0||t.toolCallCount>0||t.inputTokens>0||t.outputTokens>0}function We(t,s){const n=s===null,c=[],o=ce(),r={};let a=0;for(const i of t.days){const d=ce();let u=!1;for(const[l,p]of Object.entries(i.byProject))if(!(!n&&!s.has(l))){ke(d,p),ke(o,p),Ke(p)&&(u=!0);for(const[x,j]of Object.entries(p.byModel)){const T=r[x]??(r[x]={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,costUsd:0});T.inputTokens+=j.inputTokens,T.outputTokens+=j.outputTokens,T.cacheReadTokens+=j.cacheReadTokens,T.cacheCreationTokens+=j.cacheCreationTokens,T.costUsd+=j.costUsd,j.estimated&&(T.estimated=!0)}}u&&(a+=1),c.push({date:i.date,totals:d})}return{days:c,totals:o,prevTotals:n?t.prevTotals:ce(),prevAvailable:n,byModelTotals:r,activeDays:a}}function we(t,s){if(!s)return{pct:null,direction:"flat"};const n=(t-s)/s*100,c=n>0?"up":n<0?"down":"flat";return{pct:n,direction:c}}function ze(t,s,n){return s<=0?0:t/s*n}const Me={in:"In tokens",out:"Out tokens",total:"Total tokens",prompts:"Prompts",sessions:"Sessions",time:"Time",spend:"Spend"};function R(t,s){const n=t.inputTokens+t.cacheReadTokens+t.cacheCreationTokens;switch(s){case"in":return n;case"out":return t.outputTokens;case"total":return n+t.outputTokens;case"prompts":return t.promptCount;case"sessions":return t.sessionCount;case"time":return t.activeMinutes;case"spend":return t.estimatedCostUsd}}function Ge(t,s){const n=R(t.totals,s),c=t.prevAvailable?we(n,R(t.prevTotals,s)):null,o=t.days.map(r=>R(r.totals,s));return{value:n,delta:c,series:o}}function be(t,s){const n=new Map,c=Math.ceil(t.length/2);t.forEach((a,i)=>{for(const[d,u]of Object.entries(a.byProject)){let l=n.get(d);l||(l={totals:{promptCount:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,toolCallCount:0,sessionCount:0,errorCount:0,activeMinutes:0,estimatedCostUsd:0},toolBreakdown:{},firstHalf:0,secondHalf:0},n.set(d,l)),l.totals.promptCount+=u.promptCount,l.totals.inputTokens+=u.inputTokens,l.totals.outputTokens+=u.outputTokens,l.totals.cacheReadTokens+=u.cacheReadTokens,l.totals.cacheCreationTokens+=u.cacheCreationTokens,l.totals.toolCallCount+=u.toolCallCount,l.totals.sessionCount+=u.sessionCount,l.totals.errorCount+=u.errorCount,l.totals.activeMinutes+=u.activeMinutes,l.totals.estimatedCostUsd+=u.estimatedCostUsd;for(const[x,j]of Object.entries(u.toolBreakdown))l.toolBreakdown[x]=(l.toolBreakdown[x]??0)+j;const p=R(u,s);i<c?l.firstHalf+=p:l.secondHalf+=p}});const o=Array.from(n.entries()).map(([a,i])=>{let d="",u=-1;for(const[l,p]of Object.entries(i.toolBreakdown))p>u&&(u=p,d=l);return{projectDir:a,value:R(i.totals,s),sharePct:0,trend:we(i.secondHalf,i.firstHalf),sessions:i.totals.sessionCount,topTool:d}}),r=o.reduce((a,i)=>a+i.value,0);for(const a of o)a.sharePct=r>0?a.value/r*100:0;return o.sort((a,i)=>i.value-a.value)}const Ye=["date","cost_usd","input_tokens","output_tokens","cache_write_tokens","cache_read_tokens","total_tokens","cache_hit_pct","num_models","top_model"];function Ve(t){return/[",\n]/.test(t)?`"${t.replace(/"/g,'""')}"`:t}function ve(t){return t.inputTokens+t.outputTokens+t.cacheReadTokens+t.cacheCreationTokens}function Xe(t){const s=t.map(n=>{const c=n.inputTokens+n.outputTokens+n.cacheCreationTokens+n.cacheReadTokens,o=n.inputTokens+n.cacheReadTokens+n.cacheCreationTokens,r=o>0?n.cacheReadTokens/o*100:0,a=Object.entries(n.byModel).filter(([,l])=>ve(l)>0);let i="",d=-1;for(const[l,p]of a){const x=ve(p);x>d&&(d=x,i=l)}return[n.date,n.costUsd.toFixed(4),String(n.inputTokens),String(n.outputTokens),String(n.cacheCreationTokens),String(n.cacheReadTokens),String(c),r.toFixed(1),String(a.length),i?ue(i):""].map(Ve).join(",")});return[Ye.join(","),...s].join(`
2
- `)}const Ze={folderCount:0,sessionCount:0,promptCount:0,estimatedCostUsd:0,totalTokens:0};function qe(t,s){t.promptCount+=s.promptCount,t.inputTokens+=s.inputTokens,t.outputTokens+=s.outputTokens,t.cacheReadTokens+=s.cacheReadTokens,t.cacheCreationTokens+=s.cacheCreationTokens,t.toolCallCount+=s.toolCallCount,t.sessionCount+=s.sessionCount,t.errorCount+=s.errorCount,t.activeMinutes+=s.activeMinutes,t.estimatedCostUsd+=s.estimatedCostUsd;for(const[n,c]of Object.entries(s.toolBreakdown??{}))t.toolBreakdown[n]=(t.toolBreakdown[n]??0)+c;for(const[n,c]of Object.entries(s.byModel??{})){const o=t.byModel[n]??(t.byModel[n]={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,costUsd:0});o.inputTokens+=c.inputTokens,o.outputTokens+=c.outputTokens,o.cacheReadTokens+=c.cacheReadTokens,o.cacheCreationTokens+=c.cacheCreationTokens,o.costUsd+=c.costUsd,c.estimated&&(o.estimated=!0)}}function Je(t,s){const n={};for(const[c,o]of Object.entries(t.byModel??{}))n[c]={...o};return{...t,projectDir:s,toolBreakdown:{...t.toolBreakdown??{}},byModel:n}}function Qe(t,s){const n={...Ze},c=new Set,o=t.map(r=>{const a={};for(const[i,d]of Object.entries(r.byProject)){const u=s[i];if(!u){c.add(i),n.sessionCount+=d.sessionCount,n.promptCount+=d.promptCount,n.estimatedCostUsd+=d.estimatedCostUsd,n.totalTokens+=d.inputTokens+d.outputTokens;continue}const l=a[u];l?qe(l,d):a[u]=Je(d,u)}return{...r,byProject:a}});return n.folderCount=c.size,{days:o,excluded:n}}function et(t){return t.folderCount>0}function tt(t){const s=[`${t.folderCount} transcript folder${t.folderCount===1?"":"s"} with no resolvable working directory`];return t.sessionCount&&s.push(`${t.sessionCount.toLocaleString()} sessions`),t.promptCount&&s.push(`${t.promptCount.toLocaleString()} prompts`),t.estimatedCostUsd>=.005&&s.push(`$${t.estimatedCostUsd.toFixed(2)}`),`Excluded: ${s.join(" · ")}`}const st=["in","out","total","prompts","sessions","time","spend"],nt=[{value:30,label:"30d"},{value:60,label:"60d"},{value:90,label:"90d"},{value:0,label:"All time"}];function Te({value:t,options:s,onChange:n}){return e.jsx("div",{className:"flex gap-0.5 p-0.5 rounded bg-bg-elev shadow-[inset_0_0_0_1px_rgba(0,0,0,0.06)] text-[11px]",children:s.map(c=>e.jsx("button",{onClick:()=>n(c.value),className:`px-2 py-1 rounded whitespace-nowrap font-mono ${t===c.value?"bg-bg-hi text-fg shadow-sm":"text-fg-faint hover:text-fg"}`,children:c.label},String(c.value)))})}function ot({measure:t,onMeasure:s,range:n,onRange:c,projectsShown:o,projectsTotal:r,fromDate:a,toDate:i,onRefresh:d,onExport:u,exportDisabled:l}){return e.jsxs("div",{className:"sticky top-0 z-10 border-b border-line px-4 py-2.5 flex flex-wrap items-center gap-3 backdrop-blur-sm",style:{backgroundColor:"rgba(246,239,225,0.93)"},children:[e.jsx(Te,{value:t,options:st.map(p=>({value:p,label:Me[p]})),onChange:s}),e.jsx(Te,{value:n,options:nt,onChange:c}),e.jsxs("span",{className:"text-[11px] text-fg-faint font-mono",children:[o," / ",r," projects"]}),e.jsxs("span",{className:"text-[11px] text-fg-faint font-mono",children:[a," → ",i]}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:d,className:"px-2 py-1 text-[11px] border border-line rounded hover:text-fg hover:bg-bg-hi text-fg-faint",title:"Re-fetch the rollup (no scan)",children:"↻ Refresh"}),e.jsx("button",{onClick:u,disabled:l,className:"px-2 py-1 text-[11px] border border-line rounded hover:text-fg hover:bg-bg-hi text-fg-faint disabled:opacity-40 disabled:pointer-events-none",children:"Export CSV"})]})}function U(t){const s=Number.isFinite(t)?t:0,n=Math.abs(s)<1?4:2;return`$${s.toFixed(n)}`}function L(t){const s=Number.isFinite(t)?t:0,n=Math.abs(s);return n>=1e9?`${(s/1e9).toFixed(1)}B`:n>=1e6?`${(s/1e6).toFixed(1)}M`:n>=1e3?`${(s/1e3).toFixed(1)}K`:`${Math.round(s)}`}function K(t){const s=Number.isFinite(t)?t:0;return Math.round(s).toLocaleString("en-US")}function Se(t){const s=Math.max(0,Math.round(Number.isFinite(t)?t:0));if(s<60)return`${s}m`;const n=Math.floor(s/60),c=s%60;return c===0?`${n}h`:`${n}h ${c}m`}function $e(t){return Number.isFinite(t)?t>0&&t<1?"<1%":`${t.toFixed(1)}%`:"0.0%"}const S="rounded-card bg-bg-hi border border-line",at="#ded2bd",ct="#6f7d52";function D({kicker:t,title:s,right:n}){return e.jsxs("div",{className:"flex items-baseline justify-between gap-3 mb-3",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[10.5px] uppercase tracking-wider text-accent font-mono",children:t}),e.jsx("h2",{className:"font-serif text-[25px] leading-tight tracking-tight text-fg",children:s})]}),n]})}function ee(t,s){switch(t){case"spend":return U(s);case"in":case"out":case"total":return L(s);case"time":return Se(s);default:return K(s)}}function rt({measure:t,headline:s,activeDays:n,projectsTouched:c,totalSessions:o}){const r=n>0?s.value/n:0,a=o>0?s.value/o:0,{delta:i}=s,d=i&&(t==="spend"?i.direction==="down":i.direction==="up"),u=i&&i.direction!=="flat"&&(t==="spend"?i.direction==="up":i.direction==="down"),l=d?"text-delta-good":u?"text-delta-bad":"text-fg-faint";return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"01 · overview",title:Me[t]}),e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[52px] leading-none font-mono tracking-tight text-fg",children:ee(t,s.value)}),i?e.jsxs("div",{className:`text-xs font-mono mt-2 ${l}`,children:[i.direction==="up"?"▲":i.direction==="down"?"▼":"·"," ",$e(Math.abs(i.pct??0))," vs prior period"]}):e.jsx("div",{className:"text-xs text-fg-faint mt-2",children:"no prior window to compare"})]}),e.jsx("div",{className:"flex-1 min-w-0 self-stretch",children:e.jsx(it,{series:s.series})})]}),e.jsxs("div",{className:"grid grid-cols-4 gap-3 mt-4 pt-3 border-t border-line",children:[e.jsx(Q,{label:"per active day",value:ee(t,r)}),e.jsx(Q,{label:"per session",value:ee(t,a)}),e.jsx(Q,{label:"active days",value:K(n)}),e.jsx(Q,{label:"projects touched",value:K(c)})]})]})}function Q({label:t,value:s}){return e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] text-fg-faint uppercase tracking-wider",children:t}),e.jsx("div",{className:"text-[17px] font-mono tracking-tight text-fg-dim",children:s})]})}function it({series:t}){if(t.length<2)return null;const c=Math.max(...t,0),o=Math.min(...t,0),r=c-o||1,a=160/(t.length-1),i=t.map((u,l)=>`${l*a},${48-(u-o)/r*48}`).join(" "),d=`0,48 ${i} 160,48`;return e.jsxs("svg",{viewBox:"0 0 160 48",preserveAspectRatio:"none",className:"w-full h-full min-h-[48px]",children:[e.jsx("polygon",{points:d,fill:"#b85c34",opacity:.15}),e.jsx("polyline",{points:i,fill:"none",stroke:"#b85c34",strokeWidth:1.5,vectorEffect:"non-scaling-stroke"})]})}const Ce="sm.history.budgetCapUsd",ye=50;function lt({days:t}){const[s,n]=f.useState(()=>{try{const i=localStorage.getItem(Ce),d=i===null?NaN:Number(i);return Number.isFinite(d)&&d>0?d:ye}catch{return ye}});f.useEffect(()=>{try{localStorage.setItem(Ce,String(s))}catch{}},[s]);const{mtdSpend:c,projectedSpend:o}=f.useMemo(()=>{const i=new Date,d=i.toLocaleDateString("en-CA").slice(0,7),u=new Date(i.getFullYear(),i.getMonth()+1,0).getDate(),l=i.getDate();let p=0;for(const x of t)if(x.date.startsWith(d))for(const j of Object.values(x.byProject))p+=j.estimatedCostUsd;return{mtdSpend:p,projectedSpend:ze(p,l,u)}},[t]),r=o>s,a=s>0?Math.min(100,c/s*100):0;return e.jsxs("div",{className:`${S} p-3`,children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("h3",{className:"text-[10.5px] uppercase tracking-wider text-fg-faint font-mono",children:"Monthly budget"}),e.jsxs("label",{className:"flex items-center gap-1.5 text-[10px] text-fg-faint",children:["cap",e.jsx("span",{children:"$"}),e.jsx("input",{type:"number",min:1,step:1,value:s,onChange:i=>{const d=Number(i.target.value);Number.isFinite(d)&&d>0&&n(d)},className:"w-16 bg-bg border border-line rounded px-1.5 py-0.5 text-xs text-fg font-mono"})]})]}),e.jsxs("div",{className:"flex items-center gap-4 text-xs font-mono mb-1.5",children:[e.jsxs("span",{className:"text-fg",children:[U(c)," ",e.jsx("span",{className:"text-fg-faint",children:"month-to-date"})]}),e.jsxs("span",{className:r?"text-delta-bad":"text-fg-dim",children:[U(o)," ",e.jsx("span",{className:"text-fg-faint",children:"projected"})]})]}),e.jsx("div",{className:"h-1.5 rounded bg-bg-hi overflow-hidden",children:e.jsx("div",{className:`h-full ${r?"bg-delta-bad":"bg-sage"}`,style:{width:`${Math.max(a,a>0?2:0)}%`}})}),e.jsx("div",{className:`text-[10px] mt-1 font-mono ${r?"text-delta-bad":"text-fg-faint"}`,children:r?`projected to exceed $${s.toFixed(0)} cap`:`${a.toFixed(0)}% of $${s.toFixed(0)} cap`})]})}function dt({chips:t,keep:s,onToggle:n,onIsolate:c,onShowAll:o}){const r=s===null;return e.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[e.jsxs("button",{onClick:o,className:`px-2 py-1 rounded text-[11px] font-mono border ${r?"bg-accent text-white border-accent":"border-line text-fg-faint hover:text-fg hover:bg-bg-hi"}`,children:["show all (",t.length,")"]}),t.map(a=>{const i=r||s.has(a.projectDir);return e.jsxs("button",{onClick:d=>d.shiftKey?c(a.projectDir):n(a.projectDir),title:a.projectDir,className:`inline-flex items-center gap-1.5 px-2 py-1 rounded text-[11px] font-mono border ${i?"border-line text-fg-dim bg-bg-hi":"border-line text-fg-faint opacity-40 hover:opacity-70"}`,children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full shrink-0",style:{backgroundColor:F(a.projectDir)}}),I(a.projectDir)??a.projectDir]},a.projectDir)})]})}function ut(t,s){const n=new Map;for(const o of t)for(const[r,a]of Object.entries(o.values))n.set(r,(n.get(r)??0)+a);const c=Array.from(n.entries()).sort((o,r)=>r[1]-o[1]).map(([o])=>o);return t.map(o=>{const r=c.reduce((d,u)=>d+(o.values[u]??0),0);let a=0;const i=c.map(d=>{const u=o.values[d]??0;if(r<=0)return{projectDir:d,value:u,y0:0,y1:0};const l=s==="share"?u/r:u,p=a,x=a+l;return a=x,{projectDir:d,value:u,y0:p,y1:x}});return{date:o.date,bands:i,total:r}})}const re=720,ie=220,le=44,pt=20;function xt(t,s){return t==="spend"?U(s):L(s)}function ht({days:t,measure:s,selected:n,onSelect:c}){const[o,r]=f.useState("absolute"),a=f.useMemo(()=>ut(t,o),[t,o]),i=f.useMemo(()=>{if(o==="share")return 1;let m=0;for(const b of a)m=Math.max(m,b.total);return m||1},[a,o]),d=re-le,u=ie-pt,l=a.length>1?d/(a.length-1):d,p=m=>le+m*l,x=m=>u-m/i*u,j=Math.max(1,Math.ceil(a.length/8)),T=4,g=Array.from({length:T+1},(m,b)=>i/T*b);return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"02 · over time",title:"Trend by project",right:e.jsx("div",{className:"flex border border-line rounded overflow-hidden text-[10px] font-mono",children:["absolute","share"].map(m=>e.jsx("button",{onClick:()=>r(m),className:`px-2 py-0.5 ${o===m?"bg-accent text-white":"text-fg-faint hover:text-fg"}`,children:m},m))})}),e.jsxs("svg",{width:"100%",viewBox:`0 0 ${re} ${ie}`,className:"overflow-visible",children:[g.map((m,b)=>e.jsxs("g",{children:[e.jsx("line",{x1:le,x2:re,y1:x(m),y2:x(m),stroke:"currentColor",className:"text-line",strokeWidth:1}),e.jsx("text",{x:0,y:x(m)+3,fontSize:9,className:"fill-fg-faint",children:o==="share"?`${Math.round(m*100)}%`:xt(s,m)})]},b)),a.map((m,b)=>{if(b===0)return null;const W=a[b-1];return m.bands.map((y,se)=>{const z=W.bands[se],G=n!==null&&n!==y.projectDir,Y=G?at:F(y.projectDir),V=[`${p(b-1)},${x(z.y0)}`,`${p(b)},${x(y.y0)}`,`${p(b)},${x(y.y1)}`,`${p(b-1)},${x(z.y1)}`].join(" ");return e.jsx("polygon",{points:V,fill:Y,opacity:G?.6:.75,className:"cursor-pointer",onClick:()=>c(n===y.projectDir?null:y.projectDir),children:e.jsx("title",{children:I(y.projectDir)??y.projectDir})},`${y.projectDir}-${b}`)})}),a.map((m,b)=>b%j!==0&&b!==a.length-1?null:e.jsx("text",{x:p(b),y:ie-4,fontSize:9,textAnchor:"middle",className:"fill-fg-faint",children:m.date.slice(5)},m.date))]})]})}function ft({rows:t,measure:s,selected:n,onSelect:c}){return t.length?e.jsxs("div",{className:`${S} overflow-hidden`,children:[e.jsx("div",{className:"p-4 pb-0",children:e.jsx(D,{kicker:"03 · by project",title:"Ranking"})}),e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{className:"bg-bg-elev",children:e.jsx("tr",{children:["project","share","value","share %","trend","sessions","top tool"].map(o=>e.jsx("th",{className:"text-left px-3 py-2 text-fg-faint uppercase tracking-wider whitespace-nowrap",children:o},o))})}),e.jsx("tbody",{children:t.map(o=>{const r=F(o.projectDir),a=n===o.projectDir;return e.jsxs("tr",{onClick:()=>c(o.projectDir),className:`border-t border-line hover:bg-bg-elev cursor-pointer ${a?"bg-bg-elev":""}`,children:[e.jsx("td",{className:"px-3 py-2 font-mono text-fg-dim",children:e.jsxs("span",{className:"inline-flex items-center gap-1.5",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full shrink-0",style:{backgroundColor:r}}),I(o.projectDir)??o.projectDir]})}),e.jsx("td",{className:"px-3 py-2 w-32",children:e.jsx("div",{className:"h-1.5 rounded bg-bg-hi overflow-hidden",children:e.jsx("div",{className:"h-full",style:{width:`${Math.max(o.sharePct,o.sharePct>0?2:0)}%`,backgroundColor:r}})})}),e.jsx("td",{className:"px-3 py-2 text-fg-faint font-mono",children:ee(s,o.value)}),e.jsxs("td",{className:"px-3 py-2 text-fg-faint font-mono",children:[o.sharePct.toFixed(1),"%"]}),e.jsx("td",{className:"px-3 py-2 font-mono",children:o.trend.pct===null?e.jsx("span",{className:"text-fg-faint",children:"—"}):e.jsxs("span",{className:o.trend.direction==="up"?"text-delta-good":o.trend.direction==="down"?"text-delta-bad":"text-fg-faint",children:[o.trend.direction==="up"?"▲":o.trend.direction==="down"?"▼":"·"," ",$e(Math.abs(o.trend.pct))]})}),e.jsx("td",{className:"px-3 py-2 text-fg-faint font-mono",children:K(o.sessions)}),e.jsx("td",{className:"px-3 py-2 text-fg-faint font-mono",children:o.topTool||"—"})]},o.projectDir)})})]})]}):e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"03 · by project",title:"Ranking"}),e.jsx("div",{className:"text-xs text-fg-faint",children:"no projects in range"})]})}function mt({projectDir:t,totals:s,byModel:n,toolBreakdown:c,onClear:o}){var l;const r=R(s,"in"),a=R(s,"out"),i=Object.entries(n).sort((p,x)=>x[1].costUsd-p[1].costUsd),d=Object.entries(c).sort((p,x)=>x[1]-p[1]),u=((l=d[0])==null?void 0:l[1])??0;return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"04 · drill in",title:I(t)??t,right:e.jsx("button",{onClick:o,className:"text-[11px] text-fg-faint hover:text-fg font-mono",children:"clear ✕"})}),e.jsx("div",{className:"flex items-center gap-1.5 -mt-2 mb-3",children:e.jsx("span",{className:"w-1.5 h-1.5 rounded-full shrink-0",style:{backgroundColor:F(t)}})}),e.jsxs("div",{className:"grid grid-cols-6 gap-3 mb-4",children:[e.jsx(H,{label:"prompts",value:K(s.promptCount)}),e.jsx(H,{label:"sessions",value:K(s.sessionCount)}),e.jsx(H,{label:"in tokens",value:L(r)}),e.jsx(H,{label:"out tokens",value:L(a)}),e.jsx(H,{label:"spend",value:U(s.estimatedCostUsd)}),e.jsx(H,{label:"time",value:Se(s.activeMinutes)})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] text-fg-faint uppercase tracking-wider mb-1.5",children:"model mix"}),i.length===0?e.jsx("div",{className:"text-xs text-fg-faint",children:"no model data"}):e.jsx("div",{className:"space-y-1",children:i.map(([p,x])=>e.jsxs("div",{className:"flex items-center justify-between text-[11px] font-mono text-fg-dim",children:[e.jsx("span",{children:ue(p)}),e.jsx("span",{className:"text-fg-faint",children:U(x.costUsd)})]},p))})]}),e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] text-fg-faint uppercase tracking-wider mb-1.5",children:"tool mix"}),d.length===0?e.jsx("div",{className:"text-xs text-fg-faint",children:"no tool data"}):e.jsx("div",{className:"space-y-1",children:d.slice(0,6).map(([p,x])=>e.jsxs("div",{className:"flex items-center gap-2 text-[11px]",children:[e.jsx("span",{className:"w-20 shrink-0 truncate text-fg-dim font-mono",children:p}),e.jsx("div",{className:"flex-1 h-1.5 rounded bg-bg-hi overflow-hidden",children:e.jsx("div",{className:"h-full bg-hive-teal",style:{width:`${u>0?x/u*100:0}%`}})}),e.jsx("span",{className:"text-fg-faint font-mono w-8 text-right",children:x})]},p))})]})]})]})}function H({label:t,value:s}){return e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] text-fg-faint uppercase tracking-wider",children:t}),e.jsx("div",{className:"text-[17px] font-mono tracking-tight text-fg",children:s})]})}function jt({byModelTotals:t}){const s=Object.entries(t).sort((c,o)=>o[1].costUsd-c[1].costUsd),n=s.reduce((c,[,o])=>c+o.costUsd,0);return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"05 · composition",title:"Model mix",right:e.jsxs("span",{className:"text-xs font-mono text-fg-faint",children:[U(n)," total"]})}),s.length===0?e.jsx("div",{className:"text-xs text-fg-faint",children:"no model data in range"}):e.jsx("div",{className:"space-y-2",children:s.map(([c,o])=>{const r=n>0?o.costUsd/n*100:0,a=F(c);return e.jsxs("div",{children:[e.jsxs("div",{className:"flex items-center justify-between text-xs mb-0.5",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-fg-dim truncate",children:[e.jsx("span",{className:"w-2 h-2 rounded-sm shrink-0",style:{backgroundColor:a}}),ue(c),o.estimated&&e.jsx("span",{className:"text-fg-faint",children:"(est.)"})]}),e.jsxs("span",{className:"text-fg-faint whitespace-nowrap ml-2",children:[U(o.costUsd)," · ",r.toFixed(0),"%"]})]}),e.jsx("div",{className:"h-1.5 rounded bg-bg-hi overflow-hidden",children:e.jsx("div",{className:"h-full",style:{width:`${Math.max(r,r>0?2:0)}%`,backgroundColor:a}})})]},c)})})]})}function gt({rows:t}){const s=t.slice(0,3),n=s.reduce((r,a)=>r+a.sharePct,0),c=t.slice(3),o=c.reduce((r,a)=>r+a.sharePct,0);return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"05 · composition",title:"Concentration",right:e.jsxs("span",{className:"text-xs font-mono text-fg-faint",children:["top 3 = ",n.toFixed(0),"%"]})}),t.length===0?e.jsx("div",{className:"text-xs text-fg-faint",children:"no projects in range"}):e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"flex h-3 rounded overflow-hidden mb-2",children:t.map(r=>e.jsx("div",{style:{width:`${Math.max(r.sharePct,.5)}%`,backgroundColor:F(r.projectDir)},title:`${I(r.projectDir)??r.projectDir} · ${r.sharePct.toFixed(1)}%`},r.projectDir))}),e.jsx("div",{className:"space-y-1 mb-2",children:s.map(r=>e.jsxs("div",{className:"flex items-center justify-between text-[11px] font-mono",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-fg-dim truncate",children:[e.jsx("span",{className:"w-1.5 h-1.5 rounded-full shrink-0",style:{backgroundColor:F(r.projectDir)}}),I(r.projectDir)??r.projectDir]}),e.jsxs("span",{className:"text-fg-faint",children:[r.sharePct.toFixed(1),"%"]})]},r.projectDir))}),c.length>0&&e.jsxs("p",{className:"text-[10px] text-fg-faint",children:[c.length," more project",c.length===1?"":"s"," account for ",o.toFixed(1),"% (long tail)."]})]})]})}const te=30,de=9,P=(te+de)*2;function kt({totals:t}){const s=t.inputTokens+t.cacheReadTokens+t.cacheCreationTokens,n=s>0?t.cacheReadTokens/s*100:0,c=2*Math.PI*te,o=P/2;return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"05 · composition",title:"Cache"}),e.jsxs("div",{className:"flex items-center gap-4",children:[e.jsxs("div",{className:"relative shrink-0",style:{width:P,height:P},children:[e.jsxs("svg",{width:P,height:P,viewBox:`0 0 ${P} ${P}`,className:"-rotate-90",children:[e.jsx("circle",{cx:o,cy:o,r:te,fill:"none",stroke:"currentColor",className:"text-bg-hi",strokeWidth:de}),e.jsx("circle",{cx:o,cy:o,r:te,fill:"none",stroke:ct,strokeWidth:de,strokeDasharray:c,strokeDashoffset:c*(1-n/100),strokeLinecap:"round"})]}),e.jsxs("div",{className:"absolute inset-0 flex items-center justify-center text-[13px] font-mono text-fg",children:[n.toFixed(0),"%"]})]}),e.jsxs("div",{className:"text-[11px] space-y-1 font-mono",children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("span",{className:"text-fg-faint",children:"reused (cache read)"}),e.jsx("span",{className:"text-fg-dim",children:L(t.cacheReadTokens)})]}),e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("span",{className:"text-fg-faint",children:"written to cache"}),e.jsx("span",{className:"text-fg-dim",children:L(t.cacheCreationTokens)})]})]})]})]})}const bt=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];function vt({days:t,measure:s}){const{buckets:n,weekendShare:c,peakIdx:o}=f.useMemo(()=>{const a=new Array(7).fill(0);for(const l of t){const[p,x,j]=l.date.split("-").map(Number),T=new Date(p,x-1,j).getDay();a[T]+=R(l.totals,s)}const i=a.reduce((l,p)=>l+p,0),d=a[0]+a[6];let u=0;for(let l=1;l<7;l++)a[l]>a[u]&&(u=l);return{buckets:a,weekendShare:i>0?d/i*100:0,peakIdx:u}},[t,s]),r=Math.max(...n,1);return e.jsxs("div",{className:`${S} p-4`,children:[e.jsx(D,{kicker:"05 · composition",title:"Rhythm",right:e.jsxs("span",{className:"text-xs font-mono text-fg-faint",children:["weekend ",c.toFixed(0),"%"]})}),e.jsx("div",{className:"flex items-end gap-2 h-24",children:n.map((a,i)=>e.jsxs("div",{className:"flex-1 flex flex-col items-center justify-end gap-1 h-full",children:[e.jsx("div",{className:`w-full rounded-t ${i===o?"bg-accent":"bg-rhythm"}`,style:{height:`${r>0?a/r*100:0}%`,minHeight:a>0?2:0}}),e.jsx("span",{className:"text-[9px] text-fg-faint font-mono",children:bt[i]})]},i))})]})}const Re="sm.history.analytics.measure",De="sm.history.analytics.range",Tt=["in","out","total","prompts","sessions","time","spend"],Ct=[30,60,90,0],yt=[];function Nt(){try{const t=localStorage.getItem(Re);if(t&&Tt.includes(t))return t}catch{}return"spend"}function wt(){try{const t=Number(localStorage.getItem(De));if(Ct.includes(t))return t}catch{}return 30}function Mt(){return{promptCount:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,toolCallCount:0,sessionCount:0,errorCount:0,activeMinutes:0,estimatedCostUsd:0}}function St(){var xe;const[t,s]=f.useState(Nt),[n,c]=f.useState(wt),o=Be(h=>h.navFace),r=ge(h=>h.tabs),a=ge(h=>h.activeTabId),i=((xe=r.find(h=>h.id===a))==null?void 0:xe.cwd)??null,d=f.useRef(o),u=i?_e(i):null,[l,p]=f.useState(null),x=f.useMemo(()=>o==="project"&&u?new Set([u]):l,[o,u,l]),[j,T]=f.useState(null),[g,m]=f.useState(null),[b,W]=f.useState(!0),[y,se]=f.useState(0);f.useEffect(()=>{try{localStorage.setItem(Re,t)}catch{}},[t]),f.useEffect(()=>{try{localStorage.setItem(De,String(n))}catch{}},[n]),f.useEffect(()=>{let h=!1;return W(!0),window.api.history.dashboard({rangeDays:n}).then(v=>{h||(m(v),W(!1))}).catch(v=>{h||(W(!1),Oe.error(`History analytics failed to load: ${String(v)}`))}),()=>{h=!0}},[n,y]);const z=f.useRef(!1);f.useEffect(()=>{if(!z.current){z.current=!0;return}p(null),T(null)},[n]),f.useEffect(()=>{d.current!==o&&(d.current=o,o==="home"&&p(null))},[o]);const{projects:G,resolving:Y}=He(),V=f.useMemo(()=>{const h={};for(const v of G)for(const C of v.encodedIds)h[C]=v.cwd;return h},[G]),A=f.useMemo(()=>g&&!Y?Qe(g.days,V):null,[g,V,Y]),N=(A==null?void 0:A.days)??(g==null?void 0:g.days)??yt,ne=(A==null?void 0:A.excluded)??null,$=f.useMemo(()=>g?We({days:N,totals:g.totals,prevTotals:g.prevTotals},x):null,[g,N,x]),B=f.useMemo(()=>x===null?N:N.map(h=>({date:h.date,byProject:Object.fromEntries(Object.entries(h.byProject).filter(([v])=>x.has(v)))})),[N,x]),X=f.useMemo(()=>be(N,t),[N,t]),oe=f.useMemo(()=>be(B,t),[B,t]),Ee=f.useMemo(()=>B.map(h=>({date:h.date,values:Object.fromEntries(Object.entries(h.byProject).map(([v,C])=>[v,R(C,t)]))})),[B,t]),pe=f.useMemo(()=>$?Ge($,t):null,[$,t]),Z=f.useMemo(()=>{if(!g||!j)return null;const h=Mt(),v={},C={};for(const _ of N){const k=_.byProject[j];if(k){h.promptCount+=k.promptCount,h.inputTokens+=k.inputTokens,h.outputTokens+=k.outputTokens,h.cacheReadTokens+=k.cacheReadTokens,h.cacheCreationTokens+=k.cacheCreationTokens,h.toolCallCount+=k.toolCallCount,h.sessionCount+=k.sessionCount,h.errorCount+=k.errorCount,h.activeMinutes+=k.activeMinutes,h.estimatedCostUsd+=k.estimatedCostUsd;for(const[w,M]of Object.entries(k.byModel)){const E=v[w]??(v[w]={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,costUsd:0});E.inputTokens+=M.inputTokens,E.outputTokens+=M.outputTokens,E.cacheReadTokens+=M.cacheReadTokens,E.cacheCreationTokens+=M.cacheCreationTokens,E.costUsd+=M.costUsd}for(const[w,M]of Object.entries(k.toolBreakdown))C[w]=(C[w]??0)+M}}return{totals:h,byModel:v,toolBreakdown:C}},[g,N,j]);f.useEffect(()=>{j&&x!==null&&!x.has(j)&&T(null)},[j,x]);function Ue(h){p(v=>{const C=new Set(X.map(w=>w.projectDir)),_=v??new Set(C),k=new Set(_);return k.has(h)?k.delete(h):k.add(h),k.size>=C.size?null:k})}function Pe(h){p(new Set([h]))}function Fe(){p(null)}function Ae(){if(!g)return;const h=B.map(w=>{let M=0,E=0,he=0,fe=0,me=0;const ae={};for(const O of Object.values(w.byProject)){M+=O.estimatedCostUsd,E+=O.inputTokens,he+=O.outputTokens,fe+=O.cacheCreationTokens,me+=O.cacheReadTokens;for(const[je,q]of Object.entries(O.byModel)){const J=ae[je]??(ae[je]={inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0,costUsd:0});J.inputTokens+=q.inputTokens,J.outputTokens+=q.outputTokens,J.cacheReadTokens+=q.cacheReadTokens,J.cacheCreationTokens+=q.cacheCreationTokens}}return{date:w.date,costUsd:M,inputTokens:E,outputTokens:he,cacheCreationTokens:fe,cacheReadTokens:me,byModel:ae}}),v=Xe(h),C=new Blob([v],{type:"text/csv"}),_=URL.createObjectURL(C),k=document.createElement("a");k.href=_,k.download=`usage-history-${g.from}-${g.to}.csv`,k.click(),URL.revokeObjectURL(_)}return b&&!g?e.jsx(Ne,{}):g&&g.days.length===0?e.jsx(Ie,{title:"no sessions in range",hint:"Widen the range or check back after your next session."}):!g||!$||!pe?e.jsx(Ne,{}):e.jsxs("div",{"data-testid":"history-dashboard",className:"flex flex-col h-full",children:[e.jsxs("h1",{className:"font-serif text-[42px] leading-tight tracking-tight text-fg px-4 pt-4",children:["Where your ",e.jsx("em",{className:"italic text-accent",children:"Claude time"})," goes"]}),e.jsx(ot,{measure:t,onMeasure:s,range:n,onRange:c,projectsShown:x===null?X.length:x.size,projectsTotal:X.length,fromDate:g.from,toDate:g.to,onRefresh:()=>se(h=>h+1),onExport:Ae,exportDisabled:B.length===0}),e.jsxs("div",{className:"flex-1 min-h-0 overflow-auto p-4 space-y-4",children:[e.jsx(rt,{measure:t,headline:pe,activeDays:$.activeDays,projectsTouched:oe.length,totalSessions:$.totals.sessionCount}),e.jsx(lt,{days:N}),ne&&et(ne)&&e.jsx("div",{"data-testid":"history-excluded-note",className:"rounded-[10px] border border-line bg-bg-hi px-3 py-2 text-[11.5px] font-mono text-fg-faint",title:"A project is a working directory. ~/.claude/projects holds one folder per path the CLI was ever launched from — throwaway /tmp dirs and test fixtures included — and a folder whose cwd can't be recovered from its transcripts is not a project. Their totals are stated here rather than silently folded into the charts.",children:tt(ne)}),o==="home"&&e.jsx(dt,{chips:X.map(h=>({projectDir:h.projectDir,value:h.value})),keep:x,onToggle:Ue,onIsolate:Pe,onShowAll:Fe}),e.jsx(ht,{days:Ee,measure:t,selected:j,onSelect:T}),e.jsx(ft,{rows:oe,measure:t,selected:j,onSelect:T}),j&&Z&&e.jsx(mt,{projectDir:j,totals:Z.totals,byModel:Z.byModel,toolBreakdown:Z.toolBreakdown,onClear:()=>T(null)}),e.jsxs("div",{className:"grid grid-cols-2 lg:grid-cols-4 gap-4",children:[e.jsx(jt,{byModelTotals:$.byModelTotals}),e.jsx(gt,{rows:oe}),e.jsx(kt,{totals:$.totals}),e.jsx(vt,{days:$.days,measure:t})]})]})]})}function Ne(){return e.jsxs("div",{className:"p-4 space-y-4 animate-pulse",children:[e.jsx("div",{className:"h-24 bg-bg-elev rounded"}),e.jsx("div",{className:"h-16 bg-bg-elev rounded"}),e.jsx("div",{className:"h-8 bg-bg-elev rounded"}),e.jsx("div",{className:"h-56 bg-bg-elev rounded"}),e.jsx("div",{className:"h-40 bg-bg-elev rounded"})]})}function $t(){return e.jsx(Le,{children:e.jsx(St,{})})}const Et=f.memo($t);export{Et as History};
@@ -1,8 +0,0 @@
1
- import{aS as K,aM as V,az as l,aL as A,ak as e,m as F,Y as oe,a8 as H,a1 as le,v as q,r as ee,ac as Y,X as te}from"./index-CCl4tz-u.js";import{M as se}from"./MarkdownEditor-B74ogK2f.js";import"./monaco-editor-BW5C4Iv1.js";function ie(t){return!t||!t.trim()?"default":t.replace(/[^a-zA-Z0-9]/g,"-")}const re=/^[a-z0-9-_]+$/;function ce(t,o,r,i){return t.filter(m=>!o||m.name.toLowerCase().includes(o.toLowerCase())).filter(m=>{var s;return!r||((s=i[m.name])==null?void 0:s.stale)===!0})}const de=["Commands:"," list — show all memory entries"," show <slug> — read one entry"," search <text> — find entries containing <text>"," forget <slug> — delete an entry (with confirm)"," remember <free-form> — ask Claude to save this"," help — this message","","Anything else: I will offer to send it to Claude in the terminal,","or add it as a new memory entry directly."].join(`
2
- `);function me(t){const o=t.trim();if(!o)return{kind:"freeform",arg:"",raw:t};const r=o.toLowerCase();if(r==="help"||r==="?")return{kind:"help",arg:"",raw:t};if(r==="list"||r==="ls"||r==="list memories"||r==="list all"||r==="what do you remember"||r==="what do you remember?")return{kind:"list",arg:"",raw:t};const i=o.match(/^(?:show|read|open)\s+(.+)$/i);if(i)return{kind:"show",arg:i[1].trim(),raw:t};const m=o.match(/^(?:forget|delete|remove)\s+(.+)$/i);if(m)return{kind:"forget",arg:m[1].trim(),raw:t};const s=o.match(/^(?:search|find)\s+(.+)$/i)||o.match(/^what (?:do )?you know about\s+(.+?)\??$/i);if(s)return{kind:"search",arg:s[1].trim(),raw:t};const u=o.match(/^remember(?:\s+that)?\s+(.+)$/i)||o.match(/^note(?:\s+that)?\s+(.+)$/i)||o.match(/^save(?:\s+that)?\s+(.+)$/i);return u?{kind:"remember",arg:u[1].trim(),raw:t}:{kind:"freeform",arg:o,raw:t}}function G(t,o){const r=t.toLowerCase().replace(/\.md$/,""),i=o.find(u=>u.name.replace(/\.md$/,"").toLowerCase()===r);if(i)return i;const m=o.filter(u=>u.name.replace(/\.md$/,"").toLowerCase().startsWith(r));if(m.length===1)return m[0];const s=o.filter(u=>u.name.replace(/\.md$/,"").toLowerCase().includes(r));return s.length===1?s[0]:null}function Z(t){const o=new Set(["the","a","an","is","are","to","of","in","for","and","or"]);return t.toLowerCase().replace(/[^a-z0-9\s-]/g," ").split(/\s+/).filter(m=>m&&!o.has(m)).slice(0,4).join("-").replace(/-+/g,"-").replace(/^-|-$/g,"")||`note-${Date.now().toString(36).slice(-4)}`}function X(t,o){return[`Please save this to my workspace memory (workspace: ${t}).`,`Use the memory tool (or write a markdown file under ~/.claude/projects/${t}/memory/).`,"Pick a short kebab-case filename and add a one-line title at the top.","","Memory to save:",o].join(`
3
- `)}function Q(){return`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function xe({workspace:t,entries:o,onChanged:r}){const i=K(),m=V(),s=(m==null?void 0:m.id)??null,[u,f]=l.useState(""),[c,b]=l.useState(()=>[{id:Q(),role:"system",text:"Hi — I am the memory assistant for this workspace. Type `help` for what I understand, or just talk to me."}]),[C,g]=l.useState(!1),N=l.useRef(null),S=l.useRef(null);l.useEffect(()=>{const a=N.current;a&&(a.scrollTop=a.scrollHeight)},[c]);const x=l.useCallback(a=>{b(p=>[...p,{...a,id:Q()}])},[]),$=l.useCallback(()=>{if(o.length===0){x({role:"result",text:"No memories saved for this workspace yet."});return}x({role:"result",text:`Found ${o.length} ${o.length===1?"memory":"memories"}:`,payload:{kind:"entries",entries:o}})},[o,x]),T=l.useCallback(async a=>{const p=G(a,o);if(!p){x({role:"result",text:`No memory matches "${a}". Try \`list\` to see what is here.`});return}const y=await window.api.memory.read(p.name,t);if(y.error){A.error(`Memory read failed: ${y.error}`),x({role:"result",text:`Read failed: ${y.error}`});return}x({role:"result",text:`${p.name}`,payload:{kind:"entry",entry:p,content:y.content}})},[o,t,x]),B=l.useCallback(async a=>{const p=a.toLowerCase(),y=o.filter(k=>k.name.toLowerCase().includes(p)),O=[];for(const k of o){if(y.includes(k))continue;const L=await window.api.memory.read(k.name,t);if(L.error)continue;const d=L.content.split(`
4
- `),h=d.findIndex(w=>w.toLowerCase().includes(p));h>=0&&O.push({entry:k,snippet:d[h].trim().slice(0,200)})}const n=[...y.map(k=>({entry:k,snippet:"(filename match)"})),...O];if(n.length===0){x({role:"result",text:`No matches for "${a}".`});return}x({role:"result",text:`Found ${n.length} ${n.length===1?"match":"matches"} for "${a}":`,payload:{kind:"search",query:a,hits:n}})},[o,t,x]),D=l.useCallback(async a=>{const p=G(a,o);if(!p){x({role:"result",text:`No memory matches "${a}". Try \`list\` to see what is here.`});return}if(!window.confirm(`Delete memory "${p.name}"? This cannot be undone.`))return;const y=await window.api.memory.delete(p.name,t);if(!y.ok){A.error(`Memory delete failed: ${y.error??"unknown error"}`),x({role:"result",text:`Delete failed: ${y.error??"unknown error"}`});return}x({role:"result",text:`Deleted ${p.name}.`}),r()},[o,t,x,r]),v=l.useCallback(a=>{const p=Z(a),y=X(t,a);x({role:"result",text:"I do not write to memory from this panel. Send the request to Claude in the active terminal, or add it directly as a new memory entry below.",payload:{kind:"suggest-send",prompt:y,original:a}}),x({role:"result",text:`Add it directly as \`${p}.md\` instead?`,payload:{kind:"suggest-add",slug:p,content:a}})},[t,x]),_=l.useCallback(a=>{const p=Z(a),y=X(t,a);x({role:"result",text:"Not a recognised command. Options:",payload:{kind:"suggest-send",prompt:y,original:a}}),x({role:"result",text:`…or save it directly as \`${p}.md\`.`,payload:{kind:"suggest-add",slug:p,content:a}})},[t,x]),M=l.useCallback(async a=>{g(!0);try{switch(a.kind){case"list":$();break;case"show":await T(a.arg);break;case"search":await B(a.arg);break;case"forget":await D(a.arg);break;case"help":x({role:"result",text:de});break;case"remember":v(a.arg);break;case"freeform":_(a.arg);break}}finally{g(!1)}},[$,T,B,D,v,_,x]),R=l.useCallback(async()=>{const a=u.trim();!a||C||(f(""),x({role:"user",text:a}),await M(me(a)))},[u,C,M,x]),j=a=>{a.key==="Enter"&&!a.shiftKey&&(a.preventDefault(),R())},E=l.useCallback(a=>{if(!s){A.error("No active terminal session — open or focus a tab first.");return}window.api.pty.write({tabId:s,data:a}),A.info("Prompt sent to active terminal (not submitted)")},[s]),P=l.useCallback(async(a,p)=>{if(!re.test(a)){A.error(`Invalid slug "${a}" — must be [a-z0-9-_]+`);return}const y=`${a}.md`,O=await window.api.memory.create(y,void 0,t);if(!O.ok){A.error(`Create failed: ${O.error??"unknown error"}`);return}const n=await window.api.memory.read(y,t),k=n.error?p:`${n.content.trimEnd()}
5
-
6
- ${p}
7
- `,L=await window.api.memory.write(y,k,t);if(!L.ok){A.error(`Write failed: ${L.error??"unknown error"}`);return}x({role:"result",text:`Saved as ${y}.`}),r()},[t,x,r]),z=l.useMemo(()=>[{label:"List memories",value:"list"},{label:"Help",value:"help"},{label:"Search…",value:"search "},{label:"Remember…",value:"remember "}],[]);return i?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{ref:N,className:"flex-1 min-h-0 overflow-y-auto px-3 py-2 space-y-2",children:[c.map(a=>e.jsx(ue,{message:a,entries:o,onShow:p=>{var y;f(`show ${p.replace(/\.md$/,"")}`),(y=S.current)==null||y.focus()},onSendToClaude:E,onAddDirect:P},a.id)),C&&e.jsx("div",{className:"text-xs text-fg-faint italic",children:"working…"})]}),e.jsxs("div",{className:"shrink-0 border-t border-line px-3 py-2 space-y-2",children:[e.jsx("div",{className:"flex gap-1 flex-wrap",children:z.map(a=>e.jsx("button",{onClick:()=>{var p;f(a.value),(p=S.current)==null||p.focus()},className:"px-2 py-0.5 text-[11px] text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors",children:a.label},a.label))}),e.jsxs("div",{className:"flex gap-2 items-end",children:[e.jsx(oe,{}),e.jsx("textarea",{ref:S,value:u,onChange:a=>f(a.target.value),onKeyDown:j,placeholder:"Type `list`, `show <slug>`, `search <text>`, `remember …`, or talk freely. Enter to send, Shift+Enter for newline.",rows:2,className:"flex-1 bg-bg border border-line rounded px-2 py-1 text-xs text-fg resize-none focus:outline-none focus:border-fg-faint"}),e.jsx("button",{onClick:R,disabled:!u.trim()||C,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:"Send"})]}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Workspace: ",e.jsx("span",{className:"font-mono",children:t}),!s&&e.jsx("span",{className:"ml-2 text-amber-400",children:"(no active terminal — Send-to-Claude disabled)"})]})]})]}):e.jsx(F,{title:"loading…"})}function ue({message:t,entries:o,onShow:r,onSendToClaude:i,onAddDirect:m}){var u,f,c,b,C;const s=t.role==="user"?"bg-bg-hi text-fg ml-8":t.role==="system"?"bg-bg text-fg-dim italic":"bg-bg text-fg-dim";return e.jsxs("div",{className:`text-xs rounded border border-line px-3 py-2 ${s}`,children:[e.jsx("div",{className:"whitespace-pre-wrap font-mono text-[11px] leading-relaxed",children:t.text}),((u=t.payload)==null?void 0:u.kind)==="entries"&&e.jsx("div",{className:"mt-2 space-y-0.5",children:t.payload.entries.map(g=>e.jsxs("button",{onClick:()=>r(g.name),className:"block w-full text-left px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg hover:bg-bg-hi rounded font-mono",children:[g.name," ",e.jsxs("span",{className:"text-fg-faint",children:["(",H(g.bytes),")"]})]},g.name))}),((f=t.payload)==null?void 0:f.kind)==="entry"&&e.jsx("pre",{className:"mt-2 max-h-64 overflow-auto px-2 py-1 bg-bg-hi rounded text-[11px] whitespace-pre-wrap text-fg",children:t.payload.content}),((c=t.payload)==null?void 0:c.kind)==="search"&&e.jsx("div",{className:"mt-2 space-y-1",children:t.payload.hits.map((g,N)=>e.jsxs("button",{onClick:()=>r(g.entry.name),className:"block w-full text-left px-2 py-1 hover:bg-bg-hi rounded",children:[e.jsx("div",{className:"font-mono text-[11px] text-fg-dim",children:g.entry.name}),e.jsx("div",{className:"text-[10px] text-fg-faint truncate",children:g.snippet})]},`${g.entry.name}-${N}`))}),((b=t.payload)==null?void 0:b.kind)==="suggest-send"&&e.jsxs("div",{className:"mt-2 flex gap-2 items-center",children:[e.jsx("button",{onClick:()=>i(t.payload.kind==="suggest-send"?t.payload.prompt:""),className:"px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"Send to Claude in terminal"}),e.jsx("span",{className:"text-[10px] text-fg-faint",children:"paste-only — you press Enter"})]}),((C=t.payload)==null?void 0:C.kind)==="suggest-add"&&e.jsxs("div",{className:"mt-2 flex gap-2 items-center",children:[e.jsx("button",{onClick:()=>{var g;return((g=t.payload)==null?void 0:g.kind)==="suggest-add"&&m(t.payload.slug,t.payload.content)},className:"px-2 py-0.5 text-[11px] text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"Add as new memory"}),e.jsxs("span",{className:"text-[10px] text-fg-faint font-mono",children:[t.payload.slug,".md"]})]})]})}const W=le((t,o)=>({entriesByAgent:new Map,loading:null,errorByAgent:new Map,loadAgent:async r=>{t({loading:r});const i=await window.api.agentMemory.list(r),m=new Map(o().entriesByAgent);m.set(r,i.entries);const s=new Map(o().errorByAgent);i.error?(s.set(r,i.error),A.error(`Agent memory load failed: ${i.error}`)):s.delete(r),t({entriesByAgent:m,loading:null,errorByAgent:s})},setEntry:async(r,i,m,s)=>{const u=await window.api.agentMemory.set(r,i,m,s);return u.ok&&await o().loadAgent(r),u},deleteEntry:async(r,i)=>{const m=await window.api.agentMemory.delete(r,i);if(m.ok){const s=new Map(o().entriesByAgent),u=s.get(r)??[];s.set(r,u.filter(f=>f.id!==i)),t({entriesByAgent:s})}return m}})),U=/^[A-Za-z0-9._-]{1,128}$/,I={command:"Commands",preference:"Preferences",pattern:"Patterns",failure:"Avoid",workflow:"Workflow"},ne=["command","preference","pattern","failure","workflow"];async function fe(t,o){const r=[{dir:`${t}/.claude/agents`,scope:"user"}];o&&r.push({dir:`${o}/.claude/agents`,scope:"project"});const i=[];for(const{dir:s,scope:u}of r)try{const f=await window.api.config.listDir(s,{filesOnly:!0});for(const c of f.entries){if(!c.name.endsWith(".md"))continue;const b=c.name.replace(/\.md$/,"");U.test(b)&&i.push({id:b,label:b,scope:u})}}catch{}const m=new Map;for(const s of i)s.scope==="project"?m.set(s.id,s):m.has(s.id)||m.set(s.id,s);return Array.from(m.values()).sort((s,u)=>s.id.localeCompare(u.id))}function he(){const t=K(),o=V(),r=(o==null?void 0:o.cwd)??null,[i,m]=l.useState([]),[s,u]=l.useState(null),[f,c]=l.useState(null),[b,C]=l.useState(""),[g,N]=l.useState(!1),[S,x]=l.useState(!1),[$,T]=l.useState({}),B=W(n=>n.entriesByAgent),D=W(n=>n.loadAgent),v=W(n=>n.setEntry),_=W(n=>n.deleteEntry);l.useEffect(()=>{if(!t)return;let n=!1;return fe(t,r).then(k=>{n||(m(k),u(L=>{var d;return L&&k.find(h=>h.id===L)?L:((d=k[0])==null?void 0:d.id)??null}))}),()=>{n=!0}},[t,r]),l.useEffect(()=>{s&&(c(null),D(s))},[s,D]);const M=s?B.get(s)??[]:[],R=l.useMemo(()=>M.filter(n=>!b||n.id.toLowerCase().includes(b.toLowerCase())||n.body.toLowerCase().includes(b.toLowerCase())),[M,b]),j=l.useMemo(()=>M.find(n=>n.id===f)??null,[M,f]);l.useEffect(()=>{var n;!f&&R.length>0?c(R[0].id):f&&!R.find(k=>k.id===f)&&c(((n=R[0])==null?void 0:n.id)??null)},[R,f]);const E=s&&f?`${s}::${f}`:null,P=E?$[E]??(j==null?void 0:j.body)??"":"",z=!!E&&j!==null&&P!==j.body,a=l.useCallback(async()=>{s&&await D(s)},[s,D]),p=async()=>{if(!s||!f||!E)return;N(!0);const n=await v(s,f,P,(j==null?void 0:j.category)??void 0);if(N(!1),!n.ok){A.error(`Save failed: ${n.error??"unknown"}`);return}T(k=>{const L={...k};return delete L[E],L})},y=async()=>{if(!s||!f||!window.confirm(`Delete memory "${f}"? This cannot be undone.`))return;N(!0);const n=await _(s,f);if(N(!1),!n.ok){A.error(`Delete failed: ${n.error??"unknown"}`);return}c(null)},O=async n=>{if(!s||!f||!j)return;N(!0);const k=await v(s,f,j.body,n??void 0);N(!1),k.ok||A.error(`Update failed: ${k.error??"unknown"}`)};return t?e.jsx(q,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx("span",{className:"text-fg-faint",children:"Agent"}),e.jsxs("select",{value:s??"",onChange:n=>u(n.target.value||null),className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg max-w-[16rem]",children:[i.length===0&&e.jsx("option",{value:"",children:"(no agents found)"}),i.map(n=>e.jsxs("option",{value:n.id,children:[n.label," ",n.scope==="project"?"· project":""]},`${n.scope}:${n.id}`))]}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsxs("span",{className:"text-fg-faint",children:[M.length," ",M.length===1?"entry":"entries"]}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:()=>x(!0),disabled:!s,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40",children:"+ New entry"}),e.jsx("button",{onClick:a,disabled:!s,className:"ml-2 px-2 py-0.5 text-xs text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40",children:"Refresh"})]}),footer:j?e.jsxs("div",{className:"px-3 py-1.5 flex items-center gap-3 text-xs",children:[e.jsx("button",{onClick:p,disabled:!z||g,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:g?"Saving…":"Save"}),e.jsx("button",{onClick:y,disabled:g,className:"px-2 py-0.5 text-xs text-red-400 hover:text-red-300 border border-line hover:border-red-400/50 rounded transition-colors disabled:opacity-40",children:"Delete"}),e.jsx("span",{className:"text-fg-faint",children:"Category:"}),e.jsxs("select",{value:j.category??"",onChange:n=>O(n.target.value||null),disabled:g,className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg-dim disabled:opacity-40",children:[e.jsx("option",{value:"",children:"(none)"}),ne.map(n=>e.jsx("option",{value:n,children:I[n]},n))]}),e.jsx("span",{className:"text-fg-faint truncate flex-1",children:j.id}),z&&e.jsx("span",{className:"text-accent",children:"unsaved"})]}):null,children:e.jsx(ee,{sidebar:e.jsxs("div",{className:"py-1",children:[e.jsx("div",{className:"px-2 pb-1",children:e.jsx("input",{value:b,onChange:n=>C(n.target.value),placeholder:"filter…",className:"w-full bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"})}),s?R.length===0?e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:M.length===0?"no entries yet":"no matches"}):R.map(n=>e.jsxs("button",{onClick:()=>c(n.id),className:`w-full text-left px-3 py-1 text-xs flex items-center justify-between ${f===n.id?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,title:n.id,children:[e.jsx("span",{className:"truncate",children:n.id}),e.jsxs("span",{className:"ml-2 text-fg-faint shrink-0 text-[10px]",children:[H(n.bytes)," · ",Y(n.updatedAt)]})]},n.id)):e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:"select an agent first"})]}),detail:S&&s?e.jsx(pe,{agentId:s,existingIds:new Set(M.map(n=>n.id)),onCancel:()=>x(!1),onCreated:n=>{x(!1),c(n),D(s)}}):j&&s&&f&&E?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 px-3 py-2 border-b border-line text-xs",children:[e.jsx("div",{className:"text-fg truncate",children:j.id}),e.jsxs("div",{className:"text-fg-faint truncate",children:[H(j.bytes)," · modified ",Y(j.updatedAt),j.category?` · ${I[j.category]}`:""]})]}),e.jsx("div",{className:"flex-1 min-h-0",children:e.jsx(se,{path:`agent-memory/${s}/${j.id}.md`,value:P,onChange:n=>T(k=>({...k,[E]:n}))})})]}):e.jsx(F,{title:s?M.length===0?"no entries yet":"select an entry":"no agent selected",hint:s&&M.length===0?"Use “+ New entry” to give this subagent its first memory.":void 0})})}):e.jsx(F,{title:"loading…"})}function pe({agentId:t,existingIds:o,onCancel:r,onCreated:i}){const m=W(v=>v.setEntry),[s,u]=l.useState(""),[f,c]=l.useState(""),[b,C]=l.useState(""),[g,N]=l.useState(!1),[S,x]=l.useState(null),$=s.trim(),T=U.test($)&&!o.has($),B=s?U.test($)?o.has($)?"an entry with this id already exists":null:"must match [A-Za-z0-9._-]+":null,D=async()=>{if(!T)return;N(!0),x(null);const v=await m(t,$,f,b||void 0);if(N(!1),!v.ok){x(v.error??"create failed");return}i($)};return e.jsxs("div",{className:"p-4 max-w-xl space-y-3",children:[e.jsxs("div",{className:"text-sm text-fg",children:["New memory entry for ",e.jsx("span",{className:"font-mono text-fg-dim",children:t})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Entry id"}),e.jsx("input",{autoFocus:!0,value:s,onChange:v=>u(v.target.value),placeholder:"e.g. preferred-test-runner",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"}),B&&e.jsx("div",{className:"text-[10px] text-red-400",children:B}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Letters, digits, dots, dashes, underscores. Persists at",e.jsxs("code",{className:"ml-1 font-mono text-fg-dim",children:["~/.claude/session-manager/agent-memory/",t,".json"]}),"."]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Category (optional)"}),e.jsxs("select",{value:b,onChange:v=>C(v.target.value),className:"bg-bg border border-line rounded px-2 py-1 text-xs text-fg",children:[e.jsx("option",{value:"",children:"(none)"}),ne.map(v=>e.jsx("option",{value:v,children:I[v]},v))]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Body (markdown)"}),e.jsx("textarea",{value:f,onChange:v=>c(v.target.value),rows:6,placeholder:"What should this subagent remember?",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"})]}),S&&e.jsx("div",{className:"text-xs text-red-400",children:S}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:D,disabled:!T||g,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40",children:g?"Creating…":"Create"}),e.jsx("button",{onClick:r,disabled:g,className:"px-3 py-1 text-xs text-fg-faint hover:text-fg-dim rounded transition-colors",children:"Cancel"})]})]})}function ge({workspace:t,onOpenMember:o}){const[r,i]=l.useState(null),[m,s]=l.useState(!1),[u,f]=l.useState(!1),c=l.useRef(0),b=l.useCallback(async S=>{const x=++c.current;s(!0);try{const $=await window.api.memory.aggregate(t,S);if(c.current!==x)return;i($)}catch($){if(c.current!==x)return;A.error(`Memory aggregate failed: ${$ instanceof Error?$.message:String($)}`)}finally{c.current===x&&(s(!1),f(!0))}},[t]);l.useEffect(()=>{i(null),f(!1),b(!1)},[b]);const C=(r==null?void 0:r.clusters)??[],g=(r==null?void 0:r.orphans)??[],N=C.length>0||g.length>0;return e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 flex items-center gap-2 px-3 py-2 border-b border-line text-xs",children:[e.jsx("span",{className:"text-fg-faint",children:r!=null&&r.generatedAt?`generated ${Y(r.generatedAt)} ago`:"not yet aggregated"}),e.jsx("div",{className:"flex-1"}),e.jsx("button",{onClick:()=>b(!0),disabled:m,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:m?"Aggregating…":r!=null&&r.generatedAt?"Refresh":"Aggregate"})]}),e.jsx("div",{className:"flex-1 min-h-0 overflow-auto p-3",children:u?N?e.jsxs("div",{className:"space-y-4",children:[C.length>0&&e.jsx("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-3",children:C.map(S=>e.jsx(be,{cluster:S,onOpenMember:o},S.id))}),g.length>0&&e.jsxs("div",{className:"border border-line rounded p-3",children:[e.jsx("div",{className:"text-xs text-fg mb-2",children:"Unclustered"}),e.jsx("div",{className:"flex flex-wrap gap-1",children:g.map(S=>e.jsx("button",{onClick:()=>o(S),className:"px-2 py-0.5 text-[11px] font-mono text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:S},S))})]})]}):e.jsx(F,{title:"no clusters yet",hint:"Click Aggregate to organize this workspace's memories into named clusters."}):e.jsx(F,{title:"loading…"})})]})}function be({cluster:t,onOpenMember:o}){return e.jsxs("div",{className:"border border-line rounded p-3",children:[e.jsx("div",{className:"text-sm text-fg",children:t.name}),t.summary&&e.jsx("div",{className:"text-xs text-fg-faint mt-0.5",children:t.summary}),e.jsx("div",{className:"mt-2 flex flex-wrap gap-1",children:t.memberSlugs.map(r=>e.jsx("button",{onClick:()=>o(r),className:"px-2 py-0.5 text-[11px] font-mono text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:r},r))}),t.links.length>0&&e.jsx("div",{className:"mt-2 space-y-0.5",children:t.links.map((r,i)=>e.jsxs("div",{className:"text-[10px] text-fg-faint font-mono",children:[r.from," → ",r.to,r.label?` (${r.label})`:""]},`${r.from}-${r.to}-${i}`))})]})}const ye=[{key:"workspace",label:"Workspace"},{key:"subagent",label:"Subagent"}],J="sm.memoryTab.scope";function je(){const[t,o]=l.useState(()=>localStorage.getItem(J)==="subagent"?"subagent":"workspace");return l.useEffect(()=>{localStorage.setItem(J,t)},[t]),e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 flex items-center gap-3 px-3 py-2 border-b border-line",children:[e.jsx(te,{options:ye,active:t,onChange:o}),e.jsx("span",{className:"text-[11px] text-fg-faint",children:t==="workspace"?"memories scoped to the active project":"long-term memory per subagent"})]}),e.jsx("div",{className:"flex-1 min-h-0",children:t==="workspace"?e.jsx(ve,{}):e.jsx(he,{})})]})}const Se=l.memo(je);function ve(){const t=K(),o=V(),r=(o==null?void 0:o.cwd)??null,i=l.useMemo(()=>ie(r),[r]),[m,s]=l.useState("classic"),[u,f]=l.useState([]),[c,b]=l.useState(null),[C,g]=l.useState(""),[N,S]=l.useState({}),[x,$]=l.useState(!1),[T,B]=l.useState({}),[D,v]=l.useState({}),[_,M]=l.useState(!1),[R,j]=l.useState(!1),E=l.useCallback(async()=>{if(!t)return;const d=await window.api.memory.list(i);f(d.entries),d.error&&A.error(`Memory list failed: ${d.error}`),b(h=>{var w;return h&&d.entries.some(ae=>ae.name===h)?h:((w=d.entries[0])==null?void 0:w.name)??null});try{const h=await window.api.memory.stale(i,r??void 0);S(Object.fromEntries(h.entries.map(w=>[w.name,w])))}catch(h){S({}),A.error(`Memory staleness check failed: ${h instanceof Error?h.message:String(h)}`)}},[t,i,r]);if(l.useEffect(()=>{B({}),v({}),b(null),E()},[i,E]),l.useEffect(()=>{if(!t)return;const d=`${t}/.claude/projects/${i}/memory`;window.api.config.watch([d]);const h=window.api.config.onChanged(({path:w})=>{(w===d||w.startsWith(d+"/"))&&E()});return()=>{h(),window.api.config.unwatch([d])}},[t,i,E]),l.useEffect(()=>{if(!c||T[c]!==void 0)return;let d=!1;return(async()=>{const h=await window.api.memory.read(c,i);if(!d){if(h.error){A.error(`Memory read failed: ${h.error}`);return}B(w=>({...w,[c]:h.content})),v(w=>({...w,[c]:h.content}))}})(),()=>{d=!0}},[c,i]),!t)return e.jsx(F,{title:"loading…"});const P=u.reduce((d,h)=>{var w;return d+((w=N[h.name])!=null&&w.stale?1:0)},0),z=ce(u,C,x,N),a=u.find(d=>d.name===c)??null,p=c?N[c]:void 0,y=c?T[c]??"":"",O=c?D[c]??"":"",n=c?y!==O:!1,k=async()=>{if(!c)return;M(!0);const d=await window.api.memory.write(c,y,i);if(M(!1),!d.ok){A.error(`Memory save failed: ${d.error??"unknown error"}`);return}v(h=>({...h,[c]:y})),E()},L=async()=>{if(!c||!window.confirm(`Delete memory "${c}"? This cannot be undone.`))return;M(!0);const d=await window.api.memory.delete(c,i);if(M(!1),!d.ok){A.error(`Memory delete failed: ${d.error??"unknown error"}`);return}B(h=>{const w={...h};return delete w[c],w}),v(h=>{const w={...h};return delete w[c],w}),b(null),E()};return e.jsx(q,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx(te,{options:[{key:"classic",label:"Editor"},{key:"natural",label:"Natural"},{key:"clusters",label:"Clusters"}],active:m,onChange:s}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsx("span",{className:"text-fg-faint",children:"Workspace"}),e.jsx("span",{className:"font-mono text-fg-dim truncate max-w-[24rem]",title:i,children:i}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsxs("span",{className:"text-fg-faint",children:[u.length," ",u.length===1?"entry":"entries"]}),e.jsx("div",{className:"flex-1"}),m==="classic"&&P>0&&e.jsxs("button",{onClick:()=>$(d=>!d),className:`px-2 py-0.5 text-xs border rounded transition-colors ${x?"text-amber-400 border-amber-400/50 bg-amber-400/10":"text-fg-dim hover:text-fg border-line hover:border-fg-faint"}`,children:["Stale only (",P,")"]}),m==="classic"&&e.jsx("button",{onClick:()=>j(!0),className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors",children:"+ New memory"}),m!=="clusters"&&e.jsx("button",{onClick:E,className:"ml-2 px-2 py-0.5 text-xs text-fg-faint hover:text-fg-dim border border-line hover:border-fg-faint rounded transition-colors",children:"Refresh"})]}),footer:m==="classic"&&a?e.jsxs("div",{className:"px-3 py-1.5 flex items-center gap-3 text-xs",children:[e.jsx("button",{onClick:k,disabled:!n||_,className:"px-2 py-0.5 text-xs text-fg-dim hover:text-fg border border-line hover:border-fg-faint rounded transition-colors disabled:opacity-40 disabled:cursor-not-allowed",children:_?"Saving…":"Save"}),e.jsx("button",{onClick:L,disabled:_,className:"px-2 py-0.5 text-xs text-red-400 hover:text-red-300 border border-line hover:border-red-400/50 rounded transition-colors disabled:opacity-40",children:"Delete"}),(p==null?void 0:p.stale)&&e.jsxs("span",{className:"text-amber-400/90 truncate",title:p.reasons.join("; "),children:["stale · ",p.reasons.join(", ")]}),e.jsx("span",{className:"text-fg-faint truncate flex-1",children:a.path}),n&&e.jsx("span",{className:"text-accent",children:"unsaved"})]}):null,children:m==="natural"?e.jsx(xe,{workspace:i,entries:u,onChanged:E}):m==="clusters"?e.jsx(ge,{workspace:i,onOpenMember:d=>{b(`${d}.md`),s("classic")}}):e.jsx(ee,{sidebar:e.jsxs("div",{className:"py-1",children:[e.jsx("div",{className:"px-2 pb-1",children:e.jsx("input",{value:C,onChange:d=>g(d.target.value),placeholder:"filter…",className:"w-full bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg"})}),z.length===0?e.jsx("div",{className:"px-3 py-2 text-xs text-fg-faint italic",children:u.length===0?"no memories yet":"no matches"}):z.map(d=>{const h=N[d.name],w=h!=null&&h.stale?`${d.path}
8
- stale: ${h.reasons.join(", ")}`:d.path;return e.jsxs("button",{onClick:()=>b(d.name),className:`w-full text-left px-3 py-1 text-xs flex items-center justify-between ${c===d.name?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,title:w,children:[e.jsxs("span",{className:"truncate flex items-center",children:[e.jsx("span",{className:"truncate",children:d.name.replace(/\.md$/,"")}),(h==null?void 0:h.stale)&&e.jsx("span",{className:"ml-1.5 shrink-0 px-1 rounded text-[9px] uppercase tracking-wide text-amber-400/90 border border-amber-400/30",children:"stale"})]}),e.jsxs("span",{className:"ml-2 text-fg-faint shrink-0 text-[10px]",children:[H(d.bytes)," · ",Y(d.mtimeMs)]})]},d.name)})]}),detail:R?e.jsx(we,{workspace:i,onCancel:()=>j(!1),onCreated:d=>{j(!1),b(d),E()}}):a&&c?e.jsxs("div",{className:"h-full flex flex-col",children:[e.jsxs("div",{className:"shrink-0 px-3 py-2 border-b border-line text-xs",children:[e.jsx("div",{className:"text-fg truncate",children:a.name}),e.jsxs("div",{className:"text-fg-faint truncate",children:[H(a.bytes)," · modified ",Y(a.mtimeMs)]})]}),e.jsx("div",{className:"flex-1 min-h-0",children:e.jsx(se,{path:a.path,value:y,onChange:d=>B(h=>({...h,[c]:d}))})})]}):e.jsx(F,{title:u.length===0?"no memories yet":"select a memory",hint:u.length===0?"Claude writes to memories during sessions when you ask it to remember things, or you can create one manually with the + New memory button.":void 0})})})}function we({workspace:t,onCancel:o,onCreated:r}){const[i,m]=l.useState(""),[s,u]=l.useState(""),[f,c]=l.useState(!1),[b,C]=l.useState(null),g=i.trim().toLowerCase(),N=re.test(g),S=async()=>{if(!N){C("Slug must match [a-z0-9-_]+");return}c(!0),C(null);const x=`${g}.md`,$=await window.api.memory.create(x,s.trim()||void 0,t);if(c(!1),!$.ok){C($.error??"create failed");return}r(x)};return e.jsxs("div",{className:"p-4 max-w-xl space-y-3",children:[e.jsx("div",{className:"text-sm text-fg",children:"New memory"}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Slug"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{autoFocus:!0,value:i,onChange:x=>m(x.target.value),placeholder:"my-notes",className:"flex-1 bg-bg border border-line rounded px-2 py-1 text-xs text-fg font-mono"}),e.jsx("span",{className:"text-xs text-fg-faint",children:".md"})]}),e.jsxs("div",{className:"text-[10px] text-fg-faint",children:["Lowercase letters, digits, dashes, underscores. The file is created at",e.jsxs("code",{className:"ml-1 font-mono text-fg-dim",children:["~/.claude/projects/",t,"/memory/",g||"…",".md"]})]})]}),e.jsxs("div",{className:"space-y-1",children:[e.jsx("label",{className:"block text-xs text-fg-faint",children:"Description (optional)"}),e.jsx("input",{value:s,onChange:x=>u(x.target.value),placeholder:"one-line summary",className:"w-full bg-bg border border-line rounded px-2 py-1 text-xs text-fg"})]}),b&&e.jsx("div",{className:"text-xs text-red-400",children:b}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("button",{onClick:S,disabled:!N||f,className:"px-3 py-1 text-xs text-fg border border-line hover:bg-bg-hi rounded transition-colors disabled:opacity-40",children:f?"Creating…":"Create"}),e.jsx("button",{onClick:o,disabled:f,className:"px-3 py-1 text-xs text-fg-faint hover:text-fg-dim rounded transition-colors",children:"Cancel"})]})]})}export{Se as Memory};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./SkillReferenceGraph-BRDiE6Hv.js","./index-CCl4tz-u.js","./monaco-editor-BW5C4Iv1.js","./monaco-editor-BTnBOi8r.css","./index-CKH5Uxik.css"])))=>i.map(i=>d[i]);
2
- import{ak as e,az as p,m as y,r as X,aS as Y,v as R,aL as V,a as Z}from"./index-CCl4tz-u.js";import{P as z}from"./ProvenanceBadge-BnjBQiV9.js";import{e as ee}from"./Library-BU9np05E.js";import{p as G}from"./skillFrontmatter-Dif5JIg7.js";import{_ as te}from"./monaco-editor-BW5C4Iv1.js";import{M as ne}from"./MarkdownEditor-B74ogK2f.js";function se({columns:t,rows:n,getKey:o,onRowClick:a,activeKey:i,empty:h}){return n.length===0?e.jsx("div",{className:"p-6 text-fg-faint text-xs text-center",children:h??"no entries"}):e.jsxs("table",{className:"w-full text-xs",children:[e.jsx("thead",{className:"sticky top-0 bg-bg-elev text-fg-faint uppercase tracking-wider",children:e.jsx("tr",{className:"border-b border-line",children:t.map(l=>e.jsx("th",{className:`text-left font-normal px-3 py-1.5 ${l.className??""}`,style:l.width?{width:l.width}:void 0,children:l.header},l.key))})}),e.jsx("tbody",{children:n.map(l=>{const r=o(l),d=i===r;return e.jsx("tr",{"data-row-key":r,onClick:()=>a==null?void 0:a(l),className:`border-b border-line/50 transition-colors ${a?"cursor-pointer":""} ${d?"bg-bg-hi text-fg":"text-fg-dim hover:bg-bg-hi/50"}`,children:t.map(f=>e.jsx("td",{className:`px-3 py-1 ${f.className??""}`,children:f.render(l)},f.key))},r)})})]})}async function ie(t,n,o){var f,b;const a=`${n}/skills`;try{const c=await window.api.config.listDir(a,{dirsOnly:!0});if(c.ok&&c.entries.length>0)return a}catch{}let i=null;try{const c=await window.api.config.readJson(`${o}/.claude/plugins/installed_plugins.json`);c.exists&&!c.parseError&&c.data&&typeof c.data=="object"&&(i=c.data)}catch{return null}if(!(i!=null&&i.plugins))return null;const h=Object.keys(i.plugins).find(c=>c===t||c.startsWith(`${t}@`));if(!h)return null;const l=(b=(f=i.plugins[h])==null?void 0:f[0])==null?void 0:b.installPath;if(!l)return null;const r=`${l}/skills`;return await window.api.config.exists(r)?r:null}async function ae(t){const n=await window.api.config.listDir(t,{dirsOnly:!0}),o=[];for(const a of n.entries){const i=`${a.path}/SKILL.md`,h=await window.api.config.readText(i);if(!h.exists)continue;const l=G(h.text);o.push({id:a.name,name:l.name,description:l.description,dir:a.path,path:i,body:l.body})}return o}function le(t){const n=[],o=new Set;for(const a of t)for(const i of t){if(a.id===i.id)continue;const h=i.id.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),l=[`/${h}\\b`,`\`${h}\``];if(i.id.length>=4&&l.push(`\\b${h}\\b`),!l.some(f=>new RegExp(f,"i").test(a.body)))continue;const d=`${a.id}->${i.id}`;o.has(d)||(o.add(d),n.push({from:a.id,to:i.id}))}return n}const re=p.lazy(()=>te(()=>import("./SkillReferenceGraph-BRDiE6Hv.js"),__vite__mapDeps([0,1,2,3,4]),import.meta.url).then(t=>({default:t.SkillReferenceGraph})));function oe({pluginName:t,skills:n,onBack:o,initialSelectedId:a}){var b;const[i,h]=p.useState(a??((b=n[0])==null?void 0:b.id)??null),l=i?n.find(c=>c.id===i)??null:null,r=p.useMemo(()=>le(n),[n]),[d,f]=p.useState("list");return e.jsxs("div",{className:"flex flex-col h-full min-h-0 overflow-hidden",children:[e.jsxs("div",{className:"shrink-0 h-11 flex items-baseline gap-3.5 px-3 border-b border-line bg-bg",children:[e.jsxs("button",{type:"button",onClick:o,className:"font-mono text-accent hover:underline shrink-0 text-xs",children:["← ",t]}),e.jsx("span",{className:"font-serif text-[22px] text-fg shrink-0 truncate max-w-[16rem]",children:(l==null?void 0:l.name)??(l==null?void 0:l.id)??"skill"}),e.jsx("span",{className:"flex-1"}),e.jsx("div",{className:"flex items-center gap-1.5 shrink-0",children:e.jsxs("div",{className:"flex rounded border border-line overflow-hidden",children:[e.jsx("button",{onClick:()=>f("list"),className:`px-2 py-0.5 text-xs ${d==="list"?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,children:"list"}),e.jsx("button",{onClick:()=>f("graph"),className:`px-2 py-0.5 text-xs ${d==="graph"?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,children:"graph"})]})})]}),e.jsxs("div",{className:"shrink-0 h-[26px] flex items-center gap-[18px] px-3 bg-bg-elev border-b border-line text-[9px] font-mono uppercase tracking-wide overflow-hidden",children:[l!=null&&l.description?e.jsx("span",{className:"text-fg-dim truncate normal-case",children:l.description}):null,e.jsxs("span",{className:"ml-auto text-fg-faint truncate normal-case",children:[n.length," ",n.length===1?"skill":"skills"," · ",r.length," ",r.length===1?"reference":"references"]})]}),e.jsx("div",{className:"flex-1 min-h-0 overflow-hidden",children:n.length===0?e.jsx(y,{title:"no skills in this plugin"}):d==="graph"?e.jsx(p.Suspense,{fallback:e.jsx("div",{className:"p-6 text-xs text-fg-faint",children:"Loading graph…"}),children:e.jsx(re,{skills:n,edges:r,selectedId:i,onSelect:h})}):e.jsx(X,{sidebar:e.jsx("div",{className:"py-1",children:n.map(c=>e.jsxs("button",{onClick:()=>h(c.id),className:`w-full px-3 py-1 text-xs text-left ${i===c.id?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,children:[e.jsx("div",{className:"truncate",children:c.name??c.id}),c.description?e.jsx("div",{className:"truncate text-fg-faint",children:c.description}):null]},c.id))}),detail:l?e.jsx("div",{className:"h-full min-h-0",children:e.jsx(ne,{path:l.path,value:l.body,onChange:()=>{},readOnly:!0})}):e.jsx(y,{title:"select a skill"})})})]})}const q=t=>{var n,o;return{type:"plugin",name:t.name,repository:(n=t.manifest)==null?void 0:n.repository,homepage:(o=t.manifest)==null?void 0:o.homepage}};function ce(){const t=Y(),[n,o]=p.useState([]),[a,i]=p.useState(!0),[h,l]=p.useState(null),[r,d]=p.useState("installed"),[f,b]=p.useState(null),[c,L]=p.useState(new Set);p.useEffect(()=>{var x,k;let s=!1;return(k=(x=window.api.app).seedStatus)==null||k.call(x).then(j=>{s||b(j)}).catch(()=>{}),()=>{s=!0}},[]),p.useEffect(()=>{if(!t)return;let s=!1;return(async()=>{var k;i(!0);const x=[];try{const j=await window.api.config.readJson(`${t}/.claude/plugins/installed_plugins.json`);if(j.exists&&!j.parseError&&j.data&&typeof j.data=="object"){const w=j.data;for(const[C,A]of Object.entries(w.plugins??{})){if(!Array.isArray(A))continue;const _=C.split("@")[0],u=C.split("@")[1];for(const g of A){if(!(g!=null&&g.installPath))continue;const B={name:_,path:g.installPath,isDirectory:!0,isFile:!1,mtimeMs:0,size:0},v=await fe(B);(k=v.manifest)!=null&&k.version||(v.manifest={...v.manifest??{},version:g.version}),v.marketplace=u,x.push(v)}}}}catch{}s||(x.sort((j,w)=>j.name.localeCompare(w.name)),s||(o(x),i(!1)))})(),()=>{s=!0}},[t]);const S=[{key:"name",header:"name",render:s=>{var x;return e.jsxs("div",{style:{maxWidth:"16rem"},children:[e.jsx("div",{className:"truncate",children:s.name}),(x=s.manifest)!=null&&x.description?e.jsx("div",{className:"truncate text-fg-faint",title:s.manifest.description,children:s.manifest.description}):null]})},width:"16rem"},{key:"origin",header:"origin",render:s=>e.jsx(z,{interactive:!1,input:q(s)}),width:"7rem"},{key:"version",header:"version",render:s=>{var x;return((x=s.manifest)==null?void 0:x.version)??e.jsx("span",{className:"text-fg-faint",children:"—"})},width:"6rem"},{key:"manifest",header:"manifest",render:s=>s.hasManifest?"plugin.json":e.jsx("span",{className:"text-fg-faint",children:"—"}),width:"8rem"},{key:"contents",header:"contents",render:s=>e.jsx("span",{className:"font-mono text-fg-faint text-xs",children:[s.agents>0?`${s.agents} agents`:null,s.skills>0?`${s.skills} skills`:null,s.hooks>0?`${s.hooks} hooks`:null,s.monitors>0?`${s.monitors} monitors`:null,s.binCount>0?`${s.binCount} bin`:null,s.hasLsp?"lsp":null,s.hasMcp?"mcp":null].filter(Boolean).join(" · ")||"—"})},{key:"path",header:"path",render:s=>e.jsx("span",{className:"font-mono text-fg-faint truncate",children:s.path})}],$=p.useMemo(()=>f?Object.keys(f).filter(s=>f[s].status==="exhausted"&&!c.has(s)):[],[f,c]),E=$.length>0?e.jsx(de,{seeders:$,seedStatus:f,onDismiss:s=>L(x=>new Set(x).add(s)),onOpenLibrary:()=>d("library")}):null;if(r==="library")return e.jsxs(R,{toolbar:e.jsx(W,{active:r,onChange:d}),children:[E,e.jsx(ee,{})]});const m=h?n.find(s=>s.path===h)??null:null;return e.jsxs(R,{toolbar:e.jsxs(e.Fragment,{children:[e.jsx(W,{active:r,onChange:d}),e.jsx("span",{className:"mx-2 text-fg-faint",children:"·"}),e.jsxs("span",{className:"text-fg-faint",children:[n.length," plugins"]}),e.jsx("div",{className:"flex-1"}),e.jsx("span",{className:"text-fg-faint font-mono truncate",children:"~/.claude/plugins/installed_plugins.json"})]}),children:[E,a?e.jsx(y,{title:"scanning plugins…"}):m?e.jsx(he,{row:m,home:t,onBack:()=>l(null)},m.path):e.jsx("div",{className:"flex flex-col h-full",children:e.jsx("div",{className:"flex-1 min-h-0 overflow-auto",children:e.jsx(se,{columns:S,rows:n,getKey:s=>s.path,activeKey:h,onRowClick:s=>l(s.path),empty:e.jsxs(e.Fragment,{children:["no plugins installed",e.jsxs("div",{className:"mt-2 text-fg-faint",children:["drop a plugin into ",e.jsx("code",{children:"~/.claude/plugins/"})]})]})})})})]})}const ve=p.memo(ce),K={"dev-plugin":"session-manager-dev plugin","scheduler-mcp":"Scheduler MCP server","agent-personas":"Agent personas (Architect, Dev Lead)"};function de({seeders:t,seedStatus:n,onDismiss:o,onOpenLibrary:a}){return e.jsx("div",{className:"shrink-0 flex flex-col gap-1.5 px-3 py-2 border-b border-line bg-yellow-950/20",children:t.map(i=>e.jsxs("div",{className:"flex items-start gap-2 text-xs text-yellow-300",children:[e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"font-medium",children:[K[i]," setup failed after retrying — manual fix needed"]}),i==="dev-plugin"?e.jsxs("div",{className:"text-yellow-400/80 mt-0.5",children:["Click ",e.jsx("button",{onClick:a,className:"underline hover:no-underline",children:"here"}),' to open the Library tab, then click Install next to "session-manager-dev".']}):e.jsx("div",{className:"text-yellow-400/80 mt-0.5 font-mono",children:n[i].fix})]}),e.jsx("button",{onClick:()=>o(i),className:"shrink-0 text-yellow-400/70 hover:text-yellow-300","aria-label":`Dismiss ${K[i]} warning`,children:"✕"})]},i))})}function W({active:t,onChange:n}){const o=[["installed","Installed"],["library","Library"]];return e.jsx("div",{className:"flex rounded border border-line overflow-hidden",role:"tablist","aria-label":"Plugins view",children:o.map(([a,i])=>e.jsx("button",{onClick:()=>n(a),role:"tab","aria-selected":t===a,className:`px-2 py-0.5 text-xs ${t===a?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,children:i},a))})}function he({row:t,home:n,onBack:o}){var j,w,C,A,_;const[a,i]=p.useState([]),[h,l]=p.useState([]),[r,d]=p.useState([]),[f,b]=p.useState([]),[c,L]=p.useState("skills"),[S,$]=p.useState(null),[E,m]=p.useState(!0);p.useEffect(()=>{if(!n)return;let u=!1;return(async()=>{m(!0);try{const[g,B,v,Q]=await Promise.all([ie(t.name,t.path,n),O(`${t.path}/agents`,{filesOnly:!0}),t.hasMcp?window.api.config.readJson(`${t.path}/.mcp.json`):Promise.resolve(null),O(`${t.path}/bin`,{filesOnly:!0})]),J=g?await ae(g):[],T=B.filter(N=>N.endsWith(".md")),U=await Promise.all(T.map(N=>window.api.config.readText(`${t.path}/agents/${N}`))),I=[];T.forEach((N,F)=>{const M=U[F];if(!M.exists)return;const D=G(M.text);I.push({id:N,name:D.name??N.replace(/\.md$/,""),description:D.description})});const H=[];if(v&&v.exists&&!v.parseError&&v.data&&typeof v.data=="object"){const N=v.data.mcpServers;if(N&&typeof N=="object")for(const[F,M]of Object.entries(N)){const D=M&&typeof M=="object"?M.command:void 0;H.push({id:F,name:F,description:typeof D=="string"?D:null})}}if(u)return;i(J),l(I),d(H),b(Q),L(J.length>0?"skills":I.length>0?"agents":H.length>0?"mcp":t.hooks>0?"hooks":t.monitors>0?"monitors":t.hasLsp?"lsp":"files")}catch(g){if(u)return;V.error(`Failed to load plugin contents: ${g instanceof Error?g.message:String(g)}`),i([]),l([]),d([]),b([])}finally{u||m(!1)}})(),()=>{u=!0}},[t.path,t.name,t.hasMcp,t.hooks,t.monitors,t.hasLsp,n]);const s=[{key:"skills",label:"Skills",count:a.length},{key:"agents",label:"Agents",count:h.length},{key:"mcp",label:"MCP server",count:r.length},{key:"hooks",label:"Hooks",count:t.hooks},{key:"monitors",label:"Monitors",count:t.monitors},{key:"lsp",label:"LSP",count:t.hasLsp?1:0},{key:"files",label:"Bin",count:f.length}],x=(j=t.manifest)!=null&&j.author?typeof t.manifest.author=="string"?t.manifest.author:t.manifest.author.name??null:null,k=async u=>{const g=await window.api.shell.open({as:"external",url:u});g.ok||V.error(g.error??`Failed to open ${u}`)};return S?e.jsx(oe,{pluginName:((w=t.manifest)==null?void 0:w.name)??t.name,skills:a,initialSelectedId:S.id,onBack:()=>$(null)},S.id):e.jsxs("div",{className:"flex flex-col h-full min-h-0 overflow-hidden",children:[e.jsxs("div",{className:"shrink-0 h-11 flex items-baseline gap-3.5 px-3 border-b border-line bg-bg",children:[e.jsx("button",{type:"button",onClick:o,className:"font-mono text-accent hover:underline shrink-0 text-xs",children:"← Plugins"}),e.jsx("span",{className:"font-serif text-[22px] text-fg shrink-0 truncate max-w-[16rem]",children:((C=t.manifest)==null?void 0:C.name)??t.name}),(A=t.manifest)!=null&&A.version?e.jsxs("span",{className:"font-mono text-fg-faint text-xs shrink-0",children:["v",t.manifest.version]}):null,(_=t.manifest)!=null&&_.description?e.jsx("span",{className:"text-fg-dim text-xs min-w-0 flex-1 overflow-hidden text-ellipsis whitespace-nowrap",children:t.manifest.description}):e.jsx("span",{className:"flex-1"}),e.jsxs("div",{className:"flex items-center gap-1.5 shrink-0",children:[e.jsx(z,{input:q(t)}),t.hasMcp?e.jsx(Z,{tone:"accent",children:"MCP"}):null]})]}),t.manifest?e.jsxs("div",{className:"shrink-0 h-[26px] flex items-center gap-[18px] px-3 bg-bg-elev border-b border-line text-[9px] font-mono uppercase tracking-wide overflow-hidden",children:[x?e.jsxs("span",{className:"text-fg-dim truncate",children:[e.jsx("span",{className:"text-fg-faint",children:"Author "}),x]}):null,t.manifest.license?e.jsxs("span",{className:"text-fg-dim truncate",children:[e.jsx("span",{className:"text-fg-faint",children:"License "}),t.manifest.license]}):null,t.manifest.homepage?e.jsxs("button",{onClick:()=>k(t.manifest.homepage),className:"text-accent hover:underline truncate normal-case",title:t.manifest.homepage,children:[e.jsx("span",{className:"text-fg-faint uppercase",children:"Homepage "}),t.manifest.homepage]}):null,e.jsx("span",{className:"ml-auto text-fg-faint truncate normal-case",children:t.path})]}):e.jsx("div",{className:"shrink-0 h-[26px] flex items-center px-3 bg-bg-elev border-b border-line text-[9px] font-mono uppercase tracking-wide overflow-hidden",children:e.jsx("span",{className:"ml-auto text-fg-faint truncate normal-case",children:t.path})}),e.jsxs("div",{className:"flex-1 min-h-0 grid overflow-hidden",style:{gridTemplateColumns:"170px 1fr"},children:[e.jsx("div",{className:"min-h-0 overflow-auto bg-bg-elev border-r border-line py-1",children:s.map(u=>e.jsxs("button",{type:"button",disabled:u.count===0,onClick:()=>L(u.key),className:`w-full text-left px-3 py-1.5 text-xs flex items-center justify-between ${u.count===0?"text-fg-faint opacity-50 cursor-default":c===u.key?"bg-bg-hi text-fg":"text-fg-dim hover:text-fg hover:bg-bg-hi"}`,children:[e.jsx("span",{children:u.label}),u.count>0?e.jsx("span",{className:"font-mono text-fg-faint",children:u.count}):null]},u.key))}),e.jsx("div",{className:"min-h-0 overflow-auto",children:E?e.jsx(y,{title:"scanning…"}):e.jsx(me,{active:c,row:t,skills:a,agents:h,mcpServers:r,files:f,onOpenSkill:$})})]})]})}function me({active:t,row:n,skills:o,agents:a,mcpServers:i,files:h,onOpenSkill:l}){return t==="skills"?o.length===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:o.map((r,d)=>e.jsx(P,{n:d+1,name:r.name??r.id,description:r.description,onClick:()=>l(r)},r.id))}):t==="agents"?a.length===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:a.map((r,d)=>e.jsx(P,{n:d+1,name:r.name??r.id,description:r.description},r.id))}):t==="mcp"?i.length===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:i.map((r,d)=>e.jsx(P,{n:d+1,name:r.name,description:r.description},r.id))}):t==="hooks"?n.hooks===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:e.jsx(P,{n:1,name:`${n.hooks} ${n.hooks===1?"hook":"hooks"} configured`,description:`${n.path}/hooks/hooks.json`})}):t==="monitors"?n.monitors===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:e.jsx(P,{n:1,name:`${n.monitors} ${n.monitors===1?"monitor":"monitors"} configured`,description:`${n.path}/monitors/monitors.json`})}):t==="lsp"?n.hasLsp?e.jsx("div",{children:e.jsx(P,{n:1,name:"LSP server enabled",description:`${n.path}/.lsp.json`})}):e.jsx(y,{title:"no components"}):h.length===0?e.jsx(y,{title:"no components"}):e.jsx("div",{children:h.map((r,d)=>e.jsx(P,{n:d+1,name:r,description:null},r))})}function P({n:t,name:n,description:o,onClick:a}){const i=a?"button":"div";return e.jsxs(i,{onClick:a,className:`w-full flex items-baseline gap-2.5 py-[9px] px-3 border-b border-line text-left min-w-0 ${a?"hover:bg-bg-hi cursor-pointer":""}`,children:[e.jsx("span",{className:"font-serif text-fg-faint text-xs shrink-0 w-6 text-right",children:String(t).padStart(2,"0")}),e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsx("div",{className:"font-mono font-semibold text-accent truncate",children:n}),o?e.jsx("div",{className:"text-fg-dim text-xs truncate",children:o}):null]})]})}async function fe(t){let n=`${t.path}/.claude-plugin/plugin.json`,o=await window.api.config.exists(n);o||(n=`${t.path}/plugin.json`,o=await window.api.config.exists(n));let a=null;if(o){const m=await window.api.config.readJson(n);m.exists&&!m.parseError&&m.data&&typeof m.data=="object"&&(a=m.data)}const[i,h,l,r]=await Promise.all([window.api.config.exists(`${t.path}/.lsp.json`),window.api.config.exists(`${t.path}/.mcp.json`),window.api.config.exists(`${t.path}/monitors/monitors.json`),window.api.config.exists(`${t.path}/hooks/hooks.json`)]);let d=0;if(l){const m=await window.api.config.readJson(`${t.path}/monitors/monitors.json`);Array.isArray(m.data)?d=m.data.length:m.data&&typeof m.data=="object"&&Array.isArray(m.data.monitors)&&(d=m.data.monitors.length)}let f=0;if(r){const m=await window.api.config.readJson(`${t.path}/hooks/hooks.json`);if(m.data&&typeof m.data=="object"){const s=m.data;f=Object.values(s).reduce((x,k)=>Array.isArray(k)?x+k.reduce((j,w)=>j+(Array.isArray(w.hooks)?w.hooks.length:0),0):x,0)}}const[b,c,L]=await Promise.all([O(`${t.path}/agents`,{filesOnly:!0}),O(`${t.path}/bin`,{filesOnly:!0}),O(`${t.path}/skills`,{dirsOnly:!0})]),S=b.filter(m=>m.endsWith(".md")).length,$=c.length,E=L.length;return{name:t.name,path:t.path,mtimeMs:t.mtimeMs,hasManifest:o,manifest:a,hasLsp:i,monitors:d,hooks:f,agents:S,skills:E,binCount:$,hasMcp:h}}async function O(t,n){try{return(await window.api.config.listDir(t,n)).entries.map(a=>a.name)}catch{return[]}}export{ve as Plugins};