@redaksjon/protokoll 0.0.12 → 0.0.13
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.
- package/.cursor/rules/definition-of-done.md +1 -0
- package/.cursor/rules/no-emoticons.md +26 -12
- package/README.md +483 -69
- package/dist/agentic/executor.js +473 -41
- package/dist/agentic/executor.js.map +1 -1
- package/dist/agentic/index.js.map +1 -1
- package/dist/agentic/tools/lookup-person.js +123 -4
- package/dist/agentic/tools/lookup-person.js.map +1 -1
- package/dist/agentic/tools/lookup-project.js +139 -22
- package/dist/agentic/tools/lookup-project.js.map +1 -1
- package/dist/agentic/tools/route-note.js +5 -1
- package/dist/agentic/tools/route-note.js.map +1 -1
- package/dist/arguments.js +6 -3
- package/dist/arguments.js.map +1 -1
- package/dist/cli/action.js +704 -0
- package/dist/cli/action.js.map +1 -0
- package/dist/cli/config.js +482 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/context.js +466 -0
- package/dist/cli/context.js.map +1 -0
- package/dist/cli/feedback.js +858 -0
- package/dist/cli/feedback.js.map +1 -0
- package/dist/cli/index.js +103 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/install.js +572 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/transcript.js +199 -0
- package/dist/cli/transcript.js.map +1 -0
- package/dist/constants.js +11 -4
- package/dist/constants.js.map +1 -1
- package/dist/context/index.js +25 -1
- package/dist/context/index.js.map +1 -1
- package/dist/context/storage.js +56 -3
- package/dist/context/storage.js.map +1 -1
- package/dist/interactive/handler.js +310 -9
- package/dist/interactive/handler.js.map +1 -1
- package/dist/main.js +11 -1
- package/dist/main.js.map +1 -1
- package/dist/output/index.js.map +1 -1
- package/dist/output/manager.js +46 -1
- package/dist/output/manager.js.map +1 -1
- package/dist/phases/complete.js +37 -2
- package/dist/phases/complete.js.map +1 -1
- package/dist/pipeline/orchestrator.js +104 -31
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/protokoll.js +68 -2
- package/dist/protokoll.js.map +1 -1
- package/dist/reasoning/client.js +83 -0
- package/dist/reasoning/client.js.map +1 -1
- package/dist/reasoning/index.js +1 -0
- package/dist/reasoning/index.js.map +1 -1
- package/dist/util/metadata.js.map +1 -1
- package/dist/util/sound.js +116 -0
- package/dist/util/sound.js.map +1 -0
- package/docs/duplicate-question-prevention.md +117 -0
- package/docs/examples.md +152 -0
- package/docs/interactive-context-example.md +92 -0
- package/docs/package-lock.json +6 -0
- package/docs/package.json +3 -1
- package/guide/action.md +375 -0
- package/guide/config.md +207 -0
- package/guide/configuration.md +82 -67
- package/guide/context-commands.md +574 -0
- package/guide/context-system.md +20 -7
- package/guide/development.md +106 -4
- package/guide/feedback.md +335 -0
- package/guide/index.md +100 -4
- package/guide/interactive.md +15 -14
- package/guide/quickstart.md +21 -7
- package/guide/reasoning.md +18 -4
- package/guide/routing.md +192 -97
- package/package.json +1 -1
- package/scripts/coverage-priority.mjs +323 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +5 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action.js","sources":["../../src/cli/action.ts"],"sourcesContent":["/**\n * Action CLI\n * \n * Provides commands for performing actions on existing transcripts:\n * - combine: Merge multiple transcripts into a single document\n * - (future: split, archive, etc.)\n * \n * The combine action preserves the timestamp of the first transcript\n * and can optionally change the project (with routing-aware relocation).\n */\n\nimport { Command } from 'commander';\nimport * as fs from 'fs/promises';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport * as Context from '../context';\nimport * as Routing from '../routing';\nimport { Project } from '../context/types';\n\n// Helper to print to stdout\nconst print = (text: string) => process.stdout.write(text + '\\n');\n\n/**\n * Parsed transcript structure\n */\nexport interface ParsedTranscript {\n filePath: string;\n title?: string;\n metadata: TranscriptMetadata;\n content: string;\n rawText: string;\n}\n\nexport interface TranscriptMetadata {\n date?: string;\n time?: string;\n project?: string;\n projectId?: string;\n destination?: string;\n confidence?: string;\n signals?: string[];\n reasoning?: string;\n tags?: string[];\n duration?: string;\n}\n\n/**\n * Parse a transcript file into its components\n */\nexport const parseTranscript = async (filePath: string): Promise<ParsedTranscript> => {\n const rawText = await fs.readFile(filePath, 'utf-8');\n const lines = rawText.split('\\n');\n \n const result: ParsedTranscript = {\n filePath,\n metadata: {},\n content: '',\n rawText,\n };\n \n let inMetadata = false;\n let inRouting = false;\n let contentStartIndex = 0;\n \n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n const trimmed = line.trim();\n \n // Title detection (first # heading)\n if (!result.title && trimmed.startsWith('# ') && !trimmed.startsWith('## ')) {\n result.title = trimmed.slice(2).trim();\n continue;\n }\n \n // Metadata section start\n if (trimmed === '## Metadata') {\n inMetadata = true;\n continue;\n }\n \n // Routing subsection\n if (trimmed === '### Routing') {\n inRouting = true;\n continue;\n }\n \n // End of metadata section (horizontal rule)\n if (trimmed === '---' && inMetadata) {\n contentStartIndex = i + 1;\n inMetadata = false;\n inRouting = false;\n continue;\n }\n \n // Parse metadata fields\n if (inMetadata && trimmed.startsWith('**')) {\n const match = trimmed.match(/^\\*\\*([^*]+)\\*\\*:\\s*(.*)$/);\n if (match) {\n const [, key, value] = match;\n const normalizedKey = key.toLowerCase().replace(/\\s+/g, '');\n \n switch (normalizedKey) {\n case 'date':\n result.metadata.date = value;\n break;\n case 'time':\n result.metadata.time = value;\n break;\n case 'project':\n result.metadata.project = value;\n break;\n case 'projectid':\n // Remove backticks from project ID\n result.metadata.projectId = value.replace(/`/g, '');\n break;\n case 'destination':\n result.metadata.destination = value;\n break;\n case 'confidence':\n result.metadata.confidence = value;\n break;\n case 'reasoning':\n result.metadata.reasoning = value;\n break;\n case 'tags':\n // Parse tags from backtick-wrapped format\n result.metadata.tags = value.match(/`([^`]+)`/g)?.map(t => t.replace(/`/g, '')) || [];\n break;\n case 'duration':\n result.metadata.duration = value;\n break;\n }\n }\n }\n \n // Parse classification signals (list items under routing)\n if (inRouting && trimmed.startsWith('- ') && !trimmed.startsWith('**')) {\n if (!result.metadata.signals) {\n result.metadata.signals = [];\n }\n result.metadata.signals.push(trimmed.slice(2));\n }\n }\n \n // Extract content after metadata\n if (contentStartIndex > 0) {\n // Skip empty lines after ---\n while (contentStartIndex < lines.length && lines[contentStartIndex].trim() === '') {\n contentStartIndex++;\n }\n result.content = lines.slice(contentStartIndex).join('\\n').trim();\n } else {\n // No metadata section found, entire file is content\n result.content = rawText.trim();\n }\n \n return result;\n};\n\n/**\n * Extract the timestamp from a transcript filename\n * Expected format: DD-HHMM-subject.md (e.g., 15-1412-ne-4th-st-0.md)\n */\nexport const extractTimestampFromFilename = (filePath: string): { day: number; hour: number; minute: number } | null => {\n const basename = path.basename(filePath, '.md');\n const match = basename.match(/^(\\d{1,2})-(\\d{2})(\\d{2})/);\n \n if (match) {\n return {\n day: parseInt(match[1], 10),\n hour: parseInt(match[2], 10),\n minute: parseInt(match[3], 10),\n };\n }\n \n return null;\n};\n\n/**\n * Format metadata as Markdown heading section (matching util/metadata.ts format)\n */\nexport const formatMetadataMarkdown = (\n title: string,\n metadata: TranscriptMetadata,\n project?: Project\n): string => {\n const lines: string[] = [];\n \n // Title section\n lines.push(`# ${title}`);\n lines.push('');\n \n // Metadata frontmatter as readable markdown\n lines.push('## Metadata');\n lines.push('');\n \n // Date and Time\n if (metadata.date) {\n lines.push(`**Date**: ${metadata.date}`);\n }\n if (metadata.time) {\n lines.push(`**Time**: ${metadata.time}`);\n }\n \n lines.push('');\n \n // Project\n if (project) {\n lines.push(`**Project**: ${project.name}`);\n lines.push(`**Project ID**: \\`${project.id}\\``);\n lines.push('');\n } else if (metadata.project) {\n lines.push(`**Project**: ${metadata.project}`);\n if (metadata.projectId) {\n lines.push(`**Project ID**: \\`${metadata.projectId}\\``);\n }\n lines.push('');\n }\n \n // Routing Information\n if (metadata.destination) {\n lines.push('### Routing');\n lines.push('');\n lines.push(`**Destination**: ${metadata.destination}`);\n if (metadata.confidence) {\n lines.push(`**Confidence**: ${metadata.confidence}`);\n }\n lines.push('');\n \n if (metadata.signals && metadata.signals.length > 0) {\n lines.push('**Classification Signals**:');\n for (const signal of metadata.signals) {\n lines.push(`- ${signal}`);\n }\n lines.push('');\n }\n \n if (metadata.reasoning) {\n lines.push(`**Reasoning**: ${metadata.reasoning}`);\n lines.push('');\n }\n }\n \n // Tags\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push('**Tags**: ' + metadata.tags.map(tag => `\\`${tag}\\``).join(', '));\n lines.push('');\n }\n \n // Duration\n if (metadata.duration) {\n lines.push(`**Duration**: ${metadata.duration}`);\n lines.push('');\n }\n \n // Separator\n lines.push('---');\n lines.push('');\n \n return lines.join('\\n');\n};\n\n/**\n * Slugify a title for use in filenames\n */\nexport const slugifyTitle = (title: string): string => {\n return title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric with dash\n .replace(/--+/g, '-') // Collapse multiple dashes\n .replace(/^-|-$/g, '') // Remove leading/trailing dashes\n .slice(0, 50); // Limit length\n};\n\n/**\n * Combine multiple transcripts into a single document\n */\nexport const combineTranscripts = async (\n filePaths: string[],\n options: {\n projectId?: string;\n title?: string;\n dryRun?: boolean;\n verbose?: boolean;\n } = {}\n): Promise<{ outputPath: string; content: string }> => {\n if (filePaths.length === 0) {\n throw new Error('No transcript files provided');\n }\n \n // Parse all transcripts\n const transcripts: ParsedTranscript[] = [];\n for (const filePath of filePaths) {\n try {\n const parsed = await parseTranscript(filePath);\n transcripts.push(parsed);\n } catch (error) {\n throw new Error(`Failed to parse transcript: ${filePath} - ${error}`);\n }\n }\n \n // Sort by filename (which should be chronological due to timestamp prefix)\n transcripts.sort((a, b) => {\n const aName = path.basename(a.filePath);\n const bName = path.basename(b.filePath);\n return aName.localeCompare(bName);\n });\n \n // Use the first transcript's metadata as the base\n const firstTranscript = transcripts[0];\n const baseMetadata = { ...firstTranscript.metadata };\n \n // Load context to get project information if needed\n const context = await Context.create();\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = context.getProject(options.projectId);\n if (!targetProject) {\n throw new Error(`Project not found: ${options.projectId}`);\n }\n \n // Update metadata with new project\n baseMetadata.project = targetProject.name;\n baseMetadata.projectId = targetProject.id;\n \n // Update destination if project has routing configured\n if (targetProject.routing?.destination) {\n baseMetadata.destination = expandPath(targetProject.routing.destination);\n }\n }\n \n // Calculate combined duration if available\n let totalSeconds = 0;\n let hasDuration = false;\n for (const t of transcripts) {\n if (t.metadata.duration) {\n hasDuration = true;\n totalSeconds += parseDuration(t.metadata.duration);\n }\n }\n if (hasDuration && totalSeconds > 0) {\n baseMetadata.duration = formatDuration(totalSeconds);\n }\n \n // Combine tags from all transcripts (deduplicated)\n const allTags = new Set<string>();\n for (const t of transcripts) {\n if (t.metadata.tags) {\n for (const tag of t.metadata.tags) {\n allTags.add(tag);\n }\n }\n }\n if (allTags.size > 0) {\n baseMetadata.tags = Array.from(allTags).sort();\n }\n \n // Build combined title - use custom title if provided\n const combinedTitle = options.title \n ? options.title\n : (firstTranscript.title \n ? `${firstTranscript.title} (Combined)`\n : 'Combined Transcript');\n \n // Build combined content with section markers\n const contentParts: string[] = [];\n \n for (let i = 0; i < transcripts.length; i++) {\n const t = transcripts[i];\n const sectionTitle = t.title || `Part ${i + 1}`;\n const sourceFile = path.basename(t.filePath);\n \n contentParts.push(`## ${sectionTitle}`);\n contentParts.push(`*Source: ${sourceFile}*`);\n contentParts.push('');\n contentParts.push(t.content);\n contentParts.push('');\n }\n \n // Build final document\n const metadataSection = formatMetadataMarkdown(combinedTitle, baseMetadata, targetProject);\n const finalContent = metadataSection + contentParts.join('\\n');\n \n // Determine output path\n let outputPath: string;\n \n if (targetProject?.routing?.destination) {\n // Build path using project routing configuration\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context);\n \n // Extract date from first transcript for routing\n const audioDate = extractDateFromMetadata(baseMetadata, firstTranscript.filePath);\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: finalContent,\n audioDate,\n sourceFile: firstTranscript.filePath,\n };\n \n const decision = routing.route(routingContext);\n outputPath = routing.buildOutputPath(decision, routingContext);\n } else {\n // Use the directory of the first transcript with a new filename\n const firstDir = path.dirname(firstTranscript.filePath);\n const timestamp = extractTimestampFromFilename(firstTranscript.filePath);\n \n // Use slugified custom title if provided, otherwise \"combined\"\n const filenameSuffix = options.title \n ? slugifyTitle(options.title)\n : 'combined';\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(firstDir, `${day}-${hour}${minute}-${filenameSuffix}.md`);\n } else {\n outputPath = path.join(firstDir, `${filenameSuffix}.md`);\n }\n }\n \n return { outputPath, content: finalContent };\n};\n\n/**\n * Build a routing config from context and project\n */\nconst buildRoutingConfig = (\n context: Context.ContextInstance,\n _targetProject: Project\n): Routing.RoutingConfig => {\n const config = context.getConfig();\n const defaultPath = expandPath((config.outputDirectory as string) || '~/notes');\n \n // Build project routes from all projects\n const projects: Routing.ProjectRoute[] = context.getAllProjects()\n .filter(p => p.active !== false)\n .map(p => ({\n projectId: p.id,\n destination: {\n path: expandPath(p.routing?.destination || defaultPath),\n structure: p.routing?.structure || 'month',\n filename_options: p.routing?.filename_options || ['date', 'time', 'subject'],\n },\n classification: p.classification,\n auto_tags: p.routing?.auto_tags,\n }));\n \n return {\n default: {\n path: defaultPath,\n structure: (config.outputStructure as Routing.FilesystemStructure) || 'month',\n filename_options: (config.outputFilenameOptions as Routing.FilenameOption[]) || ['date', 'time', 'subject'],\n },\n projects,\n conflict_resolution: 'primary',\n };\n};\n\n/**\n * Extract date from metadata or filename\n */\nconst extractDateFromMetadata = (metadata: TranscriptMetadata, filePath: string): Date => {\n // Try to parse from metadata date string\n if (metadata.date) {\n const parsed = new Date(metadata.date);\n if (!isNaN(parsed.getTime())) {\n // Add time if available\n if (metadata.time) {\n const timeMatch = metadata.time.match(/(\\d{1,2}):(\\d{2})\\s*(AM|PM)?/i);\n if (timeMatch) {\n let hours = parseInt(timeMatch[1], 10);\n const minutes = parseInt(timeMatch[2], 10);\n const ampm = timeMatch[3]?.toUpperCase();\n \n if (ampm === 'PM' && hours < 12) hours += 12;\n if (ampm === 'AM' && hours === 12) hours = 0;\n \n parsed.setHours(hours, minutes);\n }\n }\n return parsed;\n }\n }\n \n // Fall back to extracting from directory structure and filename\n // Expected path: .../2026/1/15-1412-subject.md\n const parts = filePath.split(path.sep);\n \n // Look for year/month in path\n let year = new Date().getFullYear();\n let month = new Date().getMonth();\n \n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n const num = parseInt(part, 10);\n if (num >= 2000 && num <= 2100) {\n year = num;\n // Next part might be month\n if (i + 1 < parts.length - 1) {\n const nextNum = parseInt(parts[i + 1], 10);\n if (nextNum >= 1 && nextNum <= 12) {\n month = nextNum - 1; // 0-indexed\n }\n }\n }\n }\n \n // Extract day and time from filename\n const timestamp = extractTimestampFromFilename(filePath);\n const day = timestamp?.day || 1;\n const hour = timestamp?.hour || 0;\n const minute = timestamp?.minute || 0;\n \n return new Date(year, month, day, hour, minute);\n};\n\n/**\n * Parse duration string to seconds\n */\nconst parseDuration = (duration: string): number => {\n let seconds = 0;\n \n const minuteMatch = duration.match(/(\\d+)m/);\n const secondMatch = duration.match(/(\\d+)s/);\n \n if (minuteMatch) {\n seconds += parseInt(minuteMatch[1], 10) * 60;\n }\n if (secondMatch) {\n seconds += parseInt(secondMatch[1], 10);\n }\n \n return seconds;\n};\n\n/**\n * Format seconds as duration string\n */\nconst formatDuration = (seconds: number): string => {\n const minutes = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n \n if (minutes === 0) {\n return `${secs}s`;\n }\n if (secs === 0) {\n return `${minutes}m`;\n }\n return `${minutes}m ${secs}s`;\n};\n\n/**\n * Expand ~ to home directory\n */\nconst expandPath = (p: string): string => {\n if (p.startsWith('~')) {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n};\n\n/**\n * Parse file paths from the combine argument\n * Supports newline-separated paths (from command line) or array\n */\nexport const parseFilePaths = (input: string): string[] => {\n // Split by newlines and filter empty lines\n return input\n .split('\\n')\n .map(line => line.trim())\n .filter(line => line.length > 0);\n};\n\n/**\n * Edit a single transcript - update title and/or project\n */\nexport const editTranscript = async (\n filePath: string,\n options: {\n title?: string;\n projectId?: string;\n dryRun?: boolean;\n verbose?: boolean;\n }\n): Promise<{ outputPath: string; content: string }> => {\n // Parse the existing transcript\n const transcript = await parseTranscript(filePath);\n \n // Load context if we need project info\n const context = await Context.create();\n let targetProject: Project | undefined;\n \n if (options.projectId) {\n targetProject = context.getProject(options.projectId);\n if (!targetProject) {\n throw new Error(`Project not found: ${options.projectId}`);\n }\n }\n \n // Use new title if provided, otherwise keep existing\n const newTitle = options.title || transcript.title || 'Untitled';\n \n // Update metadata\n const updatedMetadata = { ...transcript.metadata };\n \n if (targetProject) {\n updatedMetadata.project = targetProject.name;\n updatedMetadata.projectId = targetProject.id;\n if (targetProject.routing?.destination) {\n updatedMetadata.destination = expandPath(targetProject.routing.destination);\n }\n }\n \n // Build the updated document\n const metadataSection = formatMetadataMarkdown(newTitle, updatedMetadata, targetProject);\n const finalContent = metadataSection + transcript.content;\n \n // Determine output path\n let outputPath: string;\n \n if (targetProject?.routing?.destination) {\n // Build path using project routing configuration\n const routingConfig = buildRoutingConfig(context, targetProject);\n const routing = Routing.create(routingConfig, context);\n \n const audioDate = extractDateFromMetadata(updatedMetadata, filePath);\n \n const routingContext: Routing.RoutingContext = {\n transcriptText: finalContent,\n audioDate,\n sourceFile: filePath,\n };\n \n const decision = routing.route(routingContext);\n \n // If we have a custom title, override the filename with slugified title\n if (options.title) {\n const basePath = path.dirname(routing.buildOutputPath(decision, routingContext));\n const timestamp = extractTimestampFromFilename(filePath);\n const sluggedTitle = slugifyTitle(options.title);\n \n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(basePath, `${day}-${hour}${minute}-${sluggedTitle}.md`);\n } else {\n outputPath = path.join(basePath, `${sluggedTitle}.md`);\n }\n } else {\n outputPath = routing.buildOutputPath(decision, routingContext);\n }\n } else {\n // Keep in same directory, potentially with new filename\n const dir = path.dirname(filePath);\n const timestamp = extractTimestampFromFilename(filePath);\n \n if (options.title) {\n const sluggedTitle = slugifyTitle(options.title);\n if (timestamp) {\n const day = timestamp.day.toString().padStart(2, '0');\n const hour = timestamp.hour.toString().padStart(2, '0');\n const minute = timestamp.minute.toString().padStart(2, '0');\n outputPath = path.join(dir, `${day}-${hour}${minute}-${sluggedTitle}.md`);\n } else {\n outputPath = path.join(dir, `${sluggedTitle}.md`);\n }\n } else {\n // Keep original filename\n outputPath = filePath;\n }\n }\n \n return { outputPath, content: finalContent };\n};\n\n/**\n * Execute the action command\n */\nconst executeAction = async (\n file: string | undefined,\n options: { \n project?: string; \n title?: string; \n combine?: string;\n dryRun?: boolean; \n verbose?: boolean;\n }\n) => {\n // Determine mode: combine or edit\n if (options.combine) {\n // Combine mode\n const filePaths = parseFilePaths(options.combine);\n \n if (filePaths.length === 0) {\n print('Error: No transcript files provided for --combine.');\n process.exit(1);\n }\n \n if (filePaths.length === 1) {\n print('Error: At least 2 transcript files are required for --combine.');\n process.exit(1);\n }\n \n // Validate all files exist\n for (const filePath of filePaths) {\n try {\n await fs.access(filePath);\n } catch {\n print(`Error: File not found: ${filePath}`);\n process.exit(1);\n }\n }\n \n if (options.verbose) {\n print(`\\n[Combining ${filePaths.length} transcripts]`);\n for (const fp of filePaths) {\n print(` - ${fp}`);\n }\n if (options.project) {\n print(`\\nTarget project: ${options.project}`);\n }\n if (options.title) {\n print(`\\nCustom title: ${options.title}`);\n }\n print('');\n }\n \n try {\n const result = await combineTranscripts(filePaths, {\n projectId: options.project,\n title: options.title,\n dryRun: options.dryRun,\n verbose: options.verbose,\n });\n \n if (options.dryRun) {\n print('[Dry Run] Would create combined transcript:');\n print(` Output: ${result.outputPath}`);\n print(` Size: ${result.content.length} characters`);\n print('');\n print('[Dry Run] Would delete source files:');\n for (const fp of filePaths) {\n print(` - ${fp}`);\n }\n if (options.verbose) {\n print('\\n--- Preview (first 500 chars) ---');\n print(result.content.slice(0, 500));\n print('...');\n }\n } else {\n // Ensure output directory exists\n await fs.mkdir(path.dirname(result.outputPath), { recursive: true });\n \n // Write the combined transcript\n await fs.writeFile(result.outputPath, result.content, 'utf-8');\n print(`Combined transcript created: ${result.outputPath}`);\n \n // Automatically delete source files when combining\n if (options.verbose) {\n print('\\nDeleting source files...');\n }\n for (const fp of filePaths) {\n try {\n await fs.unlink(fp);\n if (options.verbose) {\n print(` Deleted: ${fp}`);\n }\n } catch (error) {\n print(` Warning: Could not delete ${fp}: ${error}`);\n }\n }\n print(`Deleted ${filePaths.length} source files.`);\n }\n } catch (error) {\n print(`Error: ${error instanceof Error ? error.message : error}`);\n process.exit(1);\n }\n } else if (file) {\n // Edit mode - single file\n if (!options.title && !options.project) {\n print('Error: Must specify --title and/or --project when editing a single file.');\n process.exit(1);\n }\n \n // Validate file exists\n try {\n await fs.access(file);\n } catch {\n print(`Error: File not found: ${file}`);\n process.exit(1);\n }\n \n if (options.verbose) {\n print(`\\n[Editing transcript]`);\n print(` File: ${file}`);\n if (options.title) {\n print(` New title: ${options.title}`);\n }\n if (options.project) {\n print(` New project: ${options.project}`);\n }\n print('');\n }\n \n try {\n const result = await editTranscript(file, {\n title: options.title,\n projectId: options.project,\n dryRun: options.dryRun,\n verbose: options.verbose,\n });\n \n const isRename = result.outputPath !== file;\n \n if (options.dryRun) {\n print('[Dry Run] Would update transcript:');\n if (isRename) {\n print(` From: ${file}`);\n print(` To: ${result.outputPath}`);\n } else {\n print(` File: ${result.outputPath}`);\n }\n print(` Size: ${result.content.length} characters`);\n if (options.verbose) {\n print('\\n--- Preview (first 500 chars) ---');\n print(result.content.slice(0, 500));\n print('...');\n }\n } else {\n // Ensure output directory exists\n await fs.mkdir(path.dirname(result.outputPath), { recursive: true });\n \n // Write the updated transcript\n await fs.writeFile(result.outputPath, result.content, 'utf-8');\n \n // Delete original if renamed\n if (isRename) {\n await fs.unlink(file);\n print(`Transcript updated and renamed:`);\n print(` From: ${file}`);\n print(` To: ${result.outputPath}`);\n } else {\n print(`Transcript updated: ${result.outputPath}`);\n }\n }\n } catch (error) {\n print(`Error: ${error instanceof Error ? error.message : error}`);\n process.exit(1);\n }\n } else {\n print('Error: Must specify either a file to edit or --combine with files to combine.');\n print('');\n print('Usage:');\n print(' protokoll action --title \"New Title\" /path/to/file.md');\n print(' protokoll action --combine \"/path/to/file1.md\\\\n/path/to/file2.md\"');\n process.exit(1);\n }\n};\n\n/**\n * Register the action command\n */\nexport const registerActionCommands = (program: Command): void => {\n const actionCmd = new Command('action')\n .description('Edit a single transcript or combine multiple transcripts')\n .argument('[file]', 'Transcript file to edit (when not using --combine)')\n .option('-t, --title <title>', 'Set a custom title (also affects filename)')\n .option('-p, --project <projectId>', 'Change to a different project (updates metadata and routing)')\n .option('-c, --combine <files>', 'Combine multiple files (newline-separated list)')\n .option('--dry-run', 'Show what would happen without making changes')\n .option('-v, --verbose', 'Show detailed output')\n .action(executeAction);\n \n program.addCommand(actionCmd);\n};\n"],"names":["print","text","process","stdout","write","parseTranscript","filePath","rawText","fs","readFile","lines","split","result","metadata","content","inMetadata","inRouting","contentStartIndex","i","length","line","trimmed","trim","title","startsWith","slice","match","key","value","normalizedKey","toLowerCase","replace","date","time","project","projectId","destination","confidence","reasoning","tags","map","t","duration","signals","push","join","extractTimestampFromFilename","basename","path","day","parseInt","hour","minute","formatMetadataMarkdown","name","id","signal","tag","slugifyTitle","combineTranscripts","filePaths","options","targetProject","Error","transcripts","parsed","error","sort","a","b","aName","bName","localeCompare","firstTranscript","baseMetadata","context","Context","getProject","routing","expandPath","totalSeconds","hasDuration","parseDuration","formatDuration","allTags","Set","add","size","Array","from","combinedTitle","contentParts","sectionTitle","sourceFile","metadataSection","finalContent","outputPath","routingConfig","buildRoutingConfig","Routing","audioDate","extractDateFromMetadata","routingContext","transcriptText","decision","route","buildOutputPath","firstDir","dirname","timestamp","filenameSuffix","toString","padStart","_targetProject","config","getConfig","defaultPath","outputDirectory","projects","getAllProjects","filter","p","active","structure","filename_options","classification","auto_tags","default","outputStructure","outputFilenameOptions","conflict_resolution","Date","isNaN","getTime","timeMatch","hours","minutes","ampm","toUpperCase","setHours","parts","sep","year","getFullYear","month","getMonth","part","num","nextNum","seconds","minuteMatch","secondMatch","Math","floor","secs","round","os","homedir","parseFilePaths","input","editTranscript","transcript","newTitle","updatedMetadata","basePath","sluggedTitle","dir","executeAction","file","combine","exit","access","verbose","fp","dryRun","mkdir","recursive","writeFile","unlink","message","isRename","registerActionCommands","program","actionCmd","Command","description","argument","option","action","addCommand"],"mappings":"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"}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as yaml from 'js-yaml';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
import { ALLOWED_OUTPUT_FILENAME_OPTIONS, ALLOWED_OUTPUT_STRUCTURES, PROTOKOLL_DEFAULTS, DEFAULT_CONTEXT_DIR_NAME, DEFAULT_CONTEXT_CONFIG_FILE_NAME } from '../constants.js';
|
|
6
|
+
|
|
7
|
+
// Configuration schema with descriptions and allowed values
|
|
8
|
+
const CONFIG_SCHEMA = {
|
|
9
|
+
model: {
|
|
10
|
+
description: 'AI model for transcription enhancement',
|
|
11
|
+
type: 'string',
|
|
12
|
+
default: PROTOKOLL_DEFAULTS.model,
|
|
13
|
+
examples: [
|
|
14
|
+
'gpt-5.2',
|
|
15
|
+
'gpt-4o',
|
|
16
|
+
'gpt-4o-mini',
|
|
17
|
+
'claude-3-5-sonnet'
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
transcriptionModel: {
|
|
21
|
+
description: 'Model for audio transcription',
|
|
22
|
+
type: 'string',
|
|
23
|
+
default: PROTOKOLL_DEFAULTS.transcriptionModel,
|
|
24
|
+
examples: [
|
|
25
|
+
'whisper-1'
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
reasoningLevel: {
|
|
29
|
+
description: 'How much reasoning effort to use',
|
|
30
|
+
type: 'string',
|
|
31
|
+
default: PROTOKOLL_DEFAULTS.reasoningLevel,
|
|
32
|
+
allowed: [
|
|
33
|
+
'low',
|
|
34
|
+
'medium',
|
|
35
|
+
'high'
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
inputDirectory: {
|
|
39
|
+
description: 'Directory to read audio files from',
|
|
40
|
+
type: 'path',
|
|
41
|
+
default: './'
|
|
42
|
+
},
|
|
43
|
+
outputDirectory: {
|
|
44
|
+
description: 'Directory to write transcripts to',
|
|
45
|
+
type: 'path',
|
|
46
|
+
default: '~/notes'
|
|
47
|
+
},
|
|
48
|
+
outputStructure: {
|
|
49
|
+
description: 'Directory structure for output files',
|
|
50
|
+
type: 'string',
|
|
51
|
+
default: 'month',
|
|
52
|
+
allowed: ALLOWED_OUTPUT_STRUCTURES
|
|
53
|
+
},
|
|
54
|
+
outputFilenameOptions: {
|
|
55
|
+
description: 'Components to include in output filenames',
|
|
56
|
+
type: 'array',
|
|
57
|
+
default: [
|
|
58
|
+
'date',
|
|
59
|
+
'time',
|
|
60
|
+
'subject'
|
|
61
|
+
],
|
|
62
|
+
allowed: ALLOWED_OUTPUT_FILENAME_OPTIONS
|
|
63
|
+
},
|
|
64
|
+
processedDirectory: {
|
|
65
|
+
description: 'Directory to move processed audio files to',
|
|
66
|
+
type: 'path',
|
|
67
|
+
default: './processed'
|
|
68
|
+
},
|
|
69
|
+
timezone: {
|
|
70
|
+
description: 'Timezone for date/time operations',
|
|
71
|
+
type: 'string',
|
|
72
|
+
default: 'Etc/UTC',
|
|
73
|
+
examples: [
|
|
74
|
+
'America/New_York',
|
|
75
|
+
'Europe/London',
|
|
76
|
+
'Asia/Tokyo'
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
interactive: {
|
|
80
|
+
description: 'Enable interactive prompts during transcription',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
default: true
|
|
83
|
+
},
|
|
84
|
+
selfReflection: {
|
|
85
|
+
description: 'Generate self-reflection reports after processing',
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
default: true
|
|
88
|
+
},
|
|
89
|
+
silent: {
|
|
90
|
+
description: 'Disable sound notifications',
|
|
91
|
+
type: 'boolean',
|
|
92
|
+
default: false
|
|
93
|
+
},
|
|
94
|
+
verbose: {
|
|
95
|
+
description: 'Enable verbose logging output',
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
default: false
|
|
98
|
+
},
|
|
99
|
+
debug: {
|
|
100
|
+
description: 'Enable debug mode with detailed logs',
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
default: false
|
|
103
|
+
},
|
|
104
|
+
dryRun: {
|
|
105
|
+
description: 'Show what would happen without making changes',
|
|
106
|
+
type: 'boolean',
|
|
107
|
+
default: false
|
|
108
|
+
},
|
|
109
|
+
maxAudioSize: {
|
|
110
|
+
description: 'Maximum audio file size in bytes (default: 25MB)',
|
|
111
|
+
type: 'number',
|
|
112
|
+
default: 26214400
|
|
113
|
+
},
|
|
114
|
+
tempDirectory: {
|
|
115
|
+
description: 'Temporary directory for processing',
|
|
116
|
+
type: 'path',
|
|
117
|
+
default: '/tmp'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
// ANSI color codes for terminal output
|
|
121
|
+
const colors = {
|
|
122
|
+
reset: '\x1b[0m',
|
|
123
|
+
bold: '\x1b[1m',
|
|
124
|
+
dim: '\x1b[2m',
|
|
125
|
+
cyan: '\x1b[36m',
|
|
126
|
+
green: '\x1b[32m',
|
|
127
|
+
yellow: '\x1b[33m',
|
|
128
|
+
red: '\x1b[31m',
|
|
129
|
+
blue: '\x1b[34m'};
|
|
130
|
+
// eslint-disable-next-line no-console
|
|
131
|
+
const print = (msg)=>console.log(msg);
|
|
132
|
+
/**
|
|
133
|
+
* Find the config file path
|
|
134
|
+
*/ const findConfigPath = async ()=>{
|
|
135
|
+
// Check current directory first, then home directory
|
|
136
|
+
const cwd = process.cwd();
|
|
137
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
138
|
+
const locations = [
|
|
139
|
+
path.join(cwd, DEFAULT_CONTEXT_DIR_NAME, DEFAULT_CONTEXT_CONFIG_FILE_NAME),
|
|
140
|
+
path.join(home, DEFAULT_CONTEXT_DIR_NAME, DEFAULT_CONTEXT_CONFIG_FILE_NAME)
|
|
141
|
+
];
|
|
142
|
+
for (const loc of locations){
|
|
143
|
+
try {
|
|
144
|
+
await fs.access(loc);
|
|
145
|
+
return {
|
|
146
|
+
configPath: loc,
|
|
147
|
+
exists: true
|
|
148
|
+
};
|
|
149
|
+
} catch {
|
|
150
|
+
// Continue checking
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Default to home directory if no config exists
|
|
154
|
+
return {
|
|
155
|
+
configPath: path.join(home, DEFAULT_CONTEXT_DIR_NAME, DEFAULT_CONTEXT_CONFIG_FILE_NAME),
|
|
156
|
+
exists: false
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Load current configuration
|
|
161
|
+
*/ const loadConfig = async ()=>{
|
|
162
|
+
const { configPath, exists } = await findConfigPath();
|
|
163
|
+
if (!exists) {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
168
|
+
const parsed = yaml.load(content);
|
|
169
|
+
return parsed || {};
|
|
170
|
+
} catch {
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Save configuration
|
|
176
|
+
*/ const saveConfig = async (config)=>{
|
|
177
|
+
const { configPath } = await findConfigPath();
|
|
178
|
+
// Ensure directory exists
|
|
179
|
+
const configDir = path.dirname(configPath);
|
|
180
|
+
await fs.mkdir(configDir, {
|
|
181
|
+
recursive: true
|
|
182
|
+
});
|
|
183
|
+
// Write YAML with nice formatting
|
|
184
|
+
const yamlContent = yaml.dump(config, {
|
|
185
|
+
indent: 2,
|
|
186
|
+
lineWidth: 80,
|
|
187
|
+
quotingType: '"',
|
|
188
|
+
forceQuotes: false
|
|
189
|
+
});
|
|
190
|
+
await fs.writeFile(configPath, yamlContent, 'utf-8');
|
|
191
|
+
return configPath;
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Parse a value according to its type
|
|
195
|
+
*/ const parseValue = (value, type)=>{
|
|
196
|
+
switch(type){
|
|
197
|
+
case 'boolean':
|
|
198
|
+
return value.toLowerCase() === 'true' || value === '1' || value === 'yes';
|
|
199
|
+
case 'number':
|
|
200
|
+
return parseInt(value, 10);
|
|
201
|
+
case 'array':
|
|
202
|
+
// Support comma-separated or space-separated values
|
|
203
|
+
return value.split(/[,\s]+/).filter((v)=>v.length > 0);
|
|
204
|
+
default:
|
|
205
|
+
return value;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Format a value for display
|
|
210
|
+
*/ const formatValue = (value, type)=>{
|
|
211
|
+
if (value === undefined || value === null) {
|
|
212
|
+
return `${colors.dim}(not set)${colors.reset}`;
|
|
213
|
+
}
|
|
214
|
+
switch(type){
|
|
215
|
+
case 'boolean':
|
|
216
|
+
return value ? `${colors.green}true${colors.reset}` : `${colors.red}false${colors.reset}`;
|
|
217
|
+
case 'array':
|
|
218
|
+
return Array.isArray(value) ? value.join(', ') : String(value);
|
|
219
|
+
default:
|
|
220
|
+
return String(value);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Interactive configuration editor
|
|
225
|
+
*/ const runInteractiveConfig = async ()=>{
|
|
226
|
+
const config = await loadConfig();
|
|
227
|
+
const { configPath, exists } = await findConfigPath();
|
|
228
|
+
const rl = readline.createInterface({
|
|
229
|
+
input: process.stdin,
|
|
230
|
+
output: process.stdout
|
|
231
|
+
});
|
|
232
|
+
const question = (prompt)=>{
|
|
233
|
+
return new Promise((resolve)=>{
|
|
234
|
+
rl.question(prompt, (answer)=>{
|
|
235
|
+
resolve(answer);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
print('');
|
|
240
|
+
print(`${colors.bold}${colors.cyan}╔════════════════════════════════════════════════════════════════╗${colors.reset}`);
|
|
241
|
+
print(`${colors.bold}${colors.cyan}║ PROTOKOLL CONFIGURATION EDITOR ║${colors.reset}`);
|
|
242
|
+
print(`${colors.bold}${colors.cyan}╚════════════════════════════════════════════════════════════════╝${colors.reset}`);
|
|
243
|
+
print('');
|
|
244
|
+
print(`${colors.dim}Config file: ${configPath}${exists ? '' : ' (will be created)'}${colors.reset}`);
|
|
245
|
+
print('');
|
|
246
|
+
print(`${colors.dim}Press Enter to keep current value, or type a new value.${colors.reset}`);
|
|
247
|
+
print(`${colors.dim}Type 'q' to quit, 's' to save and exit.${colors.reset}`);
|
|
248
|
+
print('');
|
|
249
|
+
const updatedConfig = {
|
|
250
|
+
...config
|
|
251
|
+
};
|
|
252
|
+
const keys = Object.keys(CONFIG_SCHEMA);
|
|
253
|
+
// Group settings by category
|
|
254
|
+
const categories = {
|
|
255
|
+
'AI Models': [
|
|
256
|
+
'model',
|
|
257
|
+
'transcriptionModel',
|
|
258
|
+
'reasoningLevel'
|
|
259
|
+
],
|
|
260
|
+
'Directories': [
|
|
261
|
+
'inputDirectory',
|
|
262
|
+
'outputDirectory',
|
|
263
|
+
'processedDirectory',
|
|
264
|
+
'tempDirectory'
|
|
265
|
+
],
|
|
266
|
+
'Output Format': [
|
|
267
|
+
'outputStructure',
|
|
268
|
+
'outputFilenameOptions',
|
|
269
|
+
'timezone'
|
|
270
|
+
],
|
|
271
|
+
'Behavior': [
|
|
272
|
+
'interactive',
|
|
273
|
+
'selfReflection',
|
|
274
|
+
'silent',
|
|
275
|
+
'verbose',
|
|
276
|
+
'debug',
|
|
277
|
+
'dryRun'
|
|
278
|
+
],
|
|
279
|
+
'Limits': [
|
|
280
|
+
'maxAudioSize'
|
|
281
|
+
]
|
|
282
|
+
};
|
|
283
|
+
for (const [category, categoryKeys] of Object.entries(categories)){
|
|
284
|
+
print(`${colors.bold}${colors.blue}── ${category} ──${colors.reset}`);
|
|
285
|
+
print('');
|
|
286
|
+
for (const key of categoryKeys){
|
|
287
|
+
if (!keys.includes(key)) continue;
|
|
288
|
+
const schema = CONFIG_SCHEMA[key];
|
|
289
|
+
const currentValue = config[key];
|
|
290
|
+
const defaultValue = schema.default;
|
|
291
|
+
// Show setting info
|
|
292
|
+
print(` ${colors.bold}${key}${colors.reset}`);
|
|
293
|
+
print(` ${colors.dim}${schema.description}${colors.reset}`);
|
|
294
|
+
if (schema.allowed) {
|
|
295
|
+
print(` ${colors.dim}Allowed: ${schema.allowed.join(', ')}${colors.reset}`);
|
|
296
|
+
}
|
|
297
|
+
if (schema.examples) {
|
|
298
|
+
print(` ${colors.dim}Examples: ${schema.examples.join(', ')}${colors.reset}`);
|
|
299
|
+
}
|
|
300
|
+
const displayCurrent = currentValue !== undefined ? formatValue(currentValue, schema.type) : `${colors.dim}default: ${formatValue(defaultValue, schema.type)}${colors.reset}`;
|
|
301
|
+
print(` Current: ${displayCurrent}`);
|
|
302
|
+
const input = await question(` ${colors.yellow}New value${colors.reset} (Enter to skip): `);
|
|
303
|
+
if (input.toLowerCase() === 'q') {
|
|
304
|
+
print('\nConfiguration cancelled.');
|
|
305
|
+
rl.close();
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (input.toLowerCase() === 's') {
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
if (input.trim()) {
|
|
312
|
+
const parsedValue = parseValue(input.trim(), schema.type);
|
|
313
|
+
// Validate against allowed values
|
|
314
|
+
if (schema.allowed) {
|
|
315
|
+
const valueToCheck = Array.isArray(parsedValue) ? parsedValue : [
|
|
316
|
+
parsedValue
|
|
317
|
+
];
|
|
318
|
+
const invalid = valueToCheck.filter((v)=>!schema.allowed.includes(String(v)));
|
|
319
|
+
if (invalid.length > 0) {
|
|
320
|
+
print(` ${colors.red}Invalid value(s): ${invalid.join(', ')}${colors.reset}`);
|
|
321
|
+
print(` ${colors.dim}Allowed: ${schema.allowed.join(', ')}${colors.reset}`);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
updatedConfig[key] = parsedValue;
|
|
326
|
+
print(` ${colors.green}✓ Set to: ${formatValue(parsedValue, schema.type)}${colors.reset}`);
|
|
327
|
+
}
|
|
328
|
+
print('');
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
rl.close();
|
|
332
|
+
// Show summary and ask to save
|
|
333
|
+
print(`${colors.bold}${colors.cyan}── Summary of Changes ──${colors.reset}`);
|
|
334
|
+
print('');
|
|
335
|
+
let hasChanges = false;
|
|
336
|
+
for (const key of Object.keys(updatedConfig)){
|
|
337
|
+
if (JSON.stringify(updatedConfig[key]) !== JSON.stringify(config[key])) {
|
|
338
|
+
var _CONFIG_SCHEMA_key, _CONFIG_SCHEMA_key1;
|
|
339
|
+
print(` ${colors.green}${key}${colors.reset}: ${formatValue(config[key], ((_CONFIG_SCHEMA_key = CONFIG_SCHEMA[key]) === null || _CONFIG_SCHEMA_key === void 0 ? void 0 : _CONFIG_SCHEMA_key.type) || 'string')} → ${formatValue(updatedConfig[key], ((_CONFIG_SCHEMA_key1 = CONFIG_SCHEMA[key]) === null || _CONFIG_SCHEMA_key1 === void 0 ? void 0 : _CONFIG_SCHEMA_key1.type) || 'string')}`);
|
|
340
|
+
hasChanges = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (!hasChanges) {
|
|
344
|
+
print(` ${colors.dim}No changes made.${colors.reset}`);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
print('');
|
|
348
|
+
const rl2 = readline.createInterface({
|
|
349
|
+
input: process.stdin,
|
|
350
|
+
output: process.stdout
|
|
351
|
+
});
|
|
352
|
+
const confirm = await new Promise((resolve)=>{
|
|
353
|
+
rl2.question(`${colors.yellow}Save changes?${colors.reset} (Y/n): `, resolve);
|
|
354
|
+
});
|
|
355
|
+
rl2.close();
|
|
356
|
+
if (confirm.toLowerCase() !== 'n') {
|
|
357
|
+
const savedPath = await saveConfig(updatedConfig);
|
|
358
|
+
print(`${colors.green}✓ Configuration saved to: ${savedPath}${colors.reset}`);
|
|
359
|
+
} else {
|
|
360
|
+
print('Changes discarded.');
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
/**
|
|
364
|
+
* List all configuration options
|
|
365
|
+
*/ const listConfig = async ()=>{
|
|
366
|
+
const config = await loadConfig();
|
|
367
|
+
const { configPath, exists } = await findConfigPath();
|
|
368
|
+
print('');
|
|
369
|
+
print(`${colors.bold}${colors.cyan}Protokoll Configuration${colors.reset}`);
|
|
370
|
+
print(`${colors.dim}Config file: ${configPath}${exists ? '' : ' (not found)'}${colors.reset}`);
|
|
371
|
+
print('');
|
|
372
|
+
// Calculate max key length for alignment
|
|
373
|
+
const maxKeyLen = Math.max(...Object.keys(CONFIG_SCHEMA).map((k)=>k.length));
|
|
374
|
+
for (const [key, schema] of Object.entries(CONFIG_SCHEMA)){
|
|
375
|
+
const currentValue = config[key];
|
|
376
|
+
const isDefault = currentValue === undefined;
|
|
377
|
+
const displayValue = isDefault ? `${colors.dim}${formatValue(schema.default, schema.type)} (default)${colors.reset}` : formatValue(currentValue, schema.type);
|
|
378
|
+
const paddedKey = key.padEnd(maxKeyLen);
|
|
379
|
+
print(` ${colors.bold}${paddedKey}${colors.reset} ${displayValue}`);
|
|
380
|
+
}
|
|
381
|
+
print('');
|
|
382
|
+
print(`${colors.dim}Run 'protokoll config' for interactive editor${colors.reset}`);
|
|
383
|
+
print(`${colors.dim}Run 'protokoll config <key> <value>' to set a value${colors.reset}`);
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Get a specific configuration value
|
|
387
|
+
*/ const getConfigValue = async (key)=>{
|
|
388
|
+
const config = await loadConfig();
|
|
389
|
+
const schema = CONFIG_SCHEMA[key];
|
|
390
|
+
if (!schema) {
|
|
391
|
+
print(`${colors.red}Unknown configuration key: ${key}${colors.reset}`);
|
|
392
|
+
print('');
|
|
393
|
+
print('Available keys:');
|
|
394
|
+
for (const k of Object.keys(CONFIG_SCHEMA)){
|
|
395
|
+
print(` ${k}`);
|
|
396
|
+
}
|
|
397
|
+
process.exit(1);
|
|
398
|
+
}
|
|
399
|
+
const currentValue = config[key];
|
|
400
|
+
const isDefault = currentValue === undefined;
|
|
401
|
+
print('');
|
|
402
|
+
print(`${colors.bold}${key}${colors.reset}`);
|
|
403
|
+
print(`${colors.dim}${schema.description}${colors.reset}`);
|
|
404
|
+
print('');
|
|
405
|
+
if (isDefault) {
|
|
406
|
+
print(`Value: ${colors.dim}${formatValue(schema.default, schema.type)} (default)${colors.reset}`);
|
|
407
|
+
} else {
|
|
408
|
+
print(`Value: ${formatValue(currentValue, schema.type)}`);
|
|
409
|
+
}
|
|
410
|
+
if (schema.allowed) {
|
|
411
|
+
print(`${colors.dim}Allowed: ${schema.allowed.join(', ')}${colors.reset}`);
|
|
412
|
+
}
|
|
413
|
+
if (schema.examples) {
|
|
414
|
+
print(`${colors.dim}Examples: ${schema.examples.join(', ')}${colors.reset}`);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
/**
|
|
418
|
+
* Set a configuration value
|
|
419
|
+
*/ const setConfigValue = async (key, value)=>{
|
|
420
|
+
const config = await loadConfig();
|
|
421
|
+
const schema = CONFIG_SCHEMA[key];
|
|
422
|
+
if (!schema) {
|
|
423
|
+
print(`${colors.red}Unknown configuration key: ${key}${colors.reset}`);
|
|
424
|
+
print('');
|
|
425
|
+
print('Available keys:');
|
|
426
|
+
for (const k of Object.keys(CONFIG_SCHEMA)){
|
|
427
|
+
print(` ${k}`);
|
|
428
|
+
}
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
const parsedValue = parseValue(value, schema.type);
|
|
432
|
+
// Validate against allowed values
|
|
433
|
+
if (schema.allowed) {
|
|
434
|
+
const valueToCheck = Array.isArray(parsedValue) ? parsedValue : [
|
|
435
|
+
parsedValue
|
|
436
|
+
];
|
|
437
|
+
const invalid = valueToCheck.filter((v)=>!schema.allowed.includes(String(v)));
|
|
438
|
+
if (invalid.length > 0) {
|
|
439
|
+
print(`${colors.red}Invalid value: ${value}${colors.reset}`);
|
|
440
|
+
print(`${colors.dim}Allowed: ${schema.allowed.join(', ')}${colors.reset}`);
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
config[key] = parsedValue;
|
|
445
|
+
const savedPath = await saveConfig(config);
|
|
446
|
+
print(`${colors.green}✓${colors.reset} ${key} = ${formatValue(parsedValue, schema.type)}`);
|
|
447
|
+
print(`${colors.dim}Saved to: ${savedPath}${colors.reset}`);
|
|
448
|
+
};
|
|
449
|
+
/**
|
|
450
|
+
* Show config file path
|
|
451
|
+
*/ const showConfigPath = async ()=>{
|
|
452
|
+
const { configPath, exists } = await findConfigPath();
|
|
453
|
+
print(configPath);
|
|
454
|
+
if (!exists) {
|
|
455
|
+
print(`${colors.dim}(file does not exist yet)${colors.reset}`);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Register the config command
|
|
460
|
+
*/ const registerConfigCommands = (program)=>{
|
|
461
|
+
program.command('config').description('View and edit Protokoll configuration').argument('[key]', 'Configuration key to view or set').argument('[value]', 'Value to set (if provided)').option('-l, --list', 'List all configuration options').option('-p, --path', 'Show configuration file path').action(async (key, value, options)=>{
|
|
462
|
+
try {
|
|
463
|
+
if (options.path) {
|
|
464
|
+
await showConfigPath();
|
|
465
|
+
} else if (options.list) {
|
|
466
|
+
await listConfig();
|
|
467
|
+
} else if (key && value) {
|
|
468
|
+
await setConfigValue(key, value);
|
|
469
|
+
} else if (key) {
|
|
470
|
+
await getConfigValue(key);
|
|
471
|
+
} else {
|
|
472
|
+
await runInteractiveConfig();
|
|
473
|
+
}
|
|
474
|
+
} catch (error) {
|
|
475
|
+
print(`${colors.red}Error: ${error instanceof Error ? error.message : 'Unknown error'}${colors.reset}`);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
export { CONFIG_SCHEMA, registerConfigCommands };
|
|
482
|
+
//# sourceMappingURL=config.js.map
|