@pleaseai/work 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -59
- package/dist/index.js +135 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,13 +36,13 @@ instead of supervising coding agents.
|
|
|
36
36
|
|
|
37
37
|
Work Please is a long-running TypeScript service that:
|
|
38
38
|
|
|
39
|
-
1. Polls an issue tracker (
|
|
39
|
+
1. Polls an issue tracker (GitHub Projects v2 or Asana) for tasks in configured active states.
|
|
40
40
|
2. Creates an isolated workspace directory for each eligible issue.
|
|
41
41
|
3. Launches a Claude Code agent session inside that workspace with a rendered prompt.
|
|
42
42
|
4. Monitors the session, handles retries, and reconciles issue state on each poll cycle.
|
|
43
43
|
|
|
44
44
|
It is a TypeScript implementation of the [Symphony specification](vendor/symphony/SPEC.md),
|
|
45
|
-
adapted for
|
|
45
|
+
adapted for GitHub Projects v2 / Asana and Claude Code instead of Linear and Codex.
|
|
46
46
|
|
|
47
47
|
For full technical details, see [SPEC.md](SPEC.md).
|
|
48
48
|
|
|
@@ -50,18 +50,18 @@ For full technical details, see [SPEC.md](SPEC.md).
|
|
|
50
50
|
|
|
51
51
|
| | Symphony (reference) | Work Please |
|
|
52
52
|
|---|---|---|
|
|
53
|
-
| Issue Tracker | Linear |
|
|
53
|
+
| Issue Tracker | Linear | GitHub Projects v2 & Asana (under development) |
|
|
54
54
|
| Coding Agent | Codex (app-server mode) | Claude Code CLI |
|
|
55
55
|
| Language | Elixir/OTP | TypeScript + Bun |
|
|
56
|
-
| Tracker Auth | `LINEAR_API_KEY` | `
|
|
57
|
-
| Project Config | `project_slug` | `
|
|
58
|
-
| Issue States | Linear workflow states |
|
|
56
|
+
| Tracker Auth | `LINEAR_API_KEY` | `GITHUB_TOKEN`, GitHub App credentials, or `ASANA_ACCESS_TOKEN` |
|
|
57
|
+
| Project Config | `project_slug` | `owner` + `project_number` (GitHub Projects v2) or `project_gid` (Asana) |
|
|
58
|
+
| Issue States | Linear workflow states | GitHub Projects v2 Status field / Asana sections |
|
|
59
59
|
| Agent Protocol | JSON-RPC over stdio | `@anthropic-ai/claude-agent-sdk` |
|
|
60
60
|
| Permission Model | Codex approval/sandbox policies | Claude Code `--permission-mode` |
|
|
61
61
|
|
|
62
62
|
## Features
|
|
63
63
|
|
|
64
|
-
- **Multi-tracker support** — Dispatch work from
|
|
64
|
+
- **Multi-tracker support** — Dispatch work from GitHub Projects v2 items or Asana tasks (under development) on a
|
|
65
65
|
fixed cadence.
|
|
66
66
|
- **GitHub App authentication** — Authenticate the GitHub tracker with a GitHub App installation
|
|
67
67
|
token (`app_id` + `private_key` + `installation_id`) instead of a PAT, for fine-grained
|
|
@@ -90,8 +90,8 @@ Config Layer ──> Orchestrator ──> Workspace Manager ──> Agent Runner
|
|
|
90
90
|
| |
|
|
91
91
|
v v
|
|
92
92
|
Issue Tracker Client Isolated workspace/
|
|
93
|
-
(
|
|
94
|
-
|
|
93
|
+
(GitHub GraphQL API or per-issue directory
|
|
94
|
+
Asana REST API,
|
|
95
95
|
polling + reconciliation)
|
|
96
96
|
|
|
|
97
97
|
v
|
|
@@ -103,7 +103,7 @@ Components:
|
|
|
103
103
|
- **Workflow Loader** — Parses `WORKFLOW.md` YAML front matter and prompt template body.
|
|
104
104
|
- **Config Layer** — Typed getters with env-var indirection and built-in defaults.
|
|
105
105
|
- **Issue Tracker Client** — Fetches candidate issues, reconciles running-issue states. Supports
|
|
106
|
-
|
|
106
|
+
GitHub Projects v2 (GraphQL API) and Asana (REST API) adapters.
|
|
107
107
|
- **Orchestrator** — Owns in-memory state; drives the poll/dispatch/retry loop.
|
|
108
108
|
- **Workspace Manager** — Creates, reuses, and cleans per-issue workspaces; runs hooks.
|
|
109
109
|
- **Agent Runner** — Launches Claude Code, streams events back to the orchestrator.
|
|
@@ -117,9 +117,9 @@ See [SPEC.md](SPEC.md) for the full specification.
|
|
|
117
117
|
|
|
118
118
|
- **Bun** (see [bun.sh](https://bun.sh) for installation)
|
|
119
119
|
- **Claude Code CLI** (see the [official installation guide](https://docs.anthropic.com/en/docs/claude-code))
|
|
120
|
-
- **
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
- **GitHub token** (`GITHUB_TOKEN`) with access to the target project, **or** **GitHub App credentials**
|
|
121
|
+
(`GITHUB_APP_ID`, `GITHUB_APP_PRIVATE_KEY`, `GITHUB_APP_INSTALLATION_ID`) — see [GitHub App Authentication](#github-app-authentication),
|
|
122
|
+
**or** **Asana access token** (`ASANA_ACCESS_TOKEN`) (under development)
|
|
123
123
|
|
|
124
124
|
### Install
|
|
125
125
|
|
|
@@ -133,18 +133,22 @@ bun run build
|
|
|
133
133
|
### Configure
|
|
134
134
|
|
|
135
135
|
Create a `WORKFLOW.md` in your target repository. Two examples are shown below.
|
|
136
|
+
See also the [example WORKFLOW.md](https://github.com/pleaseai/workflow/blob/main/WORKFLOW.md) for a real-world reference.
|
|
136
137
|
|
|
137
|
-
####
|
|
138
|
+
#### GitHub Projects v2 (PAT)
|
|
139
|
+
|
|
140
|
+
See also the [example GitHub Project](https://github.com/orgs/pleaseai/projects/2) for a real-world reference.
|
|
138
141
|
|
|
139
142
|
```markdown
|
|
140
143
|
---
|
|
141
144
|
tracker:
|
|
142
|
-
kind:
|
|
143
|
-
api_key: $
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
kind: github_projects
|
|
146
|
+
api_key: $GITHUB_TOKEN
|
|
147
|
+
owner: your-org
|
|
148
|
+
project_number: 42
|
|
149
|
+
active_statuses:
|
|
146
150
|
- In Progress
|
|
147
|
-
|
|
151
|
+
terminal_statuses:
|
|
148
152
|
- Done
|
|
149
153
|
- Cancelled
|
|
150
154
|
|
|
@@ -165,12 +169,13 @@ agent:
|
|
|
165
169
|
|
|
166
170
|
claude:
|
|
167
171
|
permission_mode: acceptEdits
|
|
172
|
+
# setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
|
|
168
173
|
turn_timeout_ms: 3600000
|
|
169
174
|
---
|
|
170
175
|
|
|
171
|
-
You are working on
|
|
176
|
+
You are working on a GitHub issue for the repository `your-org/your-repo`.
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
Issue {{ issue.identifier }}: {{ issue.title }}
|
|
174
179
|
|
|
175
180
|
{{ issue.description }}
|
|
176
181
|
|
|
@@ -186,19 +191,23 @@ This is attempt #{{ attempt }}. Review any prior work in the workspace before co
|
|
|
186
191
|
{% endif %}
|
|
187
192
|
|
|
188
193
|
Your task:
|
|
189
|
-
1. Understand the
|
|
194
|
+
1. Understand the issue requirements.
|
|
190
195
|
2. Implement the requested changes.
|
|
191
196
|
3. Write or update tests as needed.
|
|
192
|
-
4. Open a pull request and move this
|
|
197
|
+
4. Open a pull request and move this issue to the review status.
|
|
193
198
|
```
|
|
194
199
|
|
|
195
|
-
#### GitHub Projects v2 (
|
|
200
|
+
#### GitHub Projects v2 (GitHub App)
|
|
201
|
+
|
|
202
|
+
Use GitHub App credentials instead of a PAT for fine-grained permissions and higher API rate limits:
|
|
196
203
|
|
|
197
204
|
```markdown
|
|
198
205
|
---
|
|
199
206
|
tracker:
|
|
200
207
|
kind: github_projects
|
|
201
|
-
|
|
208
|
+
app_id: $GITHUB_APP_ID
|
|
209
|
+
private_key: $GITHUB_APP_PRIVATE_KEY
|
|
210
|
+
installation_id: $GITHUB_APP_INSTALLATION_ID
|
|
202
211
|
owner: your-org
|
|
203
212
|
project_number: 42
|
|
204
213
|
active_statuses:
|
|
@@ -224,6 +233,7 @@ agent:
|
|
|
224
233
|
|
|
225
234
|
claude:
|
|
226
235
|
permission_mode: acceptEdits
|
|
236
|
+
# setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
|
|
227
237
|
turn_timeout_ms: 3600000
|
|
228
238
|
---
|
|
229
239
|
|
|
@@ -251,22 +261,19 @@ Your task:
|
|
|
251
261
|
4. Open a pull request and move this issue to the review status.
|
|
252
262
|
```
|
|
253
263
|
|
|
254
|
-
####
|
|
264
|
+
#### Asana (under development)
|
|
255
265
|
|
|
256
|
-
|
|
266
|
+
> **Note**: Asana support is under development. The configuration below is a preview and may change.
|
|
257
267
|
|
|
258
268
|
```markdown
|
|
259
269
|
---
|
|
260
270
|
tracker:
|
|
261
|
-
kind:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
owner: your-org
|
|
266
|
-
project_number: 42
|
|
267
|
-
active_statuses:
|
|
271
|
+
kind: asana
|
|
272
|
+
api_key: $ASANA_ACCESS_TOKEN
|
|
273
|
+
project_gid: "1234567890123456"
|
|
274
|
+
active_sections:
|
|
268
275
|
- In Progress
|
|
269
|
-
|
|
276
|
+
terminal_sections:
|
|
270
277
|
- Done
|
|
271
278
|
- Cancelled
|
|
272
279
|
|
|
@@ -287,27 +294,45 @@ agent:
|
|
|
287
294
|
|
|
288
295
|
claude:
|
|
289
296
|
permission_mode: acceptEdits
|
|
297
|
+
# setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
|
|
290
298
|
turn_timeout_ms: 3600000
|
|
291
299
|
---
|
|
292
300
|
|
|
293
|
-
You are working on
|
|
301
|
+
You are working on an Asana task for the project.
|
|
294
302
|
|
|
295
|
-
|
|
303
|
+
Task: {{ issue.title }}
|
|
296
304
|
|
|
297
305
|
{{ issue.description }}
|
|
306
|
+
|
|
307
|
+
{% if issue.blocked_by.size > 0 %}
|
|
308
|
+
Blocked by:
|
|
309
|
+
{% for blocker in issue.blocked_by %}
|
|
310
|
+
- {{ blocker.identifier }} ({{ blocker.state }})
|
|
311
|
+
{% endfor %}
|
|
312
|
+
{% endif %}
|
|
313
|
+
|
|
314
|
+
{% if attempt %}
|
|
315
|
+
This is attempt #{{ attempt }}. Review any prior work in the workspace before continuing.
|
|
316
|
+
{% endif %}
|
|
317
|
+
|
|
318
|
+
Your task:
|
|
319
|
+
1. Understand the task requirements.
|
|
320
|
+
2. Implement the requested changes.
|
|
321
|
+
3. Write or update tests as needed.
|
|
322
|
+
4. Open a pull request and move this task to the review section.
|
|
298
323
|
```
|
|
299
324
|
|
|
300
325
|
### Run
|
|
301
326
|
|
|
302
327
|
```bash
|
|
303
|
-
# Set your tracker token
|
|
304
|
-
export ASANA_ACCESS_TOKEN=your_token_here
|
|
305
|
-
# or (GitHub PAT)
|
|
328
|
+
# Set your tracker token (GitHub PAT)
|
|
306
329
|
export GITHUB_TOKEN=ghp_your_token_here
|
|
307
330
|
# or (GitHub App)
|
|
308
331
|
export GITHUB_APP_ID=12345
|
|
309
332
|
export GITHUB_APP_PRIVATE_KEY="$(cat path/to/private-key.pem)"
|
|
310
333
|
export GITHUB_APP_INSTALLATION_ID=67890
|
|
334
|
+
# or (Asana — under development)
|
|
335
|
+
export ASANA_ACCESS_TOKEN=your_token_here
|
|
311
336
|
|
|
312
337
|
# Run Work Please against a WORKFLOW.md in the current directory
|
|
313
338
|
bunx work-please
|
|
@@ -329,34 +354,34 @@ front matter configuration block with a Markdown prompt template body.
|
|
|
329
354
|
```yaml
|
|
330
355
|
---
|
|
331
356
|
tracker:
|
|
332
|
-
kind:
|
|
357
|
+
kind: github_projects # Required: "github_projects" or "asana"
|
|
333
358
|
|
|
334
|
-
# ---
|
|
335
|
-
api_key: $
|
|
336
|
-
endpoint: https://
|
|
337
|
-
|
|
338
|
-
|
|
359
|
+
# --- GitHub Projects v2 fields (when kind == "github_projects") ---
|
|
360
|
+
api_key: $GITHUB_TOKEN # Required: token or $ENV_VAR
|
|
361
|
+
endpoint: https://api.github.com # Optional: override GitHub API base URL
|
|
362
|
+
owner: your-org # Required: GitHub organization or user login
|
|
363
|
+
project_number: 42 # Required: GitHub Projects v2 project number
|
|
364
|
+
project_id: PVT_kwDOxxxxx # Optional: project node ID (bypasses owner+project_number lookup)
|
|
365
|
+
active_statuses: # Optional: default ["Todo", "In Progress"]
|
|
339
366
|
- In Progress
|
|
340
|
-
|
|
367
|
+
terminal_statuses: # Optional: default ["Done", "Cancelled"]
|
|
341
368
|
- Done
|
|
342
369
|
- Cancelled
|
|
343
|
-
|
|
344
|
-
# --- GitHub Projects v2 fields (when kind == "github_projects") ---
|
|
345
|
-
# api_key: $GITHUB_TOKEN # Required: token or $ENV_VAR
|
|
346
|
-
# endpoint: https://api.github.com # Optional: override GitHub API base URL
|
|
347
|
-
# owner: your-org # Required: GitHub organization or user login
|
|
348
|
-
# project_number: 42 # Required: GitHub Projects v2 project number
|
|
349
|
-
# project_id: PVT_kwDOxxxxx # Optional: project node ID (bypasses owner+project_number lookup)
|
|
350
|
-
# active_statuses: # Optional: default ["Todo", "In Progress"]
|
|
351
|
-
# - In Progress
|
|
352
|
-
# terminal_statuses: # Optional: default ["Done", "Cancelled"]
|
|
353
|
-
# - Done
|
|
354
|
-
# - Cancelled
|
|
355
370
|
# GitHub App authentication (alternative to api_key — all three required together):
|
|
356
371
|
# app_id: $GITHUB_APP_ID # Optional: GitHub App ID (integer or $ENV_VAR)
|
|
357
372
|
# private_key: $GITHUB_APP_PRIVATE_KEY # Optional: GitHub App private key PEM or $ENV_VAR
|
|
358
373
|
# installation_id: $GITHUB_APP_INSTALLATION_ID # Optional: installation ID (integer or $ENV_VAR)
|
|
359
374
|
|
|
375
|
+
# --- Asana fields (when kind == "asana") --- UNDER DEVELOPMENT
|
|
376
|
+
# api_key: $ASANA_ACCESS_TOKEN # Required: token or $ENV_VAR
|
|
377
|
+
# endpoint: https://app.asana.com/api/1.0 # Optional: override Asana API base URL
|
|
378
|
+
# project_gid: "1234567890123456" # Required: Asana project GID
|
|
379
|
+
# active_sections: # Optional: default ["To Do", "In Progress"]
|
|
380
|
+
# - In Progress
|
|
381
|
+
# terminal_sections: # Optional: default ["Done", "Cancelled"]
|
|
382
|
+
# - Done
|
|
383
|
+
# - Cancelled
|
|
384
|
+
|
|
360
385
|
# --- Shared filter fields (both trackers) ---
|
|
361
386
|
# filter:
|
|
362
387
|
# assignee: user1, user2 # Optional: CSV or YAML array; case-insensitive OR match
|
|
@@ -389,14 +414,24 @@ agent:
|
|
|
389
414
|
|
|
390
415
|
claude:
|
|
391
416
|
command: claude # Optional: Claude Code CLI command, default "claude"
|
|
417
|
+
effort: high # Optional: reasoning depth — 'low', 'medium', 'high', or 'max'. Default 'high'.
|
|
392
418
|
permission_mode: acceptEdits # Optional: one of 'default', 'acceptEdits', 'bypassPermissions'. Defaults to 'bypassPermissions'.
|
|
393
419
|
allowed_tools: # Optional: restrict available tools
|
|
394
420
|
- Read
|
|
395
421
|
- Write
|
|
396
422
|
- Bash
|
|
423
|
+
setting_sources: # Optional: filesystem settings to load. Default: [project, local, user]
|
|
424
|
+
- project # load .claude/settings.json + CLAUDE.md from the workspace directory
|
|
425
|
+
- local # load .claude/settings.local.json from the workspace directory
|
|
426
|
+
- user # load ~/.claude/settings.json + global CLAUDE.md
|
|
427
|
+
# Only "project", "local", and "user" are valid — other values are ignored
|
|
397
428
|
turn_timeout_ms: 3600000 # Optional: per-turn timeout in ms, default 3600000
|
|
398
429
|
read_timeout_ms: 5000 # Optional: initial subprocess read timeout in ms, default 5000
|
|
399
430
|
stall_timeout_ms: 300000 # Optional: stall detection timeout, default 300000
|
|
431
|
+
settings:
|
|
432
|
+
attribution:
|
|
433
|
+
commit: "🙏 Generated with [Work Please](https://github.com/pleaseai/work-please)" # Optional: appended to git commit messages. Defaults to Work Please link.
|
|
434
|
+
pr: "🙏 Generated with [Work Please](https://github.com/pleaseai/work-please)" # Optional: appended to PR descriptions. Defaults to Work Please link.
|
|
400
435
|
|
|
401
436
|
server:
|
|
402
437
|
port: 3000 # Optional: enable HTTP dashboard on this port
|
package/dist/index.js
CHANGED
|
@@ -2166,7 +2166,7 @@ var {
|
|
|
2166
2166
|
var package_default = {
|
|
2167
2167
|
name: "@pleaseai/work",
|
|
2168
2168
|
type: "module",
|
|
2169
|
-
version: "0.1.
|
|
2169
|
+
version: "0.1.7",
|
|
2170
2170
|
description: "Symphony-spec orchestrator for Claude Code + Asana/GitHub Projects v2",
|
|
2171
2171
|
license: "FSL-1.1-MIT",
|
|
2172
2172
|
repository: {
|
|
@@ -3088,6 +3088,13 @@ agent:
|
|
|
3088
3088
|
max_turns: 20
|
|
3089
3089
|
claude:
|
|
3090
3090
|
permission_mode: bypassPermissions
|
|
3091
|
+
# claude.settings controls the attribution text written into .claude/settings.local.json
|
|
3092
|
+
# of each workspace. Omit to use the default Work Please attribution.
|
|
3093
|
+
# claude:
|
|
3094
|
+
# settings:
|
|
3095
|
+
# attribution:
|
|
3096
|
+
# commit: "\uD83D\uDE4F Generated with Work Please"
|
|
3097
|
+
# pr: "\uD83D\uDE4F Generated with Work Please"
|
|
3091
3098
|
# server:
|
|
3092
3099
|
# port: 3000
|
|
3093
3100
|
---
|
|
@@ -31679,7 +31686,11 @@ function createToolsMcpServer(config2) {
|
|
|
31679
31686
|
}
|
|
31680
31687
|
|
|
31681
31688
|
// src/agent-runner.ts
|
|
31689
|
+
var UUID_PATTERN = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/i;
|
|
31690
|
+
var NEWLINE_PATTERN = /[\r\n]/g;
|
|
31691
|
+
|
|
31682
31692
|
class AppServerClient {
|
|
31693
|
+
assignedSessionId = null;
|
|
31683
31694
|
sessionId = null;
|
|
31684
31695
|
abortController = null;
|
|
31685
31696
|
workspace;
|
|
@@ -31690,11 +31701,20 @@ class AppServerClient {
|
|
|
31690
31701
|
this.workspace = workspace;
|
|
31691
31702
|
this.queryFn = queryFn;
|
|
31692
31703
|
}
|
|
31693
|
-
async startSession() {
|
|
31704
|
+
async startSession(sessionId) {
|
|
31705
|
+
this.assignedSessionId = null;
|
|
31706
|
+
this.sessionId = null;
|
|
31707
|
+
if (sessionId !== undefined && !UUID_PATTERN.test(sessionId)) {
|
|
31708
|
+
const preview = String(sessionId).slice(0, 64).replace(NEWLINE_PATTERN, " ");
|
|
31709
|
+
return new Error(`invalid_session_id: expected UUID format, got "${preview}"`);
|
|
31710
|
+
}
|
|
31694
31711
|
const validationErr = this.validateWorkspaceCwd();
|
|
31695
31712
|
if (validationErr)
|
|
31696
31713
|
return validationErr;
|
|
31697
|
-
|
|
31714
|
+
const id = sessionId ?? randomUUID();
|
|
31715
|
+
this.assignedSessionId = id;
|
|
31716
|
+
this.sessionId = sessionId ?? null;
|
|
31717
|
+
return { sessionId: id, workspace: this.workspace };
|
|
31698
31718
|
}
|
|
31699
31719
|
async runTurn(session, prompt, _issue, onMessage) {
|
|
31700
31720
|
const controller = new AbortController;
|
|
@@ -31713,6 +31733,8 @@ class AppServerClient {
|
|
|
31713
31733
|
}
|
|
31714
31734
|
if (this.sessionId) {
|
|
31715
31735
|
options.resume = this.sessionId;
|
|
31736
|
+
} else if (this.assignedSessionId) {
|
|
31737
|
+
options.sessionId = this.assignedSessionId;
|
|
31716
31738
|
}
|
|
31717
31739
|
if (this.config.claude.command !== "claude") {
|
|
31718
31740
|
options.pathToClaudeCodeExecutable = this.config.claude.command;
|
|
@@ -31720,12 +31742,22 @@ class AppServerClient {
|
|
|
31720
31742
|
if (this.config.claude.model) {
|
|
31721
31743
|
options.model = this.config.claude.model;
|
|
31722
31744
|
}
|
|
31745
|
+
const sp = this.config.claude.system_prompt;
|
|
31746
|
+
if (sp.type === "custom") {
|
|
31747
|
+
options.systemPrompt = sp.value;
|
|
31748
|
+
} else {
|
|
31749
|
+
options.systemPrompt = sp;
|
|
31750
|
+
}
|
|
31751
|
+
options.effort = this.config.claude.effort;
|
|
31723
31752
|
const toolSpecs = getToolSpecs(this.config);
|
|
31724
31753
|
if (toolSpecs.length > 0) {
|
|
31725
31754
|
options.mcpServers = {
|
|
31726
31755
|
"work-please-tools": createToolsMcpServer(this.config)
|
|
31727
31756
|
};
|
|
31728
31757
|
}
|
|
31758
|
+
if (this.config.claude.setting_sources.length > 0) {
|
|
31759
|
+
options.settingSources = this.config.claude.setting_sources;
|
|
31760
|
+
}
|
|
31729
31761
|
const turnId = randomUUID();
|
|
31730
31762
|
let sessionId = null;
|
|
31731
31763
|
let gotError = false;
|
|
@@ -31737,11 +31769,11 @@ class AppServerClient {
|
|
|
31737
31769
|
const initMsg = msg;
|
|
31738
31770
|
sessionId = initMsg.session_id;
|
|
31739
31771
|
this.sessionId = sessionId;
|
|
31772
|
+
this.assignedSessionId = null;
|
|
31740
31773
|
onMessage({
|
|
31741
31774
|
event: "session_started",
|
|
31742
31775
|
timestamp: new Date,
|
|
31743
31776
|
session_id: sessionId,
|
|
31744
|
-
thread_id: session.threadId,
|
|
31745
31777
|
turn_id: turnId
|
|
31746
31778
|
});
|
|
31747
31779
|
} else if (msg.type === "result") {
|
|
@@ -31794,12 +31826,18 @@ class AppServerClient {
|
|
|
31794
31826
|
});
|
|
31795
31827
|
return err;
|
|
31796
31828
|
}
|
|
31797
|
-
return {
|
|
31829
|
+
return { turn_id: turnId, session_id: sessionId };
|
|
31798
31830
|
} catch (err) {
|
|
31799
31831
|
clearTimeout(timeoutHandle);
|
|
31800
31832
|
const error48 = err instanceof Error ? err : new Error(String(err));
|
|
31833
|
+
if (!sessionId) {
|
|
31834
|
+
if (!options.resume) {
|
|
31835
|
+
this.sessionId = null;
|
|
31836
|
+
this.assignedSessionId = null;
|
|
31837
|
+
}
|
|
31838
|
+
}
|
|
31801
31839
|
onMessage({
|
|
31802
|
-
event: "startup_failed",
|
|
31840
|
+
event: sessionId ? "turn_failed" : "startup_failed",
|
|
31803
31841
|
timestamp: new Date,
|
|
31804
31842
|
payload: { reason: error48.message }
|
|
31805
31843
|
});
|
|
@@ -31808,6 +31846,7 @@ class AppServerClient {
|
|
|
31808
31846
|
}
|
|
31809
31847
|
stopSession() {
|
|
31810
31848
|
this.abortController?.abort();
|
|
31849
|
+
this.assignedSessionId = null;
|
|
31811
31850
|
this.sessionId = null;
|
|
31812
31851
|
this.abortController = null;
|
|
31813
31852
|
}
|
|
@@ -31828,6 +31867,7 @@ import { tmpdir } from "os";
|
|
|
31828
31867
|
import { join, sep as sep2 } from "path";
|
|
31829
31868
|
import process4 from "process";
|
|
31830
31869
|
var ENV_VAR_RE = /^\$([A-Z_]\w*)$/i;
|
|
31870
|
+
var VALID_SETTING_SOURCES = new Set(["user", "project", "local"]);
|
|
31831
31871
|
var DEFAULTS2 = {
|
|
31832
31872
|
POLL_INTERVAL_MS: 30000,
|
|
31833
31873
|
WORKSPACE_ROOT: join(tmpdir(), "work-please_workspaces"),
|
|
@@ -31835,9 +31875,11 @@ var DEFAULTS2 = {
|
|
|
31835
31875
|
MAX_CONCURRENT_AGENTS: 10,
|
|
31836
31876
|
AGENT_MAX_TURNS: 20,
|
|
31837
31877
|
MAX_RETRY_BACKOFF_MS: 300000,
|
|
31878
|
+
CLAUDE_EFFORT: "high",
|
|
31838
31879
|
CLAUDE_COMMAND: "claude",
|
|
31839
31880
|
CLAUDE_PERMISSION_MODE: "bypassPermissions",
|
|
31840
31881
|
CLAUDE_ALLOWED_TOOLS: [],
|
|
31882
|
+
CLAUDE_SETTING_SOURCES: ["project", "local", "user"],
|
|
31841
31883
|
CLAUDE_TURN_TIMEOUT_MS: 3600000,
|
|
31842
31884
|
CLAUDE_READ_TIMEOUT_MS: 5000,
|
|
31843
31885
|
CLAUDE_STALL_TIMEOUT_MS: 300000,
|
|
@@ -31879,20 +31921,34 @@ function buildConfig(workflow) {
|
|
|
31879
31921
|
max_retry_backoff_ms: posIntValue(agent.max_retry_backoff_ms, DEFAULTS2.MAX_RETRY_BACKOFF_MS),
|
|
31880
31922
|
max_concurrent_agents_by_state: stateLimitsValue(agent.max_concurrent_agents_by_state)
|
|
31881
31923
|
},
|
|
31882
|
-
claude:
|
|
31883
|
-
model: stringValue(claude.model),
|
|
31884
|
-
command: commandValue(claude.command) ?? DEFAULTS2.CLAUDE_COMMAND,
|
|
31885
|
-
permission_mode: stringValue(claude.permission_mode) ?? DEFAULTS2.CLAUDE_PERMISSION_MODE,
|
|
31886
|
-
allowed_tools: stringArrayValue(claude.allowed_tools, DEFAULTS2.CLAUDE_ALLOWED_TOOLS),
|
|
31887
|
-
turn_timeout_ms: intValue(claude.turn_timeout_ms, DEFAULTS2.CLAUDE_TURN_TIMEOUT_MS),
|
|
31888
|
-
read_timeout_ms: intValue(claude.read_timeout_ms, DEFAULTS2.CLAUDE_READ_TIMEOUT_MS),
|
|
31889
|
-
stall_timeout_ms: intValue(claude.stall_timeout_ms, DEFAULTS2.CLAUDE_STALL_TIMEOUT_MS)
|
|
31890
|
-
},
|
|
31924
|
+
claude: buildClaudeConfig(claude),
|
|
31891
31925
|
server: {
|
|
31892
31926
|
port: nonNegIntOrNull(server.port)
|
|
31893
31927
|
}
|
|
31894
31928
|
};
|
|
31895
31929
|
}
|
|
31930
|
+
function buildClaudeConfig(claude) {
|
|
31931
|
+
const settingsSec = sectionMap(claude, "settings");
|
|
31932
|
+
const attributionSec = sectionMap(settingsSec, "attribution");
|
|
31933
|
+
return {
|
|
31934
|
+
model: stringValue(claude.model),
|
|
31935
|
+
effort: effortValue(claude.effort, DEFAULTS2.CLAUDE_EFFORT),
|
|
31936
|
+
command: commandValue(claude.command) ?? DEFAULTS2.CLAUDE_COMMAND,
|
|
31937
|
+
permission_mode: stringValue(claude.permission_mode) ?? DEFAULTS2.CLAUDE_PERMISSION_MODE,
|
|
31938
|
+
allowed_tools: stringArrayValue(claude.allowed_tools, DEFAULTS2.CLAUDE_ALLOWED_TOOLS),
|
|
31939
|
+
setting_sources: stringArrayValue(claude.setting_sources, DEFAULTS2.CLAUDE_SETTING_SOURCES).filter((s2) => VALID_SETTING_SOURCES.has(s2)),
|
|
31940
|
+
turn_timeout_ms: intValue(claude.turn_timeout_ms, DEFAULTS2.CLAUDE_TURN_TIMEOUT_MS),
|
|
31941
|
+
read_timeout_ms: intValue(claude.read_timeout_ms, DEFAULTS2.CLAUDE_READ_TIMEOUT_MS),
|
|
31942
|
+
stall_timeout_ms: intValue(claude.stall_timeout_ms, DEFAULTS2.CLAUDE_STALL_TIMEOUT_MS),
|
|
31943
|
+
system_prompt: systemPromptValue(claude.system_prompt),
|
|
31944
|
+
settings: {
|
|
31945
|
+
attribution: {
|
|
31946
|
+
commit: stringValue(attributionSec.commit),
|
|
31947
|
+
pr: stringValue(attributionSec.pr)
|
|
31948
|
+
}
|
|
31949
|
+
}
|
|
31950
|
+
};
|
|
31951
|
+
}
|
|
31896
31952
|
function buildTrackerConfig(kind, tracker) {
|
|
31897
31953
|
const label_prefix = stringValue(tracker.label_prefix) ?? null;
|
|
31898
31954
|
const filter = buildFilterConfig(sectionMap(tracker, "filter"));
|
|
@@ -32006,6 +32062,26 @@ function maxConcurrentForState(config2, state) {
|
|
|
32006
32062
|
const byState = config2.agent.max_concurrent_agents_by_state;
|
|
32007
32063
|
return byState[normalized] ?? config2.agent.max_concurrent_agents;
|
|
32008
32064
|
}
|
|
32065
|
+
var DEFAULT_SYSTEM_PROMPT = { type: "preset", preset: "claude_code" };
|
|
32066
|
+
function systemPromptValue(val) {
|
|
32067
|
+
if (val == null)
|
|
32068
|
+
return DEFAULT_SYSTEM_PROMPT;
|
|
32069
|
+
if (typeof val === "string") {
|
|
32070
|
+
const trimmed = val.trim();
|
|
32071
|
+
return trimmed ? { type: "custom", value: trimmed } : DEFAULT_SYSTEM_PROMPT;
|
|
32072
|
+
}
|
|
32073
|
+
if (typeof val === "object" && !Array.isArray(val)) {
|
|
32074
|
+
const obj = val;
|
|
32075
|
+
if (obj.type === "preset" && obj.preset === "claude_code") {
|
|
32076
|
+
return typeof obj.append === "string" ? { type: "preset", preset: "claude_code", append: obj.append } : { type: "preset", preset: "claude_code" };
|
|
32077
|
+
}
|
|
32078
|
+
if (obj.type === "custom" && typeof obj.value === "string") {
|
|
32079
|
+
const trimmed = obj.value.trim();
|
|
32080
|
+
return trimmed ? { type: "custom", value: trimmed } : DEFAULT_SYSTEM_PROMPT;
|
|
32081
|
+
}
|
|
32082
|
+
}
|
|
32083
|
+
return DEFAULT_SYSTEM_PROMPT;
|
|
32084
|
+
}
|
|
32009
32085
|
function sectionMap(raw, key) {
|
|
32010
32086
|
const val = raw[key];
|
|
32011
32087
|
return val && typeof val === "object" && !Array.isArray(val) ? val : {};
|
|
@@ -32047,6 +32123,18 @@ function hookScriptValue(val) {
|
|
|
32047
32123
|
const trimmed = val.trimEnd();
|
|
32048
32124
|
return trimmed === "" ? null : trimmed;
|
|
32049
32125
|
}
|
|
32126
|
+
function effortValue(val, fallback) {
|
|
32127
|
+
const s2 = typeof val === "string" ? val.trim() : val;
|
|
32128
|
+
switch (s2) {
|
|
32129
|
+
case "low":
|
|
32130
|
+
case "medium":
|
|
32131
|
+
case "high":
|
|
32132
|
+
case "max":
|
|
32133
|
+
return s2;
|
|
32134
|
+
default:
|
|
32135
|
+
return fallback;
|
|
32136
|
+
}
|
|
32137
|
+
}
|
|
32050
32138
|
function commandValue(val) {
|
|
32051
32139
|
if (typeof val !== "string")
|
|
32052
32140
|
return null;
|
|
@@ -32070,7 +32158,7 @@ function csvValue(val) {
|
|
|
32070
32158
|
function stringArrayValue(val, fallback) {
|
|
32071
32159
|
if (!Array.isArray(val))
|
|
32072
32160
|
return fallback;
|
|
32073
|
-
return val.filter((v) => typeof v === "string");
|
|
32161
|
+
return val.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
32074
32162
|
}
|
|
32075
32163
|
function stateLimitsValue(val) {
|
|
32076
32164
|
if (!val || typeof val !== "object" || Array.isArray(val))
|
|
@@ -40173,9 +40261,28 @@ function isWorkflowError(result) {
|
|
|
40173
40261
|
}
|
|
40174
40262
|
|
|
40175
40263
|
// src/workspace.ts
|
|
40176
|
-
import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync2, rmSync as rmSync2, statSync as statSync3 } from "fs";
|
|
40177
|
-
import { join as join4, resolve as resolve4, sep as sep4 } from "path";
|
|
40264
|
+
import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
40265
|
+
import { dirname as dirname2, join as join4, resolve as resolve4, sep as sep4 } from "path";
|
|
40178
40266
|
import process6 from "process";
|
|
40267
|
+
var CLAUDE_SETTINGS_PATH = ".claude/settings.local.json";
|
|
40268
|
+
var WORK_PLEASE_URL = "https://github.com/pleaseai/work-please";
|
|
40269
|
+
var ATTRIBUTION_TEXT = `\uD83D\uDE4F Generated with [Work Please](${WORK_PLEASE_URL})`;
|
|
40270
|
+
function generateClaudeSettings(attribution) {
|
|
40271
|
+
return `${JSON.stringify({
|
|
40272
|
+
attribution: {
|
|
40273
|
+
commit: attribution?.commit ?? ATTRIBUTION_TEXT,
|
|
40274
|
+
pr: attribution?.pr ?? ATTRIBUTION_TEXT
|
|
40275
|
+
}
|
|
40276
|
+
}, null, 2)}
|
|
40277
|
+
`;
|
|
40278
|
+
}
|
|
40279
|
+
function ensureClaudeSettings(wsPath, attribution) {
|
|
40280
|
+
const settingsPath = join4(wsPath, CLAUDE_SETTINGS_PATH);
|
|
40281
|
+
if (existsSync4(settingsPath))
|
|
40282
|
+
return;
|
|
40283
|
+
mkdirSync2(dirname2(settingsPath), { recursive: true });
|
|
40284
|
+
writeFileSync2(settingsPath, generateClaudeSettings(attribution), "utf-8");
|
|
40285
|
+
}
|
|
40179
40286
|
var _git = {
|
|
40180
40287
|
spawnSync: (args) => Bun.spawnSync(args)
|
|
40181
40288
|
};
|
|
@@ -40308,6 +40415,11 @@ async function createWorkspace(config2, identifier, issue2) {
|
|
|
40308
40415
|
if (hookErr)
|
|
40309
40416
|
return hookErr;
|
|
40310
40417
|
}
|
|
40418
|
+
try {
|
|
40419
|
+
ensureClaudeSettings(wtPath, config2.claude.settings.attribution);
|
|
40420
|
+
} catch (err) {
|
|
40421
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
40422
|
+
}
|
|
40311
40423
|
return { path: wtPath, workspace_key: key, created_now: createdNow };
|
|
40312
40424
|
}
|
|
40313
40425
|
}
|
|
@@ -40338,6 +40450,11 @@ async function createWorkspace(config2, identifier, issue2) {
|
|
|
40338
40450
|
if (hookErr)
|
|
40339
40451
|
return hookErr;
|
|
40340
40452
|
}
|
|
40453
|
+
try {
|
|
40454
|
+
ensureClaudeSettings(wsPath, config2.claude.settings.attribution);
|
|
40455
|
+
} catch (err) {
|
|
40456
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
40457
|
+
}
|
|
40341
40458
|
return workspace;
|
|
40342
40459
|
}
|
|
40343
40460
|
async function removeWorkspace(config2, identifier, issue2) {
|