@gmickel/gno 1.8.0 → 1.10.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 (62) hide show
  1. package/README.md +6 -2
  2. package/assets/skill/SKILL.md +41 -16
  3. package/assets/skill/cli-reference.md +39 -0
  4. package/assets/skill/examples.md +41 -0
  5. package/assets/skill/mcp-reference.md +13 -1
  6. package/package.json +1 -1
  7. package/src/cli/commands/capture.ts +9 -6
  8. package/src/cli/commands/graph.ts +89 -2
  9. package/src/cli/commands/index-cmd.ts +17 -6
  10. package/src/cli/commands/links.ts +237 -54
  11. package/src/cli/commands/query.ts +212 -0
  12. package/src/cli/commands/ref-parser.ts +8 -103
  13. package/src/cli/commands/shared.ts +17 -1
  14. package/src/cli/commands/update.ts +17 -6
  15. package/src/cli/options.ts +4 -0
  16. package/src/cli/program.ts +176 -9
  17. package/src/config/content-types.ts +154 -0
  18. package/src/config/defaults.ts +1 -0
  19. package/src/config/index.ts +14 -0
  20. package/src/config/loader.ts +11 -2
  21. package/src/config/types.ts +37 -1
  22. package/src/core/config-mutation.ts +14 -2
  23. package/src/core/graph-query.ts +137 -0
  24. package/src/core/graph-resolver.ts +117 -0
  25. package/src/core/note-presets.ts +61 -5
  26. package/src/core/ref-parser.ts +145 -0
  27. package/src/ingestion/frontmatter.ts +170 -2
  28. package/src/ingestion/index.ts +2 -0
  29. package/src/ingestion/sync-options.ts +29 -0
  30. package/src/ingestion/sync.ts +385 -17
  31. package/src/ingestion/types.ts +14 -0
  32. package/src/mcp/tools/add-collection.ts +8 -5
  33. package/src/mcp/tools/capture.ts +5 -2
  34. package/src/mcp/tools/get.ts +1 -1
  35. package/src/mcp/tools/index-cmd.ts +13 -7
  36. package/src/mcp/tools/index.ts +97 -10
  37. package/src/mcp/tools/links.ts +83 -1
  38. package/src/mcp/tools/multi-get.ts +1 -1
  39. package/src/mcp/tools/query.ts +207 -0
  40. package/src/mcp/tools/sync.ts +12 -6
  41. package/src/mcp/tools/workspace-write.ts +16 -10
  42. package/src/pipeline/diagnose.ts +302 -0
  43. package/src/pipeline/filters.ts +119 -0
  44. package/src/pipeline/hybrid.ts +92 -17
  45. package/src/pipeline/search.ts +2 -0
  46. package/src/pipeline/types.ts +34 -0
  47. package/src/pipeline/vsearch.ts +4 -0
  48. package/src/publish/export-service.ts +1 -1
  49. package/src/sdk/client.ts +51 -24
  50. package/src/sdk/documents.ts +2 -2
  51. package/src/serve/background-runtime.ts +18 -4
  52. package/src/serve/config-sync.ts +9 -2
  53. package/src/serve/routes/api.ts +244 -24
  54. package/src/serve/routes/graph.ts +173 -1
  55. package/src/serve/server.ts +24 -1
  56. package/src/serve/watch-service.ts +12 -2
  57. package/src/store/migrations/009-content-type-rule-fingerprint.ts +36 -0
  58. package/src/store/migrations/010-typed-edges.ts +67 -0
  59. package/src/store/migrations/011-doc-edge-traversal-indexes.ts +30 -0
  60. package/src/store/migrations/index.ts +16 -1
  61. package/src/store/sqlite/adapter.ts +853 -114
  62. package/src/store/types.ts +147 -0
@@ -117,6 +117,168 @@ function parseTagsValue(
117
117
  return [];
118
118
  }
119
119
 
120
+ function parseInlineArrayValue(value: string): string[] | undefined {
121
+ const inlineMatch = INLINE_ARRAY_REGEX.exec(value.trim());
122
+ if (!inlineMatch) {
123
+ return undefined;
124
+ }
125
+ const rawItems = inlineMatch[1];
126
+ if (rawItems === undefined) {
127
+ return [];
128
+ }
129
+
130
+ return rawItems
131
+ .split(",")
132
+ .map((item) => unquoteScalar(item.trim()))
133
+ .filter((item) => item.length > 0);
134
+ }
135
+
136
+ function unquoteScalar(value: string): string {
137
+ const trimmed = value.trim();
138
+ if (trimmed.length < 2) {
139
+ return trimmed;
140
+ }
141
+ const first = trimmed[0];
142
+ const last = trimmed.at(-1);
143
+ if ((first === '"' && last === '"') || (first === "'" && last === "'")) {
144
+ return trimmed.slice(1, -1).trim();
145
+ }
146
+ return trimmed;
147
+ }
148
+
149
+ function parseBlockArrayValue(lines: string[], startIdx: number): string[] {
150
+ const items: string[] = [];
151
+ for (let i = startIdx + 1; i < lines.length; i++) {
152
+ const line = lines[i];
153
+ if (line === undefined) break;
154
+ const itemMatch = YAML_ARRAY_ITEM_REGEX.exec(line);
155
+ if (itemMatch?.[1]) {
156
+ const item = unquoteScalar(itemMatch[1]);
157
+ if (item.length > 0) {
158
+ items.push(item);
159
+ }
160
+ continue;
161
+ }
162
+ if (
163
+ line.trim().length > 0 &&
164
+ !line.startsWith(" ") &&
165
+ !line.startsWith("\t")
166
+ ) {
167
+ break;
168
+ }
169
+ }
170
+ return items;
171
+ }
172
+
173
+ function countIndent(line: string): number {
174
+ let indent = 0;
175
+ for (const char of line) {
176
+ if (char === " ") {
177
+ indent += 1;
178
+ } else if (char === "\t") {
179
+ indent += 2;
180
+ } else {
181
+ break;
182
+ }
183
+ }
184
+ return indent;
185
+ }
186
+
187
+ function parseNestedMapValue(
188
+ lines: string[],
189
+ startIdx: number
190
+ ): Record<string, string[]> {
191
+ const result: Record<string, string[]> = {};
192
+
193
+ for (let i = startIdx + 1; i < lines.length; i++) {
194
+ const line = lines[i];
195
+ if (line === undefined) break;
196
+ if (line.trim().length === 0) continue;
197
+ if (!line.startsWith(" ") && !line.startsWith("\t")) {
198
+ break;
199
+ }
200
+
201
+ const keyIndent = countIndent(line);
202
+ const trimmed = line.trim();
203
+ const colonIdx = trimmed.indexOf(":");
204
+ if (colonIdx <= 0) {
205
+ continue;
206
+ }
207
+
208
+ const key = trimmed.slice(0, colonIdx).trim();
209
+ const rawValue = trimmed.slice(colonIdx + 1).trim();
210
+ const values: string[] = [];
211
+
212
+ const inlineArray = parseInlineArrayValue(rawValue);
213
+ if (inlineArray) {
214
+ values.push(...inlineArray);
215
+ } else if (rawValue.length > 0) {
216
+ values.push(unquoteScalar(rawValue));
217
+ } else {
218
+ for (let j = i + 1; j < lines.length; j++) {
219
+ const childLine = lines[j];
220
+ if (childLine === undefined) break;
221
+ if (childLine.trim().length === 0) continue;
222
+ const childIndent = countIndent(childLine);
223
+ if (childIndent <= keyIndent) {
224
+ break;
225
+ }
226
+ const itemMatch = YAML_ARRAY_ITEM_REGEX.exec(childLine);
227
+ if (itemMatch?.[1]) {
228
+ const item = unquoteScalar(itemMatch[1]);
229
+ if (item.length > 0) {
230
+ values.push(item);
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ if (key.length > 0 && values.length > 0) {
237
+ result[key] = values;
238
+ }
239
+ }
240
+
241
+ return result;
242
+ }
243
+
244
+ function startsNestedMap(lines: string[], startIdx: number): boolean {
245
+ for (let i = startIdx + 1; i < lines.length; i++) {
246
+ const line = lines[i];
247
+ if (line === undefined) break;
248
+ if (line.trim().length === 0) continue;
249
+ if (!line.startsWith(" ") && !line.startsWith("\t")) {
250
+ return false;
251
+ }
252
+ return line.trim().indexOf(":") > 0;
253
+ }
254
+ return false;
255
+ }
256
+
257
+ function parseMetadataValue(
258
+ value: string,
259
+ lines: string[],
260
+ startIdx: number
261
+ ): string | string[] | Record<string, string[]> | undefined {
262
+ const trimmed = value.trim();
263
+ const inlineArray = parseInlineArrayValue(trimmed);
264
+ if (inlineArray) {
265
+ return inlineArray;
266
+ }
267
+
268
+ if (trimmed.length === 0) {
269
+ if (startsNestedMap(lines, startIdx)) {
270
+ const nestedMap = parseNestedMapValue(lines, startIdx);
271
+ return Object.keys(nestedMap).length > 0 ? nestedMap : undefined;
272
+ }
273
+ const blockArray = parseBlockArrayValue(lines, startIdx);
274
+ if (blockArray.length > 0) {
275
+ return blockArray;
276
+ }
277
+ }
278
+
279
+ return unquoteScalar(trimmed);
280
+ }
281
+
120
282
  /**
121
283
  * Parse frontmatter from markdown source.
122
284
  * Returns extracted tags and metadata.
@@ -150,6 +312,9 @@ export function parseFrontmatter(source: string): FrontmatterResult {
150
312
  for (let i = 0; i < lines.length; i++) {
151
313
  const line = lines[i];
152
314
  if (line === undefined) continue;
315
+ if (line.startsWith(" ") || line.startsWith("\t")) {
316
+ continue;
317
+ }
153
318
 
154
319
  // Logseq format: tags:: value
155
320
  const logseqMatch = LOGSEQ_TAGS_REGEX.exec(line);
@@ -170,8 +335,11 @@ export function parseFrontmatter(source: string): FrontmatterResult {
170
335
  if (colonIdx > 0) {
171
336
  const key = line.slice(0, colonIdx).trim();
172
337
  const value = line.slice(colonIdx + 1).trim();
173
- if (key !== "tags" && value.length > 0) {
174
- result.metadata[key] = value;
338
+ if (key !== "tags") {
339
+ const metadataValue = parseMetadataValue(value, lines, i);
340
+ if (metadataValue !== undefined) {
341
+ result.metadata[key] = metadataValue;
342
+ }
175
343
  }
176
344
  }
177
345
  }
@@ -10,12 +10,14 @@ export { defaultChunker, MarkdownChunker } from "./chunker";
10
10
  export { defaultLanguageDetector, SimpleLanguageDetector } from "./language";
11
11
  // Sync service
12
12
  export { defaultSyncService, SyncService } from "./sync";
13
+ export { resolveContentTypeRules, withContentTypeRules } from "./sync-options";
13
14
  // Types
14
15
  export type {
15
16
  ChunkerPort,
16
17
  ChunkOutput,
17
18
  ChunkParams,
18
19
  CollectionSyncResult,
20
+ ContentTypeSource,
19
21
  FileSyncResult,
20
22
  FileSyncStatus,
21
23
  LanguageDetectorPort,
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Sync option helpers.
3
+ *
4
+ * @module src/ingestion/sync-options
5
+ */
6
+
7
+ import type { Config, NormalizedContentTypeRule } from "../config";
8
+ import type { SyncOptions } from "./types";
9
+
10
+ import { fingerprintContentTypeRules, normalizeContentTypes } from "../config";
11
+
12
+ export function resolveContentTypeRules(
13
+ config?: Pick<Config, "contentTypes">
14
+ ): NormalizedContentTypeRule[] {
15
+ return normalizeContentTypes(config?.contentTypes ?? []).rules;
16
+ }
17
+
18
+ export function withContentTypeRules(
19
+ options: SyncOptions = {},
20
+ config?: Pick<Config, "contentTypes">
21
+ ): SyncOptions {
22
+ const rules = options.contentTypeRules ?? resolveContentTypeRules(config);
23
+ return {
24
+ ...options,
25
+ contentTypeRules: rules,
26
+ contentTypeRulesFingerprint:
27
+ options.contentTypeRulesFingerprint ?? fingerprintContentTypeRules(rules),
28
+ };
29
+ }