herm-tui 1.9.0-dev.8 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,16 +93,61 @@ See [`.env.example`](./.env.example) for rarely-needed overrides.
93
93
 
94
94
  ### Share and install eikons
95
95
 
96
- - Press `M` from Gallery to enter the native Eikon Marketplace.
97
- - Search shared catalog entries, preview the selected eikon, install without
98
- activating, then use it when ready.
99
- - Use `eikon.liftaris.dev` as a discovery mirror only; it previews catalog
100
- entries and points back to Herm for native install/use.
101
- - Submit local non-bundled eikons with `u`; Herm shows the exact
102
- preflight bundle before submission and blocks published marketplace installs
103
- from duplicate submission.
104
-
105
- Herm owns native Marketplace behavior. The eikon repo owns the registry,
96
+ Eikons are 48×24 terminal avatars. The shipped lifecycle is deterministic:
97
+ discover, inspect, install, use, update, and remove.
98
+
99
+ In Herm:
100
+
101
+ - Open Eikon Catalog, or run `/catalog`, to browse shared catalog
102
+ entries.
103
+ - Preview rows before installing. Trust is shown as `Verified`, `Unverified`,
104
+ or `Mismatch` beside source and compatibility state.
105
+ - Install adds the eikon to your local library without activating it.
106
+ - Use selects an installed eikon as the active avatar.
107
+ - Installing over the currently active eikon name also requires confirmation or
108
+ `--active-ok` because it replaces the active avatar's backing package.
109
+ - Update or remove an active eikon only after confirming that the active
110
+ avatar's backing package will change or be cleared.
111
+
112
+ From the shell:
113
+
114
+ ```bash
115
+ herm eikon search [query] [--json]
116
+ herm eikon browse [query] [--json]
117
+ herm eikon inspect <name|url|dir> [--json]
118
+ herm eikon install <name|url|dir> [--name N] [--no-source] [--active-ok] [--json]
119
+ herm eikon list [--json]
120
+ herm eikon use <name> [--json]
121
+ herm eikon info <name> [--json]
122
+ herm eikon update <name> [--active-ok] [--json]
123
+ herm eikon remove <name> [--active-ok] [--json]
124
+ herm eikon delist <name|id> [--json]
125
+ ```
126
+
127
+ `install` never activates. `use` is the activation action. JSON output is
128
+ available for automation with `--json`.
129
+
130
+ Default Catalog installs fetch built package artifacts referenced by the
131
+ catalog, not creator repositories. Direct GitHub installs are for sharing
132
+ outside the default catalog and support both single-package repos and
133
+ multi-eikon catalog repos addressed as `github.com/user/repo/eikon-name`.
134
+ Private GitHub repos use normal git authentication.
135
+
136
+ Creators can share Eikons through normal GitHub repositories. For official
137
+ registry listing, press `u` in Studio or use Library's Share to catalog action
138
+ after baking. Herm previews metadata, the prepared public bundle, and the GitHub
139
+ PR target; with local `gh` auth it creates the PR, otherwise it shows manual PR
140
+ steps with the prepared bundle path and compare URL. Direct-install repos can
141
+ still be prepared with upstream `eikon pack`, `eikon index`, and
142
+ `eikon manifest`.
143
+ `eikon publish` remains the lower-level GitHub PR contribution helper for the
144
+ configured/default catalog repo; it is not a hosted marketplace account, upload,
145
+ dashboard, or moderation flow.
146
+
147
+ Use `eikon.liftaris.dev` as a discovery gallery only; it previews catalog
148
+ entries and gives copyable Herm install instructions.
149
+
150
+ Herm owns native Catalog behavior. The eikon repo owns the registry,
106
151
  browser mirror, shared catalog/player exports, install resolver, and publish
107
152
  preflight. Herm imports public eikon package exports rather than browser mirror
108
153
  internals or unexported source paths.
@@ -0,0 +1,123 @@
1
+ {
2
+ "kind": "eikon.package",
3
+ "schemaVersion": "1.0",
4
+ "id": "liftaris/nous",
5
+ "name": "nous",
6
+ "version": "1.0.0",
7
+ "display": {
8
+ "title": "nous",
9
+ "author": "kaio",
10
+ "glyph": "⬡"
11
+ },
12
+ "compatibility": {
13
+ "eikon": ">=1 <2"
14
+ },
15
+ "legacy": {
16
+ "sourceFormat": "pre-launch .eikon draft",
17
+ "migration": "converted",
18
+ "notes": [
19
+ "moved legacy metadata: author, created, source_url"
20
+ ]
21
+ },
22
+ "source": {
23
+ "base": "blobs/sha256/8c4dc411ca159253cc90fa31f5263799054592e6eaeb4e5c99881c8cac29e67b",
24
+ "states": {
25
+ "idle": {
26
+ "file": "blobs/sha256/481c010826d7cacb9c419455bd0f73d020f66492a1236b37e4e53353b7965902"
27
+ },
28
+ "listening": {
29
+ "file": "blobs/sha256/94f0f4f710c708f516128125142d5dce701f83558e9fad444c1bb376ee3c1fea"
30
+ },
31
+ "thinking": {
32
+ "file": "blobs/sha256/5ae0eb2e1889c72bed69144998acc6f80b9d11b67beffccd972842a9d6ed0f24"
33
+ },
34
+ "speaking": {
35
+ "file": "blobs/sha256/5c15fd3db3fba93b378eb5b23ab9ecf283e8fb5a08828f1393e91b323b65eab2"
36
+ },
37
+ "working": {
38
+ "file": "blobs/sha256/f63a864e84c4b4f672d6b02a4bcf610b2881fcf64166273b29a642fb76e00f09"
39
+ },
40
+ "error": {
41
+ "file": "blobs/sha256/5d1a1fa59ec9091d9c03d99728b15287ef58d397ea2c787a65c55519482683c8"
42
+ }
43
+ }
44
+ },
45
+ "entrypoints": {
46
+ "default": "nous.eikon"
47
+ },
48
+ "files": [
49
+ {
50
+ "path": "nous.eikon",
51
+ "role": "runtime",
52
+ "mediaType": "application/vnd.eikon.stream+jsonl",
53
+ "size": 139822,
54
+ "digest": "sha256:6a74e9ae20ecafe6cf268230272582ef618b31927cd13e11ce59ed8931413883",
55
+ "encoding": "gzip",
56
+ "decodedSize": 1401439,
57
+ "decodedDigest": "sha256:b2685743b05ddf201920b69db58640d4fab644e637fa7d313c6dfa215fa41fb7"
58
+ },
59
+ {
60
+ "path": "blobs/sha256/8c4dc411ca159253cc90fa31f5263799054592e6eaeb4e5c99881c8cac29e67b",
61
+ "role": "source.base",
62
+ "mediaType": "image/png",
63
+ "size": 98644,
64
+ "digest": "sha256:8c4dc411ca159253cc90fa31f5263799054592e6eaeb4e5c99881c8cac29e67b"
65
+ },
66
+ {
67
+ "path": "blobs/sha256/481c010826d7cacb9c419455bd0f73d020f66492a1236b37e4e53353b7965902",
68
+ "role": "source.clip",
69
+ "mediaType": "video/mp4",
70
+ "size": 105789,
71
+ "digest": "sha256:481c010826d7cacb9c419455bd0f73d020f66492a1236b37e4e53353b7965902",
72
+ "signal": "state.idle"
73
+ },
74
+ {
75
+ "path": "blobs/sha256/94f0f4f710c708f516128125142d5dce701f83558e9fad444c1bb376ee3c1fea",
76
+ "role": "source.clip",
77
+ "mediaType": "video/mp4",
78
+ "size": 275110,
79
+ "digest": "sha256:94f0f4f710c708f516128125142d5dce701f83558e9fad444c1bb376ee3c1fea",
80
+ "signal": "state.listening"
81
+ },
82
+ {
83
+ "path": "blobs/sha256/5ae0eb2e1889c72bed69144998acc6f80b9d11b67beffccd972842a9d6ed0f24",
84
+ "role": "source.clip",
85
+ "mediaType": "video/mp4",
86
+ "size": 174520,
87
+ "digest": "sha256:5ae0eb2e1889c72bed69144998acc6f80b9d11b67beffccd972842a9d6ed0f24",
88
+ "signal": "state.thinking"
89
+ },
90
+ {
91
+ "path": "blobs/sha256/5c15fd3db3fba93b378eb5b23ab9ecf283e8fb5a08828f1393e91b323b65eab2",
92
+ "role": "source.clip",
93
+ "mediaType": "video/mp4",
94
+ "size": 96790,
95
+ "digest": "sha256:5c15fd3db3fba93b378eb5b23ab9ecf283e8fb5a08828f1393e91b323b65eab2",
96
+ "signal": "state.speaking"
97
+ },
98
+ {
99
+ "path": "blobs/sha256/f63a864e84c4b4f672d6b02a4bcf610b2881fcf64166273b29a642fb76e00f09",
100
+ "role": "source.clip",
101
+ "mediaType": "video/mp4",
102
+ "size": 160072,
103
+ "digest": "sha256:f63a864e84c4b4f672d6b02a4bcf610b2881fcf64166273b29a642fb76e00f09",
104
+ "signal": "state.working"
105
+ },
106
+ {
107
+ "path": "blobs/sha256/5d1a1fa59ec9091d9c03d99728b15287ef58d397ea2c787a65c55519482683c8",
108
+ "role": "source.clip",
109
+ "mediaType": "video/mp4",
110
+ "size": 168804,
111
+ "digest": "sha256:5d1a1fa59ec9091d9c03d99728b15287ef58d397ea2c787a65c55519482683c8",
112
+ "signal": "state.error"
113
+ }
114
+ ],
115
+ "origin": {
116
+ "kind": "default-catalog",
117
+ "source": "https://eikon.liftaris.dev/packages/liftaris/nous/1.0.0.json",
118
+ "sourceKey": "registry:eikon.liftaris.dev:liftaris/nous@1.0.0",
119
+ "identityKey": "registry:eikon.liftaris.dev:liftaris/nous@1.0.0",
120
+ "packageUrl": "https://eikon.liftaris.dev/packages/liftaris/nous/1.0.0.json",
121
+ "trust": "verified"
122
+ }
123
+ }
Binary file
@@ -1,17 +1,40 @@
1
- """Eikon install plugin."""
1
+ """Eikon lifecycle plugin."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from .schemas import EIKON_INSTALL_SCHEMA
6
- from .tools import _handle_eikon_install, check_herm_available
5
+ from .schemas import (
6
+ EIKON_INSTALL_SCHEMA,
7
+ EIKON_LIST_SCHEMA,
8
+ EIKON_REMOVE_SCHEMA,
9
+ EIKON_SEARCH_SCHEMA,
10
+ EIKON_UPDATE_SCHEMA,
11
+ EIKON_USE_SCHEMA,
12
+ )
13
+ from .tools import (
14
+ _handle_eikon_install,
15
+ _handle_eikon_list,
16
+ _handle_eikon_remove,
17
+ _handle_eikon_search,
18
+ _handle_eikon_update,
19
+ _handle_eikon_use,
20
+ check_herm_available,
21
+ )
7
22
 
8
23
 
9
24
  def register(ctx) -> None:
10
- ctx.register_tool(
11
- name="eikon_install",
12
- toolset="eikon",
13
- schema=EIKON_INSTALL_SCHEMA,
14
- handler=_handle_eikon_install,
15
- check_fn=check_herm_available,
16
- emoji="",
17
- )
25
+ for name, schema, handler in [
26
+ ("eikon_install", EIKON_INSTALL_SCHEMA, _handle_eikon_install),
27
+ ("eikon_search", EIKON_SEARCH_SCHEMA, _handle_eikon_search),
28
+ ("eikon_list", EIKON_LIST_SCHEMA, _handle_eikon_list),
29
+ ("eikon_use", EIKON_USE_SCHEMA, _handle_eikon_use),
30
+ ("eikon_update", EIKON_UPDATE_SCHEMA, _handle_eikon_update),
31
+ ("eikon_remove", EIKON_REMOVE_SCHEMA, _handle_eikon_remove),
32
+ ]:
33
+ ctx.register_tool(
34
+ name=name,
35
+ toolset="eikon",
36
+ schema=schema,
37
+ handler=handler,
38
+ check_fn=check_herm_available,
39
+ emoji="⬡",
40
+ )
@@ -1,7 +1,12 @@
1
1
  name: eikon
2
2
  version: 1.0.0
3
- description: "Herm eikon installer tool. Lets Hermes chat install terminal avatars through the existing `herm eikon install` CLI path."
3
+ description: "Herm eikon lifecycle tools. Lets Hermes chat search, install, activate, update, and remove terminal avatars through the existing `herm eikon` CLI path."
4
4
  author: Liftaris
5
5
  kind: standalone
6
6
  provides_tools:
7
7
  - eikon_install
8
+ - eikon_search
9
+ - eikon_list
10
+ - eikon_use
11
+ - eikon_update
12
+ - eikon_remove
@@ -7,7 +7,7 @@ EIKON_INSTALL_SCHEMA = {
7
7
  "description": (
8
8
  "Install a Herm eikon/avatar from the public catalog, a manifest URL, "
9
9
  "a git repository, or a local directory. Uses `herm eikon install`; "
10
- "no separate eikon executable is required."
10
+ "set_active activates the installed avatar with `herm eikon use`."
11
11
  ),
12
12
  "parameters": {
13
13
  "type": "object",
@@ -32,10 +32,78 @@ EIKON_INSTALL_SCHEMA = {
32
32
  },
33
33
  "set_active": {
34
34
  "type": "boolean",
35
- "description": "Set the installed eikon as the active Herm avatar. Default true.",
35
+ "description": "Activate the installed eikon after install. Default true.",
36
36
  "default": True,
37
37
  },
38
+ "active_ok": {
39
+ "type": "boolean",
40
+ "description": "Allow replacing the active eikon's backing package when installing over it.",
41
+ "default": False,
42
+ },
38
43
  },
39
44
  "required": ["source"],
40
45
  },
41
46
  }
47
+
48
+ EIKON_SEARCH_SCHEMA = {
49
+ "name": "eikon_search",
50
+ "description": "Search the Herm eikon marketplace/catalog via `herm eikon search --json`.",
51
+ "parameters": {
52
+ "type": "object",
53
+ "properties": {
54
+ "query": {"type": "string", "description": "Optional search query."},
55
+ },
56
+ },
57
+ }
58
+
59
+ EIKON_LIST_SCHEMA = {
60
+ "name": "eikon_list",
61
+ "description": "List installed Herm eikons via `herm eikon list --json`.",
62
+ "parameters": {"type": "object", "properties": {}},
63
+ }
64
+
65
+ EIKON_USE_SCHEMA = {
66
+ "name": "eikon_use",
67
+ "description": "Set an installed or bundled Herm eikon as active via `herm eikon use --json`.",
68
+ "parameters": {
69
+ "type": "object",
70
+ "properties": {
71
+ "name": {"type": "string", "description": "Installed or bundled eikon name."},
72
+ },
73
+ "required": ["name"],
74
+ },
75
+ }
76
+
77
+ EIKON_UPDATE_SCHEMA = {
78
+ "name": "eikon_update",
79
+ "description": "Update an installed Herm eikon from its recorded source via `herm eikon update --json`.",
80
+ "parameters": {
81
+ "type": "object",
82
+ "properties": {
83
+ "name": {"type": "string", "description": "Installed eikon name."},
84
+ "active_ok": {
85
+ "type": "boolean",
86
+ "description": "Allow updating the active avatar's backing package.",
87
+ "default": False,
88
+ },
89
+ },
90
+ "required": ["name"],
91
+ },
92
+ }
93
+
94
+ EIKON_REMOVE_SCHEMA = {
95
+ "name": "eikon_remove",
96
+ "description": "Remove an installed Herm eikon via `herm eikon remove --json`.",
97
+ "parameters": {
98
+ "type": "object",
99
+ "properties": {
100
+ "name": {"type": "string", "description": "Installed eikon name."},
101
+ "active_ok": {
102
+ "type": "boolean",
103
+ "description": "Allow clearing the active avatar if this eikon is active.",
104
+ "default": False,
105
+ },
106
+ },
107
+ "required": ["name"],
108
+ },
109
+ }
@@ -30,40 +30,96 @@ def check_herm_available() -> bool:
30
30
  return shutil.which(binary) is not None
31
31
 
32
32
 
33
- def _handle_eikon_install(args: dict[str, Any], **_: Any) -> str:
34
- source = str(args.get("source") or "").strip()
35
- if not source:
36
- return _json({"ok": False, "error": "source is required"})
37
-
33
+ def _run(parts: list[str], label: str, timeout: int = 120) -> dict[str, Any]:
38
34
  binary = _herm_bin()
39
35
  if not binary:
40
- return _json({"ok": False, "error": "herm executable not found on PATH"})
41
-
42
- cmd = [binary, "eikon", "install", source, "--json"]
43
- name = str(args.get("name") or "").strip()
44
- if name:
45
- cmd.extend(["--name", name])
46
- if args.get("media") is False or args.get("no_source") is True:
47
- cmd.append("--no-source")
48
- if args.get("set_active") is False:
49
- cmd.append("--no-use")
36
+ return {"ok": False, "error": "herm executable not found on PATH"}
50
37
 
51
38
  try:
52
- proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120, check=False)
39
+ proc = subprocess.run([binary, *parts], capture_output=True, text=True, timeout=timeout, check=False)
53
40
  except FileNotFoundError:
54
- return _json({"ok": False, "error": f"herm executable not found: {binary}"})
41
+ return {"ok": False, "error": f"herm executable not found: {binary}"}
55
42
  except subprocess.TimeoutExpired:
56
- return _json({"ok": False, "error": "herm eikon install timed out"})
43
+ return {"ok": False, "error": f"herm eikon {label} timed out"}
57
44
 
58
45
  stdout = (proc.stdout or "").strip()
59
46
  stderr = (proc.stderr or "").strip()
60
47
  if proc.returncode != 0:
61
48
  detail = stderr or stdout or f"exit {proc.returncode}"
62
- return _json({"ok": False, "error": f"herm eikon install failed: {detail}"})
49
+ return {"ok": False, "error": f"herm eikon {label} failed: {detail}"}
63
50
 
64
51
  line = next((ln for ln in reversed(stdout.splitlines()) if ln.strip()), "")
65
52
  try:
66
53
  payload = json.loads(line)
67
54
  except json.JSONDecodeError:
68
- return _json({"ok": False, "error": "herm eikon install returned non-JSON output", "output": stdout})
69
- return _json(payload)
55
+ return {"ok": False, "error": f"herm eikon {label} returned non-JSON output", "output": stdout}
56
+ if isinstance(payload, dict):
57
+ return payload
58
+ return {"ok": True, "result": payload}
59
+
60
+
61
+ def _name(args: dict[str, Any]) -> str:
62
+ return str(args.get("name") or "").strip()
63
+
64
+
65
+ def _handle_eikon_install(args: dict[str, Any], **_: Any) -> str:
66
+ source = str(args.get("source") or "").strip()
67
+ if not source:
68
+ return _json({"ok": False, "error": "source is required"})
69
+
70
+ cmd = ["eikon", "install", source, "--json"]
71
+ name = _name(args)
72
+ if name:
73
+ cmd.extend(["--name", name])
74
+ if args.get("media") is False or args.get("no_source") is True:
75
+ cmd.append("--no-source")
76
+ if args.get("active_ok") is True:
77
+ cmd.append("--active-ok")
78
+
79
+ payload = _run(cmd, "install")
80
+ if not payload.get("ok") or args.get("set_active") is False:
81
+ return _json(payload)
82
+
83
+ active = _run(["eikon", "use", str(payload.get("name") or source), "--json"], "use")
84
+ if not active.get("ok"):
85
+ return _json({"ok": False, "installed": payload, "error": f"installed but activation failed: {active.get('error', 'unknown error')}"})
86
+ return _json({**payload, "active": active.get("active")})
87
+
88
+
89
+ def _handle_eikon_search(args: dict[str, Any], **_: Any) -> str:
90
+ query = str(args.get("query") or "").strip()
91
+ cmd = ["eikon", "search"]
92
+ if query:
93
+ cmd.append(query)
94
+ return _json(_run([*cmd, "--json"], "search"))
95
+
96
+
97
+ def _handle_eikon_list(args: dict[str, Any], **_: Any) -> str:
98
+ return _json(_run(["eikon", "list", "--json"], "list"))
99
+
100
+
101
+ def _handle_eikon_use(args: dict[str, Any], **_: Any) -> str:
102
+ name = _name(args)
103
+ if not name:
104
+ return _json({"ok": False, "error": "name is required"})
105
+ return _json(_run(["eikon", "use", name, "--json"], "use"))
106
+
107
+
108
+ def _handle_eikon_update(args: dict[str, Any], **_: Any) -> str:
109
+ name = _name(args)
110
+ if not name:
111
+ return _json({"ok": False, "error": "name is required"})
112
+ cmd = ["eikon", "update", name, "--json"]
113
+ if args.get("active_ok") is True:
114
+ cmd.append("--active-ok")
115
+ return _json(_run(cmd, "update"))
116
+
117
+
118
+ def _handle_eikon_remove(args: dict[str, Any], **_: Any) -> str:
119
+ name = _name(args)
120
+ if not name:
121
+ return _json({"ok": False, "error": "name is required"})
122
+ cmd = ["eikon", "remove", name, "--json"]
123
+ if args.get("active_ok") is True:
124
+ cmd.append("--active-ok")
125
+ return _json(_run(cmd, "remove"))
@@ -1,15 +1,17 @@
1
1
  ---
2
2
  name: eikon
3
- description: Guide the user through making or editing a herm sidebar avatar (eikon) using herm's built-in Eikon Studio tab. The agent's role is advisory (what makes a good source, which knob to reach for); source generation is /eikon-create and all rasterize/bake happens in-app.
3
+ description: Use when helping a user create, edit, install, activate, update, remove, or share a Herm eikon through the built-in Eikon tab or the `herm eikon` CLI.
4
+ tags: [eikon, avatar, herm]
4
5
  related_skills: [eikon-create]
5
6
  ---
6
7
 
7
- # Building an eikon in herm
8
+ # Building and managing eikons in Herm
8
9
 
9
- An eikon is a 48×24 monochrome text avatar. It lives on disk as:
10
+ An eikon is a 48×24 monochrome text avatar. It lives in the active
11
+ Hermes profile, normally:
10
12
 
11
13
  ~/.hermes/eikons/<name>/
12
- <name>.eikon packed NDJSON — written by Studio on Ctrl+S
14
+ <name>.eikon packed NDJSON — written by Studio on Ctrl+S / Ctrl+U
13
15
  studio.json Studio's workspace state
14
16
  source/ base.<ext>, <state>.<ext>
15
17
 
@@ -17,32 +19,99 @@ You do **not** write `.eikon` or `studio.json`. Studio does.
17
19
 
18
20
  ## Where the user does the work
19
21
 
20
- Herm's built-in **Eikon** tab (Studio / Gallery). Tell the user: "open
21
- the Eikon tab" or "Ctrl+K → Eikon". In Studio:
22
+ Herm's built-in **Eikon** tab has **Library / Catalog / Studio**:
22
23
 
23
- - `eikon` row pick / New…
24
- - `source` row → Local file… / Generate image… / Generate video…
24
+ - **Library** local and bundled eikons; use, edit, update, share, delete,
25
+ and New/install entry points.
26
+ - **Catalog** — shared catalog; install runtime-only, install with source,
27
+ use installed eikons, download source, uninstall, or delist when eligible.
28
+ - **Studio** — edit an existing local/source draft eikon and bake it.
29
+
30
+ Tell the user: "open the Eikon tab"; `/library`, `/catalog`, and `/studio`
31
+ jump directly to the Eikon sub-tabs. In Studio:
32
+
33
+ - `eikon` row → pick an eikon, `+ New…`, or `+ Install…`
34
+ - `source` row → local/downloaded source; Generate rows appear only when the
35
+ corresponding Hermes generation backend is configured
25
36
  - `input` section → contrast / invert / flip (pixel-domain, shared)
26
- - `<rasterizer>` section → symbols / fill / dither (glyph-domain)
27
- - Preview pane → wheel pans, Ctrl+wheel zooms, Shift+wheel pans X
28
- - **Ctrl+S** bakes all six states and sets it active
37
+ - `<rasterizer>` section → rasterizer-specific glyph knobs, e.g. chafa
38
+ symbols/fill/dither or native symbols
39
+ - Preview pane wheel pans Y, Shift+wheel pans X, Ctrl+wheel zooms; drag the
40
+ pan bars to scrub pan
41
+ - **Ctrl+S** bakes all six states without changing the active avatar
42
+ - **Ctrl+U** bakes all six states and uses it as the active avatar
43
+ - `u` in Studio opens the Submit eikon dialog; `s` in Library opens Share to
44
+ catalog for local unpublished eikons
29
45
 
30
46
  ## What makes a good source
31
47
 
32
- One line, once: **48×24, one color. Light subject on black, high
33
- contrast, strong silhouette.** Fine detail disappears; outline is
34
- everything.
48
+ One line, once: **48×24, one color. Light subject on black, high contrast,
49
+ strong silhouette.** Fine detail disappears; outline is everything.
35
50
 
36
51
  ## When to do what
37
52
 
38
53
  | user says | you do |
39
54
  |---|---|
40
55
  | "make me an eikon of X" | Load `eikon-create` and follow it. |
41
- | drops an image path | `cp` it to `~/.hermes/eikons/<name>/source/base.<ext>` → "Eikon tab, pick <name>". |
42
- | "edit my <name> eikon" | "Eikon tab → `eikon` row → <name>." |
56
+ | drops an image path | Copy it to `${HERMES_HOME:-$HOME/.hermes}/eikons/<name>/source/base.<ext>` or use Library New… → local file, then Studio → `eikon` row → `<name>`. |
57
+ | "edit my <name> eikon" | "Eikon Studio → `eikon` row → `<name>`." |
58
+ | "install/shared catalog eikon" | "Eikon → Catalog" or `/catalog`. Choose Eikon only or Eikon + Source, then Use when ready. |
59
+ | "install from GitHub / URL / dir" | "Studio → `eikon` row → `+ Install…`", "Library → New… → inspect/install", or `herm eikon install <name|url|dir>`. |
43
60
  | "too dark / washed out" | "invert toggle, then contrast slider — under `input`." |
44
- | "off-center / too small" | "Ctrl+wheel to zoom, wheel/drag to pan on the preview." |
45
- | "make it move" | `eikon-create` §5 (video), or Studio's `source` → Generate video…. |
61
+ | "off-center / too small" | "Ctrl+wheel zooms; wheel/Shift+wheel pans; drag pan bars for precise pan." |
62
+ | "make it move" | `eikon-create` §5 (video), or Studio's `source` row → Generate video if available. |
63
+ | "publish/share my eikon" | "After baking, press `u` in Studio or `s` in Library. Review metadata, bundle, and GitHub PR target before submitting." |
64
+ | "remove from official catalog" | Use Catalog → Delist from Registry when eligible, or `herm eikon delist <name|id>`. |
65
+
66
+ ## Install and manage shared eikons
67
+
68
+ For catalog eikons, use **Eikon → Catalog**. Rows show source,
69
+ compatibility, trust (`Verified`, `Unverified`, or `Mismatch`), digest,
70
+ and installed/active state. Catalog installs fetch built package artifacts;
71
+ they do not clone creator repos.
72
+
73
+ Default install is **Eikon only**: runtime package, no activation, no editable
74
+ source. Choose **Eikon + Source**, or later **Download Source**, when the user
75
+ wants to edit it in Studio.
76
+
77
+ For direct sharing, use `github.com/user/repo/eikon-name` for a multi-eikon
78
+ GitHub catalog repo, `github.com/user/repo` for a single-package repo, a git or
79
+ HTTP URL, or a local package directory. Private GitHub repos use normal git
80
+ authentication.
81
+
82
+ The local lifecycle is explicit:
83
+
84
+ ```bash
85
+ herm eikon search [query] [--json]
86
+ herm eikon browse [query] [--json]
87
+ herm eikon inspect <name|url|dir> [--json]
88
+ herm eikon install <name|url|dir> [--name N] [--no-source] [--active-ok] [--json]
89
+ herm eikon list [--json]
90
+ herm eikon use <name> [--json]
91
+ herm eikon info <name> [--json]
92
+ herm eikon update <name> [--active-ok] [--json]
93
+ herm eikon remove <name> [--active-ok] [--json]
94
+ herm eikon delist <name|id> [--json]
95
+ ```
96
+
97
+ `install` does not activate. If the installed name is currently active,
98
+ installing replaces the active avatar's backing package and requires
99
+ `--active-ok`. `use` activates. Updating or removing the active eikon also
100
+ requires `--active-ok`.
101
+
102
+ Creators share through normal GitHub repositories. For official registry
103
+ listing, use `u` in Studio or Library → Share to catalog after baking: Herm
104
+ runs registry preflight, asks for title/author/description/glyph, previews the
105
+ public bundle, then creates a GitHub PR through local `gh` auth or gives manual
106
+ PR instructions.
107
+ Use upstream `eikon pack`, `eikon index`, and `eikon manifest` for
108
+ direct-install repos. `eikon publish` is the lower-level GitHub PR contribution
109
+ helper for the configured/default catalog repo, not a hosted account, upload,
110
+ dashboard, or moderation flow.
111
+
112
+ Browser catalog surfaces such as `eikon.liftaris.dev` are discovery-only: they
113
+ preview catalog entries and give copyable `herm eikon install …` instructions.
114
+ Do not imply a browser click installs, activates, authenticates, or mutates Herm.
46
115
 
47
116
  ## Quick poster
48
117
 
@@ -52,10 +121,14 @@ To show a candidate in chat without Studio:
52
121
  chafa --size=48x24 --symbols=braille --colors=none --format=symbols --stretch "<path>"
53
122
  ```
54
123
 
55
- Preview-only; Studio's output will differ (it tone-maps first).
124
+ Preview-only; Studio's output will differ because it tone-maps first.
56
125
 
57
126
  ## Don'ts
58
127
 
59
128
  - Don't hand-write `.eikon` NDJSON.
60
129
  - Don't pick knob values for the user. Name the knob.
61
130
  - Don't repeat the 48×24 brief.
131
+ - Don't say install activates. Install only writes the local package; Use
132
+ activates.
133
+ - Don't describe hosted marketplace accounts, upload tokens, dashboards, or
134
+ browser-native install flows for the v1 registry.