maleta.dev
v0.2.2
Published
Command-line installer for Maleta selections: validate a maleta.json and apply its skills and plugins to Claude Code, Codex, and agent CLIs.
Maintainers
Readme
maleta
Local-first command-line tool that reads a maleta.json document and installs
the skills and plugins it selects for Claude Code, Codex, and agent CLIs. The
local commands (init, validate, install, sync) need no server and no
account, and send Maleta nothing back. The optional device commands documented
below (which include prune) and the tray's periodic update check do contact a
host. No
telemetry, and no runtime dependencies. Node.js 20 or newer is required.
Install
npm install -g maleta.devThe installed command is maleta. A one-off run without installing:
npx maleta.dev --helpQuickstart
maleta init # create ./maleta.json
maleta validate # parse and check the document
maleta install # materialize its skills and plugins
maleta sync # apply later edits additively
maleta tray # start the system tray iconCommands
maleta init [--file path]creates a schema version 1 document (default./maleta.json). It uses the local host astargetOs, usesallas the compatibility default fortargetTool, and refuses to overwrite a file.maleta validate [--file path]parses the document and prints its name, schema, skill count, plugin count, and targets. It applies the same document rulesinstalldoes, including the skill-name charset and destination collisions, sovalidateandinstall --dry-runagree on every document.maleta install [--file path] [--tool all|claude|codex|agents] [--dry-run]validates first, builds one install plan, resolves selected public GitHub skills, reads inline skills from the document, then materializes the plan and the declared plugins locally.maleta sync [--file path] [--tool all|claude|codex|agents] [--dry-run]uses the same plan and executor additively: unrelated files are retained, unchanged skill files are not rewritten, and a second aligned run reportssync: no relevant changes.maleta traystarts the maleta.dev tray icon in the background and returns. Its menu shows● maleta.dev running, anOpen at startupcheckbox (login item on Windows, macOS, and Linux), andQuit. The native binary for Windows x64, macOS (x64/arm64), and Linux (x64/arm64) ships inside this package undernative/; Linux needs glibc 2.35+ and a StatusNotifierItem tray (KDE, most panels; GNOME with the AppIndicator extension). At startup and every 24 hours, the tray checks thelatestupdate manifest onunpkg.com(that host receives the request, and with it this machine's IP address and user agent), verifies the SHA-256 digest of a newer binary, replaces itself, and restarts. Network, integrity, or permission failures keep the installed version. Each network request times out after eight seconds.
targetTool and targetOs remain document defaults. --tool overrides only the
execution target and never rewrites the JSON; targetOs must match the local
host, so a mismatch is rejected. --dry-run reports the same planned actions
without writing files, making network requests, or running plugins. Builtin
artifacts are copied to ~/.claude/skills/ or ~/.agents/skills/, and the
packaged assets resolve relative to the installed module, so builtin installs
work from any directory. Plugins are invoked with fixed command arguments only;
caller-provided shell snippets are never executed.
Flags also accept --file=path and --tool=value. A repeated --file or
--tool, a stray positional word, or --tool with no value is a usage error
(exit 2). maleta help prints the same help as maleta --help, and a command
name within edit distance 2 of a real one fails with
unknown command '<x>'. Did you mean '<y>'?.
install and sync also keep a local record of what they wrote in
<home>/.maleta/local-state.json, keyed by the document's absolute path. A file
that differs and was not last written by the CLI is kept with a warning instead
of being overwritten, and maleta prune uses the record to remove a skill once
the recorded document stops declaring it.
Device commands (optional account)
A computer can follow one Cloud Maleta of your account and apply its changes
while maleta watch runs in the foreground. The local commands above never
read a credential.
maleta login [--name label]authorizes this computer with a short code.maleta attach <name|id>follows one Maleta of the account.maleta watch [--interval seconds] [--once]applies saved changes; it never deletes anything.maleta statusprints what this computer follows and what is still pending.maleta prune [--dry-run] [--yes]removes only files this CLI wrote and no longer needs, bounded to~/.claude/skills/<name>and~/.agents/skills/<name>. A missing record is not an error; a corrupt one exits 1 after quarantine. Local installs recorded in.maleta/local-state.jsonare pruned the same way.maleta detachstops following the Maleta bound to this computer.maleta logoutrevokes this computer's credential.
GitHub skills
A github source is resolved from the declared public repo, path, and
optional ref through the GitHub Contents API. A path whose final basename is
SKILL.md (case-insensitive) resolves its containing directory, and the
resolved skill root must contain a file named exactly SKILL.md — uppercase.
Files are copied as opaque data — no SKILL.md, README, hook, script, or
repository code is executed — and maleta.json is never rewritten. An omitted
ref uses the default branch and prints a mutable-ref warning; refs that are
not full 40-character commit SHAs warn as well. Resolution is bounded (10 MiB
per response, 5 MiB per skill, 200 files per skill, a 1000-entry
directory-listing cap that fails the run, 10 s timeout), rejects traversal,
absolute paths, backslashes, and encoded source paths, and treats a
destination-name collision as an error instead of an overwrite. Failures are
named distinctively: a 401 reports
GitHub authentication failed; check GITHUB_TOKEN, a rate-limited 403 reports
the reset time plus set GITHUB_TOKEN to increase the limit, another 403
reports GitHub access denied; check repository access and GITHUB_TOKEN, and a
404 on a private repository suggests if this is a private repo, set GITHUB_TOKEN.
The CLI reads rate-limit headers to name the failure but never waits or retries.
Inline skills
An inline source carries its files inside maleta.json:
{
"name": "reviewer-custom",
"source": {
"type": "inline",
"name": "reviewer-custom",
"files": [
{ "path": "SKILL.md", "content": "# Reviewer\n\nReview before shipping.\n" }
]
}
}The source contains 1 to 200 files with unique, safe relative POSIX paths and
must include SKILL.md at the skill root. The CLI reads these files directly
from the document and makes no GitHub request. Their content is opaque data:
it is written as UTF-8 and never interpreted or executed.
The 1 MiB limit on the complete maleta.json document is the inline content
budget, including JSON structure and escaping; there is no separate inline
byte allowance. A changed install reports
[ok] install inline:<name> -> <dest>, while a dry run reports
[dry-run] would install inline:<name> -> <dest>.
Inline files use the same local ownership record as other skills. A locally
changed file is preserved with
[warn] kept <path>: changed locally; delete it to take the new version.
After the recorded document drops the inline skill, maleta prune removes it
under the same ownership and managed-root checks as any other resource.
Environment variables
All optional:
| Variable | Effect |
| --- | --- |
| GITHUB_TOKEN | Authenticates GitHub requests, including private repositories. |
| MALETA_DEVICE_TOKEN | Use this token instead of credentials.json (CI, headless). Its identity comes from the session endpoint; a token for another device is refused with exit 3 instead of re-keying the computer. |
| MALETA_API_ORIGIN | Origin of the app surface; defaults to https://app.maleta.dev. Refuses to re-point a stored file credential: a mismatch exits 1 and names both origins. |
| MALETA_ALLOW_INSECURE_ORIGIN | 1 allows a non-https origin, for local development only. |
| MALETA_NO_BROWSER | 1 never opens a browser during login. |
| MALETA_CLI_HOME | Overrides the home directory used for destinations, credentials, and state (tests, CI). Empty is treated as unset; a relative value is resolved against the cwd. |
GITHUB_TOKEN is read only from the process environment. It is never persisted
or printed and is sent only to GitHub hosts. Device and GitHub tokens are also
scrubbed from any server message the CLI echoes, printed as [redacted].
Exit codes
0— command completed successfully; unsupported built-in/plugin entries may be reported as skipped.1— missing/invalid Maleta, an installation failure, an unsupported filesystem operation, an unreadable--fileinput (not a regular file, over 1 MiB, UTF-16), a corrupt state record duringprune, or an interrupted command ([error] interrupted).2— invalid CLI usage or option.3— a refusal that needs a decision:initfound an existing file,attachfound this computer already bound,loginfound a stored credential orMALETA_DEVICE_TOKENset,watch/prunefound anotherwatchorprunerunning,prunefound an environment credential for another device, ortrayfound a live instance.4— the document'stargetOscannot execute on the local host, the native tray binary is missing or lacks permission to execute, the tray exits before becoming ready (or does not become ready within 10 s), or its pid file cannot be created or resolved.
Links
- Website: https://maleta.dev
- App: https://app.maleta.dev
- Issues and bug reports: https://github.com/diego-ruas/maleta.dev/issues
