feat: precedence engine with unified am sync, replaces am hook/reload#108
Merged
feat: precedence engine with unified am sync, replaces am hook/reload#108
Conversation
…init - force-init reads _AM_PROJECT_ALIASES which now uses name|hash format - the raw entries were passed to force_unalias, causing shell errors where | was interpreted as a pipe (e.g. `functions -e foo|0de26fd`) - strip the |hash suffix before passing to unalias
- add profile_aliases parameter to generate_hook_with_security - after unloading project aliases, re-emit any global/profile alias that was shadowed by the now-removed project alias - in incremental (trusted) path, only restore for removed aliases (changed aliases get reloaded with new project value) - resolve global + active profile aliases in update.rs Hook handler - update snapshot tests to pass new parameter - add tests for leave, tamper, incremental remove, and no-shadow cases
- add Precedence builder, PrecedenceDiff, EffectiveEntry, EntryKind - resolve() returns empty diff for now - derive PartialEq on TomlAlias/AliasDetail so EntryKind::Alias compiles
- with_global/with_profiles/with_project consume AliasSet/SubcommandSet - merged_aliases/merged_subcommands overlay by name in precedence order
- alias_hash: blake3(command)[..7] - subcmd_program_hash: blake3 of 'key=long,long;key=long' serialization - subcmd_key_hash: blake3 of 'long,long'
- name|hash form stored as Some(hash) - bare name (backward compat) stored as None -> diff treats as always-differs
- added/changed/removed/unchanged derived from effective vs shell_alias_state - shadow restoration is automatic: removed project layer flips hash -> Changed
- per-key precedence: project jj:ab wins over profile jj:ab - different keys under same program coexist across layers - wrapper entry hash = hash of all merged entries under program - per-key SubcommandKey entries tracked in _AM_SUBCOMMANDS - regular alias absorbed into wrapper sets base_cmd
- drop local permissive grouping helper introduced in 40867d2 - fix test inputs to respect 1:1 short -> long parity - jj:bl with 2 longs becomes jj:b:l (valid nested path) - jj:ab with 2 longs becomes jj:ab with 1 combined long
Accepts external functions/aliases from bash/zsh scans. Names not already in env state get hash=None (always differ). Used by 'am init --force' to see everything before the nuke.
- unloads (removed + changed) then loads (added + changed) then env var updates - adds _AM_SUBCOMMANDS env var constant - program-level entries go in _AM_ALIASES alongside regular aliases - per-key SubcommandKey entries go in _AM_SUBCOMMANDS
- hidden subcommand, coexists with 'am hook' and 'am reload' for now - Message::Sync handler reads _AM_ALIASES/_AM_SUBCOMMANDS (+ legacy fold) - builds Precedence from config + profiles + (trusted) project + shell state - prints render_diff output to stdout
- Tampered/Untrusted/Unknown exclude project layer + warn where applicable - Tampered returns SaveSecurity effect - Fresh load prints full listing; incremental prints compact summary - _AM_PROJECT_PATH set when project excluded, unset when included - legacy _AM_PROJECT_ALIASES unset after first sync
- generate_init builds Precedence with empty shell state - engine emits all loads and the name|hash tracking env var - scaffolding (wrapper, cd hook, completions) unchanged
- union of _AM_ALIASES + legacy _AM_PROJECT_ALIASES + bash/zsh scans - strip |hash suffix before emitting force_unalias - clear _AM_ALIASES/_AM_SUBCOMMANDS/_AM_PROJECT_ALIASES/_AM_PROJECT_PATH - fresh load delegates to generate_init (engine-backed) Supersedes the intermediate 'strip hash suffix' fix from eb2d5fc by making it correct across both env-tracked and introspected name sources.
- every mutation (add/remove/use/trust/tui + profile mutations) -> 'am sync' - untrust -> 'am sync --quiet' - cd hook -> 'am sync' - bash/zsh/fish/powershell wrappers and hooks updated
- Precedence Engine fully supersedes hook/reload codegen paths - delete Commands::Hook, Commands::Reload, Message::Hook, Message::Reload - delete hook.rs (supersedes intermediate shadow-restoration patch) - delete generate_reload + generate_force_init - remove _AM_PROJECT_ALIASES, _AM_PROFILE_ALIASES_LEGACY env vars - users upgrading must run 'am init -f' once to clear legacy env vars
- accept 10 updated init snapshots: name|hash format, 'am sync' wrapper text - add 8 new sync-scenario snapshots covering fresh load, incremental, transitions, shadow restoration, and subcommand wrappers across shells
- move AliasName import into test module (non-test use was dead) - iterate on .keys() instead of (k, _) pattern in precedence diff loops - apply rustfmt to snapshots.rs
- toggle/use profile messages now match the 'am:' style used by sync - example: 'am: rust deactivated, 5 aliases'
Previously 'cd' into a project showed only 'am: aliases changed (N added)'.
Now it shows the detailed listing ('am: loaded .aliases' + each name -> command)
when entering a directly-owned trusted project for the first time in this shell.
- track _AM_PROJECT_PATH for trusted projects too (previously only for excluded)
- derive is_fresh_project_load from: include_project && is_direct && !already_seen_path
- use render_load_message directly on model.project_alias_set_and_subcommands()
instead of re-reading the .aliases file from disk
- incremental 'am: aliases changed (...)' still fires on edits and cd within project
Before: am: rust activated — 5 loaded: b, i, l, r, t am: aliases changed (2 added) After: am: profile rust activated — 2 added: b, r | 3 shadowed by .aliases: i, l, t - enrich ToggleProfiles and UseProfilesAt messages to split profile aliases into 'actually added' vs 'shadowed by .aliases' - deactivation mirrors: 'N removed: ... | M kept by .aliases: ...' - shell wrappers call 'am sync --quiet' for 'use'/'profile use' so sync no longer emits its generic summary for these paths (profile handler owns it) - regenerate 10 init snapshots for the new wrapper layout
'removed' sounds like deletion from disk — confusing when aliases are only unloaded from the current shell (the profile/.aliases file is untouched). Swap to the same verbs render_load_message / render_unload_message already use. - 'am: profile rust activated — 2 loaded: b, r | 3 shadowed by .aliases: ...' - 'am: profile rust deactivated — 2 unloaded: b, r | 3 kept by .aliases: ...' - 'am: aliases changed (2 loaded, 1 updated, 3 unloaded)'
Given profile git has both 'gm' and subcommand aliases 'git:psh'/'git:st', 'am use git' now reports all three items instead of just the regular alias. - profile_items(&Profile) combines aliases with subcommand keys - project_alias_names(model) now includes project subcommand keys too, so shadowing detection covers 'git:st' in profile vs 'git:st' in .aliases - message: 'am: profile git activated — 3 loaded: gm, git:psh, git:st'
Replace stale references to 'am hook' and 'am reload' (removed in favor of the unified 'am sync' command) and refresh all marketing / user-facing sample output to match the current engine behavior. - installation subcommand table: 'am hook' -> 'am sync' (EN + DE) - project-aliases 'How It Works': rewritten for the precedence engine; describes layer merging, minimal-diff emission, and automatic shadow restoration (EN + DE) - subcommand-aliases 'How It Works': 'am reload' reference -> 'am sync' triggered by cd or an am mutation (EN + DE) - UseCases.vue / UseCasesDe.vue: profile activation samples now use the 'am: profile X activated — N loaded: ...' format; cd-into-project samples show the full 'am: loaded .aliases' listing that cd actually produces
Fish's 'alias' builtin stores --wraps as a completion entry via
'complete --wraps'. That entry persists independently of 'functions -e',
so redefining the same alias accumulates --wraps= flags each time:
function i --wraps='profile_val' --wraps='project_val' --description ...
project_val $argv
end
Prepend 'functions -e NAME' + 'complete -e -c NAME' to every fish alias
emission so the completion wraps are cleared before the alias is (re)defined.
Applies to both the alias-form and the template-function form. The abbr form
is unaffected (abbr --add replaces cleanly by name).
- updated 4 fish unit tests
- regenerated 10 fish snapshots (6 init + 4 sync)
The previous attempt (task 88f6fdc) prepended 'complete -e -c NAME' hoping to clear the --wraps entry fish's 'alias' builtin stores via the completion system. On real fish installations that still produced stacked --wraps= across redefinitions. Drop fish's 'alias' builtin entirely. Emit 'function NAME\n CMD \$argv\nend' directly, with no --wraps, so there is nothing for fish to accumulate. - 'type i' now shows a single clean function body, no --wraps - 'functions -e NAME' + 'complete -e -c NAME' still prefixed for each emission to scrub any --wraps left over from older amoxide versions that used 'alias' - abbr mode unchanged (abbr --add already replaces cleanly) - trade-off: loss of --wraps completion inheritance; in practice this was unreliable for aliases whose command is a pipe or chain anyway
Follow-up to 84bd8a7 which dropped --wraps entirely. Bring back completion inheritance by emitting a separate 'complete -c NAME --wraps CMD' line after the function body, instead of on the function signature. Why this works where the prior approach failed: - fish's 'alias' builtin put '--wraps=<whole command>' directly in the function signature. On redefinition, fish accumulates --wraps= flags there (and 'type' displays them all). - A plain 'function NAME' has no --wraps in its signature, so 'type' stays clean (just the body). - 'complete -c NAME --wraps CMD' registers inheritance in the completion system; our prelude 'complete -e -c NAME' wipes the slate before each emission so no stacking. - The wrap target is the first whitespace-separated token of the command (the actual binary), which is what the completion system expects - rather than the full command with args/pipes/chains. - 'type i' now shows: function i\n cmd $argv\nend (clean, no --wraps) - tab completion for 'i ' works as if tab-completing for 'cargo' - regenerated 10 fish snapshots + 4 unit tests
Contributor
|
🔍 Website Preview: https://amoxide.rs/_preview/108/ Preview will be removed when this PR is merged or closed. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #108 +/- ##
==========================================
+ Coverage 74.62% 74.73% +0.11%
==========================================
Files 45 47 +2
Lines 12093 12164 +71
Branches 12093 12164 +71
==========================================
+ Hits 9024 9091 +67
- Misses 2886 2898 +12
+ Partials 183 175 -8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Mirror AliasSet's shape but keep the surface smaller: SubcommandSet now exposes only `new`, `is_empty` (needed by serde skip_serializing_if), and IntoIterator. Map-ish operations (`insert`, `remove`, `get`, `contains_key`, `len`, `iter`, `keys`, `values`, indexing) go through `.as_ref()` or `.as_mut()` — the escape hatch is explicit rather than delegated. - newtype with serde(transparent) so TOML layout is unchanged - AsRef<BTreeMap<..>> and AsMut<BTreeMap<..>> - IntoIterator for &SubcommandSet and SubcommandSet - FromIterator for tuple-iterator collection - 3 new unit tests covering the newtype's API - all call sites across both crates (lib + tests) route through as_ref/as_mut where needed
The free `group_by_program(&SubcommandSet)` function only operated on `self`, so it belongs on the type. Callers switch from `group_by_program(&subs)` to `subs.group_by_program()`. - moved into `impl SubcommandSet` as `group_by_program(&self)` - deleted the free function - updated call sites in display, profile, precedence, trust, bin/am, and tests
Free `render_diff(&PrecedenceDiff, &dyn ShellAdapter)` had no reason to
live outside the type. Moved it to `impl PrecedenceDiff` as
`render(&self, &dyn ShellAdapter) -> String`.
- callers switch from `render_diff(&diff, shell)` to `diff.render(shell)`
- updated init.rs, update.rs, precedence tests, snapshot tests (8 sites)
- dropped the now-unused `precedence::{self}` import alias in update.rs
Before: am: aliases changed (1 unloaded) After: am: aliases changed — 1 unloaded: foo Matches the style of the profile activation message and makes 'am tui' edits (or any other mutation that doesn't already print a rich message) self-explanatory — no more guessing which alias was affected. - extract shared format_section helper used by both profile_toggle_message and the sync handler's incremental branch - switch delimiter from '()' to ' — ' for consistency - sections now read 'N verb: name1, name2, ...' and are '|'-separated when more than one category is non-empty
…oad_message Two small follow-ups on the message-formatting work: - Extract format_change_summary(head, sections) that assembles the full '<head> — N verb1: a, b | M verb2: c' line. Both the sync handler's incremental branch and the profile toggle message now call it so the head separator and section joiner live in exactly one place. The only bit still inline is the profile-specific 'all N shadowed by .aliases' phrasing, which has no equivalent in sync. - Delete render_unload_message in trust.rs: dead since the sync handler rewrite (no production callers, only its own tests).
The sync handler's "am: aliases changed — ..." message is a pure function
of the diff, so it belongs on the type. Moved to
`PrecedenceDiff::change_summary(&self) -> Option<String>`.
- sync handler reduces to: `if let Some(msg) = diff.change_summary() { ... }`
- profile_toggle_message keeps its own `format_change_summary` helper — it
needs a dynamic head and the special "all N shadowed" phrasing that
sync doesn't have
- inline the now single-caller `format_section` into `format_change_summary`
Three places were splitting on '|' and ',' to parse the
_AM_ALIASES / _AM_SUBCOMMANDS env-var format: parse_state,
PrecedenceDiff::render, and the init --force nuke loop. Centralise the
invariant ("name|hash", or bare "name" for legacy compat) behind two
newtypes so the format lives in exactly one place.
- AliasWithHash: one entry with name() / hash() accessors, parse() that
rejects empty-name tokens, Display that round-trips to the wire form.
- AliasWithHashList: comma-separated list wrapper. parse() drops
malformed tokens silently; Display joins with ',' in order.
- parse_state, PrecedenceDiff::render, and update.rs's force-init nuke
loop all delegate to these types. No more ad-hoc split_once('|') /
split(',') in callers.
- 9 unit tests covering parse/display round-trip, bare-name backward
compat, empty/None inputs, malformed-token skipping.
1015-line precedence.rs had three cohesive groups: env-var wire format,
diff output types with rendering, and the Precedence builder engine.
Split into separate files under crates/am/src/precedence/ so each file
holds one responsibility and stays a comfortable read.
- precedence/mod.rs — module declarations + re-exports only
- precedence/env_state.rs — AliasWithHash + AliasWithHashList (~193 lines)
- precedence/diff.rs — EntryKind, EffectiveEntry, PrecedenceDiff
with change_summary + render (~194 lines)
- precedence/engine.rs — Precedence builder + resolve + hashing helpers
(~653 lines)
Public API is unchanged. All external uses of
amoxide::precedence::{Precedence, PrecedenceDiff, EntryKind,
EffectiveEntry, AliasWithHash, AliasWithHashList} still resolve. No
signatures, behavior, or test count changed.
… profile toggle
PrecedenceDiff::change_summary and profile_toggle_message had the same
inner shape: filter non-empty sections, render each as 'N verb: names',
join with ' | ', prefix with a head + ' — '. Only the head and verbs
differ.
Extract the inner logic to a single pub(crate) helper in precedence/diff.rs:
format_change_summary(head, &[(verb, &names), ...]) -> Option<String>
- PrecedenceDiff::change_summary now calls through it with the fixed
head 'am: aliases changed' and diff-shaped sections.
- profile_toggle_message imports it via
`use crate::precedence::format_change_summary;` and passes its own
head + shadow-aware sections.
- update.rs drops its duplicate helper.
The profile path still keeps its special 'all N shadowed by .aliases'
phrasing inline — sync has no equivalent shape.
`precedence/mod.rs` had `[\`env_state\`]` / `[\`diff\`]` / `[\`engine\`]` intra-doc links pointing at private submodules, which `rustdoc::private-intra-doc-links` flags when the lint is escalated to deny. Use plain backticks for code-style formatting — the module names aren't hyperlink targets in a public API listing anyway.
Merged
sassman
pushed a commit
that referenced
this pull request
Apr 27, 2026
## 🤖 New release
* `amoxide`: 0.7.0 -> 0.8.0 (⚠ API breaking changes)
* `amoxide-tui`: 0.7.0 -> 0.8.0
### ⚠ `amoxide` breaking changes
```text
--- failure constructible_struct_adds_field: externally-constructible struct adds field ---
Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/constructible_struct_adds_field.ron
Failed in:
field Config.logging in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/config.rs:30
field Config.logging in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/config.rs:30
--- failure derive_trait_impl_removed: built-in derived trait no longer implemented ---
Description:
A public type has stopped deriving one or more traits. This can break downstream code that depends on those types implementing those traits.
ref: https://doc.rust-lang.org/reference/attributes/derive.html#derive
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/derive_trait_impl_removed.ron
Failed in:
type Effect no longer derives Clone, in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:35
type Effect no longer derives Clone, in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:35
--- failure enum_struct_variant_changed_kind: An enum struct variant changed kind ---
Description:
A pub enum's struct variant with at least one pub field has changed to a different kind of enum variant, breaking access to its pub fields.
ref: https://doc.rust-lang.org/reference/items/enumerations.html
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_struct_variant_changed_kind.ron
Failed in:
variant ProfileAction::Use in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/cli.rs:162
variant Commands::Use in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/cli.rs:94
--- failure enum_variant_added: enum variant added on exhaustive enum ---
Description:
A publicly-visible enum without #[non_exhaustive] has a new variant.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#enum-variant-new
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_variant_added.ron
Failed in:
variant Commands:Sync in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/cli.rs:125
variant Effect:PrintLines in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:55
variant Effect:RenderSync in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:56
variant Effect:PrintLines in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:55
variant Effect:RenderSync in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/effects.rs:56
variant Message:Sync in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:38
variant Message:EnableProfiles in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:41
variant Message:DeactivateProfiles in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:42
variant Message:Sync in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:38
variant Message:EnableProfiles in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:41
variant Message:DeactivateProfiles in /tmp/.tmpZfglXO/amoxide-rs/crates/am/src/messages.rs:42
--- failure enum_variant_missing: pub enum variant removed or renamed ---
Description:
A publicly-visible enum has at least one variant that is no longer available under its prior name. It may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/enum_variant_missing.ron
Failed in:
variant Message::Hook, previously in file /tmp/.tmpcLonDb/amoxide/src/messages.rs:38
variant Message::Reload, previously in file /tmp/.tmpcLonDb/amoxide/src/messages.rs:39
variant Message::Hook, previously in file /tmp/.tmpcLonDb/amoxide/src/messages.rs:38
variant Message::Reload, previously in file /tmp/.tmpcLonDb/amoxide/src/messages.rs:39
variant Commands::Hook, previously in file /tmp/.tmpcLonDb/amoxide/src/cli.rs:133
variant Commands::Reload, previously in file /tmp/.tmpcLonDb/amoxide/src/cli.rs:142
--- failure function_missing: pub fn removed or renamed ---
Description:
A publicly-visible function cannot be imported by its prior path. A `pub use` may have been removed, or the function itself may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/function_missing.ron
Failed in:
function amoxide::init::generate_force_init, previously in file /tmp/.tmpcLonDb/amoxide/src/init.rs:102
function amoxide::hook::generate_hook, previously in file /tmp/.tmpcLonDb/amoxide/src/hook.rs:13
function amoxide::trust::render_unload_message, previously in file /tmp/.tmpcLonDb/amoxide/src/trust.rs:88
function amoxide::subcommand::group_by_program, previously in file /tmp/.tmpcLonDb/amoxide/src/subcommand.rs:93
function amoxide::group_by_program, previously in file /tmp/.tmpcLonDb/amoxide/src/subcommand.rs:93
function amoxide::init::generate_reload, previously in file /tmp/.tmpcLonDb/amoxide/src/init.rs:134
function amoxide::trust::render_load_message, previously in file /tmp/.tmpcLonDb/amoxide/src/trust.rs:54
function amoxide::hook::generate_hook_with_security, previously in file /tmp/.tmpcLonDb/amoxide/src/hook.rs:37
--- failure inherent_method_missing: pub method removed or renamed ---
Description:
A publicly-visible method or associated fn is no longer available under its prior name. It may have been renamed or removed entirely.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/inherent_method_missing.ron
Failed in:
SecurityConfig::save, previously in file /tmp/.tmpcLonDb/amoxide/src/security.rs:67
Session::save, previously in file /tmp/.tmpcLonDb/amoxide/src/session.rs:38
Session::save, previously in file /tmp/.tmpcLonDb/amoxide/src/session.rs:38
ProfileConfig::save, previously in file /tmp/.tmpcLonDb/amoxide/src/profile.rs:174
ProfileConfig::save, previously in file /tmp/.tmpcLonDb/amoxide/src/profile.rs:174
Config::save, previously in file /tmp/.tmpcLonDb/amoxide/src/config.rs:56
Config::save, previously in file /tmp/.tmpcLonDb/amoxide/src/config.rs:56
--- failure module_missing: pub module removed or renamed ---
Description:
A publicly-visible module cannot be imported by its prior path. A `pub use` may have been removed, or the module may have been renamed, removed, or made non-public.
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/module_missing.ron
Failed in:
mod amoxide::hook, previously in file /tmp/.tmpcLonDb/amoxide/src/hook.rs:1
--- failure pub_module_level_const_missing: pub module-level const is missing ---
Description:
A public const is missing or renamed
ref: https://doc.rust-lang.org/cargo/reference/semver.html#item-remove
impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.46.0/src/lints/pub_module_level_const_missing.ron
Failed in:
AM_PROJECT_ALIASES in file /tmp/.tmpcLonDb/amoxide/src/env_vars.rs:7
AM_PROFILE_ALIASES_LEGACY in file /tmp/.tmpcLonDb/amoxide/src/env_vars.rs:21
```
<details><summary><i><b>Changelog</b></i></summary><p>
## `amoxide`
<blockquote>
##
[0.8.0](v0.7.0...v0.8.0) -
2026-04-27
### Bug Fixes
- Apply_import was overwriting the user's real config on every cargo
test ([#111](#111))
- Hook reload local aliases when individual aliases changes
([#107](#107))
### Features
- Add explicit enable/disable flags to am use
([#115](#115))
- Make shell logging on navigation events configurable
([#113](#113))
- Precedence engine with unified am sync, replaces am hook/reload
([#108](#108))
### Miscellaneous Tasks
- Bump clap from 4.6.0 to 4.6.1
([#104](#104))
</blockquote>
## `amoxide-tui`
<blockquote>
##
[0.8.0](v0.7.0...v0.8.0) -
2026-04-27
### Bug Fixes
- Apply_import was overwriting the user's real config on every cargo
test ([#111](#111))
- Hook reload local aliases when individual aliases changes
([#107](#107))
### Features
- Precedence engine with unified am sync, replaces am hook/reload
([#108](#108))
- Add explicit enable/disable flags to am use
([#115](#115))
- Make shell logging on navigation events configurable
([#113](#113))
### Miscellaneous Tasks
- Bump clap from 4.6.0 to 4.6.1
([#104](#104))
</blockquote>
</p></details>
---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Precedenceengine (crates/am/src/precedence.rs) that merges global < profile < project alias layers, computes content-hashed diffs against shell state, and emits the minimal set of shell operations to sync. Shadow restoration falls out for free.am hookandam reloadwith a single hiddenam synccommand. The cd hook and the shell wrapper for every mutation (add,remove,use,trust,untrust,tui,profile use/add/remove) now invokeam sync.am init(andam init --force) to delegate alias emission to the engine. The force path unions_AM_ALIASES+ legacy_AM_PROJECT_ALIASES+ bash/zsh introspection scans, strips|hashsuffixes beforeforce_unalias, then lets the engine re-emit.hook.rs,generate_reload,generate_force_init,Commands::Hook,Commands::Reload,Message::Hook,Message::Reload,_AM_PROJECT_ALIASES, and_AM_PROFILE_ALIASES_LEGACY. Users upgrading must runam init -f <shell>once to clear legacy env state._AM_ALIASESand_AM_SUBCOMMANDS(bothname|hashformat, graceful bare-name backward compat).UX improvements on top of the engine
cdinto a trusted project prints the detailedam: loaded .aliaseslisting, not a compactam: aliases changed (N)summary.am: profile rust activated — 2 loaded: b, r | 3 shadowed by .aliases: i, l, t. Subcommand keys likegit:stare included. Wrapper usesam sync --quietforuse/profile useso the profile handler owns the single authoritative message.am:prefix across all user-facing messages;loaded/unloadedterminology instead of the more alarmingadded/removed.Fish-specific fixes
function NAMEbodies (no--wraps=on the signature) and register completion inheritance via a separatecomplete -c NAME --wraps CMDline. Fixes stacked--wraps=entries that fish'saliasbuiltin accumulated across redefinitions.type NAMEnow shows a clean body; tab completion still inherits from the wrapped binary.Docs
website/guide/installation.md,website/usage/project-aliases.md, andwebsite/usage/subcommand-aliases.md(EN + DE) for the new engine model andam synccommand.UseCases.vue/UseCasesDe.vueto match the new message formats.Closes
Test plan