sliceagent 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sliceagent-0.1.1 → sliceagent-0.1.2}/CHANGELOG.md +14 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/PKG-INFO +1 -1
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/__init__.py +1 -1
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/cli.py +7 -1
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/sensory_cortex.py +24 -3
- sliceagent-0.1.2/tests/test_repo_map_bounded.py +91 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.dockerignore +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.env.example +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/dependabot.yml +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/workflows/ci.yml +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.github/workflows/publish.yml +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/.gitignore +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/CODE_OF_CONDUCT.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/CONTRIBUTING.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/Dockerfile +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/LICENSE +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/NOTICE +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/QUICKSTART.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/README.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/SECURITY.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/examples/plugins/hello/__init__.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/examples/plugins/hello/plugin.toml +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/install.sh +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/pyproject.toml +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/scripts/gen_config_reference.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/scripts/run_tests.sh +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/sliceagent.toml.example +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/__main__.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/access.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/agents.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/background_review.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/binsniff.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/clock.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/code_grep.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/code_index.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/config.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/context_overflow.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/envspec.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/errors.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/events.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/finding_types.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/flags.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/fuzzy.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/guardrails.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/guidance.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/hippocampus.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/hooks.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/interfaces.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/llm.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/loop.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/mcp_client.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/mcp_security.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/memory.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/metrics.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/model_catalog.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/monitor.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/neocortex.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/onboarding.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/oracle.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/pagetable.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/pfc.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/plugins.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/policy.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/procman.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/prompt.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/records.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/recovery.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/regions.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/registry.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/retriever.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/safety.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/sandbox.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/scheduler.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/search_index.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/seed.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/session.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/skill_provenance.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/skill_usage.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/skills.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/subagent.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/subdir_hints.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/swap.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/taskstate.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/telemetry.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/terminal.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/text_utils.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/tool_summary.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/tools.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/tui.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/src/sliceagent/web.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/README.md +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_active_focus.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_agents.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_ask_echo.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_ask_user.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_background_review.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_binary_view.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bound_is_relevance.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_breadth_e2e.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_2b.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_breadth_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_current_request.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_linenum.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_loop_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_memory_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_p0a.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_p0b.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_p0c.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_selfreview.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_selfreview_siblings.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_skills_h1.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_slice_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_subagent_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bugfix_tools_wave.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_bughunt_fixes.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_cache_manifest.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_checkpoint.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_cli_smoke.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_closure.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_code_grep.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_code_index.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_consolidate.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_context_overflow.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_conversation.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_coresidency.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_echo_before_blocking.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_episode.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_errors_backoff.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_esc_sentinel.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_exec_env.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_finding_types.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_flags.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_fuzzy.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_ghost_index.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_guardrails.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_guidance.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_history.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_invariant_fixes.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_live_composer.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_llm_streaming.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_llm_usage_cache.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_llm_watchdog.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_log_redaction.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_loop_overflow.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_mcp_output_cap.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_mcp_runtime.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_memory.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_memory_persist.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_metrics.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_mining.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_mission_tier.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_model_catalog.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_monitor.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_onboarding.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_pageout_compaction.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_permission_patterns.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_pinned_composer.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_plan_tier.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_procman.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_product_features.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_progress_status.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_prompt_ab.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_provenance.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_proxy_interrupt.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_reach.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_readonly_subagent.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_recall_search.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_records.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_refault.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_registry_validation.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_reliability.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_requirements.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_review_fixes.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_route_lexical.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_safety.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_seal_markdown.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_search_index.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_skill_meta.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_skill_metadata.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_subdir_hints.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_task_agnostic.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_task_state_roundtrip.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_telemetry_convergence.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_terminal.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tool_dedup.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tool_result_ok.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tools_robust.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_topic_switch.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_trajectory.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tui.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tui_menus.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tui_render.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_tui_widgets.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_turn_budget.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_verification_agent.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_web.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_workspace.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_world_region.py +0 -0
- {sliceagent-0.1.1 → sliceagent-0.1.2}/tests/test_world_state.py +0 -0
|
@@ -5,6 +5,20 @@ this project aims for [Semantic Versioning](https://semver.org/).
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.1.2] — 2026-07-02
|
|
9
|
+
|
|
10
|
+
Nothing may hang the user — three fixes from a live first-run reproduction.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Repo-map walk is hard-bounded** (`max_dirs` budget): the first slice build can no longer hang for
|
|
14
|
+
minutes when the workspace root is huge (a home directory mistaken for a project, a giant monorepo).
|
|
15
|
+
Output caps existed; now the walk itself is bounded — worst case ~1s, maps what it saw.
|
|
16
|
+
- **The OS-account home never counts as a project root**, independent of `$HOME`: a stray
|
|
17
|
+
`package.json` in the real home no longer turns the entire home directory into a "project"
|
|
18
|
+
(the prior guard compared against `$HOME`, which containers/sandboxes/sudo can override).
|
|
19
|
+
- **Ctrl-C during the slice build cancels the turn cleanly** (`· cancelled`) instead of crashing the
|
|
20
|
+
REPL with a traceback — the build phase ran before the turn's interrupt handling.
|
|
21
|
+
|
|
8
22
|
## [0.1.1] — 2026-07-02
|
|
9
23
|
|
|
10
24
|
First-run onboarding, hardened by a live stranger walkthrough.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sliceagent
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A coding agent with a new context-engineering framework: bounded, deterministic, reconstructed context (slice architecture) — built for long-horizon work.
|
|
5
5
|
Author: TT-Wang
|
|
6
6
|
License-Expression: MIT
|
|
@@ -831,7 +831,13 @@ def main() -> None:
|
|
|
831
831
|
_stats["topic"] = one_line(session.active().goal, 40) if session.active_id else ""
|
|
832
832
|
record_user(session.active(), line) # short-range continuity: the RECENT CONVERSATION tier
|
|
833
833
|
_expand_mentions(line) # @path → pin the file into OPEN FILES
|
|
834
|
-
|
|
834
|
+
try:
|
|
835
|
+
# slice-build phase happens BEFORE run_turn's own KeyboardInterrupt handling — a ctrl-c
|
|
836
|
+
# here (e.g. during the one-time repo-map build) must cancel the turn, not crash the REPL.
|
|
837
|
+
build = make_build_slice(session, tools, retriever, memory, line, session.session_id)
|
|
838
|
+
except KeyboardInterrupt:
|
|
839
|
+
print("\n · cancelled")
|
|
840
|
+
continue
|
|
835
841
|
if os.environ.get("AGENT_TIMING"): # per-turn latency breakdown (build vs model) → find the hang
|
|
836
842
|
import time as _tt
|
|
837
843
|
_b = build
|
|
@@ -94,6 +94,17 @@ def _home() -> Optional[Path]:
|
|
|
94
94
|
return None
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def _os_home() -> Optional[Path]:
|
|
98
|
+
"""The account's home from the OS user database — independent of the $HOME env var. Guards the
|
|
99
|
+
home-is-not-a-project rule even when $HOME is overridden (containers, sudo, test sandboxes):
|
|
100
|
+
without this, a stray package.json in the real home turns the whole home dir into a 'project'."""
|
|
101
|
+
try:
|
|
102
|
+
import pwd
|
|
103
|
+
return Path(pwd.getpwuid(os.getuid()).pw_dir).resolve()
|
|
104
|
+
except (ImportError, KeyError, OSError): # pwd is POSIX-only; degrade to the env-based home
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
97
108
|
def _marker_root(cwd: Path) -> Optional[Path]:
|
|
98
109
|
"""Nearest ancestor (≤6 levels) that looks like a project root, or ``None``.
|
|
99
110
|
|
|
@@ -104,11 +115,11 @@ def _marker_root(cwd: Path) -> Optional[Path]:
|
|
|
104
115
|
current = cwd.resolve()
|
|
105
116
|
except (OSError, RuntimeError, ValueError):
|
|
106
117
|
return None
|
|
107
|
-
|
|
118
|
+
homes = {h for h in (_home(), _os_home()) if h is not None}
|
|
108
119
|
for depth, parent in enumerate([current, *current.parents]):
|
|
109
120
|
if depth > 6:
|
|
110
121
|
break
|
|
111
|
-
if parent
|
|
122
|
+
if parent in homes:
|
|
112
123
|
continue
|
|
113
124
|
try:
|
|
114
125
|
for marker in _PROJECT_MARKERS:
|
|
@@ -443,7 +454,8 @@ _CODE_SUFFIX = (".py", ".js", ".ts", ".jsx", ".tsx", ".go", ".rs", ".java", ".rb
|
|
|
443
454
|
".exs", ".clj", ".r", ".jl", ".vue", ".sql")
|
|
444
455
|
|
|
445
456
|
|
|
446
|
-
def repo_map(root: str, *, max_entries: int = 300, max_per_dir: int = 25, max_chars: int = 12000
|
|
457
|
+
def repo_map(root: str, *, max_entries: int = 300, max_per_dir: int = 25, max_chars: int = 12000,
|
|
458
|
+
max_dirs: int = 4000) -> str:
|
|
447
459
|
"""A compact, ignore-aware STRUCTURAL MAP of the project (SENSORY CORTEX — the derived-view tier-B
|
|
448
460
|
resident view, memoized for the session, never a persisted store): directories with their files,
|
|
449
461
|
pruned of VCS/venv/cache + asset/log noise, RANKED by source-density so the real code tree shows
|
|
@@ -456,8 +468,17 @@ def repo_map(root: str, *, max_entries: int = 300, max_per_dir: int = 25, max_ch
|
|
|
456
468
|
if not root or not os.path.isdir(root):
|
|
457
469
|
return ""
|
|
458
470
|
rows: list[tuple[str, list[str], int, int]] = [] # (rel, files, total, code_count)
|
|
471
|
+
walked = 0
|
|
459
472
|
try:
|
|
460
473
|
for dirpath, dirnames, filenames in os.walk(root): # symlinks not followed
|
|
474
|
+
# HARD WALK BUDGET — the invariant is that repo_map completes in bounded time on ANY root.
|
|
475
|
+
# max_entries/max_chars cap the OUTPUT but only after the walk; without this, a huge root
|
|
476
|
+
# (a home dir mistaken for a project, a giant monorepo) hangs the first slice build for
|
|
477
|
+
# minutes inside os.walk. Past the budget we map what we saw — bounded beats complete.
|
|
478
|
+
walked += 1
|
|
479
|
+
if walked > max_dirs:
|
|
480
|
+
dirnames[:] = []
|
|
481
|
+
break
|
|
461
482
|
dirnames[:] = sorted(d for d in dirnames if not _is_ignored(d))
|
|
462
483
|
files = sorted(f for f in filenames
|
|
463
484
|
if not _is_ignored(f) and not f.endswith(_MAP_SKIP_SUFFIX))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""The repo-map walk must be BOUNDED — it can never hang the first slice build, no matter what the
|
|
2
|
+
workspace root is (a huge monorepo, or a home dir that a stray package.json turned into a 'project').
|
|
3
|
+
Found live: launching from /Users/<u> with HOME overridden made os.walk crawl the entire home dir for
|
|
4
|
+
minutes; ctrl-c then killed the REPL with a traceback. Invariant: repo_map completes within its walk
|
|
5
|
+
budget on ANY root, and the OS-account home never counts as a project root regardless of $HOME.
|
|
6
|
+
Run: PYTHONPATH=src python tests/test_repo_map_bounded.py
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import pathlib
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
|
|
14
|
+
|
|
15
|
+
from sliceagent import sensory_cortex as sc # noqa: E402
|
|
16
|
+
|
|
17
|
+
CHECKS = []
|
|
18
|
+
def check(fn):
|
|
19
|
+
CHECKS.append(fn)
|
|
20
|
+
return fn
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _make_tree(root: pathlib.Path, n_dirs: int) -> None:
|
|
24
|
+
for i in range(n_dirs):
|
|
25
|
+
d = root / f"pkg{i:03d}"
|
|
26
|
+
d.mkdir(parents=True)
|
|
27
|
+
(d / "mod.py").write_text("x = 1\n")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@check
|
|
31
|
+
def walk_stops_at_the_dir_budget():
|
|
32
|
+
import tempfile
|
|
33
|
+
root = pathlib.Path(tempfile.mkdtemp())
|
|
34
|
+
_make_tree(root, 60)
|
|
35
|
+
out = sc.repo_map(str(root), max_dirs=10)
|
|
36
|
+
# bounded: only what the budget allowed got mapped (root dir itself may count as one visit)
|
|
37
|
+
mapped = [ln for ln in out.splitlines() if ln.strip()]
|
|
38
|
+
assert 0 < len(mapped) <= 10, f"expected ≤10 mapped dirs under a 10-dir budget, got {len(mapped)}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@check
|
|
42
|
+
def bounded_walk_is_fast_even_on_a_big_tree():
|
|
43
|
+
import tempfile
|
|
44
|
+
root = pathlib.Path(tempfile.mkdtemp())
|
|
45
|
+
_make_tree(root, 300)
|
|
46
|
+
t0 = time.monotonic()
|
|
47
|
+
out = sc.repo_map(str(root), max_dirs=50)
|
|
48
|
+
dt = time.monotonic() - t0
|
|
49
|
+
assert out, "map must still be produced within the budget"
|
|
50
|
+
assert dt < 5.0, f"bounded walk took {dt:.1f}s — the budget is not bounding"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@check
|
|
54
|
+
def default_budget_maps_a_normal_project_fully():
|
|
55
|
+
import tempfile
|
|
56
|
+
root = pathlib.Path(tempfile.mkdtemp())
|
|
57
|
+
_make_tree(root, 20)
|
|
58
|
+
out = sc.repo_map(str(root)) # default max_dirs — far above a normal project
|
|
59
|
+
assert sum(1 for ln in out.splitlines() if "mod.py" in ln) == 20, "normal projects must be unaffected"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@check
|
|
63
|
+
def os_account_home_never_counts_as_a_project_even_with_home_overridden():
|
|
64
|
+
import tempfile
|
|
65
|
+
fake_os_home = pathlib.Path(tempfile.mkdtemp()) # stands in for pw_dir
|
|
66
|
+
(fake_os_home / "package.json").write_text("{}") # the stray marker that caused the live bug
|
|
67
|
+
orig = sc._os_home
|
|
68
|
+
sc._os_home = lambda: fake_os_home.resolve()
|
|
69
|
+
try:
|
|
70
|
+
# $HOME points elsewhere (the live-repro condition), yet the OS home must still be skipped
|
|
71
|
+
assert sc._marker_root(fake_os_home) is None, \
|
|
72
|
+
"a marker in the OS-account home must not make the home a project"
|
|
73
|
+
# ...while a real project UNDER the home is still detected
|
|
74
|
+
proj = fake_os_home / "code" / "myproj"
|
|
75
|
+
proj.mkdir(parents=True)
|
|
76
|
+
(proj / "pyproject.toml").write_text("[project]\nname='x'\n")
|
|
77
|
+
got = sc._marker_root(proj)
|
|
78
|
+
assert got is not None and got.name == "myproj", f"real subproject must still resolve, got {got}"
|
|
79
|
+
finally:
|
|
80
|
+
sc._os_home = orig
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
ok = 0
|
|
85
|
+
for fn in CHECKS:
|
|
86
|
+
try:
|
|
87
|
+
fn(); ok += 1; print(f"PASS {fn.__name__}")
|
|
88
|
+
except Exception as e: # noqa: BLE001
|
|
89
|
+
print(f"FAIL {fn.__name__}: {type(e).__name__}: {e}")
|
|
90
|
+
print(f"\n{ok}/{len(CHECKS)} passed")
|
|
91
|
+
sys.exit(0 if ok == len(CHECKS) else 1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|