@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.
Files changed (40) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +7 -1
  3. package/content/hooks/background-design-commands.py +140 -0
  4. package/content/hooks/builder-chat-words.py +74 -0
  5. package/content/hooks/context-warning.py +48 -19
  6. package/content/hooks/git-safety.py +181 -0
  7. package/content/mods/plugins/e2e-progress/.claude-plugin/plugin.json +1 -1
  8. package/content/rules/nx-layout.md +5 -2
  9. package/content/rules/subagent-models.md +2 -2
  10. package/content/skills/agent-sessions/SKILL.md +6 -4
  11. package/content/skills/agent-sessions/agent-sessions.mjs +32 -10
  12. package/content/skills/codex-subagent/SKILL.md +8 -8
  13. package/content/skills/codex-subagent/codex-agent.mjs +1 -1
  14. package/content/skills/design-exploration/SKILL.md +47 -517
  15. package/content/skills/design-exploration/reference-check-and-notes.md +92 -0
  16. package/content/skills/design-exploration/reference-flows-and-wireframes.md +78 -0
  17. package/content/skills/design-exploration/reference-inbox-and-mcp.md +54 -0
  18. package/content/skills/design-exploration/reference-order-and-design-system.md +124 -0
  19. package/content/skills/design-exploration/reference-products-and-sketches.md +199 -0
  20. package/content/skills/design-exploration/reference-shots.md +25 -0
  21. package/content/skills/git-commit/SKILL.md +10 -0
  22. package/content/skills/grilling/SKILL.md +1 -0
  23. package/content/skills/handoff/SKILL.md +20 -4
  24. package/package.json +1 -1
  25. package/src/index.js +2 -1
  26. package/src/index.js.map +1 -1
  27. package/src/lib/git-flow-repair.d.ts +1 -1
  28. package/src/lib/git-flow-start.d.ts +1 -1
  29. package/src/lib/migrate.js +2 -1
  30. package/src/lib/migrate.js.map +1 -1
  31. package/src/lib/output-style-command.d.ts +1 -1
  32. package/src/lib/prettier.d.ts +21 -0
  33. package/src/lib/prettier.js +51 -0
  34. package/src/lib/prettier.js.map +1 -0
  35. package/src/lib/sync.d.ts +1 -1
  36. package/src/lib/sync.js +9 -2
  37. package/src/lib/sync.js.map +1 -1
  38. package/src/lib/targets/hooks-shared.d.ts +3 -3
  39. package/src/lib/targets/hooks-shared.js +58 -20
  40. 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...> (start or attach the agent in its tmux session; starts dispatch)
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.replace(/^\s*[❯›]/, '').trim();
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 = spawnSync(program, programArgs, { stdio: 'inherit' }).status ?? 1;
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 quiet ${RESTART_CLEAR_WAIT_MS / 1000}s after the clear, prompt not typed, saved to ${saved}`,
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: pane not quiet after the clear, prompt not typed. Saved to ${saved}`,
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-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>"