agentic-knowledge-mcp 0.0.1
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 +674 -0
- package/README.md +530 -0
- package/package.json +94 -0
- package/packages/cli/dist/cli.d.ts +5 -0
- package/packages/cli/dist/cli.js +21 -0
- package/packages/cli/dist/commands/create.d.ts +5 -0
- package/packages/cli/dist/commands/create.js +90 -0
- package/packages/cli/dist/commands/init.d.ts +5 -0
- package/packages/cli/dist/commands/init.js +182 -0
- package/packages/cli/dist/commands/refresh.d.ts +5 -0
- package/packages/cli/dist/commands/refresh.js +322 -0
- package/packages/cli/dist/commands/status.d.ts +5 -0
- package/packages/cli/dist/commands/status.js +268 -0
- package/packages/cli/dist/index.d.ts +6 -0
- package/packages/cli/dist/index.js +6 -0
- package/packages/cli/package.json +57 -0
- package/packages/content-loader/dist/__tests__/debug-filtering.d.ts +1 -0
- package/packages/content-loader/dist/__tests__/debug-filtering.js +17 -0
- package/packages/content-loader/dist/__tests__/test-filtering.d.ts +1 -0
- package/packages/content-loader/dist/__tests__/test-filtering.js +19 -0
- package/packages/content-loader/dist/content/api-documentation-loader.d.ts +26 -0
- package/packages/content-loader/dist/content/api-documentation-loader.js +45 -0
- package/packages/content-loader/dist/content/content-processor.d.ts +44 -0
- package/packages/content-loader/dist/content/content-processor.js +86 -0
- package/packages/content-loader/dist/content/documentation-site-loader.d.ts +26 -0
- package/packages/content-loader/dist/content/documentation-site-loader.js +45 -0
- package/packages/content-loader/dist/content/git-repo-loader.d.ts +79 -0
- package/packages/content-loader/dist/content/git-repo-loader.js +368 -0
- package/packages/content-loader/dist/content/index.d.ts +9 -0
- package/packages/content-loader/dist/content/index.js +9 -0
- package/packages/content-loader/dist/content/loader.d.ts +47 -0
- package/packages/content-loader/dist/content/loader.js +8 -0
- package/packages/content-loader/dist/content/metadata-manager.d.ts +65 -0
- package/packages/content-loader/dist/content/metadata-manager.js +160 -0
- package/packages/content-loader/dist/index.d.ts +5 -0
- package/packages/content-loader/dist/index.js +5 -0
- package/packages/content-loader/dist/types.d.ts +127 -0
- package/packages/content-loader/dist/types.js +48 -0
- package/packages/content-loader/package.json +50 -0
- package/packages/core/dist/config/discovery.d.ts +15 -0
- package/packages/core/dist/config/discovery.js +65 -0
- package/packages/core/dist/config/loader.d.ts +22 -0
- package/packages/core/dist/config/loader.js +236 -0
- package/packages/core/dist/config/manager.d.ts +55 -0
- package/packages/core/dist/config/manager.js +180 -0
- package/packages/core/dist/content/api-documentation-loader.d.ts +26 -0
- package/packages/core/dist/content/api-documentation-loader.js +45 -0
- package/packages/core/dist/content/content-processor.d.ts +44 -0
- package/packages/core/dist/content/content-processor.js +81 -0
- package/packages/core/dist/content/documentation-site-loader.d.ts +26 -0
- package/packages/core/dist/content/documentation-site-loader.js +45 -0
- package/packages/core/dist/content/git-repo-loader.d.ts +54 -0
- package/packages/core/dist/content/git-repo-loader.js +264 -0
- package/packages/core/dist/content/index.d.ts +9 -0
- package/packages/core/dist/content/index.js +9 -0
- package/packages/core/dist/content/loader.d.ts +50 -0
- package/packages/core/dist/content/loader.js +7 -0
- package/packages/core/dist/content/metadata-manager.d.ts +65 -0
- package/packages/core/dist/content/metadata-manager.js +160 -0
- package/packages/core/dist/index.d.ts +12 -0
- package/packages/core/dist/index.js +30 -0
- package/packages/core/dist/paths/calculator.d.ts +46 -0
- package/packages/core/dist/paths/calculator.js +166 -0
- package/packages/core/dist/templates/processor.d.ts +40 -0
- package/packages/core/dist/templates/processor.js +111 -0
- package/packages/core/dist/types.d.ts +129 -0
- package/packages/core/dist/types.js +79 -0
- package/packages/core/package.json +50 -0
- package/packages/mcp-server/dist/bin.d.ts +5 -0
- package/packages/mcp-server/dist/bin.js +10 -0
- package/packages/mcp-server/dist/cli.d.ts +7 -0
- package/packages/mcp-server/dist/cli.js +17 -0
- package/packages/mcp-server/dist/index.d.ts +8 -0
- package/packages/mcp-server/dist/index.js +9 -0
- package/packages/mcp-server/dist/server.d.ts +35 -0
- package/packages/mcp-server/dist/server.js +244 -0
- package/packages/mcp-server/package.json +54 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration loading and validation
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import * as fsSync from "node:fs";
|
|
6
|
+
import { load } from "js-yaml";
|
|
7
|
+
import { KnowledgeError, ErrorType } from "../types.js";
|
|
8
|
+
import { validateTemplate } from "../templates/processor.js";
|
|
9
|
+
/**
|
|
10
|
+
* Load configuration from a YAML file with strict template validation
|
|
11
|
+
* @param configPath - Path to the configuration file
|
|
12
|
+
* @returns Parsed configuration object
|
|
13
|
+
*/
|
|
14
|
+
export async function loadConfig(configPath) {
|
|
15
|
+
try {
|
|
16
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
17
|
+
const parsed = load(content);
|
|
18
|
+
if (!validateConfig(parsed)) {
|
|
19
|
+
throw new KnowledgeError(
|
|
20
|
+
ErrorType.CONFIG_INVALID,
|
|
21
|
+
"Configuration file contains invalid structure",
|
|
22
|
+
{ configPath, parsed },
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
// Validate templates strictly at load time
|
|
26
|
+
validateAllTemplates(parsed, configPath);
|
|
27
|
+
return parsed;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof KnowledgeError) {
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
if (error.code === "ENOENT") {
|
|
33
|
+
throw new KnowledgeError(
|
|
34
|
+
ErrorType.CONFIG_NOT_FOUND,
|
|
35
|
+
`Configuration file not found: ${configPath}`,
|
|
36
|
+
{ configPath },
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
throw new KnowledgeError(
|
|
40
|
+
ErrorType.YAML_PARSE_ERROR,
|
|
41
|
+
`Failed to parse YAML configuration: ${error.message}`,
|
|
42
|
+
{ configPath, error },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Synchronous version of loadConfig with strict template validation
|
|
48
|
+
* @param configPath - Path to the configuration file
|
|
49
|
+
* @returns Parsed configuration object
|
|
50
|
+
*/
|
|
51
|
+
export function loadConfigSync(configPath) {
|
|
52
|
+
try {
|
|
53
|
+
const content = fsSync.readFileSync(configPath, "utf-8");
|
|
54
|
+
const parsed = load(content);
|
|
55
|
+
if (!validateConfig(parsed)) {
|
|
56
|
+
throw new KnowledgeError(
|
|
57
|
+
ErrorType.CONFIG_INVALID,
|
|
58
|
+
"Configuration file contains invalid structure",
|
|
59
|
+
{ configPath, parsed },
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
// Validate templates strictly at load time
|
|
63
|
+
validateAllTemplates(parsed, configPath);
|
|
64
|
+
return parsed;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (error instanceof KnowledgeError) {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
if (error.code === "ENOENT") {
|
|
70
|
+
throw new KnowledgeError(
|
|
71
|
+
ErrorType.CONFIG_NOT_FOUND,
|
|
72
|
+
`Configuration file not found: ${configPath}`,
|
|
73
|
+
{ configPath },
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
throw new KnowledgeError(
|
|
77
|
+
ErrorType.YAML_PARSE_ERROR,
|
|
78
|
+
`Failed to parse YAML configuration: ${error.message}`,
|
|
79
|
+
{ configPath, error },
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate all templates in configuration at load time
|
|
85
|
+
* @param config - Configuration object to validate
|
|
86
|
+
* @param configPath - Path to config file for error context
|
|
87
|
+
*/
|
|
88
|
+
function validateAllTemplates(config, configPath) {
|
|
89
|
+
// Validate global template if present
|
|
90
|
+
if (config.template) {
|
|
91
|
+
try {
|
|
92
|
+
validateTemplate(config.template);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new KnowledgeError(
|
|
95
|
+
ErrorType.TEMPLATE_ERROR,
|
|
96
|
+
`Invalid global template in configuration: ${error.message}`,
|
|
97
|
+
{ configPath, template: config.template, originalError: error },
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Validate each docset template if present
|
|
102
|
+
for (const docset of config.docsets) {
|
|
103
|
+
if (docset.template) {
|
|
104
|
+
try {
|
|
105
|
+
validateTemplate(docset.template);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new KnowledgeError(
|
|
108
|
+
ErrorType.TEMPLATE_ERROR,
|
|
109
|
+
`Invalid template for docset '${docset.id}': ${error.message}`,
|
|
110
|
+
{
|
|
111
|
+
configPath,
|
|
112
|
+
docsetId: docset.id,
|
|
113
|
+
template: docset.template,
|
|
114
|
+
originalError: error,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Validate a configuration object
|
|
123
|
+
* @param config - Configuration to validate
|
|
124
|
+
* @returns True if valid, false if invalid
|
|
125
|
+
*/
|
|
126
|
+
export function validateConfig(config) {
|
|
127
|
+
if (!config || typeof config !== "object") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const obj = config;
|
|
131
|
+
// Check required fields
|
|
132
|
+
if (typeof obj["version"] !== "string") {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (!Array.isArray(obj["docsets"])) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// Validate each docset
|
|
139
|
+
for (const docset of obj["docsets"]) {
|
|
140
|
+
if (!validateDocset(docset)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Optional template field
|
|
145
|
+
if (obj["template"] !== undefined && typeof obj["template"] !== "string") {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validate a docset configuration object
|
|
152
|
+
* @param docset - Docset to validate
|
|
153
|
+
* @returns True if valid, false if invalid
|
|
154
|
+
*/
|
|
155
|
+
function validateDocset(docset) {
|
|
156
|
+
if (!docset || typeof docset !== "object") {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const obj = docset;
|
|
160
|
+
// Check required fields
|
|
161
|
+
if (typeof obj["id"] !== "string" || obj["id"].toString().trim() === "") {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (typeof obj["name"] !== "string" || obj["name"].toString().trim() === "") {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
// local_path is optional if web_sources are provided
|
|
168
|
+
const hasWebSources =
|
|
169
|
+
Array.isArray(obj["web_sources"]) && obj["web_sources"].length > 0;
|
|
170
|
+
const hasLocalPath =
|
|
171
|
+
typeof obj["local_path"] === "string" &&
|
|
172
|
+
obj["local_path"].toString().trim() !== "";
|
|
173
|
+
if (!hasWebSources && !hasLocalPath) {
|
|
174
|
+
return false; // Must have either web_sources or local_path
|
|
175
|
+
}
|
|
176
|
+
if (
|
|
177
|
+
obj["local_path"] !== undefined &&
|
|
178
|
+
typeof obj["local_path"] !== "string"
|
|
179
|
+
) {
|
|
180
|
+
return false; // If local_path is provided, it must be a valid string
|
|
181
|
+
}
|
|
182
|
+
// Check optional fields
|
|
183
|
+
if (
|
|
184
|
+
obj["description"] !== undefined &&
|
|
185
|
+
typeof obj["description"] !== "string"
|
|
186
|
+
) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (obj["template"] !== undefined && typeof obj["template"] !== "string") {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
// Check optional web_sources field
|
|
193
|
+
if (obj["web_sources"] !== undefined) {
|
|
194
|
+
if (!Array.isArray(obj["web_sources"])) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// Validate each web source
|
|
198
|
+
for (const webSource of obj["web_sources"]) {
|
|
199
|
+
if (!validateWebSource(webSource)) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Validate a web source configuration object
|
|
208
|
+
* @param webSource - Web source to validate
|
|
209
|
+
* @returns True if valid, false if invalid
|
|
210
|
+
*/
|
|
211
|
+
function validateWebSource(webSource) {
|
|
212
|
+
if (!webSource || typeof webSource !== "object") {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
const obj = webSource;
|
|
216
|
+
// Check required fields
|
|
217
|
+
if (typeof obj["url"] !== "string" || obj["url"].toString().trim() === "") {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
if (typeof obj["type"] !== "string") {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
// Validate type is one of the supported values
|
|
224
|
+
const validTypes = ["git_repo", "documentation_site", "api_documentation"];
|
|
225
|
+
if (!validTypes.includes(obj["type"])) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
// Check optional options field
|
|
229
|
+
if (
|
|
230
|
+
obj["options"] !== undefined &&
|
|
231
|
+
(typeof obj["options"] !== "object" || obj["options"] === null)
|
|
232
|
+
) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management with read and write capabilities
|
|
3
|
+
*/
|
|
4
|
+
import type { KnowledgeConfig } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Central configuration manager for all config file operations
|
|
7
|
+
*/
|
|
8
|
+
export declare class ConfigManager {
|
|
9
|
+
private configCache;
|
|
10
|
+
private readonly CONFIG_CACHE_TTL;
|
|
11
|
+
/**
|
|
12
|
+
* Load configuration with caching
|
|
13
|
+
* @param startDir - Directory to start searching from (defaults to cwd)
|
|
14
|
+
* @returns Configuration and path
|
|
15
|
+
*/
|
|
16
|
+
loadConfig(startDir?: string): Promise<{
|
|
17
|
+
config: KnowledgeConfig;
|
|
18
|
+
configPath: string;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* Load configuration from specific file path
|
|
22
|
+
* @param configPath - Path to configuration file
|
|
23
|
+
* @returns Parsed configuration
|
|
24
|
+
*/
|
|
25
|
+
loadConfigFromPath(configPath: string): Promise<KnowledgeConfig>;
|
|
26
|
+
/**
|
|
27
|
+
* Save configuration to file
|
|
28
|
+
* @param config - Configuration to save
|
|
29
|
+
* @param configPath - Path to save to (optional, uses cached path if available)
|
|
30
|
+
*/
|
|
31
|
+
saveConfig(config: KnowledgeConfig, configPath?: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Update paths for a specific docset with discovered files
|
|
34
|
+
* @param docsetId - ID of docset to update
|
|
35
|
+
* @param discoveredPaths - Array of file paths that were discovered
|
|
36
|
+
* @returns Updated configuration
|
|
37
|
+
*/
|
|
38
|
+
updateDocsetPaths(docsetId: string, discoveredPaths: string[]): Promise<KnowledgeConfig>;
|
|
39
|
+
/**
|
|
40
|
+
* Find configuration file path
|
|
41
|
+
* @param startDir - Directory to start searching from
|
|
42
|
+
* @returns Path to config file or null if not found
|
|
43
|
+
*/
|
|
44
|
+
findConfigPath(startDir?: string): Promise<string | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Clear configuration cache (useful for testing)
|
|
47
|
+
*/
|
|
48
|
+
clearCache(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Check if configuration exists
|
|
51
|
+
* @param startDir - Directory to start searching from
|
|
52
|
+
* @returns True if config file exists
|
|
53
|
+
*/
|
|
54
|
+
configExists(startDir?: string): Promise<boolean>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management with read and write capabilities
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import { load, dump } from "js-yaml";
|
|
6
|
+
import { KnowledgeError, ErrorType } from "../types.js";
|
|
7
|
+
import { validateConfig } from "./loader.js";
|
|
8
|
+
import { findConfigPath } from "./discovery.js";
|
|
9
|
+
/**
|
|
10
|
+
* Central configuration manager for all config file operations
|
|
11
|
+
*/
|
|
12
|
+
export class ConfigManager {
|
|
13
|
+
configCache = null;
|
|
14
|
+
CONFIG_CACHE_TTL = 60000; // 1 minute cache
|
|
15
|
+
/**
|
|
16
|
+
* Load configuration with caching
|
|
17
|
+
* @param startDir - Directory to start searching from (defaults to cwd)
|
|
18
|
+
* @returns Configuration and path
|
|
19
|
+
*/
|
|
20
|
+
async loadConfig(startDir) {
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
// Return cached config if still valid
|
|
23
|
+
if (
|
|
24
|
+
this.configCache &&
|
|
25
|
+
now - this.configCache.loadTime < this.CONFIG_CACHE_TTL
|
|
26
|
+
) {
|
|
27
|
+
return {
|
|
28
|
+
config: this.configCache.config,
|
|
29
|
+
configPath: this.configCache.configPath,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Find and load fresh config
|
|
33
|
+
const configPath = await findConfigPath(startDir);
|
|
34
|
+
if (!configPath) {
|
|
35
|
+
throw new KnowledgeError(
|
|
36
|
+
ErrorType.CONFIG_NOT_FOUND,
|
|
37
|
+
"No configuration file found. Please ensure .knowledge/config.yaml exists in your project.",
|
|
38
|
+
{ searchPath: startDir || process.cwd() },
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
const config = await this.loadConfigFromPath(configPath);
|
|
42
|
+
// Cache the result
|
|
43
|
+
this.configCache = {
|
|
44
|
+
config,
|
|
45
|
+
configPath,
|
|
46
|
+
loadTime: now,
|
|
47
|
+
};
|
|
48
|
+
return { config, configPath };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load configuration from specific file path
|
|
52
|
+
* @param configPath - Path to configuration file
|
|
53
|
+
* @returns Parsed configuration
|
|
54
|
+
*/
|
|
55
|
+
async loadConfigFromPath(configPath) {
|
|
56
|
+
try {
|
|
57
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
58
|
+
const parsed = load(content);
|
|
59
|
+
if (!validateConfig(parsed)) {
|
|
60
|
+
throw new KnowledgeError(
|
|
61
|
+
ErrorType.CONFIG_INVALID,
|
|
62
|
+
"Configuration file contains invalid structure",
|
|
63
|
+
{ configPath, parsed },
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
return parsed;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (error instanceof KnowledgeError) {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
if (error.code === "ENOENT") {
|
|
72
|
+
throw new KnowledgeError(
|
|
73
|
+
ErrorType.CONFIG_NOT_FOUND,
|
|
74
|
+
`Configuration file not found: ${configPath}`,
|
|
75
|
+
{ configPath },
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
throw new KnowledgeError(
|
|
79
|
+
ErrorType.CONFIG_INVALID,
|
|
80
|
+
`Failed to parse configuration file: ${error instanceof Error ? error.message : String(error)}`,
|
|
81
|
+
{ configPath, originalError: error },
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Save configuration to file
|
|
87
|
+
* @param config - Configuration to save
|
|
88
|
+
* @param configPath - Path to save to (optional, uses cached path if available)
|
|
89
|
+
*/
|
|
90
|
+
async saveConfig(config, configPath) {
|
|
91
|
+
const targetPath = configPath || this.configCache?.configPath;
|
|
92
|
+
if (!targetPath) {
|
|
93
|
+
throw new KnowledgeError(
|
|
94
|
+
ErrorType.CONFIG_INVALID,
|
|
95
|
+
"No configuration path available. Load config first or provide explicit path.",
|
|
96
|
+
{ configPath },
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
// Validate config before saving
|
|
100
|
+
if (!validateConfig(config)) {
|
|
101
|
+
throw new KnowledgeError(
|
|
102
|
+
ErrorType.CONFIG_INVALID,
|
|
103
|
+
"Cannot save invalid configuration",
|
|
104
|
+
{ config },
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const yamlContent = dump(config, {
|
|
109
|
+
indent: 2,
|
|
110
|
+
lineWidth: 120,
|
|
111
|
+
noRefs: true,
|
|
112
|
+
});
|
|
113
|
+
await fs.writeFile(targetPath, yamlContent, "utf-8");
|
|
114
|
+
// Invalidate cache since config changed
|
|
115
|
+
this.configCache = null;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
throw new KnowledgeError(
|
|
118
|
+
ErrorType.CONFIG_INVALID,
|
|
119
|
+
`Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`,
|
|
120
|
+
{ configPath: targetPath, originalError: error },
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update paths for a specific docset with discovered files
|
|
126
|
+
* @param docsetId - ID of docset to update
|
|
127
|
+
* @param discoveredPaths - Array of file paths that were discovered
|
|
128
|
+
* @returns Updated configuration
|
|
129
|
+
*/
|
|
130
|
+
async updateDocsetPaths(docsetId, discoveredPaths) {
|
|
131
|
+
const { config } = await this.loadConfig();
|
|
132
|
+
// Find the docset to update
|
|
133
|
+
const docset = config.docsets.find((d) => d.id === docsetId);
|
|
134
|
+
if (!docset) {
|
|
135
|
+
throw new KnowledgeError(
|
|
136
|
+
ErrorType.CONFIG_INVALID,
|
|
137
|
+
`Docset '${docsetId}' not found in configuration`,
|
|
138
|
+
{ docsetId, availableDocsets: config.docsets.map((d) => d.id) },
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
// Update web sources with discovered paths
|
|
142
|
+
if (docset.web_sources) {
|
|
143
|
+
for (const webSource of docset.web_sources) {
|
|
144
|
+
if (webSource.type === "git_repo") {
|
|
145
|
+
// Add or update the paths in options
|
|
146
|
+
if (!webSource.options) {
|
|
147
|
+
webSource.options = {};
|
|
148
|
+
}
|
|
149
|
+
webSource.options.paths = discoveredPaths;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Save updated configuration
|
|
154
|
+
await this.saveConfig(config);
|
|
155
|
+
return config;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Find configuration file path
|
|
159
|
+
* @param startDir - Directory to start searching from
|
|
160
|
+
* @returns Path to config file or null if not found
|
|
161
|
+
*/
|
|
162
|
+
async findConfigPath(startDir) {
|
|
163
|
+
return await findConfigPath(startDir);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Clear configuration cache (useful for testing)
|
|
167
|
+
*/
|
|
168
|
+
clearCache() {
|
|
169
|
+
this.configCache = null;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Check if configuration exists
|
|
173
|
+
* @param startDir - Directory to start searching from
|
|
174
|
+
* @returns True if config file exists
|
|
175
|
+
*/
|
|
176
|
+
async configExists(startDir) {
|
|
177
|
+
const path = await this.findConfigPath(startDir);
|
|
178
|
+
return path !== null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API documentation content loader (STUB - not implemented yet)
|
|
3
|
+
*/
|
|
4
|
+
import { ContentLoader, type LoadResult } from "./loader.js";
|
|
5
|
+
import { WebSourceConfig } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Content loader for API documentation (STUB IMPLEMENTATION)
|
|
8
|
+
*/
|
|
9
|
+
export declare class ApiDocumentationLoader extends ContentLoader {
|
|
10
|
+
/**
|
|
11
|
+
* Check if this loader can handle the given web source type
|
|
12
|
+
*/
|
|
13
|
+
canHandle(webSource: WebSourceConfig): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Validate the web source configuration
|
|
16
|
+
*/
|
|
17
|
+
validateConfig(webSource: WebSourceConfig): true | string;
|
|
18
|
+
/**
|
|
19
|
+
* Load content from API documentation (NOT IMPLEMENTED)
|
|
20
|
+
*/
|
|
21
|
+
load(webSource: WebSourceConfig, targetPath: string): Promise<LoadResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Get content identifier (NOT IMPLEMENTED)
|
|
24
|
+
*/
|
|
25
|
+
getContentId(webSource: WebSourceConfig): Promise<string>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API documentation content loader (STUB - not implemented yet)
|
|
3
|
+
*/
|
|
4
|
+
import { ContentLoader } from "./loader.js";
|
|
5
|
+
import { WebSourceType, KnowledgeError, ErrorType } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Content loader for API documentation (STUB IMPLEMENTATION)
|
|
8
|
+
*/
|
|
9
|
+
export class ApiDocumentationLoader extends ContentLoader {
|
|
10
|
+
/**
|
|
11
|
+
* Check if this loader can handle the given web source type
|
|
12
|
+
*/
|
|
13
|
+
canHandle(webSource) {
|
|
14
|
+
return webSource.type === WebSourceType.API_DOCUMENTATION;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate the web source configuration
|
|
18
|
+
*/
|
|
19
|
+
validateConfig(webSource) {
|
|
20
|
+
if (!webSource.url) {
|
|
21
|
+
return "API documentation URL is required";
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load content from API documentation (NOT IMPLEMENTED)
|
|
27
|
+
*/
|
|
28
|
+
async load(webSource, targetPath) {
|
|
29
|
+
throw new KnowledgeError(
|
|
30
|
+
ErrorType.NOT_IMPLEMENTED,
|
|
31
|
+
"API documentation loading is not yet implemented. Use git_repo type for repositories with API documentation.",
|
|
32
|
+
{ webSource: webSource.url, targetPath },
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get content identifier (NOT IMPLEMENTED)
|
|
37
|
+
*/
|
|
38
|
+
async getContentId(webSource) {
|
|
39
|
+
throw new KnowledgeError(
|
|
40
|
+
ErrorType.NOT_IMPLEMENTED,
|
|
41
|
+
"API documentation content ID generation is not yet implemented.",
|
|
42
|
+
{ webSource: webSource.url },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content processor for converting and preparing content
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for content processing
|
|
6
|
+
*/
|
|
7
|
+
export interface ProcessingOptions {
|
|
8
|
+
/** Add frontmatter with source metadata */
|
|
9
|
+
addFrontmatter?: boolean;
|
|
10
|
+
/** Source URL to include in frontmatter */
|
|
11
|
+
sourceUrl?: string;
|
|
12
|
+
/** Additional metadata to include */
|
|
13
|
+
metadata?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Content processor for converting and preparing documentation content
|
|
17
|
+
*/
|
|
18
|
+
export declare class ContentProcessor {
|
|
19
|
+
/**
|
|
20
|
+
* Process a file, optionally adding frontmatter metadata
|
|
21
|
+
* @param filePath - Path to the file to process
|
|
22
|
+
* @param options - Processing options
|
|
23
|
+
*/
|
|
24
|
+
processFile(filePath: string, options?: ProcessingOptions): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Add frontmatter to markdown content
|
|
27
|
+
* @param content - Original markdown content
|
|
28
|
+
* @param options - Processing options with metadata
|
|
29
|
+
* @returns Content with frontmatter added
|
|
30
|
+
*/
|
|
31
|
+
private addFrontmatter;
|
|
32
|
+
/**
|
|
33
|
+
* Simple object to YAML conversion for frontmatter
|
|
34
|
+
* @param obj - Object to convert
|
|
35
|
+
* @returns YAML string
|
|
36
|
+
*/
|
|
37
|
+
private objectToYaml;
|
|
38
|
+
/**
|
|
39
|
+
* Check if a file should be processed based on its extension
|
|
40
|
+
* @param filePath - Path to check
|
|
41
|
+
* @returns True if file should be processed
|
|
42
|
+
*/
|
|
43
|
+
shouldProcess(filePath: string): boolean;
|
|
44
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content processor for converting and preparing content
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
/**
|
|
7
|
+
* Content processor for converting and preparing documentation content
|
|
8
|
+
*/
|
|
9
|
+
export class ContentProcessor {
|
|
10
|
+
/**
|
|
11
|
+
* Process a file, optionally adding frontmatter metadata
|
|
12
|
+
* @param filePath - Path to the file to process
|
|
13
|
+
* @param options - Processing options
|
|
14
|
+
*/
|
|
15
|
+
async processFile(filePath, options = {}) {
|
|
16
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
17
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
18
|
+
// For markdown files, optionally add frontmatter
|
|
19
|
+
if (extension === ".md" || extension === ".mdx") {
|
|
20
|
+
if (options.addFrontmatter) {
|
|
21
|
+
const processedContent = this.addFrontmatter(content, options);
|
|
22
|
+
await fs.writeFile(filePath, processedContent, "utf-8");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// For other file types, we keep them as-is for now
|
|
26
|
+
// Future: HTML to Markdown conversion would go here
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Add frontmatter to markdown content
|
|
30
|
+
* @param content - Original markdown content
|
|
31
|
+
* @param options - Processing options with metadata
|
|
32
|
+
* @returns Content with frontmatter added
|
|
33
|
+
*/
|
|
34
|
+
addFrontmatter(content, options) {
|
|
35
|
+
// Check if frontmatter already exists
|
|
36
|
+
if (content.startsWith("---\n")) {
|
|
37
|
+
return content; // Don't modify existing frontmatter
|
|
38
|
+
}
|
|
39
|
+
const frontmatter = {};
|
|
40
|
+
if (options.sourceUrl) {
|
|
41
|
+
frontmatter["source_url"] = options.sourceUrl;
|
|
42
|
+
}
|
|
43
|
+
if (options.metadata) {
|
|
44
|
+
Object.assign(frontmatter, options.metadata);
|
|
45
|
+
}
|
|
46
|
+
// Add processed timestamp
|
|
47
|
+
frontmatter["processed_at"] = new Date().toISOString();
|
|
48
|
+
// Convert to YAML frontmatter
|
|
49
|
+
const yamlFrontmatter = this.objectToYaml(frontmatter);
|
|
50
|
+
return `---\n${yamlFrontmatter}---\n\n${content}`;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Simple object to YAML conversion for frontmatter
|
|
54
|
+
* @param obj - Object to convert
|
|
55
|
+
* @returns YAML string
|
|
56
|
+
*/
|
|
57
|
+
objectToYaml(obj) {
|
|
58
|
+
return (
|
|
59
|
+
Object.entries(obj)
|
|
60
|
+
.map(([key, value]) => {
|
|
61
|
+
if (typeof value === "string") {
|
|
62
|
+
// Quote strings that contain special characters
|
|
63
|
+
const needsQuotes = /[:\n\r\t"']/.test(value);
|
|
64
|
+
return `${key}: ${needsQuotes ? `"${value.replace(/"/g, '\\"')}"` : value}`;
|
|
65
|
+
}
|
|
66
|
+
return `${key}: ${value}`;
|
|
67
|
+
})
|
|
68
|
+
.join("\n") + "\n"
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if a file should be processed based on its extension
|
|
73
|
+
* @param filePath - Path to check
|
|
74
|
+
* @returns True if file should be processed
|
|
75
|
+
*/
|
|
76
|
+
shouldProcess(filePath) {
|
|
77
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
78
|
+
const processableExtensions = [".md", ".mdx", ".txt", ".rst"];
|
|
79
|
+
return processableExtensions.includes(extension);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation site content loader (STUB - not implemented yet)
|
|
3
|
+
*/
|
|
4
|
+
import { ContentLoader, type LoadResult } from "./loader.js";
|
|
5
|
+
import { WebSourceConfig } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Content loader for documentation websites (STUB IMPLEMENTATION)
|
|
8
|
+
*/
|
|
9
|
+
export declare class DocumentationSiteLoader extends ContentLoader {
|
|
10
|
+
/**
|
|
11
|
+
* Check if this loader can handle the given web source type
|
|
12
|
+
*/
|
|
13
|
+
canHandle(webSource: WebSourceConfig): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Validate the web source configuration
|
|
16
|
+
*/
|
|
17
|
+
validateConfig(webSource: WebSourceConfig): true | string;
|
|
18
|
+
/**
|
|
19
|
+
* Load content from a documentation site (NOT IMPLEMENTED)
|
|
20
|
+
*/
|
|
21
|
+
load(webSource: WebSourceConfig, targetPath: string): Promise<LoadResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Get content identifier (NOT IMPLEMENTED)
|
|
24
|
+
*/
|
|
25
|
+
getContentId(webSource: WebSourceConfig): Promise<string>;
|
|
26
|
+
}
|