Commit Graph

70 Commits

Author SHA1 Message Date
ethernet
dcd2bca06a fix(desktop): render icons with core runtime dependencies 2026-09-24 01:30:15 -04:00
ethernet
c13287c915 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	apps/desktop/electron/main.ts
#	hermes_cli/backup.py
#	hermes_cli/config.py
#	hermes_cli/plugin_catalog.py
#	hermes_cli/plugins_cmd.py
#	hermes_cli/plugins_cmd_catalog.py
#	hermes_cli/plugins_discovery.py
#	hermes_cli/profiles.py
#	hermes_cli/update_cmd_deps.py
#	pyproject.toml
#	tests/gateway/test_dm_topics.py
#	tests/hermes_cli/test_config.py
#	tests/hermes_cli/test_plugins_cmd.py
#	tests/hermes_cli/test_update_autostash.py
#	tests/tools/test_lazy_deps.py
#	tools/lazy_deps.py
#	tools/skill_ledger.py
#	utils.py
#	website/docs/user-guide/security.md
2026-09-22 05:16:50 -04:00
teknium1
c3021aff0d ci(skills-index): re-mint the App token before the catalog stars probe; keep fetched_at honest on failure
The `Probe plugin catalog stars` step reused the GitHub App installation token
minted at job start. That token lives 1 h; `build_skills_index.py` (the step
before it) has walked ClawHub for 60-85 min since Sep 17, so every scheduled
probe since Sep 18 ran on an expired token and logged
`GraphQL probe failed (HTTP Error 401: Unauthorized); keeping previous counts`.
114 of the 213 catalog repos ended up without a star count and the "Most
starred" default sort fell to alphabetical for half the catalog.

Run-log evidence (job step timestamps; `gh run view --json jobs` + job logs):
  run 35569229751 (Sep 21): mint 06:36:52Z -> probe 08:01:50Z (85 min) -> 401,
      "Probed 213 repos ... wrote 99 star counts"
  run 35426522555 (Sep 19): mint 06:24:52Z -> probe 07:39:59Z (75 min) -> 401
  run 35133575877 (Sep 16, last success): mint 18:18:39Z -> probe 18:40:12Z
      (21 min) -> "Probed 100 repos ... wrote 100 star counts"

Fix: a second `./.github/actions/get-app-token` step (`probe-token`)
immediately before the probe; the probe reads that token. Ordering stays (the
probe does not depend on the walk, but the artifact upload wants both files
and the step comment already documents the walk timing).

Freshness: `main()` stamped `fetched_at = now` whenever the written map
differed from the previous one, so a FAILED probe still moved the timestamp
forward and the catalog footer read "popularity ranking as of <today>" over
Sep-16 counts (live `plugin-stars.json`: fetched_at 2026-09-21 with Sep-16
data). `probe_stars` now returns `(stars, probed)`; `fetched_at` advances only
when GitHub actually answered, otherwise the previous timestamp is kept, a
`:⚠️:` names how many catalog repos have no count, and the summary line
says "Probe failed; wrote N cached star counts (as of <ts>)" instead of
"Probed N repos".

Local repro (3 catalog repos, cache from Sep 16 with 2 of them, GraphQL 401):
  before: fetched_at restamped to now; "Probed 3 repos ... wrote 2 star counts"
  after:  fetched_at stays 2026-09-16T18:40:16+00:00; ":⚠️:plugin star
          probe failed; reused 2 cached counts from 2026-09-16..., 1 of 3
          catalog repos have no star count"

Not taken from #118129: the per-repo REST fallback and the payload-shape
validation. The transport was never the problem (the same expired token 401s
on REST too), and one GraphQL request per run is the rate-limit rule this
script documents.

Fixes #118113
credit: @fangliquanflq #118129 (honest fetched_at on a failed probe + ::warning; slim redo)
2026-09-21 23:31:02 -07:00
ethernet
9ac6af3809 merge origin/main (28 commits) into ethie/pm-clean
main extracted the launchd backend and the setup wizard out of hermes_cli/gateway.py. The
eight launchd functions pm-clean had changed are ported into gateway_launchd.py in its
_gw() style: runtime_command/installation_command for the gateway argv (no VIRTUAL_ENV in
the plist, XML-escaped args), _prepare_service_launcher before every plist write,
utf-8-sig plist reads, and launchd_restart's refresh-first + bounded bootstrap revival.
The systemd service-unit cluster stays in the facade (no gateway_service_unit sibling).
backup.py takes main's browser_profiles backup-only exclusion on our profile_root_entry
shape. main.ts takes main's attach-first backend block with our explicit types.
2026-09-21 05:07:21 -04:00
teknium1
2463550c97 feat(website): plugin pages render the README from the pinned commit by default
The README section was gated on `readme: true` in the catalog YAML and no entry set it, so
all 222 plugin pages shipped without one. READMEs now render for every GitHub/GitLab entry
(fetched at the reviewed sha, subdir first then repo root, common casings and docs/README.md
as fallbacks); `readme: false` opts an entry out. Build proof: 222/222 READMEs rendered.
2026-09-21 01:15:33 -07:00
ethernet
9f2ba1b74d merge origin/main (779 commits) into ethie/pm-clean
Branch semantics kept where main and PM disagree: update_cmd_deps.py,
constraints-termux.txt, the Electron update-api-check module and the
post-swap hand-off test stay deleted; the pending-fleet-restart catch-up
and the local_runtime tag/download ladder stay retired (PM owns engines).

Ported from main onto the branch's shape: profile_scoped_chore for the
auto-archive and plugin-update housekeeping chores, the local-runtime
cross-process boot lock and residency cap, the checkpoint tmp_pack sweep,
the cua daemon-liveness status probe, the remote-served Desktop update
flag (posix.sh / windows.ps1), sign-in for env-pinned remote gateways
(urlDisabled on RemoteSetupFields), the uvloop extra split (uvicorn
without [standard]), and the umask-scoping spawn test.

uv.lock regenerated with pm.build_env --lock-only; new utf-8 reads from
main switched to utf-8-sig (check-windows-footguns).
2026-09-21 00:58:39 -04:00
teknium1
10c273813d feat(plugin-catalog): screenshots and readme entry fields for the plugin pages
Two optional, submitter-controlled fields on a catalog entry feed the
entry's own page at /docs/plugins/<name>:

- `screenshots:` — up to 6 https URLs on GitHub hosts (same host rule as
  `image`, so the site never fetches from third-party hosts and a raw URL
  pinned to the sha is as immutable as the code).
- `readme: true` — the docs build renders the README from the PINNED
  commit (raw.githubusercontent.com / gitlab.com raw at <sha>), never live
  content, so what a user reads is what the reviewer read.

Validator rejects malformed values (admission), the loader parses and
drops off-host screenshots with a warning (client), and the extractor
emits `screenshots`, `readme`, `readmeUrl` and a `maintainerSlug` for the
author pages. Tests on all three.
2026-09-20 20:43:13 -07:00
teknium1
4c2c20f6e7 feat(website): plugin catalog shows when each entry was added/updated and can sort by it
extract-plugins.py derives addedAt/updatedAt per entry from one `git log
--name-status` over plugin-catalog/ (committer dates, so rebase-merged PRs
are stamped when they land; renames carry the original addedAt). A shallow
checkout or missing git yields null dates instead of wrong ones, and
deploy-site.yml now checks out with fetch-depth: 0 so the deploy has the
history.

The /docs/plugins page gains a Sort control (Most starred / Newest /
Recently updated) and an "Added … · Updated …" line on every card; Updated
is hidden while it equals Added.
2026-09-20 20:18:58 -07:00
teknium1
1536e75bfe docs(website): generator prunes stale skill pages; CI fails when the committed docs drift
`generate-skill-docs.py` now deletes every page under `user-guide/skills/{bundled,optional}`
it did not write this run, together with the zh-Hans mirror twin, so a skill that moves,
merges or leaves the shipped set takes its page with it instead of lingering as an orphan
that cross-links still reach (24 such pages after the shipped-set slim, plus 21 zh-Hans
copies whose English page was already gone).

The Docs Site Checks workflow regenerated the docs and never compared the result with the
committed copies, which is what GitHub renders; it now fails with a pointer to the generator
when they differ. Also regenerates the two pages that drifted since the salvaged commits.
2026-09-20 12:54:07 -07:00
ethernet
9f837d298b Merge remote-tracking branch 'origin/main' into ethie/pm-clean
Conflicts resolved toward the PM model: main's lazy_deps/update_cmd_deps/npm
stamp machinery stays deleted (PM + scripts/build/node-deps.mjs own it), the
systemd ExecStop stop-mark rides the installation launcher, legacy
linux_only/macos_only/windows_only markers are rewritten to platforms(), and
finalize_update_receipt carries pending manual-serve obligations forward
again (lost when the ContextVar receipt rewrite crossed c0aa3ce354).

Test harness: the real-home I/O guard exempts /proc/<pid>/fd metadata reads
(deleted-WAL holder scans) and run_tests.sh drops ~/.hermes PATH entries so
shutil.which() cannot trip the tripwire.
2026-09-20 10:07:50 -04:00
teknium1
76fe8f7f5f fix: plugin catalog ranks by stars only and drops the review-claim badges
The catalog hero showed three trust chips ("Reviewed by the Hermes team",
"Installs exactly the version we reviewed", "One click from Hermes Desktop")
and the hero copy repeated the review claim. Teknium asked for the review
claim to go; the row goes with it since the remaining two chips only made
sense as a set.

Ranking pinned the official tier above every community entry, so a 5-star
official plugin sat above a 215-star community one and the page looked
unsorted. Tier no longer ranks; stars desc, unknown stars last, name as the
tiebreak. The Official/Community filter pills still exist for anyone who
wants the tier view.
2026-09-19 22:45:39 -07:00
ethernet
d29da5fe20 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
Conflicts resolved toward the branch: PM owns dependency preparation, the
Windows shim re-exec/hand-off path stays retired (main's shim-parent wait,
gateway-resume env token and update_cmd_deps tests dropped), docs describe
the PM update flow. The docker workflow parks install-stamp.json around the
toolchain step instead of deleting it so tests/docker can compare provenance.
2026-09-19 13:45:07 -04:00
teknium1
3eb9712180 Revert "feat(catalog): restore website install links to Desktop"
This reverts commit cf59980e2c.
2026-09-19 00:11:36 -07:00
ethernet
82a5affdd3 Merge remote-tracking branch 'origin/main' into ethie/pm-clean
# Conflicts:
#	hermes_cli/backup.py
#	tests/hermes_cli/test_gateway_restart_loop.py
#	website/docs/developer-guide/web-search-provider-plugin.md
#	website/docs/getting-started/installation.md
#	website/docs/getting-started/updating.md
#	website/docs/index.mdx
#	website/docs/reference/cli-commands.md
#	website/docs/user-guide/docker.md
#	website/docs/user-guide/windows-wsl-quickstart.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/plugins/index.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/developer-guide/web-search-provider-plugin.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/index.mdx
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/cli-commands.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/reference/environment-variables.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/docker.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/plugins.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/security.md
#	website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/windows-wsl-quickstart.md
2026-09-18 18:41:16 -04:00
teknium1
36fb2be926 fix(website): cross-page doc links resolve on GitHub as well as on the site
Docs under website/docs were linked with Docusaurus site routes
(`](/getting-started/installation)`, `](/docs/user-guide/x)`), which GitHub's
file viewer resolves as repository paths and 404s (#114428). Relative Markdown
file links (`](../user-guide/x.md#anchor)`) are followed by both GitHub and
Docusaurus, so that becomes the authoring convention:

- website/scripts/check_doc_links.py lints hand-authored EN + zh-Hans pages
  for route-style links (`--fix` rewrites them, refusing any route that maps
  to no doc file); wired into the Docs Site Checks workflow and
  tests/website/test_check_doc_links.py.
- generate-skill-docs.py emits the same relative form for related-skill and
  catalog links instead of `/docs/user-guide/skills/...`.
- src/remark/relativeDocLinks.js rewrites `./x.md`/`../x.md` to
  content-root-absolute `/x.md` before Docusaurus resolves links, so a
  relative link still resolves in the zh-Hans build when source and target
  sit on different sides of the translation fallback (Docusaurus resolves
  `./`/`../` only against the source file's own directory).
- website/README.md states the convention and points at the checker.
2026-09-18 14:27:04 -07:00
ethernet
b4a294fff9 Merge origin/main; keep PM as plugin dependency owner
Reconcile plugin declarations and validation through PM's atomic generation publication; preserve external runtimes, target markers, and conflict refusal. Keep one source-update completion owner and port upstream lifecycle changes to the PM desktop/runtime paths.
2026-09-17 13:52:05 -04:00
teknium1
034313e7cd feat: plugin catalog entries carry an optional version label and card image
The 40-hex sha stays the release, but nobody reads one. Entries may now add
`version: "1.4.0"` (free-form, <=32 chars, never parsed) and `image:` (an https
URL on raw.githubusercontent.com / github.com / *.githubusercontent.com).

Why GitHub-only: the Desktop catalog browser deliberately never fetches from
third-party hosts, and a raw URL pinned to the entry commit is as immutable as
the sha it decorates.

Readers updated together: PluginCatalogEntry + entry_from_mapping (drop with a
warning, entry survives), validate_plugin_catalog.py (admission error), the
site extractor (drop, never fatal), the /docs/plugins card (banner + version
pill + "1.4.0 @ abcd1234" pin), the CLI table/info (pin_label), the TUI-gateway
plugin row (catalog_version -> Desktop "Update to 1.4.0"), and the Desktop
catalog detail header (image).
2026-09-16 14:18:39 -07:00
brooklyn!
cf59980e2c feat(catalog): restore website install links to Desktop 2026-09-16 13:55:13 -05:00
teknium1
9796235822 Revert "feat(catalog): open website installs in Hermes Desktop"
This reverts commit b0e78e60fb.
2026-09-16 09:25:14 -07:00
brooklyn!
b0e78e60fb feat(catalog): open website installs in Hermes Desktop 2026-09-16 04:33:07 -05:00
teknium1
471d8a427a ci(plugin-catalog): probe stars only from the scheduled skills-index run, in one GraphQL request
Aligns the star ranking with the rule the skills index already follows:
GitHub is consulted only by the twice-daily skills-index.yml schedule, whose
artifact every docs deploy reuses. deploy-site.yml now runs
fetch-plugin-stars.py without --probe (reuse-only: artifact → live site copy →
disk → empty) and cannot call the API at all, so a same-day merge train adds
zero requests regardless of cache age.

The probe itself collapses from one REST call per repo (~26 today, growing
with the catalog) to a single GraphQL query with aliased repository fields,
so the scheduled run costs one request no matter how big the catalog gets. A
failed probe (rate limit, renamed repo, bad token) keeps the previous counts.
2026-09-15 04:39:38 -07:00
teknium1
3e2e2c50eb feat(plugin-catalog): rank entries by GitHub stars, probed at most once a day
Catalog entries sort official → stars desc → name, both in browse shelves and
filtered grids, with a ★ pill on each card linking to the repo's stargazers.

Rate-limit discipline is the design constraint: the docs site deploys many
times a day and shares one GitHub App API budget with every other workflow
(tonight's merge train got rate-limited on unrelated uploads). So
website/scripts/fetch-plugin-stars.py first fetches the live site's own
plugin-stars.json (a CDN GET, not the API); if that cache is under 24h old it
is reused verbatim and GitHub is never called. Only a stale cache triggers one
GET /repos/{owner}/{repo} per unique catalog repo, and a 403/429 mid-run keeps
the previous counts instead of zeroing them. extract-plugins.py merges the
cache into plugins.json (`stars`) and plugins-meta.json (`starsFetchedAt`), and
the page footnote says when the ranking was last refreshed.
2026-09-14 21:24:33 -07:00
teknium1
2de17e5d40 feat(plugin-catalog): default shelf is Desktop, catch-all is General; categorise today's six entries
Teknium's call: most community submissions are Desktop panes, so an entry
without a category lands on the Desktop shelf; "other" becomes "general" for
plugins that genuinely span areas. Shelf order puts Desktop first. The six
entries merged today (pets-all, newswire, auto-titler, live-voice,
metamask-wallet, web-octen) get explicit categories.
2026-09-14 21:00:29 -07:00
teknium1
55dbd7f6e1 feat(plugin-catalog): shelve the catalog by category (Memory, Desktop, Platforms, …)
The catalog page was one undifferentiated grid filtered only by tier, so a
memory provider sat between two Desktop panes. Entries now carry an optional
``category`` (memory | desktop | platform | web | tools | voice | automation |
models | other, default other) that the loader, the admission validator and
the site extractor all understand.

/docs/plugins renders one shelf per category in browse mode, a category pill
row under the tier pills, a clickable category chip on every card, and a
results bar (active category, count, clear) when a filter or search flattens
the view. ``hermes plugins catalog`` gains a Category column and groups by it.
All 18 shipped entries are categorised. Unknown categories fail admission
(same contract as tier) so a typo cannot create a phantom shelf.
2026-09-14 21:00:29 -07:00
ethernet
1bf588234c refactor(build): share product recipes across distributions
Build TUI, web, desktop UI and runnable agent products from explicit
prepared inputs. Keep dependency preparation separate from distribution
packaging, with PM and native builds sharing uv environment construction.

Docker copies compiled frontend products instead of build dependencies.
Nix retains uv2nix environments and consumes shared assembly through store
references. Native desktop and Termux use the same launcher and frontend
contracts. Preserve the independent PM runtime and source imports from
arbitrary working directories.

Keep failed frontend builds from replacing the previous product, reject
source/output overlap, and bound dependency-process output draining.
Include hermes_wisdom in the Nix wheel: real CLI smoke tests exposed its
missing package declaration on the base revision too.

Verified focused Python and JavaScript suites, Docker build/runtime checks,
Nix desktop and CLI/ACP checks, standalone TUI and packaged Electron PTY,
and real full-Chromium interaction. Native signed installers, Android device
installation and the full repository suite remain CI verification.
2026-09-11 13:16:55 -04:00
ethernet
284dbaf537 fix(pm): isolate bootstrap dependencies and unify YAML on ruamel
Activation reaches plugin discovery before the application dependencies
exist. Give PM its own locked Python project and runtime so it can install
or repair the application without importing that dependency tree.

Keep PM outside the application workspace. A shared uv workspace resolves
the application graph and cannot provide this isolation. Route mutations
through an isolated worker and preserve transaction callbacks, cancellation,
custom package registrations, and correlated receipts.

Use the same runtime builder for source installs and packaged payloads.
Keep offline wheelhouse support in that builder. Nix builds the independent
PM lock as a separate derivation. Refuse lazy-disabled bootstrap before
installing tools or dependencies.

Move first-party YAML readers and writers to ruamel. Keep the application
lock's transitive PyYAML requirements for third-party packages.

Verification:
- Focused canonical Python suite: 177 passed, 1 host-gated skip.
- Electron backend probes: 12 passed. Electron typecheck passed.
- Both uv locks, scoped lint, Bash syntax, and whitespace checks passed.
- Cold activation, corrupt-app repair, offline staging, and relocation ran.
- Built and exercised the Nix PM runtime and standalone YAML merge script.

Six broader caller test files retain the same 24 failing test IDs as an
archive of HEAD. The existing real-home guard blocks those tests before
they can exercise the affected paths. No full-suite pass is claimed.
Native Windows signing and full Bionic package execution remain unverified.
2026-09-11 12:23:51 -04:00
ethernet
e4cc7f09d9 merge: integrate upstream catalog with PM publication
Keep upstream's reviewed catalog as the only plugin name index.
Catalog pins and custom update sources share staged PM validation.
Publish code and dependencies with recovery after process death.
Reject a concurrent enablement change before publishing disabled code.

Use the manifest loader's supported version in the installer. Keep
probe cooldowns for timeouts, not TLS failures that a CA change fixes.
Preserve the backup, uninstall, browser and memory-provider repairs.

Verified with the canonical runner on native Windows ARM64, real Git
repositories, local TLS endpoints and UV dependency generations.
Desktop catalog tests and both TypeScript checks pass. The full suite
and native release builds were not run. No remote push.
2026-09-09 16:49:27 -04:00
Teknium
41b4555ed9 feat(plugins): catalog is the sole discovery system — re-port onto main's layout
- hermes_cli/plugins_cmd_catalog.py: new sibling owning resolution, the
  .hermes-catalog.json provenance sidecar, search/info/validate, re-pin on
  update, and the dashboard/TUI payload builders. plugins_cmd.py only
  gains the hooks (cmd_install catalog branch, cmd_update / dashboard
  update re-pin, dashboard_install_plugin catalog_name + kill list,
  dispatch entries); the community index (plugin_index.py) is gone.
- hermes_cli/plugin_catalog.py: catalog_dir parameter replaces the
  test-only HERMES_PLUGIN_CATALOG_DIR env var; live refresh reads ONE
  published document (/docs/api/plugin-catalog.json, 6h cache, in-tree
  fallback) instead of the unauthenticated GitHub contents API (60 req/h,
  1 request per entry); in-tree and live removals are unioned so a stale
  cache can never un-block.
- Catalog route lives in web_routers/dashboard_ui.py (the facade is off
  limits); _plugin_runtime_status shared from web_server_dashboard.py;
  hub rows carry removed_reason. TUI plugins.manage gains catalog_name
  install, catalog row fields and an update action.
- plugin_validate: the probe context honours ctx.get_config defaults
  (real plugins do int(ctx.get_config("timeout", 180)) in register()).
- Installed-state merge matches through the sidecar's catalog_name
  first — catalog names rarely equal manifest names.
- extract-plugins.py emits plugin-catalog.json; deploy-site triggers on
  plugin-catalog/** so entry merges republish it.
2026-09-09 04:38:01 -07:00
ethernet
e0a806c9d6 fix(icons): isolate build dependencies from runtime payloads
Icon generation selected the application venv, where resvg-py was
missing. Adding it to the dev extra also selected it for production
payloads built with --all-extras.

Use a locked icon-build dependency group in an isolated uv environment.
Keep its wheel cache separate from the PM cache copied into payloads.
Route generation and structural checks through the same runner.

Verified clean-source generation without resvg in the runtime venv,
runtime dependency export exclusion, targeted Python and JS tests,
and the full web workspace build. Signed desktop packaging was not run.
2026-09-07 08:50:25 -04:00
ethernet
f5b56a2746 feat(icons): no-frame wordmarks, squircle BrandMarks, on-demand generation
- website wordmarks (logo.png/logo-dark.png) drop the black/white frame:
  the girl alone on transparency, black for light navbar / white for dark
  (docusaurus srcDark stays)
- BrandMark marks are now the app-icon squircle itself (transparent
  corners, 824px safe-zone composition) instead of plain tiles; the
  components no longer paint a tile/rounding
- generated icon outputs are NOT committed anymore: all 35 targets are
  gitignored and regenerated on demand by the consuming pipelines via the
  new scripts/generate-icons.mjs (website prebuild, desktop prebuild+dev,
  installer prebuild, web prebuild)
- freshness lane switched from byte-compare to structural verification
  (sizes, squircle corner transparency, ICO frame sets via header parse) —
  no more windows/ubuntu byte drift dance
- marks saved as 8-bit palette PNGs (FASTOCTREE quantize keeps per-index
  alpha tRNS -> 2.5KB, AA edges preserved); compose_svg renders in the
  background's native space so resvg scales per output size
2026-09-06 21:49:26 -04:00
ethernet
e97de8d8c3 Merge branch 'ethie/windows-tests' into ethie/pm-clean
# Conflicts:
#	.gitignore
#	agent/deadline.py
#	pyproject.toml
#	scripts/run_tests.sh
#	tests/agent/lsp/test_install_and_lint_fixes.py
#	tests/cron/test_file_permissions.py
#	tests/cron/test_media_delivery_parity.py
#	tests/cron/test_script_claim_heartbeat.py
#	tests/hermes_cli/test_dep_ensure.py
#	tests/hermes_cli/test_gateway_wsl.py
#	tests/hermes_cli/test_linux_desktop_entry.py
#	tests/hermes_cli/test_npm_engine.py
#	tests/hermes_cli/test_runtime_repair.py
#	tests/hermes_cli/test_tui_npm_install.py
#	tests/test_hermes_constants.py
#	tests/test_install_autostash_conflict_recovery.py
#	tests/test_install_macos_launcher.py
#	tests/test_install_sh_acp_launcher.py
#	tests/test_install_sh_bootstrap_marker.py
#	tests/test_install_sh_node_deps_failure.py
#	tests/test_install_sh_symlink_stomp.py
#	tests/test_windows_subprocess_no_window_flags.py
#	tests/tools/test_approval_timeout_overflow.py
#	tests/tools/test_browser_homebrew_paths.py
#	tests/tools/test_find_shell.py
#	tests/tools/test_lazy_deps.py
#	tests/tools/test_lazy_deps_durable_target.py
#	tools/approval.py
#	uv.lock
2026-09-04 01:06:06 -04:00
ethernet
f9656f111a refactor: remove Android/Termux support entirely
Rip out the Termux/Android install lane: constraints-termux.txt,
psutil_android extraction, termux-browser tests, the apt install-method
lane in install.sh, termux api detection, and the TUI termux layout.
Zero added termux lines; 255 removed. System prompt and skill-utils
termux branches drop with the platform they served.

Rebuilt from ethie/pm (f8236a2d91, 7ad9f0c7a5, 8446c871b6, 82fb8e09d1)
onto the pm-store stack. 11 hot files hand-merged; test_code_execution
restored to branch-final (termux path assertions removed).
2026-08-31 18:00:48 -04:00
ethernet
bd258480d3 fix(tests): make the test suite pass on Windows without regressing Linux
Gate the POSIX-only and symlink-only tests with the linux_only and
require_symlinks markers. Fix the real cross-platform bugs:

- file_operations: use the translate_path flag, send snippets as base64, and
  use sys.executable (the MS Store python3 stub and the list2cmdline
  backslash collapse both broke snippets)
- approval: treat backslash as a Windows path separator, not an escape
- registry, browser_registry, secret_sources, plugins: normalize scope-key case
- checkpoint_manager, plugins_cmd: clear read-only bits before delete
- deadline: make MAX_SAFE_TIMEOUT_S fit the Windows limit
- image_routing, acp, cua_backend, daytona: fix Windows and POSIX paths
- hermes_state: match backslash in the retag LIKE clause
- kanban, disk-cleanup: match drive paths and split command arguments
- scripts: emit host separators through as_posix

207 test files are gated or isolated.
2026-08-31 17:52:30 -04:00
Teknium
cbf86f4265 feat(desktop): plugin catalog in Capabilities — embedded picker, one-click dual-target install, drift badge
- Capabilities gains a 4th tab (Plugins): the scoped (connection, profile)'s
  installed agent plugins with toggles, and the live docs-site plugin catalog
  embedded underneath (?embed=picker) — same shape as the Skills hub.
- '+ Add to this Agent' on a catalog card opens the existing dual-target
  install modal: the agent half installs at the catalog's reviewed pin into
  the scoped profile (plugins.manage catalog_name= passthrough), the desktop
  half installs locally; bundled agent+desktop packages offer both in one flow.
- Settings > Plugins: desktop halves of bundled packages get an
  'agent half missing here' badge when the currently connected backend/profile
  lacks the agent component — the one-app/N-agents drift is now visible.
- Docs /plugins page: picker embed mode (chrome hidden, pick button posts
  hermes-plugin-pick), subdir carried through the extractor.
- Gateway plugins.manage install accepts catalog_name (resolved server-side
  against the backend's own catalog; removed-blocklist enforced, no bypass).
2026-08-28 01:52:33 -07:00
Teknium
7e17ff0ab3 Merge origin/main into feat/plugin-catalog — reconcile with landed index/manifest-v2 tracks 2026-08-27 21:29:42 -07:00
Brooklyn Nicholson
a45d854d7d fix(docs): index every docs page in llms.txt, not a hand-picked 98
The section list decided membership as well as order, so it drifted as the
docs grew: 109 of 204 pages were absent from the index every LLM reads to
learn what Hermes does — Bot Mode, the desktop app, computer use, web search,
skins, Mixture of Agents, and 22 messaging platforms among them.

Enumerate the docs tree instead. SECTIONS now curates only which pages lead a
section; anything it does not name is absorbed under its path, and a page
matching no section lands in "More" rather than falling out. This also picks
up the three .mdx pages the .md-only glob never saw, points section landing
pages at the directory URL Docusaurus actually serves, and drops a curated row
still aimed at a guide moved to developer-guide/plugins in #59613.

Tests hold both directions against the filesystem rather than the enumerator,
so a page cannot go missing and a link cannot point at a page that moved.
2026-08-19 12:41:45 -05:00
dongjiang
de6a672168 fix(skills-hub): include owner in ClawHub source URLs and add retry on 429 (#51236)
Two fixes for the Skills Hub "View source" links on ClawHub skills:

1. Source URL generation was missing the required {owner} segment —
   https://clawhub.ai/skills/{slug} → 404. Correct format is
   https://clawhub.ai/{owner}/skills/{slug}. When the owner handle is
   unavailable, source_url is now "" (card omits the button) instead of
   emitting a broken link.

2. _fetch_owner_handle() previously delegated to _get_json() which
   returned None on any non-200 response with no retry. Under HTTP 429
   rate-limiting the "50 consecutive failures" safety rail in
   enrich_owners() fired immediately — the documented claim "Respects
   HTTP 429 rate-limit responses with exponential backoff" was not
   actually implemented. Now has its own retry loop: 3 attempts, honours
   Retry-After on 429, exponential backoff on 5xx/transport errors, no
   retry on 4xx.

Changes:
- tools/skills_hub.py: _coerce_skill_payload carries owner from top-level
  response; inspect() captures owner from detail API; _fetch_owner_handle()
  added with bounded retry/backoff; enrich_owners() batch method with
  safety rails (30 workers, early termination at 50 consecutive failures).
- website/scripts/extract-skills.py: _source_url() reads extra["owner"]
  for ClawHub.
- scripts/build_skills_index.py: batch enrichment step after crawling.
- tests: 35 URL/enrichment tests + 7 retry tests (42 total).

Signed-off-by: dongjiang <dongjiang1989@126.com>
2026-07-31 22:33:11 -07:00
Teknium
355b376225 docs(skills): regenerate skills catalog; fix generator sentence truncation
- Fix generate-skill-docs.py short-description truncation: split on
  sentence boundary (dot + space/end) instead of the first dot, which
  mangled descriptions containing dotted paths ('.hermes/plans/') or
  abbreviations.
- Regenerate all 181 skill pages + both catalog indexes + sidebar:
  adds missing pages for inspecting-hermes-desktop-dom, tldraw-offline
  (fixes broken catalog link), and pinecone-research.
- Delete orphaned kanban-codex-lane page (skill removed in #39028).
2026-07-29 08:48:05 -07:00
Teknium
158e9a9977 refactor: remove the claude-marketplace skill source (redundant Marketplace hub tab)
The Skills Hub 'Marketplace' tab showed a single useless entry: Anthropic
changed .claude-plugin/marketplace.json to bundle-shaped plugins whose
source is './', so all plugins collapsed to one identifier pointing at the
repo root, and the second marketplace repo (aiskillstore/marketplace) is
gone (404). Everything in anthropics/skills is already surfaced by the
GitHub tap as the Anthropic tab, making this source fully redundant.

Removes ClaudeMarketplaceSource and all wiring: source router, index
builder (crawl + floors + sort order + rate-limit messaging), extract
labels/install/URL mapping, hub UI tab, web server labels, CLI limits,
docs (en + zh), the legacy index-cache snapshot, and test fixtures.

Stale skills-index entries with source 'claude-marketplace' still install
fine: HermesIndexSource fetches via resolved GitHub paths generically.
2026-07-28 23:16:27 -07:00
Teknium
b718725121 fix(skills): move computer-use skill into the autonomous-ai-agents category
skills/computer-use/SKILL.md sat at the root of the bundled skills
tree as an uncategorized single-skill directory. Move it to
skills/autonomous-ai-agents/computer-use/ alongside claude-code,
codex, opencode, and hermes-agent.

- git mv skills/computer-use -> skills/autonomous-ai-agents/computer-use
  (history preserved)
- root-level-skill docstring examples (commands.py,
  generate-skill-docs.py) now use a generic placeholder instead of
  naming a specific skill, since categorizing root-level skills is
  ongoing
- docs: move the bundled page to autonomous-ai-agents-computer-use,
  update sidebars.ts, skills-catalog.md, and the two skill-path
  references in features/computer-use.md

E2E validated: fresh sync_skills() copies to the new nested path;
existing installs with the old flat copy keep it untouched (manifest
name-keyed, hash unchanged -> skipped, no duplicate);
_get_category_from_path resolves 'autonomous-ai-agents'; docusaurus
build green; 396 targeted tests pass.
2026-07-23 21:06:39 -07:00
Teknium
d4b6165018 fix(skills): move dogfood skill into the software-development category
skills/dogfood/SKILL.md sat at the root of the bundled skills tree,
making it one of the few uncategorized skills (Discord /skill
autocomplete listed it under 'uncategorized'; hermes_cli/commands.py
cited it as the example). Move it to
skills/software-development/dogfood/ alongside the other QA/testing
skills (test-driven-development, systematic-debugging,
requesting-code-review).

- git mv skills/dogfood -> skills/software-development/dogfood
  (history preserved)
- fix test fixture path in tests/tools/test_browser_console.py
- update root-level-skill docstring examples (commands.py,
  generate-skill-docs.py) to cite computer-use, which is still root-level
- drop 'dogfood' from the category list in hermes-agent-skill-authoring
  SKILL.md (en + zh-Hans docs mirrors)
- docs: regenerate/move the bundled page to
  software-development-dogfood (en + zh-Hans), update sidebars.ts,
  skills-catalog.md, and the adversarial-ux-test related-skills link

E2E validated: fresh sync_skills() copies to the new nested path;
existing installs with the old flat copy keep it untouched (manifest is
name-keyed, hash unchanged -> skipped, no duplicate);
_get_category_from_path resolves 'software-development'; docusaurus
build green.
2026-07-23 19:45:54 -07:00
Teknium
fb40a768fc feat(docs): add /docs/plugins catalog page fed by plugin-catalog/ extractor
- website/scripts/extract-plugins.py: reads plugin-catalog/*.yaml (+removed.yaml),
  emits static/api/plugins.json + plugins-meta.json; degrades to an empty
  catalog with exit 0 when plugin-catalog/ does not exist yet
- website/src/pages/plugins/: catalog page with search, tier tabs
  (All/Official/Community), capability chips, pinned-SHA repo links,
  copyable install commands, and an empty-state submission CTA
- cross-nav between Skills Hub and Plugin Catalog pages + navbar item
- user docs: user-guide/features/plugin-catalog.md (trust model, install,
  submission checklist, custom git-URL contrast), registered in sidebars.ts
- wired into deploy-site.yml and prebuild.mjs; artifacts gitignored
- tests: tests/website/test_extract_plugins.py
2026-07-22 07:28:53 -07:00
Teknium
cb29e8a82e refactor(cron): rebrand Cron Recipes -> Automation Blueprints
Product rename across every surface: module/file names (blueprint_catalog,
tools/blueprints, blueprint_cmd), slash command /cron-recipe -> /blueprint
(alias /bp), dashboard API /api/cron/blueprints, desktop deep-link
hermes://blueprint/<key>, docs catalog page + extract script, and the
skill frontmatter block metadata.hermes.blueprint. No behavior change.
2026-06-11 10:49:47 -07:00
teknium1
1593ca5406 feat(cron): Cron Recipes — parameterized automation templates across every surface
A 'recipe' is a one-place definition of an automation that every surface
renders natively. The slot schema (cron/recipe_catalog.py) is the single
source of truth; four renderers consume it, and all paths end at the same
cron.jobs.create_job — no second job engine.

Form where there's a screen, conversation where there's a chat line:
- Dashboard / GUI app: a Recipes sub-tab on the Cron page renders each
  recipe's typed slots as a form (time-picker, enum dropdown, free-text);
  submit POSTs /api/cron/recipes/instantiate which fills + creates the job.
- CLI / TUI / messengers: /cron-recipe lists the catalog, shows a recipe's
  fields, or fills + creates from a pasted 'key slot=val' command. The shared
  handler (hermes_cli/cron_recipe_cmd.py) names any missing/invalid slot so
  the agent can ask a targeted follow-up.
- Docs: a generated Cron Recipes catalog page (website, .mdx + React cards)
  shows each recipe with a copy-paste command and a 'Send to App' button.
- Desktop: a hermes:// URL scheme (Electron single-instance lock +
  setAsDefaultProtocolClient + open-url/second-instance) routes
  hermes://cron-recipe/<key>?slot=val into the chat composer pre-filled.

Typed slots (time/enum/text/weekdays) with defaults: users never type raw
cron — recipes parameterize time-of-day and weekday sets and translate to
cron expressions; a free-text 'schedule' slot is the full-flexibility escape
hatch. Consent-first throughout: nothing schedules without an explicit submit
or send.

Core:
- cron/recipe_catalog.py — CronRecipe + RecipeSlot, 5 curated recipes,
  recipe_form_schema / recipe_slash_command / recipe_deeplink /
  recipe_catalog_entry renderers, fill_recipe (validate + translate to
  create_job kwargs).
- hermes_cli/cron_recipe_cmd.py — shared /cron-recipe handler (CLI + TUI +
  gateway never drift). CommandDef + dispatch in commands.py / cli.py /
  gateway/run.py.

Dashboard: GET /api/cron/recipes + POST /api/cron/recipes/instantiate
(web_server.py), CronRecipes.tsx gallery+form, Segmented sub-tab on CronPage,
api.ts methods + types.

Desktop: hermes:// scheme end to end (main.cjs deep-link router + ready-queue,
preload onDeepLink/signalDeepLinkReady, global.d.ts types, desktop-controller
composer prefill, electron-builder protocols key).

Docs: extract-cron-recipes.py generator wired into prebuild.mjs,
cron-recipes-catalog.mdx + CronRecipesCatalog React component, sidebar entry.
Generated index json gitignored like skills.json.

Tests: 23 core (catalog/slots/schedule-resolution/validation/renderers/command
handler/generator) + 5 web_server endpoint tests. E2E verified end to end:
slot fill -> create_job -> persisted job with correct schedule/deliver/origin.
2026-06-11 10:49:47 -07:00
Teknium
fdc90346ea chore(skills): move red-team skills (godmode, obliteratus) to optional-skills — Anthropic classifier (#43221)
* chore(skills): remove red-team skills (godmode, obliteratus) from bundled catalog

Anthropic's output classifier on claude-fable-5 (and likely other Claude
models served through it) intermittently returns empty content for sessions
whose system prompt advertises these skills. The bundled skills-catalog block
is injected into every session's system prompt, so the descriptions

  - red-teaming/godmode      'Jailbreak LLMs: Parseltongue, GODMODE, ULTRAPLINIAN'
  - mlops/inference/obliteratus 'OBLITERATUS: abliterate LLM refusals (diff-in-means)'

trip the classifier on EVERY session regardless of which skill is actually
loaded, killing unrelated legitimate work (PR review, codebase audits, etc.).

Measured impact (controlled, interleaved A/B, claude-fable-5 via OpenRouter,
prompts differing only by the ~204 chars of these catalog lines, N=20 each):
  catalog lines present -> 19/20 (95%) blocked
  catalog lines absent  -> 5/20  (25%) blocked

Removing them ~quartered the block rate. Rewording the descriptions was not
enough; the skills must leave the bundled catalog.

- Delete skills/red-teaming/godmode and skills/mlops/inference/obliteratus
- Drop their generated doc pages + catalog/sidebar entries (EN + zh-Hans)
- Drop the godmode hand-written-page exception in generate-skill-docs.py

* chore(skills): relocate godmode + obliteratus to optional-skills

Rather than deleting outright, move both into optional-skills/ so they remain
installable via `hermes skills install` while leaving the always-injected
bundled catalog (which is what tripped Anthropic's classifier).

- optional-skills/security/godmode  (was skills/red-teaming/godmode)
- optional-skills/mlops/obliteratus  (was skills/mlops/inference/obliteratus)
- regenerate optional-skills catalog + sidebar entries
2026-06-09 21:41:00 -07:00
Teknium
59510d7b44 feat(skills): fix browse cap, add source links + copy buttons + category cleanup (#37143)
Skills discovery surfaced ~136 of 88k skills in the CLI and gave community
skills no clickable source on the docs page. Three coupled fixes:

CLI browse:
- hermes skills browse capped at 50 because the per-source limit dict had no
  'hermes-index' key — when the centralized index is available the router
  skips external APIs and serves only the index, so the default-50 fallthrough
  silently truncated the whole hub. Add hermes-index: 5000. Browse now loads
  5367 (269 pages) instead of 136.
- Add an Identifier column + install/inspect hint to the browse table so users
  can act on what they see without a second 'search'.
- Route the TUI browse_skills() helper through parallel_search_sources so it
  inherits the same index-aware source-skip (was double-counting); expose
  identifier in its output.

Docs Skills Hub page:
- Synthesize a sourceUrl for every community skill (github tree URL, clawhub /
  skills.sh / lobehub / browse.sh detail pages), preferring the adapter's
  explicit extra.detail_url/source_url/repo_url. Expanded cards now show
  'View source' for community skills (was nothing) and keep 'View full
  documentation' for built-in/optional. 99% coverage.
- Add a Copy button on the install command.
- Add a loading state instead of flashing '0 skills / No skills found' while
  the 45MB catalog fetches.

Category cleanup:
- _guess_category fell back to tags[0] verbatim, producing ~430 junk one-off
  categories (version strings, brand names: '0.10.7 Dev', 'Doramagic Crystal').
  Now only curated buckets are accepted; unknowns fold into 'Other'. Widen the
  tag->category map so common community tags route to real buckets. 430 -> 173
  categories, top 20 all meaningful.

Tests: tests/website/test_extract_skills.py covers _source_url synthesis +
precedence and _guess_category curation (13 tests). All 27 skills-hub CLI
tests still pass. Docusaurus build verified; expanded cards confirmed in
browser for both community (View source) and built-in (View full docs).
2026-06-01 19:52:28 -07:00
Teknium
b6ed3913d2 feat(skills): categorize tap skills from skills.sh.json grouping sidecar
A GitHub tap can ship a repo-root skills.sh.json (the published skills.sh
schema) declaring category groupings. The Skills Hub now reads it at index
time and uses each grouping title as the skill's category label, instead of
the tag-derived guess. Generic: any tap that ships the file gets real
categorization — NVIDIA's groupings (Inference AI, Decision Optimization,
GPU Development, etc.) flow through automatically.

- GitHubSource: _get_skillsh_groupings() fetches+caches the sidecar per repo;
  _parse_skillsh_groupings() flattens it to {skill_name: title};
  _list_skills_in_repo() stamps meta.extra['category']; _meta_to_dict now
  serializes extra so the category survives the index cache round-trip.
- extract-skills.py: prefers extra['category'] over the tag heuristic and
  exempts sidecar categories from the small-category to Other collapse.
- Docs + 12 tests.
2026-05-29 12:24:39 -07:00
Teknium
4de8009ce4 feat(skills): integrate NVIDIA/skills as a trusted skills hub tap
NVIDIA/skills is now a default trusted tap in the Hermes Skills Hub —
discoverable, browsable, searchable, and auto-updating through the same
pipeline that already serves OpenAI, Anthropic, and HuggingFace skills.

Rebased onto current main.
2026-05-29 12:24:39 -07:00
kshitijk4poor
66827f8947 chore: prune unused imports and duplicate import redefinitions
Remove unused imports (F401) and duplicate/shadowed import
redefinitions (F811) across the codebase using ruff's safe
autofixes. No behavioral changes -- imports only.

- ~1400 safe autofixes applied across 644 files (net -1072 lines)
- __init__.py re-exports preserved (excluded from F401 removal so
  public re-export surfaces stay intact)
- Re-exports that are imported or monkeypatched by tests but look
  unused in their defining module are kept with explicit # noqa:
  F401 (gateway/run.py load_dotenv; run_agent re-exports from
  agent.message_sanitization, agent.context_compressor,
  agent.retry_utils, agent.prompt_builder, agent.process_bootstrap,
  agent.codex_responses_adapter)
- Unsafe F841 (unused-variable) fixes deliberately skipped -- those
  can change behavior when the RHS has side effects
- ruff lints remain disabled in pyproject.toml (only PLW1514 is
  selected); this is a one-time cleanup, not a config change

Verification:
- python -m compileall: clean
- pytest --collect-only: all 27161 tests collect (zero import errors)
- core entry points import clean (run_agent, model_tools, cli,
  toolsets, hermes_state, batch_runner, gateway)
- static scan: every name any test imports directly from an edited
  module still resolves
2026-05-28 22:26:25 -07:00
teknium1
bfecfabd0f Revert "feat(skills): integrate NVIDIA/skills as a trusted skills hub tap"
This reverts commit 9992e32db3.
2026-05-28 20:39:39 -07:00