334 lines
12 KiB
TypeScript
334 lines
12 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
/**
|
|
* Company Analyzer - Comprehensive company research and analysis
|
|
* Automated company intelligence gathering for business research
|
|
*/
|
|
|
|
import { Database } from 'bun:sqlite';
|
|
import { Command } from 'commander';
|
|
import {
|
|
gatherBasicInfo,
|
|
gatherFoundationInfo,
|
|
gatherFinancialInfo,
|
|
gatherMarketPositionInfo,
|
|
gatherCultureInfo,
|
|
gatherRecentDevelopments,
|
|
} from './company-gatherers';
|
|
import { JSON_INDENTATION } from './constants';
|
|
import {
|
|
generateHeaderSection,
|
|
generateBasicInfoSection,
|
|
generateLeadershipSection,
|
|
generateFinancialSection,
|
|
generateMarketPositionSection,
|
|
generateRecentDevelopmentsSection,
|
|
generateCultureSection,
|
|
generateSourcesSection,
|
|
generateFooter,
|
|
} from './report-generators';
|
|
import { WebResearcher } from './web-researcher';
|
|
|
|
const logger = {
|
|
log: (message: string) => Bun.write(Bun.stdout, `${message}\n`),
|
|
warn: (message: string) => Bun.write(Bun.stderr, `${message}\n`),
|
|
error: (message: string) => Bun.write(Bun.stderr, `${message}\n`),
|
|
};
|
|
|
|
interface CompanyAnalysisOptions {
|
|
company: string;
|
|
focus: 'foundation' | 'financial' | 'market-position' | 'comprehensive';
|
|
outputFormat?: 'json' | 'markdown' | 'csv';
|
|
outputFile?: string;
|
|
}
|
|
|
|
|
|
interface CompanyData {
|
|
基本信息: {
|
|
company_name: string;
|
|
founded_date: string;
|
|
headquarters: string;
|
|
website: string;
|
|
employee_count: string;
|
|
industry: string;
|
|
sector: string;
|
|
};
|
|
leadership: { ceo: string; key_executives: Array<{ name: string; position: string; experience: string }> };
|
|
financial: { revenue: string; market_cap: string; profit_margin: string; revenue_growth: string };
|
|
market_position: { market_share: string; competitors: string[]; customer_segments: string[]; geographic_presence: string[] };
|
|
recent_developments: Array<{ date: string; type: string; description: string; source: string }>;
|
|
culture_employment: { employee_satisfaction: string; benefits: string[]; work_life_balance: string; diversity_initiatives: string[] };
|
|
sources: Array<{ url: string; title: string; access_date: string; reliability: string }>;
|
|
};
|
|
|
|
/**
|
|
* Company Analyzer class for comprehensive company research and analysis
|
|
*/
|
|
class CompanyAnalyzer {
|
|
private webResearcher: WebResearcher;
|
|
private db: Database;
|
|
|
|
/** Creates a new CompanyAnalyzer instance */
|
|
constructor() {
|
|
this.webResearcher = new WebResearcher();
|
|
this.db = new Database('company_analysis.db');
|
|
this.initializeDatabase();
|
|
}
|
|
|
|
/** Initializes the SQLite database */
|
|
private initializeDatabase(): void {
|
|
this.db.run(`
|
|
CREATE TABLE IF NOT EXISTS company_analyses (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
company_name TEXT,
|
|
analysis_focus TEXT,
|
|
analysis_data TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE(company_name, analysis_focus)
|
|
)
|
|
`);
|
|
}
|
|
|
|
/** Performs comprehensive company analysis
|
|
* @param {CompanyAnalysisOptions} options - Analysis configuration including company name and focus area
|
|
* @returns {Promise<CompanyData>} Promise that resolves to complete company analysis data
|
|
*/
|
|
async analyzeCompany(options: CompanyAnalysisOptions): Promise<CompanyData> {
|
|
logger.log(`🏢 Starting company analysis for: ${options.company}`);
|
|
logger.log(`📊 Focus area: ${options.focus}`);
|
|
|
|
try {
|
|
const companyData = this.initializeCompanyData(options.company);
|
|
|
|
// Gather information based on focus
|
|
await this.gatherFocusSpecificInfo(options.focus, companyData);
|
|
|
|
// Save analysis to database
|
|
this.saveAnalysis(options.company, options.focus, companyData);
|
|
|
|
logger.log(`✅ Company analysis completed for ${options.company}`);
|
|
return companyData;
|
|
} catch (error) {
|
|
logger.error('❌ Company analysis failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes a new CompanyData structure with default values
|
|
* @param {string} companyName - The name of the company to analyze
|
|
* @returns {CompanyData} A new CompanyData structure with default values
|
|
*/
|
|
private initializeCompanyData(companyName: string): CompanyData {
|
|
return {
|
|
基本信息: {
|
|
company_name: companyName,
|
|
founded_date: '',
|
|
headquarters: '',
|
|
website: '',
|
|
employee_count: '',
|
|
industry: '',
|
|
sector: '',
|
|
},
|
|
leadership: { ceo: '', key_executives: [] },
|
|
financial: { revenue: '', market_cap: '', profit_margin: '', revenue_growth: '' },
|
|
market_position: { market_share: '', competitors: [], customer_segments: [], geographic_presence: [] },
|
|
recent_developments: [],
|
|
culture_employment: { employee_satisfaction: '', benefits: [], work_life_balance: '', diversity_initiatives: [] },
|
|
sources: [],
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gathers information specific to the analysis focus area
|
|
* @param {string} focus - The analysis focus area (foundation, financial, market-position, comprehensive)
|
|
* @param {CompanyData} companyData - The company data object to populate with gathered information
|
|
* @returns {Promise<void>} Promise that resolves when focus-specific information gathering is complete
|
|
*/
|
|
private async gatherFocusSpecificInfo(focus: string, companyData: CompanyData): Promise<void> {
|
|
switch (focus) {
|
|
case 'foundation':
|
|
await this.gatherFoundationData(companyData);
|
|
break;
|
|
case 'financial':
|
|
await this.gatherFinancialData(companyData);
|
|
break;
|
|
case 'market-position':
|
|
await this.gatherMarketPositionData(companyData);
|
|
break;
|
|
case 'comprehensive':
|
|
await this.gatherFoundationData(companyData);
|
|
await this.gatherFinancialData(companyData);
|
|
await this.gatherMarketPositionData(companyData);
|
|
await this.gatherCultureData(companyData);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gathers all foundation data for the company
|
|
* @param {CompanyData} companyData - The company data object to populate with foundation information
|
|
* @returns {Promise<void>} Promise that resolves when foundation data gathering is complete
|
|
*/
|
|
private async gatherFoundationData(companyData: CompanyData): Promise<void> {
|
|
await gatherBasicInfo(this.webResearcher, companyData);
|
|
await gatherFoundationInfo(this.webResearcher, companyData);
|
|
await gatherRecentDevelopments(this.webResearcher, companyData);
|
|
}
|
|
|
|
/**
|
|
* Gathers financial data for the company
|
|
* @param {CompanyData} companyData - The company data object to populate with financial information
|
|
* @returns {Promise<void>} Promise that resolves when financial data gathering is complete
|
|
*/
|
|
private async gatherFinancialData(companyData: CompanyData): Promise<void> {
|
|
await gatherBasicInfo(this.webResearcher, companyData);
|
|
await gatherFinancialInfo(this.webResearcher, companyData);
|
|
await gatherRecentDevelopments(this.webResearcher, companyData);
|
|
}
|
|
|
|
/**
|
|
* Gathers market position data for the company
|
|
* @param {CompanyData} companyData - The company data object to populate with market position information
|
|
* @returns {Promise<void>} Promise that resolves when market position data gathering is complete
|
|
*/
|
|
private async gatherMarketPositionData(companyData: CompanyData): Promise<void> {
|
|
await gatherBasicInfo(this.webResearcher, companyData);
|
|
await gatherMarketPositionInfo(this.webResearcher, companyData);
|
|
await gatherRecentDevelopments(this.webResearcher, companyData);
|
|
}
|
|
|
|
/**
|
|
* Gathers culture data for the company
|
|
* @param {CompanyData} companyData - The company data object to populate with culture and employment information
|
|
* @returns {Promise<void>} Promise that resolves when culture data gathering is complete
|
|
*/
|
|
private async gatherCultureData(companyData: CompanyData): Promise<void> {
|
|
await gatherCultureInfo(this.webResearcher, companyData);
|
|
}
|
|
|
|
/**
|
|
* Saves the completed company analysis to the database
|
|
* @param {string} companyName - The name of the company being analyzed
|
|
* @param {string} focus - The analysis focus area (foundation, financial, market-position, comprehensive)
|
|
* @param {CompanyData} data - The complete company analysis data to save
|
|
* @returns {void}
|
|
*/
|
|
private saveAnalysis(companyName: string, focus: string, data: CompanyData): void {
|
|
this.db.run(
|
|
'INSERT OR REPLACE INTO company_analyses (company_name, analysis_focus, analysis_data) VALUES (?, ?, ?)',
|
|
[companyName, focus, JSON.stringify(data, null, JSON_INDENTATION)]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Generates a formatted report from the company analysis data
|
|
* @param {CompanyData} companyData - The complete company analysis data
|
|
* @param {string} format - The output format (markdown, json, csv)
|
|
* @returns {string} The formatted report as a string
|
|
*/
|
|
generateReport(companyData: CompanyData, format = 'markdown'): string {
|
|
switch (format) {
|
|
case 'json':
|
|
return JSON.stringify(companyData, null, JSON_INDENTATION);
|
|
case 'csv':
|
|
return this.generateCSVReport(companyData);
|
|
case 'markdown':
|
|
default:
|
|
return this.generateMarkdownReport(companyData);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a comprehensive Markdown report from company analysis data
|
|
* @param {CompanyData} companyData - The complete company analysis data
|
|
* @returns {string} The formatted Markdown report as a string
|
|
*/
|
|
private generateMarkdownReport(companyData: CompanyData): string {
|
|
const sections = [
|
|
generateHeaderSection(companyData),
|
|
generateBasicInfoSection(companyData),
|
|
generateLeadershipSection(companyData),
|
|
generateFinancialSection(companyData),
|
|
generateMarketPositionSection(companyData),
|
|
generateRecentDevelopmentsSection(companyData),
|
|
generateCultureSection(companyData),
|
|
generateSourcesSection(companyData),
|
|
generateFooter()
|
|
];
|
|
|
|
return sections.join('\n\n');
|
|
}
|
|
|
|
/**
|
|
* Generates a CSV report from company analysis data
|
|
* @param {CompanyData} companyData - The complete company analysis data
|
|
* @returns {string} The formatted CSV report as a string
|
|
*/
|
|
private generateCSVReport(companyData: CompanyData): string {
|
|
const headers = ['Category', 'Field', 'Value'];
|
|
const rows = [
|
|
['Basic Information', 'Company Name', companyData.基本信息.company_name],
|
|
['Financial', 'Revenue', companyData.financial.revenue],
|
|
['Market Position', 'Market Share', companyData.market_position.market_share],
|
|
];
|
|
|
|
return [headers, ...rows].map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');
|
|
}
|
|
|
|
/**
|
|
* Closes the analyzer and cleans up resources
|
|
* @returns {void}
|
|
*/
|
|
close(): void {
|
|
this.webResearcher.close();
|
|
this.db.close();
|
|
}
|
|
}
|
|
|
|
// CLI Interface
|
|
const program = new Command();
|
|
|
|
program
|
|
.name('company-analyzer')
|
|
.description('Comprehensive company research and analysis')
|
|
.version('1.0.0');
|
|
|
|
program
|
|
.requiredOption('-c, --company <string>', 'Company name to analyze')
|
|
.option('-f, --focus <string>', 'Analysis focus area', 'comprehensive')
|
|
.option('-o, --output <string>', 'Output file')
|
|
.option('--format <string>', 'Output format', 'markdown')
|
|
.action(async options => {
|
|
const analyzer = new CompanyAnalyzer();
|
|
|
|
try {
|
|
const analysis = await analyzer.analyzeCompany({
|
|
company: options.company,
|
|
focus: options.focus,
|
|
outputFormat: options.format,
|
|
outputFile: options.output,
|
|
});
|
|
|
|
const report = analyzer.generateReport(analysis, options.format);
|
|
|
|
if (options.output) {
|
|
await Bun.write(options.output, report);
|
|
logger.log(`📄 Company analysis saved to: ${options.output}`);
|
|
} else {
|
|
logger.log(report);
|
|
}
|
|
} catch (error) {
|
|
logger.error('❌ Error:', (error as Error).message);
|
|
process.exit(1);
|
|
} finally {
|
|
analyzer.close();
|
|
}
|
|
});
|
|
|
|
// Execute CLI
|
|
if (import.meta.main) {
|
|
program.parse();
|
|
}
|
|
|
|
export { CompanyAnalyzer, type CompanyAnalysisOptions, type CompanyData }; |