/ documentation

Schema

Root and source fields in maleta.json schema 1, normalization rules, and core validation messages.

The maleta.json file follows a single, explicit schema, currently version 1. Selection fields live at the top level: skills, plugins, targetTool and targetOs do not live inside a nested object. Every field is read by parseMaleta in packages/core/src/maleta.ts.

#Complete schema 1

The document below is the canonical valid example, with the GitHub source expanded across multiple lines as produced by serializeMaleta:

Valid maleta.json using schema 1
json
{
  "schemaVersion": 1,
  "id": "shared-fixture",
  "slug": "shared-fixture",
  "name": "Shared fixture",
  "description": "Builtin and GitHub selection used by site and CLI contract tests.",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "updatedAt": "2026-01-01T00:00:00.000Z",
  "skills": [
    {
      "id": "builtin:design-taste-frontend",
      "name": "design-taste-frontend",
      "source": { "type": "builtin", "name": "design-taste-frontend" }
    },
    {
      "id": "github:acme%2Fskills:skills%2Freviewer%2FSKILL.md@v1.2.0",
      "name": "reviewer",
      "source": { "type": "github", "repo": "acme/skills", "path": "skills/reviewer/SKILL.md", "ref": "v1.2.0" },
      "description": "Review code before shipping."
    }
  ],
  "plugins": ["frontend-design@claude-plugins-official"],
  "targetTool": "claude",
  "targetOs": "unix"
}

#Root fields

schemaVersion
1required

Literal 1. Any other value is rejected with Formato de Maleta não suportado — there is no migration between versions.

id
stringrequired

Maleta identifier, up to 128 characters. It is not derived from slug and does not need to be unique across files.

slug
stringrequired

Up to 96 characters, in the format palavra or palavra-palavra: Unicode letters and numbers only, with segments separated by a single hyphen.

name
stringrequired

Human-readable Maleta name, up to 80 characters. This is not the skill name: each skill has its own name, which defines the destination directory.

description
stringoptional

Free-form text up to 600 characters. It accepts line breaks; the other text fields do not.

createdAt
stringrequired

A string accepted by Date.parse, up to 64 characters. Usually ISO 8601.

updatedAt
stringrequired

The same rule as createdAt. maleta init writes the same instant to both fields.

skills
arrayrequired

Up to 500 entries, with no duplicate id values. Each entry is a { id, name, source, description? } object or a string, which is expanded into a builtin source with the same name.

plugins
string[]required

Up to 200 identifiers, each up to 200 characters, with no duplicates. Use [] for an empty list; the field is required in the input file.

targetTool
"all" | "claude" | "codex" | "agents"required

The selection's default target tool. This value is used when --tool is omitted at runtime.

targetOs
"windows" | "unix"required

Target operating system. It must match the host when running install and sync.

#source: union discriminated by type

Each item in skills[] points to a source. The id field provided in the file is discarded and recalculated by skillId(source): builtin:<name> or github:<repo>:<path>[@<ref>], with percent-encoded components.

#{ "type": "builtin", "name": "…" }

type
"builtin"required

A skill bundled with the local assets. This is the form used by a bare string in skills[], which is expanded into a builtin source with the same text.

name
stringrequired

It must be identical to the skill's name; any mismatch produces Fonte da skill "<x>" inconsistente. It is also the destination directory name and must match [A-Za-z0-9._-]+.

#{ "type": "github", "repo": "…", "path": "…", "ref": "…" }

type
"github"required

A skill resolved from a public repository at the declared revision.

repo
stringrequired

Accepts owner/name or an https://github.com/owner/name[.git] URL, which is normalized to owner/name. The result must match [A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+.

path
stringrequired

Relative path inside the repository, up to 240 characters. Rejects \, a leading slash, //, the segments . and .., empty segments, and any character outside [A-Za-z0-9._-]. A path ending in SKILL.md is reduced to its parent directory.

ref
stringoptional

Branch, tag, or SHA, up to 160 characters. Accepts [A-Za-z0-9._/-] and rejects \, //, and ... When omitted, the GitHub API resolves the repository's default branch.

#Normalization

  • Skills are sorted by id and plugins alphabetically; duplicate id values and plugin identifiers are discarded.
  • All text fields are passed through trim; fields containing control characters are rejected. Only description accepts line breaks.
  • Unknown top-level keys are silently discarded; a root selection key is an explicit error.
  • The file is read only as valid JSON up to 1 MiB in UTF-8 bytes. Above that limit, Arquivo de Maleta muito grande.
  • serializeMaleta re-emits the document with JSON.stringify(doc, null, 2) and a final newline, so parseMaleta(serializeMaleta(m)) preserves the fields and canonical order.

#Core validation messages

For maleta validate, the error is printed as [error] <caminho>: <mensagem> and the process exits with code 1. These are the possible messages:

Core validation messages
MessageCause
JSON de Maleta inválidoThe file content is not parseable JSON.
Arquivo de Maleta muito grandeThe input string exceeds 1 MiB in UTF-8 bytes.
Formato de Maleta não suportadoThe object is not a record or schemaVersion is not 1. There is no migration between versions.
Formato de Maleta inválido: os campos da seleção devem ficar no nível principalA selection key exists at the document root.
Maleta precisa expor skills, plugins, targetTool e targetOs no nível principalAt least one of slug, skills, plugins, targetTool, and targetOs is missing from the root.
Caminho da skill inválidosource.path violates the safe relative path rules.
Referência da skill inválidaref contains a forbidden character or exceeds 160 characters.
Repositório GitHub inválidorepo does not normalize to owner/name.
Fonte da skill "<x>" inconsistenteThe name of a builtin source does not match the skill's name.
Ferramenta de destino inválidaThe targetTool value is outside the four-value enum.
Sistema operacional inválidoThe targetOs value is neither windows nor unix.
Slug da Maleta inválidoThe slug field does not match hyphen-separated letters and numbers, or exceeds 96 characters.