gnosys-mcp 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/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +927 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1092 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/bootstrap.d.ts +56 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +163 -0
- package/dist/lib/bootstrap.js.map +1 -0
- package/dist/lib/history.d.ts +39 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +86 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/ingest.d.ts +47 -0
- package/dist/lib/ingest.d.ts.map +1 -0
- package/dist/lib/ingest.js +134 -0
- package/dist/lib/ingest.js.map +1 -0
- package/dist/lib/lensing.d.ts +35 -0
- package/dist/lib/lensing.d.ts.map +1 -0
- package/dist/lib/lensing.js +85 -0
- package/dist/lib/lensing.js.map +1 -0
- package/dist/lib/resolver.d.ts +84 -0
- package/dist/lib/resolver.d.ts.map +1 -0
- package/dist/lib/resolver.js +269 -0
- package/dist/lib/resolver.js.map +1 -0
- package/dist/lib/search.d.ts +50 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +166 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/store.d.ts +67 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +168 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/tags.d.ts +26 -0
- package/dist/lib/tags.d.ts.map +1 -0
- package/dist/lib/tags.js +109 -0
- package/dist/lib/tags.js.map +1 -0
- package/dist/lib/timeline.d.ts +34 -0
- package/dist/lib/timeline.d.ts.map +1 -0
- package/dist/lib/timeline.js +116 -0
- package/dist/lib/timeline.js.map +1 -0
- package/dist/lib/wikilinks.d.ts +63 -0
- package/dist/lib/wikilinks.d.ts.map +1 -0
- package/dist/lib/wikilinks.js +146 -0
- package/dist/lib/wikilinks.js.map +1 -0
- package/dist/test/bootstrap.test.d.ts +2 -0
- package/dist/test/bootstrap.test.d.ts.map +1 -0
- package/dist/test/bootstrap.test.js +208 -0
- package/dist/test/bootstrap.test.js.map +1 -0
- package/dist/test/history.test.d.ts +2 -0
- package/dist/test/history.test.d.ts.map +1 -0
- package/dist/test/history.test.js +141 -0
- package/dist/test/history.test.js.map +1 -0
- package/dist/test/ingest.test.d.ts +2 -0
- package/dist/test/ingest.test.d.ts.map +1 -0
- package/dist/test/ingest.test.js +132 -0
- package/dist/test/ingest.test.js.map +1 -0
- package/dist/test/lensing.test.d.ts +2 -0
- package/dist/test/lensing.test.d.ts.map +1 -0
- package/dist/test/lensing.test.js +154 -0
- package/dist/test/lensing.test.js.map +1 -0
- package/dist/test/search.test.d.ts +2 -0
- package/dist/test/search.test.d.ts.map +1 -0
- package/dist/test/search.test.js +129 -0
- package/dist/test/search.test.js.map +1 -0
- package/dist/test/store.test.d.ts +2 -0
- package/dist/test/store.test.d.ts.map +1 -0
- package/dist/test/store.test.js +152 -0
- package/dist/test/store.test.js.map +1 -0
- package/dist/test/tags.test.d.ts +2 -0
- package/dist/test/tags.test.d.ts.map +1 -0
- package/dist/test/tags.test.js +81 -0
- package/dist/test/tags.test.js.map +1 -0
- package/dist/test/timeline.test.d.ts +2 -0
- package/dist/test/timeline.test.d.ts.map +1 -0
- package/dist/test/timeline.test.js +127 -0
- package/dist/test/timeline.test.js.map +1 -0
- package/dist/test/wikilinks.test.d.ts +2 -0
- package/dist/test/wikilinks.test.d.ts.map +1 -0
- package/dist/test/wikilinks.test.js +178 -0
- package/dist/test/wikilinks.test.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Smart Ingestion — Uses LLM to structure raw input into atomic memories.
|
|
3
|
+
* Accepts messy human input, produces clean markdown files with YAML frontmatter.
|
|
4
|
+
*/
|
|
5
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
6
|
+
export class GnosysIngestion {
|
|
7
|
+
client = null;
|
|
8
|
+
tagRegistry;
|
|
9
|
+
store;
|
|
10
|
+
constructor(store, tagRegistry) {
|
|
11
|
+
this.store = store;
|
|
12
|
+
this.tagRegistry = tagRegistry;
|
|
13
|
+
// Initialize Anthropic client if API key is available
|
|
14
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
15
|
+
if (apiKey) {
|
|
16
|
+
this.client = new Anthropic({ apiKey });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
get isLLMAvailable() {
|
|
20
|
+
return this.client !== null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Ingest raw text and structure it into an atomic memory.
|
|
24
|
+
* Uses LLM if available, otherwise requires structured input.
|
|
25
|
+
*/
|
|
26
|
+
async ingest(rawInput) {
|
|
27
|
+
if (!this.client) {
|
|
28
|
+
throw new Error("No ANTHROPIC_API_KEY set. Smart ingestion requires an LLM. " +
|
|
29
|
+
"Set the ANTHROPIC_API_KEY environment variable or use gnosys_add_structured.");
|
|
30
|
+
}
|
|
31
|
+
const registry = this.tagRegistry.getRegistry();
|
|
32
|
+
const categories = await this.store.getCategories();
|
|
33
|
+
const systemPrompt = `You are Gnosys, a knowledge management system. Your job is to take raw, unstructured input and produce a structured atomic memory.
|
|
34
|
+
|
|
35
|
+
An atomic memory is ONE concept, decision, or piece of knowledge per file. If the input contains multiple concepts, focus on the primary one.
|
|
36
|
+
|
|
37
|
+
You must output valid JSON with these fields:
|
|
38
|
+
- title: A clear, concise title for this memory
|
|
39
|
+
- category: One of the existing categories or suggest a new one. Existing: ${categories.join(", ")}
|
|
40
|
+
- tags: An object with tag categories as keys and arrays of tags as values. Use ONLY tags from the registry below. If no existing tag fits, include a "proposed_new_tags" array.
|
|
41
|
+
- relevance: A keyword cloud (space-separated words, NOT sentences) describing every context where this memory would be useful. Include synonyms, related terms, abbreviations, and domain variations. Example: "auth authentication OAuth JWT login session tokens SSO identity credentials access-control permissions RBAC". Be generous — 15-30 keywords. This field powers discovery search.
|
|
42
|
+
- content: The memory content as clean markdown prose (NOT including the title as an H1 — that's added automatically)
|
|
43
|
+
- confidence: 0.0 to 1.0 — how confident you are this is a well-formed atomic memory
|
|
44
|
+
- filename: A kebab-case filename (without .md extension)
|
|
45
|
+
|
|
46
|
+
TAG REGISTRY:
|
|
47
|
+
${Object.entries(registry)
|
|
48
|
+
.map(([cat, tags]) => `${cat}: ${tags.join(", ")}`)
|
|
49
|
+
.join("\n")}
|
|
50
|
+
|
|
51
|
+
Rules:
|
|
52
|
+
1. One concept per memory. If the input is about multiple things, pick the most important one.
|
|
53
|
+
2. Use existing tags from the registry. Only propose new tags if nothing fits.
|
|
54
|
+
3. The content should be self-contained — someone reading just this file should understand the concept.
|
|
55
|
+
4. Be concise. Memories are meant to be quickly scanned, not deeply read.
|
|
56
|
+
5. Write in third person or neutral voice, not first person.
|
|
57
|
+
6. The relevance field is critical for discovery. Include all terms an agent might use to find this memory — think about what someone working on a related task would search for.`;
|
|
58
|
+
const response = await this.client.messages.create({
|
|
59
|
+
model: "claude-haiku-4-5-20251001",
|
|
60
|
+
max_tokens: 2000,
|
|
61
|
+
system: systemPrompt,
|
|
62
|
+
messages: [
|
|
63
|
+
{
|
|
64
|
+
role: "user",
|
|
65
|
+
content: `Structure this into an atomic memory:\n\n${rawInput}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
// Extract text from response
|
|
70
|
+
const text = response.content[0].type === "text" ? response.content[0].text : "";
|
|
71
|
+
// Parse JSON from response (handle markdown code blocks)
|
|
72
|
+
const jsonMatch = text.match(/```json\s*([\s\S]*?)```/) ||
|
|
73
|
+
text.match(/```\s*([\s\S]*?)```/) || [null, text];
|
|
74
|
+
const parsed = JSON.parse(jsonMatch[1] || text);
|
|
75
|
+
// Validate and clean up tags
|
|
76
|
+
const cleanTags = {};
|
|
77
|
+
const proposedNewTags = [];
|
|
78
|
+
if (parsed.tags && typeof parsed.tags === "object") {
|
|
79
|
+
for (const [cat, tags] of Object.entries(parsed.tags)) {
|
|
80
|
+
if (Array.isArray(tags)) {
|
|
81
|
+
const validTags = [];
|
|
82
|
+
for (const tag of tags) {
|
|
83
|
+
if (this.tagRegistry.hasTag(tag)) {
|
|
84
|
+
validTags.push(tag);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
proposedNewTags.push({ category: cat, tag });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (validTags.length > 0)
|
|
91
|
+
cleanTags[cat] = validTags;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Add any explicitly proposed new tags
|
|
96
|
+
if (parsed.proposed_new_tags) {
|
|
97
|
+
for (const item of parsed.proposed_new_tags) {
|
|
98
|
+
proposedNewTags.push(item);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
title: parsed.title || "Untitled Memory",
|
|
103
|
+
category: parsed.category || "uncategorized",
|
|
104
|
+
tags: cleanTags,
|
|
105
|
+
relevance: parsed.relevance || "",
|
|
106
|
+
content: parsed.content || rawInput,
|
|
107
|
+
confidence: parsed.confidence || 0.7,
|
|
108
|
+
filename: parsed.filename || this.slugify(parsed.title || "memory"),
|
|
109
|
+
proposedNewTags: proposedNewTags.length > 0 ? proposedNewTags : undefined,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Create a memory from already-structured input (no LLM needed).
|
|
114
|
+
*/
|
|
115
|
+
createStructured(input) {
|
|
116
|
+
return {
|
|
117
|
+
title: input.title,
|
|
118
|
+
category: input.category,
|
|
119
|
+
tags: input.tags,
|
|
120
|
+
relevance: input.relevance || "",
|
|
121
|
+
content: input.content,
|
|
122
|
+
confidence: input.confidence || 0.8,
|
|
123
|
+
filename: this.slugify(input.title),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
slugify(text) {
|
|
127
|
+
return text
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
130
|
+
.replace(/^-|-$/g, "")
|
|
131
|
+
.substring(0, 60);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/lib/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Lensing — Filtered views of the memory store.
|
|
3
|
+
*
|
|
4
|
+
* A lens is a set of filter criteria applied to memories.
|
|
5
|
+
* Compound lenses combine multiple filters with AND/OR logic.
|
|
6
|
+
*/
|
|
7
|
+
import { Memory } from "./store.js";
|
|
8
|
+
export interface LensFilter {
|
|
9
|
+
category?: string;
|
|
10
|
+
tags?: string[];
|
|
11
|
+
tagMatchMode?: "any" | "all";
|
|
12
|
+
status?: ("active" | "archived" | "superseded")[];
|
|
13
|
+
author?: ("human" | "ai" | "human+ai")[];
|
|
14
|
+
authority?: ("declared" | "observed" | "imported" | "inferred")[];
|
|
15
|
+
minConfidence?: number;
|
|
16
|
+
maxConfidence?: number;
|
|
17
|
+
createdAfter?: string;
|
|
18
|
+
createdBefore?: string;
|
|
19
|
+
modifiedAfter?: string;
|
|
20
|
+
modifiedBefore?: string;
|
|
21
|
+
}
|
|
22
|
+
export type CompoundOperator = "AND" | "OR";
|
|
23
|
+
export interface CompoundLens {
|
|
24
|
+
filters: LensFilter[];
|
|
25
|
+
operator: CompoundOperator;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Apply a single lens filter to an array of memories.
|
|
29
|
+
*/
|
|
30
|
+
export declare function applyLens(memories: Memory[], lens: LensFilter): Memory[];
|
|
31
|
+
/**
|
|
32
|
+
* Apply a compound lens (multiple filters combined with AND/OR).
|
|
33
|
+
*/
|
|
34
|
+
export declare function applyCompoundLens(memories: Memory[], compound: CompoundLens): Memory[];
|
|
35
|
+
//# sourceMappingURL=lensing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lensing.d.ts","sourceRoot":"","sources":["../../src/lib/lensing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,EAAE,CAAC,QAAQ,GAAG,UAAU,GAAG,YAAY,CAAC,EAAE,CAAC;IAClD,MAAM,CAAC,EAAE,CAAC,OAAO,GAAG,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC,EAAE,CAAC;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,EAAE,CAExE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAAE,EAClB,QAAQ,EAAE,YAAY,GACrB,MAAM,EAAE,CAcV"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Lensing — Filtered views of the memory store.
|
|
3
|
+
*
|
|
4
|
+
* A lens is a set of filter criteria applied to memories.
|
|
5
|
+
* Compound lenses combine multiple filters with AND/OR logic.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Apply a single lens filter to an array of memories.
|
|
9
|
+
*/
|
|
10
|
+
export function applyLens(memories, lens) {
|
|
11
|
+
return memories.filter((m) => matchesLens(m, lens));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Apply a compound lens (multiple filters combined with AND/OR).
|
|
15
|
+
*/
|
|
16
|
+
export function applyCompoundLens(memories, compound) {
|
|
17
|
+
if (compound.filters.length === 0)
|
|
18
|
+
return memories;
|
|
19
|
+
if (compound.operator === "AND") {
|
|
20
|
+
// AND: memory must match ALL filters
|
|
21
|
+
return memories.filter((m) => compound.filters.every((f) => matchesLens(m, f)));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// OR: memory must match ANY filter
|
|
25
|
+
return memories.filter((m) => compound.filters.some((f) => matchesLens(m, f)));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a single memory matches a lens filter.
|
|
30
|
+
*/
|
|
31
|
+
function matchesLens(m, lens) {
|
|
32
|
+
const fm = m.frontmatter;
|
|
33
|
+
// Category filter
|
|
34
|
+
if (lens.category && fm.category !== lens.category)
|
|
35
|
+
return false;
|
|
36
|
+
// Status filter (array — memory must match one of the listed statuses)
|
|
37
|
+
if (lens.status && lens.status.length > 0) {
|
|
38
|
+
if (!lens.status.includes(fm.status))
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
// Author filter
|
|
42
|
+
if (lens.author && lens.author.length > 0) {
|
|
43
|
+
if (!lens.author.includes(fm.author))
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Authority filter
|
|
47
|
+
if (lens.authority && lens.authority.length > 0) {
|
|
48
|
+
if (!lens.authority.includes(fm.authority))
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// Confidence range
|
|
52
|
+
if (lens.minConfidence !== undefined && fm.confidence < lens.minConfidence)
|
|
53
|
+
return false;
|
|
54
|
+
if (lens.maxConfidence !== undefined && fm.confidence > lens.maxConfidence)
|
|
55
|
+
return false;
|
|
56
|
+
// Tag matching
|
|
57
|
+
if (lens.tags && lens.tags.length > 0) {
|
|
58
|
+
const memTags = flattenTags(fm.tags);
|
|
59
|
+
const mode = lens.tagMatchMode ?? "any";
|
|
60
|
+
const matches = lens.tags.filter((t) => memTags.includes(t));
|
|
61
|
+
if (mode === "all" && matches.length !== lens.tags.length)
|
|
62
|
+
return false;
|
|
63
|
+
if (mode === "any" && matches.length === 0)
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
// Date filters
|
|
67
|
+
if (lens.createdAfter && fm.created < lens.createdAfter)
|
|
68
|
+
return false;
|
|
69
|
+
if (lens.createdBefore && fm.created > lens.createdBefore)
|
|
70
|
+
return false;
|
|
71
|
+
if (lens.modifiedAfter && fm.modified < lens.modifiedAfter)
|
|
72
|
+
return false;
|
|
73
|
+
if (lens.modifiedBefore && fm.modified > lens.modifiedBefore)
|
|
74
|
+
return false;
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Flatten tags from either format to a simple string array.
|
|
79
|
+
*/
|
|
80
|
+
function flattenTags(tags) {
|
|
81
|
+
if (Array.isArray(tags))
|
|
82
|
+
return tags;
|
|
83
|
+
return Object.values(tags).flat();
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=lensing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lensing.js","sourceRoot":"","sources":["../../src/lib/lensing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Store Resolver — Discovers and manages layered stores.
|
|
3
|
+
*
|
|
4
|
+
* Four layers, resolved in specificity order:
|
|
5
|
+
* Project (auto-discovered from cwd, writable, default write target)
|
|
6
|
+
* Optional (GNOSYS_STORES, read-only)
|
|
7
|
+
* Personal (GNOSYS_PERSONAL, writable, fallback write target)
|
|
8
|
+
* Global (GNOSYS_GLOBAL, writable only when explicitly targeted)
|
|
9
|
+
*/
|
|
10
|
+
import { GnosysStore, Memory } from "./store.js";
|
|
11
|
+
export type StoreLayer = "project" | "personal" | "global" | "optional";
|
|
12
|
+
export interface ResolvedStore {
|
|
13
|
+
layer: StoreLayer;
|
|
14
|
+
label: string;
|
|
15
|
+
store: GnosysStore;
|
|
16
|
+
writable: boolean;
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
export interface LayeredMemory extends Memory {
|
|
20
|
+
sourceLayer: StoreLayer;
|
|
21
|
+
sourceLabel: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class GnosysResolver {
|
|
24
|
+
private stores;
|
|
25
|
+
/**
|
|
26
|
+
* Discover and initialize all store layers.
|
|
27
|
+
*/
|
|
28
|
+
resolve(): Promise<ResolvedStore[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Get all stores in precedence order.
|
|
31
|
+
*/
|
|
32
|
+
getStores(): ResolvedStore[];
|
|
33
|
+
/**
|
|
34
|
+
* Get all memories across all stores, tagged with their source.
|
|
35
|
+
*/
|
|
36
|
+
getAllMemories(): Promise<LayeredMemory[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Read a memory, searching stores in precedence order.
|
|
39
|
+
* Path format: "layer:category/filename.md" or just "category/filename.md" (searches all).
|
|
40
|
+
*/
|
|
41
|
+
readMemory(memPath: string): Promise<LayeredMemory | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Get the writable store for a given target layer.
|
|
44
|
+
*
|
|
45
|
+
* When no target is specified, auto-selects: project → personal.
|
|
46
|
+
* Global is writable but NEVER auto-selected — it must be explicitly
|
|
47
|
+
* requested (store: "global"). This prevents accidental writes to
|
|
48
|
+
* shared org knowledge.
|
|
49
|
+
* Optional stores are always read-only.
|
|
50
|
+
*/
|
|
51
|
+
getWriteTarget(target?: StoreLayer): ResolvedStore | null;
|
|
52
|
+
/**
|
|
53
|
+
* Get a summary of all active stores for logging.
|
|
54
|
+
*/
|
|
55
|
+
getSummary(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Register a project directory in the persistent project registry.
|
|
58
|
+
* Called by gnosys_init after creating a new store.
|
|
59
|
+
*/
|
|
60
|
+
registerProject(projectDir: string): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Directly add a project store to the active stores list.
|
|
63
|
+
* Used after gnosys_init creates a store, to avoid re-resolving from cwd.
|
|
64
|
+
*/
|
|
65
|
+
addProjectStore(storePath: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Find a project store. Checks registered projects first (from
|
|
68
|
+
* ~/.config/gnosys/projects.json), then walks up from cwd as fallback.
|
|
69
|
+
*/
|
|
70
|
+
private findProjectStore;
|
|
71
|
+
/**
|
|
72
|
+
* Read registered project directories from persistent config.
|
|
73
|
+
*/
|
|
74
|
+
private getRegisteredProjects;
|
|
75
|
+
/**
|
|
76
|
+
* Path to the persistent project registry file.
|
|
77
|
+
*/
|
|
78
|
+
private getRegistryPath;
|
|
79
|
+
/**
|
|
80
|
+
* Check if a path looks like a valid Gnosys store.
|
|
81
|
+
*/
|
|
82
|
+
private isValidStore;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/lib/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAExE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,UAAU,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,WAAW,EAAE,UAAU,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAuB;IAErC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IA6EzC;;OAEG;IACH,SAAS,IAAI,aAAa,EAAE;IAI5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAiBhD;;;OAGG;IACG,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAyBhE;;;;;;;;OAQG;IACH,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,GAAG,IAAI;IAgBzD;;OAEG;IACH,UAAU,IAAI,MAAM;IAapB;;;OAGG;IACG,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBxD;;;OAGG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAevD;;;OAGG;YACW,gBAAgB;IA0B9B;;OAEG;YACW,qBAAqB;IASnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;YACW,YAAY;CAQ3B"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gnosys Store Resolver — Discovers and manages layered stores.
|
|
3
|
+
*
|
|
4
|
+
* Four layers, resolved in specificity order:
|
|
5
|
+
* Project (auto-discovered from cwd, writable, default write target)
|
|
6
|
+
* Optional (GNOSYS_STORES, read-only)
|
|
7
|
+
* Personal (GNOSYS_PERSONAL, writable, fallback write target)
|
|
8
|
+
* Global (GNOSYS_GLOBAL, writable only when explicitly targeted)
|
|
9
|
+
*/
|
|
10
|
+
import fs from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import { GnosysStore } from "./store.js";
|
|
13
|
+
export class GnosysResolver {
|
|
14
|
+
stores = [];
|
|
15
|
+
/**
|
|
16
|
+
* Discover and initialize all store layers.
|
|
17
|
+
*/
|
|
18
|
+
async resolve() {
|
|
19
|
+
this.stores = [];
|
|
20
|
+
// 1. Project store — check registered projects first, then walk up from cwd.
|
|
21
|
+
// Registered projects are saved in ~/.config/gnosys/projects.json by gnosys_init.
|
|
22
|
+
// This is more reliable than cwd because MCP servers may be spawned
|
|
23
|
+
// with cwd set to the user's home directory rather than the open project.
|
|
24
|
+
const projectPath = await this.findProjectStore();
|
|
25
|
+
if (projectPath) {
|
|
26
|
+
const store = new GnosysStore(projectPath);
|
|
27
|
+
await store.init();
|
|
28
|
+
this.stores.push({
|
|
29
|
+
layer: "project",
|
|
30
|
+
label: "project",
|
|
31
|
+
store,
|
|
32
|
+
writable: true,
|
|
33
|
+
path: projectPath,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
// 2. Optional stores (GNOSYS_STORES — colon-separated)
|
|
37
|
+
const optionalPaths = process.env.GNOSYS_STORES;
|
|
38
|
+
if (optionalPaths) {
|
|
39
|
+
const paths = optionalPaths.split(":").filter(Boolean);
|
|
40
|
+
for (let i = 0; i < paths.length; i++) {
|
|
41
|
+
const p = path.resolve(paths[i]);
|
|
42
|
+
if (await this.isValidStore(p)) {
|
|
43
|
+
const store = new GnosysStore(p);
|
|
44
|
+
await store.init();
|
|
45
|
+
const dirName = path.basename(path.dirname(p)) || path.basename(p);
|
|
46
|
+
this.stores.push({
|
|
47
|
+
layer: "optional",
|
|
48
|
+
label: `optional:${dirName}`,
|
|
49
|
+
store,
|
|
50
|
+
writable: false,
|
|
51
|
+
path: p,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// 3. Personal store (GNOSYS_PERSONAL)
|
|
57
|
+
const personalPath = process.env.GNOSYS_PERSONAL;
|
|
58
|
+
if (personalPath) {
|
|
59
|
+
const p = path.resolve(personalPath);
|
|
60
|
+
const store = new GnosysStore(p);
|
|
61
|
+
await store.init();
|
|
62
|
+
this.stores.push({
|
|
63
|
+
layer: "personal",
|
|
64
|
+
label: "personal",
|
|
65
|
+
store,
|
|
66
|
+
writable: true,
|
|
67
|
+
path: p,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
// 4. Global store (GNOSYS_GLOBAL)
|
|
71
|
+
// Writable, but only when explicitly targeted — never auto-selected.
|
|
72
|
+
const globalPath = process.env.GNOSYS_GLOBAL;
|
|
73
|
+
if (globalPath) {
|
|
74
|
+
const p = path.resolve(globalPath);
|
|
75
|
+
if (await this.isValidStore(p)) {
|
|
76
|
+
const store = new GnosysStore(p);
|
|
77
|
+
await store.init();
|
|
78
|
+
this.stores.push({
|
|
79
|
+
layer: "global",
|
|
80
|
+
label: "global",
|
|
81
|
+
store,
|
|
82
|
+
writable: true,
|
|
83
|
+
path: p,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return this.stores;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get all stores in precedence order.
|
|
91
|
+
*/
|
|
92
|
+
getStores() {
|
|
93
|
+
return this.stores;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get all memories across all stores, tagged with their source.
|
|
97
|
+
*/
|
|
98
|
+
async getAllMemories() {
|
|
99
|
+
const allMemories = [];
|
|
100
|
+
for (const resolved of this.stores) {
|
|
101
|
+
const memories = await resolved.store.getAllMemories();
|
|
102
|
+
for (const m of memories) {
|
|
103
|
+
allMemories.push({
|
|
104
|
+
...m,
|
|
105
|
+
sourceLayer: resolved.layer,
|
|
106
|
+
sourceLabel: resolved.label,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return allMemories;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Read a memory, searching stores in precedence order.
|
|
114
|
+
* Path format: "layer:category/filename.md" or just "category/filename.md" (searches all).
|
|
115
|
+
*/
|
|
116
|
+
async readMemory(memPath) {
|
|
117
|
+
// Check if path includes a layer prefix
|
|
118
|
+
const layerMatch = memPath.match(/^(project|personal|global|optional(?::[^:]+)?):(.+)/);
|
|
119
|
+
if (layerMatch) {
|
|
120
|
+
const targetLabel = layerMatch[1];
|
|
121
|
+
const filePath = layerMatch[2];
|
|
122
|
+
const resolved = this.stores.find((s) => s.label === targetLabel || s.layer === targetLabel);
|
|
123
|
+
if (!resolved)
|
|
124
|
+
return null;
|
|
125
|
+
const memory = await resolved.store.readMemory(filePath);
|
|
126
|
+
if (!memory)
|
|
127
|
+
return null;
|
|
128
|
+
return { ...memory, sourceLayer: resolved.layer, sourceLabel: resolved.label };
|
|
129
|
+
}
|
|
130
|
+
// No layer prefix — search in precedence order
|
|
131
|
+
for (const resolved of this.stores) {
|
|
132
|
+
const memory = await resolved.store.readMemory(memPath);
|
|
133
|
+
if (memory) {
|
|
134
|
+
return { ...memory, sourceLayer: resolved.layer, sourceLabel: resolved.label };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get the writable store for a given target layer.
|
|
141
|
+
*
|
|
142
|
+
* When no target is specified, auto-selects: project → personal.
|
|
143
|
+
* Global is writable but NEVER auto-selected — it must be explicitly
|
|
144
|
+
* requested (store: "global"). This prevents accidental writes to
|
|
145
|
+
* shared org knowledge.
|
|
146
|
+
* Optional stores are always read-only.
|
|
147
|
+
*/
|
|
148
|
+
getWriteTarget(target) {
|
|
149
|
+
if (target) {
|
|
150
|
+
const store = this.stores.find((s) => s.layer === target && s.writable);
|
|
151
|
+
return store || null;
|
|
152
|
+
}
|
|
153
|
+
// Default: project first, then personal. Never global (requires explicit intent).
|
|
154
|
+
const project = this.stores.find((s) => s.layer === "project");
|
|
155
|
+
if (project?.writable)
|
|
156
|
+
return project;
|
|
157
|
+
const personal = this.stores.find((s) => s.layer === "personal");
|
|
158
|
+
if (personal?.writable)
|
|
159
|
+
return personal;
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get a summary of all active stores for logging.
|
|
164
|
+
*/
|
|
165
|
+
getSummary() {
|
|
166
|
+
if (this.stores.length === 0) {
|
|
167
|
+
return "No stores found. Create a .gnosys/ directory or set GNOSYS_PERSONAL.";
|
|
168
|
+
}
|
|
169
|
+
return this.stores
|
|
170
|
+
.map((s) => `[${s.label}] ${s.path} (${s.writable ? "read-write" : "read-only"})`)
|
|
171
|
+
.join("\n");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Register a project directory in the persistent project registry.
|
|
175
|
+
* Called by gnosys_init after creating a new store.
|
|
176
|
+
*/
|
|
177
|
+
async registerProject(projectDir) {
|
|
178
|
+
const configPath = this.getRegistryPath();
|
|
179
|
+
let projects = [];
|
|
180
|
+
try {
|
|
181
|
+
const raw = await fs.readFile(configPath, "utf-8");
|
|
182
|
+
projects = JSON.parse(raw);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// File doesn't exist yet — that's fine
|
|
186
|
+
}
|
|
187
|
+
const resolved = path.resolve(projectDir);
|
|
188
|
+
if (!projects.includes(resolved)) {
|
|
189
|
+
projects.push(resolved);
|
|
190
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
191
|
+
await fs.writeFile(configPath, JSON.stringify(projects, null, 2), "utf-8");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Directly add a project store to the active stores list.
|
|
196
|
+
* Used after gnosys_init creates a store, to avoid re-resolving from cwd.
|
|
197
|
+
*/
|
|
198
|
+
async addProjectStore(storePath) {
|
|
199
|
+
// Remove any existing project store first
|
|
200
|
+
this.stores = this.stores.filter((s) => s.layer !== "project");
|
|
201
|
+
const store = new GnosysStore(storePath);
|
|
202
|
+
await store.init();
|
|
203
|
+
this.stores.unshift({
|
|
204
|
+
layer: "project",
|
|
205
|
+
label: "project",
|
|
206
|
+
store,
|
|
207
|
+
writable: true,
|
|
208
|
+
path: storePath,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Find a project store. Checks registered projects first (from
|
|
213
|
+
* ~/.config/gnosys/projects.json), then walks up from cwd as fallback.
|
|
214
|
+
*/
|
|
215
|
+
async findProjectStore() {
|
|
216
|
+
// Check registered projects first — most reliable when MCP server
|
|
217
|
+
// cwd doesn't match the editor's open project.
|
|
218
|
+
const registered = await this.getRegisteredProjects();
|
|
219
|
+
for (const projectDir of registered) {
|
|
220
|
+
const candidate = path.join(projectDir, ".gnosys");
|
|
221
|
+
if (await this.isValidStore(candidate)) {
|
|
222
|
+
return candidate;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
// Fallback: walk up from cwd (works when cwd matches the project)
|
|
226
|
+
let dir = path.resolve(process.cwd());
|
|
227
|
+
const root = path.parse(dir).root;
|
|
228
|
+
while (dir !== root) {
|
|
229
|
+
const candidate = path.join(dir, ".gnosys");
|
|
230
|
+
if (await this.isValidStore(candidate)) {
|
|
231
|
+
return candidate;
|
|
232
|
+
}
|
|
233
|
+
dir = path.dirname(dir);
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Read registered project directories from persistent config.
|
|
239
|
+
*/
|
|
240
|
+
async getRegisteredProjects() {
|
|
241
|
+
try {
|
|
242
|
+
const raw = await fs.readFile(this.getRegistryPath(), "utf-8");
|
|
243
|
+
return JSON.parse(raw);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Path to the persistent project registry file.
|
|
251
|
+
*/
|
|
252
|
+
getRegistryPath() {
|
|
253
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
254
|
+
return path.join(home, ".config", "gnosys", "projects.json");
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Check if a path looks like a valid Gnosys store.
|
|
258
|
+
*/
|
|
259
|
+
async isValidStore(storePath) {
|
|
260
|
+
try {
|
|
261
|
+
const stat = await fs.stat(storePath);
|
|
262
|
+
return stat.isDirectory();
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/lib/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,WAAW,EAAU,MAAM,YAAY,CAAC;AAiBjD,MAAM,OAAO,cAAc;IACjB,MAAM,GAAoB,EAAE,CAAC;IAErC;;OAEG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QAEjB,6EAA6E;QAC7E,qFAAqF;QACrF,uEAAuE;QACvE,6EAA6E;QAC7E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAClD,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACf,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,SAAS;gBAChB,KAAK;gBACL,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,WAAW;aAClB,CAAC,CAAC;QACL,CAAC;QAED,uDAAuD;QACvD,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QAChD,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjC,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;oBACjC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;oBACnB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;oBACnE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;wBACf,KAAK,EAAE,UAAU;wBACjB,KAAK,EAAE,YAAY,OAAO,EAAE;wBAC5B,KAAK;wBACL,QAAQ,EAAE,KAAK;wBACf,IAAI,EAAE,CAAC;qBACR,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QACjD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;YACjC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACf,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,UAAU;gBACjB,KAAK;gBACL,QAAQ,EAAE,IAAI;gBACd,IAAI,EAAE,CAAC;aACR,CAAC,CAAC;QACL,CAAC;QAED,kCAAkC;QAClC,wEAAwE;QACxE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QAC7C,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACnC,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/B,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC;gBACjC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;gBACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;oBACf,KAAK,EAAE,QAAQ;oBACf,KAAK,EAAE,QAAQ;oBACf,KAAK;oBACL,QAAQ,EAAE,IAAI;oBACd,IAAI,EAAE,CAAC;iBACR,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc;QAClB,MAAM,WAAW,GAAoB,EAAE,CAAC;QAExC,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;YACvD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzB,WAAW,CAAC,IAAI,CAAC;oBACf,GAAG,CAAC;oBACJ,WAAW,EAAE,QAAQ,CAAC,KAAK;oBAC3B,WAAW,EAAE,QAAQ,CAAC,KAAK;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,OAAe;QAC9B,wCAAwC;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,qDAAqD,CAAC,CAAC;QAExF,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,IAAI,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC;YAC7F,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAE3B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACzD,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACzB,OAAO,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjF,CAAC;QAED,+CAA+C;QAC/C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACxD,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,EAAE,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjF,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACH,cAAc,CAAC,MAAmB;QAChC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;YACxE,OAAO,KAAK,IAAI,IAAI,CAAC;QACvB,CAAC;QAED,kFAAkF;QAClF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;QAC/D,IAAI,OAAO,EAAE,QAAQ;YAAE,OAAO,OAAO,CAAC;QAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC;QACjE,IAAI,QAAQ,EAAE,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAExC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,UAAU;QACR,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,sEAAsE,CAAC;QAChF,CAAC;QAED,OAAO,IAAI,CAAC,MAAM;aACf,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,GAAG,CACxE;aACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe,CAAC,UAAkB;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC1C,IAAI,QAAQ,GAAa,EAAE,CAAC;QAE5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACnD,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,uCAAuC;QACzC,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACxB,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9D,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe,CAAC,SAAiB;QACrC,0CAA0C;QAC1C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC;QAE/D,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC;YAClB,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,SAAS;YAChB,KAAK;YACL,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,SAAS;SAChB,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,gBAAgB;QAC5B,kEAAkE;QAClE,+CAA+C;QAC/C,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACtD,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YACnD,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QAED,kEAAkE;QAClE,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QAElC,OAAO,GAAG,KAAK,IAAI,EAAE,CAAC;YACpB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YAC5C,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvC,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1B,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,OAAO,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,MAAM,CAAC;QACnE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,SAAiB;QAC1C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACtC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|