@astudioplus/compressor 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/CHANGELOG.md +52 -0
- package/LICENSE +20 -0
- package/README.md +167 -0
- package/dist/adapters/agents-md.d.ts +2 -0
- package/dist/adapters/agents-md.js +91 -0
- package/dist/adapters/apply.d.ts +3 -0
- package/dist/adapters/apply.js +83 -0
- package/dist/adapters/claude-code.d.ts +2 -0
- package/dist/adapters/claude-code.js +403 -0
- package/dist/adapters/copilot.d.ts +2 -0
- package/dist/adapters/copilot.js +418 -0
- package/dist/adapters/cursor.d.ts +2 -0
- package/dist/adapters/cursor.js +149 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +19 -0
- package/dist/adapters/markers.d.ts +7 -0
- package/dist/adapters/markers.js +129 -0
- package/dist/adapters/types.d.ts +44 -0
- package/dist/adapters/types.js +1 -0
- package/dist/bench/ablate.d.ts +35 -0
- package/dist/bench/ablate.js +163 -0
- package/dist/bench/cell.d.ts +33 -0
- package/dist/bench/cell.js +437 -0
- package/dist/bench/results.d.ts +37 -0
- package/dist/bench/results.js +157 -0
- package/dist/bench/runner.d.ts +24 -0
- package/dist/bench/runner.js +121 -0
- package/dist/bench/tasks.d.ts +4 -0
- package/dist/bench/tasks.js +147 -0
- package/dist/bench/types.d.ts +109 -0
- package/dist/bench/types.js +1 -0
- package/dist/claude/transcripts.d.ts +30 -0
- package/dist/claude/transcripts.js +154 -0
- package/dist/cli/commands/benchmark.d.ts +33 -0
- package/dist/cli/commands/benchmark.js +203 -0
- package/dist/cli/commands/compress.d.ts +8 -0
- package/dist/cli/commands/compress.js +45 -0
- package/dist/cli/commands/count.d.ts +5 -0
- package/dist/cli/commands/count.js +25 -0
- package/dist/cli/commands/hook.d.ts +6 -0
- package/dist/cli/commands/hook.js +30 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +76 -0
- package/dist/cli/commands/report.d.ts +90 -0
- package/dist/cli/commands/report.js +464 -0
- package/dist/cli/commands/savings.d.ts +38 -0
- package/dist/cli/commands/savings.js +196 -0
- package/dist/cli/commands/set-mode.d.ts +5 -0
- package/dist/cli/commands/set-mode.js +13 -0
- package/dist/cli/commands/stats.d.ts +5 -0
- package/dist/cli/commands/stats.js +51 -0
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +11 -0
- package/dist/cli/commands/uninstall.d.ts +7 -0
- package/dist/cli/commands/uninstall.js +22 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +146 -0
- package/dist/copilot-hook-entry.d.ts +1 -0
- package/dist/copilot-hook-entry.js +36 -0
- package/dist/copilot-hook.js +1000 -0
- package/dist/engine/detect.d.ts +2 -0
- package/dist/engine/detect.js +47 -0
- package/dist/engine/index.d.ts +4 -0
- package/dist/engine/index.js +90 -0
- package/dist/engine/policy.d.ts +2 -0
- package/dist/engine/policy.js +48 -0
- package/dist/engine/tiers/code.d.ts +7 -0
- package/dist/engine/tiers/code.js +206 -0
- package/dist/engine/tiers/logs.d.ts +4 -0
- package/dist/engine/tiers/logs.js +139 -0
- package/dist/engine/tiers/structural.d.ts +28 -0
- package/dist/engine/tiers/structural.js +199 -0
- package/dist/engine/types.d.ts +71 -0
- package/dist/engine/types.js +5 -0
- package/dist/hook/copilot.d.ts +5 -0
- package/dist/hook/copilot.js +136 -0
- package/dist/hook/core.d.ts +36 -0
- package/dist/hook/core.js +138 -0
- package/dist/hook/exit.d.ts +22 -0
- package/dist/hook/exit.js +56 -0
- package/dist/hook/post-tool-use.d.ts +5 -0
- package/dist/hook/post-tool-use.js +57 -0
- package/dist/hook-entry.d.ts +1 -0
- package/dist/hook-entry.js +35 -0
- package/dist/hook.js +946 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/ledger/read.d.ts +9 -0
- package/dist/ledger/read.js +91 -0
- package/dist/ledger/write.d.ts +29 -0
- package/dist/ledger/write.js +61 -0
- package/dist/packs/atoms.d.ts +3 -0
- package/dist/packs/atoms.js +108 -0
- package/dist/packs/modes.d.ts +3 -0
- package/dist/packs/modes.js +34 -0
- package/dist/packs/render.d.ts +24 -0
- package/dist/packs/render.js +115 -0
- package/dist/packs/types.d.ts +32 -0
- package/dist/packs/types.js +1 -0
- package/dist/paths.d.ts +29 -0
- package/dist/paths.js +87 -0
- package/dist/tokens/estimate.d.ts +12 -0
- package/dist/tokens/estimate.js +23 -0
- package/dist/tokens/exact.d.ts +5 -0
- package/dist/tokens/exact.js +16 -0
- package/dist/tokens/index.d.ts +2 -0
- package/dist/tokens/index.js +2 -0
- package/package.json +77 -0
package/dist/paths.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
function isCompressorRoot(dir) {
|
|
5
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
6
|
+
if (!existsSync(pkgPath)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
11
|
+
return (typeof parsed === 'object' &&
|
|
12
|
+
parsed !== null &&
|
|
13
|
+
parsed['name'] === 'compressor');
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function packageRoot() {
|
|
20
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
while (true) {
|
|
22
|
+
if (isCompressorRoot(dir)) {
|
|
23
|
+
return dir;
|
|
24
|
+
}
|
|
25
|
+
const parent = path.dirname(dir);
|
|
26
|
+
if (parent === dir) {
|
|
27
|
+
throw new Error('could not locate the compressor package root from ' + import.meta.url);
|
|
28
|
+
}
|
|
29
|
+
dir = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function hookCommandFor(mode, root) {
|
|
33
|
+
// quoted so the command survives package roots containing spaces
|
|
34
|
+
return `node "${path.join(root, 'dist', 'hook.js')}" --mode ${mode}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Hook command for ownership matching in status/uninstall — must work even
|
|
38
|
+
* when the bundle is missing (e.g. uninstalling a broken install).
|
|
39
|
+
*/
|
|
40
|
+
export function describeHookCommand(mode, root = packageRoot()) {
|
|
41
|
+
return hookCommandFor(mode, root);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Command line installed for the PostToolUse hook. Refuses to install a
|
|
45
|
+
* command that would fail on every tool call (fail-open hook = silent no-op).
|
|
46
|
+
*/
|
|
47
|
+
export function resolveHookCommand(mode, root = packageRoot()) {
|
|
48
|
+
const hookPath = path.join(root, 'dist', 'hook.js');
|
|
49
|
+
if (!existsSync(hookPath)) {
|
|
50
|
+
throw new Error(`hook bundle missing at ${hookPath} — run 'npm run build' in the compressor package, then re-run`);
|
|
51
|
+
}
|
|
52
|
+
return hookCommandFor(mode, root);
|
|
53
|
+
}
|
|
54
|
+
function copilotHookCommandFor(mode, root) {
|
|
55
|
+
// quoted so the command survives package roots containing spaces
|
|
56
|
+
return `node "${path.join(root, 'dist', 'copilot-hook.js')}" --mode ${mode}`;
|
|
57
|
+
}
|
|
58
|
+
/** Copilot hook command for display/matching — works without the bundle. */
|
|
59
|
+
export function describeCopilotHookCommand(mode, root = packageRoot()) {
|
|
60
|
+
return copilotHookCommandFor(mode, root);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Command line installed for the Copilot postToolUse hook. Like
|
|
64
|
+
* resolveHookCommand, refuses to install a command that would fail on every
|
|
65
|
+
* tool call (Copilot postToolUse is fail-open: a dead command = silent no-op).
|
|
66
|
+
*/
|
|
67
|
+
export function resolveCopilotHookCommand(mode, root = packageRoot()) {
|
|
68
|
+
const hookPath = path.join(root, 'dist', 'copilot-hook.js');
|
|
69
|
+
if (!existsSync(hookPath)) {
|
|
70
|
+
throw new Error(`copilot hook bundle missing at ${hookPath} — run 'npm run build' in the compressor package, then re-run`);
|
|
71
|
+
}
|
|
72
|
+
return copilotHookCommandFor(mode, root);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Copilot hook command derived from the Claude Code hook command carried in
|
|
76
|
+
* AdapterContext (the only resolved-path carrier adapters receive; the
|
|
77
|
+
* adapters/types.ts contract is frozen). Both commands are generated in this
|
|
78
|
+
* module from the same root, so swapping the sibling bundle name and --mode
|
|
79
|
+
* flag reproduces copilotHookCommandFor exactly. Mode omitted → base form
|
|
80
|
+
* for ownership matching (mode-agnostic, like claude-code's predicate).
|
|
81
|
+
*/
|
|
82
|
+
export function copilotHookCommandFrom(hookCommand, mode) {
|
|
83
|
+
const base = hookCommand
|
|
84
|
+
.replace(/ --mode \S+$/, '')
|
|
85
|
+
.replace(/(?<![\w-])hook\.js("?)$/, 'copilot-hook.js$1');
|
|
86
|
+
return mode === undefined ? base : `${base} --mode ${mode}`;
|
|
87
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Estimator } from '../engine/types.ts';
|
|
2
|
+
/** Zero-cost estimator for the hook hot path. */
|
|
3
|
+
export declare const cheapEstimator: Estimator;
|
|
4
|
+
/**
|
|
5
|
+
* NOTE: cl100k_base undercounts Claude tokens by ~15-20%. Estimates are for
|
|
6
|
+
* thresholds/UX only, NEVER reported as savings.
|
|
7
|
+
*
|
|
8
|
+
* js-tiktoken (~5.6MB of inlined ranks) is loaded lazily via createRequire so
|
|
9
|
+
* that importing `cheapEstimator` from the bundled hook never pulls it in.
|
|
10
|
+
*/
|
|
11
|
+
export declare function tiktokenEstimator(): Estimator;
|
|
12
|
+
export declare function estimateTokens(text: string): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
/** Zero-cost estimator for the hook hot path. */
|
|
3
|
+
export const cheapEstimator = (text) => Math.ceil(text.length / 3.5);
|
|
4
|
+
let encoder;
|
|
5
|
+
/**
|
|
6
|
+
* NOTE: cl100k_base undercounts Claude tokens by ~15-20%. Estimates are for
|
|
7
|
+
* thresholds/UX only, NEVER reported as savings.
|
|
8
|
+
*
|
|
9
|
+
* js-tiktoken (~5.6MB of inlined ranks) is loaded lazily via createRequire so
|
|
10
|
+
* that importing `cheapEstimator` from the bundled hook never pulls it in.
|
|
11
|
+
*/
|
|
12
|
+
export function tiktokenEstimator() {
|
|
13
|
+
if (encoder === undefined) {
|
|
14
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
15
|
+
const mod = nodeRequire('js-tiktoken');
|
|
16
|
+
encoder = mod.getEncoding('cl100k_base');
|
|
17
|
+
}
|
|
18
|
+
const enc = encoder;
|
|
19
|
+
return (text) => enc.encode(text).length;
|
|
20
|
+
}
|
|
21
|
+
export function estimateTokens(text) {
|
|
22
|
+
return tiktokenEstimator()(text);
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact token count via the Anthropic count_tokens endpoint. The SDK is
|
|
3
|
+
* imported lazily so it never loads unless --exact is actually used.
|
|
4
|
+
*/
|
|
5
|
+
export async function countTokensExact(text, model = 'claude-sonnet-4-6') {
|
|
6
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
7
|
+
throw new Error('ANTHROPIC_API_KEY required for --exact counts; estimated counts work without it');
|
|
8
|
+
}
|
|
9
|
+
const { default: Anthropic } = await import('@anthropic-ai/sdk');
|
|
10
|
+
const client = new Anthropic();
|
|
11
|
+
const result = await client.messages.countTokens({
|
|
12
|
+
model,
|
|
13
|
+
messages: [{ role: 'user', content: text }],
|
|
14
|
+
});
|
|
15
|
+
return result.input_tokens;
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@astudioplus/compressor",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Reduce token usage in AI coding agents (Claude Code, Copilot, Cursor) with instruction packs and tool-output compression hooks — savings you can measure.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "Andrey Vasilevsky <anvanster@gmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/anvanster/compressor#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/anvanster/compressor.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/anvanster/compressor/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"token-optimization",
|
|
18
|
+
"prompt-compression",
|
|
19
|
+
"context-compression",
|
|
20
|
+
"ai-coding-agents",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"github-copilot",
|
|
23
|
+
"cursor",
|
|
24
|
+
"agents-md",
|
|
25
|
+
"llm",
|
|
26
|
+
"anthropic",
|
|
27
|
+
"developer-tools",
|
|
28
|
+
"cli"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20.0.0"
|
|
32
|
+
},
|
|
33
|
+
"os": [
|
|
34
|
+
"darwin",
|
|
35
|
+
"linux",
|
|
36
|
+
"win32"
|
|
37
|
+
],
|
|
38
|
+
"cpu": [
|
|
39
|
+
"x64",
|
|
40
|
+
"arm64"
|
|
41
|
+
],
|
|
42
|
+
"bin": {
|
|
43
|
+
"compressor": "dist/cli/index.js"
|
|
44
|
+
},
|
|
45
|
+
"exports": {
|
|
46
|
+
".": "./dist/index.js",
|
|
47
|
+
"./engine": "./dist/engine/index.js",
|
|
48
|
+
"./tokens": "./dist/tokens/index.js"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE",
|
|
54
|
+
"CHANGELOG.md"
|
|
55
|
+
],
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -p tsconfig.build.json && npm run bundle:hook && npm run bundle:copilot-hook",
|
|
61
|
+
"bundle:hook": "esbuild src/hook-entry.ts --bundle --platform=node --format=esm --outfile=dist/hook.js",
|
|
62
|
+
"bundle:copilot-hook": "esbuild src/copilot-hook-entry.ts --bundle --platform=node --format=esm --outfile=dist/copilot-hook.js",
|
|
63
|
+
"prepublishOnly": "npm run build",
|
|
64
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
65
|
+
"test": "node --test \"test/**/*.test.ts\""
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"@anthropic-ai/sdk": "^0.104.1",
|
|
69
|
+
"commander": "^15.0.0",
|
|
70
|
+
"js-tiktoken": "^1.0.21"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@types/node": "^25.9.2",
|
|
74
|
+
"esbuild": "^0.28.0",
|
|
75
|
+
"typescript": "^6.0.3"
|
|
76
|
+
}
|
|
77
|
+
}
|