@drewpayment/mink 0.12.0 → 0.13.0-beta.2
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/dashboard/out/404.html +1 -1
- package/dashboard/out/action-log.html +1 -1
- package/dashboard/out/action-log.txt +1 -1
- package/dashboard/out/activity.html +1 -1
- package/dashboard/out/activity.txt +1 -1
- package/dashboard/out/bugs.html +1 -1
- package/dashboard/out/bugs.txt +1 -1
- package/dashboard/out/capture.html +1 -1
- package/dashboard/out/capture.txt +1 -1
- package/dashboard/out/config.html +1 -1
- package/dashboard/out/config.txt +1 -1
- package/dashboard/out/daemon.html +1 -1
- package/dashboard/out/daemon.txt +1 -1
- package/dashboard/out/design.html +1 -1
- package/dashboard/out/design.txt +1 -1
- package/dashboard/out/discord.html +1 -1
- package/dashboard/out/discord.txt +1 -1
- package/dashboard/out/file-index.html +1 -1
- package/dashboard/out/file-index.txt +1 -1
- package/dashboard/out/index.html +1 -1
- package/dashboard/out/index.txt +1 -1
- package/dashboard/out/insights.html +1 -1
- package/dashboard/out/insights.txt +1 -1
- package/dashboard/out/learning.html +1 -1
- package/dashboard/out/learning.txt +1 -1
- package/dashboard/out/overview.html +1 -1
- package/dashboard/out/overview.txt +1 -1
- package/dashboard/out/scheduler.html +1 -1
- package/dashboard/out/scheduler.txt +1 -1
- package/dashboard/out/sync.html +1 -1
- package/dashboard/out/sync.txt +1 -1
- package/dashboard/out/tokens.html +1 -1
- package/dashboard/out/tokens.txt +1 -1
- package/dashboard/out/waste.html +1 -1
- package/dashboard/out/waste.txt +1 -1
- package/dashboard/out/wiki.html +1 -1
- package/dashboard/out/wiki.txt +1 -1
- package/dist/cli.bun.js +748 -10
- package/dist/cli.node.js +752 -12
- package/package.json +1 -1
- package/src/cli.ts +14 -0
- package/src/commands/init.ts +5 -1
- package/src/commands/post-read.ts +18 -0
- package/src/commands/post-tool.ts +48 -0
- package/src/commands/retrieve.ts +32 -0
- package/src/core/code-skeleton.ts +108 -0
- package/src/core/compress-tool-output.ts +127 -0
- package/src/core/compression.ts +81 -0
- package/src/core/hook-output.ts +42 -0
- package/src/core/output-compression.ts +252 -0
- package/src/core/token-estimate.ts +40 -0
- package/src/repositories/compression-cache-repo.ts +97 -0
- package/src/repositories/token-ledger-repo.ts +87 -0
- package/src/storage/schema.ts +50 -1
- package/src/types/compression.ts +29 -0
- package/src/types/config.ts +40 -0
- package/src/types/hook-input.ts +4 -0
- package/src/types/token-ledger.ts +33 -0
- /package/dashboard/out/_next/static/{Cr7-P-E43jbsBjy4hA6wH → Yl3F-J4CwvYf6yWG-SSmG}/_buildManifest.js +0 -0
- /package/dashboard/out/_next/static/{Cr7-P-E43jbsBjy4hA6wH → Yl3F-J4CwvYf6yWG-SSmG}/_ssgManifest.js +0 -0
package/dist/cli.node.js
CHANGED
|
@@ -422,6 +422,41 @@ var init_config = __esm(() => {
|
|
|
422
422
|
envVar: "MINK_PROJECTS_IDENTITY",
|
|
423
423
|
description: "Project identity strategy: path-derived (legacy) or git-remote (stable across machines)",
|
|
424
424
|
scope: "shared"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
key: "compression.enabled",
|
|
428
|
+
default: "false",
|
|
429
|
+
envVar: "MINK_COMPRESSION_ENABLED",
|
|
430
|
+
description: "Enable tool-output compression (spec 21). Off until inline compression ships.",
|
|
431
|
+
scope: "shared"
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
key: "compression.threshold-tokens",
|
|
435
|
+
default: "800",
|
|
436
|
+
envVar: "MINK_COMPRESSION_THRESHOLD_TOKENS",
|
|
437
|
+
description: "Minimum estimated token size before a tool output is eligible for compression",
|
|
438
|
+
scope: "shared"
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
key: "compression.min-savings-ratio",
|
|
442
|
+
default: "0.25",
|
|
443
|
+
envVar: "MINK_COMPRESSION_MIN_SAVINGS_RATIO",
|
|
444
|
+
description: "Discard a compression attempt unless it saves at least this fraction of tokens",
|
|
445
|
+
scope: "shared"
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
key: "compression.holdout-fraction",
|
|
449
|
+
default: "0.1",
|
|
450
|
+
envVar: "MINK_COMPRESSION_HOLDOUT_FRACTION",
|
|
451
|
+
description: "Fraction of eligible outputs left uncompressed as a measured control group",
|
|
452
|
+
scope: "shared"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
key: "compression.retention-hours",
|
|
456
|
+
default: "168",
|
|
457
|
+
envVar: "MINK_COMPRESSION_RETENTION_HOURS",
|
|
458
|
+
description: "How long compressed originals stay retrievable before eviction",
|
|
459
|
+
scope: "shared"
|
|
425
460
|
}
|
|
426
461
|
];
|
|
427
462
|
VALID_KEYS = new Set(CONFIG_KEYS.map((k) => k.key));
|
|
@@ -3096,7 +3131,7 @@ function readMeta(db, key) {
|
|
|
3096
3131
|
function writeMeta(db, key, value) {
|
|
3097
3132
|
db.prepare("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
3098
3133
|
}
|
|
3099
|
-
var SCHEMA_VERSION =
|
|
3134
|
+
var SCHEMA_VERSION = 3, INITIAL_SCHEMA = `
|
|
3100
3135
|
CREATE TABLE IF NOT EXISTS meta (
|
|
3101
3136
|
key TEXT PRIMARY KEY,
|
|
3102
3137
|
value TEXT NOT NULL
|
|
@@ -3258,6 +3293,55 @@ CREATE TABLE IF NOT EXISTS counters (
|
|
|
3258
3293
|
file_index_hits INTEGER NOT NULL DEFAULT 0,
|
|
3259
3294
|
file_index_misses INTEGER NOT NULL DEFAULT 0
|
|
3260
3295
|
);
|
|
3296
|
+
|
|
3297
|
+
-- Tool-output compression measurement (spec 21). One row per compression
|
|
3298
|
+
-- decision: either a compressed arm (compressed_tokens < original_tokens) or a
|
|
3299
|
+
-- holdout arm (left uncompressed for control, compressed_tokens = original_tokens).
|
|
3300
|
+
-- These are append-only telemetry, independent of session lifecycle, written at
|
|
3301
|
+
-- the moment a tool output is processed. New table → applied to existing DBs via
|
|
3302
|
+
-- IF NOT EXISTS on the next open.
|
|
3303
|
+
CREATE TABLE IF NOT EXISTS ledger_compressions (
|
|
3304
|
+
id TEXT PRIMARY KEY,
|
|
3305
|
+
created_at TEXT NOT NULL,
|
|
3306
|
+
tool_name TEXT NOT NULL,
|
|
3307
|
+
content_kind TEXT NOT NULL,
|
|
3308
|
+
original_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3309
|
+
compressed_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3310
|
+
holdout INTEGER NOT NULL DEFAULT 0,
|
|
3311
|
+
device_id TEXT NOT NULL
|
|
3312
|
+
);
|
|
3313
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_compressions_created ON ledger_compressions(created_at);
|
|
3314
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_compressions_device ON ledger_compressions(device_id);
|
|
3315
|
+
|
|
3316
|
+
-- Per-device compression aggregates, summed across devices like ledger_lifetime.
|
|
3317
|
+
-- measured_savings only credits compressed arms (holdout arms save nothing by
|
|
3318
|
+
-- construction), so the reported figure is a true measured delta, not an estimate.
|
|
3319
|
+
CREATE TABLE IF NOT EXISTS ledger_compression_lifetime (
|
|
3320
|
+
device_id TEXT PRIMARY KEY,
|
|
3321
|
+
total_events INTEGER NOT NULL DEFAULT 0,
|
|
3322
|
+
total_holdout_events INTEGER NOT NULL DEFAULT 0,
|
|
3323
|
+
total_original_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3324
|
+
total_compressed_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3325
|
+
total_measured_savings INTEGER NOT NULL DEFAULT 0
|
|
3326
|
+
);
|
|
3327
|
+
|
|
3328
|
+
-- Reversible-compression cache (spec 21 §Reversibility). When a tool output is
|
|
3329
|
+
-- compressed, the original is stored here keyed by a short retrieval token and
|
|
3330
|
+
-- embedded in the compressed result; "mink retrieve <token>" returns it
|
|
3331
|
+
-- byte-exact. Rows expire after the configured retention window; an expired or
|
|
3332
|
+
-- unknown token is a graceful miss. This is a local cache, not synced state, so
|
|
3333
|
+
-- (unlike other tables) it carries no merge semantics beyond device_id for audit.
|
|
3334
|
+
CREATE TABLE IF NOT EXISTS compression_cache (
|
|
3335
|
+
token TEXT PRIMARY KEY,
|
|
3336
|
+
created_at TEXT NOT NULL,
|
|
3337
|
+
expires_at TEXT NOT NULL,
|
|
3338
|
+
tool_name TEXT NOT NULL,
|
|
3339
|
+
content_kind TEXT NOT NULL,
|
|
3340
|
+
content TEXT NOT NULL,
|
|
3341
|
+
size_bytes INTEGER NOT NULL,
|
|
3342
|
+
device_id TEXT NOT NULL
|
|
3343
|
+
);
|
|
3344
|
+
CREATE INDEX IF NOT EXISTS idx_compression_cache_expires ON compression_cache(expires_at);
|
|
3261
3345
|
`;
|
|
3262
3346
|
|
|
3263
3347
|
// src/storage/migrate-json.ts
|
|
@@ -3675,6 +3759,68 @@ class TokenLedgerRepo {
|
|
|
3675
3759
|
}
|
|
3676
3760
|
});
|
|
3677
3761
|
}
|
|
3762
|
+
recordCompression(event, deviceId = getOrCreateDeviceId()) {
|
|
3763
|
+
const id = event.id ?? crypto.randomUUID();
|
|
3764
|
+
const createdAt = event.createdAt ?? new Date().toISOString();
|
|
3765
|
+
const holdout = event.holdout ? 1 : 0;
|
|
3766
|
+
const savings = event.holdout ? 0 : Math.max(0, event.originalTokens - event.compressedTokens);
|
|
3767
|
+
this.db.transaction(() => {
|
|
3768
|
+
this.db.prepare(`
|
|
3769
|
+
INSERT OR REPLACE INTO ledger_compressions
|
|
3770
|
+
(id, created_at, tool_name, content_kind,
|
|
3771
|
+
original_tokens, compressed_tokens, holdout, device_id)
|
|
3772
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3773
|
+
`).run(id, createdAt, event.toolName, event.contentKind, event.originalTokens, event.compressedTokens, holdout, deviceId);
|
|
3774
|
+
this.db.prepare(`
|
|
3775
|
+
INSERT INTO ledger_compression_lifetime
|
|
3776
|
+
(device_id, total_events, total_holdout_events,
|
|
3777
|
+
total_original_tokens, total_compressed_tokens, total_measured_savings)
|
|
3778
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
3779
|
+
ON CONFLICT(device_id) DO UPDATE SET
|
|
3780
|
+
total_events = ledger_compression_lifetime.total_events + excluded.total_events,
|
|
3781
|
+
total_holdout_events = ledger_compression_lifetime.total_holdout_events + excluded.total_holdout_events,
|
|
3782
|
+
total_original_tokens = ledger_compression_lifetime.total_original_tokens + excluded.total_original_tokens,
|
|
3783
|
+
total_compressed_tokens = ledger_compression_lifetime.total_compressed_tokens + excluded.total_compressed_tokens,
|
|
3784
|
+
total_measured_savings = ledger_compression_lifetime.total_measured_savings + excluded.total_measured_savings
|
|
3785
|
+
`).run(deviceId, 1, holdout, event.originalTokens, event.compressedTokens, savings);
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
compressionLifetime() {
|
|
3789
|
+
const row = this.db.prepare(`
|
|
3790
|
+
SELECT
|
|
3791
|
+
COALESCE(SUM(total_events), 0) AS totalEvents,
|
|
3792
|
+
COALESCE(SUM(total_holdout_events), 0) AS totalHoldoutEvents,
|
|
3793
|
+
COALESCE(SUM(total_original_tokens), 0) AS totalOriginalTokens,
|
|
3794
|
+
COALESCE(SUM(total_compressed_tokens), 0) AS totalCompressedTokens,
|
|
3795
|
+
COALESCE(SUM(total_measured_savings), 0) AS totalMeasuredSavings
|
|
3796
|
+
FROM ledger_compression_lifetime
|
|
3797
|
+
`).get();
|
|
3798
|
+
return {
|
|
3799
|
+
totalEvents: Number(row?.totalEvents ?? 0),
|
|
3800
|
+
totalHoldoutEvents: Number(row?.totalHoldoutEvents ?? 0),
|
|
3801
|
+
totalOriginalTokens: Number(row?.totalOriginalTokens ?? 0),
|
|
3802
|
+
totalCompressedTokens: Number(row?.totalCompressedTokens ?? 0),
|
|
3803
|
+
totalMeasuredSavings: Number(row?.totalMeasuredSavings ?? 0)
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
compressionEvents(limit = 100) {
|
|
3807
|
+
const rows = this.db.prepare(`
|
|
3808
|
+
SELECT id, created_at, tool_name, content_kind,
|
|
3809
|
+
original_tokens, compressed_tokens, holdout
|
|
3810
|
+
FROM ledger_compressions
|
|
3811
|
+
ORDER BY created_at DESC
|
|
3812
|
+
LIMIT ?
|
|
3813
|
+
`).all(limit);
|
|
3814
|
+
return rows.map((r) => ({
|
|
3815
|
+
id: String(r.id),
|
|
3816
|
+
createdAt: String(r.created_at),
|
|
3817
|
+
toolName: String(r.tool_name),
|
|
3818
|
+
contentKind: String(r.content_kind),
|
|
3819
|
+
originalTokens: Number(r.original_tokens),
|
|
3820
|
+
compressedTokens: Number(r.compressed_tokens),
|
|
3821
|
+
holdout: Number(r.holdout) === 1
|
|
3822
|
+
}));
|
|
3823
|
+
}
|
|
3678
3824
|
insertSessionRow(summary, deviceId, archived) {
|
|
3679
3825
|
this.db.prepare(`
|
|
3680
3826
|
INSERT OR REPLACE INTO ledger_sessions
|
|
@@ -4449,6 +4595,28 @@ function estimateTokens2(content, filePath) {
|
|
|
4449
4595
|
}
|
|
4450
4596
|
return Math.ceil(content.length / ratio);
|
|
4451
4597
|
}
|
|
4598
|
+
function countTokens(text) {
|
|
4599
|
+
if (!text)
|
|
4600
|
+
return 0;
|
|
4601
|
+
const segments = text.match(/[A-Za-z]+|[0-9]+|[^A-Za-z0-9]/g);
|
|
4602
|
+
if (!segments)
|
|
4603
|
+
return 0;
|
|
4604
|
+
let tokens = 0;
|
|
4605
|
+
for (const seg of segments) {
|
|
4606
|
+
const first = seg.charCodeAt(0);
|
|
4607
|
+
if (first >= 65 && first <= 90 || first >= 97 && first <= 122) {
|
|
4608
|
+
tokens += Math.ceil(seg.length / 4);
|
|
4609
|
+
} else if (first >= 48 && first <= 57) {
|
|
4610
|
+
tokens += Math.ceil(seg.length / 3);
|
|
4611
|
+
} else if (seg === `
|
|
4612
|
+
`) {
|
|
4613
|
+
tokens += 1;
|
|
4614
|
+
} else if (seg === " " || seg === "\t" || seg === "\r") {} else {
|
|
4615
|
+
tokens += 1;
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
return tokens;
|
|
4619
|
+
}
|
|
4452
4620
|
var CODE_EXTENSIONS, PROSE_EXTENSIONS, BINARY_EXTENSIONS;
|
|
4453
4621
|
var init_token_estimate = __esm(() => {
|
|
4454
4622
|
CODE_EXTENSIONS = new Set([
|
|
@@ -5813,12 +5981,14 @@ function buildHooksConfig(cliPath) {
|
|
|
5813
5981
|
PostToolUse: [
|
|
5814
5982
|
{ matcher: "Read", hooks: hook(`${prefix} post-read`) },
|
|
5815
5983
|
{ matcher: "Edit", hooks: hook(`${prefix} post-write`) },
|
|
5816
|
-
{ matcher: "Write", hooks: hook(`${prefix} post-write`) }
|
|
5984
|
+
{ matcher: "Write", hooks: hook(`${prefix} post-write`) },
|
|
5985
|
+
{ matcher: "Bash", hooks: hook(`${prefix} post-tool`) },
|
|
5986
|
+
{ matcher: "Grep", hooks: hook(`${prefix} post-tool`) }
|
|
5817
5987
|
]
|
|
5818
5988
|
};
|
|
5819
5989
|
}
|
|
5820
5990
|
function isMinkCommand(cmd) {
|
|
5821
|
-
const hasMinkSubcommand = cmd.includes("session-start") || cmd.includes("session-stop") || cmd.includes("pre-read") || cmd.includes("post-read") || cmd.includes("pre-write") || cmd.includes("post-write");
|
|
5991
|
+
const hasMinkSubcommand = cmd.includes("session-start") || cmd.includes("session-stop") || cmd.includes("pre-read") || cmd.includes("post-read") || cmd.includes("pre-write") || cmd.includes("post-write") || cmd.includes("post-tool");
|
|
5822
5992
|
if (!hasMinkSubcommand)
|
|
5823
5993
|
return false;
|
|
5824
5994
|
if (/(^|\/|\s)mink\s/.test(cmd))
|
|
@@ -7121,6 +7291,488 @@ var init_pre_read = __esm(() => {
|
|
|
7121
7291
|
init_counters_repo();
|
|
7122
7292
|
});
|
|
7123
7293
|
|
|
7294
|
+
// src/core/compression.ts
|
|
7295
|
+
function numberValue(key, fallback, min, max) {
|
|
7296
|
+
const raw = resolveConfigValue(key).value;
|
|
7297
|
+
const n = Number(raw);
|
|
7298
|
+
if (!Number.isFinite(n))
|
|
7299
|
+
return fallback;
|
|
7300
|
+
return Math.min(max, Math.max(min, n));
|
|
7301
|
+
}
|
|
7302
|
+
function loadCompressionConfig() {
|
|
7303
|
+
return {
|
|
7304
|
+
enabled: resolveConfigValue("compression.enabled").value === "true",
|
|
7305
|
+
thresholdTokens: numberValue("compression.threshold-tokens", 800, 0, Number.MAX_SAFE_INTEGER),
|
|
7306
|
+
minSavingsRatio: numberValue("compression.min-savings-ratio", 0.25, 0, 1),
|
|
7307
|
+
holdoutFraction: numberValue("compression.holdout-fraction", 0.1, 0, 1),
|
|
7308
|
+
retentionHours: numberValue("compression.retention-hours", 168, 0, Number.MAX_SAFE_INTEGER)
|
|
7309
|
+
};
|
|
7310
|
+
}
|
|
7311
|
+
function isEligible(originalTokens, config) {
|
|
7312
|
+
return config.enabled && originalTokens >= config.thresholdTokens;
|
|
7313
|
+
}
|
|
7314
|
+
function meetsMinSavings(originalTokens, compressedTokens, config) {
|
|
7315
|
+
if (originalTokens <= 0)
|
|
7316
|
+
return false;
|
|
7317
|
+
const ratio = (originalTokens - compressedTokens) / originalTokens;
|
|
7318
|
+
return ratio >= config.minSavingsRatio;
|
|
7319
|
+
}
|
|
7320
|
+
function hashUnitInterval(key) {
|
|
7321
|
+
let h = 2166136261;
|
|
7322
|
+
for (let i = 0;i < key.length; i++) {
|
|
7323
|
+
h ^= key.charCodeAt(i);
|
|
7324
|
+
h = Math.imul(h, 16777619);
|
|
7325
|
+
}
|
|
7326
|
+
return (h >>> 0) / 4294967296;
|
|
7327
|
+
}
|
|
7328
|
+
function selectHoldout(eventKey, fraction) {
|
|
7329
|
+
if (fraction <= 0)
|
|
7330
|
+
return false;
|
|
7331
|
+
if (fraction >= 1)
|
|
7332
|
+
return true;
|
|
7333
|
+
return hashUnitInterval(eventKey) < fraction;
|
|
7334
|
+
}
|
|
7335
|
+
var init_compression = __esm(() => {
|
|
7336
|
+
init_global_config();
|
|
7337
|
+
});
|
|
7338
|
+
|
|
7339
|
+
// src/core/code-skeleton.ts
|
|
7340
|
+
function countChar(s, c) {
|
|
7341
|
+
let n = 0;
|
|
7342
|
+
for (let i = 0;i < s.length; i++)
|
|
7343
|
+
if (s[i] === c)
|
|
7344
|
+
n++;
|
|
7345
|
+
return n;
|
|
7346
|
+
}
|
|
7347
|
+
function netBraces(line) {
|
|
7348
|
+
let s = line.replace(/\/\/.*$/, "");
|
|
7349
|
+
s = s.replace(/\/\*.*?\*\//g, "");
|
|
7350
|
+
s = s.replace(/"(?:\\.|[^"\\])*"/g, '""');
|
|
7351
|
+
s = s.replace(/'(?:\\.|[^'\\])*'/g, "''");
|
|
7352
|
+
s = s.replace(/`(?:\\.|[^`\\])*`/g, "``");
|
|
7353
|
+
return countChar(s, "{") - countChar(s, "}");
|
|
7354
|
+
}
|
|
7355
|
+
function stripOpenBrace(sig) {
|
|
7356
|
+
return sig.replace(/\{\s*$/, "").trimEnd();
|
|
7357
|
+
}
|
|
7358
|
+
function extractCodeSkeleton(content, opts = {}) {
|
|
7359
|
+
const rawLines = content.split(`
|
|
7360
|
+
`);
|
|
7361
|
+
const totalLines = rawLines.length > 0 && rawLines[rawLines.length - 1] === "" ? rawLines.length - 1 : rawLines.length;
|
|
7362
|
+
const out = [];
|
|
7363
|
+
let depth = 0;
|
|
7364
|
+
let suppress = Infinity;
|
|
7365
|
+
for (const line of rawLines) {
|
|
7366
|
+
if (out.length >= MAX_SIGNATURES)
|
|
7367
|
+
break;
|
|
7368
|
+
const start = depth;
|
|
7369
|
+
const net = netBraces(line);
|
|
7370
|
+
if (start < suppress) {
|
|
7371
|
+
const isHeading = opts.markdown === true && HEADING.test(line);
|
|
7372
|
+
const captured = isHeading || DECL_ALWAYS.test(line) || DECL_EXPORTED_VAR.test(line) || start >= 1 && MEMBER.test(line);
|
|
7373
|
+
if (captured) {
|
|
7374
|
+
const sig = line.trim();
|
|
7375
|
+
if (net > 0) {
|
|
7376
|
+
if (DESCEND.test(line) && !isHeading) {
|
|
7377
|
+
out.push(INDENT.repeat(start) + stripOpenBrace(sig) + " {");
|
|
7378
|
+
} else {
|
|
7379
|
+
out.push(INDENT.repeat(start) + stripOpenBrace(sig) + " { … }");
|
|
7380
|
+
suppress = start + 1;
|
|
7381
|
+
}
|
|
7382
|
+
} else {
|
|
7383
|
+
out.push(INDENT.repeat(start) + sig);
|
|
7384
|
+
}
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
depth = Math.max(0, depth + net);
|
|
7388
|
+
if (depth < suppress)
|
|
7389
|
+
suppress = Infinity;
|
|
7390
|
+
}
|
|
7391
|
+
if (out.length === 0)
|
|
7392
|
+
return null;
|
|
7393
|
+
return { lines: out, totalLines };
|
|
7394
|
+
}
|
|
7395
|
+
var MAX_SIGNATURES = 80, INDENT = " ", DECL_ALWAYS, DECL_EXPORTED_VAR, MEMBER, HEADING, DESCEND;
|
|
7396
|
+
var init_code_skeleton = __esm(() => {
|
|
7397
|
+
DECL_ALWAYS = /^\s*(?:export\s+)?(?:default\s+)?(?:abstract\s+)?(?:async\s+)?(?:function|class|interface|type|enum|namespace|module|def|fn|func|impl|struct|trait)\b/;
|
|
7398
|
+
DECL_EXPORTED_VAR = /^\s*export\s+(?:default\s+)?(?:const|let|var)\b/;
|
|
7399
|
+
MEMBER = /^\s*(?:public\s+|private\s+|protected\s+|readonly\s+|static\s+|async\s+|get\s+|set\s+|#)*[\w$]+\??\s*(?:\(|:|=)/;
|
|
7400
|
+
HEADING = /^#{1,6}\s+\S/;
|
|
7401
|
+
DESCEND = /\b(?:class|interface|enum|namespace|module|struct|trait|impl)\b/;
|
|
7402
|
+
});
|
|
7403
|
+
|
|
7404
|
+
// src/core/output-compression.ts
|
|
7405
|
+
function stripAnsi(s) {
|
|
7406
|
+
return s.replace(ANSI, "");
|
|
7407
|
+
}
|
|
7408
|
+
function omittedMarker(n) {
|
|
7409
|
+
return ` … ${n} line${n === 1 ? "" : "s"} omitted — mink retrieve …`;
|
|
7410
|
+
}
|
|
7411
|
+
function toLines(content) {
|
|
7412
|
+
const lines = content.split(`
|
|
7413
|
+
`);
|
|
7414
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
7415
|
+
lines.pop();
|
|
7416
|
+
return lines;
|
|
7417
|
+
}
|
|
7418
|
+
function compressLog(content) {
|
|
7419
|
+
const lines = toLines(stripAnsi(content));
|
|
7420
|
+
const collapsed = [];
|
|
7421
|
+
let i = 0;
|
|
7422
|
+
while (i < lines.length) {
|
|
7423
|
+
let run = 1;
|
|
7424
|
+
while (i + run < lines.length && lines[i + run] === lines[i])
|
|
7425
|
+
run++;
|
|
7426
|
+
collapsed.push(run > 1 ? `${lines[i]} (×${run})` : lines[i]);
|
|
7427
|
+
i += run;
|
|
7428
|
+
}
|
|
7429
|
+
if (collapsed.length <= LOG_HEAD + LOG_TAIL) {
|
|
7430
|
+
if (collapsed.length === lines.length)
|
|
7431
|
+
return null;
|
|
7432
|
+
return {
|
|
7433
|
+
compressed: collapsed.join(`
|
|
7434
|
+
`),
|
|
7435
|
+
omittedNote: `collapsed ${lines.length - collapsed.length} repeated line(s)`
|
|
7436
|
+
};
|
|
7437
|
+
}
|
|
7438
|
+
const omitted = collapsed.length - LOG_HEAD - LOG_TAIL;
|
|
7439
|
+
const head = collapsed.slice(0, LOG_HEAD);
|
|
7440
|
+
const tail = collapsed.slice(collapsed.length - LOG_TAIL);
|
|
7441
|
+
return {
|
|
7442
|
+
compressed: [...head, omittedMarker(omitted), ...tail].join(`
|
|
7443
|
+
`),
|
|
7444
|
+
omittedNote: `${omitted} of ${collapsed.length} log line(s) omitted (middle)`
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
function compressSearch(content) {
|
|
7448
|
+
const lines = toLines(content);
|
|
7449
|
+
const seen = new Set;
|
|
7450
|
+
const perFile = new Map;
|
|
7451
|
+
const omittedByFile = new Map;
|
|
7452
|
+
const out = [];
|
|
7453
|
+
for (const line of lines) {
|
|
7454
|
+
if (seen.has(line))
|
|
7455
|
+
continue;
|
|
7456
|
+
seen.add(line);
|
|
7457
|
+
const colon = line.indexOf(":");
|
|
7458
|
+
const file = colon > 0 ? line.slice(0, colon) : line;
|
|
7459
|
+
const count = perFile.get(file) ?? 0;
|
|
7460
|
+
if (count < SEARCH_MAX_PER_FILE) {
|
|
7461
|
+
perFile.set(file, count + 1);
|
|
7462
|
+
out.push(line);
|
|
7463
|
+
} else {
|
|
7464
|
+
omittedByFile.set(file, (omittedByFile.get(file) ?? 0) + 1);
|
|
7465
|
+
}
|
|
7466
|
+
}
|
|
7467
|
+
let totalOmitted = 0;
|
|
7468
|
+
for (const [file, n] of omittedByFile) {
|
|
7469
|
+
totalOmitted += n;
|
|
7470
|
+
out.push(` … +${n} more match(es) in ${file} — mink retrieve …`);
|
|
7471
|
+
}
|
|
7472
|
+
const dedupRemoved = lines.length - seen.size;
|
|
7473
|
+
if (totalOmitted === 0 && dedupRemoved === 0)
|
|
7474
|
+
return null;
|
|
7475
|
+
const notes = [];
|
|
7476
|
+
if (totalOmitted > 0)
|
|
7477
|
+
notes.push(`${totalOmitted} match(es) capped`);
|
|
7478
|
+
if (dedupRemoved > 0)
|
|
7479
|
+
notes.push(`${dedupRemoved} duplicate(s) removed`);
|
|
7480
|
+
return { compressed: out.join(`
|
|
7481
|
+
`), omittedNote: notes.join("; ") };
|
|
7482
|
+
}
|
|
7483
|
+
function compressFile(filePath, content) {
|
|
7484
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
7485
|
+
const markdown = ext === ".md" || ext === ".mdx" || ext === ".markdown";
|
|
7486
|
+
const skeleton = extractCodeSkeleton(content, { markdown });
|
|
7487
|
+
if (!skeleton) {
|
|
7488
|
+
return compressText(content);
|
|
7489
|
+
}
|
|
7490
|
+
const header = `${filePath} — structural summary ` + `(${skeleton.lines.length} signature(s) of ${skeleton.totalLines} lines)`;
|
|
7491
|
+
return {
|
|
7492
|
+
compressed: [header, ...skeleton.lines].join(`
|
|
7493
|
+
`),
|
|
7494
|
+
omittedNote: `bodies elided; ${skeleton.totalLines} lines available via mink retrieve`
|
|
7495
|
+
};
|
|
7496
|
+
}
|
|
7497
|
+
function crush(value) {
|
|
7498
|
+
if (Array.isArray(value)) {
|
|
7499
|
+
let omitted = 0;
|
|
7500
|
+
const mapEl = (el) => {
|
|
7501
|
+
const r = crush(el);
|
|
7502
|
+
omitted += r.omitted;
|
|
7503
|
+
return r.value;
|
|
7504
|
+
};
|
|
7505
|
+
if (value.length <= JSON_ARRAY_HEAD + JSON_ARRAY_TAIL) {
|
|
7506
|
+
return { value: value.map(mapEl), omitted };
|
|
7507
|
+
}
|
|
7508
|
+
const dropped = value.length - JSON_ARRAY_HEAD - JSON_ARRAY_TAIL;
|
|
7509
|
+
omitted += dropped;
|
|
7510
|
+
const out = [
|
|
7511
|
+
...value.slice(0, JSON_ARRAY_HEAD).map(mapEl),
|
|
7512
|
+
`… ${dropped} element(s) omitted — mink retrieve …`,
|
|
7513
|
+
...value.slice(value.length - JSON_ARRAY_TAIL).map(mapEl)
|
|
7514
|
+
];
|
|
7515
|
+
return { value: out, omitted };
|
|
7516
|
+
}
|
|
7517
|
+
if (value && typeof value === "object") {
|
|
7518
|
+
let omitted = 0;
|
|
7519
|
+
const out = {};
|
|
7520
|
+
for (const [k, v] of Object.entries(value)) {
|
|
7521
|
+
const r = crush(v);
|
|
7522
|
+
omitted += r.omitted;
|
|
7523
|
+
out[k] = r.value;
|
|
7524
|
+
}
|
|
7525
|
+
return { value: out, omitted };
|
|
7526
|
+
}
|
|
7527
|
+
return { value, omitted: 0 };
|
|
7528
|
+
}
|
|
7529
|
+
function compressJson(content) {
|
|
7530
|
+
let parsed;
|
|
7531
|
+
try {
|
|
7532
|
+
parsed = JSON.parse(content);
|
|
7533
|
+
} catch {
|
|
7534
|
+
return null;
|
|
7535
|
+
}
|
|
7536
|
+
const { value, omitted } = crush(parsed);
|
|
7537
|
+
if (omitted === 0)
|
|
7538
|
+
return null;
|
|
7539
|
+
return {
|
|
7540
|
+
compressed: JSON.stringify(value, null, 2),
|
|
7541
|
+
omittedNote: `${omitted} array element(s) sampled out`
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
function compressText(content) {
|
|
7545
|
+
const lines = toLines(content);
|
|
7546
|
+
if (lines.length <= TEXT_HEAD + TEXT_TAIL)
|
|
7547
|
+
return null;
|
|
7548
|
+
const omitted = lines.length - TEXT_HEAD - TEXT_TAIL;
|
|
7549
|
+
const head = lines.slice(0, TEXT_HEAD);
|
|
7550
|
+
const tail = lines.slice(lines.length - TEXT_TAIL);
|
|
7551
|
+
return {
|
|
7552
|
+
compressed: [...head, omittedMarker(omitted), ...tail].join(`
|
|
7553
|
+
`),
|
|
7554
|
+
omittedNote: `${omitted} of ${lines.length} line(s) omitted (middle)`
|
|
7555
|
+
};
|
|
7556
|
+
}
|
|
7557
|
+
function detectContentKind(toolName, content, filePath) {
|
|
7558
|
+
const t = toolName.toLowerCase();
|
|
7559
|
+
if (t === "read")
|
|
7560
|
+
return "file";
|
|
7561
|
+
if (t === "grep" || t === "glob")
|
|
7562
|
+
return "search";
|
|
7563
|
+
if (t === "bash")
|
|
7564
|
+
return "log";
|
|
7565
|
+
const head = content.trimStart()[0];
|
|
7566
|
+
if (head === "{" || head === "[") {
|
|
7567
|
+
try {
|
|
7568
|
+
JSON.parse(content);
|
|
7569
|
+
return "json";
|
|
7570
|
+
} catch {}
|
|
7571
|
+
}
|
|
7572
|
+
if (filePath)
|
|
7573
|
+
return "file";
|
|
7574
|
+
return "text";
|
|
7575
|
+
}
|
|
7576
|
+
function compressOutput(toolName, content, filePath) {
|
|
7577
|
+
const kind = detectContentKind(toolName, content, filePath);
|
|
7578
|
+
let result;
|
|
7579
|
+
switch (kind) {
|
|
7580
|
+
case "search":
|
|
7581
|
+
result = compressSearch(content);
|
|
7582
|
+
break;
|
|
7583
|
+
case "log":
|
|
7584
|
+
result = compressLog(content);
|
|
7585
|
+
break;
|
|
7586
|
+
case "file":
|
|
7587
|
+
result = compressFile(filePath ?? "file", content);
|
|
7588
|
+
break;
|
|
7589
|
+
case "json":
|
|
7590
|
+
result = compressJson(content);
|
|
7591
|
+
break;
|
|
7592
|
+
case "text":
|
|
7593
|
+
result = compressText(content);
|
|
7594
|
+
break;
|
|
7595
|
+
}
|
|
7596
|
+
if (!result)
|
|
7597
|
+
return null;
|
|
7598
|
+
return { kind, compressed: result.compressed, omittedNote: result.omittedNote };
|
|
7599
|
+
}
|
|
7600
|
+
var SEARCH_MAX_PER_FILE = 5, LOG_HEAD = 40, LOG_TAIL = 40, TEXT_HEAD = 30, TEXT_TAIL = 20, JSON_ARRAY_HEAD = 20, JSON_ARRAY_TAIL = 5, ANSI;
|
|
7601
|
+
var init_output_compression = __esm(() => {
|
|
7602
|
+
init_code_skeleton();
|
|
7603
|
+
ANSI = /\[[0-9;?]*[ -/]*[@-~]/g;
|
|
7604
|
+
});
|
|
7605
|
+
|
|
7606
|
+
// src/repositories/compression-cache-repo.ts
|
|
7607
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7608
|
+
|
|
7609
|
+
class CompressionCacheRepo {
|
|
7610
|
+
db;
|
|
7611
|
+
constructor(db) {
|
|
7612
|
+
this.db = db;
|
|
7613
|
+
}
|
|
7614
|
+
static for(cwd) {
|
|
7615
|
+
return new CompressionCacheRepo(openProjectDb(cwd));
|
|
7616
|
+
}
|
|
7617
|
+
static newToken() {
|
|
7618
|
+
return `mc-${randomUUID3().slice(0, 8)}`;
|
|
7619
|
+
}
|
|
7620
|
+
store(input, deviceId = getOrCreateDeviceId()) {
|
|
7621
|
+
const token = input.token ?? CompressionCacheRepo.newToken();
|
|
7622
|
+
const now = input.now ?? new Date;
|
|
7623
|
+
const createdAt = now.toISOString();
|
|
7624
|
+
const expiresAt = new Date(now.getTime() + Math.max(0, input.retentionHours) * 3600000).toISOString();
|
|
7625
|
+
this.db.prepare(`
|
|
7626
|
+
INSERT OR REPLACE INTO compression_cache
|
|
7627
|
+
(token, created_at, expires_at, tool_name, content_kind,
|
|
7628
|
+
content, size_bytes, device_id)
|
|
7629
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
7630
|
+
`).run(token, createdAt, expiresAt, input.toolName, input.contentKind, input.content, Buffer.byteLength(input.content, "utf-8"), deviceId);
|
|
7631
|
+
return token;
|
|
7632
|
+
}
|
|
7633
|
+
get(token, now = new Date) {
|
|
7634
|
+
const row = this.db.prepare("SELECT * FROM compression_cache WHERE token = ?").get(token);
|
|
7635
|
+
if (!row)
|
|
7636
|
+
return null;
|
|
7637
|
+
const expiresAt = String(row.expires_at);
|
|
7638
|
+
if (expiresAt <= now.toISOString()) {
|
|
7639
|
+
try {
|
|
7640
|
+
this.db.prepare("DELETE FROM compression_cache WHERE token = ?").run(token);
|
|
7641
|
+
} catch {}
|
|
7642
|
+
return null;
|
|
7643
|
+
}
|
|
7644
|
+
return {
|
|
7645
|
+
token: String(row.token),
|
|
7646
|
+
createdAt: String(row.created_at),
|
|
7647
|
+
expiresAt,
|
|
7648
|
+
toolName: String(row.tool_name),
|
|
7649
|
+
contentKind: String(row.content_kind),
|
|
7650
|
+
content: String(row.content),
|
|
7651
|
+
sizeBytes: Number(row.size_bytes)
|
|
7652
|
+
};
|
|
7653
|
+
}
|
|
7654
|
+
evictExpired(now = new Date) {
|
|
7655
|
+
const r = this.db.prepare("DELETE FROM compression_cache WHERE expires_at <= ?").run(now.toISOString());
|
|
7656
|
+
return Number(r.changes);
|
|
7657
|
+
}
|
|
7658
|
+
count() {
|
|
7659
|
+
const row = this.db.prepare("SELECT COUNT(*) AS n FROM compression_cache").get();
|
|
7660
|
+
return Number(row.n);
|
|
7661
|
+
}
|
|
7662
|
+
}
|
|
7663
|
+
var init_compression_cache_repo = __esm(() => {
|
|
7664
|
+
init_db();
|
|
7665
|
+
init_device();
|
|
7666
|
+
});
|
|
7667
|
+
|
|
7668
|
+
// src/core/compress-tool-output.ts
|
|
7669
|
+
function contentKey(s) {
|
|
7670
|
+
let h = 2166136261;
|
|
7671
|
+
for (let i = 0;i < s.length; i++) {
|
|
7672
|
+
h ^= s.charCodeAt(i);
|
|
7673
|
+
h = Math.imul(h, 16777619);
|
|
7674
|
+
}
|
|
7675
|
+
return (h >>> 0).toString(16);
|
|
7676
|
+
}
|
|
7677
|
+
function render(result, token) {
|
|
7678
|
+
return result.compressed + `
|
|
7679
|
+
|
|
7680
|
+
` + `— mink: compressed ${result.kind} output (${result.omittedNote}). ` + `Full original: mink retrieve ${token}`;
|
|
7681
|
+
}
|
|
7682
|
+
function safeRecord(cwd, toolName, contentKind, originalTokens, compressedTokens, holdout) {
|
|
7683
|
+
try {
|
|
7684
|
+
TokenLedgerRepo.for(cwd).recordCompression({
|
|
7685
|
+
toolName,
|
|
7686
|
+
contentKind,
|
|
7687
|
+
originalTokens,
|
|
7688
|
+
compressedTokens,
|
|
7689
|
+
holdout
|
|
7690
|
+
});
|
|
7691
|
+
} catch {}
|
|
7692
|
+
}
|
|
7693
|
+
function compressToolOutput(cwd, toolName, output, filePath) {
|
|
7694
|
+
let cfg;
|
|
7695
|
+
try {
|
|
7696
|
+
cfg = loadCompressionConfig();
|
|
7697
|
+
} catch {
|
|
7698
|
+
return null;
|
|
7699
|
+
}
|
|
7700
|
+
if (!cfg.enabled)
|
|
7701
|
+
return null;
|
|
7702
|
+
if (typeof output !== "string" || output.length === 0)
|
|
7703
|
+
return null;
|
|
7704
|
+
const originalTokens = countTokens(output);
|
|
7705
|
+
if (!isEligible(originalTokens, cfg))
|
|
7706
|
+
return null;
|
|
7707
|
+
const eventKey = contentKey(output);
|
|
7708
|
+
if (selectHoldout(eventKey, cfg.holdoutFraction)) {
|
|
7709
|
+
const kind = detectContentKind(toolName, output, filePath);
|
|
7710
|
+
safeRecord(cwd, toolName, kind, originalTokens, originalTokens, true);
|
|
7711
|
+
return null;
|
|
7712
|
+
}
|
|
7713
|
+
const result = compressOutput(toolName, output, filePath);
|
|
7714
|
+
if (!result)
|
|
7715
|
+
return null;
|
|
7716
|
+
const token = CompressionCacheRepo.newToken();
|
|
7717
|
+
const replacement = render(result, token);
|
|
7718
|
+
const compressedTokens = countTokens(replacement);
|
|
7719
|
+
if (!meetsMinSavings(originalTokens, compressedTokens, cfg))
|
|
7720
|
+
return null;
|
|
7721
|
+
try {
|
|
7722
|
+
CompressionCacheRepo.for(cwd).store({
|
|
7723
|
+
toolName,
|
|
7724
|
+
contentKind: result.kind,
|
|
7725
|
+
content: output,
|
|
7726
|
+
retentionHours: cfg.retentionHours,
|
|
7727
|
+
token
|
|
7728
|
+
});
|
|
7729
|
+
} catch {
|
|
7730
|
+
return null;
|
|
7731
|
+
}
|
|
7732
|
+
safeRecord(cwd, toolName, result.kind, originalTokens, compressedTokens, false);
|
|
7733
|
+
return { updatedToolOutput: replacement, token };
|
|
7734
|
+
}
|
|
7735
|
+
var init_compress_tool_output = __esm(() => {
|
|
7736
|
+
init_compression();
|
|
7737
|
+
init_token_estimate();
|
|
7738
|
+
init_output_compression();
|
|
7739
|
+
init_compression_cache_repo();
|
|
7740
|
+
init_token_ledger_repo();
|
|
7741
|
+
});
|
|
7742
|
+
|
|
7743
|
+
// src/core/hook-output.ts
|
|
7744
|
+
function extractToolOutputText(input) {
|
|
7745
|
+
const tr = input.tool_response;
|
|
7746
|
+
if (tr) {
|
|
7747
|
+
if (typeof tr.content === "string")
|
|
7748
|
+
return tr.content;
|
|
7749
|
+
if (Array.isArray(tr.content)) {
|
|
7750
|
+
const parts = tr.content.map((p) => p && typeof p.text === "string" ? p.text : "").filter((s) => s.length > 0);
|
|
7751
|
+
if (parts.length > 0)
|
|
7752
|
+
return parts.join("");
|
|
7753
|
+
}
|
|
7754
|
+
if (typeof tr.stdout === "string" && tr.stdout.length > 0)
|
|
7755
|
+
return tr.stdout;
|
|
7756
|
+
if (typeof tr.text === "string")
|
|
7757
|
+
return tr.text;
|
|
7758
|
+
const file = tr.file;
|
|
7759
|
+
if (file && typeof file.content === "string")
|
|
7760
|
+
return file.content;
|
|
7761
|
+
}
|
|
7762
|
+
const to = input.tool_output;
|
|
7763
|
+
if (to && typeof to.content === "string")
|
|
7764
|
+
return to.content;
|
|
7765
|
+
return null;
|
|
7766
|
+
}
|
|
7767
|
+
function emitUpdatedToolOutput(text) {
|
|
7768
|
+
process.stdout.write(JSON.stringify({
|
|
7769
|
+
hookSpecificOutput: {
|
|
7770
|
+
hookEventName: "PostToolUse",
|
|
7771
|
+
updatedToolOutput: text
|
|
7772
|
+
}
|
|
7773
|
+
}));
|
|
7774
|
+
}
|
|
7775
|
+
|
|
7124
7776
|
// src/commands/post-read.ts
|
|
7125
7777
|
var exports_post_read = {};
|
|
7126
7778
|
__export(exports_post_read, {
|
|
@@ -7250,6 +7902,14 @@ async function postRead(cwd) {
|
|
|
7250
7902
|
logWriter.appendReadEntry(new Date().toISOString(), filePath, result.indexHit, result.estimatedTokens);
|
|
7251
7903
|
} catch {}
|
|
7252
7904
|
atomicWriteJson(sessionPath(cwd), state);
|
|
7905
|
+
const isRanged = input.tool_input.offset != null || input.tool_input.limit != null;
|
|
7906
|
+
if (!isRanged && content && content.length > 0) {
|
|
7907
|
+
try {
|
|
7908
|
+
const outcome = compressToolOutput(cwd, "Read", content, filePath);
|
|
7909
|
+
if (outcome)
|
|
7910
|
+
emitUpdatedToolOutput(outcome.updatedToolOutput);
|
|
7911
|
+
} catch {}
|
|
7912
|
+
}
|
|
7253
7913
|
} catch {} finally {
|
|
7254
7914
|
clearTimeout(timer);
|
|
7255
7915
|
}
|
|
@@ -7263,6 +7923,43 @@ var init_post_read = __esm(() => {
|
|
|
7263
7923
|
init_description();
|
|
7264
7924
|
init_action_log();
|
|
7265
7925
|
init_device();
|
|
7926
|
+
init_compress_tool_output();
|
|
7927
|
+
});
|
|
7928
|
+
|
|
7929
|
+
// src/commands/post-tool.ts
|
|
7930
|
+
var exports_post_tool = {};
|
|
7931
|
+
__export(exports_post_tool, {
|
|
7932
|
+
postTool: () => postTool
|
|
7933
|
+
});
|
|
7934
|
+
function isPostToolUseInput2(value) {
|
|
7935
|
+
if (value === null || typeof value !== "object")
|
|
7936
|
+
return false;
|
|
7937
|
+
const obj = value;
|
|
7938
|
+
return typeof obj.tool_name === "string";
|
|
7939
|
+
}
|
|
7940
|
+
function isCompressibleTool(toolName) {
|
|
7941
|
+
return toolName === "Bash" || toolName === "Grep" || toolName === "Glob" || toolName.startsWith("mcp__");
|
|
7942
|
+
}
|
|
7943
|
+
async function postTool(cwd) {
|
|
7944
|
+
const timer = setTimeout(() => process.exit(0), 5000);
|
|
7945
|
+
try {
|
|
7946
|
+
const input = await readStdinJson();
|
|
7947
|
+
if (!isPostToolUseInput2(input))
|
|
7948
|
+
return;
|
|
7949
|
+
if (!isCompressibleTool(input.tool_name))
|
|
7950
|
+
return;
|
|
7951
|
+
const output = extractToolOutputText(input);
|
|
7952
|
+
if (!output)
|
|
7953
|
+
return;
|
|
7954
|
+
const outcome = compressToolOutput(cwd, input.tool_name, output);
|
|
7955
|
+
if (outcome)
|
|
7956
|
+
emitUpdatedToolOutput(outcome.updatedToolOutput);
|
|
7957
|
+
} catch {} finally {
|
|
7958
|
+
clearTimeout(timer);
|
|
7959
|
+
}
|
|
7960
|
+
}
|
|
7961
|
+
var init_post_tool = __esm(() => {
|
|
7962
|
+
init_compress_tool_output();
|
|
7266
7963
|
});
|
|
7267
7964
|
|
|
7268
7965
|
// src/core/pattern-engine.ts
|
|
@@ -7501,7 +8198,7 @@ function analyzePostWrite(filePath, fileContent, index) {
|
|
|
7501
8198
|
indexEntry
|
|
7502
8199
|
};
|
|
7503
8200
|
}
|
|
7504
|
-
function
|
|
8201
|
+
function isPostToolUseInput3(value) {
|
|
7505
8202
|
if (value === null || typeof value !== "object")
|
|
7506
8203
|
return false;
|
|
7507
8204
|
const obj = value;
|
|
@@ -7515,7 +8212,7 @@ async function postWrite(cwd) {
|
|
|
7515
8212
|
const timer = setTimeout(() => process.exit(0), 1e4);
|
|
7516
8213
|
try {
|
|
7517
8214
|
const input = await readStdinJson();
|
|
7518
|
-
if (!
|
|
8215
|
+
if (!isPostToolUseInput3(input))
|
|
7519
8216
|
return;
|
|
7520
8217
|
if (input.tool_name !== "Write" && input.tool_name !== "Edit")
|
|
7521
8218
|
return;
|
|
@@ -7744,6 +8441,35 @@ var init_detect_waste = __esm(() => {
|
|
|
7744
8441
|
init_device();
|
|
7745
8442
|
});
|
|
7746
8443
|
|
|
8444
|
+
// src/commands/retrieve.ts
|
|
8445
|
+
var exports_retrieve = {};
|
|
8446
|
+
__export(exports_retrieve, {
|
|
8447
|
+
retrieve: () => retrieve
|
|
8448
|
+
});
|
|
8449
|
+
function retrieve(cwd, args) {
|
|
8450
|
+
const token = args[0];
|
|
8451
|
+
if (!token) {
|
|
8452
|
+
process.stderr.write(`[mink] usage: mink retrieve <token>
|
|
8453
|
+
`);
|
|
8454
|
+
return;
|
|
8455
|
+
}
|
|
8456
|
+
let entry = null;
|
|
8457
|
+
try {
|
|
8458
|
+
entry = CompressionCacheRepo.for(cwd).get(token);
|
|
8459
|
+
} catch {
|
|
8460
|
+
entry = null;
|
|
8461
|
+
}
|
|
8462
|
+
if (!entry) {
|
|
8463
|
+
process.stderr.write(`[mink] no retrievable output for token "${token}" (unknown or expired)
|
|
8464
|
+
`);
|
|
8465
|
+
return;
|
|
8466
|
+
}
|
|
8467
|
+
process.stdout.write(entry.content);
|
|
8468
|
+
}
|
|
8469
|
+
var init_retrieve = __esm(() => {
|
|
8470
|
+
init_compression_cache_repo();
|
|
8471
|
+
});
|
|
8472
|
+
|
|
7747
8473
|
// src/core/cron-parser.ts
|
|
7748
8474
|
function parseField(field, min, max) {
|
|
7749
8475
|
const values = new Set;
|
|
@@ -10654,12 +11380,14 @@ function buildHooksConfig2(cliPath) {
|
|
|
10654
11380
|
PostToolUse: [
|
|
10655
11381
|
{ matcher: "Read", hooks: hook(`${prefix} post-read`) },
|
|
10656
11382
|
{ matcher: "Edit", hooks: hook(`${prefix} post-write`) },
|
|
10657
|
-
{ matcher: "Write", hooks: hook(`${prefix} post-write`) }
|
|
11383
|
+
{ matcher: "Write", hooks: hook(`${prefix} post-write`) },
|
|
11384
|
+
{ matcher: "Bash", hooks: hook(`${prefix} post-tool`) },
|
|
11385
|
+
{ matcher: "Grep", hooks: hook(`${prefix} post-tool`) }
|
|
10658
11386
|
]
|
|
10659
11387
|
};
|
|
10660
11388
|
}
|
|
10661
11389
|
function isMinkCommand2(cmd) {
|
|
10662
|
-
const hasMinkSubcommand = cmd.includes("session-start") || cmd.includes("session-stop") || cmd.includes("pre-read") || cmd.includes("post-read") || cmd.includes("pre-write") || cmd.includes("post-write");
|
|
11390
|
+
const hasMinkSubcommand = cmd.includes("session-start") || cmd.includes("session-stop") || cmd.includes("pre-read") || cmd.includes("post-read") || cmd.includes("pre-write") || cmd.includes("post-write") || cmd.includes("post-tool");
|
|
10663
11391
|
if (!hasMinkSubcommand)
|
|
10664
11392
|
return false;
|
|
10665
11393
|
if (/(^|\/|\s)mink\s/.test(cmd))
|
|
@@ -77088,7 +77816,7 @@ var require_dist10 = __commonJS((exports) => {
|
|
|
77088
77816
|
exports.PacProxyAgent = undefined;
|
|
77089
77817
|
var net = __importStar(__require("net"));
|
|
77090
77818
|
var tls = __importStar(__require("tls"));
|
|
77091
|
-
var
|
|
77819
|
+
var crypto2 = __importStar(__require("crypto"));
|
|
77092
77820
|
var events_1 = __require("events");
|
|
77093
77821
|
var debug_1 = __importDefault(require_src());
|
|
77094
77822
|
var url_1 = __require("url");
|
|
@@ -77138,7 +77866,7 @@ var require_dist10 = __commonJS((exports) => {
|
|
|
77138
77866
|
(0, quickjs_emscripten_1.getQuickJS)(),
|
|
77139
77867
|
this.loadPacFile()
|
|
77140
77868
|
]);
|
|
77141
|
-
const hash =
|
|
77869
|
+
const hash = crypto2.createHash("sha1").update(code).digest("hex");
|
|
77142
77870
|
if (this.resolver && this.resolverHash === hash) {
|
|
77143
77871
|
debug2("Same sha1 hash for code - contents have not changed, reusing previous proxy resolver");
|
|
77144
77872
|
return this.resolver;
|
|
@@ -84409,12 +85137,12 @@ var init_lib = __esm(() => {
|
|
|
84409
85137
|
});
|
|
84410
85138
|
|
|
84411
85139
|
// node_modules/cliui/build/lib/string-utils.js
|
|
84412
|
-
function
|
|
85140
|
+
function stripAnsi2(str) {
|
|
84413
85141
|
return str.replace(ansi, "");
|
|
84414
85142
|
}
|
|
84415
85143
|
function wrap(str, width) {
|
|
84416
85144
|
const [start, end] = str.match(ansi) || ["", ""];
|
|
84417
|
-
str =
|
|
85145
|
+
str = stripAnsi2(str);
|
|
84418
85146
|
let wrapped = "";
|
|
84419
85147
|
for (let i = 0;i < str.length; i++) {
|
|
84420
85148
|
if (i !== 0 && i % width === 0) {
|
|
@@ -84439,7 +85167,7 @@ function ui(opts) {
|
|
|
84439
85167
|
stringWidth: (str) => {
|
|
84440
85168
|
return [...str].length;
|
|
84441
85169
|
},
|
|
84442
|
-
stripAnsi,
|
|
85170
|
+
stripAnsi: stripAnsi2,
|
|
84443
85171
|
wrap
|
|
84444
85172
|
});
|
|
84445
85173
|
}
|
|
@@ -93860,6 +94588,11 @@ switch (command2) {
|
|
|
93860
94588
|
await postRead2(cwd);
|
|
93861
94589
|
break;
|
|
93862
94590
|
}
|
|
94591
|
+
case "post-tool": {
|
|
94592
|
+
const { postTool: postTool2 } = await Promise.resolve().then(() => (init_post_tool(), exports_post_tool));
|
|
94593
|
+
await postTool2(cwd);
|
|
94594
|
+
break;
|
|
94595
|
+
}
|
|
93863
94596
|
case "pre-write": {
|
|
93864
94597
|
const { preWrite: preWrite2 } = await Promise.resolve().then(() => (init_pre_write(), exports_pre_write));
|
|
93865
94598
|
await preWrite2(cwd);
|
|
@@ -93875,6 +94608,11 @@ switch (command2) {
|
|
|
93875
94608
|
detectWaste3(cwd);
|
|
93876
94609
|
break;
|
|
93877
94610
|
}
|
|
94611
|
+
case "retrieve": {
|
|
94612
|
+
const { retrieve: retrieve2 } = await Promise.resolve().then(() => (init_retrieve(), exports_retrieve));
|
|
94613
|
+
retrieve2(cwd, process.argv.slice(3));
|
|
94614
|
+
break;
|
|
94615
|
+
}
|
|
93878
94616
|
case "cron": {
|
|
93879
94617
|
const { cron: cron2 } = await Promise.resolve().then(() => (init_cron(), exports_cron));
|
|
93880
94618
|
await cron2(cwd, process.argv.slice(3));
|
|
@@ -94044,6 +94782,7 @@ switch (command2) {
|
|
|
94044
94782
|
console.log(" restore [backup] Restore state from a backup");
|
|
94045
94783
|
console.log(" bug search <term> Search the bug log");
|
|
94046
94784
|
console.log(" detect-waste Detect and flag wasteful patterns");
|
|
94785
|
+
console.log(" retrieve <token> Return a compressed tool output's original (spec 21)");
|
|
94047
94786
|
console.log(" reflect Generate learning memory reflections");
|
|
94048
94787
|
console.log(" designqc [target] Capture design screenshots (spec 13)");
|
|
94049
94788
|
console.log(" framework-advisor Generate framework advisor knowledge file (spec 14)");
|
|
@@ -94053,6 +94792,7 @@ switch (command2) {
|
|
|
94053
94792
|
console.log(" session-stop Finalize session and log data");
|
|
94054
94793
|
console.log(" pre-read / post-read File read hooks");
|
|
94055
94794
|
console.log(" pre-write / post-write File write hooks");
|
|
94795
|
+
console.log(" post-tool Tool-output compression hook (Bash/Grep/MCP, spec 21)");
|
|
94056
94796
|
break;
|
|
94057
94797
|
default:
|
|
94058
94798
|
console.error(`[mink] unknown command: ${command2 ?? "(none)"}`);
|