@pleaseai/work 0.1.5 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/index.js +120 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -165,6 +165,7 @@ agent:
165
165
 
166
166
  claude:
167
167
  permission_mode: acceptEdits
168
+ # setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
168
169
  turn_timeout_ms: 3600000
169
170
  ---
170
171
 
@@ -224,6 +225,7 @@ agent:
224
225
 
225
226
  claude:
226
227
  permission_mode: acceptEdits
228
+ # setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
227
229
  turn_timeout_ms: 3600000
228
230
  ---
229
231
 
@@ -287,6 +289,7 @@ agent:
287
289
 
288
290
  claude:
289
291
  permission_mode: acceptEdits
292
+ # setting_sources: [] # default: [project, local, user]; set [] for SDK isolation mode
290
293
  turn_timeout_ms: 3600000
291
294
  ---
292
295
 
@@ -394,9 +397,18 @@ claude:
394
397
  - Read
395
398
  - Write
396
399
  - Bash
400
+ setting_sources: # Optional: filesystem settings to load. Default: [project, local, user]
401
+ - project # load .claude/settings.json + CLAUDE.md from the workspace directory
402
+ - local # load .claude/settings.local.json from the workspace directory
403
+ - user # load ~/.claude/settings.json + global CLAUDE.md
404
+ # Only "project", "local", and "user" are valid — other values are ignored
397
405
  turn_timeout_ms: 3600000 # Optional: per-turn timeout in ms, default 3600000
398
406
  read_timeout_ms: 5000 # Optional: initial subprocess read timeout in ms, default 5000
399
407
  stall_timeout_ms: 300000 # Optional: stall detection timeout, default 300000
408
+ settings:
409
+ attribution:
410
+ commit: "🙏 Generated with [Work Please](https://github.com/pleaseai/work-please)" # Optional: appended to git commit messages. Defaults to Work Please link.
411
+ pr: "🙏 Generated with [Work Please](https://github.com/pleaseai/work-please)" # Optional: appended to PR descriptions. Defaults to Work Please link.
400
412
 
401
413
  server:
402
414
  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.5",
2169
+ version: "0.1.6",
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
- return { threadId: randomUUID(), workspace: this.workspace };
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,21 @@ 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
+ }
31723
31751
  const toolSpecs = getToolSpecs(this.config);
31724
31752
  if (toolSpecs.length > 0) {
31725
31753
  options.mcpServers = {
31726
31754
  "work-please-tools": createToolsMcpServer(this.config)
31727
31755
  };
31728
31756
  }
31757
+ if (this.config.claude.setting_sources.length > 0) {
31758
+ options.settingSources = this.config.claude.setting_sources;
31759
+ }
31729
31760
  const turnId = randomUUID();
31730
31761
  let sessionId = null;
31731
31762
  let gotError = false;
@@ -31737,11 +31768,11 @@ class AppServerClient {
31737
31768
  const initMsg = msg;
31738
31769
  sessionId = initMsg.session_id;
31739
31770
  this.sessionId = sessionId;
31771
+ this.assignedSessionId = null;
31740
31772
  onMessage({
31741
31773
  event: "session_started",
31742
31774
  timestamp: new Date,
31743
31775
  session_id: sessionId,
31744
- thread_id: session.threadId,
31745
31776
  turn_id: turnId
31746
31777
  });
31747
31778
  } else if (msg.type === "result") {
@@ -31794,12 +31825,18 @@ class AppServerClient {
31794
31825
  });
31795
31826
  return err;
31796
31827
  }
31797
- return { thread_id: session.threadId, turn_id: turnId, session_id: sessionId };
31828
+ return { turn_id: turnId, session_id: sessionId };
31798
31829
  } catch (err) {
31799
31830
  clearTimeout(timeoutHandle);
31800
31831
  const error48 = err instanceof Error ? err : new Error(String(err));
31832
+ if (!sessionId) {
31833
+ if (!options.resume) {
31834
+ this.sessionId = null;
31835
+ this.assignedSessionId = null;
31836
+ }
31837
+ }
31801
31838
  onMessage({
31802
- event: "startup_failed",
31839
+ event: sessionId ? "turn_failed" : "startup_failed",
31803
31840
  timestamp: new Date,
31804
31841
  payload: { reason: error48.message }
31805
31842
  });
@@ -31808,6 +31845,7 @@ class AppServerClient {
31808
31845
  }
31809
31846
  stopSession() {
31810
31847
  this.abortController?.abort();
31848
+ this.assignedSessionId = null;
31811
31849
  this.sessionId = null;
31812
31850
  this.abortController = null;
31813
31851
  }
@@ -31828,6 +31866,7 @@ import { tmpdir } from "os";
31828
31866
  import { join, sep as sep2 } from "path";
31829
31867
  import process4 from "process";
31830
31868
  var ENV_VAR_RE = /^\$([A-Z_]\w*)$/i;
31869
+ var VALID_SETTING_SOURCES = new Set(["user", "project", "local"]);
31831
31870
  var DEFAULTS2 = {
31832
31871
  POLL_INTERVAL_MS: 30000,
31833
31872
  WORKSPACE_ROOT: join(tmpdir(), "work-please_workspaces"),
@@ -31838,6 +31877,7 @@ var DEFAULTS2 = {
31838
31877
  CLAUDE_COMMAND: "claude",
31839
31878
  CLAUDE_PERMISSION_MODE: "bypassPermissions",
31840
31879
  CLAUDE_ALLOWED_TOOLS: [],
31880
+ CLAUDE_SETTING_SOURCES: ["project", "local", "user"],
31841
31881
  CLAUDE_TURN_TIMEOUT_MS: 3600000,
31842
31882
  CLAUDE_READ_TIMEOUT_MS: 5000,
31843
31883
  CLAUDE_STALL_TIMEOUT_MS: 300000,
@@ -31879,20 +31919,33 @@ function buildConfig(workflow) {
31879
31919
  max_retry_backoff_ms: posIntValue(agent.max_retry_backoff_ms, DEFAULTS2.MAX_RETRY_BACKOFF_MS),
31880
31920
  max_concurrent_agents_by_state: stateLimitsValue(agent.max_concurrent_agents_by_state)
31881
31921
  },
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
- },
31922
+ claude: buildClaudeConfig(claude),
31891
31923
  server: {
31892
31924
  port: nonNegIntOrNull(server.port)
31893
31925
  }
31894
31926
  };
31895
31927
  }
31928
+ function buildClaudeConfig(claude) {
31929
+ const settingsSec = sectionMap(claude, "settings");
31930
+ const attributionSec = sectionMap(settingsSec, "attribution");
31931
+ return {
31932
+ model: stringValue(claude.model),
31933
+ command: commandValue(claude.command) ?? DEFAULTS2.CLAUDE_COMMAND,
31934
+ permission_mode: stringValue(claude.permission_mode) ?? DEFAULTS2.CLAUDE_PERMISSION_MODE,
31935
+ allowed_tools: stringArrayValue(claude.allowed_tools, DEFAULTS2.CLAUDE_ALLOWED_TOOLS),
31936
+ setting_sources: stringArrayValue(claude.setting_sources, DEFAULTS2.CLAUDE_SETTING_SOURCES).filter((s2) => VALID_SETTING_SOURCES.has(s2)),
31937
+ turn_timeout_ms: intValue(claude.turn_timeout_ms, DEFAULTS2.CLAUDE_TURN_TIMEOUT_MS),
31938
+ read_timeout_ms: intValue(claude.read_timeout_ms, DEFAULTS2.CLAUDE_READ_TIMEOUT_MS),
31939
+ stall_timeout_ms: intValue(claude.stall_timeout_ms, DEFAULTS2.CLAUDE_STALL_TIMEOUT_MS),
31940
+ system_prompt: systemPromptValue(claude.system_prompt),
31941
+ settings: {
31942
+ attribution: {
31943
+ commit: stringValue(attributionSec.commit),
31944
+ pr: stringValue(attributionSec.pr)
31945
+ }
31946
+ }
31947
+ };
31948
+ }
31896
31949
  function buildTrackerConfig(kind, tracker) {
31897
31950
  const label_prefix = stringValue(tracker.label_prefix) ?? null;
31898
31951
  const filter = buildFilterConfig(sectionMap(tracker, "filter"));
@@ -32006,6 +32059,26 @@ function maxConcurrentForState(config2, state) {
32006
32059
  const byState = config2.agent.max_concurrent_agents_by_state;
32007
32060
  return byState[normalized] ?? config2.agent.max_concurrent_agents;
32008
32061
  }
32062
+ var DEFAULT_SYSTEM_PROMPT = { type: "preset", preset: "claude_code" };
32063
+ function systemPromptValue(val) {
32064
+ if (val == null)
32065
+ return DEFAULT_SYSTEM_PROMPT;
32066
+ if (typeof val === "string") {
32067
+ const trimmed = val.trim();
32068
+ return trimmed ? { type: "custom", value: trimmed } : DEFAULT_SYSTEM_PROMPT;
32069
+ }
32070
+ if (typeof val === "object" && !Array.isArray(val)) {
32071
+ const obj = val;
32072
+ if (obj.type === "preset" && obj.preset === "claude_code") {
32073
+ return typeof obj.append === "string" ? { type: "preset", preset: "claude_code", append: obj.append } : { type: "preset", preset: "claude_code" };
32074
+ }
32075
+ if (obj.type === "custom" && typeof obj.value === "string") {
32076
+ const trimmed = obj.value.trim();
32077
+ return trimmed ? { type: "custom", value: trimmed } : DEFAULT_SYSTEM_PROMPT;
32078
+ }
32079
+ }
32080
+ return DEFAULT_SYSTEM_PROMPT;
32081
+ }
32009
32082
  function sectionMap(raw, key) {
32010
32083
  const val = raw[key];
32011
32084
  return val && typeof val === "object" && !Array.isArray(val) ? val : {};
@@ -32070,7 +32143,7 @@ function csvValue(val) {
32070
32143
  function stringArrayValue(val, fallback) {
32071
32144
  if (!Array.isArray(val))
32072
32145
  return fallback;
32073
- return val.filter((v) => typeof v === "string");
32146
+ return val.filter((v) => typeof v === "string" && v.trim().length > 0);
32074
32147
  }
32075
32148
  function stateLimitsValue(val) {
32076
32149
  if (!val || typeof val !== "object" || Array.isArray(val))
@@ -40173,9 +40246,28 @@ function isWorkflowError(result) {
40173
40246
  }
40174
40247
 
40175
40248
  // 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";
40249
+ import { existsSync as existsSync4, lstatSync as lstatSync2, mkdirSync as mkdirSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
40250
+ import { dirname as dirname2, join as join4, resolve as resolve4, sep as sep4 } from "path";
40178
40251
  import process6 from "process";
40252
+ var CLAUDE_SETTINGS_PATH = ".claude/settings.local.json";
40253
+ var WORK_PLEASE_URL = "https://github.com/pleaseai/work-please";
40254
+ var ATTRIBUTION_TEXT = `\uD83D\uDE4F Generated with [Work Please](${WORK_PLEASE_URL})`;
40255
+ function generateClaudeSettings(attribution) {
40256
+ return `${JSON.stringify({
40257
+ attribution: {
40258
+ commit: attribution?.commit ?? ATTRIBUTION_TEXT,
40259
+ pr: attribution?.pr ?? ATTRIBUTION_TEXT
40260
+ }
40261
+ }, null, 2)}
40262
+ `;
40263
+ }
40264
+ function ensureClaudeSettings(wsPath, attribution) {
40265
+ const settingsPath = join4(wsPath, CLAUDE_SETTINGS_PATH);
40266
+ if (existsSync4(settingsPath))
40267
+ return;
40268
+ mkdirSync2(dirname2(settingsPath), { recursive: true });
40269
+ writeFileSync2(settingsPath, generateClaudeSettings(attribution), "utf-8");
40270
+ }
40179
40271
  var _git = {
40180
40272
  spawnSync: (args) => Bun.spawnSync(args)
40181
40273
  };
@@ -40308,6 +40400,11 @@ async function createWorkspace(config2, identifier, issue2) {
40308
40400
  if (hookErr)
40309
40401
  return hookErr;
40310
40402
  }
40403
+ try {
40404
+ ensureClaudeSettings(wtPath, config2.claude.settings.attribution);
40405
+ } catch (err) {
40406
+ return err instanceof Error ? err : new Error(String(err));
40407
+ }
40311
40408
  return { path: wtPath, workspace_key: key, created_now: createdNow };
40312
40409
  }
40313
40410
  }
@@ -40338,6 +40435,11 @@ async function createWorkspace(config2, identifier, issue2) {
40338
40435
  if (hookErr)
40339
40436
  return hookErr;
40340
40437
  }
40438
+ try {
40439
+ ensureClaudeSettings(wsPath, config2.claude.settings.attribution);
40440
+ } catch (err) {
40441
+ return err instanceof Error ? err : new Error(String(err));
40442
+ }
40341
40443
  return workspace;
40342
40444
  }
40343
40445
  async function removeWorkspace(config2, identifier, issue2) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pleaseai/work",
3
3
  "type": "module",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "description": "Symphony-spec orchestrator for Claude Code + Asana/GitHub Projects v2",
6
6
  "license": "FSL-1.1-MIT",
7
7
  "repository": {