rovecode 0.3.3 → 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 -48
- 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 +83 -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/sextant-smoke.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/** `rovecode smoke-tui --sextant` (port #44): the sextant surface end-to-end through the real pipeline —
|
|
2
|
-
* agentLoop → SextantRenderer → Screen → an in-memory TerminalIO at 160×44 (no @xterm/headless, so it
|
|
3
|
-
* also runs from an installed tree or the compiled binary). Gated session: a scripted write, then an
|
|
4
|
-
* anchored edit of that file, each raise the approval card and are allowed once the card is on
|
|
5
|
-
* screen; a text turn closes the run; ⌃c quits and the terminal must be restored (leave sequence in
|
|
6
|
-
* the output tail, frame interval cleared). Prints the edit's card frame and the final frame; the CLI
|
|
7
|
-
* wrapper exits 0 iff every stage rendered. */
|
|
8
|
-
|
|
9
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
-
import { tmpdir } from "node:os";
|
|
11
|
-
import { join } from "node:path";
|
|
12
|
-
import { fileTag, lineHash } from "../coding/hashline.ts";
|
|
13
|
-
import { mockStream, textTurn, toolTurn } from "../providers/stream.ts";
|
|
14
|
-
import { SextantRenderer } from "../sextant/sextant-renderer.ts";
|
|
15
|
-
import { runTui } from "./app.ts";
|
|
16
|
-
import { MemoryIO } from "./sextant-io.ts";
|
|
17
|
-
|
|
18
|
-
export interface SextantSmokeResult {
|
|
19
|
-
ok: boolean;
|
|
20
|
-
reasons: string[];
|
|
21
|
-
/** the frame with the edit's approval card on screen */
|
|
22
|
-
cardFrame: string;
|
|
23
|
-
/** the final frame */
|
|
24
|
-
frame: string;
|
|
25
|
-
/** the last 400 bytes written to the terminal (the leave sequence lives here) */
|
|
26
|
-
tail: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
|
|
30
|
-
/** the two approval cards, in order: the write's, then the edit's */
|
|
31
|
-
const CARDS: readonly RegExp[] = [/needs your permission\s+write\b/, /needs your permission\s+edit\b/];
|
|
32
|
-
/** what quitting must leave in the output: main screen, cursor shown, SGR mouse off, bracketed paste off */
|
|
33
|
-
const LEAVE: readonly string[] = ["\x1b[?1049l", "\x1b[?25h", "\x1b[?1006l", "\x1b[?2004l"];
|
|
34
|
-
|
|
35
|
-
/** drive the surface headlessly and report; never exits the process (runSextantSmoke does) */
|
|
36
|
-
export async function sextantSmoke(opts: { cols?: number; rows?: number; deadlineMs?: number } = {}): Promise<SextantSmokeResult> {
|
|
37
|
-
const cwd = mkdtempSync(join(tmpdir(), "rovecode-sextant-smoke-"));
|
|
38
|
-
const io = new MemoryIO(opts.cols ?? 160, opts.rows ?? 44, { COLORTERM: "truecolor" });
|
|
39
|
-
const renderer = new SextantRenderer({ io, cwd, pet: "rovecode" });
|
|
40
|
-
const probe = join(cwd, "smoke.txt");
|
|
41
|
-
const written = "smoke-ok\n";
|
|
42
|
-
const stream = mockStream({
|
|
43
|
-
turns: [
|
|
44
|
-
toolTurn([{ id: "t1", tool: "write", args: { path: probe, content: written } }]),
|
|
45
|
-
toolTurn([{ id: "t2", tool: "edit", args: { path: probe, edits: [{ tag: fileTag(written), anchorLine: 1, anchorHash: lineHash("smoke-ok"), newLines: ["smoke-edited"] }] } }]),
|
|
46
|
-
textTurn("Smoke OK — the sextant surface rendered a write, an anchored edit and this summary."),
|
|
47
|
-
],
|
|
48
|
-
});
|
|
49
|
-
const reasons: string[] = [];
|
|
50
|
-
// The whole assertion below is "two approval cards appear", so the permission level is pinned here rather
|
|
51
|
-
// than inherited from the machine: a ~/.rovecode/settings.json with "permission":"auto" allowed the write
|
|
52
|
-
// and the edit without a card and the smoke FAILED with "approval cards seen 0/2". `yolo: false` cannot
|
|
53
|
-
// pin it — app.ts treats false as "no flag" and lets the settings file win; `permission` is the flag rung.
|
|
54
|
-
const app = runTui({ renderer, stream, cwd, permission: "ask", exitOnClose: false, model: "scripted" });
|
|
55
|
-
io.feed("hello rovecode\r");
|
|
56
|
-
const deadline = Date.now() + (opts.deadlineMs ?? 15_000);
|
|
57
|
-
const cardFrames: string[] = [];
|
|
58
|
-
let frame = "", done = false;
|
|
59
|
-
while (Date.now() < deadline) {
|
|
60
|
-
renderer.tick();
|
|
61
|
-
frame = renderer.frameText();
|
|
62
|
-
if (cardFrames.length === CARDS.length && frame.includes("Smoke OK") && !frame.includes("needs your permission")) { done = true; break; }
|
|
63
|
-
const want = CARDS[cardFrames.length];
|
|
64
|
-
if (want !== undefined && want.test(frame) && frame.includes("allow")) { cardFrames.push(frame); io.feed("\r"); } // Enter = allow once
|
|
65
|
-
await sleep(25);
|
|
66
|
-
}
|
|
67
|
-
// the boot reveal paints panels in 90 ms steps after bootAt (frame.ts REVEAL_STEP_MS); a fast host
|
|
68
|
-
// finishes the scripted run before the right column and the pet have appeared — give the reveal its time
|
|
69
|
-
const PANELS = ["─ files ─", "─ code ─", "─ messages ─", "─ plan ─", "─ usage ─", "─ rovecode ─"];
|
|
70
|
-
const shown = (p: string): boolean => frame.includes(p) || (p === "─ code ─" && frame.includes("─ diff ─"));
|
|
71
|
-
const revealBy = Date.now() + 2_000;
|
|
72
|
-
while (done && Date.now() < revealBy && !PANELS.every(shown)) { await sleep(25); renderer.tick(); frame = renderer.frameText(); }
|
|
73
|
-
if (!done) reasons.push(`final frame not reached (approval cards seen ${cardFrames.length}/${CARDS.length})`);
|
|
74
|
-
if (!/~ edit\s+smoke\.txt/.test(frame)) reasons.push("no `~ edit smoke.txt` tool row in the messages panel");
|
|
75
|
-
if (!/\+ write\s+smoke\.txt/.test(frame)) reasons.push("no `+ write smoke.txt` tool row in the messages panel");
|
|
76
|
-
for (const p of PANELS) if (!shown(p)) reasons.push(`panel ${p.trim()} missing at ${io.cols}×${io.rows}`);
|
|
77
|
-
const wasActive = renderer.active;
|
|
78
|
-
io.feed("\x03");
|
|
79
|
-
await app;
|
|
80
|
-
const tail = io.output().slice(-400);
|
|
81
|
-
for (const seq of LEAVE) if (!tail.includes(seq)) reasons.push(`the output tail lacks ${JSON.stringify(seq)} — terminal not restored`);
|
|
82
|
-
if (!wasActive) reasons.push("the frame interval was not running before quit");
|
|
83
|
-
if (renderer.active) reasons.push("the frame interval survived quit");
|
|
84
|
-
// the repo watcher's git children must be GONE before the scratch repo goes (sextant-renderer drain);
|
|
85
|
-
// on Windows a live `git status` holds its cwd and rmSync answers EBUSY — the tui-sextant "flake".
|
|
86
|
-
// The retry is the belt to that brace: handle release on Windows lags the process exit itself.
|
|
87
|
-
await renderer.drain().catch(() => {});
|
|
88
|
-
for (let attempt = 0; ; attempt++) {
|
|
89
|
-
try { rmSync(cwd, { recursive: true, force: true }); break; }
|
|
90
|
-
catch (e) {
|
|
91
|
-
const code = (e as NodeJS.ErrnoException).code;
|
|
92
|
-
if ((code !== "EBUSY" && code !== "EPERM" && code !== "ENOTEMPTY") || attempt >= 20) throw e;
|
|
93
|
-
await sleep(25);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return { ok: reasons.length === 0, reasons, cardFrame: cardFrames[1] ?? cardFrames[0] ?? "(no approval card rendered)", frame, tail };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export async function runSextantSmoke(): Promise<void> {
|
|
100
|
-
const r = await sextantSmoke();
|
|
101
|
-
console.log("── sextant: approval card for the anchored edit (160x44) ──────────────────────");
|
|
102
|
-
console.log(r.cardFrame);
|
|
103
|
-
console.log("── sextant: final frame (160x44) ────────────────────────────────────────────────");
|
|
104
|
-
console.log(r.frame);
|
|
105
|
-
console.log("─────────────────────────────────────────────────────────────────────────────────");
|
|
106
|
-
console.log(r.ok
|
|
107
|
-
? "smoke-tui --sextant: PASS (files · code · messages · plan · usage · rovecode + two approval cards through the full pipeline; terminal restored on quit)"
|
|
108
|
-
: `smoke-tui --sextant: FAIL — ${r.reasons.join("; ")}`);
|
|
109
|
-
process.exit(r.ok ? 0 : 1);
|
|
110
|
-
}
|
package/src/tui/shell-cmd.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/** `!cmd` shell mode: a prompt line that starts with `!` runs in the workspace shell through the ONE bash tool
|
|
2
|
-
* dispatch — ToolRegistry.dispatch with the session's permission rules and the very approval chain a model-issued
|
|
3
|
-
* `bash` call gets (permission rules → execpolicy → the approval hook → the human's card, cli/runtime.ts buildCfg)
|
|
4
|
-
* behind the configured executor rung. Never a direct spawn, never a second policy path: the composer's `!` is a
|
|
5
|
-
* convenience over the same door, not a way around it. The caller passes ITS human approver, so the surface's own
|
|
6
|
-
* behaviour — the diff preview, the "all edits" door — applies here too rather than being reimplemented.
|
|
7
|
-
*
|
|
8
|
-
* No model turn starts. The command and its output are appended to the session as a user message the model reads
|
|
9
|
-
* on its NEXT turn (shellRecord), so `!bun test` followed by "fix it" gives the model the failure without a tool
|
|
10
|
-
* round-trip. Both surfaces share this module: a renderer with `onEvent` (sextant) builds its rows from the
|
|
11
|
-
* dispatch events; one without (the classic pi-tui) gets the tool card pair plus a bounded output block.
|
|
12
|
-
*
|
|
13
|
-
* Pattern source (Apache-2.0, PATTERN ONLY, no code copied): gemini-cli packages/cli/src/ui/hooks/
|
|
14
|
-
* useGeminiStream.ts (shell mode short-circuits the prompt before any model call) and atCommandProcessor.ts (a
|
|
15
|
-
* client-initiated tool call whose result is folded into the user turn).
|
|
16
|
-
*
|
|
17
|
-
* Rules: `!` alone, `! x` (a space after the bang) and `!!x` are plain text for the model — the ONE classifier is
|
|
18
|
-
* sextant/overlays.ts parseInput; a busy run refuses the line (the record would fork the run's chain); staged
|
|
19
|
-
* /attach images never ride on a shell record; and a call refused BEFORE execution (a rule, execpolicy, a hook, a
|
|
20
|
-
* denied card) leaves no record at all — the model learns only what actually ran. */
|
|
21
|
-
|
|
22
|
-
import { randomUUID } from "node:crypto";
|
|
23
|
-
import type { Runtime } from "../cli/runtime.ts";
|
|
24
|
-
import type { SessionStore } from "../core/session.ts";
|
|
25
|
-
import type { ApprovalFn, Message, PermissionLevel, RunEvent, ToolCallPart, ToolContext, ToolOutput } from "../core/types.ts";
|
|
26
|
-
import { parseInput } from "../sextant/overlays.ts";
|
|
27
|
-
import type { Renderer } from "./renderer.ts";
|
|
28
|
-
|
|
29
|
-
/** the classic block shows this many output lines; the session record keeps the tool's full (≤10k) text */
|
|
30
|
-
export const OUTPUT_BLOCK_LINES = 40;
|
|
31
|
-
const OPEN = "<user_shell_command>", CLOSE = "</user_shell_command>", OUT_OPEN = "<user_shell_output", OUT_CLOSE = "</user_shell_output>";
|
|
32
|
-
|
|
33
|
-
/** The command of a `!cmd` line, or null when the line is plain text (sextant/overlays.ts parseInput). */
|
|
34
|
-
export function shellLine(text: string): string | null {
|
|
35
|
-
const p = parseInput(text);
|
|
36
|
-
return p.kind === "shell" && p.cmd ? p.cmd : null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface ShellCtx {
|
|
40
|
-
renderer: Renderer;
|
|
41
|
-
rt: Pick<Runtime, "registry" | "hooks" | "buildCfg" | "cwd">;
|
|
42
|
-
/** the ACTIVE store, read live */
|
|
43
|
-
store(): SessionStore;
|
|
44
|
-
/** the session's permission level, exactly as a model turn would run under */
|
|
45
|
-
level(): PermissionLevel;
|
|
46
|
-
/** the surface's own human approver (undefined under `auto`, where nothing is asked) */
|
|
47
|
-
approve(): ApprovalFn | undefined;
|
|
48
|
-
/** the app's busy flag (a live run, or another `!cmd`) */
|
|
49
|
-
busy(): boolean;
|
|
50
|
-
/** flip the app's busy flag (+ status) around the command */
|
|
51
|
-
setBusy(b: boolean): void;
|
|
52
|
-
/** the app's interrupt target — Esc / ⌃c / quit abort it; bound for the command's lifetime, then cleared */
|
|
53
|
-
bindAbort(ac: AbortController | null): void;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const head = (cmd: string): string => { const one = cmd.replace(/\s+/g, " ").trim(); return one.length > 40 ? one.slice(0, 39) + "…" : one; };
|
|
57
|
-
|
|
58
|
-
type FailedEvent = Extract<RunEvent, { type: "tool_call_failed" }>;
|
|
59
|
-
export const REFUSED_DETAIL = "refused by a permission rule, exec policy or an approval hook (no approval prompt)";
|
|
60
|
-
|
|
61
|
-
/** the event BOTH surfaces get for a call refused before execution. `asked` = the human saw the card and denied —
|
|
62
|
-
* the raw event stands, its "user denied" is true; otherwise the chain settled it alone (a forbidden argv, a hook
|
|
63
|
-
* deny) and dispatch's generic "user denied" would misreport a policy stop as the person's choice. */
|
|
64
|
-
function refusal(ev: FailedEvent, asked: boolean): FailedEvent {
|
|
65
|
-
return !asked && ev.detail === "user denied" ? { ...ev, detail: REFUSED_DETAIL } : ev;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Run one `!cmd` line. Echoes the typed line, dispatches the bash tool with the session's rules and approval
|
|
69
|
-
* chain, records what ran, settles the surface — and never starts a model turn. */
|
|
70
|
-
export async function runShellLine(ctx: ShellCtx, line: string): Promise<void> {
|
|
71
|
-
const cmd = shellLine(line);
|
|
72
|
-
if (cmd === null) return;
|
|
73
|
-
const { renderer } = ctx;
|
|
74
|
-
if (ctx.busy()) { renderer.addSystemNote("finish or interrupt the run first (Esc) — `!cmd` runs only while the agent is idle", "warn"); return; }
|
|
75
|
-
renderer.addUser(line.trim());
|
|
76
|
-
const ac = new AbortController();
|
|
77
|
-
ctx.setBusy(true);
|
|
78
|
-
ctx.bindAbort(ac);
|
|
79
|
-
renderer.setBusy(true, `running ${head(cmd)}…`);
|
|
80
|
-
const call: ToolCallPart = { kind: "tool_call", id: `shell-${randomUUID().slice(0, 8)}`, tool: "bash", args: { command: cmd } };
|
|
81
|
-
let asked = false, executed = false;
|
|
82
|
-
// the SAME chain a model-issued bash call gets: buildCfg wraps the surface's approver in the rules, execpolicy
|
|
83
|
-
// and the hooks. The wrapper only records that the human was reached, so the refusal text can tell the truth.
|
|
84
|
-
const human = ctx.approve();
|
|
85
|
-
const cfg = ctx.rt.buildCfg(ctx.level(), human === undefined ? undefined : async (req) => { asked = true; return human(req); });
|
|
86
|
-
const toolCtx: ToolContext = { sessionId: ctx.store().id, cwd: ctx.rt.cwd, signal: ac.signal, permissions: { effect: "allow" } };
|
|
87
|
-
// onEvent FIRST — a renderer that has it owns the rows and ignores the card calls below; the classic surface
|
|
88
|
-
// paints its `→ bash` / `← ok|FAIL` pair from them. Both are built from the ONE corrected event.
|
|
89
|
-
const emit = (raw: RunEvent): void => {
|
|
90
|
-
const ev = raw.type === "tool_call_failed" ? refusal(raw, asked) : raw;
|
|
91
|
-
renderer.onEvent?.(ev);
|
|
92
|
-
if (ev.type === "tool_execution_start") renderer.toolStart(ev.callId, ev.tool, JSON.stringify(ev.args).slice(0, 120));
|
|
93
|
-
else if (ev.type === "tool_execution_update") renderer.toolUpdate(ev.callId, ev.note);
|
|
94
|
-
else if (ev.type === "tool_execution_end") { executed = true; renderer.toolEnd(ev.callId, ev.ok, ev.output.slice(0, 160).replace(/\n/g, " ⏎ "), ev.durationMs); }
|
|
95
|
-
else if (ev.type === "tool_call_failed") renderer.toolEnd(ev.callId, false, `${ev.reason}: ${ev.detail}`.slice(0, 160), 0);
|
|
96
|
-
};
|
|
97
|
-
let out: ToolOutput = { ok: false, output: "" };
|
|
98
|
-
try {
|
|
99
|
-
// no loop guard: the person's own repeats are not a model loop and must not count toward its streak
|
|
100
|
-
out = await ctx.rt.registry.dispatch(call, toolCtx, ctx.rt.hooks, cfg.permissionRules, cfg.approval, emit, undefined);
|
|
101
|
-
if (executed) {
|
|
102
|
-
const store = ctx.store();
|
|
103
|
-
const staged = store.stagedAttachments; // /attach images ride on the next TYPED message, never on a shell record
|
|
104
|
-
store.stageAttachments([]);
|
|
105
|
-
store.append(shellRecord(cmd, out, store.messages().at(-1)?.id ?? null));
|
|
106
|
-
store.stageAttachments(staged);
|
|
107
|
-
if (!renderer.onEvent) renderer.addSystemNote(outputBlock(out.output), out.ok ? "info" : "warn");
|
|
108
|
-
} else {
|
|
109
|
-
const why = ac.signal.aborted ? "was interrupted before it ran" : asked ? "was denied at the approval card" : "was refused before any approval prompt (a permission rule, exec policy or a hook)";
|
|
110
|
-
renderer.addSystemNote(`\`${head(cmd)}\` ${why} — nothing ran, nothing recorded`, "warn");
|
|
111
|
-
}
|
|
112
|
-
} finally {
|
|
113
|
-
ctx.bindAbort(null);
|
|
114
|
-
renderer.setBusy(false, executed && out.ok ? "done" : "error");
|
|
115
|
-
ctx.setBusy(false);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** The session record the model reads next turn: a role:"user" message —
|
|
120
|
-
* `<user_shell_command>\n$ <cmd>\n</user_shell_command>\n<user_shell_output exit="N">\n<output>\n</user_shell_output>`
|
|
121
|
-
* — exit = the bash tool's `exit=N` header (`?` when the output carries none), output = the rest. */
|
|
122
|
-
export function shellRecord(cmd: string, out: ToolOutput, parentId: string | null): Message {
|
|
123
|
-
const m = /^exit=(-?\d+)\r?\n?/.exec(out.output);
|
|
124
|
-
const exit = m ? m[1]! : "?";
|
|
125
|
-
const body = (m ? out.output.slice(m[0].length) : out.output).replace(/\r?\n$/, "");
|
|
126
|
-
const text = [OPEN, `$ ${cmd}`, CLOSE, `${OUT_OPEN} exit="${exit}">`, ...(body ? [body] : []), OUT_CLOSE].join("\n");
|
|
127
|
-
return { id: randomUUID(), role: "user", parts: [{ kind: "text", text }], parentId, createdAt: Date.now() };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const RECORD_RE = /^<user_shell_command>\n\$ ([\s\S]*?)\n<\/user_shell_command>\n<user_shell_output exit="(-?\d+|\?)">\n(?:([\s\S]*?)\n)?<\/user_shell_output>$/;
|
|
131
|
-
|
|
132
|
-
/** the inverse of shellRecord for transcript replay; null for any other user text */
|
|
133
|
-
export function parseShellRecord(text: string): { cmd: string; exit: string; output: string } | null {
|
|
134
|
-
const m = RECORD_RE.exec(text);
|
|
135
|
-
return m ? { cmd: m[1]!, exit: m[2]!, output: m[3] ?? "" } : null;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** the classic surface's output block: the tool's text (its `exit=N` header first), at most OUTPUT_BLOCK_LINES lines */
|
|
139
|
-
export function outputBlock(output: string): string {
|
|
140
|
-
const lines = output.replace(/\r\n/g, "\n").replace(/\n$/, "").split("\n");
|
|
141
|
-
const shown = lines.slice(0, OUTPUT_BLOCK_LINES);
|
|
142
|
-
const more = lines.length - shown.length;
|
|
143
|
-
return shown.join("\n") + (more > 0 ? `\n… ${more} more line${more === 1 ? "" : "s"} (the session record keeps the full output)` : "");
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
let replaySeq = 0;
|
|
147
|
-
/** Transcript replay of a stored shell record: the typed `!cmd` line, then the bash tool card pair the live run
|
|
148
|
-
* showed. false = not a record. */
|
|
149
|
-
export function replayShellRecord(renderer: Renderer, text: string): boolean {
|
|
150
|
-
const r = parseShellRecord(text);
|
|
151
|
-
if (!r) return false;
|
|
152
|
-
const id = `shell-replay-${++replaySeq}`;
|
|
153
|
-
renderer.addUser(`!${r.cmd}`);
|
|
154
|
-
renderer.toolStart(id, "bash", JSON.stringify({ command: r.cmd }).slice(0, 120));
|
|
155
|
-
// the record's exact text as the preview: an empty output is `exit=N` alone, never a dangling line break
|
|
156
|
-
renderer.toolEnd(id, r.exit === "0", [`exit=${r.exit}`, ...(r.output ? [r.output] : [])].join("\n").slice(0, 160).replace(/\n/g, " ⏎ "), 0);
|
|
157
|
-
return true;
|
|
158
|
-
}
|
package/src/tui/smoke.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/** `rovecode smoke-tui`: end-to-end render check through the real pipeline —
|
|
2
|
-
* agentLoop → Renderer → pi-tui → ANSI → @xterm/headless terminal emulator.
|
|
3
|
-
* Gated session: a scripted write, then an anchored edit of that file, each raise the
|
|
4
|
-
* approval overlay with its diff card (port #24) and are approved once the card is on
|
|
5
|
-
* screen; a markdown turn closes the run. Prints the edit's diff-card screen and the
|
|
6
|
-
* final 80x24 screen; exits 0 iff every stage rendered. */
|
|
7
|
-
|
|
8
|
-
import { VirtualTerminal } from "../../vendor/pi-tui/test/virtual-terminal.ts";
|
|
9
|
-
import { PiTuiRenderer } from "./pi-renderer.ts";
|
|
10
|
-
import { runTui } from "./app.ts";
|
|
11
|
-
import { mockStream, textTurn, toolTurn } from "../providers/stream.ts";
|
|
12
|
-
import { fileTag, lineHash } from "../coding/hashline.ts";
|
|
13
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
14
|
-
import { tmpdir } from "node:os";
|
|
15
|
-
import { join } from "node:path";
|
|
16
|
-
|
|
17
|
-
export async function runTuiSmoke(): Promise<void> {
|
|
18
|
-
const cwd = mkdtempSync(join(tmpdir(), "rovecode-tui-smoke-"));
|
|
19
|
-
const term = new VirtualTerminal(80, 24);
|
|
20
|
-
const renderer = new PiTuiRenderer({ terminal: term, cwd });
|
|
21
|
-
const probe = join(cwd, "smoke.txt");
|
|
22
|
-
const written = "smoke-ok\n";
|
|
23
|
-
const stream = mockStream({
|
|
24
|
-
turns: [
|
|
25
|
-
toolTurn([{ id: "t1", tool: "write", args: { path: probe, content: written } }]),
|
|
26
|
-
// port #24: anchored edit of the file just written → the approval card shows -old/+new
|
|
27
|
-
toolTurn([{ id: "t2", tool: "edit", args: { path: probe, edits: [
|
|
28
|
-
{ tag: fileTag(written), anchorLine: 1, anchorHash: lineHash("smoke-ok"), newLines: ["smoke-edited"] },
|
|
29
|
-
] } }]),
|
|
30
|
-
textTurn("# Smoke OK\n\nrendered **markdown**, a tool card, and the status line."),
|
|
31
|
-
],
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// pinned, not inherited: this smoke asserts approval cards, and a ~/.rovecode/settings.json with
|
|
35
|
-
// "permission":"auto" would allow everything silently (`yolo: false` is "no flag" to app.ts, not "ask")
|
|
36
|
-
const app = runTui({ renderer, stream, cwd, permission: "ask", exitOnClose: false, model: "scripted" });
|
|
37
|
-
|
|
38
|
-
// type a prompt into the (focused) editor and submit
|
|
39
|
-
term.sendInput("hello rovecode");
|
|
40
|
-
term.sendInput("\r");
|
|
41
|
-
|
|
42
|
-
// each approval overlay must show its diff card before Enter (= allow once) approves it:
|
|
43
|
-
// the write is an all-adds create diff, the edit a modify diff with the removed line
|
|
44
|
-
const cards = ["+smoke-ok", "-smoke-ok"];
|
|
45
|
-
const cardScreens: string[][] = [];
|
|
46
|
-
const deadline = Date.now() + 10_000;
|
|
47
|
-
let screen: string[] = [];
|
|
48
|
-
let ok = false;
|
|
49
|
-
while (Date.now() < deadline) {
|
|
50
|
-
screen = await term.flushAndGetViewport();
|
|
51
|
-
const text = screen.join("\n");
|
|
52
|
-
if (text.includes("Smoke OK") && text.includes("write") && cardScreens.length === cards.length) { ok = true; break; }
|
|
53
|
-
const want = cards[cardScreens.length];
|
|
54
|
-
if (want !== undefined && text.includes("allow once") && text.includes(want)) {
|
|
55
|
-
cardScreens.push(screen);
|
|
56
|
-
term.sendInput("\r");
|
|
57
|
-
}
|
|
58
|
-
await new Promise((r) => setTimeout(r, 50));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
term.sendInput("\x03"); // Ctrl+C → clean exit path
|
|
62
|
-
await app;
|
|
63
|
-
rmSync(cwd, { recursive: true, force: true });
|
|
64
|
-
|
|
65
|
-
console.log("── approval overlay with diff card (edit) ──────────────────────────────");
|
|
66
|
-
for (const line of cardScreens[1] ?? ["(diff card never rendered)"]) console.log(line.trimEnd());
|
|
67
|
-
console.log("── emulated 80x24 screen ──────────────────────────────────────────────");
|
|
68
|
-
for (const line of screen) console.log(line.trimEnd());
|
|
69
|
-
console.log("───────────────────────────────────────────────────────────────────────");
|
|
70
|
-
console.log(ok ? "smoke-tui: PASS (markdown + tool card + diff card rendered through full pipeline)" : `smoke-tui: FAIL (diff cards seen: ${cardScreens.length}/${cards.length})`);
|
|
71
|
-
process.exit(ok ? 0 : 1);
|
|
72
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/** Classic TUI's first frame is measured AND serialized offscreen. No raw mode, terminal writes or
|
|
2
|
-
* input subscription until reveal(). Re-preparing replaces the buffer (resize/status), never appends
|
|
3
|
-
* an obsolete screen. Imported only by the optional classic renderer. */
|
|
4
|
-
import type { Terminal } from "../../vendor/pi-tui/src/terminal.ts";
|
|
5
|
-
|
|
6
|
-
export class StagedTerminal implements Terminal {
|
|
7
|
-
private pending: (() => void)[] = [];
|
|
8
|
-
private visible = false;
|
|
9
|
-
private discarded = false;
|
|
10
|
-
constructor(private readonly inner: Terminal) {}
|
|
11
|
-
get columns(): number { return this.inner.columns; }
|
|
12
|
-
get rows(): number { return this.inner.rows; }
|
|
13
|
-
get kittyProtocolActive(): boolean { return this.inner.kittyProtocolActive; }
|
|
14
|
-
prepare(render: () => void): void { this.pending = []; render(); }
|
|
15
|
-
reveal(start: () => void, handoff?: () => void): void {
|
|
16
|
-
if (this.discarded || this.visible) return;
|
|
17
|
-
const frame = this.pending; this.pending = [];
|
|
18
|
-
const esc = String.fromCharCode(27);
|
|
19
|
-
this.inner.write(`${esc}[?2026h`);
|
|
20
|
-
try {
|
|
21
|
-
handoff?.();
|
|
22
|
-
this.visible = true;
|
|
23
|
-
start(); // protocol setup/input first, followed immediately by the already-computed complete frame
|
|
24
|
-
for (const write of frame) write();
|
|
25
|
-
} finally { this.inner.write(`${esc}[?2026l`); }
|
|
26
|
-
}
|
|
27
|
-
private emit(write: () => void): void {
|
|
28
|
-
if (this.discarded) return;
|
|
29
|
-
if (this.visible) write(); else this.pending.push(write);
|
|
30
|
-
}
|
|
31
|
-
start(input: (data: string) => void, resize: () => void): void {
|
|
32
|
-
if (this.visible) this.inner.start(input, resize);
|
|
33
|
-
}
|
|
34
|
-
stop(): void {
|
|
35
|
-
this.discarded = true; this.pending = [];
|
|
36
|
-
if (this.visible) this.inner.stop();
|
|
37
|
-
}
|
|
38
|
-
drainInput(maxMs?: number, idleMs?: number): Promise<void> {
|
|
39
|
-
return this.visible ? this.inner.drainInput(maxMs, idleMs) : Promise.resolve();
|
|
40
|
-
}
|
|
41
|
-
write(data: string): void { this.emit(() => this.inner.write(data)); }
|
|
42
|
-
moveBy(lines: number): void { this.emit(() => this.inner.moveBy(lines)); }
|
|
43
|
-
hideCursor(): void { this.emit(() => this.inner.hideCursor()); }
|
|
44
|
-
showCursor(): void { this.emit(() => this.inner.showCursor()); }
|
|
45
|
-
clearLine(): void { this.emit(() => this.inner.clearLine()); }
|
|
46
|
-
clearFromCursor(): void { this.emit(() => this.inner.clearFromCursor()); }
|
|
47
|
-
clearScreen(): void { this.emit(() => this.inner.clearScreen()); }
|
|
48
|
-
setTitle(title: string): void { this.emit(() => this.inner.setTitle(title)); }
|
|
49
|
-
setProgress(active: boolean): void { this.emit(() => this.inner.setProgress(active)); }
|
|
50
|
-
}
|
package/src/tui/startup.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/** Wait for required startup work without trapping Ctrl+C behind a slow probe. The readiness promise
|
|
2
|
-
* stays observed after cancellation, so a late rejection cannot become an unhandled rejection. */
|
|
3
|
-
export function waitForStartup(ready: Promise<void>, signal?: AbortSignal): Promise<void> {
|
|
4
|
-
if (!signal) return ready;
|
|
5
|
-
return new Promise((resolve, reject) => {
|
|
6
|
-
const abort = () => { reject(signal.reason ?? new Error("startup cancelled")); };
|
|
7
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
8
|
-
const cleanup = () => signal.removeEventListener("abort", abort);
|
|
9
|
-
if (signal.aborted) { cleanup(); abort(); }
|
|
10
|
-
ready.then(() => { cleanup(); resolve(); }, (error) => { cleanup(); reject(error); });
|
|
11
|
-
});
|
|
12
|
-
}
|
package/src/tui/theme.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/** Rovecode TUI theme: raw ANSI styling (no chalk dep) + theme objects for vendored pi-tui
|
|
2
|
-
* components (EditorTheme / MarkdownTheme / SelectListTheme shapes from vendor). */
|
|
3
|
-
|
|
4
|
-
import type { EditorTheme, MarkdownTheme, SelectListTheme } from "../../vendor/pi-tui/src/index.ts";
|
|
5
|
-
|
|
6
|
-
const esc = (open: string, close: string) => (s: string) => `\x1b[${open}m${s}\x1b[${close}m`;
|
|
7
|
-
|
|
8
|
-
export const st = {
|
|
9
|
-
bold: esc("1", "22"),
|
|
10
|
-
dim: esc("2", "22"),
|
|
11
|
-
italic: esc("3", "23"),
|
|
12
|
-
underline: esc("4", "24"),
|
|
13
|
-
inverse: esc("7", "27"),
|
|
14
|
-
strike: esc("9", "29"),
|
|
15
|
-
fg: (n: number) => esc(`38;5;${n}`, "39"),
|
|
16
|
-
bg: (n: number) => esc(`48;5;${n}`, "49"),
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// palette (256-color): calm blues/teals for chrome, amber for warnings
|
|
20
|
-
export const pal = {
|
|
21
|
-
accent: st.fg(75), // sky blue — headings, borders
|
|
22
|
-
accentDim: st.fg(67),
|
|
23
|
-
ok: st.fg(114), // green
|
|
24
|
-
warn: st.fg(215), // amber
|
|
25
|
-
err: st.fg(203), // red
|
|
26
|
-
muted: st.dim,
|
|
27
|
-
code: st.fg(180),
|
|
28
|
-
link: st.fg(75),
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export const rovecodeSelectListTheme: SelectListTheme = {
|
|
32
|
-
selectedPrefix: (t) => pal.accent(t),
|
|
33
|
-
selectedText: (t) => st.bold(t),
|
|
34
|
-
description: (t) => st.dim(t),
|
|
35
|
-
scrollInfo: (t) => st.dim(t),
|
|
36
|
-
noMatch: (t) => st.dim(t),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const rovecodeEditorTheme: EditorTheme = {
|
|
40
|
-
borderColor: (t) => pal.accentDim(t),
|
|
41
|
-
selectList: rovecodeSelectListTheme,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export const rovecodeMarkdownTheme: MarkdownTheme = {
|
|
45
|
-
heading: (t) => st.bold(pal.accent(t)),
|
|
46
|
-
link: (t) => pal.link(st.underline(t)),
|
|
47
|
-
linkUrl: (t) => st.dim(t),
|
|
48
|
-
code: (t) => pal.code(t),
|
|
49
|
-
codeBlock: (t) => pal.code(t),
|
|
50
|
-
codeBlockBorder: (t) => st.dim(t),
|
|
51
|
-
quote: (t) => st.italic(st.dim(t)),
|
|
52
|
-
quoteBorder: (t) => st.dim(t),
|
|
53
|
-
hr: (t) => st.dim(t),
|
|
54
|
-
listBullet: (t) => pal.accent(t),
|
|
55
|
-
bold: (t) => st.bold(t),
|
|
56
|
-
italic: (t) => st.italic(t),
|
|
57
|
-
strikethrough: (t) => st.strike(t),
|
|
58
|
-
underline: (t) => st.underline(t),
|
|
59
|
-
};
|
package/src/tui/todo-label.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/** Tiny status-bar helper split from info-cmd.ts so app.ts can import it statically
|
|
2
|
-
* without pulling in the full info-cmd module at startup. */
|
|
3
|
-
import { loadTodos, todoStatusLabel } from "../tools/todo.ts";
|
|
4
|
-
|
|
5
|
-
export function todoLabel(sessionDir: string): string | undefined {
|
|
6
|
-
return todoStatusLabel(loadTodos(sessionDir).items) || undefined;
|
|
7
|
-
}
|
package/src/tui/trust-card.ts
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/** `/trust` in the TUI — the approval card for the project trust gate (core/trust.ts, 08f355a), which until now could
|
|
2
|
-
* only be answered by QUITTING the TUI and running `rovecode trust`. A security gate whose only yes is "leave the thing
|
|
3
|
-
* you are doing" is a gate people switch off, so this is the same decision without the exit.
|
|
4
|
-
*
|
|
5
|
-
* NOT the aion port of the same file name. aion's src/tui/trust-card.ts asks about a FOLDER (decideTrust, trust.json,
|
|
6
|
-
* "trust this folder / just this session / never", and a PreBootHooks so runTui can ask before createRuntime); rovecode's
|
|
7
|
-
* gate is per-FILE and keyed by sha256 in the user home, so there is no folder answer to give and no boot ordering to
|
|
8
|
-
* bend — the decision here is "these bytes of THIS file", one file at a time. Everything the person reads before
|
|
9
|
-
* answering comes from core/project-trust.ts (projectTrustRows → trustShowLines), the same lines `rovecode trust show`
|
|
10
|
-
* prints: the terminal and the TUI must not describe the same file differently.
|
|
11
|
-
*
|
|
12
|
-
* Two rules the card is built around, both of them Berkay's:
|
|
13
|
-
* - approving is a DECISION, not a keystroke: the first item of the picker — the one Enter lands on — is "keep them
|
|
14
|
-
* untrusted". There is no default yes, and Esc is the same refusal.
|
|
15
|
-
* - no bulk yes without showing what "all" is: the carries lines are printed BEFORE the picker every time, and
|
|
16
|
-
* "approve all N files" only appears when N > 1 (with one file it would be a second spelling of the same item).
|
|
17
|
-
* Approving does not retro-load anything — hooks.ts is imported at boot, sandbox.json picks the executor at boot — so
|
|
18
|
-
* the card closes with the CLI's own sentence: restart to apply. */
|
|
19
|
-
|
|
20
|
-
import { relative } from "node:path";
|
|
21
|
-
import { projectTrustRows, trustRows, trustShowLines, untrustedRows, untrustRows, type TrustRow } from "../core/project-trust.ts";
|
|
22
|
-
import { trustFile } from "../core/trust.ts";
|
|
23
|
-
import { rovecodeHome } from "../providers/auth.ts";
|
|
24
|
-
import type { PickItem, Renderer } from "./renderer.ts";
|
|
25
|
-
|
|
26
|
-
export const TRUST_CARD_USAGE = "/trust — review this repo's gated files and approve them · /trust show — review only · /trust untrust — withdraw";
|
|
27
|
-
|
|
28
|
-
/** the palette entry (the mcp-cmd.ts idiom, so app.ts's table gains ONE line) */
|
|
29
|
-
export const TRUST_SUBCOMMANDS = ["show", "untrust"] as const;
|
|
30
|
-
export const TRUST_COMMAND = { name: "trust", choices: TRUST_SUBCOMMANDS, choicesThen: "submit" as const, description: "Review what this repo's config would run and approve it: /trust · /trust show · /trust untrust", group: "modes & safety" };
|
|
31
|
-
|
|
32
|
-
/** picker value of the refusal (first item) and of the bulk approval — neither can collide with a file path */
|
|
33
|
-
export const KEEP_UNTRUSTED = "\u0000keep-untrusted";
|
|
34
|
-
export const TRUST_ALL = "\u0000trust-all";
|
|
35
|
-
|
|
36
|
-
export const RESTART_NOTE = "restart rovecode to apply — an edit to any of these files asks again";
|
|
37
|
-
|
|
38
|
-
export interface TrustCardCtx {
|
|
39
|
-
renderer: Renderer;
|
|
40
|
-
cwd: string;
|
|
41
|
-
/** the trust store's home; default rovecodeHome() (tests point it at a scratch dir) */
|
|
42
|
-
home?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** the picker for the untrusted rows: refusal FIRST, then one item per file, then the bulk item when there are several */
|
|
46
|
-
export function trustPickItems(untrusted: readonly TrustRow[], cwd: string): PickItem[] {
|
|
47
|
-
const items: PickItem[] = [{ value: KEEP_UNTRUSTED, label: "keep them untrusted", description: "nothing from these files is used; you will be asked again next launch" }];
|
|
48
|
-
for (const r of untrusted) items.push({ value: r.file, label: `approve ${rel(r.file, cwd)}`, description: r.carries.join(" · ") });
|
|
49
|
-
if (untrusted.length > 1) items.push({ value: TRUST_ALL, label: `approve all ${untrusted.length} files`, description: untrusted.map((r) => rel(r.file, cwd)).join(", ") });
|
|
50
|
-
return items;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const rel = (file: string, cwd: string): string => { const r = relative(cwd, file); return r === "" || r.startsWith("..") ? file : r; };
|
|
54
|
-
|
|
55
|
-
export function trustCardTitle(cwd: string, untrusted: readonly TrustRow[]): string {
|
|
56
|
-
const what = untrusted.length === 1 ? "1 file" : `${untrusted.length} files`;
|
|
57
|
-
return `${cwd}: ${what} would make rovecode run something and ${untrusted.length === 1 ? "is" : "are"} not trusted on this machine — the lines above say what each one carries. Enter on the first item changes nothing (Esc the same).`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** `/trust [show|untrust]` — the review lines, then (bare verb only) the approval card */
|
|
61
|
-
export async function cmdTrustCard(ctx: TrustCardCtx, arg = ""): Promise<void> {
|
|
62
|
-
const { renderer } = ctx;
|
|
63
|
-
const home = ctx.home ?? rovecodeHome();
|
|
64
|
-
const verb = arg.trim().split(/\s+/).filter(Boolean)[0] ?? "";
|
|
65
|
-
if (verb !== "" && verb !== "show" && verb !== "untrust") { renderer.addSystemNote(`unknown /trust verb "${verb}" — ${TRUST_CARD_USAGE}`, "warn"); return; }
|
|
66
|
-
const rows = projectTrustRows(ctx.cwd, home);
|
|
67
|
-
if (verb === "untrust") { for (const l of untrustRows(home, rows)) renderer.addSystemNote(l); return; }
|
|
68
|
-
show(ctx, rows);
|
|
69
|
-
if (verb === "show" || rows.length === 0) return;
|
|
70
|
-
if (untrustedRows(rows).length === 0) { renderer.addSystemNote("every gated file here is already trusted — nothing to approve"); return; }
|
|
71
|
-
await ask(ctx, home);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** the `trust show` lines, verbatim from project-trust.ts; an untrusted file's header is a warn so it is not read as prose */
|
|
75
|
-
function show(ctx: TrustCardCtx, rows: readonly TrustRow[]): void {
|
|
76
|
-
for (const line of trustShowLines([...rows])) ctx.renderer.addSystemNote(line, line.startsWith("· UNTRUSTED") ? "warn" : "info");
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** one card per remaining untrusted file: approve one → re-list what is left → ask again, until the person refuses
|
|
80
|
-
* (Enter on the first item / Esc) or nothing is left. `trustFile` records the file's CURRENT bytes. */
|
|
81
|
-
async function ask(ctx: TrustCardCtx, home: string): Promise<void> {
|
|
82
|
-
const { renderer } = ctx;
|
|
83
|
-
let approved = 0;
|
|
84
|
-
for (;;) {
|
|
85
|
-
const untrusted = untrustedRows(projectTrustRows(ctx.cwd, home));
|
|
86
|
-
if (untrusted.length === 0) break;
|
|
87
|
-
const picked = await renderer.pickOne(trustPickItems(untrusted, ctx.cwd), trustCardTitle(ctx.cwd, untrusted));
|
|
88
|
-
if (picked === null || picked === KEEP_UNTRUSTED) {
|
|
89
|
-
renderer.addSystemNote(`${untrusted.length} file${untrusted.length === 1 ? "" : "s"} left untrusted — ${untrusted.map((r) => rel(r.file, ctx.cwd)).join(", ")} contribute nothing this session`, "warn");
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
if (picked === TRUST_ALL) {
|
|
93
|
-
for (const l of trustRows(home, untrusted)) renderer.addSystemNote(l);
|
|
94
|
-
approved += untrusted.length;
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
const row = untrusted.find((r) => r.file === picked);
|
|
98
|
-
if (row === undefined) break; // a file that vanished between the card and the answer: nothing to record
|
|
99
|
-
const t = trustFile(home, row.file);
|
|
100
|
-
if (!t.ok) { renderer.addSystemNote(t.reason, "warn"); break; }
|
|
101
|
-
renderer.addSystemNote(`trusted ${row.file} (${t.digest.slice(0, 12)}…)`);
|
|
102
|
-
approved++;
|
|
103
|
-
if (untrusted.length === 1) break; // that was the last one; the loop's re-list would only confirm it
|
|
104
|
-
show(ctx, untrustedRows(projectTrustRows(ctx.cwd, home)));
|
|
105
|
-
}
|
|
106
|
-
if (approved > 0) ctx.renderer.addSystemNote(RESTART_NOTE);
|
|
107
|
-
}
|