Initial commit
This commit is contained in:
16
.claude-plugin/plugin.json
Normal file
16
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "claude-agent-lifecycle",
|
||||||
|
"description": "Persistent agent lifecycle management for Claude Code. Provides 6 agent lifespans (ephemeral, turn, context, session, workflow, project) with automatic disposal via hooks.",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "Hendrik Geldenhuys",
|
||||||
|
"url": "https://github.com/hgeldenhuys"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./.claude/skills/"
|
||||||
|
],
|
||||||
|
"hooks": {
|
||||||
|
"Stop": "hooks/lifecycle-manager.ts",
|
||||||
|
"SessionEnd": "hooks/lifecycle-manager.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
212
.claude/skills/managing-agent-lifecycles/SKILL.md
Normal file
212
.claude/skills/managing-agent-lifecycles/SKILL.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
---
|
||||||
|
name: managing-agent-lifecycles
|
||||||
|
description: Implements persistent agent lifecycle management with 6 lifespans (ephemeral, turn, context, session, workflow, project). Use when creating agents that survive across turns, managing workflow-scoped execution, or needing automatic cleanup at lifecycle boundaries.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Managing Agent Lifecycles
|
||||||
|
|
||||||
|
Persistent agent lifecycle management for Claude Code projects. Creates agents with defined lifespans and automatic disposal.
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- Creating agents that persist beyond a single call
|
||||||
|
- Managing workflow-scoped agents (story execution, feature development)
|
||||||
|
- Automatic agent cleanup at lifecycle boundaries (Stop, SessionEnd)
|
||||||
|
- Building systems with multiple cooperating agents
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
|
||||||
|
// Create session-scoped agent (survives until session ends)
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: 'my-advisor',
|
||||||
|
model: 'haiku',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resume later in same session
|
||||||
|
const advisor = await registry.resume('my-advisor');
|
||||||
|
```
|
||||||
|
|
||||||
|
## The 6 Lifespans
|
||||||
|
|
||||||
|
| Lifespan | Auto-Disposal | Use Case |
|
||||||
|
|----------|---------------|----------|
|
||||||
|
| `ephemeral` | Immediately | Fire-and-forget tasks |
|
||||||
|
| `turn` | Stop event | Per-response helpers |
|
||||||
|
| `context` | Context reset | Multi-turn conversations |
|
||||||
|
| `session` | SessionEnd | Knowledge advisors |
|
||||||
|
| `workflow` | Manual/complete | Story/feature execution |
|
||||||
|
| `project` | Manual only | Singleton services |
|
||||||
|
|
||||||
|
### Choosing a Lifespan
|
||||||
|
|
||||||
|
```
|
||||||
|
How long should the agent live?
|
||||||
|
├── Just this one call? → ephemeral
|
||||||
|
├── Until response completes? → turn
|
||||||
|
├── Until context resets? → context
|
||||||
|
├── Until session ends? → session
|
||||||
|
├── Until work unit completes? → workflow
|
||||||
|
└── Forever (manual disposal)? → project
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
When implementing agent lifecycle management:
|
||||||
|
|
||||||
|
- [ ] Install package: `bun add claude-agent-lifecycle`
|
||||||
|
- [ ] Choose appropriate lifespan for each agent type
|
||||||
|
- [ ] Configure hooks for automatic disposal (Stop, SessionEnd)
|
||||||
|
- [ ] Use descriptive agent names (e.g., `backend-dev` not `agent1`)
|
||||||
|
- [ ] Store role and capabilities in metadata
|
||||||
|
- [ ] Test with debug mode enabled
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Pattern 1: Session-Scoped Advisor
|
||||||
|
|
||||||
|
For agents persisting throughout a Claude Code session:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: 'shadow-advisor',
|
||||||
|
model: 'haiku',
|
||||||
|
metadata: { role: 'knowledge-retrieval' },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 2: Workflow-Scoped Execution
|
||||||
|
|
||||||
|
For agents tied to a unit of work:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Start executor for story
|
||||||
|
await registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: 'FEAT-001',
|
||||||
|
name: 'executor',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add specialists
|
||||||
|
await registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: 'FEAT-001',
|
||||||
|
name: 'backend-dev',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Complete when done (disposes all workflow agents)
|
||||||
|
await registry.completeWorkflow('FEAT-001');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pattern 3: Turn-Scoped Helper
|
||||||
|
|
||||||
|
For short-lived per-response agents:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { agent } = await registry.create({
|
||||||
|
lifespan: 'turn',
|
||||||
|
name: 'code-analyzer',
|
||||||
|
});
|
||||||
|
// Automatically disposed when Stop hook fires
|
||||||
|
```
|
||||||
|
|
||||||
|
See `patterns.md` for detailed implementations.
|
||||||
|
|
||||||
|
## Hook Integration
|
||||||
|
|
||||||
|
Hooks automatically dispose agents at lifecycle boundaries.
|
||||||
|
|
||||||
|
### hooks/hooks.json
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Stop": [{
|
||||||
|
"hooks": [{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/hooks/lifecycle-manager.ts\""
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"SessionEnd": [{
|
||||||
|
"hooks": [{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bun \"${CLAUDE_PLUGIN_ROOT}/hooks/lifecycle-manager.ts\""
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### hooks/lifecycle-manager.ts
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import { getHookEvent } from 'claude-hooks-sdk';
|
||||||
|
|
||||||
|
const event = getHookEvent();
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
|
||||||
|
if (event.type === 'Stop') {
|
||||||
|
await registry.disposeByLifespan('turn');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === 'SessionEnd') {
|
||||||
|
await registry.disposeByScope(event.session.sessionId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Storage Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/agents/
|
||||||
|
├── session/{session-id}/{agent-name}.json
|
||||||
|
├── workflow/{workflow-id}/{agent-name}.json
|
||||||
|
└── project/{agent-name}.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debug Mode
|
||||||
|
|
||||||
|
Enable for troubleshooting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AGENT_LIFECYCLE_DEBUG=true claude
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
```
|
||||||
|
[agent-lifecycle] Created: shadow-advisor (session)
|
||||||
|
[agent-lifecycle] Stop: Disposed 1 turn-scoped agents
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Summary
|
||||||
|
|
||||||
|
| Method | Purpose |
|
||||||
|
|--------|---------|
|
||||||
|
| `create(config)` | Create or resume agent |
|
||||||
|
| `resume(name)` | Resume by name |
|
||||||
|
| `dispose(id)` | Dispose specific agent |
|
||||||
|
| `startWorkflow(config)` | Start workflow agent |
|
||||||
|
| `completeWorkflow(id)` | Complete and dispose workflow |
|
||||||
|
|
||||||
|
See `api-reference.md` for complete API documentation.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# As plugin (recommended)
|
||||||
|
/plugin marketplace add hgeldenhuys/claude-agent-lifecycle
|
||||||
|
/plugin install claude-agent-lifecycle
|
||||||
|
|
||||||
|
# As package
|
||||||
|
bun add claude-agent-lifecycle
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Files
|
||||||
|
|
||||||
|
- `api-reference.md` - Complete API documentation
|
||||||
|
- `patterns.md` - Detailed pattern implementations
|
||||||
|
- `examples.md` - Real-world usage examples
|
||||||
270
.claude/skills/managing-agent-lifecycles/api-reference.md
Normal file
270
.claude/skills/managing-agent-lifecycles/api-reference.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# API Reference
|
||||||
|
|
||||||
|
Complete API documentation for claude-agent-lifecycle.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [AgentRegistry](#agentregistry)
|
||||||
|
2. [Methods](#methods)
|
||||||
|
3. [Types](#types)
|
||||||
|
4. [Storage Backends](#storage-backends)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## AgentRegistry
|
||||||
|
|
||||||
|
Main class for managing agent lifecycles.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
const registry = new AgentRegistry(options?: RegistryOptions);
|
||||||
|
```
|
||||||
|
|
||||||
|
### RegistryOptions
|
||||||
|
|
||||||
|
| Property | Type | Default | Description |
|
||||||
|
|----------|------|---------|-------------|
|
||||||
|
| `storagePath` | `string` | `.agent/agents` | Base path for file storage |
|
||||||
|
| `debug` | `boolean` | `false` | Enable debug logging |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### create(config)
|
||||||
|
|
||||||
|
Creates a new agent or resumes an existing one.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: 'my-agent',
|
||||||
|
sessionId?: string, // Auto-detected if not provided
|
||||||
|
model?: string, // 'haiku', 'sonnet', 'opus'
|
||||||
|
metadata?: Record<string, unknown>,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `{ agent: Agent, isNew: boolean }`
|
||||||
|
|
||||||
|
**Behavior**:
|
||||||
|
- If agent with same name exists in scope, resumes it
|
||||||
|
- If not, creates new agent
|
||||||
|
- `isNew` indicates whether agent was created or resumed
|
||||||
|
|
||||||
|
### resume(name, scope?)
|
||||||
|
|
||||||
|
Resumes an existing agent by name.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const agent = await registry.resume('my-agent');
|
||||||
|
const agent = await registry.resume('my-agent', 'session-123');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `Agent | null`
|
||||||
|
|
||||||
|
**Throws**: If agent not found and no scope provided
|
||||||
|
|
||||||
|
### dispose(agentId)
|
||||||
|
|
||||||
|
Disposes a specific agent by ID.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
await registry.dispose('agent-uuid-here');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `void`
|
||||||
|
|
||||||
|
### disposeByLifespan(lifespan)
|
||||||
|
|
||||||
|
Disposes all agents of a specific lifespan type.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const count = await registry.disposeByLifespan('turn');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `number` - Count of disposed agents
|
||||||
|
|
||||||
|
### disposeByScope(scope)
|
||||||
|
|
||||||
|
Disposes all agents in a specific scope.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const count = await registry.disposeByScope('session-123');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `number` - Count of disposed agents
|
||||||
|
|
||||||
|
### list(filter?)
|
||||||
|
|
||||||
|
Lists agents matching optional filter criteria.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// List all agents
|
||||||
|
const all = await registry.list();
|
||||||
|
|
||||||
|
// Filter by lifespan
|
||||||
|
const sessionAgents = await registry.list({ lifespan: 'session' });
|
||||||
|
|
||||||
|
// Filter by scope
|
||||||
|
const workflowAgents = await registry.list({ scope: 'FEAT-001' });
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `Agent[]`
|
||||||
|
|
||||||
|
### startWorkflow(config)
|
||||||
|
|
||||||
|
Starts a workflow-scoped agent.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const agent = await registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: 'FEAT-001',
|
||||||
|
name: 'executor',
|
||||||
|
workflowType?: string, // e.g., 'loom-story'
|
||||||
|
model?: string,
|
||||||
|
metadata?: Record<string, unknown>,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `Agent`
|
||||||
|
|
||||||
|
### completeWorkflow(workflowId)
|
||||||
|
|
||||||
|
Completes a workflow and disposes all its agents.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const disposed = await registry.completeWorkflow('FEAT-001');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `number` - Count of disposed agents
|
||||||
|
|
||||||
|
### getWorkflowAgents(workflowId)
|
||||||
|
|
||||||
|
Gets all agents for a specific workflow.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const agents = await registry.getWorkflowAgents('FEAT-001');
|
||||||
|
```
|
||||||
|
|
||||||
|
**Returns**: `Agent[]`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### Agent
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface Agent {
|
||||||
|
agentId: string; // Unique identifier (UUID)
|
||||||
|
name: string; // Human-readable name
|
||||||
|
lifespan: Lifespan; // One of 6 lifespans
|
||||||
|
scope: string; // Scope identifier
|
||||||
|
model?: string; // Model preference
|
||||||
|
turnCount: number; // Interaction count
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
|
createdAt: string; // ISO timestamp
|
||||||
|
lastActiveAt: string; // ISO timestamp
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lifespan
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type Lifespan =
|
||||||
|
| 'ephemeral' // Single use
|
||||||
|
| 'turn' // Until Stop event
|
||||||
|
| 'context' // Until context reset
|
||||||
|
| 'session' // Until SessionEnd event
|
||||||
|
| 'workflow' // Until workflow completes
|
||||||
|
| 'project'; // Until manually disposed
|
||||||
|
```
|
||||||
|
|
||||||
|
### CreateConfig
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface CreateConfig {
|
||||||
|
lifespan: Lifespan;
|
||||||
|
name: string;
|
||||||
|
sessionId?: string;
|
||||||
|
model?: string;
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorkflowConfig
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface WorkflowConfig {
|
||||||
|
lifespan: 'workflow';
|
||||||
|
workflowId: string;
|
||||||
|
name: string;
|
||||||
|
workflowType?: string;
|
||||||
|
model?: string;
|
||||||
|
metadata?: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ListFilter
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ListFilter {
|
||||||
|
lifespan?: Lifespan;
|
||||||
|
scope?: string;
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Storage Backends
|
||||||
|
|
||||||
|
### MemoryStorage
|
||||||
|
|
||||||
|
Used for ephemeral, turn, and context lifespans.
|
||||||
|
|
||||||
|
- Data stored in memory
|
||||||
|
- Lost on process restart
|
||||||
|
- Fast access, no I/O
|
||||||
|
|
||||||
|
### FileStorage
|
||||||
|
|
||||||
|
Used for session, workflow, and project lifespans.
|
||||||
|
|
||||||
|
- Data persisted to JSON files
|
||||||
|
- Survives process restarts
|
||||||
|
- Structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
.agent/agents/
|
||||||
|
├── lifecycle.log # Debug logs (when enabled)
|
||||||
|
├── session/
|
||||||
|
│ └── {session-id}/
|
||||||
|
│ └── {agent-name}.json
|
||||||
|
├── workflow/
|
||||||
|
│ └── {workflow-id}/
|
||||||
|
│ └── {agent-name}.json
|
||||||
|
└── project/
|
||||||
|
└── {agent-name}.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Agent JSON Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"agentId": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "shadow-advisor",
|
||||||
|
"lifespan": "session",
|
||||||
|
"scope": "abc-123",
|
||||||
|
"model": "haiku",
|
||||||
|
"turnCount": 5,
|
||||||
|
"metadata": {
|
||||||
|
"role": "knowledge-retrieval",
|
||||||
|
"preloadedKnowledge": ["patterns", "pain-points"]
|
||||||
|
},
|
||||||
|
"createdAt": "2025-01-15T10:30:00Z",
|
||||||
|
"lastActiveAt": "2025-01-15T11:45:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
372
.claude/skills/managing-agent-lifecycles/examples.md
Normal file
372
.claude/skills/managing-agent-lifecycles/examples.md
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
# Real-World Examples
|
||||||
|
|
||||||
|
Practical examples of agent lifecycle management in production systems.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Shadow Advisor (claude-weave)](#example-1-shadow-advisor)
|
||||||
|
2. [Story Execution (claude-loom)](#example-2-story-execution)
|
||||||
|
3. [Custom Hook Handler](#example-3-custom-hook-handler)
|
||||||
|
4. [Debug Logging Setup](#example-4-debug-logging-setup)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 1: Shadow Advisor
|
||||||
|
|
||||||
|
From claude-weave: A session-scoped knowledge retrieval agent.
|
||||||
|
|
||||||
|
### agents/shadow-advisor-lifecycle.ts
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import { getSessionId } from 'claude-hooks-sdk';
|
||||||
|
|
||||||
|
const SHADOW_ADVISOR_CONFIG = {
|
||||||
|
lifespan: 'session' as const,
|
||||||
|
name: 'shadow-advisor',
|
||||||
|
model: 'haiku',
|
||||||
|
metadata: {
|
||||||
|
role: 'knowledge-retrieval',
|
||||||
|
preloadedKnowledge: [
|
||||||
|
'qualia', // Pain points, solutions
|
||||||
|
'epistemology', // Patterns, validations
|
||||||
|
'praxeology', // WoW patterns
|
||||||
|
],
|
||||||
|
capabilities: [
|
||||||
|
'query-11d-knowledge',
|
||||||
|
'synthesize-across-dimensions',
|
||||||
|
'recommend-patterns',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getShadowAdvisor() {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
const sessionId = getSessionId();
|
||||||
|
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
...SHADOW_ADVISOR_CONFIG,
|
||||||
|
sessionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { agent, isNew };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resumeShadowAdvisor() {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
return await registry.resume('shadow-advisor');
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function disposeShadowAdvisor() {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
const agent = await registry.resume('shadow-advisor');
|
||||||
|
if (agent) {
|
||||||
|
await registry.dispose(agent.agentId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage in Weave Hooks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// hooks/weave-hooks.ts
|
||||||
|
import { getHookEvent } from 'claude-hooks-sdk';
|
||||||
|
import { getShadowAdvisor } from '../agents/shadow-advisor-lifecycle';
|
||||||
|
|
||||||
|
async function handleSessionStart() {
|
||||||
|
const { agent, isNew } = await getShadowAdvisor();
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
console.log('Shadow Advisor created for session');
|
||||||
|
// Load 11D knowledge into agent context...
|
||||||
|
} else {
|
||||||
|
console.log(`Shadow Advisor resumed (turn ${agent.turnCount})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 2: Story Execution
|
||||||
|
|
||||||
|
From claude-loom: Workflow-scoped agents for story execution.
|
||||||
|
|
||||||
|
### agents/story-lifecycle.ts
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
interface StoryConfig {
|
||||||
|
storyId: string;
|
||||||
|
title: string;
|
||||||
|
acceptanceCriteria: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StoryLifecycle {
|
||||||
|
private registry: AgentRegistry;
|
||||||
|
private storyId: string;
|
||||||
|
|
||||||
|
constructor(storyId: string) {
|
||||||
|
this.registry = new AgentRegistry();
|
||||||
|
this.storyId = storyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(config: StoryConfig) {
|
||||||
|
// Create main executor
|
||||||
|
const executor = await this.registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: this.storyId,
|
||||||
|
name: 'story-executor',
|
||||||
|
workflowType: 'loom-story',
|
||||||
|
model: 'sonnet',
|
||||||
|
metadata: {
|
||||||
|
title: config.title,
|
||||||
|
acceptanceCriteria: config.acceptanceCriteria,
|
||||||
|
status: 'in-progress',
|
||||||
|
startedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSpecialist(
|
||||||
|
role: 'backend-dev' | 'frontend-dev' | 'backend-qa' | 'frontend-qa'
|
||||||
|
) {
|
||||||
|
const modelMap = {
|
||||||
|
'backend-dev': 'sonnet',
|
||||||
|
'frontend-dev': 'sonnet',
|
||||||
|
'backend-qa': 'haiku',
|
||||||
|
'frontend-qa': 'haiku',
|
||||||
|
};
|
||||||
|
|
||||||
|
return await this.registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: this.storyId,
|
||||||
|
name: role,
|
||||||
|
model: modelMap[role],
|
||||||
|
metadata: {
|
||||||
|
role,
|
||||||
|
assignedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getActiveAgents() {
|
||||||
|
return await this.registry.getWorkflowAgents(this.storyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async complete(outcome: 'success' | 'failed' | 'cancelled') {
|
||||||
|
const agents = await this.getActiveAgents();
|
||||||
|
|
||||||
|
// Log completion stats
|
||||||
|
console.log(`Story ${this.storyId} ${outcome}`);
|
||||||
|
console.log(`Agents used: ${agents.length}`);
|
||||||
|
for (const agent of agents) {
|
||||||
|
console.log(` - ${agent.name}: ${agent.turnCount} turns`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispose all workflow agents
|
||||||
|
return await this.registry.completeWorkflow(this.storyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
async function executeStory() {
|
||||||
|
const story = new StoryLifecycle('FEAT-001');
|
||||||
|
|
||||||
|
await story.start({
|
||||||
|
storyId: 'FEAT-001',
|
||||||
|
title: 'Add user authentication',
|
||||||
|
acceptanceCriteria: [
|
||||||
|
'Users can sign up with email',
|
||||||
|
'Users can log in',
|
||||||
|
'Sessions persist across page reloads',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await story.addSpecialist('backend-dev');
|
||||||
|
await story.addSpecialist('backend-qa');
|
||||||
|
|
||||||
|
// ... execute story tasks ...
|
||||||
|
|
||||||
|
await story.complete('success');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 3: Custom Hook Handler
|
||||||
|
|
||||||
|
Complete hook handler managing multiple agent types.
|
||||||
|
|
||||||
|
### hooks/lifecycle-manager.ts
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
#!/usr/bin/env bun
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import { getHookEvent, getSessionId } from 'claude-hooks-sdk';
|
||||||
|
|
||||||
|
const DEBUG = process.env.AGENT_LIFECYCLE_DEBUG === 'true'
|
||||||
|
|| process.argv.includes('--debug');
|
||||||
|
|
||||||
|
function log(message: string) {
|
||||||
|
if (DEBUG) {
|
||||||
|
console.log(`[agent-lifecycle] ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const event = getHookEvent();
|
||||||
|
const registry = new AgentRegistry({ debug: DEBUG });
|
||||||
|
|
||||||
|
switch (event.type) {
|
||||||
|
case 'SessionStart': {
|
||||||
|
const sessionId = getSessionId();
|
||||||
|
log(`SessionStart: ${sessionId} (source: ${event.source})`);
|
||||||
|
|
||||||
|
// List active agents
|
||||||
|
const agents = await registry.list();
|
||||||
|
if (agents.length > 0) {
|
||||||
|
log(`Active agents: ${agents.map(a => `${a.name}(${a.lifespan})`).join(', ')}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'Stop': {
|
||||||
|
// Dispose turn-scoped agents
|
||||||
|
const turnCount = await registry.disposeByLifespan('turn');
|
||||||
|
if (turnCount > 0) {
|
||||||
|
log(`Stop: Disposed ${turnCount} turn-scoped agents`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SessionEnd': {
|
||||||
|
const sessionId = event.session?.sessionId;
|
||||||
|
if (sessionId) {
|
||||||
|
// Dispose session-scoped agents
|
||||||
|
const sessionCount = await registry.disposeByScope(sessionId);
|
||||||
|
log(`SessionEnd: Disposed ${sessionCount} agents for session ${sessionId}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(error => {
|
||||||
|
console.error('[agent-lifecycle] Error:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 4: Debug Logging Setup
|
||||||
|
|
||||||
|
Setting up comprehensive debug logging.
|
||||||
|
|
||||||
|
### Enable Debug Mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Option 1: Environment variable
|
||||||
|
export AGENT_LIFECYCLE_DEBUG=true
|
||||||
|
claude
|
||||||
|
|
||||||
|
# Option 2: Per-session
|
||||||
|
AGENT_LIFECYCLE_DEBUG=true claude
|
||||||
|
|
||||||
|
# Option 3: In hook command
|
||||||
|
# hooks/hooks.json
|
||||||
|
{
|
||||||
|
"Stop": [{
|
||||||
|
"hooks": [{
|
||||||
|
"type": "command",
|
||||||
|
"command": "bun hooks/lifecycle-manager.ts --debug"
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom Debug Logger
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
class DebugRegistry extends AgentRegistry {
|
||||||
|
private logPath: string;
|
||||||
|
|
||||||
|
constructor(logPath = '.agent/agents/debug.log') {
|
||||||
|
super({ debug: true });
|
||||||
|
this.logPath = logPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeLog(entry: object) {
|
||||||
|
const line = JSON.stringify({
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
...entry,
|
||||||
|
});
|
||||||
|
fs.appendFileSync(this.logPath, line + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(config: any) {
|
||||||
|
const result = await super.create(config);
|
||||||
|
this.writeLog({
|
||||||
|
event: result.isNew ? 'agent:created' : 'agent:resumed',
|
||||||
|
agentId: result.agent.agentId,
|
||||||
|
name: result.agent.name,
|
||||||
|
lifespan: result.agent.lifespan,
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
async dispose(agentId: string) {
|
||||||
|
this.writeLog({
|
||||||
|
event: 'agent:disposed',
|
||||||
|
agentId,
|
||||||
|
});
|
||||||
|
return super.dispose(agentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async completeWorkflow(workflowId: string) {
|
||||||
|
const agents = await this.getWorkflowAgents(workflowId);
|
||||||
|
this.writeLog({
|
||||||
|
event: 'workflow:completed',
|
||||||
|
workflowId,
|
||||||
|
agentCount: agents.length,
|
||||||
|
agents: agents.map(a => a.name),
|
||||||
|
});
|
||||||
|
return super.completeWorkflow(workflowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const registry = new DebugRegistry();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Log Output
|
||||||
|
|
||||||
|
```jsonl
|
||||||
|
{"timestamp":"2025-01-15T10:30:00Z","event":"agent:created","agentId":"abc-123","name":"shadow-advisor","lifespan":"session"}
|
||||||
|
{"timestamp":"2025-01-15T10:30:05Z","event":"agent:created","agentId":"def-456","name":"backend-dev","lifespan":"workflow"}
|
||||||
|
{"timestamp":"2025-01-15T10:45:00Z","event":"workflow:completed","workflowId":"FEAT-001","agentCount":3,"agents":["executor","backend-dev","backend-qa"]}
|
||||||
|
{"timestamp":"2025-01-15T11:00:00Z","event":"agent:disposed","agentId":"abc-123"}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Integration Checklist
|
||||||
|
|
||||||
|
When integrating agent lifecycle into your project:
|
||||||
|
|
||||||
|
- [ ] Install: `bun add claude-agent-lifecycle`
|
||||||
|
- [ ] Create hooks directory with `hooks.json`
|
||||||
|
- [ ] Implement `lifecycle-manager.ts` hook handler
|
||||||
|
- [ ] Define agent configurations (lifespan, name, metadata)
|
||||||
|
- [ ] Add debug logging for development
|
||||||
|
- [ ] Test all lifecycle events (SessionStart, Stop, SessionEnd)
|
||||||
|
- [ ] Verify automatic disposal at boundaries
|
||||||
|
- [ ] Document agent roles and lifespans
|
||||||
356
.claude/skills/managing-agent-lifecycles/patterns.md
Normal file
356
.claude/skills/managing-agent-lifecycles/patterns.md
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
# Agent Lifecycle Patterns
|
||||||
|
|
||||||
|
Detailed implementations of common agent lifecycle patterns.
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Session-Scoped Advisor](#pattern-1-session-scoped-advisor)
|
||||||
|
2. [Workflow-Scoped Execution](#pattern-2-workflow-scoped-execution)
|
||||||
|
3. [Turn-Scoped Helper](#pattern-3-turn-scoped-helper)
|
||||||
|
4. [Project Singleton](#pattern-4-project-singleton)
|
||||||
|
5. [Multi-Agent Orchestration](#pattern-5-multi-agent-orchestration)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 1: Session-Scoped Advisor
|
||||||
|
|
||||||
|
Agents that persist throughout a Claude Code session, ideal for knowledge retrieval and advisory roles.
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
- Knowledge advisors (Shadow Advisor, Librarian)
|
||||||
|
- Context-aware assistants
|
||||||
|
- Session-wide configuration managers
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
export async function getSessionAdvisor(role: string) {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: `${role}-advisor`,
|
||||||
|
model: 'haiku',
|
||||||
|
metadata: {
|
||||||
|
role,
|
||||||
|
capabilities: ['knowledge-retrieval', 'pattern-matching'],
|
||||||
|
preloadedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
// First creation - load initial context
|
||||||
|
console.log(`Created new ${role} advisor`);
|
||||||
|
// Initialize with domain knowledge...
|
||||||
|
} else {
|
||||||
|
// Resumed - increment interaction count
|
||||||
|
console.log(`Resumed ${role} advisor (turn ${agent.turnCount})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const shadow = await getSessionAdvisor('shadow');
|
||||||
|
const librarian = await getSessionAdvisor('librarian');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Integration
|
||||||
|
|
||||||
|
Automatically disposed at SessionEnd:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// hooks/session-cleanup.ts
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import { getHookEvent } from 'claude-hooks-sdk';
|
||||||
|
|
||||||
|
const event = getHookEvent();
|
||||||
|
|
||||||
|
if (event.type === 'SessionEnd') {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
const count = await registry.disposeByScope(event.session.sessionId);
|
||||||
|
console.log(`Disposed ${count} session agents`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 2: Workflow-Scoped Execution
|
||||||
|
|
||||||
|
Agents tied to a bounded unit of work (story, feature, task). Multiple specialists collaborate on a shared workflow.
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
- Story execution (Loom)
|
||||||
|
- Feature development
|
||||||
|
- Multi-step task orchestration
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
export class WorkflowOrchestrator {
|
||||||
|
private registry: AgentRegistry;
|
||||||
|
private workflowId: string;
|
||||||
|
|
||||||
|
constructor(workflowId: string) {
|
||||||
|
this.registry = new AgentRegistry();
|
||||||
|
this.workflowId = workflowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start() {
|
||||||
|
// Create main executor
|
||||||
|
return await this.registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: this.workflowId,
|
||||||
|
name: 'executor',
|
||||||
|
workflowType: 'story-execution',
|
||||||
|
model: 'sonnet',
|
||||||
|
metadata: {
|
||||||
|
startedAt: new Date().toISOString(),
|
||||||
|
status: 'in-progress',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async addSpecialist(role: string) {
|
||||||
|
const validRoles = [
|
||||||
|
'backend-dev',
|
||||||
|
'frontend-dev',
|
||||||
|
'backend-qa',
|
||||||
|
'frontend-qa',
|
||||||
|
'cli-dev',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (!validRoles.includes(role)) {
|
||||||
|
throw new Error(`Invalid role: ${role}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId: this.workflowId,
|
||||||
|
name: role,
|
||||||
|
model: role.includes('qa') ? 'haiku' : 'sonnet',
|
||||||
|
metadata: { role, assignedAt: new Date().toISOString() },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStatus() {
|
||||||
|
const agents = await this.registry.getWorkflowAgents(this.workflowId);
|
||||||
|
return {
|
||||||
|
workflowId: this.workflowId,
|
||||||
|
agentCount: agents.length,
|
||||||
|
agents: agents.map(a => ({
|
||||||
|
name: a.name,
|
||||||
|
turnCount: a.turnCount,
|
||||||
|
lastActive: a.lastActiveAt,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async complete() {
|
||||||
|
const count = await this.registry.completeWorkflow(this.workflowId);
|
||||||
|
console.log(`Completed workflow ${this.workflowId}, disposed ${count} agents`);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
const orchestrator = new WorkflowOrchestrator('FEAT-001');
|
||||||
|
await orchestrator.start();
|
||||||
|
await orchestrator.addSpecialist('backend-dev');
|
||||||
|
await orchestrator.addSpecialist('backend-qa');
|
||||||
|
|
||||||
|
// ... execute work ...
|
||||||
|
|
||||||
|
await orchestrator.complete();
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 3: Turn-Scoped Helper
|
||||||
|
|
||||||
|
Short-lived agents that assist with a single response cycle. Automatically disposed when Stop hook fires.
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
- Code analysis helpers
|
||||||
|
- One-off data processors
|
||||||
|
- Temporary computation agents
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
export async function createTurnHelper(task: string) {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
|
||||||
|
const { agent } = await registry.create({
|
||||||
|
lifespan: 'turn',
|
||||||
|
name: `helper-${task}`,
|
||||||
|
model: 'haiku',
|
||||||
|
metadata: {
|
||||||
|
task,
|
||||||
|
createdFor: 'single-response',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usage - agent disposed automatically at Stop
|
||||||
|
const analyzer = await createTurnHelper('code-analysis');
|
||||||
|
const formatter = await createTurnHelper('output-formatting');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hook Integration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// hooks/turn-cleanup.ts
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
import { getHookEvent } from 'claude-hooks-sdk';
|
||||||
|
|
||||||
|
const event = getHookEvent();
|
||||||
|
|
||||||
|
if (event.type === 'Stop') {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
const count = await registry.disposeByLifespan('turn');
|
||||||
|
if (count > 0) {
|
||||||
|
console.log(`Disposed ${count} turn-scoped agents`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 4: Project Singleton
|
||||||
|
|
||||||
|
Agents that persist indefinitely, shared across all sessions. Requires manual disposal.
|
||||||
|
|
||||||
|
### Use Cases
|
||||||
|
|
||||||
|
- Project configuration managers
|
||||||
|
- Shared knowledge bases
|
||||||
|
- Cross-session state holders
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
export async function getProjectSingleton(name: string) {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
|
||||||
|
const { agent, isNew } = await registry.create({
|
||||||
|
lifespan: 'project',
|
||||||
|
name,
|
||||||
|
metadata: {
|
||||||
|
singleton: true,
|
||||||
|
initVersion: '1.0.0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isNew) {
|
||||||
|
console.log(`Created project singleton: ${name}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Using existing singleton: ${name} (${agent.turnCount} turns)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manual disposal when needed
|
||||||
|
export async function disposeProjectSingleton(name: string) {
|
||||||
|
const registry = new AgentRegistry();
|
||||||
|
const agent = await registry.resume(name);
|
||||||
|
if (agent) {
|
||||||
|
await registry.dispose(agent.agentId);
|
||||||
|
console.log(`Disposed project singleton: ${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern 5: Multi-Agent Orchestration
|
||||||
|
|
||||||
|
Coordinating multiple agents across different lifespans.
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { AgentRegistry } from 'claude-agent-lifecycle';
|
||||||
|
|
||||||
|
export class MultiAgentSystem {
|
||||||
|
private registry: AgentRegistry;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.registry = new AgentRegistry({ debug: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
async initialize(sessionId: string, workflowId: string) {
|
||||||
|
// Session-scoped: knowledge advisors
|
||||||
|
await this.registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: 'shadow-advisor',
|
||||||
|
sessionId,
|
||||||
|
model: 'haiku',
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.registry.create({
|
||||||
|
lifespan: 'session',
|
||||||
|
name: 'librarian',
|
||||||
|
sessionId,
|
||||||
|
model: 'haiku',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Workflow-scoped: execution agents
|
||||||
|
await this.registry.startWorkflow({
|
||||||
|
lifespan: 'workflow',
|
||||||
|
workflowId,
|
||||||
|
name: 'executor',
|
||||||
|
model: 'sonnet',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Turn-scoped: temporary helpers created as needed
|
||||||
|
// (created per-request, not at initialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSystemStatus() {
|
||||||
|
const agents = await this.registry.list();
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: agents.length,
|
||||||
|
byLifespan: {
|
||||||
|
session: agents.filter(a => a.lifespan === 'session').length,
|
||||||
|
workflow: agents.filter(a => a.lifespan === 'workflow').length,
|
||||||
|
turn: agents.filter(a => a.lifespan === 'turn').length,
|
||||||
|
project: agents.filter(a => a.lifespan === 'project').length,
|
||||||
|
},
|
||||||
|
agents: agents.map(a => ({
|
||||||
|
name: a.name,
|
||||||
|
lifespan: a.lifespan,
|
||||||
|
scope: a.scope,
|
||||||
|
turns: a.turnCount,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Best Practices Summary
|
||||||
|
|
||||||
|
| Pattern | Lifespan | Model | Disposal |
|
||||||
|
|---------|----------|-------|----------|
|
||||||
|
| Knowledge Advisor | session | haiku | SessionEnd hook |
|
||||||
|
| Workflow Executor | workflow | sonnet | completeWorkflow() |
|
||||||
|
| Workflow Specialist | workflow | sonnet/haiku | completeWorkflow() |
|
||||||
|
| Turn Helper | turn | haiku | Stop hook |
|
||||||
|
| Project Singleton | project | varies | Manual |
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# claude-agent-lifecycle
|
||||||
|
|
||||||
|
Persistent agent lifecycle management for Claude Code. Provides 6 agent lifespans (ephemeral, turn, context, session, workflow, project) with automatic disposal via hooks.
|
||||||
57
plugin.lock.json
Normal file
57
plugin.lock.json
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:hgeldenhuys/claude-agent-lifecycle:",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "e52859c64f43f3adcef60fe99cd72a6608c55146",
|
||||||
|
"treeHash": "92549440c37c3cb2ce5cf5f7780b4a6f0b5e1b43720e6ada92266117de29d829",
|
||||||
|
"generatedAt": "2025-11-28T10:17:25.928386Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "claude-agent-lifecycle",
|
||||||
|
"description": "Persistent agent lifecycle management for Claude Code. Provides 6 agent lifespans (ephemeral, turn, context, session, workflow, project) with automatic disposal via hooks.",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "1e317d0ac04c5ae7eb5a95db05b52299b3764d7c3babf7c9c51d5221f3834794"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude/skills/managing-agent-lifecycles/examples.md",
|
||||||
|
"sha256": "7e03cd6f07583e953e41e20bcc263bcb0c915a524a912ae5b4412753241d13ce"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude/skills/managing-agent-lifecycles/patterns.md",
|
||||||
|
"sha256": "5480f50288cdcd684ae20a2b582b8cd8e7c0128e6be1e38740ec5f91bceabaca"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude/skills/managing-agent-lifecycles/SKILL.md",
|
||||||
|
"sha256": "defd9e354a411cc475ef01bd31e048386f916490c2ec4c87cf482f13bbcd03c8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude/skills/managing-agent-lifecycles/api-reference.md",
|
||||||
|
"sha256": "2dc50f3cc8520c8ab7cb80d7d4ac5cf42d151c87c8cc0d8c494fd1720793f6db"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "fa8d24e52608648ac23cffabb26471380aa872f9909a7ae605de6458a663e97c"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "92549440c37c3cb2ce5cf5f7780b4a6f0b5e1b43720e6ada92266117de29d829"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user