Merge origin/master into relprep/pr-753

Conflict in src/session-cli-registry-bridge.ts: kept the PR's gateVersion() (gates measured against the gated CLI's own version) and added master's Discord startup hint engine value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer committed 2026-11-06 02:50:25 +01:00
commit 35ff4e886c
138 files changed
+17441 -502

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
/**
* @fileoverview Discord bridge type definitions.
*
* The option lists and the config, result and status shapes of the two-way Discord bridge, kept apart
* from the IO module (`src/discord-bridge.ts`, which pulls in `ws`) so the request schemas and the
* route can import them alone, as the webhook's equivalents live in `push.ts`.
*
* Key exports:
* - DISCORD_THREAD_CREATION, DISCORD_ON_DELETE, DISCORD_PING_MODES (+ their types): the option lists
* - DiscordConfig: `~/.codeman/discord.json` (mode 0600; the bot token is a secret)
* - DiscordResult, DiscordGatewayState, DiscordStatus: what `GET /api/discord` reports
*
* No dependencies on other domain modules but `push.ts` (the shared scope list).
*/
import type { WebhookScope } from './push.js';
export const DISCORD_THREAD_CREATION = ['lazy', 'onSessionStart'] as const;
export type DiscordThreadCreation = (typeof DISCORD_THREAD_CREATION)[number];
export const DISCORD_ON_DELETE = ['keep', 'archive', 'delete'] as const;
export type DiscordOnDelete = (typeof DISCORD_ON_DELETE)[number];
export const DISCORD_PING_MODES = ['off', 'critical', 'all'] as const;
export type DiscordPingMode = (typeof DISCORD_PING_MODES)[number];
export interface DiscordConfig {
enabled: boolean;
token: string;
guildId: string;
parentChannelId: string;
allowedUserIds: string[];
threadCreation: DiscordThreadCreation;
onSessionDelete: DiscordOnDelete;
/** What to do with threads the bridge made that no tab maps to any more: checked after boot, then daily. */
staleThreads: DiscordOnDelete;
pingMode: DiscordPingMode;
/** `attention` skips info-level events (a finished response); `all` sends them too. */
scope: WebhookScope;
/** Tell each session how to reach the user (see DISCORD_STARTUP_HINT). On by default while the bridge is on. */
startupHint: boolean;
}
export interface DiscordResult {
ok: boolean;
/** True when the same event for the same tab was posted within the last few seconds, so nothing was sent. */
deduped?: boolean;
status?: number;
error?: string;
at: number;
}
export type DiscordGatewayState = 'off' | 'connecting' | 'connected' | 'error';
/** `GET /api/discord`: the config without its token, plus what the bridge is doing. */
export type DiscordStatus = Omit<DiscordConfig, 'token'> & {
hasToken: boolean;
tokenMasked: string;
gateway: DiscordGatewayState;
gatewayError: string | null;
lastResult: DiscordResult | null;
};
+2
View File
@@ -25,6 +25,7 @@
* | tools | ActiveBashTool, ImageDetectedEvent | In-memory, broadcast via SSE |
* | teams | TeamConfig, TeamMember, TeamTask, InboxMessage, PaneInfo | `~/.claude/teams/`, `~/.claude/tasks/` → `GET /api/teams` |
* | push | PushSubscriptionRecord, VapidKeys, WebhookConfig, WebhookStatus, WebhookResult | `~/.codeman/push-keys.json`, `~/.codeman/push-subscriptions.json`, `~/.codeman/webhook.json` |
* | discord | DiscordConfig, DiscordResult, DiscordStatus, DiscordGatewayState | `~/.codeman/discord.json` → `GET`/`PUT /api/discord` |
* | plan | PlanItem, PlanTaskStatus, TddPhase | In-memory → `GET /api/sessions/:id/plan/tasks` |
* | orchestrator | OrchestratorState, OrchestratorPlan, OrchestratorConfig, OrchestratorPersistState | `~/.codeman/state.json` → `GET /api/orchestrator/status` |
* | mcp-sync | McpSyncResult, McpSyncTargetResult | Other CLIs' own config files → `GET`/`POST /api/mcp-sync` |
@@ -65,6 +66,7 @@ export * from './run-summary.js';
export * from './tools.js';
export * from './teams.js';
export * from './push.js';
export * from './discord.js';
export * from './plan.js';
export * from './orchestrator.js';
export * from './update.js';
+13
View File
@@ -840,6 +840,19 @@ export interface SessionState {
status: SessionStatus;
/** Working directory path */
workingDir: string;
/**
* The directory the session's local tmux pane is in NOW (`#{pane_current_path}`),
* when known. `workingDir` is fixed at create time and never follows a `cd`;
* this is what a relative path printed in the terminal is relative to.
*
* Runtime-only: refreshed from the pane-exit watcher's read and never restored
* from disk, so a restart starts blank. Absent for remote and docker sessions
* (their local pane runs the ssh / docker-exec client) and for direct-PTY or
* rediscovered sessions. NOT a confinement root: the file routes stay confined
* to `workingDir`, and a path resolved outside it goes through the attachment
* routes like any other out-of-workspace path.
*/
paneCwd?: string;
/** Remote execution metadata, present when this session runs over SSH through local tmux */
remote?: SessionRemote;
/** Docker execution metadata, present when this session runs inside a container via local tmux + docker exec */