@ethlete/agent-rules 0.1.0-next.30 → 0.1.0-next.32
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 +43 -0
- package/README.md +7 -1
- package/content/hooks/background-design-commands.py +140 -0
- package/content/hooks/builder-chat-words.py +74 -0
- package/content/hooks/context-warning.py +48 -19
- package/content/hooks/git-safety.py +181 -0
- package/content/mods/plugins/e2e-progress/.claude-plugin/plugin.json +1 -1
- package/content/rules/nx-layout.md +5 -2
- package/content/rules/subagent-models.md +2 -2
- package/content/skills/agent-sessions/SKILL.md +6 -4
- package/content/skills/agent-sessions/agent-sessions.mjs +32 -10
- package/content/skills/codex-subagent/SKILL.md +8 -8
- package/content/skills/codex-subagent/codex-agent.mjs +1 -1
- package/content/skills/design-exploration/SKILL.md +47 -517
- package/content/skills/design-exploration/reference-check-and-notes.md +92 -0
- package/content/skills/design-exploration/reference-flows-and-wireframes.md +78 -0
- package/content/skills/design-exploration/reference-inbox-and-mcp.md +54 -0
- package/content/skills/design-exploration/reference-order-and-design-system.md +124 -0
- package/content/skills/design-exploration/reference-products-and-sketches.md +199 -0
- package/content/skills/design-exploration/reference-shots.md +25 -0
- package/content/skills/git-commit/SKILL.md +10 -0
- package/content/skills/grilling/SKILL.md +1 -0
- package/content/skills/handoff/SKILL.md +20 -4
- package/package.json +1 -1
- package/src/index.js +2 -1
- package/src/index.js.map +1 -1
- package/src/lib/git-flow-repair.d.ts +1 -1
- package/src/lib/git-flow-start.d.ts +1 -1
- package/src/lib/migrate.js +2 -1
- package/src/lib/migrate.js.map +1 -1
- package/src/lib/output-style-command.d.ts +1 -1
- package/src/lib/prettier.d.ts +21 -0
- package/src/lib/prettier.js +51 -0
- package/src/lib/prettier.js.map +1 -0
- package/src/lib/sync.d.ts +1 -1
- package/src/lib/sync.js +9 -2
- package/src/lib/sync.js.map +1 -1
- package/src/lib/targets/hooks-shared.d.ts +3 -3
- package/src/lib/targets/hooks-shared.js +58 -20
- package/src/lib/targets/hooks-shared.js.map +1 -1
|
@@ -61,6 +61,7 @@ const remove = takeFlag(args, '--remove', false);
|
|
|
61
61
|
const afterSeconds = takeFlag(args, '--after', true);
|
|
62
62
|
const now = takeFlag(args, '--now', false);
|
|
63
63
|
const dryRun = takeFlag(args, '--dry-run', false);
|
|
64
|
+
const role = takeFlag(args, '--role', true);
|
|
64
65
|
const [command, sender, ...words] = args;
|
|
65
66
|
|
|
66
67
|
const help = `Shared agent mailbox: ${mailbox}
|
|
@@ -74,7 +75,9 @@ Claim: node ${script} claim <name> <path...> [--ttl <hours>] [--force]
|
|
|
74
75
|
Release: node ${script} release <name> [path...] (no path: release every claim of <name>)
|
|
75
76
|
Claims: node ${script} claims
|
|
76
77
|
Up: node ${script} up <name>[=<command>]... (start a tmux session per agent, plus one that runs dispatch)
|
|
77
|
-
Run: node ${script} run <name> <command...>
|
|
78
|
+
Run: node ${script} run <name> <command...> [--role builder] [--dry-run]
|
|
79
|
+
(start or attach the agent in its tmux session; starts dispatch;
|
|
80
|
+
the builder role sets ETHLETE_AGENT_ROLE and gives claude a terse system prompt)
|
|
78
81
|
Install: node ${script} shell-install [<agent>...] [--file <path>] [--remove] [--dry-run]
|
|
79
82
|
(write the shell functions into ~/.zshrc or ~/.bashrc)
|
|
80
83
|
Shell: node ${script} shell-init [<agent>...] (print shell functions that wrap each agent with run; default: claude codex)
|
|
@@ -94,6 +97,10 @@ Keep credentials and personal data out of the mailbox. No agent is started autom
|
|
|
94
97
|
|
|
95
98
|
const tmux = (...tmuxArgs) => spawnSync('tmux', tmuxArgs, { encoding: 'utf8' });
|
|
96
99
|
const quote = (text) => `'${text.replaceAll("'", "'\\''")}'`;
|
|
100
|
+
const BUILDER_PROMPT =
|
|
101
|
+
'The user does not read this chat. Write no text between tool calls; end each turn with at most one line. Reports go to the grill session via SendMessage.';
|
|
102
|
+
const withRole = (words) =>
|
|
103
|
+
role === 'builder' && basename(words[0]) === 'claude' ? [...words, '--append-system-prompt', BUILDER_PROMPT] : words;
|
|
97
104
|
const sessionExists = (name) => tmux('has-session', '-t', `=${name}`).status === 0;
|
|
98
105
|
const ONE_SHOT_FLAGS = new Set(['-p', '--print', '-v', '--version', '-h', '--help']);
|
|
99
106
|
const runsInSession = (agentArgs) =>
|
|
@@ -116,14 +123,15 @@ const restartLog = (message) => {
|
|
|
116
123
|
|
|
117
124
|
/**
|
|
118
125
|
* Waits until the agent ended its turn and nobody types in its prompt, twice in a row; `false` after the time limit.
|
|
126
|
+
* With `allowBusy`, a running turn does not block, since Claude Code queues input typed during a turn.
|
|
119
127
|
* Each change of the reason it waits is logged to `restart.log` next to the mailbox.
|
|
120
128
|
*/
|
|
121
|
-
const waitForQuiet = (session, limitMs = RESTART_WAIT_MS) => {
|
|
129
|
+
const waitForQuiet = (session, limitMs = RESTART_WAIT_MS, allowBusy = false) => {
|
|
122
130
|
const end = Date.now() + limitMs;
|
|
123
131
|
let quiet = 0;
|
|
124
132
|
let reason = '';
|
|
125
133
|
while (Date.now() < end) {
|
|
126
|
-
const busy = busyLine(session);
|
|
134
|
+
const busy = allowBusy ? null : busyLine(session);
|
|
127
135
|
const draft = busy === null ? draftLine(session) : null;
|
|
128
136
|
const next = busy !== null ? `busy: ${busy.trim()}` : draft !== null ? `draft: ${draft}` : '';
|
|
129
137
|
if (next !== reason) restartLog(`${session} ${next ? `waits, ${next}` : 'is quiet'}`);
|
|
@@ -135,6 +143,8 @@ const waitForQuiet = (session, limitMs = RESTART_WAIT_MS) => {
|
|
|
135
143
|
return false;
|
|
136
144
|
};
|
|
137
145
|
|
|
146
|
+
/** The IDE context chip (`[⧉ In file.ts]`, `[⧉ 3 lines selected]`) shares the prompt line and survives `/clear`. */
|
|
147
|
+
const IDE_CHIP = /\[⧉ [^\]]*\]/g;
|
|
138
148
|
const draftLine = (session) => {
|
|
139
149
|
const pane = tmux('capture-pane', '-p', '-e', '-t', `=${session}:`).stdout ?? '';
|
|
140
150
|
const prompt = pane
|
|
@@ -155,7 +165,10 @@ const draftLine = (session) => {
|
|
|
155
165
|
return '';
|
|
156
166
|
})
|
|
157
167
|
.join('');
|
|
158
|
-
const text = visible
|
|
168
|
+
const text = visible
|
|
169
|
+
.replace(/^\s*[❯›]/, '')
|
|
170
|
+
.replace(IDE_CHIP, '')
|
|
171
|
+
.trim();
|
|
159
172
|
return text.length > 0 ? text : null;
|
|
160
173
|
};
|
|
161
174
|
const hasDraft = (session) => draftLine(session) !== null;
|
|
@@ -265,15 +278,24 @@ if (command === 'send' && sender) {
|
|
|
265
278
|
const [name, ...rest] = spec.split('=');
|
|
266
279
|
startSession(sessionFor(name), rest.join('=') || undefined);
|
|
267
280
|
});
|
|
281
|
+
} else if (command === 'run' && sender && words.length && role !== undefined && role !== 'builder') {
|
|
282
|
+
process.stderr.write(`Unknown role ${role}. Only builder exists.\n`);
|
|
283
|
+
process.exitCode = 1;
|
|
284
|
+
} else if (command === 'run' && sender && words.length && dryRun) {
|
|
285
|
+
process.stdout.write(`${role ? `ETHLETE_AGENT_ROLE=${role} ` : ''}${withRole(words).map(quote).join(' ')}\n`);
|
|
268
286
|
} else if (command === 'run' && sender && words.length && !runsInSession(words.slice(1))) {
|
|
269
|
-
const [program, ...programArgs] = words;
|
|
270
|
-
process.exitCode =
|
|
287
|
+
const [program, ...programArgs] = withRole(words);
|
|
288
|
+
process.exitCode =
|
|
289
|
+
spawnSync(program, programArgs, {
|
|
290
|
+
stdio: 'inherit',
|
|
291
|
+
env: role ? { ...process.env, ETHLETE_AGENT_ROLE: role } : process.env,
|
|
292
|
+
}).status ?? 1;
|
|
271
293
|
} else if (command === 'run' && sender && words.length) {
|
|
272
294
|
const withEnv = process.env.AGENT_MAILBOX ? `AGENT_MAILBOX=${quote(process.env.AGENT_MAILBOX)} ` : '';
|
|
273
295
|
if (!sessionExists(DISPATCH_SESSION))
|
|
274
296
|
startSession(DISPATCH_SESSION, `${withEnv}node ${quote(script)} dispatch`, true);
|
|
275
297
|
const session = freeSessionFor(sender);
|
|
276
|
-
const shellCommand = words.map(quote).join(' ')
|
|
298
|
+
const shellCommand = `${role ? `ETHLETE_AGENT_ROLE=${role} ` : ''}${withRole(words).map(quote).join(' ')}`;
|
|
277
299
|
if (process.env.TMUX) {
|
|
278
300
|
if (!sessionExists(session)) tmux('new-session', '-d', '-s', session, '-c', process.cwd(), shellCommand);
|
|
279
301
|
process.exitCode = spawnSync('tmux', ['switch-client', '-t', `=${session}`], { stdio: 'inherit' }).status ?? 1;
|
|
@@ -353,7 +375,7 @@ if (command === 'send' && sender) {
|
|
|
353
375
|
sleep(ENTER_DELAY_MS);
|
|
354
376
|
type('Enter');
|
|
355
377
|
sleep(RESTART_CLEAR_MS);
|
|
356
|
-
if (waitForQuiet(session, RESTART_CLEAR_WAIT_MS)) {
|
|
378
|
+
if (waitForQuiet(session, RESTART_CLEAR_WAIT_MS, true)) {
|
|
357
379
|
type('-l', words.join(' '));
|
|
358
380
|
sleep(ENTER_DELAY_MS);
|
|
359
381
|
type('Enter');
|
|
@@ -361,13 +383,13 @@ if (command === 'send' && sender) {
|
|
|
361
383
|
const saved = join(dirname(mailbox), `restart.prompt.${session}.txt`);
|
|
362
384
|
writeFileSync(saved, `${words.join(' ')}\n`);
|
|
363
385
|
restartLog(
|
|
364
|
-
`${session} not
|
|
386
|
+
`${session} prompt not empty ${RESTART_CLEAR_WAIT_MS / 1000}s after the clear, prompt not typed, saved to ${saved}`,
|
|
365
387
|
);
|
|
366
388
|
tmux(
|
|
367
389
|
'display-message',
|
|
368
390
|
'-t',
|
|
369
391
|
`=${session}:`,
|
|
370
|
-
`restart:
|
|
392
|
+
`restart: prompt not empty after the clear, prompt not typed. Saved to ${saved}`,
|
|
371
393
|
);
|
|
372
394
|
process.exitCode = 1;
|
|
373
395
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: codex-subagent
|
|
3
|
-
description: Delegate a task to a Codex agent (gpt-
|
|
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-
|
|
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-
|
|
42
|
-
| `gpt-
|
|
43
|
-
| `gpt-
|
|
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-
|
|
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>"
|