oh-my-codex 0.3.8 → 0.3.10
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.
- package/dist/cli/__tests__/doctor-team.test.js +58 -0
- package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +3 -3
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/lifecycle-notifications.test.d.ts +2 -0
- package/dist/cli/__tests__/lifecycle-notifications.test.d.ts.map +1 -0
- package/dist/cli/__tests__/lifecycle-notifications.test.js +48 -0
- package/dist/cli/__tests__/lifecycle-notifications.test.js.map +1 -0
- package/dist/cli/doctor.js +28 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +41 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/config/__tests__/models.test.d.ts +2 -0
- package/dist/config/__tests__/models.test.d.ts.map +1 -0
- package/dist/config/__tests__/models.test.js +69 -0
- package/dist/config/__tests__/models.test.js.map +1 -0
- package/dist/config/models.d.ts +24 -0
- package/dist/config/models.d.ts.map +1 -0
- package/dist/config/models.js +53 -0
- package/dist/config/models.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +221 -36
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +21 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server-team-tools.test.js +53 -1
- package/dist/mcp/__tests__/state-server-team-tools.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +1 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +34 -1
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +46 -11
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/notifications/__tests__/config.test.d.ts +2 -0
- package/dist/notifications/__tests__/config.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/config.test.js +186 -0
- package/dist/notifications/__tests__/config.test.js.map +1 -0
- package/dist/notifications/__tests__/dispatcher.test.d.ts +2 -0
- package/dist/notifications/__tests__/dispatcher.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/dispatcher.test.js +202 -0
- package/dist/notifications/__tests__/dispatcher.test.js.map +1 -0
- package/dist/notifications/__tests__/formatter.test.d.ts +2 -0
- package/dist/notifications/__tests__/formatter.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/formatter.test.js +103 -0
- package/dist/notifications/__tests__/formatter.test.js.map +1 -0
- package/dist/notifications/__tests__/notifier.test.d.ts +2 -0
- package/dist/notifications/__tests__/notifier.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/notifier.test.js +104 -0
- package/dist/notifications/__tests__/notifier.test.js.map +1 -0
- package/dist/notifications/__tests__/profiles.test.d.ts +2 -0
- package/dist/notifications/__tests__/profiles.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/profiles.test.js +404 -0
- package/dist/notifications/__tests__/profiles.test.js.map +1 -0
- package/dist/notifications/__tests__/reply-listener.test.d.ts +2 -0
- package/dist/notifications/__tests__/reply-listener.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/reply-listener.test.js +58 -0
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -0
- package/dist/notifications/__tests__/session-registry.test.d.ts +2 -0
- package/dist/notifications/__tests__/session-registry.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/session-registry.test.js +147 -0
- package/dist/notifications/__tests__/session-registry.test.js.map +1 -0
- package/dist/notifications/__tests__/tmux-detector.test.d.ts +2 -0
- package/dist/notifications/__tests__/tmux-detector.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/tmux-detector.test.js +77 -0
- package/dist/notifications/__tests__/tmux-detector.test.js.map +1 -0
- package/dist/notifications/__tests__/tmux.test.d.ts +2 -0
- package/dist/notifications/__tests__/tmux.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/tmux.test.js +86 -0
- package/dist/notifications/__tests__/tmux.test.js.map +1 -0
- package/dist/notifications/config.d.ts +44 -0
- package/dist/notifications/config.d.ts.map +1 -0
- package/dist/notifications/config.js +407 -0
- package/dist/notifications/config.js.map +1 -0
- package/dist/notifications/dispatcher.d.ts +15 -0
- package/dist/notifications/dispatcher.d.ts.map +1 -0
- package/dist/notifications/dispatcher.js +410 -0
- package/dist/notifications/dispatcher.js.map +1 -0
- package/dist/notifications/formatter.d.ts +14 -0
- package/dist/notifications/formatter.d.ts.map +1 -0
- package/dist/notifications/formatter.js +134 -0
- package/dist/notifications/formatter.js.map +1 -0
- package/dist/notifications/index.d.ts +32 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +93 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/reply-listener.d.ts +47 -0
- package/dist/notifications/reply-listener.d.ts.map +1 -0
- package/dist/notifications/reply-listener.js +656 -0
- package/dist/notifications/reply-listener.js.map +1 -0
- package/dist/notifications/session-registry.d.ts +26 -0
- package/dist/notifications/session-registry.d.ts.map +1 -0
- package/dist/notifications/session-registry.js +275 -0
- package/dist/notifications/session-registry.js.map +1 -0
- package/dist/notifications/tmux-detector.d.ts +17 -0
- package/dist/notifications/tmux-detector.d.ts.map +1 -0
- package/dist/notifications/tmux-detector.js +77 -0
- package/dist/notifications/tmux-detector.js.map +1 -0
- package/dist/notifications/tmux.d.ts +28 -0
- package/dist/notifications/tmux.d.ts.map +1 -0
- package/dist/notifications/tmux.js +203 -0
- package/dist/notifications/tmux.js.map +1 -0
- package/dist/notifications/types.d.ts +181 -0
- package/dist/notifications/types.d.ts.map +1 -0
- package/dist/notifications/types.js +9 -0
- package/dist/notifications/types.js.map +1 -0
- package/dist/team/__tests__/runtime.test.js +54 -2
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +30 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +59 -1
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/runtime.d.ts +2 -2
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +50 -23
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state.d.ts +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +5 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +53 -10
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +12 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +37 -0
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/package.json +1 -1
- package/prompts/planner.md +3 -3
- package/scripts/notify-hook.js +137 -7
- package/skills/team/SKILL.md +3 -1
package/prompts/planner.md
CHANGED
|
@@ -26,7 +26,7 @@ Plans that are too vague waste executor time guessing. Plans that are too detail
|
|
|
26
26
|
|
|
27
27
|
- Never write code files (.ts, .js, .py, .go, etc.). Only output plans to `.omx/plans/*.md` and drafts to `.omx/drafts/*.md`.
|
|
28
28
|
- Never generate a plan until the user explicitly requests it ("make it into a work plan", "generate the plan").
|
|
29
|
-
- Never start implementation. Always hand off
|
|
29
|
+
- Never start implementation. Always hand off by presenting actionable next-step commands (see Output Format).
|
|
30
30
|
- Ask ONE question at a time using AskUserQuestion tool. Never batch multiple questions.
|
|
31
31
|
- Never ask the user about codebase facts (use explore agent to look them up).
|
|
32
32
|
- Default to 3-6 step plans. Avoid architecture redesign unless the task requires it.
|
|
@@ -41,7 +41,7 @@ Plans that are too vague waste executor time guessing. Plans that are too detail
|
|
|
41
41
|
4) When user triggers plan generation ("make it into a work plan"), consult analyst (Metis) first for gap analysis.
|
|
42
42
|
5) Generate plan with: Context, Work Objectives, Guardrails (Must Have / Must NOT Have), Task Flow, Detailed TODOs with acceptance criteria, Success Criteria.
|
|
43
43
|
6) Display confirmation summary and wait for explicit user approval.
|
|
44
|
-
7) On approval,
|
|
44
|
+
7) On approval, present concrete next-step commands the user can copy-paste to begin execution (e.g. `$ralph "execute plan: {plan-name}"` or `$team 3:executor "execute plan: {plan-name}"`).
|
|
45
45
|
|
|
46
46
|
## Tool Usage
|
|
47
47
|
|
|
@@ -71,7 +71,7 @@ Plans that are too vague waste executor time guessing. Plans that are too detail
|
|
|
71
71
|
2. [Deliverable 2]
|
|
72
72
|
|
|
73
73
|
**Does this plan capture your intent?**
|
|
74
|
-
- "proceed" -
|
|
74
|
+
- "proceed" - Show executable next-step commands
|
|
75
75
|
- "adjust [X]" - Return to interview to modify
|
|
76
76
|
- "restart" - Discard and start fresh
|
|
77
77
|
|
package/scripts/notify-hook.js
CHANGED
|
@@ -437,7 +437,63 @@ function resolveLeaderNudgeIntervalMs() {
|
|
|
437
437
|
return 120_000;
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
function resolveLeaderStalenessThresholdMs() {
|
|
441
|
+
const raw = safeString(process.env.OMX_TEAM_LEADER_STALE_MS || '');
|
|
442
|
+
const parsed = asNumber(raw);
|
|
443
|
+
// Default: 3 minutes. Guard against unreasonable values.
|
|
444
|
+
if (parsed !== null && parsed >= 10_000 && parsed <= 30 * 60_000) return parsed;
|
|
445
|
+
return 180_000;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function checkWorkerPanesAlive(tmuxTarget) {
|
|
449
|
+
// Check if the team tmux session has worker panes running.
|
|
450
|
+
// tmuxTarget is either "omx-team-foo" or "session:window".
|
|
451
|
+
const sessionName = tmuxTarget.split(':')[0];
|
|
452
|
+
try {
|
|
453
|
+
const result = await runProcess('tmux', ['list-panes', '-t', sessionName, '-F', '#{pane_id} #{pane_pid}'], 2000);
|
|
454
|
+
const lines = (result.stdout || '')
|
|
455
|
+
.split('\n')
|
|
456
|
+
.map(l => l.trim())
|
|
457
|
+
.filter(Boolean);
|
|
458
|
+
return { alive: lines.length > 0, paneCount: lines.length };
|
|
459
|
+
} catch {
|
|
460
|
+
return { alive: false, paneCount: 0 };
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function isLeaderStale(stateDir, thresholdMs, nowMs) {
|
|
465
|
+
// Check HUD state (updated by the notify hook on each leader turn) for staleness.
|
|
466
|
+
const hudStatePath = join(stateDir, 'hud-state.json');
|
|
467
|
+
const hudState = await readJsonIfExists(hudStatePath, null);
|
|
468
|
+
if (!hudState || typeof hudState !== 'object') return true;
|
|
469
|
+
const lastTurnAt = safeString(hudState.last_turn_at || '');
|
|
470
|
+
if (!lastTurnAt) return true;
|
|
471
|
+
const lastMs = Date.parse(lastTurnAt);
|
|
472
|
+
if (!Number.isFinite(lastMs)) return true;
|
|
473
|
+
return (nowMs - lastMs) >= thresholdMs;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
async function emitTeamNudgeEvent(cwd, teamName, reason, nowIso) {
|
|
477
|
+
// Write a team_leader_nudge event to the team's events.ndjson log.
|
|
478
|
+
const eventsDir = join(cwd, '.omx', 'state', 'team', teamName, 'events');
|
|
479
|
+
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
480
|
+
try {
|
|
481
|
+
await mkdir(eventsDir, { recursive: true });
|
|
482
|
+
const event = {
|
|
483
|
+
event_id: `nudge-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
484
|
+
team: teamName,
|
|
485
|
+
type: 'team_leader_nudge',
|
|
486
|
+
worker: 'leader-fixed',
|
|
487
|
+
reason,
|
|
488
|
+
created_at: nowIso,
|
|
489
|
+
};
|
|
490
|
+
await appendFile(eventsPath, JSON.stringify(event) + '\n');
|
|
491
|
+
} catch {
|
|
492
|
+
// Best effort
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function maybeNudgeTeamLeader({ cwd, stateDir, logsDir, preComputedLeaderStale }) {
|
|
441
497
|
const intervalMs = resolveLeaderNudgeIntervalMs();
|
|
442
498
|
const nowMs = Date.now();
|
|
443
499
|
const nowIso = new Date().toISOString();
|
|
@@ -467,6 +523,9 @@ async function maybeNudgeTeamLeader({ cwd, stateDir, logsDir }) {
|
|
|
467
523
|
// Non-critical
|
|
468
524
|
}
|
|
469
525
|
|
|
526
|
+
// Use pre-computed staleness (captured before HUD state was updated this turn)
|
|
527
|
+
const leaderStale = typeof preComputedLeaderStale === 'boolean' ? preComputedLeaderStale : false;
|
|
528
|
+
|
|
470
529
|
for (const teamName of activeTeamNames) {
|
|
471
530
|
// Resolve tmux target (session:window) from manifest/config. Best effort.
|
|
472
531
|
let tmuxTarget = '';
|
|
@@ -483,6 +542,9 @@ async function maybeNudgeTeamLeader({ cwd, stateDir, logsDir }) {
|
|
|
483
542
|
}
|
|
484
543
|
if (!tmuxTarget) continue;
|
|
485
544
|
|
|
545
|
+
// Check if worker panes are still alive in tmux
|
|
546
|
+
const paneStatus = await checkWorkerPanesAlive(tmuxTarget);
|
|
547
|
+
|
|
486
548
|
let mailbox = null;
|
|
487
549
|
try {
|
|
488
550
|
const mailboxPath = join(omxDir, 'state', 'team', teamName, 'mailbox', 'leader-fixed.json');
|
|
@@ -503,17 +565,51 @@ async function maybeNudgeTeamLeader({ cwd, stateDir, logsDir }) {
|
|
|
503
565
|
|
|
504
566
|
const hasNewMessage = newestId && newestId !== prevMsgId;
|
|
505
567
|
const dueByTime = !Number.isFinite(prevAtMs) || (nowMs - prevAtMs >= intervalMs);
|
|
506
|
-
if (!hasNewMessage && !dueByTime) continue;
|
|
507
568
|
|
|
569
|
+
// New condition: worker panes alive + leader stale = always nudge
|
|
570
|
+
const stalePanesNudge = paneStatus.alive && leaderStale;
|
|
571
|
+
|
|
572
|
+
if (!hasNewMessage && !dueByTime && !stalePanesNudge) continue;
|
|
573
|
+
|
|
574
|
+
// Build contextual nudge message
|
|
508
575
|
const msgCount = messages.length;
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
576
|
+
let nudgeReason = '';
|
|
577
|
+
let text = '';
|
|
578
|
+
if (stalePanesNudge && hasNewMessage) {
|
|
579
|
+
nudgeReason = 'stale_leader_with_messages';
|
|
580
|
+
text = `Team ${teamName}: leader stale, ${paneStatus.paneCount} pane(s) active, ${msgCount} msg(s) pending. Run: omx team status ${teamName}`;
|
|
581
|
+
} else if (stalePanesNudge) {
|
|
582
|
+
nudgeReason = 'stale_leader_panes_alive';
|
|
583
|
+
text = `Team ${teamName}: leader stale, ${paneStatus.paneCount} worker pane(s) still active. Run: omx team status ${teamName}`;
|
|
584
|
+
} else if (hasNewMessage) {
|
|
585
|
+
nudgeReason = 'new_mailbox_message';
|
|
586
|
+
text = `Team ${teamName}: ${msgCount} msg(s) for leader. Run: omx team status ${teamName}`;
|
|
587
|
+
} else {
|
|
588
|
+
nudgeReason = 'periodic_check';
|
|
589
|
+
text = `Team ${teamName} active. Run: omx team status ${teamName}`;
|
|
590
|
+
}
|
|
512
591
|
const capped = text.length > 180 ? `${text.slice(0, 177)}...` : text;
|
|
513
592
|
|
|
514
593
|
try {
|
|
515
|
-
await runProcess('tmux', ['
|
|
594
|
+
await runProcess('tmux', ['send-keys', '-t', tmuxTarget, capped, 'C-m', 'C-m'], 1200);
|
|
516
595
|
nudgeState.last_nudged_by_team[teamName] = { at: nowIso, last_message_id: newestId || prevMsgId || '' };
|
|
596
|
+
|
|
597
|
+
// Emit team event for the nudge
|
|
598
|
+
await emitTeamNudgeEvent(cwd, teamName, nudgeReason, nowIso);
|
|
599
|
+
|
|
600
|
+
// Log the nudge
|
|
601
|
+
try {
|
|
602
|
+
await logTmuxHookEvent(logsDir, {
|
|
603
|
+
timestamp: nowIso,
|
|
604
|
+
type: 'team_leader_nudge',
|
|
605
|
+
team: teamName,
|
|
606
|
+
tmux_target: tmuxTarget,
|
|
607
|
+
reason: nudgeReason,
|
|
608
|
+
pane_count: paneStatus.paneCount,
|
|
609
|
+
leader_stale: leaderStale,
|
|
610
|
+
message_count: msgCount,
|
|
611
|
+
});
|
|
612
|
+
} catch { /* ignore */ }
|
|
517
613
|
} catch (err) {
|
|
518
614
|
// Best effort. Log only in debug mode to avoid noise.
|
|
519
615
|
try {
|
|
@@ -522,6 +618,7 @@ async function maybeNudgeTeamLeader({ cwd, stateDir, logsDir }) {
|
|
|
522
618
|
type: 'team_leader_nudge',
|
|
523
619
|
team: teamName,
|
|
524
620
|
tmux_target: tmuxTarget,
|
|
621
|
+
reason: nudgeReason,
|
|
525
622
|
error: safeString(err && err.message ? err.message : err),
|
|
526
623
|
});
|
|
527
624
|
} catch { /* ignore */ }
|
|
@@ -968,6 +1065,17 @@ async function main() {
|
|
|
968
1065
|
}
|
|
969
1066
|
}
|
|
970
1067
|
|
|
1068
|
+
// 3.5. Pre-compute leader staleness BEFORE updating HUD state (used by nudge in step 6)
|
|
1069
|
+
let preComputedLeaderStale = false;
|
|
1070
|
+
if (!isTeamWorker) {
|
|
1071
|
+
try {
|
|
1072
|
+
const stalenessMs = resolveLeaderStalenessThresholdMs();
|
|
1073
|
+
preComputedLeaderStale = await isLeaderStale(stateDir, stalenessMs, Date.now());
|
|
1074
|
+
} catch {
|
|
1075
|
+
// Non-critical
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
971
1079
|
// 4. Write HUD state summary for `omx hud` (lead session only)
|
|
972
1080
|
if (!isTeamWorker) {
|
|
973
1081
|
const hudStatePath = join(stateDir, 'hud-state.json');
|
|
@@ -1026,11 +1134,33 @@ async function main() {
|
|
|
1026
1134
|
// 6. Team leader nudge (lead session only): remind the leader to check teammate/mailbox state.
|
|
1027
1135
|
if (!isTeamWorker) {
|
|
1028
1136
|
try {
|
|
1029
|
-
await maybeNudgeTeamLeader({ cwd, stateDir, logsDir });
|
|
1137
|
+
await maybeNudgeTeamLeader({ cwd, stateDir, logsDir, preComputedLeaderStale });
|
|
1030
1138
|
} catch {
|
|
1031
1139
|
// Non-critical
|
|
1032
1140
|
}
|
|
1033
1141
|
}
|
|
1142
|
+
|
|
1143
|
+
// 7. Dispatch session-idle lifecycle notification (lead session only, best effort)
|
|
1144
|
+
if (!isTeamWorker) {
|
|
1145
|
+
try {
|
|
1146
|
+
const { notifyLifecycle } = await import('../dist/notifications/index.js');
|
|
1147
|
+
const sessionJsonPath = join(stateDir, 'session.json');
|
|
1148
|
+
let notifySessionId = '';
|
|
1149
|
+
try {
|
|
1150
|
+
const sessionData = JSON.parse(await readFile(sessionJsonPath, 'utf-8'));
|
|
1151
|
+
notifySessionId = safeString(sessionData && sessionData.session_id ? sessionData.session_id : '');
|
|
1152
|
+
} catch { /* no session file */ }
|
|
1153
|
+
|
|
1154
|
+
if (notifySessionId) {
|
|
1155
|
+
await notifyLifecycle('session-idle', {
|
|
1156
|
+
sessionId: notifySessionId,
|
|
1157
|
+
projectPath: cwd,
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1160
|
+
} catch {
|
|
1161
|
+
// Non-fatal: notification module may not be built or config may not exist
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1034
1164
|
}
|
|
1035
1165
|
|
|
1036
1166
|
async function readdir(dir) {
|
package/skills/team/SKILL.md
CHANGED
|
@@ -63,7 +63,9 @@ If duplicates exist, remove extras before `omx team` to prevent HUD ending up in
|
|
|
63
63
|
- `.omx/state/team/<team>/config.json`
|
|
64
64
|
- `.omx/state/team/<team>/manifest.v2.json`
|
|
65
65
|
- `.omx/state/team/<team>/tasks/task-<id>.json`
|
|
66
|
-
4.
|
|
66
|
+
4. Compose team-scoped worker instructions file at:
|
|
67
|
+
- `.omx/state/team/<team>/worker-agents.md`
|
|
68
|
+
- Uses project `AGENTS.md` content (if present) + worker overlay, without mutating project `AGENTS.md`
|
|
67
69
|
5. Split current tmux window into worker panes
|
|
68
70
|
6. Launch workers with `OMX_TEAM_WORKER=<team>/worker-<n>`
|
|
69
71
|
7. Wait for worker readiness (`capture-pane` polling)
|