agentram 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -53,9 +53,10 @@ If using the Claude plugin, `.mcp.json` sets `AGENTRAM_AUTO_INIT=true`, so these
53
53
 
54
54
  ## Terminal UI CLI
55
55
 
56
- AgentRAM also includes a dependency-free terminal UI with a Claude Code-like chat frame and button menu:
56
+ AgentRAM includes a terminal UI with Claude-like slash autocomplete. It uses `prompt_toolkit` when installed and falls back to plain `input()` otherwise:
57
57
 
58
58
  ```bash
59
+ pip install -e ".[tui]"
59
60
  agentram
60
61
  ```
61
62
 
@@ -870,3 +871,14 @@ Inside TUI:
870
871
  ```
871
872
 
872
873
  Do not paste raw API keys into `/bind-model`. `api_key_env` means environment variable name, e.g. `OPENAI_API_KEY`.
874
+
875
+
876
+ ### Optional Rich TUI
877
+
878
+ For live slash-command dropdown and command metadata, install the optional TUI extra:
879
+
880
+ ```bash
881
+ pip install agentram[tui]
882
+ ```
883
+
884
+ Without `prompt_toolkit`, AgentRAM still works with basic terminal input; `/` then Enter opens the full slash palette.
package/agentram/cli.py CHANGED
@@ -18,6 +18,30 @@ SUBAGENTS: dict[str, str] = {
18
18
  "documenter": "Update README or usage notes from current implementation. Keep docs concise and runnable.",
19
19
  }
20
20
 
21
+ SLASH_COMMANDS: list[tuple[str, str]] = [
22
+ ("/help", "Show commands"),
23
+ ("/init", "Create .agentram files"),
24
+ ("/status", "Show RAM status"),
25
+ ("/retrieve <query>", "Print relevant RAM context"),
26
+ ("/goal", "Show Goal Stack"),
27
+ ("/set-goal key=value ...", "Update mission/current_goal/current_task/etc"),
28
+ ("/drift <task>", "Check goal drift"),
29
+ ("/events", "Show latest events"),
30
+ ("/replay", "Replay events into memory"),
31
+ ("/agents", "List Claude-style subagent presets"),
32
+ ("/agent <name> <task>", "Build Claude Code subagent prompt"),
33
+ ("/models", "List bound coding models"),
34
+ ("/bind-model provider model", "Bind model endpoint"),
35
+ ("/orchestrate <task>", "Build multi-model agent plan"),
36
+ ("/ask <prompt>", "Execute bound models and return outputs"),
37
+ ("/claude-install [path]", "Install .claude commands and agents"),
38
+ ("/note <text>", "Save decision note"),
39
+ ("/clear", "Clear chat"),
40
+ ("/exit", "Quit"),
41
+ ("Plain text", "Capture coding chat message and retrieve context"),
42
+ ]
43
+
44
+
21
45
  SLASH_HELP = """Slash commands:
22
46
  /help Show commands
23
47
  /init Create .agentram files
@@ -151,10 +175,19 @@ def run_command(args: argparse.Namespace) -> int:
151
175
 
152
176
  def run_tui(context: McpContext) -> None:
153
177
  context.init_storage()
154
- messages = ["system > AgentRAM coding-agent CLI ready", f"system > RAM root: {context.ram_root}", "system > Type /help, /agent reviewer <task>, or plain coding request."]
178
+ prompt_session = create_prompt_session()
179
+ input_hint = "live slash autocomplete enabled" if prompt_session else "basic input fallback; install prompt_toolkit for live slash dropdown"
180
+ messages = [
181
+ "system > AgentRAM coding-agent CLI ready",
182
+ f"system > RAM root: {context.ram_root}",
183
+ f"system > {input_hint}",
184
+ ]
155
185
  while True:
156
186
  draw_screen(context, messages)
157
- user_input = input("You > ").strip()
187
+ try:
188
+ user_input = read_tui_input(prompt_session).strip()
189
+ except (EOFError, KeyboardInterrupt):
190
+ return
158
191
  if user_input in {"0", "q", "quit", "exit", "/exit", "/quit"}:
159
192
  return
160
193
  if user_input in BUTTONS:
@@ -170,9 +203,46 @@ def run_tui(context: McpContext) -> None:
170
203
  del messages[:-10]
171
204
 
172
205
 
206
+
207
+ def slash_command_names() -> list[str]:
208
+ return [command.split()[0] for command, _ in SLASH_COMMANDS if command.startswith("/")]
209
+
210
+
211
+ def slash_command_meta() -> dict[str, str]:
212
+ return {command.split()[0]: description for command, description in SLASH_COMMANDS if command.startswith("/")}
213
+
214
+
215
+ def create_prompt_session() -> Any | None:
216
+ try:
217
+ from prompt_toolkit import PromptSession
218
+ from prompt_toolkit.completion import WordCompleter
219
+ except ImportError:
220
+ return None
221
+
222
+ completer = WordCompleter(
223
+ slash_command_names(),
224
+ meta_dict=slash_command_meta(),
225
+ ignore_case=True,
226
+ sentence=True,
227
+ )
228
+ return PromptSession(
229
+ completer=completer,
230
+ complete_while_typing=True,
231
+ reserve_space_for_menu=12,
232
+ bottom_toolbar="Type / for slash commands · /agents for subagents · /ask to call bound models",
233
+ )
234
+
235
+
236
+ def read_tui_input(prompt_session: Any | None) -> str:
237
+ if prompt_session is None:
238
+ return input("You > ")
239
+ return prompt_session.prompt("You > ")
240
+
173
241
  def handle_chat_input(context: McpContext, text: str, messages: list[str] | None = None) -> str:
174
242
  if not text:
175
243
  return SLASH_HELP
244
+ if text == "/":
245
+ return slash_palette_text()
176
246
  if text.startswith("/"):
177
247
  return handle_slash_command(context, text, messages)
178
248
  call_tool(
@@ -313,8 +383,34 @@ def replay_text(context: McpContext, arguments: dict[str, object]) -> str:
313
383
 
314
384
 
315
385
  def agents_text() -> str:
316
- lines = ["Subagent presets:"]
317
- lines.extend(f"- {name}: {description}" for name, description in SUBAGENTS.items())
386
+ rows = [
387
+ ("agentram-memory", "Memory", "Records durable notes. Never edits code."),
388
+ ("agentram-planner", "Planner", "Checks Goal Stack and decomposes work."),
389
+ ("agentram-reviewer", "Reviewer", "Reviews changes, tests, and drift risk."),
390
+ ("agentram-docs", "Docs", "Updates README and usage docs."),
391
+ ]
392
+ lines = ["Claude Code subagents", ""]
393
+ for name, label, description in rows:
394
+ lines.append(f" {name.ljust(22)} {label}")
395
+ lines.append(f" {description}")
396
+ lines.append("")
397
+ lines.append("Use: /agent reviewer <task>")
398
+ lines.append("Install: /claude-install .")
399
+ return "\n".join(lines)
400
+
401
+
402
+ def slash_palette_text(filter_text: str = "") -> str:
403
+ normalized = filter_text.strip().lower().lstrip("/")
404
+ rows = []
405
+ for command, description in SLASH_COMMANDS:
406
+ command_key = command.lower().lstrip("/")
407
+ if normalized and normalized not in command_key:
408
+ continue
409
+ rows.append((command, description))
410
+ command_width = max([len(command) for command, _ in rows] + [12])
411
+ lines = ["Slash commands", ""]
412
+ for command, description in rows:
413
+ lines.append(f" {command.ljust(command_width)} {description}")
318
414
  return "\n".join(lines)
319
415
 
320
416
 
@@ -484,7 +580,7 @@ def draw_screen(context: McpContext, messages: list[str]) -> None:
484
580
  left_width = 30
485
581
  right_width = width - left_width - 3
486
582
  print("+" + "-" * (width - 2) + "+")
487
- print(box_text("AgentRAM Code Agent CLI | RAM + Claude subagents + slash commands", width))
583
+ print(box_text("AgentRAM Code | RAM + Claude subagents + slash commands", width))
488
584
  print("+" + "-" * (width - 2) + "+")
489
585
 
490
586
  sidebar = sidebar_lines(context)
@@ -495,7 +591,7 @@ def draw_screen(context: McpContext, messages: list[str]) -> None:
495
591
  right = chat[index] if index < len(chat) else ""
496
592
  print("| " + left[: left_width - 2].ljust(left_width - 2) + "| " + right[: right_width - 2].ljust(right_width - 2) + "|")
497
593
  print("+" + "-" * (left_width - 1) + "+" + "-" * (right_width) + "+")
498
- print(box_text("Input: plain coding request, /help, /agent reviewer <task>, /claude-install .", width))
594
+ print(box_text("Input: type / for live dropdown. Enter '/' for full palette. /ask calls models.", width))
499
595
  print("+" + "-" * (width - 2) + "+")
500
596
 
501
597
 
@@ -526,10 +622,10 @@ def sidebar_lines(context: McpContext) -> list[str]:
526
622
  " 9 help 0 exit",
527
623
  "",
528
624
  "CLAUDE",
625
+ " / = command palette",
626
+ " /agents",
529
627
  " /claude-install .",
530
628
  " /models",
531
- " /bind-model claude <model>",
532
- " /orchestrate <task>",
533
629
  " /ask <prompt>",
534
630
  ]
535
631
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentram",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Async, model-agnostic Working RAM for coding agents.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/trancongnghia/AGENT_RAM#readme",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentram"
7
- version = "0.1.3"
7
+ version = "0.1.5"
8
8
  description = "Async, model-agnostic RAM layer for agentic coding tools."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -21,6 +21,9 @@ classifiers = [
21
21
  requires-python = ">=3.10"
22
22
  dependencies = []
23
23
 
24
+ [project.optional-dependencies]
25
+ tui = ["prompt_toolkit>=3.0"]
26
+
24
27
  [project.urls]
25
28
  Homepage = "https://github.com/your-org/agentram"
26
29
  Repository = "https://github.com/your-org/agentram"