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.
- package/CHANGELOG.md +55 -0
- package/SHIPPED.md +1 -1
- package/docs/web/exclusions.md +1 -1
- package/docs/web/footprint.md +1 -1
- package/docs/web/foreman-mode.md +2 -2
- package/docs/web/themes-author.md +1 -1
- package/docs/web-ui.md +41 -29
- package/harness.config.json +1 -1
- package/js/web/api.mjs +82 -1
- package/js/web/routes.mjs +22 -0
- package/package.json +1 -1
- package/web/dist/assets/{Activity-z0uXdS7J.js → Activity-B49fE_ug.js} +1 -1
- package/web/dist/assets/{ActivityDetail-CEj5IJpF.js → ActivityDetail-abt7hzD2.js} +1 -1
- package/web/dist/assets/{Diff-BMR8fPuG.js → Diff-D3_bVkih.js} +1 -1
- package/web/dist/assets/{Doctor-Bz5qE4r9.js → Doctor-hXp7srsx.js} +1 -1
- package/web/dist/assets/{Graph-BV6a6t2l.js → Graph-DvYMtlvl.js} +1 -1
- package/web/dist/assets/Harness-BIWei7N5.js +1 -0
- package/web/dist/assets/Laws-CxmnRdwt.js +3 -0
- package/web/dist/assets/{Library-CiXN9sC3.js → Library-MjbOkHIW.js} +1 -1
- package/web/dist/assets/{Markdown-DG6FjUz-.js → Markdown-Dx_w0faR.js} +1 -1
- package/web/dist/assets/{Profile-B2oSvdxH.js → Profile-BRFvv2gr.js} +1 -1
- package/web/dist/assets/{Rules-DobVPd80.js → Rules-CfsV1EVy.js} +1 -1
- package/web/dist/assets/{Skills-DQPsvsn9.js → Skills-DArA6IHS.js} +1 -1
- package/web/dist/assets/{StatusBadge-BhJ3HhdI.js → StatusBadge-C9bZOEZk.js} +1 -1
- package/web/dist/assets/{activity-D9mfv7pY.js → activity-Cc0n2jwx.js} +1 -1
- package/web/dist/assets/index-Bx3oxZ1o.css +1 -0
- package/web/dist/assets/index-DC3-GlCY.js +1 -0
- package/web/dist/assets/index-DP035wPa.js +12 -0
- package/web/dist/assets/{index-DGM5j2hB.js → index-dHi-7M-F.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/pages/Laws.jsx +4 -18
- package/web/dist/assets/About-CPvIFZGh.js +0 -1
- package/web/dist/assets/Harnesses-C5Ag5PaQ.js +0 -1
- package/web/dist/assets/Laws-BK07NKw9.js +0 -3
- package/web/dist/assets/Themes-BP38FBU9.js +0 -2
- package/web/dist/assets/index-BE226Gv6.css +0 -1
- package/web/dist/assets/index-BZ7cPLMI.js +0 -1
- 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.
|
|
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);
|
package/docs/web/exclusions.md
CHANGED
|
@@ -3,7 +3,7 @@ group: concepts
|
|
|
3
3
|
order: 9
|
|
4
4
|
title: "Sovereign repos (exclusions)"
|
|
5
5
|
kind: "concept"
|
|
6
|
-
link: {"hash": "#/
|
|
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
|
|
package/docs/web/footprint.md
CHANGED
|
@@ -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
|
-
- **
|
|
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
|
|
package/docs/web/foreman-mode.md
CHANGED
|
@@ -3,7 +3,7 @@ group: concepts
|
|
|
3
3
|
order: 12
|
|
4
4
|
title: "Foreman mode"
|
|
5
5
|
kind: "concept"
|
|
6
|
-
link: {"hash": "#/
|
|
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
|
-
- **
|
|
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": "#/
|
|
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
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
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
|
-
|
|
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
|
-
### 🎨
|
|
133
|
-
|
|
134
|
-
- **
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
151
|
-
(deployed/target/theme/version/memory store), a Maintenance card (PATH
|
|
152
|
-
uninstall), an offline package download,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
package/harness.config.json
CHANGED
|
@@ -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.
|
|
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))
|
|
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.
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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};
|