lmsheet skill

Use lmsheet for small, durable spreadsheets in the authenticated lmctl user's private namespace. Data is stored as strings.

Setup

Always set the API base explicitly:

export LMSHEET_API_URL='https://lmctl.ai/lmsheet'

Use the shared lmctl appkey. The CLI resolves it from:

  1. LMCTL_APPKEY
  2. LMCTL_APPKEY_FILE
  3. ~/.config/lmctl/appkey.json

lmsheet login configures the shared default file. lmsheet whoami reports the target and credential source without revealing the key. Never send an owner ID: the API derives ownership from the verified appkey.

Addressing and semantics

CLI

lmsheet create contacts --cols "Name,Email,Age"
lmsheet sheets
lmsheet add-column contacts Country

lmsheet add-row contacts \
  --set A=Ada \
  --set B=ada@example.com \
  --set C=30

lmsheet add-row contacts --row 10 --set A=Grace
lmsheet read contacts
lmsheet read contacts --after 10 --limit 100

lmsheet update-row contacts 1 --set B=new@example.com --set C=31
lmsheet set-cell contacts C1 32
lmsheet clear-cell contacts B1
lmsheet delete-row contacts 1
lmsheet delete-sheet contacts

Quote shell arguments containing spaces. Each --set is LETTER=value; the value may contain additional = characters.

read prints a column-definition line, then tab-separated rows whose final field is a JSON cell map. It ends with next_after=ROW\thas_more=true|false. Pass next_after as --after to continue.

All commands accept --api URL. Otherwise LMSHEET_API_URL is required.

Raw HTTP

export LMCTL_APPKEY='replace-me'
auth=(-H "Authorization: Bearer $LMCTL_APPKEY")
json=(-H 'Content-Type: application/json')

Create:

curl -fsS -X POST "$LMSHEET_API_URL/sheets" \
  "${auth[@]}" "${json[@]}" \
  --data '{"name":"contacts","columns":["Name","Email"]}'

List sheets:

curl -fsS "$LMSHEET_API_URL/sheets" "${auth[@]}"

Add a column:

curl -fsS -X POST "$LMSHEET_API_URL/sheets/contacts/columns" \
  "${auth[@]}" "${json[@]}" --data '{"label":"Age"}'

Append a row:

curl -fsS -X POST "$LMSHEET_API_URL/sheets/contacts/rows" \
  "${auth[@]}" "${json[@]}" \
  --data '{"cells":{"A":"Ada","B":"ada@example.com","C":"30"}}'

Use "row_num":10 in that body for explicit placement. An occupied row returns 409 row_exists. Later appends continue after the highest row number in use, explicit rows included.

Read:

curl -fsS "$LMSHEET_API_URL/sheets/contacts/rows?after=0&limit=100" "${auth[@]}"

The response includes sheet metadata, rows, next_after, and has_more. Raw-HTTP clients must continue with after=next_after while has_more is true and tolerate an empty final page: a full page can be followed by a page with no rows. Treat next_after as an opaque position.

Update row cells:

curl -fsS -X PUT "$LMSHEET_API_URL/sheets/contacts/rows/1" \
  "${auth[@]}" "${json[@]}" \
  --data '{"cells":{"B":"new@example.com","C":"31"}}'

Set or clear one cell:

curl -fsS -X PUT "$LMSHEET_API_URL/sheets/contacts/cells/B1" \
  "${auth[@]}" "${json[@]}" --data '{"value":"new@example.com"}'

curl -fsS -X DELETE "$LMSHEET_API_URL/sheets/contacts/cells/B1" "${auth[@]}"

Delete a row or the entire sheet:

curl -fsS -X DELETE "$LMSHEET_API_URL/sheets/contacts/rows/1" "${auth[@]}"
curl -fsS -X DELETE "$LMSHEET_API_URL/sheets/contacts" "${auth[@]}"

Large sheet deletion is resumable. A response with "deleted":false means the server completed its bounded batch and the same DELETE should be repeated. The CLI performs these retries automatically.

Errors

Do not retry validation or authorization errors unchanged. Re-read before recovering from a concurrent update.

Verification scope

The API and CLI syntax above was checked against the tool source on 2026-10-19. The raw HTTP steps were also run end to end against https://lmctl.ai on 2026-10-20. Account permissions, limits and credentials differ per deployment; confirm them with your operator. Do not assume that access to one deployment grants access to another.