Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **First-class script package activation (#14)**: script packages whose registry entry declares `activation: {kind: "nu-script", entry_points: [{name, path}, ...]}` can now be activated with `numan activate`. Each declared name becomes a command in the Numan-managed autoload file that runs the package-relative `.nu` target under the activating Nu executable, forwarding arguments, stdout/stderr, and exit status (pipeline input is not forwarded; see the design spec). Command names are validated, collisions across active script packages are rejected (never shadowed), and install remains inert — package code runs only when a user invokes a declared command. Scripts without a declaration stay install-only; `numan info` shows declared entry points before activation.

- **First-class completion package activation (#15)**: completion packages whose registry entry declares `activation: {kind: "nu-completion", targets: [{command, path, kind}, ...]}` can now be activated with `numan activate`. Each declared target is composed into the Numan-managed autoload file as a `use "<path>" *` contribution under an ownership comment; target ownership is exclusive across active completion packages (conflicts are rejected, never shadowed), and install remains inert — completion providers are never invoked at install or activation time, and `command-backed` providers run only when Nushell requests a completion. `numan info` shows declared targets and flags `command-backed` targets as able to execute code during completion. Completion packages without a declaration stay install-only. This is distinct from `numan completions <shell>`, which generates shell completions for Numan itself.

## [0.3.0] - 2026-10-24

### Added
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 15 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ These workflows are covered by unit tests, hermetic integration tests, and real-

- **Registry-backed installs**: Search packages, inspect available versions, and install `owner/name` or `owner/name@version`.
- **Official registry**: `numan init` configures the `official` registry automatically. Its production trust root is built into numan, and `numan registry sync` verifies every signed index.
- **Package types**: Plugins and modules support `numan activate`. Hook profiles require explicit `numan hook enable`. Scripts and completion packages are currently install-only.
- **Package types**: Plugins, modules, scripts, and completion packages support `numan activate`. Hook profiles require explicit `numan hook enable`. Packages without a supported activation surface are install-only.
- **Verified artifacts**: Plugin binaries require SHA-256 hashes, and registry indexes require valid Ed25519 signatures.
- **Scoped activation**: Plugins remain active only while the Nushell executable hash, Nushell version, and plugin registry path match the recorded activation state.
- **Module autoloads**: numan writes managed vendor autoload files with ownership markers and validates candidate files before promotion.
Expand All @@ -62,12 +62,18 @@ These workflows are covered by unit tests, hermetic integration tests, and real-
| Module | Yes | `numan activate` (managed vendor autoload) | Supported |
| Overlay module | Yes | `numan overlay use` / `hide` (interactive Nu wrapper) | Explicit activation in the current shell; applied at the next prompt |
| Hook profile | Yes | `numan hook enable` (owned vendor autoload) | Explicit opt-in; handlers run only on declared interactive events |
| Script | Yes | None | Install-only; activation is deferred |
| Completion package | Yes | None | Install-only; activation is deferred |

Install-only packages remain inert: numan downloads, verifies, locks, lists,
removes, and garbage-collects their payloads, but does not execute them or
modify Nu configuration for them. Hook packages are also inert until explicitly
| Script | Yes | `numan activate` (managed vendor autoload) | Supported |
| Completion package | Yes | `numan activate` (managed vendor autoload) | Supported |

Install remains inert for every package type: installation only downloads,
verifies, extracts, and locks payloads; nothing is executed or wired into
Nu until an explicit activation command. Listing, removal, and garbage
collection are separate lifecycle commands. Packages
whose registry entry declares no activation surface stay install-only.
Comment thread
tonythethompson marked this conversation as resolved.
Resolved
Completion packages declare their targets (`activation: {kind: "nu-completion",
targets: [{command, path, kind}, ...]}`); a target is owned exclusively by one
active package, and `command-backed` providers run only when Nushell requests
a completion, never at install or activation time. Hook packages are also inert until explicitly
enabled; inspect their declared events with `numan info` first. Hook changes load
in new Nushell sessions. This is separate from numan's own shell
completion installer: `numan completions <shell>` is supported for bash, fish,
Expand Down Expand Up @@ -362,8 +368,8 @@ Global flag: `--root <path>` — override the numan root directory (all commands
| `numan info <owner/name>` | Show package metadata and available versions |
| `numan install <owner/name[@version]>` | Download, verify, extract, and lock |
| `numan list` | List installed packages and activation status |
| `numan activate [pkg...]` | Register plugins / write module autoloads (scripts and completion packages are deferred) |
| `numan deactivate [pkg...]` | Remove module autoload entries |
| `numan activate [pkg...]` | Register plugins / write managed autoloads (modules, scripts, completion packages) |
Comment thread
tonythethompson marked this conversation as resolved.
Resolved
| `numan deactivate [pkg...]` | Remove managed autoload entries (modules, scripts, completion packages) / unregister plugins |
| `numan hook enable [pkg...]` | Explicitly enable declared Nushell hook bindings |
| `numan hook disable [pkg...]` | Disable hook bindings; no IDs disables all |
| `numan hook list` | Show active hook bindings |
Expand Down
91 changes: 91 additions & 0 deletions docs/specs/2026-10-29-completion-activation-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Completion package activation (issue #15)

## Contract

- A completion package declares its targets in its registry activation spec:
`activation: {kind: "nu-completion", targets: [{command, path, kind}, ...]}`.
`command` is the external command surface the package augments (a single
command name, e.g. `git`); `path` is a package-relative `.nu` source file
contributing that command's completions; `kind` is the declared completion
kind, `"static"` or `"command-backed"`. At least one target is required.
Comment thread
tonythethompson marked this conversation as resolved.
Resolved
- Command names use the same small grammar as script command names (ASCII
letter or `_`, then letters, digits, `_`, `-`) because the target name is
emitted into the generated managed file's ownership comments, and a
declared target is an ownership claim, not free text. Duplicate commands
or duplicate paths within a package are rejected, as are Nushell reserved
keywords.
- `kind` is declared, never inferred. `"command-backed"` means the source
defines completer functions that Nushell will execute when it requests a
completion; `"static"` means completions are literal values in the source.
Activation and install never invoke a completer either way. `numan info`
displays every declared target and, for `"command-backed"` targets, states
plainly that the provider can execute code during completion.
- Target ownership is exclusive across active completion packages: if a
command target is already owned by another active completion package,
activation of the second package is rejected. Numan never silently
replaces or shadows another active package's completion.
- Install persists the declaration in the lockfile (`completion_targets`);
install never executes package code. Activation validates every target
with the same containment/regular-file/`.nu` rules as script entry points.
- Activation composes each target into the Numan-managed autoload file
(`numan.nu`), alongside module `use` statements and script wrappers:

```nu
# numan:completion <owner/name> <command>
use "<absolute path to target file>" *
```

The `use ... *` form matches how Nushell custom-completion files are
consumed (they export `extern` definitions plus completer functions).
Rendering is sorted by scoped package ID, then command, so the generated
file is deterministic.
- The managed file goes through the existing candidate-validation, journal,
drift, and recovery machinery; the lockfile ground truth is a per-package
`completion_activation` record with the same Nu-identity fields as
`script_activation` (Nu executable hash, Nu version, vendor autoload dir,
managed file path, resolved targets).
- Deactivation removes exactly the package's `use` lines and clears its
record, leaving other packages' contributions and all user-authored
configuration untouched. Update/remove follow the existing autoload
reconciliation used for script packages.
- `numan init --refresh` re-resolves active completion targets and
regenerates the managed file when the Nu identity changes, matching
script behaviour.

## Acceptance

- Fixture coverage for static and command-backed targets, multi-command
packages, cross-package target conflicts, malformed declarations,
traversal attempts, deactivation, interrupted activation (recovery), and
updates, mirroring `tests/script_activation_test.rs`.
Comment thread
tonythethompson marked this conversation as resolved.
Resolved
- Real-Nu acceptance (ignored by default, run explicitly) proves a clean
interactive session receives a contributed completion after activation
and no longer receives it after deactivation, without modifying user
config files.
- Documentation distinguishes Numan's own shell-completion generator
(`numan completions <shell>`) from installable Nushell completion
packages, and the README package-support table lists completion packages
as activatable via `numan activate`.

## Non-goals

- Managing Bash/Fish/Zsh/PowerShell completion packages for arbitrary tools.
- Executing completion providers at install, inspect, lock, update, or
activation time. Providers run only when Nushell requests a completion.
- Merging arbitrary user-authored completion files, or detecting conflicts
with completions a user defined outside Numan. Declared targets are
registry-curated; the ownership check covers Numan-activated completion
packages.
- Inferring a target's kind by parsing its source. The declaration is the
contract; a package that mis-declares is a curation failure, surfaced by
`numan info` showing the declared kind before activation.

## Registry dependency

This contract is implemented on the client side only. The numan-registry
index schema (`schemas/index-v1.json` in numan-cli/numan-registry) does not
yet permit the `targets` field on an activation spec, so registry packages
cannot declare completion targets until that schema and its validation
path are updated to accept `nu-completion` targets. That schema change is
a tracked numan-registry follow-up; it is not part of the client PR that
implements this spec.
Loading
Loading