polish-cli
Version:
AI-powered file organization for Obsidian with automatic markdown conversion
261 lines • 10.4 kB
JavaScript
import chalk from 'chalk';
import inquirer from 'inquirer';
import { ProfileManager } from '../../services/ProfileManager.js';
import * as path from 'path';
import * as os from 'os';
export async function configCommand(action, key, value) {
const profileManager = new ProfileManager();
await profileManager.initialize();
try {
switch (action) {
case 'init':
await initializeConfig(profileManager);
break;
case 'show':
await showConfig(profileManager);
break;
case 'get':
if (!key) {
console.log(chalk.red('Error: Key is required for get command'));
console.log(chalk.gray('Usage: polish config get <key>'));
return;
}
await getConfigValue(profileManager, key);
break;
case 'set':
if (!key || !value) {
console.log(chalk.red('Error: Key and value are required for set command'));
console.log(chalk.gray('Usage: polish config set <key> <value>'));
return;
}
await setConfigValue(profileManager, key, value);
break;
case 'edit':
await editConfig(profileManager);
break;
default:
console.log(chalk.yellow('Unknown config action. Available actions:'));
console.log(chalk.gray(' show - Show full configuration'));
console.log(chalk.gray(' get - Get configuration value'));
console.log(chalk.gray(' set - Set configuration value'));
console.log(chalk.gray(' init - Initialize configuration'));
console.log(chalk.gray(' edit - Edit configuration interactively'));
}
}
catch (error) {
console.error(chalk.red('Config command failed:'), error instanceof Error ? error.message : error);
process.exit(1);
}
}
async function showConfig(profileManager) {
const config = await profileManager.getActiveConfig();
const activeProfileName = await profileManager.getActiveProfile();
console.log(chalk.bold('\nCurrent Configuration:\n'));
console.log(chalk.gray(`Active Profile: ${activeProfileName || 'default'}\n`));
console.log(JSON.stringify(config, null, 2));
}
async function getConfigValue(profileManager, key) {
const config = await profileManager.getActiveConfig();
const value = getNestedValue(config, key);
if (value !== undefined) {
console.log(chalk.green(`${key}:`), value);
}
else {
console.log(chalk.red(`Configuration key not found: ${key}`));
}
}
async function setConfigValue(profileManager, key, value) {
const activeProfileName = await profileManager.getActiveProfile();
if (!activeProfileName) {
console.log(chalk.red('No active profile found. Please create a profile first.'));
return;
}
const activeProfile = await profileManager.getProfile(activeProfileName);
if (!activeProfile) {
console.log(chalk.red('Active profile not found. Please create a profile first.'));
return;
}
setNestedValue(activeProfile.config, key, value);
await profileManager.updateProfile(activeProfileName, { config: activeProfile.config });
console.log(chalk.green(`✓ Set ${key} = ${value} in profile '${activeProfile.name}'`));
}
async function editConfig(profileManager) {
console.log(chalk.bold('\n🔧 Edit Configuration\n'));
const activeProfileName = await profileManager.getActiveProfile();
if (!activeProfileName) {
console.log(chalk.red('No active profile found. Please create a profile first.'));
return;
}
const activeProfile = await profileManager.getProfile(activeProfileName);
if (!activeProfile) {
console.log(chalk.red('Active profile not found. Please create a profile first.'));
return;
}
const config = activeProfile.config;
const answers = await inquirer.prompt([
{
type: 'input',
name: 'vaultPath',
message: 'Obsidian vault path:',
default: config.vault.path,
validate: (input) => input.length > 0 || 'Vault path is required',
},
{
type: 'input',
name: 'originalsPath',
message: 'Original files organization path:',
default: config.originals.path,
},
{
type: 'list',
name: 'organizationStyle',
message: 'How should original files be organized?',
default: config.originals.organizationStyle,
choices: [
{ name: 'By file type', value: 'type-based' },
{ name: 'By project/context', value: 'project-based' },
{ name: 'By date', value: 'date-based' },
],
},
{
type: 'list',
name: 'mode',
message: 'Default processing mode:',
default: config.api.mode,
choices: [
{ name: 'Claude Code (no API key needed)', value: 'claude-code' },
{ name: 'Claude API (requires API key)', value: 'api' },
{ name: 'Hybrid (API with local fallback)', value: 'hybrid' },
{ name: 'Local only (no AI)', value: 'local' },
],
},
]);
// Update config with new values
config.vault.path = answers.vaultPath;
config.originals.path = answers.originalsPath;
config.originals.organizationStyle = answers.organizationStyle;
config.api.mode = answers.mode;
await profileManager.updateProfile(activeProfileName, { config });
console.log(chalk.green(`\n✓ Profile '${activeProfile.name}' updated successfully!`));
}
async function initializeConfig(profileManager) {
console.log(chalk.bold('\n🎯 Polish Configuration Setup\n'));
const answers = await inquirer.prompt([
{
type: 'input',
name: 'vaultPath',
message: 'Obsidian vault path:',
default: path.join(os.homedir(), 'ObsidianVault'),
validate: (input) => input.length > 0 || 'Vault path is required',
},
{
type: 'input',
name: 'originalsPath',
message: 'Original files organization path:',
default: path.join(os.homedir(), 'OrganizedFiles'),
},
{
type: 'checkbox',
name: 'sources',
message: 'Select default source folders to monitor:',
choices: [
{ name: 'Desktop', value: path.join(os.homedir(), 'Desktop') },
{ name: 'Downloads', value: path.join(os.homedir(), 'Downloads') },
{ name: 'Documents', value: path.join(os.homedir(), 'Documents') },
],
},
{
type: 'list',
name: 'organizationStyle',
message: 'How should original files be organized?',
choices: [
{ name: 'By file type', value: 'type-based' },
{ name: 'By project/context', value: 'project-based' },
{ name: 'By date', value: 'date-based' },
],
},
{
type: 'list',
name: 'mode',
message: 'Default processing mode:',
choices: [
{ name: 'Claude Code (no API key needed)', value: 'claude-code' },
{ name: 'Claude API (requires API key)', value: 'api' },
{ name: 'Hybrid (API with local fallback)', value: 'hybrid' },
{ name: 'Local only (no AI)', value: 'local' },
],
},
]);
let apiKey;
if (answers.mode === 'api' || answers.mode === 'hybrid') {
const apiAnswer = await inquirer.prompt([
{
type: 'password',
name: 'apiKey',
message: 'Anthropic API key:',
validate: (input) => input.length > 0 || 'API key is required for API mode',
},
]);
apiKey = apiAnswer.apiKey;
}
const config = {
vault: {
path: answers.vaultPath,
structure: {
documents: 'Documents',
media: 'Media',
code: 'Code',
references: 'References',
},
},
originals: {
path: answers.originalsPath,
organizationStyle: answers.organizationStyle,
createYearFolders: true,
},
sources: answers.sources.map((src) => ({
path: src,
includeSubfolders: false,
})),
processing: {
extractText: true,
maxFileSize: '50MB',
supportedFormats: ['pdf', 'docx', 'txt', 'md', 'png', 'jpg', 'py', 'js'],
},
tagging: {
maxTags: 10,
autoGenerateTypeTags: true,
autoGenerateDateTags: true,
customTagPatterns: {},
},
api: {
mode: answers.mode,
apiKey: apiKey || 'env:ANTHROPIC_API_KEY',
model: 'claude-3-opus-20240229',
maxTokens: 4096,
temperature: 0.3,
},
};
const profileName = 'default';
await profileManager.createProfile(profileName, config, 'Default configuration profile');
await profileManager.setActiveProfile(profileName);
console.log(chalk.green('\n✓ Configuration saved successfully!'));
console.log(chalk.gray('Created profile:'), chalk.cyan(profileName));
console.log(chalk.gray('You can now run'), chalk.cyan('polish organize'), chalk.gray('to start organizing files'));
}
function getNestedValue(obj, path) {
return path.split('.').reduce((curr, key) => curr?.[key], obj);
}
function setNestedValue(obj, path, value) {
const keys = path.split('.');
const lastKey = keys.pop();
const target = keys.reduce((curr, key) => {
if (!curr[key])
curr[key] = {};
return curr[key];
}, obj);
target[lastKey] = value.toLowerCase() === 'true' ? true :
value.toLowerCase() === 'false' ? false :
isNaN(Number(value)) ? value : Number(value);
}
//# sourceMappingURL=config.js.map