@ambicuity/kindx 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/CHANGELOG.md +36 -0
- package/LICENSE +21 -0
- package/README.md +578 -0
- package/dist/catalogs.d.ts +137 -0
- package/dist/catalogs.js +349 -0
- package/dist/inference.d.ts +398 -0
- package/dist/inference.js +1131 -0
- package/dist/kindx.d.ts +1 -0
- package/dist/kindx.js +2621 -0
- package/dist/protocol.d.ts +21 -0
- package/dist/protocol.js +666 -0
- package/dist/renderer.d.ts +119 -0
- package/dist/renderer.js +350 -0
- package/dist/repository.d.ts +783 -0
- package/dist/repository.js +2787 -0
- package/dist/runtime.d.ts +33 -0
- package/dist/runtime.js +34 -0
- package/package.json +90 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections configuration management
|
|
3
|
+
*
|
|
4
|
+
* This module manages the YAML-based collection configuration at ~/.config/kindx/index.yml.
|
|
5
|
+
* Collections define which directories to index and their associated contexts.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Context definitions for a collection
|
|
9
|
+
* Key is path prefix (e.g., "/", "/2024", "/Board of Directors")
|
|
10
|
+
* Value is the context description
|
|
11
|
+
*/
|
|
12
|
+
export type ContextMap = Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* A single collection configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface Collection {
|
|
17
|
+
path: string;
|
|
18
|
+
pattern: string;
|
|
19
|
+
ignore?: string[];
|
|
20
|
+
context?: ContextMap;
|
|
21
|
+
update?: string;
|
|
22
|
+
includeByDefault?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The complete configuration file structure
|
|
26
|
+
*/
|
|
27
|
+
export interface CollectionConfig {
|
|
28
|
+
global_context?: string;
|
|
29
|
+
collections: Record<string, Collection>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Collection with its name (for return values)
|
|
33
|
+
*/
|
|
34
|
+
export interface NamedCollection extends Collection {
|
|
35
|
+
name: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Set the current index name for config file lookup
|
|
39
|
+
* Config file will be ~/.config/kindx/{indexName}.yml
|
|
40
|
+
*/
|
|
41
|
+
export declare function setConfigIndexName(name: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Load configuration from ~/.config/kindx/index.yml
|
|
44
|
+
* Returns empty config if file doesn't exist
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadConfig(): CollectionConfig;
|
|
47
|
+
/**
|
|
48
|
+
* Save configuration to ~/.config/kindx/index.yml
|
|
49
|
+
*/
|
|
50
|
+
export declare function saveConfig(config: CollectionConfig): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get a specific collection by name
|
|
53
|
+
* Returns null if not found
|
|
54
|
+
*/
|
|
55
|
+
export declare function getCollection(name: string): NamedCollection | null;
|
|
56
|
+
/**
|
|
57
|
+
* List all collections
|
|
58
|
+
*/
|
|
59
|
+
export declare function listCollections(): NamedCollection[];
|
|
60
|
+
/**
|
|
61
|
+
* Get collections that are included by default in queries
|
|
62
|
+
*/
|
|
63
|
+
export declare function getDefaultCollections(): NamedCollection[];
|
|
64
|
+
/**
|
|
65
|
+
* Get collection names that are included by default
|
|
66
|
+
*/
|
|
67
|
+
export declare function getDefaultCollectionNames(): string[];
|
|
68
|
+
/**
|
|
69
|
+
* Update a collection's settings
|
|
70
|
+
*/
|
|
71
|
+
export declare function updateCollectionSettings(name: string, settings: {
|
|
72
|
+
update?: string | null;
|
|
73
|
+
includeByDefault?: boolean;
|
|
74
|
+
}): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Add or update a collection
|
|
77
|
+
*/
|
|
78
|
+
export declare function addCollection(name: string, path: string, pattern?: string): void;
|
|
79
|
+
/**
|
|
80
|
+
* Remove a collection
|
|
81
|
+
*/
|
|
82
|
+
export declare function removeCollection(name: string): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Rename a collection
|
|
85
|
+
*/
|
|
86
|
+
export declare function renameCollection(oldName: string, newName: string): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Get global context
|
|
89
|
+
*/
|
|
90
|
+
export declare function getGlobalContext(): string | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Set global context
|
|
93
|
+
*/
|
|
94
|
+
export declare function setGlobalContext(context: string | undefined): void;
|
|
95
|
+
/**
|
|
96
|
+
* Get all contexts for a collection
|
|
97
|
+
*/
|
|
98
|
+
export declare function getContexts(collectionName: string): ContextMap | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Add or update a context for a specific path in a collection
|
|
101
|
+
*/
|
|
102
|
+
export declare function addContext(collectionName: string, pathPrefix: string, contextText: string): boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Remove a context from a collection
|
|
105
|
+
*/
|
|
106
|
+
export declare function removeContext(collectionName: string, pathPrefix: string): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* List all contexts across all collections
|
|
109
|
+
*/
|
|
110
|
+
export declare function listAllContexts(): Array<{
|
|
111
|
+
collection: string;
|
|
112
|
+
path: string;
|
|
113
|
+
context: string;
|
|
114
|
+
}>;
|
|
115
|
+
/**
|
|
116
|
+
* Find best matching context for a given collection and path
|
|
117
|
+
* Returns the most specific matching context (longest path prefix match)
|
|
118
|
+
*/
|
|
119
|
+
export declare function findContextForPath(collectionName: string, filePath: string): string | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* Get the config file path (useful for error messages)
|
|
122
|
+
*/
|
|
123
|
+
export declare function getConfigPath(): string;
|
|
124
|
+
/**
|
|
125
|
+
* Check if config file exists
|
|
126
|
+
*/
|
|
127
|
+
export declare function configExists(): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Validate a collection name
|
|
130
|
+
* Collection names must be valid and not contain special characters
|
|
131
|
+
*/
|
|
132
|
+
export declare function isValidCollectionName(name: string): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Update the glob pattern for an existing collection.
|
|
135
|
+
* Returns false if collection does not exist.
|
|
136
|
+
*/
|
|
137
|
+
export declare function updateCollectionPattern(name: string, newPattern: string): boolean;
|
package/dist/catalogs.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections configuration management
|
|
3
|
+
*
|
|
4
|
+
* This module manages the YAML-based collection configuration at ~/.config/kindx/index.yml.
|
|
5
|
+
* Collections define which directories to index and their associated contexts.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import YAML from "yaml";
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Configuration paths
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Current index name (default: "index")
|
|
15
|
+
let currentIndexName = "index";
|
|
16
|
+
/**
|
|
17
|
+
* Set the current index name for config file lookup
|
|
18
|
+
* Config file will be ~/.config/kindx/{indexName}.yml
|
|
19
|
+
*/
|
|
20
|
+
export function setConfigIndexName(name) {
|
|
21
|
+
// Resolve relative paths to absolute paths and sanitize for use as filename
|
|
22
|
+
if (name.includes('/')) {
|
|
23
|
+
const { resolve } = require('path');
|
|
24
|
+
const { cwd } = require('process');
|
|
25
|
+
const absolutePath = resolve(cwd(), name);
|
|
26
|
+
// Replace path separators with underscores to create a valid filename
|
|
27
|
+
currentIndexName = absolutePath.replace(/\//g, '_').replace(/^_/, '');
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
currentIndexName = name;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function getConfigDir() {
|
|
34
|
+
// Allow override via KINDX_CONFIG_DIR for testing
|
|
35
|
+
if (process.env.KINDX_CONFIG_DIR) {
|
|
36
|
+
return process.env.KINDX_CONFIG_DIR;
|
|
37
|
+
}
|
|
38
|
+
// Respect XDG Base Directory specification (consistent with repository.ts)
|
|
39
|
+
if (process.env.XDG_CONFIG_HOME) {
|
|
40
|
+
return join(process.env.XDG_CONFIG_HOME, "kindx");
|
|
41
|
+
}
|
|
42
|
+
return join(homedir(), ".config", "kindx");
|
|
43
|
+
}
|
|
44
|
+
function getConfigFilePath() {
|
|
45
|
+
return join(getConfigDir(), `${currentIndexName}.yml`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Ensure config directory exists
|
|
49
|
+
*/
|
|
50
|
+
function ensureConfigDir() {
|
|
51
|
+
const configDir = getConfigDir();
|
|
52
|
+
if (!existsSync(configDir)) {
|
|
53
|
+
mkdirSync(configDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Core functions
|
|
58
|
+
// ============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Load configuration from ~/.config/kindx/index.yml
|
|
61
|
+
* Returns empty config if file doesn't exist
|
|
62
|
+
*/
|
|
63
|
+
export function loadConfig() {
|
|
64
|
+
const configPath = getConfigFilePath();
|
|
65
|
+
if (!existsSync(configPath)) {
|
|
66
|
+
return { collections: {} };
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const content = readFileSync(configPath, "utf-8");
|
|
70
|
+
const config = YAML.parse(content);
|
|
71
|
+
// Ensure collections object exists
|
|
72
|
+
if (!config.collections) {
|
|
73
|
+
config.collections = {};
|
|
74
|
+
}
|
|
75
|
+
return config;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new Error(`Failed to parse ${configPath}: ${error}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Save configuration to ~/.config/kindx/index.yml
|
|
83
|
+
*/
|
|
84
|
+
export function saveConfig(config) {
|
|
85
|
+
ensureConfigDir();
|
|
86
|
+
const configPath = getConfigFilePath();
|
|
87
|
+
try {
|
|
88
|
+
const yaml = YAML.stringify(config, {
|
|
89
|
+
indent: 2,
|
|
90
|
+
lineWidth: 0, // Don't wrap lines
|
|
91
|
+
});
|
|
92
|
+
writeFileSync(configPath, yaml, "utf-8");
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
throw new Error(`Failed to write ${configPath}: ${error}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get a specific collection by name
|
|
100
|
+
* Returns null if not found
|
|
101
|
+
*/
|
|
102
|
+
export function getCollection(name) {
|
|
103
|
+
const config = loadConfig();
|
|
104
|
+
const collection = config.collections[name];
|
|
105
|
+
if (!collection) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return { name, ...collection };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* List all collections
|
|
112
|
+
*/
|
|
113
|
+
export function listCollections() {
|
|
114
|
+
const config = loadConfig();
|
|
115
|
+
return Object.entries(config.collections).map(([name, collection]) => ({
|
|
116
|
+
name,
|
|
117
|
+
...collection,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Get collections that are included by default in queries
|
|
122
|
+
*/
|
|
123
|
+
export function getDefaultCollections() {
|
|
124
|
+
return listCollections().filter(c => c.includeByDefault !== false);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get collection names that are included by default
|
|
128
|
+
*/
|
|
129
|
+
export function getDefaultCollectionNames() {
|
|
130
|
+
return getDefaultCollections().map(c => c.name);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Update a collection's settings
|
|
134
|
+
*/
|
|
135
|
+
export function updateCollectionSettings(name, settings) {
|
|
136
|
+
const config = loadConfig();
|
|
137
|
+
const collection = config.collections[name];
|
|
138
|
+
if (!collection)
|
|
139
|
+
return false;
|
|
140
|
+
if (settings.update !== undefined) {
|
|
141
|
+
if (settings.update === null) {
|
|
142
|
+
delete collection.update;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
collection.update = settings.update;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (settings.includeByDefault !== undefined) {
|
|
149
|
+
if (settings.includeByDefault === true) {
|
|
150
|
+
// true is default, remove the field
|
|
151
|
+
delete collection.includeByDefault;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
collection.includeByDefault = settings.includeByDefault;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
saveConfig(config);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Add or update a collection
|
|
162
|
+
*/
|
|
163
|
+
export function addCollection(name, path, pattern = "**/*.md") {
|
|
164
|
+
const config = loadConfig();
|
|
165
|
+
config.collections[name] = {
|
|
166
|
+
path,
|
|
167
|
+
pattern,
|
|
168
|
+
context: config.collections[name]?.context, // Preserve existing context
|
|
169
|
+
};
|
|
170
|
+
saveConfig(config);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Remove a collection
|
|
174
|
+
*/
|
|
175
|
+
export function removeCollection(name) {
|
|
176
|
+
const config = loadConfig();
|
|
177
|
+
if (!config.collections[name]) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
delete config.collections[name];
|
|
181
|
+
saveConfig(config);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Rename a collection
|
|
186
|
+
*/
|
|
187
|
+
export function renameCollection(oldName, newName) {
|
|
188
|
+
const config = loadConfig();
|
|
189
|
+
if (!config.collections[oldName]) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
if (config.collections[newName]) {
|
|
193
|
+
throw new Error(`Collection '${newName}' already exists`);
|
|
194
|
+
}
|
|
195
|
+
config.collections[newName] = config.collections[oldName];
|
|
196
|
+
delete config.collections[oldName];
|
|
197
|
+
saveConfig(config);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Context management
|
|
202
|
+
// ============================================================================
|
|
203
|
+
/**
|
|
204
|
+
* Get global context
|
|
205
|
+
*/
|
|
206
|
+
export function getGlobalContext() {
|
|
207
|
+
const config = loadConfig();
|
|
208
|
+
return config.global_context;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Set global context
|
|
212
|
+
*/
|
|
213
|
+
export function setGlobalContext(context) {
|
|
214
|
+
const config = loadConfig();
|
|
215
|
+
config.global_context = context;
|
|
216
|
+
saveConfig(config);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Get all contexts for a collection
|
|
220
|
+
*/
|
|
221
|
+
export function getContexts(collectionName) {
|
|
222
|
+
const collection = getCollection(collectionName);
|
|
223
|
+
return collection?.context;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Add or update a context for a specific path in a collection
|
|
227
|
+
*/
|
|
228
|
+
export function addContext(collectionName, pathPrefix, contextText) {
|
|
229
|
+
const config = loadConfig();
|
|
230
|
+
const collection = config.collections[collectionName];
|
|
231
|
+
if (!collection) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
if (!collection.context) {
|
|
235
|
+
collection.context = {};
|
|
236
|
+
}
|
|
237
|
+
collection.context[pathPrefix] = contextText;
|
|
238
|
+
saveConfig(config);
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Remove a context from a collection
|
|
243
|
+
*/
|
|
244
|
+
export function removeContext(collectionName, pathPrefix) {
|
|
245
|
+
const config = loadConfig();
|
|
246
|
+
const collection = config.collections[collectionName];
|
|
247
|
+
if (!collection?.context?.[pathPrefix]) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
delete collection.context[pathPrefix];
|
|
251
|
+
// Remove empty context object
|
|
252
|
+
if (Object.keys(collection.context).length === 0) {
|
|
253
|
+
delete collection.context;
|
|
254
|
+
}
|
|
255
|
+
saveConfig(config);
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* List all contexts across all collections
|
|
260
|
+
*/
|
|
261
|
+
export function listAllContexts() {
|
|
262
|
+
const config = loadConfig();
|
|
263
|
+
const results = [];
|
|
264
|
+
// Add global context if present
|
|
265
|
+
if (config.global_context) {
|
|
266
|
+
results.push({
|
|
267
|
+
collection: "*",
|
|
268
|
+
path: "/",
|
|
269
|
+
context: config.global_context,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// Add collection contexts
|
|
273
|
+
for (const [name, collection] of Object.entries(config.collections)) {
|
|
274
|
+
if (collection.context) {
|
|
275
|
+
for (const [path, context] of Object.entries(collection.context)) {
|
|
276
|
+
results.push({
|
|
277
|
+
collection: name,
|
|
278
|
+
path,
|
|
279
|
+
context,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return results;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Find best matching context for a given collection and path
|
|
288
|
+
* Returns the most specific matching context (longest path prefix match)
|
|
289
|
+
*/
|
|
290
|
+
export function findContextForPath(collectionName, filePath) {
|
|
291
|
+
const config = loadConfig();
|
|
292
|
+
const collection = config.collections[collectionName];
|
|
293
|
+
if (!collection?.context) {
|
|
294
|
+
return config.global_context;
|
|
295
|
+
}
|
|
296
|
+
// Find all matching prefixes
|
|
297
|
+
const matches = [];
|
|
298
|
+
for (const [prefix, context] of Object.entries(collection.context)) {
|
|
299
|
+
// Normalize paths for comparison
|
|
300
|
+
const normalizedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
|
|
301
|
+
const normalizedPrefix = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
302
|
+
if (normalizedPath.startsWith(normalizedPrefix)) {
|
|
303
|
+
matches.push({ prefix: normalizedPrefix, context });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Return most specific match (longest prefix)
|
|
307
|
+
if (matches.length > 0) {
|
|
308
|
+
matches.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
309
|
+
return matches[0].context;
|
|
310
|
+
}
|
|
311
|
+
// Fallback to global context
|
|
312
|
+
return config.global_context;
|
|
313
|
+
}
|
|
314
|
+
// ============================================================================
|
|
315
|
+
// Utility functions
|
|
316
|
+
// ============================================================================
|
|
317
|
+
/**
|
|
318
|
+
* Get the config file path (useful for error messages)
|
|
319
|
+
*/
|
|
320
|
+
export function getConfigPath() {
|
|
321
|
+
return getConfigFilePath();
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Check if config file exists
|
|
325
|
+
*/
|
|
326
|
+
export function configExists() {
|
|
327
|
+
return existsSync(getConfigFilePath());
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Validate a collection name
|
|
331
|
+
* Collection names must be valid and not contain special characters
|
|
332
|
+
*/
|
|
333
|
+
export function isValidCollectionName(name) {
|
|
334
|
+
// Allow alphanumeric, hyphens, underscores
|
|
335
|
+
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Update the glob pattern for an existing collection.
|
|
339
|
+
* Returns false if collection does not exist.
|
|
340
|
+
*/
|
|
341
|
+
export function updateCollectionPattern(name, newPattern) {
|
|
342
|
+
const config = loadConfig();
|
|
343
|
+
const collection = config.collections[name];
|
|
344
|
+
if (!collection)
|
|
345
|
+
return false;
|
|
346
|
+
collection.pattern = newPattern;
|
|
347
|
+
saveConfig(config);
|
|
348
|
+
return true;
|
|
349
|
+
}
|