581f37965c feat(skills): SKILL.md skills UI — browse, create, install from URL (#681) (#740)
* feat(skills/core): add read_skill_resource with size + traversal guards (#681)

Introduces `read_skill_resource(skill_id, relative_path)` in the skills
ops module. Used by the new `skills.read_resource` RPC (landed in a
follow-up commit) to let the UI preview files bundled alongside a
SKILL.md without having to shell out to the Node runtime.

Guards rejecting each known attack surface have their own unit test:
- empty skill_id / empty relative_path
- unknown skill
- absolute paths
- `..` traversal escapes (checked after canonicalization against the
  skill root, reusing the pattern from the Node exec allowlist)
- directory targets
- symlinked leaves (reject via `symlink_metadata` before open)
- files over the 128 KB cap
- non-UTF-8 content (binary allowlist is text-only)

Happy-path test covers a small text resource under the skill root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(skills/core): wire skills.read_resource RPC + namespace (#681)

Adds `skills` RPC namespace with `skills.list` and `skills.read_resource`
handlers. `read_resource` delegates to `read_skill_resource` (previous
commit) and surfaces path-traversal / size / encoding errors back to the
caller verbatim so the UI can render the error string as-is.

- `src/openhuman/skills/schemas.rs` (new): controller + schema
  definitions, plus unit tests for schema name stability, round-trip of
  the minimum `SkillSummary` fields, and controller list/schema length
  parity.
- `src/openhuman/skills/mod.rs`: declare `pub mod schemas` and re-export
  `all_skills_controller_schemas`, `all_skills_registered_controllers`,
  and `skills_schemas`.
- `src/core/all.rs`: register the controllers + schemas and add a
  namespace description so the RPC discovery endpoint surfaces it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(app/api): typed skillsApi client for list + read_resource (#681)

Thin typed wrapper around the `skills.list` and `skills.read_resource`
RPCs added in the previous commit. The client normalises the backend
response shape (bytes + UTF-8 content) and rethrows backend error
strings verbatim so the preview pane can render them unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(app/ui): SkillResourceTree groups bundled resources by top dir (#681)

Presentational component that takes the `resources: PathBuf[]` from a
loaded Skill and renders it as a grouped list (scripts, assets,
references, etc. based on the first path segment). Selecting a leaf
calls `onSelect(relativePath)` so the parent drawer can drive preview
state.

Stateless — no fetching, no effects. Styling follows the stone/coral
design tokens used across the Skills page.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(app/ui): SkillResourcePreview size-gated viewer + specs (#681)

Presentational component that fetches a single bundled resource via
`skillsApi.readSkillResource`. Backend caps payloads at 128 KB and
either returns UTF-8 text or a plain error string, so the preview pane
has three visual states: loading, error, success.

- On error (e.g. "path escape", ">128KB", "non-UTF-8"), renders the
  backend message verbatim in a coral panel.
- On success, renders a monospace pre block with the byte count in the
  footer.
- `key={id:path}` on the mount site (in SkillDetailDrawer, next commit)
  drives a remount when the selected resource changes — so no
  setState-in-effect hack is needed to reset loading state.

Vitest specs cover: loading state, success rendering with byte footer,
error rendering for traversal / oversize / encoding strings, cancelled
fetch guard on unmount.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(app/ui): SkillDetailDrawer right-side detail panel + specs (#681)

Slide-in right-hand drawer that displays frontmatter metadata
(description, version, author, license, tags, allowed_tools) and hosts
SkillResourceTree + SkillResourcePreview. Opened by clicking a skill
card on the Skills page (wired in the next commit).

- Focus management: on mount, focuses the close button via
  `window.requestAnimationFrame` and restores the previously focused
  element on unmount.
- Esc + backdrop click dismiss.
- Preview pane is conditionally rendered and keyed on
  `${skill.id}:${selectedResource}` so changing the selected resource
  remounts the previewer (avoids setState-in-effect pattern).

Vitest specs cover: render with frontmatter, resource tree click opens
preview, close button / Esc / backdrop dismiss paths, focus
restoration, empty-resources case.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(app/ui): open SkillDetailDrawer on skill card click (#681)

Wires the Skills page to the new drawer: clicking a skill card sets
`selectedSkill` state, which mounts `SkillDetailDrawer`. Dismissing the
drawer clears the state. Cards gain an explicit "View details"
affordance for discoverability.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(skills/core): add skills.create RPC for scaffolded SKILL.md authoring (#681)

Adds `create_skill` in ops.rs plus the `skills.create` controller + handler
in schemas.rs. Writes a minimal SKILL.md (with optional license/author/tags/
allowed-tools frontmatter) under the selected scope, scaffolds scripts/
references/assets subdirs, and re-discovers the skill to return the parsed
SkillSummary. Legacy scope rejects; Project scope requires the trust marker;
User scope is always allowed when a home directory is available.

Hardened against path traversal the same way read_skill_resource is:
canonicalize the scope root, canonicalize the target dir, reject unless the
target starts with the root. Slug derivation is ASCII-only (collapse whitespace/
-/_ to a single hyphen, drop other chars, trim hyphens, enforce MAX_NAME_LEN).

Tests (hermetic via create_skill_inner):
- user-scope happy path (slug, metadata, SKILL.md on disk, subdirs)
- slug collision rejection
- invalid name (no alphanumerics) rejection
- project-scope without trust marker rejection
- project-scope with trust marker happy path
- legacy-scope rejection
- empty-description rejection
- slugify edge cases

Closes part of #681 (backend scope for create flow).

* feat(skills/core): add skills.install_from_url RPC via npx skills add (#681)

Introduces `install_skill_from_url(url, timeout_secs?)` — a JSON-RPC method
that shells out to `npx --yes skills add <url>` under the managed Node
runtime so the UI can install published SKILL.md packages directly.

Security posture:
- https scheme only (no http, file, ssh, git+https…)
- Rejects `localhost`, `*.localhost`, `*.local`, RFC1918 private IPv4,
  loopback, link-local, multicast, broadcast, unspecified, 100.64/10 CGN,
  0.0.0.0/8, and IPv6 loopback/unspecified/multicast, fc00::/7 ULA,
  fe80::/10 link-local. Explicitly covers 169.254.169.254 cloud metadata.
- Trims + caps URL at 2048 chars; parses with the `url` crate.
- IPv6 brackets stripped from `host_str()` before address parse.

Process posture:
- Reuses `NodeBootstrap` so the managed toolchain resolves first.
- `env_clear()` + explicit PATH injection (bootstrap bin_dir first) + a
  narrow safe-env allow-list (HOME, TERM, LANG, LC_ALL, LC_CTYPE, USER,
  SHELL, TMPDIR). Matches the npm_exec pattern from #723.
- Default 60s wall-clock timeout, capped at 600s.
- Captures stdout/stderr; returns both on success or failure.
- Diff-based `new_skills`: snapshots discovered skills pre-install and
  reports slugs that appear post-install.

Surface:
- JSON-RPC: `openhuman.skills_install_from_url`
  params:  { url: string, timeout_secs?: number }
  result:  { url, stdout, stderr, new_skills[] }
- Wired into `all_skills_controller_schemas()` and
  `all_skills_registered_controllers()`.

Tests (5 new unit tests, all pass — 51/51 in skills::):
- validate_install_url_accepts_public_https
- validate_install_url_rejects_non_https_scheme (http, file, ftp, ssh,
  git+https, javascript)
- validate_install_url_rejects_empty_and_oversized
- validate_install_url_rejects_private_and_loopback (20 URLs inc. CGN,
  cloud metadata, IPv6 ULA/link-local/loopback/multicast)
- validate_install_url_rejects_malformed (missing scheme, empty host,
  non-https scheme, unparseable bracketed host)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(app/api): skillsApi.createSkill + installSkillFromUrl wrappers (#681)

Adds typed frontend wrappers for the two new skill-authoring RPC methods:

- `skillsApi.createSkill(input)` — scaffolds a new SKILL.md skill via
  `openhuman.skills_create`. Accepts camelCase `allowedTools` and rekeys
  it to the `allowed-tools` spelling the SKILL.md frontmatter convention
  expects, matching `SkillsCreateParams` in `src/openhuman/skills/schemas.rs`.
  Optional fields are only sent when explicitly provided so the Rust
  `#[serde(default)]` defaults apply cleanly.

- `skillsApi.installSkillFromUrl(input)` — installs a published skill
  package via `openhuman.skills_install_from_url`. Accepts camelCase
  `timeoutSecs` and rekeys it to `timeout_secs`. Normalizes the response
  (snake_case `new_skills` -> camelCase `newSkills`, missing list -> []).

Both wrappers reuse the existing `unwrapEnvelope` helper so they
tolerate either a bare RPC payload or the `{ data: … }` envelope some
transports emit.

Adds `CreateSkillInput`, `InstallSkillFromUrlInput`, and
`InstallSkillFromUrlResult` type exports for downstream modal components.

Tests (vitest, 6 new specs, all pass):
- createSkill forwards inputs and rekeys allowedTools
- createSkill omits optional fields when absent
- createSkill unwraps envelope responses
- installSkillFromUrl forwards url and rekeys timeoutSecs
- installSkillFromUrl omits timeout_secs + defaults newSkills to []
- installSkillFromUrl unwraps envelope responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(app/ui): CreateSkillModal for scaffolding SKILL.md skills (#681)

Adds a centered white modal that scaffolds a new SKILL.md skill via
`skillsApi.createSkill`, matching the settings-modal design rules
(520px desktop, 16px radius, backdrop+blur, Escape/click-out to close,
focus capture).

Form fields mirror the Rust `SkillsCreateParams` schema:
  - name (required) — display name, also slugified into the on-disk
    directory; a live slug preview surfaces what will hit disk
  - description (required) — short prose; written as the
    `description:` field in the generated YAML frontmatter
  - scope (user | project radio) — `legacy` is hidden because that
    layout is read-only and being phased out
  - license (optional) — free-form SPDX-style string
  - author (optional)
  - tags (optional, CSV) — normalised client-side; empty entries dropped
  - allowedTools (optional, CSV) — rekeyed to `allowed-tools` on the
    JSON-RPC wire by `skillsApi.createSkill`

The slug preview mirrors `slugify_skill_name` on the Rust side
(lowercase ASCII alnum + `-`, collapse repeats, trim edge hyphens) so
the user sees what the Rust slugifier will produce; the Rust side stays
authoritative when the skill is persisted.

On success `onCreated(skill)` fires with the freshly-discovered
`SkillSummary`, letting the parent grid insert the new row without a
full refetch. On failure the Rust error string is surfaced verbatim in
a coral-styled alert and the submit button re-enables.

Vitest specs cover: required-field rendering, live slug preview,
submit-disabled gating, Escape close, wire-format rekey of
`allowedTools` → `'allowed-tools'`, `onCreated` dispatch, and
error-banner recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(app/ui): InstallSkillDialog for npx skills add <url> (#681)

Adds a centered white modal that installs a published skill package
via `skillsApi.installSkillFromUrl`. The Rust side shells out to
`npx --yes skills add <url>` under the managed Node toolchain, with
an allow-list on the URL (https only, no private/loopback/link-local/
multicast/cloud-metadata hosts) and a wall-clock timeout (default 60s,
max 600s).

UI contract:
  - Single URL input plus optional timeout in seconds.
  - Client-side `isLikelyValidUrl` fails fast on non-https URLs so the
    user doesn't pay a round-trip for shape errors the Rust side would
    reject anyway; the Rust side remains authoritative.
  - Timeout field validates `1 <= n <= 600` client-side to mirror the
    server-side clamp range.
  - While the RPC is in flight we render a spinner with "Running
    `npx skills add`…" copy and disable close / backdrop dismiss so we
    don't orphan the subprocess.
  - On success we surface the list of `newSkills` (ids that appeared
    post-install) plus captured stdout/stderr panes inside collapsible
    <details> elements, then hand the full result back to the caller
    via `onInstalled` so the parent can refetch the skills list and
    auto-select the new row.
  - On failure the Rust error string is rendered verbatim in a coral
    alert and the submit button re-enables.

Vitest specs cover: required-field rendering, URL shape gating (empty,
malformed, http://, https://), timeout range validation, `timeoutSecs`
forwarding on submit, success panel with newSkills rendering, blank
timeout omitted from payload, and error-banner recovery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(app/ui): wire New skill + Install from URL into Skills page (#681)

Adds a header row on the Skills page with two buttons:
  - **New skill** → opens `CreateSkillModal`
  - **Install from URL** → opens `InstallSkillDialog`

Extracts the existing `listSkills` effect into a reusable
`refreshDiscoveredSkills` helper so both new flows can reconcile their
results against the freshly-discovered `SkillSummary` rows rather than
relying on the optimistic payload from the RPC alone.

Create flow:
  - Optimistically appends the returned `SkillSummary` to
    `discoveredSkills` (dedupe by id).
  - Auto-opens the detail drawer for the new skill so the user lands in
    context — matches the install flow's UX.
  - Follows up with `refreshDiscoveredSkills()` so version/author/
    warnings picked up by the Rust discoverer end up in state too.

Install flow:
  - Always refreshes the list (the install can add multiple skills if
    the package declares several).
  - Auto-opens the detail drawer for the first newly-installed skill
    when at least one id is reported back; otherwise leaves the grid in
    its refreshed state.

Both buttons sit in a flush `max-w-lg` header above the existing
search bar, styled consistent with `UnifiedSkillCard` CTAs — ocean
primary for "New skill" (positive action), neutral stone for "Install
from URL" (secondary).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(skills/core): direct SKILL.md fetch replaces npx skills add (#681)

Installer no longer shells out to the vercel-labs/skills CLI. It now fetches
SKILL.md over HTTPS, validates YAML frontmatter, and writes into the user's
skills dir. Size cap (1 MiB), timeout clamp (1-600s), GitHub blob->raw URL
normalization, and path-traversal guards are covered by unit tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(app/ui): install dialog copy + categorized errors for direct fetch (#681)

Dialog subtitle, helper text, and in-flight indicator reflect the new direct
SKILL.md fetch flow. Errors from the core are categorized into friendly titles
(URL rejected, too large, timeout, parse failure, already installed, write
failed) with the raw backend message tucked under a details disclosure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(app/ui): dialog specs cover direct-fetch fixtures + error categorization (#681)

Fixtures updated to raw GitHub SKILL.md URLs. New cases assert the
categorization helper surfaces the right title for invalid SKILL.md, unsupported
URL form, and unknown backend errors (raw text hidden under details).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills/core): install_from_url writes to user scope (#681)

Project scope (`<ws>/.openhuman/skills/`) is gated on a `<ws>/.openhuman/trust`
marker that the workspace rarely has, so freshly-installed skills were
invisible to `skills.list` until the user opted the workspace into trust.
Route installs to `~/.openhuman/skills/<slug>` — the user-scope root that
`discover_skills` always scans — so "Install from URL" surfaces the new
skill immediately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(skills): align install_from_url docs with direct-fetch impl (#681)

`install_from_url` stopped shelling out to `npx --yes skills add <url>` when
it was rewritten to fetch SKILL.md over HTTPS directly, but schema
descriptions and SDK wrappers still described the old subprocess flow. Fix
the module-level rustdoc, the JSON-RPC schema `description`/`comment`
fields, and the TS client wrapper doc comments so the surface documents
what actually runs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills/core): DNS-to-private-IP SSRF guard + install rollback (#681)

Two fixes surfaced by CodeRabbit review on PR #740:

* `validate_install_url` only inspected literal-IP hosts, so a
  public-looking hostname like `evil.example.com` with an A record
  pointing at `127.0.0.1` / `169.254.x` / etc. would still be handed to
  `reqwest`. Resolve the host via `tokio::net::lookup_host` before the
  GET and reject if any returned address falls in loopback / private /
  link-local / multicast / unspecified ranges. Document the remaining
  DNS-rebinding gap (pinning to a `SocketAddr` + custom reqwest
  resolver is tracked separately).
* If `std::fs::write` or `std::fs::rename` fails after `create_dir_all`
  succeeded, the empty/partial target directory used to survive and
  permanently block retries under the same slug. Wrap the write+rename
  in a rollback that removes the temp file + the just-created directory
  on failure (best-effort; cleanup errors are logged and the original
  write error is surfaced).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style(skills): cargo fmt break long Host::Ipv6 conditional (#681)

CI's cargo fmt (stable) rewraps the long `.map(..).unwrap_or(false)` chain
that passed local fmt. Apply the break so the pre-push hook and the
upstream lint job agree.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:45:12 -07:00
2026-04-21 14:14:15 +00:00
2026-04-22 04:42:12 +00:00
2026-02-20 13:03:15 +04:00
2026-02-20 13:03:15 +04:00

OpenHuman

The age of super intelligence is here. OpenHuman is your Personal AI super intelligence. Private, Simple and extremely powerful.

DiscordRedditX/TwitterDocs

Early Beta Platforms: desktop only Latest Release

The Tet

"The Tet. What a brilliant machine" — Morgan Freeman as he reminisces about alien superintelligence in the movie Oblivion

Early Beta — Under active development. Expect rough edges.

To install or get started, either download from the website over at tinyhumans.ai/openhuman or run

# For MacOS/Linux
curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash

# For Windows
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex

What is OpenHuman?

OpenHuman is an open-source agentic assistant that is designed to integrate with you in your daily life. Here's what makes OpenHuman special:

  • Simple, UI-first — A clean desktop experience and short onboarding paths so you can go from install to a working agent in a few clicks, without a config-first setup. You don't need a terminal to run OpenHuman.

  • One subscription, many providers — You only need one account to get access to many agentic APIs (AI Models, Search, Webhooks/Tunnels and other 3rd party APIs etc..), simplifying the experience to get a powerful agent going.

  • Rich Skills — Plug into Gmail, Slack, Notion, and the rest of your stack via rich, feature-backed skills. Connections are typically one click through setup wizards instead of wiring APIs by hand. Workflow data is kept on device, encrypted locally, and treated as yours: encryption and sensitive context stay on your machine. Webhooks give instant feedback into the agent when external systems or skills emit events, so the loop stays tight without constant polling.

  • Local knowledge base — Built from your data and your activity. How you work across tools, sessions, and connected services—so the agent gets rich, workflow-aware context, not a one-off chat transcript. Everything is stored on your machine and compounding over time without becoming a cloud dossier. Channels, skills and ongoing conversations feed the same loop so day-to-day context does not reset every session.

  • Local AI model — The Rust core exposes local AI paths (and the desktop bundle can ship local/bundled runners where applicable) for the workloads above—vision snippets, speech helpers, summarization, tooling—so sensitive steps can stay off the cloud when you choose.

  • Deep desktop integrations — OpenHuman is a native desktop assistant, not a web-only chat: memory-aware keyboard autocomplete, voice (STT listening and TTS replies), screen intelligence that understands what is on screen and feeds your local context, plus windowing and OS-level permissions—so the agent meets you on the machine, not trapped in a browser tab.

Architecture: docs/ARCHITECTURE.md. Contributor orientation: CONTRIBUTING.md. Running from source: docs/install.md.

Highlights

  • Neocortex — local-first knowledge base that learns from your data and activity, compounding context across tools and sessions.
  • The Subconscious — background self-learning loops that turn everyday usage into workflow-aware intelligence.
  • Screen Intelligence — the agent sees what's on your screen and feeds it into your local context.
  • Inline Autocomplete — memory-aware keyboard autocomplete anywhere on your desktop.
  • Voice (STT + TTS) — speak to OpenHuman and hear it reply, natively on the desktop.
  • Skills & Integrations — one-click skills for Gmail, Slack, Notion and the rest of your stack, with local encryption and webhooks for instant feedback.
  • Messaging Channels — inbound/outbound across the channels you already use, routed through your agent.
  • Teams & Organizations — shared workspaces for collaborating with an agent across a team.
  • Rewards & Achievements — gamified progression as your agent grows with you.
  • Privacy & Security — workflow data stays on device, encrypted locally, and treated as yours.

OpenHuman vs other agents

High-level comparison (products evolve—verify against each vendor). OpenHuman is built to minimize vendor sprawl, keep workflow knowledge on-device, and ship deep desktop features—not only chat.

Claude Code/Cowork OpenClaw Hermes Agent OpenHuman
Open-source 🚫 Proprietary MIT MIT GNU
Simple to start Desktop + CLI ⚠️ Terminal-first ⚠️ Terminal-first Clean UI, minutes
Cost ⚠️ Sub + add-ons ⚠️ BYO models ⚠️ BYO models Local-friendly
Memory & KB Chat-scoped ⚠️ Plugin-reliant Self-learning 🚀 Local KB + learning
API sprawl 🚫 Extra keys 🚫 BYOK 🚫 Multi-vendor One account
Extensibility MCP SKILL.md SKILL.md 🚀 Rich Skills
Desktop integration ⚠️ Basic ⚠️ Light ⚠️ Light STT/TTS/screen/more

Contributors Hall of Fame

Show some love and end up in the hall of fame

OpenHuman contributors
S
Description
No description provided
Readme GPL-3.0
214 MiB
Languages
Rust 59.1%
TypeScript 37.9%
JavaScript 1.6%
Shell 1.2%
CSS 0.1%