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.
- package/.agent/skills/openspec-apply-change/SKILL.md +156 -0
- package/.agent/skills/openspec-archive-change/SKILL.md +114 -0
- package/.agent/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.agent/skills/openspec-continue-change/SKILL.md +118 -0
- package/.agent/skills/openspec-explore/SKILL.md +290 -0
- package/.agent/skills/openspec-ff-change/SKILL.md +101 -0
- package/.agent/skills/openspec-new-change/SKILL.md +74 -0
- package/.agent/skills/openspec-onboard/SKILL.md +529 -0
- package/.agent/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.agent/skills/openspec-verify-change/SKILL.md +168 -0
- package/.agent/workflows/opsx-apply.md +149 -0
- package/.agent/workflows/opsx-archive.md +154 -0
- package/.agent/workflows/opsx-bulk-archive.md +239 -0
- package/.agent/workflows/opsx-continue.md +111 -0
- package/.agent/workflows/opsx-explore.md +171 -0
- package/.agent/workflows/opsx-ff.md +91 -0
- package/.agent/workflows/opsx-new.md +66 -0
- package/.agent/workflows/opsx-onboard.md +522 -0
- package/.agent/workflows/opsx-sync.md +131 -0
- package/.agent/workflows/opsx-verify.md +161 -0
- package/.github/prompts/opsx-apply.prompt.md +149 -0
- package/.github/prompts/opsx-archive.prompt.md +154 -0
- package/.github/prompts/opsx-bulk-archive.prompt.md +239 -0
- package/.github/prompts/opsx-continue.prompt.md +111 -0
- package/.github/prompts/opsx-explore.prompt.md +171 -0
- package/.github/prompts/opsx-ff.prompt.md +91 -0
- package/.github/prompts/opsx-new.prompt.md +66 -0
- package/.github/prompts/opsx-onboard.prompt.md +522 -0
- package/.github/prompts/opsx-sync.prompt.md +131 -0
- package/.github/prompts/opsx-verify.prompt.md +161 -0
- package/.github/skills/openspec-apply-change/SKILL.md +156 -0
- package/.github/skills/openspec-archive-change/SKILL.md +114 -0
- package/.github/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.github/skills/openspec-continue-change/SKILL.md +118 -0
- package/.github/skills/openspec-explore/SKILL.md +290 -0
- package/.github/skills/openspec-ff-change/SKILL.md +101 -0
- package/.github/skills/openspec-new-change/SKILL.md +74 -0
- package/.github/skills/openspec-onboard/SKILL.md +529 -0
- package/.github/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.github/skills/openspec-verify-change/SKILL.md +168 -0
- package/.legacyverignore.example +43 -0
- package/.legacyverrc +7 -0
- package/.opencode/command/opsx-apply.md +149 -0
- package/.opencode/command/opsx-archive.md +154 -0
- package/.opencode/command/opsx-bulk-archive.md +239 -0
- package/.opencode/command/opsx-continue.md +111 -0
- package/.opencode/command/opsx-explore.md +171 -0
- package/.opencode/command/opsx-ff.md +91 -0
- package/.opencode/command/opsx-new.md +66 -0
- package/.opencode/command/opsx-onboard.md +522 -0
- package/.opencode/command/opsx-sync.md +131 -0
- package/.opencode/command/opsx-verify.md +161 -0
- package/.opencode/skills/openspec-apply-change/SKILL.md +156 -0
- package/.opencode/skills/openspec-archive-change/SKILL.md +114 -0
- package/.opencode/skills/openspec-bulk-archive-change/SKILL.md +246 -0
- package/.opencode/skills/openspec-continue-change/SKILL.md +118 -0
- package/.opencode/skills/openspec-explore/SKILL.md +290 -0
- package/.opencode/skills/openspec-ff-change/SKILL.md +101 -0
- package/.opencode/skills/openspec-new-change/SKILL.md +74 -0
- package/.opencode/skills/openspec-onboard/SKILL.md +529 -0
- package/.opencode/skills/openspec-sync-specs/SKILL.md +138 -0
- package/.opencode/skills/openspec-verify-change/SKILL.md +168 -0
- package/LICENSE +1 -1
- package/README.md +128 -83
- package/bin/legacyver.js +48 -25
- package/legacyver-docs/SUMMARY.md +3 -0
- package/legacyver-docs/components.md +57 -0
- package/legacyver-docs/index.md +15 -0
- package/nul +2 -0
- package/package.json +23 -25
- package/src/cache/hash.js +9 -10
- package/src/cache/index.js +43 -65
- package/src/cli/commands/analyze.js +212 -190
- package/src/cli/commands/cache.js +15 -35
- package/src/cli/commands/init.js +63 -107
- package/src/cli/commands/providers.js +56 -81
- package/src/cli/commands/version.js +7 -10
- package/src/cli/ui.js +58 -77
- package/src/crawler/filters.js +41 -40
- package/src/crawler/index.js +52 -36
- package/src/crawler/manifest.js +31 -43
- package/src/crawler/walk.js +32 -38
- package/src/llm/chunker.js +34 -56
- package/src/llm/cost-estimator.js +68 -51
- package/src/llm/free-model.js +67 -0
- package/src/llm/index.js +22 -43
- package/src/llm/prompts.js +45 -33
- package/src/llm/providers/gemini.js +94 -0
- package/src/llm/providers/groq.js +55 -40
- package/src/llm/providers/ollama.js +38 -65
- package/src/llm/providers/openrouter.js +67 -0
- package/src/llm/queue.js +59 -88
- package/src/llm/re-prompter.js +41 -0
- package/src/llm/validator.js +72 -0
- package/src/parser/ast/generic.js +45 -222
- package/src/parser/ast/go.js +86 -205
- package/src/parser/ast/java.js +76 -146
- package/src/parser/ast/javascript.js +173 -241
- package/src/parser/ast/laravel/blade.js +56 -0
- package/src/parser/ast/laravel/classifier.js +30 -0
- package/src/parser/ast/laravel/controller.js +35 -0
- package/src/parser/ast/laravel/index.js +54 -0
- package/src/parser/ast/laravel/model.js +41 -0
- package/src/parser/ast/laravel/provider.js +28 -0
- package/src/parser/ast/laravel/routes.js +45 -0
- package/src/parser/ast/php.js +129 -0
- package/src/parser/ast/python.js +76 -199
- package/src/parser/ast/typescript.js +10 -244
- package/src/parser/body-extractor.js +40 -0
- package/src/parser/call-graph.js +50 -67
- package/src/parser/complexity-scorer.js +59 -0
- package/src/parser/index.js +61 -86
- package/src/parser/pattern-detector.js +71 -0
- package/src/parser/pkg-builder.js +36 -83
- package/src/renderer/html.js +63 -135
- package/src/renderer/index.js +23 -35
- package/src/renderer/json.js +17 -35
- package/src/renderer/markdown.js +83 -117
- package/src/utils/config.js +52 -53
- package/src/utils/errors.js +26 -41
- package/src/utils/logger.js +32 -53
- package/src/cli/flags.js +0 -87
- package/src/llm/providers/anthropic.js +0 -57
- package/src/llm/providers/google.js +0 -65
- package/src/llm/providers/openai.js +0 -52
- package/src/parser/ast/tree-sitter-init.js +0 -80
package/src/cli/commands/init.js
CHANGED
|
@@ -1,125 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
* Init command — interactive wizard to configure legacyver.
|
|
3
|
-
* Saves API key to OS user config using `conf`.
|
|
4
|
-
* Creates example .legacyverrc file.
|
|
5
|
-
*/
|
|
1
|
+
'use strict';
|
|
6
2
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import picocolors from 'picocolors';
|
|
12
|
-
import { logger } from '../../utils/logger.js';
|
|
13
|
-
|
|
14
|
-
const userConfig = new Conf({ projectName: 'legacyver' });
|
|
15
|
-
|
|
16
|
-
const PROVIDERS = ['anthropic', 'openai', 'google', 'groq', 'ollama'];
|
|
17
|
-
|
|
18
|
-
const ENV_VAR_MAP = {
|
|
19
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
20
|
-
openai: 'OPENAI_API_KEY',
|
|
21
|
-
google: 'GOOGLE_API_KEY',
|
|
22
|
-
groq: 'GROQ_API_KEY',
|
|
23
|
-
};
|
|
3
|
+
const { existsSync, writeFileSync } = require('fs');
|
|
4
|
+
const { join } = require('path');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const pc = require('picocolors');
|
|
24
7
|
|
|
25
8
|
function ask(rl, question) {
|
|
26
|
-
return new Promise((resolve) =>
|
|
27
|
-
rl.question(question, resolve);
|
|
28
|
-
});
|
|
9
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
29
10
|
}
|
|
30
11
|
|
|
31
|
-
async function
|
|
32
|
-
const rl = createInterface({
|
|
33
|
-
input: process.stdin,
|
|
34
|
-
output: process.stdout,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
console.log('');
|
|
38
|
-
console.log(picocolors.bold('Legacyver Setup Wizard'));
|
|
39
|
-
console.log('');
|
|
40
|
-
|
|
41
|
-
// Select provider
|
|
42
|
-
console.log('Available LLM providers:');
|
|
43
|
-
PROVIDERS.forEach((p, i) => {
|
|
44
|
-
console.log(` ${i + 1}. ${p}${p === 'ollama' ? ' (no API key needed)' : ''}`);
|
|
45
|
-
});
|
|
46
|
-
console.log('');
|
|
12
|
+
module.exports = async function initCommand() {
|
|
13
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
47
14
|
|
|
48
|
-
|
|
49
|
-
const providerIndex = parseInt(providerChoice, 10) - 1;
|
|
50
|
-
const provider = PROVIDERS[providerIndex >= 0 && providerIndex < PROVIDERS.length ? providerIndex : 0];
|
|
15
|
+
console.log(pc.bold('\nWelcome to Legacyver setup wizard!\n'));
|
|
51
16
|
|
|
52
|
-
console.log(`Selected: ${picocolors.cyan(provider)}`);
|
|
53
|
-
|
|
54
|
-
// API key (skip for ollama)
|
|
55
|
-
if (provider !== 'ollama') {
|
|
56
|
-
const envVar = ENV_VAR_MAP[provider];
|
|
57
|
-
const existingKey = process.env[envVar] || userConfig.get(`apiKeys.${provider}`);
|
|
58
|
-
|
|
59
|
-
if (existingKey) {
|
|
60
|
-
console.log(` API key found ${process.env[envVar] ? '(from environment)' : '(from saved config)'}`);
|
|
61
|
-
const overwrite = await ask(rl, ' Overwrite? (y/N): ');
|
|
62
|
-
if (overwrite.toLowerCase() === 'y') {
|
|
63
|
-
const newKey = await ask(rl, ` Enter ${envVar}: `);
|
|
64
|
-
if (newKey.trim()) {
|
|
65
|
-
userConfig.set(`apiKeys.${provider}`, newKey.trim());
|
|
66
|
-
logger.success('API key saved to user config');
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
const newKey = await ask(rl, ` Enter ${envVar}: `);
|
|
71
|
-
if (newKey.trim()) {
|
|
72
|
-
userConfig.set(`apiKeys.${provider}`, newKey.trim());
|
|
73
|
-
logger.success('API key saved to user config');
|
|
74
|
-
} else {
|
|
75
|
-
logger.warn(`No API key set. Set ${envVar} environment variable before running.`);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Save default provider
|
|
81
|
-
userConfig.set('defaultProvider', provider);
|
|
82
|
-
|
|
83
|
-
// Create .legacyverrc
|
|
84
17
|
const rcPath = join(process.cwd(), '.legacyverrc');
|
|
85
18
|
if (existsSync(rcPath)) {
|
|
86
|
-
const overwrite = await ask(rl,
|
|
87
|
-
if (overwrite.toLowerCase() !== 'y') {
|
|
19
|
+
const overwrite = await ask(rl, pc.yellow('⚠ .legacyverrc already exists. Overwrite? [y/N] '));
|
|
20
|
+
if (overwrite.trim().toLowerCase() !== 'y') {
|
|
21
|
+
console.log('Aborted.');
|
|
88
22
|
rl.close();
|
|
89
|
-
logger.info('Setup complete (existing .legacyverrc preserved).');
|
|
90
23
|
return;
|
|
91
24
|
}
|
|
92
25
|
}
|
|
93
26
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
27
|
+
const providerRaw = await ask(rl, `LLM provider [openrouter/gemini/groq/ollama] (default: openrouter): `);
|
|
28
|
+
const providerChoice = providerRaw.trim() || 'openrouter';
|
|
29
|
+
const isOllama = providerChoice === 'ollama';
|
|
30
|
+
const isGroq = providerChoice === 'groq';
|
|
31
|
+
const isGemini = providerChoice === 'gemini';
|
|
32
|
+
|
|
33
|
+
const defaultModel = isOllama
|
|
34
|
+
? 'llama3.2'
|
|
35
|
+
: isGroq
|
|
36
|
+
? 'llama-3.3-70b-versatile'
|
|
37
|
+
: isGemini
|
|
38
|
+
? 'gemini-2.0-flash'
|
|
39
|
+
: 'meta-llama/llama-3.3-70b-instruct:free';
|
|
40
|
+
|
|
41
|
+
let apiKey = '';
|
|
42
|
+
if (!isOllama) {
|
|
43
|
+
const keyLabel = isGemini ? 'Google Gemini' : isGroq ? 'Groq' : 'OpenRouter';
|
|
44
|
+
const keyHint = isGemini ? 'https://aistudio.google.com/apikey' : isGroq ? 'https://console.groq.com/keys' : 'https://openrouter.ai/keys';
|
|
45
|
+
apiKey = await ask(rl, `${keyLabel} API key (leave blank to set via env var — see ${keyHint}): `);
|
|
46
|
+
}
|
|
102
47
|
|
|
103
|
-
|
|
104
|
-
|
|
48
|
+
const modelRaw = await ask(rl, `Default model (default: ${defaultModel}): `);
|
|
49
|
+
const formatRaw = await ask(rl, `Default output format [markdown/html/json] (default: markdown): `);
|
|
105
50
|
|
|
106
51
|
rl.close();
|
|
107
|
-
console.log('');
|
|
108
|
-
console.log(picocolors.green('Setup complete!'));
|
|
109
|
-
console.log(`Run ${picocolors.cyan('legacyver analyze ./src')} to generate documentation.`);
|
|
110
|
-
console.log('');
|
|
111
|
-
}
|
|
112
52
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
.
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
53
|
+
const config = {
|
|
54
|
+
provider: providerChoice,
|
|
55
|
+
model: modelRaw.trim() || defaultModel,
|
|
56
|
+
format: formatRaw.trim() || 'markdown',
|
|
57
|
+
out: './legacyver-docs',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (apiKey.trim()) {
|
|
61
|
+
if (isGemini) {
|
|
62
|
+
config.geminiApiKey = apiKey.trim();
|
|
63
|
+
} else if (isGroq) {
|
|
64
|
+
config.groqApiKey = apiKey.trim();
|
|
65
|
+
} else {
|
|
66
|
+
config.apiKey = apiKey.trim();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
writeFileSync(rcPath, JSON.stringify(config, null, 2), 'utf8');
|
|
71
|
+
console.log(pc.green('\n✓ Created .legacyverrc'));
|
|
72
|
+
|
|
73
|
+
const exampleCmd = isOllama
|
|
74
|
+
? 'legacyver analyze --provider ollama'
|
|
75
|
+
: isGroq
|
|
76
|
+
? 'legacyver analyze --provider groq'
|
|
77
|
+
: isGemini
|
|
78
|
+
? 'legacyver analyze --provider gemini'
|
|
79
|
+
: 'legacyver analyze';
|
|
80
|
+
console.log(pc.cyan(`\nRun \`${exampleCmd}\` to generate documentation.`));
|
|
81
|
+
};
|
|
@@ -1,83 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
envVar: 'ANTHROPIC_API_KEY',
|
|
15
|
-
defaultModel: 'claude-haiku-3-5',
|
|
16
|
-
costPer1kInput: 0.00025,
|
|
17
|
-
costPer1kOutput: 0.00125,
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: 'openai',
|
|
21
|
-
envVar: 'OPENAI_API_KEY',
|
|
22
|
-
defaultModel: 'gpt-4o-mini',
|
|
23
|
-
costPer1kInput: 0.00015,
|
|
24
|
-
costPer1kOutput: 0.0006,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'google',
|
|
28
|
-
envVar: 'GOOGLE_API_KEY',
|
|
29
|
-
defaultModel: 'gemini-1.5-flash',
|
|
30
|
-
costPer1kInput: 0.000075,
|
|
31
|
-
costPer1kOutput: 0.0003,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'groq',
|
|
35
|
-
envVar: 'GROQ_API_KEY',
|
|
36
|
-
defaultModel: 'llama-3.3-70b-versatile',
|
|
37
|
-
costPer1kInput: 0.00059,
|
|
38
|
-
costPer1kOutput: 0.00079,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'ollama',
|
|
42
|
-
envVar: null,
|
|
43
|
-
defaultModel: 'llama3.2',
|
|
44
|
-
costPer1kInput: 0,
|
|
45
|
-
costPer1kOutput: 0,
|
|
46
|
-
},
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const pc = require('picocolors');
|
|
4
|
+
const logger = require('../../utils/logger');
|
|
5
|
+
|
|
6
|
+
const RECOMMENDED_MODELS = [
|
|
7
|
+
{ id: 'meta-llama/llama-3.3-70b-instruct:free', context: '128k', inputCost: 0, outputCost: 0, free: true },
|
|
8
|
+
{ id: 'anthropic/claude-haiku-3-5', context: '200k', inputCost: 0.80, outputCost: 4.00, free: false },
|
|
9
|
+
{ id: 'anthropic/claude-sonnet-4-5', context: '200k', inputCost: 3.00, outputCost: 15.00, free: false },
|
|
10
|
+
{ id: 'openai/gpt-4o-mini', context: '128k', inputCost: 0.15, outputCost: 0.60, free: false },
|
|
11
|
+
{ id: 'openai/gpt-4o', context: '128k', inputCost: 5.00, outputCost: 15.00, free: false },
|
|
12
|
+
{ id: 'google/gemini-flash-1.5', context: '1M', inputCost: 0.075, outputCost: 0.30, free: false },
|
|
13
|
+
{ id: 'mistralai/mistral-7b-instruct:free', context: '32k', inputCost: 0, outputCost: 0, free: true },
|
|
47
14
|
];
|
|
48
15
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
16
|
+
module.exports = async function providersCommand() {
|
|
17
|
+
const { loadConfig } = require('../../utils/config');
|
|
18
|
+
const config = loadConfig({});
|
|
19
|
+
|
|
20
|
+
console.log(pc.bold('\nLegacyver — Supported LLM Providers\n'));
|
|
21
|
+
console.log(pc.bold('OpenRouter') + ' (https://openrouter.ai)');
|
|
22
|
+
console.log(' Unified gateway to 200+ models. Set OPENROUTER_API_KEY env variable.');
|
|
23
|
+
console.log(' Status: ' + (process.env.OPENROUTER_API_KEY ? pc.green('API key detected') : pc.yellow('No API key found')));
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log(pc.bold('Ollama') + ' (https://ollama.ai)');
|
|
26
|
+
console.log(' Local offline LLM. No API key required. Run `ollama serve` first.');
|
|
27
|
+
console.log('');
|
|
28
|
+
console.log(pc.bold('Groq') + ' (https://groq.com)');
|
|
29
|
+
console.log(' Fastest free LLM inference. Set GROQ_API_KEY env variable.');
|
|
30
|
+
console.log(' Status: ' + (process.env.GROQ_API_KEY ? pc.green('API key detected') : pc.yellow('No API key found')));
|
|
31
|
+
console.log(' Get a free key at: https://console.groq.com/keys');
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log(pc.bold('Google Gemini') + ' (https://ai.google.dev)');
|
|
34
|
+
console.log(' Free tier: 15 req/min, 1,500 req/day. Set GEMINI_API_KEY env variable.');
|
|
35
|
+
console.log(' Status: ' + (process.env.GEMINI_API_KEY ? pc.green('API key detected') : pc.yellow('No API key found')));
|
|
36
|
+
console.log(' Get a free key at: https://aistudio.google.com/apikey');
|
|
37
|
+
console.log('');
|
|
38
|
+
|
|
39
|
+
console.log(pc.bold('Recommended Models (via OpenRouter):'));
|
|
40
|
+
console.log('');
|
|
41
|
+
const header = ` ${'Model ID'.padEnd(48)} ${'Context'.padEnd(8)} ${'Input $/1M'.padEnd(12)} ${'Output $/1M'.padEnd(12)}`;
|
|
42
|
+
console.log(pc.dim(header));
|
|
43
|
+
console.log(pc.dim(' ' + '-'.repeat(84)));
|
|
44
|
+
|
|
45
|
+
for (const m of RECOMMENDED_MODELS) {
|
|
46
|
+
const badge = m.free ? pc.green(' [FREE]') : '';
|
|
47
|
+
const selected = m.id === config.model ? pc.cyan(' ◀ selected') : '';
|
|
48
|
+
const inputCostStr = m.free ? 'FREE' : `$${m.inputCost.toFixed(3)}`;
|
|
49
|
+
const outputCostStr = m.free ? 'FREE' : `$${m.outputCost.toFixed(3)}`;
|
|
50
|
+
console.log(
|
|
51
|
+
` ${m.id.padEnd(48)} ${m.context.padEnd(8)} ${inputCostStr.padEnd(12)} ${outputCostStr.padEnd(12)}${badge}${selected}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log('');
|
|
56
|
+
console.log(pc.dim('Fetch live model list from OpenRouter: https://openrouter.ai/api/v1/models'));
|
|
57
|
+
console.log('');
|
|
58
|
+
};
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
* Version command — reads version from package.json and prints it.
|
|
3
|
-
*/
|
|
1
|
+
'use strict';
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
const { readFileSync } = require('fs');
|
|
4
|
+
const { join } = require('path');
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log(`legacyver v${pkg.version}`);
|
|
12
|
-
}
|
|
6
|
+
module.exports = function versionCommand() {
|
|
7
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../../package.json'), 'utf8'));
|
|
8
|
+
console.log(pkg.version);
|
|
9
|
+
};
|
package/src/cli/ui.js
CHANGED
|
@@ -1,104 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
* CLI UI helpers: spinners, progress bars, prompts, summary.
|
|
3
|
-
*/
|
|
1
|
+
'use strict';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import { isCIMode } from '../utils/logger.js';
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const { SingleBar, Presets } = require('cli-progress');
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
const pc = require('picocolors');
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
export function createSpinner(text) {
|
|
17
|
-
if (isCIMode()) {
|
|
8
|
+
const isCI = !process.stdout.isTTY;
|
|
9
|
+
|
|
10
|
+
function createSpinner(text) {
|
|
11
|
+
if (isCI) {
|
|
12
|
+
console.log(`[spinner] ${text}`);
|
|
18
13
|
return {
|
|
19
|
-
start()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
start: (t) => t && console.log(`[spinner] ${t}`),
|
|
15
|
+
succeed: (t) => console.log(`[done] ${t || text}`),
|
|
16
|
+
fail: (t) => console.error(`[fail] ${t || text}`),
|
|
17
|
+
warn: (t) => console.warn(`[warn] ${t || text}`),
|
|
18
|
+
stop: () => {},
|
|
19
|
+
text: text,
|
|
24
20
|
};
|
|
25
21
|
}
|
|
26
22
|
return ora({ text, spinner: 'dots' });
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* @param {number} total - Total items
|
|
32
|
-
* @returns {object} Progress bar with start/increment/stop methods
|
|
33
|
-
*/
|
|
34
|
-
export function createProgressBar(total) {
|
|
35
|
-
if (isCIMode()) {
|
|
25
|
+
function createProgressBar(total) {
|
|
26
|
+
if (isCI) {
|
|
36
27
|
let current = 0;
|
|
37
28
|
return {
|
|
38
|
-
start()
|
|
39
|
-
increment(
|
|
40
|
-
current
|
|
41
|
-
|
|
42
|
-
console.log(`Processing ${current}/${total} files...`);
|
|
43
|
-
}
|
|
29
|
+
start: () => {},
|
|
30
|
+
increment: () => {
|
|
31
|
+
current++;
|
|
32
|
+
process.stdout.write(`Progress: ${current}/${total}\n`);
|
|
44
33
|
},
|
|
45
|
-
stop()
|
|
34
|
+
stop: () => {},
|
|
46
35
|
};
|
|
47
36
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
37
|
+
const bar = new SingleBar(
|
|
38
|
+
{
|
|
39
|
+
format: `${pc.cyan('Analyzing')} [{bar}] {percentage}% | {value}/{total} files`,
|
|
40
|
+
clearOnComplete: false,
|
|
41
|
+
hideCursor: true,
|
|
42
|
+
},
|
|
43
|
+
Presets.shades_classic
|
|
44
|
+
);
|
|
56
45
|
return {
|
|
57
|
-
start()
|
|
58
|
-
increment(
|
|
59
|
-
stop()
|
|
46
|
+
start: () => bar.start(total, 0),
|
|
47
|
+
increment: () => bar.increment(),
|
|
48
|
+
stop: () => bar.stop(),
|
|
60
49
|
};
|
|
61
50
|
}
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
*/
|
|
68
|
-
export async function confirmPrompt(message) {
|
|
69
|
-
if (isCIMode()) {
|
|
70
|
-
return false; // CI mode requires --no-confirm
|
|
52
|
+
async function confirmPrompt(message) {
|
|
53
|
+
if (isCI) {
|
|
54
|
+
console.error(`[confirm] ${message} — non-interactive mode, aborting. Use --no-confirm to skip.`);
|
|
55
|
+
process.exit(4);
|
|
71
56
|
}
|
|
72
|
-
|
|
73
|
-
const rl = createInterface({
|
|
74
|
-
input: process.stdin,
|
|
75
|
-
output: process.stdout,
|
|
76
|
-
});
|
|
77
|
-
|
|
78
57
|
return new Promise((resolve) => {
|
|
79
|
-
rl.
|
|
58
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
59
|
+
rl.question(`${pc.yellow('?')} ${message} [y/N] `, (answer) => {
|
|
80
60
|
rl.close();
|
|
81
|
-
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
61
|
+
resolve(answer.trim().toLowerCase() === 'y' || answer.trim().toLowerCase() === 'yes');
|
|
82
62
|
});
|
|
83
63
|
});
|
|
84
64
|
}
|
|
85
65
|
|
|
86
|
-
|
|
87
|
-
* Print analysis summary.
|
|
88
|
-
* @param {object} stats - Summary statistics
|
|
89
|
-
*/
|
|
90
|
-
export function printSummary(stats) {
|
|
66
|
+
function printSummary(stats) {
|
|
91
67
|
console.log('');
|
|
92
|
-
console.log(
|
|
93
|
-
console.log(` Files analyzed:
|
|
94
|
-
console.log(` Files
|
|
95
|
-
console.log(` Files
|
|
96
|
-
console.log(`
|
|
97
|
-
console.log(`
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
68
|
+
console.log(pc.bold('=== Legacyver Summary ==='));
|
|
69
|
+
console.log(` Files analyzed: ${pc.green(stats.filesAnalyzed)}`);
|
|
70
|
+
console.log(` Files cached: ${pc.cyan(stats.filesCached || 0)}`);
|
|
71
|
+
console.log(` Files skipped: ${pc.yellow(stats.filesSkipped || 0)}`);
|
|
72
|
+
console.log(` Tokens used: ${stats.tokensUsed !== undefined ? stats.tokensUsed : 'n/a'}`);
|
|
73
|
+
console.log(` Estimated cost: ${stats.estimatedCost !== undefined ? '$' + stats.estimatedCost.toFixed(4) : 'n/a'}`);
|
|
74
|
+
if (stats.qualityWarnings > 0) {
|
|
75
|
+
console.log(` Quality warnings: ${pc.yellow(stats.qualityWarnings)}`);
|
|
76
|
+
}
|
|
77
|
+
if (stats.errors && stats.errors.length > 0) {
|
|
78
|
+
console.log(` Errors: ${pc.red(stats.errors.length)}`);
|
|
79
|
+
stats.errors.forEach((e) => console.log(` - ${e}`));
|
|
80
|
+
}
|
|
81
|
+
console.log(` Output: ${stats.outputDir}`);
|
|
103
82
|
console.log('');
|
|
104
83
|
}
|
|
84
|
+
|
|
85
|
+
module.exports = { createSpinner, createProgressBar, confirmPrompt, printSummary };
|
package/src/crawler/filters.js
CHANGED
|
@@ -1,57 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
* Language extension map and default ignore patterns.
|
|
3
|
-
*/
|
|
1
|
+
'use strict';
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
javascript: ['.js', '.jsx', '.mjs'],
|
|
3
|
+
const LANGUAGE_EXTENSIONS = {
|
|
4
|
+
javascript: ['.js', '.jsx', '.mjs', '.cjs'],
|
|
7
5
|
typescript: ['.ts', '.tsx'],
|
|
8
6
|
python: ['.py'],
|
|
9
7
|
java: ['.java'],
|
|
10
8
|
go: ['.go'],
|
|
9
|
+
php: ['.php', '.blade.php'],
|
|
11
10
|
};
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
* Reverse map: extension -> language
|
|
15
|
-
*/
|
|
16
|
-
export const EXTENSION_TO_LANGUAGE = {};
|
|
17
|
-
for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
|
|
18
|
-
for (const ext of exts) {
|
|
19
|
-
EXTENSION_TO_LANGUAGE[ext] = lang;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
12
|
+
const ALL_EXTENSIONS = Object.values(LANGUAGE_EXTENSIONS).flat();
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
* All supported file extensions as glob patterns.
|
|
25
|
-
*/
|
|
26
|
-
export const SUPPORTED_EXTENSIONS = Object.values(LANGUAGE_EXTENSIONS).flat();
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Default ignore patterns (always applied).
|
|
30
|
-
*/
|
|
31
|
-
export const DEFAULT_IGNORE_PATTERNS = [
|
|
14
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
32
15
|
'**/node_modules/**',
|
|
33
16
|
'**/.git/**',
|
|
34
17
|
'**/dist/**',
|
|
35
18
|
'**/build/**',
|
|
36
|
-
'
|
|
37
|
-
'**/.nuxt/**',
|
|
19
|
+
'**/vendor/**',
|
|
38
20
|
'**/coverage/**',
|
|
39
21
|
'**/__pycache__/**',
|
|
40
|
-
'
|
|
41
|
-
'**/
|
|
42
|
-
'**/.venv/**',
|
|
43
|
-
'**/env/**',
|
|
44
|
-
'**/.env/**',
|
|
45
|
-
'**/vendor/**',
|
|
46
|
-
'**/.idea/**',
|
|
47
|
-
'**/.vscode/**',
|
|
48
|
-
'**/*.min.js',
|
|
49
|
-
'**/*.min.css',
|
|
50
|
-
'**/*.map',
|
|
51
|
-
'**/*.lock',
|
|
52
|
-
'**/package-lock.json',
|
|
53
|
-
'**/yarn.lock',
|
|
54
|
-
'**/pnpm-lock.yaml',
|
|
22
|
+
'**/storage/**',
|
|
23
|
+
'**/bootstrap/cache/**',
|
|
55
24
|
'**/.legacyver-cache/**',
|
|
56
25
|
'**/legacyver-docs/**',
|
|
26
|
+
'**/test/**',
|
|
27
|
+
'**/tests/**',
|
|
28
|
+
'**/spec/**',
|
|
29
|
+
'**/openspec/**',
|
|
30
|
+
'**/*.test.js',
|
|
31
|
+
'**/*.test.ts',
|
|
32
|
+
'**/*.spec.js',
|
|
33
|
+
'**/*.spec.ts',
|
|
34
|
+
'**/*.test.py',
|
|
57
35
|
];
|
|
36
|
+
|
|
37
|
+
function detectLanguage(ext) {
|
|
38
|
+
for (const [lang, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
|
|
39
|
+
if (exts.includes(ext)) return lang;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function detectPrimaryLanguage(files) {
|
|
45
|
+
const counts = {};
|
|
46
|
+
for (const f of files) {
|
|
47
|
+
const lang = f.language;
|
|
48
|
+
if (lang) counts[lang] = (counts[lang] || 0) + 1;
|
|
49
|
+
}
|
|
50
|
+
let max = 0;
|
|
51
|
+
let primary = null;
|
|
52
|
+
for (const [lang, cnt] of Object.entries(counts)) {
|
|
53
|
+
if (cnt > max) { max = cnt; primary = lang; }
|
|
54
|
+
}
|
|
55
|
+
return primary;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { LANGUAGE_EXTENSIONS, ALL_EXTENSIONS, DEFAULT_IGNORE_PATTERNS, detectLanguage, detectPrimaryLanguage };
|