@squidcode/forever-plugin 0.1.0 → 0.2.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/dist/index.js +87 -14
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,15 +2,51 @@
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
|
+
import { randomBytes } from 'crypto';
|
|
7
|
+
import { basename } from 'path';
|
|
5
8
|
import { createApiClient } from './client.js';
|
|
6
9
|
import { getOrCreateMachineId } from './machine.js';
|
|
7
10
|
const server = new McpServer({
|
|
8
11
|
name: 'forever',
|
|
9
|
-
version: '0.
|
|
12
|
+
version: '0.2.0',
|
|
10
13
|
});
|
|
11
14
|
const machineId = getOrCreateMachineId();
|
|
15
|
+
const sessionId = `${Date.now()}-${randomBytes(4).toString('hex')}`;
|
|
16
|
+
function git(...args) {
|
|
17
|
+
try {
|
|
18
|
+
return execFileSync('git', args, {
|
|
19
|
+
encoding: 'utf-8',
|
|
20
|
+
timeout: 5000,
|
|
21
|
+
}).trim();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getGitContext() {
|
|
28
|
+
return {
|
|
29
|
+
gitBranch: git('rev-parse', '--abbrev-ref', 'HEAD'),
|
|
30
|
+
gitCommit: git('rev-parse', '--short', 'HEAD'),
|
|
31
|
+
directory: process.cwd(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function resolveProject(explicit) {
|
|
35
|
+
if (explicit)
|
|
36
|
+
return explicit;
|
|
37
|
+
const remote = git('remote', 'get-url', 'origin');
|
|
38
|
+
if (remote)
|
|
39
|
+
return remote;
|
|
40
|
+
const dir = process.cwd();
|
|
41
|
+
if (dir && dir !== '/')
|
|
42
|
+
return basename(dir);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
12
45
|
server.tool('memory_log', 'Log an entry to Forever memory (summary, decision, or error)', {
|
|
13
|
-
project: z
|
|
46
|
+
project: z
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Project name or git remote URL (auto-detected from git if omitted)'),
|
|
14
50
|
type: z
|
|
15
51
|
.enum(['summary', 'decision', 'error'])
|
|
16
52
|
.describe('Type of memory entry'),
|
|
@@ -19,30 +55,51 @@ server.tool('memory_log', 'Log an entry to Forever memory (summary, decision, or
|
|
|
19
55
|
.array(z.string())
|
|
20
56
|
.optional()
|
|
21
57
|
.describe('Optional tags for categorization'),
|
|
22
|
-
sessionId: z
|
|
23
|
-
|
|
58
|
+
sessionId: z
|
|
59
|
+
.string()
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('Session ID for grouping (auto-generated if omitted)'),
|
|
62
|
+
}, async ({ project, type, content, tags, sessionId: explicitSessionId }) => {
|
|
24
63
|
const api = createApiClient();
|
|
25
64
|
if (!api) {
|
|
26
65
|
return {
|
|
27
66
|
content: [
|
|
28
67
|
{
|
|
29
68
|
type: 'text',
|
|
30
|
-
text: 'Not authenticated. Run: forever-plugin login',
|
|
69
|
+
text: 'Not authenticated. Run: npx @squidcode/forever-plugin login',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const resolvedProject = resolveProject(project);
|
|
75
|
+
if (!resolvedProject) {
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: 'Could not detect project. Please specify a project name.',
|
|
31
81
|
},
|
|
32
82
|
],
|
|
33
83
|
};
|
|
34
84
|
}
|
|
85
|
+
const gitContext = getGitContext();
|
|
35
86
|
try {
|
|
36
87
|
await api.post('/logs', {
|
|
37
|
-
project,
|
|
88
|
+
project: resolvedProject,
|
|
38
89
|
type,
|
|
39
90
|
content,
|
|
40
91
|
machineId,
|
|
41
92
|
tags,
|
|
42
|
-
sessionId,
|
|
93
|
+
sessionId: explicitSessionId || sessionId,
|
|
94
|
+
...gitContext,
|
|
43
95
|
});
|
|
44
96
|
return {
|
|
45
|
-
content: [
|
|
97
|
+
content: [
|
|
98
|
+
{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `Logged ${type} entry for "${resolvedProject}".`,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
46
103
|
};
|
|
47
104
|
}
|
|
48
105
|
catch (err) {
|
|
@@ -57,7 +114,10 @@ server.tool('memory_log', 'Log an entry to Forever memory (summary, decision, or
|
|
|
57
114
|
}
|
|
58
115
|
});
|
|
59
116
|
server.tool('memory_get_recent', 'Get recent memory entries for a project', {
|
|
60
|
-
project: z
|
|
117
|
+
project: z
|
|
118
|
+
.string()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe('Project name or git remote URL (auto-detected from git if omitted)'),
|
|
61
121
|
limit: z
|
|
62
122
|
.number()
|
|
63
123
|
.optional()
|
|
@@ -70,14 +130,25 @@ server.tool('memory_get_recent', 'Get recent memory entries for a project', {
|
|
|
70
130
|
content: [
|
|
71
131
|
{
|
|
72
132
|
type: 'text',
|
|
73
|
-
text: 'Not authenticated. Run: forever-plugin login',
|
|
133
|
+
text: 'Not authenticated. Run: npx @squidcode/forever-plugin login',
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const resolvedProject = resolveProject(project);
|
|
139
|
+
if (!resolvedProject) {
|
|
140
|
+
return {
|
|
141
|
+
content: [
|
|
142
|
+
{
|
|
143
|
+
type: 'text',
|
|
144
|
+
text: 'Could not detect project. Please specify a project name.',
|
|
74
145
|
},
|
|
75
146
|
],
|
|
76
147
|
};
|
|
77
148
|
}
|
|
78
149
|
try {
|
|
79
150
|
const res = await api.get('/logs/recent', {
|
|
80
|
-
params: { project, limit },
|
|
151
|
+
params: { project: resolvedProject, limit },
|
|
81
152
|
});
|
|
82
153
|
const logs = res.data;
|
|
83
154
|
if (!logs.length) {
|
|
@@ -85,7 +156,7 @@ server.tool('memory_get_recent', 'Get recent memory entries for a project', {
|
|
|
85
156
|
content: [
|
|
86
157
|
{
|
|
87
158
|
type: 'text',
|
|
88
|
-
text: `No memory entries found for project "${
|
|
159
|
+
text: `No memory entries found for project "${resolvedProject}".`,
|
|
89
160
|
},
|
|
90
161
|
],
|
|
91
162
|
};
|
|
@@ -121,7 +192,7 @@ server.tool('memory_search', 'Search memory entries across projects', {
|
|
|
121
192
|
content: [
|
|
122
193
|
{
|
|
123
194
|
type: 'text',
|
|
124
|
-
text: 'Not authenticated. Run: forever-plugin login',
|
|
195
|
+
text: 'Not authenticated. Run: npx @squidcode/forever-plugin login',
|
|
125
196
|
},
|
|
126
197
|
],
|
|
127
198
|
};
|
|
@@ -169,7 +240,9 @@ if (process.argv[2] === 'login') {
|
|
|
169
240
|
});
|
|
170
241
|
const ask = (q) => new Promise((resolve) => rl.question(q, resolve));
|
|
171
242
|
console.log('Forever Plugin Login\n');
|
|
172
|
-
const
|
|
243
|
+
const DEFAULT_SERVER = 'https://forever.squidcode.com';
|
|
244
|
+
const serverUrlInput = await ask(`Server URL [${DEFAULT_SERVER}]: `);
|
|
245
|
+
const serverUrl = serverUrlInput.trim() || DEFAULT_SERVER;
|
|
173
246
|
const email = await ask('Email: ');
|
|
174
247
|
const password = await ask('Password: ');
|
|
175
248
|
try {
|