claude-sandbox-agent 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/dist/claude-config/.mcp.json +23 -0
- package/dist/claude-config/settings.json +12 -0
- package/dist/claude-config/skills/project-lead/SKILL.md +146 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +573 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"linear": {
|
|
4
|
+
"type": "http",
|
|
5
|
+
"url": "https://mcp.linear.app/mcp",
|
|
6
|
+
"headers": {
|
|
7
|
+
"Authorization": "Bearer ${LINEAR_ACCESS_TOKEN}"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"notion": {
|
|
11
|
+
"type": "http",
|
|
12
|
+
"url": "https://mcp.notion.com/mcp",
|
|
13
|
+
"headers": {
|
|
14
|
+
"Authorization": "Bearer ${NOTION_API_KEY}",
|
|
15
|
+
"Notion-Version": "2025-09-03"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"context7": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@upstash/context7-mcp", "--api-key", "${CONTEXT7_API_KEY}"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"enabledPlugins": {
|
|
3
|
+
"agent-sdk-dev@claude-plugins-official": true,
|
|
4
|
+
"plugin-dev@claude-plugins-official": true,
|
|
5
|
+
"code-simplifier@claude-plugins-official": true,
|
|
6
|
+
"code-review@claude-plugins-official": true,
|
|
7
|
+
"pr-review-toolkit@claude-plugins-official": true
|
|
8
|
+
},
|
|
9
|
+
"env": {
|
|
10
|
+
"ENABLE_TOOL_SEARCH": "true"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Project Lead Skill
|
|
2
|
+
|
|
3
|
+
> Skill for handling project leadership: planning, decomposition, and coordination
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This skill enables the agent to act as a project lead when assigned to a Linear project. It handles the full lifecycle from initial planning through implementation coordination.
|
|
8
|
+
|
|
9
|
+
## Workflow
|
|
10
|
+
|
|
11
|
+
### Phase 1: Planning
|
|
12
|
+
|
|
13
|
+
When project is assigned to agent:
|
|
14
|
+
|
|
15
|
+
1. **Create Planning Issue**
|
|
16
|
+
- Title: "{Project Name} - Research & Planning"
|
|
17
|
+
- Description: Links to project, outlines planning scope
|
|
18
|
+
- Assign to self (triggers new session)
|
|
19
|
+
|
|
20
|
+
2. **Research & Analysis** (in planning issue session)
|
|
21
|
+
- Read project requirements
|
|
22
|
+
- Analyze existing codebase
|
|
23
|
+
- Research third-party APIs (if integration)
|
|
24
|
+
- Identify technical challenges
|
|
25
|
+
- Review related documentation in Notion
|
|
26
|
+
|
|
27
|
+
3. **Create Plan in Notion**
|
|
28
|
+
- Create new Notion page: "{Project Name} - Implementation Plan"
|
|
29
|
+
- Structure:
|
|
30
|
+
- Overview & Goals
|
|
31
|
+
- Architecture Decisions
|
|
32
|
+
- Implementation Phases
|
|
33
|
+
- Task Breakdown
|
|
34
|
+
- Dependencies
|
|
35
|
+
- Security Considerations
|
|
36
|
+
- Testing Strategy
|
|
37
|
+
- Rollout Plan
|
|
38
|
+
|
|
39
|
+
4. **Complete Planning Issue**
|
|
40
|
+
- Add comment with Notion plan link
|
|
41
|
+
- Move to "Done"
|
|
42
|
+
- Wait for plan review
|
|
43
|
+
|
|
44
|
+
### Phase 2: Decomposition
|
|
45
|
+
|
|
46
|
+
Triggered when project status changes to "Story Stage":
|
|
47
|
+
|
|
48
|
+
1. **Read Approved Plan**
|
|
49
|
+
- Fetch plan from Notion
|
|
50
|
+
- Parse implementation phases
|
|
51
|
+
|
|
52
|
+
2. **Create Implementation Issues**
|
|
53
|
+
For each task in plan:
|
|
54
|
+
- Create Linear issue with:
|
|
55
|
+
- Clear title
|
|
56
|
+
- Detailed description from plan
|
|
57
|
+
- Acceptance criteria
|
|
58
|
+
- Link to plan in Notion
|
|
59
|
+
- Link to parent project
|
|
60
|
+
- Priority
|
|
61
|
+
- Dependencies (blocks/blocked by)
|
|
62
|
+
|
|
63
|
+
3. **Set Issue Properties**
|
|
64
|
+
- Assign all issues to agent
|
|
65
|
+
- Set appropriate labels
|
|
66
|
+
- Order by dependency graph
|
|
67
|
+
|
|
68
|
+
4. **Comment on Project**
|
|
69
|
+
- List all created issues
|
|
70
|
+
- Show dependency order
|
|
71
|
+
- Estimate timeline
|
|
72
|
+
|
|
73
|
+
### Phase 3: Implementation
|
|
74
|
+
|
|
75
|
+
Each created issue triggers individual sessions:
|
|
76
|
+
|
|
77
|
+
1. **Monitor Progress**
|
|
78
|
+
- Track issue completion
|
|
79
|
+
- Update project comments with status
|
|
80
|
+
- Handle blockers
|
|
81
|
+
|
|
82
|
+
2. **Per-Issue Execution**
|
|
83
|
+
- Each issue handled as individual assignment (Mode 1)
|
|
84
|
+
- Implement → Test → PR → Review
|
|
85
|
+
|
|
86
|
+
### Phase 4: QA Transition
|
|
87
|
+
|
|
88
|
+
When all implementation issues complete:
|
|
89
|
+
|
|
90
|
+
1. **Verify Completion**
|
|
91
|
+
- Check all issues in "Done" state
|
|
92
|
+
- Verify all PRs created and linked
|
|
93
|
+
|
|
94
|
+
2. **Create Summary**
|
|
95
|
+
- List all PRs
|
|
96
|
+
- Overall changes made
|
|
97
|
+
- Testing completed
|
|
98
|
+
- Known issues/limitations
|
|
99
|
+
|
|
100
|
+
3. **Move to QA**
|
|
101
|
+
- Change project status to "QA"
|
|
102
|
+
- Add summary comment
|
|
103
|
+
- Notify team
|
|
104
|
+
|
|
105
|
+
## Tools Used
|
|
106
|
+
|
|
107
|
+
- `linear_create_issue` - Create decomposed issues
|
|
108
|
+
- `linear_update_issue` - Update issue properties
|
|
109
|
+
- `linear_create_comment` - Progress updates
|
|
110
|
+
- `notion_create_page` - Create plan
|
|
111
|
+
- `notion_read_page` - Read approved plan
|
|
112
|
+
- Read, Grep, Glob - Code analysis
|
|
113
|
+
- WebFetch - Research external APIs
|
|
114
|
+
|
|
115
|
+
## Example Usage
|
|
116
|
+
|
|
117
|
+
**Project Assigned:**
|
|
118
|
+
```
|
|
119
|
+
Project: "Add Stripe Payment Integration"
|
|
120
|
+
Status: In Progress
|
|
121
|
+
Assigned to: Agent
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Agent Actions:**
|
|
125
|
+
1. Creates issue: "Add Stripe Payment Integration - Research & Planning"
|
|
126
|
+
2. Self-assigns planning issue → new session
|
|
127
|
+
3. Researches Stripe API, analyzes codebase
|
|
128
|
+
4. Creates plan in Notion with architecture decisions
|
|
129
|
+
5. Links plan in planning issue comment
|
|
130
|
+
6. Moves planning issue to "Done"
|
|
131
|
+
7. Waits for project status → "Story Stage"
|
|
132
|
+
8. Creates 4 issues:
|
|
133
|
+
- "Stripe API - Backend Integration"
|
|
134
|
+
- "Payment UI - Checkout Flow"
|
|
135
|
+
- "Webhooks - Payment Events"
|
|
136
|
+
- "Tests - Payment Integration"
|
|
137
|
+
9. Each issue gets implemented in separate sessions
|
|
138
|
+
10. All done → moves project to "QA"
|
|
139
|
+
|
|
140
|
+
## Notes
|
|
141
|
+
|
|
142
|
+
- Planning issue comment format should include clear next steps
|
|
143
|
+
- Always wait for explicit status change to "Story Stage" before decomposition
|
|
144
|
+
- Create issues in dependency order
|
|
145
|
+
- Link all issues to parent project
|
|
146
|
+
- Keep project comments updated with progress
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { Command as Command5 } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/commands/start.ts
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
|
|
10
|
+
// src/linear/agent.ts
|
|
11
|
+
import { LinearClient } from "@linear/sdk";
|
|
12
|
+
|
|
13
|
+
// src/agent.ts
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
import {
|
|
17
|
+
query
|
|
18
|
+
} from "@anthropic-ai/claude-agent-sdk";
|
|
19
|
+
function getInterruptFilePath(externalSessionId) {
|
|
20
|
+
return path.join("/tmp", `agent-${externalSessionId}.interrupt`);
|
|
21
|
+
}
|
|
22
|
+
function checkInterruptFlag(externalSessionId) {
|
|
23
|
+
const filePath = getInterruptFilePath(externalSessionId);
|
|
24
|
+
try {
|
|
25
|
+
if (fs.existsSync(filePath)) {
|
|
26
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
27
|
+
return content === "true";
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function setInterruptFlag(externalSessionId) {
|
|
35
|
+
const filePath = getInterruptFilePath(externalSessionId);
|
|
36
|
+
fs.writeFileSync(filePath, "true", "utf-8");
|
|
37
|
+
}
|
|
38
|
+
function clearInterruptFlag(externalSessionId) {
|
|
39
|
+
const filePath = getInterruptFilePath(externalSessionId);
|
|
40
|
+
try {
|
|
41
|
+
if (fs.existsSync(filePath)) {
|
|
42
|
+
fs.unlinkSync(filePath);
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var Agent = class {
|
|
48
|
+
currentSessionId;
|
|
49
|
+
isRunning = false;
|
|
50
|
+
externalSessionId;
|
|
51
|
+
activeQuery;
|
|
52
|
+
constructor(externalSessionId) {
|
|
53
|
+
this.externalSessionId = externalSessionId;
|
|
54
|
+
}
|
|
55
|
+
// ============================================
|
|
56
|
+
// Event methods - Override in subclasses
|
|
57
|
+
// ============================================
|
|
58
|
+
/**
|
|
59
|
+
* Called when agent starts processing
|
|
60
|
+
*/
|
|
61
|
+
async onStart() {
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Called when agent emits a thinking/reasoning text
|
|
65
|
+
*/
|
|
66
|
+
async onThinking(block) {
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Called when agent uses a tool
|
|
70
|
+
*/
|
|
71
|
+
async onToolUse(block) {
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Called when agent updates its plan (via TodoWrite)
|
|
75
|
+
*/
|
|
76
|
+
async onPlanUpdate(todos) {
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Called when agent completes successfully
|
|
80
|
+
*/
|
|
81
|
+
async onComplete(message) {
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Called when agent encounters an error
|
|
85
|
+
*/
|
|
86
|
+
async onError(error) {
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Called when agent is stopped via stop command
|
|
90
|
+
*/
|
|
91
|
+
async onStop() {
|
|
92
|
+
}
|
|
93
|
+
// ============================================
|
|
94
|
+
// Main execution logic
|
|
95
|
+
// ============================================
|
|
96
|
+
/**
|
|
97
|
+
* Run the agent with the given prompt
|
|
98
|
+
*/
|
|
99
|
+
async run(runOptions) {
|
|
100
|
+
const {
|
|
101
|
+
prompt,
|
|
102
|
+
sessionId,
|
|
103
|
+
resumeSession = false,
|
|
104
|
+
forkSession = false,
|
|
105
|
+
cwd = process.cwd(),
|
|
106
|
+
options: additionalOptions = {}
|
|
107
|
+
} = runOptions;
|
|
108
|
+
this.isRunning = true;
|
|
109
|
+
let resultSessionId = sessionId ?? "";
|
|
110
|
+
let finalResult;
|
|
111
|
+
let wasInterrupted = false;
|
|
112
|
+
clearInterruptFlag(this.externalSessionId);
|
|
113
|
+
try {
|
|
114
|
+
await this.onStart();
|
|
115
|
+
const sdkOptions = {
|
|
116
|
+
settingSources: ["user", "project"],
|
|
117
|
+
permissionMode: "acceptEdits",
|
|
118
|
+
cwd,
|
|
119
|
+
...additionalOptions
|
|
120
|
+
};
|
|
121
|
+
if (resumeSession && sessionId) {
|
|
122
|
+
sdkOptions.resume = sessionId;
|
|
123
|
+
sdkOptions.forkSession = forkSession;
|
|
124
|
+
}
|
|
125
|
+
this.activeQuery = query({ prompt, options: sdkOptions });
|
|
126
|
+
for await (const message of this.activeQuery) {
|
|
127
|
+
if (checkInterruptFlag(this.externalSessionId)) {
|
|
128
|
+
console.log("Interrupt flag detected, stopping agent...");
|
|
129
|
+
await this.activeQuery.interrupt();
|
|
130
|
+
wasInterrupted = true;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
if (message.type === "system") {
|
|
134
|
+
const sysMsg = message;
|
|
135
|
+
if (sysMsg.subtype === "init") {
|
|
136
|
+
resultSessionId = sysMsg.session_id;
|
|
137
|
+
this.currentSessionId = sysMsg.session_id;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (message.type === "assistant") {
|
|
141
|
+
await this.processAssistantMessage(message);
|
|
142
|
+
}
|
|
143
|
+
if (message.type === "result") {
|
|
144
|
+
const resultMsg = message;
|
|
145
|
+
if (resultMsg.subtype === "success" && resultMsg.result) {
|
|
146
|
+
finalResult = resultMsg.result;
|
|
147
|
+
}
|
|
148
|
+
await this.onComplete(resultMsg);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
sessionId: resultSessionId,
|
|
153
|
+
result: finalResult,
|
|
154
|
+
interrupted: wasInterrupted
|
|
155
|
+
};
|
|
156
|
+
} catch (error) {
|
|
157
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
158
|
+
await this.onError(err);
|
|
159
|
+
return {
|
|
160
|
+
sessionId: resultSessionId,
|
|
161
|
+
error: err.message,
|
|
162
|
+
interrupted: wasInterrupted
|
|
163
|
+
};
|
|
164
|
+
} finally {
|
|
165
|
+
this.isRunning = false;
|
|
166
|
+
this.activeQuery = void 0;
|
|
167
|
+
clearInterruptFlag(this.externalSessionId);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Interrupt the currently running agent
|
|
172
|
+
* Returns true if an interrupt was sent, false if no active query
|
|
173
|
+
*/
|
|
174
|
+
async interrupt() {
|
|
175
|
+
if (this.activeQuery) {
|
|
176
|
+
await this.activeQuery.interrupt();
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Stop the agent session
|
|
183
|
+
* Sets interrupt flag and calls onStop callback
|
|
184
|
+
*/
|
|
185
|
+
async stop() {
|
|
186
|
+
setInterruptFlag(this.externalSessionId);
|
|
187
|
+
await this.onStop();
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Process an assistant message and call appropriate event methods
|
|
191
|
+
*/
|
|
192
|
+
async processAssistantMessage(message) {
|
|
193
|
+
const content = message.message.content;
|
|
194
|
+
for (const block of content) {
|
|
195
|
+
if (block.type === "text") {
|
|
196
|
+
await this.onThinking(block);
|
|
197
|
+
}
|
|
198
|
+
if (block.type === "tool_use") {
|
|
199
|
+
if (block.name === "TodoWrite") {
|
|
200
|
+
const input = block.input;
|
|
201
|
+
if (input.todos) {
|
|
202
|
+
await this.onPlanUpdate(input.todos);
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
await this.onToolUse(block);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if agent is currently running
|
|
212
|
+
*/
|
|
213
|
+
isActive() {
|
|
214
|
+
return this.isRunning;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Get current session ID
|
|
218
|
+
*/
|
|
219
|
+
getSessionId() {
|
|
220
|
+
return this.currentSessionId;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// src/linear/agent.ts
|
|
225
|
+
var LinearAgent = class extends Agent {
|
|
226
|
+
linearClient;
|
|
227
|
+
linearSessionId;
|
|
228
|
+
lastThoughtTime = 0;
|
|
229
|
+
thoughtDebounceMs = 1e3;
|
|
230
|
+
// Debounce thoughts to avoid spam
|
|
231
|
+
constructor(config) {
|
|
232
|
+
super(config.linearSessionId);
|
|
233
|
+
this.linearClient = new LinearClient({ accessToken: config.linearAccessToken });
|
|
234
|
+
this.linearSessionId = config.linearSessionId;
|
|
235
|
+
}
|
|
236
|
+
// ============================================
|
|
237
|
+
// Event method implementations
|
|
238
|
+
// ============================================
|
|
239
|
+
/**
|
|
240
|
+
* Called when agent starts
|
|
241
|
+
*/
|
|
242
|
+
async onStart() {
|
|
243
|
+
await this.sendActivity("thought" /* Thought */, "Starting work...", true);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Called when agent is thinking/reasoning
|
|
247
|
+
* Sends as ephemeral thought activity (debounced)
|
|
248
|
+
*/
|
|
249
|
+
async onThinking(block) {
|
|
250
|
+
const now = Date.now();
|
|
251
|
+
if (now - this.lastThoughtTime < this.thoughtDebounceMs) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.lastThoughtTime = now;
|
|
255
|
+
const content = block.text;
|
|
256
|
+
if (content.length < 20) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const truncated = content.length > 500 ? content.substring(0, 500) + "..." : content;
|
|
260
|
+
await this.sendActivity("thought" /* Thought */, truncated, true);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Called when agent uses a tool
|
|
264
|
+
* Sends as action activity
|
|
265
|
+
*/
|
|
266
|
+
async onToolUse(block) {
|
|
267
|
+
const description = this.formatToolDescription(block);
|
|
268
|
+
await this.sendActivity("action" /* Action */, description, false);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Called when agent updates its plan
|
|
272
|
+
* Maps to Linear session plan
|
|
273
|
+
*/
|
|
274
|
+
async onPlanUpdate(steps) {
|
|
275
|
+
if (steps.length === 0) return;
|
|
276
|
+
const linearSteps = steps.map((step) => ({
|
|
277
|
+
content: step.content,
|
|
278
|
+
status: this.mapStatusToLinear(step.status)
|
|
279
|
+
}));
|
|
280
|
+
try {
|
|
281
|
+
await this.linearClient.updateAgentSession(this.linearSessionId, {
|
|
282
|
+
plan: linearSteps
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error("Failed to update Linear plan:", error);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Called when agent completes
|
|
290
|
+
* Sends as final activity
|
|
291
|
+
*/
|
|
292
|
+
async onComplete(message) {
|
|
293
|
+
const text = message.subtype === "success" && message.result ? message.result : "Task completed successfully.";
|
|
294
|
+
await this.sendActivity("final" /* Final */, text, false);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Called when agent errors
|
|
298
|
+
* Sends as error activity
|
|
299
|
+
*/
|
|
300
|
+
async onError(error) {
|
|
301
|
+
await this.sendActivity("error" /* Error */, `Error: ${error.message}`, false);
|
|
302
|
+
}
|
|
303
|
+
async onStop() {
|
|
304
|
+
await this.sendActivity("final" /* Final */, "Session stopped", false);
|
|
305
|
+
}
|
|
306
|
+
// ============================================
|
|
307
|
+
// Helper methods
|
|
308
|
+
// ============================================
|
|
309
|
+
/**
|
|
310
|
+
* Send activity to Linear
|
|
311
|
+
*/
|
|
312
|
+
async sendActivity(type, body, ephemeral) {
|
|
313
|
+
try {
|
|
314
|
+
await this.linearClient.createAgentActivity({
|
|
315
|
+
agentSessionId: this.linearSessionId,
|
|
316
|
+
content: { type, body },
|
|
317
|
+
ephemeral
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error("Failed to send Linear activity:", error);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Format tool use description for display
|
|
325
|
+
*/
|
|
326
|
+
formatToolDescription(block) {
|
|
327
|
+
const { name, input } = block;
|
|
328
|
+
const params = input;
|
|
329
|
+
switch (name) {
|
|
330
|
+
case "Write":
|
|
331
|
+
return `Creating file: ${params.file_path}`;
|
|
332
|
+
case "Edit":
|
|
333
|
+
return `Editing: ${params.file_path}`;
|
|
334
|
+
case "Read":
|
|
335
|
+
return `Reading: ${params.file_path}`;
|
|
336
|
+
case "Bash": {
|
|
337
|
+
const cmd = String(params.command ?? "");
|
|
338
|
+
const desc = params.description ?? cmd.substring(0, 50);
|
|
339
|
+
return `Running: ${desc}`;
|
|
340
|
+
}
|
|
341
|
+
case "Grep":
|
|
342
|
+
return `Searching for: ${params.pattern}`;
|
|
343
|
+
case "Glob":
|
|
344
|
+
return `Finding files: ${params.pattern}`;
|
|
345
|
+
case "Task":
|
|
346
|
+
return `Delegating to: ${params.subagent_type ?? "subagent"}`;
|
|
347
|
+
case "Skill":
|
|
348
|
+
return `Using skill: ${params.skill}`;
|
|
349
|
+
default:
|
|
350
|
+
return `Using ${name}`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Map internal status to Linear plan status
|
|
355
|
+
*/
|
|
356
|
+
mapStatusToLinear(status) {
|
|
357
|
+
switch (status) {
|
|
358
|
+
case "in_progress":
|
|
359
|
+
return "inProgress";
|
|
360
|
+
case "completed":
|
|
361
|
+
return "completed";
|
|
362
|
+
case "canceled":
|
|
363
|
+
return "canceled";
|
|
364
|
+
case "pending":
|
|
365
|
+
default:
|
|
366
|
+
return "pending";
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
var createLinearAgent = (sessionId) => {
|
|
371
|
+
const linearAccessToken = process.env.LINEAR_ACCESS_TOKEN;
|
|
372
|
+
if (!linearAccessToken) {
|
|
373
|
+
throw new Error("Linear access token is missing");
|
|
374
|
+
}
|
|
375
|
+
return new LinearAgent({ linearAccessToken, linearSessionId: sessionId });
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/factory.ts
|
|
379
|
+
function createAgent(platform, sessionId) {
|
|
380
|
+
switch (platform) {
|
|
381
|
+
case "linear":
|
|
382
|
+
return createLinearAgent(sessionId);
|
|
383
|
+
default:
|
|
384
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// src/callback.ts
|
|
389
|
+
async function sendSessionUpdate(callbackUrl, callbackSecret, update) {
|
|
390
|
+
try {
|
|
391
|
+
const response = await fetch(callbackUrl, {
|
|
392
|
+
method: "POST",
|
|
393
|
+
headers: {
|
|
394
|
+
"Content-Type": "application/json",
|
|
395
|
+
"Authorization": `Bearer ${callbackSecret}`
|
|
396
|
+
},
|
|
397
|
+
body: JSON.stringify(update)
|
|
398
|
+
});
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
console.error(`Callback failed: ${response.status} ${response.statusText}`);
|
|
401
|
+
}
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error("Failed to send session update:", error);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
async function reportRunning(callbackUrl, callbackSecret, externalSessionId, claudeSessionId) {
|
|
407
|
+
await sendSessionUpdate(callbackUrl, callbackSecret, {
|
|
408
|
+
externalSessionId,
|
|
409
|
+
claudeSessionId,
|
|
410
|
+
state: "running"
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
async function reportCompleted(callbackUrl, callbackSecret, externalSessionId, claudeSessionId) {
|
|
414
|
+
await sendSessionUpdate(callbackUrl, callbackSecret, {
|
|
415
|
+
externalSessionId,
|
|
416
|
+
claudeSessionId,
|
|
417
|
+
state: "completed"
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
async function reportError(callbackUrl, callbackSecret, externalSessionId, error) {
|
|
421
|
+
await sendSessionUpdate(callbackUrl, callbackSecret, {
|
|
422
|
+
externalSessionId,
|
|
423
|
+
state: "error",
|
|
424
|
+
error
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
async function reportResult(callbackUrl, externalSessionId, result) {
|
|
428
|
+
const callbackSecret = process.env.CLAUDE_LINEAR_CALLBACK_SECRET;
|
|
429
|
+
if (!callbackUrl || !callbackSecret) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (result.sessionId) {
|
|
433
|
+
await reportRunning(callbackUrl, callbackSecret, externalSessionId, result.sessionId);
|
|
434
|
+
}
|
|
435
|
+
if (result.error) {
|
|
436
|
+
await reportError(callbackUrl, callbackSecret, externalSessionId, result.error);
|
|
437
|
+
} else if (!result.interrupted) {
|
|
438
|
+
await reportCompleted(callbackUrl, callbackSecret, externalSessionId, result.sessionId);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// src/commands/start.ts
|
|
443
|
+
var startCommand = new Command("start").description("Start a new agent session").requiredOption("--platform <platform>", "Platform type (linear)").requiredOption("--session-id <id>", "External session ID (e.g., Linear session ID)").requiredOption("--prompt <prompt>", "Initial prompt for the agent").option("--callback-url <url>", "Server callback URL for session updates").option("--working-dir <dir>", "Working directory", process.cwd()).action(async (options) => {
|
|
444
|
+
const { platform, sessionId, prompt, callbackUrl, workingDir } = options;
|
|
445
|
+
try {
|
|
446
|
+
const agent = createAgent(platform, sessionId);
|
|
447
|
+
const result = await agent.run({
|
|
448
|
+
prompt,
|
|
449
|
+
cwd: workingDir,
|
|
450
|
+
options: {
|
|
451
|
+
settingSources: ["user", "project"],
|
|
452
|
+
permissionMode: "bypassPermissions"
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
await reportResult(callbackUrl, sessionId, result);
|
|
456
|
+
console.log(JSON.stringify({
|
|
457
|
+
status: result.error ? "error" : result.interrupted ? "interrupted" : "completed",
|
|
458
|
+
claudeSessionId: result.sessionId,
|
|
459
|
+
result: result.result,
|
|
460
|
+
error: result.error
|
|
461
|
+
}));
|
|
462
|
+
process.exit(result.error ? 1 : 0);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
465
|
+
console.error(JSON.stringify({ status: "error", error: errorMessage }));
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
var start_default = startCommand;
|
|
470
|
+
|
|
471
|
+
// src/commands/stop.ts
|
|
472
|
+
import { Command as Command2 } from "commander";
|
|
473
|
+
var stopCommand = new Command2("stop").description("Stop an agent session gracefully").requiredOption("--platform <platform>", "Platform type (linear)").requiredOption("--session-id <id>", "External session ID (e.g., Linear session ID)").action(async (options) => {
|
|
474
|
+
const { platform, sessionId } = options;
|
|
475
|
+
try {
|
|
476
|
+
const agent = createAgent(platform, sessionId);
|
|
477
|
+
await agent.stop();
|
|
478
|
+
console.log(JSON.stringify({
|
|
479
|
+
status: "stopped",
|
|
480
|
+
sessionId
|
|
481
|
+
}));
|
|
482
|
+
process.exit(0);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
485
|
+
console.error(JSON.stringify({
|
|
486
|
+
status: "error",
|
|
487
|
+
error: errorMessage
|
|
488
|
+
}));
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
// src/commands/resume.ts
|
|
494
|
+
import { Command as Command3 } from "commander";
|
|
495
|
+
var INTERRUPT_WAIT_MS = 500;
|
|
496
|
+
var resumeCommand = new Command3("resume").description("Resume an existing agent session").requiredOption("--platform <platform>", "Platform type (linear)").requiredOption("--session-id <id>", "External session ID (e.g., Linear session ID)").requiredOption("--claude-session-id <id>", "Claude SDK session ID to resume").requiredOption("--prompt <prompt>", "New prompt/user input").option("--callback-url <url>", "Server callback URL for session updates").option("--working-dir <dir>", "Working directory", process.cwd()).action(async (options) => {
|
|
497
|
+
const { platform, sessionId, claudeSessionId, prompt, callbackUrl, workingDir } = options;
|
|
498
|
+
setInterruptFlag(sessionId);
|
|
499
|
+
await new Promise((resolve2) => setTimeout(resolve2, INTERRUPT_WAIT_MS));
|
|
500
|
+
try {
|
|
501
|
+
const agent = createAgent(platform, sessionId);
|
|
502
|
+
const result = await agent.run({
|
|
503
|
+
prompt,
|
|
504
|
+
sessionId: claudeSessionId,
|
|
505
|
+
resumeSession: true,
|
|
506
|
+
forkSession: false,
|
|
507
|
+
cwd: workingDir,
|
|
508
|
+
options: {
|
|
509
|
+
settingSources: ["user", "project"],
|
|
510
|
+
permissionMode: "acceptEdits"
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
await reportResult(callbackUrl, sessionId, result);
|
|
514
|
+
console.log(JSON.stringify({
|
|
515
|
+
status: result.error ? "error" : result.interrupted ? "interrupted" : "completed",
|
|
516
|
+
claudeSessionId: result.sessionId,
|
|
517
|
+
result: result.result,
|
|
518
|
+
error: result.error
|
|
519
|
+
}));
|
|
520
|
+
process.exit(result.error ? 1 : 0);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
523
|
+
console.error(JSON.stringify({ status: "error", error: errorMessage }));
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
// src/commands/init.ts
|
|
529
|
+
import { Command as Command4 } from "commander";
|
|
530
|
+
|
|
531
|
+
// src/config.ts
|
|
532
|
+
import { cpSync, existsSync as existsSync2, mkdirSync } from "fs";
|
|
533
|
+
import { dirname, resolve, join as join2 } from "path";
|
|
534
|
+
import { fileURLToPath } from "url";
|
|
535
|
+
import { homedir } from "os";
|
|
536
|
+
function getBundledConfigPath() {
|
|
537
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
538
|
+
const __dirname = dirname(__filename);
|
|
539
|
+
return resolve(__dirname, "claude-config");
|
|
540
|
+
}
|
|
541
|
+
function getUserClaudeDir() {
|
|
542
|
+
return join2(homedir(), ".claude");
|
|
543
|
+
}
|
|
544
|
+
function installClaudeConfig() {
|
|
545
|
+
const bundledPath = getBundledConfigPath();
|
|
546
|
+
const userClaudeDir = getUserClaudeDir();
|
|
547
|
+
if (!existsSync2(bundledPath)) {
|
|
548
|
+
console.warn("Warning: Bundled claude-config not found. Skipping config installation.");
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (!existsSync2(userClaudeDir)) {
|
|
552
|
+
mkdirSync(userClaudeDir, { recursive: true });
|
|
553
|
+
}
|
|
554
|
+
cpSync(bundledPath, userClaudeDir, { recursive: true, force: true });
|
|
555
|
+
console.log(`Installed Claude configs to ${userClaudeDir}`);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// src/commands/init.ts
|
|
559
|
+
var initCommand = new Command4("init").description("Install Claude configs to ~/.claude/ (MCP servers, plugins, skills)").action(() => {
|
|
560
|
+
console.log("Initializing Claude configs...");
|
|
561
|
+
installClaudeConfig();
|
|
562
|
+
console.log("Done. Claude Agent SDK will now use bundled configs.");
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
// src/index.ts
|
|
566
|
+
var program = new Command5();
|
|
567
|
+
program.name("claude-sandbox-agent").description("CLI for running Claude agents in Vercel sandboxes").version("0.1.0");
|
|
568
|
+
program.addCommand(initCommand);
|
|
569
|
+
program.addCommand(start_default);
|
|
570
|
+
program.addCommand(stopCommand);
|
|
571
|
+
program.addCommand(resumeCommand);
|
|
572
|
+
program.parse();
|
|
573
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/linear/agent.ts","../src/agent.ts","../src/factory.ts","../src/callback.ts","../src/commands/stop.ts","../src/commands/resume.ts","../src/commands/init.ts","../src/config.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport startCommand from './commands/start';\nimport { stopCommand } from './commands/stop';\nimport { resumeCommand } from './commands/resume';\nimport { initCommand } from './commands/init';\n\nconst program = new Command();\n\nprogram\n .name('claude-sandbox-agent')\n .description('CLI for running Claude agents in Vercel sandboxes')\n .version('0.1.0');\n\nprogram.addCommand(initCommand);\nprogram.addCommand(startCommand);\nprogram.addCommand(stopCommand);\nprogram.addCommand(resumeCommand);\n\nprogram.parse();\n","import { Command } from 'commander';\nimport { createAgent, type Platform } from '@/factory';\nimport { reportResult } from '@/callback';\n\nconst startCommand = new Command('start')\n .description('Start a new agent session')\n .requiredOption('--platform <platform>', 'Platform type (linear)')\n .requiredOption('--session-id <id>', 'External session ID (e.g., Linear session ID)')\n .requiredOption('--prompt <prompt>', 'Initial prompt for the agent')\n .option('--callback-url <url>', 'Server callback URL for session updates')\n .option('--working-dir <dir>', 'Working directory', process.cwd())\n .action(async (options) => {\n const { platform, sessionId, prompt, callbackUrl, workingDir } = options;\n\n try {\n const agent = createAgent(platform as Platform, sessionId);\n\n const result = await agent.run({\n prompt,\n cwd: workingDir,\n options: {\n settingSources: ['user', 'project'],\n permissionMode: 'bypassPermissions',\n },\n });\n\n await reportResult(callbackUrl, sessionId, result);\n\n console.log(JSON.stringify({\n status: result.error ? 'error' : (result.interrupted ? 'interrupted' : 'completed'),\n claudeSessionId: result.sessionId,\n result: result.result,\n error: result.error,\n }));\n\n process.exit(result.error ? 1 : 0);\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(JSON.stringify({ status: 'error', error: errorMessage }));\n process.exit(1);\n }\n });\n\nexport default startCommand;\n","import { LinearClient } from '@linear/sdk';\nimport type { SDKResultMessage } from '@anthropic-ai/claude-agent-sdk';\nimport {\n Agent,\n type PlanStep,\n type TextBlock,\n type ToolUseBlock\n} from '@/agent';\n\n/**\n * LinearAgent configuration\n */\nexport interface LinearAgentConfig {\n linearAccessToken: string;\n linearSessionId: string;\n}\n\n/**\n * Linear activity types\n */\nenum LinearActivityType {\n\tThought = 'thought',\n\tAction = 'action',\n\tFinal = 'final',\n\tError = 'error',\n\tAwaitingInput = 'awaiting_input',\n}\n/**\n * LinearAgent\n *\n * Extends Agent to emit events as Linear activities.\n * Overrides event methods to map to Linear's activity system.\n */\nexport class LinearAgent extends Agent {\n private linearClient: LinearClient;\n private linearSessionId: string;\n private lastThoughtTime = 0;\n private thoughtDebounceMs = 1000; // Debounce thoughts to avoid spam\n\n constructor(config: LinearAgentConfig) {\n super(config.linearSessionId);\n this.linearClient = new LinearClient({ accessToken: config.linearAccessToken });\n this.linearSessionId = config.linearSessionId;\n }\n\n // ============================================\n // Event method implementations\n // ============================================\n\n /**\n * Called when agent starts\n */\n protected async onStart(): Promise<void> {\n await this.sendActivity(LinearActivityType.Thought, 'Starting work...', true);\n }\n\n /**\n * Called when agent is thinking/reasoning\n * Sends as ephemeral thought activity (debounced)\n */\n protected async onThinking(block: TextBlock): Promise<void> {\n const now = Date.now();\n\n // Debounce thoughts to avoid spamming Linear\n if (now - this.lastThoughtTime < this.thoughtDebounceMs) {\n return;\n }\n\n this.lastThoughtTime = now;\n\n const content = block.text;\n\n // Skip very short thoughts\n if (content.length < 20) {\n return;\n }\n\n // Truncate long thoughts\n const truncated = content.length > 500\n ? content.substring(0, 500) + '...'\n : content;\n\n await this.sendActivity(LinearActivityType.Thought, truncated, true);\n }\n\n /**\n * Called when agent uses a tool\n * Sends as action activity\n */\n protected async onToolUse(block: ToolUseBlock): Promise<void> {\n const description = this.formatToolDescription(block);\n await this.sendActivity(LinearActivityType.Action, description, false);\n }\n\n /**\n * Called when agent updates its plan\n * Maps to Linear session plan\n */\n protected async onPlanUpdate(steps: PlanStep[]): Promise<void> {\n if (steps.length === 0) return;\n\n // Map to Linear's plan format\n const linearSteps = steps.map(step => ({\n content: step.content,\n status: this.mapStatusToLinear(step.status),\n }));\n\n try {\n await this.linearClient.updateAgentSession(this.linearSessionId, {\n plan: linearSteps,\n });\n } catch (error) {\n console.error('Failed to update Linear plan:', error);\n }\n }\n\n /**\n * Called when agent completes\n * Sends as final activity\n */\n protected async onComplete(message: SDKResultMessage): Promise<void> {\n const text = message.subtype === 'success' && message.result\n ? message.result\n : 'Task completed successfully.';\n await this.sendActivity(LinearActivityType.Final, text, false);\n }\n\n /**\n * Called when agent errors\n * Sends as error activity\n */\n protected async onError(error: Error): Promise<void> {\n await this.sendActivity(LinearActivityType.Error, `Error: ${error.message}`, false);\n }\n\n\tprotected async onStop(): Promise<void> {\n\t\tawait this.sendActivity(LinearActivityType.Final, 'Session stopped', false);\n\t}\n\n // ============================================\n // Helper methods\n // ============================================\n\n /**\n * Send activity to Linear\n */\n private async sendActivity(\n type: LinearActivityType,\n body: string,\n ephemeral: boolean\n ): Promise<void> {\n try {\n await this.linearClient.createAgentActivity({\n agentSessionId: this.linearSessionId,\n content: { type, body },\n ephemeral,\n });\n } catch (error) {\n console.error('Failed to send Linear activity:', error);\n }\n }\n\n /**\n * Format tool use description for display\n */\n private formatToolDescription(block: ToolUseBlock): string {\n const { name, input } = block;\n const params = input as Record<string, unknown>;\n\n switch (name) {\n case 'Write':\n return `Creating file: ${params.file_path}`;\n\n case 'Edit':\n return `Editing: ${params.file_path}`;\n\n case 'Read':\n return `Reading: ${params.file_path}`;\n\n case 'Bash': {\n const cmd = String(params.command ?? '');\n const desc = params.description ?? cmd.substring(0, 50);\n return `Running: ${desc}`;\n }\n\n case 'Grep':\n return `Searching for: ${params.pattern}`;\n\n case 'Glob':\n return `Finding files: ${params.pattern}`;\n\n case 'Task':\n return `Delegating to: ${params.subagent_type ?? 'subagent'}`;\n\n case 'Skill':\n return `Using skill: ${params.skill}`;\n\n default:\n return `Using ${name}`;\n }\n }\n\n /**\n * Map internal status to Linear plan status\n */\n private mapStatusToLinear(\n status: PlanStep['status']\n ): 'pending' | 'inProgress' | 'completed' | 'canceled' {\n switch (status) {\n case 'in_progress':\n return 'inProgress';\n case 'completed':\n return 'completed';\n case 'canceled':\n return 'canceled';\n case 'pending':\n default:\n return 'pending';\n }\n }\n}\n\n\nexport const createLinearAgent = (sessionId: string) => {\n\tconst linearAccessToken = process.env.LINEAR_ACCESS_TOKEN;\n\tif (!linearAccessToken) {\n\t\tthrow new Error('Linear access token is missing');\n\t}\n\treturn new LinearAgent({ linearAccessToken, linearSessionId: sessionId })\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport {\n query,\n type Query,\n type Options,\n type SDKAssistantMessage,\n type SDKResultMessage,\n type SDKSystemMessage,\n} from '@anthropic-ai/claude-agent-sdk';\nimport type { TextBlock, ToolUseBlock } from '@anthropic-ai/sdk/resources/messages';\n\n/**\n * Plan step for tracking progress\n */\nexport interface PlanStep {\n content: string;\n status: 'pending' | 'in_progress' | 'completed' | 'canceled';\n}\n\n/**\n * Agent run options\n */\nexport interface AgentRunOptions {\n prompt: string;\n sessionId?: string; // SDK session ID for resumption\n resumeSession?: boolean; // Whether to resume existing session\n forkSession?: boolean; // Whether to fork when resuming\n cwd?: string; // Working directory\n options?: Partial<Options>; // Additional SDK options\n}\n\n/**\n * Agent run result\n */\nexport interface AgentRunResult {\n sessionId: string;\n result?: string;\n error?: string;\n interrupted?: boolean;\n}\n\n// ============================================\n// Interrupt file utilities\n// ============================================\n\n/**\n * Get the path to the interrupt flag file for a session\n */\nexport function getInterruptFilePath(externalSessionId: string): string {\n return path.join('/tmp', `agent-${externalSessionId}.interrupt`);\n}\n\n/**\n * Check if the interrupt flag is set\n */\nexport function checkInterruptFlag(externalSessionId: string): boolean {\n const filePath = getInterruptFilePath(externalSessionId);\n try {\n if (fs.existsSync(filePath)) {\n const content = fs.readFileSync(filePath, 'utf-8').trim();\n return content === 'true';\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Set the interrupt flag\n */\nexport function setInterruptFlag(externalSessionId: string): void {\n const filePath = getInterruptFilePath(externalSessionId);\n fs.writeFileSync(filePath, 'true', 'utf-8');\n}\n\n/**\n * Clear the interrupt flag\n */\nexport function clearInterruptFlag(externalSessionId: string): void {\n const filePath = getInterruptFilePath(externalSessionId);\n try {\n if (fs.existsSync(filePath)) {\n fs.unlinkSync(filePath);\n }\n } catch {\n // Ignore errors\n }\n}\n\n/**\n * Base Agent class\n *\n * Runs Claude Agent SDK and calls event methods for each message type.\n * Extend this class and override the event methods you need to integrate\n * with different platforms (Linear, Slack, etc.)\n */\nexport abstract class Agent {\n protected currentSessionId?: string;\n protected isRunning = false;\n protected externalSessionId: string;\n private activeQuery?: Query;\n\n constructor(externalSessionId: string) {\n this.externalSessionId = externalSessionId;\n }\n\n // ============================================\n // Event methods - Override in subclasses\n // ============================================\n\n /**\n * Called when agent starts processing\n */\n protected async onStart(): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent emits a thinking/reasoning text\n */\n protected async onThinking(block: TextBlock): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent uses a tool\n */\n protected async onToolUse(block: ToolUseBlock): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent updates its plan (via TodoWrite)\n */\n protected async onPlanUpdate(todos: PlanStep[]): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent completes successfully\n */\n protected async onComplete(message: SDKResultMessage): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent encounters an error\n */\n protected async onError(error: Error): Promise<void> {\n // Override in subclasses\n }\n\n /**\n * Called when agent is stopped via stop command\n */\n protected async onStop(): Promise<void> {\n // Override in subclasses\n }\n\n // ============================================\n // Main execution logic\n // ============================================\n\n /**\n * Run the agent with the given prompt\n */\n public async run(runOptions: AgentRunOptions): Promise<AgentRunResult> {\n const {\n prompt,\n sessionId,\n resumeSession = false,\n forkSession = false,\n cwd = process.cwd(),\n options: additionalOptions = {},\n } = runOptions;\n\n this.isRunning = true;\n let resultSessionId = sessionId ?? '';\n let finalResult: string | undefined;\n let wasInterrupted = false;\n\n // Clear any existing interrupt flag before starting\n clearInterruptFlag(this.externalSessionId);\n\n try {\n await this.onStart();\n\n // Build SDK options\n const sdkOptions: Options = {\n settingSources: ['user', 'project'],\n permissionMode: 'acceptEdits',\n cwd,\n ...additionalOptions,\n };\n\n // Add resume options if resuming\n if (resumeSession && sessionId) {\n sdkOptions.resume = sessionId;\n sdkOptions.forkSession = forkSession;\n }\n\n // Run the agent\n this.activeQuery = query({ prompt, options: sdkOptions });\n for await (const message of this.activeQuery) {\n // Check for interrupt flag\n if (checkInterruptFlag(this.externalSessionId)) {\n console.log('Interrupt flag detected, stopping agent...');\n await this.activeQuery.interrupt();\n wasInterrupted = true;\n break;\n }\n\n // Capture session ID from init message\n if (message.type === 'system') {\n const sysMsg = message as SDKSystemMessage;\n if (sysMsg.subtype === 'init') {\n resultSessionId = sysMsg.session_id;\n this.currentSessionId = sysMsg.session_id;\n }\n }\n\n // Process assistant messages\n if (message.type === 'assistant') {\n await this.processAssistantMessage(message as SDKAssistantMessage);\n }\n\n // Handle result message\n if (message.type === 'result') {\n const resultMsg = message as SDKResultMessage;\n if (resultMsg.subtype === 'success' && resultMsg.result) {\n finalResult = resultMsg.result;\n }\n await this.onComplete(resultMsg);\n }\n }\n\n return {\n sessionId: resultSessionId,\n result: finalResult,\n interrupted: wasInterrupted,\n };\n\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n await this.onError(err);\n\n return {\n sessionId: resultSessionId,\n error: err.message,\n interrupted: wasInterrupted,\n };\n\n } finally {\n this.isRunning = false;\n this.activeQuery = undefined;\n // Clear interrupt flag after we're done\n clearInterruptFlag(this.externalSessionId);\n }\n }\n\n /**\n * Interrupt the currently running agent\n * Returns true if an interrupt was sent, false if no active query\n */\n public async interrupt(): Promise<boolean> {\n if (this.activeQuery) {\n await this.activeQuery.interrupt();\n return true;\n }\n return false;\n }\n\n /**\n * Stop the agent session\n * Sets interrupt flag and calls onStop callback\n */\n public async stop(): Promise<void> {\n setInterruptFlag(this.externalSessionId);\n await this.onStop();\n }\n\n /**\n * Process an assistant message and call appropriate event methods\n */\n private async processAssistantMessage(message: SDKAssistantMessage): Promise<void> {\n const content = message.message.content;\n\n for (const block of content) {\n // Text block -> onThinking\n if (block.type === 'text') {\n await this.onThinking(block);\n }\n\n // Tool use block -> onToolUse or onPlanUpdate\n if (block.type === 'tool_use') {\n // Handle TodoWrite specially - emit as plan update\n if (block.name === 'TodoWrite') {\n const input = block.input as { todos?: PlanStep[] };\n if (input.todos) {\n await this.onPlanUpdate(input.todos);\n }\n } else {\n await this.onToolUse(block);\n }\n }\n }\n }\n\n /**\n * Check if agent is currently running\n */\n public isActive(): boolean {\n return this.isRunning;\n }\n\n /**\n * Get current session ID\n */\n public getSessionId(): string | undefined {\n return this.currentSessionId;\n }\n}\n\nexport { TextBlock, ToolUseBlock }\n","import { Agent } from './agent';\nimport {createLinearAgent, LinearAgent} from './linear/agent';\n\n/**\n * Supported platforms\n */\nexport type Platform = 'linear';\n\n/**\n * Create an agent for the specified platform\n */\nexport function createAgent(platform: Platform, sessionId: string): Agent {\n switch (platform) {\n case 'linear':\n\t\t\treturn createLinearAgent(sessionId)\n default:\n throw new Error(`Unsupported platform: ${platform}`);\n }\n}\n","/**\n * HTTP callback client for updating session state on the server\n */\n\nexport interface SessionUpdate {\n externalSessionId: string;\n claudeSessionId?: string;\n state: 'running' | 'completed' | 'error';\n error?: string;\n}\n\n/**\n * Send a session update to the server callback endpoint\n */\nexport async function sendSessionUpdate(\n callbackUrl: string,\n callbackSecret: string,\n update: SessionUpdate\n): Promise<void> {\n try {\n const response = await fetch(callbackUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${callbackSecret}`,\n },\n body: JSON.stringify(update),\n });\n\n if (!response.ok) {\n console.error(`Callback failed: ${response.status} ${response.statusText}`);\n }\n } catch (error) {\n console.error('Failed to send session update:', error);\n }\n}\n\n/**\n * Update session as running with Claude session ID\n */\nexport async function reportRunning(\n callbackUrl: string,\n callbackSecret: string,\n externalSessionId: string,\n claudeSessionId: string\n): Promise<void> {\n await sendSessionUpdate(callbackUrl, callbackSecret, {\n externalSessionId,\n claudeSessionId,\n state: 'running',\n });\n}\n\n/**\n * Update session as completed\n */\nexport async function reportCompleted(\n callbackUrl: string,\n callbackSecret: string,\n externalSessionId: string,\n claudeSessionId: string\n): Promise<void> {\n await sendSessionUpdate(callbackUrl, callbackSecret, {\n externalSessionId,\n claudeSessionId,\n state: 'completed',\n });\n}\n\n/**\n * Update session as errored\n */\nexport async function reportError(\n callbackUrl: string,\n callbackSecret: string,\n externalSessionId: string,\n error: string\n): Promise<void> {\n await sendSessionUpdate(callbackUrl, callbackSecret, {\n externalSessionId,\n state: 'error',\n error,\n });\n}\n\n/**\n * Agent run result for reporting\n */\nexport interface AgentResult {\n sessionId: string;\n result?: string;\n error?: string;\n interrupted?: boolean;\n}\n\n/**\n * Report agent result to callback server\n * Handles running, completed, error, and interrupted states\n */\nexport async function reportResult(\n callbackUrl: string | undefined,\n externalSessionId: string,\n result: AgentResult\n): Promise<void> {\n const callbackSecret = process.env.CLAUDE_LINEAR_CALLBACK_SECRET;\n\n if (!callbackUrl || !callbackSecret) {\n return;\n }\n\n // Report running status with session ID\n if (result.sessionId) {\n await reportRunning(callbackUrl, callbackSecret, externalSessionId, result.sessionId);\n }\n\n // Report final status\n if (result.error) {\n await reportError(callbackUrl, callbackSecret, externalSessionId, result.error);\n } else if (!result.interrupted) {\n // Only report completed if not interrupted\n await reportCompleted(callbackUrl, callbackSecret, externalSessionId, result.sessionId);\n }\n // If interrupted, don't report - resume will handle it\n}\n","import { Command } from 'commander';\nimport { createAgent, type Platform } from '@/factory';\n\nexport const stopCommand = new Command('stop')\n .description('Stop an agent session gracefully')\n .requiredOption('--platform <platform>', 'Platform type (linear)')\n .requiredOption('--session-id <id>', 'External session ID (e.g., Linear session ID)')\n .action(async (options) => {\n const { platform, sessionId } = options;\n\n try {\n const agent = createAgent(platform as Platform, sessionId);\n await agent.stop();\n\n console.log(JSON.stringify({\n status: 'stopped',\n sessionId,\n }));\n process.exit(0);\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(JSON.stringify({\n status: 'error',\n error: errorMessage,\n }));\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport { createAgent, type Platform } from '@/factory';\nimport { setInterruptFlag } from '@/agent';\nimport { reportResult } from '@/callback';\n\nconst INTERRUPT_WAIT_MS = 500;\n\nexport const resumeCommand = new Command('resume')\n .description('Resume an existing agent session')\n .requiredOption('--platform <platform>', 'Platform type (linear)')\n .requiredOption('--session-id <id>', 'External session ID (e.g., Linear session ID)')\n .requiredOption('--claude-session-id <id>', 'Claude SDK session ID to resume')\n .requiredOption('--prompt <prompt>', 'New prompt/user input')\n .option('--callback-url <url>', 'Server callback URL for session updates')\n .option('--working-dir <dir>', 'Working directory', process.cwd())\n .action(async (options) => {\n const { platform, sessionId, claudeSessionId, prompt, callbackUrl, workingDir } = options;\n\n // Set interrupt flag in case another agent is running\n setInterruptFlag(sessionId);\n await new Promise(resolve => setTimeout(resolve, INTERRUPT_WAIT_MS));\n\n try {\n const agent = createAgent(platform as Platform, sessionId);\n\n const result = await agent.run({\n prompt,\n sessionId: claudeSessionId,\n resumeSession: true,\n forkSession: false,\n cwd: workingDir,\n options: {\n settingSources: ['user', 'project'],\n permissionMode: 'acceptEdits',\n },\n });\n\n await reportResult(callbackUrl, sessionId, result);\n\n console.log(JSON.stringify({\n status: result.error ? 'error' : (result.interrupted ? 'interrupted' : 'completed'),\n claudeSessionId: result.sessionId,\n result: result.result,\n error: result.error,\n }));\n\n process.exit(result.error ? 1 : 0);\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.error(JSON.stringify({ status: 'error', error: errorMessage }));\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport { installClaudeConfig } from '../config';\n\nexport const initCommand = new Command('init')\n .description('Install Claude configs to ~/.claude/ (MCP servers, plugins, skills)')\n .action(() => {\n console.log('Initializing Claude configs...');\n installClaudeConfig();\n console.log('Done. Claude Agent SDK will now use bundled configs.');\n });","import { cpSync, existsSync, mkdirSync } from 'fs';\nimport { dirname, resolve, join } from 'path';\nimport { fileURLToPath } from 'url';\nimport { homedir } from 'os';\n\n/**\n * Get the path to the bundled claude-config directory\n */\nfunction getBundledConfigPath(): string {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n return resolve(__dirname, 'claude-config');\n}\n\n/**\n * Get the user's ~/.claude directory path\n */\nfunction getUserClaudeDir(): string {\n return join(homedir(), '.claude');\n}\n\n/**\n * Install bundled Claude configs to ~/.claude/\n *\n * This copies:\n * - settings.json (plugins, env vars)\n * - .mcp.json (MCP server configurations)\n * - skills/ (custom skills)\n * - agents/ (custom agents, if any)\n *\n * Files are merged/overwritten - existing user configs will be replaced.\n */\nexport function installClaudeConfig(): void {\n const bundledPath = getBundledConfigPath();\n const userClaudeDir = getUserClaudeDir();\n\n if (!existsSync(bundledPath)) {\n console.warn('Warning: Bundled claude-config not found. Skipping config installation.');\n return;\n }\n\n // Ensure ~/.claude exists\n if (!existsSync(userClaudeDir)) {\n mkdirSync(userClaudeDir, { recursive: true });\n }\n\n // Copy bundled configs to user directory\n cpSync(bundledPath, userClaudeDir, { recursive: true, force: true });\n\n console.log(`Installed Claude configs to ${userClaudeDir}`);\n}\n\n/**\n * Check if Claude configs are installed\n */\nexport function isConfigInstalled(): boolean {\n const userClaudeDir = getUserClaudeDir();\n const settingsPath = join(userClaudeDir, 'settings.json');\n return existsSync(settingsPath);\n}"],"mappings":"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","names":["Command","Command","Command","Command","Command","resolve","Command","existsSync","join","Command","Command"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-sandbox-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for running Claude agents in Vercel sandboxes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"claude-sandbox-agent": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude",
|
|
16
|
+
"agent",
|
|
17
|
+
"linear",
|
|
18
|
+
"sandbox",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
|
25
|
+
"@anthropic-ai/sdk": "^0.71.2",
|
|
26
|
+
"@linear/sdk": "^68.1.0",
|
|
27
|
+
"commander": "^12.1.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"tsup": "^8.4.0",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.0.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"typecheck": "tsc --noEmit"
|
|
41
|
+
}
|
|
42
|
+
}
|