@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.
Files changed (75) hide show
  1. package/.cursor/rules/definition-of-done.md +1 -0
  2. package/.cursor/rules/no-emoticons.md +26 -12
  3. package/README.md +483 -69
  4. package/dist/agentic/executor.js +473 -41
  5. package/dist/agentic/executor.js.map +1 -1
  6. package/dist/agentic/index.js.map +1 -1
  7. package/dist/agentic/tools/lookup-person.js +123 -4
  8. package/dist/agentic/tools/lookup-person.js.map +1 -1
  9. package/dist/agentic/tools/lookup-project.js +139 -22
  10. package/dist/agentic/tools/lookup-project.js.map +1 -1
  11. package/dist/agentic/tools/route-note.js +5 -1
  12. package/dist/agentic/tools/route-note.js.map +1 -1
  13. package/dist/arguments.js +6 -3
  14. package/dist/arguments.js.map +1 -1
  15. package/dist/cli/action.js +704 -0
  16. package/dist/cli/action.js.map +1 -0
  17. package/dist/cli/config.js +482 -0
  18. package/dist/cli/config.js.map +1 -0
  19. package/dist/cli/context.js +466 -0
  20. package/dist/cli/context.js.map +1 -0
  21. package/dist/cli/feedback.js +858 -0
  22. package/dist/cli/feedback.js.map +1 -0
  23. package/dist/cli/index.js +103 -0
  24. package/dist/cli/index.js.map +1 -0
  25. package/dist/cli/install.js +572 -0
  26. package/dist/cli/install.js.map +1 -0
  27. package/dist/cli/transcript.js +199 -0
  28. package/dist/cli/transcript.js.map +1 -0
  29. package/dist/constants.js +11 -4
  30. package/dist/constants.js.map +1 -1
  31. package/dist/context/index.js +25 -1
  32. package/dist/context/index.js.map +1 -1
  33. package/dist/context/storage.js +56 -3
  34. package/dist/context/storage.js.map +1 -1
  35. package/dist/interactive/handler.js +310 -9
  36. package/dist/interactive/handler.js.map +1 -1
  37. package/dist/main.js +11 -1
  38. package/dist/main.js.map +1 -1
  39. package/dist/output/index.js.map +1 -1
  40. package/dist/output/manager.js +46 -1
  41. package/dist/output/manager.js.map +1 -1
  42. package/dist/phases/complete.js +37 -2
  43. package/dist/phases/complete.js.map +1 -1
  44. package/dist/pipeline/orchestrator.js +104 -31
  45. package/dist/pipeline/orchestrator.js.map +1 -1
  46. package/dist/protokoll.js +68 -2
  47. package/dist/protokoll.js.map +1 -1
  48. package/dist/reasoning/client.js +83 -0
  49. package/dist/reasoning/client.js.map +1 -1
  50. package/dist/reasoning/index.js +1 -0
  51. package/dist/reasoning/index.js.map +1 -1
  52. package/dist/util/metadata.js.map +1 -1
  53. package/dist/util/sound.js +116 -0
  54. package/dist/util/sound.js.map +1 -0
  55. package/docs/duplicate-question-prevention.md +117 -0
  56. package/docs/examples.md +152 -0
  57. package/docs/interactive-context-example.md +92 -0
  58. package/docs/package-lock.json +6 -0
  59. package/docs/package.json +3 -1
  60. package/guide/action.md +375 -0
  61. package/guide/config.md +207 -0
  62. package/guide/configuration.md +82 -67
  63. package/guide/context-commands.md +574 -0
  64. package/guide/context-system.md +20 -7
  65. package/guide/development.md +106 -4
  66. package/guide/feedback.md +335 -0
  67. package/guide/index.md +100 -4
  68. package/guide/interactive.md +15 -14
  69. package/guide/quickstart.md +21 -7
  70. package/guide/reasoning.md +18 -4
  71. package/guide/routing.md +192 -97
  72. package/package.json +1 -1
  73. package/scripts/coverage-priority.mjs +323 -0
  74. package/tsconfig.tsbuildinfo +1 -1
  75. 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