@ethlete/agent-rules 0.1.0-next.28 → 0.1.0-next.29
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 +8 -0
- package/content/mods/plugins/e2e-progress/.claude-plugin/plugin.json +1 -1
- package/content/skills/agent-sessions/SKILL.md +1 -1
- package/content/skills/agent-sessions/agent-sessions.mjs +32 -8
- package/content/skills/design-exploration/SKILL.md +8 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @ethlete/agent-rules
|
|
2
2
|
|
|
3
|
+
## 0.1.0-next.29
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `agent-sessions restart` now waits until the session ended its turn and its prompt is empty, so it no longer types into a running turn or into your draft.
|
|
8
|
+
- A multi-target sketch merge gives each target only the `shared/` files it imports, directly or transitively; an unimported file returns to the target that held it. `sketch new` refuses targets whose `shared/` files clash.
|
|
9
|
+
- `et design sketch new` takes several parts: one sketch, one variant folder holding them all, one `ready`, one merge; `ready` and `preview` name every part a `shared/` edit reaches; Studio shows every target part.
|
|
10
|
+
|
|
3
11
|
## 0.1.0-next.28
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
|
@@ -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
|
|
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.
|
|
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
|
|
|
@@ -35,6 +35,8 @@ const WAKE_GRACE_MS = 1500;
|
|
|
35
35
|
const DRAFT_RETRY_MS = 2000;
|
|
36
36
|
const RESTART_AFTER_SECONDS = 5;
|
|
37
37
|
const RESTART_CLEAR_MS = 2500;
|
|
38
|
+
const RESTART_POLL_MS = 2000;
|
|
39
|
+
const RESTART_WAIT_MS = 30 * 60 * 1000;
|
|
38
40
|
const repoSlug = basename(root).replace(/[.:]/g, '-');
|
|
39
41
|
const sessionFor = (agent) => `${agent}@${repoSlug}`;
|
|
40
42
|
const DISPATCH_SESSION = process.env.AGENT_MAILBOX_DISPATCH_SESSION ?? `mailbox-dispatch@${repoSlug}`;
|
|
@@ -102,6 +104,20 @@ const runsInSession = (agentArgs) =>
|
|
|
102
104
|
* wake-up line from answering a permission dialog.
|
|
103
105
|
*/
|
|
104
106
|
const SGR = `${String.fromCharCode(27)}\\[([0-9;]*)m`;
|
|
107
|
+
const isBusy = (session) => /esc to inte/i.test(tmux('capture-pane', '-p', '-t', `=${session}:`).stdout ?? '');
|
|
108
|
+
|
|
109
|
+
/** Waits until the agent ended its turn and nobody types in its prompt, twice in a row; `false` after the time limit. */
|
|
110
|
+
const waitForQuiet = (session) => {
|
|
111
|
+
const end = Date.now() + RESTART_WAIT_MS;
|
|
112
|
+
let quiet = 0;
|
|
113
|
+
while (Date.now() < end) {
|
|
114
|
+
quiet = isBusy(session) || hasDraft(session) ? 0 : quiet + 1;
|
|
115
|
+
if (quiet === 2) return true;
|
|
116
|
+
sleep(RESTART_POLL_MS);
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
};
|
|
120
|
+
|
|
105
121
|
const hasDraft = (session) => {
|
|
106
122
|
const pane = tmux('capture-pane', '-p', '-e', '-t', `=${session}:`).stdout ?? '';
|
|
107
123
|
const prompt = pane
|
|
@@ -311,20 +327,28 @@ if (command === 'send' && sender) {
|
|
|
311
327
|
} else if (now) {
|
|
312
328
|
const type = (...keys) => tmux('send-keys', '-t', `=${session}:`, ...keys);
|
|
313
329
|
sleep(Number(afterSeconds ?? RESTART_AFTER_SECONDS) * 1000);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
330
|
+
if (!waitForQuiet(session)) {
|
|
331
|
+
process.exitCode = 1;
|
|
332
|
+
} else {
|
|
333
|
+
type('-l', sender.startsWith('codex') ? '/new' : '/clear');
|
|
334
|
+
sleep(ENTER_DELAY_MS);
|
|
335
|
+
type('Enter');
|
|
336
|
+
sleep(RESTART_CLEAR_MS);
|
|
337
|
+
if (waitForQuiet(session)) {
|
|
338
|
+
type('-l', words.join(' '));
|
|
339
|
+
sleep(ENTER_DELAY_MS);
|
|
340
|
+
type('Enter');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
321
343
|
} else {
|
|
322
344
|
const delay = Number(afterSeconds ?? RESTART_AFTER_SECONDS);
|
|
323
345
|
spawn(process.execPath, [script, 'restart', sender, '--now', '--after', String(delay), ...words], {
|
|
324
346
|
detached: true,
|
|
325
347
|
stdio: 'ignore',
|
|
326
348
|
}).unref();
|
|
327
|
-
process.stdout.write(
|
|
349
|
+
process.stdout.write(
|
|
350
|
+
`Restarting ${session} once its turn ended and its prompt is empty, ${delay} seconds at the earliest. End your turn now.\n`,
|
|
351
|
+
);
|
|
328
352
|
}
|
|
329
353
|
} else if (command === 'down') {
|
|
330
354
|
[DISPATCH_SESSION, ...[sender, ...words].filter(Boolean).map(sessionFor)].forEach((name) => {
|
|
@@ -115,6 +115,7 @@ answers. Variants are drawn at the same geometry, and one variant wins only agai
|
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
117
|
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
|
|
118
|
+
et design sketch new <layer>/<name> <layer>/<name>… --variants a --by <model id> # one change across several parts: one sketch, one ready, one merge
|
|
118
119
|
et design sketch report <layer>/<name> --variant b --by <model id> "phone" # tell Studio what you build now
|
|
119
120
|
et design sketch report <layer>/<name> --variant b --by <model id> --done # you stopped drawing b; Studio shows its frames
|
|
120
121
|
et design sketch check [<layer>/<name>] [--fix] # each variant laid over the product
|
|
@@ -123,6 +124,10 @@ et design sketch reject <layer>/<name> --variant b [--reason <text>] --by <model
|
|
|
123
124
|
et design sketch reopen <layer>/<name> --variant b [--reason <text>] --by <model id> # only when the user takes b back; the rejection stays in the history
|
|
124
125
|
```
|
|
125
126
|
|
|
127
|
+
One change across several parts (an alias removal, a token rename) is one sketch with several targets: each
|
|
128
|
+
variant holds every target, and `ready` compares them all. `ready` and `preview` also name every part a `shared/`
|
|
129
|
+
edit reaches.
|
|
130
|
+
|
|
126
131
|
Variants are alternatives. A variant never stands for a state of another variant: a hidden or
|
|
127
132
|
collapsed mode of variant d is a further state of d, not a variant e. Before you draw a new control,
|
|
128
133
|
look for a control that already does the job (an active rail button that toggles its panel), and
|
|
@@ -162,7 +167,9 @@ variant imports it as `@shared/<path>`, and a shared file may import `@system/..
|
|
|
162
167
|
changed holds its own copy at `<variant>/shared/<path>`; `@shared/<path>` finds that copy first, and a merge
|
|
163
168
|
of that variant takes it. On a merge, `shared/` moves into
|
|
164
169
|
the target's folder as `shared/` with its imports made relative; when no part imports it, it is
|
|
165
|
-
dropped.
|
|
170
|
+
dropped. With several targets, each takes only the files it imports, directly or through another
|
|
171
|
+
shared file; a file nobody imports goes back to the target that held it, and the merge refuses one no
|
|
172
|
+
target held. `sketch new` refuses targets whose `shared/` folders hold one path with different contents. Only the target may import it: a merge refuses while another part of the picked variant
|
|
166
173
|
does, so move that code into the part. `sketch new` prints each file it puts in `shared/`, for example
|
|
167
174
|
the `shared/` folder of a merged page: edit it there, as part of the sketch, not in a variant copy.
|
|
168
175
|
|