factory-factory 0.2.4 → 0.2.6
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.
- package/dist/client/assets/{_basePickBy-CCB8XrLV.js → _basePickBy-B1TXLukC.js} +1 -1
- package/dist/client/assets/{_baseUniq-CnpEKy-E.js → _baseUniq-C997CyNN.js} +1 -1
- package/dist/client/assets/{arc-BVt3NpFL.js → arc-Dj9rabaY.js} +1 -1
- package/dist/client/assets/{architectureDiagram-VXUJARFQ-DQXtRVG1.js → architectureDiagram-VXUJARFQ-CDb4dzGu.js} +1 -1
- package/dist/client/assets/{blockDiagram-VD42YOAC-C_GKWlYL.js → blockDiagram-VD42YOAC-C0GqgSqb.js} +1 -1
- package/dist/client/assets/{c4Diagram-YG6GDRKO-njABqJg1.js → c4Diagram-YG6GDRKO-DwI5UcIR.js} +1 -1
- package/dist/client/assets/channel-CUWT3YnL.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DNMCzzfR.js → chunk-4BX2VUAB-B-xUvS2Q.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-CajLRh0F.js → chunk-55IACEB6-DnfB7mO6.js} +1 -1
- package/dist/client/assets/{chunk-B4BG7PRW-BLSFZNLe.js → chunk-B4BG7PRW-C3mlyOKh.js} +1 -1
- package/dist/client/assets/{chunk-DI55MBZ5-DtQPQERJ.js → chunk-DI55MBZ5-CWrY69vI.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-BhtFvMT5.js → chunk-FMBD7UC4-DqIuy8cX.js} +1 -1
- package/dist/client/assets/{chunk-QN33PNHL-DBHUU683.js → chunk-QN33PNHL-DaQ7aatm.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-B1rA-SBe.js → chunk-QZHKN3VN-DIAhn72l.js} +1 -1
- package/dist/client/assets/{chunk-TZMSLE5B-BJUlg6Pf.js → chunk-TZMSLE5B-Vv2yea82.js} +1 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-DIs9Yb8N.js +1 -0
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-DIs9Yb8N.js +1 -0
- package/dist/client/assets/clone-DWe4ixM1.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-CyjRxz-A.js → cose-bilkent-S5V4N54A-DCt0h-5b.js} +1 -1
- package/dist/client/assets/{dagre-6UL2VRFP-CURGwkx9.js → dagre-6UL2VRFP-Dx9zq5ck.js} +1 -1
- package/dist/client/assets/{diagram-PSM6KHXK-CaOIVsbD.js → diagram-PSM6KHXK-DWDEKgzo.js} +1 -1
- package/dist/client/assets/{diagram-QEK2KX5R-0zoz9z8_.js → diagram-QEK2KX5R-DWNDqpN2.js} +1 -1
- package/dist/client/assets/{diagram-S2PKOQOG-D6WyG3pJ.js → diagram-S2PKOQOG-DG0T0gs6.js} +1 -1
- package/dist/client/assets/{erDiagram-Q2GNP2WA-BwfTai9D.js → erDiagram-Q2GNP2WA-CqDgOfYs.js} +1 -1
- package/dist/client/assets/{flowDiagram-NV44I4VS-z6idjLXa.js → flowDiagram-NV44I4VS-V_QvSvWm.js} +1 -1
- package/dist/client/assets/{ganttDiagram-JELNMOA3-BLAK0GDW.js → ganttDiagram-JELNMOA3-BCPELgz0.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-NY62KEGX-DsC9TQow.js → gitGraphDiagram-NY62KEGX-D31RxlxC.js} +1 -1
- package/dist/client/assets/{graph-72LNxX96.js → graph-D1yJ-cNO.js} +1 -1
- package/dist/client/assets/index-CgaNRc7X.js +436 -0
- package/dist/client/assets/index-EHCwHZbd.css +1 -0
- package/dist/client/assets/{infoDiagram-WHAUD3N6-CInk5tUs.js → infoDiagram-WHAUD3N6-BIE2gcL7.js} +1 -1
- package/dist/client/assets/{journeyDiagram-XKPGCS4Q-nTrHP_HR.js → journeyDiagram-XKPGCS4Q-B5q1A61b.js} +1 -1
- package/dist/client/assets/{kanban-definition-3W4ZIXB7-xiF9MwcX.js → kanban-definition-3W4ZIXB7-D7FRiZXq.js} +1 -1
- package/dist/client/assets/{layout-BxYnyUX2.js → layout-X-mlIEi7.js} +1 -1
- package/dist/client/assets/{linear-BOpSSl-n.js → linear-CGpwoS11.js} +1 -1
- package/dist/client/assets/{mindmap-definition-VGOIOE7T-BxYEWuiO.js → mindmap-definition-VGOIOE7T-XMWWZCmt.js} +1 -1
- package/dist/client/assets/{pieDiagram-ADFJNKIX-DHQkQuZp.js → pieDiagram-ADFJNKIX-4wqJE3Xv.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-AYHSOK5B--dbc4LsQ.js → quadrantDiagram-AYHSOK5B-ApAId2lG.js} +1 -1
- package/dist/client/assets/{requirementDiagram-UZGBJVZJ-Dr-h39fn.js → requirementDiagram-UZGBJVZJ-CNkajZw2.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-TZEHDZUN-BwKHqT4R.js → sankeyDiagram-TZEHDZUN-BFLGpYF1.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-WL72ISMW-DizdB6A9.js → sequenceDiagram-WL72ISMW-DB-mQnOQ.js} +1 -1
- package/dist/client/assets/{stateDiagram-FKZM4ZOC-C-J72Wtl.js → stateDiagram-FKZM4ZOC-7gdB28fK.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-6YMfUfib.js +1 -0
- package/dist/client/assets/{terminal-instance-qIJHj7KS.js → terminal-instance-BHOJvhaZ.js} +1 -1
- package/dist/client/assets/{timeline-definition-IT6M3QCI-DAGw_FKd.js → timeline-definition-IT6M3QCI-Cc-mzevR.js} +1 -1
- package/dist/client/assets/{treemap-KMMF4GRG-Bo4f4Al3.js → treemap-KMMF4GRG-BSCzOTjP.js} +1 -1
- package/dist/client/assets/{xychartDiagram-PRI3JC2R-CAJ9NVad.js → xychartDiagram-PRI3JC2R-DA09PDY1.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/prisma/generated/internal/class.js +2 -2
- package/dist/prisma/generated/internal/class.js.map +1 -1
- package/dist/prisma/generated/internal/prismaNamespace.d.ts +1 -4
- package/dist/prisma/generated/internal/prismaNamespace.d.ts.map +1 -1
- package/dist/prisma/generated/internal/prismaNamespace.js +1 -4
- package/dist/prisma/generated/internal/prismaNamespace.js.map +1 -1
- package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts +1 -4
- package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts.map +1 -1
- package/dist/prisma/generated/internal/prismaNamespaceBrowser.js +1 -4
- package/dist/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
- package/dist/prisma/generated/models/ClaudeSession.d.ts +36 -1
- package/dist/prisma/generated/models/ClaudeSession.d.ts.map +1 -1
- package/dist/prisma/generated/models/UserSettings.d.ts +1 -107
- package/dist/prisma/generated/models/UserSettings.d.ts.map +1 -1
- package/dist/prompts/ratchet/dispatch.md +31 -0
- package/dist/src/backend/app-context.d.ts +4 -4
- package/dist/src/backend/app-context.d.ts.map +1 -1
- package/dist/src/backend/app-context.js +4 -4
- package/dist/src/backend/app-context.js.map +1 -1
- package/dist/src/backend/claude/protocol.d.ts +2 -2
- package/dist/src/backend/claude/protocol.d.ts.map +1 -1
- package/dist/src/backend/claude/protocol.js +13 -4
- package/dist/src/backend/claude/protocol.js.map +1 -1
- package/dist/src/backend/claude/protocol.test.js +18 -0
- package/dist/src/backend/claude/protocol.test.js.map +1 -1
- package/dist/src/backend/claude/session.d.ts +6 -10
- package/dist/src/backend/claude/session.d.ts.map +1 -1
- package/dist/src/backend/claude/session.js +169 -38
- package/dist/src/backend/claude/session.js.map +1 -1
- package/dist/src/backend/claude/session.test.js +259 -20
- package/dist/src/backend/claude/session.test.js.map +1 -1
- package/dist/src/backend/claude/types.d.ts +3 -0
- package/dist/src/backend/claude/types.d.ts.map +1 -1
- package/dist/src/backend/claude/types.js +5 -0
- package/dist/src/backend/claude/types.js.map +1 -1
- package/dist/src/backend/prompts/markdown-loader.d.ts +22 -0
- package/dist/src/backend/prompts/markdown-loader.d.ts.map +1 -0
- package/dist/src/backend/prompts/markdown-loader.js +77 -0
- package/dist/src/backend/prompts/markdown-loader.js.map +1 -0
- package/dist/src/backend/prompts/markdown-loader.test.d.ts +2 -0
- package/dist/src/backend/prompts/markdown-loader.test.d.ts.map +1 -0
- package/dist/src/backend/prompts/markdown-loader.test.js +190 -0
- package/dist/src/backend/prompts/markdown-loader.test.js.map +1 -0
- package/dist/src/backend/prompts/quick-actions.d.ts.map +1 -1
- package/dist/src/backend/prompts/quick-actions.js +34 -92
- package/dist/src/backend/prompts/quick-actions.js.map +1 -1
- package/dist/src/backend/prompts/ratchet-dispatch.d.ts +3 -0
- package/dist/src/backend/prompts/ratchet-dispatch.d.ts.map +1 -0
- package/dist/src/backend/prompts/ratchet-dispatch.js +53 -0
- package/dist/src/backend/prompts/ratchet-dispatch.js.map +1 -0
- package/dist/src/backend/prompts/ratchet-dispatch.test.d.ts +2 -0
- package/dist/src/backend/prompts/ratchet-dispatch.test.d.ts.map +1 -0
- package/dist/src/backend/prompts/ratchet-dispatch.test.js +34 -0
- package/dist/src/backend/prompts/ratchet-dispatch.test.js.map +1 -0
- package/dist/src/backend/prompts/workflows.d.ts.map +1 -1
- package/dist/src/backend/prompts/workflows.js +23 -76
- package/dist/src/backend/prompts/workflows.js.map +1 -1
- package/dist/src/backend/resource_accessors/claude-session.accessor.d.ts +2 -0
- package/dist/src/backend/resource_accessors/claude-session.accessor.d.ts.map +1 -1
- package/dist/src/backend/resource_accessors/claude-session.accessor.js +1 -0
- package/dist/src/backend/resource_accessors/claude-session.accessor.js.map +1 -1
- package/dist/src/backend/resource_accessors/user-settings.accessor.d.ts +0 -4
- package/dist/src/backend/resource_accessors/user-settings.accessor.d.ts.map +1 -1
- package/dist/src/backend/resource_accessors/user-settings.accessor.js.map +1 -1
- package/dist/src/backend/resource_accessors/workspace.accessor.d.ts +4 -3
- package/dist/src/backend/resource_accessors/workspace.accessor.d.ts.map +1 -1
- package/dist/src/backend/resource_accessors/workspace.accessor.js +4 -2
- package/dist/src/backend/resource_accessors/workspace.accessor.js.map +1 -1
- package/dist/src/backend/routers/websocket/chat.handler.d.ts.map +1 -1
- package/dist/src/backend/routers/websocket/chat.handler.js +1 -32
- package/dist/src/backend/routers/websocket/chat.handler.js.map +1 -1
- package/dist/src/backend/routers/websocket/dev-logs.handler.d.ts.map +1 -1
- package/dist/src/backend/routers/websocket/dev-logs.handler.js +1 -2
- package/dist/src/backend/routers/websocket/dev-logs.handler.js.map +1 -1
- package/dist/src/backend/routers/websocket/terminal.handler.d.ts.map +1 -1
- package/dist/src/backend/routers/websocket/terminal.handler.js +3 -4
- package/dist/src/backend/routers/websocket/terminal.handler.js.map +1 -1
- package/dist/src/backend/services/chat-event-forwarder.service.d.ts +11 -2
- package/dist/src/backend/services/chat-event-forwarder.service.d.ts.map +1 -1
- package/dist/src/backend/services/chat-event-forwarder.service.js +254 -138
- package/dist/src/backend/services/chat-event-forwarder.service.js.map +1 -1
- package/dist/src/backend/services/chat-event-forwarder.service.test.d.ts +2 -0
- package/dist/src/backend/services/chat-event-forwarder.service.test.d.ts.map +1 -0
- package/dist/src/backend/services/chat-event-forwarder.service.test.js +133 -0
- package/dist/src/backend/services/chat-event-forwarder.service.test.js.map +1 -0
- package/dist/src/backend/services/chat-message-handlers/handlers/list-sessions.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/list-sessions.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/list-sessions.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/load-session.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/load-session.handler.js +24 -89
- package/dist/src/backend/services/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/permission-response.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/permission-response.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/permission-response.handler.js +5 -6
- package/dist/src/backend/services/chat-message-handlers/handlers/permission-response.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/question-response.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/question-response.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/question-response.handler.js +5 -6
- package/dist/src/backend/services/chat-message-handlers/handlers/question-response.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/queue-message.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/queue-message.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/queue-message.handler.js +32 -14
- package/dist/src/backend/services/chat-message-handlers/handlers/queue-message.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/remove-queued-message.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/remove-queued-message.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/remove-queued-message.handler.js +8 -6
- package/dist/src/backend/services/chat-message-handlers/handlers/remove-queued-message.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/rewind-files.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/rewind-files.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/rewind-files.handler.js +8 -9
- package/dist/src/backend/services/chat-message-handlers/handlers/rewind-files.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-model.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-model.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-model.handler.js +3 -5
- package/dist/src/backend/services/chat-message-handlers/handlers/set-model.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-thinking-budget.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-thinking-budget.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/set-thinking-budget.handler.js +3 -5
- package/dist/src/backend/services/chat-message-handlers/handlers/set-thinking-budget.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/start.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/start.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/start.handler.js +4 -2
- package/dist/src/backend/services/chat-message-handlers/handlers/start.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/stop.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/stop.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/stop.handler.js +7 -2
- package/dist/src/backend/services/chat-message-handlers/handlers/stop.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/user-input.handler.d.ts +2 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/user-input.handler.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/user-input.handler.js +1 -2
- package/dist/src/backend/services/chat-message-handlers/handlers/user-input.handler.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/interactive-response.d.ts +1 -2
- package/dist/src/backend/services/chat-message-handlers/interactive-response.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/interactive-response.js +31 -23
- package/dist/src/backend/services/chat-message-handlers/interactive-response.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/interactive-response.test.d.ts +2 -0
- package/dist/src/backend/services/chat-message-handlers/interactive-response.test.d.ts.map +1 -0
- package/dist/src/backend/services/chat-message-handlers/interactive-response.test.js +83 -0
- package/dist/src/backend/services/chat-message-handlers/interactive-response.test.js.map +1 -0
- package/dist/src/backend/services/chat-message-handlers/registry.d.ts +5 -1
- package/dist/src/backend/services/chat-message-handlers/registry.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/registry.js +0 -4
- package/dist/src/backend/services/chat-message-handlers/registry.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/utils.d.ts +1 -2
- package/dist/src/backend/services/chat-message-handlers/utils.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers/utils.js +1 -10
- package/dist/src/backend/services/chat-message-handlers/utils.js.map +1 -1
- package/dist/src/backend/services/chat-message-handlers.service.d.ts.map +1 -1
- package/dist/src/backend/services/chat-message-handlers.service.js +35 -17
- package/dist/src/backend/services/chat-message-handlers.service.js.map +1 -1
- package/dist/src/backend/services/data-backup.service.d.ts +2 -622
- package/dist/src/backend/services/data-backup.service.d.ts.map +1 -1
- package/dist/src/backend/services/data-backup.service.js +6 -162
- package/dist/src/backend/services/data-backup.service.js.map +1 -1
- package/dist/src/backend/services/data-backup.service.test.js +0 -18
- package/dist/src/backend/services/data-backup.service.test.js.map +1 -1
- package/dist/src/backend/services/file-lock.service.d.ts.map +1 -1
- package/dist/src/backend/services/file-lock.service.js +3 -5
- package/dist/src/backend/services/file-lock.service.js.map +1 -1
- package/dist/src/backend/services/file-lock.service.test.js +55 -0
- package/dist/src/backend/services/file-lock.service.test.js.map +1 -1
- package/dist/src/backend/services/fixer-session.service.d.ts.map +1 -1
- package/dist/src/backend/services/fixer-session.service.js +9 -0
- package/dist/src/backend/services/fixer-session.service.js.map +1 -1
- package/dist/src/backend/services/fixer-session.service.test.js +3 -0
- package/dist/src/backend/services/fixer-session.service.test.js.map +1 -1
- package/dist/src/backend/services/git-ops.service.d.ts.map +1 -1
- package/dist/src/backend/services/git-ops.service.js +1 -1
- package/dist/src/backend/services/git-ops.service.js.map +1 -1
- package/dist/src/backend/services/github-cli.service.d.ts.map +1 -1
- package/dist/src/backend/services/github-cli.service.js +118 -19
- package/dist/src/backend/services/github-cli.service.js.map +1 -1
- package/dist/src/backend/services/github-cli.service.test.js +126 -0
- package/dist/src/backend/services/github-cli.service.test.js.map +1 -1
- package/dist/src/backend/services/index.d.ts +1 -2
- package/dist/src/backend/services/index.d.ts.map +1 -1
- package/dist/src/backend/services/index.js +1 -2
- package/dist/src/backend/services/index.js.map +1 -1
- package/dist/src/backend/services/ratchet.service.d.ts +23 -69
- package/dist/src/backend/services/ratchet.service.d.ts.map +1 -1
- package/dist/src/backend/services/ratchet.service.js +414 -419
- package/dist/src/backend/services/ratchet.service.js.map +1 -1
- package/dist/src/backend/services/ratchet.service.test.js +319 -415
- package/dist/src/backend/services/ratchet.service.test.js.map +1 -1
- package/dist/src/backend/services/run-script-state-machine.service.d.ts +27 -0
- package/dist/src/backend/services/run-script-state-machine.service.d.ts.map +1 -0
- package/dist/src/backend/services/run-script-state-machine.service.js +149 -0
- package/dist/src/backend/services/run-script-state-machine.service.js.map +1 -0
- package/dist/src/backend/services/run-script-state-machine.service.test.d.ts +2 -0
- package/dist/src/backend/services/run-script-state-machine.service.test.d.ts.map +1 -0
- package/dist/src/backend/services/run-script-state-machine.service.test.js +453 -0
- package/dist/src/backend/services/run-script-state-machine.service.test.js.map +1 -0
- package/dist/src/backend/services/run-script.service.d.ts +0 -7
- package/dist/src/backend/services/run-script.service.d.ts.map +1 -1
- package/dist/src/backend/services/run-script.service.js +145 -76
- package/dist/src/backend/services/run-script.service.js.map +1 -1
- package/dist/src/backend/services/session-store.service.d.ts +73 -0
- package/dist/src/backend/services/session-store.service.d.ts.map +1 -0
- package/dist/src/backend/services/session-store.service.js +732 -0
- package/dist/src/backend/services/session-store.service.js.map +1 -0
- package/dist/src/backend/services/session-store.service.test.d.ts +2 -0
- package/dist/src/backend/services/session-store.service.test.d.ts.map +1 -0
- package/dist/src/backend/services/session-store.service.test.js +738 -0
- package/dist/src/backend/services/session-store.service.test.js.map +1 -0
- package/dist/src/backend/services/session.prompt-builder.d.ts +2 -0
- package/dist/src/backend/services/session.prompt-builder.d.ts.map +1 -1
- package/dist/src/backend/services/session.prompt-builder.js +3 -1
- package/dist/src/backend/services/session.prompt-builder.js.map +1 -1
- package/dist/src/backend/services/session.prompt-builder.test.js +66 -1
- package/dist/src/backend/services/session.prompt-builder.test.js.map +1 -1
- package/dist/src/backend/services/session.repository.d.ts +3 -2
- package/dist/src/backend/services/session.repository.d.ts.map +1 -1
- package/dist/src/backend/services/session.repository.js +14 -0
- package/dist/src/backend/services/session.repository.js.map +1 -1
- package/dist/src/backend/services/session.repository.test.d.ts +2 -0
- package/dist/src/backend/services/session.repository.test.d.ts.map +1 -0
- package/dist/src/backend/services/session.repository.test.js +60 -0
- package/dist/src/backend/services/session.repository.test.js.map +1 -0
- package/dist/src/backend/services/session.service.d.ts.map +1 -1
- package/dist/src/backend/services/session.service.js +6 -1
- package/dist/src/backend/services/session.service.js.map +1 -1
- package/dist/src/backend/services/session.service.test.js +3 -1
- package/dist/src/backend/services/session.service.test.js.map +1 -1
- package/dist/src/backend/services/workspace-activity.service.d.ts.map +1 -1
- package/dist/src/backend/services/workspace-activity.service.js +2 -1
- package/dist/src/backend/services/workspace-activity.service.js.map +1 -1
- package/dist/src/backend/services/workspace-activity.service.test.d.ts +2 -0
- package/dist/src/backend/services/workspace-activity.service.test.d.ts.map +1 -0
- package/dist/src/backend/services/workspace-activity.service.test.js +32 -0
- package/dist/src/backend/services/workspace-activity.service.test.js.map +1 -0
- package/dist/src/backend/services/workspace-creation.service.d.ts.map +1 -1
- package/dist/src/backend/services/workspace-creation.service.js +6 -0
- package/dist/src/backend/services/workspace-creation.service.js.map +1 -1
- package/dist/src/backend/services/workspace-creation.service.test.js +2 -8
- package/dist/src/backend/services/workspace-creation.service.test.js.map +1 -1
- package/dist/src/backend/services/workspace-query.service.d.ts +4 -0
- package/dist/src/backend/services/workspace-query.service.d.ts.map +1 -1
- package/dist/src/backend/services/workspace-query.service.js +32 -0
- package/dist/src/backend/services/workspace-query.service.js.map +1 -1
- package/dist/src/backend/services/worktree-lifecycle.service.d.ts.map +1 -1
- package/dist/src/backend/services/worktree-lifecycle.service.js +141 -21
- package/dist/src/backend/services/worktree-lifecycle.service.js.map +1 -1
- package/dist/src/backend/services/worktree-lifecycle.service.test.js +48 -1
- package/dist/src/backend/services/worktree-lifecycle.service.test.js.map +1 -1
- package/dist/src/backend/trpc/admin.trpc.d.ts +22 -19
- package/dist/src/backend/trpc/admin.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/admin.trpc.js +2 -1
- package/dist/src/backend/trpc/admin.trpc.js.map +1 -1
- package/dist/src/backend/trpc/index.d.ts +48 -41
- package/dist/src/backend/trpc/index.d.ts.map +1 -1
- package/dist/src/backend/trpc/project.trpc.d.ts +9 -0
- package/dist/src/backend/trpc/project.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/project.trpc.js +12 -0
- package/dist/src/backend/trpc/project.trpc.js.map +1 -1
- package/dist/src/backend/trpc/project.trpc.test.d.ts +2 -0
- package/dist/src/backend/trpc/project.trpc.test.d.ts.map +1 -0
- package/dist/src/backend/trpc/project.trpc.test.js +73 -0
- package/dist/src/backend/trpc/project.trpc.test.js.map +1 -0
- package/dist/src/backend/trpc/session.trpc.d.ts +10 -3
- package/dist/src/backend/trpc/session.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/session.trpc.js +12 -3
- package/dist/src/backend/trpc/session.trpc.js.map +1 -1
- package/dist/src/backend/trpc/user-settings.trpc.d.ts +0 -12
- package/dist/src/backend/trpc/user-settings.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/user-settings.trpc.js +1 -13
- package/dist/src/backend/trpc/user-settings.trpc.js.map +1 -1
- package/dist/src/backend/trpc/workspace/init.trpc.d.ts +1 -0
- package/dist/src/backend/trpc/workspace/init.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/workspace/run-script.trpc.d.ts +0 -7
- package/dist/src/backend/trpc/workspace/run-script.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts +1 -0
- package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts.map +1 -1
- package/dist/src/backend/trpc/workspace.trpc.d.ts +7 -7
- package/dist/src/backend/trpc/workspace.trpc.d.ts.map +1 -1
- package/dist/src/backend/trpc/workspace.trpc.js +12 -1
- package/dist/src/backend/trpc/workspace.trpc.js.map +1 -1
- package/dist/src/shared/ci-status.d.ts +9 -0
- package/dist/src/shared/ci-status.d.ts.map +1 -0
- package/dist/src/shared/ci-status.js +44 -0
- package/dist/src/shared/ci-status.js.map +1 -0
- package/dist/src/shared/claude/index.d.ts +1 -0
- package/dist/src/shared/claude/index.d.ts.map +1 -1
- package/dist/src/shared/claude/index.js +1 -0
- package/dist/src/shared/claude/index.js.map +1 -1
- package/dist/src/shared/claude/protocol.d.ts +203 -9
- package/dist/src/shared/claude/protocol.d.ts.map +1 -1
- package/dist/src/shared/claude/protocol.js +182 -0
- package/dist/src/shared/claude/protocol.js.map +1 -1
- package/dist/src/shared/pending-request-types.d.ts +1 -0
- package/dist/src/shared/pending-request-types.d.ts.map +1 -1
- package/dist/src/shared/pending-request-types.js +3 -0
- package/dist/src/shared/pending-request-types.js.map +1 -1
- package/dist/src/shared/schemas/export-data.schema.d.ts +870 -0
- package/dist/src/shared/schemas/export-data.schema.d.ts.map +1 -0
- package/dist/src/shared/schemas/export-data.schema.js +168 -0
- package/dist/src/shared/schemas/export-data.schema.js.map +1 -0
- package/dist/src/shared/schemas/persisted-stores.schema.d.ts +19 -0
- package/dist/src/shared/schemas/persisted-stores.schema.d.ts.map +1 -0
- package/dist/src/shared/schemas/persisted-stores.schema.js +17 -0
- package/dist/src/shared/schemas/persisted-stores.schema.js.map +1 -0
- package/dist/src/shared/session-runtime.d.ts +17 -0
- package/dist/src/shared/session-runtime.d.ts.map +1 -0
- package/dist/src/shared/session-runtime.js +9 -0
- package/dist/src/shared/session-runtime.js.map +1 -0
- package/dist/src/shared/websocket/chat-message.schema.d.ts +18 -15
- package/dist/src/shared/websocket/chat-message.schema.d.ts.map +1 -1
- package/dist/src/shared/websocket/chat-message.schema.js +4 -3
- package/dist/src/shared/websocket/chat-message.schema.js.map +1 -1
- package/dist/src/shared/websocket/index.d.ts +1 -1
- package/dist/src/shared/websocket/index.d.ts.map +1 -1
- package/dist/src/shared/websocket/index.js.map +1 -1
- package/dist/src/shared/workspace-sidebar-status.d.ts +20 -0
- package/dist/src/shared/workspace-sidebar-status.d.ts.map +1 -0
- package/dist/src/shared/workspace-sidebar-status.js +73 -0
- package/dist/src/shared/workspace-sidebar-status.js.map +1 -0
- package/dist/tsconfig.backend.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/prisma/migrations/20260208000000_remove_unused_ratchet_settings/migration.sql +12 -0
- package/prisma/migrations/20260208153000_add_claude_project_path/migration.sql +2 -0
- package/prisma/schema.prisma +1 -4
- package/prompts/ratchet/dispatch.md +31 -0
- package/dist/client/assets/channel-DXVcwOT7.js +0 -1
- package/dist/client/assets/classDiagram-2ON5EDUG-DSXhUv2N.js +0 -1
- package/dist/client/assets/classDiagram-v2-WZHVMYZB-DSXhUv2N.js +0 -1
- package/dist/client/assets/clone-Cel7XaEC.js +0 -1
- package/dist/client/assets/index-C0E1P_GL.js +0 -399
- package/dist/client/assets/index-DI1BSYUN.css +0 -1
- package/dist/client/assets/stateDiagram-v2-4FDKWEC3-waz9l7Vx.js +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/get-history.handler.d.ts +0 -3
- package/dist/src/backend/services/chat-message-handlers/handlers/get-history.handler.d.ts.map +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/get-history.handler.js +0 -16
- package/dist/src/backend/services/chat-message-handlers/handlers/get-history.handler.js.map +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/get-queue.handler.d.ts +0 -3
- package/dist/src/backend/services/chat-message-handlers/handlers/get-queue.handler.d.ts.map +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/get-queue.handler.js +0 -13
- package/dist/src/backend/services/chat-message-handlers/handlers/get-queue.handler.js.map +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/handlers.test.d.ts +0 -2
- package/dist/src/backend/services/chat-message-handlers/handlers/handlers.test.d.ts.map +0 -1
- package/dist/src/backend/services/chat-message-handlers/handlers/handlers.test.js +0 -512
- package/dist/src/backend/services/chat-message-handlers/handlers/handlers.test.js.map +0 -1
- package/dist/src/backend/services/chat-transport-adapter.service.d.ts +0 -10
- package/dist/src/backend/services/chat-transport-adapter.service.d.ts.map +0 -1
- package/dist/src/backend/services/chat-transport-adapter.service.js +0 -46
- package/dist/src/backend/services/chat-transport-adapter.service.js.map +0 -1
- package/dist/src/backend/services/chat-transport-adapter.service.test.d.ts +0 -2
- package/dist/src/backend/services/chat-transport-adapter.service.test.d.ts.map +0 -1
- package/dist/src/backend/services/chat-transport-adapter.service.test.js +0 -67
- package/dist/src/backend/services/chat-transport-adapter.service.test.js.map +0 -1
- package/dist/src/backend/services/event-compression.service.d.ts +0 -37
- package/dist/src/backend/services/event-compression.service.d.ts.map +0 -1
- package/dist/src/backend/services/event-compression.service.js +0 -235
- package/dist/src/backend/services/event-compression.service.js.map +0 -1
- package/dist/src/backend/services/event-compression.service.test.d.ts +0 -2
- package/dist/src/backend/services/event-compression.service.test.d.ts.map +0 -1
- package/dist/src/backend/services/event-compression.service.test.js +0 -336
- package/dist/src/backend/services/event-compression.service.test.js.map +0 -1
- package/dist/src/backend/services/message-event-store.d.ts +0 -12
- package/dist/src/backend/services/message-event-store.d.ts.map +0 -1
- package/dist/src/backend/services/message-event-store.js +0 -21
- package/dist/src/backend/services/message-event-store.js.map +0 -1
- package/dist/src/backend/services/message-event-store.test.d.ts +0 -2
- package/dist/src/backend/services/message-event-store.test.d.ts.map +0 -1
- package/dist/src/backend/services/message-event-store.test.js +0 -32
- package/dist/src/backend/services/message-event-store.test.js.map +0 -1
- package/dist/src/backend/services/message-queue.service.d.ts +0 -21
- package/dist/src/backend/services/message-queue.service.d.ts.map +0 -1
- package/dist/src/backend/services/message-queue.service.js +0 -105
- package/dist/src/backend/services/message-queue.service.js.map +0 -1
- package/dist/src/backend/services/message-queue.service.test.d.ts +0 -2
- package/dist/src/backend/services/message-queue.service.test.d.ts.map +0 -1
- package/dist/src/backend/services/message-queue.service.test.js +0 -278
- package/dist/src/backend/services/message-queue.service.test.js.map +0 -1
- package/dist/src/backend/services/message-state-machine.d.ts +0 -37
- package/dist/src/backend/services/message-state-machine.d.ts.map +0 -1
- package/dist/src/backend/services/message-state-machine.js +0 -262
- package/dist/src/backend/services/message-state-machine.js.map +0 -1
- package/dist/src/backend/services/message-state-machine.test.d.ts +0 -2
- package/dist/src/backend/services/message-state-machine.test.d.ts.map +0 -1
- package/dist/src/backend/services/message-state-machine.test.js +0 -67
- package/dist/src/backend/services/message-state-machine.test.js.map +0 -1
- package/dist/src/backend/services/message-state.service.d.ts +0 -79
- package/dist/src/backend/services/message-state.service.d.ts.map +0 -1
- package/dist/src/backend/services/message-state.service.js +0 -285
- package/dist/src/backend/services/message-state.service.js.map +0 -1
- package/dist/src/backend/services/message-state.service.test.d.ts +0 -2
- package/dist/src/backend/services/message-state.service.test.d.ts.map +0 -1
- package/dist/src/backend/services/message-state.service.test.js +0 -550
- package/dist/src/backend/services/message-state.service.test.js.map +0 -1
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { CIStatus, RatchetState, SessionStatus } from '../../../prisma/generated/client.js';
|
|
2
2
|
import pLimit from 'p-limit';
|
|
3
|
+
import { buildRatchetDispatchPrompt } from '../prompts/ratchet-dispatch.js';
|
|
3
4
|
import { claudeSessionAccessor } from '../resource_accessors/claude-session.accessor.js';
|
|
4
|
-
import { userSettingsAccessor } from '../resource_accessors/user-settings.accessor.js';
|
|
5
5
|
import { workspaceAccessor } from '../resource_accessors/workspace.accessor.js';
|
|
6
6
|
import { fixerSessionService } from './fixer-session.service.js';
|
|
7
7
|
import { githubCLIService } from './github-cli.service.js';
|
|
8
8
|
import { createLogger } from './logger.service.js';
|
|
9
|
-
import { messageStateService } from './message-state.service.js';
|
|
10
9
|
import { sessionService } from './session.service.js';
|
|
10
|
+
import { sessionStoreService } from './session-store.service.js';
|
|
11
11
|
const logger = createLogger('ratchet');
|
|
12
12
|
const RATCHET_POLL_INTERVAL_MS = 60_000;
|
|
13
13
|
const MAX_CONCURRENT_CHECKS = 5;
|
|
14
14
|
const RATCHET_WORKFLOW = 'ratchet';
|
|
15
|
+
const AUTHENTICATED_USERNAME_CACHE_TTL_MS = 5 * 60_000;
|
|
15
16
|
class RatchetService {
|
|
16
17
|
isShuttingDown = false;
|
|
17
18
|
monitorLoop = null;
|
|
18
19
|
checkLimit = pLimit(MAX_CONCURRENT_CHECKS);
|
|
20
|
+
cachedAuthenticatedUsername = null;
|
|
19
21
|
start() {
|
|
20
22
|
if (this.monitorLoop) {
|
|
21
23
|
return;
|
|
@@ -53,21 +55,13 @@ class RatchetService {
|
|
|
53
55
|
if (this.isShuttingDown) {
|
|
54
56
|
return { checked: 0, stateChanges: 0, actionsTriggered: 0, results: [] };
|
|
55
57
|
}
|
|
56
|
-
const userSettings = await userSettingsAccessor.get();
|
|
57
|
-
const settings = {
|
|
58
|
-
autoFixCi: userSettings.ratchetAutoFixCi,
|
|
59
|
-
autoFixReviews: userSettings.ratchetAutoFixReviews,
|
|
60
|
-
autoMerge: userSettings.ratchetAutoMerge,
|
|
61
|
-
allowedReviewers: userSettings.ratchetAllowedReviewers ?? [],
|
|
62
|
-
};
|
|
63
58
|
const workspaces = await workspaceAccessor.findWithPRsForRatchet();
|
|
64
59
|
if (workspaces.length === 0) {
|
|
65
60
|
return { checked: 0, stateChanges: 0, actionsTriggered: 0, results: [] };
|
|
66
61
|
}
|
|
67
|
-
|
|
68
|
-
const results = await Promise.all(workspaces.map((workspace) => this.checkLimit(() => this.processWorkspace(workspace, settings))));
|
|
62
|
+
const results = await Promise.all(workspaces.map((workspace) => this.checkLimit(() => this.processWorkspace(workspace))));
|
|
69
63
|
const stateChanges = results.filter((r) => r.previousState !== r.newState).length;
|
|
70
|
-
const actionsTriggered = results.filter((r) => r.action.type === 'TRIGGERED_FIXER'
|
|
64
|
+
const actionsTriggered = results.filter((r) => r.action.type === 'TRIGGERED_FIXER').length;
|
|
71
65
|
if (stateChanges > 0 || actionsTriggered > 0) {
|
|
72
66
|
logger.info('Ratchet check completed', {
|
|
73
67
|
checked: workspaces.length,
|
|
@@ -85,16 +79,9 @@ class RatchetService {
|
|
|
85
79
|
if (!workspace) {
|
|
86
80
|
return null;
|
|
87
81
|
}
|
|
88
|
-
|
|
89
|
-
const settings = {
|
|
90
|
-
autoFixCi: userSettings.ratchetAutoFixCi,
|
|
91
|
-
autoFixReviews: userSettings.ratchetAutoFixReviews,
|
|
92
|
-
autoMerge: userSettings.ratchetAutoMerge,
|
|
93
|
-
allowedReviewers: userSettings.ratchetAllowedReviewers ?? [],
|
|
94
|
-
};
|
|
95
|
-
return this.processWorkspace(workspace, settings);
|
|
82
|
+
return this.processWorkspace(workspace);
|
|
96
83
|
}
|
|
97
|
-
async processWorkspace(workspace
|
|
84
|
+
async processWorkspace(workspace) {
|
|
98
85
|
if (this.isShuttingDown) {
|
|
99
86
|
return {
|
|
100
87
|
workspaceId: workspace.id,
|
|
@@ -104,46 +91,27 @@ class RatchetService {
|
|
|
104
91
|
};
|
|
105
92
|
}
|
|
106
93
|
try {
|
|
107
|
-
const
|
|
94
|
+
const authenticatedUsername = await this.getAuthenticatedUsernameCached();
|
|
95
|
+
const prStateInfo = await this.fetchPRState(workspace, authenticatedUsername);
|
|
108
96
|
if (!prStateInfo) {
|
|
97
|
+
const action = { type: 'ERROR', error: 'Failed to fetch PR state' };
|
|
98
|
+
this.logWorkspaceRatchetingDecision(workspace, workspace.ratchetState, workspace.ratchetState, action, null);
|
|
109
99
|
return {
|
|
110
100
|
workspaceId: workspace.id,
|
|
111
101
|
previousState: workspace.ratchetState,
|
|
112
102
|
newState: workspace.ratchetState,
|
|
113
|
-
action
|
|
103
|
+
action,
|
|
114
104
|
};
|
|
115
105
|
}
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
from: previousState,
|
|
122
|
-
to: newState,
|
|
123
|
-
prNumber: prStateInfo.prNumber,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const latestWorkspace = await workspaceAccessor.findById(workspace.id);
|
|
127
|
-
const shouldTakeAction = latestWorkspace?.ratchetEnabled ?? workspace.ratchetEnabled;
|
|
128
|
-
const action = shouldTakeAction
|
|
129
|
-
? await this.executeRatchetAction(workspace, newState, prStateInfo, settings)
|
|
130
|
-
: { type: 'DISABLED', reason: 'Workspace ratcheting disabled' };
|
|
131
|
-
const now = new Date();
|
|
132
|
-
const shouldRecordCiRunId = shouldTakeAction &&
|
|
133
|
-
prStateInfo.ciStatus === CIStatus.FAILURE &&
|
|
134
|
-
!!prStateInfo.ciRunId &&
|
|
135
|
-
(action.type === 'NOTIFIED_ACTIVE_FIXER' ||
|
|
136
|
-
(action.type === 'TRIGGERED_FIXER' && action.promptSent));
|
|
137
|
-
await workspaceAccessor.update(workspace.id, {
|
|
138
|
-
ratchetState: shouldTakeAction ? newState : RatchetState.IDLE,
|
|
139
|
-
ratchetLastCheckedAt: now,
|
|
140
|
-
...(shouldRecordCiRunId ? { ratchetLastCiRunId: prStateInfo.ciRunId } : {}),
|
|
141
|
-
...(prStateInfo.hasNewReviewComments ? { prReviewLastCheckedAt: now } : {}),
|
|
142
|
-
});
|
|
106
|
+
const decisionContext = await this.buildRatchetDecisionContext(workspace, prStateInfo);
|
|
107
|
+
const decision = this.decideRatchetAction(decisionContext);
|
|
108
|
+
const action = await this.applyRatchetDecision(decisionContext, decision);
|
|
109
|
+
await this.updateWorkspaceAfterCheck(workspace, prStateInfo, action, decisionContext.finalState);
|
|
110
|
+
this.logWorkspaceRatchetingDecision(workspace, decisionContext.previousState, decisionContext.finalState, action, prStateInfo, decisionContext);
|
|
143
111
|
return {
|
|
144
112
|
workspaceId: workspace.id,
|
|
145
|
-
previousState,
|
|
146
|
-
newState:
|
|
113
|
+
previousState: decisionContext.previousState,
|
|
114
|
+
newState: decisionContext.finalState,
|
|
147
115
|
action,
|
|
148
116
|
};
|
|
149
117
|
}
|
|
@@ -152,13 +120,306 @@ class RatchetService {
|
|
|
152
120
|
logger.error('Error processing workspace in ratchet', error, {
|
|
153
121
|
workspaceId: workspace.id,
|
|
154
122
|
});
|
|
123
|
+
const action = { type: 'ERROR', error: errorMessage };
|
|
124
|
+
this.logWorkspaceRatchetingDecision(workspace, workspace.ratchetState, workspace.ratchetState, action, null);
|
|
155
125
|
return {
|
|
156
126
|
workspaceId: workspace.id,
|
|
157
127
|
previousState: workspace.ratchetState,
|
|
158
128
|
newState: workspace.ratchetState,
|
|
159
|
-
action
|
|
129
|
+
action,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
logWorkspaceRatchetingDecision(workspace, previousState, newState, action, prStateInfo, decisionContext = null) {
|
|
134
|
+
const prNumber = prStateInfo?.prNumber ?? workspace.prNumber;
|
|
135
|
+
const prNumberLabel = prNumber ?? 'unknown';
|
|
136
|
+
const workspacePrPrefix = `workspace ${workspace.id} for PR #${prNumberLabel}`;
|
|
137
|
+
const context = this.buildRatchetingLogContext(workspace, previousState, newState, action, prStateInfo, prNumber, decisionContext);
|
|
138
|
+
if (action.type === 'TRIGGERED_FIXER') {
|
|
139
|
+
logger.info(`Ratcheting ${workspacePrPrefix}`, {
|
|
140
|
+
...context,
|
|
141
|
+
sessionId: action.sessionId,
|
|
142
|
+
promptSent: action.promptSent,
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const reason = this.describeNonRatchetingReason(action);
|
|
147
|
+
logger.info(`Not ratcheting ${workspacePrPrefix} because ${reason}`, context);
|
|
148
|
+
}
|
|
149
|
+
buildRatchetingLogContext(workspace, previousState, newState, action, prStateInfo, prNumber, decisionContext) {
|
|
150
|
+
const reviewDiagnostics = this.buildReviewTimestampDiagnostics(workspace, prStateInfo, decisionContext);
|
|
151
|
+
const snapshotDiagnostics = this.buildSnapshotDiagnostics(workspace, prStateInfo, decisionContext);
|
|
152
|
+
const latestReviewActivityAt = reviewDiagnostics.latestReviewActivityAtMs;
|
|
153
|
+
return {
|
|
154
|
+
workspaceId: workspace.id,
|
|
155
|
+
prUrl: workspace.prUrl,
|
|
156
|
+
prNumber,
|
|
157
|
+
prState: prStateInfo?.prState ?? null,
|
|
158
|
+
ciStatus: prStateInfo?.ciStatus ?? null,
|
|
159
|
+
hasChangesRequested: prStateInfo?.hasChangesRequested ?? null,
|
|
160
|
+
snapshotKey: prStateInfo?.snapshotKey ?? null,
|
|
161
|
+
ciSnapshotKey: snapshotDiagnostics.ciSnapshotKey,
|
|
162
|
+
snapshotComparison: snapshotDiagnostics.snapshotComparison,
|
|
163
|
+
previousState,
|
|
164
|
+
newState,
|
|
165
|
+
ratchetEnabled: workspace.ratchetEnabled,
|
|
166
|
+
ratchetActiveSessionId: workspace.ratchetActiveSessionId,
|
|
167
|
+
ratchetLastCiRunId: workspace.ratchetLastCiRunId,
|
|
168
|
+
ciStatusCheckRollup: prStateInfo?.statusCheckRollup ?? null,
|
|
169
|
+
ciFailedChecks: this.buildFailedCheckDiagnostics(prStateInfo),
|
|
170
|
+
prReviewLastCheckedAt: workspace.prReviewLastCheckedAt?.toISOString() ?? null,
|
|
171
|
+
latestReviewActivityAt: latestReviewActivityAt
|
|
172
|
+
? new Date(latestReviewActivityAt).toISOString()
|
|
173
|
+
: null,
|
|
174
|
+
reviewTimestampComparison: reviewDiagnostics.reviewTimestampComparison,
|
|
175
|
+
actionType: action.type,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
buildSnapshotDiagnostics(workspace, prStateInfo, decisionContext) {
|
|
179
|
+
if (!prStateInfo) {
|
|
180
|
+
return {
|
|
181
|
+
ciSnapshotKey: null,
|
|
182
|
+
snapshotComparison: null,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
ciSnapshotKey: this.computeCiSnapshotKey(prStateInfo.ciStatus, prStateInfo.statusCheckRollup),
|
|
187
|
+
snapshotComparison: {
|
|
188
|
+
previousDispatchSnapshotKey: workspace.ratchetLastCiRunId,
|
|
189
|
+
currentSnapshotKey: prStateInfo.snapshotKey,
|
|
190
|
+
changedSinceLastDispatch: decisionContext?.hasStateChangedSinceLastDispatch ??
|
|
191
|
+
this.hasStateChangedSinceLastDispatch(workspace, prStateInfo),
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
buildReviewTimestampDiagnostics(workspace, prStateInfo, decisionContext) {
|
|
196
|
+
const latestReviewActivityAtMs = prStateInfo?.latestReviewActivityAtMs ?? null;
|
|
197
|
+
const prReviewLastCheckedAtMs = workspace.prReviewLastCheckedAt?.getTime() ?? null;
|
|
198
|
+
const deltaMs = latestReviewActivityAtMs !== null && prReviewLastCheckedAtMs !== null
|
|
199
|
+
? latestReviewActivityAtMs - prReviewLastCheckedAtMs
|
|
200
|
+
: null;
|
|
201
|
+
if (!prStateInfo) {
|
|
202
|
+
return {
|
|
203
|
+
latestReviewActivityAtMs,
|
|
204
|
+
reviewTimestampComparison: null,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
latestReviewActivityAtMs,
|
|
209
|
+
reviewTimestampComparison: {
|
|
210
|
+
prReviewLastCheckedAt: workspace.prReviewLastCheckedAt?.toISOString() ?? null,
|
|
211
|
+
latestReviewActivityAt: latestReviewActivityAtMs !== null
|
|
212
|
+
? new Date(latestReviewActivityAtMs).toISOString()
|
|
213
|
+
: null,
|
|
214
|
+
prReviewLastCheckedAtMs,
|
|
215
|
+
latestReviewActivityAtMs,
|
|
216
|
+
deltaMs,
|
|
217
|
+
hasNewReviewActivitySinceLastDispatch: decisionContext?.hasNewReviewActivitySinceLastDispatch !== undefined
|
|
218
|
+
? decisionContext.hasNewReviewActivitySinceLastDispatch
|
|
219
|
+
: this.hasNewReviewActivitySinceLastDispatch(workspace, prStateInfo),
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
buildFailedCheckDiagnostics(prStateInfo) {
|
|
224
|
+
return (prStateInfo?.statusCheckRollup
|
|
225
|
+
?.filter((check) => {
|
|
226
|
+
const conclusion = check.conclusion?.toUpperCase();
|
|
227
|
+
return (conclusion === 'FAILURE' ||
|
|
228
|
+
conclusion === 'TIMED_OUT' ||
|
|
229
|
+
conclusion === 'CANCELLED' ||
|
|
230
|
+
conclusion === 'ERROR' ||
|
|
231
|
+
conclusion === 'ACTION_REQUIRED');
|
|
232
|
+
})
|
|
233
|
+
.map((check) => {
|
|
234
|
+
const runIdMatch = check.detailsUrl?.match(/\/actions\/runs\/(\d+)/);
|
|
235
|
+
return {
|
|
236
|
+
name: check.name ?? 'unknown',
|
|
237
|
+
status: check.status ?? null,
|
|
238
|
+
conclusion: check.conclusion ?? null,
|
|
239
|
+
runId: runIdMatch?.[1] ?? null,
|
|
240
|
+
detailsUrl: check.detailsUrl ?? null,
|
|
241
|
+
};
|
|
242
|
+
}) ?? []);
|
|
243
|
+
}
|
|
244
|
+
describeNonRatchetingReason(action) {
|
|
245
|
+
switch (action.type) {
|
|
246
|
+
case 'WAITING':
|
|
247
|
+
return action.reason;
|
|
248
|
+
case 'FIXER_ACTIVE':
|
|
249
|
+
return `Ratchet fixer session is already active (${action.sessionId})`;
|
|
250
|
+
case 'DISABLED':
|
|
251
|
+
return action.reason;
|
|
252
|
+
case 'COMPLETED':
|
|
253
|
+
return 'PR is already merged';
|
|
254
|
+
case 'ERROR':
|
|
255
|
+
return action.error;
|
|
256
|
+
}
|
|
257
|
+
const exhaustiveCheck = action;
|
|
258
|
+
throw new Error(`Unhandled ratchet action: ${JSON.stringify(exhaustiveCheck)}`);
|
|
259
|
+
}
|
|
260
|
+
async buildRatchetDecisionContext(workspace, prStateInfo) {
|
|
261
|
+
const previousState = workspace.ratchetState;
|
|
262
|
+
const newState = this.determineRatchetState(prStateInfo);
|
|
263
|
+
const finalState = workspace.ratchetEnabled ? newState : RatchetState.IDLE;
|
|
264
|
+
const hasNewReviewActivitySinceLastDispatch = this.hasNewReviewActivitySinceLastDispatch(workspace, prStateInfo);
|
|
265
|
+
const hasStateChangedSinceLastDispatch = this.hasStateChangedSinceLastDispatch(workspace, prStateInfo);
|
|
266
|
+
const isCleanPrWithNoNewReviewActivity = this.shouldSkipCleanPR(workspace, prStateInfo);
|
|
267
|
+
const activityChecks = await this.collectRatchetingActivityChecks(workspace, prStateInfo, isCleanPrWithNoNewReviewActivity, hasStateChangedSinceLastDispatch);
|
|
268
|
+
return {
|
|
269
|
+
workspace,
|
|
270
|
+
prStateInfo,
|
|
271
|
+
previousState,
|
|
272
|
+
newState,
|
|
273
|
+
finalState,
|
|
274
|
+
hasNewReviewActivitySinceLastDispatch,
|
|
275
|
+
hasStateChangedSinceLastDispatch,
|
|
276
|
+
isCleanPrWithNoNewReviewActivity,
|
|
277
|
+
activeRatchetSession: activityChecks.activeRatchetSession,
|
|
278
|
+
hasOtherActiveSession: activityChecks.hasOtherActiveSession,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async collectRatchetingActivityChecks(workspace, prStateInfo, isCleanPrWithNoNewReviewActivity, hasStateChangedSinceLastDispatch) {
|
|
282
|
+
if (!workspace.ratchetEnabled ||
|
|
283
|
+
prStateInfo.prState !== 'OPEN' ||
|
|
284
|
+
isCleanPrWithNoNewReviewActivity) {
|
|
285
|
+
return {
|
|
286
|
+
activeRatchetSession: null,
|
|
287
|
+
hasOtherActiveSession: false,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const activeRatchetSession = await this.getActiveRatchetSession(workspace);
|
|
291
|
+
if (activeRatchetSession) {
|
|
292
|
+
return {
|
|
293
|
+
activeRatchetSession,
|
|
294
|
+
hasOtherActiveSession: false,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
if (!hasStateChangedSinceLastDispatch) {
|
|
298
|
+
return {
|
|
299
|
+
activeRatchetSession: null,
|
|
300
|
+
hasOtherActiveSession: false,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return {
|
|
304
|
+
activeRatchetSession: null,
|
|
305
|
+
hasOtherActiveSession: await this.hasNonRatchetActiveSession(workspace.id),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
decideRatchetAction(context) {
|
|
309
|
+
if (!context.workspace.ratchetEnabled) {
|
|
310
|
+
return {
|
|
311
|
+
type: 'RETURN_ACTION',
|
|
312
|
+
action: { type: 'DISABLED', reason: 'Workspace ratcheting disabled' },
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
if (context.prStateInfo.prState === 'MERGED') {
|
|
316
|
+
return { type: 'RETURN_ACTION', action: { type: 'COMPLETED' } };
|
|
317
|
+
}
|
|
318
|
+
if (context.prStateInfo.prState !== 'OPEN') {
|
|
319
|
+
return {
|
|
320
|
+
type: 'RETURN_ACTION',
|
|
321
|
+
action: { type: 'WAITING', reason: 'PR is not open' },
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const isTerminalCIStatus = context.prStateInfo.ciStatus === CIStatus.SUCCESS ||
|
|
325
|
+
context.prStateInfo.ciStatus === CIStatus.FAILURE;
|
|
326
|
+
if (!isTerminalCIStatus) {
|
|
327
|
+
return {
|
|
328
|
+
type: 'RETURN_ACTION',
|
|
329
|
+
action: { type: 'WAITING', reason: 'Waiting for CI to complete (not in terminal state)' },
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
if (context.isCleanPrWithNoNewReviewActivity) {
|
|
333
|
+
return {
|
|
334
|
+
type: 'RETURN_ACTION',
|
|
335
|
+
action: { type: 'WAITING', reason: 'PR is clean (green CI and no new review activity)' },
|
|
160
336
|
};
|
|
161
337
|
}
|
|
338
|
+
if (context.activeRatchetSession) {
|
|
339
|
+
return { type: 'RETURN_ACTION', action: context.activeRatchetSession };
|
|
340
|
+
}
|
|
341
|
+
if (!context.hasStateChangedSinceLastDispatch) {
|
|
342
|
+
return {
|
|
343
|
+
type: 'RETURN_ACTION',
|
|
344
|
+
action: { type: 'WAITING', reason: 'PR state unchanged since last ratchet dispatch' },
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (context.hasOtherActiveSession) {
|
|
348
|
+
return {
|
|
349
|
+
type: 'RETURN_ACTION',
|
|
350
|
+
action: {
|
|
351
|
+
type: 'WAITING',
|
|
352
|
+
reason: 'Workspace is not idle (active non-ratchet chat session)',
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return { type: 'TRIGGER_FIXER' };
|
|
357
|
+
}
|
|
358
|
+
async applyRatchetDecision(context, decision) {
|
|
359
|
+
if (decision.type === 'RETURN_ACTION') {
|
|
360
|
+
return decision.action;
|
|
361
|
+
}
|
|
362
|
+
return await this.triggerFixer(context.workspace, context.prStateInfo);
|
|
363
|
+
}
|
|
364
|
+
shouldSkipCleanPR(workspace, prStateInfo) {
|
|
365
|
+
if (prStateInfo.ciStatus !== CIStatus.SUCCESS || prStateInfo.hasChangesRequested) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
return !this.hasNewReviewActivitySinceLastDispatch(workspace, prStateInfo);
|
|
369
|
+
}
|
|
370
|
+
hasNewReviewActivitySinceLastDispatch(workspace, prStateInfo) {
|
|
371
|
+
if (prStateInfo.latestReviewActivityAtMs === null) {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
if (!workspace.prReviewLastCheckedAt) {
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
return prStateInfo.latestReviewActivityAtMs > workspace.prReviewLastCheckedAt.getTime();
|
|
378
|
+
}
|
|
379
|
+
hasStateChangedSinceLastDispatch(workspace, prStateInfo) {
|
|
380
|
+
return workspace.ratchetLastCiRunId !== prStateInfo.snapshotKey;
|
|
381
|
+
}
|
|
382
|
+
async updateWorkspaceAfterCheck(workspace, prStateInfo, action, nextState) {
|
|
383
|
+
const now = new Date();
|
|
384
|
+
const dispatched = action.type === 'TRIGGERED_FIXER' && action.promptSent;
|
|
385
|
+
await workspaceAccessor.update(workspace.id, {
|
|
386
|
+
ratchetState: nextState,
|
|
387
|
+
ratchetLastCheckedAt: now,
|
|
388
|
+
...(dispatched
|
|
389
|
+
? {
|
|
390
|
+
ratchetLastCiRunId: prStateInfo.snapshotKey,
|
|
391
|
+
prReviewLastCheckedAt: now,
|
|
392
|
+
}
|
|
393
|
+
: {}),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
async getActiveRatchetSession(workspace) {
|
|
397
|
+
if (!workspace.ratchetActiveSessionId) {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
const session = await claudeSessionAccessor.findById(workspace.ratchetActiveSessionId);
|
|
401
|
+
if (!session) {
|
|
402
|
+
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
if (session.status !== SessionStatus.RUNNING) {
|
|
406
|
+
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
if (!sessionService.isSessionRunning(session.id)) {
|
|
410
|
+
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
return { type: 'FIXER_ACTIVE', sessionId: workspace.ratchetActiveSessionId };
|
|
414
|
+
}
|
|
415
|
+
async hasNonRatchetActiveSession(workspaceId) {
|
|
416
|
+
const sessions = await claudeSessionAccessor.findByWorkspaceId(workspaceId);
|
|
417
|
+
return sessions.some((session) => {
|
|
418
|
+
if (session.workflow === RATCHET_WORKFLOW) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
return sessionService.isSessionWorking(session.id);
|
|
422
|
+
});
|
|
162
423
|
}
|
|
163
424
|
resolveRatchetPrContext(workspace) {
|
|
164
425
|
const prInfo = githubCLIService.extractPRInfo(workspace.prUrl);
|
|
@@ -179,7 +440,7 @@ class RatchetService {
|
|
|
179
440
|
prNumber,
|
|
180
441
|
};
|
|
181
442
|
}
|
|
182
|
-
async fetchPRState(workspace,
|
|
443
|
+
async fetchPRState(workspace, authenticatedUsername) {
|
|
183
444
|
const prContext = this.resolveRatchetPrContext(workspace);
|
|
184
445
|
if (!prContext) {
|
|
185
446
|
return null;
|
|
@@ -190,56 +451,21 @@ class RatchetService {
|
|
|
190
451
|
githubCLIService.getReviewComments(prContext.repo, prContext.prNumber),
|
|
191
452
|
]);
|
|
192
453
|
const statusCheckRollup = prDetails.statusCheckRollup?.map((check) => ({
|
|
454
|
+
name: check.name,
|
|
193
455
|
status: check.status,
|
|
194
456
|
conclusion: check.conclusion ?? undefined,
|
|
457
|
+
detailsUrl: check.detailsUrl,
|
|
195
458
|
})) ?? null;
|
|
196
459
|
const ciStatus = githubCLIService.computeCIStatus(statusCheckRollup);
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const conclusion = String(check.conclusion || check.status);
|
|
201
|
-
if (conclusion === 'FAILURE' ||
|
|
202
|
-
conclusion === 'ACTION_REQUIRED' ||
|
|
203
|
-
conclusion === 'ERROR') {
|
|
204
|
-
failedChecks.push({
|
|
205
|
-
name: check.name || 'Unknown check',
|
|
206
|
-
conclusion,
|
|
207
|
-
detailsUrl: check.detailsUrl,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const ciRunId = this.extractFailedCiSignature(failedChecks);
|
|
213
|
-
const hasChangesRequested = prDetails.reviews.some((r) => r.state === 'CHANGES_REQUESTED');
|
|
214
|
-
const lastCheckedAt = workspace.prReviewLastCheckedAt?.getTime() ?? 0;
|
|
215
|
-
const filterByReviewer = allowedReviewers.length > 0;
|
|
216
|
-
const newReviewComments = reviewComments.filter((comment) => {
|
|
217
|
-
const createdTime = new Date(comment.createdAt).getTime();
|
|
218
|
-
const updatedTime = new Date(comment.updatedAt).getTime();
|
|
219
|
-
const isNewOrEdited = createdTime > lastCheckedAt || updatedTime > lastCheckedAt;
|
|
220
|
-
const isAllowedReviewer = !filterByReviewer || allowedReviewers.includes(comment.author.login);
|
|
221
|
-
return isNewOrEdited && isAllowedReviewer;
|
|
222
|
-
});
|
|
223
|
-
const newPRComments = prDetails.comments.filter((comment) => {
|
|
224
|
-
const createdTime = new Date(comment.createdAt).getTime();
|
|
225
|
-
const updatedTime = new Date(comment.updatedAt).getTime();
|
|
226
|
-
const isNewOrEdited = createdTime > lastCheckedAt || updatedTime > lastCheckedAt;
|
|
227
|
-
const isAllowedReviewer = !filterByReviewer || allowedReviewers.includes(comment.author.login);
|
|
228
|
-
return isNewOrEdited && isAllowedReviewer;
|
|
229
|
-
});
|
|
230
|
-
const hasNewReviewComments = newReviewComments.length > 0 || newPRComments.length > 0;
|
|
460
|
+
const hasChangesRequested = prDetails.reviewDecision === 'CHANGES_REQUESTED';
|
|
461
|
+
const latestReviewActivityAtMs = this.computeLatestReviewActivityAtMs(prDetails, reviewComments, authenticatedUsername);
|
|
462
|
+
const snapshotKey = this.computeDispatchSnapshotKey(ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusCheckRollup);
|
|
231
463
|
return {
|
|
232
464
|
ciStatus,
|
|
233
|
-
|
|
465
|
+
snapshotKey,
|
|
234
466
|
hasChangesRequested,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
ciRunId,
|
|
238
|
-
reviews: prDetails.reviews,
|
|
239
|
-
comments: prDetails.comments,
|
|
240
|
-
reviewComments,
|
|
241
|
-
newReviewComments,
|
|
242
|
-
newPRComments,
|
|
467
|
+
latestReviewActivityAtMs,
|
|
468
|
+
statusCheckRollup,
|
|
243
469
|
prState: prDetails.state,
|
|
244
470
|
prNumber: prDetails.number,
|
|
245
471
|
};
|
|
@@ -252,168 +478,95 @@ class RatchetService {
|
|
|
252
478
|
return null;
|
|
253
479
|
}
|
|
254
480
|
}
|
|
481
|
+
computeDispatchSnapshotKey(ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusChecks) {
|
|
482
|
+
const ciKey = this.computeCiSnapshotKey(ciStatus, statusChecks);
|
|
483
|
+
const reviewKey = `${hasChangesRequested ? 'changes-requested' : 'no-changes-requested'}:${latestReviewActivityAtMs ?? 'none'}`;
|
|
484
|
+
return `${ciKey}|${reviewKey}`;
|
|
485
|
+
}
|
|
486
|
+
computeCiSnapshotKey(ciStatus, statusChecks) {
|
|
487
|
+
if (ciStatus !== CIStatus.FAILURE) {
|
|
488
|
+
return `ci:${ciStatus}`;
|
|
489
|
+
}
|
|
490
|
+
const failedChecks = statusChecks?.filter((check) => {
|
|
491
|
+
const conclusion = check.conclusion?.toUpperCase();
|
|
492
|
+
return (conclusion === 'FAILURE' ||
|
|
493
|
+
conclusion === 'TIMED_OUT' ||
|
|
494
|
+
conclusion === 'CANCELLED' ||
|
|
495
|
+
conclusion === 'ERROR' ||
|
|
496
|
+
conclusion === 'ACTION_REQUIRED');
|
|
497
|
+
}) ?? [];
|
|
498
|
+
if (failedChecks.length === 0) {
|
|
499
|
+
return 'ci:FAILURE:unknown';
|
|
500
|
+
}
|
|
501
|
+
const signature = failedChecks
|
|
502
|
+
.map((check) => {
|
|
503
|
+
const runIdMatch = check.detailsUrl?.match(/\/actions\/runs\/(\d+)/);
|
|
504
|
+
const runId = runIdMatch?.[1];
|
|
505
|
+
const stableCheckIdentity = runId ?? check.detailsUrl ?? 'no-run-id-or-details-url';
|
|
506
|
+
return `${check.name ?? 'unknown'}:${check.conclusion ?? 'UNKNOWN'}:${stableCheckIdentity}`;
|
|
507
|
+
})
|
|
508
|
+
.sort()
|
|
509
|
+
.join('|');
|
|
510
|
+
return `ci:FAILURE:${signature}`;
|
|
511
|
+
}
|
|
512
|
+
computeLatestReviewActivityAtMs(prDetails, reviewComments, authenticatedUsername) {
|
|
513
|
+
const entries = [
|
|
514
|
+
...prDetails.reviews.map((review) => ({
|
|
515
|
+
authorLogin: review.author.login,
|
|
516
|
+
timestamp: review.submittedAt,
|
|
517
|
+
})),
|
|
518
|
+
...prDetails.comments.map((comment) => ({
|
|
519
|
+
authorLogin: comment.author.login,
|
|
520
|
+
timestamp: comment.updatedAt,
|
|
521
|
+
})),
|
|
522
|
+
...reviewComments.map((reviewComment) => ({
|
|
523
|
+
authorLogin: reviewComment.author.login,
|
|
524
|
+
timestamp: reviewComment.updatedAt,
|
|
525
|
+
})),
|
|
526
|
+
];
|
|
527
|
+
const timestamps = entries
|
|
528
|
+
.filter((entry) => !this.isIgnoredReviewAuthor(entry.authorLogin, authenticatedUsername))
|
|
529
|
+
.map((entry) => Date.parse(entry.timestamp))
|
|
530
|
+
.filter((timestamp) => Number.isFinite(timestamp));
|
|
531
|
+
return timestamps.length > 0 ? Math.max(...timestamps) : null;
|
|
532
|
+
}
|
|
533
|
+
isIgnoredReviewAuthor(authorLogin, authenticatedUsername) {
|
|
534
|
+
if (!authenticatedUsername) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
return authorLogin === authenticatedUsername;
|
|
538
|
+
}
|
|
539
|
+
async getAuthenticatedUsernameCached() {
|
|
540
|
+
const nowMs = Date.now();
|
|
541
|
+
if (this.cachedAuthenticatedUsername && this.cachedAuthenticatedUsername.expiresAtMs > nowMs) {
|
|
542
|
+
return this.cachedAuthenticatedUsername.value;
|
|
543
|
+
}
|
|
544
|
+
const username = await githubCLIService.getAuthenticatedUsername();
|
|
545
|
+
this.cachedAuthenticatedUsername = {
|
|
546
|
+
value: username,
|
|
547
|
+
expiresAtMs: nowMs + AUTHENTICATED_USERNAME_CACHE_TTL_MS,
|
|
548
|
+
};
|
|
549
|
+
return username;
|
|
550
|
+
}
|
|
255
551
|
determineRatchetState(pr) {
|
|
256
552
|
if (pr.prState === 'MERGED') {
|
|
257
553
|
return RatchetState.MERGED;
|
|
258
554
|
}
|
|
259
|
-
if (pr.
|
|
555
|
+
if (pr.prState !== 'OPEN') {
|
|
556
|
+
return RatchetState.IDLE;
|
|
557
|
+
}
|
|
558
|
+
if (pr.ciStatus === CIStatus.PENDING || pr.ciStatus === CIStatus.UNKNOWN) {
|
|
260
559
|
return RatchetState.CI_RUNNING;
|
|
261
560
|
}
|
|
262
561
|
if (pr.ciStatus === CIStatus.FAILURE) {
|
|
263
562
|
return RatchetState.CI_FAILED;
|
|
264
563
|
}
|
|
265
|
-
if (pr.
|
|
266
|
-
return RatchetState.CI_RUNNING;
|
|
267
|
-
}
|
|
268
|
-
if (pr.hasChangesRequested || pr.hasNewReviewComments) {
|
|
564
|
+
if (pr.hasChangesRequested) {
|
|
269
565
|
return RatchetState.REVIEW_PENDING;
|
|
270
566
|
}
|
|
271
567
|
return RatchetState.READY;
|
|
272
568
|
}
|
|
273
|
-
async
|
|
274
|
-
const existingFixerResult = await this.handleExistingFixerSession(workspace, state, prStateInfo);
|
|
275
|
-
if (existingFixerResult) {
|
|
276
|
-
return existingFixerResult;
|
|
277
|
-
}
|
|
278
|
-
return this.handleStateWithoutActiveFixer(workspace, state, prStateInfo, settings);
|
|
279
|
-
}
|
|
280
|
-
async handleExistingFixerSession(workspace, state, prStateInfo) {
|
|
281
|
-
if (!workspace.ratchetActiveSessionId) {
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
const session = await claudeSessionAccessor.findById(workspace.ratchetActiveSessionId);
|
|
285
|
-
const client = sessionService.getClient(workspace.ratchetActiveSessionId);
|
|
286
|
-
if (!session) {
|
|
287
|
-
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
288
|
-
return null;
|
|
289
|
-
}
|
|
290
|
-
if (session.status === SessionStatus.IDLE) {
|
|
291
|
-
if (!client) {
|
|
292
|
-
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
293
|
-
}
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
if (session.status !== SessionStatus.RUNNING || !client) {
|
|
297
|
-
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
const shouldNotify = this.shouldNotifyActiveFixer(state, workspace.ratchetLastNotifiedState, prStateInfo.ciRunId, workspace.ratchetLastCiRunId);
|
|
301
|
-
if (!shouldNotify) {
|
|
302
|
-
return { type: 'FIXER_ACTIVE', sessionId: workspace.ratchetActiveSessionId };
|
|
303
|
-
}
|
|
304
|
-
const delivered = await this.notifyActiveFixer(workspace, state, prStateInfo, client);
|
|
305
|
-
if (delivered) {
|
|
306
|
-
return {
|
|
307
|
-
type: 'NOTIFIED_ACTIVE_FIXER',
|
|
308
|
-
sessionId: workspace.ratchetActiveSessionId,
|
|
309
|
-
issue: state,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
logger.warn('Failed to notify active fixer; clearing stale reference and retrying', {
|
|
313
|
-
workspaceId: workspace.id,
|
|
314
|
-
sessionId: workspace.ratchetActiveSessionId,
|
|
315
|
-
state,
|
|
316
|
-
});
|
|
317
|
-
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
async handleStateWithoutActiveFixer(workspace, state, prStateInfo, settings) {
|
|
321
|
-
switch (state) {
|
|
322
|
-
case RatchetState.IDLE:
|
|
323
|
-
return { type: 'WAITING', reason: 'No PR or ratchet not active' };
|
|
324
|
-
case RatchetState.CI_RUNNING:
|
|
325
|
-
return { type: 'WAITING', reason: 'CI is running' };
|
|
326
|
-
case RatchetState.CI_FAILED:
|
|
327
|
-
if (!settings.autoFixCi) {
|
|
328
|
-
return { type: 'DISABLED', reason: 'CI auto-fix disabled' };
|
|
329
|
-
}
|
|
330
|
-
if (prStateInfo.ciRunId && prStateInfo.ciRunId === workspace.ratchetLastCiRunId) {
|
|
331
|
-
logger.info('Skipping CI fixer dispatch — already handled this CI run', {
|
|
332
|
-
workspaceId: workspace.id,
|
|
333
|
-
ciRunId: prStateInfo.ciRunId,
|
|
334
|
-
});
|
|
335
|
-
return {
|
|
336
|
-
type: 'WAITING',
|
|
337
|
-
reason: 'Already dispatched fixer for this CI run; waiting for new run',
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
return await this.triggerFixer(workspace, 'ci', prStateInfo, settings);
|
|
341
|
-
case RatchetState.REVIEW_PENDING:
|
|
342
|
-
if (!settings.autoFixReviews) {
|
|
343
|
-
return { type: 'DISABLED', reason: 'Review auto-fix disabled' };
|
|
344
|
-
}
|
|
345
|
-
return await this.triggerFixer(workspace, 'review', prStateInfo, settings);
|
|
346
|
-
case RatchetState.READY:
|
|
347
|
-
if (settings.autoMerge) {
|
|
348
|
-
logger.info('PR ready for merge (auto-merge not yet implemented)', {
|
|
349
|
-
workspaceId: workspace.id,
|
|
350
|
-
prNumber: workspace.prNumber,
|
|
351
|
-
});
|
|
352
|
-
return { type: 'READY_FOR_MERGE' };
|
|
353
|
-
}
|
|
354
|
-
return { type: 'READY_FOR_MERGE' };
|
|
355
|
-
case RatchetState.MERGED:
|
|
356
|
-
return { type: 'COMPLETED' };
|
|
357
|
-
default:
|
|
358
|
-
return { type: 'WAITING', reason: `Unknown state: ${state}` };
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
shouldNotifyActiveFixer(currentState, lastNotifiedState, currentCiRunId, lastCiRunId) {
|
|
362
|
-
if (currentState === RatchetState.CI_FAILED &&
|
|
363
|
-
currentCiRunId &&
|
|
364
|
-
currentCiRunId !== lastCiRunId) {
|
|
365
|
-
return true;
|
|
366
|
-
}
|
|
367
|
-
if (currentState === lastNotifiedState) {
|
|
368
|
-
return false;
|
|
369
|
-
}
|
|
370
|
-
return currentState === RatchetState.CI_FAILED;
|
|
371
|
-
}
|
|
372
|
-
async notifyActiveFixer(workspace, state, prStateInfo, client) {
|
|
373
|
-
if (!workspace.ratchetActiveSessionId) {
|
|
374
|
-
logger.warn('notifyActiveFixer called without active session ID', {
|
|
375
|
-
workspaceId: workspace.id,
|
|
376
|
-
});
|
|
377
|
-
return false;
|
|
378
|
-
}
|
|
379
|
-
let message = '';
|
|
380
|
-
if (state === RatchetState.CI_FAILED) {
|
|
381
|
-
message = `⚠️ **CI Failed**
|
|
382
|
-
|
|
383
|
-
The CI checks have failed. This may be due to your recent changes.
|
|
384
|
-
|
|
385
|
-
**Failed checks:**
|
|
386
|
-
${prStateInfo.failedChecks.map((c) => `- **${c.name}**: ${c.conclusion}${c.detailsUrl ? ` ([logs](${c.detailsUrl}))` : ''}`).join('\n')}
|
|
387
|
-
|
|
388
|
-
Please investigate and fix the CI failure before continuing with your current task.
|
|
389
|
-
Use \`gh pr checks ${prStateInfo.prNumber}\` to see current status.
|
|
390
|
-
Use \`gh run view <run-id> --log-failed\` to see detailed logs.`;
|
|
391
|
-
}
|
|
392
|
-
if (message) {
|
|
393
|
-
try {
|
|
394
|
-
await client.sendMessage(message);
|
|
395
|
-
logger.info('Notified active fixer about state change', {
|
|
396
|
-
workspaceId: workspace.id,
|
|
397
|
-
sessionId: workspace.ratchetActiveSessionId,
|
|
398
|
-
state,
|
|
399
|
-
});
|
|
400
|
-
await workspaceAccessor.update(workspace.id, {
|
|
401
|
-
ratchetLastNotifiedState: state,
|
|
402
|
-
});
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
405
|
-
catch (error) {
|
|
406
|
-
logger.warn('Failed to notify fixer about state change', {
|
|
407
|
-
workspaceId: workspace.id,
|
|
408
|
-
state,
|
|
409
|
-
error,
|
|
410
|
-
});
|
|
411
|
-
return false;
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return false;
|
|
415
|
-
}
|
|
416
|
-
async triggerFixer(workspace, fixerType, prStateInfo, settings) {
|
|
569
|
+
async triggerFixer(workspace, prStateInfo) {
|
|
417
570
|
try {
|
|
418
571
|
const result = await fixerSessionService.acquireAndDispatch({
|
|
419
572
|
workspaceId: workspace.id,
|
|
@@ -421,30 +574,31 @@ Use \`gh run view <run-id> --log-failed\` to see detailed logs.`;
|
|
|
421
574
|
sessionName: 'Ratchet',
|
|
422
575
|
runningIdleAction: 'restart',
|
|
423
576
|
dispatchMode: 'start_empty_and_send',
|
|
424
|
-
buildPrompt: () =>
|
|
577
|
+
buildPrompt: () => buildRatchetDispatchPrompt(workspace.prUrl, prStateInfo.prNumber),
|
|
425
578
|
beforeStart: ({ sessionId, prompt }) => {
|
|
426
|
-
|
|
579
|
+
sessionStoreService.injectCommittedUserMessage(sessionId, prompt);
|
|
427
580
|
},
|
|
428
581
|
});
|
|
429
582
|
if (result.status === 'started') {
|
|
430
|
-
const
|
|
583
|
+
const promptSent = result.promptSent ?? true;
|
|
584
|
+
if (!promptSent) {
|
|
585
|
+
logger.warn('Ratchet session started but prompt delivery failed', {
|
|
586
|
+
workspaceId: workspace.id,
|
|
587
|
+
sessionId: result.sessionId,
|
|
588
|
+
});
|
|
589
|
+
await workspaceAccessor.update(workspace.id, { ratchetActiveSessionId: null });
|
|
590
|
+
if (sessionService.isSessionRunning(result.sessionId)) {
|
|
591
|
+
await sessionService.stopClaudeSession(result.sessionId);
|
|
592
|
+
}
|
|
593
|
+
return { type: 'ERROR', error: 'Failed to deliver initial ratchet prompt' };
|
|
594
|
+
}
|
|
431
595
|
await workspaceAccessor.update(workspace.id, {
|
|
432
596
|
ratchetActiveSessionId: result.sessionId,
|
|
433
|
-
...(shouldMarkNotified && {
|
|
434
|
-
ratchetLastNotifiedState: this.determineRatchetState(prStateInfo),
|
|
435
|
-
}),
|
|
436
|
-
});
|
|
437
|
-
logger.info('Ratchet fixer session started', {
|
|
438
|
-
workspaceId: workspace.id,
|
|
439
|
-
sessionId: result.sessionId,
|
|
440
|
-
fixerType,
|
|
441
|
-
prNumber: prStateInfo.prNumber,
|
|
442
597
|
});
|
|
443
598
|
return {
|
|
444
599
|
type: 'TRIGGERED_FIXER',
|
|
445
600
|
sessionId: result.sessionId,
|
|
446
|
-
|
|
447
|
-
promptSent: shouldMarkNotified,
|
|
601
|
+
promptSent,
|
|
448
602
|
};
|
|
449
603
|
}
|
|
450
604
|
if (result.status === 'already_active') {
|
|
@@ -460,169 +614,10 @@ Use \`gh run view <run-id> --log-failed\` to see detailed logs.`;
|
|
|
460
614
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
461
615
|
logger.error('Failed to trigger ratchet fixer', error, {
|
|
462
616
|
workspaceId: workspace.id,
|
|
463
|
-
fixerType,
|
|
464
617
|
});
|
|
465
618
|
return { type: 'ERROR', error: errorMessage };
|
|
466
619
|
}
|
|
467
620
|
}
|
|
468
|
-
buildInitialPrompt(fixerType, prUrl, prStateInfo, settings) {
|
|
469
|
-
const prNumber = prStateInfo.prNumber;
|
|
470
|
-
switch (fixerType) {
|
|
471
|
-
case 'ci':
|
|
472
|
-
return this.buildCIFixPrompt(prNumber, prUrl, prStateInfo);
|
|
473
|
-
case 'review':
|
|
474
|
-
return this.buildReviewFixPrompt(prNumber, prUrl, prStateInfo, settings);
|
|
475
|
-
default:
|
|
476
|
-
return `Please investigate the issue with PR #${prNumber}.`;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
buildCIFixPrompt(prNumber, prUrl, prStateInfo) {
|
|
480
|
-
let prompt = `## CI Failure Alert
|
|
481
|
-
|
|
482
|
-
The CI checks for PR #${prNumber} have failed.
|
|
483
|
-
|
|
484
|
-
**PR URL:** ${prUrl}
|
|
485
|
-
|
|
486
|
-
`;
|
|
487
|
-
if (prStateInfo.failedChecks.length > 0) {
|
|
488
|
-
prompt += `### Failed Checks\n\n`;
|
|
489
|
-
for (const check of prStateInfo.failedChecks) {
|
|
490
|
-
prompt += `- **${check.name}**: ${check.conclusion}`;
|
|
491
|
-
if (check.detailsUrl) {
|
|
492
|
-
prompt += ` ([logs](${check.detailsUrl}))`;
|
|
493
|
-
}
|
|
494
|
-
prompt += `\n`;
|
|
495
|
-
}
|
|
496
|
-
prompt += `\n`;
|
|
497
|
-
}
|
|
498
|
-
prompt += `### Next Steps
|
|
499
|
-
|
|
500
|
-
1. Sync with main first: \`git fetch origin && git merge origin/main\` (resolve any conflicts)
|
|
501
|
-
2. Use \`gh pr checks ${prNumber}\` to see the current check status
|
|
502
|
-
3. Use \`gh run view <run-id> --log-failed\` to see detailed failure logs
|
|
503
|
-
4. Identify the root cause and implement a fix
|
|
504
|
-
5. Run tests locally to verify: \`pnpm test\`
|
|
505
|
-
6. Run type checking: \`pnpm typecheck\`
|
|
506
|
-
7. Run linting: \`pnpm check:fix\`
|
|
507
|
-
8. Commit and push your changes
|
|
508
|
-
|
|
509
|
-
Please investigate and fix these CI failures.`;
|
|
510
|
-
return prompt;
|
|
511
|
-
}
|
|
512
|
-
filterByAllowedReviewers(items, allowedReviewers) {
|
|
513
|
-
if (allowedReviewers.length === 0) {
|
|
514
|
-
return items;
|
|
515
|
-
}
|
|
516
|
-
return items.filter((item) => allowedReviewers.includes(item.author.login));
|
|
517
|
-
}
|
|
518
|
-
formatReviewsSection(reviews) {
|
|
519
|
-
if (reviews.length === 0) {
|
|
520
|
-
return '';
|
|
521
|
-
}
|
|
522
|
-
const lines = ['### Reviews Requesting Changes\n\n'];
|
|
523
|
-
for (const review of reviews) {
|
|
524
|
-
lines.push(`**${review.author.login}** (${new Date(review.submittedAt).toLocaleDateString()}):\n`);
|
|
525
|
-
if (review.body) {
|
|
526
|
-
lines.push(`> ${review.body.split('\n').join('\n> ')}\n`);
|
|
527
|
-
}
|
|
528
|
-
lines.push('\n');
|
|
529
|
-
}
|
|
530
|
-
return lines.join('');
|
|
531
|
-
}
|
|
532
|
-
formatCodeCommentsSection(comments) {
|
|
533
|
-
if (comments.length === 0) {
|
|
534
|
-
return '';
|
|
535
|
-
}
|
|
536
|
-
const lines = ['### Review Comments on Code\n\n'];
|
|
537
|
-
for (const comment of comments) {
|
|
538
|
-
const location = comment.line ? `:${comment.line}` : '';
|
|
539
|
-
lines.push(`**${comment.author.login}** on \`${comment.path}\`${location}:\n`);
|
|
540
|
-
lines.push(`> ${comment.body.split('\n').join('\n> ')}\n`);
|
|
541
|
-
lines.push(`> [View comment](${comment.url})\n\n`);
|
|
542
|
-
}
|
|
543
|
-
return lines.join('');
|
|
544
|
-
}
|
|
545
|
-
formatPRCommentsSection(comments) {
|
|
546
|
-
if (comments.length === 0) {
|
|
547
|
-
return '';
|
|
548
|
-
}
|
|
549
|
-
const lines = ['### PR Comments\n\n'];
|
|
550
|
-
for (const comment of comments) {
|
|
551
|
-
lines.push(`**${comment.author.login}** (${new Date(comment.createdAt).toLocaleDateString()}):\n`);
|
|
552
|
-
lines.push(`> ${comment.body.split('\n').join('\n> ')}\n\n`);
|
|
553
|
-
}
|
|
554
|
-
return lines.join('');
|
|
555
|
-
}
|
|
556
|
-
buildReviewFixPrompt(prNumber, prUrl, prStateInfo, settings) {
|
|
557
|
-
const parts = [
|
|
558
|
-
`## PR Review Comments Alert
|
|
559
|
-
|
|
560
|
-
New review comments have been received on PR #${prNumber}.
|
|
561
|
-
|
|
562
|
-
**PR URL:** ${prUrl}
|
|
563
|
-
|
|
564
|
-
`,
|
|
565
|
-
];
|
|
566
|
-
const changesRequestedReviews = prStateInfo.reviews.filter((r) => r.state === 'CHANGES_REQUESTED');
|
|
567
|
-
const filteredReviews = this.filterByAllowedReviewers(changesRequestedReviews, settings.allowedReviewers);
|
|
568
|
-
parts.push(this.formatReviewsSection(filteredReviews));
|
|
569
|
-
parts.push(this.formatCodeCommentsSection(prStateInfo.newReviewComments));
|
|
570
|
-
parts.push(this.formatPRCommentsSection(prStateInfo.newPRComments));
|
|
571
|
-
const reviewerSet = new Set([
|
|
572
|
-
...filteredReviews.map((r) => r.author.login),
|
|
573
|
-
...prStateInfo.newReviewComments.map((c) => c.author.login),
|
|
574
|
-
...prStateInfo.newPRComments.map((c) => c.author.login),
|
|
575
|
-
]);
|
|
576
|
-
const reviewerMentions = Array.from(reviewerSet)
|
|
577
|
-
.map((r) => `@${r}`)
|
|
578
|
-
.join(' ');
|
|
579
|
-
parts.push(`### Instructions
|
|
580
|
-
|
|
581
|
-
**IMPORTANT: Execute autonomously. Do not ask the user for input or confirmation. Complete all steps without waiting.**
|
|
582
|
-
|
|
583
|
-
1. **Sync with main** - Before making changes, sync your branch:
|
|
584
|
-
\`\`\`bash
|
|
585
|
-
git fetch origin && git merge origin/main
|
|
586
|
-
\`\`\`
|
|
587
|
-
Resolve any conflicts as part of your fix.
|
|
588
|
-
|
|
589
|
-
2. **Analyze each comment** - Determine what changes are requested. If a comment is purely informational (e.g., automated coverage reports with no action items), note it and move on.
|
|
590
|
-
|
|
591
|
-
3. **Implement fixes** - Address each actionable comment systematically. Make focused changes that directly address the feedback.
|
|
592
|
-
|
|
593
|
-
4. **Verify changes**:
|
|
594
|
-
\`\`\`bash
|
|
595
|
-
pnpm test && pnpm typecheck && pnpm check:fix
|
|
596
|
-
\`\`\`
|
|
597
|
-
|
|
598
|
-
5. **Commit and push**:
|
|
599
|
-
\`\`\`bash
|
|
600
|
-
git add -A && git commit -m "Address review comments" && git push
|
|
601
|
-
\`\`\`
|
|
602
|
-
|
|
603
|
-
6. **Post re-review request** - After pushing, notify the reviewers:
|
|
604
|
-
\`\`\`bash
|
|
605
|
-
gh pr comment ${prNumber} --body "${reviewerMentions} I've addressed the review comments. Please re-review when you have a chance."
|
|
606
|
-
\`\`\`
|
|
607
|
-
|
|
608
|
-
**Do not ask the user what to do. Analyze the comments, implement fixes, push, and request re-review.**`);
|
|
609
|
-
return parts.join('');
|
|
610
|
-
}
|
|
611
|
-
extractFailedCiSignature(failedChecks) {
|
|
612
|
-
if (failedChecks.length === 0) {
|
|
613
|
-
return null;
|
|
614
|
-
}
|
|
615
|
-
const failedEntries = [];
|
|
616
|
-
for (const check of failedChecks) {
|
|
617
|
-
const runIdMatch = check.detailsUrl?.match(/\/actions\/runs\/(\d+)/);
|
|
618
|
-
const runId = runIdMatch?.[1] ?? 'no-run-id';
|
|
619
|
-
failedEntries.push(`${check.name}:${runId}`);
|
|
620
|
-
}
|
|
621
|
-
if (failedEntries.length === 0) {
|
|
622
|
-
return null;
|
|
623
|
-
}
|
|
624
|
-
return failedEntries.sort().join('|');
|
|
625
|
-
}
|
|
626
621
|
}
|
|
627
622
|
export const ratchetService = new RatchetService();
|
|
628
623
|
//# sourceMappingURL=ratchet.service.js.map
|