geneseed 3.1.2 → 3.1.3

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 (38) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/SHIPPED.md +1 -1
  3. package/docs/web/exclusions.md +1 -1
  4. package/docs/web/footprint.md +1 -1
  5. package/docs/web/foreman-mode.md +2 -2
  6. package/docs/web/themes-author.md +1 -1
  7. package/docs/web-ui.md +41 -29
  8. package/harness.config.json +1 -1
  9. package/js/web/api.mjs +82 -1
  10. package/js/web/routes.mjs +22 -0
  11. package/package.json +1 -1
  12. package/web/dist/assets/{Activity-z0uXdS7J.js → Activity-B49fE_ug.js} +1 -1
  13. package/web/dist/assets/{ActivityDetail-CEj5IJpF.js → ActivityDetail-abt7hzD2.js} +1 -1
  14. package/web/dist/assets/{Diff-BMR8fPuG.js → Diff-D3_bVkih.js} +1 -1
  15. package/web/dist/assets/{Doctor-Bz5qE4r9.js → Doctor-hXp7srsx.js} +1 -1
  16. package/web/dist/assets/{Graph-BV6a6t2l.js → Graph-DvYMtlvl.js} +1 -1
  17. package/web/dist/assets/Harness-BIWei7N5.js +1 -0
  18. package/web/dist/assets/Laws-CxmnRdwt.js +3 -0
  19. package/web/dist/assets/{Library-CiXN9sC3.js → Library-MjbOkHIW.js} +1 -1
  20. package/web/dist/assets/{Markdown-DG6FjUz-.js → Markdown-Dx_w0faR.js} +1 -1
  21. package/web/dist/assets/{Profile-B2oSvdxH.js → Profile-BRFvv2gr.js} +1 -1
  22. package/web/dist/assets/{Rules-DobVPd80.js → Rules-CfsV1EVy.js} +1 -1
  23. package/web/dist/assets/{Skills-DQPsvsn9.js → Skills-DArA6IHS.js} +1 -1
  24. package/web/dist/assets/{StatusBadge-BhJ3HhdI.js → StatusBadge-C9bZOEZk.js} +1 -1
  25. package/web/dist/assets/{activity-D9mfv7pY.js → activity-Cc0n2jwx.js} +1 -1
  26. package/web/dist/assets/index-Bx3oxZ1o.css +1 -0
  27. package/web/dist/assets/index-DC3-GlCY.js +1 -0
  28. package/web/dist/assets/index-DP035wPa.js +12 -0
  29. package/web/dist/assets/{index-DGM5j2hB.js → index-dHi-7M-F.js} +1 -1
  30. package/web/dist/index.html +2 -2
  31. package/web/src/pages/Laws.jsx +4 -18
  32. package/web/dist/assets/About-CPvIFZGh.js +0 -1
  33. package/web/dist/assets/Harnesses-C5Ag5PaQ.js +0 -1
  34. package/web/dist/assets/Laws-BK07NKw9.js +0 -3
  35. package/web/dist/assets/Themes-BP38FBU9.js +0 -2
  36. package/web/dist/assets/index-BE226Gv6.css +0 -1
  37. package/web/dist/assets/index-BZ7cPLMI.js +0 -1
  38. package/web/dist/assets/index-BgIs5eIa.js +0 -12
package/CHANGELOG.md CHANGED
@@ -6,6 +6,61 @@ labels in `harness.config.json`. The canonical identity of an *installed* harnes
6
6
  is the source fingerprint in `.geneseed-version` (see `geneseed version`), not this
7
7
  label. For the capability ↔ spec map, see [SHIPPED.md](SHIPPED.md).
8
8
 
9
+ ## [3.1.3] — 2026-08-24
10
+
11
+ ### Added
12
+
13
+ - **The Journal Atlas console.** A fourth dashboard lens, `journal`: growth rings per section,
14
+ the newest hand-authored entries, and the constitution drawn as a map — a hub of rules in
15
+ force, its three tiers, and dashed edges to the skills, agents, memory and wiki they govern.
16
+ Every node navigates. The map is data-driven where the design was hand-placed: circles size
17
+ to the text they hold and the pack nodes ride a fitted arc, so a harness with seven
18
+ invariants or six packs draws the same picture rather than overflowing it.
19
+ - **`atlas`, a twelfth console skin** — loam dark, sage ink, Space Grotesk, pills, in both
20
+ light and dark. Atlas and Greenhouse default to the journal lens; every other skin is
21
+ unchanged, and the lens is pickable from any of them in Settings.
22
+ - **`GET /api/recent`** — the newest file-backed entries across the harness. Nothing on the API
23
+ carried a per-entry date: every catalog row is `{name, title, desc, source}` and a browser
24
+ cannot stat a path. It answers only the four sections a person writes by hand, and drops any
25
+ section too large to stat exhaustively rather than reporting the newest of a truncated
26
+ sample. Declared in `GET_BEYOND_REF` — the GET twin of `POST_BEYOND_REF` — so the route
27
+ partition keeps its set equality against a reference surface that must not be rewritten.
28
+ - **`overview.install` now reports `build_epoch`** beside `build_time`, so a relative label is
29
+ computed rather than parsed back out of a formatted stamp.
30
+ - **`Ctrl`/`⌘`+`K` opens Spotlight**, beside `/`. The chord is the one that still works from
31
+ inside a text field, which is where `/` deliberately does not fire.
32
+
33
+ ### Changed
34
+
35
+ - **The rail regroups into Codex · Care · Setup**, with the Dashboard on its own and a vitals
36
+ card above the index — germination, drift, the doctor's verdict, and how long ago the harness
37
+ was built, on every page. Doctor badges `clean` in words; it used to badge nothing, which
38
+ reads the same as never having run.
39
+ - **Harnesses and Themes merge into one Harness page**, and About folds into Settings. No route
40
+ dies: `#/harnesses`, `#/themes` and `#/about` all still resolve. An uninstalled host now
41
+ offers one `Install…` that discloses its four choices instead of rendering them inline.
42
+ - **The voice gallery is a reference, not a second control.** Every install picks its own voice
43
+ beside its own footprint, posture and mode; a gallery at the top of the page could only ever
44
+ act on one of them. It keeps the taglines — which a `<select>` of bare names cannot show —
45
+ and gives up the buttons.
46
+ - **The rail width is responsive**, `clamp(232px, 18vw, 256px)`, between the two widths the
47
+ console has been designed at. The breakpoint overrides still win, so it still collapses.
48
+
49
+ ### Fixed
50
+
51
+ - **The Spotlight input was never wired to its own listbox.** No `role="combobox"`,
52
+ `aria-expanded`, `aria-controls`, or `aria-activedescendant`, and the rows had no ids —
53
+ so the arrow keys moved a highlight that was visual and nothing else, and a screen reader
54
+ was never told results had appeared.
55
+ - **The rail overflowed the document** on short windows once the vitals card was added: a fixed
56
+ `100vh` box with no scroller of its own produced a second scrollbar beside the page's.
57
+ - **`web/dist` is now gated on the publish path.** It ships in `files[]`, and until now nothing
58
+ on that path built it or looked at it — the freshness check ran only on pull requests, while
59
+ `workflow_dispatch` can publish any ref.
60
+ - **Two tests in `laws.test.jsx` failed roughly one run in three.** They gated on rows existing,
61
+ but the pack selection is seeded in an effect, so there is a rendered frame in which every
62
+ pack reads as off. The same race, twice, and neither was a product defect.
63
+
9
64
  ## [3.1.2] — 2026-08-24
10
65
 
11
66
  ### Changed
package/SHIPPED.md CHANGED
@@ -20,7 +20,7 @@ historical pointers, not files distributed with the repo.
20
20
  > **On version numbers.** [`harness.config.json`](harness.config.json)'s `version` is a
21
21
  > human-readable label only. The canonical identity of an installed harness is the
22
22
  > **source fingerprint** stamped in `.geneseed-version` (see `geneseed version`), not the
23
- > string. Current label: **3.1.2**.
23
+ > string. Current label: **3.1.3**.
24
24
 
25
25
  > **Spec status audit (2026-06-15).** Every dated spec authored before today was
26
26
  > verified against the source tree and confirmed implemented (code + tests + docs);
@@ -3,7 +3,7 @@ group: concepts
3
3
  order: 9
4
4
  title: "Sovereign repos (exclusions)"
5
5
  kind: "concept"
6
- link: {"hash": "#/harnesses", "label": "Manage in Harnesses →"}
6
+ link: {"hash": "#/harness", "label": "Manage in Harness →"}
7
7
  ---
8
8
  A **sovereign repo** is a folder where every GLOBAL harness install goes fully dormant: hooks stay silent and the global preamble (`AGENT.md`/`CLAUDE.md`) is never loaded there. A repo that ships its own agent config you don't want a global harness layered onto is the usual case.
9
9
 
@@ -29,7 +29,7 @@ Neither setting hides text from the agent; they hide it from *every turn*.
29
29
  It's set-and-forget — stored in the `.geneseed-footprint` marker, preserved across every rebuild, identical on every host (OpenCode, Claude Code, Bob, Copilot). Changing it re-emits the install.
30
30
 
31
31
  - **Settings** — the **Footprint** toggle flips the current install (full ⇄ lean) and rebuilds it in place.
32
- - **Harnesses tab** — a per-harness dropdown sets it for any one install independently, then **Apply**.
32
+ - **Harness tab** — on an active install, a per-harness dropdown sets it for that install independently, then **Apply**. On a host that is not installed yet, it is step 2 of **Install…**.
33
33
  - **Setup / re-theme wizard** — asks for footprint alongside voice, posture, mode, and doctrine packs.
34
34
  - **CLI** — `geneseed-build --footprint lean` (with any `--emit`).
35
35
 
@@ -3,7 +3,7 @@ group: concepts
3
3
  order: 12
4
4
  title: "Foreman mode"
5
5
  kind: "concept"
6
- link: {"hash": "#/harnesses", "label": "Set the mode in Harnesses →"}
6
+ link: {"hash": "#/harness", "label": "Set the mode in Harness →"}
7
7
  ---
8
8
  **Mode** is the session's *operating* register — how work gets executed — one of four build-time dials, beside posture (the *relationship* register), [doctrine packs](#/docs/setup-choices) (which practice rules bind) and footprint (how much of the constitution loads inline). Two modes ship, set at build time so the choice holds steady instead of drifting mid-session, and preserved across every rebuild and re-theme.
9
9
 
@@ -24,7 +24,7 @@ Because the crew runs apart from the conversation, the session stays responsive
24
24
 
25
25
  Set-and-forget, stored beside the install and preserved across rebuilds and re-themes:
26
26
 
27
- - **Harnesses tab** — the per-install **Mode** dropdown (beside Voice, Footprint, and Posture), then **Apply**.
27
+ - **Harness tab** — on an active install, the per-install **Mode** dropdown (beside Voice, Footprint, and Posture), then **Apply**. On a host that is not installed yet, the same four choices are steps inside **Install…**.
28
28
  - **Setup / re-theme wizard** — asks for mode alongside voice, posture, doctrine packs, and footprint.
29
29
  - **CLI** — `geneseed-build --mode foreman`; switch back with `--mode direct`.
30
30
 
@@ -3,7 +3,7 @@ group: extend
3
3
  order: 1
4
4
  title: "Write a new theme"
5
5
  kind: "concept"
6
- link: {"hash": "#/themes", "label": "Open the theme gallery →"}
6
+ link: {"hash": "#/harness", "label": "Open the voice gallery →"}
7
7
  ---
8
8
  A theme is one JSON file under `themes/` declaring voice tokens only: `BANNER`, `TAGLINE`, `LOADED_SIGIL`, `VOICE`, the core nouns (`ONTOLOGY`/`LAW(S)`/`DOCTRINE(S)`/`AGENT(S)`/`SKILL(S)`/`MEMORY`/`NOTEBOOK`/`WIKI`), the section intros `INTRO_*`, the epigraphs `EPI_*`, the `BENEDICTION`, the `ROAST_PERSONA`, and `DESC_*` blurbs. Copy `themes/neutral.json` and edit. `geneseed build --theme yours` renders it; `doctor` checks for missing tokens.
9
9
 
package/docs/web-ui.md CHANGED
@@ -50,12 +50,17 @@ The left rail mirrors the harness's own shape:
50
50
 
51
51
  | Group | Views |
52
52
  | --- | --- |
53
- | **🧬 Harness** | Dashboard · Activity · Constitution · Rules · Profile · Skills · Agents · Library · Docs · Graph |
54
- | **🔧 Maintain** | Changes · Doctor |
55
- | **🎨 Configure** | Harnesses · Themes · Settings |
56
- | **ℹ️ About** | About |
53
+ | *(ungrouped)* | Dashboard |
54
+ | **📖 Codex** | Constitution · Rules · Profile · Skills · Agents · Library · Docs · Graph |
55
+ | **🩺 Care** | Activity · Changes · Doctor |
56
+ | **🎨 Setup** | Harness · Settings |
57
57
 
58
- A global **Spotlight** search lives in the topbar — press <kbd>/</kbd> to focus it, type
58
+ Above the index sits a **vitals** card — the germination ring, drift, the doctor's verdict
59
+ and how long ago the harness was built — so those four facts are on screen from every page.
60
+ It is hidden where the rail collapses to icons (≤960px) and returns with the phone drawer.
61
+
62
+ A global **Spotlight** search lives in the topbar — press <kbd>/</kbd> to focus it (or
63
+ <kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>K</kbd>, which also works from inside a text field), type
59
64
  to jump to any agent, skill, doc, spec, MCP server, or constitutional rule — the `laws`
60
65
  catalog it indexes is the whole constitution, so an ontology section, an invariant and a
61
66
  doctrine rule are all reachable by name; <kbd>↑</kbd>/<kbd>↓</kbd> to
@@ -129,32 +134,39 @@ streams the output of background jobs and keeps their history across reloads.
129
134
  theme, parity, links, authoring gates, drift), grouping any problems per check. Re-run
130
135
  on demand.
131
136
 
132
- ### 🎨 Configure
133
-
134
- - **Harnesses** (`#/harnesses`) — every detected install (host × scope), one row each,
135
- with per-row **Voice**, **Footprint**, **Posture**, and **Mode** pickers plus
136
- Install/Apply, an on/off switch, and remove. **Posture** is the relationship register
137
- (peer/mentor/expert/assistant/artisan — default **peer**); **Mode** is the operating
138
- register (direct/foreman — default **direct**; foreman triages tasks into isolated
139
- pipelines instead of working every one itself). Both are orthogonal to voice and to
140
- each other, picked here or via the setup wizard, and preserved across every rebuild.
141
- "Deploy to folder…" and "Rebuild all" apply to the whole set. An **Excluded folders**
142
- card (shown once a global install exists) manages sovereign repos — folders where every
143
- global harness goes dormant (hooks stay silent, the global preamble never loads);
144
- add or remove one here, the web mirror of `harness exclude add|remove|list`.
145
- - **Themes** (`#/themes`) — a gallery of the shipped voices (name, tagline, sigil, accent
146
- glow). **Apply** a voice to rebuild the deployed harness in that theme; the current one
147
- is marked.
137
+ ### 🎨 Setup
138
+
139
+ - **Harness** (`#/harness`) — this machine's own install first (path, voice, footprint,
140
+ and when it was built), then the **Voice** reference, then every detected install.
141
+ `#/harnesses` and `#/themes` both still resolve here, so old links and bookmarks keep
142
+ working.
143
+ - The **Voice** card is the former Themes tab, kept as a REFERENCE rather than a second
144
+ control: a collapsed row that opens to every shipped voice with its tagline, the
145
+ deployed one pinned first and marked. It is the one place you can read what a voice
146
+ sounds like — the per-install picker below is a list of bare names. Applying happens
147
+ there, per install, beside that install's own footprint, posture and mode; a gallery at
148
+ the top of the page could only ever act on one of them.
149
+ - **Every install** lists each detected install (host × scope), one row each. An
150
+ ACTIVE row carries per-row **Voice**, **Footprint**, **Posture** and **Mode**
151
+ pickers plus **Apply**, an on/off switch, and remove; a host that is NOT installed
152
+ yet shows a single **Install…** that discloses the same four choices as steps.
153
+ **Posture** is the relationship register (peer/mentor/expert/assistant/artisan —
154
+ default **peer**); **Mode** is the operating register (direct/foreman — default
155
+ **direct**; foreman triages tasks into isolated pipelines instead of working every
156
+ one itself). Both are orthogonal to voice and to each other, picked here or via the
157
+ setup wizard, and preserved across every rebuild.
158
+ - "Deploy to folder…" and "Rebuild all" apply to the whole set. An **Excluded folders**
159
+ card (shown once a global install exists) manages sovereign repos — folders where every
160
+ global harness goes dormant (hooks stay silent, the global preamble never loads);
161
+ add or remove one here, the web mirror of `harness exclude add|remove|list`.
148
162
  - **Settings** (`#/settings`) — a **Console direction** picker (the visual flavour of the
149
163
  console) with a **Dashboard layout** control (Auto follows each theme's designed Status
150
- lens; Cultivar / Greenhouse / Operator force one regardless of skin), the install snapshot
151
- (deployed/target/theme/version/memory store), a Maintenance card (PATH link/unlink,
152
- uninstall), an offline package download, and server controls (Stop). Build, update, and MCP
153
- wiring live in the Harnesses tab.
154
-
155
- ### ℹ️ About
156
-
157
- - **About** (`#/about`) — project + creator credits and the source link.
164
+ lens; Cultivar / Greenhouse / Operator / Journal force one regardless of skin), the
165
+ install snapshot (deployed/target/theme/version/memory store), a Maintenance card (PATH
166
+ link/unlink, uninstall), an offline package download, server controls (Stop), and the
167
+ **About** colophon — project + creator credits and the source link, folded in from the
168
+ retired `#/about` tab (which still resolves here). Build, update, and MCP wiring live in
169
+ the Harness tab.
158
170
 
159
171
  ## Rebuilding the UI
160
172
 
@@ -2,5 +2,5 @@
2
2
  "_comment": "version is a human-readable release label only; the canonical identity of an installed harness is the source fingerprint stamped in .geneseed-version (see `harness version`), not this string.",
3
3
  "theme": "neutral",
4
4
  "product": "Geneseed",
5
- "version": "3.1.2"
5
+ "version": "3.1.3"
6
6
  }
package/js/web/api.mjs CHANGED
@@ -842,8 +842,18 @@ export function apiOverview(state) {
842
842
  }
843
843
  }
844
844
  let buildTime = null;
845
+ // The SAME mtime twice, on purpose. `build_time` is the stamp a person reads; `build_epoch`
846
+ // is the number a relative label ("20h") is computed from. The console used to have only the
847
+ // first and had to re-parse it, and `Date.parse` on a space-separated local stamp is
848
+ // engine-dependent — a plausible-but-wrong "2h" is the exact failure a second field costs
849
+ // nothing to prevent.
850
+ let buildEpoch = null;
845
851
  const agentMd = path.join(state.target, 'AGENT.md');
846
- if (isFile(agentMd)) buildTime = stampMinute(statSync(agentMd).mtimeMs);
852
+ if (isFile(agentMd)) {
853
+ const ms = statSync(agentMd).mtimeMs;
854
+ buildTime = stampMinute(ms);
855
+ buildEpoch = Math.floor(ms / 1000);
856
+ }
847
857
  // Which detected install the current view points at, so the dashboard's footprint hero
848
858
  // can re-emit exactly it. Mirrors `viewCfg`'s rule, spelled out as the reference spells
849
859
  // it out — the two are separate code in the Python too.
@@ -903,6 +913,7 @@ export function apiOverview(state) {
903
913
  doctor: state.doctor,
904
914
  diff,
905
915
  build_time: buildTime,
916
+ build_epoch: buildEpoch,
906
917
  // The RELEASE LABEL OF THE SOURCE THIS CONSOLE IS SERVED FROM, not of the deployed install —
907
918
  // the two differ whenever something has changed since the last emit, which is exactly when
908
919
  // knowing which one you are looking at matters. `.geneseed-version` carries the install's own
@@ -916,6 +927,73 @@ export function apiOverview(state) {
916
927
  };
917
928
  }
918
929
 
930
+ /**
931
+ * How many files `apiRecent` will `stat` before it gives up on a section.
932
+ *
933
+ * The wiki is the only section with no ceiling — `WIKI_FILE_CAP` lets one vault contribute
934
+ * 5000 pages — and "the newest N of the first 1200 I looked at" is not the newest N. So a
935
+ * section over the cap is DROPPED and NAMED in `skipped`, rather than answered from a
936
+ * truncated pool: the card can then say which shelves it read, which is a fact, where a
937
+ * silently-biased list would be a wrong one nobody could catch.
938
+ */
939
+ const RECENT_STAT_CAP = 1200;
940
+
941
+ /** How many entries `/api/recent` returns — the card shows a handful, not a feed. */
942
+ const RECENT_LIMIT = 8;
943
+
944
+ /**
945
+ * THE NEWEST FILE-BACKED ENTRIES ACROSS THE HARNESS — the "freshly grown" card's whole payload.
946
+ *
947
+ * A GET BEYOND THE REFERENCE (`GET_BEYOND_REF` in `routes.mjs`), and the first one. It exists
948
+ * because NOTHING else in this API carries a per-entry date: every catalog row is
949
+ * `{name, title, desc, source}` and the only two timestamps on the whole surface are
950
+ * `overview.build_time` and `doctor.checked_at`. A client cannot stat a path, so "what changed
951
+ * lately" was unanswerable without a server that looks.
952
+ *
953
+ * ONLY THE SECTIONS THE USER WRITES BY HAND. Memory, notebook, wiki and config each name a
954
+ * file someone actually authored, at a moment that means something.
955
+ *
956
+ * ⚠ SKILLS AND AGENTS ARE EXCLUDED, AND THEY WERE IN HERE FIRST — a live check is what took
957
+ * them out. Their `source` is the EMITTED artefact (`~/.config/opencode/skills/<x>/SKILL.md`),
958
+ * written by the last build, so on a real install all 67 of them carry ONE mtime and it is
959
+ * `overview.build_epoch` to the second. Sorted newest-first they filled the whole answer with
960
+ * the alphabetical head of the last rebuild — `advocate, architect, brainstorm, clarify…` —
961
+ * and pushed out every genuinely recent memory and note. Dating a rendered file dates the
962
+ * BUILD, which the overview already states once and correctly. (Reading the repo's `src/`
963
+ * instead would not save it: a fresh clone stamps every file with the checkout.)
964
+ *
965
+ * The constitution is out for the same family of reason: its tiers are entries inside shared
966
+ * law files, so a per-rule mtime would be the file's, repeated — one wrong date on nine rows.
967
+ */
968
+ export function apiRecent(state) {
969
+ const sections = [
970
+ ['memory', 'memory', () => memoryItems(state)],
971
+ ['notebook', 'notebook', () => notebookItems(state)],
972
+ ['wiki', 'wiki', () => wikiItems(state)],
973
+ ['config', 'config', () => configItems(state)],
974
+ ];
975
+ const items = [];
976
+ const skipped = [];
977
+ for (const [section, type, load] of sections) {
978
+ let rows;
979
+ try { rows = load() ?? []; } catch { skipped.push(section); continue; }
980
+ if (rows.length > RECENT_STAT_CAP) { skipped.push(section); continue; }
981
+ for (const r of rows) {
982
+ if (!r.source) continue;
983
+ let mtime;
984
+ // A row whose file has been moved or deleted since the inventory was rendered is not an
985
+ // error — it is simply not among the newest anything. Skipping beats a null date that
986
+ // would sort somewhere.
987
+ try { mtime = Math.floor(statSync(r.source).mtimeMs / 1000); } catch { continue; }
988
+ items.push({ section, type, name: r.name, title: r.title || r.name, mtime });
989
+ }
990
+ }
991
+ // Newest first, ties broken by name so the order is stable between two requests a second
992
+ // apart — several files written by one rebuild share a second exactly.
993
+ items.sort((a, b) => (b.mtime - a.mtime) || comparePaths(a.name, b.name));
994
+ return { items: items.slice(0, RECENT_LIMIT), skipped, limit: RECENT_LIMIT };
995
+ }
996
+
919
997
  /** The source's release label, or null when it cannot be read. */
920
998
  function releaseLabel() {
921
999
  const v = sourceReleaseVersion({ config: CONFIG });
@@ -972,6 +1050,9 @@ function splitN(s, sep, n) {
972
1050
 
973
1051
  export const STATE_ROUTES = {
974
1052
  '/api/overview': apiOverview,
1053
+ // Beyond the reference — declared in `GET_BEYOND_REF`, which is what keeps the partition
1054
+ // gate's set equality honest instead of forging `REF_GET`.
1055
+ '/api/recent': apiRecent,
975
1056
  '/api/themes': apiThemes,
976
1057
  '/api/setup': apiSetup,
977
1058
  '/api/doctor': apiDoctor,
package/js/web/routes.mjs CHANGED
@@ -185,6 +185,28 @@ export const POST_BEYOND_REF = new Set([
185
185
  '/api/reveal',
186
186
  ]);
187
187
 
188
+ /**
189
+ * GET routes THAT NEVER EXISTED ON THE REFERENCE — the same fourth part of the partition, one
190
+ * verb over, and it exists for the reason `POST_BEYOND_REF` above spells out in full.
191
+ *
192
+ * `test_every_get_route_is_either_ported_or_declared_unported` asserts SET EQUALITY against
193
+ * `REF_GET`, a frozen literal of the paths the Python daemon answered. Widening that list to
194
+ * admit a route added in 2026 would forge the record; leaving the equality unqualified would
195
+ * mean this daemon may never grow a GET. So additions are enumerated here instead, and the
196
+ * gate compares the covered set against the reference's surface PLUS this one.
197
+ *
198
+ * `/api/recent` is the first, and it is a genuine gap rather than a convenience: no catalog row
199
+ * on the whole surface carries a date, and a browser cannot stat the `source` path it is handed
200
+ * — so "what changed lately" had no answer that did not involve the server looking. See
201
+ * `apiRecent` in `api.mjs` for what it will and will not date.
202
+ *
203
+ * A route belongs here only once it is dispatched; the dispatcher probe is what keeps this from
204
+ * becoming a declaration nobody honours.
205
+ */
206
+ export const GET_BEYOND_REF = new Set([
207
+ '/api/recent',
208
+ ]);
209
+
188
210
  export function notPorted(path) {
189
211
  return NOT_PORTED.has(path) || NOT_PORTED_PREFIXES.some((p) => path.startsWith(p));
190
212
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "//": "Every field here is argued and gated in tests/unit/package_manifest.test.mjs. `files`, `bin` and `engines` each have a test that fails when reality drifts from this file; `version` mirrors harness.config.json and is gated too. Read that docblock before editing this one.",
3
3
  "name": "geneseed",
4
- "version": "3.1.2",
4
+ "version": "3.1.3",
5
5
  "description": "A portable, theme-able harness you implant once and use everywhere to grow a disciplined AI coding agent.",
6
6
  "keywords": [
7
7
  "ai",
@@ -1 +1 @@
1
- import{j as e,E as v,L as y,a as x,I as g,r as f,g as u}from"./index-BgIs5eIa.js";import{r as p}from"./react-DOpiTn5v.js";import{S as j,b,E as h,c as N,a as w,T as k}from"./activity-D9mfv7pY.js";function S({s}){const l=j[s.status]||j.idle,n=s.files,a=s.todos,i=a&&a.total>0||s.error||s.blocked_on,r=()=>u(`#/activity/${encodeURIComponent(s.session_id)}`);return e.jsxs("div",{className:"card pad-md act-card",style:{cursor:"pointer"},onClick:r,role:"link",tabIndex:0,onKeyDown:d=>{d.key==="Enter"&&r()},children:[e.jsxs("div",{className:"row between wrap gap-12",children:[e.jsxs("div",{className:"row gap-10",style:{minWidth:0},children:[e.jsx("span",{className:`feed-dot ${l.cls||"acc"}`,style:{width:9,height:9}}),e.jsxs("div",{style:{minWidth:0},children:[e.jsx("h3",{style:{margin:0},children:s.title||b(s.cwd)||s.session_id}),s.cwd&&e.jsx("div",{className:"dim mono",style:{fontSize:12,...h},title:s.cwd,children:s.cwd})]})]}),e.jsxs("div",{className:"row gap-8",style:{flexShrink:0},children:[s.model&&e.jsx("span",{className:"badge mono",title:"model",children:s.model}),s.agent&&e.jsx("span",{className:"badge",children:s.agent}),e.jsxs("span",{className:`badge ${l.cls}`,children:[e.jsx("span",{className:"dot"}),l.label]}),e.jsx(g,{name:"chevron",className:"glyph act-chev"})]})]}),s.phase&&e.jsxs("div",{className:"row gap-8",style:{marginTop:10,fontSize:14},children:[e.jsx(g,{name:"activity"}),e.jsx("span",{style:h,children:s.phase})]}),(s.tokens>0||s.cost>0||s.turn_started_at||n&&n.count>0)&&e.jsxs("div",{className:"row wrap",style:{gap:16,marginTop:12,fontSize:13,color:"var(--text-2)"},children:[s.tokens>0&&e.jsxs("span",{children:[N(s.tokens)," tok"]}),s.cost>0&&e.jsxs("span",{children:["$",s.cost.toFixed(2)]}),s.turn_started_at?e.jsx("span",{children:e.jsx(w,{startedAt:s.turn_started_at})}):null,n&&n.count>0&&e.jsxs("span",{children:[n.count," file",n.count===1?"":"s"," ",e.jsxs("span",{style:{color:"var(--good)"},children:["+",n.additions]})," ",e.jsxs("span",{style:{color:"var(--bad)"},children:["−",n.deletions]})]})]}),i&&e.jsxs("div",{className:"row between wrap gap-12",style:{marginTop:12,paddingTop:10,borderTop:"1px solid var(--line)"},children:[a&&a.total>0?e.jsxs("span",{className:"row gap-8 dim",style:{fontSize:13},children:[e.jsx(k,{done:a.done,total:a.total}),"plan ",a.done,"/",a.total]}):e.jsx("span",{}),s.error?e.jsx("span",{className:"badge bad",title:s.error,style:{maxWidth:300,...h},children:s.error}):s.blocked_on?e.jsx("span",{className:"badge bad",title:s.blocked_on,style:{maxWidth:300,...h},children:s.blocked_on}):null]}),e.jsxs("div",{className:"dim mono",style:{fontSize:11,marginTop:8},children:["updated ",f(s.updated_at)," ago"]})]})}function I(){const[s,l]=p.useState(null),[n,a]=p.useState("");p.useEffect(()=>{let t=!0;const o=()=>x.activity().then(m=>{t&&(l(m),a(""))},m=>{t&&a(m.message)});o();const c=setInterval(o,2e3);return()=>{t=!1,clearInterval(c)}},[]);const i=s?s.enabled!==!1:!0,r=(s==null?void 0:s.activity)||[],d=async()=>{const t=!i;try{const o=await x.activityToggle(t);l(c=>({...c||{},enabled:o.enabled,activity:o.enabled?(c==null?void 0:c.activity)||[]:[]})),a("")}catch(o){a(o.message)}};return e.jsxs("div",{className:"narrow",children:[e.jsxs("div",{className:"head-row mb-16",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"h",children:"Activity"}),e.jsx("p",{className:"sub",children:"Live OpenCode sessions this harness is running: what each one is doing right now. Refreshes every couple of seconds; sessions drop off when their process exits."})]}),e.jsxs("div",{className:"row gap-10",style:{alignItems:"center",flexShrink:0},children:[e.jsx("span",{className:"dim mono",style:{fontSize:12},children:i?"tracking on":"tracking off"}),e.jsx("div",{className:`sw-toggle${i?" on":""}`,role:"switch","aria-checked":i,"aria-label":"Toggle activity tracking",tabIndex:0,onClick:d,onKeyDown:t=>{(t.key==="Enter"||t.key===" ")&&(t.key===" "&&t.preventDefault(),d())}})]})]}),e.jsx(v,{error:n,style:{marginBottom:12}}),s?i?r.length===0?e.jsxs("div",{className:"empty",children:[e.jsx("div",{className:"big",children:"No active sessions"}),"Run ",e.jsx("code",{children:"opencode"})," and the sessions it spins up appear here."]}):e.jsx("div",{className:"stack gap-12",children:r.map(t=>e.jsx(S,{s:t},t.session_id))}):e.jsxs("div",{className:"empty",children:[e.jsx("div",{className:"big",children:"Activity tracking is off"}),"The plugin isn't recording sessions. Flip the switch to turn it back on."]}):e.jsx(y,{label:"Reading live sessions…"})]})}export{I as default};
1
+ import{j as e,E as v,L as y,a as x,I as g,r as f,g as u}from"./index-DP035wPa.js";import{r as p}from"./react-DOpiTn5v.js";import{S as j,b,E as h,c as N,a as w,T as k}from"./activity-Cc0n2jwx.js";function S({s}){const l=j[s.status]||j.idle,n=s.files,a=s.todos,i=a&&a.total>0||s.error||s.blocked_on,r=()=>u(`#/activity/${encodeURIComponent(s.session_id)}`);return e.jsxs("div",{className:"card pad-md act-card",style:{cursor:"pointer"},onClick:r,role:"link",tabIndex:0,onKeyDown:d=>{d.key==="Enter"&&r()},children:[e.jsxs("div",{className:"row between wrap gap-12",children:[e.jsxs("div",{className:"row gap-10",style:{minWidth:0},children:[e.jsx("span",{className:`feed-dot ${l.cls||"acc"}`,style:{width:9,height:9}}),e.jsxs("div",{style:{minWidth:0},children:[e.jsx("h3",{style:{margin:0},children:s.title||b(s.cwd)||s.session_id}),s.cwd&&e.jsx("div",{className:"dim mono",style:{fontSize:12,...h},title:s.cwd,children:s.cwd})]})]}),e.jsxs("div",{className:"row gap-8",style:{flexShrink:0},children:[s.model&&e.jsx("span",{className:"badge mono",title:"model",children:s.model}),s.agent&&e.jsx("span",{className:"badge",children:s.agent}),e.jsxs("span",{className:`badge ${l.cls}`,children:[e.jsx("span",{className:"dot"}),l.label]}),e.jsx(g,{name:"chevron",className:"glyph act-chev"})]})]}),s.phase&&e.jsxs("div",{className:"row gap-8",style:{marginTop:10,fontSize:14},children:[e.jsx(g,{name:"activity"}),e.jsx("span",{style:h,children:s.phase})]}),(s.tokens>0||s.cost>0||s.turn_started_at||n&&n.count>0)&&e.jsxs("div",{className:"row wrap",style:{gap:16,marginTop:12,fontSize:13,color:"var(--text-2)"},children:[s.tokens>0&&e.jsxs("span",{children:[N(s.tokens)," tok"]}),s.cost>0&&e.jsxs("span",{children:["$",s.cost.toFixed(2)]}),s.turn_started_at?e.jsx("span",{children:e.jsx(w,{startedAt:s.turn_started_at})}):null,n&&n.count>0&&e.jsxs("span",{children:[n.count," file",n.count===1?"":"s"," ",e.jsxs("span",{style:{color:"var(--good)"},children:["+",n.additions]})," ",e.jsxs("span",{style:{color:"var(--bad)"},children:["−",n.deletions]})]})]}),i&&e.jsxs("div",{className:"row between wrap gap-12",style:{marginTop:12,paddingTop:10,borderTop:"1px solid var(--line)"},children:[a&&a.total>0?e.jsxs("span",{className:"row gap-8 dim",style:{fontSize:13},children:[e.jsx(k,{done:a.done,total:a.total}),"plan ",a.done,"/",a.total]}):e.jsx("span",{}),s.error?e.jsx("span",{className:"badge bad",title:s.error,style:{maxWidth:300,...h},children:s.error}):s.blocked_on?e.jsx("span",{className:"badge bad",title:s.blocked_on,style:{maxWidth:300,...h},children:s.blocked_on}):null]}),e.jsxs("div",{className:"dim mono",style:{fontSize:11,marginTop:8},children:["updated ",f(s.updated_at)," ago"]})]})}function I(){const[s,l]=p.useState(null),[n,a]=p.useState("");p.useEffect(()=>{let t=!0;const o=()=>x.activity().then(m=>{t&&(l(m),a(""))},m=>{t&&a(m.message)});o();const c=setInterval(o,2e3);return()=>{t=!1,clearInterval(c)}},[]);const i=s?s.enabled!==!1:!0,r=(s==null?void 0:s.activity)||[],d=async()=>{const t=!i;try{const o=await x.activityToggle(t);l(c=>({...c||{},enabled:o.enabled,activity:o.enabled?(c==null?void 0:c.activity)||[]:[]})),a("")}catch(o){a(o.message)}};return e.jsxs("div",{className:"narrow",children:[e.jsxs("div",{className:"head-row mb-16",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"h",children:"Activity"}),e.jsx("p",{className:"sub",children:"Live OpenCode sessions this harness is running: what each one is doing right now. Refreshes every couple of seconds; sessions drop off when their process exits."})]}),e.jsxs("div",{className:"row gap-10",style:{alignItems:"center",flexShrink:0},children:[e.jsx("span",{className:"dim mono",style:{fontSize:12},children:i?"tracking on":"tracking off"}),e.jsx("div",{className:`sw-toggle${i?" on":""}`,role:"switch","aria-checked":i,"aria-label":"Toggle activity tracking",tabIndex:0,onClick:d,onKeyDown:t=>{(t.key==="Enter"||t.key===" ")&&(t.key===" "&&t.preventDefault(),d())}})]})]}),e.jsx(v,{error:n,style:{marginBottom:12}}),s?i?r.length===0?e.jsxs("div",{className:"empty",children:[e.jsx("div",{className:"big",children:"No active sessions"}),"Run ",e.jsx("code",{children:"opencode"})," and the sessions it spins up appear here."]}):e.jsx("div",{className:"stack gap-12",children:r.map(t=>e.jsx(S,{s:t},t.session_id))}):e.jsxs("div",{className:"empty",children:[e.jsx("div",{className:"big",children:"Activity tracking is off"}),"The plugin isn't recording sessions. Flip the switch to turn it back on."]}):e.jsx(y,{label:"Reading live sessions…"})]})}export{I as default};
@@ -1 +1 @@
1
- import{j as s,L as I,I as k,r as _,a as E}from"./index-BgIs5eIa.js";import{r as m}from"./react-DOpiTn5v.js";import{S as b,b as W,E as x,c as w,a as A}from"./activity-D9mfv7pY.js";function C(e){return e.kind==="tool"?e.status==="error"?"bad":e.status==="completed"?"ok":"acc":{retry:"warn",error:"bad",subtask:"acc",text:"acc"}[e.kind]||""}function F({rec:e}){const n=e.who==="you",i=[];n||(e.kind==="tool"&&e.ms!=null&&i.push(`${(e.ms/1e3).toFixed(1)}s`),e.kind==="step"&&(e.tokens&&i.push(`${w(e.tokens)} tok`),e.cost&&i.push(`$${e.cost.toFixed(2)}`)),e.kind==="subtask"&&e.agent&&i.unshift(e.agent));const l=!n&&(e.kind==="text"||e.kind==="thinking"||e.kind==="step"),o=n?e.text:e.label||e.snippet||e.kind;return s.jsxs("div",{className:"row gap-10",style:{alignItems:"baseline"},children:[s.jsx("span",{className:`badge ${n?"":"acc"}`,style:{flexShrink:0,minWidth:52,justifyContent:"center"},children:n?"you":"agent"}),s.jsxs("div",{style:{minWidth:0,flex:1,fontSize:13},children:[!n&&s.jsx("span",{className:`feed-dot ${C(e)||"acc"}`,style:{display:"inline-block",marginRight:8,verticalAlign:"middle"}}),e.kind==="thinking"&&s.jsx("span",{className:"dim",children:"thinking: "}),l?s.jsx("span",{className:"dim",children:o}):n?o:s.jsx("b",{children:o}),e.error&&s.jsxs("span",{className:"dim",children:[": ",e.error]})]}),i.length>0&&s.jsx("span",{className:"dim mono",style:{flexShrink:0,fontSize:12},children:i.join(" · ")})]})}function L({turns:e}){return!e||e.length===0?s.jsx("div",{className:"dim",style:{fontSize:13},children:"No conversation captured yet."}):s.jsx("div",{className:"stack gap-12",children:e.map((n,i)=>{const l=n.role==="user";return s.jsxs("div",{className:"row gap-10",style:{alignItems:"baseline"},children:[s.jsx("span",{className:`badge ${l?"":"acc"}`,style:{flexShrink:0,minWidth:52,justifyContent:"center"},children:l?"you":"agent"}),s.jsx("div",{style:{minWidth:0,fontSize:13,color:l?"inherit":"var(--text-2)"},children:n.text})]},i)})})}function P({sid:e}){const[n,i]=m.useState(null),[l,o]=m.useState(""),[S,f]=m.useState(!1),[y,T]=m.useState(!1);m.useEffect(()=>{let a=!0;const d=()=>E.activityDetail(e).then(h=>{a&&(i(h),o(""),f(!1))},h=>{a&&(h.status===404?f(!0):o(h.message))});d();const z=setInterval(d,2e3);return()=>{a=!1,clearInterval(z)}},[e]);const p=s.jsxs("a",{className:"btn ghost sm",href:"#/activity",style:{marginBottom:12,display:"inline-flex",gap:6},children:[s.jsx(k,{name:"arrow",style:{transform:"rotate(180deg)"}})," Activity"]});if(S)return s.jsxs("div",{className:"narrow",children:[p,s.jsxs("div",{className:"empty",children:[s.jsx("div",{className:"big",children:"Session ended"}),"It's no longer live. ",s.jsx("a",{href:"#/activity",children:"Back to Activity"}),"."]})]});if(!n)return s.jsxs("div",{className:"narrow",children:[p,s.jsx(I,{label:"Loading session…"})]});const t=n.session,v=b[t.status]||b.idle,c=t.files,r=t.todos,j=!!(c&&c.items&&c.items.length>0),g=!!(r&&r.items&&r.items.length>0),N=n.conversation||[],u=n.timeline||[],$=[...N.filter(a=>a.role==="user").map(a=>({who:"you",text:a.text,t:a.t||0})),...u.map(a=>({...a,who:"agent"}))].sort((a,d)=>(a.t||0)-(d.t||0));return s.jsxs("div",{className:"narrow",children:[p,l&&s.jsx("p",{className:"badge bad",style:{marginBottom:12},children:l}),s.jsxs("div",{className:"card pad-md",children:[s.jsxs("div",{className:"row between wrap gap-12",children:[s.jsxs("div",{style:{minWidth:0},children:[s.jsx("h1",{className:"h",style:{margin:0},children:t.title||W(t.cwd)||t.session_id}),t.cwd&&s.jsx("div",{className:"dim mono",style:{fontSize:12,...x},title:t.cwd,children:t.cwd})]}),s.jsxs("div",{className:"row gap-8",style:{flexShrink:0},children:[t.model&&s.jsx("span",{className:"badge mono",children:t.model}),t.agent&&s.jsx("span",{className:"badge",children:t.agent}),s.jsxs("span",{className:`badge ${v.cls}`,children:[s.jsx("span",{className:"dot"}),v.label]})]})]}),t.phase&&s.jsxs("div",{className:"row gap-8",style:{marginTop:10,fontSize:14},children:[s.jsx(k,{name:"activity"}),s.jsx("span",{style:x,children:t.phase})]}),s.jsxs("div",{className:"row wrap",style:{gap:16,marginTop:12,fontSize:13,color:"var(--text-2)"},children:[t.tokens>0&&s.jsxs("span",{children:[w(t.tokens)," tok"]}),t.cost>0&&s.jsxs("span",{children:["$",t.cost.toFixed(2)]}),t.turn_started_at?s.jsx("span",{children:s.jsx(A,{startedAt:t.turn_started_at})}):null,s.jsxs("span",{className:"dim",children:["updated ",_(t.updated_at)," ago"]})]}),(t.error||t.blocked_on)&&s.jsx("div",{style:{marginTop:10},children:s.jsx("span",{className:"badge bad",style:{maxWidth:"100%",...x},children:t.error||t.blocked_on})})]}),(j||g)&&s.jsxs("div",{className:"grid gap-12",style:{marginTop:12,gridTemplateColumns:j&&g?"minmax(0, 1fr) minmax(0, 1fr)":"1fr"},children:[j&&s.jsxs("div",{className:"card pad-md",children:[s.jsx("div",{className:"card-head",children:s.jsxs("h3",{children:["Files touched ",s.jsxs("span",{className:"dim",children:["(",c.count,")"]})]})}),s.jsx("div",{className:"stack",style:{gap:6,marginTop:8},children:c.items.map(a=>s.jsxs("div",{className:"row between gap-12",style:{fontSize:13},children:[s.jsx("span",{className:"mono",style:{...x,minWidth:0,flex:1},title:a.file,children:a.file}),s.jsxs("span",{style:{flexShrink:0},children:[s.jsxs("span",{style:{color:"var(--good)"},children:["+",a.additions]})," ",s.jsxs("span",{style:{color:"var(--bad)"},children:["−",a.deletions]})]})]},a.file))})]}),g&&s.jsxs("div",{className:"card pad-md",children:[s.jsx("div",{className:"card-head",children:s.jsxs("h3",{children:["Plan"," ",s.jsxs("span",{className:"dim",children:["(",r.done,"/",r.total,")"]})]})}),s.jsx("div",{className:"stack",style:{gap:6,marginTop:8},children:r.items.map((a,d)=>s.jsxs("div",{className:"row gap-8",style:{fontSize:13},children:[s.jsx("span",{className:`feed-dot ${a.status==="completed"?"ok":a.status==="in_progress"?"acc":""}`}),s.jsx("span",{style:{textDecoration:a.status==="completed"?"line-through":"none",color:a.status==="completed"?"var(--text-3)":"inherit"},children:a.content})]},d))})]})]}),s.jsxs("div",{className:"card pad-md",style:{marginTop:12},children:[s.jsxs("div",{className:"card-head",style:{justifyContent:"space-between",marginBottom:12},children:[s.jsx("h3",{children:"Timeline"}),u.length>0&&s.jsx("button",{className:"btn ghost sm",onClick:()=>T(a=>!a),children:y?"Show conversation":`Show all ${u.length} steps`})]}),y?s.jsx("div",{className:"stack gap-12",children:$.map((a,d)=>s.jsx(F,{rec:a},d))}):s.jsx(L,{turns:N})]})]})}export{P as default};
1
+ import{j as s,L as I,I as k,r as _,a as E}from"./index-DP035wPa.js";import{r as m}from"./react-DOpiTn5v.js";import{S as b,b as W,E as x,c as w,a as A}from"./activity-Cc0n2jwx.js";function C(e){return e.kind==="tool"?e.status==="error"?"bad":e.status==="completed"?"ok":"acc":{retry:"warn",error:"bad",subtask:"acc",text:"acc"}[e.kind]||""}function F({rec:e}){const n=e.who==="you",i=[];n||(e.kind==="tool"&&e.ms!=null&&i.push(`${(e.ms/1e3).toFixed(1)}s`),e.kind==="step"&&(e.tokens&&i.push(`${w(e.tokens)} tok`),e.cost&&i.push(`$${e.cost.toFixed(2)}`)),e.kind==="subtask"&&e.agent&&i.unshift(e.agent));const l=!n&&(e.kind==="text"||e.kind==="thinking"||e.kind==="step"),o=n?e.text:e.label||e.snippet||e.kind;return s.jsxs("div",{className:"row gap-10",style:{alignItems:"baseline"},children:[s.jsx("span",{className:`badge ${n?"":"acc"}`,style:{flexShrink:0,minWidth:52,justifyContent:"center"},children:n?"you":"agent"}),s.jsxs("div",{style:{minWidth:0,flex:1,fontSize:13},children:[!n&&s.jsx("span",{className:`feed-dot ${C(e)||"acc"}`,style:{display:"inline-block",marginRight:8,verticalAlign:"middle"}}),e.kind==="thinking"&&s.jsx("span",{className:"dim",children:"thinking: "}),l?s.jsx("span",{className:"dim",children:o}):n?o:s.jsx("b",{children:o}),e.error&&s.jsxs("span",{className:"dim",children:[": ",e.error]})]}),i.length>0&&s.jsx("span",{className:"dim mono",style:{flexShrink:0,fontSize:12},children:i.join(" · ")})]})}function L({turns:e}){return!e||e.length===0?s.jsx("div",{className:"dim",style:{fontSize:13},children:"No conversation captured yet."}):s.jsx("div",{className:"stack gap-12",children:e.map((n,i)=>{const l=n.role==="user";return s.jsxs("div",{className:"row gap-10",style:{alignItems:"baseline"},children:[s.jsx("span",{className:`badge ${l?"":"acc"}`,style:{flexShrink:0,minWidth:52,justifyContent:"center"},children:l?"you":"agent"}),s.jsx("div",{style:{minWidth:0,fontSize:13,color:l?"inherit":"var(--text-2)"},children:n.text})]},i)})})}function P({sid:e}){const[n,i]=m.useState(null),[l,o]=m.useState(""),[S,f]=m.useState(!1),[y,T]=m.useState(!1);m.useEffect(()=>{let a=!0;const d=()=>E.activityDetail(e).then(h=>{a&&(i(h),o(""),f(!1))},h=>{a&&(h.status===404?f(!0):o(h.message))});d();const z=setInterval(d,2e3);return()=>{a=!1,clearInterval(z)}},[e]);const p=s.jsxs("a",{className:"btn ghost sm",href:"#/activity",style:{marginBottom:12,display:"inline-flex",gap:6},children:[s.jsx(k,{name:"arrow",style:{transform:"rotate(180deg)"}})," Activity"]});if(S)return s.jsxs("div",{className:"narrow",children:[p,s.jsxs("div",{className:"empty",children:[s.jsx("div",{className:"big",children:"Session ended"}),"It's no longer live. ",s.jsx("a",{href:"#/activity",children:"Back to Activity"}),"."]})]});if(!n)return s.jsxs("div",{className:"narrow",children:[p,s.jsx(I,{label:"Loading session…"})]});const t=n.session,v=b[t.status]||b.idle,c=t.files,r=t.todos,j=!!(c&&c.items&&c.items.length>0),g=!!(r&&r.items&&r.items.length>0),N=n.conversation||[],u=n.timeline||[],$=[...N.filter(a=>a.role==="user").map(a=>({who:"you",text:a.text,t:a.t||0})),...u.map(a=>({...a,who:"agent"}))].sort((a,d)=>(a.t||0)-(d.t||0));return s.jsxs("div",{className:"narrow",children:[p,l&&s.jsx("p",{className:"badge bad",style:{marginBottom:12},children:l}),s.jsxs("div",{className:"card pad-md",children:[s.jsxs("div",{className:"row between wrap gap-12",children:[s.jsxs("div",{style:{minWidth:0},children:[s.jsx("h1",{className:"h",style:{margin:0},children:t.title||W(t.cwd)||t.session_id}),t.cwd&&s.jsx("div",{className:"dim mono",style:{fontSize:12,...x},title:t.cwd,children:t.cwd})]}),s.jsxs("div",{className:"row gap-8",style:{flexShrink:0},children:[t.model&&s.jsx("span",{className:"badge mono",children:t.model}),t.agent&&s.jsx("span",{className:"badge",children:t.agent}),s.jsxs("span",{className:`badge ${v.cls}`,children:[s.jsx("span",{className:"dot"}),v.label]})]})]}),t.phase&&s.jsxs("div",{className:"row gap-8",style:{marginTop:10,fontSize:14},children:[s.jsx(k,{name:"activity"}),s.jsx("span",{style:x,children:t.phase})]}),s.jsxs("div",{className:"row wrap",style:{gap:16,marginTop:12,fontSize:13,color:"var(--text-2)"},children:[t.tokens>0&&s.jsxs("span",{children:[w(t.tokens)," tok"]}),t.cost>0&&s.jsxs("span",{children:["$",t.cost.toFixed(2)]}),t.turn_started_at?s.jsx("span",{children:s.jsx(A,{startedAt:t.turn_started_at})}):null,s.jsxs("span",{className:"dim",children:["updated ",_(t.updated_at)," ago"]})]}),(t.error||t.blocked_on)&&s.jsx("div",{style:{marginTop:10},children:s.jsx("span",{className:"badge bad",style:{maxWidth:"100%",...x},children:t.error||t.blocked_on})})]}),(j||g)&&s.jsxs("div",{className:"grid gap-12",style:{marginTop:12,gridTemplateColumns:j&&g?"minmax(0, 1fr) minmax(0, 1fr)":"1fr"},children:[j&&s.jsxs("div",{className:"card pad-md",children:[s.jsx("div",{className:"card-head",children:s.jsxs("h3",{children:["Files touched ",s.jsxs("span",{className:"dim",children:["(",c.count,")"]})]})}),s.jsx("div",{className:"stack",style:{gap:6,marginTop:8},children:c.items.map(a=>s.jsxs("div",{className:"row between gap-12",style:{fontSize:13},children:[s.jsx("span",{className:"mono",style:{...x,minWidth:0,flex:1},title:a.file,children:a.file}),s.jsxs("span",{style:{flexShrink:0},children:[s.jsxs("span",{style:{color:"var(--good)"},children:["+",a.additions]})," ",s.jsxs("span",{style:{color:"var(--bad)"},children:["−",a.deletions]})]})]},a.file))})]}),g&&s.jsxs("div",{className:"card pad-md",children:[s.jsx("div",{className:"card-head",children:s.jsxs("h3",{children:["Plan"," ",s.jsxs("span",{className:"dim",children:["(",r.done,"/",r.total,")"]})]})}),s.jsx("div",{className:"stack",style:{gap:6,marginTop:8},children:r.items.map((a,d)=>s.jsxs("div",{className:"row gap-8",style:{fontSize:13},children:[s.jsx("span",{className:`feed-dot ${a.status==="completed"?"ok":a.status==="in_progress"?"acc":""}`}),s.jsx("span",{style:{textDecoration:a.status==="completed"?"line-through":"none",color:a.status==="completed"?"var(--text-3)":"inherit"},children:a.content})]},d))})]})]}),s.jsxs("div",{className:"card pad-md",style:{marginTop:12},children:[s.jsxs("div",{className:"card-head",style:{justifyContent:"space-between",marginBottom:12},children:[s.jsx("h3",{children:"Timeline"}),u.length>0&&s.jsx("button",{className:"btn ghost sm",onClick:()=>T(a=>!a),children:y?"Show conversation":`Show all ${u.length} steps`})]}),y?s.jsx("div",{className:"stack gap-12",children:$.map((a,d)=>s.jsx(F,{rec:a},d))}):s.jsx(L,{turns:N})]})]})}export{P as default};
@@ -1,4 +1,4 @@
1
- import{u as R,j as e,E as O,L as T,I as b,a as g}from"./index-BgIs5eIa.js";import{r as c}from"./react-DOpiTn5v.js";const W=700;function D(r){return r.startsWith("@@")||r.startsWith("+++")||r.startsWith("---")?"hunk":r.startsWith("+")?"add":r.startsWith("-")?"del":"ctx"}function A({onMutated:r,dataRev:w}){const{data:i,error:f,reload:y}=R(()=>g.diff(),[w]),[p,h]=c.useState(!1),[x,m]=c.useState(""),[l,u]=c.useState(()=>new Set),[v,j]=c.useState(()=>new Set);c.useEffect(()=>{i&&(u(new Set),j(new Set(i.files.map(s=>s.rel))))},[i]);const S=s=>u(t=>{const a=new Set(t);return a.has(s)?a.delete(s):a.add(s),a}),k=()=>u(s=>s.size===i.files.length?new Set:new Set(i.files.map(t=>t.rel))),E=async()=>{h(!0);try{const{job_id:s}=await g.action("export");let t;do await new Promise(a=>setTimeout(a,W)),t=await g.job(s);while(t.status==="running");m(t.status==="done"?"Improvements file written.":"Export failed. See logs.")}catch(s){m(s.message)}finally{h(!1)}},$=async()=>{const s=[...l],t=s.filter(n=>{var d;return((d=i.files.find(I=>I.rel===n))==null?void 0:d.status)==="added"}),a=t.length?`Restoring will DELETE ${t.length} deployed-only file(s):
1
+ import{u as R,j as e,E as O,L as T,I as b,a as g}from"./index-DP035wPa.js";import{r as c}from"./react-DOpiTn5v.js";const W=700;function D(r){return r.startsWith("@@")||r.startsWith("+++")||r.startsWith("---")?"hunk":r.startsWith("+")?"add":r.startsWith("-")?"del":"ctx"}function A({onMutated:r,dataRev:w}){const{data:i,error:f,reload:y}=R(()=>g.diff(),[w]),[p,h]=c.useState(!1),[x,m]=c.useState(""),[l,u]=c.useState(()=>new Set),[v,j]=c.useState(()=>new Set);c.useEffect(()=>{i&&(u(new Set),j(new Set(i.files.map(s=>s.rel))))},[i]);const S=s=>u(t=>{const a=new Set(t);return a.has(s)?a.delete(s):a.add(s),a}),k=()=>u(s=>s.size===i.files.length?new Set:new Set(i.files.map(t=>t.rel))),E=async()=>{h(!0);try{const{job_id:s}=await g.action("export");let t;do await new Promise(a=>setTimeout(a,W)),t=await g.job(s);while(t.status==="running");m(t.status==="done"?"Improvements file written.":"Export failed. See logs.")}catch(s){m(s.message)}finally{h(!1)}},$=async()=>{const s=[...l],t=s.filter(n=>{var d;return((d=i.files.find(I=>I.rel===n))==null?void 0:d.status)==="added"}),a=t.length?`Restoring will DELETE ${t.length} deployed-only file(s):
2
2
  ${t.join(`
3
3
  `)}
4
4
 
@@ -1 +1 @@
1
- import{u as t,j as s,E as i,I as d,L as o,a as h}from"./index-BgIs5eIa.js";import{r as m}from"./react-DOpiTn5v.js";function p({group:e}){const a=e.problems.length===0,[c,r]=m.useState(!a);return s.jsxs("div",{className:`card check ${a?"":"bad"} ${c?"open":""}`,children:[s.jsxs("div",{className:"check-head",onClick:()=>!a&&r(l=>!l),style:{cursor:a?"default":"pointer"},children:[s.jsx("span",{className:`feed-dot ${a?"ok":"bad"}`,style:{width:9,height:9}}),s.jsx("h3",{children:e.label}),s.jsxs("span",{className:`badge ${a?"ok":"bad"}`,children:[s.jsx("span",{className:"dot"}),a?"clean":`${e.problems.length} problem${e.problems.length===1?"":"s"}`]}),!a&&s.jsx(d,{name:"chevron",className:"chev glyph"})]}),!a&&c&&s.jsx("div",{className:"check-body",children:e.problems.map(l=>s.jsxs("div",{className:"problem",children:[s.jsx("span",{className:"x",children:"✕"}),s.jsx("span",{children:l})]},l))})]})}function b(){const{data:e,error:a,reload:c,setData:r}=t(()=>h.doctor(),[]),l=()=>{r(null),c()};return a?s.jsx(i,{error:a}):s.jsxs("div",{className:"narrow",children:[s.jsxs("div",{className:"head-row mb-16",children:[s.jsxs("div",{children:[s.jsx("h1",{className:"h",children:"Doctor"}),s.jsx("p",{className:"sub",children:"Every check builds each theme in a sandbox and validates the result: token resolution, link integrity, parity, and drift."})]}),s.jsxs("button",{className:"btn ghost",onClick:l,disabled:!e,children:[s.jsx(d,{name:"refresh"}),e?"Re-run checks":"Running…"]})]}),e?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"card pad-md mb-16",children:s.jsxs("div",{className:"row wrap between gap-12",children:[s.jsxs("div",{className:"row",style:{gap:10,flexWrap:"wrap"},children:[s.jsxs("span",{className:"dim mono",style:{fontSize:12},children:["validated ",e.themes.length," theme",e.themes.length===1?"":"s"]}),e.themes.map(n=>s.jsx("span",{className:"badge",style:{textTransform:"capitalize"},children:n},n))]}),e.ok?s.jsxs("span",{className:"badge ok",children:[s.jsx("span",{className:"dot"}),"all clean"]}):s.jsxs("span",{className:"badge bad",children:[s.jsx("span",{className:"dot"}),e.problems.length," problem",e.problems.length===1?"":"s"]})]})}),s.jsx("div",{className:"stack gap-12",children:e.groups.map(n=>s.jsx(p,{group:n},n.label))})]}):s.jsx(o,{label:"Running every check (builds each theme in a sandbox)…"})]})}export{b as default};
1
+ import{u as t,j as s,E as i,I as d,L as o,a as h}from"./index-DP035wPa.js";import{r as m}from"./react-DOpiTn5v.js";function p({group:e}){const a=e.problems.length===0,[c,r]=m.useState(!a);return s.jsxs("div",{className:`card check ${a?"":"bad"} ${c?"open":""}`,children:[s.jsxs("div",{className:"check-head",onClick:()=>!a&&r(l=>!l),style:{cursor:a?"default":"pointer"},children:[s.jsx("span",{className:`feed-dot ${a?"ok":"bad"}`,style:{width:9,height:9}}),s.jsx("h3",{children:e.label}),s.jsxs("span",{className:`badge ${a?"ok":"bad"}`,children:[s.jsx("span",{className:"dot"}),a?"clean":`${e.problems.length} problem${e.problems.length===1?"":"s"}`]}),!a&&s.jsx(d,{name:"chevron",className:"chev glyph"})]}),!a&&c&&s.jsx("div",{className:"check-body",children:e.problems.map(l=>s.jsxs("div",{className:"problem",children:[s.jsx("span",{className:"x",children:"✕"}),s.jsx("span",{children:l})]},l))})]})}function b(){const{data:e,error:a,reload:c,setData:r}=t(()=>h.doctor(),[]),l=()=>{r(null),c()};return a?s.jsx(i,{error:a}):s.jsxs("div",{className:"narrow",children:[s.jsxs("div",{className:"head-row mb-16",children:[s.jsxs("div",{children:[s.jsx("h1",{className:"h",children:"Doctor"}),s.jsx("p",{className:"sub",children:"Every check builds each theme in a sandbox and validates the result: token resolution, link integrity, parity, and drift."})]}),s.jsxs("button",{className:"btn ghost",onClick:l,disabled:!e,children:[s.jsx(d,{name:"refresh"}),e?"Re-run checks":"Running…"]})]}),e?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"card pad-md mb-16",children:s.jsxs("div",{className:"row wrap between gap-12",children:[s.jsxs("div",{className:"row",style:{gap:10,flexWrap:"wrap"},children:[s.jsxs("span",{className:"dim mono",style:{fontSize:12},children:["validated ",e.themes.length," theme",e.themes.length===1?"":"s"]}),e.themes.map(n=>s.jsx("span",{className:"badge",style:{textTransform:"capitalize"},children:n},n))]}),e.ok?s.jsxs("span",{className:"badge ok",children:[s.jsx("span",{className:"dot"}),"all clean"]}):s.jsxs("span",{className:"badge bad",children:[s.jsx("span",{className:"dot"}),e.problems.length," problem",e.problems.length===1?"":"s"]})]})}),s.jsx("div",{className:"stack gap-12",children:e.groups.map(n=>s.jsx(p,{group:n},n.label))})]}):s.jsx(o,{label:"Running every check (builds each theme in a sandbox)…"})]})}export{b as default};
@@ -1 +1 @@
1
- import{u as z,j as g,E as R,L as T,a as M,g as B}from"./index-BgIs5eIa.js";import{r as L,R as V}from"./react-DOpiTn5v.js";import{r as W}from"./roman-DkYN0LX9.js";const u=V.createElement,k={agent:"var(--accent)",skill:"var(--good)",law:"var(--warn)"},N=n=>n.type==="law"?`Rule ${n.id}`:n.id,F=(n,i)=>i(c=>c===n?null:c);function D(n){var x,r;const i=new Map(n.nodes.map(e=>[e.id,e])),c=new Map(n.nodes.map(e=>[e.id,0])),b=new Map(n.nodes.map(e=>[e.id,0])),f=new Map(n.nodes.map(e=>[e.id,0])),$=new Map(n.nodes.map(e=>[e.id,new Set]));for(const e of n.edges)c.set(e.source,(c.get(e.source)||0)+1),c.set(e.target,(c.get(e.target)||0)+1),b.set(e.source,(b.get(e.source)||0)+1),f.set(e.target,(f.get(e.target)||0)+1),(x=$.get(e.source))==null||x.add(e.target),(r=$.get(e.target))==null||r.add(e.source);const d=e=>n.nodes.filter(o=>o.type===e),m=d("agent").sort((e,o)=>e.id.localeCompare(o.id)),h=d("skill").sort((e,o)=>e.id.localeCompare(o.id)),S=d("law").sort((e,o)=>W(e.id)-W(o.id));return{nodes:n.nodes,edges:n.edges,byId:i,deg:c,outDeg:b,inDeg:f,nbr:$,agents:m,skills:h,laws:S}}function q({M:n,c:i,hover:c,types:b,setHover:f,openNode:$}){const d=[...n.agents,...n.skills,...n.laws],m=d.filter(t=>n.outDeg.get(t.id)>0&&b[t.type]),h=d.filter(t=>n.inDeg.get(t.id)>0&&b[t.type]),S=new Map(m.map((t,p)=>[t.id,p])),x=new Map(h.map((t,p)=>[t.id,p])),r=11,e=160,o=170,s=e+h.length*r+24,y=o+m.length*r+30,l=[],w=t=>{const p=[];let E=0;for(const[j,C]of[["agent","AGENTS"],["skill","SKILLS"],["law","LAWS"]]){const A=t.filter(I=>I.type===j).length;A>0&&(p.push({label:C,color:k[j],start:E,count:A}),E+=A)}return p},v=w(m),a=w(h);c&&(S.has(c)&&l.push(u("rect",{key:"rb",x:e,y:o+S.get(c)*r,width:h.length*r,height:r,fill:"rgba(255,255,255,.055)"})),x.has(c)&&l.push(u("rect",{key:"cb",x:e+x.get(c)*r,y:o,width:r,height:m.length*r,fill:"rgba(255,255,255,.055)"}))),v.slice(1).forEach((t,p)=>l.push(u("line",{key:"hs"+p,x1:e,y1:o+t.start*r,x2:e+h.length*r,y2:o+t.start*r,style:{stroke:"var(--line)"},strokeWidth:1}))),a.slice(1).forEach((t,p)=>l.push(u("line",{key:"vs"+p,x1:e+t.start*r,y1:o,x2:e+t.start*r,y2:o+m.length*r,style:{stroke:"var(--line)"},strokeWidth:1})));for(const t of n.edges){if(!S.has(t.source)||!x.has(t.target)||!i.shown(t.source)||!i.shown(t.target))continue;const p=S.get(t.source),E=x.get(t.target),j=i.hasFocus?c?t.source===c||t.target===c:i.matches(t.source)||i.matches(t.target):!0;l.push(u("rect",{key:t.source+">"+t.target,x:e+E*r+.5,y:o+p*r+.5,width:r-1,height:r-1,rx:1.5,style:{fill:k[n.byId.get(t.source).type]},opacity:i.hasFocus?j?.95:.07:.82},u("title",null,t.source+" → "+N(n.byId.get(t.target)))))}return m.forEach((t,p)=>{const E=i.hasFocus&&!i.nodeFocus(t.id);l.push(u("text",{key:"r"+t.id,x:e-8,y:o+p*r+r-3,textAnchor:"end",fontSize:8.6,style:{fill:E?"var(--text-3)":k[t.type],cursor:"pointer"},opacity:E?.32:1,onMouseEnter:()=>f(t.id),onMouseLeave:()=>F(t.id,f),onClick:()=>$(t)},N(t)))}),h.forEach((t,p)=>{const E=i.hasFocus&&!i.nodeFocus(t.id),j=e+p*r+r-3;l.push(u("text",{key:"c"+t.id,x:j,y:o-8,fontSize:8.6,transform:`rotate(-90 ${j} ${o-8})`,style:{fill:E?"var(--text-3)":k[t.type],cursor:"pointer"},opacity:E?.32:1,onMouseEnter:()=>f(t.id),onMouseLeave:()=>F(t.id,f),onClick:()=>$(t)},N(t)))}),v.forEach((t,p)=>{const E=o+(t.start+t.count/2)*r;l.push(u("text",{key:"gr"+p,x:16,y:E,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:t.color},opacity:.85,transform:`rotate(-90 16 ${E})`,textAnchor:"middle"},t.label))}),a.forEach((t,p)=>l.push(u("text",{key:"gc"+p,x:e+(t.start+t.count/2)*r,y:18,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:t.color},opacity:.85,textAnchor:"middle"},t.label))),l.push(u("text",{key:"capR",x:e-8,y:o-150,fontSize:8.5,style:{fill:"var(--text-3)"},textAnchor:"end"},"rows cite ↓")),l.push(u("text",{key:"capC",x:e+6,y:o-8,fontSize:8.5,style:{fill:"var(--text-3)"}},"→ are cited")),u("svg",{width:s,height:y,viewBox:`0 0 ${s} ${y}`,preserveAspectRatio:"xMinYMin meet",className:"gx-svg gx-fluid",onMouseLeave:()=>f(null)},l)}function K({M:n,c:i,setHover:c,openNode:b}){const f=[];[n.agents,n.skills,n.laws].forEach(s=>s.slice().sort((y,l)=>n.deg.get(l.id)-n.deg.get(y.id)).forEach(y=>f.push(y)));const $=new Map(f.map((s,y)=>[s.id,y])),d=14,m=34,h=330,S=290,x=m+f.length*d+34,r=h+170,e=[];e.push(u("line",{key:"axis",x1:m-8,y1:h,x2:m+f.length*d,y2:h,style:{stroke:"var(--line)"},strokeWidth:1})),n.edges.forEach((s,y)=>{if(!i.shown(s.source)||!i.shown(s.target))return;const l=m+$.get(s.source)*d,w=m+$.get(s.target)*d,v=Math.min(S,26+Math.abs(w-l)*.42),a=(l+w)/2,t=i.edgeFocus(s);e.push(u("path",{key:"a"+y,d:`M${l},${h} Q${a},${h-v} ${w},${h}`,fill:"none",style:{stroke:k[n.byId.get(s.source).type]},strokeWidth:t&&i.hasFocus?1.4:1,opacity:i.hasFocus?t?.85:.035:.16}))}),f.forEach((s,y)=>{const l=m+y*d,w=!i.shown(s.id),v=i.hasFocus&&!i.nodeFocus(s.id),a=w?.1:v?.28:1,t=N(s);e.push(u("circle",{key:"d"+s.id,cx:l,cy:h,r:i.hasFocus&&i.nodeFocus(s.id)?4:3,style:{fill:k[s.type],cursor:"pointer"},opacity:a,onMouseEnter:()=>c(s.id),onMouseLeave:()=>F(s.id,c),onClick:()=>b(s)},u("title",null,t+" · "+n.deg.get(s.id)+" links"))),e.push(u("text",{key:"t"+s.id,x:l,y:h+12,fontSize:8.4,transform:`rotate(90 ${l} ${h+12})`,style:{fill:v?"var(--text-3)":k[s.type],cursor:"pointer"},opacity:a,onMouseEnter:()=>c(s.id),onMouseLeave:()=>F(s.id,c),onClick:()=>b(s)},t))});let o=0;return[["AGENTS",n.agents,k.agent],["SKILLS",n.skills,k.skill],["LAWS",n.laws,k.law]].forEach((s,y)=>{if(s[1].length===0)return;const l=m+o*d-4,w=m+(o+s[1].length-1)*d+4;e.push(u("line",{key:"gb"+y,x1:l,y1:14,x2:w,y2:14,style:{stroke:s[2]},strokeWidth:2,opacity:.6})),e.push(u("text",{key:"gt"+y,x:(l+w)/2,y:9,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:s[2]},textAnchor:"middle"},s[0])),o+=s[1].length}),u("svg",{width:x,height:r,viewBox:`0 0 ${x} ${r}`,className:"gx-svg",onMouseLeave:()=>c(null)},e)}function Q({M:n,c:i,setHover:c,openNode:b}){const f=[{list:n.agents,x:210,side:"left",title:"AGENTS",color:k.agent},{list:n.laws,x:560,side:"mid",title:"LAWS",color:k.law},{list:n.skills,x:910,side:"right",title:"SKILLS",color:k.skill}],$=22,d=78,m=new Map;f.forEach(e=>{e.list.slice().sort((o,s)=>n.deg.get(s.id)-n.deg.get(o.id)).forEach((o,s)=>m.set(o.id,{x:e.x,y:d+s*$}))});const h=Math.max(0,...f.map(e=>e.list.length)),S=1120,x=d+h*$+30,r=[];return n.edges.forEach((e,o)=>{const s=m.get(e.source),y=m.get(e.target);if(!s||!y||!i.shown(e.source)||!i.shown(e.target))return;const l=i.edgeFocus(e),w=(s.x+y.x)/2;r.push(u("path",{key:"e"+o,d:`M${s.x},${s.y} C${w},${s.y} ${w},${y.y} ${y.x},${y.y}`,fill:"none",style:{stroke:k[n.byId.get(e.source).type]},strokeWidth:l&&i.hasFocus?1.3:.9,opacity:i.hasFocus?l?.8:.025:.16}))}),f.forEach((e,o)=>{const s=e.side==="left"?"end":e.side==="right"?"start":"middle",y=e.side==="left"?e.x+6:e.side==="right"?e.x-6:e.x;r.push(u("text",{key:"h"+o,x:y,y:46,fontSize:10.5,fontWeight:600,letterSpacing:".14em",style:{fill:e.color},textAnchor:s},e.title+" · "+e.list.length))}),f.forEach(e=>{e.list.forEach(o=>{const s=m.get(o.id),y=!i.shown(o.id),l=i.hasFocus&&!i.nodeFocus(o.id),w=y?.1:l?.25:1,v=e.side==="left",a=N(o),t=i.hasFocus&&i.nodeFocus(o.id),p=e.side==="mid"?"start":v?"end":"start";r.push(u("g",{key:o.id,transform:`translate(${s.x},${s.y})`,style:{cursor:"pointer"},onMouseEnter:()=>c(o.id),onMouseLeave:()=>F(o.id,c),onClick:()=>b(o)},u("circle",{r:t?4.4:3.4,style:{fill:k[o.type]},opacity:w}),u("text",{x:e.side==="mid"?9:v?-9:9,y:3,textAnchor:p,fontSize:10.5,style:{fill:l?"var(--text-3)":t?"var(--text)":"var(--text-2)",fontWeight:t?600:400},opacity:w},a),u("title",null,a+" · "+n.deg.get(o.id)+" links")))})}),u("svg",{width:S,height:x,viewBox:`0 0 ${S} ${x}`,className:"gx-svg",onMouseLeave:()=>c(null)},r)}const Y={matrix:q,arc:K,network:Q},H=[["matrix","Matrix"],["arc","Arc"],["network","Network"]],O=[["agent","Agents"],["skill","Skills"],["law","Laws"]],P={matrix:"Adjacency matrix: one dot per link, zero crossings. Rows are the things that cite (agents + skills); columns are the things that get cited (agents, skills, laws). Laws never cite, so they appear only as columns; that wide gold band is every agent and skill grounding itself in the universal laws. Hover a label to light its row + column.",arc:"Arc diagram: every node on one axis, ordered by type then by how connected it is. Long arcs reach across the harness; the fan of arcs landing on the laws shows them as the shared backbone. Hover a node to isolate its arcs.",network:"Network: the familiar layered layout, but links sit near-invisible until you focus. Hover or search a node and only its connections light up, so the structure reads instead of a hairball. Columns are sorted with the most-connected hubs on top."};function Z(){const{data:n,error:i}=z(()=>M.graph(),[]),[c,b]=L.useState("matrix"),[f,$]=L.useState(""),[d,m]=L.useState(null),[h,S]=L.useState({agent:!0,skill:!0,law:!0}),x=L.useMemo(()=>n?D(n):null,[n]);if(i)return g.jsx(R,{error:i});if(!n||!x)return g.jsx(T,{});const r=a=>B(`#/item/${a.type}/${encodeURIComponent(a.id)}`),e=f.trim().toLowerCase(),o=a=>!!e&&a.toLowerCase().includes(e),s={hasFocus:!!d||!!e,matches:o,nodeFocus:a=>{var t;return d?a===d||!!((t=x.nbr.get(d))!=null&&t.has(a)):e?o(a):!0},edgeFocus:a=>d?a.source===d||a.target===d:e?o(a.source)||o(a.target):!0,shown:a=>h[x.byId.get(a).type]},y=Y[c]({M:x,c:s,hover:d,types:h,setHover:m,openNode:r}),l=x.nodes.filter(a=>h[a.type]).length,w=x.edges.filter(a=>h[x.byId.get(a.source).type]&&h[x.byId.get(a.target).type]).length;let v;if(d){const a=x.byId.get(d);v=`${N(a)} · ${a.type} · ${x.deg.get(d)} links`}else e?v=`filtering “${f.trim()}”`:v=`${l} nodes · ${w} links shown`;return g.jsxs(g.Fragment,{children:[g.jsxs("div",{className:"head-row mb-16",children:[g.jsxs("div",{children:[g.jsx("div",{className:"eyebrow",children:"constellation"}),g.jsx("h1",{className:"h",children:"Graph"}),g.jsxs("p",{className:"sub",children:["Every agent, skill and law in the harness with the cross-links parsed from source. Three ways to read the same ",x.edges.length," connections. Pick the lens that fits the question. Hover anything to trace what it touches."]})]}),g.jsxs("div",{className:"gx-stats mono",children:[g.jsxs("div",{children:[g.jsx("span",{children:x.nodes.length})," nodes"]}),g.jsxs("div",{children:[g.jsx("span",{children:x.edges.length})," links"]})]})]}),g.jsxs("div",{className:"card pad-md",children:[g.jsxs("div",{className:"gx-toolbar",children:[g.jsx("div",{className:"seg",children:H.map(([a,t])=>g.jsx("button",{className:c===a?"on":"",onClick:()=>b(a),children:t},a))}),g.jsxs("span",{className:"gx-search",children:[g.jsx("span",{className:"gx-search-ic",children:"⌕"}),g.jsx("input",{type:"text",className:"graph-search",placeholder:"search nodes…",value:f,onChange:a=>$(a.target.value)})]}),g.jsx("div",{className:"gx-filters",children:O.map(([a,t])=>g.jsxs("button",{className:`gx-filter ${h[a]?"":"off"}`,onClick:()=>S(p=>({...p,[a]:!p[a]})),"aria-pressed":h[a],children:[g.jsx("span",{className:"dot",style:{background:k[a]}}),t]},a))}),g.jsx("div",{className:"gx-readout",children:v})]}),g.jsx("div",{className:"gx-canvas",children:g.jsx("div",{className:`gx-inner${c==="matrix"?" fluid":""}`,children:y})}),g.jsx("p",{className:"gx-hint",children:P[c]})]})]})}export{Z as default};
1
+ import{u as z,j as g,E as R,L as T,a as M,g as B}from"./index-DP035wPa.js";import{r as L,R as V}from"./react-DOpiTn5v.js";import{r as W}from"./roman-DkYN0LX9.js";const u=V.createElement,k={agent:"var(--accent)",skill:"var(--good)",law:"var(--warn)"},N=n=>n.type==="law"?`Rule ${n.id}`:n.id,F=(n,i)=>i(c=>c===n?null:c);function D(n){var x,r;const i=new Map(n.nodes.map(e=>[e.id,e])),c=new Map(n.nodes.map(e=>[e.id,0])),b=new Map(n.nodes.map(e=>[e.id,0])),f=new Map(n.nodes.map(e=>[e.id,0])),$=new Map(n.nodes.map(e=>[e.id,new Set]));for(const e of n.edges)c.set(e.source,(c.get(e.source)||0)+1),c.set(e.target,(c.get(e.target)||0)+1),b.set(e.source,(b.get(e.source)||0)+1),f.set(e.target,(f.get(e.target)||0)+1),(x=$.get(e.source))==null||x.add(e.target),(r=$.get(e.target))==null||r.add(e.source);const d=e=>n.nodes.filter(o=>o.type===e),m=d("agent").sort((e,o)=>e.id.localeCompare(o.id)),h=d("skill").sort((e,o)=>e.id.localeCompare(o.id)),S=d("law").sort((e,o)=>W(e.id)-W(o.id));return{nodes:n.nodes,edges:n.edges,byId:i,deg:c,outDeg:b,inDeg:f,nbr:$,agents:m,skills:h,laws:S}}function q({M:n,c:i,hover:c,types:b,setHover:f,openNode:$}){const d=[...n.agents,...n.skills,...n.laws],m=d.filter(t=>n.outDeg.get(t.id)>0&&b[t.type]),h=d.filter(t=>n.inDeg.get(t.id)>0&&b[t.type]),S=new Map(m.map((t,p)=>[t.id,p])),x=new Map(h.map((t,p)=>[t.id,p])),r=11,e=160,o=170,s=e+h.length*r+24,y=o+m.length*r+30,l=[],w=t=>{const p=[];let E=0;for(const[j,C]of[["agent","AGENTS"],["skill","SKILLS"],["law","LAWS"]]){const A=t.filter(I=>I.type===j).length;A>0&&(p.push({label:C,color:k[j],start:E,count:A}),E+=A)}return p},v=w(m),a=w(h);c&&(S.has(c)&&l.push(u("rect",{key:"rb",x:e,y:o+S.get(c)*r,width:h.length*r,height:r,fill:"rgba(255,255,255,.055)"})),x.has(c)&&l.push(u("rect",{key:"cb",x:e+x.get(c)*r,y:o,width:r,height:m.length*r,fill:"rgba(255,255,255,.055)"}))),v.slice(1).forEach((t,p)=>l.push(u("line",{key:"hs"+p,x1:e,y1:o+t.start*r,x2:e+h.length*r,y2:o+t.start*r,style:{stroke:"var(--line)"},strokeWidth:1}))),a.slice(1).forEach((t,p)=>l.push(u("line",{key:"vs"+p,x1:e+t.start*r,y1:o,x2:e+t.start*r,y2:o+m.length*r,style:{stroke:"var(--line)"},strokeWidth:1})));for(const t of n.edges){if(!S.has(t.source)||!x.has(t.target)||!i.shown(t.source)||!i.shown(t.target))continue;const p=S.get(t.source),E=x.get(t.target),j=i.hasFocus?c?t.source===c||t.target===c:i.matches(t.source)||i.matches(t.target):!0;l.push(u("rect",{key:t.source+">"+t.target,x:e+E*r+.5,y:o+p*r+.5,width:r-1,height:r-1,rx:1.5,style:{fill:k[n.byId.get(t.source).type]},opacity:i.hasFocus?j?.95:.07:.82},u("title",null,t.source+" → "+N(n.byId.get(t.target)))))}return m.forEach((t,p)=>{const E=i.hasFocus&&!i.nodeFocus(t.id);l.push(u("text",{key:"r"+t.id,x:e-8,y:o+p*r+r-3,textAnchor:"end",fontSize:8.6,style:{fill:E?"var(--text-3)":k[t.type],cursor:"pointer"},opacity:E?.32:1,onMouseEnter:()=>f(t.id),onMouseLeave:()=>F(t.id,f),onClick:()=>$(t)},N(t)))}),h.forEach((t,p)=>{const E=i.hasFocus&&!i.nodeFocus(t.id),j=e+p*r+r-3;l.push(u("text",{key:"c"+t.id,x:j,y:o-8,fontSize:8.6,transform:`rotate(-90 ${j} ${o-8})`,style:{fill:E?"var(--text-3)":k[t.type],cursor:"pointer"},opacity:E?.32:1,onMouseEnter:()=>f(t.id),onMouseLeave:()=>F(t.id,f),onClick:()=>$(t)},N(t)))}),v.forEach((t,p)=>{const E=o+(t.start+t.count/2)*r;l.push(u("text",{key:"gr"+p,x:16,y:E,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:t.color},opacity:.85,transform:`rotate(-90 16 ${E})`,textAnchor:"middle"},t.label))}),a.forEach((t,p)=>l.push(u("text",{key:"gc"+p,x:e+(t.start+t.count/2)*r,y:18,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:t.color},opacity:.85,textAnchor:"middle"},t.label))),l.push(u("text",{key:"capR",x:e-8,y:o-150,fontSize:8.5,style:{fill:"var(--text-3)"},textAnchor:"end"},"rows cite ↓")),l.push(u("text",{key:"capC",x:e+6,y:o-8,fontSize:8.5,style:{fill:"var(--text-3)"}},"→ are cited")),u("svg",{width:s,height:y,viewBox:`0 0 ${s} ${y}`,preserveAspectRatio:"xMinYMin meet",className:"gx-svg gx-fluid",onMouseLeave:()=>f(null)},l)}function K({M:n,c:i,setHover:c,openNode:b}){const f=[];[n.agents,n.skills,n.laws].forEach(s=>s.slice().sort((y,l)=>n.deg.get(l.id)-n.deg.get(y.id)).forEach(y=>f.push(y)));const $=new Map(f.map((s,y)=>[s.id,y])),d=14,m=34,h=330,S=290,x=m+f.length*d+34,r=h+170,e=[];e.push(u("line",{key:"axis",x1:m-8,y1:h,x2:m+f.length*d,y2:h,style:{stroke:"var(--line)"},strokeWidth:1})),n.edges.forEach((s,y)=>{if(!i.shown(s.source)||!i.shown(s.target))return;const l=m+$.get(s.source)*d,w=m+$.get(s.target)*d,v=Math.min(S,26+Math.abs(w-l)*.42),a=(l+w)/2,t=i.edgeFocus(s);e.push(u("path",{key:"a"+y,d:`M${l},${h} Q${a},${h-v} ${w},${h}`,fill:"none",style:{stroke:k[n.byId.get(s.source).type]},strokeWidth:t&&i.hasFocus?1.4:1,opacity:i.hasFocus?t?.85:.035:.16}))}),f.forEach((s,y)=>{const l=m+y*d,w=!i.shown(s.id),v=i.hasFocus&&!i.nodeFocus(s.id),a=w?.1:v?.28:1,t=N(s);e.push(u("circle",{key:"d"+s.id,cx:l,cy:h,r:i.hasFocus&&i.nodeFocus(s.id)?4:3,style:{fill:k[s.type],cursor:"pointer"},opacity:a,onMouseEnter:()=>c(s.id),onMouseLeave:()=>F(s.id,c),onClick:()=>b(s)},u("title",null,t+" · "+n.deg.get(s.id)+" links"))),e.push(u("text",{key:"t"+s.id,x:l,y:h+12,fontSize:8.4,transform:`rotate(90 ${l} ${h+12})`,style:{fill:v?"var(--text-3)":k[s.type],cursor:"pointer"},opacity:a,onMouseEnter:()=>c(s.id),onMouseLeave:()=>F(s.id,c),onClick:()=>b(s)},t))});let o=0;return[["AGENTS",n.agents,k.agent],["SKILLS",n.skills,k.skill],["LAWS",n.laws,k.law]].forEach((s,y)=>{if(s[1].length===0)return;const l=m+o*d-4,w=m+(o+s[1].length-1)*d+4;e.push(u("line",{key:"gb"+y,x1:l,y1:14,x2:w,y2:14,style:{stroke:s[2]},strokeWidth:2,opacity:.6})),e.push(u("text",{key:"gt"+y,x:(l+w)/2,y:9,fontSize:9.5,fontWeight:600,letterSpacing:".12em",style:{fill:s[2]},textAnchor:"middle"},s[0])),o+=s[1].length}),u("svg",{width:x,height:r,viewBox:`0 0 ${x} ${r}`,className:"gx-svg",onMouseLeave:()=>c(null)},e)}function Q({M:n,c:i,setHover:c,openNode:b}){const f=[{list:n.agents,x:210,side:"left",title:"AGENTS",color:k.agent},{list:n.laws,x:560,side:"mid",title:"LAWS",color:k.law},{list:n.skills,x:910,side:"right",title:"SKILLS",color:k.skill}],$=22,d=78,m=new Map;f.forEach(e=>{e.list.slice().sort((o,s)=>n.deg.get(s.id)-n.deg.get(o.id)).forEach((o,s)=>m.set(o.id,{x:e.x,y:d+s*$}))});const h=Math.max(0,...f.map(e=>e.list.length)),S=1120,x=d+h*$+30,r=[];return n.edges.forEach((e,o)=>{const s=m.get(e.source),y=m.get(e.target);if(!s||!y||!i.shown(e.source)||!i.shown(e.target))return;const l=i.edgeFocus(e),w=(s.x+y.x)/2;r.push(u("path",{key:"e"+o,d:`M${s.x},${s.y} C${w},${s.y} ${w},${y.y} ${y.x},${y.y}`,fill:"none",style:{stroke:k[n.byId.get(e.source).type]},strokeWidth:l&&i.hasFocus?1.3:.9,opacity:i.hasFocus?l?.8:.025:.16}))}),f.forEach((e,o)=>{const s=e.side==="left"?"end":e.side==="right"?"start":"middle",y=e.side==="left"?e.x+6:e.side==="right"?e.x-6:e.x;r.push(u("text",{key:"h"+o,x:y,y:46,fontSize:10.5,fontWeight:600,letterSpacing:".14em",style:{fill:e.color},textAnchor:s},e.title+" · "+e.list.length))}),f.forEach(e=>{e.list.forEach(o=>{const s=m.get(o.id),y=!i.shown(o.id),l=i.hasFocus&&!i.nodeFocus(o.id),w=y?.1:l?.25:1,v=e.side==="left",a=N(o),t=i.hasFocus&&i.nodeFocus(o.id),p=e.side==="mid"?"start":v?"end":"start";r.push(u("g",{key:o.id,transform:`translate(${s.x},${s.y})`,style:{cursor:"pointer"},onMouseEnter:()=>c(o.id),onMouseLeave:()=>F(o.id,c),onClick:()=>b(o)},u("circle",{r:t?4.4:3.4,style:{fill:k[o.type]},opacity:w}),u("text",{x:e.side==="mid"?9:v?-9:9,y:3,textAnchor:p,fontSize:10.5,style:{fill:l?"var(--text-3)":t?"var(--text)":"var(--text-2)",fontWeight:t?600:400},opacity:w},a),u("title",null,a+" · "+n.deg.get(o.id)+" links")))})}),u("svg",{width:S,height:x,viewBox:`0 0 ${S} ${x}`,className:"gx-svg",onMouseLeave:()=>c(null)},r)}const Y={matrix:q,arc:K,network:Q},H=[["matrix","Matrix"],["arc","Arc"],["network","Network"]],O=[["agent","Agents"],["skill","Skills"],["law","Laws"]],P={matrix:"Adjacency matrix: one dot per link, zero crossings. Rows are the things that cite (agents + skills); columns are the things that get cited (agents, skills, laws). Laws never cite, so they appear only as columns; that wide gold band is every agent and skill grounding itself in the universal laws. Hover a label to light its row + column.",arc:"Arc diagram: every node on one axis, ordered by type then by how connected it is. Long arcs reach across the harness; the fan of arcs landing on the laws shows them as the shared backbone. Hover a node to isolate its arcs.",network:"Network: the familiar layered layout, but links sit near-invisible until you focus. Hover or search a node and only its connections light up, so the structure reads instead of a hairball. Columns are sorted with the most-connected hubs on top."};function Z(){const{data:n,error:i}=z(()=>M.graph(),[]),[c,b]=L.useState("matrix"),[f,$]=L.useState(""),[d,m]=L.useState(null),[h,S]=L.useState({agent:!0,skill:!0,law:!0}),x=L.useMemo(()=>n?D(n):null,[n]);if(i)return g.jsx(R,{error:i});if(!n||!x)return g.jsx(T,{});const r=a=>B(`#/item/${a.type}/${encodeURIComponent(a.id)}`),e=f.trim().toLowerCase(),o=a=>!!e&&a.toLowerCase().includes(e),s={hasFocus:!!d||!!e,matches:o,nodeFocus:a=>{var t;return d?a===d||!!((t=x.nbr.get(d))!=null&&t.has(a)):e?o(a):!0},edgeFocus:a=>d?a.source===d||a.target===d:e?o(a.source)||o(a.target):!0,shown:a=>h[x.byId.get(a).type]},y=Y[c]({M:x,c:s,hover:d,types:h,setHover:m,openNode:r}),l=x.nodes.filter(a=>h[a.type]).length,w=x.edges.filter(a=>h[x.byId.get(a.source).type]&&h[x.byId.get(a.target).type]).length;let v;if(d){const a=x.byId.get(d);v=`${N(a)} · ${a.type} · ${x.deg.get(d)} links`}else e?v=`filtering “${f.trim()}”`:v=`${l} nodes · ${w} links shown`;return g.jsxs(g.Fragment,{children:[g.jsxs("div",{className:"head-row mb-16",children:[g.jsxs("div",{children:[g.jsx("div",{className:"eyebrow",children:"constellation"}),g.jsx("h1",{className:"h",children:"Graph"}),g.jsxs("p",{className:"sub",children:["Every agent, skill and law in the harness with the cross-links parsed from source. Three ways to read the same ",x.edges.length," connections. Pick the lens that fits the question. Hover anything to trace what it touches."]})]}),g.jsxs("div",{className:"gx-stats mono",children:[g.jsxs("div",{children:[g.jsx("span",{children:x.nodes.length})," nodes"]}),g.jsxs("div",{children:[g.jsx("span",{children:x.edges.length})," links"]})]})]}),g.jsxs("div",{className:"card pad-md",children:[g.jsxs("div",{className:"gx-toolbar",children:[g.jsx("div",{className:"seg",children:H.map(([a,t])=>g.jsx("button",{className:c===a?"on":"",onClick:()=>b(a),children:t},a))}),g.jsxs("span",{className:"gx-search",children:[g.jsx("span",{className:"gx-search-ic",children:"⌕"}),g.jsx("input",{type:"text",className:"graph-search",placeholder:"search nodes…",value:f,onChange:a=>$(a.target.value)})]}),g.jsx("div",{className:"gx-filters",children:O.map(([a,t])=>g.jsxs("button",{className:`gx-filter ${h[a]?"":"off"}`,onClick:()=>S(p=>({...p,[a]:!p[a]})),"aria-pressed":h[a],children:[g.jsx("span",{className:"dot",style:{background:k[a]}}),t]},a))}),g.jsx("div",{className:"gx-readout",children:v})]}),g.jsx("div",{className:"gx-canvas",children:g.jsx("div",{className:`gx-inner${c==="matrix"?" fluid":""}`,children:y})}),g.jsx("p",{className:"gx-hint",children:P[c]})]})]})}export{Z as default};
@@ -0,0 +1 @@
1
+ import{u as U,j as e,E as De,L as Fe,I as E,a as v}from"./index-DP035wPa.js";import{r as x,R as Ge}from"./react-DOpiTn5v.js";function O({label:a,value:n,themes:r,onChange:h}){return r.length?e.jsx("select",{className:"sel","aria-label":a,value:n,onChange:o=>h(o.target.value),children:r.map(o=>e.jsx("option",{value:o.name,children:o.name},o.name))}):null}function V({label:a,value:n,onChange:r}){return e.jsxs("select",{className:"sel","aria-label":a,value:n,onChange:h=>r(h.target.value),children:[e.jsx("option",{value:"full",children:"full"}),e.jsx("option",{value:"lean",children:"lean"})]})}function K({label:a,value:n,postures:r,onChange:h}){return r.length?e.jsx("select",{className:"sel","aria-label":a,value:n,onChange:o=>h(o.target.value),children:r.map(o=>e.jsx("option",{value:o,children:o},o))}):null}function H({label:a,value:n,modes:r,onChange:h}){return r.length?e.jsx("select",{className:"sel","aria-label":a,value:n,onChange:o=>h(o.target.value),children:r.map(o=>e.jsx("option",{value:o,children:o},o))}):null}function ae({on:a,disabled:n,label:r,onToggle:h}){const o=m=>{(m.key==="Enter"||m.key===" ")&&(m.key===" "&&m.preventDefault(),h())};return e.jsx("div",{className:`sw-toggle${a?" on":""}`,role:"switch","aria-checked":a,"aria-label":r,"aria-disabled":n||void 0,tabIndex:n?-1:0,onClick:n?void 0:h,onKeyDown:n?void 0:o})}const te=(a,n)=>`${a} ${n}`,Ie=(a,n)=>n==="project"?a==="claude"?".claude/ + the CLAUDE.md block":a==="bob"?".bob/ + the AGENTS.md block":a==="copilot"?".github's Geneseed agents/skills + the AGENTS.md block":".opencode/ + AGENT.md + the bundle":a==="claude"?"~/.claude's agents/skills + the CLAUDE.md block + settings hooks":a==="bob"?"~/.bob's agents/skills + the AGENTS.md block + settings hooks":a==="copilot"?"~/.copilot's agents/skills + the copilot-instructions.md block":"~/.config/opencode's AGENT.md, agents, skills, plugins + the opencode.json entry";function Re(){var $;const{data:a,error:n,reload:r}=U(()=>v.excludes(),[]),[h,o]=x.useState(!1),[m,g]=x.useState([]),w=async(p,d)=>{var k;o(!0);try{const f=await v.excludeMutate(p,d);g(f.messages||[])}catch(f){g(((k=f.body)==null?void 0:k.messages)||[f.message])}finally{o(!1),r()}},y=async()=>{o(!0);try{const p=await v.pickFolder();p.path?await w("add",p.path):p.error&&g([p.error])}catch(p){g([p.message])}finally{o(!1)}};return n||!(($=a==null?void 0:a.installs)!=null&&$.length)?null:e.jsxs("div",{className:"card pad-lg mb-16",children:[e.jsx("div",{className:"card-head",children:e.jsx("h3",{children:"Excluded folders"})}),e.jsx("p",{className:"sub mb-16",children:"Sovereign repos: inside these folders every global harness goes dormant — hooks stay silent and the global preamble is not loaded."}),(a.excludes||[]).map(p=>e.jsxs("div",{className:"row wrap between gap-12",children:[e.jsx("code",{children:p.path}),e.jsxs("span",{className:"mono muted",children:["[",p.hosts.join(", "),"]"]}),e.jsx("button",{className:"btn ghost sm",disabled:h,onClick:()=>w("remove",p.path),children:"Remove"})]},p.path)),e.jsxs("button",{className:"btn ghost sm",disabled:h,onClick:y,children:[e.jsx(E,{name:"folder"})," Exclude a folder…"]}),m.map((p,d)=>e.jsx("p",{className:"sub",children:p},d))]})}function Te({overview:a}){const n=a==null?void 0:a.install;return n?e.jsxs("div",{className:"card pad-lg mb-16",children:[e.jsxs("div",{className:"card-head",children:[e.jsxs("h3",{children:[n.host," · ",n.scope]}),e.jsx("span",{className:"tick right",children:"active"})]}),e.jsxs("div",{className:"kv",children:[e.jsx("span",{className:"k",children:"path"}),e.jsx("code",{className:"v",children:n.path})]}),e.jsxs("div",{className:"kv",children:[e.jsx("span",{className:"k",children:"voice"}),e.jsx("span",{className:"v mono",children:a.theme||"—"})]}),e.jsxs("div",{className:"kv",children:[e.jsx("span",{className:"k",children:"footprint"}),e.jsx("span",{className:"v mono",children:a.footprint||"—"})]}),e.jsxs("div",{className:"kv",children:[e.jsx("span",{className:"k",children:"built"}),e.jsx("span",{className:"v mono",children:a.build_time||"never"})]}),e.jsx("p",{className:"sub motto",children:"Per-folder overrides global: inside a folder with its own harness, only the folder’s harness loads there."})]}):null}function Be({themes:a,overview:n}){if(!a.length||!n)return null;const r=n.theme,h=[...a].sort((o,m)=>o.name===r?-1:m.name===r?1:0);return e.jsxs("details",{className:"card voice-ref mb-16",children:[e.jsxs("summary",{children:[e.jsx("span",{className:"vr-title",children:"Voice"}),e.jsx("span",{className:"vr-current",children:r}),e.jsxs("span",{className:"tick vr-hint",children:[a.length," to choose from — set it per install below"]})]}),e.jsx("div",{className:"vr-list",children:h.map(o=>e.jsxs("div",{className:`voice-row${o.name===r?" current":""}`,children:[e.jsx("span",{className:"vr-name",children:o.name}),e.jsx("span",{className:"vr-desc",children:o.tagline?`“${o.tagline}”`:o.blurb})]},o.name))})]})}function Oe({onAction:a,themes:n=[],currentTheme:r,overview:h,dataRev:o,onMutated:m}){const{data:g,error:w}=U(()=>v.installs(),[o]),{data:y,error:$}=U(()=>v.mcp(),[o]),[p,d]=x.useState(""),[k,f]=x.useState(""),[oe,M]=x.useState(""),[ne,ce]=x.useState({}),[re,de]=x.useState({}),[ie,he]=x.useState({}),[me,pe]=x.useState({}),[ue,xe]=x.useState({}),[i,b]=x.useState(null),[_,z]=x.useState(!1),[S,D]=x.useState(null),[A,W]=x.useState(""),je=x.useMemo(()=>{const s={};for(const l of(y==null?void 0:y.targets)||[]){const c=te(l.host||"opencode",l.root);(s[c]||(s[c]=[])).push(l)}return s},[y]);if(w||$)return e.jsx(De,{error:w||$});if(!g||!y)return e.jsx(Fe,{});const C=g.installs,B=g.postures||[],P=g.modes||[],be=C.filter(s=>s.state==="active").length,ve=[{key:"global",title:"Global",sub:"machine-wide",rows:C.filter(s=>s.scope==="global"),empty:"No global installs detected."},{key:"project",title:"Per-project",sub:"one folder each",rows:C.filter(s=>s.scope!=="global"),empty:"No per-project installs yet; use “Deploy to folder…”."}],F=s=>ne[s.id]||s.theme||r||"neutral",q=(s,l)=>ce(c=>({...c,[s.id]:l})),G=s=>re[s.id]||s.footprint||"full",J=(s,l)=>de(c=>({...c,[s.id]:l})),I=s=>ie[s.id]||s.posture||"peer",Q=(s,l)=>he(c=>({...c,[s.id]:l})),R=s=>me[s.id]||s.mode||"direct",X=(s,l)=>pe(c=>({...c,[s.id]:l})),ge=s=>F(s)===s.theme&&G(s)===(s.footprint||"full")&&I(s)===(s.posture||"peer")&&R(s)===(s.mode||"direct"),Y=s=>{const l=F(s),c=G(s),j=I(s),N=R(s),T=s.state==="absent"?`Install Geneseed into ${s.path} with the “${l}” voice (${c} footprint, ${j} posture, ${N} mode)? Files are added non-destructively (your own config is left untouched); deactivate or uninstall later.`:`Rebuild this install (voice “${l}”, ${c} footprint, ${j} posture, ${N} mode)? It rebuilds in place, non-destructive.`;window.confirm(T)&&(a==null||a("install",{host:s.host,scope:s.scope,path:s.path,theme:l,footprint:c,posture:j,mode:N}))},fe=async s=>{if(!(s.state==="active"&&!window.confirm("Deactivate this install? Files are moved aside, not deleted; reactivate any time."))){f(s.id),d("");try{const l=await v.installToggle(s.host,s.path,s.state==="active"?"deactivate":"activate");if(!l.ok){const c=Array.isArray(l.failed)?l.failed.join(", "):"";d(l.error||c&&`unrestored: ${c}`||"action failed");return}m==null||m()}catch(l){d(l.message)}finally{f("")}}},Ne=async()=>{const s=S;if(s){f(s.id),d("");try{const l=await v.installRemove(s.host,s.path,s.memory);if(!l.ok){d(l.error||"remove failed");return}D(null),m==null||m()}catch(l){d(l.message)}finally{f("")}}},ye=async s=>{d("");try{const l=await v.mcpReveal(s.path);d(`Opening ${l.dir} — if no window appeared, open that folder by hand.`)}catch(l){d(l.message)}},Z=async(s,l)=>{const c=s.path+l.name;M(c),d("");try{await v.mcpToggle(s.path,l.name,l.state!=="enabled"),m==null||m()}catch(j){d(j.message)}finally{M("")}},ke=s=>xe(l=>({...l,[s]:!l[s]})),Ce=()=>{var s,l,c;return((s=C.find(j=>j.selected))==null?void 0:s.host)||((l=C.find(j=>j.state==="active"))==null?void 0:l.host)||((c=C[0])==null?void 0:c.host)||"opencode"},we=()=>b({path:"",host:Ce(),theme:r||"neutral",footprint:"full",posture:"peer",mode:"direct"}),$e=async()=>{z(!0),d("");try{const s=await v.pickFolder();s.path?b(l=>l&&{...l,path:s.path}):s.error&&d(s.error)}catch(s){d(s.message)}finally{z(!1)}},ee=async()=>{const s=((i==null?void 0:i.path)||"").trim();if(!s){d("Choose or type a folder to deploy into.");return}d(""),await(a==null?void 0:a("deploy",{host:i.host,path:s,theme:i.theme,footprint:i.footprint,posture:i.posture,mode:i.mode}))&&b(null)};return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"head-row mb-18",children:[e.jsxs("div",{children:[e.jsx("div",{className:"eyebrow",children:"Setup"}),e.jsx("h1",{className:"h",children:"Harness"}),e.jsx("p",{className:"sub",children:"This machine’s install: where it lives, how it’s built, and the voice it speaks with. Other hosts (Claude Code, Bob, Copilot) install from here too."})]}),e.jsxs("div",{className:"row wrap gap-10",children:[a?e.jsxs("button",{className:"btn",onClick:()=>i?b(null):we(),children:[e.jsx(E,{name:"folder"})," Deploy to folder…"]}):null,e.jsxs("button",{className:"btn",onClick:()=>a("build-all"),children:[e.jsx(E,{name:"refresh"})," Rebuild all"]})]})]}),e.jsx(Te,{overview:h}),e.jsx(Be,{themes:n,overview:h}),e.jsxs("div",{className:"card pad-lg mb-16",children:[e.jsxs("div",{className:"card-head",children:[e.jsx("h3",{children:"Every install"}),e.jsx("div",{className:"right",children:e.jsxs("span",{className:"tick",children:[be," active · ",C.length," total"]})})]}),i?e.jsxs("div",{className:"deploy-pop",children:[e.jsxs("div",{className:"dp-row",children:[e.jsx("input",{className:"inp dp-path",type:"text",placeholder:"/path/to/project, or click Browse…",value:i.path,onChange:s=>b(l=>({...l,path:s.target.value})),onKeyDown:s=>s.key==="Enter"&&ee()}),e.jsx("button",{className:"btn ghost sm",disabled:_,onClick:$e,children:_?"Choosing…":"Browse…"})]}),e.jsxs("div",{className:"dp-row",children:[e.jsxs("label",{className:"dp-field",children:[e.jsx("span",{children:"Deploy as"}),e.jsxs("select",{className:"sel","aria-label":"host for the new harness",value:i.host,onChange:s=>b(l=>({...l,host:s.target.value})),children:[e.jsx("option",{value:"opencode",children:"OpenCode"}),e.jsx("option",{value:"claude",children:"Claude Code"}),e.jsx("option",{value:"bob",children:"BOB (IBM)"}),e.jsx("option",{value:"copilot",children:"GitHub Copilot"})]})]}),e.jsxs("label",{className:"dp-field",children:[e.jsx("span",{children:"Voice"}),e.jsx(O,{label:"voice for the new harness",value:i.theme,themes:n,onChange:s=>b(l=>({...l,theme:s}))})]}),e.jsxs("label",{className:"dp-field",children:[e.jsx("span",{children:"Footprint"}),e.jsx(V,{label:"footprint for the new harness",value:i.footprint,onChange:s=>b(l=>({...l,footprint:s}))})]}),e.jsxs("label",{className:"dp-field",children:[e.jsx("span",{children:"Posture"}),e.jsx(K,{label:"posture for the new harness",value:i.posture,postures:B,onChange:s=>b(l=>({...l,posture:s}))})]}),e.jsxs("label",{className:"dp-field",children:[e.jsx("span",{children:"Mode"}),e.jsx(H,{label:"mode for the new harness",value:i.mode,modes:P,onChange:s=>b(l=>({...l,mode:s}))})]}),e.jsx("button",{className:"btn sm",onClick:ee,children:"Deploy"}),e.jsx("button",{className:"btn ghost sm",onClick:()=>b(null),children:"Cancel"})]}),e.jsxs("p",{className:"sub dp-note",children:["Adds a per-repo harness (",e.jsx("code",{children:i.host==="claude"?".claude/ + CLAUDE.md":i.host==="bob"?".bob/ + AGENTS.md":i.host==="copilot"?".github/ + AGENTS.md":".opencode/ + AGENT.md"}),") into the folder, non-destructively. It’s then tracked here even after you leave its directory."]})]}):null,e.jsxs("p",{className:"sub mb-16",children:["Every Geneseed install on this machine: OpenCode, Claude Code, Bob, and Copilot — global and per-repo. Toggle one off without deleting it (files move aside, reactivate any time). Active rows expand to wire their MCP servers. ",e.jsx("strong",{children:"Rebuild all"})," re-emits every active install in its own voice and mode, as one background job."]}),e.jsxs("p",{className:"sub mb-16",children:[e.jsx("strong",{children:"Per-folder now overrides global."})," Inside a folder that has its own harness, the ",e.jsx("em",{children:"same host’s"})," global harness steps aside; only the folder’s harness loads there (the global one still applies everywhere else). Set"," ",e.jsx("code",{children:"GENESEED_STACK_GLOBAL=1"})," to load both. Existing installs pick this up on their next rebuild."]}),p?e.jsx("p",{className:"badge bad mb-16",children:p}):null,e.jsx("div",{className:"tbl-scroll",children:e.jsxs("table",{className:"tbl harness-tbl",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{"aria-label":"expand"}),e.jsx("th",{children:"Harness"}),e.jsx("th",{children:"Voice"}),e.jsx("th",{children:"MCP"}),e.jsx("th",{children:"Status"}),e.jsx("th",{className:"th-acts"})]})}),ve.map(s=>e.jsxs("tbody",{children:[e.jsx("tr",{className:"h-group",children:e.jsxs("td",{colSpan:6,children:[s.title,e.jsxs("span",{className:"hg-sub",children:[" · ",s.sub]})]})}),s.rows.length?s.rows.map(Se):e.jsx("tr",{className:"h-empty-row",children:e.jsx("td",{colSpan:6,className:"h-empty",children:s.empty})})]},s.key))]})})]}),e.jsx(Re,{})]});function Se(s){const l=s.state==="active",c=je[te(s.host,s.path)]||[],j=c.length>0,N=j&&!ue[s.id],T=c.reduce((t,u)=>t+u.servers.filter(L=>L.state==="enabled").length,0),se=`voice for ${s.host} · ${s.scope}`,Ee=l?"active":s.state==="disabled"?"disabled":"not installed";return e.jsxs(Ge.Fragment,{children:[e.jsxs("tr",{children:[e.jsx("td",{className:"h-exp-cell",children:j?e.jsx("button",{className:"h-exp","aria-expanded":N,"aria-label":`${N?"collapse":"expand"} MCP for ${s.host} · ${s.scope}`,onClick:()=>ke(s.id),children:e.jsx(E,{name:"chevron",className:`glyph${N?" open":""}`})}):null}),e.jsxs("td",{children:[e.jsxs("span",{className:"name",children:[s.host," · ",s.scope]}),e.jsx("code",{className:"h-path",title:s.path,children:s.path})]}),e.jsx("td",{className:"mono",children:s.theme||"—"}),e.jsx("td",{children:j?e.jsxs("span",{className:T?"mono":"mono muted",children:[T," on"]}):e.jsx("span",{className:"muted",children:"—"})}),e.jsx("td",{children:e.jsx("span",{className:`badge ${l?"ok":""}`,children:Ee})}),e.jsx("td",{children:e.jsxs("div",{className:"h-acts",children:[e.jsx("div",{className:"ha-cell ha-voice",children:l&&a?e.jsx(O,{label:se,value:F(s),themes:n,onChange:t=>q(s,t)}):null}),e.jsx("div",{className:"ha-cell ha-fp",children:l&&a?e.jsx(V,{label:`footprint for ${s.host} · ${s.scope}`,value:G(s),onChange:t=>J(s,t)}):null}),e.jsx("div",{className:"ha-cell ha-posture",children:l&&a?e.jsx(K,{label:`posture for ${s.host} · ${s.scope}`,value:I(s),postures:B,onChange:t=>Q(s,t)}):null}),e.jsx("div",{className:"ha-cell ha-mode",children:l&&a?e.jsx(H,{label:`mode for ${s.host} · ${s.scope}`,value:R(s),modes:P,onChange:t=>X(s,t)}):null}),e.jsx("div",{className:"ha-cell ha-btn",children:s.state==="absent"&&a?e.jsx("button",{className:"btn ghost sm","aria-expanded":A===s.id,onClick:()=>W(t=>t===s.id?"":s.id),children:"Install…"}):l&&a?e.jsx("button",{className:"btn ghost sm",disabled:ge(s),onClick:()=>Y(s),children:"Apply"}):null}),e.jsx("div",{className:"ha-cell ha-sw",children:s.state!=="absent"?e.jsx(ae,{on:l,disabled:k===s.id,label:`activate ${s.host} · ${s.scope}`,onToggle:()=>fe(s)}):null}),e.jsx("div",{className:"ha-cell ha-trash",children:s.state!=="absent"&&a?e.jsx("button",{className:"btn ghost sm h-trash","aria-label":`remove ${s.host} · ${s.scope} from ${s.path}`,title:"Remove this harness",disabled:k===s.id,onClick:()=>D(t=>(t==null?void 0:t.id)===s.id?null:{id:s.id,host:s.host,path:s.path,memory:"keep"}),children:e.jsx(E,{name:"clear"})}):null})]})})]}),s.state==="absent"&&A===s.id?e.jsxs("tr",{className:"h-detail-row h-setup-row",children:[e.jsx("td",{}),e.jsx("td",{colSpan:5,className:"h-detail",children:e.jsxs("div",{className:"h-setup",children:[e.jsxs("p",{className:"sub hs-msg",children:["Install Geneseed into ",e.jsx("code",{children:s.path})," as ",e.jsx("strong",{children:s.host}),". Files are added non-destructively; deactivate or uninstall later."]}),e.jsxs("div",{className:"hs-steps",children:[e.jsxs("label",{className:"hs-step",children:[e.jsx("span",{children:"1 · Voice"}),e.jsx(O,{label:se,value:F(s),themes:n,onChange:t=>q(s,t)})]}),e.jsxs("label",{className:"hs-step",children:[e.jsx("span",{children:"2 · Footprint"}),e.jsx(V,{label:`footprint for ${s.host} · ${s.scope}`,value:G(s),onChange:t=>J(s,t)})]}),e.jsxs("label",{className:"hs-step",children:[e.jsx("span",{children:"3 · Posture"}),e.jsx(K,{label:`posture for ${s.host} · ${s.scope}`,value:I(s),postures:B,onChange:t=>Q(s,t)})]}),e.jsxs("label",{className:"hs-step",children:[e.jsx("span",{children:"4 · Mode"}),e.jsx(H,{label:`mode for ${s.host} · ${s.scope}`,value:R(s),modes:P,onChange:t=>X(s,t)})]})]}),e.jsxs("div",{className:"hr-acts",children:[e.jsx("button",{className:"btn sm",onClick:()=>Y(s),children:"Install"}),e.jsx("button",{className:"btn ghost sm",onClick:()=>W(""),children:"Cancel"})]})]})})]}):null,(S==null?void 0:S.id)===s.id?e.jsxs("tr",{className:"h-detail-row h-remove-row",children:[e.jsx("td",{}),e.jsx("td",{colSpan:5,className:"h-detail",children:e.jsxs("div",{className:"h-remove",children:[e.jsxs("div",{className:"hr-msg",children:[e.jsx("strong",{children:s.scope==="project"?"Remove this harness from the folder?":`Remove the global ${s.host} install?`}),e.jsxs("span",{className:"sub",children:["Deletes ",e.jsx("code",{children:Ie(s.host,s.scope)}),s.scope==="project"?" and de-lists it.":"; the row stays, marked “not installed.”"," ","This can’t be undone."]})]}),e.jsxs("label",{className:"hr-field",children:[e.jsx("span",{children:"Memory & notebook"}),e.jsxs("select",{className:"sel","aria-label":"memory disposition",value:S.memory,onChange:t=>D(u=>({...u,memory:t.target.value})),children:[e.jsx("option",{value:"keep",children:"keep in place"}),e.jsx("option",{value:"archive",children:"archive aside"}),e.jsx("option",{value:"delete",children:"delete too"})]})]}),e.jsxs("div",{className:"hr-acts",children:[e.jsx("button",{className:"btn sm hr-go",disabled:k===s.id,onClick:Ne,children:k===s.id?"Removing…":"Remove"}),e.jsx("button",{className:"btn ghost sm",onClick:()=>D(null),children:"Cancel"})]})]})})]}):null,N?e.jsxs("tr",{className:"h-detail-row",children:[e.jsx("td",{}),e.jsx("td",{colSpan:5,className:"h-detail",children:c.map(t=>e.jsxs("div",{className:"mcp-target",children:[e.jsxs("div",{className:"mt-head",children:[t.label," · ",e.jsx("code",{children:t.path}),e.jsx("button",{className:"btn ghost sm",onClick:()=>ye(t),title:`Open the folder holding ${t.path} — tokens and URLs go in this file`,children:"Open folder"}),t.commented&&" (has comments; edit by hand)"]}),t.servers.map(u=>{const L=t.path+u.name,le=!!(t.commented||oe===L);return e.jsxs("div",{className:"mcp-row",children:[e.jsxs("div",{className:"mcp-info",children:[e.jsxs("div",{className:"mi-top",children:[e.jsx("strong",{children:u.label}),e.jsx("span",{className:`badge ${u.state==="enabled"?"ok":""}`,children:u.state})]}),e.jsx("p",{children:u.desc})]}),u.state!=="absent"?e.jsx(ae,{on:u.state==="enabled",disabled:le,label:`${u.label} server`,onToggle:()=>Z(t,u)}):u.preset?e.jsx("button",{className:"btn ghost sm",disabled:le,onClick:()=>Z(t,u),children:"Add"}):null]},u.name)})]},t.path))})]}):null]},s.id)}}export{Oe as default};