rhylthyme-cli-runner 0.2.2a0__tar.gz → 0.2.3a0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {rhylthyme_cli_runner-0.2.2a0/src/rhylthyme_cli_runner.egg-info → rhylthyme_cli_runner-0.2.3a0}/PKG-INFO +114 -2
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/README.md +113 -1
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/setup.py +1 -1
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/__init__.py +1 -1
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/cli.py +58 -8
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0/src/rhylthyme_cli_runner.egg-info}/PKG-INFO +114 -2
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_remote.py +55 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_skill.py +11 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/LICENSE +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/pyproject.toml +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/setup.cfg +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/environment_icons.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/environment_loader.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/environment_schemas.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/compare.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/gold.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/harness.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/llm.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/matcher.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/metrics.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/authoring_guide.md +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/baseline.md +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/baseline.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/extract.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/patterns/four_turn.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/report.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/expand_replicates.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/calibrate.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/calibrate_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/evaluate.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/evaluate_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/factors.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/hash.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/predict.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/recorder.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/render.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/replay.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/report.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/report_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/store.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/synth.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/history/usable.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/instance_checks.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/program_planner.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/program_runner.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/__init__.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/auth.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/checks.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/mcp_client.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/validate_program.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner.egg-info/SOURCES.txt +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner.egg-info/dependency_links.txt +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner.egg-info/entry_points.txt +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner.egg-info/requires.txt +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner.egg-info/top_level.txt +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_calibrate.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_cli.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_eval_baseline.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_eval_harness.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_eval_providers.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_eval_scorer.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_evaluate.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_example_validation.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_examples_integration.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_expand_replicates.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_hash_parity.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_history_report.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_mcp_checks.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_planner_time_strings.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_predict.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_program_runner.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_replay.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_run_recorder.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_synth.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_usable.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_validate_examples_ci.py +0 -0
- {rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_validate_program.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rhylthyme-cli-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3a0
|
|
4
4
|
Summary: CLI runner for Rhylthyme real-time program schedules
|
|
5
5
|
Home-page: https://github.com/rhylthyme/rhylthyme-cli-runner
|
|
6
6
|
Author: Rhylthyme Team
|
|
@@ -69,6 +69,106 @@ rhylthyme login
|
|
|
69
69
|
rhylthyme generate "roast chicken, potatoes and green beans for 6" -e kitchen --by 19:00 --with "one oven"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## A timeline in five commands: a birthday party
|
|
73
|
+
|
|
74
|
+
Parents collect at 4:00. Working backwards from that: when does the cake go in
|
|
75
|
+
the oven, when do the balloons go up, and do the pizzas and the cake ever want
|
|
76
|
+
the oven at the same time?
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install rhylthyme-cli-runner
|
|
80
|
+
|
|
81
|
+
# 1. a program: the party, the kitchen and the setup crew, 13 steps
|
|
82
|
+
curl -sO https://raw.githubusercontent.com/rhylthyme/rhylthyme-cli-runner/main/examples/recipe/birthday-party.json
|
|
83
|
+
|
|
84
|
+
# 2. is it well formed?
|
|
85
|
+
rhylthyme validate birthday-party.json --strict
|
|
86
|
+
|
|
87
|
+
# 3. any clashes, and what time does everything start if we finish at 4?
|
|
88
|
+
rhylthyme analyze birthday-party.json --finish-at 16:00
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
**Makespan:** 3h 10m across 3 tracks, 13 steps.
|
|
93
|
+
**Critical path:** Guests arrive → Party games → Pizza → Candles and cake → Piñata, until it breaks → Party bags and goodbyes
|
|
94
|
+
**Binding constraints:** step dependencies only — no resource or in-flight limit gates the critical path.
|
|
95
|
+
**Peak concurrency:** 2 steps at 10:00.
|
|
96
|
+
**Resource conflicts:** none.
|
|
97
|
+
|
|
98
|
+
**Tracks that finish early:** Kitchen (1h 10m early), Setup crew (50m early)
|
|
99
|
+
|
|
100
|
+
Start Sat 12:50, finish Sat 16:00 (local time)
|
|
101
|
+
Sat 12:50 Bake the cake
|
|
102
|
+
Sat 13:00 Balloons and banner
|
|
103
|
+
Sat 13:25 Cake cools
|
|
104
|
+
Sat 13:30 Hang the piñata
|
|
105
|
+
Sat 13:50 Guests arrive
|
|
106
|
+
Sat 14:05 Frost and decorate
|
|
107
|
+
Sat 14:20 Party games
|
|
108
|
+
Sat 14:35 Pizzas in the oven
|
|
109
|
+
Sat 14:50 Fill party bags
|
|
110
|
+
Sat 14:50 Pizza
|
|
111
|
+
Sat 15:15 Candles and cake
|
|
112
|
+
Sat 15:30 Piñata, until it breaks
|
|
113
|
+
Sat 15:45 Party bags and goodbyes
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 4. a live timeline for the fridge door (or a phone): timers, cues, and you end the piñata when it breaks
|
|
118
|
+
rhylthyme publish birthday-party.json --open --image party-preview.png
|
|
119
|
+
|
|
120
|
+
# 5. a picture for the group chat, on the wall clock (needs Node; nothing to install)
|
|
121
|
+
npx -y github:rhylthyme/rhylthyme-timeline birthday-party.json -o birthday-party.png \
|
|
122
|
+
--style web --palette vivid --label-overflow outside --start-at "$(date +%F)T12:50:00"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+

|
|
126
|
+
|
|
127
|
+
The pizzas go in 15 minutes after the games *start*, so they come out as the
|
|
128
|
+
games end; the party bags get filled while everyone is eating. Try
|
|
129
|
+
`--finish-at 17:30`, or turn the cake into a two-hour fruitcake (`"seconds": "2h"`
|
|
130
|
+
on `bake`) and `analyze` reports the clash: `oven needs 2 but max is 1 from
|
|
131
|
+
1:45:00 to 2:00:00 (bake, heat-pizza)`. `rhylthyme run birthday-party.json --time-scale 60` rehearses
|
|
132
|
+
the whole afternoon in the terminal at a minute per second.
|
|
133
|
+
|
|
134
|
+
## This repository and rhylthyme-mcp
|
|
135
|
+
|
|
136
|
+
Two repositories do related things and are easy to confuse. This one is the
|
|
137
|
+
**command a person types**. [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp)
|
|
138
|
+
is the **server an AI assistant talks to**.
|
|
139
|
+
|
|
140
|
+
| | [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp) | [rhylthyme-cli-runner](https://github.com/rhylthyme/rhylthyme-cli-runner) |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| What it is | The MCP **server**: the tools an AI assistant calls | A command-line **program**: the `rhylthyme` command |
|
|
143
|
+
| Who uses it | Claude, ChatGPT, Cursor or any MCP client, on a person's behalf | A person at a terminal, a script, or CI |
|
|
144
|
+
| Where it runs | Hosted at `mcp.rhylthyme.com`; nothing to install | On your machine: `pip install rhylthyme-cli-runner` |
|
|
145
|
+
| Language | JavaScript (Node 20+) | Python 3.12+ |
|
|
146
|
+
| Input | A program the assistant builds in conversation | A program file on disk (JSON or YAML) |
|
|
147
|
+
| Validate a program | `validate_program` | `rhylthyme validate` (works offline) |
|
|
148
|
+
| Timing, conflicts, deadlines | `analyze_schedule` | `rhylthyme analyze` (asks the server) |
|
|
149
|
+
| Publish a live timeline | `visualize_schedule` | `rhylthyme publish` (asks the server) |
|
|
150
|
+
| Run a schedule with timers | no: it hands back a link to the web timeline | `rhylthyme run`, an interactive terminal runner |
|
|
151
|
+
| Recorded runs, calibration | reads runs saved to an account | records runs locally; `rhylthyme runs`, `rhylthyme calibrate` |
|
|
152
|
+
| Catalog search, imports, account library | yes | no |
|
|
153
|
+
| Also in the repository | the `rhylthyme-mcp` PyPI package (a stdio bridge to the hosted server), the Claude plugin marketplace | the Claude skill's source, the prompt-evaluation harness and its results, `rhylthyme mcp-test` |
|
|
154
|
+
|
|
155
|
+
How they fit together: this tool is one of the MCP server's clients.
|
|
156
|
+
`rhylthyme analyze`, `publish`, `generate` and `mcp-test` are MCP calls to
|
|
157
|
+
`mcp.rhylthyme.com`; `rhylthyme validate`, `run`, `runs` and `calibrate` never
|
|
158
|
+
touch the network. Each has its own validator for the same program schema
|
|
159
|
+
(Python here, JavaScript there), so a program is checked again when it is
|
|
160
|
+
published.
|
|
161
|
+
|
|
162
|
+
Use this repository if you have a program file and a terminal, want timers in
|
|
163
|
+
the terminal, or keep run records. Use rhylthyme-mcp to connect Claude, ChatGPT
|
|
164
|
+
or another assistant, or to read or self-host the server.
|
|
165
|
+
|
|
166
|
+
Two names to keep apart: `rhylthyme-cli-runner` on PyPI is this tool (command
|
|
167
|
+
`rhylthyme`); `rhylthyme-mcp` on PyPI is the server's stdio bridge (command
|
|
168
|
+
`rhylthyme-mcp`). The program format itself is defined in
|
|
169
|
+
[rhylthyme-spec](https://github.com/rhylthyme/rhylthyme-spec), with examples in
|
|
170
|
+
[rhylthyme-examples](https://github.com/rhylthyme/rhylthyme-examples).
|
|
171
|
+
|
|
72
172
|
## Installation
|
|
73
173
|
|
|
74
174
|
### From Source
|
|
@@ -257,6 +357,16 @@ it for conflicts, and hand back a live timeline. It follows the layout of
|
|
|
257
357
|
[K-Dense scientific skills](https://github.com/K-Dense-AI/claude-scientific-skills):
|
|
258
358
|
a `SKILL.md` plus `references/`.
|
|
259
359
|
|
|
360
|
+
The easiest install is the Claude Code plugin, which also connects the hosted
|
|
361
|
+
MCP server:
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
/plugin marketplace add rhylthyme/rhylthyme-mcp
|
|
365
|
+
/plugin install rhylthyme@rhylthyme
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Or copy the skill by hand:
|
|
369
|
+
|
|
260
370
|
```bash
|
|
261
371
|
# Claude Code, for one user
|
|
262
372
|
mkdir -p ~/.claude/skills
|
|
@@ -421,13 +531,15 @@ and tracks that finish early. No sign-in; nothing is published.
|
|
|
421
531
|
|
|
422
532
|
### `rhylthyme publish`
|
|
423
533
|
|
|
424
|
-
Publishes a program file
|
|
534
|
+
Publishes a program (a file, an http(s) URL, or `-` for standard input) as a
|
|
535
|
+
live timeline and prints its URL. No sign-in.
|
|
425
536
|
A published timeline is reachable by anyone who has the link.
|
|
426
537
|
|
|
427
538
|
**Options:**
|
|
428
539
|
- `-e, --env`: `generic`, `kitchen`, `lab`, `events`, `gym` (default: from `environmentType`)
|
|
429
540
|
- `-q, --quiet`: print only the URL
|
|
430
541
|
- `--json`: `url`, `shareId`, `imageUrl`, `makespanSeconds`, `warnings`
|
|
542
|
+
- `--image PATH`: also save a PNG of the timeline
|
|
431
543
|
- `--open`: open it in a browser
|
|
432
544
|
|
|
433
545
|
### `rhylthyme plan`
|
|
@@ -9,6 +9,106 @@ rhylthyme login
|
|
|
9
9
|
rhylthyme generate "roast chicken, potatoes and green beans for 6" -e kitchen --by 19:00 --with "one oven"
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
+
## A timeline in five commands: a birthday party
|
|
13
|
+
|
|
14
|
+
Parents collect at 4:00. Working backwards from that: when does the cake go in
|
|
15
|
+
the oven, when do the balloons go up, and do the pizzas and the cake ever want
|
|
16
|
+
the oven at the same time?
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install rhylthyme-cli-runner
|
|
20
|
+
|
|
21
|
+
# 1. a program: the party, the kitchen and the setup crew, 13 steps
|
|
22
|
+
curl -sO https://raw.githubusercontent.com/rhylthyme/rhylthyme-cli-runner/main/examples/recipe/birthday-party.json
|
|
23
|
+
|
|
24
|
+
# 2. is it well formed?
|
|
25
|
+
rhylthyme validate birthday-party.json --strict
|
|
26
|
+
|
|
27
|
+
# 3. any clashes, and what time does everything start if we finish at 4?
|
|
28
|
+
rhylthyme analyze birthday-party.json --finish-at 16:00
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
**Makespan:** 3h 10m across 3 tracks, 13 steps.
|
|
33
|
+
**Critical path:** Guests arrive → Party games → Pizza → Candles and cake → Piñata, until it breaks → Party bags and goodbyes
|
|
34
|
+
**Binding constraints:** step dependencies only — no resource or in-flight limit gates the critical path.
|
|
35
|
+
**Peak concurrency:** 2 steps at 10:00.
|
|
36
|
+
**Resource conflicts:** none.
|
|
37
|
+
|
|
38
|
+
**Tracks that finish early:** Kitchen (1h 10m early), Setup crew (50m early)
|
|
39
|
+
|
|
40
|
+
Start Sat 12:50, finish Sat 16:00 (local time)
|
|
41
|
+
Sat 12:50 Bake the cake
|
|
42
|
+
Sat 13:00 Balloons and banner
|
|
43
|
+
Sat 13:25 Cake cools
|
|
44
|
+
Sat 13:30 Hang the piñata
|
|
45
|
+
Sat 13:50 Guests arrive
|
|
46
|
+
Sat 14:05 Frost and decorate
|
|
47
|
+
Sat 14:20 Party games
|
|
48
|
+
Sat 14:35 Pizzas in the oven
|
|
49
|
+
Sat 14:50 Fill party bags
|
|
50
|
+
Sat 14:50 Pizza
|
|
51
|
+
Sat 15:15 Candles and cake
|
|
52
|
+
Sat 15:30 Piñata, until it breaks
|
|
53
|
+
Sat 15:45 Party bags and goodbyes
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# 4. a live timeline for the fridge door (or a phone): timers, cues, and you end the piñata when it breaks
|
|
58
|
+
rhylthyme publish birthday-party.json --open --image party-preview.png
|
|
59
|
+
|
|
60
|
+
# 5. a picture for the group chat, on the wall clock (needs Node; nothing to install)
|
|
61
|
+
npx -y github:rhylthyme/rhylthyme-timeline birthday-party.json -o birthday-party.png \
|
|
62
|
+
--style web --palette vivid --label-overflow outside --start-at "$(date +%F)T12:50:00"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
The pizzas go in 15 minutes after the games *start*, so they come out as the
|
|
68
|
+
games end; the party bags get filled while everyone is eating. Try
|
|
69
|
+
`--finish-at 17:30`, or turn the cake into a two-hour fruitcake (`"seconds": "2h"`
|
|
70
|
+
on `bake`) and `analyze` reports the clash: `oven needs 2 but max is 1 from
|
|
71
|
+
1:45:00 to 2:00:00 (bake, heat-pizza)`. `rhylthyme run birthday-party.json --time-scale 60` rehearses
|
|
72
|
+
the whole afternoon in the terminal at a minute per second.
|
|
73
|
+
|
|
74
|
+
## This repository and rhylthyme-mcp
|
|
75
|
+
|
|
76
|
+
Two repositories do related things and are easy to confuse. This one is the
|
|
77
|
+
**command a person types**. [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp)
|
|
78
|
+
is the **server an AI assistant talks to**.
|
|
79
|
+
|
|
80
|
+
| | [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp) | [rhylthyme-cli-runner](https://github.com/rhylthyme/rhylthyme-cli-runner) |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| What it is | The MCP **server**: the tools an AI assistant calls | A command-line **program**: the `rhylthyme` command |
|
|
83
|
+
| Who uses it | Claude, ChatGPT, Cursor or any MCP client, on a person's behalf | A person at a terminal, a script, or CI |
|
|
84
|
+
| Where it runs | Hosted at `mcp.rhylthyme.com`; nothing to install | On your machine: `pip install rhylthyme-cli-runner` |
|
|
85
|
+
| Language | JavaScript (Node 20+) | Python 3.12+ |
|
|
86
|
+
| Input | A program the assistant builds in conversation | A program file on disk (JSON or YAML) |
|
|
87
|
+
| Validate a program | `validate_program` | `rhylthyme validate` (works offline) |
|
|
88
|
+
| Timing, conflicts, deadlines | `analyze_schedule` | `rhylthyme analyze` (asks the server) |
|
|
89
|
+
| Publish a live timeline | `visualize_schedule` | `rhylthyme publish` (asks the server) |
|
|
90
|
+
| Run a schedule with timers | no: it hands back a link to the web timeline | `rhylthyme run`, an interactive terminal runner |
|
|
91
|
+
| Recorded runs, calibration | reads runs saved to an account | records runs locally; `rhylthyme runs`, `rhylthyme calibrate` |
|
|
92
|
+
| Catalog search, imports, account library | yes | no |
|
|
93
|
+
| Also in the repository | the `rhylthyme-mcp` PyPI package (a stdio bridge to the hosted server), the Claude plugin marketplace | the Claude skill's source, the prompt-evaluation harness and its results, `rhylthyme mcp-test` |
|
|
94
|
+
|
|
95
|
+
How they fit together: this tool is one of the MCP server's clients.
|
|
96
|
+
`rhylthyme analyze`, `publish`, `generate` and `mcp-test` are MCP calls to
|
|
97
|
+
`mcp.rhylthyme.com`; `rhylthyme validate`, `run`, `runs` and `calibrate` never
|
|
98
|
+
touch the network. Each has its own validator for the same program schema
|
|
99
|
+
(Python here, JavaScript there), so a program is checked again when it is
|
|
100
|
+
published.
|
|
101
|
+
|
|
102
|
+
Use this repository if you have a program file and a terminal, want timers in
|
|
103
|
+
the terminal, or keep run records. Use rhylthyme-mcp to connect Claude, ChatGPT
|
|
104
|
+
or another assistant, or to read or self-host the server.
|
|
105
|
+
|
|
106
|
+
Two names to keep apart: `rhylthyme-cli-runner` on PyPI is this tool (command
|
|
107
|
+
`rhylthyme`); `rhylthyme-mcp` on PyPI is the server's stdio bridge (command
|
|
108
|
+
`rhylthyme-mcp`). The program format itself is defined in
|
|
109
|
+
[rhylthyme-spec](https://github.com/rhylthyme/rhylthyme-spec), with examples in
|
|
110
|
+
[rhylthyme-examples](https://github.com/rhylthyme/rhylthyme-examples).
|
|
111
|
+
|
|
12
112
|
## Installation
|
|
13
113
|
|
|
14
114
|
### From Source
|
|
@@ -197,6 +297,16 @@ it for conflicts, and hand back a live timeline. It follows the layout of
|
|
|
197
297
|
[K-Dense scientific skills](https://github.com/K-Dense-AI/claude-scientific-skills):
|
|
198
298
|
a `SKILL.md` plus `references/`.
|
|
199
299
|
|
|
300
|
+
The easiest install is the Claude Code plugin, which also connects the hosted
|
|
301
|
+
MCP server:
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
/plugin marketplace add rhylthyme/rhylthyme-mcp
|
|
305
|
+
/plugin install rhylthyme@rhylthyme
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Or copy the skill by hand:
|
|
309
|
+
|
|
200
310
|
```bash
|
|
201
311
|
# Claude Code, for one user
|
|
202
312
|
mkdir -p ~/.claude/skills
|
|
@@ -361,13 +471,15 @@ and tracks that finish early. No sign-in; nothing is published.
|
|
|
361
471
|
|
|
362
472
|
### `rhylthyme publish`
|
|
363
473
|
|
|
364
|
-
Publishes a program file
|
|
474
|
+
Publishes a program (a file, an http(s) URL, or `-` for standard input) as a
|
|
475
|
+
live timeline and prints its URL. No sign-in.
|
|
365
476
|
A published timeline is reachable by anyone who has the link.
|
|
366
477
|
|
|
367
478
|
**Options:**
|
|
368
479
|
- `-e, --env`: `generic`, `kitchen`, `lab`, `events`, `gym` (default: from `environmentType`)
|
|
369
480
|
- `-q, --quiet`: print only the URL
|
|
370
481
|
- `--json`: `url`, `shareId`, `imageUrl`, `makespanSeconds`, `warnings`
|
|
482
|
+
- `--image PATH`: also save a PNG of the timeline
|
|
371
483
|
- `--open`: open it in a browser
|
|
372
484
|
|
|
373
485
|
### `rhylthyme plan`
|
|
@@ -19,7 +19,7 @@ def read_readme():
|
|
|
19
19
|
|
|
20
20
|
setup(
|
|
21
21
|
name="rhylthyme-cli-runner",
|
|
22
|
-
version="0.2.
|
|
22
|
+
version="0.2.3a0",
|
|
23
23
|
description="CLI runner for Rhylthyme real-time program schedules",
|
|
24
24
|
long_description=read_readme(),
|
|
25
25
|
long_description_content_type="text/markdown",
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/__init__.py
RENAMED
|
@@ -6,7 +6,7 @@ This package provides the command-line interface for running and validating
|
|
|
6
6
|
Rhylthyme real-time program schedules.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "0.2.
|
|
9
|
+
__version__ = "0.2.3a0"
|
|
10
10
|
__author__ = "Rhylthyme Team"
|
|
11
11
|
__description__ = "CLI runner for Rhylthyme real-time program schedules"
|
|
12
12
|
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/remote/cli.py
RENAMED
|
@@ -386,16 +386,47 @@ def mcp_test(
|
|
|
386
386
|
ctx.exit(1)
|
|
387
387
|
|
|
388
388
|
|
|
389
|
+
MAX_PROGRAM_BYTES = 2 * 1024 * 1024
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _fetch_bytes(url: str, limit: int = 8 * 1024 * 1024) -> bytes:
|
|
393
|
+
import urllib.request
|
|
394
|
+
|
|
395
|
+
if not url.startswith(("http://", "https://")):
|
|
396
|
+
raise ValueError("not an http(s) URL")
|
|
397
|
+
request = urllib.request.Request(url, headers={"User-Agent": "rhylthyme-cli"})
|
|
398
|
+
with urllib.request.urlopen(
|
|
399
|
+
request, timeout=60
|
|
400
|
+
) as response: # nosec B310 - scheme checked above
|
|
401
|
+
data = response.read(limit + 1)
|
|
402
|
+
if len(data) > limit:
|
|
403
|
+
raise ValueError(f"larger than {limit // (1024 * 1024)} MB")
|
|
404
|
+
return data
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _read_source(source: str) -> str:
|
|
408
|
+
"""The text of a program given as a file path, an http(s) URL, or `-`
|
|
409
|
+
for standard input."""
|
|
410
|
+
if source == "-":
|
|
411
|
+
return sys.stdin.read()
|
|
412
|
+
if source.startswith(("http://", "https://")):
|
|
413
|
+
return _fetch_bytes(source, MAX_PROGRAM_BYTES).decode("utf-8")
|
|
414
|
+
path = Path(source)
|
|
415
|
+
if not path.is_file():
|
|
416
|
+
raise FileNotFoundError("no such file")
|
|
417
|
+
return path.read_text()
|
|
418
|
+
|
|
419
|
+
|
|
389
420
|
def _load_program(program_file: str) -> dict:
|
|
390
|
-
path = Path(program_file)
|
|
391
421
|
program = None
|
|
392
422
|
try:
|
|
393
|
-
|
|
423
|
+
text = _read_source(program_file)
|
|
424
|
+
if program_file.lower().split("?")[0].endswith((".yaml", ".yml")):
|
|
394
425
|
import yaml
|
|
395
426
|
|
|
396
|
-
program = yaml.safe_load(
|
|
427
|
+
program = yaml.safe_load(text)
|
|
397
428
|
else:
|
|
398
|
-
program = json.loads(
|
|
429
|
+
program = json.loads(text)
|
|
399
430
|
except Exception as e: # noqa: BLE001
|
|
400
431
|
_fail(f"Could not read {program_file}: {e}")
|
|
401
432
|
if not isinstance(program, dict) or not isinstance(program.get("tracks"), list):
|
|
@@ -439,7 +470,7 @@ def _local(iso: Optional[str]) -> str:
|
|
|
439
470
|
|
|
440
471
|
|
|
441
472
|
@click.command("analyze")
|
|
442
|
-
@click.argument("program_file",
|
|
473
|
+
@click.argument("program_file", metavar="PROGRAM")
|
|
443
474
|
@click.option(
|
|
444
475
|
"--finish-at",
|
|
445
476
|
"finish_at",
|
|
@@ -461,6 +492,8 @@ def _local(iso: Optional[str]) -> str:
|
|
|
461
492
|
def analyze(program_file, finish_at, start_at, json_output, strict):
|
|
462
493
|
"""Total length, critical path, resource conflicts and clock times. No sign-in needed.
|
|
463
494
|
|
|
495
|
+
PROGRAM is a JSON or YAML file, an http(s) URL, or - for standard input.
|
|
496
|
+
|
|
464
497
|
\b
|
|
465
498
|
Computed by the hosted MCP server (analyze_schedule); nothing is published.
|
|
466
499
|
rhylthyme analyze dinner.json --finish-at 19:00
|
|
@@ -520,7 +553,7 @@ ENV_BY_TYPE = {
|
|
|
520
553
|
|
|
521
554
|
|
|
522
555
|
@click.command("publish")
|
|
523
|
-
@click.argument("program_file",
|
|
556
|
+
@click.argument("program_file", metavar="PROGRAM")
|
|
524
557
|
@click.option(
|
|
525
558
|
"-e",
|
|
526
559
|
"--env",
|
|
@@ -539,14 +572,24 @@ ENV_BY_TYPE = {
|
|
|
539
572
|
help="Print {url, imageUrl, makespanSeconds, warnings} as JSON.",
|
|
540
573
|
)
|
|
541
574
|
@click.option("-q", "--quiet", is_flag=True, help="Print only the live-timeline URL.")
|
|
542
|
-
|
|
543
|
-
""
|
|
575
|
+
@click.option(
|
|
576
|
+
"--image",
|
|
577
|
+
"image_path",
|
|
578
|
+
type=click.Path(dir_okay=False),
|
|
579
|
+
default=None,
|
|
580
|
+
help="Also save a PNG of the timeline here.",
|
|
581
|
+
)
|
|
582
|
+
def publish(program_file, environment, open_url, json_output, quiet, image_path):
|
|
583
|
+
"""Publish a program as a live, shareable timeline. No sign-in needed.
|
|
584
|
+
|
|
585
|
+
PROGRAM is a JSON or YAML file, an http(s) URL, or - for standard input.
|
|
544
586
|
|
|
545
587
|
\b
|
|
546
588
|
The hosted MCP server validates the program first and refuses an invalid
|
|
547
589
|
one, so run `rhylthyme validate` locally and fix what it reports.
|
|
548
590
|
rhylthyme publish dinner.json
|
|
549
591
|
rhylthyme publish blot.json -e lab -q
|
|
592
|
+
rhylthyme publish https://example.org/party.json --image party.png --open
|
|
550
593
|
"""
|
|
551
594
|
program = _load_program(program_file)
|
|
552
595
|
env = environment or ENV_BY_TYPE.get(
|
|
@@ -585,6 +628,13 @@ def publish(program_file, environment, open_url, json_output, quiet):
|
|
|
585
628
|
click.echo(published.text.strip())
|
|
586
629
|
if url:
|
|
587
630
|
click.echo(f"\nLive timeline: {url}")
|
|
631
|
+
if image_path and info.get("imageUrl"):
|
|
632
|
+
try:
|
|
633
|
+
Path(image_path).write_bytes(_fetch_bytes(info["imageUrl"]))
|
|
634
|
+
if not json_output and not quiet:
|
|
635
|
+
click.echo(f"Picture: {image_path}")
|
|
636
|
+
except Exception as e: # noqa: BLE001
|
|
637
|
+
click.echo(f"Could not save the picture: {e}", err=True)
|
|
588
638
|
if open_url and url:
|
|
589
639
|
webbrowser.open(url)
|
|
590
640
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rhylthyme-cli-runner
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3a0
|
|
4
4
|
Summary: CLI runner for Rhylthyme real-time program schedules
|
|
5
5
|
Home-page: https://github.com/rhylthyme/rhylthyme-cli-runner
|
|
6
6
|
Author: Rhylthyme Team
|
|
@@ -69,6 +69,106 @@ rhylthyme login
|
|
|
69
69
|
rhylthyme generate "roast chicken, potatoes and green beans for 6" -e kitchen --by 19:00 --with "one oven"
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## A timeline in five commands: a birthday party
|
|
73
|
+
|
|
74
|
+
Parents collect at 4:00. Working backwards from that: when does the cake go in
|
|
75
|
+
the oven, when do the balloons go up, and do the pizzas and the cake ever want
|
|
76
|
+
the oven at the same time?
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install rhylthyme-cli-runner
|
|
80
|
+
|
|
81
|
+
# 1. a program: the party, the kitchen and the setup crew, 13 steps
|
|
82
|
+
curl -sO https://raw.githubusercontent.com/rhylthyme/rhylthyme-cli-runner/main/examples/recipe/birthday-party.json
|
|
83
|
+
|
|
84
|
+
# 2. is it well formed?
|
|
85
|
+
rhylthyme validate birthday-party.json --strict
|
|
86
|
+
|
|
87
|
+
# 3. any clashes, and what time does everything start if we finish at 4?
|
|
88
|
+
rhylthyme analyze birthday-party.json --finish-at 16:00
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
**Makespan:** 3h 10m across 3 tracks, 13 steps.
|
|
93
|
+
**Critical path:** Guests arrive → Party games → Pizza → Candles and cake → Piñata, until it breaks → Party bags and goodbyes
|
|
94
|
+
**Binding constraints:** step dependencies only — no resource or in-flight limit gates the critical path.
|
|
95
|
+
**Peak concurrency:** 2 steps at 10:00.
|
|
96
|
+
**Resource conflicts:** none.
|
|
97
|
+
|
|
98
|
+
**Tracks that finish early:** Kitchen (1h 10m early), Setup crew (50m early)
|
|
99
|
+
|
|
100
|
+
Start Sat 12:50, finish Sat 16:00 (local time)
|
|
101
|
+
Sat 12:50 Bake the cake
|
|
102
|
+
Sat 13:00 Balloons and banner
|
|
103
|
+
Sat 13:25 Cake cools
|
|
104
|
+
Sat 13:30 Hang the piñata
|
|
105
|
+
Sat 13:50 Guests arrive
|
|
106
|
+
Sat 14:05 Frost and decorate
|
|
107
|
+
Sat 14:20 Party games
|
|
108
|
+
Sat 14:35 Pizzas in the oven
|
|
109
|
+
Sat 14:50 Fill party bags
|
|
110
|
+
Sat 14:50 Pizza
|
|
111
|
+
Sat 15:15 Candles and cake
|
|
112
|
+
Sat 15:30 Piñata, until it breaks
|
|
113
|
+
Sat 15:45 Party bags and goodbyes
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 4. a live timeline for the fridge door (or a phone): timers, cues, and you end the piñata when it breaks
|
|
118
|
+
rhylthyme publish birthday-party.json --open --image party-preview.png
|
|
119
|
+
|
|
120
|
+
# 5. a picture for the group chat, on the wall clock (needs Node; nothing to install)
|
|
121
|
+
npx -y github:rhylthyme/rhylthyme-timeline birthday-party.json -o birthday-party.png \
|
|
122
|
+
--style web --palette vivid --label-overflow outside --start-at "$(date +%F)T12:50:00"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+

|
|
126
|
+
|
|
127
|
+
The pizzas go in 15 minutes after the games *start*, so they come out as the
|
|
128
|
+
games end; the party bags get filled while everyone is eating. Try
|
|
129
|
+
`--finish-at 17:30`, or turn the cake into a two-hour fruitcake (`"seconds": "2h"`
|
|
130
|
+
on `bake`) and `analyze` reports the clash: `oven needs 2 but max is 1 from
|
|
131
|
+
1:45:00 to 2:00:00 (bake, heat-pizza)`. `rhylthyme run birthday-party.json --time-scale 60` rehearses
|
|
132
|
+
the whole afternoon in the terminal at a minute per second.
|
|
133
|
+
|
|
134
|
+
## This repository and rhylthyme-mcp
|
|
135
|
+
|
|
136
|
+
Two repositories do related things and are easy to confuse. This one is the
|
|
137
|
+
**command a person types**. [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp)
|
|
138
|
+
is the **server an AI assistant talks to**.
|
|
139
|
+
|
|
140
|
+
| | [rhylthyme-mcp](https://github.com/rhylthyme/rhylthyme-mcp) | [rhylthyme-cli-runner](https://github.com/rhylthyme/rhylthyme-cli-runner) |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| What it is | The MCP **server**: the tools an AI assistant calls | A command-line **program**: the `rhylthyme` command |
|
|
143
|
+
| Who uses it | Claude, ChatGPT, Cursor or any MCP client, on a person's behalf | A person at a terminal, a script, or CI |
|
|
144
|
+
| Where it runs | Hosted at `mcp.rhylthyme.com`; nothing to install | On your machine: `pip install rhylthyme-cli-runner` |
|
|
145
|
+
| Language | JavaScript (Node 20+) | Python 3.12+ |
|
|
146
|
+
| Input | A program the assistant builds in conversation | A program file on disk (JSON or YAML) |
|
|
147
|
+
| Validate a program | `validate_program` | `rhylthyme validate` (works offline) |
|
|
148
|
+
| Timing, conflicts, deadlines | `analyze_schedule` | `rhylthyme analyze` (asks the server) |
|
|
149
|
+
| Publish a live timeline | `visualize_schedule` | `rhylthyme publish` (asks the server) |
|
|
150
|
+
| Run a schedule with timers | no: it hands back a link to the web timeline | `rhylthyme run`, an interactive terminal runner |
|
|
151
|
+
| Recorded runs, calibration | reads runs saved to an account | records runs locally; `rhylthyme runs`, `rhylthyme calibrate` |
|
|
152
|
+
| Catalog search, imports, account library | yes | no |
|
|
153
|
+
| Also in the repository | the `rhylthyme-mcp` PyPI package (a stdio bridge to the hosted server), the Claude plugin marketplace | the Claude skill's source, the prompt-evaluation harness and its results, `rhylthyme mcp-test` |
|
|
154
|
+
|
|
155
|
+
How they fit together: this tool is one of the MCP server's clients.
|
|
156
|
+
`rhylthyme analyze`, `publish`, `generate` and `mcp-test` are MCP calls to
|
|
157
|
+
`mcp.rhylthyme.com`; `rhylthyme validate`, `run`, `runs` and `calibrate` never
|
|
158
|
+
touch the network. Each has its own validator for the same program schema
|
|
159
|
+
(Python here, JavaScript there), so a program is checked again when it is
|
|
160
|
+
published.
|
|
161
|
+
|
|
162
|
+
Use this repository if you have a program file and a terminal, want timers in
|
|
163
|
+
the terminal, or keep run records. Use rhylthyme-mcp to connect Claude, ChatGPT
|
|
164
|
+
or another assistant, or to read or self-host the server.
|
|
165
|
+
|
|
166
|
+
Two names to keep apart: `rhylthyme-cli-runner` on PyPI is this tool (command
|
|
167
|
+
`rhylthyme`); `rhylthyme-mcp` on PyPI is the server's stdio bridge (command
|
|
168
|
+
`rhylthyme-mcp`). The program format itself is defined in
|
|
169
|
+
[rhylthyme-spec](https://github.com/rhylthyme/rhylthyme-spec), with examples in
|
|
170
|
+
[rhylthyme-examples](https://github.com/rhylthyme/rhylthyme-examples).
|
|
171
|
+
|
|
72
172
|
## Installation
|
|
73
173
|
|
|
74
174
|
### From Source
|
|
@@ -257,6 +357,16 @@ it for conflicts, and hand back a live timeline. It follows the layout of
|
|
|
257
357
|
[K-Dense scientific skills](https://github.com/K-Dense-AI/claude-scientific-skills):
|
|
258
358
|
a `SKILL.md` plus `references/`.
|
|
259
359
|
|
|
360
|
+
The easiest install is the Claude Code plugin, which also connects the hosted
|
|
361
|
+
MCP server:
|
|
362
|
+
|
|
363
|
+
```
|
|
364
|
+
/plugin marketplace add rhylthyme/rhylthyme-mcp
|
|
365
|
+
/plugin install rhylthyme@rhylthyme
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
Or copy the skill by hand:
|
|
369
|
+
|
|
260
370
|
```bash
|
|
261
371
|
# Claude Code, for one user
|
|
262
372
|
mkdir -p ~/.claude/skills
|
|
@@ -421,13 +531,15 @@ and tracks that finish early. No sign-in; nothing is published.
|
|
|
421
531
|
|
|
422
532
|
### `rhylthyme publish`
|
|
423
533
|
|
|
424
|
-
Publishes a program file
|
|
534
|
+
Publishes a program (a file, an http(s) URL, or `-` for standard input) as a
|
|
535
|
+
live timeline and prints its URL. No sign-in.
|
|
425
536
|
A published timeline is reachable by anyone who has the link.
|
|
426
537
|
|
|
427
538
|
**Options:**
|
|
428
539
|
- `-e, --env`: `generic`, `kitchen`, `lab`, `events`, `gym` (default: from `environmentType`)
|
|
429
540
|
- `-q, --quiet`: print only the URL
|
|
430
541
|
- `--json`: `url`, `shareId`, `imageUrl`, `makespanSeconds`, `warnings`
|
|
542
|
+
- `--image PATH`: also save a PNG of the timeline
|
|
431
543
|
- `--open`: open it in a browser
|
|
432
544
|
|
|
433
545
|
### `rhylthyme plan`
|
|
@@ -511,3 +511,58 @@ def test_clock_to_iso():
|
|
|
511
511
|
assert _clock_to_iso(iso, now) == iso
|
|
512
512
|
assert _clock_to_iso("25:00", now) == "25:00", "left for the server to reject"
|
|
513
513
|
assert _clock_to_iso("7", now) == "7", "a bare number is not a time"
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def test_publish_and_analyze_read_a_url_or_stdin(
|
|
517
|
+
fake_mcp, config_home, tmp_path, monkeypatch
|
|
518
|
+
):
|
|
519
|
+
import threading
|
|
520
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
521
|
+
|
|
522
|
+
class Files(BaseHTTPRequestHandler):
|
|
523
|
+
def log_message(self, *a):
|
|
524
|
+
pass
|
|
525
|
+
|
|
526
|
+
def do_GET(self):
|
|
527
|
+
if self.path == "/dinner.json":
|
|
528
|
+
body, status = json.dumps(PROGRAM).encode(), 200
|
|
529
|
+
elif self.path == "/timeline.png":
|
|
530
|
+
body, status = b"\x89PNG fake", 200
|
|
531
|
+
else:
|
|
532
|
+
body, status = b"nope", 404
|
|
533
|
+
self.send_response(status)
|
|
534
|
+
self.send_header("Content-Length", str(len(body)))
|
|
535
|
+
self.end_headers()
|
|
536
|
+
self.wfile.write(body)
|
|
537
|
+
|
|
538
|
+
server = HTTPServer(("127.0.0.1", 0), Files)
|
|
539
|
+
threading.Thread(target=server.serve_forever, daemon=True).start()
|
|
540
|
+
base = f"http://127.0.0.1:{server.server_address[1]}"
|
|
541
|
+
try:
|
|
542
|
+
from_url = CliRunner().invoke(cli, ["publish", f"{base}/dinner.json", "-q"])
|
|
543
|
+
assert from_url.exit_code == 0, from_url.output
|
|
544
|
+
assert from_url.output.strip() == "https://kitchen.rhylthyme.com?share=abc"
|
|
545
|
+
assert fake_mcp.tool_calls()[-1][2]["program"]["programId"] == "dinner"
|
|
546
|
+
|
|
547
|
+
piped = CliRunner().invoke(cli, ["analyze", "-"], input=json.dumps(PROGRAM))
|
|
548
|
+
assert piped.exit_code == 0, piped.output
|
|
549
|
+
assert fake_mcp.tool_calls()[-1][1] == "analyze_schedule"
|
|
550
|
+
|
|
551
|
+
missing = CliRunner().invoke(cli, ["publish", f"{base}/nope.json"])
|
|
552
|
+
assert missing.exit_code != 0 and "404" in missing.output
|
|
553
|
+
assert (
|
|
554
|
+
CliRunner()
|
|
555
|
+
.invoke(cli, ["publish", str(tmp_path / "absent.json")])
|
|
556
|
+
.exit_code
|
|
557
|
+
!= 0
|
|
558
|
+
)
|
|
559
|
+
finally:
|
|
560
|
+
server.shutdown()
|
|
561
|
+
server.server_close()
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def test_clock_and_sources_reject_other_schemes():
|
|
565
|
+
from rhylthyme_cli_runner.remote.cli import _fetch_bytes
|
|
566
|
+
|
|
567
|
+
with pytest.raises(ValueError):
|
|
568
|
+
_fetch_bytes("file:///etc/passwd")
|
|
@@ -69,3 +69,14 @@ def test_every_command_the_skill_names_exists():
|
|
|
69
69
|
assert {"validate", "analyze", "publish", "run", "calibrate", "generate"} <= named
|
|
70
70
|
missing = named - set(cli.commands)
|
|
71
71
|
assert not missing, missing
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_the_readme_recipe_program_is_valid_and_is_what_the_readme_shows():
|
|
75
|
+
root = SKILL.parent.parent
|
|
76
|
+
recipe = root / "examples" / "recipe" / "birthday-party.json"
|
|
77
|
+
result = CliRunner().invoke(cli, ["validate", str(recipe), "--strict"])
|
|
78
|
+
assert result.exit_code == 0, result.output
|
|
79
|
+
readme = (root / "README.md").read_text()
|
|
80
|
+
assert "examples/recipe/birthday-party.json" in readme
|
|
81
|
+
for step in json.loads(recipe.read_text())["tracks"][0]["steps"]:
|
|
82
|
+
assert step["name"] in readme, f"README itinerary is missing {step['name']!r}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/gold.py
RENAMED
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/src/rhylthyme_cli_runner/eval/llm.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_example_validation.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_examples_integration.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_expand_replicates.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_planner_time_strings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_validate_examples_ci.py
RENAMED
|
File without changes
|
{rhylthyme_cli_runner-0.2.2a0 → rhylthyme_cli_runner-0.2.3a0}/tests/test_validate_program.py
RENAMED
|
File without changes
|