Skip to content

Commit 362723d

Browse files
committed
Fix AI intro video system and safety check
- Increase signal file TTL from 30s to 60s (allows time for menu navigation) - Fix video path resolution using process.cwd() instead of __dirname - Add LIKU_SAFE_LAUNCH env var to bypass safety check in spawned windows - Tighten safety check to only trigger on TERM_PROGRAM=vscode - Update documentation with correct launch commands
1 parent faaf2cd commit 362723d

File tree

4 files changed

+60
-24
lines changed

4 files changed

+60
-24
lines changed

.github/copilot-instructions.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111
> 2. **THEN READ:** Game-specific docs in `docs/ai/` (e.g., `LIKU-CHESS.md`)
1212
>
1313
> The game MUST run in a **separate terminal window**, not in VS Code's integrated terminal.
14-
> See LIKU-CORE.md for the correct `Start-Process` command.
14+
>
15+
> **Correct command:**
16+
> ```powershell
17+
> Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd C:\dev\Liku-AI; `$env:LIKU_SAFE_LAUNCH='1'; node dist/index.js"
18+
> ```
1519
1620
---
1721

LIKU-CORE.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
```powershell
1010
# 1. Spawn game in separate window (REQUIRED)
11-
Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd C:\dev\Liku-AI; node dist/index.js"
11+
Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd C:\dev\Liku-AI; `$env:LIKU_SAFE_LAUNCH='1'; node dist/index.js"
1212
1313
# 2. Read state file to see current screen
1414
Get-Content .\likubuddy-state.txt
@@ -53,7 +53,7 @@ node send-command.js --key enter
5353
### 🪟 Windows
5454

5555
```powershell
56-
Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd C:\dev\Liku-AI; node dist/index.js"
56+
Start-Process pwsh -ArgumentList "-NoExit", "-Command", "cd C:\dev\Liku-AI; `$env:LIKU_SAFE_LAUNCH='1'; node dist/index.js"
5757
```
5858

5959
This spawns a window titled "LikuBuddy Game Window". All `send-keys.ps1` commands auto-target this window.
@@ -174,22 +174,22 @@ Register your identity before playing Chess to trigger your intro video:
174174

175175
**Windows:**
176176
```powershell
177-
# Signal file format: Line 1 = agent ID, Line 2 = timestamp (expires after 30s)
177+
# Signal file format: Line 1 = agent ID, Line 2 = timestamp (expires after 60s)
178178
New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\.liku-ai" | Out-Null
179179
$timestamp = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
180180
Set-Content -Path "$env:USERPROFILE\.liku-ai\current-agent.txt" -Value "claude`n$timestamp"
181181
```
182182

183183
**Linux/macOS:**
184184
```bash
185-
# Signal file format: Line 1 = agent ID, Line 2 = timestamp (expires after 30s)
185+
# Signal file format: Line 1 = agent ID, Line 2 = timestamp (expires after 60s)
186186
mkdir -p ~/.liku-ai
187187
echo -e "claude\n$(date +%s%3N)" > ~/.liku-ai/current-agent.txt
188188
```
189189

190190
**Supported IDs:** `claude`, `gemini`, `chatgpt`, `grok` (and aliases like `anthropic`, `openai`, `google`, etc.)
191191

192-
> **Note:** Signal files expire after 30 seconds to prevent stale agent detection from previous sessions.
192+
> **Note:** Signal files expire after 60 seconds to prevent stale agent detection from previous sessions.
193193
194194
### ⏳ Intro Video Handling (CRITICAL)
195195

src/index.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,28 +123,33 @@ const checkAISafetyPreFlight = (): boolean => {
123123
// Skip check if --ai flag is set (agent knows what it's doing)
124124
if (cli.flags.ai) return true;
125125

126-
// Skip check if explicitly disabled
126+
// Skip check if explicitly disabled (used by Start-Process command)
127127
if (process.env.LIKU_SKIP_SAFETY_CHECK === '1') return true;
128128

129+
// Skip if LIKU_SAFE_LAUNCH is set (inherited doesn't matter)
130+
if (process.env.LIKU_SAFE_LAUNCH === '1') return true;
131+
129132
// Check for VS Code integrated terminal indicators
130-
const isVSCodeTerminal = !!(
133+
// Note: Start-Process inherits env vars, so we need additional checks
134+
const hasVSCodeEnv = !!(
131135
process.env.VSCODE_INJECTION ||
132136
process.env.TERM_PROGRAM === 'vscode' ||
133137
process.env.VSCODE_GIT_IPC_HANDLE ||
134138
process.env.VSCODE_PID
135139
);
136140

137-
// Check for GitHub Copilot / AI tool indicators
138-
const isLikelyAISession = !!(
139-
process.env.GITHUB_COPILOT_AGENT ||
140-
process.env.CURSOR_SESSION ||
141-
process.env.AIDER_SESSION ||
142-
// Parent process check for AI tools running terminal commands
143-
process.ppid && process.env.VSCODE_PID
141+
// Check if this is truly an inline terminal (not spawned via Start-Process)
142+
// Start-Process creates a new console window, which has different characteristics
143+
const isInteractiveVSCodeTerminal = hasVSCodeEnv && (
144+
// Check if we're in the VS Code pseudo-terminal (PTY)
145+
process.env.TERM_PROGRAM === 'vscode' ||
146+
// Check for VS Code injection (extension host)
147+
process.env.VSCODE_INJECTION === '1'
144148
);
145149

146-
// If in VS Code terminal AND looks like AI session, warn and exit
147-
if (isVSCodeTerminal) {
150+
// If in VS Code's interactive terminal, warn and exit
151+
if (isInteractiveVSCodeTerminal) {
152+
const cwd = process.cwd().replace(/\\/g, '/');
148153
console.log(`
149154
╔══════════════════════════════════════════════════════════════════════╗
150155
║ ⚠️ LIKU-AI SAFETY CHECK ⚠️ ║
@@ -159,11 +164,9 @@ const checkAISafetyPreFlight = (): boolean => {
159164
║ CORRECT LAUNCH METHOD (Windows PowerShell): ║
160165
║ ║
161166
║ Start-Process pwsh -ArgumentList "-NoExit", "-Command", ║
162-
║ "cd ${process.cwd().replace(/\\/g, '/')}; node dist/index.js"
167+
║ "cd ${cwd}; \`$env:LIKU_SAFE_LAUNCH='1'; node dist/index.js" ║
163168
║ ║
164-
║ To bypass this check (humans only): ║
165-
║ - Set LIKU_SKIP_SAFETY_CHECK=1 environment variable ║
166-
║ - Or run with: node dist/index.js --ai ║
169+
║ To bypass (experienced users): node dist/index.js --ai ║
167170
║ ║
168171
╚══════════════════════════════════════════════════════════════════════╝
169172
`);

src/intro/IntroPlayer.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import { existsSync, readFileSync, writeFileSync, unlinkSync, mkdirSync, statSyn
3232
import path from 'path';
3333
import { fileURLToPath } from 'url';
3434

35-
// Signal file staleness threshold (30 seconds)
36-
const SIGNAL_FILE_TTL_MS = 30 * 1000;
35+
// Signal file staleness threshold (60 seconds - allows time for menu navigation)
36+
const SIGNAL_FILE_TTL_MS = 60 * 1000;
3737

3838
// ES module __dirname equivalent
3939
const __filename = fileURLToPath(import.meta.url);
@@ -334,6 +334,7 @@ export function detectAgent(): AIAgent | null {
334334
// Signal files older than SIGNAL_FILE_TTL_MS are considered stale
335335
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
336336
const signalFile = path.join(homeDir, '.liku-ai', 'current-agent.txt');
337+
337338
if (existsSync(signalFile)) {
338339
try {
339340
const fileContent = readFileSync(signalFile, 'utf-8').trim();
@@ -375,7 +376,9 @@ export function detectAgent(): AIAgent | null {
375376
* Get the absolute path to an intro video
376377
*/
377378
function getIntroVideoPath(agent: AIAgent): string | null {
378-
const projectRoot = path.resolve(__dirname, '..', '..');
379+
// __dirname is dist/intro/ when compiled, so go up to dist/, then to project root
380+
// Or use process.cwd() which is more reliable for project root
381+
const projectRoot = process.cwd();
379382

380383
// Try MP4 first, then GIF
381384
const mp4Path = path.join(projectRoot, agent.introVideo);
@@ -389,6 +392,14 @@ function getIntroVideoPath(agent: AIAgent): string | null {
389392
return gifPath;
390393
}
391394

395+
// Fallback: Try relative to __dirname (for backward compatibility)
396+
const fallbackRoot = path.resolve(__dirname, '..', '..');
397+
const fallbackPath = path.join(fallbackRoot, agent.introVideo);
398+
if (existsSync(fallbackPath)) {
399+
return fallbackPath;
400+
}
401+
402+
console.log(`[Intro] Video not found: ${mp4Path} or ${fallbackPath}`);
392403
return null;
393404
}
394405

@@ -397,21 +408,38 @@ function getIntroVideoPath(agent: AIAgent): string | null {
397408
* Returns the process info for state file notification
398409
*/
399410
export function playIntroVideo(agent: AIAgent): { pid: number | null; duration: number } {
411+
// Debug log helper
412+
const debugLog = (msg: string) => {
413+
try {
414+
const line = `[${new Date().toISOString()}] [playIntroVideo] ${msg}\n`;
415+
const debugPath = path.join(process.cwd(), 'intro-debug.txt');
416+
const existing = existsSync(debugPath) ? readFileSync(debugPath, 'utf-8') : '';
417+
writeFileSync(debugPath, existing + line);
418+
} catch { /* ignore */ }
419+
};
420+
421+
debugLog(`Called for agent: ${agent.id}`);
422+
400423
const videoPath = getIntroVideoPath(agent);
401424

425+
debugLog(`Video path resolved: ${videoPath || 'null'}`);
426+
402427
if (!videoPath) {
428+
debugLog(`No video file found for ${agent.name}`);
403429
console.log(`[Intro] No video file found for ${agent.name}`);
404430
return { pid: null, duration: 0 };
405431
}
406432

407433
console.log(`[Intro] Playing: ${videoPath}`);
434+
debugLog(`Playing video: ${videoPath}`);
408435

409436
const plat = platform();
410437
const duration = agent.introDuration;
411438
let pid: number | null = null;
412439

413440
try {
414441
if (plat === 'win32') {
442+
debugLog(`Platform: win32, using Start-Process`);
415443
// Windows: Use Start-Process (proven to work) and spawn separate closer
416444
// Step 1: Open video immediately with Start-Process
417445
exec(`powershell -Command "Start-Process '${videoPath.replace(/'/g, "''")}'"`);
@@ -425,6 +453,7 @@ export function playIntroVideo(agent: AIAgent): { pid: number | null; duration:
425453
}).unref();
426454

427455
pid = process.pid;
456+
debugLog(`Video launched with pid: ${pid}`);
428457

429458
} else if (plat === 'darwin') {
430459
// macOS: Use 'open' command and close after duration

0 commit comments

Comments
 (0)