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/memory/scope.ts
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/** Memory scoping: WHERE a remembered fact lands, and the one-time move of the old per-session store.
|
|
2
|
-
*
|
|
3
|
-
* Until 2026-09-07 both blocks were built over ONE directory, `<cwd>/.rovecode/sessions/<id>/memory`, which got
|
|
4
|
-
* two things wrong at once. Every fact died with the session id — a new session is a new uuid directory and
|
|
5
|
-
* nothing carried anything forward — and the USER block, whose entire purpose is preferences that follow the
|
|
6
|
-
* person from project to project, was written INSIDE the repository. A private preference in someone's checkout
|
|
7
|
-
* is the wrong file in the wrong place; that is what this module fixes.
|
|
8
|
-
*
|
|
9
|
-
* MEMORY → `<cwd>/.rovecode/memory/MEMORY.md` — this project, shared by every session in it (two sessions in
|
|
10
|
-
* the same checkout see the same facts; the ledger's optimistic concurrency serialises their edits)
|
|
11
|
-
* USER → `<ROVECODE_HOME | ~/.rovecode>/memory/USER.md` — the person, across every project, never in a repo
|
|
12
|
-
*
|
|
13
|
-
* A note with NO scope goes to MEMORY: an unscoped project fact landing in the user file could carry something
|
|
14
|
-
* private out of one project and into every other, while the reverse is only a nuisance. `#<text>` and
|
|
15
|
-
* `/memory <text>` append to MEMORY; `/memory --user <text>` is the only composer path to USER; the memory_edit
|
|
16
|
-
* tool has no default at all (`block` is a required enum).
|
|
17
|
-
*
|
|
18
|
-
* LEGACY (the pre-2026-09-07 layout): `<sessions>/<id>/memory/`. Processed ONCE per legacy store — when a block's
|
|
19
|
-
* scoped target has never been used (neither its file nor its `.versions.jsonl` sidecar exists) the legacy file
|
|
20
|
-
* and sidecar are copied forward, history intact, BEFORE the BlockStore takes its boot snapshot, so a resumed
|
|
21
|
-
* session keeps its memory in the prompt. A target already in use is never overwritten; the block is reported as
|
|
22
|
-
* skipped. NOTHING OF THE USER'S IS EVER DELETED: the legacy files stay exactly where they are, and a small
|
|
23
|
-
* marker beside them records the outcome so the copy and its note happen once and are said once.
|
|
24
|
-
*
|
|
25
|
-
* TRUST: `<cwd>/.rovecode/memory/MEMORY.md` is repo data — committable, and it goes into the system prompt — so a
|
|
26
|
-
* cloned repository could put text of its choosing in front of the model. The gate is core/trust.ts, the same
|
|
27
|
-
* digest store as project mcp.json and hooks.ts, with one adaptation that matters: a plain digest gate over a file
|
|
28
|
-
* that changes on every note would re-ask constantly and be switched off within a day, so the store SELF-TRUSTS
|
|
29
|
-
* the file after a write it performed itself (the `mcp add --project` precedent — what we wrote, we vouch for).
|
|
30
|
-
* Narrowly: only a write that actually landed through this store, never "the file looks close enough". A MEMORY.md
|
|
31
|
-
* that arrived from a clone or a hand-edit is READ but WITHHELD from the prompt with one boot note, and writes to
|
|
32
|
-
* it are REFUSED (memory/blocks.ts capCheck) — appending our own note to a stranger's file would launder its
|
|
33
|
-
* content into the next prompt. `rovecode trust` accepts it, exactly as for the other project files. The USER
|
|
34
|
-
* block lives in the user's own home and is never gated. */
|
|
35
|
-
|
|
36
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
37
|
-
import { join } from "node:path";
|
|
38
|
-
import { isTrustedFile, trustFile, untrustedFileNote } from "../core/trust.ts";
|
|
39
|
-
import { rovecodeHome } from "../providers/auth.ts";
|
|
40
|
-
import { LEDGER_SUFFIX } from "../skills/versioned.ts";
|
|
41
|
-
import { BLOCK_FILES, BlockStore, cutAtCap, defaultCaps, type BlockCaps, type BlockDirs, type BlockName, type BlockStoreOptions } from "./blocks.ts";
|
|
42
|
-
|
|
43
|
-
/** the memory sub-directory of a scope's state dir */
|
|
44
|
-
export const MEMORY_DIR = "memory";
|
|
45
|
-
/** written beside a legacy store's files once it has been processed (copied or skipped) — the "once" of the header */
|
|
46
|
-
export const LEGACY_MARKER = ".copied-forward.json";
|
|
47
|
-
/** an over-cap legacy block is copied cut to this share of the cap, so the migrated store can still take a note */
|
|
48
|
-
export const LEGACY_CUT_RATIO = 0.9;
|
|
49
|
-
const BLOCKS: readonly BlockName[] = ["memory", "user"];
|
|
50
|
-
|
|
51
|
-
/** `<cwd>/.rovecode/memory` — the MEMORY block's home for this project */
|
|
52
|
-
export function projectMemoryDir(cwd: string): string { return join(cwd, ".rovecode", MEMORY_DIR); }
|
|
53
|
-
/** `<ROVECODE_HOME | ~/.rovecode>/memory` — the USER block's home, the same home providers.json and plugins.json use */
|
|
54
|
-
export function userMemoryDir(home: string = rovecodeHome()): string { return join(home, MEMORY_DIR); }
|
|
55
|
-
/** the pre-2026-09-07 per-session store: `<sessions>/<id>/memory` */
|
|
56
|
-
export function legacySessionMemoryDir(sessionsDir: string, sessionId: string): string { return join(sessionsDir, sessionId, MEMORY_DIR); }
|
|
57
|
-
/** the scoped directories a runtime's BlockStore is built over */
|
|
58
|
-
export function scopedBlockDirs(cwd: string, home?: string): BlockDirs {
|
|
59
|
-
return { memory: projectMemoryDir(cwd), user: userMemoryDir(home) };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** true when `block` has never been written under `dir`: neither its file nor its ledger sidecar exists */
|
|
63
|
-
export function blockUnused(dir: string, block: BlockName): boolean {
|
|
64
|
-
const file = join(dir, BLOCK_FILES[block]);
|
|
65
|
-
return !existsSync(file) && !existsSync(file + LEDGER_SUFFIX);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface MigrationResult {
|
|
69
|
-
/** the legacy store that was read */
|
|
70
|
-
from: string;
|
|
71
|
-
/** blocks copied forward (file + sidecar when present) */
|
|
72
|
-
copied: BlockName[];
|
|
73
|
-
/** blocks the legacy store had but whose scoped target is already in use — left alone */
|
|
74
|
-
skipped: BlockName[];
|
|
75
|
-
/** copied blocks whose legacy file was over the block cap: the copy is cut at the cap (present only when non-empty) */
|
|
76
|
-
cut?: BlockName[];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Copy a legacy `<session>/memory` store forward into the scoped dirs, block by block, only where the target is
|
|
80
|
-
* unused (header rules), then leave the marker. The legacy files are never modified or removed. null when the
|
|
81
|
-
* legacy dir holds no block file (nothing to migrate) or was processed before — the caller stays silent in both. */
|
|
82
|
-
export function migrateLegacyMemory(legacyDir: string, dirs: BlockDirs, caps: BlockCaps = defaultCaps): MigrationResult | null {
|
|
83
|
-
if (existsSync(join(legacyDir, LEGACY_MARKER))) return null;
|
|
84
|
-
const result: MigrationResult = { from: legacyDir, copied: [], skipped: [] };
|
|
85
|
-
const cut: BlockName[] = [];
|
|
86
|
-
for (const block of BLOCKS) {
|
|
87
|
-
const src = join(legacyDir, BLOCK_FILES[block]);
|
|
88
|
-
if (!existsSync(src)) continue;
|
|
89
|
-
if (!blockUnused(dirs[block], block)) { result.skipped.push(block); continue; }
|
|
90
|
-
mkdirSync(dirs[block], { recursive: true });
|
|
91
|
-
const text = readFileSync(src, "utf8");
|
|
92
|
-
// over the cap: the copy is cut to LEGACY_CUT_RATIO of the cap (line boundary) so the store has room for notes
|
|
93
|
-
// — the legacy file keeps the whole text, because nothing of the user's is deleted by a migration
|
|
94
|
-
if (text.length > caps[block]) { writeFileSync(join(dirs[block], BLOCK_FILES[block]), cutAtCap(text, Math.floor(caps[block] * LEGACY_CUT_RATIO))); cut.push(block); }
|
|
95
|
-
else copyFileSync(src, join(dirs[block], BLOCK_FILES[block]));
|
|
96
|
-
if (existsSync(src + LEDGER_SUFFIX)) copyFileSync(src + LEDGER_SUFFIX, join(dirs[block], BLOCK_FILES[block] + LEDGER_SUFFIX));
|
|
97
|
-
result.copied.push(block);
|
|
98
|
-
}
|
|
99
|
-
if (result.copied.length + result.skipped.length === 0) return null;
|
|
100
|
-
if (cut.length > 0) result.cut = cut;
|
|
101
|
-
writeFileSync(join(legacyDir, LEGACY_MARKER), JSON.stringify({ at: new Date().toISOString(), copied: result.copied, skipped: result.skipped, cut }) + "\n");
|
|
102
|
-
return result;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** The transcript note for a processed legacy store; null when there was nothing to process (or it was processed
|
|
106
|
-
* before). A pure skip — every block's target already in use — says so once instead of dropping silently. */
|
|
107
|
-
export function migrationNote(r: MigrationResult | null, dirs: BlockDirs): string | null {
|
|
108
|
-
if (!r) return null;
|
|
109
|
-
const files = (blocks: readonly BlockName[]): string => blocks.map((b) => BLOCK_FILES[b]).join(", ");
|
|
110
|
-
if (r.copied.length === 0) {
|
|
111
|
-
return `memory: legacy per-session store ${r.from} not copied — ${files(r.skipped)} already ${r.skipped.length === 1 ? "has" : "have"} content in the scoped store (/memory shows it; the legacy files are still there if you need the text — this note shows once)`;
|
|
112
|
-
}
|
|
113
|
-
const copied = r.copied.map((b) => `${BLOCK_FILES[b]} → ${dirs[b]}`).join(", ");
|
|
114
|
-
const cut = r.cut && r.cut.length > 0 ? ` (${files(r.cut)} cut to fit the block cap — the legacy file keeps the whole text)` : "";
|
|
115
|
-
const skipped = r.skipped.length > 0 ? `; ${files(r.skipped)} not copied — the scoped store already has content` : "";
|
|
116
|
-
return `memory: copied ${copied} forward from the legacy per-session store ${r.from} (left in place; in the prompt from this run on)${cut}${skipped}`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/** The copy-forward is OUR OWN write of the user's own file, so the file it produces is trusted at the moment it is
|
|
120
|
-
* written — exactly like a note we append. Without this, migrating a legacy store would hand the person a MEMORY.md
|
|
121
|
-
* that this machine then refused to put in the prompt, which is the opposite of what the migration is for. */
|
|
122
|
-
function trustMigrated(r: MigrationResult | null, dirs: BlockDirs, home: string): void {
|
|
123
|
-
if (r?.copied.includes("memory")) trustFile(home, join(dirs.memory, BLOCK_FILES.memory));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/** The store options for `cwd`: withhold a project MEMORY.md this machine has not approved, and keep the digest
|
|
127
|
-
* current for the writes we perform ourselves (header — the narrow self-trust). A MEMORY.md that does not exist
|
|
128
|
-
* yet is not withheld: the first note creates it, and creating it is our own write. */
|
|
129
|
-
export function scopedStoreOptions(cwd: string, home: string = rovecodeHome()): BlockStoreOptions {
|
|
130
|
-
const file = join(projectMemoryDir(cwd), BLOCK_FILES.memory);
|
|
131
|
-
const withhold: BlockName[] = existsSync(file) && !isTrustedFile(home, file) ? ["memory"] : [];
|
|
132
|
-
return {
|
|
133
|
-
...(withhold.length > 0 ? { withhold } : {}),
|
|
134
|
-
// only the project block needs a digest: USER.md lives in the user's own home and is never gated
|
|
135
|
-
onCommit: (block, path) => { if (block === "memory") trustFile(home, path); },
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** The boot notes about the store itself: a withheld project block, a block over its cap. */
|
|
140
|
-
export function storeNotes(blocks: BlockStore, cwd: string): string[] {
|
|
141
|
-
const notes: string[] = [];
|
|
142
|
-
void cwd;
|
|
143
|
-
for (const b of BLOCKS) {
|
|
144
|
-
if (blocks.isWithheld(b)) {
|
|
145
|
-
notes.push(untrustedFileNote(blocks.path(b), "it is not in the prompt and cannot be written to (it came with this repository, and a repo file would be choosing what the model reads); /memory still shows it"));
|
|
146
|
-
}
|
|
147
|
-
const over = blocks.overCap(b);
|
|
148
|
-
if (over) notes.push(`memory: ${blocks.path(b)} holds ${over.readCut ? "more than the read cap" : `${over.chars} chars`} — over the ${over.cap}-char cap; the prompt shows the first ${over.cap} and memory_edit is refused until the file is trimmed`);
|
|
149
|
-
}
|
|
150
|
-
return notes;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export interface ScopedMemory {
|
|
154
|
-
blocks: BlockStore;
|
|
155
|
-
/** the boot notes for the surface's transcript (the one-time copy-forward, a withheld or over-cap block), else null */
|
|
156
|
-
note: string | null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/** Build the runtime's BlockStore over the scoped dirs AFTER the one-time legacy copy for `sessionId`, so the boot
|
|
160
|
-
* snapshot already carries a resumed session's migrated memory. */
|
|
161
|
-
export function openScopedMemory(d: { cwd: string; sessionsDir: string; sessionId: string; home?: string }): ScopedMemory {
|
|
162
|
-
const home = d.home ?? rovecodeHome();
|
|
163
|
-
const dirs = scopedBlockDirs(d.cwd, home);
|
|
164
|
-
const migrated = migrateLegacyMemory(legacySessionMemoryDir(d.sessionsDir, d.sessionId), dirs);
|
|
165
|
-
trustMigrated(migrated, dirs, home); // before the store reads: what we just copied is ours, not the repo's
|
|
166
|
-
const migration = migrationNote(migrated, dirs);
|
|
167
|
-
const blocks = new BlockStore(dirs, defaultCaps, scopedStoreOptions(d.cwd, home));
|
|
168
|
-
const notes = [...(migration ? [migration] : []), ...storeNotes(blocks, d.cwd)];
|
|
169
|
-
return { blocks, note: notes.length > 0 ? notes.join("\n") : null };
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** TUI session switch (tui/app.ts): memory is project-scoped, so the store STAYS — unless the switched-to session
|
|
173
|
-
* carries a legacy store that copies forward, in which case a FRESH BlockStore is handed back so the migrated text
|
|
174
|
-
* is in the prompt now rather than only next run. A pure-skip note returns without a new store. */
|
|
175
|
-
export function adoptLegacyMemory(cwd: string, sessionsDir: string, sessionId: string, home: string = rovecodeHome()): { blocks?: BlockStore; note: string | null } {
|
|
176
|
-
const dirs = scopedBlockDirs(cwd, home);
|
|
177
|
-
const r = migrateLegacyMemory(legacySessionMemoryDir(sessionsDir, sessionId), dirs);
|
|
178
|
-
trustMigrated(r, dirs, home);
|
|
179
|
-
const note = migrationNote(r, dirs);
|
|
180
|
-
if (r && r.copied.length > 0) return { blocks: new BlockStore(dirs, defaultCaps, scopedStoreOptions(cwd, home)), note };
|
|
181
|
-
return { note };
|
|
182
|
-
}
|
package/src/memory/store.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/** Bounded memory (ADR-008): typed records with budgets, decay, dedup, provenance.
|
|
2
|
-
* Memory edits flow through the tool pipeline so policy applies. */
|
|
3
|
-
|
|
4
|
-
import { createHash } from "node:crypto";
|
|
5
|
-
import { mkdirSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
|
|
8
|
-
export type MemoryKind = "task" | "episodic" | "semantic";
|
|
9
|
-
|
|
10
|
-
export interface MemoryRecord {
|
|
11
|
-
id: string;
|
|
12
|
-
kind: MemoryKind;
|
|
13
|
-
text: string;
|
|
14
|
-
createdAt: number;
|
|
15
|
-
lastAccessedAt: number;
|
|
16
|
-
accessCount: number;
|
|
17
|
-
/** where this came from: session id, tool call, user */
|
|
18
|
-
provenance: string;
|
|
19
|
-
tags: string[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface MemoryLimits {
|
|
23
|
-
maxRecords: number;
|
|
24
|
-
maxCharsPerRecord: number;
|
|
25
|
-
/** records unused for this many ms get decayed (deleted at the tail) */
|
|
26
|
-
decayAfterMs: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const defaultLimits: MemoryLimits = { maxRecords: 500, maxCharsPerRecord: 2_000, decayAfterMs: 1000 * 60 * 60 * 24 * 14 };
|
|
30
|
-
|
|
31
|
-
function dedupKey(r: MemoryRecord): string {
|
|
32
|
-
return createHash("sha256").update(r.kind + "|" + r.text.replace(/\s+/g, " ").trim().toLowerCase()).digest("hex").slice(0, 16);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export class MemoryStore {
|
|
36
|
-
private records: MemoryRecord[] = [];
|
|
37
|
-
private seen = new Set<string>();
|
|
38
|
-
|
|
39
|
-
constructor(private readonly dir: string, private readonly limits: MemoryLimits = defaultLimits) {
|
|
40
|
-
mkdirSync(dir, { recursive: true });
|
|
41
|
-
const p = join(dir, "memory.json");
|
|
42
|
-
if (existsSync(p)) {
|
|
43
|
-
this.records = JSON.parse(readFileSync(p, "utf8")) as MemoryRecord[];
|
|
44
|
-
this.seen = new Set(this.records.map(dedupKey));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
private persist(): void {
|
|
49
|
-
writeFileSync(join(this.dir, "memory.json"), JSON.stringify(this.records, null, 2));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
add(kind: MemoryKind, text: string, provenance: string, tags: string[] = []): { ok: boolean; reason?: string } {
|
|
53
|
-
const trimmed = text.trim();
|
|
54
|
-
if (trimmed.length === 0) return { ok: false, reason: "empty record" };
|
|
55
|
-
if (trimmed.length > this.limits.maxCharsPerRecord) return { ok: false, reason: `record exceeds ${this.limits.maxCharsPerRecord} chars` };
|
|
56
|
-
const rec: MemoryRecord = {
|
|
57
|
-
id: createHash("sha256").update(kind + trimmed + Date.now()).digest("hex").slice(0, 12),
|
|
58
|
-
kind, text: trimmed, createdAt: Date.now(), lastAccessedAt: Date.now(), accessCount: 0, provenance, tags,
|
|
59
|
-
};
|
|
60
|
-
const key = dedupKey(rec);
|
|
61
|
-
if (this.seen.has(key)) return { ok: false, reason: "duplicate" };
|
|
62
|
-
this.seen.add(key);
|
|
63
|
-
this.records.push(rec);
|
|
64
|
-
this.enforceLimits();
|
|
65
|
-
this.persist();
|
|
66
|
-
return { ok: true };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
private enforceLimits(): void {
|
|
70
|
-
const now = Date.now();
|
|
71
|
-
// decay: drop stale records beyond the cap, oldest-accessed first
|
|
72
|
-
this.records = this.records.filter((r) => now - r.lastAccessedAt < this.limits.decayAfterMs);
|
|
73
|
-
if (this.records.length > this.limits.maxRecords) {
|
|
74
|
-
this.records.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
|
|
75
|
-
this.records = this.records.slice(this.records.length - this.limits.maxRecords);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** Recency × access × simple lexical relevance. */
|
|
80
|
-
retrieve(query: string, limit = 5, kind?: MemoryKind): MemoryRecord[] {
|
|
81
|
-
const terms = query.toLowerCase().split(/\W+/).filter((t) => t.length > 2);
|
|
82
|
-
const now = Date.now();
|
|
83
|
-
const scored = this.records
|
|
84
|
-
.filter((r) => !kind || r.kind === kind)
|
|
85
|
-
.map((r) => {
|
|
86
|
-
const text = r.text.toLowerCase();
|
|
87
|
-
const hits = terms.reduce((n, t) => n + (text.includes(t) ? 1 : 0), 0);
|
|
88
|
-
const recency = 1 / (1 + (now - r.lastAccessedAt) / 3_600_000);
|
|
89
|
-
const score = hits * 2 + Math.log1p(r.accessCount) + recency;
|
|
90
|
-
return { r, score };
|
|
91
|
-
})
|
|
92
|
-
.filter((s) => s.score > 0.3)
|
|
93
|
-
.sort((a, b) => b.score - a.score)
|
|
94
|
-
.slice(0, limit);
|
|
95
|
-
for (const s of scored) { s.r.accessCount++; s.r.lastAccessedAt = now; }
|
|
96
|
-
this.persist();
|
|
97
|
-
return scored.map((s) => s.r);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
stats(): { count: number; byKind: Record<string, number> } {
|
|
101
|
-
const byKind: Record<string, number> = {};
|
|
102
|
-
for (const r of this.records) byKind[r.kind] = (byKind[r.kind] ?? 0) + 1;
|
|
103
|
-
return { count: this.records.length, byKind };
|
|
104
|
-
}
|
|
105
|
-
}
|
package/src/memory/tools.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/** memory_edit tool (hermes memory_tool pattern): add/replace/remove on the bounded
|
|
2
|
-
* markdown blocks. Failures (bad match, cap overflow, bad args) count toward a
|
|
3
|
-
* per-turn cap of 3; at the cap the tool returns a terminal skip so the agent
|
|
4
|
-
* stops burning turns on memory writes (hermes #42405). */
|
|
5
|
-
|
|
6
|
-
import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
|
|
7
|
-
import { BlockStore, type BlockName } from "./blocks.ts";
|
|
8
|
-
|
|
9
|
-
const MAX_FAILURES_PER_TURN = 3;
|
|
10
|
-
const CAP_MESSAGE = "save skipped: memory at capacity or repeatedly failing";
|
|
11
|
-
|
|
12
|
-
let turnFailureCount = 0;
|
|
13
|
-
|
|
14
|
-
export function resetTurnFailureCount(): void { turnFailureCount = 0; }
|
|
15
|
-
|
|
16
|
-
export function turnFailures(): number { return turnFailureCount; }
|
|
17
|
-
|
|
18
|
-
export interface MemoryEditArgs {
|
|
19
|
-
op: "add" | "replace" | "remove";
|
|
20
|
-
block: BlockName;
|
|
21
|
-
text?: string;
|
|
22
|
-
oldText?: string;
|
|
23
|
-
newText?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Build the tool bound to a store instance (the store is per-session). */
|
|
27
|
-
export function memoryEditTool(store: BlockStore): Tool {
|
|
28
|
-
return {
|
|
29
|
-
schema: {
|
|
30
|
-
name: "memory_edit",
|
|
31
|
-
description:
|
|
32
|
-
`Edit long-term memory blocks. Ops: add(text) appends a line; replace(oldText,newText) requires oldText to match exactly once; remove(oldText) same. ` +
|
|
33
|
-
`block: "memory" (session/task facts, cap ${store.cap("memory")} chars) or "user" (stable user preferences, cap ${store.cap("user")} chars). ` +
|
|
34
|
-
`Failures count toward a per-turn budget of ${MAX_FAILURES_PER_TURN}; after that saves are skipped until next turn.`,
|
|
35
|
-
args: {
|
|
36
|
-
type: "object",
|
|
37
|
-
properties: {
|
|
38
|
-
op: { type: "string", enum: ["add", "replace", "remove"] },
|
|
39
|
-
block: { type: "string", enum: ["memory", "user"] },
|
|
40
|
-
text: { type: "string", description: "text to add (op=add)" },
|
|
41
|
-
oldText: { type: "string", description: "exact text to replace/remove; must match exactly once" },
|
|
42
|
-
newText: { type: "string", description: "replacement text (op=replace; empty removes)" },
|
|
43
|
-
},
|
|
44
|
-
required: ["op", "block"],
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
kind: "memory",
|
|
48
|
-
sequential: true,
|
|
49
|
-
async execute(args: unknown, _ctx: ToolContext): Promise<ToolOutput> {
|
|
50
|
-
const a = args as MemoryEditArgs;
|
|
51
|
-
if (turnFailureCount >= MAX_FAILURES_PER_TURN) {
|
|
52
|
-
return { ok: false, output: CAP_MESSAGE };
|
|
53
|
-
}
|
|
54
|
-
const block = a.block === "user" ? "user" : a.block === "memory" ? "memory" : undefined;
|
|
55
|
-
if (!block) return fail(`invalid block: ${String(a.block)}`);
|
|
56
|
-
|
|
57
|
-
let res;
|
|
58
|
-
switch (a.op) {
|
|
59
|
-
case "add":
|
|
60
|
-
if (typeof a.text !== "string" || a.text.trim().length === 0) {
|
|
61
|
-
return fail("op=add requires non-empty text");
|
|
62
|
-
}
|
|
63
|
-
res = store.add(block, a.text);
|
|
64
|
-
break;
|
|
65
|
-
case "replace":
|
|
66
|
-
if (typeof a.oldText !== "string" || a.oldText.length === 0) {
|
|
67
|
-
return fail("op=replace requires oldText");
|
|
68
|
-
}
|
|
69
|
-
if (typeof a.newText !== "string") return fail("op=replace requires newText (use \"\" to delete)");
|
|
70
|
-
res = store.replace(block, a.oldText, a.newText);
|
|
71
|
-
break;
|
|
72
|
-
case "remove":
|
|
73
|
-
if (typeof a.oldText !== "string" || a.oldText.length === 0) {
|
|
74
|
-
return fail("op=remove requires oldText");
|
|
75
|
-
}
|
|
76
|
-
res = store.remove(block, a.oldText);
|
|
77
|
-
break;
|
|
78
|
-
default:
|
|
79
|
-
return fail(`invalid op: ${String(a.op)}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
return fail(res.reason ?? "edit failed", res.current, res.limit);
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
ok: true,
|
|
87
|
-
output: `${a.op} ok: ${block} block now ${res.current}/${res.limit} chars`,
|
|
88
|
-
data: { block, chars: res.current, limit: res.limit },
|
|
89
|
-
};
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Every failure path routes through here so the per-turn counter stays honest. */
|
|
95
|
-
function fail(reason: string, current?: number, limit?: number): ToolOutput {
|
|
96
|
-
turnFailureCount++;
|
|
97
|
-
const detail = current !== undefined && limit !== undefined ? ` (current ${current}/${limit} chars)` : "";
|
|
98
|
-
return { ok: false, output: `memory_edit failed: ${reason}${detail}; failures this turn: ${turnFailureCount}/${MAX_FAILURES_PER_TURN}` };
|
|
99
|
-
}
|
package/src/plugins/cli.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/** `rovecode plugin …` — list · add · remove · enable · disable · trust · untrust · show. A pure
|
|
2
|
-
* command function over injected cwd/home/output so tests drive it without a process; cli/main.ts
|
|
3
|
-
* wires `case "plugin"` to it with process.cwd() and rovecodeHome(). Nothing here imports plugin
|
|
4
|
-
* code: `show` and `list` read manifests and folder digests only, so an untrusted project plugin can
|
|
5
|
-
* be inspected before it is ever run — that inspection is what `trust` asks the human to do. */
|
|
6
|
-
|
|
7
|
-
import { relative } from "node:path";
|
|
8
|
-
import { rovecodeHome } from "../providers/auth.ts";
|
|
9
|
-
import { discoverPlugins, type DiscoveredPlugin, type PluginScope } from "./discover.ts";
|
|
10
|
-
import { addPlugin, removePlugin, scopeRoot, setPluginEnabled, trustPlugin, untrustPlugin, type Spawn } from "./install.ts";
|
|
11
|
-
import { contributions } from "./manifest.ts";
|
|
12
|
-
import { pluginFiles, statePath } from "./state.ts";
|
|
13
|
-
|
|
14
|
-
export interface PluginCliDeps {
|
|
15
|
-
cwd?: string;
|
|
16
|
-
home?: string;
|
|
17
|
-
out?: (line: string) => void;
|
|
18
|
-
err?: (line: string) => void;
|
|
19
|
-
spawn?: Spawn;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const PLUGIN_USAGE = [
|
|
23
|
-
"usage: rovecode plugin <command>",
|
|
24
|
-
" list every plugin in ~/.rovecode/plugins and .rovecode/plugins, with its status",
|
|
25
|
-
" add <folder|git-url> [--project] [--force]",
|
|
26
|
-
" copy (or shallow-clone) a plugin into the user scope, or this repo's .rovecode/plugins",
|
|
27
|
-
" remove <name> [--project] delete the installed copy",
|
|
28
|
-
" enable <name> · disable <name>",
|
|
29
|
-
" switch a plugin off/on in every scope (~/.rovecode/plugins.json)",
|
|
30
|
-
" trust <name> approve a PROJECT plugin's current files on this machine — until they change",
|
|
31
|
-
" untrust <name> withdraw that approval",
|
|
32
|
-
" show <name> manifest, files and what it contributes, without running any of it",
|
|
33
|
-
"status: active · disabled · untrusted (project plugin not yet approved here) · broken (see problems)",
|
|
34
|
-
].join("\n");
|
|
35
|
-
|
|
36
|
-
const flag = (args: string[], name: string): boolean => args.includes(name);
|
|
37
|
-
const words = (args: string[]): string[] => args.filter((a) => !a.startsWith("--"));
|
|
38
|
-
|
|
39
|
-
export async function cmdPlugin(args: string[], deps: PluginCliDeps = {}): Promise<number> {
|
|
40
|
-
const cwd = deps.cwd ?? process.cwd(), home = deps.home ?? rovecodeHome();
|
|
41
|
-
const out = deps.out ?? ((l) => console.log(l)), err = deps.err ?? ((l) => console.error(l));
|
|
42
|
-
const [sub = "list", ...rest] = args;
|
|
43
|
-
const scope: PluginScope = flag(rest, "--project") ? "project" : "user";
|
|
44
|
-
const name = words(rest)[0];
|
|
45
|
-
switch (sub) {
|
|
46
|
-
case "list": case "ls": {
|
|
47
|
-
const { plugins, warnings } = discoverPlugins(cwd, { home });
|
|
48
|
-
if (plugins.length === 0) { out(`no plugins. add one: rovecode plugin add <folder|git-url> (user: ${scopeRoot("user", cwd, home)}, project: ${relative(cwd, scopeRoot("project", cwd, home)) || "."})`); }
|
|
49
|
-
for (const p of plugins) out(row(p));
|
|
50
|
-
for (const w of warnings) err(`warning: ${w}`);
|
|
51
|
-
return 0;
|
|
52
|
-
}
|
|
53
|
-
case "add": {
|
|
54
|
-
const src = name;
|
|
55
|
-
if (!src) { err(PLUGIN_USAGE); return 2; }
|
|
56
|
-
const r = await addPlugin(src, { cwd, home, scope, force: flag(rest, "--force"), ...(deps.spawn ? { spawn: deps.spawn } : {}) });
|
|
57
|
-
if (!r.ok) { err(`plugin add: ${r.error}`); return 1; }
|
|
58
|
-
out(`added ${r.name}@${r.manifest.version} (${r.scope}) → ${r.dir}`);
|
|
59
|
-
for (const c of contributions(r.manifest)) out(` ${c}`);
|
|
60
|
-
if (r.scope === "project") out(` trusted on this machine (recorded in ${statePath(home)}); a change to its files will ask again`);
|
|
61
|
-
out("restart rovecode to load it — plugins are read once per process, like hooks");
|
|
62
|
-
return 0;
|
|
63
|
-
}
|
|
64
|
-
case "remove": case "rm": {
|
|
65
|
-
if (!name) { err(PLUGIN_USAGE); return 2; }
|
|
66
|
-
const r = removePlugin(name, { cwd, home, scope });
|
|
67
|
-
if (!r.ok) { err(`plugin remove: ${r.error}`); return 1; }
|
|
68
|
-
out(`removed ${name} (${scope}) from ${r.dir}`);
|
|
69
|
-
return 0;
|
|
70
|
-
}
|
|
71
|
-
case "enable": case "disable": {
|
|
72
|
-
if (!name) { err(PLUGIN_USAGE); return 2; }
|
|
73
|
-
const known = discoverPlugins(cwd, { home }).plugins.some((p) => p.name === name);
|
|
74
|
-
if (!known) err(`warning: no installed plugin named "${name}" — the setting is recorded anyway`);
|
|
75
|
-
const path = setPluginEnabled(name, sub === "enable", home);
|
|
76
|
-
out(`${name} ${sub}d (${path})`);
|
|
77
|
-
return 0;
|
|
78
|
-
}
|
|
79
|
-
case "trust": {
|
|
80
|
-
if (!name) { err(PLUGIN_USAGE); return 2; }
|
|
81
|
-
const p = discoverPlugins(cwd, { home }).plugins.find((x) => x.name === name && x.scope === "project");
|
|
82
|
-
if (!p) { err(`plugin trust: no project plugin "${name}" under ${scopeRoot("project", cwd, home)}`); return 1; }
|
|
83
|
-
if (p.manifest === null) { err(`plugin trust: "${name}" is broken — fix it first:`); for (const w of p.problems) err(` ${w}`); return 1; }
|
|
84
|
-
const r = trustPlugin(name, { cwd, home });
|
|
85
|
-
if (!r.ok) { err(`plugin trust: ${r.error}`); return 1; }
|
|
86
|
-
out(`trusted ${name}@${p.manifest.version} — ${pluginFiles(p.dir).length} files, digest ${r.digest.slice(0, 12)}…`);
|
|
87
|
-
for (const c of contributions(p.manifest)) out(` ${c}`);
|
|
88
|
-
out("it loads on the next start; `rovecode plugin show " + name + "` lists every file it brings");
|
|
89
|
-
return 0;
|
|
90
|
-
}
|
|
91
|
-
case "untrust": {
|
|
92
|
-
if (!name) { err(PLUGIN_USAGE); return 2; }
|
|
93
|
-
out(untrustPlugin(name, { cwd, home }) ? `untrusted ${name}` : `${name} was not trusted`);
|
|
94
|
-
return 0;
|
|
95
|
-
}
|
|
96
|
-
case "show": case "info": {
|
|
97
|
-
if (!name) { err(PLUGIN_USAGE); return 2; }
|
|
98
|
-
const p = discoverPlugins(cwd, { home }).plugins.find((x) => x.name === name);
|
|
99
|
-
if (!p) { err(`plugin show: no plugin "${name}"`); return 1; }
|
|
100
|
-
out(row(p));
|
|
101
|
-
out(` folder: ${p.dir}`);
|
|
102
|
-
if (p.digest) out(` digest: ${p.digest}`);
|
|
103
|
-
if (p.manifest) for (const c of contributions(p.manifest)) out(` ${c}`);
|
|
104
|
-
for (const f of pluginFiles(p.dir)) out(` - ${f}`);
|
|
105
|
-
for (const w of p.problems) out(` ! ${w}`);
|
|
106
|
-
if (p.status === "untrusted") out(` run \`rovecode plugin trust ${p.name}\` after reading the files above`);
|
|
107
|
-
return 0;
|
|
108
|
-
}
|
|
109
|
-
case "help": case "--help": case "-h": out(PLUGIN_USAGE); return 0;
|
|
110
|
-
default: err(`unknown plugin command "${sub}"\n${PLUGIN_USAGE}`); return 2;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/** one list row: `name@version scope status — description` */
|
|
115
|
-
export function row(p: DiscoveredPlugin): string {
|
|
116
|
-
const v = p.manifest ? `@${p.manifest.version}` : "";
|
|
117
|
-
const desc = p.manifest?.description ? ` — ${p.manifest.description}` : "";
|
|
118
|
-
return `${(p.name + v).padEnd(28)} ${p.scope.padEnd(8)} ${p.status.padEnd(9)}${desc}`.trimEnd();
|
|
119
|
-
}
|
package/src/plugins/discover.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/** Plugins: discovery. Two roots — `~/.rovecode/plugins/<name>/` (user) and `<cwd>/.rovecode/plugins/
|
|
2
|
-
* <name>/` (project) — each subfolder with a `plugin.json` is a plugin. Sync and filesystem-only:
|
|
3
|
-
* nothing is imported here, so `rovecode plugin list` can show a hostile plugin without running it.
|
|
4
|
-
*
|
|
5
|
-
* Status decides what load.ts may do with a plugin:
|
|
6
|
-
* active — will be imported and wired
|
|
7
|
-
* disabled — the human switched it off (plugins.json)
|
|
8
|
-
* untrusted — a PROJECT plugin whose folder digest the human has not approved on this machine;
|
|
9
|
-
* listed, never imported (state.ts explains why the record lives in the user home)
|
|
10
|
-
* broken — no usable manifest; listed with its reasons so the author can fix it
|
|
11
|
-
* Same name in both roots: the project copy shadows the user copy (the commands loader's rule) and
|
|
12
|
-
* says so. ROVECODE_NO_PLUGINS=1 skips discovery the way ROVECODE_NO_HOOKS=1 skips hooks. */
|
|
13
|
-
|
|
14
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
15
|
-
import { join, resolve } from "node:path";
|
|
16
|
-
import { rovecodeHome } from "../providers/auth.ts";
|
|
17
|
-
import { normalizeEntry, type McpServerConfig } from "../mcp/config.ts";
|
|
18
|
-
import { MANIFEST_FILE, parseManifest, type PluginManifest } from "./manifest.ts";
|
|
19
|
-
import { isDir, isTrusted, loadState, pluginDigest, type PluginState } from "./state.ts";
|
|
20
|
-
|
|
21
|
-
export type PluginScope = "user" | "project";
|
|
22
|
-
export type PluginStatus = "active" | "disabled" | "untrusted" | "broken";
|
|
23
|
-
|
|
24
|
-
export interface DiscoveredPlugin {
|
|
25
|
-
name: string;
|
|
26
|
-
/** absolute plugin folder */
|
|
27
|
-
dir: string;
|
|
28
|
-
scope: PluginScope;
|
|
29
|
-
manifest: PluginManifest | null;
|
|
30
|
-
status: PluginStatus;
|
|
31
|
-
/** folder digest (state.ts); null when the folder could not be walked */
|
|
32
|
-
digest: string | null;
|
|
33
|
-
/** why it is broken / what the manifest dropped — the author's fix list */
|
|
34
|
-
problems: string[];
|
|
35
|
-
/** the declarative halves, resolved here because they need no code run: the runtime wires them
|
|
36
|
-
* synchronously at construction (the commands and skills loaders, the MCP config) and only the
|
|
37
|
-
* entry module waits for load.ts. Filled for every plugin with a manifest; the runtime reads
|
|
38
|
-
* them for ACTIVE ones only. */
|
|
39
|
-
commandsDir: string | null;
|
|
40
|
-
skillsDir: string | null;
|
|
41
|
-
mcp: McpServerConfig[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface DiscoverOptions { home?: string; state?: PluginState }
|
|
45
|
-
export interface Discovered { plugins: DiscoveredPlugin[]; warnings: string[] }
|
|
46
|
-
|
|
47
|
-
/** the two roots, project last so it shadows; one root when cwd IS the home (commands.ts idiom) */
|
|
48
|
-
export function pluginRoots(cwd: string, home: string): [PluginScope, string][] {
|
|
49
|
-
const user = join(home, "plugins"), project = join(cwd, ".rovecode", "plugins");
|
|
50
|
-
return resolve(user) === resolve(project) ? [["project", project]] : [["user", user], ["project", project]];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function discoverPlugins(cwd: string, opts: DiscoverOptions = {}): Discovered {
|
|
54
|
-
const out: Discovered = { plugins: [], warnings: [] };
|
|
55
|
-
if (process.env.ROVECODE_NO_PLUGINS === "1") return out;
|
|
56
|
-
const home = opts.home ?? rovecodeHome();
|
|
57
|
-
const state = opts.state ?? loadState(home);
|
|
58
|
-
const byName = new Map<string, DiscoveredPlugin>();
|
|
59
|
-
for (const [scope, root] of pluginRoots(cwd, home)) {
|
|
60
|
-
if (!isDir(root)) continue;
|
|
61
|
-
let names: string[];
|
|
62
|
-
try { names = readdirSync(root).sort(); } catch (e) { out.warnings.push(`${root}: cannot list — ${(e as Error).message}`); continue; }
|
|
63
|
-
for (const folder of names) {
|
|
64
|
-
const dir = join(root, folder);
|
|
65
|
-
if (!isDir(dir) || !existsSync(join(dir, MANIFEST_FILE))) continue; // a stray file or a folder without a manifest is not a plugin
|
|
66
|
-
const p = readPlugin(dir, folder, scope, state);
|
|
67
|
-
const prev = byName.get(p.name);
|
|
68
|
-
if (prev) out.warnings.push(`${dir}: plugin "${p.name}" shadows ${prev.dir} (project over user)`);
|
|
69
|
-
byName.set(p.name, p);
|
|
70
|
-
for (const w of p.problems) out.warnings.push(w);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
out.plugins = [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** one folder → its DiscoveredPlugin: manifest, status, digest, and the declarative halves resolved
|
|
78
|
-
* (a named commands/skills dir must exist; MCP entries go through the same normalizer as mcp.json) */
|
|
79
|
-
export function readPlugin(dir: string, folder: string, scope: PluginScope, state: PluginState): DiscoveredPlugin {
|
|
80
|
-
const file = join(dir, MANIFEST_FILE);
|
|
81
|
-
const problems: string[] = [];
|
|
82
|
-
let manifest: PluginManifest | null = null;
|
|
83
|
-
try { manifest = parseManifest(readFileSync(file, "utf8"), file, problems); }
|
|
84
|
-
catch (e) { problems.push(`${file}: cannot read — ${(e as Error).message}`); }
|
|
85
|
-
let digest: string | null = null;
|
|
86
|
-
try { digest = pluginDigest(dir); } catch { digest = null; }
|
|
87
|
-
const name = manifest?.name ?? folder;
|
|
88
|
-
if (manifest && manifest.name !== folder) problems.push(`${file}: name "${manifest.name}" differs from its folder "${folder}" — the manifest name is the plugin's identity`);
|
|
89
|
-
let status: PluginStatus;
|
|
90
|
-
if (manifest === null) status = "broken";
|
|
91
|
-
else if (state.disabled.includes(name)) status = "disabled";
|
|
92
|
-
else if (scope === "project" && (digest === null || !isTrusted(state, dir, digest))) status = "untrusted";
|
|
93
|
-
else status = "active";
|
|
94
|
-
const tag = `plugin ${name}`;
|
|
95
|
-
const commandsDir = manifest ? dirOrProblem(dir, manifest.commands, `${tag}: commands`, problems) : null;
|
|
96
|
-
const skillsDir = manifest ? dirOrProblem(dir, manifest.skills, `${tag}: skills`, problems) : null;
|
|
97
|
-
const mcp: McpServerConfig[] = [];
|
|
98
|
-
if (manifest?.mcp) for (const [n, raw] of Object.entries(manifest.mcp)) { const c = normalizeEntry(n, raw, file, problems); if (c) mcp.push(c); }
|
|
99
|
-
return { name, dir, scope, manifest, status, digest, problems, commandsDir, skillsDir, mcp };
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function dirOrProblem(base: string, rel: string | undefined, what: string, problems: string[]): string | null {
|
|
103
|
-
if (!rel) return null;
|
|
104
|
-
const p = join(base, rel);
|
|
105
|
-
try { if (statSync(p).isDirectory()) return p; } catch { /* below */ }
|
|
106
|
-
problems.push(`${what} directory "${rel}" does not exist — ignored`);
|
|
107
|
-
return null;
|
|
108
|
-
}
|