setup-cc-room 0.2.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.
@@ -0,0 +1,129 @@
1
+ import {
2
+ resolveCcRoomDir
3
+ } from "./chunk-WCEABGOA.js";
4
+
5
+ // src/register-service.ts
6
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
7
+ import { join } from "path";
8
+ import { homedir, platform } from "os";
9
+ import { execFileSync } from "child_process";
10
+ var CC_ROOM_DIR = resolveCcRoomDir();
11
+ function getDaemonBinPath() {
12
+ try {
13
+ const which = execFileSync("which", ["cc-room-daemon"], { encoding: "utf-8", stdio: "pipe" }).trim();
14
+ if (which) return which;
15
+ } catch {
16
+ }
17
+ return join(CC_ROOM_DIR, "bin", "cc-room-daemon");
18
+ }
19
+ function ccRoomHomeEnvXml() {
20
+ if (!process.env.CC_ROOM_HOME) return "";
21
+ return `
22
+ <key>CC_ROOM_HOME</key>
23
+ <string>${process.env.CC_ROOM_HOME}</string>`;
24
+ }
25
+ function registerLaunchd() {
26
+ const plistDir = join(homedir(), "Library", "LaunchAgents");
27
+ const plistPath = join(plistDir, "dev.ccroom.daemon.plist");
28
+ const daemonBin = getDaemonBinPath();
29
+ const logPath = join(CC_ROOM_DIR, "logs", "daemon.log");
30
+ mkdirSync(join(CC_ROOM_DIR, "logs"), { recursive: true });
31
+ mkdirSync(plistDir, { recursive: true });
32
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
33
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
34
+ <plist version="1.0">
35
+ <dict>
36
+ <key>Label</key>
37
+ <string>dev.ccroom.daemon</string>
38
+ <key>ProgramArguments</key>
39
+ <array>
40
+ <string>${daemonBin}</string>
41
+ </array>
42
+ <key>KeepAlive</key>
43
+ <true/>
44
+ <key>RunAtLoad</key>
45
+ <true/>
46
+ <key>StandardOutPath</key>
47
+ <string>${logPath}</string>
48
+ <key>StandardErrorPath</key>
49
+ <string>${logPath}</string>
50
+ <key>EnvironmentVariables</key>
51
+ <dict>
52
+ <key>PATH</key>
53
+ <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>${ccRoomHomeEnvXml()}
54
+ </dict>
55
+ </dict>
56
+ </plist>
57
+ `;
58
+ writeFileSync(plistPath, plist, { mode: 420 });
59
+ try {
60
+ execFileSync("launchctl", ["unload", plistPath], { stdio: "pipe" });
61
+ } catch {
62
+ }
63
+ try {
64
+ execFileSync("launchctl", ["load", plistPath], { stdio: "pipe" });
65
+ console.log(` launchd \u306B\u767B\u9332\u3057\u307E\u3057\u305F: ${plistPath}`);
66
+ } catch (err) {
67
+ const msg = err instanceof Error ? err.message : String(err);
68
+ console.log(` \x1B[33m\u26A0 launchd \u767B\u9332\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${msg}\x1B[0m`);
69
+ console.log(` \u624B\u52D5\u3067\u767B\u9332: launchctl load ${plistPath}`);
70
+ }
71
+ }
72
+ function registerSystemd() {
73
+ const serviceDir = join(homedir(), ".config", "systemd", "user");
74
+ const servicePath = join(serviceDir, "cc-room-daemon.service");
75
+ const daemonBin = getDaemonBinPath();
76
+ const logPath = join(CC_ROOM_DIR, "logs", "daemon.log");
77
+ mkdirSync(join(CC_ROOM_DIR, "logs"), { recursive: true });
78
+ mkdirSync(serviceDir, { recursive: true });
79
+ const envLine = process.env.CC_ROOM_HOME ? `Environment=CC_ROOM_HOME=${process.env.CC_ROOM_HOME}
80
+ ` : "";
81
+ const unit = `[Unit]
82
+ Description=cc-room daemon
83
+ After=network.target
84
+
85
+ [Service]
86
+ ExecStart=${daemonBin}
87
+ Restart=on-failure
88
+ RestartSec=5
89
+ ${envLine}StandardOutput=append:${logPath}
90
+ StandardError=append:${logPath}
91
+
92
+ [Install]
93
+ WantedBy=default.target
94
+ `;
95
+ writeFileSync(servicePath, unit, { mode: 420 });
96
+ try {
97
+ execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
98
+ execFileSync("systemctl", ["--user", "enable", "--now", "cc-room-daemon"], { stdio: "pipe" });
99
+ console.log(` systemd \u306B\u767B\u9332\u3057\u307E\u3057\u305F: ${servicePath}`);
100
+ } catch (err) {
101
+ const msg = err instanceof Error ? err.message : String(err);
102
+ console.log(` \x1B[33m\u26A0 systemd \u767B\u9332\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${msg}\x1B[0m`);
103
+ console.log(` \u624B\u52D5\u3067\u767B\u9332: systemctl --user enable --now cc-room-daemon`);
104
+ }
105
+ }
106
+ async function registerService() {
107
+ const os = platform();
108
+ if (os === "darwin") {
109
+ registerLaunchd();
110
+ } else if (os === "linux") {
111
+ registerSystemd();
112
+ } else {
113
+ console.log(` \x1B[33m\u26A0 ${os} \u3067\u306E\u81EA\u52D5\u8D77\u52D5\u767B\u9332\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u624B\u52D5\u3067 cc-room-daemon \u3092\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044\u3002\x1B[0m`);
114
+ }
115
+ const pidPath = join(CC_ROOM_DIR, "daemon.pid");
116
+ if (!existsSync(pidPath)) {
117
+ console.log(" daemon \u306E\u8D77\u52D5\u3092\u5F85\u6A5F\u4E2D...");
118
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
119
+ }
120
+ if (existsSync(pidPath)) {
121
+ console.log(" daemon \u304C\u8D77\u52D5\u3057\u307E\u3057\u305F");
122
+ } else {
123
+ console.log(" \x1B[33m\u26A0 daemon \u306E\u81EA\u52D5\u8D77\u52D5\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u624B\u52D5\u3067 cc-room-daemon \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\x1B[0m");
124
+ }
125
+ }
126
+
127
+ export {
128
+ registerService
129
+ };
@@ -0,0 +1,10 @@
1
+ // src/paths.ts
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ function resolveCcRoomDir(env = process.env) {
5
+ return env.CC_ROOM_HOME || join(homedir(), ".cc-room");
6
+ }
7
+
8
+ export {
9
+ resolveCcRoomDir
10
+ };
@@ -0,0 +1,117 @@
1
+ // src/register-service.ts
2
+ import { writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { homedir, platform } from "os";
5
+ import { execFileSync } from "child_process";
6
+ var CC_ROOM_DIR = join(homedir(), ".cc-room");
7
+ function getDaemonBinPath() {
8
+ try {
9
+ const which = execFileSync("which", ["cc-room-daemon"], { encoding: "utf-8", stdio: "pipe" }).trim();
10
+ if (which) return which;
11
+ } catch {
12
+ }
13
+ return join(CC_ROOM_DIR, "bin", "cc-room-daemon");
14
+ }
15
+ function registerLaunchd() {
16
+ const plistDir = join(homedir(), "Library", "LaunchAgents");
17
+ const plistPath = join(plistDir, "dev.ccroom.daemon.plist");
18
+ const daemonBin = getDaemonBinPath();
19
+ const logPath = join(CC_ROOM_DIR, "logs", "daemon.log");
20
+ mkdirSync(join(CC_ROOM_DIR, "logs"), { recursive: true });
21
+ mkdirSync(plistDir, { recursive: true });
22
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
23
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
24
+ <plist version="1.0">
25
+ <dict>
26
+ <key>Label</key>
27
+ <string>dev.ccroom.daemon</string>
28
+ <key>ProgramArguments</key>
29
+ <array>
30
+ <string>${daemonBin}</string>
31
+ </array>
32
+ <key>KeepAlive</key>
33
+ <true/>
34
+ <key>RunAtLoad</key>
35
+ <true/>
36
+ <key>StandardOutPath</key>
37
+ <string>${logPath}</string>
38
+ <key>StandardErrorPath</key>
39
+ <string>${logPath}</string>
40
+ <key>EnvironmentVariables</key>
41
+ <dict>
42
+ <key>PATH</key>
43
+ <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin</string>
44
+ </dict>
45
+ </dict>
46
+ </plist>
47
+ `;
48
+ writeFileSync(plistPath, plist, { mode: 420 });
49
+ try {
50
+ execFileSync("launchctl", ["unload", plistPath], { stdio: "pipe" });
51
+ } catch {
52
+ }
53
+ try {
54
+ execFileSync("launchctl", ["load", plistPath], { stdio: "pipe" });
55
+ console.log(` launchd \u306B\u767B\u9332\u3057\u307E\u3057\u305F: ${plistPath}`);
56
+ } catch (err) {
57
+ const msg = err instanceof Error ? err.message : String(err);
58
+ console.log(` \x1B[33m\u26A0 launchd \u767B\u9332\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${msg}\x1B[0m`);
59
+ console.log(` \u624B\u52D5\u3067\u767B\u9332: launchctl load ${plistPath}`);
60
+ }
61
+ }
62
+ function registerSystemd() {
63
+ const serviceDir = join(homedir(), ".config", "systemd", "user");
64
+ const servicePath = join(serviceDir, "cc-room-daemon.service");
65
+ const daemonBin = getDaemonBinPath();
66
+ const logPath = join(CC_ROOM_DIR, "logs", "daemon.log");
67
+ mkdirSync(join(CC_ROOM_DIR, "logs"), { recursive: true });
68
+ mkdirSync(serviceDir, { recursive: true });
69
+ const unit = `[Unit]
70
+ Description=cc-room daemon
71
+ After=network.target
72
+
73
+ [Service]
74
+ ExecStart=${daemonBin}
75
+ Restart=on-failure
76
+ RestartSec=5
77
+ StandardOutput=append:${logPath}
78
+ StandardError=append:${logPath}
79
+
80
+ [Install]
81
+ WantedBy=default.target
82
+ `;
83
+ writeFileSync(servicePath, unit, { mode: 420 });
84
+ try {
85
+ execFileSync("systemctl", ["--user", "daemon-reload"], { stdio: "pipe" });
86
+ execFileSync("systemctl", ["--user", "enable", "--now", "cc-room-daemon"], { stdio: "pipe" });
87
+ console.log(` systemd \u306B\u767B\u9332\u3057\u307E\u3057\u305F: ${servicePath}`);
88
+ } catch (err) {
89
+ const msg = err instanceof Error ? err.message : String(err);
90
+ console.log(` \x1B[33m\u26A0 systemd \u767B\u9332\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${msg}\x1B[0m`);
91
+ console.log(` \u624B\u52D5\u3067\u767B\u9332: systemctl --user enable --now cc-room-daemon`);
92
+ }
93
+ }
94
+ async function registerService() {
95
+ const os = platform();
96
+ if (os === "darwin") {
97
+ registerLaunchd();
98
+ } else if (os === "linux") {
99
+ registerSystemd();
100
+ } else {
101
+ console.log(` \x1B[33m\u26A0 ${os} \u3067\u306E\u81EA\u52D5\u8D77\u52D5\u767B\u9332\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u624B\u52D5\u3067 cc-room-daemon \u3092\u8D77\u52D5\u3057\u3066\u304F\u3060\u3055\u3044\u3002\x1B[0m`);
102
+ }
103
+ const pidPath = join(CC_ROOM_DIR, "daemon.pid");
104
+ if (!existsSync(pidPath)) {
105
+ console.log(" daemon \u306E\u8D77\u52D5\u3092\u5F85\u6A5F\u4E2D...");
106
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
107
+ }
108
+ if (existsSync(pidPath)) {
109
+ console.log(" daemon \u304C\u8D77\u52D5\u3057\u307E\u3057\u305F");
110
+ } else {
111
+ console.log(" \x1B[33m\u26A0 daemon \u306E\u81EA\u52D5\u8D77\u52D5\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u624B\u52D5\u3067 cc-room-daemon \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\x1B[0m");
112
+ }
113
+ }
114
+
115
+ export {
116
+ registerService
117
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * setup パッケージ基準で cc-room-daemon 本体のパスを探す。
3
+ * 優先: vendor 同梱(npm / pack:vendor)→ ローカル dist-bundle
4
+ */
5
+ declare function resolveDaemonSource(setupPackageRoot: string): string | null;
6
+ declare const DAEMON_MISSING_HINT = "cc-room-daemon \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30EA\u30DD\u30B8\u30C8\u30EA\u3067\u306F `pnpm --filter setup-cc-room run pack:vendor` \u3092\u5B9F\u884C\u3059\u308B\u304B\u3001npm \u306E setup-cc-room \u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u4F7F\u3063\u3066\u304F\u3060\u3055\u3044\u3002";
7
+
8
+ export { DAEMON_MISSING_HINT, resolveDaemonSource };
@@ -0,0 +1,8 @@
1
+ import {
2
+ DAEMON_MISSING_HINT,
3
+ resolveDaemonSource
4
+ } from "./chunk-R5BAC4BK.js";
5
+ export {
6
+ DAEMON_MISSING_HINT,
7
+ resolveDaemonSource
8
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ install
4
+ } from "./chunk-QOXK3LB2.js";
5
+ import "./chunk-R5BAC4BK.js";
6
+ import "./chunk-UWFC4FU6.js";
7
+ import "./chunk-WCEABGOA.js";
8
+ import "./chunk-5NTML4PF.js";
9
+ import {
10
+ printResults,
11
+ validateAll
12
+ } from "./chunk-NJES6PZ3.js";
13
+
14
+ // src/index.ts
15
+ async function main() {
16
+ console.log("\n \u{1F3E0} setup-cc-room v0.2.0\n");
17
+ console.log(" Preflight checks \u3092\u5B9F\u884C\u4E2D...");
18
+ const { passed, results } = validateAll();
19
+ printResults(results);
20
+ if (!passed) {
21
+ console.log(" \x1B[31m\u524D\u63D0\u6761\u4EF6\u3092\u6E80\u305F\u3057\u3066\u3044\u307E\u305B\u3093\u3002\u4E0A\u8A18\u306E\u554F\u984C\u3092\u89E3\u6C7A\u3057\u3066\u304B\u3089\u518D\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002\x1B[0m\n");
22
+ process.exit(1);
23
+ }
24
+ console.log(" \x1B[32m\u5168\u30C1\u30A7\u30C3\u30AF\u901A\u904E\u3002\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3092\u958B\u59CB\u3057\u307E\u3059\u3002\x1B[0m\n");
25
+ await install();
26
+ console.log(`
27
+ \x1B[32m\u2705 \u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F\uFF01\x1B[0m
28
+
29
+ cc-room \u306E\u4F7F\u3044\u65B9:
30
+
31
+ \u3010\u4F1A\u8B70\u5BA4\u3092\u958B\u304F\u5834\u5408\u3011
32
+ 1. Claude Code \u3092\u518D\u8D77\u52D5\uFF08MCP \u3068 Hooks \u3092\u6709\u52B9\u5316\uFF09
33
+ 2. /room open <name> \u2190 \u4F1A\u8B70\u5BA4\u3092\u958B\u304F\uFF08PIN \u767A\u884C\uFF09
34
+ 3. \u8868\u793A\u3055\u308C\u308B\u90E8\u5C4B\u540D\u3068 PIN \u3092\u76F8\u624B\u306B\u53E3\u982D\u3084 DM \u3067\u4F1D\u3048\u308B
35
+ 4. \u76F8\u624B\u304C /room join <name> <PIN> \u3092\u5B9F\u884C\u3059\u308C\u3070\u53C2\u52A0\u5B8C\u4E86
36
+
37
+ \u3010\u62DB\u5F85\u3055\u308C\u305F\u5834\u5408\u3011
38
+ 1. Claude Code \u3092\u518D\u8D77\u52D5
39
+ 2. /room join <name> <PIN> \u2190 \u76F8\u624B\u304B\u3089\u53D7\u3051\u53D6\u3063\u305F\u5024\u3092\u5165\u529B
40
+
41
+ \u3010\u65E5\u5E38\u64CD\u4F5C\u3011
42
+ /room \u2026 \u30DB\u30EF\u30A4\u30C8\u30DC\u30FC\u30C9\u3092\u898B\u308B
43
+ /private \u2026 \u72B6\u614B\u8868\u793A\u3001\u624B\u5143/\u516C\u958B\u306E\u5207\u66FF\uFF08on|off|share|drop\uFF09
44
+
45
+ \u26A0\uFE0F cc-room \u306F\u81EA\u52D5\u3067\u76F8\u624B\u306B\u901A\u77E5\u3057\u307E\u305B\u3093\u3002
46
+ \u90E8\u5C4B\u540D\u3068 PIN \u306F\u81EA\u5206\u3067\u76F8\u624B\u306B\u4F1D\u3048\u3066\u304F\u3060\u3055\u3044\u3002
47
+
48
+ \u63A5\u7D9A\u5F8C\u306F\u540C\u3058 WiFi\uFF08LAN\uFF09\u5185\u306B\u3044\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002
49
+ \uFF08VPN \u3067\u540C\u4E00 LAN \u76F8\u5F53\u306B\u306A\u308C\u3070\u30EA\u30E2\u30FC\u30C8\u3067\u3082\u4F7F\u3048\u307E\u3059\uFF09
50
+
51
+ \u30C9\u30AD\u30E5\u30E1\u30F3\u30C8: https://github.com/takanorisuzuki/cc-room
52
+ `);
53
+ }
54
+ main().catch((err) => {
55
+ console.error(" \x1B[31m\u30A8\u30E9\u30FC:\x1B[0m", err instanceof Error ? err.message : err);
56
+ process.exit(1);
57
+ });
@@ -0,0 +1,3 @@
1
+ declare function install(): Promise<void>;
2
+
3
+ export { install };
@@ -0,0 +1,10 @@
1
+ import {
2
+ install
3
+ } from "./chunk-QOXK3LB2.js";
4
+ import "./chunk-R5BAC4BK.js";
5
+ import "./chunk-UWFC4FU6.js";
6
+ import "./chunk-WCEABGOA.js";
7
+ import "./chunk-5NTML4PF.js";
8
+ export {
9
+ install
10
+ };
@@ -0,0 +1,4 @@
1
+ /** daemon と同じく CC_ROOM_HOME を尊重する */
2
+ declare function resolveCcRoomDir(env?: NodeJS.ProcessEnv): string;
3
+
4
+ export { resolveCcRoomDir };
package/dist/paths.js ADDED
@@ -0,0 +1,6 @@
1
+ import {
2
+ resolveCcRoomDir
3
+ } from "./chunk-WCEABGOA.js";
4
+ export {
5
+ resolveCcRoomDir
6
+ };
@@ -0,0 +1,3 @@
1
+ declare function registerService(): Promise<void>;
2
+
3
+ export { registerService };
@@ -0,0 +1,7 @@
1
+ import {
2
+ registerService
3
+ } from "./chunk-UWFC4FU6.js";
4
+ import "./chunk-WCEABGOA.js";
5
+ export {
6
+ registerService
7
+ };
@@ -0,0 +1,3 @@
1
+ declare function mergeSettings(): void;
2
+
3
+ export { mergeSettings };
@@ -0,0 +1,6 @@
1
+ import {
2
+ mergeSettings
3
+ } from "./chunk-5NTML4PF.js";
4
+ export {
5
+ mergeSettings
6
+ };
@@ -0,0 +1,12 @@
1
+ interface ValidationResult {
2
+ ok: boolean;
3
+ message: string;
4
+ hint?: string;
5
+ }
6
+ declare function validateAll(): {
7
+ passed: boolean;
8
+ results: ValidationResult[];
9
+ };
10
+ declare function printResults(results: ValidationResult[]): void;
11
+
12
+ export { printResults, validateAll };
@@ -0,0 +1,8 @@
1
+ import {
2
+ printResults,
3
+ validateAll
4
+ } from "./chunk-NJES6PZ3.js";
5
+ export {
6
+ printResults,
7
+ validateAll
8
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "setup-cc-room",
3
+ "version": "0.2.0",
4
+ "description": "Install cc-room — bring your Claude Code into a shared LAN meeting room",
5
+ "license": "MIT",
6
+ "author": "Takanori Suzuki",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/takanorisuzuki/cc-room.git",
10
+ "directory": "packages/setup"
11
+ },
12
+ "type": "module",
13
+ "bin": {
14
+ "setup-cc-room": "dist/index.js"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "vendor"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup src/index.ts src/installer.ts src/settings-merger.ts src/register-service.ts src/validators.ts src/daemon-resolver.ts src/paths.ts --format esm --dts",
22
+ "dev": "tsup src/index.ts --format esm --watch",
23
+ "pack:vendor": "node scripts/pack-vendor.mjs",
24
+ "prepack": "pnpm run build && pnpm run pack:vendor",
25
+ "typecheck": "tsc --noEmit",
26
+ "clean": "rm -rf dist vendor"
27
+ },
28
+ "dependencies": {
29
+ "yaml": "^2.4.0"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.5.0",
33
+ "tsup": "^8.1.0"
34
+ }
35
+ }
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: private
3
+ description: 手元モードの ON/OFF を切り替える。OFF に戻すときは溜まった作業を share(送る)/ drop(捨てる)から毎回選択する。
4
+ ---
5
+
6
+ 手元(Private)/ 公開を切り替えるコマンド。公開中(Private OFF)は Primary ルームへサマリー・成果物が自動で流れる。Private ON 中の執筆は pending に蓄積され、誰にも見えない。
7
+
8
+ ## ポートの取得
9
+
10
+ すべての HTTP リクエスト前に、以下でポートを取得する:
11
+
12
+ ```bash
13
+ CC_ROOM_HOME="${CC_ROOM_HOME:-$HOME/.cc-room}"
14
+ HTTP_PORT=$(grep 'http_port:' "$CC_ROOM_HOME/config.yaml" 2>/dev/null | awk '{print $2}' | tr -d '[:space:]')
15
+ HTTP_PORT="${HTTP_PORT:-7332}"
16
+ BASE_URL="http://127.0.0.1:$HTTP_PORT"
17
+ ```
18
+
19
+ ## サブコマンド
20
+
21
+ ### `/private`(引数なし)— 状態表示
22
+
23
+ 1. `GET $BASE_URL/status` を取得する。
24
+ 2. 以下の形式で表示する:
25
+ ```
26
+ 🔒 Private ON(手元モード) / 📡 公開中(Private OFF)
27
+ Primary: <primary_room_name>
28
+ 手元に溜まっている作業: <pending_turns + pending_files> 件
29
+ ```
30
+
31
+ ### `/private on` — 手元モードへ
32
+
33
+ 1. `POST $BASE_URL/private` に `{ "mode": "on" }` を送信する。
34
+ 2. 成功した場合:
35
+ ```
36
+ 🔒 手元モードにしました。作業は誰にも見えません(/private off で公開に戻す)
37
+ ```
38
+
39
+ ### `/private off` — 公開へ戻る
40
+
41
+ 1. `POST $BASE_URL/private` に `{ "mode": "off" }` を送信する。
42
+ 2. レスポンスで分岐する:
43
+ - `needs_choice: true` の場合、**必ずユーザーに確認する**(自動で share しない):
44
+ ```
45
+ 手元に <pending> 件あります。Primary(<primary_room_name>)へ送りますか?
46
+ → share(送ってから公開に戻る) / drop(捨てて公開に戻る)
47
+ ```
48
+ ユーザーの選択に応じて `/private share` または `/private drop` を実行する。
49
+ - `needs_choice` がない場合(pending 0 件):
50
+ ```
51
+ 📡 公開に戻りました。作業サマリーが Primary(<primary_room_name>)に流れます
52
+ ```
53
+
54
+ ### `/private share` — pending を送ってから公開へ
55
+
56
+ 1. `POST $BASE_URL/private` に `{ "mode": "share" }` を送信する。
57
+ 2. 成功した場合:
58
+ ```
59
+ 📡 手元の <shared> 件を Primary(<primary_room_name>)へ共有して公開に戻りました
60
+ ```
61
+
62
+ ### `/private drop` — pending を捨てて公開へ
63
+
64
+ 1. `POST $BASE_URL/private` に `{ "mode": "drop" }` を送信する。
65
+ 2. 成功した場合:
66
+ ```
67
+ 🗑 手元の <dropped> 件を破棄して公開に戻りました
68
+ ```
69
+
70
+ ## エラー処理
71
+
72
+ - daemon が起動していない場合:
73
+ 「cc-room daemon が起動していません。」
74
+ - ルームに参加していない場合:
75
+ 「ルームに参加していません。`/room open` または `/room join` で参加してください。」