turnloop 0.1.8__tar.gz → 0.1.10__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 (163) hide show
  1. {turnloop-0.1.8 → turnloop-0.1.10}/PKG-INFO +15 -7
  2. {turnloop-0.1.8 → turnloop-0.1.10}/README.md +14 -6
  3. {turnloop-0.1.8 → turnloop-0.1.10}/pyproject.toml +9 -1
  4. turnloop-0.1.10/tests/fixtures/github_chrome.html +12 -0
  5. turnloop-0.1.10/tests/fixtures/github_page.html +1548 -0
  6. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_bash.py +194 -150
  7. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_skills_install.py +863 -523
  8. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_system_prompt_layout.py +119 -85
  9. turnloop-0.1.10/tests/test_webfetch.py +70 -0
  10. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/__init__.py +1 -1
  11. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/system_prompt.py +308 -294
  12. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/cli.py +69 -7
  13. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/config.py +5 -1
  14. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/skills_install.py +492 -328
  15. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/bash.py +434 -423
  16. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/webfetch.py +273 -235
  17. turnloop-0.1.8/.github/workflows/ci.yml +0 -36
  18. turnloop-0.1.8/.turnloop/.gitignore +0 -2
  19. turnloop-0.1.8/.turnloop/settings.local.json +0 -3
  20. {turnloop-0.1.8 → turnloop-0.1.10}/.gitignore +0 -0
  21. {turnloop-0.1.8 → turnloop-0.1.10}/tests/conftest.py +0 -0
  22. {turnloop-0.1.8 → turnloop-0.1.10}/tests/fixtures/anthropic_tool_call.sse +0 -0
  23. {turnloop-0.1.8 → turnloop-0.1.10}/tests/fixtures/glm_tool_call.sse +0 -0
  24. {turnloop-0.1.8 → turnloop-0.1.10}/tests/fixtures/mcp_echo_server.py +0 -0
  25. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_compaction.py +0 -0
  26. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_config.py +0 -0
  27. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_config_screen.py +0 -0
  28. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_configio.py +0 -0
  29. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_experiments.py +0 -0
  30. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_hooks_mcp_commands.py +0 -0
  31. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_loop.py +0 -0
  32. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_permissions.py +0 -0
  33. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_providers.py +0 -0
  34. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_tools_files.py +0 -0
  35. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_tui.py +0 -0
  36. {turnloop-0.1.8 → turnloop-0.1.10}/tests/test_websearch.py +0 -0
  37. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/__main__.py +0 -0
  38. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/__init__.py +0 -0
  39. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/factory.py +0 -0
  40. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/headless.py +0 -0
  41. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/loop.py +0 -0
  42. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/agent/subagent.py +0 -0
  43. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/commands/__init__.py +0 -0
  44. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/commands/dispatch.py +0 -0
  45. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/commands/loader.py +0 -0
  46. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/configio.py +0 -0
  47. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/context/__init__.py +0 -0
  48. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/context/budget.py +0 -0
  49. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/context/compaction.py +0 -0
  50. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/context/memory.py +0 -0
  51. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/core/__init__.py +0 -0
  52. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/core/events.py +0 -0
  53. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/core/ids.py +0 -0
  54. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/core/messages.py +0 -0
  55. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/core/tokens.py +0 -0
  56. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/diagnostics.py +0 -0
  57. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/errors.py +0 -0
  58. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/__init__.py +0 -0
  59. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/bakeoff.yaml +0 -0
  60. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/ceiling-check.yaml +0 -0
  61. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/context-glm.yaml +0 -0
  62. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/context.yaml +0 -0
  63. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/glm-selfhosted.yaml +0 -0
  64. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/groq-free.yaml +0 -0
  65. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/hard-calibration.yaml +0 -0
  66. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/hard-glm.yaml +0 -0
  67. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/loop.yaml +0 -0
  68. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/nvidia-smoke.yaml +0 -0
  69. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/reliability.yaml +0 -0
  70. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/rewrite-calibration.yaml +0 -0
  71. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/configs/smoke.yaml +0 -0
  72. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/graders.py +0 -0
  73. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/report.py +0 -0
  74. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/runner.py +0 -0
  75. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/default.yaml +0 -0
  76. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_1.py +0 -0
  77. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_2.py +0 -0
  78. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_3.py +0 -0
  79. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_4.py +0 -0
  80. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_5.py +0 -0
  81. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_6.py +0 -0
  82. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_7.py +0 -0
  83. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/bulky/module_8.py +0 -0
  84. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/circular_import/models.py +0 -0
  85. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/circular_import/services.py +0 -0
  86. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/circular_import/test_cancel.py +0 -0
  87. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/cli_flag/app.py +0 -0
  88. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/cross_file_contract/decode.py +0 -0
  89. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/cross_file_contract/encode.py +0 -0
  90. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/cross_file_contract/test_roundtrip.py +0 -0
  91. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/cross_file_contract/test_wire_format.py +0 -0
  92. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/empty/.keep +0 -0
  93. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/failing_test/calc.py +0 -0
  94. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/failing_test/test_calc.py +0 -0
  95. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/generate_bulky.py +0 -0
  96. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/grep_edit/ingest.py +0 -0
  97. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/grep_edit/load.py +0 -0
  98. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/grep_edit/transform.py +0 -0
  99. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/misleading_traceback/checkout.py +0 -0
  100. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/misleading_traceback/invoice.py +0 -0
  101. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/misleading_traceback/pricing.py +0 -0
  102. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/misleading_traceback/test_checkout.py +0 -0
  103. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/misleading_traceback/test_invoice.py +0 -0
  104. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/needle/inventory.py +0 -0
  105. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/needle/pricing.py +0 -0
  106. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/needle/shipping.py +0 -0
  107. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/regression_trap/test_validators.py +0 -0
  108. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/regression_trap/validators.py +0 -0
  109. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/rename/billing.py +0 -0
  110. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/rename/report.py +0 -0
  111. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/rename/test_billing.py +0 -0
  112. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/spec/parser.py +0 -0
  113. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/spec/test_parser.py +0 -0
  114. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/subtle_spec_edge/leaderboard.py +0 -0
  115. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/subtle_spec_edge/test_leaderboard.py +0 -0
  116. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/experiments/suite/fixtures/util/util.py +0 -0
  117. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/hooks/__init__.py +0 -0
  118. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/hooks/runner.py +0 -0
  119. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/mcp/__init__.py +0 -0
  120. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/mcp/adapter.py +0 -0
  121. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/mcp/client.py +0 -0
  122. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/permissions/__init__.py +0 -0
  123. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/permissions/engine.py +0 -0
  124. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/permissions/rules.py +0 -0
  125. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/__init__.py +0 -0
  126. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/anthropic.py +0 -0
  127. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/base.py +0 -0
  128. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/gemini.py +0 -0
  129. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/mock.py +0 -0
  130. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/openai_compat.py +0 -0
  131. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/pricing.py +0 -0
  132. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/registry.py +0 -0
  133. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/providers/sse.py +0 -0
  134. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/sessions/__init__.py +0 -0
  135. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/sessions/models.py +0 -0
  136. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/sessions/store.py +0 -0
  137. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/__init__.py +0 -0
  138. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/ask.py +0 -0
  139. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/base.py +0 -0
  140. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/builtin.py +0 -0
  141. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/edit.py +0 -0
  142. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/glob.py +0 -0
  143. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/grep.py +0 -0
  144. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/read.py +0 -0
  145. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/runner.py +0 -0
  146. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/shell.py +0 -0
  147. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/skill.py +0 -0
  148. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/task.py +0 -0
  149. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/textio.py +0 -0
  150. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/todo.py +0 -0
  151. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/websearch.py +0 -0
  152. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tools/write.py +0 -0
  153. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/__init__.py +0 -0
  154. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/app.py +0 -0
  155. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/app.tcss +0 -0
  156. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/bridge.py +0 -0
  157. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/config_screen.py +0 -0
  158. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/mcp_screen.py +0 -0
  159. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/session_screen.py +0 -0
  160. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/widgets/__init__.py +0 -0
  161. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/widgets/permission.py +0 -0
  162. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/widgets/status.py +0 -0
  163. {turnloop-0.1.8 → turnloop-0.1.10}/turnloop/tui/widgets/transcript.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: turnloop
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: A local-first agentic coding harness: streaming tool loop, permission gating, context compaction, subagents, and any LLM provider.
5
5
  Project-URL: Homepage, https://github.com/Pranesh-2005/turnloop
6
6
  Author: Pranesh
@@ -49,7 +49,7 @@ tl experiment run smoke # a measurement run, offline and free
49
49
 
50
50
  Four runtime dependencies: `textual`, `pydantic`, `httpx`, `pyyaml`. No vendor
51
51
  SDKs, no agent framework — the loop is the point, so the loop is written here.
52
- ~20,000 lines of Python, 346 tests, no network in the default test run.
52
+ ~21,500 lines of Python, 364 tests, no network in the default test run.
53
53
 
54
54
  ---
55
55
 
@@ -117,7 +117,7 @@ point.
117
117
  Verify:
118
118
 
119
119
  ```bash
120
- tl --version # turnloop 0.1.8
120
+ tl --version # turnloop 0.1.10
121
121
  tl doctor # every provider, key presence, shell, context budget
122
122
  ```
123
123
 
@@ -1023,7 +1023,7 @@ one-line edit in a CRLF checkout does not produce a whole-file diff.
1023
1023
  ## Testing
1024
1024
 
1025
1025
  ```bash
1026
- pytest # 346 tests, no network
1026
+ pytest # 364 tests, no network
1027
1027
  ruff check turnloop
1028
1028
  mypy turnloop
1029
1029
  ```
@@ -1037,7 +1037,7 @@ mypy turnloop
1037
1037
  | `test_hooks_mcp_commands.py` | 28 | lifecycle hooks, MCP client, slash commands |
1038
1038
  | `test_tui.py` | 25 | Textual snapshots, `/config` and `/mcp add` driven through a real app |
1039
1039
  | `test_config.py` | 23 | layering, env overrides, presets, narrow-console guard |
1040
- | `test_skills_install.py` | 27 | frontmatter validation, GitHub resolution, path dedupe, EOF on every prompt, drive-root refusal |
1040
+ | `test_skills_install.py` | 39 | frontmatter validation, GitHub resolution, agent-mirror tiebreak, concurrent fetch ordering, console caps, EOF on every prompt, drive-root refusal |
1041
1041
  | `test_loop.py` | 23 | streaming, truncation, iteration cap |
1042
1042
  | `test_compaction.py` | 16 | three tiers, tool_use/tool_result invariant |
1043
1043
  | `test_configio.py` | 15 | minimal-diff writes, atomic save, secret-bearing MCP targets, the settings deny rules |
@@ -1092,11 +1092,19 @@ in the system prompt and load their body on demand, which on a 65k window is the
1092
1092
  difference between having skills and not.
1093
1093
 
1094
1094
  The system prompt also documents this layout to the model itself, as `CONFIG_LAYOUT`
1095
- — 326 tokens, in the cacheable region, omitted from the `minimal` variant. Without it
1095
+ — 432 tokens, in the cacheable region, omitted from the `minimal` variant. Without it
1096
1096
  the model cannot configure the tool it is running inside: asked to add an MCP server
1097
1097
  it invented `.turnloop/turnloop.config.json`, and asked to install a skill it wrote to
1098
1098
  `examples/` — both real outputs from live runs. With it, both go to the right place
1099
- first try. It includes the literal YAML frontmatter a skill needs, since a skill with
1099
+ first try.
1100
+
1101
+ It also names `tl skills add` explicitly and tells the model *not* to run the install
1102
+ scripts skill repos publish for other agents. That paragraph was added after watching
1103
+ three separate sessions try `/plugin marketplace add` as a shell command, then
1104
+ `clawhub install`, then `git clone` plus `npm install` plus `npm test` — twenty-six
1105
+ messages ending in "the ponytail skill has been installed", when nothing had been put
1106
+ anywhere turnloop looks. The same request now costs 11,090 tokens and answers
1107
+ `tl skills add DietrichGebert/ponytail`. It includes the literal YAML frontmatter a skill needs, since a skill with
1100
1108
  markdown-bold frontmatter instead of YAML used to vanish with no diagnostic; `/skills`
1101
1109
  now also reports anything found on disk but rejected, with the reason and path — see
1102
1110
  [Bugs worth reading about](#bugs-worth-reading-about).
@@ -20,7 +20,7 @@ tl experiment run smoke # a measurement run, offline and free
20
20
 
21
21
  Four runtime dependencies: `textual`, `pydantic`, `httpx`, `pyyaml`. No vendor
22
22
  SDKs, no agent framework — the loop is the point, so the loop is written here.
23
- ~20,000 lines of Python, 346 tests, no network in the default test run.
23
+ ~21,500 lines of Python, 364 tests, no network in the default test run.
24
24
 
25
25
  ---
26
26
 
@@ -88,7 +88,7 @@ point.
88
88
  Verify:
89
89
 
90
90
  ```bash
91
- tl --version # turnloop 0.1.8
91
+ tl --version # turnloop 0.1.10
92
92
  tl doctor # every provider, key presence, shell, context budget
93
93
  ```
94
94
 
@@ -994,7 +994,7 @@ one-line edit in a CRLF checkout does not produce a whole-file diff.
994
994
  ## Testing
995
995
 
996
996
  ```bash
997
- pytest # 346 tests, no network
997
+ pytest # 364 tests, no network
998
998
  ruff check turnloop
999
999
  mypy turnloop
1000
1000
  ```
@@ -1008,7 +1008,7 @@ mypy turnloop
1008
1008
  | `test_hooks_mcp_commands.py` | 28 | lifecycle hooks, MCP client, slash commands |
1009
1009
  | `test_tui.py` | 25 | Textual snapshots, `/config` and `/mcp add` driven through a real app |
1010
1010
  | `test_config.py` | 23 | layering, env overrides, presets, narrow-console guard |
1011
- | `test_skills_install.py` | 27 | frontmatter validation, GitHub resolution, path dedupe, EOF on every prompt, drive-root refusal |
1011
+ | `test_skills_install.py` | 39 | frontmatter validation, GitHub resolution, agent-mirror tiebreak, concurrent fetch ordering, console caps, EOF on every prompt, drive-root refusal |
1012
1012
  | `test_loop.py` | 23 | streaming, truncation, iteration cap |
1013
1013
  | `test_compaction.py` | 16 | three tiers, tool_use/tool_result invariant |
1014
1014
  | `test_configio.py` | 15 | minimal-diff writes, atomic save, secret-bearing MCP targets, the settings deny rules |
@@ -1063,11 +1063,19 @@ in the system prompt and load their body on demand, which on a 65k window is the
1063
1063
  difference between having skills and not.
1064
1064
 
1065
1065
  The system prompt also documents this layout to the model itself, as `CONFIG_LAYOUT`
1066
- — 326 tokens, in the cacheable region, omitted from the `minimal` variant. Without it
1066
+ — 432 tokens, in the cacheable region, omitted from the `minimal` variant. Without it
1067
1067
  the model cannot configure the tool it is running inside: asked to add an MCP server
1068
1068
  it invented `.turnloop/turnloop.config.json`, and asked to install a skill it wrote to
1069
1069
  `examples/` — both real outputs from live runs. With it, both go to the right place
1070
- first try. It includes the literal YAML frontmatter a skill needs, since a skill with
1070
+ first try.
1071
+
1072
+ It also names `tl skills add` explicitly and tells the model *not* to run the install
1073
+ scripts skill repos publish for other agents. That paragraph was added after watching
1074
+ three separate sessions try `/plugin marketplace add` as a shell command, then
1075
+ `clawhub install`, then `git clone` plus `npm install` plus `npm test` — twenty-six
1076
+ messages ending in "the ponytail skill has been installed", when nothing had been put
1077
+ anywhere turnloop looks. The same request now costs 11,090 tokens and answers
1078
+ `tl skills add DietrichGebert/ponytail`. It includes the literal YAML frontmatter a skill needs, since a skill with
1071
1079
  markdown-bold frontmatter instead of YAML used to vanish with no diagnostic; `/skills`
1072
1080
  now also reports anything found on disk but rejected, with the reason and path — see
1073
1081
  [Bugs worth reading about](#bugs-worth-reading-about).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "turnloop"
7
- version = "0.1.8"
7
+ version = "0.1.10"
8
8
  description = "A local-first agentic coding harness: streaming tool loop, permission gating, context compaction, subagents, and any LLM provider."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -54,6 +54,14 @@ Homepage = "https://github.com/Pranesh-2005/turnloop"
54
54
  [tool.hatch.build.targets.wheel]
55
55
  packages = ["turnloop"]
56
56
 
57
+ # Without an explicit include, the sdist sweeps up whatever happens to be sitting
58
+ # in the working tree. A stray `git clone` left behind by a failed skill install
59
+ # put 157 of 317 entries — 4.2 MB of an unrelated repository — into the 0.1.8
60
+ # sdist before this was pinned down. An allowlist fails closed: new junk is not
61
+ # published, and a genuinely new top-level path has to be added here on purpose.
62
+ [tool.hatch.build.targets.sdist]
63
+ include = ["turnloop", "tests", "README.md", "pyproject.toml", "LICENSE"]
64
+
57
65
  [tool.ruff]
58
66
  line-length = 100
59
67
  target-version = "py311"
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html><head><title>anthropics/anthropic-sdk-python</title></head>
3
+ <body>
4
+ <nav aria-label="Global"><a href="#start-of-content" data-skip-target-assigned="false" class="px-2 tmp-py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a></nav>
5
+ <header>
6
+ <nav class="prc-components-UnderlineWrapper-eT-Yj" aria-label="Repository files"><ul class="prc-UnderlineNav-ItemsList-oj8gN prc-components-UnderlineItemList-xKlKC" role="list"><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a href="#" aria-current="page" class="prc-components-UnderlineItem-7fP-n"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-book" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path></svg></span><span data-component="text" data-content="README">README</span></a></li></ul></nav>
7
+ </header>
8
+ <main>
9
+ <h1>Claude SDK for Python</h1>
10
+ <p>The Claude SDK for Python provides access to the Claude API from Python applications.</p>
11
+ </main>
12
+ </body></html>