@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,218 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sync Lincoln framework runtime payload into packages/lincoln/framework/.
4
+ *
5
+ * This script mirrors the allowlist used by scripts/package-lincoln-plugin.py
6
+ * so the npm package can bundle the same files that the release tarball uses.
7
+ */
8
+
9
+ import crypto from 'node:crypto'
10
+ import fs from 'node:fs/promises'
11
+ import path from 'node:path'
12
+ import { fileURLToPath } from 'node:url'
13
+
14
+ const __filename = fileURLToPath(import.meta.url)
15
+ const ROOT = path.resolve(path.dirname(__filename), '..')
16
+ const PAYLOAD_DIR = path.join(ROOT, 'packages', 'lincoln', 'framework')
17
+ const VERSION_FILE = path.join(ROOT, '.version-bump.json')
18
+
19
+ const ALLOWLIST_DIRS = ['.claude', '.claude-plugin', 'scripts', 'tools']
20
+ const ALLOWLIST_FILES = [
21
+ 'README.md',
22
+ 'README.en.md',
23
+ 'USAGE.md',
24
+ 'CONTRIBUTING.md',
25
+ 'CLAUDE.md',
26
+ 'LICENSE',
27
+ 'RELEASE.md',
28
+ '.version-bump.json',
29
+ 'requirements.txt',
30
+ 'SKILL.md'
31
+ ]
32
+
33
+ const DENYLIST_NAMES = new Set([
34
+ '.git',
35
+ '.context',
36
+ '.venv',
37
+ 'venv',
38
+ 'oss',
39
+ '.pytest_cache',
40
+ '__pycache__',
41
+ '.DS_Store',
42
+ 'node_modules'
43
+ ])
44
+ const DENYLIST_PREFIXES = ['issue-']
45
+ const SKIP_PATHS = new Set([
46
+ '.claude/templates/issue-package',
47
+ 'tools/lincoln-installer/dist',
48
+ 'tools/lincoln-installer/node_modules',
49
+ 'tools/lincoln-installer/coverage'
50
+ ])
51
+
52
+ async function loadVersion() {
53
+ const raw = await fs.readFile(VERSION_FILE, 'utf8')
54
+ const data = JSON.parse(raw)
55
+ if (typeof data.version !== 'string') {
56
+ throw new Error('.version-bump.json missing version string')
57
+ }
58
+ return data.version
59
+ }
60
+
61
+ function isDenied(relativePath) {
62
+ const parts = relativePath.split(path.sep)
63
+ for (const part of parts) {
64
+ if (DENYLIST_NAMES.has(part)) return true
65
+ for (const prefix of DENYLIST_PREFIXES) {
66
+ if (part.startsWith(prefix)) return true
67
+ }
68
+ }
69
+ for (const skip of SKIP_PATHS) {
70
+ if (relativePath === skip || relativePath.startsWith(skip + path.sep)) {
71
+ return true
72
+ }
73
+ }
74
+ return false
75
+ }
76
+
77
+ async function* walk(dir, baseDir) {
78
+ const entries = await fs.readdir(dir, { withFileTypes: true })
79
+ for (const entry of entries) {
80
+ const fullPath = path.join(dir, entry.name)
81
+ const relativePath = path.relative(baseDir, fullPath)
82
+ if (isDenied(relativePath)) continue
83
+ if (entry.isDirectory()) {
84
+ yield* walk(fullPath, baseDir)
85
+ } else if (entry.isFile()) {
86
+ yield { fullPath, relativePath }
87
+ }
88
+ }
89
+ }
90
+
91
+ async function sha256(filePath) {
92
+ const data = await fs.readFile(filePath)
93
+ return crypto.createHash('sha256').update(data).digest('hex')
94
+ }
95
+
96
+ async function syncPayload(version) {
97
+ await fs.rm(PAYLOAD_DIR, { recursive: true, force: true })
98
+ await fs.mkdir(PAYLOAD_DIR, { recursive: true })
99
+
100
+ const copied = []
101
+
102
+ for (const dir of ALLOWLIST_DIRS) {
103
+ const src = path.join(ROOT, dir)
104
+ try {
105
+ await fs.access(src)
106
+ } catch {
107
+ continue
108
+ }
109
+
110
+ for await (const { fullPath, relativePath } of walk(src, ROOT)) {
111
+ const dest = path.join(PAYLOAD_DIR, relativePath)
112
+ await fs.mkdir(path.dirname(dest), { recursive: true })
113
+ await fs.copyFile(fullPath, dest)
114
+ copied.push(relativePath)
115
+ }
116
+ }
117
+
118
+ for (const file of ALLOWLIST_FILES) {
119
+ const src = path.join(ROOT, file)
120
+ try {
121
+ await fs.access(src)
122
+ } catch {
123
+ continue
124
+ }
125
+ const dest = path.join(PAYLOAD_DIR, file)
126
+ await fs.mkdir(path.dirname(dest), { recursive: true })
127
+ await fs.copyFile(src, dest)
128
+ copied.push(file)
129
+ }
130
+
131
+ copied.sort()
132
+
133
+ const manifestFiles = []
134
+ for (const relativePath of copied) {
135
+ const filePath = path.join(PAYLOAD_DIR, relativePath)
136
+ manifestFiles.push({
137
+ path: relativePath,
138
+ sha256: await sha256(filePath)
139
+ })
140
+ }
141
+
142
+ const manifest = {
143
+ version,
144
+ generatedAt: new Date().toISOString(),
145
+ files: manifestFiles
146
+ }
147
+
148
+ await fs.writeFile(
149
+ path.join(PAYLOAD_DIR, 'manifest.json'),
150
+ JSON.stringify(manifest, null, 2) + '\n'
151
+ )
152
+
153
+ return copied
154
+ }
155
+
156
+ async function checkPayload(version) {
157
+ try {
158
+ await fs.access(PAYLOAD_DIR)
159
+ } catch {
160
+ return { ok: false, reason: 'framework payload directory missing' }
161
+ }
162
+
163
+ const manifestPath = path.join(PAYLOAD_DIR, 'manifest.json')
164
+ let manifest
165
+ try {
166
+ const raw = await fs.readFile(manifestPath, 'utf8')
167
+ manifest = JSON.parse(raw)
168
+ } catch {
169
+ return { ok: false, reason: 'manifest.json missing or unreadable' }
170
+ }
171
+
172
+ if (manifest.version !== version) {
173
+ return { ok: false, reason: `version mismatch: ${manifest.version} != ${version}` }
174
+ }
175
+
176
+ for (const entry of manifest.files) {
177
+ const filePath = path.join(PAYLOAD_DIR, entry.path)
178
+ try {
179
+ const actual = await sha256(filePath)
180
+ if (actual !== entry.sha256) {
181
+ return { ok: false, reason: `checksum mismatch: ${entry.path}` }
182
+ }
183
+ } catch {
184
+ return { ok: false, reason: `missing file: ${entry.path}` }
185
+ }
186
+ }
187
+
188
+ return { ok: true }
189
+ }
190
+
191
+ async function main() {
192
+ const args = process.argv.slice(2)
193
+ const version = await loadVersion()
194
+
195
+ if (args.includes('--dump-allowlist')) {
196
+ console.log(JSON.stringify({ dirs: ALLOWLIST_DIRS, files: ALLOWLIST_FILES }, null, 2))
197
+ return 0
198
+ }
199
+
200
+ if (args.includes('--check')) {
201
+ const result = await checkPayload(version)
202
+ if (result.ok) {
203
+ console.log(`framework payload up to date (${version})`)
204
+ return 0
205
+ }
206
+ console.error(`framework payload out of sync: ${result.reason}`)
207
+ return 1
208
+ }
209
+
210
+ const copied = await syncPayload(version)
211
+ console.log(`synced ${copied.length} files to ${PAYLOAD_DIR} (${version})`)
212
+ return 0
213
+ }
214
+
215
+ main().then((code) => process.exit(code)).catch((err) => {
216
+ console.error(err)
217
+ process.exit(1)
218
+ })
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Task-tool guard for the Lincoln workflow.
4
+
5
+ Prevents the agent from using TaskCreate/TaskUpdate as placeholders for
6
+ "I will send a user message soon" in dialogue stages, and limits consecutive
7
+ task-tool calls everywhere else so a runaway loop cannot accumulate hundreds
8
+ of task artifacts without a non-task action in between.
9
+
10
+ Intended to be invoked from .claude/hooks/pre-tool-use.sh for every tool call.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ import sys
19
+ import tempfile
20
+ from pathlib import Path
21
+ from typing import Any
22
+
23
+ import yaml
24
+
25
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
26
+ try:
27
+ from scripts.lincoln_paths import resolve_state_path
28
+ except Exception: # pragma: no cover - standalone fallback
29
+ resolve_state_path = None # type: ignore[assignment]
30
+
31
+ # Stages where the agent is expected to talk to the PM, not to manage a task
32
+ # list. We default this from the workflow YAML (human_gate: true), but allow
33
+ # an override for tests.
34
+ DIALOGUE_STAGE_FALLBACK: frozenset[str] = frozenset(
35
+ {"clarify", "product-design-docs", "product-prototype", "implement"}
36
+ )
37
+
38
+ TASK_TOOLS: frozenset[str] = frozenset({"TaskCreate", "TaskUpdate"})
39
+
40
+ DEFAULT_MAX_CONSECUTIVE = 3
41
+
42
+
43
+ def load_yaml(path: Path) -> Any:
44
+ if not path.exists():
45
+ raise FileNotFoundError(path)
46
+ return yaml.safe_load(path.read_text(encoding="utf-8"))
47
+
48
+
49
+ def load_state(state_file: Path) -> dict[str, Any] | None:
50
+ if not state_file.exists():
51
+ return None
52
+ data = load_yaml(state_file)
53
+ if not isinstance(data, dict):
54
+ return None
55
+ return data
56
+
57
+
58
+ def resolve_project_root(state_file: Path) -> Path | None:
59
+ """Derive project root from <root>/.claude/workflow-state.yaml."""
60
+ resolved = state_file.resolve()
61
+ if len(resolved.parents) < 2:
62
+ return None
63
+ return resolved.parents[1]
64
+
65
+
66
+ def dialogue_stages(workflow_file: Path | None, override: set[str] | None) -> set[str]:
67
+ if override is not None:
68
+ return override
69
+ if workflow_file is None or not workflow_file.exists():
70
+ return set(DIALOGUE_STAGE_FALLBACK)
71
+ try:
72
+ data = load_yaml(workflow_file)
73
+ workflow = data.get("workflow", data) if isinstance(data, dict) else {}
74
+ return {
75
+ step["id"]
76
+ for step in workflow.get("steps", [])
77
+ if isinstance(step, dict) and step.get("human_gate")
78
+ }
79
+ except (yaml.YAMLError, AttributeError, KeyError, TypeError):
80
+ return set(DIALOGUE_STAGE_FALLBACK)
81
+
82
+
83
+ def counter_file_path(state_file: Path) -> Path:
84
+ root = resolve_project_root(state_file)
85
+ if root is None:
86
+ # Unusual path depth; keep the counter next to the state file.
87
+ return state_file.resolve().parent / ".context" / "task-tool-burst.json"
88
+ return root / ".context" / "task-tool-burst.json"
89
+
90
+
91
+ def load_counter(counter_file: Path) -> dict[str, Any]:
92
+ if not counter_file.exists():
93
+ return {"count": 0, "last_stage": None, "last_tool": None}
94
+ try:
95
+ data = json.loads(counter_file.read_text(encoding="utf-8"))
96
+ if isinstance(data, dict):
97
+ return data
98
+ except json.JSONDecodeError:
99
+ pass
100
+ return {"count": 0, "last_stage": None, "last_tool": None}
101
+
102
+
103
+ def save_counter(counter_file: Path, data: dict[str, Any]) -> None:
104
+ counter_file.parent.mkdir(parents=True, exist_ok=True)
105
+ tmp_fd, tmp_path = tempfile.mkstemp(dir=counter_file.parent, suffix=".json.tmp")
106
+ try:
107
+ with os.fdopen(tmp_fd, "w", encoding="utf-8") as f:
108
+ json.dump(data, f, ensure_ascii=False, indent=2)
109
+ os.replace(tmp_path, counter_file)
110
+ except Exception:
111
+ try:
112
+ os.close(tmp_fd)
113
+ except OSError:
114
+ pass
115
+ if os.path.exists(tmp_path):
116
+ os.unlink(tmp_path)
117
+ raise
118
+
119
+
120
+ def block(message: str) -> int:
121
+ print(f"BLOCKED: {message}", file=sys.stderr)
122
+ return 1
123
+
124
+
125
+ def allow() -> int:
126
+ return 0
127
+
128
+
129
+ def main(argv: list[str] | None = None) -> int:
130
+ parser = argparse.ArgumentParser(description="Lincoln task-tool guard")
131
+ parser.add_argument("--state-file", type=Path, default=None)
132
+ parser.add_argument("--tool-name", required=True)
133
+ parser.add_argument("--tool-args", default="{}")
134
+ parser.add_argument(
135
+ "--dialogue-stages",
136
+ help="Comma-separated list of stage IDs where task tools are forbidden",
137
+ )
138
+ parser.add_argument(
139
+ "--max-consecutive",
140
+ type=int,
141
+ default=DEFAULT_MAX_CONSECUTIVE,
142
+ help="Maximum allowed consecutive TaskCreate/TaskUpdate calls",
143
+ )
144
+ args = parser.parse_args(argv)
145
+
146
+ state_file = args.state_file
147
+ if state_file is None:
148
+ env_path = os.environ.get("LINCOLN_STATE_FILE")
149
+ if env_path:
150
+ state_file = Path(env_path)
151
+ elif resolve_state_path is not None:
152
+ state_file = resolve_state_path(None, Path(__file__).resolve().parents[1])
153
+ else:
154
+ state_file = Path.cwd() / ".claude" / "workflow-state.yaml"
155
+
156
+ state = load_state(state_file)
157
+ if state is None:
158
+ # Not a Lincoln project or not initialized yet; stay out of the way.
159
+ return allow()
160
+
161
+ current_stage = state.get("current_run", {}).get("current_stage") or "not_started"
162
+
163
+ # Reset the burst counter whenever a non-task tool is used.
164
+ if args.tool_name not in TASK_TOOLS:
165
+ counter_file = counter_file_path(state_file)
166
+ save_counter(
167
+ counter_file,
168
+ {"count": 0, "last_stage": current_stage, "last_tool": args.tool_name},
169
+ )
170
+ return allow()
171
+
172
+ # We are handling a task tool from here on.
173
+ override_stages = None
174
+ if args.dialogue_stages is not None:
175
+ override_stages = {s.strip() for s in args.dialogue_stages.split(",") if s.strip()}
176
+
177
+ root = resolve_project_root(state_file)
178
+ workflow_file = None
179
+ if root is not None:
180
+ workflow_file = root / ".claude" / "workflows" / "interview-to-knowledge.yaml"
181
+
182
+ dialogue = dialogue_stages(workflow_file, override_stages)
183
+
184
+ if current_stage in dialogue:
185
+ return block(
186
+ f"TaskCreate/TaskUpdate are not allowed in the dialogue stage '{current_stage}'. "
187
+ "Send your message to the user directly instead of using task tools as placeholders."
188
+ )
189
+
190
+ counter_file = counter_file_path(state_file)
191
+ counter = load_counter(counter_file)
192
+
193
+ if counter.get("last_stage") != current_stage or counter.get("last_tool") not in TASK_TOOLS:
194
+ counter = {"count": 1, "last_stage": current_stage, "last_tool": args.tool_name}
195
+ else:
196
+ counter = {
197
+ **counter,
198
+ "count": counter.get("count", 0) + 1,
199
+ }
200
+
201
+ save_counter(counter_file, counter)
202
+
203
+ if counter["count"] > args.max_consecutive:
204
+ return block(
205
+ f"Too many consecutive task-tool calls ({counter['count']}) in stage '{current_stage}'. "
206
+ "Perform a non-task action (e.g. Read or send a user message) before creating/updating more tasks."
207
+ )
208
+
209
+ return allow()
210
+
211
+
212
+ if __name__ == "__main__":
213
+ sys.exit(main())
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Track artifacts produced by side-effect tools and update workflow-state.yaml.
4
+
5
+ Usage:
6
+ python scripts/track-artifacts.py --state-file .claude/workflow-state.yaml \
7
+ --tool Write --args '{"file_path": "designs/foo/bar.md"}'
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import json
14
+ import re
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ import yaml
19
+
20
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
21
+ from scripts.lincoln_paths import get_process_slug
22
+
23
+
24
+ # Paths that are considered workflow artifacts (relative to project root).
25
+ ARTIFACT_PREFIXES = (
26
+ "interviews/",
27
+ "requirements/",
28
+ "designs/",
29
+ "openspec/",
30
+ "knowledge/",
31
+ ".github/linked-issues.yaml",
32
+ ".github/lincoln-sync-queue/",
33
+ )
34
+
35
+
36
+ def load_state(path: Path) -> dict:
37
+ return yaml.safe_load(path.read_text(encoding="utf-8"))
38
+
39
+
40
+ def save_state(path: Path, state: dict) -> None:
41
+ path.write_text(yaml.dump(state, allow_unicode=True, sort_keys=False), encoding="utf-8")
42
+
43
+
44
+ def extract_paths(tool: str, args: str, project_root: Path) -> list[str]:
45
+ """Extract relative file paths from tool arguments (best-effort)."""
46
+ paths: list[str] = []
47
+
48
+ # For Write/Edit the argument is JSON with file_path
49
+ if tool in ("Write", "Edit"):
50
+ try:
51
+ data = json.loads(args)
52
+ file_path = data.get("file_path") or data.get("path")
53
+ if file_path:
54
+ paths.append(str(file_path))
55
+ except json.JSONDecodeError:
56
+ pass
57
+
58
+ # For Bash, scan for common output paths in the command string
59
+ if tool == "Bash":
60
+ try:
61
+ data = json.loads(args)
62
+ command = data.get("command", "")
63
+ except json.JSONDecodeError:
64
+ command = args
65
+ # Match paths that look like output files
66
+ for match in re.finditer(r"(?:\s|=|>\s?)([\w\-./]+\.(?:md|yaml|yml|json|pen))", command):
67
+ paths.append(match.group(1))
68
+
69
+ # For Pencil MCP tools, we can't easily know exported files; skip.
70
+
71
+ # Normalize and filter to project-relative artifact paths.
72
+ result: list[str] = []
73
+ for p in paths:
74
+ raw = Path(p.strip())
75
+ if raw.is_absolute():
76
+ try:
77
+ p = str(raw.resolve().relative_to(project_root.resolve()))
78
+ except ValueError:
79
+ continue
80
+ else:
81
+ p = str(raw).lstrip("/")
82
+ if any(p.startswith(prefix) for prefix in ARTIFACT_PREFIXES) or "/recordings/" in p or "/requirements/" in p or "/designs/" in p or "/interviews/" in p or "/openspec/" in p:
83
+ result.append(p)
84
+ return result
85
+
86
+
87
+ def main() -> int:
88
+ parser = argparse.ArgumentParser()
89
+ parser.add_argument("--state-file", type=Path, required=True)
90
+ parser.add_argument("--tool", required=True)
91
+ parser.add_argument("--args", default="{}")
92
+ parser.add_argument("--project-root", type=Path, default=Path.cwd())
93
+ args = parser.parse_args()
94
+
95
+ if not args.state_file.exists():
96
+ return 0
97
+
98
+ state = load_state(args.state_file)
99
+ current_stage = state.get("current_run", {}).get("current_stage")
100
+ if not current_stage:
101
+ return 0
102
+
103
+ new_paths = extract_paths(args.tool, args.args, args.project_root)
104
+ if not new_paths:
105
+ return 0
106
+
107
+ if "nodes" in state:
108
+ matching = [n for n in state.setdefault("nodes", []) if n.get("stage_id") == current_stage]
109
+ if matching:
110
+ node = matching[-1]
111
+ else:
112
+ node = {
113
+ "stage_id": current_stage,
114
+ "node_id": f"{current_stage}-artifact-tracking",
115
+ "status": state.get("current_run", {}).get("status", "in_progress"),
116
+ "started_at": state.get("current_run", {}).get("last_updated_at"),
117
+ "completed_at": None,
118
+ "gate_passed": False,
119
+ "approved_by": None,
120
+ "artifacts": [],
121
+ "handoff_file": None,
122
+ }
123
+ state["nodes"].append(node)
124
+ existing = set(node.get("artifacts", []))
125
+ node["artifacts"] = sorted(existing | set(new_paths))
126
+ else:
127
+ stage_state = state.setdefault("stages", {}).setdefault(current_stage, {})
128
+ existing = set(stage_state.get("artifacts_produced", []))
129
+ stage_state["artifacts_produced"] = sorted(existing | set(new_paths))
130
+
131
+ state.setdefault("current_run", {}).setdefault("variables", {}).setdefault(
132
+ "process_slug",
133
+ get_process_slug(state, args.state_file),
134
+ )
135
+ save_state(args.state_file, state)
136
+ return 0
137
+
138
+
139
+ if __name__ == "__main__":
140
+ sys.exit(main())