@ethlete/agent-rules 0.1.0-next.30 → 0.1.0-next.31

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @ethlete/agent-rules
2
2
 
3
+ ## 0.1.0-next.31
4
+
5
+ ### Minor Changes
6
+
7
+ - The design-exploration skill now describes the `ethlete-design` MCP server: its tools by job, when to use them instead of the `et design` CLI, and the `sketch_report` and `sketch_ready` rules.
8
+
9
+ ### Patch Changes
10
+
11
+ - The Codex subagent skill lists gpt-6-luna, gpt-6.1-terra (the new default), gpt-6.1-sol and gpt-6-astra. The context hook gives gpt-6.1 a 500k pricing boundary.
12
+ - `agent-sessions restart` types the prompt after the clear even when a message woke the fresh session. Only typed text in its prompt holds the prompt back.
13
+ - The design exploration skill lists `et design sketch target add`.
14
+ - `agent-sessions restart` no longer reads the Claude Code IDE context chip (`[⧉ In file]`) on the prompt line as a draft, so it types the resume prompt after the clear.
15
+ - `control(markup, name)` from `@ethlete/components/design/control` renames a control that is already marked, so a design reuses a control part under its own control name.
16
+ - CONTROL-STATE counts an interaction word only as the last word of a state name, drops modifier words from the element name, and asks a component without controls to declare them (Q164).
17
+ - `et design check`, `sketch check`, `sketch ready` and `design complete` fail interaction states on a part that declares no control (`CONTROL-STATE`). `design complete` hints a state that changes under 10 % of its frame.
18
+ - Design checks report a drawn button, link, input or control role without `data-et-control` as `CONTROL-PART-INPUT`: a hint on merged parts, an error when a sketch adds one.
19
+
3
20
  ## 0.1.0-next.30
4
21
 
5
22
  ### Minor Changes
@@ -137,6 +137,7 @@ PREMIUM_BOUNDARY = 200_000
137
137
  # Codex models whose long-context pricing starts above 272k input tokens. Models
138
138
  # without that pricing rule use their reported context window instead.
139
139
  CODEX_PREMIUM_BOUNDARIES = (
140
+ ("gpt-6.1", 500_000),
140
141
  ("gpt-5.6", 272_000),
141
142
  ("gpt-5.5", 272_000),
142
143
  ("gpt-5.4-mini", None),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "e2e-progress",
3
- "version": "0.1.0-next.30",
3
+ "version": "0.1.0-next.31",
4
4
  "description": "Live Playwright e2e progress from a run log, as a status line entry and a pane",
5
5
  "types": "./types/index.d.ts"
6
6
  }
@@ -49,7 +49,7 @@ Nothing runs until you start it, so a person who does not use this does nothing.
49
49
 
50
50
  `up codex=codex gemini=gemini` starts a detached `tmux` session per agent, named like its sender name, and runs the command in it. It also starts a session named `mailbox-dispatch` that runs `dispatch`. A running session is left alone. Attach to a session with `tmux attach -t <name>`. Detach with `Ctrl-b`, then `d`. The session and its agent keep running. Use `down` to stop the dispatcher and the named sessions.
51
51
 
52
- `restart <name> <prompt...>` types the clear command (`/clear`, or `/new` for a `codex` name) and then the prompt into the session `<name>@<repo>`, no sooner than `--after` seconds (default 5). It waits until that session ended its turn and its prompt holds no typed text, and gives up after 30 minutes. The command returns at once, so end your turn after it. An agent uses it to clear itself and resume from its saved work state. It refuses any session other than the caller's own (from `$TMUX_PANE`), and the error names the caller's own sender name. Background commands of the old session still report into the new one.
52
+ `restart <name> <prompt...>` types the clear command (`/clear`, or `/new` for a `codex` name) and then the prompt into the session `<name>@<repo>`, no sooner than `--after` seconds (default 5). It waits until that session ended its turn and its prompt holds no typed text, and gives up after 30 minutes. After the clear, a new turn does not hold the prompt back: a message can wake the fresh session, and Claude Code queues input typed during a turn. Only typed text in its prompt holds the prompt back. After 60 seconds of typed text, it saves the prompt to `.ethlete/restart.prompt.<session>.txt` and logs why in `.ethlete/restart.log`. The command returns at once, so end your turn after it. An agent uses it to clear itself and resume from its saved work state. It refuses any session other than the caller's own (from `$TMUX_PANE`), and the error names the caller's own sender name. Background commands of the old session still report into the new one.
53
53
 
54
54
  `shell-install` writes one shell function per agent into `~/.zshrc` or `~/.bashrc`, between two marker lines. A second run replaces the block, and `--remove` deletes it. Use `--file` for another startup file and `--dry-run` to preview. After a new terminal opens, typing `claude` or `codex` in a repo with this skill runs the agent through `run`. Elsewhere the function starts the plain command. Run `shell-install` only when the user asks: it changes a file the user owns. `shell-init` prints the same functions without writing a file.
55
55
 
@@ -116,14 +116,15 @@ const restartLog = (message) => {
116
116
 
117
117
  /**
118
118
  * Waits until the agent ended its turn and nobody types in its prompt, twice in a row; `false` after the time limit.
119
+ * With `allowBusy`, a running turn does not block, since Claude Code queues input typed during a turn.
119
120
  * Each change of the reason it waits is logged to `restart.log` next to the mailbox.
120
121
  */
121
- const waitForQuiet = (session, limitMs = RESTART_WAIT_MS) => {
122
+ const waitForQuiet = (session, limitMs = RESTART_WAIT_MS, allowBusy = false) => {
122
123
  const end = Date.now() + limitMs;
123
124
  let quiet = 0;
124
125
  let reason = '';
125
126
  while (Date.now() < end) {
126
- const busy = busyLine(session);
127
+ const busy = allowBusy ? null : busyLine(session);
127
128
  const draft = busy === null ? draftLine(session) : null;
128
129
  const next = busy !== null ? `busy: ${busy.trim()}` : draft !== null ? `draft: ${draft}` : '';
129
130
  if (next !== reason) restartLog(`${session} ${next ? `waits, ${next}` : 'is quiet'}`);
@@ -135,6 +136,8 @@ const waitForQuiet = (session, limitMs = RESTART_WAIT_MS) => {
135
136
  return false;
136
137
  };
137
138
 
139
+ /** The IDE context chip (`[⧉ In file.ts]`, `[⧉ 3 lines selected]`) shares the prompt line and survives `/clear`. */
140
+ const IDE_CHIP = /\[⧉ [^\]]*\]/g;
138
141
  const draftLine = (session) => {
139
142
  const pane = tmux('capture-pane', '-p', '-e', '-t', `=${session}:`).stdout ?? '';
140
143
  const prompt = pane
@@ -155,7 +158,10 @@ const draftLine = (session) => {
155
158
  return '';
156
159
  })
157
160
  .join('');
158
- const text = visible.replace(/^\s*[❯›]/, '').trim();
161
+ const text = visible
162
+ .replace(/^\s*[❯›]/, '')
163
+ .replace(IDE_CHIP, '')
164
+ .trim();
159
165
  return text.length > 0 ? text : null;
160
166
  };
161
167
  const hasDraft = (session) => draftLine(session) !== null;
@@ -353,7 +359,7 @@ if (command === 'send' && sender) {
353
359
  sleep(ENTER_DELAY_MS);
354
360
  type('Enter');
355
361
  sleep(RESTART_CLEAR_MS);
356
- if (waitForQuiet(session, RESTART_CLEAR_WAIT_MS)) {
362
+ if (waitForQuiet(session, RESTART_CLEAR_WAIT_MS, true)) {
357
363
  type('-l', words.join(' '));
358
364
  sleep(ENTER_DELAY_MS);
359
365
  type('Enter');
@@ -361,13 +367,13 @@ if (command === 'send' && sender) {
361
367
  const saved = join(dirname(mailbox), `restart.prompt.${session}.txt`);
362
368
  writeFileSync(saved, `${words.join(' ')}\n`);
363
369
  restartLog(
364
- `${session} not quiet ${RESTART_CLEAR_WAIT_MS / 1000}s after the clear, prompt not typed, saved to ${saved}`,
370
+ `${session} prompt not empty ${RESTART_CLEAR_WAIT_MS / 1000}s after the clear, prompt not typed, saved to ${saved}`,
365
371
  );
366
372
  tmux(
367
373
  'display-message',
368
374
  '-t',
369
375
  `=${session}:`,
370
- `restart: pane not quiet after the clear, prompt not typed. Saved to ${saved}`,
376
+ `restart: prompt not empty after the clear, prompt not typed. Saved to ${saved}`,
371
377
  );
372
378
  process.exitCode = 1;
373
379
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: codex-subagent
3
- description: Delegate a task to a Codex agent (gpt-5.6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-6-astra) instead of a Claude subagent - a second opinion from another model family, a review, or a cheap parallel lookup. Read before spawning a subagent when another model would answer better, and whenever the user says codex, GPT, or "second opinion".
3
+ description: Delegate a task to a Codex agent (gpt-6-luna, gpt-6.1-terra, gpt-6.1-sol, gpt-6-astra) instead of a Claude subagent - a second opinion from another model family, a review, or a cheap parallel lookup. Read before spawning a subagent when another model would answer better, and whenever the user says codex, GPT, or "second opinion".
4
4
  kind: skill
5
5
  scope: both
6
6
  ---
@@ -24,7 +24,7 @@ only the final message, so one run costs you a few hundred tokens instead of the
24
24
  Below, `<skill-dir>` is the directory this file sits in.
25
25
 
26
26
  ```bash
27
- node <skill-dir>/codex-agent.mjs -m gpt-5.6-terra -e medium "<prompt>"
27
+ node <skill-dir>/codex-agent.mjs -m gpt-6.1-terra -e medium "<prompt>"
28
28
  ```
29
29
 
30
30
  Options: `-m` model, `-e` effort (`low`, `medium`, `high`, `xhigh`), `-C` working root,
@@ -36,12 +36,12 @@ id and the log path. Read the log only when a run fails.
36
36
 
37
37
  ## Pick the model
38
38
 
39
- | Model | The work it fits |
40
- | --------------- | ---------------------------------------------------------------------------------------------------------------- |
41
- | `gpt-5.6-luna` | Fast and cheap: a lookup, a grep, a question about one file. |
42
- | `gpt-5.6-terra` | Balanced agentic coding. The default for real work, and the wrapper's default. |
43
- | `gpt-5.6-sol` | A reliable workhorse for a straight task. It costs about what `fable` costs, so ask the user before you pick it. |
44
- | `gpt-6-astra` | Complex, demanding work. The most expensive of the four, so ask the user before you pick it. |
39
+ | Model | The work it fits |
40
+ | --------------- | ------------------------------------------------------------------------------------------------------------ |
41
+ | `gpt-6-luna` | Fast and cheap: a lookup, a grep, a question about one file. |
42
+ | `gpt-6.1-terra` | Balanced agentic coding. The default for real work, and the wrapper's default. |
43
+ | `gpt-6.1-sol` | A reliable workhorse for a straight task. Its usage limit runs out fast, so ask the user before you pick it. |
44
+ | `gpt-6-astra` | Complex, demanding work. The most expensive of the four, so ask the user before you pick it. |
45
45
 
46
46
  Effort follows both the flag and the prompt. Pass `-e low` for a lookup and `-e high` for a
47
47
  hard bug. Say "keep it brief" in the prompt when you want a short answer.
@@ -14,7 +14,7 @@ import { tmpdir } from 'node:os';
14
14
  import { join } from 'node:path';
15
15
  import { createInterface } from 'node:readline';
16
16
 
17
- const DEFAULT_MODEL = 'gpt-5.6-terra';
17
+ const DEFAULT_MODEL = 'gpt-6.1-terra';
18
18
  const DEFAULT_TIMEOUT_SECONDS = 900;
19
19
 
20
20
  const USAGE = `Usage: node codex-agent.mjs [options] "<prompt>"
@@ -122,6 +122,7 @@ answers. Variants are drawn at the same geometry, and one variant wins only agai
122
122
  ```bash
123
123
  et design sketch new <layer>/<name> --variants a,b --by <model id> [--like <layer>/<name>] [--variant-note a=<text>]… [--product <p>] # each variant starts as a copy of the part
124
124
  et design sketch new <layer>/<name> <layer>/<name>… --variants a --by <model id> # one change across several parts: one sketch, one ready, one merge
125
+ et design sketch target add <layer>/<name> <layer>/<name> --by <model id> # add a part to an open sketch's targets, seeded into each variant
125
126
  et design sketch report <layer>/<name> --variant b --by <model id> "phone" # tell Studio what you build now
126
127
  et design sketch report <layer>/<name> --variant b --by <model id> --done # you stopped drawing b; Studio shows its frames
127
128
  et design sketch check [<layer>/<name>] [--fix] # each variant laid over the product
@@ -255,7 +256,7 @@ in progress) and `sketch release` it once the change is in.
255
256
 
256
257
  Every command that writes takes `--by <model id>` and fails without it: `sketch new`, `variant`, `report`,
257
258
  `ready`, `rebase`, `merge`, `discard`, `inbox done` and `comment reply`. The id is the exact model
258
- id as free text, for example `claude-opus-5-5`, `gpt-5.6-sol` or `gemini-3-argon`. Studio shows it
259
+ id as free text, for example `claude-opus-5-5`, `gpt-6.1-sol` or `gemini-3-argon`. Studio shows it
259
260
  on the variant, the reply and the part, so the user sees which model made what.
260
261
 
261
262
  **Never guess your id.** Use the one your prompt names. If your prompt names none and you do not
@@ -308,6 +309,27 @@ Pass `--question` only when you cannot go on without the user's answer. Studio l
308
309
  threads under "Questions for you". A report of what you did is no question: reply without the
309
310
  flag, and the thread shows as "answered" until the user withdraws it or replies.
310
311
 
312
+ ### The `ethlete-design` MCP server
313
+
314
+ `ethlete-agents sync` registers `et design mcp` as `ethlete-design` in `.mcp.json` and
315
+ `.codex/config.toml` when `@ethlete/cli` is installed. When the tools are in your tool list, use them
316
+ instead of the CLI: each runs the `et design` command of the same name in the session's working
317
+ directory, takes its flags as arguments, and every write takes `by` (your model id). `product` may be
318
+ left out when the workspace has one. `shot` and `motion_shots` return the sheet image itself, so you
319
+ look at it without opening a PNG path.
320
+
321
+ - **Read:** `parts` (every part, as JSON), `call` (a call's `call.ts`, `fixture.ts`, the variant file to draw into and the frame URL; never change the fixture).
322
+ - **Check:** `check` (a product; with `call` and `variant` it lints, type-checks and renders that variant - run it after every edit of a variant), `sketch_check` (each variant laid over the product), `sketch_preview` (what a merge of a variant changes).
323
+ - **Shots:** `shot` (a part's states, `variants` for the open sketch), `motion_shots` (a motion at its keyframes, full and reduced, plus its tracks as JSON).
324
+ - **Sketch lifecycle:** `sketch_new`, `sketch_variant`, `sketch_round`, `sketch_report`, `sketch_ready`, `sketch_rebase`, `sketch_list`. `sketch_merge` and `sketch_discard` exist, but Studio runs them on the user's click; do not call them.
325
+ - **Comments:** `inbox` (what waits for you, then for the user), `comment_reply` (`question` waits for the user's answer), `inbox_done` (needs the new part `note`).
326
+
327
+ Part files are written with your own file tools; the server has no write tool for them. Call
328
+ `sketch_report` before every variant edit: it tells Studio what you build, and it moves a `ready`
329
+ sketch back to in progress, so call `sketch_ready` again when you are done. `sketch_ready` runs the
330
+ render check and refuses on any render issue; mark an element that renders wrong on purpose with
331
+ `data-et-skip="<rule>"`. A tool that fails returns the CLI's output; fix it and call again.
332
+
311
333
  ### Draw a flow
312
334
 
313
335
  A flow joins states of pages into a path the user reads left to right in Studio's Flows view.
@@ -527,6 +549,14 @@ When each of several cases is drawn at rest and in the same interactions, build
527
549
  from `@design-system` and spread the result into `definePart`; never write those states and the grid by hand.
528
550
  `sketch check` prints `grid <rows> x <columns> ok`, or the states the grid leaves out; a state in two cells
529
551
  fails it as `GRID`, in `design check` too.
552
+ Interaction states (a state name whose last word is hover, focus, pressed, active or selected, such as `row-hover`) live only on a control
553
+ part, one whose `controls` declare a control of a checked kind. On a shell, element or page they fail as
554
+ `CONTROL-STATE`: declare the component's controls (Q164), or extract the button or row as its own control part and draw its states there.
555
+ A drawn `button`, `a[href]`, `input`, `select`, `textarea` or control role without `data-et-control` on it or around
556
+ it is `CONTROL-PART-INPUT`: no control check sees it. Wrap it in `control(markup, name)` from
557
+ `@ethlete/components/design/control` and declare the name in `controls`; a sketch fails on one it adds,
558
+ `design check` only warns of one a merged part already draws. On a reused control part's render,
559
+ `control(drawn.body, name)` renames its control: never string-replace `data-et-control`.
530
560
  `definePart({ interactions })` forces an interaction on the part's root only. To draw one element of the part
531
561
  hovered, focused, pressed or disabled, such as one row of a list, wrap that element's markup in
532
562
  `forceInteraction(markup, 'hover')` from `@design-system` inside `render`, driven by a prop; never write
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethlete/agent-rules",
3
- "version": "0.1.0-next.30",
3
+ "version": "0.1.0-next.31",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/ethlete-io/ethdk",