troxy-cli 1.30.21 → 1.30.22
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/package.json +1 -1
- package/src/init.js +3 -3
- package/src/slim.js +23 -4
- package/src/tests/slim.test.js +15 -1
package/package.json
CHANGED
package/src/init.js
CHANGED
|
@@ -325,14 +325,14 @@ async function printHostedAgentSetup(key) {
|
|
|
325
325
|
// thing as a bare ~/AGENTS.md, which (per our own testing) a project working
|
|
326
326
|
// directory does not walk up to. This is the one path that actually applies
|
|
327
327
|
// everywhere, so it's the file the "all projects" option below writes to.
|
|
328
|
-
function globalClaudeMdPath() {
|
|
328
|
+
export function globalClaudeMdPath() {
|
|
329
329
|
return path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
// Same merge algorithm as writeAgentsFile (marker-delimited, never clobbers
|
|
333
333
|
// content outside its own span), generalized to any file path so both the
|
|
334
334
|
// per-project AGENTS.md and the global CLAUDE.md can share it.
|
|
335
|
-
function writeMarkedBlock(file, text, startMarker, endMarker) {
|
|
335
|
+
export function writeMarkedBlock(file, text, startMarker, endMarker) {
|
|
336
336
|
const block = `${startMarker}\n${text}\n${endMarker}`;
|
|
337
337
|
let existing = '';
|
|
338
338
|
try { existing = fs.readFileSync(file, 'utf8'); } catch {}
|
|
@@ -352,7 +352,7 @@ function writeMarkedBlock(file, text, startMarker, endMarker) {
|
|
|
352
352
|
return file;
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
function removeMarkedBlock(file, startMarker, endMarker) {
|
|
355
|
+
export function removeMarkedBlock(file, startMarker, endMarker) {
|
|
356
356
|
let existing;
|
|
357
357
|
try { existing = fs.readFileSync(file, 'utf8'); } catch { return false; }
|
|
358
358
|
if (!existing.includes(startMarker) || !existing.includes(endMarker)) return false;
|
package/src/slim.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
|
-
import { claudeCodeSettingsPath } from './init.js';
|
|
4
|
+
import { claudeCodeSettingsPath, globalClaudeMdPath, writeMarkedBlock, removeMarkedBlock } from './init.js';
|
|
5
5
|
import { loadConfig, saveConfig } from './config.js';
|
|
6
6
|
import { api } from './api.js';
|
|
7
7
|
|
|
@@ -21,9 +21,9 @@ import { api } from './api.js';
|
|
|
21
21
|
// of the tool in a transcript; tool definitions are in transcripts too, so
|
|
22
22
|
// matching the bare name would find every session.
|
|
23
23
|
export const TOOLS = [
|
|
24
|
-
{ id: 'chrome', label: 'Claude in Chrome (lets Claude control your browser)', tokens: 7200,
|
|
24
|
+
{ id: 'chrome', label: 'Claude in Chrome (lets Claude control your browser)', tokens: 7200, asks: 'to use or control a web browser',
|
|
25
25
|
used: /"type":"tool_use","id":"[^"]+","name":"mcp__claude-in-chrome__/ },
|
|
26
|
-
{ id: 'artifact', label: 'Artifacts (lets Claude publish pages to claude.ai)', tokens: 7500,
|
|
26
|
+
{ id: 'artifact', label: 'Artifacts (lets Claude publish pages to claude.ai)', tokens: 7500, asks: 'for an artifact or a page published to claude.ai',
|
|
27
27
|
used: /"type":"tool_use","id":"[^"]+","name":"Artifact"/ },
|
|
28
28
|
];
|
|
29
29
|
|
|
@@ -57,6 +57,23 @@ export function setToolOff(id, off, file = claudeCodeSettingsPath()) {
|
|
|
57
57
|
throw new Error(`Unknown tool "${id}". Try: ${TOOLS.map(t => t.id).join(', ')}`);
|
|
58
58
|
}
|
|
59
59
|
write(file, s);
|
|
60
|
+
if (file === claudeCodeSettingsPath()) syncOffNote(file);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const NOTE_START = '<!-- troxy:slim:start -->';
|
|
64
|
+
const NOTE_END = '<!-- troxy:slim:end -->';
|
|
65
|
+
|
|
66
|
+
// A turned-off tool is simply gone from Claude's tool list, so nothing errors:
|
|
67
|
+
// Claude would only say it can't do that. This note in the global CLAUDE.md
|
|
68
|
+
// tells Claude why and what to tell the user. About 50 tokens per tool, vs
|
|
69
|
+
// the ~7k the tool itself costs.
|
|
70
|
+
export function syncOffNote(file = claudeCodeSettingsPath(), noteFile = globalClaudeMdPath()) {
|
|
71
|
+
const off = TOOLS.filter(t => toolIsOff(t.id, file));
|
|
72
|
+
if (!off.length) { removeMarkedBlock(noteFile, NOTE_START, NOTE_END); return; }
|
|
73
|
+
writeMarkedBlock(noteFile, '## Turned off by Troxy\n\n' + off.map(t =>
|
|
74
|
+
`- ${t.label.split(' (')[0]} is turned off by Troxy to make every call smaller. If the user asks you ${t.asks}, ` +
|
|
75
|
+
`tell them it is off and that they can turn it back on by running \`troxy slim ${t.id} --undo\` in a terminal and then starting a new Claude Code session.`
|
|
76
|
+
).join('\n'), NOTE_START, NOTE_END);
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
export function printToolsStatus(file = claudeCodeSettingsPath()) {
|
|
@@ -102,7 +119,9 @@ export async function reportSlim(apiKey = loadConfig()?.apiKey, file = claudeCod
|
|
|
102
119
|
// back on with --undo stays on.
|
|
103
120
|
export async function autoSlim({ file = claudeCodeSettingsPath(), projectsDir } = {}) {
|
|
104
121
|
const cfg = loadConfig();
|
|
105
|
-
if (!cfg
|
|
122
|
+
if (!cfg) return;
|
|
123
|
+
if (file === claudeCodeSettingsPath()) syncOffNote(file); // also for tools turned off before the note existed
|
|
124
|
+
if (cfg.slimChecked) return;
|
|
106
125
|
const used = usedTools(projectsDir);
|
|
107
126
|
for (const t of TOOLS) {
|
|
108
127
|
if (used.has(t.id) || toolIsOff(t.id, file)) continue;
|
package/src/tests/slim.test.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
|
-
import { TOOLS, toolIsOff, setToolOff, usedTools } from '../slim.js';
|
|
7
|
+
import { TOOLS, toolIsOff, setToolOff, usedTools, syncOffNote } from '../slim.js';
|
|
8
8
|
|
|
9
9
|
// Turning a tool off and back on must leave the user's own settings.json
|
|
10
10
|
// exactly as it was, including their own env vars and deny rules.
|
|
@@ -53,4 +53,18 @@ describe('troxy slim', () => {
|
|
|
53
53
|
assert.deepEqual([...usedTools(path.join(dir, 'projects'))], ['chrome']);
|
|
54
54
|
assert.deepEqual([...usedTools(path.join(dir, 'missing'))], []);
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
it('keeps a note for Claude listing only the tools that are off, and keeps the user text', () => {
|
|
58
|
+
const note = path.join(dir, 'CLAUDE.md');
|
|
59
|
+
fs.writeFileSync(note, '# mine\n');
|
|
60
|
+
setToolOff('chrome', true, file);
|
|
61
|
+
syncOffNote(file, note);
|
|
62
|
+
let text = fs.readFileSync(note, 'utf8');
|
|
63
|
+
assert.match(text, /^# mine/);
|
|
64
|
+
assert.match(text, /troxy slim chrome --undo/);
|
|
65
|
+
assert.doesNotMatch(text, /troxy slim artifact/);
|
|
66
|
+
setToolOff('chrome', false, file);
|
|
67
|
+
syncOffNote(file, note);
|
|
68
|
+
assert.equal(fs.readFileSync(note, 'utf8').trim(), '# mine');
|
|
69
|
+
});
|
|
56
70
|
});
|