@zerwiz/ymir 0.1.7 → 0.1.8

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/bin/ymir.js CHANGED
@@ -1,19 +1,32 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * ymir — the CLI the npm package publishes.
3
+ * ymir — the CLI the npm package publishes, and the operator's front door.
4
4
  *
5
5
  * It is a thin, honest wrapper: the real work lives in the shell scripts under
6
- * bin/, which are the same scripts a git clone runs. This file exists because
7
- * npm needs a Node entry point (`bin: ymir`) and because a package user should
8
- * not have to know where the checkout landed.
6
+ * bin/ and scripts/ the same scripts a git clone runs. This file exists
7
+ * because npm needs a Node entry point (`bin: ymir`) and because an operator
8
+ * should never have to know where the package landed.
9
9
  *
10
- * ymir # the same as `ymir install`
11
- * ymir install [--yes] # first setup (idempotent, self-healing)
12
- * ymir sense # what THIS machine is
13
- * ymir validate # prove what stands
14
- * ymir migrate # heal an older home forward
10
+ * The doors are named for the figure whose work they do (the naming law):
11
+ * Eir heals, Gróa renews, Heimdall keeps the way in, Smíðja is the smithy and
12
+ * its board, Hlidskjalf the high seat, Sessrúmnir the seat-hall, Mímir the well.
13
+ *
14
+ * ymir first setup (the same as `ymir install`)
15
+ * ymir install [...] first setup, idempotent and self-healing
16
+ * ymir raise / lower lift the hall, or lay it down
17
+ * ymir eir what stands, and mend what does not
18
+ * ymir groa take the latest, and mend this home forward
19
+ * ymir heimdall the way in: your credential, and invites
20
+ * ymir smidja the smithy's board (:8437) — build · start · stop · status
21
+ * ymir hlidskjalf the high seat's window
22
+ * ymir sessrumnir the seat-hall's window
23
+ * ymir mimir the memory well
24
+ * ymir sense what THIS machine is
15
25
  * ymir --version
16
26
  *
27
+ * Colour appears only where a human is watching (a TTY, no NO_COLOR); the data
28
+ * on stdout is always plain TOON.
29
+ *
17
30
  * Exit codes follow the scripts: 0 ok, 1 error, 2 usage.
18
31
  */
19
32
  'use strict';
@@ -27,32 +40,65 @@ const pkg = (() => {
27
40
  try { return require(path.join(ROOT, 'package.json')); } catch { return { version: '0.0.0' }; }
28
41
  })();
29
42
 
30
- const TASKS = {
31
- install: 'ymir-install.sh',
32
- validate: 'ymir-validate.sh',
33
- migrate: 'ymir-migrate.sh',
34
- sense: 'host-sense.sh',
43
+ // ── the cloth (the same palette as bin/ymir-style.sh, cut from the tokens) ───
44
+ const colour = process.stderr.isTTY && !process.env.NO_COLOR && process.env.TERM !== 'dumb';
45
+ const wrap = (code) => (s) => (colour ? `\u001b[${code}m${s}\u001b[0m` : `${s}`);
46
+ const bone = wrap('38;2;207;195;169');
47
+ const bronze = wrap('38;2;201;151;79');
48
+ const faint = wrap('38;2;107;98;80');
49
+ const blood = wrap('38;2;194;88;74');
50
+ const bold = wrap('1');
51
+
52
+ // ── the doors ────────────────────────────────────────────────────────────────
53
+ // verb → { script, args } — args are prepended to whatever the operator passes,
54
+ // so a verb can be a doorway to a sub-verb of a script that has several.
55
+ const DOORS = {
56
+ install: { script: 'bin/ymir-install.sh', about: 'first setup (idempotent, self-healing)' },
57
+ raise: { script: 'scripts/start.sh', about: 'lift the hall — SPA, gate API, Nornir, bridges, the board' },
58
+ lower: { script: 'scripts/stop.sh', about: 'lay the hall down' },
59
+ eir: { script: 'bin/eir-doctor.sh', about: 'diagnose every surface; mend what is broken' },
60
+ groa: { script: 'bin/groa-update.sh', about: 'take the latest, then mend this home forward' },
61
+ heimdall: { script: 'bin/ymir-setup-auth.sh', about: 'the way in — your credential (status · set · github)' },
62
+ invite: { script: 'bin/ymir-invite.sh', about: 'let someone else in (mint · list · revoke)' },
63
+ smidja: { script: 'bin/smidja-board.sh', about: "the smithy's board on :8437 (build · start · stop · status)" },
64
+ hlidskjalf: { script: 'scripts/electron.sh', about: "the high seat's window", args: ['start', '--view', 'hlidskjalf'] },
65
+ sessrumnir: { script: 'scripts/electron.sh', about: "the seat-hall's window", args: ['start', '--view', 'sessrumnir'] },
66
+ mimir: { script: 'bin/mimir.sh', about: 'the memory well' },
67
+ sense: { script: 'bin/host-sense.sh', about: 'what THIS machine is' },
68
+ plan: { script: 'bin/ymir-plan.sh', about: 'what an install would do here — writes nothing' },
69
+ migrate: { script: 'bin/ymir-migrate.sh', about: "heal this home's structure forward (Gr\u00f3a's mend)" },
70
+ validate: { script: 'bin/ymir-validate.sh', about: 'alias of `ymir eir`' },
35
71
  };
36
72
 
73
+ // Names the law has not given a home are kept for a while, so a muscle memory
74
+ // built yesterday still works — and says what to type instead.
75
+ const RENAMED = { validate: 'eir', auth: 'heimdall', desktop: 'hlidskjalf', doctor: 'eir', update: 'groa' };
76
+
37
77
  function usage() {
38
- process.stdout.write(
39
- `ymir ${pkg.version} — the single-tenant agent operating system\n\n` +
40
- 'Usage:\n' +
41
- ' ymir first setup (same as `ymir install`)\n' +
42
- ' ymir install [--yes] first setup, idempotent and self-healing\n' +
43
- ' ymir sense report THIS machine (distro, session, desktop, capabilities)\n' +
44
- ' ymir validate verify the running system\n' +
45
- ' ymir migrate heal an older home forward\n' +
46
- ' ymir --version\n\n' +
47
- 'Output is TOON. The scripts are the source of truth; this CLI only points at them.\n'
48
- );
78
+ const lines = [];
79
+ lines.push('');
80
+ lines.push(` ${bronze('\u16c9')} ${bold('Ymir')} ${faint(pkg.version)}`);
81
+ lines.push(` ${faint('the single-tenant agent operating system')}`);
82
+ lines.push('');
83
+ lines.push(bold(' The doors'));
84
+ for (const [verb, d] of Object.entries(DOORS)) {
85
+ lines.push(` ${bronze(verb.padEnd(11))} ${faint(d.about)}`);
86
+ }
87
+ lines.push('');
88
+ lines.push(bold(' Composed by hand'));
89
+ lines.push(` ${bronze('ymir groa migrate'.padEnd(11))} ${faint("heal this home's structure (run by groa / install)")}`);
90
+ lines.push(` ${bronze('ymir heimdall set'.padEnd(11))} ${faint('set the operator password (or `github`)')}`);
91
+ lines.push('');
92
+ lines.push(faint(' A bare `ymir` runs the first setup. Output is TOON; colour only on a TTY.'));
93
+ lines.push('');
94
+ process.stdout.write(lines.join('\n') + '\n');
49
95
  }
50
96
 
51
97
  function run(script, args) {
52
- const file = path.join(ROOT, 'bin', script);
98
+ const file = path.join(ROOT, script);
53
99
  if (!fs.existsSync(file)) {
54
- process.stderr.write(`error: ${path.relative(ROOT, file)} is missing from this install\n`);
55
- process.stderr.write('help: reinstall — npm i -g @zerwiz/ymir, or git clone the distro\n');
100
+ process.stderr.write(`${blood('error:')} ${script} is missing from this install\n`);
101
+ process.stderr.write(`${faint('help: reinstall — npm i -g @zerwiz/ymir, or git clone the distro')}\n`);
56
102
  process.exit(1);
57
103
  }
58
104
  const r = spawnSync('bash', [file, ...args], { stdio: 'inherit' });
@@ -62,17 +108,19 @@ function run(script, args) {
62
108
  const argv = process.argv.slice(2);
63
109
  const first = argv[0];
64
110
 
65
- if (!first) run(TASKS.install, []);
111
+ if (!first) run(DOORS.install.script, []);
66
112
  if (first === '--version' || first === '-v' || first === '-V') {
67
113
  process.stdout.write(`${pkg.version}\n`);
68
114
  process.exit(0);
69
115
  }
70
116
  if (first === '--help' || first === '-h' || first === 'help') { usage(); process.exit(0); }
71
117
 
72
- const script = TASKS[first];
73
- if (!script) {
74
- process.stderr.write(`error: unknown command ${first}\n`);
75
- process.stderr.write('help: ymir [install|sense|validate|migrate|--version]\n');
118
+ let door = DOORS[first];
119
+ if (!door) {
120
+ const replacement = RENAMED[first];
121
+ process.stderr.write(`${blood('error:')} unknown door ${bold(first)}\n`);
122
+ if (replacement) process.stderr.write(`${faint(`help: the door is named \`ymir ${replacement}\` now (the naming law — a figure does the work)`)}\n`);
123
+ process.stderr.write(`${faint('help: `ymir --help` lists every door')}\n`);
76
124
  process.exit(2);
77
125
  }
78
- run(script, argv.slice(1));
126
+ run(door.script, [...(door.args || []), ...argv.slice(1)]);
package/docs/design.md CHANGED
@@ -226,7 +226,7 @@ everywhere; never put bronze text on bronze.
226
226
  - **Runecoded, not emoji.** A minimal, hand-drawn-style rune set replaces emoji
227
227
  in all UI: 16×16 and 24×24 grid, 2px stroke, chisel bevel on primary.
228
228
  - System subsystems each carry one rune-glyph (reuse Algiz-family geometry:
229
- Bifrost gate, Ratatoskr squirrel, the Well). Map is in `midgard/design-system/icons.md` (TBD) created from this doc.
229
+ Bifrost gate, Ratatoskr squirrel, the Well). Map is in `midgard/design-system/icons.md` and the library in `midgard/design-system/runes.md`.
230
230
  - Status dot: filled rune ring (ok) / half-ring (warn) / broken ring (danger), never color-only.
231
231
 
232
232
  ### 4.5 Surface & texture
@@ -345,7 +345,7 @@ All components read tokens from `tokens.css`; color-only states are forbidden
345
345
 
346
346
  - `docs/design.md` — this document (master).
347
347
  - `midgard/design-system/tokens.css` — the consumable design tokens.
348
- - `midgard/design-system/icons.md` — rune-glyph map (to create).
348
+ - `midgard/design-system/icons.md` — rune-glyph map (21 glyphs, drawn in `icons/*.svg`).
349
349
  - `docs/ymir-rut.md` Part 3 — the original Rut design-system specification this
350
350
  document implements.
351
351
  - `docs/lore.md` §VI — house lineage behind the house accents.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerwiz/ymir",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Ymir \u2014 the single-tenant agent operating system. Omarchy-first, host-aware, self-healing.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/zerwiz/ymir",
package/scripts/start.sh CHANGED
@@ -17,6 +17,18 @@ if [ -z "${YMIR_PLATFORM_LOADED:-}" ]; then
17
17
  fi
18
18
 
19
19
  ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
20
+ # Where the smithy's parts live: apps/smidja-factory in a clone, or the
21
+ # @zerwiz/smidja-factory package in an npm install (bin/smidja-lib.sh).
22
+ if [ -z "${YMIR_SMIDJA_LIB_LOADED:-}" ]; then
23
+ _ys="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ for _yc in "$_ys/smidja-lib.sh" "$(dirname "$_ys")/bin/smidja-lib.sh"; do
25
+ [ -r "$_yc" ] && { . "$_yc"; YMIR_SMIDJA_LIB_LOADED=1; break; }
26
+ done
27
+ unset _ys _yc
28
+ fi
29
+ smidja_visualizer_dir SMIDJA_VIZ
30
+ smidja_factory_dir SMIDJA_FACTORY
31
+
20
32
 
21
33
  # The operator's settings and secrets live in the home they chose, never in the
22
34
  # code tree — a packaged install replaces its tree on upgrade, and a credential
@@ -116,7 +128,7 @@ fi
116
128
  # Smíðja's eye — the Vue trace visualizer (API :8437, UI :8438). Reads the repo's
117
129
  # own smidja.db and exposes the Sessions/Trace/Decisions/Stats views behind the
118
130
  # Hlidskjalf Sessions gate's "Open visualizer" button.
119
- VIZ_DIR="$ROOT/.agents/skills/smidja-factory/apps/visualizer"
131
+ VIZ_DIR="${SMIDJA_VIZ:-}"
120
132
  VIZ_API_PORT="${SMIDJA_VIZ_API_PORT:-8437}"
121
133
  VIZ_UI_PORT="${SMIDJA_VIZ_UI_PORT:-8438}"
122
134
  VIZ_API_PID_FILE="$RUN/smidja-viz-api.pid"