claude-code-session-manager 0.111.0 → 0.113.0

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 (290) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/dist/assets/{AgentLibrary-rBO1daqM.js → AgentLibrary-wvrcLGGS.js} +2 -2
  3. package/dist/assets/{Button-DsEYRZg0.js → Button-B6h0F0XT.js} +1 -1
  4. package/dist/assets/{DataModel-D8sbvvBC.js → DataModel-C5pW9E3u.js} +1 -1
  5. package/dist/assets/{History-BEHLtxia.js → History-BV4e8YOM.js} +2 -2
  6. package/dist/assets/{Hooks-CKpvlXrg.js → Hooks-CrFSv4x1.js} +3 -3
  7. package/dist/assets/{Library-CeF4qh9d.js → Library-D-GIt3EY.js} +5 -5
  8. package/dist/assets/{ListDetail-DcfmxPFk.js → ListDetail-Bco_wfs6.js} +1 -1
  9. package/dist/assets/{MarkdownEditor-CvK1pyto.js → MarkdownEditor-C-yHSXNV.js} +1 -1
  10. package/dist/assets/{McpServers-D-h2ihzJ.js → McpServers-CfDekadG.js} +2 -2
  11. package/dist/assets/{Memory-BnF89x8v.js → Memory-CGGLexkR.js} +4 -4
  12. package/dist/assets/{Panel-CQY5zano.js → Panel-DGMEuYYG.js} +1 -1
  13. package/dist/assets/{Permissions-DLukmmsz.js → Permissions-Cx2zjtYZ.js} +3 -3
  14. package/dist/assets/{Plugins-0Pf0OZ4l.js → Plugins-Bc_W3IyR.js} +2 -2
  15. package/dist/assets/ProvenanceBadge-DiKAUR1u.js +1 -0
  16. package/dist/assets/RemoteMachines-8_77-WPD.js +3 -0
  17. package/dist/assets/{SaveBar-DjijiUb0.js → SaveBar-Blpzdq1R.js} +1 -1
  18. package/dist/assets/{Scheduler-Cm3EXZvE.js → Scheduler-Bd70AM4X.js} +9 -9
  19. package/dist/assets/{ScopeSwitcher-B-P-kSfq.js → ScopeSwitcher-wxN0h4YW.js} +1 -1
  20. package/dist/assets/Settings-CQ7DA3zW.js +4 -0
  21. package/dist/assets/{SkillReferenceGraph-Epv2GJmj.js → SkillReferenceGraph-DH1NLOiS.js} +1 -1
  22. package/dist/assets/Skills-CmyRyu4_.js +2 -0
  23. package/dist/assets/SystemPrompt-KAc3UMCd.js +1 -0
  24. package/dist/assets/TagLibrary-C8EvHnpg.js +1 -0
  25. package/dist/assets/{TiptapBody-DAMO3DhO.js → TiptapBody-d423mH6D.js} +1 -1
  26. package/dist/assets/{Toggle-Ba_f7q_m.js → Toggle-D1wsGE9S.js} +1 -1
  27. package/dist/assets/{ViewTabs-CGX6MAhc.js → ViewTabs-CjHw3kGY.js} +1 -1
  28. package/dist/assets/{historyTotals-vgfk8EOL.js → historyTotals-DFHzqLt7.js} +1 -1
  29. package/dist/assets/{index-CPieAFsl.js → index-Ci1u018F.js} +266 -266
  30. package/dist/assets/{settingsSchema-D0FxNrTy.js → settingsSchema-Ctf3pZVI.js} +1 -1
  31. package/dist/assets/{useAgentsDirWatch-DCu6BNYN.js → useAgentsDirWatch-B1NBmOAe.js} +1 -1
  32. package/dist/assets/{useRemotePeers-D4cdJDyh.js → useRemotePeers-BHjSJs9D.js} +1 -1
  33. package/dist/index.html +1 -1
  34. package/package.json +3 -2
  35. package/plugins/CLAUDE.md +5 -8
  36. package/plugins/session-manager-dev/.claude-plugin/plugin.json +3 -6
  37. package/plugins/session-manager-dev/skills/builder/1-classify-and-bump/SKILL.md +2 -2
  38. package/plugins/session-manager-dev/skills/builder/SKILL.md +2 -3
  39. package/plugins/session-manager-dev/skills/develop/SKILL.md +1 -1
  40. package/plugins/session-manager-dev/skills/develop/standards.md +3 -3
  41. package/plugins/session-manager-dev/skills/send-feedback/SKILL.md +13 -0
  42. package/scripts/README.md +3 -3
  43. package/scripts/audit-ops-hygiene.cjs +4 -4
  44. package/scripts/hooks/guard-destructive-git.cjs +4 -4
  45. package/scripts/hooks/guard-inline-implementation.cjs +1 -1
  46. package/scripts/hooks/guard-prd-writes.cjs +2 -2
  47. package/scripts/hooks/lib/guard-destructive-git-policy.cjs +1 -1
  48. package/scripts/mint-epic.cjs +2 -2
  49. package/scripts/scheduler-mcp-server.cjs +27 -9
  50. package/src/main/agentLibrary.cjs +5 -5
  51. package/src/main/agentsIpc.cjs +76 -0
  52. package/src/main/build-info.json +4 -4
  53. package/src/main/chatRunner.cjs +30 -13
  54. package/src/main/config.cjs +20 -3
  55. package/src/main/crashDiagnostics.cjs +2 -2
  56. package/src/main/docEdit.cjs +5 -5
  57. package/src/main/files.cjs +2 -2
  58. package/src/main/git.cjs +1 -1
  59. package/src/main/health.cjs +20 -18
  60. package/src/main/heapSnapshot.cjs +1 -1
  61. package/src/main/historyAggregator.cjs +2 -2
  62. package/src/main/index.cjs +50 -132
  63. package/src/main/ipcSchemas.cjs +13 -10
  64. package/src/main/lib/CLAUDE.md +48 -0
  65. package/src/main/lib/activeSessions.cjs +14 -1
  66. package/src/main/lib/classifyPromptTicket.cjs +2 -2
  67. package/src/main/lib/{agentModelResolve.cjs → claude/agentModelResolve.cjs} +7 -7
  68. package/src/main/lib/{claudeBin.cjs → claude/claudeBin.cjs} +4 -4
  69. package/src/main/lib/{effectiveModelInfo.cjs → claude/effectiveModelInfo.cjs} +9 -9
  70. package/src/main/lib/{modelCatalog.cjs → claude/modelCatalog.cjs} +3 -3
  71. package/src/main/lib/claude/rateLimitDetect.cjs +54 -0
  72. package/src/main/lib/{rateLimitWindow.cjs → claude/rateLimitWindow.cjs} +24 -12
  73. package/src/main/lib/{runClaudeP.cjs → claude/runClaudeP.cjs} +1 -1
  74. package/src/main/lib/credentials.cjs +18 -92
  75. package/src/main/lib/cwdClassify.cjs +1 -1
  76. package/src/main/lib/{activeIndexMerge.cjs → epic/activeIndexMerge.cjs} +3 -3
  77. package/src/main/lib/{activeIndexRebuild.cjs → epic/activeIndexRebuild.cjs} +1 -1
  78. package/src/main/lib/{delegationReadiness.cjs → epic/delegationReadiness.cjs} +21 -29
  79. package/src/main/lib/{epicContextDigest.cjs → epic/epicContextDigest.cjs} +1 -1
  80. package/src/main/lib/{epicDelegationStats.cjs → epic/epicDelegationStats.cjs} +2 -2
  81. package/src/main/lib/{epicMint.cjs → epic/epicMint.cjs} +11 -11
  82. package/src/main/lib/{epicSpawnPlan.cjs → epic/epicSpawnPlan.cjs} +3 -3
  83. package/src/main/lib/{epicStatusMirror.cjs → epic/epicStatusMirror.cjs} +2 -2
  84. package/src/main/lib/{epicTranscriptDiagnostic.cjs → epic/epicTranscriptDiagnostic.cjs} +2 -2
  85. package/src/main/lib/{epicTranscriptPath.cjs → epic/epicTranscriptPath.cjs} +2 -2
  86. package/src/main/lib/{epicValidationHook.cjs → epic/epicValidationHook.cjs} +3 -3
  87. package/src/main/lib/{epicWorktreeBoot.cjs → epic/epicWorktreeBoot.cjs} +1 -1
  88. package/src/main/lib/{epicWorktreeMerge.cjs → epic/epicWorktreeMerge.cjs} +3 -3
  89. package/src/main/lib/{epicWorktreeMint.cjs → epic/epicWorktreeMint.cjs} +3 -3
  90. package/src/main/lib/{epicWorktreeProjectConfig.cjs → epic/epicWorktreeProjectConfig.cjs} +4 -4
  91. package/src/main/lib/{promptSessionsCreateEpic.cjs → epic/promptSessionsCreateEpic.cjs} +4 -4
  92. package/src/main/lib/{crossProjectFeedback.cjs → feedback-remote/crossProjectFeedback.cjs} +114 -16
  93. package/src/main/lib/{customerFeedbackClient.cjs → feedback-remote/customerFeedbackClient.cjs} +3 -3
  94. package/src/main/lib/{customerFeedbackInbox.cjs → feedback-remote/customerFeedbackInbox.cjs} +2 -2
  95. package/src/main/lib/{customerFeedbackIpc.cjs → feedback-remote/customerFeedbackIpc.cjs} +1 -1
  96. package/src/main/lib/feedback-remote/humanPromptLedger.cjs +93 -0
  97. package/src/main/lib/{localAdminHttp.cjs → feedback-remote/localAdminHttp.cjs} +2 -2
  98. package/src/main/lib/{macroAdminRoutes.cjs → feedback-remote/macroAdminRoutes.cjs} +1 -1
  99. package/src/main/lib/{projectHomeAdminRoutes.cjs → feedback-remote/projectHomeAdminRoutes.cjs} +4 -4
  100. package/src/main/lib/{relayClientAuth.cjs → feedback-remote/relayClientAuth.cjs} +2 -2
  101. package/src/main/lib/{remoteClientSessions.cjs → feedback-remote/remoteClientSessions.cjs} +1 -1
  102. package/src/main/lib/{gitWorktree.cjs → git/gitWorktree.cjs} +7 -7
  103. package/src/main/lib/historyRollup.cjs +1 -1
  104. package/src/main/lib/importReferences.cjs +1 -1
  105. package/src/main/lib/machineProfile.cjs +1 -1
  106. package/src/main/lib/mcpToolCatalog.cjs +9 -3
  107. package/src/main/lib/openExternalApp.cjs +1 -1
  108. package/src/main/lib/opsOwnership.cjs +3 -3
  109. package/src/main/lib/{agentPersonaSchema.cjs → persona-macro/agentPersonaSchema.cjs} +2 -2
  110. package/src/main/lib/{macroLibrary.cjs → persona-macro/macroLibrary.cjs} +5 -5
  111. package/src/main/lib/{personaMerge.cjs → persona-macro/personaMerge.cjs} +4 -4
  112. package/src/main/lib/{depSlugResolve.cjs → prd/depSlugResolve.cjs} +16 -1
  113. package/src/main/lib/{prdAdminRoutes.cjs → prd/prdAdminRoutes.cjs} +7 -7
  114. package/src/main/lib/{prdAgentType.cjs → prd/prdAgentType.cjs} +3 -3
  115. package/src/main/lib/{prdCreate.cjs → prd/prdCreate.cjs} +12 -12
  116. package/src/main/lib/{prdDeclaredPaths.cjs → prd/prdDeclaredPaths.cjs} +1 -1
  117. package/src/main/lib/{prdGateFiles.cjs → prd/prdGateFiles.cjs} +1 -1
  118. package/src/main/lib/{prdLocations.cjs → prd/prdLocations.cjs} +2 -2
  119. package/src/main/lib/{prdMigration.cjs → prd/prdMigration.cjs} +3 -3
  120. package/src/main/lib/prereqs.cjs +2 -2
  121. package/src/main/lib/{instanceLock.cjs → process/instanceLock.cjs} +4 -11
  122. package/src/main/lib/{pidAlive.cjs → process/pidAlive.cjs} +1 -1
  123. package/src/main/lib/{procName.cjs → process/procName.cjs} +2 -2
  124. package/src/main/lib/{timeoutShim.cjs → process/timeoutShim.cjs} +1 -1
  125. package/src/main/lib/projectRootResolve.cjs +1 -1
  126. package/src/main/lib/promptSessionSchema.cjs +1 -1
  127. package/src/main/lib/{definitionOfDone.cjs → scheduler/definitionOfDone.cjs} +6 -12
  128. package/src/main/lib/scheduler/finalizeReason.cjs +30 -0
  129. package/src/main/lib/{fixChainDepth.cjs → scheduler/fixChainDepth.cjs} +1 -1
  130. package/src/main/lib/scheduler/flatSweepThrottle.cjs +17 -0
  131. package/src/main/lib/{jobDirtFilter.cjs → scheduler/jobDirtFilter.cjs} +1 -1
  132. package/src/main/lib/{jobSupervisorRecord.cjs → scheduler/jobSupervisorRecord.cjs} +1 -1
  133. package/src/main/lib/{launchFailure.cjs → scheduler/launchFailure.cjs} +2 -2
  134. package/src/main/lib/{queueStore.cjs → scheduler/queueStore.cjs} +6 -6
  135. package/src/main/lib/{quietMachineLease.cjs → scheduler/quietMachineLease.cjs} +1 -1
  136. package/src/main/lib/{rcaReport.cjs → scheduler/rcaReport.cjs} +4 -4
  137. package/src/main/lib/{reaperHelpers.cjs → scheduler/reaperHelpers.cjs} +3 -3
  138. package/src/main/lib/{reservationExpiry.cjs → scheduler/reservationExpiry.cjs} +4 -11
  139. package/src/main/lib/{reviewNotice.cjs → scheduler/reviewNotice.cjs} +1 -1
  140. package/src/main/lib/{runLogRetention.cjs → scheduler/runLogRetention.cjs} +34 -13
  141. package/src/main/lib/{scheduleJobTransitions.cjs → scheduler/scheduleJobTransitions.cjs} +3 -3
  142. package/src/main/lib/{schedulerBatch.cjs → scheduler/schedulerBatch.cjs} +9 -13
  143. package/src/main/lib/{schedulerConfig.cjs → scheduler/schedulerConfig.cjs} +1 -1
  144. package/src/main/lib/{schedulerRuntimeState.cjs → scheduler/schedulerRuntimeState.cjs} +1 -1
  145. package/src/main/lib/{sessionSlots.cjs → scheduler/sessionSlots.cjs} +2 -2
  146. package/src/main/lib/{upgradeDrain.cjs → scheduler/upgradeDrain.cjs} +8 -11
  147. package/src/main/lib/{watchdogHelpers.cjs → scheduler/watchdogHelpers.cjs} +5 -19
  148. package/src/main/lib/{auditLog.cjs → telemetry/auditLog.cjs} +1 -1
  149. package/src/main/lib/{opsErrorLog.cjs → telemetry/opsErrorLog.cjs} +4 -4
  150. package/src/main/lib/{telemetryBacklog.cjs → telemetry/telemetryBacklog.cjs} +4 -4
  151. package/src/main/lib/{telemetryBoot.cjs → telemetry/telemetryBoot.cjs} +1 -1
  152. package/src/main/lib/{telemetryClient.cjs → telemetry/telemetryClient.cjs} +12 -8
  153. package/src/main/lib/{telemetrySettings.cjs → telemetry/telemetrySettings.cjs} +1 -1
  154. package/src/main/lib/{atomicFs.cjs → util/atomicFs.cjs} +42 -0
  155. package/src/main/lib/{loopDelay.cjs → util/loopDelay.cjs} +1 -1
  156. package/src/main/logs.cjs +1 -1
  157. package/src/main/mcpStatus.cjs +2 -2
  158. package/src/main/memoryAggregate.cjs +3 -3
  159. package/src/main/memoryTool.cjs +1 -1
  160. package/src/main/otel.cjs +2 -2
  161. package/src/main/pluginInstall.cjs +29 -3
  162. package/src/main/projectBrief.cjs +6 -6
  163. package/src/main/projectPages.cjs +1 -1
  164. package/src/main/promptSessionEvents.cjs +2 -2
  165. package/src/main/pty.cjs +7 -7
  166. package/src/main/queueOps.cjs +6 -6
  167. package/src/main/remoteClient.cjs +4 -4
  168. package/src/main/remoteClientIpc.cjs +1 -1
  169. package/src/main/runVerify.cjs +6 -3
  170. package/src/main/scheduler/CLAUDE.md +26 -0
  171. package/src/main/scheduler/budget.cjs +576 -0
  172. package/src/main/scheduler/gitTreeGuard.cjs +672 -0
  173. package/src/main/scheduler/prdParser.cjs +4 -4
  174. package/src/main/scheduler/prdPaths.cjs +487 -0
  175. package/src/main/scheduler/reviewLadder.cjs +1011 -0
  176. package/src/main/scheduler/starvation.cjs +390 -0
  177. package/src/main/scheduler.cjs +331 -3320
  178. package/src/main/seedAgentPersonas.cjs +5 -5
  179. package/src/main/seedDevPlugin.cjs +70 -3
  180. package/src/main/seedSchedulerMcp.cjs +2 -2
  181. package/src/main/supervisor.cjs +50 -27
  182. package/src/main/telemetryIpc.cjs +51 -0
  183. package/src/main/transcripts.cjs +3 -3
  184. package/src/main/updaterIpc.cjs +1 -1
  185. package/src/main/usage.cjs +1 -1
  186. package/src/main/watchers.cjs +1 -1
  187. package/src/main/webRemote.cjs +22 -7
  188. package/src/preload/api.d.ts +14 -8
  189. package/src/preload/index.cjs +9 -3
  190. package/dist/assets/ProvenanceBadge-vC7Tj-JP.js +0 -1
  191. package/dist/assets/RemoteMachines-CWnKbFHc.js +0 -3
  192. package/dist/assets/Settings-DYDlp2m_.js +0 -4
  193. package/dist/assets/Skills-DHO0N0C2.js +0 -2
  194. package/dist/assets/SystemPrompt-DxeNofL9.js +0 -1
  195. package/dist/assets/TagLibrary-QOAs7ISO.js +0 -1
  196. package/plugins/session-manager-dev/skills/blog-for-project-feature/0-select/SKILL.md +0 -39
  197. package/plugins/session-manager-dev/skills/blog-for-project-feature/1-research/SKILL.md +0 -37
  198. package/plugins/session-manager-dev/skills/blog-for-project-feature/2-storyboard/SKILL.md +0 -64
  199. package/plugins/session-manager-dev/skills/blog-for-project-feature/3-capture/SKILL.md +0 -52
  200. package/plugins/session-manager-dev/skills/blog-for-project-feature/4-draft-copy/SKILL.md +0 -37
  201. package/plugins/session-manager-dev/skills/blog-for-project-feature/5-illustrate/SKILL.md +0 -50
  202. package/plugins/session-manager-dev/skills/blog-for-project-feature/6-compose/SKILL.md +0 -51
  203. package/plugins/session-manager-dev/skills/blog-for-project-feature/7-review/SKILL.md +0 -54
  204. package/plugins/session-manager-dev/skills/blog-for-project-feature/SKILL.md +0 -144
  205. package/plugins/session-manager-dev/skills/blog-for-project-feature/feature.config.yaml +0 -90
  206. package/plugins/session-manager-dev/skills/discover-features/SKILL.md +0 -95
  207. package/plugins/session-manager-dev/skills/explain-to-me/SKILL.md +0 -205
  208. package/plugins/session-manager-dev/skills/explain-to-me/assets/style-reference.html +0 -163
  209. package/plugins/session-manager-dev/skills/find-opportunity/SKILL.md +0 -83
  210. package/plugins/session-manager-dev/skills/issue-address/0-select/0-fetch-pool/SKILL.md +0 -38
  211. package/plugins/session-manager-dev/skills/issue-address/0-select/1-reject-fixed/SKILL.md +0 -64
  212. package/plugins/session-manager-dev/skills/issue-address/0-select/2-reject-claimed/SKILL.md +0 -71
  213. package/plugins/session-manager-dev/skills/issue-address/0-select/3-rank-impact/SKILL.md +0 -39
  214. package/plugins/session-manager-dev/skills/issue-address/0-select/SKILL.md +0 -75
  215. package/plugins/session-manager-dev/skills/issue-address/1-confirm-open/SKILL.md +0 -39
  216. package/plugins/session-manager-dev/skills/issue-address/2-claim/SKILL.md +0 -57
  217. package/plugins/session-manager-dev/skills/issue-address/3-reproduce/SKILL.md +0 -40
  218. package/plugins/session-manager-dev/skills/issue-address/4-fix/SKILL.md +0 -33
  219. package/plugins/session-manager-dev/skills/issue-address/5-verify/SKILL.md +0 -40
  220. package/plugins/session-manager-dev/skills/issue-address/SKILL.md +0 -120
  221. package/plugins/session-manager-dev/skills/local-project-health/SKILL.md +0 -58
  222. package/plugins/session-manager-dev/skills/memory-sanitation/SKILL.md +0 -147
  223. package/plugins/session-manager-dev/skills/ops-sweep/SKILL.md +0 -137
  224. package/plugins/session-manager-dev/skills/pr-review-sweep/0-fetch/SKILL.md +0 -41
  225. package/plugins/session-manager-dev/skills/pr-review-sweep/1-classify/SKILL.md +0 -56
  226. package/plugins/session-manager-dev/skills/pr-review-sweep/2-check-fixed/SKILL.md +0 -49
  227. package/plugins/session-manager-dev/skills/pr-review-sweep/3-queue/SKILL.md +0 -57
  228. package/plugins/session-manager-dev/skills/pr-review-sweep/4-land-and-resolve/SKILL.md +0 -60
  229. package/plugins/session-manager-dev/skills/pr-review-sweep/SKILL.md +0 -124
  230. package/plugins/session-manager-dev/skills/pr-signal/SKILL.md +0 -67
  231. package/plugins/session-manager-dev/skills/project-status/SKILL.md +0 -243
  232. package/plugins/session-manager-dev/skills/requesting-code-review/SKILL.md +0 -98
  233. package/plugins/session-manager-dev/skills/requesting-code-review/code-reviewer.md +0 -146
  234. package/src/main/lib/rateLimitDetect.cjs +0 -35
  235. /package/src/main/lib/{agentEffortResolve.cjs → claude/agentEffortResolve.cjs} +0 -0
  236. /package/src/main/lib/{claudeCliCaps.cjs → claude/claudeCliCaps.cjs} +0 -0
  237. /package/src/main/lib/{usageCircuit.cjs → claude/usageCircuit.cjs} +0 -0
  238. /package/src/main/lib/{epicSpawnCwd.cjs → epic/epicSpawnCwd.cjs} +0 -0
  239. /package/src/main/lib/{customerFeedbackAdminRoutes.cjs → feedback-remote/customerFeedbackAdminRoutes.cjs} +0 -0
  240. /package/src/main/lib/{remoteE2eCrypto.cjs → feedback-remote/remoteE2eCrypto.cjs} +0 -0
  241. /package/src/main/lib/{branchSweep.cjs → git/branchSweep.cjs} +0 -0
  242. /package/src/main/lib/{gitExec.cjs → git/gitExec.cjs} +0 -0
  243. /package/src/main/lib/{jobWorktree.cjs → git/jobWorktree.cjs} +0 -0
  244. /package/src/main/lib/{jobWorktreeBootLive.cjs → git/jobWorktreeBootLive.cjs} +0 -0
  245. /package/src/main/lib/{jobWorktreeTerminalOrphanLive.cjs → git/jobWorktreeTerminalOrphanLive.cjs} +0 -0
  246. /package/src/main/lib/{builtinMacros.cjs → persona-macro/builtinMacros.cjs} +0 -0
  247. /package/src/main/lib/{personaImportHealth.cjs → persona-macro/personaImportHealth.cjs} +0 -0
  248. /package/src/main/lib/{shippedPersonaSeeds.cjs → persona-macro/shippedPersonaSeeds.cjs} +0 -0
  249. /package/src/main/lib/{planValidator.cjs → prd/planValidator.cjs} +0 -0
  250. /package/src/main/lib/{prdAuthoringSeed.cjs → prd/prdAuthoringSeed.cjs} +0 -0
  251. /package/src/main/lib/{prdDisposition.cjs → prd/prdDisposition.cjs} +0 -0
  252. /package/src/main/lib/{prdFrontmatter.cjs → prd/prdFrontmatter.cjs} +0 -0
  253. /package/src/main/lib/{prdSizing.cjs → prd/prdSizing.cjs} +0 -0
  254. /package/src/main/lib/{workTypeLibrary.cjs → prd/workTypeLibrary.cjs} +0 -0
  255. /package/src/main/lib/{childWithLog.cjs → process/childWithLog.cjs} +0 -0
  256. /package/src/main/lib/{cleanEnv.cjs → process/cleanEnv.cjs} +0 -0
  257. /package/src/main/lib/{defaultShell.cjs → process/defaultShell.cjs} +0 -0
  258. /package/src/main/lib/{killTree.cjs → process/killTree.cjs} +0 -0
  259. /package/src/main/lib/{loginShellPath.cjs → process/loginShellPath.cjs} +0 -0
  260. /package/src/main/lib/{procIdentity.cjs → process/procIdentity.cjs} +0 -0
  261. /package/src/main/lib/{singleInstanceGuard.cjs → process/singleInstanceGuard.cjs} +0 -0
  262. /package/src/main/lib/{smProcNames.cjs → process/smProcNames.cjs} +0 -0
  263. /package/src/main/lib/{timeoutShimScript.cjs → process/timeoutShimScript.cjs} +0 -0
  264. /package/src/main/lib/{winSpawn.cjs → process/winSpawn.cjs} +0 -0
  265. /package/src/main/lib/{adoptedRunSupervisor.cjs → scheduler/adoptedRunSupervisor.cjs} +0 -0
  266. /package/src/main/lib/{dispatchLoop.cjs → scheduler/dispatchLoop.cjs} +0 -0
  267. /package/src/main/lib/{dodDrainHook.cjs → scheduler/dodDrainHook.cjs} +0 -0
  268. /package/src/main/lib/{fixPlanSlug.cjs → scheduler/fixPlanSlug.cjs} +0 -0
  269. /package/src/main/lib/{gateAuthority.cjs → scheduler/gateAuthority.cjs} +0 -0
  270. /package/src/main/lib/{landedSinceRun.cjs → scheduler/landedSinceRun.cjs} +0 -0
  271. /package/src/main/lib/{loadGate.cjs → scheduler/loadGate.cjs} +0 -0
  272. /package/src/main/lib/{needsReviewLedger.cjs → scheduler/needsReviewLedger.cjs} +0 -0
  273. /package/src/main/lib/{queueHealth.cjs → scheduler/queueHealth.cjs} +0 -0
  274. /package/src/main/lib/{queueHistory.cjs → scheduler/queueHistory.cjs} +0 -0
  275. /package/src/main/lib/{scheduleJobSchema.cjs → scheduler/scheduleJobSchema.cjs} +0 -0
  276. /package/src/main/lib/{schedulerPaths.cjs → scheduler/schedulerPaths.cjs} +0 -0
  277. /package/src/main/lib/{terminalRunOutcome.cjs → scheduler/terminalRunOutcome.cjs} +0 -0
  278. /package/src/main/lib/{validationSentinels.cjs → scheduler/validationSentinels.cjs} +0 -0
  279. /package/src/main/lib/{crashTelemetry.cjs → telemetry/crashTelemetry.cjs} +0 -0
  280. /package/src/main/lib/{otlpTraceExporter.cjs → telemetry/otlpTraceExporter.cjs} +0 -0
  281. /package/src/main/lib/{telemetryConsent.cjs → telemetry/telemetryConsent.cjs} +0 -0
  282. /package/src/main/lib/{telemetryCounters.cjs → telemetry/telemetryCounters.cjs} +0 -0
  283. /package/src/main/lib/{encodeCwd.cjs → util/encodeCwd.cjs} +0 -0
  284. /package/src/main/lib/{expandHome.cjs → util/expandHome.cjs} +0 -0
  285. /package/src/main/lib/{extractJson.cjs → util/extractJson.cjs} +0 -0
  286. /package/src/main/lib/{fileTail.cjs → util/fileTail.cjs} +0 -0
  287. /package/src/main/lib/{headTailBuffer.cjs → util/headTailBuffer.cjs} +0 -0
  288. /package/src/main/lib/{kebabCase.cjs → util/kebabCase.cjs} +0 -0
  289. /package/src/main/lib/{lruCache.cjs → util/lruCache.cjs} +0 -0
  290. /package/src/main/lib/{withTimeout.cjs → util/withTimeout.cjs} +0 -0
@@ -26,9 +26,9 @@
26
26
  const fs = require('node:fs');
27
27
  const path = require('node:path');
28
28
  const os = require('node:os');
29
- const { splitFrontmatter } = require('./prdFrontmatter.cjs');
30
- const { resolveEpicTranscriptPath } = require('./epicTranscriptPath.cjs');
31
- const schedulerPaths = require('./schedulerPaths.cjs');
29
+ const { splitFrontmatter } = require('../prd/prdFrontmatter.cjs');
30
+ const { resolveEpicTranscriptPath } = require('../epic/epicTranscriptPath.cjs');
31
+ const schedulerPaths = require('../scheduler/schedulerPaths.cjs');
32
32
 
33
33
  // Deliberately stays a FIXED alias set (reviewed against the catalog-driven raw picker): it answers
34
34
  // "alias vs concrete id", not "which options to offer", so live catalog ids must not enter it.
@@ -70,7 +70,7 @@ function isConcreteModelId(modelAlias) {
70
70
  */
71
71
  function computeEffortEnvReachable(deps) {
72
72
  try {
73
- const { cleanChildEnv } = deps.cleanEnv || require('./cleanEnv.cjs');
73
+ const { cleanChildEnv } = deps.cleanEnv || require('../process/cleanEnv.cjs');
74
74
  const probe = cleanChildEnv({ CLAUDE_EFFORT: 'high', CLAUDE_CODE_EFFORT_LEVEL: 'high' });
75
75
  return Boolean(probe.CLAUDE_EFFORT || probe.CLAUDE_CODE_EFFORT_LEVEL);
76
76
  } catch {
@@ -93,7 +93,7 @@ function computeEffortEnvReachable(deps) {
93
93
  * for the same dangling reference.
94
94
  */
95
95
  async function resolvePersonaModelAlias(cwd, agentType, deps) {
96
- const getPersonaBody = deps.getPersonaBody || require('../agentLibrary.cjs').getPersonaBody;
96
+ const getPersonaBody = deps.getPersonaBody || require('../../agentLibrary.cjs').getPersonaBody;
97
97
  // The overlay-vs-global provenance question itself ("which of
98
98
  // resolvePersonaPaths' two candidates won") is answered by
99
99
  // agentModelResolve.cjs's isProjectOverlayPersonaPath, not re-derived here
@@ -125,7 +125,7 @@ async function resolvePersonaModelAlias(cwd, agentType, deps) {
125
125
 
126
126
  /** Best-effort read of `<cwd>/session-manager-operations/scheduler/state/queue.json`'s hot jobs[]. */
127
127
  function readHotJobsForCwd(cwd, deps) {
128
- const { projectQueuePath } = deps.queueStore || require('./queueStore.cjs');
128
+ const { projectQueuePath } = deps.queueStore || require('../scheduler/queueStore.cjs');
129
129
  try {
130
130
  const data = JSON.parse(fs.readFileSync(projectQueuePath(cwd), 'utf8'));
131
131
  return Array.isArray(data.jobs) ? data.jobs : [];
@@ -136,7 +136,7 @@ function readHotJobsForCwd(cwd, deps) {
136
136
 
137
137
  /** Best-effort read of `<cwd>/session-manager-operations/scheduler/state/history.jsonl`'s archived rows. */
138
138
  function readHistoryEntriesForCwd(cwd, deps) {
139
- const { projectHistoryPath } = deps.queueStore || require('./queueStore.cjs');
139
+ const { projectHistoryPath } = deps.queueStore || require('../scheduler/queueStore.cjs');
140
140
  let text;
141
141
  try {
142
142
  text = fs.readFileSync(projectHistoryPath(cwd), 'utf8');
@@ -246,7 +246,7 @@ function findLatestSchedulerModel(cwd, agentType, deps) {
246
246
  * O(min(transcriptSize, cap)) per transcript read.
247
247
  */
248
248
  function findLatestTranscriptModel(cwd, agentType, deps) {
249
- const readActiveIndex = deps.readActiveIndex || require('./epicMint.cjs').readActiveIndex;
249
+ const readActiveIndex = deps.readActiveIndex || require('../epic/epicMint.cjs').readActiveIndex;
250
250
  let sessions;
251
251
  try {
252
252
  ({ sessions } = readActiveIndex(cwd));
@@ -296,7 +296,7 @@ async function resolveEffectiveModelInfo({ cwd, agentType, deps = {} } = {}) {
296
296
  if (!cwd || !agentType) return miss;
297
297
 
298
298
  try {
299
- const { projectRootOf } = deps.activeSessions || require('./activeSessions.cjs');
299
+ const { projectRootOf } = deps.activeSessions || require('../activeSessions.cjs');
300
300
  const normalizedCwd = projectRootOf(path.resolve(cwd));
301
301
  if (!normalizedCwd) return miss;
302
302
 
@@ -29,7 +29,7 @@ const SCAN_OVERLAP_BYTES = 128;
29
29
  const MODEL_ID_RE = /claude-(?:opus|sonnet|haiku|fable)-[0-9][0-9-]*/g;
30
30
  // A real CLI binary is tens of MB; test stubs and launcher shims are tiny shell scripts.
31
31
  const MIN_SCAN_BYTES = 1024 * 1024;
32
- const SCHEMA_PATH = path.join(__dirname, '..', '..', 'renderer', 'data', 'claude-settings-schema.json');
32
+ const SCHEMA_PATH = path.join(__dirname, '..', '..', '..', 'renderer', 'data', 'claude-settings-schema.json');
33
33
 
34
34
  const CATALOG_FLOOR = Object.freeze({
35
35
  aliases: Object.freeze(['haiku', 'sonnet', 'opus', 'fable']),
@@ -118,7 +118,7 @@ function readAvailableModels(cwd, d, fsImpl) {
118
118
  const home = d.homeDir ? d.homeDir() : os.homedir();
119
119
  let root = null;
120
120
  if (cwd) {
121
- try { root = (d.projectRootOf || require('./activeSessions.cjs').projectRootOf)(path.resolve(cwd)); } catch { root = path.resolve(cwd); }
121
+ try { root = (d.projectRootOf || require('../activeSessions.cjs').projectRootOf)(path.resolve(cwd)); } catch { root = path.resolve(cwd); }
122
122
  }
123
123
  const files = [path.join(home, '.claude', 'settings.json')];
124
124
  if (root) files.push(path.join(root, '.claude', 'settings.json'), path.join(root, '.claude', 'settings.local.json'));
@@ -221,7 +221,7 @@ async function doResolve({ cwd, force, deps }) {
221
221
  };
222
222
  // Unknown version = unknown cache key: writing it would stamp `claudeVersion: null` over a real entry.
223
223
  if (!degraded && cacheFile && claudeVersion) {
224
- try { await (d.writeJson || require('../config.cjs').writeJson)(cacheFile, catalog); } catch { /* cache is best-effort */ }
224
+ try { await (d.writeJson || require('../../config.cjs').writeJson)(cacheFile, catalog); } catch { /* cache is best-effort */ }
225
225
  }
226
226
  return catalog;
227
227
  }
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * rateLimitDetect.cjs — the single source of truth for "did this claude -p
5
+ * log tail show a rate-limit death". Shared by spawnJob() (a still-running
6
+ * process that just exited) and reapDeadRunningJobs()/classifyRunOutcome()
7
+ * (a process the reaper found already dead) — see PRD 1117. Before this,
8
+ * only spawnJob checked this; the reaper had no rate-limit branch at all,
9
+ * so a rate-limited exit that the reaper won the race to finalize got
10
+ * stamped terminal 'failed' instead of retryable 'pending'. There must
11
+ * never be a second, independently-drifting regex set at either call site.
12
+ */
13
+
14
+ const { readTail } = require('../util/fileTail.cjs');
15
+
16
+ /** Scan the tail of a job's log for a GENUINE rate-limit rejection. Parses the
17
+ * last 16 KB line by line as JSON (non-JSON / partial lines are skipped) and
18
+ * returns true only for: (a) a rate_limit_event whose rate_limit_info.status
19
+ * is 'rejected'; (b) a result event with api_error_status 429; (c) an
20
+ * is_error result event whose `result` text is the CLI's limit message
21
+ * ("You've hit your ... limit" / "You've reached your ... limit").
22
+ *
23
+ * Never substring-match the raw text: informational rate_limit_event lines
24
+ * (status 'allowed' / 'allowed_warning') carry rateLimitType on every run, so
25
+ * the old substring match turned any non-zero exit (e.g. a watchdog SIGTERM,
26
+ * exit 143) into a machine-wide rate_limit pause (2026-10-11 incident: 4-day
27
+ * pause at 58% weekly). Tool output may also mention 429 or "rate limit". */
28
+ function detectRateLimitInLog(logPath) {
29
+ try {
30
+ const text = readTail(logPath, 16384);
31
+ if (!text) return false;
32
+ for (const line of text.split('\n')) {
33
+ const t = line.trim();
34
+ if (!t || t[0] !== '{') continue;
35
+ let ev;
36
+ try { ev = JSON.parse(t); } catch { continue; }
37
+ if (!ev || typeof ev !== 'object') continue;
38
+ if (ev.type === 'rate_limit_event') {
39
+ if (ev.rate_limit_info && ev.rate_limit_info.status === 'rejected') return true;
40
+ } else if (ev.type === 'result') {
41
+ if (ev.api_error_status === 429) return true;
42
+ if (ev.is_error === true && typeof ev.result === 'string'
43
+ && (/You'?ve hit your .*limit/.test(ev.result) || /You'?ve reached your .* limit/.test(ev.result))) {
44
+ return true;
45
+ }
46
+ }
47
+ }
48
+ return false;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ module.exports = { detectRateLimitInLog };
@@ -13,7 +13,7 @@
13
13
  * unit-testable without scheduler.cjs (mirrors reaperHelpers.cjs's split).
14
14
  */
15
15
 
16
- const { readTail } = require('./fileTail.cjs');
16
+ const { readTail } = require('../util/fileTail.cjs');
17
17
 
18
18
  // Authoritative window block rides on the LAST rate_limit_event in the log
19
19
  // (often the one attached to the final result), so mirror classifyRunOutcome's
@@ -21,17 +21,21 @@ const { readTail } = require('./fileTail.cjs');
21
21
  const TAIL_BYTES = 64 * 1024;
22
22
 
23
23
  /**
24
- * Scans a run log's tail for rate_limit_event entries and returns the
25
- * UNIX-seconds resetsAt of the window that actually bound (utilization >= 1.0)
26
- * in the LAST such event. When multiple windows bind in that event, the
27
- * latest resetsAt wins. Returns null when no rate_limit_event is found, or
28
- * none of its windows are at/above 1.0 utilization.
24
+ * Scans a run log's tail for rate_limit_event entries. The REJECTED event is
25
+ * authoritative (2026-10-11 incident: an allowed-only log with a seven_day
26
+ * window pause landed on the seven_day reset 4 days out): if any event has
27
+ * status 'rejected', the LAST one's reset wins — rate_limit_info.resetsAt, else
28
+ * unifiedWindows[rateLimitType].resetsAt. Otherwise, a last event carrying a
29
+ * status (allowed / allowed_warning) yields null, even if a window shows
30
+ * utilization >= 1.0 (overage is not a rejection). Legacy: a last event with
31
+ * no status falls back to the latest resetsAt among windows at utilization >= 1.0.
29
32
  */
30
33
  function resolveBindingRateLimitReset(logPath) {
31
34
  const text = readTail(logPath, TAIL_BYTES);
32
35
  if (!text) return null;
33
36
 
34
- let lastWindows = null;
37
+ let lastRejected = null;
38
+ let lastEvent = null;
35
39
  for (const line of text.split('\n')) {
36
40
  const trimmed = line.trim();
37
41
  if (!trimmed || trimmed[0] !== '{') continue;
@@ -41,12 +45,20 @@ function resolveBindingRateLimitReset(logPath) {
41
45
  } catch {
42
46
  continue;
43
47
  }
44
- const windows = event?.rate_limit_info?.unifiedWindows;
45
- if (event?.type === 'rate_limit_event' && windows && typeof windows === 'object') {
46
- lastWindows = windows;
47
- }
48
+ if (event?.type !== 'rate_limit_event' || !event.rate_limit_info) continue;
49
+ lastEvent = event.rate_limit_info;
50
+ if (lastEvent.status === 'rejected') lastRejected = lastEvent;
51
+ }
52
+
53
+ if (lastRejected) {
54
+ if (typeof lastRejected.resetsAt === 'number') return lastRejected.resetsAt;
55
+ const typed = lastRejected.unifiedWindows?.[lastRejected.rateLimitType]?.resetsAt;
56
+ return typeof typed === 'number' ? typed : null;
48
57
  }
49
- if (!lastWindows) return null;
58
+ if (!lastEvent || lastEvent.status !== undefined) return null;
59
+
60
+ const lastWindows = lastEvent.unifiedWindows;
61
+ if (!lastWindows || typeof lastWindows !== 'object') return null;
50
62
 
51
63
  let latestBoundReset = null;
52
64
  for (const window of Object.values(lastWindows)) {
@@ -17,7 +17,7 @@
17
17
  const { spawn } = require('node:child_process');
18
18
  const { resolveClaudeBin, claudeSpawnTarget } = require('./claudeBin.cjs');
19
19
  const { headlessPermissionArgs, ensureCliCapsProbed } = require('./claudeCliCaps.cjs');
20
- const { withProcRole } = require('./cleanEnv.cjs');
20
+ const { withProcRole } = require('../process/cleanEnv.cjs');
21
21
 
22
22
  const MAX_OUT_BYTES = 8 * 1024 * 1024;
23
23
 
@@ -5,9 +5,8 @@ const fs = require('node:fs');
5
5
  const path = require('node:path');
6
6
  const os = require('node:os');
7
7
  const { spawn, execFileSync } = require('node:child_process');
8
- const { cleanChildEnv, pathWithUserBins } = require('./cleanEnv.cjs');
9
- const atomicFs = require('./atomicFs.cjs');
10
- const { resolveClaudeBin, claudeSpawnTarget } = require('./claudeBin.cjs');
8
+ const { cleanChildEnv, pathWithUserBins } = require('./process/cleanEnv.cjs');
9
+ const { resolveClaudeBin, claudeSpawnTarget } = require('./claude/claudeBin.cjs');
11
10
 
12
11
  const CREDS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
13
12
 
@@ -47,22 +46,9 @@ function keychainAccount() {
47
46
  return os.userInfo().username;
48
47
  }
49
48
 
50
- /** Write the credential JSON back into the Keychain (-U upserts in place). */
51
- function writeKeychainRaw(value) {
52
- execFileSync(
53
- 'security',
54
- ['add-generic-password', '-U', '-s', KEYCHAIN_SERVICE, '-a', keychainAccount(), '-w', value],
55
- { timeout: 10_000, stdio: 'ignore' },
56
- );
57
- }
58
49
  const REFRESH_LOG_PATH = path.join(os.homedir(), '.claude', 'session-manager', 'credential-refresh.log');
59
50
  const REFRESH_LOG_MAX_BYTES = 100 * 1024;
60
51
 
61
- // Standard OAuth 2.0 refresh grant — endpoint discovered from Claude Code CLI behavior.
62
- // Returns { kind: 'unsupported' } if the endpoint returns 404 or cannot be reached,
63
- // allowing the caller to fall back gracefully.
64
- const OAUTH_TOKEN_URL = 'https://claude.ai/api/auth/oauth/token';
65
-
66
52
  /** Parse a raw credential JSON blob (from file or Keychain) into a result. */
67
53
  function parseCredsRaw(raw, source) {
68
54
  let data;
@@ -125,14 +111,13 @@ function isExpiringSoon(creds, withinMs = 5 * 60_000) {
125
111
  * isRefreshFutile(creds) → boolean
126
112
  *
127
113
  * True when the token is already expired AND there is no refresh token to
128
- * present. In that state every refresh avenue is a dead end: tryOAuthRefresh
129
- * short-circuits to 'unsupported' (it needs `creds.refreshToken`), and the
114
+ * present. In that state the refresh avenue is a dead end: the
130
115
  * `claude --version` CLI fallback performs its own silent OAuth refresh from
131
116
  * that SAME missing refresh token, so it cannot help either. The only recovery
132
117
  * is an out-of-band `claude login` — which refreshIfNeeded picks up on its very
133
118
  * next call via the isExpiringSoon early-return, since fresh creds never reach
134
119
  * this path. Left ungated, this state made pollLoop spawn a `claude --version`
135
- * process and write the oauth_refresh_unsupported → cli_fallback_ok →
120
+ * process and write the cli_fallback_ok →
136
121
  * auth_failed_expired triple on EVERY 15 s poll, forever (observed 2026-09-23:
137
122
  * expiredAtMs 1775277875159, identical triple every ~15 min across the log).
138
123
  */
@@ -153,17 +138,6 @@ function __resetFutileRefreshThrottle() {
153
138
  lastFutileRefreshLoggedAt = null;
154
139
  }
155
140
 
156
- async function writeCredentials(rawData, freshOauth, source = 'file') {
157
- const next = { ...rawData, claudeAiOauth: { ...rawData.claudeAiOauth, ...freshOauth } };
158
- if (source === 'keychain') {
159
- // macOS: upsert back into the Keychain. Sync + may throw — caller catches
160
- // and falls back to the `claude --version` CLI refresh path.
161
- writeKeychainRaw(JSON.stringify(next));
162
- return;
163
- }
164
- await atomicFs.writeJsonAtomic(CREDS_PATH, next, { mode: 0o600, newline: false });
165
- }
166
-
167
141
  function appendRefreshLog(entry) {
168
142
  try {
169
143
  const line = JSON.stringify({ ...entry, ts: new Date().toISOString() }) + '\n';
@@ -179,36 +153,7 @@ function appendRefreshLog(entry) {
179
153
  } catch { /* non-fatal; telemetry must not break the main flow */ }
180
154
  }
181
155
 
182
- // Stretch: attempt standard OAuth 2.0 refresh token grant.
183
- async function tryOAuthRefresh(creds) {
184
- if (!creds.refreshToken) return { kind: 'unsupported', message: 'no refresh token in credentials' };
185
- try {
186
- const r = await fetch(OAUTH_TOKEN_URL, {
187
- method: 'POST',
188
- headers: { 'Content-Type': 'application/json' },
189
- body: JSON.stringify({ grant_type: 'refresh_token', refresh_token: creds.refreshToken }),
190
- signal: AbortSignal.timeout(10_000),
191
- });
192
- if (r.status === 404) return { kind: 'unsupported', message: 'refresh endpoint not found (HTTP 404)' };
193
- if (r.status === 401 || r.status === 403) return { kind: 'auth', message: `refresh rejected: HTTP ${r.status}` };
194
- if (!r.ok) return { kind: 'transient', message: `HTTP ${r.status}` };
195
- const j = await r.json();
196
- if (!j.access_token) return { kind: 'auth', message: 'no access_token in refresh response' };
197
- return {
198
- kind: 'ok',
199
- fresh: {
200
- accessToken: j.access_token,
201
- refreshToken: j.refresh_token ?? creds.refreshToken,
202
- expiresAt: j.expires_at ?? (Date.now() + (j.expires_in ?? 3600) * 1000),
203
- },
204
- };
205
- } catch (e) {
206
- if (e?.name === 'TimeoutError') return { kind: 'transient', message: 'refresh request timed out' };
207
- return { kind: 'unsupported', message: `refresh error: ${e?.message}` };
208
- }
209
- }
210
-
211
- // Stretch fallback: spawning `claude --version` triggers silent token refresh in the CLI binary.
156
+ // Sole refresh avenue: spawning `claude --version` triggers silent token refresh in the CLI binary.
212
157
  function tryCliFallback() {
213
158
  return new Promise((resolve) => {
214
159
  let settled = false;
@@ -244,7 +189,7 @@ function tryCliFallback() {
244
189
  async function refreshIfNeeded(forceRefresh = false) {
245
190
  const cr = await readCredentials();
246
191
  if (cr.kind !== 'ok') return cr;
247
- const { creds, raw, source } = cr;
192
+ const { creds } = cr;
248
193
 
249
194
  if (!forceRefresh && !isExpiringSoon(creds)) {
250
195
  return { kind: 'ok', creds };
@@ -253,12 +198,12 @@ async function refreshIfNeeded(forceRefresh = false) {
253
198
  const alreadyExpired = isExpired(creds);
254
199
 
255
200
  // Futile-refresh short-circuit: token already expired AND no refresh token
256
- // to present. Every downstream avenue (OAuth grant, `claude --version` CLI
257
- // fallback) is a guaranteed dead end from the same missing token, so running
258
- // them on every 15 s poll only spawns a doomed child process and re-logs the
201
+ // to present. The `claude --version` CLI fallback is a guaranteed dead end
202
+ // from the same missing token, so running it on every 15 s poll only spawns
203
+ // a doomed child process and re-logs the
259
204
  // same triple forever (2026-09-23 storm). Re-read once — cheap, and the ONE
260
205
  // real recovery (`claude login`) lands here — then, if still futile, return
261
- // the auth verdict WITHOUT the OAuth call or the CLI-fallback spawn, logging
206
+ // the auth verdict WITHOUT the CLI-fallback spawn, logging
262
207
  // at most once per FUTILE_REFRESH_COOLDOWN_MS instead of every poll.
263
208
  if (isRefreshFutile(creds)) {
264
209
  const recheckCr = await readCredentials();
@@ -279,38 +224,19 @@ async function refreshIfNeeded(forceRefresh = false) {
279
224
  };
280
225
  }
281
226
 
282
- // Stretch: try OAuth refresh endpoint first.
283
- const oauthResult = await tryOAuthRefresh(creds);
284
- appendRefreshLog({ event: `oauth_refresh_${oauthResult.kind}`, message: oauthResult.message ?? null });
285
-
286
- if (oauthResult.kind === 'ok') {
287
- try {
288
- await writeCredentials(raw, oauthResult.fresh, source);
289
- const freshCr = await readCredentials();
290
- if (freshCr.kind === 'ok') {
291
- appendRefreshLog({ event: 'oauth_refresh_written_ok' });
292
- return { kind: 'ok', creds: freshCr.creds };
293
- }
294
- } catch (e) {
295
- appendRefreshLog({ event: 'oauth_refresh_write_failed', error: e?.message });
296
- }
297
- }
298
-
299
- // Stretch: if OAuth didn't explicitly reject the token, try `claude --version` fallback.
300
- if (oauthResult.kind !== 'auth') {
301
- const cliResult = await tryCliFallback();
302
- appendRefreshLog({ event: cliResult.ok ? 'cli_fallback_ok' : 'cli_fallback_failed', reason: cliResult.reason ?? null });
303
- if (cliResult.ok) {
304
- const freshCr = await readCredentials();
305
- if (freshCr.kind === 'ok' && !isExpired(freshCr.creds)) {
306
- return { kind: 'ok', creds: freshCr.creds };
307
- }
227
+ // The CLI owns the real refresh contract: `claude --version` refreshes silently.
228
+ const cliResult = await tryCliFallback();
229
+ appendRefreshLog({ event: cliResult.ok ? 'cli_fallback_ok' : 'cli_fallback_failed', reason: cliResult.reason ?? null });
230
+ if (cliResult.ok) {
231
+ const freshCr = await readCredentials();
232
+ if (freshCr.kind === 'ok' && !isExpired(freshCr.creds)) {
233
+ return { kind: 'ok', creds: freshCr.creds };
308
234
  }
309
235
  }
310
236
 
311
237
  if (alreadyExpired) {
312
238
  // Re-read from disk in case credentials were externally refreshed (e.g. via
313
- // `claude login`) between our initial read and the failed OAuth attempt.
239
+ // `claude login`) between our initial read and the failed CLI fallback.
314
240
  const recheckCr = await readCredentials();
315
241
  if (recheckCr.kind === 'ok' && !isExpired(recheckCr.creds)) {
316
242
  appendRefreshLog({ event: 'externally_refreshed_ok', recheckExpiresAt: recheckCr.creds.expiresAt ?? null });
@@ -41,7 +41,7 @@
41
41
  const fs = require('node:fs');
42
42
  const os = require('node:os');
43
43
  const path = require('node:path');
44
- const schedulerPaths = require('./schedulerPaths.cjs');
44
+ const schedulerPaths = require('./scheduler/schedulerPaths.cjs');
45
45
 
46
46
  const OPS_DIRNAME = 'session-manager-operations';
47
47
  const WORKTREES_MARKER = `${path.sep}.git${path.sep}worktrees${path.sep}`;
@@ -35,7 +35,7 @@
35
35
 
36
36
  const path = require('path');
37
37
  const { activeIndexPath, withPathLock } = require('./epicMint.cjs');
38
- const config = require('../config.cjs');
38
+ const config = require('../../config.cjs');
39
39
  const { mirrorEpicStatus } = require('./epicStatusMirror.cjs');
40
40
 
41
41
  function hasOwn(obj, key) {
@@ -73,7 +73,7 @@ async function mergeActiveIndex(cwd, { sessions: rawMemorySessions = {}, events:
73
73
  if (!cwd || typeof cwd !== 'string') throw new Error('mergeActiveIndex: cwd is required');
74
74
  // Register this project's cwd as a write-allowed root (config.cjs's
75
75
  // validateWrite gate) — mirrors the precedent + comment in
76
- // lib/prdCreate.cjs:152-155. A chat-only Epic (headless claude -p, no
76
+ // lib/prd/prdCreate.cjs:152-155. A chat-only Epic (headless claude -p, no
77
77
  // Terminal PTY ever spawned for this cwd) reaches this merge on a cold
78
78
  // boot before pty.cjs's addAllowedRoot call has ever run for it — without
79
79
  // this, every active-index.json write for such a project fails with
@@ -181,7 +181,7 @@ async function mergeActiveIndex(cwd, { sessions: rawMemorySessions = {}, events:
181
181
 
182
182
  function registerActiveIndexMergeHandlers() {
183
183
  const { ipcMain } = require('electron');
184
- const { schemas: s, validated: v } = require('../ipcSchemas.cjs');
184
+ const { schemas: s, validated: v } = require('../../ipcSchemas.cjs');
185
185
  ipcMain.handle(
186
186
  'promptSessions:merge-active-index',
187
187
  v(s.promptSessionsMergeActiveIndex, ({ cwd, sessions, events, removedIds }) =>
@@ -20,7 +20,7 @@ const path = require('node:path');
20
20
  const { activeIndexPath, readActiveIndex, writeActiveIndex, withPathLock } = require('./epicMint.cjs');
21
21
 
22
22
  function promptSessionsDir(cwd) {
23
- const { opsPath } = require('./opsOwnership.cjs');
23
+ const { opsPath } = require('../opsOwnership.cjs');
24
24
  return opsPath(cwd, 'prompt-sessions');
25
25
  }
26
26
 
@@ -20,36 +20,28 @@ const fs = require('node:fs');
20
20
  const path = require('node:path');
21
21
  const os = require('node:os');
22
22
  const { spawn } = require('node:child_process');
23
- const { writeJson, addAllowedRoot } = require('../config.cjs');
24
- const { cleanChildEnv, pathWithUserBins } = require('./cleanEnv.cjs');
25
- const { shimPath: guardShimPath, resolveShimTarget, ensureGuardShimsOrError } = require('./guardShims.cjs');
26
- const { resolveProjectRoot } = require('./opsOwnership.cjs');
27
- const { nodeShellCommand } = require('./appRuntime.cjs');
23
+ const { writeJson, addAllowedRoot } = require('../../config.cjs');
24
+ const { cleanChildEnv, pathWithUserBins } = require('../process/cleanEnv.cjs');
25
+ const { shimPath: guardShimPath, resolveShimTarget, ensureGuardShimsOrError } = require('../guardShims.cjs');
26
+ const { resolveProjectRoot } = require('../opsOwnership.cjs');
27
+ const { nodeShellCommand } = require('../appRuntime.cjs');
28
+ const { readJsonOrSync } = require('../util/atomicFs.cjs');
28
29
 
29
30
  const SCHEDULER_MCP_NAME = 'session-manager-scheduler';
30
31
  const DEV_PLUGIN_ENABLED_KEY = 'session-manager-dev@session-manager';
31
- const SCHEDULER_MCP_SERVER_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'scheduler-mcp-server.cjs');
32
- const PRD_WRITE_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-prd-writes.cjs');
32
+ const SCHEDULER_MCP_SERVER_SCRIPT = path.resolve(__dirname, '..', '..', '..', '..', 'scripts', 'scheduler-mcp-server.cjs');
33
+ const PRD_WRITE_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', '..', 'scripts', 'hooks', 'guard-prd-writes.cjs');
33
34
  const PRD_WRITE_GUARD_MATCHER = 'Write|Edit|NotebookEdit';
34
- const DESTRUCTIVE_GIT_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-destructive-git.cjs');
35
+ const DESTRUCTIVE_GIT_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', '..', 'scripts', 'hooks', 'guard-destructive-git.cjs');
35
36
  const DESTRUCTIVE_GIT_GUARD_MATCHER = 'Bash';
36
- const INLINE_IMPLEMENTATION_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-inline-implementation.cjs');
37
+ const INLINE_IMPLEMENTATION_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', '..', 'scripts', 'hooks', 'guard-inline-implementation.cjs');
37
38
  const INLINE_IMPLEMENTATION_GUARD_MATCHER = 'Write|Edit|NotebookEdit';
38
- const SELF_SCHEDULE_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', 'scripts', 'hooks', 'guard-self-schedule.cjs');
39
+ const SELF_SCHEDULE_GUARD_SCRIPT = path.resolve(__dirname, '..', '..', '..', '..', 'scripts', 'hooks', 'guard-self-schedule.cjs');
39
40
  const SELF_SCHEDULE_GUARD_MATCHER = 'ScheduleWakeup|CronCreate|Task|Agent';
40
41
  const LIVE_PROBE_TIMEOUT_MS = 10_000;
41
42
  const LIVE_PROBE_TTL_MS = 60_000;
42
43
  const REQUIRED_LIVE_TOOLS = ['scheduler_create_prd', 'session_manager_help'];
43
44
 
44
- /** Read + JSON.parse a file, returning `fallback` on any read/parse failure. */
45
- function readJsonSafe(absPath, fallback) {
46
- try {
47
- return JSON.parse(fs.readFileSync(absPath, 'utf8'));
48
- } catch {
49
- return fallback;
50
- }
51
- }
52
-
53
45
  /** Lazy (not a module-load constant) so tests can toggle packaged mode. */
54
46
  function schedulerMcpFixCmd() {
55
47
  const cmd = nodeShellCommand(SCHEDULER_MCP_SERVER_SCRIPT);
@@ -68,10 +60,10 @@ function schedulerMcpFixCmd() {
68
60
  * which is the incident this PRD closes.
69
61
  */
70
62
  function checkSchedulerMcp({ cwd, homeDir }) {
71
- const userConfig = readJsonSafe(path.join(homeDir, '.claude.json'), null);
63
+ const userConfig = readJsonOrSync(path.join(homeDir, '.claude.json'), null);
72
64
  const userEntry = userConfig?.mcpServers?.[SCHEDULER_MCP_NAME];
73
65
 
74
- const projectConfig = readJsonSafe(path.join(cwd, '.mcp.json'), null);
66
+ const projectConfig = readJsonOrSync(path.join(cwd, '.mcp.json'), null);
75
67
  const projectHas = !!projectConfig?.mcpServers?.[SCHEDULER_MCP_NAME];
76
68
 
77
69
  if (userEntry) {
@@ -129,10 +121,10 @@ function checkSchedulerMcp({ cwd, homeDir }) {
129
121
  * (enableAllProjectMcpServers), not something this probe decides.
130
122
  */
131
123
  function checkSchedulerMcpProjectDuplicate({ cwd, homeDir }) {
132
- const userConfig = readJsonSafe(path.join(homeDir, '.claude.json'), null);
124
+ const userConfig = readJsonOrSync(path.join(homeDir, '.claude.json'), null);
133
125
  const userHas = !!userConfig?.mcpServers?.[SCHEDULER_MCP_NAME];
134
126
 
135
- const projectConfig = readJsonSafe(path.join(cwd, '.mcp.json'), null);
127
+ const projectConfig = readJsonOrSync(path.join(cwd, '.mcp.json'), null);
136
128
  const projectHas = !!projectConfig?.mcpServers?.[SCHEDULER_MCP_NAME];
137
129
 
138
130
  const warn = userHas && projectHas;
@@ -289,7 +281,7 @@ async function checkSchedulerMcpLive({ cwd, homeDir, schedulerMcpCheck }) {
289
281
  // executed the moment its cwd is merely probed, bypassing that approval
290
282
  // entirely. Auto-approving project-scope servers is explicitly out of
291
283
  // scope for this probe — see this PRD's "Out of scope".
292
- const userConfig = readJsonSafe(path.join(homeDir, '.claude.json'), null);
284
+ const userConfig = readJsonOrSync(path.join(homeDir, '.claude.json'), null);
293
285
  const entry = userConfig?.mcpServers?.[SCHEDULER_MCP_NAME];
294
286
 
295
287
  if (!entry?.command) {
@@ -312,7 +304,7 @@ async function checkSchedulerMcpLive({ cwd, homeDir, schedulerMcpCheck }) {
312
304
  }
313
305
 
314
306
  function checkDevPlugin({ homeDir }) {
315
- const settings = readJsonSafe(path.join(homeDir, '.claude', 'settings.json'), null);
307
+ const settings = readJsonOrSync(path.join(homeDir, '.claude', 'settings.json'), null);
316
308
  const ok = settings?.enabledPlugins?.[DEV_PLUGIN_ENABLED_KEY] === true;
317
309
 
318
310
  return {
@@ -444,7 +436,7 @@ const GUARD_DEFS = {
444
436
  * while reporting green" failure mode, one hop downstream of the hook command.
445
437
  */
446
438
  function checkGuard(def, { cwd, homeDir = os.homedir() }) {
447
- const settings = readJsonSafe(path.join(cwd, '.claude', 'settings.json'), null);
439
+ const settings = readJsonOrSync(path.join(cwd, '.claude', 'settings.json'), null);
448
440
  const preToolUse = Array.isArray(settings?.hooks?.PreToolUse) ? settings.hooks.PreToolUse : [];
449
441
 
450
442
  let mentioned = false;
@@ -536,7 +528,7 @@ async function installGuard(def, { cwd, homeDir = os.homedir(), skipShimEnsure =
536
528
  // documents for PromptSession archive writes. `cwd` here IS a project root.
537
529
  addAllowedRoot(cwd);
538
530
 
539
- const existing = fs.existsSync(settingsPath) ? readJsonSafe(settingsPath, undefined) : {};
531
+ const existing = fs.existsSync(settingsPath) ? readJsonOrSync(settingsPath, undefined) : {};
540
532
  if (existing === undefined) {
541
533
  // Unparseable settings — refuse rather than silently discarding whatever
542
534
  // the human has in there.
@@ -629,7 +621,7 @@ async function installSelfScheduleGuard({ cwd, homeDir = os.homedir(), skipShimE
629
621
 
630
622
  /**
631
623
  * Runs all eight delegation-readiness checks for `cwd`. Every filesystem read
632
- * is wrapped (readJsonSafe / try-catch) so a missing or unparseable file
624
+ * is wrapped (readJsonOrSync / try-catch) so a missing or unparseable file
633
625
  * yields ok:false with a detail, never a thrown exception. Async because
634
626
  * scheduler-mcp-live genuinely spawns a process and waits on it (bounded by
635
627
  * LIVE_PROBE_TIMEOUT_MS, and memoized per registration signature so repeat
@@ -673,7 +665,7 @@ function logAutoInstallEvent({ cwd, guard, action, error }) {
673
665
  // A no-op outcome is not an event worth an error-log row (24 rows/3 days of boot noise).
674
666
  if (action === 'already-installed') return;
675
667
  try {
676
- const opsErrorLog = require('./opsErrorLog.cjs');
668
+ const opsErrorLog = require('../telemetry/opsErrorLog.cjs');
677
669
  opsErrorLog.appendError({
678
670
  cwd,
679
671
  scope: 'delegationReadiness',
@@ -13,7 +13,7 @@
13
13
  * scheduler.cjs.
14
14
  */
15
15
 
16
- const { readTurns } = require('../promptSessionTranscript.cjs');
16
+ const { readTurns } = require('../../promptSessionTranscript.cjs');
17
17
  const { readActiveIndex } = require('./epicMint.cjs');
18
18
 
19
19
  const DEFAULT_MAX_CHARS = 4000;
@@ -28,7 +28,7 @@ const fs = require('node:fs');
28
28
  const os = require('node:os');
29
29
  const path = require('node:path');
30
30
  const { resolveEpicTranscriptPath } = require('./epicTranscriptPath.cjs');
31
- const { classifyLine } = require('./classifyTranscriptLine.cjs');
31
+ const { classifyLine } = require('../classifyTranscriptLine.cjs');
32
32
 
33
33
  // Same cap transcripts.cjs's readDelta uses for a single pass — bounds
34
34
  // memory/time on a very large transcript instead of reading it whole.
@@ -40,7 +40,7 @@ const APPLICATION_SOURCE_PREFIXES = ['src/', 'scripts/', 'plugins/', 'bin/'];
40
40
  function countPrdsQueued(cwd, epicId, deps = {}) {
41
41
  const readdirSync = deps.readdirSync || fs.readdirSync;
42
42
  try {
43
- const { opsPath } = require('./opsOwnership.cjs');
43
+ const { opsPath } = require('../opsOwnership.cjs');
44
44
  const dir = opsPath(cwd, 'scheduler', 'epics', epicId, 'prds');
45
45
  return readdirSync(dir).filter((name) => name.endsWith('.md')).length;
46
46
  } catch {