@repo-os/repoos 0.5.76 → 0.5.78
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/.build-info.json +2 -2
- package/dist/.build-stamp.json +1 -1
- package/dist/cli/api-route-parity.js +3 -0
- package/dist/cli/help.js +10 -4
- package/dist/cli/index.js +7 -1
- package/dist/commands/control-api.d.ts +65 -2
- package/dist/commands/control-api.js +201 -50
- package/dist/commands/message-input.test.d.ts +1 -0
- package/dist/commands/message-input.test.js +61 -0
- package/dist/core/active-task-card.d.ts +5 -0
- package/dist/core/active-task-card.js +9 -0
- package/dist/core/agent-compatibility.json +22 -0
- package/dist/core/agent-contract.d.ts +61 -0
- package/dist/core/agent-contract.js +144 -2
- package/dist/core/agents-md-size.test.d.ts +1 -0
- package/dist/core/agents-md-size.test.js +13 -0
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +15 -0
- package/dist/core/db.d.ts +5 -1
- package/dist/core/db.js +7 -3
- package/dist/core/detect.js +14 -0
- package/dist/core/doctor.js +4 -2
- package/dist/core/frontmatter.js +7 -1
- package/dist/core/git-maintenance-lsof.test.d.ts +1 -0
- package/dist/core/git-maintenance-lsof.test.js +32 -0
- package/dist/core/git-maintenance.d.ts +2 -0
- package/dist/core/git-maintenance.js +10 -5
- package/dist/core/indexer.js +7 -8
- package/dist/core/model-routing/facts.js +1 -0
- package/dist/core/model-routing/harness.js +4 -0
- package/dist/core/models.d.ts +7 -0
- package/dist/core/models.js +17 -0
- package/dist/core/providers/spend.d.ts +36 -5
- package/dist/core/providers/spend.js +130 -4
- package/dist/core/repo-log.js +18 -8
- package/dist/core/shot-fixtures.d.ts +1 -1
- package/dist/core/shot-fixtures.js +11 -0
- package/dist/core/shot-targets.d.ts +1 -1
- package/dist/core/shot-targets.js +1 -0
- package/dist/core/task-body-normalize.test.js +10 -0
- package/dist/core/task-dependencies.js +22 -8
- package/dist/core/task-dependencies.test.js +46 -0
- package/dist/core/task-summary.d.ts +149 -0
- package/dist/core/task-summary.js +269 -0
- package/dist/core/task-summary.test.d.ts +1 -0
- package/dist/core/task-summary.test.js +247 -0
- package/dist/core/task.d.ts +6 -0
- package/dist/core/task.js +14 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/server/agents.d.ts +117 -2
- package/dist/server/agents.js +655 -7
- package/dist/server/handoff-status.d.ts +16 -0
- package/dist/server/handoff-status.js +14 -0
- package/dist/server/handoff.d.ts +2 -0
- package/dist/server/handoff.js +38 -2
- package/dist/server/integration-orchestrator.d.ts +1 -0
- package/dist/server/integration-orchestrator.js +56 -40
- package/dist/server/live-index.js +13 -6
- package/dist/server/model-test.js +1 -0
- package/dist/server/remote-validation.d.ts +96 -10
- package/dist/server/remote-validation.js +463 -55
- package/dist/server/routes/agents.js +1 -1
- package/dist/server/routes/model-providers.js +21 -7
- package/dist/server/routes/tasks.d.ts +12 -0
- package/dist/server/routes/tasks.js +60 -0
- package/dist/server/server.js +3 -1
- package/dist/server/task-summary.d.ts +131 -0
- package/dist/server/task-summary.js +754 -0
- package/dist/server/write.js +4 -6
- package/dist/ui/assets/{ActivityIndicator-meIh-6Qi.js → ActivityIndicator-BXn8FXaH.js} +1 -1
- package/dist/ui/assets/{AgentModelControl-CUkgcahC.js → AgentModelControl-B7Nx53jN.js} +1 -1
- package/dist/ui/assets/{AgentModelModal-et6vEhsU.js → AgentModelModal-BexSFFvC.js} +1 -1
- package/dist/ui/assets/AgentsView-BDSUKKjx.js +2 -0
- package/dist/ui/assets/AgentsView-fw80jZzb.css +1 -0
- package/dist/ui/assets/{ChatAgentModelChip-BzJzDKcR.js → ChatAgentModelChip-BH10OXwQ.js} +1 -1
- package/dist/ui/assets/ChecksView-7XjxxvM3.js +0 -0
- package/dist/ui/assets/{ChecksView-CfIhwy6Z.css → ChecksView-bW4060TJ.css} +1 -1
- package/dist/ui/assets/ClientBoardView-BtvaFmwp.js +1 -0
- package/dist/ui/assets/ClientBoardView-CMI5835W.css +1 -0
- package/dist/ui/assets/ContextView-BfO-Io9R.js +4 -0
- package/dist/ui/assets/DashboardView-CAu2hv1W.js +1 -0
- package/dist/ui/assets/{DeploymentsView-BvFWskO3.js → DeploymentsView-DB-oQnih.js} +1 -1
- package/dist/ui/assets/{DevView-DKXpK5Qm.js → DevView-DK0NFETd.js} +2 -2
- package/dist/ui/assets/DiffView-DLg3DKTC.css +1 -0
- package/dist/ui/assets/DiffView-Doamf-Ib.js +6 -0
- package/dist/ui/assets/{FailedTestsList-DoNaF-o9.js → FailedTestsList-dqUj2Jdc.js} +1 -1
- package/dist/ui/assets/{InputsView-KeayUEJY.js → InputsView-CIi6y1tW.js} +1 -1
- package/dist/ui/assets/{LoginView-CvXpXpGP.js → LoginView-DHoT2wvS.js} +1 -1
- package/dist/ui/assets/{PageSkeleton-DJRKj5JK.js → PageSkeleton-BYwhPs40.js} +1 -1
- package/dist/ui/assets/{ReleasesView-9CdkP52G.js → ReleasesView-DMoT64oC.js} +1 -1
- package/dist/ui/assets/SecretsView-DHitDUwx.js +1 -0
- package/dist/ui/assets/SettingsView-Doem5tZO.js +4 -0
- package/dist/ui/assets/StoriesView-C092wbH4.js +1 -0
- package/dist/ui/assets/{VoiceDictate-CaVNQ8T-.js → VoiceDictate-BpAHW05d.js} +1 -1
- package/dist/ui/assets/WorkView-6a0gN2l-.css +1 -0
- package/dist/ui/assets/WorkView-DUltG-o6.js +2 -0
- package/dist/ui/assets/{abnfDiagram-VCTEODGH-BI749mHF.js → abnfDiagram-VCTEODGH-Ck6r7miV.js} +1 -1
- package/dist/ui/assets/{architectureDiagram-5GKGNRK7-CtGA7LVO.js → architectureDiagram-5GKGNRK7-Cc0fxIsa.js} +1 -1
- package/dist/ui/assets/{blockDiagram-I7D4REHJ-puRAhxem.js → blockDiagram-I7D4REHJ-0UOj4eU8.js} +1 -1
- package/dist/ui/assets/{c4Diagram-7LVT6UL2-C4CGYisP.js → c4Diagram-7LVT6UL2-DFVZRqEB.js} +1 -1
- package/dist/ui/assets/channel-Cvd9RE10.js +1 -0
- package/dist/ui/assets/check-setup-C5eaJYZG.js +2 -0
- package/dist/ui/assets/{chunk-2E4U76K2-DjnKVsYi.js → chunk-2E4U76K2-Bx7GZJ_K.js} +1 -1
- package/dist/ui/assets/{chunk-4HAMMTFA-CDnKKfj8.js → chunk-4HAMMTFA-CcFh7yhX.js} +1 -1
- package/dist/ui/assets/{chunk-75Z2AOVW-CKQ7DeM6.js → chunk-75Z2AOVW-Ch_fDxwg.js} +1 -1
- package/dist/ui/assets/{chunk-CLGD4ZFX-C4wu0WZN.js → chunk-CLGD4ZFX-C3H0Sy2V.js} +1 -1
- package/dist/ui/assets/{chunk-DU6HZSFF-DrJ5bmzy.js → chunk-DU6HZSFF-Ip5LXuND.js} +1 -1
- package/dist/ui/assets/{chunk-F27PBJKO-CzXjmHDd.js → chunk-F27PBJKO-BC2HCExR.js} +1 -1
- package/dist/ui/assets/{chunk-GMAD6QVW-CcWcSwCd.js → chunk-GMAD6QVW-D9vexk6P.js} +1 -1
- package/dist/ui/assets/{chunk-GVQU2GXP-BaenLrCK.js → chunk-GVQU2GXP-DlFChOLd.js} +1 -1
- package/dist/ui/assets/{chunk-IMKFNOWR-JyaOH2dq.js → chunk-IMKFNOWR-BwF7XF8L.js} +1 -1
- package/dist/ui/assets/{chunk-L3NEJ4N5-D-Soe8Pq.js → chunk-L3NEJ4N5-8JuimrXI.js} +1 -1
- package/dist/ui/assets/{chunk-OSK3NFVY-DyEqccWY.js → chunk-OSK3NFVY-CEk1_jZZ.js} +1 -1
- package/dist/ui/assets/{chunk-P2QGCYS3-PZxLXffD.js → chunk-P2QGCYS3-BFEJ9oIY.js} +1 -1
- package/dist/ui/assets/{chunk-POPQ4Y6H-CLOE0hWn.js → chunk-POPQ4Y6H-ls4xuYo0.js} +1 -1
- package/dist/ui/assets/{chunk-PWAF6VOD-092-eJK8.js → chunk-PWAF6VOD-DAVLljYp.js} +1 -1
- package/dist/ui/assets/{chunk-SHT3W25Y-CSc5sy8R.js → chunk-SHT3W25Y-59nxdP_9.js} +1 -1
- package/dist/ui/assets/{chunk-SVP7TREG-BLTmX5VJ.js → chunk-SVP7TREG-BexCXvFB.js} +1 -1
- package/dist/ui/assets/{chunk-TICWLB2K-SzItQUhb.js → chunk-TICWLB2K-BOEpbfJI.js} +1 -1
- package/dist/ui/assets/{chunk-TLUHSLCS-DMnSH-ft.js → chunk-TLUHSLCS-BE4liAeZ.js} +2 -2
- package/dist/ui/assets/classDiagram-ZZMXUADV-0Lpgi9vA.js +1 -0
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-0Lpgi9vA.js +1 -0
- package/dist/ui/assets/{cynefinDiagram-5FMLGOSQ-1ilx8mKu.js → cynefinDiagram-5FMLGOSQ-BEuH8cqK.js} +1 -1
- package/dist/ui/assets/{dagre-GXQ25YYZ-C3A59Q6w.js → dagre-GXQ25YYZ-DJHX7JRM.js} +1 -1
- package/dist/ui/assets/{diagram-S7CK7UJ4-B-6B9kWN.js → diagram-S7CK7UJ4-CeD3KxHQ.js} +1 -1
- package/dist/ui/assets/{diagram-UQ7AKVKN-BqxmQJR8.js → diagram-UQ7AKVKN-B_Ur_853.js} +1 -1
- package/dist/ui/assets/{diagram-VSXAHHWV-Ck9wWlUm.js → diagram-VSXAHHWV-k1B3tbTY.js} +1 -1
- package/dist/ui/assets/{diagram-VX7I27RA-BQe2g3vh.js → diagram-VX7I27RA-CaI3Gi6a.js} +1 -1
- package/dist/ui/assets/{diagram-Z3DM3KII-DD8365dG.js → diagram-Z3DM3KII-AaNCy6XD.js} +1 -1
- package/dist/ui/assets/{docs-itn09t2R.js → docs-Clr18AxL.js} +1 -1
- package/dist/ui/assets/{ebnfDiagram-PWID7BFC-Bz8cpJNy.js → ebnfDiagram-PWID7BFC-CWdQLfbF.js} +1 -1
- package/dist/ui/assets/{erDiagram-RLTQ6QDP-Bc6u3S9N.js → erDiagram-RLTQ6QDP-vPkf6OX1.js} +1 -1
- package/dist/ui/assets/flowDiagram-HODETNUW-DzaFBPZM.js +1 -0
- package/dist/ui/assets/{ganttDiagram-EL5Y4UJY-DVDVRjA0.js → ganttDiagram-EL5Y4UJY-Dfc5nDxI.js} +1 -1
- package/dist/ui/assets/{gitGraphDiagram-WWUBYQGX-CFUXHZWf.js → gitGraphDiagram-WWUBYQGX-Bq9a64PC.js} +1 -1
- package/dist/ui/assets/index-CqzMM_H1.css +1 -0
- package/dist/ui/assets/index-CrnbCa_P.js +92 -0
- package/dist/ui/assets/{infoDiagram-27XIBGKW-C-7l9NCQ.js → infoDiagram-27XIBGKW-CW8gtkIj.js} +1 -1
- package/dist/ui/assets/{ishikawaDiagram-5VMMS53U-Bjq-LdEc.js → ishikawaDiagram-5VMMS53U-Bc1Rh_cU.js} +1 -1
- package/dist/ui/assets/{journeyDiagram-3NMN7TZE-o4M0lP2i.js → journeyDiagram-3NMN7TZE-BjWhklZZ.js} +1 -1
- package/dist/ui/assets/{kanban-definition-UXKFOSKX-BYqVEzsr.js → kanban-definition-UXKFOSKX-EJjRtvtc.js} +1 -1
- package/dist/ui/assets/{line-4TL08MsA.js → line-DsEqHdtk.js} +1 -1
- package/dist/ui/assets/{mermaid-parser.core-DrM_vPwv.js → mermaid-parser.core-BN01g6kH.js} +1 -1
- package/dist/ui/assets/{mermaid.core-Dzz_-qU1.js → mermaid.core-BZVMMw-7.js} +3 -3
- package/dist/ui/assets/{mindmap-definition-YA3MSWOX-CaKNZzR5.js → mindmap-definition-YA3MSWOX-C4KW0x2p.js} +1 -1
- package/dist/ui/assets/{pegDiagram-XKGWAZYB-DbKJ-jrI.js → pegDiagram-XKGWAZYB-CWCksxEq.js} +1 -1
- package/dist/ui/assets/{pieDiagram-E7YTZNPT-CPiwJysC.js → pieDiagram-E7YTZNPT-DIa72Gin.js} +1 -1
- package/dist/ui/assets/{quadrantDiagram-AXDQQJYC-q32X56Gx.js → quadrantDiagram-AXDQQJYC-Cr3_KoMM.js} +1 -1
- package/dist/ui/assets/{railroadDiagram-O6MQD6OU-DNMhXeke.js → railroadDiagram-O6MQD6OU-BblfxqzT.js} +1 -1
- package/dist/ui/assets/repo-W2udocGJ.js +61 -0
- package/dist/ui/assets/{requirementDiagram-BXWQKSXE-BW09ERRd.js → requirementDiagram-BXWQKSXE-DiDG9q-4.js} +1 -1
- package/dist/ui/assets/{sankeyDiagram-P5KCCOFB-BM5y8xIj.js → sankeyDiagram-P5KCCOFB-6DsHJ6Jh.js} +1 -1
- package/dist/ui/assets/{sequenceDiagram-WJ2MYXX4-QCl8JI9d.js → sequenceDiagram-WJ2MYXX4-CnA-wuKZ.js} +1 -1
- package/dist/ui/assets/{stateDiagram-D77RDMKH-CURAhx8F.js → stateDiagram-D77RDMKH-B1qC_HH_.js} +1 -1
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-D7Wr-Zwf.js +1 -0
- package/dist/ui/assets/{swimlanes-42K2YHIH-CxCV3G9b.js → swimlanes-42K2YHIH-CqZkLtRu.js} +1 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-CJtYP160.js +8 -0
- package/dist/ui/assets/time-8KGOT2-i.js +1 -0
- package/dist/ui/assets/{timeline-definition-24CTP7MA-_hEyz10W.js → timeline-definition-24CTP7MA-Be1h9Gq-.js} +1 -1
- package/dist/ui/assets/{vennDiagram-4TSXK5OY-CdB16vQ8.js → vennDiagram-4TSXK5OY-BE5YDpBV.js} +1 -1
- package/dist/ui/assets/{wardleyDiagram-VM6X3IG4-Bl7UPuNA.js → wardleyDiagram-VM6X3IG4-D_s2lGsn.js} +1 -1
- package/dist/ui/assets/{xychartDiagram-S5SC5T6Z-BN1ho0Vx.js → xychartDiagram-S5SC5T6Z-BgsF5rLo.js} +1 -1
- package/dist/ui/index.html +12 -12
- package/dist/ui/sw.js +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/AgentsView-DBPauK5u.css +0 -1
- package/dist/ui/assets/AgentsView-DF4JpOuL.js +0 -2
- package/dist/ui/assets/ChecksView-C7N3Ypeq.js +0 -0
- package/dist/ui/assets/ContextView-BZdavSqS.js +0 -4
- package/dist/ui/assets/DashboardView-OFLi1McO.js +0 -1
- package/dist/ui/assets/DiffView-Br-vTJ0v.js +0 -4
- package/dist/ui/assets/DiffView-CcApcT6t.css +0 -1
- package/dist/ui/assets/SecretsView-BwDra-GT.js +0 -1
- package/dist/ui/assets/SettingsView-DNCRJx13.js +0 -4
- package/dist/ui/assets/StoriesView-to9fS2F0.js +0 -1
- package/dist/ui/assets/WorkView-C7qnLKcz.css +0 -1
- package/dist/ui/assets/WorkView-D9GE8twS.js +0 -2
- package/dist/ui/assets/channel-D_S1ZUW_.js +0 -1
- package/dist/ui/assets/check-setup-DSNmeJ0T.js +0 -2
- package/dist/ui/assets/classDiagram-ZZMXUADV-B7QHFk-U.js +0 -1
- package/dist/ui/assets/classDiagram-v2-VYDZK3BY-B7QHFk-U.js +0 -1
- package/dist/ui/assets/flowDiagram-HODETNUW-Ba7ylFp4.js +0 -1
- package/dist/ui/assets/index-DITWgaOj.css +0 -1
- package/dist/ui/assets/index-DqJOYNud.js +0 -68
- package/dist/ui/assets/repo-BuEVC7d2.js +0 -59
- package/dist/ui/assets/stateDiagram-v2-MP3YSRHH-CnyC3tkF.js +0 -1
- package/dist/ui/assets/swimlanesDiagram-VR7AAH4N-R6ULRXhK.js +0 -8
- package/dist/ui/assets/time-CKklpdEK.js +0 -1
package/dist/.build-info.json
CHANGED
package/dist/.build-stamp.json
CHANGED
|
@@ -18,6 +18,8 @@ const CONTROL_API_MATCHERS = [
|
|
|
18
18
|
re("POST", /^\/api\/tasks\/([^/]+)\/needs-input\/dismiss$/.source),
|
|
19
19
|
re("POST", /^\/api\/tasks\/([^/]+)\/review\/again$/.source),
|
|
20
20
|
re("GET", /^\/api\/tasks\/([^/]+)\/landable$/.source),
|
|
21
|
+
// `repoos summary <id>` generates or retries the post-merge summary (#1041).
|
|
22
|
+
re("POST", /^\/api\/tasks\/([^/]+)\/summary$/.source),
|
|
21
23
|
// Non-blocking handoff outcome for `repoos handoff-status` (#1028).
|
|
22
24
|
re("GET", /^\/api\/tasks\/([^/]+)\/handoff-status$/.source),
|
|
23
25
|
re("POST", /^\/api\/tasks\/([^/]+)\/preview$/.source),
|
|
@@ -78,6 +80,7 @@ const UI_ONLY_MATCHERS = [
|
|
|
78
80
|
// Task-card-only explicit cancellation of a stalled, registered local check.
|
|
79
81
|
re("POST", /^\/api\/tasks\/([^/]+)\/cancel-check$/.source),
|
|
80
82
|
// Read-only task engineering-report history used by the Dev tab.
|
|
83
|
+
re("GET", /^\/api\/tasks\/([^/]+)\/summary$/.source),
|
|
81
84
|
re("GET", /^\/api\/tasks\/([^/]+)\/engineering-reports$/.source),
|
|
82
85
|
re("GET", /^\/api\/tasks\/([^/]+)\/engineering-reports\/([^/]+)$/.source),
|
|
83
86
|
under("/api/auth/"),
|
package/dist/cli/help.js
CHANGED
|
@@ -83,9 +83,9 @@ const GROUPS = [
|
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
name: "send-back",
|
|
86
|
-
usage: 'send-back <id>
|
|
86
|
+
usage: 'send-back <id> [text | - | --message "…" | --file <path> | --message-file <path>] [--json] [--port N]',
|
|
87
87
|
desc: "Return a task to the engineer, start work, then queue feedback",
|
|
88
|
-
flags: "--message · --message-file · --json · --port",
|
|
88
|
+
flags: "--message · --file · --message-file · --json · --port",
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
name: "start",
|
|
@@ -113,9 +113,9 @@ const GROUPS = [
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
name: "message",
|
|
116
|
-
usage: 'message <id> "<
|
|
116
|
+
usage: 'message <id> [text | - | --message "…" | --file <path> | --message-file <path>] [--now] [--json] [--port N]',
|
|
117
117
|
desc: "Send a follow-up to the task agent; queues while busy, --now interrupts the turn and delivers immediately (POST /api/tasks/:id/message)",
|
|
118
|
-
flags: "--now · --json · --port",
|
|
118
|
+
flags: "--message · --file · --message-file · --now · --json · --port",
|
|
119
119
|
},
|
|
120
120
|
{
|
|
121
121
|
name: "review",
|
|
@@ -160,6 +160,12 @@ const GROUPS = [
|
|
|
160
160
|
desc: "Read-only checks before merging (verdict, lock, worktree, merge, scope, main) — exits non-zero when the task is not ready to merge (GET /api/tasks/:id/landable)",
|
|
161
161
|
flags: "--json · --port",
|
|
162
162
|
},
|
|
163
|
+
{
|
|
164
|
+
name: "summary",
|
|
165
|
+
usage: "summary <id> [--json] [--port N]",
|
|
166
|
+
desc: "Generate or retry the post-merge PM summary report (POST /api/tasks/:id/summary)",
|
|
167
|
+
flags: "--json · --port",
|
|
168
|
+
},
|
|
163
169
|
{
|
|
164
170
|
name: "handoff-status",
|
|
165
171
|
usage: "handoff-status <id> [--json] [--port N]",
|
package/dist/cli/index.js
CHANGED
|
@@ -27,7 +27,7 @@ import { cmdCertify } from "../commands/certify.js";
|
|
|
27
27
|
import { cmdSupport } from "../commands/support.js";
|
|
28
28
|
import { cmdService } from "../commands/service.js";
|
|
29
29
|
import { cmdServers } from "../commands/servers.js";
|
|
30
|
-
import { cmdAgentsRunning, cmdConfig, cmdDone, cmdLandable, cmdHandoffStatus, cmdMessage, cmdSendBack, cmdOverride, cmdPause, cmdPreview, cmdDev, cmdReview, cmdVerify, cmdRunners, cmdStart, cmdSync, cmdStats, cmdDecisions, cmdApi, cmdIntegration, cmdAttention, cmdHold, cmdUnhold, } from "../commands/control-api.js";
|
|
30
|
+
import { cmdAgentsRunning, cmdConfig, cmdDone, cmdLandable, cmdHandoffStatus, cmdSummary, cmdMessage, cmdSendBack, cmdOverride, cmdPause, cmdPreview, cmdDev, cmdReview, cmdVerify, cmdRunners, cmdStart, cmdSync, cmdStats, cmdDecisions, cmdApi, cmdIntegration, cmdAttention, cmdHold, cmdUnhold, } from "../commands/control-api.js";
|
|
31
31
|
import { cmdWatch } from "../commands/watch.js";
|
|
32
32
|
import { cmdDriver } from "../commands/driver.js";
|
|
33
33
|
import { cmdBundle } from "../commands/bundle.js";
|
|
@@ -333,6 +333,12 @@ function main() {
|
|
|
333
333
|
process.exitCode = code;
|
|
334
334
|
});
|
|
335
335
|
break;
|
|
336
|
+
case "summary":
|
|
337
|
+
void cmdSummary(rest).then((code) => {
|
|
338
|
+
if (code !== 0)
|
|
339
|
+
process.exitCode = code;
|
|
340
|
+
});
|
|
341
|
+
break;
|
|
336
342
|
case "override":
|
|
337
343
|
void cmdOverride(rest).then((code) => {
|
|
338
344
|
if (code !== 0)
|
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import { RepoOsApi, type ApiJson } from "../cli/repoos-api.js";
|
|
2
|
+
interface CommonOpts {
|
|
3
|
+
json: boolean;
|
|
4
|
+
wait: boolean;
|
|
5
|
+
now: boolean;
|
|
6
|
+
port?: number;
|
|
7
|
+
/** `repoos hold`: free-text reason the task was parked (#0960). */
|
|
8
|
+
reason?: string;
|
|
9
|
+
/** `repoos hold`: ISO-8601 auto-release time (#0960). */
|
|
10
|
+
until?: string;
|
|
11
|
+
/** `repoos hold`: who placed the hold (#0960); defaults to "driver" server-side. */
|
|
12
|
+
by?: string;
|
|
13
|
+
/** `repoos unhold --start`: start the engineer after releasing the hold (#0960). */
|
|
14
|
+
start?: boolean;
|
|
15
|
+
/** `repoos start --force`: start even a held task (#0960). */
|
|
16
|
+
force?: boolean;
|
|
17
|
+
}
|
|
2
18
|
export declare function cmdStart(args: string[]): Promise<number>;
|
|
3
19
|
export declare function cmdHold(args: string[]): Promise<number>;
|
|
4
20
|
export declare function cmdUnhold(args: string[]): Promise<number>;
|
|
5
21
|
export declare function cmdPause(args: string[]): Promise<number>;
|
|
6
|
-
|
|
22
|
+
/** Parse message sources before contacting the server, preserving option values verbatim. */
|
|
23
|
+
export declare function parseMessageInput(args: string[], stdin?: {
|
|
24
|
+
isTTY: boolean;
|
|
25
|
+
read: () => string;
|
|
26
|
+
}): {
|
|
27
|
+
id: string;
|
|
28
|
+
text: string;
|
|
29
|
+
opts: CommonOpts;
|
|
30
|
+
};
|
|
31
|
+
export declare function cmdMessage(args: string[], stdin?: Parameters<typeof parseMessageInput>[1]): Promise<number>;
|
|
7
32
|
/** `repoos send-back` — reopen, start the engineer, then deliver feedback. */
|
|
8
|
-
export declare function cmdSendBack(args: string[]): Promise<number>;
|
|
33
|
+
export declare function cmdSendBack(args: string[], stdin?: Parameters<typeof parseMessageInput>[1]): Promise<number>;
|
|
9
34
|
export declare function cmdReviewAgain(args: string[]): Promise<number>;
|
|
10
35
|
export declare function cmdReview(args: string[]): Promise<number>;
|
|
11
36
|
type ReviewWaitApi = Pick<RepoOsApi, "requestOk">;
|
|
@@ -94,6 +119,44 @@ export declare function cmdHandoffStatus(args: string[]): Promise<number>;
|
|
|
94
119
|
* exit 0 only when every check passes. Never edits, commits or moves status.
|
|
95
120
|
*/
|
|
96
121
|
export declare function cmdLandable(args: string[]): Promise<number>;
|
|
122
|
+
/** Shape of `GET /api/tasks/:id/summary` used by `repoos summary`. */
|
|
123
|
+
export interface TaskSummaryState {
|
|
124
|
+
ok: boolean;
|
|
125
|
+
exists: boolean;
|
|
126
|
+
markdown: string | null;
|
|
127
|
+
status: "idle" | "pending" | "done" | "failed";
|
|
128
|
+
statusAt: string | null;
|
|
129
|
+
/** Anchor for this `pending` run (ISO); mirrors server payload. */
|
|
130
|
+
pendingSince: string | null;
|
|
131
|
+
reportMtimeMs: number | null;
|
|
132
|
+
error: string | null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Terminal poll states: `done`/`failed`, a committed report while status is
|
|
136
|
+
* `idle` (lost write), or `pending` long enough that the server coerces to `done`
|
|
137
|
+
* (API returns `done` when stale; this mirrors that for older servers).
|
|
138
|
+
*/
|
|
139
|
+
export declare function isSummaryPollTerminal(state: Pick<TaskSummaryState, "status" | "exists" | "statusAt" | "pendingSince" | "reportMtimeMs">, expectedRunAt?: string | null): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Poll a summary status fetcher until terminal. A committed report (`exists`)
|
|
142
|
+
* with non-`pending` status is terminal (including `idle` when the done-status
|
|
143
|
+
* write was lost). While `pending`, keep polling even when the file exists —
|
|
144
|
+
* a retry may still be running. Persistent fetch failures fail fast.
|
|
145
|
+
*/
|
|
146
|
+
export declare function pollSummaryState(getState: () => Promise<TaskSummaryState>, opts?: {
|
|
147
|
+
label?: string;
|
|
148
|
+
timeoutMs?: number;
|
|
149
|
+
intervalMs?: number;
|
|
150
|
+
maxConsecutiveErrors?: number;
|
|
151
|
+
expectedRunAt?: string | null;
|
|
152
|
+
}): Promise<TaskSummaryState>;
|
|
153
|
+
/**
|
|
154
|
+
* `repoos summary <id>` (#1041): queue the post-merge PM summary report and
|
|
155
|
+
* wait for its terminal state. Requires a released (`done`) task — the normal
|
|
156
|
+
* path runs it automatically after Move to done; this is the visible retry.
|
|
157
|
+
* Never touches the task status.
|
|
158
|
+
*/
|
|
159
|
+
export declare function cmdSummary(args: string[]): Promise<number>;
|
|
97
160
|
/** Shared server-down check for tests. */
|
|
98
161
|
export declare function controlApiServerDownMessage(port: number): Promise<string>;
|
|
99
162
|
export {};
|
|
@@ -8,6 +8,7 @@ import { effectiveEngineerFromApi } from "../cli/effective-from-api.js";
|
|
|
8
8
|
import { HANDOFF_DEADLINE_MS } from "../server/handoff.js";
|
|
9
9
|
import { fmtDuration } from "./driver.js";
|
|
10
10
|
import { agentModelAllowRows, pauseAllowlistEntry, resumeAllowlistEntry, pairPauseInfo, } from "../core/budget-allowlist.js";
|
|
11
|
+
import { effectiveSummaryJobStatus } from "../core/task-summary.js";
|
|
11
12
|
function parseCommonFlags(args) {
|
|
12
13
|
const opts = { json: false, wait: false, now: false };
|
|
13
14
|
const rest = [];
|
|
@@ -276,18 +277,88 @@ export async function cmdPause(args) {
|
|
|
276
277
|
return 1;
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
/** Parse message sources before contacting the server, preserving option values verbatim. */
|
|
281
|
+
export function parseMessageInput(args, stdin = { isTTY: process.stdin.isTTY, read: () => readFileSync(0, "utf8") }) {
|
|
282
|
+
const opts = { json: false, wait: false, now: false };
|
|
283
|
+
let id;
|
|
284
|
+
let source;
|
|
285
|
+
let value = "";
|
|
286
|
+
const choose = (next, input) => {
|
|
287
|
+
if (source && !(source === "inline" && next === "inline"))
|
|
288
|
+
throw new Error("Use only one message input source");
|
|
289
|
+
value = source === "inline" ? `${value} ${input}` : input;
|
|
290
|
+
source = next;
|
|
291
|
+
};
|
|
292
|
+
for (let i = 0; i < args.length; i++) {
|
|
293
|
+
const arg = args[i];
|
|
294
|
+
if (arg === "--json")
|
|
295
|
+
opts.json = true;
|
|
296
|
+
else if (arg === "--now")
|
|
297
|
+
opts.now = true;
|
|
298
|
+
else if (arg === "--port") {
|
|
299
|
+
const port = Number(args[++i]);
|
|
300
|
+
if (!Number.isInteger(port) || port <= 0)
|
|
301
|
+
throw new Error("--port requires a positive integer");
|
|
302
|
+
opts.port = port;
|
|
303
|
+
}
|
|
304
|
+
else if (arg === "--message" || arg === "--file" || arg === "--message-file") {
|
|
305
|
+
const input = args[++i];
|
|
306
|
+
if (input === undefined || input.startsWith("--"))
|
|
307
|
+
throw new Error(`${arg} requires a value`);
|
|
308
|
+
choose(arg === "--message" ? "message" : "file", input);
|
|
309
|
+
}
|
|
310
|
+
else if (arg.startsWith("--"))
|
|
311
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
312
|
+
else if (!id) {
|
|
313
|
+
if (arg === "-")
|
|
314
|
+
throw new Error("A task id is required before stdin");
|
|
315
|
+
id = arg;
|
|
316
|
+
}
|
|
317
|
+
else if (arg === "-")
|
|
318
|
+
choose("stdin", "-");
|
|
319
|
+
else
|
|
320
|
+
choose("inline", arg);
|
|
284
321
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (!
|
|
288
|
-
|
|
322
|
+
if (!id)
|
|
323
|
+
throw new Error("A task id is required");
|
|
324
|
+
if (!source && !stdin.isTTY)
|
|
325
|
+
source = "stdin";
|
|
326
|
+
if (source === "stdin" || (source === "file" && value === "-")) {
|
|
327
|
+
if (stdin.isTTY)
|
|
328
|
+
throw new Error("No stdin provided; pipe a message or use a heredoc");
|
|
329
|
+
try {
|
|
330
|
+
value = stdin.read();
|
|
331
|
+
}
|
|
332
|
+
catch (e) {
|
|
333
|
+
throw new Error(`Could not read stdin: ${e.message}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
else if (source === "file") {
|
|
337
|
+
try {
|
|
338
|
+
value = readFileSync(value, "utf8");
|
|
339
|
+
}
|
|
340
|
+
catch (e) {
|
|
341
|
+
throw new Error(`Could not read message file: ${e.message}`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const text = value.trim();
|
|
345
|
+
if (!text)
|
|
346
|
+
throw new Error("Message cannot be empty or whitespace-only");
|
|
347
|
+
return { id, text, opts };
|
|
348
|
+
}
|
|
349
|
+
function messageSummary(text) {
|
|
350
|
+
return `${text.length} characters: ${text.split(/\r?\n/, 1)[0]}`;
|
|
351
|
+
}
|
|
352
|
+
export async function cmdMessage(args, stdin) {
|
|
353
|
+
let input;
|
|
354
|
+
try {
|
|
355
|
+
input = parseMessageInput(args, stdin);
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
printErr(e.message);
|
|
289
359
|
return 1;
|
|
290
360
|
}
|
|
361
|
+
const { id, text, opts } = input;
|
|
291
362
|
const api = new RepoOsApi({ port: opts.port });
|
|
292
363
|
try {
|
|
293
364
|
await ensureServer(api);
|
|
@@ -301,14 +372,14 @@ export async function cmdMessage(args) {
|
|
|
301
372
|
const why = data.now
|
|
302
373
|
? (data.reason ?? "agent still busy after the interrupt")
|
|
303
374
|
: "agent busy — delivers when the turn ends";
|
|
304
|
-
console.log(c.green(" ✓ ") + c.dim(`message queued to #${id} (${why})`));
|
|
375
|
+
console.log(c.green(" ✓ ") + c.dim(`message queued to #${id} (${why}) — ${messageSummary(text)}`));
|
|
305
376
|
}
|
|
306
377
|
else if (data?.deliveredNow) {
|
|
307
378
|
const how = data.interrupted ? " (interrupted the running turn)" : " (agent was idle)";
|
|
308
|
-
console.log(c.green(" ✓ ") + c.dim(`message delivered now to #${id}${how}`));
|
|
379
|
+
console.log(c.green(" ✓ ") + c.dim(`message delivered now to #${id}${how} — ${messageSummary(text)}`));
|
|
309
380
|
}
|
|
310
381
|
else {
|
|
311
|
-
console.log(c.green(" ✓ ") + c.dim(`message sent to #${id}`));
|
|
382
|
+
console.log(c.green(" ✓ ") + c.dim(`message sent to #${id} — ${messageSummary(text)}`));
|
|
312
383
|
}
|
|
313
384
|
return 0;
|
|
314
385
|
}
|
|
@@ -318,49 +389,20 @@ export async function cmdMessage(args) {
|
|
|
318
389
|
}
|
|
319
390
|
}
|
|
320
391
|
/** `repoos send-back` — reopen, start the engineer, then deliver feedback. */
|
|
321
|
-
export async function cmdSendBack(args) {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
392
|
+
export async function cmdSendBack(args, stdin) {
|
|
393
|
+
let input;
|
|
394
|
+
try {
|
|
395
|
+
input = parseMessageInput(args, stdin);
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
printErr(e.message);
|
|
325
399
|
return 1;
|
|
326
400
|
}
|
|
401
|
+
const { id, text: message, opts } = input;
|
|
327
402
|
if (opts.now) {
|
|
328
403
|
printErr("repoos send-back does not support --now; feedback is queued after the engineer starts");
|
|
329
404
|
return 1;
|
|
330
405
|
}
|
|
331
|
-
let id;
|
|
332
|
-
let message;
|
|
333
|
-
let messageFile;
|
|
334
|
-
for (let i = 0; i < rest.length; i++) {
|
|
335
|
-
if (rest[i] === "--message")
|
|
336
|
-
message = rest[++i];
|
|
337
|
-
else if (rest[i] === "--message-file")
|
|
338
|
-
messageFile = rest[++i];
|
|
339
|
-
else if (!id)
|
|
340
|
-
id = rest[i];
|
|
341
|
-
else {
|
|
342
|
-
printErr(`Unexpected argument: ${rest[i]}`);
|
|
343
|
-
return 1;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (message !== undefined && messageFile !== undefined) {
|
|
347
|
-
printErr("Use --message or --message-file, not both");
|
|
348
|
-
return 1;
|
|
349
|
-
}
|
|
350
|
-
if (messageFile !== undefined) {
|
|
351
|
-
try {
|
|
352
|
-
message = messageFile === "-" ? readFileSync(0, "utf8") : readFileSync(messageFile, "utf8");
|
|
353
|
-
}
|
|
354
|
-
catch (e) {
|
|
355
|
-
printErr(`Could not read message file: ${e.message}`);
|
|
356
|
-
return 1;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
message = message?.trim();
|
|
360
|
-
if (!id || !message) {
|
|
361
|
-
printErr('Usage: repoos send-back <id> (--message "…" | --message-file <file|->) [--json] [--port N]');
|
|
362
|
-
return 1;
|
|
363
|
-
}
|
|
364
406
|
const api = new RepoOsApi({ port: opts.port });
|
|
365
407
|
try {
|
|
366
408
|
await ensureServer(api);
|
|
@@ -370,7 +412,7 @@ export async function cmdSendBack(args) {
|
|
|
370
412
|
else {
|
|
371
413
|
console.log(c.green(" ✓ ") + c.dim(`moved #${id} to active`));
|
|
372
414
|
console.log(c.green(" ✓ ") + c.dim(`started engineer on #${id}`));
|
|
373
|
-
console.log(c.green(" ✓ ") + c.dim(`queued message to #${id}`));
|
|
415
|
+
console.log(c.green(" ✓ ") + c.dim(`queued message to #${id} — ${messageSummary(message)}`));
|
|
374
416
|
}
|
|
375
417
|
return 0;
|
|
376
418
|
}
|
|
@@ -1602,6 +1644,115 @@ export async function cmdLandable(args) {
|
|
|
1602
1644
|
return 1;
|
|
1603
1645
|
}
|
|
1604
1646
|
}
|
|
1647
|
+
/**
|
|
1648
|
+
* Terminal poll states: `done`/`failed`, a committed report while status is
|
|
1649
|
+
* `idle` (lost write), or `pending` long enough that the server coerces to `done`
|
|
1650
|
+
* (API returns `done` when stale; this mirrors that for older servers).
|
|
1651
|
+
*/
|
|
1652
|
+
export function isSummaryPollTerminal(state, expectedRunAt) {
|
|
1653
|
+
if (state.status === "failed")
|
|
1654
|
+
return true;
|
|
1655
|
+
// A successful POST starts a specific run. Do not let an unchanged idle/done
|
|
1656
|
+
// record and its previous report satisfy that run's poll.
|
|
1657
|
+
if (expectedRunAt) {
|
|
1658
|
+
const runAt = Date.parse(expectedRunAt);
|
|
1659
|
+
if (!Number.isFinite(runAt))
|
|
1660
|
+
return false;
|
|
1661
|
+
if (state.status === "pending") {
|
|
1662
|
+
const pendingAt = Date.parse(state.pendingSince ?? state.statusAt ?? "");
|
|
1663
|
+
if (!Number.isFinite(pendingAt) || pendingAt < runAt)
|
|
1664
|
+
return false;
|
|
1665
|
+
}
|
|
1666
|
+
else {
|
|
1667
|
+
const stateAt = Date.parse(state.statusAt ?? "");
|
|
1668
|
+
if (!Number.isFinite(stateAt) || stateAt < runAt)
|
|
1669
|
+
return false;
|
|
1670
|
+
if (state.exists && state.reportMtimeMs !== null && state.reportMtimeMs < runAt)
|
|
1671
|
+
return false;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
if (state.status === "done")
|
|
1675
|
+
return true;
|
|
1676
|
+
const eff = effectiveSummaryJobStatus(state.status, state.statusAt ?? "", state.exists, Date.now(), state.reportMtimeMs ?? null, state.pendingSince);
|
|
1677
|
+
if (eff === "done")
|
|
1678
|
+
return true;
|
|
1679
|
+
if (state.exists && state.status !== "pending")
|
|
1680
|
+
return true;
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
const SUMMARY_MAX_GET_ERRORS = 10;
|
|
1684
|
+
/**
|
|
1685
|
+
* Poll a summary status fetcher until terminal. A committed report (`exists`)
|
|
1686
|
+
* with non-`pending` status is terminal (including `idle` when the done-status
|
|
1687
|
+
* write was lost). While `pending`, keep polling even when the file exists —
|
|
1688
|
+
* a retry may still be running. Persistent fetch failures fail fast.
|
|
1689
|
+
*/
|
|
1690
|
+
export async function pollSummaryState(getState, opts = {}) {
|
|
1691
|
+
const maxConsecutiveErrors = opts.maxConsecutiveErrors ?? SUMMARY_MAX_GET_ERRORS;
|
|
1692
|
+
let consecutiveGetErrors = 0;
|
|
1693
|
+
const label = opts.label ?? "summary";
|
|
1694
|
+
return pollUntil(async () => {
|
|
1695
|
+
let state;
|
|
1696
|
+
try {
|
|
1697
|
+
state = await getState();
|
|
1698
|
+
}
|
|
1699
|
+
catch (err) {
|
|
1700
|
+
consecutiveGetErrors += 1;
|
|
1701
|
+
if (consecutiveGetErrors >= maxConsecutiveErrors)
|
|
1702
|
+
throw new RepoOsApiError(`summary status unreachable (${label}): ${err.message}`, 503);
|
|
1703
|
+
return null;
|
|
1704
|
+
}
|
|
1705
|
+
consecutiveGetErrors = 0;
|
|
1706
|
+
return isSummaryPollTerminal(state, opts.expectedRunAt) ? state : null;
|
|
1707
|
+
}, { label, timeoutMs: opts.timeoutMs ?? 900_000, intervalMs: opts.intervalMs ?? 2_000 });
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* `repoos summary <id>` (#1041): queue the post-merge PM summary report and
|
|
1711
|
+
* wait for its terminal state. Requires a released (`done`) task — the normal
|
|
1712
|
+
* path runs it automatically after Move to done; this is the visible retry.
|
|
1713
|
+
* Never touches the task status.
|
|
1714
|
+
*/
|
|
1715
|
+
export async function cmdSummary(args) {
|
|
1716
|
+
const { rest, opts, error } = parseCommonFlags(args);
|
|
1717
|
+
if (error) {
|
|
1718
|
+
printErr(error);
|
|
1719
|
+
return 1;
|
|
1720
|
+
}
|
|
1721
|
+
const id = rest[0];
|
|
1722
|
+
if (!id) {
|
|
1723
|
+
printErr("Usage: repoos summary <id> [--json] [--port N]");
|
|
1724
|
+
return 1;
|
|
1725
|
+
}
|
|
1726
|
+
const api = new RepoOsApi({ port: opts.port });
|
|
1727
|
+
try {
|
|
1728
|
+
await ensureServer(api);
|
|
1729
|
+
const queued = await api.request("POST", `/api/tasks/${id}/summary`, {});
|
|
1730
|
+
if (queued.status === 404)
|
|
1731
|
+
throw new RepoOsApiError(`Task #${id} not found`, 404, queued.body);
|
|
1732
|
+
if (queued.status !== 202 || !queued.body.ok)
|
|
1733
|
+
throw new RepoOsApiError(queued.body.error ?? `summary refused (${queued.status})`, queued.status, queued.body);
|
|
1734
|
+
if (!opts.json)
|
|
1735
|
+
console.log(c.dim(` … generating post-merge summary for #${id}`));
|
|
1736
|
+
const expectedRunAt = typeof queued.body.pendingSince === "string" ? queued.body.pendingSince : null;
|
|
1737
|
+
const final = await pollSummaryState(async () => (await api.requestOk("GET", `/api/tasks/${id}/summary`)), { label: `summary for #${id}`, expectedRunAt });
|
|
1738
|
+
const eff = effectiveSummaryJobStatus(final.status, final.statusAt ?? "", final.exists, Date.now(), final.reportMtimeMs ?? null, final.pendingSince);
|
|
1739
|
+
const success = eff === "done" || (final.exists && eff !== "failed" && eff !== "pending");
|
|
1740
|
+
if (opts.json) {
|
|
1741
|
+
printJson(final);
|
|
1742
|
+
return success ? 0 : 1;
|
|
1743
|
+
}
|
|
1744
|
+
if (success) {
|
|
1745
|
+
console.log(c.green(` ✓ #${id} summary ready: work/reports/${id}/summary.md`));
|
|
1746
|
+
return 0;
|
|
1747
|
+
}
|
|
1748
|
+
printErr(final.error ?? `summary generation failed for #${id}`);
|
|
1749
|
+
return 1;
|
|
1750
|
+
}
|
|
1751
|
+
catch (e) {
|
|
1752
|
+
printErr(e.message);
|
|
1753
|
+
return 1;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1605
1756
|
/** Shared server-down check for tests. */
|
|
1606
1757
|
export async function controlApiServerDownMessage(port) {
|
|
1607
1758
|
const { baseUrl } = resolveControlPlaneBase({ port });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { cmdMessage, cmdSendBack, parseMessageInput } from "./control-api.js";
|
|
6
|
+
import { RepoOsApi } from "../cli/repoos-api.js";
|
|
7
|
+
const dir = mkdtempSync(join(tmpdir(), "repoos-message-input-"));
|
|
8
|
+
const file = join(dir, "brief.txt");
|
|
9
|
+
writeFileSync(file, " first line\nsecond line \n");
|
|
10
|
+
const piped = { isTTY: false, read: () => " first line\nsecond line \n" };
|
|
11
|
+
const terminal = {
|
|
12
|
+
isTTY: true,
|
|
13
|
+
read: () => {
|
|
14
|
+
throw new Error("unexpected read");
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
afterEach(() => vi.restoreAllMocks());
|
|
18
|
+
process.on("exit", () => rmSync(dir, { recursive: true, force: true }));
|
|
19
|
+
describe.each([
|
|
20
|
+
["message", cmdMessage],
|
|
21
|
+
["send-back", cmdSendBack],
|
|
22
|
+
])("%s message input", (_name, command) => {
|
|
23
|
+
it.each([
|
|
24
|
+
[["first", "line"], "first line"],
|
|
25
|
+
[["-"], "first line\nsecond line"],
|
|
26
|
+
[[], "first line\nsecond line"],
|
|
27
|
+
[["--file", file], "first line\nsecond line"],
|
|
28
|
+
[["--message-file", file], "first line\nsecond line"],
|
|
29
|
+
[["--message", "first line"], "first line"],
|
|
30
|
+
[["--message-file", "-"], "first line\nsecond line"],
|
|
31
|
+
])("accepts %j", async (args, expected) => {
|
|
32
|
+
vi.spyOn(RepoOsApi.prototype, "health").mockResolvedValue(true);
|
|
33
|
+
const request = vi.spyOn(RepoOsApi.prototype, "requestOk").mockResolvedValue({});
|
|
34
|
+
vi.spyOn(console, "log").mockImplementation(() => { });
|
|
35
|
+
expect(await command(["123", ...args], piped)).toBe(0);
|
|
36
|
+
expect(request).toHaveBeenCalledWith("POST", expect.any(String), expect.objectContaining(_name === "message" ? { text: expected } : { message: expected }));
|
|
37
|
+
});
|
|
38
|
+
it.each([
|
|
39
|
+
["text", "--file", file],
|
|
40
|
+
["-"],
|
|
41
|
+
["--unknown"],
|
|
42
|
+
["--message", " "],
|
|
43
|
+
["--file", join(dir, "missing")],
|
|
44
|
+
["--message"],
|
|
45
|
+
["--message", "x", "extra"],
|
|
46
|
+
])("rejects %j before the API", async (...args) => {
|
|
47
|
+
expect(() => parseMessageInput(["123", ...args], terminal)).toThrow();
|
|
48
|
+
const health = vi.spyOn(RepoOsApi.prototype, "health");
|
|
49
|
+
vi.spyOn(console, "error").mockImplementation(() => { });
|
|
50
|
+
expect(await command(["123", ...args], terminal)).toBe(1);
|
|
51
|
+
expect(health).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
it("reports the text length and first line", async () => {
|
|
54
|
+
vi.spyOn(RepoOsApi.prototype, "health").mockResolvedValue(true);
|
|
55
|
+
const request = vi.spyOn(RepoOsApi.prototype, "requestOk").mockResolvedValue({});
|
|
56
|
+
const log = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
57
|
+
expect(await command(["123", "--message", "hello\nworld"])).toBe(0);
|
|
58
|
+
expect(request).toHaveBeenCalledWith("POST", expect.any(String), expect.objectContaining(_name === "message" ? { text: "hello\nworld" } : { message: "hello\nworld" }));
|
|
59
|
+
expect(log.mock.calls.flat().join(" ")).toContain("11 characters: hello");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -56,6 +56,11 @@ export interface ActiveTaskCardInput {
|
|
|
56
56
|
localQueueTitle: string | null;
|
|
57
57
|
remotePoolQueueLabel: string | null;
|
|
58
58
|
remotePoolQueueTitle: string | null;
|
|
59
|
+
/** A live local check slot can prove a run when no check history row exists. */
|
|
60
|
+
localCheckSlot?: {
|
|
61
|
+
ageMs: number;
|
|
62
|
+
heartbeatAgeMs: number;
|
|
63
|
+
} | null;
|
|
59
64
|
busyChild: string | null;
|
|
60
65
|
stuckSilenceMs?: number;
|
|
61
66
|
checkStallMs?: number;
|
|
@@ -99,6 +99,15 @@ export function resolveActiveEngineerCardHint(input) {
|
|
|
99
99
|
blocksAgentInterrupt: true,
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
if (input.localCheckSlot) {
|
|
103
|
+
const elapsed = formatCardDuration(input.localCheckSlot.ageMs);
|
|
104
|
+
return {
|
|
105
|
+
label: `running checks · check · ${elapsed}`,
|
|
106
|
+
title: `A local check slot is held for this task · running for ${elapsed}.`,
|
|
107
|
+
cls: "tc-reviewing",
|
|
108
|
+
blocksAgentInterrupt: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
102
111
|
const check = input.check;
|
|
103
112
|
if (check?.running && checkHeartbeating(check, input.nowMs, stallMs)) {
|
|
104
113
|
const elapsed = formatCardDuration(msSince(check.startedAt, input.nowMs) ?? 0);
|
|
@@ -216,6 +216,28 @@
|
|
|
216
216
|
"verificationSource": "repoos certify pi live probe \u2014 8/8 seams passed on 2026-10-04T11:42:16.118Z against binary /Users/nick/.local/bin/pi on model openrouter/z-ai/glm-5.3-flash",
|
|
217
217
|
"upgradeGuidance": "pi is pre-1.0, so `--mode json` and session flags can change between minor releases; the supported range is deliberately narrow. Upgrade with `npm i -g @earendil-works/pi-coding-agent`, then run `repoos doctor --probe pi --yes` and update this contract only after a documented passing probe.",
|
|
218
218
|
"officialUrl": "https://github.com/earendil-works/pi-coding-agent"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"cli": "muse",
|
|
222
|
+
"name": "Muse Code",
|
|
223
|
+
"supportedMajor": 1,
|
|
224
|
+
"supportedRange": ">=1.0.0 <2.0.0",
|
|
225
|
+
"newestCertifiedVersion": "1.4.14",
|
|
226
|
+
"knownIncompatibleRanges": [],
|
|
227
|
+
"requiredCapabilities": [
|
|
228
|
+
"version",
|
|
229
|
+
"help",
|
|
230
|
+
"model-discovery",
|
|
231
|
+
"headless-one-shot",
|
|
232
|
+
"structured-events",
|
|
233
|
+
"auto-permissions",
|
|
234
|
+
"session-continuation",
|
|
235
|
+
"cancellation"
|
|
236
|
+
],
|
|
237
|
+
"verifiedAt": "2026-11-05",
|
|
238
|
+
"verificationSource": "repoos certify muse live probe \u2014 8/8 seams passed on 2026-11-05T14:30:48.057Z against binary /Users/nick/.local/bin/muse on model muse-spark-1.3",
|
|
239
|
+
"upgradeGuidance": "Install the latest Muse Code release: curl -fsSL https://dev.meta.ai/install.sh | sh",
|
|
240
|
+
"officialUrl": "https://dev.meta.ai/docs/muse-code"
|
|
219
241
|
}
|
|
220
242
|
]
|
|
221
243
|
}
|