sliceagent 0.1.0__tar.gz → 0.1.1__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.
Files changed (202) hide show
  1. {sliceagent-0.1.0 → sliceagent-0.1.1}/CHANGELOG.md +17 -0
  2. {sliceagent-0.1.0 → sliceagent-0.1.1}/PKG-INFO +18 -8
  3. {sliceagent-0.1.0 → sliceagent-0.1.1}/README.md +17 -7
  4. {sliceagent-0.1.0 → sliceagent-0.1.1}/install.sh +40 -4
  5. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/__init__.py +1 -1
  6. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/cli.py +29 -5
  7. sliceagent-0.1.1/tests/test_cli_smoke.py +159 -0
  8. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_prompt_ab.py +6 -0
  9. sliceagent-0.1.0/tests/test_cli_smoke.py +0 -41
  10. {sliceagent-0.1.0 → sliceagent-0.1.1}/.dockerignore +0 -0
  11. {sliceagent-0.1.0 → sliceagent-0.1.1}/.env.example +0 -0
  12. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  13. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/dependabot.yml +0 -0
  16. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/workflows/ci.yml +0 -0
  17. {sliceagent-0.1.0 → sliceagent-0.1.1}/.github/workflows/publish.yml +0 -0
  18. {sliceagent-0.1.0 → sliceagent-0.1.1}/.gitignore +0 -0
  19. {sliceagent-0.1.0 → sliceagent-0.1.1}/CODE_OF_CONDUCT.md +0 -0
  20. {sliceagent-0.1.0 → sliceagent-0.1.1}/CONTRIBUTING.md +0 -0
  21. {sliceagent-0.1.0 → sliceagent-0.1.1}/Dockerfile +0 -0
  22. {sliceagent-0.1.0 → sliceagent-0.1.1}/LICENSE +0 -0
  23. {sliceagent-0.1.0 → sliceagent-0.1.1}/NOTICE +0 -0
  24. {sliceagent-0.1.0 → sliceagent-0.1.1}/QUICKSTART.md +0 -0
  25. {sliceagent-0.1.0 → sliceagent-0.1.1}/SECURITY.md +0 -0
  26. {sliceagent-0.1.0 → sliceagent-0.1.1}/examples/plugins/hello/__init__.py +0 -0
  27. {sliceagent-0.1.0 → sliceagent-0.1.1}/examples/plugins/hello/plugin.toml +0 -0
  28. {sliceagent-0.1.0 → sliceagent-0.1.1}/pyproject.toml +0 -0
  29. {sliceagent-0.1.0 → sliceagent-0.1.1}/scripts/gen_config_reference.py +0 -0
  30. {sliceagent-0.1.0 → sliceagent-0.1.1}/scripts/run_tests.sh +0 -0
  31. {sliceagent-0.1.0 → sliceagent-0.1.1}/sliceagent.toml.example +0 -0
  32. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/__main__.py +0 -0
  33. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/access.py +0 -0
  34. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/agents.py +0 -0
  35. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/background_review.py +0 -0
  36. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/binsniff.py +0 -0
  37. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/clock.py +0 -0
  38. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/code_grep.py +0 -0
  39. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/code_index.py +0 -0
  40. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/config.py +0 -0
  41. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/context_overflow.py +0 -0
  42. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/envspec.py +0 -0
  43. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/errors.py +0 -0
  44. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/events.py +0 -0
  45. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/finding_types.py +0 -0
  46. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/flags.py +0 -0
  47. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/fuzzy.py +0 -0
  48. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/guardrails.py +0 -0
  49. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/guidance.py +0 -0
  50. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/hippocampus.py +0 -0
  51. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/hooks.py +0 -0
  52. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/interfaces.py +0 -0
  53. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/llm.py +0 -0
  54. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/loop.py +0 -0
  55. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/mcp_client.py +0 -0
  56. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/mcp_security.py +0 -0
  57. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/memory.py +0 -0
  58. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/metrics.py +0 -0
  59. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/model_catalog.py +0 -0
  60. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/monitor.py +0 -0
  61. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/neocortex.py +0 -0
  62. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/onboarding.py +0 -0
  63. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/oracle.py +0 -0
  64. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/pagetable.py +0 -0
  65. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/pfc.py +0 -0
  66. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/plugins.py +0 -0
  67. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/policy.py +0 -0
  68. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/procman.py +0 -0
  69. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/prompt.py +0 -0
  70. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/records.py +0 -0
  71. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/recovery.py +0 -0
  72. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/regions.py +0 -0
  73. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/registry.py +0 -0
  74. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/retriever.py +0 -0
  75. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/safety.py +0 -0
  76. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/sandbox.py +0 -0
  77. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/scheduler.py +0 -0
  78. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/search_index.py +0 -0
  79. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/seed.py +0 -0
  80. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/sensory_cortex.py +0 -0
  81. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/session.py +0 -0
  82. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/skill_provenance.py +0 -0
  83. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/skill_usage.py +0 -0
  84. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/skills.py +0 -0
  85. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/subagent.py +0 -0
  86. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/subdir_hints.py +0 -0
  87. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/swap.py +0 -0
  88. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/taskstate.py +0 -0
  89. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/telemetry.py +0 -0
  90. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/terminal.py +0 -0
  91. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/text_utils.py +0 -0
  92. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/tool_summary.py +0 -0
  93. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/tools.py +0 -0
  94. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/tui.py +0 -0
  95. {sliceagent-0.1.0 → sliceagent-0.1.1}/src/sliceagent/web.py +0 -0
  96. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/README.md +0 -0
  97. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_active_focus.py +0 -0
  98. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_agents.py +0 -0
  99. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_ask_echo.py +0 -0
  100. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_ask_user.py +0 -0
  101. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_background_review.py +0 -0
  102. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_binary_view.py +0 -0
  103. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bound_is_relevance.py +0 -0
  104. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_breadth_e2e.py +0 -0
  105. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_2b.py +0 -0
  106. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_breadth_wave.py +0 -0
  107. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_current_request.py +0 -0
  108. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_linenum.py +0 -0
  109. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_loop_wave.py +0 -0
  110. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_memory_wave.py +0 -0
  111. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_p0a.py +0 -0
  112. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_p0b.py +0 -0
  113. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_p0c.py +0 -0
  114. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_selfreview.py +0 -0
  115. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_selfreview_siblings.py +0 -0
  116. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_skills_h1.py +0 -0
  117. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_slice_wave.py +0 -0
  118. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_subagent_wave.py +0 -0
  119. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bugfix_tools_wave.py +0 -0
  120. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_bughunt_fixes.py +0 -0
  121. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_cache_manifest.py +0 -0
  122. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_checkpoint.py +0 -0
  123. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_closure.py +0 -0
  124. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_code_grep.py +0 -0
  125. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_code_index.py +0 -0
  126. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_consolidate.py +0 -0
  127. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_context_overflow.py +0 -0
  128. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_conversation.py +0 -0
  129. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_coresidency.py +0 -0
  130. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_echo_before_blocking.py +0 -0
  131. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_episode.py +0 -0
  132. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_errors_backoff.py +0 -0
  133. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_esc_sentinel.py +0 -0
  134. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_exec_env.py +0 -0
  135. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_finding_types.py +0 -0
  136. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_flags.py +0 -0
  137. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_fuzzy.py +0 -0
  138. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_ghost_index.py +0 -0
  139. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_guardrails.py +0 -0
  140. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_guidance.py +0 -0
  141. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_history.py +0 -0
  142. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_invariant_fixes.py +0 -0
  143. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_live_composer.py +0 -0
  144. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_llm_streaming.py +0 -0
  145. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_llm_usage_cache.py +0 -0
  146. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_llm_watchdog.py +0 -0
  147. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_log_redaction.py +0 -0
  148. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_loop_overflow.py +0 -0
  149. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_mcp_output_cap.py +0 -0
  150. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_mcp_runtime.py +0 -0
  151. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_memory.py +0 -0
  152. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_memory_persist.py +0 -0
  153. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_metrics.py +0 -0
  154. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_mining.py +0 -0
  155. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_mission_tier.py +0 -0
  156. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_model_catalog.py +0 -0
  157. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_monitor.py +0 -0
  158. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_onboarding.py +0 -0
  159. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_pageout_compaction.py +0 -0
  160. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_permission_patterns.py +0 -0
  161. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_pinned_composer.py +0 -0
  162. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_plan_tier.py +0 -0
  163. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_procman.py +0 -0
  164. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_product_features.py +0 -0
  165. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_progress_status.py +0 -0
  166. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_provenance.py +0 -0
  167. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_proxy_interrupt.py +0 -0
  168. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_reach.py +0 -0
  169. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_readonly_subagent.py +0 -0
  170. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_recall_search.py +0 -0
  171. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_records.py +0 -0
  172. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_refault.py +0 -0
  173. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_registry_validation.py +0 -0
  174. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_reliability.py +0 -0
  175. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_requirements.py +0 -0
  176. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_review_fixes.py +0 -0
  177. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_route_lexical.py +0 -0
  178. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_safety.py +0 -0
  179. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_seal_markdown.py +0 -0
  180. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_search_index.py +0 -0
  181. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_skill_meta.py +0 -0
  182. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_skill_metadata.py +0 -0
  183. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_subdir_hints.py +0 -0
  184. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_task_agnostic.py +0 -0
  185. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_task_state_roundtrip.py +0 -0
  186. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_telemetry_convergence.py +0 -0
  187. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_terminal.py +0 -0
  188. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tool_dedup.py +0 -0
  189. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tool_result_ok.py +0 -0
  190. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tools_robust.py +0 -0
  191. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_topic_switch.py +0 -0
  192. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_trajectory.py +0 -0
  193. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tui.py +0 -0
  194. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tui_menus.py +0 -0
  195. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tui_render.py +0 -0
  196. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_tui_widgets.py +0 -0
  197. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_turn_budget.py +0 -0
  198. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_verification_agent.py +0 -0
  199. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_web.py +0 -0
  200. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_workspace.py +0 -0
  201. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_world_region.py +0 -0
  202. {sliceagent-0.1.0 → sliceagent-0.1.1}/tests/test_world_state.py +0 -0
@@ -5,6 +5,23 @@ this project aims for [Semantic Versioning](https://semver.org/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.1.1] — 2026-07-02
9
+
10
+ First-run onboarding, hardened by a live stranger walkthrough.
11
+
12
+ ### Added
13
+ - **Auto-onboarding** — a bare interactive `sliceagent` with nothing configured now drops straight
14
+ into the guided setup wizard (provider → key → model → live test), then continues into the session.
15
+ No more "go run `sliceagent init`" bounce. Piped/CI runs keep the print-and-exit gate.
16
+ - **Installer handles everything** — `install.sh` now also installs ripgrep (brew when available,
17
+ else an isolated ~2 MB static binary; no sudo) and pins `uv tool install --python 3.12`, so the
18
+ curl path has zero prerequisites even when the default Python is 3.9/3.10 (conda base, Ubuntu 22.04).
19
+
20
+ ### Changed
21
+ - README: the curl installer is the primary install path; PyPI/pip is the "you manage the Python"
22
+ alternative. (Project renamed memagent → sliceagent the same day, before 0.1.1: PyPI's
23
+ name-similarity check blocks "memagent".)
24
+
8
25
  ## [0.1.0] — 2026-07-02
9
26
 
10
27
  First public release.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sliceagent
3
- Version: 0.1.0
3
+ Version: 0.1.1
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
@@ -116,21 +116,31 @@ Early, but the **core bet is validated** — see the measured head-to-head bench
116
116
 
117
117
  ## Install
118
118
 
119
- Straight from PyPI (any one of):
119
+ One command Linux, macOS, WSL2:
120
120
 
121
121
  ```bash
122
- uv tool install "sliceagent[tui]" # uv (recommended)
123
- pipx install "sliceagent[tui]" # pipx
124
- pip install "sliceagent[tui]" # plain pip
122
+ curl -fsSL https://raw.githubusercontent.com/TT-Wang/sliceagent/main/install.sh | sh
125
123
  ```
126
124
 
127
- Or the one-command bootstrap (installs `uv` if needed, then sliceagent in an isolated tool env):
125
+ **The installer handles everything**: `uv`, its own Python 3.12, ripgrep, and sliceagent in an isolated tool env, no sudo, no prerequisites, no conflicts with any Python you already have (conda base at 3.10? Rosetta-Intel conda on an M-series Mac? Doesn't matter). Then:
128
126
 
129
127
  ```bash
130
- curl -fsSL https://raw.githubusercontent.com/TT-Wang/sliceagent/main/install.sh | sh
128
+ sliceagent # first run drops you straight into guided setup, then start chatting
131
129
  ```
132
130
 
133
- Footprint is light (no torch). `pip install -e .` works for a clone too. `ripgrep` is recommended (code search degrades gracefully without it). Homebrew / Docker arrive in v0.2.
131
+ <details>
132
+ <summary><b>Alternative: install from PyPI yourself</b> (you manage the Python — needs ≥ 3.11)</summary>
133
+
134
+ ```bash
135
+ uv tool install --python 3.12 "sliceagent[tui]" # uv — fetches Python itself
136
+ pipx install "sliceagent[tui]" # pipx
137
+ pip install "sliceagent[tui]" # plain pip (use a venv)
138
+ ```
139
+
140
+ If `pip` refuses with `Requires-Python >=3.11`: `conda create -n sliceagent python=3.12 -y && conda activate sliceagent`, then pip install. `ripgrep` is recommended (code search degrades gracefully without it).
141
+ </details>
142
+
143
+ Footprint is light (no torch). `pip install -e .` works for a clone too. Homebrew / Docker arrive in v0.2.
134
144
 
135
145
  ## Quickstart
136
146
 
@@ -83,21 +83,31 @@ Early, but the **core bet is validated** — see the measured head-to-head bench
83
83
 
84
84
  ## Install
85
85
 
86
- Straight from PyPI (any one of):
86
+ One command Linux, macOS, WSL2:
87
87
 
88
88
  ```bash
89
- uv tool install "sliceagent[tui]" # uv (recommended)
90
- pipx install "sliceagent[tui]" # pipx
91
- pip install "sliceagent[tui]" # plain pip
89
+ curl -fsSL https://raw.githubusercontent.com/TT-Wang/sliceagent/main/install.sh | sh
92
90
  ```
93
91
 
94
- Or the one-command bootstrap (installs `uv` if needed, then sliceagent in an isolated tool env):
92
+ **The installer handles everything**: `uv`, its own Python 3.12, ripgrep, and sliceagent in an isolated tool env, no sudo, no prerequisites, no conflicts with any Python you already have (conda base at 3.10? Rosetta-Intel conda on an M-series Mac? Doesn't matter). Then:
95
93
 
96
94
  ```bash
97
- curl -fsSL https://raw.githubusercontent.com/TT-Wang/sliceagent/main/install.sh | sh
95
+ sliceagent # first run drops you straight into guided setup, then start chatting
98
96
  ```
99
97
 
100
- Footprint is light (no torch). `pip install -e .` works for a clone too. `ripgrep` is recommended (code search degrades gracefully without it). Homebrew / Docker arrive in v0.2.
98
+ <details>
99
+ <summary><b>Alternative: install from PyPI yourself</b> (you manage the Python — needs ≥ 3.11)</summary>
100
+
101
+ ```bash
102
+ uv tool install --python 3.12 "sliceagent[tui]" # uv — fetches Python itself
103
+ pipx install "sliceagent[tui]" # pipx
104
+ pip install "sliceagent[tui]" # plain pip (use a venv)
105
+ ```
106
+
107
+ If `pip` refuses with `Requires-Python >=3.11`: `conda create -n sliceagent python=3.12 -y && conda activate sliceagent`, then pip install. `ripgrep` is recommended (code search degrades gracefully without it).
108
+ </details>
109
+
110
+ Footprint is light (no torch). `pip install -e .` works for a clone too. Homebrew / Docker arrive in v0.2.
101
111
 
102
112
  ## Quickstart
103
113
 
@@ -44,16 +44,52 @@ if ! command -v uv >/dev/null 2>&1; then
44
44
  fi
45
45
 
46
46
  # 2. install (or upgrade) sliceagent as an isolated uv tool
47
+ # --python 3.12: don't inherit whatever python happens to be on PATH (conda base = 3.10,
48
+ # Ubuntu 22.04 = 3.10, macOS system = 3.9 — all below the >=3.11 floor). uv fetches a managed
49
+ # CPython 3.12 automatically when none is installed, so the installer has zero prerequisites.
47
50
  info "Installing sliceagent …"
48
- uv tool install --force "$PKG"
51
+ uv tool install --force --python 3.12 "$PKG"
49
52
 
50
53
  # 3. make sure uv's tool bin is on PATH for future shells
51
54
  uv tool update-shell >/dev/null 2>&1 || warn "Could not auto-update PATH — you may need to add uv's tool bin (see 'uv tool dir') to your PATH."
52
55
 
53
- # 4. soft prerequisite: ripgrep powers the code index (sliceagent still runs without it, just searches less well)
56
+ # 4. ripgrep powers the code index install it too (brew when available, else a ~2 MB static
57
+ # binary from GitHub into uv's tool bin: no sudo, isolated, removable with the rest).
54
58
  if ! command -v rg >/dev/null 2>&1; then
55
- warn "ripgrep (rg) not found — sliceagent works without it, but code search is much better with it."
56
- warn "Install it: brew install ripgrep | apt install ripgrep | https://github.com/BurntSushi/ripgrep"
59
+ info "Installing ripgrep (code search) "
60
+ if command -v brew >/dev/null 2>&1; then
61
+ brew install ripgrep >/dev/null 2>&1 || true
62
+ fi
63
+ fi
64
+ if ! command -v rg >/dev/null 2>&1; then
65
+ RG_VER="14.1.1"
66
+ case "$(uname -s)-$(uname -m)" in
67
+ Darwin-arm64) RG_TARGET="aarch64-apple-darwin" ;;
68
+ Darwin-x86_64) RG_TARGET="x86_64-apple-darwin" ;;
69
+ Linux-x86_64) RG_TARGET="x86_64-unknown-linux-musl" ;;
70
+ Linux-aarch64|Linux-arm64) RG_TARGET="aarch64-unknown-linux-gnu" ;;
71
+ *) RG_TARGET="" ;;
72
+ esac
73
+ BIN_DIR="$(uv tool dir --bin 2>/dev/null || true)"
74
+ [ -n "$BIN_DIR" ] || BIN_DIR="$HOME/.local/bin"
75
+ if [ -n "$RG_TARGET" ]; then
76
+ RG_TMP="$(mktemp -d)"
77
+ RG_URL="https://github.com/BurntSushi/ripgrep/releases/download/${RG_VER}/ripgrep-${RG_VER}-${RG_TARGET}.tar.gz"
78
+ if { command -v curl >/dev/null 2>&1 && curl -fsSL "$RG_URL" -o "$RG_TMP/rg.tgz"; } \
79
+ || { command -v wget >/dev/null 2>&1 && wget -qO "$RG_TMP/rg.tgz" "$RG_URL"; }; then
80
+ mkdir -p "$BIN_DIR" \
81
+ && tar -xzf "$RG_TMP/rg.tgz" -C "$RG_TMP" \
82
+ && mv "$RG_TMP/ripgrep-${RG_VER}-${RG_TARGET}/rg" "$BIN_DIR/rg" \
83
+ && chmod +x "$BIN_DIR/rg" \
84
+ && info "ripgrep installed to $BIN_DIR/rg" \
85
+ || warn "Could not unpack ripgrep — sliceagent still works, code search is just weaker without it."
86
+ else
87
+ warn "Could not download ripgrep — sliceagent still works, code search is just weaker without it."
88
+ fi
89
+ rm -rf "$RG_TMP"
90
+ else
91
+ warn "No prebuilt ripgrep for this platform — sliceagent works without it (weaker code search)."
92
+ fi
57
93
  fi
58
94
 
59
95
  cat <<'EOF'
@@ -1,3 +1,3 @@
1
1
  """sliceagent — a memory-native coding agent (deterministic, bounded, reconstructed context)."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.1"
@@ -163,12 +163,32 @@ def main() -> None:
163
163
  # config-persisted key/endpoint (written by `sliceagent init`) populate the env BEFORE the gate, so a
164
164
  # configured user never has to export anything; ENV still wins for one-off overrides.
165
165
  from .config import load_config
166
+
167
+ def _env_from_config(c) -> None:
168
+ for _env, _val in (("LLM_API_KEY", c.api_key), ("LLM_BASE_URL", c.base_url)):
169
+ if not os.environ.get(_env) and _val:
170
+ os.environ[_env] = _val
171
+
172
+ def _key_present() -> bool:
173
+ return bool(os.environ.get("LLM_API_KEY") or os.environ.get("OPENAI_API_KEY")
174
+ or os.environ.get("MOONSHOT_API_KEY"))
175
+
176
+ def _first_run_setup(reason: str) -> bool:
177
+ """First-run UX: a bare interactive `sliceagent` with nothing configured drops STRAIGHT into the
178
+ init wizard instead of bouncing the user to a separate command. Non-interactive (piped/CI) keeps
179
+ the print-and-exit gate — never prompt into a pipe. Returns True when the wizard completed."""
180
+ if not (sys.stdin.isatty() and sys.stdout.isatty()):
181
+ return False
182
+ print(f" {reason} — starting guided setup.\n")
183
+ from .onboarding import run_init
184
+ return run_init() == 0
185
+
166
186
  cfg = load_config()
167
- for _env, _val in (("LLM_API_KEY", cfg.api_key), ("LLM_BASE_URL", cfg.base_url)):
168
- if not os.environ.get(_env) and _val:
169
- os.environ[_env] = _val
170
- if not (os.environ.get("LLM_API_KEY") or os.environ.get("OPENAI_API_KEY")
171
- or os.environ.get("MOONSHOT_API_KEY")):
187
+ _env_from_config(cfg)
188
+ if not _key_present() and _first_run_setup("Welcome! No API key configured yet"):
189
+ cfg = load_config() # the wizard just wrote ~/.sliceagent/config.toml — pick it up
190
+ _env_from_config(cfg)
191
+ if not _key_present():
172
192
  print("No API key found. Run `sliceagent init` for guided setup, or set LLM_API_KEY (e.g. in a .env file).")
173
193
  sys.exit(1)
174
194
  # validate enum env vars (warn + use default; never crash) — a typo'd AGENT_POLICY is now visible.
@@ -211,6 +231,10 @@ def main() -> None:
211
231
  policy = make_policy("letitgo" if CONFIRMS.get(canonical) else canonical)
212
232
  # model + reasoning resolution: explicit env wins, then the saved /model choice (prefs), then config.
213
233
  _model = os.environ.get("AGENT_MODEL") or _prefs.get("model") or cfg.model
234
+ if not _model and _first_run_setup("No model configured yet"):
235
+ cfg = load_config() # wizard picks provider+model → re-resolve from the fresh config
236
+ _env_from_config(cfg)
237
+ _model = os.environ.get("AGENT_MODEL") or _prefs.get("model") or cfg.model
214
238
  if not _model: # no built-in default — the user picks the model (parallels the API-key gate above)
215
239
  print("No model configured. Run `sliceagent init` to pick a provider + model, "
216
240
  "or set AGENT_MODEL to your model name.")
@@ -0,0 +1,159 @@
1
+ """End-to-end launch smoke test: `sliceagent` must reach the input prompt WITHOUT crashing.
2
+
3
+ Regression guard for the cli.py banner `NameError: name 'policy_mode' is not defined` (the var was
4
+ renamed to the resolved `_eff_mode`/`canonical` in the 3-modes change but the banner still referenced the
5
+ old name). No prior test exercised `main()` end-to-end, so the crash shipped. Runs main() in a subprocess
6
+ with a fake key + AGENT_TUI=off + EOF stdin, so it boots, prints the banner, and exits on EOF — no network.
7
+ """
8
+ import os
9
+ import subprocess
10
+ import sys
11
+
12
+ _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
13
+
14
+
15
+ def _launch(extra=None):
16
+ import tempfile
17
+ env = dict(os.environ)
18
+ env.update({
19
+ "PYTHONPATH": os.path.join(_ROOT, "src"),
20
+ "HOME": tempfile.mkdtemp(prefix="smoke-home-"), # hermetic: the dev machine's ~/.sliceagent
21
+ # config must not leak in (CI has none either)
22
+ "AGENT_TUI": "off", # plain REPL — no prompt_toolkit app to drive
23
+ "LLM_API_KEY": "sk-dummy-smoke", "OPENAI_API_KEY": "sk-dummy-smoke",
24
+ "AGENT_MODEL": "dummy-model-smoke", # required since the no-default-model gate — without it the
25
+ # CLI exits at the gate before the banner this test asserts on
26
+ "AGENT_PROXY": "off", # don't route through a local proxy that isn't there
27
+ })
28
+ env.update(extra or {})
29
+ return subprocess.run(
30
+ [sys.executable, "-c", "from sliceagent.cli import main; main()"],
31
+ cwd=_ROOT, env=env, stdin=subprocess.DEVNULL,
32
+ capture_output=True, text=True, timeout=120,
33
+ )
34
+
35
+
36
+ def main_reaches_prompt_without_crashing():
37
+ r = _launch()
38
+ out = r.stdout + r.stderr
39
+ assert "Traceback" not in out and "NameError" not in out, out[-2000:]
40
+ assert "policy=" in out, "startup banner never rendered (the line that held the NameError):\n" + out[-1500:]
41
+ assert r.returncode == 0, f"nonzero exit {r.returncode}\n{out[-1500:]}"
42
+
43
+
44
+ def _no_key_env():
45
+ """A truly blank first-run env: temp HOME, every key/model var stripped."""
46
+ import tempfile
47
+ env = {k: v for k, v in os.environ.items()
48
+ if k not in ("LLM_API_KEY", "OPENAI_API_KEY", "MOONSHOT_API_KEY", "DEEPSEEK_API_KEY",
49
+ "AGENT_MODEL", "LLM_BASE_URL", "AGENT_PROVIDER")}
50
+ env.update({"PYTHONPATH": os.path.join(_ROOT, "src"),
51
+ "HOME": tempfile.mkdtemp(prefix="firstrun-home-"),
52
+ "AGENT_TUI": "off", "AGENT_PROXY": "off"})
53
+ return env
54
+
55
+
56
+ def piped_no_key_run_keeps_the_gate_no_prompt():
57
+ """Non-interactive (stdin=pipe) + no key must print the gate and exit 1 — never start the wizard
58
+ (a prompt into a pipe would hang CI/scripts)."""
59
+ r = subprocess.run([sys.executable, "-c", "from sliceagent.cli import main; main()"],
60
+ cwd=_ROOT, env=_no_key_env(), stdin=subprocess.DEVNULL,
61
+ capture_output=True, text=True, timeout=60)
62
+ out = r.stdout + r.stderr
63
+ assert "No API key found" in out, out[-800:]
64
+ assert "guided setup.\n\n" not in out and "sliceagent setup" not in out, \
65
+ "wizard must not auto-start without a tty:\n" + out[-800:]
66
+ assert r.returncode == 1, f"expected gate exit 1, got {r.returncode}"
67
+
68
+
69
+ def interactive_first_run_auto_starts_the_wizard():
70
+ """First-run UX: a bare interactive `sliceagent` (tty, nothing configured) drops straight into the
71
+ init wizard — proven on a REAL pty by the wizard header + provider menu appearing unprompted.
72
+ (The abort path is covered in-process below; macOS getpass on a detached pty is not reliably
73
+ drivable, and the wizard's own logic has an injectable seam for exactly that reason.)"""
74
+ import pty
75
+ import select
76
+ import signal
77
+ import time
78
+ try:
79
+ m, s = pty.openpty()
80
+ except OSError:
81
+ return # no pty on this host — skip
82
+ p = subprocess.Popen([sys.executable, "-c", "from sliceagent.cli import main; main()"],
83
+ cwd=_ROOT, env=_no_key_env(), stdin=s, stdout=s, stderr=s,
84
+ start_new_session=True)
85
+ os.close(s)
86
+ buf = bytearray()
87
+ deadline = time.monotonic() + 30
88
+ while time.monotonic() < deadline and b"Choose a provider" not in buf:
89
+ r, _, _ = select.select([m], [], [], 0.2)
90
+ if r:
91
+ try:
92
+ buf.extend(os.read(m, 4096))
93
+ except OSError:
94
+ break
95
+ if p.poll() is not None:
96
+ break
97
+ try:
98
+ os.killpg(p.pid, signal.SIGKILL) # wizard reached (or not) — tear the child down either way
99
+ except (ProcessLookupError, PermissionError):
100
+ pass
101
+ p.wait()
102
+ try:
103
+ os.close(m)
104
+ except OSError:
105
+ pass
106
+ out = buf.decode(errors="replace")
107
+ assert "starting guided setup" in out, "wizard did not auto-start on an interactive first run:\n" + out[-1200:]
108
+ assert "sliceagent setup" in out and "Choose a provider" in out, "wizard header/menu missing:\n" + out[-1200:]
109
+
110
+
111
+ def aborted_wizard_falls_back_to_the_gate():
112
+ """If the auto-started wizard is aborted (returns nonzero), main() must fall back to the plain
113
+ gate message and exit 1 — never proceed keyless. In-process with the wizard mocked, tty faked."""
114
+ import contextlib
115
+ import io
116
+ import tempfile
117
+ from types import SimpleNamespace
118
+ from unittest import mock
119
+
120
+ from sliceagent import cli as cli_mod
121
+ from sliceagent import onboarding as ob
122
+
123
+ env_patch = {k: "" for k in ("LLM_API_KEY", "OPENAI_API_KEY", "MOONSHOT_API_KEY", "AGENT_MODEL")}
124
+ env_patch["HOME"] = tempfile.mkdtemp(prefix="firstrun-abort-")
125
+ out = io.StringIO()
126
+ # one fake object doubling as tty-stdin (isatty only) and capturing tty-stdout: redirect_stdout
127
+ # would swap in a StringIO whose isatty() is False and silently close the wizard path.
128
+ fake_tty = SimpleNamespace(isatty=lambda: True, write=out.write, flush=lambda: None)
129
+ called = {"n": 0}
130
+
131
+ def _fake_init():
132
+ called["n"] += 1
133
+ return 1 # user aborted the wizard
134
+
135
+ _ = contextlib # (kept import shape stable)
136
+ with mock.patch.dict(os.environ, env_patch), \
137
+ mock.patch.object(ob, "run_init", _fake_init), \
138
+ mock.patch.object(sys, "argv", ["sliceagent"]), \
139
+ mock.patch.object(sys, "stdin", fake_tty), \
140
+ mock.patch.object(sys, "stdout", fake_tty):
141
+ try:
142
+ cli_mod.main()
143
+ raise AssertionError("main() must exit after an aborted wizard")
144
+ except SystemExit as e:
145
+ assert e.code == 1, f"expected exit 1, got {e.code}"
146
+ text = out.getvalue()
147
+ assert called["n"] == 1, "the wizard was never invoked"
148
+ assert "No API key found" in text, "aborted wizard must fall back to the gate message:\n" + text
149
+
150
+
151
+ if __name__ == "__main__":
152
+ main_reaches_prompt_without_crashing()
153
+ print("PASS main_reaches_prompt_without_crashing")
154
+ piped_no_key_run_keeps_the_gate_no_prompt()
155
+ print("PASS piped_no_key_run_keeps_the_gate_no_prompt")
156
+ interactive_first_run_auto_starts_the_wizard()
157
+ print("PASS interactive_first_run_auto_starts_the_wizard")
158
+ aborted_wizard_falls_back_to_the_gate()
159
+ print("PASS aborted_wizard_falls_back_to_the_gate")
@@ -9,6 +9,12 @@ ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
9
9
  sys.path.insert(0, os.path.join(ROOT, "src"))
10
10
  sys.path.insert(0, os.path.join(ROOT, "evals"))
11
11
 
12
+ # The prompt A/B harness lives in evals/, which is local-only dev tooling (not shipped in the public
13
+ # repo) — skip cleanly where it's absent (e.g. CI on the public checkout), like the no-pty skips.
14
+ if not os.path.isdir(os.path.join(ROOT, "evals", "prompt_ab")):
15
+ print("SKIP: evals/prompt_ab not present (local-only eval tooling)")
16
+ sys.exit(0)
17
+
12
18
  from prompt_ab import stats as S # noqa: E402
13
19
  from prompt_ab import variants as V # noqa: E402
14
20
 
@@ -1,41 +0,0 @@
1
- """End-to-end launch smoke test: `sliceagent` must reach the input prompt WITHOUT crashing.
2
-
3
- Regression guard for the cli.py banner `NameError: name 'policy_mode' is not defined` (the var was
4
- renamed to the resolved `_eff_mode`/`canonical` in the 3-modes change but the banner still referenced the
5
- old name). No prior test exercised `main()` end-to-end, so the crash shipped. Runs main() in a subprocess
6
- with a fake key + AGENT_TUI=off + EOF stdin, so it boots, prints the banner, and exits on EOF — no network.
7
- """
8
- import os
9
- import subprocess
10
- import sys
11
-
12
- _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
13
-
14
-
15
- def _launch(extra=None):
16
- env = dict(os.environ)
17
- env.update({
18
- "PYTHONPATH": os.path.join(_ROOT, "src"),
19
- "AGENT_TUI": "off", # plain REPL — no prompt_toolkit app to drive
20
- "LLM_API_KEY": "sk-dummy-smoke", "OPENAI_API_KEY": "sk-dummy-smoke",
21
- "AGENT_PROXY": "off", # don't route through a local proxy that isn't there
22
- })
23
- env.update(extra or {})
24
- return subprocess.run(
25
- [sys.executable, "-c", "from sliceagent.cli import main; main()"],
26
- cwd=_ROOT, env=env, stdin=subprocess.DEVNULL,
27
- capture_output=True, text=True, timeout=120,
28
- )
29
-
30
-
31
- def main_reaches_prompt_without_crashing():
32
- r = _launch()
33
- out = r.stdout + r.stderr
34
- assert "Traceback" not in out and "NameError" not in out, out[-2000:]
35
- assert "policy=" in out, "startup banner never rendered (the line that held the NameError):\n" + out[-1500:]
36
- assert r.returncode == 0, f"nonzero exit {r.returncode}\n{out[-1500:]}"
37
-
38
-
39
- if __name__ == "__main__":
40
- main_reaches_prompt_without_crashing()
41
- print("PASS main_reaches_prompt_without_crashing")
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