@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,427 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # On-session-start hook for Lincoln workflow.
5
+ # Loads stage context, checks dependencies, parses <process_slug>/workflow-stage.yaml,
6
+ # reads previous node handoff, and injects current node driving context.
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ LOCAL_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
10
+ PROJECT_ROOT="$(pwd)"
11
+
12
+ FRAMEWORK_ROOT="${CLAUDE_PLUGIN_ROOT:-${LINCOLN_HOME:-}}"
13
+ if [[ -z "$FRAMEWORK_ROOT" ]]; then
14
+ # If this hook file lives inside a vendored project framework, prefer that.
15
+ if [[ "$SCRIPT_DIR" == "$LOCAL_ROOT/.claude/hooks"* && -d "$LOCAL_ROOT/.claude/stages" ]]; then
16
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
17
+ elif [[ -d "$HOME/.lincoln/current" ]]; then
18
+ FRAMEWORK_ROOT="$HOME/.lincoln/current"
19
+ else
20
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
21
+ fi
22
+ fi
23
+
24
+ if [[ "$FRAMEWORK_ROOT" != "$LOCAL_ROOT" ]]; then
25
+ # Global plugin mode: only activate if the project has opted in.
26
+ MARKER_FOUND="false"
27
+ SEARCH_DIR="$PROJECT_ROOT"
28
+ while [[ "$SEARCH_DIR" != "/" ]]; do
29
+ if [[ -f "$SEARCH_DIR/.lincoln.yaml" || -f "$SEARCH_DIR/.claude/workflow-state.yaml" ]]; then
30
+ MARKER_FOUND="true"
31
+ break
32
+ fi
33
+ SEARCH_DIR="$(dirname "$SEARCH_DIR")"
34
+ done
35
+ if [[ "$MARKER_FOUND" != "true" && -z "${LINCOLN_ALWAYS_ON:-}" ]]; then
36
+ echo "Lincoln inactive: no .lincoln.yaml in $PROJECT_ROOT"
37
+ exit 0
38
+ fi
39
+ fi
40
+
41
+ ROOT="$PROJECT_ROOT"
42
+
43
+ if [ -x "$FRAMEWORK_ROOT/.venv/bin/python3" ]; then
44
+ PYTHON="$FRAMEWORK_ROOT/.venv/bin/python3"
45
+ elif [ -x "$FRAMEWORK_ROOT/venv/bin/python3" ]; then
46
+ PYTHON="$FRAMEWORK_ROOT/venv/bin/python3"
47
+ elif [ -x "$HOME/.lincoln/venv/bin/python3" ]; then
48
+ PYTHON="$HOME/.lincoln/venv/bin/python3"
49
+ else
50
+ PYTHON="python3"
51
+ fi
52
+
53
+ STATE_FILE="${LINCOLN_STATE_FILE:-}"
54
+ LEGACY_STATE_FILE="$FRAMEWORK_ROOT/.claude/workflow-state.yaml"
55
+
56
+ # Runtime state captured for optional machine-readable JSON output
57
+ # (LINCOLN_SESSION_START_JSON=1). Updated at key decision points.
58
+ _LINCOLN_SETUP_COMPLETE="false"
59
+ _LINCOLN_HAS_STATE="false"
60
+ _LINCOLN_GUIDANCE_INJECTED="false"
61
+ _LINCOLN_CURRENT_STAGE="not_started"
62
+ _LINCOLN_STATUS="not_started"
63
+ _LINCOLN_WORKFLOW_TEMPLATE="unknown"
64
+ _LINCOLN_PROCESS_SLUG=""
65
+
66
+ # Capture the full hook stdout so we can quantify the session-start token cost (#63).
67
+ HOOK_OUTPUT_CAPTURE="$(mktemp)"
68
+ trap 'rm -f "$HOOK_OUTPUT_CAPTURE"' EXIT
69
+ exec > >(tee "$HOOK_OUTPUT_CAPTURE")
70
+
71
+ emit_session_start_json() {
72
+ if [[ "${LINCOLN_SESSION_START_JSON:-}" != "1" ]]; then
73
+ return 0
74
+ fi
75
+ "$PYTHON" - "$_LINCOLN_SETUP_COMPLETE" "$_LINCOLN_HAS_STATE" "$_LINCOLN_GUIDANCE_INJECTED" "$_LINCOLN_CURRENT_STAGE" "$_LINCOLN_STATUS" "$_LINCOLN_WORKFLOW_TEMPLATE" "$_LINCOLN_PROCESS_SLUG" <<'PY'
76
+ import sys, json
77
+ setup_complete = sys.argv[1] == "true"
78
+ has_state = sys.argv[2] == "true"
79
+ guidance_injected = sys.argv[3] == "true"
80
+ current_stage = sys.argv[4]
81
+ status = sys.argv[5]
82
+ workflow_template = sys.argv[6]
83
+ process_slug = sys.argv[7]
84
+ print(json.dumps({
85
+ "schema_version": "1.0.0",
86
+ "has_state": has_state,
87
+ "guidance_injected": guidance_injected,
88
+ "setup_complete": setup_complete,
89
+ "current_stage": current_stage,
90
+ "status": status,
91
+ "workflow_template": workflow_template,
92
+ "process_slug": process_slug,
93
+ }))
94
+ PY
95
+ }
96
+
97
+ # Reset any stale task-tool burst counter from a previous session
98
+ rm -f "$ROOT/.context/task-tool-burst.json"
99
+
100
+ echo ""
101
+ echo "=== Lincoln Session Start ==="
102
+ echo ""
103
+
104
+ # 1. Dependency check + first-run prompt (LINCOLN_SKIP_DEP_CHECK=1 seals this off for tests)
105
+ if [[ "${LINCOLN_SKIP_DEP_CHECK:-}" != "1" && -f "$FRAMEWORK_ROOT/.claude/skills/dependencies.yaml" ]]; then
106
+ echo "[Lincoln] Checking dependencies..."
107
+ if ! "$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln-setup.py" check --root "$ROOT" > /dev/null 2>&1; then
108
+ SETUP_COMPLETE="false"
109
+ if [[ -f "$ROOT/.context/lc-setup-state.yaml" ]]; then
110
+ if "$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln-setup.py" is-setup-complete --root "$ROOT" > /dev/null 2>&1; then
111
+ SETUP_COMPLETE="true"
112
+ _LINCOLN_SETUP_COMPLETE="true"
113
+ fi
114
+ fi
115
+ if [[ "$SETUP_COMPLETE" != "true" ]]; then
116
+ echo ""
117
+ echo "Lincoln 依赖未就绪。请调用 lc-setup 技能完成一次性安装;安装完成前不要进行其他 Lincoln 工作流操作。"
118
+ echo ""
119
+ emit_session_start_json
120
+ exit 0
121
+ fi
122
+ else
123
+ _LINCOLN_SETUP_COMPLETE="true"
124
+ fi
125
+ echo ""
126
+ else
127
+ # Even when dependency check is skipped, surface setup completeness in the JSON shape.
128
+ if [[ -f "$ROOT/.context/lc-setup-state.yaml" ]]; then
129
+ if "$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln-setup.py" is-setup-complete --root "$ROOT" > /dev/null 2>&1; then
130
+ _LINCOLN_SETUP_COMPLETE="true"
131
+ fi
132
+ fi
133
+ fi
134
+
135
+ # Print the cold-start opening guidance block (fresh repo / unclear issue branch).
136
+ print_opening_guidance() {
137
+ _LINCOLN_GUIDANCE_INJECTED="true"
138
+ echo ""
139
+ echo "=== Lincoln 开场引导 ==="
140
+ echo ""
141
+ echo "Lincoln 当前没有可驱动的工作状态。请先完成 .claude/skills/lc-workflow-router/prompts/intake-prompt.md 中的摸排 → 判断 → 确认流程。"
142
+ echo "如需初始化 issue 工作包,由你(Agent)代为执行 scripts/init-lincoln-branch.sh,不让用户敲命令。"
143
+ echo ""
144
+ echo "=== End Lincoln 开场引导 ==="
145
+ echo ""
146
+ }
147
+
148
+ # 2. Determine state file (process package preferred, legacy fallback)
149
+ STATE_FILE=$("$PYTHON" - "$FRAMEWORK_ROOT" "$ROOT" "$STATE_FILE" <<'PY'
150
+ import sys
151
+ from pathlib import Path
152
+ framework_root = Path(sys.argv[1])
153
+ project_root = Path(sys.argv[2])
154
+ provided = sys.argv[3]
155
+ sys.path.insert(0, str(framework_root))
156
+ from scripts.lincoln_paths import resolve_state_path
157
+ path = Path(provided) if provided else None
158
+ print(resolve_state_path(path, project_root))
159
+ PY
160
+ )
161
+
162
+ CURRENT_BRANCH="$(git branch --show-current 2>/dev/null || true)"
163
+
164
+ if [[ ! -f "$STATE_FILE" ]]; then
165
+ echo "No Lincoln state file found ($STATE_FILE)."
166
+ if [[ "$CURRENT_BRANCH" == issue-* ]]; then
167
+ ISSUE_NUMBER="${CURRENT_BRANCH#issue-}"
168
+ # Strip any suffix after the issue number (e.g. issue-33-follow-up -> 33)
169
+ ISSUE_NUMBER="${ISSUE_NUMBER%%-*}"
170
+ if [[ "$ISSUE_NUMBER" =~ ^[0-9]+$ ]]; then
171
+ echo "Auto-initializing issue work package for branch $CURRENT_BRANCH..."
172
+ "$FRAMEWORK_ROOT/scripts/init-lincoln-branch.sh" --issue-number "$ISSUE_NUMBER" --no-commit --auto || {
173
+ echo "Failed to auto-initialize issue work package. Run manually:"
174
+ echo " scripts/init-lincoln-branch.sh --issue-number $ISSUE_NUMBER"
175
+ echo "=== End Lincoln Session Start ==="
176
+ echo ""
177
+ emit_session_start_json
178
+ exit 0
179
+ }
180
+ # Re-resolve state file after initialization
181
+ STATE_FILE=$("$PYTHON" - "$FRAMEWORK_ROOT" "$ROOT" "" <<'PY'
182
+ import sys
183
+ from pathlib import Path
184
+ framework_root = Path(sys.argv[1])
185
+ project_root = Path(sys.argv[2])
186
+ sys.path.insert(0, str(framework_root))
187
+ from scripts.lincoln_paths import resolve_state_path
188
+ print(resolve_state_path(None, project_root))
189
+ PY
190
+ )
191
+ else
192
+ echo "Current branch looks like an issue branch but issue number is unclear."
193
+ print_opening_guidance
194
+ echo "=== End Lincoln Session Start ==="
195
+ echo ""
196
+ emit_session_start_json
197
+ exit 0
198
+ fi
199
+ else
200
+ print_opening_guidance
201
+ echo "=== End Lincoln Session Start ==="
202
+ echo ""
203
+ emit_session_start_json
204
+ exit 0
205
+ fi
206
+ fi
207
+
208
+ _LINCOLN_HAS_STATE="true"
209
+
210
+ # 3. Read current stage and node info
211
+ STATE_JSON=$("$PYTHON" - "$STATE_FILE" <<'PY'
212
+ import sys, yaml, json
213
+ state = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
214
+ current = state.get("current_run", {}).get("current_stage") or "not_started"
215
+ status = state.get("current_run", {}).get("status") or "not_started"
216
+ workflow_name = state.get("workflow", {}).get("name") or "unknown"
217
+ workflow_template = state.get("workflow", {}).get("template") or workflow_name
218
+ nodes = state.get("nodes", [])
219
+ last_node = nodes[-1] if nodes else {}
220
+ waiting_for = last_node.get("status") if last_node else None
221
+ if waiting_for in ("completed", "merged"):
222
+ waiting_for = "next_stage"
223
+ variables = state.get("current_run", {}).get("variables", {})
224
+ process_slug = variables.get("process_slug", "")
225
+ if not process_slug and sys.argv[1].endswith("/workflow-stage.yaml"):
226
+ process_slug = __import__("pathlib").Path(sys.argv[1]).parent.name
227
+ print(json.dumps({
228
+ "current_stage": current,
229
+ "status": status,
230
+ "workflow_name": workflow_name,
231
+ "workflow_template": workflow_template,
232
+ "last_node": last_node,
233
+ "waiting_for": waiting_for,
234
+ "session_id": variables.get("session_id", ""),
235
+ "design_id": variables.get("design_id", ""),
236
+ "process_slug": process_slug,
237
+ }))
238
+ PY
239
+ )
240
+
241
+ CURRENT_STAGE=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['current_stage'])")
242
+ STATUS=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['status'])")
243
+ WORKFLOW_NAME=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['workflow_name'])")
244
+ WORKFLOW_TEMPLATE=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['workflow_template'])")
245
+ LAST_NODE=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.dumps(json.load(sys.stdin)['last_node']))")
246
+ WAITING_FOR=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['waiting_for'] or 'none')")
247
+ SESSION_ID=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['session_id'])")
248
+ DESIGN_ID=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['design_id'])")
249
+ PROCESS_SLUG=$(echo "$STATE_JSON" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin)['process_slug'])")
250
+
251
+ _LINCOLN_CURRENT_STAGE="$CURRENT_STAGE"
252
+ _LINCOLN_STATUS="$STATUS"
253
+ _LINCOLN_WORKFLOW_TEMPLATE="$WORKFLOW_TEMPLATE"
254
+ _LINCOLN_PROCESS_SLUG="$PROCESS_SLUG"
255
+
256
+ echo "Workflow: $WORKFLOW_NAME ($WORKFLOW_TEMPLATE)"
257
+ echo "Process package: ${PROCESS_SLUG:-(unset)}"
258
+ echo "State file: ${STATE_FILE#$ROOT/}"
259
+ echo "Current stage: $CURRENT_STAGE"
260
+ echo "Stage status: $STATUS"
261
+ echo "Session ID: ${SESSION_ID:-(unset)}"
262
+ echo "Design ID: ${DESIGN_ID:-(unset)}"
263
+ echo "Waiting for: $WAITING_FOR"
264
+ echo ""
265
+
266
+ # 4. Load stage context, agent role, and workflow template (summaries only)
267
+ if [[ "$CURRENT_STAGE" != "not_started" ]]; then
268
+ STAGE_YAML="$FRAMEWORK_ROOT/.claude/stages/$CURRENT_STAGE.yaml"
269
+ if [[ -f "$STAGE_YAML" ]]; then
270
+ echo "=== Lincoln Stage Context ==="
271
+ echo ""
272
+ echo "Stage YAML: ${STAGE_YAML#$FRAMEWORK_ROOT/}"
273
+ "$PYTHON" - "$STAGE_YAML" <<'PY'
274
+ import sys, yaml
275
+ data = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
276
+ ctx = data.get("context", {})
277
+ goal = ctx.get("goal", "")
278
+ if goal:
279
+ summary = goal.splitlines()[0]
280
+ print(f"Goal: {summary}")
281
+ agent = data.get("agent", {}).get("primary", "")
282
+ if agent:
283
+ print(f"Primary agent: {agent}")
284
+ skills = data.get("skills", {})
285
+ if isinstance(skills, dict):
286
+ skill_names = skills.get("required", []) + skills.get("optional", [])
287
+ elif isinstance(skills, list):
288
+ skill_names = skills
289
+ else:
290
+ skill_names = []
291
+ if skill_names:
292
+ print(f"Skills: {', '.join(skill_names[:5])}{'...' if len(skill_names) > 5 else ''}")
293
+ print("Use Read to inspect the full stage YAML if needed.")
294
+ PY
295
+ echo ""
296
+ echo "=== End Lincoln Stage Context ==="
297
+ echo ""
298
+ fi
299
+
300
+ # Print agent file pointers instead of full text to keep session-start lean.
301
+ PRIMARY_AGENT=$("$PYTHON" - "$STAGE_YAML" <<'PY' 2>/dev/null || true
302
+ import sys, yaml
303
+ data = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
304
+ print(data.get("agent", {}).get("primary", ""))
305
+ PY
306
+ )
307
+ # Stage YAML uses role IDs like lc-pm; agent files omit the lc- prefix.
308
+ AGENT_FILE="${PRIMARY_AGENT#lc-}.md"
309
+ if [[ -n "$PRIMARY_AGENT" && -f "$FRAMEWORK_ROOT/.claude/agents/$AGENT_FILE" ]]; then
310
+ echo "=== Agent Context ($PRIMARY_AGENT) ==="
311
+ echo "Agent file: .claude/agents/$AGENT_FILE"
312
+ echo "Default contract: .claude/agents/default.md"
313
+ echo "Behavioral contract: .claude/agents/_contract.md"
314
+ echo "Use Read to inspect the agent role if needed."
315
+ echo ""
316
+ echo "=== End Agent Context ==="
317
+ echo ""
318
+ fi
319
+
320
+ # Load workflow template summary
321
+ WORKFLOW_FILE="$FRAMEWORK_ROOT/.claude/workflows/${WORKFLOW_TEMPLATE}.yaml"
322
+ if [[ -f "$WORKFLOW_FILE" ]]; then
323
+ echo "=== Workflow Template ($WORKFLOW_TEMPLATE) ==="
324
+ echo "File: ${WORKFLOW_FILE#$FRAMEWORK_ROOT/}"
325
+ "$PYTHON" - "$WORKFLOW_FILE" <<'PY'
326
+ import sys, yaml
327
+ data = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
328
+ wf = data.get("workflow", data)
329
+ print("Name:", wf.get("name", "unknown"))
330
+ print("Description:", wf.get("description", ""))
331
+ print("Steps:")
332
+ for step in wf.get("steps", []):
333
+ print(f" - {step.get('id', 'unknown')}: {step.get('name', '')}")
334
+ PY
335
+ echo ""
336
+ echo "=== End Workflow Template ==="
337
+ echo ""
338
+ fi
339
+ else
340
+ NEEDS_OPENING_GUIDANCE="true"
341
+ fi
342
+
343
+ # 5. Load Conductor / OMC context
344
+ if [[ -d "$ROOT/.context" || -d "$ROOT/.omc" ]]; then
345
+ echo "=== Conductor / OMC Context ==="
346
+ for dir in "$ROOT/.context" "$ROOT/.omc"; do
347
+ if [[ -d "$dir" ]]; then
348
+ for f in $(find "$dir" -maxdepth 2 -type f \( -name '*.md' -o -name '*.yaml' -o -name '*.json' -o -name '*.txt' \) 2>/dev/null | sort); do
349
+ size=$(stat -f%z "$f" 2>/dev/null || stat -c%s "$f" 2>/dev/null || echo 0)
350
+ if [[ "$size" -lt 8192 ]]; then
351
+ echo ""
352
+ echo "--- ${f#$ROOT/} ---"
353
+ cat "$f"
354
+ else
355
+ echo ""
356
+ echo "--- ${f#$ROOT/} (too large to print: $size bytes) ---"
357
+ fi
358
+ done
359
+ fi
360
+ done
361
+ echo ""
362
+ echo "=== End Conductor / OMC Context ==="
363
+ echo ""
364
+ fi
365
+
366
+ # 6. Read last node handoff
367
+ HANDOFF_FILE=""
368
+ if [[ "$LAST_NODE" != "{}" ]]; then
369
+ HANDOFF_FILE=$(echo "$LAST_NODE" | "$PYTHON" -c "import sys,json; print(json.load(sys.stdin).get('handoff_file',''))" 2>/dev/null || echo "")
370
+ fi
371
+
372
+ if [[ -n "$HANDOFF_FILE" && -f "$ROOT/$HANDOFF_FILE" ]]; then
373
+ echo "=== Last Node Handoff ==="
374
+ cat "$ROOT/$HANDOFF_FILE"
375
+ echo ""
376
+ echo "=== End Handoff ==="
377
+ echo ""
378
+ fi
379
+
380
+ if [[ -n "$PROCESS_SLUG" && -d "$ROOT/$PROCESS_SLUG/handoffs" ]]; then
381
+ LATEST_HANDOFF=$(ls -t "$ROOT/$PROCESS_SLUG/handoffs"/*.md 2>/dev/null | head -1 || true)
382
+ if [[ -n "$LATEST_HANDOFF" ]]; then
383
+ echo "=== Latest Process Handoff ==="
384
+ cat "$LATEST_HANDOFF"
385
+ echo ""
386
+ echo "=== End Latest Process Handoff ==="
387
+ echo ""
388
+ fi
389
+ fi
390
+
391
+ # 7. Run status summary
392
+ STATUS_OUTPUT=$("$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln-status.py" --format markdown --state-file "$STATE_FILE" 2>/dev/null) || STATUS_OUTPUT=""
393
+ if [[ -n "$STATUS_OUTPUT" ]]; then
394
+ echo "=== Lincoln Status Summary ==="
395
+ echo "$STATUS_OUTPUT"
396
+ echo "=== End Lincoln Status Summary ==="
397
+ echo ""
398
+ fi
399
+
400
+ # 8. Opening guidance for a work package that has not started yet
401
+ if [[ "${NEEDS_OPENING_GUIDANCE:-}" == "true" ]]; then
402
+ _LINCOLN_GUIDANCE_INJECTED="true"
403
+ echo "=== Lincoln 开场引导 ==="
404
+ echo ""
405
+ echo "工作包已就绪但尚未启动(current_stage: not_started)。请先完成 .claude/skills/lc-workflow-router/prompts/intake-prompt.md 中的确认流程,再运行 validate-entry 进入第一阶段。"
406
+ echo ""
407
+ echo "=== End Lincoln 开场引导 ==="
408
+ echo ""
409
+ fi
410
+
411
+ echo "=== End Lincoln Session Start ==="
412
+ echo ""
413
+
414
+ emit_session_start_json
415
+
416
+ # Quantify the session-start token cost and make it available to benchmark reports (#63).
417
+ if [[ -n "$PROCESS_SLUG" && -f "$HOOK_OUTPUT_CAPTURE" ]]; then
418
+ METRICS_DIR="$ROOT/$PROCESS_SLUG/.trace"
419
+ mkdir -p "$METRICS_DIR"
420
+ "$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln_session_start_metrics.py" \
421
+ --input "$HOOK_OUTPUT_CAPTURE" \
422
+ --output "$METRICS_DIR/session-start-metrics.json" \
423
+ --stage "$CURRENT_STAGE" \
424
+ --status "$STATUS" >/dev/null 2>&1 || true
425
+ fi
426
+
427
+ exit 0
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # On-stop hook for Lincoln workflow.
5
+ # Updates last_updated_at in the workflow stage file when a session ends.
6
+ #
7
+ # The operational state file is branch-scoped: <process_slug>/workflow-stage.yaml
8
+ # (falls back to legacy .claude/workflow-state.yaml if present).
9
+
10
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
+ LOCAL_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
12
+ PROJECT_ROOT="$(pwd)"
13
+
14
+ FRAMEWORK_ROOT="${CLAUDE_PLUGIN_ROOT:-${LINCOLN_HOME:-}}"
15
+ if [[ -z "$FRAMEWORK_ROOT" ]]; then
16
+ # If this hook file lives inside a vendored project framework, prefer that.
17
+ if [[ "$SCRIPT_DIR" == "$LOCAL_ROOT/.claude/hooks"* && -d "$LOCAL_ROOT/.claude/stages" ]]; then
18
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
19
+ elif [[ -d "$HOME/.lincoln/current" ]]; then
20
+ FRAMEWORK_ROOT="$HOME/.lincoln/current"
21
+ else
22
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
23
+ fi
24
+ fi
25
+
26
+ if [[ "$FRAMEWORK_ROOT" != "$LOCAL_ROOT" ]]; then
27
+ MARKER_FOUND="false"
28
+ SEARCH_DIR="$PROJECT_ROOT"
29
+ while [[ "$SEARCH_DIR" != "/" ]]; do
30
+ if [[ -f "$SEARCH_DIR/.lincoln.yaml" || -f "$SEARCH_DIR/.claude/workflow-state.yaml" ]]; then
31
+ MARKER_FOUND="true"
32
+ break
33
+ fi
34
+ SEARCH_DIR="$(dirname "$SEARCH_DIR")"
35
+ done
36
+ if [[ "$MARKER_FOUND" != "true" && -z "${LINCOLN_ALWAYS_ON:-}" ]]; then
37
+ echo "Lincoln inactive: no .lincoln.yaml in $PROJECT_ROOT"
38
+ exit 0
39
+ fi
40
+ fi
41
+
42
+ ROOT="$PROJECT_ROOT"
43
+
44
+ if [ -x "$FRAMEWORK_ROOT/.venv/bin/python3" ]; then
45
+ PYTHON="$FRAMEWORK_ROOT/.venv/bin/python3"
46
+ elif [ -x "$FRAMEWORK_ROOT/venv/bin/python3" ]; then
47
+ PYTHON="$FRAMEWORK_ROOT/venv/bin/python3"
48
+ elif [ -x "$HOME/.lincoln/venv/bin/python3" ]; then
49
+ PYTHON="$HOME/.lincoln/venv/bin/python3"
50
+ else
51
+ PYTHON="python3"
52
+ fi
53
+
54
+ STATE_FILE=$("$PYTHON" - "$FRAMEWORK_ROOT" "$ROOT" "${LINCOLN_STATE_FILE:-}" <<'PY'
55
+ import sys
56
+ from pathlib import Path
57
+ framework_root = Path(sys.argv[1])
58
+ project_root = Path(sys.argv[2])
59
+ provided = sys.argv[3]
60
+ sys.path.insert(0, str(framework_root))
61
+ from scripts.lincoln_paths import resolve_state_path
62
+ path = Path(provided) if provided else None
63
+ print(resolve_state_path(path, project_root))
64
+ PY
65
+ )
66
+
67
+ if [[ ! -f "$STATE_FILE" ]]; then
68
+ exit 0
69
+ fi
70
+
71
+ # Update last_updated_at through the canonical state mutation layer.
72
+ "$PYTHON" "$FRAMEWORK_ROOT/scripts/stage_loader.py" \
73
+ --state-file "$STATE_FILE" \
74
+ --action update-last-updated \
75
+ 2>/dev/null || true
76
+
77
+ CURRENT_STAGE=$("$PYTHON" - "$STATE_FILE" <<'PY' 2>/dev/null
78
+ import sys, yaml
79
+ state = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
80
+ print(state.get("current_run", {}).get("current_stage") or "")
81
+ PY
82
+ ) || CURRENT_STAGE=""
83
+
84
+ STAGE_STATUS=$("$PYTHON" - "$STATE_FILE" <<'PY' 2>/dev/null
85
+ import sys, yaml
86
+ state = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
87
+ stage = state.get("current_run", {}).get("current_stage")
88
+ nodes = [n for n in state.get("nodes", []) if n.get("stage_id") == stage]
89
+ if nodes:
90
+ print(nodes[-1].get("status") or state.get("current_run", {}).get("status") or "")
91
+ else:
92
+ print(state.get("current_run", {}).get("status") or "")
93
+ PY
94
+ ) || STAGE_STATUS=""
95
+
96
+ if [[ -n "$CURRENT_STAGE" && ( "$STAGE_STATUS" == "waiting_for_human" || "$STAGE_STATUS" == "validation_failed" ) ]]; then
97
+ "$PYTHON" "$FRAMEWORK_ROOT/scripts/stage_loader.py" \
98
+ --state-file "$STATE_FILE" \
99
+ --stage "$CURRENT_STAGE" \
100
+ --action handoff-report \
101
+ >/dev/null 2>&1 || true
102
+ fi
103
+
104
+ exit 0
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # PostToolUse hook for Lincoln workflow.
5
+ # Tracks artifacts produced by side-effect tools and detects PR/branch sync events.
6
+ #
7
+ # Usage (manual):
8
+ # .claude/hooks/post-tool-use.sh "Write" '{"file_path": "foo.md"}' 0
9
+ #
10
+ # Expected arguments:
11
+ # $1: tool name
12
+ # $2: JSON-encoded tool arguments
13
+ # $3: tool exit code
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ LOCAL_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
17
+ PROJECT_ROOT="$(pwd)"
18
+
19
+ FRAMEWORK_ROOT="${CLAUDE_PLUGIN_ROOT:-${LINCOLN_HOME:-}}"
20
+ if [[ -z "$FRAMEWORK_ROOT" ]]; then
21
+ # If this hook file lives inside a vendored project framework, prefer that.
22
+ if [[ "$SCRIPT_DIR" == "$LOCAL_ROOT/.claude/hooks"* && -d "$LOCAL_ROOT/.claude/stages" ]]; then
23
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
24
+ elif [[ -d "$HOME/.lincoln/current" ]]; then
25
+ FRAMEWORK_ROOT="$HOME/.lincoln/current"
26
+ else
27
+ FRAMEWORK_ROOT="$LOCAL_ROOT"
28
+ fi
29
+ fi
30
+
31
+ if [[ "$FRAMEWORK_ROOT" != "$LOCAL_ROOT" ]]; then
32
+ MARKER_FOUND="false"
33
+ SEARCH_DIR="$PROJECT_ROOT"
34
+ while [[ "$SEARCH_DIR" != "/" ]]; do
35
+ if [[ -f "$SEARCH_DIR/.lincoln.yaml" || -f "$SEARCH_DIR/.claude/workflow-state.yaml" ]]; then
36
+ MARKER_FOUND="true"
37
+ break
38
+ fi
39
+ SEARCH_DIR="$(dirname "$SEARCH_DIR")"
40
+ done
41
+ if [[ "$MARKER_FOUND" != "true" && -z "${LINCOLN_ALWAYS_ON:-}" ]]; then
42
+ echo "Lincoln inactive: no .lincoln.yaml in $PROJECT_ROOT"
43
+ exit 0
44
+ fi
45
+ fi
46
+
47
+ ROOT="$PROJECT_ROOT"
48
+
49
+ mkdir -p "$ROOT/.context"
50
+
51
+ if [ -x "$FRAMEWORK_ROOT/.venv/bin/python3" ]; then
52
+ PYTHON="$FRAMEWORK_ROOT/.venv/bin/python3"
53
+ elif [ -x "$FRAMEWORK_ROOT/venv/bin/python3" ]; then
54
+ PYTHON="$FRAMEWORK_ROOT/venv/bin/python3"
55
+ elif [ -x "$HOME/.lincoln/venv/bin/python3" ]; then
56
+ PYTHON="$HOME/.lincoln/venv/bin/python3"
57
+ else
58
+ PYTHON="python3"
59
+ fi
60
+
61
+ TOOL_NAME="${1:-}"
62
+ TOOL_ARGS="${2:-}"
63
+ EXIT_CODE="${3:-0}"
64
+ STATE_FILE=$("$PYTHON" - "$FRAMEWORK_ROOT" "$ROOT" "${LINCOLN_STATE_FILE:-}" <<'PY'
65
+ import sys
66
+ from pathlib import Path
67
+ framework_root = Path(sys.argv[1])
68
+ project_root = Path(sys.argv[2])
69
+ provided = sys.argv[3]
70
+ sys.path.insert(0, str(framework_root))
71
+ from scripts.lincoln_paths import resolve_state_path
72
+ path = Path(provided) if provided else None
73
+ print(resolve_state_path(path, project_root))
74
+ PY
75
+ )
76
+
77
+ if [[ ! -f "$STATE_FILE" ]]; then
78
+ exit 0
79
+ fi
80
+
81
+ # Trace logging: record (nearly) every tool invocation to the session trace.
82
+ # This runs before the early success-only exit so failures are captured too.
83
+ # Skip no-op/recursive tools and any call already flagged with LINCOLN_SKIP_TRACE=1.
84
+ if [[ "${LINCOLN_SKIP_TRACE:-}" != "1" ]]; then
85
+ if [[ "$TOOL_NAME" != "Read" && "$TOOL_NAME" != "Grep" && "$TOOL_NAME" != "Glob" ]]; then
86
+ LINCOLN_SKIP_TRACE=1 "$PYTHON" "$FRAMEWORK_ROOT/scripts/lincoln_trace.py" \
87
+ --state-file "$STATE_FILE" \
88
+ --tool "$TOOL_NAME" \
89
+ --args-json "$TOOL_ARGS" \
90
+ --exit-code "$EXIT_CODE" \
91
+ 2>>"$ROOT/.context/lc-trace-errors.log" || true
92
+ fi
93
+ fi
94
+
95
+ # Only track successful side-effect tool uses
96
+ if [[ "$EXIT_CODE" != "0" ]]; then
97
+ exit 0
98
+ fi
99
+
100
+ SIDE_EFFECT_TOOLS=(
101
+ "Bash"
102
+ "Edit"
103
+ "Write"
104
+ "mcp__pencil__batch_design"
105
+ "mcp__pencil__export_nodes"
106
+ "mcp__pencil__export_html"
107
+ "mcp__plugin_ecc_github__create_pull_request"
108
+ "mcp__plugin_ecc_github__merge_pull_request"
109
+ )
110
+
111
+ is_side_effect() {
112
+ local tool="$1"
113
+ for t in "${SIDE_EFFECT_TOOLS[@]}"; do
114
+ if [[ "$tool" == "$t" ]]; then
115
+ return 0
116
+ fi
117
+ done
118
+ return 1
119
+ }
120
+
121
+ if is_side_effect "$TOOL_NAME"; then
122
+ "$PYTHON" "$FRAMEWORK_ROOT/scripts/track-artifacts.py" \
123
+ --state-file "$STATE_FILE" \
124
+ --tool "$TOOL_NAME" \
125
+ --args "$TOOL_ARGS" \
126
+ --project-root "$ROOT" \
127
+ 2>/dev/null || true
128
+ fi
129
+
130
+ # Determine the current stage so PR lifecycle nodes are attached to the stage
131
+ # that actually produced the PR, instead of hardcoding "implement".
132
+ CURRENT_STAGE=$("$PYTHON" - "$STATE_FILE" <<'PY' 2>/dev/null
133
+ import sys, yaml
134
+ state = yaml.safe_load(open(sys.argv[1], encoding="utf-8"))
135
+ print(state.get("current_run", {}).get("current_stage") or "implement")
136
+ PY
137
+ ) || CURRENT_STAGE="implement"
138
+
139
+ # Detect PR/branch sync events, append a node record, and queue the matching
140
+ # benchmark trigger in a single mapping to avoid duplicated logic.
141
+ BENCHMARK_TRIGGER=""
142
+ EVENT_STATUS=""
143
+ if [[ "$TOOL_NAME" == "mcp__plugin_ecc_github__create_pull_request" ]]; then
144
+ BENCHMARK_TRIGGER="pr_created"
145
+ EVENT_STATUS="pr_submitted"
146
+ elif [[ "$TOOL_NAME" == "mcp__plugin_ecc_github__merge_pull_request" ]]; then
147
+ BENCHMARK_TRIGGER="pr_merged"
148
+ EVENT_STATUS="merged"
149
+ fi
150
+
151
+ if [[ -n "$BENCHMARK_TRIGGER" ]]; then
152
+ EVENT_NODE="${CURRENT_STAGE:-implement}"
153
+ "$PYTHON" "$FRAMEWORK_ROOT/scripts/stage_loader.py" \
154
+ --state-file "$STATE_FILE" \
155
+ --action append-node \
156
+ --node-id "$EVENT_NODE" \
157
+ --status "$EVENT_STATUS" \
158
+ 2>/dev/null || true
159
+ fi
160
+
161
+ exit 0