claude-threads 0.12.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +473 -0
  2. package/LICENSE +21 -0
  3. package/README.md +303 -0
  4. package/dist/changelog.d.ts +20 -0
  5. package/dist/changelog.js +134 -0
  6. package/dist/claude/cli.d.ts +42 -0
  7. package/dist/claude/cli.js +173 -0
  8. package/dist/claude/session.d.ts +256 -0
  9. package/dist/claude/session.js +1964 -0
  10. package/dist/config.d.ts +27 -0
  11. package/dist/config.js +94 -0
  12. package/dist/git/worktree.d.ts +50 -0
  13. package/dist/git/worktree.js +228 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +371 -0
  16. package/dist/logo.d.ts +31 -0
  17. package/dist/logo.js +57 -0
  18. package/dist/mattermost/api.d.ts +85 -0
  19. package/dist/mattermost/api.js +124 -0
  20. package/dist/mattermost/api.test.d.ts +1 -0
  21. package/dist/mattermost/api.test.js +319 -0
  22. package/dist/mattermost/client.d.ts +56 -0
  23. package/dist/mattermost/client.js +321 -0
  24. package/dist/mattermost/emoji.d.ts +43 -0
  25. package/dist/mattermost/emoji.js +65 -0
  26. package/dist/mattermost/emoji.test.d.ts +1 -0
  27. package/dist/mattermost/emoji.test.js +131 -0
  28. package/dist/mattermost/types.d.ts +71 -0
  29. package/dist/mattermost/types.js +1 -0
  30. package/dist/mcp/permission-server.d.ts +2 -0
  31. package/dist/mcp/permission-server.js +201 -0
  32. package/dist/onboarding.d.ts +1 -0
  33. package/dist/onboarding.js +116 -0
  34. package/dist/persistence/session-store.d.ts +65 -0
  35. package/dist/persistence/session-store.js +127 -0
  36. package/dist/update-notifier.d.ts +3 -0
  37. package/dist/update-notifier.js +31 -0
  38. package/dist/utils/logger.d.ts +34 -0
  39. package/dist/utils/logger.js +42 -0
  40. package/dist/utils/logger.test.d.ts +1 -0
  41. package/dist/utils/logger.test.js +121 -0
  42. package/dist/utils/tool-formatter.d.ts +56 -0
  43. package/dist/utils/tool-formatter.js +247 -0
  44. package/dist/utils/tool-formatter.test.d.ts +1 -0
  45. package/dist/utils/tool-formatter.test.js +357 -0
  46. package/package.json +85 -0
@@ -0,0 +1,256 @@
1
+ import { ClaudeCli } from './cli.js';
2
+ import { MattermostClient } from '../mattermost/client.js';
3
+ import { MattermostFile } from '../mattermost/types.js';
4
+ import { PersistedSession, WorktreeInfo } from '../persistence/session-store.js';
5
+ import { WorktreeMode } from '../config.js';
6
+ interface QuestionOption {
7
+ label: string;
8
+ description: string;
9
+ }
10
+ interface PendingQuestionSet {
11
+ toolUseId: string;
12
+ currentIndex: number;
13
+ currentPostId: string | null;
14
+ questions: Array<{
15
+ header: string;
16
+ question: string;
17
+ options: QuestionOption[];
18
+ answer: string | null;
19
+ }>;
20
+ }
21
+ interface PendingApproval {
22
+ postId: string;
23
+ type: 'plan' | 'action';
24
+ toolUseId: string;
25
+ }
26
+ /**
27
+ * Pending message from unauthorized user awaiting approval
28
+ */
29
+ interface PendingMessageApproval {
30
+ postId: string;
31
+ originalMessage: string;
32
+ fromUser: string;
33
+ }
34
+ /**
35
+ * Represents a single Claude Code session tied to a Mattermost thread.
36
+ * Each session has its own Claude CLI process and state.
37
+ */
38
+ interface Session {
39
+ threadId: string;
40
+ claudeSessionId: string;
41
+ startedBy: string;
42
+ startedAt: Date;
43
+ lastActivityAt: Date;
44
+ sessionNumber: number;
45
+ workingDir: string;
46
+ claude: ClaudeCli;
47
+ currentPostId: string | null;
48
+ pendingContent: string;
49
+ pendingApproval: PendingApproval | null;
50
+ pendingQuestionSet: PendingQuestionSet | null;
51
+ pendingMessageApproval: PendingMessageApproval | null;
52
+ planApproved: boolean;
53
+ sessionAllowedUsers: Set<string>;
54
+ forceInteractivePermissions: boolean;
55
+ sessionStartPostId: string | null;
56
+ tasksPostId: string | null;
57
+ activeSubagents: Map<string, string>;
58
+ updateTimer: ReturnType<typeof setTimeout> | null;
59
+ typingTimer: ReturnType<typeof setInterval> | null;
60
+ timeoutWarningPosted: boolean;
61
+ isRestarting: boolean;
62
+ isResumed: boolean;
63
+ wasInterrupted: boolean;
64
+ inProgressTaskStart: number | null;
65
+ activeToolStarts: Map<string, number>;
66
+ worktreeInfo?: WorktreeInfo;
67
+ pendingWorktreePrompt?: boolean;
68
+ worktreePromptDisabled?: boolean;
69
+ queuedPrompt?: string;
70
+ worktreePromptPostId?: string;
71
+ }
72
+ export declare class SessionManager {
73
+ private mattermost;
74
+ private workingDir;
75
+ private skipPermissions;
76
+ private chromeEnabled;
77
+ private worktreeMode;
78
+ private debug;
79
+ private sessions;
80
+ private postIndex;
81
+ private sessionStore;
82
+ private cleanupTimer;
83
+ private isShuttingDown;
84
+ constructor(mattermost: MattermostClient, workingDir: string, skipPermissions?: boolean, chromeEnabled?: boolean, worktreeMode?: WorktreeMode);
85
+ /**
86
+ * Initialize session manager by resuming any persisted sessions.
87
+ * Should be called before starting to listen for new messages.
88
+ */
89
+ initialize(): Promise<void>;
90
+ /**
91
+ * Resume a single session from persisted state
92
+ */
93
+ private resumeSession;
94
+ /**
95
+ * Persist a session to disk
96
+ */
97
+ private persistSession;
98
+ /**
99
+ * Remove a session from persistence
100
+ */
101
+ private unpersistSession;
102
+ /** Get a session by thread ID */
103
+ getSession(threadId: string): Session | undefined;
104
+ /** Check if a session exists for this thread */
105
+ hasSession(threadId: string): boolean;
106
+ /** Get the number of active sessions */
107
+ getSessionCount(): number;
108
+ /** Get all active session thread IDs */
109
+ getActiveThreadIds(): string[];
110
+ /** Mark that we're shutting down (prevents cleanup of persisted sessions) */
111
+ setShuttingDown(): void;
112
+ /** Register a post for reaction routing */
113
+ private registerPost;
114
+ /** Find session by post ID (for reaction routing) */
115
+ private getSessionByPost;
116
+ /**
117
+ * Check if a user is allowed in a specific session.
118
+ * Checks global allowlist first, then session-specific allowlist.
119
+ */
120
+ isUserAllowedInSession(threadId: string, username: string): boolean;
121
+ startSession(options: {
122
+ prompt: string;
123
+ files?: MattermostFile[];
124
+ }, username: string, replyToPostId?: string): Promise<void>;
125
+ /**
126
+ * Start a session with an initial worktree specified.
127
+ * Used when user specifies "on branch X" or "!worktree X" in their initial message.
128
+ */
129
+ startSessionWithWorktree(options: {
130
+ prompt: string;
131
+ files?: MattermostFile[];
132
+ }, branch: string, username: string, replyToPostId?: string): Promise<void>;
133
+ /**
134
+ * Check if we should prompt for a worktree before starting work.
135
+ * Returns the reason string if we should prompt, or null if not.
136
+ */
137
+ private shouldPromptForWorktree;
138
+ /**
139
+ * Check if another session is using the same repository
140
+ */
141
+ private hasOtherSessionInRepo;
142
+ /**
143
+ * Post the worktree prompt message
144
+ */
145
+ private postWorktreePrompt;
146
+ private handleEvent;
147
+ private handleTaskComplete;
148
+ private handleExitPlanMode;
149
+ private handleTodoWrite;
150
+ private handleTaskStart;
151
+ private handleAskUserQuestion;
152
+ private postCurrentQuestion;
153
+ private handleReaction;
154
+ private handleQuestionReaction;
155
+ private handleApprovalReaction;
156
+ private handleMessageApprovalReaction;
157
+ private formatEvent;
158
+ private appendContent;
159
+ private scheduleUpdate;
160
+ /**
161
+ * Build message content for Claude, including images if present.
162
+ * Returns either a string or an array of content blocks.
163
+ */
164
+ private buildMessageContent;
165
+ private startTyping;
166
+ private stopTyping;
167
+ private flush;
168
+ private handleExit;
169
+ /** Check if any sessions are active */
170
+ isSessionActive(): boolean;
171
+ /** Check if a session exists for this thread */
172
+ isInSessionThread(threadRoot: string): boolean;
173
+ /** Send a follow-up message to an existing session */
174
+ sendFollowUp(threadId: string, message: string, files?: MattermostFile[]): Promise<void>;
175
+ /**
176
+ * Check if there's a paused (persisted but not active) session for this thread.
177
+ * This is used to detect when we should resume a session instead of ignoring the message.
178
+ */
179
+ hasPausedSession(threadId: string): boolean;
180
+ /**
181
+ * Resume a paused session and send a message to it.
182
+ * Called when a user sends a message to a thread with a paused session.
183
+ */
184
+ resumePausedSession(threadId: string, message: string, files?: MattermostFile[]): Promise<void>;
185
+ /**
186
+ * Get persisted session info for access control checks
187
+ */
188
+ getPersistedSession(threadId: string): PersistedSession | undefined;
189
+ /** Kill a specific session */
190
+ killSession(threadId: string, unpersist?: boolean): void;
191
+ /** Cancel a session with user feedback */
192
+ cancelSession(threadId: string, username: string): Promise<void>;
193
+ /** Interrupt current processing but keep session alive (like Escape in CLI) */
194
+ interruptSession(threadId: string, username: string): Promise<void>;
195
+ /** Change working directory for a session (restarts Claude CLI) */
196
+ changeDirectory(threadId: string, newDir: string, username: string): Promise<void>;
197
+ /** Invite a user to participate in a specific session */
198
+ inviteUser(threadId: string, invitedUser: string, invitedBy: string): Promise<void>;
199
+ /** Kick a user from a specific session */
200
+ kickUser(threadId: string, kickedUser: string, kickedBy: string): Promise<void>;
201
+ /**
202
+ * Enable interactive permissions for a session.
203
+ * Can only downgrade (skip → interactive), not upgrade.
204
+ */
205
+ enableInteractivePermissions(threadId: string, username: string): Promise<void>;
206
+ /** Check if a session should use interactive permissions */
207
+ isSessionInteractive(threadId: string): boolean;
208
+ /** Update the session header post with current participants */
209
+ private updateSessionHeader;
210
+ /** Request approval for a message from an unauthorized user */
211
+ requestMessageApproval(threadId: string, username: string, message: string): Promise<void>;
212
+ /**
213
+ * Handle a worktree branch response from user.
214
+ * Called when user replies with a branch name to the worktree prompt.
215
+ */
216
+ handleWorktreeBranchResponse(threadId: string, branchName: string, username: string): Promise<boolean>;
217
+ /**
218
+ * Handle ❌ reaction on worktree prompt - skip worktree and continue in main repo.
219
+ */
220
+ handleWorktreeSkip(threadId: string, username: string): Promise<void>;
221
+ /**
222
+ * Create a new worktree and switch the session to it.
223
+ */
224
+ createAndSwitchToWorktree(threadId: string, branch: string, username: string): Promise<void>;
225
+ /**
226
+ * Switch to an existing worktree.
227
+ */
228
+ switchToWorktree(threadId: string, branchOrPath: string, username: string): Promise<void>;
229
+ /**
230
+ * List all worktrees for the current repository.
231
+ */
232
+ listWorktreesCommand(threadId: string, _username: string): Promise<void>;
233
+ /**
234
+ * Remove a worktree.
235
+ */
236
+ removeWorktreeCommand(threadId: string, branchOrPath: string, username: string): Promise<void>;
237
+ /**
238
+ * Disable worktree prompts for a session.
239
+ */
240
+ disableWorktreePrompt(threadId: string, username: string): Promise<void>;
241
+ /**
242
+ * Check if a session has a pending worktree prompt.
243
+ */
244
+ hasPendingWorktreePrompt(threadId: string): boolean;
245
+ /**
246
+ * Get the worktree prompt post ID for a session.
247
+ */
248
+ getWorktreePromptPostId(threadId: string): string | undefined;
249
+ /** Kill all active sessions (for graceful shutdown) */
250
+ killAllSessions(): void;
251
+ /** Kill all sessions AND unpersist them (for emergency shutdown - no resume) */
252
+ killAllSessionsAndUnpersist(): void;
253
+ /** Cleanup idle sessions that have exceeded timeout */
254
+ private cleanupIdleSessions;
255
+ }
256
+ export {};