@sushanglewis/lincoln 1.5.0 → 1.5.1

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 +1231 -906
  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,335 @@
1
+ #!/usr/bin/env python3
2
+ """Lincoln session trace writer.
3
+
4
+ Appends structured, immutable trace entries to a per-process JSONL file.
5
+ Designed to be called from the PostToolUse hook for every tool invocation
6
+ (except recursive or no-op tools).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import fcntl
13
+ import json
14
+ import logging
15
+ import os
16
+ import re
17
+ import sys
18
+ import uuid
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ # Ensure project root on path when invoked as a script
24
+ _PROJECT_ROOT = Path(__file__).resolve().parents[1]
25
+ if str(_PROJECT_ROOT) not in sys.path:
26
+ sys.path.insert(0, str(_PROJECT_ROOT))
27
+
28
+ from scripts.lincoln_paths import ( # noqa: E402
29
+ get_process_slug,
30
+ load_yaml,
31
+ process_package_root,
32
+ )
33
+
34
+ TRACE_SCHEMA_VERSION = "1.0.0"
35
+
36
+ # Tools whose invocations are not traced to avoid recursion or noise.
37
+ NO_TRACE_TOOLS = {"Read", "Grep", "Glob"}
38
+
39
+ # Task-management tools that share the same trace category and redaction rules.
40
+ TASK_TOOLS = frozenset({
41
+ "TaskCreate",
42
+ "TaskUpdate",
43
+ "TaskGet",
44
+ "TaskList",
45
+ "TaskOutput",
46
+ "TaskStop",
47
+ })
48
+
49
+ # Maximum length stored for a Bash command summary in the trace entry.
50
+ COMMAND_SUMMARY_MAX_LEN = 500
51
+
52
+ # Basic secret masking for Bash command strings. Values following common secret
53
+ # flags or environment variable assignments are replaced with `***`.
54
+ _MASK_PATTERNS = [
55
+ re.compile(r"(?i)((?:--api-key|-k|--token|--password|--secret|--access-token)\s+)\S+"),
56
+ re.compile(r"(?i)((?:--api-key|-k|--token|--password|--secret|--access-token)=)\S+"),
57
+ re.compile(
58
+ r"(?i)\b(API_KEY|TOKEN|PASSWORD|SECRET|GH_TOKEN|GITHUB_TOKEN|OPENAI_API_KEY|ANTHROPIC_API_KEY|AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|ACCESS_TOKEN)=\S+"
59
+ ),
60
+ re.compile(r"(?i)(Authorization:\s*(?:Bearer|Basic|Token)\s+)\S+"),
61
+ re.compile(r"(?i)(X-Api-Key:\s+)\S+"),
62
+ re.compile(r"(?i)((?:--user|-u)\s+)\S+:\S+"),
63
+ re.compile(r"(?i)(https?://)[^:]+:[^@]+@"),
64
+ re.compile(r"(?i)([\?\x26])(api_key|token|key)=[^\x26]+"),
65
+ ]
66
+
67
+
68
+ def categorize_tool(tool: str) -> str:
69
+ """Bucket a Claude Code tool name into a Lincoln trace category."""
70
+ if tool == "Skill":
71
+ return "skill"
72
+ if tool == "Agent":
73
+ return "agent"
74
+ if tool in ("Read", "Grep", "Glob"):
75
+ return "read"
76
+ if tool == "Write":
77
+ return "write"
78
+ if tool == "Edit":
79
+ return "edit"
80
+ if tool == "Bash":
81
+ return "bash"
82
+ if tool in TASK_TOOLS:
83
+ return "task"
84
+ if tool.startswith("mcp__"):
85
+ return "mcp"
86
+ if tool == "LincolnHandoff":
87
+ return "handoff"
88
+ return "other"
89
+
90
+
91
+ def _first_non_none(mapping: dict[str, Any], *keys: str) -> Any:
92
+ for key in keys:
93
+ if key in mapping:
94
+ return mapping[key]
95
+ return None
96
+
97
+
98
+ def _mask_command(command: str) -> str:
99
+ masked = command
100
+ for pattern in _MASK_PATTERNS:
101
+ masked = pattern.sub(r"\1***", masked)
102
+ return masked
103
+
104
+
105
+ def _redact_bash(args: dict[str, Any]) -> dict[str, Any]:
106
+ command = args.get("command", "")
107
+ return {"command": _mask_command(str(command))[:COMMAND_SUMMARY_MAX_LEN]}
108
+
109
+
110
+ def _redact_file(args: dict[str, Any]) -> dict[str, Any]:
111
+ return {"file_path": str(args.get("file_path", ""))[:500]}
112
+
113
+
114
+ def _redact_skill(args: dict[str, Any]) -> dict[str, Any]:
115
+ return {
116
+ "skill": str(args.get("skill", ""))[:200],
117
+ "args": str(args.get("args", ""))[:500],
118
+ }
119
+
120
+
121
+ def _redact_agent(args: dict[str, Any]) -> dict[str, Any]:
122
+ return {
123
+ "subagent_type": str(args.get("subagent_type", ""))[:200] or "general-purpose",
124
+ "description": str(args.get("description", ""))[:300],
125
+ }
126
+
127
+
128
+ def _redact_task(args: dict[str, Any]) -> dict[str, Any]:
129
+ return {
130
+ "subject": str(_first_non_none(args, "subject", "taskId", "description") or "")[:300],
131
+ }
132
+
133
+
134
+ def _redact_mcp(args: dict[str, Any]) -> dict[str, Any]:
135
+ summary: dict[str, Any] = {}
136
+ for key in ("path", "name", "url", "projectId", "skill", "query"):
137
+ if key in args:
138
+ summary[key] = str(args[key])[:200]
139
+ return summary
140
+
141
+
142
+ def _redact_handoff(args: dict[str, Any]) -> dict[str, Any]:
143
+ return {"stage": str(args.get("stage", ""))[:100]}
144
+
145
+
146
+ def _redact_generic(args: dict[str, Any]) -> dict[str, Any]:
147
+ return {
148
+ key: str(value)[:200]
149
+ for key, value in args.items()
150
+ if isinstance(value, (str, int, float, bool))
151
+ }
152
+
153
+
154
+ def redact_args(tool: str, args: Any) -> dict[str, Any]:
155
+ """Produce a privacy-safe summary of tool arguments.
156
+
157
+ This implementation is intentionally conservative: it records paths and
158
+ high-level identifiers but does not store secrets, full command output, or
159
+ large payloads. It is designed as a pluggable hook for future MCP-specific
160
+ redaction rules.
161
+ """
162
+ if not isinstance(args, dict):
163
+ return {"_raw_type": type(args).__name__}
164
+
165
+ if tool == "Bash":
166
+ return _redact_bash(args)
167
+ if tool in ("Write", "Edit", "Read"):
168
+ return _redact_file(args)
169
+ if tool == "Skill":
170
+ return _redact_skill(args)
171
+ if tool == "Agent":
172
+ return _redact_agent(args)
173
+ if tool == "LincolnHandoff":
174
+ return _redact_handoff(args)
175
+ if tool in TASK_TOOLS:
176
+ return _redact_task(args)
177
+ if tool.startswith("mcp__"):
178
+ return _redact_mcp(args)
179
+ return _redact_generic(args)
180
+
181
+
182
+ def generate_sequence_id() -> str:
183
+ """Return a globally unique trace sequence identifier."""
184
+ return uuid.uuid4().hex
185
+
186
+
187
+ def _resolve_trace_file(state: dict[str, Any] | None, state_path: Path | None) -> Path:
188
+ """Decide which JSONL file to write to, honouring child-agent overrides."""
189
+ env_file = os.environ.get("LINCOLN_TRACE_FILE")
190
+ if env_file:
191
+ return Path(env_file)
192
+
193
+ project_root = state_path.resolve().parents[1] if state_path else _PROJECT_ROOT
194
+ slug = get_process_slug(state, state_path)
195
+ root = process_package_root(slug, state, state_path, project_root=project_root)
196
+ trace_dir = root / ".trace"
197
+
198
+ agent_id = os.environ.get("LINCOLN_AGENT_ID")
199
+ if agent_id:
200
+ return trace_dir / f"lc-trace-{agent_id}.jsonl"
201
+
202
+ return trace_dir / "lc-trace.jsonl"
203
+
204
+
205
+ def _get_run_id(state: dict[str, Any] | None, run_id: str | None) -> str:
206
+ if run_id:
207
+ return run_id
208
+ if state:
209
+ return str(state.get("current_run", {}).get("run_id", "")) or "unknown"
210
+ return "unknown"
211
+
212
+
213
+ def _get_stage(state: dict[str, Any] | None, stage: str | None) -> str:
214
+ if stage:
215
+ return stage
216
+ if state:
217
+ return str(state.get("current_run", {}).get("current_stage", "")) or "unknown"
218
+ return "unknown"
219
+
220
+
221
+ def _safe_json_loads(value: Any) -> Any:
222
+ if isinstance(value, str):
223
+ try:
224
+ return json.loads(value)
225
+ except Exception as exc:
226
+ logging.warning("Could not parse trace args as JSON: %s", exc)
227
+ return {"_unparsed": value[:200]}
228
+ return value
229
+
230
+
231
+ def _load_state(state_path: Path | None) -> dict[str, Any] | None:
232
+ if not state_path or not state_path.exists():
233
+ return None
234
+ try:
235
+ return load_yaml(state_path)
236
+ except Exception as exc:
237
+ logging.warning("Failed to load workflow state from %s: %s", state_path, exc)
238
+ return None
239
+
240
+
241
+ def _build_trace_target(tool: str, args_summary: dict[str, Any]) -> str:
242
+ if tool == "Skill":
243
+ return str(args_summary.get("skill", ""))
244
+ if tool == "Agent":
245
+ return str(args_summary.get("subagent_type", ""))
246
+ if tool == "Bash":
247
+ return str(args_summary.get("command", ""))[:120]
248
+ if "file_path" in args_summary:
249
+ return str(args_summary["file_path"])
250
+ return str(args_summary.get("path", args_summary.get("name", "")))
251
+
252
+
253
+ def _build_trace_entry(
254
+ tool: str,
255
+ args_summary: dict[str, Any],
256
+ exit_code: int,
257
+ run_id: str,
258
+ stage: str,
259
+ ) -> dict[str, Any]:
260
+ return {
261
+ "schema_version": TRACE_SCHEMA_VERSION,
262
+ "sequence_id": generate_sequence_id(),
263
+ "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
264
+ "run_id": run_id,
265
+ "stage": stage,
266
+ "tool": tool,
267
+ "category": categorize_tool(tool),
268
+ "target": _build_trace_target(tool, args_summary),
269
+ "exit_code": int(exit_code),
270
+ "args_summary": args_summary,
271
+ }
272
+
273
+
274
+ def _append_trace_line(trace_file: Path, entry: dict[str, Any]) -> None:
275
+ trace_file.parent.mkdir(parents=True, exist_ok=True)
276
+ line = json.dumps(entry, ensure_ascii=False, separators=(",", ":")) + "\n"
277
+ with open(trace_file, "a", encoding="utf-8") as fh:
278
+ fcntl.lockf(fh.fileno(), fcntl.LOCK_EX)
279
+ try:
280
+ fh.write(line)
281
+ fh.flush()
282
+ finally:
283
+ fcntl.lockf(fh.fileno(), fcntl.LOCK_UN)
284
+
285
+
286
+ def append_trace_entry(
287
+ state_path: Path | None,
288
+ tool: str,
289
+ args: Any,
290
+ exit_code: int,
291
+ stage: str | None = None,
292
+ run_id: str | None = None,
293
+ ) -> Path | None:
294
+ """Append a single trace entry to the appropriate JSONL file.
295
+
296
+ The write is atomic with respect to other processes on the same host: the
297
+ file is locked while appending, so concurrent parent and child agents do
298
+ not interleave JSON lines.
299
+ """
300
+ if tool in NO_TRACE_TOOLS:
301
+ return None
302
+
303
+ state = _load_state(state_path)
304
+ resolved_run_id = _get_run_id(state, run_id)
305
+ resolved_stage = _get_stage(state, stage)
306
+ trace_file = _resolve_trace_file(state, state_path)
307
+ args_summary = redact_args(tool, _safe_json_loads(args))
308
+ entry = _build_trace_entry(tool, args_summary, exit_code, resolved_run_id, resolved_stage)
309
+ _append_trace_line(trace_file, entry)
310
+ return trace_file
311
+
312
+
313
+ def main(argv: list[str] | None = None) -> int:
314
+ parser = argparse.ArgumentParser(description="Append a Lincoln trace entry")
315
+ parser.add_argument("--state-file", type=Path, default=None)
316
+ parser.add_argument("--tool", required=True)
317
+ parser.add_argument("--args-json", default="{}")
318
+ parser.add_argument("--exit-code", type=int, default=0)
319
+ parser.add_argument("--stage", default=None)
320
+ parser.add_argument("--run-id", default=None)
321
+ args = parser.parse_args(argv)
322
+
323
+ append_trace_entry(
324
+ state_path=args.state_file,
325
+ tool=args.tool,
326
+ args=args.args_json,
327
+ exit_code=args.exit_code,
328
+ stage=args.stage,
329
+ run_id=args.run_id,
330
+ )
331
+ return 0
332
+
333
+
334
+ if __name__ == "__main__":
335
+ sys.exit(main())
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python3
2
+ """Lincoln workflow bundle CLI backing the lc-wf-* harness commands.
3
+
4
+ Usage:
5
+ python3 scripts/lincoln_workflow.py list
6
+ python3 scripts/lincoln_workflow.py start --workflow <name> [--issue-number N] [--force]
7
+ python3 scripts/lincoln_workflow.py status
8
+
9
+ - solo workflows render a session-scoped instance at .context/workflow/<name>.yaml
10
+ - team workflows delegate to scripts/init-lincoln-branch.sh (branch + issue package)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import re
17
+ import subprocess
18
+ import sys
19
+ from datetime import datetime, timezone
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ import yaml
24
+
25
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
26
+
27
+ from lincoln_paths import PROJECT_ROOT, assert_contained, validate_name # noqa: E402
28
+
29
+ WORKFLOWS_DIR = PROJECT_ROOT / ".claude" / "workflows"
30
+ SOLO_TEMPLATE_PATH = PROJECT_ROOT / ".claude" / "templates" / "solo-workflow-context.yaml"
31
+ SOLO_STATE_DIR = PROJECT_ROOT / ".context" / "workflow"
32
+ INIT_BRANCH_SCRIPT = PROJECT_ROOT / "scripts" / "init-lincoln-branch.sh"
33
+ STAGE_LOADER_SCRIPT = PROJECT_ROOT / "scripts" / "stage_loader.py"
34
+
35
+ DEFAULT_EXECUTION_MODE = "team"
36
+
37
+
38
+ def now_iso() -> str:
39
+ return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
40
+
41
+
42
+ def run_id_stamp() -> str:
43
+ return datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
44
+
45
+
46
+ def load_workflow_definition(name: str) -> dict[str, Any]:
47
+ try:
48
+ validate_name(name, "workflow name")
49
+ except ValueError as exc:
50
+ raise SystemExit(f"ERROR: {exc}")
51
+ path = WORKFLOWS_DIR / f"{name}.yaml"
52
+ assert_contained(path, WORKFLOWS_DIR, "workflow path")
53
+ if not path.is_file():
54
+ available = ", ".join(sorted(p.stem for p in WORKFLOWS_DIR.glob("*.yaml")))
55
+ raise SystemExit(f"ERROR: unknown workflow '{name}'. Available: {available}")
56
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
57
+ return data.get("workflow", data)
58
+
59
+
60
+ def list_workflows() -> int:
61
+ rows = []
62
+ for path in sorted(WORKFLOWS_DIR.glob("*.yaml")):
63
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
64
+ wf = data.get("workflow", {})
65
+ rows.append(
66
+ (
67
+ wf.get("name", path.stem),
68
+ wf.get("execution_mode", DEFAULT_EXECUTION_MODE),
69
+ wf.get("description", ""),
70
+ )
71
+ )
72
+ if not rows:
73
+ print("No workflows found under .claude/workflows/")
74
+ return 1
75
+ print(f"{'NAME':<32} {'MODE':<6} DESCRIPTION")
76
+ for name, mode, description in rows:
77
+ print(f"{name:<32} {mode:<6} {description}")
78
+ return 0
79
+
80
+
81
+ def _default_session_id(workflow_name: str) -> str:
82
+ """Generate a filesystem-safe session id for solo workflows."""
83
+ timestamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
84
+ safe = re.sub(r"[^a-z0-9_-]", "-", workflow_name.lower())
85
+ return f"{safe}-{timestamp}"
86
+
87
+
88
+ def start_solo(workflow_name: str, wf: dict[str, Any], force: bool = False, session_id: str | None = None) -> int:
89
+ try:
90
+ validate_name(workflow_name, "workflow name")
91
+ except ValueError as exc:
92
+ raise SystemExit(f"ERROR: {exc}")
93
+ state_path = SOLO_STATE_DIR / f"{workflow_name}.yaml"
94
+ assert_contained(state_path, SOLO_STATE_DIR, "solo state path")
95
+ if state_path.exists() and not force:
96
+ raise SystemExit(f"ERROR: solo instance already exists: {state_path} (use --force to overwrite)")
97
+
98
+ state = yaml.safe_load(SOLO_TEMPLATE_PATH.read_text(encoding="utf-8"))
99
+ steps = wf.get("steps", [])
100
+ first_stage = steps[0].get("id", "not_started") if steps else "not_started"
101
+ now = now_iso()
102
+
103
+ state["workflow"]["name"] = wf.get("name", workflow_name)
104
+ state["workflow"]["version"] = wf.get("version", "1.0.0")
105
+ state["workflow"]["template"] = workflow_name
106
+
107
+ current_run = state["current_run"]
108
+ current_run["run_id"] = f"{workflow_name}-{run_id_stamp()}"
109
+ current_run["current_stage"] = first_stage
110
+ current_run["status"] = "in_progress"
111
+ current_run["started_at"] = now
112
+ current_run["last_updated_at"] = now
113
+ state["recovery"]["can_resume_from"] = first_stage
114
+
115
+ variables = state.setdefault("current_run", {}).setdefault("variables", {})
116
+ variables["session_id"] = session_id or _default_session_id(workflow_name)
117
+
118
+ state_path.parent.mkdir(parents=True, exist_ok=True)
119
+ tmp_path = state_path.with_suffix(".yaml.tmp")
120
+ try:
121
+ tmp_path.write_text(
122
+ yaml.dump(state, allow_unicode=True, sort_keys=False, width=120), encoding="utf-8"
123
+ )
124
+ tmp_path.replace(state_path)
125
+ except Exception:
126
+ if tmp_path.exists():
127
+ tmp_path.unlink()
128
+ raise
129
+
130
+ try:
131
+ display_path = state_path.relative_to(PROJECT_ROOT)
132
+ except ValueError:
133
+ display_path = state_path
134
+ print(f"Solo workflow instance created: {display_path}")
135
+ print(f"Workflow: {workflow_name} (solo) | first stage: {first_stage}")
136
+ print(f"Next: python3 scripts/stage_loader.py --stage {first_stage} --action validate-entry")
137
+ return 0
138
+
139
+
140
+ def start_team(workflow_name: str, issue_number: str | None) -> int:
141
+ if not issue_number:
142
+ raise SystemExit(
143
+ "ERROR: --issue-number is required for team workflows (branch-scoped issue package)"
144
+ )
145
+ cmd = [
146
+ "bash",
147
+ str(INIT_BRANCH_SCRIPT),
148
+ "--workflow",
149
+ workflow_name,
150
+ "--issue-number",
151
+ str(issue_number),
152
+ ]
153
+ return subprocess.run(cmd, cwd=PROJECT_ROOT).returncode
154
+
155
+
156
+ def cmd_start(args: argparse.Namespace) -> int:
157
+ wf = load_workflow_definition(args.workflow)
158
+ mode = wf.get("execution_mode", DEFAULT_EXECUTION_MODE)
159
+ if mode == "solo":
160
+ return start_solo(args.workflow, wf, args.force, getattr(args, "session_id", None))
161
+ return start_team(args.workflow, args.issue_number)
162
+
163
+
164
+ def cmd_status(_args: argparse.Namespace) -> int:
165
+ return subprocess.run(
166
+ [sys.executable, str(STAGE_LOADER_SCRIPT), "--action", "status"], cwd=PROJECT_ROOT
167
+ ).returncode
168
+
169
+
170
+ def main() -> int:
171
+ parser = argparse.ArgumentParser(
172
+ description="Lincoln workflow bundle CLI (lc-wf-* commands)",
173
+ )
174
+ sub = parser.add_subparsers(dest="command", required=True)
175
+ sub.add_parser("list", help="List registered workflows with execution mode")
176
+
177
+ start = sub.add_parser("start", help="Start a workflow run (solo instance or team issue package)")
178
+ start.add_argument("--workflow", required=True, help="Workflow name under .claude/workflows/")
179
+ start.add_argument("--issue-number", help="Issue number (required for team workflows)")
180
+ start.add_argument("--session-id", help="Session identifier for solo artifacts (auto-generated if omitted)")
181
+ start.add_argument("--force", action="store_true", help="Overwrite existing solo instance")
182
+
183
+ sub.add_parser("status", help="Show current workflow run status")
184
+
185
+ args = parser.parse_args()
186
+ if args.command == "list":
187
+ return list_workflows()
188
+ if args.command == "start":
189
+ try:
190
+ validate_name(args.workflow, "workflow name")
191
+ return cmd_start(args)
192
+ except ValueError as exc:
193
+ print(f"ERROR: {exc}", file=sys.stderr)
194
+ return 1
195
+ return cmd_status(args)
196
+
197
+
198
+ if __name__ == "__main__":
199
+ sys.exit(main())
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # List all active Lincoln feature branches and their current stage state.
5
+ #
6
+ # Usage:
7
+ # scripts/list-active-lincoln-branches.sh [--waiting-for-me <role>]
8
+ #
9
+ # Options:
10
+ # --waiting-for-me <role> Filter by waiting_for role (pm|agent|agent-fix|next-role)
11
+ #
12
+ # Output:
13
+ # Calls lincoln-status.py --format table for each remote issue-* branch
14
+ # (legacy lincoln/* branches are also listed).
15
+
16
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17
+ ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
18
+ cd "$ROOT"
19
+
20
+ REMOTE=""
21
+ FILTER_ROLE=""
22
+
23
+ # Parse optional --waiting-for-me flag
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --waiting-for-me)
27
+ FILTER_ROLE="$2"
28
+ shift 2
29
+ ;;
30
+ *)
31
+ REMOTE="$1"
32
+ shift
33
+ ;;
34
+ esac
35
+ done
36
+ REMOTE="${REMOTE:-origin}"
37
+
38
+ echo "==> Fetching remote branches from $REMOTE"
39
+ git fetch "$REMOTE" '+refs/heads/issue-*:refs/remotes/'"$REMOTE"'/issue-*' 2>/dev/null || true
40
+ git fetch "$REMOTE" '+refs/heads/lincoln/*:refs/remotes/'"$REMOTE"'/lincoln/*' 2>/dev/null || true
41
+
42
+ BRANCHES=$(git branch -r --list "$REMOTE/issue-*" --list "$REMOTE/lincoln/*" 2>/dev/null || true)
43
+
44
+ if [[ -z "$BRANCHES" ]]; then
45
+ echo "No active Lincoln branches found."
46
+ exit 0
47
+ fi
48
+
49
+ echo ""
50
+
51
+ for ref in $BRANCHES; do
52
+ # Extract branch name without remote prefix
53
+ branch="${ref#$REMOTE/}"
54
+
55
+ # Prefer the issue work-package state file; fall back to the legacy path.
56
+ state_yaml=$(git show "$ref:$branch/workflow-stage.yaml" 2>/dev/null || \
57
+ git show "$ref:.claude/workflow-state.yaml" 2>/dev/null || true)
58
+
59
+ status_json=""
60
+ if [[ -n "$state_yaml" ]]; then
61
+ status_json=$(printf '%s' "$state_yaml" | \
62
+ python3 "$ROOT/scripts/lincoln-status.py" --format json --state-file /dev/stdin 2>/dev/null || true)
63
+ fi
64
+
65
+ if [[ -z "$status_json" ]]; then
66
+ printf "%-50s %-22s %-20s %-15s\n" "$branch" "unknown" "no-state-file" ""
67
+ continue
68
+ fi
69
+
70
+ # Extract fields using Python
71
+ python3 - "$branch" "$status_json" "$FILTER_ROLE" <<'PY'
72
+ import json
73
+ import sys
74
+
75
+ branch, raw, filter_role = sys.argv[1:4]
76
+ data = json.loads(raw)
77
+
78
+ stage_id = data.get("current_stage") or "unknown"
79
+ stage_status = data.get("stage_status") or "unknown"
80
+ waiting_for = data.get("waiting_for") or ""
81
+
82
+ if filter_role and waiting_for != filter_role:
83
+ sys.exit(0)
84
+
85
+ print(f"{branch:50} {stage_id:22} {stage_status:20} {waiting_for:15}")
86
+ PY
87
+ done