rovecode 0.3.2 → 0.4.0-beta.2
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 +512 -0
- package/README.md +33 -45
- package/bin/rovecode.js +24 -0
- package/dist/cli/app-dybnr56b.js +2 -0
- package/dist/cli/ask-user-p8hq4xgj.js +2 -0
- package/dist/cli/auth-login-ewpgw5sm.js +2 -0
- package/dist/cli/auth-m8p9grty.js +2 -0
- package/dist/cli/bench-xv3ypwev.js +9 -0
- package/dist/cli/catalog-737wb2s0.js +2 -0
- package/dist/cli/cli-arhg40m0.js +2 -0
- package/dist/cli/client-cf2pxx8q.js +2 -0
- package/dist/cli/commands-3p7e4xxs.js +2 -0
- package/dist/cli/connect-3q93d7cb.js +2 -0
- package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
- package/dist/cli/context-report-hbw9zfes.js +2 -0
- package/dist/cli/count-remote-mby98cd0.js +2 -0
- package/dist/cli/design-122y0axd.js +2 -0
- package/dist/cli/dispatch-b4egzvvh.js +2 -0
- package/dist/cli/doctor-x4jkv72e.js +3 -0
- package/dist/cli/executor-ftvg6tsy.js +2 -0
- package/dist/cli/export-pdgdhkch.js +2 -0
- package/dist/cli/files-cez9a96p.js +2 -0
- package/dist/cli/gauntlet-r3xxaszc.js +2 -0
- package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
- package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
- package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
- package/dist/cli/hashline-ewg5hbe3.js +2 -0
- package/dist/cli/http-n0kehsk8.js +5 -0
- package/dist/cli/index-z5qt1s76.js +2 -0
- package/dist/cli/install-80mp63kx.js +2 -0
- package/dist/cli/loop-12twjcat.js +2 -0
- package/dist/cli/main-01pv9206.js +4 -0
- package/dist/cli/main-0jys2ccn.js +3 -0
- package/dist/cli/main-1ztz6fkj.js +10 -0
- package/dist/cli/main-23q7cmww.js +9 -0
- package/dist/cli/main-2rzbexn2.js +3 -0
- package/dist/cli/main-2wyax8k9.js +9 -0
- package/dist/cli/main-2yeveeve.js +6 -0
- package/dist/cli/main-2z3dek0b.js +3 -0
- package/dist/cli/main-2zgsknth.js +3 -0
- package/dist/cli/main-45ejth3a.js +4 -0
- package/dist/cli/main-45rn3trk.js +22 -0
- package/dist/cli/main-4p4e2w7x.js +4 -0
- package/dist/cli/main-4y0tnfpa.js +16 -0
- package/dist/cli/main-5py0rkmc.js +4 -0
- package/dist/cli/main-6dtqmbt6.js +7 -0
- package/dist/cli/main-6h9x282m.js +4 -0
- package/dist/cli/main-6vjeds42.js +3 -0
- package/dist/cli/main-78gq4bt9.js +6 -0
- package/dist/cli/main-7jd5vh3x.js +4 -0
- package/dist/cli/main-7kt6r53y.js +4 -0
- package/dist/cli/main-8c1tbazx.js +58 -0
- package/dist/cli/main-9a9rnh47.js +19 -0
- package/dist/cli/main-9ht36z12.js +3 -0
- package/dist/cli/main-a2yfvcy9.js +7 -0
- package/dist/cli/main-a3f51n0x.js +5 -0
- package/dist/cli/main-b8zq261k.js +3 -0
- package/dist/cli/main-bxtvnf6d.js +13 -0
- package/dist/cli/main-edxc3yzt.js +4 -0
- package/dist/cli/main-evgz4mp5.js +21 -0
- package/dist/cli/main-f33fc5je.js +9 -0
- package/dist/cli/main-fvnpq46y.js +12 -0
- package/dist/cli/main-gbbty4d4.js +3 -0
- package/dist/cli/main-gth53dnt.js +25 -0
- package/dist/cli/main-hqbz10aw.js +9 -0
- package/dist/cli/main-hrrvcfan.js +38 -0
- package/dist/cli/main-hzwtsb2m.js +5 -0
- package/dist/cli/main-j7ttv0sd.js +34 -0
- package/dist/cli/main-jak598k9.js +5 -0
- package/dist/cli/main-kba6zeyd.js +6 -0
- package/dist/cli/main-kwwsz6rq.js +3 -0
- package/dist/cli/main-m8vm17zq.js +3 -0
- package/dist/cli/main-mg4f96e1.js +3 -0
- package/dist/cli/main-mg9b20ac.js +18 -0
- package/dist/cli/main-mgb9ccnx.js +3 -0
- package/dist/cli/main-mjt2p7aj.js +3 -0
- package/dist/cli/main-n6qrdbmy.js +3 -0
- package/dist/cli/main-na7wse0x.js +5 -0
- package/dist/cli/main-nqveez48.js +4 -0
- package/dist/cli/main-ntqef02r.js +10 -0
- package/dist/cli/main-nvc3yjay.js +136 -0
- package/dist/cli/main-p0cfn6nr.js +16 -0
- package/dist/cli/main-qj2djy17.js +19 -0
- package/dist/cli/main-qsevpgsv.js +3 -0
- package/dist/cli/main-qvarybsp.js +3 -0
- package/dist/cli/main-rebtt91r.js +5 -0
- package/dist/cli/main-rpg7h8mb.js +3 -0
- package/dist/cli/main-rsy72qmw.js +15 -0
- package/dist/cli/main-rvetps99.js +18 -0
- package/dist/cli/main-s4bb0jav.js +3 -0
- package/dist/cli/main-s9v8k74e.js +3 -0
- package/dist/cli/main-tjvwmscs.js +3 -0
- package/dist/cli/main-tkgarpjj.js +4 -0
- package/dist/cli/main-v8y60bb2.js +3 -0
- package/dist/cli/main-vhrrq337.js +3 -0
- package/dist/cli/main-vp2dfb7s.js +4 -0
- package/dist/cli/main-vqbr22sz.js +8 -0
- package/dist/cli/main-vxnwe5xx.js +18 -0
- package/dist/cli/main-wgph00xf.js +5 -0
- package/dist/cli/main-wk2csfnj.js +5 -0
- package/dist/cli/main-wm997zjx.js +3 -0
- package/dist/cli/main-wpkyraxh.js +3 -0
- package/dist/cli/main-wqt32p5x.js +4 -0
- package/dist/cli/main-x9ct6y1a.js +3 -0
- package/dist/cli/main-xfekqh9m.js +7 -0
- package/dist/cli/main-xt9zc3n6.js +7 -0
- package/dist/cli/main-xx2z3zh5.js +4 -0
- package/dist/cli/main-y5c82rxr.js +3 -0
- package/dist/cli/main-yrjt2sqt.js +14 -0
- package/dist/cli/main-ys6zj3yr.js +3 -0
- package/dist/cli/main-ywbxshqc.js +8 -0
- package/dist/cli/main-z13755t8.js +25 -0
- package/dist/cli/main-zc7pyrbj.js +4 -0
- package/dist/cli/main.js +279 -0
- package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
- package/dist/cli/mcp-login-bthtfpt7.js +2 -0
- package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
- package/dist/cli/notify-54v5z9dz.js +2 -0
- package/dist/cli/oauth-g5gme95c.js +2 -0
- package/dist/cli/output-satndjap.js +16 -0
- package/dist/cli/profiles-sfhpbq3m.js +2 -0
- package/dist/cli/provider-config-hv3xtdt4.js +2 -0
- package/dist/cli/provider-kwzq6g84.js +2 -0
- package/dist/cli/registry-fh0hdnyn.js +2 -0
- package/dist/cli/registry-y1y8e94r.js +2 -0
- package/dist/cli/repl-t4z03mqq.js +11 -0
- package/dist/cli/resume-fqt4chg8.js +2 -0
- package/dist/cli/run-flags-rysbag9t.js +2 -0
- package/dist/cli/runtime-j19fjbsa.js +2 -0
- package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
- package/dist/cli/server-r0b6bksk.js +5 -0
- package/dist/cli/session-arg-txmn5g4x.js +2 -0
- package/dist/cli/session-ed250d9j.js +2 -0
- package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
- package/dist/cli/settings-y9rzcqx8.js +2 -0
- package/dist/cli/setup-jmbr11j0.js +2 -0
- package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
- package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
- package/dist/cli/smoke-1bg937kx.js +8 -0
- package/dist/cli/start-chat-p01cdks3.js +12 -0
- package/dist/cli/stream-4wmyaypz.js +2 -0
- package/dist/cli/task-eg4s093s.js +2 -0
- package/dist/cli/tasks-12v9rr9k.js +2 -0
- package/dist/cli/thinking-a5ngvqyh.js +2 -0
- package/dist/cli/todo-1wxpcecx.js +2 -0
- package/dist/cli/tools-2ftsya7w.js +2 -0
- package/dist/cli/tools-x1tj4fxm.js +2 -0
- package/dist/cli/trust-cmd-hccxehzb.js +2 -0
- package/dist/cli/update-check-ygt3vd7m.js +2 -0
- package/dist/cli/update-cmd-v23qhr8c.js +2 -0
- package/dist/cli/voice-g1gtck92.js +2 -0
- package/dist/cli/webfetch-0nnrjgb5.js +2 -0
- package/dist/cli/websearch-f0vr2p7d.js +2 -0
- package/dist/cli/workspace-9rq1w4ta.js +2 -0
- package/dist/lib/index.js +62 -0
- package/dist/lib/models-index.json +1 -0
- package/dist/lib/plugins.js +6 -0
- package/dist/lib/providers.js +17 -0
- package/dist/lib/public-api.js +20 -0
- package/dist/rovecode.exe +4 -0
- package/package.json +38 -11
- 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
|
@@ -1,574 +0,0 @@
|
|
|
1
|
-
/** SextantRenderer (port #44): the sextant surface as rovecode's `Renderer` — the ONE controller stays
|
|
2
|
-
* runTui (ADR-003, no second loop); this class maps the Renderer seam plus the optional onEvent/attach
|
|
3
|
-
* members onto the SextantState (#41 model), paints through FrameLoop (#40 screen, #41-#46 painters)
|
|
4
|
-
* and routes keys through #43. Ported from the user's sextant v0.4.0 app.js:1599-1632 (boot: enter
|
|
5
|
-
* the alt screen, state, tick, input, resize, leave).
|
|
6
|
-
*
|
|
7
|
-
* ONE source of truth for transcript rows while a run is busy: the RunEvent stream (onEvent →
|
|
8
|
-
* applyEvent). The per-event Renderer calls app.ts makes for the same events are therefore no-ops
|
|
9
|
-
* while busy (beginAssistant views, toolStart/toolUpdate/toolEnd) or dropped by exact count (the
|
|
10
|
-
* compaction / "↪ steering applied" / run_end notes — sextant-bridge duplicateNotes); every other
|
|
11
|
-
* note (boot, slash commands, task settlements, "queued as steering", router notes) is a row + toast.
|
|
12
|
-
* When NOT busy (history replay after /resume, /rewind, /new) the same methods build rows directly.
|
|
13
|
-
* Renderer-local slash commands (/theme /open /diff /focus /agents, the /help card) run in keys.ts
|
|
14
|
-
* runLocal through the `local` hooks below — src/sextant/local-commands.ts is their future home.
|
|
15
|
-
* I/O: the terminal through TerminalIO; git/fs through sextant-repo.ts (RepoWatcher: scheduled OFF
|
|
16
|
-
* the frame loop from the tick and run async, so a slow `git status` never freezes a frame). */
|
|
17
|
-
|
|
18
|
-
import { homedir } from "node:os";
|
|
19
|
-
import { basename, join } from "node:path";
|
|
20
|
-
import pkg from "../../package.json";
|
|
21
|
-
import type { RunEvent } from "../core/types.ts";
|
|
22
|
-
import { loadTodos } from "../tools/todo.ts";
|
|
23
|
-
import type { ApprovalAnswer, AssistantView, PickItem, QuestionAnswer, QuestionPrompt, Renderer, RendererHooks, RendererStartOptions, SlashCommand, StatusInfo } from "../tui/renderer.ts";
|
|
24
|
-
import { drawAgents } from "./draw-agents.ts";
|
|
25
|
-
import { openMarket } from "./draw-market.ts";
|
|
26
|
-
import { closeWizard, openWizard, wizardEndpointRegistered, wizardKeyStored, wizardModelConfirmed, type WizardProvider } from "./draw-wizard.ts";
|
|
27
|
-
import { docsFor, install, loadMarket, planFor } from "./market-source.ts";
|
|
28
|
-
import { openContext } from "./draw-context.ts";
|
|
29
|
-
import { fixedFrom, loadContext } from "./context-source.ts";
|
|
30
|
-
import { hunksFromUnified, setAgentsPainter } from "./draw-code.ts";
|
|
31
|
-
import { fuzzy } from "./engine.ts";
|
|
32
|
-
import { spawnGitAsync, toAsync, type GitRunner, type GitRunnerAsync } from "./git-status.ts";
|
|
33
|
-
import { enterSequence, leaveSequence } from "./input.ts";
|
|
34
|
-
import { ESC_WINDOW_MS, type KeyCtx } from "./keys.ts";
|
|
35
|
-
import { initialState, makeApplyEvent, planCounts, pushToast, setCrew, setPlan, setUsage } from "./model.ts";
|
|
36
|
-
import { suggestions } from "./overlays.ts";
|
|
37
|
-
import { createPet, type Pet } from "./pet.ts";
|
|
38
|
-
import { argsFromPreview, duplicateNotes, petOnEvent, petOnTask, replayToolRow, userRow, type LiveCall } from "./sextant-bridge.ts";
|
|
39
|
-
import { CardHost } from "./sextant-cards.ts";
|
|
40
|
-
import { editOpsOf, type HashlineOp } from "./sextant-diff-base.ts";
|
|
41
|
-
import { readFileBounded } from "./sextant-files.ts";
|
|
42
|
-
import { FrameLoop } from "./sextant-frame-loop.ts";
|
|
43
|
-
import { RepoWatcher } from "./sextant-repo.ts";
|
|
44
|
-
import { buildTheme, isThemeName } from "./theme.ts";
|
|
45
|
-
import { clipText, relPath, summarizeEnd } from "./tool-rows.ts";
|
|
46
|
-
import type { ApplyEvent, InputEvent, SextantAttach, SextantState, TerminalIO, Theme, ThemeName, ToolRow } from "./types.ts";
|
|
47
|
-
|
|
48
|
-
export { IDLE_SCAN_MS } from "./sextant-repo.ts";
|
|
49
|
-
|
|
50
|
-
export interface SextantRendererOptions {
|
|
51
|
-
io: TerminalIO;
|
|
52
|
-
/** the frame clock (Date.now in production; tests inject a controllable one) */
|
|
53
|
-
clock?: () => number;
|
|
54
|
-
/** starting palette (ROVECODE_THEME / --theme); an unknown name falls back to night */
|
|
55
|
-
theme?: string;
|
|
56
|
-
/** the pet's name (`--pet <name>`); attach() may override it */
|
|
57
|
-
pet?: string;
|
|
58
|
-
/** emit truecolor SGR (default true; false quantizes to xterm-256) */
|
|
59
|
-
truecolor?: boolean;
|
|
60
|
-
/** the project dir until attach() supplies the runtime's cwd */
|
|
61
|
-
cwd?: string;
|
|
62
|
-
/** git seam for tests (a sync fake is wrapped); undefined = spawn git asynchronously */
|
|
63
|
-
git?: GitRunner | GitRunnerAsync;
|
|
64
|
-
/** false disables the repo scan entirely (pure tests) */
|
|
65
|
-
scan?: boolean;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const TOAST_MAX = 48;
|
|
69
|
-
const NOOP_VIEW: AssistantView = { append() {}, done() {} };
|
|
70
|
-
const NO_HOOKS: RendererHooks = { onSubmit() {}, onInterrupt() {}, onExit() {} };
|
|
71
|
-
|
|
72
|
-
export class SextantRenderer implements Renderer {
|
|
73
|
-
/** the surface state (keys.ts and the reducer mutate it; tests read it) */
|
|
74
|
-
readonly state: SextantState;
|
|
75
|
-
private theme: Theme;
|
|
76
|
-
private readonly io: TerminalIO;
|
|
77
|
-
private readonly clock: () => number;
|
|
78
|
-
private readonly pet: Pet;
|
|
79
|
-
private readonly loop: FrameLoop;
|
|
80
|
-
/** the git/fs work, off the loop and async (sextant-repo.ts) */
|
|
81
|
-
private readonly repo: RepoWatcher;
|
|
82
|
-
private readonly applyEv: ApplyEvent;
|
|
83
|
-
/** SGR mode the Screen emits (false = the xterm-256 quantizer; tests/pickRenderer read it) */
|
|
84
|
-
readonly truecolor: boolean;
|
|
85
|
-
private hooks: RendererHooks | null = null;
|
|
86
|
-
private ctx: SextantAttach | null = null;
|
|
87
|
-
private unsubTasks: (() => void) | null = null;
|
|
88
|
-
private started = false;
|
|
89
|
-
private stopped = false;
|
|
90
|
-
/** between setBusy(true) and setBusy(false): the event stream owns the rows */
|
|
91
|
-
private busy = false;
|
|
92
|
-
private dropNotes = 0;
|
|
93
|
-
/** the approval / question / picker promises (sextant-cards.ts) */
|
|
94
|
-
private readonly cards: CardHost;
|
|
95
|
-
private readonly liveCalls = new Map<string, LiveCall>();
|
|
96
|
-
/** per cwd-relative path: the content when its edit/write was APPROVED (null = did not exist) — the
|
|
97
|
-
* pre-edit diff base. Captured at askApproval (the one moment before the tool runs), NOT at
|
|
98
|
-
* tool_execution_start: that event reaches the renderer through the loop's buffered pump after
|
|
99
|
-
* rovecode's synchronous edit/write already wrote, so a snapshot then is the post-edit file. Ungated
|
|
100
|
-
* calls (yolo / always) rebuild their base from the hashline ops instead (sextant-diff-base.ts). */
|
|
101
|
-
private readonly before = new Map<string, string | null>();
|
|
102
|
-
/** per live edit callId: its hashline ops (the base of an ungated edit is rebuilt from them at the end) */
|
|
103
|
-
private readonly editOps = new Map<string, HashlineOp[]>();
|
|
104
|
-
/** the file whose content the code panel holds (null → reload on the next tick) */
|
|
105
|
-
private loadedFile: string | null = null;
|
|
106
|
-
private planLoaded = false;
|
|
107
|
-
private todoDone = 0;
|
|
108
|
-
|
|
109
|
-
/** the model step's ONE fetch: the chosen provider through the wizardModels seam, the answer
|
|
110
|
-
* written with wizardModelConfirmed. Empty or failed leaves the step on the typed fallback. */
|
|
111
|
-
private fetchWizardModels(): void {
|
|
112
|
-
const w = this.state.wizard;
|
|
113
|
-
if (!w) return;
|
|
114
|
-
w.modelsLoading = true;
|
|
115
|
-
this.loop.markDirty();
|
|
116
|
-
const provider = w.providers[w.selected]?.key ?? "";
|
|
117
|
-
void (this.ctx?.wizardModels?.(provider) ?? Promise.resolve([]))
|
|
118
|
-
.then((models) => {
|
|
119
|
-
if (!this.state.wizard) return;
|
|
120
|
-
wizardModelConfirmed(this.state.wizard, models);
|
|
121
|
-
if (models.length === 0) this.state.wizard.error = "the endpoint listed no models — type the id by hand (it still pins)";
|
|
122
|
-
this.loop.markDirty();
|
|
123
|
-
})
|
|
124
|
-
.catch(() => {
|
|
125
|
-
if (!this.state.wizard) return;
|
|
126
|
-
this.state.wizard.modelsLoading = false;
|
|
127
|
-
this.state.wizard.error = "the model list could not be read — type the id by hand";
|
|
128
|
-
this.loop.markDirty();
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
constructor(o: SextantRendererOptions) {
|
|
133
|
-
this.io = o.io; this.clock = o.clock ?? Date.now; this.truecolor = o.truecolor ?? true;
|
|
134
|
-
const themeName: ThemeName = isThemeName(o.theme ?? "") ? (o.theme as ThemeName) : "night";
|
|
135
|
-
this.theme = buildTheme(themeName);
|
|
136
|
-
const cwd = o.cwd ?? process.cwd();
|
|
137
|
-
this.state = initialState({ cwd, repo: { name: basename(cwd) || cwd, branch: null }, version: pkg.version, theme: themeName, mode: "act", yolo: false, commands: [], now: this.clock() });
|
|
138
|
-
this.pet = createPet({ name: o.pet });
|
|
139
|
-
// the reducer's diffFor seam stays empty: the hunks arrive through the watcher's scheduleDiff (a
|
|
140
|
-
// setTimeout(0) from the end event, then async git — nothing runs inside the event dispatch or a painter)
|
|
141
|
-
this.applyEv = makeApplyEvent();
|
|
142
|
-
this.repo = new RepoWatcher({ state: this.state, clock: this.clock, git: o.git ? toAsync(o.git) : spawnGitAsync, dirty: () => this.loop.markDirty() }, o.scan ?? true);
|
|
143
|
-
this.cards = new CardHost({
|
|
144
|
-
state: this.state, dirty: () => this.loop.markDirty(), stopped: () => this.stopped,
|
|
145
|
-
onApprovalOpen: () => this.pet.event("permission", undefined, this.clock()),
|
|
146
|
-
onAllowed: () => this.pet.event("allowed", undefined, this.clock()),
|
|
147
|
-
});
|
|
148
|
-
setAgentsPainter(drawAgents); // #46: the crew board paints the ∷ mode
|
|
149
|
-
this.loop = new FrameLoop({
|
|
150
|
-
io: this.io, clock: this.clock, state: this.state, theme: () => this.theme, pet: this.pet, truecolor: this.truecolor,
|
|
151
|
-
keyCtx: () => ({ hooks: this.hooks ?? NO_HOOKS, local: this.local }),
|
|
152
|
-
beforeInput: (ev, now) => this.beforeInput(ev, now),
|
|
153
|
-
afterInput: (ev, now) => this.afterInput(ev, now),
|
|
154
|
-
onTick: (now) => this.onTick(now),
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ------------------------------------------------------------------ test/smoke seams
|
|
159
|
-
/** the last painted frame as text ("" before start) */
|
|
160
|
-
frameText(): string { return this.loop.frameText(); }
|
|
161
|
-
/** the frame interval is live */
|
|
162
|
-
get active(): boolean { return this.loop.active; }
|
|
163
|
-
/** frames painted so far (tests: "nothing paints after stop") */
|
|
164
|
-
get frames(): number { return this.loop.frames; }
|
|
165
|
-
get themeName(): ThemeName { return this.theme.name; }
|
|
166
|
-
/** run one frame-loop tick now (tests: deterministic instead of waiting 40 ms) */
|
|
167
|
-
tick(): void { this.loop.tick(); }
|
|
168
|
-
/** parse input held by the ESC-hold timer now (tests) */
|
|
169
|
-
flushInput(): void { this.loop.flushInput(); }
|
|
170
|
-
|
|
171
|
-
// ------------------------------------------------------------------ lifecycle
|
|
172
|
-
start(hooks: RendererHooks, options?: RendererStartOptions): void | Promise<void> {
|
|
173
|
-
if (this.started) return;
|
|
174
|
-
this.started = true; this.hooks = hooks;
|
|
175
|
-
this.state.bootAt = options?.animate === false ? -Infinity : this.clock();
|
|
176
|
-
options?.beforeFirstRender?.();
|
|
177
|
-
const reveal = (): void => {
|
|
178
|
-
if (this.stopped) return;
|
|
179
|
-
// Rebuild at the current size/state before handing off: a resize or file refresh may have landed.
|
|
180
|
-
this.loop.start(() => {
|
|
181
|
-
options?.onReveal?.();
|
|
182
|
-
this.terminalEntered = true;
|
|
183
|
-
this.io.enterRaw();
|
|
184
|
-
this.io.write(enterSequence(true));
|
|
185
|
-
}, options?.beforeReveal !== undefined);
|
|
186
|
-
};
|
|
187
|
-
if (!options?.beforeReveal) { reveal(); return; }
|
|
188
|
-
this.loop.prepare(); // textbox, cursor and all panels really rendered behind the intro
|
|
189
|
-
const files = this.repo.prepare().catch(() => {
|
|
190
|
-
if (!this.stopped) this.addSystemNote("initial file list unavailable — file tools remain available", "warn");
|
|
191
|
-
});
|
|
192
|
-
return Promise.all([files, options.beforeReveal()]).then(reveal);
|
|
193
|
-
}
|
|
194
|
-
private terminalEntered = false;
|
|
195
|
-
|
|
196
|
-
/** clear the interval, settle every open card/picker (deny / null), restore the terminal */
|
|
197
|
-
/** settled once stop() has run AND the repo watcher's git children are gone (sextant-repo.ts stop) */
|
|
198
|
-
private drained: Promise<void> | null = null;
|
|
199
|
-
/** wait for the git children a stop() killed to be gone — call after stop(); a caller that removes
|
|
200
|
-
* the cwd (a test's scratch repo) must await this first, or Windows answers EBUSY */
|
|
201
|
-
drain(): Promise<void> { return this.drained ?? this.repo.stop(); }
|
|
202
|
-
stop(): void {
|
|
203
|
-
if (this.stopped) return;
|
|
204
|
-
this.stopped = true;
|
|
205
|
-
this.loop.stop();
|
|
206
|
-
this.drained = this.repo.stop(); // kills the git children in flight; drain() awaits them
|
|
207
|
-
this.unsubTasks?.(); this.unsubTasks = null;
|
|
208
|
-
this.cards.settleAll();
|
|
209
|
-
this.restoreTerminal();
|
|
210
|
-
this.hooks = null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/** The terminal, and nothing else. `stop()` ends with this; the crash guard (tui/crash-guard.ts)
|
|
214
|
-
* calls it on its own, because everything above in `stop()` — settling cards, killing the repo
|
|
215
|
-
* watcher's git children — can throw, and a throw before the restore leaves the alt screen open with
|
|
216
|
-
* mouse reporting on, which is the state where every pointer movement types escape sequences at the
|
|
217
|
-
* shell prompt. Idempotent (the second call has nothing to undo) and silent on failure: if writing to
|
|
218
|
-
* stdout is itself what broke, there is nothing left to try. */
|
|
219
|
-
restoreTerminal(): void {
|
|
220
|
-
if (!this.terminalEntered || this.restored) return;
|
|
221
|
-
this.restored = true;
|
|
222
|
-
try { this.io.write(leaveSequence()); } catch { /* the pipe may already be gone */ }
|
|
223
|
-
try { this.io.leaveRaw(); } catch { /* ditto */ }
|
|
224
|
-
}
|
|
225
|
-
private restored = false;
|
|
226
|
-
|
|
227
|
-
attach(ctx: SextantAttach): void {
|
|
228
|
-
this.ctx = ctx;
|
|
229
|
-
const s = this.state;
|
|
230
|
-
s.cwd = ctx.cwd; s.repo.name = basename(ctx.cwd) || ctx.cwd;
|
|
231
|
-
if (ctx.petName) this.pet.state.name = ctx.petName.slice(0, 14);
|
|
232
|
-
this.unsubTasks?.();
|
|
233
|
-
const sync = (): void => {
|
|
234
|
-
const list = ctx.tasks.list();
|
|
235
|
-
if (list.length > s.crew.length) s.code.lane = list.length - 1; // the newest lane is selected
|
|
236
|
-
setCrew(s, list); this.loop.markDirty();
|
|
237
|
-
};
|
|
238
|
-
sync();
|
|
239
|
-
this.unsubTasks = ctx.tasks.subscribe((t) => { sync(); petOnTask(this.pet, t, this.clock()); });
|
|
240
|
-
this.repo.afterAttach();
|
|
241
|
-
this.refreshPlan();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// ------------------------------------------------------------------ the event stream (source of truth while busy)
|
|
245
|
-
onEvent(ev: RunEvent): void {
|
|
246
|
-
const now = this.clock(), s = this.state;
|
|
247
|
-
this.applyEv(s, ev, now);
|
|
248
|
-
petOnEvent(this.pet, ev, this.liveCalls, s.cwd, now);
|
|
249
|
-
this.dropNotes += duplicateNotes(ev);
|
|
250
|
-
if (ev.type === "tool_execution_start" && ev.tool === "edit") this.editOps.set(ev.callId, editOpsOf(ev.args));
|
|
251
|
-
if (ev.type === "tool_execution_end" || ev.type === "tool_call_failed") {
|
|
252
|
-
const row = this.rowOf(ev.callId);
|
|
253
|
-
const before = row?.path !== undefined ? this.before.get(row.path) : undefined;
|
|
254
|
-
if (row?.path !== undefined) this.before.delete(row.path);
|
|
255
|
-
const ops = this.editOps.get(ev.callId) ?? [];
|
|
256
|
-
this.editOps.delete(ev.callId);
|
|
257
|
-
if (row && (row.verb === "edit" || row.verb === "write" || row.verb === "remove" || row.verb === "run")) {
|
|
258
|
-
this.repo.afterMutation();
|
|
259
|
-
if (row.path !== undefined && row.path === s.code.file) s.code.content = null; // reload the edited file (before the next paint: onTick)
|
|
260
|
-
this.loadedFile = null;
|
|
261
|
-
if (ev.type === "tool_execution_end" && ev.ok && row.path !== undefined && (row.verb === "edit" || row.verb === "write")) this.repo.scheduleDiff(row, row.path, before, ops);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
this.loop.markDirty();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/** pre-approval (the approver runs BEFORE the tool executes — the one real-time moment): capture the
|
|
268
|
-
* file's current content as the diff base, and let the card's previewDiff text fill the code panel's
|
|
269
|
-
* diff view for the pending file; returns the cwd-relative file (null when the card names none) */
|
|
270
|
-
private previewInCodePanel(tool: string, argsPreview: string, detail: string | undefined): string | null {
|
|
271
|
-
if (tool !== "edit" && tool !== "write") return null;
|
|
272
|
-
const path = argsFromPreview(argsPreview).path;
|
|
273
|
-
if (typeof path !== "string" || !path) return null;
|
|
274
|
-
const file = relPath(this.state.cwd, path);
|
|
275
|
-
this.before.set(file, readFileBounded(this.state.cwd, file));
|
|
276
|
-
const hunks = detail ? hunksFromUnified(detail) : [];
|
|
277
|
-
if (!hunks.length) return file;
|
|
278
|
-
const rows = hunks.flatMap((h) => h.rows);
|
|
279
|
-
this.state.code.diff = { file, hunks, add: rows.filter((r) => r.op === "+").length, del: rows.filter((r) => r.op === "-").length };
|
|
280
|
-
this.state.code.mode = "diff";
|
|
281
|
-
return file;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// ------------------------------------------------------------------ Renderer → state
|
|
285
|
-
setCommands(cmds: SlashCommand[]): void {
|
|
286
|
-
this.state.commands = cmds.map((c) => ({ name: c.name, description: c.description, ...(c.choices ? { choices: c.choices } : {}), ...(c.choicesThen ? { choicesThen: c.choicesThen } : {}) }));
|
|
287
|
-
this.loop.markDirty();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
addUser(text: string): void { this.push(userRow(text, this.clock())); }
|
|
291
|
-
|
|
292
|
-
addSystemNote(text: string, tone: "info" | "warn" | "error" = "info"): void {
|
|
293
|
-
if (this.dropNotes > 0) { this.dropNotes--; return; } // the reducer already made this row from the event
|
|
294
|
-
this.push({ kind: "system", text, tone });
|
|
295
|
-
// toasts: every warning/error, and one-line info notes; a multi-line info note (boot banner, /help,
|
|
296
|
-
// /cost) is a transcript row only — a clipped first line would only smear the top of the code panel
|
|
297
|
-
if (tone !== "info" || (!text.includes("\n") && text.length <= TOAST_MAX)) this.toast(text.split("\n")[0] ?? "", tone);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
beginAssistant(): AssistantView {
|
|
301
|
-
if (this.busy) return NOOP_VIEW; // message_update events stream the row
|
|
302
|
-
const row: Extract<SextantState["messages"][number], { kind: "assistant" }> = { kind: "assistant", text: "", streaming: true };
|
|
303
|
-
this.push(row);
|
|
304
|
-
return { append: (delta) => { row.text += delta; this.loop.markDirty(); }, done: () => { row.streaming = false; this.loop.markDirty(); } };
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
toolStart(callId: string, tool: string, argsPreview: string): void {
|
|
308
|
-
if (this.busy) return;
|
|
309
|
-
this.push(replayToolRow(callId, tool, argsPreview, this.state.cwd));
|
|
310
|
-
}
|
|
311
|
-
toolUpdate(callId: string, note: string): void {
|
|
312
|
-
if (this.busy) return;
|
|
313
|
-
const row = this.rowOf(callId);
|
|
314
|
-
if (row) { row.detail = clipText(note, 80); this.loop.markDirty(); }
|
|
315
|
-
}
|
|
316
|
-
toolEnd(callId: string, ok: boolean, outputPreview: string, durationMs: number): void {
|
|
317
|
-
if (this.busy) return;
|
|
318
|
-
let row = this.rowOf(callId);
|
|
319
|
-
if (!row) { row = replayToolRow(callId, "tool", "{}", this.state.cwd); this.push(row); }
|
|
320
|
-
row.running = false; row.ok = ok; row.ms = durationMs;
|
|
321
|
-
const end = summarizeEnd({ verb: row.verb }, row.tool, ok, outputPreview);
|
|
322
|
-
if (end.detail) row.detail = end.detail;
|
|
323
|
-
this.loop.markDirty();
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/** the approval card (sextant-cards.ts); an edit/write preview also fills the code panel's diff view.
|
|
327
|
-
* A denied call never executes (no row, no end event), so its captured base is dropped here — a later
|
|
328
|
-
* ungated edit of the same file must not diff against that stale snapshot */
|
|
329
|
-
askApproval(tool: string, argsPreview: string, detail?: string): Promise<ApprovalAnswer> {
|
|
330
|
-
const file = this.previewInCodePanel(tool, argsPreview, detail);
|
|
331
|
-
const answer = this.cards.approval(tool, argsPreview, detail);
|
|
332
|
-
if (file !== null) void answer.then((a) => { if (a === "deny") this.before.delete(file); });
|
|
333
|
-
return answer;
|
|
334
|
-
}
|
|
335
|
-
/** the palette as a picker (sextant-cards.ts) */
|
|
336
|
-
pickOne(items: PickItem[], title?: string): Promise<string | null> { return this.cards.pick(items, title); }
|
|
337
|
-
/** the question card (sextant-cards.ts): `signal` abort dismisses it, a second concurrent ask is rejected */
|
|
338
|
-
askQuestion(q: QuestionPrompt, signal?: AbortSignal): Promise<QuestionAnswer | null> { return this.cards.question(q, signal); }
|
|
339
|
-
|
|
340
|
-
clearTranscript(): void { this.state.messages = []; this.state.stick = true; this.pet.event("fresh", undefined, this.clock()); this.loop.markDirty(); }
|
|
341
|
-
|
|
342
|
-
prefillEditor(text: string): void {
|
|
343
|
-
const I = this.state.input;
|
|
344
|
-
I.text = text; I.cur = text.length; I.sgSel = 0; I.histIdx = -1; this.state.focus = "messages"; this.loop.markDirty();
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/** busy = a run is in flight: the event stream owns the rows. setBusy(false) after a run the
|
|
348
|
-
* reducer never saw end (an interrupted generator yields no run_end) settles what run_end would have. */
|
|
349
|
-
setBusy(busy: boolean, label?: string): void {
|
|
350
|
-
const s = this.state, now = this.clock();
|
|
351
|
-
this.busy = busy;
|
|
352
|
-
if (busy) { s.running = true; if (label) s.activity.label = label.replace(/…$/, ""); }
|
|
353
|
-
else {
|
|
354
|
-
this.dropNotes = 0;
|
|
355
|
-
if (s.running) {
|
|
356
|
-
s.running = false;
|
|
357
|
-
if (s.activity.startedAt !== null && s.activity.endedAt === null) s.activity.endedAt = now;
|
|
358
|
-
if (s.activity.state !== "SUCCESS" && s.activity.state !== "ERROR") { s.activity.state = "IDLE"; s.activity.label = "stopped"; }
|
|
359
|
-
for (const r of s.messages) if (r.kind === "tool" && r.running) { r.running = false; r.ok = false; r.detail ??= "interrupted"; }
|
|
360
|
-
this.pet.event("stopped", undefined, now);
|
|
361
|
-
}
|
|
362
|
-
s.escUntil = 0; delete s.ctrlCUntil;
|
|
363
|
-
}
|
|
364
|
-
this.loop.markDirty();
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
setStatus(info: StatusInfo): void {
|
|
368
|
-
const s = this.state;
|
|
369
|
-
s.yolo = info.yolo; s.acceptEdits = info.permission === "accept-edits"; if (info.mode) s.mode = info.mode;
|
|
370
|
-
const u = this.ctx?.usage?.();
|
|
371
|
-
setUsage(s, {
|
|
372
|
-
provider: info.provider, model: info.model, turns: info.turns, tokensIn: info.tokensIn, tokensOut: info.tokensOut,
|
|
373
|
-
...(info.effort !== undefined ? { effort: info.effort } : {}),
|
|
374
|
-
contextTokens: u?.contextTokens ?? 0, contextWindow: this.ctx?.contextWindow(), costUsd: u ? u.costUsd : null,
|
|
375
|
-
});
|
|
376
|
-
this.refreshPlan();
|
|
377
|
-
this.loop.markDirty();
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// ------------------------------------------------------------------ internals
|
|
381
|
-
private push(row: SextantState["messages"][number]): void { this.state.messages.push(row); this.state.stick = true; this.loop.markDirty(); }
|
|
382
|
-
private rowOf(callId: string): ToolRow | undefined {
|
|
383
|
-
for (let i = this.state.messages.length - 1; i >= 0; i--) { const r = this.state.messages[i]!; if (r.kind === "tool" && r.callId === callId) return r; }
|
|
384
|
-
return undefined;
|
|
385
|
-
}
|
|
386
|
-
private toast(text: string, tone: "info" | "warn" | "error" = "info"): void { pushToast(this.state, clipText(text.trim(), TOAST_MAX), this.clock(), tone); this.loop.markDirty(); }
|
|
387
|
-
|
|
388
|
-
/** the session's todos.json → plan panel; a newly completed step is a pet quip */
|
|
389
|
-
private refreshPlan(): void {
|
|
390
|
-
if (!this.ctx) return;
|
|
391
|
-
setPlan(this.state, loadTodos(join(this.ctx.sessionsDir, this.ctx.store().id)));
|
|
392
|
-
const c = planCounts(this.state);
|
|
393
|
-
if (this.planLoaded && c.completed > this.todoDone) this.pet.event("todo_done", { d: c.completed, n: c.total }, this.clock());
|
|
394
|
-
this.planLoaded = true; this.todoDone = c.completed;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/** the renderer-owned effects keys.ts fires after writing the state field (theme rebuild, file/diff loading, toasts) */
|
|
398
|
-
private readonly local: KeyCtx["local"] = {
|
|
399
|
-
setTheme: (name) => { this.theme = buildTheme(name); this.state.theme = name; this.toast(`theme · ${name}`); this.pet.event("theme", undefined, this.clock()); },
|
|
400
|
-
// port #54: an @image mention stages onto the store the surface attached (SextantAttach.attachImage)
|
|
401
|
-
attachImage: (abs) => this.ctx?.attachImage?.(abs),
|
|
402
|
-
setMode: (mode) => { if (mode === "diff" && this.state.code.file) this.repo.loadDiff(this.state.code.file); },
|
|
403
|
-
openFile: (path) => { this.state.code.content = readFileBounded(this.state.cwd, path); this.state.code.hl = null; this.loadedFile = path; },
|
|
404
|
-
toast: (text) => this.toast(text),
|
|
405
|
-
// the market: the overlay is opened at once in its loading state so the frame after ⌃m already shows
|
|
406
|
-
// the box, and the catalog is joined in when the module answers (it may touch the network)
|
|
407
|
-
openMarket: () => {
|
|
408
|
-
openMarket(this.state, [], { kind: "loading" });
|
|
409
|
-
this.loop.markDirty();
|
|
410
|
-
void loadMarket(this.state.cwd, homedir())
|
|
411
|
-
.then((load) => { if (this.state.market) { this.state.market.rows = load.rows; this.state.market.status = load.status; this.state.market.notes = load.notes; this.loop.markDirty(); } })
|
|
412
|
-
.catch((e: unknown) => { if (this.state.market) { this.state.market.status = { kind: "error", reason: e instanceof Error ? e.message : String(e) }; this.loop.markDirty(); } });
|
|
413
|
-
},
|
|
414
|
-
// /context (⌃g). The panel opens on the NEXT frame with what we already have — the model and an
|
|
415
|
-
// empty count — so the chord feels instant, and the real numbers are joined in when counting is
|
|
416
|
-
// done. Counting is synchronous but not free on a long transcript, so it happens off the frame.
|
|
417
|
-
openWizard: () => {
|
|
418
|
-
const rows = this.ctx?.wizardProviders?.();
|
|
419
|
-
if (!rows || rows.length === 0) { this.toast("no provider table behind this surface"); return; }
|
|
420
|
-
openWizard(this.state, rows satisfies WizardProvider[]);
|
|
421
|
-
this.loop.markDirty();
|
|
422
|
-
},
|
|
423
|
-
openContext: () => {
|
|
424
|
-
const ref = this.ctx?.model() ?? { provider: "unknown", model: "unknown" };
|
|
425
|
-
const inputs = this.ctx?.contextInputs?.();
|
|
426
|
-
openContext(this.state, {
|
|
427
|
-
model: `${ref.provider}/${ref.model}`, raw: 0, estimated: 0,
|
|
428
|
-
scale: { factor: 1, measured: false, note: "counting…" },
|
|
429
|
-
slices: [], images: 0, billed: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
430
|
-
unpricedTurns: 0, live: false, tolerance: 0.05,
|
|
431
|
-
});
|
|
432
|
-
this.loop.markDirty();
|
|
433
|
-
if (!inputs) return; // the panel stays in its "not counted" state and says so
|
|
434
|
-
void loadContext({
|
|
435
|
-
messages: inputs.messages, current: ref, lookup: inputs.lookup,
|
|
436
|
-
fixed: fixedFrom(inputs.runtime, ref),
|
|
437
|
-
}).then((state) => {
|
|
438
|
-
// the human may have closed it while we counted; writing into a closed panel would reopen it
|
|
439
|
-
if (!this.state.context) return;
|
|
440
|
-
this.state.context = { ...state, scroll: this.state.context.scroll };
|
|
441
|
-
this.loop.markDirty();
|
|
442
|
-
}).catch(() => { /* loadContext does not reject; this is belt and braces on the dynamic import */ });
|
|
443
|
-
},
|
|
444
|
-
marketPlan: (row, local) => {
|
|
445
|
-
void planFor(row, { scope: "user", cwd: this.state.cwd, home: homedir() }, local)
|
|
446
|
-
.then((plan) => {
|
|
447
|
-
const m = this.state.market;
|
|
448
|
-
if (!m) return;
|
|
449
|
-
if ("error" in plan) { this.toast(plan.error); return; }
|
|
450
|
-
m.plan = plan;
|
|
451
|
-
this.loop.markDirty();
|
|
452
|
-
});
|
|
453
|
-
},
|
|
454
|
-
// the connect wizard (draw-wizard.ts): the overlay owns the WALK, the attach seams own the
|
|
455
|
-
// registry — a surface without them (the smoke harness) degrades to honest errors, the same
|
|
456
|
-
// way reloadMcp does
|
|
457
|
-
wizardRegister: (id, url, protocol) => {
|
|
458
|
-
const w = this.state.wizard;
|
|
459
|
-
const r = this.ctx?.wizardRegister?.(id, url, protocol);
|
|
460
|
-
if (!w) return;
|
|
461
|
-
// on success the chosen row IS this endpoint now — the walk continues at KEY with a live row
|
|
462
|
-
wizardEndpointRegistered(w, r?.ok ?? false, r?.error ?? "this surface cannot register endpoints — use rovecode provider add");
|
|
463
|
-
if (r?.ok) w.providers[w.selected] = { key: id, label: `${id} — ${url}`, configured: false };
|
|
464
|
-
this.loop.markDirty();
|
|
465
|
-
},
|
|
466
|
-
wizardStoreKey: (provider, secret) => {
|
|
467
|
-
const w = this.state.wizard;
|
|
468
|
-
const r = this.ctx?.wizardStoreKey?.(provider, secret);
|
|
469
|
-
if (!w) return;
|
|
470
|
-
wizardKeyStored(w, r?.ok ?? false, r?.error ?? "this surface cannot store keys — use rovecode auth set");
|
|
471
|
-
// a stored key is what makes the endpoint ASKABLE: the model step that just opened fetches now
|
|
472
|
-
if (r?.ok) this.fetchWizardModels();
|
|
473
|
-
this.loop.markDirty();
|
|
474
|
-
},
|
|
475
|
-
wizardLoadModels: () => { this.fetchWizardModels(); },
|
|
476
|
-
// confirmModels: pin the checked rows as the provider's active list + name the default. The
|
|
477
|
-
// seam (SextantAttach.wizardActivateModels → app.ts activateModels → registry setModels) is
|
|
478
|
-
// Berkay's; this only runs it and advances on its answer. The confirmed id rides the state
|
|
479
|
-
// (modelDefaultTo), so a walk back to re-edit the list keeps the marker where the person left it.
|
|
480
|
-
wizardActivate: (provider, models, makeDefault) => {
|
|
481
|
-
const w = this.state.wizard;
|
|
482
|
-
if (!w) return;
|
|
483
|
-
const r = this.ctx?.wizardActivateModels?.(provider, models, makeDefault);
|
|
484
|
-
if (r?.ok) {
|
|
485
|
-
closeWizard(this.state); // the walk is DONE: the list is pinned, the default is set
|
|
486
|
-
this.toast(`connected — ${r.models !== undefined && r.models.length > 0 ? `${r.models.length} models active` : "all models live"}`);
|
|
487
|
-
} else {
|
|
488
|
-
w.error = r?.error ?? "the model list could not be written";
|
|
489
|
-
}
|
|
490
|
-
this.loop.markDirty();
|
|
491
|
-
},
|
|
492
|
-
wizardModel: (model) => {
|
|
493
|
-
const w = this.state.wizard;
|
|
494
|
-
if (!w) return;
|
|
495
|
-
const provider = w.providers[w.selected]?.key ?? "";
|
|
496
|
-
void (this.ctx?.wizardModels?.(provider) ?? Promise.resolve([]))
|
|
497
|
-
.then((models) => {
|
|
498
|
-
if (!this.state.wizard) return;
|
|
499
|
-
if (models.length === 0) { closeWizard(this.state); this.toast(`connected — ${model} pinned by hand`); } // the typed fallback IS the finish
|
|
500
|
-
else wizardModelConfirmed(this.state.wizard, models);
|
|
501
|
-
this.loop.markDirty();
|
|
502
|
-
})
|
|
503
|
-
.catch(() => { if (this.state.wizard) { this.state.wizard.error = "the model list could not be read — type the id instead"; this.loop.markDirty(); } });
|
|
504
|
-
},
|
|
505
|
-
marketDocs: (row) => {
|
|
506
|
-
void docsFor(row).then((docs) => {
|
|
507
|
-
const m = this.state.market;
|
|
508
|
-
if (!m || !docs) return;
|
|
509
|
-
// write onto the row the list holds, so closing and reopening the pane costs nothing
|
|
510
|
-
const target = m.rows.find((r) => r.kind === row.kind && r.id === row.id);
|
|
511
|
-
if (target) target.docs = docs;
|
|
512
|
-
this.loop.markDirty();
|
|
513
|
-
});
|
|
514
|
-
},
|
|
515
|
-
marketInstall: (row, local) => {
|
|
516
|
-
// `local` is the approved card's own answer (keys.ts hands over req.plan.local): npm runs only now
|
|
517
|
-
void install(row, { scope: "user", cwd: this.state.cwd, home: homedir() }, local === true ? { local: true } : {}).then(async (outcome) => {
|
|
518
|
-
// An MCP server lands in a file this session already read, so the install used to end in
|
|
519
|
-
// "restart rovecode" — a poor answer to "I just installed it". Ask the runtime to re-read the
|
|
520
|
-
// files and connect what is new, and report what actually happened instead of what to do next.
|
|
521
|
-
// Only on success, only for MCP, and never fatal: a reload that throws leaves the install
|
|
522
|
-
// reported exactly as it was, because the write DID happen and saying otherwise would be worse.
|
|
523
|
-
let text = outcome.text;
|
|
524
|
-
if (outcome.ok && row.kind === "mcp" && this.ctx?.reloadMcp) {
|
|
525
|
-
try {
|
|
526
|
-
const r = await this.ctx.reloadMcp();
|
|
527
|
-
const failed = r.failed.find((f) => r.added.includes(f.name));
|
|
528
|
-
text = text.replace(/ · restart rovecode[^·]*/, "");
|
|
529
|
-
// `skipped` before `added`: an entry the loader refused never reaches `added`, so without this
|
|
530
|
-
// the install read "installed" and then said nothing at all about why no tools appeared.
|
|
531
|
-
const skipped = r.skipped.length > 0 ? ` · ${r.skipped.join(" · ")}` : "";
|
|
532
|
-
text += failed
|
|
533
|
-
? ` · ${failed.name} did not connect: ${failed.error}`
|
|
534
|
-
: r.added.length > 0 ? ` · connected as ${r.added.join(", ")} — no restart needed${skipped}` : skipped;
|
|
535
|
-
} catch { /* the install stands on its own */ }
|
|
536
|
-
}
|
|
537
|
-
const m = this.state.market;
|
|
538
|
-
if (!m?.plan) return;
|
|
539
|
-
m.plan.running = false;
|
|
540
|
-
m.plan.outcome = { ...outcome, text };
|
|
541
|
-
this.loop.markDirty();
|
|
542
|
-
});
|
|
543
|
-
},
|
|
544
|
-
};
|
|
545
|
-
|
|
546
|
-
/** renderer-level intercepts: the picker's Enter/Esc, and the two-press ⌃c guarantee (a ⌃c while busy
|
|
547
|
-
* interrupts through keys.ts and arms ctrlCUntil; a second one inside the window quits even before the
|
|
548
|
-
* interrupted run settled — keys.ts alone would interrupt again while s.running is still true) */
|
|
549
|
-
private beforeInput(ev: InputEvent, now: number): boolean {
|
|
550
|
-
const s = this.state;
|
|
551
|
-
if (this.cards.interceptKey(ev)) return true;
|
|
552
|
-
if (ev.type === "key" && ev.ctrl && ev.name === "c" && s.running && s.ctrlCUntil !== undefined && now < s.ctrlCUntil) { this.hooks?.onExit(); return true; }
|
|
553
|
-
return false;
|
|
554
|
-
}
|
|
555
|
-
private afterInput(ev: InputEvent, now: number): void {
|
|
556
|
-
const s = this.state;
|
|
557
|
-
this.cards.afterKey();
|
|
558
|
-
if (ev.type === "key" && ev.ctrl && ev.name === "c" && s.running) s.ctrlCUntil = now + ESC_WINDOW_MS;
|
|
559
|
-
if (ev.type === "key" && !ev.ctrl && s.input.text.startsWith("/")) { const first = suggestions(s, s.files.paths, fuzzy)[0]; if (first?.kind === "slash") this.pet.suggest(first.label, now); }
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/** off-frame work, BEFORE the tick's paint: the code panel's file content (a bounded sync read, so
|
|
563
|
-
* the frame that follows an edit shows the file, never `cannot read`) and the repo scan (full after
|
|
564
|
-
* a mutating tool, statuses while idle — async, sextant-repo.ts) */
|
|
565
|
-
private onTick(now: number): void {
|
|
566
|
-
const s = this.state;
|
|
567
|
-
if (s.code.file && s.code.content === null && this.loadedFile !== s.code.file) { s.code.content = readFileBounded(s.cwd, s.code.file); this.loadedFile = s.code.file; this.loop.markDirty(); }
|
|
568
|
-
// the staged images are owned by the store (tui/attach.ts); mirror their names so the prompt shows
|
|
569
|
-
// chips before the message is sent — one read per tick, never a second copy of the list
|
|
570
|
-
const staged = this.ctx?.staged?.() ?? [];
|
|
571
|
-
if (staged.length !== s.staged.length || staged.some((n, i) => n !== s.staged[i])) { s.staged = staged; this.loop.markDirty(); }
|
|
572
|
-
this.repo.onTick(now, s.running);
|
|
573
|
-
}
|
|
574
|
-
}
|