import { getGlobalConfig, saveGlobalConfig, getCurrentProjectConfig, saveCurrentProjectConfig, } from './config.js' import { env } from './env' import { getCwd } from './state' import { exec } from 'child_process' import { logError } from './log' import { memoize, sample } from 'lodash-es' import { promisify } from 'util' import { getIsGit } from './git' import { queryQuick } from '../services/claude' const execPromise = promisify(exec) async function getFrequentlyModifiedFiles(): Promise { if (process.env.NODE_ENV === 'test') return [] if (env.platform === 'windows') return [] if (!(await getIsGit())) return [] try { let filenames = '' // Look up files modified by the user's recent commits // Be careful to do it async, so it doesn't block the main thread const { stdout: userFilenames } = await execPromise( 'git log -n 1000 --pretty=format: --name-only --diff-filter=M --author=$(git config user.email) | sort | uniq -c | sort -nr | head -n 20', { cwd: getCwd(), encoding: 'utf8' }, ) filenames = 'Files modified by user:\n' + userFilenames // Look at other users' commits if we don't have enough files if (userFilenames.split('\n').length < 10) { const { stdout: allFilenames } = await execPromise( 'git log -n 1000 --pretty=format: --name-only --diff-filter=M | sort | uniq -c | sort -nr | head -n 20', { cwd: getCwd(), encoding: 'utf8' }, ) filenames += '\n\nFiles modified by other users:\n' + allFilenames } const response = await queryQuick({ systemPrompt: [ "You are an expert at analyzing git history. Given a list of files and their modification counts, return exactly five filenames that are frequently modified and represent core application logic (not auto-generated files, dependencies, or configuration). Make sure filenames are diverse, not all in the same folder, and are a mix of user and other users. Return only the filenames' basenames (without the path) separated by newlines with no explanation.", ], userPrompt: filenames, }) const content = response.message.content[0] if (!content || content.type !== 'text') return [] const chosenFilenames = content.text.trim().split('\n') if (chosenFilenames.length < 5) { // Likely error return [] } return chosenFilenames } catch (err) { logError(err) return [] } } export const getExampleCommands = memoize(async (): Promise => { const globalConfig = getGlobalConfig() const projectConfig = getCurrentProjectConfig() const now = Date.now() const lastGenerated = projectConfig.exampleFilesGeneratedAt ?? 0 const oneWeek = 7 * 24 * 60 * 60 * 1000 // Regenerate examples if they're over a week old if (now - lastGenerated > oneWeek) { projectConfig.exampleFiles = [] } // Update global startup count const newGlobalConfig = { ...globalConfig, numStartups: (globalConfig.numStartups ?? 0) + 1, } saveGlobalConfig(newGlobalConfig) // // If no example files cached, kickstart fetch in background // if (!projectConfig.exampleFiles?.length) { // getFrequentlyModifiedFiles().then(files => { // if (files.length) { // saveCurrentProjectConfig({ // ...getCurrentProjectConfig(), // exampleFiles: files, // exampleFilesGeneratedAt: Date.now(), // }) // } // }) // } const frequentFile = projectConfig.exampleFiles?.length ? sample(projectConfig.exampleFiles) : '' return [ 'fix lint errors', 'fix typecheck errors', `how does ${frequentFile} work?`, `refactor ${frequentFile}`, 'how do I log an error?', `edit ${frequentFile} to...`, `write a test for ${frequentFile}`, 'create a util logging.py that...', ] })