novacode-cli 1.2.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 (691) hide show
  1. novacode_cli/__init__.py +29 -0
  2. novacode_cli/__main__.py +6 -0
  3. novacode_cli/_lazy_heavy.py +99 -0
  4. novacode_cli/_version.py +3 -0
  5. novacode_cli/agent_stream.py +178 -0
  6. novacode_cli/agents/agent_file.py +250 -0
  7. novacode_cli/agents/agent_mailbox.py +120 -0
  8. novacode_cli/agents/approval_decisions.py +146 -0
  9. novacode_cli/agents/arg_repair.py +196 -0
  10. novacode_cli/agents/async_agents/__init__.py +6 -0
  11. novacode_cli/agents/async_agents/_config.py +213 -0
  12. novacode_cli/agents/async_agents/_mcp_tools.py +192 -0
  13. novacode_cli/agents/async_agents/_specialist.py +84 -0
  14. novacode_cli/agents/async_agents/_test_commands.py +89 -0
  15. novacode_cli/agents/async_agents/_tools.py +432 -0
  16. novacode_cli/agents/async_agents/general_purpose.py +42 -0
  17. novacode_cli/agents/async_agents/langgraph.json +9 -0
  18. novacode_cli/agents/async_agents/research.py +22 -0
  19. novacode_cli/agents/async_context.py +303 -0
  20. novacode_cli/agents/async_on_demand.py +52 -0
  21. novacode_cli/agents/async_task_errors.py +75 -0
  22. novacode_cli/agents/async_workspace.py +27 -0
  23. novacode_cli/agents/attribution.py +165 -0
  24. novacode_cli/agents/browser_mode.py +119 -0
  25. novacode_cli/agents/btw_agent.py +71 -0
  26. novacode_cli/agents/caveman.py +352 -0
  27. novacode_cli/agents/caveman_support.py +370 -0
  28. novacode_cli/agents/code_mode.py +774 -0
  29. novacode_cli/agents/code_runtime.py +160 -0
  30. novacode_cli/agents/context_compaction.py +1339 -0
  31. novacode_cli/agents/core_agent.py +2710 -0
  32. novacode_cli/agents/default_subagents/__init__.py +24 -0
  33. novacode_cli/agents/default_subagents/async_subagents.py +296 -0
  34. novacode_cli/agents/default_subagents/builtin/browser-automation-agent.md +110 -0
  35. novacode_cli/agents/default_subagents/builtin/bug-fix-agent.md +22 -0
  36. novacode_cli/agents/default_subagents/builtin/bug-investigation-agent.md +42 -0
  37. novacode_cli/agents/default_subagents/builtin/code-explorer.md +37 -0
  38. novacode_cli/agents/default_subagents/builtin/code-review-agent.md +33 -0
  39. novacode_cli/agents/default_subagents/builtin/dependency-audit-agent.md +34 -0
  40. novacode_cli/agents/default_subagents/builtin/documentation-update-agent.md +46 -0
  41. novacode_cli/agents/default_subagents/builtin/performance-audit-agent.md +41 -0
  42. novacode_cli/agents/default_subagents/builtin/plan-scout-agent.md +64 -0
  43. novacode_cli/agents/default_subagents/builtin/refactoring-agent.md +35 -0
  44. novacode_cli/agents/default_subagents/builtin/refactoring-specialist-agent.md +175 -0
  45. novacode_cli/agents/default_subagents/builtin/security-audit-agent.md +96 -0
  46. novacode_cli/agents/default_subagents/builtin/test-generation-agent.md +33 -0
  47. novacode_cli/agents/default_subagents/builtin/test-runner-agent.md +55 -0
  48. novacode_cli/agents/default_subagents/prompt.py +88 -0
  49. novacode_cli/agents/default_subagents/subagents.py +138 -0
  50. novacode_cli/agents/dream_agent.py +93 -0
  51. novacode_cli/agents/edit_diagnostics.py +362 -0
  52. novacode_cli/agents/edit_fallback.py +62 -0
  53. novacode_cli/agents/handoff.py +166 -0
  54. novacode_cli/agents/invocation.py +152 -0
  55. novacode_cli/agents/model_budget.py +42 -0
  56. novacode_cli/agents/model_retry.py +113 -0
  57. novacode_cli/agents/model_router.py +420 -0
  58. novacode_cli/agents/openai_decisions.py +186 -0
  59. novacode_cli/agents/plan_agent/__init__.py +12 -0
  60. novacode_cli/agents/plan_agent/plan_agent.py +357 -0
  61. novacode_cli/agents/plan_agent/plan_mode_middleware.py +64 -0
  62. novacode_cli/agents/plan_execution.py +237 -0
  63. novacode_cli/agents/ponytail.py +66 -0
  64. novacode_cli/agents/prime.py +274 -0
  65. novacode_cli/agents/provider_caching.py +99 -0
  66. novacode_cli/agents/server_launcher.py +904 -0
  67. novacode_cli/agents/strict_tools.py +384 -0
  68. novacode_cli/agents/task_discipline.py +294 -0
  69. novacode_cli/agents/tool_call_repair.py +290 -0
  70. novacode_cli/agents/tool_offload.py +291 -0
  71. novacode_cli/agents/tool_search.py +806 -0
  72. novacode_cli/agents/tool_search_variants.py +106 -0
  73. novacode_cli/agents/tool_verdicts.py +1194 -0
  74. novacode_cli/agents/user_async_agents.py +312 -0
  75. novacode_cli/artifacts/__init__.py +10 -0
  76. novacode_cli/artifacts/registry.py +278 -0
  77. novacode_cli/artifacts/server.py +426 -0
  78. novacode_cli/assets/Nova.png +0 -0
  79. novacode_cli/assets/nova-avatar.png +0 -0
  80. novacode_cli/audio/__init__.py +301 -0
  81. novacode_cli/audio/capture.py +137 -0
  82. novacode_cli/audio/downloads.py +273 -0
  83. novacode_cli/audio/pipeline.py +391 -0
  84. novacode_cli/audio/playback.py +87 -0
  85. novacode_cli/audio/providers.py +208 -0
  86. novacode_cli/audio/speakable.py +78 -0
  87. novacode_cli/audio/stt.py +132 -0
  88. novacode_cli/audio/stt_deepgram.py +69 -0
  89. novacode_cli/audio/stt_elevenlabs.py +89 -0
  90. novacode_cli/audio/stt_parakeet.py +94 -0
  91. novacode_cli/audio/stt_whistle.py +61 -0
  92. novacode_cli/audio/summarize.py +92 -0
  93. novacode_cli/audio/tts.py +85 -0
  94. novacode_cli/audio/tts_kokoro.py +73 -0
  95. novacode_cli/audio/tts_orpheus.py +180 -0
  96. novacode_cli/audio/tts_pocket.py +131 -0
  97. novacode_cli/audio/tts_sano.py +69 -0
  98. novacode_cli/audio/vad.py +145 -0
  99. novacode_cli/backends/__init__.py +17 -0
  100. novacode_cli/backends/filesystem.py +894 -0
  101. novacode_cli/backends/native_search.py +92 -0
  102. novacode_cli/bootstrap/__init__.py +9 -0
  103. novacode_cli/bootstrap/env_snapshot.py +240 -0
  104. novacode_cli/bootstrap/steering.py +464 -0
  105. novacode_cli/bootstrap/vision_router.py +604 -0
  106. novacode_cli/brand.py +362 -0
  107. novacode_cli/cli_args.py +718 -0
  108. novacode_cli/commands/__init__.py +237 -0
  109. novacode_cli/commands/agents_commands.py +231 -0
  110. novacode_cli/commands/auth_handler.py +40 -0
  111. novacode_cli/commands/browser_use_handler.py +75 -0
  112. novacode_cli/commands/cache_handler.py +32 -0
  113. novacode_cli/commands/chat_handler.py +1357 -0
  114. novacode_cli/commands/ci_fix.py +171 -0
  115. novacode_cli/commands/claude_plan.py +331 -0
  116. novacode_cli/commands/codex_plan.py +195 -0
  117. novacode_cli/commands/commands.py +1113 -0
  118. novacode_cli/commands/create_handler.py +124 -0
  119. novacode_cli/commands/create_server.py +1887 -0
  120. novacode_cli/commands/cron_handler.py +119 -0
  121. novacode_cli/commands/dream_handler.py +203 -0
  122. novacode_cli/commands/effort_handler.py +65 -0
  123. novacode_cli/commands/evolution_handler.py +214 -0
  124. novacode_cli/commands/file_commands.py +381 -0
  125. novacode_cli/commands/handoff_handler.py +68 -0
  126. novacode_cli/commands/hooks_handler.py +269 -0
  127. novacode_cli/commands/init_handler.py +146 -0
  128. novacode_cli/commands/learning_handler.py +160 -0
  129. novacode_cli/commands/lite_mode_handler.py +76 -0
  130. novacode_cli/commands/log_commands.py +363 -0
  131. novacode_cli/commands/mcp_handler.py +29 -0
  132. novacode_cli/commands/model_handler.py +29 -0
  133. novacode_cli/commands/notifications_handler.py +158 -0
  134. novacode_cli/commands/onboard_handler.py +121 -0
  135. novacode_cli/commands/plan_handler.py +440 -0
  136. novacode_cli/commands/planner_usage.py +250 -0
  137. novacode_cli/commands/plugin_install_handler.py +38 -0
  138. novacode_cli/commands/plugins_handler.py +35 -0
  139. novacode_cli/commands/prime_handler.py +455 -0
  140. novacode_cli/commands/prompt_handler.py +149 -0
  141. novacode_cli/commands/provider_usage.py +160 -0
  142. novacode_cli/commands/ralph_events.py +106 -0
  143. novacode_cli/commands/ralph_handler.py +1293 -0
  144. novacode_cli/commands/refine_handler.py +213 -0
  145. novacode_cli/commands/research_handler.py +202 -0
  146. novacode_cli/commands/rootapp_remote.py +83 -0
  147. novacode_cli/commands/router_handler.py +36 -0
  148. novacode_cli/commands/server_commands.py +249 -0
  149. novacode_cli/commands/session_commands.py +203 -0
  150. novacode_cli/commands/share_handler.py +450 -0
  151. novacode_cli/commands/side_commands.py +366 -0
  152. novacode_cli/commands/skill_invoke.py +420 -0
  153. novacode_cli/commands/skills_commands.py +48 -0
  154. novacode_cli/commands/trace_handler.py +322 -0
  155. novacode_cli/commands/trello_handler.py +226 -0
  156. novacode_cli/commands/trello_remote.py +177 -0
  157. novacode_cli/commands/trello_server.py +1214 -0
  158. novacode_cli/commands/vision_handler.py +127 -0
  159. novacode_cli/commands/voice_handler.py +447 -0
  160. novacode_cli/commands/webhook_handler.py +134 -0
  161. novacode_cli/commands/wiki_commands.py +227 -0
  162. novacode_cli/compaction.py +753 -0
  163. novacode_cli/computation_cache.py +159 -0
  164. novacode_cli/computer/__init__.py +1 -0
  165. novacode_cli/computer/persistent.py +234 -0
  166. novacode_cli/computer/preview.py +253 -0
  167. novacode_cli/computer/runtime.py +474 -0
  168. novacode_cli/computer/windows_console.py +152 -0
  169. novacode_cli/computer/windows_qemu.py +211 -0
  170. novacode_cli/config/browser_mode.py +159 -0
  171. novacode_cli/config/catalog_models.py +95 -0
  172. novacode_cli/config/config.py +1388 -0
  173. novacode_cli/config/credentials.py +540 -0
  174. novacode_cli/config/google_oauth_auth.py +281 -0
  175. novacode_cli/config/model_capabilities.py +343 -0
  176. novacode_cli/config/model_catalog.py +275 -0
  177. novacode_cli/config/model_create.py +1325 -0
  178. novacode_cli/config/model_manager.py +695 -0
  179. novacode_cli/config/nova_config.py +1283 -0
  180. novacode_cli/config/openai_chatgpt_auth.py +384 -0
  181. novacode_cli/config/opencode_gateway.py +25 -0
  182. novacode_cli/config/plan_mode.py +188 -0
  183. novacode_cli/config/provider_auth.py +438 -0
  184. novacode_cli/config/provider_catalog.json +5127 -0
  185. novacode_cli/config/provider_registry.py +38 -0
  186. novacode_cli/config/provider_transports.py +258 -0
  187. novacode_cli/config/role_models.py +175 -0
  188. novacode_cli/context/__init__.py +50 -0
  189. novacode_cli/context/_analysis.py +799 -0
  190. novacode_cli/context/_budget.py +116 -0
  191. novacode_cli/context/_conversation.py +73 -0
  192. novacode_cli/context/_dynamic.py +264 -0
  193. novacode_cli/context/_model_config.py +380 -0
  194. novacode_cli/context/_models_dev.py +99 -0
  195. novacode_cli/context/archives.py +248 -0
  196. novacode_cli/context/estimates.py +65 -0
  197. novacode_cli/context/history.py +17 -0
  198. novacode_cli/context/knowledge_cache.py +292 -0
  199. novacode_cli/context/manager.py +101 -0
  200. novacode_cli/context/observations.py +65 -0
  201. novacode_cli/context/pressure.py +168 -0
  202. novacode_cli/context/provider_accounting.py +109 -0
  203. novacode_cli/context/snapshot.py +493 -0
  204. novacode_cli/context/strategies.py +279 -0
  205. novacode_cli/context/task_progress.py +122 -0
  206. novacode_cli/core/__init__.py +0 -0
  207. novacode_cli/core/agent_loop.py +1821 -0
  208. novacode_cli/core/autonomous_loop.py +277 -0
  209. novacode_cli/core/code_approvals.py +57 -0
  210. novacode_cli/core/input_preparation.py +276 -0
  211. novacode_cli/core/reasoning.py +67 -0
  212. novacode_cli/core/streaming.py +326 -0
  213. novacode_cli/core/subagent_tasks.py +696 -0
  214. novacode_cli/core/subagent_tracking.py +235 -0
  215. novacode_cli/core/tool_results.py +116 -0
  216. novacode_cli/core/verification_loop.py +272 -0
  217. novacode_cli/council.py +595 -0
  218. novacode_cli/council_consensus.py +115 -0
  219. novacode_cli/council_decision.py +157 -0
  220. novacode_cli/council_planner.py +1421 -0
  221. novacode_cli/council_settings.py +208 -0
  222. novacode_cli/council_web_settings.py +41 -0
  223. novacode_cli/council_workspace.py +56 -0
  224. novacode_cli/cowork/__init__.py +10 -0
  225. novacode_cli/cowork/broker_middleware.py +162 -0
  226. novacode_cli/cowork/launcher.py +102 -0
  227. novacode_cli/cowork/policy.py +284 -0
  228. novacode_cli/cowork/ui.py +826 -0
  229. novacode_cli/daemons/__init__.py +1 -0
  230. novacode_cli/daemons/registry.py +342 -0
  231. novacode_cli/desktop_notifications.py +302 -0
  232. novacode_cli/doctor.py +251 -0
  233. novacode_cli/embeddings.py +109 -0
  234. novacode_cli/entrypoint.py +56 -0
  235. novacode_cli/errors/__init__.py +19 -0
  236. novacode_cli/errors/handlers.py +648 -0
  237. novacode_cli/errors/provider_errors.py +249 -0
  238. novacode_cli/errors/taxonomy.py +80 -0
  239. novacode_cli/events.py +179 -0
  240. novacode_cli/file_ops.py +714 -0
  241. novacode_cli/git_safety.py +281 -0
  242. novacode_cli/handoff.py +383 -0
  243. novacode_cli/headless/__init__.py +18 -0
  244. novacode_cli/headless/input.py +58 -0
  245. novacode_cli/headless/output.py +224 -0
  246. novacode_cli/headless/pipe.py +532 -0
  247. novacode_cli/headless/runner.py +461 -0
  248. novacode_cli/hermes/__init__.py +39 -0
  249. novacode_cli/hermes/activation.py +297 -0
  250. novacode_cli/hermes/config.py +129 -0
  251. novacode_cli/hermes/curator.py +199 -0
  252. novacode_cli/hermes/evolution.py +374 -0
  253. novacode_cli/hermes/ledger.py +364 -0
  254. novacode_cli/hermes/memory_tiers.py +969 -0
  255. novacode_cli/hermes/middleware.py +678 -0
  256. novacode_cli/hermes/overview.py +174 -0
  257. novacode_cli/hermes/prompt_evolution.py +503 -0
  258. novacode_cli/hermes/refine_loop.py +643 -0
  259. novacode_cli/hermes/refinement_log.py +190 -0
  260. novacode_cli/hermes/review.py +674 -0
  261. novacode_cli/hermes/skill_debate.py +135 -0
  262. novacode_cli/hermes/skill_discovery.py +450 -0
  263. novacode_cli/hermes/skill_manager.py +335 -0
  264. novacode_cli/hermes/skill_proposals.py +43 -0
  265. novacode_cli/hermes/test_gaming.py +119 -0
  266. novacode_cli/hermes/tracker.py +435 -0
  267. novacode_cli/hermes/tuner.py +206 -0
  268. novacode_cli/hermes/verification_memory.py +134 -0
  269. novacode_cli/hermes/verifier.py +277 -0
  270. novacode_cli/hitl/__init__.py +22 -0
  271. novacode_cli/hitl/interrupts.py +493 -0
  272. novacode_cli/hooks.py +359 -0
  273. novacode_cli/image_utils.py +517 -0
  274. novacode_cli/init/__init__.py +7 -0
  275. novacode_cli/init/events.py +43 -0
  276. novacode_cli/input_utils.py +526 -0
  277. novacode_cli/install_commands.py +87 -0
  278. novacode_cli/integrations/__init__.py +82 -0
  279. novacode_cli/integrations/daytona.py +122 -0
  280. novacode_cli/integrations/docker.py +309 -0
  281. novacode_cli/integrations/host_path.py +111 -0
  282. novacode_cli/integrations/langsmith.py +151 -0
  283. novacode_cli/integrations/modal.py +134 -0
  284. novacode_cli/integrations/runloop.py +130 -0
  285. novacode_cli/integrations/sandbox_factory.py +1388 -0
  286. novacode_cli/integrations/sandbox_registry.py +312 -0
  287. novacode_cli/integrations/workdir_backend.py +589 -0
  288. novacode_cli/main.py +2078 -0
  289. novacode_cli/mcp/__init__.py +73 -0
  290. novacode_cli/mcp/auth.py +189 -0
  291. novacode_cli/mcp/client.py +571 -0
  292. novacode_cli/mcp/code_mode.py +5 -0
  293. novacode_cli/mcp/code_runtime.py +5 -0
  294. novacode_cli/mcp/commands.py +902 -0
  295. novacode_cli/mcp/config.py +406 -0
  296. novacode_cli/mcp/generic.py +128 -0
  297. novacode_cli/mcp/middleware.py +993 -0
  298. novacode_cli/mcp/presets.py +367 -0
  299. novacode_cli/memory/__init__.py +1 -0
  300. novacode_cli/memory/agent_memory.py +1312 -0
  301. novacode_cli/memory/builtin_lessons.py +60 -0
  302. novacode_cli/memory/dream_gates.py +192 -0
  303. novacode_cli/memory/limits.py +59 -0
  304. novacode_cli/memory/parsed_cache.py +106 -0
  305. novacode_cli/memory/recall_stats.py +97 -0
  306. novacode_cli/memory/semantic.py +116 -0
  307. novacode_cli/memory/store.py +335 -0
  308. novacode_cli/memory/wiki.py +274 -0
  309. novacode_cli/migrate.py +185 -0
  310. novacode_cli/onboarding.py +649 -0
  311. novacode_cli/onboarding_check.py +119 -0
  312. novacode_cli/path_approval.py +297 -0
  313. novacode_cli/plan_archive.py +148 -0
  314. novacode_cli/plugins/__init__.py +121 -0
  315. novacode_cli/plugins/claude_plugins.py +538 -0
  316. novacode_cli/plugins/cli.py +168 -0
  317. novacode_cli/plugins/components.py +413 -0
  318. novacode_cli/plugins/hooks.py +356 -0
  319. novacode_cli/plugins/loader.py +317 -0
  320. novacode_cli/plugins/marketplaces.py +199 -0
  321. novacode_cli/plugins/storage.py +70 -0
  322. novacode_cli/prime.py +759 -0
  323. novacode_cli/prime_factory/__init__.py +15 -0
  324. novacode_cli/prime_factory/container.py +198 -0
  325. novacode_cli/prime_factory/controller.py +565 -0
  326. novacode_cli/prime_factory/knowledge.py +261 -0
  327. novacode_cli/prime_factory/publication.py +218 -0
  328. novacode_cli/prime_factory/recovery.py +50 -0
  329. novacode_cli/prime_factory/research.py +370 -0
  330. novacode_cli/prime_factory/runner.py +608 -0
  331. novacode_cli/prime_factory/runtime.py +261 -0
  332. novacode_cli/prime_factory/state.py +201 -0
  333. novacode_cli/prime_factory/store.py +128 -0
  334. novacode_cli/prime_factory/worker.py +129 -0
  335. novacode_cli/prime_factory/workflow.py +899 -0
  336. novacode_cli/prime_factory/workspace.py +317 -0
  337. novacode_cli/prime_memory.py +290 -0
  338. novacode_cli/process_manager.py +585 -0
  339. novacode_cli/project_onboard.py +537 -0
  340. novacode_cli/prompts/System_Prompt_Nova.jinja +23 -0
  341. novacode_cli/prompts/__init__.py +168 -0
  342. novacode_cli/prompts/agent_definitions.jinja +11 -0
  343. novacode_cli/prompts/agent_generation.jinja +125 -0
  344. novacode_cli/prompts/async_agents/general_purpose.jinja +22 -0
  345. novacode_cli/prompts/claude_plan_mode.jinja +12 -0
  346. novacode_cli/prompts/coding_workflow.jinja +18 -0
  347. novacode_cli/prompts/computer_use.jinja +17 -0
  348. novacode_cli/prompts/core_agent_system.jinja +81 -0
  349. novacode_cli/prompts/delegation.jinja +15 -0
  350. novacode_cli/prompts/desktop_power.jinja +4 -0
  351. novacode_cli/prompts/file_paths.jinja +15 -0
  352. novacode_cli/prompts/file_tracker.jinja +23 -0
  353. novacode_cli/prompts/init_agents_md.jinja +56 -0
  354. novacode_cli/prompts/init_exploration.jinja +174 -0
  355. novacode_cli/prompts/init_semantic_orchestrate.jinja +37 -0
  356. novacode_cli/prompts/instruction_scope.jinja +5 -0
  357. novacode_cli/prompts/learnings_extraction.jinja +45 -0
  358. novacode_cli/prompts/longterm_memory.jinja +54 -0
  359. novacode_cli/prompts/mcp.jinja +84 -0
  360. novacode_cli/prompts/memory_consolidation.jinja +40 -0
  361. novacode_cli/prompts/nova_evolution.jinja +73 -0
  362. novacode_cli/prompts/nova_review.jinja +158 -0
  363. novacode_cli/prompts/nova_skill_debate.jinja +36 -0
  364. novacode_cli/prompts/nova_standing_instructions.jinja +26 -0
  365. novacode_cli/prompts/nova_verify.jinja +100 -0
  366. novacode_cli/prompts/onboard_dashboard.jinja +378 -0
  367. novacode_cli/prompts/plan_agent.jinja +87 -0
  368. novacode_cli/prompts/plan_scouts.jinja +5 -0
  369. novacode_cli/prompts/plan_storage.jinja +8 -0
  370. novacode_cli/prompts/planning.jinja +82 -0
  371. novacode_cli/prompts/ponytail/LICENSE +21 -0
  372. novacode_cli/prompts/ponytail/UPSTREAM.json +15 -0
  373. novacode_cli/prompts/ponytail/hooks/ponytail-config.js +175 -0
  374. novacode_cli/prompts/ponytail/hooks/ponytail-instructions.js +98 -0
  375. novacode_cli/prompts/ponytail/skills/ponytail/SKILL.md +52 -0
  376. novacode_cli/prompts/ponytail/skills/ponytail-audit/SKILL.md +103 -0
  377. novacode_cli/prompts/ponytail/skills/ponytail-debt/SKILL.md +44 -0
  378. novacode_cli/prompts/ponytail/skills/ponytail-gain/SKILL.md +52 -0
  379. novacode_cli/prompts/ponytail/skills/ponytail-help/SKILL.md +76 -0
  380. novacode_cli/prompts/ponytail/skills/ponytail-review/SKILL.md +101 -0
  381. novacode_cli/prompts/prompt_evolution.jinja +27 -0
  382. novacode_cli/prompts/ralph_iteration.jinja +75 -0
  383. novacode_cli/prompts/refine_gate.jinja +31 -0
  384. novacode_cli/prompts/refine_plan.jinja +64 -0
  385. novacode_cli/prompts/refine_review.jinja +34 -0
  386. novacode_cli/prompts/research_delivery.jinja +9 -0
  387. novacode_cli/prompts/research_swarm.jinja +130 -0
  388. novacode_cli/prompts/response_style.jinja +5 -0
  389. novacode_cli/prompts/session_summarization.jinja +86 -0
  390. novacode_cli/prompts/skill_creation.jinja +85 -0
  391. novacode_cli/prompts/subagents/fact_checker.jinja +48 -0
  392. novacode_cli/prompts/subagents/financial_analyst.jinja +50 -0
  393. novacode_cli/prompts/subagents/literature_reviewer.jinja +52 -0
  394. novacode_cli/prompts/subagents/market_analyst.jinja +48 -0
  395. novacode_cli/prompts/subagents/research_synthesizer.jinja +57 -0
  396. novacode_cli/prompts/subagents/technical_researcher.jinja +56 -0
  397. novacode_cli/prompts/subagents/web_researcher.jinja +46 -0
  398. novacode_cli/prompts/summarization.jinja +107 -0
  399. novacode_cli/prompts/summarization_answers.jinja +23 -0
  400. novacode_cli/prompts/summarization_questions.jinja +32 -0
  401. novacode_cli/prompts/vision_caption.jinja +21 -0
  402. novacode_cli/prompts/voice_summary.jinja +65 -0
  403. novacode_cli/prompts/wiki_file.jinja +57 -0
  404. novacode_cli/prompts/wiki_ingest.jinja +87 -0
  405. novacode_cli/recovery.py +383 -0
  406. novacode_cli/remote/__init__.py +45 -0
  407. novacode_cli/remote/approvals.py +282 -0
  408. novacode_cli/remote/async_task_clients.py +97 -0
  409. novacode_cli/remote/async_task_watcher.py +240 -0
  410. novacode_cli/remote/bridge.py +905 -0
  411. novacode_cli/remote/command_menu.py +26 -0
  412. novacode_cli/remote/command_ui.py +243 -0
  413. novacode_cli/remote/config.py +199 -0
  414. novacode_cli/remote/discord_bridge.py +530 -0
  415. novacode_cli/remote/discord_format.py +31 -0
  416. novacode_cli/remote/discord_threads.py +225 -0
  417. novacode_cli/remote/images.py +97 -0
  418. novacode_cli/remote/processor.py +601 -0
  419. novacode_cli/remote/rootapp_bot/bot.cjs +16 -0
  420. novacode_cli/remote/rootapp_bot/package-lock.json +2683 -0
  421. novacode_cli/remote/rootapp_bot/package.json +18 -0
  422. novacode_cli/remote/rootapp_bot/transport.cjs +80 -0
  423. novacode_cli/remote/rootapp_bridge.py +370 -0
  424. novacode_cli/remote/rootapp_setup.py +89 -0
  425. novacode_cli/remote/routing.py +103 -0
  426. novacode_cli/remote/scheduler.py +302 -0
  427. novacode_cli/remote/session.py +164 -0
  428. novacode_cli/remote/status.py +411 -0
  429. novacode_cli/remote/streaming.py +188 -0
  430. novacode_cli/remote/telegram_bridge.py +1118 -0
  431. novacode_cli/remote/telegram_format.py +215 -0
  432. novacode_cli/remote/telegram_hub.py +256 -0
  433. novacode_cli/remote/voice_notes.py +120 -0
  434. novacode_cli/remote/webhook_adapters.py +167 -0
  435. novacode_cli/remote/webhook_server.py +154 -0
  436. novacode_cli/rewind.py +150 -0
  437. novacode_cli/rooms/__init__.py +1 -0
  438. novacode_cli/rooms/bridge.py +86 -0
  439. novacode_cli/rooms/cli.py +231 -0
  440. novacode_cli/rooms/nova.py +227 -0
  441. novacode_cli/rooms/runtime.py +299 -0
  442. novacode_cli/rooms/store.py +538 -0
  443. novacode_cli/rooms/workspace.py +94 -0
  444. novacode_cli/security/__init__.py +29 -0
  445. novacode_cli/security/approval_context.py +45 -0
  446. novacode_cli/security/delegated_approval.py +68 -0
  447. novacode_cli/security/local_http.py +85 -0
  448. novacode_cli/security/middleware.py +87 -0
  449. novacode_cli/security/paths.py +35 -0
  450. novacode_cli/security/policy.py +535 -0
  451. novacode_cli/security/policy_writer.py +90 -0
  452. novacode_cli/security/prompt_injection.py +203 -0
  453. novacode_cli/security/remember.py +81 -0
  454. novacode_cli/security/rule_synthesis.py +116 -0
  455. novacode_cli/security/secret_files.py +61 -0
  456. novacode_cli/security/session_allow.py +114 -0
  457. novacode_cli/security/unicode_security.py +516 -0
  458. novacode_cli/security/validator.py +173 -0
  459. novacode_cli/server/__init__.py +9 -0
  460. novacode_cli/server/app.py +632 -0
  461. novacode_cli/server/event_adapter.py +189 -0
  462. novacode_cli/server/session_manager.py +90 -0
  463. novacode_cli/server_main.py +64 -0
  464. novacode_cli/server_runner/dev_server.py +530 -0
  465. novacode_cli/server_runner/test_runner.py +470 -0
  466. novacode_cli/session/__init__.py +1 -0
  467. novacode_cli/session/adapters/__init__.py +240 -0
  468. novacode_cli/session/adapters/claude_code.py +102 -0
  469. novacode_cli/session/adapters/codex.py +77 -0
  470. novacode_cli/session/adapters/harness_native.py +75 -0
  471. novacode_cli/session/checkpoint_coordinator.py +448 -0
  472. novacode_cli/session/context_restore.py +25 -0
  473. novacode_cli/session/history_export.py +228 -0
  474. novacode_cli/session/history_tree.py +174 -0
  475. novacode_cli/session/imported_context.py +267 -0
  476. novacode_cli/session/runtime_identity.py +32 -0
  477. novacode_cli/session/session_persistence.py +1168 -0
  478. novacode_cli/session/session_prompt_builder.py +279 -0
  479. novacode_cli/session/session_restore.py +351 -0
  480. novacode_cli/session/session_summarization.py +20 -0
  481. novacode_cli/session/transcript_journal.py +153 -0
  482. novacode_cli/sessions/__init__.py +11 -0
  483. novacode_cli/sessions/gateway.py +84 -0
  484. novacode_cli/sessions/launch.py +51 -0
  485. novacode_cli/sessions/lease.py +213 -0
  486. novacode_cli/sessions/messaging.py +255 -0
  487. novacode_cli/sessions/protocol.py +216 -0
  488. novacode_cli/sessions/resume.py +31 -0
  489. novacode_cli/sessions/supervisor.py +498 -0
  490. novacode_cli/sessions/tasks.py +73 -0
  491. novacode_cli/sessions/tui_messaging.py +413 -0
  492. novacode_cli/sessions/worker.py +652 -0
  493. novacode_cli/sessions/worktree.py +262 -0
  494. novacode_cli/shell/__init__.py +27 -0
  495. novacode_cli/shell/jobs.py +475 -0
  496. novacode_cli/shell/middleware.py +1568 -0
  497. novacode_cli/shell/os_sandbox.py +205 -0
  498. novacode_cli/shell/output.py +181 -0
  499. novacode_cli/shell/patterns.py +275 -0
  500. novacode_cli/shell/processes.py +87 -0
  501. novacode_cli/shell/utils.py +236 -0
  502. novacode_cli/skills/__init__.py +26 -0
  503. novacode_cli/skills/builtin/handoff/SKILL.md +15 -0
  504. novacode_cli/skills/curation_middleware.py +69 -0
  505. novacode_cli/skills/executable.py +161 -0
  506. novacode_cli/skills/libraries.py +102 -0
  507. novacode_cli/skills/load.py +248 -0
  508. novacode_cli/skills/prune.py +215 -0
  509. novacode_cli/skills/refreshing_middleware.py +896 -0
  510. novacode_cli/skills/retrieval.py +248 -0
  511. novacode_cli/skills/runtime.py +118 -0
  512. novacode_cli/skills/schema.py +254 -0
  513. novacode_cli/skills/skill_creation.py +1549 -0
  514. novacode_cli/skills/skill_lock.py +75 -0
  515. novacode_cli/skills/skill_system_prompt.py +16 -0
  516. novacode_cli/skills/skills_prefs.py +126 -0
  517. novacode_cli/skills/tool_resolver.py +31 -0
  518. novacode_cli/skills/upstream.py +50 -0
  519. novacode_cli/skills/versioning.py +291 -0
  520. novacode_cli/states/Session.py +847 -0
  521. novacode_cli/states/__init__.py +31 -0
  522. novacode_cli/states/slices/__init__.py +17 -0
  523. novacode_cli/states/slices/agent_runtime.py +249 -0
  524. novacode_cli/states/slices/background_tasks.py +21 -0
  525. novacode_cli/states/slices/notifications.py +74 -0
  526. novacode_cli/states/slices/remote_bridge.py +56 -0
  527. novacode_cli/states/slices/ui_settings.py +49 -0
  528. novacode_cli/states/slices/wiki.py +25 -0
  529. novacode_cli/teammates/__init__.py +1 -0
  530. novacode_cli/teammates/controller.py +557 -0
  531. novacode_cli/teammates/graph.py +104 -0
  532. novacode_cli/teammates/registry.py +175 -0
  533. novacode_cli/teammates/service.py +254 -0
  534. novacode_cli/teammates/worker.py +19 -0
  535. novacode_cli/teams/__init__.py +1 -0
  536. novacode_cli/teams/adapters/__init__.py +1 -0
  537. novacode_cli/teams/adapters/container.py +316 -0
  538. novacode_cli/teams/adapters/inprocess.py +206 -0
  539. novacode_cli/teams/adapters/process.py +364 -0
  540. novacode_cli/teams/bootstrap.py +246 -0
  541. novacode_cli/teams/container_git.py +67 -0
  542. novacode_cli/teams/context.py +34 -0
  543. novacode_cli/teams/coordinator.py +815 -0
  544. novacode_cli/teams/gateway.py +200 -0
  545. novacode_cli/teams/host.py +64 -0
  546. novacode_cli/teams/legacy.py +52 -0
  547. novacode_cli/teams/middleware.py +176 -0
  548. novacode_cli/teams/models.py +104 -0
  549. novacode_cli/teams/output.py +47 -0
  550. novacode_cli/teams/protocol.py +50 -0
  551. novacode_cli/teams/recovery.py +80 -0
  552. novacode_cli/teams/scheduler.py +44 -0
  553. novacode_cli/teams/store.py +631 -0
  554. novacode_cli/teams/worker.py +251 -0
  555. novacode_cli/teams/workspace.py +100 -0
  556. novacode_cli/token_utils.py +256 -0
  557. novacode_cli/tools/__init__.py +104 -0
  558. novacode_cli/tools/_shared.py +97 -0
  559. novacode_cli/tools/_types.py +24 -0
  560. novacode_cli/tools/artifact_tools.py +137 -0
  561. novacode_cli/tools/code_search_tools.py +330 -0
  562. novacode_cli/tools/computer_tools.py +77 -0
  563. novacode_cli/tools/cron_tools.py +65 -0
  564. novacode_cli/tools/daemon_tool.py +290 -0
  565. novacode_cli/tools/desktop_tools.py +161 -0
  566. novacode_cli/tools/fetch_tools.py +530 -0
  567. novacode_cli/tools/http_tools.py +279 -0
  568. novacode_cli/tools/inspection_tools.py +107 -0
  569. novacode_cli/tools/job_tools.py +143 -0
  570. novacode_cli/tools/knowledge_tools.py +427 -0
  571. novacode_cli/tools/memory_tools.py +461 -0
  572. novacode_cli/tools/memory_wiki_tools.py +40 -0
  573. novacode_cli/tools/notebook_tools.py +238 -0
  574. novacode_cli/tools/oracle_tool.py +83 -0
  575. novacode_cli/tools/plan_archive_tools.py +80 -0
  576. novacode_cli/tools/plan_mode_tools.py +315 -0
  577. novacode_cli/tools/python_kernel_loop.py +165 -0
  578. novacode_cli/tools/python_kernel_tool.py +222 -0
  579. novacode_cli/tools/reflection_tools.py +34 -0
  580. novacode_cli/tools/scraper_tools.py +377 -0
  581. novacode_cli/tools/skill_tools.py +275 -0
  582. novacode_cli/tools/speak_tool.py +51 -0
  583. novacode_cli/tools/store_memory_tools.py +227 -0
  584. novacode_cli/tools/ui_tools.py +62 -0
  585. novacode_cli/tools/waiter_tools.py +408 -0
  586. novacode_cli/tools/web_tools.py +349 -0
  587. novacode_cli/tools/wiki_tools.py +152 -0
  588. novacode_cli/tools/worktree_tools.py +219 -0
  589. novacode_cli/tracking/__init__.py +10 -0
  590. novacode_cli/tracking/file_tracker.py +826 -0
  591. novacode_cli/tracking/local_metrics.py +170 -0
  592. novacode_cli/tracking/loop_guard.py +507 -0
  593. novacode_cli/tracking/request_metrics.py +60 -0
  594. novacode_cli/tracking/tracing.py +692 -0
  595. novacode_cli/tracking/usage_tree.py +677 -0
  596. novacode_cli/tracking/workspace_anchoring.py +231 -0
  597. novacode_cli/tui/__init__.py +11 -0
  598. novacode_cli/tui/animation_rate.py +9 -0
  599. novacode_cli/tui/animations.py +332 -0
  600. novacode_cli/tui/app.py +19315 -0
  601. novacode_cli/tui/auth_display.py +87 -0
  602. novacode_cli/tui/auth_screens.py +826 -0
  603. novacode_cli/tui/avatar.py +294 -0
  604. novacode_cli/tui/avatar_frames.py +21 -0
  605. novacode_cli/tui/background_tasks.py +35 -0
  606. novacode_cli/tui/browser_settings_screen.py +70 -0
  607. novacode_cli/tui/caveman_screen.py +185 -0
  608. novacode_cli/tui/council_settings_screen.py +182 -0
  609. novacode_cli/tui/council_view.py +535 -0
  610. novacode_cli/tui/decision_settings.py +268 -0
  611. novacode_cli/tui/diff_preview.py +199 -0
  612. novacode_cli/tui/gc_tuning.py +63 -0
  613. novacode_cli/tui/handoff_screen.py +206 -0
  614. novacode_cli/tui/harness.py +244 -0
  615. novacode_cli/tui/history_tree_screen.py +179 -0
  616. novacode_cli/tui/home.py +328 -0
  617. novacode_cli/tui/home_scene.py +94 -0
  618. novacode_cli/tui/icons.py +81 -0
  619. novacode_cli/tui/interface_styles.tcss +327 -0
  620. novacode_cli/tui/matrix3.py +108 -0
  621. novacode_cli/tui/mcp_tools_screen.py +131 -0
  622. novacode_cli/tui/motion.py +178 -0
  623. novacode_cli/tui/output_body.py +136 -0
  624. novacode_cli/tui/output_buffer.py +59 -0
  625. novacode_cli/tui/palette.py +334 -0
  626. novacode_cli/tui/pickers.py +237 -0
  627. novacode_cli/tui/prime_panel.py +506 -0
  628. novacode_cli/tui/provider_signin.py +213 -0
  629. novacode_cli/tui/pty_shell.py +159 -0
  630. novacode_cli/tui/reference_screens.py +444 -0
  631. novacode_cli/tui/resume_picker.py +346 -0
  632. novacode_cli/tui/rooms.py +597 -0
  633. novacode_cli/tui/screens.py +7088 -0
  634. novacode_cli/tui/session_import.py +333 -0
  635. novacode_cli/tui/session_pane.py +242 -0
  636. novacode_cli/tui/session_tab.py +42 -0
  637. novacode_cli/tui/session_transcript.py +118 -0
  638. novacode_cli/tui/shell_output.py +7 -0
  639. novacode_cli/tui/stall_watch.py +174 -0
  640. novacode_cli/tui/statusline.py +40 -0
  641. novacode_cli/tui/statusline_screen.py +91 -0
  642. novacode_cli/tui/teammate_updates.py +162 -0
  643. novacode_cli/tui/teammates.py +548 -0
  644. novacode_cli/tui/terminal.py +289 -0
  645. novacode_cli/tui/terminal_view.py +235 -0
  646. novacode_cli/tui/themes.py +118 -0
  647. novacode_cli/tui/update_notice.py +84 -0
  648. novacode_cli/tui/voice_downloads.py +113 -0
  649. novacode_cli/tui/widgets.py +2038 -0
  650. novacode_cli/tui/windows_input.py +38 -0
  651. novacode_cli/tui/workspace_approval.py +165 -0
  652. novacode_cli/ui/council_web.py +113 -0
  653. novacode_cli/ui/council_workspace.py +72 -0
  654. novacode_cli/ui/cowork_web.py +41 -0
  655. novacode_cli/ui/diff_highlight.py +160 -0
  656. novacode_cli/ui/execution.py +1396 -0
  657. novacode_cli/ui/exit_summary.py +65 -0
  658. novacode_cli/ui/hitl_approval.py +196 -0
  659. novacode_cli/ui/input_preparation.py +2 -0
  660. novacode_cli/ui/interrupt_handlers.py +215 -0
  661. novacode_cli/ui/question_prompt.py +151 -0
  662. novacode_cli/ui/status_phrases.py +157 -0
  663. novacode_cli/ui/stream_preview.py +66 -0
  664. novacode_cli/ui/streaming.py +2 -0
  665. novacode_cli/ui/subagent_tracking.py +2 -0
  666. novacode_cli/ui/ui_elements.py +1585 -0
  667. novacode_cli/ui/web_workspace.py +244 -0
  668. novacode_cli/ui_events.py +299 -0
  669. novacode_cli/update_ui.py +243 -0
  670. novacode_cli/updates.py +489 -0
  671. novacode_cli/utils/backend_patches.py +399 -0
  672. novacode_cli/utils/backend_paths.py +467 -0
  673. novacode_cli/utils/lazy_providers.py +51 -0
  674. novacode_cli/utils/model_info.py +223 -0
  675. novacode_cli/utils/pdf_extraction.py +183 -0
  676. novacode_cli/utils/startup.py +29 -0
  677. novacode_cli/utils/tasks.py +42 -0
  678. novacode_cli/utils/windows.py +9 -0
  679. novacode_cli/vixie/hook_adapter.py +127 -0
  680. novacode_cli/vixie/server.py +375 -0
  681. novacode_cli/wiki/__init__.py +15 -0
  682. novacode_cli/wiki/ask.py +56 -0
  683. novacode_cli/wiki/file.py +71 -0
  684. novacode_cli/wiki/ingest.py +184 -0
  685. novacode_cli/wiki/manager.py +261 -0
  686. novacode_cli-1.2.0.dist-info/METADATA +271 -0
  687. novacode_cli-1.2.0.dist-info/RECORD +691 -0
  688. novacode_cli-1.2.0.dist-info/WHEEL +5 -0
  689. novacode_cli-1.2.0.dist-info/entry_points.txt +2 -0
  690. novacode_cli-1.2.0.dist-info/licenses/LICENSE +21 -0
  691. novacode_cli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,119 @@
1
+ """``/cron`` command — manage scheduled (heartbeat) tasks (Enhancement 3).
2
+
3
+ Subcommands::
4
+
5
+ /cron list scheduled jobs
6
+ /cron list
7
+ /cron add "0 9 * * *" "review the project and summarise what's left"
8
+ /cron remove <job_id>
9
+ /cron now "check if CI is broken" fire a one-off task immediately
10
+
11
+ Jobs are fired by :class:`~novacode_cli.remote.scheduler.CronScheduler`, which
12
+ puts them on the same queue the remote bridges use, so they run like any prompt.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import shlex
18
+ from typing import TYPE_CHECKING
19
+
20
+ if TYPE_CHECKING:
21
+ from rich.console import Console
22
+
23
+ from novacode_cli.remote.scheduler import CronScheduler
24
+ from novacode_cli.states.Session import SessionState
25
+
26
+ _ADD_PARTS = 2
27
+
28
+
29
+ async def _ensure_scheduler(session_state: SessionState) -> CronScheduler:
30
+ """Return the session's scheduler, creating + starting it on first use."""
31
+ scheduler = getattr(session_state, "_cron_scheduler", None)
32
+ if scheduler is None:
33
+ from novacode_cli.memory.store import get_durable_store
34
+ from novacode_cli.remote.scheduler import CronScheduler
35
+
36
+ queue = getattr(session_state, "_remote_message_queue", None)
37
+ if queue is None:
38
+ import asyncio
39
+
40
+ queue = asyncio.Queue(maxsize=100)
41
+ session_state._remote_message_queue = queue
42
+ scheduler = CronScheduler(queue, store=get_durable_store())
43
+ session_state._cron_scheduler = scheduler
44
+ if not scheduler.running:
45
+ await scheduler.start()
46
+ return scheduler
47
+
48
+
49
+ async def handle_cron_command( # noqa: PLR0911 — command dispatcher, one return per subcommand
50
+ cmd_args: str | None, session_state: SessionState, console: Console
51
+ ) -> bool:
52
+ """Dispatch a ``/cron`` subcommand. Returns ``True`` (command handled)."""
53
+ scheduler = await _ensure_scheduler(session_state)
54
+ args = (cmd_args or "").strip()
55
+
56
+ if not args or args.split()[0] == "list":
57
+ _print_jobs(scheduler, console)
58
+ return True
59
+
60
+ try:
61
+ tokens = shlex.split(args)
62
+ except ValueError as exc:
63
+ console.print(f"[red]Could not parse arguments: {exc}[/red]")
64
+ return True
65
+
66
+ action = tokens[0]
67
+ rest = tokens[1:]
68
+
69
+ if action == "add":
70
+ if len(rest) < _ADD_PARTS:
71
+ console.print('[yellow]Usage:[/yellow] /cron add "<cron expr>" "<task>"')
72
+ return True
73
+ cron_expr, task = rest[0], rest[1]
74
+ try:
75
+ job_id = await scheduler.add_job(cron_expr, task)
76
+ except ValueError as exc:
77
+ console.print(f"[red]Invalid cron expression:[/red] {exc}")
78
+ return True
79
+ console.print(
80
+ f" [green]✓[/green] Scheduled job [cyan]{job_id}[/cyan] "
81
+ f"([dim]{cron_expr}[/dim]): {task}"
82
+ )
83
+ return True
84
+
85
+ if action == "remove":
86
+ if not rest:
87
+ console.print("[yellow]Usage:[/yellow] /cron remove <job_id>")
88
+ return True
89
+ removed = await scheduler.remove_job(rest[0])
90
+ if removed:
91
+ console.print(f" [green]✓[/green] Removed job [cyan]{rest[0]}[/cyan]")
92
+ else:
93
+ console.print(f" [dim]No job with id {rest[0]}[/dim]")
94
+ return True
95
+
96
+ if action == "now":
97
+ if not rest:
98
+ console.print('[yellow]Usage:[/yellow] /cron now "<task>"')
99
+ return True
100
+ await scheduler.fire_now(rest[0])
101
+ console.print(f" [green]✓[/green] Fired one-off task: {rest[0]}")
102
+ return True
103
+
104
+ console.print(f"[yellow]Unknown /cron subcommand:[/yellow] {action}")
105
+ return True
106
+
107
+
108
+ def _print_jobs(scheduler: CronScheduler, console: Console) -> None:
109
+ """Render the active job list."""
110
+ jobs = scheduler.list_jobs()
111
+ if not jobs:
112
+ console.print('[dim]No scheduled jobs. Add one with /cron add "0 9 * * *" "task".[/dim]')
113
+ return
114
+ console.print("[bold]Scheduled jobs[/bold]")
115
+ for job in jobs:
116
+ console.print(
117
+ f" [cyan]{job.get('job_id', '?')}[/cyan] "
118
+ f"[dim]{job.get('cron_expr', '?')}[/dim] — {job.get('task', '')}"
119
+ )
@@ -0,0 +1,203 @@
1
+ """Handler for the /dream command - memory consolidation.
2
+
3
+ This module provides the handle_dream_command function that runs
4
+ a memory consolidation pass to organize and clean up memory files.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ from collections.abc import Callable
11
+ from pathlib import Path
12
+ from typing import TYPE_CHECKING
13
+
14
+ from novacode_cli.config.config import COLORS, console
15
+ from novacode_cli.prompts import render_template
16
+
17
+ if TYPE_CHECKING:
18
+ from novacode_cli.commands import CommandContext, CommandRegistry
19
+
20
+ _CONTEXT_TOPIC_PREVIEW = 8
21
+ _STATUS_TOPIC_PREVIEW = 6
22
+
23
+ # A UI sink: called with one Rich-markup string per line ("" = blank line).
24
+ EmitFn = Callable[[str], None]
25
+
26
+
27
+ def _console_emit(message: str = "") -> None:
28
+ """Default emitter (CLI): render a Rich-markup string to the console."""
29
+ console.print(message)
30
+
31
+
32
+ async def handle_dream_command(
33
+ session_state: object,
34
+ assistant_id: str | None = None,
35
+ agent_dir: Path | None = None,
36
+ emit: EmitFn | None = None,
37
+ ) -> bool | str:
38
+ """Handle the /dream command - run memory consolidation.
39
+
40
+ Performs a multi-phase memory consolidation pass over the agent's *real*
41
+ memory directory (``~/.nova/agents/{assistant_id}/``): agent.md (user model +
42
+ preferences) and the topic files under ``memories/`` (indexed by INDEX.md).
43
+
44
+ Returns the consolidation prompt (a ``str``) for the caller to stream to the
45
+ agent when memory exists, or ``True`` (handled, nothing to do) when it does
46
+ not. The agent acts on the **virtual** ``/memories/`` route — which maps to
47
+ this directory — so its read/edit tools work regardless of OS path form.
48
+
49
+ Args:
50
+ session_state: Current session state.
51
+ assistant_id: The active agent id (defaults to the main agent).
52
+ agent_dir: Optional explicit agent directory (overrides the resolved one).
53
+ emit: Optional renderer-specific status sink.
54
+
55
+ Returns:
56
+ The consolidation prompt string, or True if there is nothing to consolidate.
57
+ """
58
+ from novacode_cli.config.config import MAIN_AGENT_ID, settings
59
+
60
+ if emit is None:
61
+ emit = _console_emit
62
+ if getattr(session_state, "plan_mode_enabled", False):
63
+ emit("[yellow]Exit plan mode before running memory wiki maintenance.[/yellow]")
64
+ return True
65
+
66
+ # Resolve the REAL memory directory (the one Nova actually writes to).
67
+ if agent_dir is None:
68
+ try:
69
+ agent_dir = settings.get_agent_dir(assistant_id or MAIN_AGENT_ID)
70
+ except Exception: # noqa: BLE001 - bad agent id, fall back to main
71
+ agent_dir = settings.get_agent_dir(MAIN_AGENT_ID)
72
+
73
+ # Nova's semantic surface: agent.md (user model + prefs) + topic files
74
+ # under memories/ (indexed by INDEX.md).
75
+ tier_files = {
76
+ "agent.md": "user model, preferences & general notes",
77
+ "HABITS.md": "stable operating checklist; incident lessons belong in topic memory",
78
+ }
79
+ memories_dir = agent_dir / "memories"
80
+
81
+ present = [(name, role) for name, role in tier_files.items() if (agent_dir / name).exists()]
82
+ topic_files = sorted(
83
+ [
84
+ *memories_dir.glob("*.md"),
85
+ *(memories_dir / "projects").glob("*/*.md"),
86
+ ]
87
+ )
88
+
89
+ if not present and not topic_files:
90
+ emit("")
91
+ emit("[yellow]No memory files found yet.[/yellow]")
92
+ emit(f"[dim]Looked in: {agent_dir}[/dim]")
93
+ emit(
94
+ "[dim]Ask me to 'remember this' during a session and the memory tiers"
95
+ " fill in; then /dream consolidates them.[/dim]"
96
+ )
97
+ emit("")
98
+ return True
99
+
100
+ transcripts_dir = agent_dir / "transcripts"
101
+
102
+ # Describe Nova's concrete memory layout so the agent consolidates the right
103
+ # files. Use the VIRTUAL /memories/ route — the agent's file tools resolve it
104
+ # to the real agent dir; passing the raw OS path would be rejected by the
105
+ # virtual-mode filesystem backend.
106
+ lines = ["These memory files exist (operate on them under `/memories/`):"]
107
+ for name, role in present:
108
+ lines.append(f"- `/memories/{name}` — {role}")
109
+ if topic_files:
110
+ lines.append(
111
+ f"- `/memories/memories/` — {len(topic_files)} topic file(s): "
112
+ + ", ".join(f.name for f in topic_files[:_CONTEXT_TOPIC_PREVIEW])
113
+ + ("…" if len(topic_files) > _CONTEXT_TOPIC_PREVIEW else "")
114
+ )
115
+ memory_dir_context = "\n".join(lines)
116
+
117
+ # The wiki has one canonical home, including on its first maintenance pass.
118
+ index_file = "/memories/memories/INDEX.md"
119
+
120
+ from novacode_cli.memory.wiki import maintain_wiki, snapshot_wiki
121
+
122
+ try:
123
+ wiki = await asyncio.to_thread(maintain_wiki, agent_dir)
124
+ backup = wiki["backup"] or str(await asyncio.to_thread(snapshot_wiki, agent_dir))
125
+ except (OSError, ValueError, RuntimeError) as exc:
126
+ emit(f"[yellow]Memory maintenance stopped: {exc}[/yellow]")
127
+ return True
128
+
129
+ emit("")
130
+ emit(f"[dim]Wiki: {wiki['topics']} topics; {len(wiki['broken_links'])} unresolved links.[/dim]")
131
+ emit(f"[bold {COLORS['primary']}]💭 Memory Consolidation (Dream)[/bold {COLORS['primary']}]")
132
+ for name, role in present:
133
+ emit(f" [cyan]{name}[/cyan] [dim]— {role}[/dim]")
134
+ if topic_files:
135
+ names = ", ".join(f.name for f in topic_files[:_STATUS_TOPIC_PREVIEW]) + (
136
+ "…" if len(topic_files) > _STATUS_TOPIC_PREVIEW else ""
137
+ )
138
+ emit(f" [cyan]memories/[/cyan] [dim]— {len(topic_files)} topic file(s): {names}[/dim]")
139
+ emit("")
140
+
141
+ return render_template(
142
+ "memory_consolidation.jinja",
143
+ memory_dir="/memories/",
144
+ memory_dir_context=memory_dir_context,
145
+ index_file=index_file,
146
+ index_max_lines=100,
147
+ transcripts_dir="/memories/transcripts/" if transcripts_dir.exists() else None,
148
+ backup_dir="/memories/" + Path(backup).relative_to(agent_dir).as_posix(),
149
+ )
150
+
151
+
152
+ def get_dream_prompt(
153
+ memory_dir: Path,
154
+ transcripts_dir: Path | None = None,
155
+ index_file: str = "/memories/memories/INDEX.md",
156
+ index_max_lines: int = 100,
157
+ ) -> str:
158
+ """Generate the memory consolidation prompt.
159
+
160
+ Args:
161
+ memory_dir: Path to the memory directory
162
+ transcripts_dir: Optional path to transcripts directory
163
+ index_file: Virtual wiki home path.
164
+ index_max_lines: Maximum lines for the index (default: 100)
165
+
166
+ Returns:
167
+ The rendered prompt string
168
+ """
169
+ return render_template(
170
+ "memory_consolidation.jinja",
171
+ memory_dir=memory_dir.as_posix().rstrip("/") + "/",
172
+ memory_dir_context=f"Memory files are stored in {memory_dir}",
173
+ index_file=index_file,
174
+ index_max_lines=index_max_lines,
175
+ transcripts_dir=str(transcripts_dir) if transcripts_dir else None,
176
+ )
177
+
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # Registry
181
+ # ---------------------------------------------------------------------------
182
+
183
+
184
+ def register_commands(registry: CommandRegistry) -> None:
185
+ """Register the shared command handler for every renderer."""
186
+
187
+ async def _handle(ctx: CommandContext) -> bool:
188
+ result = await handle_dream_command(ctx.session_state, ctx.assistant_id)
189
+ # When memory exists, the handler returns a consolidation prompt to run.
190
+ if isinstance(result, str) and result.strip() and ctx.agent is not None:
191
+ from novacode_cli.ui.execution import execute_task
192
+
193
+ console.print("[dim]💭 Dreaming over memories…[/dim]")
194
+ await execute_task(
195
+ result,
196
+ ctx.agent,
197
+ ctx.assistant_id,
198
+ ctx.session_state,
199
+ ctx.token_tracker,
200
+ )
201
+ return True
202
+
203
+ registry.register("dream", _handle)
@@ -0,0 +1,65 @@
1
+ """Handler for the /effort command to manage reasoning effort configuration."""
2
+
3
+ from novacode_cli.commands import CommandContext
4
+ from novacode_cli.config.config import COLORS, console
5
+ from novacode_cli.config.nova_config import NovaConfig
6
+ from novacode_cli.config.model_create import create_model
7
+
8
+ async def handle_effort_command(ctx: CommandContext) -> bool:
9
+ """Handle the /effort command."""
10
+ args = ctx.cmd_args
11
+ nova_config = NovaConfig()
12
+ current_effort = nova_config.get("reasoning_effort", "off")
13
+
14
+ if not args:
15
+ console.print()
16
+ console.print(f"[bold]Reasoning Effort Configuration[/bold]", style=COLORS["primary"])
17
+ console.print(f"Current reasoning effort: [bold cyan]{current_effort}[/bold cyan]")
18
+ console.print()
19
+ console.print("[bold]Usage:[/bold]")
20
+ console.print(" /effort <low|medium|high|off> - Set the reasoning effort level")
21
+ console.print()
22
+ console.print("[bold]Supported Providers:[/bold]")
23
+ console.print(" • [bold]OpenAI[/bold] (o1, o3-mini models): supports low, medium, high")
24
+ console.print(" • [bold]Google Gemini[/bold] (Gemini 2.5/3 models): supports low, medium, high")
25
+ console.print(" • [bold]Anthropic Claude[/bold] (Claude 3.7 Sonnet): maps to token budget")
26
+ console.print(" • [bold]Ollama[/bold]: Local models (like DeepSeek-R1) generate reasoning automatically")
27
+ console.print()
28
+ return True
29
+
30
+ val = args.strip().lower()
31
+ if val not in ("low", "medium", "high", "off", "none", "default"):
32
+ console.print(f"[red]Error: Invalid effort level '{args}'.[/red]")
33
+ console.print("Choose one of: low, medium, high, off")
34
+ return True
35
+
36
+ # Normalize none / default to off
37
+ if val in ("none", "default"):
38
+ val = "off"
39
+
40
+ # Save configuration
41
+ nova_config.set("reasoning_effort", val)
42
+ console.print()
43
+ console.print(f"[green]✓ Reasoning effort set to '{val}' and saved to config.[/green]")
44
+
45
+ # Trigger hot-swap
46
+ session_state = ctx.session_state
47
+ if session_state is not None:
48
+ try:
49
+ new_model = create_model()
50
+ await session_state.switch_model(new_model)
51
+ console.print("[green]✓ Model recreated with new reasoning effort dynamically![/green]")
52
+ except Exception as e:
53
+ console.print(f"[yellow]⚠ Could not recreate model dynamically: {e}[/yellow]")
54
+ console.print("[dim]The change will take effect on next model switch or restart.[/dim]")
55
+ else:
56
+ console.print("[dim]The change will take effect on restart.[/dim]")
57
+
58
+ console.print()
59
+ return True
60
+
61
+ def register_commands(registry) -> None:
62
+ async def _handle(ctx: CommandContext) -> bool:
63
+ return await handle_effort_command(ctx)
64
+
65
+ registry.register("effort", _handle)
@@ -0,0 +1,214 @@
1
+ """Read-only learning activity for /evolution, including labelled legacy history."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from collections.abc import Callable
7
+ from datetime import UTC, datetime
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from rich.markup import escape
11
+
12
+ from novacode_cli.config.config import COLORS, MAIN_AGENT_ID, console
13
+
14
+ if TYPE_CHECKING:
15
+ from novacode_cli.commands import CommandContext, CommandRegistry
16
+
17
+ # A UI sink: called with one Rich-markup string per line ("" = blank line).
18
+ # Mirrors dream_handler so the TUI can render natively instead of printing to
19
+ # the Rich console (which corrupts the Textual display).
20
+ EmitFn = Callable[[str], None]
21
+
22
+ LEARNING_STATUSES = (
23
+ "candidate",
24
+ "approved",
25
+ "applied",
26
+ "rejected",
27
+ "rollback_pending",
28
+ "rolled_back",
29
+ "failed",
30
+ )
31
+
32
+
33
+ def evolution_summary(counters: dict[str, int]) -> str:
34
+ """Describe settings and current ledger state without claiming proposals are applied."""
35
+ enabled = "on" if counters.get("learning_enabled") else "off"
36
+ automatic = "on" if counters.get("auto_approve") else "off"
37
+ return (
38
+ f"Learning {enabled} · auto approval {automatic}\n"
39
+ f"{counters.get('candidate', 0)} pending review · "
40
+ f"{counters.get('approved', 0)} approved · {counters.get('applied', 0)} applied"
41
+ )
42
+
43
+
44
+ def evolution_label(entry: dict[str, Any]) -> str:
45
+ """Return the current decision state, or an explicitly historical label."""
46
+ status = entry.get("status", "historical")
47
+ if status == "historical":
48
+ kind = {"unlock": "unlock", "levelup": "level-up"}.get(
49
+ str(entry.get("kind", "")), "activity"
50
+ )
51
+ return f"Historical {kind} (application unverified)"
52
+ return {
53
+ "candidate": "Pending review",
54
+ "approved": "Approved; awaiting application",
55
+ "applied": "Applied",
56
+ "rejected": "Rejected",
57
+ "rollback_pending": "Rollback pending",
58
+ "rolled_back": "Rolled back",
59
+ "failed": "Application failed",
60
+ }.get(status, str(status))
61
+
62
+
63
+ def evolution_detail(entry: dict[str, Any]) -> str:
64
+ """Build literal report text shared by the console and native dashboard."""
65
+ summary = str(entry.get("task_summary") or "No summary recorded.")
66
+ if entry.get("source") != "ledger":
67
+ return f"{evolution_label(entry)}\n{summary}"
68
+ return (
69
+ f"{entry['reference']} · {entry['kind']} · {evolution_label(entry)}\n"
70
+ f"Scope: {entry['scope']} · Project: {entry['project'] or 'shared'}\n"
71
+ "Use /learning review to inspect and manage proposals.\n\n" + summary
72
+ )
73
+
74
+
75
+ def _learning_activity(
76
+ agent_id: str = MAIN_AGENT_ID,
77
+ ) -> tuple[dict[str, int], list[dict[str, Any]]]:
78
+ """Read the current learning ledger and settings; never approve or apply anything."""
79
+ from novacode_cli.config.config import Settings
80
+ from novacode_cli.config.nova_config import NovaConfig
81
+ from novacode_cli.hermes.ledger import LearningLedger
82
+
83
+ cfg = NovaConfig()
84
+ path = Settings.from_environment().get_agent_dir(agent_id) / "learning.sqlite3"
85
+ counters = dict.fromkeys(LEARNING_STATUSES, 0)
86
+ counters.update(
87
+ learning_enabled=int(cfg.get_learning_enabled()),
88
+ auto_approve=int(cfg.get_learning_auto_approve()),
89
+ )
90
+ entries = []
91
+ for record, timestamp in LearningLedger.read_activity(path):
92
+ counters[record.status] = counters.get(record.status, 0) + 1
93
+ entries.append(
94
+ {
95
+ "source": "ledger",
96
+ "status": record.status,
97
+ "kind": record.kind,
98
+ "skill": record.target or record.kind.replace("_", " ").title(),
99
+ "reference": record.reference,
100
+ "ts": timestamp,
101
+ "task_summary": record.content[:8000],
102
+ "scope": record.scope,
103
+ "project": record.project,
104
+ "origin": record.origin,
105
+ }
106
+ )
107
+ return counters, entries
108
+
109
+
110
+ def _console_emit(message: str = "") -> None:
111
+ """Default emitter (CLI/REPL): render a Rich-markup string to the console."""
112
+ console.print(message)
113
+
114
+
115
+ async def _load_evolution(
116
+ *, strict: bool = False, agent_id: str = MAIN_AGENT_ID
117
+ ) -> tuple[dict[str, int], list[dict[str, Any]]]:
118
+ """Return current proposals followed by explicitly historical evolution records."""
119
+ from novacode_cli.memory.store import get_durable_store
120
+
121
+ store = await asyncio.to_thread(get_durable_store)
122
+
123
+ try:
124
+ counters, current = await asyncio.to_thread(_learning_activity, agent_id)
125
+ except Exception:
126
+ if strict:
127
+ raise
128
+ counters, current = {}, []
129
+ counters.update(unlocked=0, leveled=0)
130
+ try:
131
+ entry = await store.aget(("nova", "meta"), "evolution")
132
+ if entry and isinstance(entry.value, dict):
133
+ counters.update(
134
+ unlocked=int(entry.value.get("unlocked", 0)),
135
+ leveled=int(entry.value.get("leveled", 0)),
136
+ )
137
+ except Exception:
138
+ if strict:
139
+ raise
140
+
141
+ entries: list[dict[str, Any]] = []
142
+ try:
143
+ results = await store.asearch(("nova", "evolution_log"), limit=1000)
144
+ for item in results or []:
145
+ value = getattr(item, "value", None)
146
+ if isinstance(value, dict):
147
+ entries.append({**value, "source": "legacy", "status": "historical"})
148
+ except Exception:
149
+ if strict:
150
+ raise
151
+ entries.sort(key=lambda e: e.get("ts", 0.0), reverse=True)
152
+ return counters, [*current, *entries]
153
+
154
+
155
+ async def handle_evolution_command(
156
+ limit: int = 15,
157
+ emit: EmitFn | None = None,
158
+ *,
159
+ agent_id: str = MAIN_AGENT_ID,
160
+ ) -> bool:
161
+ """Render the evolution summary + recent unlocks/level-ups.
162
+
163
+ Writes through ``emit`` (one Rich-markup line at a time) so the TUI can
164
+ render natively; defaults to the Rich console for the CLI/REPL.
165
+ """
166
+ if emit is None:
167
+ emit = _console_emit
168
+
169
+ try:
170
+ counters, entries = await _load_evolution(strict=True, agent_id=agent_id)
171
+ except Exception as error: # noqa: BLE001 — report storage failure without failing the task
172
+ emit(f"Evolution history unavailable: {escape(str(error))}")
173
+ return True
174
+
175
+ emit("")
176
+ emit(f"[bold {COLORS['primary']}]🧬 Self-Evolution[/bold {COLORS['primary']}]")
177
+ emit(escape(evolution_summary(counters)))
178
+
179
+ if not entries:
180
+ emit(
181
+ " [dim]No activity yet. /learning on enables task-end reviews; "
182
+ "/learning review lists proposals before application.[/dim]"
183
+ )
184
+ emit("")
185
+ return True
186
+
187
+ emit("")
188
+ for e in entries[:limit]:
189
+ icon = "•"
190
+ verb = evolution_label(e)
191
+ skill = e.get("skill", "?")
192
+ when = ""
193
+ ts = e.get("ts")
194
+ if isinstance(ts, (int, float)) and ts > 0:
195
+ when = datetime.fromtimestamp(ts, UTC).strftime("%Y-%m-%d %H:%M")
196
+ summary = (e.get("task_summary") or "").strip()
197
+ line = f" {icon} [bold]{escape(str(skill))}[/bold] [dim]{escape(verb)}"
198
+ if when:
199
+ line += f" · {when}"
200
+ line += "[/dim]"
201
+ emit(line)
202
+ if summary:
203
+ emit(escape(evolution_detail(e)))
204
+ emit("")
205
+ return True
206
+
207
+
208
+ def register_commands(registry: CommandRegistry) -> None:
209
+ """Register the read-only evolution report for console transports."""
210
+
211
+ async def _handle(ctx: CommandContext) -> bool:
212
+ return await handle_evolution_command(agent_id=ctx.assistant_id)
213
+
214
+ registry.register("evolution", _handle)