algo-cli-runtime 0.14.0__py3-none-any.whl

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.
Files changed (237) hide show
  1. algo_cli/__init__.py +3 -0
  2. algo_cli/__main__.py +7 -0
  3. algo_cli/_internal/__init__.py +12 -0
  4. algo_cli/_internal/policy_chain.py +259 -0
  5. algo_cli/action_registry.py +1047 -0
  6. algo_cli/agent_blocks.py +550 -0
  7. algo_cli/agent_pipeline.py +1457 -0
  8. algo_cli/agent_threads.py +308 -0
  9. algo_cli/animations.py +316 -0
  10. algo_cli/cache_admission.py +209 -0
  11. algo_cli/capability_mask.py +66 -0
  12. algo_cli/chat_protocol.py +116 -0
  13. algo_cli/chatgpt_auth.py +510 -0
  14. algo_cli/chatgpt_client.py +657 -0
  15. algo_cli/code_rag.py +479 -0
  16. algo_cli/config.py +651 -0
  17. algo_cli/context_budget.py +679 -0
  18. algo_cli/credential_helpers.py +315 -0
  19. algo_cli/deliberation.py +29 -0
  20. algo_cli/display.py +1470 -0
  21. algo_cli/evals/__init__.py +21 -0
  22. algo_cli/evals/algorithm_effectiveness.py +560 -0
  23. algo_cli/evals/competitive_harness_rating.py +702 -0
  24. algo_cli/evals/cot_quality.py +220 -0
  25. algo_cli/evals/harness_retrieval_benchmark.py +401 -0
  26. algo_cli/evals/performance_regression.py +136 -0
  27. algo_cli/evals/scorecard_grading.py +308 -0
  28. algo_cli/evals/session_distribution.py +84 -0
  29. algo_cli/execution_guardrails.py +806 -0
  30. algo_cli/extensions_manifest.py +84 -0
  31. algo_cli/git_evidence.py +227 -0
  32. algo_cli/google_workspace.py +407 -0
  33. algo_cli/google_workspace_auth.py +523 -0
  34. algo_cli/harness.py +2587 -0
  35. algo_cli/identity.py +557 -0
  36. algo_cli/index_compute_lab.py +228 -0
  37. algo_cli/inference_harness.py +70 -0
  38. algo_cli/intelligence/__init__.py +1103 -0
  39. algo_cli/intelligence/acrobat_config.py +307 -0
  40. algo_cli/intelligence/acrobat_manifests.py +338 -0
  41. algo_cli/intelligence/acrobat_models.py +195 -0
  42. algo_cli/intelligence/acrobat_pipeline.py +295 -0
  43. algo_cli/intelligence/acrobat_runtime.py +302 -0
  44. algo_cli/intelligence/acrobat_security.py +261 -0
  45. algo_cli/intelligence/acrobat_workflows.py +226 -0
  46. algo_cli/intelligence/actionability.py +165 -0
  47. algo_cli/intelligence/adversarial_audit.py +136 -0
  48. algo_cli/intelligence/agent_arena.py +92 -0
  49. algo_cli/intelligence/agent_benchmark.py +236 -0
  50. algo_cli/intelligence/agent_runtime.py +171 -0
  51. algo_cli/intelligence/agents_as_tools.py +70 -0
  52. algo_cli/intelligence/artifact_binding.py +80 -0
  53. algo_cli/intelligence/autonomous_engineer.py +1976 -0
  54. algo_cli/intelligence/backpressure.py +99 -0
  55. algo_cli/intelligence/bloom_filter.py +186 -0
  56. algo_cli/intelligence/bonferroni.py +66 -0
  57. algo_cli/intelligence/boundary_compaction.py +98 -0
  58. algo_cli/intelligence/catalog_verifier.py +172 -0
  59. algo_cli/intelligence/cavecrew.py +118 -0
  60. algo_cli/intelligence/changelog.py +176 -0
  61. algo_cli/intelligence/checkpoint_resume.py +92 -0
  62. algo_cli/intelligence/circuit_breaker.py +88 -0
  63. algo_cli/intelligence/clarification_gate.py +101 -0
  64. algo_cli/intelligence/code_graph.py +180 -0
  65. algo_cli/intelligence/coderank.py +97 -0
  66. algo_cli/intelligence/consistent_hash.py +150 -0
  67. algo_cli/intelligence/consortium_synthesis.py +139 -0
  68. algo_cli/intelligence/construction/__init__.py +241 -0
  69. algo_cli/intelligence/construction/common.py +273 -0
  70. algo_cli/intelligence/construction/documents.py +496 -0
  71. algo_cli/intelligence/construction/labor_units.py +1395 -0
  72. algo_cli/intelligence/construction/payments.py +470 -0
  73. algo_cli/intelligence/construction/risk.py +784 -0
  74. algo_cli/intelligence/content_extractor.py +132 -0
  75. algo_cli/intelligence/context_adaptive.py +102 -0
  76. algo_cli/intelligence/context_ops.py +95 -0
  77. algo_cli/intelligence/count_min.py +145 -0
  78. algo_cli/intelligence/cow_state.py +103 -0
  79. algo_cli/intelligence/critic_loop.py +119 -0
  80. algo_cli/intelligence/cross_source.py +113 -0
  81. algo_cli/intelligence/daemon_mode.py +99 -0
  82. algo_cli/intelligence/dag_orchestration.py +151 -0
  83. algo_cli/intelligence/deep_research.py +155 -0
  84. algo_cli/intelligence/degenerate_detector.py +78 -0
  85. algo_cli/intelligence/delta_report.py +92 -0
  86. algo_cli/intelligence/discovery_event_log.py +92 -0
  87. algo_cli/intelligence/document_ingest.py +298 -0
  88. algo_cli/intelligence/dual_layer_validate.py +151 -0
  89. algo_cli/intelligence/echo_fidelity.py +73 -0
  90. algo_cli/intelligence/ema_tuning.py +104 -0
  91. algo_cli/intelligence/event_log.py +92 -0
  92. algo_cli/intelligence/evidence_graph.py +114 -0
  93. algo_cli/intelligence/extension_host.py +162 -0
  94. algo_cli/intelligence/extension_manifest.py +115 -0
  95. algo_cli/intelligence/falsification_suite.py +178 -0
  96. algo_cli/intelligence/finance/__init__.py +169 -0
  97. algo_cli/intelligence/finance/anomalies.py +135 -0
  98. algo_cli/intelligence/finance/ap_ar.py +351 -0
  99. algo_cli/intelligence/finance/cash.py +162 -0
  100. algo_cli/intelligence/finance/close.py +332 -0
  101. algo_cli/intelligence/finance/common.py +244 -0
  102. algo_cli/intelligence/finance/construction.py +135 -0
  103. algo_cli/intelligence/finance/controls.py +172 -0
  104. algo_cli/intelligence/finance/evidence.py +119 -0
  105. algo_cli/intelligence/finance/exceptions.py +157 -0
  106. algo_cli/intelligence/finance/reconciliations.py +254 -0
  107. algo_cli/intelligence/finance/revenue.py +109 -0
  108. algo_cli/intelligence/finance/tax.py +74 -0
  109. algo_cli/intelligence/finance/workpapers.py +111 -0
  110. algo_cli/intelligence/finding_record.py +120 -0
  111. algo_cli/intelligence/flow_dag.py +267 -0
  112. algo_cli/intelligence/gatherer.py +223 -0
  113. algo_cli/intelligence/golden_master.py +98 -0
  114. algo_cli/intelligence/graph_rag.py +195 -0
  115. algo_cli/intelligence/group_chat.py +143 -0
  116. algo_cli/intelligence/hash_dedup.py +145 -0
  117. algo_cli/intelligence/hyperloglog.py +128 -0
  118. algo_cli/intelligence/incremental_index.py +316 -0
  119. algo_cli/intelligence/index_store.py +16 -0
  120. algo_cli/intelligence/iteration_plan.py +133 -0
  121. algo_cli/intelligence/kernel_plugins.py +167 -0
  122. algo_cli/intelligence/lesson_catalog.py +135 -0
  123. algo_cli/intelligence/llm_fallback.py +169 -0
  124. algo_cli/intelligence/log2_histogram.py +267 -0
  125. algo_cli/intelligence/lsp_integration.py +147 -0
  126. algo_cli/intelligence/memory_evolution.py +117 -0
  127. algo_cli/intelligence/minhash_lsh.py +182 -0
  128. algo_cli/intelligence/multi_model_score.py +174 -0
  129. algo_cli/intelligence/multi_tier_grade.py +211 -0
  130. algo_cli/intelligence/negative_controls.py +113 -0
  131. algo_cli/intelligence/numeric_clamp.py +63 -0
  132. algo_cli/intelligence/occ_editor.py +66 -0
  133. algo_cli/intelligence/output_normalize.py +112 -0
  134. algo_cli/intelligence/parallel_delegation.py +98 -0
  135. algo_cli/intelligence/parallel_fanout.py +104 -0
  136. algo_cli/intelligence/permission_modes.py +105 -0
  137. algo_cli/intelligence/pre_push_gate.py +68 -0
  138. algo_cli/intelligence/prefetch.py +171 -0
  139. algo_cli/intelligence/process_framework.py +217 -0
  140. algo_cli/intelligence/project_graph.py +387 -0
  141. algo_cli/intelligence/query_expansion.py +146 -0
  142. algo_cli/intelligence/ralph_loop.py +117 -0
  143. algo_cli/intelligence/rate_limiter.py +153 -0
  144. algo_cli/intelligence/refactor_transaction.py +94 -0
  145. algo_cli/intelligence/research_workspace.py +108 -0
  146. algo_cli/intelligence/retraction_ledger.py +72 -0
  147. algo_cli/intelligence/saga_pattern.py +88 -0
  148. algo_cli/intelligence/session_fork.py +100 -0
  149. algo_cli/intelligence/shadow_editor.py +67 -0
  150. algo_cli/intelligence/shell_session.py +213 -0
  151. algo_cli/intelligence/source_registry.py +143 -0
  152. algo_cli/intelligence/spawn_scales.py +99 -0
  153. algo_cli/intelligence/stat_stability.py +104 -0
  154. algo_cli/intelligence/structural_validator.py +148 -0
  155. algo_cli/intelligence/subagent_spawner.py +111 -0
  156. algo_cli/intelligence/symmetric_verify.py +70 -0
  157. algo_cli/intelligence/task_classifier.py +129 -0
  158. algo_cli/intelligence/team_execution.py +122 -0
  159. algo_cli/intelligence/tiered_access.py +121 -0
  160. algo_cli/intelligence/utility_registry.py +159 -0
  161. algo_cli/intuition_engine.py +560 -0
  162. algo_cli/intuition_injector.py +82 -0
  163. algo_cli/kernels/__init__.py +5 -0
  164. algo_cli/kernels/manifest.py +763 -0
  165. algo_cli/main.py +3903 -0
  166. algo_cli/memory_candidates.py +541 -0
  167. algo_cli/memory_echo_veil.py +394 -0
  168. algo_cli/memory_runtime.py +112 -0
  169. algo_cli/model_info.py +548 -0
  170. algo_cli/model_profile.py +160 -0
  171. algo_cli/model_routing.py +74 -0
  172. algo_cli/oneshot.py +331 -0
  173. algo_cli/perf_telemetry.py +389 -0
  174. algo_cli/plugins.py +245 -0
  175. algo_cli/private_event_store.py +654 -0
  176. algo_cli/quantization/__init__.py +24 -0
  177. algo_cli/quantization/lloyd_max.py +98 -0
  178. algo_cli/quantization/turbo_quant.py +308 -0
  179. algo_cli/reasoning/__init__.py +46 -0
  180. algo_cli/reasoning/combinatorial.py +356 -0
  181. algo_cli/reasoning/graph_of_thought.py +297 -0
  182. algo_cli/reasoning/mcts.py +220 -0
  183. algo_cli/reasoning/neuro_symbolic.py +250 -0
  184. algo_cli/reasoning/react.py +246 -0
  185. algo_cli/reasoning/reflexion.py +225 -0
  186. algo_cli/reasoning/tree_of_thought.py +241 -0
  187. algo_cli/reasoning_bridge.py +150 -0
  188. algo_cli/reconciliation.py +284 -0
  189. algo_cli/reflex.py +385 -0
  190. algo_cli/resources/docs/ALGO.md +13958 -0
  191. algo_cli/resources/docs/algo-cli-algorithm-evidence-contract.md +60 -0
  192. algo_cli/resources/docs/algo-cli-execution-verification-contract.md +59 -0
  193. algo_cli/resources/docs/algo-cli-memory-lifecycle-contract.md +72 -0
  194. algo_cli/resources/docs/harness-extension-cleanup-recommendation.md +41 -0
  195. algo_cli/resources/docs/index-compute-lab-integration.md +32 -0
  196. algo_cli/resources/docs/inference-harness-loop-blueprint-2026-06.md +55 -0
  197. algo_cli/resources/docs/main-split-map.md +35 -0
  198. algo_cli/resources/docs/privacy-and-context.md +48 -0
  199. algo_cli/resources/docs/reflex-loop-v0.2.md +354 -0
  200. algo_cli/resources/skills/README.md +26 -0
  201. algo_cli/resources/skills/algo-cli.md +59 -0
  202. algo_cli/resources/skills/edit-file-precision.md +49 -0
  203. algo_cli/resources/skills/harness-search-first.md +47 -0
  204. algo_cli/resources/skills/memory-recall-ritual.md +51 -0
  205. algo_cli/resources/skills/qol-algorithms.md +224 -0
  206. algo_cli/resources/skills/smart-error-recovery.md +56 -0
  207. algo_cli/resources/skills/tool-selection-cheatsheet.md +65 -0
  208. algo_cli/retrieval_algorithms.py +127 -0
  209. algo_cli/runtime_qos.py +236 -0
  210. algo_cli/runtime_services.py +320 -0
  211. algo_cli/session_commands.py +95 -0
  212. algo_cli/session_mode.py +113 -0
  213. algo_cli/skills.py +430 -0
  214. algo_cli/slash_dispatch.py +1265 -0
  215. algo_cli/small_context.py +206 -0
  216. algo_cli/spawn_budget.py +89 -0
  217. algo_cli/task_ledger.py +84 -0
  218. algo_cli/task_router.py +197 -0
  219. algo_cli/tool_context.py +94 -0
  220. algo_cli/tool_contract.py +99 -0
  221. algo_cli/tool_policy.py +357 -0
  222. algo_cli/tool_runtime.py +647 -0
  223. algo_cli/tools.py +3056 -0
  224. algo_cli/url_scheme.py +174 -0
  225. algo_cli/verify.py +154 -0
  226. algo_cli/version_manifest.py +178 -0
  227. algo_cli/vision_screenshot_verify.py +76 -0
  228. algo_cli/workspace_resolver.py +68 -0
  229. algo_cli/x_account.py +209 -0
  230. algo_cli/xai_auth.py +374 -0
  231. algo_cli/xai_client.py +600 -0
  232. algo_cli_runtime-0.14.0.dist-info/METADATA +369 -0
  233. algo_cli_runtime-0.14.0.dist-info/RECORD +237 -0
  234. algo_cli_runtime-0.14.0.dist-info/WHEEL +4 -0
  235. algo_cli_runtime-0.14.0.dist-info/entry_points.txt +3 -0
  236. algo_cli_runtime-0.14.0.dist-info/licenses/LICENSE +21 -0
  237. ollama_cli/__init__.py +67 -0
@@ -0,0 +1,1265 @@
1
+ """Slash-command table, completion, and REPL dispatch."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from difflib import get_close_matches
8
+ from pathlib import Path
9
+ from typing import Any
10
+ from dataclasses import fields
11
+
12
+ from ollama import Client
13
+ from prompt_toolkit.completion import Completer, Completion
14
+
15
+ from . import display
16
+ from .config import (
17
+ CODE_RAG_CONSENT_VERSION,
18
+ Config,
19
+ code_rag_consent_granted,
20
+ safe_conversation_name,
21
+ )
22
+ from . import code_rag
23
+ from . import harness
24
+ from . import identity
25
+ from . import memory_candidates
26
+ from . import perf_telemetry
27
+ from . import reflex
28
+ from . import skills
29
+
30
+
31
+ SLASH_COMMANDS: list[tuple[str, str]] = [
32
+ ("/help", "Show help"),
33
+ ("/model", "Show or switch model"),
34
+ ("/models", "Browse available local models"),
35
+ ("/host", "Set local Ollama host"),
36
+ ("/cloud", "Set/toggle direct Ollama Cloud API mode: /cloud [on|off|status]"),
37
+ ("/login", "Run Ollama signin"),
38
+ ("/cloudauto", "Set/toggle cloud auto-connect: /cloudauto [on|off|status]"),
39
+ ("/xai-login", "Optional xAI OAuth; requires your XAI_CLIENT_ID. Flags: --no-browser, --manual"),
40
+ ("/xai-logout", "Revoke local xAI tokens"),
41
+ ("/xai-status", "Show optional xAI OAuth setup and auth state"),
42
+ ("/xai-test", "List xAI models the current token can access"),
43
+ ("/google-login", "Authenticate with Google Workspace (OAuth2 loopback). Flags: --no-browser, --manual"),
44
+ ("/google-callback", "Complete Google Workspace OAuth from callback URL. Flags: --clipboard, --file PATH"),
45
+ ("/google-logout", "Revoke local Google Workspace tokens"),
46
+ ("/google-status", "Show Google Workspace auth state"),
47
+ ("/google", "Run a Google Workspace command: drive-list, drive-search, drive-get, docs-get, sheets-values, calendar-list, gmail-list, gmail-get, help"),
48
+ ("/chatgpt-login", "Authenticate with ChatGPT/Codex OAuth. Flags: --no-browser, --manual, --device-code"),
49
+ ("/chatgpt-logout", "Revoke local ChatGPT tokens"),
50
+ ("/chatgpt-status", "Show ChatGPT auth state"),
51
+ ("/model-check", "Report Algo CLI support for a Grok/xAI model name"),
52
+ ("/x-account", "Manage X account via xurl: status, draft-post, draft-reply, post --confirm, reply --confirm"),
53
+ ("/system", "Show or set system prompt"),
54
+ ("/auto", "Set/toggle auto-approve: /auto [on|off|status]"),
55
+ ("/safe", "Set/toggle safe mode: /safe [on|off|status]"),
56
+ ("/policy", "Toggle or inspect algorithmic tool policy enforcement"),
57
+ ("/reflex", "Toggle reflex self-heal loop (v0.1, default off)"),
58
+ ("/reason", "Reasoning engine: /reason status|guide|react|reflexion|tot|got|mcts|qcr|neuro_symbolic"),
59
+ ("/thinking", "Set/toggle thinking display: /thinking [on|off|status]"),
60
+ ("/verify", "Set/toggle claim-grounding verify mode: /verify [on|off|status]"),
61
+ ("/keepalive", "Show or set model keep-alive"),
62
+ ("/perf", "Show recent latency metrics"),
63
+ ("/metrics", "Reset recent latency metrics"),
64
+ ("/dashboard", "Open the runtime overview"),
65
+ ("/status", "Show current model, context usage, and active features"),
66
+ ("/identity", "Show identity file status"),
67
+ ("/lesson", "Append a lesson to lessons-learned.md"),
68
+ ("/lessons", "Show lesson index status or 'reindex'"),
69
+ ("/skills", "Review quarantined skills; 'crystallize' / 'approve' / 'reject'"),
70
+ ("/intuition", "Manage embedded memory recall"),
71
+ ("/intelligence", "Inspect repository intelligence: status, query, reindex"),
72
+ ("/intel", "Alias for /intelligence"),
73
+ ("/intelagence", "Alias for /intelligence"),
74
+ ("/kernel", "Kernel registry: /kernel list | show NAME | check [NAME]"),
75
+ ("/kernel list", "List promoted kernel specs"),
76
+ ("/kernel show", "Show a promoted kernel spec"),
77
+ ("/kernel check", "Validate kernel imports, slash routes, and active action contracts"),
78
+ ("/icl", "index-compute-lab graph status, on/off, or ask"),
79
+ ("/code-rag", "Opt in or out of working-directory code retrieval: on, off, status"),
80
+ ("/agent", "Run, delegate, resume, and inspect agent threads"),
81
+ ("/agent team", "Fan out 2-4 read-only specialists, then integrate and verify"),
82
+ ("/agent threads", "List persistent agent runs and child threads"),
83
+ ("/goal", "Work a task until complete: /goal [--rounds N] <task> | resume | status | clear"),
84
+ ("/route", "Preview routing, Agent Blocks budget, and tool policy"),
85
+ ("/memory-auto", "Inspect or toggle bounded automatic memory capture: /memory-auto [on|off|status]"),
86
+ ("/remember", "Store a memory"),
87
+ ("/memories", "List memories"),
88
+ ("/forget", "Delete a memory"),
89
+ ("/clear", "Clear conversation"),
90
+ ("/diff", "Show last verified Git diff captured by an agent pipeline"),
91
+ ("/changes", "Summarize the most recent agent pipeline's per-block activity"),
92
+ ("/context", "Show or manage context compression"),
93
+ ("/save", "Save conversation"),
94
+ ("/load", "Load conversation"),
95
+ ("/ctx", "Set context window"),
96
+ ("/temp", "Set temperature"),
97
+ ("/toolmax", "Set max tool iterations"),
98
+ ("/thinkevery", "Set reflection interval"),
99
+ ("/embed", "Generate embeddings"),
100
+ ("/vision", "Describe an image"),
101
+ ("/pdf", "Extract text from a PDF"),
102
+ ("/theme", "Switch visual theme"),
103
+ ("/info", "Show configuration"),
104
+ ("/actions", "Browse commands, tools, and capabilities"),
105
+ ("/doctor", "Show provider, dependency, ICL, and safety readiness"),
106
+ ("/selfcheck", "Audit harness, slash/action wiring, kernels, and retrieval readiness"),
107
+ ("/reload", "Reload configuration, tools, and harness state"),
108
+ ("/harness", "Inspect harness index health"),
109
+ ("/harness status", "Show harness index status and quality"),
110
+ ("/harness refresh", "Refresh harness index"),
111
+ ("/harness embed", "Embed pending harness records"),
112
+ ("/harness score", "Run the ten-gate benchmark and algorithm-effectiveness scorecard"),
113
+ ("/harness compare", "Recompute the external rating and strict leader gates"),
114
+ ("/harness external", "Opt in or out of indexing other local agent stores"),
115
+ ("/harness benchmark-embed", "Measure synthetic embed throughput (--count N --model NAME)"),
116
+ ("/harness build-rust", "Build optional Rust indexer"),
117
+ ("/harness rust", "Build optional Rust indexer"),
118
+ ("/hsearch", "Search harness assets"),
119
+ ("/hs", "Alias for /hsearch"),
120
+ ("/hread", "Read a harness record"),
121
+ ("/hr", "Alias for /hread"),
122
+ ("/cd", "Change working directory"),
123
+ ("/ls", "List directory (cwd-relative)"),
124
+ ("/read", "Read a file (cwd-relative; LLM: session_slash tool)"),
125
+ ("/plugins", "Inspect plugin manifests without executing plugin code"),
126
+ ("/credentials", "List helpers or check a named helper key (value redacted)"),
127
+ ("/url-scheme", "Parse an algo-cli:// deep link: /url-scheme <url> | help"),
128
+ ("/mode", "Session mode: execute | explore | publish"),
129
+ ("/exit", "Exit"),
130
+ ("/quit", "Exit"),
131
+ ]
132
+
133
+ SLASH_COMMAND_ALIASES: dict[str, str] = {
134
+ "/hs": "/hsearch",
135
+ "/hr": "/hread",
136
+ }
137
+
138
+
139
+ class SlashCommandCompleter(Completer):
140
+ def __init__(self, commands: list[tuple[str, str]]):
141
+ self.commands = commands
142
+
143
+ def get_completions(self, document, complete_event):
144
+ text = document.text_before_cursor
145
+ if not text.startswith("/"):
146
+ return
147
+ normalized = text.lower()
148
+ for command, description in self.commands:
149
+ if command.lower().startswith(normalized):
150
+ yield Completion(
151
+ command,
152
+ start_position=-len(text),
153
+ display=command,
154
+ display_meta=description,
155
+ )
156
+
157
+
158
+ _PATH_SLASH_COMMANDS = frozenset({"/cd", "/read", "/ls"})
159
+ _HARNESS_SUBCOMMANDS = (
160
+ "status",
161
+ "refresh",
162
+ "embed",
163
+ "score",
164
+ "compare",
165
+ "external",
166
+ "build-rust",
167
+ "benchmark-embed",
168
+ )
169
+ _HARNESS_USAGE = (
170
+ "Usage: /harness [status|refresh|embed|score|compare|build-rust] or "
171
+ "/harness external [on|off|status] or "
172
+ "/harness benchmark-embed [--count N] [--model NAME]. "
173
+ "Search with /hsearch (alias /hs); read with /hread (alias /hr)."
174
+ )
175
+
176
+
177
+ def slash_command_suggestions(command: str, *, limit: int = 3) -> list[str]:
178
+ """Return close slash-command matches for a mistyped command token."""
179
+ token = (command or "").strip().split(maxsplit=1)[0].lower()
180
+ if not token.startswith("/"):
181
+ return []
182
+ commands = [name for name, _description in SLASH_COMMANDS if " " not in name]
183
+ return get_close_matches(token, commands, n=max(1, limit), cutoff=0.6)
184
+
185
+
186
+ def unknown_command_message(raw: str) -> str:
187
+ command = (raw or "").strip().split(maxsplit=1)[0] or "(empty)"
188
+ suggestions = slash_command_suggestions(command, limit=1)
189
+ suggestion = f" Did you mean {suggestions[0]}?" if suggestions else ""
190
+ return (
191
+ f"Unknown command: {command}.{suggestion} "
192
+ "Use /help. If this command was added in a code update, exit and restart algo-cli."
193
+ )
194
+
195
+
196
+ def harness_subcommand_error(arg: str) -> str:
197
+ """Return an actionable error for an unknown ``/harness`` subcommand."""
198
+ parts = (arg or "").strip().split(maxsplit=1)
199
+ subcommand = parts[0].lower() if parts else "(empty)"
200
+ if len(parts) > 1 and subcommand in {
201
+ "status",
202
+ "stats",
203
+ "quality",
204
+ "refresh",
205
+ "embed",
206
+ "score",
207
+ "scorecard",
208
+ "compare",
209
+ "competitive",
210
+ "grade",
211
+ "rating",
212
+ "build-rust",
213
+ "rust",
214
+ "help",
215
+ "?",
216
+ }:
217
+ return f"Unexpected arguments for /harness {subcommand}: {parts[1]}. {_HARNESS_USAGE}"
218
+ suggestions = get_close_matches(subcommand, _HARNESS_SUBCOMMANDS, n=1, cutoff=0.6)
219
+ suggestion = f" Did you mean {suggestions[0]}?" if suggestions else ""
220
+ return f"Unknown /harness subcommand: {subcommand}.{suggestion} {_HARNESS_USAGE}"
221
+
222
+
223
+ _TOGGLE_ON = {"on", "true", "1", "yes", "enable", "enabled"}
224
+ _TOGGLE_OFF = {"off", "false", "0", "no", "disable", "disabled"}
225
+ _TOGGLE_STATUS = {"status", "show", "?"}
226
+ _REASON_MODE_ALIASES = {
227
+ "neuro-symbolic": "neuro_symbolic",
228
+ "neurosymbolic": "neuro_symbolic",
229
+ }
230
+ _REASON_MODE_GUIDE = [
231
+ "react: default for normal tool-use loops, file inspection, small coding edits, and straightforward tasks.",
232
+ "reflexion: after a failed, partial, or contradicted attempt when self-critique/retry discipline is needed.",
233
+ "tot: Tree-of-Thought for ambiguous planning or architecture decisions with several independent paths.",
234
+ "got: Graph-of-Thought for interconnected evidence/ideas that should merge or revise each other.",
235
+ "mcts: Monte Carlo tree search for deeper exploration/exploitation across many possible action sequences.",
236
+ "qcr: quantum-inspired aggregation for comparing/ranking multiple candidate solutions or reasoning fragments.",
237
+ "neuro_symbolic: verification-heavy logic, math, code invariants, contracts, or claim checking.",
238
+ "depth N / branches N: raise search cost only when deeper exploration is worth the latency.",
239
+ "auto-reflexion / auto-verify: enable only when the user wants automated retry/verification behavior.",
240
+ "Do not change /reason for routine reads or simple edits; still verify facts with tools.",
241
+ ]
242
+
243
+
244
+ def _parse_toggle_arg(arg: str, current: bool) -> tuple[bool, bool] | None:
245
+ """Return (new_value, should_change). Empty arg means toggle; status means report only."""
246
+ value = (arg or "").strip().lower()
247
+ if not value:
248
+ return (not current, True)
249
+ if value in _TOGGLE_ON:
250
+ return (True, True)
251
+ if value in _TOGGLE_OFF:
252
+ return (False, True)
253
+ if value in _TOGGLE_STATUS:
254
+ return (current, False)
255
+ return None
256
+
257
+
258
+ def handle_command(raw: str, cfg: Config, client: Client, session: Any = None) -> tuple[bool, Client]:
259
+ from . import main as m
260
+
261
+ def refresh_after_model_change(updated_client: Client) -> None:
262
+ m.refresh_runtime_status(cfg, updated_client, force=True)
263
+ m.invalidate_prompt_toolbar(session)
264
+
265
+ stripped = raw.strip()
266
+ parts = stripped.split(maxsplit=1)
267
+ entered_command = parts[0].lower()
268
+ command = SLASH_COMMAND_ALIASES.get(entered_command, entered_command)
269
+ arg = parts[1] if len(parts) > 1 else ""
270
+ # Use the original command (parts[0]) to compute arg offset
271
+ # so that /CD, /Cd, /cD all work the same as /cd
272
+ if command in _PATH_SLASH_COMMANDS:
273
+ arg = stripped[len(parts[0]):].strip()
274
+
275
+ if command in {"/exit", "/quit"}:
276
+ raise EOFError
277
+ if command == "/help":
278
+ display.show_help()
279
+ elif command == "/model":
280
+ if arg:
281
+ cfg.model = arg
282
+ cfg.save()
283
+ m.show_info(f"Model set to {cfg.model}")
284
+ client = m.create_client(cfg)
285
+ refresh_after_model_change(client)
286
+ else:
287
+ if m.model_picker(cfg):
288
+ client = m.create_client(cfg)
289
+ refresh_after_model_change(client)
290
+ elif command == "/models":
291
+ if m.model_picker(cfg):
292
+ client = m.create_client(cfg)
293
+ refresh_after_model_change(client)
294
+ elif command == "/host":
295
+ if arg:
296
+ cfg.host = arg
297
+ cfg.cloud = False
298
+ cfg.save()
299
+ m.start_ollama_server(cfg)
300
+ client = m.create_client(cfg)
301
+ m.show_info(f"Local host set to {cfg.host}")
302
+ else:
303
+ m.show_info(f"Host: {cfg.host}")
304
+ elif command == "/cloud":
305
+ parsed = _parse_toggle_arg(arg, bool(cfg.cloud))
306
+ if parsed is None:
307
+ m.show_error("Usage: /cloud [on|off|status]")
308
+ return True, client
309
+ new_value, should_change = parsed
310
+ m.load_runtime_env(override=True)
311
+ has_api_key = bool(os.environ.get("OLLAMA_API_KEY", "").strip())
312
+ if should_change and new_value and not has_api_key:
313
+ if cfg.cloud:
314
+ cfg.cloud = False
315
+ cfg.save()
316
+ m.start_ollama_server(cfg)
317
+ client = m.create_client(cfg)
318
+ m.show_info(
319
+ "Ollama Cloud via local Ollama login: using local Ollama at "
320
+ f"{cfg.host}. Direct Cloud API mode requires OLLAMA_API_KEY."
321
+ )
322
+ if m.is_cloud_model_name(cfg.model):
323
+ m.show_info(f"Current cloud model {cfg.model} will route through local Ollama login.")
324
+ else:
325
+ m.show_info("Run /login if needed, then select a :cloud model with /model or /models.")
326
+ return True, client
327
+ changed = should_change and (bool(cfg.cloud) != new_value)
328
+ if should_change:
329
+ cfg.cloud = new_value
330
+ cfg.save()
331
+ client = m.create_client(cfg)
332
+ if cfg.cloud:
333
+ m.show_info("Ollama Cloud direct API mode: ON")
334
+ elif m.is_cloud_model_name(cfg.model):
335
+ m.show_info(f"Ollama Cloud via local Ollama login: ON for {cfg.model}")
336
+ else:
337
+ m.show_info("Ollama Cloud direct API mode: OFF. Local Ollama login can still run :cloud models.")
338
+ if should_change:
339
+ if cfg.cloud:
340
+ m.start_supplemental_gateway(cfg)
341
+ if cfg.auto_cloud_connect:
342
+ m.maybe_prompt_cloud_login()
343
+ else:
344
+ m.show_info("Direct Cloud API mode is on. OLLAMA_API_KEY is used for chat/web access.")
345
+ elif changed:
346
+ m.start_ollama_server(cfg)
347
+ elif command == "/cloudauto":
348
+ parsed = _parse_toggle_arg(arg, bool(cfg.auto_cloud_connect))
349
+ if parsed is None:
350
+ m.show_error("Usage: /cloudauto [on|off|status]")
351
+ return True, client
352
+ new_value, should_change = parsed
353
+ if should_change:
354
+ cfg.auto_cloud_connect = new_value
355
+ cfg.save()
356
+ m.show_info(f"Cloud auto-connect prompt: {'ON' if cfg.auto_cloud_connect else 'OFF'}")
357
+ elif command == "/login":
358
+ m.run_ollama_login()
359
+ m.load_runtime_env(override=True)
360
+ client = m.create_client(cfg)
361
+ elif command == "/xai-login":
362
+ m.run_xai_login(arg)
363
+ elif command == "/xai-logout":
364
+ m.run_xai_logout()
365
+ elif command == "/xai-status":
366
+ m.run_xai_status()
367
+ elif command == "/xai-test":
368
+ m.run_xai_test()
369
+ elif command == "/google-login":
370
+ m.run_google_login(arg)
371
+ elif command == "/google-callback":
372
+ m.run_google_callback(arg)
373
+ elif command == "/google-logout":
374
+ m.run_google_logout()
375
+ elif command == "/google-status":
376
+ m.run_google_status()
377
+ elif command == "/google":
378
+ m.run_google(arg)
379
+ elif command == "/chatgpt-login":
380
+ m.run_chatgpt_login(arg)
381
+ elif command == "/chatgpt-logout":
382
+ m.run_chatgpt_logout()
383
+ elif command == "/chatgpt-status":
384
+ m.run_chatgpt_status()
385
+ elif command == "/model-check":
386
+ m.run_model_check(arg, active_model=cfg.model)
387
+ elif command == "/x-account":
388
+ m.run_x_account(arg)
389
+ elif command == "/system":
390
+ if arg:
391
+ cfg.system = arg
392
+ cfg.save()
393
+ m.show_info("System prompt updated.")
394
+ else:
395
+ m.console.print(cfg.system)
396
+ elif command == "/auto":
397
+ parsed = _parse_toggle_arg(arg, bool(cfg.auto_mode))
398
+ if parsed is None:
399
+ m.show_error("Usage: /auto [on|off|status]")
400
+ return True, client
401
+ new_value, should_change = parsed
402
+ if should_change:
403
+ cfg.auto_mode = new_value
404
+ cfg.save()
405
+ m.show_info(f"Auto-approve: {'ON' if cfg.auto_mode else 'OFF'}")
406
+ elif command == "/safe":
407
+ parsed = _parse_toggle_arg(arg, bool(cfg.safe_mode))
408
+ if parsed is None:
409
+ m.show_error("Usage: /safe [on|off|status]")
410
+ return True, client
411
+ new_value, should_change = parsed
412
+ if should_change:
413
+ cfg.safe_mode = new_value
414
+ cfg.save()
415
+ m.show_info(f"Safe mode: {'ON' if cfg.safe_mode else 'OFF'}")
416
+ elif command == "/policy":
417
+ sub = arg.strip().lower() or "status"
418
+ if sub in {"on", "off"}:
419
+ cfg.algorithmic_tool_policy_enabled = sub == "on"
420
+ cfg.save()
421
+ elif sub != "status":
422
+ m.show_error("Usage: /policy [on|off|status]")
423
+ return True, client
424
+ state = "ON" if cfg.algorithmic_tool_policy_enabled else "OFF"
425
+ detail = "enforced for Agent Blocks" if cfg.algorithmic_tool_policy_enabled else "preview only"
426
+ m.show_info(f"Algorithmic tool policy: {state} ({detail}).")
427
+ elif command == "/thinking":
428
+ parsed = _parse_toggle_arg(arg, bool(cfg.show_thinking))
429
+ if parsed is None:
430
+ m.show_error("Usage: /thinking [on|off|status]")
431
+ return True, client
432
+ new_value, should_change = parsed
433
+ if should_change:
434
+ cfg.show_thinking = new_value
435
+ cfg.save()
436
+ m.show_info(f"Thinking display: {'ON' if cfg.show_thinking else 'OFF'}")
437
+ elif command == "/verify":
438
+ parsed = _parse_toggle_arg(arg, bool(cfg.verify_mode))
439
+ if parsed is None:
440
+ m.show_error("Usage: /verify [on|off|status]")
441
+ return True, client
442
+ new_value, should_change = parsed
443
+ if should_change:
444
+ cfg.verify_mode = new_value
445
+ cfg.save()
446
+ m.show_info(
447
+ f"Verify mode: {'ON' if cfg.verify_mode else 'OFF'} "
448
+ f"({'claim-grounding pipeline active after each response' if cfg.verify_mode else 'disabled'})"
449
+ )
450
+ elif command == "/keepalive":
451
+ if arg:
452
+ cfg.keep_alive = arg
453
+ cfg.save()
454
+ m.show_info(f"Keep-alive set to {cfg.keep_alive}")
455
+ else:
456
+ m.show_info(f"Keep-alive: {cfg.keep_alive}")
457
+ elif command == "/perf":
458
+ perf_telemetry.show_perf_summary()
459
+ elif command == "/metrics":
460
+ if arg.strip().lower() != "reset":
461
+ m.show_error("Usage: /metrics reset")
462
+ else:
463
+ for key in [item for item in m.RUNTIME_STATUS if item == "last_metrics" or item.startswith("last_") and item.endswith("_metrics")]:
464
+ m.RUNTIME_STATUS.pop(key, None)
465
+ try:
466
+ perf_telemetry.PERF_HISTORY_FILE.unlink(missing_ok=True)
467
+ except OSError as exc:
468
+ m.show_error(f"Could not reset performance history: {exc}")
469
+ else:
470
+ m.show_info("Performance metrics reset.")
471
+ elif command == "/dashboard":
472
+ installed_models, running_models, event_lines = m.collect_dashboard_state(client, cfg)
473
+ used, total, _remaining, _runtime_cap, _native = m.context_status(cfg, client=client)
474
+ m.show_session_overview(
475
+ model=cfg.model,
476
+ host=cfg.host,
477
+ cwd=cfg.cwd,
478
+ theme_name=cfg.theme,
479
+ cloud=cfg.cloud,
480
+ auto_mode=cfg.auto_approve_active,
481
+ safe_mode=cfg.safe_mode,
482
+ temperature=cfg.temperature,
483
+ used_tokens=used,
484
+ total_tokens=total,
485
+ summary_active=bool(cfg.session_summary.strip()),
486
+ tool_think_every=max(1, int(cfg.tool_think_every)),
487
+ max_tool_iterations=max(1, int(cfg.max_tool_iterations)),
488
+ memory_count=len(cfg.memories),
489
+ system_prompt=cfg.system,
490
+ messages=cfg.messages,
491
+ installed_models=installed_models,
492
+ running_models=running_models,
493
+ event_lines=event_lines,
494
+ )
495
+ elif command == "/status":
496
+ m.handle_status_command(cfg, client)
497
+ elif command == "/identity":
498
+ rows = identity.status()
499
+ m.console.print(f"[muted]Identity directory:[/] {identity.IDENTITY_DIR}")
500
+ for row in rows:
501
+ if row["exists"]:
502
+ m.console.print(
503
+ f" [primary]{row['name']:<20}[/] "
504
+ f"[muted]{row['size']:>6} B[/] "
505
+ f"[muted]{row['modified']}[/]"
506
+ )
507
+ else:
508
+ m.console.print(f" [muted]{row['name']:<20} missing[/]")
509
+ elif command == "/lesson":
510
+ if not arg:
511
+ m.show_error("Usage: /lesson <text>")
512
+ else:
513
+ path = identity.append_lesson(arg)
514
+ m.capture_intuition_block(cfg, "lesson", arg, source="/lesson")
515
+ m.show_info(f"Lesson saved to {path}")
516
+ elif command == "/lessons":
517
+ lessons_sub = arg.strip().lower()
518
+ if lessons_sub == "reindex":
519
+ backend, _reason = m.resolve_embed_backend(cfg)
520
+ if not m.host_is_local(cfg.host) or not m.ollama_server_ready(cfg.host):
521
+ m.show_error("Local Ollama is not reachable; cannot rebuild lesson embeddings.")
522
+ else:
523
+ embed_fn, _backend, active_model = m.make_embed_fn(cfg, harness.resolve_embed_model(cfg))
524
+ m.show_info(f"Rebuilding lesson embeddings with {active_model} ({backend})…")
525
+ result = identity.rebuild_lessons_index(
526
+ embed_fn,
527
+ active_model,
528
+ expected_dimensions=m.configured_embed_dimensions(cfg),
529
+ )
530
+ if result.get("ready"):
531
+ m.show_info(f"Indexed {result.get('chunk_count', 0)} lesson chunks.")
532
+ else:
533
+ m.show_error(f"Reindex failed: {result.get('reason', 'unknown')}")
534
+ elif lessons_sub in {"", "status", "show", "?"}:
535
+ status = identity.lessons_index_status()
536
+ m.console.print(f"[muted]Lessons file:[/] {identity.LESSONS_PATH}")
537
+ m.console.print(f" file present : [text]{status['file']}[/]")
538
+ m.console.print(f" index built : [text]{status['index']}[/]")
539
+ m.console.print(f" chunks : [text]{status['chunk_count']}[/]")
540
+ m.console.print(f" model : [text]{status['model'] or '-'}[/]")
541
+ m.console.print(f" stale : [{'warning' if status['stale'] else 'text'}]{status['stale']}[/]")
542
+ m.console.print("[muted]Use /lessons reindex to rebuild.[/]")
543
+ else:
544
+ m.show_error("Usage: /lessons [status|reindex]")
545
+ elif command == "/skills":
546
+ skill_parts = arg.strip().split(maxsplit=1)
547
+ sub = skill_parts[0].lower() if skill_parts else ""
548
+ skill_name = skill_parts[1].strip() if len(skill_parts) > 1 else ""
549
+ if sub == "crystallize":
550
+ llm_fn = m.make_local_maintenance_llm_fn(cfg)
551
+ if llm_fn is None:
552
+ m.show_error(
553
+ "Skill crystallization requires a reachable local Ollama host with a "
554
+ "non-embedding model; cloud fallback is disabled."
555
+ )
556
+ else:
557
+ m.show_info("Reviewing recent runs for skill discoveries…")
558
+ result = skills.crystallize(llm_fn)
559
+ quarantined = result.get("quarantined", [])
560
+ if quarantined:
561
+ m.show_info(
562
+ f"Quarantined {len(quarantined)} skill candidate(s): "
563
+ f"{', '.join(quarantined)}. Review and promote with /skills approve NAME."
564
+ )
565
+ else:
566
+ m.show_info(f"No skill candidates quarantined ({result.get('reason', 'nothing qualified')}).")
567
+ elif sub in {"approve", "reject"}:
568
+ if not skill_name:
569
+ m.show_error(f"Usage: /skills {sub} NAME")
570
+ else:
571
+ try:
572
+ if sub == "approve":
573
+ promoted = skills.promote_quarantined_skill(skill_name)
574
+ m.show_info(
575
+ f"Promoted skill {promoted.stem}. Run /harness refresh to index it."
576
+ )
577
+ else:
578
+ rejected = skills.reject_quarantined_skill(skill_name)
579
+ m.show_info(f"Rejected skill candidate {rejected.stem}.")
580
+ except (FileNotFoundError, FileExistsError, ValueError, OSError) as exc:
581
+ m.show_error(str(exc))
582
+ elif sub in {"on", "off"}:
583
+ cfg.skill_crystallize_enabled = sub == "on"
584
+ cfg.save()
585
+ m.show_info(
586
+ "Skill run-history capture and automatic crystallization: "
587
+ f"{'ON' if cfg.skill_crystallize_enabled else 'OFF'}"
588
+ )
589
+ if cfg.skill_crystallize_enabled:
590
+ m.show_info(
591
+ "Bounded run summaries are stored under ~/.algo_cli/private and are "
592
+ "processed only by a local non-embedding Ollama model."
593
+ )
594
+ elif sub in {"", "status", "show", "?"}:
595
+ status = skills.skills_status()
596
+ m.console.print(f"[muted]Skills directory:[/] {status['skills_dir']}")
597
+ m.console.print(f" crystallized : [text]{status['skill_count']}[/]")
598
+ m.console.print(f" quarantined : [text]{len(status['quarantined'])}[/]")
599
+ m.console.print(f" recorded runs : [text]{status['run_count']}[/]")
600
+ m.console.print(f" auto-crystallize: [text]{'on' if cfg.skill_crystallize_enabled else 'off'}[/] (every {max(1, int(cfg.skill_crystallize_every))} runs, {cfg.runs_since_crystallize} since last)")
601
+ if status["skills"]:
602
+ for name in status["skills"][:20]:
603
+ m.console.print(f" [primary]{name}[/]")
604
+ if status["quarantined"]:
605
+ m.console.print("[muted]Pending explicit review:[/]")
606
+ for name in status["quarantined"][:20]:
607
+ m.console.print(f" [warning]{name}[/]")
608
+ m.console.print(
609
+ "[muted]Use /skills crystallize to propose, /skills approve|reject NAME to review, "
610
+ "or /skills on|off to toggle.[/]"
611
+ )
612
+ else:
613
+ m.show_error("Usage: /skills [status|crystallize|approve NAME|reject NAME|on|off]")
614
+ elif command == "/icl":
615
+ m.handle_icl_command(arg, cfg)
616
+ elif command == "/code-rag":
617
+ sub = (arg or "status").strip().lower() or "status"
618
+ if sub == "on":
619
+ cfg.code_rag_enabled = True
620
+ cfg.code_rag_consent_version = CODE_RAG_CONSENT_VERSION
621
+ cfg.save()
622
+ m.show_info("Working-directory code retrieval: ON")
623
+ m.show_info(
624
+ "Source snippets from the active cwd may be included in model prompts, "
625
+ "including requests sent to cloud providers."
626
+ )
627
+ elif sub == "off":
628
+ cfg.code_rag_enabled = False
629
+ cfg.code_rag_consent_version = 0
630
+ cfg.save()
631
+ try:
632
+ removed = code_rag.purge_persisted_indexes()
633
+ except OSError as exc:
634
+ m.show_error(f"Code retrieval disabled, but persisted index purge failed: {exc}")
635
+ else:
636
+ m.show_info(
637
+ "Working-directory code retrieval: OFF · "
638
+ f"purged {removed} persisted index file(s)"
639
+ )
640
+ elif sub in {"status", "show", "?"}:
641
+ enabled = code_rag_consent_granted(cfg)
642
+ m.console.print(
643
+ "Working-directory code retrieval: "
644
+ f"{'on' if enabled else 'off'} · "
645
+ f"persisted indexes {code_rag.persisted_index_count()}"
646
+ )
647
+ m.console.print(
648
+ "[muted]When on, retrieved cwd snippets join the active model request and "
649
+ "may leave this machine with a cloud provider.[/]"
650
+ )
651
+ else:
652
+ m.show_error("Usage: /code-rag [on|off|status]")
653
+ elif command == "/intuition":
654
+ m.handle_intuition_command(arg, cfg)
655
+ elif command in {"/intelligence", "/intel", "/intelagence"}:
656
+ m.handle_intelligence_command(arg, cfg)
657
+ elif command == "/kernel":
658
+ m.handle_kernel_command(arg)
659
+ elif command == "/agent":
660
+ m.execute_agent_command(arg, cfg, client)
661
+ elif command == "/goal":
662
+ m.run_goal_loop(client, cfg, arg)
663
+ elif command == "/route":
664
+ if not arg.strip():
665
+ m.show_error("Usage: /route <task>")
666
+ else:
667
+ m.show_task_route(m.task_router.route_task(arg), cfg, prompt=arg)
668
+ elif command == "/memory-auto":
669
+ sub = arg.strip().lower()
670
+ if sub in {"on", "off"}:
671
+ cfg.memory_auto_capture_enabled = sub == "on"
672
+ cfg.save()
673
+ m.show_info(
674
+ f"Automatic memory capture: {'ON' if cfg.memory_auto_capture_enabled else 'OFF'}"
675
+ )
676
+ elif sub in {"", "status", "show", "?"}:
677
+ daily_limit = min(
678
+ memory_candidates.MAX_DAILY_WRITES,
679
+ max(0, int(cfg.memory_auto_daily_limit)),
680
+ )
681
+ entry_limit = min(
682
+ memory_candidates.MAX_AUTO_FINGERPRINTS,
683
+ max(0, int(cfg.memory_auto_entry_limit)),
684
+ )
685
+ char_limit = min(
686
+ memory_candidates.MAX_MEMORY_CHARS,
687
+ max(0, int(cfg.memory_auto_char_limit)),
688
+ )
689
+ m.show_info(
690
+ "Automatic memory capture: "
691
+ f"{'ON' if cfg.memory_auto_capture_enabled else 'OFF'} · "
692
+ f"daily limit {daily_limit} · "
693
+ f"fingerprint cap {entry_limit} · "
694
+ f"memory budget {char_limit} chars"
695
+ )
696
+ else:
697
+ m.show_error("Usage: /memory-auto [on|off|status]")
698
+ elif command == "/remember":
699
+ if not arg:
700
+ m.show_error("Usage: /remember <fact>")
701
+ else:
702
+ cfg.remember_fact(arg)
703
+ m.capture_intuition_block(cfg, "memory", arg, source="/remember")
704
+ m.show_info("Memory saved.")
705
+ elif command == "/memories":
706
+ display.show_memory(cfg.memories)
707
+ elif command == "/forget":
708
+ try:
709
+ display_index = int(arg)
710
+ if display_index < 1:
711
+ raise ValueError
712
+ removed = cfg.forget_memory_index(display_index - 1)
713
+ m.show_info(f"Forgot: {removed}")
714
+ except (ValueError, IndexError):
715
+ m.show_error("Usage: /forget <number>")
716
+ elif command == "/clear":
717
+ cfg.messages.clear()
718
+ cfg.session_summary = ""
719
+ cfg.attempt_ledger.clear()
720
+ reflex.reset_reflex_cycles(cfg)
721
+ m.clear_session_pipeline_blocks()
722
+ cfg.save()
723
+ m.show_info("Conversation cleared.")
724
+ elif command == "/mode":
725
+ from . import session_mode
726
+
727
+ sub = arg.strip().lower() or "status"
728
+ if sub in session_mode.VALID_MODES:
729
+ previous = cfg.session_mode
730
+ cfg.session_mode = sub
731
+ for note in session_mode.apply_mode_side_effects(cfg, sub, previous=previous):
732
+ m.show_info(note)
733
+ cfg.save()
734
+ m.show_info(session_mode.status_line(cfg))
735
+ elif sub == "status":
736
+ m.console.print(session_mode.describe(cfg))
737
+ else:
738
+ m.show_error("Usage: /mode [execute|explore|publish|status]")
739
+ elif command == "/plugins":
740
+ from .plugins import discover_plugins, plugin_status
741
+ sub = arg.strip().lower() or "list"
742
+ if sub in ("list", ""):
743
+ discovered = discover_plugins()
744
+ if not discovered:
745
+ m.show_info("No plugins discovered in ~/.algo_cli/plugins/")
746
+ else:
747
+ from rich.table import Table as _Table
748
+ table = _Table(title="Discovered Plugins")
749
+ table.add_column("Name", style="cyan")
750
+ table.add_column("Version", style="green")
751
+ table.add_column("Description")
752
+ table.add_column("Enabled", style="yellow")
753
+ for manifest in sorted(discovered, key=lambda item: item.name.lower()):
754
+ table.add_row(
755
+ manifest.name,
756
+ manifest.version,
757
+ manifest.description,
758
+ "yes" if manifest.enabled else "no",
759
+ )
760
+ m.console.print(table)
761
+ elif sub == "status":
762
+ statuses = plugin_status()
763
+ if not statuses:
764
+ m.show_info("No plugins loaded.")
765
+ else:
766
+ from rich.table import Table as _Table
767
+ table = _Table(title="Plugin Status")
768
+ table.add_column("Name", style="cyan")
769
+ table.add_column("Loaded", style="green")
770
+ table.add_column("Error", style="red")
771
+ for s in statuses:
772
+ table.add_row(
773
+ s.get("name", "?"),
774
+ "yes" if s.get("loaded") else "no",
775
+ s.get("error", "") or "",
776
+ )
777
+ m.console.print(table)
778
+ else:
779
+ m.show_error("Usage: /plugins [list|status]")
780
+ elif command == "/credentials":
781
+ from .credential_helpers import list_helpers, get_helper, get_credential
782
+ raw_sub = arg.strip()
783
+ sub = raw_sub.lower() or "list"
784
+ if sub in ("list", ""):
785
+ helpers = sorted(list_helpers())
786
+ if not helpers:
787
+ m.show_info("No credential helpers registered.")
788
+ else:
789
+ from rich.table import Table as _Table
790
+ table = _Table(title="Credential Helpers")
791
+ table.add_column("Name", style="cyan")
792
+ table.add_column("Description")
793
+ for name in helpers:
794
+ h = get_helper(name)
795
+ table.add_row(name, h.description if h else "?")
796
+ m.console.print(table)
797
+ elif sub.startswith("get "):
798
+ parts = raw_sub.split(maxsplit=2)
799
+ if len(parts) != 3:
800
+ m.show_error("Usage: /credentials get <helper> <key>")
801
+ return True, client
802
+ _verb, helper, key = parts
803
+ val = get_credential(helper, key)
804
+ if val is None:
805
+ m.show_info(f"No credential found for '{key}' in helper '{helper}'")
806
+ else:
807
+ m.show_info(f"{helper}/{key}: configured (value redacted)")
808
+ else:
809
+ m.show_error("Usage: /credentials [list|get <helper> <key>]")
810
+ elif command == "/url-scheme":
811
+ from .url_scheme import handle_deep_link, format_help
812
+ url = arg.strip()
813
+ if not url or url.lower() == "help":
814
+ m.console.print(format_help())
815
+ return True, client
816
+ deep_link_result = handle_deep_link(url)
817
+ if not deep_link_result.get("valid"):
818
+ m.show_error(f"Invalid URL: {deep_link_result.get('error', 'unknown error')}")
819
+ return True, client
820
+ m.show_info(f"Action: {deep_link_result.get('action', '?')}")
821
+ if deep_link_result.get("target"):
822
+ m.show_info(f"Target: {deep_link_result['target']}")
823
+ if deep_link_result.get("query"):
824
+ m.show_info(f"Query: {deep_link_result['query']}")
825
+ elif command == "/reflex":
826
+ sub = arg.strip().lower() or "status"
827
+ if sub in {"on", "off"}:
828
+ cfg.reflex_enabled = sub == "on"
829
+ cfg.save()
830
+ m.show_info(f"Reflex loop v0.1: {'ON' if cfg.reflex_enabled else 'OFF'}")
831
+ elif sub == "reset":
832
+ reflex.reset_reflex_cycles(cfg)
833
+ cfg.save()
834
+ m.show_info("Reflex cycle counter reset for this session.")
835
+ elif sub in {"status", "show", "?"}:
836
+ m.show_info(reflex.status_line(cfg))
837
+ else:
838
+ m.show_error("Usage: /reflex [on|off|status|reset]")
839
+ elif command == "/reason":
840
+ sub, _, rest = (arg or "status").strip().partition(" ")
841
+ sub = _REASON_MODE_ALIASES.get(sub.lower(), sub.lower()) or "status"
842
+ if sub == "status":
843
+ mode = getattr(cfg, "reasoning_mode", "react")
844
+ depth = getattr(cfg, "reasoning_depth", 4)
845
+ branches = getattr(cfg, "reasoning_branches", 3)
846
+ auto_ref = getattr(cfg, "reasoning_auto_reflexion", False)
847
+ auto_ver = getattr(cfg, "reasoning_auto_verify", False)
848
+ chat_on = getattr(cfg, "reasoning_chat_enabled", False)
849
+ m.show_info(
850
+ f"Reasoning engine: mode={mode} depth={depth} branches={branches}"
851
+ f" chat-preflight={'on' if chat_on else 'off'}"
852
+ f" auto-reflexion={auto_ref} auto-verify={auto_ver}. "
853
+ "Use /reason guide for when-to-use guidance."
854
+ )
855
+ elif sub in {"guide", "help"}:
856
+ m.show_info("Reasoning mode guide:")
857
+ for line in _REASON_MODE_GUIDE:
858
+ m.show_info(f"- {line}")
859
+ elif sub in {"react", "reflexion", "tot", "got", "mcts", "qcr", "neuro_symbolic", "hybrid"}:
860
+ cfg.reasoning_mode = sub
861
+ cfg.save()
862
+ m.show_info(f"Reasoning mode set to: {sub}")
863
+ elif sub == "depth":
864
+ try:
865
+ requested_depth = int(rest.strip())
866
+ if not 1 <= requested_depth <= 16:
867
+ raise ValueError
868
+ cfg.reasoning_depth = requested_depth
869
+ cfg.save()
870
+ m.show_info(f"Reasoning depth set to: {cfg.reasoning_depth}")
871
+ except (ValueError, TypeError):
872
+ m.show_error("Usage: /reason depth <1-16>")
873
+ elif sub == "branches":
874
+ try:
875
+ requested_branches = int(rest.strip())
876
+ if not 1 <= requested_branches <= 8:
877
+ raise ValueError
878
+ cfg.reasoning_branches = requested_branches
879
+ cfg.save()
880
+ m.show_info(f"Reasoning branches set to: {cfg.reasoning_branches}")
881
+ except (ValueError, TypeError):
882
+ m.show_error("Usage: /reason branches <1-8>")
883
+ elif sub == "auto-reflexion":
884
+ value = rest.strip().lower()
885
+ if value not in _TOGGLE_ON | _TOGGLE_OFF | _TOGGLE_STATUS:
886
+ m.show_error("Usage: /reason auto-reflexion [on|off|status]")
887
+ return True, client
888
+ toggle = cfg.reasoning_auto_reflexion if value in _TOGGLE_STATUS else value in _TOGGLE_ON
889
+ if value not in _TOGGLE_STATUS:
890
+ cfg.reasoning_auto_reflexion = toggle
891
+ cfg.save()
892
+ m.show_info(f'Auto-reflexion on failed blocks: {"ON" if toggle else "OFF"}')
893
+ elif sub == "auto-verify":
894
+ value = rest.strip().lower()
895
+ if value not in _TOGGLE_ON | _TOGGLE_OFF | _TOGGLE_STATUS:
896
+ m.show_error("Usage: /reason auto-verify [on|off|status]")
897
+ return True, client
898
+ toggle = cfg.reasoning_auto_verify if value in _TOGGLE_STATUS else value in _TOGGLE_ON
899
+ if value not in _TOGGLE_STATUS:
900
+ cfg.reasoning_auto_verify = toggle
901
+ cfg.save()
902
+ m.show_info(f'Auto-verify implement blocks: {"ON" if toggle else "OFF"}')
903
+ elif sub == "chat":
904
+ value = rest.strip().lower()
905
+ if value not in _TOGGLE_ON | _TOGGLE_OFF | _TOGGLE_STATUS:
906
+ m.show_error("Usage: /reason chat [on|off|status]")
907
+ return True, client
908
+ toggle = cfg.reasoning_chat_enabled if value in _TOGGLE_STATUS else value in _TOGGLE_ON
909
+ if value not in _TOGGLE_STATUS:
910
+ cfg.reasoning_chat_enabled = toggle
911
+ cfg.save()
912
+ m.show_info(
913
+ f'Reasoning chat preflight: {"ON" if toggle else "OFF"}'
914
+ + (f" (mode={cfg.reasoning_mode}; multi-call, runs each turn)" if toggle else "")
915
+ )
916
+ else:
917
+ m.show_info(
918
+ "Usage: /reason [status|guide|react|reflexion|tot|got|mcts|qcr|neuro_symbolic|neuro-symbolic|hybrid|"
919
+ "depth N|branches N|chat on|off|auto-reflexion on|off|auto-verify on|off]"
920
+ )
921
+ elif command == "/diff":
922
+ m.handle_diff_command()
923
+ elif command == "/changes":
924
+ m.handle_changes_command()
925
+ elif command == "/context":
926
+ m.handle_context_command(arg, cfg, client)
927
+ elif command == "/save":
928
+ if not arg:
929
+ m.show_error("Usage: /save <name>")
930
+ else:
931
+ try:
932
+ safe_name = safe_conversation_name(arg)
933
+ if safe_name != arg.strip():
934
+ m.show_info(f"Using canonical save name: {safe_name}")
935
+ path = cfg.save_conversation(safe_name)
936
+ except ValueError as exc:
937
+ m.show_error(str(exc))
938
+ else:
939
+ m.show_info(f"Saved conversation to {path}")
940
+ elif command == "/load":
941
+ if not arg:
942
+ m.show_error("Usage: /load <name>")
943
+ else:
944
+ try:
945
+ safe_name = safe_conversation_name(arg)
946
+ if safe_name != arg.strip():
947
+ m.show_info(f"Using canonical load name: {safe_name}")
948
+ count = cfg.load_conversation(safe_name)
949
+ m.show_info(f"Loaded {count} messages.")
950
+ except Exception as exc:
951
+ m.show_error(str(exc))
952
+ elif command == "/ctx":
953
+ if arg:
954
+ try:
955
+ requested_context = int(arg)
956
+ if not 256 <= requested_context <= 2_000_000:
957
+ raise ValueError
958
+ cfg.num_ctx = requested_context
959
+ cfg.save()
960
+ except ValueError:
961
+ m.show_error("Usage: /ctx <number from 256 to 2000000>")
962
+ m.show_info(f"Context: {cfg.num_ctx}")
963
+ elif command == "/temp":
964
+ if arg:
965
+ try:
966
+ import math
967
+
968
+ requested_temperature = float(arg)
969
+ if not math.isfinite(requested_temperature) or not 0.0 <= requested_temperature <= 2.0:
970
+ raise ValueError
971
+ cfg.temperature = requested_temperature
972
+ cfg.save()
973
+ except ValueError:
974
+ m.show_error("Usage: /temp <number from 0.0 to 2.0>")
975
+ m.show_info(f"Temperature: {cfg.temperature}")
976
+ elif command == "/toolmax":
977
+ if arg:
978
+ try:
979
+ requested_tool_limit = int(arg)
980
+ if not 1 <= requested_tool_limit <= 128:
981
+ raise ValueError
982
+ cfg.max_tool_iterations = requested_tool_limit
983
+ cfg.save()
984
+ except ValueError:
985
+ m.show_error("Usage: /toolmax <1-128>")
986
+ m.show_info(f"Max tool iterations: {cfg.max_tool_iterations}")
987
+ elif command == "/thinkevery":
988
+ if arg:
989
+ try:
990
+ requested_reflection_interval = int(arg)
991
+ if not 1 <= requested_reflection_interval <= 128:
992
+ raise ValueError
993
+ cfg.tool_think_every = requested_reflection_interval
994
+ cfg.save()
995
+ except ValueError:
996
+ m.show_error("Usage: /thinkevery <1-128>")
997
+ m.show_info(f"Tool-call reflection interval: {cfg.tool_think_every}")
998
+ elif command == "/embed":
999
+ m.handle_embed_command(arg, cfg, client)
1000
+ elif command == "/vision":
1001
+ m.handle_vision_command(arg, cfg, client)
1002
+ elif command == "/pdf":
1003
+ m.handle_pdf_command(arg, cfg)
1004
+ elif command == "/theme":
1005
+ if not arg:
1006
+ m.show_info(f"Current theme: {cfg.theme}")
1007
+ m.console.print("Available themes: " + ", ".join(display.available_themes()))
1008
+ else:
1009
+ try:
1010
+ cfg.theme = m.set_theme(arg)
1011
+ cfg.save()
1012
+ if session is not None:
1013
+ try:
1014
+ session.style = m.build_prompt_style(m.theme_colors(cfg.theme))
1015
+ except Exception:
1016
+ pass
1017
+ m.invalidate_prompt_toolbar(session)
1018
+ m.show_info(f"Theme set to {cfg.theme}")
1019
+ except ValueError as exc:
1020
+ m.show_error(f"{exc}. Available themes: {', '.join(display.available_themes())}")
1021
+ elif command == "/cd":
1022
+ from .workspace_resolver import parse_path_arg
1023
+
1024
+ if not arg:
1025
+ m.show_info(f"cwd: {cfg.cwd}")
1026
+ else:
1027
+ from .workspace_resolver import parse_path_arg
1028
+
1029
+ target = parse_path_arg(arg)
1030
+ path = Path(target).expanduser()
1031
+ if not path.is_absolute():
1032
+ path = Path(cfg.cwd) / path
1033
+ if path.exists() and path.is_dir():
1034
+ cfg.cwd = str(path.resolve())
1035
+ cfg.save()
1036
+ m.show_info(f"cwd: {cfg.cwd}")
1037
+ else:
1038
+ m.show_error(f"Not a directory: {path}")
1039
+ elif command == "/ls":
1040
+ from . import tools as tools_module
1041
+ from .workspace_resolver import parse_path_arg
1042
+
1043
+ rel = parse_path_arg(arg) or "."
1044
+ directory_listing = tools_module.list_directory(rel, cwd=cfg.cwd, limit=40)
1045
+ m.console.print(directory_listing)
1046
+ elif command == "/read":
1047
+ from . import tools as tools_module
1048
+ from .workspace_resolver import parse_path_arg
1049
+
1050
+ rel = parse_path_arg(arg)
1051
+ if not rel:
1052
+ m.show_error("Usage: /read <path>")
1053
+ else:
1054
+ file_contents = tools_module.read_file(rel, cwd=cfg.cwd)
1055
+ m.console.print(file_contents)
1056
+ elif command == "/info":
1057
+ if arg.strip().lower() == "json":
1058
+ m.console.print(
1059
+ json.dumps(
1060
+ {
1061
+ "model": cfg.model,
1062
+ "host": cfg.host,
1063
+ "cloud": cfg.cloud,
1064
+ "cwd": cfg.cwd,
1065
+ "context": cfg.num_ctx,
1066
+ "temperature": cfg.temperature,
1067
+ "theme": cfg.theme,
1068
+ "auto_mode": cfg.auto_mode,
1069
+ "auto_cloud_connect": cfg.auto_cloud_connect,
1070
+ "safe_mode": cfg.safe_mode,
1071
+ "algorithmic_tool_policy_enabled": cfg.algorithmic_tool_policy_enabled,
1072
+ "thinking": cfg.show_thinking,
1073
+ "keep_alive": cfg.keep_alive,
1074
+ "tool_think_every": cfg.tool_think_every,
1075
+ "messages": len(cfg.messages),
1076
+ "memories": len(cfg.memories),
1077
+ "attempts": len(cfg.attempt_ledger),
1078
+ "last_metrics": m.RUNTIME_STATUS.get("last_metrics", {}),
1079
+ },
1080
+ indent=2,
1081
+ )
1082
+ )
1083
+ else:
1084
+ info_used, info_total, *_rest = m.context_status(cfg, client=client)
1085
+ m.show_session_overview(
1086
+ model=cfg.model,
1087
+ host=cfg.host,
1088
+ cwd=cfg.cwd,
1089
+ theme_name=cfg.theme,
1090
+ cloud=cfg.cloud,
1091
+ auto_mode=cfg.auto_approve_active,
1092
+ safe_mode=cfg.safe_mode,
1093
+ temperature=cfg.temperature,
1094
+ used_tokens=info_used,
1095
+ total_tokens=info_total,
1096
+ summary_active=bool(cfg.session_summary.strip()),
1097
+ tool_think_every=max(1, int(cfg.tool_think_every)),
1098
+ max_tool_iterations=max(1, int(cfg.max_tool_iterations)),
1099
+ memory_count=len(cfg.memories),
1100
+ system_prompt=cfg.system,
1101
+ messages=cfg.messages,
1102
+ )
1103
+ elif command == "/harness":
1104
+ from .tools import (
1105
+ harness_competitive_rating,
1106
+ harness_refresh,
1107
+ harness_scorecard,
1108
+ harness_stats,
1109
+ )
1110
+
1111
+ harness_arg = arg.strip()
1112
+ harness_parts = harness_arg.split(maxsplit=1)
1113
+ subcommand = harness_parts[0].lower() if harness_parts else ""
1114
+ subargs = harness_parts[1] if len(harness_parts) > 1 else ""
1115
+ if subargs and subcommand not in {"benchmark-embed", "external"}:
1116
+ m.show_error(harness_subcommand_error(harness_arg))
1117
+ elif subcommand == "refresh":
1118
+ m.console.print(harness_refresh())
1119
+ elif subcommand in {"", "status", "stats", "quality"}:
1120
+ m.console.print(harness_stats())
1121
+ elif subcommand in {"score", "scorecard", "grade", "rating"}:
1122
+ m.console.print(harness_scorecard())
1123
+ elif subcommand in {"compare", "competitive"}:
1124
+ m.console.print(harness_competitive_rating())
1125
+ elif subcommand == "external":
1126
+ external_arg = (subargs or "status").strip().lower()
1127
+ if external_arg in {"on", "off"}:
1128
+ cfg.external_harness_sources_enabled = external_arg == "on"
1129
+ cfg.save()
1130
+ harness.configure_context_sources(
1131
+ external=cfg.external_harness_sources_enabled,
1132
+ index_compute_lab=cfg.index_compute_lab_auto_inject,
1133
+ )
1134
+ harness.load_index(refresh=True)
1135
+ m.show_info(
1136
+ "External harness sources: "
1137
+ f"{'ON' if cfg.external_harness_sources_enabled else 'OFF'}"
1138
+ )
1139
+ if cfg.external_harness_sources_enabled:
1140
+ m.show_info(
1141
+ "Content from other local agent stores may be included in model prompts, "
1142
+ "including requests sent to cloud providers."
1143
+ )
1144
+ elif external_arg in {"", "status", "show", "?"}:
1145
+ m.console.print(
1146
+ "External harness sources: "
1147
+ f"{'on' if cfg.external_harness_sources_enabled else 'off'}"
1148
+ )
1149
+ else:
1150
+ m.show_error("Usage: /harness external [on|off|status]")
1151
+ elif subcommand in {"build-rust", "rust"}:
1152
+ m.build_rust_indexer()
1153
+ elif subcommand == "benchmark-embed":
1154
+ m.run_harness_benchmark_embed(cfg, harness_arg)
1155
+ elif subcommand == "embed":
1156
+ backend, _reason = m.resolve_embed_backend(cfg)
1157
+ if not m.host_is_local(cfg.host) or not m.ollama_server_ready(cfg.host):
1158
+ m.show_error("Local Ollama is not reachable; cannot embed harness records.")
1159
+ else:
1160
+ embed_fn, _backend, active_model = m.make_embed_fn(cfg, harness.resolve_embed_model(cfg))
1161
+ matching, total = harness.embedded_count(active_model)
1162
+ pending = total - matching
1163
+ if matching == 0 and any(r.get("embedding") for r in (harness.load_index().get("records") or [])):
1164
+ m.show_info(
1165
+ f"Backend/model change detected: all {total} records "
1166
+ f"will be re-embedded under {active_model}."
1167
+ )
1168
+ queue = harness.embedding_progress(active_model)
1169
+ high_value = (
1170
+ f" High-value coverage: {queue.get('high_value_embedded', 0)}/"
1171
+ f"{queue.get('high_value_total', 0)}; next tier: "
1172
+ f"{str(queue.get('next_priority') or 'complete').replace('_', ' ')}."
1173
+ if int(queue.get("total", 0)) == total and int(queue.get("high_value_total", 0)) > 0
1174
+ else ""
1175
+ )
1176
+ m.show_info(
1177
+ f"Embedding {pending} of {total} harness records with {active_model} ({backend}) "
1178
+ f"using {harness.EMBED_PRIORITY_POLICY}.{high_value}"
1179
+ )
1180
+ def _prog(done: int, target: int) -> None:
1181
+ if done == target or done % (max(1, target // 5)) == 0:
1182
+ m.show_info(f" embeddings: {done}/{target}")
1183
+ result = harness.embed_index_records(
1184
+ embed_fn,
1185
+ active_model,
1186
+ on_progress=_prog,
1187
+ on_perf=lambda rec: m.log_embed_perf(rec, source="harness_embed_command", backend=backend),
1188
+ )
1189
+ if result.get("ready"):
1190
+ m.show_info(f"Embedded {result.get('embedded', 0)} records. Total now embedded: {result.get('total', 0)}.")
1191
+ elif result.get("reason") == "max_records_reached":
1192
+ next_priority = str(result.get("next_priority") or "complete").replace("_", " ")
1193
+ m.show_info(
1194
+ f"Embedded {result.get('embedded', 0)} records; {result.get('pending', 0)} pending. "
1195
+ f"Next tier: {next_priority}."
1196
+ )
1197
+ else:
1198
+ m.show_error(f"Embedding failed: {result.get('reason', 'unknown')}")
1199
+ elif subcommand in {"help", "?"}:
1200
+ m.show_info(_HARNESS_USAGE)
1201
+ else:
1202
+ m.show_error(harness_subcommand_error(harness_arg))
1203
+ elif command == "/actions":
1204
+ from .tools import available_actions
1205
+
1206
+ m.console.print(available_actions(arg or None))
1207
+ elif command == "/doctor":
1208
+ from .action_registry import build_doctor_report, render_doctor
1209
+
1210
+ m.console.print(render_doctor(build_doctor_report(cfg)))
1211
+ elif command == "/selfcheck":
1212
+ from .tools import available_actions, harness_search, harness_stats
1213
+ from .action_registry import audit_action_registry_runtime, render_action_registry_runtime_audit
1214
+ from .kernels.manifest import audit_kernels, render_kernel_audit
1215
+ from .perf_telemetry import render_runtime_quality_snapshot
1216
+
1217
+ m.console.print(harness_stats())
1218
+ m.console.print()
1219
+ m.console.print(render_action_registry_runtime_audit(audit_action_registry_runtime()))
1220
+ m.console.print()
1221
+ m.console.print(render_kernel_audit(audit_kernels()))
1222
+ m.console.print()
1223
+ m.console.print(render_runtime_quality_snapshot(cfg))
1224
+ m.console.print()
1225
+ m.console.print(available_actions("harness"))
1226
+ m.console.print()
1227
+ for query in (
1228
+ "index-compute-lab",
1229
+ "reflex loop",
1230
+ "harness context",
1231
+ "memory recall",
1232
+ "verification before completion",
1233
+ ):
1234
+ m.console.print(f"[bold]Search:[/] {query}")
1235
+ m.console.print(harness_search(query=query, limit=5))
1236
+ m.console.print()
1237
+ elif command == "/reload":
1238
+ reloaded_cfg = m.reload_runtime()
1239
+ for field in fields(Config):
1240
+ if field.name in {"messages", "session_summary", "context_state", "attempt_ledger"}:
1241
+ continue
1242
+ setattr(cfg, field.name, getattr(reloaded_cfg, field.name))
1243
+ client = m.create_client(cfg)
1244
+ m.show_info("Reloaded config, tools, and harness index.")
1245
+ m.show_info(
1246
+ "Restart algo-cli after Python code or plugin changes; /reload refreshes "
1247
+ "configuration, core tool modules, routing, and the harness index."
1248
+ )
1249
+ elif command == "/hsearch":
1250
+ if not arg:
1251
+ m.show_error("Usage: /hsearch <query>")
1252
+ else:
1253
+ if harness.index_is_stale():
1254
+ m.show_info("Harness index may be stale (source dirs changed). Run /harness refresh to update.")
1255
+ m.print_harness_results(arg, cfg=cfg)
1256
+ elif command == "/hread":
1257
+ if not arg:
1258
+ m.show_error("Usage: /hread <record-id>")
1259
+ else:
1260
+ from .tools import harness_read
1261
+
1262
+ m.console.print(harness_read(arg))
1263
+ else:
1264
+ return False, client
1265
+ return True, client