ffwf-tau-coding-agent 0.9.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.
Files changed (109) hide show
  1. ffwf_tau_coding_agent-0.9.2/LICENSE +21 -0
  2. ffwf_tau_coding_agent-0.9.2/PKG-INFO +166 -0
  3. ffwf_tau_coding_agent-0.9.2/README.md +136 -0
  4. ffwf_tau_coding_agent-0.9.2/pyproject.toml +107 -0
  5. ffwf_tau_coding_agent-0.9.2/setup.cfg +4 -0
  6. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/PKG-INFO +166 -0
  7. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/SOURCES.txt +107 -0
  8. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/dependency_links.txt +1 -0
  9. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/entry_points.txt +3 -0
  10. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/requires.txt +18 -0
  11. ffwf_tau_coding_agent-0.9.2/src/ffwf_tau_coding_agent.egg-info/top_level.txt +1 -0
  12. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/__init__.py +33 -0
  13. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/app.py +5014 -0
  14. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/backends.py +1825 -0
  15. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/chat_widgets.py +487 -0
  16. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/cli.py +710 -0
  17. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/config.py +115 -0
  18. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/devshot.py +119 -0
  19. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/headless.py +973 -0
  20. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/parley.tcss +797 -0
  21. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/rpc_mode.py +395 -0
  22. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/session_store.py +912 -0
  23. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/store_factory.py +265 -0
  24. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/tagline.py +66 -0
  25. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/tau_default_config.json +23 -0
  26. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/testing/__init__.py +38 -0
  27. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/testing/render.py +273 -0
  28. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/testing/sandbox.py +108 -0
  29. ffwf_tau_coding_agent-0.9.2/src/tau_coding_agent/testing/scenes.py +591 -0
  30. ffwf_tau_coding_agent-0.9.2/tests/test_38_todo.py +225 -0
  31. ffwf_tau_coding_agent-0.9.2/tests/test_41_bookmarks.py +169 -0
  32. ffwf_tau_coding_agent-0.9.2/tests/test_42_session_autoname.py +164 -0
  33. ffwf_tau_coding_agent-0.9.2/tests/test_50_review_swarm.py +385 -0
  34. ffwf_tau_coding_agent-0.9.2/tests/test_51_delegate_fleet.py +448 -0
  35. ffwf_tau_coding_agent-0.9.2/tests/test_52_red_team_memory.py +397 -0
  36. ffwf_tau_coding_agent-0.9.2/tests/test_54_consequence_engine.py +461 -0
  37. ffwf_tau_coding_agent-0.9.2/tests/test_agent_session_live_path.py +210 -0
  38. ffwf_tau_coding_agent-0.9.2/tests/test_app_actions.py +514 -0
  39. ffwf_tau_coding_agent-0.9.2/tests/test_app_command_dispatch.py +243 -0
  40. ffwf_tau_coding_agent-0.9.2/tests/test_app_extension_commands.py +219 -0
  41. ffwf_tau_coding_agent-0.9.2/tests/test_app_extension_loading.py +386 -0
  42. ffwf_tau_coding_agent-0.9.2/tests/test_app_extension_mgmt.py +139 -0
  43. ffwf_tau_coding_agent-0.9.2/tests/test_app_lifecycle_hooks.py +65 -0
  44. ffwf_tau_coding_agent-0.9.2/tests/test_app_session_runtime.py +102 -0
  45. ffwf_tau_coding_agent-0.9.2/tests/test_append_entry_reload.py +82 -0
  46. ffwf_tau_coding_agent-0.9.2/tests/test_append_only_compaction_store.py +116 -0
  47. ffwf_tau_coding_agent-0.9.2/tests/test_backend_reasoning.py +157 -0
  48. ffwf_tau_coding_agent-0.9.2/tests/test_backend_telemetry_extra.py +133 -0
  49. ffwf_tau_coding_agent-0.9.2/tests/test_backend_tool_filter.py +100 -0
  50. ffwf_tau_coding_agent-0.9.2/tests/test_before_agent_start_durable.py +246 -0
  51. ffwf_tau_coding_agent-0.9.2/tests/test_chat_placeholder.py +372 -0
  52. ffwf_tau_coding_agent-0.9.2/tests/test_chat_rendering.py +916 -0
  53. ffwf_tau_coding_agent-0.9.2/tests/test_chat_widgets.py +297 -0
  54. ffwf_tau_coding_agent-0.9.2/tests/test_cli.py +977 -0
  55. ffwf_tau_coding_agent-0.9.2/tests/test_cli_flag_inventory.py +162 -0
  56. ffwf_tau_coding_agent-0.9.2/tests/test_config.py +140 -0
  57. ffwf_tau_coding_agent-0.9.2/tests/test_context_surgeon.py +482 -0
  58. ffwf_tau_coding_agent-0.9.2/tests/test_contract_file_catalog.py +39 -0
  59. ffwf_tau_coding_agent-0.9.2/tests/test_contract_file_session.py +62 -0
  60. ffwf_tau_coding_agent-0.9.2/tests/test_cost.py +185 -0
  61. ffwf_tau_coding_agent-0.9.2/tests/test_ctx_fork_export.py +96 -0
  62. ffwf_tau_coding_agent-0.9.2/tests/test_delegate.py +242 -0
  63. ffwf_tau_coding_agent-0.9.2/tests/test_e5_integration_floor.py +344 -0
  64. ffwf_tau_coding_agent-0.9.2/tests/test_e5_status_doc.py +123 -0
  65. ffwf_tau_coding_agent-0.9.2/tests/test_ext_config_cli.py +147 -0
  66. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_gate.py +328 -0
  67. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_ledger.py +375 -0
  68. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_spawn.py +396 -0
  69. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_state.py +332 -0
  70. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_steer.py +551 -0
  71. ffwf_tau_coding_agent-0.9.2/tests/test_ext_kit_stream.py +392 -0
  72. ffwf_tau_coding_agent-0.9.2/tests/test_extension_dialogs.py +204 -0
  73. ffwf_tau_coding_agent-0.9.2/tests/test_extension_form.py +189 -0
  74. ffwf_tau_coding_agent-0.9.2/tests/test_extension_notify_and_veto.py +191 -0
  75. ffwf_tau_coding_agent-0.9.2/tests/test_extension_panel.py +264 -0
  76. ffwf_tau_coding_agent-0.9.2/tests/test_extension_shortcut.py +206 -0
  77. ffwf_tau_coding_agent-0.9.2/tests/test_extension_status_bar.py +116 -0
  78. ffwf_tau_coding_agent-0.9.2/tests/test_handoff.py +191 -0
  79. ffwf_tau_coding_agent-0.9.2/tests/test_headless_command_output.py +193 -0
  80. ffwf_tau_coding_agent-0.9.2/tests/test_headless_extension_records.py +142 -0
  81. ffwf_tau_coding_agent-0.9.2/tests/test_headless_lifecycle.py +175 -0
  82. ffwf_tau_coding_agent-0.9.2/tests/test_headless_resume.py +362 -0
  83. ffwf_tau_coding_agent-0.9.2/tests/test_headless_submission_source.py +518 -0
  84. ffwf_tau_coding_agent-0.9.2/tests/test_json_mode.py +262 -0
  85. ffwf_tau_coding_agent-0.9.2/tests/test_live_procedures_doc.py +389 -0
  86. ffwf_tau_coding_agent-0.9.2/tests/test_missing_api_key_raises.py +68 -0
  87. ffwf_tau_coding_agent-0.9.2/tests/test_model_grammar_config.py +87 -0
  88. ffwf_tau_coding_agent-0.9.2/tests/test_model_resolver_wiring.py +118 -0
  89. ffwf_tau_coding_agent-0.9.2/tests/test_nested_collapsible_spike.py +86 -0
  90. ffwf_tau_coding_agent-0.9.2/tests/test_no_host_addresses.py +64 -0
  91. ffwf_tau_coding_agent-0.9.2/tests/test_packaging.py +299 -0
  92. ffwf_tau_coding_agent-0.9.2/tests/test_palette_command_args.py +160 -0
  93. ffwf_tau_coding_agent-0.9.2/tests/test_render_router.py +502 -0
  94. ffwf_tau_coding_agent-0.9.2/tests/test_rollback_affordance.py +401 -0
  95. ffwf_tau_coding_agent-0.9.2/tests/test_rpc_conformance.py +2334 -0
  96. ffwf_tau_coding_agent-0.9.2/tests/test_rpc_line_limit.py +420 -0
  97. ffwf_tau_coding_agent-0.9.2/tests/test_rpc_session_dir_isolation.py +478 -0
  98. ffwf_tau_coding_agent-0.9.2/tests/test_session_dir_flag.py +287 -0
  99. ffwf_tau_coding_agent-0.9.2/tests/test_session_store.py +399 -0
  100. ffwf_tau_coding_agent-0.9.2/tests/test_session_tree_browser.py +304 -0
  101. ffwf_tau_coding_agent-0.9.2/tests/test_store_factory.py +371 -0
  102. ffwf_tau_coding_agent-0.9.2/tests/test_tree_detail_pane.py +360 -0
  103. ffwf_tau_coding_agent-0.9.2/tests/test_tree_elide.py +448 -0
  104. ffwf_tau_coding_agent-0.9.2/tests/test_tui_appearance.py +788 -0
  105. ffwf_tau_coding_agent-0.9.2/tests/test_tui_multi_lane_render.py +653 -0
  106. ffwf_tau_coding_agent-0.9.2/tests/test_tui_snapshots.py +174 -0
  107. ffwf_tau_coding_agent-0.9.2/tests/test_tui_sole_persister.py +180 -0
  108. ffwf_tau_coding_agent-0.9.2/tests/test_tui_submission_source.py +268 -0
  109. ffwf_tau_coding_agent-0.9.2/tests/test_ui_defaults_cli.py +99 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fight Fire with Fire Robotics, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: ffwf-tau-coding-agent
3
+ Version: 0.9.2
4
+ Summary: τ-coding-agent: Interactive TUI (Textual) + CLI
5
+ Author: Fight Fire with Fire Robotics, LLC
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://ffwfrobotics.github.io/tau/
8
+ Project-URL: Documentation, https://ffwfrobotics.github.io/tau/
9
+ Project-URL: Repository, https://github.com/jmccardle/tau
10
+ Project-URL: Issues, https://github.com/jmccardle/tau/issues
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: ffwf-tau-agent-core==0.9.2
15
+ Provides-Extra: tui
16
+ Requires-Dist: textual>=0.47; extra == "tui"
17
+ Requires-Dist: rich; extra == "tui"
18
+ Provides-Extra: jmfts
19
+ Requires-Dist: ffwf-tau-jmfts==0.9.2; extra == "jmfts"
20
+ Provides-Extra: dev
21
+ Requires-Dist: ffwf-tau-coding-agent[jmfts,tui]==0.9.2; extra == "dev"
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: pytest-asyncio; extra == "dev"
24
+ Requires-Dist: ruff; extra == "dev"
25
+ Requires-Dist: mypy==2.1.*; extra == "dev"
26
+ Requires-Dist: textual-dev; extra == "dev"
27
+ Requires-Dist: pytest-textual-snapshot; extra == "dev"
28
+ Requires-Dist: cairosvg; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # ffwf-tau-coding-agent
32
+
33
+ The interface of **Tau**, a programmable coding agent harness: the `tau`
34
+ command, its Textual terminal UI, and the headless run paths behind `tau -p` and
35
+ `tau --mode rpc`.
36
+
37
+ Tau began as a Python port of the TypeScript project
38
+ [pi-mono](https://github.com/badlogic/pi-mono), which is still read as the
39
+ reference implementation when porting or debugging; it now diverges from pi
40
+ deliberately in several places.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install ffwf-tau-coding-agent # headless: the CLI, `tau -p`, `--mode rpc`
46
+ pip install 'ffwf-tau-coding-agent[tui]' # add the interactive TUI
47
+ ```
48
+
49
+ Python 3.11 or newer. Pulls in `ffwf-tau-agent-core`, and through it
50
+ `ffwf-tau-llm`. Note the `ffwf-` prefix: `tau-llm` and `tau-ai` on PyPI are
51
+ unrelated projects.
52
+
53
+ | Extra | Adds | Needed for |
54
+ |---|---|---|
55
+ | `ffwf-tau-coding-agent[tui]` | `textual`, `rich` | the interactive TUI. `tau -p` runs a full turn without it |
56
+ | `ffwf-tau-coding-agent[jmfts]` | `ffwf-tau-jmfts` | `--store jmfts` session storage |
57
+
58
+ **The interface is an extra, not a base dependency.** A headless install is 15
59
+ packages and 13 MB; adding `[tui]` makes it 27 packages and 31 MB. Both extras
60
+ report their own absence — asking for the TUI or for `--store jmfts` without the
61
+ matching extra fails with the install command you need, not a traceback.
62
+
63
+ ### Two names for one command
64
+
65
+ The install puts **both `tau` and `ffwf-tau`** on your PATH. They are the same
66
+ entry point, not a link: each is a wrapper pip owns and removes on uninstall.
67
+
68
+ Type `tau`. PyPI reserves distribution names but not command names, and at least
69
+ one unrelated project ships its own `tau`; in an environment holding both,
70
+ whichever installed last owns the name and neither pip nor uv says a word.
71
+ `ffwf-tau` cannot be taken, which makes it the right name inside scripts,
72
+ systemd units, and Dockerfiles. If `tau --version` does not report τ,
73
+ `ffwf-tau --version` will.
74
+
75
+ ## Run
76
+
77
+ ```bash
78
+ tau # interactive TUI
79
+ tau -p "list the files here" # headless: print a transcript, exit
80
+ tau -p --mode json "..." # headless: JSONL lifecycle events instead of text
81
+ tau --mode rpc # drive τ as a subprocess over stdio (JSON-RPC 2.0)
82
+ tau -e permission_gate.py # load an extension from a path
83
+ tau --help # the exact contract; treat this page as the stale one
84
+ ```
85
+
86
+ `tau -p` and `tau --mode rpc` both write and resume real sessions under
87
+ `~/.tau/sessions/`, so a headless run appears in the TUI's sidebar and can be
88
+ picked up interactively later. `--no-session` means nothing is persisted.
89
+
90
+ Flags worth knowing: `-m/--model`, `-t/--tools` and `-nt/--no-tools`,
91
+ `-e/--extension PATH`, `--thinking {off,minimal,low,medium,high,xhigh}`,
92
+ `-c/--continue`, `--session REF`, `--fork REF`, `--store {file,jmfts}`.
93
+
94
+ ## Configure
95
+
96
+ On first run τ writes `~/.tau/config.json` from a default shipped inside the
97
+ package. It holds the `models` map that `--model` resolves names against, plus
98
+ per-extension config and the `session_store` block. A model entry names the
99
+ provider, the base URL, and the context window, so pointing τ at a local
100
+ OpenAI-compatible server (vLLM, llama.cpp, Ollama) is a config edit rather than
101
+ a code change.
102
+
103
+ ## The TUI
104
+
105
+ Built on [Textual](https://textual.textualize.io/). Streamed text renders at
106
+ 30 Hz; tool calls and their results appear as their own blocks.
107
+
108
+ | Key | Action |
109
+ |---|---|
110
+ | `Ctrl+Enter` | send the message |
111
+ | `Ctrl+B` | toggle the session sidebar |
112
+ | `Ctrl+N` | new chat |
113
+ | `Ctrl+G` | browse the conversation tree |
114
+ | `Ctrl+Z` | roll back the last turn |
115
+ | `Ctrl+R` / `Ctrl+T` | show or hide reasoning / tool blocks |
116
+ | `Ctrl+E` | extensions |
117
+ | `Ctrl+P` | command palette |
118
+ | `Escape` | cancel the current generation |
119
+
120
+ **The sidebar and the tree browser are two different things.** The sidebar picks
121
+ *which session to open* — a flat list grouped by date. `Ctrl+G` opens a browser
122
+ over the branch structure *inside the current conversation*, where you navigate
123
+ to an earlier node, summarise a branch, or elide a span. Typed commands cover
124
+ the rest: `/compact`, `/tree`, `/fork`, `/extensions`.
125
+
126
+ ## Why it is a separate package
127
+
128
+ Everything here is presentation and process management. The agent lives in
129
+ `ffwf-tau-agent-core`, which has no Textual dependency and never imports this
130
+ package. `ffwf-tau-jmfts` is a peer rather than a dependency: `--store jmfts`
131
+ imports it lazily and fails at startup if it is absent, never falling back to
132
+ files without saying so.
133
+
134
+ If you want the agent rather than the interface, use the runtime directly:
135
+
136
+ ```python
137
+ import asyncio
138
+ from tau_agent_core import create_agent_session
139
+
140
+
141
+ async def main():
142
+ session = create_agent_session(model="gpt-4o", tools=["read", "bash"])
143
+ print(await session.prompt("What is in this directory?"))
144
+
145
+
146
+ asyncio.run(main())
147
+ ```
148
+
149
+ ## Docs
150
+
151
+ - `docs/tau-coding-agent.md` — design notes for this package.
152
+ - `docs/CLI-PLAN.md` — the flag set and its status.
153
+ - `docs/REMOTE-CONTROL.md`, `docs/RPC-PROTOCOL.md` — `--mode rpc`.
154
+ - `docs/EXTENSIONS-WALKTHROUGH.md` — writing an extension to load with `-e`.
155
+
156
+ Repository: <https://github.com/jmccardle/tau>
157
+
158
+ ## The rest of Tau
159
+
160
+ | Distribution | Imports as | What it is |
161
+ |---|---|---|
162
+ | `ffwf-tau-llm` | `tau_llm` | the provider and streaming layer |
163
+ | `ffwf-tau-agent-core` | `tau_agent_core` | the agent loop, tools, sessions, extensions |
164
+ | `ffwf-tau-jmfts` | `tau_jmfts` | a JMFTS-backed session store |
165
+
166
+ MIT © Fight Fire with Fire Robotics, LLC
@@ -0,0 +1,136 @@
1
+ # ffwf-tau-coding-agent
2
+
3
+ The interface of **Tau**, a programmable coding agent harness: the `tau`
4
+ command, its Textual terminal UI, and the headless run paths behind `tau -p` and
5
+ `tau --mode rpc`.
6
+
7
+ Tau began as a Python port of the TypeScript project
8
+ [pi-mono](https://github.com/badlogic/pi-mono), which is still read as the
9
+ reference implementation when porting or debugging; it now diverges from pi
10
+ deliberately in several places.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ pip install ffwf-tau-coding-agent # headless: the CLI, `tau -p`, `--mode rpc`
16
+ pip install 'ffwf-tau-coding-agent[tui]' # add the interactive TUI
17
+ ```
18
+
19
+ Python 3.11 or newer. Pulls in `ffwf-tau-agent-core`, and through it
20
+ `ffwf-tau-llm`. Note the `ffwf-` prefix: `tau-llm` and `tau-ai` on PyPI are
21
+ unrelated projects.
22
+
23
+ | Extra | Adds | Needed for |
24
+ |---|---|---|
25
+ | `ffwf-tau-coding-agent[tui]` | `textual`, `rich` | the interactive TUI. `tau -p` runs a full turn without it |
26
+ | `ffwf-tau-coding-agent[jmfts]` | `ffwf-tau-jmfts` | `--store jmfts` session storage |
27
+
28
+ **The interface is an extra, not a base dependency.** A headless install is 15
29
+ packages and 13 MB; adding `[tui]` makes it 27 packages and 31 MB. Both extras
30
+ report their own absence — asking for the TUI or for `--store jmfts` without the
31
+ matching extra fails with the install command you need, not a traceback.
32
+
33
+ ### Two names for one command
34
+
35
+ The install puts **both `tau` and `ffwf-tau`** on your PATH. They are the same
36
+ entry point, not a link: each is a wrapper pip owns and removes on uninstall.
37
+
38
+ Type `tau`. PyPI reserves distribution names but not command names, and at least
39
+ one unrelated project ships its own `tau`; in an environment holding both,
40
+ whichever installed last owns the name and neither pip nor uv says a word.
41
+ `ffwf-tau` cannot be taken, which makes it the right name inside scripts,
42
+ systemd units, and Dockerfiles. If `tau --version` does not report τ,
43
+ `ffwf-tau --version` will.
44
+
45
+ ## Run
46
+
47
+ ```bash
48
+ tau # interactive TUI
49
+ tau -p "list the files here" # headless: print a transcript, exit
50
+ tau -p --mode json "..." # headless: JSONL lifecycle events instead of text
51
+ tau --mode rpc # drive τ as a subprocess over stdio (JSON-RPC 2.0)
52
+ tau -e permission_gate.py # load an extension from a path
53
+ tau --help # the exact contract; treat this page as the stale one
54
+ ```
55
+
56
+ `tau -p` and `tau --mode rpc` both write and resume real sessions under
57
+ `~/.tau/sessions/`, so a headless run appears in the TUI's sidebar and can be
58
+ picked up interactively later. `--no-session` means nothing is persisted.
59
+
60
+ Flags worth knowing: `-m/--model`, `-t/--tools` and `-nt/--no-tools`,
61
+ `-e/--extension PATH`, `--thinking {off,minimal,low,medium,high,xhigh}`,
62
+ `-c/--continue`, `--session REF`, `--fork REF`, `--store {file,jmfts}`.
63
+
64
+ ## Configure
65
+
66
+ On first run τ writes `~/.tau/config.json` from a default shipped inside the
67
+ package. It holds the `models` map that `--model` resolves names against, plus
68
+ per-extension config and the `session_store` block. A model entry names the
69
+ provider, the base URL, and the context window, so pointing τ at a local
70
+ OpenAI-compatible server (vLLM, llama.cpp, Ollama) is a config edit rather than
71
+ a code change.
72
+
73
+ ## The TUI
74
+
75
+ Built on [Textual](https://textual.textualize.io/). Streamed text renders at
76
+ 30 Hz; tool calls and their results appear as their own blocks.
77
+
78
+ | Key | Action |
79
+ |---|---|
80
+ | `Ctrl+Enter` | send the message |
81
+ | `Ctrl+B` | toggle the session sidebar |
82
+ | `Ctrl+N` | new chat |
83
+ | `Ctrl+G` | browse the conversation tree |
84
+ | `Ctrl+Z` | roll back the last turn |
85
+ | `Ctrl+R` / `Ctrl+T` | show or hide reasoning / tool blocks |
86
+ | `Ctrl+E` | extensions |
87
+ | `Ctrl+P` | command palette |
88
+ | `Escape` | cancel the current generation |
89
+
90
+ **The sidebar and the tree browser are two different things.** The sidebar picks
91
+ *which session to open* — a flat list grouped by date. `Ctrl+G` opens a browser
92
+ over the branch structure *inside the current conversation*, where you navigate
93
+ to an earlier node, summarise a branch, or elide a span. Typed commands cover
94
+ the rest: `/compact`, `/tree`, `/fork`, `/extensions`.
95
+
96
+ ## Why it is a separate package
97
+
98
+ Everything here is presentation and process management. The agent lives in
99
+ `ffwf-tau-agent-core`, which has no Textual dependency and never imports this
100
+ package. `ffwf-tau-jmfts` is a peer rather than a dependency: `--store jmfts`
101
+ imports it lazily and fails at startup if it is absent, never falling back to
102
+ files without saying so.
103
+
104
+ If you want the agent rather than the interface, use the runtime directly:
105
+
106
+ ```python
107
+ import asyncio
108
+ from tau_agent_core import create_agent_session
109
+
110
+
111
+ async def main():
112
+ session = create_agent_session(model="gpt-4o", tools=["read", "bash"])
113
+ print(await session.prompt("What is in this directory?"))
114
+
115
+
116
+ asyncio.run(main())
117
+ ```
118
+
119
+ ## Docs
120
+
121
+ - `docs/tau-coding-agent.md` — design notes for this package.
122
+ - `docs/CLI-PLAN.md` — the flag set and its status.
123
+ - `docs/REMOTE-CONTROL.md`, `docs/RPC-PROTOCOL.md` — `--mode rpc`.
124
+ - `docs/EXTENSIONS-WALKTHROUGH.md` — writing an extension to load with `-e`.
125
+
126
+ Repository: <https://github.com/jmccardle/tau>
127
+
128
+ ## The rest of Tau
129
+
130
+ | Distribution | Imports as | What it is |
131
+ |---|---|---|
132
+ | `ffwf-tau-llm` | `tau_llm` | the provider and streaming layer |
133
+ | `ffwf-tau-agent-core` | `tau_agent_core` | the agent loop, tools, sessions, extensions |
134
+ | `ffwf-tau-jmfts` | `tau_jmfts` | a JMFTS-backed session store |
135
+
136
+ MIT © Fight Fire with Fire Robotics, LLC
@@ -0,0 +1,107 @@
1
+ [project]
2
+ name = "ffwf-tau-coding-agent"
3
+ # The version is not a literal here. Each distribution reads it from its own
4
+ # package's ``__version__``, so the wheel metadata and what the code reports can
5
+ # never disagree -- they are the same line. Bump that line to cut a release.
6
+ dynamic = ["version"]
7
+ description = "τ-coding-agent: Interactive TUI (Textual) + CLI"
8
+ requires-python = ">=3.11"
9
+ readme = "README.md"
10
+ # SPDX expression + the file itself (PEP 639), which is why build-system below
11
+ # needs setuptools >= 77.0.3: earlier versions ignore `license-files` and the
12
+ # wheel ships without the licence text. Each package carries its own copy of the
13
+ # repo-root LICENSE because a wheel is built from one package directory and
14
+ # cannot reach outside it.
15
+ license = "MIT"
16
+ license-files = ["LICENSE"]
17
+ authors = [{ name = "Fight Fire with Fire Robotics, LLC" }]
18
+ dependencies = [
19
+ "ffwf-tau-agent-core==0.9.2",
20
+ ]
21
+
22
+ [project.optional-dependencies]
23
+ # The interface. `tau -p` (cli.py -> headless.py) never imports textual: the only
24
+ # entry into the Textual app is a lazy `from tau_coding_agent.app import Parley`
25
+ # inside cli._launch_tui, so a headless install runs a full turn -- extensions and
26
+ # all -- with neither library present. Measured: 15 packages / 13 MB without this
27
+ # extra, 27 packages / 31 MB with it.
28
+ #
29
+ # rich belongs here rather than in the base: its importers are app.py,
30
+ # chat_widgets.py and testing/render.py, and every one of them imports textual at
31
+ # module scope too. tau_coding_agent.testing is therefore TUI-side as a whole --
32
+ # testing/__init__.py re-exports render -- and needs this extra.
33
+ tui = [
34
+ "textual>=0.47",
35
+ "rich",
36
+ ]
37
+ # The session store. tau-jmfts is a separate distribution and stays that way; this
38
+ # extra is only a convenience spelling for it. store_factory.py imports it lazily
39
+ # and converts a missing package into a StoreError that names this extra, so
40
+ # `--store jmfts` without it fails with an instruction rather than a traceback.
41
+ jmfts = ["ffwf-tau-jmfts==0.9.2"]
42
+ dev = [
43
+ "ffwf-tau-coding-agent[tui,jmfts]==0.9.2",
44
+ "pytest",
45
+ "pytest-asyncio",
46
+ "ruff",
47
+ # Pinned, unlike every other dev tool here. mypy has no "state your rule set"
48
+ # knob the way ruff does, so its findings move with its version: verified on
49
+ # this tree, 2.1.0 reports 0 errors and 2.3.1 reports 50 across 12 files.
50
+ # Those 50 look like real findings that a newer mypy got better at seeing --
51
+ # mostly no-any-return -- so they are scheduled, not suppressed: clearing
52
+ # them is its own pass, and until then the gate must mean the same thing in
53
+ # every clone. Raise this bound WITH that pass, never to make a hook go green.
54
+ "mypy==2.1.*",
55
+ # TUI visual inspection (see docs/textual-headless-testing.md):
56
+ # textual-dev -> `textual run --dev` live CSS editing, `textual console`
57
+ # pytest-textual-snapshot -> SVG visual-regression tests
58
+ # cairosvg -> rasterize an exported SVG so a human can look at it
59
+ "textual-dev",
60
+ "pytest-textual-snapshot",
61
+ "cairosvg",
62
+ ]
63
+
64
+ # The PUBLIC repository only. This tree has a second, private git remote whose
65
+ # history is unfiltered; its hostname must never reach this file or a README,
66
+ # because both are uploaded verbatim (pyproject.toml ships inside the sdist).
67
+ # tests/test_packaging.py enforces that.
68
+ [project.urls]
69
+ Homepage = "https://ffwfrobotics.github.io/tau/"
70
+ Documentation = "https://ffwfrobotics.github.io/tau/"
71
+ Repository = "https://github.com/jmccardle/tau"
72
+ Issues = "https://github.com/jmccardle/tau/issues"
73
+
74
+ [build-system]
75
+ requires = ["setuptools>=77.0.3", "wheel"]
76
+ build-backend = "setuptools.build_meta"
77
+
78
+ [tool.setuptools.packages.find]
79
+ where = ["src"]
80
+
81
+ [tool.setuptools.dynamic]
82
+ version = { attr = "tau_coding_agent.__version__" }
83
+
84
+ # Non-.py files the installed package reads at runtime. Both are load-bearing:
85
+ # bootstrap_config() reads tau_default_config.json out of the installed package on
86
+ # first run (without it, `pip install tau-coding-agent` ships a τ that dies with
87
+ # FileNotFoundError before it can print a prompt), and Parley.CSS_PATH resolves
88
+ # parley.tcss next to app.py (without it, the TUI raises on startup with no
89
+ # stylesheet). Covered by tests/test_packaging.py.
90
+ [tool.setuptools.package-data]
91
+ tau_coding_agent = ["tau_default_config.json", "parley.tcss"]
92
+
93
+ # Two names for one entry point, not a symlink. A wheel cannot portably express a
94
+ # symlink, and pip generates each console script as an independent wrapper it owns
95
+ # in its RECORD -- so two entries uninstall as cleanly as one, on Windows too.
96
+ #
97
+ # `tau` is the name to type. It is also the name PyPI cannot reserve: console
98
+ # script names have no registry, and at least one active project (`tau-ai`, an
99
+ # unrelated pi-style harness) already ships its own `tau`. In an environment
100
+ # holding both, whichever installed last owns the name, silently.
101
+ #
102
+ # `ffwf-tau` is the name that cannot be taken. It is the fallback when `tau` is
103
+ # not ours, and the stable name for scripts, systemd units and Dockerfiles, where
104
+ # ergonomics do not matter and being the right program does.
105
+ [project.scripts]
106
+ tau = "tau_coding_agent.cli:main"
107
+ ffwf-tau = "tau_coding_agent.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: ffwf-tau-coding-agent
3
+ Version: 0.9.2
4
+ Summary: τ-coding-agent: Interactive TUI (Textual) + CLI
5
+ Author: Fight Fire with Fire Robotics, LLC
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://ffwfrobotics.github.io/tau/
8
+ Project-URL: Documentation, https://ffwfrobotics.github.io/tau/
9
+ Project-URL: Repository, https://github.com/jmccardle/tau
10
+ Project-URL: Issues, https://github.com/jmccardle/tau/issues
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: ffwf-tau-agent-core==0.9.2
15
+ Provides-Extra: tui
16
+ Requires-Dist: textual>=0.47; extra == "tui"
17
+ Requires-Dist: rich; extra == "tui"
18
+ Provides-Extra: jmfts
19
+ Requires-Dist: ffwf-tau-jmfts==0.9.2; extra == "jmfts"
20
+ Provides-Extra: dev
21
+ Requires-Dist: ffwf-tau-coding-agent[jmfts,tui]==0.9.2; extra == "dev"
22
+ Requires-Dist: pytest; extra == "dev"
23
+ Requires-Dist: pytest-asyncio; extra == "dev"
24
+ Requires-Dist: ruff; extra == "dev"
25
+ Requires-Dist: mypy==2.1.*; extra == "dev"
26
+ Requires-Dist: textual-dev; extra == "dev"
27
+ Requires-Dist: pytest-textual-snapshot; extra == "dev"
28
+ Requires-Dist: cairosvg; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # ffwf-tau-coding-agent
32
+
33
+ The interface of **Tau**, a programmable coding agent harness: the `tau`
34
+ command, its Textual terminal UI, and the headless run paths behind `tau -p` and
35
+ `tau --mode rpc`.
36
+
37
+ Tau began as a Python port of the TypeScript project
38
+ [pi-mono](https://github.com/badlogic/pi-mono), which is still read as the
39
+ reference implementation when porting or debugging; it now diverges from pi
40
+ deliberately in several places.
41
+
42
+ ## Install
43
+
44
+ ```bash
45
+ pip install ffwf-tau-coding-agent # headless: the CLI, `tau -p`, `--mode rpc`
46
+ pip install 'ffwf-tau-coding-agent[tui]' # add the interactive TUI
47
+ ```
48
+
49
+ Python 3.11 or newer. Pulls in `ffwf-tau-agent-core`, and through it
50
+ `ffwf-tau-llm`. Note the `ffwf-` prefix: `tau-llm` and `tau-ai` on PyPI are
51
+ unrelated projects.
52
+
53
+ | Extra | Adds | Needed for |
54
+ |---|---|---|
55
+ | `ffwf-tau-coding-agent[tui]` | `textual`, `rich` | the interactive TUI. `tau -p` runs a full turn without it |
56
+ | `ffwf-tau-coding-agent[jmfts]` | `ffwf-tau-jmfts` | `--store jmfts` session storage |
57
+
58
+ **The interface is an extra, not a base dependency.** A headless install is 15
59
+ packages and 13 MB; adding `[tui]` makes it 27 packages and 31 MB. Both extras
60
+ report their own absence — asking for the TUI or for `--store jmfts` without the
61
+ matching extra fails with the install command you need, not a traceback.
62
+
63
+ ### Two names for one command
64
+
65
+ The install puts **both `tau` and `ffwf-tau`** on your PATH. They are the same
66
+ entry point, not a link: each is a wrapper pip owns and removes on uninstall.
67
+
68
+ Type `tau`. PyPI reserves distribution names but not command names, and at least
69
+ one unrelated project ships its own `tau`; in an environment holding both,
70
+ whichever installed last owns the name and neither pip nor uv says a word.
71
+ `ffwf-tau` cannot be taken, which makes it the right name inside scripts,
72
+ systemd units, and Dockerfiles. If `tau --version` does not report τ,
73
+ `ffwf-tau --version` will.
74
+
75
+ ## Run
76
+
77
+ ```bash
78
+ tau # interactive TUI
79
+ tau -p "list the files here" # headless: print a transcript, exit
80
+ tau -p --mode json "..." # headless: JSONL lifecycle events instead of text
81
+ tau --mode rpc # drive τ as a subprocess over stdio (JSON-RPC 2.0)
82
+ tau -e permission_gate.py # load an extension from a path
83
+ tau --help # the exact contract; treat this page as the stale one
84
+ ```
85
+
86
+ `tau -p` and `tau --mode rpc` both write and resume real sessions under
87
+ `~/.tau/sessions/`, so a headless run appears in the TUI's sidebar and can be
88
+ picked up interactively later. `--no-session` means nothing is persisted.
89
+
90
+ Flags worth knowing: `-m/--model`, `-t/--tools` and `-nt/--no-tools`,
91
+ `-e/--extension PATH`, `--thinking {off,minimal,low,medium,high,xhigh}`,
92
+ `-c/--continue`, `--session REF`, `--fork REF`, `--store {file,jmfts}`.
93
+
94
+ ## Configure
95
+
96
+ On first run τ writes `~/.tau/config.json` from a default shipped inside the
97
+ package. It holds the `models` map that `--model` resolves names against, plus
98
+ per-extension config and the `session_store` block. A model entry names the
99
+ provider, the base URL, and the context window, so pointing τ at a local
100
+ OpenAI-compatible server (vLLM, llama.cpp, Ollama) is a config edit rather than
101
+ a code change.
102
+
103
+ ## The TUI
104
+
105
+ Built on [Textual](https://textual.textualize.io/). Streamed text renders at
106
+ 30 Hz; tool calls and their results appear as their own blocks.
107
+
108
+ | Key | Action |
109
+ |---|---|
110
+ | `Ctrl+Enter` | send the message |
111
+ | `Ctrl+B` | toggle the session sidebar |
112
+ | `Ctrl+N` | new chat |
113
+ | `Ctrl+G` | browse the conversation tree |
114
+ | `Ctrl+Z` | roll back the last turn |
115
+ | `Ctrl+R` / `Ctrl+T` | show or hide reasoning / tool blocks |
116
+ | `Ctrl+E` | extensions |
117
+ | `Ctrl+P` | command palette |
118
+ | `Escape` | cancel the current generation |
119
+
120
+ **The sidebar and the tree browser are two different things.** The sidebar picks
121
+ *which session to open* — a flat list grouped by date. `Ctrl+G` opens a browser
122
+ over the branch structure *inside the current conversation*, where you navigate
123
+ to an earlier node, summarise a branch, or elide a span. Typed commands cover
124
+ the rest: `/compact`, `/tree`, `/fork`, `/extensions`.
125
+
126
+ ## Why it is a separate package
127
+
128
+ Everything here is presentation and process management. The agent lives in
129
+ `ffwf-tau-agent-core`, which has no Textual dependency and never imports this
130
+ package. `ffwf-tau-jmfts` is a peer rather than a dependency: `--store jmfts`
131
+ imports it lazily and fails at startup if it is absent, never falling back to
132
+ files without saying so.
133
+
134
+ If you want the agent rather than the interface, use the runtime directly:
135
+
136
+ ```python
137
+ import asyncio
138
+ from tau_agent_core import create_agent_session
139
+
140
+
141
+ async def main():
142
+ session = create_agent_session(model="gpt-4o", tools=["read", "bash"])
143
+ print(await session.prompt("What is in this directory?"))
144
+
145
+
146
+ asyncio.run(main())
147
+ ```
148
+
149
+ ## Docs
150
+
151
+ - `docs/tau-coding-agent.md` — design notes for this package.
152
+ - `docs/CLI-PLAN.md` — the flag set and its status.
153
+ - `docs/REMOTE-CONTROL.md`, `docs/RPC-PROTOCOL.md` — `--mode rpc`.
154
+ - `docs/EXTENSIONS-WALKTHROUGH.md` — writing an extension to load with `-e`.
155
+
156
+ Repository: <https://github.com/jmccardle/tau>
157
+
158
+ ## The rest of Tau
159
+
160
+ | Distribution | Imports as | What it is |
161
+ |---|---|---|
162
+ | `ffwf-tau-llm` | `tau_llm` | the provider and streaming layer |
163
+ | `ffwf-tau-agent-core` | `tau_agent_core` | the agent loop, tools, sessions, extensions |
164
+ | `ffwf-tau-jmfts` | `tau_jmfts` | a JMFTS-backed session store |
165
+
166
+ MIT © Fight Fire with Fire Robotics, LLC