@dungle-scrubs/tallow 0.8.5 → 0.8.7
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.
- package/README.md +121 -203
- package/dist/auth-hardening.d.ts +17 -23
- package/dist/auth-hardening.d.ts.map +1 -1
- package/dist/auth-hardening.js +78 -33
- package/dist/auth-hardening.js.map +1 -1
- package/dist/cli.js +152 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/fatal-errors.js +1 -1
- package/dist/fatal-errors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive-mode-patch.d.ts +2 -0
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +36 -0
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/plugins.d.ts +27 -2
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +163 -2
- package/dist/plugins.js.map +1 -1
- package/dist/project-trust-banner.d.ts +32 -0
- package/dist/project-trust-banner.d.ts.map +1 -0
- package/dist/project-trust-banner.js +60 -0
- package/dist/project-trust-banner.js.map +1 -0
- package/dist/sdk.d.ts +78 -2
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +375 -96
- package/dist/sdk.js.map +1 -1
- package/dist/startup-profile.d.ts +31 -0
- package/dist/startup-profile.d.ts.map +1 -0
- package/dist/startup-profile.js +30 -0
- package/dist/startup-profile.js.map +1 -0
- package/dist/startup-timing.d.ts +21 -0
- package/dist/startup-timing.d.ts.map +1 -0
- package/dist/startup-timing.js +50 -0
- package/dist/startup-timing.js.map +1 -0
- package/extensions/__integration__/shell-policy-confirm.test.ts +85 -4
- package/extensions/_icons/extension.json +10 -0
- package/extensions/_shared/__tests__/lazy-init.test.ts +84 -0
- package/extensions/_shared/__tests__/permissions.test.ts +59 -1
- package/extensions/_shared/__tests__/shell-policy.test.ts +67 -0
- package/extensions/_shared/interop-events.ts +4 -1
- package/extensions/_shared/lazy-init.ts +177 -0
- package/extensions/_shared/permissions.ts +355 -31
- package/extensions/_shared/shell-policy.ts +29 -7
- package/extensions/agent-commands-tool/extension.json +10 -0
- package/extensions/ask-user-question-tool/extension.json +10 -0
- package/extensions/background-task-tool/__tests__/render-style.test.ts +32 -0
- package/extensions/background-task-tool/extension.json +12 -0
- package/extensions/background-task-tool/index.ts +203 -65
- package/extensions/bash-tool-enhanced/__tests__/render-style.test.ts +45 -0
- package/extensions/bash-tool-enhanced/extension.json +11 -0
- package/extensions/bash-tool-enhanced/index.ts +53 -38
- package/extensions/cd-tool/extension.json +11 -0
- package/extensions/cheatsheet/extension.json +10 -0
- package/extensions/claude-bridge/extension.json +10 -0
- package/extensions/claude-bridge/index.ts +2 -1
- package/extensions/clear/extension.json +10 -0
- package/extensions/command-expansion/__tests__/expansion.test.ts +84 -1
- package/extensions/command-expansion/extension.json +10 -0
- package/extensions/command-expansion/index.ts +134 -18
- package/extensions/command-prompt/extension.json +11 -0
- package/extensions/context-files/__tests__/add-dir.test.ts +1 -1
- package/extensions/context-files/__tests__/lazy-init.test.ts +242 -0
- package/extensions/context-files/extension.json +11 -0
- package/extensions/context-files/index.ts +77 -15
- package/extensions/context-fork/__tests__/context-fork.test.ts +88 -0
- package/extensions/context-fork/extension.json +10 -0
- package/extensions/context-fork/index.ts +96 -16
- package/extensions/context-usage/extension.json +10 -0
- package/extensions/custom-footer/extension.json +10 -0
- package/extensions/debug/__tests__/diagnostics-commands.test.ts +244 -0
- package/extensions/debug/extension.json +38 -0
- package/extensions/debug/index.ts +149 -81
- package/extensions/edit-tool-enhanced/__tests__/render-style.test.ts +47 -0
- package/extensions/edit-tool-enhanced/extension.json +10 -0
- package/extensions/edit-tool-enhanced/index.ts +32 -9
- package/extensions/file-reference/extension.json +10 -0
- package/extensions/git-status/extension.json +10 -0
- package/extensions/health/extension.json +10 -0
- package/extensions/hooks/extension.json +38 -0
- package/extensions/init/extension.json +10 -0
- package/extensions/lsp/__tests__/lsp-timeouts.test.ts +236 -2
- package/extensions/lsp/__tests__/lsp-tools.test.ts +17 -1
- package/extensions/lsp/extension.json +18 -0
- package/extensions/lsp/index.ts +127 -10
- package/extensions/mcp-adapter-tool/__tests__/lazy-init.test.ts +349 -0
- package/extensions/mcp-adapter-tool/extension.json +12 -0
- package/extensions/mcp-adapter-tool/index.ts +133 -41
- package/extensions/minimal-skill-display/extension.json +10 -0
- package/extensions/output-styles-tool/extension.json +11 -0
- package/extensions/permissions/extension.json +11 -0
- package/extensions/permissions/index.ts +34 -12
- package/extensions/plan-mode-tool/extension.json +19 -0
- package/extensions/progress-indicator/extension.json +10 -0
- package/extensions/prompt-suggestions/extension.json +10 -0
- package/extensions/random-spinner/extension.json +10 -0
- package/extensions/read-tool-enhanced/__tests__/render-style.test.ts +49 -0
- package/extensions/read-tool-enhanced/extension.json +11 -0
- package/extensions/read-tool-enhanced/index.ts +58 -34
- package/extensions/rewind/extension.json +11 -0
- package/extensions/session-memory/extension.json +10 -0
- package/extensions/session-namer/extension.json +10 -0
- package/extensions/shell-interpolation/extension.json +10 -0
- package/extensions/show-system-prompt/extension.json +10 -0
- package/extensions/skill-commands/extension.json +10 -0
- package/extensions/slash-command-bridge/extension.json +23 -3
- package/extensions/stats/extension.json +11 -0
- package/extensions/subagent-tool/__tests__/auto-cheap-model.test.ts +87 -36
- package/extensions/subagent-tool/__tests__/background-retention.test.ts +135 -0
- package/extensions/subagent-tool/__tests__/model-router-explicit-resolution.test.ts +114 -0
- package/extensions/subagent-tool/__tests__/model-router-routing-options.test.ts +125 -0
- package/extensions/subagent-tool/__tests__/model-router-select-options.test.ts +157 -0
- package/extensions/subagent-tool/__tests__/model-router.test.ts +61 -0
- package/extensions/subagent-tool/__tests__/presentation-rendering.test.ts +326 -0
- package/extensions/subagent-tool/__tests__/process-liveness.test.ts +141 -0
- package/extensions/subagent-tool/__tests__/subagent-status-retention.test.ts +144 -0
- package/extensions/subagent-tool/extension.json +11 -0
- package/extensions/subagent-tool/index.ts +927 -345
- package/extensions/subagent-tool/model-router.ts +653 -20
- package/extensions/subagent-tool/process.ts +414 -16
- package/extensions/subagent-tool/widget.ts +171 -10
- package/extensions/tasks/__tests__/widget-subagents.test.ts +405 -0
- package/extensions/tasks/agents/index.ts +9 -18
- package/extensions/tasks/commands/register-tasks-extension.ts +288 -128
- package/extensions/tasks/extension.json +27 -1
- package/extensions/tasks/ui/index.ts +13 -11
- package/extensions/teams-tool/__tests__/dashboard-feed.test.ts +87 -0
- package/extensions/teams-tool/__tests__/message-retention.test.ts +149 -0
- package/extensions/teams-tool/dashboard/feed.ts +63 -18
- package/extensions/teams-tool/dashboard/state.ts +29 -9
- package/extensions/teams-tool/dashboard.ts +103 -63
- package/extensions/teams-tool/extension.json +20 -0
- package/extensions/teams-tool/sessions/spawn.ts +1 -1
- package/extensions/teams-tool/store.ts +80 -0
- package/extensions/theme-selector/extension.json +12 -0
- package/extensions/tool-display/extension.json +8 -0
- package/extensions/tool-display/index.ts +178 -1
- package/extensions/upstream-check/extension.json +10 -0
- package/extensions/web-fetch-tool/extension.json +10 -0
- package/extensions/web-search-tool/extension.json +10 -0
- package/extensions/wezterm-notify/__tests__/index.test.ts +232 -0
- package/extensions/wezterm-notify/__tests__/lua-behavior.test.ts +62 -0
- package/extensions/wezterm-notify/extension.json +32 -0
- package/extensions/wezterm-notify/index.ts +236 -0
- package/extensions/wezterm-notify/wezterm/tallow.lua +211 -0
- package/extensions/wezterm-pane-control/extension.json +11 -0
- package/extensions/write-tool-enhanced/extension.json +15 -1
- package/extensions/write-tool-enhanced/index.ts +25 -8
- package/package.json +6 -5
- package/schemas/settings.schema.json +75 -0
- package/skills/tallow-expert/SKILL.md +2 -2
- package/templates/commands/implement.md +21 -5
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<h1 align="center">Tallow</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
|
|
8
|
+
A modular coding agent for your terminal. Built on <a href="https://github.com/nicobrinkkemper/pi-coding-agent">pi</a>.
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
@@ -23,64 +23,35 @@
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
25
|
<p align="center">
|
|
26
|
-
<img src="assets/screenshot.
|
|
26
|
+
<img src="assets/screenshot-annotated.png" alt="Tallow session showing status bar, auto-named session, and model selection" />
|
|
27
|
+
<br />
|
|
28
|
+
<sub>Shown with a customized <a href="https://wezfurlong.org/wezterm/">WezTerm</a> configuration.</sub>
|
|
27
29
|
</p>
|
|
28
30
|
|
|
29
|
-
Tallow is
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
issue and PR response times.
|
|
31
|
+
Tallow is a terminal coding agent that starts minimal and scales up. Install only the
|
|
32
|
+
extensions, themes, and agents your project needs, or enable everything. It drops into
|
|
33
|
+
existing Claude Code projects via `.claude/` bridging, so nothing breaks when you switch.
|
|
34
|
+
Ships with 50 extensions, 34 themes, and 10 specialized agents.
|
|
34
35
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
- **Most valuable capabilities (non-exhaustive):**
|
|
38
|
-
- **Multi-model routing** — route work by intent/cost (`auto-cheap`, `auto-balanced`,
|
|
39
|
-
`auto-premium`) across available providers
|
|
40
|
-
- **Multi-agent teams** — coordinate specialized agents with shared task boards,
|
|
41
|
-
dependencies, messaging, and archive/resume
|
|
42
|
-
- **Context fork** — run isolated subprocess workflows with separate tools/models and
|
|
43
|
-
merge results back cleanly
|
|
44
|
-
- **Workspace rewind snapshots** — roll file changes back to earlier conversation turns
|
|
45
|
-
- **Task primitives + background execution** — explicit task lifecycle tracking and
|
|
46
|
-
non-blocking long-running work
|
|
47
|
-
- **Built-in LSP navigation** — definitions, references, hover, and workspace symbol
|
|
48
|
-
lookup
|
|
49
|
-
- **Opt-in and modular** — install only the pieces you need, skip the rest
|
|
50
|
-
- **Claude Code compatible** — `.claude/` + `.tallow/` directories are bridged so existing
|
|
51
|
-
project workflows keep working
|
|
52
|
-
- **Fully featured when you want it** — 49 bundled extensions, 34 themes, 8 slash commands,
|
|
53
|
-
and 10 specialized agents
|
|
54
|
-
- **Session naming** — auto-generated descriptive names for each session, shown in footer
|
|
55
|
-
and `--list`
|
|
56
|
-
- **Debug mode** — structured JSONL diagnostic logging with `/diag` command
|
|
57
|
-
- **SDK** — embed Tallow in your own scripts and orchestrators
|
|
58
|
-
- **User-owned config** — agents and commands are installed to `~/.tallow/` where you can
|
|
59
|
-
edit, remove, or add your own
|
|
60
|
-
|
|
61
|
-
Read the full [documentation](https://tallow.dungle-scrubs.com).
|
|
62
|
-
|
|
63
|
-
## Requirements
|
|
64
|
-
|
|
65
|
-
- Node.js ≥ 22
|
|
66
|
-
- An API key for at least one supported LLM provider (Anthropic, OpenAI, Google, etc.)
|
|
67
|
-
|
|
68
|
-
## Installation
|
|
69
|
-
|
|
70
|
-
### Global install
|
|
36
|
+
## Quick start
|
|
71
37
|
|
|
72
38
|
```bash
|
|
73
|
-
bun install -g tallow
|
|
74
|
-
tallow install
|
|
39
|
+
npm install -g tallow # or: pnpm add -g tallow / bun install -g tallow
|
|
40
|
+
tallow install # pick extensions, themes, agents
|
|
41
|
+
tallow # start coding
|
|
75
42
|
```
|
|
76
43
|
|
|
77
|
-
Or without
|
|
44
|
+
Or try it without installing globally:
|
|
78
45
|
|
|
79
46
|
```bash
|
|
80
|
-
bunx tallow install
|
|
47
|
+
npx tallow install # or: pnpm dlx tallow install / bunx tallow install
|
|
81
48
|
```
|
|
82
49
|
|
|
83
|
-
|
|
50
|
+
> Requires Node.js ≥ 22 and an API key for at least one LLM provider
|
|
51
|
+
> (Anthropic, OpenAI, Google, etc.)
|
|
52
|
+
|
|
53
|
+
<details>
|
|
54
|
+
<summary><strong>Install from source</strong></summary>
|
|
84
55
|
|
|
85
56
|
```bash
|
|
86
57
|
git clone https://github.com/dungle-scrubs/tallow.git
|
|
@@ -93,155 +64,85 @@ node dist/install.js
|
|
|
93
64
|
The installer walks you through selecting extensions, themes, and agents,
|
|
94
65
|
then links the `tallow` binary globally.
|
|
95
66
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
# Interactive mode
|
|
100
|
-
tallow
|
|
101
|
-
|
|
102
|
-
# Single-shot prompt
|
|
103
|
-
tallow -p "Fix the failing tests"
|
|
67
|
+
</details>
|
|
104
68
|
|
|
105
|
-
|
|
106
|
-
echo "Explain this error" | tallow
|
|
107
|
-
cat README.md | tallow -p "Summarize this"
|
|
108
|
-
git diff | tallow -p "Review these changes"
|
|
69
|
+
## Highlights
|
|
109
70
|
|
|
110
|
-
|
|
111
|
-
|
|
71
|
+
**Multi-model routing** — Route tasks by intent and cost across providers.
|
|
72
|
+
`auto-cheap` for boilerplate, `auto-balanced` for everyday work, `auto-premium`
|
|
73
|
+
when accuracy matters.
|
|
112
74
|
|
|
113
|
-
|
|
114
|
-
|
|
75
|
+
**Multi-agent teams** — Spawn specialized agents that share a task board with
|
|
76
|
+
dependencies, messaging, and archive/resume. Coordinate complex work across
|
|
77
|
+
multiple models in parallel.
|
|
115
78
|
|
|
116
|
-
|
|
117
|
-
|
|
79
|
+
**Context fork** — Branch into an isolated subprocess with its own tools and model,
|
|
80
|
+
then merge results back into the main session.
|
|
118
81
|
|
|
119
|
-
|
|
120
|
-
|
|
82
|
+
**Workspace rewind** — Every conversation turn snapshots your file changes. Roll back
|
|
83
|
+
to any earlier turn when something goes wrong.
|
|
121
84
|
|
|
122
|
-
|
|
123
|
-
|
|
85
|
+
**Background tasks** — Kick off long-running work without blocking the session.
|
|
86
|
+
Track task lifecycle explicitly and check back when ready.
|
|
124
87
|
|
|
125
|
-
|
|
126
|
-
|
|
88
|
+
**LSP** — Jump to definitions, find references, inspect types, and search
|
|
89
|
+
workspace symbols — no editor required.
|
|
127
90
|
|
|
128
|
-
|
|
129
|
-
|
|
91
|
+
**Claude Code compatible** — Projects with `.claude/` directories (skills, agents,
|
|
92
|
+
commands) work without changes. Both `.tallow/` and `.claude/` are scanned;
|
|
93
|
+
`.tallow/` takes precedence.
|
|
130
94
|
|
|
131
|
-
|
|
132
|
-
|
|
95
|
+
**User-owned config** — Agents, commands, and extensions install to `~/.tallow/`
|
|
96
|
+
where you own them. Edit, remove, or add your own.
|
|
133
97
|
|
|
134
|
-
|
|
135
|
-
tallow --mode rpc
|
|
136
|
-
|
|
137
|
-
# Restrict available tools
|
|
138
|
-
tallow --tools read,grep,find # Only read, grep, find
|
|
139
|
-
tallow --tools readonly # Preset: read, grep, find, ls
|
|
140
|
-
tallow --tools none # Chat only, no tools
|
|
141
|
-
|
|
142
|
-
# Disable all extensions
|
|
143
|
-
tallow --no-extensions
|
|
144
|
-
|
|
145
|
-
# Print tallow home directory
|
|
146
|
-
tallow --home
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
`--api-key` was removed to avoid leaking secrets in process arguments.
|
|
150
|
-
Use `TALLOW_API_KEY` or `TALLOW_API_KEY_REF` instead.
|
|
151
|
-
|
|
152
|
-
### Piped input
|
|
153
|
-
|
|
154
|
-
Pipe file contents or command output directly into Tallow:
|
|
98
|
+
## Usage
|
|
155
99
|
|
|
156
100
|
```bash
|
|
157
|
-
#
|
|
158
|
-
|
|
101
|
+
# Interactive session
|
|
102
|
+
tallow
|
|
159
103
|
|
|
160
|
-
#
|
|
161
|
-
|
|
104
|
+
# Single-shot prompt
|
|
105
|
+
tallow -p "Fix the failing tests"
|
|
162
106
|
|
|
163
|
-
# Pipe
|
|
164
|
-
git
|
|
165
|
-
|
|
107
|
+
# Pipe in context
|
|
108
|
+
git diff | tallow -p "Review these changes"
|
|
109
|
+
cat src/main.ts | tallow -p "Find bugs in this code"
|
|
166
110
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
prompt. Piped input is capped at 10 MB. JSON mode (`--mode json`) also
|
|
170
|
-
accepts piped stdin.
|
|
111
|
+
# Continue the last session
|
|
112
|
+
tallow --continue
|
|
171
113
|
|
|
172
|
-
|
|
114
|
+
# Pick a model and thinking level
|
|
115
|
+
tallow -m anthropic/claude-sonnet-4-20250514 --thinking high
|
|
173
116
|
|
|
174
|
-
|
|
117
|
+
# List saved sessions
|
|
118
|
+
tallow --list
|
|
175
119
|
|
|
120
|
+
# Restrict available tools
|
|
121
|
+
tallow --tools readonly # read, grep, find, ls only
|
|
122
|
+
tallow --tools none # chat only, no tools
|
|
176
123
|
```
|
|
177
|
-
!`ls -la`
|
|
178
|
-
!`git status`
|
|
179
|
-
!`git branch --show-current`
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**Disabled by default.** Enable explicitly with either:
|
|
183
|
-
|
|
184
|
-
- `TALLOW_ENABLE_SHELL_INTERPOLATION=1` (or `TALLOW_SHELL_INTERPOLATION=1`)
|
|
185
|
-
- `"shellInterpolation": true` in `.tallow/settings.json` or `~/.tallow/settings.json`
|
|
186
124
|
|
|
187
|
-
|
|
188
|
-
replaces the pattern before reaching the agent. 5-second timeout, 1 MB
|
|
189
|
-
max output, non-recursive. High-risk explicit shell commands require
|
|
190
|
-
confirmation (`TALLOW_ALLOW_UNSAFE_SHELL=1` bypasses confirmation in
|
|
191
|
-
non-interactive environments).
|
|
125
|
+
### Extension catalog + least-privilege startup
|
|
192
126
|
|
|
193
|
-
|
|
127
|
+
Use the extension catalog to inspect what each extension does:
|
|
194
128
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
| `/implement-and-review` | Implement then self-review |
|
|
201
|
-
| `/review` | Review recent changes |
|
|
202
|
-
| `/fix` | Fix a bug from a description |
|
|
203
|
-
| `/test` | Write or fix tests |
|
|
204
|
-
| `/scout-and-plan` | Explore the codebase and create a plan |
|
|
205
|
-
| `/scaffold` | Scaffold a new project |
|
|
206
|
-
| `/question` | Introspect on agent reasoning without triggering actions |
|
|
207
|
-
|
|
208
|
-
## SDK
|
|
209
|
-
|
|
210
|
-
Use Tallow programmatically in your own tools:
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
import { createTallowSession } from "tallow";
|
|
214
|
-
|
|
215
|
-
const { session } = await createTallowSession({
|
|
216
|
-
provider: "anthropic",
|
|
217
|
-
modelId: "claude-sonnet-4-20250514",
|
|
218
|
-
});
|
|
129
|
+
```bash
|
|
130
|
+
tallow extensions # table view (default)
|
|
131
|
+
tallow extensions --json # machine-readable catalog
|
|
132
|
+
tallow extensions tasks # details for one extension ID
|
|
133
|
+
```
|
|
219
134
|
|
|
220
|
-
|
|
221
|
-
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
222
|
-
process.stdout.write(event.assistantMessageEvent.delta);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
135
|
+
For least-privilege sessions, start from an explicit allowlist:
|
|
225
136
|
|
|
226
|
-
|
|
227
|
-
|
|
137
|
+
```bash
|
|
138
|
+
tallow --extensions-only --extension tasks --extension lsp
|
|
139
|
+
tallow --extensions-only --extension read-tool-enhanced --extension write-tool-enhanced
|
|
228
140
|
```
|
|
229
141
|
|
|
230
|
-
|
|
142
|
+
Repeat `--extension <selector>` to add only what the task needs.
|
|
231
143
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
cwd: "/path/to/project",
|
|
235
|
-
provider: "anthropic",
|
|
236
|
-
modelId: "claude-sonnet-4-20250514",
|
|
237
|
-
thinkingLevel: "high",
|
|
238
|
-
session: { type: "memory" }, // Don't persist
|
|
239
|
-
noBundledExtensions: true, // Start clean
|
|
240
|
-
additionalExtensions: ["./my-ext"], // Add your own
|
|
241
|
-
systemPrompt: "You are a test bot.", // Override system prompt
|
|
242
|
-
apiKey: process.env.ANTHROPIC_API_KEY, // Runtime only, not persisted
|
|
243
|
-
});
|
|
244
|
-
```
|
|
144
|
+
See the [full CLI reference](https://tallow.dungle-scrubs.com) for all flags
|
|
145
|
+
and modes (RPC, JSON, piped stdin, shell interpolation, etc.)
|
|
245
146
|
|
|
246
147
|
## Configuration
|
|
247
148
|
|
|
@@ -249,47 +150,38 @@ Tallow stores its configuration in `~/.tallow/`:
|
|
|
249
150
|
|
|
250
151
|
| Path | Purpose |
|
|
251
152
|
|------|---------|
|
|
252
|
-
|
|
|
253
|
-
|
|
|
254
|
-
|
|
|
255
|
-
|
|
|
256
|
-
|
|
|
257
|
-
|
|
|
258
|
-
|
|
|
259
|
-
|
|
|
260
|
-
|
|
153
|
+
| `settings.json` | Global settings (theme, icons, keybindings) |
|
|
154
|
+
| `.env` | Environment variables loaded at startup (supports `op://` refs) |
|
|
155
|
+
| `auth.json` | Provider auth references (see [SECURITY.md](SECURITY.md)) |
|
|
156
|
+
| `models.json` | Model configuration |
|
|
157
|
+
| `agents/` | Agent profiles — yours to edit |
|
|
158
|
+
| `commands/` | Slash commands — yours to edit |
|
|
159
|
+
| `extensions/` | User extensions (override bundled ones by name) |
|
|
160
|
+
| `sessions/` | Persisted conversation sessions |
|
|
161
|
+
|
|
162
|
+
Project-level overrides live in `.tallow/` within your repo.
|
|
261
163
|
|
|
262
|
-
|
|
164
|
+
## Extending Tallow
|
|
263
165
|
|
|
264
|
-
|
|
166
|
+
### Themes
|
|
265
167
|
|
|
266
|
-
|
|
168
|
+
Switch themes in-session with `/theme`, or set a default:
|
|
267
169
|
|
|
268
170
|
```json
|
|
269
|
-
{
|
|
270
|
-
"icons": {
|
|
271
|
-
"success": "✔",
|
|
272
|
-
"error": "✘",
|
|
273
|
-
"spinner": ["⠋", "⠙", "⠹", "⠸"]
|
|
274
|
-
}
|
|
275
|
-
}
|
|
171
|
+
{ "theme": "tokyo-night" }
|
|
276
172
|
```
|
|
277
173
|
|
|
278
|
-
|
|
279
|
-
See the [icon reference](https://tallow.dungle-scrubs.com/getting-started/icons/) for all available keys.
|
|
280
|
-
|
|
281
|
-
## Themes
|
|
174
|
+
### Icons
|
|
282
175
|
|
|
283
|
-
|
|
284
|
-
or set one in `~/.tallow/settings.json`:
|
|
176
|
+
Override any TUI glyph in `settings.json` — only the keys you set change:
|
|
285
177
|
|
|
286
178
|
```json
|
|
287
|
-
{
|
|
288
|
-
"theme": "tokyo-night"
|
|
289
|
-
}
|
|
179
|
+
{ "icons": { "success": "✔", "error": "✘" } }
|
|
290
180
|
```
|
|
291
181
|
|
|
292
|
-
|
|
182
|
+
See the [icon reference](https://tallow.dungle-scrubs.com/getting-started/icons/) for all keys.
|
|
183
|
+
|
|
184
|
+
### Writing extensions
|
|
293
185
|
|
|
294
186
|
Extensions are TypeScript files that receive the pi `ExtensionAPI`:
|
|
295
187
|
|
|
@@ -306,20 +198,46 @@ export default function myExtension(api: ExtensionAPI): void {
|
|
|
306
198
|
}
|
|
307
199
|
```
|
|
308
200
|
|
|
309
|
-
Place
|
|
310
|
-
|
|
201
|
+
Place it in `~/.tallow/extensions/my-extension/index.ts`. If it shares a name
|
|
202
|
+
with a bundled extension, yours takes precedence.
|
|
203
|
+
|
|
204
|
+
### SDK
|
|
205
|
+
|
|
206
|
+
Embed Tallow in your own scripts:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { createTallowSession } from "tallow";
|
|
210
|
+
|
|
211
|
+
const { session } = await createTallowSession({
|
|
212
|
+
provider: "anthropic",
|
|
213
|
+
modelId: "claude-sonnet-4-20250514",
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
session.subscribe((event) => {
|
|
217
|
+
if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
|
|
218
|
+
process.stdout.write(event.assistantMessageEvent.delta);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await session.prompt("What files are in this directory?");
|
|
223
|
+
session.dispose();
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
See the [SDK docs](https://tallow.dungle-scrubs.com) for all options.
|
|
311
227
|
|
|
312
228
|
## Known limitations
|
|
313
229
|
|
|
314
230
|
- Requires Node.js 22+ (uses modern ESM features)
|
|
315
231
|
- Session persistence is local — no cloud sync
|
|
316
|
-
-
|
|
317
|
-
for JS-heavy pages
|
|
232
|
+
- `web_fetch` works best with a [Firecrawl](https://firecrawl.dev) API key for JS-heavy pages
|
|
318
233
|
|
|
319
234
|
## Contributing
|
|
320
235
|
|
|
321
236
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
|
|
322
237
|
|
|
238
|
+
This is a personal project I build in my spare time — please be patient with
|
|
239
|
+
issue and PR response times.
|
|
240
|
+
|
|
323
241
|
## License
|
|
324
242
|
|
|
325
243
|
[MIT](LICENSE) © Kevin Frilot
|
package/dist/auth-hardening.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
/** Result of the plaintext migration run. */
|
|
3
3
|
export interface MigrationResult {
|
|
4
4
|
/** Providers migrated from plaintext/op:// to secure references. */
|
|
@@ -22,30 +22,24 @@ export interface ApiKeySecretStore {
|
|
|
22
22
|
export interface SecureAuthStorageOptions {
|
|
23
23
|
readonly secretStore?: ApiKeySecretStore;
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
/** Return value from {@link createSecureAuthStorage}. */
|
|
26
|
+
export interface SecureAuthStorageResult {
|
|
27
|
+
/** AuthStorage instance with secure persistence. */
|
|
28
|
+
readonly authStorage: AuthStorage;
|
|
29
|
+
/** Providers migrated from plaintext to secure references on creation. */
|
|
30
30
|
readonly migration: MigrationResult;
|
|
31
|
-
private readonly authPathValue;
|
|
32
|
-
private readonly secretStore;
|
|
33
|
-
/**
|
|
34
|
-
* Create a secure auth storage instance and run one-time migration.
|
|
35
|
-
*
|
|
36
|
-
* @param authPath - Absolute auth.json path
|
|
37
|
-
* @param options - Optional testing dependencies
|
|
38
|
-
*/
|
|
39
|
-
constructor(authPath: string, options?: SecureAuthStorageOptions);
|
|
40
|
-
/**
|
|
41
|
-
* Persist a provider credential.
|
|
42
|
-
* API keys are converted to secure references before writing to disk.
|
|
43
|
-
*
|
|
44
|
-
* @param provider - Provider ID
|
|
45
|
-
* @param credential - Credential payload
|
|
46
|
-
*/
|
|
47
|
-
set(provider: string, credential: AuthCredential): void;
|
|
48
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Create an AuthStorage instance with secure persistence.
|
|
34
|
+
* Raw API keys are normalized to references at the storage-backend layer,
|
|
35
|
+
* ensuring they never reach disk in plaintext. Runs one-time migration
|
|
36
|
+
* for any existing plaintext keys.
|
|
37
|
+
*
|
|
38
|
+
* @param authPath - Absolute auth.json path
|
|
39
|
+
* @param options - Optional testing dependencies
|
|
40
|
+
* @returns AuthStorage instance and migration result
|
|
41
|
+
*/
|
|
42
|
+
export declare function createSecureAuthStorage(authPath: string, options?: SecureAuthStorageOptions): SecureAuthStorageResult;
|
|
49
43
|
/**
|
|
50
44
|
* Fail when auth.json permissions are insecure.
|
|
51
45
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-hardening.d.ts","sourceRoot":"","sources":["../src/auth-hardening.ts"],"names":[],"mappings":"AAIA,OAAO,
|
|
1
|
+
{"version":3,"file":"auth-hardening.d.ts","sourceRoot":"","sources":["../src/auth-hardening.ts"],"names":[],"mappings":"AAIA,OAAO,EAGN,WAAW,EAGX,MAAM,+BAA+B,CAAC;AASvC,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,QAAQ,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAED,0DAA0D;AAC1D,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,WAAW,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAChD;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;CACzC;AAED,yDAAyD;AACzD,MAAM,WAAW,uBAAuB;IACvC,oDAAoD;IACpD,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;CACpC;AA+ED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,wBAA6B,GACpC,uBAAuB,CAUzB;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAUtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,iBAA6C,GACxD,gBAAgB,CAMlB;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,MAAM,EAChB,WAAW,GAAE,iBAA6C,GACxD,eAAe,CA0BjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,GAAG,SAAS,CAUhE"}
|
package/dist/auth-hardening.js
CHANGED
|
@@ -2,7 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, statSync } from "node:fs";
|
|
3
3
|
import { platform } from "node:os";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
|
-
import { AuthStorage, } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { AuthStorage, FileAuthStorageBackend, } from "@mariozechner/pi-coding-agent";
|
|
6
6
|
import { atomicWriteFileSync, restoreFromBackup } from "./atomic-write.js";
|
|
7
7
|
const AUTH_FILE_MODE = 0o600;
|
|
8
8
|
const AUTH_DIRECTORY_MODE = 0o700;
|
|
@@ -10,50 +10,95 @@ const KEYCHAIN_ACCOUNT = "tallow";
|
|
|
10
10
|
const KEYCHAIN_SERVICE_PREFIX = "tallow.api-key";
|
|
11
11
|
const ENV_REFERENCE_PATTERN = /^[A-Z][A-Z0-9]*_[A-Z0-9_]*$/;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* AuthStorageBackend that intercepts writes to normalize API key credentials.
|
|
14
|
+
* Wraps FileAuthStorageBackend — raw keys are converted to secure references
|
|
15
|
+
* (keychain/opchain/env/shell) before they reach disk.
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
authPathValue;
|
|
17
|
+
class SecureFileAuthStorageBackend {
|
|
18
|
+
inner;
|
|
19
19
|
secretStore;
|
|
20
20
|
/**
|
|
21
|
-
* Create a secure auth storage instance and run one-time migration.
|
|
22
|
-
*
|
|
23
21
|
* @param authPath - Absolute auth.json path
|
|
24
|
-
* @param
|
|
22
|
+
* @param secretStore - Backend for raw key storage
|
|
25
23
|
*/
|
|
26
|
-
constructor(authPath,
|
|
27
|
-
|
|
28
|
-
this.
|
|
29
|
-
this.secretStore = options.secretStore ?? createApiKeySecretStore();
|
|
30
|
-
assertSecureAuthFilePermissions(authPath);
|
|
31
|
-
this.migration = migratePlaintextApiKeys(authPath, this.secretStore);
|
|
32
|
-
if (this.migration.migratedProviders.length > 0) {
|
|
33
|
-
super.reload();
|
|
34
|
-
}
|
|
24
|
+
constructor(authPath, secretStore) {
|
|
25
|
+
this.inner = new FileAuthStorageBackend(authPath);
|
|
26
|
+
this.secretStore = secretStore;
|
|
35
27
|
}
|
|
36
28
|
/**
|
|
37
|
-
*
|
|
38
|
-
* API keys are converted to secure references before writing to disk.
|
|
29
|
+
* Execute fn under lock, normalizing any API keys in the write payload.
|
|
39
30
|
*
|
|
40
|
-
* @param
|
|
41
|
-
* @
|
|
31
|
+
* @param fn - Lock callback receiving current content
|
|
32
|
+
* @returns Result from fn
|
|
33
|
+
*/
|
|
34
|
+
withLock(fn) {
|
|
35
|
+
return this.inner.withLock((current) => {
|
|
36
|
+
const lockResult = fn(current);
|
|
37
|
+
if (lockResult.next !== undefined) {
|
|
38
|
+
lockResult.next = this.normalizeStorageContent(lockResult.next);
|
|
39
|
+
}
|
|
40
|
+
return lockResult;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Async variant of withLock.
|
|
45
|
+
*
|
|
46
|
+
* @param fn - Async lock callback receiving current content
|
|
47
|
+
* @returns Result from fn
|
|
48
|
+
*/
|
|
49
|
+
withLockAsync(fn) {
|
|
50
|
+
return this.inner.withLockAsync(async (current) => {
|
|
51
|
+
const lockResult = await fn(current);
|
|
52
|
+
if (lockResult.next !== undefined) {
|
|
53
|
+
lockResult.next = this.normalizeStorageContent(lockResult.next);
|
|
54
|
+
}
|
|
55
|
+
return lockResult;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse JSON content and normalize any raw API keys to secure references.
|
|
60
|
+
*
|
|
61
|
+
* @param jsonContent - Serialized auth data
|
|
62
|
+
* @returns Normalized JSON content
|
|
42
63
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
normalizeStorageContent(jsonContent) {
|
|
65
|
+
try {
|
|
66
|
+
const data = JSON.parse(jsonContent);
|
|
67
|
+
let changed = false;
|
|
68
|
+
for (const [provider, credential] of Object.entries(data)) {
|
|
69
|
+
if (credential?.type !== "api_key" || typeof credential.key !== "string")
|
|
70
|
+
continue;
|
|
71
|
+
const normalized = normalizeApiKeyValue(provider, credential.key, this.secretStore);
|
|
72
|
+
if (normalized !== credential.key) {
|
|
73
|
+
data[provider].key = normalized;
|
|
74
|
+
changed = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return changed ? JSON.stringify(data, null, 2) : jsonContent;
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return jsonContent;
|
|
48
81
|
}
|
|
49
|
-
const secureCredential = {
|
|
50
|
-
type: "api_key",
|
|
51
|
-
key: normalizeApiKeyValue(provider, credential.key, this.secretStore),
|
|
52
|
-
};
|
|
53
|
-
super.set(provider, secureCredential);
|
|
54
|
-
assertSecureAuthFilePermissions(this.authPathValue);
|
|
55
82
|
}
|
|
56
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Create an AuthStorage instance with secure persistence.
|
|
86
|
+
* Raw API keys are normalized to references at the storage-backend layer,
|
|
87
|
+
* ensuring they never reach disk in plaintext. Runs one-time migration
|
|
88
|
+
* for any existing plaintext keys.
|
|
89
|
+
*
|
|
90
|
+
* @param authPath - Absolute auth.json path
|
|
91
|
+
* @param options - Optional testing dependencies
|
|
92
|
+
* @returns AuthStorage instance and migration result
|
|
93
|
+
*/
|
|
94
|
+
export function createSecureAuthStorage(authPath, options = {}) {
|
|
95
|
+
const secretStore = options.secretStore ?? createApiKeySecretStore();
|
|
96
|
+
assertSecureAuthFilePermissions(authPath);
|
|
97
|
+
const migration = migratePlaintextApiKeys(authPath, secretStore);
|
|
98
|
+
const backend = new SecureFileAuthStorageBackend(authPath, secretStore);
|
|
99
|
+
const authStorage = AuthStorage.fromStorage(backend);
|
|
100
|
+
return { authStorage, migration };
|
|
101
|
+
}
|
|
57
102
|
/**
|
|
58
103
|
* Fail when auth.json permissions are insecure.
|
|
59
104
|
*
|