@recall_v3/mcp-server 0.1.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 (50) hide show
  1. package/dist/api/client.d.ts +111 -0
  2. package/dist/api/client.d.ts.map +1 -0
  3. package/dist/api/client.js +244 -0
  4. package/dist/config/index.d.ts +89 -0
  5. package/dist/config/index.d.ts.map +1 -0
  6. package/dist/config/index.js +256 -0
  7. package/dist/crypto/index.d.ts +56 -0
  8. package/dist/crypto/index.d.ts.map +1 -0
  9. package/dist/crypto/index.js +224 -0
  10. package/dist/index.d.ts +10 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +189 -0
  13. package/dist/tools/getContext.d.ts +18 -0
  14. package/dist/tools/getContext.d.ts.map +1 -0
  15. package/dist/tools/getContext.js +87 -0
  16. package/dist/tools/getHistory.d.ts +18 -0
  17. package/dist/tools/getHistory.d.ts.map +1 -0
  18. package/dist/tools/getHistory.js +97 -0
  19. package/dist/tools/getTranscripts.d.ts +19 -0
  20. package/dist/tools/getTranscripts.d.ts.map +1 -0
  21. package/dist/tools/getTranscripts.js +129 -0
  22. package/dist/tools/index.d.ts +13 -0
  23. package/dist/tools/index.d.ts.map +1 -0
  24. package/dist/tools/index.js +37 -0
  25. package/dist/tools/logDecision.d.ts +19 -0
  26. package/dist/tools/logDecision.d.ts.map +1 -0
  27. package/dist/tools/logDecision.js +92 -0
  28. package/dist/tools/saveSession.d.ts +26 -0
  29. package/dist/tools/saveSession.d.ts.map +1 -0
  30. package/dist/tools/saveSession.js +115 -0
  31. package/dist/tools/types.d.ts +32 -0
  32. package/dist/tools/types.d.ts.map +1 -0
  33. package/dist/tools/types.js +33 -0
  34. package/dist/tools/utils.d.ts +52 -0
  35. package/dist/tools/utils.d.ts.map +1 -0
  36. package/dist/tools/utils.js +238 -0
  37. package/package.json +46 -0
  38. package/src/api/client.ts +295 -0
  39. package/src/config/index.ts +247 -0
  40. package/src/crypto/index.ts +207 -0
  41. package/src/index.ts +232 -0
  42. package/src/tools/getContext.ts +106 -0
  43. package/src/tools/getHistory.ts +118 -0
  44. package/src/tools/getTranscripts.ts +150 -0
  45. package/src/tools/index.ts +13 -0
  46. package/src/tools/logDecision.ts +118 -0
  47. package/src/tools/saveSession.ts +159 -0
  48. package/src/tools/types.ts +47 -0
  49. package/src/tools/utils.ts +226 -0
  50. package/tsconfig.json +14 -0
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * logDecision Tool Implementation
4
+ *
5
+ * Logs an important decision made during coding.
6
+ * Quick way to capture why something was done.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.logDecision = logDecision;
10
+ const client_js_1 = require("../api/client.js");
11
+ const index_js_1 = require("../config/index.js");
12
+ const types_js_1 = require("./types.js");
13
+ const utils_js_1 = require("./utils.js");
14
+ /**
15
+ * Execute the logDecision tool
16
+ *
17
+ * @param args - Tool arguments with decision and reasoning
18
+ * @returns MCP tool response with confirmation
19
+ */
20
+ async function logDecision(args) {
21
+ try {
22
+ // Validate required fields
23
+ if (!args.decision || args.decision.trim().length === 0) {
24
+ return (0, types_js_1.errorResponse)('Decision is required. Please describe what was decided.');
25
+ }
26
+ if (!args.reasoning || args.reasoning.trim().length === 0) {
27
+ return (0, types_js_1.errorResponse)('Reasoning is required. Please explain why this decision was made.');
28
+ }
29
+ // Get API token
30
+ const token = (0, index_js_1.getApiToken)();
31
+ if (!token) {
32
+ return (0, types_js_1.errorResponse)('Not authenticated. Run `recall auth` to connect your account, or set RECALL_API_TOKEN environment variable.');
33
+ }
34
+ // Resolve project path (use cwd)
35
+ const projectPath = (0, utils_js_1.resolveProjectPath)(undefined);
36
+ // Get repo info from git
37
+ const repoInfo = await (0, utils_js_1.getRepoInfo)(projectPath);
38
+ if (!repoInfo) {
39
+ return (0, types_js_1.errorResponse)(`Could not determine repository info for: ${projectPath}\n` +
40
+ 'Make sure this is a git repository with a remote origin.');
41
+ }
42
+ // Create API client
43
+ const client = new client_js_1.RecallApiClient({
44
+ baseUrl: (0, index_js_1.getApiBaseUrl)(),
45
+ token,
46
+ });
47
+ // Resolve repo to get repoId and teamId
48
+ const { repoId, teamId } = await client.resolveRepo(repoInfo.fullName, repoInfo.defaultBranch);
49
+ // Get team key (fetch from API if not cached)
50
+ let teamKey = (0, index_js_1.getTeamKey)(teamId);
51
+ if (!teamKey) {
52
+ const keyResponse = await client.getTeamKey(teamId);
53
+ teamKey = keyResponse.encryptionKey;
54
+ (0, index_js_1.setTeamKey)(teamId, teamKey);
55
+ }
56
+ // Build the decision request
57
+ const decisionRequest = {
58
+ decision: args.decision.trim(),
59
+ reasoning: args.reasoning.trim(),
60
+ };
61
+ // Log decision via API
62
+ const response = await client.logDecision(repoId, decisionRequest);
63
+ // Build success message
64
+ const successMessage = [
65
+ `Decision logged successfully.`,
66
+ ``,
67
+ `Decision ID: ${response.decisionId}`,
68
+ `Repository: ${repoInfo.fullName}`,
69
+ ``,
70
+ `What: ${args.decision.trim()}`,
71
+ `Why: ${args.reasoning.trim()}`,
72
+ ``,
73
+ `This decision has been added to the team memory.`,
74
+ ].join('\n');
75
+ return (0, types_js_1.successResponse)(successMessage);
76
+ }
77
+ catch (error) {
78
+ if (error instanceof client_js_1.AuthenticationError) {
79
+ return (0, types_js_1.errorResponse)('Authentication failed. Your token may have expired.\n' +
80
+ 'Run `recall auth` to reconnect your account.');
81
+ }
82
+ if (error instanceof client_js_1.RecallApiError) {
83
+ if (error.code === 'REPO_NOT_FOUND') {
84
+ return (0, types_js_1.errorResponse)('This repository is not connected to Recall.\n' +
85
+ 'Run `recall init` to set up team memory for this repo.');
86
+ }
87
+ return (0, types_js_1.errorResponse)(`API Error: ${error.message}`);
88
+ }
89
+ const message = error instanceof Error ? error.message : 'Unknown error';
90
+ return (0, types_js_1.errorResponse)(message);
91
+ }
92
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * saveSession Tool Implementation
3
+ *
4
+ * Saves a summary of what was accomplished in this coding session.
5
+ * Updates the team memory files via the Recall API.
6
+ */
7
+ import { type ToolResponse } from './types.js';
8
+ export interface SaveSessionArgs {
9
+ summary: string;
10
+ decisions?: Array<{
11
+ what: string;
12
+ why: string;
13
+ }>;
14
+ mistakes?: string[];
15
+ filesChanged?: string[];
16
+ nextSteps?: string;
17
+ blockers?: string;
18
+ }
19
+ /**
20
+ * Execute the saveSession tool
21
+ *
22
+ * @param args - Tool arguments with session summary and metadata
23
+ * @returns MCP tool response with save confirmation
24
+ */
25
+ export declare function saveSession(args: SaveSessionArgs): Promise<ToolResponse>;
26
+ //# sourceMappingURL=saveSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saveSession.d.ts","sourceRoot":"","sources":["../../src/tools/saveSession.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CA8H9E"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ /**
3
+ * saveSession Tool Implementation
4
+ *
5
+ * Saves a summary of what was accomplished in this coding session.
6
+ * Updates the team memory files via the Recall API.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.saveSession = saveSession;
10
+ const client_js_1 = require("../api/client.js");
11
+ const index_js_1 = require("../config/index.js");
12
+ const types_js_1 = require("./types.js");
13
+ const utils_js_1 = require("./utils.js");
14
+ /**
15
+ * Execute the saveSession tool
16
+ *
17
+ * @param args - Tool arguments with session summary and metadata
18
+ * @returns MCP tool response with save confirmation
19
+ */
20
+ async function saveSession(args) {
21
+ try {
22
+ // Validate required fields
23
+ if (!args.summary || args.summary.trim().length === 0) {
24
+ return (0, types_js_1.errorResponse)('Summary is required. Please provide a description of what was accomplished.');
25
+ }
26
+ // Get API token
27
+ const token = (0, index_js_1.getApiToken)();
28
+ if (!token) {
29
+ return (0, types_js_1.errorResponse)('Not authenticated. Run `recall auth` to connect your account, or set RECALL_API_TOKEN environment variable.');
30
+ }
31
+ // Resolve project path (use cwd since projectPath is not in args)
32
+ const projectPath = (0, utils_js_1.resolveProjectPath)(undefined);
33
+ // Get repo info from git
34
+ const repoInfo = await (0, utils_js_1.getRepoInfo)(projectPath);
35
+ if (!repoInfo) {
36
+ return (0, types_js_1.errorResponse)(`Could not determine repository info for: ${projectPath}\n` +
37
+ 'Make sure this is a git repository with a remote origin.');
38
+ }
39
+ // Create API client
40
+ const client = new client_js_1.RecallApiClient({
41
+ baseUrl: (0, index_js_1.getApiBaseUrl)(),
42
+ token,
43
+ });
44
+ // Resolve repo to get repoId and teamId
45
+ const { repoId, teamId } = await client.resolveRepo(repoInfo.fullName, repoInfo.defaultBranch);
46
+ // Get team key (fetch from API if not cached)
47
+ let teamKey = (0, index_js_1.getTeamKey)(teamId);
48
+ if (!teamKey) {
49
+ const keyResponse = await client.getTeamKey(teamId);
50
+ teamKey = keyResponse.encryptionKey;
51
+ (0, index_js_1.setTeamKey)(teamId, teamKey);
52
+ }
53
+ // Build the session request
54
+ const sessionRequest = {
55
+ summary: args.summary.trim(),
56
+ };
57
+ if (args.decisions && args.decisions.length > 0) {
58
+ sessionRequest.decisions = args.decisions;
59
+ }
60
+ if (args.mistakes && args.mistakes.length > 0) {
61
+ sessionRequest.mistakes = args.mistakes;
62
+ }
63
+ if (args.filesChanged && args.filesChanged.length > 0) {
64
+ sessionRequest.filesChanged = args.filesChanged;
65
+ }
66
+ if (args.nextSteps && args.nextSteps.trim().length > 0) {
67
+ sessionRequest.nextSteps = args.nextSteps.trim();
68
+ }
69
+ if (args.blockers && args.blockers.trim().length > 0) {
70
+ sessionRequest.blockers = args.blockers.trim();
71
+ }
72
+ // Save session via API
73
+ const response = await client.saveSession(repoId, sessionRequest);
74
+ // Build success message
75
+ const parts = [
76
+ `Session saved successfully.`,
77
+ ``,
78
+ `Session ID: ${response.sessionId}`,
79
+ `Repository: ${repoInfo.fullName}`,
80
+ ];
81
+ if (args.decisions && args.decisions.length > 0) {
82
+ parts.push(`Decisions logged: ${args.decisions.length}`);
83
+ }
84
+ if (args.mistakes && args.mistakes.length > 0) {
85
+ parts.push(`Mistakes documented: ${args.mistakes.length}`);
86
+ }
87
+ if (args.filesChanged && args.filesChanged.length > 0) {
88
+ parts.push(`Files tracked: ${args.filesChanged.length}`);
89
+ }
90
+ if (args.nextSteps) {
91
+ parts.push(`Next steps: ${args.nextSteps}`);
92
+ }
93
+ if (args.blockers) {
94
+ parts.push(`Blockers: ${args.blockers}`);
95
+ }
96
+ parts.push(``);
97
+ parts.push(`Team memory has been updated.`);
98
+ return (0, types_js_1.successResponse)(parts.join('\n'));
99
+ }
100
+ catch (error) {
101
+ if (error instanceof client_js_1.AuthenticationError) {
102
+ return (0, types_js_1.errorResponse)('Authentication failed. Your token may have expired.\n' +
103
+ 'Run `recall auth` to reconnect your account.');
104
+ }
105
+ if (error instanceof client_js_1.RecallApiError) {
106
+ if (error.code === 'REPO_NOT_FOUND') {
107
+ return (0, types_js_1.errorResponse)('This repository is not connected to Recall.\n' +
108
+ 'Run `recall init` to set up team memory for this repo.');
109
+ }
110
+ return (0, types_js_1.errorResponse)(`API Error: ${error.message}`);
111
+ }
112
+ const message = error instanceof Error ? error.message : 'Unknown error';
113
+ return (0, types_js_1.errorResponse)(message);
114
+ }
115
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * MCP Tool Response Types
3
+ *
4
+ * Shared types for tool implementations.
5
+ */
6
+ /**
7
+ * Content item in MCP response
8
+ */
9
+ export interface TextContent {
10
+ type: 'text';
11
+ text: string;
12
+ }
13
+ /**
14
+ * Standard MCP tool response format
15
+ */
16
+ export interface ToolResponse {
17
+ content: TextContent[];
18
+ isError?: boolean;
19
+ }
20
+ /**
21
+ * Create a success response
22
+ */
23
+ export declare function successResponse(text: string): ToolResponse;
24
+ /**
25
+ * Create an error response
26
+ */
27
+ export declare function errorResponse(message: string): ToolResponse;
28
+ /**
29
+ * Create a formatted response with header and content
30
+ */
31
+ export declare function formattedResponse(header: string, content: string): ToolResponse;
32
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAI1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAK3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAE/E"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /**
3
+ * MCP Tool Response Types
4
+ *
5
+ * Shared types for tool implementations.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.successResponse = successResponse;
9
+ exports.errorResponse = errorResponse;
10
+ exports.formattedResponse = formattedResponse;
11
+ /**
12
+ * Create a success response
13
+ */
14
+ function successResponse(text) {
15
+ return {
16
+ content: [{ type: 'text', text }],
17
+ };
18
+ }
19
+ /**
20
+ * Create an error response
21
+ */
22
+ function errorResponse(message) {
23
+ return {
24
+ content: [{ type: 'text', text: `Error: ${message}` }],
25
+ isError: true,
26
+ };
27
+ }
28
+ /**
29
+ * Create a formatted response with header and content
30
+ */
31
+ function formattedResponse(header, content) {
32
+ return successResponse(`[${header}]\n\n${content}`);
33
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Tool Utilities
3
+ *
4
+ * Shared utilities for tool implementations.
5
+ */
6
+ /**
7
+ * Repository information extracted from git
8
+ */
9
+ export interface RepoInfo {
10
+ /** Full name like "owner/repo" */
11
+ fullName: string;
12
+ /** Remote URL */
13
+ remoteUrl: string;
14
+ /** Default branch name */
15
+ defaultBranch: string;
16
+ /** Local repo root path */
17
+ localPath: string;
18
+ }
19
+ /**
20
+ * Resolve project path from argument or cwd
21
+ */
22
+ export declare function resolveProjectPath(projectPath?: string): string;
23
+ /**
24
+ * Find the git root directory for a given path
25
+ */
26
+ export declare function findGitRoot(startPath: string): string | null;
27
+ /**
28
+ * Get repository information from git
29
+ */
30
+ export declare function getRepoInfo(projectPath: string): Promise<RepoInfo | null>;
31
+ /**
32
+ * Read a file if it exists
33
+ */
34
+ export declare function readFileIfExists(filePath: string): string | null;
35
+ /**
36
+ * Get the Claude session directory for a project
37
+ * Claude stores sessions in ~/.claude/projects/<hash>/
38
+ */
39
+ export declare function getClaudeSessionDir(projectPath: string): string | null;
40
+ /**
41
+ * Find the most recent Claude session JSONL file
42
+ */
43
+ export declare function findLatestSessionFile(projectPath: string): string | null;
44
+ /**
45
+ * Format bytes to human-readable size
46
+ */
47
+ export declare function formatBytes(bytes: number): string;
48
+ /**
49
+ * Format duration in seconds to human-readable
50
+ */
51
+ export declare function formatDuration(seconds: number): string;
52
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D;AAqCD;;GAEG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoC/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoCtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMtD"}
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ /**
3
+ * Tool Utilities
4
+ *
5
+ * Shared utilities for tool implementations.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.resolveProjectPath = resolveProjectPath;
42
+ exports.findGitRoot = findGitRoot;
43
+ exports.getRepoInfo = getRepoInfo;
44
+ exports.readFileIfExists = readFileIfExists;
45
+ exports.getClaudeSessionDir = getClaudeSessionDir;
46
+ exports.findLatestSessionFile = findLatestSessionFile;
47
+ exports.formatBytes = formatBytes;
48
+ exports.formatDuration = formatDuration;
49
+ const fs = __importStar(require("node:fs"));
50
+ const path = __importStar(require("node:path"));
51
+ const node_child_process_1 = require("node:child_process");
52
+ /**
53
+ * Resolve project path from argument or cwd
54
+ */
55
+ function resolveProjectPath(projectPath) {
56
+ if (projectPath) {
57
+ // Resolve to absolute path
58
+ return path.resolve(projectPath);
59
+ }
60
+ // Use current working directory
61
+ return process.cwd();
62
+ }
63
+ /**
64
+ * Find the git root directory for a given path
65
+ */
66
+ function findGitRoot(startPath) {
67
+ let currentPath = path.resolve(startPath);
68
+ while (currentPath !== path.dirname(currentPath)) {
69
+ if (fs.existsSync(path.join(currentPath, '.git'))) {
70
+ return currentPath;
71
+ }
72
+ currentPath = path.dirname(currentPath);
73
+ }
74
+ return null;
75
+ }
76
+ /**
77
+ * Execute a git command in the given directory
78
+ */
79
+ function execGit(args, cwd) {
80
+ try {
81
+ const result = (0, node_child_process_1.execSync)(`git ${args.join(' ')}`, {
82
+ cwd,
83
+ encoding: 'utf-8',
84
+ stdio: ['pipe', 'pipe', 'pipe'],
85
+ });
86
+ return result.trim();
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * Parse GitHub/GitLab remote URL to get owner/repo
94
+ */
95
+ function parseRemoteUrl(url) {
96
+ // SSH format: git@github.com:owner/repo.git
97
+ const sshMatch = url.match(/git@[\w.-]+:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
98
+ if (sshMatch) {
99
+ return sshMatch[1];
100
+ }
101
+ // HTTPS format: https://github.com/owner/repo.git
102
+ const httpsMatch = url.match(/https?:\/\/[\w.-]+\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
103
+ if (httpsMatch) {
104
+ return httpsMatch[1];
105
+ }
106
+ return null;
107
+ }
108
+ /**
109
+ * Get repository information from git
110
+ */
111
+ async function getRepoInfo(projectPath) {
112
+ // Find git root
113
+ const gitRoot = findGitRoot(projectPath);
114
+ if (!gitRoot) {
115
+ return null;
116
+ }
117
+ // Get remote URL
118
+ const remoteUrl = execGit(['config', '--get', 'remote.origin.url'], gitRoot);
119
+ if (!remoteUrl) {
120
+ return null;
121
+ }
122
+ // Parse full name from URL
123
+ const fullName = parseRemoteUrl(remoteUrl);
124
+ if (!fullName) {
125
+ return null;
126
+ }
127
+ // Get default branch
128
+ // Try to get from remote HEAD first
129
+ let defaultBranch = execGit(['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'], gitRoot);
130
+ if (defaultBranch) {
131
+ // Remove "origin/" prefix
132
+ defaultBranch = defaultBranch.replace(/^origin\//, '');
133
+ }
134
+ else {
135
+ // Fallback to common defaults
136
+ defaultBranch = 'main';
137
+ }
138
+ return {
139
+ fullName,
140
+ remoteUrl,
141
+ defaultBranch,
142
+ localPath: gitRoot,
143
+ };
144
+ }
145
+ /**
146
+ * Read a file if it exists
147
+ */
148
+ function readFileIfExists(filePath) {
149
+ try {
150
+ return fs.readFileSync(filePath, 'utf-8');
151
+ }
152
+ catch {
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Get the Claude session directory for a project
158
+ * Claude stores sessions in ~/.claude/projects/<hash>/
159
+ */
160
+ function getClaudeSessionDir(projectPath) {
161
+ const claudeDir = path.join(process.env.HOME || '', '.claude', 'projects');
162
+ if (!fs.existsSync(claudeDir)) {
163
+ return null;
164
+ }
165
+ // Claude uses a hash of the project path
166
+ // We need to find the matching directory
167
+ try {
168
+ const dirs = fs.readdirSync(claudeDir);
169
+ for (const dir of dirs) {
170
+ const sessionPath = path.join(claudeDir, dir);
171
+ const stat = fs.statSync(sessionPath);
172
+ if (!stat.isDirectory())
173
+ continue;
174
+ // Check if this directory's config points to our project
175
+ const configPath = path.join(sessionPath, 'config.json');
176
+ if (fs.existsSync(configPath)) {
177
+ try {
178
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
179
+ if (config.projectPath === projectPath) {
180
+ return sessionPath;
181
+ }
182
+ }
183
+ catch {
184
+ // Skip invalid config files
185
+ }
186
+ }
187
+ }
188
+ }
189
+ catch {
190
+ return null;
191
+ }
192
+ return null;
193
+ }
194
+ /**
195
+ * Find the most recent Claude session JSONL file
196
+ */
197
+ function findLatestSessionFile(projectPath) {
198
+ const sessionDir = getClaudeSessionDir(projectPath);
199
+ if (!sessionDir) {
200
+ return null;
201
+ }
202
+ try {
203
+ const files = fs.readdirSync(sessionDir)
204
+ .filter(f => f.endsWith('.jsonl'))
205
+ .map(f => ({
206
+ name: f,
207
+ path: path.join(sessionDir, f),
208
+ mtime: fs.statSync(path.join(sessionDir, f)).mtime,
209
+ }))
210
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
211
+ return files.length > 0 ? files[0].path : null;
212
+ }
213
+ catch {
214
+ return null;
215
+ }
216
+ }
217
+ /**
218
+ * Format bytes to human-readable size
219
+ */
220
+ function formatBytes(bytes) {
221
+ if (bytes < 1024)
222
+ return `${bytes} B`;
223
+ if (bytes < 1024 * 1024)
224
+ return `${(bytes / 1024).toFixed(1)} KB`;
225
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
226
+ }
227
+ /**
228
+ * Format duration in seconds to human-readable
229
+ */
230
+ function formatDuration(seconds) {
231
+ if (seconds < 60)
232
+ return `${seconds}s`;
233
+ if (seconds < 3600)
234
+ return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
235
+ const hours = Math.floor(seconds / 3600);
236
+ const mins = Math.floor((seconds % 3600) / 60);
237
+ return `${hours}h ${mins}m`;
238
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@recall_v3/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "Recall MCP Server - Team memory for AI coding assistants",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "recall-mcp": "./dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "typecheck": "tsc --noEmit",
15
+ "clean": "rm -rf dist",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^0.6.0",
20
+ "@recall_v3/shared": "^0.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^20.10.0",
24
+ "typescript": "^5.3.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "claude",
32
+ "ai",
33
+ "memory",
34
+ "coding",
35
+ "recall"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "author": "Stoodio",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/stoodiohq/recall-v3"
45
+ }
46
+ }