recallmem 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.
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * RecallMEM CLI
4
+ *
5
+ * The npx entry point. Pure Node.js, no React/Next imports.
6
+ *
7
+ * Auto-detects install mode and supports two workflows:
8
+ *
9
+ * Use case 1 - "just run it" (most users):
10
+ * $ npx recallmem
11
+ * CLI clones the repo to ~/.recallmem on first run, then starts the app.
12
+ * Subsequent runs use ~/.recallmem and start instantly.
13
+ *
14
+ * Use case 3 - "fork and hack on it" (developers):
15
+ * $ git clone https://github.com/RealChrisSean/RecallMEM.git
16
+ * $ cd RecallMEM && npm install
17
+ * $ npx recallmem
18
+ * CLI detects it's already inside a recallmem checkout and uses cwd.
19
+ * Hot reload works, edits are reflected immediately.
20
+ *
21
+ * Commands:
22
+ * recallmem - run setup if needed, then start the server
23
+ * recallmem init - setup only (deps check, migrations, models, env)
24
+ * recallmem start - start the server (assumes init was done)
25
+ * recallmem doctor - diagnose what's missing or broken
26
+ * recallmem upgrade - git pull, run pending migrations, restart
27
+ * recallmem version - print version
28
+ */
29
+
30
+ const { setupCommand } = require("./commands/setup");
31
+ const { startCommand } = require("./commands/start");
32
+ const { doctorCommand } = require("./commands/doctor");
33
+ const { upgradeCommand } = require("./commands/upgrade");
34
+ const {
35
+ detectInstallMode,
36
+ cloneAndInstall,
37
+ gitInstalled,
38
+ RECALLMEM_HOME,
39
+ } = require("./lib/install-mode");
40
+ const {
41
+ printHeader,
42
+ color,
43
+ step,
44
+ success,
45
+ fail,
46
+ info,
47
+ section,
48
+ blank,
49
+ } = require("./lib/output");
50
+
51
+ const COMMANDS = {
52
+ init: { fn: initCommand, desc: "Run setup only (deps check, DB, models, env)" },
53
+ start: { fn: startWrapper, desc: "Start the server (assumes setup was done)" },
54
+ doctor: { fn: doctorCommand, desc: "Diagnose what's missing or broken" },
55
+ upgrade: { fn: upgradeCommand, desc: "Git pull and run pending migrations" },
56
+ version: { fn: versionCommand, desc: "Print version" },
57
+ help: { fn: helpCommand, desc: "Show this help" },
58
+ };
59
+
60
+ /**
61
+ * Resolve the install path AND tell the caller whether we're in dev mode.
62
+ * Dev mode = the user is running from inside a recallmem git checkout.
63
+ * In dev mode we skip the production build so they get hot reload via next dev.
64
+ */
65
+ async function resolveInstall() {
66
+ const mode = detectInstallMode();
67
+
68
+ if (mode.mode === "dev") {
69
+ info(`Using local checkout: ${mode.path}`);
70
+ return { path: mode.path, devMode: true };
71
+ }
72
+
73
+ if (mode.mode === "user") {
74
+ info(`Using install at: ${mode.path}`);
75
+ return { path: mode.path, devMode: false };
76
+ }
77
+
78
+ // First run - clone the repo
79
+ section("First-time setup");
80
+ info(`Cloning RecallMEM to ${mode.path}`);
81
+
82
+ if (!gitInstalled()) {
83
+ fail("git is not installed");
84
+ blank();
85
+ console.log("Install git first:");
86
+ console.log(" Mac: brew install git");
87
+ console.log(" Linux: sudo apt install git");
88
+ console.log(" Win: https://git-scm.com/download/win");
89
+ return null;
90
+ }
91
+
92
+ const result = cloneAndInstall(mode.path);
93
+ if (!result.ok) {
94
+ fail(`Install failed: ${result.error}`);
95
+ return null;
96
+ }
97
+
98
+ success(`Installed RecallMEM to ${mode.path}`);
99
+ return { path: mode.path, devMode: false };
100
+ }
101
+
102
+ async function initCommand() {
103
+ printHeader();
104
+ const install = await resolveInstall();
105
+ if (!install) process.exit(1);
106
+ const result = await setupCommand({
107
+ installPath: install.path,
108
+ devMode: install.devMode,
109
+ });
110
+ if (!result.ok) process.exit(1);
111
+ }
112
+
113
+ async function startWrapper() {
114
+ const install = await resolveInstall();
115
+ if (!install) process.exit(1);
116
+ await startCommand({ installPath: install.path });
117
+ }
118
+
119
+ async function defaultCommand() {
120
+ printHeader();
121
+ const install = await resolveInstall();
122
+ if (!install) process.exit(1);
123
+ const setupResult = await setupCommand({
124
+ installPath: install.path,
125
+ devMode: install.devMode,
126
+ skipIfDone: true,
127
+ });
128
+ if (!setupResult.ok) process.exit(1);
129
+ await startCommand({ installPath: install.path });
130
+ }
131
+
132
+ async function main() {
133
+ const args = process.argv.slice(2);
134
+ const cmd = args[0];
135
+
136
+ // Default behavior: setup if needed, then start
137
+ if (!cmd) {
138
+ return defaultCommand();
139
+ }
140
+
141
+ if (cmd === "--help" || cmd === "-h") {
142
+ return helpCommand();
143
+ }
144
+
145
+ if (cmd === "--version" || cmd === "-v") {
146
+ return versionCommand();
147
+ }
148
+
149
+ const command = COMMANDS[cmd];
150
+ if (!command) {
151
+ console.error(color.red(`Unknown command: ${cmd}`));
152
+ console.error("");
153
+ helpCommand();
154
+ process.exit(1);
155
+ }
156
+
157
+ try {
158
+ await command.fn();
159
+ } catch (err) {
160
+ const message = err instanceof Error ? err.message : String(err);
161
+ console.error(color.red(`✗ ${message}`));
162
+ process.exit(1);
163
+ }
164
+ }
165
+
166
+ function helpCommand() {
167
+ printHeader();
168
+ console.log("Usage:");
169
+ console.log(` ${color.bold("recallmem")} Setup if needed, then start the app`);
170
+ console.log("");
171
+ console.log("Commands:");
172
+ for (const [name, { desc }] of Object.entries(COMMANDS)) {
173
+ console.log(` ${color.bold(name.padEnd(10))} ${desc}`);
174
+ }
175
+ console.log("");
176
+ console.log("Options:");
177
+ console.log(` ${color.bold("--help, -h")} Show this help`);
178
+ console.log(` ${color.bold("--version, -v")} Show version`);
179
+ console.log("");
180
+ console.log("Install location:");
181
+ console.log(` ${color.dim("Default: ~/.recallmem")}`);
182
+ console.log(` ${color.dim("Override: RECALLMEM_HOME=/custom/path npx recallmem")}`);
183
+ console.log("");
184
+ console.log("Docs: https://github.com/RealChrisSean/RecallMEM");
185
+ }
186
+
187
+ function versionCommand() {
188
+ const pkg = require("../package.json");
189
+ console.log(`recallmem ${pkg.version}`);
190
+ }
191
+
192
+ main().catch((err) => {
193
+ const message = err instanceof Error ? err.message : String(err);
194
+ console.error(color.red(`✗ ${message}`));
195
+ process.exit(1);
196
+ });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "recallmem",
3
+ "version": "0.1.0",
4
+ "description": "Private, local-first AI chatbot with persistent working memory. One command install via npx.",
5
+ "license": "Apache-2.0",
6
+ "author": "Chris Sean",
7
+ "homepage": "https://github.com/RealChrisSean/RecallMEM",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/RealChrisSean/RecallMEM.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/RealChrisSean/RecallMEM/issues"
14
+ },
15
+ "keywords": [
16
+ "ai",
17
+ "chatbot",
18
+ "local",
19
+ "private",
20
+ "ollama",
21
+ "gemma",
22
+ "claude",
23
+ "memory",
24
+ "rag",
25
+ "vector-search",
26
+ "self-hosted"
27
+ ],
28
+ "bin": {
29
+ "recallmem": "./bin/recallmem.js"
30
+ },
31
+ "files": [
32
+ "bin",
33
+ "README.md",
34
+ "LICENSE",
35
+ "NOTICE"
36
+ ],
37
+ "engines": {
38
+ "node": ">=20"
39
+ }
40
+ }