troxy-cli 1.29.17 → 1.29.18
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/bin/troxy.js +22 -9
- package/package.json +1 -1
- package/src/init.js +93 -43
package/bin/troxy.js
CHANGED
|
@@ -127,23 +127,36 @@ switch (command) {
|
|
|
127
127
|
process.exit(1);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
// ── Token-efficiency instructions in AGENTS.md
|
|
130
|
+
// ── Token-efficiency instructions in AGENTS.md / global CLAUDE.md ──────
|
|
131
|
+
// --project scopes enable/disable/status to just this directory's
|
|
132
|
+
// AGENTS.md; default (no flag) is the global ~/.claude/CLAUDE.md, matching
|
|
133
|
+
// what the init/update prompt now recommends by default.
|
|
131
134
|
case 'concise': {
|
|
132
|
-
const {
|
|
135
|
+
const {
|
|
136
|
+
maybeEnableConciseInstructions, removeConciseInstructions, conciseInstructionsConfigured,
|
|
137
|
+
enableGlobalConciseInstructions, removeGlobalConciseInstructions, globalConciseConfigured,
|
|
138
|
+
} = await import('../src/init.js');
|
|
133
139
|
const dir = process.cwd();
|
|
134
|
-
|
|
140
|
+
const projectOnly = Boolean(flags.project);
|
|
141
|
+
|
|
142
|
+
if (sub === 'enable') {
|
|
143
|
+
if (projectOnly) { await maybeEnableConciseInstructions('project', dir); }
|
|
144
|
+
else { console.log(`\n Added token-efficiency instructions to ${enableGlobalConciseInstructions()} (all projects)\n`); }
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
135
147
|
if (sub === 'disable') {
|
|
136
|
-
const removed = removeConciseInstructions(dir);
|
|
137
|
-
console.log(removed ? '\n Removed ✓\n' :
|
|
148
|
+
const removed = projectOnly ? removeConciseInstructions(dir) : removeGlobalConciseInstructions();
|
|
149
|
+
console.log(removed ? '\n Removed ✓\n' : `\n Not enabled ${projectOnly ? 'in this project' : 'globally'}.\n`);
|
|
138
150
|
break;
|
|
139
151
|
}
|
|
140
152
|
if (sub === 'status' || !sub) {
|
|
141
|
-
console.log(
|
|
142
|
-
|
|
143
|
-
|
|
153
|
+
console.log('');
|
|
154
|
+
console.log(` Global (~/.claude/CLAUDE.md): ${globalConciseConfigured() ? 'enabled' : 'not enabled'}`);
|
|
155
|
+
console.log(` This project (./AGENTS.md): ${conciseInstructionsConfigured(dir) ? 'enabled' : 'not enabled'}`);
|
|
156
|
+
console.log('');
|
|
144
157
|
break;
|
|
145
158
|
}
|
|
146
|
-
console.error(` Unknown: troxy concise ${sub}. Try: status, enable, disable\n`);
|
|
159
|
+
console.error(` Unknown: troxy concise ${sub}. Try: status, enable, disable (add --project to scope to this directory)\n`);
|
|
147
160
|
process.exit(1);
|
|
148
161
|
}
|
|
149
162
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "troxy-cli",
|
|
3
|
-
"version": "1.29.
|
|
3
|
+
"version": "1.29.18",
|
|
4
4
|
"description": "A secure control layer for AI agents: policies across payments, messages, logins, destructive actions, model usage, and secrets, all enforceable from the CLI",
|
|
5
5
|
"homepage": "https://troxy.io",
|
|
6
6
|
"bugs": {
|
package/src/init.js
CHANGED
|
@@ -264,13 +264,18 @@ async function printHostedAgentSetup(key) {
|
|
|
264
264
|
console.log(' Then follow it before you pay, email, log in, or delete anything.\n');
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
// Claude Code's real, documented global instructions file - not the same
|
|
268
|
+
// thing as a bare ~/AGENTS.md, which (per our own testing) a project working
|
|
269
|
+
// directory does not walk up to. This is the one path that actually applies
|
|
270
|
+
// everywhere, so it's the file the "all projects" option below writes to.
|
|
271
|
+
function globalClaudeMdPath() {
|
|
272
|
+
return path.join(os.homedir(), '.claude', 'CLAUDE.md');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Same merge algorithm as writeAgentsFile (marker-delimited, never clobbers
|
|
276
|
+
// content outside its own span), generalized to any file path so both the
|
|
277
|
+
// per-project AGENTS.md and the global CLAUDE.md can share it.
|
|
278
|
+
function writeMarkedBlock(file, text, startMarker, endMarker) {
|
|
274
279
|
const block = `${startMarker}\n${text}\n${endMarker}`;
|
|
275
280
|
let existing = '';
|
|
276
281
|
try { existing = fs.readFileSync(file, 'utf8'); } catch {}
|
|
@@ -285,55 +290,94 @@ export function writeAgentsFile(dir, text, startMarker = AGENTS_START, endMarker
|
|
|
285
290
|
} else {
|
|
286
291
|
next = block + '\n';
|
|
287
292
|
}
|
|
293
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
288
294
|
fs.writeFileSync(file, next);
|
|
289
295
|
return file;
|
|
290
296
|
}
|
|
291
297
|
|
|
292
|
-
|
|
298
|
+
function removeMarkedBlock(file, startMarker, endMarker) {
|
|
299
|
+
let existing;
|
|
300
|
+
try { existing = fs.readFileSync(file, 'utf8'); } catch { return false; }
|
|
301
|
+
if (!existing.includes(startMarker) || !existing.includes(endMarker)) return false;
|
|
302
|
+
const head = existing.slice(0, existing.indexOf(startMarker));
|
|
303
|
+
const tail = existing.slice(existing.indexOf(endMarker) + endMarker.length);
|
|
304
|
+
fs.writeFileSync(file, (head + tail).replace(/\n{3,}/g, '\n\n'));
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function hasMarkedBlock(file, startMarker, endMarker) {
|
|
293
309
|
try {
|
|
294
|
-
const text = fs.readFileSync(
|
|
295
|
-
return text.includes(
|
|
310
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
311
|
+
return text.includes(startMarker) && text.includes(endMarker);
|
|
296
312
|
} catch { return false; }
|
|
297
313
|
}
|
|
298
314
|
|
|
315
|
+
// Idempotent, and never clobbers instructions the user wrote themselves: the
|
|
316
|
+
// Troxy section lives between markers, so re-running init (or rotate-key,
|
|
317
|
+
// which changes the key inside the block) replaces only that span. Each
|
|
318
|
+
// marker pair is independent - writing/removing one never touches another's
|
|
319
|
+
// span or anything the user wrote outside all of them.
|
|
320
|
+
export function writeAgentsFile(dir, text, startMarker = AGENTS_START, endMarker = AGENTS_END) {
|
|
321
|
+
return writeMarkedBlock(path.join(dir, 'AGENTS.md'), text, startMarker, endMarker);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function conciseInstructionsConfigured(dir) {
|
|
325
|
+
return hasMarkedBlock(path.join(dir, 'AGENTS.md'), CONCISE_START, CONCISE_END);
|
|
326
|
+
}
|
|
327
|
+
|
|
299
328
|
export function removeConciseInstructions(dir) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const tail = existing.slice(existing.indexOf(CONCISE_END) + CONCISE_END.length);
|
|
306
|
-
fs.writeFileSync(file, (head + tail).replace(/\n{3,}/g, '\n\n'));
|
|
307
|
-
return true;
|
|
329
|
+
return removeMarkedBlock(path.join(dir, 'AGENTS.md'), CONCISE_START, CONCISE_END);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function globalConciseConfigured() {
|
|
333
|
+
return hasMarkedBlock(globalClaudeMdPath(), CONCISE_START, CONCISE_END);
|
|
308
334
|
}
|
|
309
335
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
336
|
+
export function removeGlobalConciseInstructions() {
|
|
337
|
+
return removeMarkedBlock(globalClaudeMdPath(), CONCISE_START, CONCISE_END);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export function enableGlobalConciseInstructions() {
|
|
341
|
+
return writeMarkedBlock(globalClaudeMdPath(), CONCISE_INSTRUCTIONS, CONCISE_START, CONCISE_END);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// conciseOptIn is one of: 'global', 'project', false (explicitly skip), or
|
|
345
|
+
// null (ask interactively). Global first, and recommended by default in the
|
|
346
|
+
// prompt: it's the one location Claude Code always reads regardless of which
|
|
347
|
+
// directory you're in, so it structurally can't land in the wrong place the
|
|
348
|
+
// way the project-scoped option did on its first real run. Never claims a
|
|
349
|
+
// specific percentage in the copy below - the one number we measured for
|
|
350
|
+
// this came from a flawed test (subagents don't reliably load these files
|
|
351
|
+
// the way a real session does) and shouldn't be repeated as fact.
|
|
316
352
|
export async function maybeEnableConciseInstructions(conciseOptIn, dir) {
|
|
317
|
-
if (conciseInstructionsConfigured(dir)) return;
|
|
353
|
+
if (globalConciseConfigured() || conciseInstructionsConfigured(dir)) return;
|
|
318
354
|
|
|
319
|
-
let
|
|
320
|
-
if (conciseOptIn
|
|
321
|
-
|
|
355
|
+
let choice;
|
|
356
|
+
if (conciseOptIn === 'global' || conciseOptIn === 'project' || conciseOptIn === false) {
|
|
357
|
+
choice = conciseOptIn;
|
|
322
358
|
} else if (!process.stdin.isTTY) {
|
|
323
|
-
|
|
359
|
+
choice = false;
|
|
324
360
|
} else {
|
|
325
|
-
console.log('\n Add token-efficiency instructions to
|
|
326
|
-
console.log('
|
|
327
|
-
console.log('
|
|
328
|
-
console.log('
|
|
329
|
-
console.log('
|
|
330
|
-
|
|
331
|
-
|
|
361
|
+
console.log('\n Add token-efficiency instructions to make Claude Code\'s responses');
|
|
362
|
+
console.log(' more concise? Trade-off: less verbose can mean less detail.');
|
|
363
|
+
console.log('');
|
|
364
|
+
console.log(' [G] All projects (recommended) - writes once to ~/.claude/CLAUDE.md');
|
|
365
|
+
console.log(' [P] This project only - writes to ./AGENTS.md');
|
|
366
|
+
console.log(' [N] Skip');
|
|
367
|
+
console.log('');
|
|
368
|
+
console.log(' Remove any time with: troxy concise disable (add --project for just this one)');
|
|
369
|
+
const ans = (await prompt(' Choice (G/p/n): ')).toLowerCase();
|
|
370
|
+
choice = ans === 'p' ? 'project' : ans === 'n' ? false : 'global';
|
|
332
371
|
}
|
|
333
372
|
|
|
334
|
-
if (
|
|
335
|
-
|
|
336
|
-
|
|
373
|
+
if (choice === false) return;
|
|
374
|
+
if (choice === 'global') {
|
|
375
|
+
const file = enableGlobalConciseInstructions();
|
|
376
|
+
console.log(` Added token-efficiency instructions to ${file} (all projects)`);
|
|
377
|
+
} else {
|
|
378
|
+
const file = writeAgentsFile(dir, CONCISE_INSTRUCTIONS, CONCISE_START, CONCISE_END);
|
|
379
|
+
console.log(` Added token-efficiency instructions to ${file} (this project)`);
|
|
380
|
+
}
|
|
337
381
|
}
|
|
338
382
|
|
|
339
383
|
export function prompt(question) {
|
|
@@ -394,10 +438,16 @@ export async function runInit({ key, name, proxy, concise } = {}) {
|
|
|
394
438
|
// undefined (flag never passed) -> null, so reprovisionKeyConsumers can
|
|
395
439
|
// tell "not decided, ask interactively" apart from an explicit --proxy.
|
|
396
440
|
const proxyOptIn = proxy === undefined ? null : Boolean(proxy);
|
|
397
|
-
// Same null-means-ask-interactively shape as proxyOptIn
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
|
|
441
|
+
// Same null-means-ask-interactively shape as proxyOptIn, but three-valued:
|
|
442
|
+
// --concise alone (bare flag, parses as boolean true) means the
|
|
443
|
+
// recommended default, 'global'; --concise project or --concise global
|
|
444
|
+
// pick explicitly; --concise false skips. Omit the flag entirely for the
|
|
445
|
+
// default (interactive ask, or off when non-interactive).
|
|
446
|
+
const conciseOptIn =
|
|
447
|
+
concise === undefined ? null :
|
|
448
|
+
concise === true ? 'global' :
|
|
449
|
+
concise === 'false' ? false :
|
|
450
|
+
concise;
|
|
401
451
|
if (!key || !key.startsWith('txy-')) {
|
|
402
452
|
console.error('\n Error: --key is required and must start with txy-');
|
|
403
453
|
console.error(' Usage: npx troxy-cli init --key txy-...\n');
|