Skip to content

Feature github-install: hy-gh-3: Update + trust UX (hy-qvmo) - #13

Merged
philcunliffe merged 1 commit into
integration/github-installfrom
polecat/hy-qvmo
May 21, 2026
Merged

Feature github-install: hy-gh-3: Update + trust UX (hy-qvmo)#13
philcunliffe merged 1 commit into
integration/github-installfrom
polecat/hy-qvmo

Conversation

@philcunliffe

Copy link
Copy Markdown
Contributor

Summary

Feature github-install: hy-gh-3: Update + trust UX

Delivery

  • Issue: hy-qvmo
  • Branch: polecat/hy-qvmo
  • Integration target: integration/github-install
  • Eventual target: master

This PR is auto-merged into the integration branch once CI is green. Human
review for the full feature happens on the downstream PR
(integration/github-install -> master), which is opened separately when
the feature is ready to ship.

Add a confirmation gate to `hyp plugin install` and a real update flow
to `hyp plugin update <plugin>` so remote installs no longer commit
without the user (or `--yes`) seeing what is about to land.
Install (git sources)
- New `--yes`/`-y` flag.
- Non-TTY without `--yes` rejects with `remote_install_confirmation_required`
(exit code 2).
- TTY without `--yes` prints a summary (source URL, resolved commit,
plugin name + version, permissions, entrypoint, content_hash) and
prompts `Proceed? [y/N]`. Both summary and prompt land on stderr so
stdout stays parseable.
- Local-dir installs keep their current non-interactive behavior — the
confirmation gate only fires inside `fetchGitSource`.
Update
- `hyp plugin update <plugin>` now re-clones with the same source, runs
manifest validate, and shows old vs. new resolved_ref/version/content_hash
before the artifact swap. `--yes` skips the prompt; non-TTY without
`--yes` rejects as above. On rejection the prior install is left
intact because the rename swap only fires once `beforeCommit` returns
`proceed=true`.
- The bare `hyp plugin update` (no plugin name) keeps the legacy
"refresh update_check state for every plugin" behavior so `outdated`
can still be recomputed without committing to a re-install.
Soft warnings (stderr only)
- Manifest permissions containing `network` warn about broad scope.
- Branch-shaped refs (and missing refs) warn that pinning a tag or
commit SHA gives reproducible installs.
Telemetry
- `plugin.install` and a new `plugin.update` span both carry a
`confirmation` attribute drawn from `confirmed`, `auto_yes`,
`rejected`, `non_tty_no_yes`. The `plugin.installed` log row mirrors
the attribute when it was set.
- `fetch.FetchErrorKind` / `git_fetch.GitFetchErrorKind` gain
`remote_install_confirmation_required` and `remote_install_rejected`.
Tests + smoke
- New `test/core/plugin-install-confirm.test.js` covers the pure
helpers (`decideConfirmation`, `buildWarnings`, `sourceIsUnpinnedBranch`,
`renderConfirmationSummary`) and exercises the `installPlugin` +
`updatePlugin` integration against a hermetic file:// bare repo.
- New env-gated acceptance smoke
`hypaware-core/smoke/flows/plugin_install_github_url.js` installs a
real GitHub plugin fixture at a pinned tag (opt-in via
`HYP_SMOKE_REAL_GITHUB=1`, with overridable URL/ref/name env vars).
Skips with a clear marker when the env var is unset so CI runs of
`npm run smoke` do not hit the network.
- The existing hermetic git-url and boot-installed smokes now pass
`--yes` (they run with non-TTY buffer streams) and assert
`confirmation=auto_yes` on the install span.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@philcunliffe
philcunliffe merged commit 4f69e33 into integration/github-installMay 21, 2026
6 checks passed
philcunliffe added a commit that referenced this pull request May 22, 2026
* chore: seed integration/github-install for feature flow
* feat: install pipeline for GitHub-URL plugins (hy-2xeb) (#9)
Extend src/core/plugin_install to support git sources. Parses GitHub URL
forms (https with optional .git/#ref, github: shorthand, git@github.com
SSH) plus generic git+/git:/ssh:/gitlab:/bitbucket:/file:// URLs through
a new pure git_source parser. Adds --ref and --path CLI flags to the
plugin install command; --ref conflicting with a URL #ref is rejected
as source_ambiguous, and --path is reserved as git_subdir_unsupported.
The fetch path clones with --filter=blob:none --no-checkout, checks out
the requested ref (or the remote's HEAD symref by default), resolves
HEAD to a commit, validates the manifest and entrypoint, refuses any
symlinks in the artifact tree, then copies into an atomic install
directory swap. Lock entries gain resolved_ref; the update probe runs
git ls-remote and flips update.available when the upstream commit moved.
Telemetry: the existing plugin.install span gains hyp_source_kind=git,
git_url_host/owner/repo/ref/resolved_ref, content_hash, and
manifest_hash. Child spans plugin.git.clone, plugin.git.checkout,
plugin.git.resolve_ref, plugin.artifact.validate, and
plugin.artifact.copy each carry status and error_kind. Git stderr is
redacted for credentials before being surfaced.
Tests cover URL parsing variants, the --ref/URL-fragment conflict, the
subdir reservation, entrypoint traversal rejection, symlink detection,
and content-hash stability. New hermetic smoke plugin_install_git_url
builds a local bare repo and exercises the install end-to-end through
the dispatcher.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: boot integration for installed plugins (hy-jky3) (#11)
bootKernel now reads plugin-lock.json, merges bundled and installed
manifest pools, resolves dependencies across the merged set, and
activates installed third-party plugins when the config names them.
Rejects boot with `installed_shadows_bundled` when an installed
plugin tries to shadow a bundled first-party name. Adds telemetry
attributes (installed_available, installed_failed,
installed_shadow_collisions, installed_selected) and a
plugin.installed_active log per installed-and-selected plugin.
Threads installed manifest metadata into the cross-plugin config
validator via a new mergeInstalledManifestsIntoKnown helper, so
third-party plugin sections are no longer flagged plugin_unknown
and their provides/requires participate in sink-pair and
capability-ambiguity checks. Updates hyp config validate, hyp init
--from-file, and daemon status to feed the merged map in.
Adds boot-installed unit tests covering merged pool, dep resolution
over bundled+installed, shadow rejection, and config-validator
behavior, plus a hermetic plugin_boot_installed smoke that installs
a git-fixture plugin, activates it via config, runs its contributed
command, and asserts the boot telemetry surface.
* feat: update + trust UX for installed plugins (hy-qvmo) (#13)
Add a confirmation gate to `hyp plugin install` and a real update flow
to `hyp plugin update <plugin>` so remote installs no longer commit
without the user (or `--yes`) seeing what is about to land.
Install (git sources)
- New `--yes`/`-y` flag.
- Non-TTY without `--yes` rejects with `remote_install_confirmation_required`
(exit code 2).
- TTY without `--yes` prints a summary (source URL, resolved commit,
plugin name + version, permissions, entrypoint, content_hash) and
prompts `Proceed? [y/N]`. Both summary and prompt land on stderr so
stdout stays parseable.
- Local-dir installs keep their current non-interactive behavior — the
confirmation gate only fires inside `fetchGitSource`.
Update
- `hyp plugin update <plugin>` now re-clones with the same source, runs
manifest validate, and shows old vs. new resolved_ref/version/content_hash
before the artifact swap. `--yes` skips the prompt; non-TTY without
`--yes` rejects as above. On rejection the prior install is left
intact because the rename swap only fires once `beforeCommit` returns
`proceed=true`.
- The bare `hyp plugin update` (no plugin name) keeps the legacy
"refresh update_check state for every plugin" behavior so `outdated`
can still be recomputed without committing to a re-install.
Soft warnings (stderr only)
- Manifest permissions containing `network` warn about broad scope.
- Branch-shaped refs (and missing refs) warn that pinning a tag or
commit SHA gives reproducible installs.
Telemetry
- `plugin.install` and a new `plugin.update` span both carry a
`confirmation` attribute drawn from `confirmed`, `auto_yes`,
`rejected`, `non_tty_no_yes`. The `plugin.installed` log row mirrors
the attribute when it was set.
- `fetch.FetchErrorKind` / `git_fetch.GitFetchErrorKind` gain
`remote_install_confirmation_required` and `remote_install_rejected`.
Tests + smoke
- New `test/core/plugin-install-confirm.test.js` covers the pure
helpers (`decideConfirmation`, `buildWarnings`, `sourceIsUnpinnedBranch`,
`renderConfirmationSummary`) and exercises the `installPlugin` +
`updatePlugin` integration against a hermetic file:// bare repo.
- New env-gated acceptance smoke
`hypaware-core/smoke/flows/plugin_install_github_url.js` installs a
real GitHub plugin fixture at a pinned tag (opt-in via
`HYP_SMOKE_REAL_GITHUB=1`, with overridable URL/ref/name env vars).
Skips with a clear marker when the env var is unset so CI runs of
`npm run smoke` do not hit the network.
- The existing hermetic git-url and boot-installed smokes now pass
`--yes` (they run with non-TTY buffer streams) and assert
`confirmation=auto_yes` on the install span.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: review findings on integration/github-install plugin install (hy-lpo3) (#18)
Address all blocker and major findings from the dual-review on PR #6:
confirm.js — switch to node:readline/promises so the awaited
rl.question() actually returns a Promise and the interactive TTY
prompt path can return a decision instead of throwing.
update_check.js — pickLsRemoteSha now prefers the peeled `^{}` SHA
when present so annotated tags compare against entry.resolved_ref
correctly instead of producing spurious "update available" signals.
Also wraps the ls-remote spawn with a hard timeout (default 5s,
HYP_GIT_PROBE_TIMEOUT_MS-overridable, clamped) that emits a
git_probe_timeout error_kind, and the post-install path now passes
freshlyResolved=true so the probe is skipped entirely when we just
resolved the upstream ref — the span still emits with
probe_skipped=freshly_resolved for observability.
git_source.js / resolver.js / install.js — strip user:pass@ userinfo
from source.raw and source.gitUrl before they reach the lock entry,
the confirmation prompt, or the install span. Persistence and display
are always credential-free; clones rely on the user's git credential
helper. Adds redactGitUrl / redactRawSource helpers.
git_fetch.js / update_check.js — insert `--` end-of-options separator
before user-controlled positionals in `git clone`, `git checkout`,
and `git ls-remote` so a hostile URL or ref like `--upload-pack=<cmd>`
cannot be parsed as a git option (CVE-2018-17456 family). Also reject
leading-dash inputs in parseGitSource, applyGitSourceFlags, and the
CLI parser as belt-and-braces defense before the spawn boundary.
git_source.js — applyGitSourceFlags JSDoc no longer claims the subdir
value is "still recorded on the returned spec" since the function
throws unconditionally; the unreachable subdir spread is removed.
Tests cover redactGitUrl/redactRawSource, leading-dash rejection
across parser layers, peeled-tag SHA preference in pickLsRemoteSha,
and the real readline/promises behavior of buildTtyPrompt via
PassThrough streams.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: feature-launch <feature-launch@gas.city>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@platypii
platypii deleted the polecat/hy-qvmo branch May 28, 2026 06:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@philcunliffe