Skip to content

Add mc-prompter: local teleprompter skill (Phase A) - #4

Closed
bmadcode wants to merge 6 commits into
mainfrom
feat-prompter
Closed

Add mc-prompter: local teleprompter skill (Phase A)#4
bmadcode wants to merge 6 commits into
mainfrom
feat-prompter

Conversation

@bmadcode

@bmadcode bmadcode commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What this is

Phase A of mc-prompter: a classic teleprompter as a new Manticore service skill (mc-audio pattern: no stage, no gate, no project.json state). It serves the creator-owned record stage for scripted videos and works standalone for any text. This is PR 1 of a planned stack of 3: PR 2 adds voice-follow (streaming local ASR tracks the speaker through the script), PR 3 adds producer mode (rundown + local LLM cues for free-talk shows).

What you get

One command starts a local server and opens the browser UI:

uv run {skill-root}/scripts/run_prompter.py --script <path>
  • Prompter display: smooth WPM scroll with live adjustment, timed mode (continuously re-derived speed with drift display), countdown, elapsed/remaining clocks, mirror flip H/V/both for beam-splitter rigs, font/size/colors/margins/line-height, adjustable eyeline marker, fullscreen, section list with jump, full keyboard map with a help overlay, per-device settings persistence (localStorage)
  • Phone remote over LAN with a per-session token URL: play/pause, speed, section jumps, live clocks
  • Home page: load or paste a script, edit in place (backup-then-atomic-save), remote URL with copy button
  • script.md ingestion understands the pipeline markers: TAKE lines render dimmed with a badge (already recorded in the interview), INVENTED flags are subtle and toggleable, bracket notes never count as speakable
  • OBS overlay page ships as a placeholder (the producer rail lands in PR 3)

Everything is offline and local: no CDN, no external requests, aiohttp pinned, browser UI is plain HTML/JS/CSS with no build step. Server binds 127.0.0.1 by default; --lan opts into LAN with the token requirement for non-loopback peers.

Module integration

  • skills/mc-prompter/ with SKILL.md, customize.toml, scripts, and self-running tests
  • One module-help.csv row (menu-code TP, phase anytime), marketplace.json skills entry (no version bump)
  • No PIPELINE.md stage or gate changes; record stays creator-owned

Verification

  • 76 unit tests across ingest, server, and launcher, discoverable and green under the CI quality gate's exact command; all pre-existing module tests still pass; genericity lint clean
  • Live browser smoke: scroll, countdown, timed mode, and the phone-remote round trip (pause from the remote stops the prompter through the WS relay), zero console errors
  • Adversarial 3-lens review after tests were green found 20 issues (orphaned server process on launcher kill, UTF-8 BOM breaking heading parsing, WS leader/follower races, hide-takes breaking pacing math, non-atomic saves, and a genericity-lint failure on the UI palette); all fixed with regression tests where testable

🤖 Generated with Claude Code

https://claude.ai/code/session_01BbneC1PckyV8vkDzhGYkpN

Summary by CodeRabbit

  • New Features
    • Added an offline teleprompter experience with prompt, phone remote, home/editor, and OBS overlay views.
    • Supports Markdown scripts, section navigation, playback controls, speed adjustments, countdowns, WPM estimates, and display customization.
    • Recognizes TAKE and INVENTED script markers with visual cues and configurable visibility.
    • Enables in-place editing, script loading, backups, session links, and optional LAN access.
  • Documentation
    • Added setup, usage, operating guidelines, and launch checklist documentation.
  • Tests
    • Added coverage for script parsing, server behavior, networking, editing, and launcher workflows.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds the mc-prompter skill as an offline local teleprompter with script parsing, authenticated HTTP/WebSocket coordination, synchronized prompt playback, remote and overlay interfaces, editing, backups, and comprehensive tests.

Changes

mc-prompter skill

Layer / File(s) Summary
Skill registration and operating contract
.claude-plugin/marketplace.json, skills/mc-prompter/SKILL.md, skills/mc-prompter/customize.toml
Registers the skill and documents its launch workflow, configuration, operating rules, and checklist.
Script ingestion model and validation
skills/mc-prompter/scripts/server/script_ingest.py, skills/mc-prompter/scripts/tests/test-script_ingest.py, skills/mc-prompter/scripts/tests/fixtures/sample-script.md
Parses sections, notes, takes, invented markers, and word counts for Markdown or plain scripts, with fixture and robustness tests.
Launcher, server state, and protocol
skills/mc-prompter/scripts/run_prompter.py, skills/mc-prompter/scripts/server/*, skills/mc-prompter/scripts/tests/test-run_prompter.py, skills/mc-prompter/scripts/tests/test-server.py
Adds port/session management, process launching, authenticated HTTP APIs, atomic backups, WebSocket leader coordination, and server protocol tests.
Prompt runtime and shared browser model
skills/mc-prompter/scripts/server/static/js/{model,ws,settings,scroll,prompt}.js, skills/mc-prompter/scripts/server/static/{prompt.html,css/prompt.css}
Adds document rendering, persisted settings, smooth scrolling, synchronized playback, prompt controls, and display styling.
Home, remote, and overlay interfaces
skills/mc-prompter/scripts/server/static/{home.html,remote.html,overlay.html}, skills/mc-prompter/scripts/server/static/js/{home,remote,overlay}.js, skills/mc-prompter/scripts/server/static/css/*
Adds script loading/editing, phone controls, overlay status UI, shared styling, and responsive page layouts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Creator
  participant HomePage
  participant PrompterServer
  participant PromptPage
  participant RemotePage

  Creator->>HomePage: load or edit script
  HomePage->>PrompterServer: POST source and backup request
  PrompterServer-->>HomePage: updated document version
  PrompterServer-->>PromptPage: broadcast doc-updated
  PromptPage->>PrompterServer: send leader state
  RemotePage->>PrompterServer: send playback command
  PrompterServer-->>PromptPage: relay command
  PromptPage-->>PrompterServer: broadcast synchronized state
  PrompterServer-->>RemotePage: return state snapshot
Loading

Poem

I’m a rabbit with a script in my paw,
Local pages hop without a flaw.
Takes and notes now neatly align,
Remote buttons keep pace just fine.
Backups bloom where edits are spun—
Hop, hop, teleprompting fun!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the mc-prompter local teleprompter skill for Phase A.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-prompter

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
skills/mc-prompter/scripts/server/script_ingest.py (2)

225-227: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer \ufeff escape over literal BOM character.

The lstrip("") uses a literal U+FEFF character that is invisible in most editors and code review tools, making it easy to accidentally delete or overlook. Using the escape sequence is clearer and more maintainable.

♻️ Optional fix
-    text = text.lstrip("")
+    text = text.lstrip("\ufeff")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/scripts/server/script_ingest.py` around lines 225 - 227,
Replace the literal BOM character in the `text.lstrip` call with the explicit
`\ufeff` escape sequence, preserving the existing defensive stripping behavior.

159-164: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Filter empty notes from whitespace-only bracket content.

When bracket content is whitespace-only (e.g., [ ] produced after inner extraction of [[]]), _extract_note appends content.strip() which yields "", producing empty note blocks {"type": "note", "text": ""} in the output. The test test_empty_brackets_never_leak only asserts speakable text, so this goes uncaught.

♻️ Optional fix
     def _extract_note(m):
         content = m.group(1)
         if content == INVENTED[1:-1]:
             return m.group(0)
-        notes.append(content.strip())
+        stripped = content.strip()
+        if stripped:
+            notes.append(stripped)
         return " "
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/scripts/server/script_ingest.py` around lines 159 - 164,
Update _extract_note to strip bracket content before appending it, and only
append non-empty text; for whitespace-only content, return the replacement
without adding an empty note. Preserve the existing INVENTED handling and ensure
test_empty_brackets_never_leak also verifies that no empty note blocks are
produced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/mc-prompter/scripts/run_prompter.py`:
- Around line 294-333: Ensure the child process is always cleaned up when the
outer startup block exits, including interruptions during health checks or
output. Move or add terminate_server(child) at the beginning of the outer
finally block surrounding wait_for_health and URL printing, while retaining
session-file cleanup; use the existing terminate_server helper and avoid relying
only on the nested KeyboardInterrupt handler.
- Line 58: The AIOHTTP_PIN dependency is set to the vulnerable 3.12.15 release;
update AIOHTTP_PIN to version 3.14.1 or newer and ensure any corresponding
dependency references remain consistent.

In `@skills/mc-prompter/scripts/server/static/js/remote.js`:
- Around line 55-72: Update the catch handler in loadSections to retain the
existing 401/403 message and display a minimal non-fatal error message for all
other failures, including network errors and server responses such as 500, so
users are informed instead of seeing an empty section list.

In `@skills/mc-prompter/SKILL.md`:
- Around line 13-14: Align the documented no-file workflow with the
implementation: update the launcher CLI handling in run_prompter.py so --script
is optional, and ensure the server initializes an empty document that opens the
paste editor when omitted; otherwise remove the no-file claim from SKILL.md and
related launch instructions.

---

Nitpick comments:
In `@skills/mc-prompter/scripts/server/script_ingest.py`:
- Around line 225-227: Replace the literal BOM character in the `text.lstrip`
call with the explicit `\ufeff` escape sequence, preserving the existing
defensive stripping behavior.
- Around line 159-164: Update _extract_note to strip bracket content before
appending it, and only append non-empty text; for whitespace-only content,
return the replacement without adding an empty note. Preserve the existing
INVENTED handling and ensure test_empty_brackets_never_leak also verifies that
no empty note blocks are produced.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: aedd1ce5-30e1-4496-8669-a7a6376b4a2c

📥 Commits

Reviewing files that changed from the base of the PR and between 08bfbfb and e67c16c.

⛔ Files ignored due to path filters (1)
  • skills/module-help.csv is excluded by !**/*.csv
📒 Files selected for processing (28)
  • .claude-plugin/marketplace.json
  • skills/mc-prompter/SKILL.md
  • skills/mc-prompter/customize.toml
  • skills/mc-prompter/scripts/run_prompter.py
  • skills/mc-prompter/scripts/server/__init__.py
  • skills/mc-prompter/scripts/server/main.py
  • skills/mc-prompter/scripts/server/script_ingest.py
  • skills/mc-prompter/scripts/server/static/css/home.css
  • skills/mc-prompter/scripts/server/static/css/overlay.css
  • skills/mc-prompter/scripts/server/static/css/prompt.css
  • skills/mc-prompter/scripts/server/static/css/remote.css
  • skills/mc-prompter/scripts/server/static/css/shared.css
  • skills/mc-prompter/scripts/server/static/home.html
  • skills/mc-prompter/scripts/server/static/js/home.js
  • skills/mc-prompter/scripts/server/static/js/model.js
  • skills/mc-prompter/scripts/server/static/js/overlay.js
  • skills/mc-prompter/scripts/server/static/js/prompt.js
  • skills/mc-prompter/scripts/server/static/js/remote.js
  • skills/mc-prompter/scripts/server/static/js/scroll.js
  • skills/mc-prompter/scripts/server/static/js/settings.js
  • skills/mc-prompter/scripts/server/static/js/ws.js
  • skills/mc-prompter/scripts/server/static/overlay.html
  • skills/mc-prompter/scripts/server/static/prompt.html
  • skills/mc-prompter/scripts/server/static/remote.html
  • skills/mc-prompter/scripts/tests/fixtures/sample-script.md
  • skills/mc-prompter/scripts/tests/test-run_prompter.py
  • skills/mc-prompter/scripts/tests/test-script_ingest.py
  • skills/mc-prompter/scripts/tests/test-server.py


SCRIPTS_DIR = Path(__file__).resolve().parent
DEFAULT_PORT = 8770
AIOHTTP_PIN = "aiohttp==3.12.15"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check PyPI for the pinned aiohttp version
curl -s https://pypi.org/pypi/aiohttp/3.12.15/json | jq '.info.version' 2>/dev/null || echo "version not found"

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 10, ecosystem: PIP, package: "aiohttp") {
    nodes {
      advisory { summary severity publishedAt }
      vulnerableVersionRange
      firstPatchedVersion { identifier }
    }
  }
}'

Repository: bmad-code-org/bmad-manticore

Length of output: 2598


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="skills/mc-prompter/scripts/run_prompter.py"

# Show the relevant section with line numbers
sed -n '1,140p' "$FILE" | cat -n

Repository: bmad-code-org/bmad-manticore

Length of output: 6058


Bump aiohttp to a patched release. aiohttp==3.12.15 is published, but it falls within current advisories for <= 3.14.0; upgrade to 3.14.1 or newer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/scripts/run_prompter.py` at line 58, The AIOHTTP_PIN
dependency is set to the vulnerable 3.12.15 release; update AIOHTTP_PIN to
version 3.14.1 or newer and ensure any corresponding dependency references
remain consistent.

Comment on lines +294 to +333
try:
info = wait_for_health(port, child)
if info is None:
print("error: server failed to start", file=sys.stderr)
terminate_server(child)
return 1

write_session_file(session_file, port, child.pid, token, script)

base_url = f"http://127.0.0.1:{port}"
local_url = f"{base_url}/?token={token}"
print(f"mc-prompter is up (session {token[:8]})")
print(f" home: {local_url}")
print(f" prompt: {base_url}/prompt")
print(f" remote: http://127.0.0.1:{port}/remote?token={token}")
if args.lan:
ip = local_ip()
print(f" LAN remote: http://{ip}:{port}/remote?token={token}")
print(" note: on Windows the first --lan launch triggers a "
"Windows Firewall consent dialog; allow it for the LAN "
"remote to reach the server.")
print(f" session file: {session_file}", flush=True)

if not args.no_open:
webbrowser.open(local_url)

def _on_terminate(signum, frame):
raise KeyboardInterrupt

signal.signal(signal.SIGTERM, _on_terminate)
try:
child.wait()
except KeyboardInterrupt:
terminate_server(child)
return 0
return 0 if child.returncode == 0 else 1
finally:
# Never leave a stale session file advertising a dead pid/token.
with contextlib.suppress(OSError):
session_file.unlink(missing_ok=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Child process is orphaned if interrupted during startup.

terminate_server(child) is only called in the except KeyboardInterrupt block (line 327) and on failed startup (line 298), but not in the finally block. If the user presses Ctrl-C during wait_for_health (up to 300 s on a cold uv cache) or during URL printing, the KeyboardInterrupt propagates directly to the finally block — which only removes the session file. The child, running in its own process group, keeps the port alive with no parent to reap it.

Move terminate_server(child) into the finally block so the child is always terminated regardless of how the try exits. terminate_server already no-ops when the child has exited (child.poll() is not None check at line 193).

🔧 Proposed fix
     try:
         info = wait_for_health(port, child)
         if info is None:
             print("error: server failed to start", file=sys.stderr)
-            terminate_server(child)
             return 1
 
         write_session_file(session_file, port, child.pid, token, script)
 
         base_url = f"http://127.0.0.1:{port}"
         local_url = f"{base_url}/?token={token}"
         print(f"mc-prompter is up (session {token[:8]})")
         print(f"  home:    {local_url}")
         print(f"  prompt:  {base_url}/prompt")
         print(f"  remote:  http://127.0.0.1:{port}/remote?token={token}")
         if args.lan:
             ip = local_ip()
             print(f"  LAN remote: http://{ip}:{port}/remote?token={token}")
             print("  note: on Windows the first --lan launch triggers a "
                   "Windows Firewall consent dialog; allow it for the LAN "
                   "remote to reach the server.")
         print(f"  session file: {session_file}", flush=True)
 
         if not args.no_open:
             webbrowser.open(local_url)
 
         def _on_terminate(signum, frame):
             raise KeyboardInterrupt
 
         signal.signal(signal.SIGTERM, _on_terminate)
         try:
             child.wait()
         except KeyboardInterrupt:
-            terminate_server(child)
             return 0
         return 0 if child.returncode == 0 else 1
     finally:
+        terminate_server(child)
         # Never leave a stale session file advertising a dead pid/token.
         with contextlib.suppress(OSError):
             session_file.unlink(missing_ok=True)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try:
info = wait_for_health(port, child)
if info is None:
print("error: server failed to start", file=sys.stderr)
terminate_server(child)
return 1
write_session_file(session_file, port, child.pid, token, script)
base_url = f"http://127.0.0.1:{port}"
local_url = f"{base_url}/?token={token}"
print(f"mc-prompter is up (session {token[:8]})")
print(f" home: {local_url}")
print(f" prompt: {base_url}/prompt")
print(f" remote: http://127.0.0.1:{port}/remote?token={token}")
if args.lan:
ip = local_ip()
print(f" LAN remote: http://{ip}:{port}/remote?token={token}")
print(" note: on Windows the first --lan launch triggers a "
"Windows Firewall consent dialog; allow it for the LAN "
"remote to reach the server.")
print(f" session file: {session_file}", flush=True)
if not args.no_open:
webbrowser.open(local_url)
def _on_terminate(signum, frame):
raise KeyboardInterrupt
signal.signal(signal.SIGTERM, _on_terminate)
try:
child.wait()
except KeyboardInterrupt:
terminate_server(child)
return 0
return 0 if child.returncode == 0 else 1
finally:
# Never leave a stale session file advertising a dead pid/token.
with contextlib.suppress(OSError):
session_file.unlink(missing_ok=True)
try:
info = wait_for_health(port, child)
if info is None:
print("error: server failed to start", file=sys.stderr)
return 1
write_session_file(session_file, port, child.pid, token, script)
base_url = f"http://127.0.0.1:{port}"
local_url = f"{base_url}/?token={token}"
print(f"mc-prompter is up (session {token[:8]})")
print(f" home: {local_url}")
print(f" prompt: {base_url}/prompt")
print(f" remote: http://127.0.0.1:{port}/remote?token={token}")
if args.lan:
ip = local_ip()
print(f" LAN remote: http://{ip}:{port}/remote?token={token}")
print(" note: on Windows the first --lan launch triggers a "
"Windows Firewall consent dialog; allow it for the LAN "
"remote to reach the server.")
print(f" session file: {session_file}", flush=True)
if not args.no_open:
webbrowser.open(local_url)
def _on_terminate(signum, frame):
raise KeyboardInterrupt
signal.signal(signal.SIGTERM, _on_terminate)
try:
child.wait()
except KeyboardInterrupt:
return 0
return 0 if child.returncode == 0 else 1
finally:
terminate_server(child)
# Never leave a stale session file advertising a dead pid/token.
with contextlib.suppress(OSError):
session_file.unlink(missing_ok=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/scripts/run_prompter.py` around lines 294 - 333, Ensure
the child process is always cleaned up when the outer startup block exits,
including interruptions during health checks or output. Move or add
terminate_server(child) at the beginning of the outer finally block surrounding
wait_for_health and URL printing, while retaining session-file cleanup; use the
existing terminate_server helper and avoid relying only on the nested
KeyboardInterrupt handler.

Comment on lines +55 to +72
function loadSections() {
return MC.model.fetchSource(token).then(function (src) {
docVersion = src['doc-version'];
sections = [];
var docSections = (src.doc && src.doc.sections) || [];
for (var i = 0; i < docSections.length; i++) {
sections.push({
id: docSections[i].id,
heading: docSections[i].heading || (i === 0 ? 'Preamble' : 'Untitled section')
});
}
renderSections();
}).catch(function (err) {
if (err.status === 401 || err.status === 403) {
showError('The session token was rejected. Grab a fresh remote URL from the home page.');
}
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Surface non-auth errors from loadSections to the user.

The .catch handler only shows an error for 401/403. Network failures or 500s are silently swallowed — the user sees an empty section list with no indication of what went wrong. A minimal non-fatal message would close the feedback gap.

💡 Proposed fix
     }).catch(function (err) {
       if (err.status === 401 || err.status === 403) {
         showError('The session token was rejected. Grab a fresh remote URL from the home page.');
+      } else {
+        els.secCount.textContent = '(load failed)';
       }
     });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function loadSections() {
return MC.model.fetchSource(token).then(function (src) {
docVersion = src['doc-version'];
sections = [];
var docSections = (src.doc && src.doc.sections) || [];
for (var i = 0; i < docSections.length; i++) {
sections.push({
id: docSections[i].id,
heading: docSections[i].heading || (i === 0 ? 'Preamble' : 'Untitled section')
});
}
renderSections();
}).catch(function (err) {
if (err.status === 401 || err.status === 403) {
showError('The session token was rejected. Grab a fresh remote URL from the home page.');
}
});
}
function loadSections() {
return MC.model.fetchSource(token).then(function (src) {
docVersion = src['doc-version'];
sections = [];
var docSections = (src.doc && src.doc.sections) || [];
for (var i = 0; i < docSections.length; i++) {
sections.push({
id: docSections[i].id,
heading: docSections[i].heading || (i === 0 ? 'Preamble' : 'Untitled section')
});
}
renderSections();
}).catch(function (err) {
if (err.status === 401 || err.status === 403) {
showError('The session token was rejected. Grab a fresh remote URL from the home page.');
} else {
els.secCount.textContent = '(load failed)';
}
});
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/scripts/server/static/js/remote.js` around lines 55 - 72,
Update the catch handler in loadSections to retain the existing 401/403 message
and display a minimal non-fatal error message for all other failures, including
network errors and server responses such as 500, so users are informed instead
of seeing an empty section list.

Comment on lines +13 to +14
2. Locate the script. Inside a pipeline project ("record with the teleprompter"), it is the project's `script.md` under the project folder. Standalone, it is any file path the creator names, markdown or plain text. No file at all is also valid: launch without `--script` and the creator pastes text on the home page.
3. Launch: `uv run {skill-root}/scripts/run_prompter.py --script <path>` with `--port <N>` when the config or the creator sets one and `--owner-wpm <N>` when `[owner] wpm` is known. Add `--lan` only when the creator wants the phone remote or a tablet display; it binds the LAN and on Windows triggers a firewall consent dialog. The launcher probes the port, prints the local URL, the remote URL with its session token, and the session file path, then keeps the server running until Ctrl-C.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align the no-file workflow with the launcher CLI.

The skill says launching without --script is valid, but skills/mc-prompter/scripts/run_prompter.py declares --script as required. A no-file session therefore exits with a usage error instead of opening the paste editor.

Either make --script optional in the launcher and ensure the server supports an empty document, or remove the no-file workflow from this contract.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/mc-prompter/SKILL.md` around lines 13 - 14, Align the documented
no-file workflow with the implementation: update the launcher CLI handling in
run_prompter.py so --script is optional, and ensure the server initializes an
empty document that opens the paste editor when omitted; otherwise remove the
no-file claim from SKILL.md and related launch instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant