agentsclimarketplace

Jo

Skill petr-korobeinikov/skills/skills/jo

Claude Code agent skills. Plain markdown. Highly opinionated.

Install
npx -y skills add petr-korobeinikov/skills --skill jo

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Prefers `jo` (https://github.com/jpmens/jo) for constructing JSON in shell snippets over hand-written quoted JSON literals. Removes quote-escaping pitfalls, makes variable interpolation natural, and lets `jo` infer field types from values (with explicit coercion when needed).

SKILL.md

8.5 KB, as published. Nobody here has run it

jo

Build JSON in shell snippets with jo, not by hand-typing a quoted JSON literal. jo handles JSON quoting internally, so shell variables interpolate cleanly and you never juggle nested single/double quotes.

Authoritative documentation: https://github.com/jpmens/jo (README + jo.md man page). Verify any non-trivial form against the manual before relying on it.

Why

  • Hand-written JSON in a shell string is a quote-escaping minefield. Inserting $VAR into '{"user":"x"}' either breaks the single quotes or forces an awkward '"$VAR"' pattern that is hard to read and easy to mistype.
  • Typos in inline JSON (missing comma, stray quote, trailing comma) fail at the server, not at parse time — hard to spot in a long single-line payload.
  • jo figures out types from the value: numbers, booleans, null, nested objects, and arrays are detected automatically; override with -s/-n/-b per word when the guess would be wrong (e.g. a numeric ID that must stay a string).
  • jo composes: shell variables, command substitution, bracket-path shorthands, and nested jo calls all work without re-escaping.

How jo types a value

Per the man page, the value pipeline is:

  1. Try to parse the value as JSON. 5 → number, true/false → boolean, null → null, {...} / [...] → embedded object/array, strings that look numeric but aren't valid floats (e.g. 3.14159.26) → string.
  2. If JSON parse fails, check special value prefixes: @file, %file, :file (see Reading from files below).
  3. Otherwise it's a literal string.

A missing/empty value (k=) becomes null. Pass -B to disable detection of the literal strings true/false/null. Globally, -n drops keys with empty values (not to be confused with the per-word -n coercion after --).

Direct substitutions

Don'tDo
'{"name":"alice"}'"$(jo name=alice)"
'{"name":"'"$USER"'"}'"$(jo name="$USER")"
'{"count":5,"active":true}'"$(jo count=5 active=true)"
'{"value":null}'"$(jo value=null)" (or "$(jo value=)")
'{"tags":["a","b","c"]}'"$(jo tags="$(jo -a a b c)")"
'{"point":[1,2]}'"$(jo 'point[]=1' 'point[]=2')"
'{"geo":{"lat":10,"lon":20}}'"$(jo 'geo[lat]=10' 'geo[lon]=20')"
'{"user":{"id":1,"name":"alice"}}'"$(jo user="$(jo id=1 name=alice)")"
'{"code":"12345"}' (string ID)"$(jo -- -s code=12345)"

Bracket-path forms (key[]=, key[sub]=) are one level deep only'a[b][c]=1' silently drops [c] and produces {"a":{"b":1}}. For deeper nesting, use nested jo via command substitution.

Syntax cheatsheet

Value forms

  • k=v — JSON-parsed then prefix-checked then literal string (see How jo types a value).
  • k@v — boolean from a truthy value (T/t or non-zero numeric → true, else false); handy for flag@$ENABLED.
  • k=@file — load file contents as a string value.
  • k=%file — load file as base64-encoded string.
  • k=:file — parse file as JSON and embed.
  • k:=file or k:=- — same as k=:file but as an operator; - reads JSON from stdin.

To use a literal @, %, or : at the start of a string value, escape it with \: twitter='\@jpmens', vimcmd='\:split', uri='\\%20'.

Bracket paths (one level)

  • 'name[]=v1' 'name[]=v2' — appends to the name array.
  • 'obj[k]=v' — creates {"obj":{"k":v}}.
  • Combine -d. (path separator) for dot notation: jo -d. geo.lat=10 geo.lon=20 is equivalent to jo 'geo[lat]=10' 'geo[lon]=20'.
  • Always quote bracket forms — unquoted [ / ] get caught by zsh globbing (and bash too, if any expansion matches).

Arrays

  • jo -a v1 v2 v3 — array of auto-typed values.
  • jo -a -- -s 123 hello -n "five" — per-word coercion inside an array (here: ["123","hello",4]-n on a non-numeric string yields its length).

Type coercion

  • jo -- -s k=v — force string. Use for numeric IDs, version strings, phone numbers, ZIPs.
  • jo -- -n k=v — force number (non-numeric strings become their length).
  • jo -- -b k=v — force boolean (empty → false, anything else → true).
  • -- is mandatory before the word list whenever the first word starts with -; pass it always for consistency with the manual.

Global options

The two most-used:

  • -p — pretty-print (drop for production payloads).
  • -a — emit an array instead of an object.

For the full option list (-B, -D, -n global, -d sep, -f file, -e, -o, -v/-V, plus per-word coercion semantics after --) see references/options.md.

Idioms

curl POST with a body:

curl -X POST https://api.example.com/v1/users \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "$(jo name="$NAME" email="$EMAIL" active=true)"

Bracket shorthand for one-level nesting (no nested jo substitution needed):

curl -X POST https://api.example.com/v1/orders \
    -H "Authorization: Bearer $TOKEN" \
    -d "$(jo \
        'customer[id]='"$CUSTOMER_ID" \
        'customer[name]='"$CUSTOMER_NAME" \
        'items[]='"$SKU_A" \
        'items[]='"$SKU_B")"

Nested deeper than one level — build via command substitution (or stash in a variable):

payload=$(jo \
    name="$NAME" \
    address="$(jo street="$STREET" city="$CITY")" \
    tags="$(jo -a admin user)")

curl -X POST -d "$payload" https://api.example.com/v1/users

kubectl patch:

kubectl patch deployment web \
    --type=merge \
    --patch "$(jo spec="$(jo replicas=3)")"

Numeric ID that must stay a string (otherwise jo emits "id":12345 as a number):

curl -X POST -d "$(jo -- -s id=12345 name="$NAME")" \
    https://api.example.com/v1/users

Modify an existing JSON document:

curl -sSL https://api.example.com/v1/config \
    | jo -f - updated_at="$(date -Iseconds)" version="$NEW_VERSION"

When NOT to use jo

Large, fixed JSON document

A heredoc with literal JSON is clearer than a long chain of jo calls, and the JSON can be copy-pasted from API docs without translation:

curl -X POST -d @- https://api.example.com/v1/config <<'JSON'
{
  "feature_flags": {
    "alpha": true,
    "beta": false
  },
  "limits": {
    "rps": 100,
    "burst": 200
  }
}
JSON

jo not available on the host

Some minimal CI images or locked-down servers lack it. Fall back to a heredoc; do not fall back to fragile inline quoted JSON.

Installing jo

  • macOS: brew install jo or sudo port install jo
  • Debian/Ubuntu: apt install jo
  • Fedora: dnf install jo
  • Alpine: apk add jo
  • Gentoo: emerge jo
  • Arch: pacman -S jo (in extra)
  • Snap: snap install jo
  • Windows: scoop install jo
  • Docker: docker run --rm jpmens/jo ...
  • From source: https://github.com/jpmens/jo

jo is not in the default aqua/mise short-name registry; on a mise-managed host install via the package manager or via ubi:jpmens/jo.

Scope

Applies anywhere a shell snippet emits JSON:

  • Bash tool invocations.
  • Snippets in SKILL.md / README.md / docs.
  • Scripts under scripts/.
  • Makefile recipes.
  • CI configs.
  • API smoke-test instructions.

If you encounter a hand-written inline JSON string while editing a file, replace it with the equivalent jo form as part of the same edit.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.