gnosys-mcp 2.0.1 → 2.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.js CHANGED
@@ -25,6 +25,7 @@ import { GnosysAsk } from "./lib/ask.js";
25
25
  import { getLLMProvider, isProviderAvailable } from "./lib/llm.js";
26
26
  import { GnosysDB } from "./lib/db.js";
27
27
  import { migrate, formatMigrationReport } from "./lib/migrate.js";
28
+ import { createProjectIdentity } from "./lib/projectIdentity.js";
28
29
  // Load API keys from ~/.config/gnosys/.env (same as MCP server)
29
30
  const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
30
31
  dotenv.config({ path: path.join(home, ".config", "gnosys", ".env") });
@@ -212,67 +213,95 @@ program
212
213
  // ─── gnosys init ─────────────────────────────────────────────────────────
213
214
  program
214
215
  .command("init")
215
- .description("Initialize a new .gnosys store in the current directory")
216
+ .description("Initialize Gnosys in the current directory. Creates project identity, registers in central DB, and sets up store.")
216
217
  .option("-d, --directory <dir>", "Target directory (default: cwd)")
218
+ .option("-n, --name <name>", "Project name (default: directory basename)")
217
219
  .action(async (opts) => {
218
220
  const targetDir = opts.directory
219
221
  ? path.resolve(opts.directory)
220
222
  : process.cwd();
221
223
  const storePath = path.join(targetDir, ".gnosys");
224
+ // Check if already exists — re-sync identity instead of failing
225
+ let isResync = false;
222
226
  try {
223
227
  await fs.stat(storePath);
224
- console.error(`A .gnosys store already exists at ${storePath}`);
225
- process.exit(1);
228
+ isResync = true;
226
229
  }
227
230
  catch {
228
- // Good
229
- }
230
- await fs.mkdir(storePath, { recursive: true });
231
- await fs.mkdir(path.join(storePath, ".config"), { recursive: true });
232
- const defaultRegistry = {
233
- domain: [
234
- "architecture", "api", "auth", "database", "devops",
235
- "frontend", "backend", "testing", "security", "performance",
236
- ],
237
- type: [
238
- "decision", "concept", "convention", "requirement",
239
- "observation", "fact", "question",
240
- ],
241
- concern: ["dx", "scalability", "maintainability", "reliability"],
242
- status_tag: ["draft", "stable", "deprecated", "experimental"],
243
- };
244
- await fs.writeFile(path.join(storePath, ".config", "tags.json"), JSON.stringify(defaultRegistry, null, 2), "utf-8");
245
- // Write default gnosys.json config
246
- await fs.writeFile(path.join(storePath, "gnosys.json"), generateConfigTemplate() + "\n", "utf-8");
247
- const changelog = `# Gnosys Changelog\n\n## ${new Date().toISOString().split("T")[0]}\n\n- Store initialized\n`;
248
- await fs.writeFile(path.join(storePath, "CHANGELOG.md"), changelog, "utf-8");
249
- try {
250
- const { execSync } = await import("child_process");
251
- execSync("git init", { cwd: storePath, stdio: "pipe" });
252
- // Ensure git has a user identity for the initial commit
231
+ // Good — fresh init
232
+ }
233
+ if (!isResync) {
234
+ await fs.mkdir(storePath, { recursive: true });
235
+ await fs.mkdir(path.join(storePath, ".config"), { recursive: true });
236
+ const defaultRegistry = {
237
+ domain: [
238
+ "architecture", "api", "auth", "database", "devops",
239
+ "frontend", "backend", "testing", "security", "performance",
240
+ ],
241
+ type: [
242
+ "decision", "concept", "convention", "requirement",
243
+ "observation", "fact", "question",
244
+ ],
245
+ concern: ["dx", "scalability", "maintainability", "reliability"],
246
+ status_tag: ["draft", "stable", "deprecated", "experimental"],
247
+ };
248
+ await fs.writeFile(path.join(storePath, ".config", "tags.json"), JSON.stringify(defaultRegistry, null, 2), "utf-8");
249
+ // Write default gnosys.json config (LLM settings)
250
+ await fs.writeFile(path.join(storePath, ".config", "gnosys-config.json"), generateConfigTemplate() + "\n", "utf-8");
251
+ const changelog = `# Gnosys Changelog\n\n## ${new Date().toISOString().split("T")[0]}\n\n- Store initialized\n`;
252
+ await fs.writeFile(path.join(storePath, "CHANGELOG.md"), changelog, "utf-8");
253
253
  try {
254
- execSync("git config user.name", { cwd: storePath, stdio: "pipe" });
254
+ const { execSync } = await import("child_process");
255
+ execSync("git init", { cwd: storePath, stdio: "pipe" });
256
+ try {
257
+ execSync("git config user.name", { cwd: storePath, stdio: "pipe" });
258
+ }
259
+ catch {
260
+ execSync('git config user.name "Gnosys"', { cwd: storePath, stdio: "pipe" });
261
+ execSync('git config user.email "gnosys@local"', { cwd: storePath, stdio: "pipe" });
262
+ }
263
+ execSync("git add -A && git add -f .config/", { cwd: storePath, stdio: "pipe" });
264
+ execSync('git commit -m "Initialize Gnosys store"', {
265
+ cwd: storePath,
266
+ stdio: "pipe",
267
+ });
255
268
  }
256
269
  catch {
257
- execSync('git config user.name "Gnosys"', { cwd: storePath, stdio: "pipe" });
258
- execSync('git config user.email "gnosys@local"', { cwd: storePath, stdio: "pipe" });
270
+ // Git not available
259
271
  }
260
- execSync("git add -A && git add -f .config/", { cwd: storePath, stdio: "pipe" });
261
- execSync('git commit -m "Initialize Gnosys store"', {
262
- cwd: storePath,
263
- stdio: "pipe",
264
- });
272
+ }
273
+ // v3.0: Create/update project identity and register in central DB
274
+ let centralDb = null;
275
+ try {
276
+ centralDb = GnosysDB.openCentral();
277
+ if (!centralDb.isAvailable())
278
+ centralDb = null;
265
279
  }
266
280
  catch {
267
- // Git not available
268
- }
269
- console.log(`Gnosys store initialized at ${storePath}`);
270
- console.log(`\nCreated:`);
271
- console.log(` .config/ (internal config)`);
272
- console.log(` gnosys.json (configuration — edit to customize)`);
273
- console.log(` tags.json (tag registry)`);
274
- console.log(` CHANGELOG.md`);
275
- console.log(` git repo`);
281
+ centralDb = null;
282
+ }
283
+ const identity = await createProjectIdentity(targetDir, {
284
+ projectName: opts.name,
285
+ centralDb: centralDb || undefined,
286
+ });
287
+ if (centralDb)
288
+ centralDb.close();
289
+ const action = isResync ? "re-synced" : "initialized";
290
+ console.log(`Gnosys store ${action} at ${storePath}`);
291
+ console.log(`\nProject Identity:`);
292
+ console.log(` ID: ${identity.projectId}`);
293
+ console.log(` Name: ${identity.projectName}`);
294
+ console.log(` Directory: ${identity.workingDirectory}`);
295
+ console.log(` Agent: ${identity.agentRulesTarget || "none detected"}`);
296
+ console.log(` Central DB: ${centralDb ? "registered ✓" : "not available"}`);
297
+ if (!isResync) {
298
+ console.log(`\nCreated:`);
299
+ console.log(` gnosys.json (project identity)`);
300
+ console.log(` .config/ (internal config)`);
301
+ console.log(` tags.json (tag registry)`);
302
+ console.log(` CHANGELOG.md`);
303
+ console.log(` git repo`);
304
+ }
276
305
  console.log(`\nStart adding memories with: gnosys add "your knowledge here"`);
277
306
  });
278
307
  // ─── gnosys stale ───────────────────────────────────────────────────────
@@ -1556,33 +1585,7 @@ program
1556
1585
  console.log(` → ${rp}`);
1557
1586
  }
1558
1587
  });
1559
- // ─── gnosys migrate ─────────────────────────────────────────────────────
1560
- program
1561
- .command("migrate")
1562
- .description("Migrate v1.x data (Markdown + archive.db) into unified gnosys.db (v2.0)")
1563
- .option("--verbose", "Show detailed progress")
1564
- .action(async (opts) => {
1565
- const resolver = await getResolver();
1566
- const stores = resolver.getStores();
1567
- if (stores.length === 0) {
1568
- console.error("No stores found. Run gnosys init first.");
1569
- process.exit(1);
1570
- }
1571
- const storePath = stores[0].path;
1572
- // Check if already migrated
1573
- const db = new GnosysDB(storePath);
1574
- if (db.isMigrated()) {
1575
- const counts = db.getMemoryCount();
1576
- console.log(`Already migrated. gnosys.db has ${counts.active} active + ${counts.archived} archived memories.`);
1577
- console.log("To re-migrate, delete gnosys.db and run this command again.");
1578
- db.close();
1579
- return;
1580
- }
1581
- db.close();
1582
- console.log("Migrating v1.x data → gnosys.db...\n");
1583
- const stats = await migrate(storePath, { verbose: opts.verbose });
1584
- console.log(formatMigrationReport(stats));
1585
- });
1588
+ // NOTE: gnosys migrate is defined below (near the end) with --to-central support
1586
1589
  // ─── gnosys doctor ──────────────────────────────────────────────────────
1587
1590
  program
1588
1591
  .command("doctor")
@@ -1981,5 +1984,180 @@ program
1981
1984
  console.log(formatAuditTimeline(entries));
1982
1985
  }
1983
1986
  });
1987
+ // ─── gnosys backup ──────────────────────────────────────────────────────
1988
+ program
1989
+ .command("backup")
1990
+ .description("Create a backup of the central Gnosys database")
1991
+ .option("-o, --output <dir>", "Backup output directory (default: ~/.gnosys/)")
1992
+ .action(async (opts) => {
1993
+ let centralDb = null;
1994
+ try {
1995
+ centralDb = GnosysDB.openCentral();
1996
+ if (!centralDb.isAvailable()) {
1997
+ console.error("Central DB not available (better-sqlite3 missing).");
1998
+ process.exit(1);
1999
+ }
2000
+ const backupPath = centralDb.backup(opts.output);
2001
+ console.log(`Backup created: ${backupPath}`);
2002
+ const counts = centralDb.getMemoryCount();
2003
+ console.log(` Memories: ${counts.total} (${counts.active} active, ${counts.archived} archived)`);
2004
+ console.log(` Projects: ${centralDb.getAllProjects().length}`);
2005
+ }
2006
+ catch (err) {
2007
+ console.error(`Backup failed: ${err instanceof Error ? err.message : err}`);
2008
+ process.exit(1);
2009
+ }
2010
+ finally {
2011
+ centralDb?.close();
2012
+ }
2013
+ });
2014
+ // ─── gnosys restore ─────────────────────────────────────────────────────
2015
+ program
2016
+ .command("restore <backupFile>")
2017
+ .description("Restore the central Gnosys database from a backup")
2018
+ .action(async (backupFile) => {
2019
+ const resolved = path.resolve(backupFile);
2020
+ try {
2021
+ const db = GnosysDB.restore(resolved);
2022
+ const counts = db.getMemoryCount();
2023
+ console.log(`Database restored from ${resolved}`);
2024
+ console.log(` Memories: ${counts.total} (${counts.active} active, ${counts.archived} archived)`);
2025
+ console.log(` Projects: ${db.getAllProjects().length}`);
2026
+ db.close();
2027
+ }
2028
+ catch (err) {
2029
+ console.error(`Restore failed: ${err instanceof Error ? err.message : err}`);
2030
+ process.exit(1);
2031
+ }
2032
+ });
2033
+ // ─── gnosys migrate --to-central ────────────────────────────────────────
2034
+ program
2035
+ .command("migrate")
2036
+ .description("Migrate data. Use --to-central to move per-project stores into the central DB.")
2037
+ .option("--to-central", "Migrate all discovered per-project stores into ~/.gnosys/gnosys.db")
2038
+ .option("-v, --verbose", "Verbose output")
2039
+ .action(async (opts) => {
2040
+ if (!opts.toCentral) {
2041
+ // Legacy v1→v2 migration (existing behavior)
2042
+ const resolver = await getResolver();
2043
+ const writeTarget = resolver.getWriteTarget();
2044
+ if (!writeTarget) {
2045
+ console.error("No writable store found. Run 'gnosys init' first.");
2046
+ process.exit(1);
2047
+ }
2048
+ const stats = await migrate(writeTarget.store.getStorePath(), { verbose: opts.verbose });
2049
+ console.log(formatMigrationReport(stats));
2050
+ return;
2051
+ }
2052
+ // v3.0: Migrate per-project stores into central DB
2053
+ console.log("Migrating per-project stores to central DB (~/.gnosys/gnosys.db)...\n");
2054
+ let centralDb = null;
2055
+ try {
2056
+ centralDb = GnosysDB.openCentral();
2057
+ if (!centralDb.isAvailable()) {
2058
+ console.error("Central DB not available (better-sqlite3 missing).");
2059
+ process.exit(1);
2060
+ }
2061
+ }
2062
+ catch (err) {
2063
+ console.error(`Cannot open central DB: ${err instanceof Error ? err.message : err}`);
2064
+ process.exit(1);
2065
+ }
2066
+ // Discover all registered project stores
2067
+ const resolver = await getResolver();
2068
+ const detectedStores = await resolver.detectAllStores();
2069
+ const projectDirs = detectedStores
2070
+ .filter(s => s.hasGnosys)
2071
+ .map(s => s.path);
2072
+ if (projectDirs.length === 0) {
2073
+ console.log("No per-project stores found to migrate.");
2074
+ centralDb.close();
2075
+ return;
2076
+ }
2077
+ console.log(`Found ${projectDirs.length} project store(s) to migrate:\n`);
2078
+ let totalMemories = 0;
2079
+ let totalProjects = 0;
2080
+ for (const projectDir of projectDirs) {
2081
+ const storePath = path.join(projectDir, ".gnosys");
2082
+ const log = opts.verbose ? console.log : () => { };
2083
+ try {
2084
+ // Create project identity if it doesn't exist
2085
+ const identity = await createProjectIdentity(projectDir, {
2086
+ centralDb: centralDb,
2087
+ });
2088
+ log(` [${identity.projectName}] ID: ${identity.projectId}`);
2089
+ // Open per-project DB and import memories
2090
+ const projectDb = new GnosysDB(storePath);
2091
+ if (!projectDb.isAvailable() || !projectDb.isMigrated()) {
2092
+ log(` [${identity.projectName}] No migrated gnosys.db — skipping`);
2093
+ continue;
2094
+ }
2095
+ const memories = projectDb.getAllMemories();
2096
+ let count = 0;
2097
+ centralDb.transaction(() => {
2098
+ for (const mem of memories) {
2099
+ centralDb.insertMemory({
2100
+ ...mem,
2101
+ project_id: identity.projectId,
2102
+ scope: "project",
2103
+ });
2104
+ count++;
2105
+ }
2106
+ });
2107
+ projectDb.close();
2108
+ totalMemories += count;
2109
+ totalProjects++;
2110
+ console.log(` ✓ ${identity.projectName}: ${count} memories migrated`);
2111
+ }
2112
+ catch (err) {
2113
+ console.error(` ✗ ${projectDir}: ${err instanceof Error ? err.message : err}`);
2114
+ }
2115
+ }
2116
+ centralDb.close();
2117
+ console.log(`\n╔════════════════════════════════════════╗`);
2118
+ console.log(`║ Central Migration Complete ║`);
2119
+ console.log(`╚════════════════════════════════════════╝`);
2120
+ console.log(` Projects migrated: ${totalProjects}`);
2121
+ console.log(` Memories imported: ${totalMemories}`);
2122
+ console.log(`\n Per-project gnosys.db files are untouched.`);
2123
+ console.log(` Central DB: ${GnosysDB.getCentralDbPath()}`);
2124
+ });
2125
+ // ─── gnosys projects ────────────────────────────────────────────────────
2126
+ program
2127
+ .command("projects")
2128
+ .description("List all registered projects in the central DB")
2129
+ .action(async () => {
2130
+ let centralDb = null;
2131
+ try {
2132
+ centralDb = GnosysDB.openCentral();
2133
+ if (!centralDb.isAvailable()) {
2134
+ console.error("Central DB not available (better-sqlite3 missing).");
2135
+ process.exit(1);
2136
+ }
2137
+ const projects = centralDb.getAllProjects();
2138
+ if (projects.length === 0) {
2139
+ console.log("No projects registered. Run 'gnosys init' in a project directory.");
2140
+ centralDb.close();
2141
+ return;
2142
+ }
2143
+ console.log(`${projects.length} registered project(s):\n`);
2144
+ for (const p of projects) {
2145
+ const memCount = centralDb.getMemoriesByProject(p.id).length;
2146
+ console.log(` ${p.name}`);
2147
+ console.log(` ID: ${p.id}`);
2148
+ console.log(` Directory: ${p.working_directory}`);
2149
+ console.log(` Memories: ${memCount}`);
2150
+ console.log(` Created: ${p.created}`);
2151
+ console.log();
2152
+ }
2153
+ }
2154
+ catch (err) {
2155
+ console.error(`Error: ${err instanceof Error ? err.message : err}`);
2156
+ process.exit(1);
2157
+ }
2158
+ finally {
2159
+ centralDb?.close();
2160
+ }
2161
+ });
1984
2162
  program.parse();
1985
2163
  //# sourceMappingURL=cli.js.map