audacity-cli 0.1.0
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/LICENSE +18 -0
- package/README.md +247 -0
- package/dist/index.js +1805 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Audacity Investments CLI License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Audacity Investments. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software is proprietary to Audacity Investments. Permission is granted
|
|
6
|
+
to download, install, and use this software solely to access services
|
|
7
|
+
provided by Audacity Investments, subject to your agreement with Audacity
|
|
8
|
+
Investments.
|
|
9
|
+
|
|
10
|
+
You may not modify, distribute, sublicense, or create derivative works of
|
|
11
|
+
this software, in whole or in part, except as expressly permitted in
|
|
12
|
+
writing by Audacity Investments.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
17
|
+
AUDACITY INVESTMENTS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY
|
|
18
|
+
ARISING FROM THE USE OF THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# audacity
|
|
2
|
+
|
|
3
|
+
A local, Claude-Code-style CLI coding agent whose "brain" is **your own Audacity
|
|
4
|
+
infrastructure** — never an external Claude/OpenAI endpoint. It talks only to the
|
|
5
|
+
Audacity web host's OpenAI-compatible chat endpoints, authenticated with a personal
|
|
6
|
+
`audacity_api_…` key.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
audacity # interactive REPL
|
|
10
|
+
audacity "fix the failing test" # run one prompt to completion, then exit
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## How it works
|
|
14
|
+
|
|
15
|
+
- **Phase 1 — `/rag false` (default):** a full coding agent. Each turn streams from
|
|
16
|
+
`POST {AUDACITY_API_URL}/v1/chat/completions`, and the model can call local tools
|
|
17
|
+
(`read_file`, `list_files`, `grep`, `edit_file`, `write_file`, `run_bash`). The
|
|
18
|
+
conversation is held in memory and resent on every call (the endpoint is stateless).
|
|
19
|
+
- **Phase 2 — `/rag true` (coming soon):** routes to `POST {AUDACITY_API_URL}/v1/agent/chat/completions`,
|
|
20
|
+
the Mastra RAG agent (server-side memory + company/fund/doc tools). Same key, same
|
|
21
|
+
wire format. The `/rag true` slash command already toggles session state; sending in
|
|
22
|
+
RAG mode currently prints a "Phase 2" notice.
|
|
23
|
+
|
|
24
|
+
All transport/auth lives in `src/model.ts` (the only integration seam). The public web
|
|
25
|
+
host handles the litellm-proxy JWT and Cloud Run IAM downstream — the CLI only ever
|
|
26
|
+
sends your `audacity_api_…` key. No `gcloud`, no service token.
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bun install
|
|
32
|
+
bun run build # → dist/index.js (executable, with shebang)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Run it:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bun run dev # dev (bun --watch src/index.ts)
|
|
39
|
+
node dist/index.js # after build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Install globally (recommended)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
cd apps/audacity
|
|
46
|
+
npm install -g . # builds dist/ via the prepare hook, then links `audacity` onto your PATH
|
|
47
|
+
audacity # now works from any directory
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`npm install -g .` runs the `prepare` script (`tsup`), so the self-contained
|
|
51
|
+
`dist/index.js` is built at install time — no separate build step needed. (`bun link`
|
|
52
|
+
or `npm link` also work for local development.)
|
|
53
|
+
|
|
54
|
+
On first run, `audacity` prompts for your **production** key and a model, then saves
|
|
55
|
+
them to `~/.audacity/config.json` — so it's remembered across every session, shell,
|
|
56
|
+
and terminal on the machine. You only enter the key once.
|
|
57
|
+
|
|
58
|
+
To unregister the local global install later:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm uninstall -g audacity-cli # removes the `audacity` command from your PATH
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Publishing to npm
|
|
65
|
+
|
|
66
|
+
The package is **self-contained** — no `workspace:*` deps, only `commander` at runtime —
|
|
67
|
+
so it publishes straight from `apps/audacity` with no monorepo build orchestration.
|
|
68
|
+
|
|
69
|
+
> **You don't need to build first.** The `prepare` lifecycle script (`tsup && chmod +x`)
|
|
70
|
+
> runs automatically on `npm publish`, so the shipped `dist/index.js` is always freshly
|
|
71
|
+
> built from the current `src/`.
|
|
72
|
+
|
|
73
|
+
**One-time setup:**
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm login # authenticate to the npm registry
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Publish:**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
cd apps/audacity
|
|
83
|
+
npm publish --dry-run # inspect the tarball WITHOUT publishing — do this first
|
|
84
|
+
npm publish # ships audacity-cli@<version> to the public registry
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The dry run prints exactly what ships. Expect only three files —
|
|
88
|
+
`dist/index.js`, `package.json`, `README.md` (controlled by the `files: ["dist"]`
|
|
89
|
+
whitelist; `README.md` is always included automatically). No `src/`, no tests.
|
|
90
|
+
|
|
91
|
+
After publishing, anyone can install it:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install -g audacity-cli
|
|
95
|
+
audacity
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> **Name note:** the package name is `audacity-cli` (the bare `audacity` name is taken
|
|
99
|
+
> on npm). If you ever want to namespace it under the org instead, rename to a scoped
|
|
100
|
+
> name like `@audacity-investments/cli` in `package.json` and publish with
|
|
101
|
+
> `npm publish --access public` (scoped packages default to private otherwise).
|
|
102
|
+
|
|
103
|
+
## Updating the published package
|
|
104
|
+
|
|
105
|
+
You can **never** republish the same version number, and unpublishing is restricted
|
|
106
|
+
(only within 72h). The fix for any bad release is always to **roll forward** — publish a
|
|
107
|
+
higher version. Never delete; just bump.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cd apps/audacity
|
|
111
|
+
npm version patch # 0.1.0 → 0.1.1 (bug fixes)
|
|
112
|
+
# npm version minor # 0.1.1 → 0.2.0 (new features, backwards-compatible)
|
|
113
|
+
# npm version major # 0.2.0 → 1.0.0 (breaking changes)
|
|
114
|
+
npm publish
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`npm version` bumps `package.json` and creates a git commit + tag. Then `npm publish`
|
|
118
|
+
rebuilds via `prepare` and ships the new version. Users upgrade with:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
npm install -g audacity-cli@latest
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
While the version stays under `1.0.0`, semver convention treats every release as
|
|
125
|
+
potentially breaking — fine for early development. Cut `1.0.0` once the wire format and
|
|
126
|
+
CLI surface are stable.
|
|
127
|
+
|
|
128
|
+
## Configuration
|
|
129
|
+
|
|
130
|
+
`audacity` is **self-contained**: the only source of configuration is
|
|
131
|
+
`~/.audacity/config.json`. It depends on no environment variables and nothing else on
|
|
132
|
+
the machine — a key entered once is remembered globally on that device, across every
|
|
133
|
+
shell, directory, and session.
|
|
134
|
+
|
|
135
|
+
On first interactive run, `audacity` prompts for your production key (pasted, not
|
|
136
|
+
echoed) and a model, defaults the base URL to **production**
|
|
137
|
+
(`https://portal.audacityinvestments.com`), and **auto-saves** to
|
|
138
|
+
`~/.audacity/config.json` (`chmod 600`) — no "save?" prompt. Requests go to the public
|
|
139
|
+
prod host, which routes them through the production litellm-proxy downstream. Run `/key`
|
|
140
|
+
anytime to change the saved key. One-shot mode (`audacity "…"`) never prompts — it reads
|
|
141
|
+
the saved config and errors clearly if no key is saved.
|
|
142
|
+
|
|
143
|
+
Optional field: **`compactModel`** — the model `/compact` uses to summarize. Summarizing
|
|
144
|
+
is an easy task, so it defaults to a cheap, fast model (`gemini-2.5-flash`) independent of
|
|
145
|
+
your chat model; set `compactModel` to override it.
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"apiUrl": "https://portal.audacityinvestments.com",
|
|
150
|
+
"apiKey": "audacity_api_…",
|
|
151
|
+
"model": "claude-opus-4-8",
|
|
152
|
+
"compactModel": "gemini-2.5-flash"
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## REPL commands
|
|
157
|
+
|
|
158
|
+
| Command | Action |
|
|
159
|
+
|---------|--------|
|
|
160
|
+
| `/model [id]` | Pick a model (no arg → numbered picker; sourced from `apps/litellm/litellm_config.yaml`) |
|
|
161
|
+
| `/rag true\|false` | Toggle RAG mode for the session (Phase 2 for `true`) |
|
|
162
|
+
| `/auto` | Toggle auto-approve for mutating tools |
|
|
163
|
+
| `/key` | Re-enter and save your API key |
|
|
164
|
+
| `/cwd` | Show the working directory |
|
|
165
|
+
| `/compact` | Summarize older turns into one message, keep the last 2 verbatim (shrinks context) |
|
|
166
|
+
| `/clear` | Reset the conversation |
|
|
167
|
+
| `/exit` | Quit |
|
|
168
|
+
|
|
169
|
+
## Permissions
|
|
170
|
+
|
|
171
|
+
`read_file`, `list_files`, `grep` run without prompting. `edit_file`, `write_file`,
|
|
172
|
+
`run_bash` ask for `y/N` approval first (showing exactly what will run) — bypass with
|
|
173
|
+
the `--yes`/`-y` flag or the `/auto` toggle. In one-shot mode, mutating tools require
|
|
174
|
+
`-y`.
|
|
175
|
+
|
|
176
|
+
## Testing prompt caching
|
|
177
|
+
|
|
178
|
+
The harness is a realistic way to exercise **OpenAI prompt caching**: it resends
|
|
179
|
+
the whole conversation on every turn (the endpoint is stateless), so a large,
|
|
180
|
+
stable prefix repeats call after call — exactly what the provider cache rewards.
|
|
181
|
+
Cheaper cached input tokens are captured server-side as
|
|
182
|
+
`litellm_usage_log.cached_input_tokens` (see `apps/litellm-proxy/README.md` for
|
|
183
|
+
the full metering/verification story).
|
|
184
|
+
|
|
185
|
+
### Prerequisites
|
|
186
|
+
|
|
187
|
+
1. **Point at an environment where the capture is deployed.** Prompt-cache
|
|
188
|
+
metering lives on the **UAT** proxy first. Set `~/.audacity/config.json`:
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"apiUrl": "https://web-uat-qpe5npob6a-uk.a.run.app",
|
|
192
|
+
"apiKey": "audacity_api_…your UAT key…",
|
|
193
|
+
"model": "gpt-5.5"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
2. **Use an OpenAI model** (`gpt-5.5`, `gpt-5.4`, …). OpenAI caches
|
|
197
|
+
automatically; other providers need explicit cache markers that aren't wired
|
|
198
|
+
yet.
|
|
199
|
+
|
|
200
|
+
### ⚠️ Cache population is asynchronous
|
|
201
|
+
|
|
202
|
+
The cache isn't guaranteed warm on the very next turn — an early turn can still
|
|
203
|
+
report `cached_tokens = 0`, and hits land reliably only after the prefix has been
|
|
204
|
+
seen a few times. Run **several turns in one session** and don't `/clear`.
|
|
205
|
+
|
|
206
|
+
### Good sessions to trigger hits
|
|
207
|
+
|
|
208
|
+
Load a large file early (so the prefix crosses OpenAI's ~1024-token floor), then
|
|
209
|
+
ask follow-ups that reuse that context:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
read apps/litellm-proxy/src/routes/api/chat/completions/index.ts
|
|
213
|
+
walk me through the streaming success path
|
|
214
|
+
where exactly is user cost computed vs fmv cost?
|
|
215
|
+
now explain the non-streaming path and how it differs
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
or a multi-file exploration:
|
|
219
|
+
|
|
220
|
+
```
|
|
221
|
+
read src/agent.ts, src/model.ts, and src/tools.ts
|
|
222
|
+
explain how a tool call flows from the model back into the loop
|
|
223
|
+
which tools require approval and where is that enforced?
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Bad** cache tests (expect `no cache`): short one-liners with no file context,
|
|
227
|
+
`/clear` between questions, or jumping topics each turn.
|
|
228
|
+
|
|
229
|
+
### Observe the hits
|
|
230
|
+
|
|
231
|
+
The harness streams and doesn't print usage, so watch the proxy logs
|
|
232
|
+
(the `stream` variant fires for harness traffic):
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
gcloud logging read 'resource.type="cloud_run_revision" AND resource.labels.service_name="litellm-proxy-uat" AND textPayload:"[DEV:prompt-cache]"' \
|
|
236
|
+
--project=audacity-uat --limit=10 --freshness=10m --format='value(textPayload)'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Expect `cached_input_tokens` to climb turn over turn, e.g.
|
|
240
|
+
`[DEV:prompt-cache] stream model=gpt-5.5 prompt_tokens=4450 cached_input_tokens=4224 CACHE HIT`.
|
|
241
|
+
|
|
242
|
+
## Test
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
bun test # tool-layer unit tests (no API key needed)
|
|
246
|
+
bun run typecheck
|
|
247
|
+
```
|