@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.
Files changed (2) hide show
  1. package/dist/index.js +87 -14
  2. 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.1.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.string().describe('Project name or git remote URL'),
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.string().optional().describe('Session ID for grouping'),
23
- }, async ({ project, type, content, tags, sessionId }) => {
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: [{ type: 'text', text: `Logged ${type} entry.` }],
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.string().describe('Project name or git remote URL'),
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 "${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 serverUrl = await ask('Server URL (e.g. https://forever-z44hy.ondigitalocean.app): ');
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squidcode/forever-plugin",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP plugin for Forever - Claude Memory System",
5
5
  "type": "module",
6
6
  "bin": {