rovecode 0.3.3 → 0.4.0-beta.3
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/CHANGELOG.md +527 -0
- package/README.md +46 -48
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-j6gn14w3.js +2 -0
- package/dist/cli/ask-user-cwstt8fz.js +2 -0
- package/dist/cli/auth-login-9bbp9915.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-16zqdms5.js +9 -0
- package/dist/cli/catalog-1xchffa4.js +2 -0
- package/dist/cli/cli-1n1zb64f.js +2 -0
- package/dist/cli/client-2t9gjkck.js +2 -0
- package/dist/cli/commands-exafvm2b.js +2 -0
- package/dist/cli/connect-6zde0kn3.js +2 -0
- package/dist/cli/context-cmd-5t43wgqt.js +2 -0
- package/dist/cli/context-report-kt01pw8y.js +2 -0
- package/dist/cli/count-remote-ap7x3vh6.js +2 -0
- package/dist/cli/design-ne5zszyh.js +2 -0
- package/dist/cli/dispatch-2r5myxye.js +2 -0
- package/dist/cli/doctor-ws4fh4tn.js +3 -0
- package/dist/cli/executor-bdrjn634.js +2 -0
- package/dist/cli/export-1mxb9g5p.js +2 -0
- package/dist/cli/files-g104xghh.js +2 -0
- package/dist/cli/gauntlet-07xrjpj7.js +2 -0
- package/dist/cli/gauntlet-runner-xvy64436.js +10 -0
- package/dist/cli/gauntlet-wave3-jm91yt5w.js +5 -0
- package/dist/cli/gauntlet-wave4-r13py7p1.js +14 -0
- package/dist/cli/hashline-znvrat11.js +2 -0
- package/dist/cli/http-xafw6fsh.js +143 -0
- package/dist/cli/index-1sgjm25y.js +2 -0
- package/dist/cli/init-g2m0tn4m.js +51 -0
- package/dist/cli/install-avaqjjqq.js +2 -0
- package/dist/cli/loop-mmpfft01.js +2 -0
- package/dist/cli/main-0904f6ps.js +5 -0
- package/dist/cli/main-0ab9fc26.js +9 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-0mtcdbs7.js +3 -0
- package/dist/cli/main-0z1w2zsg.js +3 -0
- package/dist/cli/main-1dchs7xv.js +18 -0
- package/dist/cli/main-1ereejm1.js +3 -0
- package/dist/cli/main-1k1kw6b5.js +3 -0
- package/dist/cli/main-27y4sm2k.js +38 -0
- package/dist/cli/main-2wwjex5j.js +58 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2yfck9b5.js +3 -0
- package/dist/cli/main-2zmzgkwh.js +3 -0
- package/dist/cli/main-351pz3z7.js +7 -0
- package/dist/cli/main-3gjqfh7a.js +6 -0
- package/dist/cli/main-3nf3kgve.js +3 -0
- package/dist/cli/main-3pjrb2hd.js +3 -0
- package/dist/cli/main-3rxcvgna.js +19 -0
- package/dist/cli/main-4b3jgy66.js +19 -0
- package/dist/cli/main-4wndhjdc.js +7 -0
- package/dist/cli/main-4xcmvxnk.js +3 -0
- package/dist/cli/main-5tbz0wbz.js +4 -0
- package/dist/cli/main-5ywnwthm.js +3 -0
- package/dist/cli/main-6b62vkz0.js +14 -0
- package/dist/cli/main-6dnk69vp.js +3 -0
- package/dist/cli/main-6genrmhs.js +136 -0
- package/dist/cli/main-73g7eff4.js +15 -0
- package/dist/cli/main-7c5thhjd.js +5 -0
- package/dist/cli/main-7rn6bqje.js +3 -0
- package/dist/cli/main-80haw7qk.js +4 -0
- package/dist/cli/main-875s60s2.js +4 -0
- package/dist/cli/main-8kjxbpw4.js +8 -0
- package/dist/cli/main-90ds1z4e.js +10 -0
- package/dist/cli/main-9etavkew.js +3 -0
- package/dist/cli/main-a9njrkk1.js +3 -0
- package/dist/cli/main-aecrjq2d.js +12 -0
- package/dist/cli/main-ck9asesq.js +9 -0
- package/dist/cli/main-cta9racd.js +4 -0
- package/dist/cli/main-ddv7j2ag.js +3 -0
- package/dist/cli/main-dfreez27.js +10 -0
- package/dist/cli/main-f7rw7des.js +3 -0
- package/dist/cli/main-ggcn7rd7.js +5 -0
- package/dist/cli/main-gzkmycnv.js +3 -0
- package/dist/cli/main-hq51jg8v.js +18 -0
- package/dist/cli/main-jft389w9.js +8 -0
- package/dist/cli/main-k1eqkg83.js +3 -0
- package/dist/cli/main-k2y8a2aw.js +9 -0
- package/dist/cli/main-kcpbykxz.js +4 -0
- package/dist/cli/main-kd488vje.js +22 -0
- package/dist/cli/main-kh32yvgk.js +5 -0
- package/dist/cli/main-kqxnqjnv.js +25 -0
- package/dist/cli/main-kyn0xnsg.js +3 -0
- package/dist/cli/main-m1kk6fp5.js +21 -0
- package/dist/cli/main-mv40pcr2.js +4 -0
- package/dist/cli/main-n0t3973w.js +3 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-pknhvrmj.js +3 -0
- package/dist/cli/main-pn1w7a7j.js +3 -0
- package/dist/cli/main-prxxs70n.js +4 -0
- package/dist/cli/main-q3vsesf9.js +3 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-rdgdw24b.js +25 -0
- package/dist/cli/main-rfth4tbm.js +16 -0
- package/dist/cli/main-rg0wn0xf.js +5 -0
- package/dist/cli/main-sdmxhtv8.js +4 -0
- package/dist/cli/main-skbp13js.js +18 -0
- package/dist/cli/main-t4xnd213.js +7 -0
- package/dist/cli/main-vqak588n.js +4 -0
- package/dist/cli/main-w2n1303f.js +9 -0
- package/dist/cli/main-wbrdspr2.js +5 -0
- package/dist/cli/main-wsrg79c1.js +7 -0
- package/dist/cli/main-x4r0fne4.js +5 -0
- package/dist/cli/main-xea2f3tn.js +6 -0
- package/dist/cli/main-xg704a3c.js +3 -0
- package/dist/cli/main-xvnrabfp.js +16 -0
- package/dist/cli/main-xy53xf0r.js +4 -0
- package/dist/cli/main-y1fqy60y.js +3 -0
- package/dist/cli/main-yn8cd281.js +34 -0
- package/dist/cli/main-yr0ksc0h.js +4 -0
- package/dist/cli/main-z2ex2vyf.js +4 -0
- package/dist/cli/main-z3aayzvq.js +3 -0
- package/dist/cli/main-zaqh35jg.js +3 -0
- package/dist/cli/main-zc2e8e46.js +4 -0
- package/dist/cli/main-zzrfw6cf.js +13 -0
- package/dist/cli/main.js +280 -0
- package/dist/cli/market-cmd-e14kmx9n.js +5 -0
- package/dist/cli/mcp-login-wq7ktdek.js +2 -0
- package/dist/cli/mcp-market-cmd-9mg3jecy.js +2 -0
- package/dist/cli/notify-b7qc0cjb.js +2 -0
- package/dist/cli/oauth-z8whcgfx.js +2 -0
- package/dist/cli/output-b3ewj3ps.js +16 -0
- package/dist/cli/profiles-6mr5he5e.js +2 -0
- package/dist/cli/provider-config-g7j42q8x.js +2 -0
- package/dist/cli/provider-jr1y8vvm.js +2 -0
- package/dist/cli/registry-s8yk86g0.js +2 -0
- package/dist/cli/registry-t6p8d4mn.js +2 -0
- package/dist/cli/repl-bajwe1mh.js +11 -0
- package/dist/cli/resume-rwn9nz7y.js +2 -0
- package/dist/cli/run-flags-nah7ndpt.js +2 -0
- package/dist/cli/runtime-n7gafzhb.js +2 -0
- package/dist/cli/sandbox-config-emdy18x4.js +2 -0
- package/dist/cli/server-b0nvs2bn.js +5 -0
- package/dist/cli/session-arg-y75wd4kj.js +2 -0
- package/dist/cli/session-j62evmjq.js +2 -0
- package/dist/cli/sessions-cmd-tsnwz0ns.js +7 -0
- package/dist/cli/settings-df10wfez.js +2 -0
- package/dist/cli/setup-jzvv72fg.js +2 -0
- package/dist/cli/sextant-smoke-37m81ke6.js +5 -0
- package/dist/cli/skills-cmd-gjxnxnhx.js +2 -0
- package/dist/cli/smoke-p7748apt.js +8 -0
- package/dist/cli/start-chat-s4st3mm0.js +12 -0
- package/dist/cli/stream-gmeyewds.js +2 -0
- package/dist/cli/task-gh0kkp3n.js +2 -0
- package/dist/cli/tasks-z1kfpe8e.js +2 -0
- package/dist/cli/thinking-0eqkrz6t.js +2 -0
- package/dist/cli/todo-5brcrt9m.js +2 -0
- package/dist/cli/tools-7pzm0vj9.js +2 -0
- package/dist/cli/tools-s635p6s8.js +2 -0
- package/dist/cli/trust-cmd-cjav8zgm.js +2 -0
- package/dist/cli/update-check-pt31bm2f.js +2 -0
- package/dist/cli/update-cmd-tk131s9t.js +2 -0
- package/dist/cli/voice-56nabd8d.js +2 -0
- package/dist/cli/webfetch-xd8q596m.js +2 -0
- package/dist/cli/websearch-5hkf98k1.js +2 -0
- package/dist/cli/workflow-cmd-cy3cvzjp.js +4 -0
- package/dist/cli/workspace-q10g5z3e.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +55 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/lib/sdk.js +360 -0
- package/package.json +84 -72
- package/bin/rovecode.ts +0 -21
- package/src/acp/server.ts +0 -374
- package/src/cli/auth-login.ts +0 -122
- package/src/cli/connect.ts +0 -244
- package/src/cli/context-cmd.ts +0 -199
- package/src/cli/dispatch.ts +0 -82
- package/src/cli/doctor.ts +0 -362
- package/src/cli/export.ts +0 -276
- package/src/cli/help.ts +0 -293
- package/src/cli/is-tui-invocation.ts +0 -8
- package/src/cli/main.ts +0 -583
- package/src/cli/market-cmd.ts +0 -658
- package/src/cli/mcp-login.ts +0 -141
- package/src/cli/mcp-market-cmd.ts +0 -302
- package/src/cli/output.ts +0 -382
- package/src/cli/repl.ts +0 -250
- package/src/cli/repomap-root.ts +0 -14
- package/src/cli/resume.ts +0 -57
- package/src/cli/run-flags.ts +0 -43
- package/src/cli/run-limits.ts +0 -78
- package/src/cli/runtime.ts +0 -931
- package/src/cli/session-arg.ts +0 -30
- package/src/cli/sessions-cmd.ts +0 -145
- package/src/cli/setup.ts +0 -153
- package/src/cli/skills-cmd.ts +0 -194
- package/src/cli/start-chat.ts +0 -65
- package/src/cli/trust-cmd.ts +0 -52
- package/src/coding/bash.ts +0 -148
- package/src/coding/checkpoints.ts +0 -327
- package/src/coding/diff.ts +0 -138
- package/src/coding/files.ts +0 -341
- package/src/coding/hashline.ts +0 -274
- package/src/coding/lsp-gate.ts +0 -254
- package/src/coding/lsp-servers.ts +0 -147
- package/src/coding/lsp.ts +0 -283
- package/src/coding/repomap-cache.ts +0 -99
- package/src/coding/repomap-files.ts +0 -192
- package/src/coding/repomap.ts +0 -481
- package/src/core/agents.ts +0 -255
- package/src/core/compaction.ts +0 -259
- package/src/core/config.ts +0 -289
- package/src/core/context-report.ts +0 -228
- package/src/core/context.ts +0 -60
- package/src/core/count-remote.ts +0 -107
- package/src/core/execpolicy-rules.ts +0 -196
- package/src/core/execpolicy.ts +0 -385
- package/src/core/executor.ts +0 -454
- package/src/core/guardrails.ts +0 -400
- package/src/core/hooks.ts +0 -411
- package/src/core/images.ts +0 -230
- package/src/core/intro.ts +0 -266
- package/src/core/loop.ts +0 -567
- package/src/core/modes.ts +0 -372
- package/src/core/orchestrator.ts +0 -245
- package/src/core/proc-group.ts +0 -48
- package/src/core/project-trust.ts +0 -98
- package/src/core/reflection.ts +0 -165
- package/src/core/sandbox-config.ts +0 -186
- package/src/core/session-id.ts +0 -24
- package/src/core/session-images.ts +0 -73
- package/src/core/session-ops.ts +0 -183
- package/src/core/session-text.ts +0 -29
- package/src/core/session.ts +0 -469
- package/src/core/settings.ts +0 -170
- package/src/core/tasks.ts +0 -646
- package/src/core/token-scale.ts +0 -108
- package/src/core/tools.ts +0 -309
- package/src/core/trust.ts +0 -104
- package/src/core/types.ts +0 -330
- package/src/core/update-check.ts +0 -171
- package/src/core/usage.ts +0 -204
- package/src/core/validate.ts +0 -121
- package/src/core/verify-gate.ts +0 -159
- package/src/core/verify.ts +0 -236
- package/src/core/voice.ts +0 -158
- package/src/core/win-job.ts +0 -183
- package/src/core/workspace.ts +0 -184
- package/src/design/audit.ts +0 -797
- package/src/design/direction.ts +0 -190
- package/src/design/rules.ts +0 -157
- package/src/eval/bench.ts +0 -150
- package/src/eval/gauntlet-runner.ts +0 -215
- package/src/eval/gauntlet-support.ts +0 -84
- package/src/eval/gauntlet-wave3.ts +0 -269
- package/src/eval/gauntlet-wave4.ts +0 -217
- package/src/eval/gauntlet.ts +0 -253
- package/src/index.ts +0 -17
- package/src/lanes/agy.ts +0 -95
- package/src/lanes/approval.ts +0 -24
- package/src/lanes/claude.ts +0 -129
- package/src/lanes/codex.ts +0 -127
- package/src/lanes/events.ts +0 -130
- package/src/lanes/job.ts +0 -142
- package/src/lanes/opencode.ts +0 -122
- package/src/lanes/process.ts +0 -184
- package/src/lanes/progress.ts +0 -183
- package/src/lanes/registry.ts +0 -178
- package/src/lanes/runner.ts +0 -124
- package/src/lanes/types.ts +0 -112
- package/src/market/catalogs/mcp-docs.json +0 -111
- package/src/market/catalogs/plugins.json +0 -111
- package/src/market/catalogs/skills.json +0 -478
- package/src/market/clone.ts +0 -72
- package/src/market/context-cost.ts +0 -121
- package/src/market/digest.ts +0 -106
- package/src/market/index.ts +0 -22
- package/src/market/install.ts +0 -578
- package/src/market/manifest.ts +0 -187
- package/src/market/prereq.ts +0 -145
- package/src/market/registry.ts +0 -363
- package/src/market/resolve.ts +0 -111
- package/src/market/types.ts +0 -236
- package/src/market/validate.ts +0 -227
- package/src/mcp/client.ts +0 -449
- package/src/mcp/config.ts +0 -252
- package/src/mcp/local-package.ts +0 -211
- package/src/mcp/market-catalog.ts +0 -84
- package/src/mcp/market-install.ts +0 -289
- package/src/mcp/market.ts +0 -362
- package/src/mcp/oauth.ts +0 -251
- package/src/mcp/prompts-resources.ts +0 -249
- package/src/mcp/shared.ts +0 -149
- package/src/mcp/status.ts +0 -67
- package/src/mcp/tools.ts +0 -275
- package/src/mcp/transport.ts +0 -122
- package/src/mcp/trust.ts +0 -25
- package/src/memory/blocks.ts +0 -278
- package/src/memory/recall.ts +0 -355
- package/src/memory/scope.ts +0 -182
- package/src/memory/store.ts +0 -105
- package/src/memory/tools.ts +0 -99
- package/src/plugins/cli.ts +0 -119
- package/src/plugins/discover.ts +0 -108
- package/src/plugins/index.ts +0 -50
- package/src/plugins/install.ts +0 -184
- package/src/plugins/load.ts +0 -124
- package/src/plugins/manifest.ts +0 -92
- package/src/plugins/state.ts +0 -83
- package/src/providers/auth.ts +0 -408
- package/src/providers/cache.ts +0 -223
- package/src/providers/catalog-local.ts +0 -160
- package/src/providers/catalog.ts +0 -421
- package/src/providers/middleware-context.ts +0 -86
- package/src/providers/middleware.ts +0 -373
- package/src/providers/model-list.ts +0 -23
- package/src/providers/oauth/common.ts +0 -105
- package/src/providers/oauth/device-code.ts +0 -107
- package/src/providers/oauth/github-copilot.ts +0 -146
- package/src/providers/oauth/loopback.ts +0 -158
- package/src/providers/oauth/openai.ts +0 -163
- package/src/providers/oauth/openrouter.ts +0 -89
- package/src/providers/oauth/pkce.ts +0 -45
- package/src/providers/oauth/registry.ts +0 -39
- package/src/providers/oauth/seam.ts +0 -89
- package/src/providers/profile-glm53.ts +0 -111
- package/src/providers/profile-sonnet5-persona.ts +0 -65
- package/src/providers/profile-sonnet5-voice.ts +0 -23
- package/src/providers/profiles.ts +0 -156
- package/src/providers/provider-config.ts +0 -311
- package/src/providers/registry.ts +0 -333
- package/src/providers/responses.ts +0 -209
- package/src/providers/retry.ts +0 -234
- package/src/providers/router.ts +0 -294
- package/src/providers/sse.ts +0 -26
- package/src/providers/stream-errors.ts +0 -117
- package/src/providers/stream.ts +0 -566
- package/src/providers/thinking.ts +0 -189
- package/src/providers/wire-messages.ts +0 -129
- package/src/providers/wire-responses.ts +0 -79
- package/src/providers/wire-select.ts +0 -53
- package/src/server/http.ts +0 -291
- package/src/server/openapi.ts +0 -246
- package/src/sextant/card-hits.ts +0 -102
- package/src/sextant/card-keys.ts +0 -55
- package/src/sextant/context-source.ts +0 -157
- package/src/sextant/crew-cards.ts +0 -350
- package/src/sextant/draw-agents.ts +0 -273
- package/src/sextant/draw-code.ts +0 -388
- package/src/sextant/draw-context.ts +0 -222
- package/src/sextant/draw-frame.ts +0 -164
- package/src/sextant/draw-market.ts +0 -573
- package/src/sextant/draw-messages.ts +0 -386
- package/src/sextant/draw-pet.ts +0 -230
- package/src/sextant/draw-plan.ts +0 -187
- package/src/sextant/draw-tabs.ts +0 -85
- package/src/sextant/draw-util.ts +0 -65
- package/src/sextant/draw-wizard.ts +0 -378
- package/src/sextant/engine.ts +0 -230
- package/src/sextant/frame-hits.ts +0 -25
- package/src/sextant/frame.ts +0 -101
- package/src/sextant/git-status.ts +0 -197
- package/src/sextant/grid.ts +0 -59
- package/src/sextant/input.ts +0 -119
- package/src/sextant/keys.ts +0 -521
- package/src/sextant/layout.ts +0 -86
- package/src/sextant/local-commands.ts +0 -169
- package/src/sextant/market-source.ts +0 -287
- package/src/sextant/mentions.ts +0 -200
- package/src/sextant/message-hits.ts +0 -26
- package/src/sextant/model.ts +0 -387
- package/src/sextant/overlays.ts +0 -456
- package/src/sextant/panel-hits.ts +0 -38
- package/src/sextant/pet.ts +0 -399
- package/src/sextant/screen.ts +0 -324
- package/src/sextant/scroll-hits.ts +0 -66
- package/src/sextant/scrollbar.ts +0 -82
- package/src/sextant/sextant-bridge.ts +0 -174
- package/src/sextant/sextant-cards.ts +0 -142
- package/src/sextant/sextant-diff-base.ts +0 -63
- package/src/sextant/sextant-files.ts +0 -154
- package/src/sextant/sextant-frame-loop.ts +0 -335
- package/src/sextant/sextant-renderer.ts +0 -574
- package/src/sextant/sextant-repo.ts +0 -140
- package/src/sextant/theme.ts +0 -66
- package/src/sextant/tool-rows.ts +0 -189
- package/src/sextant/types.ts +0 -493
- package/src/skills/index.ts +0 -387
- package/src/skills/pack.ts +0 -220
- package/src/skills/spec.ts +0 -162
- package/src/skills/tools.ts +0 -69
- package/src/skills/versioned.ts +0 -227
- package/src/telemetry/otel-export.ts +0 -122
- package/src/telemetry/otel-lanes.ts +0 -89
- package/src/telemetry/otel-logs.ts +0 -131
- package/src/telemetry/otel-metrics.ts +0 -136
- package/src/telemetry/otel.ts +0 -397
- package/src/telemetry/otlp.ts +0 -76
- package/src/tools/ask-user.ts +0 -156
- package/src/tools/bash-bg.ts +0 -94
- package/src/tools/bash-jobs.ts +0 -237
- package/src/tools/design.ts +0 -151
- package/src/tools/evalcell.ts +0 -338
- package/src/tools/html-text.ts +0 -139
- package/src/tools/provider.ts +0 -149
- package/src/tools/task.ts +0 -250
- package/src/tools/todo.ts +0 -320
- package/src/tools/webfetch.ts +0 -332
- package/src/tools/websearch.ts +0 -359
- package/src/tui/agents-cmd.ts +0 -41
- package/src/tui/app.ts +0 -749
- package/src/tui/attach.ts +0 -127
- package/src/tui/boot-notes.ts +0 -41
- package/src/tui/builtin-prompts.ts +0 -59
- package/src/tui/checkpoints-cmd.ts +0 -70
- package/src/tui/clipboard-image.ts +0 -81
- package/src/tui/clipboard.ts +0 -78
- package/src/tui/commands.ts +0 -283
- package/src/tui/config-view.ts +0 -53
- package/src/tui/context-cmds.ts +0 -282
- package/src/tui/cost.ts +0 -108
- package/src/tui/crash-guard.ts +0 -173
- package/src/tui/focus-terminal.ts +0 -34
- package/src/tui/git-cmds.ts +0 -273
- package/src/tui/git-plain.ts +0 -58
- package/src/tui/info-cmd.ts +0 -150
- package/src/tui/input-plain.ts +0 -76
- package/src/tui/mcp-cmd.ts +0 -128
- package/src/tui/memory-note.ts +0 -77
- package/src/tui/modes-cmd.ts +0 -45
- package/src/tui/notify-seq.ts +0 -100
- package/src/tui/notify.ts +0 -318
- package/src/tui/overlays.ts +0 -97
- package/src/tui/pi-renderer.ts +0 -428
- package/src/tui/providers-cmd.ts +0 -377
- package/src/tui/reasoning-view.ts +0 -56
- package/src/tui/renderer.ts +0 -128
- package/src/tui/replay-marker.ts +0 -29
- package/src/tui/session-cmd.ts +0 -148
- package/src/tui/session-manage.ts +0 -95
- package/src/tui/sextant-attach.ts +0 -102
- package/src/tui/sextant-io.ts +0 -202
- package/src/tui/sextant-smoke.ts +0 -110
- package/src/tui/shell-cmd.ts +0 -158
- package/src/tui/smoke.ts +0 -72
- package/src/tui/staged-terminal.ts +0 -50
- package/src/tui/startup.ts +0 -12
- package/src/tui/theme.ts +0 -59
- package/src/tui/todo-label.ts +0 -7
- package/src/tui/trust-card.ts +0 -107
- package/src/tui/tui-commands.ts +0 -87
- package/tsconfig.json +0 -30
- package/vendor/pi-tui/LICENSE +0 -21
- package/vendor/pi-tui/PATCHES.md +0 -12
- package/vendor/pi-tui/PROVENANCE.md +0 -12
- package/vendor/pi-tui/README.upstream.md +0 -854
- package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
- package/vendor/pi-tui/src/autocomplete.ts +0 -827
- package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
- package/vendor/pi-tui/src/components/box.ts +0 -138
- package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
- package/vendor/pi-tui/src/components/editor.ts +0 -2364
- package/vendor/pi-tui/src/components/h-stack.ts +0 -45
- package/vendor/pi-tui/src/components/image.ts +0 -128
- package/vendor/pi-tui/src/components/input.ts +0 -448
- package/vendor/pi-tui/src/components/loader.ts +0 -93
- package/vendor/pi-tui/src/components/markdown.ts +0 -1016
- package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
- package/vendor/pi-tui/src/components/select-list.ts +0 -230
- package/vendor/pi-tui/src/components/settings-list.ts +0 -277
- package/vendor/pi-tui/src/components/spacer.ts +0 -29
- package/vendor/pi-tui/src/components/stack.ts +0 -155
- package/vendor/pi-tui/src/components/text.ts +0 -108
- package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
- package/vendor/pi-tui/src/components/v-stack.ts +0 -34
- package/vendor/pi-tui/src/editor-component.ts +0 -75
- package/vendor/pi-tui/src/fuzzy.ts +0 -138
- package/vendor/pi-tui/src/index.ts +0 -149
- package/vendor/pi-tui/src/keybindings.ts +0 -321
- package/vendor/pi-tui/src/keys.ts +0 -1402
- package/vendor/pi-tui/src/kill-ring.ts +0 -47
- package/vendor/pi-tui/src/latex.ts +0 -1381
- package/vendor/pi-tui/src/layout-node.ts +0 -52
- package/vendor/pi-tui/src/layout.ts +0 -411
- package/vendor/pi-tui/src/native-modifiers.ts +0 -60
- package/vendor/pi-tui/src/native-module-path.ts +0 -32
- package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
- package/vendor/pi-tui/src/terminal-colors.ts +0 -74
- package/vendor/pi-tui/src/terminal-image.ts +0 -701
- package/vendor/pi-tui/src/terminal.ts +0 -554
- package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
- package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
- package/vendor/pi-tui/src/tui.ts +0 -1264
- package/vendor/pi-tui/src/undo-stack.ts +0 -29
- package/vendor/pi-tui/src/utils.ts +0 -1327
- package/vendor/pi-tui/src/word-navigation.ts +0 -118
- package/vendor/pi-tui/test/test-themes.ts +0 -39
- package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
- /package/{src/providers → dist/cli}/models-index.json +0 -0
package/src/tui/git-plain.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/** `/commit` and `/undo` for `rovecode --plain` (port #65): the readline REPL has no Renderer, so this adapter maps
|
|
2
|
-
* the handful of Renderer calls git-cmds.ts makes onto console lines and the REPL's own y/n question — the SAME
|
|
3
|
-
* cmdCommit / cmdUndo run (one implementation, ADR-002), the same bash-tool dispatch and approval chain. A denied
|
|
4
|
-
* card cannot prefill a readline prompt, so the draft is printed for copy-and-resubmit instead. */
|
|
5
|
-
|
|
6
|
-
import type { Runtime } from "../cli/runtime.ts";
|
|
7
|
-
import { cmdCommit, cmdUndo, type GitCmdCtx } from "./git-cmds.ts";
|
|
8
|
-
import type { ApprovalAnswer, AssistantView, Renderer, RendererHooks, StatusInfo } from "./renderer.ts";
|
|
9
|
-
import type { ApprovalFn } from "../core/types.ts";
|
|
10
|
-
|
|
11
|
-
export interface PlainGitDeps {
|
|
12
|
-
rt: GitCmdCtx["rt"] & Pick<Runtime, "store">;
|
|
13
|
-
/** the REPL's own approver (its y/n asker) — what a card would have asked, asked on the line */
|
|
14
|
-
approve: ApprovalFn | undefined;
|
|
15
|
-
yolo: boolean;
|
|
16
|
-
/** the REPL's readline question (answers arrive trimmed + lowercased) */
|
|
17
|
-
ask(question: string): Promise<string>;
|
|
18
|
-
/** console.log by default; tests capture */
|
|
19
|
-
out?: (line: string) => void;
|
|
20
|
-
/** Share the REPL's busy/interrupt state with shell and model turns. */
|
|
21
|
-
busy?: () => boolean;
|
|
22
|
-
bindAbort?: (ac: AbortController | null) => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** the console Renderer: notes, tool card lines, the approval question; everything else inert */
|
|
26
|
-
export function plainRenderer(ask: PlainGitDeps["ask"], out: (line: string) => void): Renderer {
|
|
27
|
-
const oneLine = (s: string): string => s.replace(/\n/g, " ⏎ ");
|
|
28
|
-
return {
|
|
29
|
-
start(_h: RendererHooks): void {}, stop(): void {}, setCommands(): void {},
|
|
30
|
-
addUser(text: string): void { out(text); },
|
|
31
|
-
addSystemNote(text: string, tone: "info" | "warn" | "error" = "info"): void { out(` ${tone === "info" ? "" : `${tone}: `}${text}`); },
|
|
32
|
-
beginAssistant(): AssistantView { return { append() {}, done() {} }; },
|
|
33
|
-
toolStart(_id: string, tool: string, args: string): void { out(`\n → ${tool} ${args}`); },
|
|
34
|
-
toolUpdate(): void {},
|
|
35
|
-
toolEnd(_id: string, ok: boolean, preview: string): void { out(` ← ${ok ? "ok" : "FAIL"} ${oneLine(preview)}`); },
|
|
36
|
-
async askApproval(tool: string, argsPreview: string, detail?: string): Promise<ApprovalAnswer> {
|
|
37
|
-
out(`\n approval needed: ${tool} ${argsPreview}`);
|
|
38
|
-
if (detail) for (const l of detail.split("\n")) out(` ${l}`);
|
|
39
|
-
const a = await ask(" allow? [y]es / [n]o: ");
|
|
40
|
-
return a === "y" || a === "yes" ? "once" : "deny";
|
|
41
|
-
},
|
|
42
|
-
async askQuestion(): Promise<null> { return null; },
|
|
43
|
-
async pickOne(): Promise<null> { return null; },
|
|
44
|
-
clearTranscript(): void {},
|
|
45
|
-
prefillEditor(text: string): void { out(` resubmit with: ${oneLine(text)}`); }, // no editor to prefill in --plain
|
|
46
|
-
setBusy(_b: boolean, _label?: string): void {},
|
|
47
|
-
setStatus(_i: StatusInfo): void {},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** `/commit [message]` or `/undo` typed at the `--plain` prompt (the REPL gates a busy run before calling this) */
|
|
52
|
-
export async function runPlainGitCommand(deps: PlainGitDeps, text: string): Promise<void> {
|
|
53
|
-
const renderer = plainRenderer(deps.ask, deps.out ?? console.log);
|
|
54
|
-
const ctx: GitCmdCtx = { renderer, rt: deps.rt, store: () => deps.rt.store, approve: () => deps.approve, yolo: () => deps.yolo, busy: deps.busy ?? (() => false), setBusy(): void {}, bindAbort: deps.bindAbort ?? (() => {}) };
|
|
55
|
-
const line = text.trim();
|
|
56
|
-
if (line === "/undo") await cmdUndo(ctx);
|
|
57
|
-
else if (line === "/commit" || line.startsWith("/commit ")) await cmdCommit(ctx, line.slice("/commit".length));
|
|
58
|
-
}
|
package/src/tui/info-cmd.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/** TUI info commands, extracted from app.ts for the ADR-002 cap (wiring pass): /help /status
|
|
2
|
-
* /cost /skills /memory /export read state and print a note; /todos (port #32) and /tasks
|
|
3
|
-
* (port #26) surface the agent-maintained lists. Nothing here moves the session leaf — the
|
|
4
|
-
* CALLER owns the swappable store/blocks and the status slice, so every read runs through
|
|
5
|
-
* the injected ctx (same shape as session-cmd / checkpoints-cmd). */
|
|
6
|
-
|
|
7
|
-
import type { Runtime } from "../cli/runtime.ts";
|
|
8
|
-
import { exportSession } from "../cli/export.ts";
|
|
9
|
-
import { describeSandbox } from "../core/sandbox-config.ts";
|
|
10
|
-
import type { SessionStore } from "../core/session.ts";
|
|
11
|
-
import { formatTaskList, isTerminal } from "../core/tasks.ts";
|
|
12
|
-
import type { BlockStore } from "../memory/blocks.ts";
|
|
13
|
-
import type { ModelCatalog } from "../providers/catalog.ts";
|
|
14
|
-
import { loadTodos, renderTodos, todoStatusLabel } from "../tools/todo.ts";
|
|
15
|
-
import { helpForCommands, type CustomCommand } from "./commands.ts";
|
|
16
|
-
import { memoryCommand } from "./memory-note.ts";
|
|
17
|
-
import { buildCostNote } from "./cost.ts";
|
|
18
|
-
import type { Renderer, SlashCommand } from "./renderer.ts";
|
|
19
|
-
import { join } from "node:path";
|
|
20
|
-
|
|
21
|
-
export interface InfoStateSlice { provider: string; model: string; turns: number; tokensIn: number; tokensOut: number }
|
|
22
|
-
|
|
23
|
-
export interface InfoCmdCtx {
|
|
24
|
-
renderer: Renderer;
|
|
25
|
-
/** the runtime slices the info commands read: cwd, config provenance, sandbox rung, skills, tasks */
|
|
26
|
-
rt: Pick<Runtime, "cwd" | "projectContext" | "sandbox" | "skillStore" | "tasks">;
|
|
27
|
-
/** <cwd>/.rovecode/sessions — todos.json lives under <sessionsDir>/<session id> */
|
|
28
|
-
sessionsDir: string;
|
|
29
|
-
/** the ACTIVE session store, read live (/sessions and a root /rewind swap it) */
|
|
30
|
-
store(): SessionStore;
|
|
31
|
-
/** the ACTIVE memory block store, read live (swapped with the session) */
|
|
32
|
-
blocks(): BlockStore;
|
|
33
|
-
/** provider/model/turns/tokens exactly as the status line shows them */
|
|
34
|
-
state: InfoStateSlice;
|
|
35
|
-
commands: { builtin: readonly SlashCommand[]; custom: readonly CustomCommand[] };
|
|
36
|
-
/** /cost pricing + context window (offline snapshot; `/cost refresh` is the TUI's one fetch) */
|
|
37
|
-
catalog: ModelCatalog;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** /help topics in display order; a command with an unknown or missing group lands under "more" at the end */
|
|
41
|
-
export const HELP_GROUP_ORDER: readonly string[] = ["start here", "session", "model & provider", "modes & safety", "files & history", "info"];
|
|
42
|
-
|
|
43
|
-
/** /help — the built-ins grouped by topic (SlashCommand.group), one plain line each, then the "custom:" tail (port #30). */
|
|
44
|
-
export function cmdHelp(ctx: InfoCmdCtx): void {
|
|
45
|
-
ctx.renderer.addSystemNote(groupedHelp(ctx.commands.builtin) + helpForCommands(ctx.commands.custom));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** pure: the grouped body /help prints (tests read it without a renderer) */
|
|
49
|
-
export function groupedHelp(builtin: readonly SlashCommand[]): string {
|
|
50
|
-
const groups = new Map<string, SlashCommand[]>();
|
|
51
|
-
for (const c of builtin) { const g = c.group !== undefined && HELP_GROUP_ORDER.includes(c.group) ? c.group : "more"; const l = groups.get(g) ?? []; l.push(c); groups.set(g, l); }
|
|
52
|
-
const order = [...HELP_GROUP_ORDER, "more"].filter((g) => groups.has(g));
|
|
53
|
-
return order.map((g) => `${g}\n` + groups.get(g)!.map((c) => ` /${c.name} — ${c.description}`).join("\n")).join("\n");
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** /status — provider/model/turns/tokens, the active executor rung + its origin (port #27), and
|
|
57
|
-
* config provenance (port #8 HIGH-2: dropped/truncated sources must be visible). */
|
|
58
|
-
export function cmdStatus(ctx: InfoCmdCtx): void {
|
|
59
|
-
const pc = ctx.rt.projectContext;
|
|
60
|
-
const cfgBits = pc.sources.map((s) => s.chars === 0 ? `${s.path} (dropped)` : s.truncated ? `${s.path} (truncated)` : s.path);
|
|
61
|
-
if (pc.skippedFiles > 0) cfgBits.push(`+${pc.skippedFiles} skipped (file cap)`);
|
|
62
|
-
const s = ctx.state;
|
|
63
|
-
ctx.renderer.addSystemNote(
|
|
64
|
-
`provider=${s.provider} model=${s.model} turns=${s.turns} tokens=${s.tokensIn}in/${s.tokensOut}out` +
|
|
65
|
-
`\nsandbox: ${describeSandbox(ctx.rt.sandbox)}` +
|
|
66
|
-
`\nconfig: ${cfgBits.length > 0 ? cfgBits.join(", ") : "(none)"}`,
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** /cost [refresh] — ports #5+#6: normalized usage (incl. cache traffic) priced per message at its
|
|
71
|
-
* origin model; `refresh` re-fetches models.dev pricing (24h disk cache) — the live half is
|
|
72
|
-
* user-invoked only, so the TUI stays network-free unless asked. */
|
|
73
|
-
export function cmdCost(ctx: InfoCmdCtx, arg: string): void {
|
|
74
|
-
if (arg === "refresh") {
|
|
75
|
-
void ctx.catalog.refresh().then((ok) => ctx.renderer.addSystemNote(
|
|
76
|
-
ok ? "model catalog refreshed from models.dev" : "catalog refresh failed — using the offline snapshot",
|
|
77
|
-
ok ? "info" : "warn",
|
|
78
|
-
));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
ctx.renderer.addSystemNote(buildCostNote(ctx.store().messages(), ctx.catalog, { provider: ctx.state.provider, model: ctx.state.model }));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** /skills — installed skills, one `name — description` row each. */
|
|
85
|
-
export function cmdSkills(ctx: InfoCmdCtx): void {
|
|
86
|
-
const rows = ctx.rt.skillStore.list().map((s) => `${s.name} — ${s.description}`);
|
|
87
|
-
ctx.renderer.addSystemNote(rows.length ? rows.join("\n") : "(no skills installed)");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** `/memory` — both blocks as the store holds them NOW, each with the file it lives in; `/memory --user` the USER
|
|
91
|
-
* block alone; `/memory <text>` appends to MEMORY and `/memory --user <text>` to USER (tui/memory-note.ts, the
|
|
92
|
-
* same append path as `#<text>` and the memory_edit tool). It shows the live text rather than renderForPrompt()
|
|
93
|
-
* so an edit made this run is visible immediately, with a hint that the prompt picks it up next run — and it
|
|
94
|
-
* names the paths, because the bug this replaced was a note landing somewhere the person did not mean. */
|
|
95
|
-
export function cmdMemory(ctx: InfoCmdCtx, arg = ""): void {
|
|
96
|
-
const r = memoryCommand(ctx.blocks(), arg);
|
|
97
|
-
ctx.renderer.addSystemNote(r.text, r.tone);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** /export [--json] [path] [--force] — port #38: write THIS session as markdown (raw JSONL with
|
|
101
|
-
* --json), local only. Read-only over the store (exportSession re-reads from disk), so no busy
|
|
102
|
-
* gate; a spaced path stays whole (every non-flag word joins the path). */
|
|
103
|
-
export function cmdExport(ctx: InfoCmdCtx, arg: string): void {
|
|
104
|
-
try {
|
|
105
|
-
const words = arg.split(/\s+/).filter(Boolean);
|
|
106
|
-
const res = exportSession(ctx.sessionsDir, ctx.store().id, {
|
|
107
|
-
json: words.includes("--json"), force: words.includes("--force"),
|
|
108
|
-
out: words.filter((w) => !w.startsWith("-")).join(" ") || undefined, cwd: ctx.rt.cwd,
|
|
109
|
-
});
|
|
110
|
-
ctx.renderer.addSystemNote(`exported ${res.format} → ${res.path}`);
|
|
111
|
-
} catch (e) {
|
|
112
|
-
ctx.renderer.addSystemNote(e instanceof Error ? e.message : String(e), "error");
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/** /todos — port #32: the agent-maintained list at <session>/todos.json as checkbox rows. A
|
|
117
|
-
* corrupt file is a warning above the (empty) list, never a throw (loadTodos contract). */
|
|
118
|
-
export function cmdTodos(ctx: InfoCmdCtx): void {
|
|
119
|
-
const { items, note } = loadTodos(join(ctx.sessionsDir, ctx.store().id));
|
|
120
|
-
if (note) ctx.renderer.addSystemNote(note, "warn");
|
|
121
|
-
ctx.renderer.addSystemNote(items.length > 0 ? renderTodos(items) : "(no todos — the agent maintains the list with todo_write)");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Status-bar label for the session's list ("todos 1/3"); undefined when empty, so the app omits
|
|
125
|
-
* the key and plain surfaces never see a blank segment. */
|
|
126
|
-
export function todoLabel(sessionDir: string): string | undefined {
|
|
127
|
-
return todoStatusLabel(loadTodos(sessionDir).items) || undefined;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** /tasks [cancel <id>|cancel all] — port #26: the runtime TaskManager's snapshot (the same rows
|
|
131
|
-
* as the tool's `list` action). Cancel acknowledges the REQUEST here; the "cancelled" line itself
|
|
132
|
-
* arrives through the app's task subscription when the child run has actually settled (a queued
|
|
133
|
-
* task settles at once, a running one when its aborted run returns) — one terminal note per task. */
|
|
134
|
-
export function cmdTasks(ctx: InfoCmdCtx, arg: string): void {
|
|
135
|
-
const words = arg.split(/\s+/).filter(Boolean);
|
|
136
|
-
const tasks = ctx.rt.tasks;
|
|
137
|
-
if (words.length === 0) { ctx.renderer.addSystemNote(formatTaskList(tasks.list())); return; }
|
|
138
|
-
if (words[0] !== "cancel" || words.length !== 2) { ctx.renderer.addSystemNote("usage: /tasks [cancel <id>|cancel all]", "warn"); return; }
|
|
139
|
-
const id = words[1]!;
|
|
140
|
-
if (id === "all") {
|
|
141
|
-
const n = tasks.cancelAll();
|
|
142
|
-
ctx.renderer.addSystemNote(n === 0 ? "no queued or running tasks to cancel" : `cancelling ${n} task${n === 1 ? "" : "s"}`);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const before = tasks.status(id);
|
|
146
|
-
if (!before) { ctx.renderer.addSystemNote(`unknown task '${id}' — list with /tasks`, "warn"); return; }
|
|
147
|
-
if (isTerminal(before.status)) { ctx.renderer.addSystemNote(`task ${id} already ${before.status}`); return; }
|
|
148
|
-
tasks.cancel(id);
|
|
149
|
-
ctx.renderer.addSystemNote(`cancelling task ${id} (${before.label})`);
|
|
150
|
-
}
|
package/src/tui/input-plain.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/** Port #78: --plain shell/mention parity over rovecode's existing dispatch and hashline attachments.
|
|
2
|
-
* No second executor, classifier, policy or file reader. Unlike aion, rovecode's approval contract has
|
|
3
|
-
* once/always/deny (no persistent-save verdict), and mentions carry read-tool anchors, not XML file blocks.
|
|
4
|
-
* This console renderer shares the REPL's once/always/deny approval ladder; git confirmations stay yes/no. */
|
|
5
|
-
import { existsSync } from "node:fs";
|
|
6
|
-
import { resolve } from "node:path";
|
|
7
|
-
import type { Runtime } from "../cli/runtime.ts";
|
|
8
|
-
import type { ApprovalFn } from "../core/types.ts";
|
|
9
|
-
import { expandMentions } from "../sextant/mentions.ts";
|
|
10
|
-
import { cmdAttach, type AttachCtx } from "./attach.ts";
|
|
11
|
-
import type { Renderer } from "./renderer.ts";
|
|
12
|
-
import { runShellLine, type ShellCtx } from "./shell-cmd.ts";
|
|
13
|
-
|
|
14
|
-
export const PLAIN_APPROVAL_QUESTION = " allow? [y]es / [a]lways / [n]o: ";
|
|
15
|
-
type Ask = (question: string) => Promise<string>;
|
|
16
|
-
type Out = (line: string) => void;
|
|
17
|
-
const NL = String.fromCharCode(10);
|
|
18
|
-
|
|
19
|
-
/** The existing REPL ladder, shared by model calls and shell calls; always is session-only. */
|
|
20
|
-
export async function askPlainApproval(ask: Ask, tool: string, args: string, detail?: string, out: Out = console.log): ReturnType<ApprovalFn> {
|
|
21
|
-
out(`${NL} approval needed: ${tool} ${args}`);
|
|
22
|
-
if (detail) for (const line of detail.split(NL)) out(` ${line}`);
|
|
23
|
-
const answer = await ask(PLAIN_APPROVAL_QUESTION);
|
|
24
|
-
return answer === "a" ? "always" : answer === "n" || answer === "" ? "deny" : "once";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface PlainInputDeps {
|
|
28
|
-
rt: ShellCtx["rt"] & Pick<Runtime, "store" | "roots">;
|
|
29
|
-
yolo(): boolean;
|
|
30
|
-
busy(): boolean;
|
|
31
|
-
bindAbort(ac: AbortController | null): void;
|
|
32
|
-
ask: Ask;
|
|
33
|
-
modelRef(): { provider: string; model: string };
|
|
34
|
-
out?: Out;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Readline already echoed the typed input; only the tool cards and notes are printed here. */
|
|
38
|
-
export function plainInputRenderer(ask: Ask, out: Out): Renderer {
|
|
39
|
-
return {
|
|
40
|
-
start() {}, stop() {}, setCommands() {}, addUser() {},
|
|
41
|
-
addSystemNote(text, tone = "info") { out(` ${tone === "info" ? "" : `${tone}: `}${text}`); },
|
|
42
|
-
beginAssistant() { return { append() {}, done() {} }; },
|
|
43
|
-
toolStart(_id, tool, args) { out(`${NL} → ${tool} ${args}`); },
|
|
44
|
-
toolUpdate() {},
|
|
45
|
-
toolEnd(_id, ok, preview) { out(` ← ${ok ? "ok" : "FAIL"} ${preview.split(NL).join(" ⏎ ")}`); },
|
|
46
|
-
askApproval: (tool, args, detail) => askPlainApproval(ask, tool, args, detail, out),
|
|
47
|
-
async askQuestion() { return null; }, async pickOne() { return null; },
|
|
48
|
-
clearTranscript() {}, prefillEditor() {}, setBusy() {}, setStatus() {},
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export async function runPlainShellLine(deps: PlainInputDeps, line: string): Promise<void> {
|
|
53
|
-
const out = deps.out ?? console.log;
|
|
54
|
-
const ctx: ShellCtx = {
|
|
55
|
-
renderer: plainInputRenderer(deps.ask, out), rt: deps.rt, store: () => deps.rt.store,
|
|
56
|
-
level: () => deps.yolo() ? "auto" : "ask",
|
|
57
|
-
approve: () => deps.yolo() ? undefined : (req) => askPlainApproval(deps.ask, req.tool, JSON.stringify(req.revisedArgs).slice(0, 140), undefined, out),
|
|
58
|
-
busy: () => deps.busy(), setBusy() {}, bindAbort: (ac) => deps.bindAbort(ac),
|
|
59
|
-
};
|
|
60
|
-
await runShellLine(ctx, line);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** LAST transform before agentLoop: exact paths (like the classic renderer), added roots, existing
|
|
64
|
-
* containment/caps, valid edit anchors, and images staged on the same SessionStore through cmdAttach. */
|
|
65
|
-
export function expandPlainInput(deps: PlainInputDeps, text: string): string {
|
|
66
|
-
const renderer = plainInputRenderer(deps.ask, deps.out ?? console.log);
|
|
67
|
-
const attach: AttachCtx = { renderer, cwd: deps.rt.cwd, store: () => deps.rt.store, modelRef: () => deps.modelRef() };
|
|
68
|
-
const result = expandMentions(text, {
|
|
69
|
-
cwd: deps.rt.cwd, roots: deps.rt.roots.dirs,
|
|
70
|
-
resolve: (path) => existsSync(resolve(deps.rt.cwd, path)) ? path : null,
|
|
71
|
-
attachImage: (path) => cmdAttach(attach, path),
|
|
72
|
-
});
|
|
73
|
-
for (const note of result.notes) renderer.addSystemNote(note, note.includes(": attached as an image (") ? "info" : "warn");
|
|
74
|
-
if (result.attached.length > 0) renderer.addSystemNote(`attached: ${result.attached.map((file) => `${file.path} (${file.shown} lines${file.capped ? ", capped" : ""})`).join(" · ")}`);
|
|
75
|
-
return result.text;
|
|
76
|
-
}
|
package/src/tui/mcp-cmd.ts
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/** /mcp — the MCP market inside the TUI, through the two cards the surface already has. `/mcp [query]`
|
|
2
|
-
* opens the palette (Renderer.pickOne: the same box, keys and fuzzy filter as ⌃k) over the curated
|
|
3
|
-
* shelf plus the registry's matches; Enter on a row → if the server has several launch forms, one more
|
|
4
|
-
* pick; then the APPROVAL card (Renderer.askApproval) whose detail is the exact plan — command + args or
|
|
5
|
-
* URL, source, publisher, version, the env NAMES, the file — and only a yes writes. Kept out of app.ts
|
|
6
|
-
* (ADR-002 cap) like providers-cmd.ts.
|
|
7
|
-
*
|
|
8
|
-
* Secrets: the TUI has no masked input, so nothing is ever asked here. An install that wants a key is
|
|
9
|
-
* written with `${NAME}` (config.ts fills it from the environment at launch) and the closing note says
|
|
10
|
-
* which names to export — or to run `rovecode mcp add <name>` on a shell, where the prompt is masked. */
|
|
11
|
-
|
|
12
|
-
import type { Renderer, PickItem } from "./renderer.ts";
|
|
13
|
-
import { installLabel, searchMarket, type MarketDeps, type MarketEntry } from "../mcp/market.ts";
|
|
14
|
-
import { describePlan, fillPlan, namesWritten, planInstall, serverLine, writeServer, type McpScope } from "../mcp/market-install.ts";
|
|
15
|
-
import { parseConfigFile } from "../mcp/config.ts";
|
|
16
|
-
import { mcpTrustStatus, projectMcpFiles, trustMcpFile } from "../mcp/trust.ts";
|
|
17
|
-
import { installLocalPackage, localLaunch, npxPackage } from "../mcp/local-package.ts";
|
|
18
|
-
import { buildRecord, recordInstall } from "../market/manifest.ts";
|
|
19
|
-
import { rovecodeHome } from "../providers/auth.ts";
|
|
20
|
-
|
|
21
|
-
/** the subcommands the sextant offers after `/mcp ` (Enter completes the word, the name comes next) */
|
|
22
|
-
export const MCP_SUBCOMMANDS = ["search", "info", "add", "remove", "list", "show", "trust", "untrust"] as const;
|
|
23
|
-
export const MCP_COMMAND = { name: "mcp", choices: MCP_SUBCOMMANDS, choicesThen: "complete" as const, description: "Find and install an MCP server: /mcp [query] [--project] — the curated shelf, then the registry · /mcp trust approves this repo's MCP files", group: "modes & safety" };
|
|
24
|
-
|
|
25
|
-
/** `/mcp trust` — one approval card per project MCP file: the file as preview, its servers as detail; a yes
|
|
26
|
-
* records the file's current bytes as trusted (mcp/trust.ts), so the NEXT launch loads it */
|
|
27
|
-
async function trustProjectFiles(ctx: McpCmdCtx, home: string): Promise<void> {
|
|
28
|
-
const files = projectMcpFiles(ctx.cwd);
|
|
29
|
-
if (files.length === 0) { ctx.renderer.addSystemNote("mcp: no project MCP files here (.rovecode/mcp.json, .mcp.json)"); return; }
|
|
30
|
-
const anySet = new Proxy({}, { get: () => "set" }) as Record<string, string>;
|
|
31
|
-
for (const file of files) {
|
|
32
|
-
if (mcpTrustStatus(home, file) === "trusted") { ctx.renderer.addSystemNote(`mcp: ${file} is already trusted as it is now`); continue; }
|
|
33
|
-
const warnings: string[] = [];
|
|
34
|
-
const lines = parseConfigFile(file, warnings, anySet).map(serverLine).concat(warnings.map((w) => `! ${w}`));
|
|
35
|
-
const answer = await ctx.renderer.askApproval("mcp trust", file, lines.join("\n") || "(no servers in it)");
|
|
36
|
-
if (answer === "deny") { ctx.renderer.addSystemNote(`mcp: ${file} stays untrusted — nothing in it loads`); continue; }
|
|
37
|
-
const r = trustMcpFile(home, file);
|
|
38
|
-
ctx.renderer.addSystemNote(r.ok ? `mcp: trusted ${file} — restart me to connect; an edit asks again` : `mcp: ${r.reason}`, r.ok ? "info" : "warn");
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface McpCmdCtx {
|
|
43
|
-
renderer: Renderer;
|
|
44
|
-
cwd: string;
|
|
45
|
-
home?: string;
|
|
46
|
-
/** registry access — tests inject a fixture fetch or `offline` */
|
|
47
|
-
market?: MarketDeps;
|
|
48
|
-
/** how `npm install` runs for the install-once pick — tests inject one that writes a fake node_modules */
|
|
49
|
-
spawn?: import("../mcp/local-package.ts").Spawn;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const clip = (s: string, n: number): string => (s.length > n ? s.slice(0, n - 1) + "…" : s);
|
|
53
|
-
|
|
54
|
-
function pickItem(e: MarketEntry): PickItem {
|
|
55
|
-
const src = e.source === "curated" ? "curated" : `registry · ${e.publisher ?? "?"}`;
|
|
56
|
-
return { value: e.key, label: e.title ?? e.key, description: `${src}${e.status ? ` · ${e.status}` : ""} · ${clip(e.description, 60)}` };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export async function cmdMcp(ctx: McpCmdCtx, arg: string): Promise<void> {
|
|
60
|
-
const { renderer } = ctx;
|
|
61
|
-
const words = arg.split(/\s+/).filter(Boolean);
|
|
62
|
-
const scope: McpScope = words.includes("--project") ? "project" : "user";
|
|
63
|
-
const query = words.filter((w) => !w.startsWith("--")).join(" ");
|
|
64
|
-
const home = ctx.home ?? rovecodeHome();
|
|
65
|
-
if (words[0] === "trust") { await trustProjectFiles(ctx, home); return; }
|
|
66
|
-
const market: MarketDeps = { home, ...ctx.market };
|
|
67
|
-
const found = await searchMarket(query, market);
|
|
68
|
-
for (const n of found.notes) renderer.addSystemNote(`mcp: ${n}`, "warn");
|
|
69
|
-
if (found.entries.length === 0) { renderer.addSystemNote(`mcp: nothing matches "${query}" — the registry matches on the server's name`, "warn"); return; }
|
|
70
|
-
const key = await renderer.pickOne(found.entries.map(pickItem), query ? `mcp market · ${query}` : "mcp market");
|
|
71
|
-
if (key === null) return;
|
|
72
|
-
const entry = found.entries.find((e) => e.key === key);
|
|
73
|
-
if (!entry) return;
|
|
74
|
-
let pick = 0;
|
|
75
|
-
if (entry.installs.length > 1) {
|
|
76
|
-
const how = await renderer.pickOne(entry.installs.map((i, ix) => ({ value: String(ix), label: i.kind === "stdio" ? `run ${installLabel(i)}` : `connect ${i.url}` })), `${entry.title ?? entry.key} · how`);
|
|
77
|
-
if (how === null) return;
|
|
78
|
-
pick = Number(how);
|
|
79
|
-
}
|
|
80
|
-
// the install-once offer (mcp/local-package.ts) as one more pick, BEFORE the plan: the card the human
|
|
81
|
-
// approves is then the plan that runs. Esc here writes nothing; "as today" is the npx line unchanged.
|
|
82
|
-
const chosen = entry.installs[pick];
|
|
83
|
-
const offer = chosen !== undefined ? npxPackage(chosen) : undefined;
|
|
84
|
-
let local = false;
|
|
85
|
-
if (offer !== undefined && scope === "user") { // a project file is shared: install-once's absolute path has no place in it (planInstall refuses it too)
|
|
86
|
-
const how = await renderer.pickOne([
|
|
87
|
-
{ value: "local", label: `install once — node starts it in ~0.4 s`, description: `runs npm install now: ${offer.spec}'s code lands under ~/.rovecode/mcp (typically 20–30 MB, one time); no network needed to start` },
|
|
88
|
-
{ value: "npx", label: `run through npx at every start — as today`, description: `~2 s per start, re-resolves the package and asks the npm registry each time; nothing installed now` },
|
|
89
|
-
], `${entry.title ?? entry.key} · how to start it`);
|
|
90
|
-
if (how === null) return;
|
|
91
|
-
local = how === "local";
|
|
92
|
-
}
|
|
93
|
-
const plan = planInstall(entry, { scope, cwd: ctx.cwd, home, pick, ...(local ? { local: true } : {}) });
|
|
94
|
-
if ("error" in plan) { renderer.addSystemNote(`mcp: ${plan.error}`, "warn"); return; }
|
|
95
|
-
// the approval card: title = what is being done, preview = the one line that runs, detail = the whole plan
|
|
96
|
-
// (for install-once the detail says, in words, that npm runs and code lands on this machine)
|
|
97
|
-
const answer = await renderer.askApproval("mcp add", `${plan.name} ← ${plan.local ? `node ${plan.local.pkg.spec} (installed once)` : installLabel(plan.install)}`, describePlan(plan, "env").join("\n"));
|
|
98
|
-
if (answer === "deny") { renderer.addSystemNote("mcp: nothing written"); return; }
|
|
99
|
-
// install-once: npm first; only its success reaches the file, and what landed goes on record
|
|
100
|
-
let launch: { command: string; args: string[] } | undefined;
|
|
101
|
-
let pkgRecord: NonNullable<Parameters<typeof buildRecord>[1]["package"]> | undefined;
|
|
102
|
-
if (plan.local) {
|
|
103
|
-
renderer.addSystemNote(`mcp: npm install ${plan.local.pkg.spec} → ${plan.local.prefix} …`);
|
|
104
|
-
const lr = await installLocalPackage(plan.local.pkg, plan.local.prefix, ctx.spawn ? { spawn: ctx.spawn } : {});
|
|
105
|
-
if (!lr.ok) { renderer.addSystemNote(`mcp: ${lr.error} — nothing written`, "error"); return; }
|
|
106
|
-
launch = localLaunch(lr.pkg, plan.local.pkg.rest);
|
|
107
|
-
pkgRecord = { name: lr.pkg.name, version: lr.pkg.version, prefix: plan.local.prefix, bin: lr.pkg.bin, missing: lr.pkg.missing,
|
|
108
|
-
...(lr.pkg.integrity !== undefined ? { integrity: lr.pkg.integrity } : {}), ...(lr.pkg.resolved !== undefined ? { resolved: lr.pkg.resolved } : {}) };
|
|
109
|
-
}
|
|
110
|
-
let trusted: boolean | undefined;
|
|
111
|
-
const raw = fillPlan(plan, {}, launch); // no answers: required asks become ${NAME}, optional ones are left out — the file works without them
|
|
112
|
-
try {
|
|
113
|
-
// the card just approved this exact content: a project file is trusted as written (mcp/trust.ts)
|
|
114
|
-
trusted = writeServer(plan.file, plan.name, raw, scope === "project" ? { trustHome: home } : {}).trusted;
|
|
115
|
-
} catch (e) { renderer.addSystemNote(`mcp: ${e instanceof Error ? e.message : String(e)}`, "error"); return; }
|
|
116
|
-
renderer.addSystemNote(`mcp: added "${plan.name}" → ${plan.file}${trusted === true ? " (trusted as written)" : ""} — restart me to connect (servers are read once per process)`);
|
|
117
|
-
if (pkgRecord) {
|
|
118
|
-
recordInstall(buildRecord({ kind: "mcp", id: entry.key, source: entry.source, ...(entry.version !== undefined ? { version: entry.version } : {}) },
|
|
119
|
-
{ scope, target: plan.file, package: pkgRecord, installedBy: "mcp add" }), { cwd: ctx.cwd, home });
|
|
120
|
-
renderer.addSystemNote(`mcp: installed ${pkgRecord.name} ${pkgRecord.version} once → ${plan.local!.prefix}${pkgRecord.integrity !== undefined ? " (integrity recorded in installed.json)" : ""}`);
|
|
121
|
-
if (pkgRecord.missing?.length) renderer.addSystemNote(`mcp: record incomplete: ${pkgRecord.missing.join("; ")}`, "warn");
|
|
122
|
-
}
|
|
123
|
-
if (trusted === false) renderer.addSystemNote("mcp: that file already held servers you have not approved, so it is NOT trusted yet — /mcp trust shows them", "warn");
|
|
124
|
-
// only the names the FILE now refers to: an optional ask that was left out is not something to go and set
|
|
125
|
-
const named = namesWritten(plan, raw);
|
|
126
|
-
if (named.length) renderer.addSystemNote(`mcp: set ${named.join(", ")} in your environment before the restart — or run \`rovecode mcp add ${entry.key}\` on a shell, which asks for them masked`, "warn");
|
|
127
|
-
if (plan.pending.length) renderer.addSystemNote(`mcp: fill in ${plan.pending.join(", ")} in that file's args before use`, "warn");
|
|
128
|
-
}
|
package/src/tui/memory-note.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/** `#<text>` memory shortcut + `/memory [--user] [text]`. A prompt LINE that starts with `#` appends the rest of
|
|
2
|
-
* the line to the MEMORY block (the project store, memory/scope.ts) — no model turn, one transcript note — the way
|
|
3
|
-
* `!cmd` short-circuits the prompt (tui/shell-cmd.ts). Pattern source: Claude Code's `#` "add to memory" shortcut
|
|
4
|
-
* (proprietary — PATTERN ONLY, no code copied).
|
|
5
|
-
*
|
|
6
|
-
* The classifier is deliberately narrow, because the obvious version swallows pasted code: a note is ONE line
|
|
7
|
-
* whose trimmed text is `#` followed by a letter or digit (`#always use bun`, `#1 is flaky`). Everything else is a
|
|
8
|
-
* prompt for the model — any submission with a newline (a pasted `#include …` block, a shebang script), `#`,
|
|
9
|
-
* `# heading`, `##x`, `#!…`, `#[…]`, `#-flag`, and a `#` INSIDE a line (`fix the #1 bug`).
|
|
10
|
-
*
|
|
11
|
-
* The edit lands on disk and in the live store at once (memory_edit and /memory see it); the prompt keeps its boot
|
|
12
|
-
* snapshot and picks the note up on the NEXT run (memory/blocks.ts). Both surfaces share this module. No store is
|
|
13
|
-
* constructed here — the ACTIVE runtime store is injected, so a session switch cannot leave this writing to a
|
|
14
|
-
* store nobody reads. */
|
|
15
|
-
|
|
16
|
-
import type { BlockName, BlockStore } from "../memory/blocks.ts";
|
|
17
|
-
import type { Renderer } from "./renderer.ts";
|
|
18
|
-
|
|
19
|
-
/** the classifier's opener: `#` then a letter or digit (any script) */
|
|
20
|
-
const NOTE_OPENER = /^#[\p{L}\p{N}]/u;
|
|
21
|
-
|
|
22
|
-
/** The note of a `#<text>` line, or null when the submission is a prompt for the model (header rule). */
|
|
23
|
-
export function memoryNoteLine(text: string): string | null {
|
|
24
|
-
if (/[\r\n]/.test(text)) return null; // a multi-line submission is pasted content, never a note
|
|
25
|
-
const t = text.trim();
|
|
26
|
-
if (!NOTE_OPENER.test(t)) return null;
|
|
27
|
-
return t.slice(1).trim();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type Tone = "info" | "warn";
|
|
31
|
-
|
|
32
|
-
const LABEL: Readonly<Record<BlockName, string>> = { memory: "MEMORY", user: "USER" };
|
|
33
|
-
|
|
34
|
-
/** Append `text` to `block` through the store (cap, trust and ledger guarded); the line says WHERE it landed —
|
|
35
|
-
* the path is the point, since the whole bug this replaced was a note landing somewhere the person did not mean. */
|
|
36
|
-
export function appendMemory(store: BlockStore, block: BlockName, text: string): { ok: boolean; text: string; tone: Tone } {
|
|
37
|
-
const r = store.add(block, text);
|
|
38
|
-
if (!r.ok) return { ok: false, tone: "warn", text: `memory: not saved to ${LABEL[block]} — ${r.reason ?? "edit failed"}` };
|
|
39
|
-
return { ok: true, tone: "info", text: `memory: noted in ${LABEL[block]} (${r.current}/${r.limit} chars, ${store.path(block)}) — in the prompt from the next run` };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface MemoryNoteCtx {
|
|
43
|
-
renderer: Pick<Renderer, "addUser" | "addSystemNote">;
|
|
44
|
-
/** the ACTIVE block store, read live */
|
|
45
|
-
blocks(): BlockStore;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Run one `#<text>` line: echo it, append to MEMORY, note the outcome. false = not a note line (nothing done). */
|
|
49
|
-
export function runMemoryNote(ctx: MemoryNoteCtx, line: string): boolean {
|
|
50
|
-
const note = memoryNoteLine(line);
|
|
51
|
-
if (note === null) return false;
|
|
52
|
-
ctx.renderer.addUser(line.trim());
|
|
53
|
-
const r = appendMemory(ctx.blocks(), "memory", note);
|
|
54
|
-
ctx.renderer.addSystemNote(r.text, r.tone);
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** the /memory hints under a block: an edit this run, a block withheld from the prompt, a block over its cap */
|
|
59
|
-
function blockHints(store: BlockStore, b: BlockName): string {
|
|
60
|
-
const over = store.overCap(b);
|
|
61
|
-
return (store.edited(b) ? "\n(edited this run — in the prompt from the next run)" : "")
|
|
62
|
-
+ (store.isWithheld(b) ? "\n(not in the prompt and not writable — it came with this repository; rovecode trust show)" : "")
|
|
63
|
-
+ (over ? `\n(over the ${over.cap}-char cap — the prompt shows the first ${over.cap}; trim the file)` : "");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** `/memory` → both blocks as the store holds them NOW (live text, path, and a hint when an edit this run is not
|
|
67
|
-
* yet in the prompt); `/memory --user` → the USER block alone; `/memory <text>` appends to MEMORY and
|
|
68
|
-
* `/memory --user <text>` to USER (the same append path as `#<text>` and memory_edit add). */
|
|
69
|
-
export function memoryCommand(store: BlockStore, arg: string): { text: string; tone: Tone } {
|
|
70
|
-
const a = arg.trim();
|
|
71
|
-
const user = a === "--user" || a.startsWith("--user ");
|
|
72
|
-
const text = user ? a.slice("--user".length).trim() : a;
|
|
73
|
-
if (text) { const r = appendMemory(store, user ? "user" : "memory", text); return { text: r.text, tone: r.tone }; }
|
|
74
|
-
const blocks: BlockName[] = user ? ["user"] : ["memory", "user"];
|
|
75
|
-
const shown = blocks.map((b) => `# ${LABEL[b]} — ${store.path(b)}\n${store.liveText(b) || "(empty)"}${blockHints(store, b)}`);
|
|
76
|
-
return { text: shown.join("\n\n"), tone: "info" };
|
|
77
|
-
}
|
package/src/tui/modes-cmd.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/** TUI glue for plan/act modes (port #20), extracted from app.ts for the ADR-002 cap.
|
|
2
|
-
* Pure functions over the ModeManager + the app's mutable state slice. */
|
|
3
|
-
|
|
4
|
-
import { ModeManager, applyModeRules, buildModeChangeEntry, modeSwitchOf, planModePromptSection, type AgentMode } from "../core/modes.ts";
|
|
5
|
-
import type { SessionStore } from "../core/session.ts";
|
|
6
|
-
import type { AgentDefinition, RunConfig } from "../core/types.ts";
|
|
7
|
-
import type { Renderer } from "./renderer.ts";
|
|
8
|
-
|
|
9
|
-
export interface ModeStateSlice { provider: string; model: string; mode: AgentMode; busy: boolean }
|
|
10
|
-
|
|
11
|
-
/** /plan and /act — busy-gated toggle; updates the state slice from the mode's model slot. */
|
|
12
|
-
export function togglePlanAct(modes: ModeManager, cmd: AgentMode, state: ModeStateSlice, renderer: Renderer, pushStatus: () => void): void {
|
|
13
|
-
if (state.busy) { renderer.addSystemNote("finish or interrupt the run first (Esc)", "warn"); return; }
|
|
14
|
-
const sw = modes.toggle(cmd);
|
|
15
|
-
if (!sw) { renderer.addSystemNote(`already in ${cmd} mode`); return; }
|
|
16
|
-
const cur = modes.modelFor();
|
|
17
|
-
state.mode = modes.mode; state.model = cur.model; state.provider = cur.provider;
|
|
18
|
-
renderer.addSystemNote(cmd === "plan" ? "plan mode: read-only tools — writes/shell/spawn denied by policy" : "act mode: full toolset restored");
|
|
19
|
-
pushStatus();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Run-start enforcement: plan mode appends read-only rules AFTER the base set
|
|
23
|
-
* (last-match-wins overrides even yolo) and steers the system prompt. */
|
|
24
|
-
export function applyModeToRun(modes: ModeManager, cfg: RunConfig, def: AgentDefinition): void {
|
|
25
|
-
cfg.permissionRules = applyModeRules(modes.mode, cfg.permissionRules);
|
|
26
|
-
if (modes.mode !== "plan") return;
|
|
27
|
-
delete cfg.verify; // plan mode changes nothing and runs nothing: the verify gate (core/verify-gate.ts) is off here, not merely idle
|
|
28
|
-
const base = def.systemPrompt;
|
|
29
|
-
def.systemPrompt = (v) => (typeof base === "function" ? base(v) : base) + "\n\n" + planModePromptSection();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** MED-2: a pending mode switch must survive quit and session swaps, not just the
|
|
33
|
-
* next submit — flush it as a durable entry so resume restores the last mode.
|
|
34
|
-
* Round-trip cancellations have no pending switch, so nothing lands for them. */
|
|
35
|
-
export function flushModeSwitch(modes: ModeManager, store: SessionStore): void {
|
|
36
|
-
const sw = modes.consumeSwitchNotice();
|
|
37
|
-
if (sw) store.append(buildModeChangeEntry(sw, store.messages().at(-1)?.id ?? null));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** LOW-3: replay label for a system entry — mode switches render as a human line
|
|
41
|
-
* ("mode → plan"), never the raw <mode_notice> XML the entry carries. */
|
|
42
|
-
export function replayLabel(entry: unknown, text: string): string {
|
|
43
|
-
const sw = modeSwitchOf(entry);
|
|
44
|
-
return sw ? `mode → ${sw.to}` : text;
|
|
45
|
-
}
|
package/src/tui/notify-seq.ts
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
/** Notification sequences + focus tracking for the interactive TUI — PURE: no I/O, no env reads, no timers, no settings.
|
|
2
|
-
*
|
|
3
|
-
* Pattern source: openai/codex (Apache-2.0, pattern level; no code copied) — codex-rs/tui/src/notifications/mod.rs (the
|
|
4
|
-
* `auto` backend chooser: OSC 9 on Ghostty / iTerm2 / kitty / Warp / WezTerm, else BEL; the not-OSC-9 list incl. Windows
|
|
5
|
-
* Terminal, VsCode, AppleTerminal, Alacritty, Vte, Konsole), codex-rs/tui/src/notifications/osc9.rs (the tmux DCS
|
|
6
|
-
* passthrough: `ESC P tmux ;` + the OSC with every ESC doubled + `ESC \`, and its three test vectors),
|
|
7
|
-
* codex-rs/terminal-detection/src/lib.rs (the env markers each terminal sets; TERM_PROGRAM masks nothing here — any
|
|
8
|
-
* marker counts), codex-rs/tui/src/chatwidget/notifications.rs (the whitespace-normalised, capped preview). The
|
|
9
|
-
* focus-report idiom (DECSET 1004 → CSI I / CSI O) is xterm's. OSC 777 (rxvt-unicode / VTE `notify;title;body`), the
|
|
10
|
-
* control-byte scrub and the argv parser are rovecode's own.
|
|
11
|
-
*
|
|
12
|
-
* Why a scrub at all: the model's last answer rides inside an OSC payload whose own terminator is BEL — an embedded BEL
|
|
13
|
-
* or ESC would end the toast early or start a second sequence. Nothing a model wrote reaches the wire unfiltered. */
|
|
14
|
-
|
|
15
|
-
export type Env = Readonly<Record<string, string | undefined>>;
|
|
16
|
-
export type NotifyMethod = "bell" | "osc9" | "osc777";
|
|
17
|
-
export type NotifyKind = "run_end" | "approval" | "question";
|
|
18
|
-
export interface NotifyDetail { tool?: string; argsPreview?: string; question?: string; lastText?: string }
|
|
19
|
-
|
|
20
|
-
export const BEL = "\x07";
|
|
21
|
-
/** DECSET 1004: the terminal reports focus changes as CSI I (in) / CSI O (out) */
|
|
22
|
-
export const FOCUS_ON = "\x1b[?1004h";
|
|
23
|
-
export const FOCUS_OFF = "\x1b[?1004l";
|
|
24
|
-
export const FOCUS_IN = "\x1b[I";
|
|
25
|
-
export const FOCUS_OUT = "\x1b[O";
|
|
26
|
-
|
|
27
|
-
/** strip every C0 / C1 control byte (tab, newline, CR count as whitespace first, so words never fuse), collapse
|
|
28
|
-
* whitespace, trim, and cut to `max` characters with a closing `…` */
|
|
29
|
-
export function scrub(text: string, max: number): string {
|
|
30
|
-
const flat = text.replace(/[\x00-\x1f\x7f-\x9f]/g, (c) => (/\s/.test(c) ? " " : "")).replace(/\s+/g, " ").trim();
|
|
31
|
-
if (max <= 0) return "";
|
|
32
|
-
return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const has = (env: Env, key: string): boolean => (env[key] ?? "") !== "";
|
|
36
|
-
/** codex terminal-detection normalisation of TERM_PROGRAM: spaces, `-`, `_`, `.` dropped, lower-cased */
|
|
37
|
-
const program = (env: Env): string => (env.TERM_PROGRAM ?? "").replace(/[\s_.-]/g, "").toLowerCase();
|
|
38
|
-
|
|
39
|
-
/** `auto`: OSC 9 only where codex lists a toast-capable terminal (Ghostty, iTerm2, kitty, Warp, WezTerm), else the bell
|
|
40
|
-
* (Windows Terminal, vscode, Apple_Terminal, alacritty, VTE, unknown). OSC 777 is never picked automatically. */
|
|
41
|
-
export function autoMethod(env: Env): NotifyMethod {
|
|
42
|
-
const p = program(env), term = env.TERM ?? "";
|
|
43
|
-
const ghostty = p === "ghostty" || term === "xterm-ghostty" || has(env, "GHOSTTY_RESOURCES_DIR");
|
|
44
|
-
const iterm = p === "itermapp" || p === "iterm" || p === "iterm2" || has(env, "ITERM_SESSION_ID");
|
|
45
|
-
const kitty = p === "kitty" || term === "xterm-kitty" || has(env, "KITTY_WINDOW_ID");
|
|
46
|
-
const warp = p === "warpterminal" || p === "warp";
|
|
47
|
-
const wezterm = p === "wezterm" || has(env, "WEZTERM_VERSION");
|
|
48
|
-
return ghostty || iterm || kitty || warp || wezterm ? "osc9" : "bell";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** inside tmux (a non-empty TMUX): OSC sequences must ride a DCS passthrough to reach the outer terminal */
|
|
52
|
-
export const isTmux = (env: Env): boolean => has(env, "TMUX");
|
|
53
|
-
|
|
54
|
-
/** the byte-exact sequence for one notification (`msg` already scrubbed by the caller): bell `\x07`; osc9 `ESC ] 9 ; msg BEL`;
|
|
55
|
-
* osc777 `ESC ] 777 ; notify ; rovecode ; msg BEL`; under tmux the two OSC forms become `ESC P tmux ;` + the sequence with
|
|
56
|
-
* every ESC doubled + `ESC \` (BEL needs no wrap, tmux passes it through) */
|
|
57
|
-
export function sequenceFor(method: NotifyMethod, msg: string, tmux: boolean): string {
|
|
58
|
-
if (method === "bell") return BEL;
|
|
59
|
-
const osc = method === "osc9" ? `\x1b]9;${msg}\x07` : `\x1b]777;notify;rovecode;${msg}\x07`;
|
|
60
|
-
return tmux ? `\x1bPtmux;${osc.replace(/\x1b/g, "\x1b\x1b")}\x1b\\` : osc;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** the human line a toast shows — every part scrubbed and capped (approval: ≤ 40 chars of args; question ≤ 60; answer ≤ 120) */
|
|
64
|
-
export function message(kind: NotifyKind, d: NotifyDetail): string {
|
|
65
|
-
if (kind === "approval") return `approval needed: ${scrub(d.tool ?? "", 40)} ${scrub(d.argsPreview ?? "", 40)}`.trimEnd();
|
|
66
|
-
if (kind === "question") return `question: ${scrub(d.question ?? "", 60)}`;
|
|
67
|
-
const last = scrub(d.lastText ?? "", 120);
|
|
68
|
-
return last ? `run finished: ${last}` : "run finished";
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type Argv = string[] | { error: string };
|
|
72
|
-
export const isArgvError = (a: Argv): a is { error: string } => !Array.isArray(a);
|
|
73
|
-
|
|
74
|
-
/** `notify_command` text → argv: a JSON string array when it starts with `[` (`["notify-send","rovecode"]`), else split on
|
|
75
|
-
* whitespace; NEVER handed to a shell (a Windows path with `\` or an argument holding spaces needs the JSON form). `{ error }`
|
|
76
|
-
* for a `[`-prefixed value that is not a non-empty array of non-empty strings, and for an empty argv. */
|
|
77
|
-
export function parseArgv(value: string): Argv {
|
|
78
|
-
const text = value.trim();
|
|
79
|
-
if (text.startsWith("[")) {
|
|
80
|
-
let parsed: unknown;
|
|
81
|
-
try { parsed = JSON.parse(text); } catch (e) { return { error: `not a JSON array (${e instanceof Error ? e.message : String(e)})` }; }
|
|
82
|
-
if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every((x) => typeof x === "string" && x.length > 0)) return { error: "a JSON array must hold one or more non-empty strings" };
|
|
83
|
-
return parsed as string[];
|
|
84
|
-
}
|
|
85
|
-
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
86
|
-
return words.length === 0 ? { error: "empty command" } : words;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Consumes CSI I / CSI O (focus in / out) from a raw input chunk BEFORE the surface's parser sees it. `focused` starts
|
|
90
|
-
* true: a terminal that never reports focus reads as focused — watched — so `notify_when: unfocused` stays silent there
|
|
91
|
-
* instead of ringing on every run. No carry across chunks — the classic surface feeds StdinBuffer-reassembled sequences
|
|
92
|
-
* and the sextant's parseInput degrades a split CSI to a harmless unknown key. */
|
|
93
|
-
export class FocusTracker {
|
|
94
|
-
focused = true;
|
|
95
|
-
/** the chunk with every focus report removed (the flag follows the LAST one in it); "" when nothing else was there */
|
|
96
|
-
feed(chunk: string): string {
|
|
97
|
-
if (!chunk.includes("\x1b[")) return chunk;
|
|
98
|
-
return chunk.replace(/\x1b\[([IO])/g, (_m, c: string) => { this.focused = c === "I"; return ""; });
|
|
99
|
-
}
|
|
100
|
-
}
|