@zabaca/lattice 1.0.0 → 1.0.2

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.
@@ -3,7 +3,7 @@ description: Extract entities from modified docs and sync to graph
3
3
  model: sonnet
4
4
  ---
5
5
 
6
- Identify modified documents, extract entities from them, and sync to the knowledge graph.
6
+ Identify modified documents in `~/.lattice/docs/`, extract entities from them, and sync to the knowledge graph.
7
7
 
8
8
  ## Process
9
9
 
package/dist/main.js CHANGED
@@ -24,12 +24,52 @@ import { Module as Module5 } from "@nestjs/common";
24
24
  import { ConfigModule as ConfigModule2 } from "@nestjs/config";
25
25
 
26
26
  // src/commands/init.command.ts
27
+ import { existsSync as existsSync2, writeFileSync } from "fs";
27
28
  import * as fs from "fs/promises";
28
- import { homedir } from "os";
29
+ import { homedir as homedir2 } from "os";
29
30
  import * as path from "path";
30
31
  import { fileURLToPath } from "url";
31
32
  import { Injectable } from "@nestjs/common";
32
33
  import { Command, CommandRunner, Option } from "nest-commander";
34
+
35
+ // src/utils/paths.ts
36
+ import { existsSync, mkdirSync } from "fs";
37
+ import { homedir } from "os";
38
+ import { join } from "path";
39
+ var latticeHomeOverride = null;
40
+ function getLatticeHomeInternal() {
41
+ if (latticeHomeOverride) {
42
+ return latticeHomeOverride;
43
+ }
44
+ return join(homedir(), ".lattice");
45
+ }
46
+ function getLatticeHome() {
47
+ return getLatticeHomeInternal();
48
+ }
49
+ function getDocsPath() {
50
+ return join(getLatticeHomeInternal(), "docs");
51
+ }
52
+ function getDatabasePath() {
53
+ return join(getLatticeHomeInternal(), "lattice.duckdb");
54
+ }
55
+ function getManifestPath() {
56
+ return join(getLatticeHomeInternal(), ".sync-manifest.json");
57
+ }
58
+ function getEnvPath() {
59
+ return join(getLatticeHomeInternal(), ".env");
60
+ }
61
+ function ensureLatticeHome() {
62
+ const home = getLatticeHomeInternal();
63
+ if (!existsSync(home)) {
64
+ mkdirSync(home, { recursive: true });
65
+ }
66
+ const docsPath = getDocsPath();
67
+ if (!existsSync(docsPath)) {
68
+ mkdirSync(docsPath, { recursive: true });
69
+ }
70
+ }
71
+
72
+ // src/commands/init.command.ts
33
73
  var __filename2 = fileURLToPath(import.meta.url);
34
74
  var __dirname2 = path.dirname(__filename2);
35
75
  var COMMANDS = ["research.md", "graph-sync.md", "entity-extract.md"];
@@ -37,7 +77,20 @@ var COMMANDS = ["research.md", "graph-sync.md", "entity-extract.md"];
37
77
  class InitCommand extends CommandRunner {
38
78
  async run(_inputs, options) {
39
79
  try {
40
- const targetDir = options.global ? path.join(homedir(), ".claude", "commands") : path.join(process.cwd(), ".claude", "commands");
80
+ ensureLatticeHome();
81
+ const envPath = getEnvPath();
82
+ if (!existsSync2(envPath)) {
83
+ writeFileSync(envPath, `# Lattice Configuration
84
+ # Get your API key from: https://www.voyageai.com/
85
+
86
+ VOYAGE_API_KEY=
87
+ `);
88
+ }
89
+ console.log(`\u2705 Lattice home directory: ${getLatticeHome()}`);
90
+ console.log(` Documents: ${getDocsPath()}`);
91
+ console.log(` Config: ${envPath}`);
92
+ console.log();
93
+ const targetDir = options.global ? path.join(homedir2(), ".claude", "commands") : path.join(process.cwd(), ".claude", "commands");
41
94
  let commandsSourceDir = path.resolve(__dirname2, "..", "commands");
42
95
  try {
43
96
  await fs.access(commandsSourceDir);
@@ -97,7 +150,10 @@ class InitCommand extends CommandRunner {
97
150
  console.log();
98
151
  if (!options.global) {
99
152
  console.log("\uD83D\uDCA1 Tip: Use 'lattice init --global' to install for all projects");
153
+ console.log();
100
154
  }
155
+ console.log(`\u26A0\uFE0F Add your Voyage API key to: ${getEnvPath()}`);
156
+ console.log();
101
157
  process.exit(0);
102
158
  } catch (error) {
103
159
  console.error("Error:", error instanceof Error ? error.message : String(error));
@@ -134,31 +190,13 @@ import { Injectable as Injectable3 } from "@nestjs/common";
134
190
  // src/sync/document-parser.service.ts
135
191
  import { createHash } from "crypto";
136
192
  import { readFile as readFile2 } from "fs/promises";
137
- import { resolve as resolve2 } from "path";
138
193
  import { Injectable as Injectable2, Logger } from "@nestjs/common";
139
194
  import { glob } from "glob";
140
195
 
141
- // src/schemas/config.schemas.ts
142
- import { z } from "zod";
143
- var DuckDBConfigSchema = z.object({
144
- dbPath: z.string().optional(),
145
- embeddingDimensions: z.coerce.number().int().positive().default(512)
146
- });
147
- var EmbeddingConfigSchema = z.object({
148
- provider: z.enum(["openai", "voyage", "nomic", "mock"]).default("voyage"),
149
- apiKey: z.string().optional(),
150
- model: z.string().min(1).default("voyage-3.5-lite"),
151
- dimensions: z.coerce.number().int().positive().default(512)
152
- });
153
- var DocsConfigSchema = z.object({
154
- projectRoot: z.string().default(process.cwd()),
155
- docsPath: z.string().default("docs")
156
- });
157
-
158
196
  // src/utils/frontmatter.ts
159
197
  import matter from "gray-matter";
160
- import { z as z2 } from "zod";
161
- var EntityTypeSchema = z2.enum([
198
+ import { z } from "zod";
199
+ var EntityTypeSchema = z.enum([
162
200
  "Topic",
163
201
  "Technology",
164
202
  "Concept",
@@ -168,20 +206,20 @@ var EntityTypeSchema = z2.enum([
168
206
  "Organization",
169
207
  "Document"
170
208
  ]);
171
- var RelationTypeSchema = z2.enum(["REFERENCES"]);
172
- var EntitySchema = z2.object({
173
- name: z2.string().min(1),
209
+ var RelationTypeSchema = z.enum(["REFERENCES"]);
210
+ var EntitySchema = z.object({
211
+ name: z.string().min(1),
174
212
  type: EntityTypeSchema,
175
- description: z2.string().optional()
213
+ description: z.string().optional()
176
214
  });
177
- var RelationshipSchema = z2.object({
178
- source: z2.string().min(1),
215
+ var RelationshipSchema = z.object({
216
+ source: z.string().min(1),
179
217
  relation: RelationTypeSchema,
180
- target: z2.string().min(1)
218
+ target: z.string().min(1)
181
219
  });
182
- var GraphMetadataSchema = z2.object({
183
- importance: z2.enum(["high", "medium", "low"]).optional(),
184
- domain: z2.string().optional()
220
+ var GraphMetadataSchema = z.object({
221
+ importance: z.enum(["high", "medium", "low"]).optional(),
222
+ domain: z.string().optional()
185
223
  });
186
224
  var validateDateFormat = (dateStr) => {
187
225
  const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
@@ -201,15 +239,15 @@ var validateDateFormat = (dateStr) => {
201
239
  }
202
240
  return day <= daysInMonth[month - 1];
203
241
  };
204
- var FrontmatterSchema = z2.object({
205
- created: z2.string().refine(validateDateFormat, "Date must be in YYYY-MM-DD format"),
206
- updated: z2.string().refine(validateDateFormat, "Date must be in YYYY-MM-DD format"),
207
- status: z2.enum(["draft", "ongoing", "complete"]).optional(),
208
- topic: z2.string().optional(),
209
- tags: z2.array(z2.string()).optional(),
210
- summary: z2.string().optional(),
211
- entities: z2.array(EntitySchema).optional(),
212
- relationships: z2.array(RelationshipSchema).optional(),
242
+ var FrontmatterSchema = z.object({
243
+ created: z.string().refine(validateDateFormat, "Date must be in YYYY-MM-DD format"),
244
+ updated: z.string().refine(validateDateFormat, "Date must be in YYYY-MM-DD format"),
245
+ status: z.enum(["draft", "ongoing", "complete"]).optional(),
246
+ topic: z.string().optional(),
247
+ tags: z.array(z.string()).optional(),
248
+ summary: z.string().optional(),
249
+ entities: z.array(EntitySchema).optional(),
250
+ relationships: z.array(RelationshipSchema).optional(),
213
251
  graph: GraphMetadataSchema.optional()
214
252
  }).passthrough();
215
253
  function parseFrontmatter(content) {
@@ -256,15 +294,8 @@ class DocumentParserService {
256
294
  logger = new Logger(DocumentParserService.name);
257
295
  docsPath;
258
296
  constructor() {
259
- const config = DocsConfigSchema.parse({
260
- projectRoot: process.env.PROJECT_ROOT,
261
- docsPath: process.env.DOCS_PATH
262
- });
263
- if (config.docsPath.startsWith("/")) {
264
- this.docsPath = config.docsPath;
265
- } else {
266
- this.docsPath = resolve2(config.projectRoot, config.docsPath);
267
- }
297
+ ensureLatticeHome();
298
+ this.docsPath = getDocsPath();
268
299
  }
269
300
  getDocsPath() {
270
301
  return this.docsPath;
@@ -542,6 +573,18 @@ import { Command as Command3, CommandRunner as CommandRunner3, Option as Option2
542
573
  import { Injectable as Injectable5, Logger as Logger2 } from "@nestjs/common";
543
574
  import { ConfigService } from "@nestjs/config";
544
575
 
576
+ // src/schemas/config.schemas.ts
577
+ import { z as z2 } from "zod";
578
+ var DuckDBConfigSchema = z2.object({
579
+ embeddingDimensions: z2.coerce.number().int().positive().default(512)
580
+ });
581
+ var EmbeddingConfigSchema = z2.object({
582
+ provider: z2.enum(["openai", "voyage", "nomic", "mock"]).default("voyage"),
583
+ apiKey: z2.string().optional(),
584
+ model: z2.string().min(1).default("voyage-3.5-lite"),
585
+ dimensions: z2.coerce.number().int().positive().default(512)
586
+ });
587
+
545
588
  // src/embedding/embedding.types.ts
546
589
  var DEFAULT_EMBEDDING_CONFIG = {
547
590
  provider: "voyage",
@@ -642,7 +685,7 @@ var VoyageEmbeddingResponseSchema = z3.object({
642
685
  })),
643
686
  model: z3.string(),
644
687
  usage: z3.object({
645
- total_tokens: z3.number().int().positive()
688
+ total_tokens: z3.number().int().nonnegative()
646
689
  })
647
690
  });
648
691
 
@@ -803,7 +846,8 @@ class GraphService {
803
846
  embeddingDimensions;
804
847
  constructor(configService) {
805
848
  this.configService = configService;
806
- this.dbPath = this.configService.get("DUCKDB_PATH") || "./.lattice.duckdb";
849
+ ensureLatticeHome();
850
+ this.dbPath = getDatabasePath();
807
851
  this.embeddingDimensions = this.configService.get("EMBEDDING_DIMENSIONS") || 512;
808
852
  }
809
853
  async onModuleDestroy() {
@@ -1329,9 +1373,8 @@ import { Command as Command4, CommandRunner as CommandRunner4, Option as Option3
1329
1373
 
1330
1374
  // src/sync/manifest.service.ts
1331
1375
  import { createHash as createHash2 } from "crypto";
1332
- import { existsSync } from "fs";
1376
+ import { existsSync as existsSync3 } from "fs";
1333
1377
  import { readFile as readFile3, writeFile } from "fs/promises";
1334
- import { resolve as resolve3 } from "path";
1335
1378
  import { Injectable as Injectable8 } from "@nestjs/common";
1336
1379
 
1337
1380
  // src/schemas/manifest.schemas.ts
@@ -1350,23 +1393,15 @@ var SyncManifestSchema = z4.object({
1350
1393
  });
1351
1394
 
1352
1395
  // src/sync/manifest.service.ts
1353
- function getProjectRoot() {
1354
- if (process.env.PROJECT_ROOT) {
1355
- return process.env.PROJECT_ROOT;
1356
- }
1357
- return process.cwd();
1358
- }
1359
-
1360
1396
  class ManifestService {
1361
1397
  manifestPath;
1362
1398
  manifest = null;
1363
1399
  constructor() {
1364
- const docsPath = process.env.DOCS_PATH || "docs";
1365
- this.manifestPath = resolve3(getProjectRoot(), docsPath, ".sync-manifest.json");
1400
+ this.manifestPath = getManifestPath();
1366
1401
  }
1367
1402
  async load() {
1368
1403
  try {
1369
- if (existsSync(this.manifestPath)) {
1404
+ if (existsSync3(this.manifestPath)) {
1370
1405
  const content = await readFile3(this.manifestPath, "utf-8");
1371
1406
  this.manifest = SyncManifestSchema.parse(JSON.parse(content));
1372
1407
  } else {
@@ -1852,16 +1887,16 @@ CascadeService = __legacyDecorateClassTS([
1852
1887
  ], CascadeService);
1853
1888
 
1854
1889
  // src/sync/path-resolver.service.ts
1855
- import { existsSync as existsSync2 } from "fs";
1856
- import { isAbsolute, resolve as resolve4 } from "path";
1890
+ import { existsSync as existsSync4 } from "fs";
1891
+ import { isAbsolute, resolve as resolve2 } from "path";
1857
1892
  import { Injectable as Injectable10 } from "@nestjs/common";
1858
1893
  class PathResolverService {
1859
- parser;
1860
- constructor(parser) {
1861
- this.parser = parser;
1894
+ docsPath;
1895
+ constructor() {
1896
+ this.docsPath = getDocsPath();
1862
1897
  }
1863
1898
  getDocsPath() {
1864
- return this.parser.getDocsPath();
1899
+ return this.docsPath;
1865
1900
  }
1866
1901
  resolveDocPath(userPath, options = {}) {
1867
1902
  const { requireExists = true, requireInDocs = true } = options;
@@ -1869,28 +1904,12 @@ class PathResolverService {
1869
1904
  if (isAbsolute(userPath)) {
1870
1905
  resolvedPath = userPath;
1871
1906
  } else {
1872
- const fromCwd = resolve4(process.cwd(), userPath);
1873
- const fromDocs = resolve4(this.getDocsPath(), userPath);
1874
- const docsPrefix = "docs/";
1875
- const strippedFromDocs = userPath.startsWith(docsPrefix) ? resolve4(this.getDocsPath(), userPath.slice(docsPrefix.length)) : null;
1876
- if (this.isUnderDocs(fromCwd) && existsSync2(fromCwd)) {
1877
- resolvedPath = fromCwd;
1878
- } else if (strippedFromDocs && existsSync2(strippedFromDocs)) {
1879
- resolvedPath = strippedFromDocs;
1880
- } else if (existsSync2(fromDocs)) {
1881
- resolvedPath = fromDocs;
1882
- } else if (this.isUnderDocs(fromCwd)) {
1883
- resolvedPath = fromCwd;
1884
- } else if (strippedFromDocs) {
1885
- resolvedPath = strippedFromDocs;
1886
- } else {
1887
- resolvedPath = fromDocs;
1888
- }
1907
+ resolvedPath = resolve2(this.docsPath, userPath);
1889
1908
  }
1890
1909
  if (requireInDocs && !this.isUnderDocs(resolvedPath)) {
1891
- throw new Error(`Path "${userPath}" resolves to "${resolvedPath}" which is outside the docs directory (${this.getDocsPath()})`);
1910
+ throw new Error(`Path "${userPath}" resolves to "${resolvedPath}" which is outside the docs directory (${this.docsPath})`);
1892
1911
  }
1893
- if (requireExists && !existsSync2(resolvedPath)) {
1912
+ if (requireExists && !existsSync4(resolvedPath)) {
1894
1913
  throw new Error(`Path "${userPath}" does not exist (resolved to: ${resolvedPath})`);
1895
1914
  }
1896
1915
  return resolvedPath;
@@ -1914,9 +1933,7 @@ class PathResolverService {
1914
1933
  }
1915
1934
  PathResolverService = __legacyDecorateClassTS([
1916
1935
  Injectable10(),
1917
- __legacyMetadataTS("design:paramtypes", [
1918
- typeof DocumentParserService === "undefined" ? Object : DocumentParserService
1919
- ])
1936
+ __legacyMetadataTS("design:paramtypes", [])
1920
1937
  ], PathResolverService);
1921
1938
 
1922
1939
  // src/sync/sync.service.ts
@@ -2419,7 +2436,7 @@ StatusCommand = __legacyDecorateClassTS([
2419
2436
  ], StatusCommand);
2420
2437
  // src/commands/sync.command.ts
2421
2438
  import { watch } from "fs";
2422
- import { join as join2 } from "path";
2439
+ import { join as join3 } from "path";
2423
2440
  import { Injectable as Injectable13 } from "@nestjs/common";
2424
2441
  import { Command as Command5, CommandRunner as CommandRunner5, Option as Option4 } from "nest-commander";
2425
2442
  class SyncCommand extends CommandRunner5 {
@@ -2545,7 +2562,7 @@ class SyncCommand extends CommandRunner5 {
2545
2562
  `);
2546
2563
  this.watcher = watch(docsPath, { recursive: true }, (event, filename) => {
2547
2564
  if (filename?.endsWith(".md")) {
2548
- const fullPath = join2(docsPath, filename);
2565
+ const fullPath = join3(docsPath, filename);
2549
2566
  trackedFiles.add(fullPath);
2550
2567
  debouncedSync();
2551
2568
  }
@@ -2920,7 +2937,8 @@ AppModule = __legacyDecorateClassTS([
2920
2937
  Module5({
2921
2938
  imports: [
2922
2939
  ConfigModule2.forRoot({
2923
- isGlobal: true
2940
+ isGlobal: true,
2941
+ envFilePath: getEnvPath()
2924
2942
  }),
2925
2943
  GraphModule,
2926
2944
  SyncModule,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zabaca/lattice",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Human-initiated, AI-powered knowledge graph for markdown documentation",
5
5
  "type": "module",
6
6
  "bin": {