claude-smart 0.2.42 → 0.2.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/README.md +1 -1
- package/bin/claude-smart.js +2 -2
- package/package.json +9 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +23 -3
- package/plugin/pyproject.toml +3 -3
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +51 -4
- package/plugin/scripts/cli.sh +3 -1
- package/plugin/scripts/codex-hook.js +72 -4
- package/plugin/scripts/dashboard-build.sh +1 -0
- package/plugin/scripts/dashboard-service.sh +1 -0
- package/plugin/scripts/ensure-plugin-root.sh +1 -0
- package/plugin/scripts/hook_entry.sh +6 -3
- package/plugin/scripts/smart-install.sh +3 -2
- package/plugin/src/README.md +57 -0
- package/plugin/src/claude_smart/context_format.py +11 -12
- package/plugin/src/claude_smart/cs_cite.py +26 -12
- package/plugin/src/claude_smart/ids.py +13 -5
- package/plugin/uv.lock +126 -5
- package/plugin/vendor/reflexio/.env.example +62 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +274 -0
- package/plugin/vendor/reflexio/reflexio/README.md +184 -0
- package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
- package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
- package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
- package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +265 -0
- package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +964 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
- package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
- package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
- package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
- package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
- package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
- package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
- package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
- package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
- package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
- package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
- package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
- package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
- package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1112 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +768 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +333 -0
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
- package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +622 -0
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +2868 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +562 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
- package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +359 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1871 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +140 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +479 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +338 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +288 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +716 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.1.prompt.md +69 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +71 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +283 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.2.prompt.md +234 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +244 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +137 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +336 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1668 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/services/embedding_text.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +358 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +535 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +779 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +94 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +702 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +1045 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +772 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +737 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +803 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +80 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +756 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1298 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +384 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
- package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
- package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
- package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
|
@@ -0,0 +1,867 @@
|
|
|
1
|
+
"""Structured output formatting for Reflexio CLI.
|
|
2
|
+
|
|
3
|
+
All commands produce output through this module, ensuring a consistent
|
|
4
|
+
JSON envelope for agent consumption and Rich formatting for humans.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import sys
|
|
11
|
+
from datetime import UTC
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
# ---------------------------------------------------------------------------
|
|
15
|
+
# JSON envelope
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def render(
|
|
20
|
+
data: Any,
|
|
21
|
+
*,
|
|
22
|
+
json_mode: bool = False,
|
|
23
|
+
meta: dict[str, Any] | None = None,
|
|
24
|
+
exclude_none: bool = True,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Render command output to stdout.
|
|
27
|
+
|
|
28
|
+
In JSON mode, wraps data in a standard envelope:
|
|
29
|
+
``{"ok": true, "data": ..., "meta": {...}}``
|
|
30
|
+
|
|
31
|
+
In human mode, prints the data as-is (string) or pretty-prints dicts/lists.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
data: The data to render (dict, list, string, or Pydantic model)
|
|
35
|
+
json_mode: If True, output JSON envelope; otherwise human-readable
|
|
36
|
+
meta: Optional metadata (count, limit, has_more, etc.)
|
|
37
|
+
exclude_none: If True (default), omit None fields from Pydantic models
|
|
38
|
+
"""
|
|
39
|
+
if json_mode:
|
|
40
|
+
envelope: dict[str, Any] = {
|
|
41
|
+
"ok": True,
|
|
42
|
+
"data": _serialize(data, exclude_none=exclude_none),
|
|
43
|
+
}
|
|
44
|
+
if meta:
|
|
45
|
+
envelope["meta"] = meta
|
|
46
|
+
print(json.dumps(envelope, indent=2, default=str))
|
|
47
|
+
elif isinstance(data, str):
|
|
48
|
+
print(data)
|
|
49
|
+
elif isinstance(data, (dict, list)):
|
|
50
|
+
print(json.dumps(data, indent=2, default=str))
|
|
51
|
+
elif hasattr(data, "model_dump"):
|
|
52
|
+
# Pydantic model safety net — serialize to dict instead of __repr__
|
|
53
|
+
print(
|
|
54
|
+
json.dumps(
|
|
55
|
+
data.model_dump(mode="json", exclude_none=exclude_none),
|
|
56
|
+
indent=2,
|
|
57
|
+
default=str,
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
else:
|
|
61
|
+
print(data)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _serialize(data: Any, *, exclude_none: bool = True) -> Any:
|
|
65
|
+
"""Serialize data for JSON output, handling Pydantic models.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
data: Data to serialize
|
|
69
|
+
exclude_none: If True, omit None fields from Pydantic models
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Any: JSON-serializable data
|
|
73
|
+
"""
|
|
74
|
+
if hasattr(data, "model_dump"):
|
|
75
|
+
return data.model_dump(mode="json", exclude_none=exclude_none)
|
|
76
|
+
if isinstance(data, list):
|
|
77
|
+
return [_serialize(item, exclude_none=exclude_none) for item in data]
|
|
78
|
+
return data
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Human-readable formatters (kept from _output.py for backward compat)
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def print_info(msg: str) -> None:
|
|
87
|
+
"""Print an informational message to stderr.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
msg: Message to print
|
|
91
|
+
"""
|
|
92
|
+
print(msg, file=sys.stderr)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def print_error(msg: str) -> None:
|
|
96
|
+
"""Print an error message to stderr.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
msg: Error message to print
|
|
100
|
+
"""
|
|
101
|
+
print(f"Error: {msg}", file=sys.stderr)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _lifecycle_tag(obj: Any) -> str:
|
|
105
|
+
"""Return a lifecycle status tag for objects with a ``status`` attribute.
|
|
106
|
+
|
|
107
|
+
Only emits a tag for non-current statuses (PENDING, ARCHIVED). Objects
|
|
108
|
+
whose status is CURRENT or None are considered active and get no tag.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
obj: Any object that may have a ``status`` attribute
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
str: Tag string like " [PENDING]" or "", including leading space
|
|
115
|
+
"""
|
|
116
|
+
status = getattr(obj, "status", None)
|
|
117
|
+
if status is None:
|
|
118
|
+
return ""
|
|
119
|
+
if isinstance(status, str):
|
|
120
|
+
if status.upper() not in ("CURRENT", ""):
|
|
121
|
+
return f" [{status.upper()}]"
|
|
122
|
+
return ""
|
|
123
|
+
# Handle enum-style status where .value is not None
|
|
124
|
+
value = getattr(status, "value", None)
|
|
125
|
+
if value is not None:
|
|
126
|
+
return f" [{str(value).upper()}]"
|
|
127
|
+
return ""
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def format_profiles(profiles: list[Any]) -> str:
|
|
131
|
+
"""Format profile objects as markdown bullet list.
|
|
132
|
+
|
|
133
|
+
Shows a lifecycle tag ([PENDING]/[ARCHIVED]) when the profile status is
|
|
134
|
+
not CURRENT or None.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
profiles: Profile objects with a profile_content attribute
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
str: Formatted profiles, one per line
|
|
141
|
+
"""
|
|
142
|
+
if not profiles:
|
|
143
|
+
return ""
|
|
144
|
+
return "\n".join(f"- {p.content}{_lifecycle_tag(p)}" for p in profiles)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _structured_lines(playbook: Any) -> list[str]:
|
|
148
|
+
"""Build indented structured-data lines from a playbook's top-level fields.
|
|
149
|
+
|
|
150
|
+
Uses the canonical labels: Trigger, Rationale, Blocking Issue.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
playbook: Playbook object with optional trigger, rationale, blocking_issue attrs
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
list[str]: Indented lines (may be empty)
|
|
157
|
+
"""
|
|
158
|
+
lines: list[str] = []
|
|
159
|
+
if playbook is None:
|
|
160
|
+
return lines
|
|
161
|
+
if getattr(playbook, "trigger", None):
|
|
162
|
+
lines.append(f" Trigger: {playbook.trigger}")
|
|
163
|
+
if getattr(playbook, "rationale", None):
|
|
164
|
+
lines.append(f" Rationale: {playbook.rationale}")
|
|
165
|
+
return lines
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def format_agent_playbooks(agent_playbooks: list[Any]) -> str:
|
|
169
|
+
"""Format agent playbook objects with structured fields.
|
|
170
|
+
|
|
171
|
+
Shows an approval status tag ([APPROVED]/[PENDING]/[REJECTED]) from
|
|
172
|
+
``playbook_status`` and a lifecycle tag ([PENDING]/[ARCHIVED]) from
|
|
173
|
+
``status`` when not CURRENT/None.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
agent_playbooks: Agent playbook objects with content, trigger, rationale,
|
|
177
|
+
blocking_issue as top-level fields
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
str: Formatted agent playbooks with structured action lines
|
|
181
|
+
"""
|
|
182
|
+
if not agent_playbooks:
|
|
183
|
+
return ""
|
|
184
|
+
blocks: list[str] = []
|
|
185
|
+
for playbook in agent_playbooks:
|
|
186
|
+
# Approval status tag from playbook_status
|
|
187
|
+
approval_tag = ""
|
|
188
|
+
ps = getattr(playbook, "playbook_status", None)
|
|
189
|
+
if ps is not None:
|
|
190
|
+
ps_str = str(ps).upper() if not isinstance(ps, str) else ps.upper()
|
|
191
|
+
if ps_str:
|
|
192
|
+
approval_tag = f" [{ps_str}]"
|
|
193
|
+
|
|
194
|
+
lifecycle = _lifecycle_tag(playbook)
|
|
195
|
+
parts = [f"- {playbook.content}{approval_tag}{lifecycle}"]
|
|
196
|
+
parts.extend(_structured_lines(playbook))
|
|
197
|
+
blocks.append("\n".join(parts))
|
|
198
|
+
return "\n\n".join(blocks)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def format_user_playbooks(user_playbooks: list[Any]) -> str:
|
|
202
|
+
"""Format user playbook objects with structured fields and source metadata.
|
|
203
|
+
|
|
204
|
+
User playbooks show source/request_id metadata instead of approval status.
|
|
205
|
+
A lifecycle tag ([PENDING]/[ARCHIVED]) is shown when status is not
|
|
206
|
+
CURRENT/None.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
user_playbooks: User playbook objects with content, trigger, rationale,
|
|
210
|
+
blocking_issue as top-level fields, plus source and request_id
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
str: Formatted user playbooks with structured action lines and source
|
|
214
|
+
"""
|
|
215
|
+
if not user_playbooks:
|
|
216
|
+
return ""
|
|
217
|
+
blocks: list[str] = []
|
|
218
|
+
for playbook in user_playbooks:
|
|
219
|
+
lifecycle = _lifecycle_tag(playbook)
|
|
220
|
+
parts = [f"- {playbook.content}{lifecycle}"]
|
|
221
|
+
parts.extend(_structured_lines(playbook))
|
|
222
|
+
source = getattr(playbook, "source", None) or "unknown"
|
|
223
|
+
request_id = getattr(playbook, "request_id", None) or "unknown"
|
|
224
|
+
parts.append(f" Source: {source} (request: {request_id})")
|
|
225
|
+
blocks.append("\n".join(parts))
|
|
226
|
+
return "\n\n".join(blocks)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
# Rich human-readable rendering
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
#
|
|
233
|
+
# The ``format_*`` functions above return plain text and are used by
|
|
234
|
+
# ``format_context`` (which builds the agent bootstrap context block —
|
|
235
|
+
# pure text, no rich). The ``print_*`` functions below render directly
|
|
236
|
+
# to stdout via a ``rich.console.Console`` so they can wrap long lines,
|
|
237
|
+
# colorize labels, and visually separate entries. ``rich.console.Console``
|
|
238
|
+
# auto-detects whether stdout is a TTY and degrades to plain text when
|
|
239
|
+
# piped, so these are safe to use unconditionally.
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _human_relative_timestamp(ts: int | None) -> str:
|
|
243
|
+
"""Render a Unix timestamp as a short relative description.
|
|
244
|
+
|
|
245
|
+
Examples: ``"2m ago"``, ``"3h ago"``, ``"5d ago"``, ``"2026-04-10"``.
|
|
246
|
+
Returns ``"unknown"`` for None or invalid input.
|
|
247
|
+
"""
|
|
248
|
+
if not ts:
|
|
249
|
+
return "unknown"
|
|
250
|
+
try:
|
|
251
|
+
from datetime import datetime
|
|
252
|
+
|
|
253
|
+
delta = datetime.now(UTC) - datetime.fromtimestamp(int(ts), tz=UTC)
|
|
254
|
+
seconds = int(delta.total_seconds())
|
|
255
|
+
if seconds < 60:
|
|
256
|
+
return f"{seconds}s ago"
|
|
257
|
+
if seconds < 3600:
|
|
258
|
+
return f"{seconds // 60}m ago"
|
|
259
|
+
if seconds < 86400:
|
|
260
|
+
return f"{seconds // 3600}h ago"
|
|
261
|
+
if seconds < 86400 * 30:
|
|
262
|
+
return f"{seconds // 86400}d ago"
|
|
263
|
+
return datetime.fromtimestamp(int(ts), tz=UTC).strftime("%Y-%m-%d")
|
|
264
|
+
except (TypeError, ValueError, OSError):
|
|
265
|
+
return "unknown"
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def print_user_profiles(profiles: list[Any]) -> None:
|
|
269
|
+
"""Render user profiles with rich formatting: numbered, wrapped, dimmed metadata.
|
|
270
|
+
|
|
271
|
+
Each profile is shown as a numbered, word-wrapped block. The
|
|
272
|
+
profile content is the primary visual element; source / age /
|
|
273
|
+
lifecycle tag render in a dim metadata footer beneath. When stdout
|
|
274
|
+
is piped, rich auto-detects and emits plain text without colors so
|
|
275
|
+
the output stays grep-friendly.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
profiles: Profile objects with at least ``content``; optional
|
|
279
|
+
``source``, ``last_modified_timestamp``, ``status``.
|
|
280
|
+
"""
|
|
281
|
+
if not profiles:
|
|
282
|
+
return
|
|
283
|
+
from rich.console import Console
|
|
284
|
+
from rich.text import Text
|
|
285
|
+
|
|
286
|
+
console = Console()
|
|
287
|
+
for i, p in enumerate(profiles, 1):
|
|
288
|
+
# Index + content. ``Text.assemble`` keeps the styles separate
|
|
289
|
+
# while wrapping the whole line at the terminal width.
|
|
290
|
+
line = Text.assemble(
|
|
291
|
+
(f"[{i}] ", "bold cyan"),
|
|
292
|
+
getattr(p, "content", "") or "",
|
|
293
|
+
)
|
|
294
|
+
console.print(line)
|
|
295
|
+
|
|
296
|
+
# Dim metadata footer: source, age, lifecycle.
|
|
297
|
+
meta_parts: list[str] = []
|
|
298
|
+
source = getattr(p, "source", None)
|
|
299
|
+
if source:
|
|
300
|
+
meta_parts.append(f"source: {source}")
|
|
301
|
+
ts = getattr(p, "last_modified_timestamp", None)
|
|
302
|
+
if ts:
|
|
303
|
+
meta_parts.append(_human_relative_timestamp(ts))
|
|
304
|
+
lifecycle = _lifecycle_tag(p).strip()
|
|
305
|
+
if lifecycle:
|
|
306
|
+
meta_parts.append(lifecycle)
|
|
307
|
+
if meta_parts:
|
|
308
|
+
console.print(Text(" " + " · ".join(meta_parts), style="dim"))
|
|
309
|
+
console.print() # blank line between entries
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def print_user_playbooks(user_playbooks: list[Any]) -> None:
|
|
313
|
+
"""Render user playbooks as rich panels with aligned structured fields.
|
|
314
|
+
|
|
315
|
+
Each playbook becomes a bordered panel. The panel title is the
|
|
316
|
+
rule's content (the human-readable summary). The body is a
|
|
317
|
+
two-column grid of the structured fields (Trigger / Rationale /
|
|
318
|
+
Blocking Issue) with right-aligned bold labels and word-wrapped
|
|
319
|
+
values. A dim footer shows source + short request ID.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
user_playbooks: Playbook objects with ``content``,
|
|
323
|
+
``trigger``, ``rationale``, ``blocking_issue`` as top-level
|
|
324
|
+
fields, plus ``source``, ``request_id``, optional ``status``.
|
|
325
|
+
"""
|
|
326
|
+
if not user_playbooks:
|
|
327
|
+
return
|
|
328
|
+
from rich import box
|
|
329
|
+
from rich.console import Console, Group
|
|
330
|
+
from rich.panel import Panel
|
|
331
|
+
from rich.table import Table
|
|
332
|
+
from rich.text import Text
|
|
333
|
+
|
|
334
|
+
console = Console()
|
|
335
|
+
for i, pb in enumerate(user_playbooks, 1):
|
|
336
|
+
# Build a borderless grid for the structured fields. The label
|
|
337
|
+
# column is right-aligned and bold-dim; the value column wraps
|
|
338
|
+
# at the panel width.
|
|
339
|
+
grid = Table.grid(padding=(0, 2), expand=True)
|
|
340
|
+
grid.add_column(style="bold dim", justify="right", no_wrap=True)
|
|
341
|
+
grid.add_column(overflow="fold")
|
|
342
|
+
|
|
343
|
+
for label, attr in (
|
|
344
|
+
("Trigger", "trigger"),
|
|
345
|
+
("Rationale", "rationale"),
|
|
346
|
+
):
|
|
347
|
+
value = getattr(pb, attr, None)
|
|
348
|
+
if value:
|
|
349
|
+
grid.add_row(label, value)
|
|
350
|
+
|
|
351
|
+
# Footer: source + truncated request_id.
|
|
352
|
+
source = getattr(pb, "source", None) or "unknown"
|
|
353
|
+
request_id = getattr(pb, "request_id", None) or "unknown"
|
|
354
|
+
short_req = request_id[:8] if request_id != "unknown" else request_id
|
|
355
|
+
footer = Text(f"source: {source} · request: {short_req}", style="dim")
|
|
356
|
+
|
|
357
|
+
# Content goes inside the panel body, NOT the title — Rich's
|
|
358
|
+
# Panel title doesn't word-wrap and would truncate long rule
|
|
359
|
+
# summaries with a "─" continuation, leaving the most
|
|
360
|
+
# important part of the playbook unreadable. The title stays
|
|
361
|
+
# short (just the index) so the panel border is always tidy.
|
|
362
|
+
content_text = Text(
|
|
363
|
+
getattr(pb, "content", "") or "", style="bold", overflow="fold"
|
|
364
|
+
)
|
|
365
|
+
title = Text.assemble((f"[{i}]", "bold cyan"))
|
|
366
|
+
lifecycle = _lifecycle_tag(pb).strip()
|
|
367
|
+
if lifecycle:
|
|
368
|
+
title.append(f" {lifecycle}", style="yellow")
|
|
369
|
+
|
|
370
|
+
panel = Panel(
|
|
371
|
+
Group(content_text, Text(""), grid, Text(""), footer),
|
|
372
|
+
title=title,
|
|
373
|
+
title_align="left",
|
|
374
|
+
border_style="blue",
|
|
375
|
+
box=box.ROUNDED,
|
|
376
|
+
padding=(1, 2),
|
|
377
|
+
)
|
|
378
|
+
console.print(panel)
|
|
379
|
+
console.print() # spacer between panels
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def mask_api_key(api_key: str) -> str:
|
|
383
|
+
"""Mask an API key for safe display in command output.
|
|
384
|
+
|
|
385
|
+
Keeps the short provider prefix (``rflx-``, ``sk-``, etc.) and the
|
|
386
|
+
last 4 characters so the user can still distinguish which key is
|
|
387
|
+
active without exposing the full secret. Used by ``reflexio status
|
|
388
|
+
whoami`` and ``reflexio auth status``.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
api_key (str): The raw API key, possibly empty.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
str: A masked representation safe for printing. Returns
|
|
395
|
+
``"<unset>"`` for an empty key, ``"*" * len`` for very
|
|
396
|
+
short keys (<=8 chars), and ``"{prefix}****{last4}"``
|
|
397
|
+
for normal keys.
|
|
398
|
+
"""
|
|
399
|
+
if not api_key:
|
|
400
|
+
return "<unset>"
|
|
401
|
+
if len(api_key) <= 8:
|
|
402
|
+
return "*" * len(api_key)
|
|
403
|
+
prefix = api_key[:5] if "-" in api_key[:8] else api_key[:4]
|
|
404
|
+
return f"{prefix}****{api_key[-4:]}"
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def print_agent_playbooks(agent_playbooks: list[Any]) -> None:
|
|
408
|
+
"""Render agent playbooks as rich panels with approval badge.
|
|
409
|
+
|
|
410
|
+
Mirrors :func:`print_user_playbooks` but the title row shows an
|
|
411
|
+
approval badge (``[APPROVED]`` / ``[PENDING]`` / ``[REJECTED]``)
|
|
412
|
+
colour-coded green/yellow/red instead of the source metadata
|
|
413
|
+
footer. The body shows the structured Trigger / Rationale /
|
|
414
|
+
Blocking Issue grid. The footer shows ``playbook_name ·
|
|
415
|
+
agent_version · <relative time>``.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
agent_playbooks: Agent playbook objects with ``content``,
|
|
419
|
+
``trigger``, ``rationale``, ``blocking_issue`` as top-level
|
|
420
|
+
fields, ``playbook_status``, optional ``playbook_name``,
|
|
421
|
+
``agent_version``, ``created_at``, ``status``.
|
|
422
|
+
"""
|
|
423
|
+
if not agent_playbooks:
|
|
424
|
+
return
|
|
425
|
+
from rich import box
|
|
426
|
+
from rich.console import Console, Group
|
|
427
|
+
from rich.panel import Panel
|
|
428
|
+
from rich.table import Table
|
|
429
|
+
from rich.text import Text
|
|
430
|
+
|
|
431
|
+
badge_styles = {
|
|
432
|
+
"APPROVED": "bold green",
|
|
433
|
+
"PENDING": "bold yellow",
|
|
434
|
+
"REJECTED": "bold red",
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
console = Console()
|
|
438
|
+
for i, pb in enumerate(agent_playbooks, 1):
|
|
439
|
+
grid = Table.grid(padding=(0, 2), expand=True)
|
|
440
|
+
grid.add_column(style="bold dim", justify="right", no_wrap=True)
|
|
441
|
+
grid.add_column(overflow="fold")
|
|
442
|
+
|
|
443
|
+
for label, attr in (
|
|
444
|
+
("Trigger", "trigger"),
|
|
445
|
+
("Rationale", "rationale"),
|
|
446
|
+
):
|
|
447
|
+
value = getattr(pb, attr, None)
|
|
448
|
+
if value:
|
|
449
|
+
grid.add_row(label, value)
|
|
450
|
+
|
|
451
|
+
# Title: [N] + approval badge + lifecycle tag
|
|
452
|
+
title = Text.assemble((f"[{i}]", "bold cyan"))
|
|
453
|
+
ps = getattr(pb, "playbook_status", None)
|
|
454
|
+
if ps is not None:
|
|
455
|
+
ps_str = (ps if isinstance(ps, str) else str(ps)).upper()
|
|
456
|
+
if ps_str:
|
|
457
|
+
style = badge_styles.get(ps_str, "bold white")
|
|
458
|
+
title.append(f" [{ps_str}]", style=style)
|
|
459
|
+
lifecycle = _lifecycle_tag(pb).strip()
|
|
460
|
+
if lifecycle:
|
|
461
|
+
title.append(f" {lifecycle}", style="yellow")
|
|
462
|
+
|
|
463
|
+
# Footer: playbook_name · agent_version · relative created_at
|
|
464
|
+
footer_parts: list[str] = []
|
|
465
|
+
playbook_name = getattr(pb, "playbook_name", None)
|
|
466
|
+
if playbook_name:
|
|
467
|
+
footer_parts.append(str(playbook_name))
|
|
468
|
+
agent_version = getattr(pb, "agent_version", None)
|
|
469
|
+
if agent_version:
|
|
470
|
+
footer_parts.append(f"v{agent_version}")
|
|
471
|
+
created_at = getattr(pb, "created_at", None)
|
|
472
|
+
if created_at:
|
|
473
|
+
footer_parts.append(_human_relative_timestamp(int(created_at)))
|
|
474
|
+
|
|
475
|
+
content_text = Text(
|
|
476
|
+
getattr(pb, "content", "") or "", style="bold", overflow="fold"
|
|
477
|
+
)
|
|
478
|
+
body_parts: list[Any] = [content_text, Text(""), grid]
|
|
479
|
+
if footer_parts:
|
|
480
|
+
body_parts.extend([Text(""), Text(" · ".join(footer_parts), style="dim")])
|
|
481
|
+
|
|
482
|
+
panel = Panel(
|
|
483
|
+
Group(*body_parts),
|
|
484
|
+
title=title,
|
|
485
|
+
title_align="left",
|
|
486
|
+
border_style="magenta",
|
|
487
|
+
box=box.ROUNDED,
|
|
488
|
+
padding=(1, 2),
|
|
489
|
+
)
|
|
490
|
+
console.print(panel)
|
|
491
|
+
console.print()
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def print_interactions(interactions: list[Any]) -> None:
|
|
495
|
+
"""Render interactions grouped by request_id as rich panels.
|
|
496
|
+
|
|
497
|
+
Each request_id becomes one bordered panel. The title shows the
|
|
498
|
+
short request id, relative timestamp, and turn count. The body is
|
|
499
|
+
a borderless two-column grid with role labels in the left column
|
|
500
|
+
(User cyan, Assistant magenta, System dim, others white) and
|
|
501
|
+
folded content on the right. When stdout is piped, rich
|
|
502
|
+
auto-detects and degrades to plain text.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
interactions: InteractionView-like objects with
|
|
506
|
+
``request_id``, ``role``, ``content``, ``created_at``.
|
|
507
|
+
"""
|
|
508
|
+
if not interactions:
|
|
509
|
+
return
|
|
510
|
+
from collections import defaultdict
|
|
511
|
+
|
|
512
|
+
from rich import box
|
|
513
|
+
from rich.console import Console
|
|
514
|
+
from rich.panel import Panel
|
|
515
|
+
from rich.table import Table
|
|
516
|
+
from rich.text import Text
|
|
517
|
+
|
|
518
|
+
role_styles: dict[str, str] = {
|
|
519
|
+
"user": "bold cyan",
|
|
520
|
+
"assistant": "bold magenta",
|
|
521
|
+
"system": "bold dim",
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
groups: dict[str, list[Any]] = defaultdict(list)
|
|
525
|
+
group_mins: dict[str, int] = {}
|
|
526
|
+
for ix in interactions:
|
|
527
|
+
groups[ix.request_id].append(ix)
|
|
528
|
+
existing = group_mins.get(ix.request_id)
|
|
529
|
+
if existing is None or ix.created_at < existing:
|
|
530
|
+
group_mins[ix.request_id] = ix.created_at
|
|
531
|
+
|
|
532
|
+
# Stable order: earliest group first (O(N) pre-pass above + O(G log G) sort)
|
|
533
|
+
sorted_groups = sorted(groups.items(), key=lambda g: group_mins[g[0]])
|
|
534
|
+
|
|
535
|
+
console = Console()
|
|
536
|
+
for request_id, items in sorted_groups:
|
|
537
|
+
ts = group_mins[request_id]
|
|
538
|
+
try:
|
|
539
|
+
relative = _human_relative_timestamp(int(ts))
|
|
540
|
+
except (TypeError, ValueError):
|
|
541
|
+
relative = "unknown"
|
|
542
|
+
short_id = (request_id or "unknown")[:8]
|
|
543
|
+
turn_count = len(items)
|
|
544
|
+
turn_label = "turn" if turn_count == 1 else "turns"
|
|
545
|
+
title = Text.assemble(
|
|
546
|
+
("req: ", "dim"),
|
|
547
|
+
(short_id, "bold cyan"),
|
|
548
|
+
(" · ", "dim"),
|
|
549
|
+
(relative, "dim"),
|
|
550
|
+
(" · ", "dim"),
|
|
551
|
+
(f"{turn_count} {turn_label}", "dim"),
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
grid = Table.grid(padding=(0, 2), expand=True)
|
|
555
|
+
grid.add_column(justify="right", no_wrap=True)
|
|
556
|
+
grid.add_column(overflow="fold")
|
|
557
|
+
|
|
558
|
+
for item in sorted(items, key=lambda i: i.created_at):
|
|
559
|
+
role = (item.role or "").strip() or "unknown"
|
|
560
|
+
style = role_styles.get(role.lower(), "bold white")
|
|
561
|
+
grid.add_row(Text(role.capitalize(), style=style), item.content or "")
|
|
562
|
+
|
|
563
|
+
panel = Panel(
|
|
564
|
+
grid,
|
|
565
|
+
title=title,
|
|
566
|
+
title_align="left",
|
|
567
|
+
border_style="blue",
|
|
568
|
+
box=box.ROUNDED,
|
|
569
|
+
padding=(1, 2),
|
|
570
|
+
)
|
|
571
|
+
console.print(panel)
|
|
572
|
+
console.print()
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def print_storage_credentials(
|
|
576
|
+
storage_type: str | None,
|
|
577
|
+
storage_config: dict[str, Any],
|
|
578
|
+
*,
|
|
579
|
+
revealed: bool,
|
|
580
|
+
) -> None:
|
|
581
|
+
"""Render the output of ``reflexio config storage`` as a rich table.
|
|
582
|
+
|
|
583
|
+
Prints a bold "Storage type: ..." header followed by a two-column
|
|
584
|
+
grid (field name / value). When the credentials are masked, a
|
|
585
|
+
dim-italic hint reminds the user that ``--reveal`` exists.
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
storage_type: The server's storage_type label (e.g.,
|
|
589
|
+
``"sqlite"``, ``"supabase"``).
|
|
590
|
+
storage_config: The serialized StorageConfig dict (already
|
|
591
|
+
masked or revealed by the caller).
|
|
592
|
+
revealed: Whether ``storage_config`` contains raw credentials.
|
|
593
|
+
When False, a reveal-hint footer is printed.
|
|
594
|
+
"""
|
|
595
|
+
from rich.console import Console
|
|
596
|
+
from rich.table import Table
|
|
597
|
+
from rich.text import Text
|
|
598
|
+
|
|
599
|
+
console = Console()
|
|
600
|
+
console.print(
|
|
601
|
+
Text.assemble(
|
|
602
|
+
("Storage type: ", "bold dim"),
|
|
603
|
+
(storage_type or "unknown", "bold"),
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
grid = Table.grid(padding=(0, 2))
|
|
608
|
+
grid.add_column(style="bold dim", justify="right", no_wrap=True)
|
|
609
|
+
grid.add_column(overflow="fold")
|
|
610
|
+
for key, value in storage_config.items():
|
|
611
|
+
grid.add_row(str(key), "<none>" if value is None else str(value))
|
|
612
|
+
console.print(grid)
|
|
613
|
+
|
|
614
|
+
if not revealed:
|
|
615
|
+
console.print(
|
|
616
|
+
Text(
|
|
617
|
+
"(use --reveal to print raw credential values)",
|
|
618
|
+
style="dim italic",
|
|
619
|
+
)
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def print_whoami_summary(
|
|
624
|
+
*,
|
|
625
|
+
endpoint: str,
|
|
626
|
+
api_key: str,
|
|
627
|
+
org_id: str | None,
|
|
628
|
+
storage_type: str | None,
|
|
629
|
+
storage_label: str | None,
|
|
630
|
+
storage_configured: bool,
|
|
631
|
+
message: str | None,
|
|
632
|
+
) -> None:
|
|
633
|
+
"""Render the ``reflexio status whoami`` summary as a rich table.
|
|
634
|
+
|
|
635
|
+
Emits a two-column grid (right-aligned bold-dim label column +
|
|
636
|
+
value column) covering endpoint, masked API key, org id, and
|
|
637
|
+
storage info. The ``[configured]`` / ``[unconfigured]`` marker is
|
|
638
|
+
colour-coded green/yellow. A trailing dim-italic line renders any
|
|
639
|
+
message the server attached to the whoami payload.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
endpoint: Fully-qualified server URL (``client.base_url``).
|
|
643
|
+
api_key: The raw resolved API key — masked internally via
|
|
644
|
+
:func:`mask_api_key`.
|
|
645
|
+
org_id: Organization id from the whoami response, or None.
|
|
646
|
+
storage_type: Storage backend label from the whoami response.
|
|
647
|
+
storage_label: Human-friendly storage name (e.g., a DSN with
|
|
648
|
+
masked credentials), or None.
|
|
649
|
+
storage_configured: Whether the server reports configured
|
|
650
|
+
storage for this org.
|
|
651
|
+
message: Optional advisory message from the whoami response.
|
|
652
|
+
"""
|
|
653
|
+
from rich.console import Console
|
|
654
|
+
from rich.table import Table
|
|
655
|
+
from rich.text import Text
|
|
656
|
+
|
|
657
|
+
console = Console()
|
|
658
|
+
grid = Table.grid(padding=(0, 2))
|
|
659
|
+
grid.add_column(style="bold dim", justify="right", no_wrap=True)
|
|
660
|
+
grid.add_column(overflow="fold")
|
|
661
|
+
|
|
662
|
+
grid.add_row("Endpoint", endpoint)
|
|
663
|
+
grid.add_row("API key", mask_api_key(api_key))
|
|
664
|
+
grid.add_row("Org ID", org_id or "<none>")
|
|
665
|
+
if storage_type:
|
|
666
|
+
grid.add_row("Storage type", storage_type)
|
|
667
|
+
|
|
668
|
+
if storage_label:
|
|
669
|
+
marker_text = "[configured]" if storage_configured else "[unconfigured]"
|
|
670
|
+
marker_style = "green" if storage_configured else "yellow"
|
|
671
|
+
storage_value = Text.assemble(
|
|
672
|
+
(storage_label, ""),
|
|
673
|
+
(" ", ""),
|
|
674
|
+
(marker_text, marker_style),
|
|
675
|
+
)
|
|
676
|
+
grid.add_row("Storage", storage_value)
|
|
677
|
+
elif not storage_configured:
|
|
678
|
+
grid.add_row("Storage", Text("<not configured>", style="yellow"))
|
|
679
|
+
|
|
680
|
+
console.print(grid)
|
|
681
|
+
|
|
682
|
+
if message:
|
|
683
|
+
console.print(Text(message, style="dim italic"))
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def print_doctor_checks(checks: list[dict[str, Any]]) -> None:
|
|
687
|
+
"""Render ``reflexio doctor check`` results as a rich grid.
|
|
688
|
+
|
|
689
|
+
Each check row becomes ``<icon> <name>: <message>`` with the icon
|
|
690
|
+
and name colour-coded by status — green ``✓`` for pass, yellow
|
|
691
|
+
``⚠`` for warn, red ``✗`` for fail. If a check has a hint, it is
|
|
692
|
+
rendered on an indented second line in dim style.
|
|
693
|
+
|
|
694
|
+
Args:
|
|
695
|
+
checks: Doctor check dicts with keys ``status`` (``"pass"``,
|
|
696
|
+
``"warn"``, or ``"fail"``), ``name``, ``message``, and
|
|
697
|
+
``hint`` (possibly None).
|
|
698
|
+
"""
|
|
699
|
+
if not checks:
|
|
700
|
+
return
|
|
701
|
+
from rich.console import Console
|
|
702
|
+
from rich.table import Table
|
|
703
|
+
from rich.text import Text
|
|
704
|
+
|
|
705
|
+
icon_styles: dict[str, tuple[str, str]] = {
|
|
706
|
+
"pass": ("✓", "bold green"),
|
|
707
|
+
"warn": ("⚠", "bold yellow"),
|
|
708
|
+
"fail": ("✗", "bold red"),
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
console = Console()
|
|
712
|
+
grid = Table.grid(padding=(0, 1), expand=True)
|
|
713
|
+
grid.add_column(no_wrap=True) # icon
|
|
714
|
+
grid.add_column(no_wrap=True) # name
|
|
715
|
+
grid.add_column(overflow="fold") # message + hint
|
|
716
|
+
|
|
717
|
+
for check in checks:
|
|
718
|
+
status = check.get("status", "warn")
|
|
719
|
+
icon, style = icon_styles.get(status, ("?", "bold white"))
|
|
720
|
+
name = check.get("name", "") or ""
|
|
721
|
+
message = check.get("message", "") or ""
|
|
722
|
+
hint = check.get("hint")
|
|
723
|
+
|
|
724
|
+
body = Text(message)
|
|
725
|
+
if hint:
|
|
726
|
+
body.append("\n")
|
|
727
|
+
body.append(Text(f" Hint: {hint}", style="dim"))
|
|
728
|
+
|
|
729
|
+
grid.add_row(
|
|
730
|
+
Text(icon, style=style),
|
|
731
|
+
Text(f"{name}:", style="bold"),
|
|
732
|
+
body,
|
|
733
|
+
)
|
|
734
|
+
console.print(grid)
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
def print_auth_status(
|
|
738
|
+
url: str,
|
|
739
|
+
api_key: str,
|
|
740
|
+
env_path: str,
|
|
741
|
+
*,
|
|
742
|
+
env_exists: bool,
|
|
743
|
+
) -> None:
|
|
744
|
+
"""Render ``reflexio auth status`` as a two-column rich grid.
|
|
745
|
+
|
|
746
|
+
Three rows: URL, masked API Key (via :func:`mask_api_key`), and
|
|
747
|
+
env file path. When ``env_exists`` is False, the env file row is
|
|
748
|
+
annotated so the user understands displayed values came from the
|
|
749
|
+
shell environment or defaults rather than the persisted file.
|
|
750
|
+
|
|
751
|
+
Args:
|
|
752
|
+
url: The configured REFLEXIO_URL value, possibly empty.
|
|
753
|
+
api_key: The raw REFLEXIO_API_KEY value, possibly empty.
|
|
754
|
+
env_path: Absolute path to the env file.
|
|
755
|
+
env_exists: Whether the env file currently exists on disk.
|
|
756
|
+
"""
|
|
757
|
+
from rich.console import Console
|
|
758
|
+
from rich.table import Table
|
|
759
|
+
|
|
760
|
+
console = Console()
|
|
761
|
+
grid = Table.grid(padding=(0, 2))
|
|
762
|
+
grid.add_column(style="bold dim", justify="right", no_wrap=True)
|
|
763
|
+
grid.add_column(overflow="fold")
|
|
764
|
+
grid.add_row("URL", url or "<unset>")
|
|
765
|
+
grid.add_row("API Key", mask_api_key(api_key))
|
|
766
|
+
env_suffix = "" if env_exists else " (not found — using shell env or defaults)"
|
|
767
|
+
grid.add_row("Env file", f"{env_path}{env_suffix}")
|
|
768
|
+
console.print(grid)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def format_interactions(interactions: list[Any]) -> str:
|
|
772
|
+
"""Format interaction objects grouped by request_id as conversation turns.
|
|
773
|
+
|
|
774
|
+
Args:
|
|
775
|
+
interactions: InteractionView objects with request_id, role, content, created_at
|
|
776
|
+
|
|
777
|
+
Returns:
|
|
778
|
+
str: Formatted conversation turns
|
|
779
|
+
"""
|
|
780
|
+
if not interactions:
|
|
781
|
+
return ""
|
|
782
|
+
|
|
783
|
+
from collections import defaultdict
|
|
784
|
+
from datetime import datetime
|
|
785
|
+
|
|
786
|
+
groups: dict[str, list[Any]] = defaultdict(list)
|
|
787
|
+
for ix in interactions:
|
|
788
|
+
groups[ix.request_id].append(ix)
|
|
789
|
+
|
|
790
|
+
# Sort groups by earliest created_at
|
|
791
|
+
sorted_groups = sorted(
|
|
792
|
+
groups.items(), key=lambda g: min(i.created_at for i in g[1])
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
blocks: list[str] = []
|
|
796
|
+
for request_id, items in sorted_groups:
|
|
797
|
+
ts = min(i.created_at for i in items)
|
|
798
|
+
try:
|
|
799
|
+
dt = datetime.fromtimestamp(ts, tz=UTC).strftime("%Y-%m-%d %H:%M")
|
|
800
|
+
except (TypeError, ValueError, OSError):
|
|
801
|
+
dt = "unknown"
|
|
802
|
+
short_id = request_id[:8]
|
|
803
|
+
header = f"── {short_id} ({dt} UTC) ──"
|
|
804
|
+
lines = [header]
|
|
805
|
+
for item in sorted(items, key=lambda i: i.created_at):
|
|
806
|
+
role = item.role.capitalize()
|
|
807
|
+
lines.append(f" {role}: {item.content}")
|
|
808
|
+
blocks.append("\n".join(lines))
|
|
809
|
+
|
|
810
|
+
return "\n\n".join(blocks)
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
def format_context(
|
|
814
|
+
profiles: list[Any],
|
|
815
|
+
agent_playbooks: list[Any],
|
|
816
|
+
user_playbooks: list[Any] | None = None,
|
|
817
|
+
) -> str:
|
|
818
|
+
"""Build a combined context block from profiles, agent playbooks, and user playbooks.
|
|
819
|
+
|
|
820
|
+
Args:
|
|
821
|
+
profiles: Profile objects
|
|
822
|
+
agent_playbooks: Agent playbook objects (org-level behavioral corrections)
|
|
823
|
+
user_playbooks: User playbook objects (user-specific behavioral corrections).
|
|
824
|
+
When provided, rendered after agent playbooks in their own section.
|
|
825
|
+
|
|
826
|
+
Returns:
|
|
827
|
+
str: Combined markdown context block, or empty string if all empty
|
|
828
|
+
"""
|
|
829
|
+
sections: list[str] = []
|
|
830
|
+
|
|
831
|
+
if profile_text := format_profiles(profiles):
|
|
832
|
+
sections.append(f"## User Preferences — APPLY THESE\n{profile_text}")
|
|
833
|
+
|
|
834
|
+
if playbook_text := format_agent_playbooks(agent_playbooks):
|
|
835
|
+
sections.append(f"## Behavior Corrections — FOLLOW THESE\n{playbook_text}")
|
|
836
|
+
|
|
837
|
+
if user_playbooks and (user_pb_text := format_user_playbooks(user_playbooks)):
|
|
838
|
+
sections.append(f"## User Behavior Corrections — FOLLOW THESE\n{user_pb_text}")
|
|
839
|
+
|
|
840
|
+
if not sections:
|
|
841
|
+
return ""
|
|
842
|
+
|
|
843
|
+
return (
|
|
844
|
+
"---\n# IMPORTANT: Apply These Corrections (from Reflexio)\n\n"
|
|
845
|
+
+ "\n\n".join(sections)
|
|
846
|
+
+ "\n\nFailure to follow these means repeating mistakes the user already corrected.\n---"
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
def pagination_meta(
|
|
851
|
+
items: list[Any],
|
|
852
|
+
limit: int,
|
|
853
|
+
) -> dict[str, Any]:
|
|
854
|
+
"""Build pagination metadata for output envelope.
|
|
855
|
+
|
|
856
|
+
Args:
|
|
857
|
+
items: The items returned
|
|
858
|
+
limit: The requested limit
|
|
859
|
+
|
|
860
|
+
Returns:
|
|
861
|
+
dict: Metadata with count, limit, and has_more flag
|
|
862
|
+
"""
|
|
863
|
+
return {
|
|
864
|
+
"count": len(items),
|
|
865
|
+
"limit": limit,
|
|
866
|
+
"has_more": len(items) == limit,
|
|
867
|
+
}
|