dev-sessions 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -0
- package/dist/backends/claude-tmux.d.ts +19 -0
- package/dist/backends/claude-tmux.js +162 -0
- package/dist/backends/claude-tmux.js.map +1 -0
- package/dist/backends/codex-appserver.d.ts +71 -0
- package/dist/backends/codex-appserver.js +839 -0
- package/dist/backends/codex-appserver.js.map +1 -0
- package/dist/champion-ids.d.ts +11 -0
- package/dist/champion-ids.js +51 -0
- package/dist/champion-ids.js.map +1 -0
- package/dist/cli.d.ts +33 -0
- package/dist/cli.js +307 -0
- package/dist/cli.js.map +1 -0
- package/dist/gateway/client.d.ts +31 -0
- package/dist/gateway/client.js +146 -0
- package/dist/gateway/client.js.map +1 -0
- package/dist/gateway/server.d.ts +27 -0
- package/dist/gateway/server.js +409 -0
- package/dist/gateway/server.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/session-manager.d.ts +36 -0
- package/dist/session-manager.js +407 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/session-store.d.ts +15 -0
- package/dist/session-store.js +143 -0
- package/dist/session-store.js.map +1 -0
- package/dist/transcript/claude-parser.d.ts +17 -0
- package/dist/transcript/claude-parser.js +203 -0
- package/dist/transcript/claude-parser.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
- package/skill/SKILL.md +141 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.sanitizeWorkspacePath = sanitizeWorkspacePath;
|
|
7
|
+
exports.getClaudeTranscriptPath = getClaudeTranscriptPath;
|
|
8
|
+
exports.readClaudeTranscript = readClaudeTranscript;
|
|
9
|
+
exports.extractTextBlocks = extractTextBlocks;
|
|
10
|
+
exports.getAssistantTextBlocks = getAssistantTextBlocks;
|
|
11
|
+
exports.countAssistantMessages = countAssistantMessages;
|
|
12
|
+
exports.countSystemEntries = countSystemEntries;
|
|
13
|
+
exports.inferTranscriptStatus = inferTranscriptStatus;
|
|
14
|
+
exports.hasAssistantResponseAfterLatestUser = hasAssistantResponseAfterLatestUser;
|
|
15
|
+
const promises_1 = require("node:fs/promises");
|
|
16
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
17
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
18
|
+
const WAITING_TOOL_NAMES = new Set([
|
|
19
|
+
'askuserquestion',
|
|
20
|
+
'ask_user',
|
|
21
|
+
'ask_human',
|
|
22
|
+
'request_user_input',
|
|
23
|
+
'requestuserinput'
|
|
24
|
+
]);
|
|
25
|
+
function sanitizeWorkspacePath(workspacePath) {
|
|
26
|
+
// Claude normalizes project directory names by replacing non-alphanumeric characters with "-".
|
|
27
|
+
return node_path_1.default.resolve(workspacePath).replace(/[^a-zA-Z0-9]/g, '-');
|
|
28
|
+
}
|
|
29
|
+
function getClaudeTranscriptPath(workspacePath, internalId, homeDir = node_os_1.default.homedir()) {
|
|
30
|
+
const sanitizedWorkspacePath = sanitizeWorkspacePath(workspacePath);
|
|
31
|
+
return node_path_1.default.join(homeDir, '.claude', 'projects', sanitizedWorkspacePath, `${internalId}.jsonl`);
|
|
32
|
+
}
|
|
33
|
+
async function readClaudeTranscript(filePath) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = await (0, promises_1.readFile)(filePath, 'utf8');
|
|
36
|
+
const lines = raw.split('\n');
|
|
37
|
+
const entries = [];
|
|
38
|
+
for (const line of lines) {
|
|
39
|
+
const trimmed = line.trim();
|
|
40
|
+
if (trimmed.length === 0) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(trimmed);
|
|
45
|
+
entries.push(parsed);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Ignore malformed lines to keep parsing resilient.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return entries;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === 'ENOENT') {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function extractTextBlocks(content) {
|
|
61
|
+
if (typeof content === 'string') {
|
|
62
|
+
return content.length > 0 ? [content] : [];
|
|
63
|
+
}
|
|
64
|
+
if (Array.isArray(content)) {
|
|
65
|
+
const textBlocks = [];
|
|
66
|
+
for (const item of content) {
|
|
67
|
+
textBlocks.push(...extractTextBlocks(item));
|
|
68
|
+
}
|
|
69
|
+
return textBlocks;
|
|
70
|
+
}
|
|
71
|
+
if (!content || typeof content !== 'object') {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const record = content;
|
|
75
|
+
const blockType = typeof record.type === 'string' ? record.type.toLowerCase() : undefined;
|
|
76
|
+
if (typeof record.text === 'string' &&
|
|
77
|
+
record.text.length > 0 &&
|
|
78
|
+
(blockType === undefined || blockType === 'text')) {
|
|
79
|
+
return [record.text];
|
|
80
|
+
}
|
|
81
|
+
if (record.content !== undefined) {
|
|
82
|
+
return extractTextBlocks(record.content);
|
|
83
|
+
}
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
function isHumanMessage(entry) {
|
|
87
|
+
const normalizedType = entry.type?.toLowerCase();
|
|
88
|
+
return normalizedType === 'human' || normalizedType === 'user';
|
|
89
|
+
}
|
|
90
|
+
function isAssistantMessage(entry) {
|
|
91
|
+
return entry.type?.toLowerCase() === 'assistant';
|
|
92
|
+
}
|
|
93
|
+
function isWaitingToolName(value) {
|
|
94
|
+
return typeof value === 'string' && WAITING_TOOL_NAMES.has(value.toLowerCase());
|
|
95
|
+
}
|
|
96
|
+
function containsAskUserToolCall(value) {
|
|
97
|
+
if (value === null || value === undefined) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
return value.some((item) => containsAskUserToolCall(item));
|
|
102
|
+
}
|
|
103
|
+
if (typeof value !== 'object') {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const record = value;
|
|
107
|
+
const blockType = typeof record.type === 'string' ? record.type.toLowerCase() : '';
|
|
108
|
+
const functionValue = record.function && typeof record.function === 'object'
|
|
109
|
+
? record.function
|
|
110
|
+
: undefined;
|
|
111
|
+
const hasKnownToolName = isWaitingToolName(record.name) ||
|
|
112
|
+
isWaitingToolName(record.tool) ||
|
|
113
|
+
isWaitingToolName(record.tool_name) ||
|
|
114
|
+
isWaitingToolName(functionValue?.name);
|
|
115
|
+
const hasToolShape = blockType.includes('tool') ||
|
|
116
|
+
blockType === 'function_call' ||
|
|
117
|
+
record.input !== undefined ||
|
|
118
|
+
record.arguments !== undefined ||
|
|
119
|
+
record.tool !== undefined ||
|
|
120
|
+
record.tool_name !== undefined ||
|
|
121
|
+
functionValue !== undefined;
|
|
122
|
+
if (hasKnownToolName && hasToolShape) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return Object.values(record).some((item) => containsAskUserToolCall(item));
|
|
126
|
+
}
|
|
127
|
+
function getAssistantTextBlocks(entries) {
|
|
128
|
+
const assistantBlocks = [];
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (!isAssistantMessage(entry)) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
assistantBlocks.push(...extractTextBlocks(entry.message?.content));
|
|
134
|
+
}
|
|
135
|
+
return assistantBlocks;
|
|
136
|
+
}
|
|
137
|
+
function countAssistantMessages(entries) {
|
|
138
|
+
return entries.reduce((count, entry) => count + (isAssistantMessage(entry) ? 1 : 0), 0);
|
|
139
|
+
}
|
|
140
|
+
function countSystemEntries(entries) {
|
|
141
|
+
return entries.reduce((count, entry) => count + (entry.type === 'system' ? 1 : 0), 0);
|
|
142
|
+
}
|
|
143
|
+
function findLastIndex(entries, predicate) {
|
|
144
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
145
|
+
if (predicate(entries[index])) {
|
|
146
|
+
return index;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return -1;
|
|
150
|
+
}
|
|
151
|
+
function inferTranscriptStatus(entries) {
|
|
152
|
+
if (entries.length === 0) {
|
|
153
|
+
return 'idle';
|
|
154
|
+
}
|
|
155
|
+
let lastHumanIndex = -1;
|
|
156
|
+
let lastAskUserIndex = -1;
|
|
157
|
+
entries.forEach((entry, index) => {
|
|
158
|
+
if (isHumanMessage(entry)) {
|
|
159
|
+
lastHumanIndex = index;
|
|
160
|
+
}
|
|
161
|
+
if (isAssistantMessage(entry) && containsAskUserToolCall(entry)) {
|
|
162
|
+
lastAskUserIndex = index;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
if (lastAskUserIndex > lastHumanIndex) {
|
|
166
|
+
return 'waiting_for_input';
|
|
167
|
+
}
|
|
168
|
+
const lastEntry = entries[entries.length - 1];
|
|
169
|
+
// A 'system' entry reliably marks the end of a complete turn in Claude Code transcripts.
|
|
170
|
+
if (lastEntry.type === 'system') {
|
|
171
|
+
return 'idle';
|
|
172
|
+
}
|
|
173
|
+
if (isAssistantMessage(lastEntry)) {
|
|
174
|
+
return 'idle';
|
|
175
|
+
}
|
|
176
|
+
if (isHumanMessage(lastEntry)) {
|
|
177
|
+
return 'working';
|
|
178
|
+
}
|
|
179
|
+
// For other entry types (progress, file-history-snapshot), fall back to index comparison.
|
|
180
|
+
// During tool execution, a 'progress' entry may follow an assistant tool_use entry,
|
|
181
|
+
// but the subsequent tool_result (type=user) hasn't arrived yet — treat as 'working'
|
|
182
|
+
// unless a system entry closed the turn.
|
|
183
|
+
const lastAssistantIndex = findLastIndex(entries, (entry) => isAssistantMessage(entry));
|
|
184
|
+
const lastSystemIndex = findLastIndex(entries, (entry) => entry.type === 'system');
|
|
185
|
+
// If the last system entry is after the last assistant, the turn is definitely complete.
|
|
186
|
+
if (lastSystemIndex > lastAssistantIndex && lastAssistantIndex > lastHumanIndex) {
|
|
187
|
+
return 'idle';
|
|
188
|
+
}
|
|
189
|
+
// If there's a progress entry between assistant and the next user entry, Claude may be
|
|
190
|
+
// executing a tool — prefer 'working' unless the system entry confirms idle.
|
|
191
|
+
if (lastAssistantIndex > lastHumanIndex && lastSystemIndex < lastAssistantIndex) {
|
|
192
|
+
return 'working';
|
|
193
|
+
}
|
|
194
|
+
return lastAssistantIndex > lastHumanIndex ? 'idle' : 'working';
|
|
195
|
+
}
|
|
196
|
+
function hasAssistantResponseAfterLatestUser(entries) {
|
|
197
|
+
const lastHumanIndex = findLastIndex(entries, (entry) => isHumanMessage(entry));
|
|
198
|
+
if (lastHumanIndex < 0) {
|
|
199
|
+
return entries.some((entry) => isAssistantMessage(entry));
|
|
200
|
+
}
|
|
201
|
+
return entries.slice(lastHumanIndex + 1).some((entry) => isAssistantMessage(entry));
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=claude-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-parser.js","sourceRoot":"","sources":["../../src/transcript/claude-parser.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type SessionMode = 'yolo' | 'native' | 'docker';
|
|
2
|
+
export type SessionStatus = 'active' | 'inactive';
|
|
3
|
+
export type AgentTurnStatus = 'idle' | 'working' | 'waiting_for_input';
|
|
4
|
+
export type SessionCli = 'claude' | 'codex';
|
|
5
|
+
export type CodexTurnStatus = 'completed' | 'failed' | 'interrupted';
|
|
6
|
+
export interface StoredSession {
|
|
7
|
+
championId: string;
|
|
8
|
+
internalId: string;
|
|
9
|
+
cli: SessionCli;
|
|
10
|
+
mode: SessionMode;
|
|
11
|
+
path: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
status: SessionStatus;
|
|
14
|
+
appServerPid?: number;
|
|
15
|
+
appServerPort?: number;
|
|
16
|
+
model?: string;
|
|
17
|
+
codexTurnInProgress?: boolean;
|
|
18
|
+
codexLastCompletedAt?: string;
|
|
19
|
+
lastTurnStatus?: CodexTurnStatus;
|
|
20
|
+
lastTurnError?: string;
|
|
21
|
+
lastAssistantMessages?: string[];
|
|
22
|
+
createdAt: string;
|
|
23
|
+
lastUsed: string;
|
|
24
|
+
}
|
|
25
|
+
export interface WaitResult {
|
|
26
|
+
completed: boolean;
|
|
27
|
+
timedOut: boolean;
|
|
28
|
+
elapsedMs: number;
|
|
29
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dev-sessions",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool to spawn and manage coding agent sessions",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dev-sessions": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"skill/SKILL.md",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json",
|
|
16
|
+
"clean": "rm -rf dist",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:all": "vitest run",
|
|
19
|
+
"test:integration": "vitest run tests/integration/",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"commander": "^14.0.0",
|
|
27
|
+
"express": "^5.2.1",
|
|
28
|
+
"ws": "^8.19.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/express": "^5.0.6",
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"@types/ws": "^8.18.1",
|
|
34
|
+
"typescript": "^5.8.2",
|
|
35
|
+
"vitest": "^3.2.4"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dev-sessions
|
|
3
|
+
description: Spawn, manage, and communicate with other coding agent sessions for parallel work delegation. Use when tasks can be parallelized or when handing off work.
|
|
4
|
+
allowed-tools: Bash(dev-sessions:*)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# dev-sessions: Parallel Agent Delegation
|
|
8
|
+
|
|
9
|
+
Use `dev-sessions` to offload work to other coding-agent sessions while you continue orchestrating, reviewing, and integrating.
|
|
10
|
+
|
|
11
|
+
## Quick Reference
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Create sessions
|
|
15
|
+
dev-sessions create --description "backend API hardening"
|
|
16
|
+
dev-sessions create --path /path/to/repo --mode native -q
|
|
17
|
+
|
|
18
|
+
# Send work
|
|
19
|
+
dev-sessions send fizz-top "Implement task from TODO.md"
|
|
20
|
+
dev-sessions send fizz-top --file BRIEF.md
|
|
21
|
+
|
|
22
|
+
# Monitor and collect
|
|
23
|
+
dev-sessions status fizz-top
|
|
24
|
+
dev-sessions wait fizz-top --timeout 600 --interval 3
|
|
25
|
+
dev-sessions last-message fizz-top --count 2
|
|
26
|
+
dev-sessions list
|
|
27
|
+
|
|
28
|
+
# Cleanup
|
|
29
|
+
dev-sessions kill fizz-top
|
|
30
|
+
|
|
31
|
+
# Install this skill
|
|
32
|
+
dev-sessions install-skill --global
|
|
33
|
+
dev-sessions install-skill --local --claude
|
|
34
|
+
dev-sessions install-skill --global --codex
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Delegate vs Do It Yourself
|
|
38
|
+
|
|
39
|
+
Delegate when:
|
|
40
|
+
- The task can run in parallel with your current work.
|
|
41
|
+
- The scope is large enough to benefit from independent execution.
|
|
42
|
+
- A different expertise area is needed (for example frontend vs backend).
|
|
43
|
+
- Your own context window is getting long and you want to isolate sub-work.
|
|
44
|
+
|
|
45
|
+
Do it yourself when:
|
|
46
|
+
- The task is trivial or faster to complete directly.
|
|
47
|
+
- The task depends heavily on your current conversation context.
|
|
48
|
+
- The change is a quick fix where delegation overhead would dominate.
|
|
49
|
+
|
|
50
|
+
## Writing Strong Task Briefs
|
|
51
|
+
|
|
52
|
+
Include:
|
|
53
|
+
- Context: repo path, subsystem, and what the delegate is expected to own.
|
|
54
|
+
- Constraints: coding standards, forbidden changes, environment limitations.
|
|
55
|
+
- Acceptance criteria: exact behavior or checks that define done.
|
|
56
|
+
- File pointers: specific files to read first.
|
|
57
|
+
- Why: the intent behind the task so the delegate can make good tradeoffs.
|
|
58
|
+
|
|
59
|
+
Brief template:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
Repo: /abs/path/to/repo
|
|
63
|
+
Goal: <what to implement>
|
|
64
|
+
Why: <business/technical reason>
|
|
65
|
+
Read first:
|
|
66
|
+
- src/moduleA.ts
|
|
67
|
+
- tests/moduleA.test.ts
|
|
68
|
+
Constraints:
|
|
69
|
+
- Do not modify generated files
|
|
70
|
+
- Preserve existing CLI behavior
|
|
71
|
+
Acceptance criteria:
|
|
72
|
+
- All related tests pass
|
|
73
|
+
- New behavior verified in <specific test>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Delegation Patterns
|
|
77
|
+
|
|
78
|
+
### Fire-and-Forget (Handoff)
|
|
79
|
+
|
|
80
|
+
Use when user will check output later.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
sid=$(dev-sessions create -q --description "handoff")
|
|
84
|
+
dev-sessions send "$sid" --file HANDOFF.md
|
|
85
|
+
# Done for now; no immediate follow-up needed.
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Synchronous Delegation
|
|
89
|
+
|
|
90
|
+
Use when you need result before continuing.
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
sid=$(dev-sessions create -q --description "implement parser fix")
|
|
94
|
+
dev-sessions send "$sid" "Fix parser bug in src/parser.ts and add tests."
|
|
95
|
+
dev-sessions wait "$sid"
|
|
96
|
+
dev-sessions last-message "$sid"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Fan-Out
|
|
100
|
+
|
|
101
|
+
Use when independent tasks can run in parallel.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
s1=$(dev-sessions create -q --description "frontend")
|
|
105
|
+
s2=$(dev-sessions create -q --description "backend")
|
|
106
|
+
dev-sessions send "$s1" --file FRONTEND_BRIEF.md
|
|
107
|
+
dev-sessions send "$s2" --file BACKEND_BRIEF.md
|
|
108
|
+
dev-sessions wait "$s1"
|
|
109
|
+
dev-sessions wait "$s2"
|
|
110
|
+
dev-sessions last-message "$s1"
|
|
111
|
+
dev-sessions last-message "$s2"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Iterative Delegation
|
|
115
|
+
|
|
116
|
+
Use when delegate output informs the next prompt.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
sid=$(dev-sessions create -q --description "iterative refactor")
|
|
120
|
+
dev-sessions send "$sid" "Step 1: propose refactor plan for src/cache.ts."
|
|
121
|
+
dev-sessions wait "$sid"
|
|
122
|
+
dev-sessions last-message "$sid"
|
|
123
|
+
dev-sessions send "$sid" "Step 2: implement approved plan and add tests."
|
|
124
|
+
dev-sessions wait "$sid"
|
|
125
|
+
dev-sessions last-message "$sid"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Polling Best Practices
|
|
129
|
+
|
|
130
|
+
- Prefer `dev-sessions wait <id>` instead of manual polling loops.
|
|
131
|
+
- If polling manually:
|
|
132
|
+
- Check `dev-sessions status <id>` first.
|
|
133
|
+
- Read output with `dev-sessions last-message <id>` only when status is `idle`.
|
|
134
|
+
- For complex tasks, wait 30-60 seconds between status checks.
|
|
135
|
+
|
|
136
|
+
## Anti-Patterns
|
|
137
|
+
|
|
138
|
+
- Delegating tasks that are quicker to do directly.
|
|
139
|
+
- Sending vague briefs without file references or acceptance criteria.
|
|
140
|
+
- Polling every few seconds for long-running work (wastes tokens).
|
|
141
|
+
- Reading `last-message` repeatedly without checking status first.
|