claude-task-worker 0.101.0 → 0.103.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +17 -2
  2. package/dist/index.js +82 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -61,7 +61,7 @@ CLI が GitHub ラベルを検知してタスクを起動し、プラグイン
61
61
  | [Pen CLI](https://docs.pen.dev/for-developers/pen-cli) | `.pen` の編集・参照。UIデザイン先行ワークフロー使用時のみ(要ログイン) |
62
62
  | [Playwright](https://playwright.dev/) のブラウザ | Playwright MCP でのブラウザ確認 |
63
63
  | [DESIGN.md CLI](https://github.com/google-labs-code/design.md) | `DESIGN.md` の lint。`update-design-md` 使用時のみ(未導入でも動く) |
64
- | [herdr](https://herdr.dev) | `--project` / `mode: "herdr"` 使用時のみ(`--cloud` は起動ゲートこそ herdr を要求しないが、実行経路の分岐が未追随のため現状は `mode: "herdr"` が要る) |
64
+ | [herdr](https://herdr.dev) | `--project` / `mode: "herdr"` 使用時のみ(`--cloud` herdr に依存しない) |
65
65
  | [GitHub MCP](https://github.com/github/github-mcp-server) | GitHub アクセスの高速化・クラウド実行時のプロキシ制限回避(任意。Claude 側のコネクタで有効化) |
66
66
 
67
67
  CLI 本体に npm の実行時依存はない(Node.js 標準モジュールのみで動作する)。
@@ -205,7 +205,7 @@ npx claude-task-worker cloud-setup
205
205
  - 完了は `cc-cloud-done` ラベルで検知する。4時間で応答がなければ打ち切り、`cc-need-human-check` を付けて失敗通知する
206
206
  - `--project` と併用した場合、`--cloud` は各プロジェクトへそのまま転送される
207
207
  - `--cloud` と併用できないコマンド: `init` / `install` / `update` / `usage` / `version`
208
- - **現時点では `mode: "default"` での `--cloud` はクラウド経路へ流れない**(実行経路の分岐が `mode: "herdr"` 判定のまま未追随のため)。`--cloud` を使うには `mode: "herdr"` にしておく必要がある。追随は別Issueで対応予定
208
+ - `--cloud` `mode`(`default` / `herdr`)に依存しない。クラウドセッションの作成は `script` コマンドの疑似 pty で完結し、herdr のペインを使わないため、どちらの `mode` でも同じ経路を通る
209
209
 
210
210
  詳細は [`docs/prd-cloud-worker-execution.md`](./docs/prd-cloud-worker-execution.md) を参照。
211
211
 
@@ -258,10 +258,25 @@ CI やクラウド VM など対話ログインできない環境では、環境
258
258
  | キー | 型 | 既定 | 説明 |
259
259
  |---|---|---|---|
260
260
  | `fixReviewPointCallbackCommentMessage` | string | - | `fix-review-point` 完了時に PR へ投稿するコメント(未設定なら投稿しない) |
261
+ | `remoteEnvId` | string \| null | `null` | クラウド実行(`--cloud`)時に `--environment` へ渡すクラウド環境ID。`null` なら渡さず claude 側の既定解決に任せる(下記) |
261
262
  | `uiDesign` | object | `{ "enabled": false, "designDir": "designs", "yolo": false }` | UIデザイン先行ワークフロー(下記) |
262
263
  | `workers` | object | `{}` | ワーカーごとの上書き設定(下記) |
263
264
  | `lastRun` | object | `{}` | 定期ワーカーの最終実行時刻。ワーカーが自動更新するため手で編集しない |
264
265
 
266
+ #### `claude-task-worker.local.json`(ローカル上書き)
267
+
268
+ 同じディレクトリに `claude-task-worker.local.json` を置くと、`claude-task-worker.json` と同じキーを書けて**同じキーはローカル側が勝つ**。マージはネストしたオブジェクトのキー単位(`workers.<name>.model` だけを差し替えられる。配列・スカラーは丸ごと置き換え)。
269
+
270
+ コミットしない前提のファイルで、`claude-task-worker init` が `.gitignore` へ登録する。`remoteEnvId` のように各自の環境で値が違う設定を置く。
271
+
272
+ #### クラウド環境の指定(`remoteEnvId`)
273
+
274
+ `--cloud` 実行時に `claude --environment <id>` へ渡す環境ID。指定しない場合の選択は claude CLI に任せる(`~/.claude/settings.json` の `remote.defaultEnvironmentId` → アカウントの最初の `anthropic_cloud` 環境 → 無ければ自動作成)。ID は claude.ai の環境設定、または `/remote-env` で確認できる。自己ホスト環境(`ccpool_...`)も同じキーに書ける。
275
+
276
+ ```json
277
+ { "remoteEnvId": "env_xxxxxxxx" }
278
+ ```
279
+
265
280
  #### ワーカーごとの設定
266
281
 
267
282
  未指定のワーカー・フィールドは既定値にフォールバックする。
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ var __export = (target, all) => {
12
12
  // src/table.ts
13
13
  var table_exports = {};
14
14
  __export(table_exports, {
15
+ CLOUD_WORKTREE_LABEL: () => CLOUD_WORKTREE_LABEL,
15
16
  CONTROL_CHARS: () => CONTROL_CHARS,
16
17
  LOG_DISPLAY_LIMIT: () => LOG_DISPLAY_LIMIT,
17
18
  TASK_DISPLAY_LIMIT: () => TASK_DISPLAY_LIMIT,
@@ -29,7 +30,7 @@ __export(table_exports, {
29
30
  import { format } from "node:util";
30
31
  function getDisplayWidth(str) {
31
32
  let width = 0;
32
- for (const char of str) {
33
+ for (const char of str.replace(CONTROL_CHARS, "")) {
33
34
  const code = char.codePointAt(0);
34
35
  if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141) {
35
36
  width += 2;
@@ -181,10 +182,11 @@ function buildLogTableLines(entries) {
181
182
  });
182
183
  return renderBoxTable(["Time", "#", "Stream", "Log"], [rows]);
183
184
  }
184
- var TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, logLines, rawLog, rawClear, consoleCaptured, CONTROL_CHARS;
185
+ var CLOUD_WORKTREE_LABEL, TASK_DISPLAY_LIMIT, LOG_DISPLAY_LIMIT, logLines, rawLog, rawClear, consoleCaptured, CONTROL_CHARS;
185
186
  var init_table = __esm({
186
187
  "src/table.ts"() {
187
188
  "use strict";
189
+ CLOUD_WORKTREE_LABEL = "\x1B[36m\u2601 running in cloud\x1B[0m";
188
190
  TASK_DISPLAY_LIMIT = 20;
189
191
  LOG_DISPLAY_LIMIT = 20;
190
192
  logLines = [];
@@ -1905,11 +1907,13 @@ function checkCloudConfig(input) {
1905
1907
  }
1906
1908
  var DEFAULT_CONFIG = {
1907
1909
  fixReviewPointCallbackCommentMessage: "",
1910
+ remoteEnvId: null,
1908
1911
  uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG },
1909
1912
  lastRun: {},
1910
1913
  workers: {}
1911
1914
  };
1912
1915
  var CONFIG_PATH = join(process.cwd(), "claude-task-worker.json");
1916
+ var LOCAL_CONFIG_PATH = join(process.cwd(), "claude-task-worker.local.json");
1913
1917
  function defaultsFor(name) {
1914
1918
  return WORKER_DEFAULTS[name] ?? DEFAULT_WORKER_CONFIG;
1915
1919
  }
@@ -2041,18 +2045,44 @@ function parseLastRunEntry(val) {
2041
2045
  }
2042
2046
  return result;
2043
2047
  }
2044
- function loadConfig() {
2045
- const configPath = CONFIG_PATH;
2046
- let raw;
2048
+ function isPlainObject(val) {
2049
+ return typeof val === "object" && val !== null && !Array.isArray(val);
2050
+ }
2051
+ function mergeConfigRaw(base, local) {
2052
+ const result = { ...base };
2053
+ for (const [key, val] of Object.entries(local)) {
2054
+ const current = result[key];
2055
+ result[key] = isPlainObject(current) && isPlainObject(val) ? mergeConfigRaw(current, val) : val;
2056
+ }
2057
+ return result;
2058
+ }
2059
+ function readRawConfig(path2) {
2060
+ let parsed;
2047
2061
  try {
2048
- raw = JSON.parse(readFileSync(configPath, "utf-8"));
2062
+ parsed = JSON.parse(readFileSync(path2, "utf-8"));
2049
2063
  } catch (err) {
2050
- if (err.code === "ENOENT") {
2051
- return { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, lastRun: {}, workers: {} };
2052
- }
2064
+ if (err.code === "ENOENT") return {};
2053
2065
  throw err;
2054
2066
  }
2067
+ if (!isPlainObject(parsed)) {
2068
+ console.warn(`[config] invalid ${path2}: expected object, ignoring`);
2069
+ return {};
2070
+ }
2071
+ return parsed;
2072
+ }
2073
+ function loadConfig() {
2074
+ const raw = mergeConfigRaw(readRawConfig(CONFIG_PATH), readRawConfig(LOCAL_CONFIG_PATH));
2055
2075
  const result = { ...DEFAULT_CONFIG, uiDesign: { ...DEFAULT_UI_DESIGN_CONFIG }, lastRun: {}, workers: {} };
2076
+ if ("remoteEnvId" in raw) {
2077
+ const val = raw["remoteEnvId"];
2078
+ if (val === null) {
2079
+ result.remoteEnvId = null;
2080
+ } else if (typeof val === "string" && val.trim().length > 0) {
2081
+ result.remoteEnvId = val.trim();
2082
+ } else {
2083
+ console.warn(`[config] invalid remoteEnvId: ${String(val)}, using default null`);
2084
+ }
2085
+ }
2056
2086
  if ("lastRun" in raw) {
2057
2087
  result.lastRun = parseLastRunEntry(raw["lastRun"]);
2058
2088
  }
@@ -2112,6 +2142,14 @@ function writeLastRun(repoRoot, workerName, at = /* @__PURE__ */ new Date()) {
2112
2142
  writeFileSync(path2, `${JSON.stringify(raw, null, 2)}
2113
2143
  `, "utf-8");
2114
2144
  }
2145
+ function getRemoteEnvId() {
2146
+ try {
2147
+ return loadConfig().remoteEnvId;
2148
+ } catch (err) {
2149
+ console.warn(`[config] failed to load remoteEnvId, not passing --environment: ${err}`);
2150
+ return null;
2151
+ }
2152
+ }
2115
2153
  function getUiDesignConfig() {
2116
2154
  try {
2117
2155
  return loadConfig().uiDesign;
@@ -2175,7 +2213,7 @@ function parseConfigFile(path2) {
2175
2213
  throw err;
2176
2214
  }
2177
2215
  }
2178
- function readRawConfig() {
2216
+ function readRawConfig2() {
2179
2217
  return parseConfigFile(getUserConfigPath());
2180
2218
  }
2181
2219
  function parseMode(raw, path2) {
@@ -2201,7 +2239,7 @@ function parsePermission(raw, path2) {
2201
2239
  }
2202
2240
  function loadUserConfig() {
2203
2241
  const path2 = getUserConfigPath();
2204
- const raw = readRawConfig();
2242
+ const raw = readRawConfig2();
2205
2243
  if (raw === void 0) {
2206
2244
  throw new UserConfigError(`config.json not found: ${path2}`);
2207
2245
  }
@@ -2293,7 +2331,7 @@ function getPermissionMode() {
2293
2331
  function readTopLevel(parse, fallback, label) {
2294
2332
  let raw;
2295
2333
  try {
2296
- raw = readRawConfig();
2334
+ raw = readRawConfig2();
2297
2335
  } catch (err) {
2298
2336
  console.warn(`[config] failed to read config file, using default ${label}: ${err}`);
2299
2337
  return fallback;
@@ -2443,6 +2481,7 @@ function buildClaudeArgs({
2443
2481
  advisorModel,
2444
2482
  permissionMode,
2445
2483
  cloud,
2484
+ remoteEnvId,
2446
2485
  baseRef,
2447
2486
  onBranch
2448
2487
  }) {
@@ -2452,6 +2491,7 @@ function buildClaudeArgs({
2452
2491
  throw new Error("--on-branch and --ref both set the cloud session's base branch; pass one or the other");
2453
2492
  }
2454
2493
  const advisor = advisorModel?.trim() ?? "";
2494
+ const remoteEnv = remoteEnvId?.trim() ?? "";
2455
2495
  const permission = permissionMode ?? DEFAULT_PERMISSION_MODE;
2456
2496
  return [
2457
2497
  // default モードはプロンプトを引数で渡す(print モード)。herdr モードでは渡さない:
@@ -2475,6 +2515,9 @@ function buildClaudeArgs({
2475
2515
  // advisor 未指定(空文字)ならフラグごと省く。値なしの `--advisor` を渡すと
2476
2516
  // 後続フラグを値として食われるため、必ずモデル名とセットでのみ付ける。
2477
2517
  ...advisor === "" ? [] : ["--advisor", advisor],
2518
+ // クラウド環境の指名。値なしの `--environment` は後続フラグを値として食うため、
2519
+ // `--advisor` と同じく ID とセットでのみ付ける。
2520
+ ...cloud === true && remoteEnv !== "" ? ["--environment", remoteEnv] : [],
2478
2521
  // クラウド実行時は「作成コマンドの共通フラグ」だけをここで返す。`--cloud` 自体は
2479
2522
  // 付けない(値として渡す description(=クラウドセッションの初期プロンプト。
2480
2523
  // appendCloudDoneInstruction() 適用後のタスクプロンプトそのもの)は
@@ -2972,7 +3015,9 @@ function run(command, args, id, title, workerName, path2, onComplete, cwd, env,
2972
3015
  title,
2973
3016
  status: "running",
2974
3017
  workerName,
2975
- path: path2,
3018
+ // クラウド実行は worktree を持たない(ワーカーが path に undefined を渡す)ため、
3019
+ // 空欄ではなく実行場所が分かる表示を入れる。
3020
+ path: cloud ? CLOUD_WORKTREE_LABEL : path2,
2976
3021
  startedAt: /* @__PURE__ */ new Date()
2977
3022
  });
2978
3023
  ensureRenderInterval();
@@ -3786,7 +3831,7 @@ function createIssuePollingWorker(config) {
3786
3831
  // config.json の advisor が false なら advisorModel の指定に関わらず渡さない。
3787
3832
  advisorModel: isAdvisorEnabled() ? advisorModel : "",
3788
3833
  permissionMode: getPermissionMode(),
3789
- ...cloud ? { cloud: true, baseRef: baseBranch } : {}
3834
+ ...cloud ? { cloud: true, baseRef: baseBranch, remoteEnvId: getRemoteEnvId() ?? "" } : {}
3790
3835
  });
3791
3836
  let cwd;
3792
3837
  if (cloud) {
@@ -4054,6 +4099,7 @@ function createPrPollingWorker(config) {
4054
4099
  advisorModel: isAdvisorEnabled() ? advisorModel : "",
4055
4100
  permissionMode: getPermissionMode(),
4056
4101
  cloud: isCloud,
4102
+ remoteEnvId: isCloud ? getRemoteEnvId() ?? "" : void 0,
4057
4103
  onBranch: isCloud ? pr.headRefName : void 0
4058
4104
  });
4059
4105
  if (isCloud) {
@@ -4660,7 +4706,7 @@ function createScheduledWorker(config) {
4660
4706
  effort,
4661
4707
  advisorModel: isAdvisorEnabled() ? advisorModel : "",
4662
4708
  permissionMode: getPermissionMode(),
4663
- ...cloud ? { cloud: true, baseRef: defaultBranch } : {}
4709
+ ...cloud ? { cloud: true, baseRef: defaultBranch, remoteEnvId: getRemoteEnvId() ?? "" } : {}
4664
4710
  });
4665
4711
  await publishLastRunPr(config.name, defaultBranch, new Date(now)).catch(
4666
4712
  (err) => console.error(`[${config.name}] publishLastRunPr failed: ${err}`)
@@ -4748,7 +4794,8 @@ var updateDesignMdWorker = createScheduledWorker({
4748
4794
  init_table();
4749
4795
 
4750
4796
  // src/commands/init.ts
4751
- import { mkdir as mkdir2, writeFile as writeFile2, access } from "node:fs/promises";
4797
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, access } from "node:fs/promises";
4798
+ import { basename as basename3 } from "node:path";
4752
4799
 
4753
4800
  // src/commands/codegraph.ts
4754
4801
  import { mkdir, readFile, writeFile } from "node:fs/promises";
@@ -4937,6 +4984,22 @@ async function createConfig(force) {
4937
4984
  const result = await writeFileWithMode(CONFIG_PATH, JSON.stringify(initialConfig, null, 2), force);
4938
4985
  logWriteResult(result, CONFIG_PATH);
4939
4986
  }
4987
+ async function ensureLocalConfigGitIgnore() {
4988
+ const entry = basename3(LOCAL_CONFIG_PATH);
4989
+ const path2 = ".gitignore";
4990
+ let current = "";
4991
+ try {
4992
+ current = await readFile2(path2, "utf-8");
4993
+ } catch {
4994
+ }
4995
+ const next = appendIgnoreEntry(current, entry);
4996
+ if (next === null) {
4997
+ console.log(`[init] Already ignored: ${entry} (${path2})`);
4998
+ return;
4999
+ }
5000
+ await writeFile2(path2, next, "utf-8");
5001
+ console.log(`[init] Added ${entry} to ${path2}`);
5002
+ }
4940
5003
  async function init(options = {}) {
4941
5004
  const force = options.force ?? false;
4942
5005
  console.log(`[init] Creating labels...${force ? " (force mode)" : ""}`);
@@ -4958,6 +5021,7 @@ async function init(options = {}) {
4958
5021
  logWriteResult(await writeFileWithMode(workflowPath, ASSIGN_CREATOR_WORKFLOW, force), workflowPath);
4959
5022
  console.log("[init] Creating config file...");
4960
5023
  await createConfig(force);
5024
+ await ensureLocalConfigGitIgnore();
4961
5025
  console.log("[init] Setting up CodeGraph...");
4962
5026
  await ensureCodegraphGitIgnore("init");
4963
5027
  await runCodegraphInit("init");
@@ -5119,7 +5183,7 @@ async function install() {
5119
5183
  }
5120
5184
 
5121
5185
  // src/commands/cloud-setup.ts
5122
- import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile2 } from "node:fs/promises";
5186
+ import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile3 } from "node:fs/promises";
5123
5187
  import { homedir as homedir6 } from "node:os";
5124
5188
  import { join as join8 } from "node:path";
5125
5189
  var LOG_PREFIX = "cloud-setup";
@@ -5160,7 +5224,7 @@ async function writeClaudeSettings(force) {
5160
5224
  const path2 = claudeSettingsPath();
5161
5225
  let existing;
5162
5226
  try {
5163
- existing = await readFile2(path2, "utf-8");
5227
+ existing = await readFile3(path2, "utf-8");
5164
5228
  } catch {
5165
5229
  existing = null;
5166
5230
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-task-worker",
3
- "version": "0.101.0",
3
+ "version": "0.103.0",
4
4
  "description": "CLI tool that polls GitHub Issues/PRs and delegates work to Claude CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",