UNPKG

scai

Version:

> AI-powered CLI tool for commit messages **and** pull request reviews — using local models.

187 lines (186 loc) â€ĸ 6.28 kB
import fs from 'fs'; import path from 'path'; import { CONFIG_PATH, SCAI_HOME, SCAI_REPOS } from './constants.js'; import { getDbForRepo } from './db/client.js'; import { normalizePath } from './utils/normalizePath.js'; import chalk from 'chalk'; import { getHashedRepoKey } from './utils/repoKey.js'; const defaultConfig = { model: 'codellama:13b', contextLength: 4096, language: 'ts', indexDir: '', githubToken: '', repos: {}, activeRepo: null, }; function ensureConfigDir() { if (!fs.existsSync(SCAI_HOME)) { fs.mkdirSync(SCAI_HOME, { recursive: true }); } } export function readConfig() { try { const content = fs.readFileSync(CONFIG_PATH, 'utf-8'); return { ...defaultConfig, ...JSON.parse(content) }; } catch { return defaultConfig; } } export function writeConfig(newCfg) { ensureConfigDir(); const current = readConfig(); const merged = { ...current, ...newCfg, repos: { ...current.repos, ...(newCfg.repos || {}), }, }; // Remove repos explicitly set to null if (newCfg.repos) { for (const [key, value] of Object.entries(newCfg.repos)) { if (value === null) { delete merged.repos[key]; } } } fs.writeFileSync(CONFIG_PATH, JSON.stringify(merged, null, 2)); } export const Config = { getModel() { const cfg = readConfig(); const repoCfg = cfg.repos?.[cfg.activeRepo ?? '']; return repoCfg?.model || cfg.model; }, setModel(model) { const cfg = readConfig(); const active = cfg.activeRepo; if (active) { cfg.repos[active] = { ...cfg.repos[active], model }; writeConfig(cfg); console.log(`đŸ“Ļ Model set to: ${model}`); } else { writeConfig({ model }); console.log(`đŸ“Ļ Default model set to: ${model}`); } }, getLanguage() { const cfg = readConfig(); const repoCfg = cfg.repos?.[cfg.activeRepo ?? '']; return repoCfg?.language || cfg.language; }, setLanguage(language) { const cfg = readConfig(); const active = cfg.activeRepo; if (active) { cfg.repos[active] = { ...cfg.repos[active], language }; writeConfig(cfg); console.log(`đŸ—Ŗī¸ Language set to: ${language}`); } else { writeConfig({ language }); console.log(`đŸ—Ŗī¸ Default language set to: ${language}`); } }, getIndexDir() { const cfg = readConfig(); const activeRepo = cfg.activeRepo; if (!activeRepo) return ''; return cfg.repos[activeRepo]?.indexDir ?? ''; }, async setIndexDir(indexDir) { // Normalize the provided index directory const normalizedIndexDir = normalizePath(indexDir); // Compute a stable repo key const repoKey = getHashedRepoKey(normalizedIndexDir); const scaiRepoRoot = path.join(SCAI_REPOS, repoKey); // Ensure base folders exist fs.mkdirSync(scaiRepoRoot, { recursive: true }); // Set the active repo using the precomputed repoKey this.setActiveRepo(repoKey); // Update the repo configuration with the normalized indexDir await this.setRepoIndexDir(repoKey, normalizedIndexDir); // Initialize DB if it does not exist const dbPath = path.join(scaiRepoRoot, 'db.sqlite'); if (!fs.existsSync(dbPath)) { console.log(`đŸ“Ļ Database not found. ${chalk.green('Initializing DB')} at ${normalizePath(dbPath)}`); getDbForRepo(); } }, async setRepoIndexDir(repoKey, indexDir) { const cfg = readConfig(); if (!cfg.repos[repoKey]) cfg.repos[repoKey] = {}; cfg.repos[repoKey] = { ...cfg.repos[repoKey], indexDir, // Already normalized }; await writeConfig(cfg); console.log(`✅ Repo index directory set for ${repoKey} : ${indexDir}`); }, setActiveRepo(repoKey) { const cfg = readConfig(); cfg.activeRepo = repoKey; if (!cfg.repos[repoKey]) cfg.repos[repoKey] = {}; writeConfig(cfg); console.log(`✅ Active repo switched to: ${repoKey}`); }, printAllRepos() { const cfg = readConfig(); const keys = Object.keys(cfg.repos || {}); if (!keys.length) { console.log('â„šī¸ No repositories configured yet.'); return; } console.log('📁 Configured repositories:\n'); for (const key of keys) { const r = cfg.repos[key]; const isActive = cfg.activeRepo === key; const label = isActive ? chalk.green(`✅ ${key} (active)`) : chalk.white(` ${key}`); console.log(`- ${label}`); console.log(` â†ŗ indexDir: ${r.indexDir}`); } }, getGitHubToken() { const cfg = readConfig(); const active = cfg.activeRepo; if (active) return cfg.repos[active]?.githubToken || null; return cfg.githubToken || null; }, setGitHubToken(token) { const cfg = readConfig(); const active = cfg.activeRepo; if (active) { if (!cfg.repos[active]) cfg.repos[active] = {}; cfg.repos[active] = { ...cfg.repos[active], githubToken: token }; } else { cfg.githubToken = token; } writeConfig(cfg); console.log('✅ GitHub token updated'); }, show() { const cfg = readConfig(); const active = cfg.activeRepo; console.log(`🔧 Current configuration:`); console.log(` Active index dir: ${active || 'Not Set'}`); const repoCfg = active ? cfg.repos[active] : {}; console.log(` Model : ${repoCfg?.model || cfg.model}`); console.log(` Language : ${repoCfg?.language || cfg.language}`); console.log(` GitHub Token : ${cfg.githubToken ? '*****' : 'Not Set'}`); }, getRaw() { return readConfig(); }, };