You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The README install path now suggests pip install -e ".[sqlite]", but there is no installed callstack command yet. Operators still need to write Python or run server.py directly for common tasks. A small CLI would make the developer experience much better for Raspberry Pi deployments: smoke-test the modem, send a manual SMS, inspect signal/registration, and tail events without inventing one-off scripts.
This issue scopes a minimal installable CLI slice for send and status first. monitor can be included if it stays small, but it should not block the first PR.
User journey
A developer installs Callstack in editable mode on a Pi.
They run callstack status --at-port /dev/ttyUSB2 and see modem/network readiness, signal quality, registration state, and operator if available.
They run callstack send --to <number> --body "test" --at-port /dev/ttyUSB2 to send a one-off SMS.
The command exits non-zero with a clear error when the modem/SIM/network is not ready.
The command output is usable in scripts and does not reveal secrets or full private identifiers in logs.
Implement callstack/cli.py using stdlib argparse and asyncio.run(); avoid adding a CLI dependency unless there is a strong reason.
Build ModemConfig from shared global flags: --at-port, --audio-port, --baudrate, --sms-db-path, --sim-pin-env (optional; read a PIN from an env var name without printing it), --log-level.
For status, open Modem, query modem.network.registration(), modem.network.signal_quality(), and modem.network.operator(), then print human or JSON output.
For send, open Modem, call modem.sms.send(to, body), print the SMS reference/status, and return non-zero on SMSSendError/modem setup errors.
Keep command-side validation small: require --to and --body; do not implement phone-number normalization in this PR.
Tests should patch/construct a fake modem or CLI runner so no real serial ports are opened.
Affected modules and tests
Likely files:
Create: callstack/cli.py
Modify: pyproject.toml
Modify (optional): callstack/__init__.py only if a clean public helper is needed
Create: tests/test_cli.py
Docs follow-up: README installation/CLI usage section after implementation
Existing context:
pyproject.toml already scopes package discovery to callstack* and has optional sqlite/dev dependencies.
ModemConfig already exposes port, baudrate, SMS DB path, SIM PIN, and log level knobs.
NetworkService already exposes signal_quality(), registration(), operator(), and emits signal events.
SMSService.send() already returns an SMS object with recipient/body/status/reference.
Hardware / modem caveats
The default port examples are SIMCOM/Raspberry Pi oriented (/dev/ttyUSB2, /dev/ttyUSB4), but users must be able to override both ports.
status must tolerate missing optional operator info and unknown signal values without crashing.
send must not log full destination numbers or message bodies on errors; stdout can print the destination only if explicitly requested or masked.
Tests must use mock/fake modem behavior only; no serial devices, SIM cards, or carrier network access.
Acceptance criteria
pip install -e . or uv run ... callstack --help exposes a callstack command through [project.scripts].
callstack --help, callstack status --help, and callstack send --help show useful examples/flags.
callstack status --json prints valid JSON with connected, registration, signal, and operator fields.
callstack status human output masks or omits private identifiers and handles unknown operator/signal values.
callstack send --to ... --body ... calls SMSService.send() once and prints a concise success message containing the message reference.
CLI failures return non-zero and print actionable errors without tracebacks by default.
Unit tests cover argument parsing, JSON status output, send success, send failure, and config flag mapping.
Motivation
The README install path now suggests
pip install -e ".[sqlite]", but there is no installedcallstackcommand yet. Operators still need to write Python or runserver.pydirectly for common tasks. A small CLI would make the developer experience much better for Raspberry Pi deployments: smoke-test the modem, send a manual SMS, inspect signal/registration, and tail events without inventing one-off scripts.This issue scopes a minimal installable CLI slice for
sendandstatusfirst.monitorcan be included if it stays small, but it should not block the first PR.User journey
callstack status --at-port /dev/ttyUSB2and see modem/network readiness, signal quality, registration state, and operator if available.callstack send --to <number> --body "test" --at-port /dev/ttyUSB2to send a one-off SMS.API / UX sketch
Add a console script in
pyproject.toml:Proposed commands:
Human status output should be concise:
JSON output should be stable for automation:
{ "connected": true, "registration": {"registered": true, "roaming": false, "description": "registered (home)"}, "signal": {"rssi": 18, "dbm": -77, "description": "good", "ber": 2, "ber_description": "good"}, "operator": "example" }Technical approach
callstack/cli.pyusing stdlibargparseandasyncio.run(); avoid adding a CLI dependency unless there is a strong reason.ModemConfigfrom shared global flags:--at-port,--audio-port,--baudrate,--sms-db-path,--sim-pin-env(optional; read a PIN from an env var name without printing it),--log-level.status, openModem, querymodem.network.registration(),modem.network.signal_quality(), andmodem.network.operator(), then print human or JSON output.send, openModem, callmodem.sms.send(to, body), print the SMS reference/status, and return non-zero onSMSSendError/modem setup errors.--toand--body; do not implement phone-number normalization in this PR.Affected modules and tests
Likely files:
callstack/cli.pypyproject.tomlcallstack/__init__.pyonly if a clean public helper is neededtests/test_cli.pyExisting context:
pyproject.tomlalready scopes package discovery tocallstack*and has optionalsqlite/devdependencies.ModemConfigalready exposes port, baudrate, SMS DB path, SIM PIN, and log level knobs.NetworkServicealready exposessignal_quality(),registration(),operator(), and emits signal events.SMSService.send()already returns anSMSobject with recipient/body/status/reference.Hardware / modem caveats
/dev/ttyUSB2,/dev/ttyUSB4), but users must be able to override both ports.statusmust tolerate missing optional operator info and unknown signal values without crashing.sendmust not log full destination numbers or message bodies on errors; stdout can print the destination only if explicitly requested or masked.Acceptance criteria
pip install -e .oruv run ... callstack --helpexposes acallstackcommand through[project.scripts].callstack --help,callstack status --help, andcallstack send --helpshow useful examples/flags.callstack status --jsonprints valid JSON with connected, registration, signal, and operator fields.callstack statushuman output masks or omits private identifiers and handles unknown operator/signal values.callstack send --to ... --body ...callsSMSService.send()once and prints a concise success message containing the message reference.Exact verification gates
Run these before opening the PR:
Because this touches packaging/installability, also run one packaging gate and record the exact result:
Optional if build tooling is available:
Non-goals
monitorif it turns this PR into a large event-streaming feature; create a follow-up instead.