A visual marker each time Jev acts: one sky-blue line in the transcript per action

Quick Start Guide • Installation Instructions • Core Features • Agent Ecosystem Overview • Command Reference • Documentation Index • Contributing Guidelines
This repository provides a comprehensive Smart Agent Orchestration Framework for Claude Code CLI, featuring 12 specialized agents (consolidated from 31) organized across 6 functional domains, 20 essential commands, and 5 lightweight skills forming a three-tier execution model. With multi-instance parallelization delivering 4-6x performance improvements and intelligent task delegation, the framework transforms development workflows through smart orchestration, coordinated parallel execution, and continuous quality validation.
/sync commandExperience the power of parallel agent orchestration:
# Traditional approach: 3-5 minutes
# Framework approach: 30-45 seconds (5-6x faster)
/audit --scope agents
# Traditional approach: 2-3 minutes
# Framework approach: 30-40 seconds (4-5x faster)
/test
# Traditional approach: 5-7 minutes
# Framework approach: 1-2 minutes (3-4x faster)
/docs
Ensure you have Claude Code CLI installed:
# Install via npm (recommended)
npm install -g @anthropic/claude-code
# Or via Homebrew (macOS)
brew install claude-code
# Verify installation
claude-code --version
# 1. Clone the repository
git clone https://github.com/damilola-elegbede/claude-config.git
cd claude-config
# 2. Launch Claude Code CLI
claude-code
# 3. Deploy complete framework with one command
/sync
# 4. Verify installation with ecosystem health check
/audit --scope all
# 5. Experience the power - try these commands
/prime # Repository analysis with 5 parallel specialists
/test # Intelligent test execution with auto-discovery
/review # Multi-dimensional code review
🎉 You're ready! You now have access to 12 specialized agents and 20 essential commands.
# Multi-agent repository analysis with parallel execution
/prime
# Comprehensive test execution with framework auto-discovery
/test
# Multi-dimensional code review (code-reviewer + security-auditor + test-engineer)
/review
# Documentation generation with 6 parallel processors
/docs
# Smart dependency audit with security scanning
/deps
Table: Performance Improvements with Smart Agent Orchestration Framework
| Operation | Before Framework | After Framework | Performance Improvement | Technology Implementation |
|---|---|---|---|---|
| Agent Ecosystem Audit | 3-5 min | 30-45 sec | 5-6x faster | 8 parallel agent-auditors |
| Test Suite Execution | 2-3 min | 30-40 sec | 4-5x faster | 5 test framework instances |
| Documentation Generation | 5-7 min | 1-2 min | 3-4x faster | 6 document processors |
| Repository Analysis | 1-2 min | 15-20 sec | 4-6x faster | 5 analyzer instances |
| Dependency Security Audit | 2 min | 20-30 sec | 4-6x faster | Per-ecosystem instances |
/syncThe framework includes 5 lightweight skills that provide focused domain expertise without orchestration overhead, filling the gap between direct execution and full agent delegation.
Level 1: Direct Execution → Simple, deterministic tasks (< 5 min)
Level 2: Skills → Lightweight expertise (YAML, Markdown, Python)
Level 3: Agents → Complex specialists (backend-engineer, ml-engineer)
| Skill | Category | Focus | Use When |
|---|---|---|---|
| yaml | format | YAML syntax, frontmatter validation | Creating/editing agents and commands |
| markdown | format | Markdown linting, documentation | Writing/fixing documentation |
| python | language | Python patterns, validation scripts | Writing automation tools |
| bash | workflow | Shell scripting, git hooks | Building workflows and hooks |
| git-workflows | workflow | Git operations, branching, commits | Managing version control |
Example:
User: "Validate this YAML frontmatter"
→ Uses yaml skill (seconds)
User: "Implement a new backend API"
→ Uses backend-engineer agent (minutes, multi-step)
See Skills Guide for detailed documentation and usage patterns.
The framework features 12 specialized agents (consolidated from 31) organized across 6 functional domains for complete development lifecycle coverage with reduced complexity.
| Category | Count | Key Specialists | Purpose |
|---|---|---|---|
| Development | 3 | backend-engineer, frontend-engineer, data-engineer | Core programming and implementation |
| Architecture | 1 | architect | System, API, cloud, and frontend architecture |
| Quality | 3 | code-reviewer, security-auditor, test-engineer | Testing, validation, and quality assurance |
| Infrastructure | 2 | devops, debugger | Operations, CI/CD, and debugging |
| Research | 1 | researcher | Tech research and codebase analysis |
| Resume/Career | 1 | career-assistant | Job applications and career support |
| Documentation | 1 | tech-writer | Technical documentation and READMEs |
19 agents were consolidated into the remaining 12:
Project: Full-Stack Application
Strategy: Parallel specialist deployment across platforms
Execution:
Wave 1:
- architect: System architecture and API design
- researcher: Requirements analysis and user research
Wave 2:
- backend-engineer: API implementation and microservices
- frontend-engineer: Web application development
Wave 3:
- test-engineer: Comprehensive testing strategy and automation
- security-auditor: Security assessment and vulnerability testing
- debugger: Performance optimization and monitoring
Result: 70% faster delivery through coordinated parallel execution
Project: Production Readiness Assessment
Strategy: Multi-dimensional validation with parallel specialists
Execution:
- code-reviewer: Code quality analysis and best practices validation
- security-auditor: Security vulnerability assessment and compliance
- test-engineer: Test coverage analysis and automation strategy
- debugger: Performance bottleneck identification and optimization
Quality Gates: 95% coverage across all validation dimensions
Performance: 5x faster than sequential validation
| Command | Description | Performance Gain | Key Features |
|---|---|---|---|
/sync | Deploy complete framework configurations | N/A | One-command setup, validation, backup |
/test | Multi-agent test execution with auto-discovery | 4-5x faster | 5 test suite instances, framework detection |
/prime | Parallel repository analysis and insights | 4-6x faster | 5 analyzer instances, comprehensive profiling |
/audit | Unified ecosystem validation (agents/commands/all) | 5-6x faster | Parallel validation, comprehensive coverage |
/review | Multi-dimensional quality analysis | Enhanced coverage | Parallel specialists, comprehensive assessment |
/docs | Documentation orchestration with parallel processors | 3-4x faster | 6 document instances, automated generation |
/plan | Strategic project planning with principal-architect | Enhanced quality | TDD methodology, architectural guidance |
/debug | Systematic investigation with evidence gathering | Improved accuracy | Hypothesis testing, systematic debugging |
/resolve-comments | Intelligent PR resolution based on comment analysis | Context-aware | Multi-agent deployment, automated fixes |
/deps | Security-first dependency management | Vulnerability scanning | Multi-language support, security assessment |
/fix-ci | Automated CI/CD failure resolution | Pattern recognition | DevOps expertise, automated remediation |
/pr | Intelligent PR creation with tech-writer collaboration | Enhanced descriptions | Context-aware analysis, professional documentation |
/implementation-plan | Generate detailed implementation plans without execution | Planning acceleration | Task breakdown, risk assessment, verification planning |
/verify | Command execution verification with percentage alignment analysis | Quality assurance | Multi-wave analysis, requirement validation, actionable recommendations |
/commit - Enhanced Git operations with repository hygiene features, automatic temporary file cleanup, and professional repository standards maintenance/push - Safe repository operations with comprehensive validation/branch - Context-aware branching with intelligent naming conventions/deploy - Production deployment with comprehensive orchestration/monitor - System monitoring with intelligent alerting and analysis/audit --scope commands - Command quality assurance and ecosystem validation/ship-it - Release management with comprehensive workflow automation/prompt - Prompt development and testing utility for optimizationPerfect for most users wanting immediate access to the complete framework:
# Clone the repository
git clone https://github.com/damilola-elegbede/claude-config.git
cd claude-config
# Launch Claude Code CLI
claude-code
# Deploy complete framework
/sync
# Verify installation
/audit --scope agents
/prime --lite
For users who want selective component installation:
# Clone repository
git clone https://github.com/damilola-elegbede/claude-config.git
cd claude-config
# Create Claude configuration directory
mkdir -p ~/.claude
# Install specific components:
# Core agents only
cp -r system-configs/.claude/agents ~/.claude/agents
# Essential commands only
cp -r system-configs/.claude/commands ~/.claude/commands
# Audio notifications (macOS)
cp system-configs/.claude/settings.json ~/.claude/settings.json
# Verify installation
claude-code
/audit --scope agents
For contributors and advanced users:
# Fork and clone your fork
git clone https://github.com/YOUR-USERNAME/claude-config.git
cd claude-config
# Add upstream remote
git remote add upstream https://github.com/damilola-elegbede/claude-config.git
# Run comprehensive validation
./tests/test.sh
./scripts/validate-agent-yaml.py
# Deploy development configuration
/sync
# Run full ecosystem validation
/audit --scope agents
/audit --scope commands
/sync Command: Framework DeploymentThe /sync command is the cornerstone of the framework deployment system, providing one-command setup with comprehensive validation and rollback capabilities.
# Standard deployment
/sync
# Preview changes without deployment
/sync --dry-run
# Force deployment with backup
/sync --backup --force
The sync process deploys the complete framework configuration:
~/.claude/agents/~/.claude/commands/~/.claude/output-styles/glassbox, a live view of Claude's work; jevlight, a marker each time Jev acts) to ~/.claude/mods/, loaded via CLAUDE_CODE_PLUGIN_DIRS~/.claude/settings.json🔄 Syncing Claude configurations...
📁 Source: system-configs/.claude/ (56 files)
📁 Target: ~/.claude/
✅ Pre-sync validation:
- Configuration syntax: Valid (12 agents, 20 commands)
- Target directory: Ready
- Permissions: OK
💾 Creating backup: ~/.claude.backup.20250909_143022
🔄 Synchronizing files:
✅ Agents: 12 files → ~/.claude/agents/
✅ Skills: 34 skills → ~/.claude/skills/
✅ Output styles: 8 files → ~/.claude/output-styles/
✅ Settings: settings.json, statusline.sh, exit_hook.sh, session_start_version_check.sh
📡 MCP Server Configuration:
✅ Updated Claude Desktop config with MCP servers:
- filesystem, github, shadcn-ui
- context7, notionApi
✅ Post-sync validation:
- File integrity: All files copied successfully
- Agent configs: 12/12 valid
- Commands: 20/20 functional
- MCP integration: 5/5 connected
📊 Sync completed successfully:
Files synced: 56 total
Backup location: ~/.claude.backup.20250909_143022
Sync time: 2.3 seconds
# Run all tests with intelligent orchestration
/test
# Run specific test categories
./tests/test.sh commands
./tests/test.sh config
./tests/test.sh integration
./tests/test.sh performance
# Validate agent YAML compliance
./scripts/validate-agent-yaml.py
# Command behavioral validation
/audit --scope commands
claude-config/
├── README.md # This comprehensive documentation
├── QUICKSTART.md # 5-minute setup guide
├── CONTRIBUTING.md # Development and contribution guidelines
├── SECURITY.md # Security policies and reporting
├── LICENSE # MIT license
├── system-configs/ # Source-of-truth configurations
│ ├── .claude/ # Claude Code configuration
│ │ ├── agents/ # 12 agent definitions
│ │ │ ├── backend-engineer.md
│ │ │ ├── frontend-engineer.md
│ │ │ ├── security-auditor.md
│ │ │ ├── test-engineer.md
│ │ │ └── ... (8 more agents)
│ │ ├── commands/ # 20 command definitions
│ │ │ ├── sync.md
│ │ │ ├── test.md
│ │ │ ├── prime.md
│ │ │ ├── agent-audit.md
│ │ │ ├── verify.md
│ │ │ └── ... (15 more commands)
│ │ ├── output-styles/ # Formatting configurations
│ │ ├── mods/ # Hook-module plugins (glassbox, jevlight)
│ │ ├── settings.json # Hook configuration and audio preferences
│ │ ├── statusline.sh # Terminal statusline integration
│ │ ├── exit_hook.sh # SessionEnd cleanup hook
│ │ └── session_start_version_check.sh # SessionStart upgrade/CHANGELOG capture hook
├── docs/ # 42 comprehensive documentation files
│ ├── setup/ # Installation and configuration guides
│ ├── development/ # Development guidelines and requirements
│ ├── performance/ # Performance optimization guides
│ ├── quality/ # Quality assurance documentation
│ ├── architecture/ # System architecture documentation
│ ├── agents/ # Agent templates and categories
│ ├── api/ # API documentation and specifications
│ ├── guides/ # Tutorials and comprehensive guhooks/register.tsx 396 lines1import { atom, read, update } from "claude-code";
2import type { EngineInterface, Register } from "claude-code";
3
4import type { Marks } from "../types";
5
6// jevlight: a visual marker each time Jev acts. Jev is the set of settings
7// (shell) hooks in settings.json; they run beneath every hooks module, so
8// awaiting `next(e)` here returns what they decided. A pass leaves no mark.
9//
10// v2 draws EVERY action in sky blue, in three places:
11// 1. a tool call's action (deny, ask, note, rewrite, trim) is a line under
12// that call's row (ToolResult / ToolGroup), keyed by tool_use_id;
13// 2. a prompt's action (UserPromptSubmit note or hold) is a line under the
14// user's own message row (UserMessage), keyed by the prompt text;
15// 3. a stop's action (held stop, note) is a line under the reply it judged
16// (AssistantMessage), keyed by the reply text;
17// and a session-level action (SessionStart context) is a plain log row (the
18// record) plus a sky-blue band above the prompt until the next prompt.
19// If a coloured row is never drawn (a -p host, a text that does not match), a
20// plain log line says it after FALLBACK_MS, so no action is ever silent.
21//
22// It only watches: every hook returns the result it was handed, unchanged. It
23// cannot tell which settings hook acted, so a non-Jev settings hook that acts
24// (gate.sh) is marked as Jev too. A hook that answers only `systemMessage` or
25// plain stdout is invisible here (the folded result has no such field); the
26// engine itself draws a systemMessage as a "<Event> says: ..." notice.
27
28// The fields of a settings hook result that mean a hook acted.
29export type Outcome = {
30 deny?: string;
31 ask?: string;
32 updatedInput?: Record<string, unknown>;
33 block?: string;
34 preventContinuation?: true;
35 stopReason?: string;
36 additionalContext?: string[];
37 updatedToolOutput?: unknown;
38 updatedMCPToolOutput?: unknown;
39};
40
41export const SKY = "#87CEEB";
42// A reason is shown whole and the transcript row wraps it; the cap only stops a runaway line.
43const LINE_MAX = 1000;
44// Calls whose marks are kept; older ones scroll out of view anyway.
45const KEEP = 200;
46// Sessions whose marks the store keeps, so a resume can draw them again.
47const SESSIONS_KEPT = 20;
48// How long an anchored line waits for its coloured row before a plain log line says it.
49export const FALLBACK_MS = 2500;
50
51const marks = atom({ plugin: "jevlight", key: "marks" } as const, {} as Marks);
52// Lines anchored to a row that has no tool_use_id: "u:<hash>" under a prompt, "a:<hash>" under a reply.
53const notes = atom({ plugin: "jevlight", key: "notes" } as const, {} as Marks);
54// Session-level lines drawn in the band above the prompt until the next prompt.
55const band = atom({ plugin: "jevlight", key: "band" } as const, [] as string[]);
56// The session the marks belong to, learned from any settings hook event.
57const sid = atom(
58 { plugin: "jevlight", key: "sid" } as const,
59 null as string | null,
60);
61
62// Anchors whose coloured row was drawn at least once (read by the fallback timer).
63const drawn = new Set<string>();
64
65export const firstLine = (text: string) => {
66 const line =
67 text
68 .trim()
69 .split("\n")
70 .find((l) => l.trim() !== "") ?? "";
71 return line.length > LINE_MAX ? `${line.slice(0, LINE_MAX - 1)}…` : line;
72};
73
74// The engine prefixes a hook's reason with "PreToolUse:Bash hook error: ", and an
75// exit-2 inline guard adds its whole script in brackets before its stderr.
76export const unprefix = (text: string) => {
77 let t = text.replace(/^[A-Za-z]+(:\S+)? hook error: /, "");
78 if (t.startsWith("[")) {
79 const k = t.lastIndexOf("]: ");
80 if (k > 0) t = t.slice(k + 3);
81 }
82 return t;
83};
84
85// Who acted. Everything is Jev by default (D wants every action flashed); only the inline git guards'
86// "BLOCKED: ..." / "WARNING: ..." wording marks a settings hook that is not Jev.
87export const who = (text: string) =>
88 /^(BLOCKED|WARNING):/.test(unprefix(text).trim()) ? "Hook" : "Jev";
89
90// The line a reader needs: a reason that is only a header ("... violations:")
91// is followed by its first bullet, since the bullet says what was wrong.
92export const gist = (text: string) => {
93 const lines = unprefix(text.trim())
94 .split("\n")
95 .map((l) => l.trim())
96 .filter((l) => l !== "");
97 const head = lines[0] ?? "";
98 const next = lines[1]?.replace(/^[-*•]\s*/, "");
99 const joined = /[:)]$/.test(head) && next ? `${head} ${next}` : head;
100 return joined.length > LINE_MAX
101 ? `${joined.slice(0, LINE_MAX - 1)}…`
102 : joined;
103};
104
105const said = (text: string) => (gist(text) ? `: ${gist(text)}` : "");
106
107// What Jev did at one event, one line per action. `where` names the tool
108// call or the moment (`Bash`, `Bash failure`, `your prompt`).
109export const actionsOf = (where: string, r: Outcome | undefined): string[] => {
110 if (!r) return [];
111 const lines: string[] = [];
112 const add = (text: string | undefined, line: string) =>
113 // U+FE0F asks for emoji presentation; without it a programming font draws its own outline glyph.
114 lines.push(`⚡️ ${text === undefined ? "Jev" : who(text)} ${line}`);
115 if (r.deny !== undefined) add(r.deny, `blocked ${where}${said(r.deny)}`);
116 else if (r.ask !== undefined)
117 add(r.ask, `asked about ${where}${said(r.ask)}`);
118 if (r.block !== undefined) add(r.block, `held ${where}${said(r.block)}`);
119 if (r.preventContinuation) {
120 add(r.stopReason, `stopped the session${said(r.stopReason ?? "")}`);
121 }
122 // Each hook that added context is its own action.
123 for (const c of r.additionalContext ?? []) {
124 if (c.trim() !== "") add(c, `noted ${where}${said(c)}`);
125 }
126 if (r.updatedInput !== undefined) add(undefined, `rewrote ${where}'s input`);
127 if (
128 r.updatedToolOutput !== undefined ||
129 r.updatedMCPToolOutput !== undefined
130 ) {
131 add(undefined, `trimmed ${where}`);
132 }
133 return lines;
134};
135
136// Adds lines to a call's marks, dropping the oldest calls past KEEP.
137export const addMarks = (all: Marks, id: string, lines: string[]): Marks => {
138 const next = { ...all, [id]: [...(all[id] ?? []), ...lines] };
139 const ids = Object.keys(next);
140 for (const old of ids.slice(0, Math.max(0, ids.length - KEEP))) {
141 delete next[old];
142 }
143 return next;
144};
145
146// A key for a row with no tool_use_id: the kind and a hash of its whitespace-normalised text.
147export const anchorKey = (kind: "u" | "a", text: string) => {
148 const s = text.replace(/\s+/g, " ").trim();
149 let h = 5381;
150 for (let i = 0; i < s.length; i++) h = ((h * 33) ^ s.charCodeAt(i)) >>> 0;
151 return `${kind}:${h.toString(36)}:${s.length}`;
152};
153
154// Saves this session's marks and notes, keeping the newest SESSIONS_KEPT sessions.
155const persist = async ($: EngineInterface) => {
156 const session = await read($, sid);
157 if (!session) return;
158 await $.store.set(`marks:${session}`, await read($, marks));
159 await $.store.set(`notes:${session}`, await read($, notes));
160 const kept = ((await $.store.get("sessions")) as string[] | undefined) ?? [];
161 const sessions = [...kept.filter((s) => s !== session), session];
162 for (const old of sessions.slice(0, -SESSIONS_KEPT)) {
163 await $.store.delete(`marks:${old}`);
164 await $.store.delete(`notes:${old}`);
165 }
166 await $.store.set("sessions", sessions.slice(-SESSIONS_KEPT));
167};
168
169// Learns the session id; on a resume, draws its saved marks again.
170const remember = async ($: EngineInterface, session: string) => {
171 try {
172 const previous = await read($, sid);
173 if (previous === session) return;
174 await update($, sid, () => session);
175 const saved = (await $.store.get(`marks:${session}`)) as Marks | undefined;
176 const savedNotes = (await $.store.get(`notes:${session}`)) as
177 | Marks
178 | undefined;
179 // Switching sessions drops the old session's marks (tool_use_ids could
180 // coincide); the first id learned keeps marks drawn before it was known.
181 if (previous) {
182 await update($, marks, () => saved ?? {});
183 await update($, notes, () => savedNotes ?? {});
184 await update($, band, () => []);
185 } else {
186 if (saved) {
187 await update($, marks, (all) => ({ ...saved, ...(all ?? {}) }));
188 }
189 if (savedNotes) {
190 await update($, notes, (all) => ({ ...savedNotes, ...(all ?? {}) }));
191 }
192 }
193 } catch {
194 // losing old marks never stops the session
195 }
196};
197
198// Asks the engine to draw the rows again now that a line exists for one.
199const redraw = ($: EngineInterface) => {
200 try {
201 $.ui.invalidate("ui.render");
202 } catch {
203 // a host that cannot redraw shows the line at the next natural draw
204 }
205};
206
207type Where = { id?: string; anchor?: string; session?: boolean };
208
209// A marker must never break the hook chain: a failure to record it is dropped.
210const mark = async (
211 $: EngineInterface,
212 where: string,
213 r: unknown,
214 at: Where = {},
215) => {
216 try {
217 const lines = actionsOf(where, r as Outcome);
218 if (lines.length === 0) return;
219 if (at.id) {
220 await update($, marks, (all) => addMarks(all ?? {}, at.id!, lines));
221 await persist($);
222 } else if (at.anchor && !(await read($, notes))[at.anchor]) {
223 // A row is found by its text alone, so only a text's first occurrence is anchored; a repeat
224 // ("continue" twice) falls through to a plain line where it happened, never under the first.
225 const key = at.anchor;
226 drawn.delete(key);
227 await update($, notes, (all) => addMarks(all ?? {}, key, lines));
228 await persist($);
229 // No coloured row drawn in time (a -p host, a text that does not match): say it plainly.
230 const say = () => {
231 if (!drawn.has(key)) for (const line of lines) $.ui.log(line);
232 };
233 try {
234 $.clock.after(FALLBACK_MS, say);
235 } catch {
236 // a host with no engine clock (the test kit): the host's own timer
237 setTimeout(say, FALLBACK_MS);
238 }
239 redraw($);
240 } else {
241 // The record: a plain row. For a session-level action, also a band above the prompt.
242 for (const line of lines) $.ui.log(line);
243 if (at.session) {
244 await update($, band, (all) => [...(all ?? []), ...lines].slice(-5));
245 redraw($);
246 }
247 }
248 } catch {
249 // recording the marker failed; Jev's result still goes on below
250 }
251};
252
253// If a hook here throws, hand on what Jev decided: next(e) replays the call
254// that already ran, so nothing runs twice and a Jev block still stands.
255const keepJev = <E, R>(_$: unknown, e: E, next: (e: E) => R) => next(e);
256
257export const register: Register = (on) => {
258 // 0.1.0 pinned a status line, which outlives a reload; clear it.
259 on("session.start", async ($, e, next) => {
260 $.ui.status(undefined);
261 return next(e);
262 });
263
264 // PreToolUse carries no session id; the events around it teach `sid`.
265 on("classic.PreToolUse", async ($, e, next) => {
266 const r = await next(e);
267 await mark($, e.tool, r, { id: e.tool_use_id });
268 return r;
269 }).catch(keepJev);
270
271 on("classic.PostToolUse", async ($, e, next) => {
272 await remember($, e.session_id);
273 const r = await next(e);
274 await mark($, `${e.tool_name} output`, r, { id: e.tool_use_id });
275 return r;
276 }).catch(keepJev);
277
278 on("classic.PostToolUseFailure", async ($, e, next) => {
279 await remember($, e.session_id);
280 const r = await next(e);
281 await mark($, `${e.tool_name} failure`, r, { id: e.tool_use_id });
282 return r;
283 }).catch(keepJev);
284
285 on("classic.UserPromptSubmit", async ($, e, next) => {
286 await remember($, e.session_id);
287 // A new prompt ends the session-level band.
288 await update($, band, () => []);
289 const r = await next(e);
290 await mark($, "your prompt", r, { anchor: anchorKey("u", e.prompt ?? "") });
291 return r;
292 }).catch(keepJev);
293
294 on("classic.Stop", async ($, e, next) => {
295 await remember($, e.session_id);
296 const r = await next(e);
297 const reply = e.last_assistant_message ?? "";
298 await mark(
299 $,
300 "the stop",
301 r,
302 reply.trim() ? { anchor: anchorKey("a", reply) } : {},
303 );
304 return r;
305 }).catch(keepJev);
306
307 on("classic.SessionStart", async ($, e, next) => {
308 await remember($, e.session_id);
309 const r = await next(e);
310 await mark($, "session start", r, { session: true });
311 return r;
312 }).catch(keepJev);
313
314 // The sky-blue lines under a call's result row.
315 on("ui.render", { component: "ToolResult" }, async ($, e, next) => {
316 const lines = (await read($, marks))[e.props.tool_use_id] ?? [];
317 if (lines.length === 0) return next(e);
318 const { Box, Text } = $.ui.resolve(e);
319 return (
320 <Box flexDirection="column">
321 {await next(e)}
322 {lines.map((l) => (
323 <Text color={SKY}>{l}</Text>
324 ))}
325 </Box>
326 );
327 });
328
329 // And under a group of calls ("Read 3 files"), folded or expanded.
330 on("ui.render", { component: "ToolGroup" }, async ($, e, next) => {
331 const all = await read($, marks);
332 const lines = e.props.calls.flatMap((c) =>
333 c.tool_use_id ? (all[c.tool_use_id] ?? []) : [],
334 );
335 if (lines.length === 0) return next(e);
336 const { Box, Text } = $.ui.resolve(e);
337 return (
338 <Box flexDirection="column">
339 {await next(e)}
340 {lines.map((l) => (
341 <Text color={SKY}>{l}</Text>
342 ))}
343 </Box>
344 );
345 });
346
347 // Under the user's own message: what Jev did with that prompt.
348 on("ui.render", { component: "UserMessage" }, async ($, e, next) => {
349 const key = anchorKey("u", e.props.text);
350 const lines = (await read($, notes))[key] ?? [];
351 if (lines.length === 0) return next(e);
352 drawn.add(key);
353 const { Box, Text } = $.ui.resolve(e);
354 return (
355 <Box flexDirection="column">
356 {await next(e)}
357 {lines.map((l) => (
358 <Text color={SKY}>{l}</Text>
359 ))}
360 </Box>
361 );
362 });
363
364 // Under the reply Jev judged at the stop (a hold, a note).
365 on("ui.render", { component: "AssistantMessage" }, async ($, e, next) => {
366 const key = anchorKey("a", e.props.text);
367 const lines = (await read($, notes))[key] ?? [];
368 if (lines.length === 0) return next(e);
369 drawn.add(key);
370 const { Box, Text } = $.ui.resolve(e);
371 return (
372 <Box flexDirection="column">
373 {await next(e)}
374 {lines.map((l) => (
375 <Text color={SKY}>{l}</Text>
376 ))}
377 </Box>
378 );
379 });
380
381 // The band above the prompt: session-level actions until the next prompt.
382 on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
383 const lines = await read($, band);
384 if (lines.length === 0 || e.props.hasSurvey) return next(e);
385 const { Box, Text } = $.ui.resolve(e);
386 return (
387 <Box flexDirection="column">
388 {await next(e)}
389 {lines.map((l) => (
390 <Text color={SKY}>{l}</Text>
391 ))}
392 </Box>
393 );
394 });
395};
396types/index.d.ts 16 lines1// Jev's marks by tool call: each `tool_use_id` Jev acted on, and one line per
2// action, drawn in sky blue under that call's row. Notes use the same shape,
3// keyed by an anchor ("u:<hash>" for a prompt, "a:<hash>" for a reply).
4export type Marks = Record<string, string[]>;
5
6declare module "claude-code" {
7 interface PluginState {
8 jevlight: {
9 marks: Marks;
10 notes: Marks;
11 band: string[];
12 sid: string | null;
13 };
14 }
15}
16