@zabaca/lattice 1.0.22 → 1.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.
Files changed (2) hide show
  1. package/dist/main.js +398 -103
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -406,95 +406,172 @@ function ensureLatticeHome() {
406
406
  var __filename2 = fileURLToPath(import.meta.url);
407
407
  var __dirname2 = path.dirname(__filename2);
408
408
  var COMMANDS = ["research.md", "graph-sync.md", "entity-extract.md"];
409
+ var SITE_TEMPLATE_FILES = [
410
+ "astro.config.ts",
411
+ "package.json",
412
+ "tsconfig.json",
413
+ "src/content.config.ts",
414
+ "src/collections/authors.ts",
415
+ "src/collections/documents.ts",
416
+ "src/collections/tags.ts"
417
+ ];
409
418
 
410
419
  class InitCommand extends CommandRunner2 {
411
420
  async run(_inputs, _options) {
412
421
  try {
413
422
  ensureLatticeHome();
423
+ const latticeHome = getLatticeHome();
414
424
  const envPath = getEnvPath();
415
425
  if (!existsSync3(envPath)) {
416
426
  writeFileSync(envPath, `# Lattice Configuration
417
427
  # Get your API key from: https://www.voyageai.com/
418
-
419
428
  VOYAGE_API_KEY=
429
+
430
+ # Site Configuration (for lattice site command)
431
+ SPACESHIP_AUTHOR="Lattice"
432
+ SPACESHIP_BASE="/"
433
+ SPACESHIP_SITE="https://example.com"
434
+ SPACESHIP_TITLE="Lattice"
435
+ SPACESHIP_DESCRIPTION="Research Knowledge Base"
436
+ OBSIDIAN_VAULT_DIR=docs
420
437
  `);
438
+ } else {
439
+ const envContent = await fs.readFile(envPath, "utf-8");
440
+ if (!envContent.includes("SPACESHIP_")) {
441
+ await fs.appendFile(envPath, `
442
+ # Site Configuration (for lattice site command)
443
+ SPACESHIP_AUTHOR="Lattice"
444
+ SPACESHIP_BASE="/"
445
+ SPACESHIP_SITE="https://example.com"
446
+ SPACESHIP_TITLE="Lattice"
447
+ SPACESHIP_DESCRIPTION="Research Knowledge Base"
448
+ OBSIDIAN_VAULT_DIR=docs
449
+ `);
450
+ console.log("\u2705 Added site configuration to .env");
451
+ }
421
452
  }
422
- console.log(`\u2705 Lattice home directory: ${getLatticeHome()}`);
453
+ console.log(`\u2705 Lattice home directory: ${latticeHome}`);
423
454
  console.log(` Documents: ${getDocsPath()}`);
424
455
  console.log(` Config: ${envPath}`);
425
456
  console.log();
426
- const targetDir = path.join(homedir2(), ".claude", "commands");
427
- let commandsSourceDir = path.resolve(__dirname2, "..", "commands");
428
- try {
429
- await fs.access(commandsSourceDir);
430
- } catch {
431
- commandsSourceDir = path.resolve(__dirname2, "..", "..", "commands");
432
- }
433
- try {
434
- await fs.access(commandsSourceDir);
435
- } catch {
436
- console.error("Error: Commands source directory not found at", commandsSourceDir);
437
- console.error("This may indicate a corrupted installation. Try reinstalling @zabaca/lattice.");
438
- process.exit(1);
439
- }
440
- await fs.mkdir(targetDir, { recursive: true });
441
- let copied = 0;
442
- let skipped = 0;
443
- const installed = [];
444
- for (const file of COMMANDS) {
445
- const sourcePath = path.join(commandsSourceDir, file);
446
- const targetPath = path.join(targetDir, file);
447
- try {
448
- await fs.access(sourcePath);
449
- try {
450
- await fs.access(targetPath);
451
- const sourceContent = await fs.readFile(sourcePath, "utf-8");
452
- const targetContent = await fs.readFile(targetPath, "utf-8");
453
- if (sourceContent === targetContent) {
454
- skipped++;
455
- continue;
456
- }
457
- } catch {}
458
- await fs.copyFile(sourcePath, targetPath);
459
- installed.push(file);
460
- copied++;
461
- } catch (err) {
462
- console.error(`Warning: Could not copy ${file}:`, err instanceof Error ? err.message : String(err));
463
- }
464
- }
457
+ await this.setupSiteTemplate(latticeHome);
458
+ await this.installClaudeCommands();
465
459
  console.log();
466
- console.log(`\u2705 Lattice commands installed to ${targetDir}`);
460
+ console.log("Available commands:");
461
+ console.log(" lattice site - Build and run the documentation site");
462
+ console.log(" lattice sync - Sync documents to knowledge graph");
463
+ console.log(" lattice status - Show documents needing sync");
464
+ console.log(" lattice search - Semantic search across documents");
467
465
  console.log();
468
- if (copied > 0) {
469
- console.log(`Installed ${copied} command(s):`);
470
- installed.forEach((f) => {
471
- const name = f.replace(".md", "");
472
- console.log(` - /${name}`);
473
- });
474
- }
475
- if (skipped > 0) {
476
- console.log(`Skipped ${skipped} unchanged command(s)`);
477
- }
478
- console.log();
479
- console.log("Available commands in Claude Code:");
466
+ console.log("Claude Code slash commands:");
480
467
  console.log(" /research <topic> - AI-assisted research workflow");
481
468
  console.log(" /graph-sync - Extract entities and sync to graph");
482
- console.log(" /entity-extract - Extract entities from a single document");
483
- console.log();
484
- console.log(`\u26A0\uFE0F Add your Voyage API key to: ${getEnvPath()}`);
469
+ console.log(" /entity-extract - Extract entities from a document");
485
470
  console.log();
471
+ if (!(await fs.readFile(envPath, "utf-8")).includes("pa-")) {
472
+ console.log(`\u26A0\uFE0F Add your Voyage API key to: ${envPath}`);
473
+ console.log();
474
+ }
486
475
  process.exit(0);
487
476
  } catch (error) {
488
477
  console.error("Error:", error instanceof Error ? error.message : String(error));
489
478
  process.exit(1);
490
479
  }
491
480
  }
481
+ async setupSiteTemplate(latticeHome) {
482
+ let templateDir = path.resolve(__dirname2, "..", "site-template");
483
+ try {
484
+ await fs.access(templateDir);
485
+ } catch {
486
+ templateDir = path.resolve(__dirname2, "..", "..", "site-template");
487
+ }
488
+ try {
489
+ await fs.access(templateDir);
490
+ } catch {
491
+ console.log("\u26A0\uFE0F Site template not found - skipping site setup");
492
+ return;
493
+ }
494
+ await fs.mkdir(path.join(latticeHome, "src", "collections"), {
495
+ recursive: true
496
+ });
497
+ let copied = 0;
498
+ let skipped = 0;
499
+ for (const file of SITE_TEMPLATE_FILES) {
500
+ const sourcePath = path.join(templateDir, file);
501
+ const targetPath = path.join(latticeHome, file);
502
+ try {
503
+ await fs.access(sourcePath);
504
+ try {
505
+ await fs.access(targetPath);
506
+ const sourceContent = await fs.readFile(sourcePath, "utf-8");
507
+ const targetContent = await fs.readFile(targetPath, "utf-8");
508
+ if (sourceContent === targetContent) {
509
+ skipped++;
510
+ continue;
511
+ }
512
+ } catch {}
513
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
514
+ await fs.copyFile(sourcePath, targetPath);
515
+ copied++;
516
+ } catch (err) {}
517
+ }
518
+ if (copied > 0) {
519
+ console.log(`\u2705 Site template: ${copied} file(s) installed`);
520
+ }
521
+ if (skipped > 0) {
522
+ console.log(` Site template: ${skipped} file(s) unchanged`);
523
+ }
524
+ }
525
+ async installClaudeCommands() {
526
+ const targetDir = path.join(homedir2(), ".claude", "commands");
527
+ let commandsSourceDir = path.resolve(__dirname2, "..", "commands");
528
+ try {
529
+ await fs.access(commandsSourceDir);
530
+ } catch {
531
+ commandsSourceDir = path.resolve(__dirname2, "..", "..", "commands");
532
+ }
533
+ try {
534
+ await fs.access(commandsSourceDir);
535
+ } catch {
536
+ console.log("\u26A0\uFE0F Claude commands not found - skipping");
537
+ return;
538
+ }
539
+ await fs.mkdir(targetDir, { recursive: true });
540
+ let copied = 0;
541
+ let skipped = 0;
542
+ const installed = [];
543
+ for (const file of COMMANDS) {
544
+ const sourcePath = path.join(commandsSourceDir, file);
545
+ const targetPath = path.join(targetDir, file);
546
+ try {
547
+ await fs.access(sourcePath);
548
+ try {
549
+ await fs.access(targetPath);
550
+ const sourceContent = await fs.readFile(sourcePath, "utf-8");
551
+ const targetContent = await fs.readFile(targetPath, "utf-8");
552
+ if (sourceContent === targetContent) {
553
+ skipped++;
554
+ continue;
555
+ }
556
+ } catch {}
557
+ await fs.copyFile(sourcePath, targetPath);
558
+ installed.push(file);
559
+ copied++;
560
+ } catch (err) {}
561
+ }
562
+ if (copied > 0) {
563
+ console.log(`\u2705 Claude commands: ${copied} installed to ${targetDir}`);
564
+ }
565
+ if (skipped > 0) {
566
+ console.log(` Claude commands: ${skipped} unchanged`);
567
+ }
568
+ }
492
569
  }
493
570
  InitCommand = __legacyDecorateClassTS([
494
571
  Injectable3(),
495
572
  Command2({
496
573
  name: "init",
497
- description: "Install Claude Code slash commands for Lattice"
574
+ description: "Initialize Lattice with Claude Code commands and site generator"
498
575
  })
499
576
  ], InitCommand);
500
577
  // src/commands/migrate.command.ts
@@ -873,8 +950,7 @@ class GraphService {
873
950
  const allResults = [];
874
951
  const conn = await this.ensureConnected();
875
952
  const vectorStr = `[${queryVector.join(", ")}]`;
876
- try {
877
- const reader = await conn.runAndReadAll(`
953
+ const sql = `
878
954
  SELECT
879
955
  name,
880
956
  label,
@@ -883,10 +959,11 @@ class GraphService {
883
959
  array_cosine_similarity(embedding, ${vectorStr}::FLOAT[${this.embeddingDimensions}]) as similarity
884
960
  FROM nodes
885
961
  WHERE embedding IS NOT NULL
886
- ORDER BY similarity DESC
887
- LIMIT ${k}
888
- `);
889
- for (const row of reader.getRows()) {
962
+ `;
963
+ try {
964
+ const reader = await conn.runAndReadAll(sql);
965
+ const rows = reader.getRows();
966
+ for (const row of rows) {
890
967
  const [name, label, title, description, similarity] = row;
891
968
  allResults.push({
892
969
  name,
@@ -3120,10 +3197,214 @@ SqlCommand = __legacyDecorateClassTS([
3120
3197
  typeof GraphService === "undefined" ? Object : GraphService
3121
3198
  ])
3122
3199
  ], SqlCommand);
3123
- // src/commands/status.command.ts
3200
+ // src/commands/site.command.ts
3201
+ import { spawn } from "child_process";
3202
+ import { existsSync as existsSync6, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
3203
+ import * as path2 from "path";
3124
3204
  import { Injectable as Injectable16 } from "@nestjs/common";
3125
3205
  import { Command as Command6, CommandRunner as CommandRunner6, Option as Option4 } from "nest-commander";
3126
- class StatusCommand extends CommandRunner6 {
3206
+ class SiteCommand extends CommandRunner6 {
3207
+ getPidFile() {
3208
+ return path2.join(getLatticeHome(), "site.pid");
3209
+ }
3210
+ async run(_inputs, options) {
3211
+ if (options.kill) {
3212
+ this.killSiteProcess();
3213
+ process.exit(0);
3214
+ }
3215
+ const latticeHome = getLatticeHome();
3216
+ const packageJsonPath = path2.join(latticeHome, "package.json");
3217
+ const nodeModulesPath = path2.join(latticeHome, "node_modules");
3218
+ if (!existsSync6(packageJsonPath)) {
3219
+ console.error("Error: Site not initialized. Run 'lattice init' first.");
3220
+ process.exit(1);
3221
+ }
3222
+ if (this.isRunning()) {
3223
+ console.error("Error: A Lattice site is already running.");
3224
+ console.error("Use 'lattice site --kill' to stop it first.");
3225
+ process.exit(1);
3226
+ }
3227
+ if (!existsSync6(nodeModulesPath)) {
3228
+ console.log("\uD83D\uDCE6 Installing dependencies...");
3229
+ await this.runCommand("bun", ["install"], latticeHome);
3230
+ console.log();
3231
+ }
3232
+ if (options.build) {
3233
+ console.log("\uD83D\uDD28 Building site...");
3234
+ await this.runCommand("bun", ["run", "build"], latticeHome);
3235
+ console.log(`
3236
+ \u2705 Build complete! Output in: ~/.lattice/dist/`);
3237
+ process.exit(0);
3238
+ }
3239
+ if (options.dev) {
3240
+ console.log("\uD83D\uDE80 Starting dev server...");
3241
+ const port2 = options.port || "4321";
3242
+ await this.runServerCommand("bun", ["run", "dev", "--", "--port", port2], latticeHome);
3243
+ process.exit(0);
3244
+ }
3245
+ console.log("\uD83D\uDD28 Building site with search index...");
3246
+ await this.runCommand("bun", ["run", "build"], latticeHome);
3247
+ console.log(`
3248
+ \uD83D\uDE80 Starting preview server...`);
3249
+ const port = options.port || "4321";
3250
+ await this.runServerCommand("bun", ["run", "preview", "--", "--port", port], latticeHome);
3251
+ }
3252
+ runCommand(cmd, args, cwd) {
3253
+ return new Promise((resolve4, reject) => {
3254
+ const child = spawn(cmd, args, {
3255
+ cwd,
3256
+ stdio: "pipe",
3257
+ env: { ...process.env, FORCE_COLOR: "1" }
3258
+ });
3259
+ child.stdout?.on("data", (data) => {
3260
+ process.stdout.write(data);
3261
+ });
3262
+ child.stderr?.on("data", (data) => {
3263
+ process.stderr.write(data);
3264
+ });
3265
+ child.on("close", (code) => {
3266
+ if (code === 0) {
3267
+ resolve4();
3268
+ } else {
3269
+ reject(new Error(`Command failed with code ${code}`));
3270
+ }
3271
+ });
3272
+ child.on("error", (err) => {
3273
+ reject(err);
3274
+ });
3275
+ });
3276
+ }
3277
+ runServerCommand(cmd, args, cwd) {
3278
+ return new Promise((resolve4, reject) => {
3279
+ const child = spawn(cmd, args, {
3280
+ cwd,
3281
+ stdio: "inherit",
3282
+ env: { ...process.env, FORCE_COLOR: "1" }
3283
+ });
3284
+ if (child.pid) {
3285
+ writeFileSync2(this.getPidFile(), String(child.pid));
3286
+ }
3287
+ const cleanup = () => {
3288
+ try {
3289
+ unlinkSync(this.getPidFile());
3290
+ } catch {}
3291
+ };
3292
+ child.on("close", (code) => {
3293
+ cleanup();
3294
+ if (code === 0) {
3295
+ resolve4();
3296
+ } else {
3297
+ reject(new Error(`Command failed with code ${code}`));
3298
+ }
3299
+ });
3300
+ child.on("error", (err) => {
3301
+ cleanup();
3302
+ reject(err);
3303
+ });
3304
+ process.on("SIGINT", cleanup);
3305
+ process.on("SIGTERM", cleanup);
3306
+ });
3307
+ }
3308
+ isRunning() {
3309
+ const pidFile = this.getPidFile();
3310
+ if (!existsSync6(pidFile)) {
3311
+ return false;
3312
+ }
3313
+ try {
3314
+ const pid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
3315
+ process.kill(pid, 0);
3316
+ return true;
3317
+ } catch {
3318
+ try {
3319
+ unlinkSync(pidFile);
3320
+ } catch {}
3321
+ return false;
3322
+ }
3323
+ }
3324
+ killSiteProcess() {
3325
+ const pidFile = this.getPidFile();
3326
+ if (!existsSync6(pidFile)) {
3327
+ console.log("No Lattice site process running");
3328
+ return;
3329
+ }
3330
+ try {
3331
+ const pid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
3332
+ try {
3333
+ process.kill(-pid, "SIGTERM");
3334
+ } catch {
3335
+ process.kill(pid, "SIGTERM");
3336
+ }
3337
+ unlinkSync(pidFile);
3338
+ console.log(`\u2705 Killed Lattice site process (PID: ${pid})`);
3339
+ } catch (err) {
3340
+ try {
3341
+ unlinkSync(pidFile);
3342
+ } catch {}
3343
+ console.log("No Lattice site process running");
3344
+ }
3345
+ }
3346
+ parseBuild() {
3347
+ return true;
3348
+ }
3349
+ parseDev() {
3350
+ return true;
3351
+ }
3352
+ parsePort(val) {
3353
+ return val;
3354
+ }
3355
+ parseKill() {
3356
+ return true;
3357
+ }
3358
+ }
3359
+ __legacyDecorateClassTS([
3360
+ Option4({
3361
+ flags: "-b, --build",
3362
+ description: "Build the site without starting the server"
3363
+ }),
3364
+ __legacyMetadataTS("design:type", Function),
3365
+ __legacyMetadataTS("design:paramtypes", []),
3366
+ __legacyMetadataTS("design:returntype", Boolean)
3367
+ ], SiteCommand.prototype, "parseBuild", null);
3368
+ __legacyDecorateClassTS([
3369
+ Option4({
3370
+ flags: "-d, --dev",
3371
+ description: "Run in development mode (hot reload, no search)"
3372
+ }),
3373
+ __legacyMetadataTS("design:type", Function),
3374
+ __legacyMetadataTS("design:paramtypes", []),
3375
+ __legacyMetadataTS("design:returntype", Boolean)
3376
+ ], SiteCommand.prototype, "parseDev", null);
3377
+ __legacyDecorateClassTS([
3378
+ Option4({
3379
+ flags: "-p, --port <port>",
3380
+ description: "Port to run the server on (default: 4321)"
3381
+ }),
3382
+ __legacyMetadataTS("design:type", Function),
3383
+ __legacyMetadataTS("design:paramtypes", [
3384
+ String
3385
+ ]),
3386
+ __legacyMetadataTS("design:returntype", String)
3387
+ ], SiteCommand.prototype, "parsePort", null);
3388
+ __legacyDecorateClassTS([
3389
+ Option4({
3390
+ flags: "-k, --kill",
3391
+ description: "Kill the running Lattice site process"
3392
+ }),
3393
+ __legacyMetadataTS("design:type", Function),
3394
+ __legacyMetadataTS("design:paramtypes", []),
3395
+ __legacyMetadataTS("design:returntype", Boolean)
3396
+ ], SiteCommand.prototype, "parseKill", null);
3397
+ SiteCommand = __legacyDecorateClassTS([
3398
+ Injectable16(),
3399
+ Command6({
3400
+ name: "site",
3401
+ description: "Build and run the Lattice documentation site"
3402
+ })
3403
+ ], SiteCommand);
3404
+ // src/commands/status.command.ts
3405
+ import { Injectable as Injectable17 } from "@nestjs/common";
3406
+ import { Command as Command7, CommandRunner as CommandRunner7, Option as Option5 } from "nest-commander";
3407
+ class StatusCommand extends CommandRunner7 {
3127
3408
  syncService;
3128
3409
  dbChangeDetector;
3129
3410
  constructor(syncService, dbChangeDetector) {
@@ -3189,7 +3470,7 @@ class StatusCommand extends CommandRunner6 {
3189
3470
  }
3190
3471
  }
3191
3472
  __legacyDecorateClassTS([
3192
- Option4({
3473
+ Option5({
3193
3474
  flags: "-v, --verbose",
3194
3475
  description: "Show all documents including unchanged"
3195
3476
  }),
@@ -3198,8 +3479,8 @@ __legacyDecorateClassTS([
3198
3479
  __legacyMetadataTS("design:returntype", Boolean)
3199
3480
  ], StatusCommand.prototype, "parseVerbose", null);
3200
3481
  StatusCommand = __legacyDecorateClassTS([
3201
- Injectable16(),
3202
- Command6({
3482
+ Injectable17(),
3483
+ Command7({
3203
3484
  name: "status",
3204
3485
  description: "Show documents that need syncing (new or updated)"
3205
3486
  }),
@@ -3210,12 +3491,12 @@ StatusCommand = __legacyDecorateClassTS([
3210
3491
  ], StatusCommand);
3211
3492
  // src/commands/sync.command.ts
3212
3493
  import { watch } from "fs";
3213
- import { join as join3 } from "path";
3214
- import { Injectable as Injectable18 } from "@nestjs/common";
3215
- import { Command as Command7, CommandRunner as CommandRunner7, Option as Option5 } from "nest-commander";
3494
+ import { join as join4 } from "path";
3495
+ import { Injectable as Injectable19 } from "@nestjs/common";
3496
+ import { Command as Command8, CommandRunner as CommandRunner8, Option as Option6 } from "nest-commander";
3216
3497
 
3217
3498
  // src/sync/graph-validator.service.ts
3218
- import { Injectable as Injectable17, Logger as Logger9 } from "@nestjs/common";
3499
+ import { Injectable as Injectable18, Logger as Logger9 } from "@nestjs/common";
3219
3500
  class GraphValidatorService {
3220
3501
  graph;
3221
3502
  logger = new Logger9(GraphValidatorService.name);
@@ -3334,15 +3615,15 @@ class GraphValidatorService {
3334
3615
  });
3335
3616
  }
3336
3617
  }
3337
- async validateDocument(path2) {
3618
+ async validateDocument(path3) {
3338
3619
  const issues = [];
3339
3620
  try {
3340
- const result = await this.graph.query(`SELECT label, name, properties FROM nodes WHERE label = 'Document' AND name = '${this.escape(path2)}'`);
3621
+ const result = await this.graph.query(`SELECT label, name, properties FROM nodes WHERE label = 'Document' AND name = '${this.escape(path3)}'`);
3341
3622
  if (result.resultSet.length === 0) {
3342
3623
  issues.push({
3343
3624
  type: "error",
3344
3625
  nodeLabel: "Document",
3345
- nodeName: path2,
3626
+ nodeName: path3,
3346
3627
  field: "node",
3347
3628
  message: "Document not found in graph",
3348
3629
  suggestion: "Run 'lattice sync' to add this document"
@@ -3352,9 +3633,9 @@ class GraphValidatorService {
3352
3633
  const row = result.resultSet[0];
3353
3634
  const propertiesJson = row[2];
3354
3635
  const properties = typeof propertiesJson === "string" ? JSON.parse(propertiesJson) : propertiesJson;
3355
- this.validateDocumentNode(path2, properties, issues);
3636
+ this.validateDocumentNode(path3, properties, issues);
3356
3637
  } catch (error) {
3357
- this.logger.error(`Failed to validate document ${path2}: ${error instanceof Error ? error.message : String(error)}`);
3638
+ this.logger.error(`Failed to validate document ${path3}: ${error instanceof Error ? error.message : String(error)}`);
3358
3639
  throw error;
3359
3640
  }
3360
3641
  return issues;
@@ -3364,35 +3645,45 @@ class GraphValidatorService {
3364
3645
  }
3365
3646
  }
3366
3647
  GraphValidatorService = __legacyDecorateClassTS([
3367
- Injectable17(),
3648
+ Injectable18(),
3368
3649
  __legacyMetadataTS("design:paramtypes", [
3369
3650
  typeof GraphService === "undefined" ? Object : GraphService
3370
3651
  ])
3371
3652
  ], GraphValidatorService);
3372
3653
 
3373
3654
  // src/commands/sync.command.ts
3374
- class SyncCommand extends CommandRunner7 {
3655
+ class SyncCommand extends CommandRunner8 {
3375
3656
  syncService;
3657
+ graphService;
3376
3658
  _graphValidator;
3377
3659
  watcher = null;
3378
3660
  isShuttingDown = false;
3379
- constructor(syncService, _graphValidator) {
3661
+ constructor(syncService, graphService, _graphValidator) {
3380
3662
  super();
3381
3663
  this.syncService = syncService;
3664
+ this.graphService = graphService;
3382
3665
  this._graphValidator = _graphValidator;
3383
3666
  }
3667
+ async safeExit(code) {
3668
+ try {
3669
+ await this.graphService.checkpoint();
3670
+ } catch (error) {
3671
+ console.error("Warning: checkpoint failed during exit");
3672
+ }
3673
+ process.exit(code);
3674
+ }
3384
3675
  async run(paths, options) {
3385
3676
  if (options.watch && options.dryRun) {
3386
3677
  console.log(`
3387
3678
  \u26A0\uFE0F Watch mode is not compatible with --dry-run mode
3388
3679
  `);
3389
- process.exit(1);
3680
+ await this.safeExit(1);
3390
3681
  }
3391
3682
  if (options.watch && options.force) {
3392
3683
  console.log(`
3393
3684
  \u26A0\uFE0F Watch mode is not compatible with --force mode (for safety)
3394
3685
  `);
3395
- process.exit(1);
3686
+ await this.safeExit(1);
3396
3687
  }
3397
3688
  if (options.force && paths.length === 0) {
3398
3689
  console.log(`
@@ -3400,7 +3691,7 @@ class SyncCommand extends CommandRunner7 {
3400
3691
  `);
3401
3692
  console.log(` Usage: lattice sync --force <path1> [path2] ...
3402
3693
  `);
3403
- process.exit(1);
3694
+ await this.safeExit(1);
3404
3695
  }
3405
3696
  const syncOptions = {
3406
3697
  force: options.force,
@@ -3447,12 +3738,12 @@ class SyncCommand extends CommandRunner7 {
3447
3738
  if (options.watch) {
3448
3739
  await this.enterWatchMode(syncOptions);
3449
3740
  } else {
3450
- process.exit(initialResult.errors.length > 0 ? 1 : 0);
3741
+ await this.safeExit(initialResult.errors.length > 0 ? 1 : 0);
3451
3742
  }
3452
3743
  } catch (error) {
3453
3744
  console.error(`
3454
3745
  \u274C Sync failed:`, error instanceof Error ? error.message : String(error));
3455
- process.exit(1);
3746
+ await this.safeExit(1);
3456
3747
  }
3457
3748
  }
3458
3749
  async enterWatchMode(syncOptions) {
@@ -3504,15 +3795,17 @@ class SyncCommand extends CommandRunner7 {
3504
3795
  `);
3505
3796
  this.watcher = watch(docsPath, { recursive: true }, (event, filename) => {
3506
3797
  if (filename?.endsWith(".md")) {
3507
- const fullPath = join3(docsPath, filename);
3798
+ const fullPath = join4(docsPath, filename);
3508
3799
  trackedFiles.add(fullPath);
3509
3800
  debouncedSync();
3510
3801
  }
3511
3802
  });
3512
- process.on("SIGINT", () => this.shutdown());
3803
+ process.on("SIGINT", () => {
3804
+ this.shutdown().catch(console.error);
3805
+ });
3513
3806
  await new Promise(() => {});
3514
3807
  }
3515
- shutdown() {
3808
+ async shutdown() {
3516
3809
  if (this.isShuttingDown)
3517
3810
  return;
3518
3811
  this.isShuttingDown = true;
@@ -3522,7 +3815,7 @@ class SyncCommand extends CommandRunner7 {
3522
3815
  if (this.watcher) {
3523
3816
  this.watcher.close();
3524
3817
  }
3525
- process.exit(0);
3818
+ await this.safeExit(0);
3526
3819
  }
3527
3820
  printSyncResults(result, isWatchMode = false) {
3528
3821
  console.log(`
@@ -3632,7 +3925,7 @@ class SyncCommand extends CommandRunner7 {
3632
3925
  }
3633
3926
  }
3634
3927
  __legacyDecorateClassTS([
3635
- Option5({
3928
+ Option6({
3636
3929
  flags: "-f, --force",
3637
3930
  description: "Force re-sync specified documents (requires paths to be specified)"
3638
3931
  }),
@@ -3641,7 +3934,7 @@ __legacyDecorateClassTS([
3641
3934
  __legacyMetadataTS("design:returntype", Boolean)
3642
3935
  ], SyncCommand.prototype, "parseForce", null);
3643
3936
  __legacyDecorateClassTS([
3644
- Option5({
3937
+ Option6({
3645
3938
  flags: "-d, --dry-run",
3646
3939
  description: "Show what would change without applying"
3647
3940
  }),
@@ -3650,7 +3943,7 @@ __legacyDecorateClassTS([
3650
3943
  __legacyMetadataTS("design:returntype", Boolean)
3651
3944
  ], SyncCommand.prototype, "parseDryRun", null);
3652
3945
  __legacyDecorateClassTS([
3653
- Option5({
3946
+ Option6({
3654
3947
  flags: "-v, --verbose",
3655
3948
  description: "Show detailed output"
3656
3949
  }),
@@ -3659,7 +3952,7 @@ __legacyDecorateClassTS([
3659
3952
  __legacyMetadataTS("design:returntype", Boolean)
3660
3953
  ], SyncCommand.prototype, "parseVerbose", null);
3661
3954
  __legacyDecorateClassTS([
3662
- Option5({
3955
+ Option6({
3663
3956
  flags: "-w, --watch",
3664
3957
  description: "Watch for file changes and sync automatically"
3665
3958
  }),
@@ -3668,7 +3961,7 @@ __legacyDecorateClassTS([
3668
3961
  __legacyMetadataTS("design:returntype", Boolean)
3669
3962
  ], SyncCommand.prototype, "parseWatch", null);
3670
3963
  __legacyDecorateClassTS([
3671
- Option5({
3964
+ Option6({
3672
3965
  flags: "--diff",
3673
3966
  description: "Show only changed documents (alias for --dry-run)"
3674
3967
  }),
@@ -3677,7 +3970,7 @@ __legacyDecorateClassTS([
3677
3970
  __legacyMetadataTS("design:returntype", Boolean)
3678
3971
  ], SyncCommand.prototype, "parseDiff", null);
3679
3972
  __legacyDecorateClassTS([
3680
- Option5({
3973
+ Option6({
3681
3974
  flags: "--skip-cascade",
3682
3975
  description: "Skip cascade analysis (faster for large repos)"
3683
3976
  }),
@@ -3686,7 +3979,7 @@ __legacyDecorateClassTS([
3686
3979
  __legacyMetadataTS("design:returntype", Boolean)
3687
3980
  ], SyncCommand.prototype, "parseSkipCascade", null);
3688
3981
  __legacyDecorateClassTS([
3689
- Option5({
3982
+ Option6({
3690
3983
  flags: "--no-embeddings",
3691
3984
  description: "Disable embedding generation during sync"
3692
3985
  }),
@@ -3695,7 +3988,7 @@ __legacyDecorateClassTS([
3695
3988
  __legacyMetadataTS("design:returntype", Boolean)
3696
3989
  ], SyncCommand.prototype, "parseNoEmbeddings", null);
3697
3990
  __legacyDecorateClassTS([
3698
- Option5({
3991
+ Option6({
3699
3992
  flags: "--skip-extraction",
3700
3993
  description: "Skip AI entity extraction (sync without re-extracting entities)"
3701
3994
  }),
@@ -3704,14 +3997,15 @@ __legacyDecorateClassTS([
3704
3997
  __legacyMetadataTS("design:returntype", Boolean)
3705
3998
  ], SyncCommand.prototype, "parseSkipExtraction", null);
3706
3999
  SyncCommand = __legacyDecorateClassTS([
3707
- Injectable18(),
3708
- Command7({
4000
+ Injectable19(),
4001
+ Command8({
3709
4002
  name: "sync",
3710
4003
  arguments: "[paths...]",
3711
4004
  description: "Synchronize documents to the knowledge graph"
3712
4005
  }),
3713
4006
  __legacyMetadataTS("design:paramtypes", [
3714
4007
  typeof SyncService === "undefined" ? Object : SyncService,
4008
+ typeof GraphService === "undefined" ? Object : GraphService,
3715
4009
  typeof GraphValidatorService === "undefined" ? Object : GraphValidatorService
3716
4010
  ])
3717
4011
  ], SyncCommand);
@@ -3743,7 +4037,7 @@ GraphModule = __legacyDecorateClassTS([
3743
4037
  import { Module as Module3 } from "@nestjs/common";
3744
4038
 
3745
4039
  // src/query/query.service.ts
3746
- import { Injectable as Injectable19, Logger as Logger10 } from "@nestjs/common";
4040
+ import { Injectable as Injectable20, Logger as Logger10 } from "@nestjs/common";
3747
4041
  class QueryService {
3748
4042
  graphService;
3749
4043
  logger = new Logger10(QueryService.name);
@@ -3756,7 +4050,7 @@ class QueryService {
3756
4050
  }
3757
4051
  }
3758
4052
  QueryService = __legacyDecorateClassTS([
3759
- Injectable19(),
4053
+ Injectable20(),
3760
4054
  __legacyMetadataTS("design:paramtypes", [
3761
4055
  typeof GraphService === "undefined" ? Object : GraphService
3762
4056
  ])
@@ -3829,7 +4123,8 @@ AppModule = __legacyDecorateClassTS([
3829
4123
  SqlCommand,
3830
4124
  OntologyCommand,
3831
4125
  InitCommand,
3832
- MigrateCommand
4126
+ MigrateCommand,
4127
+ SiteCommand
3833
4128
  ]
3834
4129
  })
3835
4130
  ], AppModule);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zabaca/lattice",
3
- "version": "1.0.22",
3
+ "version": "1.1.0",
4
4
  "description": "Human-initiated, AI-powered knowledge graph for markdown documentation",
5
5
  "type": "module",
6
6
  "bin": {