From 95dc2f22171e1d4ca43df2e859b8efff2105861a Mon Sep 17 00:00:00 2001 From: "Kensan (Enterprise IT Team)" Date: Mon, 27 Apr 2026 20:55:31 +0900 Subject: [PATCH] =?UTF-8?q?fix(v3.2.97):=20S1=20=E8=B5=B7=E5=8B=95?= =?UTF-8?q?=E6=99=82=20~/=E3=83=91=E3=82=B9=E4=BF=AE=E6=AD=A3=20+=20status?= =?UTF-8?q?line.js=20=E8=87=AA=E5=8B=95=E3=83=87=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=82=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New-RemoteTemplateDeployScript で bash double-quote 内 ~ が展開されない バグを修正(~ → $HOME 変換)。cron-launcher.sh が ~/~/.claudeos/ に 誤配置される問題を解消。S1 起動時に statusline.js を ~/.claude/ へ InitializeOnly でデプロイするよう deployParts に追加。 Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 28 ++++++ README.md | 6 +- scripts/main/Start-ClaudeCode.ps1 | 6 ++ scripts/templates/statusline.js | 145 ++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 3 deletions(-) create mode 100644 scripts/templates/statusline.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c660e36a..4c91f2ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,34 @@ # CHANGELOG +## [v3.2.97] - 2026-04-27 — S1 起動時 ~/パス修正 + statusline.js 自動デプロイ + +### 🎯 概要 +S1 起動(SSH モード)でのテンプレートデプロイに2つの問題を修正。 + +### 🐛 根本原因 + +| バグ | 原因 | 修正 | +|---|---|---| +| `~/.claudeos/cron-launcher.sh` が `~/~/.cloudeos/` に誤配置 | bash double-quoteの中では `~` は展開されない | `~` → `$HOME` に変換するロジック追加 | +| S1 起動時に `statusline.js` が Linux に自動デプロイされない | デプロイリストに含まれていなかった | `scripts/templates/statusline.js` を追加・`$HOME/.claude/statusline.js` として InitializeOnly でデプロイ | + +### 🔧 変更対象 + +| ファイル | 変更内容 | +|---|---| +| `scripts/main/Start-ClaudeCode.ps1` | `New-RemoteTemplateDeployScript` で `~` → `$HOME` 変換追加 / `statusline.js` デプロイ追加 | +| `scripts/templates/statusline.js` | statusline.js テンプレートとして新規追加 | + +### ✅ 修正後の動作 + +``` +S1 起動 → プロジェクト選択 +→ ~/.claudeos/cron-launcher.sh が /home/kensan/.claudeos/ に正しく配置 ✅ +→ ~/.claude/statusline.js が /home/kensan/.claude/ に InitializeOnly でデプロイ ✅ +→ 次回 S1 からは statusline が確実に動作 +``` + ## [v3.2.96] - 2026-04-27 — statusline.js Linux パス修正・自動デプロイ対応 ### 🎯 概要 diff --git a/README.md b/README.md index fa0fa624..fc02c11a 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ > **📌 v3.1.0 で Claude Code 専用ツールに整理** > v3.1.0 より、Codex CLI / GitHub Copilot CLI の起動メニュー (S2/S3/L2/L3) は削除されました。本ツールは **Claude Code 専用の自律開発ランチャー** として位置づけを明確化し、Linux crontab 連携・セッション情報タブ・Statusline グローバル適用などの新機能に投資が集中しています。 -> **🐛 v3.2.96 — statusline.js Linux パス修正・自動デプロイ対応(Windows/Linux 両対応)** -> `Invoke-RemoteSettingsSync` の Windows 絶対パス → `~/` 自動変換 + `statusline.js` 自動コピー。詳細は [`CHANGELOG.md`](./CHANGELOG.md) を参照。 +> **🐛 v3.2.97 — S1 起動時 `~` パス修正 + statusline.js 自動デプロイ** +> `New-RemoteTemplateDeployScript` の bash double-quote `~` 非展開バグ修正(`$HOME` 置換)+ S1 起動時に `statusline.js` を自動デプロイ。詳細は [`CHANGELOG.md`](./CHANGELOG.md) を参照。 > **📨 v3.2.0 — Cron HTML メールレポート (Visual Recap Mail)** > Cron で起動された ClaudeCode セッションの完了時に、**HTML 形式のレポートメール** を Gmail SMTP 経由で送信。アイコン+色付き表組み+実行サマリ(Monitor/Development/Verify/Improvement の出現回数/エラー検出/STABLE 達成)+次フェーズ提案を含む。送信先は `CLAUDEOS_DEFAULT_TO`(未設定時 `CLAUDEOS_SMTP_USER`)で指定し、SMTP 認証情報は `~/.env-claudeos` の Linux 環境変数で管理(config.json には書かない設計)。詳細は [`docs/common/16_HTMLメールレポート設定.md`](./docs/common/16_HTMLメールレポート設定.md) を参照。 @@ -27,7 +27,7 @@ | 項目 | 状態 | |------|------| -| バージョン | **v3.2.96** (statusline.js Linux パス修正・自動デプロイ) — 旧: v3.2.95 | +| バージョン | **v3.2.97** (S1 起動 `~` パス修正 + statusline.js 自動デプロイ) — 旧: v3.2.96 | | テスト | **776件** — Pester (Unit 21 / Integration 11 / Smoke 1) | | CI | ✅ SUCCESS | | ClaudeOS (Claude Code 専用) | v8 (Opus 4.7 最適化 / Token 1.35x 補正 / Agent Teams 並列 spawn / `/compact` 事前発動 / `task_budget` / 1H cache / `/ultrareview` / PreCompact hook / `/recap` fallback / Push Notification / Effort 動的切替) | diff --git a/scripts/main/Start-ClaudeCode.ps1 b/scripts/main/Start-ClaudeCode.ps1 index 3d81d75a..7a4886c5 100644 --- a/scripts/main/Start-ClaudeCode.ps1 +++ b/scripts/main/Start-ClaudeCode.ps1 @@ -57,7 +57,11 @@ function New-RemoteTemplateDeployScript { $content = Get-Content -Path $TemplatePath -Raw -Encoding UTF8 $base64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($content)) + # bash double-quote does not expand ~ — replace with $HOME for correct expansion $normalizedTargetPath = $TargetPath.Replace('\', '/') + if ($normalizedTargetPath -match '^~/') { + $normalizedTargetPath = '$HOME/' + $normalizedTargetPath.Substring(2) + } $mkdir = "" if ($EnsureParentDirectory) { $mkdir = "mkdir -p `"`$(dirname `"$normalizedTargetPath`")`"`n" @@ -442,6 +446,8 @@ try { (New-RemoteTemplateDeployScript -TemplatePath (Join-Path $ScriptRoot 'Claude\templates\claudeos\commands\session-info.md') -TargetPath "$linuxProject/.claude/commands/session-info.md" -Label '.claude/commands/session-info.md' -EnsureParentDirectory) # v3.1.0: cron-launcher.sh を ~/.claudeos/ に配布 (New-RemoteTemplateDeployScript -TemplatePath (Join-Path $ScriptRoot 'Claude\templates\linux\cron-launcher.sh') -TargetPath "~/.claudeos/cron-launcher.sh" -Label '~/.claudeos/cron-launcher.sh' -EnsureParentDirectory) + # v3.2.97: statusline.js を ~/.claude/ に配布 (InitializeOnly: 存在時は上書きしない) + (New-RemoteTemplateDeployScript -TemplatePath (Join-Path $ScriptRoot 'scripts\templates\statusline.js') -TargetPath "~/.claude/statusline.js" -Label '~/.claude/statusline.js' -EnsureParentDirectory -InitializeOnly) @" cat > $(ConvertTo-BashSingleQuoted -Value $remoteBootstrap) <<'EOF' #!/usr/bin/env bash diff --git a/scripts/templates/statusline.js b/scripts/templates/statusline.js new file mode 100644 index 00000000..a68ec48d --- /dev/null +++ b/scripts/templates/statusline.js @@ -0,0 +1,145 @@ +// Claude Code status line script (Windows / Node.js) +// Reads JSON from stdin, outputs formatted multi-line status bar with ANSI colors. +const { execSync } = require("child_process"); +const os = require("os"); + +// ANSI color codes +const C = { + cyan: "\x1b[36m", + green: "\x1b[32m", + yellow: "\x1b[33m", + magenta: "\x1b[35m", + blue: "\x1b[34m", + white: "\x1b[97m", + red: "\x1b[31m", + bold: "\x1b[1m", + r: "\x1b[0m", +}; + +function getGitBranch(cwd) { + try { + return execSync("git rev-parse --abbrev-ref HEAD", { + cwd, + encoding: "utf8", + stdio: ["pipe", "pipe", "ignore"], + }).trim(); + } catch { + return "?"; + } +} + +function progressBar(pct, width = 10) { + const filled = Math.round((pct / 100) * width); + const empty = width - filled; + // Green for filled, dim gray for empty + let color = C.green; + if (pct >= 80) color = C.red; + else if (pct >= 50) color = C.yellow; + return color + "\u25b0".repeat(filled) + C.cyan + "\u25b1".repeat(empty) + C.r; +} + +function formatDuration(ms) { + const totalSec = Math.floor(ms / 1000); + const hours = Math.floor(totalSec / 3600); + const minutes = Math.floor((totalSec % 3600) / 60); + if (hours > 0) return `${hours}h ${String(minutes).padStart(2, "0")}m`; + return `${minutes}m`; +} + +function formatResetTime(epoch) { + if (!epoch) return ""; + const dt = new Date(epoch * 1000); + const now = new Date(); + const timeStr = dt + .toLocaleString("en-US", { + hour: "numeric", + hour12: true, + timeZone: "Asia/Tokyo", + }) + .toLowerCase(); + const sameDay = + dt.toLocaleDateString("en-US", { timeZone: "Asia/Tokyo" }) === + now.toLocaleDateString("en-US", { timeZone: "Asia/Tokyo" }); + if (sameDay) return `Resets ${timeStr} (Asia/Tokyo)`; + const dateStr = dt.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + timeZone: "Asia/Tokyo", + }); + return `Resets ${dateStr} at ${timeStr} (Asia/Tokyo)`; +} + +let raw = ""; +process.stdin.on("data", (chunk) => (raw += chunk)); +process.stdin.on("end", () => { + if (!raw.trim()) return; + + let data; + try { + data = JSON.parse(raw); + } catch { + return; + } + + const model = data.model || {}; + const modelName = model.display_name || model.id || "?"; + + const cwd = data.cwd || ""; + const project = cwd ? require("path").basename(cwd) : "?"; + + const branch = getGitBranch(cwd || "."); + const osName = os.platform() === "win32" ? "Windows" : os.platform(); + + const ctx = data.context_window || {}; + const ctxPct = ctx.used_percentage || 0; + + const cost = data.cost || {}; + const linesAdded = cost.total_lines_added || 0; + const linesRemoved = cost.total_lines_removed || 0; + const durationMs = cost.total_duration_ms || 0; + + const rateLimits = data.rate_limits || {}; + const fiveHour = rateLimits.five_hour || {}; + const sevenDay = rateLimits.seven_day || {}; + + const sep = ` ${C.blue}\u2502${C.r} `; + + // Line 1: Model / Project / Branch / OS + const line1 = [ + `${C.magenta}\ud83e\udd16 ${C.bold}${modelName}${C.r}`, + `${C.yellow}\ud83d\udcc1 ${project}${C.r}`, + `${C.green}\ud83c\udf3f ${branch}${C.r}`, + `${C.cyan}\ud83d\udda5 ${osName}${C.r}`, + ].join(sep); + console.log(line1); + + // Line 2: Context % / File changes / Duration + const ctxBar = progressBar(ctxPct); + const line2Parts = [ + `${C.blue}\ud83d\udcca ${C.white}${Math.round(ctxPct)}%${C.r} ${ctxBar}`, + `${C.cyan}\u270f\ufe0f ${C.green}+${linesAdded}${C.r}/${C.red}-${linesRemoved}${C.r}`, + ]; + if (durationMs > 0) + line2Parts.push(`${C.blue}\u23f1 ${C.white}${formatDuration(durationMs)}${C.r}`); + console.log(line2Parts.join(sep)); + + // Line 3: 5-hour rate limit (Pro/Max only) + const fivePct = fiveHour.used_percentage; + if (fivePct != null) { + const fiveBar = progressBar(fivePct); + const fiveReset = formatResetTime(fiveHour.resets_at); + console.log( + `${C.blue}\u23f1 5h${C.r} ${fiveBar} ${C.white}${Math.round(fivePct)}%${C.r} ${C.cyan}${fiveReset}${C.r}` + ); + } + + // Line 4: 7-day rate limit (Pro/Max only) + const sevenPct = sevenDay.used_percentage; + if (sevenPct != null) { + const sevenBar = progressBar(sevenPct); + const sevenReset = formatResetTime(sevenDay.resets_at); + console.log( + `${C.blue}\ud83d\udcc5 7d${C.r} ${sevenBar} ${C.white}${Math.round(sevenPct)}%${C.r} ${C.cyan}\u5168\u30e2\u30c7\u30eb${C.r} ${C.cyan}${sevenReset}${C.r}` + ); + } +});