What is this
Fonn is a local command line tool and a reusable TypeScript core. It wraps the CLIs you already use. Running fonn opens the unmodified Claude Code or Codex interface, with its own login, settings, permissions and questions.
On top of that launch, Fonn adds three things:
- Saved profiles. A provider, model and effort level stored under a short name, so
fonn --profile focusedalways starts the same way. - Subagent guidance. Startup instructions that ask the agent to hand meaningful independent work to its built-in subagents, while simple tasks stay with the parent.
- Output filtering. Recognized noisy shell output, like test progress rows, is shortened before the model reads it. The full original is archived locally and can be read back in bounded slices.
Separately, fonn run starts a structured task through the Claude Agent SDK, with validated results, budgets and estimated usage accounting. That path uses an API key and API billing. See permissions and billing.
How it works
Section titled “How it works”-
Launch. Fonn starts the native CLI with your profile’s model and effort, plus per-launch hook settings. Your global Claude or Codex settings are not changed.
-
Delegate. With
--subagents auto, startup guidance suggests at most two workers, no nested delegation and read-only exploration. These are instructions. Your policies still decide. -
Filter. Each new Bash observation passes through a post-tool hook, including observations from native workers. Only recognized formats and repeated lines are reduced. Reasoning, history, instructions and source files are never rewritten.
-
Recover. A reduced observation ends with a footer naming the archived original. The agent, or you, can read it back 4,000 characters at a time, up to 20,000 per request.
See it on real samples
Section titled “See it on real samples”$ vendor/bin/phpunit
Original observation 3,025 chars
PHPUnit 11.5.2 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.1
Configuration: /app/phpunit.xml
............................................................... 63 / 2016 ( 3%)
............................................................... 126 / 2016 ( 6%)
............................................................... 189 / 2016 ( 9%)
............................................................... 252 / 2016 ( 12%)
............................................................... 315 / 2016 ( 15%)
............................................................... 378 / 2016 ( 18%)
............................................................... 441 / 2016 ( 21%)
............................................................... 504 / 2016 ( 25%)
............................................................... 567 / 2016 ( 28%)
............................................................... 630 / 2016 ( 31%)
............................................................... 693 / 2016 ( 34%)
............................................................... 756 / 2016 ( 37%)
............................................................... 819 / 2016 ( 40%)
............................................................... 882 / 2016 ( 43%)
............................................................... 945 / 2016 ( 46%)
............................................................... 1008 / 2016 ( 50%)
............................................................... 1071 / 2016 ( 53%)
............................................................... 1134 / 2016 ( 56%)
............................................................... 1197 / 2016 ( 59%)
............................................................... 1260 / 2016 ( 62%)
............................................................... 1323 / 2016 ( 65%)
............................................................... 1386 / 2016 ( 68%)
............................................................... 1449 / 2016 ( 71%)
............................................................... 1512 / 2016 ( 75%)
............................................................... 1575 / 2016 ( 78%)
............................................................... 1638 / 2016 ( 81%)
............................................................... 1701 / 2016 ( 84%)
............................................................... 1764 / 2016 ( 87%)
............................................................... 1827 / 2016 ( 90%)
............................................................... 1890 / 2016 ( 93%)
............................................................... 1953 / 2016 ( 96%)
........................................F...................... 2016 / 2016 (100%)
Time: 00:06.204, Memory: 52.00 MB
There was 1 failure:
1) Tests\Feature\InvoiceTest::test_zero_limit_is_preserved
Failed asserting that 20 is identical to 0.
/app/tests/Feature/InvoiceTest.php:42
FAILURES!
Tests: 2016, Assertions: 6048, Failures: 1.What the model receives
PHPUnit 11.5.2 by Sebastian Bergmann and contributors.
Runtime: PHP 8.4.1
Configuration: /app/phpunit.xml
........................................F...................... 2016 / 2016 (100%)
Time: 00:06.204, Memory: 52.00 MB
There was 1 failure:
1) Tests\Feature\InvoiceTest::test_zero_limit_is_preserved
Failed asserting that 20 is identical to 0.
/app/tests/Feature/InvoiceTest.php:42
FAILURES!
Tests: 2016, Assertions: 6048, Failures: 1.
[fonn: full original tool response archived as 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f; recover with Recover the original tool response with: fonn native-output <launch-id> 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f.]3,025 characters in, 662 delivered, 31 lines removed. That is 78% fewer characters for this sample only.
$ npm run dev 2>&1 | head -n 200
Original observation 3,318 chars
> app@1.4.0 dev
> node server.js
Starting server on port 3000
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Waiting for database connection on localhost:5432...
Database connected after 61 attempts
Server ready at http://localhost:3000What the model receives
> app@1.4.0 dev
> node server.js
Starting server on port 3000
Waiting for database connection on localhost:5432...
[fonn: identical line occurred 60 times; 59 duplicate copies archived]
Database connected after 61 attempts
Server ready at http://localhost:3000
[fonn: full original tool response archived as 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f; recover with Recover the original tool response with: fonn native-output <launch-id> 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f.]3,318 characters in, 472 delivered, 59 lines removed. That is 86% fewer characters for this sample only.
$ node scripts/worker.js
Original observation
BEGIN_TASK_PAYLOAD
{"task":"preserve zero values","code":"return input.limit || 20;"}
END_TASK_PAYLOAD
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
OBSERVATION_COMPLETE exit=02,339 characters in, the same delivered. Nothing changes at the standard level.
$ node scripts/worker.js--filter-level similar
Original observation 2,339 chars
BEGIN_TASK_PAYLOAD
{"task":"preserve zero values","code":"return input.limit || 20;"}
END_TASK_PAYLOAD
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
PROGRESS worker=0 status=healthy heartbeat=ok
PROGRESS worker=1 status=healthy heartbeat=ok
PROGRESS worker=2 status=healthy heartbeat=ok
PROGRESS worker=3 status=healthy heartbeat=ok
OBSERVATION_COMPLETE exit=0What the model receives
BEGIN_TASK_PAYLOAD
{"task":"preserve zero values","code":"return input.limit || 20;"}
END_TASK_PAYLOAD
PROGRESS worker=0 status=healthy heartbeat=ok
[fonn: similar lines omitted; 46 lines between these differ only in numbers or IDs; originals archived]
PROGRESS worker=3 status=healthy heartbeat=ok
OBSERVATION_COMPLETE exit=0
[fonn: full original tool response archived as 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f; recover with Recover the original tool response with: fonn native-output <launch-id> 3f6c1d2e-8a4b-4c1d-9e2f-5a6b7c8d9e0f.]2,339 characters in, 537 delivered, 46 lines removed. That is 77% fewer characters for this sample only.