agnostic-prompt-aps 1.1.2__py3-none-any.whl → 1.1.4__py3-none-any.whl

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.
Files changed (30) hide show
  1. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/METADATA +36 -5
  2. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/RECORD +28 -24
  3. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/WHEEL +1 -1
  4. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/entry_points.txt +1 -0
  5. aps_cli/__init__.py +1 -1
  6. aps_cli/__main__.py +5 -0
  7. aps_cli/cli.py +43 -53
  8. aps_cli/core.py +32 -25
  9. aps_cli/payload/agnostic-prompt-standard/SKILL.md +1 -2
  10. aps_cli/payload/agnostic-prompt-standard/assets/constants/constants-json-block-v1.0.0.example.md +3 -3
  11. aps_cli/payload/agnostic-prompt-standard/platforms/README.md +7 -1
  12. aps_cli/payload/agnostic-prompt-standard/platforms/_schemas/platform-manifest.schema.json +83 -10
  13. aps_cli/payload/agnostic-prompt-standard/platforms/_template/README.md +0 -2
  14. aps_cli/payload/agnostic-prompt-standard/platforms/_template/manifest.json +1 -2
  15. aps_cli/payload/agnostic-prompt-standard/platforms/claude-code/README.md +247 -0
  16. aps_cli/payload/agnostic-prompt-standard/platforms/claude-code/frontmatter/agent-frontmatter.md +98 -0
  17. aps_cli/payload/agnostic-prompt-standard/platforms/claude-code/frontmatter/rules-frontmatter.md +47 -0
  18. aps_cli/payload/agnostic-prompt-standard/platforms/claude-code/manifest.json +71 -0
  19. aps_cli/payload/agnostic-prompt-standard/platforms/claude-code/tools-registry.json +300 -0
  20. aps_cli/payload/agnostic-prompt-standard/platforms/vscode-copilot/README.md +3 -11
  21. aps_cli/payload/agnostic-prompt-standard/platforms/vscode-copilot/frontmatter/agent-frontmatter.md +1 -1
  22. aps_cli/payload/agnostic-prompt-standard/platforms/vscode-copilot/frontmatter/instructions-frontmatter.md +0 -1
  23. aps_cli/payload/agnostic-prompt-standard/platforms/vscode-copilot/manifest.json +0 -1
  24. aps_cli/payload/agnostic-prompt-standard/references/03-agentic-control.md +55 -14
  25. aps_cli/payload/agnostic-prompt-standard/references/04-schemas-and-types.md +0 -3
  26. aps_cli/payload/agnostic-prompt-standard/references/05-grammar.md +5 -3
  27. aps_cli/payload/agnostic-prompt-standard/assets/agents/vscode-agent-v1.0.0.agent.md +0 -187
  28. aps_cli/payload/agnostic-prompt-standard/platforms/vscode-copilot/templates/AGENTS.md +0 -7
  29. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/licenses/LICENSE +0 -0
  30. {agnostic_prompt_aps-1.1.2.dist-info → agnostic_prompt_aps-1.1.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,300 @@
1
+ {
2
+ "platformId": "claude-code",
3
+ "adapterVersion": "1.0.0",
4
+ "lastUpdated": "2026-01-20",
5
+ "toolNaming": {
6
+ "style": "Tools are referenced by their exact names (e.g., Bash, Read, Edit).",
7
+ "mcpTools": "MCP tools use pattern: mcp__<server>__<tool>",
8
+ "rule": "Use the exact tool name. No qualification or prefixes needed."
9
+ },
10
+ "toolSets": [
11
+ {
12
+ "id": "filesystem",
13
+ "mention": "filesystem",
14
+ "description": "Tool set: file system operations (read, write, edit, search).",
15
+ "tools": [
16
+ "Read",
17
+ "Write",
18
+ "Edit",
19
+ "Glob",
20
+ "Grep",
21
+ "NotebookEdit"
22
+ ]
23
+ },
24
+ {
25
+ "id": "execution",
26
+ "mention": "execution",
27
+ "description": "Tool set: command execution and task management.",
28
+ "tools": [
29
+ "Bash",
30
+ "Task",
31
+ "TodoWrite"
32
+ ]
33
+ },
34
+ {
35
+ "id": "web",
36
+ "mention": "web",
37
+ "description": "Tool set: web fetching and search operations.",
38
+ "tools": [
39
+ "WebFetch",
40
+ "WebSearch"
41
+ ]
42
+ },
43
+ {
44
+ "id": "interaction",
45
+ "mention": "interaction",
46
+ "description": "Tool set: user interaction and questions.",
47
+ "tools": [
48
+ "AskUserQuestion"
49
+ ]
50
+ }
51
+ ],
52
+ "tools": [
53
+ {
54
+ "id": "Read",
55
+ "mention": "Read",
56
+ "preferredName": "Read",
57
+ "legacyName": null,
58
+ "description": "Read file contents from the local filesystem. Supports text files, images, PDFs, and Jupyter notebooks.",
59
+ "risk": "low",
60
+ "sideEffects": "reads",
61
+ "capabilities": [
62
+ "fs.read"
63
+ ],
64
+ "notes": "Can read any file type. Images and PDFs are processed visually."
65
+ },
66
+ {
67
+ "id": "Write",
68
+ "mention": "Write",
69
+ "preferredName": "Write",
70
+ "legacyName": null,
71
+ "description": "Create or overwrite files on the local filesystem.",
72
+ "risk": "medium",
73
+ "sideEffects": "writes",
74
+ "capabilities": [
75
+ "fs.write"
76
+ ],
77
+ "notes": "Requires reading the file first if it exists. Prefer Edit for existing files."
78
+ },
79
+ {
80
+ "id": "Edit",
81
+ "mention": "Edit",
82
+ "preferredName": "Edit",
83
+ "legacyName": null,
84
+ "description": "Perform exact string replacements in existing files.",
85
+ "risk": "medium",
86
+ "sideEffects": "writes",
87
+ "capabilities": [
88
+ "fs.patch"
89
+ ],
90
+ "notes": "Requires reading the file first. Fails if old_string is not unique."
91
+ },
92
+ {
93
+ "id": "Bash",
94
+ "mention": "Bash",
95
+ "preferredName": "Bash",
96
+ "legacyName": null,
97
+ "description": "Execute bash commands in a persistent shell session.",
98
+ "risk": "high",
99
+ "sideEffects": "executes",
100
+ "capabilities": [
101
+ "shell.exec"
102
+ ],
103
+ "notes": "Use for git, npm, docker, etc. Avoid for file operations (use Read/Write/Edit)."
104
+ },
105
+ {
106
+ "id": "Glob",
107
+ "mention": "Glob",
108
+ "preferredName": "Glob",
109
+ "legacyName": null,
110
+ "description": "Find files by glob pattern matching.",
111
+ "risk": "low",
112
+ "sideEffects": "reads",
113
+ "capabilities": [
114
+ "search.files"
115
+ ],
116
+ "notes": "Faster than find command. Returns paths sorted by modification time."
117
+ },
118
+ {
119
+ "id": "Grep",
120
+ "mention": "Grep",
121
+ "preferredName": "Grep",
122
+ "legacyName": null,
123
+ "description": "Search file contents using regular expressions (built on ripgrep).",
124
+ "risk": "low",
125
+ "sideEffects": "reads",
126
+ "capabilities": [
127
+ "search.text"
128
+ ],
129
+ "notes": "Supports full regex syntax. Use output_mode for different result formats."
130
+ },
131
+ {
132
+ "id": "WebFetch",
133
+ "mention": "WebFetch",
134
+ "preferredName": "WebFetch",
135
+ "legacyName": null,
136
+ "description": "Fetch content from a URL and process it with a prompt.",
137
+ "risk": "medium",
138
+ "sideEffects": "network",
139
+ "capabilities": [
140
+ "web.fetch"
141
+ ],
142
+ "notes": "Converts HTML to markdown. Has 15-minute cache."
143
+ },
144
+ {
145
+ "id": "WebSearch",
146
+ "mention": "WebSearch",
147
+ "preferredName": "WebSearch",
148
+ "legacyName": null,
149
+ "description": "Search the web and return results.",
150
+ "risk": "medium",
151
+ "sideEffects": "network",
152
+ "capabilities": [
153
+ "web.search"
154
+ ],
155
+ "notes": "Must include Sources section with URLs after answering."
156
+ },
157
+ {
158
+ "id": "Task",
159
+ "mention": "Task",
160
+ "preferredName": "Task",
161
+ "legacyName": null,
162
+ "description": "Launch specialized subagents for complex multi-step tasks.",
163
+ "risk": "medium",
164
+ "sideEffects": "mixed",
165
+ "capabilities": [
166
+ "agent.spawn"
167
+ ],
168
+ "notes": "Subagent types: Bash, general-purpose, Explore, Plan, etc."
169
+ },
170
+ {
171
+ "id": "TodoWrite",
172
+ "mention": "TodoWrite",
173
+ "preferredName": "TodoWrite",
174
+ "legacyName": null,
175
+ "description": "Create and manage a structured task list for tracking progress.",
176
+ "risk": "low",
177
+ "sideEffects": "none",
178
+ "capabilities": [
179
+ "planning.todos"
180
+ ],
181
+ "notes": "Use for complex multi-step tasks. States: pending, in_progress, completed."
182
+ },
183
+ {
184
+ "id": "NotebookEdit",
185
+ "mention": "NotebookEdit",
186
+ "preferredName": "NotebookEdit",
187
+ "legacyName": null,
188
+ "description": "Replace, insert, or delete cells in Jupyter notebooks.",
189
+ "risk": "medium",
190
+ "sideEffects": "writes",
191
+ "capabilities": [
192
+ "notebook.patch"
193
+ ],
194
+ "notes": "Cell numbers are 0-indexed. Supports code and markdown cells."
195
+ },
196
+ {
197
+ "id": "AskUserQuestion",
198
+ "mention": "AskUserQuestion",
199
+ "preferredName": "AskUserQuestion",
200
+ "legacyName": null,
201
+ "description": "Ask the user questions to gather preferences, clarify instructions, or get decisions.",
202
+ "risk": "low",
203
+ "sideEffects": "none",
204
+ "capabilities": [
205
+ "user.prompt"
206
+ ],
207
+ "notes": "Supports multiple choice with 2-4 options. Users can always select 'Other'."
208
+ },
209
+ {
210
+ "id": "TaskOutput",
211
+ "mention": "TaskOutput",
212
+ "preferredName": "TaskOutput",
213
+ "legacyName": null,
214
+ "description": "Retrieve output from a running or completed background task.",
215
+ "risk": "low",
216
+ "sideEffects": "reads",
217
+ "capabilities": [
218
+ "agent.output"
219
+ ],
220
+ "notes": "Use block=true to wait for completion, block=false for status check."
221
+ },
222
+ {
223
+ "id": "KillShell",
224
+ "mention": "KillShell",
225
+ "preferredName": "KillShell",
226
+ "legacyName": null,
227
+ "description": "Kill a running background bash shell by its ID.",
228
+ "risk": "low",
229
+ "sideEffects": "executes",
230
+ "capabilities": [
231
+ "shell.kill"
232
+ ],
233
+ "notes": "Use when you need to terminate a long-running shell."
234
+ },
235
+ {
236
+ "id": "Skill",
237
+ "mention": "Skill",
238
+ "preferredName": "Skill",
239
+ "legacyName": null,
240
+ "description": "Execute a skill (slash command) within the conversation.",
241
+ "risk": "medium",
242
+ "sideEffects": "mixed",
243
+ "capabilities": [
244
+ "skill.exec"
245
+ ],
246
+ "notes": "Used for slash commands like /commit, /review-pr."
247
+ },
248
+ {
249
+ "id": "EnterPlanMode",
250
+ "mention": "EnterPlanMode",
251
+ "preferredName": "EnterPlanMode",
252
+ "legacyName": null,
253
+ "description": "Transition to plan mode for designing implementation approaches.",
254
+ "risk": "low",
255
+ "sideEffects": "none",
256
+ "capabilities": [
257
+ "planning.enter"
258
+ ],
259
+ "notes": "Use for non-trivial implementation tasks requiring user approval."
260
+ },
261
+ {
262
+ "id": "ExitPlanMode",
263
+ "mention": "ExitPlanMode",
264
+ "preferredName": "ExitPlanMode",
265
+ "legacyName": null,
266
+ "description": "Signal plan completion and request user approval.",
267
+ "risk": "low",
268
+ "sideEffects": "none",
269
+ "capabilities": [
270
+ "planning.exit"
271
+ ],
272
+ "notes": "Can request bash command permissions via allowedPrompts."
273
+ }
274
+ ],
275
+ "recommended": {
276
+ "aps": {
277
+ "planner": [
278
+ "Read",
279
+ "Glob",
280
+ "Grep",
281
+ "WebFetch",
282
+ "WebSearch",
283
+ "TodoWrite",
284
+ "AskUserQuestion",
285
+ "Task"
286
+ ],
287
+ "implementer": [
288
+ "Read",
289
+ "Write",
290
+ "Edit",
291
+ "Bash",
292
+ "Glob",
293
+ "Grep",
294
+ "TodoWrite",
295
+ "Task",
296
+ "NotebookEdit"
297
+ ]
298
+ }
299
+ }
300
+ }
@@ -1,24 +1,17 @@
1
1
  # VS Code + GitHub Copilot adapter
2
2
 
3
- This adapter documents **how APS fits into VS Code Copilot's file conventions**, and provides ready-to-copy templates.
3
+ This adapter documents **how APS fits into VS Code Copilot's file conventions**.
4
4
 
5
5
  ## What this adapter provides
6
6
 
7
7
  - `manifest.json` — where VS Code discovers skills, agents, prompts, and instructions
8
8
  - `tools-registry.json` — a curated registry of tools/tool-sets and their **canonical frontmatter names**
9
9
  - `frontmatter/` — copy/paste YAML frontmatter templates for VS Code files
10
- - `templates/` — drop-in `.github/*` templates (agents)
11
10
 
12
11
  ## Quickstart
13
12
 
14
- 1. Install this APS skill into your repo at:
15
- - `.github/skills/agnostic-prompt-standard/`
16
- 2. Copy the templates into your workspace (root):
17
- - From: `platforms/vscode-copilot/templates/.github/`
18
- - To: `.github/`
19
-
20
- After copying, you'll have:
21
- - `.github/agents/aps-prompt-protocol.agent.md`
13
+ Install this APS skill into your repo at:
14
+ - `.github/skills/agnostic-prompt-standard/`
22
15
 
23
16
  ## VS Code file locations (summary)
24
17
 
@@ -30,7 +23,6 @@ VS Code Copilot discovers these files:
30
23
  - **Custom instructions**:
31
24
  - `.github/copilot-instructions.md`
32
25
  - `.github/instructions/*.instructions.md`
33
- - `AGENTS.md` (optional)
34
26
 
35
27
  See `manifest.json` for the full matrix (including personal and Claude locations).
36
28
 
@@ -5,7 +5,7 @@ description: "What this agent does and when to use it."
5
5
  # Optional: a short hint shown in the UI
6
6
  argument-hint: "Describe the task…"
7
7
  # Optional: list of tools/tool-sets available to this agent
8
- tools: ["search", "read", "edit", "execute", "todo", "web/fetch"]
8
+ tools: ['execute/runInTerminal', 'read/readFile', 'edit/createDirectory', 'edit/createFile', 'edit/editFiles', 'web/fetch', 'todo']
9
9
  # Optional: default model for this agent (if your org enables model selection)
10
10
  model: "GPT-4.1"
11
11
  # Optional: allow VS Code to run subagents (isolated contexts)
@@ -9,4 +9,3 @@ description: "Frontend coding conventions"
9
9
  Notes:
10
10
  - VS Code supports a single `.github/copilot-instructions.md` (workspace-wide).
11
11
  - VS Code also supports targeted `*.instructions.md` files (default folder: `.github/instructions`).
12
- - VS Code can read `AGENTS.md` files (workspace root; optional nested via setting).
@@ -27,7 +27,6 @@
27
27
  "instructions": [
28
28
  ".github/copilot-instructions.md",
29
29
  ".github/instructions/*.instructions.md",
30
- "AGENTS.md (root, optional nested via setting)",
31
30
  "CLAUDE.md / GEMINI.md (GitHub ecosystem alternates)"
32
31
  ]
33
32
  },
@@ -6,10 +6,10 @@ This document defines the **agentic control DSL** used inside `<process>` bodies
6
6
 
7
7
  ```yaml
8
8
  keywords:
9
- control: [GIVEN, WHEN, IF, ELSE IF, ELSE, THEN]
9
+ control: [GIVEN, WHEN, THEN, IF, ELSE IF, ELSE, IN]
10
10
  actions: [RUN, USE, CAPTURE, SET, UNSET, RETURN, ASSERT]
11
11
  story: [TELL, SNAP, MILESTONE]
12
- blocks: [WITH, PAR, JOIN]
12
+ blocks: [WITH, PAR, JOIN, TRY, FOREACH, RECOVER]
13
13
  modifiers: [atomic, timeout_ms, retry]
14
14
  ```
15
15
 
@@ -37,9 +37,13 @@ identifiers:
37
37
  reserved:
38
38
  - GIVEN
39
39
  - WHEN
40
+ - THEN
40
41
  - IF
41
42
  - ELSE
42
- - THEN
43
+ - FOREACH
44
+ - IN
45
+ - TRY
46
+ - RECOVER
43
47
  - RUN
44
48
  - USE
45
49
  - SET
@@ -61,6 +65,17 @@ identifiers:
61
65
 
62
66
  Reserved words cannot be used as ids/keys/symbols (`AG-002`).
63
67
 
68
+ ## GIVEN / WHEN / THEN (BDD pattern)
69
+
70
+ These keywords support Behavior-Driven Design (BDD) style specifications:
71
+
72
+ - `GIVEN`: establishes preconditions or context that must hold before execution.
73
+ - `WHEN`: describes a trigger, event, or condition that initiates action.
74
+ - `THEN`: specifies expected outcomes or postconditions after execution.
75
+
76
+ Each keyword opens a block with `:` (Python-style). The block body contains statements that execute
77
+ when the condition is satisfied. Engines SHOULD evaluate conditions in lexical order.
78
+
64
79
  ## Strings, booleans, and numbers
65
80
 
66
81
  ```yaml
@@ -108,35 +123,61 @@ except as explicitly defined by the engine:
108
123
 
109
124
  Engines SHOULD log deterministic span indices and MUST NOT perform speculative execution.
110
125
 
126
+ ## FOREACH iteration
127
+
128
+ `FOREACH` provides deterministic iteration over symbol arrays:
129
+
130
+ - `FOREACH item IN ITEMS:` iterates over each element in the `ITEMS` array in **index order**.
131
+ - The loop variable (`item`) is bound to the current element for each iteration.
132
+ - Loop body statements execute sequentially; the engine MUST NOT parallelize unless explicitly
133
+ wrapped in `PAR`.
134
+ - If `ITEMS` is empty, the loop body is skipped entirely.
135
+ - The loop variable is scoped to the block; it is undefined after the closing boundary.
136
+
137
+ Engines SHOULD expose the current index via a deterministic mechanism (e.g., `_INDEX` symbol).
138
+
139
+ ## TRY / RECOVER error handling
140
+
141
+ `TRY` and `RECOVER` define structured error handling:
142
+
143
+ - `TRY:` begins a guarded block; statements execute until completion or first hard error.
144
+ - `RECOVER (err):` binds the error to the named variable (`err`) and executes recovery statements.
145
+ - If no error occurs, the `RECOVER` block is skipped.
146
+ - Recovery statements MAY access the error variable to inspect type, message, or context.
147
+ - Nested `TRY` blocks are permitted; inner handlers take precedence.
148
+
149
+ Error binding follows engine-defined schema; at minimum, engines MUST expose `err.type` and
150
+ `err.message`.
151
+
111
152
  ## Invocation syntax (normative)
112
153
 
113
154
  The following describes statement syntax at a human-readable level. The authoritative grammar is
114
155
  in **05 Grammar**.
115
156
 
116
157
  ```text
117
- RUN `process_id` [where: k1=V1, ...].
158
+ RUN `process_id` [where: k1=V1, ...]
118
159
 
119
- USE `tool_name` [where: k1=V1, ...] [(atomic[, timeout_ms=NUM][, retry=NUM])].
160
+ USE `tool_name` [where: k1=V1, ...] [(atomic[, timeout_ms=NUM][, retry=NUM])]
120
161
 
121
- CAPTURE S1[, S2 ...] from `tool_name` [map: "path1"→S1, "path2?"→S2 ...].
162
+ CAPTURE S1[, S2 ...] from `tool_name` [map: "path1"→S1, "path2?"→S2 ...]
122
163
  - Optional field via suffix '?'; no error if missing; symbol unchanged.
123
164
 
124
165
  SET SYMBOL := VALUE [(from SOURCE)]
125
166
  - SOURCE ∈ { `tool`, INP, UpperSym, "Agent Inference" }
126
167
 
127
- UNSET SYMBOL.
168
+ UNSET SYMBOL
128
169
 
129
- RETURN: SYMBOL[, SYMBOL...].
130
- RETURN: key=VALUE[, key=VALUE ...].
170
+ RETURN: SYMBOL[, SYMBOL...]
171
+ RETURN: key=VALUE[, key=VALUE ...]
131
172
  - VALUE may be an artifact reference: {"$artifact":"SYMBOL","hash":"sha256:..."}.
132
173
 
133
- ASSERT <condition>.
134
- ASSERT ALL: [ <condition>, <condition>, ... ].
174
+ ASSERT <condition>
175
+ ASSERT ALL: [ <condition>, <condition>, ... ]
135
176
 
136
- TELL "message" [why:SYMBOL] [level={brief|full}] [outcome:"text"].
137
- MILESTONE "title".
177
+ TELL "message" [why:SYMBOL] [level={brief|full}] [outcome:"text"]
178
+ MILESTONE "title"
138
179
 
139
- SNAP [SYM1, SYM2 ...] [delta=true|false] [redact=[SYM_A, SYM_B ...]].
180
+ SNAP [SYM1, SYM2 ...] [delta=true|false] [redact=[SYM_A, SYM_B ...]]
140
181
  ```
141
182
 
142
183
  ## Arguments and values
@@ -132,6 +132,3 @@ Style guidelines:
132
132
  - JSON block constant example: [../assets/constants/constants-json-block-v1.0.0.example.md](../assets/constants/constants-json-block-v1.0.0.example.md)
133
133
  - TEXT block constant example: [../assets/constants/constants-text-block-v1.0.0.example.md](../assets/constants/constants-text-block-v1.0.0.example.md)
134
134
 
135
- ## Example agents
136
-
137
- - VS Code orchestration agent: [../assets/agents/vscode-agent-v1.0.0.agent.md](../assets/agents/vscode-agent-v1.0.0.agent.md)
@@ -9,6 +9,7 @@ This document defines the normative EBNF for the agentic control DSL.
9
9
 
10
10
  ```ebnf
11
11
  Letter = "A"…"Z" | "a"…"z" ;
12
+ LowerLetter = "a"…"z" ;
12
13
  Digit = "0"…"9" ;
13
14
  Space = " " ;
14
15
  Newline = "\n" ;
@@ -26,13 +27,13 @@ BlockOpen = UpperSym, ":", Space, BlockType, "<<", EOL ;
26
27
  BlockClose = ">>" ;
27
28
  BlockValue = BlockType, "<<", EOL, { <any line except BlockClose>, EOL }, BlockClose ;
28
29
 
29
- JsonKey = Letter, { Letter | Digit | "_" | "-" } ;
30
+ JsonKey = LowerLetter, { LowerLetter | Digit | "_" | "-" } ;
30
31
  JsonValue = String | Number | Bool | "null" | JsonObj | JsonArr | UpperSym ;
31
32
  JsonPair = "\"", JsonKey, "\"", ":", Space?, JsonValue ;
32
33
  JsonObj = "{", Space?, [ JsonPair, { ",", Space?, JsonPair } ], Space?, "}" ;
33
34
  JsonArr = "[", Space?, [ JsonValue, { ",", Space?, JsonValue } ], Space?, "]" ;
34
35
 
35
- IdLower = Letter, { Letter | Digit | "_" | "-" } ;
36
+ IdLower = LowerLetter, { LowerLetter | Digit | "_" | "-" } ;
36
37
  Key = IdLower ;
37
38
  Value = String | Number | Bool | JsonObj | JsonArr | UpperSym | Placeholder | EnumLit ;
38
39
 
@@ -65,6 +66,7 @@ IfStmt = "IF", Space, <expr>, ":" ;
65
66
  ElseIfStmt = "ELSE IF", Space, <expr>, ":" ;
66
67
  ElseStmt = "ELSE", ":" ;
67
68
  WhenStmt = "WHEN", Space, <condition-text-no-colon>, ":" ;
69
+ ThenStmt = "THEN", Space, <condition-text-no-colon>, ":" ;
68
70
  GivenStmt = "GIVEN", Space, <condition-text-no-colon>, ":" ;
69
71
 
70
72
  EOL = Newline ;
@@ -88,7 +90,7 @@ RegexConst = "matches", Space, String ;
88
90
 
89
91
  Statement = RunStmt | UseStmt | CaptureStmt | SetStmt | UnsetStmt | ReturnStmt | AssertStmt
90
92
  | TellStmt | MileStmt | WithBlock | ParBlock | JoinBlock | ForEachStmt | TryBlock
91
- | IfStmt | ElseIfStmt | ElseStmt | SnapStmt ;
93
+ | IfStmt | ElseIfStmt | ElseStmt | GivenStmt | WhenStmt | ThenStmt | SnapStmt ;
92
94
 
93
95
  Program = { Statement, EOL } ;
94
96
  ```