• .claude/skills/javascript/SKILL.md

    From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Sat Jun 6 15:23:28 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/90570a43c9e774e639c3ab04
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    skills/javascript: document inkey timed input + inactivity model

    Add a "Timed / non-blocking key input, and the inactivity model"
    subsection to the input/output coverage: console.inkey(mode, timeout)
    semantics (timeout in ms; K_NONE returns "" vs K_NUL returns null on
    timeout; 1-char string on a key), the getkey-enforces-idle /
    inkey-doesn't trap, and the live console inactivity properties (max_getkey_inactivity, getkey_inactivity_warning, last_getkey_activity). Verified against inkey.cpp, getkey.cpp, js_console.cpp, scfglib1.c (NOINP=0x0100, default max_getkey_inactivity=300).

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Sun Jun 7 18:41:23 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e9e5884dba659e4eec2e8ee0
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    skills/javascript: load() caller-scope trap + uselect/printfile/directory gotchas

    Lessons captured from Synchronet door work:
    - load('file') runs in the CALLER's scope, so its top-level vars become locals of
    whatever function called load() -- a top-level / on_exit handler can't see a
    value load()ed inside main(); capture it into a reachable scope (the silent
    "quetzal is not defined" ReferenceError).
    - console.uselect: the title is auto-prefixed with "Select " (pass "a Game", not
    "Select a Game"); the display call's number argument is the DEFAULT item index.
    console.line_counter = 0 discards a pending auto-pager prompt before a clear. - console.printfile renders Ctrl-A codes + ANSI/CP437 by default; P_PCBOARD is
    only for PCBoard @X codes (it would misread a literal @).
    - directory() defaults to GLOB_MARK, so directory entries come back with a trailing
    '/' (self-identifying). The CWD is process-global (always ctrl/, since sbbs is
    multi-threaded) -- which is why no chdir is exposed to BBS JS; build absolute
    paths from js.exec_dir / system.*_dir.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Thu Jun 11 15:38:02 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/4804fef3b8d8cbf70a850169
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: document terminal-control abstraction + K_CTRLKEYS vs K_EXTKEYS

    Two lessons from the Z-Machine v6 door work:

    - Terminal control sequences (cursor show/hide, etc.) are abstracted in
    ansiterm_lib.js (CSI ?25h/l via ansiterm.send) -- reach for that before
    hand-writing escape codes.

    - Function/cursor key input: inkey/getkey pre-translate the arrow/Home/End
    escapes into control codes (TERM_KEY_*), conflating them with Ctrl-letters.
    K_EXTKEYS still yields those conflated codes and has no F-key codes; K_CTRLKEYS
    passes control keys through AND leaves the escape sequences RAW so you can parse
    the cterm forms yourself (ESC[A/B/C/D, ESC[11~..24~) and keep arrows/F-keys
    distinct. getbyte() is the raw alternative but drops idle-disconnect + UTF-8.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell (on Debian Linux)@1:103/705 to Git commit to main/sbbs/master on Mon Jul 6 20:44:06 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/371c3e49b542335fac8e210c
    Modified Files:
    .claude/skills/javascript/SKILL.md
    Log Message:
    javascript skill: five API gotchas learned from real module work

    - MsgBase.remove_msg(): passing a header object silently returns false
    with an empty last_error; pass the message number/offset/ID.
    - bbs.text() accepts the string identifier (via text_id_map), and a
    text.dat format string can carry more %-specifiers than the name
    suggests (NodeMsgFmt has three, including the message body) -- count
    them in ctrl/text.dat before format()ing, or the leftover %s renders
    literally.
    - bbs.compare_ars() evaluates the current user only; use the User
    object's compare_ars() to test any user (e.g. other nodes' useron),
    plus how to find a program's execution_ars and enumerate in-use nodes.
    - Emit line breaks with console.newline() rather than a hand-written
    "\r\n", same terminal-correctness principle as console.creturn().
    - Don't copy P_NOPAUSE onto printfile() calls reflexively: it disables
    the default pagination, which is only right when the surrounding flow
    provides the pause.
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)