pixel-agents-vscode
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePixel Agents VSCode Extension
Pixel Agents VS Code扩展
Skill by ara.so — AI Agent Skills collection.
Pixel Agents is a VS Code extension that visualizes multi-agent AI systems (currently Claude Code) as pixel art characters in a customizable office environment. Each agent becomes an animated character that reflects its real-time activity — typing when writing code, reading when searching files, and displaying speech bubbles when waiting for input.
由ara.so开发的技能——AI Agent技能合集。
Pixel Agents是一款VS Code扩展,它可将多Agent AI系统(目前支持Claude Code)在可自定义的办公环境中以像素艺术角色的形式可视化展示。每个Agent都会变成一个动画角色,实时反映其活动状态——编写代码时会呈现打字动作,搜索文件时会呈现阅读动作,等待输入时会显示对话气泡。
Installation
安装
From VS Code Marketplace
从VS Code应用商店安装
- Open VS Code
- Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
- Search for "Pixel Agents"
- Click Install
Or install via command line:
bash
code --install-extension pablodelucca.pixel-agents- 打开VS Code
- 进入扩展面板(快捷键:Ctrl+Shift+X / Cmd+Shift+X)
- 搜索“Pixel Agents”
- 点击安装
或通过命令行安装:
bash
code --install-extension pablodelucca.pixel-agentsFrom Source
从源码安装
bash
git clone https://github.com/pablodelucca/pixel-agents.git
cd pixel-agents
npm install
cd webview-ui && npm install && cd ..
npm run buildThen press F5 in VS Code to launch the Extension Development Host.
bash
git clone https://github.com/pablodelucca/pixel-agents.git
cd pixel-agents
npm install
cd webview-ui && npm install && cd ..
npm run build然后在VS Code中按F5启动扩展开发宿主环境。
Prerequisites
前置要求
- VS Code: Version 1.105.0 or later
- Claude Code CLI: Must be installed and configured
bash
# Install Claude Code (if not already installed) npm install -g @anthropic-ai/claude-code
- VS Code:版本1.105.0或更高
- Claude Code CLI:必须已安装并配置
bash
# 安装Claude Code(如未安装) npm install -g @anthropic-ai/claude-code
Key Features and Usage
核心功能与使用方法
Spawning Agents
创建Agent
Open the Pixel Agents panel (appears in bottom panel area alongside terminal):
Create normal agent:
typescript
// Click "+ Agent" button in Pixel Agents panel
// This spawns a new Claude Code terminal with a characterCreate agent with skip permissions:
typescript
// Right-click "+ Agent" button
// Select "Launch with --dangerously-skip-permissions"
// Agent will bypass all tool approval prompts打开Pixel Agents面板(位于底部面板区域,与终端并列):
创建普通Agent:
typescript
// 点击Pixel Agents面板中的“+ Agent”按钮
// 这会生成一个带有角色的新Claude Code终端创建带有跳过权限的Agent:
typescript
// 右键点击“+ Agent”按钮
// 选择“Launch with --dangerously-skip-permissions”
// 该Agent将绕过所有工具授权提示Managing Characters
管理角色
Assign character to a seat:
typescript
// 1. Click a character to select it
// 2. Click an empty seat to reassign the characterView agent status:
- Walking: Agent is navigating to desk/idle
- Typing: Writing code, creating files, making changes
- Reading: Searching files, analyzing code
- Speech bubble: Waiting for user input or permission
- Idle: No current activity
为角色分配座位:
typescript
// 1. 点击一个角色选中它
// 2. 点击空座位重新分配角色查看Agent状态:
- 行走:Agent正在前往工位/处于空闲状态
- 打字:正在编写代码、创建文件、修改内容
- 阅读:正在搜索文件、分析代码
- 对话气泡:正在等待用户输入或授权
- 空闲:无当前活动
Office Layout Editor
办公布局编辑器
Open editor:
typescript
// Click "Layout" button in Pixel Agents panelEditor tools:
- Select (S): Select and move furniture
- Paint (P): Paint floor tiles
- Erase (E): Remove items
- Place (F): Add furniture
- Eyedropper (I): Pick colors/tiles
- Pick (K): Select furniture type
Keyboard shortcuts:
- /
Ctrl+Z: Undo (50 levels)Cmd+Z - /
Ctrl+Y: RedoCmd+Y - : Remove selected item
Delete - Arrow keys: Fine-tune placement
Expand grid:
typescript
// Click the ghost border outside current grid
// Grid expands up to 64×64 tiles打开编辑器:
typescript
// 点击Pixel Agents面板中的“Layout”按钮编辑器工具:
- 选择(S):选择并移动家具
- 绘制(P):绘制地板瓷砖
- 擦除(E):移除物品
- 放置(F):添加家具
- 取色器(I):拾取颜色/瓷砖
- 选择家具类型(K):选择要放置的家具类型
键盘快捷键:
- /
Ctrl+Z:撤销(最多50级)Cmd+Z - /
Ctrl+Y:重做Cmd+Y - :移除选中物品
Delete - 方向键:微调放置位置
扩展网格:
typescript
// 点击当前网格外的虚边框
// 网格最大可扩展至64×64瓷砖Configuration
配置
Access settings via gear icon in Pixel Agents panel:
Sound notifications:
typescript
// Toggle sound when agent finishes turn
// Settings → Enable Sound NotificationsDebug view:
typescript
// Settings → Debug View
// Shows per-agent diagnostics:
// - JSONL file status
// - Lines parsed
// - Last data timestamp
// - File pathExport/Import layouts:
typescript
// Settings → Export Layout (saves as JSON)
// Settings → Import Layout (load from JSON file)Add external assets:
typescript
// Settings → Add Asset Directory
// Point to folder with custom furniture packs通过Pixel Agents面板中的齿轮图标访问设置:
声音通知:
typescript
// 切换Agent完成任务时的声音提示
// 设置 → Enable Sound Notifications调试视图:
typescript
// 设置 → Debug View
// 显示每个Agent的诊断信息:
// - JSONL文件状态
// - 已解析行数
// - 最后数据时间戳
// - 文件路径导出/导入布局:
typescript
// 设置 → Export Layout(保存为JSON格式)
// 设置 → Import Layout(从JSON文件加载)添加外部资源:
typescript
// 设置 → Add Asset Directory
// 指向包含自定义家具包的文件夹Working with Assets
资源使用说明
Asset Structure
资源结构
All assets are in :
webview-ui/public/assets/assets/
├── furniture/
│ ├── desk-01/
│ │ ├── manifest.json
│ │ └── sprite.png
│ └── chair-01/
│ ├── manifest.json
│ └── sprite.png
├── floors/
│ └── tile-wood.png
└── walls/
└── brick/
├── manifest.json
└── tileset.png所有资源都位于目录下:
webview-ui/public/assets/assets/
├── furniture/
│ ├── desk-01/
│ │ ├── manifest.json
│ │ └── sprite.png
│ └── chair-01/
│ ├── manifest.json
│ └── sprite.png
├── floors/
│ └── tile-wood.png
└── walls/
└── brick/
├── manifest.json
└── tileset.pngCreating Custom Furniture
创建自定义家具
Manifest structure:
json
{
"id": "desk-modern",
"name": "Modern Desk",
"category": "desks",
"width": 2,
"height": 1,
"isSeat": true,
"seatOffset": { "x": 0, "y": -16 },
"rotationGroups": [
{
"rotations": ["north", "east", "south", "west"],
"sprite": "desk-modern.png"
}
],
"stateGroups": [
{
"state": "off",
"sprite": "desk-modern-off.png"
},
{
"state": "on",
"sprite": "desk-modern-on.png"
}
]
}Add to project:
bash
undefined清单结构:
json
{
"id": "desk-modern",
"name": "Modern Desk",
"category": "desks",
"width": 2,
"height": 1,
"isSeat": true,
"seatOffset": { "x": 0, "y": -16 },
"rotationGroups": [
{
"rotations": ["north", "east", "south", "west"],
"sprite": "desk-modern.png"
}
],
"stateGroups": [
{
"state": "off",
"sprite": "desk-modern-off.png"
},
{
"state": "on",
"sprite": "desk-modern-on.png"
}
]
}添加到项目:
bash
undefined1. Create folder in assets/furniture/
1. 在assets/furniture/目录下创建文件夹
mkdir webview-ui/public/assets/furniture/desk-modern
mkdir webview-ui/public/assets/furniture/desk-modern
2. Add sprite PNG and manifest.json
2. 添加精灵图PNG和manifest.json
3. Rebuild extension
3. 重新构建扩展
npm run build
undefinednpm run build
undefinedUsing External Asset Directories
使用外部资源目录
typescript
// 1. Create asset directory structure:
// /my-assets/
// furniture/
// custom-desk/
// manifest.json
// sprite.png
// 2. In Pixel Agents Settings → Add Asset Directory
// 3. Select /my-assets/
// 4. Assets appear in furniture pickerExternal manifest.json:
json
{
"id": "custom-plant",
"name": "Custom Plant",
"category": "decorations",
"width": 1,
"height": 1,
"isSeat": false,
"rotationGroups": [
{
"rotations": ["north"],
"sprite": "plant.png"
}
]
}typescript
// 1. 创建资源目录结构:
// /my-assets/
// furniture/
// custom-desk/
// manifest.json
// sprite.png
// 2. 在Pixel Agents设置中 → Add Asset Directory
// 3. 选择/my-assets/
// 4. 资源将出现在家具选择器中外部manifest.json示例:
json
{
"id": "custom-plant",
"name": "Custom Plant",
"category": "decorations",
"width": 1,
"height": 1,
"isSeat": false,
"rotationGroups": [
{
"rotations": ["north"],
"sprite": "plant.png"
}
]
}Code Examples
代码示例
Monitoring Agent Activity (Extension Side)
监控Agent活动(扩展端)
typescript
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
// Watch Claude Code JSONL transcript for agent activity
function watchAgentTranscript(projectPath: string, agentId: string): vscode.Disposable {
const jsonlPath = path.join(
projectPath,
'.claude',
'projects',
agentId,
'transcript.jsonl'
);
let lastPosition = 0;
const interval = setInterval(() => {
if (!fs.existsSync(jsonlPath)) {
return;
}
const stats = fs.statSync(jsonlPath);
if (stats.size <= lastPosition) {
return;
}
const buffer = Buffer.alloc(stats.size - lastPosition);
const fd = fs.openSync(jsonlPath, 'r');
fs.readSync(fd, buffer, 0, buffer.length, lastPosition);
fs.closeSync(fd);
const lines = buffer.toString('utf-8').split('\n').filter(l => l.trim());
lines.forEach(line => {
try {
const record = JSON.parse(line);
handleAgentRecord(agentId, record);
} catch (err) {
console.error('[Pixel Agents] Failed to parse JSONL:', err);
}
});
lastPosition = stats.size;
}, 500);
return new vscode.Disposable(() => clearInterval(interval));
}
function handleAgentRecord(agentId: string, record: any): void {
// Detect agent activity from JSONL record type
if (record.type === 'tool_use') {
const toolName = record.content?.name;
if (toolName === 'write_file' || toolName === 'edit_file') {
updateAgentState(agentId, 'typing');
} else if (toolName === 'search_files' || toolName === 'read_file') {
updateAgentState(agentId, 'reading');
} else if (toolName === 'run_command') {
updateAgentState(agentId, 'typing');
}
} else if (record.type === 'user_message') {
updateAgentState(agentId, 'idle');
}
}
function updateAgentState(agentId: string, state: string): void {
// Send message to webview
webviewPanel.webview.postMessage({
command: 'updateAgentState',
agentId,
state
});
}typescript
import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
// 监控Claude Code JSONL记录文件以追踪Agent活动
function watchAgentTranscript(projectPath: string, agentId: string): vscode.Disposable {
const jsonlPath = path.join(
projectPath,
'.claude',
'projects',
agentId,
'transcript.jsonl'
);
let lastPosition = 0;
const interval = setInterval(() => {
if (!fs.existsSync(jsonlPath)) {
return;
}
const stats = fs.statSync(jsonlPath);
if (stats.size <= lastPosition) {
return;
}
const buffer = Buffer.alloc(stats.size - lastPosition);
const fd = fs.openSync(jsonlPath, 'r');
fs.readSync(fd, buffer, 0, buffer.length, lastPosition);
fs.closeSync(fd);
const lines = buffer.toString('utf-8').split('\n').filter(l => l.trim());
lines.forEach(line => {
try {
const record = JSON.parse(line);
handleAgentRecord(agentId, record);
} catch (err) {
console.error('[Pixel Agents] Failed to parse JSONL:', err);
}
});
lastPosition = stats.size;
}, 500);
return new vscode.Disposable(() => clearInterval(interval));
}
function handleAgentRecord(agentId: string, record: any): void {
// 从JSONL记录类型检测Agent活动
if (record.type === 'tool_use') {
const toolName = record.content?.name;
if (toolName === 'write_file' || toolName === 'edit_file') {
updateAgentState(agentId, 'typing');
} else if (toolName === 'search_files' || toolName === 'read_file') {
updateAgentState(agentId, 'reading');
} else if (toolName === 'run_command') {
updateAgentState(agentId, 'typing');
}
} else if (record.type === 'user_message') {
updateAgentState(agentId, 'idle');
}
}
function updateAgentState(agentId: string, state: string): void {
// 向Webview发送消息
webviewPanel.webview.postMessage({
command: 'updateAgentState',
agentId,
state
});
}Character Animation (Webview Side)
角色动画(Webview端)
typescript
interface Character {
id: string;
x: number;
y: number;
targetX: number;
targetY: number;
state: 'idle' | 'walking' | 'typing' | 'reading';
direction: 'north' | 'south' | 'east' | 'west';
frame: number;
spriteSheet: HTMLImageElement;
}
class CharacterRenderer {
private characters: Map<string, Character> = new Map();
private readonly TILE_SIZE = 16;
private readonly FRAME_RATE = 8; // frames per second
private frameCounter = 0;
update(deltaTime: number): void {
this.frameCounter += deltaTime;
const frameInterval = 1000 / this.FRAME_RATE;
this.characters.forEach(char => {
// Update position if walking
if (char.state === 'walking') {
const dx = char.targetX - char.x;
const dy = char.targetY - char.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 1) {
char.x = char.targetX;
char.y = char.targetY;
char.state = 'idle';
} else {
const speed = 2; // pixels per frame
char.x += (dx / distance) * speed;
char.y += (dy / distance) * speed;
// Update direction based on movement
if (Math.abs(dx) > Math.abs(dy)) {
char.direction = dx > 0 ? 'east' : 'west';
} else {
char.direction = dy > 0 ? 'south' : 'north';
}
}
}
// Advance animation frame
if (this.frameCounter >= frameInterval) {
char.frame = (char.frame + 1) % this.getFrameCount(char.state);
}
});
if (this.frameCounter >= frameInterval) {
this.frameCounter = 0;
}
}
render(ctx: CanvasRenderingContext2D): void {
this.characters.forEach(char => {
const spriteX = this.getSpriteX(char);
const spriteY = this.getSpriteY(char);
ctx.drawImage(
char.spriteSheet,
spriteX, spriteY,
this.TILE_SIZE, this.TILE_SIZE * 2, // source
Math.floor(char.x), Math.floor(char.y),
this.TILE_SIZE, this.TILE_SIZE * 2 // destination
);
});
}
private getSpriteX(char: Character): number {
return char.frame * this.TILE_SIZE;
}
private getSpriteY(char: Character): number {
const directionOffsets = {
'south': 0,
'west': 1,
'east': 2,
'north': 3
};
const stateOffsets = {
'idle': 0,
'walking': 0,
'typing': 4,
'reading': 8
};
return (stateOffsets[char.state] + directionOffsets[char.direction])
* this.TILE_SIZE * 2;
}
private getFrameCount(state: string): number {
if (state === 'walking') return 4;
if (state === 'typing' || state === 'reading') return 3;
return 1; // idle
}
moveCharacter(id: string, targetX: number, targetY: number): void {
const char = this.characters.get(id);
if (char) {
char.targetX = targetX;
char.targetY = targetY;
char.state = 'walking';
}
}
setCharacterState(id: string, state: Character['state']): void {
const char = this.characters.get(id);
if (char) {
char.state = state;
char.frame = 0;
}
}
}typescript
interface Character {
id: string;
x: number;
y: number;
targetX: number;
targetY: number;
state: 'idle' | 'walking' | 'typing' | 'reading';
direction: 'north' | 'south' | 'east' | 'west';
frame: number;
spriteSheet: HTMLImageElement;
}
class CharacterRenderer {
private characters: Map<string, Character> = new Map();
private readonly TILE_SIZE = 16;
private readonly FRAME_RATE = 8; // 每秒帧数
private frameCounter = 0;
update(deltaTime: number): void {
this.frameCounter += deltaTime;
const frameInterval = 1000 / this.FRAME_RATE;
this.characters.forEach(char => {
// 如果处于行走状态,更新位置
if (char.state === 'walking') {
const dx = char.targetX - char.x;
const dy = char.targetY - char.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 1) {
char.x = char.targetX;
char.y = char.targetY;
char.state = 'idle';
} else {
const speed = 2; // 每帧移动像素数
char.x += (dx / distance) * speed;
char.y += (dy / distance) * speed;
// 根据移动方向更新角色朝向
if (Math.abs(dx) > Math.abs(dy)) {
char.direction = dx > 0 ? 'east' : 'west';
} else {
char.direction = dy > 0 ? 'south' : 'north';
}
}
}
// 更新动画帧
if (this.frameCounter >= frameInterval) {
char.frame = (char.frame + 1) % this.getFrameCount(char.state);
}
});
if (this.frameCounter >= frameInterval) {
this.frameCounter = 0;
}
}
render(ctx: CanvasRenderingContext2D): void {
this.characters.forEach(char => {
const spriteX = this.getSpriteX(char);
const spriteY = this.getSpriteY(char);
ctx.drawImage(
char.spriteSheet,
spriteX, spriteY,
this.TILE_SIZE, this.TILE_SIZE * 2, // 源区域
Math.floor(char.x), Math.floor(char.y),
this.TILE_SIZE, this.TILE_SIZE * 2 // 目标区域
);
});
}
private getSpriteX(char: Character): number {
return char.frame * this.TILE_SIZE;
}
private getSpriteY(char: Character): number {
const directionOffsets = {
'south': 0,
'west': 1,
'east': 2,
'north': 3
};
const stateOffsets = {
'idle': 0,
'walking': 0,
'typing': 4,
'reading': 8
};
return (stateOffsets[char.state] + directionOffsets[char.direction])
* this.TILE_SIZE * 2;
}
private getFrameCount(state: string): number {
if (state === 'walking') return 4;
if (state === 'typing' || state === 'reading') return 3;
return 1; // 空闲状态
}
moveCharacter(id: string, targetX: number, targetY: number): void {
const char = this.characters.get(id);
if (char) {
char.targetX = targetX;
char.targetY = targetY;
char.state = 'walking';
}
}
setCharacterState(id: string, state: Character['state']): void {
const char = this.characters.get(id);
if (char) {
char.state = state;
char.frame = 0;
}
}
}Pathfinding (BFS for Agent Movement)
路径查找(Agent移动的BFS算法)
typescript
interface Point {
x: number;
y: number;
}
class Pathfinder {
private grid: boolean[][]; // true = walkable
private width: number;
private height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
this.grid = Array(height).fill(null).map(() => Array(width).fill(true));
}
setObstacle(x: number, y: number, blocked: boolean): void {
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
this.grid[y][x] = !blocked;
}
}
findPath(start: Point, end: Point): Point[] | null {
if (!this.isWalkable(end.x, end.y)) return null;
const queue: { point: Point; path: Point[] }[] = [
{ point: start, path: [start] }
];
const visited = new Set<string>();
visited.add(`${start.x},${start.y}`);
const directions = [
{ x: 0, y: -1 }, // north
{ x: 1, y: 0 }, // east
{ x: 0, y: 1 }, // south
{ x: -1, y: 0 } // west
];
while (queue.length > 0) {
const { point, path } = queue.shift()!;
if (point.x === end.x && point.y === end.y) {
return path;
}
for (const dir of directions) {
const next = { x: point.x + dir.x, y: point.y + dir.y };
const key = `${next.x},${next.y}`;
if (
this.isWalkable(next.x, next.y) &&
!visited.has(key)
) {
visited.add(key);
queue.push({
point: next,
path: [...path, next]
});
}
}
}
return null; // No path found
}
private isWalkable(x: number, y: number): boolean {
return (
x >= 0 &&
x < this.width &&
y >= 0 &&
y < this.height &&
this.grid[y][x]
);
}
}
// Usage
const pathfinder = new Pathfinder(32, 32);
// Mark furniture as obstacles
pathfinder.setObstacle(5, 5, true); // desk
pathfinder.setObstacle(6, 5, true); // desk continuation
// Find path from spawn to desk
const path = pathfinder.findPath({ x: 1, y: 1 }, { x: 5, y: 6 });
if (path) {
// Move character along path
path.forEach((point, index) => {
setTimeout(() => {
renderer.moveCharacter('agent-1', point.x * 16, point.y * 16);
}, index * 200);
});
}typescript
interface Point {
x: number;
y: number;
}
class Pathfinder {
private grid: boolean[][]; // true = 可通行
private width: number;
private height: number;
constructor(width: number, height: number) {
this.width = width;
this.height = height;
this.grid = Array(height).fill(null).map(() => Array(width).fill(true));
}
setObstacle(x: number, y: number, blocked: boolean): void {
if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
this.grid[y][x] = !blocked;
}
}
findPath(start: Point, end: Point): Point[] | null {
if (!this.isWalkable(end.x, end.y)) return null;
const queue: { point: Point; path: Point[] }[] = [
{ point: start, path: [start] }
];
const visited = new Set<string>();
visited.add(`${start.x},${start.y}`);
const directions = [
{ x: 0, y: -1 }, // 北
{ x: 1, y: 0 }, // 东
{ x: 0, y: 1 }, // 南
{ x: -1, y: 0 } // 西
];
while (queue.length > 0) {
const { point, path } = queue.shift()!;
if (point.x === end.x && point.y === end.y) {
return path;
}
for (const dir of directions) {
const next = { x: point.x + dir.x, y: point.y + dir.y };
const key = `${next.x},${next.y}`;
if (
this.isWalkable(next.x, next.y) &&
!visited.has(key)
) {
visited.add(key);
queue.push({
point: next,
path: [...path, next]
});
}
}
}
return null; // 未找到路径
}
private isWalkable(x: number, y: number): boolean {
return (
x >= 0 &&
x < this.width &&
y >= 0 &&
y < this.height &&
this.grid[y][x]
);
}
}
// 使用示例
const pathfinder = new Pathfinder(32, 32);
// 将家具标记为障碍物
pathfinder.setObstacle(5, 5, true); // 桌子
pathfinder.setObstacle(6, 5, true); // 桌子延伸部分
// 查找从出生点到桌子的路径
const path = pathfinder.findPath({ x: 1, y: 1 }, { x: 5, y: 6 });
if (path) {
// 沿路径移动角色
path.forEach((point, index) => {
setTimeout(() => {
renderer.moveCharacter('agent-1', point.x * 16, point.y * 16);
}, index * 200);
});
}Troubleshooting
故障排除
Agent Not Appearing
Agent未显示
Enable Debug View:
typescript
// Settings → Debug View (toggle on)
// Check per-agent diagnostics:
// - "JSONL not found" = extension can't locate session file
// - "Lines parsed: 0" = no activity detected
// - Verify file path matches expected locationCheck Debug Console:
typescript
// If running from source (F5 Development Host):
// View → Debug Console
// Search for "[Pixel Agents]"
// Look for:
// - Project directory resolution errors
// - JSONL polling status
// - Path encoding mismatches启用调试视图:
typescript
// 设置 → Debug View(开启)
// 查看每个Agent的诊断信息:
// - "JSONL not found" = 扩展无法找到会话文件
// - "Lines parsed: 0" = 未检测到活动
// - 验证文件路径是否与预期位置匹配检查调试控制台:
typescript
// 如果从源码运行(F5开发宿主环境):
// 视图 → 调试控制台
// 搜索"[Pixel Agents]"
// 查找以下信息:
// - 项目目录解析错误
// - JSONL轮询状态
// - 路径编码不匹配Agent Stuck on Idle
Agent一直处于空闲状态
Common causes:
-
JSONL file not found: Claude Code session hasn't created transcript yetbash
# Check if file exists ls ~/.claude/projects/*/transcript.jsonl -
Polling lag: Extension polls every 500ms, brief delays are normal
-
Unrecognized record type: Claude Code may emit new JSONL typestypescript
// Check Debug Console for "Unrecognized JSONL record type: X"
常见原因:
-
JSONL文件未找到:Claude Code会话尚未创建记录文件bash
# 检查文件是否存在 ls ~/.claude/projects/*/transcript.jsonl -
轮询延迟:扩展每500ms轮询一次,短暂延迟属于正常现象
-
无法识别的记录类型:Claude Code可能会生成新的JSONL类型typescript
// 在调试控制台中查找"Unrecognized JSONL record type: X"
Agent-Terminal Desync
Agent与终端不同步
Symptoms:
- Character appears but terminal is closed
- Terminal exists but no character
- Multiple characters for one terminal
Fixes:
typescript
// 1. Close all Claude Code terminals
// 2. In Pixel Agents panel, click each character
// 3. Press Delete or click trash icon
// 4. Spawn fresh agent with "+ Agent"症状:
- 角色已显示但终端已关闭
- 终端存在但无角色显示
- 一个终端对应多个角色
修复方法:
typescript
// 1. 关闭所有Claude Code终端
// 2. 在Pixel Agents面板中,点击每个角色
// 3. 按Delete键或点击垃圾桶图标
// 4. 点击"+ Agent"重新创建AgentSpeech Bubble Not Clearing
对话气泡无法清除
Heuristic-based detection limitations:
The extension uses idle timers to detect when agents wait for input. If speech bubble persists:
typescript
// 1. Type something in the Claude Code terminal
// 2. Wait 2-3 seconds for polling to update
// 3. If stuck, close and reopen Pixel Agents panel基于启发式检测的局限性:
扩展使用空闲计时器检测Agent是否等待输入。如果对话气泡持续显示:
typescript
// 1. 在Claude Code终端中输入内容
// 2. 等待2-3秒,待轮询更新状态
// 3. 如果仍未清除,关闭并重新打开Pixel Agents面板Linux/macOS Home Directory Confusion
Linux/macOS主目录识别问题
If you launch VS Code without opening a folder:
bash
undefined如果未打开文件夹就启动VS Code:
bash
undefinedAgent starts in home directory
Agent将在主目录启动
code
code
Sessions tracked under:
会话记录存储在:
~/.claude/projects/<home-directory-hash>/
**Solution:**
```bash~/.claude/projects/<home-directory-hash>/
**解决方案:**
```bashAlways open VS Code with a folder
始终打开VS Code时指定文件夹
code /path/to/project
undefinedcode /path/to/project
undefinedCommon Patterns
常见使用模式
Multi-Agent Workflow
多Agent工作流
typescript
// 1. Spawn multiple agents for parallel work
// Right-click "+ Agent" → create 3 agents
// 2. Assign each to different task
// Agent 1: "Refactor authentication module"
// Agent 2: "Write unit tests for API"
// Agent 3: "Update documentation"
// 3. Monitor all agents visually in office
// Watch characters move between typing/reading states
// 4. Intervene when speech bubble appears
// Click terminal to provide input or approvaltypescript
// 1. 创建多个Agent并行工作
// 右键点击"+ Agent" → 创建3个Agent
// 2. 为每个Agent分配不同任务
// Agent 1:"重构认证模块"
// Agent 2:"为API编写单元测试"
// Agent 3:"更新文档"
// 3. 在办公界面中可视化监控所有Agent
// 观察角色在打字/阅读状态间切换
// 4. 当对话气泡出现时进行干预
// 点击终端提供输入或授权Custom Office for Team Visualization
用于团队可视化的自定义办公布局
typescript
// 1. Design office layout per team structure
// Layout → Create 4 desk clusters for 4 teams
// 2. Export layout for sharing
// Settings → Export Layout → save as team-office.json
// 3. Team members import same layout
// Settings → Import Layout → select team-office.json
// 4. Everyone sees agents in same office structuretypescript
// 1. 根据团队结构设计办公布局
// 布局 → 创建4个工位集群对应4个团队
// 2. 导出布局以便共享
// 设置 → Export Layout → 保存为team-office.json
// 3. 团队成员导入相同布局
// 设置 → Import Layout → 选择team-office.json
// 4. 所有人将在相同的办公结构中查看AgentSub-Agent Visualization
子Agent可视化
When Claude Code uses the tool to spawn sub-agents:
tasktypescript
// Parent agent spawns sub-agent
// Example: Main agent delegates "write tests" to sub-agent
// Visual behavior:
// - New character appears linked to parent
// - Sub-agent character animates independently
// - Both visible until sub-task completes
// - Sub-agent disappears when task done当Claude Code使用工具创建子Agent时:
tasktypescript
// 父Agent创建子Agent
// 示例:主Agent将"编写测试"任务委托给子Agent
// 可视化表现:
// - 新角色出现并关联到父Agent
// - 子Agent角色独立动画
// - 两者将同时显示直到子任务完成
// - 子任务完成后子Agent消失External Asset Workflow
外部资源工作流
typescript
// 1. Create asset pack structure
mkdir -p /my-assets/furniture/custom-furniture
// 2. Add manifest and sprites
cat > /my-assets/furniture/custom-furniture/manifest.json << 'EOF'
{
"id": "gaming-chair",
"name": "Gaming Chair",
"category": "seats",
"width": 1,
"height": 1,
"isSeat": true,
"seatOffset": { "x": 0, "y": -8 },
"rotationGroups": [
{
"rotations": ["north", "south", "east", "west"],
"sprite": "chair.png"
}
]
}
EOF
// 3. Link in Pixel Agents
// Settings → Add Asset Directory → /my-assets
// 4. Use in layout editor
// Layout → Place → Select "Gaming Chair"typescript
// 1. 创建资源包结构
mkdir -p /my-assets/furniture/custom-furniture
// 2. 添加清单和精灵图
cat > /my-assets/furniture/custom-furniture/manifest.json << 'EOF'
{
"id": "gaming-chair",
"name": "Gaming Chair",
"category": "seats",
"width": 1,
"height": 1,
"isSeat": true,
"seatOffset": { "x": 0, "y": -8 },
"rotationGroups": [
{
"rotations": ["north", "south", "east", "west"],
"sprite": "chair.png"
}
]
}
EOF
// 3. 在Pixel Agents中关联
// 设置 → Add Asset Directory → /my-assets
// 4. 在布局编辑器中使用
// 布局 → Place → 选择"Gaming Chair"Extension Commands
扩展命令
Pixel Agents registers the following VS Code commands:
typescript
// Open Pixel Agents panel
"pixelAgents.openPanel"
// Spawn new agent (normal)
"pixelAgents.spawnAgent"
// Spawn agent with skip permissions
"pixelAgents.spawnAgentSkipPermissions"
// Open layout editor
"pixelAgents.openLayoutEditor"
// Open settings modal
"pixelAgents.openSettings"
// Toggle debug view
"pixelAgents.toggleDebug"Access via Command Palette ( / ):
Ctrl+Shift+PCmd+Shift+P> Pixel Agents: Open Panel
> Pixel Agents: Spawn AgentPixel Agents注册了以下VS Code命令:
typescript
// 打开Pixel Agents面板
"pixelAgents.openPanel"
// 创建新Agent(普通模式)
"pixelAgents.spawnAgent"
// 创建带有跳过权限的Agent
"pixelAgents.spawnAgentSkipPermissions"
// 打开布局编辑器
"pixelAgents.openLayoutEditor"
// 打开设置窗口
"pixelAgents.openSettings"
// 切换调试视图
"pixelAgents.toggleDebug"通过命令面板访问(快捷键: / ):
Ctrl+Shift+PCmd+Shift+P> Pixel Agents: Open Panel
> Pixel Agents: Spawn AgentPerformance Considerations
性能考量
JSONL polling:
- Polls every 500ms per agent
- Minimal CPU impact for <10 agents
- For 10+ agents, consider increasing interval in source
Canvas rendering:
- 60 FPS target, degrades gracefully
- Tested up to 64×64 grid with 20 characters
- Use integer zoom levels for pixel-perfect rendering
Memory usage:
- ~5-10 MB per agent (JSONL tracking + character state)
- Asset cache: ~20-50 MB depending on furniture count
JSONL轮询:
- 每个Agent每500ms轮询一次
- 当Agent数量少于10个时,CPU影响极小
- 当Agent数量超过10个时,可考虑在源码中增加轮询间隔
Canvas渲染:
- 目标帧率为60FPS,可优雅降级
- 已测试64×64网格搭配20个角色的场景
- 使用整数缩放级别以实现像素完美渲染
内存使用:
- 每个Agent约占用5-10MB(JSONL追踪 + 角色状态)
- 资源缓存:根据家具数量约占用20-50MB
Future Roadmap
未来规划
Pixel Agents is evolving toward a fully agent-agnostic, platform-agnostic interface:
- Agent-agnostic adapters: Support for Codex, Cursor, Copilot, Gemini
- Platform flexibility: Electron app, web app, beyond VS Code
- Advanced features:
- Desks as directories (drag agent to desk = assign to project)
- Kanban board for autonomous task picking
- Deep agent inspection (model, context, history, prompts)
- Token health bars (rate limits, context windows)
- 3D/VR office environments
See GitHub Discussions for ongoing architecture work.
Pixel Agents正朝着完全Agent无关、平台无关的界面方向发展:
- Agent适配层:支持Codex、Cursor、Copilot、Gemini
- 平台灵活性:Electron应用、Web应用,超越VS Code限制
- 高级功能:
- 工位对应目录(将Agent拖到工位 = 分配到项目)
- 用于自主任务选择的看板
- Agent深度检查(模型、上下文、历史记录、提示词)
- Token健康条(速率限制、上下文窗口)
- 3D/VR办公环境
查看GitHub Discussions了解正在进行的架构工作。