repowiki-agent 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const configCommand: Command;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,eAAO,MAAM,aAAa,SACW,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { Command } from 'commander';
2
+ import { loadLLMConfig, saveGlobalConfig } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+ export const configCommand = new Command('config')
5
+ .description('管理全局 LLM API 鉴权配置');
6
+ // 配置键名映射转换
7
+ function normalizeKey(key) {
8
+ const k = key.toLowerCase();
9
+ if (k === 'endpoint' || k === 'apiendpoint')
10
+ return 'apiEndpoint';
11
+ if (k === 'model' || k === 'modelname')
12
+ return 'modelName';
13
+ if (k === 'key' || k === 'apikey')
14
+ return 'apiKey';
15
+ return null;
16
+ }
17
+ configCommand
18
+ .command('set')
19
+ .arguments('<key> <value>')
20
+ .description('设置全局配置(例如:endpoint, model, key)')
21
+ .action(async (key, value) => {
22
+ const field = normalizeKey(key);
23
+ if (!field) {
24
+ console.error(chalk.red(`❌ 无效的配置项: "${key}"。可选:endpoint, model, key`));
25
+ process.exit(1);
26
+ }
27
+ try {
28
+ await saveGlobalConfig({ [field]: value });
29
+ console.log(chalk.green(`✓ 成功将全局配置 [${field}] 设置为: "${value}"`));
30
+ }
31
+ catch (err) {
32
+ console.error(chalk.red('❌ 保存配置失败:'), err?.message || err);
33
+ process.exit(1);
34
+ }
35
+ });
36
+ configCommand
37
+ .command('get')
38
+ .arguments('[key]')
39
+ .description('查看全局配置')
40
+ .action(async (key) => {
41
+ try {
42
+ const config = await loadLLMConfig();
43
+ if (key) {
44
+ const field = normalizeKey(key);
45
+ if (!field) {
46
+ console.error(chalk.red(`❌ 无效的配置项: "${key}"。可选:endpoint, model, key`));
47
+ process.exit(1);
48
+ }
49
+ let value = config[field];
50
+ if (field === 'apiKey' && value) {
51
+ // 脱敏显示
52
+ value = value.slice(0, 6) + '...' + value.slice(-4);
53
+ }
54
+ console.log(`${field}: ${value || chalk.gray('(未设置)')}`);
55
+ }
56
+ else {
57
+ console.log(chalk.bold('\n⚙️ 全局配置预览:'));
58
+ console.log(` apiEndpoint: ${config.apiEndpoint}`);
59
+ console.log(` modelName: ${config.modelName}`);
60
+ const maskedKey = config.apiKey
61
+ ? config.apiKey.slice(0, 6) + '...' + config.apiKey.slice(-4)
62
+ : chalk.gray('(未设置)');
63
+ console.log(` apiKey: ${maskedKey}\n`);
64
+ }
65
+ }
66
+ catch (err) {
67
+ console.error(chalk.red('❌ 获取配置失败:'), err?.message || err);
68
+ process.exit(1);
69
+ }
70
+ });
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const generateCommand: Command;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,eAAe,SAoEtB,CAAC"}
@@ -0,0 +1,76 @@
1
+ import { Command } from 'commander';
2
+ import { runPipeline } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import * as path from 'node:path';
6
+ export const generateCommand = new Command('generate')
7
+ .arguments('[path]')
8
+ .description('为指定的项目目录生成 Wiki 文档')
9
+ .option('-o, --output <dir>', 'Wiki 输出目录(默认: <path>/docs/wiki)')
10
+ .option('-m, --model <model>', '使用的大语言模型名称')
11
+ .option('-c, --concurrency <number>', '并发大模型请求数', (val) => parseInt(val, 10), 3)
12
+ .option('--skip-llm', '免大模型,仅基于本地模板快速生成文档结构')
13
+ .option('--json-stdout', '以 JSON Lines 格式流式输出执行进度(适用于集成 IDE 插件)')
14
+ .action(async (targetPath, options) => {
15
+ const workspacePath = path.resolve(targetPath || '.');
16
+ const outputDir = options.output ? path.resolve(options.output) : undefined;
17
+ const jsonStdout = !!options.jsonStdout;
18
+ let spinner = null;
19
+ if (!jsonStdout) {
20
+ console.log(chalk.bold.cyan('\n🚀 开始运行 RepoWiki 文档生成引擎...\n'));
21
+ spinner = ora('初始化环境中...').start();
22
+ }
23
+ const handleProgress = (event) => {
24
+ if (jsonStdout) {
25
+ // 流式输出 JSONL
26
+ console.log(JSON.stringify(event));
27
+ return;
28
+ }
29
+ if (!spinner)
30
+ return;
31
+ if (event.type === 'PROGRESS') {
32
+ spinner.text = chalk.dim(`[${event.stage}] `) + event.message;
33
+ // 根据阶段定制颜色或特定显示
34
+ if (event.stage === 'Scanning') {
35
+ spinner.color = 'blue';
36
+ }
37
+ else if (event.stage === 'Analysis') {
38
+ spinner.color = 'magenta';
39
+ }
40
+ else if (event.stage === 'LLM Inference') {
41
+ spinner.color = 'yellow';
42
+ }
43
+ else {
44
+ spinner.color = 'cyan';
45
+ }
46
+ }
47
+ else if (event.type === 'DONE') {
48
+ spinner.succeed(chalk.green('文档生成完成!'));
49
+ console.log('\n' + chalk.bold.green('✨ 生成结果详情:'));
50
+ console.log(chalk.gray(' - 输出目录: ') + chalk.underline.cyan(event.payload.docsPath));
51
+ console.log(chalk.gray(' - 生成文件数: ') + chalk.bold(event.payload.pagesCount) + ' 个\n');
52
+ }
53
+ else if (event.type === 'ERROR') {
54
+ spinner.fail(chalk.red('文档生成失败!'));
55
+ console.error('\n' + chalk.red(`❌ [错误码 ${event.code}] `) + event.message + '\n');
56
+ }
57
+ };
58
+ try {
59
+ await runPipeline({
60
+ workspacePath,
61
+ outputDir,
62
+ modelName: options.model,
63
+ concurrency: options.concurrency,
64
+ skipLlm: !!options.skipLlm,
65
+ onProgress: handleProgress,
66
+ });
67
+ }
68
+ catch (err) {
69
+ if (jsonStdout) {
70
+ // runPipeline 内部已经输出了 ERROR 事件,这里捕获只作为最终异常退出
71
+ process.exit(1);
72
+ }
73
+ process.exit(1);
74
+ }
75
+ });
76
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAsB,MAAM,eAAe,CAAC;AAChE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC;KACjD,SAAS,CAAC,QAAQ,CAAC;KACnB,WAAW,CAAC,oBAAoB,CAAC;KACjC,MAAM,CAAC,oBAAoB,EAAE,iCAAiC,CAAC;KAC/D,MAAM,CAAC,qBAAqB,EAAE,YAAY,CAAC;KAC3C,MAAM,CAAC,4BAA4B,EAAE,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;KAC/E,MAAM,CAAC,YAAY,EAAE,sBAAsB,CAAC;KAC5C,MAAM,CAAC,eAAe,EAAE,uCAAuC,CAAC;KAChE,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;IAClC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,MAAM,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC;IAExC,IAAI,OAAO,GAAkC,IAAI,CAAC;IAElD,IAAI,CAAC,UAAU,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC;QAC/D,OAAO,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,cAAc,GAAG,CAAC,KAAoB,EAAE,EAAE;QAC5C,IAAI,UAAU,EAAE,CAAC;YACb,aAAa;YACb,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;YACnC,OAAO;QACX,CAAC;QAED,IAAI,CAAC,OAAO;YAAE,OAAO;QAErB,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;YAC9D,gBAAgB;YAChB,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBAC7B,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;YAC3B,CAAC;iBAAM,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACpC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;YAC9B,CAAC;iBAAM,IAAI,KAAK,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;gBACzC,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC;YAC3B,CAAC;QACL,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC/B,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACrF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,MAAM,CAAC,CAAC;QAC3F,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YACnC,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QACrF,CAAC;IACL,CAAC,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,WAAW,CAAC;YACd,aAAa;YACb,SAAS;YACT,SAAS,EAAE,OAAO,CAAC,KAAK;YACxB,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO;YAC1B,UAAU,EAAE,cAAc;SAC7B,CAAC,CAAC;IACP,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAChB,IAAI,UAAU,EAAE,CAAC;YACb,6CAA6C;YAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const loginCommand: Command;
3
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,eAAO,MAAM,YAAY,SAoDnB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { Command } from 'commander';
2
+ import { loadLLMConfig, saveGlobalConfig } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+ import * as readline from 'node:readline/promises';
5
+ import { stdin as input, stdout as output } from 'node:process';
6
+ export const loginCommand = new Command('login')
7
+ .description('交互式配置大语言模型(LLM)API 鉴权凭证')
8
+ .action(async () => {
9
+ console.log(chalk.bold.cyan('\n🗝️ RepoWiki 鉴权登录向导\n'));
10
+ const rl = readline.createInterface({ input, output });
11
+ try {
12
+ const currentConfig = await loadLLMConfig();
13
+ // 1. 获取 API Endpoint
14
+ const endpointPrompt = `请输入 API 端点地址 ${chalk.gray(`[默认: ${currentConfig.apiEndpoint || 'https://api.openai.com/v1'}]: `)}`;
15
+ const endpointInput = await rl.question(endpointPrompt);
16
+ const apiEndpoint = endpointInput.trim() || currentConfig.apiEndpoint || 'https://api.openai.com/v1';
17
+ // 2. 获取 Model Name
18
+ const modelPrompt = `请输入默认模型名称 ${chalk.gray(`[默认: ${currentConfig.modelName || 'gpt-4o'}]: `)}`;
19
+ const modelInput = await rl.question(modelPrompt);
20
+ const modelName = modelInput.trim() || currentConfig.modelName || 'gpt-4o';
21
+ // 3. 获取 API Key
22
+ const hasExistingKey = !!currentConfig.apiKey;
23
+ const keyPromptStr = hasExistingKey
24
+ ? `请输入 API 密钥 (API Key) ${chalk.gray(`[留空保留已有密钥: ${currentConfig.apiKey.slice(0, 6)}...${currentConfig.apiKey.slice(-4)}]: `)}`
25
+ : '请输入 API 密钥 (API Key): ';
26
+ const keyInput = await rl.question(keyPromptStr);
27
+ let apiKey = keyInput.trim();
28
+ if (!apiKey && hasExistingKey) {
29
+ apiKey = currentConfig.apiKey;
30
+ }
31
+ if (!apiKey) {
32
+ console.error(chalk.red('\n❌ 错误: API 密钥不能为空。'));
33
+ rl.close();
34
+ process.exit(1);
35
+ }
36
+ // 保存配置
37
+ await saveGlobalConfig({
38
+ apiEndpoint,
39
+ modelName,
40
+ apiKey,
41
+ });
42
+ console.log(chalk.green('\n✓ 凭证保存成功!配置文件路径: ~/.repowiki/config.json\n'));
43
+ }
44
+ catch (err) {
45
+ console.error(chalk.red('\n❌ 配置失败:'), err?.message || err);
46
+ process.exit(1);
47
+ }
48
+ finally {
49
+ rl.close();
50
+ }
51
+ });
52
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const scanCommand: Command;
3
+ //# sourceMappingURL=scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,eAAO,MAAM,WAAW,SAkDlB,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { Command } from 'commander';
2
+ import { scanDirectory, buildTreeString, detectTechStack, detectEntrypoints, buildDependencyGraph, analyzeModules, analyzeApiRoutes, analyzeDatabaseModels, analyzeConfigs, analyzeWorkflows, } from 'repowiki-core';
3
+ import * as path from 'node:path';
4
+ import chalk from 'chalk';
5
+ export const scanCommand = new Command('scan')
6
+ .arguments('[path]')
7
+ .description('对指定目录运行 AST 扫描和静态分析,并输出原始 JSON 数据')
8
+ .option('--pretty', '格式化输出的 JSON 文本')
9
+ .action(async (targetPath, options) => {
10
+ const workspacePath = path.resolve(targetPath || '.');
11
+ const pretty = !!options.pretty;
12
+ try {
13
+ const files = await scanDirectory(workspacePath);
14
+ const treeStr = buildTreeString(files, workspacePath);
15
+ const techStackResult = await detectTechStack(workspacePath, files);
16
+ const entrypoints = await detectEntrypoints(workspacePath, files);
17
+ const dependencyGraph = await buildDependencyGraph(workspacePath, files);
18
+ const modules = await analyzeModules(workspacePath, files, null); // 不使用 LLM
19
+ const apiRoutes = await analyzeApiRoutes(workspacePath, files);
20
+ const databaseModels = await analyzeDatabaseModels(workspacePath, files);
21
+ const configInfos = await analyzeConfigs(workspacePath, files);
22
+ const workflowInfos = await analyzeWorkflows(workspacePath, files);
23
+ const result = {
24
+ project: {
25
+ name: path.basename(workspacePath) || 'unnamed-project',
26
+ rootPath: workspacePath,
27
+ languages: techStackResult.languages,
28
+ frameworks: techStackResult.frameworks,
29
+ packageManagers: techStackResult.packageManagers,
30
+ databases: techStackResult.databases,
31
+ services: techStackResult.services,
32
+ entrypoints,
33
+ configFiles: techStackResult.configFiles,
34
+ },
35
+ tree: treeStr,
36
+ modules,
37
+ dependencies: dependencyGraph,
38
+ apiRoutes,
39
+ databaseModels,
40
+ configs: configInfos,
41
+ workflows: workflowInfos,
42
+ };
43
+ const jsonOutput = pretty
44
+ ? JSON.stringify(result, null, 2)
45
+ : JSON.stringify(result);
46
+ console.log(jsonOutput);
47
+ }
48
+ catch (err) {
49
+ console.error(chalk.red('❌ 扫描失败:'), err?.message || err);
50
+ process.exit(1);
51
+ }
52
+ });
53
+ //# sourceMappingURL=scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan.js","sourceRoot":"","sources":["../../src/commands/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EACH,aAAa,EACb,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,gBAAgB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KACzC,SAAS,CAAC,QAAQ,CAAC;KACnB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,UAAU,EAAE,gBAAgB,CAAC;KACpC,MAAM,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE;IAClC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;IAEhC,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACtD,MAAM,eAAe,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACpE,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAClE,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU;QAC5E,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,cAAc,GAAG,MAAM,qBAAqB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QACzE,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAC/D,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAEnE,MAAM,MAAM,GAAG;YACX,OAAO,EAAE;gBACL,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,iBAAiB;gBACvD,QAAQ,EAAE,aAAa;gBACvB,SAAS,EAAE,eAAe,CAAC,SAAS;gBACpC,UAAU,EAAE,eAAe,CAAC,UAAU;gBACtC,eAAe,EAAE,eAAe,CAAC,eAAe;gBAChD,SAAS,EAAE,eAAe,CAAC,SAAS;gBACpC,QAAQ,EAAE,eAAe,CAAC,QAAQ;gBAClC,WAAW;gBACX,WAAW,EAAE,eAAe,CAAC,WAAW;aAC3C;YACD,IAAI,EAAE,OAAO;YACb,OAAO;YACP,YAAY,EAAE,eAAe;YAC7B,SAAS;YACT,cAAc;YACd,OAAO,EAAE,WAAW;YACpB,SAAS,EAAE,aAAa;SAC3B,CAAC;QAEF,MAAM,UAAU,GAAG,MAAM;YACrB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YACjC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAE7B,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,IAAI,GAAG,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { generateCommand } from './commands/generate.js';
4
+ import { scanCommand } from './commands/scan.js';
5
+ import { configCommand } from './commands/config.js';
6
+ import { loginCommand } from './commands/login.js';
7
+ const program = new Command();
8
+ program
9
+ .name('repowiki')
10
+ .description('RepoWiki: 本地化代码库 Wiki 知识库生成工具')
11
+ .version('0.1.0');
12
+ // 注册子命令
13
+ program.addCommand(generateCommand);
14
+ program.addCommand(scanCommand);
15
+ program.addCommand(configCommand);
16
+ program.addCommand(loginCommand);
17
+ program.parse(process.argv);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,+BAA+B,CAAC;KAC5C,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,QAAQ;AACR,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAEjC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "repowiki-agent",
3
+ "version": "0.1.0",
4
+ "description": "RepoWiki 命令行工具",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "repowiki": "dist/index.js"
9
+ },
10
+ "dependencies": {
11
+ "commander": "^12.1.0",
12
+ "chalk": "^5.3.0",
13
+ "ora": "^8.0.0",
14
+ "repowiki-core": "0.1.0"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.5.0",
18
+ "rimraf": "^5.0.0",
19
+ "@types/node": "^20.0.0"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsc --watch",
24
+ "typecheck": "tsc --noEmit",
25
+ "clean": "rimraf dist",
26
+ "start": "node dist/index.js"
27
+ }
28
+ }
@@ -0,0 +1,71 @@
1
+ import { Command } from 'commander';
2
+ import { loadLLMConfig, saveGlobalConfig, type LLMConfig } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+
5
+ export const configCommand = new Command('config')
6
+ .description('管理全局 LLM API 鉴权配置');
7
+
8
+ // 配置键名映射转换
9
+ function normalizeKey(key: string): keyof LLMConfig | null {
10
+ const k = key.toLowerCase();
11
+ if (k === 'endpoint' || k === 'apiendpoint') return 'apiEndpoint';
12
+ if (k === 'model' || k === 'modelname') return 'modelName';
13
+ if (k === 'key' || k === 'apikey') return 'apiKey';
14
+ return null;
15
+ }
16
+
17
+ configCommand
18
+ .command('set')
19
+ .arguments('<key> <value>')
20
+ .description('设置全局配置(例如:endpoint, model, key)')
21
+ .action(async (key, value) => {
22
+ const field = normalizeKey(key);
23
+ if (!field) {
24
+ console.error(chalk.red(`❌ 无效的配置项: "${key}"。可选:endpoint, model, key`));
25
+ process.exit(1);
26
+ }
27
+
28
+ try {
29
+ await saveGlobalConfig({ [field]: value });
30
+ console.log(chalk.green(`✓ 成功将全局配置 [${field}] 设置为: "${value}"`));
31
+ } catch (err: any) {
32
+ console.error(chalk.red('❌ 保存配置失败:'), err?.message || err);
33
+ process.exit(1);
34
+ }
35
+ });
36
+
37
+ configCommand
38
+ .command('get')
39
+ .arguments('[key]')
40
+ .description('查看全局配置')
41
+ .action(async (key) => {
42
+ try {
43
+ const config = await loadLLMConfig();
44
+
45
+ if (key) {
46
+ const field = normalizeKey(key);
47
+ if (!field) {
48
+ console.error(chalk.red(`❌ 无效的配置项: "${key}"。可选:endpoint, model, key`));
49
+ process.exit(1);
50
+ }
51
+
52
+ let value = config[field];
53
+ if (field === 'apiKey' && value) {
54
+ // 脱敏显示
55
+ value = value.slice(0, 6) + '...' + value.slice(-4);
56
+ }
57
+ console.log(`${field}: ${value || chalk.gray('(未设置)')}`);
58
+ } else {
59
+ console.log(chalk.bold('\n⚙️ 全局配置预览:'));
60
+ console.log(` apiEndpoint: ${config.apiEndpoint}`);
61
+ console.log(` modelName: ${config.modelName}`);
62
+ const maskedKey = config.apiKey
63
+ ? config.apiKey.slice(0, 6) + '...' + config.apiKey.slice(-4)
64
+ : chalk.gray('(未设置)');
65
+ console.log(` apiKey: ${maskedKey}\n`);
66
+ }
67
+ } catch (err: any) {
68
+ console.error(chalk.red('❌ 获取配置失败:'), err?.message || err);
69
+ process.exit(1);
70
+ }
71
+ });
@@ -0,0 +1,75 @@
1
+ import { Command } from 'commander';
2
+ import { runPipeline, type PipelineEvent } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import * as path from 'node:path';
6
+
7
+ export const generateCommand = new Command('generate')
8
+ .arguments('[path]')
9
+ .description('为指定的项目目录生成 Wiki 文档')
10
+ .option('-o, --output <dir>', 'Wiki 输出目录(默认: <path>/docs/wiki)')
11
+ .option('-m, --model <model>', '使用的大语言模型名称')
12
+ .option('-c, --concurrency <number>', '并发大模型请求数', (val) => parseInt(val, 10), 3)
13
+ .option('--skip-llm', '免大模型,仅基于本地模板快速生成文档结构')
14
+ .option('--json-stdout', '以 JSON Lines 格式流式输出执行进度(适用于集成 IDE 插件)')
15
+ .action(async (targetPath, options) => {
16
+ const workspacePath = path.resolve(targetPath || '.');
17
+ const outputDir = options.output ? path.resolve(options.output) : undefined;
18
+ const jsonStdout = !!options.jsonStdout;
19
+
20
+ let spinner: ReturnType<typeof ora> | null = null;
21
+
22
+ if (!jsonStdout) {
23
+ console.log(chalk.bold.cyan('\n🚀 开始运行 RepoWiki 文档生成引擎...\n'));
24
+ spinner = ora('初始化环境中...').start();
25
+ }
26
+
27
+ const handleProgress = (event: PipelineEvent) => {
28
+ if (jsonStdout) {
29
+ // 流式输出 JSONL
30
+ console.log(JSON.stringify(event));
31
+ return;
32
+ }
33
+
34
+ if (!spinner) return;
35
+
36
+ if (event.type === 'PROGRESS') {
37
+ spinner.text = chalk.dim(`[${event.stage}] `) + event.message;
38
+ // 根据阶段定制颜色或特定显示
39
+ if (event.stage === 'Scanning') {
40
+ spinner.color = 'blue';
41
+ } else if (event.stage === 'Analysis') {
42
+ spinner.color = 'magenta';
43
+ } else if (event.stage === 'LLM Inference') {
44
+ spinner.color = 'yellow';
45
+ } else {
46
+ spinner.color = 'cyan';
47
+ }
48
+ } else if (event.type === 'DONE') {
49
+ spinner.succeed(chalk.green('文档生成完成!'));
50
+ console.log('\n' + chalk.bold.green('✨ 生成结果详情:'));
51
+ console.log(chalk.gray(' - 输出目录: ') + chalk.underline.cyan(event.payload.docsPath));
52
+ console.log(chalk.gray(' - 生成文件数: ') + chalk.bold(event.payload.pagesCount) + ' 个\n');
53
+ } else if (event.type === 'ERROR') {
54
+ spinner.fail(chalk.red('文档生成失败!'));
55
+ console.error('\n' + chalk.red(`❌ [错误码 ${event.code}] `) + event.message + '\n');
56
+ }
57
+ };
58
+
59
+ try {
60
+ await runPipeline({
61
+ workspacePath,
62
+ outputDir,
63
+ modelName: options.model,
64
+ concurrency: options.concurrency,
65
+ skipLlm: !!options.skipLlm,
66
+ onProgress: handleProgress,
67
+ });
68
+ } catch (err: any) {
69
+ if (jsonStdout) {
70
+ // runPipeline 内部已经输出了 ERROR 事件,这里捕获只作为最终异常退出
71
+ process.exit(1);
72
+ }
73
+ process.exit(1);
74
+ }
75
+ });
@@ -0,0 +1,59 @@
1
+ import { Command } from 'commander';
2
+ import { loadLLMConfig, saveGlobalConfig } from 'repowiki-core';
3
+ import chalk from 'chalk';
4
+ import * as readline from 'node:readline/promises';
5
+ import { stdin as input, stdout as output } from 'node:process';
6
+
7
+ export const loginCommand = new Command('login')
8
+ .description('交互式配置大语言模型(LLM)API 鉴权凭证')
9
+ .action(async () => {
10
+ console.log(chalk.bold.cyan('\n🗝️ RepoWiki 鉴权登录向导\n'));
11
+
12
+ const rl = readline.createInterface({ input, output });
13
+
14
+ try {
15
+ const currentConfig = await loadLLMConfig();
16
+
17
+ // 1. 获取 API Endpoint
18
+ const endpointPrompt = `请输入 API 端点地址 ${chalk.gray(`[默认: ${currentConfig.apiEndpoint || 'https://api.openai.com/v1'}]: `)}`;
19
+ const endpointInput = await rl.question(endpointPrompt);
20
+ const apiEndpoint = endpointInput.trim() || currentConfig.apiEndpoint || 'https://api.openai.com/v1';
21
+
22
+ // 2. 获取 Model Name
23
+ const modelPrompt = `请输入默认模型名称 ${chalk.gray(`[默认: ${currentConfig.modelName || 'gpt-4o'}]: `)}`;
24
+ const modelInput = await rl.question(modelPrompt);
25
+ const modelName = modelInput.trim() || currentConfig.modelName || 'gpt-4o';
26
+
27
+ // 3. 获取 API Key
28
+ const hasExistingKey = !!currentConfig.apiKey;
29
+ const keyPromptStr = hasExistingKey
30
+ ? `请输入 API 密钥 (API Key) ${chalk.gray(`[留空保留已有密钥: ${currentConfig.apiKey.slice(0, 6)}...${currentConfig.apiKey.slice(-4)}]: `)}`
31
+ : '请输入 API 密钥 (API Key): ';
32
+ const keyInput = await rl.question(keyPromptStr);
33
+ let apiKey = keyInput.trim();
34
+
35
+ if (!apiKey && hasExistingKey) {
36
+ apiKey = currentConfig.apiKey;
37
+ }
38
+
39
+ if (!apiKey) {
40
+ console.error(chalk.red('\n❌ 错误: API 密钥不能为空。'));
41
+ rl.close();
42
+ process.exit(1);
43
+ }
44
+
45
+ // 保存配置
46
+ await saveGlobalConfig({
47
+ apiEndpoint,
48
+ modelName,
49
+ apiKey,
50
+ });
51
+
52
+ console.log(chalk.green('\n✓ 凭证保存成功!配置文件路径: ~/.repowiki/config.json\n'));
53
+ } catch (err: any) {
54
+ console.error(chalk.red('\n❌ 配置失败:'), err?.message || err);
55
+ process.exit(1);
56
+ } finally {
57
+ rl.close();
58
+ }
59
+ });
@@ -0,0 +1,67 @@
1
+ import { Command } from 'commander';
2
+ import {
3
+ scanDirectory,
4
+ buildTreeString,
5
+ detectTechStack,
6
+ detectEntrypoints,
7
+ buildDependencyGraph,
8
+ analyzeModules,
9
+ analyzeApiRoutes,
10
+ analyzeDatabaseModels,
11
+ analyzeConfigs,
12
+ analyzeWorkflows,
13
+ } from 'repowiki-core';
14
+ import * as path from 'node:path';
15
+ import chalk from 'chalk';
16
+
17
+ export const scanCommand = new Command('scan')
18
+ .arguments('[path]')
19
+ .description('对指定目录运行 AST 扫描和静态分析,并输出原始 JSON 数据')
20
+ .option('--pretty', '格式化输出的 JSON 文本')
21
+ .action(async (targetPath, options) => {
22
+ const workspacePath = path.resolve(targetPath || '.');
23
+ const pretty = !!options.pretty;
24
+
25
+ try {
26
+ const files = await scanDirectory(workspacePath);
27
+ const treeStr = buildTreeString(files, workspacePath);
28
+ const techStackResult = await detectTechStack(workspacePath, files);
29
+ const entrypoints = await detectEntrypoints(workspacePath, files);
30
+ const dependencyGraph = await buildDependencyGraph(workspacePath, files);
31
+ const modules = await analyzeModules(workspacePath, files, null); // 不使用 LLM
32
+ const apiRoutes = await analyzeApiRoutes(workspacePath, files);
33
+ const databaseModels = await analyzeDatabaseModels(workspacePath, files);
34
+ const configInfos = await analyzeConfigs(workspacePath, files);
35
+ const workflowInfos = await analyzeWorkflows(workspacePath, files);
36
+
37
+ const result = {
38
+ project: {
39
+ name: path.basename(workspacePath) || 'unnamed-project',
40
+ rootPath: workspacePath,
41
+ languages: techStackResult.languages,
42
+ frameworks: techStackResult.frameworks,
43
+ packageManagers: techStackResult.packageManagers,
44
+ databases: techStackResult.databases,
45
+ services: techStackResult.services,
46
+ entrypoints,
47
+ configFiles: techStackResult.configFiles,
48
+ },
49
+ tree: treeStr,
50
+ modules,
51
+ dependencies: dependencyGraph,
52
+ apiRoutes,
53
+ databaseModels,
54
+ configs: configInfos,
55
+ workflows: workflowInfos,
56
+ };
57
+
58
+ const jsonOutput = pretty
59
+ ? JSON.stringify(result, null, 2)
60
+ : JSON.stringify(result);
61
+
62
+ console.log(jsonOutput);
63
+ } catch (err: any) {
64
+ console.error(chalk.red('❌ 扫描失败:'), err?.message || err);
65
+ process.exit(1);
66
+ }
67
+ });
package/src/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { generateCommand } from './commands/generate.js';
5
+ import { scanCommand } from './commands/scan.js';
6
+ import { configCommand } from './commands/config.js';
7
+ import { loginCommand } from './commands/login.js';
8
+
9
+ const program = new Command();
10
+
11
+ program
12
+ .name('repowiki')
13
+ .description('RepoWiki: 本地化代码库 Wiki 知识库生成工具')
14
+ .version('0.1.0');
15
+
16
+ // 注册子命令
17
+ program.addCommand(generateCommand);
18
+ program.addCommand(scanCommand);
19
+ program.addCommand(configCommand);
20
+ program.addCommand(loginCommand);
21
+
22
+ program.parse(process.argv);
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }