throughline 0.16.8 → 0.16.9
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/CHANGELOG.md +23 -1
- package/README.ja.md +1 -1
- package/README.md +1 -1
- package/docs/adr/0043-claude-successor-opens-in-desktop.md +17 -2
- package/package.json +1 -1
- package/src/claude-auto-handoff.mjs +8 -5
- package/src/claude-auto-handoff.test.mjs +10 -2
- package/src/os/pty-run.mjs +20 -4
- package/src/os/pty-run.test.mjs +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,27 @@ shipped to npm but were not individually tagged on GitHub.
|
|
|
10
10
|
|
|
11
11
|
## [Unreleased]
|
|
12
12
|
|
|
13
|
+
## [0.16.9] — 2026-10-09
|
|
14
|
+
|
|
15
|
+
### 追加
|
|
16
|
+
|
|
17
|
+
- Claude Desktopから始まった会話の後継を、作業を終えた時にClaude Desktopへ開く動き(0.16.8、[ADR 0043](docs/adr/0043-claude-successor-opens-in-desktop.md))を、Windowsでも行う。`claude --desktop --resume`は出力が端末でないと動かないので、Windowsでは新しいconsole(最小化)の中のPowerShellに実行させ、その終了codeで成否を決める。
|
|
18
|
+
- Windowsの端末(Claude Code 2.1.293、Claude Desktop 2.16120)の実測。裏の会話は、作業中も手すきも`That session is running in the background`(終了code 1)で断られ、`claude stop`の後は`Opening session <id> in Claude Desktop`(終了code 0)で開いた。macOSと同じ。
|
|
19
|
+
- `cmd /c start /wait`は、中の命令が断られても0を返し、batchのshim(`claude.cmd`)を閉じないwindowで開くので使わない。
|
|
20
|
+
|
|
21
|
+
### 変わらない事
|
|
22
|
+
|
|
23
|
+
- macOSの動き。LinuxのClaude Codeには`--desktop`が無い(`--desktop isn't available on this platform. It works on macOS and Windows (x64).`)ので、Linuxでは今までどおり裏の会話のままにする。
|
|
24
|
+
|
|
25
|
+
### 確認した範囲
|
|
26
|
+
|
|
27
|
+
- Windowsで、端末から始めた会話に`THROUGHLINE_AUTO_HANDOFF_OPEN=desktop`を付けて流した。続けて4回引き継ぎ、最後の後継が作業を終えた16秒後に、Claude Desktopがその会話を取り込んだ。途中の後継は移っていない。
|
|
28
|
+
- Linux(Claude Code 2.1.293)で、Claude Codeの自動継続が続けて3回引き継いで完了する事(後継は裏の会話のまま)。
|
|
29
|
+
|
|
30
|
+
### 確認していない範囲
|
|
31
|
+
|
|
32
|
+
- WindowsとmacOSの、Claude Desktopの画面から始めた本物の会話での通し。
|
|
33
|
+
|
|
13
34
|
## [0.16.8] — 2026-10-08
|
|
14
35
|
|
|
15
36
|
### 追加
|
|
@@ -2055,7 +2076,8 @@ two attempts, instrument first instead of patching again.
|
|
|
2055
2076
|
|
|
2056
2077
|
---
|
|
2057
2078
|
|
|
2058
|
-
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.16.
|
|
2079
|
+
[Unreleased]: https://github.com/kitepon/Throughline/compare/v0.16.9...HEAD
|
|
2080
|
+
[0.16.9]: https://github.com/kitepon/Throughline/compare/v0.16.8...v0.16.9
|
|
2059
2081
|
[0.16.8]: https://github.com/kitepon/Throughline/compare/v0.16.7...v0.16.8
|
|
2060
2082
|
[0.16.7]: https://github.com/kitepon/Throughline/compare/v0.16.6...v0.16.7
|
|
2061
2083
|
[0.16.6]: https://github.com/kitepon/Throughline/compare/v0.16.5...v0.16.6
|
package/README.ja.md
CHANGED
|
@@ -61,7 +61,7 @@ throughline auto-handoff disable --host claude
|
|
|
61
61
|
3. 同じprojectに、継続の指示を最初の指示として付けた新しいClaudeの会話を裏で立てます(`claude --bg`)。会話の名前は、前任の会話の題から`<project名>|<作業の概要>(自動引き継ぎ)`にします。モデル・推論強度・権限は旧い会話から引き継ぎます。
|
|
62
62
|
4. 最初の指示を受け取った時に、止めた時点の依頼、そのターンでここまでにしたこと(発言と道具の呼び出し)、実行されなかった道具、直近の会話の原文を注入します(上限9,500字)。止めたターンの道具の入出力は、後継が`throughline detail`で取り出せます。
|
|
63
63
|
|
|
64
|
-
続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面から始まった会話の後継は、そのターンの作業を終えた時に、Claude Desktopへ移して開きます(macOS
|
|
64
|
+
続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面から始まった会話の後継は、そのターンの作業を終えた時に、Claude Desktopへ移して開きます(macOSは0.16.8から、Windowsは0.16.9から。LinuxのClaude Codeには`--desktop`が無いので開きません。[ADR 0043](docs/adr/0043-claude-successor-opens-in-desktop.md))。Claude Codeは裏で動いている会話をDesktopへ移さないので、作業の間はDesktopには出ません。続けて引き継いだ時は、最後に作業を終えた後継だけを開きます。端末から始めた会話の後継は開きません。手で開く時は、`claude stop <id>`で裏の実行を停止し、`claude --desktop --resume <session-id>`を実行します。`session-id`は`claude agents --json --all`の`sessionId`です。環境変数`THROUGHLINE_AUTO_HANDOFF_OPEN`を`desktop`にすると出どころに関係なく開き、`off`にすると開きません。手動`/compact`、subagentの中の圧縮、`claude -p`などscriptから起動した会話は対象に含めません。
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
throughline auto-handoff status --host claude --operation <handoff-id> --json
|
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ throughline auto-handoff disable --host claude
|
|
|
60
60
|
3. 同じprojectに、継続の指示を最初の指示として付けた新しいClaudeの会話を裏で立てます(`claude --bg`)。会話の名前は、前任の会話の題から`<project名>|<作業の概要>(自動引き継ぎ)`にします。モデル・推論強度・権限は旧い会話から引き継ぎます。
|
|
61
61
|
4. 最初の指示を受け取った時に、止めた時点の依頼、そのターンでここまでにしたこと(発言と道具の呼び出し)、実行されなかった道具、直近の会話の原文を注入します(上限9,500字)。止めたターンの道具の入出力は、後継が`throughline detail`で取り出せます。
|
|
62
62
|
|
|
63
|
-
続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面から始まった会話の後継は、そのターンの作業を終えた時に、Claude Desktopへ移して開きます(macOS
|
|
63
|
+
続きは裏の会話で動きます。`claude agents`の一覧と`claude attach <id>`で見られます。Claude Desktopの画面から始まった会話の後継は、そのターンの作業を終えた時に、Claude Desktopへ移して開きます(macOSは0.16.8から、Windowsは0.16.9から。LinuxのClaude Codeには`--desktop`が無いので開きません。[ADR 0043](docs/adr/0043-claude-successor-opens-in-desktop.md))。Claude Codeは裏で動いている会話をDesktopへ移さないので、作業の間はDesktopには出ません。続けて引き継いだ時は、最後に作業を終えた後継だけを開きます。端末から始めた会話の後継は開きません。手で開く時は、`claude stop <id>`で裏の実行を停止し、`claude --desktop --resume <session-id>`を実行します。`session-id`は`claude agents --json --all`の`sessionId`です。環境変数`THROUGHLINE_AUTO_HANDOFF_OPEN`を`desktop`にすると出どころに関係なく開き、`off`にすると開きません。手動`/compact`、subagentの中の圧縮、`claude -p`などscriptから起動した会話は対象に含めません。
|
|
64
64
|
|
|
65
65
|
```bash
|
|
66
66
|
throughline auto-handoff status --host claude --operation <handoff-id> --json
|
|
@@ -35,7 +35,9 @@ macOS(Claude Code 2.1.289、Claude Desktop 2.26454.2)で確かめた事:
|
|
|
35
35
|
擬似端末の中で `claude --desktop --resume <session id>` を呼ぶ。出力の `Opening session <id>` と終了 code で成否を決める。
|
|
36
36
|
4. 後継が引き継ぎの途中(その後継自身の記録がある)なら移さない。作業は次の後継が続けるので、移すのは最後に作業を終えた
|
|
37
37
|
後継だけ。手すきにならないまま時間切れになった時は、止めずに戻り、次の Stop でやり直す。
|
|
38
|
-
5. 対応は macOS
|
|
38
|
+
5. 対応は macOS と Windows。macOS は擬似端末に `/usr/bin/script` を使う。Windows は PowerShell の
|
|
39
|
+
`Start-Process -Wait -PassThru` で新しい console(最小化)の中の pwsh に実行させ、その終了 code で成否を決める
|
|
40
|
+
(出た文は読めない)。Linux の Claude Code には `--desktop` が無いので、印を付けず、今までどおり裏の会話のままにする。
|
|
39
41
|
6. 環境変数 `THROUGHLINE_AUTO_HANDOFF_OPEN` で上書きできる。`desktop` は出どころに関係なく開き、`off` は開かない。
|
|
40
42
|
7. 旧い会話を止めた理由の文に、後継がターンを終えた時に Desktop へ開く事を書く。
|
|
41
43
|
8. 結果は記録の `desktop`(`state`: `requested`・`opened`・`superseded`・`failed`、`error_code`、`opened_at`)に残し、
|
|
@@ -61,8 +63,21 @@ macOS、Claude Code 2.1.289、Haiku 4.5、試験用フォルダ。端末から
|
|
|
61
63
|
- 1回の引き継ぎ: 後継が 22:00:59 に作業を終え、22:01:03 に Desktop が取り込んだ。
|
|
62
64
|
- 続けて3回の引き継ぎ: 最後の後継だけが移り(22:03:13)、途中の2つは移らなかった。
|
|
63
65
|
|
|
66
|
+
## Windows(2026-10-08 23:45〜23:57、0.16.9)
|
|
67
|
+
|
|
68
|
+
Claude Code 2.1.293、Claude Desktop 2.16120(MSIX)。画面のある session で確かめた(ssh は session 0 で、Desktop へ届かない)。
|
|
69
|
+
|
|
70
|
+
- 裏の会話の3つの状態は macOS と同じ。作業中と手すきは `That session is running in the background`(終了 code 1)、
|
|
71
|
+
`claude stop` の後は `Opening session <id> in Claude Desktop`(終了 code 0)で、数秒後に Desktop の会話の置き場
|
|
72
|
+
(`%LOCALAPPDATA%\Packages\Claude_…\LocalCache\Roaming\Claude\claude-code-sessions`)へ入った。
|
|
73
|
+
- `cmd /c start "" /min /wait claude …` は、batch の shim を `cmd /K` で開いて戻らない。内側に `cmd /c` を挟むと戻るが、
|
|
74
|
+
断られた時も終了 code は 0 だった。`Start-Process pwsh … -Wait -PassThru` の `ExitCode` は、断られた時 1、開けた時 0。
|
|
75
|
+
- 通し: 端末から始めた会話に `THROUGHLINE_AUTO_HANDOFF_OPEN=desktop` を付けた。続けて4回引き継ぎ、最後の後継が
|
|
76
|
+
23:56:26 に作業を終え、23:56:42 に Desktop が取り込んだ。途中の3つは移っていない。
|
|
77
|
+
|
|
78
|
+
Linux(Claude Code 2.1.293)は `--desktop isn't available on this platform. It works on macOS and Windows (x64).` で断る。
|
|
79
|
+
|
|
64
80
|
## 確かめていない範囲
|
|
65
81
|
|
|
66
82
|
- Claude Desktop の画面から始めた本物の会話での通し(印の付き方は、実物の transcript の値と単体の試験で確かめた)。
|
|
67
|
-
- Windows と Linux の Claude Desktop。
|
|
68
83
|
- 長いターンの後継と、後継が質問で止まった時の見え方。
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 外から後継へ文を送らない。権限のバイパス中の会話は、外から届いた文を
|
|
14
14
|
* 利用者の承認まで止めるため(0.15.3)。
|
|
15
15
|
* 5. 後継の Stop : Claude Desktop から始まった会話の後継は、そのターンの作業を終えた時に
|
|
16
|
-
* Desktop へ移して開く(`claude stop` → `claude --desktop --resume
|
|
16
|
+
* Desktop へ移して開く(`claude stop` → `claude --desktop --resume`。macOS と Windows)。
|
|
17
17
|
* Claude Code は、裏で動いている会話を Desktop へ移さない。
|
|
18
18
|
*
|
|
19
19
|
* 旧い会話は止めるだけで、空にしない。手動の /compact と subagent の中の圧縮は対象にしない。
|
|
@@ -210,10 +210,11 @@ function readLatestEntrypoint(transcriptPath) {
|
|
|
210
210
|
/**
|
|
211
211
|
* この会話の後継を、Claude Desktop で開くか。
|
|
212
212
|
* Desktop の画面から始まった会話と、その後継がさらに引き継ぐ時だけ開く。端末から始めた会話の後継は
|
|
213
|
-
* `claude agents` の一覧に出るので、Desktop
|
|
213
|
+
* `claude agents` の一覧に出るので、Desktop へは移さない。Claude Code の `--desktop` があるのは macOS と Windows だけ
|
|
214
|
+
* (Linux は `--desktop isn't available on this platform` で断る)。
|
|
214
215
|
*/
|
|
215
216
|
function openInDesktopWanted({ sessionId, transcriptPath, env, dir, platform }) {
|
|
216
|
-
if (
|
|
217
|
+
if (!['darwin', 'win32'].includes(platform) || env[OPEN_OVERRIDE_ENV] === 'off') return false;
|
|
217
218
|
if (env[OPEN_OVERRIDE_ENV] === 'desktop') return true;
|
|
218
219
|
if (env.CLAUDE_CODE_ENTRYPOINT === DESKTOP_ENTRYPOINT || readLatestEntrypoint(transcriptPath) === DESKTOP_ENTRYPOINT) return true;
|
|
219
220
|
return listClaudeAutoHandoffs({ dir }).some(record => record.successor?.session_id === sessionId && record.desktop?.wanted);
|
|
@@ -732,7 +733,7 @@ export async function requestClaudeDesktopOpen({ sessionId, dir = claudeAutoHand
|
|
|
732
733
|
*
|
|
733
734
|
* Claude Code は、裏で動いている会話(作業中も、手すきも)を Desktop へ移さない。手すきになるのを待って
|
|
734
735
|
* `claude stop` で止め、`claude --desktop --resume <session id>` で開く。この命令は出力が端末でないと動かないので、
|
|
735
|
-
*
|
|
736
|
+
* 擬似端末(macOS)か新しい console(Windows)の中で呼ぶ。後継がまた動き出した時は止めずに戻り、次の Stop でやり直す。
|
|
736
737
|
*/
|
|
737
738
|
export async function runClaudeDesktopOpen(sourceSessionId, {
|
|
738
739
|
dir = claudeAutoHandoffDir(),
|
|
@@ -765,7 +766,9 @@ export async function runClaudeDesktopOpen(sourceSessionId, {
|
|
|
765
766
|
if (stopped.error || stopped.status !== 0) return set({ state: 'failed', error_code: 'desktop_open_stop_failed' });
|
|
766
767
|
const opened = pty('claude', ['--desktop', '--resume', successorId], { cwd: record.project_path, env: childEnv, timeout: 60_000 });
|
|
767
768
|
const output = normalizeTerminalText(`${opened?.stdout ?? ''}${opened?.stderr ?? ''}`);
|
|
768
|
-
|
|
769
|
+
// Windows は新しい console の中で動かすので、出た文を読めない。終了 code で成否を見る。
|
|
770
|
+
const announced = opened?.outputUnavailable || output.includes(`Opening session ${successorId}`);
|
|
771
|
+
if (!opened || opened.error || opened.status !== 0 || !announced) {
|
|
769
772
|
// 理由を端末内のログに残す(命令の出力だけ。会話の中身は含まれない)。
|
|
770
773
|
process.stderr.write(`[auto-handoff] claude --desktop --resume exit=${opened?.status ?? 'unsupported'} output=${JSON.stringify(output.slice(-400))}\n`);
|
|
771
774
|
return set({ state: 'failed', error_code: opened ? 'desktop_open_failed' : 'desktop_open_platform_unsupported' });
|
|
@@ -620,8 +620,10 @@ test('PreCompact: Claude Desktop から始まった会話と、その後継の
|
|
|
620
620
|
assert.equal(wanted({ platform: 'darwin', env: { CLAUDE_CODE_ENTRYPOINT: 'claude-desktop' } }).wanted, true);
|
|
621
621
|
assert.equal(wanted({ platform: 'darwin', transcriptPath: cliTranscript }).wanted, false, '端末から始めた会話の後継は、claude agents の一覧に出る');
|
|
622
622
|
assert.equal(wanted({ platform: 'darwin' }).wanted, false);
|
|
623
|
-
assert.equal(wanted({ platform: 'linux', transcriptPath: desktopTranscript }).wanted, false, '
|
|
624
|
-
assert.equal(wanted({ platform: '
|
|
623
|
+
assert.equal(wanted({ platform: 'linux', transcriptPath: desktopTranscript }).wanted, false, 'Linux の Claude Code に --desktop は無い');
|
|
624
|
+
assert.equal(wanted({ platform: 'linux', transcriptPath: desktopTranscript, env: { THROUGHLINE_AUTO_HANDOFF_OPEN: 'desktop' } }).wanted, false);
|
|
625
|
+
assert.equal(wanted({ platform: 'win32', payload: { cwd: 'C:\\work\\app' }, env: { CLAUDE_CODE_ENTRYPOINT: 'claude-desktop' } }).wanted, true);
|
|
626
|
+
assert.equal(wanted({ platform: 'win32', payload: { cwd: 'C:\\work\\app' } }).wanted, false);
|
|
625
627
|
assert.equal(wanted({ platform: 'darwin', transcriptPath: cliTranscript, env: { THROUGHLINE_AUTO_HANDOFF_OPEN: 'desktop' } }).wanted, true);
|
|
626
628
|
assert.equal(wanted({ platform: 'darwin', transcriptPath: desktopTranscript, env: { THROUGHLINE_AUTO_HANDOFF_OPEN: 'off' } }).wanted, false);
|
|
627
629
|
|
|
@@ -721,6 +723,12 @@ test('Desktop で開く: 後継が手すきになるのを待って止め、擬
|
|
|
721
723
|
const refused = await run({ statuses: ['idle'], opened: { status: 1, stdout: `Session ${SUCCESSOR} is open in another terminal. Run /desktop there instead.\r\n`, stderr: '' } });
|
|
722
724
|
assert.deepEqual([refused.record.desktop.state, refused.record.desktop.error_code], ['failed', 'desktop_open_failed']);
|
|
723
725
|
|
|
726
|
+
// Windows は新しい console の中で動かすので、出た文を読めない。終了 code だけで成否を決める
|
|
727
|
+
const windowsOk = await run({ statuses: ['idle'], opened: { status: 0, stdout: '', stderr: '', outputUnavailable: true } });
|
|
728
|
+
assert.equal(windowsOk.record.desktop.state, 'opened');
|
|
729
|
+
const windowsRefused = await run({ statuses: ['idle'], opened: { status: 1, stdout: '', stderr: '', outputUnavailable: true } });
|
|
730
|
+
assert.deepEqual([windowsRefused.record.desktop.state, windowsRefused.record.desktop.error_code], ['failed', 'desktop_open_failed']);
|
|
731
|
+
|
|
724
732
|
// 対象でない記録には何もしない
|
|
725
733
|
writeRecord(dir, SESSION, {});
|
|
726
734
|
const untouched = await runClaudeDesktopOpen(SESSION, { dir, spawn: () => { throw new Error('must not run'); }, pty: () => { throw new Error('must not run'); } });
|
package/src/os/pty-run.mjs
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* os/pty-run.mjs — 端末を要求するコマンドを、端末の無い process から呼ぶ
|
|
3
3
|
*
|
|
4
|
-
* darwin: `script -q /dev/null <command> <args…>`
|
|
4
|
+
* darwin: `script -q /dev/null <command> <args…>` が擬似端末を作る。出力を読める。
|
|
5
|
+
* win32 : PowerShell の `Start-Process -Wait -PassThru` で、新しい console(最小化)の中の pwsh にコマンドを実行させ、
|
|
6
|
+
* その終了 code を受け取る。出力はその console に出るので読めない(`outputUnavailable: true`)。
|
|
7
|
+
* `cmd /c start /wait` は、中のコマンドが失敗しても 0 を返した(Windows で実測)ので使わない。
|
|
5
8
|
* 他の OS は未対応で、null を返す(呼び出し元が見送る)。
|
|
6
9
|
*/
|
|
7
10
|
import { spawnSync } from 'node:child_process';
|
|
8
11
|
|
|
12
|
+
const psQuote = value => `'${String(value).replaceAll("'", "''")}'`;
|
|
13
|
+
const psEncoded = script => Buffer.from(script, 'utf16le').toString('base64');
|
|
14
|
+
|
|
9
15
|
export function runWithPty(command, args, { platform = process.platform, spawnImpl = spawnSync, ...options } = {}) {
|
|
10
|
-
if (platform
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
if (platform === 'darwin') {
|
|
17
|
+
return spawnImpl('/usr/bin/script', ['-q', '/dev/null', command, ...args],
|
|
18
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], ...options });
|
|
19
|
+
}
|
|
20
|
+
if (platform === 'win32') {
|
|
21
|
+
const inner = `& ${[command, ...args].map(psQuote).join(' ')}; exit $LASTEXITCODE`;
|
|
22
|
+
const outer = `$p = Start-Process -FilePath pwsh.exe -ArgumentList @('-NoProfile','-EncodedCommand',${psQuote(psEncoded(inner))}) ` +
|
|
23
|
+
'-WindowStyle Minimized -Wait -PassThru; exit $p.ExitCode';
|
|
24
|
+
const result = spawnImpl('pwsh.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', psEncoded(outer)],
|
|
25
|
+
{ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, ...options });
|
|
26
|
+
return { ...result, outputUnavailable: true };
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
13
29
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { runWithPty } from './pty-run.mjs';
|
|
4
|
+
|
|
5
|
+
const decode = encoded => Buffer.from(encoded, 'base64').toString('utf16le');
|
|
6
|
+
|
|
7
|
+
test('runWithPty: macOS は script の擬似端末、Windows は新しい console の pwsh、ほかは未対応', () => {
|
|
8
|
+
const calls = [];
|
|
9
|
+
const spawnImpl = (command, args, options) => { calls.push({ command, args, options }); return { status: 0, stdout: 'ok', stderr: '' }; };
|
|
10
|
+
const mac = runWithPty('claude', ['--desktop', '--resume', 'id'], { platform: 'darwin', spawnImpl, cwd: '/p' });
|
|
11
|
+
assert.deepEqual([calls[0].command, calls[0].args], ['/usr/bin/script', ['-q', '/dev/null', 'claude', '--desktop', '--resume', 'id']]);
|
|
12
|
+
assert.deepEqual([calls[0].options.cwd, calls[0].options.stdio], ['/p', ['ignore', 'pipe', 'pipe']]);
|
|
13
|
+
assert.equal(mac.outputUnavailable, undefined);
|
|
14
|
+
|
|
15
|
+
const win = runWithPty('claude', ['--desktop', '--resume', "it's"], { platform: 'win32', spawnImpl, cwd: 'C:\\p' });
|
|
16
|
+
assert.equal(calls[1].command, 'pwsh.exe');
|
|
17
|
+
assert.deepEqual(calls[1].args.slice(0, 4), ['-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand']);
|
|
18
|
+
const outer = decode(calls[1].args[4]);
|
|
19
|
+
assert.match(outer, /^\$p = Start-Process -FilePath pwsh\.exe -ArgumentList @\('-NoProfile','-EncodedCommand','[A-Za-z0-9+/=]+'\) -WindowStyle Minimized -Wait -PassThru; exit \$p\.ExitCode$/);
|
|
20
|
+
// fox の実測(2026-10-08): 新しい console の中の終了 code は、断られた時 1、開けた時 0。
|
|
21
|
+
assert.equal(decode(/'-EncodedCommand','([A-Za-z0-9+/=]+)'/.exec(outer)[1]), "& 'claude' '--desktop' '--resume' 'it''s'; exit $LASTEXITCODE");
|
|
22
|
+
assert.deepEqual([calls[1].options.cwd, calls[1].options.windowsHide], ['C:\\p', true]);
|
|
23
|
+
assert.deepEqual([win.status, win.outputUnavailable], [0, true], 'console へ出た文は読めないので、終了 code で成否を見る');
|
|
24
|
+
|
|
25
|
+
assert.equal(runWithPty('claude', ['--desktop'], { platform: 'linux', spawnImpl }), null);
|
|
26
|
+
assert.equal(calls.length, 2);
|
|
27
|
+
});
|