@zerwiz/ymir 0.1.32 → 0.1.33

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.
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: galdr
3
+ description: Galdr — agent-CLI ergonomics and the master builder/maintainer of the Ymir (Brokk distro) runtime. Use when building, modifying, or reviewing any agent-facing CLI, or when building/maintaining any Ymir subsystem across harnesses (OpenCode, Pi, Claude Code, Cursor, Codex). Load its assets[] row for the task before editing a governed path.
4
+ domain: brokkforge
5
+ allowed-tools: read,write,bash,glob,grep
6
+ ---
7
+
8
+ # galdr-ymirsystem — agent-CLI ergonomics — the master builder/maintainer of the runtime
9
+
10
+ Lean TOON router. This file is a manifest — load only the asset that matches the
11
+ task; never read the whole tree.
12
+
13
+ ```
14
+ galdr[1]{role,operator,naming}:
15
+ "agent-CLI ergonomics + master builder/maintainer of the Brokk distro runtime","Allfather (Odin) — address directly; never named by an imported term","name every subsystem for the figure whose role matches its work"
16
+ ```
17
+
18
+ ## Surfaces
19
+
20
+ Galdr is dual-surface: the same content loads as a skill (canonical) and as an
21
+ agent (a symlink, so the two can never drift).
22
+
23
+ ```
24
+ surfaces[2]{path,kind}:
25
+ ".agents/skills/galdr-ymirsystem/SKILL.md","skill (canonical)"
26
+ ".agents/agents/galdr.md","agent (symlink → the skill)"
27
+ ```
28
+
29
+ ## Mandate
30
+
31
+ Galdr owns two things: the **10 principles** for agent-facing CLIs (TOON output,
32
+ minimal schemas, self-correcting errors), and the **whole Ymir runtime** — how the
33
+ Brokk distro boots under any harness, how Eindri workers are spawned, how the
34
+ Nornir jobs run, and how it is all verified. A runtime change not reflected in
35
+ `assets/` is an incomplete change.
36
+
37
+ ## Routing (load one row)
38
+
39
+ ```
40
+ assets[24]{path,load_when}:
41
+ "assets/principles.md","the 10 CLI design principles (full doctrine)"
42
+ "assets/build-method.md","building/maintaining the runtime; forging a skill"
43
+ "assets/registry.md","skills, tools, commands, Eindri profiles, aett, schemas"
44
+ "assets/norse-naming.md","naming any component; the naming law + component map"
45
+ "assets/brokk-distro-runtime.md","the runtime spec (home, digest, lock, supervision, cron)"
46
+ "assets/runtime-components.md","every runtime component, interface, and env var"
47
+ "assets/local-models.md","Vog — local models: engines (llama.cpp, LM Studio, Ollama), detection, harness wiring, honest measurement"
48
+ "assets/runtime-compliance.md","runtime acceptance gates + runnable checklist"
49
+ "assets/memory-well.md","Mimirsbrunn/engram: the well, bridge, MCP, harness wiring, laws"
50
+ "assets/installation.md","first setup / install: the computed plan (bin/ymir-plan.sh), the home the operator chooses, the roots law (code in the package, the operator's things in the home), engines, hermes"
51
+ "assets/harness-integration/README.md","choosing a harness; adding one"
52
+ "assets/harness-integration/opencode.md","OpenCode adapter"
53
+ "assets/harness-integration/pi.md","Pi adapter"
54
+ "assets/harness-integration/claude-code.md","Claude Code adapter"
55
+ "assets/harness-integration/cursor.md","Cursor adapter"
56
+ "assets/harness-integration/codex.md","Codex adapter"
57
+ "assets/porting-upstream-to-norse.md","porting a validated upstream into Norse"
58
+ "assets/eindri-orchestration.md","spawn/brief/supervise Eindri workers"
59
+ "assets/nornir-jobs.md","the scheduler, jobs, and Runes ledger"
60
+ "assets/smidja.md","the smithy: install, roster/pi models, run, trace, visualizer ports, observer"
61
+ "assets/hlidskjalf-ui.md","any change under apps/hlidskjalf"
62
+ "assets/odrerir-hall.md","the Óðrerir Live Hall — apps/odrerir, :4322, its window, the planning snapshot feed (bin/hall-snapshot.sh)"
63
+ "assets/pi-boot-guide.md","the PI primary boot path"
64
+ "assets/README.md","the full asset index"
65
+ ```
66
+
67
+ ## Scripts (compliance + TOON)
68
+
69
+ ```
70
+ scripts[2]{path,purpose}:
71
+ "scripts/toon-check.py","validate TOON blocks (header count/fields vs rows)"
72
+ "scripts/compliance-check.sh","run all gates: toon, naming, mocks, syntax, json, sync"
73
+ ```
74
+
75
+ Run `bash .agents/skills/galdr-ymirsystem/scripts/compliance-check.sh` before claiming any
76
+ runtime or asset change done. Exit `1` on any FAIL.
77
+
78
+ ## The 10 principles (manifest)
79
+
80
+ ```
81
+ principles[10]{id,name,gist}:
82
+ 1,"TOON output","stdout TOON, ~40% under JSON; JSON internally"
83
+ 2,"Minimal schemas","3-4 fields per list row, not 10"
84
+ 3,"Content truncation","preview + total size + --full hint"
85
+ 4,"Pre-computed aggregates","counts and derived state inline"
86
+ 5,"Definitive empty states","state the zero with success context"
87
+ 6,"Structured errors & exit codes","stdout errors, no prompts, fail loud"
88
+ 7,"Ambient context via session integrations","hooks inject a compact dashboard"
89
+ 8,"Content first","no-args shows live state, not help"
90
+ 9,"Contextual disclosure","suggest the next command, parameterized"
91
+ 10,"Consistent help","identify the tool; --help per subcommand; fast --version"
92
+ channels[3]{stream,carries,agent_reads}:
93
+ "stdout","data, errors, suggestions","yes"
94
+ "stderr","debug, progress, diagnostics","no"
95
+ "exit","0 success/no-op, 1 error, 2 usage","yes"
96
+ ```
97
+
98
+ Full rules, examples, and the `--version` fast path: `assets/principles.md`.
99
+
100
+ ## Harnesses (authoritative names)
101
+
102
+ ```
103
+ harnesses[6]{harness,surface,tier,figures}:
104
+ "OpenCode","`.opencode/plugins/saga-sessionstart.js`, `syn-watch-arm.js`, `syn-turnend-guard.js`, `syn-pretool-check.js`, `syn-cd-check.js`","run","Sága+Sýn"
105
+ "Pi","`.pi/extensions/syn-turnend-guard.ts`, `gna-pi-watch.ts` (+ Vörðr supervisor)","run","Sága+Sýn+Gná+Vörðr"
106
+ "Claude Code","`.claude/settings.json` SessionStart + Stop","run","Sága+Sýn"
107
+ "Cursor","`.cursor/hooks.json` sessionStart + stop + preToolUse","run","Sága+Sýn"
108
+ "Codex","`.codex/hooks.json` SessionStart + PreToolUse + Stop (`[features].hooks=true`)","run","Sága+Sýn"
109
+ "Grok","not yet implemented","—","—"
110
+ ```
111
+
112
+ Details and gotchas: `assets/harness-integration/`.
113
+
114
+ ## Build method (any harness, any component)
115
+
116
+ ```
117
+ build[7]{id,step,detail}:
118
+ 1,"Read the runtime spec","assets/brokk-distro-runtime.md"
119
+ 2,"Read the harness guide","assets/harness-integration/<harness>.md"
120
+ 3,"Reuse the runtime scripts","saga-*, syn-*, rodd-*, gleipnir-* — never reimplement"
121
+ 4,"Wire the contract","session-open injection, watch arm, turn-end guard, pretool"
122
+ 5,"Bind the lock to the live session","pass BROKK_SESSION_PID to Gleipnir"
123
+ 6,"Fail closed","never launch on an unverified harness"
124
+ 7,"Verify","bash -n, JSON parse, smoke, assets/runtime-compliance.md"
125
+ ```
126
+
127
+ Full procedure, forge gates, and porting: `assets/build-method.md`.
128
+
129
+ ## Before you start
130
+
131
+ Read the TOON specification and `AGENTS.md`. Then load the one `assets/` row the
132
+ task needs. The operator is the **Allfather**; the house voice is Norse; an
133
+ imported term never names a subsystem.
134
+
135
+ ## Acceptance (master builder)
136
+
137
+ ```
138
+ acceptance[7]{id,gate}:
139
+ 1,"Comprehensive & reusable — assets/README.md routes every task"
140
+ 2,"Norse methodology enforced — role-matched names; operator is the Allfather"
141
+ 3,"Every harness documented and wired"
142
+ 4,"Runtime contract honored — injection, lock binding, guard inert-until-armed"
143
+ 5,"Production, no mocks — no examples/placeholders in the shipped runtime"
144
+ 6,"Verified — bash -n, JSON parse, smoke, compliance checklist"
145
+ 7,"No drift — plan, code, and assets agree (Tyr judges)"
146
+ ```
147
+
148
+ ## Private data — Hodd (Rule 04)
149
+
150
+ All private material lives in `hodd/` (secrets · docs · tenants · identity),
151
+ untracked but for its guard and README. Read secrets by path with
152
+ `bin/hodd.sh emit <file>` (`YMIR_HOARD`), never inline a value. When a governed
153
+ path needs a secret, resolve it through Hodd. Law: `RULES/04-hoard.md`.
154
+
155
+ ## Operator runbooks
156
+
157
+ Task-oriented guides live in `docs/runbooks/` (models · agents · Tailscale sync ·
158
+ updates/migrations · secrets/Hodd), indexed by the `ymir` skill asset
159
+ `assets/runbooks.md` and listed in `assets/registry.md`.
@@ -121,6 +121,23 @@ npm_renewal[6]{step,how,why}:
121
121
  rewrite a manifest at publish time (that is how `vite.config.ts` went missing and
122
122
  every panel died on `Unexpected token '<'`).
123
123
 
124
+ ## The publish discipline — this cannot happen again
125
+
126
+ Tonight cost seven publishes in an hour, one version thrown away, and a tag left on a
127
+ build that did not carry the fix. Gróa holds four laws now:
128
+
129
+ ```
130
+ publish_laws[4]{law,what_it_cost_us}:
131
+ "ONE version per sitting, at most","npm accepted 0.1.32 and never created it (404) — rapid publishes read as abuse, and a dropped publish looks exactly like a successful one"
132
+ "verify by the VERSION DOCUMENT","curl -s -o /dev/null -w '%{http_code}' …/@zerwiz/ymir/<version> — 200 or it did not happen; npm's success line is not evidence"
133
+ "unpack the TARBALL before calling it released","npm pack <version> and read install.sh / README.md from it: 0.1.31 was tagged latest and did NOT carry the installer fix"
134
+ "move the TAG, do not republish","npm dist-tag add @zerwiz/ymir@<good> latest — no new version, and every next install gets it"
135
+ ```
136
+
137
+ **Why they are law:** the registry serves what was **published**, not what is on `main`.
138
+ A publish that appears to succeed may reach no user at all; every check above tests the
139
+ artefact — the only thing a user ever touches.
140
+
124
141
  ## Safety
125
142
 
126
143
  - **Fast-forward only.** Dirty, diverged, offline, or non-default-branch targets are skipped and reported, never forced or stashed. Nothing with unlanded work is ever discarded.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## 2026-09-18 — the seat s chat left the repo, and no file names a home
2
+
3
+ - **`chat/` was TRACKED in a public repo** — the seat s own conversations. It lives in the
4
+ home now (`$YMIR_HOME/state/chat/`, where the runtime reads it) and is gitignored: the
5
+ operator s words are the operator s (Rule 04).
6
+ - **Seven files carried `/home/<user>`** — a username, in public. All seven read `$HOME` now,
7
+ verified by name: *zero* tracked files name a home path.
8
+
9
+ ## 2026-09-18 — a username was in a tracked file (corrected, not rewritten)
10
+
11
+ - Two entries carried `$HOME/Ymir` — the operator s own username, in a public file.
12
+ They now read `$HOME`. The rule is absolute and already in the changelog: **no hardcoded
13
+ absolute file paths**, ever — a home path names a person, and this repo is public.
14
+
15
+ ## 2026-09-18 — the publish discipline, written into Gróa (this cannot happen again)
16
+
17
+ - Seven publishes in an hour cost a version: npm accepted 0.1.32 and never created it
18
+ (404). Four laws are hers now: one version per sitting; verify by the VERSION document;
19
+ unpack the TARBALL before calling a release done (0.1.31 was tagged latest and did not
20
+ carry the installer fix); move the TAG rather than republishing.
21
+
1
22
  ## 2026-09-18 — the one-liner writes the PATH first (it never reached .bashrc)
2
23
 
3
24
  - **The bug, exactly:** install.sh passed `"${@:-}"` to npm, which with no arguments is an
@@ -423,7 +444,7 @@ npm path alone to be true.
423
444
  directory` — every package had arrived, and eighteen scripts were looking in the
424
445
  clone's `apps/`. `bin/app-lib.sh` resolves a surface in either shape now.
425
446
  - **A clone and a package are one tree to the scripts.** Proven against both: the
426
- five surfaces resolve from `/home/zerwiz/Ymir` and from an npm-installed
447
+ five surfaces resolve from `$HOME/Ymir` and from an npm-installed
427
448
  package, and `ymir raise` against a packaged tree fetches the app's
428
449
  dependencies, raises the gate API, starts Nornir cron, and brings up Bifrost and
429
450
  the well.
@@ -443,7 +464,7 @@ npm path alone to be true.
443
464
  the invite door, the seat-hall, Eir, the icons, the placement, the hall snapshot
444
465
  and the installer's own SPA and shell steps. `bin/smidja-lib.sh` delegates to it
445
466
  now, so there is one truth about where things live.
446
- - **Proven in both trees**: all five surfaces resolve from `/home/zerwiz/Ymir`
467
+ - **Proven in both trees**: all five surfaces resolve from `$HOME/Ymir`
447
468
  (a clone) and from an npm-installed package. And `ymir raise` run against the
448
469
  packaged tree no longer dies — it fetches the app's dependencies, raises the
449
470
  gate API, **starts Nornir cron**, and brings up Bifrost and the well.
@@ -158,7 +158,7 @@
158
158
  ## Phase 1: Planning & Execution Pipeline Updates (Week 2)
159
159
 
160
160
  ### 1.1 Update `create-plan` Skill
161
- **File**: `/home/zerwiz/firstmate/.agents/skills/create-plan/` (or wherever it lives)
161
+ **File**: `$HOME/firstmate/.agents/skills/create-plan/` (or wherever it lives)
162
162
 
163
163
  **Changes**:
164
164
  - Add **Layer 1 (Product)** section template: problem, metrics, announcement post, HTML mockups
@@ -177,7 +177,7 @@
177
177
  ---
178
178
 
179
179
  ### 1.2 Update `ticket-executor` Skill
180
- **File**: `/home/zerwiz/firstmate/.agents/skills/ticket-executor/`
180
+ **File**: `$HOME/firstmate/.agents/skills/ticket-executor/`
181
181
 
182
182
  **Changes**:
183
183
  - **Phase 0 (NEW)**: Program Design + Vertical Slice Definition
@@ -198,7 +198,7 @@
198
198
  ---
199
199
 
200
200
  ### 1.3 Update `validate-implementation` Skill
201
- **File**: `/home/zerwiz/firstmate/.agents/skills/validate-implementation/`
201
+ **File**: `$HOME/firstmate/.agents/skills/validate-implementation/`
202
202
 
203
203
  **Changes**:
204
204
  - **Live dev environment testing**: integrate with orbs/portals/preview deployments
@@ -265,7 +265,7 @@
265
265
  **Timeframe**: [e.g., 14 days post-deploy]
266
266
  **Rollback Threshold**: [e.g., < 11% for 48h]
267
267
  ```
268
- - `bin/measurable-goal-validate.sh` — checks goal is quantitative, not descriptive (at `/home/zerwiz/CodeP/wayofmono/bin/measurable-goal-validate.sh`)
268
+ - `bin/measurable-goal-validate.sh` — checks goal is quantitative, not descriptive (at `$HOME/CodeP/wayofmono/bin/measurable-goal-validate.sh`)
269
269
 
270
270
  **Integration Points (DONE)**:
271
271
  - `ticket-manager`: Added `measurable_goal` to frontmatter schema + Production-Ready Standard
@@ -283,7 +283,7 @@
283
283
  ### 2.3 `logic-retention-guard` Skill (NEW)
284
284
  **Purpose**: Periodic codebase logic quizzes / agent-educates-human during long tasks
285
285
 
286
- **Location**: `/home/zerwiz/firstmate/.agents/skills/logic-retention-guard/`
286
+ **Location**: `$HOME/firstmate/.agents/skills/logic-retention-guard/`
287
287
 
288
288
  **Deliverables**:
289
289
  - `SKILL.md` — trigger: long-running tasks (>30 min), after major structural changes
@@ -304,7 +304,7 @@
304
304
  ### 2.4 `multi-model-review` Skill (NEW)
305
305
  **Purpose**: Parallel Codex + Opus review for high-stakes changes
306
306
 
307
- **Location**: `/home/zerwiz/firstmate/.agents/skills/multi-model-review/`
307
+ **Location**: `$HOME/firstmate/.agents/skills/multi-model-review/`
308
308
 
309
309
  **Deliverables**:
310
310
  - `SKILL.md` — trigger: PR validation for high-stakes changes (configurable: security, payments, core infra)
@@ -325,7 +325,7 @@
325
325
  ## Phase 3: Factory-Wide Integration & Polish (Week 4)
326
326
 
327
327
  ### 3.1 Update `fm-brief.sh` — Master Scaffold
328
- **File**: `/home/zerwiz/firstmate/bin/fm-brief.sh`
328
+ **File**: `$HOME/firstmate/bin/fm-brief.sh`
329
329
 
330
330
  **Changes**:
331
331
  - Scaffold includes ALL four layers as mandatory sections:
@@ -344,7 +344,7 @@
344
344
  ---
345
345
 
346
346
  ### 3.2 Update `fm-session-start.sh` — Context Budget Init
347
- **File**: `/home/zerwiz/firstmate/bin/fm-session-start.sh`
347
+ **File**: `$HOME/firstmate/bin/fm-session-start.sh`
348
348
 
349
349
  **Changes**:
350
350
  - Initialize context budget tracking per task
@@ -354,7 +354,7 @@
354
354
  ---
355
355
 
356
356
  ### 3.3 Update `fm-teardown.sh` — Goal Validation
357
- **File**: `/home/zerwiz/firstmate/bin/fm-teardown.sh`
357
+ **File**: `$HOME/firstmate/bin/fm-teardown.sh`
358
358
 
359
359
  **Changes**:
360
360
  - Require measurable goal validation before teardown (for ship tasks)
@@ -364,7 +364,7 @@
364
364
  ---
365
365
 
366
366
  ### 3.4 Update `data/backlog.md` Schema
367
- **File**: `/home/zerwiz/firstmate/.tasks.toml` + backlog items
367
+ **File**: `$HOME/firstmate/.tasks.toml` + backlog items
368
368
 
369
369
  **Changes**:
370
370
  - Add fields to task metadata:
@@ -69,7 +69,7 @@ Added validation checks for Layer 3 (section 1e):
69
69
  Verdict: ❌ REJECTED if Layer 3 missing or incomplete.
70
70
 
71
71
  ### 3. Updated Global Plan Template
72
- **Location**: `/home/zerwiz/CodeP/wayofmono/thoughts/global/templates/plan-template.md` (rewritten)
72
+ **Location**: `$HOME/CodeP/wayofmono/thoughts/global/templates/plan-template.md` (rewritten)
73
73
 
74
74
  Complete rewrite including:
75
75
  - Linked Ticket section
@@ -13,7 +13,7 @@ Dex Horthy: "An agent given a number to move will go much further than one given
13
13
 
14
14
  ## Current State
15
15
 
16
- - Ticket template at `/home/zerwiz/.pi/thoughts/global/templates/ticket-template.md` has Acceptance Criteria (checkboxes)
16
+ - Ticket template at `$HOME/.pi/thoughts/global/templates/ticket-template.md` has Acceptance Criteria (checkboxes)
17
17
  - Ticket frontmatter in `ticket-manager/SKILL.md` has no `measurable_goal` field
18
18
  - Plans have "Success Criteria" but they're descriptive, not quantitative
19
19
  - No validation that goals are measurable vs descriptive
@@ -26,7 +26,7 @@ APPEND_ONLY='(^CHANGELOG\.md$|^docs/append-only-log\.md$|^docs/plans/|^assets/re
26
26
  # Note: the public owner identity — "zerwiz" the repo holder, its public domains,
27
27
  # and the project's own repo forms — is intentionally NOT private. What stays
28
28
  # guarded: other personal names, real /home paths, tenants, and secret shapes.
29
- PATTERNS='(josef|lindbom|/home/zerwiz|(ghp|gho|ghs|ghr)_[A-Za-z0-9]{36}|sk-[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]+|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'
29
+ PATTERNS='(josef|lindbom|$HOME|(ghp|gho|ghs|ghr)_[A-Za-z0-9]{36}|sk-[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]+|AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'
30
30
  # Legitimate exceptions, named explicitly rather than by widening the pattern:
31
31
  # - the public owner identity (the repo's full URL, the short owner/repo form,
32
32
  # and the owner's public homepage — the copyright holder on every licence);
@@ -36,7 +36,7 @@ ALLOW='github\.com/zerwiz/|zerwiz/ymir|zerwiz\.org|akka://|AKIAIOSFODNN7EXAMPLE|
36
36
  leaks() { # stdin → prints masked hits, returns 1 on any
37
37
  grep -En "$PATTERNS" \
38
38
  | grep -Ev "$ALLOW" \
39
- | sed -E 's/(sk-|ghp_|AKIA|xox[a-z]*-)[A-Za-z0-9-]+/\1…/g; s#/home/zerwiz#/home/<user>#g' \
39
+ | sed -E 's/(sk-|ghp_|AKIA|xox[a-z]*-)[A-Za-z0-9-]+/\1…/g; s#$HOME#/home/<user>#g' \
40
40
  | head -5
41
41
  }
42
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerwiz/ymir",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "Ymir — the single-tenant agent operating system. One command: curl -fsSL https://raw.githubusercontent.com/zerwiz/ymir/main/install.sh | bash · or: npx @zerwiz/ymir · or: npm i -g @zerwiz/ymir (if `ymir` is not found, the README has the three-line PATH cure)",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/zerwiz/ymir",
@@ -51,8 +51,7 @@
51
51
  ".cursor/",
52
52
  "assets/",
53
53
  "data/",
54
- "deploy/",
55
- "install.sh"
54
+ "deploy/"
56
55
  ],
57
56
  "engines": {
58
57
  "node": ">=20"
package/install.sh DELETED
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env bash
2
- # install.sh — the one-liner. Ymir into the operator's OWN prefix, with PATH set,
3
- # so `ymir` works in the shell they are standing in and every shell after it.
4
- #
5
- # curl -fsSL https://raw.githubusercontent.com/zerwiz/ymir/main/install.sh | bash
6
- #
7
- # Why this exists, and why `npm install -g @zerwiz/ymir` alone is not enough: npm
8
- # puts a global command in its global prefix's bin directory, and on an ordinary
9
- # machine that directory is not on PATH. The command is installed and the shell
10
- # cannot see it — the most common "it does not work" in the whole Node ecosystem.
11
- # This script removes that class of failure:
12
- #
13
- # · a prefix the user owns (no sudo, no EACCES on /usr/lib/node_modules)
14
- # · the export written into the shell's rc, so it survives the session
15
- # · and then it proves the door: `ymir --version`
16
- #
17
- # Env: YMIR_NPM_PREFIX (default ~/.npm-global), YMIR_PKG (default @zerwiz/ymir).
18
- set -eu
19
-
20
- PREFIX="${YMIR_NPM_PREFIX:-$HOME/.npm-global}"
21
- PKG="${YMIR_PKG:-@zerwiz/ymir}"
22
-
23
- say() { printf '%s\n' "$*" >&2; }
24
-
25
- command -v npm >/dev/null 2>&1 || {
26
- say "error: npm is not installed, and this script does not install Node."
27
- say "help: install Node 20+ (https://nodejs.org), then run this again."
28
- exit 1
29
- }
30
-
31
- # 1. a prefix the user owns — every later install is painless
32
- mkdir -p "$PREFIX/bin"
33
- npm config set prefix "$PREFIX" >/dev/null 2>&1 || true
34
- export PATH="$PREFIX/bin:$PATH"
35
-
36
- # 2. the PATH FIRST — so a failed install still leaves the command reachable, and
37
- # the user can simply run it again. (It used to be written after the install,
38
- # and `"${@:-}"` handed npm an EMPTY argument, so the install failed and the
39
- # script exited before any shell file was touched: "it never writes to .bashrc".)
40
- rc_written=""
41
- for rc in "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.zshrc" "$HOME/.profile"; do
42
- [ -e "$rc" ] || continue
43
- if ! grep -q "$PREFIX/bin" "$rc" 2>/dev/null; then
44
- printf '\n# Ymir — the npm global bin, so `ymir` is found\ncase ":$PATH:" in *":%s/bin:"*) ;; *) export PATH="%s/bin:$PATH" ;; esac\n' "$PREFIX" "$PREFIX" >>"$rc"
45
- rc_written="$rc_written ${rc##*/}"
46
- fi
47
- done
48
- export PATH="$PREFIX/bin:$PATH"
49
-
50
- # 3. the package
51
- say "installing $PKG into $PREFIX …"
52
- # `@latest` AND --prefer-online: a stale cached `latest` is how an install
53
- # succeeds while changing nothing, and the user is left on an old build.
54
- install_args=(-g "$PKG@latest" --prefer-online)
55
- [ "$#" -gt 0 ] && install_args+=("$@")
56
- if ! npm install "${install_args[@]}"; then
57
- say "error: the install failed — see npm's output above."
58
- say "note: your PATH is already set, so a second run picks up where this left off."
59
- exit 1
60
- fi
61
-
62
-
63
- # 4. the FIRST SETUP — the whole platform, not just a command
64
- # pi.dev and opencode finish here by running their own onboarding; an installer
65
- # that stops at a binary leaves the user with a tool that does nothing yet, and
66
- # with eight things still to go wrong. YMIR_SKIP_SETUP=1 installs the command only.
67
- if [ "${YMIR_SKIP_SETUP:-0}" != 1 ]; then
68
- say ""
69
- say "the first setup — the plan, then the work. This takes a few minutes."
70
- if ! ymir install --yes; then
71
- say "warn: the setup did not finish cleanly — nothing is lost; run \`ymir\` again to continue."
72
- fi
73
- fi
74
-
75
- # 5. prove the doors rather than promise them
76
- if command -v ymir >/dev/null 2>&1; then
77
- say ""
78
- say "ymir $(ymir --version 2>/dev/null || printf '?') — installed."
79
- say "what stands:"
80
- ymir validate --quiet 2>/dev/null | grep -E "^ \"" | head -12 | sed 's/^/ /' >&2 || true
81
- say "next: ymir raise # lift the hall (SPA :3888, the board :8437, the windows)"
82
- say " ymir --help # every door"
83
- else
84
- say "error: the package is installed but \`ymir\` is still not on PATH."
85
- say "help: export PATH=\"$PREFIX/bin:\$PATH\" (then open a new shell)"
86
- exit 1
87
- fi
88
- [ -n "$rc_written" ] && say "PATH written into:$rc_written — open a new shell for other terminals."