mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
perf: speed up session creation by ~500ms — remove redundant sleep, reduce tmux wait, fire-and-forget config
This commit is contained in:
@@ -35,7 +35,7 @@ When user says "COM":
|
|||||||
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
|
1. Increment version in BOTH `package.json` AND `CLAUDE.md` (verify they match with `grep version package.json && grep Version CLAUDE.md`)
|
||||||
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
||||||
|
|
||||||
**Version**: 0.1567 (must match `package.json` for npm publish)
|
**Version**: 0.1568 (must match `package.json` for npm publish)
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ The frontend is a single 16K-line vanilla JS file with these key systems:
|
|||||||
|
|
||||||
**Z-index layers**: subagent windows (1000), plan agents (1100), log viewers (2000), image popups (3000), local echo overlay (7).
|
**Z-index layers**: subagent windows (1000), plan agents (1100), log viewers (2000), image popups (3000), local echo overlay (7).
|
||||||
|
|
||||||
**Built-in respawn presets**: `solo-work` (3s idle, 60min), `subagent-workflow` (45s idle, 240min), `team-lead` (90s idle, 480min), `overnight-autonomous` (10s idle, 480min, full reset).
|
**Built-in respawn presets**: `solo-work` (3s idle, 60min), `subagent-workflow` (45s idle, 240min), `team-lead` (90s idle, 480min), `ralph-todo` (8s idle, 480min, works through @fix_plan.md tasks), `overnight-autonomous` (10s idle, 480min, full reset).
|
||||||
|
|
||||||
**Keyboard shortcuts**: Escape (close panels), Ctrl+? (help), Ctrl+Enter (quick start), Ctrl+W (kill session), Ctrl+Tab (next session), Ctrl+K (kill all), Ctrl+L (clear), Ctrl+Shift+R (restore size), Ctrl/Cmd +/- (font size).
|
**Keyboard shortcuts**: Escape (close panels), Ctrl+? (help), Ctrl+Enter (quick start), Ctrl+W (kill session), Ctrl+Tab (next session), Ctrl+K (kill all), Ctrl+L (clear), Ctrl+Shift+R (restore size), Ctrl/Cmd +/- (font size).
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudeman",
|
"name": "claudeman",
|
||||||
"version": "0.1567",
|
"version": "0.1568",
|
||||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
+2
-6
@@ -878,9 +878,7 @@ export class Session extends EventEmitter {
|
|||||||
// Create a new mux session
|
// Create a new mux session
|
||||||
this._muxSession = await this._mux.createSession(this.id, this.workingDir, 'claude', this._name, this._niceConfig, this._model);
|
this._muxSession = await this._mux.createSession(this.id, this.workingDir, 'claude', this._name, this._niceConfig, this._model);
|
||||||
console.log('[Session] Created mux session:', this._muxSession.muxName);
|
console.log('[Session] Created mux session:', this._muxSession.muxName);
|
||||||
|
// No extra sleep — createSession() already waits for tmux readiness
|
||||||
// Wait a moment for mux to fully start
|
|
||||||
await new Promise(resolve => setTimeout(resolve, MUX_STARTUP_DELAY_MS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to the mux session via PTY
|
// Attach to the mux session via PTY
|
||||||
@@ -1176,9 +1174,7 @@ export class Session extends EventEmitter {
|
|||||||
// Create a new mux session
|
// Create a new mux session
|
||||||
this._muxSession = await this._mux.createSession(this.id, this.workingDir, 'shell', this._name, this._niceConfig);
|
this._muxSession = await this._mux.createSession(this.id, this.workingDir, 'shell', this._name, this._niceConfig);
|
||||||
console.log('[Session] Created mux session:', this._muxSession.muxName);
|
console.log('[Session] Created mux session:', this._muxSession.muxName);
|
||||||
|
// No extra sleep — createSession() already waits for tmux readiness
|
||||||
// Wait a moment for mux to fully start
|
|
||||||
await new Promise(resolve => setTimeout(resolve, MUX_STARTUP_DELAY_MS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to the mux session via PTY
|
// Attach to the mux session via PTY
|
||||||
|
|||||||
+5
-3
@@ -45,8 +45,8 @@ import { findClaudeDir } from './utils/claude-cli-resolver.js';
|
|||||||
/** Timeout for exec commands (5 seconds) */
|
/** Timeout for exec commands (5 seconds) */
|
||||||
const EXEC_TIMEOUT_MS = 5000;
|
const EXEC_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
/** Delay after tmux session creation (300ms) */
|
/** Delay after tmux session creation — enough for detached tmux to be queryable */
|
||||||
const TMUX_CREATION_WAIT_MS = 300;
|
const TMUX_CREATION_WAIT_MS = 100;
|
||||||
|
|
||||||
/** Delay after tmux kill command (200ms) */
|
/** Delay after tmux kill command (200ms) */
|
||||||
const TMUX_KILL_WAIT_MS = 200;
|
const TMUX_KILL_WAIT_MS = 200;
|
||||||
@@ -298,7 +298,9 @@ export class TmuxManager extends EventEmitter implements TerminalMultiplexer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(configPromises);
|
// Fire-and-forget — these are non-critical UX niceties that don't need
|
||||||
|
// to complete before the session is usable. Errors are already swallowed.
|
||||||
|
void Promise.all(configPromises);
|
||||||
|
|
||||||
// Get the PID of the pane process
|
// Get the PID of the pane process
|
||||||
const pid = this.getPanePid(muxName);
|
const pid = this.getPanePid(muxName);
|
||||||
|
|||||||
@@ -96,6 +96,23 @@ const BUILTIN_RESPAWN_PRESETS = [
|
|||||||
builtIn: true,
|
builtIn: true,
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'ralph-todo',
|
||||||
|
name: 'Ralph/Todo',
|
||||||
|
description: 'Ralph Loop task list — works through todos with progress tracking',
|
||||||
|
config: {
|
||||||
|
idleTimeoutMs: 8000,
|
||||||
|
updatePrompt: 'update CLAUDE.md with discoveries and progress notes, mark completed tasks in @fix_plan.md, write a brief summary so the next cycle can continue seamlessly.',
|
||||||
|
interStepDelayMs: 3000,
|
||||||
|
sendClear: true,
|
||||||
|
sendInit: true,
|
||||||
|
kickstartPrompt: 'read @fix_plan.md for task status, continue on the next uncompleted task. When ALL tasks are complete, output <promise>COMPLETE</promise>.',
|
||||||
|
autoAcceptPrompts: true,
|
||||||
|
},
|
||||||
|
durationMinutes: 480,
|
||||||
|
builtIn: true,
|
||||||
|
createdAt: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'overnight-autonomous',
|
id: 'overnight-autonomous',
|
||||||
name: 'Overnight',
|
name: 'Overnight',
|
||||||
|
|||||||
+27
-22
@@ -4635,37 +4635,42 @@ NOW: Generate the implementation plan for the task above. Think step by step.`;
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to get global Nice priority config from settings
|
// Read ~/.claudeman/settings.json once and return the parsed object.
|
||||||
private async getGlobalNiceConfig(): Promise<NiceConfig | undefined> {
|
// Cached for 2s to avoid redundant reads during session creation bursts.
|
||||||
|
private _settingsCache: { data: Record<string, unknown>; ts: number } | null = null;
|
||||||
|
private async readSettings(): Promise<Record<string, unknown>> {
|
||||||
|
const now = Date.now();
|
||||||
|
if (this._settingsCache && now - this._settingsCache.ts < 2000) {
|
||||||
|
return this._settingsCache.data;
|
||||||
|
}
|
||||||
const settingsPath = join(homedir(), '.claudeman', 'settings.json');
|
const settingsPath = join(homedir(), '.claudeman', 'settings.json');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = await fs.readFile(settingsPath, 'utf-8');
|
const content = await fs.readFile(settingsPath, 'utf-8');
|
||||||
const settings = JSON.parse(content);
|
const data = JSON.parse(content) as Record<string, unknown>;
|
||||||
if (settings.nice && settings.nice.enabled) {
|
this._settingsCache = { data, ts: now };
|
||||||
return {
|
return data;
|
||||||
enabled: settings.nice.enabled ?? false,
|
} catch {
|
||||||
niceValue: settings.nice.niceValue ?? DEFAULT_NICE_CONFIG.niceValue,
|
return {};
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
|
// Helper to get global Nice priority config from settings
|
||||||
console.error('Failed to read Nice priority settings:', err);
|
private async getGlobalNiceConfig(): Promise<NiceConfig | undefined> {
|
||||||
}
|
const settings = await this.readSettings();
|
||||||
|
const nice = settings.nice as { enabled?: boolean; niceValue?: number } | undefined;
|
||||||
|
if (nice && nice.enabled) {
|
||||||
|
return {
|
||||||
|
enabled: nice.enabled ?? false,
|
||||||
|
niceValue: nice.niceValue ?? DEFAULT_NICE_CONFIG.niceValue,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to get model configuration from settings
|
// Helper to get model configuration from settings
|
||||||
private async getModelConfig(): Promise<{ defaultModel?: string; agentTypeOverrides?: Record<string, string> } | null> {
|
private async getModelConfig(): Promise<{ defaultModel?: string; agentTypeOverrides?: Record<string, string> } | null> {
|
||||||
const settingsPath = join(homedir(), '.claudeman', 'settings.json');
|
const settings = await this.readSettings();
|
||||||
|
return (settings.modelConfig as { defaultModel?: string; agentTypeOverrides?: Record<string, string> }) || null;
|
||||||
try {
|
|
||||||
const content = await fs.readFile(settingsPath, 'utf-8');
|
|
||||||
return JSON.parse(content).modelConfig || null;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async startScheduledRun(prompt: string, workingDir: string, durationMinutes: number): Promise<ScheduledRun> {
|
private async startScheduledRun(prompt: string, workingDir: string, durationMinutes: number): Promise<ScheduledRun> {
|
||||||
|
|||||||
Reference in New Issue
Block a user