claude-sandbox-agent 0.2.4 → 0.2.6
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/_claude-plugin/plugin.json +11 -0
- package/dist/claude-config/skills/doc-drift-check/SKILL.md +140 -0
- package/dist/claude-config/skills/doc-drift-check/references/severity-guide.md +174 -0
- package/dist/index.js +19 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/claude-config/skills/project-lead/SKILL.md +0 -146
- /package/dist/claude-config/{mcp.json → _mcp.json} +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-linear-agent",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Skills and MCP integrations for the Linear AI agent — Linear, GitHub, Notion, and Context7",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Matt Aliev"
|
|
7
|
+
},
|
|
8
|
+
"repository": "https://github.com/mattaliev/claude-linear-agent",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": ["linear", "agent", "mcp", "notion", "github", "context7"]
|
|
11
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Doc Drift Check
|
|
3
|
+
description: This skill should be used when the user asks to "check docs for drift", "compare code changes against documentation", "review docs after PR", "find outdated documentation", "check if docs match the code", "verify documentation is up to date", or after completing a pull request review to verify documentation accuracy. Analyzes code changes against project docs, CLAUDE.md, database schema, Notion module pages, and Notion processes to catch mismatches.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Doc Drift Check
|
|
7
|
+
|
|
8
|
+
Analyze code changes from a PR or issue against all project documentation — both in the repository and in Notion — to catch mismatches that would lead to stale docs. Write a severity-categorized comment to the Linear issue only when meaningful drift is found.
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Invoke manually after reviewing a pull request or completing an issue review. Skip for trivial changes (typo fixes, formatting, dependency bumps) where doc drift is impossible.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
### 1. Gather Context
|
|
17
|
+
|
|
18
|
+
Collect three inputs before analysis:
|
|
19
|
+
|
|
20
|
+
**Code changes** — Get the diff for the issue's PR:
|
|
21
|
+
- Use GitHub MCP to read the PR diff
|
|
22
|
+
- Note which files changed and the nature of changes (new files, modified APIs, removed features, config changes, model changes, service changes)
|
|
23
|
+
|
|
24
|
+
**Linear issue** — Read the issue description and metadata:
|
|
25
|
+
- Issue title, description, and acceptance criteria
|
|
26
|
+
- Team identifier (determines which Notion workspace to check):
|
|
27
|
+
- `KPL-` → Platform
|
|
28
|
+
- `KPE-` → Personal
|
|
29
|
+
- `KBU-` → Business
|
|
30
|
+
- Links to Notion pages or external docs in the description
|
|
31
|
+
|
|
32
|
+
**Repository documentation** — Read relevant local docs:
|
|
33
|
+
- `docs/` directory — scan for files related to changed code areas
|
|
34
|
+
- `CLAUDE.md` — project overview, architecture, env vars, structure
|
|
35
|
+
- Database schema files — check if schema changes are reflected
|
|
36
|
+
|
|
37
|
+
### 2. Identify Relevant Notion Documentation
|
|
38
|
+
|
|
39
|
+
Based on the team identifier from the Linear issue, fetch the corresponding Product Development page in Notion. These pages contain module documentation, processes, and feature documents for each team.
|
|
40
|
+
|
|
41
|
+
**Team → Notion Product Development page mapping:**
|
|
42
|
+
|
|
43
|
+
| Team Prefix | Team | Notion Page ID |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `KPL-` | Platform | `24c80383e1518012a3c2c410588782d5` |
|
|
46
|
+
| `KPE-` | Personal | `23a80383e15180ae91bae32b407ebaf9` |
|
|
47
|
+
| `KBU-` | Business | `24c80383e15180e19e78cfca4b49fe66` |
|
|
48
|
+
|
|
49
|
+
**Scan the Product Development page** for the appropriate team:
|
|
50
|
+
1. Fetch the page and scan its child pages
|
|
51
|
+
2. Identify module pages that relate to the changed code area
|
|
52
|
+
3. Read those module pages — they describe models, services, and APIs for each module
|
|
53
|
+
|
|
54
|
+
**Check the Processes database** (`252cc951ac514d86ad3b6d9fc689409c`):
|
|
55
|
+
1. The Processes database has a "Модули" relation linking processes to modules
|
|
56
|
+
2. If code changes affect a module, find linked processes
|
|
57
|
+
3. Check if process descriptions still match the implementation
|
|
58
|
+
|
|
59
|
+
### 3. Analyze for Mismatches
|
|
60
|
+
|
|
61
|
+
Compare code changes against each relevant documentation source. Look for:
|
|
62
|
+
|
|
63
|
+
- **Model drift** — Code adds/removes/renames model fields, but module documentation in Notion still describes the old schema
|
|
64
|
+
- **Service drift** — New services added or service logic changed, but module page doesn't list them
|
|
65
|
+
- **API drift** — Endpoints added/changed/removed, but docs still reference old API
|
|
66
|
+
- **Process drift** — Implementation changes how a process works, but the process page in Notion doesn't reflect it
|
|
67
|
+
- **Missing documentation** — New module, feature, or config with no corresponding docs
|
|
68
|
+
- **Stale references** — Docs reference removed code, old file paths, or deprecated models
|
|
69
|
+
- **Issue description mismatch** — What was implemented differs from what the issue describes
|
|
70
|
+
- **Structural drift** — File/directory structure changed but project structure docs not updated
|
|
71
|
+
- **Environment drift** — New env vars, config values, or dependencies not documented
|
|
72
|
+
|
|
73
|
+
For detailed severity classification examples, consult `references/severity-guide.md`.
|
|
74
|
+
|
|
75
|
+
### 4. Decision Gate
|
|
76
|
+
|
|
77
|
+
After analysis, decide whether to comment:
|
|
78
|
+
|
|
79
|
+
- **No mismatches found** → Do nothing. Do not write a "no issues found" comment.
|
|
80
|
+
- **Only trivial mismatches** (e.g., a slightly outdated example that doesn't mislead) → Skip.
|
|
81
|
+
- **Meaningful mismatches** → Proceed to write comment.
|
|
82
|
+
|
|
83
|
+
The bar for commenting: would someone reading the docs be misled or confused by the current state? If yes, comment. If not, skip.
|
|
84
|
+
|
|
85
|
+
### 5. Write Linear Comment
|
|
86
|
+
|
|
87
|
+
Post a comment on the Linear issue using this format:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
📋 Doc Drift Report
|
|
91
|
+
|
|
92
|
+
[1-2 sentence summary of what was found]
|
|
93
|
+
|
|
94
|
+
🔴 Critical
|
|
95
|
+
- [Notion: Module "Payments" → Models section] — Code adds TransferFee model but module page doesn't list it
|
|
96
|
+
- [File: `CLAUDE.md` env vars table] — New SYNC_API_KEY env var required but not listed
|
|
97
|
+
|
|
98
|
+
🟡 Moderate
|
|
99
|
+
- [Notion: Process "Customer Onboarding"] — Step 3 now calls ProviderService instead of direct API, process diagram outdated
|
|
100
|
+
- [File: `docs/architecture.md`] — New endpoint POST /api/v2/transfers not documented
|
|
101
|
+
|
|
102
|
+
🟢 Light
|
|
103
|
+
- [Notion: Module "Crypto" → Services section] — CryptoSigningService renamed to AccessKeyService in code
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
Suggested actions:
|
|
107
|
+
- Update Notion module page "Payments" with new TransferFee model
|
|
108
|
+
- Add SYNC_API_KEY to CLAUDE.md environment variables table
|
|
109
|
+
- Update process diagram in "Customer Onboarding" process page
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Formatting rules:**
|
|
113
|
+
- Use severity emojis: 🔴 Critical, 🟡 Moderate, 🟢 Light
|
|
114
|
+
- Prefix with source: `[Notion: ...]` or `[File: ...]`
|
|
115
|
+
- Include specific page/section references
|
|
116
|
+
- Include a "Suggested actions" section with concrete fixes
|
|
117
|
+
- Omit empty severity categories
|
|
118
|
+
|
|
119
|
+
## Severity Classification Quick Reference
|
|
120
|
+
|
|
121
|
+
| Severity | Description |
|
|
122
|
+
|---|---|
|
|
123
|
+
| 🔴 Critical | Model/schema changes undocumented, API changes, env var changes, breaking behavior, architecture shifts — someone would build against wrong assumptions |
|
|
124
|
+
| 🟡 Moderate | New services undocumented, process flow changes, new features without docs, workflow changes — someone would miss important capabilities |
|
|
125
|
+
| 🟢 Light | Renamed entities, outdated examples, minor wording, service method renames — someone might be briefly confused |
|
|
126
|
+
|
|
127
|
+
For detailed examples and edge cases, see `references/severity-guide.md`.
|
|
128
|
+
|
|
129
|
+
## Tools Used
|
|
130
|
+
|
|
131
|
+
- **GitHub MCP** — read PR diffs and changed files
|
|
132
|
+
- **Linear MCP** — read issue description and team, post comments
|
|
133
|
+
- **Notion MCP** — fetch Product Development pages, module pages, process pages
|
|
134
|
+
- **Read, Grep, Glob** — scan local documentation files in the repository
|
|
135
|
+
|
|
136
|
+
## Additional Resources
|
|
137
|
+
|
|
138
|
+
### Reference Files
|
|
139
|
+
|
|
140
|
+
- **`references/severity-guide.md`** — Detailed severity classification with examples, edge cases, and per-source guidance
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Severity Classification Guide
|
|
2
|
+
|
|
3
|
+
Detailed guide for categorizing doc drift findings by severity level. Use this when analyzing mismatches between code changes and documentation.
|
|
4
|
+
|
|
5
|
+
## 🔴 Critical — Would Cause Wrong Assumptions
|
|
6
|
+
|
|
7
|
+
These mismatches would cause someone to build against incorrect assumptions or make wrong decisions.
|
|
8
|
+
|
|
9
|
+
### Model/Schema Changes
|
|
10
|
+
|
|
11
|
+
- Code adds a new model but the Notion module page doesn't list it
|
|
12
|
+
- Code removes or renames a model field, but the module documentation still references the old field
|
|
13
|
+
- Relationship between models changed (e.g., ForeignKey → ManyToMany) but not reflected in docs
|
|
14
|
+
- New required field added to a model but not documented in the module's models section
|
|
15
|
+
|
|
16
|
+
**Example:**
|
|
17
|
+
> Code adds `AccessKey` model to `andgate_crypto` module, but the Crypto module page in Notion under Platform's Product Development doesn't mention it.
|
|
18
|
+
|
|
19
|
+
### API/Endpoint Changes
|
|
20
|
+
|
|
21
|
+
- New endpoint added without documentation
|
|
22
|
+
- Endpoint removed but still listed in docs
|
|
23
|
+
- Request/response format changed but docs show old format
|
|
24
|
+
- Authentication requirements changed
|
|
25
|
+
|
|
26
|
+
### Environment & Configuration
|
|
27
|
+
|
|
28
|
+
- New environment variable required but not in CLAUDE.md or deployment docs
|
|
29
|
+
- Configuration option removed but still documented
|
|
30
|
+
- Default value changed for existing config
|
|
31
|
+
|
|
32
|
+
### Architecture Changes
|
|
33
|
+
|
|
34
|
+
- New module/app created without corresponding Notion documentation
|
|
35
|
+
- Service dependencies changed (module A now depends on module B, but docs don't show this)
|
|
36
|
+
- Database migration adds/removes tables not reflected in schema docs
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🟡 Moderate — Would Miss Important Capabilities
|
|
41
|
+
|
|
42
|
+
These mismatches would cause someone to miss important features or follow outdated procedures.
|
|
43
|
+
|
|
44
|
+
### Service Changes
|
|
45
|
+
|
|
46
|
+
- New service class added to a module but module page doesn't list it in the services section
|
|
47
|
+
- Service method signature changed (different params) but process docs reference old signature
|
|
48
|
+
- Service now calls a different external provider but process diagrams show old flow
|
|
49
|
+
|
|
50
|
+
**Example:**
|
|
51
|
+
> Code adds `AccessKeyService` to handle crypto signing, but the Crypto module page doesn't list it under Services.
|
|
52
|
+
|
|
53
|
+
### Process Flow Changes
|
|
54
|
+
|
|
55
|
+
- Steps in a business process reordered or modified
|
|
56
|
+
- New step added to a process (e.g., new validation step in onboarding)
|
|
57
|
+
- Conditional logic changed (e.g., different handling for B2B vs B2C)
|
|
58
|
+
- Process now has prerequisites that aren't documented
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
> Customer onboarding now requires provider-specific KYC before account creation, but the process page still shows a single KYC step.
|
|
62
|
+
|
|
63
|
+
### Feature Documentation Gaps
|
|
64
|
+
|
|
65
|
+
- New feature implemented but no feature document exists
|
|
66
|
+
- Feature behavior changed significantly but feature docs describe old behavior
|
|
67
|
+
- Feature flag added without documentation in relevant places
|
|
68
|
+
|
|
69
|
+
### Workflow Changes
|
|
70
|
+
|
|
71
|
+
- Webhook handling changed but webhook documentation outdated
|
|
72
|
+
- Background task behavior modified but not reflected in process docs
|
|
73
|
+
- Error handling strategy changed for a module
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🟢 Light — Brief Confusion Only
|
|
78
|
+
|
|
79
|
+
These mismatches would cause someone brief confusion but wouldn't lead to incorrect work.
|
|
80
|
+
|
|
81
|
+
### Naming/Renaming
|
|
82
|
+
|
|
83
|
+
- Service or utility class renamed but docs use old name
|
|
84
|
+
- File moved to different directory but docs reference old path
|
|
85
|
+
- Variable or constant renamed
|
|
86
|
+
|
|
87
|
+
**Example:**
|
|
88
|
+
> `CryptoSigningService` renamed to `AccessKeyService` in code but Notion module page still says `CryptoSigningService`.
|
|
89
|
+
|
|
90
|
+
### Outdated Examples
|
|
91
|
+
|
|
92
|
+
- Code example in docs uses old import path
|
|
93
|
+
- Example shows deprecated API usage but functionally equivalent
|
|
94
|
+
- Test examples reference old fixtures
|
|
95
|
+
|
|
96
|
+
### Minor Wording
|
|
97
|
+
|
|
98
|
+
- Documentation says "will be implemented" for already-implemented features
|
|
99
|
+
- Status markers (TODO, WIP) left in docs for completed work
|
|
100
|
+
- Comments in code reference old Notion page names
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Per-Source Analysis Guide
|
|
105
|
+
|
|
106
|
+
### Notion Module Pages
|
|
107
|
+
|
|
108
|
+
Module pages describe models and services for each module. When checking:
|
|
109
|
+
|
|
110
|
+
1. **Models section** — Compare listed models against actual Django models in the code
|
|
111
|
+
2. **Services section** — Compare listed services against actual service classes
|
|
112
|
+
3. **API section** — Compare listed endpoints against actual URL configs
|
|
113
|
+
4. **Shared models** — Verify cross-module dependencies are still accurate
|
|
114
|
+
5. **Dependency graph** — Check if module interdependencies changed
|
|
115
|
+
|
|
116
|
+
### Notion Processes Database
|
|
117
|
+
|
|
118
|
+
Process pages describe business workflows. When checking:
|
|
119
|
+
|
|
120
|
+
1. **Process steps** — Do the documented steps match the actual code flow?
|
|
121
|
+
2. **Prerequisites** — Are process prerequisites still accurate?
|
|
122
|
+
3. **Provider interactions** — Do provider-specific flows match current implementation?
|
|
123
|
+
4. **Status transitions** — Do documented state machines match code?
|
|
124
|
+
5. **Related modules** — Is the "Модули" relation still correct?
|
|
125
|
+
|
|
126
|
+
### Repository docs/ Directory
|
|
127
|
+
|
|
128
|
+
Local documentation files. When checking:
|
|
129
|
+
|
|
130
|
+
1. **Architecture docs** — Do they reflect current module structure?
|
|
131
|
+
2. **API docs** — Do endpoint descriptions match implementations?
|
|
132
|
+
3. **Setup/deployment docs** — Are instructions still accurate?
|
|
133
|
+
|
|
134
|
+
### CLAUDE.md
|
|
135
|
+
|
|
136
|
+
Project-level documentation. When checking:
|
|
137
|
+
|
|
138
|
+
1. **Environment variables table** — All required vars listed?
|
|
139
|
+
2. **Project structure** — Does the tree match actual directory layout?
|
|
140
|
+
3. **Technology stack** — Any new dependencies or tools?
|
|
141
|
+
4. **Key documentation links** — Do referenced docs still exist?
|
|
142
|
+
|
|
143
|
+
### Database Schema
|
|
144
|
+
|
|
145
|
+
Schema documentation. When checking:
|
|
146
|
+
|
|
147
|
+
1. **Table definitions** — Do they match current migrations?
|
|
148
|
+
2. **Relationships** — Are FK/M2M relations documented correctly?
|
|
149
|
+
3. **Indexes** — Any new indexes not mentioned in schema docs?
|
|
150
|
+
4. **New tables** — Any new migrations creating tables not in schema docs?
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Edge Cases
|
|
155
|
+
|
|
156
|
+
### When NOT to Flag
|
|
157
|
+
|
|
158
|
+
- Code comments that are slightly outdated (not worth a Linear comment)
|
|
159
|
+
- Test file documentation being slightly behind (tests are self-documenting)
|
|
160
|
+
- Third-party library version bumps with no behavior change
|
|
161
|
+
- Formatting-only changes to documentation files
|
|
162
|
+
- Internal refactoring that doesn't change public interfaces or behavior
|
|
163
|
+
|
|
164
|
+
### When to Escalate to Critical
|
|
165
|
+
|
|
166
|
+
Even if a change seems small, escalate if:
|
|
167
|
+
- It affects an external-facing API that developers build against
|
|
168
|
+
- It changes model fields that other services query
|
|
169
|
+
- It modifies environment variables needed for deployment
|
|
170
|
+
- It alters the behavior described in a process that operations follows
|
|
171
|
+
|
|
172
|
+
### Multiple Small Issues
|
|
173
|
+
|
|
174
|
+
If there are many Light issues (5+) concentrated in one documentation area, consider grouping them and elevating to a single Moderate finding with a suggestion to do a documentation review for that area.
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
19
19
|
|
|
20
20
|
// src/config.ts
|
|
21
|
-
import { cpSync, existsSync, mkdirSync,
|
|
21
|
+
import { cpSync, existsSync, mkdirSync, renameSync } from "fs";
|
|
22
22
|
import { dirname, resolve, join } from "path";
|
|
23
23
|
import { fileURLToPath } from "url";
|
|
24
24
|
var PLUGIN_DIRNAME = "agent-plugin";
|
|
@@ -33,25 +33,21 @@ function getPluginPath(cwd = process.cwd()) {
|
|
|
33
33
|
function installClaudeConfig(cwd = process.cwd()) {
|
|
34
34
|
const bundledPath = getBundledConfigPath();
|
|
35
35
|
if (!existsSync(bundledPath)) {
|
|
36
|
-
console.warn("Warning: Bundled claude-config not found. Skipping
|
|
36
|
+
console.warn("Warning: Bundled claude-config not found. Skipping plugin installation.");
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
const pluginDir = getPluginPath(cwd);
|
|
40
40
|
mkdirSync(pluginDir, { recursive: true });
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
if (existsSync(
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
const mcpSrc = join(bundledPath, "mcp.json");
|
|
53
|
-
if (existsSync(mcpSrc)) {
|
|
54
|
-
cpSync(mcpSrc, join(pluginDir, ".mcp.json"));
|
|
41
|
+
cpSync(bundledPath, pluginDir, { recursive: true, force: true });
|
|
42
|
+
const underscorePlugin = join(pluginDir, "_claude-plugin");
|
|
43
|
+
const dotPlugin = join(pluginDir, ".claude-plugin");
|
|
44
|
+
if (existsSync(underscorePlugin)) {
|
|
45
|
+
renameSync(underscorePlugin, dotPlugin);
|
|
46
|
+
}
|
|
47
|
+
const underscoreMcp = join(pluginDir, "_mcp.json");
|
|
48
|
+
const dotMcp = join(pluginDir, ".mcp.json");
|
|
49
|
+
if (existsSync(underscoreMcp)) {
|
|
50
|
+
renameSync(underscoreMcp, dotMcp);
|
|
55
51
|
}
|
|
56
52
|
console.log(`Installed agent plugin to ${pluginDir}`);
|
|
57
53
|
}
|
|
@@ -170,6 +166,8 @@ var Agent = class {
|
|
|
170
166
|
sdkOptions.resume = sessionId;
|
|
171
167
|
sdkOptions.forkSession = forkSession;
|
|
172
168
|
}
|
|
169
|
+
console.log(`[Agent.run] resume=${resumeSession} | sdkResume=${sdkOptions.resume ?? "none"} | pluginPath=${getPluginPath(cwd)}`);
|
|
170
|
+
console.log(`[Agent.run] prompt=${prompt.substring(0, 300)}...`);
|
|
173
171
|
this.activeQuery = query({ prompt, options: sdkOptions });
|
|
174
172
|
for await (const message of this.activeQuery) {
|
|
175
173
|
if (checkInterruptFlag(this.externalSessionId)) {
|
|
@@ -183,6 +181,7 @@ var Agent = class {
|
|
|
183
181
|
if (sysMsg.subtype === "init") {
|
|
184
182
|
resultSessionId = sysMsg.session_id;
|
|
185
183
|
this.currentSessionId = sysMsg.session_id;
|
|
184
|
+
console.log(`[Agent.run] SDK session initialized: ${sysMsg.session_id}`);
|
|
186
185
|
}
|
|
187
186
|
}
|
|
188
187
|
if (message.type === "assistant") {
|
|
@@ -497,6 +496,8 @@ var startCommand = new Command("start").description("Start a new agent session")
|
|
|
497
496
|
process.exit(1);
|
|
498
497
|
}
|
|
499
498
|
try {
|
|
499
|
+
console.log(`[CLI.start] sessionId=${sessionId} | cwd=${workingDir}`);
|
|
500
|
+
console.log(`[CLI.start] prompt=${prompt.substring(0, 300)}...`);
|
|
500
501
|
const agent = createAgent(platform, sessionId);
|
|
501
502
|
const result = await agent.run({
|
|
502
503
|
prompt,
|
|
@@ -552,6 +553,8 @@ var resumeCommand = new Command3("resume").description("Resume an existing agent
|
|
|
552
553
|
setInterruptFlag(sessionId);
|
|
553
554
|
await new Promise((resolve2) => setTimeout(resolve2, INTERRUPT_WAIT_MS));
|
|
554
555
|
try {
|
|
556
|
+
console.log(`[CLI.resume] sessionId=${sessionId} | claudeSessionId=${claudeSessionId} | cwd=${workingDir}`);
|
|
557
|
+
console.log(`[CLI.resume] prompt=${prompt.substring(0, 300)}...`);
|
|
555
558
|
const agent = createAgent(platform, sessionId);
|
|
556
559
|
const result = await agent.run({
|
|
557
560
|
prompt,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/linear/agent.ts","../src/agent.ts","../src/config.ts","../src/factory.ts","../src/callback.ts","../src/commands/stop.ts","../src/commands/resume.ts","../src/commands/init.ts"],"sourcesContent":["import { 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 { LinearClient } from '@linear/sdk';\nimport { createAgent, type Platform } from '@/factory';\nimport { reportResult } from '@/callback';\n\n/**\n * Send activity to Linear for pre-agent logging\n */\nasync function sendLinearActivity(\n sessionId: string,\n type: 'thought' | 'error',\n message: string,\n ephemeral = true\n): Promise<void> {\n const token = process.env.LINEAR_ACCESS_TOKEN;\n if (!token) return;\n\n try {\n const client = new LinearClient({ accessToken: token });\n await client.createAgentActivity({\n agentSessionId: sessionId,\n content: { type, body: message },\n ephemeral,\n });\n } catch {\n // Don't fail if activity sending fails\n }\n}\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 if (!process.env.ANTHROPIC_API_KEY) {\n await sendLinearActivity(sessionId, 'error', 'ANTHROPIC_API_KEY not set', false);\n process.exit(1);\n }\n\n try {\n const agent = createAgent(platform as Platform, sessionId);\n\n const result = await agent.run({\n prompt,\n cwd: workingDir,\n });\n\n if (result.error) {\n await sendLinearActivity(sessionId, 'error', `Agent error: ${result.error}`, false);\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 await sendLinearActivity(sessionId, 'error', `CLI fatal error: ${errorMessage}`, false);\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\nexport interface LinearAgentConfig {\n linearAccessToken: string;\n linearSessionId: string;\n}\n\nenum LinearActivityType {\n\tThought = 'thought',\n\tAction = 'action',\n\tResponse = 'response',\n\tError = 'error',\n\tElicitation = 'elicitation',\n}\n\n/** Tools that indicate exploration/subagent work — always ephemeral */\nconst EPHEMERAL_TOOLS = new Set(['Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch', 'ToolSearch']);\n\nexport class LinearAgent extends Agent {\n private linearClient: LinearClient;\n private linearSessionId: string;\n private lastThoughtTime = 0;\n private thoughtDebounceMs = 1000;\n private isDelegating = false;\n private isCompleted = false;\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 protected async onStart(): Promise<void> {\n await this.sendActivity(LinearActivityType.Thought, 'Starting work...', true);\n }\n\n protected async onThinking(block: TextBlock): Promise<void> {\n if (this.isCompleted) return;\n\n const now = Date.now();\n if (now - this.lastThoughtTime < this.thoughtDebounceMs) return;\n this.lastThoughtTime = now;\n\n const content = block.text;\n if (content.length < 20) return;\n\n const truncated = content.length > 500\n ? content.substring(0, 500) + '...'\n : content;\n\n // Thoughts are always ephemeral\n await this.sendActivity(LinearActivityType.Thought, truncated, true);\n }\n\n protected async onToolUse(block: ToolUseBlock): Promise<void> {\n if (this.isCompleted) return;\n\n const { name } = block;\n\n // Task tool = delegation — mark as delegating\n if (name === 'Task') {\n this.isDelegating = true;\n const subagentType = String((block.input as Record<string, unknown>).subagent_type ?? 'subagent');\n await this.sendActionActivity('Delegating', subagentType, false);\n return;\n }\n\n // Determine if this action should be ephemeral\n const ephemeral = this.isDelegating || EPHEMERAL_TOOLS.has(name);\n\n const { action, parameter } = this.formatToolAction(block);\n await this.sendActionActivity(action, parameter, ephemeral);\n }\n\n protected async onPlanUpdate(steps: PlanStep[]): Promise<void> {\n if (steps.length === 0) return;\n\n // When we get a plan update from the main agent, delegation is over\n this.isDelegating = false;\n\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 protected async onComplete(message: SDKResultMessage): Promise<void> {\n this.isCompleted = true;\n this.isDelegating = false;\n\n const text = message.subtype === 'success' && message.result\n ? message.result\n : 'Task completed successfully.';\n await this.sendActivity(LinearActivityType.Response, text, false);\n }\n\n protected async onError(error: Error): Promise<void> {\n this.isCompleted = true;\n await this.sendActivity(LinearActivityType.Error, `Error: ${error.message}`, false);\n }\n\n\tprotected async onStop(): Promise<void> {\n\t\tthis.isCompleted = true;\n\t\tawait this.sendActivity(LinearActivityType.Response, 'Session stopped', false);\n\t}\n\n // ============================================\n // Helper methods\n // ============================================\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 private async sendActionActivity(\n action: string,\n parameter: string,\n ephemeral: boolean\n ): Promise<void> {\n try {\n await this.linearClient.createAgentActivity({\n agentSessionId: this.linearSessionId,\n content: { type: LinearActivityType.Action, action, parameter },\n ephemeral,\n });\n } catch (error) {\n console.error('Failed to send Linear activity:', error);\n }\n }\n\n private formatToolAction(block: ToolUseBlock): { action: string; parameter: string } {\n const { name, input } = block;\n const params = input as Record<string, unknown>;\n\n switch (name) {\n case 'Write':\n return { action: 'Creating file', parameter: String(params.file_path ?? '') };\n\n case 'Edit':\n return { action: 'Editing file', parameter: String(params.file_path ?? '') };\n\n case 'Read':\n return { action: 'Reading file', parameter: String(params.file_path ?? '') };\n\n case 'Bash': {\n const cmd = String(params.command ?? '');\n return { action: 'Running command', parameter: cmd.length > 80 ? cmd.substring(0, 80) + '...' : cmd };\n }\n\n case 'Grep':\n return { action: 'Searching', parameter: String(params.pattern ?? '') };\n\n case 'Glob':\n return { action: 'Finding files', parameter: String(params.pattern ?? '') };\n\n case 'Task':\n return { action: 'Delegating', parameter: String(params.subagent_type ?? 'subagent') };\n\n case 'Skill':\n return { action: 'Using skill', parameter: String(params.skill ?? '') };\n\n default:\n return { action: name, parameter: JSON.stringify(params).substring(0, 100) };\n }\n }\n\n private mapStatusToLinear(\n status: PlanStep['status']\n ): 'pending' | 'inProgress' | 'completed' | 'canceled' {\n switch (status) {\n case 'in_progress': return 'inProgress';\n case 'completed': return 'completed';\n case 'canceled': return 'canceled';\n case 'pending':\n default: 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\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';\nimport { getPluginPath } from './config.js';\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 // Set env vars before spawning (SDK env option replaces process env)\n process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';\n\n // Build SDK options\n const sdkOptions: Options = {\n settingSources: ['project'],\n permissionMode: 'bypassPermissions',\n allowDangerouslySkipPermissions: true,\n plugins: [{ type: 'local', path: getPluginPath(cwd) }],\n\t systemPrompt: {\n\t\t\t\t\tpreset: 'claude_code',\n\t\t type: 'preset',\n\t },\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 { cpSync, existsSync, mkdirSync, writeFileSync } from 'fs';\nimport { dirname, resolve, join } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst PLUGIN_DIRNAME = 'agent-plugin';\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 plugin path relative to a working directory\n */\nexport function getPluginPath(cwd: string = process.cwd()): string {\n return join(cwd, PLUGIN_DIRNAME);\n}\n\n/**\n * Install bundled configs as a Claude Code plugin in the given directory.\n *\n * Creates <cwd>/agent-plugin/ with:\n * - .claude-plugin/plugin.json (manifest)\n * - .mcp.json (MCP server configurations)\n * - skills/ (custom skills)\n *\n * The plugin is then loaded by the SDK via the `plugins` option.\n */\nexport function installClaudeConfig(cwd: string = process.cwd()): void {\n const bundledPath = getBundledConfigPath();\n\n if (!existsSync(bundledPath)) {\n console.warn('Warning: Bundled claude-config not found. Skipping config installation.');\n return;\n }\n\n const pluginDir = getPluginPath(cwd);\n\n // Create plugin directory\n mkdirSync(pluginDir, { recursive: true });\n\n // Create plugin manifest\n const manifestDir = join(pluginDir, '.claude-plugin');\n mkdirSync(manifestDir, { recursive: true });\n writeFileSync(join(manifestDir, 'plugin.json'), JSON.stringify({\n name: 'claude-linear-agent',\n version: '1.0.0',\n description: 'MCP servers and skills for the Linear agent',\n }, null, 2));\n\n // Copy skills\n const skillsSrc = join(bundledPath, 'skills');\n if (existsSync(skillsSrc)) {\n cpSync(skillsSrc, join(pluginDir, 'skills'), { recursive: true, force: true });\n }\n\n // Copy MCP config (stored as mcp.json to survive npm install, rename to .mcp.json)\n const mcpSrc = join(bundledPath, 'mcp.json');\n if (existsSync(mcpSrc)) {\n cpSync(mcpSrc, join(pluginDir, '.mcp.json'));\n }\n\n console.log(`Installed agent plugin to ${pluginDir}`);\n}\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\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 const body = await response.text().catch(() => '');\n console.error(`[callback] Failed: ${response.status} ${response.statusText} - ${body}`);\n }\n } catch (error) {\n console.error('[callback] Failed to send session update:', error);\n }\n}\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\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\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\nexport interface AgentResult {\n sessionId: string;\n result?: string;\n error?: string;\n interrupted?: boolean;\n}\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 await reportCompleted(callbackUrl, callbackSecret, externalSessionId, result.sessionId);\n }\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 });\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 agent plugin (MCP servers, skills)')\n .action(() => {\n installClaudeConfig();\n });"],"mappings":"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","names":["Command","LinearClient","LinearClient","Command","Command","Command","Command","resolve","Command","Command","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/start.ts","../src/linear/agent.ts","../src/agent.ts","../src/config.ts","../src/factory.ts","../src/callback.ts","../src/commands/stop.ts","../src/commands/resume.ts","../src/commands/init.ts"],"sourcesContent":["import { 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 { LinearClient } from '@linear/sdk';\nimport { createAgent, type Platform } from '@/factory';\nimport { reportResult } from '@/callback';\n\n/**\n * Send activity to Linear for pre-agent logging\n */\nasync function sendLinearActivity(\n sessionId: string,\n type: 'thought' | 'error',\n message: string,\n ephemeral = true\n): Promise<void> {\n const token = process.env.LINEAR_ACCESS_TOKEN;\n if (!token) return;\n\n try {\n const client = new LinearClient({ accessToken: token });\n await client.createAgentActivity({\n agentSessionId: sessionId,\n content: { type, body: message },\n ephemeral,\n });\n } catch {\n // Don't fail if activity sending fails\n }\n}\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 if (!process.env.ANTHROPIC_API_KEY) {\n await sendLinearActivity(sessionId, 'error', 'ANTHROPIC_API_KEY not set', false);\n process.exit(1);\n }\n\n try {\n console.log(`[CLI.start] sessionId=${sessionId} | cwd=${workingDir}`);\n console.log(`[CLI.start] prompt=${prompt.substring(0, 300)}...`);\n\n const agent = createAgent(platform as Platform, sessionId);\n\n const result = await agent.run({\n prompt,\n cwd: workingDir,\n });\n\n if (result.error) {\n await sendLinearActivity(sessionId, 'error', `Agent error: ${result.error}`, false);\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 await sendLinearActivity(sessionId, 'error', `CLI fatal error: ${errorMessage}`, false);\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\nexport interface LinearAgentConfig {\n linearAccessToken: string;\n linearSessionId: string;\n}\n\nenum LinearActivityType {\n\tThought = 'thought',\n\tAction = 'action',\n\tResponse = 'response',\n\tError = 'error',\n\tElicitation = 'elicitation',\n}\n\n/** Tools that indicate exploration/subagent work — always ephemeral */\nconst EPHEMERAL_TOOLS = new Set(['Read', 'Grep', 'Glob', 'WebSearch', 'WebFetch', 'ToolSearch']);\n\nexport class LinearAgent extends Agent {\n private linearClient: LinearClient;\n private linearSessionId: string;\n private lastThoughtTime = 0;\n private thoughtDebounceMs = 1000;\n private isDelegating = false;\n private isCompleted = false;\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 protected async onStart(): Promise<void> {\n await this.sendActivity(LinearActivityType.Thought, 'Starting work...', true);\n }\n\n protected async onThinking(block: TextBlock): Promise<void> {\n if (this.isCompleted) return;\n\n const now = Date.now();\n if (now - this.lastThoughtTime < this.thoughtDebounceMs) return;\n this.lastThoughtTime = now;\n\n const content = block.text;\n if (content.length < 20) return;\n\n const truncated = content.length > 500\n ? content.substring(0, 500) + '...'\n : content;\n\n // Thoughts are always ephemeral\n await this.sendActivity(LinearActivityType.Thought, truncated, true);\n }\n\n protected async onToolUse(block: ToolUseBlock): Promise<void> {\n if (this.isCompleted) return;\n\n const { name } = block;\n\n // Task tool = delegation — mark as delegating\n if (name === 'Task') {\n this.isDelegating = true;\n const subagentType = String((block.input as Record<string, unknown>).subagent_type ?? 'subagent');\n await this.sendActionActivity('Delegating', subagentType, false);\n return;\n }\n\n // Determine if this action should be ephemeral\n const ephemeral = this.isDelegating || EPHEMERAL_TOOLS.has(name);\n\n const { action, parameter } = this.formatToolAction(block);\n await this.sendActionActivity(action, parameter, ephemeral);\n }\n\n protected async onPlanUpdate(steps: PlanStep[]): Promise<void> {\n if (steps.length === 0) return;\n\n // When we get a plan update from the main agent, delegation is over\n this.isDelegating = false;\n\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 protected async onComplete(message: SDKResultMessage): Promise<void> {\n this.isCompleted = true;\n this.isDelegating = false;\n\n const text = message.subtype === 'success' && message.result\n ? message.result\n : 'Task completed successfully.';\n await this.sendActivity(LinearActivityType.Response, text, false);\n }\n\n protected async onError(error: Error): Promise<void> {\n this.isCompleted = true;\n await this.sendActivity(LinearActivityType.Error, `Error: ${error.message}`, false);\n }\n\n\tprotected async onStop(): Promise<void> {\n\t\tthis.isCompleted = true;\n\t\tawait this.sendActivity(LinearActivityType.Response, 'Session stopped', false);\n\t}\n\n // ============================================\n // Helper methods\n // ============================================\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 private async sendActionActivity(\n action: string,\n parameter: string,\n ephemeral: boolean\n ): Promise<void> {\n try {\n await this.linearClient.createAgentActivity({\n agentSessionId: this.linearSessionId,\n content: { type: LinearActivityType.Action, action, parameter },\n ephemeral,\n });\n } catch (error) {\n console.error('Failed to send Linear activity:', error);\n }\n }\n\n private formatToolAction(block: ToolUseBlock): { action: string; parameter: string } {\n const { name, input } = block;\n const params = input as Record<string, unknown>;\n\n switch (name) {\n case 'Write':\n return { action: 'Creating file', parameter: String(params.file_path ?? '') };\n\n case 'Edit':\n return { action: 'Editing file', parameter: String(params.file_path ?? '') };\n\n case 'Read':\n return { action: 'Reading file', parameter: String(params.file_path ?? '') };\n\n case 'Bash': {\n const cmd = String(params.command ?? '');\n return { action: 'Running command', parameter: cmd.length > 80 ? cmd.substring(0, 80) + '...' : cmd };\n }\n\n case 'Grep':\n return { action: 'Searching', parameter: String(params.pattern ?? '') };\n\n case 'Glob':\n return { action: 'Finding files', parameter: String(params.pattern ?? '') };\n\n case 'Task':\n return { action: 'Delegating', parameter: String(params.subagent_type ?? 'subagent') };\n\n case 'Skill':\n return { action: 'Using skill', parameter: String(params.skill ?? '') };\n\n default:\n return { action: name, parameter: JSON.stringify(params).substring(0, 100) };\n }\n }\n\n private mapStatusToLinear(\n status: PlanStep['status']\n ): 'pending' | 'inProgress' | 'completed' | 'canceled' {\n switch (status) {\n case 'in_progress': return 'inProgress';\n case 'completed': return 'completed';\n case 'canceled': return 'canceled';\n case 'pending':\n default: 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\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';\nimport { getPluginPath } from './config.js';\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 // Set env vars before spawning (SDK env option replaces process env)\n process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = '1';\n\n // Build SDK options\n const sdkOptions: Options = {\n settingSources: ['project'],\n permissionMode: 'bypassPermissions',\n allowDangerouslySkipPermissions: true,\n plugins: [{ type: 'local', path: getPluginPath(cwd) }],\n\t systemPrompt: {\n\t\t\t\t\tpreset: 'claude_code',\n\t\t type: 'preset',\n\t },\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 console.log(`[Agent.run] resume=${resumeSession} | sdkResume=${sdkOptions.resume ?? 'none'} | pluginPath=${getPluginPath(cwd)}`);\n console.log(`[Agent.run] prompt=${prompt.substring(0, 300)}...`);\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 console.log(`[Agent.run] SDK session initialized: ${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 { cpSync, existsSync, mkdirSync, renameSync } from 'fs';\nimport { dirname, resolve, join } from 'path';\nimport { fileURLToPath } from 'url';\n\nconst PLUGIN_DIRNAME = 'agent-plugin';\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 plugin path relative to a working directory\n */\nexport function getPluginPath(cwd: string = process.cwd()): string {\n return join(cwd, PLUGIN_DIRNAME);\n}\n\n/**\n * Install the bundled Claude Code plugin to <cwd>/agent-plugin/.\n *\n * The bundled files use underscores instead of dots (npm strips dotfiles).\n * This function copies them and renames back:\n * _claude-plugin/ → .claude-plugin/\n * _mcp.json → .mcp.json\n * skills/ → skills/ (no rename needed)\n */\nexport function installClaudeConfig(cwd: string = process.cwd()): void {\n const bundledPath = getBundledConfigPath();\n\n if (!existsSync(bundledPath)) {\n console.warn('Warning: Bundled claude-config not found. Skipping plugin installation.');\n return;\n }\n\n const pluginDir = getPluginPath(cwd);\n\n // Copy entire bundled directory\n mkdirSync(pluginDir, { recursive: true });\n cpSync(bundledPath, pluginDir, { recursive: true, force: true });\n\n // Restore dotfiles: _claude-plugin → .claude-plugin\n const underscorePlugin = join(pluginDir, '_claude-plugin');\n const dotPlugin = join(pluginDir, '.claude-plugin');\n if (existsSync(underscorePlugin)) {\n renameSync(underscorePlugin, dotPlugin);\n }\n\n // Restore dotfiles: _mcp.json → .mcp.json\n const underscoreMcp = join(pluginDir, '_mcp.json');\n const dotMcp = join(pluginDir, '.mcp.json');\n if (existsSync(underscoreMcp)) {\n renameSync(underscoreMcp, dotMcp);\n }\n\n console.log(`Installed agent plugin to ${pluginDir}`);\n}\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\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 const body = await response.text().catch(() => '');\n console.error(`[callback] Failed: ${response.status} ${response.statusText} - ${body}`);\n }\n } catch (error) {\n console.error('[callback] Failed to send session update:', error);\n }\n}\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\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\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\nexport interface AgentResult {\n sessionId: string;\n result?: string;\n error?: string;\n interrupted?: boolean;\n}\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 await reportCompleted(callbackUrl, callbackSecret, externalSessionId, result.sessionId);\n }\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 console.log(`[CLI.resume] sessionId=${sessionId} | claudeSessionId=${claudeSessionId} | cwd=${workingDir}`);\n console.log(`[CLI.resume] prompt=${prompt.substring(0, 300)}...`);\n\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 });\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 agent plugin (MCP servers, skills)')\n .action(() => {\n installClaudeConfig();\n });"],"mappings":"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","names":["Command","LinearClient","LinearClient","Command","Command","Command","Command","resolve","Command","Command","Command"]}
|
package/package.json
CHANGED
|
@@ -1,146 +0,0 @@
|
|
|
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
|
|
File without changes
|