org-qmd 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 +529 -0
- package/LICENSE +21 -0
- package/README.md +917 -0
- package/bin/qmd +32 -0
- package/dist/ast.d.ts +64 -0
- package/dist/ast.js +324 -0
- package/dist/cli/formatter.d.ts +120 -0
- package/dist/cli/formatter.js +350 -0
- package/dist/cli/qmd.d.ts +1 -0
- package/dist/cli/qmd.js +2820 -0
- package/dist/collections.d.ts +146 -0
- package/dist/collections.js +385 -0
- package/dist/db.d.ts +41 -0
- package/dist/db.js +75 -0
- package/dist/embedded-skills.d.ts +6 -0
- package/dist/embedded-skills.js +14 -0
- package/dist/index.d.ts +226 -0
- package/dist/index.js +234 -0
- package/dist/llm.d.ts +406 -0
- package/dist/llm.js +1174 -0
- package/dist/maintenance.d.ts +23 -0
- package/dist/maintenance.js +37 -0
- package/dist/mcp/server.d.ts +21 -0
- package/dist/mcp/server.js +653 -0
- package/dist/store.d.ts +993 -0
- package/dist/store.js +3806 -0
- package/package.json +101 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections configuration management
|
|
3
|
+
*
|
|
4
|
+
* This module manages the YAML-based collection configuration at ~/.config/qmd/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 config source for SDK mode.
|
|
39
|
+
* - File path: load/save from a specific YAML file
|
|
40
|
+
* - Inline config: use an in-memory CollectionConfig (saveConfig updates in place, no file I/O)
|
|
41
|
+
* - undefined: reset to default file-based config
|
|
42
|
+
*/
|
|
43
|
+
export declare function setConfigSource(source?: {
|
|
44
|
+
configPath?: string;
|
|
45
|
+
config?: CollectionConfig;
|
|
46
|
+
}): void;
|
|
47
|
+
/**
|
|
48
|
+
* Set the current index name for config file lookup
|
|
49
|
+
* Config file will be ~/.config/qmd/{indexName}.yml
|
|
50
|
+
*/
|
|
51
|
+
export declare function setConfigIndexName(name: string): void;
|
|
52
|
+
/**
|
|
53
|
+
* Load configuration from the configured source.
|
|
54
|
+
* - Inline config: returns the in-memory object directly
|
|
55
|
+
* - File-based: reads from YAML file (default ~/.config/qmd/index.yml)
|
|
56
|
+
* Returns empty config if file doesn't exist
|
|
57
|
+
*/
|
|
58
|
+
export declare function loadConfig(): CollectionConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Save configuration to the configured source.
|
|
61
|
+
* - Inline config: updates the in-memory object (no file I/O)
|
|
62
|
+
* - File-based: writes to YAML file (default ~/.config/qmd/index.yml)
|
|
63
|
+
*/
|
|
64
|
+
export declare function saveConfig(config: CollectionConfig): void;
|
|
65
|
+
/**
|
|
66
|
+
* Get a specific collection by name
|
|
67
|
+
* Returns null if not found
|
|
68
|
+
*/
|
|
69
|
+
export declare function getCollection(name: string): NamedCollection | null;
|
|
70
|
+
/**
|
|
71
|
+
* List all collections
|
|
72
|
+
*/
|
|
73
|
+
export declare function listCollections(): NamedCollection[];
|
|
74
|
+
/**
|
|
75
|
+
* Get collections that are included by default in queries
|
|
76
|
+
*/
|
|
77
|
+
export declare function getDefaultCollections(): NamedCollection[];
|
|
78
|
+
/**
|
|
79
|
+
* Get collection names that are included by default
|
|
80
|
+
*/
|
|
81
|
+
export declare function getDefaultCollectionNames(): string[];
|
|
82
|
+
/**
|
|
83
|
+
* Update a collection's settings
|
|
84
|
+
*/
|
|
85
|
+
export declare function updateCollectionSettings(name: string, settings: {
|
|
86
|
+
update?: string | null;
|
|
87
|
+
includeByDefault?: boolean;
|
|
88
|
+
}): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Add or update a collection
|
|
91
|
+
*/
|
|
92
|
+
export declare function addCollection(name: string, path: string, pattern?: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* Remove a collection
|
|
95
|
+
*/
|
|
96
|
+
export declare function removeCollection(name: string): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Rename a collection
|
|
99
|
+
*/
|
|
100
|
+
export declare function renameCollection(oldName: string, newName: string): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Get global context
|
|
103
|
+
*/
|
|
104
|
+
export declare function getGlobalContext(): string | undefined;
|
|
105
|
+
/**
|
|
106
|
+
* Set global context
|
|
107
|
+
*/
|
|
108
|
+
export declare function setGlobalContext(context: string | undefined): void;
|
|
109
|
+
/**
|
|
110
|
+
* Get all contexts for a collection
|
|
111
|
+
*/
|
|
112
|
+
export declare function getContexts(collectionName: string): ContextMap | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Add or update a context for a specific path in a collection
|
|
115
|
+
*/
|
|
116
|
+
export declare function addContext(collectionName: string, pathPrefix: string, contextText: string): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Remove a context from a collection
|
|
119
|
+
*/
|
|
120
|
+
export declare function removeContext(collectionName: string, pathPrefix: string): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* List all contexts across all collections
|
|
123
|
+
*/
|
|
124
|
+
export declare function listAllContexts(): Array<{
|
|
125
|
+
collection: string;
|
|
126
|
+
path: string;
|
|
127
|
+
context: string;
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* Find best matching context for a given collection and path
|
|
131
|
+
* Returns the most specific matching context (longest path prefix match)
|
|
132
|
+
*/
|
|
133
|
+
export declare function findContextForPath(collectionName: string, filePath: string): string | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Get the config file path (useful for error messages)
|
|
136
|
+
*/
|
|
137
|
+
export declare function getConfigPath(): string;
|
|
138
|
+
/**
|
|
139
|
+
* Check if config file exists
|
|
140
|
+
*/
|
|
141
|
+
export declare function configExists(): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Validate a collection name
|
|
144
|
+
* Collection names must be valid and not contain special characters
|
|
145
|
+
*/
|
|
146
|
+
export declare function isValidCollectionName(name: string): boolean;
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collections configuration management
|
|
3
|
+
*
|
|
4
|
+
* This module manages the YAML-based collection configuration at ~/.config/qmd/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, dirname } 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
|
+
// SDK mode: optional in-memory config or custom config path
|
|
17
|
+
let configSource = { type: 'file' };
|
|
18
|
+
/**
|
|
19
|
+
* Set the config source for SDK mode.
|
|
20
|
+
* - File path: load/save from a specific YAML file
|
|
21
|
+
* - Inline config: use an in-memory CollectionConfig (saveConfig updates in place, no file I/O)
|
|
22
|
+
* - undefined: reset to default file-based config
|
|
23
|
+
*/
|
|
24
|
+
export function setConfigSource(source) {
|
|
25
|
+
if (!source) {
|
|
26
|
+
configSource = { type: 'file' };
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (source.config) {
|
|
30
|
+
// Ensure collections object exists
|
|
31
|
+
if (!source.config.collections) {
|
|
32
|
+
source.config.collections = {};
|
|
33
|
+
}
|
|
34
|
+
configSource = { type: 'inline', config: source.config };
|
|
35
|
+
}
|
|
36
|
+
else if (source.configPath) {
|
|
37
|
+
configSource = { type: 'file', path: source.configPath };
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
configSource = { type: 'file' };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Set the current index name for config file lookup
|
|
45
|
+
* Config file will be ~/.config/qmd/{indexName}.yml
|
|
46
|
+
*/
|
|
47
|
+
export function setConfigIndexName(name) {
|
|
48
|
+
// Resolve relative paths to absolute paths and sanitize for use as filename
|
|
49
|
+
if (name.includes('/')) {
|
|
50
|
+
const { resolve } = require('path');
|
|
51
|
+
const { cwd } = require('process');
|
|
52
|
+
const absolutePath = resolve(cwd(), name);
|
|
53
|
+
// Replace path separators with underscores to create a valid filename
|
|
54
|
+
currentIndexName = absolutePath.replace(/\//g, '_').replace(/^_/, '');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
currentIndexName = name;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function getConfigDir() {
|
|
61
|
+
// Allow override via QMD_CONFIG_DIR for testing
|
|
62
|
+
if (process.env.QMD_CONFIG_DIR) {
|
|
63
|
+
return process.env.QMD_CONFIG_DIR;
|
|
64
|
+
}
|
|
65
|
+
// Respect XDG Base Directory specification (consistent with store.ts)
|
|
66
|
+
if (process.env.XDG_CONFIG_HOME) {
|
|
67
|
+
return join(process.env.XDG_CONFIG_HOME, "qmd");
|
|
68
|
+
}
|
|
69
|
+
return join(homedir(), ".config", "qmd");
|
|
70
|
+
}
|
|
71
|
+
function getConfigFilePath() {
|
|
72
|
+
return join(getConfigDir(), `${currentIndexName}.yml`);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Ensure config directory exists
|
|
76
|
+
*/
|
|
77
|
+
function ensureConfigDir() {
|
|
78
|
+
const configDir = getConfigDir();
|
|
79
|
+
if (!existsSync(configDir)) {
|
|
80
|
+
mkdirSync(configDir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Core functions
|
|
85
|
+
// ============================================================================
|
|
86
|
+
/**
|
|
87
|
+
* Load configuration from the configured source.
|
|
88
|
+
* - Inline config: returns the in-memory object directly
|
|
89
|
+
* - File-based: reads from YAML file (default ~/.config/qmd/index.yml)
|
|
90
|
+
* Returns empty config if file doesn't exist
|
|
91
|
+
*/
|
|
92
|
+
export function loadConfig() {
|
|
93
|
+
// SDK inline config mode
|
|
94
|
+
if (configSource.type === 'inline') {
|
|
95
|
+
return configSource.config;
|
|
96
|
+
}
|
|
97
|
+
// File-based config (SDK custom path or default)
|
|
98
|
+
const configPath = configSource.path || getConfigFilePath();
|
|
99
|
+
if (!existsSync(configPath)) {
|
|
100
|
+
return { collections: {} };
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const content = readFileSync(configPath, "utf-8");
|
|
104
|
+
const config = YAML.parse(content);
|
|
105
|
+
// Ensure collections object exists
|
|
106
|
+
if (!config.collections) {
|
|
107
|
+
config.collections = {};
|
|
108
|
+
}
|
|
109
|
+
return config;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new Error(`Failed to parse ${configPath}: ${error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Save configuration to the configured source.
|
|
117
|
+
* - Inline config: updates the in-memory object (no file I/O)
|
|
118
|
+
* - File-based: writes to YAML file (default ~/.config/qmd/index.yml)
|
|
119
|
+
*/
|
|
120
|
+
export function saveConfig(config) {
|
|
121
|
+
// SDK inline config mode: update in place, no file I/O
|
|
122
|
+
if (configSource.type === 'inline') {
|
|
123
|
+
configSource.config = config;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const configPath = configSource.path || getConfigFilePath();
|
|
127
|
+
const configDir = dirname(configPath);
|
|
128
|
+
if (!existsSync(configDir)) {
|
|
129
|
+
mkdirSync(configDir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const yaml = YAML.stringify(config, {
|
|
133
|
+
indent: 2,
|
|
134
|
+
lineWidth: 0, // Don't wrap lines
|
|
135
|
+
});
|
|
136
|
+
writeFileSync(configPath, yaml, "utf-8");
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new Error(`Failed to write ${configPath}: ${error}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get a specific collection by name
|
|
144
|
+
* Returns null if not found
|
|
145
|
+
*/
|
|
146
|
+
export function getCollection(name) {
|
|
147
|
+
const config = loadConfig();
|
|
148
|
+
const collection = config.collections[name];
|
|
149
|
+
if (!collection) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
return { name, ...collection };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* List all collections
|
|
156
|
+
*/
|
|
157
|
+
export function listCollections() {
|
|
158
|
+
const config = loadConfig();
|
|
159
|
+
return Object.entries(config.collections).map(([name, collection]) => ({
|
|
160
|
+
name,
|
|
161
|
+
...collection,
|
|
162
|
+
}));
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get collections that are included by default in queries
|
|
166
|
+
*/
|
|
167
|
+
export function getDefaultCollections() {
|
|
168
|
+
return listCollections().filter(c => c.includeByDefault !== false);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get collection names that are included by default
|
|
172
|
+
*/
|
|
173
|
+
export function getDefaultCollectionNames() {
|
|
174
|
+
return getDefaultCollections().map(c => c.name);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Update a collection's settings
|
|
178
|
+
*/
|
|
179
|
+
export function updateCollectionSettings(name, settings) {
|
|
180
|
+
const config = loadConfig();
|
|
181
|
+
const collection = config.collections[name];
|
|
182
|
+
if (!collection)
|
|
183
|
+
return false;
|
|
184
|
+
if (settings.update !== undefined) {
|
|
185
|
+
if (settings.update === null) {
|
|
186
|
+
delete collection.update;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
collection.update = settings.update;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (settings.includeByDefault !== undefined) {
|
|
193
|
+
if (settings.includeByDefault === true) {
|
|
194
|
+
// true is default, remove the field
|
|
195
|
+
delete collection.includeByDefault;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
collection.includeByDefault = settings.includeByDefault;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
saveConfig(config);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Add or update a collection
|
|
206
|
+
*/
|
|
207
|
+
export function addCollection(name, path, pattern = "**/*.{md,org}") {
|
|
208
|
+
const config = loadConfig();
|
|
209
|
+
config.collections[name] = {
|
|
210
|
+
path,
|
|
211
|
+
pattern,
|
|
212
|
+
context: config.collections[name]?.context, // Preserve existing context
|
|
213
|
+
};
|
|
214
|
+
saveConfig(config);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Remove a collection
|
|
218
|
+
*/
|
|
219
|
+
export function removeCollection(name) {
|
|
220
|
+
const config = loadConfig();
|
|
221
|
+
if (!config.collections[name]) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
delete config.collections[name];
|
|
225
|
+
saveConfig(config);
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Rename a collection
|
|
230
|
+
*/
|
|
231
|
+
export function renameCollection(oldName, newName) {
|
|
232
|
+
const config = loadConfig();
|
|
233
|
+
if (!config.collections[oldName]) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
if (config.collections[newName]) {
|
|
237
|
+
throw new Error(`Collection '${newName}' already exists`);
|
|
238
|
+
}
|
|
239
|
+
config.collections[newName] = config.collections[oldName];
|
|
240
|
+
delete config.collections[oldName];
|
|
241
|
+
saveConfig(config);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// Context management
|
|
246
|
+
// ============================================================================
|
|
247
|
+
/**
|
|
248
|
+
* Get global context
|
|
249
|
+
*/
|
|
250
|
+
export function getGlobalContext() {
|
|
251
|
+
const config = loadConfig();
|
|
252
|
+
return config.global_context;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Set global context
|
|
256
|
+
*/
|
|
257
|
+
export function setGlobalContext(context) {
|
|
258
|
+
const config = loadConfig();
|
|
259
|
+
config.global_context = context;
|
|
260
|
+
saveConfig(config);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Get all contexts for a collection
|
|
264
|
+
*/
|
|
265
|
+
export function getContexts(collectionName) {
|
|
266
|
+
const collection = getCollection(collectionName);
|
|
267
|
+
return collection?.context;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Add or update a context for a specific path in a collection
|
|
271
|
+
*/
|
|
272
|
+
export function addContext(collectionName, pathPrefix, contextText) {
|
|
273
|
+
const config = loadConfig();
|
|
274
|
+
const collection = config.collections[collectionName];
|
|
275
|
+
if (!collection) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (!collection.context) {
|
|
279
|
+
collection.context = {};
|
|
280
|
+
}
|
|
281
|
+
collection.context[pathPrefix] = contextText;
|
|
282
|
+
saveConfig(config);
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Remove a context from a collection
|
|
287
|
+
*/
|
|
288
|
+
export function removeContext(collectionName, pathPrefix) {
|
|
289
|
+
const config = loadConfig();
|
|
290
|
+
const collection = config.collections[collectionName];
|
|
291
|
+
if (!collection?.context?.[pathPrefix]) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
delete collection.context[pathPrefix];
|
|
295
|
+
// Remove empty context object
|
|
296
|
+
if (Object.keys(collection.context).length === 0) {
|
|
297
|
+
delete collection.context;
|
|
298
|
+
}
|
|
299
|
+
saveConfig(config);
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* List all contexts across all collections
|
|
304
|
+
*/
|
|
305
|
+
export function listAllContexts() {
|
|
306
|
+
const config = loadConfig();
|
|
307
|
+
const results = [];
|
|
308
|
+
// Add global context if present
|
|
309
|
+
if (config.global_context) {
|
|
310
|
+
results.push({
|
|
311
|
+
collection: "*",
|
|
312
|
+
path: "/",
|
|
313
|
+
context: config.global_context,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
// Add collection contexts
|
|
317
|
+
for (const [name, collection] of Object.entries(config.collections)) {
|
|
318
|
+
if (collection.context) {
|
|
319
|
+
for (const [path, context] of Object.entries(collection.context)) {
|
|
320
|
+
results.push({
|
|
321
|
+
collection: name,
|
|
322
|
+
path,
|
|
323
|
+
context,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return results;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Find best matching context for a given collection and path
|
|
332
|
+
* Returns the most specific matching context (longest path prefix match)
|
|
333
|
+
*/
|
|
334
|
+
export function findContextForPath(collectionName, filePath) {
|
|
335
|
+
const config = loadConfig();
|
|
336
|
+
const collection = config.collections[collectionName];
|
|
337
|
+
if (!collection?.context) {
|
|
338
|
+
return config.global_context;
|
|
339
|
+
}
|
|
340
|
+
// Find all matching prefixes
|
|
341
|
+
const matches = [];
|
|
342
|
+
for (const [prefix, context] of Object.entries(collection.context)) {
|
|
343
|
+
// Normalize paths for comparison
|
|
344
|
+
const normalizedPath = filePath.startsWith("/") ? filePath : `/${filePath}`;
|
|
345
|
+
const normalizedPrefix = prefix.startsWith("/") ? prefix : `/${prefix}`;
|
|
346
|
+
if (normalizedPath.startsWith(normalizedPrefix)) {
|
|
347
|
+
matches.push({ prefix: normalizedPrefix, context });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Return most specific match (longest prefix)
|
|
351
|
+
if (matches.length > 0) {
|
|
352
|
+
matches.sort((a, b) => b.prefix.length - a.prefix.length);
|
|
353
|
+
return matches[0].context;
|
|
354
|
+
}
|
|
355
|
+
// Fallback to global context
|
|
356
|
+
return config.global_context;
|
|
357
|
+
}
|
|
358
|
+
// ============================================================================
|
|
359
|
+
// Utility functions
|
|
360
|
+
// ============================================================================
|
|
361
|
+
/**
|
|
362
|
+
* Get the config file path (useful for error messages)
|
|
363
|
+
*/
|
|
364
|
+
export function getConfigPath() {
|
|
365
|
+
if (configSource.type === 'inline')
|
|
366
|
+
return '<inline>';
|
|
367
|
+
return configSource.path || getConfigFilePath();
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Check if config file exists
|
|
371
|
+
*/
|
|
372
|
+
export function configExists() {
|
|
373
|
+
if (configSource.type === 'inline')
|
|
374
|
+
return true;
|
|
375
|
+
const path = configSource.path || getConfigFilePath();
|
|
376
|
+
return existsSync(path);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Validate a collection name
|
|
380
|
+
* Collection names must be valid and not contain special characters
|
|
381
|
+
*/
|
|
382
|
+
export function isValidCollectionName(name) {
|
|
383
|
+
// Allow alphanumeric, hyphens, underscores
|
|
384
|
+
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
385
|
+
}
|
package/dist/db.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.ts - Cross-runtime SQLite compatibility layer
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified Database export that works under both Bun (bun:sqlite)
|
|
5
|
+
* and Node.js (better-sqlite3). The APIs are nearly identical — the main
|
|
6
|
+
* difference is the import path.
|
|
7
|
+
*
|
|
8
|
+
* On macOS, Apple's system SQLite is compiled with SQLITE_OMIT_LOAD_EXTENSION,
|
|
9
|
+
* which prevents loading native extensions like sqlite-vec. When running under
|
|
10
|
+
* Bun we call Database.setCustomSQLite() to swap in Homebrew's full-featured
|
|
11
|
+
* SQLite build before creating any database instances.
|
|
12
|
+
*/
|
|
13
|
+
export declare const isBun: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Open a SQLite database. Works with both bun:sqlite and better-sqlite3.
|
|
16
|
+
*/
|
|
17
|
+
export declare function openDatabase(path: string): Database;
|
|
18
|
+
/**
|
|
19
|
+
* Common subset of the Database interface used throughout QMD.
|
|
20
|
+
*/
|
|
21
|
+
export interface Database {
|
|
22
|
+
exec(sql: string): void;
|
|
23
|
+
prepare(sql: string): Statement;
|
|
24
|
+
loadExtension(path: string): void;
|
|
25
|
+
close(): void;
|
|
26
|
+
}
|
|
27
|
+
export interface Statement {
|
|
28
|
+
run(...params: any[]): {
|
|
29
|
+
changes: number;
|
|
30
|
+
lastInsertRowid: number | bigint;
|
|
31
|
+
};
|
|
32
|
+
get(...params: any[]): any;
|
|
33
|
+
all(...params: any[]): any[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Load the sqlite-vec extension into a database.
|
|
37
|
+
*
|
|
38
|
+
* Throws with platform-specific fix instructions when the extension is
|
|
39
|
+
* unavailable.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadSqliteVec(db: Database): void;
|
package/dist/db.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* db.ts - Cross-runtime SQLite compatibility layer
|
|
3
|
+
*
|
|
4
|
+
* Provides a unified Database export that works under both Bun (bun:sqlite)
|
|
5
|
+
* and Node.js (better-sqlite3). The APIs are nearly identical — the main
|
|
6
|
+
* difference is the import path.
|
|
7
|
+
*
|
|
8
|
+
* On macOS, Apple's system SQLite is compiled with SQLITE_OMIT_LOAD_EXTENSION,
|
|
9
|
+
* which prevents loading native extensions like sqlite-vec. When running under
|
|
10
|
+
* Bun we call Database.setCustomSQLite() to swap in Homebrew's full-featured
|
|
11
|
+
* SQLite build before creating any database instances.
|
|
12
|
+
*/
|
|
13
|
+
export const isBun = typeof globalThis.Bun !== "undefined";
|
|
14
|
+
let _Database;
|
|
15
|
+
let _sqliteVecLoad;
|
|
16
|
+
if (isBun) {
|
|
17
|
+
// Dynamic string prevents tsc from resolving bun:sqlite on Node.js builds
|
|
18
|
+
const bunSqlite = "bun:" + "sqlite";
|
|
19
|
+
const BunDatabase = (await import(/* @vite-ignore */ bunSqlite)).Database;
|
|
20
|
+
// See: https://bun.com/docs/runtime/sqlite#setcustomsqlite
|
|
21
|
+
if (process.platform === "darwin") {
|
|
22
|
+
const homebrewPaths = [
|
|
23
|
+
"/opt/homebrew/opt/sqlite/lib/libsqlite3.dylib", // Apple Silicon
|
|
24
|
+
"/usr/local/opt/sqlite/lib/libsqlite3.dylib", // Intel
|
|
25
|
+
];
|
|
26
|
+
for (const p of homebrewPaths) {
|
|
27
|
+
try {
|
|
28
|
+
BunDatabase.setCustomSQLite(p);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
_Database = BunDatabase;
|
|
35
|
+
// setCustomSQLite may have silently failed — test that extensions actually work.
|
|
36
|
+
try {
|
|
37
|
+
const { getLoadablePath } = await import("sqlite-vec");
|
|
38
|
+
const vecPath = getLoadablePath();
|
|
39
|
+
const testDb = new BunDatabase(":memory:");
|
|
40
|
+
testDb.loadExtension(vecPath);
|
|
41
|
+
testDb.close();
|
|
42
|
+
_sqliteVecLoad = (db) => db.loadExtension(vecPath);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Vector search won't work, but BM25 and other operations are unaffected.
|
|
46
|
+
_sqliteVecLoad = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
_Database = (await import("better-sqlite3")).default;
|
|
51
|
+
const sqliteVec = await import("sqlite-vec");
|
|
52
|
+
_sqliteVecLoad = (db) => sqliteVec.load(db);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Open a SQLite database. Works with both bun:sqlite and better-sqlite3.
|
|
56
|
+
*/
|
|
57
|
+
export function openDatabase(path) {
|
|
58
|
+
return new _Database(path);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Load the sqlite-vec extension into a database.
|
|
62
|
+
*
|
|
63
|
+
* Throws with platform-specific fix instructions when the extension is
|
|
64
|
+
* unavailable.
|
|
65
|
+
*/
|
|
66
|
+
export function loadSqliteVec(db) {
|
|
67
|
+
if (!_sqliteVecLoad) {
|
|
68
|
+
const hint = isBun && process.platform === "darwin"
|
|
69
|
+
? "On macOS with Bun, install Homebrew SQLite: brew install sqlite\n" +
|
|
70
|
+
"Or install qmd with npm instead: npm install -g @tobilu/qmd"
|
|
71
|
+
: "Ensure the sqlite-vec native module is installed correctly.";
|
|
72
|
+
throw new Error(`sqlite-vec extension is unavailable. ${hint}`);
|
|
73
|
+
}
|
|
74
|
+
_sqliteVecLoad(db);
|
|
75
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated from skills/qmd source files. Keep this in sync when updating the packaged skill.
|
|
2
|
+
const EMBEDDED_QMD_SKILL_BASE64 = {
|
|
3
|
+
"SKILL.md": "LS0tCm5hbWU6IHFtZApkZXNjcmlwdGlvbjogU2VhcmNoIG1hcmtkb3duIGtub3dsZWRnZSBiYXNlcywgbm90ZXMsIGFuZCBkb2N1bWVudGF0aW9uIHVzaW5nIFFNRC4gVXNlIHdoZW4gdXNlcnMgYXNrIHRvIHNlYXJjaCBub3RlcywgZmluZCBkb2N1bWVudHMsIG9yIGxvb2sgdXAgaW5mb3JtYXRpb24uCmxpY2Vuc2U6IE1JVApjb21wYXRpYmlsaXR5OiBSZXF1aXJlcyBxbWQgQ0xJIG9yIE1DUCBzZXJ2ZXIuIEluc3RhbGwgdmlhIGBucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZGAuCm1ldGFkYXRhOgogIGF1dGhvcjogdG9iaQogIHZlcnNpb246ICIyLjAuMCIKYWxsb3dlZC10b29sczogQmFzaChxbWQ6KiksIG1jcF9fcW1kX18qCi0tLQoKIyBRTUQgLSBRdWljayBNYXJrZG93biBTZWFyY2gKCkxvY2FsIHNlYXJjaCBlbmdpbmUgZm9yIG1hcmtkb3duIGNvbnRlbnQuCgojIyBTdGF0dXMKCiFgcW1kIHN0YXR1cyAyPi9kZXYvbnVsbCB8fCBlY2hvICJOb3QgaW5zdGFsbGVkOiBucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZCJgCgojIyBNQ1A6IGBxdWVyeWAKCmBgYGpzb24KewogICJzZWFyY2hlcyI6IFsKICAgIHsgInR5cGUiOiAibGV4IiwgInF1ZXJ5IjogIkNBUCB0aGVvcmVtIGNvbnNpc3RlbmN5IiB9LAogICAgeyAidHlwZSI6ICJ2ZWMiLCAicXVlcnkiOiAidHJhZGVvZmYgYmV0d2VlbiBjb25zaXN0ZW5jeSBhbmQgYXZhaWxhYmlsaXR5IiB9CiAgXSwKICAiY29sbGVjdGlvbnMiOiBbImRvY3MiXSwKICAibGltaXQiOiAxMAp9CmBgYAoKIyMjIFF1ZXJ5IFR5cGVzCgp8IFR5cGUgfCBNZXRob2QgfCBJbnB1dCB8CnwtLS0tLS18LS0tLS0tLS18LS0tLS0tLXwKfCBgbGV4YCB8IEJNMjUgfCBLZXl3b3JkcyDigJQgZXhhY3QgdGVybXMsIG5hbWVzLCBjb2RlIHwKfCBgdmVjYCB8IFZlY3RvciB8IFF1ZXN0aW9uIOKAlCBuYXR1cmFsIGxhbmd1YWdlIHwKfCBgaHlkZWAgfCBWZWN0b3IgfCBBbnN3ZXIg4oCUIGh5cG90aGV0aWNhbCByZXN1bHQgKDUwLTEwMCB3b3JkcykgfAoKIyMjIFdyaXRpbmcgR29vZCBRdWVyaWVzCgoqKmxleCAoa2V5d29yZCkqKgotIDItNSB0ZXJtcywgbm8gZmlsbGVyIHdvcmRzCi0gRXhhY3QgcGhyYXNlOiBgImNvbm5lY3Rpb24gcG9vbCJgIChxdW90ZWQpCi0gRXhjbHVkZSB0ZXJtczogYHBlcmZvcm1hbmNlIC1zcG9ydHNgIChtaW51cyBwcmVmaXgpCi0gQ29kZSBpZGVudGlmaWVycyB3b3JrOiBgaGFuZGxlRXJyb3IgYXN5bmNgCgoqKnZlYyAoc2VtYW50aWMpKioKLSBGdWxsIG5hdHVyYWwgbGFuZ3VhZ2UgcXVlc3Rpb24KLSBCZSBzcGVjaWZpYzogYCJob3cgZG9lcyB0aGUgcmF0ZSBsaW1pdGVyIGhhbmRsZSBidXJzdCB0cmFmZmljImAKLSBJbmNsdWRlIGNvbnRleHQ6IGAiaW4gdGhlIHBheW1lbnQgc2VydmljZSwgaG93IGFyZSByZWZ1bmRzIHByb2Nlc3NlZCJgCgoqKmh5ZGUgKGh5cG90aGV0aWNhbCBkb2N1bWVudCkqKgotIFdyaXRlIDUwLTEwMCB3b3JkcyBvZiB3aGF0IHRoZSAqYW5zd2VyKiBsb29rcyBsaWtlCi0gVXNlIHRoZSB2b2NhYnVsYXJ5IHlvdSBleHBlY3QgaW4gdGhlIHJlc3VsdAoKKipleHBhbmQgKGF1dG8tZXhwYW5kKSoqCi0gVXNlIGEgc2luZ2xlLWxpbmUgcXVlcnkgKGltcGxpY2l0KSBvciBgZXhwYW5kOiBxdWVzdGlvbmAgb24gaXRzIG93biBsaW5lCi0gTGV0cyB0aGUgbG9jYWwgTExNIGdlbmVyYXRlIGxleC92ZWMvaHlkZSB2YXJpYXRpb25zCi0gRG8gbm90IG1peCBgZXhwYW5kOmAgd2l0aCBvdGhlciB0eXBlZCBsaW5lcyDigJQgaXQncyBlaXRoZXIgYSBzdGFuZGFsb25lIGV4cGFuZCBxdWVyeSBvciBhIGZ1bGwgcXVlcnkgZG9jdW1lbnQKCiMjIyBJbnRlbnQgKERpc2FtYmlndWF0aW9uKQoKV2hlbiBhIHF1ZXJ5IHRlcm0gaXMgYW1iaWd1b3VzLCBhZGQgYGludGVudGAgdG8gc3RlZXIgcmVzdWx0czoKCmBgYGpzb24KewogICJzZWFyY2hlcyI6IFsKICAgIHsgInR5cGUiOiAibGV4IiwgInF1ZXJ5IjogInBlcmZvcm1hbmNlIiB9CiAgXSwKICAiaW50ZW50IjogIndlYiBwYWdlIGxvYWQgdGltZXMgYW5kIENvcmUgV2ViIFZpdGFscyIKfQpgYGAKCkludGVudCBhZmZlY3RzIGV4cGFuc2lvbiwgcmVyYW5raW5nLCBjaHVuayBzZWxlY3Rpb24sIGFuZCBzbmlwcGV0IGV4dHJhY3Rpb24uIEl0IGRvZXMgbm90IHNlYXJjaCBvbiBpdHMgb3duIOKAlCBpdCdzIGEgc3RlZXJpbmcgc2lnbmFsIHRoYXQgZGlzYW1iaWd1YXRlcyBxdWVyaWVzIGxpa2UgInBlcmZvcm1hbmNlIiAod2ViLXBlcmYgdnMgdGVhbSBoZWFsdGggdnMgZml0bmVzcykuCgojIyMgQ29tYmluaW5nIFR5cGVzCgp8IEdvYWwgfCBBcHByb2FjaCB8CnwtLS0tLS18LS0tLS0tLS0tLXwKfCBLbm93IGV4YWN0IHRlcm1zIHwgYGxleGAgb25seSB8CnwgRG9uJ3Qga25vdyB2b2NhYnVsYXJ5IHwgVXNlIGEgc2luZ2xlLWxpbmUgcXVlcnkgKGltcGxpY2l0IGBleHBhbmQ6YCkgb3IgYHZlY2AgfAp8IEJlc3QgcmVjYWxsIHwgYGxleGAgKyBgdmVjYCB8CnwgQ29tcGxleCB0b3BpYyB8IGBsZXhgICsgYHZlY2AgKyBgaHlkZWAgfAp8IEFtYmlndW91cyBxdWVyeSB8IEFkZCBgaW50ZW50YCB0byBhbnkgY29tYmluYXRpb24gYWJvdmUgfAoKRmlyc3QgcXVlcnkgZ2V0cyAyeCB3ZWlnaHQgaW4gZnVzaW9uIOKAlCBwdXQgeW91ciBiZXN0IGd1ZXNzIGZpcnN0LgoKIyMjIExleCBRdWVyeSBTeW50YXgKCnwgU3ludGF4IHwgTWVhbmluZyB8IEV4YW1wbGUgfAp8LS0tLS0tLS18LS0tLS0tLS0tfC0tLS0tLS0tLXwKfCBgdGVybWAgfCBQcmVmaXggbWF0Y2ggfCBgcGVyZmAgbWF0Y2hlcyAicGVyZm9ybWFuY2UiIHwKfCBgInBocmFzZSJgIHwgRXhhY3QgcGhyYXNlIHwgYCJyYXRlIGxpbWl0ZXIiYCB8CnwgYC10ZXJtYCB8IEV4Y2x1ZGUgfCBgcGVyZm9ybWFuY2UgLXNwb3J0c2AgfAoKTm90ZTogYC10ZXJtYCBvbmx5IHdvcmtzIGluIGxleCBxdWVyaWVzLCBub3QgdmVjL2h5ZGUuCgojIyMgQ29sbGVjdGlvbiBGaWx0ZXJpbmcKCmBgYGpzb24KeyAiY29sbGVjdGlvbnMiOiBbImRvY3MiXSB9ICAgICAgICAgICAgICAvLyBTaW5nbGUKeyAiY29sbGVjdGlvbnMiOiBbImRvY3MiLCAibm90ZXMiXSB9ICAgICAvLyBNdWx0aXBsZSAoT1IpCmBgYAoKT21pdCB0byBzZWFyY2ggYWxsIGNvbGxlY3Rpb25zLgoKIyMgT3RoZXIgTUNQIFRvb2xzCgp8IFRvb2wgfCBVc2UgfAp8LS0tLS0tfC0tLS0tfAp8IGBnZXRgIHwgUmV0cmlldmUgZG9jIGJ5IHBhdGggb3IgYCNkb2NpZGAgfAp8IGBtdWx0aV9nZXRgIHwgUmV0cmlldmUgbXVsdGlwbGUgYnkgZ2xvYi9saXN0IHwKfCBgc3RhdHVzYCB8IENvbGxlY3Rpb25zIGFuZCBoZWFsdGggfAoKIyMgQ0xJCgpgYGBiYXNoCnFtZCBxdWVyeSAicXVlc3Rpb24iICAgICAgICAgICAgICAjIEF1dG8tZXhwYW5kICsgcmVyYW5rCnFtZCBxdWVyeSAkJ2xleDogWFxudmVjOiBZJyAgICAgICAjIFN0cnVjdHVyZWQKcW1kIHF1ZXJ5ICQnZXhwYW5kOiBxdWVzdGlvbicgICAgICMgRXhwbGljaXQgZXhwYW5kCnFtZCBxdWVyeSAtLWpzb24gLS1leHBsYWluICJxIiAgICAjIFNob3cgc2NvcmUgdHJhY2VzIChSUkYgKyByZXJhbmsgYmxlbmQpCnFtZCBzZWFyY2ggImtleXdvcmRzIiAgICAgICAgICAgICAjIEJNMjUgb25seSAobm8gTExNKQpxbWQgZ2V0ICIjYWJjMTIzIiAgICAgICAgICAgICAgICAgIyBCeSBkb2NpZApxbWQgbXVsdGktZ2V0ICJqb3VybmFscy8yMDI2LSoubWQiIC1sIDQwICAjIEJhdGNoIHB1bGwgc25pcHBldHMgYnkgZ2xvYgpxbWQgbXVsdGktZ2V0IG5vdGVzL2Zvby5tZCxub3Rlcy9iYXIubWQgICAjIENvbW1hLXNlcGFyYXRlZCBsaXN0LCBwcmVzZXJ2ZXMgb3JkZXIKYGBgCgojIyBIVFRQIEFQSQoKYGBgYmFzaApjdXJsIC1YIFBPU1QgaHR0cDovL2xvY2FsaG9zdDo4MTgxL3F1ZXJ5IFwKICAtSCAiQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9qc29uIiBcCiAgLWQgJ3sic2VhcmNoZXMiOiBbeyJ0eXBlIjogImxleCIsICJxdWVyeSI6ICJ0ZXN0In1dfScKYGBgCgojIyBTZXR1cAoKYGBgYmFzaApucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZApxbWQgY29sbGVjdGlvbiBhZGQgfi9ub3RlcyAtLW5hbWUgbm90ZXMKcW1kIGVtYmVkCmBgYAo=",
|
|
4
|
+
"references/mcp-setup.md": "IyBRTUQgTUNQIFNlcnZlciBTZXR1cAoKIyMgSW5zdGFsbAoKYGBgYmFzaApucG0gaW5zdGFsbCAtZyBAdG9iaWx1L3FtZApxbWQgY29sbGVjdGlvbiBhZGQgfi9wYXRoL3RvL21hcmtkb3duIC0tbmFtZSBteWtub3dsZWRnZQpxbWQgZW1iZWQKYGBgCgojIyBDb25maWd1cmUgTUNQIENsaWVudAoKKipDbGF1ZGUgQ29kZSoqIChgfi8uY2xhdWRlL3NldHRpbmdzLmpzb25gKToKYGBganNvbgp7CiAgIm1jcFNlcnZlcnMiOiB7CiAgICAicW1kIjogeyAiY29tbWFuZCI6ICJxbWQiLCAiYXJncyI6IFsibWNwIl0gfQogIH0KfQpgYGAKCioqQ2xhdWRlIERlc2t0b3AqKiAoYH4vTGlicmFyeS9BcHBsaWNhdGlvbiBTdXBwb3J0L0NsYXVkZS9jbGF1ZGVfZGVza3RvcF9jb25maWcuanNvbmApOgpgYGBqc29uCnsKICAibWNwU2VydmVycyI6IHsKICAgICJxbWQiOiB7ICJjb21tYW5kIjogInFtZCIsICJhcmdzIjogWyJtY3AiXSB9CiAgfQp9CmBgYAoKKipPcGVuQ2xhdyoqIChgfi8ub3BlbmNsYXcvb3BlbmNsYXcuanNvbmApOgpgYGBqc29uCnsKICAibWNwIjogewogICAgInNlcnZlcnMiOiB7CiAgICAgICJxbWQiOiB7ICJjb21tYW5kIjogInFtZCIsICJhcmdzIjogWyJtY3AiXSB9CiAgICB9CiAgfQp9CmBgYAoKIyMgSFRUUCBNb2RlCgpgYGBiYXNoCnFtZCBtY3AgLS1odHRwICAgICAgICAgICAgICAjIFBvcnQgODE4MQpxbWQgbWNwIC0taHR0cCAtLWRhZW1vbiAgICAgIyBCYWNrZ3JvdW5kCnFtZCBtY3Agc3RvcCAgICAgICAgICAgICAgICAjIFN0b3AgZGFlbW9uCmBgYAoKIyMgVG9vbHMKCiMjIyBzdHJ1Y3R1cmVkX3NlYXJjaAoKU2VhcmNoIHdpdGggcHJlLWV4cGFuZGVkIHF1ZXJpZXMuCgpgYGBqc29uCnsKICAic2VhcmNoZXMiOiBbCiAgICB7ICJ0eXBlIjogImxleCIsICJxdWVyeSI6ICJrZXl3b3JkIHBocmFzZXMiIH0sCiAgICB7ICJ0eXBlIjogInZlYyIsICJxdWVyeSI6ICJuYXR1cmFsIGxhbmd1YWdlIHF1ZXN0aW9uIiB9LAogICAgeyAidHlwZSI6ICJoeWRlIiwgInF1ZXJ5IjogImh5cG90aGV0aWNhbCBhbnN3ZXIgcGFzc2FnZS4uLiIgfQogIF0sCiAgImxpbWl0IjogMTAsCiAgImNvbGxlY3Rpb24iOiAib3B0aW9uYWwiLAogICJtaW5TY29yZSI6IDAuMAp9CmBgYAoKfCBUeXBlIHwgTWV0aG9kIHwgSW5wdXQgfAp8LS0tLS0tfC0tLS0tLS0tfC0tLS0tLS18CnwgYGxleGAgfCBCTTI1IHwgS2V5d29yZHMgKDItNSB0ZXJtcykgfAp8IGB2ZWNgIHwgVmVjdG9yIHwgUXVlc3Rpb24gfAp8IGBoeWRlYCB8IFZlY3RvciB8IEFuc3dlciBwYXNzYWdlICg1MC0xMDAgd29yZHMpIHwKCiMjIyBnZXQKClJldHJpZXZlIGRvY3VtZW50IGJ5IHBhdGggb3IgYCNkb2NpZGAuCgp8IFBhcmFtIHwgVHlwZSB8IERlc2NyaXB0aW9uIHwKfC0tLS0tLS18LS0tLS0tfC0tLS0tLS0tLS0tLS18CnwgYHBhdGhgIHwgc3RyaW5nIHwgRmlsZSBwYXRoIG9yIGAjZG9jaWRgIHwKfCBgZnVsbGAgfCBib29sPyB8IFJldHVybiBmdWxsIGNvbnRlbnQgfAp8IGBsaW5lTnVtYmVyc2AgfCBib29sPyB8IEFkZCBsaW5lIG51bWJlcnMgfAoKIyMjIG11bHRpX2dldAoKUmV0cmlldmUgbXVsdGlwbGUgZG9jdW1lbnRzLgoKfCBQYXJhbSB8IFR5cGUgfCBEZXNjcmlwdGlvbiB8CnwtLS0tLS0tfC0tLS0tLXwtLS0tLS0tLS0tLS0tfAp8IGBwYXR0ZXJuYCB8IHN0cmluZyB8IEdsb2Igb3IgY29tbWEtc2VwYXJhdGVkIGxpc3QgfAp8IGBtYXhCeXRlc2AgfCBudW1iZXI/IHwgU2tpcCBsYXJnZSBmaWxlcyAoZGVmYXVsdCAxMEtCKSB8CgojIyMgc3RhdHVzCgpJbmRleCBoZWFsdGggYW5kIGNvbGxlY3Rpb25zLiBObyBwYXJhbXMuCgojIyBUcm91Ymxlc2hvb3RpbmcKCi0gKipOb3Qgc3RhcnRpbmcqKjogYHdoaWNoIHFtZGAsIGBxbWQgbWNwYCBtYW51YWxseQotICoqTm8gcmVzdWx0cyoqOiBgcW1kIGNvbGxlY3Rpb24gbGlzdGAsIGBxbWQgZW1iZWRgCi0gKipTbG93IGZpcnN0IHNlYXJjaCoqOiBOb3JtYWwsIG1vZGVscyBsb2FkaW5nICh+M0dCKQo="
|
|
5
|
+
};
|
|
6
|
+
export function getEmbeddedQmdSkillFiles() {
|
|
7
|
+
return Object.entries(EMBEDDED_QMD_SKILL_BASE64).map(([relativePath, encoded]) => ({
|
|
8
|
+
relativePath,
|
|
9
|
+
content: Buffer.from(encoded, 'base64').toString('utf8'),
|
|
10
|
+
}));
|
|
11
|
+
}
|
|
12
|
+
export function getEmbeddedQmdSkillContent() {
|
|
13
|
+
return Buffer.from(EMBEDDED_QMD_SKILL_BASE64["SKILL.md"], "base64").toString("utf8");
|
|
14
|
+
}
|