agentic-knowledge-mcp 0.0.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/package.json +5 -5
  2. package/packages/cli/dist/cli.d.ts +2 -3
  3. package/packages/cli/dist/cli.js +15 -14
  4. package/packages/cli/dist/commands/create.js +13 -7
  5. package/packages/cli/dist/commands/init.js +117 -167
  6. package/packages/cli/dist/commands/refresh.js +248 -290
  7. package/packages/cli/dist/commands/status.js +185 -239
  8. package/packages/cli/dist/exports.d.ts +6 -0
  9. package/packages/cli/dist/exports.js +6 -0
  10. package/packages/cli/dist/index.d.ts +5 -4
  11. package/packages/cli/dist/index.js +30 -4
  12. package/packages/cli/package.json +9 -8
  13. package/packages/content-loader/dist/content/api-documentation-loader.js +27 -35
  14. package/packages/content-loader/dist/content/documentation-site-loader.js +27 -35
  15. package/packages/content-loader/dist/content/metadata-manager.d.ts +46 -56
  16. package/packages/content-loader/dist/content/metadata-manager.js +150 -147
  17. package/packages/content-loader/package.json +1 -1
  18. package/packages/core/dist/config/loader.js +162 -186
  19. package/packages/core/dist/config/manager.js +128 -160
  20. package/packages/core/dist/index.js +2 -16
  21. package/packages/core/dist/paths/calculator.d.ts +7 -0
  22. package/packages/core/dist/paths/calculator.js +143 -103
  23. package/packages/core/dist/paths/symlinks.d.ts +21 -0
  24. package/packages/core/dist/paths/symlinks.js +93 -0
  25. package/packages/core/dist/types.d.ts +35 -15
  26. package/packages/core/package.json +1 -1
  27. package/packages/mcp-server/dist/server.js +5 -5
  28. package/packages/mcp-server/package.json +2 -2
  29. package/packages/content-loader/dist/__tests__/debug-filtering.d.ts +0 -1
  30. package/packages/content-loader/dist/__tests__/debug-filtering.js +0 -17
  31. package/packages/content-loader/dist/__tests__/test-filtering.d.ts +0 -1
  32. package/packages/content-loader/dist/__tests__/test-filtering.js +0 -19
  33. package/packages/core/dist/content/api-documentation-loader.d.ts +0 -26
  34. package/packages/core/dist/content/api-documentation-loader.js +0 -45
  35. package/packages/core/dist/content/content-processor.d.ts +0 -44
  36. package/packages/core/dist/content/content-processor.js +0 -81
  37. package/packages/core/dist/content/documentation-site-loader.d.ts +0 -26
  38. package/packages/core/dist/content/documentation-site-loader.js +0 -45
  39. package/packages/core/dist/content/git-repo-loader.d.ts +0 -54
  40. package/packages/core/dist/content/git-repo-loader.js +0 -264
  41. package/packages/core/dist/content/index.d.ts +0 -9
  42. package/packages/core/dist/content/index.js +0 -9
  43. package/packages/core/dist/content/loader.d.ts +0 -50
  44. package/packages/core/dist/content/loader.js +0 -7
  45. package/packages/core/dist/content/metadata-manager.d.ts +0 -65
  46. package/packages/core/dist/content/metadata-manager.js +0 -160
@@ -10,171 +10,139 @@ import { findConfigPath } from "./discovery.js";
10
10
  * Central configuration manager for all config file operations
11
11
  */
12
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
- );
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 (this.configCache &&
24
+ now - this.configCache.loadTime < this.CONFIG_CACHE_TTL) {
25
+ return {
26
+ config: this.configCache.config,
27
+ configPath: this.configCache.configPath,
28
+ };
29
+ }
30
+ // Find and load fresh config
31
+ const configPath = await findConfigPath(startDir);
32
+ if (!configPath) {
33
+ throw new KnowledgeError(ErrorType.CONFIG_NOT_FOUND, "No configuration file found. Please ensure .knowledge/config.yaml exists in your project.", { searchPath: startDir || process.cwd() });
34
+ }
35
+ const config = await this.loadConfigFromPath(configPath);
36
+ // Cache the result
37
+ this.configCache = {
38
+ config,
39
+ configPath,
40
+ loadTime: now,
41
+ };
42
+ return { config, configPath };
40
43
  }
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
- );
44
+ /**
45
+ * Load configuration from specific file path
46
+ * @param configPath - Path to configuration file
47
+ * @returns Parsed configuration
48
+ */
49
+ async loadConfigFromPath(configPath) {
50
+ try {
51
+ const content = await fs.readFile(configPath, "utf-8");
52
+ const parsed = load(content);
53
+ if (!validateConfig(parsed)) {
54
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, "Configuration file contains invalid structure", { configPath, parsed });
55
+ }
56
+ return parsed;
57
+ }
58
+ catch (error) {
59
+ if (error instanceof KnowledgeError) {
60
+ throw error;
61
+ }
62
+ if (error.code === "ENOENT") {
63
+ throw new KnowledgeError(ErrorType.CONFIG_NOT_FOUND, `Configuration file not found: ${configPath}`, { configPath });
64
+ }
65
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, `Failed to parse configuration file: ${error instanceof Error ? error.message : String(error)}`, { configPath, originalError: error });
66
+ }
83
67
  }
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
- );
68
+ /**
69
+ * Save configuration to file
70
+ * @param config - Configuration to save
71
+ * @param configPath - Path to save to (optional, uses cached path if available)
72
+ */
73
+ async saveConfig(config, configPath) {
74
+ const targetPath = configPath || this.configCache?.configPath;
75
+ if (!targetPath) {
76
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, "No configuration path available. Load config first or provide explicit path.", { configPath });
77
+ }
78
+ // Validate config before saving
79
+ if (!validateConfig(config)) {
80
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, "Cannot save invalid configuration", { config });
81
+ }
82
+ try {
83
+ const yamlContent = dump(config, {
84
+ indent: 2,
85
+ lineWidth: 120,
86
+ noRefs: true,
87
+ });
88
+ await fs.writeFile(targetPath, yamlContent, "utf-8");
89
+ // Invalidate cache since config changed
90
+ this.configCache = null;
91
+ }
92
+ catch (error) {
93
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, `Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`, { configPath: targetPath, originalError: error });
94
+ }
98
95
  }
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
- );
96
+ /**
97
+ * Update paths for a specific docset with discovered files
98
+ * @param docsetId - ID of docset to update
99
+ * @param discoveredPaths - Array of file paths that were discovered
100
+ * @returns Updated configuration
101
+ */
102
+ async updateDocsetPaths(docsetId, discoveredPaths) {
103
+ const { config } = await this.loadConfig();
104
+ // Find the docset to update
105
+ const docset = config.docsets.find((d) => d.id === docsetId);
106
+ if (!docset) {
107
+ throw new KnowledgeError(ErrorType.CONFIG_INVALID, `Docset '${docsetId}' not found in configuration`, { docsetId, availableDocsets: config.docsets.map((d) => d.id) });
108
+ }
109
+ // Update sources with discovered paths
110
+ if (docset.sources) {
111
+ for (const source of docset.sources) {
112
+ if (source.type === "git_repo") {
113
+ // Add or update the paths in options
114
+ if (!source.paths) {
115
+ source.paths = [];
116
+ }
117
+ source.paths = discoveredPaths;
118
+ }
119
+ }
120
+ }
121
+ // Save updated configuration
122
+ await this.saveConfig(config);
123
+ return config;
106
124
  }
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
- );
125
+ /**
126
+ * Find configuration file path
127
+ * @param startDir - Directory to start searching from
128
+ * @returns Path to config file or null if not found
129
+ */
130
+ async findConfigPath(startDir) {
131
+ return await findConfigPath(startDir);
122
132
  }
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
- );
133
+ /**
134
+ * Clear configuration cache (useful for testing)
135
+ */
136
+ clearCache() {
137
+ this.configCache = null;
140
138
  }
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
- }
139
+ /**
140
+ * Check if configuration exists
141
+ * @param startDir - Directory to start searching from
142
+ * @returns True if config file exists
143
+ */
144
+ async configExists(startDir) {
145
+ const path = await this.findConfigPath(startDir);
146
+ return path !== null;
152
147
  }
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
148
  }
@@ -11,20 +11,6 @@ export { findConfigPath, findConfigPathSync } from "./config/discovery.js";
11
11
  export { loadConfig, loadConfigSync, validateConfig } from "./config/loader.js";
12
12
  export { ConfigManager } from "./config/manager.js";
13
13
  // Export path calculation utilities
14
- export {
15
- calculateLocalPath,
16
- formatPath,
17
- validatePath,
18
- validatePathSync,
19
- getRelativePath,
20
- ensureKnowledgeGitignore,
21
- ensureKnowledgeGitignoreSync,
22
- } from "./paths/calculator.js";
14
+ export { calculateLocalPath, formatPath, validatePath, validatePathSync, getRelativePath, ensureKnowledgeGitignore, ensureKnowledgeGitignoreSync, } from "./paths/calculator.js";
23
15
  // Export template processing
24
- export {
25
- processTemplate,
26
- getEffectiveTemplate,
27
- validateTemplate,
28
- extractVariables,
29
- createTemplateContext,
30
- } from "./templates/processor.js";
16
+ export { processTemplate, getEffectiveTemplate, validateTemplate, extractVariables, createTemplateContext, } from "./templates/processor.js";
@@ -9,6 +9,13 @@ import type { DocsetConfig } from "../types.js";
9
9
  * @returns Calculated local path
10
10
  */
11
11
  export declare function calculateLocalPath(docset: DocsetConfig, configPath: string): string;
12
+ /**
13
+ * Calculate the local path for a docset and create symlinks if needed
14
+ * @param docset - Docset configuration
15
+ * @param configPath - Path to the configuration file
16
+ * @returns Calculated local path
17
+ */
18
+ export declare function calculateLocalPathWithSymlinks(docset: DocsetConfig, configPath: string): Promise<string>;
12
19
  /**
13
20
  * Ensure .knowledge/.gitignore exists with docsets/ ignored
14
21
  * @param configPath - Path to the configuration file
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Path calculation utilities
3
3
  */
4
- import { resolve, dirname, isAbsolute, join, normalize } from "node:path";
4
+ import { resolve, dirname, isAbsolute, join, normalize, relative, } from "node:path";
5
5
  import { promises as fs } from "node:fs";
6
6
  import * as fsSync from "node:fs";
7
7
  import * as pathModule from "node:path";
8
8
  import { KnowledgeError, ErrorType } from "../types.js";
9
+ import { createSymlinks } from "./symlinks.js";
9
10
  /**
10
11
  * Calculate the local path for a docset
11
12
  * @param docset - Docset configuration
@@ -13,109 +14,146 @@ import { KnowledgeError, ErrorType } from "../types.js";
13
14
  * @returns Calculated local path
14
15
  */
15
16
  export function calculateLocalPath(docset, configPath) {
16
- try {
17
- const configDir = dirname(configPath); // This is the .knowledge directory
18
- // For docsets with web sources, use standardized path: .knowledge/docsets/{id}
19
- if (docset.web_sources && docset.web_sources.length > 0) {
20
- return join(configDir, "docsets", docset.id);
17
+ try {
18
+ const configDir = dirname(configPath); // This is the .knowledge directory
19
+ const projectRoot = dirname(configDir); // This is the project root
20
+ if (!docset.sources || docset.sources.length === 0) {
21
+ throw new Error(`Docset '${docset.id}' must have sources configured`);
22
+ }
23
+ // For now, use the first source to determine the path
24
+ const primarySource = docset.sources[0];
25
+ if (!primarySource) {
26
+ throw new Error(`Docset '${docset.id}' has no sources configured`);
27
+ }
28
+ if (primarySource.type === "local_folder") {
29
+ // For local folders, return relative path from project root
30
+ if (!primarySource.paths || primarySource.paths.length === 0) {
31
+ throw new Error(`Local folder source for docset '${docset.id}' has no paths configured`);
32
+ }
33
+ const firstPath = primarySource.paths[0];
34
+ if (!firstPath) {
35
+ throw new Error(`Local folder source for docset '${docset.id}' has empty path`);
36
+ }
37
+ if (isAbsolute(firstPath)) {
38
+ // If absolute path, return as-is
39
+ return normalize(firstPath);
40
+ }
41
+ // If relative path, resolve from project root and return relative
42
+ const resolvedPath = resolve(projectRoot, firstPath);
43
+ return relative(projectRoot, resolvedPath) || ".";
44
+ }
45
+ if (primarySource.type === "git_repo") {
46
+ // For git repos, use standardized path: .knowledge/docsets/{id}
47
+ return join(configDir, "docsets", docset.id);
48
+ }
49
+ throw new Error(`Unsupported source type: ${primarySource.type}`);
50
+ }
51
+ catch (error) {
52
+ throw new KnowledgeError(ErrorType.PATH_INVALID, `Failed to calculate local path for docset '${docset.id}': ${error.message}`, { docset, configPath, error });
53
+ }
54
+ }
55
+ /**
56
+ * Calculate the local path for a docset and create symlinks if needed
57
+ * @param docset - Docset configuration
58
+ * @param configPath - Path to the configuration file
59
+ * @returns Calculated local path
60
+ */
61
+ export async function calculateLocalPathWithSymlinks(docset, configPath) {
62
+ const configDir = dirname(configPath);
63
+ const projectRoot = dirname(configDir);
64
+ if (!docset.sources || docset.sources.length === 0) {
65
+ throw new Error(`Docset '${docset.id}' must have sources configured`);
66
+ }
67
+ const primarySource = docset.sources[0];
68
+ if (!primarySource) {
69
+ throw new Error(`Docset '${docset.id}' has no sources configured`);
21
70
  }
22
- // For traditional local docsets, use the configured local_path
23
- if (!docset.local_path) {
24
- throw new Error(
25
- "Docset must have either web_sources or local_path configured",
26
- );
71
+ if (primarySource.type === "local_folder") {
72
+ // Create symlinks in .knowledge/docsets/{id}/
73
+ const symlinkDir = join(configDir, "docsets", docset.id);
74
+ if (!primarySource.paths || primarySource.paths.length === 0) {
75
+ throw new Error(`Local folder source for docset '${docset.id}' has no paths configured`);
76
+ }
77
+ try {
78
+ await createSymlinks(primarySource.paths, symlinkDir, projectRoot);
79
+ // Return relative path to symlink directory
80
+ return relative(projectRoot, symlinkDir) || ".";
81
+ }
82
+ catch (error) {
83
+ throw new KnowledgeError(ErrorType.PATH_INVALID, `Failed to create symlinks for docset '${docset.id}': ${error.message}`, { docset, configPath, error });
84
+ }
27
85
  }
28
- const projectRoot = dirname(configDir); // This is the project root
29
- // If the path is absolute, use it as-is
30
- if (isAbsolute(docset.local_path)) {
31
- return normalize(docset.local_path);
86
+ if (primarySource.type === "git_repo") {
87
+ // For git repos, use standardized path: .knowledge/docsets/{id}
88
+ return join(configDir, "docsets", docset.id);
32
89
  }
33
- // If relative, resolve relative to project root directory
34
- const resolvedPath = resolve(projectRoot, docset.local_path);
35
- return normalize(resolvedPath);
36
- } catch (error) {
37
- throw new KnowledgeError(
38
- ErrorType.PATH_INVALID,
39
- `Failed to calculate local path for docset '${docset.id}': ${error.message}`,
40
- { docset, configPath, error },
41
- );
42
- }
90
+ throw new Error(`Unsupported source type: ${primarySource.type}`);
43
91
  }
44
92
  /**
45
93
  * Ensure .knowledge/.gitignore exists with docsets/ ignored
46
94
  * @param configPath - Path to the configuration file
47
95
  */
48
96
  export async function ensureKnowledgeGitignore(configPath) {
49
- try {
50
- const configDir = dirname(configPath);
51
- const gitignorePath = join(configDir, ".gitignore");
52
- // Check if .gitignore already exists
53
97
  try {
54
- const content = await fs.readFile(gitignorePath, "utf-8");
55
- // Check if it already contains docsets/ ignore rule
56
- if (content.includes("docsets/")) {
57
- return; // Already configured
58
- }
59
- // Append to existing file
60
- await fs.appendFile(
61
- gitignorePath,
62
- "\n# Agentic Knowledge - Downloaded docsets\ndocsets/\n",
63
- );
64
- } catch (error) {
65
- // File doesn't exist, create it
66
- if (error.code === "ENOENT") {
67
- await fs.writeFile(
68
- gitignorePath,
69
- "# Agentic Knowledge - Downloaded docsets\ndocsets/\n",
70
- );
71
- } else {
72
- throw error;
73
- }
98
+ const configDir = dirname(configPath);
99
+ const gitignorePath = join(configDir, ".gitignore");
100
+ // Check if .gitignore already exists
101
+ try {
102
+ const content = await fs.readFile(gitignorePath, "utf-8");
103
+ // Check if it already contains docsets/ ignore rule
104
+ if (content.includes("docsets/")) {
105
+ return; // Already configured
106
+ }
107
+ // Append to existing file
108
+ await fs.appendFile(gitignorePath, "\n# Agentic Knowledge - Downloaded docsets\ndocsets/\n");
109
+ }
110
+ catch (error) {
111
+ // File doesn't exist, create it
112
+ if (error.code === "ENOENT") {
113
+ await fs.writeFile(gitignorePath, "# Agentic Knowledge - Downloaded docsets\ndocsets/\n");
114
+ }
115
+ else {
116
+ throw error;
117
+ }
118
+ }
119
+ }
120
+ catch (error) {
121
+ // Don't fail the entire operation if gitignore creation fails
122
+ console.warn(`Warning: Could not create .knowledge/.gitignore: ${error.message}`);
74
123
  }
75
- } catch (error) {
76
- // Don't fail the entire operation if gitignore creation fails
77
- console.warn(
78
- `Warning: Could not create .knowledge/.gitignore: ${error.message}`,
79
- );
80
- }
81
124
  }
82
125
  /**
83
126
  * Synchronous version of ensureKnowledgeGitignore
84
127
  * @param configPath - Path to the configuration file
85
128
  */
86
129
  export function ensureKnowledgeGitignoreSync(configPath) {
87
- try {
88
- const configDir = dirname(configPath);
89
- const gitignorePath = join(configDir, ".gitignore");
90
- // Check if .gitignore already exists
91
130
  try {
92
- const content = fsSync.readFileSync(gitignorePath, "utf-8");
93
- // Check if it already contains docsets/ ignore rule
94
- if (content.includes("docsets/")) {
95
- return; // Already configured
96
- }
97
- // Append to existing file
98
- fsSync.appendFileSync(
99
- gitignorePath,
100
- "\n# Agentic Knowledge - Downloaded docsets\ndocsets/\n",
101
- );
102
- } catch (error) {
103
- // File doesn't exist, create it
104
- if (error.code === "ENOENT") {
105
- fsSync.writeFileSync(
106
- gitignorePath,
107
- "# Agentic Knowledge - Downloaded docsets\ndocsets/\n",
108
- );
109
- } else {
110
- throw error;
111
- }
131
+ const configDir = dirname(configPath);
132
+ const gitignorePath = join(configDir, ".gitignore");
133
+ // Check if .gitignore already exists
134
+ try {
135
+ const content = fsSync.readFileSync(gitignorePath, "utf-8");
136
+ // Check if it already contains docsets/ ignore rule
137
+ if (content.includes("docsets/")) {
138
+ return; // Already configured
139
+ }
140
+ // Append to existing file
141
+ fsSync.appendFileSync(gitignorePath, "\n# Agentic Knowledge - Downloaded docsets\ndocsets/\n");
142
+ }
143
+ catch (error) {
144
+ // File doesn't exist, create it
145
+ if (error.code === "ENOENT") {
146
+ fsSync.writeFileSync(gitignorePath, "# Agentic Knowledge - Downloaded docsets\ndocsets/\n");
147
+ }
148
+ else {
149
+ throw error;
150
+ }
151
+ }
152
+ }
153
+ catch (error) {
154
+ // Don't fail the entire operation if gitignore creation fails
155
+ console.warn(`Warning: Could not create .knowledge/.gitignore: ${error.message}`);
112
156
  }
113
- } catch (error) {
114
- // Don't fail the entire operation if gitignore creation fails
115
- console.warn(
116
- `Warning: Could not create .knowledge/.gitignore: ${error.message}`,
117
- );
118
- }
119
157
  }
120
158
  /**
121
159
  * Format a path for use in instructions (normalize separators, etc.)
@@ -123,11 +161,11 @@ export function ensureKnowledgeGitignoreSync(configPath) {
123
161
  * @returns Formatted path
124
162
  */
125
163
  export function formatPath(path) {
126
- // Normalize path separators and remove redundant separators
127
- const normalized = normalize(path);
128
- // Ensure path ends with separator for directory paths (if it looks like a directory)
129
- // This helps with search instructions that need to specify directories
130
- return normalized;
164
+ // Normalize path separators and remove redundant separators
165
+ const normalized = normalize(path);
166
+ // Ensure path ends with separator for directory paths (if it looks like a directory)
167
+ // This helps with search instructions that need to specify directories
168
+ return normalized;
131
169
  }
132
170
  /**
133
171
  * Validate that a path is accessible and exists
@@ -135,12 +173,13 @@ export function formatPath(path) {
135
173
  * @returns True if path exists and is accessible
136
174
  */
137
175
  export async function validatePath(path) {
138
- try {
139
- await fs.access(path);
140
- return true;
141
- } catch {
142
- return false;
143
- }
176
+ try {
177
+ await fs.access(path);
178
+ return true;
179
+ }
180
+ catch {
181
+ return false;
182
+ }
144
183
  }
145
184
  /**
146
185
  * Synchronous version of validatePath
@@ -148,12 +187,13 @@ export async function validatePath(path) {
148
187
  * @returns True if path exists and is accessible
149
188
  */
150
189
  export function validatePathSync(path) {
151
- try {
152
- fsSync.accessSync(path);
153
- return true;
154
- } catch {
155
- return false;
156
- }
190
+ try {
191
+ fsSync.accessSync(path);
192
+ return true;
193
+ }
194
+ catch {
195
+ return false;
196
+ }
157
197
  }
158
198
  /**
159
199
  * Get relative path from one location to another (useful for instructions)
@@ -162,5 +202,5 @@ export function validatePathSync(path) {
162
202
  * @returns Relative path from 'from' to 'to'
163
203
  */
164
204
  export function getRelativePath(from, to) {
165
- return pathModule.relative(from, to);
205
+ return pathModule.relative(from, to);
166
206
  }