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.
Files changed (4) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +247 -0
  3. package/dist/index.js +1805 -0
  4. 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
+ ```