PluginsModsMake a mod

Make a mod

This page walks you through building a working mod from an empty folder. By the end you will have a small program that shows the state of the current turn in the footer, keeps a short history in a side pane, and posts a note when a turn runs long.

A mod needs only two things: a mod.json that describes it, and a program that reads and writes JSON lines. You can write the program in any language that can read standard input and write standard output. The example here uses plain sh and sed, so it runs on any Unix-like system without extra tools.

Before you start

  • Mods only run in interactive sessions. A syntic -p run never starts them.
  • Mods are read only from your own config folder, ~/.syntic/mods/. If you have set SYNTIC_CONFIG_DIR to an absolute path, the folder is mods/ inside that path instead.
  • A mod is off until you switch it on with syntic mod enable. Changes take effect from the next session.

Build the mod

Create the folder

The folder name is the mod’s name. It must be 1 to 40 characters of lowercase letters, digits and dashes, and it cannot start with a dash.

mkdir -p ~/.syntic/mods/turn-clock
cd ~/.syntic/mods/turn-clock

Write mod.json

{
  "name": "turn-clock",
  "version": "0.1.0",
  "description": "Footer clock for the current turn and a pane with recent turn times",
  "command": ["sh", "run.sh"],
  "events": ["turn.start", "turn.end"],
  "ui": ["status", "pane"],
  "permissions": ["notes"]
}

The name must match the folder name exactly. command says how to start the program; here it runs sh (found on a cleaned PATH) with run.sh from the mod folder as its argument. events, ui and permissions declare everything the mod may hear, draw and ask for. Anything not listed is refused at run time. Every field is described in the mod.json reference.

Write the program

Save this as run.sh next to mod.json:

#!/bin/sh
# Show the current turn in the footer, keep the last five turn times
# in a pane, and post a note when a turn takes two minutes or more.
 
LONG=120
history=""
 
say() { printf '%s\n' "$1"; }
num() { printf '%s\n' "$line" | sed -n "s/.*\"$1\":\([0-9][0-9]*\).*/\1/p"; }
 
show_pane() {
  rows=""
  for item in $history; do
    rows="$rows${rows:+,}\"turn ${item%%:*}: ${item#*:}s\""
  done
  say "{\"type\":\"draw\",\"slot\":\"pane\",\"title\":\"Turn times\",\"lines\":[$rows]}"
}
 
while IFS= read -r line; do
  case "$line" in
    *'"type":"hello"'*)
      say '{"type":"draw","slot":"status","text":"turn clock ready"}' ;;
    *'"event":"turn.start"'*)
      say "{\"type\":\"draw\",\"slot\":\"status\",\"text\":\"turn $(num turn) running\"}" ;;
    *'"event":"turn.end"'*)
      t=$(num turn); s=$(num seconds); [ -n "$s" ] || s="?"
      say "{\"type\":\"draw\",\"slot\":\"status\",\"text\":\"turn $t took ${s}s\"}"
      history=$(printf '%s\n' "$t:$s" $history | head -n 5 | tr '\n' ' ')
      show_pane
      if [ "$s" != "?" ] && [ "$s" -ge "$LONG" ]; then
        say "{\"type\":\"call\",\"id\":$t,\"method\":\"note.post\",\"params\":{\"text\":\"Turn $t took ${s}s.\"}}"
      fi ;;
    *'"type":"bye"'*)
      exit 0 ;;
  esac
done

The loop reads one message per line. The first message is always hello. After that, the mod gets an event line for each subscribed event and a result line for each call it made. When the session ends it gets bye and should exit.

Check that the mod is valid

syntic mod list

If the manifest has a problem, the mod shows as [broken] with the reason on the next line, for example an unknown event name or a folder name that does not match name. A valid mod shows as [off].

Switch it on

syntic mod enable turn-clock

The command prints what you are granting (here, notes), which slots the mod draws in and which events it hears. It also checks that the program in command can be found. Start a new interactive session to see the mod run.

How the example works

MomentWhat the mod sendsWhat you see
Start (hello)draw to statusturn clock ready in the footer
turn.startdraw to statusturn 3 running
turn.enddraw to status and paneturn 3 took 12s, plus a box titled “Turn times”
A turn of 120 seconds or morecall to note.post[mod turn-clock] Turn 3 took 130s. in the conversation
Session end (bye)nothing; exitsthe footer and pane are cleared

The note is only shown because the mod was granted notes. Without that grant, the call gets an error result and counts as a message the mod was not allowed to send, but drawing still works.

Pick a language

Any program that can be started from the mod folder works:

commandStarts
["sh", "run.sh"]A shell script in the mod folder
["python3", "main.py"]A Python script, with python3 looked up on the cleaned PATH
"./run"An executable file in the mod folder
["bin/turn-clock", "--quiet"]A compiled program inside the mod folder, with arguments
"/usr/local/bin/my-mod"A program at an absolute path

Whatever you choose, keep these rules in mind:

  • Write whole lines and flush. Each message must be one JSON object followed by a newline. Buffered output that never flushes means nothing is drawn.
  • Read all of standard input. Syntic Code keeps a small queue of messages for each mod. If your program stops reading, new events are dropped for it.
  • Do not print anything else to standard output. Stray text counts as a bad line. After more than 20 bad lines the mod is stopped for the session.
  • Use standard error or a file for logs. Standard error is thrown away, so write debug output to a file in $SYNTIC_MOD_DATA.

Keep working on a mod

Edit the files in place. The running session keeps the old program, so start a new session to pick up changes. You do not need to enable the mod again unless you added to permissions; in that case syntic mod list shows now also asks for: ..., and the new permission stays off until you run syntic mod enable again.

To switch a mod off:

syntic mod disable turn-clock

To remove it completely, disable it and delete its folder. Its private data folder lives at ~/.syntic/mod-data/<name>/; delete that too if you no longer need it.

Share a mod

There is no mod marketplace. To share a mod, share its folder (for example, as a git repository) and ask the other person to copy it into their own ~/.syntic/mods/ and run syntic mod enable themselves. A project repository cannot install or enable a mod for anyone.

⚠️

Before you enable a mod someone sent you, read its program. A mod runs with your user rights. Syntic Code limits what it can ask the session to do, not what it can do on your computer.

Next steps

  • Screen areas: sizes and behavior of the footer, band and pane.
  • Events: every event and its data.
  • Mod API: note.post and session.state.
  • Test a mod: drive your program by hand before you enable it.