gabriel / muse public
README.md markdown
129 lines 3.2 KB
3f48b6eb feat(omzsh-plugin): show muse:(domain:branch) mirroring git:(branch) Gabriel Cardona <gabriel@tellurstori.com> 3d ago
1 # Muse — Oh My ZSH Plugin
2
3 Minimal shell integration for [Muse](https://github.com/cgcardona/muse).
4 Shows your active domain and branch in the prompt. That's it.
5
6 ---
7
8 ## Install
9
10 ```bash
11 bash /path/to/muse/tools/install-omzsh-plugin.sh
12 ```
13
14 Then add `muse` to your `plugins` array in `~/.zshrc`:
15
16 ```zsh
17 plugins=(git muse)
18 ```
19
20 ---
21
22 ## Prompt setup
23
24 Add `$(muse_prompt_info)` wherever you want the indicator in your `PROMPT`:
25
26 ```zsh
27 PROMPT='%~ $(muse_prompt_info) %# '
28 ```
29
30 Inside a Muse repo this renders as:
31
32 ```
33 ~/my-song ♪ muse:(midi:main) %
34 ```
35
36 Outside a Muse repo it emits nothing.
37
38 ---
39
40 ## What it shows
41
42 | Segment | Meaning |
43 |---------|---------|
44 | `♪ muse:(midi:main)` | `midi` domain, branch `main` |
45 | `₿ muse:(bitcoin:lightning)` | `bitcoin` domain, branch `lightning` |
46 | `⌥ muse:(code:feature/x)` | `code` domain, branch `feature/x` |
47 | `◈ muse:(_default:main)` | unknown domain |
48 | `♪ muse:(midi:a1b2c3d4)` | detached HEAD (short SHA) |
49 | `♪ muse:(midi:main) ✗ 3` | dirty working tree, 3 changed paths |
50
51 The dirty indicator (`✗`) only appears after you run a `muse` command in the
52 same shell session. This keeps the prompt fast on first open.
53
54 ---
55
56 ## Configuration
57
58 Set these in `~/.zshrc` **before** `plugins=(… muse …)`:
59
60 ```zsh
61 MUSE_PROMPT_ICONS=1 # 0 = ASCII fallback, e.g. [midi] main
62 MUSE_DIRTY_TIMEOUT=1 # seconds before dirty check gives up
63 ```
64
65 Override individual domain icons:
66
67 ```zsh
68 MUSE_DOMAIN_ICONS[midi]="🎵"
69 MUSE_DOMAIN_ICONS[bitcoin]="🔑"
70 ```
71
72 ---
73
74 ## Aliases
75
76 | Alias | Command |
77 |-------|---------|
78 | `mst` | `muse status` |
79 | `msts` | `muse status --short` |
80 | `mcm` | `muse commit -m` |
81 | `mco` | `muse checkout` |
82 | `mlg` | `muse log` |
83 | `mlgo` | `muse log --oneline` |
84 | `mlgg` | `muse log --graph` |
85 | `mdf` | `muse diff` |
86 | `mdfst` | `muse diff --stat` |
87 | `mbr` | `muse branch` |
88 | `mtg` | `muse tag` |
89 | `mfh` | `muse fetch` |
90 | `mpull` | `muse pull` |
91 | `mpush` | `muse push` |
92 | `mrm` | `muse remote` |
93
94 ---
95
96 ## Tab completion
97
98 All top-level `muse` commands and common argument types (branches, tags,
99 remotes, config keys, subcommands) complete with `<TAB>`.
100
101 Completion reads directly from `.muse/refs/` using ZSH globbing — no
102 subprocesses, no `ls`, instant response.
103
104 ---
105
106 ## How it works
107
108 1. **On directory change (`chpwd`)** — walks up to find `.muse/`, reads
109 `.muse/HEAD` (pure ZSH, no subprocess), reads `.muse/repo.json` for the
110 domain (one `python3` call).
111
112 2. **After a `muse` command (`precmd`)** — additionally runs
113 `muse status --porcelain` with a timeout to update the dirty indicator.
114
115 3. **On prompt render** — reads only cached shell variables; zero subprocesses.
116
117 ---
118
119 ## Security model
120
121 - No `eval` of any data from disk or environment.
122 - Branch names are regex-validated (`[a-zA-Z0-9/_.-]` only) and
123 `%`-escaped before prompt interpolation to prevent ZSH prompt injection.
124 - Domain names are validated as alphanumeric (max 32 chars) in Python.
125 - Repo paths are passed to Python via environment variables, never
126 interpolated into `-c` strings.
127 - `cd` and `timeout` calls use `--` to prevent option injection.
128 - Completion uses ZSH glob patterns, never `ls` or command substitution
129 on arbitrary file content.