@weavelogic/knowledge-graph-agent 0.7.4 → 0.8.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.
Files changed (86) hide show
  1. package/dist/_virtual/__vite-browser-external.js +2 -2
  2. package/dist/_virtual/__vite-browser-external.js.map +1 -1
  3. package/dist/_virtual/browser.js +2 -3
  4. package/dist/_virtual/browser.js.map +1 -1
  5. package/dist/_virtual/index10.js +2 -4
  6. package/dist/_virtual/index10.js.map +1 -1
  7. package/dist/_virtual/index11.js +2 -2
  8. package/dist/cli/commands/docs.d.ts.map +1 -1
  9. package/dist/cli/commands/docs.js +10 -5
  10. package/dist/cli/commands/docs.js.map +1 -1
  11. package/dist/cli/commands/hive-mind/add-frontmatter.js +2 -2
  12. package/dist/cli/commands/hive-mind/add-frontmatter.js.map +1 -1
  13. package/dist/cli/commands/hive-mind/analyze-links.js +2 -2
  14. package/dist/cli/commands/hive-mind/analyze-links.js.map +1 -1
  15. package/dist/cli/commands/hive-mind/find-connections.js +2 -2
  16. package/dist/cli/commands/hive-mind/find-connections.js.map +1 -1
  17. package/dist/cli/commands/hive-mind/validate-names.js +2 -2
  18. package/dist/cli/commands/hive-mind/validate-names.js.map +1 -1
  19. package/dist/graphql/server.js +2 -2
  20. package/dist/graphql/server.js.map +1 -1
  21. package/dist/mcp-server/tools/audit/index.d.ts +4 -0
  22. package/dist/mcp-server/tools/audit/index.d.ts.map +1 -1
  23. package/dist/node_modules/@typescript-eslint/project-service/dist/index.js +1 -1
  24. package/dist/node_modules/debug/src/browser.js +1 -1
  25. package/dist/node_modules/fdir/dist/index.js +14 -14
  26. package/dist/node_modules/fdir/dist/index.js.map +1 -1
  27. package/dist/node_modules/tinyglobby/dist/index.js +14 -14
  28. package/dist/node_modules/tinyglobby/dist/index.js.map +1 -1
  29. package/dist/node_modules/ts-api-utils/lib/index.js +1 -1
  30. package/dist/node_modules/typescript/lib/typescript.js +24 -24
  31. package/dist/node_modules/typescript/lib/typescript.js.map +1 -1
  32. package/dist/vector/services/embedding-service.js +1 -7
  33. package/dist/vector/services/embedding-service.js.map +1 -1
  34. package/package.json +2 -1
  35. package/dist/_virtual/browser2.js +0 -5
  36. package/dist/_virtual/browser2.js.map +0 -1
  37. package/dist/_virtual/index12.js +0 -5
  38. package/dist/_virtual/index12.js.map +0 -1
  39. package/dist/_virtual/ort-web.min.js +0 -8
  40. package/dist/_virtual/ort-web.min.js.map +0 -1
  41. package/dist/_virtual/ort-web.min2.js +0 -5
  42. package/dist/_virtual/ort-web.min2.js.map +0 -1
  43. package/dist/node_modules/@huggingface/jinja/dist/index.js +0 -118
  44. package/dist/node_modules/@huggingface/jinja/dist/index.js.map +0 -1
  45. package/dist/node_modules/@xenova/transformers/src/backends/onnx.js +0 -24
  46. package/dist/node_modules/@xenova/transformers/src/backends/onnx.js.map +0 -1
  47. package/dist/node_modules/@xenova/transformers/src/configs.js +0 -52
  48. package/dist/node_modules/@xenova/transformers/src/configs.js.map +0 -1
  49. package/dist/node_modules/@xenova/transformers/src/env.js +0 -35
  50. package/dist/node_modules/@xenova/transformers/src/env.js.map +0 -1
  51. package/dist/node_modules/@xenova/transformers/src/models.js +0 -3852
  52. package/dist/node_modules/@xenova/transformers/src/models.js.map +0 -1
  53. package/dist/node_modules/@xenova/transformers/src/tokenizers.js +0 -144
  54. package/dist/node_modules/@xenova/transformers/src/tokenizers.js.map +0 -1
  55. package/dist/node_modules/@xenova/transformers/src/utils/core.js +0 -52
  56. package/dist/node_modules/@xenova/transformers/src/utils/core.js.map +0 -1
  57. package/dist/node_modules/@xenova/transformers/src/utils/generation.js +0 -623
  58. package/dist/node_modules/@xenova/transformers/src/utils/generation.js.map +0 -1
  59. package/dist/node_modules/@xenova/transformers/src/utils/hub.js +0 -395
  60. package/dist/node_modules/@xenova/transformers/src/utils/hub.js.map +0 -1
  61. package/dist/node_modules/@xenova/transformers/src/utils/image.js +0 -12
  62. package/dist/node_modules/@xenova/transformers/src/utils/image.js.map +0 -1
  63. package/dist/node_modules/@xenova/transformers/src/utils/maths.js +0 -89
  64. package/dist/node_modules/@xenova/transformers/src/utils/maths.js.map +0 -1
  65. package/dist/node_modules/@xenova/transformers/src/utils/tensor.js +0 -750
  66. package/dist/node_modules/@xenova/transformers/src/utils/tensor.js.map +0 -1
  67. package/dist/node_modules/onnxruntime-common/dist/lib/backend-impl.js +0 -67
  68. package/dist/node_modules/onnxruntime-common/dist/lib/backend-impl.js.map +0 -1
  69. package/dist/node_modules/onnxruntime-common/dist/lib/env-impl.js +0 -24
  70. package/dist/node_modules/onnxruntime-common/dist/lib/env-impl.js.map +0 -1
  71. package/dist/node_modules/onnxruntime-common/dist/lib/env.js +0 -6
  72. package/dist/node_modules/onnxruntime-common/dist/lib/env.js.map +0 -1
  73. package/dist/node_modules/onnxruntime-common/dist/lib/index.js +0 -11
  74. package/dist/node_modules/onnxruntime-common/dist/lib/index.js.map +0 -1
  75. package/dist/node_modules/onnxruntime-common/dist/lib/inference-session-impl.js +0 -162
  76. package/dist/node_modules/onnxruntime-common/dist/lib/inference-session-impl.js.map +0 -1
  77. package/dist/node_modules/onnxruntime-common/dist/lib/inference-session.js +0 -6
  78. package/dist/node_modules/onnxruntime-common/dist/lib/inference-session.js.map +0 -1
  79. package/dist/node_modules/onnxruntime-common/dist/lib/tensor-impl.js +0 -393
  80. package/dist/node_modules/onnxruntime-common/dist/lib/tensor-impl.js.map +0 -1
  81. package/dist/node_modules/onnxruntime-common/dist/lib/tensor.js +0 -6
  82. package/dist/node_modules/onnxruntime-common/dist/lib/tensor.js.map +0 -1
  83. package/dist/node_modules/onnxruntime-web/dist/ort-web.min.js +0 -12919
  84. package/dist/node_modules/onnxruntime-web/dist/ort-web.min.js.map +0 -1
  85. package/dist/node_modules/ws/browser.js +0 -16
  86. package/dist/node_modules/ws/browser.js.map +0 -1
@@ -1,5 +1,5 @@
1
- const sharp = {};
1
+ const __viteBrowserExternal = {};
2
2
  export {
3
- sharp as default
3
+ __viteBrowserExternal as default
4
4
  };
5
5
  //# sourceMappingURL=__vite-browser-external.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"__vite-browser-external.js","sources":["../../__vite-browser-external"],"sourcesContent":["export default {}"],"names":[],"mappings":"AAAA,MAAA,QAAe,CAAA;"}
1
+ {"version":3,"file":"__vite-browser-external.js","sources":["../../__vite-browser-external"],"sourcesContent":["export default {}"],"names":[],"mappings":"AAAA,MAAA,wBAAe,CAAA;"}
@@ -1,6 +1,5 @@
1
- import { __require as requireBrowser } from "../node_modules/ws/browser.js";
2
- var browserExports = requireBrowser();
1
+ var browser = { exports: {} };
3
2
  export {
4
- browserExports as b
3
+ browser as __module
5
4
  };
6
5
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"browser.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,7 +1,5 @@
1
- import { getAugmentedNamespace } from "./_commonjsHelpers.js";
2
- import * as index from "../node_modules/onnxruntime-common/dist/lib/index.js";
3
- const require$$0 = /* @__PURE__ */ getAugmentedNamespace(index);
1
+ var dist = {};
4
2
  export {
5
- require$$0 as default
3
+ dist as __exports
6
4
  };
7
5
  //# sourceMappingURL=index10.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index10.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
1
+ {"version":3,"file":"index10.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +1,5 @@
1
- var dist = {};
1
+ var lib = {};
2
2
  export {
3
- dist as __exports
3
+ lib as __exports
4
4
  };
5
5
  //# sourceMappingURL=index11.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA6I3C"}
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/docs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAmJ3C"}
@@ -12,10 +12,9 @@ function createDocsCommand() {
12
12
  const projectRoot = validateProjectRoot(options.path);
13
13
  const docsPath = options.docs;
14
14
  validateDocsPath(projectRoot, docsPath);
15
- if (docsExist(projectRoot, docsPath) && !options.force) {
16
- spinner.warn(`Documentation already exists at ${docsPath}`);
17
- console.log(chalk.gray(" Use --force to reinitialize"));
18
- return;
15
+ const isExisting = docsExist(projectRoot, docsPath);
16
+ if (isExisting) {
17
+ spinner.text = "Adding missing files to existing documentation...";
19
18
  }
20
19
  const result = await initDocs({
21
20
  projectRoot,
@@ -24,7 +23,13 @@ function createDocsCommand() {
24
23
  detectFramework: options.detect !== false
25
24
  });
26
25
  if (result.success) {
27
- spinner.succeed("Documentation initialized!");
26
+ if (isExisting && result.filesCreated.length === 0) {
27
+ spinner.succeed("Documentation already complete - no new files needed");
28
+ } else if (isExisting) {
29
+ spinner.succeed(`Documentation updated - added ${result.filesCreated.length} missing files`);
30
+ } else {
31
+ spinner.succeed("Documentation initialized!");
32
+ }
28
33
  } else {
29
34
  spinner.warn("Documentation initialized with errors");
30
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"docs.js","sources":["../../../src/cli/commands/docs.ts"],"sourcesContent":["/**\n * Docs Command\n *\n * Initialize and manage documentation directory.\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { initDocs, docsExist, getDocsPath } from '../../generators/docs-init.js';\nimport { validateProjectRoot, validateDocsPath } from '../../core/security.js';\n\n/**\n * Create docs command\n */\nexport function createDocsCommand(): Command {\n const command = new Command('docs');\n\n command\n .description('Documentation management commands');\n\n // Init subcommand\n command\n .command('init')\n .description('Initialize documentation directory with weave-nn structure')\n .option('-p, --path <path>', 'Project root path', '.')\n .option('-d, --docs <path>', 'Docs directory path', 'docs')\n .option('-t, --template <template>', 'Template to use (default, minimal)')\n .option('--no-examples', 'Skip example files')\n .option('--no-detect', 'Skip framework detection')\n .option('-f, --force', 'Overwrite existing files')\n .action(async (options) => {\n const spinner = ora('Initializing documentation...').start();\n\n try {\n // Validate paths to prevent traversal attacks\n const projectRoot = validateProjectRoot(options.path);\n const docsPath = options.docs;\n validateDocsPath(projectRoot, docsPath); // Ensure docs stays within project\n\n // Check if docs already exist\n if (docsExist(projectRoot, docsPath) && !options.force) {\n spinner.warn(`Documentation already exists at ${docsPath}`);\n console.log(chalk.gray(' Use --force to reinitialize'));\n return;\n }\n\n const result = await initDocs({\n projectRoot,\n docsPath,\n includeExamples: options.examples !== false,\n detectFramework: options.detect !== false,\n });\n\n if (result.success) {\n spinner.succeed('Documentation initialized!');\n } else {\n spinner.warn('Documentation initialized with errors');\n }\n\n console.log();\n console.log(chalk.white(' Created:'));\n console.log(chalk.gray(` Path: ${result.docsPath}`));\n console.log(chalk.green(` Files: ${result.filesCreated.length}`));\n\n if (result.errors.length > 0) {\n console.log();\n console.log(chalk.yellow(' Errors:'));\n result.errors.forEach(err => {\n console.log(chalk.gray(` - ${err}`));\n });\n }\n\n console.log();\n console.log(chalk.cyan('Structure created:'));\n console.log(chalk.gray(`\n ${docsPath}/\n ├── README.md # Documentation home\n ├── PRIMITIVES.md # Technology primitives\n ├── MOC.md # Map of Content\n ├── concepts/ # Abstract concepts\n ├── components/ # Reusable components\n ├── services/ # Backend services\n ├── features/ # Product features\n ├── integrations/ # External integrations\n ├── standards/ # Coding standards\n ├── guides/ # How-to guides\n └── references/ # API references\n `));\n\n console.log(chalk.cyan('Next: ') + chalk.white('kg graph') + chalk.gray(' to generate knowledge graph'));\n console.log();\n\n } catch (error) {\n spinner.fail('Failed to initialize documentation');\n console.error(chalk.red(String(error)));\n process.exit(1);\n }\n });\n\n // Status subcommand\n command\n .command('status')\n .description('Show documentation status')\n .option('-p, --path <path>', 'Project root path', '.')\n .action(async (options) => {\n // Validate path to prevent traversal\n const projectRoot = validateProjectRoot(options.path);\n const docsPath = getDocsPath(projectRoot);\n\n if (!docsPath) {\n console.log(chalk.yellow(' No documentation directory found'));\n console.log(chalk.gray(' Run ') + chalk.cyan('kg docs init') + chalk.gray(' to create one'));\n return;\n }\n\n console.log(chalk.white('\\n Documentation Status\\n'));\n console.log(chalk.gray(' Path:'), chalk.white(docsPath));\n console.log();\n\n // Count files\n const fg = await import('fast-glob');\n const files = await fg.default('**/*.md', {\n cwd: docsPath,\n ignore: ['node_modules/**', '.git/**'],\n });\n\n console.log(chalk.gray(' Markdown files:'), chalk.white(files.length));\n\n // Check for key files\n const keyFiles = ['README.md', 'PRIMITIVES.md', 'MOC.md'];\n const fs = await import('fs');\n const path = await import('path');\n\n console.log();\n console.log(chalk.white(' Key Files:'));\n keyFiles.forEach(file => {\n const exists = fs.existsSync(path.join(docsPath, file));\n const icon = exists ? chalk.green('✓') : chalk.red('✗');\n console.log(` ${icon} ${file}`);\n });\n\n // Check directories\n const dirs = ['concepts', 'components', 'services', 'features', 'guides'];\n console.log();\n console.log(chalk.white(' Directories:'));\n dirs.forEach(dir => {\n const exists = fs.existsSync(path.join(docsPath, dir));\n const icon = exists ? chalk.green('✓') : chalk.gray('○');\n console.log(` ${icon} ${dir}/`);\n });\n\n console.log();\n });\n\n return command;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"docs.js","sources":["../../../src/cli/commands/docs.ts"],"sourcesContent":["/**\n * Docs Command\n *\n * Initialize and manage documentation directory.\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { initDocs, docsExist, getDocsPath } from '../../generators/docs-init.js';\nimport { validateProjectRoot, validateDocsPath } from '../../core/security.js';\n\n/**\n * Create docs command\n */\nexport function createDocsCommand(): Command {\n const command = new Command('docs');\n\n command\n .description('Documentation management commands');\n\n // Init subcommand\n command\n .command('init')\n .description('Initialize documentation directory with weave-nn structure')\n .option('-p, --path <path>', 'Project root path', '.')\n .option('-d, --docs <path>', 'Docs directory path', 'docs')\n .option('-t, --template <template>', 'Template to use (default, minimal)')\n .option('--no-examples', 'Skip example files')\n .option('--no-detect', 'Skip framework detection')\n .option('-f, --force', 'Overwrite existing files')\n .action(async (options) => {\n const spinner = ora('Initializing documentation...').start();\n\n try {\n // Validate paths to prevent traversal attacks\n const projectRoot = validateProjectRoot(options.path);\n const docsPath = options.docs;\n validateDocsPath(projectRoot, docsPath); // Ensure docs stays within project\n\n // Note: initDocs is additive - it only creates missing files/directories\n // The --force flag is for overwriting existing files if needed in the future\n const isExisting = docsExist(projectRoot, docsPath);\n if (isExisting) {\n spinner.text = 'Adding missing files to existing documentation...';\n }\n\n const result = await initDocs({\n projectRoot,\n docsPath,\n includeExamples: options.examples !== false,\n detectFramework: options.detect !== false,\n });\n\n if (result.success) {\n if (isExisting && result.filesCreated.length === 0) {\n spinner.succeed('Documentation already complete - no new files needed');\n } else if (isExisting) {\n spinner.succeed(`Documentation updated - added ${result.filesCreated.length} missing files`);\n } else {\n spinner.succeed('Documentation initialized!');\n }\n } else {\n spinner.warn('Documentation initialized with errors');\n }\n\n console.log();\n console.log(chalk.white(' Created:'));\n console.log(chalk.gray(` Path: ${result.docsPath}`));\n console.log(chalk.green(` Files: ${result.filesCreated.length}`));\n\n if (result.errors.length > 0) {\n console.log();\n console.log(chalk.yellow(' Errors:'));\n result.errors.forEach(err => {\n console.log(chalk.gray(` - ${err}`));\n });\n }\n\n console.log();\n console.log(chalk.cyan('Structure created:'));\n console.log(chalk.gray(`\n ${docsPath}/\n ├── README.md # Documentation home\n ├── PRIMITIVES.md # Technology primitives\n ├── MOC.md # Map of Content\n ├── concepts/ # Abstract concepts\n ├── components/ # Reusable components\n ├── services/ # Backend services\n ├── features/ # Product features\n ├── integrations/ # External integrations\n ├── standards/ # Coding standards\n ├── guides/ # How-to guides\n └── references/ # API references\n `));\n\n console.log(chalk.cyan('Next: ') + chalk.white('kg graph') + chalk.gray(' to generate knowledge graph'));\n console.log();\n\n } catch (error) {\n spinner.fail('Failed to initialize documentation');\n console.error(chalk.red(String(error)));\n process.exit(1);\n }\n });\n\n // Status subcommand\n command\n .command('status')\n .description('Show documentation status')\n .option('-p, --path <path>', 'Project root path', '.')\n .action(async (options) => {\n // Validate path to prevent traversal\n const projectRoot = validateProjectRoot(options.path);\n const docsPath = getDocsPath(projectRoot);\n\n if (!docsPath) {\n console.log(chalk.yellow(' No documentation directory found'));\n console.log(chalk.gray(' Run ') + chalk.cyan('kg docs init') + chalk.gray(' to create one'));\n return;\n }\n\n console.log(chalk.white('\\n Documentation Status\\n'));\n console.log(chalk.gray(' Path:'), chalk.white(docsPath));\n console.log();\n\n // Count files\n const fg = await import('fast-glob');\n const files = await fg.default('**/*.md', {\n cwd: docsPath,\n ignore: ['node_modules/**', '.git/**'],\n });\n\n console.log(chalk.gray(' Markdown files:'), chalk.white(files.length));\n\n // Check for key files\n const keyFiles = ['README.md', 'PRIMITIVES.md', 'MOC.md'];\n const fs = await import('fs');\n const path = await import('path');\n\n console.log();\n console.log(chalk.white(' Key Files:'));\n keyFiles.forEach(file => {\n const exists = fs.existsSync(path.join(docsPath, file));\n const icon = exists ? chalk.green('✓') : chalk.red('✗');\n console.log(` ${icon} ${file}`);\n });\n\n // Check directories\n const dirs = ['concepts', 'components', 'services', 'features', 'guides'];\n console.log();\n console.log(chalk.white(' Directories:'));\n dirs.forEach(dir => {\n const exists = fs.existsSync(path.join(docsPath, dir));\n const icon = exists ? chalk.green('✓') : chalk.gray('○');\n console.log(` ${icon} ${dir}/`);\n });\n\n console.log();\n });\n\n return command;\n}\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import * as path from "path";
4
4
  import { writeFile, readFile } from "fs/promises";
5
- import { glob } from "fast-glob";
5
+ import fg from "fast-glob";
6
6
  import matter from "gray-matter";
7
7
  import * as yaml from "js-yaml";
8
8
  class FrontmatterEnricher {
@@ -11,7 +11,7 @@ class FrontmatterEnricher {
11
11
  */
12
12
  async enrichVault(vaultPath, options = {}) {
13
13
  const resolvedPath = path.resolve(vaultPath);
14
- const files = await glob("**/*.md", {
14
+ const files = await fg("**/*.md", {
15
15
  cwd: resolvedPath,
16
16
  ignore: ["node_modules/**", ".git/**", "dist/**"],
17
17
  absolute: false
@@ -1 +1 @@
1
- {"version":3,"file":"add-frontmatter.js","sources":["../../../../src/cli/commands/hive-mind/add-frontmatter.ts"],"sourcesContent":["/**\n * Hive Mind - Frontmatter Enricher\n *\n * Adds or enriches YAML frontmatter in markdown files to improve discoverability\n * and enable better linking between documents.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, writeFile } from 'fs/promises';\nimport { glob } from 'fast-glob';\nimport matter from 'gray-matter';\nimport * as yaml from 'js-yaml';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EnrichOptions {\n overwrite?: boolean;\n tags?: boolean;\n aliases?: boolean;\n links?: boolean;\n dryRun?: boolean;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface FrontmatterTemplate {\n title: string;\n created: string;\n modified: string;\n tags: string[];\n aliases: string[];\n links: string[];\n type: string;\n status: string;\n description?: string;\n}\n\nexport interface EnrichedFile {\n file: string;\n added: string[];\n updated: string[];\n frontmatter: FrontmatterTemplate;\n}\n\nexport interface EnrichResult {\n enriched: EnrichedFile[];\n skipped: string[];\n errors: Array<{ file: string; error: string }>;\n statistics: {\n totalFiles: number;\n enrichedCount: number;\n skippedCount: number;\n errorCount: number;\n tagsAdded: number;\n aliasesAdded: number;\n linksExtracted: number;\n };\n}\n\n// ============================================================================\n// Frontmatter Enricher Class\n// ============================================================================\n\nexport class FrontmatterEnricher {\n /**\n * Enrich frontmatter for all files in a vault\n */\n async enrichVault(vaultPath: string, options: EnrichOptions = {}): Promise<EnrichResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await glob('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n const enriched: EnrichedFile[] = [];\n const skipped: string[] = [];\n const errors: Array<{ file: string; error: string }> = [];\n let tagsAdded = 0;\n let aliasesAdded = 0;\n let linksExtracted = 0;\n\n for (const file of files) {\n try {\n const result = await this.enrichFile(\n path.join(resolvedPath, file),\n file,\n options\n );\n\n if (result) {\n enriched.push(result);\n tagsAdded += result.frontmatter.tags.length;\n aliasesAdded += result.frontmatter.aliases.length;\n linksExtracted += result.frontmatter.links.length;\n } else {\n skipped.push(file);\n }\n } catch (error) {\n errors.push({\n file,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n\n return {\n enriched,\n skipped,\n errors,\n statistics: {\n totalFiles: files.length,\n enrichedCount: enriched.length,\n skippedCount: skipped.length,\n errorCount: errors.length,\n tagsAdded,\n aliasesAdded,\n linksExtracted,\n },\n };\n }\n\n /**\n * Enrich a single file's frontmatter\n */\n async enrichFile(\n filePath: string,\n relativePath: string,\n options: EnrichOptions\n ): Promise<EnrichedFile | null> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = matter(content);\n\n const existingFm = parsed.data as Partial<FrontmatterTemplate>;\n const hasExistingFm = Object.keys(existingFm).length > 0;\n\n // Skip if has frontmatter and not overwriting\n if (hasExistingFm && !options.overwrite) {\n // Check if we need to add anything\n const needsTags = options.tags && (!existingFm.tags || existingFm.tags.length === 0);\n const needsAliases = options.aliases && (!existingFm.aliases || existingFm.aliases.length === 0);\n const needsLinks = options.links && (!existingFm.links || existingFm.links.length === 0);\n\n if (!needsTags && !needsAliases && !needsLinks && existingFm.title) {\n return null;\n }\n }\n\n // Extract metadata from content\n const metadata = this.extractMetadata(parsed.content, relativePath);\n\n // Build new frontmatter\n const added: string[] = [];\n const updated: string[] = [];\n const newFm: FrontmatterTemplate = {\n title: existingFm.title || metadata.title,\n created: existingFm.created || metadata.created,\n modified: new Date().toISOString().split('T')[0],\n tags: [],\n aliases: [],\n links: [],\n type: existingFm.type || metadata.type,\n status: existingFm.status || 'active',\n };\n\n // Track what was added/updated\n if (!existingFm.title) added.push('title');\n if (!existingFm.created) added.push('created');\n if (!existingFm.type) added.push('type');\n if (!existingFm.status) added.push('status');\n updated.push('modified');\n\n // Handle tags\n if (options.tags !== false) {\n const existingTags = existingFm.tags || [];\n const extractedTags = metadata.tags;\n const mergedTags = [...new Set([...existingTags, ...extractedTags])];\n newFm.tags = mergedTags;\n\n const newTagCount = mergedTags.length - existingTags.length;\n if (newTagCount > 0) added.push(`${newTagCount} tags`);\n }\n\n // Handle aliases\n if (options.aliases !== false) {\n const existingAliases = existingFm.aliases || [];\n const extractedAliases = metadata.aliases;\n const mergedAliases = [...new Set([...existingAliases, ...extractedAliases])];\n newFm.aliases = mergedAliases;\n\n const newAliasCount = mergedAliases.length - existingAliases.length;\n if (newAliasCount > 0) added.push(`${newAliasCount} aliases`);\n }\n\n // Handle links\n if (options.links !== false) {\n const existingLinks = existingFm.links || [];\n const extractedLinks = metadata.links;\n const mergedLinks = [...new Set([...existingLinks, ...extractedLinks])];\n newFm.links = mergedLinks;\n\n const newLinkCount = mergedLinks.length - existingLinks.length;\n if (newLinkCount > 0) added.push(`${newLinkCount} links`);\n }\n\n // Add description if missing\n if (!existingFm.description && metadata.description) {\n newFm.description = metadata.description;\n added.push('description');\n }\n\n // Write file if not dry run\n if (!options.dryRun) {\n const newContent = this.buildMarkdownWithFrontmatter(newFm, parsed.content);\n await writeFile(filePath, newContent);\n }\n\n return {\n file: relativePath,\n added,\n updated,\n frontmatter: newFm,\n };\n }\n\n /**\n * Extract metadata from content and filename\n */\n private extractMetadata(content: string, filename: string): {\n title: string;\n created: string;\n type: string;\n tags: string[];\n aliases: string[];\n links: string[];\n description?: string;\n } {\n const basename = path.basename(filename, '.md');\n\n // Extract title from first heading or filename\n let title = basename;\n const headingMatch = content.match(/^#\\s+(.+)$/m);\n if (headingMatch) {\n title = headingMatch[1].trim();\n }\n\n // Generate aliases from title variations\n const aliases = this.generateAliases(title, basename);\n\n // Extract tags from hashtags in content\n const tags = this.extractHashtags(content);\n\n // Infer type from content and filename\n const type = this.inferType(content, basename);\n\n // Extract wiki-links\n const links = this.extractWikiLinks(content);\n\n // Extract first paragraph as description\n const description = this.extractDescription(content);\n\n // Use current date for created\n const created = new Date().toISOString().split('T')[0];\n\n return { title, created, type, tags, aliases, links, description };\n }\n\n /**\n * Generate aliases from title\n */\n private generateAliases(title: string, filename: string): string[] {\n const aliases: string[] = [];\n\n // Add filename if different from title\n const cleanFilename = filename.replace(/-/g, ' ').toLowerCase();\n const cleanTitle = title.toLowerCase();\n if (cleanFilename !== cleanTitle) {\n // Convert filename to readable form\n const readableFilename = filename\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n if (readableFilename !== title) {\n aliases.push(readableFilename);\n }\n }\n\n // Add lowercase version if title has caps\n if (title !== title.toLowerCase() && !aliases.includes(title.toLowerCase())) {\n aliases.push(title.toLowerCase());\n }\n\n // Add acronym if title has multiple words\n const words = title.split(/\\s+/);\n if (words.length >= 2 && words.length <= 5) {\n const acronym = words\n .map(w => w[0])\n .filter(c => c && /[A-Z]/.test(c))\n .join('');\n if (acronym.length >= 2 && !aliases.includes(acronym)) {\n aliases.push(acronym);\n }\n }\n\n return aliases.slice(0, 5); // Limit to 5 aliases\n }\n\n /**\n * Extract hashtags from content\n */\n private extractHashtags(content: string): string[] {\n const tags = new Set<string>();\n\n // Match #tag patterns (but not #headings)\n const regex = /(?:^|\\s)#([a-zA-Z][a-zA-Z0-9_-]*)/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const tag = match[1].toLowerCase();\n if (tag.length > 1 && tag.length < 30) {\n tags.add(tag);\n }\n }\n\n // Also infer tags from content keywords\n const inferredTags = this.inferTagsFromContent(content);\n for (const tag of inferredTags) {\n tags.add(tag);\n }\n\n return Array.from(tags).slice(0, 10); // Limit to 10 tags\n }\n\n /**\n * Infer tags from content analysis\n */\n private inferTagsFromContent(content: string): string[] {\n const tags: string[] = [];\n const lowerContent = content.toLowerCase();\n\n // Common topic patterns\n const patterns: Array<{ pattern: RegExp; tag: string }> = [\n { pattern: /\\b(api|endpoint|rest|graphql)\\b/i, tag: 'api' },\n { pattern: /\\b(database|sql|postgres|mysql|mongo)\\b/i, tag: 'database' },\n { pattern: /\\b(test|testing|jest|vitest|unittest)\\b/i, tag: 'testing' },\n { pattern: /\\b(docker|container|kubernetes|k8s)\\b/i, tag: 'devops' },\n { pattern: /\\b(security|auth|authentication|oauth)\\b/i, tag: 'security' },\n { pattern: /\\b(performance|optimization|cache)\\b/i, tag: 'performance' },\n { pattern: /\\b(react|vue|angular|frontend)\\b/i, tag: 'frontend' },\n { pattern: /\\b(node|express|backend|server)\\b/i, tag: 'backend' },\n { pattern: /\\b(typescript|javascript|python|rust)\\b/i, tag: 'programming' },\n { pattern: /\\b(guide|tutorial|howto|how-to)\\b/i, tag: 'guide' },\n { pattern: /\\b(architecture|design|pattern)\\b/i, tag: 'architecture' },\n { pattern: /\\b(config|configuration|setup)\\b/i, tag: 'configuration' },\n ];\n\n for (const { pattern, tag } of patterns) {\n if (pattern.test(lowerContent)) {\n tags.push(tag);\n }\n }\n\n return tags;\n }\n\n /**\n * Infer document type from content\n */\n private inferType(content: string, filename: string): string {\n const lowerContent = content.toLowerCase();\n const lowerFilename = filename.toLowerCase();\n\n // Check filename patterns\n if (lowerFilename.includes('readme') || lowerFilename.includes('index')) {\n return 'guide';\n }\n if (lowerFilename.includes('api') || lowerFilename.includes('endpoint')) {\n return 'technical';\n }\n if (lowerFilename.includes('standard') || lowerFilename.includes('convention')) {\n return 'standard';\n }\n\n // Check content patterns\n if (/```[a-z]+\\n/i.test(content)) {\n return 'technical';\n }\n if (/##\\s*installation|##\\s*getting started|##\\s*usage/i.test(content)) {\n return 'guide';\n }\n if (/##\\s*api|##\\s*endpoints|##\\s*methods/i.test(content)) {\n return 'technical';\n }\n if (/##\\s*overview|##\\s*introduction|##\\s*background/i.test(content)) {\n return 'concept';\n }\n\n return 'concept'; // Default type\n }\n\n /**\n * Extract wiki-links from content\n */\n private extractWikiLinks(content: string): string[] {\n const links = new Set<string>();\n const regex = /\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]+)?\\]\\]/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[1].trim();\n if (link) {\n links.add(link);\n }\n }\n\n return Array.from(links);\n }\n\n /**\n * Extract first paragraph as description\n */\n private extractDescription(content: string): string | undefined {\n // Skip headings and find first paragraph\n const lines = content.split('\\n');\n let foundContent = false;\n const paragraphLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines at start\n if (!foundContent && !trimmed) continue;\n\n // Skip headings\n if (trimmed.startsWith('#')) {\n if (foundContent) break;\n continue;\n }\n\n // Skip code blocks\n if (trimmed.startsWith('```')) continue;\n\n // Found content\n if (trimmed) {\n foundContent = true;\n paragraphLines.push(trimmed);\n } else if (foundContent) {\n // End of paragraph\n break;\n }\n }\n\n const description = paragraphLines.join(' ').slice(0, 200);\n return description || undefined;\n }\n\n /**\n * Build markdown content with frontmatter\n */\n private buildMarkdownWithFrontmatter(fm: FrontmatterTemplate, content: string): string {\n // Clean frontmatter - remove empty arrays and undefined values\n const cleanFm: Record<string, unknown> = {};\n\n if (fm.title) cleanFm.title = fm.title;\n if (fm.description) cleanFm.description = fm.description;\n if (fm.type) cleanFm.type = fm.type;\n if (fm.status) cleanFm.status = fm.status;\n if (fm.created) cleanFm.created = fm.created;\n if (fm.modified) cleanFm.modified = fm.modified;\n if (fm.tags && fm.tags.length > 0) cleanFm.tags = fm.tags;\n if (fm.aliases && fm.aliases.length > 0) cleanFm.aliases = fm.aliases;\n if (fm.links && fm.links.length > 0) cleanFm.links = fm.links;\n\n const yamlStr = yaml.dump(cleanFm, {\n lineWidth: -1,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n return `---\\n${yamlStr}---\\n\\n${content.trim()}\\n`;\n }\n\n /**\n * Generate report\n */\n generateReport(result: EnrichResult): string {\n const lines: string[] = [];\n\n lines.push('# Frontmatter Enrichment Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n lines.push('');\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.statistics.totalFiles} |`);\n lines.push(`| Enriched | ${result.statistics.enrichedCount} |`);\n lines.push(`| Skipped | ${result.statistics.skippedCount} |`);\n lines.push(`| Errors | ${result.statistics.errorCount} |`);\n lines.push(`| Tags Added | ${result.statistics.tagsAdded} |`);\n lines.push(`| Aliases Added | ${result.statistics.aliasesAdded} |`);\n lines.push(`| Links Extracted | ${result.statistics.linksExtracted} |`);\n lines.push('');\n\n if (result.enriched.length > 0) {\n lines.push('## Enriched Files\\n');\n for (const { file, added } of result.enriched.slice(0, 50)) {\n if (added.length > 0) {\n lines.push(`- \\`${file}\\`: ${added.join(', ')}`);\n }\n }\n if (result.enriched.length > 50) {\n lines.push(`\\n*... and ${result.enriched.length - 50} more*`);\n }\n lines.push('');\n }\n\n if (result.errors.length > 0) {\n lines.push('## Errors\\n');\n for (const { file, error } of result.errors) {\n lines.push(`- \\`${file}\\`: ${error}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createAddFrontmatterCommand(): Command {\n const command = new Command('add-frontmatter')\n .description('Add/enrich YAML frontmatter in markdown files')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('--overwrite', 'Overwrite existing frontmatter fields')\n .option('--tags', 'Auto-generate tags from content (default: true)')\n .option('--no-tags', 'Skip tag generation')\n .option('--aliases', 'Generate aliases from title (default: true)')\n .option('--no-aliases', 'Skip alias generation')\n .option('--links', 'Extract wiki-links to frontmatter (default: true)')\n .option('--no-links', 'Skip link extraction')\n .option('--dry-run', 'Preview changes without writing files')\n .option('-o, --output <file>', 'Output file for report')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: EnrichOptions) => {\n const enricher = new FrontmatterEnricher();\n\n const mode = options.dryRun ? ' (dry run)' : '';\n console.log(chalk.cyan(`\\nEnriching frontmatter${mode}...\\n`));\n\n try {\n const result = await enricher.enrichVault(vaultPath, options);\n\n if (options.json) {\n if (options.output) {\n await writeFile(options.output, JSON.stringify(result, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n } else {\n // Display summary\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.statistics.totalFiles}`));\n console.log(chalk.green(` Enriched: ${result.statistics.enrichedCount}`));\n console.log(chalk.gray(` Skipped: ${result.statistics.skippedCount}`));\n if (result.statistics.errorCount > 0) {\n console.log(chalk.red(` Errors: ${result.statistics.errorCount}`));\n }\n console.log('');\n\n console.log(chalk.bold('Additions:'));\n console.log(chalk.white(` Tags Added: ${result.statistics.tagsAdded}`));\n console.log(chalk.white(` Aliases Added: ${result.statistics.aliasesAdded}`));\n console.log(chalk.white(` Links Extracted: ${result.statistics.linksExtracted}`));\n console.log('');\n\n if (options.verbose && result.enriched.length > 0) {\n console.log(chalk.bold('Enriched Files:'));\n for (const { file, added } of result.enriched.slice(0, 20)) {\n if (added.length > 0) {\n console.log(chalk.green(` ${file}`));\n console.log(chalk.gray(` Added: ${added.join(', ')}`));\n }\n }\n if (result.enriched.length > 20) {\n console.log(chalk.gray(` ... and ${result.enriched.length - 20} more`));\n }\n console.log('');\n }\n\n if (result.errors.length > 0) {\n console.log(chalk.bold(chalk.red('Errors:')));\n for (const { file, error } of result.errors.slice(0, 10)) {\n console.log(chalk.red(` ${file}: ${error}`));\n }\n if (result.errors.length > 10) {\n console.log(chalk.gray(` ... and ${result.errors.length - 10} more`));\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output && !options.json) {\n const report = enricher.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show dry-run notice\n if (options.dryRun) {\n console.log(chalk.yellow('Dry run complete - no files were modified.'));\n console.log(chalk.gray('Remove --dry-run to apply changes.\\n'));\n }\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createAddFrontmatterCommand;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"add-frontmatter.js","sources":["../../../../src/cli/commands/hive-mind/add-frontmatter.ts"],"sourcesContent":["/**\n * Hive Mind - Frontmatter Enricher\n *\n * Adds or enriches YAML frontmatter in markdown files to improve discoverability\n * and enable better linking between documents.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, writeFile } from 'fs/promises';\nimport fg from 'fast-glob';\nimport matter from 'gray-matter';\nimport * as yaml from 'js-yaml';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface EnrichOptions {\n overwrite?: boolean;\n tags?: boolean;\n aliases?: boolean;\n links?: boolean;\n dryRun?: boolean;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface FrontmatterTemplate {\n title: string;\n created: string;\n modified: string;\n tags: string[];\n aliases: string[];\n links: string[];\n type: string;\n status: string;\n description?: string;\n}\n\nexport interface EnrichedFile {\n file: string;\n added: string[];\n updated: string[];\n frontmatter: FrontmatterTemplate;\n}\n\nexport interface EnrichResult {\n enriched: EnrichedFile[];\n skipped: string[];\n errors: Array<{ file: string; error: string }>;\n statistics: {\n totalFiles: number;\n enrichedCount: number;\n skippedCount: number;\n errorCount: number;\n tagsAdded: number;\n aliasesAdded: number;\n linksExtracted: number;\n };\n}\n\n// ============================================================================\n// Frontmatter Enricher Class\n// ============================================================================\n\nexport class FrontmatterEnricher {\n /**\n * Enrich frontmatter for all files in a vault\n */\n async enrichVault(vaultPath: string, options: EnrichOptions = {}): Promise<EnrichResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await fg('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n const enriched: EnrichedFile[] = [];\n const skipped: string[] = [];\n const errors: Array<{ file: string; error: string }> = [];\n let tagsAdded = 0;\n let aliasesAdded = 0;\n let linksExtracted = 0;\n\n for (const file of files) {\n try {\n const result = await this.enrichFile(\n path.join(resolvedPath, file),\n file,\n options\n );\n\n if (result) {\n enriched.push(result);\n tagsAdded += result.frontmatter.tags.length;\n aliasesAdded += result.frontmatter.aliases.length;\n linksExtracted += result.frontmatter.links.length;\n } else {\n skipped.push(file);\n }\n } catch (error) {\n errors.push({\n file,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n\n return {\n enriched,\n skipped,\n errors,\n statistics: {\n totalFiles: files.length,\n enrichedCount: enriched.length,\n skippedCount: skipped.length,\n errorCount: errors.length,\n tagsAdded,\n aliasesAdded,\n linksExtracted,\n },\n };\n }\n\n /**\n * Enrich a single file's frontmatter\n */\n async enrichFile(\n filePath: string,\n relativePath: string,\n options: EnrichOptions\n ): Promise<EnrichedFile | null> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = matter(content);\n\n const existingFm = parsed.data as Partial<FrontmatterTemplate>;\n const hasExistingFm = Object.keys(existingFm).length > 0;\n\n // Skip if has frontmatter and not overwriting\n if (hasExistingFm && !options.overwrite) {\n // Check if we need to add anything\n const needsTags = options.tags && (!existingFm.tags || existingFm.tags.length === 0);\n const needsAliases = options.aliases && (!existingFm.aliases || existingFm.aliases.length === 0);\n const needsLinks = options.links && (!existingFm.links || existingFm.links.length === 0);\n\n if (!needsTags && !needsAliases && !needsLinks && existingFm.title) {\n return null;\n }\n }\n\n // Extract metadata from content\n const metadata = this.extractMetadata(parsed.content, relativePath);\n\n // Build new frontmatter\n const added: string[] = [];\n const updated: string[] = [];\n const newFm: FrontmatterTemplate = {\n title: existingFm.title || metadata.title,\n created: existingFm.created || metadata.created,\n modified: new Date().toISOString().split('T')[0],\n tags: [],\n aliases: [],\n links: [],\n type: existingFm.type || metadata.type,\n status: existingFm.status || 'active',\n };\n\n // Track what was added/updated\n if (!existingFm.title) added.push('title');\n if (!existingFm.created) added.push('created');\n if (!existingFm.type) added.push('type');\n if (!existingFm.status) added.push('status');\n updated.push('modified');\n\n // Handle tags\n if (options.tags !== false) {\n const existingTags = existingFm.tags || [];\n const extractedTags = metadata.tags;\n const mergedTags = [...new Set([...existingTags, ...extractedTags])];\n newFm.tags = mergedTags;\n\n const newTagCount = mergedTags.length - existingTags.length;\n if (newTagCount > 0) added.push(`${newTagCount} tags`);\n }\n\n // Handle aliases\n if (options.aliases !== false) {\n const existingAliases = existingFm.aliases || [];\n const extractedAliases = metadata.aliases;\n const mergedAliases = [...new Set([...existingAliases, ...extractedAliases])];\n newFm.aliases = mergedAliases;\n\n const newAliasCount = mergedAliases.length - existingAliases.length;\n if (newAliasCount > 0) added.push(`${newAliasCount} aliases`);\n }\n\n // Handle links\n if (options.links !== false) {\n const existingLinks = existingFm.links || [];\n const extractedLinks = metadata.links;\n const mergedLinks = [...new Set([...existingLinks, ...extractedLinks])];\n newFm.links = mergedLinks;\n\n const newLinkCount = mergedLinks.length - existingLinks.length;\n if (newLinkCount > 0) added.push(`${newLinkCount} links`);\n }\n\n // Add description if missing\n if (!existingFm.description && metadata.description) {\n newFm.description = metadata.description;\n added.push('description');\n }\n\n // Write file if not dry run\n if (!options.dryRun) {\n const newContent = this.buildMarkdownWithFrontmatter(newFm, parsed.content);\n await writeFile(filePath, newContent);\n }\n\n return {\n file: relativePath,\n added,\n updated,\n frontmatter: newFm,\n };\n }\n\n /**\n * Extract metadata from content and filename\n */\n private extractMetadata(content: string, filename: string): {\n title: string;\n created: string;\n type: string;\n tags: string[];\n aliases: string[];\n links: string[];\n description?: string;\n } {\n const basename = path.basename(filename, '.md');\n\n // Extract title from first heading or filename\n let title = basename;\n const headingMatch = content.match(/^#\\s+(.+)$/m);\n if (headingMatch) {\n title = headingMatch[1].trim();\n }\n\n // Generate aliases from title variations\n const aliases = this.generateAliases(title, basename);\n\n // Extract tags from hashtags in content\n const tags = this.extractHashtags(content);\n\n // Infer type from content and filename\n const type = this.inferType(content, basename);\n\n // Extract wiki-links\n const links = this.extractWikiLinks(content);\n\n // Extract first paragraph as description\n const description = this.extractDescription(content);\n\n // Use current date for created\n const created = new Date().toISOString().split('T')[0];\n\n return { title, created, type, tags, aliases, links, description };\n }\n\n /**\n * Generate aliases from title\n */\n private generateAliases(title: string, filename: string): string[] {\n const aliases: string[] = [];\n\n // Add filename if different from title\n const cleanFilename = filename.replace(/-/g, ' ').toLowerCase();\n const cleanTitle = title.toLowerCase();\n if (cleanFilename !== cleanTitle) {\n // Convert filename to readable form\n const readableFilename = filename\n .replace(/-/g, ' ')\n .replace(/\\b\\w/g, c => c.toUpperCase());\n if (readableFilename !== title) {\n aliases.push(readableFilename);\n }\n }\n\n // Add lowercase version if title has caps\n if (title !== title.toLowerCase() && !aliases.includes(title.toLowerCase())) {\n aliases.push(title.toLowerCase());\n }\n\n // Add acronym if title has multiple words\n const words = title.split(/\\s+/);\n if (words.length >= 2 && words.length <= 5) {\n const acronym = words\n .map(w => w[0])\n .filter(c => c && /[A-Z]/.test(c))\n .join('');\n if (acronym.length >= 2 && !aliases.includes(acronym)) {\n aliases.push(acronym);\n }\n }\n\n return aliases.slice(0, 5); // Limit to 5 aliases\n }\n\n /**\n * Extract hashtags from content\n */\n private extractHashtags(content: string): string[] {\n const tags = new Set<string>();\n\n // Match #tag patterns (but not #headings)\n const regex = /(?:^|\\s)#([a-zA-Z][a-zA-Z0-9_-]*)/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const tag = match[1].toLowerCase();\n if (tag.length > 1 && tag.length < 30) {\n tags.add(tag);\n }\n }\n\n // Also infer tags from content keywords\n const inferredTags = this.inferTagsFromContent(content);\n for (const tag of inferredTags) {\n tags.add(tag);\n }\n\n return Array.from(tags).slice(0, 10); // Limit to 10 tags\n }\n\n /**\n * Infer tags from content analysis\n */\n private inferTagsFromContent(content: string): string[] {\n const tags: string[] = [];\n const lowerContent = content.toLowerCase();\n\n // Common topic patterns\n const patterns: Array<{ pattern: RegExp; tag: string }> = [\n { pattern: /\\b(api|endpoint|rest|graphql)\\b/i, tag: 'api' },\n { pattern: /\\b(database|sql|postgres|mysql|mongo)\\b/i, tag: 'database' },\n { pattern: /\\b(test|testing|jest|vitest|unittest)\\b/i, tag: 'testing' },\n { pattern: /\\b(docker|container|kubernetes|k8s)\\b/i, tag: 'devops' },\n { pattern: /\\b(security|auth|authentication|oauth)\\b/i, tag: 'security' },\n { pattern: /\\b(performance|optimization|cache)\\b/i, tag: 'performance' },\n { pattern: /\\b(react|vue|angular|frontend)\\b/i, tag: 'frontend' },\n { pattern: /\\b(node|express|backend|server)\\b/i, tag: 'backend' },\n { pattern: /\\b(typescript|javascript|python|rust)\\b/i, tag: 'programming' },\n { pattern: /\\b(guide|tutorial|howto|how-to)\\b/i, tag: 'guide' },\n { pattern: /\\b(architecture|design|pattern)\\b/i, tag: 'architecture' },\n { pattern: /\\b(config|configuration|setup)\\b/i, tag: 'configuration' },\n ];\n\n for (const { pattern, tag } of patterns) {\n if (pattern.test(lowerContent)) {\n tags.push(tag);\n }\n }\n\n return tags;\n }\n\n /**\n * Infer document type from content\n */\n private inferType(content: string, filename: string): string {\n const lowerContent = content.toLowerCase();\n const lowerFilename = filename.toLowerCase();\n\n // Check filename patterns\n if (lowerFilename.includes('readme') || lowerFilename.includes('index')) {\n return 'guide';\n }\n if (lowerFilename.includes('api') || lowerFilename.includes('endpoint')) {\n return 'technical';\n }\n if (lowerFilename.includes('standard') || lowerFilename.includes('convention')) {\n return 'standard';\n }\n\n // Check content patterns\n if (/```[a-z]+\\n/i.test(content)) {\n return 'technical';\n }\n if (/##\\s*installation|##\\s*getting started|##\\s*usage/i.test(content)) {\n return 'guide';\n }\n if (/##\\s*api|##\\s*endpoints|##\\s*methods/i.test(content)) {\n return 'technical';\n }\n if (/##\\s*overview|##\\s*introduction|##\\s*background/i.test(content)) {\n return 'concept';\n }\n\n return 'concept'; // Default type\n }\n\n /**\n * Extract wiki-links from content\n */\n private extractWikiLinks(content: string): string[] {\n const links = new Set<string>();\n const regex = /\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]+)?\\]\\]/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[1].trim();\n if (link) {\n links.add(link);\n }\n }\n\n return Array.from(links);\n }\n\n /**\n * Extract first paragraph as description\n */\n private extractDescription(content: string): string | undefined {\n // Skip headings and find first paragraph\n const lines = content.split('\\n');\n let foundContent = false;\n const paragraphLines: string[] = [];\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines at start\n if (!foundContent && !trimmed) continue;\n\n // Skip headings\n if (trimmed.startsWith('#')) {\n if (foundContent) break;\n continue;\n }\n\n // Skip code blocks\n if (trimmed.startsWith('```')) continue;\n\n // Found content\n if (trimmed) {\n foundContent = true;\n paragraphLines.push(trimmed);\n } else if (foundContent) {\n // End of paragraph\n break;\n }\n }\n\n const description = paragraphLines.join(' ').slice(0, 200);\n return description || undefined;\n }\n\n /**\n * Build markdown content with frontmatter\n */\n private buildMarkdownWithFrontmatter(fm: FrontmatterTemplate, content: string): string {\n // Clean frontmatter - remove empty arrays and undefined values\n const cleanFm: Record<string, unknown> = {};\n\n if (fm.title) cleanFm.title = fm.title;\n if (fm.description) cleanFm.description = fm.description;\n if (fm.type) cleanFm.type = fm.type;\n if (fm.status) cleanFm.status = fm.status;\n if (fm.created) cleanFm.created = fm.created;\n if (fm.modified) cleanFm.modified = fm.modified;\n if (fm.tags && fm.tags.length > 0) cleanFm.tags = fm.tags;\n if (fm.aliases && fm.aliases.length > 0) cleanFm.aliases = fm.aliases;\n if (fm.links && fm.links.length > 0) cleanFm.links = fm.links;\n\n const yamlStr = yaml.dump(cleanFm, {\n lineWidth: -1,\n quotingType: '\"',\n forceQuotes: false,\n });\n\n return `---\\n${yamlStr}---\\n\\n${content.trim()}\\n`;\n }\n\n /**\n * Generate report\n */\n generateReport(result: EnrichResult): string {\n const lines: string[] = [];\n\n lines.push('# Frontmatter Enrichment Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n lines.push('');\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.statistics.totalFiles} |`);\n lines.push(`| Enriched | ${result.statistics.enrichedCount} |`);\n lines.push(`| Skipped | ${result.statistics.skippedCount} |`);\n lines.push(`| Errors | ${result.statistics.errorCount} |`);\n lines.push(`| Tags Added | ${result.statistics.tagsAdded} |`);\n lines.push(`| Aliases Added | ${result.statistics.aliasesAdded} |`);\n lines.push(`| Links Extracted | ${result.statistics.linksExtracted} |`);\n lines.push('');\n\n if (result.enriched.length > 0) {\n lines.push('## Enriched Files\\n');\n for (const { file, added } of result.enriched.slice(0, 50)) {\n if (added.length > 0) {\n lines.push(`- \\`${file}\\`: ${added.join(', ')}`);\n }\n }\n if (result.enriched.length > 50) {\n lines.push(`\\n*... and ${result.enriched.length - 50} more*`);\n }\n lines.push('');\n }\n\n if (result.errors.length > 0) {\n lines.push('## Errors\\n');\n for (const { file, error } of result.errors) {\n lines.push(`- \\`${file}\\`: ${error}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createAddFrontmatterCommand(): Command {\n const command = new Command('add-frontmatter')\n .description('Add/enrich YAML frontmatter in markdown files')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('--overwrite', 'Overwrite existing frontmatter fields')\n .option('--tags', 'Auto-generate tags from content (default: true)')\n .option('--no-tags', 'Skip tag generation')\n .option('--aliases', 'Generate aliases from title (default: true)')\n .option('--no-aliases', 'Skip alias generation')\n .option('--links', 'Extract wiki-links to frontmatter (default: true)')\n .option('--no-links', 'Skip link extraction')\n .option('--dry-run', 'Preview changes without writing files')\n .option('-o, --output <file>', 'Output file for report')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: EnrichOptions) => {\n const enricher = new FrontmatterEnricher();\n\n const mode = options.dryRun ? ' (dry run)' : '';\n console.log(chalk.cyan(`\\nEnriching frontmatter${mode}...\\n`));\n\n try {\n const result = await enricher.enrichVault(vaultPath, options);\n\n if (options.json) {\n if (options.output) {\n await writeFile(options.output, JSON.stringify(result, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n } else {\n // Display summary\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.statistics.totalFiles}`));\n console.log(chalk.green(` Enriched: ${result.statistics.enrichedCount}`));\n console.log(chalk.gray(` Skipped: ${result.statistics.skippedCount}`));\n if (result.statistics.errorCount > 0) {\n console.log(chalk.red(` Errors: ${result.statistics.errorCount}`));\n }\n console.log('');\n\n console.log(chalk.bold('Additions:'));\n console.log(chalk.white(` Tags Added: ${result.statistics.tagsAdded}`));\n console.log(chalk.white(` Aliases Added: ${result.statistics.aliasesAdded}`));\n console.log(chalk.white(` Links Extracted: ${result.statistics.linksExtracted}`));\n console.log('');\n\n if (options.verbose && result.enriched.length > 0) {\n console.log(chalk.bold('Enriched Files:'));\n for (const { file, added } of result.enriched.slice(0, 20)) {\n if (added.length > 0) {\n console.log(chalk.green(` ${file}`));\n console.log(chalk.gray(` Added: ${added.join(', ')}`));\n }\n }\n if (result.enriched.length > 20) {\n console.log(chalk.gray(` ... and ${result.enriched.length - 20} more`));\n }\n console.log('');\n }\n\n if (result.errors.length > 0) {\n console.log(chalk.bold(chalk.red('Errors:')));\n for (const { file, error } of result.errors.slice(0, 10)) {\n console.log(chalk.red(` ${file}: ${error}`));\n }\n if (result.errors.length > 10) {\n console.log(chalk.gray(` ... and ${result.errors.length - 10} more`));\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output && !options.json) {\n const report = enricher.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show dry-run notice\n if (options.dryRun) {\n console.log(chalk.yellow('Dry run complete - no files were modified.'));\n console.log(chalk.gray('Remove --dry-run to apply changes.\\n'));\n }\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createAddFrontmatterCommand;\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import * as path from "path";
4
4
  import { writeFile, access, readFile } from "fs/promises";
5
- import { glob } from "fast-glob";
5
+ import fg from "fast-glob";
6
6
  import matter from "gray-matter";
7
7
  class LinkAnalyzer {
8
8
  fileMap = /* @__PURE__ */ new Map();
@@ -17,7 +17,7 @@ class LinkAnalyzer {
17
17
  } catch {
18
18
  throw new Error(`Vault path does not exist: ${resolvedPath}`);
19
19
  }
20
- const files = await glob("**/*.md", {
20
+ const files = await fg("**/*.md", {
21
21
  cwd: resolvedPath,
22
22
  ignore: ["node_modules/**", ".git/**", "dist/**"],
23
23
  absolute: false
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-links.js","sources":["../../../../src/cli/commands/hive-mind/analyze-links.ts"],"sourcesContent":["/**\n * Hive Mind - Link Analyzer\n *\n * Analyzes wiki-links and markdown links in a vault to build an adjacency list\n * and identify orphan files.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, access, writeFile } from 'fs/promises';\nimport { glob } from 'fast-glob';\nimport matter from 'gray-matter';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AnalyzeOptions {\n output?: string;\n json?: boolean;\n verbose?: boolean;\n includeContent?: boolean;\n}\n\nexport interface BrokenLink {\n source: string;\n target: string;\n type: 'wikilink' | 'markdown';\n lineNumber?: number;\n}\n\nexport interface LinkAnalysisResult {\n totalFiles: number;\n filesWithLinks: number;\n orphanFiles: string[];\n orphanRate: number;\n linkDensity: number;\n adjacencyList: Map<string, string[]>;\n brokenLinks: BrokenLink[];\n statistics: {\n totalLinks: number;\n wikiLinks: number;\n markdownLinks: number;\n averageLinksPerFile: number;\n maxLinksInFile: number;\n maxLinksFile: string;\n isolatedClusters: number;\n };\n}\n\n// ============================================================================\n// Link Analyzer Class\n// ============================================================================\n\nexport class LinkAnalyzer {\n private fileMap: Map<string, string> = new Map();\n private allFiles: Set<string> = new Set();\n\n /**\n * Analyze a vault for links and build adjacency list\n */\n async analyzeVault(vaultPath: string, options: AnalyzeOptions = {}): Promise<LinkAnalysisResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Check if path exists\n try {\n await access(resolvedPath);\n } catch {\n throw new Error(`Vault path does not exist: ${resolvedPath}`);\n }\n\n // Find all markdown files\n const files = await glob('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n // Build file map for resolution (filename without extension -> full path)\n this.buildFileMap(files);\n\n const adjacencyList = new Map<string, string[]>();\n const brokenLinks: BrokenLink[] = [];\n const incomingLinks = new Map<string, Set<string>>();\n let totalWikiLinks = 0;\n let totalMarkdownLinks = 0;\n let maxLinksInFile = 0;\n let maxLinksFile = '';\n\n // Initialize adjacency list and incoming links\n for (const file of files) {\n adjacencyList.set(file, []);\n incomingLinks.set(file, new Set());\n }\n\n // Process each file\n for (const file of files) {\n const filePath = path.join(resolvedPath, file);\n const content = await readFile(filePath, 'utf-8');\n\n // Parse frontmatter to skip it for link counting\n const { content: bodyContent } = matter(content);\n\n // Extract links\n const wikiLinks = this.parseWikiLinks(bodyContent);\n const markdownLinks = this.parseMarkdownLinks(bodyContent);\n\n totalWikiLinks += wikiLinks.length;\n totalMarkdownLinks += markdownLinks.length;\n\n const fileLinks = adjacencyList.get(file) || [];\n const totalLinksInThisFile = wikiLinks.length + markdownLinks.length;\n\n if (totalLinksInThisFile > maxLinksInFile) {\n maxLinksInFile = totalLinksInThisFile;\n maxLinksFile = file;\n }\n\n // Process wiki links\n for (const link of wikiLinks) {\n const resolved = this.resolveLink(link, file);\n if (resolved && this.allFiles.has(resolved)) {\n fileLinks.push(resolved);\n incomingLinks.get(resolved)?.add(file);\n } else {\n brokenLinks.push({\n source: file,\n target: link,\n type: 'wikilink',\n });\n }\n }\n\n // Process markdown links\n for (const link of markdownLinks) {\n const resolved = this.resolveLink(link, file);\n if (resolved && this.allFiles.has(resolved)) {\n fileLinks.push(resolved);\n incomingLinks.get(resolved)?.add(file);\n } else if (!this.isExternalLink(link)) {\n brokenLinks.push({\n source: file,\n target: link,\n type: 'markdown',\n });\n }\n }\n\n adjacencyList.set(file, [...new Set(fileLinks)]); // Dedupe\n }\n\n // Find orphan files (no incoming AND no outgoing links)\n const orphanFiles: string[] = [];\n for (const file of files) {\n const outgoing = adjacencyList.get(file) || [];\n const incoming = incomingLinks.get(file) || new Set();\n if (outgoing.length === 0 && incoming.size === 0) {\n orphanFiles.push(file);\n }\n }\n\n // Calculate metrics\n const totalFiles = files.length;\n const filesWithLinks = files.filter(f => {\n const outgoing = adjacencyList.get(f) || [];\n const incoming = incomingLinks.get(f) || new Set();\n return outgoing.length > 0 || incoming.size > 0;\n }).length;\n\n const orphanRate = totalFiles > 0 ? (orphanFiles.length / totalFiles) * 100 : 0;\n const totalLinks = totalWikiLinks + totalMarkdownLinks;\n const linkDensity = totalFiles > 0 ? totalLinks / totalFiles : 0;\n const averageLinksPerFile = totalFiles > 0 ? totalLinks / totalFiles : 0;\n\n // Calculate isolated clusters using union-find\n const isolatedClusters = this.countClusters(adjacencyList, files);\n\n return {\n totalFiles,\n filesWithLinks,\n orphanFiles,\n orphanRate: Math.round(orphanRate * 100) / 100,\n linkDensity: Math.round(linkDensity * 100) / 100,\n adjacencyList,\n brokenLinks,\n statistics: {\n totalLinks,\n wikiLinks: totalWikiLinks,\n markdownLinks: totalMarkdownLinks,\n averageLinksPerFile: Math.round(averageLinksPerFile * 100) / 100,\n maxLinksInFile,\n maxLinksFile,\n isolatedClusters,\n },\n };\n }\n\n /**\n * Build a map from filename (without extension) to full path\n */\n private buildFileMap(files: string[]): void {\n this.fileMap.clear();\n this.allFiles.clear();\n\n for (const file of files) {\n this.allFiles.add(file);\n\n // Map by filename without extension\n const basename = path.basename(file, '.md');\n if (!this.fileMap.has(basename)) {\n this.fileMap.set(basename, file);\n }\n\n // Also map by full path without extension\n const pathWithoutExt = file.replace(/\\.md$/, '');\n this.fileMap.set(pathWithoutExt, file);\n }\n }\n\n /**\n * Parse [[wiki-links]] from content\n * Handles: [[link]], [[link|alias]], [[folder/link]], [[link#heading]]\n */\n parseWikiLinks(content: string): string[] {\n const links: string[] = [];\n // Match [[link]] and [[link|alias]] patterns\n const regex = /\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]+)?\\]\\]/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[1].trim();\n if (link) {\n links.push(link);\n }\n }\n\n return links;\n }\n\n /**\n * Parse [markdown](links) from content\n * Handles: [text](link.md), [text](./link.md), [text](../folder/link.md)\n */\n parseMarkdownLinks(content: string): string[] {\n const links: string[] = [];\n // Match [text](link) patterns, excluding images ![...](...) and external links\n const regex = /(?<!!)\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[2].trim();\n // Skip external links, anchors, and non-md files\n if (link && !this.isExternalLink(link) && !link.startsWith('#')) {\n links.push(link);\n }\n }\n\n return links;\n }\n\n /**\n * Check if a link is external (http, https, etc.)\n */\n private isExternalLink(link: string): boolean {\n return /^(https?:|mailto:|tel:|ftp:)/i.test(link);\n }\n\n /**\n * Resolve a link to a file in the vault\n */\n private resolveLink(link: string, sourceFile: string): string | null {\n // Remove .md extension if present\n let cleanLink = link.replace(/\\.md$/, '');\n\n // Remove leading ./ or ../ and resolve relative paths\n if (cleanLink.startsWith('./') || cleanLink.startsWith('../')) {\n const sourceDir = path.dirname(sourceFile);\n const resolved = path.normalize(path.join(sourceDir, cleanLink));\n cleanLink = resolved;\n }\n\n // Try to find in file map\n const found = this.fileMap.get(cleanLink);\n if (found) return found;\n\n // Try with .md extension\n const withMd = cleanLink + '.md';\n if (this.allFiles.has(withMd)) return withMd;\n\n // Try just the basename (for simple wiki-links)\n const basename = path.basename(cleanLink);\n const byBasename = this.fileMap.get(basename);\n if (byBasename) return byBasename;\n\n return null;\n }\n\n /**\n * Count isolated clusters using union-find algorithm\n */\n private countClusters(adjacencyList: Map<string, string[]>, files: string[]): number {\n const parent = new Map<string, string>();\n const rank = new Map<string, number>();\n\n // Initialize each file as its own cluster\n for (const file of files) {\n parent.set(file, file);\n rank.set(file, 0);\n }\n\n const find = (x: string): string => {\n if (parent.get(x) !== x) {\n parent.set(x, find(parent.get(x)!));\n }\n return parent.get(x)!;\n };\n\n const union = (x: string, y: string): void => {\n const rootX = find(x);\n const rootY = find(y);\n if (rootX !== rootY) {\n const rankX = rank.get(rootX) || 0;\n const rankY = rank.get(rootY) || 0;\n if (rankX < rankY) {\n parent.set(rootX, rootY);\n } else if (rankX > rankY) {\n parent.set(rootY, rootX);\n } else {\n parent.set(rootY, rootX);\n rank.set(rootX, rankX + 1);\n }\n }\n };\n\n // Union connected files\n for (const [source, targets] of adjacencyList) {\n for (const target of targets) {\n if (parent.has(target)) {\n union(source, target);\n }\n }\n }\n\n // Count unique roots\n const roots = new Set<string>();\n for (const file of files) {\n roots.add(find(file));\n }\n\n return roots.size;\n }\n\n /**\n * Generate a detailed report\n */\n generateReport(result: LinkAnalysisResult): string {\n const lines: string[] = [];\n\n lines.push('# Link Analysis Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.totalFiles} |`);\n lines.push(`| Files with Links | ${result.filesWithLinks} |`);\n lines.push(`| Orphan Files | ${result.orphanFiles.length} |`);\n lines.push(`| Orphan Rate | ${result.orphanRate}% |`);\n lines.push(`| Link Density | ${result.linkDensity} |`);\n lines.push(`| Total Links | ${result.statistics.totalLinks} |`);\n lines.push(`| Wiki Links | ${result.statistics.wikiLinks} |`);\n lines.push(`| Markdown Links | ${result.statistics.markdownLinks} |`);\n lines.push(`| Avg Links/File | ${result.statistics.averageLinksPerFile} |`);\n lines.push(`| Max Links in File | ${result.statistics.maxLinksInFile} |`);\n lines.push(`| Most Linked File | ${result.statistics.maxLinksFile} |`);\n lines.push(`| Isolated Clusters | ${result.statistics.isolatedClusters} |`);\n lines.push('');\n\n if (result.orphanFiles.length > 0) {\n lines.push('## Orphan Files\\n');\n lines.push('Files with no incoming or outgoing links:\\n');\n for (const file of result.orphanFiles.slice(0, 50)) {\n lines.push(`- \\`${file}\\``);\n }\n if (result.orphanFiles.length > 50) {\n lines.push(`\\n... and ${result.orphanFiles.length - 50} more`);\n }\n lines.push('');\n }\n\n if (result.brokenLinks.length > 0) {\n lines.push('## Broken Links\\n');\n lines.push('Links that point to non-existent files:\\n');\n for (const broken of result.brokenLinks.slice(0, 50)) {\n lines.push(`- \\`${broken.source}\\` -> \\`${broken.target}\\` (${broken.type})`);\n }\n if (result.brokenLinks.length > 50) {\n lines.push(`\\n... and ${result.brokenLinks.length - 50} more`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createAnalyzeLinksCommand(): Command {\n const command = new Command('analyze-links')\n .description('Analyze wiki-links and markdown links in a vault')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('-o, --output <file>', 'Output file for results (JSON or Markdown)')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: AnalyzeOptions) => {\n const analyzer = new LinkAnalyzer();\n\n console.log(chalk.cyan('\\nAnalyzing links in vault...\\n'));\n\n try {\n const result = await analyzer.analyzeVault(vaultPath, options);\n\n if (options.json) {\n // Convert Map to Object for JSON serialization\n const jsonResult = {\n ...result,\n adjacencyList: Object.fromEntries(result.adjacencyList),\n };\n\n if (options.output) {\n await writeFile(options.output, JSON.stringify(jsonResult, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(jsonResult, null, 2));\n }\n } else {\n // Display summary\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.totalFiles}`));\n console.log(chalk.white(` Files with Links: ${result.filesWithLinks}`));\n console.log(chalk.white(` Orphan Files: ${chalk.yellow(result.orphanFiles.length.toString())}`));\n console.log(chalk.white(` Orphan Rate: ${chalk.yellow(result.orphanRate + '%')}`));\n console.log(chalk.white(` Link Density: ${result.linkDensity}`));\n console.log('');\n\n console.log(chalk.bold('Link Statistics:'));\n console.log(chalk.white(` Total Links: ${result.statistics.totalLinks}`));\n console.log(chalk.white(` Wiki Links: ${result.statistics.wikiLinks}`));\n console.log(chalk.white(` Markdown Links: ${result.statistics.markdownLinks}`));\n console.log(chalk.white(` Avg Links/File: ${result.statistics.averageLinksPerFile}`));\n console.log(chalk.white(` Isolated Clusters: ${result.statistics.isolatedClusters}`));\n console.log('');\n\n if (result.brokenLinks.length > 0) {\n console.log(chalk.bold(chalk.red(`Broken Links: ${result.brokenLinks.length}`)));\n if (options.verbose) {\n for (const broken of result.brokenLinks.slice(0, 10)) {\n console.log(chalk.red(` ${broken.source} -> ${broken.target}`));\n }\n if (result.brokenLinks.length > 10) {\n console.log(chalk.gray(` ... and ${result.brokenLinks.length - 10} more`));\n }\n }\n console.log('');\n }\n\n if (options.verbose && result.orphanFiles.length > 0) {\n console.log(chalk.bold(chalk.yellow('Orphan Files:')));\n for (const file of result.orphanFiles.slice(0, 10)) {\n console.log(chalk.yellow(` ${file}`));\n }\n if (result.orphanFiles.length > 10) {\n console.log(chalk.gray(` ... and ${result.orphanFiles.length - 10} more`));\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output) {\n const report = analyzer.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show target metrics comparison\n console.log(chalk.bold('Target Metrics:'));\n const orphanStatus = result.orphanRate < 10 ? chalk.green('PASS') : chalk.red('FAIL');\n const densityStatus = result.linkDensity > 5.0 ? chalk.green('PASS') : chalk.red('FAIL');\n console.log(chalk.white(` Orphan Rate: ${result.orphanRate}% (target: < 10%) ${orphanStatus}`));\n console.log(chalk.white(` Link Density: ${result.linkDensity} (target: > 5.0) ${densityStatus}`));\n console.log('');\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createAnalyzeLinksCommand;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze-links.js","sources":["../../../../src/cli/commands/hive-mind/analyze-links.ts"],"sourcesContent":["/**\n * Hive Mind - Link Analyzer\n *\n * Analyzes wiki-links and markdown links in a vault to build an adjacency list\n * and identify orphan files.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, access, writeFile } from 'fs/promises';\nimport fg from 'fast-glob';\nimport matter from 'gray-matter';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AnalyzeOptions {\n output?: string;\n json?: boolean;\n verbose?: boolean;\n includeContent?: boolean;\n}\n\nexport interface BrokenLink {\n source: string;\n target: string;\n type: 'wikilink' | 'markdown';\n lineNumber?: number;\n}\n\nexport interface LinkAnalysisResult {\n totalFiles: number;\n filesWithLinks: number;\n orphanFiles: string[];\n orphanRate: number;\n linkDensity: number;\n adjacencyList: Map<string, string[]>;\n brokenLinks: BrokenLink[];\n statistics: {\n totalLinks: number;\n wikiLinks: number;\n markdownLinks: number;\n averageLinksPerFile: number;\n maxLinksInFile: number;\n maxLinksFile: string;\n isolatedClusters: number;\n };\n}\n\n// ============================================================================\n// Link Analyzer Class\n// ============================================================================\n\nexport class LinkAnalyzer {\n private fileMap: Map<string, string> = new Map();\n private allFiles: Set<string> = new Set();\n\n /**\n * Analyze a vault for links and build adjacency list\n */\n async analyzeVault(vaultPath: string, options: AnalyzeOptions = {}): Promise<LinkAnalysisResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Check if path exists\n try {\n await access(resolvedPath);\n } catch {\n throw new Error(`Vault path does not exist: ${resolvedPath}`);\n }\n\n // Find all markdown files\n const files = await fg('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n // Build file map for resolution (filename without extension -> full path)\n this.buildFileMap(files);\n\n const adjacencyList = new Map<string, string[]>();\n const brokenLinks: BrokenLink[] = [];\n const incomingLinks = new Map<string, Set<string>>();\n let totalWikiLinks = 0;\n let totalMarkdownLinks = 0;\n let maxLinksInFile = 0;\n let maxLinksFile = '';\n\n // Initialize adjacency list and incoming links\n for (const file of files) {\n adjacencyList.set(file, []);\n incomingLinks.set(file, new Set());\n }\n\n // Process each file\n for (const file of files) {\n const filePath = path.join(resolvedPath, file);\n const content = await readFile(filePath, 'utf-8');\n\n // Parse frontmatter to skip it for link counting\n const { content: bodyContent } = matter(content);\n\n // Extract links\n const wikiLinks = this.parseWikiLinks(bodyContent);\n const markdownLinks = this.parseMarkdownLinks(bodyContent);\n\n totalWikiLinks += wikiLinks.length;\n totalMarkdownLinks += markdownLinks.length;\n\n const fileLinks = adjacencyList.get(file) || [];\n const totalLinksInThisFile = wikiLinks.length + markdownLinks.length;\n\n if (totalLinksInThisFile > maxLinksInFile) {\n maxLinksInFile = totalLinksInThisFile;\n maxLinksFile = file;\n }\n\n // Process wiki links\n for (const link of wikiLinks) {\n const resolved = this.resolveLink(link, file);\n if (resolved && this.allFiles.has(resolved)) {\n fileLinks.push(resolved);\n incomingLinks.get(resolved)?.add(file);\n } else {\n brokenLinks.push({\n source: file,\n target: link,\n type: 'wikilink',\n });\n }\n }\n\n // Process markdown links\n for (const link of markdownLinks) {\n const resolved = this.resolveLink(link, file);\n if (resolved && this.allFiles.has(resolved)) {\n fileLinks.push(resolved);\n incomingLinks.get(resolved)?.add(file);\n } else if (!this.isExternalLink(link)) {\n brokenLinks.push({\n source: file,\n target: link,\n type: 'markdown',\n });\n }\n }\n\n adjacencyList.set(file, [...new Set(fileLinks)]); // Dedupe\n }\n\n // Find orphan files (no incoming AND no outgoing links)\n const orphanFiles: string[] = [];\n for (const file of files) {\n const outgoing = adjacencyList.get(file) || [];\n const incoming = incomingLinks.get(file) || new Set();\n if (outgoing.length === 0 && incoming.size === 0) {\n orphanFiles.push(file);\n }\n }\n\n // Calculate metrics\n const totalFiles = files.length;\n const filesWithLinks = files.filter(f => {\n const outgoing = adjacencyList.get(f) || [];\n const incoming = incomingLinks.get(f) || new Set();\n return outgoing.length > 0 || incoming.size > 0;\n }).length;\n\n const orphanRate = totalFiles > 0 ? (orphanFiles.length / totalFiles) * 100 : 0;\n const totalLinks = totalWikiLinks + totalMarkdownLinks;\n const linkDensity = totalFiles > 0 ? totalLinks / totalFiles : 0;\n const averageLinksPerFile = totalFiles > 0 ? totalLinks / totalFiles : 0;\n\n // Calculate isolated clusters using union-find\n const isolatedClusters = this.countClusters(adjacencyList, files);\n\n return {\n totalFiles,\n filesWithLinks,\n orphanFiles,\n orphanRate: Math.round(orphanRate * 100) / 100,\n linkDensity: Math.round(linkDensity * 100) / 100,\n adjacencyList,\n brokenLinks,\n statistics: {\n totalLinks,\n wikiLinks: totalWikiLinks,\n markdownLinks: totalMarkdownLinks,\n averageLinksPerFile: Math.round(averageLinksPerFile * 100) / 100,\n maxLinksInFile,\n maxLinksFile,\n isolatedClusters,\n },\n };\n }\n\n /**\n * Build a map from filename (without extension) to full path\n */\n private buildFileMap(files: string[]): void {\n this.fileMap.clear();\n this.allFiles.clear();\n\n for (const file of files) {\n this.allFiles.add(file);\n\n // Map by filename without extension\n const basename = path.basename(file, '.md');\n if (!this.fileMap.has(basename)) {\n this.fileMap.set(basename, file);\n }\n\n // Also map by full path without extension\n const pathWithoutExt = file.replace(/\\.md$/, '');\n this.fileMap.set(pathWithoutExt, file);\n }\n }\n\n /**\n * Parse [[wiki-links]] from content\n * Handles: [[link]], [[link|alias]], [[folder/link]], [[link#heading]]\n */\n parseWikiLinks(content: string): string[] {\n const links: string[] = [];\n // Match [[link]] and [[link|alias]] patterns\n const regex = /\\[\\[([^\\]|#]+)(?:#[^\\]|]*)?(?:\\|[^\\]]+)?\\]\\]/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[1].trim();\n if (link) {\n links.push(link);\n }\n }\n\n return links;\n }\n\n /**\n * Parse [markdown](links) from content\n * Handles: [text](link.md), [text](./link.md), [text](../folder/link.md)\n */\n parseMarkdownLinks(content: string): string[] {\n const links: string[] = [];\n // Match [text](link) patterns, excluding images ![...](...) and external links\n const regex = /(?<!!)\\[([^\\]]*)\\]\\(([^)]+)\\)/g;\n let match;\n\n while ((match = regex.exec(content)) !== null) {\n const link = match[2].trim();\n // Skip external links, anchors, and non-md files\n if (link && !this.isExternalLink(link) && !link.startsWith('#')) {\n links.push(link);\n }\n }\n\n return links;\n }\n\n /**\n * Check if a link is external (http, https, etc.)\n */\n private isExternalLink(link: string): boolean {\n return /^(https?:|mailto:|tel:|ftp:)/i.test(link);\n }\n\n /**\n * Resolve a link to a file in the vault\n */\n private resolveLink(link: string, sourceFile: string): string | null {\n // Remove .md extension if present\n let cleanLink = link.replace(/\\.md$/, '');\n\n // Remove leading ./ or ../ and resolve relative paths\n if (cleanLink.startsWith('./') || cleanLink.startsWith('../')) {\n const sourceDir = path.dirname(sourceFile);\n const resolved = path.normalize(path.join(sourceDir, cleanLink));\n cleanLink = resolved;\n }\n\n // Try to find in file map\n const found = this.fileMap.get(cleanLink);\n if (found) return found;\n\n // Try with .md extension\n const withMd = cleanLink + '.md';\n if (this.allFiles.has(withMd)) return withMd;\n\n // Try just the basename (for simple wiki-links)\n const basename = path.basename(cleanLink);\n const byBasename = this.fileMap.get(basename);\n if (byBasename) return byBasename;\n\n return null;\n }\n\n /**\n * Count isolated clusters using union-find algorithm\n */\n private countClusters(adjacencyList: Map<string, string[]>, files: string[]): number {\n const parent = new Map<string, string>();\n const rank = new Map<string, number>();\n\n // Initialize each file as its own cluster\n for (const file of files) {\n parent.set(file, file);\n rank.set(file, 0);\n }\n\n const find = (x: string): string => {\n if (parent.get(x) !== x) {\n parent.set(x, find(parent.get(x)!));\n }\n return parent.get(x)!;\n };\n\n const union = (x: string, y: string): void => {\n const rootX = find(x);\n const rootY = find(y);\n if (rootX !== rootY) {\n const rankX = rank.get(rootX) || 0;\n const rankY = rank.get(rootY) || 0;\n if (rankX < rankY) {\n parent.set(rootX, rootY);\n } else if (rankX > rankY) {\n parent.set(rootY, rootX);\n } else {\n parent.set(rootY, rootX);\n rank.set(rootX, rankX + 1);\n }\n }\n };\n\n // Union connected files\n for (const [source, targets] of adjacencyList) {\n for (const target of targets) {\n if (parent.has(target)) {\n union(source, target);\n }\n }\n }\n\n // Count unique roots\n const roots = new Set<string>();\n for (const file of files) {\n roots.add(find(file));\n }\n\n return roots.size;\n }\n\n /**\n * Generate a detailed report\n */\n generateReport(result: LinkAnalysisResult): string {\n const lines: string[] = [];\n\n lines.push('# Link Analysis Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.totalFiles} |`);\n lines.push(`| Files with Links | ${result.filesWithLinks} |`);\n lines.push(`| Orphan Files | ${result.orphanFiles.length} |`);\n lines.push(`| Orphan Rate | ${result.orphanRate}% |`);\n lines.push(`| Link Density | ${result.linkDensity} |`);\n lines.push(`| Total Links | ${result.statistics.totalLinks} |`);\n lines.push(`| Wiki Links | ${result.statistics.wikiLinks} |`);\n lines.push(`| Markdown Links | ${result.statistics.markdownLinks} |`);\n lines.push(`| Avg Links/File | ${result.statistics.averageLinksPerFile} |`);\n lines.push(`| Max Links in File | ${result.statistics.maxLinksInFile} |`);\n lines.push(`| Most Linked File | ${result.statistics.maxLinksFile} |`);\n lines.push(`| Isolated Clusters | ${result.statistics.isolatedClusters} |`);\n lines.push('');\n\n if (result.orphanFiles.length > 0) {\n lines.push('## Orphan Files\\n');\n lines.push('Files with no incoming or outgoing links:\\n');\n for (const file of result.orphanFiles.slice(0, 50)) {\n lines.push(`- \\`${file}\\``);\n }\n if (result.orphanFiles.length > 50) {\n lines.push(`\\n... and ${result.orphanFiles.length - 50} more`);\n }\n lines.push('');\n }\n\n if (result.brokenLinks.length > 0) {\n lines.push('## Broken Links\\n');\n lines.push('Links that point to non-existent files:\\n');\n for (const broken of result.brokenLinks.slice(0, 50)) {\n lines.push(`- \\`${broken.source}\\` -> \\`${broken.target}\\` (${broken.type})`);\n }\n if (result.brokenLinks.length > 50) {\n lines.push(`\\n... and ${result.brokenLinks.length - 50} more`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createAnalyzeLinksCommand(): Command {\n const command = new Command('analyze-links')\n .description('Analyze wiki-links and markdown links in a vault')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('-o, --output <file>', 'Output file for results (JSON or Markdown)')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: AnalyzeOptions) => {\n const analyzer = new LinkAnalyzer();\n\n console.log(chalk.cyan('\\nAnalyzing links in vault...\\n'));\n\n try {\n const result = await analyzer.analyzeVault(vaultPath, options);\n\n if (options.json) {\n // Convert Map to Object for JSON serialization\n const jsonResult = {\n ...result,\n adjacencyList: Object.fromEntries(result.adjacencyList),\n };\n\n if (options.output) {\n await writeFile(options.output, JSON.stringify(jsonResult, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(jsonResult, null, 2));\n }\n } else {\n // Display summary\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.totalFiles}`));\n console.log(chalk.white(` Files with Links: ${result.filesWithLinks}`));\n console.log(chalk.white(` Orphan Files: ${chalk.yellow(result.orphanFiles.length.toString())}`));\n console.log(chalk.white(` Orphan Rate: ${chalk.yellow(result.orphanRate + '%')}`));\n console.log(chalk.white(` Link Density: ${result.linkDensity}`));\n console.log('');\n\n console.log(chalk.bold('Link Statistics:'));\n console.log(chalk.white(` Total Links: ${result.statistics.totalLinks}`));\n console.log(chalk.white(` Wiki Links: ${result.statistics.wikiLinks}`));\n console.log(chalk.white(` Markdown Links: ${result.statistics.markdownLinks}`));\n console.log(chalk.white(` Avg Links/File: ${result.statistics.averageLinksPerFile}`));\n console.log(chalk.white(` Isolated Clusters: ${result.statistics.isolatedClusters}`));\n console.log('');\n\n if (result.brokenLinks.length > 0) {\n console.log(chalk.bold(chalk.red(`Broken Links: ${result.brokenLinks.length}`)));\n if (options.verbose) {\n for (const broken of result.brokenLinks.slice(0, 10)) {\n console.log(chalk.red(` ${broken.source} -> ${broken.target}`));\n }\n if (result.brokenLinks.length > 10) {\n console.log(chalk.gray(` ... and ${result.brokenLinks.length - 10} more`));\n }\n }\n console.log('');\n }\n\n if (options.verbose && result.orphanFiles.length > 0) {\n console.log(chalk.bold(chalk.yellow('Orphan Files:')));\n for (const file of result.orphanFiles.slice(0, 10)) {\n console.log(chalk.yellow(` ${file}`));\n }\n if (result.orphanFiles.length > 10) {\n console.log(chalk.gray(` ... and ${result.orphanFiles.length - 10} more`));\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output) {\n const report = analyzer.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show target metrics comparison\n console.log(chalk.bold('Target Metrics:'));\n const orphanStatus = result.orphanRate < 10 ? chalk.green('PASS') : chalk.red('FAIL');\n const densityStatus = result.linkDensity > 5.0 ? chalk.green('PASS') : chalk.red('FAIL');\n console.log(chalk.white(` Orphan Rate: ${result.orphanRate}% (target: < 10%) ${orphanStatus}`));\n console.log(chalk.white(` Link Density: ${result.linkDensity} (target: > 5.0) ${densityStatus}`));\n console.log('');\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createAnalyzeLinksCommand;\n"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { Command } from "commander";
2
2
  import chalk from "chalk";
3
3
  import * as path from "path";
4
4
  import { writeFile, readFile } from "fs/promises";
5
- import { glob } from "fast-glob";
5
+ import fg from "fast-glob";
6
6
  import matter from "gray-matter";
7
7
  function tokenize(text) {
8
8
  const noCode = text.replace(/```[\s\S]*?```/g, "");
@@ -85,7 +85,7 @@ class ConnectionFinder {
85
85
  */
86
86
  async buildIndex(vaultPath) {
87
87
  const resolvedPath = path.resolve(vaultPath);
88
- const files = await glob("**/*.md", {
88
+ const files = await fg("**/*.md", {
89
89
  cwd: resolvedPath,
90
90
  ignore: ["node_modules/**", ".git/**", "dist/**"],
91
91
  absolute: false