@recall_v3/mcp-server 0.1.0 → 3.0.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.
- package/README.md +66 -0
- package/dist/api/client.d.ts +102 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +65 -16
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +297 -0
- package/dist/config/index.d.ts +10 -3
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +71 -4
- package/dist/crypto/index.d.ts +10 -0
- package/dist/crypto/index.d.ts.map +1 -1
- package/dist/crypto/index.js +29 -0
- package/dist/index.js +12 -12
- package/dist/tools/getContext.d.ts +2 -2
- package/dist/tools/getContext.d.ts.map +1 -1
- package/dist/tools/getContext.js +57 -20
- package/dist/tools/getHistory.d.ts +1 -1
- package/dist/tools/getHistory.d.ts.map +1 -1
- package/dist/tools/getHistory.js +93 -30
- package/dist/tools/getTranscripts.js +1 -1
- package/dist/tools/saveSession.d.ts +8 -3
- package/dist/tools/saveSession.d.ts.map +1 -1
- package/dist/tools/saveSession.js +186 -45
- package/package.json +1 -1
- package/src/api/client.ts +154 -17
- package/src/cli.ts +334 -0
- package/src/config/index.ts +84 -4
- package/src/crypto/index.ts +33 -0
- package/src/index.ts +12 -12
- package/src/tools/getContext.ts +102 -20
- package/src/tools/getHistory.ts +157 -31
- package/src/tools/getTranscripts.ts +1 -1
- package/src/tools/saveSession.ts +282 -50
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @recall_v3/mcp-server
|
|
2
|
+
|
|
3
|
+
Team memory for AI coding assistants. Recall captures session context, summarizes with AI, and makes it available to your whole team.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @recall_v3/mcp-server install
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will:
|
|
12
|
+
1. Generate an API token
|
|
13
|
+
2. Configure your Claude Code MCP settings
|
|
14
|
+
3. Connect to your team's Recall workspace
|
|
15
|
+
|
|
16
|
+
## Manual Configuration
|
|
17
|
+
|
|
18
|
+
Add to your Claude Code MCP config (`~/.config/claude/mcp.json`):
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"recall": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["@recall_v3/mcp-server"],
|
|
26
|
+
"env": {
|
|
27
|
+
"RECALL_API_TOKEN": "your_token_here"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Available Tools
|
|
35
|
+
|
|
36
|
+
Once installed, the following MCP tools are available in Claude Code:
|
|
37
|
+
|
|
38
|
+
| Tool | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `recall_get_context` | Load team context at session start |
|
|
41
|
+
| `recall_get_history` | Get recent session summaries |
|
|
42
|
+
| `recall_get_transcripts` | Deep dive into full transcripts (Pro) |
|
|
43
|
+
| `recall_save_session` | Save session summary at end |
|
|
44
|
+
| `recall_log_decision` | Log an important decision |
|
|
45
|
+
|
|
46
|
+
## Environment Variables
|
|
47
|
+
|
|
48
|
+
| Variable | Description | Default |
|
|
49
|
+
|----------|-------------|---------|
|
|
50
|
+
| `RECALL_API_TOKEN` | Your API token | Required |
|
|
51
|
+
| `RECALL_API_URL` | API base URL | `https://api-v3.recall.team` |
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- Node.js 18+
|
|
56
|
+
- Claude Code or compatible MCP client
|
|
57
|
+
|
|
58
|
+
## Links
|
|
59
|
+
|
|
60
|
+
- [Documentation](https://v3.recall.team/docs)
|
|
61
|
+
- [Dashboard](https://v3.recall.team/app)
|
|
62
|
+
- [Support](mailto:support@recall.team)
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
package/dist/api/client.d.ts
CHANGED
|
@@ -61,21 +61,123 @@ export declare class RecallApiClient {
|
|
|
61
61
|
* Called by saveSession tool after reading JSONL from disk
|
|
62
62
|
*/
|
|
63
63
|
summarize(request: SummarizeRequest): Promise<SummarizeResponse>;
|
|
64
|
+
/**
|
|
65
|
+
* Summarize a session transcript using the actual API format
|
|
66
|
+
* This bypasses the strict shared types which don't match the real API
|
|
67
|
+
* Uses 2 minute timeout since AI summarization can take time
|
|
68
|
+
*/
|
|
69
|
+
summarizeRaw<TRequest, TResponse>(request: TRequest): Promise<TResponse>;
|
|
64
70
|
/**
|
|
65
71
|
* Save a session with manual input (not from transcript)
|
|
66
72
|
* Used when user provides summary directly via MCP tool
|
|
73
|
+
* @deprecated Use saveSessionV3 for proper encrypted format
|
|
67
74
|
*/
|
|
68
75
|
saveSession(repoId: string, data: SaveSessionRequest): Promise<SaveSessionResponse>;
|
|
76
|
+
/**
|
|
77
|
+
* Save a session with encrypted content (v3 format)
|
|
78
|
+
* This is the correct format for the v3 API
|
|
79
|
+
*/
|
|
80
|
+
saveSessionV3(repoId: string, data: {
|
|
81
|
+
encrypted_content: string;
|
|
82
|
+
tldr: {
|
|
83
|
+
summary: string;
|
|
84
|
+
status: 'complete' | 'in-progress' | 'blocked';
|
|
85
|
+
decisions?: string[];
|
|
86
|
+
mistakes?: string[];
|
|
87
|
+
tags?: string[];
|
|
88
|
+
files_changed?: string[];
|
|
89
|
+
blockers?: string | null;
|
|
90
|
+
next_steps?: string | null;
|
|
91
|
+
};
|
|
92
|
+
started_at: string;
|
|
93
|
+
ended_at: string;
|
|
94
|
+
tool?: string;
|
|
95
|
+
}): Promise<{
|
|
96
|
+
id: string;
|
|
97
|
+
created_at: string;
|
|
98
|
+
warnings?: string[];
|
|
99
|
+
}>;
|
|
69
100
|
/**
|
|
70
101
|
* Get context for a repository (context.md)
|
|
71
102
|
* Returns the distilled team brain for this repo
|
|
103
|
+
* @deprecated Use getContextV3 for proper v3 API format
|
|
72
104
|
*/
|
|
73
105
|
getContext(repoId: string): Promise<GetContextResponse>;
|
|
106
|
+
/**
|
|
107
|
+
* Get context for a repository (v3 format)
|
|
108
|
+
* Returns sessions with encrypted content for client-side decryption
|
|
109
|
+
*/
|
|
110
|
+
getContextV3(repoId: string): Promise<{
|
|
111
|
+
sessions: Array<{
|
|
112
|
+
id: string;
|
|
113
|
+
encrypted_content: string;
|
|
114
|
+
started_at: string;
|
|
115
|
+
ended_at: string;
|
|
116
|
+
status: string;
|
|
117
|
+
tldr_summary: string | null;
|
|
118
|
+
user: {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string | null;
|
|
121
|
+
github_username: string | null;
|
|
122
|
+
avatar_url: string | null;
|
|
123
|
+
};
|
|
124
|
+
}>;
|
|
125
|
+
cached_context: string;
|
|
126
|
+
updated_at: string;
|
|
127
|
+
session_count: number;
|
|
128
|
+
tier: string;
|
|
129
|
+
}>;
|
|
74
130
|
/**
|
|
75
131
|
* Get history for a repository (context.md + history.md)
|
|
76
132
|
* Returns more detail than getContext
|
|
133
|
+
* @deprecated Use getHistoryV3 for proper v3 API format
|
|
77
134
|
*/
|
|
78
135
|
getHistory(repoId: string): Promise<GetHistoryResponse>;
|
|
136
|
+
/**
|
|
137
|
+
* Get history for a repository (v3 format)
|
|
138
|
+
* Returns sessions, decisions, and mistakes with encrypted content
|
|
139
|
+
*/
|
|
140
|
+
getHistoryV3(repoId: string, days?: number): Promise<{
|
|
141
|
+
sessions: Array<{
|
|
142
|
+
id: string;
|
|
143
|
+
encrypted_content: string;
|
|
144
|
+
started_at: string;
|
|
145
|
+
ended_at: string;
|
|
146
|
+
status: string;
|
|
147
|
+
tldr_summary: string | null;
|
|
148
|
+
user: {
|
|
149
|
+
id: string;
|
|
150
|
+
name: string | null;
|
|
151
|
+
github_username: string | null;
|
|
152
|
+
avatar_url: string | null;
|
|
153
|
+
};
|
|
154
|
+
}>;
|
|
155
|
+
decisions: Array<{
|
|
156
|
+
id: string;
|
|
157
|
+
title: string;
|
|
158
|
+
encrypted_content: string;
|
|
159
|
+
created_at: string;
|
|
160
|
+
user: {
|
|
161
|
+
id: string;
|
|
162
|
+
name: string | null;
|
|
163
|
+
github_username: string | null;
|
|
164
|
+
avatar_url: string | null;
|
|
165
|
+
};
|
|
166
|
+
}>;
|
|
167
|
+
mistakes: Array<{
|
|
168
|
+
id: string;
|
|
169
|
+
title: string;
|
|
170
|
+
encrypted_content: string;
|
|
171
|
+
created_at: string;
|
|
172
|
+
user: {
|
|
173
|
+
id: string;
|
|
174
|
+
name: string | null;
|
|
175
|
+
github_username: string | null;
|
|
176
|
+
avatar_url: string | null;
|
|
177
|
+
};
|
|
178
|
+
}>;
|
|
179
|
+
token_warning?: string;
|
|
180
|
+
}>;
|
|
79
181
|
/**
|
|
80
182
|
* Get full transcripts for a repository
|
|
81
183
|
* WARNING: Can be very large, uses many tokens
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAG3B,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,MAAM;aACZ,UAAU,CAAC,EAAE,MAAM;aACnB,SAAS,EAAE,OAAO;gBAHlC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,GAAE,OAAe;CAK7C;AAED,qBAAa,YAAa,SAAQ,cAAc;aACD,KAAK,CAAC,EAAE,KAAK;gBAA9C,OAAO,EAAE,MAAM,EAAkB,KAAK,CAAC,EAAE,KAAK,YAAA;CAI3D;AAED,qBAAa,mBAAoB,SAAQ,cAAc;gBACzC,OAAO,GAAE,MAAgC;CAItD;AAED,qBAAa,YAAa,SAAQ,cAAc;gBAClC,OAAO,GAAE,MAA4B;CAIlD;AAGD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,qBAAqB;IAQzC;;OAEG;YACW,OAAO;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/api/client.js
CHANGED
|
@@ -69,13 +69,14 @@ class RecallApiClient {
|
|
|
69
69
|
/**
|
|
70
70
|
* Internal fetch wrapper with timeout and retry logic
|
|
71
71
|
*/
|
|
72
|
-
async request(method, path, body) {
|
|
72
|
+
async request(method, path, body, options) {
|
|
73
73
|
const url = `${this.baseUrl}${path}`;
|
|
74
|
+
const timeout = options?.timeout ?? this.timeout;
|
|
74
75
|
let lastError;
|
|
75
76
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
76
77
|
try {
|
|
77
78
|
const controller = new AbortController();
|
|
78
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
79
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
79
80
|
try {
|
|
80
81
|
const response = await fetch(url, {
|
|
81
82
|
method,
|
|
@@ -104,11 +105,18 @@ class RecallApiClient {
|
|
|
104
105
|
throw new RecallApiError(errorMessage, errorCode, response.status, false);
|
|
105
106
|
}
|
|
106
107
|
// Parse successful response
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
// API may return { success: true, data: T } or T directly
|
|
109
|
+
const rawData = await response.json();
|
|
110
|
+
// Check if it's a wrapped response
|
|
111
|
+
if (rawData && typeof rawData === 'object' && 'success' in rawData) {
|
|
112
|
+
const wrapped = rawData;
|
|
113
|
+
if (!wrapped.success && wrapped.error) {
|
|
114
|
+
throw new RecallApiError(wrapped.error.message, wrapped.error.code, response.status, false);
|
|
115
|
+
}
|
|
116
|
+
return wrapped.data;
|
|
110
117
|
}
|
|
111
|
-
|
|
118
|
+
// Direct response (not wrapped)
|
|
119
|
+
return rawData;
|
|
112
120
|
}
|
|
113
121
|
finally {
|
|
114
122
|
clearTimeout(timeoutId);
|
|
@@ -175,67 +183,108 @@ class RecallApiClient {
|
|
|
175
183
|
* Called by saveSession tool after reading JSONL from disk
|
|
176
184
|
*/
|
|
177
185
|
async summarize(request) {
|
|
178
|
-
return this.request('POST', '/
|
|
186
|
+
return this.request('POST', '/v1/summarize', request);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Summarize a session transcript using the actual API format
|
|
190
|
+
* This bypasses the strict shared types which don't match the real API
|
|
191
|
+
* Uses 2 minute timeout since AI summarization can take time
|
|
192
|
+
*/
|
|
193
|
+
async summarizeRaw(request) {
|
|
194
|
+
return this.request('POST', '/v1/summarize', request, { timeout: 120000 });
|
|
179
195
|
}
|
|
180
196
|
/**
|
|
181
197
|
* Save a session with manual input (not from transcript)
|
|
182
198
|
* Used when user provides summary directly via MCP tool
|
|
199
|
+
* @deprecated Use saveSessionV3 for proper encrypted format
|
|
183
200
|
*/
|
|
184
201
|
async saveSession(repoId, data) {
|
|
185
|
-
return this.request('POST', `/
|
|
202
|
+
return this.request('POST', `/v1/repos/${repoId}/sessions`, data);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Save a session with encrypted content (v3 format)
|
|
206
|
+
* This is the correct format for the v3 API
|
|
207
|
+
*/
|
|
208
|
+
async saveSessionV3(repoId, data) {
|
|
209
|
+
return this.request('POST', `/v1/repos/${repoId}/sessions`, data);
|
|
186
210
|
}
|
|
187
211
|
/**
|
|
188
212
|
* Get context for a repository (context.md)
|
|
189
213
|
* Returns the distilled team brain for this repo
|
|
214
|
+
* @deprecated Use getContextV3 for proper v3 API format
|
|
190
215
|
*/
|
|
191
216
|
async getContext(repoId) {
|
|
192
|
-
return this.request('GET', `/
|
|
217
|
+
return this.request('GET', `/v1/repos/${repoId}/context`);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Get context for a repository (v3 format)
|
|
221
|
+
* Returns sessions with encrypted content for client-side decryption
|
|
222
|
+
*/
|
|
223
|
+
async getContextV3(repoId) {
|
|
224
|
+
return this.request('GET', `/v1/repos/${repoId}/context`);
|
|
193
225
|
}
|
|
194
226
|
/**
|
|
195
227
|
* Get history for a repository (context.md + history.md)
|
|
196
228
|
* Returns more detail than getContext
|
|
229
|
+
* @deprecated Use getHistoryV3 for proper v3 API format
|
|
197
230
|
*/
|
|
198
231
|
async getHistory(repoId) {
|
|
199
|
-
return this.request('GET', `/
|
|
232
|
+
return this.request('GET', `/v1/repos/${repoId}/history`);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get history for a repository (v3 format)
|
|
236
|
+
* Returns sessions, decisions, and mistakes with encrypted content
|
|
237
|
+
*/
|
|
238
|
+
async getHistoryV3(repoId, days = 30) {
|
|
239
|
+
return this.request('GET', `/v1/repos/${repoId}/history?days=${days}`);
|
|
200
240
|
}
|
|
201
241
|
/**
|
|
202
242
|
* Get full transcripts for a repository
|
|
203
243
|
* WARNING: Can be very large, uses many tokens
|
|
204
244
|
*/
|
|
205
245
|
async getTranscripts(repoId) {
|
|
206
|
-
return this.request('GET', `/
|
|
246
|
+
return this.request('GET', `/v1/repos/${repoId}/transcripts`);
|
|
207
247
|
}
|
|
208
248
|
/**
|
|
209
249
|
* Log a decision for a repository
|
|
210
250
|
*/
|
|
211
251
|
async logDecision(repoId, data) {
|
|
212
|
-
return this.request('POST', `/
|
|
252
|
+
return this.request('POST', `/v1/repos/${repoId}/decisions`, data);
|
|
213
253
|
}
|
|
214
254
|
/**
|
|
215
255
|
* Get the encryption key for a team
|
|
216
256
|
* Used to decrypt content locally
|
|
217
257
|
*/
|
|
218
258
|
async getTeamKey(teamId) {
|
|
219
|
-
|
|
259
|
+
// API returns { hasAccess, key, keyVersion, teamId, teamName, teamSlug, tier }
|
|
260
|
+
// Map to TeamKeyResponse type
|
|
261
|
+
const response = await this.request('GET', '/v1/keys/team');
|
|
262
|
+
return {
|
|
263
|
+
teamId: response.teamId,
|
|
264
|
+
encryptionKey: response.key,
|
|
265
|
+
keyVersion: response.keyVersion,
|
|
266
|
+
teamName: response.teamName,
|
|
267
|
+
tier: response.tier,
|
|
268
|
+
};
|
|
220
269
|
}
|
|
221
270
|
/**
|
|
222
271
|
* List teams the user belongs to
|
|
223
272
|
*/
|
|
224
273
|
async listTeams() {
|
|
225
|
-
return this.request('GET', '/
|
|
274
|
+
return this.request('GET', '/v1/teams');
|
|
226
275
|
}
|
|
227
276
|
/**
|
|
228
277
|
* Get authentication status
|
|
229
278
|
*/
|
|
230
279
|
async getStatus() {
|
|
231
|
-
return this.request('GET', '/
|
|
280
|
+
return this.request('GET', '/v1/status');
|
|
232
281
|
}
|
|
233
282
|
/**
|
|
234
283
|
* Lookup or create repo by GitHub repo info
|
|
235
284
|
* Returns the repo ID for subsequent API calls
|
|
236
285
|
*/
|
|
237
286
|
async resolveRepo(fullName, defaultBranch) {
|
|
238
|
-
return this.request('POST', '/
|
|
287
|
+
return this.request('POST', '/v1/repos/resolve', {
|
|
239
288
|
fullName,
|
|
240
289
|
defaultBranch,
|
|
241
290
|
});
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Recall v3 CLI
|
|
4
|
+
*
|
|
5
|
+
* Command-line interface for Recall MCP operations.
|
|
6
|
+
* Used by Claude Code hooks and for manual operations.
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* recall-mcp context Load and display team context
|
|
10
|
+
* recall-mcp save Save current session
|
|
11
|
+
* recall-mcp status Check authentication status
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Recall v3 CLI
|
|
5
|
+
*
|
|
6
|
+
* Command-line interface for Recall MCP operations.
|
|
7
|
+
* Used by Claude Code hooks and for manual operations.
|
|
8
|
+
*
|
|
9
|
+
* Commands:
|
|
10
|
+
* recall-mcp context Load and display team context
|
|
11
|
+
* recall-mcp save Save current session
|
|
12
|
+
* recall-mcp status Check authentication status
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const getContext_js_1 = require("./tools/getContext.js");
|
|
16
|
+
const saveSession_js_1 = require("./tools/saveSession.js");
|
|
17
|
+
const index_js_1 = require("./config/index.js");
|
|
18
|
+
const client_js_1 = require("./api/client.js");
|
|
19
|
+
// Parse command line arguments
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
const command = args[0];
|
|
22
|
+
const flags = args.slice(1);
|
|
23
|
+
// Check for --quiet flag
|
|
24
|
+
const quiet = flags.includes('--quiet') || flags.includes('-q');
|
|
25
|
+
// Helper to log only if not quiet
|
|
26
|
+
function log(message) {
|
|
27
|
+
if (!quiet) {
|
|
28
|
+
console.log(message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Helper to log errors (always shown)
|
|
32
|
+
function logError(message) {
|
|
33
|
+
console.error(message);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Parse --project-path flag from args
|
|
37
|
+
*/
|
|
38
|
+
function getProjectPathFromFlags(flagList) {
|
|
39
|
+
const projectPathIndex = flagList.findIndex(f => f === '--project-path' || f === '-p');
|
|
40
|
+
if (projectPathIndex !== -1 && flagList[projectPathIndex + 1]) {
|
|
41
|
+
return flagList[projectPathIndex + 1];
|
|
42
|
+
}
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
// Parse --project-path flag
|
|
46
|
+
const projectPath = getProjectPathFromFlags(flags);
|
|
47
|
+
/**
|
|
48
|
+
* Display usage information
|
|
49
|
+
*/
|
|
50
|
+
function showHelp() {
|
|
51
|
+
console.log(`
|
|
52
|
+
Recall v3 CLI - Team memory for AI coding assistants
|
|
53
|
+
|
|
54
|
+
Usage:
|
|
55
|
+
recall-mcp <command> [options]
|
|
56
|
+
|
|
57
|
+
Commands:
|
|
58
|
+
context Load team context for current repository
|
|
59
|
+
save Save current session (with AI summarization)
|
|
60
|
+
status Check authentication and connection status
|
|
61
|
+
help Show this help message
|
|
62
|
+
|
|
63
|
+
Options:
|
|
64
|
+
--quiet, -q Suppress non-essential output
|
|
65
|
+
--summary, -s Provide a manual summary (skips AI summarization)
|
|
66
|
+
--project-path, -p PATH Specify the git repository path (defaults to cwd)
|
|
67
|
+
|
|
68
|
+
Save Command:
|
|
69
|
+
The save command supports three modes:
|
|
70
|
+
|
|
71
|
+
1. AI Summarization (recommended):
|
|
72
|
+
echo "<conversation transcript>" | recall-mcp save
|
|
73
|
+
- Pipes conversation to API for AI-powered summarization
|
|
74
|
+
- Extracts decisions, mistakes, files changed automatically
|
|
75
|
+
|
|
76
|
+
2. Manual Summary:
|
|
77
|
+
recall-mcp save --summary "Fixed authentication bug"
|
|
78
|
+
- Use when you want to write your own summary
|
|
79
|
+
|
|
80
|
+
3. Placeholder (fallback):
|
|
81
|
+
recall-mcp save --quiet
|
|
82
|
+
- Saves minimal placeholder when no transcript available
|
|
83
|
+
- Used by Claude Code stop hooks as a backup
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
recall-mcp context # Load context at session start
|
|
87
|
+
recall-mcp save --quiet # Save session quietly (for hooks)
|
|
88
|
+
recall-mcp save --summary "Fixed X" # Save with manual summary
|
|
89
|
+
cat transcript.txt | recall-mcp save # Save with AI summarization
|
|
90
|
+
recall-mcp save -p /path/to/repo --quiet # Save for specific repo (Claude Code hooks)
|
|
91
|
+
recall-mcp context --project-path /path/to/repo # Load context for specific repo
|
|
92
|
+
recall-mcp status # Check if properly configured
|
|
93
|
+
|
|
94
|
+
Environment Variables:
|
|
95
|
+
RECALL_API_URL API base URL (default: https://api-v3.recall.team)
|
|
96
|
+
RECALL_API_TOKEN Authentication token (required)
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Load and display team context
|
|
101
|
+
*/
|
|
102
|
+
async function runContext() {
|
|
103
|
+
try {
|
|
104
|
+
const result = await (0, getContext_js_1.getContext)({ projectPath });
|
|
105
|
+
if (result.isError) {
|
|
106
|
+
logError(`Error: ${result.content[0]?.text || 'Unknown error'}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
// Output the context (always, even in quiet mode - it's the primary output)
|
|
110
|
+
console.log(result.content[0]?.text || '');
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
114
|
+
logError(`Failed to load context: ${message}`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Read from stdin if available (non-blocking)
|
|
120
|
+
* Handles large transcripts (Claude Code sessions can be 1MB+)
|
|
121
|
+
*/
|
|
122
|
+
async function readStdin() {
|
|
123
|
+
return new Promise((resolve) => {
|
|
124
|
+
// Check if stdin has data (is a pipe, not TTY)
|
|
125
|
+
if (process.stdin.isTTY) {
|
|
126
|
+
resolve(null);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const chunks = [];
|
|
130
|
+
let resolved = false;
|
|
131
|
+
// Initial timeout - wait up to 500ms for first data
|
|
132
|
+
let timeout = setTimeout(() => {
|
|
133
|
+
if (!resolved) {
|
|
134
|
+
resolved = true;
|
|
135
|
+
resolve(chunks.length > 0 ? chunks.join('') : null);
|
|
136
|
+
}
|
|
137
|
+
}, 500);
|
|
138
|
+
process.stdin.setEncoding('utf8');
|
|
139
|
+
process.stdin.on('data', (chunk) => {
|
|
140
|
+
// Clear and reset timeout on each chunk (allows large files)
|
|
141
|
+
clearTimeout(timeout);
|
|
142
|
+
chunks.push(chunk);
|
|
143
|
+
// Set a shorter timeout between chunks (100ms idle = end of data)
|
|
144
|
+
timeout = setTimeout(() => {
|
|
145
|
+
if (!resolved) {
|
|
146
|
+
resolved = true;
|
|
147
|
+
resolve(chunks.join(''));
|
|
148
|
+
}
|
|
149
|
+
}, 100);
|
|
150
|
+
});
|
|
151
|
+
process.stdin.on('end', () => {
|
|
152
|
+
clearTimeout(timeout);
|
|
153
|
+
if (!resolved) {
|
|
154
|
+
resolved = true;
|
|
155
|
+
resolve(chunks.length > 0 ? chunks.join('') : null);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
process.stdin.on('error', () => {
|
|
159
|
+
clearTimeout(timeout);
|
|
160
|
+
if (!resolved) {
|
|
161
|
+
resolved = true;
|
|
162
|
+
resolve(null);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
// Start reading
|
|
166
|
+
process.stdin.resume();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Save current session
|
|
171
|
+
*
|
|
172
|
+
* Supports two modes:
|
|
173
|
+
* 1. With transcript via stdin: `echo "conversation..." | recall-mcp save`
|
|
174
|
+
* - Sends transcript to API for AI summarization
|
|
175
|
+
* 2. Without transcript: `recall-mcp save`
|
|
176
|
+
* - Saves a minimal placeholder (fallback)
|
|
177
|
+
*
|
|
178
|
+
* Usage with --summary flag: `recall-mcp save --summary "What we did"`
|
|
179
|
+
*/
|
|
180
|
+
async function runSave() {
|
|
181
|
+
try {
|
|
182
|
+
// Check for --summary flag
|
|
183
|
+
const summaryIndex = flags.findIndex(f => f === '--summary' || f === '-s');
|
|
184
|
+
let providedSummary;
|
|
185
|
+
if (summaryIndex !== -1 && flags[summaryIndex + 1]) {
|
|
186
|
+
providedSummary = flags[summaryIndex + 1];
|
|
187
|
+
}
|
|
188
|
+
// Try to read transcript from stdin
|
|
189
|
+
const transcript = await readStdin();
|
|
190
|
+
// Build saveSession args
|
|
191
|
+
const saveArgs = {};
|
|
192
|
+
// Add project path if specified
|
|
193
|
+
if (projectPath) {
|
|
194
|
+
saveArgs.projectPath = projectPath;
|
|
195
|
+
}
|
|
196
|
+
if (transcript && transcript.trim().length >= 50) {
|
|
197
|
+
// We have a transcript - send it for AI summarization
|
|
198
|
+
saveArgs.transcript = transcript;
|
|
199
|
+
log('Summarizing session with AI...');
|
|
200
|
+
}
|
|
201
|
+
else if (providedSummary) {
|
|
202
|
+
// Use provided summary
|
|
203
|
+
saveArgs.summary = providedSummary;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
// Fallback to placeholder - this is the minimal save case
|
|
207
|
+
saveArgs.summary = 'Session auto-saved by Recall hook';
|
|
208
|
+
}
|
|
209
|
+
const result = await (0, saveSession_js_1.saveSession)(saveArgs);
|
|
210
|
+
if (result.isError) {
|
|
211
|
+
logError(`Error: ${result.content[0]?.text || 'Unknown error'}`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
log(result.content[0]?.text || 'Session saved');
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
218
|
+
logError(`Failed to save session: ${message}`);
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check authentication and connection status
|
|
224
|
+
*/
|
|
225
|
+
async function runStatus() {
|
|
226
|
+
try {
|
|
227
|
+
const token = (0, index_js_1.getApiToken)();
|
|
228
|
+
const baseUrl = (0, index_js_1.getApiBaseUrl)();
|
|
229
|
+
console.log('Recall v3 Status');
|
|
230
|
+
console.log('================');
|
|
231
|
+
console.log(`Config: ${(0, index_js_1.getConfigPath)()}`);
|
|
232
|
+
console.log(`API URL: ${baseUrl}`);
|
|
233
|
+
console.log(`Token: ${token ? `${token.substring(0, 10)}...` : 'Not set'}`);
|
|
234
|
+
if (!token) {
|
|
235
|
+
logError('\nError: RECALL_API_TOKEN not set');
|
|
236
|
+
logError(`Set the environment variable or configure ${(0, index_js_1.getConfigPath)()}`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
// Test API connection
|
|
240
|
+
const client = new client_js_1.RecallApiClient({
|
|
241
|
+
baseUrl,
|
|
242
|
+
token,
|
|
243
|
+
});
|
|
244
|
+
console.log('\nTesting API connection...');
|
|
245
|
+
try {
|
|
246
|
+
const teams = await client.listTeams();
|
|
247
|
+
console.log(`✓ Connected successfully`);
|
|
248
|
+
console.log(`✓ Teams: ${teams.teams?.length || 0}`);
|
|
249
|
+
if (teams.teams && teams.teams.length > 0) {
|
|
250
|
+
for (const team of teams.teams) {
|
|
251
|
+
console.log(` - ${team.name} (${team.role})`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (apiError) {
|
|
256
|
+
const message = apiError instanceof Error ? apiError.message : 'Unknown error';
|
|
257
|
+
logError(`✗ API connection failed: ${message}`);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
263
|
+
logError(`Status check failed: ${message}`);
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Main entry point
|
|
269
|
+
*/
|
|
270
|
+
async function main() {
|
|
271
|
+
switch (command) {
|
|
272
|
+
case 'context':
|
|
273
|
+
await runContext();
|
|
274
|
+
break;
|
|
275
|
+
case 'save':
|
|
276
|
+
await runSave();
|
|
277
|
+
break;
|
|
278
|
+
case 'status':
|
|
279
|
+
await runStatus();
|
|
280
|
+
break;
|
|
281
|
+
case 'help':
|
|
282
|
+
case '--help':
|
|
283
|
+
case '-h':
|
|
284
|
+
case undefined:
|
|
285
|
+
showHelp();
|
|
286
|
+
break;
|
|
287
|
+
default:
|
|
288
|
+
logError(`Unknown command: ${command}`);
|
|
289
|
+
logError('Run "recall-mcp help" for usage information');
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Run CLI
|
|
294
|
+
main().catch((error) => {
|
|
295
|
+
logError(`Fatal error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
296
|
+
process.exit(1);
|
|
297
|
+
});
|