agentic-cli 0.4.3__tar.gz → 0.4.4__tar.gz
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.
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/CHANGELOG.md +32 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/CLAUDE.md +0 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/PKG-INFO +4 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/agents.py +3 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/settings.py +14 -6
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/websearch_demo.py +5 -5
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/pyproject.toml +4 -2
- agentic_cli-0.4.4/requirements.txt +4 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/__init__.py +4 -3
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/__init__.py +1 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/app.py +63 -21
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/builtin_commands.py +139 -17
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/commands.py +0 -25
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/message_processor.py +120 -82
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/settings_introspection.py +0 -26
- agentic_cli-0.4.4/src/agentic_cli/cli/usage_tracker.py +154 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/workflow_controller.py +20 -103
- agentic_cli-0.4.4/src/agentic_cli/config.py +338 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/constants.py +1 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/manager.py +76 -82
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/vector_store.py +73 -54
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/logging.py +0 -14
- agentic_cli-0.4.4/src/agentic_cli/persistence/_utils.py +81 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/persistence/artifacts.py +55 -28
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/persistence/session.py +61 -28
- agentic_cli-0.4.4/src/agentic_cli/settings_mixins.py +120 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/settings_persistence.py +24 -8
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/__init__.py +3 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/arxiv_tools.py +11 -3
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/execution_tools.py +1 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/executor.py +6 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/glob_tool.py +1 -7
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/grep_tool.py +3 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/hitl_tools.py +29 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/interaction_tools.py +5 -3
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/knowledge_tools.py +150 -87
- agentic_cli-0.4.4/src/agentic_cli/tools/pdf_utils.py +55 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/registry.py +51 -12
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/__init__.py +57 -0
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/backends/__init__.py +5 -0
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/backends/base.py +46 -0
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/backends/jupyter_local.py +238 -0
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/manager.py +174 -0
- agentic_cli-0.4.4/src/agentic_cli/tools/sandbox/models.py +16 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/search.py +7 -7
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/executor.py +2 -64
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/task_tools.py +38 -15
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/converter.py +6 -18
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch_tool.py +22 -5
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/__init__.py +8 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/adk/event_processor.py +4 -98
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/adk/manager.py +184 -49
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/base_manager.py +254 -64
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/context.py +2 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/events.py +42 -47
- agentic_cli-0.4.4/src/agentic_cli/workflow/factory.py +112 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/__init__.py +0 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/graph_builder.py +44 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/manager.py +142 -46
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/state.py +0 -18
- agentic_cli-0.4.4/src/agentic_cli/workflow/models.py +508 -0
- agentic_cli-0.4.4/src/agentic_cli/workflow/retry.py +47 -0
- agentic_cli-0.4.4/src/agentic_cli/workflow/settings.py +448 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/task_progress.py +1 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/tool_summaries.py +5 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/test_adk_integration.py +20 -36
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_commands.py +0 -49
- agentic_cli-0.4.4/tests/test_concurrent_stores.py +192 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_config.py +41 -28
- agentic_cli-0.4.4/tests/test_context_trimming.py +249 -0
- agentic_cli-0.4.4/tests/test_context_window.py +89 -0
- agentic_cli-0.4.4/tests/test_grep_tool_cache.py +29 -0
- agentic_cli-0.4.4/tests/test_input_callback.py +26 -0
- agentic_cli-0.4.4/tests/test_kb_helpers.py +63 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_knowledge_tools.py +139 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_langgraph.py +2 -30
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_memory.py +37 -47
- agentic_cli-0.4.4/tests/test_model_registry.py +500 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_planning.py +26 -25
- agentic_cli-0.4.4/tests/test_session_save_resume.py +490 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_settings_persistence.py +35 -5
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_task_tools.py +154 -154
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_tool_summaries.py +10 -33
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_tools.py +152 -2
- agentic_cli-0.4.4/tests/test_update_setting.py +21 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_usage_tracker.py +84 -2
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_webfetch.py +35 -35
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_workflow.py +0 -1
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_workflow_controller.py +20 -19
- agentic_cli-0.4.4/tests/tools/test_sandbox.py +579 -0
- agentic_cli-0.4.3/requirements.txt +0 -13
- agentic_cli-0.4.3/src/agentic_cli/cli/settings.py +0 -58
- agentic_cli-0.4.3/src/agentic_cli/cli/usage_tracker.py +0 -87
- agentic_cli-0.4.3/src/agentic_cli/config.py +0 -610
- agentic_cli-0.4.3/src/agentic_cli/persistence/_utils.py +0 -33
- agentic_cli-0.4.3/src/agentic_cli/resolvers.py +0 -28
- agentic_cli-0.4.3/src/agentic_cli/workflow/settings.py +0 -153
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/.gitignore +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/LICENSE +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/README.md +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/environment.yml +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/arxiv_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/fileops_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/hello_agent.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/hello_langgraph.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/memory_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/planning_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/__main__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/app.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/research_demo/commands.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/shell_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/examples/webfetch_demo.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/requirements-dev.txt +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/cli/settings_command.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/_mocks.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/embeddings.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/models.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/knowledge_base/sources.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/persistence/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/arxiv_source.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/file_read.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/file_write.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/memory_tools.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/planning_tools.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/audit.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/classifier.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/config.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/models.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/path_analyzer.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/preprocessor.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/risk_assessor.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/sandbox.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/shell/tokenizer.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/fetcher.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/robots.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/summarizer.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/tools/webfetch/validator.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/adk/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/adk/llm_event_logger.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/config.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/persistence/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/persistence/checkpointers.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/persistence/stores.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/tools/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/tools/file_search.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/langgraph/tools/shell.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/src/agentic_cli/workflow/thinking.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/conftest.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/conftest.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/helpers.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/test_langgraph_integration.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/integration/test_live.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_arxiv_tools.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_embeddings.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_hitl.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_knowledge_base.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_persistence.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_thinking.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_token_caching.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/test_vector_store.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/tools/__init__.py +0 -0
- {agentic_cli-0.4.3 → agentic_cli-0.4.4}/tests/tools/test_shell_security.py +0 -0
|
@@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.4] - 2026-03-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Session Save/Resume**: Persistent conversations across CLI exits with `/save` and `/resume`
|
|
12
|
+
- **Sandbox Executor**: Stateful multi-turn Python code execution with sandboxing and default deps
|
|
13
|
+
- **Context Window Management**: Native ADK and LangGraph context trimming with source-level detection
|
|
14
|
+
- **Context Window Visibility**: Trim detection, status bar token display, and `/status` breakdown
|
|
15
|
+
- **Dynamic Model Registry**: Replace static model lists with live API discovery from Google/Anthropic
|
|
16
|
+
- **Task Progress Display**: Rich-colored task progress in thinking box, persisted across turns
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- **Architecture**: Separated workflow+tools layer from UI/CLI layer; composable settings mixins
|
|
20
|
+
- **DRY/SOLID Cleanup** (PR #59, #60): Deduplicated config paths, ArXiv parsing, session persistence, artifact loading; extracted `drain_trim_events()`, `format_detail_rows()`, `format_task_checklist()`; removed dead code (`get_help()`, `FinanceResearchState`, unused settings introspection); replaced dynamic `type()` with `_SessionEvent` class; moved token-drop heuristic into `UsageTracker`
|
|
21
|
+
- **MessageProcessor**: Removed dead code, unused params, cleaner state init
|
|
22
|
+
- **HITL**: Removed dead Future-based machinery, simplified callback path
|
|
23
|
+
- **Ripgrep**: Cache availability check with `lru_cache`
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
- Executor import validation blocking allowed submodules
|
|
27
|
+
- Settings not persisting (replaced `exclude_defaults` with explicit exclusion)
|
|
28
|
+
- `verbose_thinking` not persisting across restarts
|
|
29
|
+
- Thread safety, path traversal, and TOCTOU race conditions in persistence
|
|
30
|
+
- VectorStore crash safety: write mappings before FAISS index
|
|
31
|
+
- Embeddings: update in-memory state after `ingest_document`
|
|
32
|
+
- `open_document`: extension allowlist, DANGEROUS permission, audit logging
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
- `FinanceResearchState` (domain-specific code in shared library)
|
|
36
|
+
- Dead `get_help()`, `get_ui_section`, `is_ui_hidden` methods
|
|
37
|
+
- Dead `generate_tool_summary`, `clear_context`/`unbind_context`, model constant re-exports
|
|
38
|
+
- Dead `cli/settings.py` re-export shim
|
|
39
|
+
|
|
8
40
|
## [0.4.3] - 2026-02-12
|
|
9
41
|
|
|
10
42
|
### Changed
|
|
@@ -20,7 +20,6 @@ agentic-cli/
|
|
|
20
20
|
│ ├── __init__.py # Package exports, lazy imports
|
|
21
21
|
│ ├── config.py # BaseSettings (pydantic-settings)
|
|
22
22
|
│ ├── constants.py # Shared constants, truncate()
|
|
23
|
-
│ ├── resolvers.py # Model/path constants (GOOGLE_MODELS, etc.)
|
|
24
23
|
│ ├── settings_persistence.py
|
|
25
24
|
│ ├── logging.py
|
|
26
25
|
│ ├── cli/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
4
4
|
Summary: A framework for building domain-specific agentic CLI applications
|
|
5
5
|
Project-URL: Homepage, https://github.com/shoom1/agentic-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/shoom1/agentic-cli
|
|
@@ -17,6 +17,8 @@ Requires-Python: >=3.12
|
|
|
17
17
|
Requires-Dist: feedparser>=6.0.0
|
|
18
18
|
Requires-Dist: google-adk[genai]>=0.4.0
|
|
19
19
|
Requires-Dist: httpx>=0.27.0
|
|
20
|
+
Requires-Dist: ipykernel>=6.0.0
|
|
21
|
+
Requires-Dist: jupyter-client>=8.0.0
|
|
20
22
|
Requires-Dist: numpy>=1.26.0
|
|
21
23
|
Requires-Dist: prompt-toolkit>=3.0.0
|
|
22
24
|
Requires-Dist: pydantic-settings>=2.0.0
|
|
@@ -24,7 +26,7 @@ Requires-Dist: pydantic>=2.0.0
|
|
|
24
26
|
Requires-Dist: pypdf>=4.0.0
|
|
25
27
|
Requires-Dist: rich>=13.0.0
|
|
26
28
|
Requires-Dist: structlog>=24.0.0
|
|
27
|
-
Requires-Dist: thinking-prompt>=0.2.
|
|
29
|
+
Requires-Dist: thinking-prompt>=0.2.5
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
30
32
|
Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
|
|
@@ -108,7 +108,7 @@ When the user asks you to research something:
|
|
|
108
108
|
6. For arXiv paper research, **delegate to arxiv_specialist**
|
|
109
109
|
7. Execute ONE task at a time, updating the plan after each
|
|
110
110
|
8. Use `web_fetch` to extract information from specific URLs found during research
|
|
111
|
-
9. Use `execute_python` for
|
|
111
|
+
9. Use `execute_python` for quick calculations and data validation
|
|
112
112
|
10. Use `ask_clarification` when you need user input to proceed
|
|
113
113
|
11. Update the plan with `save_plan` if you discover changes are needed
|
|
114
114
|
12. Store learnings with `save_memory` and share them with the user
|
|
@@ -156,7 +156,8 @@ AGENT_CONFIGS = [
|
|
|
156
156
|
# arXiv (2 tools)
|
|
157
157
|
search_arxiv,
|
|
158
158
|
fetch_arxiv_paper,
|
|
159
|
-
# Document store (
|
|
159
|
+
# Document store (4 tools)
|
|
160
|
+
search_knowledge_base,
|
|
160
161
|
ingest_document,
|
|
161
162
|
list_documents,
|
|
162
163
|
read_document,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from pydantic import Field
|
|
6
5
|
from pydantic_settings import SettingsConfigDict
|
|
7
6
|
|
|
8
7
|
from agentic_cli import BaseSettings
|
|
@@ -27,10 +26,19 @@ class ResearchDemoSettings(BaseSettings):
|
|
|
27
26
|
extra="ignore",
|
|
28
27
|
)
|
|
29
28
|
|
|
30
|
-
app_name: str = Field(default="research_demo")
|
|
31
|
-
workspace_dir: Path = Field(default=Path.home() / ".research_demo")
|
|
32
|
-
|
|
33
29
|
def __init__(self, **kwargs):
|
|
34
|
-
|
|
35
|
-
kwargs.setdefault("
|
|
30
|
+
kwargs.setdefault("app_name", "research_demo")
|
|
31
|
+
kwargs.setdefault("workspace_dir", Path.home() / ".research_demo")
|
|
36
32
|
super().__init__(**kwargs)
|
|
33
|
+
|
|
34
|
+
def model_post_init(self, __context):
|
|
35
|
+
"""Override verbose_thinking default without blocking JSON persistence.
|
|
36
|
+
|
|
37
|
+
kwargs.setdefault would inject at init level (highest priority),
|
|
38
|
+
overriding saved JSON values. model_post_init runs after all sources
|
|
39
|
+
are resolved, and model_fields_set tracks which fields were explicitly
|
|
40
|
+
set by any source (env, JSON, init kwargs). We only apply our custom
|
|
41
|
+
default when no source provided a value.
|
|
42
|
+
"""
|
|
43
|
+
if "verbose_thinking" not in self.model_fields_set:
|
|
44
|
+
object.__setattr__(self, "verbose_thinking", False)
|
|
@@ -22,7 +22,7 @@ import sys
|
|
|
22
22
|
|
|
23
23
|
from agentic_cli.tools.search import (
|
|
24
24
|
web_search,
|
|
25
|
-
|
|
25
|
+
WebSearchResult,
|
|
26
26
|
TavilyBackend,
|
|
27
27
|
BraveBackend,
|
|
28
28
|
SEARCH_BACKENDS,
|
|
@@ -89,20 +89,20 @@ def demo_configuration_check():
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def demo_search_result_format():
|
|
92
|
-
"""Demo the
|
|
92
|
+
"""Demo the WebSearchResult data structure."""
|
|
93
93
|
print("\n" + "=" * 60)
|
|
94
|
-
print("
|
|
94
|
+
print("WebSearchResult Data Structure")
|
|
95
95
|
print("=" * 60)
|
|
96
96
|
|
|
97
97
|
# Create sample result
|
|
98
|
-
sample =
|
|
98
|
+
sample = WebSearchResult(
|
|
99
99
|
title="Example Search Result",
|
|
100
100
|
url="https://example.com/article",
|
|
101
101
|
snippet="This is a sample snippet showing what search results look like...",
|
|
102
102
|
score=0.95,
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
-
print("
|
|
105
|
+
print(" WebSearchResult fields:")
|
|
106
106
|
print(f" title: {sample.title}")
|
|
107
107
|
print(f" url: {sample.url}")
|
|
108
108
|
print(f" snippet: {sample.snippet[:50]}...")
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentic-cli"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.4"
|
|
8
8
|
description = "A framework for building domain-specific agentic CLI applications"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -21,7 +21,7 @@ classifiers = [
|
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
22
|
]
|
|
23
23
|
dependencies = [
|
|
24
|
-
"thinking-prompt>=0.2.
|
|
24
|
+
"thinking-prompt>=0.2.5",
|
|
25
25
|
"google-adk[genai]>=0.4.0",
|
|
26
26
|
"pydantic>=2.0.0",
|
|
27
27
|
"pydantic-settings>=2.0.0",
|
|
@@ -32,6 +32,8 @@ dependencies = [
|
|
|
32
32
|
"numpy>=1.26.0",
|
|
33
33
|
"feedparser>=6.0.0",
|
|
34
34
|
"pypdf>=4.0.0",
|
|
35
|
+
"jupyter_client>=8.0.0",
|
|
36
|
+
"ipykernel>=6.0.0",
|
|
35
37
|
]
|
|
36
38
|
|
|
37
39
|
[project.optional-dependencies]
|
|
@@ -23,7 +23,7 @@ Note: GoogleADKWorkflowManager and LangGraphWorkflowManager are lazy-loaded to a
|
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
from agentic_cli.cli.app import BaseCLIApp
|
|
26
|
-
from agentic_cli.
|
|
26
|
+
from agentic_cli.workflow.factory import create_workflow_manager_from_settings
|
|
27
27
|
from agentic_cli.cli.commands import Command, CommandRegistry
|
|
28
28
|
from agentic_cli.workflow.config import AgentConfig
|
|
29
29
|
from agentic_cli.workflow.events import WorkflowEvent, EventType
|
|
@@ -40,7 +40,7 @@ from agentic_cli.config import (
|
|
|
40
40
|
)
|
|
41
41
|
from agentic_cli.settings_persistence import SettingsPersistence
|
|
42
42
|
from agentic_cli.workflow.settings import WorkflowSettingsMixin
|
|
43
|
-
from agentic_cli.
|
|
43
|
+
from agentic_cli.settings_mixins import AppSettingsMixin, CLISettingsMixin
|
|
44
44
|
|
|
45
45
|
# Heavy imports - lazy loaded on first access
|
|
46
46
|
_lazy_imports = {
|
|
@@ -88,7 +88,8 @@ __all__ = [
|
|
|
88
88
|
"reload_settings",
|
|
89
89
|
# Settings Mixins (organized settings by domain)
|
|
90
90
|
"WorkflowSettingsMixin",
|
|
91
|
+
"AppSettingsMixin",
|
|
91
92
|
"CLISettingsMixin",
|
|
92
93
|
]
|
|
93
94
|
|
|
94
|
-
__version__ = "0.4.
|
|
95
|
+
__version__ = "0.4.4"
|
|
@@ -11,7 +11,7 @@ from agentic_cli.cli.commands import (
|
|
|
11
11
|
from agentic_cli.cli.app import BaseCLIApp
|
|
12
12
|
from agentic_cli.cli.message_processor import MessageProcessor, MessageHistory, MessageType
|
|
13
13
|
from agentic_cli.cli.workflow_controller import WorkflowController
|
|
14
|
-
from agentic_cli.
|
|
14
|
+
from agentic_cli.settings_mixins import CLISettingsMixin
|
|
15
15
|
|
|
16
16
|
__all__ = [
|
|
17
17
|
"AppInfo",
|
|
@@ -111,6 +111,7 @@ class BaseCLIApp:
|
|
|
111
111
|
app_info: AppInfo,
|
|
112
112
|
agent_configs: list["AgentConfig"],
|
|
113
113
|
settings: BaseSettings,
|
|
114
|
+
session_id: str | None = None,
|
|
114
115
|
) -> None:
|
|
115
116
|
"""Initialize the CLI application.
|
|
116
117
|
|
|
@@ -118,7 +119,10 @@ class BaseCLIApp:
|
|
|
118
119
|
app_info: Application info (name, version, welcome message)
|
|
119
120
|
agent_configs: List of agent configurations for the workflow
|
|
120
121
|
settings: Application settings instance
|
|
122
|
+
session_id: Optional session ID for save/resume. If provided,
|
|
123
|
+
the session will be loaded on startup and saved on exit.
|
|
121
124
|
"""
|
|
125
|
+
self._session_id = session_id
|
|
122
126
|
# === Configuration ===
|
|
123
127
|
self._app_info = app_info
|
|
124
128
|
self._settings = settings
|
|
@@ -282,6 +286,16 @@ class BaseCLIApp:
|
|
|
282
286
|
"""Get the application settings."""
|
|
283
287
|
return self._settings
|
|
284
288
|
|
|
289
|
+
@property
|
|
290
|
+
def session_id(self) -> str | None:
|
|
291
|
+
"""Get the persistent session ID, if any."""
|
|
292
|
+
return self._session_id
|
|
293
|
+
|
|
294
|
+
@session_id.setter
|
|
295
|
+
def session_id(self, value: str | None) -> None:
|
|
296
|
+
"""Set the persistent session ID."""
|
|
297
|
+
self._session_id = value
|
|
298
|
+
|
|
285
299
|
def stop(self) -> None:
|
|
286
300
|
"""Stop the application."""
|
|
287
301
|
self.should_exit = True
|
|
@@ -305,32 +319,18 @@ class BaseCLIApp:
|
|
|
305
319
|
needs_reinit = False
|
|
306
320
|
new_model = changes.get("model")
|
|
307
321
|
|
|
308
|
-
|
|
309
|
-
special_settings = {"model", "thinking_effort"}
|
|
310
|
-
|
|
311
|
-
# Apply model change (requires dedicated setter + reinit)
|
|
312
|
-
if new_model:
|
|
313
|
-
try:
|
|
314
|
-
self._settings.set_model(new_model)
|
|
315
|
-
needs_reinit = True
|
|
316
|
-
except ValueError as e:
|
|
317
|
-
self.session.add_error(f"Failed to set model: {e}")
|
|
318
|
-
return
|
|
322
|
+
reinit_settings = {"model", "thinking_effort"}
|
|
319
323
|
|
|
320
|
-
|
|
321
|
-
if "thinking_effort" in changes:
|
|
324
|
+
for key, value in changes.items():
|
|
322
325
|
try:
|
|
323
|
-
self._settings.
|
|
324
|
-
|
|
326
|
+
self._settings.update_setting(key, value)
|
|
327
|
+
if key in reinit_settings:
|
|
328
|
+
needs_reinit = True
|
|
325
329
|
except ValueError as e:
|
|
326
|
-
|
|
330
|
+
label = key.replace("_", " ").title()
|
|
331
|
+
self.session.add_error(f"Failed to set {label}: {e}")
|
|
327
332
|
return
|
|
328
333
|
|
|
329
|
-
# Apply all other settings directly
|
|
330
|
-
for key, value in changes.items():
|
|
331
|
-
if key not in special_settings:
|
|
332
|
-
object.__setattr__(self._settings, key, value)
|
|
333
|
-
|
|
334
334
|
# Reinitialize workflow if needed
|
|
335
335
|
if needs_reinit and self._workflow_controller.is_ready:
|
|
336
336
|
try:
|
|
@@ -351,7 +351,9 @@ class BaseCLIApp:
|
|
|
351
351
|
ClearCommand,
|
|
352
352
|
ExitCommand,
|
|
353
353
|
StatusCommand,
|
|
354
|
+
SandboxCommand,
|
|
354
355
|
PapersCommand,
|
|
356
|
+
SessionsCommand,
|
|
355
357
|
)
|
|
356
358
|
from agentic_cli.cli.settings_command import SettingsCommand
|
|
357
359
|
|
|
@@ -359,8 +361,10 @@ class BaseCLIApp:
|
|
|
359
361
|
self.command_registry.register(ClearCommand())
|
|
360
362
|
self.command_registry.register(ExitCommand())
|
|
361
363
|
self.command_registry.register(StatusCommand())
|
|
364
|
+
self.command_registry.register(SandboxCommand())
|
|
362
365
|
self.command_registry.register(SettingsCommand())
|
|
363
366
|
self.command_registry.register(PapersCommand())
|
|
367
|
+
self.command_registry.register(SessionsCommand())
|
|
364
368
|
|
|
365
369
|
async def process_input(self, user_input: str) -> None:
|
|
366
370
|
"""Process user input.
|
|
@@ -448,11 +452,45 @@ class BaseCLIApp:
|
|
|
448
452
|
except Exception as e:
|
|
449
453
|
logger.error("activity_log_save_failed", error=str(e))
|
|
450
454
|
|
|
455
|
+
async def _load_session_on_startup(self) -> None:
|
|
456
|
+
"""Load a saved session after workflow initialization."""
|
|
457
|
+
if not self._session_id:
|
|
458
|
+
return
|
|
459
|
+
|
|
460
|
+
if not await self._workflow_controller.ensure_initialized(self.session):
|
|
461
|
+
self.session.add_warning("Cannot load session — workflow not initialized.")
|
|
462
|
+
return
|
|
463
|
+
|
|
464
|
+
workflow = self._workflow_controller.workflow
|
|
465
|
+
loaded = await workflow.load_session(self._session_id)
|
|
466
|
+
if loaded:
|
|
467
|
+
self.session.add_success(f"Session '{self._session_id}' resumed.")
|
|
468
|
+
else:
|
|
469
|
+
self.session.add_message("system", f"New session '{self._session_id}'.")
|
|
470
|
+
|
|
471
|
+
async def _save_session_on_exit(self) -> None:
|
|
472
|
+
"""Save the current session on exit."""
|
|
473
|
+
if not self._session_id:
|
|
474
|
+
return
|
|
475
|
+
|
|
476
|
+
if not self._workflow_controller.is_ready:
|
|
477
|
+
return
|
|
478
|
+
|
|
479
|
+
workflow = self._workflow_controller.workflow
|
|
480
|
+
result = await workflow.save_session(self._session_id)
|
|
481
|
+
if result.get("success"):
|
|
482
|
+
logger.info("session_saved_on_exit", session_id=self._session_id)
|
|
483
|
+
else:
|
|
484
|
+
logger.error("session_save_on_exit_failed", error=result.get("error"))
|
|
485
|
+
|
|
451
486
|
async def run(self) -> None:
|
|
452
487
|
"""Run the main application loop."""
|
|
453
488
|
logger.info("repl_starting")
|
|
454
489
|
|
|
455
490
|
async with self._workflow_controller.background_init(self.session):
|
|
491
|
+
if self._session_id:
|
|
492
|
+
await self._load_session_on_startup()
|
|
493
|
+
|
|
456
494
|
# Register input handler
|
|
457
495
|
@self.session.on_input
|
|
458
496
|
async def handle_input(text: str) -> None:
|
|
@@ -463,6 +501,10 @@ class BaseCLIApp:
|
|
|
463
501
|
# Run the session - user sees prompt immediately!
|
|
464
502
|
await self.session.run_async()
|
|
465
503
|
|
|
504
|
+
# Save persistent session on exit
|
|
505
|
+
if self._session_id:
|
|
506
|
+
await self._save_session_on_exit()
|
|
507
|
+
|
|
466
508
|
# Auto-save activity log if enabled
|
|
467
509
|
if self._settings.log_activity and len(self.message_history) > 0:
|
|
468
510
|
await self._save_activity_log()
|
|
@@ -108,33 +108,98 @@ class StatusCommand(Command):
|
|
|
108
108
|
if init_error:
|
|
109
109
|
table.add_row("Error", f"[red]{init_error}[/red]")
|
|
110
110
|
|
|
111
|
+
# Persistent session info
|
|
112
|
+
sid = app.session_id
|
|
113
|
+
if sid:
|
|
114
|
+
table.add_row("Session", f"{sid} (persistent)")
|
|
115
|
+
else:
|
|
116
|
+
table.add_row("Session", "ephemeral (not saved)")
|
|
117
|
+
|
|
111
118
|
# Message history stats
|
|
112
119
|
table.add_row("Messages", str(len(app.message_history)))
|
|
113
120
|
|
|
114
121
|
# Token usage breakdown
|
|
115
122
|
tracker = getattr(app, "usage_tracker", None)
|
|
116
|
-
if tracker is not None
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
table.add_row("", "") # Spacer
|
|
120
|
-
table.add_row("LLM Invocations", str(tracker.invocation_count))
|
|
121
|
-
table.add_row("Input Tokens", format_tokens(tracker.prompt_tokens))
|
|
122
|
-
table.add_row("Output Tokens", format_tokens(tracker.completion_tokens))
|
|
123
|
-
table.add_row("Total Tokens", format_tokens(tracker.total_tokens))
|
|
124
|
-
if tracker.cached_tokens > 0:
|
|
125
|
-
table.add_row("Cached Tokens", format_tokens(tracker.cached_tokens))
|
|
126
|
-
if tracker.cache_creation_tokens > 0:
|
|
127
|
-
table.add_row("Cache Creation", format_tokens(tracker.cache_creation_tokens))
|
|
128
|
-
if tracker.thinking_tokens > 0:
|
|
129
|
-
table.add_row("Thinking Tokens", format_tokens(tracker.thinking_tokens))
|
|
130
|
-
if tracker.total_latency_ms > 0:
|
|
131
|
-
avg_ms = tracker.total_latency_ms / tracker.invocation_count
|
|
132
|
-
table.add_row("Avg Latency", f"{avg_ms:.0f}ms")
|
|
123
|
+
if tracker is not None:
|
|
124
|
+
for label, value in tracker.format_detail_rows():
|
|
125
|
+
table.add_row(label, value)
|
|
133
126
|
|
|
134
127
|
panel = Panel(table, title="[bold]Session Status[/bold]", border_style="cyan")
|
|
135
128
|
app.session.add_rich(panel)
|
|
136
129
|
|
|
137
130
|
|
|
131
|
+
class SandboxCommand(Command):
|
|
132
|
+
"""Manage sandbox sessions."""
|
|
133
|
+
|
|
134
|
+
def __init__(self) -> None:
|
|
135
|
+
super().__init__(
|
|
136
|
+
name="sandbox",
|
|
137
|
+
description="List and manage sandbox sessions",
|
|
138
|
+
aliases=["sb"],
|
|
139
|
+
usage="/sandbox [reset [session_id] [--all]]",
|
|
140
|
+
examples=["/sandbox", "/sandbox reset", "/sandbox reset my_session", "/sandbox reset --all"],
|
|
141
|
+
category=CommandCategory.WORKFLOW,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def execute(self, args: str, app: Any) -> None:
|
|
145
|
+
"""List or reset sandbox sessions."""
|
|
146
|
+
# Get sandbox manager from workflow
|
|
147
|
+
try:
|
|
148
|
+
workflow = app.workflow
|
|
149
|
+
manager = getattr(workflow, "sandbox_manager", None)
|
|
150
|
+
except (RuntimeError, AttributeError):
|
|
151
|
+
manager = None
|
|
152
|
+
|
|
153
|
+
if manager is None:
|
|
154
|
+
app.session.add_warning(
|
|
155
|
+
"Sandbox not available. Add sandbox tools to your agent config to enable it."
|
|
156
|
+
)
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
parsed = self.parse_args(args)
|
|
160
|
+
subcommand = parsed.positional.strip()
|
|
161
|
+
|
|
162
|
+
if subcommand.startswith("reset"):
|
|
163
|
+
# Parse session_id from after "reset"
|
|
164
|
+
rest = subcommand[len("reset"):].strip()
|
|
165
|
+
if parsed.has_flag("all"):
|
|
166
|
+
# Reset all sessions
|
|
167
|
+
sessions = manager.list_sessions()
|
|
168
|
+
if not sessions:
|
|
169
|
+
app.session.add_message("system", "No active sandbox sessions.")
|
|
170
|
+
return
|
|
171
|
+
for s in sessions:
|
|
172
|
+
manager.reset_session(s["session_id"])
|
|
173
|
+
app.session.add_success(f"Reset {len(sessions)} sandbox session(s).")
|
|
174
|
+
else:
|
|
175
|
+
session_id = rest if rest else "default"
|
|
176
|
+
was_active = manager.reset_session(session_id)
|
|
177
|
+
if was_active:
|
|
178
|
+
app.session.add_success(f"Sandbox session '{session_id}' reset.")
|
|
179
|
+
else:
|
|
180
|
+
app.session.add_warning(f"Sandbox session '{session_id}' was not active.")
|
|
181
|
+
else:
|
|
182
|
+
# List sessions
|
|
183
|
+
sessions = manager.list_sessions()
|
|
184
|
+
if not sessions:
|
|
185
|
+
app.session.add_message("system", "No active sandbox sessions.")
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
table = Table(title="Sandbox Sessions", show_lines=False, padding=(0, 1))
|
|
189
|
+
table.add_column("Session ID", style="bold cyan", no_wrap=True)
|
|
190
|
+
table.add_column("Working Dir", style="dim")
|
|
191
|
+
table.add_column("Executions", style="dim", no_wrap=True, justify="right")
|
|
192
|
+
|
|
193
|
+
for s in sessions:
|
|
194
|
+
table.add_row(
|
|
195
|
+
s["session_id"],
|
|
196
|
+
s["working_dir"],
|
|
197
|
+
str(s["execution_count"]),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
app.session.add_rich(table)
|
|
201
|
+
|
|
202
|
+
|
|
138
203
|
class PapersCommand(Command):
|
|
139
204
|
"""List documents in the knowledge base."""
|
|
140
205
|
|
|
@@ -227,3 +292,60 @@ class PapersCommand(Command):
|
|
|
227
292
|
)
|
|
228
293
|
|
|
229
294
|
app.session.add_rich(table)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class SessionsCommand(Command):
|
|
298
|
+
"""List and manage saved sessions."""
|
|
299
|
+
|
|
300
|
+
def __init__(self) -> None:
|
|
301
|
+
super().__init__(
|
|
302
|
+
name="sessions",
|
|
303
|
+
description="List saved sessions",
|
|
304
|
+
aliases=["sess"],
|
|
305
|
+
usage="/sessions [--delete=<id>]",
|
|
306
|
+
examples=["/sessions", "/sessions --delete=my-research"],
|
|
307
|
+
category=CommandCategory.SESSION,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
async def execute(self, args: str, app: Any) -> None:
|
|
311
|
+
"""Display saved sessions or delete one."""
|
|
312
|
+
from agentic_cli.persistence.session import SessionPersistence
|
|
313
|
+
|
|
314
|
+
parsed = self.parse_args(args)
|
|
315
|
+
delete_id = parsed.get_option("delete", "", str) or ""
|
|
316
|
+
|
|
317
|
+
persistence = SessionPersistence(app.settings)
|
|
318
|
+
|
|
319
|
+
if delete_id:
|
|
320
|
+
if persistence.delete_session(delete_id):
|
|
321
|
+
app.session.add_success(f"Session '{delete_id}' deleted.")
|
|
322
|
+
else:
|
|
323
|
+
app.session.add_error(f"Session '{delete_id}' not found.")
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
sessions = persistence.list_sessions()
|
|
327
|
+
if not sessions:
|
|
328
|
+
app.session.add_message("system", "No saved sessions.")
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
table = Table(title="Saved Sessions", show_lines=False, padding=(0, 1))
|
|
332
|
+
table.add_column("Session ID", style="bold cyan")
|
|
333
|
+
table.add_column("Messages", style="dim", justify="right")
|
|
334
|
+
table.add_column("Last Saved", style="dim")
|
|
335
|
+
table.add_column("Created", style="dim")
|
|
336
|
+
|
|
337
|
+
current_sid = app.session_id
|
|
338
|
+
|
|
339
|
+
for s in sessions:
|
|
340
|
+
sid = s["session_id"]
|
|
341
|
+
label = f"* {sid}" if sid == current_sid else sid
|
|
342
|
+
saved_at = s.get("saved_at", "")[:19].replace("T", " ")
|
|
343
|
+
created_at = s.get("created_at", "")[:10]
|
|
344
|
+
table.add_row(
|
|
345
|
+
label,
|
|
346
|
+
str(s.get("message_count", 0)),
|
|
347
|
+
saved_at,
|
|
348
|
+
created_at,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
app.session.add_rich(table)
|
|
@@ -245,31 +245,6 @@ class Command(ABC):
|
|
|
245
245
|
|
|
246
246
|
return [strip_quotes(token) for token in tokens]
|
|
247
247
|
|
|
248
|
-
def get_help(self) -> str:
|
|
249
|
-
"""Get detailed help text for this command.
|
|
250
|
-
|
|
251
|
-
Returns:
|
|
252
|
-
Formatted help string
|
|
253
|
-
"""
|
|
254
|
-
lines = [
|
|
255
|
-
f"**/{self.name}**",
|
|
256
|
-
f" {self.description}",
|
|
257
|
-
"",
|
|
258
|
-
f"**Usage:** `{self.usage}`",
|
|
259
|
-
]
|
|
260
|
-
|
|
261
|
-
if self.aliases:
|
|
262
|
-
lines.append(f"**Aliases:** {', '.join(f'/{a}' for a in self.aliases)}")
|
|
263
|
-
|
|
264
|
-
if self.examples:
|
|
265
|
-
lines.append("")
|
|
266
|
-
lines.append("**Examples:**")
|
|
267
|
-
for example in self.examples:
|
|
268
|
-
lines.append(f" `{example}`")
|
|
269
|
-
|
|
270
|
-
return "\n".join(lines)
|
|
271
|
-
|
|
272
|
-
|
|
273
248
|
class CommandRegistry:
|
|
274
249
|
"""Registry for managing slash commands.
|
|
275
250
|
|