henchman-ai 0.1.11__tar.gz → 0.1.12__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.
- henchman_ai-0.1.12/ALPHA_TEST_LOG.md +45 -0
- henchman_ai-0.1.12/BETA_TESTING_ISSUES.md +55 -0
- henchman_ai-0.1.12/BETA_TESTING_ISSUES2.md +215 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/PKG-INFO +1 -1
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/pyproject.toml +1 -1
- henchman_ai-0.1.12/src/henchman/cli/app.py +322 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/builtins.py +6 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/chat.py +50 -36
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/rag.py +9 -4
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/console.py +5 -1
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/input.py +65 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/repl.py +190 -33
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/turn.py +15 -9
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/system.py +33 -1
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/utils/compaction.py +4 -3
- henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- henchman_ai-0.1.12/tests/__init__.py +0 -0
- henchman_ai-0.1.12/tests/cli/__init__.py +0 -0
- henchman_ai-0.1.12/tests/config/__init__.py +0 -0
- henchman_ai-0.1.12/tests/core/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_turn_state.py +12 -9
- henchman_ai-0.1.12/tests/mcp/__init__.py +0 -0
- henchman_ai-0.1.12/tests/providers/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/test_version.py +1 -1
- henchman_ai-0.1.12/tests/tools/__init__.py +0 -0
- henchman_ai-0.1.11/src/henchman/cli/app.py +0 -213
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/.github/copilot-instructions.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/.github/workflows/ci.yml +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/.github/workflows/publish.yml +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/.gitignore +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/CHANGELOG.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/LICENSE +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/MagicMock/mock.rag.cache_dir/131782863223120/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/MagicMock/mock.rag.cache_dir/131782952758032/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/MagicMock/mock.rag.cache_dir/131782953186608/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/MagicMock/mock.rag.cache_dir/131783467925184/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/131953655609296/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/131953656855104/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/131954146008512/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/131954155550192/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/134508512171968/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/134508512231200/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/134508512485616/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/134508855764000/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/__init__.py → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/139391159286624/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/cli/__init__.py → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/139391160298352/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/config/__init__.py → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/139391160361968/b0c5ce5844ad8acc/.rag.lock +0 -0
- /henchman_ai-0.1.11/tests/core/__init__.py → /henchman_ai-0.1.12/MagicMock/mock.rag.cache_dir/139391778658240/b0c5ce5844ad8acc/.rag.lock +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/PROJECT_PLAN.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/README.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/TASK_COMPLETION_SUMMARY.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/debug_compaction.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/api.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/configuration.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/extensions.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/getting-started.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/index.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/mcp.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/providers.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/docs/tools.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/README.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/conftest.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/helpers.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/test_answer_vs_action.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/test_coding_tasks.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/test_edit_precision.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/test_skills_memory.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/evals/test_tool_selection.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/fix_repl.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/fix_repl_simple.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/mkdocs.yml +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/replace_method.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/reproduce_400_error.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/run_interactive_tests.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/scripts/ci.sh +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/scripts/run_evals.sh +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/__main__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/extensions.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/mcp.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/plan.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/skill.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/commands/unlimited.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/json_output.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/prompts.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/repl.py.backup +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/cli/repl.py.backup2 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/config/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/config/context.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/config/schema.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/config/settings.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/agent.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/agent.py.backup +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/events.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/core/session.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/extensions/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/extensions/base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/extensions/manager.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/mcp/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/mcp/client.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/mcp/config.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/mcp/manager.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/mcp/tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/anthropic.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/deepseek.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/ollama.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/openai_compat.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/openai_compat.py.backup +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/providers/registry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/chunker.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/concurrency.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/embedder.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/indexer.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/repo_id.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/rag/store.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/skills/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/skills/executor.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/skills/learner.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/skills/models.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/skills/store.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/ask_user.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/file_edit.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/file_read.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/file_write.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/glob_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/grep.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/ls.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/rag_search.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/shell.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/builtins/web_fetch.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/tools/registry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/utils/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/utils/retry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/utils/tokens.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/utils/validation.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/src/henchman/version.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/test_compaction.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/test_compaction_fix.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/test_fixes.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/test_output.txt +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/test_run.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- /henchman_ai-0.1.11/tests/mcp/__init__.py → /henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572362825280/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- /henchman_ai-0.1.11/tests/providers/__init__.py → /henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572765096208/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- /henchman_ai-0.1.11/tests/tools/__init__.py → /henchman_ai-0.1.12/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/link_lists.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572822401392/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/125572896176320/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520285986352/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520286228656/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520287933552/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/data_level0.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/header.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/88b10860-7f3a-42c9-a3aa-20e09850b445/length.bin +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/chroma/chroma.sqlite3 +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/MagicMock/mock.rag.cache_dir/135520588106160/b0c5ce5844ad8acc/manifest.json +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/commands/test_plan.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/commands/test_skill.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/commands/test_skill_extended.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/commands/test_unlimited.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_app.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_app_extended.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_builtins.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_chat_command.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_cli_smoke.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_commands.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_commands_repro.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_console.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_enhanced_tool_display.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_input.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_input_bindings.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_json_output.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_keyboard_fixes.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_keyboard_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_keyboard_interrupt.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_keyboard_verification.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_loop_protection.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_mcp_command.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_repl.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_repl_attribute_fix.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_repl_startup_message.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/cli/test_repl_toolbar.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/config/test_context.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/config/test_schema.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/config/test_settings.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/conftest.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_automatic_compaction.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_events.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_session.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_session_manager.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/core/test_streaming_tool_calls.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/e2e/test_context_safety.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/e2e/test_tool_fix.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/empty_message_validation/test_empty_messages.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/extensions/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/extensions/test_base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/extensions/test_command.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/extensions/test_manager.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/integration/test_context_limits.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/integration/test_tool_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/mcp/test_client.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/mcp/test_config.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/mcp/test_manager.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/mcp/test_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_413_error_handling.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_anthropic.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_deepseek.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_ollama.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_openai_compat.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/providers/test_registry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_chunker.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_concurrency.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_concurrency_smoke.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_embedder.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_indexer.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_rag_command.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_rag_concurrency_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_rag_search_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_repo_id.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_store.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/rag/test_system.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_executor.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_learner.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_markdown_skills.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_models.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_store.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/skills/test_store_extended.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/smoke/test_escape_key_behavior.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/smoke/test_large_file_handling.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/test_coverage_suite.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/test_main.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_ask_user_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_base.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_directory_tools.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_file_tools.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_grep_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_plan_mode_enforcement.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_registry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_shell_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/tools/test_web_fetch_tool.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/INTERACTIVE_SESSION_TESTS.md +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/__init__.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/conftest.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_agent.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_compaction_llm.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_events.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_llm.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_mcp.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_plan_mode.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_repl_e2e.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_repl_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_session.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_skills.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_slash_commands.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_tokens_llm.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_tool_calls.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/ui_integration/test_tool_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_compaction.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_compaction_edge_cases.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_compaction_validation.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_multi_turn_tool_calls.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_protected_zone.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_retry.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_summarization.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_tiktoken_integration.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_token_counter_extended.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_tool_sequence_compaction.py +0 -0
- {henchman_ai-0.1.11 → henchman_ai-0.1.12}/tests/utils/test_validation.py +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Henchman Alpha Test Log
|
|
2
|
+
**Date:** 2026-02-02
|
|
3
|
+
**Tester:** Senior Principal QA Lead
|
|
4
|
+
|
|
5
|
+
## Objectives
|
|
6
|
+
- Verify architectural constraints.
|
|
7
|
+
- specific tool usage patterns.
|
|
8
|
+
- Test self-correction capabilities.
|
|
9
|
+
- Verify context maintenance.
|
|
10
|
+
|
|
11
|
+
## Issues Found
|
|
12
|
+
|
|
13
|
+
### 1. Session Management Completely Missing in CLI
|
|
14
|
+
**Severity:** Critical
|
|
15
|
+
**Description:** `app.py` does not initialize `SessionManager` or `Session` for `Repl`. As a result, conversation history is not recorded or saved to disk by default.
|
|
16
|
+
**Impact:** Users lose all conversation history when the CLI exits.
|
|
17
|
+
|
|
18
|
+
### 2. Session Loading Amnesia
|
|
19
|
+
**Severity:** High
|
|
20
|
+
**Description:** When a session is loaded (manually or via `/chat resume`), the `Agent.messages` history is not automatically synced with the loaded session's history. While `/chat resume` attempts to do this, it doesn't update `Repl.session`, leading to inconsistent state.
|
|
21
|
+
**Impact:** Context loss and failed auto-saves for resumed sessions.
|
|
22
|
+
|
|
23
|
+
### 4. Interrupted Turn Inconsistency
|
|
24
|
+
**Severity:** High
|
|
25
|
+
**Description:** If a turn is interrupted (e.g., Ctrl+C) during tool execution, the assistant message is recorded in the session with tool calls, but no tool results are added.
|
|
26
|
+
**Impact:** Resuming such a session creates an invalid message sequence for most LLM providers (OpenAI requires responses for all tool calls), causing the next turn to fail with an API error.
|
|
27
|
+
|
|
28
|
+
### 5. Brittle Tool Execution Loop
|
|
29
|
+
**Severity:** Medium
|
|
30
|
+
**Description:** `Repl` executes tool calls sequentially instead of using `ToolRegistry.execute_batch`.
|
|
31
|
+
**Impact:** Significant performance penalty when multiple independent tools are called in a single turn.
|
|
32
|
+
|
|
33
|
+
### 6. Duplication Risk in Context Compaction
|
|
34
|
+
**Severity:** Low/Medium
|
|
35
|
+
**Description:** The `ContextCompactor` extracts system messages to prepend them to the result, but also includes them in the first atomic sequence. If the first sequence is kept, the system message is duplicated.
|
|
36
|
+
**Impact:** Minor token waste, but could potentially confuse some sensitive models.
|
|
37
|
+
|
|
38
|
+
### 7. Missing Tool Confirmation Handler
|
|
39
|
+
**Severity:** Critical (Security)
|
|
40
|
+
**Description:** The CLI does not set a confirmation handler on the `ToolRegistry`. Consequently, tools marked as `WRITE`, `EXECUTE`, or `NETWORK` (like `shell` or `write_file`) are executed immediately without any user oversight.
|
|
41
|
+
**Impact:** Highly dangerous. The agent can run arbitrary shell commands or modify any file without the user being able to stop it.
|
|
42
|
+
|
|
43
|
+
## Summary for Development Team
|
|
44
|
+
The core agent logic and loop protection are robust, but the **CLI integration layer** is currently an "Alpha" state with broken session persistence and context handling. Priority should be given to wiring up `SessionManager` in `app.py` and ensuring `Agent.messages` is always synced with `Repl.session.messages`.
|
|
45
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Beta Testing Issues Report
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-02-02
|
|
4
|
+
**Tester:** Gemini CLI
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
The Henchman CLI (v0.1.11) shows significant improvements over the Alpha state. Session persistence is functional (files are saved), and tool confirmation workflows are implemented. However, critical CLI commands for managing sessions and MCP servers are missing from the registry, making it impossible to manage sessions or MCP connections interactively.
|
|
8
|
+
|
|
9
|
+
## Issues Found
|
|
10
|
+
|
|
11
|
+
### 1. Missing `/chat` Command
|
|
12
|
+
**Severity:** High
|
|
13
|
+
**Description:** The `/chat` command (implemented in `src/henchman/cli/commands/chat.py`) is not registered in `src/henchman/cli/commands/builtins.py`.
|
|
14
|
+
**Impact:** Users cannot save, list, or resume sessions interactively. The `ChatCommand` class exists but is unreachable.
|
|
15
|
+
**Reproduction:**
|
|
16
|
+
```bash
|
|
17
|
+
henchman
|
|
18
|
+
> /chat list
|
|
19
|
+
✗ Unknown command: /chat
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. Missing `/mcp` Command
|
|
23
|
+
**Severity:** Medium
|
|
24
|
+
**Description:** The `/mcp` command (implemented in `src/henchman/cli/commands/mcp.py`) is not registered in `src/henchman/cli/commands/builtins.py`.
|
|
25
|
+
**Impact:** Users cannot manage or inspect Model Context Protocol (MCP) servers interactively.
|
|
26
|
+
**Reproduction:**
|
|
27
|
+
```bash
|
|
28
|
+
henchman
|
|
29
|
+
> /mcp list
|
|
30
|
+
✗ Unknown command: /mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Session Resume Requires Tags
|
|
34
|
+
**Severity:** Medium
|
|
35
|
+
**Description:** The `/chat resume` command implementation only supports loading by `tag` (`manager.load_by_tag`). It does not appear to support loading by Session ID. Since sessions are created without tags by default, users cannot easily resume a specific previous session without manually editing the session file to add a tag or implementing a tagging workflow.
|
|
36
|
+
**Impact:** Resuming the "last session" or a specific untitled session is difficult/impossible via the CLI.
|
|
37
|
+
**Location:** `src/henchman/cli/commands/chat.py`, `_resume` method.
|
|
38
|
+
|
|
39
|
+
### 4. Cosmetic: CLI Self-Identification
|
|
40
|
+
**Severity:** Low
|
|
41
|
+
**Description:** `henchman --version` output identifies as `mlg`.
|
|
42
|
+
**Output:** `mlg, version 0.1.11`
|
|
43
|
+
**Expected:** `henchman, version 0.1.11`
|
|
44
|
+
|
|
45
|
+
## Verification of Alpha Issues
|
|
46
|
+
|
|
47
|
+
- **Session Management Missing:** [FIXED] `SessionManager` is correctly initialized in `app.py`, and session files are created in `~/.henchman/sessions`.
|
|
48
|
+
- **Missing Tool Confirmation Handler:** [FIXED] `ToolRegistry.set_confirmation_handler` is called in `Repl.__init__`, and prompts are displayed for dangerous tools (verified with `write_file`).
|
|
49
|
+
- **Session Loading Amnesia:** [PARTIALLY VERIFIED] Could not fully verify due to missing `/chat` command, but code inspection of `ChatCommand._resume` suggests it now correctly syncs `Repl.session` and `Agent.messages`.
|
|
50
|
+
|
|
51
|
+
## Recommendations
|
|
52
|
+
|
|
53
|
+
1. **Register Missing Commands:** Add `ChatCommand()` and `McpCommand()` to the list returned by `get_builtin_commands()` in `src/henchman/cli/commands/builtins.py`.
|
|
54
|
+
2. **Enhance Resume:** Modify `ChatCommand._resume` to try loading by ID if loading by tag fails, or add a separate `load` subcommand that accepts IDs.
|
|
55
|
+
3. **Auto-Resume:** Consider an option or flag (e.g., `henchman --resume`) to automatically load the most recent session.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Henchman Beta Testing Notes
|
|
2
|
+
|
|
3
|
+
**Tester**: GitHub Copilot (Claude Opus 4.5)
|
|
4
|
+
**Date**: February 2, 2026
|
|
5
|
+
**Version Tested**: v0.1.11 (package name: `mlg`)
|
|
6
|
+
**CLI Location**: `/home/matthew/mlg-cli`
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Overview
|
|
11
|
+
|
|
12
|
+
Henchman is a model-agnostic AI agent CLI. It supports interactive sessions and headless mode with `--prompt`. This document captures observations, issues, and feedback from beta testing.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## CLI Options Discovered
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Usage: henchman [OPTIONS]
|
|
20
|
+
|
|
21
|
+
Options:
|
|
22
|
+
--version Show the version and exit.
|
|
23
|
+
-p, --prompt TEXT Run with a single prompt and exit
|
|
24
|
+
--output-format [text|json|stream-json] Output format for responses
|
|
25
|
+
--plan Start in plan mode (read-only)
|
|
26
|
+
--help Show this message and exit.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Testing Sessions
|
|
32
|
+
|
|
33
|
+
### Session 1 - Initial Launch (Prior)
|
|
34
|
+
- **Command**: `henchman`
|
|
35
|
+
- **Working Directory**: `/home/matthew/mlg-cli`
|
|
36
|
+
- **Exit Code**: 130 (Ctrl+C interrupt)
|
|
37
|
+
- **Status**: ⚠️ Inconclusive - manual interrupt
|
|
38
|
+
|
|
39
|
+
### Session 2 - Help & Version Check
|
|
40
|
+
- **Command**: `henchman --help` and `henchman --version`
|
|
41
|
+
- **Result**: ✅ Success - Clean output, proper CLI structure
|
|
42
|
+
- **Version**: 0.1.11
|
|
43
|
+
|
|
44
|
+
### Session 3 - Simple Workspace Query
|
|
45
|
+
- **Command**: `henchman -p "What files are in this workspace?"`
|
|
46
|
+
- **Result**: ✅ Success - Correctly listed directories and files
|
|
47
|
+
- **Tools Used**: `ls()`
|
|
48
|
+
- **Iterations**: 1/25
|
|
49
|
+
|
|
50
|
+
### Session 4 - File Reading & Summarization
|
|
51
|
+
- **Command**: `henchman -p "Read .github/copilot-instructions.md and summarize"`
|
|
52
|
+
- **Result**: ✅ Success - Read file, provided accurate 2-sentence summary
|
|
53
|
+
- **Tools Used**: `read_file()`
|
|
54
|
+
- **Quality**: Excellent - understood project context accurately
|
|
55
|
+
|
|
56
|
+
### Session 5 - Plan Mode (Complex Analysis)
|
|
57
|
+
- **Command**: `henchman --plan -p "What tests would you run to validate Elo?"`
|
|
58
|
+
- **Result**: ✅ Success - Comprehensive analysis with 10 test categories
|
|
59
|
+
- **Tools Used**: `ls()`, `read_file()`, `rag_search()`
|
|
60
|
+
- **Iterations**: 14/25
|
|
61
|
+
- **Note**: Loop detection triggered at iteration 11 ("⚠ Possible loop detected") but recovered gracefully
|
|
62
|
+
|
|
63
|
+
### Session 6 - Code Generation (File Creation)
|
|
64
|
+
- **Command**: `henchman -p "Create a test file for NBAEloRating"`
|
|
65
|
+
- **Result**: ✅ Success - Created valid, working test file
|
|
66
|
+
- **Tools Used**: `rag_search()`, `read_file()`, `ls()`, `write_file()`
|
|
67
|
+
- **File Created**: `tests/test_henchman_demo.py` (3944 bytes)
|
|
68
|
+
- **Test Verification**: Both tests passed when run with pytest!
|
|
69
|
+
- **User Interaction**: Required "y/n" confirmation for file write (good safety feature)
|
|
70
|
+
|
|
71
|
+
### Session 7 - JSON Output Format
|
|
72
|
+
- **Command**: `henchman -p "What is 2+2?" --output-format json`
|
|
73
|
+
- **Result**: ✅ Success - Streamed JSON tokens properly
|
|
74
|
+
- **Note**: Output is token-by-token, final line has full response
|
|
75
|
+
|
|
76
|
+
### Session 8 - Shell Command Execution
|
|
77
|
+
- **Command**: `henchman -p "Run 'echo Hello from Henchman'"`
|
|
78
|
+
- **Result**: ✅ Success - Executed command, showed output
|
|
79
|
+
- **Tools Used**: `shell()`
|
|
80
|
+
- **User Interaction**: Required "y/n" confirmation (good safety feature)
|
|
81
|
+
|
|
82
|
+
### Session 9 - Multi-Step File Operations
|
|
83
|
+
- **Command**: `henchman -p "Find Python files in plugins/elo and count them"`
|
|
84
|
+
- **Result**: ✅ Success - Found all 15 Python files correctly
|
|
85
|
+
- **Tools Used**: `ls()`, `glob()`, `shell()`
|
|
86
|
+
- **Iterations**: 6/35
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Issues Found
|
|
91
|
+
|
|
92
|
+
### Issue #1: Loop Detection Warning (Minor)
|
|
93
|
+
- **Severity**: Low
|
|
94
|
+
- **Description**: During complex analysis tasks, Henchman triggers "⚠ Possible loop detected" warnings when reading multiple files sequentially.
|
|
95
|
+
- **Observed In**: Session 5 (Plan mode analysis)
|
|
96
|
+
- **Impact**: None - it recovered and continued successfully
|
|
97
|
+
- **Suggestion**: Consider adjusting the loop detection heuristics to differentiate between legitimate sequential file reads and actual loops.
|
|
98
|
+
|
|
99
|
+
### Issue #2: Version Mismatch Display
|
|
100
|
+
- **Severity**: Very Low (cosmetic)
|
|
101
|
+
- **Description**: `--version` shows `mlg, version 0.1.11` but product is "Henchman"
|
|
102
|
+
- **Expected**: `henchman, version 0.1.11`
|
|
103
|
+
- **Impact**: Confusion about package vs. product naming
|
|
104
|
+
|
|
105
|
+
### Issue #3: JSON Output Token Streaming
|
|
106
|
+
- **Severity**: Low
|
|
107
|
+
- **Description**: JSON output streams token-by-token which may not be ideal for programmatic consumption
|
|
108
|
+
- **Observed**: `{"type": "content", "data": "2"}` per token
|
|
109
|
+
- **Suggestion**: Consider a `--output-format json-complete` option for full response in single JSON object
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Feature Requests
|
|
114
|
+
|
|
115
|
+
1. **Non-interactive mode flag**: A `--yes` or `-y` flag to auto-approve tool executions for CI/CD pipelines
|
|
116
|
+
2. **Verbosity control**: `--quiet` or `--verbose` flags to control output detail
|
|
117
|
+
3. **Session logging**: Option to log full session to file for debugging
|
|
118
|
+
4. **Context file**: Ability to specify a context file (like copilot-instructions.md) for automatic project conventions
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Positive Observations
|
|
123
|
+
|
|
124
|
+
### ✅ Excellent Code Quality
|
|
125
|
+
The test file Henchman generated was:
|
|
126
|
+
- Properly structured with docstrings
|
|
127
|
+
- Followed existing project conventions
|
|
128
|
+
- Included multiple test cases beyond requirements
|
|
129
|
+
- **Actually passed when run with pytest!**
|
|
130
|
+
|
|
131
|
+
### ✅ Smart Tool Selection
|
|
132
|
+
- Uses RAG search for semantic queries
|
|
133
|
+
- Falls back to file system operations for concrete tasks
|
|
134
|
+
- Chains tools effectively (ls → read_file → write_file)
|
|
135
|
+
|
|
136
|
+
### ✅ Good Safety Features
|
|
137
|
+
- Prompts for confirmation before file writes
|
|
138
|
+
- Prompts for confirmation before shell commands
|
|
139
|
+
- Clear display of what tool is being called
|
|
140
|
+
|
|
141
|
+
### ✅ Context Awareness
|
|
142
|
+
- Understood project structure quickly
|
|
143
|
+
- Read relevant files before generating code
|
|
144
|
+
- Matched existing code style and imports
|
|
145
|
+
|
|
146
|
+
### ✅ Plan Mode
|
|
147
|
+
- Excellent for read-only analysis
|
|
148
|
+
- Thorough exploration of codebase
|
|
149
|
+
- Generates actionable recommendations
|
|
150
|
+
|
|
151
|
+
### ✅ Progress Indicators
|
|
152
|
+
- Shows iteration count (e.g., "[Iter 3/25 | 3 calls | 2K tokens]")
|
|
153
|
+
- Indicates token usage and protection status
|
|
154
|
+
- Shows "✓ progress" vs "⚠ spinning" status
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Comparison Notes
|
|
159
|
+
|
|
160
|
+
As an agentic coding AI myself, here's my evaluation:
|
|
161
|
+
|
|
162
|
+
- [x] **Tool Usage**: Excellent - smart tool selection, effective chaining
|
|
163
|
+
- [x] **Context Awareness**: Excellent - understands project structure
|
|
164
|
+
- [x] **Autonomy**: Good - handles multi-step tasks independently
|
|
165
|
+
- [x] **Error Recovery**: Good - recovered from loop detection warnings
|
|
166
|
+
- [x] **Code Quality**: Excellent - generated working, idiomatic code
|
|
167
|
+
- [x] **Communication**: Good - clear about what it's doing
|
|
168
|
+
- [x] **Persistence**: Good - follows through on complex tasks
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Testing Checklist
|
|
173
|
+
|
|
174
|
+
- [x] Basic CLI functionality
|
|
175
|
+
- [x] File reading/editing capabilities
|
|
176
|
+
- [x] Terminal command execution
|
|
177
|
+
- [ ] Multi-file refactoring (not tested yet)
|
|
178
|
+
- [ ] Error handling and recovery (partially tested)
|
|
179
|
+
- [x] Project-specific conventions
|
|
180
|
+
- [ ] Database operations (not tested yet)
|
|
181
|
+
- [x] Test execution (generated tests that work!)
|
|
182
|
+
- [ ] Long-running task management (not tested yet)
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Recommendations for Henchman Team
|
|
187
|
+
|
|
188
|
+
1. **Fix version string**: Change from `mlg` to `henchman` in `--version` output
|
|
189
|
+
2. **Tune loop detection**: Current threshold may be too aggressive for legitimate file exploration
|
|
190
|
+
3. **Add batch mode**: For CI/CD integration, add `--yes` flag to skip confirmations
|
|
191
|
+
4. **Document tool set**: List available tools (ls, read_file, write_file, shell, rag_search, glob) in docs
|
|
192
|
+
5. **Consider token limits**: Show remaining context budget more prominently
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Overall Assessment
|
|
197
|
+
|
|
198
|
+
**Rating: 8.5/10** ⭐⭐⭐⭐
|
|
199
|
+
|
|
200
|
+
Henchman is a solid, well-designed agentic AI CLI. The headless mode (`-p`) is particularly useful for scripting. Code generation quality is impressive - the test file it created actually worked! The safety features (confirmations for writes/commands) are appropriate for a beta. Minor polish issues exist but don't impact functionality.
|
|
201
|
+
|
|
202
|
+
**Would recommend for**: Developers who want CLI-based AI assistance for file exploration, code generation, and analysis tasks.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Changelog
|
|
207
|
+
|
|
208
|
+
| Date | Notes |
|
|
209
|
+
|------|-------|
|
|
210
|
+
| 2026-02-02 | Created initial beta testing document |
|
|
211
|
+
| 2026-02-02 | Completed comprehensive testing - 9 sessions, 3 issues found, overall positive |
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
*Testing complete. Document may be updated with additional findings.*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: henchman-ai
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.12
|
|
4
4
|
Summary: A model-agnostic AI agent CLI - your AI henchman for the terminal
|
|
5
5
|
Project-URL: Homepage, https://github.com/MGPowerlytics/henchman-ai
|
|
6
6
|
Project-URL: Repository, https://github.com/MGPowerlytics/henchman-ai
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"""MLG CLI application entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
import anyio
|
|
10
|
+
import click
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from henchman.version import VERSION
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from henchman.providers.base import ModelProvider
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _get_provider() -> ModelProvider:
|
|
22
|
+
"""Get the configured model provider.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A ModelProvider instance.
|
|
26
|
+
|
|
27
|
+
Raises:
|
|
28
|
+
click.ClickException: If no provider is configured.
|
|
29
|
+
"""
|
|
30
|
+
from henchman.providers import DeepSeekProvider, get_default_registry
|
|
31
|
+
|
|
32
|
+
# Check environment for API key
|
|
33
|
+
api_key = os.environ.get("DEEPSEEK_API_KEY") or os.environ.get("HENCHMAN_API_KEY")
|
|
34
|
+
|
|
35
|
+
if api_key:
|
|
36
|
+
return DeepSeekProvider(api_key=api_key)
|
|
37
|
+
|
|
38
|
+
# Try to load from settings
|
|
39
|
+
try:
|
|
40
|
+
from henchman.config import load_settings
|
|
41
|
+
|
|
42
|
+
settings = load_settings()
|
|
43
|
+
registry = get_default_registry()
|
|
44
|
+
|
|
45
|
+
provider_name = settings.providers.default or "deepseek"
|
|
46
|
+
provider_settings = getattr(settings.providers, provider_name, None)
|
|
47
|
+
|
|
48
|
+
if provider_settings:
|
|
49
|
+
return registry.create(
|
|
50
|
+
provider_name,
|
|
51
|
+
api_key=getattr(provider_settings, "api_key", None) or "",
|
|
52
|
+
model=getattr(provider_settings, "model", None),
|
|
53
|
+
)
|
|
54
|
+
except Exception: # pragma: no cover
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
raise click.ClickException( # pragma: no cover
|
|
58
|
+
"No API key configured. Set DEEPSEEK_API_KEY or configure in ~/.henchman/settings.yaml"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _run_interactive(output_format: str, plan_mode: bool = False, yes: bool = False) -> None:
|
|
63
|
+
"""Run interactive REPL mode.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
output_format: Output format (text, json, stream-json, json-complete).
|
|
67
|
+
plan_mode: Whether to start in plan mode.
|
|
68
|
+
yes: Auto-approve all tool executions.
|
|
69
|
+
"""
|
|
70
|
+
from pathlib import Path
|
|
71
|
+
from henchman.cli.repl import Repl, ReplConfig
|
|
72
|
+
from henchman.config import ContextLoader, load_settings
|
|
73
|
+
from henchman.core.session import SessionManager
|
|
74
|
+
from henchman.rag import initialize_rag
|
|
75
|
+
|
|
76
|
+
provider = _get_provider()
|
|
77
|
+
settings = load_settings()
|
|
78
|
+
|
|
79
|
+
# Load context from MLG.md files
|
|
80
|
+
context_loader = ContextLoader()
|
|
81
|
+
system_prompt = context_loader.load()
|
|
82
|
+
|
|
83
|
+
config = ReplConfig(system_prompt=system_prompt, auto_approve_tools=yes)
|
|
84
|
+
repl = Repl(
|
|
85
|
+
provider=provider,
|
|
86
|
+
console=console,
|
|
87
|
+
config=config,
|
|
88
|
+
settings=settings
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Initialize session management
|
|
92
|
+
session_manager = SessionManager()
|
|
93
|
+
project_hash = session_manager.compute_project_hash(Path.cwd())
|
|
94
|
+
session = session_manager.create_session(project_hash)
|
|
95
|
+
|
|
96
|
+
repl.session_manager = session_manager
|
|
97
|
+
repl.session = session
|
|
98
|
+
|
|
99
|
+
# Initialize RAG system
|
|
100
|
+
rag_system = initialize_rag(settings.rag, console=console, index=False)
|
|
101
|
+
if rag_system:
|
|
102
|
+
repl.tool_registry.register(rag_system.search_tool)
|
|
103
|
+
repl.rag_system = rag_system
|
|
104
|
+
|
|
105
|
+
# Set plan mode if requested
|
|
106
|
+
if plan_mode and repl.session:
|
|
107
|
+
repl.session.plan_mode = True
|
|
108
|
+
repl.tool_registry.set_plan_mode(True)
|
|
109
|
+
# Add plan mode prompt to system prompt
|
|
110
|
+
from henchman.cli.commands.plan import PLAN_MODE_PROMPT
|
|
111
|
+
repl.agent.system_prompt += PLAN_MODE_PROMPT
|
|
112
|
+
|
|
113
|
+
if output_format == "text":
|
|
114
|
+
anyio.run(repl.run)
|
|
115
|
+
else: # pragma: no cover
|
|
116
|
+
console.print(
|
|
117
|
+
"[yellow]Warning: JSON output formats not supported in interactive mode. "
|
|
118
|
+
"Using text format.[/yellow]"
|
|
119
|
+
)
|
|
120
|
+
anyio.run(repl.run)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _run_headless(prompt: str, output_format: str, plan_mode: bool = False, yes: bool = False) -> None:
|
|
124
|
+
"""Run headless mode with a single prompt.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
prompt: The prompt to process.
|
|
128
|
+
output_format: Output format (text, json, stream-json, json-complete).
|
|
129
|
+
plan_mode: Whether to run in plan mode.
|
|
130
|
+
yes: Auto-approve all tool executions.
|
|
131
|
+
"""
|
|
132
|
+
from pathlib import Path
|
|
133
|
+
from henchman.cli.json_output import JsonOutputRenderer
|
|
134
|
+
from henchman.cli.repl import Repl, ReplConfig
|
|
135
|
+
from henchman.config import ContextLoader, load_settings
|
|
136
|
+
from henchman.core.events import EventType
|
|
137
|
+
from henchman.core.session import SessionManager
|
|
138
|
+
from henchman.rag import initialize_rag
|
|
139
|
+
|
|
140
|
+
provider = _get_provider()
|
|
141
|
+
settings = load_settings()
|
|
142
|
+
|
|
143
|
+
# Load context from MLG.md files
|
|
144
|
+
context_loader = ContextLoader()
|
|
145
|
+
system_prompt = context_loader.load()
|
|
146
|
+
|
|
147
|
+
# Add plan mode prompt if requested
|
|
148
|
+
if plan_mode: # pragma: no cover
|
|
149
|
+
from henchman.cli.commands.plan import PLAN_MODE_PROMPT
|
|
150
|
+
system_prompt += PLAN_MODE_PROMPT
|
|
151
|
+
|
|
152
|
+
config = ReplConfig(system_prompt=system_prompt, auto_approve_tools=yes)
|
|
153
|
+
repl = Repl(provider=provider, console=console, config=config, settings=settings)
|
|
154
|
+
|
|
155
|
+
# Initialize session management
|
|
156
|
+
session_manager = SessionManager()
|
|
157
|
+
project_hash = session_manager.compute_project_hash(Path.cwd())
|
|
158
|
+
session = session_manager.create_session(project_hash)
|
|
159
|
+
|
|
160
|
+
repl.session_manager = session_manager
|
|
161
|
+
repl.session = session
|
|
162
|
+
|
|
163
|
+
# Initialize RAG system
|
|
164
|
+
rag_system = initialize_rag(settings.rag, index=False) # No console output in headless
|
|
165
|
+
if rag_system:
|
|
166
|
+
repl.tool_registry.register(rag_system.search_tool)
|
|
167
|
+
repl.rag_system = rag_system
|
|
168
|
+
|
|
169
|
+
# Set plan mode if requested
|
|
170
|
+
if plan_mode and repl.session: # pragma: no cover
|
|
171
|
+
repl.session.plan_mode = True
|
|
172
|
+
repl.tool_registry.set_plan_mode(True)
|
|
173
|
+
|
|
174
|
+
async def run_single_prompt_text() -> None: # pragma: no cover
|
|
175
|
+
"""Process a single prompt and exit with text output."""
|
|
176
|
+
if repl.rag_system:
|
|
177
|
+
asyncio.create_task(repl.rag_system.index_async())
|
|
178
|
+
await repl.process_input(prompt)
|
|
179
|
+
|
|
180
|
+
async def run_single_prompt_json() -> None:
|
|
181
|
+
"""Process a single prompt and exit with JSON output."""
|
|
182
|
+
from henchman.core.events import AgentEvent, EventType
|
|
183
|
+
from henchman.providers.base import ToolCall
|
|
184
|
+
from collections.abc import AsyncIterator
|
|
185
|
+
|
|
186
|
+
if repl.rag_system:
|
|
187
|
+
asyncio.create_task(repl.rag_system.index_async())
|
|
188
|
+
|
|
189
|
+
json_renderer = JsonOutputRenderer(console)
|
|
190
|
+
|
|
191
|
+
# We need to manually run the tool loop for JSON output
|
|
192
|
+
# to ensure all events are rendered as JSON
|
|
193
|
+
async def run_and_render(stream: AsyncIterator[AgentEvent]) -> None:
|
|
194
|
+
pending_tool_calls = []
|
|
195
|
+
async for event in stream:
|
|
196
|
+
json_renderer.render(event)
|
|
197
|
+
if event.type == EventType.TOOL_CALL_REQUEST:
|
|
198
|
+
pending_tool_calls.append(event.data)
|
|
199
|
+
|
|
200
|
+
if pending_tool_calls:
|
|
201
|
+
# Execute tool calls
|
|
202
|
+
for tool_call in pending_tool_calls:
|
|
203
|
+
result = await repl.tool_registry.execute(tool_call.name, tool_call.arguments)
|
|
204
|
+
repl.agent.submit_tool_result(tool_call.id, result.content)
|
|
205
|
+
# Emit tool result event
|
|
206
|
+
json_renderer.render(AgentEvent(type=EventType.TOOL_RESULT, data=result))
|
|
207
|
+
|
|
208
|
+
# Continue
|
|
209
|
+
await run_and_render(repl.agent.continue_with_tool_results())
|
|
210
|
+
|
|
211
|
+
await run_and_render(repl.agent.run(prompt))
|
|
212
|
+
|
|
213
|
+
async def run_single_prompt_stream_json() -> None:
|
|
214
|
+
"""Process a single prompt and exit with streaming JSON output."""
|
|
215
|
+
from henchman.core.events import AgentEvent, EventType
|
|
216
|
+
from henchman.providers.base import ToolCall
|
|
217
|
+
from collections.abc import AsyncIterator
|
|
218
|
+
|
|
219
|
+
if repl.rag_system:
|
|
220
|
+
asyncio.create_task(repl.rag_system.index_async())
|
|
221
|
+
|
|
222
|
+
json_renderer = JsonOutputRenderer(console)
|
|
223
|
+
|
|
224
|
+
async def run_and_render_stream(stream: AsyncIterator[AgentEvent]) -> None:
|
|
225
|
+
pending_tool_calls = []
|
|
226
|
+
async for event in stream:
|
|
227
|
+
json_renderer.render_stream_json(event)
|
|
228
|
+
if event.type == EventType.TOOL_CALL_REQUEST:
|
|
229
|
+
pending_tool_calls.append(event.data)
|
|
230
|
+
|
|
231
|
+
if pending_tool_calls:
|
|
232
|
+
for tool_call in pending_tool_calls:
|
|
233
|
+
result = await repl.tool_registry.execute(tool_call.name, tool_call.arguments)
|
|
234
|
+
repl.agent.submit_tool_result(tool_call.id, result.content)
|
|
235
|
+
json_renderer.render_stream_json(AgentEvent(type=EventType.TOOL_RESULT, data=result))
|
|
236
|
+
|
|
237
|
+
await run_and_render_stream(repl.agent.continue_with_tool_results())
|
|
238
|
+
|
|
239
|
+
await run_and_render_stream(repl.agent.run(prompt))
|
|
240
|
+
|
|
241
|
+
async def run_single_prompt_json_complete() -> None:
|
|
242
|
+
"""Process a single prompt and exit with complete JSON output."""
|
|
243
|
+
from henchman.core.events import AgentEvent, EventType
|
|
244
|
+
from henchman.providers.base import ToolCall
|
|
245
|
+
from collections.abc import AsyncIterator
|
|
246
|
+
|
|
247
|
+
if repl.rag_system:
|
|
248
|
+
asyncio.create_task(repl.rag_system.index_async())
|
|
249
|
+
|
|
250
|
+
json_renderer = JsonOutputRenderer(console)
|
|
251
|
+
all_events: list[AgentEvent] = []
|
|
252
|
+
|
|
253
|
+
async def run_and_collect(stream: AsyncIterator[AgentEvent]) -> None:
|
|
254
|
+
pending_tool_calls = []
|
|
255
|
+
async for event in stream:
|
|
256
|
+
all_events.append(event)
|
|
257
|
+
if event.type == EventType.TOOL_CALL_REQUEST:
|
|
258
|
+
pending_tool_calls.append(event.data)
|
|
259
|
+
|
|
260
|
+
if pending_tool_calls:
|
|
261
|
+
for tool_call in pending_tool_calls:
|
|
262
|
+
result = await repl.tool_registry.execute(tool_call.name, tool_call.arguments)
|
|
263
|
+
repl.agent.submit_tool_result(tool_call.id, result.content)
|
|
264
|
+
all_events.append(AgentEvent(type=EventType.TOOL_RESULT, data=result))
|
|
265
|
+
|
|
266
|
+
await run_and_collect(repl.agent.continue_with_tool_results())
|
|
267
|
+
|
|
268
|
+
await run_and_collect(repl.agent.run(prompt))
|
|
269
|
+
json_renderer.render_final_json(all_events)
|
|
270
|
+
|
|
271
|
+
if output_format == "text":
|
|
272
|
+
anyio.run(run_single_prompt_text)
|
|
273
|
+
elif output_format == "json":
|
|
274
|
+
anyio.run(run_single_prompt_json)
|
|
275
|
+
elif output_format == "stream-json":
|
|
276
|
+
anyio.run(run_single_prompt_stream_json)
|
|
277
|
+
elif output_format == "json-complete":
|
|
278
|
+
anyio.run(run_single_prompt_json_complete)
|
|
279
|
+
else: # pragma: no cover
|
|
280
|
+
pass
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@click.command()
|
|
284
|
+
@click.version_option(version=VERSION, prog_name="henchman")
|
|
285
|
+
@click.option("-p", "--prompt", help="Run with a single prompt and exit")
|
|
286
|
+
@click.option(
|
|
287
|
+
"--output-format",
|
|
288
|
+
type=click.Choice(["text", "json", "stream-json", "json-complete"]),
|
|
289
|
+
default="text",
|
|
290
|
+
help="Output format for responses",
|
|
291
|
+
)
|
|
292
|
+
@click.option(
|
|
293
|
+
"--plan",
|
|
294
|
+
is_flag=True,
|
|
295
|
+
default=False,
|
|
296
|
+
help="Start in plan mode (read-only)",
|
|
297
|
+
)
|
|
298
|
+
@click.option(
|
|
299
|
+
"-y",
|
|
300
|
+
"--yes",
|
|
301
|
+
is_flag=True,
|
|
302
|
+
default=False,
|
|
303
|
+
help="Auto-approve all tool executions (non-interactive mode)",
|
|
304
|
+
)
|
|
305
|
+
def cli(prompt: str | None, output_format: str, plan: bool, yes: bool) -> None:
|
|
306
|
+
"""Henchman-AI: A model-agnostic AI agent CLI.
|
|
307
|
+
|
|
308
|
+
Start an interactive session or run with --prompt for headless mode.
|
|
309
|
+
"""
|
|
310
|
+
if prompt:
|
|
311
|
+
_run_headless(prompt, output_format, plan, yes)
|
|
312
|
+
else:
|
|
313
|
+
_run_interactive(output_format, plan, yes)
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def main() -> None: # pragma: no cover
|
|
317
|
+
"""Main entry point for the CLI."""
|
|
318
|
+
cli()
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
if __name__ == "__main__": # pragma: no cover
|
|
322
|
+
main()
|
|
@@ -6,6 +6,8 @@ This module provides the default slash commands like /help, /quit, /clear, /tool
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from henchman.cli.commands import Command, CommandContext
|
|
9
|
+
from henchman.cli.commands.chat import ChatCommand
|
|
10
|
+
from henchman.cli.commands.mcp import McpCommand
|
|
9
11
|
from henchman.cli.commands.plan import PlanCommand
|
|
10
12
|
from henchman.cli.commands.rag import RagCommand
|
|
11
13
|
from henchman.cli.commands.skill import SkillCommand
|
|
@@ -53,6 +55,8 @@ class HelpCommand(Command):
|
|
|
53
55
|
ctx.console.print(" /plan - Toggle Plan Mode (Read-Only)")
|
|
54
56
|
ctx.console.print(" /rag - Manage semantic search index")
|
|
55
57
|
ctx.console.print(" /skill - Manage and execute learned skills")
|
|
58
|
+
ctx.console.print(" /chat - Manage chat sessions (save, list, resume)")
|
|
59
|
+
ctx.console.print(" /mcp - Manage MCP server connections")
|
|
56
60
|
ctx.console.print(" /quit - Exit the CLI")
|
|
57
61
|
ctx.console.print(" /clear - Clear the screen")
|
|
58
62
|
ctx.console.print(" /tools - List available tools")
|
|
@@ -206,6 +210,8 @@ def get_builtin_commands() -> list[Command]:
|
|
|
206
210
|
QuitCommand(),
|
|
207
211
|
ClearCommand(),
|
|
208
212
|
ToolsCommand(),
|
|
213
|
+
ChatCommand(),
|
|
214
|
+
McpCommand(),
|
|
209
215
|
PlanCommand(),
|
|
210
216
|
RagCommand(),
|
|
211
217
|
SkillCommand(),
|