@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,48 @@
1
+ {
2
+ "name": "@sushanglewis/lincoln-recorder",
3
+ "version": "1.5.2",
4
+ "description": "Cross-tool terminal UI for recording Lincoln interviews",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "lincoln-record": "./dist/main.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "typecheck": "tsc --noEmit",
21
+ "typecheck:tests": "tsc -p tsconfig.tests.json",
22
+ "lint": "echo 'lint not configured'"
23
+ },
24
+ "dependencies": {
25
+ "ink": "^5.2.0",
26
+ "js-yaml": "^4.1.0",
27
+ "react": "^18.3.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/js-yaml": "^4.0.9",
31
+ "@types/node": "^22.15.0",
32
+ "@types/react": "^18.3.12",
33
+ "ink-testing-library": "^4.0.0",
34
+ "tsup": "^8.5.0",
35
+ "typescript": "^5.8.3",
36
+ "vitest": "^4.1.9"
37
+ },
38
+ "engines": {
39
+ "node": ">=20.0.0"
40
+ },
41
+ "keywords": [
42
+ "lincoln",
43
+ "interview",
44
+ "recording",
45
+ "tui"
46
+ ],
47
+ "license": "MIT"
48
+ }
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { Box, Text } from 'ink'
3
+ import React from 'react'
4
+
5
+ import { parseArgs, type ParsedArgs } from './config/args'
6
+ import { resolveConfig, type ResolvedConfig } from './config/resolveConfig'
7
+ import { RecordingApp } from './components/RecordingApp'
8
+
9
+ export interface AppProps {
10
+ args: ParsedArgs
11
+ config: ResolvedConfig
12
+ }
13
+
14
+ export function App({ args, config }: AppProps) {
15
+ if (args.help) {
16
+ return (
17
+ <Box flexDirection="column" padding={1}>
18
+ <Text bold>lincoln-record</Text>
19
+ <Text>Cross-tool TUI for Lincoln interview recording.</Text>
20
+ <Text />
21
+ <Text bold>Usage:</Text>
22
+ <Text> lincoln-record [options]</Text>
23
+ <Text />
24
+ <Text bold>Options:</Text>
25
+ <Text> --topic Interview topic</Text>
26
+ <Text> --design-id Design ID for the session</Text>
27
+ <Text> --branch Current branch name</Text>
28
+ <Text> --session-id Explicit session ID</Text>
29
+ <Text> --no-tui Run without the terminal UI</Text>
30
+ <Text> --help, -h Show this help message</Text>
31
+ <Text />
32
+ <Text dimColor>Configuration files: ~/.lincolnrc, ./.lincolnrc</Text>
33
+ </Box>
34
+ )
35
+ }
36
+
37
+ return (
38
+ <RecordingApp
39
+ workspaceRoot={config.workspaceRoot}
40
+ sessionId={config.sessionId}
41
+ topic={config.topic}
42
+ designId={config.designId}
43
+ branch={config.branch}
44
+ audioMeterStyle={config.audioMeterStyle}
45
+ />
46
+ )
47
+ }
48
+
49
+ export async function main(argv: string[] = process.argv.slice(2)): Promise<void> {
50
+ const args = parseArgs(argv)
51
+ const config = resolveConfig(args)
52
+
53
+ if (args.noTui) {
54
+ console.log(JSON.stringify(config, null, 2))
55
+ return
56
+ }
57
+
58
+ const { render } = await import('ink')
59
+ render(<App args={args} config={config} />)
60
+ }
@@ -0,0 +1,34 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+
4
+ import { amplitudeToMeter } from '../utils/amplitudeToMeter'
5
+
6
+ export interface AudioMeterProps {
7
+ amplitude: number
8
+ style: 'bar' | 'dot' | 'wave'
9
+ }
10
+
11
+ const BLOCKS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█']
12
+
13
+ export function AudioMeter({ amplitude, style }: AudioMeterProps) {
14
+ const bars = amplitudeToMeter(amplitude, 16)
15
+
16
+ if (style === 'bar') {
17
+ const filled = '█'.repeat(bars)
18
+ const empty = '░'.repeat(16 - bars)
19
+ return (<Text>{filled + empty}</Text>)
20
+ }
21
+
22
+ if (style === 'dot') {
23
+ const filled = '●'.repeat(bars)
24
+ const empty = '○'.repeat(16 - bars)
25
+ return (<Text>{filled + empty}</Text>)
26
+ }
27
+
28
+ // wave
29
+ const wave = BLOCKS.slice(0, Math.max(1, Math.ceil(amplitude * BLOCKS.length)))
30
+ .join('')
31
+ .repeat(2)
32
+ .slice(0, 16)
33
+ return (<Text>{wave.padEnd(16, '~')}</Text>)
34
+ }
@@ -0,0 +1,11 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+
4
+ export function CancelledScreen() {
5
+ return (
6
+ <Box flexDirection="column" padding={1}>
7
+ <Text bold color="yellow">Recording cancelled</Text>
8
+ <Text>No files were saved.</Text>
9
+ </Box>
10
+ )
11
+ }
@@ -0,0 +1,79 @@
1
+ import { Box, Text, useInput } from 'ink'
2
+ import React, { useEffect, useState } from 'react'
3
+
4
+ import { SelectMenu } from './SelectMenu'
5
+ import type { DeviceList } from '../devices/parseDevices'
6
+
7
+ export interface DevicesMenuProps {
8
+ listDevicesFn: () => Promise<DeviceList>
9
+ onSelect: (device: string) => void
10
+ onClose: () => void
11
+ }
12
+
13
+ type LoadState =
14
+ | { status: 'loading' }
15
+ | { status: 'loaded'; list: DeviceList }
16
+ | { status: 'error'; message: string }
17
+
18
+ export function DevicesMenu({ listDevicesFn, onSelect, onClose }: DevicesMenuProps) {
19
+ const [loadState, setLoadState] = useState<LoadState>({ status: 'loading' })
20
+
21
+ useEffect(() => {
22
+ let cancelled = false
23
+ listDevicesFn()
24
+ .then(list => {
25
+ if (!cancelled) setLoadState({ status: 'loaded', list })
26
+ })
27
+ .catch(error => {
28
+ if (!cancelled) {
29
+ setLoadState({
30
+ status: 'error',
31
+ message: error instanceof Error ? error.message : String(error),
32
+ })
33
+ }
34
+ })
35
+ return () => {
36
+ cancelled = true
37
+ }
38
+ }, [listDevicesFn])
39
+
40
+ useInput(
41
+ (_input, key) => {
42
+ if (key.escape) onClose()
43
+ },
44
+ { isActive: loadState.status !== 'loaded' },
45
+ )
46
+
47
+ if (loadState.status === 'loading') {
48
+ return (
49
+ <Box paddingX={1}>
50
+ <Text dimColor>Loading devices…</Text>
51
+ </Box>
52
+ )
53
+ }
54
+
55
+ if (loadState.status === 'error') {
56
+ return (
57
+ <Box flexDirection="column" paddingX={1}>
58
+ <Text color="red">Failed to list devices: {loadState.message}</Text>
59
+ <Text dimColor>[esc] back</Text>
60
+ </Box>
61
+ )
62
+ }
63
+
64
+ const { list } = loadState
65
+ const items = list.devices.map(name => ({
66
+ key: name,
67
+ label: name === list.defaultDevice ? `${name} (default)` : name,
68
+ }))
69
+
70
+ return (
71
+ <SelectMenu
72
+ title="Devices"
73
+ items={items}
74
+ onSelect={onSelect}
75
+ onClose={onClose}
76
+ footer="[↑↓] select [enter] confirm [esc] back"
77
+ />
78
+ )
79
+ }
@@ -0,0 +1,25 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+
4
+ export type HintKey = 'idle' | 'recording' | 'stopped' | 'cancelled' | 'error' | 'menu'
5
+
6
+ export interface HintBarProps {
7
+ mode: HintKey
8
+ }
9
+
10
+ const HINTS: Record<HintKey, string> = {
11
+ idle: '[r] record [d] devices [m] model [q] quit',
12
+ recording: '[s] stop [c] cancel',
13
+ stopped: '[any key] exit',
14
+ cancelled: '[q] exit',
15
+ error: '[q] exit',
16
+ menu: '[↑↓] select [enter] confirm [esc] back',
17
+ }
18
+
19
+ export function HintBar({ mode }: HintBarProps) {
20
+ return (
21
+ <Box paddingX={1} paddingY={1}>
22
+ <Text dimColor>{HINTS[mode]}</Text>
23
+ </Box>
24
+ )
25
+ }
@@ -0,0 +1,36 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+
4
+ export interface LogEntry {
5
+ id: string
6
+ message: string
7
+ type?: 'info' | 'success' | 'error' | 'command'
8
+ }
9
+
10
+ export interface LogViewProps {
11
+ logs: LogEntry[]
12
+ }
13
+
14
+ const TYPE_COLORS: Record<string, string | undefined> = {
15
+ info: undefined,
16
+ success: 'green',
17
+ error: 'red',
18
+ command: 'cyan',
19
+ }
20
+
21
+ export function LogView({ logs }: LogViewProps) {
22
+ const visible = logs.slice(-8)
23
+
24
+ return (
25
+ <Box flexDirection="column" paddingX={1} flexGrow={1}>
26
+ <Text underline dimColor>Log</Text>
27
+ {visible.length === 0 ? (
28
+ <Text dimColor>No events yet.</Text>
29
+ ) : (
30
+ visible.map(log => (
31
+ <Text key={log.id} color={TYPE_COLORS[log.type ?? 'info']}>{log.message}</Text>
32
+ ))
33
+ )}
34
+ </Box>
35
+ )
36
+ }
@@ -0,0 +1,122 @@
1
+ import { Box, Text, useInput } from 'ink'
2
+ import React, { useEffect, useState } from 'react'
3
+
4
+ import { SelectMenu } from './SelectMenu'
5
+ import type { ModelStatus } from '../models/models'
6
+ import type { WarmupProgress } from '../models/warmupProgress'
7
+
8
+ export interface ModelMenuProps {
9
+ listModelStatusesFn: () => Promise<ModelStatus[]>
10
+ runWarmupFn: (model: string, onProgress: (p: WarmupProgress) => void) => Promise<string>
11
+ onSelect: (model: string) => void
12
+ onClose: () => void
13
+ }
14
+
15
+ type MenuState =
16
+ | { status: 'loading' }
17
+ | { status: 'loaded'; models: ModelStatus[] }
18
+ | { status: 'downloading'; model: string; percent: number | null; downloaded: number }
19
+ | { status: 'error'; message: string }
20
+
21
+ export function ModelMenu({ listModelStatusesFn, runWarmupFn, onSelect, onClose }: ModelMenuProps) {
22
+ const [menuState, setMenuState] = useState<MenuState>({ status: 'loading' })
23
+
24
+ useEffect(() => {
25
+ let cancelled = false
26
+ listModelStatusesFn()
27
+ .then(models => {
28
+ if (!cancelled) setMenuState({ status: 'loaded', models })
29
+ })
30
+ .catch(error => {
31
+ if (!cancelled) {
32
+ setMenuState({
33
+ status: 'error',
34
+ message: error instanceof Error ? error.message : String(error),
35
+ })
36
+ }
37
+ })
38
+ return () => {
39
+ cancelled = true
40
+ }
41
+ }, [listModelStatusesFn])
42
+
43
+ useInput(
44
+ (_input, key) => {
45
+ if (key.escape) onClose()
46
+ },
47
+ { isActive: menuState.status === 'error' },
48
+ )
49
+
50
+ const handleSelect = (name: string) => {
51
+ if (menuState.status !== 'loaded') return
52
+ const target = menuState.models.find(m => m.name === name)
53
+ if (!target) return
54
+
55
+ if (target.cached) {
56
+ onSelect(name)
57
+ return
58
+ }
59
+
60
+ setMenuState({ status: 'downloading', model: name, percent: null, downloaded: 0 })
61
+ runWarmupFn(name, progress => {
62
+ setMenuState({
63
+ status: 'downloading',
64
+ model: name,
65
+ percent: progress.percent,
66
+ downloaded: progress.downloaded,
67
+ })
68
+ })
69
+ .then(() => onSelect(name))
70
+ .catch(error => {
71
+ setMenuState({
72
+ status: 'error',
73
+ message: error instanceof Error ? error.message : String(error),
74
+ })
75
+ })
76
+ }
77
+
78
+ if (menuState.status === 'loading') {
79
+ return (
80
+ <Box paddingX={1}>
81
+ <Text dimColor>Loading models…</Text>
82
+ </Box>
83
+ )
84
+ }
85
+
86
+ if (menuState.status === 'error') {
87
+ return (
88
+ <Box flexDirection="column" paddingX={1}>
89
+ <Text color="red">{menuState.message}</Text>
90
+ <Text dimColor>[esc] back</Text>
91
+ </Box>
92
+ )
93
+ }
94
+
95
+ if (menuState.status === 'downloading') {
96
+ const progressText =
97
+ menuState.percent !== null
98
+ ? `${menuState.percent.toFixed(1)}%`
99
+ : `${menuState.downloaded} bytes`
100
+ return (
101
+ <Box flexDirection="column" paddingX={1}>
102
+ <Text>Downloading model '{menuState.model}'… {progressText}</Text>
103
+ <Text dimColor>please wait</Text>
104
+ </Box>
105
+ )
106
+ }
107
+
108
+ const items = menuState.models.map(m => ({
109
+ key: m.name,
110
+ label: m.cached ? `✓ ${m.name}` : `· ${m.name} (not downloaded)`,
111
+ }))
112
+
113
+ return (
114
+ <SelectMenu
115
+ title="Model"
116
+ items={items}
117
+ onSelect={handleSelect}
118
+ onClose={onClose}
119
+ footer="[↑↓] select [enter] confirm [esc] back"
120
+ />
121
+ )
122
+ }
@@ -0,0 +1,54 @@
1
+ import { Box, Text } from 'ink'
2
+ import React from 'react'
3
+
4
+ export interface ReadyScreenProps {
5
+ sessionId: string
6
+ topic: string
7
+ designId: string
8
+ branch: string
9
+ selectedIndex?: number
10
+ }
11
+
12
+ const OPTIONS = [
13
+ { label: '开始录音', hint: 'Enter' },
14
+ { label: '退出', hint: 'q / Esc' },
15
+ ]
16
+
17
+ export function ReadyScreen({ sessionId, topic, designId, branch, selectedIndex = 0 }: ReadyScreenProps) {
18
+ return (
19
+ <Box flexDirection="column" borderStyle="round" borderColor="gray" padding={1}>
20
+ <Box flexDirection="row" justifyContent="space-between" alignItems="center">
21
+ <Box gap={1}>
22
+ <Text color="red">●</Text>
23
+ <Text color="yellow">●</Text>
24
+ <Text color="green">●</Text>
25
+ </Box>
26
+ <Text bold color="white">Lincoln Recorder</Text>
27
+ </Box>
28
+
29
+ <Box flexDirection="column" paddingY={1}>
30
+ <Text color="gray">Session: {sessionId}</Text>
31
+ {topic ? <Text color="gray">Topic: {topic}</Text> : null}
32
+ {designId ? <Text color="gray">Design: {designId}</Text> : null}
33
+ {branch ? <Text color="gray">Branch: {branch}</Text> : null}
34
+ </Box>
35
+
36
+ <Box flexDirection="column" paddingY={1} gap={1}>
37
+ {OPTIONS.map((option, index) => {
38
+ const isSelected = index === selectedIndex
39
+ return (
40
+ <Box key={option.label} flexDirection="row" gap={1}>
41
+ <Text color={isSelected ? 'green' : 'gray'}>{isSelected ? '▸' : ' '}</Text>
42
+ <Text bold={isSelected} color={isSelected ? 'green' : 'gray'} dimColor={!isSelected}>
43
+ {option.label}
44
+ </Text>
45
+ <Text color="gray" dimColor>
46
+ [{option.hint}]
47
+ </Text>
48
+ </Box>
49
+ )
50
+ })}
51
+ </Box>
52
+ </Box>
53
+ )
54
+ }
@@ -0,0 +1,211 @@
1
+ import { Box, useApp } from 'ink'
2
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
3
+
4
+ import { StatusPanel } from './StatusPanel'
5
+ import { LogView, type LogEntry } from './LogView'
6
+ import { HintBar } from './HintBar'
7
+ import { DevicesMenu } from './DevicesMenu'
8
+ import { ModelMenu } from './ModelMenu'
9
+ import { useKeyHandler } from '../hooks/useKeyHandler'
10
+ import { useRecorder } from '../recording/useRecorder'
11
+ import { listDevices } from '../devices/listDevices'
12
+ import { defaultModelCacheDir, listModelStatuses, type ModelStatus } from '../models/models'
13
+ import { runWarmup } from '../models/runWarmup'
14
+ import type { WarmupProgress } from '../models/warmupProgress'
15
+ import type { DeviceList } from '../devices/parseDevices'
16
+
17
+ type AppMode = 'main' | 'devices' | 'model'
18
+
19
+ export interface RecordingAppProps {
20
+ workspaceRoot: string
21
+ sessionId: string
22
+ topic: string
23
+ designId: string
24
+ branch: string
25
+ audioMeterStyle?: 'bar' | 'dot' | 'wave'
26
+ lincolnRecordPath?: string
27
+ listDevicesFn?: () => Promise<DeviceList>
28
+ listModelStatusesFn?: () => Promise<ModelStatus[]>
29
+ runWarmupFn?: (model: string, onProgress: (p: WarmupProgress) => void) => Promise<string>
30
+ }
31
+
32
+ function createLogId() {
33
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
34
+ }
35
+
36
+ export function RecordingApp({
37
+ workspaceRoot,
38
+ sessionId,
39
+ topic,
40
+ designId,
41
+ branch,
42
+ audioMeterStyle = 'bar',
43
+ lincolnRecordPath,
44
+ listDevicesFn,
45
+ listModelStatusesFn,
46
+ runWarmupFn,
47
+ }: RecordingAppProps) {
48
+ const { exit } = useApp()
49
+ const [logs, setLogs] = useState<LogEntry[]>([])
50
+ const [mode, setMode] = useState<AppMode>('main')
51
+ const [mic, setMic] = useState<string | null>(null)
52
+ const [model, setModel] = useState<string | null>(null)
53
+
54
+ const addLog = useCallback((message: string, type: LogEntry['type'] = 'info') => {
55
+ setLogs(prev => [...prev, { id: createLogId(), message, type }])
56
+ }, [])
57
+
58
+ const { state, start, stop, cancel } = useRecorder({
59
+ workspaceRoot,
60
+ sessionId,
61
+ lincolnRecordPath,
62
+ mic,
63
+ model,
64
+ startOnMount: false,
65
+ })
66
+
67
+ useEffect(() => {
68
+ switch (state.status) {
69
+ case 'recording':
70
+ addLog('Recording started.', 'info')
71
+ break
72
+ case 'stopped':
73
+ addLog(`Saved to ${workspaceRoot}/${sessionId}/audio.wav`, 'success')
74
+ addLog(`Run: claude process-interview ${sessionId}`, 'command')
75
+ break
76
+ case 'cancelled':
77
+ addLog('Recording cancelled. No files were saved.', 'error')
78
+ break
79
+ case 'error':
80
+ addLog(`Recording error: ${state.errorMessage ?? 'unknown error'}`, 'error')
81
+ break
82
+ }
83
+ }, [state.status, state.errorMessage, addLog, workspaceRoot, sessionId])
84
+
85
+ const handleQuit = useCallback(() => {
86
+ exit()
87
+ }, [exit])
88
+
89
+ const handleRecord = useCallback(() => {
90
+ if (state.status === 'idle') {
91
+ start()
92
+ }
93
+ }, [state.status, start])
94
+
95
+ const handleStop = useCallback(() => {
96
+ if (state.status === 'recording') {
97
+ stop().catch(() => {})
98
+ }
99
+ }, [state.status, stop])
100
+
101
+ const handleCancel = useCallback(() => {
102
+ if (state.status === 'recording') {
103
+ cancel().catch(() => {})
104
+ }
105
+ }, [state.status, cancel])
106
+
107
+ const handleDevices = useCallback(() => {
108
+ if (state.status === 'idle') {
109
+ setMode('devices')
110
+ }
111
+ }, [state.status])
112
+
113
+ const handleModel = useCallback(() => {
114
+ if (state.status === 'idle') {
115
+ setMode('model')
116
+ }
117
+ }, [state.status])
118
+
119
+ const handleDeviceSelect = useCallback(
120
+ (device: string) => {
121
+ setMic(device)
122
+ addLog(`Input device: ${device}`, 'success')
123
+ setMode('main')
124
+ },
125
+ [addLog],
126
+ )
127
+
128
+ const handleModelSelect = useCallback(
129
+ (name: string) => {
130
+ setModel(name)
131
+ addLog(`Model: ${name}`, 'success')
132
+ setMode('main')
133
+ },
134
+ [addLog],
135
+ )
136
+
137
+ const handleMenuClose = useCallback(() => {
138
+ setMode('main')
139
+ }, [])
140
+
141
+ const isTerminal = state.status === 'stopped' || state.status === 'cancelled' || state.status === 'error'
142
+
143
+ useKeyHandler({
144
+ enabled: mode === 'main',
145
+ onRecord: handleRecord,
146
+ onStop: handleStop,
147
+ onCancel: handleCancel,
148
+ onQuit: handleQuit,
149
+ onDevices: handleDevices,
150
+ onModel: handleModel,
151
+ onAnyKey: isTerminal ? handleQuit : undefined,
152
+ })
153
+
154
+ const hintMode = useMemo(() => {
155
+ if (mode !== 'main') return 'menu'
156
+ if (state.status === 'idle') return 'idle'
157
+ if (state.status === 'recording') return 'recording'
158
+ if (state.status === 'stopped') return 'stopped'
159
+ if (state.status === 'cancelled') return 'cancelled'
160
+ return 'error'
161
+ }, [mode, state.status])
162
+
163
+ const devicesLoader = useCallback(
164
+ () => (listDevicesFn ?? (() => listDevices(lincolnRecordPath)))(),
165
+ [listDevicesFn, lincolnRecordPath],
166
+ )
167
+
168
+ const modelsLoader = useCallback(
169
+ () => (listModelStatusesFn ?? (() => Promise.resolve(listModelStatuses(defaultModelCacheDir()))))(),
170
+ [listModelStatusesFn],
171
+ )
172
+
173
+ const warmupRunner = useCallback(
174
+ (name: string, onProgress: (p: WarmupProgress) => void) =>
175
+ runWarmupFn
176
+ ? runWarmupFn(name, onProgress)
177
+ : runWarmup({ model: name, lincolnRecordPath, onProgress }),
178
+ [runWarmupFn, lincolnRecordPath],
179
+ )
180
+
181
+ return (
182
+ <Box flexDirection="column" borderStyle="round" borderColor="gray" height={24}>
183
+ <StatusPanel
184
+ sessionId={sessionId}
185
+ topic={topic}
186
+ designId={designId}
187
+ branch={branch}
188
+ status={state.status}
189
+ duration={state.duration}
190
+ />
191
+ {mode === 'devices' && (
192
+ <DevicesMenu
193
+ listDevicesFn={devicesLoader}
194
+ onSelect={handleDeviceSelect}
195
+ onClose={handleMenuClose}
196
+ />
197
+ )}
198
+ {mode === 'model' && (
199
+ <ModelMenu
200
+ listModelStatusesFn={modelsLoader}
201
+ runWarmupFn={warmupRunner}
202
+ onSelect={handleModelSelect}
203
+ onClose={handleMenuClose}
204
+ />
205
+ )}
206
+ <LogView logs={logs} />
207
+ <Box flexGrow={1} />
208
+ <HintBar mode={hintMode} />
209
+ </Box>
210
+ )
211
+ }