pytorial 0.1__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.
pytorial-0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Daniel Bosk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pytorial-0.1/PKG-INFO ADDED
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: pytorial
3
+ Version: 0.1
4
+ Summary: Interactive command-line tutorials with PTY-backed steps
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Author: Daniel Bosk
8
+ Author-email: daniel@bosk.se
9
+ Maintainer: Daniel Bosk
10
+ Maintainer-email: dbosk@kth.se
11
+ Requires-Python: >=3.10
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Intended Audience :: Education
16
+ Classifier: Topic :: Education
17
+ Requires-Dist: PyYAML (>=6)
18
+ Requires-Dist: platformdirs (>=4)
19
+ Requires-Dist: rich (>=12.3.0)
20
+ Requires-Dist: typer (>=0.9.0)
21
+ Project-URL: Bug Tracker, https://github.com/dbosk/tutorial/issues
22
+ Project-URL: Repository, https://github.com/dbosk/tutorial
23
+ Description-Content-Type: text/markdown
24
+
25
+ # tutorial
26
+
27
+ A Python library for interactive command-line tutorials.
28
+
29
+ ## Install
30
+
31
+ ```
32
+ pipx install tutorial
33
+ # or
34
+ uv tool install tutorial
35
+ ```
36
+
37
+ To run from a clone instead of an installed copy, see
38
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
39
+
40
+ ## Usage
41
+
42
+ The package ships with three built-in tutorials. The first one is a
43
+ self-guided tour of the tool itself:
44
+
45
+ ```
46
+ tutorial run using-tutorials
47
+ ```
48
+
49
+ Companion tours: `tutorial run shell-basics` (shell and editor skills)
50
+ and `tutorial run writing-tutorials` (authoring tutorials of your own).
51
+
52
+ ### Discover what's available
53
+
54
+ ```
55
+ tutorial list
56
+ ```
57
+
58
+ Pipe the same command into a script to get tab-separated rows instead
59
+ of the rich table.
60
+
61
+ ### Run or resume
62
+
63
+ ```
64
+ tutorial run <id> # start or resume saved progress
65
+ tutorial run --restart <id> # discard saved progress and start over
66
+ ```
67
+
68
+ ### Review past runs
69
+
70
+ ```
71
+ tutorial review <id>
72
+ tutorial review <id> --step 2
73
+ tutorial review <id> --run-id <run-id>
74
+ ```
75
+
76
+ ### Trust author-supplied shell checks
77
+
78
+ ```
79
+ tutorial run --allow-shell-checks <id>
80
+ ```
81
+
82
+ A tutorial step may declare a `check_command` that runs in your shell
83
+ to validate the step. This validation is opt-in: pass
84
+ `--allow-shell-checks` only when you trust the tutorial author.
85
+
86
+ ## Writing tutorials
87
+
88
+ Take the interactive walkthrough first:
89
+
90
+ ```
91
+ tutorial run writing-tutorials
92
+ ```
93
+
94
+ ### File format
95
+
96
+ A tutorial is a single Markdown file beginning with YAML front matter:
97
+
98
+ ````markdown
99
+ ---
100
+ id: my-tutorial
101
+ title: My Tutorial
102
+ summary: One-line description shown by `tutorial list`.
103
+ ---
104
+
105
+ # First step
106
+
107
+ ```tutorial-step
108
+ required_patterns:
109
+ - some-command
110
+ hint: Try running `some-command`.
111
+ ```
112
+
113
+ Step body in Markdown.
114
+ ````
115
+
116
+ - Required front-matter fields: `id`, `title`, `summary`.
117
+ - Each top-level `# Heading` becomes one step.
118
+ - A step may begin with a fenced `tutorial-step` YAML block. Recognised
119
+ fields are `required_patterns`, `check_command`, `hint`, `edit_file`,
120
+ `kind`, `options`, and `answers`.
121
+ - Without `kind` or `edit_file`, a step opens an interactive shell.
122
+ - `edit_file` opens a workspace-relative file in `$EDITOR`, falling
123
+ back to `vim`, `vi`, or `nano` when `$EDITOR` is unset.
124
+ - `kind: input`, `kind: single_select`, and `kind: multi_select` prompt
125
+ directly in the CLI instead of opening a shell or editor. `input`
126
+ answers use the same string-or-`{mode, pattern}` format as
127
+ `required_patterns`; select questions use literal option text in
128
+ `options` and `answers`.
129
+ - `check_command` only runs when the reader passes
130
+ `--allow-shell-checks`.
131
+
132
+ ### Share and install
133
+
134
+ Install a tutorial Markdown file or URL into the user's tutorial
135
+ directory:
136
+
137
+ ```
138
+ tutorial install path/to/tutorial.md
139
+ tutorial install https://example.com/tutorial.md
140
+ tutorial install --force path/to/tutorial.md
141
+ ```
142
+
143
+ Use `--force` to overwrite an installed tutorial with the same `id`.
144
+ The standalone CLI auto-loads installed tutorials alongside the
145
+ built-ins.
146
+
147
+ ### Load ad hoc
148
+
149
+ To use a tutorial without installing it, point at the file or directory
150
+ on the command line:
151
+
152
+ ```
153
+ tutorial list --tutorial-path some/dir
154
+ tutorial run --tutorial-path some/dir <id>
155
+ ```
156
+
157
+ `--tutorial-path` may be repeated and is appended after the built-ins.
158
+
159
+ ## Embedding the CLI
160
+
161
+ The package can be mounted as a subcommand inside another Typer or
162
+ argparse application via `add_typer_subcommand` and
163
+ `add_argparse_subcommand` in `src/tutorial/cli.py`. Embedded hosts
164
+ prepend the `using-tutorials` lesson before host-specific tutorials, do
165
+ not load the user's installed tutorial directory, and hide the
166
+ standalone-only `install` command.
167
+
168
+ ## Contributing
169
+
170
+ For the literate sources, the `make` build, and the repo layout, see
171
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
172
+
pytorial-0.1/README.md ADDED
@@ -0,0 +1,147 @@
1
+ # tutorial
2
+
3
+ A Python library for interactive command-line tutorials.
4
+
5
+ ## Install
6
+
7
+ ```
8
+ pipx install tutorial
9
+ # or
10
+ uv tool install tutorial
11
+ ```
12
+
13
+ To run from a clone instead of an installed copy, see
14
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
15
+
16
+ ## Usage
17
+
18
+ The package ships with three built-in tutorials. The first one is a
19
+ self-guided tour of the tool itself:
20
+
21
+ ```
22
+ tutorial run using-tutorials
23
+ ```
24
+
25
+ Companion tours: `tutorial run shell-basics` (shell and editor skills)
26
+ and `tutorial run writing-tutorials` (authoring tutorials of your own).
27
+
28
+ ### Discover what's available
29
+
30
+ ```
31
+ tutorial list
32
+ ```
33
+
34
+ Pipe the same command into a script to get tab-separated rows instead
35
+ of the rich table.
36
+
37
+ ### Run or resume
38
+
39
+ ```
40
+ tutorial run <id> # start or resume saved progress
41
+ tutorial run --restart <id> # discard saved progress and start over
42
+ ```
43
+
44
+ ### Review past runs
45
+
46
+ ```
47
+ tutorial review <id>
48
+ tutorial review <id> --step 2
49
+ tutorial review <id> --run-id <run-id>
50
+ ```
51
+
52
+ ### Trust author-supplied shell checks
53
+
54
+ ```
55
+ tutorial run --allow-shell-checks <id>
56
+ ```
57
+
58
+ A tutorial step may declare a `check_command` that runs in your shell
59
+ to validate the step. This validation is opt-in: pass
60
+ `--allow-shell-checks` only when you trust the tutorial author.
61
+
62
+ ## Writing tutorials
63
+
64
+ Take the interactive walkthrough first:
65
+
66
+ ```
67
+ tutorial run writing-tutorials
68
+ ```
69
+
70
+ ### File format
71
+
72
+ A tutorial is a single Markdown file beginning with YAML front matter:
73
+
74
+ ````markdown
75
+ ---
76
+ id: my-tutorial
77
+ title: My Tutorial
78
+ summary: One-line description shown by `tutorial list`.
79
+ ---
80
+
81
+ # First step
82
+
83
+ ```tutorial-step
84
+ required_patterns:
85
+ - some-command
86
+ hint: Try running `some-command`.
87
+ ```
88
+
89
+ Step body in Markdown.
90
+ ````
91
+
92
+ - Required front-matter fields: `id`, `title`, `summary`.
93
+ - Each top-level `# Heading` becomes one step.
94
+ - A step may begin with a fenced `tutorial-step` YAML block. Recognised
95
+ fields are `required_patterns`, `check_command`, `hint`, `edit_file`,
96
+ `kind`, `options`, and `answers`.
97
+ - Without `kind` or `edit_file`, a step opens an interactive shell.
98
+ - `edit_file` opens a workspace-relative file in `$EDITOR`, falling
99
+ back to `vim`, `vi`, or `nano` when `$EDITOR` is unset.
100
+ - `kind: input`, `kind: single_select`, and `kind: multi_select` prompt
101
+ directly in the CLI instead of opening a shell or editor. `input`
102
+ answers use the same string-or-`{mode, pattern}` format as
103
+ `required_patterns`; select questions use literal option text in
104
+ `options` and `answers`.
105
+ - `check_command` only runs when the reader passes
106
+ `--allow-shell-checks`.
107
+
108
+ ### Share and install
109
+
110
+ Install a tutorial Markdown file or URL into the user's tutorial
111
+ directory:
112
+
113
+ ```
114
+ tutorial install path/to/tutorial.md
115
+ tutorial install https://example.com/tutorial.md
116
+ tutorial install --force path/to/tutorial.md
117
+ ```
118
+
119
+ Use `--force` to overwrite an installed tutorial with the same `id`.
120
+ The standalone CLI auto-loads installed tutorials alongside the
121
+ built-ins.
122
+
123
+ ### Load ad hoc
124
+
125
+ To use a tutorial without installing it, point at the file or directory
126
+ on the command line:
127
+
128
+ ```
129
+ tutorial list --tutorial-path some/dir
130
+ tutorial run --tutorial-path some/dir <id>
131
+ ```
132
+
133
+ `--tutorial-path` may be repeated and is appended after the built-ins.
134
+
135
+ ## Embedding the CLI
136
+
137
+ The package can be mounted as a subcommand inside another Typer or
138
+ argparse application via `add_typer_subcommand` and
139
+ `add_argparse_subcommand` in `src/tutorial/cli.py`. Embedded hosts
140
+ prepend the `using-tutorials` lesson before host-specific tutorials, do
141
+ not load the user's installed tutorial directory, and hide the
142
+ standalone-only `install` command.
143
+
144
+ ## Contributing
145
+
146
+ For the literate sources, the `make` build, and the repo layout, see
147
+ [`CONTRIBUTING.md`](CONTRIBUTING.md).
@@ -0,0 +1,60 @@
1
+ [project]
2
+ name = "pytorial"
3
+ version = "0.1"
4
+ description = "Interactive command-line tutorials with PTY-backed steps"
5
+ license = "MIT"
6
+ readme = "README.md"
7
+ authors = [
8
+ {name = "Daniel Bosk", email = "daniel@bosk.se"}
9
+ ]
10
+ maintainers = [
11
+ {name = "Daniel Bosk", email = "dbosk@kth.se"}
12
+ ]
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Intended Audience :: Education",
18
+ "Topic :: Education"
19
+ ]
20
+ requires-python = ">=3.10"
21
+ dependencies = [
22
+ "platformdirs>=4",
23
+ "PyYAML>=6",
24
+ "rich>=12.3.0",
25
+ "typer>=0.9.0"
26
+ ]
27
+
28
+ [project.scripts]
29
+ tutorial = "tutorial.cli:app"
30
+ pytorial = "tutorial.cli:app"
31
+
32
+ [project.urls]
33
+ repository = "https://github.com/dbosk/tutorial"
34
+ "Bug Tracker" = "https://github.com/dbosk/tutorial/issues"
35
+
36
+ [tool.poetry]
37
+ packages = [{include = "tutorial", from = "src"}]
38
+ include = [
39
+ {path = "src/**/*.py", format = "wheel"},
40
+ {path = "src/tutorial/tutorials/*.md"},
41
+ ]
42
+ exclude = [
43
+ "src/tutorial/.gitignore",
44
+ "src/tutorial/Makefile",
45
+ "src/tutorial/*.nw",
46
+ "src/tutorial/*.tex",
47
+ "src/tutorial/ltxobj",
48
+ ]
49
+
50
+ [tool.black]
51
+ target-version = ["py310"]
52
+
53
+ [build-system]
54
+ requires = ["poetry-core>=1.0.0"]
55
+ build-backend = "poetry.core.masonry.api"
56
+
57
+ [dependency-groups]
58
+ dev = [
59
+ "pytest>=7.4"
60
+ ]
@@ -0,0 +1,101 @@
1
+ ---
2
+ id: shell-basics
3
+ title: Shell Basics
4
+ summary: Use directories, files, paths, and an editor in the workspace.
5
+ ---
6
+
7
+ # Inspect the workspace
8
+
9
+ ```tutorial-step
10
+ required_patterns:
11
+ - pwd
12
+ - ls
13
+ hint: The shell starts in the tutorial workspace.
14
+ ```
15
+
16
+ Run `pwd` and `ls`.
17
+
18
+ Each tutorial run gets its own workspace. These commands show where your
19
+ files for this run will live.
20
+
21
+ # Create a directory
22
+
23
+ ```tutorial-step
24
+ required_patterns:
25
+ - mkdir notes
26
+ - ls
27
+ - notes
28
+ hint: Create a directory called `notes`, then list the workspace again.
29
+ ```
30
+
31
+ Create a directory named `notes`, then run `ls` again.
32
+
33
+ The contrast here is between the workspace before and after one directory
34
+ is added.
35
+
36
+ # Create a file in that directory
37
+
38
+ ```tutorial-step
39
+ required_patterns:
40
+ - touch notes/todo.md
41
+ - ls notes
42
+ - todo.md
43
+ hint: Use `touch` with the nested path `notes/todo.md`.
44
+ ```
45
+
46
+ Create `notes/todo.md`, then run `ls notes`.
47
+
48
+ Keep the same path stem `notes` while changing only what you create:
49
+ first a directory, then a file inside it.
50
+
51
+ # Edit the file
52
+
53
+ ```tutorial-step
54
+ edit_file: notes/todo.md
55
+ required_patterns:
56
+ - tutorial authoring
57
+ - remember this path
58
+ ```
59
+
60
+ Open `notes/todo.md` in your editor and add two short lines:
61
+
62
+ - `tutorial authoring`
63
+ - `remember this path`
64
+
65
+ Save the file and quit the editor.
66
+
67
+ The path stays the same, but the interaction changes from shell commands
68
+ to an editor-backed step.
69
+
70
+ # Review the directory and the file
71
+
72
+ ```tutorial-step
73
+ required_patterns:
74
+ - ls notes
75
+ - todo.md
76
+ - cat notes/todo.md
77
+ - tutorial authoring
78
+ hint: First list the directory, then display the file.
79
+ ```
80
+
81
+ Run `ls notes` and `cat notes/todo.md`.
82
+
83
+ This contrasts two related views: listing a directory tells you which
84
+ files exist, while `cat` shows what one file contains.
85
+
86
+ # Generalise the pattern for tutorial files
87
+
88
+ ```tutorial-step
89
+ required_patterns:
90
+ - mkdir my-tutorials
91
+ - touch my-tutorials/demo.md
92
+ - ls my-tutorials
93
+ - demo.md
94
+ hint: Repeat the same directory-and-file pattern with new names.
95
+ ```
96
+
97
+ Create a directory named `my-tutorials`, create
98
+ `my-tutorials/demo.md`, then run `ls my-tutorials`.
99
+
100
+ Now the names vary, but the path pattern `directory/file.md` stays the
101
+ same. That is the pattern the tutorial-authoring lesson will build on.
@@ -0,0 +1,212 @@
1
+ ---
2
+ id: using-tutorials
3
+ title: Using Tutorials
4
+ summary: Learn how list, run, question prompts, and review fit together.
5
+ ---
6
+
7
+ # List the available tutorials
8
+
9
+ ```tutorial-step
10
+ required_patterns:
11
+ - tutorial list
12
+ - using-tutorials
13
+ - shell-basics
14
+ - writing-tutorials
15
+ hint: Run `tutorial list`.
16
+ ```
17
+
18
+ Run `tutorial list`.
19
+
20
+ In a terminal, `tutorial list` renders a readable table. When the same
21
+ command is piped into a script, it switches to tab-separated rows.
22
+
23
+ Its main job is to show which tutorials are available and how much
24
+ progress is already saved for each one.
25
+
26
+ # Start another tutorial
27
+
28
+ ```tutorial-step
29
+ required_patterns:
30
+ - tutorial run --restart shell-basics
31
+ - "shell-basics $"
32
+ - "Step not complete yet: Inspect the workspace"
33
+ hint: Start `shell-basics`, then leave its first shell with `exit`.
34
+ ```
35
+
36
+ Run `tutorial run --restart shell-basics`.
37
+
38
+ The `run` command starts or resumes saved work. We use `--restart` here so
39
+ this step behaves the same way even if you have already finished
40
+ `shell-basics` before. `--restart` discards every saved transcript for that
41
+ tutorial, not just the step you are about to revisit.
42
+
43
+ Because this lesson is already running, the nested shell prompt changes to
44
+ `shell-basics $ ` so you can tell which tutorial owns the current shell.
45
+
46
+ For this step:
47
+
48
+ 1. Run `tutorial run --restart shell-basics`.
49
+ 2. When the nested shell opens, type `exit`.
50
+ 3. After the nested command reports that the step is not complete yet,
51
+ exit this outer shell too.
52
+
53
+ # Retry a shell step
54
+
55
+ ```tutorial-step
56
+ required_patterns:
57
+ - "using-tutorials $"
58
+ - pwd
59
+ - ls
60
+ hint: First exit after `pwd`, then choose `try again` and run both commands.
61
+ ```
62
+
63
+ Now return to this tutorial's own shell.
64
+
65
+ To feel how step validation works, try this step in two rounds:
66
+
67
+ 1. First run only `pwd`, then exit the shell.
68
+ 2. The tutorial should say the step is not complete yet.
69
+ 3. Choose `try again`, run both `pwd` and `ls`, then exit again.
70
+
71
+ Only the latest recorded attempt counts. If you leave now and come back
72
+ later, `tutorial run using-tutorials` resumes from the first unfinished
73
+ step unless you restart it.
74
+
75
+ # Capture the workflow in a file
76
+
77
+ ```tutorial-step
78
+ edit_file: tutorial-commands.txt
79
+ required_patterns:
80
+ - tutorial list discovers tutorials
81
+ - tutorial run starts or resumes one
82
+ - tutorial review shows saved transcripts
83
+ ```
84
+
85
+ Open `tutorial-commands.txt` in your editor and add these three lines:
86
+
87
+ - `tutorial list discovers tutorials`
88
+ - `tutorial run starts or resumes one`
89
+ - `tutorial review shows saved transcripts`
90
+
91
+ Save the file and quit the editor.
92
+
93
+ This contrasts a shell step with an editor step while keeping the same
94
+ validation idea: the tutorial only checks what was recorded after you
95
+ finish the step.
96
+
97
+ # Answer a one-line question
98
+
99
+ ```tutorial-step
100
+ kind: input
101
+ answers:
102
+ - mode: regex
103
+ pattern: ^tutorial\s+review\s+using-tutorials(?:\s+)?$
104
+ hint: Type the full review command for this tutorial.
105
+ ```
106
+
107
+ Type the exact command that reviews this tutorial:
108
+ `tutorial review using-tutorials`.
109
+
110
+ This is the smallest question step. The tutorial asks for one line of
111
+ text, records it immediately, and validates it without opening a shell.
112
+
113
+ # Pick the command that starts work
114
+
115
+ ```tutorial-step
116
+ kind: single_select
117
+ options:
118
+ - tutorial list
119
+ - tutorial run
120
+ - tutorial review
121
+ answers:
122
+ - tutorial run
123
+ ```
124
+
125
+ Choose the command that starts or resumes one tutorial.
126
+
127
+ Single-select questions show numbered options. You can answer with the
128
+ number or with the full option text.
129
+
130
+ # Pick every read-only tutorial command
131
+
132
+ ```tutorial-step
133
+ kind: multi_select
134
+ options:
135
+ - tutorial list
136
+ - tutorial run
137
+ - tutorial review
138
+ - tutorial install
139
+ answers:
140
+ - tutorial list
141
+ - tutorial review
142
+ hint: Choose the commands that only read tutorial definitions or saved state.
143
+ ```
144
+
145
+ Choose every tutorial command here that reads existing information without
146
+ starting a run or installing anything.
147
+
148
+ Multi-select questions also use literal option text. The learner may pick
149
+ the correct options in any order, but the tutorial still stores the
150
+ authored answer list.
151
+
152
+ # Review the saved run
153
+
154
+ ```tutorial-step
155
+ required_patterns:
156
+ - tutorial review using-tutorials
157
+ - Using Tutorials
158
+ - "Step 1: List the available tutorials"
159
+ - "Step 4: Capture the workflow in a file"
160
+ - "Step 7: Pick every read-only tutorial command"
161
+ - tutorial review shows saved transcripts
162
+ hint: Review `using-tutorials` from inside this shell.
163
+ ```
164
+
165
+ Run `tutorial review using-tutorials`.
166
+
167
+ Review reads the saved transcripts for the current run. Because this shell
168
+ session is still in progress, the review output can show the earlier saved
169
+ steps, but it cannot show this step until after you exit the shell and the
170
+ step is recorded.
171
+
172
+ At this point you have seen the main run-and-review workflow end to end:
173
+
174
+ - `tutorial list` discovers what you can run
175
+ - `tutorial run` starts or resumes one tutorial at a time
176
+ - question steps can ask for input, one choice, or several choices
177
+ - `tutorial review` inspects the saved record afterwards
178
+
179
+ # Inspect one step of the saved run
180
+
181
+ ```tutorial-step
182
+ required_patterns:
183
+ - tutorial review using-tutorials --step 1
184
+ - "Step 1: List the available tutorials"
185
+ hint: Review only step 1 from inside this shell.
186
+ ```
187
+
188
+ Run `tutorial review using-tutorials --step 1`.
189
+
190
+ `--step N` narrows the review to one 1-indexed step instead of the whole
191
+ run. Review normally shows the latest saved run for that tutorial; use
192
+ `--run-id <id>` to pick an older one instead. `tutorial review --help`
193
+ shows the full form.
194
+
195
+ # Install a tutorial from a file or URL
196
+
197
+ ```tutorial-step
198
+ kind: input
199
+ answers:
200
+ - mode: regex
201
+ pattern: ^tutorial\s+install\s+\S+(?:\s+--force)?\s*$
202
+ hint: Type an install command with a file path or URL.
203
+ ```
204
+
205
+ Type a command such as `tutorial install ./lesson.md` or
206
+ `tutorial install https://example.com/lesson.md --force`.
207
+
208
+ `tutorial install <source>` copies a tutorial Markdown file into your
209
+ installed tutorial directory so it appears in `tutorial list` alongside the
210
+ built-ins. Add `--force` to overwrite an installed tutorial with the same
211
+ `id`. This question step only records your answer, so you can practice the
212
+ command safely without installing anything.
@@ -0,0 +1,414 @@
1
+ ---
2
+ id: writing-tutorials
3
+ title: Writing Tutorials
4
+ summary: Create a tutorial file, add shell, editor, and question steps, and load it.
5
+ ---
6
+
7
+ # Create a tutorial directory
8
+
9
+ ```tutorial-step
10
+ required_patterns:
11
+ - mkdir my-tutorials
12
+ - ls
13
+ - my-tutorials
14
+ hint: Create `my-tutorials`, then list the workspace.
15
+ ```
16
+
17
+ Create a directory named `my-tutorials`, then run `ls`.
18
+
19
+ This lesson keeps the path `my-tutorials/greeting.md` invariant while you
20
+ add one tutorial feature at a time.
21
+
22
+ # Draft the smallest tutorial
23
+
24
+ ```tutorial-step
25
+ edit_file: my-tutorials/greeting.md
26
+ required_patterns:
27
+ - "id: greeting"
28
+ - "title: Greeting Tutorial"
29
+ - "summary: A tiny tutorial for saying hello."
30
+ - "# Say hello"
31
+ - echo hello
32
+ ```
33
+
34
+ Open `my-tutorials/greeting.md` and write this tutorial:
35
+
36
+ ```md
37
+ ---
38
+ id: greeting
39
+ title: Greeting Tutorial
40
+ summary: A tiny tutorial for saying hello.
41
+ ---
42
+
43
+ # Say hello
44
+
45
+ Run `echo hello`.
46
+ ```
47
+
48
+ This is the smallest useful tutorial: front matter plus one top-level
49
+ step.
50
+
51
+ Because this step has no `kind`, no `edit_file`, and no metadata fence,
52
+ the tutorial runner will open a shell for it.
53
+
54
+ # Load the tutorial from disk
55
+
56
+ ```tutorial-step
57
+ required_patterns:
58
+ - tutorial list --tutorial-path my-tutorials
59
+ - greeting
60
+ hint: Point the standalone CLI at `my-tutorials`.
61
+ ```
62
+
63
+ Run `tutorial list --tutorial-path my-tutorials`.
64
+
65
+ If the file parses, `greeting` should appear in the list.
66
+
67
+ # Add a contains match
68
+
69
+ ```tutorial-step
70
+ edit_file: my-tutorials/greeting.md
71
+ required_patterns:
72
+ - "```tutorial-step"
73
+ - "required_patterns:"
74
+ - " - echo hello"
75
+ ```
76
+
77
+ Keep the same tutorial and the same step, but add a metadata fence before
78
+ the instructions:
79
+
80
+ ````md
81
+ # Say hello
82
+
83
+ ```tutorial-step
84
+ required_patterns:
85
+ - echo hello
86
+ ```
87
+
88
+ Run `echo hello`.
89
+ ````
90
+
91
+ Only one aspect changed: the step now has an explicit validation rule.
92
+ It is still a shell step, because the metadata fence adds
93
+ `required_patterns` but still does not add `kind` or `edit_file`.
94
+
95
+ A bare string uses the default `contains` mode. That mode is the best
96
+ default because the runner strips ANSI control noise and collapses
97
+ whitespace before matching. Small differences such as trailing spaces
98
+ from shell completion do not force authors to write regexes.
99
+
100
+ # Contrast it with a regex match
101
+
102
+ ```tutorial-step
103
+ edit_file: my-tutorials/greeting.md
104
+ required_patterns:
105
+ - "required_patterns:"
106
+ - " - mode: regex"
107
+ - ' pattern: (?m)echo hello(?:\s+)?$'
108
+ ```
109
+
110
+ Keep the same tutorial, the same step title, and the same command, but
111
+ replace the string rule with an explicit regex rule:
112
+
113
+ ````md
114
+ # Say hello
115
+
116
+ ```tutorial-step
117
+ required_patterns:
118
+ - mode: regex
119
+ pattern: (?m)echo hello(?:\s+)?$
120
+ ```
121
+
122
+ Run `echo hello`.
123
+ ````
124
+
125
+ Only one critical aspect changed: the matching mode. The command
126
+ `echo hello` stays invariant, while the authored rule changes from a
127
+ short normalized contains check to a regex that says more precisely what
128
+ part of the transcript should match.
129
+
130
+ Use regex when you need that extra control. Otherwise, keep the shorter
131
+ string form.
132
+
133
+ # Match shell output with a regex
134
+
135
+ ```tutorial-step
136
+ edit_file: my-tutorials/greeting.md
137
+ required_patterns:
138
+ - "required_patterns:"
139
+ - " - mode: regex"
140
+ - " pattern: (?m)^hello$"
141
+ ```
142
+
143
+ Keep the same tutorial file, the same step title, and the same
144
+ `echo hello` command, but change the regex so it matches the output line
145
+ instead of the command line:
146
+
147
+ ````md
148
+ # Say hello
149
+
150
+ ```tutorial-step
151
+ required_patterns:
152
+ - mode: regex
153
+ pattern: (?m)^hello$
154
+ ```
155
+
156
+ Run `echo hello`.
157
+ ````
158
+
159
+ `(?m)` lets `^` and `$` mean line boundaries inside the recorded shell
160
+ transcript. That is where regex starts to earn its keep: `contains` can
161
+ only say that `hello` appears somewhere, while this rule matches one whole
162
+ line of shell output.
163
+
164
+ # Add an editor-backed step
165
+
166
+ ```tutorial-step
167
+ edit_file: my-tutorials/greeting.md
168
+ required_patterns:
169
+ - "# Edit a note"
170
+ - "edit_file: hello.txt"
171
+ - hello from the editor
172
+ ```
173
+
174
+ Add this second step to the same file:
175
+
176
+ ````md
177
+ # Edit a note
178
+
179
+ ```tutorial-step
180
+ edit_file: hello.txt
181
+ required_patterns:
182
+ - hello from the editor
183
+ ```
184
+
185
+ Open `hello.txt` and add `hello from the editor`.
186
+ ````
187
+
188
+ This now contrasts two different step backends in the same tutorial: a
189
+ shell step with explicit matching metadata and an editor-backed step with
190
+ `edit_file`. `edit_file` still selects the editor backend, while question
191
+ steps use `kind`. `edit_file` and `kind` are mutually exclusive, so
192
+ combining them is a parse-time error.
193
+
194
+ # Add an input question
195
+
196
+ ```tutorial-step
197
+ edit_file: my-tutorials/greeting.md
198
+ required_patterns:
199
+ - "# Type the greeting"
200
+ - "kind: input"
201
+ - "answers:"
202
+ - " - mode: regex"
203
+ - " pattern: (?i)hello(?:\\s+)?$"
204
+ ```
205
+
206
+ Add this third step to the same file:
207
+
208
+ ````md
209
+ # Type the greeting
210
+
211
+ ```tutorial-step
212
+ kind: input
213
+ answers:
214
+ - mode: regex
215
+ pattern: (?i)hello(?:\s+)?$
216
+ ```
217
+
218
+ Type `hello`.
219
+ ````
220
+
221
+ Input questions reuse the same string-or-regex answer format as
222
+ `required_patterns`. Only the interaction kind changes: the runner now
223
+ prompts for one line of text instead of opening a shell or editor.
224
+
225
+ # Add a single-select question
226
+
227
+ ```tutorial-step
228
+ edit_file: my-tutorials/greeting.md
229
+ required_patterns:
230
+ - "# Pick the shell command"
231
+ - "kind: single_select"
232
+ - "options:"
233
+ - " - echo hello"
234
+ - " - pwd"
235
+ - " - ls"
236
+ - "answers:"
237
+ - " - echo hello"
238
+ ```
239
+
240
+ Add this fourth step to the same file:
241
+
242
+ ````md
243
+ # Pick the shell command
244
+
245
+ ```tutorial-step
246
+ kind: single_select
247
+ options:
248
+ - echo hello
249
+ - pwd
250
+ - ls
251
+ answers:
252
+ - echo hello
253
+ ```
254
+
255
+ Choose the command that prints the greeting.
256
+ ````
257
+
258
+ Single-select questions use literal option text. The learner may answer by
259
+ number or by the full option, but the authored answer stays the option
260
+ text itself.
261
+
262
+ # Add a multi-select question
263
+
264
+ ```tutorial-step
265
+ edit_file: my-tutorials/greeting.md
266
+ required_patterns:
267
+ - "# Pick every inspection command"
268
+ - "kind: multi_select"
269
+ - "options:"
270
+ - " - pwd"
271
+ - " - ls"
272
+ - " - echo hello"
273
+ - "answers:"
274
+ - " - pwd"
275
+ - " - ls"
276
+ ```
277
+
278
+ Add this fifth step to the same file:
279
+
280
+ ````md
281
+ # Pick every inspection command
282
+
283
+ ```tutorial-step
284
+ kind: multi_select
285
+ options:
286
+ - pwd
287
+ - ls
288
+ - echo hello
289
+ answers:
290
+ - pwd
291
+ - ls
292
+ ```
293
+
294
+ Choose every command that inspects the workspace.
295
+ ````
296
+
297
+ Multi-select questions also use literal YAML lists. The learner may choose
298
+ the correct options in any order, but authors still list the correct
299
+ option texts explicitly under `answers`.
300
+
301
+ # Add a shell check to the editor step
302
+
303
+ ```tutorial-step
304
+ edit_file: my-tutorials/greeting.md
305
+ required_patterns:
306
+ - "check_command:"
307
+ - 'test "$(cat hello.txt)" = "hello from the editor"'
308
+ ```
309
+
310
+ Keep the same `# Edit a note` step, but add a filesystem-based shell check:
311
+
312
+ ````md
313
+ # Edit a note
314
+
315
+ ```tutorial-step
316
+ edit_file: hello.txt
317
+ required_patterns:
318
+ - hello from the editor
319
+ check_command: test "$(cat hello.txt)" = "hello from the editor"
320
+ ```
321
+
322
+ Open `hello.txt` and add `hello from the editor`.
323
+ ````
324
+
325
+ `check_command` is opt-in. The runner refuses to execute it unless the
326
+ reader passes `--allow-shell-checks`. It runs in a separate
327
+ `/bin/bash -lc` process with the tutorial workspace as its current
328
+ directory, so it can inspect files there but it does not inherit shell
329
+ variables or other session state from the interactive step. It is also
330
+ AND-ed with `required_patterns`: both checks must pass before the step
331
+ completes.
332
+
333
+ # Test the tutorial you wrote
334
+
335
+ ```tutorial-step
336
+ required_patterns:
337
+ - tutorial run --tutorial-path my-tutorials greeting
338
+ - "greeting $"
339
+ - "Step not complete yet: Say hello"
340
+ hint: Start `greeting`, then leave its first shell with `exit`.
341
+ ```
342
+
343
+ Run `tutorial run --tutorial-path my-tutorials greeting`.
344
+
345
+ This still tests a different aspect from `tutorial list --tutorial-path
346
+ my-tutorials`. Listing only proves that the file parses and the tutorial is
347
+ discoverable. Running it proves that the authored tutorial still starts
348
+ with the expected shell backend even after you add editor and question
349
+ steps later in the file.
350
+
351
+ Keep the tutorial file itself invariant here. Only the command changes: you
352
+ now move from inspecting tutorial metadata to exercising the authored
353
+ behaviour.
354
+
355
+ For this step:
356
+
357
+ 1. Run `tutorial run --tutorial-path my-tutorials greeting`.
358
+ 2. When the nested shell opens with the `greeting $ ` prompt, type `exit`.
359
+ 3. The nested run should report `Step not complete yet: Say hello`.
360
+ 4. Exit this lesson's shell too so the transcript is recorded.
361
+
362
+ # Iterate on one step with `tutorial develop`
363
+
364
+ ```tutorial-step
365
+ required_patterns:
366
+ - tutorial develop --tutorial-path my-tutorials greeting 1
367
+ - "greeting $"
368
+ - "Verdict: FAIL"
369
+ hint: Start develop for step 1, exit immediately, then keep the FAIL verdict and stop.
370
+ ```
371
+
372
+ Run `tutorial develop --tutorial-path my-tutorials greeting 1`.
373
+
374
+ For this step:
375
+
376
+ 1. Run `tutorial develop --tutorial-path my-tutorials greeting 1`.
377
+ 2. When the temporary `greeting $ ` shell opens, type `exit` immediately.
378
+ 3. Answer yes when `tutorial develop` asks whether the `FAIL` verdict is
379
+ correct.
380
+ 4. Answer no when it asks whether to run the step again.
381
+
382
+ `tutorial develop` exercises one authored step in a temporary workspace,
383
+ prints `PASS` or `FAIL`, and lets you edit and re-run that step without
384
+ restarting the whole tutorial. We point at step `1` by number here, but the
385
+ same command also accepts a regex that matches the step title. Develop also
386
+ enables shell checks by default; pass `--deny-shell-checks` to turn them
387
+ off for one session.
388
+
389
+ # Inspect the finished tutorial
390
+
391
+ ```tutorial-step
392
+ required_patterns:
393
+ - tutorial list --tutorial-path my-tutorials
394
+ - greeting
395
+ - cat my-tutorials/greeting.md
396
+ - " pattern: (?m)^hello$"
397
+ - "kind: input"
398
+ - "kind: single_select"
399
+ - "kind: multi_select"
400
+ - "edit_file: hello.txt"
401
+ - 'check_command: test "$(cat hello.txt)" = "hello from the editor"'
402
+ - " pattern: (?i)hello(?:\\s+)?$"
403
+ hint: List the tutorials, then display the file you wrote.
404
+ ```
405
+
406
+ Run `tutorial list --tutorial-path my-tutorials` and
407
+ `cat my-tutorials/greeting.md`.
408
+
409
+ At this point you have checked the same tutorial in three different ways:
410
+
411
+ - `tutorial list --tutorial-path my-tutorials` shows that it loads
412
+ - `tutorial run --tutorial-path my-tutorials greeting` shows that it runs
413
+ - `cat my-tutorials/greeting.md` lets you inspect the final shell, editor,
414
+ shell-check, and question metadata directly