fix(hub): SessionStart-Hook und agenthub start liefern denselben Text + Verzeichnis-Guard

1) hookContext druckte einen eigenen, aelteren Text. Fuer Codex/Kimi ist der
   Hook aber oft der EINZIGE Einstieg (sie starten automatisch) — dadurch
   kannten auto-gestartete Agenten weder den Check-in-Kanal noch DEC-0035.
   Aufgefallen, als codex ohne `agenthub start` losgelaufen ist. Beide Wege
   nutzen jetzt agentBriefing().

2) `server start` prueft jetzt VOR dem Binden, ob hier ueberhaupt ein Projekt
   liegt. Vorher band der Server erst den Port und starb dann beim Laden der
   Config — der alte Hub war da schon gekillt, die Agenten liefen ins Leere.
   Klassiker: aus dem Quellrepo statt aus dem Projekt gestartet (an einem Tag
   dreimal passiert). Jetzt sofortiger, verstaendlicher Fehler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-07-29 19:21:08 +02:00
parent 1250359e6d
commit 5fb2b6d6c6
3 changed files with 31 additions and 13 deletions

View File

@ -2,6 +2,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
import { join, dirname } from 'node:path'; import { join, dirname } from 'node:path';
import { homedir } from 'node:os'; import { homedir } from 'node:os';
import { findProjectRoot } from '../../core/paths.js'; import { findProjectRoot } from '../../core/paths.js';
import { agentBriefing } from './startAgent.js';
/** /**
* Auto-start: make an agent enter the agenthub work-loop the moment its CLI * Auto-start: make an agent enter the agenthub work-loop the moment its CLI
@ -37,16 +38,12 @@ export function inferCli(agent: string, explicit?: string): AgentCli {
/** Printed by the SessionStart hook → injected as session context. */ /** Printed by the SessionStart hook → injected as session context. */
export function hookContext(opts: { agent: string; role: string }): void { export function hookContext(opts: { agent: string; role: string }): void {
const { agent, role } = opts; const { agent, role } = opts;
process.stdout.write( // Denselben Text wie `agenthub start` ausgeben. Der Hook ist fuer viele
`AgentHub auto-start — you are "${agent}" (${role}). Before anything else, enter the work loop:\n` + // Sessions der EINZIGE Einstieg (Codex/Kimi starten automatisch) — ein
` agenthub work --agent ${agent} --role ${role}\n` + // eigener, aelterer Text hier fuehrt dazu, dass auto-gestartete Agenten den
`(or call the agenthub_work tool with { agent: "${agent}", role: "${role}" }). It BLOCKS until a ` + // Check-in-Kanal und DEC-0035 gar nicht kennen. Genau das war der Fall, bis
`task or message addressed to you arrives, then returns it. Handle it, then IMMEDIATELY call ` + // der CEO bemerkte, dass codex ohne `agenthub start` losgelaufen ist.
`agenthub_work AGAIN — run it in the BACKGROUND so you're woken on the next event without ` + process.stdout.write(`${agentBriefing(agent, role)}\n`);
`tying up your turn. The loop is: work -> handle -> agenthub_task_review -> work, forever. ` +
`agenthub_work claims your tasks AND delivers your messages. Never run agenthub_task_done. ` +
`Staying in this loop is what keeps you reachable — do not end a turn without relaunching agenthub_work.\n`,
);
} }
// ─── Marker-delimited upsert (for line-based TOML configs) ─────────────────── // ─── Marker-delimited upsert (for line-based TOML configs) ───────────────────

View File

@ -1,4 +1,5 @@
import { startServer } from '../../server/index.js'; import { startServer } from '../../server/index.js';
import { findProjectRoot } from '../../core/paths.js';
/** /**
* Probe a URL to check whether an AgentHub server is already answering there. * Probe a URL to check whether an AgentHub server is already answering there.
@ -30,5 +31,22 @@ export async function serverStart(cwd: string, options: { port: number; host: st
return; return;
} }
await startServer(cwd, options); // Verzeichnis-Guard: NICHT den Port binden, wenn hier gar kein Projekt liegt.
// Ohne diese Prüfung bindet der Server erst den Port, stolpert dann beim
// Laden der Config und stirbt — der Port ist kurz belegt, der alte Hub ist
// schon gekillt, und die Agenten laufen ins Leere. Klassischer Fall: aus dem
// agenthub-Quellrepo statt aus dem Projekt gestartet (mir an einem Tag
// dreimal passiert). Lieber sofort und verständlich scheitern.
const projectRoot = findProjectRoot(cwd);
if (!projectRoot) {
console.error(
`Kein AgentHub-Projekt in ${cwd} (und keinem übergeordneten Verzeichnis).\n` +
`Starte den Server aus dem Projekt-Root — dort liegt .agenthub/ mit den Daten.\n` +
`Falls das hier ein neues Projekt werden soll: agenthub init`,
);
process.exitCode = 1;
return;
}
await startServer(projectRoot, options);
} }

View File

@ -24,7 +24,10 @@ export interface StartAgentOptions {
role?: string; role?: string;
} }
function briefing(agent: string, role: string): string { /** Das verbindliche Briefing. Wird von `agenthub start` UND vom SessionStart-Hook
* benutzt beide MÜSSEN denselben Text liefern, sonst arbeitet ein
* auto-gestarteter Agent nach veralteten Regeln. */
export function agentBriefing(agent: string, role: string): string {
return [ return [
`Du bist "${agent}" im AgentHub (Rolle: ${role}).`, `Du bist "${agent}" im AgentHub (Rolle: ${role}).`,
'', '',
@ -174,7 +177,7 @@ export async function startAgentSession(options: StartAgentOptions): Promise<str
return 'architect'; return 'architect';
} }
console.log(briefing(agent, role ?? 'implementer')); console.log(agentBriefing(agent, role ?? "implementer"));
// Was liegt gerade an? Damit die Session nicht blind in den Loop geht. // Was liegt gerade an? Damit die Session nicht blind in den Loop geht.
try { try {