chore: rename Claudeman to Codeman

Full product rename across 109 files (~834 occurrences):
- Env vars: CLAUDEMAN_* → CODEMAN_*
- Data dirs: ~/.claudeman/ → ~/.codeman/, ~/claudeman-cases/ → ~/codeman-cases/
- tmux prefix: claudeman- → codeman-
- localStorage: claudeman-* → codeman-*
- Package/CLI: claudeman → codeman
- GitHub repo: Ark0N/Claudeman → Ark0N/Codeman
- systemd service: claudeman-web → codeman-web
- Class: ClaudemanApp → CodemanApp

Migration infrastructure for seamless transition:
- state-store.ts: auto-migrates data directories on startup
- tmux-manager.ts: dual-prefix detection (legacy claudeman- sessions)
- app.js: localStorage key migration (preserves old keys)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-02-26 16:44:34 +01:00
co-authored by Claude Opus 4.6
parent 3ccd22161c
commit 3795c45cc1
109 changed files with 711 additions and 679 deletions
+8 -8
View File
@@ -2,7 +2,7 @@
/**
* Browser Framework Comparison Script
*
* Compares Playwright, Puppeteer, and Agent-Browser for testing Claudeman web UI.
* Compares Playwright, Puppeteer, and Agent-Browser for testing Codeman web UI.
* Run: node scripts/browser-comparison.mjs
*/
@@ -88,7 +88,7 @@ async function main() {
const page = await playwrightBrowser.newPage();
await page.goto(`http://localhost:${PORTS.playwright}`);
const title = await page.title();
if (title !== 'Claudeman') throw new Error(`Expected Claudeman, got ${title}`);
if (title !== 'Codeman') throw new Error(`Expected Codeman, got ${title}`);
await page.close();
});
@@ -98,7 +98,7 @@ async function main() {
await page.goto(`http://localhost:${PORTS.playwright}`);
const logo = await page.locator('.header-brand .logo').textContent();
const version = await page.locator('#versionDisplay').textContent();
if (logo !== 'Claudeman') throw new Error('Logo mismatch');
if (logo !== 'Codeman') throw new Error('Logo mismatch');
if (!version.match(/v\d+/)) throw new Error('Version mismatch');
await page.close();
});
@@ -149,7 +149,7 @@ async function main() {
const page = await puppeteerBrowser.newPage();
await page.goto(`http://localhost:${PORTS.puppeteer}`);
const title = await page.title();
if (title !== 'Claudeman') throw new Error(`Expected Claudeman, got ${title}`);
if (title !== 'Codeman') throw new Error(`Expected Codeman, got ${title}`);
await page.close();
});
@@ -159,7 +159,7 @@ async function main() {
await page.goto(`http://localhost:${PORTS.puppeteer}`);
const logo = await page.$eval('.header-brand .logo', el => el.textContent);
const version = await page.$eval('#versionDisplay', el => el.textContent);
if (logo !== 'Claudeman') throw new Error('Logo mismatch');
if (logo !== 'Codeman') throw new Error('Logo mismatch');
if (!version.match(/v\d+/)) throw new Error('Version mismatch');
await page.close();
});
@@ -202,7 +202,7 @@ async function main() {
agentBrowser(`open http://localhost:${PORTS.agentBrowser}`);
await new Promise(r => setTimeout(r, 2000));
const title = agentBrowserJson('get title');
agentBrowserAvailable = title.title === 'Claudeman';
agentBrowserAvailable = title.title === 'Codeman';
console.log(' Browser launched');
// Test 1: Page load
@@ -210,14 +210,14 @@ async function main() {
agentBrowser(`open http://localhost:${PORTS.agentBrowser}`);
await new Promise(r => setTimeout(r, 1000));
const title = agentBrowserJson('get title');
if (title.title !== 'Claudeman') throw new Error(`Expected Claudeman, got ${title.title}`);
if (title.title !== 'Codeman') throw new Error(`Expected Codeman, got ${title.title}`);
});
// Test 2: Element selection
await runBenchmark('element-selection', 'agent-browser', async () => {
const logo = agentBrowserJson('get text ".header-brand .logo"');
const version = agentBrowserJson('get text "#versionDisplay"');
if (logo.text !== 'Claudeman') throw new Error('Logo mismatch');
if (logo.text !== 'Codeman') throw new Error('Logo mismatch');
if (!version.text.match(/v\d+/)) throw new Error('Version mismatch');
});
+10 -10
View File
@@ -98,7 +98,7 @@ function makeSession(id, name, mode, status, extra = {}) {
id,
pid: status === 'idle' ? null : 12345 + Math.floor(Math.random() * 10000),
status,
workingDir: '/home/arkon/claudeman-cases/testcase',
workingDir: '/home/arkon/codeman-cases/testcase',
currentTaskId: null,
createdAt: Date.now() - 3600000,
lastActivityAt: Date.now() - (status === 'idle' ? 60000 : 5000),
@@ -336,7 +336,7 @@ const TERMINAL_INIT = [
'',
` ${RED}████${RST} ${BOLD}Claude Code${RST} v2.1.47`,
` ${RED}████${RST} ${GRY}Opus 4.6${RST} ${DIM}·${RST} ${GRY}Claude Max${RST}`,
` ${RED}██${RST} ${RED}██${RST} ${GRY}~/claudeman-cases/testcase${RST}`,
` ${RED}██${RST} ${RED}██${RST} ${GRY}~/codeman-cases/testcase${RST}`,
'',
'',
`${GRY}❯ Try "how do I log an error?"${RST}`,
@@ -353,7 +353,7 @@ const TERMINAL_WORKING = [
`${GRN}●${RST} The codebase is essentially a template/scaffold with just a CLAUDE.md file and empty ${BOLD}src/${RST} directory. Since there's already a comprehensive`,
`CLAUDE.md file present, let me read it fully and suggest improvements based on the /init command requirements:`,
'',
`${GRN}●${RST} ${BOLD}Read${RST}(/home/arkon/claudeman-cases/testcase/CLAUDE.md)`,
`${GRN}●${RST} ${BOLD}Read${RST}(/home/arkon/codeman-cases/testcase/CLAUDE.md)`,
` ${GRY}░${RST} Read ${BOLD}407${RST} lines`,
'',
`${GRN}●${RST} Now I understand the situation. This is a ${BOLD}template CLAUDE.md file${RST} designed for the Claude Code framework with the "Ralph Loop" autonomous work`,
@@ -420,15 +420,15 @@ const TERMINAL_RALPH = [
const SUBAGENT_ACTIVITY = {
'agent-001': [
{ type: 'tool', tool: 'Glob', input: { pattern: 'src/**/*.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/claudeman/src/web/server.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/src/web/server.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'tool', tool: 'Grep', input: { pattern: 'app\\.get|app\\.post|app\\.put|app\\.delete', path: 'src/' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/claudeman/src/web/schemas.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/src/web/schemas.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-001' },
{ type: 'message', role: 'assistant', text: 'Found 47 API endpoints across server.ts. Documenting REST paths...', timestamp: new Date().toISOString(), agentId: 'agent-001' },
],
'agent-002': [
{ type: 'tool', tool: 'Glob', input: { pattern: 'test/**/*.test.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/claudeman/test/respawn-test-utils.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/claudeman/vitest.config.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/test/respawn-test-utils.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/vitest.config.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'message', role: 'assistant', text: 'Analyzing test patterns: MockSession, unique ports, fileParallelism: false...', timestamp: new Date().toISOString(), agentId: 'agent-002' },
],
};
@@ -436,9 +436,9 @@ const SUBAGENT_ACTIVITY = {
// Subagent spawn terminal content
const TERMINAL_SUBAGENT = [
'',
`${GRN}●${RST} Working on ${CYN}/home/arkon/claudeman-cases/testcase${RST} - I'll use the ${BOLD}Task tool${RST} to spawn parallel agents.`,
`${GRN}●${RST} Working on ${CYN}/home/arkon/codeman-cases/testcase${RST} - I'll use the ${BOLD}Task tool${RST} to spawn parallel agents.`,
'',
`${GRN}●${RST} ${BOLD}Read${RST}(/home/arkon/claudeman-cases/testcase/CLAUDE.md)`,
`${GRN}●${RST} ${BOLD}Read${RST}(/home/arkon/codeman-cases/testcase/CLAUDE.md)`,
` ${GRY}░${RST} Read ${BOLD}127${RST} lines ${GRY}│${RST} ${CYN}1.2KB${RST}`,
'',
`${GRN}●${RST} ${BOLD}Bash${RST}(find . -name "*.ts" -not -path "*/node_modules/*" | head -20)`,
@@ -864,7 +864,7 @@ async function captureMultiSessionMonitor(page) {
async function main() {
console.log('='.repeat(60));
console.log('Claudeman README Screenshot Capture');
console.log('Codeman README Screenshot Capture');
console.log('='.repeat(60));
console.log(`Port: ${PORT} | Viewport: ${VIEWPORT.width}x${VIEWPORT.height}`);
console.log('');
+6 -6
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Capture a GIF of subagent spawning on the LIVE Claudeman server.
* Capture a GIF of subagent spawning on the LIVE Codeman server.
*
* Connects to the running server on port 3000, creates a test session,
* sends a prompt that spawns 3 parallel agents, and captures a tight
@@ -69,7 +69,7 @@ async function capture() {
try {
// Create case and seed with real source files
console.log(`Creating case: ${CASE_NAME}`);
const caseDir = join(process.env.HOME, 'claudeman-cases', CASE_NAME);
const caseDir = join(process.env.HOME, 'codeman-cases', CASE_NAME);
const caseSrc = join(caseDir, 'src');
const caseTest = join(caseDir, 'test');
mkdirSync(caseSrc, { recursive: true });
@@ -123,7 +123,7 @@ async function capture() {
// Configure settings: enable subagents, disable Files pane
await setupPage.evaluate(() => {
const existing = JSON.parse(localStorage.getItem('claudeman-app-settings') || '{}');
const existing = JSON.parse(localStorage.getItem('codeman-app-settings') || '{}');
Object.assign(existing, {
showSubagents: true,
subagentTrackingEnabled: true,
@@ -132,7 +132,7 @@ async function capture() {
showProjectInsights: false,
showFileBrowser: false,
});
localStorage.setItem('claudeman-app-settings', JSON.stringify(existing));
localStorage.setItem('codeman-app-settings', JSON.stringify(existing));
});
await setupPage.reload({ waitUntil: 'domcontentloaded' });
await sleep(1000);
@@ -227,7 +227,7 @@ async function capture() {
// Re-apply settings (new context = fresh localStorage)
await recordPage.evaluate(() => {
const existing = JSON.parse(localStorage.getItem('claudeman-app-settings') || '{}');
const existing = JSON.parse(localStorage.getItem('codeman-app-settings') || '{}');
Object.assign(existing, {
showSubagents: true,
subagentTrackingEnabled: true,
@@ -236,7 +236,7 @@ async function capture() {
showProjectInsights: false,
showFileBrowser: false,
});
localStorage.setItem('claudeman-app-settings', JSON.stringify(existing));
localStorage.setItem('codeman-app-settings', JSON.stringify(existing));
});
await recordPage.reload({ waitUntil: 'domcontentloaded' });
await sleep(1000);
+8 -8
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Capture screenshots and GIFs of Claudeman's subagent visualization
* Capture screenshots and GIFs of Codeman's subagent visualization
*
* Uses real Claude sessions that spawn actual subagents via the Task tool.
*
@@ -23,7 +23,7 @@ import { mkdirSync, existsSync, unlinkSync, rmSync } from 'fs';
const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = join(__dirname, '..');
const OUTPUT_DIR = join(PROJECT_ROOT, 'docs', 'images');
const CASES_DIR = join(process.env.HOME, 'claudeman-cases');
const CASES_DIR = join(process.env.HOME, 'codeman-cases');
// Configuration
const PORT = 3198; // Unique port for capture script (avoid 3199 conflicts)
@@ -58,10 +58,10 @@ let serverProcess = null;
let browser = null;
/**
* Start the Claudeman web server
* Start the Codeman web server
*/
async function startServer() {
console.log(`Starting Claudeman server on port ${PORT}...`);
console.log(`Starting Codeman server on port ${PORT}...`);
serverProcess = spawn('npx', ['tsx', 'src/index.ts', 'web', '--port', String(PORT)], {
cwd: PROJECT_ROOT,
@@ -222,7 +222,7 @@ async function configureSettings(page) {
subagentActiveTabOnly: false, // Show all subagents regardless of active tab
showMonitor: true,
};
localStorage.setItem('claudeman-app-settings', JSON.stringify(settings));
localStorage.setItem('codeman-app-settings', JSON.stringify(settings));
});
await page.reload({ waitUntil: 'domcontentloaded' });
await sleep(500);
@@ -292,7 +292,7 @@ function convertToGif(inputPath, outputPath) {
*/
async function capture() {
console.log('='.repeat(60));
console.log('Claudeman Subagent Screenshot Capture');
console.log('Codeman Subagent Screenshot Capture');
console.log('='.repeat(60));
console.log(`Output directory: ${OUTPUT_DIR}`);
console.log(`Case name: ${CASE_NAME}`);
@@ -323,8 +323,8 @@ async function capture() {
const page = await context.newPage();
page.setDefaultTimeout(30000);
// Navigate to Claudeman
console.log('Loading Claudeman...');
// Navigate to Codeman
console.log('Loading Codeman...');
await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' });
await sleep(1000);
@@ -1,10 +1,10 @@
[Unit]
Description=Claudeman Web Server
Description=Codeman Web Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/arkon/default/claudeman
WorkingDirectory=/home/arkon/default/codeman
ExecStart=/usr/bin/node dist/index.js web --https
Restart=always
RestartSec=5
@@ -15,7 +15,7 @@ Environment=HOME=/home/arkon
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=claudeman
SyslogIdentifier=codeman
# Resource limits
LimitNOFILE=65536
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Take mobile screenshots of Claudeman using Playwright
* Take mobile screenshots of Codeman using Playwright
* Emulates iPhone 15 Pro (closest to iPhone 17 Pro)
* Includes keyboard simulation
*/
+3 -3
View File
@@ -4,8 +4,8 @@
SESSION_ID="${1:-5ecdf859-3605-42d8-b0a2-f8f0a1b5ff98}"
DURATION_HOURS="${2:-12}"
ISSUES_FILE="/home/arkon/default/claudeman/respawn-monitor-issues.md"
FIXES_FILE="/home/arkon/default/claudeman/respawn-monitor-fixes.md"
ISSUES_FILE="/home/arkon/default/codeman/respawn-monitor-issues.md"
FIXES_FILE="/home/arkon/default/codeman/respawn-monitor-fixes.md"
API_URL="https://localhost:3000"
POLL_INTERVAL=60 # seconds
@@ -54,7 +54,7 @@ while [ $(date +%s) -lt $END_TIME ]; do
if [ -z "$RESP" ]; then
log_issue "ERROR" "API not responding - server may be down"
add_fix "API not responding" "Check if claudeman-web service is running:\n\`\`\`bash\nsystemctl --user status claudeman-web\nsystemctl --user restart claudeman-web\n\`\`\`"
add_fix "API not responding" "Check if codeman-web service is running:\n\`\`\`bash\nsystemctl --user status codeman-web\nsystemctl --user restart codeman-web\n\`\`\`"
sleep $POLL_INTERVAL
continue
fi
+4 -4
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env node
/**
* Claudeman postinstall verification script
* Codeman postinstall verification script
* Runs after `npm install` to check environment readiness
*/
@@ -94,7 +94,7 @@ function getTmuxInstallInstructions() {
// Main Checks
// ============================================================================
console.log(colors.bold('Claudeman postinstall check...'));
console.log(colors.bold('Codeman postinstall check...'));
console.log('');
let hasWarnings = false;
@@ -259,11 +259,11 @@ if (hasErrors) {
console.log(colors.bold('Next steps:'));
if (isGlobalInstall) {
console.log(colors.dim(' 1. Start: ') + colors.cyan('claudeman web'));
console.log(colors.dim(' 1. Start: ') + colors.cyan('codeman web'));
console.log(colors.dim(' 2. Open: ') + colors.cyan('http://localhost:3000'));
} else {
console.log(colors.dim(' 1. Build: ') + colors.cyan('npm run build'));
console.log(colors.dim(' 2. Start: ') + colors.cyan('claudeman web'));
console.log(colors.dim(' 2. Start: ') + colors.cyan('codeman web'));
console.log(colors.dim(' 3. Open: ') + colors.cyan('http://localhost:3000'));
}
+4 -4
View File
@@ -8,7 +8,7 @@ import { join } from 'node:path';
import { homedir } from 'node:os';
const BASE_URL = 'https://localhost:3000';
const CASE_NAME = 'claudeman-ios';
const CASE_NAME = 'codeman-ios';
async function main() {
console.log('🎬 Starting Ralph Loop Wizard demo...\n');
@@ -25,7 +25,7 @@ async function main() {
});
const page = await context.newPage();
console.log('📱 Opening Claudeman web UI...');
console.log('📱 Opening Codeman web UI...');
await page.goto(BASE_URL);
await page.waitForLoadState('networkidle');
@@ -41,8 +41,8 @@ async function main() {
await page.selectOption('#ralphCaseSelect', CASE_NAME);
// Load initprompt.md if it exists
const initPromptPath = join(homedir(), 'claudeman-cases', CASE_NAME, 'initprompt.md');
let taskDescription = 'Build an awesome iOS app for Claudeman!';
const initPromptPath = join(homedir(), 'codeman-cases', CASE_NAME, 'initprompt.md');
let taskDescription = 'Build an awesome iOS app for Codeman!';
if (existsSync(initPromptPath)) {
taskDescription = readFileSync(initPromptPath, 'utf-8').trim();
console.log(`📝 Loaded initprompt.md (${taskDescription.length} chars)`);
+5 -5
View File
@@ -10,8 +10,8 @@ import { join } from 'node:path';
import { homedir } from 'node:os';
const BASE_URL = 'https://localhost:3000';
const CASE_NAME = 'claudeman-ios';
const CASE_PATH = join(homedir(), 'claudeman-cases', CASE_NAME);
const CASE_NAME = 'codeman-ios';
const CASE_PATH = join(homedir(), 'codeman-cases', CASE_NAME);
async function main() {
// Read initprompt.md
@@ -49,7 +49,7 @@ async function main() {
page.on('pageerror', err => console.log(`[Browser Error] ${err.message}`));
try {
console.log('[1/10] Navigating to Claudeman...');
console.log('[1/10] Navigating to Codeman...');
await page.goto(BASE_URL, { timeout: 10000 });
console.log('[2/10] Clicking Ralph Loop button (.btn-ralph)...');
@@ -122,7 +122,7 @@ async function main() {
});
session = sessions
.filter(s => s.workingDir?.includes('claudeman-ios'))
.filter(s => s.workingDir?.includes('codeman-ios'))
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))[0];
if (session) {
@@ -151,7 +151,7 @@ async function main() {
console.log('SUCCESS! Ralph Loop started.');
console.log('========================================');
console.log(`Session ID: ${session?.id}`);
console.log(`Screen: claudeman-${session?.id?.slice(0, 8)}`);
console.log(`Screen: codeman-${session?.id?.slice(0, 8)}`);
console.log(`Status: ${session?.status}`);
console.log('');
console.log('Session is now running. View at: https://localhost:3000');
+6 -6
View File
@@ -8,7 +8,7 @@ import { join } from 'node:path';
import { homedir } from 'node:os';
const BASE_URL = 'https://localhost:3000';
const CASE_NAME = 'claudeman-ios';
const CASE_NAME = 'codeman-ios';
async function main() {
console.log('Starting Ralph Loop via wizard on production server...\n');
@@ -21,7 +21,7 @@ async function main() {
const context = await browser.newContext({ ignoreHTTPSErrors: true });
const page = await context.newPage();
console.log('1. Opening Claudeman web UI...');
console.log('1. Opening Codeman web UI...');
await page.goto(BASE_URL);
await page.waitForLoadState('networkidle');
@@ -36,8 +36,8 @@ async function main() {
await page.selectOption('#ralphCaseSelect', CASE_NAME);
// Load initprompt.md
const initPromptPath = join(homedir(), 'claudeman-cases', CASE_NAME, 'initprompt.md');
let taskDescription = 'Build an awesome iOS app for Claudeman!';
const initPromptPath = join(homedir(), 'codeman-cases', CASE_NAME, 'initprompt.md');
let taskDescription = 'Build an awesome iOS app for Codeman!';
if (existsSync(initPromptPath)) {
taskDescription = readFileSync(initPromptPath, 'utf-8').trim();
console.log(`5. Loaded initprompt.md (${taskDescription.length} chars)`);
@@ -87,13 +87,13 @@ async function main() {
return res.json();
});
const iosSession = sessions.find(s => s.workingDir?.includes('claudeman-ios'));
const iosSession = sessions.find(s => s.workingDir?.includes('codeman-ios'));
console.log('\n========================================');
console.log('SUCCESS! Ralph Loop started.');
console.log('========================================');
console.log(`Session ID: ${iosSession?.id}`);
console.log(`Screen: claudeman-${iosSession?.id?.slice(0, 8)}`);
console.log(`Screen: codeman-${iosSession?.id?.slice(0, 8)}`);
console.log(`Working Dir: ${iosSession?.workingDir}`);
console.log('\nThe session is now running on your production server.');
console.log('View it at: https://localhost:3000');
+12 -12
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Browser test for clickable file links in Claudeman terminal
* Browser test for clickable file links in Codeman terminal
* Uses Playwright directly to ignore HTTPS errors
*/
@@ -33,8 +33,8 @@ async function main() {
const page = await context.newPage();
try {
// Navigate to Claudeman
console.log('Navigating to Claudeman...');
// Navigate to Codeman
console.log('Navigating to Codeman...');
await page.goto(BASE_URL, { waitUntil: 'networkidle', timeout: 30000 });
console.log('Page loaded: ' + await page.title());
@@ -63,9 +63,9 @@ async function main() {
await page.waitForTimeout(2000);
}
// Get active session (use claudeman session if exists)
const claudemanSession = sessions.find(s => s.workingDir?.includes('claudeman'));
const sessionId = claudemanSession?.id || sessions[0]?.id;
// Get active session (use codeman session if exists)
const codemanSession = sessions.find(s => s.workingDir?.includes('codeman'));
const sessionId = codemanSession?.id || sessions[0]?.id;
console.log(`Using session: ${sessionId}\n`);
// Send a command that outputs file paths
@@ -81,8 +81,8 @@ async function main() {
await page.waitForTimeout(2000);
// Take screenshot
await page.screenshot({ path: '/tmp/claudeman-link-test-1.png' });
console.log('Screenshot saved: /tmp/claudeman-link-test-1.png\n');
await page.screenshot({ path: '/tmp/codeman-link-test-1.png' });
console.log('Screenshot saved: /tmp/codeman-link-test-1.png\n');
// Check if terminal contains our file path
const terminalContent = await page.evaluate(() => {
@@ -132,8 +132,8 @@ async function main() {
console.log('Log viewer windows:', logViewerCount);
// Take final screenshot
await page.screenshot({ path: '/tmp/claudeman-link-test-2.png' });
console.log('\nFinal screenshot: /tmp/claudeman-link-test-2.png');
await page.screenshot({ path: '/tmp/codeman-link-test-2.png' });
console.log('\nFinal screenshot: /tmp/codeman-link-test-2.png');
// Test the link provider registration
console.log('\n--- Link Provider Debug ---');
@@ -155,8 +155,8 @@ async function main() {
} catch (error) {
console.error('Error:', error.message);
await page.screenshot({ path: '/tmp/claudeman-link-test-error.png' });
console.log('Error screenshot saved to /tmp/claudeman-link-test-error.png');
await page.screenshot({ path: '/tmp/codeman-link-test-error.png' });
console.log('Error screenshot saved to /tmp/codeman-link-test-error.png');
} finally {
await browser.close();
console.log('\nBrowser closed');
+2 -2
View File
@@ -1,8 +1,8 @@
#!/bin/bash
# Test script for clickable file links in Claudeman terminal
# Test script for clickable file links in Codeman terminal
# Creates a log file and continuously writes timestamped data
LOGFILE="/tmp/claudeman-test-$(date +%s).log"
LOGFILE="/tmp/codeman-test-$(date +%s).log"
echo "Created log file: $LOGFILE"
echo ""
+29 -29
View File
@@ -1,6 +1,6 @@
#!/bin/bash
# ============================================================================
# Claudeman Sessions - Mobile-friendly Tmux Session Chooser
# Codeman Sessions - Mobile-friendly Tmux Session Chooser
# Optimized for iPhone/Termius (portrait ~45 chars, landscape ~95 chars)
# ============================================================================
#
@@ -8,7 +8,7 @@
# - Single-digit selection (1-9) for fast thumb typing
# - Compact display, no wasted space
# - Color-coded status for quick scanning
# - Names pulled from Claudeman state.json
# - Names pulled from Codeman state.json
# - Minimal keystrokes to attach
#
# Usage:
@@ -29,8 +29,8 @@ set -e
# Configuration
# ============================================================================
CLAUDEMAN_STATE="$HOME/.claudeman/state.json"
CLAUDEMAN_SESSIONS="$HOME/.claudeman/mux-sessions.json"
CODEMAN_STATE="$HOME/.codeman/state.json"
CODEMAN_SESSIONS="$HOME/.codeman/mux-sessions.json"
# iPhone 17 Pro portrait width (conservative)
@@ -106,22 +106,22 @@ truncate() {
find_full_session_id() {
local short_id="$1"
if [ -f "$CLAUDEMAN_STATE" ]; then
if [ -f "$CODEMAN_STATE" ]; then
local full_id
full_id=$(jq -r --arg short "$short_id" '
.sessions | keys[] | select(startswith($short))
' "$CLAUDEMAN_STATE" 2>/dev/null | head -1)
' "$CODEMAN_STATE" 2>/dev/null | head -1)
if [ -n "$full_id" ]; then
echo "$full_id"
return
fi
fi
if [ -f "$CLAUDEMAN_SESSIONS" ]; then
if [ -f "$CODEMAN_SESSIONS" ]; then
local full_id
full_id=$(jq -r --arg short "$short_id" '
.[] | select(.sessionId | startswith($short)) | .sessionId
' "$CLAUDEMAN_SESSIONS" 2>/dev/null | head -1)
' "$CODEMAN_SESSIONS" 2>/dev/null | head -1)
if [ -n "$full_id" ]; then
echo "$full_id"
return
@@ -136,22 +136,22 @@ get_session_name() {
local name=""
local workdir=""
if [ -f "$CLAUDEMAN_SESSIONS" ]; then
if [ -f "$CODEMAN_SESSIONS" ]; then
local result
result=$(jq -r --arg id "$session_id" '
.[] | select(.sessionId | startswith($id)) | "\(.name // "")\t\(.workingDir // "")"
' "$CLAUDEMAN_SESSIONS" 2>/dev/null | head -1)
' "$CODEMAN_SESSIONS" 2>/dev/null | head -1)
if [ -n "$result" ]; then
name="${result%% *}"
workdir="${result#* }"
fi
fi
if [ -z "$name" ] && [ -f "$CLAUDEMAN_STATE" ]; then
if [ -z "$name" ] && [ -f "$CODEMAN_STATE" ]; then
local result
result=$(jq -r --arg id "$session_id" '
.sessions | to_entries[] | select(.key | startswith($id)) | "\(.value.name // "")\t\(.value.workingDir // "")"
' "$CLAUDEMAN_STATE" 2>/dev/null | head -1)
' "$CODEMAN_STATE" 2>/dev/null | head -1)
if [ -n "$result" ]; then
name="${result%% *}"
[ -z "$workdir" ] && workdir="${result#* }"
@@ -174,22 +174,22 @@ get_session_name() {
get_working_dir() {
local session_id="$1"
if [ -f "$CLAUDEMAN_SESSIONS" ]; then
if [ -f "$CODEMAN_SESSIONS" ]; then
local dir
dir=$(jq -r --arg id "$session_id" '
.[] | select(.sessionId | startswith($id)) | .workingDir // empty
' "$CLAUDEMAN_SESSIONS" 2>/dev/null | head -1)
' "$CODEMAN_SESSIONS" 2>/dev/null | head -1)
if [ -n "$dir" ] && [ "$dir" != "null" ]; then
echo "${dir/#$HOME/~}"
return
fi
fi
if [ -f "$CLAUDEMAN_STATE" ]; then
if [ -f "$CODEMAN_STATE" ]; then
local dir
dir=$(jq -r --arg id "$session_id" '
.sessions | to_entries[] | select(.key | startswith($id)) | .value.workingDir // empty
' "$CLAUDEMAN_STATE" 2>/dev/null | head -1)
' "$CODEMAN_STATE" 2>/dev/null | head -1)
if [ -n "$dir" ] && [ "$dir" != "null" ]; then
echo "${dir/#$HOME/~}"
return
@@ -201,12 +201,12 @@ get_working_dir() {
get_tokens() {
local session_id="$1"
if [ -f "$CLAUDEMAN_STATE" ]; then
if [ -f "$CODEMAN_STATE" ]; then
local tokens
tokens=$(jq -r --arg id "$session_id" '
.sessions | to_entries[] | select(.key | startswith($id)) |
((.value.inputTokens // 0) + (.value.outputTokens // 0))
' "$CLAUDEMAN_STATE" 2>/dev/null | head -1)
' "$CODEMAN_STATE" 2>/dev/null | head -1)
if [ -n "$tokens" ] && [ "$tokens" != "null" ] && [ "$tokens" -gt 0 ] 2>/dev/null; then
if [ "$tokens" -gt 1000 ]; then
@@ -223,11 +223,11 @@ get_tokens() {
get_respawn_status() {
local session_id="$1"
if [ -f "$CLAUDEMAN_SESSIONS" ]; then
if [ -f "$CODEMAN_SESSIONS" ]; then
local respawn_enabled
respawn_enabled=$(jq -r --arg id "$session_id" '
.[] | select(.sessionId | startswith($id)) | .respawnConfig.enabled // false
' "$CLAUDEMAN_SESSIONS" 2>/dev/null | head -1)
' "$CODEMAN_SESSIONS" 2>/dev/null | head -1)
if [ "$respawn_enabled" = "true" ]; then
echo "R"
@@ -273,8 +273,8 @@ parse_sessions() {
while IFS= read -r line; do
local session_name="${line%%:*}"
# Only show claudeman sessions
if [[ "$session_name" != claudeman-* ]]; then
# Only show codeman sessions
if [[ "$session_name" != codeman-* ]]; then
continue
fi
@@ -292,9 +292,9 @@ parse_sessions() {
MUX_NAMES+=("$session_name")
SESSION_STATES+=("$state")
# Extract session ID from claudeman session name
# Extract session ID from codeman session name
local session_id=""
local cm_regex='^claudeman-(.+)$'
local cm_regex='^codeman-(.+)$'
if [[ "$session_name" =~ $cm_regex ]]; then
session_id="${BASH_REMATCH[1]}"
fi
@@ -327,7 +327,7 @@ clear_screen() {
print_header() {
local count=${#SESSION_PIDS[@]}
echo -e "${B}${CYAN}Claudeman Sessions${R} ${D}($count)${R}"
echo -e "${B}${CYAN}Codeman Sessions${R} ${D}($count)${R}"
echo -e "${D}$(printf '%.0s─' {1..32})${R}"
}
@@ -395,13 +395,13 @@ print_footer() {
print_no_sessions() {
clear_screen
echo -e "${B}${CYAN}Claudeman Sessions${R}"
echo -e "${B}${CYAN}Codeman Sessions${R}"
echo -e "${D}$(printf '%.0s─' {1..32})${R}"
echo ""
echo -e " ${YELLOW}No tmux sessions found${R}"
echo ""
echo -e " ${D}Start one with:${R}"
echo -e " ${WHITE}claudeman web${R}"
echo -e " ${WHITE}codeman web${R}"
echo ""
echo -e "${D}$(printf '%.0s─' {1..32})${R}"
echo -e " ${GRAY}[${WHITE}r${GRAY}]efresh [${WHITE}q${GRAY}]uit${R}"
@@ -584,7 +584,7 @@ quick_attach() {
show_help() {
cat << 'EOF'
Claudeman Sessions - Mobile-friendly Tmux Session Chooser
Codeman Sessions - Mobile-friendly Tmux Session Chooser
USAGE:
sc Interactive chooser
@@ -607,7 +607,7 @@ INDICATORS:
TIPS:
- Detach from tmux: Ctrl+B D
- Session names from Claudeman state
- Session names from Codeman state
- Optimized for Termius/iPhone
EOF
+8 -8
View File
@@ -1,8 +1,8 @@
#!/bin/bash
#
# Claudeman Tmux Session Manager
# Codeman Tmux Session Manager
# Interactive tool with arrow key navigation
# Reads from ~/.claudeman/mux-sessions.json
# Reads from ~/.codeman/mux-sessions.json
#
# Colors
@@ -17,8 +17,8 @@ DIM='\033[2m'
NC='\033[0m'
REVERSE='\033[7m'
# Use the same path as claudeman (src/tmux-manager.ts)
SESSIONS_FILE="${HOME}/.claudeman/mux-sessions.json"
# Use the same path as codeman (src/tmux-manager.ts)
SESSIONS_FILE="${HOME}/.codeman/mux-sessions.json"
# Cached data
@@ -133,7 +133,7 @@ kill_session() {
# Draw header (only once)
draw_header() {
echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}${CYAN}║ Claudeman Tmux Session Manager ║${NC}"
echo -e "${BOLD}${CYAN}║ Codeman Tmux Session Manager ║${NC}"
echo -e "${BOLD}${CYAN}║ ${DIM}Press q or Esc to exit${CYAN} ║${NC}"
echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════════════╝${NC}"
echo ""
@@ -401,11 +401,11 @@ quick_list() {
local now=$(date +%s)
if [[ $CACHED_COUNT -eq 0 ]]; then
echo "No claudeman sessions found."
echo "No codeman sessions found."
exit 0
fi
echo "Claudeman Sessions ($SESSIONS_FILE):"
echo "Codeman Sessions ($SESSIONS_FILE):"
echo ""
printf "%-4s %-22s %-14s %-10s %-8s\n" "#" "NAME" "RUNNING" "STATUS" "MODE"
echo "─────────────────────────────────────────────────────────────────"
@@ -432,7 +432,7 @@ quick_list() {
# Usage
usage() {
cat << EOF
Claudeman Tmux Session Manager
Codeman Tmux Session Manager
Reads from: $SESSIONS_FILE