herm-tui 1.9.0-dev.16 → 1.9.0-dev.17

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,15 +93,49 @@ See [`.env.example`](./.env.example) for rarely-needed overrides.
93
93
 
94
94
  ### Share and install eikons
95
95
 
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
+
96
101
  - Open Eikon → Marketplace, or run `/marketplace`, to browse shared catalog
97
102
  entries.
98
- - Preview the selected eikon, install without activating, then use it when
99
- ready.
100
- - Use `eikon.liftaris.dev` as a discovery gallery only; it previews catalog
101
- entries and gives copyable Herm install instructions.
102
- - Submit local non-bundled eikons from Studio with `u`; Herm shows the exact
103
- preflight bundle before submission and blocks published marketplace installs
104
- from duplicate submission.
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
+ - Update or remove an active eikon only after confirming that the active
108
+ avatar's backing package will change or be cleared.
109
+
110
+ From the shell:
111
+
112
+ ```bash
113
+ herm eikon search [query]
114
+ herm eikon inspect <name|github.com/user/repo/eikon-name|dir>
115
+ herm eikon install <name|github.com/user/repo/eikon-name|dir>
116
+ herm eikon use <name>
117
+ herm eikon info <name>
118
+ herm eikon update <name> --active-ok
119
+ herm eikon remove <name> --active-ok
120
+ ```
121
+
122
+ `install` never activates. `use` is the activation action. JSON output is
123
+ available for automation with `--json`.
124
+
125
+ Default Marketplace installs fetch built package artifacts referenced by the
126
+ catalog, not creator repositories. Direct GitHub installs are for sharing
127
+ outside the default catalog and support both single-package repos and
128
+ multi-eikon catalog repos addressed as `github.com/user/repo/eikon-name`.
129
+ Private GitHub repos use normal git authentication.
130
+
131
+ Creators can share eikons through normal GitHub repositories. Use upstream
132
+ `eikon pack`, `eikon index`, and `eikon manifest` to prepare single-package or
133
+ multi-eikon repos for direct installs. `eikon publish` remains a GitHub PR
134
+ contribution helper for the configured/default catalog repo; it is not a
135
+ hosted marketplace account, upload, dashboard, or moderation flow.
136
+
137
+ Use `eikon.liftaris.dev` as a discovery gallery only; it previews catalog
138
+ entries and gives copyable Herm install instructions.
105
139
 
106
140
  Herm owns native Marketplace behavior. The eikon repo owns the registry,
107
141
  browser mirror, shared catalog/player exports, install resolver, and publish
@@ -0,0 +1,120 @@
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": "blobs/sha256/b2685743b05ddf201920b69db58640d4fab644e637fa7d313c6dfa215fa41fb7"
47
+ },
48
+ "files": [
49
+ {
50
+ "path": "blobs/sha256/b2685743b05ddf201920b69db58640d4fab644e637fa7d313c6dfa215fa41fb7",
51
+ "role": "runtime",
52
+ "mediaType": "application/vnd.eikon.stream+jsonl",
53
+ "size": 1401439,
54
+ "digest": "sha256:b2685743b05ddf201920b69db58640d4fab644e637fa7d313c6dfa215fa41fb7"
55
+ },
56
+ {
57
+ "path": "blobs/sha256/8c4dc411ca159253cc90fa31f5263799054592e6eaeb4e5c99881c8cac29e67b",
58
+ "role": "source.base",
59
+ "mediaType": "image/png",
60
+ "size": 98644,
61
+ "digest": "sha256:8c4dc411ca159253cc90fa31f5263799054592e6eaeb4e5c99881c8cac29e67b"
62
+ },
63
+ {
64
+ "path": "blobs/sha256/481c010826d7cacb9c419455bd0f73d020f66492a1236b37e4e53353b7965902",
65
+ "role": "source.clip",
66
+ "mediaType": "video/mp4",
67
+ "size": 105789,
68
+ "digest": "sha256:481c010826d7cacb9c419455bd0f73d020f66492a1236b37e4e53353b7965902",
69
+ "signal": "state.idle"
70
+ },
71
+ {
72
+ "path": "blobs/sha256/94f0f4f710c708f516128125142d5dce701f83558e9fad444c1bb376ee3c1fea",
73
+ "role": "source.clip",
74
+ "mediaType": "video/mp4",
75
+ "size": 275110,
76
+ "digest": "sha256:94f0f4f710c708f516128125142d5dce701f83558e9fad444c1bb376ee3c1fea",
77
+ "signal": "state.listening"
78
+ },
79
+ {
80
+ "path": "blobs/sha256/5ae0eb2e1889c72bed69144998acc6f80b9d11b67beffccd972842a9d6ed0f24",
81
+ "role": "source.clip",
82
+ "mediaType": "video/mp4",
83
+ "size": 174520,
84
+ "digest": "sha256:5ae0eb2e1889c72bed69144998acc6f80b9d11b67beffccd972842a9d6ed0f24",
85
+ "signal": "state.thinking"
86
+ },
87
+ {
88
+ "path": "blobs/sha256/5c15fd3db3fba93b378eb5b23ab9ecf283e8fb5a08828f1393e91b323b65eab2",
89
+ "role": "source.clip",
90
+ "mediaType": "video/mp4",
91
+ "size": 96790,
92
+ "digest": "sha256:5c15fd3db3fba93b378eb5b23ab9ecf283e8fb5a08828f1393e91b323b65eab2",
93
+ "signal": "state.speaking"
94
+ },
95
+ {
96
+ "path": "blobs/sha256/f63a864e84c4b4f672d6b02a4bcf610b2881fcf64166273b29a642fb76e00f09",
97
+ "role": "source.clip",
98
+ "mediaType": "video/mp4",
99
+ "size": 160072,
100
+ "digest": "sha256:f63a864e84c4b4f672d6b02a4bcf610b2881fcf64166273b29a642fb76e00f09",
101
+ "signal": "state.working"
102
+ },
103
+ {
104
+ "path": "blobs/sha256/5d1a1fa59ec9091d9c03d99728b15287ef58d397ea2c787a65c55519482683c8",
105
+ "role": "source.clip",
106
+ "mediaType": "video/mp4",
107
+ "size": 168804,
108
+ "digest": "sha256:5d1a1fa59ec9091d9c03d99728b15287ef58d397ea2c787a65c55519482683c8",
109
+ "signal": "state.error"
110
+ }
111
+ ],
112
+ "origin": {
113
+ "kind": "default-catalog",
114
+ "source": "https://eikon.liftaris.dev/packages/liftaris/nous/1.0.0.json",
115
+ "sourceKey": "registry:eikon.liftaris.dev:liftaris/nous@1.0.0",
116
+ "identityKey": "registry:eikon.liftaris.dev:liftaris/nous@1.0.0",
117
+ "packageUrl": "https://eikon.liftaris.dev/packages/liftaris/nous/1.0.0.json",
118
+ "trust": "verified"
119
+ }
120
+ }
@@ -1,4 +1,4 @@
1
- {"type":"header","eikon":1,"id":"liftaris/default","version":"1.0","title":"Nous","author":{"name":"kaio"},"size":{"cols":48,"rows":24},"defaultSignal":"state.idle","signals":{"state.idle":{"clip":"idle"},"state.listening":{"clip":"listening","fallback":"state.idle"},"state.thinking":{"clip":"thinking","fallback":"state.idle"},"state.speaking":{"clip":"speaking","fallback":"state.idle"},"state.working":{"clip":"working","fallback":"state.idle"},"state.error":{"clip":"error","fallback":"state.idle"}}}
1
+ {"type":"header","eikon":1,"id":"liftaris/nous","version":"1.0.0","title":"nous","author":{"name":"kaio"},"size":{"cols":48,"rows":24},"defaultSignal":"state.idle","signals":{"state.idle":{"clip":"idle"},"state.listening":{"clip":"listening","fallback":"state.idle"},"state.thinking":{"clip":"thinking","fallback":"state.idle"},"state.speaking":{"clip":"speaking","fallback":"state.idle"},"state.working":{"clip":"working","fallback":"state.idle"},"state.error":{"clip":"error","fallback":"state.idle"}}}
2
2
  {"type":"clip","name":"idle","fps":16,"frameCount":64,"loopFrom":0}
3
3
  {"type":"frame","clip":"idle","index":0,"rows":["⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣠⣤⣤⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⣤⣬⣥⣄⣀⠀⠉⠙⠻⣿⣿⣿⣿⣷⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣷⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⢀⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⡿⢦⡀⠀⠈⢿⣿⣿⣿⣿⣿⢿⡗⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⡰⣻⣿⣿⣿⢏⣿⣿⣿⣿⣿⣿⣿⡿⠙⠏⠀⠀⣮⡳⠀⠀⠈⢿⢿⠻⠿⡏⠁⠁⣸⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠌⠈⠉⢺⠻⢃⠊⠙⠛⢠⡀⢀⠙⠁⠁⠀⡘⣶⣷⣿⣷⣾⠀⠀⠈⣶⣆⣳⣶⣮⢔⣿⣿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⡼⣴⢶⣤⣶⣾⣼⣿⣿⣿⣿⢿⣿⣿⣿⣾⣶⣿⣿⣿⣿⣿⣿⡇⣀⣠⣸⣿⣷⣻⣿⣿⣿⣿⣷⡀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⢰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢯⣿⣚⣿⣿⣿⢿⣿⣿⣿⣿⣗⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⡿⠧⠻⠟⣻⠟⡓⠈⠛⠛⠛⠛⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣹⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠻⣿⡿⣿⣿⣉⣁⠀⠀⠀⠀⠸⢻⣿⡷⢦⡄⠀⠀⠀⢹⣿⣿⣿⣿⢟⣛⡻⣿⣿⣿⣯⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠈⠙⠊⢻⣏⢿⠇⠀⠀⠀⠀⠬⠿⠥⠊⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡼⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣸⣯⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⢿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣿⡇⠠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣿⣇⠰⣄⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣿⣿⣿⣿⣿⡀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⡆⠣⠄⠈⠁⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣼⣿⣿⢹⣿⣇⠀⠀⠀⠀","⠀⠀⢠⠀⠀⠀⠀⢸⣿⣿⣿⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⡏⢈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠄⣿⣿⡀⠀⠀⠀","⠀⠀⣟⠀⠀⠀⠀⣿⣿⣿⣿⣿⣦⣀⣀⣀⣀⣠⣄⡀⠀⠀⢸⣿⣿⣿⡾⠟⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⣹⣿⡇⠐⡄⠀","⠀⢀⣿⣦⣀⣠⣼⣿⣿⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⣸⣿⣿⣯⡀⣀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢀⣿⣿⡇⠀⣷⠀","⠀⠘⣜⠿⣿⣿⠿⠋⣱⣿⣿⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⡾⠋⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣣⣾⣿⣿⡇⢠⣿⠀","⠀⠀⠙⠿⣶⣤⣶⣾⣿⣿⢋⣿⣿⣿⣿⣿⣿⣿⣿⠛⢋⡠⢆⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢠⣿⠏⠀","⠀⠀⠀⠀⠀⠉⢯⣉⣉⣡⣾⣿⣿⣿⣿⣿⣿⣿⣿⣧⡀⢋⠝⠃⣀⣴⠟⠫⢛⣭⡿⠚⠋⠉⠁⠒⠫⣝⠻⣿⣿⣿⢟⣵⡿⠋⠀⠀","⠀⠀⠀⠀⠀⠀⠈⠛⠿⠿⠛⢁⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡶⠟⠉⠀⢀⣴⡟⠁⠀⠀⠀⠀⠀⠀⠀⠈⠳⣜⢿⡿⠟⠋⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⠿⣿⢯⡾⠋⠀⠀⢀⡔⠀⡾⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠮⠅⠀⠀⠀⠀⠀⠀"]}
4
4
  {"type":"frame","clip":"idle","index":1,"rows":["⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣀⣠⣠⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣤⣥⣤⣀⠀⠉⠉⠻⢿⣿⣿⣿⣷⣶⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣄⠀⠀⠙⢿⣿⣿⣿⣿⣿⣷⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⡿⢷⡀⠀⠈⢻⣿⣿⣿⣿⣿⣿⣧⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⡠⣫⡿⣿⣿⢟⣾⣿⡟⣿⣿⣿⣿⣿⠎⠟⠀⠀⢴⡻⠄⠀⠀⢻⣿⠿⢿⣿⠃⠉⢠⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠔⠄⠀⠙⠳⠋⠎⠉⠚⢁⣄⣀⢸⠉⠁⠀⡈⢶⣾⣿⣷⣾⡀⠀⠀⢶⣦⡴⣦⣶⣂⣦⢿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⣸⣵⣦⣧⣶⣾⣿⣿⣿⣿⣿⣻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇⢀⣀⡸⣿⣿⣻⣿⣷⢾⣿⣿⡀⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⢠⣷⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣏⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣌⣿⣰⢿⣿⣷⢿⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⡜⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⣿⣿⣿⣿⣿⡃⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⠿⠇⠙⠛⡻⢟⣋⠉⠛⠛⠛⠛⠛⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣹⣿⣿⣿⣿⣷⠀⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠙⢿⣟⢿⣿⣁⣐⠀⠀⠀⠀⠘⢻⣿⡷⢦⡄⠀⠀⠀⣸⣿⣿⣿⣿⠟⠛⠻⣿⣿⣿⣯⣿⣿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠉⠃⢹⣏⢻⠇⠀⠀⠀⠀⠨⠽⠥⠊⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣹⣿⣿⣿⣿⣇⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⢸⣏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⢿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣼⠇⠠⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡾⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⣿⣇⠸⣄⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣏⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⢰⣿⣿⡆⠫⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⣿⣿⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢻⣿⡇⠀⠀⠀⠀","⠀⠀⡠⠀⠀⠀⠀⣸⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⣿⣿⣿⠃⢘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠈⣿⣿⠀⠀⠀⠀","⠀⢠⣧⠀⠀⠀⢀⣿⣿⣿⣿⣿⣦⣀⣀⣀⣀⣠⣄⡀⠀⠀⣿⣿⣿⣯⡶⠟⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠁⢹⣿⡇⢠⠀⠀","⠀⠈⣿⣶⣤⣤⣾⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣄⠀⣿⣿⣿⣏⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢀⣿⣿⡇⠀⣧⠀","⠀⠘⣌⡻⠿⠿⠟⢋⣼⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡾⠛⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣣⣾⣿⣿⡇⢰⣿⠀","⠀⠀⠘⠻⣷⣶⣾⣿⣿⡿⢳⣿⣿⣿⣿⣿⣿⣿⣿⠛⢋⡠⢆⠈⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢠⣿⠇⠀","⠀⠀⠀⠀⠀⠈⣯⣉⣁⣴⣿⡿⣿⣿⣿⣿⣿⣿⣿⣧⡀⢋⠝⠃⣀⣴⠟⠻⢛⣭⡿⠚⠋⠉⠁⠒⠫⣙⠻⣿⣿⣿⡟⣱⣿⠋⠀⠀","⠀⠀⠀⠀⠀⠀⠈⠛⠛⠛⠋⣰⣿⣿⣿⣿⣿⣿⣿⣿⣷⣤⡶⠟⠉⠀⢀⣴⠟⠁⠀⠀⠀⠀⠀⠀⠀⠈⠳⣜⢿⣿⠿⠋⠁⠀⠀⠀","⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⣿⣿⣿⠿⣿⢯⡾⠋⠀⠀⢀⡔⢀⡾⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠺⠅⠀⠀⠀⠀⠀⠀"]}
@@ -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"))
@@ -9,7 +9,7 @@ related_skills: [eikon-create]
9
9
  An eikon is a 48×24 monochrome text avatar. It lives on disk as:
10
10
 
11
11
  ~/.hermes/eikons/<name>/
12
- <name>.eikon packed NDJSON — written by Studio on Ctrl+S
12
+ <name>.eikon packed NDJSON — written by Studio on Ctrl+S / Ctrl+U
13
13
  studio.json Studio's workspace state
14
14
  source/ base.<ext>, <state>.<ext>
15
15
 
@@ -25,7 +25,8 @@ Herm's built-in **Eikon** tab (Gallery / Studio / Marketplace). Tell the user:
25
25
  - `input` section → contrast / invert / flip (pixel-domain, shared)
26
26
  - `<rasterizer>` section → symbols / fill / dither (glyph-domain)
27
27
  - Preview pane → wheel pans, Ctrl+wheel zooms, Shift+wheel pans X
28
- - **Ctrl+S** bakes all six states and sets it active
28
+ - **Ctrl+S** bakes all six states without changing the active avatar
29
+ - **Ctrl+U** bakes all six states and uses it as the active avatar
29
30
 
30
31
  ## What makes a good source
31
32
 
@@ -40,11 +41,44 @@ everything.
40
41
  | "make me an eikon of X" | Load `eikon-create` and follow it. |
41
42
  | drops an image path | `cp` it to `~/.hermes/eikons/<name>/source/base.<ext>` → "Eikon tab, pick <name>". |
42
43
  | "edit my <name> eikon" | "Eikon tab → `eikon` row → <name>." |
43
- | "install/shared marketplace eikon" | "Eikon tab → Marketplace", or `/marketplace`. |
44
+ | "install/shared marketplace eikon" | "Eikon tab → Marketplace", or `/marketplace`. Install, then Use when ready. |
45
+ | "install from GitHub" | "Eikon tab → New… → inspect/install", or `herm eikon install github.com/user/repo/eikon-name`. |
44
46
  | "too dark / washed out" | "invert toggle, then contrast slider — under `input`." |
45
47
  | "off-center / too small" | "Ctrl+wheel to zoom, wheel/drag to pan on the preview." |
46
48
  | "make it move" | `eikon-create` §5 (video), or Studio's `source` → Generate video…. |
47
49
 
50
+ ## Install and manage shared eikons
51
+
52
+ For catalog eikons, use **Eikon → Marketplace**. Rows show source,
53
+ compatibility, and trust (`Verified`, `Unverified`, or `Mismatch`).
54
+ Marketplace installs fetch built package artifacts and do not clone creator
55
+ repos.
56
+
57
+ For direct sharing, use `github.com/user/repo/eikon-name` for a multi-eikon
58
+ GitHub catalog repo, `github.com/user/repo` for a single-package repo, or a
59
+ local package directory. Private GitHub repos use normal git authentication.
60
+
61
+ The local lifecycle is explicit:
62
+
63
+ ```bash
64
+ herm eikon search [query]
65
+ herm eikon inspect <name|github.com/user/repo/eikon-name|dir>
66
+ herm eikon install <name|github.com/user/repo/eikon-name|dir>
67
+ herm eikon use <name>
68
+ herm eikon info <name>
69
+ herm eikon update <name> --active-ok
70
+ herm eikon remove <name> --active-ok
71
+ ```
72
+
73
+ `install` never activates. `use` activates. Updating or removing the active
74
+ eikon requires explicit acknowledgement because it changes or clears the active
75
+ avatar.
76
+
77
+ Creators share through normal GitHub repositories. Use upstream `eikon pack`,
78
+ `eikon index`, and `eikon manifest` to prepare repo artifacts. `eikon publish`
79
+ is a GitHub PR contribution helper for the configured/default catalog repo, not
80
+ a hosted marketplace account or upload flow.
81
+
48
82
  ## Quick poster
49
83
 
50
84
  To show a candidate in chat without Studio:
@@ -16,6 +16,8 @@ deliverable is one or more images/videos under
16
16
  named so Studio resolves them: `base.*` for the default pose, `<state>.*`
17
17
  for per-state overrides (`idle listening thinking speaking working
18
18
  error`). Extensions: `png jpg jpeg webp gif bmp mp4 webm mov mkv`.
19
+ Studio bakes with **Ctrl+S** without activation; **Ctrl+U** bakes and uses the
20
+ eikon as the active avatar.
19
21
 
20
22
  ## What survives rasterization
21
23
 
@@ -140,7 +142,8 @@ Adopt as `<state>.mp4` (or `base.mp4` for an animated idle).
140
142
  Once source files are in place:
141
143
 
142
144
  > Open the **Eikon** tab → `eikon` row → **<name>**. Tune zoom /
143
- > contrast / invert / symbols there, then **Ctrl+S** to bake.
145
+ > contrast / invert / symbols there, then **Ctrl+S** to bake or **Ctrl+U**
146
+ > to bake and use.
144
147
 
145
148
  Stop there. Studio writes `<name>.eikon` and `studio.json`.
146
149