@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.bun.js
CHANGED
|
@@ -421,6 +421,41 @@ var init_config = __esm(() => {
|
|
|
421
421
|
envVar: "MINK_PROJECTS_IDENTITY",
|
|
422
422
|
description: "Project identity strategy: path-derived (legacy) or git-remote (stable across machines)",
|
|
423
423
|
scope: "shared"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
key: "compression.enabled",
|
|
427
|
+
default: "false",
|
|
428
|
+
envVar: "MINK_COMPRESSION_ENABLED",
|
|
429
|
+
description: "Enable tool-output compression (spec 21). Off until inline compression ships.",
|
|
430
|
+
scope: "shared"
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
key: "compression.threshold-tokens",
|
|
434
|
+
default: "800",
|
|
435
|
+
envVar: "MINK_COMPRESSION_THRESHOLD_TOKENS",
|
|
436
|
+
description: "Minimum estimated token size before a tool output is eligible for compression",
|
|
437
|
+
scope: "shared"
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
key: "compression.min-savings-ratio",
|
|
441
|
+
default: "0.25",
|
|
442
|
+
envVar: "MINK_COMPRESSION_MIN_SAVINGS_RATIO",
|
|
443
|
+
description: "Discard a compression attempt unless it saves at least this fraction of tokens",
|
|
444
|
+
scope: "shared"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
key: "compression.holdout-fraction",
|
|
448
|
+
default: "0.1",
|
|
449
|
+
envVar: "MINK_COMPRESSION_HOLDOUT_FRACTION",
|
|
450
|
+
description: "Fraction of eligible outputs left uncompressed as a measured control group",
|
|
451
|
+
scope: "shared"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
key: "compression.retention-hours",
|
|
455
|
+
default: "168",
|
|
456
|
+
envVar: "MINK_COMPRESSION_RETENTION_HOURS",
|
|
457
|
+
description: "How long compressed originals stay retrievable before eviction",
|
|
458
|
+
scope: "shared"
|
|
424
459
|
}
|
|
425
460
|
];
|
|
426
461
|
VALID_KEYS = new Set(CONFIG_KEYS.map((k) => k.key));
|
|
@@ -3095,7 +3130,7 @@ function readMeta(db, key) {
|
|
|
3095
3130
|
function writeMeta(db, key, value) {
|
|
3096
3131
|
db.prepare("INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value").run(key, value);
|
|
3097
3132
|
}
|
|
3098
|
-
var SCHEMA_VERSION =
|
|
3133
|
+
var SCHEMA_VERSION = 3, INITIAL_SCHEMA = `
|
|
3099
3134
|
CREATE TABLE IF NOT EXISTS meta (
|
|
3100
3135
|
key TEXT PRIMARY KEY,
|
|
3101
3136
|
value TEXT NOT NULL
|
|
@@ -3257,6 +3292,55 @@ CREATE TABLE IF NOT EXISTS counters (
|
|
|
3257
3292
|
file_index_hits INTEGER NOT NULL DEFAULT 0,
|
|
3258
3293
|
file_index_misses INTEGER NOT NULL DEFAULT 0
|
|
3259
3294
|
);
|
|
3295
|
+
|
|
3296
|
+
-- Tool-output compression measurement (spec 21). One row per compression
|
|
3297
|
+
-- decision: either a compressed arm (compressed_tokens < original_tokens) or a
|
|
3298
|
+
-- holdout arm (left uncompressed for control, compressed_tokens = original_tokens).
|
|
3299
|
+
-- These are append-only telemetry, independent of session lifecycle, written at
|
|
3300
|
+
-- the moment a tool output is processed. New table \u2192 applied to existing DBs via
|
|
3301
|
+
-- IF NOT EXISTS on the next open.
|
|
3302
|
+
CREATE TABLE IF NOT EXISTS ledger_compressions (
|
|
3303
|
+
id TEXT PRIMARY KEY,
|
|
3304
|
+
created_at TEXT NOT NULL,
|
|
3305
|
+
tool_name TEXT NOT NULL,
|
|
3306
|
+
content_kind TEXT NOT NULL,
|
|
3307
|
+
original_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3308
|
+
compressed_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3309
|
+
holdout INTEGER NOT NULL DEFAULT 0,
|
|
3310
|
+
device_id TEXT NOT NULL
|
|
3311
|
+
);
|
|
3312
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_compressions_created ON ledger_compressions(created_at);
|
|
3313
|
+
CREATE INDEX IF NOT EXISTS idx_ledger_compressions_device ON ledger_compressions(device_id);
|
|
3314
|
+
|
|
3315
|
+
-- Per-device compression aggregates, summed across devices like ledger_lifetime.
|
|
3316
|
+
-- measured_savings only credits compressed arms (holdout arms save nothing by
|
|
3317
|
+
-- construction), so the reported figure is a true measured delta, not an estimate.
|
|
3318
|
+
CREATE TABLE IF NOT EXISTS ledger_compression_lifetime (
|
|
3319
|
+
device_id TEXT PRIMARY KEY,
|
|
3320
|
+
total_events INTEGER NOT NULL DEFAULT 0,
|
|
3321
|
+
total_holdout_events INTEGER NOT NULL DEFAULT 0,
|
|
3322
|
+
total_original_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3323
|
+
total_compressed_tokens INTEGER NOT NULL DEFAULT 0,
|
|
3324
|
+
total_measured_savings INTEGER NOT NULL DEFAULT 0
|
|
3325
|
+
);
|
|
3326
|
+
|
|
3327
|
+
-- Reversible-compression cache (spec 21 \xA7Reversibility). When a tool output is
|
|
3328
|
+
-- compressed, the original is stored here keyed by a short retrieval token and
|
|
3329
|
+
-- embedded in the compressed result; "mink retrieve <token>" returns it
|
|
3330
|
+
-- byte-exact. Rows expire after the configured retention window; an expired or
|
|
3331
|
+
-- unknown token is a graceful miss. This is a local cache, not synced state, so
|
|
3332
|
+
-- (unlike other tables) it carries no merge semantics beyond device_id for audit.
|
|
3333
|
+
CREATE TABLE IF NOT EXISTS compression_cache (
|
|
3334
|
+
token TEXT PRIMARY KEY,
|
|
3335
|
+
created_at TEXT NOT NULL,
|
|
3336
|
+
expires_at TEXT NOT NULL,
|
|
3337
|
+
tool_name TEXT NOT NULL,
|
|
3338
|
+
content_kind TEXT NOT NULL,
|
|
3339
|
+
content TEXT NOT NULL,
|
|
3340
|
+
size_bytes INTEGER NOT NULL,
|
|
3341
|
+
device_id TEXT NOT NULL
|
|
3342
|
+
);
|
|
3343
|
+
CREATE INDEX IF NOT EXISTS idx_compression_cache_expires ON compression_cache(expires_at);
|
|
3260
3344
|
`;
|
|
3261
3345
|
|
|
3262
3346
|
// src/storage/migrate-json.ts
|
|
@@ -3674,6 +3758,68 @@ class TokenLedgerRepo {
|
|
|
3674
3758
|
}
|
|
3675
3759
|
});
|
|
3676
3760
|
}
|
|
3761
|
+
recordCompression(event, deviceId = getOrCreateDeviceId()) {
|
|
3762
|
+
const id = event.id ?? crypto.randomUUID();
|
|
3763
|
+
const createdAt = event.createdAt ?? new Date().toISOString();
|
|
3764
|
+
const holdout = event.holdout ? 1 : 0;
|
|
3765
|
+
const savings = event.holdout ? 0 : Math.max(0, event.originalTokens - event.compressedTokens);
|
|
3766
|
+
this.db.transaction(() => {
|
|
3767
|
+
this.db.prepare(`
|
|
3768
|
+
INSERT OR REPLACE INTO ledger_compressions
|
|
3769
|
+
(id, created_at, tool_name, content_kind,
|
|
3770
|
+
original_tokens, compressed_tokens, holdout, device_id)
|
|
3771
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
3772
|
+
`).run(id, createdAt, event.toolName, event.contentKind, event.originalTokens, event.compressedTokens, holdout, deviceId);
|
|
3773
|
+
this.db.prepare(`
|
|
3774
|
+
INSERT INTO ledger_compression_lifetime
|
|
3775
|
+
(device_id, total_events, total_holdout_events,
|
|
3776
|
+
total_original_tokens, total_compressed_tokens, total_measured_savings)
|
|
3777
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
3778
|
+
ON CONFLICT(device_id) DO UPDATE SET
|
|
3779
|
+
total_events = ledger_compression_lifetime.total_events + excluded.total_events,
|
|
3780
|
+
total_holdout_events = ledger_compression_lifetime.total_holdout_events + excluded.total_holdout_events,
|
|
3781
|
+
total_original_tokens = ledger_compression_lifetime.total_original_tokens + excluded.total_original_tokens,
|
|
3782
|
+
total_compressed_tokens = ledger_compression_lifetime.total_compressed_tokens + excluded.total_compressed_tokens,
|
|
3783
|
+
total_measured_savings = ledger_compression_lifetime.total_measured_savings + excluded.total_measured_savings
|
|
3784
|
+
`).run(deviceId, 1, holdout, event.originalTokens, event.compressedTokens, savings);
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
compressionLifetime() {
|
|
3788
|
+
const row = this.db.prepare(`
|
|
3789
|
+
SELECT
|
|
3790
|
+
COALESCE(SUM(total_events), 0) AS totalEvents,
|
|
3791
|
+
COALESCE(SUM(total_holdout_events), 0) AS totalHoldoutEvents,
|
|
3792
|
+
COALESCE(SUM(total_original_tokens), 0) AS totalOriginalTokens,
|
|
3793
|
+
COALESCE(SUM(total_compressed_tokens), 0) AS totalCompressedTokens,
|
|
3794
|
+
COALESCE(SUM(total_measured_savings), 0) AS totalMeasuredSavings
|
|
3795
|
+
FROM ledger_compression_lifetime
|
|
3796
|
+
`).get();
|
|
3797
|
+
return {
|
|
3798
|
+
totalEvents: Number(row?.totalEvents ?? 0),
|
|
3799
|
+
totalHoldoutEvents: Number(row?.totalHoldoutEvents ?? 0),
|
|
3800
|
+
totalOriginalTokens: Number(row?.totalOriginalTokens ?? 0),
|
|
3801
|
+
totalCompressedTokens: Number(row?.totalCompressedTokens ?? 0),
|
|
3802
|
+
totalMeasuredSavings: Number(row?.totalMeasuredSavings ?? 0)
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
compressionEvents(limit = 100) {
|
|
3806
|
+
const rows = this.db.prepare(`
|
|
3807
|
+
SELECT id, created_at, tool_name, content_kind,
|
|
3808
|
+
original_tokens, compressed_tokens, holdout
|
|
3809
|
+
FROM ledger_compressions
|
|
3810
|
+
ORDER BY created_at DESC
|
|
3811
|
+
LIMIT ?
|
|
3812
|
+
`).all(limit);
|
|
3813
|
+
return rows.map((r) => ({
|
|
3814
|
+
id: String(r.id),
|
|
3815
|
+
createdAt: String(r.created_at),
|
|
3816
|
+
toolName: String(r.tool_name),
|
|
3817
|
+
contentKind: String(r.content_kind),
|
|
3818
|
+
originalTokens: Number(r.original_tokens),
|
|
3819
|
+
compressedTokens: Number(r.compressed_tokens),
|
|
3820
|
+
holdout: Number(r.holdout) === 1
|
|
3821
|
+
}));
|
|
3822
|
+
}
|
|
3677
3823
|
insertSessionRow(summary, deviceId, archived) {
|
|
3678
3824
|
this.db.prepare(`
|
|
3679
3825
|
INSERT OR REPLACE INTO ledger_sessions
|
|
@@ -4448,6 +4594,28 @@ function estimateTokens2(content, filePath) {
|
|
|
4448
4594
|
}
|
|
4449
4595
|
return Math.ceil(content.length / ratio);
|
|
4450
4596
|
}
|
|
4597
|
+
function countTokens(text) {
|
|
4598
|
+
if (!text)
|
|
4599
|
+
return 0;
|
|
4600
|
+
const segments = text.match(/[A-Za-z]+|[0-9]+|[^A-Za-z0-9]/g);
|
|
4601
|
+
if (!segments)
|
|
4602
|
+
return 0;
|
|
4603
|
+
let tokens = 0;
|
|
4604
|
+
for (const seg of segments) {
|
|
4605
|
+
const first = seg.charCodeAt(0);
|
|
4606
|
+
if (first >= 65 && first <= 90 || first >= 97 && first <= 122) {
|
|
4607
|
+
tokens += Math.ceil(seg.length / 4);
|
|
4608
|
+
} else if (first >= 48 && first <= 57) {
|
|
4609
|
+
tokens += Math.ceil(seg.length / 3);
|
|
4610
|
+
} else if (seg === `
|
|
4611
|
+
`) {
|
|
4612
|
+
tokens += 1;
|
|
4613
|
+
} else if (seg === " " || seg === "\t" || seg === "\r") {} else {
|
|
4614
|
+
tokens += 1;
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
return tokens;
|
|
4618
|
+
}
|
|
4451
4619
|
var CODE_EXTENSIONS, PROSE_EXTENSIONS, BINARY_EXTENSIONS;
|
|
4452
4620
|
var init_token_estimate = __esm(() => {
|
|
4453
4621
|
CODE_EXTENSIONS = new Set([
|
|
@@ -5643,12 +5811,14 @@ function buildHooksConfig(cliPath) {
|
|
|
5643
5811
|
PostToolUse: [
|
|
5644
5812
|
{ matcher: "Read", hooks: hook(`${prefix} post-read`) },
|
|
5645
5813
|
{ matcher: "Edit", hooks: hook(`${prefix} post-write`) },
|
|
5646
|
-
{ matcher: "Write", hooks: hook(`${prefix} post-write`) }
|
|
5814
|
+
{ matcher: "Write", hooks: hook(`${prefix} post-write`) },
|
|
5815
|
+
{ matcher: "Bash", hooks: hook(`${prefix} post-tool`) },
|
|
5816
|
+
{ matcher: "Grep", hooks: hook(`${prefix} post-tool`) }
|
|
5647
5817
|
]
|
|
5648
5818
|
};
|
|
5649
5819
|
}
|
|
5650
5820
|
function isMinkCommand(cmd) {
|
|
5651
|
-
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");
|
|
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") || cmd.includes("post-tool");
|
|
5652
5822
|
if (!hasMinkSubcommand)
|
|
5653
5823
|
return false;
|
|
5654
5824
|
if (/(^|\/|\s)mink\s/.test(cmd))
|
|
@@ -6782,6 +6952,488 @@ var init_pre_read = __esm(() => {
|
|
|
6782
6952
|
init_counters_repo();
|
|
6783
6953
|
});
|
|
6784
6954
|
|
|
6955
|
+
// src/core/compression.ts
|
|
6956
|
+
function numberValue(key, fallback, min, max) {
|
|
6957
|
+
const raw = resolveConfigValue(key).value;
|
|
6958
|
+
const n = Number(raw);
|
|
6959
|
+
if (!Number.isFinite(n))
|
|
6960
|
+
return fallback;
|
|
6961
|
+
return Math.min(max, Math.max(min, n));
|
|
6962
|
+
}
|
|
6963
|
+
function loadCompressionConfig() {
|
|
6964
|
+
return {
|
|
6965
|
+
enabled: resolveConfigValue("compression.enabled").value === "true",
|
|
6966
|
+
thresholdTokens: numberValue("compression.threshold-tokens", 800, 0, Number.MAX_SAFE_INTEGER),
|
|
6967
|
+
minSavingsRatio: numberValue("compression.min-savings-ratio", 0.25, 0, 1),
|
|
6968
|
+
holdoutFraction: numberValue("compression.holdout-fraction", 0.1, 0, 1),
|
|
6969
|
+
retentionHours: numberValue("compression.retention-hours", 168, 0, Number.MAX_SAFE_INTEGER)
|
|
6970
|
+
};
|
|
6971
|
+
}
|
|
6972
|
+
function isEligible(originalTokens, config) {
|
|
6973
|
+
return config.enabled && originalTokens >= config.thresholdTokens;
|
|
6974
|
+
}
|
|
6975
|
+
function meetsMinSavings(originalTokens, compressedTokens, config) {
|
|
6976
|
+
if (originalTokens <= 0)
|
|
6977
|
+
return false;
|
|
6978
|
+
const ratio = (originalTokens - compressedTokens) / originalTokens;
|
|
6979
|
+
return ratio >= config.minSavingsRatio;
|
|
6980
|
+
}
|
|
6981
|
+
function hashUnitInterval(key) {
|
|
6982
|
+
let h = 2166136261;
|
|
6983
|
+
for (let i = 0;i < key.length; i++) {
|
|
6984
|
+
h ^= key.charCodeAt(i);
|
|
6985
|
+
h = Math.imul(h, 16777619);
|
|
6986
|
+
}
|
|
6987
|
+
return (h >>> 0) / 4294967296;
|
|
6988
|
+
}
|
|
6989
|
+
function selectHoldout(eventKey, fraction) {
|
|
6990
|
+
if (fraction <= 0)
|
|
6991
|
+
return false;
|
|
6992
|
+
if (fraction >= 1)
|
|
6993
|
+
return true;
|
|
6994
|
+
return hashUnitInterval(eventKey) < fraction;
|
|
6995
|
+
}
|
|
6996
|
+
var init_compression = __esm(() => {
|
|
6997
|
+
init_global_config();
|
|
6998
|
+
});
|
|
6999
|
+
|
|
7000
|
+
// src/core/code-skeleton.ts
|
|
7001
|
+
function countChar(s, c) {
|
|
7002
|
+
let n = 0;
|
|
7003
|
+
for (let i = 0;i < s.length; i++)
|
|
7004
|
+
if (s[i] === c)
|
|
7005
|
+
n++;
|
|
7006
|
+
return n;
|
|
7007
|
+
}
|
|
7008
|
+
function netBraces(line) {
|
|
7009
|
+
let s = line.replace(/\/\/.*$/, "");
|
|
7010
|
+
s = s.replace(/\/\*.*?\*\//g, "");
|
|
7011
|
+
s = s.replace(/"(?:\\.|[^"\\])*"/g, '""');
|
|
7012
|
+
s = s.replace(/'(?:\\.|[^'\\])*'/g, "''");
|
|
7013
|
+
s = s.replace(/`(?:\\.|[^`\\])*`/g, "``");
|
|
7014
|
+
return countChar(s, "{") - countChar(s, "}");
|
|
7015
|
+
}
|
|
7016
|
+
function stripOpenBrace(sig) {
|
|
7017
|
+
return sig.replace(/\{\s*$/, "").trimEnd();
|
|
7018
|
+
}
|
|
7019
|
+
function extractCodeSkeleton(content, opts = {}) {
|
|
7020
|
+
const rawLines = content.split(`
|
|
7021
|
+
`);
|
|
7022
|
+
const totalLines = rawLines.length > 0 && rawLines[rawLines.length - 1] === "" ? rawLines.length - 1 : rawLines.length;
|
|
7023
|
+
const out = [];
|
|
7024
|
+
let depth = 0;
|
|
7025
|
+
let suppress = Infinity;
|
|
7026
|
+
for (const line of rawLines) {
|
|
7027
|
+
if (out.length >= MAX_SIGNATURES)
|
|
7028
|
+
break;
|
|
7029
|
+
const start = depth;
|
|
7030
|
+
const net = netBraces(line);
|
|
7031
|
+
if (start < suppress) {
|
|
7032
|
+
const isHeading = opts.markdown === true && HEADING.test(line);
|
|
7033
|
+
const captured = isHeading || DECL_ALWAYS.test(line) || DECL_EXPORTED_VAR.test(line) || start >= 1 && MEMBER.test(line);
|
|
7034
|
+
if (captured) {
|
|
7035
|
+
const sig = line.trim();
|
|
7036
|
+
if (net > 0) {
|
|
7037
|
+
if (DESCEND.test(line) && !isHeading) {
|
|
7038
|
+
out.push(INDENT.repeat(start) + stripOpenBrace(sig) + " {");
|
|
7039
|
+
} else {
|
|
7040
|
+
out.push(INDENT.repeat(start) + stripOpenBrace(sig) + " { \u2026 }");
|
|
7041
|
+
suppress = start + 1;
|
|
7042
|
+
}
|
|
7043
|
+
} else {
|
|
7044
|
+
out.push(INDENT.repeat(start) + sig);
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7047
|
+
}
|
|
7048
|
+
depth = Math.max(0, depth + net);
|
|
7049
|
+
if (depth < suppress)
|
|
7050
|
+
suppress = Infinity;
|
|
7051
|
+
}
|
|
7052
|
+
if (out.length === 0)
|
|
7053
|
+
return null;
|
|
7054
|
+
return { lines: out, totalLines };
|
|
7055
|
+
}
|
|
7056
|
+
var MAX_SIGNATURES = 80, INDENT = " ", DECL_ALWAYS, DECL_EXPORTED_VAR, MEMBER, HEADING, DESCEND;
|
|
7057
|
+
var init_code_skeleton = __esm(() => {
|
|
7058
|
+
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/;
|
|
7059
|
+
DECL_EXPORTED_VAR = /^\s*export\s+(?:default\s+)?(?:const|let|var)\b/;
|
|
7060
|
+
MEMBER = /^\s*(?:public\s+|private\s+|protected\s+|readonly\s+|static\s+|async\s+|get\s+|set\s+|#)*[\w$]+\??\s*(?:\(|:|=)/;
|
|
7061
|
+
HEADING = /^#{1,6}\s+\S/;
|
|
7062
|
+
DESCEND = /\b(?:class|interface|enum|namespace|module|struct|trait|impl)\b/;
|
|
7063
|
+
});
|
|
7064
|
+
|
|
7065
|
+
// src/core/output-compression.ts
|
|
7066
|
+
function stripAnsi(s) {
|
|
7067
|
+
return s.replace(ANSI, "");
|
|
7068
|
+
}
|
|
7069
|
+
function omittedMarker(n) {
|
|
7070
|
+
return ` \u2026 ${n} line${n === 1 ? "" : "s"} omitted \u2014 mink retrieve \u2026`;
|
|
7071
|
+
}
|
|
7072
|
+
function toLines(content) {
|
|
7073
|
+
const lines = content.split(`
|
|
7074
|
+
`);
|
|
7075
|
+
if (lines.length > 0 && lines[lines.length - 1] === "")
|
|
7076
|
+
lines.pop();
|
|
7077
|
+
return lines;
|
|
7078
|
+
}
|
|
7079
|
+
function compressLog(content) {
|
|
7080
|
+
const lines = toLines(stripAnsi(content));
|
|
7081
|
+
const collapsed = [];
|
|
7082
|
+
let i = 0;
|
|
7083
|
+
while (i < lines.length) {
|
|
7084
|
+
let run = 1;
|
|
7085
|
+
while (i + run < lines.length && lines[i + run] === lines[i])
|
|
7086
|
+
run++;
|
|
7087
|
+
collapsed.push(run > 1 ? `${lines[i]} (\xD7${run})` : lines[i]);
|
|
7088
|
+
i += run;
|
|
7089
|
+
}
|
|
7090
|
+
if (collapsed.length <= LOG_HEAD + LOG_TAIL) {
|
|
7091
|
+
if (collapsed.length === lines.length)
|
|
7092
|
+
return null;
|
|
7093
|
+
return {
|
|
7094
|
+
compressed: collapsed.join(`
|
|
7095
|
+
`),
|
|
7096
|
+
omittedNote: `collapsed ${lines.length - collapsed.length} repeated line(s)`
|
|
7097
|
+
};
|
|
7098
|
+
}
|
|
7099
|
+
const omitted = collapsed.length - LOG_HEAD - LOG_TAIL;
|
|
7100
|
+
const head = collapsed.slice(0, LOG_HEAD);
|
|
7101
|
+
const tail = collapsed.slice(collapsed.length - LOG_TAIL);
|
|
7102
|
+
return {
|
|
7103
|
+
compressed: [...head, omittedMarker(omitted), ...tail].join(`
|
|
7104
|
+
`),
|
|
7105
|
+
omittedNote: `${omitted} of ${collapsed.length} log line(s) omitted (middle)`
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
7108
|
+
function compressSearch(content) {
|
|
7109
|
+
const lines = toLines(content);
|
|
7110
|
+
const seen = new Set;
|
|
7111
|
+
const perFile = new Map;
|
|
7112
|
+
const omittedByFile = new Map;
|
|
7113
|
+
const out = [];
|
|
7114
|
+
for (const line of lines) {
|
|
7115
|
+
if (seen.has(line))
|
|
7116
|
+
continue;
|
|
7117
|
+
seen.add(line);
|
|
7118
|
+
const colon = line.indexOf(":");
|
|
7119
|
+
const file = colon > 0 ? line.slice(0, colon) : line;
|
|
7120
|
+
const count = perFile.get(file) ?? 0;
|
|
7121
|
+
if (count < SEARCH_MAX_PER_FILE) {
|
|
7122
|
+
perFile.set(file, count + 1);
|
|
7123
|
+
out.push(line);
|
|
7124
|
+
} else {
|
|
7125
|
+
omittedByFile.set(file, (omittedByFile.get(file) ?? 0) + 1);
|
|
7126
|
+
}
|
|
7127
|
+
}
|
|
7128
|
+
let totalOmitted = 0;
|
|
7129
|
+
for (const [file, n] of omittedByFile) {
|
|
7130
|
+
totalOmitted += n;
|
|
7131
|
+
out.push(` \u2026 +${n} more match(es) in ${file} \u2014 mink retrieve \u2026`);
|
|
7132
|
+
}
|
|
7133
|
+
const dedupRemoved = lines.length - seen.size;
|
|
7134
|
+
if (totalOmitted === 0 && dedupRemoved === 0)
|
|
7135
|
+
return null;
|
|
7136
|
+
const notes = [];
|
|
7137
|
+
if (totalOmitted > 0)
|
|
7138
|
+
notes.push(`${totalOmitted} match(es) capped`);
|
|
7139
|
+
if (dedupRemoved > 0)
|
|
7140
|
+
notes.push(`${dedupRemoved} duplicate(s) removed`);
|
|
7141
|
+
return { compressed: out.join(`
|
|
7142
|
+
`), omittedNote: notes.join("; ") };
|
|
7143
|
+
}
|
|
7144
|
+
function compressFile(filePath, content) {
|
|
7145
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
7146
|
+
const markdown = ext === ".md" || ext === ".mdx" || ext === ".markdown";
|
|
7147
|
+
const skeleton = extractCodeSkeleton(content, { markdown });
|
|
7148
|
+
if (!skeleton) {
|
|
7149
|
+
return compressText(content);
|
|
7150
|
+
}
|
|
7151
|
+
const header = `${filePath} \u2014 structural summary ` + `(${skeleton.lines.length} signature(s) of ${skeleton.totalLines} lines)`;
|
|
7152
|
+
return {
|
|
7153
|
+
compressed: [header, ...skeleton.lines].join(`
|
|
7154
|
+
`),
|
|
7155
|
+
omittedNote: `bodies elided; ${skeleton.totalLines} lines available via mink retrieve`
|
|
7156
|
+
};
|
|
7157
|
+
}
|
|
7158
|
+
function crush(value) {
|
|
7159
|
+
if (Array.isArray(value)) {
|
|
7160
|
+
let omitted = 0;
|
|
7161
|
+
const mapEl = (el) => {
|
|
7162
|
+
const r = crush(el);
|
|
7163
|
+
omitted += r.omitted;
|
|
7164
|
+
return r.value;
|
|
7165
|
+
};
|
|
7166
|
+
if (value.length <= JSON_ARRAY_HEAD + JSON_ARRAY_TAIL) {
|
|
7167
|
+
return { value: value.map(mapEl), omitted };
|
|
7168
|
+
}
|
|
7169
|
+
const dropped = value.length - JSON_ARRAY_HEAD - JSON_ARRAY_TAIL;
|
|
7170
|
+
omitted += dropped;
|
|
7171
|
+
const out = [
|
|
7172
|
+
...value.slice(0, JSON_ARRAY_HEAD).map(mapEl),
|
|
7173
|
+
`\u2026 ${dropped} element(s) omitted \u2014 mink retrieve \u2026`,
|
|
7174
|
+
...value.slice(value.length - JSON_ARRAY_TAIL).map(mapEl)
|
|
7175
|
+
];
|
|
7176
|
+
return { value: out, omitted };
|
|
7177
|
+
}
|
|
7178
|
+
if (value && typeof value === "object") {
|
|
7179
|
+
let omitted = 0;
|
|
7180
|
+
const out = {};
|
|
7181
|
+
for (const [k, v] of Object.entries(value)) {
|
|
7182
|
+
const r = crush(v);
|
|
7183
|
+
omitted += r.omitted;
|
|
7184
|
+
out[k] = r.value;
|
|
7185
|
+
}
|
|
7186
|
+
return { value: out, omitted };
|
|
7187
|
+
}
|
|
7188
|
+
return { value, omitted: 0 };
|
|
7189
|
+
}
|
|
7190
|
+
function compressJson(content) {
|
|
7191
|
+
let parsed;
|
|
7192
|
+
try {
|
|
7193
|
+
parsed = JSON.parse(content);
|
|
7194
|
+
} catch {
|
|
7195
|
+
return null;
|
|
7196
|
+
}
|
|
7197
|
+
const { value, omitted } = crush(parsed);
|
|
7198
|
+
if (omitted === 0)
|
|
7199
|
+
return null;
|
|
7200
|
+
return {
|
|
7201
|
+
compressed: JSON.stringify(value, null, 2),
|
|
7202
|
+
omittedNote: `${omitted} array element(s) sampled out`
|
|
7203
|
+
};
|
|
7204
|
+
}
|
|
7205
|
+
function compressText(content) {
|
|
7206
|
+
const lines = toLines(content);
|
|
7207
|
+
if (lines.length <= TEXT_HEAD + TEXT_TAIL)
|
|
7208
|
+
return null;
|
|
7209
|
+
const omitted = lines.length - TEXT_HEAD - TEXT_TAIL;
|
|
7210
|
+
const head = lines.slice(0, TEXT_HEAD);
|
|
7211
|
+
const tail = lines.slice(lines.length - TEXT_TAIL);
|
|
7212
|
+
return {
|
|
7213
|
+
compressed: [...head, omittedMarker(omitted), ...tail].join(`
|
|
7214
|
+
`),
|
|
7215
|
+
omittedNote: `${omitted} of ${lines.length} line(s) omitted (middle)`
|
|
7216
|
+
};
|
|
7217
|
+
}
|
|
7218
|
+
function detectContentKind(toolName, content, filePath) {
|
|
7219
|
+
const t = toolName.toLowerCase();
|
|
7220
|
+
if (t === "read")
|
|
7221
|
+
return "file";
|
|
7222
|
+
if (t === "grep" || t === "glob")
|
|
7223
|
+
return "search";
|
|
7224
|
+
if (t === "bash")
|
|
7225
|
+
return "log";
|
|
7226
|
+
const head = content.trimStart()[0];
|
|
7227
|
+
if (head === "{" || head === "[") {
|
|
7228
|
+
try {
|
|
7229
|
+
JSON.parse(content);
|
|
7230
|
+
return "json";
|
|
7231
|
+
} catch {}
|
|
7232
|
+
}
|
|
7233
|
+
if (filePath)
|
|
7234
|
+
return "file";
|
|
7235
|
+
return "text";
|
|
7236
|
+
}
|
|
7237
|
+
function compressOutput(toolName, content, filePath) {
|
|
7238
|
+
const kind = detectContentKind(toolName, content, filePath);
|
|
7239
|
+
let result;
|
|
7240
|
+
switch (kind) {
|
|
7241
|
+
case "search":
|
|
7242
|
+
result = compressSearch(content);
|
|
7243
|
+
break;
|
|
7244
|
+
case "log":
|
|
7245
|
+
result = compressLog(content);
|
|
7246
|
+
break;
|
|
7247
|
+
case "file":
|
|
7248
|
+
result = compressFile(filePath ?? "file", content);
|
|
7249
|
+
break;
|
|
7250
|
+
case "json":
|
|
7251
|
+
result = compressJson(content);
|
|
7252
|
+
break;
|
|
7253
|
+
case "text":
|
|
7254
|
+
result = compressText(content);
|
|
7255
|
+
break;
|
|
7256
|
+
}
|
|
7257
|
+
if (!result)
|
|
7258
|
+
return null;
|
|
7259
|
+
return { kind, compressed: result.compressed, omittedNote: result.omittedNote };
|
|
7260
|
+
}
|
|
7261
|
+
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;
|
|
7262
|
+
var init_output_compression = __esm(() => {
|
|
7263
|
+
init_code_skeleton();
|
|
7264
|
+
ANSI = /\u001B\[[0-9;?]*[ -/]*[@-~]/g;
|
|
7265
|
+
});
|
|
7266
|
+
|
|
7267
|
+
// src/repositories/compression-cache-repo.ts
|
|
7268
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
7269
|
+
|
|
7270
|
+
class CompressionCacheRepo {
|
|
7271
|
+
db;
|
|
7272
|
+
constructor(db) {
|
|
7273
|
+
this.db = db;
|
|
7274
|
+
}
|
|
7275
|
+
static for(cwd) {
|
|
7276
|
+
return new CompressionCacheRepo(openProjectDb(cwd));
|
|
7277
|
+
}
|
|
7278
|
+
static newToken() {
|
|
7279
|
+
return `mc-${randomUUID3().slice(0, 8)}`;
|
|
7280
|
+
}
|
|
7281
|
+
store(input, deviceId = getOrCreateDeviceId()) {
|
|
7282
|
+
const token = input.token ?? CompressionCacheRepo.newToken();
|
|
7283
|
+
const now = input.now ?? new Date;
|
|
7284
|
+
const createdAt = now.toISOString();
|
|
7285
|
+
const expiresAt = new Date(now.getTime() + Math.max(0, input.retentionHours) * 3600000).toISOString();
|
|
7286
|
+
this.db.prepare(`
|
|
7287
|
+
INSERT OR REPLACE INTO compression_cache
|
|
7288
|
+
(token, created_at, expires_at, tool_name, content_kind,
|
|
7289
|
+
content, size_bytes, device_id)
|
|
7290
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
7291
|
+
`).run(token, createdAt, expiresAt, input.toolName, input.contentKind, input.content, Buffer.byteLength(input.content, "utf-8"), deviceId);
|
|
7292
|
+
return token;
|
|
7293
|
+
}
|
|
7294
|
+
get(token, now = new Date) {
|
|
7295
|
+
const row = this.db.prepare("SELECT * FROM compression_cache WHERE token = ?").get(token);
|
|
7296
|
+
if (!row)
|
|
7297
|
+
return null;
|
|
7298
|
+
const expiresAt = String(row.expires_at);
|
|
7299
|
+
if (expiresAt <= now.toISOString()) {
|
|
7300
|
+
try {
|
|
7301
|
+
this.db.prepare("DELETE FROM compression_cache WHERE token = ?").run(token);
|
|
7302
|
+
} catch {}
|
|
7303
|
+
return null;
|
|
7304
|
+
}
|
|
7305
|
+
return {
|
|
7306
|
+
token: String(row.token),
|
|
7307
|
+
createdAt: String(row.created_at),
|
|
7308
|
+
expiresAt,
|
|
7309
|
+
toolName: String(row.tool_name),
|
|
7310
|
+
contentKind: String(row.content_kind),
|
|
7311
|
+
content: String(row.content),
|
|
7312
|
+
sizeBytes: Number(row.size_bytes)
|
|
7313
|
+
};
|
|
7314
|
+
}
|
|
7315
|
+
evictExpired(now = new Date) {
|
|
7316
|
+
const r = this.db.prepare("DELETE FROM compression_cache WHERE expires_at <= ?").run(now.toISOString());
|
|
7317
|
+
return Number(r.changes);
|
|
7318
|
+
}
|
|
7319
|
+
count() {
|
|
7320
|
+
const row = this.db.prepare("SELECT COUNT(*) AS n FROM compression_cache").get();
|
|
7321
|
+
return Number(row.n);
|
|
7322
|
+
}
|
|
7323
|
+
}
|
|
7324
|
+
var init_compression_cache_repo = __esm(() => {
|
|
7325
|
+
init_db();
|
|
7326
|
+
init_device();
|
|
7327
|
+
});
|
|
7328
|
+
|
|
7329
|
+
// src/core/compress-tool-output.ts
|
|
7330
|
+
function contentKey(s) {
|
|
7331
|
+
let h = 2166136261;
|
|
7332
|
+
for (let i = 0;i < s.length; i++) {
|
|
7333
|
+
h ^= s.charCodeAt(i);
|
|
7334
|
+
h = Math.imul(h, 16777619);
|
|
7335
|
+
}
|
|
7336
|
+
return (h >>> 0).toString(16);
|
|
7337
|
+
}
|
|
7338
|
+
function render(result, token) {
|
|
7339
|
+
return result.compressed + `
|
|
7340
|
+
|
|
7341
|
+
` + `\u2014 mink: compressed ${result.kind} output (${result.omittedNote}). ` + `Full original: mink retrieve ${token}`;
|
|
7342
|
+
}
|
|
7343
|
+
function safeRecord(cwd, toolName, contentKind, originalTokens, compressedTokens, holdout) {
|
|
7344
|
+
try {
|
|
7345
|
+
TokenLedgerRepo.for(cwd).recordCompression({
|
|
7346
|
+
toolName,
|
|
7347
|
+
contentKind,
|
|
7348
|
+
originalTokens,
|
|
7349
|
+
compressedTokens,
|
|
7350
|
+
holdout
|
|
7351
|
+
});
|
|
7352
|
+
} catch {}
|
|
7353
|
+
}
|
|
7354
|
+
function compressToolOutput(cwd, toolName, output, filePath) {
|
|
7355
|
+
let cfg;
|
|
7356
|
+
try {
|
|
7357
|
+
cfg = loadCompressionConfig();
|
|
7358
|
+
} catch {
|
|
7359
|
+
return null;
|
|
7360
|
+
}
|
|
7361
|
+
if (!cfg.enabled)
|
|
7362
|
+
return null;
|
|
7363
|
+
if (typeof output !== "string" || output.length === 0)
|
|
7364
|
+
return null;
|
|
7365
|
+
const originalTokens = countTokens(output);
|
|
7366
|
+
if (!isEligible(originalTokens, cfg))
|
|
7367
|
+
return null;
|
|
7368
|
+
const eventKey = contentKey(output);
|
|
7369
|
+
if (selectHoldout(eventKey, cfg.holdoutFraction)) {
|
|
7370
|
+
const kind = detectContentKind(toolName, output, filePath);
|
|
7371
|
+
safeRecord(cwd, toolName, kind, originalTokens, originalTokens, true);
|
|
7372
|
+
return null;
|
|
7373
|
+
}
|
|
7374
|
+
const result = compressOutput(toolName, output, filePath);
|
|
7375
|
+
if (!result)
|
|
7376
|
+
return null;
|
|
7377
|
+
const token = CompressionCacheRepo.newToken();
|
|
7378
|
+
const replacement = render(result, token);
|
|
7379
|
+
const compressedTokens = countTokens(replacement);
|
|
7380
|
+
if (!meetsMinSavings(originalTokens, compressedTokens, cfg))
|
|
7381
|
+
return null;
|
|
7382
|
+
try {
|
|
7383
|
+
CompressionCacheRepo.for(cwd).store({
|
|
7384
|
+
toolName,
|
|
7385
|
+
contentKind: result.kind,
|
|
7386
|
+
content: output,
|
|
7387
|
+
retentionHours: cfg.retentionHours,
|
|
7388
|
+
token
|
|
7389
|
+
});
|
|
7390
|
+
} catch {
|
|
7391
|
+
return null;
|
|
7392
|
+
}
|
|
7393
|
+
safeRecord(cwd, toolName, result.kind, originalTokens, compressedTokens, false);
|
|
7394
|
+
return { updatedToolOutput: replacement, token };
|
|
7395
|
+
}
|
|
7396
|
+
var init_compress_tool_output = __esm(() => {
|
|
7397
|
+
init_compression();
|
|
7398
|
+
init_token_estimate();
|
|
7399
|
+
init_output_compression();
|
|
7400
|
+
init_compression_cache_repo();
|
|
7401
|
+
init_token_ledger_repo();
|
|
7402
|
+
});
|
|
7403
|
+
|
|
7404
|
+
// src/core/hook-output.ts
|
|
7405
|
+
function extractToolOutputText(input) {
|
|
7406
|
+
const tr = input.tool_response;
|
|
7407
|
+
if (tr) {
|
|
7408
|
+
if (typeof tr.content === "string")
|
|
7409
|
+
return tr.content;
|
|
7410
|
+
if (Array.isArray(tr.content)) {
|
|
7411
|
+
const parts = tr.content.map((p) => p && typeof p.text === "string" ? p.text : "").filter((s) => s.length > 0);
|
|
7412
|
+
if (parts.length > 0)
|
|
7413
|
+
return parts.join("");
|
|
7414
|
+
}
|
|
7415
|
+
if (typeof tr.stdout === "string" && tr.stdout.length > 0)
|
|
7416
|
+
return tr.stdout;
|
|
7417
|
+
if (typeof tr.text === "string")
|
|
7418
|
+
return tr.text;
|
|
7419
|
+
const file = tr.file;
|
|
7420
|
+
if (file && typeof file.content === "string")
|
|
7421
|
+
return file.content;
|
|
7422
|
+
}
|
|
7423
|
+
const to = input.tool_output;
|
|
7424
|
+
if (to && typeof to.content === "string")
|
|
7425
|
+
return to.content;
|
|
7426
|
+
return null;
|
|
7427
|
+
}
|
|
7428
|
+
function emitUpdatedToolOutput(text) {
|
|
7429
|
+
process.stdout.write(JSON.stringify({
|
|
7430
|
+
hookSpecificOutput: {
|
|
7431
|
+
hookEventName: "PostToolUse",
|
|
7432
|
+
updatedToolOutput: text
|
|
7433
|
+
}
|
|
7434
|
+
}));
|
|
7435
|
+
}
|
|
7436
|
+
|
|
6785
7437
|
// src/commands/post-read.ts
|
|
6786
7438
|
var exports_post_read = {};
|
|
6787
7439
|
__export(exports_post_read, {
|
|
@@ -6911,6 +7563,14 @@ async function postRead(cwd) {
|
|
|
6911
7563
|
logWriter.appendReadEntry(new Date().toISOString(), filePath, result.indexHit, result.estimatedTokens);
|
|
6912
7564
|
} catch {}
|
|
6913
7565
|
atomicWriteJson(sessionPath(cwd), state);
|
|
7566
|
+
const isRanged = input.tool_input.offset != null || input.tool_input.limit != null;
|
|
7567
|
+
if (!isRanged && content && content.length > 0) {
|
|
7568
|
+
try {
|
|
7569
|
+
const outcome = compressToolOutput(cwd, "Read", content, filePath);
|
|
7570
|
+
if (outcome)
|
|
7571
|
+
emitUpdatedToolOutput(outcome.updatedToolOutput);
|
|
7572
|
+
} catch {}
|
|
7573
|
+
}
|
|
6914
7574
|
} catch {} finally {
|
|
6915
7575
|
clearTimeout(timer);
|
|
6916
7576
|
}
|
|
@@ -6924,6 +7584,43 @@ var init_post_read = __esm(() => {
|
|
|
6924
7584
|
init_description();
|
|
6925
7585
|
init_action_log();
|
|
6926
7586
|
init_device();
|
|
7587
|
+
init_compress_tool_output();
|
|
7588
|
+
});
|
|
7589
|
+
|
|
7590
|
+
// src/commands/post-tool.ts
|
|
7591
|
+
var exports_post_tool = {};
|
|
7592
|
+
__export(exports_post_tool, {
|
|
7593
|
+
postTool: () => postTool
|
|
7594
|
+
});
|
|
7595
|
+
function isPostToolUseInput2(value) {
|
|
7596
|
+
if (value === null || typeof value !== "object")
|
|
7597
|
+
return false;
|
|
7598
|
+
const obj = value;
|
|
7599
|
+
return typeof obj.tool_name === "string";
|
|
7600
|
+
}
|
|
7601
|
+
function isCompressibleTool(toolName) {
|
|
7602
|
+
return toolName === "Bash" || toolName === "Grep" || toolName === "Glob" || toolName.startsWith("mcp__");
|
|
7603
|
+
}
|
|
7604
|
+
async function postTool(cwd) {
|
|
7605
|
+
const timer = setTimeout(() => process.exit(0), 5000);
|
|
7606
|
+
try {
|
|
7607
|
+
const input = await readStdinJson();
|
|
7608
|
+
if (!isPostToolUseInput2(input))
|
|
7609
|
+
return;
|
|
7610
|
+
if (!isCompressibleTool(input.tool_name))
|
|
7611
|
+
return;
|
|
7612
|
+
const output = extractToolOutputText(input);
|
|
7613
|
+
if (!output)
|
|
7614
|
+
return;
|
|
7615
|
+
const outcome = compressToolOutput(cwd, input.tool_name, output);
|
|
7616
|
+
if (outcome)
|
|
7617
|
+
emitUpdatedToolOutput(outcome.updatedToolOutput);
|
|
7618
|
+
} catch {} finally {
|
|
7619
|
+
clearTimeout(timer);
|
|
7620
|
+
}
|
|
7621
|
+
}
|
|
7622
|
+
var init_post_tool = __esm(() => {
|
|
7623
|
+
init_compress_tool_output();
|
|
6927
7624
|
});
|
|
6928
7625
|
|
|
6929
7626
|
// src/core/pattern-engine.ts
|
|
@@ -7162,7 +7859,7 @@ function analyzePostWrite(filePath, fileContent, index) {
|
|
|
7162
7859
|
indexEntry
|
|
7163
7860
|
};
|
|
7164
7861
|
}
|
|
7165
|
-
function
|
|
7862
|
+
function isPostToolUseInput3(value) {
|
|
7166
7863
|
if (value === null || typeof value !== "object")
|
|
7167
7864
|
return false;
|
|
7168
7865
|
const obj = value;
|
|
@@ -7176,7 +7873,7 @@ async function postWrite(cwd) {
|
|
|
7176
7873
|
const timer = setTimeout(() => process.exit(0), 1e4);
|
|
7177
7874
|
try {
|
|
7178
7875
|
const input = await readStdinJson();
|
|
7179
|
-
if (!
|
|
7876
|
+
if (!isPostToolUseInput3(input))
|
|
7180
7877
|
return;
|
|
7181
7878
|
if (input.tool_name !== "Write" && input.tool_name !== "Edit")
|
|
7182
7879
|
return;
|
|
@@ -7405,6 +8102,35 @@ var init_detect_waste = __esm(() => {
|
|
|
7405
8102
|
init_device();
|
|
7406
8103
|
});
|
|
7407
8104
|
|
|
8105
|
+
// src/commands/retrieve.ts
|
|
8106
|
+
var exports_retrieve = {};
|
|
8107
|
+
__export(exports_retrieve, {
|
|
8108
|
+
retrieve: () => retrieve
|
|
8109
|
+
});
|
|
8110
|
+
function retrieve(cwd, args) {
|
|
8111
|
+
const token = args[0];
|
|
8112
|
+
if (!token) {
|
|
8113
|
+
process.stderr.write(`[mink] usage: mink retrieve <token>
|
|
8114
|
+
`);
|
|
8115
|
+
return;
|
|
8116
|
+
}
|
|
8117
|
+
let entry = null;
|
|
8118
|
+
try {
|
|
8119
|
+
entry = CompressionCacheRepo.for(cwd).get(token);
|
|
8120
|
+
} catch {
|
|
8121
|
+
entry = null;
|
|
8122
|
+
}
|
|
8123
|
+
if (!entry) {
|
|
8124
|
+
process.stderr.write(`[mink] no retrievable output for token "${token}" (unknown or expired)
|
|
8125
|
+
`);
|
|
8126
|
+
return;
|
|
8127
|
+
}
|
|
8128
|
+
process.stdout.write(entry.content);
|
|
8129
|
+
}
|
|
8130
|
+
var init_retrieve = __esm(() => {
|
|
8131
|
+
init_compression_cache_repo();
|
|
8132
|
+
});
|
|
8133
|
+
|
|
7408
8134
|
// src/core/cron-parser.ts
|
|
7409
8135
|
function parseField(field, min, max) {
|
|
7410
8136
|
const values = new Set;
|
|
@@ -73756,7 +74482,7 @@ var require_dist10 = __commonJS((exports) => {
|
|
|
73756
74482
|
exports.PacProxyAgent = undefined;
|
|
73757
74483
|
var net = __importStar(__require("net"));
|
|
73758
74484
|
var tls = __importStar(__require("tls"));
|
|
73759
|
-
var
|
|
74485
|
+
var crypto2 = __importStar(__require("crypto"));
|
|
73760
74486
|
var events_1 = __require("events");
|
|
73761
74487
|
var debug_1 = __importDefault(require_src());
|
|
73762
74488
|
var url_1 = __require("url");
|
|
@@ -73806,7 +74532,7 @@ var require_dist10 = __commonJS((exports) => {
|
|
|
73806
74532
|
(0, quickjs_emscripten_1.getQuickJS)(),
|
|
73807
74533
|
this.loadPacFile()
|
|
73808
74534
|
]);
|
|
73809
|
-
const hash =
|
|
74535
|
+
const hash = crypto2.createHash("sha1").update(code).digest("hex");
|
|
73810
74536
|
if (this.resolver && this.resolverHash === hash) {
|
|
73811
74537
|
debug2("Same sha1 hash for code - contents have not changed, reusing previous proxy resolver");
|
|
73812
74538
|
return this.resolver;
|
|
@@ -81077,12 +81803,12 @@ var init_lib = __esm(() => {
|
|
|
81077
81803
|
});
|
|
81078
81804
|
|
|
81079
81805
|
// node_modules/cliui/build/lib/string-utils.js
|
|
81080
|
-
function
|
|
81806
|
+
function stripAnsi2(str) {
|
|
81081
81807
|
return str.replace(ansi, "");
|
|
81082
81808
|
}
|
|
81083
81809
|
function wrap(str, width) {
|
|
81084
81810
|
const [start, end] = str.match(ansi) || ["", ""];
|
|
81085
|
-
str =
|
|
81811
|
+
str = stripAnsi2(str);
|
|
81086
81812
|
let wrapped = "";
|
|
81087
81813
|
for (let i = 0;i < str.length; i++) {
|
|
81088
81814
|
if (i !== 0 && i % width === 0) {
|
|
@@ -81107,7 +81833,7 @@ function ui(opts) {
|
|
|
81107
81833
|
stringWidth: (str) => {
|
|
81108
81834
|
return [...str].length;
|
|
81109
81835
|
},
|
|
81110
|
-
stripAnsi,
|
|
81836
|
+
stripAnsi: stripAnsi2,
|
|
81111
81837
|
wrap
|
|
81112
81838
|
});
|
|
81113
81839
|
}
|
|
@@ -90528,6 +91254,11 @@ switch (command2) {
|
|
|
90528
91254
|
await postRead2(cwd);
|
|
90529
91255
|
break;
|
|
90530
91256
|
}
|
|
91257
|
+
case "post-tool": {
|
|
91258
|
+
const { postTool: postTool2 } = await Promise.resolve().then(() => (init_post_tool(), exports_post_tool));
|
|
91259
|
+
await postTool2(cwd);
|
|
91260
|
+
break;
|
|
91261
|
+
}
|
|
90531
91262
|
case "pre-write": {
|
|
90532
91263
|
const { preWrite: preWrite2 } = await Promise.resolve().then(() => (init_pre_write(), exports_pre_write));
|
|
90533
91264
|
await preWrite2(cwd);
|
|
@@ -90543,6 +91274,11 @@ switch (command2) {
|
|
|
90543
91274
|
detectWaste2(cwd);
|
|
90544
91275
|
break;
|
|
90545
91276
|
}
|
|
91277
|
+
case "retrieve": {
|
|
91278
|
+
const { retrieve: retrieve2 } = await Promise.resolve().then(() => (init_retrieve(), exports_retrieve));
|
|
91279
|
+
retrieve2(cwd, process.argv.slice(3));
|
|
91280
|
+
break;
|
|
91281
|
+
}
|
|
90546
91282
|
case "cron": {
|
|
90547
91283
|
const { cron: cron2 } = await Promise.resolve().then(() => (init_cron(), exports_cron));
|
|
90548
91284
|
await cron2(cwd, process.argv.slice(3));
|
|
@@ -90712,6 +91448,7 @@ switch (command2) {
|
|
|
90712
91448
|
console.log(" restore [backup] Restore state from a backup");
|
|
90713
91449
|
console.log(" bug search <term> Search the bug log");
|
|
90714
91450
|
console.log(" detect-waste Detect and flag wasteful patterns");
|
|
91451
|
+
console.log(" retrieve <token> Return a compressed tool output's original (spec 21)");
|
|
90715
91452
|
console.log(" reflect Generate learning memory reflections");
|
|
90716
91453
|
console.log(" designqc [target] Capture design screenshots (spec 13)");
|
|
90717
91454
|
console.log(" framework-advisor Generate framework advisor knowledge file (spec 14)");
|
|
@@ -90721,6 +91458,7 @@ switch (command2) {
|
|
|
90721
91458
|
console.log(" session-stop Finalize session and log data");
|
|
90722
91459
|
console.log(" pre-read / post-read File read hooks");
|
|
90723
91460
|
console.log(" pre-write / post-write File write hooks");
|
|
91461
|
+
console.log(" post-tool Tool-output compression hook (Bash/Grep/MCP, spec 21)");
|
|
90724
91462
|
break;
|
|
90725
91463
|
default:
|
|
90726
91464
|
console.error(`[mink] unknown command: ${command2 ?? "(none)"}`);
|