feat(cli): add memory introspection commands#102
Merged
warren618 merged 4 commits intoMay 13, 2026
Conversation
Add `vibe-trading memory list/show/search/forget` so users can inspect and manage persistent memory without opening the file store directly. Two recent memory bugs (#87 underscore tokenization, #95 CJK slug collisions) were hard to spot without observability into what is actually saved and recalled. Also expand the PersistentMemory public surface to keep CLI lookup logic out of storage internals: - MEMORY_TYPES tuple as the single source of truth for type names, shared by argparse choices and the CLI style table - list_entries() / find() / remove_entry() back the new commands; find() resolves by title first, then by on-disk filename stem so users can paste either form from the index - remove_entry(entry) skips the re-scan that remove(title) does Existing remove(title) is unchanged; remember_tool stays green.
Hoisting PersistentMemory to top-level cli.py made every CLI invocation import `src.agent.frontmatter`, which transitively triggers `src/agent/__init__.py` and eagerly loads AgentLoop, SkillsLoader (74 skills), and ToolRegistry. `vibe-trading --version` measured 7.6s because of this chain. - Move `from src.memory.persistent import PersistentMemory` back into each `cmd_memory_*` so only memory commands pay the import cost - Mirror MEMORY_TYPES as a local `_MEMORY_TYPES` tuple inside cli.py so argparse `choices` and `_MEMORY_TYPE_STYLES` no longer require the module-level import. The module-load assertion still guards against drift between the local tuple and the style dict `vibe-trading --version` drops from 7.6s to 0.66s (11.5x). Memory commands keep the same latency since they need the module anyway.
The previous two commits inadvertently captured whole-file output from a local pre-tool-edit formatter, reformatting ~30 functions (`_print_status_bar`, `_format_tool_call_args`, `_build_benchmark_table`, `_print_result`, `_build_welcome_panel`, ...) that have nothing to do with the memory introspection feature. Restore those functions to upstream `main` while keeping only the intentional changes for this PR: - `from datetime import datetime` hoisted to top imports (so the new CLI commands can use it without a per-call local import) - `from rich.markup import escape as rich_escape` for safely rendering user-supplied memory titles/descriptions - New `memory` subparser block in `_build_parser` - New `cmd_memory_list / show / search / forget` plus the local `_MEMORY_TYPES` constant and `_MEMORY_TYPE_STYLES` dict - Dispatch for `args.command == "memory"` in `main()` - Drop the now-redundant local `from datetime import datetime` inside `cmd_trace` Net diff for agent/cli.py shrinks from +296/-53 to +170/-1.
The frontmatter parser returns lists for ``[a, b]`` syntax and bools for ``true``/``false``, but MemoryEntry annotates ``title``, ``description``, and ``memory_type`` as ``str``. The mismatch was latent until the new memory CLI tried to render those fields through ``rich.markup.escape``, which raised ``TypeError: expected string or bytes-like object, got 'list'`` for any description that looked like a YAML list (e.g. ``description: [red]inject[/red]``). Add a small ``_coerce_str`` helper and route the three frontmatter fields through it in ``_scan_entries`` so the dataclass type contract is truthful at the storage boundary. Other consumers (recall scoring, ``remember_tool``) also benefit transparently. Tests: - Unit coverage of ``_coerce_str`` for str/None/list/bool inputs - Storage-layer regression: a markdown file with a bracketed description parses to a string-typed description - CLI regression: ``cmd_memory_list`` no longer crashes on entries with a bracketed description
Collaborator
|
Thanks @Teerapat-Vatpitak — this is a clean and useful addition. The memory CLI makes the persistent-memory layer much easier to inspect and debug, and the extra frontmatter coercion fix is a nice catch. Merged! |
This was referenced May 13, 2026
Closed
longmfe
pushed a commit
to longmfe/Vibe-Trading-Mars
that referenced
this pull request
Jul 14, 2026
…spection-cli feat(cli): add memory introspection commands
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
vibe-trading memory list/show/search/forgetso users can inspectand manage persistent memory without opening
~/.vibe-trading/memory/by hand. Surfaces real bug signal: two recent memory fixes (#87
snake_case, #95 CJK slugs) were hard to triage without observability
into what is actually saved and what the recall scorer returns.
Diff vs
main: +516 / -5 acrosscli.py,src/memory/persistent.py,and two test files. 81 tests pass.
vibe-trading --versionmeasuredat 1.0s (no regression on cold startup).
CLI surface
<name>resolves by exact title first, then by on-disk filename stem(
{type}_{slug}or the bareslug), so users can paste either formfrom the index.
Exit codes mirror existing commands:
EXIT_SUCCESS(0)EXIT_SUCCESS(0)EXIT_USAGE_ERROR(2)EXIT_RUN_FAILED(1)Commits
The branch is split into four commits so each step is reviewable on
its own:
892cd16feat(cli): add memory introspection commandsf8de9d0perf(cli): avoid loading agent runtime on cli startupPersistentMemorymade--version7.6s becausesrc.agent.frontmattertransitively loadsAgentLoop/SkillsLoader. Reverted to lazy imports inside eachcmd_memory_*and a local_MEMORY_TYPEStuple incli.pyso non-memory commands stay fastfe4fca6style(cli): revert unintended formatter spillover on unrelated functionscli.py. This commit restores them to upstream form so the diff only shows memory-feature lines672e2bafix(memory): coerce frontmatter values to str in _scan_entriesChanges
New (no impact on existing callers)
agent/cli.py—memorysubparser;cmd_memory_list/show/search/forget;_MEMORY_TYPEStuple;_MEMORY_TYPE_STYLES(with module-load assert);rich.markup.escapeon every user-supplied field;Confirm.askwrapped against
EOFErrorfor non-interactive deletesagent/src/memory/persistent.py—MEMORY_TYPESconstant; modulelogger; public
list_entries()/find(name)/remove_entry(entry).Existing
remove(title)is unchanged soremember_toolis unaffectedagent/tests/test_cli_memory.py— 29 new tests (command logic,name resolution incl. true stem fallback, parser wiring, Rich markup
escape, empty body rendering, EOF path, remove-failure path,
bracketed-description regression)
One existing-code change:
_scan_entriescoercionMemoryEntryannotatestitle,description, andmemory_typeasstr, but_scan_entrieswas passing the rawparse_frontmatteroutput through untouched. The parser returns lists for
[a, b]syntax and bools for
true/false, so any markdown file with e.g.description: [red]inject[/red]leaked a list into the dataclass.The previous CLI never called string ops on these fields, so the
mismatch was latent; the new CLI hits
rich.markup.escape(...)andcrashes with
TypeError.Add a small
_coerce_strhelper and route the three frontmatterfields through it so the dataclass contract is truthful at the storage
boundary. Existing consumers (
find_relevant's tokenization,remember_tool's rendering,remove(title)'s title comparison) allsilently improve on the edge case and behave identically on the happy
path.
tests/test_remember_tool.py(16 tests) is unchanged and stillgreen.
Pre-existing limitations surfaced by the new CLI (out of scope)
Heavy E2E testing with Thai content surfaced two pre-existing bugs in
persistent.pythat this PR intentionally does not fix:_tokenizeaccepts only CJK Unified Ideographs (一-鿿);Thai / Arabic / Hebrew / Cyrillic queries always return zero
matches because the tokenizer returns an empty set on those scripts
add()strips Thai (and other non-CJK scripts)to
_, producing filenames likefeedback______________.mdforThai titles
These are follow-ups of #95 and worth a separate issue/PR. The new
CLI happens to make them visible (
memory showrenders the Thaititle fine, but the on-disk slug is mangled).
Test Plan
pytest agent/tests/test_cli_memory.py agent/tests/test_persistent_memory.py agent/tests/test_remember_tool.py agent/tests/test_cli_init.py→ 81 passruff check agent/cli.py agent/src/memory/persistent.py agent/tests/test_cli_memory.py agent/tests/test_persistent_memory.py→ no new errors (14 pre-existing E402/F541/F401 untouched)time vibe-trading --version→ 1.0s~/.vibe-trading/memory/: list (with and without--typefilter), show by title / by full slug / by bare slug, search (Latin and CJK queries), forget with-y, withndecline, with closed stdin (EOF)vibe-trading run→ agent invokesremembertool → new entry visible inmemory list, retrievable viamemory show, matchable bymemory search, removable viamemory forget -yChecklist
src/agent/,src/session/,src/providers/)CONTRIBUTING.mdremember_toolworkflows are byte-for-byte compatible