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,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation site 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 documentation websites (STUB IMPLEMENTATION)
|
|
8
|
+
*/
|
|
9
|
+
export class DocumentationSiteLoader extends ContentLoader {
|
|
10
|
+
/**
|
|
11
|
+
* Check if this loader can handle the given web source type
|
|
12
|
+
*/
|
|
13
|
+
canHandle(webSource) {
|
|
14
|
+
return webSource.type === WebSourceType.DOCUMENTATION_SITE;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate the web source configuration
|
|
18
|
+
*/
|
|
19
|
+
validateConfig(webSource) {
|
|
20
|
+
if (!webSource.url) {
|
|
21
|
+
return "Documentation site URL is required";
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load content from a documentation site (NOT IMPLEMENTED)
|
|
27
|
+
*/
|
|
28
|
+
async load(webSource, targetPath) {
|
|
29
|
+
throw new KnowledgeError(
|
|
30
|
+
ErrorType.NOT_IMPLEMENTED,
|
|
31
|
+
"Documentation site loading is not yet implemented. Use git_repo type for repositories with 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
|
+
"Documentation site content ID generation is not yet implemented.",
|
|
42
|
+
{ webSource: webSource.url },
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git repository content loader
|
|
3
|
+
*/
|
|
4
|
+
import { ContentLoader, type LoadResult } from "./loader.js";
|
|
5
|
+
import { WebSourceConfig } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Content loader for Git repositories (GitHub, GitLab, any Git repo)
|
|
8
|
+
*/
|
|
9
|
+
export declare class GitRepoLoader 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 Git repository
|
|
20
|
+
*/
|
|
21
|
+
load(webSource: WebSourceConfig, targetPath: string): Promise<LoadResult>;
|
|
22
|
+
/**
|
|
23
|
+
* Get content identifier for change detection
|
|
24
|
+
*/
|
|
25
|
+
getContentId(webSource: WebSourceConfig): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Validate if URL is a valid Git repository URL
|
|
28
|
+
*/
|
|
29
|
+
private isValidGitUrl;
|
|
30
|
+
/**
|
|
31
|
+
* Create a temporary directory for cloning
|
|
32
|
+
*/
|
|
33
|
+
private createTempDirectory;
|
|
34
|
+
/**
|
|
35
|
+
* Clone the Git repository
|
|
36
|
+
*/
|
|
37
|
+
private cloneRepository;
|
|
38
|
+
/**
|
|
39
|
+
* Extract content from cloned repository to target directory
|
|
40
|
+
*/
|
|
41
|
+
private extractContent;
|
|
42
|
+
/**
|
|
43
|
+
* Copy directory recursively
|
|
44
|
+
*/
|
|
45
|
+
private copyDirectory;
|
|
46
|
+
/**
|
|
47
|
+
* Generate content hash for change detection
|
|
48
|
+
*/
|
|
49
|
+
private generateContentHash;
|
|
50
|
+
/**
|
|
51
|
+
* Clean up temporary directory
|
|
52
|
+
*/
|
|
53
|
+
private cleanupTempDirectory;
|
|
54
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git repository content loader
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { ContentLoader } from "./loader.js";
|
|
8
|
+
import { WebSourceType, KnowledgeError, ErrorType } from "../types.js";
|
|
9
|
+
import * as crypto from "node:crypto";
|
|
10
|
+
/**
|
|
11
|
+
* Content loader for Git repositories (GitHub, GitLab, any Git repo)
|
|
12
|
+
*/
|
|
13
|
+
export class GitRepoLoader extends ContentLoader {
|
|
14
|
+
/**
|
|
15
|
+
* Check if this loader can handle the given web source type
|
|
16
|
+
*/
|
|
17
|
+
canHandle(webSource) {
|
|
18
|
+
return webSource.type === WebSourceType.GIT_REPO;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Validate the web source configuration
|
|
22
|
+
*/
|
|
23
|
+
validateConfig(webSource) {
|
|
24
|
+
if (!webSource.url) {
|
|
25
|
+
return "Git repository URL is required";
|
|
26
|
+
}
|
|
27
|
+
// Basic URL validation for Git repos
|
|
28
|
+
if (!this.isValidGitUrl(webSource.url)) {
|
|
29
|
+
return "Invalid Git repository URL";
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Load content from a Git repository
|
|
35
|
+
*/
|
|
36
|
+
async load(webSource, targetPath) {
|
|
37
|
+
try {
|
|
38
|
+
const options = webSource.options;
|
|
39
|
+
const tempDir = await this.createTempDirectory();
|
|
40
|
+
try {
|
|
41
|
+
// Clone the repository
|
|
42
|
+
await this.cloneRepository(webSource.url, tempDir, options);
|
|
43
|
+
// Extract specified paths or all content
|
|
44
|
+
const extractedFiles = await this.extractContent(
|
|
45
|
+
tempDir,
|
|
46
|
+
targetPath,
|
|
47
|
+
options?.paths,
|
|
48
|
+
);
|
|
49
|
+
// Generate content hash
|
|
50
|
+
const contentHash = await this.generateContentHash(
|
|
51
|
+
targetPath,
|
|
52
|
+
extractedFiles,
|
|
53
|
+
);
|
|
54
|
+
return {
|
|
55
|
+
success: true,
|
|
56
|
+
files: extractedFiles,
|
|
57
|
+
contentHash,
|
|
58
|
+
};
|
|
59
|
+
} finally {
|
|
60
|
+
// Clean up temp directory
|
|
61
|
+
await this.cleanupTempDirectory(tempDir);
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const errorMessage =
|
|
65
|
+
error instanceof Error ? error.message : String(error);
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
files: [],
|
|
69
|
+
contentHash: "",
|
|
70
|
+
error: `Git repository loading failed: ${errorMessage}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get content identifier for change detection
|
|
76
|
+
*/
|
|
77
|
+
async getContentId(webSource) {
|
|
78
|
+
try {
|
|
79
|
+
const options = webSource.options;
|
|
80
|
+
const branch = options?.branch || "HEAD";
|
|
81
|
+
// Get the latest commit hash from the remote repository
|
|
82
|
+
const command = `git ls-remote ${webSource.url} ${branch}`;
|
|
83
|
+
const output = execSync(command, { encoding: "utf8", timeout: 30000 });
|
|
84
|
+
const commitHash = output.trim().split("\t")[0];
|
|
85
|
+
// Combine with URL and paths for a unique identifier
|
|
86
|
+
const paths = options?.paths ? options.paths.sort().join(",") : "all";
|
|
87
|
+
return crypto
|
|
88
|
+
.createHash("sha256")
|
|
89
|
+
.update(`${webSource.url}:${commitHash}:${paths}`)
|
|
90
|
+
.digest("hex");
|
|
91
|
+
} catch (error) {
|
|
92
|
+
// Fallback to URL-based hash if remote access fails
|
|
93
|
+
const options = webSource.options;
|
|
94
|
+
const paths = options?.paths ? options.paths.sort().join(",") : "all";
|
|
95
|
+
return crypto
|
|
96
|
+
.createHash("sha256")
|
|
97
|
+
.update(`${webSource.url}:${paths}`)
|
|
98
|
+
.digest("hex");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate if URL is a valid Git repository URL
|
|
103
|
+
*/
|
|
104
|
+
isValidGitUrl(url) {
|
|
105
|
+
const gitUrlPatterns = [
|
|
106
|
+
/^https:\/\/github\.com\/[\w\-._]+\/[\w\-._]+(?:\.git)?$/,
|
|
107
|
+
/^https:\/\/gitlab\.com\/[\w\-._\/]+(?:\.git)?$/,
|
|
108
|
+
/^https:\/\/[\w\-._]+\/[\w\-._\/]+\.git$/,
|
|
109
|
+
/^git@[\w\-._]+:[\w\-._\/]+\.git$/,
|
|
110
|
+
];
|
|
111
|
+
return gitUrlPatterns.some((pattern) => pattern.test(url));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Create a temporary directory for cloning
|
|
115
|
+
*/
|
|
116
|
+
async createTempDirectory() {
|
|
117
|
+
const tempDir = path.join(
|
|
118
|
+
process.cwd(),
|
|
119
|
+
".tmp",
|
|
120
|
+
`git-clone-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
121
|
+
);
|
|
122
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
123
|
+
return tempDir;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Clone the Git repository
|
|
127
|
+
*/
|
|
128
|
+
async cloneRepository(url, targetDir, options) {
|
|
129
|
+
const branch = options?.branch || "main";
|
|
130
|
+
const depth = "--depth 1"; // Shallow clone for efficiency
|
|
131
|
+
let gitCommand = `git clone ${depth} --branch ${branch} ${url} ${targetDir}`;
|
|
132
|
+
// Add authentication if token is provided
|
|
133
|
+
if (options?.token) {
|
|
134
|
+
// For HTTPS URLs, inject token
|
|
135
|
+
if (url.startsWith("https://")) {
|
|
136
|
+
const urlWithToken = url.replace(
|
|
137
|
+
"https://",
|
|
138
|
+
`https://${options.token}@`,
|
|
139
|
+
);
|
|
140
|
+
gitCommand = `git clone ${depth} --branch ${branch} ${urlWithToken} ${targetDir}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
execSync(gitCommand, {
|
|
145
|
+
stdio: "pipe",
|
|
146
|
+
timeout: 120000, // 2 minutes timeout
|
|
147
|
+
cwd: process.cwd(),
|
|
148
|
+
});
|
|
149
|
+
} catch (error) {
|
|
150
|
+
// Try with master branch if main fails
|
|
151
|
+
if (branch === "main") {
|
|
152
|
+
const masterCommand = gitCommand.replace(
|
|
153
|
+
"--branch main",
|
|
154
|
+
"--branch master",
|
|
155
|
+
);
|
|
156
|
+
try {
|
|
157
|
+
execSync(masterCommand, {
|
|
158
|
+
stdio: "pipe",
|
|
159
|
+
timeout: 120000,
|
|
160
|
+
cwd: process.cwd(),
|
|
161
|
+
});
|
|
162
|
+
return;
|
|
163
|
+
} catch (masterError) {
|
|
164
|
+
// If both fail, throw the original error
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
throw new KnowledgeError(
|
|
168
|
+
ErrorType.GIT_REPO_ERROR,
|
|
169
|
+
`Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`,
|
|
170
|
+
{ url, branch, command: gitCommand },
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Extract content from cloned repository to target directory
|
|
176
|
+
*/
|
|
177
|
+
async extractContent(sourceDir, targetDir, paths) {
|
|
178
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
179
|
+
const extractedFiles = [];
|
|
180
|
+
if (paths && paths.length > 0) {
|
|
181
|
+
// Extract only specified paths
|
|
182
|
+
for (const relPath of paths) {
|
|
183
|
+
const sourcePath = path.join(sourceDir, relPath);
|
|
184
|
+
const targetPath = path.join(targetDir, relPath);
|
|
185
|
+
try {
|
|
186
|
+
const stats = await fs.stat(sourcePath);
|
|
187
|
+
if (stats.isDirectory()) {
|
|
188
|
+
await this.copyDirectory(sourcePath, targetPath, extractedFiles);
|
|
189
|
+
} else if (stats.isFile()) {
|
|
190
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
191
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
192
|
+
extractedFiles.push(relPath);
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// Skip files that don't exist or can't be accessed
|
|
196
|
+
console.warn(
|
|
197
|
+
`Warning: Could not extract ${relPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
// Extract all content (excluding .git directory)
|
|
203
|
+
await this.copyDirectory(sourceDir, targetDir, extractedFiles, [".git"]);
|
|
204
|
+
}
|
|
205
|
+
return extractedFiles;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Copy directory recursively
|
|
209
|
+
*/
|
|
210
|
+
async copyDirectory(source, target, fileList, excludeDirs = []) {
|
|
211
|
+
await fs.mkdir(target, { recursive: true });
|
|
212
|
+
const items = await fs.readdir(source);
|
|
213
|
+
for (const item of items) {
|
|
214
|
+
if (excludeDirs.includes(item)) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const sourcePath = path.join(source, item);
|
|
218
|
+
const targetPath = path.join(target, item);
|
|
219
|
+
const stats = await fs.stat(sourcePath);
|
|
220
|
+
if (stats.isDirectory()) {
|
|
221
|
+
await this.copyDirectory(sourcePath, targetPath, fileList, excludeDirs);
|
|
222
|
+
} else {
|
|
223
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
224
|
+
// Calculate relative path from the initial target directory
|
|
225
|
+
const relativePath = path.relative(target, targetPath);
|
|
226
|
+
fileList.push(relativePath);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Generate content hash for change detection
|
|
232
|
+
*/
|
|
233
|
+
async generateContentHash(targetDir, files) {
|
|
234
|
+
const hash = crypto.createHash("sha256");
|
|
235
|
+
// Sort files for consistent hashing
|
|
236
|
+
const sortedFiles = files.slice().sort();
|
|
237
|
+
for (const file of sortedFiles) {
|
|
238
|
+
const filePath = path.join(targetDir, file);
|
|
239
|
+
try {
|
|
240
|
+
const content = await fs.readFile(filePath);
|
|
241
|
+
hash.update(file); // Include filename
|
|
242
|
+
hash.update(content); // Include content
|
|
243
|
+
} catch (error) {
|
|
244
|
+
// Skip files that can't be read
|
|
245
|
+
console.warn(
|
|
246
|
+
`Warning: Could not hash ${file}: ${error instanceof Error ? error.message : String(error)}`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return hash.digest("hex");
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Clean up temporary directory
|
|
254
|
+
*/
|
|
255
|
+
async cleanupTempDirectory(tempDir) {
|
|
256
|
+
try {
|
|
257
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.warn(
|
|
260
|
+
`Warning: Could not clean up temp directory ${tempDir}: ${error instanceof Error ? error.message : String(error)}`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content loading and processing exports
|
|
3
|
+
*/
|
|
4
|
+
export { ContentLoader } from "./loader.js";
|
|
5
|
+
export { GitRepoLoader } from "./git-repo-loader.js";
|
|
6
|
+
export { DocumentationSiteLoader } from "./documentation-site-loader.js";
|
|
7
|
+
export { ApiDocumentationLoader } from "./api-documentation-loader.js";
|
|
8
|
+
export { ContentProcessor } from "./content-processor.js";
|
|
9
|
+
export { MetadataManager } from "./metadata-manager.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content loading and processing exports
|
|
3
|
+
*/
|
|
4
|
+
export { ContentLoader } from "./loader.js";
|
|
5
|
+
export { GitRepoLoader } from "./git-repo-loader.js";
|
|
6
|
+
export { DocumentationSiteLoader } from "./documentation-site-loader.js";
|
|
7
|
+
export { ApiDocumentationLoader } from "./api-documentation-loader.js";
|
|
8
|
+
export { ContentProcessor } from "./content-processor.js";
|
|
9
|
+
export { MetadataManager } from "./metadata-manager.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for content loaders
|
|
3
|
+
*/
|
|
4
|
+
import type { WebSourceConfig } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Result of a content loading operation
|
|
7
|
+
*/
|
|
8
|
+
export interface LoadResult {
|
|
9
|
+
/** Whether the operation was successful */
|
|
10
|
+
success: boolean;
|
|
11
|
+
/** Files that were created or updated */
|
|
12
|
+
files: string[];
|
|
13
|
+
/** Hash of the loaded content for change detection */
|
|
14
|
+
contentHash: string;
|
|
15
|
+
/** Error message if success is false */
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Abstract base class for loading content from different web sources
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class ContentLoader {
|
|
22
|
+
/**
|
|
23
|
+
* Load content from a web source to the specified target directory
|
|
24
|
+
* @param webSource - Configuration for the web source
|
|
25
|
+
* @param targetPath - Local directory to store the content
|
|
26
|
+
* @returns Promise with load result
|
|
27
|
+
*/
|
|
28
|
+
abstract load(
|
|
29
|
+
webSource: WebSourceConfig,
|
|
30
|
+
targetPath: string,
|
|
31
|
+
): Promise<LoadResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Check if this loader can handle the given web source type
|
|
34
|
+
* @param webSource - Web source configuration
|
|
35
|
+
* @returns True if this loader can handle the source type
|
|
36
|
+
*/
|
|
37
|
+
abstract canHandle(webSource: WebSourceConfig): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Validate the web source configuration for this loader
|
|
40
|
+
* @param webSource - Web source configuration
|
|
41
|
+
* @returns True if configuration is valid, error message if not
|
|
42
|
+
*/
|
|
43
|
+
abstract validateConfig(webSource: WebSourceConfig): true | string;
|
|
44
|
+
/**
|
|
45
|
+
* Get a unique identifier for the content (used for change detection)
|
|
46
|
+
* @param webSource - Web source configuration
|
|
47
|
+
* @returns Promise with content identifier
|
|
48
|
+
*/
|
|
49
|
+
abstract getContentId(webSource: WebSourceConfig): Promise<string>;
|
|
50
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata manager for tracking web source downloads
|
|
3
|
+
*/
|
|
4
|
+
import { DocsetMetadata, WebSourceMetadata } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Manager for docset metadata files
|
|
7
|
+
*/
|
|
8
|
+
export declare class MetadataManager {
|
|
9
|
+
/**
|
|
10
|
+
* Load metadata from a docset directory
|
|
11
|
+
* @param docsetPath - Path to the docset directory
|
|
12
|
+
* @returns Metadata object or null if not found
|
|
13
|
+
*/
|
|
14
|
+
loadMetadata(docsetPath: string): Promise<DocsetMetadata | null>;
|
|
15
|
+
/**
|
|
16
|
+
* Save metadata to a docset directory
|
|
17
|
+
* @param docsetPath - Path to the docset directory
|
|
18
|
+
* @param metadata - Metadata to save
|
|
19
|
+
*/
|
|
20
|
+
saveMetadata(docsetPath: string, metadata: DocsetMetadata): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Create initial metadata for a docset
|
|
23
|
+
* @param docsetId - ID of the docset
|
|
24
|
+
* @returns Initial metadata structure
|
|
25
|
+
*/
|
|
26
|
+
createInitialMetadata(docsetId: string): DocsetMetadata;
|
|
27
|
+
/**
|
|
28
|
+
* Update metadata for a specific web source
|
|
29
|
+
* @param metadata - Current metadata
|
|
30
|
+
* @param sourceUrl - URL of the web source
|
|
31
|
+
* @param sourceMetadata - New metadata for the source
|
|
32
|
+
* @returns Updated metadata
|
|
33
|
+
*/
|
|
34
|
+
updateSourceMetadata(
|
|
35
|
+
metadata: DocsetMetadata,
|
|
36
|
+
sourceUrl: string,
|
|
37
|
+
sourceMetadata: Partial<WebSourceMetadata>,
|
|
38
|
+
): DocsetMetadata;
|
|
39
|
+
/**
|
|
40
|
+
* Get metadata for a specific web source
|
|
41
|
+
* @param metadata - Metadata to search
|
|
42
|
+
* @param sourceUrl - URL of the web source
|
|
43
|
+
* @returns Source metadata or null if not found
|
|
44
|
+
*/
|
|
45
|
+
getSourceMetadata(
|
|
46
|
+
metadata: DocsetMetadata,
|
|
47
|
+
sourceUrl: string,
|
|
48
|
+
): WebSourceMetadata | null;
|
|
49
|
+
/**
|
|
50
|
+
* Check if metadata file exists
|
|
51
|
+
* @param docsetPath - Path to the docset directory
|
|
52
|
+
* @returns True if metadata file exists
|
|
53
|
+
*/
|
|
54
|
+
metadataExists(docsetPath: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Remove metadata for a specific web source
|
|
57
|
+
* @param metadata - Current metadata
|
|
58
|
+
* @param sourceUrl - URL of the web source to remove
|
|
59
|
+
* @returns Updated metadata
|
|
60
|
+
*/
|
|
61
|
+
removeSourceMetadata(
|
|
62
|
+
metadata: DocsetMetadata,
|
|
63
|
+
sourceUrl: string,
|
|
64
|
+
): DocsetMetadata;
|
|
65
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata manager for tracking web source downloads
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from "node:fs";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import { METADATA_FILENAME } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Manager for docset metadata files
|
|
9
|
+
*/
|
|
10
|
+
export class MetadataManager {
|
|
11
|
+
/**
|
|
12
|
+
* Load metadata from a docset directory
|
|
13
|
+
* @param docsetPath - Path to the docset directory
|
|
14
|
+
* @returns Metadata object or null if not found
|
|
15
|
+
*/
|
|
16
|
+
async loadMetadata(docsetPath) {
|
|
17
|
+
const metadataPath = path.join(docsetPath, METADATA_FILENAME);
|
|
18
|
+
try {
|
|
19
|
+
const content = await fs.readFile(metadataPath, "utf-8");
|
|
20
|
+
return JSON.parse(content);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error.code === "ENOENT") {
|
|
23
|
+
return null; // File doesn't exist
|
|
24
|
+
}
|
|
25
|
+
throw error; // Other errors should be propagated
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Save metadata to a docset directory
|
|
30
|
+
* @param docsetPath - Path to the docset directory
|
|
31
|
+
* @param metadata - Metadata to save
|
|
32
|
+
*/
|
|
33
|
+
async saveMetadata(docsetPath, metadata) {
|
|
34
|
+
await fs.mkdir(docsetPath, { recursive: true });
|
|
35
|
+
const metadataPath = path.join(docsetPath, METADATA_FILENAME);
|
|
36
|
+
const content = JSON.stringify(metadata, null, 2);
|
|
37
|
+
await fs.writeFile(metadataPath, content, "utf-8");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create initial metadata for a docset
|
|
41
|
+
* @param docsetId - ID of the docset
|
|
42
|
+
* @returns Initial metadata structure
|
|
43
|
+
*/
|
|
44
|
+
createInitialMetadata(docsetId) {
|
|
45
|
+
return {
|
|
46
|
+
version: "1.0",
|
|
47
|
+
docset_id: docsetId,
|
|
48
|
+
sources: [],
|
|
49
|
+
last_refresh: new Date().toISOString(),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Update metadata for a specific web source
|
|
54
|
+
* @param metadata - Current metadata
|
|
55
|
+
* @param sourceUrl - URL of the web source
|
|
56
|
+
* @param sourceMetadata - New metadata for the source
|
|
57
|
+
* @returns Updated metadata
|
|
58
|
+
*/
|
|
59
|
+
updateSourceMetadata(metadata, sourceUrl, sourceMetadata) {
|
|
60
|
+
const updatedMetadata = { ...metadata };
|
|
61
|
+
updatedMetadata.last_refresh = new Date().toISOString();
|
|
62
|
+
// Find existing source or create new one
|
|
63
|
+
const existingIndex = updatedMetadata.sources.findIndex(
|
|
64
|
+
(s) => s.url === sourceUrl,
|
|
65
|
+
);
|
|
66
|
+
if (existingIndex >= 0) {
|
|
67
|
+
// Update existing source - TypeScript knows it exists here
|
|
68
|
+
const existing = updatedMetadata.sources[existingIndex];
|
|
69
|
+
updatedMetadata.sources[existingIndex] = {
|
|
70
|
+
url: existing.url,
|
|
71
|
+
type: existing.type,
|
|
72
|
+
status: sourceMetadata.status ?? existing.status,
|
|
73
|
+
};
|
|
74
|
+
// Update optional fields if provided, preserve existing if not
|
|
75
|
+
const updated = updatedMetadata.sources[existingIndex];
|
|
76
|
+
if (sourceMetadata.last_fetched !== undefined) {
|
|
77
|
+
updated.last_fetched = sourceMetadata.last_fetched;
|
|
78
|
+
} else if (existing.last_fetched !== undefined) {
|
|
79
|
+
updated.last_fetched = existing.last_fetched;
|
|
80
|
+
}
|
|
81
|
+
if (sourceMetadata.content_hash !== undefined) {
|
|
82
|
+
updated.content_hash = sourceMetadata.content_hash;
|
|
83
|
+
} else if (existing.content_hash !== undefined) {
|
|
84
|
+
updated.content_hash = existing.content_hash;
|
|
85
|
+
}
|
|
86
|
+
if (sourceMetadata.files_created !== undefined) {
|
|
87
|
+
updated.files_created = sourceMetadata.files_created;
|
|
88
|
+
} else if (existing.files_created !== undefined) {
|
|
89
|
+
updated.files_created = existing.files_created;
|
|
90
|
+
}
|
|
91
|
+
if (sourceMetadata.error_message !== undefined) {
|
|
92
|
+
updated.error_message = sourceMetadata.error_message;
|
|
93
|
+
} else if (existing.error_message !== undefined) {
|
|
94
|
+
updated.error_message = existing.error_message;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
// Add new source - ensure required fields are present
|
|
98
|
+
if (!sourceMetadata.type) {
|
|
99
|
+
throw new Error("Source type is required for new metadata entries");
|
|
100
|
+
}
|
|
101
|
+
const newSource = {
|
|
102
|
+
url: sourceUrl,
|
|
103
|
+
type: sourceMetadata.type,
|
|
104
|
+
status: sourceMetadata.status || "pending",
|
|
105
|
+
};
|
|
106
|
+
// Add optional fields if provided
|
|
107
|
+
if (sourceMetadata.last_fetched !== undefined) {
|
|
108
|
+
newSource.last_fetched = sourceMetadata.last_fetched;
|
|
109
|
+
}
|
|
110
|
+
if (sourceMetadata.content_hash !== undefined) {
|
|
111
|
+
newSource.content_hash = sourceMetadata.content_hash;
|
|
112
|
+
}
|
|
113
|
+
if (sourceMetadata.files_created !== undefined) {
|
|
114
|
+
newSource.files_created = sourceMetadata.files_created;
|
|
115
|
+
}
|
|
116
|
+
if (sourceMetadata.error_message !== undefined) {
|
|
117
|
+
newSource.error_message = sourceMetadata.error_message;
|
|
118
|
+
}
|
|
119
|
+
updatedMetadata.sources.push(newSource);
|
|
120
|
+
}
|
|
121
|
+
return updatedMetadata;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get metadata for a specific web source
|
|
125
|
+
* @param metadata - Metadata to search
|
|
126
|
+
* @param sourceUrl - URL of the web source
|
|
127
|
+
* @returns Source metadata or null if not found
|
|
128
|
+
*/
|
|
129
|
+
getSourceMetadata(metadata, sourceUrl) {
|
|
130
|
+
return metadata.sources.find((s) => s.url === sourceUrl) || null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Check if metadata file exists
|
|
134
|
+
* @param docsetPath - Path to the docset directory
|
|
135
|
+
* @returns True if metadata file exists
|
|
136
|
+
*/
|
|
137
|
+
async metadataExists(docsetPath) {
|
|
138
|
+
const metadataPath = path.join(docsetPath, METADATA_FILENAME);
|
|
139
|
+
try {
|
|
140
|
+
await fs.access(metadataPath);
|
|
141
|
+
return true;
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Remove metadata for a specific web source
|
|
148
|
+
* @param metadata - Current metadata
|
|
149
|
+
* @param sourceUrl - URL of the web source to remove
|
|
150
|
+
* @returns Updated metadata
|
|
151
|
+
*/
|
|
152
|
+
removeSourceMetadata(metadata, sourceUrl) {
|
|
153
|
+
const updatedMetadata = { ...metadata };
|
|
154
|
+
updatedMetadata.last_refresh = new Date().toISOString();
|
|
155
|
+
updatedMetadata.sources = updatedMetadata.sources.filter(
|
|
156
|
+
(s) => s.url !== sourceUrl,
|
|
157
|
+
);
|
|
158
|
+
return updatedMetadata;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @codemcp/knowledge-core
|
|
3
|
+
*
|
|
4
|
+
* Core functionality for the agentic knowledge guidance system.
|
|
5
|
+
* Provides configuration discovery, path calculation, and template processing.
|
|
6
|
+
*/
|
|
7
|
+
export * from "./types.js";
|
|
8
|
+
export { findConfigPath, findConfigPathSync } from "./config/discovery.js";
|
|
9
|
+
export { loadConfig, loadConfigSync, validateConfig } from "./config/loader.js";
|
|
10
|
+
export { ConfigManager } from "./config/manager.js";
|
|
11
|
+
export { calculateLocalPath, formatPath, validatePath, validatePathSync, getRelativePath, ensureKnowledgeGitignore, ensureKnowledgeGitignoreSync, } from "./paths/calculator.js";
|
|
12
|
+
export { processTemplate, getEffectiveTemplate, validateTemplate, extractVariables, createTemplateContext, } from "./templates/processor.js";
|