@repo-os/repoos 0.5.45
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/LICENSE.md +110 -0
- package/README.md +272 -0
- package/dist/.build-info.json +4 -0
- package/dist/.build-stamp.json +3 -0
- package/dist/cli/colors.d.ts +13 -0
- package/dist/cli/colors.js +37 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +221 -0
- package/dist/commands/check.d.ts +170 -0
- package/dist/commands/check.js +1227 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.js +70 -0
- package/dist/commands/gc.d.ts +1 -0
- package/dist/commands/gc.js +57 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +699 -0
- package/dist/commands/screenshots.d.ts +7 -0
- package/dist/commands/screenshots.js +77 -0
- package/dist/commands/serve.d.ts +55 -0
- package/dist/commands/serve.js +204 -0
- package/dist/commands/status.d.ts +140 -0
- package/dist/commands/status.js +569 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +59 -0
- package/dist/commands/tasks.d.ts +47 -0
- package/dist/commands/tasks.js +334 -0
- package/dist/commands/tunnel.d.ts +1 -0
- package/dist/commands/tunnel.js +1271 -0
- package/dist/commands/ui-harness.d.ts +53 -0
- package/dist/commands/ui-harness.js +62 -0
- package/dist/commands/ui-smoke.d.ts +6 -0
- package/dist/commands/ui-smoke.js +220 -0
- package/dist/commands/upgrade.d.ts +15 -0
- package/dist/commands/upgrade.js +188 -0
- package/dist/core/auth-store.d.ts +81 -0
- package/dist/core/auth-store.js +530 -0
- package/dist/core/auth.d.ts +75 -0
- package/dist/core/auth.js +124 -0
- package/dist/core/bootstrap.d.ts +41 -0
- package/dist/core/bootstrap.js +236 -0
- package/dist/core/build.d.ts +92 -0
- package/dist/core/build.js +249 -0
- package/dist/core/canary.d.ts +10 -0
- package/dist/core/canary.js +14 -0
- package/dist/core/check-failure-summary.d.ts +10 -0
- package/dist/core/check-failure-summary.js +34 -0
- package/dist/core/config.d.ts +160 -0
- package/dist/core/config.js +1288 -0
- package/dist/core/context-pack.d.ts +74 -0
- package/dist/core/context-pack.js +778 -0
- package/dist/core/db.d.ts +236 -0
- package/dist/core/db.js +747 -0
- package/dist/core/detect.d.ts +69 -0
- package/dist/core/detect.js +251 -0
- package/dist/core/docs.d.ts +76 -0
- package/dist/core/docs.js +225 -0
- package/dist/core/frontmatter.d.ts +29 -0
- package/dist/core/frontmatter.js +215 -0
- package/dist/core/git.d.ts +505 -0
- package/dist/core/git.js +1453 -0
- package/dist/core/indexer.d.ts +33 -0
- package/dist/core/indexer.js +282 -0
- package/dist/core/input.d.ts +66 -0
- package/dist/core/input.js +222 -0
- package/dist/core/logger.d.ts +70 -0
- package/dist/core/logger.js +138 -0
- package/dist/core/models.d.ts +45 -0
- package/dist/core/models.js +291 -0
- package/dist/core/net-probe.d.ts +4 -0
- package/dist/core/net-probe.js +20 -0
- package/dist/core/providers/deepinfra.d.ts +23 -0
- package/dist/core/providers/deepinfra.js +44 -0
- package/dist/core/providers/index.d.ts +24 -0
- package/dist/core/providers/index.js +59 -0
- package/dist/core/providers/openrouter.d.ts +28 -0
- package/dist/core/providers/openrouter.js +82 -0
- package/dist/core/providers/spend.d.ts +86 -0
- package/dist/core/providers/spend.js +286 -0
- package/dist/core/providers/types.d.ts +56 -0
- package/dist/core/providers/types.js +39 -0
- package/dist/core/repoos.d.ts +59 -0
- package/dist/core/repoos.js +221 -0
- package/dist/core/runtime.d.ts +35 -0
- package/dist/core/runtime.js +167 -0
- package/dist/core/skills-registry.d.ts +39 -0
- package/dist/core/skills-registry.js +148 -0
- package/dist/core/skills-registry.test.d.ts +1 -0
- package/dist/core/skills-registry.test.js +52 -0
- package/dist/core/sysmem.d.ts +7 -0
- package/dist/core/sysmem.js +55 -0
- package/dist/core/tailscale.d.ts +2 -0
- package/dist/core/tailscale.js +19 -0
- package/dist/core/task.d.ts +53 -0
- package/dist/core/task.js +310 -0
- package/dist/core/toml-line.d.ts +20 -0
- package/dist/core/toml-line.js +52 -0
- package/dist/core/toml-validate.d.ts +44 -0
- package/dist/core/toml-validate.js +335 -0
- package/dist/core/tunnel-assistant.d.ts +30 -0
- package/dist/core/tunnel-assistant.js +65 -0
- package/dist/core/tunnel-registry.d.ts +96 -0
- package/dist/core/tunnel-registry.js +267 -0
- package/dist/core/tunnel-registry.test.d.ts +1 -0
- package/dist/core/tunnel-registry.test.js +309 -0
- package/dist/core/tunnel.d.ts +103 -0
- package/dist/core/tunnel.js +320 -0
- package/dist/core/types.d.ts +811 -0
- package/dist/core/types.js +27 -0
- package/dist/core/worktree-gc.d.ts +57 -0
- package/dist/core/worktree-gc.js +194 -0
- package/dist/server/__tests__/integration-job.test.d.ts +1 -0
- package/dist/server/__tests__/integration-job.test.js +129 -0
- package/dist/server/agents.d.ts +1020 -0
- package/dist/server/agents.js +4070 -0
- package/dist/server/attachments.d.ts +51 -0
- package/dist/server/attachments.js +130 -0
- package/dist/server/auto-engineering.d.ts +45 -0
- package/dist/server/auto-engineering.js +275 -0
- package/dist/server/branchless-release.d.ts +22 -0
- package/dist/server/branchless-release.js +67 -0
- package/dist/server/built-in-agents.d.ts +305 -0
- package/dist/server/built-in-agents.js +1838 -0
- package/dist/server/cto-monitor.d.ts +35 -0
- package/dist/server/cto-monitor.js +281 -0
- package/dist/server/cto.d.ts +125 -0
- package/dist/server/cto.js +590 -0
- package/dist/server/deployments.d.ts +119 -0
- package/dist/server/deployments.js +325 -0
- package/dist/server/diff-snapshot.d.ts +20 -0
- package/dist/server/diff-snapshot.js +73 -0
- package/dist/server/done.d.ts +160 -0
- package/dist/server/done.js +488 -0
- package/dist/server/freeform.d.ts +55 -0
- package/dist/server/freeform.js +179 -0
- package/dist/server/handoff.d.ts +99 -0
- package/dist/server/handoff.js +575 -0
- package/dist/server/hetzner.d.ts +45 -0
- package/dist/server/hetzner.js +117 -0
- package/dist/server/icons.d.ts +8 -0
- package/dist/server/icons.js +195 -0
- package/dist/server/integration-job.d.ts +75 -0
- package/dist/server/integration-job.js +144 -0
- package/dist/server/integration-orchestrator.d.ts +211 -0
- package/dist/server/integration-orchestrator.js +1299 -0
- package/dist/server/integration-status.d.ts +48 -0
- package/dist/server/integration-status.js +71 -0
- package/dist/server/live-index.d.ts +295 -0
- package/dist/server/live-index.js +452 -0
- package/dist/server/model-test.d.ts +20 -0
- package/dist/server/model-test.js +100 -0
- package/dist/server/ntfy.d.ts +57 -0
- package/dist/server/ntfy.js +112 -0
- package/dist/server/pm-attachments.d.ts +35 -0
- package/dist/server/pm-attachments.js +181 -0
- package/dist/server/pm-runs.d.ts +58 -0
- package/dist/server/pm-runs.js +80 -0
- package/dist/server/preview.d.ts +174 -0
- package/dist/server/preview.js +641 -0
- package/dist/server/release.d.ts +88 -0
- package/dist/server/release.js +449 -0
- package/dist/server/reload.d.ts +194 -0
- package/dist/server/reload.js +586 -0
- package/dist/server/remote-validation.d.ts +132 -0
- package/dist/server/remote-validation.js +478 -0
- package/dist/server/repo-lock.d.ts +36 -0
- package/dist/server/repo-lock.js +141 -0
- package/dist/server/review-guard.d.ts +14 -0
- package/dist/server/review-guard.js +159 -0
- package/dist/server/review.d.ts +243 -0
- package/dist/server/review.js +1211 -0
- package/dist/server/routes/agents.d.ts +6 -0
- package/dist/server/routes/agents.js +28 -0
- package/dist/server/routes/auth.d.ts +60 -0
- package/dist/server/routes/auth.js +755 -0
- package/dist/server/routes/config.d.ts +20 -0
- package/dist/server/routes/config.js +347 -0
- package/dist/server/routes/debugger.d.ts +33 -0
- package/dist/server/routes/debugger.js +360 -0
- package/dist/server/routes/deployments.d.ts +3 -0
- package/dist/server/routes/deployments.js +16 -0
- package/dist/server/routes/docs.d.ts +8 -0
- package/dist/server/routes/docs.js +135 -0
- package/dist/server/routes/helpers.d.ts +21 -0
- package/dist/server/routes/helpers.js +171 -0
- package/dist/server/routes/index.d.ts +19 -0
- package/dist/server/routes/index.js +18 -0
- package/dist/server/routes/info.d.ts +17 -0
- package/dist/server/routes/info.js +177 -0
- package/dist/server/routes/inputs.d.ts +7 -0
- package/dist/server/routes/inputs.js +125 -0
- package/dist/server/routes/model-providers.d.ts +27 -0
- package/dist/server/routes/model-providers.js +94 -0
- package/dist/server/routes/models.d.ts +3 -0
- package/dist/server/routes/models.js +36 -0
- package/dist/server/routes/notify.d.ts +2 -0
- package/dist/server/routes/notify.js +17 -0
- package/dist/server/routes/playground.d.ts +29 -0
- package/dist/server/routes/playground.js +81 -0
- package/dist/server/routes/release.d.ts +20 -0
- package/dist/server/routes/release.js +86 -0
- package/dist/server/routes/router.d.ts +12 -0
- package/dist/server/routes/router.js +32 -0
- package/dist/server/routes/skill-registry.d.ts +5 -0
- package/dist/server/routes/skill-registry.js +51 -0
- package/dist/server/routes/tasks.d.ts +58 -0
- package/dist/server/routes/tasks.js +1442 -0
- package/dist/server/routes/transcribe.d.ts +2 -0
- package/dist/server/routes/transcribe.js +124 -0
- package/dist/server/routes/types.d.ts +68 -0
- package/dist/server/routes/types.js +1 -0
- package/dist/server/routes/ui.d.ts +5 -0
- package/dist/server/routes/ui.js +81 -0
- package/dist/server/routes/utils.d.ts +3 -0
- package/dist/server/routes/utils.js +23 -0
- package/dist/server/serve-reaper.d.ts +135 -0
- package/dist/server/serve-reaper.js +491 -0
- package/dist/server/server.d.ts +49 -0
- package/dist/server/server.js +2171 -0
- package/dist/server/supervisor.d.ts +89 -0
- package/dist/server/supervisor.js +231 -0
- package/dist/server/system.d.ts +198 -0
- package/dist/server/system.js +480 -0
- package/dist/server/task-check.d.ts +45 -0
- package/dist/server/task-check.js +71 -0
- package/dist/server/task-transitions.d.ts +51 -0
- package/dist/server/task-transitions.js +78 -0
- package/dist/server/task-watchdog.d.ts +158 -0
- package/dist/server/task-watchdog.js +681 -0
- package/dist/server/test-run.d.ts +28 -0
- package/dist/server/test-run.js +81 -0
- package/dist/server/watcher.d.ts +21 -0
- package/dist/server/watcher.js +194 -0
- package/dist/server/write.d.ts +94 -0
- package/dist/server/write.js +345 -0
- package/dist/ui/assets/AgentsView-CF4wRvHh.css +1 -0
- package/dist/ui/assets/AgentsView-D-myL-kA.js +1 -0
- package/dist/ui/assets/ContextView-D3TC-RSp.css +1 -0
- package/dist/ui/assets/ContextView-DsOqAdyE.js +4 -0
- package/dist/ui/assets/DashboardView-BOhsIJqO.js +1 -0
- package/dist/ui/assets/DashboardView-Ck9o0RP2.css +1 -0
- package/dist/ui/assets/DeploymentsView--iIUVfEH.css +1 -0
- package/dist/ui/assets/DeploymentsView-DXu_FYR2.js +1 -0
- package/dist/ui/assets/InputsView-24HVbVPk.js +1 -0
- package/dist/ui/assets/InputsView-BLb7GHtx.css +1 -0
- package/dist/ui/assets/LoginView-6QlGg6cx.css +1 -0
- package/dist/ui/assets/LoginView-BqupI0Lt.js +1 -0
- package/dist/ui/assets/ReleasesView-BEG1A0YH.css +1 -0
- package/dist/ui/assets/ReleasesView-CAgMuxpe.js +3 -0
- package/dist/ui/assets/SettingsView-CA-NYSMK.css +1 -0
- package/dist/ui/assets/SettingsView-O3iZSnjg.js +3 -0
- package/dist/ui/assets/WorkView-D8Tl6qwy.js +1 -0
- package/dist/ui/assets/WorkView-DMYL2s3W.css +1 -0
- package/dist/ui/assets/abnfDiagram-VCTEODGH-J5lkSnEE.js +1 -0
- package/dist/ui/assets/arc-BHZnmhhM.js +1 -0
- package/dist/ui/assets/architecture-7GRP2DOG-pLdFt-xS.js +1 -0
- package/dist/ui/assets/architectureDiagram-5GKGNRK7-C3k89uKX.js +36 -0
- package/dist/ui/assets/array-BifhSqXX.js +1 -0
- package/dist/ui/assets/blockDiagram-I7D4REHJ-AWNoBzop.js +129 -0
- package/dist/ui/assets/button-lbikqSph.js +1 -0
- package/dist/ui/assets/c4Diagram-7LVT6UL2-DKG1D5gd.js +38 -0
- package/dist/ui/assets/channel-gA4qBv3A.js +1 -0
- package/dist/ui/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/ui/assets/chunk-4HAMMTFA-D1YR8lF_.js +62 -0
- package/dist/ui/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/ui/assets/chunk-75Z2AOVW-5GuBxfsL.js +2 -0
- package/dist/ui/assets/chunk-DU6HZSFF-DfdNNqwa.js +127 -0
- package/dist/ui/assets/chunk-F27PBJKO-BPZzinQ4.js +1 -0
- package/dist/ui/assets/chunk-FOHPRMQF-l2z4K2jD.js +161 -0
- package/dist/ui/assets/chunk-GMAD6QVW-DGWvSKj5.js +72 -0
- package/dist/ui/assets/chunk-GVQU2GXP-D6Aw75wJ.js +1 -0
- package/dist/ui/assets/chunk-IMKFNOWR-B8mMBKCW.js +231 -0
- package/dist/ui/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/ui/assets/chunk-L3NEJ4N5-D5OJuXYE.js +1 -0
- package/dist/ui/assets/chunk-OSK3NFVY-B7r9w2UW.js +10 -0
- package/dist/ui/assets/chunk-P2QGCYS3-C74X4AQX.js +1 -0
- package/dist/ui/assets/chunk-POPQ4Y6H-C5_u1gUS.js +1 -0
- package/dist/ui/assets/chunk-PWAF6VOD-DfoTLtxn.js +1 -0
- package/dist/ui/assets/chunk-SHT3W25Y-D40m2eqz.js +168 -0
- package/dist/ui/assets/chunk-SVP7TREG-euxi0Kap.js +88 -0
- package/dist/ui/assets/chunk-TICWLB2K-fkdxngzN.js +206 -0
- package/dist/ui/assets/chunk-XXDRQBXY-DFBUG-OT.js +1 -0
- package/dist/ui/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/ui/assets/classDiagram-ZZMXUADV-BzW8bq2P.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-BzW8bq2P.js +1 -0
- package/dist/ui/assets/cose-bilkent-JH36ORCC-BTcgMon1.js +1 -0
- package/dist/ui/assets/cynefin-OW5HDTMX-B5m_COsS.js +1 -0
- package/dist/ui/assets/cynefinDiagram-5FMLGOSQ-C-AG6CGe.js +62 -0
- package/dist/ui/assets/cytoscape.esm-FBntH-9t.js +321 -0
- package/dist/ui/assets/dagre-GXQ25YYZ-DDfDnp8L.js +4 -0
- package/dist/ui/assets/dagre-PrKaheQc.js +1 -0
- package/dist/ui/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/ui/assets/diagram-S7CK7UJ4-DjQZAlEY.js +30 -0
- package/dist/ui/assets/diagram-UQ7AKVKN-DAZ9yp1B.js +41 -0
- package/dist/ui/assets/diagram-VSXAHHWV-BWepUKJD.js +3 -0
- package/dist/ui/assets/diagram-VX7I27RA-EcV1J81b.js +24 -0
- package/dist/ui/assets/diagram-Z3DM3KII-Dbz7XU_K.js +24 -0
- package/dist/ui/assets/dist-DTg6UBE_.js +1 -0
- package/dist/ui/assets/ebnfDiagram-PWID7BFC-BUyQw8Mm.js +1 -0
- package/dist/ui/assets/erDiagram-RLTQ6QDP-WVf8ukKt.js +99 -0
- package/dist/ui/assets/eventmodeling-NTZA5JFV-DHqyLOAn.js +1 -0
- package/dist/ui/assets/flowDiagram-HODETNUW-CYk_ze5K.js +1 -0
- package/dist/ui/assets/ganttDiagram-EL5Y4UJY-rB4MZuB-.js +292 -0
- package/dist/ui/assets/gitGraph-4MIJSDKK-BOTWM8dY.js +1 -0
- package/dist/ui/assets/gitGraphDiagram-WWUBYQGX-eYscuhtP.js +106 -0
- package/dist/ui/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/ui/assets/index-BACBfnga.css +1 -0
- package/dist/ui/assets/index-BOQ-ust6.js +26 -0
- package/dist/ui/assets/info-A6RAGUB7-_67wOORe.js +1 -0
- package/dist/ui/assets/infoDiagram-27XIBGKW-XVXbddTA.js +2 -0
- package/dist/ui/assets/init-C-OQMol4.js +1 -0
- package/dist/ui/assets/ishikawaDiagram-5VMMS53U-ClLNiEru.js +70 -0
- package/dist/ui/assets/journeyDiagram-3NMN7TZE-NRawjR6i.js +139 -0
- package/dist/ui/assets/kanban-definition-UXKFOSKX-BgSzBfIx.js +89 -0
- package/dist/ui/assets/katex-DolUETbr.js +257 -0
- package/dist/ui/assets/line-KAyfuul4.js +1 -0
- package/dist/ui/assets/linear-cCCPOpu7.js +1 -0
- package/dist/ui/assets/mermaid-parser.core-CgXZk1iN.js +7 -0
- package/dist/ui/assets/mermaid.core-BUUkChHW.js +44 -0
- package/dist/ui/assets/mindmap-definition-YA3MSWOX-Cpn7vW4r.js +96 -0
- package/dist/ui/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/ui/assets/packet-AYTQ26CC-0lZwM-d6.js +1 -0
- package/dist/ui/assets/path-COt_16Va.js +1 -0
- package/dist/ui/assets/pegDiagram-XKGWAZYB-impy0inn.js +1 -0
- package/dist/ui/assets/pie-WAS4IAKB-CqfdGHYg.js +1 -0
- package/dist/ui/assets/pieDiagram-E7YTZNPT-CxjL4I1h.js +39 -0
- package/dist/ui/assets/quadrantDiagram-AXDQQJYC-Cjd1QMIe.js +7 -0
- package/dist/ui/assets/radar-RG4KPBEZ-CRkQIwiv.js +1 -0
- package/dist/ui/assets/railroad-74A4TZTK-i67XSWTB.js +1 -0
- package/dist/ui/assets/railroad-abnf-HS5TGJTU-Cv9m5aS3.js +1 -0
- package/dist/ui/assets/railroad-ebnf-LZEXJU2U-C5ZIn-Jh.js +1 -0
- package/dist/ui/assets/railroad-peg-WCYAUIDC-CGra9Oq-.js +1 -0
- package/dist/ui/assets/railroadDiagram-O6MQD6OU-_pK0qyaY.js +1 -0
- package/dist/ui/assets/releases-B0WNV-SY.js +1 -0
- package/dist/ui/assets/repoos-cto-square.webp +0 -0
- package/dist/ui/assets/repoos-helpful-pm-square.webp +0 -0
- package/dist/ui/assets/repoos-orchestrator-square.webp +0 -0
- package/dist/ui/assets/repoos-performance-agent-square.webp +0 -0
- package/dist/ui/assets/repoos-ross-from-friends-square.webp +0 -0
- package/dist/ui/assets/repoos-tech-debt-agent-square.webp +0 -0
- package/dist/ui/assets/requirementDiagram-BXWQKSXE-CRrFX-yw.js +84 -0
- package/dist/ui/assets/rough.esm-By172zw-.js +1 -0
- package/dist/ui/assets/sankeyDiagram-P5KCCOFB-X_xFRj9E.js +40 -0
- package/dist/ui/assets/sequenceDiagram-WJ2MYXX4-Byb49lKf.js +162 -0
- package/dist/ui/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/dist/ui/assets/src-BH-TyZbA.js +1 -0
- package/dist/ui/assets/stateDiagram-D77RDMKH-CzaPrqYF.js +1 -0
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-BCXckz7K.js +1 -0
- package/dist/ui/assets/swimlanes-42K2YHIH-Cx_1lnqr.js +1 -0
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-54qsKi6A.js +8 -0
- package/dist/ui/assets/timeline-definition-24CTP7MA-B1IbZPQ-.js +120 -0
- package/dist/ui/assets/treeView-Q6P3EWNA-DFWhVE3d.js +1 -0
- package/dist/ui/assets/treemap-WGGIJYW6-BmbYPThn.js +1 -0
- package/dist/ui/assets/vendor-ui-DPm7zLxi.js +3 -0
- package/dist/ui/assets/vendor-vue-CobgyEJp.js +1 -0
- package/dist/ui/assets/vennDiagram-4TSXK5OY-Ae2AtRpt.js +34 -0
- package/dist/ui/assets/wardley-WFR3VGLG-BtD4X4tL.js +1 -0
- package/dist/ui/assets/wardleyDiagram-VM6X3IG4-B3YN2fTd.js +78 -0
- package/dist/ui/assets/xychartDiagram-S5SC5T6Z-CIzJfMKA.js +7 -0
- package/dist/ui/favicon.svg +11 -0
- package/dist/ui/index.html +49 -0
- package/dist/ui/sw.js +48 -0
- package/package.json +91 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository-level locks for mutual exclusion on the main checkout.
|
|
3
|
+
*
|
|
4
|
+
* The close-out lock serializes publication to main, and the root lock
|
|
5
|
+
* additionally prevents a hotfix from running while a close-out holds the
|
|
6
|
+
* main checkout. Both locks share a single file to keep acquisition atomic.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, writeFileSync, unlinkSync, mkdirSync, statSync, readFileSync } from "node:fs";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
const LOCK_FILE = ".repoos/close-out.lock";
|
|
11
|
+
const ROOT_LOCK_FILE = ".repoos/root.lock";
|
|
12
|
+
export function createRepositoryLock(root) {
|
|
13
|
+
return {
|
|
14
|
+
acquire(taskId) {
|
|
15
|
+
const lockPath = join(root, LOCK_FILE);
|
|
16
|
+
if (existsSync(lockPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const stat = statSync(lockPath);
|
|
19
|
+
const age = Date.now() - stat.mtime.getTime();
|
|
20
|
+
if (age > 60_000) {
|
|
21
|
+
unlinkSync(lockPath);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
33
|
+
writeFileSync(lockPath, JSON.stringify({ taskId, acquiredAt: new Date().toISOString() }));
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
release(taskId) {
|
|
41
|
+
const lockPath = join(root, LOCK_FILE);
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(lockPath)) {
|
|
44
|
+
const content = readFileSync(lockPath, "utf8");
|
|
45
|
+
const lock = JSON.parse(content);
|
|
46
|
+
if (lock.taskId === taskId) {
|
|
47
|
+
unlinkSync(lockPath);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
/* ignore */
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
},
|
|
57
|
+
isLocked() {
|
|
58
|
+
const lockPath = join(root, LOCK_FILE);
|
|
59
|
+
return existsSync(lockPath);
|
|
60
|
+
},
|
|
61
|
+
getHolder() {
|
|
62
|
+
const lockPath = join(root, LOCK_FILE);
|
|
63
|
+
try {
|
|
64
|
+
if (existsSync(lockPath)) {
|
|
65
|
+
const content = readFileSync(lockPath, "utf8");
|
|
66
|
+
const lock = JSON.parse(content);
|
|
67
|
+
return lock.taskId || null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
/* ignore */
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function createRootLock(root) {
|
|
78
|
+
const lockPath = join(root, ROOT_LOCK_FILE);
|
|
79
|
+
return {
|
|
80
|
+
acquire(taskId, kind) {
|
|
81
|
+
if (existsSync(lockPath)) {
|
|
82
|
+
try {
|
|
83
|
+
const stat = statSync(lockPath);
|
|
84
|
+
const age = Date.now() - stat.mtime.getTime();
|
|
85
|
+
// Hotfix holds last indefinitely (until finalization), so use a
|
|
86
|
+
// longer stale window (10 min) to avoid crashing a live hotfix.
|
|
87
|
+
if (age > 600_000) {
|
|
88
|
+
unlinkSync(lockPath);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
mkdirSync(dirname(lockPath), { recursive: true });
|
|
100
|
+
writeFileSync(lockPath, JSON.stringify({ taskId, kind, acquiredAt: new Date().toISOString() }));
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
release(taskId) {
|
|
108
|
+
try {
|
|
109
|
+
if (existsSync(lockPath)) {
|
|
110
|
+
const content = readFileSync(lockPath, "utf8");
|
|
111
|
+
const lock = JSON.parse(content);
|
|
112
|
+
if (lock.taskId === taskId) {
|
|
113
|
+
unlinkSync(lockPath);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
/* ignore */
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
},
|
|
123
|
+
isLocked() {
|
|
124
|
+
return existsSync(lockPath);
|
|
125
|
+
},
|
|
126
|
+
getHolder() {
|
|
127
|
+
try {
|
|
128
|
+
if (existsSync(lockPath)) {
|
|
129
|
+
const content = readFileSync(lockPath, "utf8");
|
|
130
|
+
const lock = JSON.parse(content);
|
|
131
|
+
const kind = lock.kind === "hotfix" || lock.kind === "close-out" ? lock.kind : "close-out";
|
|
132
|
+
return { taskId: lock.taskId, kind };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
/* ignore */
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RepoOSConfig, Task } from "../core/types.js";
|
|
2
|
+
export interface ReviewGuardResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
/** Human-readable failure reason when not ok. */
|
|
5
|
+
detail?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Stage and commit the worktree's implementation changes (excluding generated
|
|
9
|
+
* artifacts and the task's own file), and reject a vacuous review transition.
|
|
10
|
+
* Mutates the worktree's git state: on success the branch carries the agent's
|
|
11
|
+
* work. Returns `ok: false` with a human-readable reason when the branch
|
|
12
|
+
* cannot be prepared or there is no source change without `no_source_change`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function guardReviewTransition(config: RepoOSConfig, task: Task): Promise<ReviewGuardResult>;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The commit+validate gate that every transition into `review` must pass.
|
|
3
|
+
*
|
|
4
|
+
* Originally handoff-finalization carried this logic privately; #0170 added
|
|
5
|
+
* the vacuous-handoff rejection and #0210 made it the single boundary for ALL
|
|
6
|
+
* paths into `review` — the trusted handoff signal, `PATCH /api/tasks/:id`,
|
|
7
|
+
* and a direct task-file edit picked up by the file watcher. This module is
|
|
8
|
+
* that shared gate.
|
|
9
|
+
*
|
|
10
|
+
* It enforces two guarantees before a task may land in `review`:
|
|
11
|
+
* 1. No uncommitted implementation changes are left behind — either they
|
|
12
|
+
* are committed (excluding `dist`/`screenshots`/the task file itself) or
|
|
13
|
+
* the transition is rejected.
|
|
14
|
+
* 2. A transition with zero source changes since the branch diverged from
|
|
15
|
+
* main is rejected unless the task is marked `no_source_change: true`.
|
|
16
|
+
*
|
|
17
|
+
* The function is idempotent: calling it again after a successful commit (e.g.
|
|
18
|
+
* a PATCH route runs it, then the index's file-watch hook runs it again for
|
|
19
|
+
* the same transition) finds the work already committed and passes without
|
|
20
|
+
* re-committing or spuriously rejecting. Vacuity is therefore judged against
|
|
21
|
+
* the branch's divergence point (merge-base with main), not the staged diff.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
24
|
+
import { resolve } from "node:path";
|
|
25
|
+
import { runGit, worktreePathForBranch, branchChangesSinceBase, currentBranch, agentTouchedFiles, } from "../core/git.js";
|
|
26
|
+
function samePath(a, b) {
|
|
27
|
+
try {
|
|
28
|
+
return realpathSync(a) === realpathSync(b);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return resolve(a) === resolve(b);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** True for generated/task-file paths that never count as "source changes". */
|
|
35
|
+
function isGeneratedOrTask(path, taskPath) {
|
|
36
|
+
return (path === taskPath ||
|
|
37
|
+
path.startsWith("dist/") ||
|
|
38
|
+
path === "dist" ||
|
|
39
|
+
path.startsWith("screenshots/") ||
|
|
40
|
+
path === "screenshots");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Stage and commit the worktree's implementation changes (excluding generated
|
|
44
|
+
* artifacts and the task's own file), and reject a vacuous review transition.
|
|
45
|
+
* Mutates the worktree's git state: on success the branch carries the agent's
|
|
46
|
+
* work. Returns `ok: false` with a human-readable reason when the branch
|
|
47
|
+
* cannot be prepared or there is no source change without `no_source_change`.
|
|
48
|
+
*/
|
|
49
|
+
export async function guardReviewTransition(config, task) {
|
|
50
|
+
if (!task.branch) {
|
|
51
|
+
return { ok: false, detail: `task #${task.id} has no branch to finalize from` };
|
|
52
|
+
}
|
|
53
|
+
const registered = worktreePathForBranch(config.root, task.branch);
|
|
54
|
+
const isHotfix = task.hotfix === true;
|
|
55
|
+
if (!registered || (!isHotfix && samePath(registered, config.root)) || !existsSync(registered)) {
|
|
56
|
+
return {
|
|
57
|
+
ok: false,
|
|
58
|
+
detail: `task #${task.id} has no registered worktree for branch ${task.branch}`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const clearIndex = await runGit(registered,
|
|
62
|
+
// The guard owns the index for this commit. Clear any stale staging (for
|
|
63
|
+
// example a prior accidental `git add -f node_modules`) before selectively
|
|
64
|
+
// staging implementation changes below. This leaves working-tree edits
|
|
65
|
+
// untouched.
|
|
66
|
+
["reset", "--quiet", "HEAD", "--", "."], 10_000);
|
|
67
|
+
if (clearIndex.status !== 0) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
detail: `could not clear the staging index: ${concise(clearIndex)}`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (isHotfix) {
|
|
74
|
+
const touched = agentTouchedFiles(registered, "HEAD");
|
|
75
|
+
const sourceFiles = touched.filter((p) => !isGeneratedOrTask(p, task.path));
|
|
76
|
+
if (sourceFiles.length > 0) {
|
|
77
|
+
const add = await runGit(registered, ["add", "--", ...sourceFiles], 30_000);
|
|
78
|
+
if (add.status !== 0)
|
|
79
|
+
return { ok: false, detail: `git add failed: ${concise(add)}` };
|
|
80
|
+
}
|
|
81
|
+
const staged = await runGit(registered, ["diff", "--cached", "--quiet"], 10_000);
|
|
82
|
+
if (staged.status === 1) {
|
|
83
|
+
const commit = await runGit(registered, ["commit", "-m", `hotfix(${task.id}): ${task.title}`], 30_000);
|
|
84
|
+
if (commit.status !== 0)
|
|
85
|
+
return { ok: false, detail: `git commit failed: ${concise(commit)}` };
|
|
86
|
+
}
|
|
87
|
+
else if (staged.status !== 0) {
|
|
88
|
+
return { ok: false, detail: `could not inspect staged changes: ${concise(staged)}` };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true };
|
|
91
|
+
}
|
|
92
|
+
const add = await runGit(registered,
|
|
93
|
+
// `git add` honors .gitignore. Do not name ignored paths as exclusions:
|
|
94
|
+
// Git rejects those pathspecs even though they are exclusions.
|
|
95
|
+
["add", "-A", "--", "."], 30_000);
|
|
96
|
+
if (add.status !== 0)
|
|
97
|
+
return { ok: false, detail: `git add failed: ${concise(add)}` };
|
|
98
|
+
// Remove generated and task metadata paths after staging. Unlike pathspec
|
|
99
|
+
// exclusions, `git reset` is safe whether these paths are ignored, tracked,
|
|
100
|
+
// or absent, and it preserves their working-tree contents.
|
|
101
|
+
const unstageGenerated = await runGit(registered, ["reset", "--quiet", "HEAD", "--", "dist", "screenshots", task.path], 10_000);
|
|
102
|
+
if (unstageGenerated.status !== 0) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
detail: `could not unstage generated artifacts: ${concise(unstageGenerated)}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Drop any OTHER task's work/*.md from this commit. A concurrent board write,
|
|
109
|
+
// a `repoos` CLI call, or a stale merge can leave a sibling task file dirty in
|
|
110
|
+
// the worktree, and the `git add -A` above folds it into this task's
|
|
111
|
+
// "implement" commit — which then publishes to main on close-out (observed:
|
|
112
|
+
// #0319 landed #0202/#0275 frontmatter drift). Only this task's own file
|
|
113
|
+
// (already unstaged above) is the handoff's to carry.
|
|
114
|
+
const stagedWorkFiles = (await runGit(registered, ["diff", "--cached", "--name-only", "--", config.workDir], 10_000)).stdout
|
|
115
|
+
.split("\n")
|
|
116
|
+
.map((s) => s.trim())
|
|
117
|
+
.filter((p) => p && p.endsWith(".md") && p !== task.path);
|
|
118
|
+
if (stagedWorkFiles.length > 0) {
|
|
119
|
+
const unstageForeign = await runGit(registered, ["reset", "--quiet", "HEAD", "--", ...stagedWorkFiles], 10_000);
|
|
120
|
+
if (unstageForeign.status !== 0) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
detail: `could not unstage unrelated task files: ${concise(unstageForeign)}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const staged = await runGit(registered, ["diff", "--cached", "--quiet"], 10_000);
|
|
128
|
+
if (staged.status === 1) {
|
|
129
|
+
const commit = await runGit(registered, ["commit", "-m", `feat(${task.id}): implement ${task.title}`], 30_000);
|
|
130
|
+
if (commit.status !== 0)
|
|
131
|
+
return { ok: false, detail: `git commit failed: ${concise(commit)}` };
|
|
132
|
+
}
|
|
133
|
+
else if (staged.status !== 0) {
|
|
134
|
+
return { ok: false, detail: `could not inspect staged changes: ${concise(staged)}` };
|
|
135
|
+
}
|
|
136
|
+
// Judge vacuity against the branch's divergence point, so the guard is
|
|
137
|
+
// idempotent: work already committed (by a previous guard run, the handoff
|
|
138
|
+
// path, or the agent) keeps the transition valid. Only non-generated, non
|
|
139
|
+
// -task paths count as evidence of implementation.
|
|
140
|
+
const baseBranch = currentBranch(config.root) ?? "main";
|
|
141
|
+
const changes = branchChangesSinceBase(registered, baseBranch);
|
|
142
|
+
const realChange = changes.paths.some((p) => !isGeneratedOrTask(p, task.path));
|
|
143
|
+
if (!realChange && !task.noSourceChange) {
|
|
144
|
+
return {
|
|
145
|
+
ok: false,
|
|
146
|
+
detail: "no implementation found since the branch diverged; use no_source_change: true to force a no-op handoff",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return { ok: true };
|
|
150
|
+
}
|
|
151
|
+
function concise(run) {
|
|
152
|
+
return ([run.stdout, run.stderr]
|
|
153
|
+
.join("\n")
|
|
154
|
+
.split("\n")
|
|
155
|
+
.map((line) => line.trim())
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
.slice(-8)
|
|
158
|
+
.join(" · ") || `exit ${run.status}`);
|
|
159
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { Agent, AgentOutputEntry, RepoOSConfig, Task } from "../core/types.js";
|
|
2
|
+
import type { LiveIndex, RepoEvent } from "./live-index.js";
|
|
3
|
+
import { type AgentRunner } from "./agents.js";
|
|
4
|
+
/** A stored agent review, as served to the UI. */
|
|
5
|
+
export interface ReviewReport {
|
|
6
|
+
/** Task id this report belongs to. */
|
|
7
|
+
id: string;
|
|
8
|
+
/** ISO timestamp the report was written. */
|
|
9
|
+
at: string;
|
|
10
|
+
/** Agent role name that produced it (normally "reviewer"). */
|
|
11
|
+
agent: string;
|
|
12
|
+
/** Coding-agent CLI and model behind that role. */
|
|
13
|
+
cli: string;
|
|
14
|
+
model: string;
|
|
15
|
+
/** Branch that was reviewed. */
|
|
16
|
+
branch: string;
|
|
17
|
+
/** "ok" when the agent reported; "failed" when the run itself failed. */
|
|
18
|
+
state: "ok" | "failed";
|
|
19
|
+
/** The report markdown (or the failure detail when state is "failed"). */
|
|
20
|
+
markdown: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ReviewRunResult {
|
|
23
|
+
ok: boolean;
|
|
24
|
+
/** True when no review was attempted at all (agent disabled, no worktree). */
|
|
25
|
+
skipped?: boolean;
|
|
26
|
+
reason?: string;
|
|
27
|
+
report?: ReviewReport;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Prefix on the SSE event id used for a task's reviewer conversation. The
|
|
31
|
+
* engineer session streams under the plain task id; reviewer output streams
|
|
32
|
+
* under `review:<taskId>` so the two conversations can never mix on the wire
|
|
33
|
+
* or in the UI (0110).
|
|
34
|
+
*/
|
|
35
|
+
export declare const REVIEW_SESSION_ID_PREFIX = "review:";
|
|
36
|
+
/**
|
|
37
|
+
* The mission handed to the review agent. Deliberately narrow: what to look
|
|
38
|
+
* at, what to write, and a hard boundary on what it may not touch.
|
|
39
|
+
*/
|
|
40
|
+
export declare function reviewMission(task: Task, agent: Agent, workdir: string, baseBranch: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* The mission handed to the review agent for a follow-up chat turn. The human
|
|
43
|
+
* asked a question about the review; the reviewer answers in the same read-only
|
|
44
|
+
* style, grounded in the task spec, its own previous report (when one exists),
|
|
45
|
+
* and the actual code. It still never touches the repo beyond reading.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reviewFollowupMission(task: Task, agent: Agent, workdir: string, baseBranch: string, text: string, previousReport: ReviewReport | null): string;
|
|
48
|
+
export declare class ReviewManager {
|
|
49
|
+
private readonly config;
|
|
50
|
+
private readonly emit;
|
|
51
|
+
private readonly runs;
|
|
52
|
+
/** Tasks the status guard just put back in `review`, by timestamp. */
|
|
53
|
+
private readonly reverted;
|
|
54
|
+
/** AgentRunner to send auto-bounce messages to the engineer session. */
|
|
55
|
+
private readonly runner?;
|
|
56
|
+
/**
|
|
57
|
+
* Reflects a trusted write straight into the index (see server.ts's other
|
|
58
|
+
* `applyFileChange(path, { guarded: true })` call sites). Without this the
|
|
59
|
+
* revert below is only ever seen by the async file watcher, which under
|
|
60
|
+
* load can fire the transition-into-review path twice for one write —
|
|
61
|
+
* `claimRevert` is single-use, so the second, spurious firing starts a
|
|
62
|
+
* second review the guard was supposed to prevent.
|
|
63
|
+
*/
|
|
64
|
+
private readonly index?;
|
|
65
|
+
/**
|
|
66
|
+
* Own logger rather than a constructor param (0187 review): the only call
|
|
67
|
+
* site is `startServer`, and every review event is a task-lifecycle event —
|
|
68
|
+
* it belongs on the same per-task log stream `logger.task()` already
|
|
69
|
+
* writes to elsewhere, keyed by the same `config.root`.
|
|
70
|
+
*/
|
|
71
|
+
private readonly logger;
|
|
72
|
+
/** Database for recording review sessions. */
|
|
73
|
+
private readonly db;
|
|
74
|
+
constructor(config: RepoOSConfig, emit: (e: RepoEvent) => void, runner?: AgentRunner, index?: LiveIndex);
|
|
75
|
+
/** Whether an enabled review agent exists — the Agents page toggle. */
|
|
76
|
+
enabled(): boolean;
|
|
77
|
+
isRunning(taskId: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* How many reviews are in flight (the server's idle check).
|
|
80
|
+
*
|
|
81
|
+
* `this.runs` is the single source of truth for running reviews: fresh turns
|
|
82
|
+
* register in `run()`/`send()`, and adopted turns (re-attached after a reload)
|
|
83
|
+
* are registered by `armAdoptedTimeouts()` at boot. So counting `this.runs`
|
|
84
|
+
* already covers every in-flight review, including adopted ones — adding a
|
|
85
|
+
* separate count of `runner.running()` review sessions here would DOUBLE-count
|
|
86
|
+
* each adopted review (0288 review round 2).
|
|
87
|
+
*/
|
|
88
|
+
runningCount(): number;
|
|
89
|
+
/**
|
|
90
|
+
* Claim the "this task is only back in `review` because the guard put it
|
|
91
|
+
* there" marker, if it is fresh. The revert lands on disk as an ordinary
|
|
92
|
+
* status change, so without this the trigger would start another review,
|
|
93
|
+
* whose agent would move it to done again, forever.
|
|
94
|
+
*/
|
|
95
|
+
claimRevert(taskId: string): boolean;
|
|
96
|
+
/** Where a task's report lives. Derived cache — safe to delete. */
|
|
97
|
+
path(taskId: string): string;
|
|
98
|
+
/** The stored report for a task, or null when none was ever written. */
|
|
99
|
+
read(taskId: string): ReviewReport | null;
|
|
100
|
+
/**
|
|
101
|
+
* The synchronous gate both `run` and `send` share: is a review possible for
|
|
102
|
+
* this task right now? Resolved before firing the async spawn so an HTTP
|
|
103
|
+
* handler can answer "why not" without waiting for a prompt that will never
|
|
104
|
+
* start.
|
|
105
|
+
*/
|
|
106
|
+
canRun(task: Task): {
|
|
107
|
+
ok: boolean;
|
|
108
|
+
reason?: string;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Same gate for a follow-up chat turn, which additionally needs a prior
|
|
112
|
+
* conversation (a review must have run at least once).
|
|
113
|
+
*/
|
|
114
|
+
canSend(task: Task): {
|
|
115
|
+
ok: boolean;
|
|
116
|
+
reason?: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Run the review agent against a task sitting in `review`.
|
|
120
|
+
*
|
|
121
|
+
* Skips silently (no event, no file) when the review agent is disabled or
|
|
122
|
+
* the task has no worktree to inspect — "disabled means nothing happens" is
|
|
123
|
+
* the contract, not "something happens quietly and fails".
|
|
124
|
+
*
|
|
125
|
+
* A fresh run every time: the reviewer conversation is reset, so the run is
|
|
126
|
+
* a new assessment (the initial landing review, or a deliberate "Review
|
|
127
|
+
* again"), never a continuation of the prior one (0110).
|
|
128
|
+
*/
|
|
129
|
+
run(task: Task): Promise<ReviewRunResult>;
|
|
130
|
+
/**
|
|
131
|
+
* Spawn (or re-start) a durable review turn in the AgentRunner and arm its
|
|
132
|
+
* hard timeout. Completion is event-driven: `AgentRunner` fires `onReviewDone`
|
|
133
|
+
* (0288), which lands in `handleReviewDone` — so a review that is in flight
|
|
134
|
+
* across a server reload is finalized by the NEW process's `ReviewManager`
|
|
135
|
+
* rather than dying with a `runPrompt` continuation.
|
|
136
|
+
*/
|
|
137
|
+
private spawnDurable;
|
|
138
|
+
/**
|
|
139
|
+
* Re-arm the hard review timeout for durable review sessions re-attached
|
|
140
|
+
* from the previous server (0288). The spawning process's timer died with the
|
|
141
|
+
* reload, so without this an adopted review could hang forever. Also registers
|
|
142
|
+
* each adopted turn as a `Run` so cancellation and the run/chat mode keep
|
|
143
|
+
* working across the handoff. Called once at boot, after the runner has
|
|
144
|
+
* adopted still-alive review children.
|
|
145
|
+
*/
|
|
146
|
+
armAdoptedTimeouts(): void;
|
|
147
|
+
/**
|
|
148
|
+
* Re-spawn reviews that a real (non-reload) server shutdown killed. On
|
|
149
|
+
* shutdown `handle.close()` runs `reviews.cancelAll()`, which SIGKILLs every
|
|
150
|
+
* in-flight review agent (unlike the engineer, which is left running and
|
|
151
|
+
* re-adopted by PID). The next boot's `adoptRunningAgents()` then finds
|
|
152
|
+
* nothing to re-attach, so the task sits in `review` forever with a stale or
|
|
153
|
+
* missing report. This closes that gap — the review equivalent of
|
|
154
|
+
* `recoverPendingHandoffs()`.
|
|
155
|
+
*
|
|
156
|
+
* A task is re-reviewed when it is in `review`, has a reviewer enabled and a
|
|
157
|
+
* worktree, is not already running/adopted, was not deliberately cancelled,
|
|
158
|
+
* AND its stored report is missing or older than the branch's HEAD commit
|
|
159
|
+
* (i.e. the engineer pushed a fix the report never saw). Runs are staggered
|
|
160
|
+
* so a batch doesn't spawn every reviewer at once.
|
|
161
|
+
*
|
|
162
|
+
* Must be called AFTER the index has populated — it reads `index.getTasks()`.
|
|
163
|
+
*/
|
|
164
|
+
recoverInterruptedReviews(tasks: Task[]): void;
|
|
165
|
+
/** Durable review-turn completion hook (0288). */
|
|
166
|
+
handleReviewDone(sessionKey: string, exitedCleanly?: boolean, reviewKind?: "run" | "chat"): void;
|
|
167
|
+
/** Finalize a fresh review run from its durable session transcript. */
|
|
168
|
+
private finalizeRun;
|
|
169
|
+
/**
|
|
170
|
+
* Increment the `review_passes` frontmatter counter. Unlike `review_rounds`
|
|
171
|
+
* (which counts only auto-bounced rounds and caps at MAX_AUTO_REVIEW_ROUNDS),
|
|
172
|
+
* this counts every completed review pass — auto and manual ("Review again")
|
|
173
|
+
* alike — so the UI badge (`D# · R#`) matches the true number of review
|
|
174
|
+
* sessions.
|
|
175
|
+
*
|
|
176
|
+
* The write also bumps `updated_at` (a field the live index's `diff()`
|
|
177
|
+
* watches, since it omits `extra`) and commits the file, so the drawer's
|
|
178
|
+
* badge refreshes live and `main` stays clean/mergeable. Best-effort, never
|
|
179
|
+
* fails.
|
|
180
|
+
*/
|
|
181
|
+
private bumpReviewPasses;
|
|
182
|
+
/** Record a review session to the database. Best-effort, never fails. */
|
|
183
|
+
private recordReviewSession;
|
|
184
|
+
/**
|
|
185
|
+
* Send a follow-up chat turn to the reviewer. The human's message joins the
|
|
186
|
+
* reviewer conversation (never the engineer session), and the reviewer's
|
|
187
|
+
* answer streams into the same conversation. Chat turns do not overwrite the
|
|
188
|
+
* stored report — the verdict callout keeps describing the last full review.
|
|
189
|
+
*/
|
|
190
|
+
send(task: Task, text: string): Promise<ReviewRunResult>;
|
|
191
|
+
/** Finalize a follow-up chat turn from its durable session transcript. */
|
|
192
|
+
private finalizeChat;
|
|
193
|
+
/** Record a review chat turn to the database. Best-effort, never fails. */
|
|
194
|
+
private recordReviewChatTurn;
|
|
195
|
+
/** The retained reviewer conversation for a task, or [] when none exists. */
|
|
196
|
+
session(taskId: string): AgentOutputEntry[];
|
|
197
|
+
/**
|
|
198
|
+
* Abandon a task's review: kill the child if one is running and mark the run
|
|
199
|
+
* cancelled so it writes nothing. Called when the task leaves `review` by a
|
|
200
|
+
* human route (the close-out, a status change from the API), where both the
|
|
201
|
+
* report and the status guard have stopped being relevant — and where the
|
|
202
|
+
* worktree is about to be removed out from under the child.
|
|
203
|
+
*/
|
|
204
|
+
cancel(taskId: string): void;
|
|
205
|
+
/** Cancel every in-flight review (server shutdown). */
|
|
206
|
+
cancelAll(): void;
|
|
207
|
+
private cancelledFilePath;
|
|
208
|
+
private readCancelled;
|
|
209
|
+
private writeCancelled;
|
|
210
|
+
private isCancelled;
|
|
211
|
+
private markCancelled;
|
|
212
|
+
private clearCancelled;
|
|
213
|
+
/**
|
|
214
|
+
* Auto-bounce logic: if the verdict is "needs some work" or "back to the
|
|
215
|
+
* drawing board" and we haven't exceeded the max rounds, send the findings
|
|
216
|
+
* back to the engineer session and increment the review_rounds counter.
|
|
217
|
+
*/
|
|
218
|
+
private autoBounce;
|
|
219
|
+
/** The runner session key (== SSE event id) for a task's reviewer chat. */
|
|
220
|
+
private reviewKey;
|
|
221
|
+
/** Whether a review conversation exists for the task (memory or persisted). */
|
|
222
|
+
private hasSession;
|
|
223
|
+
/** Drop the task's durable conversation, in memory and on disk. */
|
|
224
|
+
private resetReviewSession;
|
|
225
|
+
/**
|
|
226
|
+
* Emit the human's follow-up text under the review session id. The durable
|
|
227
|
+
* transcript records it too (via the runner session's `humanEntry`), and the
|
|
228
|
+
* live SSE stream is emitted here because the runner does not surface a
|
|
229
|
+
* seeded human entry on spawn.
|
|
230
|
+
*/
|
|
231
|
+
private emitHumanEntry;
|
|
232
|
+
/** Append a trusted system marker into the durable review transcript. */
|
|
233
|
+
private appendMarker;
|
|
234
|
+
/** Persist a report, creating `<cacheDir>/reviews/` on first use. */
|
|
235
|
+
private write;
|
|
236
|
+
/**
|
|
237
|
+
* Defense in depth for the one thing the review agent must never do. If the
|
|
238
|
+
* task file says `done` when the review ends, the reviewer moved it there:
|
|
239
|
+
* every human route out of `review` cancels the run first, so a live run can
|
|
240
|
+
* only be looking at a task a human has not touched. Put it back and say so.
|
|
241
|
+
*/
|
|
242
|
+
private enforceStillInReview;
|
|
243
|
+
}
|