legacyver 2.1.0 → 2.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 (126) hide show
  1. package/.agent/skills/openspec-apply-change/SKILL.md +156 -0
  2. package/.agent/skills/openspec-archive-change/SKILL.md +114 -0
  3. package/.agent/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  4. package/.agent/skills/openspec-continue-change/SKILL.md +118 -0
  5. package/.agent/skills/openspec-explore/SKILL.md +290 -0
  6. package/.agent/skills/openspec-ff-change/SKILL.md +101 -0
  7. package/.agent/skills/openspec-new-change/SKILL.md +74 -0
  8. package/.agent/skills/openspec-onboard/SKILL.md +529 -0
  9. package/.agent/skills/openspec-sync-specs/SKILL.md +138 -0
  10. package/.agent/skills/openspec-verify-change/SKILL.md +168 -0
  11. package/.agent/workflows/opsx-apply.md +149 -0
  12. package/.agent/workflows/opsx-archive.md +154 -0
  13. package/.agent/workflows/opsx-bulk-archive.md +239 -0
  14. package/.agent/workflows/opsx-continue.md +111 -0
  15. package/.agent/workflows/opsx-explore.md +171 -0
  16. package/.agent/workflows/opsx-ff.md +91 -0
  17. package/.agent/workflows/opsx-new.md +66 -0
  18. package/.agent/workflows/opsx-onboard.md +522 -0
  19. package/.agent/workflows/opsx-sync.md +131 -0
  20. package/.agent/workflows/opsx-verify.md +161 -0
  21. package/.github/prompts/opsx-apply.prompt.md +149 -0
  22. package/.github/prompts/opsx-archive.prompt.md +154 -0
  23. package/.github/prompts/opsx-bulk-archive.prompt.md +239 -0
  24. package/.github/prompts/opsx-continue.prompt.md +111 -0
  25. package/.github/prompts/opsx-explore.prompt.md +171 -0
  26. package/.github/prompts/opsx-ff.prompt.md +91 -0
  27. package/.github/prompts/opsx-new.prompt.md +66 -0
  28. package/.github/prompts/opsx-onboard.prompt.md +522 -0
  29. package/.github/prompts/opsx-sync.prompt.md +131 -0
  30. package/.github/prompts/opsx-verify.prompt.md +161 -0
  31. package/.github/skills/openspec-apply-change/SKILL.md +156 -0
  32. package/.github/skills/openspec-archive-change/SKILL.md +114 -0
  33. package/.github/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  34. package/.github/skills/openspec-continue-change/SKILL.md +118 -0
  35. package/.github/skills/openspec-explore/SKILL.md +290 -0
  36. package/.github/skills/openspec-ff-change/SKILL.md +101 -0
  37. package/.github/skills/openspec-new-change/SKILL.md +74 -0
  38. package/.github/skills/openspec-onboard/SKILL.md +529 -0
  39. package/.github/skills/openspec-sync-specs/SKILL.md +138 -0
  40. package/.github/skills/openspec-verify-change/SKILL.md +168 -0
  41. package/.legacyverignore.example +43 -0
  42. package/.legacyverrc +7 -0
  43. package/.opencode/command/opsx-apply.md +149 -0
  44. package/.opencode/command/opsx-archive.md +154 -0
  45. package/.opencode/command/opsx-bulk-archive.md +239 -0
  46. package/.opencode/command/opsx-continue.md +111 -0
  47. package/.opencode/command/opsx-explore.md +171 -0
  48. package/.opencode/command/opsx-ff.md +91 -0
  49. package/.opencode/command/opsx-new.md +66 -0
  50. package/.opencode/command/opsx-onboard.md +522 -0
  51. package/.opencode/command/opsx-sync.md +131 -0
  52. package/.opencode/command/opsx-verify.md +161 -0
  53. package/.opencode/skills/openspec-apply-change/SKILL.md +156 -0
  54. package/.opencode/skills/openspec-archive-change/SKILL.md +114 -0
  55. package/.opencode/skills/openspec-bulk-archive-change/SKILL.md +246 -0
  56. package/.opencode/skills/openspec-continue-change/SKILL.md +118 -0
  57. package/.opencode/skills/openspec-explore/SKILL.md +290 -0
  58. package/.opencode/skills/openspec-ff-change/SKILL.md +101 -0
  59. package/.opencode/skills/openspec-new-change/SKILL.md +74 -0
  60. package/.opencode/skills/openspec-onboard/SKILL.md +529 -0
  61. package/.opencode/skills/openspec-sync-specs/SKILL.md +138 -0
  62. package/.opencode/skills/openspec-verify-change/SKILL.md +168 -0
  63. package/LICENSE +1 -1
  64. package/README.md +128 -83
  65. package/bin/legacyver.js +48 -25
  66. package/legacyver-docs/SUMMARY.md +3 -0
  67. package/legacyver-docs/components.md +57 -0
  68. package/legacyver-docs/index.md +15 -0
  69. package/nul +2 -0
  70. package/package.json +23 -25
  71. package/src/cache/hash.js +9 -10
  72. package/src/cache/index.js +43 -65
  73. package/src/cli/commands/analyze.js +212 -190
  74. package/src/cli/commands/cache.js +15 -35
  75. package/src/cli/commands/init.js +63 -107
  76. package/src/cli/commands/providers.js +56 -81
  77. package/src/cli/commands/version.js +7 -10
  78. package/src/cli/ui.js +58 -77
  79. package/src/crawler/filters.js +41 -40
  80. package/src/crawler/index.js +52 -36
  81. package/src/crawler/manifest.js +31 -43
  82. package/src/crawler/walk.js +32 -38
  83. package/src/llm/chunker.js +34 -56
  84. package/src/llm/cost-estimator.js +68 -51
  85. package/src/llm/free-model.js +67 -0
  86. package/src/llm/index.js +22 -43
  87. package/src/llm/prompts.js +45 -33
  88. package/src/llm/providers/gemini.js +94 -0
  89. package/src/llm/providers/groq.js +55 -40
  90. package/src/llm/providers/ollama.js +38 -65
  91. package/src/llm/providers/openrouter.js +67 -0
  92. package/src/llm/queue.js +59 -88
  93. package/src/llm/re-prompter.js +41 -0
  94. package/src/llm/validator.js +72 -0
  95. package/src/parser/ast/generic.js +45 -222
  96. package/src/parser/ast/go.js +86 -205
  97. package/src/parser/ast/java.js +76 -146
  98. package/src/parser/ast/javascript.js +173 -241
  99. package/src/parser/ast/laravel/blade.js +56 -0
  100. package/src/parser/ast/laravel/classifier.js +30 -0
  101. package/src/parser/ast/laravel/controller.js +35 -0
  102. package/src/parser/ast/laravel/index.js +54 -0
  103. package/src/parser/ast/laravel/model.js +41 -0
  104. package/src/parser/ast/laravel/provider.js +28 -0
  105. package/src/parser/ast/laravel/routes.js +45 -0
  106. package/src/parser/ast/php.js +129 -0
  107. package/src/parser/ast/python.js +76 -199
  108. package/src/parser/ast/typescript.js +10 -244
  109. package/src/parser/body-extractor.js +40 -0
  110. package/src/parser/call-graph.js +50 -67
  111. package/src/parser/complexity-scorer.js +59 -0
  112. package/src/parser/index.js +61 -86
  113. package/src/parser/pattern-detector.js +71 -0
  114. package/src/parser/pkg-builder.js +36 -83
  115. package/src/renderer/html.js +63 -135
  116. package/src/renderer/index.js +23 -35
  117. package/src/renderer/json.js +17 -35
  118. package/src/renderer/markdown.js +83 -117
  119. package/src/utils/config.js +52 -53
  120. package/src/utils/errors.js +26 -41
  121. package/src/utils/logger.js +32 -53
  122. package/src/cli/flags.js +0 -87
  123. package/src/llm/providers/anthropic.js +0 -57
  124. package/src/llm/providers/google.js +0 -65
  125. package/src/llm/providers/openai.js +0 -52
  126. package/src/parser/ast/tree-sitter-init.js +0 -80
package/package.json CHANGED
@@ -1,51 +1,49 @@
1
1
  {
2
2
  "name": "legacyver",
3
- "version": "2.1.0",
4
- "description": "Generate technical documentation from undocumented or legacy codebases using AST parsing and LLMs",
3
+ "version": "2.1.2",
4
+ "description": "AI-powered CLI tool to auto-generate technical documentation from legacy/undocumented codebases",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "legacyver": "bin/legacyver.js"
8
8
  },
9
+ "type": "commonjs",
9
10
  "engines": {
10
11
  "node": ">=18.0.0"
11
12
  },
12
13
  "scripts": {
13
14
  "test": "vitest run",
14
15
  "test:watch": "vitest",
15
- "lint": "eslint src/ test/"
16
+ "lint": "eslint src/ bin/"
16
17
  },
17
18
  "keywords": [
18
19
  "documentation",
19
- "legacy",
20
- "codebase",
20
+ "cli",
21
21
  "ast",
22
22
  "llm",
23
- "tree-sitter",
24
- "cli"
23
+ "legacy",
24
+ "code-analysis",
25
+ "tree-sitter"
25
26
  ],
27
+ "author": "",
26
28
  "license": "MIT",
27
- "type": "module",
28
29
  "dependencies": {
29
- "@anthropic-ai/sdk": "^0.75.0",
30
- "@google/generative-ai": "^0.24.1",
31
- "chalk": "^5.6.2",
30
+ "chalk": "^4.1.2",
32
31
  "cli-progress": "^3.12.0",
33
- "commander": "^14.0.3",
34
- "conf": "^15.1.0",
32
+ "commander": "^11.1.0",
33
+ "conf": "^10.2.0",
35
34
  "cosmiconfig": "^9.0.0",
36
- "fast-glob": "^3.3.3",
37
- "groq-sdk": "^0.37.0",
38
- "ignore": "^7.0.5",
39
- "marked": "^17.0.3",
40
- "openai": "^6.22.0",
41
- "ora": "^9.3.0",
42
- "p-limit": "^7.3.0",
43
- "p-retry": "^7.1.1",
44
- "picocolors": "^1.1.1",
45
- "web-tree-sitter": "^0.26.5"
35
+ "fast-glob": "^3.3.2",
36
+ "ignore": "^5.3.1",
37
+ "marked": "^11.1.1",
38
+ "ora": "^5.4.1",
39
+ "p-limit": "^3.1.0",
40
+ "p-retry": "^4.6.2",
41
+ "picocolors": "^1.0.0",
42
+ "tiktoken": "^1.0.15",
43
+ "web-tree-sitter": "^0.22.6"
46
44
  },
47
45
  "devDependencies": {
48
- "eslint": "^10.0.0",
49
- "vitest": "^4.0.18"
46
+ "eslint": "^8.57.0",
47
+ "vitest": "^1.2.2"
50
48
  }
51
49
  }
package/src/cache/hash.js CHANGED
@@ -1,17 +1,16 @@
1
- /**
2
- * File hash computation for incremental cache.
3
- * Uses Node.js crypto SHA-256 to produce deterministic file fingerprints.
4
- */
1
+ 'use strict';
5
2
 
6
- import { readFileSync } from 'node:fs';
7
- import { createHash } from 'node:crypto';
3
+ const { createHash } = require('crypto');
4
+ const { readFileSync } = require('fs');
8
5
 
9
6
  /**
10
- * Compute SHA-256 hash of a file's contents.
11
- * @param {string} filePath - Absolute path to file
12
- * @returns {string} Hex-encoded SHA-256 hash with "sha256:" prefix
7
+ * Compute SHA-256 hash of a file.
8
+ * @param {string} filePath
9
+ * @returns {string} hex string prefixed with 'sha256:'
13
10
  */
14
- export function computeHash(filePath) {
11
+ function computeHash(filePath) {
15
12
  const content = readFileSync(filePath);
16
13
  return 'sha256:' + createHash('sha256').update(content).digest('hex');
17
14
  }
15
+
16
+ module.exports = { computeHash };
@@ -1,56 +1,47 @@
1
- /**
2
- * Incremental cache module.
3
- * Persists file hashes to .legacyver-cache/hashes.json so that
4
- * unchanged files can skip LLM re-analysis on subsequent runs.
5
- */
1
+ 'use strict';
6
2
 
7
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
8
- import { join } from 'node:path';
3
+ const { readFileSync, writeFileSync, mkdirSync, existsSync, appendFileSync } = require('fs');
4
+ const path = require('path');
5
+ const logger = require('../utils/logger');
9
6
 
10
7
  const CACHE_FILE = 'hashes.json';
11
8
 
12
9
  /**
13
- * Load the cache map from disk.
14
- * @param {string} cacheDir - Path to .legacyver-cache directory
15
- * @returns {Record<string, { hash: string, generatedAt: string }>} Cache map (empty object if no cache)
10
+ * Load cache from .legacyver-cache/hashes.json.
11
+ * @param {string} cacheDir
12
+ * @returns {Object} map of relativePath -> { hash, docFile, generatedAt }
16
13
  */
17
- export function loadCache(cacheDir) {
18
- const cachePath = join(cacheDir, CACHE_FILE);
19
- if (!existsSync(cachePath)) {
20
- return {};
21
- }
14
+ function loadCache(cacheDir) {
15
+ const cachePath = path.join(cacheDir, CACHE_FILE);
16
+ if (!existsSync(cachePath)) return {};
22
17
  try {
23
- const raw = readFileSync(cachePath, 'utf-8');
24
- return JSON.parse(raw);
25
- } catch {
26
- // Corrupted cache — start fresh
18
+ return JSON.parse(readFileSync(cachePath, 'utf8'));
19
+ } catch (e) {
20
+ logger.warn(`Could not read cache: ${e.message}`);
27
21
  return {};
28
22
  }
29
23
  }
30
24
 
31
25
  /**
32
- * Save the cache map to disk.
33
- * Creates the cache directory if it doesn't exist.
34
- * @param {string} cacheDir - Path to .legacyver-cache directory
35
- * @param {Record<string, { hash: string, generatedAt: string }>} cacheMap - Cache map to persist
26
+ * Save cache to .legacyver-cache/hashes.json.
27
+ * @param {string} cacheDir
28
+ * @param {Object} map
36
29
  */
37
- export function saveCache(cacheDir, cacheMap) {
30
+ function saveCache(cacheDir, map) {
38
31
  mkdirSync(cacheDir, { recursive: true });
39
- const cachePath = join(cacheDir, CACHE_FILE);
40
- writeFileSync(cachePath, JSON.stringify(cacheMap, null, 2), 'utf-8');
32
+ const cachePath = path.join(cacheDir, CACHE_FILE);
33
+ writeFileSync(cachePath, JSON.stringify(map, null, 2), 'utf8');
41
34
  }
42
35
 
43
36
  /**
44
- * Separate manifest entries into cache hits and misses.
45
- * A hit means the file hash matches the cached hash — no re-analysis needed.
46
- * @param {import('../crawler/manifest.js').FileManifest[]} manifest - Current file manifest
47
- * @param {Record<string, { hash: string }>} cacheMap - Loaded cache map
48
- * @returns {{ hits: import('../crawler/manifest.js').FileManifest[], misses: import('../crawler/manifest.js').FileManifest[] }}
37
+ * Separate files into cache hits and misses.
38
+ * @param {Array} manifest FileManifest[]
39
+ * @param {Object} cacheMap
40
+ * @returns {{ hits: Array, misses: Array }}
49
41
  */
50
- export function getCacheHits(manifest, cacheMap) {
42
+ function getCacheHits(manifest, cacheMap) {
51
43
  const hits = [];
52
44
  const misses = [];
53
-
54
45
  for (const file of manifest) {
55
46
  const cached = cacheMap[file.relativePath];
56
47
  if (cached && cached.hash === file.hash) {
@@ -59,48 +50,35 @@ export function getCacheHits(manifest, cacheMap) {
59
50
  misses.push(file);
60
51
  }
61
52
  }
62
-
63
53
  return { hits, misses };
64
54
  }
65
55
 
66
56
  /**
67
- * Remove cache entries for files that no longer exist on disk.
68
- * @param {Record<string, { hash: string }>} cacheMap - Current cache map
69
- * @param {string[]} currentPaths - Relative paths of files currently on disk
70
- * @returns {Record<string, { hash: string }>} Cleaned cache map
57
+ * Remove entries for files that no longer exist on disk.
58
+ * @param {Object} cacheMap mutated in place
59
+ * @param {string[]} currentPaths relative paths currently on disk
71
60
  */
72
- export function purgeDeleted(cacheMap, currentPaths) {
73
- const pathSet = new Set(currentPaths);
74
- const cleaned = {};
75
- for (const [key, value] of Object.entries(cacheMap)) {
76
- if (pathSet.has(key)) {
77
- cleaned[key] = value;
61
+ function purgeDeleted(cacheMap, currentPaths) {
62
+ const current = new Set(currentPaths);
63
+ for (const key of Object.keys(cacheMap)) {
64
+ if (!current.has(key)) {
65
+ delete cacheMap[key];
78
66
  }
79
67
  }
80
- return cleaned;
81
68
  }
82
69
 
83
70
  /**
84
- * Auto-add .legacyver-cache/ to .gitignore if .gitignore exists
85
- * and doesn't already contain the entry.
86
- * @param {string} projectRoot - Root directory of the project being analyzed
71
+ * Auto-add .legacyver-cache/ to .gitignore if it exists in projectRoot.
72
+ * @param {string} projectRoot
87
73
  */
88
- export function addToGitignore(projectRoot) {
89
- const gitignorePath = join(projectRoot, '.gitignore');
90
- if (!existsSync(gitignorePath)) {
91
- return;
74
+ function autoAddToGitignore(projectRoot) {
75
+ const gitignorePath = path.join(projectRoot, '.gitignore');
76
+ if (!existsSync(gitignorePath)) return;
77
+ const content = readFileSync(gitignorePath, 'utf8');
78
+ if (!content.includes('.legacyver-cache')) {
79
+ appendFileSync(gitignorePath, '\n.legacyver-cache/\n');
80
+ logger.info('Added .legacyver-cache/ to .gitignore');
92
81
  }
93
-
94
- const content = readFileSync(gitignorePath, 'utf-8');
95
- const entry = '.legacyver-cache/';
96
-
97
- // Check if already present (exact line match)
98
- const lines = content.split('\n');
99
- if (lines.some((line) => line.trim() === entry || line.trim() === '.legacyver-cache')) {
100
- return;
101
- }
102
-
103
- // Append entry with a newline separator
104
- const separator = content.endsWith('\n') ? '' : '\n';
105
- writeFileSync(gitignorePath, content + separator + entry + '\n', 'utf-8');
106
82
  }
83
+
84
+ module.exports = { loadCache, saveCache, getCacheHits, purgeDeleted, autoAddToGitignore };