@sushanglewis/lincoln 1.5.0 → 1.5.2

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 (305) hide show
  1. package/dist/main.d.ts +3 -0
  2. package/dist/main.js +1286 -916
  3. package/dist/main.js.map +1 -1
  4. package/dist/postinstall.d.ts +3 -0
  5. package/dist/postinstall.js +18 -0
  6. package/dist/postinstall.js.map +1 -0
  7. package/framework/.claude/agents/_contract.md +79 -0
  8. package/framework/.claude/agents/architect.md +31 -0
  9. package/framework/.claude/agents/default.md +151 -0
  10. package/framework/.claude/agents/designer.md +33 -0
  11. package/framework/.claude/agents/engineer.md +42 -0
  12. package/framework/.claude/agents/external/NOTICES.md +12 -0
  13. package/framework/.claude/agents/external/everything-claude-code.manifest.yaml +23 -0
  14. package/framework/.claude/agents/external/oh-my-claudecode.manifest.yaml +26 -0
  15. package/framework/.claude/agents/external/wshobson-agents.manifest.yaml +23 -0
  16. package/framework/.claude/agents/frontend-engineer.md +32 -0
  17. package/framework/.claude/agents/knowledge-curator.md +28 -0
  18. package/framework/.claude/agents/pm.md +38 -0
  19. package/framework/.claude/agents/qa.md +28 -0
  20. package/framework/.claude/agents/release-coordinator.md +25 -0
  21. package/framework/.claude/agents/researcher.md +41 -0
  22. package/framework/.claude/harnesses/claude-code.yaml +15 -0
  23. package/framework/.claude/harnesses/codex.yaml +40 -0
  24. package/framework/.claude/harnesses/command-map.yaml +255 -0
  25. package/framework/.claude/harnesses/opencode.yaml +32 -0
  26. package/framework/.claude/harnesses/scenarios.yaml +45 -0
  27. package/framework/.claude/hooks/on-pr-event.sh +69 -0
  28. package/framework/.claude/hooks/on-session-start.sh +427 -0
  29. package/framework/.claude/hooks/on-stop.sh +104 -0
  30. package/framework/.claude/hooks/post-tool-use.sh +161 -0
  31. package/framework/.claude/hooks/pre-tool-use.sh +246 -0
  32. package/framework/.claude/schemas/agent-definition.schema.json +35 -0
  33. package/framework/.claude/schemas/benchmark-report.schema.json +148 -0
  34. package/framework/.claude/schemas/pm-to-ux-handoff.schema.yaml +128 -0
  35. package/framework/.claude/schemas/stage-definition.schema.json +233 -0
  36. package/framework/.claude/schemas/workflow-stage.schema.json +86 -0
  37. package/framework/.claude/schemas/workflow-template.schema.json +88 -0
  38. package/framework/.claude/settings.json +14 -0
  39. package/framework/.claude/skills/build-product-prototype/SKILL.md +35 -0
  40. package/framework/.claude/skills/build-product-prototype/prompts/main.md +78 -0
  41. package/framework/.claude/skills/clarify-requirements/SKILL.md +32 -0
  42. package/framework/.claude/skills/clarify-requirements/prompts/main.md +81 -0
  43. package/framework/.claude/skills/dependencies.yaml +139 -0
  44. package/framework/.claude/skills/draft-product-design/SKILL.md +40 -0
  45. package/framework/.claude/skills/draft-product-design/prompts/main.md +47 -0
  46. package/framework/.claude/skills/lc-analyze-frameworks/SKILL.md +43 -0
  47. package/framework/.claude/skills/lc-analyze-frameworks/prompts/main.md +95 -0
  48. package/framework/.claude/skills/lc-benchmark/SKILL.md +61 -0
  49. package/framework/.claude/skills/lc-build-codebase-knowledge/SKILL.md +38 -0
  50. package/framework/.claude/skills/lc-build-codebase-knowledge/prompts/build-codebase-knowledge.md +61 -0
  51. package/framework/.claude/skills/lc-collect-intelligence/SKILL.md +43 -0
  52. package/framework/.claude/skills/lc-collect-intelligence/prompts/main.md +72 -0
  53. package/framework/.claude/skills/lc-competitive-analysis/SKILL.md +43 -0
  54. package/framework/.claude/skills/lc-competitive-analysis/prompts/main.md +70 -0
  55. package/framework/.claude/skills/lc-explore-opensource/SKILL.md +36 -0
  56. package/framework/.claude/skills/lc-explore-opensource/prompts/explore-opensource.md +77 -0
  57. package/framework/.claude/skills/lc-first-principles/SKILL.md +43 -0
  58. package/framework/.claude/skills/lc-first-principles/prompts/main.md +73 -0
  59. package/framework/.claude/skills/lc-handoff/SKILL.md +34 -0
  60. package/framework/.claude/skills/lc-init-branch/SKILL.md +52 -0
  61. package/framework/.claude/skills/lc-market-research/SKILL.md +43 -0
  62. package/framework/.claude/skills/lc-market-research/prompts/main.md +79 -0
  63. package/framework/.claude/skills/lc-product-research/SKILL.md +43 -0
  64. package/framework/.claude/skills/lc-product-research/prompts/main.md +80 -0
  65. package/framework/.claude/skills/lc-research-report/SKILL.md +43 -0
  66. package/framework/.claude/skills/lc-research-report/prompts/main.md +90 -0
  67. package/framework/.claude/skills/lc-research-scope/SKILL.md +43 -0
  68. package/framework/.claude/skills/lc-research-scope/prompts/main.md +82 -0
  69. package/framework/.claude/skills/lc-setup/SKILL.md +68 -0
  70. package/framework/.claude/skills/lc-setup/prompts/main.md +88 -0
  71. package/framework/.claude/skills/lc-stage/SKILL.md +45 -0
  72. package/framework/.claude/skills/lc-stakeholder-research/SKILL.md +43 -0
  73. package/framework/.claude/skills/lc-stakeholder-research/prompts/main.md +65 -0
  74. package/framework/.claude/skills/lc-status/SKILL.md +25 -0
  75. package/framework/.claude/skills/lc-storytelling/SKILL.md +43 -0
  76. package/framework/.claude/skills/lc-storytelling/prompts/main.md +77 -0
  77. package/framework/.claude/skills/lc-wf/SKILL.md +54 -0
  78. package/framework/.claude/skills/lc-workflow/SKILL.md +48 -0
  79. package/framework/.claude/skills/lc-workflow/validators/validate.py +755 -0
  80. package/framework/.claude/skills/lc-workflow-router/SKILL.md +41 -0
  81. package/framework/.claude/skills/lc-workflow-router/prompts/intake-prompt.md +91 -0
  82. package/framework/.claude/skills/lc-workflow-router/prompts/router-prompt.md +44 -0
  83. package/framework/.claude/skills/plan-tdd-development/SKILL.md +31 -0
  84. package/framework/.claude/skills/plan-tdd-development/prompts/main.md +44 -0
  85. package/framework/.claude/skills/process-interview/SKILL.md +32 -0
  86. package/framework/.claude/skills/process-interview/prompts/main.md +47 -0
  87. package/framework/.claude/skills/propose-with-openspec/SKILL.md +37 -0
  88. package/framework/.claude/skills/propose-with-openspec/prompts/main.md +53 -0
  89. package/framework/.claude/skills/split-to-github/SKILL.md +32 -0
  90. package/framework/.claude/skills/split-to-github/prompts/main.md +42 -0
  91. package/framework/.claude/skills/sync-to-knowledge/SKILL.md +35 -0
  92. package/framework/.claude/skills/sync-to-knowledge/prompts/main.md +54 -0
  93. package/framework/.claude/skills/workflow-continue/SKILL.md +24 -0
  94. package/framework/.claude/skills/workflow-continue/prompts/main.md +25 -0
  95. package/framework/.claude/stages/build-codebase-knowledge.yaml +42 -0
  96. package/framework/.claude/stages/clarify.yaml +59 -0
  97. package/framework/.claude/stages/explore-opensource.yaml +40 -0
  98. package/framework/.claude/stages/implement.yaml +60 -0
  99. package/framework/.claude/stages/ingest.yaml +40 -0
  100. package/framework/.claude/stages/lc-analyze-frameworks.yaml +37 -0
  101. package/framework/.claude/stages/lc-collect-intelligence.yaml +37 -0
  102. package/framework/.claude/stages/lc-competitive-analysis.yaml +37 -0
  103. package/framework/.claude/stages/lc-first-principles.yaml +37 -0
  104. package/framework/.claude/stages/lc-market-research.yaml +37 -0
  105. package/framework/.claude/stages/lc-product-research.yaml +37 -0
  106. package/framework/.claude/stages/lc-research-report.yaml +38 -0
  107. package/framework/.claude/stages/lc-research-scope.yaml +38 -0
  108. package/framework/.claude/stages/lc-stakeholder-research.yaml +37 -0
  109. package/framework/.claude/stages/lc-storytelling.yaml +37 -0
  110. package/framework/.claude/stages/product-design-docs.yaml +66 -0
  111. package/framework/.claude/stages/product-prototype.yaml +53 -0
  112. package/framework/.claude/stages/propose.yaml +45 -0
  113. package/framework/.claude/stages/split.yaml +41 -0
  114. package/framework/.claude/stages/sync-knowledge.yaml +49 -0
  115. package/framework/.claude/stages/tdd-development-plan.yaml +43 -0
  116. package/framework/.claude/stages/workflow-router.yaml +45 -0
  117. package/framework/.claude/templates/solo-workflow-context.yaml +23 -0
  118. package/framework/.claude/workflows/README.md +93 -0
  119. package/framework/.claude/workflows/bug-fix.yaml +81 -0
  120. package/framework/.claude/workflows/design-spike.yaml +55 -0
  121. package/framework/.claude/workflows/existing-project-iteration.yaml +106 -0
  122. package/framework/.claude/workflows/interview-to-knowledge.yaml +189 -0
  123. package/framework/.claude/workflows/oss-first-design.yaml +100 -0
  124. package/framework/.claude/workflows/pm-research.yaml +115 -0
  125. package/framework/.claude-plugin/hooks.json +6 -0
  126. package/framework/.claude-plugin/marketplace.json +32 -0
  127. package/framework/.claude-plugin/plugin.json +57 -0
  128. package/framework/.version-bump.json +43 -0
  129. package/framework/CLAUDE.md +140 -0
  130. package/framework/CONTRIBUTING.md +219 -0
  131. package/framework/LICENSE +21 -0
  132. package/framework/README.en.md +139 -0
  133. package/framework/README.md +139 -0
  134. package/framework/RELEASE.md +334 -0
  135. package/framework/SKILL.md +24 -0
  136. package/framework/USAGE.md +356 -0
  137. package/framework/manifest.json +1214 -0
  138. package/framework/requirements.txt +4 -0
  139. package/framework/scripts/__init__.py +0 -0
  140. package/framework/scripts/build_codebase_knowledge.py +76 -0
  141. package/framework/scripts/bump_version.py +181 -0
  142. package/framework/scripts/check-dependency-drift.py +69 -0
  143. package/framework/scripts/check-harness-drift.sh +57 -0
  144. package/framework/scripts/check-main-merge-hygiene.py +128 -0
  145. package/framework/scripts/check-skill-dependencies.sh +124 -0
  146. package/framework/scripts/init-lincoln-branch.sh +300 -0
  147. package/framework/scripts/init-project.sh +184 -0
  148. package/framework/scripts/lc-benchmark-cli.py +66 -0
  149. package/framework/scripts/lincoln-audit.py +288 -0
  150. package/framework/scripts/lincoln-setup.py +651 -0
  151. package/framework/scripts/lincoln-status.py +448 -0
  152. package/framework/scripts/lincoln_benchmark.py +224 -0
  153. package/framework/scripts/lincoln_benchmark_eval.py +104 -0
  154. package/framework/scripts/lincoln_benchmark_metrics.py +818 -0
  155. package/framework/scripts/lincoln_benchmark_report.py +141 -0
  156. package/framework/scripts/lincoln_command_map.py +289 -0
  157. package/framework/scripts/lincoln_dependency_manager.py +335 -0
  158. package/framework/scripts/lincoln_documents.py +175 -0
  159. package/framework/scripts/lincoln_harness_adapter.py +319 -0
  160. package/framework/scripts/lincoln_index.py +255 -0
  161. package/framework/scripts/lincoln_paths.py +198 -0
  162. package/framework/scripts/lincoln_prd.py +227 -0
  163. package/framework/scripts/lincoln_role.py +46 -0
  164. package/framework/scripts/lincoln_scenario.py +59 -0
  165. package/framework/scripts/lincoln_session_start_metrics.py +71 -0
  166. package/framework/scripts/lincoln_skill_prompt.py +48 -0
  167. package/framework/scripts/lincoln_trace.py +335 -0
  168. package/framework/scripts/lincoln_workflow.py +199 -0
  169. package/framework/scripts/list-active-lincoln-branches.sh +87 -0
  170. package/framework/scripts/package-lincoln-plugin.py +340 -0
  171. package/framework/scripts/run-infrastructure-tests.py +84 -0
  172. package/framework/scripts/stage_loader.py +1071 -0
  173. package/framework/scripts/static-check.sh +356 -0
  174. package/framework/scripts/sync-external-agents.sh +210 -0
  175. package/framework/scripts/sync-framework-package.mjs +218 -0
  176. package/framework/scripts/task_tool_guard.py +213 -0
  177. package/framework/scripts/track-artifacts.py +140 -0
  178. package/framework/scripts/validate_stage.py +350 -0
  179. package/{README.md → framework/tools/lincoln/README.md} +4 -4
  180. package/framework/tools/lincoln/package-lock.json +3349 -0
  181. package/framework/tools/lincoln/package.json +48 -0
  182. package/framework/tools/lincoln/src/cli.tsx +60 -0
  183. package/framework/tools/lincoln/src/components/AudioMeter.tsx +34 -0
  184. package/framework/tools/lincoln/src/components/CancelledScreen.tsx +11 -0
  185. package/framework/tools/lincoln/src/components/DevicesMenu.tsx +79 -0
  186. package/framework/tools/lincoln/src/components/HintBar.tsx +25 -0
  187. package/framework/tools/lincoln/src/components/LogView.tsx +36 -0
  188. package/framework/tools/lincoln/src/components/ModelMenu.tsx +122 -0
  189. package/framework/tools/lincoln/src/components/ReadyScreen.tsx +54 -0
  190. package/framework/tools/lincoln/src/components/RecordingApp.tsx +211 -0
  191. package/framework/tools/lincoln/src/components/RecordingScreen.tsx +41 -0
  192. package/framework/tools/lincoln/src/components/SelectMenu.tsx +53 -0
  193. package/framework/tools/lincoln/src/components/StatusPanel.tsx +52 -0
  194. package/framework/tools/lincoln/src/components/StopConfirmation.tsx +32 -0
  195. package/framework/tools/lincoln/src/config/args.ts +35 -0
  196. package/framework/tools/lincoln/src/config/loadConfig.ts +81 -0
  197. package/framework/tools/lincoln/src/config/resolveConfig.ts +85 -0
  198. package/framework/tools/lincoln/src/devices/listDevices.ts +11 -0
  199. package/framework/tools/lincoln/src/devices/parseDevices.ts +25 -0
  200. package/framework/tools/lincoln/src/hooks/useKeyHandler.ts +63 -0
  201. package/framework/tools/lincoln/src/main.tsx +4 -0
  202. package/framework/tools/lincoln/src/models/models.ts +34 -0
  203. package/framework/tools/lincoln/src/models/runWarmup.ts +46 -0
  204. package/framework/tools/lincoln/src/models/warmupProgress.ts +30 -0
  205. package/framework/tools/lincoln/src/recording/spawnRecorder.ts +127 -0
  206. package/framework/tools/lincoln/src/recording/useRecorder.ts +146 -0
  207. package/framework/tools/lincoln/src/utils/amplitudeToMeter.ts +4 -0
  208. package/framework/tools/lincoln/src/utils/formatDuration.ts +6 -0
  209. package/framework/tools/lincoln/src/utils/sessionId.ts +25 -0
  210. package/framework/tools/lincoln/tests/cli.test.tsx +52 -0
  211. package/framework/tools/lincoln/tests/components/AudioMeter.test.tsx +31 -0
  212. package/framework/tools/lincoln/tests/components/CancelledScreen.test.tsx +12 -0
  213. package/framework/tools/lincoln/tests/components/ReadyScreen.test.tsx +68 -0
  214. package/framework/tools/lincoln/tests/components/RecordingApp.menus.test.tsx +158 -0
  215. package/framework/tools/lincoln/tests/components/RecordingApp.test.tsx +248 -0
  216. package/framework/tools/lincoln/tests/components/RecordingScreen.test.tsx +37 -0
  217. package/framework/tools/lincoln/tests/components/SelectMenu.test.tsx +59 -0
  218. package/framework/tools/lincoln/tests/components/StopConfirmation.test.tsx +38 -0
  219. package/framework/tools/lincoln/tests/config/args.test.ts +49 -0
  220. package/framework/tools/lincoln/tests/config/loadConfig.test.ts +59 -0
  221. package/framework/tools/lincoln/tests/config/resolveConfig.test.ts +72 -0
  222. package/framework/tools/lincoln/tests/devices/parseDevices.test.ts +21 -0
  223. package/framework/tools/lincoln/tests/hooks/useKeyHandler.enabled.test.tsx +44 -0
  224. package/framework/tools/lincoln/tests/hooks/useKeyHandler.test.tsx +130 -0
  225. package/framework/tools/lincoln/tests/models/models.test.ts +27 -0
  226. package/framework/tools/lincoln/tests/models/warmupProgress.test.ts +25 -0
  227. package/framework/tools/lincoln/tests/recording/spawnRecorder.test.ts +85 -0
  228. package/framework/tools/lincoln/tests/recording/useRecorder.test.tsx +172 -0
  229. package/framework/tools/lincoln/tests/utils/amplitudeToMeter.test.ts +22 -0
  230. package/framework/tools/lincoln/tests/utils/formatDuration.test.ts +11 -0
  231. package/framework/tools/lincoln/tests/utils/sessionId.test.ts +30 -0
  232. package/framework/tools/lincoln/tsconfig.json +22 -0
  233. package/framework/tools/lincoln/tsconfig.tests.json +10 -0
  234. package/framework/tools/lincoln/tsup.config.ts +17 -0
  235. package/framework/tools/lincoln/vitest.config.ts +20 -0
  236. package/framework/tools/lincoln-installer/package-lock.json +3611 -0
  237. package/framework/tools/lincoln-installer/package.json +53 -0
  238. package/framework/tools/lincoln-installer/src/cli.tsx +63 -0
  239. package/framework/tools/lincoln-installer/src/components/HarnessSelectScreen.tsx +43 -0
  240. package/framework/tools/lincoln-installer/src/components/InstallerApp.tsx +98 -0
  241. package/framework/tools/lincoln-installer/src/components/OptionsScreen.tsx +56 -0
  242. package/framework/tools/lincoln-installer/src/components/ProgressScreen.tsx +25 -0
  243. package/framework/tools/lincoln-installer/src/components/ResultScreen.tsx +40 -0
  244. package/framework/tools/lincoln-installer/src/components/SelectMenu.tsx +73 -0
  245. package/framework/tools/lincoln-installer/src/components/SummaryScreen.tsx +49 -0
  246. package/framework/tools/lincoln-installer/src/components/UpdateApp.tsx +135 -0
  247. package/framework/tools/lincoln-installer/src/components/WelcomeScreen.tsx +22 -0
  248. package/framework/tools/lincoln-installer/src/config/args.ts +76 -0
  249. package/framework/tools/lincoln-installer/src/config/resolveConfig.ts +33 -0
  250. package/framework/tools/lincoln-installer/src/config/updateArgs.ts +67 -0
  251. package/framework/tools/lincoln-installer/src/hooks/useSetup.ts +149 -0
  252. package/framework/tools/lincoln-installer/src/main.tsx +11 -0
  253. package/framework/tools/lincoln-installer/src/types.ts +19 -0
  254. package/framework/tools/lincoln-installer/src/update-main.tsx +11 -0
  255. package/framework/tools/lincoln-installer/src/update.tsx +90 -0
  256. package/framework/tools/lincoln-installer/src/utils/detectHarness.ts +51 -0
  257. package/framework/tools/lincoln-installer/src/utils/runSetup.ts +62 -0
  258. package/framework/tools/lincoln-installer/src/utils/updateRelease.ts +454 -0
  259. package/framework/tools/lincoln-installer/tests/components/InstallerApp.test.tsx +88 -0
  260. package/framework/tools/lincoln-installer/tests/components/SelectMenu.test.tsx +26 -0
  261. package/framework/tools/lincoln-installer/tests/components/UpdateApp.test.tsx +170 -0
  262. package/framework/tools/lincoln-installer/tests/components/screens.test.tsx +310 -0
  263. package/framework/tools/lincoln-installer/tests/config/args.test.ts +66 -0
  264. package/framework/tools/lincoln-installer/tests/config/resolveConfig.test.ts +75 -0
  265. package/framework/tools/lincoln-installer/tests/config/updateArgs.test.ts +52 -0
  266. package/framework/tools/lincoln-installer/tests/hooks/useSetup.test.tsx +213 -0
  267. package/framework/tools/lincoln-installer/tests/update.test.tsx +108 -0
  268. package/framework/tools/lincoln-installer/tests/utils/detectHarness.test.ts +40 -0
  269. package/framework/tools/lincoln-installer/tests/utils/runSetup.test.ts +45 -0
  270. package/framework/tools/lincoln-installer/tests/utils/updateRelease.test.ts +559 -0
  271. package/framework/tools/lincoln-installer/tsconfig.json +22 -0
  272. package/framework/tools/lincoln-installer/tsconfig.tests.json +8 -0
  273. package/framework/tools/lincoln-installer/tsup.config.ts +17 -0
  274. package/framework/tools/lincoln-installer/vitest.config.ts +20 -0
  275. package/framework/tools/lincoln-record/Cargo.lock +1896 -0
  276. package/framework/tools/lincoln-record/Cargo.toml +24 -0
  277. package/framework/tools/lincoln-record/diarize/diarize.py +117 -0
  278. package/framework/tools/lincoln-record/diarize/merge.py +55 -0
  279. package/framework/tools/lincoln-record/src/audio/capture/microphone.rs +194 -0
  280. package/framework/tools/lincoln-record/src/audio/capture/mod.rs +1 -0
  281. package/framework/tools/lincoln-record/src/audio/devices.rs +20 -0
  282. package/framework/tools/lincoln-record/src/audio/mod.rs +5 -0
  283. package/framework/tools/lincoln-record/src/audio/resampler.rs +44 -0
  284. package/framework/tools/lincoln-record/src/audio/saver.rs +87 -0
  285. package/framework/tools/lincoln-record/src/audio/source.rs +104 -0
  286. package/framework/tools/lincoln-record/src/cli.rs +96 -0
  287. package/framework/tools/lincoln-record/src/config.rs +135 -0
  288. package/framework/tools/lincoln-record/src/lib.rs +6 -0
  289. package/framework/tools/lincoln-record/src/main.rs +242 -0
  290. package/framework/tools/lincoln-record/src/model/mod.rs +248 -0
  291. package/framework/tools/lincoln-record/src/output/metadata.rs +30 -0
  292. package/framework/tools/lincoln-record/src/output/mod.rs +2 -0
  293. package/framework/tools/lincoln-record/src/output/transcript.rs +42 -0
  294. package/framework/tools/lincoln-record/src/transcription/diarization.rs +64 -0
  295. package/framework/tools/lincoln-record/src/transcription/mod.rs +3 -0
  296. package/framework/tools/lincoln-record/src/transcription/provider.rs +43 -0
  297. package/framework/tools/lincoln-record/src/transcription/whisper.rs +176 -0
  298. package/framework/tools/lincoln-record/tests/cli_args.rs +103 -0
  299. package/framework/tools/lincoln-record/tests/config_test.rs +70 -0
  300. package/framework/tools/lincoln-record/tests/devices_test.rs +23 -0
  301. package/framework/tools/lincoln-record/tests/diarization_tests.rs +101 -0
  302. package/framework/tools/lincoln-record/tests/integration_tests.rs +44 -0
  303. package/framework/tools/lincoln-record/tests/model_test.rs +68 -0
  304. package/framework/tools/lincoln-record/tests/transcription_tests.rs +73 -0
  305. package/package.json +19 -13
@@ -0,0 +1,96 @@
1
+ use clap::Parser;
2
+ use std::path::PathBuf;
3
+
4
+ /// Top-level CLI for `lincoln-record`.
5
+ #[derive(Debug, Clone, Parser)]
6
+ #[command(name = "lincoln-record")]
7
+ #[command(about = "Headless local recorder and transcriber for Lincoln interviews")]
8
+ #[command(version)]
9
+ pub enum Cli {
10
+ /// Record microphone and optional system audio.
11
+ Record(RecordArgs),
12
+ /// Stop a running recording session.
13
+ Stop(StopArgs),
14
+ /// Transcribe an existing audio file.
15
+ Transcribe(TranscribeArgs),
16
+ /// List available audio input devices.
17
+ Devices,
18
+ /// Download models and warm up caches.
19
+ Warmup(WarmupArgs),
20
+ }
21
+
22
+ /// Arguments for the `warmup` subcommand.
23
+ #[derive(Debug, Clone, Parser)]
24
+ pub struct WarmupArgs {
25
+ #[arg(long, help = "Model name to download")]
26
+ pub model: Option<String>,
27
+
28
+ #[arg(long, help = "Engine name")]
29
+ pub engine: Option<String>,
30
+
31
+ #[arg(long, help = "Output directory for the model cache")]
32
+ pub cache_dir: Option<PathBuf>,
33
+ }
34
+
35
+ /// Arguments for the `record` subcommand.
36
+ #[derive(Debug, Clone, Parser)]
37
+ pub struct RecordArgs {
38
+ #[arg(long, help = "Unique session identifier for this interview")]
39
+ pub session_id: String,
40
+
41
+ #[arg(long, help = "Microphone device name to use")]
42
+ pub mic: Option<String>,
43
+
44
+ #[arg(long, help = "Automatically capture system audio on macOS")]
45
+ pub system_auto: bool,
46
+
47
+ #[arg(long, default_value = "whisper", help = "Transcription engine")]
48
+ pub engine: String,
49
+
50
+ #[arg(long, help = "Model name or path")]
51
+ pub model: Option<String>,
52
+
53
+ #[arg(long, help = "Enable speaker diarization")]
54
+ pub diarize: bool,
55
+
56
+ #[arg(long, help = "Output directory")]
57
+ pub output: Option<PathBuf>,
58
+
59
+ #[arg(long, help = "Language code (e.g. en, zh)")]
60
+ pub language: Option<String>,
61
+
62
+ /// Recording duration in seconds. Hidden while full SIGINT stop control is in progress.
63
+ #[arg(long, hide = true)]
64
+ pub duration: Option<f32>,
65
+ }
66
+
67
+ /// Arguments for the `stop` subcommand.
68
+ #[derive(Debug, Clone, Parser)]
69
+ pub struct StopArgs {
70
+ #[arg(long, help = "Session identifier to stop")]
71
+ pub session_id: String,
72
+ }
73
+
74
+ /// Arguments for the `transcribe` subcommand.
75
+ #[derive(Debug, Clone, Parser)]
76
+ pub struct TranscribeArgs {
77
+ pub path: PathBuf,
78
+
79
+ #[arg(long, help = "Session identifier")]
80
+ pub session_id: Option<String>,
81
+
82
+ #[arg(long, default_value = "whisper", help = "Transcription engine")]
83
+ pub engine: String,
84
+
85
+ #[arg(long, help = "Model name or path")]
86
+ pub model: Option<String>,
87
+
88
+ #[arg(long, help = "Enable speaker diarization")]
89
+ pub diarize: bool,
90
+
91
+ #[arg(long, help = "Output directory")]
92
+ pub output: Option<PathBuf>,
93
+
94
+ #[arg(long, help = "Language code (e.g. en, zh)")]
95
+ pub language: Option<String>,
96
+ }
@@ -0,0 +1,135 @@
1
+ use anyhow::Context;
2
+ use serde::{Deserialize, Serialize};
3
+ use std::path::{Path, PathBuf};
4
+
5
+ /// Top-level configuration for `lincoln-record`.
6
+ #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
7
+ pub struct Config {
8
+ #[serde(default)]
9
+ pub audio: AudioConfig,
10
+ #[serde(default)]
11
+ pub transcription: TranscriptionConfig,
12
+ #[serde(default)]
13
+ pub diarization: DiarizationConfig,
14
+ #[serde(default)]
15
+ pub output: OutputConfig,
16
+ #[serde(default)]
17
+ pub model_cache_dir: Option<std::path::PathBuf>,
18
+ }
19
+
20
+ /// Audio capture and pipeline settings.
21
+ #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
22
+ pub struct AudioConfig {
23
+ #[serde(default = "default_sample_rate")]
24
+ pub sample_rate: u32,
25
+ #[serde(default = "default_channels")]
26
+ pub channels: u16,
27
+ }
28
+
29
+ /// Transcription engine settings.
30
+ #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
31
+ pub struct TranscriptionConfig {
32
+ #[serde(default = "default_engine")]
33
+ pub engine: String,
34
+ #[serde(default = "default_model")]
35
+ pub model: String,
36
+ #[serde(default = "default_language")]
37
+ pub language: String,
38
+ }
39
+
40
+ /// Speaker diarization settings.
41
+ #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
42
+ pub struct DiarizationConfig {
43
+ #[serde(default)]
44
+ pub enabled: bool,
45
+ #[serde(default)]
46
+ pub hf_token: String,
47
+ }
48
+
49
+ /// Output formatting and retention settings.
50
+ #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
51
+ pub struct OutputConfig {
52
+ #[serde(default = "default_output_format")]
53
+ pub format: String,
54
+ #[serde(default = "default_keep_recording")]
55
+ pub keep_recording: bool,
56
+ }
57
+
58
+ impl Default for AudioConfig {
59
+ fn default() -> Self {
60
+ Self {
61
+ sample_rate: 48000,
62
+ channels: 2,
63
+ }
64
+ }
65
+ }
66
+
67
+ impl Default for TranscriptionConfig {
68
+ fn default() -> Self {
69
+ Self {
70
+ engine: default_engine(),
71
+ model: default_model(),
72
+ language: default_language(),
73
+ }
74
+ }
75
+ }
76
+
77
+ impl Default for OutputConfig {
78
+ fn default() -> Self {
79
+ Self {
80
+ format: "markdown".to_string(),
81
+ keep_recording: true,
82
+ }
83
+ }
84
+ }
85
+
86
+ impl Config {
87
+ /// Load configuration from `path`. Returns defaults if the file is missing.
88
+ pub fn load_from_path<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> {
89
+ let path = path.as_ref();
90
+ if !path.exists() {
91
+ return Ok(Self::default());
92
+ }
93
+ let contents = std::fs::read_to_string(path)
94
+ .with_context(|| format!("failed to read config: {}", path.display()))?;
95
+ let config: Config = toml::from_str(&contents)
96
+ .with_context(|| format!("failed to parse config: {}", path.display()))?;
97
+ Ok(config)
98
+ }
99
+
100
+ /// Load configuration from `~/.lincolnrc`, falling back to defaults.
101
+ pub fn load() -> anyhow::Result<Self> {
102
+ let path = dirs::home_dir()
103
+ .map(|home| home.join(".lincolnrc"))
104
+ .unwrap_or_else(|| PathBuf::from(".lincolnrc"));
105
+ Self::load_from_path(path)
106
+ }
107
+ }
108
+
109
+ fn default_sample_rate() -> u32 {
110
+ 48000
111
+ }
112
+
113
+ fn default_channels() -> u16 {
114
+ 2
115
+ }
116
+
117
+ fn default_engine() -> String {
118
+ "whisper".to_string()
119
+ }
120
+
121
+ fn default_model() -> String {
122
+ "large-v3".to_string()
123
+ }
124
+
125
+ fn default_language() -> String {
126
+ "auto".to_string()
127
+ }
128
+
129
+ fn default_output_format() -> String {
130
+ "markdown".to_string()
131
+ }
132
+
133
+ fn default_keep_recording() -> bool {
134
+ true
135
+ }
@@ -0,0 +1,6 @@
1
+ pub mod audio;
2
+ pub mod cli;
3
+ pub mod config;
4
+ pub mod model;
5
+ pub mod output;
6
+ pub mod transcription;
@@ -0,0 +1,242 @@
1
+ use anyhow::Context;
2
+ use clap::Parser;
3
+ use lincoln_record::audio::capture::microphone::MicrophoneSource;
4
+ use lincoln_record::audio::devices::{
5
+ default_input_device, list_input_devices, log_device_enumeration_error,
6
+ };
7
+ use lincoln_record::audio::saver::write_source_to_wav;
8
+ use lincoln_record::audio::source::AudioSource;
9
+ use lincoln_record::cli::{Cli, RecordArgs, TranscribeArgs, WarmupArgs};
10
+ use lincoln_record::config::Config;
11
+ use lincoln_record::model::{default_cache_dir, download_model, resolve_model_path};
12
+ use lincoln_record::output::metadata::{DeviceInfo, FileInfo, SessionMetadata};
13
+ use lincoln_record::output::transcript::format_transcript;
14
+ use lincoln_record::transcription::diarization::diarize_and_merge;
15
+ use lincoln_record::transcription::provider::{
16
+ MockProvider, TranscriptSegment, TranscriptionProvider,
17
+ };
18
+ use lincoln_record::transcription::whisper::WhisperProvider;
19
+ use std::path::PathBuf;
20
+
21
+ #[tokio::main]
22
+ async fn main() {
23
+ env_logger::init();
24
+
25
+ let config = match Config::load() {
26
+ Ok(config) => config,
27
+ Err(error) => {
28
+ log::error!("failed to load config: {:#}", error);
29
+ std::process::exit(1);
30
+ }
31
+ };
32
+
33
+ let result = match Cli::parse() {
34
+ Cli::Devices => {
35
+ run_devices();
36
+ Ok(())
37
+ }
38
+ Cli::Record(args) => run_record(args).await,
39
+ Cli::Transcribe(args) => run_transcribe(args, &config).await,
40
+ Cli::Warmup(args) => run_warmup(args, &config),
41
+ other => {
42
+ eprintln!("{:?} is not yet implemented", other);
43
+ std::process::exit(1);
44
+ }
45
+ };
46
+
47
+ if let Err(error) = result {
48
+ log::error!("{:#}", error);
49
+ std::process::exit(1);
50
+ }
51
+ }
52
+
53
+ fn run_devices() {
54
+ match default_input_device() {
55
+ Some(name) => println!("Default input: {}", name),
56
+ None => eprintln!("No default input device found"),
57
+ }
58
+ match list_input_devices() {
59
+ Ok(devices) => {
60
+ for name in devices {
61
+ println!(" {}", name);
62
+ }
63
+ }
64
+ Err(error) => log_device_enumeration_error(&error),
65
+ }
66
+ }
67
+
68
+ async fn run_record(args: RecordArgs) -> anyhow::Result<()> {
69
+ validate_session_id(&args.session_id)?;
70
+
71
+ let output_dir = args.output.unwrap_or_else(|| PathBuf::from("."));
72
+ let session_dir = output_dir.join(&args.session_id);
73
+ tokio::fs::create_dir_all(&session_dir).await?;
74
+
75
+ let output_path = session_dir.join("audio.wav");
76
+ let duration = args.duration.unwrap_or(30.0);
77
+ let mic = args.mic;
78
+
79
+ let output_path_clone = output_path.clone();
80
+ let source = MicrophoneSource::new(mic.as_deref(), duration)?;
81
+ let stop_handle = source.stop_handle();
82
+ let mut handle = tokio::task::spawn_blocking(move || {
83
+ write_source_to_wav(
84
+ &source,
85
+ &output_path_clone,
86
+ source.sample_rate(),
87
+ source.channels(),
88
+ )
89
+ });
90
+
91
+ tokio::select! {
92
+ _ = tokio::signal::ctrl_c() => {
93
+ log::info!("received stop signal, shutting down recording");
94
+ stop_handle.stop();
95
+ (&mut handle).await??;
96
+ }
97
+ result = &mut handle => {
98
+ result??;
99
+ }
100
+ }
101
+
102
+ println!("Wrote {}", output_path.display());
103
+ Ok(())
104
+ }
105
+
106
+ async fn run_transcribe(args: TranscribeArgs, config: &Config) -> anyhow::Result<()> {
107
+ let session_id = args.session_id.unwrap_or_else(|| {
108
+ args.path
109
+ .file_stem()
110
+ .unwrap_or_default()
111
+ .to_string_lossy()
112
+ .to_string()
113
+ });
114
+ validate_session_id(&session_id)?;
115
+
116
+ let output_dir = args.output.unwrap_or_else(|| PathBuf::from("."));
117
+ let session_dir = output_dir.join(&session_id);
118
+ tokio::fs::create_dir_all(&session_dir).await?;
119
+
120
+ let provider: Box<dyn TranscriptionProvider + Send + Sync> = match args.engine.as_str() {
121
+ "mock" => Box::new(MockProvider::new(vec![TranscriptSegment {
122
+ start: 0.0,
123
+ end: 1.0,
124
+ speaker: None,
125
+ text: "Mock transcription output".to_string(),
126
+ }])),
127
+ "whisper" => {
128
+ let model_arg = args.model.as_ref().map(|s| std::path::Path::new(s.as_str()));
129
+ let model_path = resolve_model_path(model_arg, config)?;
130
+ Box::new(WhisperProvider::new(
131
+ model_path,
132
+ language_choice(args.language.as_deref(), config.transcription.language.as_str()),
133
+ ))
134
+ }
135
+ other => anyhow::bail!(
136
+ "transcription engine '{}' is not yet implemented; use 'mock' or 'whisper'",
137
+ other
138
+ ),
139
+ };
140
+
141
+ let audio_path = args.path.clone();
142
+ let language = args.language.clone();
143
+ let diarize = args.diarize;
144
+ let diarize_work_dir = session_dir.clone();
145
+ let segments = tokio::task::spawn_blocking(move || {
146
+ let mut segs = provider.transcribe(&audio_path, language.as_deref())?;
147
+ if diarize {
148
+ segs = diarize_and_merge(&audio_path, &segs, &diarize_work_dir)?;
149
+ }
150
+ Ok::<_, anyhow::Error>(segs)
151
+ })
152
+ .await??;
153
+
154
+ let transcript = format_transcript(&session_id, &segments);
155
+ tokio::fs::write(session_dir.join("transcript.md"), transcript).await?;
156
+
157
+ let metadata = SessionMetadata {
158
+ session_id: session_id.clone(),
159
+ process_slug: "issue-25".to_string(),
160
+ started_at: chrono::Utc::now().to_rfc3339(),
161
+ ended_at: Some(chrono::Utc::now().to_rfc3339()),
162
+ engine: args.engine,
163
+ model: args.model.unwrap_or_else(|| config.transcription.model.clone()),
164
+ diarization: args.diarize,
165
+ devices: DeviceInfo {
166
+ microphone: "unknown".to_string(),
167
+ system: None,
168
+ },
169
+ files: FileInfo {
170
+ transcript: "transcript.md".to_string(),
171
+ audio: args
172
+ .path
173
+ .file_name()
174
+ .unwrap_or_default()
175
+ .to_string_lossy()
176
+ .to_string(),
177
+ metadata: "metadata.json".to_string(),
178
+ },
179
+ };
180
+ tokio::fs::write(
181
+ session_dir.join("metadata.json"),
182
+ serde_json::to_string_pretty(&metadata)?,
183
+ )
184
+ .await?;
185
+
186
+ println!("Wrote transcript and metadata to {}", session_dir.display());
187
+ Ok(())
188
+ }
189
+
190
+ fn run_warmup(args: WarmupArgs, config: &Config) -> anyhow::Result<()> {
191
+ let model_name = args
192
+ .model
193
+ .as_deref()
194
+ .unwrap_or(config.transcription.model.as_str());
195
+ let engine = args
196
+ .engine
197
+ .as_deref()
198
+ .unwrap_or(config.transcription.engine.as_str());
199
+ let cache_dir = args
200
+ .cache_dir
201
+ .clone()
202
+ .or_else(|| config.model_cache_dir.clone())
203
+ .unwrap_or_else(default_cache_dir);
204
+
205
+ eprintln!("Warming up {} model '{}' into {}...", engine, model_name, cache_dir.display());
206
+
207
+ let path = download_model(model_name, engine, &cache_dir, |downloaded, total| {
208
+ match total {
209
+ Some(t) => {
210
+ let pct = (downloaded as f64 / t as f64) * 100.0;
211
+ eprint!("\r downloaded {} / {} bytes ({:.1}%)", downloaded, t, pct);
212
+ }
213
+ None => {
214
+ eprint!("\r downloaded {} bytes", downloaded);
215
+ }
216
+ }
217
+ })
218
+ .context("model warmup failed")?;
219
+
220
+ eprintln!();
221
+ println!("Model ready at {}", path.display());
222
+ Ok(())
223
+ }
224
+
225
+ fn language_choice(cli: Option<&str>, config: &str) -> Option<String> {
226
+ cli.filter(|l| *l != "auto")
227
+ .or_else(|| if config == "auto" { None } else { Some(config) })
228
+ .map(|s| s.to_string())
229
+ }
230
+
231
+ fn validate_session_id(session_id: &str) -> anyhow::Result<()> {
232
+ if session_id.is_empty() {
233
+ anyhow::bail!("session-id must not be empty");
234
+ }
235
+ if session_id.len() > 128 {
236
+ anyhow::bail!("session-id must be 128 characters or fewer");
237
+ }
238
+ if session_id.contains('/') || session_id.contains('\\') || session_id.contains("..") {
239
+ anyhow::bail!("session-id must not contain path separators or parent references");
240
+ }
241
+ Ok(())
242
+ }