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.
- novacode_cli/__init__.py +29 -0
- novacode_cli/__main__.py +6 -0
- novacode_cli/_lazy_heavy.py +99 -0
- novacode_cli/_version.py +3 -0
- novacode_cli/agent_stream.py +178 -0
- novacode_cli/agents/agent_file.py +250 -0
- novacode_cli/agents/agent_mailbox.py +120 -0
- novacode_cli/agents/approval_decisions.py +146 -0
- novacode_cli/agents/arg_repair.py +196 -0
- novacode_cli/agents/async_agents/__init__.py +6 -0
- novacode_cli/agents/async_agents/_config.py +213 -0
- novacode_cli/agents/async_agents/_mcp_tools.py +192 -0
- novacode_cli/agents/async_agents/_specialist.py +84 -0
- novacode_cli/agents/async_agents/_test_commands.py +89 -0
- novacode_cli/agents/async_agents/_tools.py +432 -0
- novacode_cli/agents/async_agents/general_purpose.py +42 -0
- novacode_cli/agents/async_agents/langgraph.json +9 -0
- novacode_cli/agents/async_agents/research.py +22 -0
- novacode_cli/agents/async_context.py +303 -0
- novacode_cli/agents/async_on_demand.py +52 -0
- novacode_cli/agents/async_task_errors.py +75 -0
- novacode_cli/agents/async_workspace.py +27 -0
- novacode_cli/agents/attribution.py +165 -0
- novacode_cli/agents/browser_mode.py +119 -0
- novacode_cli/agents/btw_agent.py +71 -0
- novacode_cli/agents/caveman.py +352 -0
- novacode_cli/agents/caveman_support.py +370 -0
- novacode_cli/agents/code_mode.py +774 -0
- novacode_cli/agents/code_runtime.py +160 -0
- novacode_cli/agents/context_compaction.py +1339 -0
- novacode_cli/agents/core_agent.py +2710 -0
- novacode_cli/agents/default_subagents/__init__.py +24 -0
- novacode_cli/agents/default_subagents/async_subagents.py +296 -0
- novacode_cli/agents/default_subagents/builtin/browser-automation-agent.md +110 -0
- novacode_cli/agents/default_subagents/builtin/bug-fix-agent.md +22 -0
- novacode_cli/agents/default_subagents/builtin/bug-investigation-agent.md +42 -0
- novacode_cli/agents/default_subagents/builtin/code-explorer.md +37 -0
- novacode_cli/agents/default_subagents/builtin/code-review-agent.md +33 -0
- novacode_cli/agents/default_subagents/builtin/dependency-audit-agent.md +34 -0
- novacode_cli/agents/default_subagents/builtin/documentation-update-agent.md +46 -0
- novacode_cli/agents/default_subagents/builtin/performance-audit-agent.md +41 -0
- novacode_cli/agents/default_subagents/builtin/plan-scout-agent.md +64 -0
- novacode_cli/agents/default_subagents/builtin/refactoring-agent.md +35 -0
- novacode_cli/agents/default_subagents/builtin/refactoring-specialist-agent.md +175 -0
- novacode_cli/agents/default_subagents/builtin/security-audit-agent.md +96 -0
- novacode_cli/agents/default_subagents/builtin/test-generation-agent.md +33 -0
- novacode_cli/agents/default_subagents/builtin/test-runner-agent.md +55 -0
- novacode_cli/agents/default_subagents/prompt.py +88 -0
- novacode_cli/agents/default_subagents/subagents.py +138 -0
- novacode_cli/agents/dream_agent.py +93 -0
- novacode_cli/agents/edit_diagnostics.py +362 -0
- novacode_cli/agents/edit_fallback.py +62 -0
- novacode_cli/agents/handoff.py +166 -0
- novacode_cli/agents/invocation.py +152 -0
- novacode_cli/agents/model_budget.py +42 -0
- novacode_cli/agents/model_retry.py +113 -0
- novacode_cli/agents/model_router.py +420 -0
- novacode_cli/agents/openai_decisions.py +186 -0
- novacode_cli/agents/plan_agent/__init__.py +12 -0
- novacode_cli/agents/plan_agent/plan_agent.py +357 -0
- novacode_cli/agents/plan_agent/plan_mode_middleware.py +64 -0
- novacode_cli/agents/plan_execution.py +237 -0
- novacode_cli/agents/ponytail.py +66 -0
- novacode_cli/agents/prime.py +274 -0
- novacode_cli/agents/provider_caching.py +99 -0
- novacode_cli/agents/server_launcher.py +904 -0
- novacode_cli/agents/strict_tools.py +384 -0
- novacode_cli/agents/task_discipline.py +294 -0
- novacode_cli/agents/tool_call_repair.py +290 -0
- novacode_cli/agents/tool_offload.py +291 -0
- novacode_cli/agents/tool_search.py +806 -0
- novacode_cli/agents/tool_search_variants.py +106 -0
- novacode_cli/agents/tool_verdicts.py +1194 -0
- novacode_cli/agents/user_async_agents.py +312 -0
- novacode_cli/artifacts/__init__.py +10 -0
- novacode_cli/artifacts/registry.py +278 -0
- novacode_cli/artifacts/server.py +426 -0
- novacode_cli/assets/Nova.png +0 -0
- novacode_cli/assets/nova-avatar.png +0 -0
- novacode_cli/audio/__init__.py +301 -0
- novacode_cli/audio/capture.py +137 -0
- novacode_cli/audio/downloads.py +273 -0
- novacode_cli/audio/pipeline.py +391 -0
- novacode_cli/audio/playback.py +87 -0
- novacode_cli/audio/providers.py +208 -0
- novacode_cli/audio/speakable.py +78 -0
- novacode_cli/audio/stt.py +132 -0
- novacode_cli/audio/stt_deepgram.py +69 -0
- novacode_cli/audio/stt_elevenlabs.py +89 -0
- novacode_cli/audio/stt_parakeet.py +94 -0
- novacode_cli/audio/stt_whistle.py +61 -0
- novacode_cli/audio/summarize.py +92 -0
- novacode_cli/audio/tts.py +85 -0
- novacode_cli/audio/tts_kokoro.py +73 -0
- novacode_cli/audio/tts_orpheus.py +180 -0
- novacode_cli/audio/tts_pocket.py +131 -0
- novacode_cli/audio/tts_sano.py +69 -0
- novacode_cli/audio/vad.py +145 -0
- novacode_cli/backends/__init__.py +17 -0
- novacode_cli/backends/filesystem.py +894 -0
- novacode_cli/backends/native_search.py +92 -0
- novacode_cli/bootstrap/__init__.py +9 -0
- novacode_cli/bootstrap/env_snapshot.py +240 -0
- novacode_cli/bootstrap/steering.py +464 -0
- novacode_cli/bootstrap/vision_router.py +604 -0
- novacode_cli/brand.py +362 -0
- novacode_cli/cli_args.py +718 -0
- novacode_cli/commands/__init__.py +237 -0
- novacode_cli/commands/agents_commands.py +231 -0
- novacode_cli/commands/auth_handler.py +40 -0
- novacode_cli/commands/browser_use_handler.py +75 -0
- novacode_cli/commands/cache_handler.py +32 -0
- novacode_cli/commands/chat_handler.py +1357 -0
- novacode_cli/commands/ci_fix.py +171 -0
- novacode_cli/commands/claude_plan.py +331 -0
- novacode_cli/commands/codex_plan.py +195 -0
- novacode_cli/commands/commands.py +1113 -0
- novacode_cli/commands/create_handler.py +124 -0
- novacode_cli/commands/create_server.py +1887 -0
- novacode_cli/commands/cron_handler.py +119 -0
- novacode_cli/commands/dream_handler.py +203 -0
- novacode_cli/commands/effort_handler.py +65 -0
- novacode_cli/commands/evolution_handler.py +214 -0
- novacode_cli/commands/file_commands.py +381 -0
- novacode_cli/commands/handoff_handler.py +68 -0
- novacode_cli/commands/hooks_handler.py +269 -0
- novacode_cli/commands/init_handler.py +146 -0
- novacode_cli/commands/learning_handler.py +160 -0
- novacode_cli/commands/lite_mode_handler.py +76 -0
- novacode_cli/commands/log_commands.py +363 -0
- novacode_cli/commands/mcp_handler.py +29 -0
- novacode_cli/commands/model_handler.py +29 -0
- novacode_cli/commands/notifications_handler.py +158 -0
- novacode_cli/commands/onboard_handler.py +121 -0
- novacode_cli/commands/plan_handler.py +440 -0
- novacode_cli/commands/planner_usage.py +250 -0
- novacode_cli/commands/plugin_install_handler.py +38 -0
- novacode_cli/commands/plugins_handler.py +35 -0
- novacode_cli/commands/prime_handler.py +455 -0
- novacode_cli/commands/prompt_handler.py +149 -0
- novacode_cli/commands/provider_usage.py +160 -0
- novacode_cli/commands/ralph_events.py +106 -0
- novacode_cli/commands/ralph_handler.py +1293 -0
- novacode_cli/commands/refine_handler.py +213 -0
- novacode_cli/commands/research_handler.py +202 -0
- novacode_cli/commands/rootapp_remote.py +83 -0
- novacode_cli/commands/router_handler.py +36 -0
- novacode_cli/commands/server_commands.py +249 -0
- novacode_cli/commands/session_commands.py +203 -0
- novacode_cli/commands/share_handler.py +450 -0
- novacode_cli/commands/side_commands.py +366 -0
- novacode_cli/commands/skill_invoke.py +420 -0
- novacode_cli/commands/skills_commands.py +48 -0
- novacode_cli/commands/trace_handler.py +322 -0
- novacode_cli/commands/trello_handler.py +226 -0
- novacode_cli/commands/trello_remote.py +177 -0
- novacode_cli/commands/trello_server.py +1214 -0
- novacode_cli/commands/vision_handler.py +127 -0
- novacode_cli/commands/voice_handler.py +447 -0
- novacode_cli/commands/webhook_handler.py +134 -0
- novacode_cli/commands/wiki_commands.py +227 -0
- novacode_cli/compaction.py +753 -0
- novacode_cli/computation_cache.py +159 -0
- novacode_cli/computer/__init__.py +1 -0
- novacode_cli/computer/persistent.py +234 -0
- novacode_cli/computer/preview.py +253 -0
- novacode_cli/computer/runtime.py +474 -0
- novacode_cli/computer/windows_console.py +152 -0
- novacode_cli/computer/windows_qemu.py +211 -0
- novacode_cli/config/browser_mode.py +159 -0
- novacode_cli/config/catalog_models.py +95 -0
- novacode_cli/config/config.py +1388 -0
- novacode_cli/config/credentials.py +540 -0
- novacode_cli/config/google_oauth_auth.py +281 -0
- novacode_cli/config/model_capabilities.py +343 -0
- novacode_cli/config/model_catalog.py +275 -0
- novacode_cli/config/model_create.py +1325 -0
- novacode_cli/config/model_manager.py +695 -0
- novacode_cli/config/nova_config.py +1283 -0
- novacode_cli/config/openai_chatgpt_auth.py +384 -0
- novacode_cli/config/opencode_gateway.py +25 -0
- novacode_cli/config/plan_mode.py +188 -0
- novacode_cli/config/provider_auth.py +438 -0
- novacode_cli/config/provider_catalog.json +5127 -0
- novacode_cli/config/provider_registry.py +38 -0
- novacode_cli/config/provider_transports.py +258 -0
- novacode_cli/config/role_models.py +175 -0
- novacode_cli/context/__init__.py +50 -0
- novacode_cli/context/_analysis.py +799 -0
- novacode_cli/context/_budget.py +116 -0
- novacode_cli/context/_conversation.py +73 -0
- novacode_cli/context/_dynamic.py +264 -0
- novacode_cli/context/_model_config.py +380 -0
- novacode_cli/context/_models_dev.py +99 -0
- novacode_cli/context/archives.py +248 -0
- novacode_cli/context/estimates.py +65 -0
- novacode_cli/context/history.py +17 -0
- novacode_cli/context/knowledge_cache.py +292 -0
- novacode_cli/context/manager.py +101 -0
- novacode_cli/context/observations.py +65 -0
- novacode_cli/context/pressure.py +168 -0
- novacode_cli/context/provider_accounting.py +109 -0
- novacode_cli/context/snapshot.py +493 -0
- novacode_cli/context/strategies.py +279 -0
- novacode_cli/context/task_progress.py +122 -0
- novacode_cli/core/__init__.py +0 -0
- novacode_cli/core/agent_loop.py +1821 -0
- novacode_cli/core/autonomous_loop.py +277 -0
- novacode_cli/core/code_approvals.py +57 -0
- novacode_cli/core/input_preparation.py +276 -0
- novacode_cli/core/reasoning.py +67 -0
- novacode_cli/core/streaming.py +326 -0
- novacode_cli/core/subagent_tasks.py +696 -0
- novacode_cli/core/subagent_tracking.py +235 -0
- novacode_cli/core/tool_results.py +116 -0
- novacode_cli/core/verification_loop.py +272 -0
- novacode_cli/council.py +595 -0
- novacode_cli/council_consensus.py +115 -0
- novacode_cli/council_decision.py +157 -0
- novacode_cli/council_planner.py +1421 -0
- novacode_cli/council_settings.py +208 -0
- novacode_cli/council_web_settings.py +41 -0
- novacode_cli/council_workspace.py +56 -0
- novacode_cli/cowork/__init__.py +10 -0
- novacode_cli/cowork/broker_middleware.py +162 -0
- novacode_cli/cowork/launcher.py +102 -0
- novacode_cli/cowork/policy.py +284 -0
- novacode_cli/cowork/ui.py +826 -0
- novacode_cli/daemons/__init__.py +1 -0
- novacode_cli/daemons/registry.py +342 -0
- novacode_cli/desktop_notifications.py +302 -0
- novacode_cli/doctor.py +251 -0
- novacode_cli/embeddings.py +109 -0
- novacode_cli/entrypoint.py +56 -0
- novacode_cli/errors/__init__.py +19 -0
- novacode_cli/errors/handlers.py +648 -0
- novacode_cli/errors/provider_errors.py +249 -0
- novacode_cli/errors/taxonomy.py +80 -0
- novacode_cli/events.py +179 -0
- novacode_cli/file_ops.py +714 -0
- novacode_cli/git_safety.py +281 -0
- novacode_cli/handoff.py +383 -0
- novacode_cli/headless/__init__.py +18 -0
- novacode_cli/headless/input.py +58 -0
- novacode_cli/headless/output.py +224 -0
- novacode_cli/headless/pipe.py +532 -0
- novacode_cli/headless/runner.py +461 -0
- novacode_cli/hermes/__init__.py +39 -0
- novacode_cli/hermes/activation.py +297 -0
- novacode_cli/hermes/config.py +129 -0
- novacode_cli/hermes/curator.py +199 -0
- novacode_cli/hermes/evolution.py +374 -0
- novacode_cli/hermes/ledger.py +364 -0
- novacode_cli/hermes/memory_tiers.py +969 -0
- novacode_cli/hermes/middleware.py +678 -0
- novacode_cli/hermes/overview.py +174 -0
- novacode_cli/hermes/prompt_evolution.py +503 -0
- novacode_cli/hermes/refine_loop.py +643 -0
- novacode_cli/hermes/refinement_log.py +190 -0
- novacode_cli/hermes/review.py +674 -0
- novacode_cli/hermes/skill_debate.py +135 -0
- novacode_cli/hermes/skill_discovery.py +450 -0
- novacode_cli/hermes/skill_manager.py +335 -0
- novacode_cli/hermes/skill_proposals.py +43 -0
- novacode_cli/hermes/test_gaming.py +119 -0
- novacode_cli/hermes/tracker.py +435 -0
- novacode_cli/hermes/tuner.py +206 -0
- novacode_cli/hermes/verification_memory.py +134 -0
- novacode_cli/hermes/verifier.py +277 -0
- novacode_cli/hitl/__init__.py +22 -0
- novacode_cli/hitl/interrupts.py +493 -0
- novacode_cli/hooks.py +359 -0
- novacode_cli/image_utils.py +517 -0
- novacode_cli/init/__init__.py +7 -0
- novacode_cli/init/events.py +43 -0
- novacode_cli/input_utils.py +526 -0
- novacode_cli/install_commands.py +87 -0
- novacode_cli/integrations/__init__.py +82 -0
- novacode_cli/integrations/daytona.py +122 -0
- novacode_cli/integrations/docker.py +309 -0
- novacode_cli/integrations/host_path.py +111 -0
- novacode_cli/integrations/langsmith.py +151 -0
- novacode_cli/integrations/modal.py +134 -0
- novacode_cli/integrations/runloop.py +130 -0
- novacode_cli/integrations/sandbox_factory.py +1388 -0
- novacode_cli/integrations/sandbox_registry.py +312 -0
- novacode_cli/integrations/workdir_backend.py +589 -0
- novacode_cli/main.py +2078 -0
- novacode_cli/mcp/__init__.py +73 -0
- novacode_cli/mcp/auth.py +189 -0
- novacode_cli/mcp/client.py +571 -0
- novacode_cli/mcp/code_mode.py +5 -0
- novacode_cli/mcp/code_runtime.py +5 -0
- novacode_cli/mcp/commands.py +902 -0
- novacode_cli/mcp/config.py +406 -0
- novacode_cli/mcp/generic.py +128 -0
- novacode_cli/mcp/middleware.py +993 -0
- novacode_cli/mcp/presets.py +367 -0
- novacode_cli/memory/__init__.py +1 -0
- novacode_cli/memory/agent_memory.py +1312 -0
- novacode_cli/memory/builtin_lessons.py +60 -0
- novacode_cli/memory/dream_gates.py +192 -0
- novacode_cli/memory/limits.py +59 -0
- novacode_cli/memory/parsed_cache.py +106 -0
- novacode_cli/memory/recall_stats.py +97 -0
- novacode_cli/memory/semantic.py +116 -0
- novacode_cli/memory/store.py +335 -0
- novacode_cli/memory/wiki.py +274 -0
- novacode_cli/migrate.py +185 -0
- novacode_cli/onboarding.py +649 -0
- novacode_cli/onboarding_check.py +119 -0
- novacode_cli/path_approval.py +297 -0
- novacode_cli/plan_archive.py +148 -0
- novacode_cli/plugins/__init__.py +121 -0
- novacode_cli/plugins/claude_plugins.py +538 -0
- novacode_cli/plugins/cli.py +168 -0
- novacode_cli/plugins/components.py +413 -0
- novacode_cli/plugins/hooks.py +356 -0
- novacode_cli/plugins/loader.py +317 -0
- novacode_cli/plugins/marketplaces.py +199 -0
- novacode_cli/plugins/storage.py +70 -0
- novacode_cli/prime.py +759 -0
- novacode_cli/prime_factory/__init__.py +15 -0
- novacode_cli/prime_factory/container.py +198 -0
- novacode_cli/prime_factory/controller.py +565 -0
- novacode_cli/prime_factory/knowledge.py +261 -0
- novacode_cli/prime_factory/publication.py +218 -0
- novacode_cli/prime_factory/recovery.py +50 -0
- novacode_cli/prime_factory/research.py +370 -0
- novacode_cli/prime_factory/runner.py +608 -0
- novacode_cli/prime_factory/runtime.py +261 -0
- novacode_cli/prime_factory/state.py +201 -0
- novacode_cli/prime_factory/store.py +128 -0
- novacode_cli/prime_factory/worker.py +129 -0
- novacode_cli/prime_factory/workflow.py +899 -0
- novacode_cli/prime_factory/workspace.py +317 -0
- novacode_cli/prime_memory.py +290 -0
- novacode_cli/process_manager.py +585 -0
- novacode_cli/project_onboard.py +537 -0
- novacode_cli/prompts/System_Prompt_Nova.jinja +23 -0
- novacode_cli/prompts/__init__.py +168 -0
- novacode_cli/prompts/agent_definitions.jinja +11 -0
- novacode_cli/prompts/agent_generation.jinja +125 -0
- novacode_cli/prompts/async_agents/general_purpose.jinja +22 -0
- novacode_cli/prompts/claude_plan_mode.jinja +12 -0
- novacode_cli/prompts/coding_workflow.jinja +18 -0
- novacode_cli/prompts/computer_use.jinja +17 -0
- novacode_cli/prompts/core_agent_system.jinja +81 -0
- novacode_cli/prompts/delegation.jinja +15 -0
- novacode_cli/prompts/desktop_power.jinja +4 -0
- novacode_cli/prompts/file_paths.jinja +15 -0
- novacode_cli/prompts/file_tracker.jinja +23 -0
- novacode_cli/prompts/init_agents_md.jinja +56 -0
- novacode_cli/prompts/init_exploration.jinja +174 -0
- novacode_cli/prompts/init_semantic_orchestrate.jinja +37 -0
- novacode_cli/prompts/instruction_scope.jinja +5 -0
- novacode_cli/prompts/learnings_extraction.jinja +45 -0
- novacode_cli/prompts/longterm_memory.jinja +54 -0
- novacode_cli/prompts/mcp.jinja +84 -0
- novacode_cli/prompts/memory_consolidation.jinja +40 -0
- novacode_cli/prompts/nova_evolution.jinja +73 -0
- novacode_cli/prompts/nova_review.jinja +158 -0
- novacode_cli/prompts/nova_skill_debate.jinja +36 -0
- novacode_cli/prompts/nova_standing_instructions.jinja +26 -0
- novacode_cli/prompts/nova_verify.jinja +100 -0
- novacode_cli/prompts/onboard_dashboard.jinja +378 -0
- novacode_cli/prompts/plan_agent.jinja +87 -0
- novacode_cli/prompts/plan_scouts.jinja +5 -0
- novacode_cli/prompts/plan_storage.jinja +8 -0
- novacode_cli/prompts/planning.jinja +82 -0
- novacode_cli/prompts/ponytail/LICENSE +21 -0
- novacode_cli/prompts/ponytail/UPSTREAM.json +15 -0
- novacode_cli/prompts/ponytail/hooks/ponytail-config.js +175 -0
- novacode_cli/prompts/ponytail/hooks/ponytail-instructions.js +98 -0
- novacode_cli/prompts/ponytail/skills/ponytail/SKILL.md +52 -0
- novacode_cli/prompts/ponytail/skills/ponytail-audit/SKILL.md +103 -0
- novacode_cli/prompts/ponytail/skills/ponytail-debt/SKILL.md +44 -0
- novacode_cli/prompts/ponytail/skills/ponytail-gain/SKILL.md +52 -0
- novacode_cli/prompts/ponytail/skills/ponytail-help/SKILL.md +76 -0
- novacode_cli/prompts/ponytail/skills/ponytail-review/SKILL.md +101 -0
- novacode_cli/prompts/prompt_evolution.jinja +27 -0
- novacode_cli/prompts/ralph_iteration.jinja +75 -0
- novacode_cli/prompts/refine_gate.jinja +31 -0
- novacode_cli/prompts/refine_plan.jinja +64 -0
- novacode_cli/prompts/refine_review.jinja +34 -0
- novacode_cli/prompts/research_delivery.jinja +9 -0
- novacode_cli/prompts/research_swarm.jinja +130 -0
- novacode_cli/prompts/response_style.jinja +5 -0
- novacode_cli/prompts/session_summarization.jinja +86 -0
- novacode_cli/prompts/skill_creation.jinja +85 -0
- novacode_cli/prompts/subagents/fact_checker.jinja +48 -0
- novacode_cli/prompts/subagents/financial_analyst.jinja +50 -0
- novacode_cli/prompts/subagents/literature_reviewer.jinja +52 -0
- novacode_cli/prompts/subagents/market_analyst.jinja +48 -0
- novacode_cli/prompts/subagents/research_synthesizer.jinja +57 -0
- novacode_cli/prompts/subagents/technical_researcher.jinja +56 -0
- novacode_cli/prompts/subagents/web_researcher.jinja +46 -0
- novacode_cli/prompts/summarization.jinja +107 -0
- novacode_cli/prompts/summarization_answers.jinja +23 -0
- novacode_cli/prompts/summarization_questions.jinja +32 -0
- novacode_cli/prompts/vision_caption.jinja +21 -0
- novacode_cli/prompts/voice_summary.jinja +65 -0
- novacode_cli/prompts/wiki_file.jinja +57 -0
- novacode_cli/prompts/wiki_ingest.jinja +87 -0
- novacode_cli/recovery.py +383 -0
- novacode_cli/remote/__init__.py +45 -0
- novacode_cli/remote/approvals.py +282 -0
- novacode_cli/remote/async_task_clients.py +97 -0
- novacode_cli/remote/async_task_watcher.py +240 -0
- novacode_cli/remote/bridge.py +905 -0
- novacode_cli/remote/command_menu.py +26 -0
- novacode_cli/remote/command_ui.py +243 -0
- novacode_cli/remote/config.py +199 -0
- novacode_cli/remote/discord_bridge.py +530 -0
- novacode_cli/remote/discord_format.py +31 -0
- novacode_cli/remote/discord_threads.py +225 -0
- novacode_cli/remote/images.py +97 -0
- novacode_cli/remote/processor.py +601 -0
- novacode_cli/remote/rootapp_bot/bot.cjs +16 -0
- novacode_cli/remote/rootapp_bot/package-lock.json +2683 -0
- novacode_cli/remote/rootapp_bot/package.json +18 -0
- novacode_cli/remote/rootapp_bot/transport.cjs +80 -0
- novacode_cli/remote/rootapp_bridge.py +370 -0
- novacode_cli/remote/rootapp_setup.py +89 -0
- novacode_cli/remote/routing.py +103 -0
- novacode_cli/remote/scheduler.py +302 -0
- novacode_cli/remote/session.py +164 -0
- novacode_cli/remote/status.py +411 -0
- novacode_cli/remote/streaming.py +188 -0
- novacode_cli/remote/telegram_bridge.py +1118 -0
- novacode_cli/remote/telegram_format.py +215 -0
- novacode_cli/remote/telegram_hub.py +256 -0
- novacode_cli/remote/voice_notes.py +120 -0
- novacode_cli/remote/webhook_adapters.py +167 -0
- novacode_cli/remote/webhook_server.py +154 -0
- novacode_cli/rewind.py +150 -0
- novacode_cli/rooms/__init__.py +1 -0
- novacode_cli/rooms/bridge.py +86 -0
- novacode_cli/rooms/cli.py +231 -0
- novacode_cli/rooms/nova.py +227 -0
- novacode_cli/rooms/runtime.py +299 -0
- novacode_cli/rooms/store.py +538 -0
- novacode_cli/rooms/workspace.py +94 -0
- novacode_cli/security/__init__.py +29 -0
- novacode_cli/security/approval_context.py +45 -0
- novacode_cli/security/delegated_approval.py +68 -0
- novacode_cli/security/local_http.py +85 -0
- novacode_cli/security/middleware.py +87 -0
- novacode_cli/security/paths.py +35 -0
- novacode_cli/security/policy.py +535 -0
- novacode_cli/security/policy_writer.py +90 -0
- novacode_cli/security/prompt_injection.py +203 -0
- novacode_cli/security/remember.py +81 -0
- novacode_cli/security/rule_synthesis.py +116 -0
- novacode_cli/security/secret_files.py +61 -0
- novacode_cli/security/session_allow.py +114 -0
- novacode_cli/security/unicode_security.py +516 -0
- novacode_cli/security/validator.py +173 -0
- novacode_cli/server/__init__.py +9 -0
- novacode_cli/server/app.py +632 -0
- novacode_cli/server/event_adapter.py +189 -0
- novacode_cli/server/session_manager.py +90 -0
- novacode_cli/server_main.py +64 -0
- novacode_cli/server_runner/dev_server.py +530 -0
- novacode_cli/server_runner/test_runner.py +470 -0
- novacode_cli/session/__init__.py +1 -0
- novacode_cli/session/adapters/__init__.py +240 -0
- novacode_cli/session/adapters/claude_code.py +102 -0
- novacode_cli/session/adapters/codex.py +77 -0
- novacode_cli/session/adapters/harness_native.py +75 -0
- novacode_cli/session/checkpoint_coordinator.py +448 -0
- novacode_cli/session/context_restore.py +25 -0
- novacode_cli/session/history_export.py +228 -0
- novacode_cli/session/history_tree.py +174 -0
- novacode_cli/session/imported_context.py +267 -0
- novacode_cli/session/runtime_identity.py +32 -0
- novacode_cli/session/session_persistence.py +1168 -0
- novacode_cli/session/session_prompt_builder.py +279 -0
- novacode_cli/session/session_restore.py +351 -0
- novacode_cli/session/session_summarization.py +20 -0
- novacode_cli/session/transcript_journal.py +153 -0
- novacode_cli/sessions/__init__.py +11 -0
- novacode_cli/sessions/gateway.py +84 -0
- novacode_cli/sessions/launch.py +51 -0
- novacode_cli/sessions/lease.py +213 -0
- novacode_cli/sessions/messaging.py +255 -0
- novacode_cli/sessions/protocol.py +216 -0
- novacode_cli/sessions/resume.py +31 -0
- novacode_cli/sessions/supervisor.py +498 -0
- novacode_cli/sessions/tasks.py +73 -0
- novacode_cli/sessions/tui_messaging.py +413 -0
- novacode_cli/sessions/worker.py +652 -0
- novacode_cli/sessions/worktree.py +262 -0
- novacode_cli/shell/__init__.py +27 -0
- novacode_cli/shell/jobs.py +475 -0
- novacode_cli/shell/middleware.py +1568 -0
- novacode_cli/shell/os_sandbox.py +205 -0
- novacode_cli/shell/output.py +181 -0
- novacode_cli/shell/patterns.py +275 -0
- novacode_cli/shell/processes.py +87 -0
- novacode_cli/shell/utils.py +236 -0
- novacode_cli/skills/__init__.py +26 -0
- novacode_cli/skills/builtin/handoff/SKILL.md +15 -0
- novacode_cli/skills/curation_middleware.py +69 -0
- novacode_cli/skills/executable.py +161 -0
- novacode_cli/skills/libraries.py +102 -0
- novacode_cli/skills/load.py +248 -0
- novacode_cli/skills/prune.py +215 -0
- novacode_cli/skills/refreshing_middleware.py +896 -0
- novacode_cli/skills/retrieval.py +248 -0
- novacode_cli/skills/runtime.py +118 -0
- novacode_cli/skills/schema.py +254 -0
- novacode_cli/skills/skill_creation.py +1549 -0
- novacode_cli/skills/skill_lock.py +75 -0
- novacode_cli/skills/skill_system_prompt.py +16 -0
- novacode_cli/skills/skills_prefs.py +126 -0
- novacode_cli/skills/tool_resolver.py +31 -0
- novacode_cli/skills/upstream.py +50 -0
- novacode_cli/skills/versioning.py +291 -0
- novacode_cli/states/Session.py +847 -0
- novacode_cli/states/__init__.py +31 -0
- novacode_cli/states/slices/__init__.py +17 -0
- novacode_cli/states/slices/agent_runtime.py +249 -0
- novacode_cli/states/slices/background_tasks.py +21 -0
- novacode_cli/states/slices/notifications.py +74 -0
- novacode_cli/states/slices/remote_bridge.py +56 -0
- novacode_cli/states/slices/ui_settings.py +49 -0
- novacode_cli/states/slices/wiki.py +25 -0
- novacode_cli/teammates/__init__.py +1 -0
- novacode_cli/teammates/controller.py +557 -0
- novacode_cli/teammates/graph.py +104 -0
- novacode_cli/teammates/registry.py +175 -0
- novacode_cli/teammates/service.py +254 -0
- novacode_cli/teammates/worker.py +19 -0
- novacode_cli/teams/__init__.py +1 -0
- novacode_cli/teams/adapters/__init__.py +1 -0
- novacode_cli/teams/adapters/container.py +316 -0
- novacode_cli/teams/adapters/inprocess.py +206 -0
- novacode_cli/teams/adapters/process.py +364 -0
- novacode_cli/teams/bootstrap.py +246 -0
- novacode_cli/teams/container_git.py +67 -0
- novacode_cli/teams/context.py +34 -0
- novacode_cli/teams/coordinator.py +815 -0
- novacode_cli/teams/gateway.py +200 -0
- novacode_cli/teams/host.py +64 -0
- novacode_cli/teams/legacy.py +52 -0
- novacode_cli/teams/middleware.py +176 -0
- novacode_cli/teams/models.py +104 -0
- novacode_cli/teams/output.py +47 -0
- novacode_cli/teams/protocol.py +50 -0
- novacode_cli/teams/recovery.py +80 -0
- novacode_cli/teams/scheduler.py +44 -0
- novacode_cli/teams/store.py +631 -0
- novacode_cli/teams/worker.py +251 -0
- novacode_cli/teams/workspace.py +100 -0
- novacode_cli/token_utils.py +256 -0
- novacode_cli/tools/__init__.py +104 -0
- novacode_cli/tools/_shared.py +97 -0
- novacode_cli/tools/_types.py +24 -0
- novacode_cli/tools/artifact_tools.py +137 -0
- novacode_cli/tools/code_search_tools.py +330 -0
- novacode_cli/tools/computer_tools.py +77 -0
- novacode_cli/tools/cron_tools.py +65 -0
- novacode_cli/tools/daemon_tool.py +290 -0
- novacode_cli/tools/desktop_tools.py +161 -0
- novacode_cli/tools/fetch_tools.py +530 -0
- novacode_cli/tools/http_tools.py +279 -0
- novacode_cli/tools/inspection_tools.py +107 -0
- novacode_cli/tools/job_tools.py +143 -0
- novacode_cli/tools/knowledge_tools.py +427 -0
- novacode_cli/tools/memory_tools.py +461 -0
- novacode_cli/tools/memory_wiki_tools.py +40 -0
- novacode_cli/tools/notebook_tools.py +238 -0
- novacode_cli/tools/oracle_tool.py +83 -0
- novacode_cli/tools/plan_archive_tools.py +80 -0
- novacode_cli/tools/plan_mode_tools.py +315 -0
- novacode_cli/tools/python_kernel_loop.py +165 -0
- novacode_cli/tools/python_kernel_tool.py +222 -0
- novacode_cli/tools/reflection_tools.py +34 -0
- novacode_cli/tools/scraper_tools.py +377 -0
- novacode_cli/tools/skill_tools.py +275 -0
- novacode_cli/tools/speak_tool.py +51 -0
- novacode_cli/tools/store_memory_tools.py +227 -0
- novacode_cli/tools/ui_tools.py +62 -0
- novacode_cli/tools/waiter_tools.py +408 -0
- novacode_cli/tools/web_tools.py +349 -0
- novacode_cli/tools/wiki_tools.py +152 -0
- novacode_cli/tools/worktree_tools.py +219 -0
- novacode_cli/tracking/__init__.py +10 -0
- novacode_cli/tracking/file_tracker.py +826 -0
- novacode_cli/tracking/local_metrics.py +170 -0
- novacode_cli/tracking/loop_guard.py +507 -0
- novacode_cli/tracking/request_metrics.py +60 -0
- novacode_cli/tracking/tracing.py +692 -0
- novacode_cli/tracking/usage_tree.py +677 -0
- novacode_cli/tracking/workspace_anchoring.py +231 -0
- novacode_cli/tui/__init__.py +11 -0
- novacode_cli/tui/animation_rate.py +9 -0
- novacode_cli/tui/animations.py +332 -0
- novacode_cli/tui/app.py +19315 -0
- novacode_cli/tui/auth_display.py +87 -0
- novacode_cli/tui/auth_screens.py +826 -0
- novacode_cli/tui/avatar.py +294 -0
- novacode_cli/tui/avatar_frames.py +21 -0
- novacode_cli/tui/background_tasks.py +35 -0
- novacode_cli/tui/browser_settings_screen.py +70 -0
- novacode_cli/tui/caveman_screen.py +185 -0
- novacode_cli/tui/council_settings_screen.py +182 -0
- novacode_cli/tui/council_view.py +535 -0
- novacode_cli/tui/decision_settings.py +268 -0
- novacode_cli/tui/diff_preview.py +199 -0
- novacode_cli/tui/gc_tuning.py +63 -0
- novacode_cli/tui/handoff_screen.py +206 -0
- novacode_cli/tui/harness.py +244 -0
- novacode_cli/tui/history_tree_screen.py +179 -0
- novacode_cli/tui/home.py +328 -0
- novacode_cli/tui/home_scene.py +94 -0
- novacode_cli/tui/icons.py +81 -0
- novacode_cli/tui/interface_styles.tcss +327 -0
- novacode_cli/tui/matrix3.py +108 -0
- novacode_cli/tui/mcp_tools_screen.py +131 -0
- novacode_cli/tui/motion.py +178 -0
- novacode_cli/tui/output_body.py +136 -0
- novacode_cli/tui/output_buffer.py +59 -0
- novacode_cli/tui/palette.py +334 -0
- novacode_cli/tui/pickers.py +237 -0
- novacode_cli/tui/prime_panel.py +506 -0
- novacode_cli/tui/provider_signin.py +213 -0
- novacode_cli/tui/pty_shell.py +159 -0
- novacode_cli/tui/reference_screens.py +444 -0
- novacode_cli/tui/resume_picker.py +346 -0
- novacode_cli/tui/rooms.py +597 -0
- novacode_cli/tui/screens.py +7088 -0
- novacode_cli/tui/session_import.py +333 -0
- novacode_cli/tui/session_pane.py +242 -0
- novacode_cli/tui/session_tab.py +42 -0
- novacode_cli/tui/session_transcript.py +118 -0
- novacode_cli/tui/shell_output.py +7 -0
- novacode_cli/tui/stall_watch.py +174 -0
- novacode_cli/tui/statusline.py +40 -0
- novacode_cli/tui/statusline_screen.py +91 -0
- novacode_cli/tui/teammate_updates.py +162 -0
- novacode_cli/tui/teammates.py +548 -0
- novacode_cli/tui/terminal.py +289 -0
- novacode_cli/tui/terminal_view.py +235 -0
- novacode_cli/tui/themes.py +118 -0
- novacode_cli/tui/update_notice.py +84 -0
- novacode_cli/tui/voice_downloads.py +113 -0
- novacode_cli/tui/widgets.py +2038 -0
- novacode_cli/tui/windows_input.py +38 -0
- novacode_cli/tui/workspace_approval.py +165 -0
- novacode_cli/ui/council_web.py +113 -0
- novacode_cli/ui/council_workspace.py +72 -0
- novacode_cli/ui/cowork_web.py +41 -0
- novacode_cli/ui/diff_highlight.py +160 -0
- novacode_cli/ui/execution.py +1396 -0
- novacode_cli/ui/exit_summary.py +65 -0
- novacode_cli/ui/hitl_approval.py +196 -0
- novacode_cli/ui/input_preparation.py +2 -0
- novacode_cli/ui/interrupt_handlers.py +215 -0
- novacode_cli/ui/question_prompt.py +151 -0
- novacode_cli/ui/status_phrases.py +157 -0
- novacode_cli/ui/stream_preview.py +66 -0
- novacode_cli/ui/streaming.py +2 -0
- novacode_cli/ui/subagent_tracking.py +2 -0
- novacode_cli/ui/ui_elements.py +1585 -0
- novacode_cli/ui/web_workspace.py +244 -0
- novacode_cli/ui_events.py +299 -0
- novacode_cli/update_ui.py +243 -0
- novacode_cli/updates.py +489 -0
- novacode_cli/utils/backend_patches.py +399 -0
- novacode_cli/utils/backend_paths.py +467 -0
- novacode_cli/utils/lazy_providers.py +51 -0
- novacode_cli/utils/model_info.py +223 -0
- novacode_cli/utils/pdf_extraction.py +183 -0
- novacode_cli/utils/startup.py +29 -0
- novacode_cli/utils/tasks.py +42 -0
- novacode_cli/utils/windows.py +9 -0
- novacode_cli/vixie/hook_adapter.py +127 -0
- novacode_cli/vixie/server.py +375 -0
- novacode_cli/wiki/__init__.py +15 -0
- novacode_cli/wiki/ask.py +56 -0
- novacode_cli/wiki/file.py +71 -0
- novacode_cli/wiki/ingest.py +184 -0
- novacode_cli/wiki/manager.py +261 -0
- novacode_cli-1.2.0.dist-info/METADATA +271 -0
- novacode_cli-1.2.0.dist-info/RECORD +691 -0
- novacode_cli-1.2.0.dist-info/WHEEL +5 -0
- novacode_cli-1.2.0.dist-info/entry_points.txt +2 -0
- novacode_cli-1.2.0.dist-info/licenses/LICENSE +21 -0
- novacode_cli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"""Handlers for file-related commands: /files, /images, /restore."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
|
|
8
|
+
from novacode_cli.config.config import COLORS, console
|
|
9
|
+
from novacode_cli.recovery import REASON_LABELS, get_recovery_manager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def handle_files_command() -> bool:
|
|
13
|
+
"""Handle /files command to show file operation summary for the session.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
True (always handled)
|
|
17
|
+
"""
|
|
18
|
+
from novacode_cli.tracking.file_tracker import get_session_tracker
|
|
19
|
+
|
|
20
|
+
console.print()
|
|
21
|
+
|
|
22
|
+
tracker = get_session_tracker()
|
|
23
|
+
|
|
24
|
+
# Header
|
|
25
|
+
header = Text()
|
|
26
|
+
header.append("📁 ", style="bold")
|
|
27
|
+
header.append("Session File Operations", style=f"bold {COLORS['primary']}")
|
|
28
|
+
|
|
29
|
+
console.print(Panel(header, border_style=COLORS["primary"]))
|
|
30
|
+
console.print()
|
|
31
|
+
|
|
32
|
+
# Stats summary
|
|
33
|
+
console.print("[bold]Statistics:[/bold]")
|
|
34
|
+
console.print(f" • Files read: [cyan]{len(tracker.files_read)}[/cyan]")
|
|
35
|
+
console.print(f" • Files modified: [cyan]{len(tracker.files_written)}[/cyan]")
|
|
36
|
+
console.print(f" • Total read operations: [dim]{tracker.total_reads}[/dim]")
|
|
37
|
+
console.print(f" • Total write operations: [dim]{tracker.total_writes}[/dim]")
|
|
38
|
+
if tracker.rejected_edits > 0:
|
|
39
|
+
console.print(
|
|
40
|
+
f" • [red]Rejected edits (unread files): {tracker.rejected_edits}[/red]"
|
|
41
|
+
)
|
|
42
|
+
console.print()
|
|
43
|
+
|
|
44
|
+
# Files read
|
|
45
|
+
if tracker.files_read:
|
|
46
|
+
console.print("[bold]Files Read:[/bold]")
|
|
47
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 1))
|
|
48
|
+
table.add_column("File", style="cyan")
|
|
49
|
+
table.add_column("Lines", justify="right")
|
|
50
|
+
table.add_column("Read At", style="dim")
|
|
51
|
+
|
|
52
|
+
for path in tracker.read_order[-15:]: # Show last 15
|
|
53
|
+
record = tracker.files_read[path]
|
|
54
|
+
# Shorten path for display
|
|
55
|
+
display_path = path
|
|
56
|
+
if len(display_path) > 60:
|
|
57
|
+
display_path = "..." + display_path[-57:]
|
|
58
|
+
time_str = (
|
|
59
|
+
record.read_at.split("T")[1][:8]
|
|
60
|
+
if "T" in record.read_at
|
|
61
|
+
else record.read_at
|
|
62
|
+
)
|
|
63
|
+
table.add_row(display_path, str(record.line_count), time_str)
|
|
64
|
+
|
|
65
|
+
console.print(table)
|
|
66
|
+
if len(tracker.read_order) > 15:
|
|
67
|
+
console.print(
|
|
68
|
+
f" [dim]... and {len(tracker.read_order) - 15} more files[/dim]"
|
|
69
|
+
)
|
|
70
|
+
console.print()
|
|
71
|
+
else:
|
|
72
|
+
console.print("[dim]No files read in this session.[/dim]")
|
|
73
|
+
console.print()
|
|
74
|
+
|
|
75
|
+
# Files modified
|
|
76
|
+
if tracker.files_written:
|
|
77
|
+
console.print("[bold]Files Modified:[/bold]")
|
|
78
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 1))
|
|
79
|
+
table.add_column("File", style="yellow")
|
|
80
|
+
table.add_column("Operations", justify="center")
|
|
81
|
+
table.add_column("Last Modified", style="dim")
|
|
82
|
+
|
|
83
|
+
for path in tracker.write_order[-15:]: # Show last 15
|
|
84
|
+
records = tracker.files_written[path]
|
|
85
|
+
# Shorten path for display
|
|
86
|
+
display_path = path
|
|
87
|
+
if len(display_path) > 60:
|
|
88
|
+
display_path = "..." + display_path[-57:]
|
|
89
|
+
ops = ", ".join(r.operation for r in records[-3:]) # Last 3 ops
|
|
90
|
+
if len(records) > 3:
|
|
91
|
+
ops = f"({len(records)}x) " + ops
|
|
92
|
+
last_time = records[-1].written_at
|
|
93
|
+
time_str = last_time.split("T")[1][:8] if "T" in last_time else last_time
|
|
94
|
+
table.add_row(display_path, ops, time_str)
|
|
95
|
+
|
|
96
|
+
console.print(table)
|
|
97
|
+
if len(tracker.write_order) > 15:
|
|
98
|
+
console.print(
|
|
99
|
+
f" [dim]... and {len(tracker.write_order) - 15} more files[/dim]"
|
|
100
|
+
)
|
|
101
|
+
console.print()
|
|
102
|
+
else:
|
|
103
|
+
console.print("[dim]No files modified in this session.[/dim]")
|
|
104
|
+
console.print()
|
|
105
|
+
|
|
106
|
+
# Context note
|
|
107
|
+
console.print(
|
|
108
|
+
"[dim]Note: The system enforces read-before-edit to prevent hallucinations.[/dim]"
|
|
109
|
+
)
|
|
110
|
+
console.print("[dim]Files must be read before they can be edited.[/dim]")
|
|
111
|
+
console.print()
|
|
112
|
+
|
|
113
|
+
return True
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
async def handle_images_command(args: str | None, image_tracker) -> bool:
|
|
117
|
+
"""Handle /images command to manage images in the conversation.
|
|
118
|
+
|
|
119
|
+
Usage:
|
|
120
|
+
/images - List all images in the conversation
|
|
121
|
+
/images list - List all images
|
|
122
|
+
/images remove <id> - Remove an image by ID (e.g., /images remove image-1)
|
|
123
|
+
/images clear - Clear all images
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
args: Optional arguments (list, remove <id>, clear).
|
|
127
|
+
image_tracker: The ImageTracker instance.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
True (always handled).
|
|
131
|
+
"""
|
|
132
|
+
console.print()
|
|
133
|
+
|
|
134
|
+
if image_tracker is None:
|
|
135
|
+
console.print("[yellow]Image tracking not available.[/yellow]")
|
|
136
|
+
console.print()
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
# Parse subcommand
|
|
140
|
+
if args is None or args.strip() == "" or args.strip().lower() == "list":
|
|
141
|
+
# List all images
|
|
142
|
+
images = image_tracker.list_images()
|
|
143
|
+
|
|
144
|
+
if not images:
|
|
145
|
+
console.print("[dim]No images in the current conversation.[/dim]")
|
|
146
|
+
console.print()
|
|
147
|
+
console.print(
|
|
148
|
+
"[dim]Tip: Paste an image with Ctrl+V or use @path/to/image.png[/dim]"
|
|
149
|
+
)
|
|
150
|
+
console.print()
|
|
151
|
+
return True
|
|
152
|
+
|
|
153
|
+
# Header
|
|
154
|
+
header = Text()
|
|
155
|
+
header.append("🖼️ ", style="bold")
|
|
156
|
+
header.append("Images in Conversation", style=f"bold {COLORS['primary']}")
|
|
157
|
+
|
|
158
|
+
console.print(Panel(header, border_style=COLORS["primary"]))
|
|
159
|
+
console.print()
|
|
160
|
+
|
|
161
|
+
# Create table
|
|
162
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 1))
|
|
163
|
+
table.add_column("ID", style="cyan")
|
|
164
|
+
table.add_column("Format", style="dim")
|
|
165
|
+
table.add_column("Size", justify="right")
|
|
166
|
+
table.add_column("Placeholder")
|
|
167
|
+
|
|
168
|
+
for img in images:
|
|
169
|
+
size_str = f"{img['size_kb']:.1f} KB"
|
|
170
|
+
table.add_row(
|
|
171
|
+
img["id"],
|
|
172
|
+
img["format"].upper(),
|
|
173
|
+
size_str,
|
|
174
|
+
img["placeholder"],
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
console.print(table)
|
|
178
|
+
console.print()
|
|
179
|
+
console.print(f"[dim]Total: {len(images)} image(s)[/dim]")
|
|
180
|
+
console.print()
|
|
181
|
+
console.print("[dim]Use /images remove <id> to remove an image[/dim]")
|
|
182
|
+
console.print("[dim]Use /images clear to remove all images[/dim]")
|
|
183
|
+
console.print()
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
arg_parts = args.strip().split(maxsplit=1)
|
|
187
|
+
subcmd = arg_parts[0].lower()
|
|
188
|
+
|
|
189
|
+
if subcmd == "remove":
|
|
190
|
+
if len(arg_parts) < 2:
|
|
191
|
+
console.print("[red]Usage: /images remove <id>[/red]")
|
|
192
|
+
console.print("[dim]Example: /images remove image-1[/dim]")
|
|
193
|
+
console.print()
|
|
194
|
+
return True
|
|
195
|
+
|
|
196
|
+
image_id = arg_parts[1].strip()
|
|
197
|
+
|
|
198
|
+
# Normalize ID format (allow "1" or "image-1")
|
|
199
|
+
if not image_id.startswith("image-"):
|
|
200
|
+
image_id = f"image-{image_id}"
|
|
201
|
+
|
|
202
|
+
if image_tracker.remove_image(image_id):
|
|
203
|
+
console.print(f"[green]Removed {image_id}[/green]")
|
|
204
|
+
else:
|
|
205
|
+
console.print(f"[red]Image not found: {image_id}[/red]")
|
|
206
|
+
# Show available images
|
|
207
|
+
images = image_tracker.list_images()
|
|
208
|
+
if images:
|
|
209
|
+
available = ", ".join(img["id"] for img in images)
|
|
210
|
+
console.print(f"[dim]Available images: {available}[/dim]")
|
|
211
|
+
console.print()
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
if subcmd == "clear":
|
|
215
|
+
count = image_tracker.count
|
|
216
|
+
if count == 0:
|
|
217
|
+
console.print("[dim]No images to clear.[/dim]")
|
|
218
|
+
else:
|
|
219
|
+
image_tracker.clear()
|
|
220
|
+
console.print(f"[green]Cleared {count} image(s) from conversation[/green]")
|
|
221
|
+
console.print()
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
console.print("[red]Unknown subcommand. Usage:[/red]")
|
|
225
|
+
console.print(" /images - List all images")
|
|
226
|
+
console.print(" /images list - List all images")
|
|
227
|
+
console.print(" /images remove <id> - Remove an image")
|
|
228
|
+
console.print(" /images clear - Clear all images")
|
|
229
|
+
console.print()
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
async def handle_restore_command(cmd_args: str | None) -> bool:
|
|
234
|
+
"""Handle /restore [index|path] — recover a file from the snapshot trash.
|
|
235
|
+
|
|
236
|
+
With no args: show an interactive numbered list of recent snapshots.
|
|
237
|
+
With an index (e.g. /restore 2) or a path (e.g. /restore src/foo.py):
|
|
238
|
+
restore that snapshot directly.
|
|
239
|
+
"""
|
|
240
|
+
mgr = get_recovery_manager()
|
|
241
|
+
if mgr is None:
|
|
242
|
+
console.print()
|
|
243
|
+
console.print("[yellow]No recovery manager active for this session.[/yellow]")
|
|
244
|
+
console.print()
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
snapshots = mgr.list_snapshots(include_past_sessions=True)
|
|
248
|
+
|
|
249
|
+
if not snapshots:
|
|
250
|
+
console.print()
|
|
251
|
+
console.print("[yellow]No file snapshots found.[/yellow]")
|
|
252
|
+
console.print(
|
|
253
|
+
"[dim]Snapshots are created automatically before rm, write_file, and edit_file.[/dim]"
|
|
254
|
+
)
|
|
255
|
+
console.print()
|
|
256
|
+
return True
|
|
257
|
+
|
|
258
|
+
# ------------------------------------------------------------------
|
|
259
|
+
# If the user gave an argument, try to resolve it without prompting
|
|
260
|
+
# ------------------------------------------------------------------
|
|
261
|
+
if cmd_args:
|
|
262
|
+
arg = cmd_args.strip()
|
|
263
|
+
# Try numeric index
|
|
264
|
+
if arg.isdigit():
|
|
265
|
+
idx = int(arg) - 1
|
|
266
|
+
if 0 <= idx < len(snapshots):
|
|
267
|
+
session_id, entry = snapshots[idx]
|
|
268
|
+
_do_restore(mgr, session_id, entry)
|
|
269
|
+
return True
|
|
270
|
+
console.print(f"[red]No snapshot at index {arg}.[/red]")
|
|
271
|
+
console.print()
|
|
272
|
+
return True
|
|
273
|
+
|
|
274
|
+
# Try path match (most recent matching snapshot)
|
|
275
|
+
for session_id, entry in snapshots:
|
|
276
|
+
if arg in entry.original_path or entry.original_path.endswith(arg):
|
|
277
|
+
_do_restore(mgr, session_id, entry)
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
console.print(f"[red]No snapshot found matching '{arg}'.[/red]")
|
|
281
|
+
console.print()
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
# ------------------------------------------------------------------
|
|
285
|
+
# Interactive list
|
|
286
|
+
# ------------------------------------------------------------------
|
|
287
|
+
console.print()
|
|
288
|
+
console.print("[bold]File snapshots[/bold] (newest first):")
|
|
289
|
+
console.print()
|
|
290
|
+
|
|
291
|
+
now = datetime.now()
|
|
292
|
+
|
|
293
|
+
for i, (session_id, entry) in enumerate(snapshots, 1):
|
|
294
|
+
label = REASON_LABELS.get(entry.reason, entry.reason)
|
|
295
|
+
try:
|
|
296
|
+
ts = datetime.fromisoformat(entry.timestamp)
|
|
297
|
+
delta = now - ts
|
|
298
|
+
secs = int(delta.total_seconds())
|
|
299
|
+
if secs < 60:
|
|
300
|
+
age = f"{secs}s ago"
|
|
301
|
+
elif secs < 3600:
|
|
302
|
+
age = f"{secs // 60}m ago"
|
|
303
|
+
elif secs < 86400:
|
|
304
|
+
age = f"{secs // 3600}h ago"
|
|
305
|
+
else:
|
|
306
|
+
age = f"{secs // 86400}d ago"
|
|
307
|
+
except Exception:
|
|
308
|
+
age = entry.timestamp
|
|
309
|
+
|
|
310
|
+
console.print(
|
|
311
|
+
f" [bold cyan][{i}][/bold cyan] {entry.original_path} "
|
|
312
|
+
f"[dim]— {label} ({age})[/dim]"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
console.print()
|
|
316
|
+
console.print(
|
|
317
|
+
"[dim]Restore which file? Enter a number, path, or q to cancel:[/dim] ", end=""
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
try:
|
|
321
|
+
choice = input().strip()
|
|
322
|
+
except (EOFError, KeyboardInterrupt):
|
|
323
|
+
console.print()
|
|
324
|
+
return True
|
|
325
|
+
|
|
326
|
+
if not choice or choice.lower() == "q":
|
|
327
|
+
console.print()
|
|
328
|
+
return True
|
|
329
|
+
|
|
330
|
+
if choice.isdigit():
|
|
331
|
+
idx = int(choice) - 1
|
|
332
|
+
if 0 <= idx < len(snapshots):
|
|
333
|
+
session_id, entry = snapshots[idx]
|
|
334
|
+
_do_restore(mgr, session_id, entry)
|
|
335
|
+
else:
|
|
336
|
+
console.print(f"[red]No snapshot at index {choice}.[/red]")
|
|
337
|
+
console.print()
|
|
338
|
+
else:
|
|
339
|
+
for session_id, entry in snapshots:
|
|
340
|
+
if choice in entry.original_path or entry.original_path.endswith(choice):
|
|
341
|
+
_do_restore(mgr, session_id, entry)
|
|
342
|
+
return True
|
|
343
|
+
console.print(f"[red]No snapshot found matching '{choice}'.[/red]")
|
|
344
|
+
console.print()
|
|
345
|
+
|
|
346
|
+
return True
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def _do_restore(mgr, session_id: str, entry) -> None:
|
|
350
|
+
"""Restore a single snapshot entry and print the result."""
|
|
351
|
+
ok = mgr.restore(entry, session_id=session_id)
|
|
352
|
+
console.print()
|
|
353
|
+
if ok:
|
|
354
|
+
console.print(f"[green]Restored:[/green] {entry.original_path}")
|
|
355
|
+
else:
|
|
356
|
+
console.print(f"[red]Failed to restore {entry.original_path}[/red]")
|
|
357
|
+
console.print(
|
|
358
|
+
"[dim]The snapshot file may have been deleted from the trash.[/dim]"
|
|
359
|
+
)
|
|
360
|
+
console.print()
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# ---------------------------------------------------------------------------
|
|
364
|
+
# Registry
|
|
365
|
+
# ---------------------------------------------------------------------------
|
|
366
|
+
|
|
367
|
+
def register_commands(registry) -> None:
|
|
368
|
+
from novacode_cli.commands import CommandContext
|
|
369
|
+
|
|
370
|
+
async def _handle_files(ctx: CommandContext) -> bool:
|
|
371
|
+
return await handle_files_command()
|
|
372
|
+
|
|
373
|
+
async def _handle_images(ctx: CommandContext) -> bool:
|
|
374
|
+
return await handle_images_command(ctx.cmd_args, ctx.image_tracker)
|
|
375
|
+
|
|
376
|
+
async def _handle_restore(ctx: CommandContext) -> bool:
|
|
377
|
+
return await handle_restore_command(ctx.cmd_args)
|
|
378
|
+
|
|
379
|
+
registry.register("files", _handle_files)
|
|
380
|
+
registry.register("images", _handle_images)
|
|
381
|
+
registry.register("restore", _handle_restore)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Local handoff controls shared by TUI, console and remote command dispatch."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from novacode_cli.handoff import HandoffMemory, clear_deleted_handoff, document, prepare
|
|
8
|
+
from novacode_cli.prime_memory import session_directory
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from novacode_cli.commands import CommandContext, CommandRegistry
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def handle_passing_command(session: Any, args: str = "") -> str: # noqa: ANN401, PLR0911 - Command grammar.
|
|
15
|
+
"""Toggle or inspect context without starting work or restoring approval decisions."""
|
|
16
|
+
parts = args.strip().split()
|
|
17
|
+
action = parts[0].lower() if parts else "list"
|
|
18
|
+
if action in {"on", "off", "status"} and len(parts) == 1:
|
|
19
|
+
if action != "status":
|
|
20
|
+
session.handoff_enabled = action == "on"
|
|
21
|
+
state = "on" if getattr(session, "handoff_enabled", False) is True else "off"
|
|
22
|
+
return f"Passing mode: {state}. Saves project context during subsequent requests."
|
|
23
|
+
try:
|
|
24
|
+
if action == "list" and len(parts) <= 1:
|
|
25
|
+
records = HandoffMemory(session_directory(session)).records()
|
|
26
|
+
return "\n".join(f"{r['id']} · {r['title']} · {r['status']}" for r in records) or (
|
|
27
|
+
"No saved handoffs in this project. "
|
|
28
|
+
"Enable Passing beside the composer, then send a task."
|
|
29
|
+
)
|
|
30
|
+
if action == "preview" and len(parts) == 2: # noqa: PLR2004 - Command grammar.
|
|
31
|
+
record = HandoffMemory(session_directory(session)).get(parts[1])
|
|
32
|
+
return document(record) if record else "Handoff not found in this project."
|
|
33
|
+
if action == "resume" and len(parts) == 2: # noqa: PLR2004 - Command grammar.
|
|
34
|
+
return prepare(session, parts[1])
|
|
35
|
+
if action == "delete" and len(parts) in {2, 3}:
|
|
36
|
+
memory = HandoffMemory(session_directory(session))
|
|
37
|
+
identity = parts[1]
|
|
38
|
+
record = memory.get_for_deletion(identity)
|
|
39
|
+
if record is None:
|
|
40
|
+
return "Handoff not found in this project."
|
|
41
|
+
target = memory.context["project"] + ":" + identity
|
|
42
|
+
if parts[-1] != "confirm" or getattr(session, "handoff_delete_pending", None) != target:
|
|
43
|
+
session.handoff_delete_pending = target
|
|
44
|
+
return (
|
|
45
|
+
f"Confirm deletion of {record['title']} with "
|
|
46
|
+
f"/passing delete {identity} confirm. "
|
|
47
|
+
"Project files and conversation history stay intact."
|
|
48
|
+
)
|
|
49
|
+
memory.delete(identity)
|
|
50
|
+
clear_deleted_handoff(session, identity)
|
|
51
|
+
return f"Handoff deleted: {record['title']}."
|
|
52
|
+
except Exception: # noqa: BLE001 - Storage errors remain temporary local notices.
|
|
53
|
+
return "Handoff storage unavailable. Your workspace and conversation are unchanged."
|
|
54
|
+
return (
|
|
55
|
+
"Usage: /passing [on|off|status|list|preview <id>|resume <id>|delete <id> [confirm]] "
|
|
56
|
+
"· /skill:handoff invokes the skill."
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def register_commands(registry: CommandRegistry) -> None:
|
|
61
|
+
"""Make handoff commands available through the canonical registry."""
|
|
62
|
+
from novacode_cli.config.config import console
|
|
63
|
+
|
|
64
|
+
async def handler(ctx: CommandContext) -> bool:
|
|
65
|
+
console.print(handle_passing_command(ctx.session_state, ctx.cmd_args or ""), markup=False)
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
registry.register("passing", handler)
|