@slorenzot/memento-cli 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.
package/dist/CLI.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export declare class CLI {
2
+ private program;
3
+ private memory;
4
+ private activeSessionId;
5
+ constructor(dbPath?: string);
6
+ private setupCommands;
7
+ private getOrCreateSessionId;
8
+ run(argv?: string[]): Promise<void>;
9
+ close(): void;
10
+ }
11
+ //# sourceMappingURL=CLI.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CLI.d.ts","sourceRoot":"","sources":["../src/CLI.ts"],"names":[],"mappings":"AAKA,qBAAa,GAAG;IACd,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,eAAe,CAAuB;gBAElC,MAAM,GAAE,MAA4B;IAMhD,OAAO,CAAC,aAAa;YAsJP,oBAAoB;IAa5B,GAAG,CAAC,IAAI,GAAE,MAAM,EAAiB;IAIvC,KAAK;CAGN"}
package/dist/CLI.js ADDED
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CLI = void 0;
4
+ const commander_1 = require("commander");
5
+ const core_1 = require("@memento/core");
6
+ class CLI {
7
+ program;
8
+ memory;
9
+ activeSessionId = null;
10
+ constructor(dbPath = './data/memento.db') {
11
+ this.program = new commander_1.Command();
12
+ this.memory = new core_1.MemoryEngine(dbPath);
13
+ this.setupCommands();
14
+ }
15
+ setupCommands() {
16
+ this.program
17
+ .name('memento')
18
+ .description('Persistent memory system for AI coding agents')
19
+ .version('0.1.0');
20
+ this.program
21
+ .command('setup [agent]')
22
+ .description('Setup configuration for an AI agent')
23
+ .action((agent) => {
24
+ console.log(`Setup for agent: ${agent || 'default'}`);
25
+ console.log('Configuration saved to ~/.memento/config.json');
26
+ });
27
+ this.program
28
+ .command('serve [port]')
29
+ .description('Start API server')
30
+ .option('-d, --db <path>', 'Database path', './data/memento.db')
31
+ .action((port, options) => {
32
+ console.log(`Starting API server on port ${port || 3000}`);
33
+ console.log(`Database: ${options.db}`);
34
+ console.log('Note: API server not implemented in CLI mode');
35
+ });
36
+ this.program
37
+ .command('mcp')
38
+ .description('Start MCP server')
39
+ .option('-d, --db <path>', 'Database path', './data/memento.db')
40
+ .action((options) => {
41
+ console.log(`Starting MCP server`);
42
+ console.log(`Database: ${options.db}`);
43
+ console.log('Note: MCP server not implemented in CLI mode');
44
+ });
45
+ this.program
46
+ .command('search <query>')
47
+ .description('Search observations')
48
+ .option('-t, --type <type>', 'Filter by type')
49
+ .option('-p, --project <project>', 'Filter by project')
50
+ .option('--limit <number>', 'Limit results')
51
+ .action(async (query, options) => {
52
+ const result = await this.memory.search({
53
+ query,
54
+ type: options.type,
55
+ projectId: options.project,
56
+ limit: options.limit ? parseInt(options.limit) : undefined,
57
+ });
58
+ console.log(`Found ${result.total} observations:`);
59
+ result.observations.forEach((obs) => {
60
+ console.log(` [${obs.type}] ${obs.title}`);
61
+ console.log(` ${obs.content.substring(0, 100)}...`);
62
+ });
63
+ });
64
+ this.program
65
+ .command('save <title> <content>')
66
+ .description('Save an observation')
67
+ .option('-t, --type <type>', 'Observation type', 'note')
68
+ .option('-k, --topic <topic>', 'Topic key')
69
+ .option('-p, --project <project>', 'Project ID', 'default')
70
+ .action(async (title, content, options) => {
71
+ const sessionId = await this.getOrCreateSessionId(options.project);
72
+ const observation = await this.memory.createObservation({
73
+ sessionId,
74
+ title,
75
+ content,
76
+ type: options.type,
77
+ topicKey: options.topic || null,
78
+ projectId: options.project,
79
+ metadata: {},
80
+ });
81
+ console.log(`Saved observation: ${observation.uuid}`);
82
+ });
83
+ this.program
84
+ .command('get <id>')
85
+ .description('Get observation by ID')
86
+ .action(async (id) => {
87
+ const observation = await this.memory.getObservation(parseInt(id));
88
+ if (!observation) {
89
+ console.error('Observation not found');
90
+ return;
91
+ }
92
+ console.log(`[${observation.type}] ${observation.title}`);
93
+ console.log(observation.content);
94
+ console.log(`Topic: ${observation.topicKey || 'none'}`);
95
+ console.log(`Created: ${observation.createdAt.toISOString()}`);
96
+ });
97
+ this.program
98
+ .command('update <id>')
99
+ .description('Update observation')
100
+ .option('-t, --title <title>', 'New title')
101
+ .option('-c, --content <content>', 'New content')
102
+ .option('-k, --topic <topic>', 'New topic key')
103
+ .action(async (id, options) => {
104
+ const updates = {};
105
+ if (options.title)
106
+ updates.title = options.title;
107
+ if (options.content)
108
+ updates.content = options.content;
109
+ if (options.topic)
110
+ updates.topicKey = options.topic;
111
+ const observation = await this.memory.updateObservation(parseInt(id), updates);
112
+ console.log(`Updated observation: ${observation.uuid}`);
113
+ });
114
+ this.program
115
+ .command('delete <id>')
116
+ .description('Delete observation')
117
+ .action(async (id) => {
118
+ await this.memory.deleteObservation(parseInt(id));
119
+ console.log(`Deleted observation ${id}`);
120
+ });
121
+ this.program
122
+ .command('timeline [project]')
123
+ .description('Show timeline of observations')
124
+ .option('-l, --limit <number>', 'Limit results', '20')
125
+ .action(async (project, options) => {
126
+ const result = await this.memory.search({
127
+ projectId: project,
128
+ limit: parseInt(options.limit),
129
+ });
130
+ console.log(`Timeline (${result.total} observations):`);
131
+ result.observations.forEach((obs) => {
132
+ const date = obs.createdAt.toLocaleDateString();
133
+ console.log(` ${date} [${obs.type}] ${obs.title}`);
134
+ });
135
+ });
136
+ this.program
137
+ .command('stats')
138
+ .description('Show statistics')
139
+ .action(async () => {
140
+ const result = await this.memory.search({});
141
+ const byType = result.observations.reduce((acc, obs) => {
142
+ acc[obs.type] = (acc[obs.type] || 0) + 1;
143
+ return acc;
144
+ }, {});
145
+ console.log('Statistics:');
146
+ console.log(` Total observations: ${result.total}`);
147
+ console.log(' By type:');
148
+ Object.entries(byType).forEach(([type, count]) => {
149
+ console.log(` ${type}: ${count}`);
150
+ });
151
+ });
152
+ }
153
+ async getOrCreateSessionId(projectId) {
154
+ if (this.activeSessionId) {
155
+ return this.activeSessionId;
156
+ }
157
+ const session = await this.memory.createSession({
158
+ projectId,
159
+ endedAt: null,
160
+ metadata: {},
161
+ });
162
+ this.activeSessionId = session.id;
163
+ return session.id;
164
+ }
165
+ async run(argv = process.argv) {
166
+ await this.program.parseAsync(argv);
167
+ }
168
+ close() {
169
+ this.memory.close();
170
+ }
171
+ }
172
+ exports.CLI = CLI;
173
+ //# sourceMappingURL=CLI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CLI.js","sourceRoot":"","sources":["../src/CLI.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { CLI } from './CLI';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@slorenzot/memento-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI interface for Memento",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "bin": {
8
+ "memento": "./dist/cli.js"
9
+ },
10
+ "scripts": {
11
+ "dev": "bun run src/cli.ts",
12
+ "build": "bun tsc --declaration --declarationMap --emitDeclarationOnly --outDir ./dist",
13
+ "test": "bun test",
14
+ "typecheck": "bun tsc --noEmit"
15
+ },
16
+ "dependencies": {
17
+ "@slorenzot/memento-core": "workspace:*",
18
+ "commander": "^12.0.0",
19
+ "ink": "^4.4.1",
20
+ "chalk": "^5.3.0",
21
+ "ora": "^8.0.1",
22
+ "zod": "^3.22.4",
23
+ "dotenv": "^16.4.1"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.11.0",
27
+ "bun-types": "^1.3.11"
28
+ }
29
+ }
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { CLI } from './CLI';
3
+
4
+ describe('CLI', () => {
5
+ let cli: CLI;
6
+ let testDbPath: string;
7
+
8
+ beforeEach(() => {
9
+ testDbPath = `/tmp/test-cli-${Date.now()}.db`;
10
+ cli = new CLI(testDbPath);
11
+ });
12
+
13
+ afterEach(() => {
14
+ cli.close();
15
+ });
16
+
17
+ it('should initialize without errors', () => {
18
+ expect(cli).toBeDefined();
19
+ });
20
+
21
+ describe('Commands', () => {
22
+ it('should have all required commands', () => {
23
+ const commands = cli['program'].commands;
24
+
25
+ const commandNames = commands.map((c) => c.name());
26
+ expect(commandNames).toContain('setup');
27
+ expect(commandNames).toContain('serve');
28
+ expect(commandNames).toContain('mcp');
29
+ expect(commandNames).toContain('search');
30
+ expect(commandNames).toContain('save');
31
+ expect(commandNames).toContain('get');
32
+ expect(commandNames).toContain('update');
33
+ expect(commandNames).toContain('delete');
34
+ expect(commandNames).toContain('timeline');
35
+ expect(commandNames).toContain('stats');
36
+ });
37
+
38
+ it('should have proper descriptions', () => {
39
+ const commands = cli['program'].commands;
40
+
41
+ const setupCommand = commands.find((c) => c.name() === 'setup');
42
+ expect(setupCommand).toBeDefined();
43
+ expect(setupCommand?.description()).toContain('Setup');
44
+
45
+ const searchCommand = commands.find((c) => c.name() === 'search');
46
+ expect(searchCommand).toBeDefined();
47
+ expect(searchCommand?.description()).toContain('Search');
48
+ });
49
+ });
50
+
51
+ describe('Session Management', () => {
52
+ it('should create and track active session', async () => {
53
+ const sessionId = await cli['getOrCreateSessionId']('test-project');
54
+ expect(sessionId).toBeDefined();
55
+
56
+ const sameSessionId = await cli['getOrCreateSessionId']('test-project');
57
+ expect(sameSessionId).toBe(sessionId);
58
+ });
59
+ });
60
+ });
package/src/CLI.ts ADDED
@@ -0,0 +1,187 @@
1
+ import { Command } from 'commander';
2
+ import { MemoryEngine } from '@slorenzot/memento-core';
3
+ import type { Observation } from '@slorenzot/memento-core';
4
+
5
+ // @ts-ignore
6
+ export class CLI {
7
+ private program: Command;
8
+ private memory: MemoryEngine;
9
+ private activeSessionId: number | null = null;
10
+
11
+ constructor(dbPath: string = './data/memento.db') {
12
+ this.program = new Command();
13
+ this.memory = new MemoryEngine(dbPath);
14
+ this.setupCommands();
15
+ }
16
+
17
+ private setupCommands() {
18
+ this.program
19
+ .name('memento')
20
+ .description('Persistent memory system for AI coding agents')
21
+ .version('0.1.0');
22
+
23
+ this.program
24
+ .command('setup [agent]')
25
+ .description('Setup configuration for an AI agent')
26
+ .action((agent) => {
27
+ console.log(`Setup for agent: ${agent || 'default'}`);
28
+ console.log('Configuration saved to ~/.memento/config.json');
29
+ });
30
+
31
+ this.program
32
+ .command('serve [port]')
33
+ .description('Start API server')
34
+ .option('-d, --db <path>', 'Database path', './data/memento.db')
35
+ .action((port, options) => {
36
+ console.log(`Starting API server on port ${port || 3000}`);
37
+ console.log(`Database: ${options.db}`);
38
+ console.log('Note: API server not implemented in CLI mode');
39
+ });
40
+
41
+ this.program
42
+ .command('mcp')
43
+ .description('Start MCP server')
44
+ .option('-d, --db <path>', 'Database path', './data/memento.db')
45
+ .action((options) => {
46
+ console.log(`Starting MCP server`);
47
+ console.log(`Database: ${options.db}`);
48
+ console.log('Note: MCP server not implemented in CLI mode');
49
+ });
50
+
51
+ this.program
52
+ .command('search <query>')
53
+ .description('Search observations')
54
+ .option('-t, --type <type>', 'Filter by type')
55
+ .option('-p, --project <project>', 'Filter by project')
56
+ .option('--limit <number>', 'Limit results')
57
+ .action(async (query, options) => {
58
+ const result = await this.memory.search({
59
+ query,
60
+ type: options.type as Observation['type'] | undefined,
61
+ projectId: options.project as string | undefined,
62
+ limit: options.limit ? parseInt(options.limit as string) : undefined,
63
+ });
64
+ console.log(`Found ${result.total} observations:`);
65
+ result.observations.forEach((obs) => {
66
+ console.log(` [${obs.type}] ${obs.title}`);
67
+ console.log(` ${obs.content.substring(0, 100)}...`);
68
+ });
69
+ });
70
+
71
+ this.program
72
+ .command('save <title> <content>')
73
+ .description('Save an observation')
74
+ .option('-t, --type <type>', 'Observation type', 'note')
75
+ .option('-k, --topic <topic>', 'Topic key')
76
+ .option('-p, --project <project>', 'Project ID', 'default')
77
+ .action(async (title, content, options) => {
78
+ const sessionId = await this.getOrCreateSessionId(options.project as string);
79
+ const observation = await this.memory.createObservation({
80
+ sessionId,
81
+ title,
82
+ content,
83
+ type: options.type as Observation['type'],
84
+ topicKey: options.topic || null,
85
+ projectId: options.project as string,
86
+ metadata: {},
87
+ });
88
+ console.log(`Saved observation: ${observation.uuid}`);
89
+ });
90
+
91
+ this.program
92
+ .command('get <id>')
93
+ .description('Get observation by ID')
94
+ .action(async (id) => {
95
+ const observation = await this.memory.getObservation(parseInt(id));
96
+ if (!observation) {
97
+ console.error('Observation not found');
98
+ return;
99
+ }
100
+ console.log(`[${observation.type}] ${observation.title}`);
101
+ console.log(observation.content);
102
+ console.log(`Topic: ${observation.topicKey || 'none'}`);
103
+ console.log(`Created: ${observation.createdAt.toISOString()}`);
104
+ });
105
+
106
+ this.program
107
+ .command('update <id>')
108
+ .description('Update observation')
109
+ .option('-t, --title <title>', 'New title')
110
+ .option('-c, --content <content>', 'New content')
111
+ .option('-k, --topic <topic>', 'New topic key')
112
+ .action(async (id, options) => {
113
+ const updates: Partial<Observation> = {};
114
+ if (options.title) updates.title = options.title;
115
+ if (options.content) updates.content = options.content;
116
+ if (options.topic) updates.topicKey = options.topic;
117
+
118
+ const observation = await this.memory.updateObservation(parseInt(id), updates);
119
+ console.log(`Updated observation: ${observation.uuid}`);
120
+ });
121
+
122
+ this.program
123
+ .command('delete <id>')
124
+ .description('Delete observation')
125
+ .action(async (id) => {
126
+ await this.memory.deleteObservation(parseInt(id));
127
+ console.log(`Deleted observation ${id}`);
128
+ });
129
+
130
+ this.program
131
+ .command('timeline [project]')
132
+ .description('Show timeline of observations')
133
+ .option('-l, --limit <number>', 'Limit results', '20')
134
+ .action(async (project, options) => {
135
+ const result = await this.memory.search({
136
+ projectId: project,
137
+ limit: parseInt(options.limit as string),
138
+ });
139
+ console.log(`Timeline (${result.total} observations):`);
140
+ result.observations.forEach((obs) => {
141
+ const date = obs.createdAt.toLocaleDateString();
142
+ console.log(` ${date} [${obs.type}] ${obs.title}`);
143
+ });
144
+ });
145
+
146
+ this.program
147
+ .command('stats')
148
+ .description('Show statistics')
149
+ .action(async () => {
150
+ const result = await this.memory.search({});
151
+ const byType = result.observations.reduce(
152
+ (acc, obs) => {
153
+ acc[obs.type] = (acc[obs.type] || 0) + 1;
154
+ return acc;
155
+ },
156
+ {} as Record<string, number>
157
+ );
158
+ console.log('Statistics:');
159
+ console.log(` Total observations: ${result.total}`);
160
+ console.log(' By type:');
161
+ Object.entries(byType).forEach(([type, count]) => {
162
+ console.log(` ${type}: ${count}`);
163
+ });
164
+ });
165
+ }
166
+
167
+ private async getOrCreateSessionId(projectId: string): Promise<number> {
168
+ if (this.activeSessionId) {
169
+ return this.activeSessionId;
170
+ }
171
+ const session = await this.memory.createSession({
172
+ projectId,
173
+ endedAt: null,
174
+ metadata: {},
175
+ });
176
+ this.activeSessionId = session.id;
177
+ return session.id;
178
+ }
179
+
180
+ async run(argv: string[] = process.argv) {
181
+ await this.program.parseAsync(argv);
182
+ }
183
+
184
+ close() {
185
+ this.memory.close();
186
+ }
187
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { CLI } from './CLI';
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "removeComments": true,
10
+ "types": ["bun-types"]
11
+ },
12
+ "include": ["src/**/*"],
13
+ "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
14
+ }