nuxs-capsule 0.1.20 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/nuxs-capsule-agent-compress.js +4 -0
- package/bin/nuxs-capsule-read-compress.js +4 -0
- package/bin/nuxs-capsule-session-end.js +4 -0
- package/bin/nuxs-capsule-sticky-inject.js +4 -0
- package/dist/agent-compress.d.ts +17 -0
- package/dist/agent-compress.d.ts.map +1 -0
- package/dist/agent-compress.js +155 -0
- package/dist/agent-compress.js.map +1 -0
- package/dist/lib/archive.d.ts +53 -0
- package/dist/lib/archive.d.ts.map +1 -0
- package/dist/lib/archive.js +121 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/stickies.d.ts +34 -0
- package/dist/lib/stickies.d.ts.map +1 -0
- package/dist/lib/stickies.js +168 -0
- package/dist/lib/stickies.js.map +1 -0
- package/dist/read-compress.d.ts +24 -0
- package/dist/read-compress.d.ts.map +1 -0
- package/dist/read-compress.js +189 -0
- package/dist/read-compress.js.map +1 -0
- package/dist/session-end.d.ts +18 -0
- package/dist/session-end.d.ts.map +1 -0
- package/dist/session-end.js +88 -0
- package/dist/session-end.js.map +1 -0
- package/dist/setup.js +217 -43
- package/dist/setup.js.map +1 -1
- package/dist/sticky-inject.d.ts +20 -0
- package/dist/sticky-inject.d.ts.map +1 -0
- package/dist/sticky-inject.js +88 -0
- package/dist/sticky-inject.js.map +1 -0
- package/dist/telemetry/usage.d.ts.map +1 -1
- package/dist/telemetry/usage.js +22 -5
- package/dist/telemetry/usage.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook PostToolUse — `nuxs-capsule-agent-compress`.
|
|
4
|
+
*
|
|
5
|
+
* Intercepta result do tool Task (Agent) ou WebFetch quando retorna
|
|
6
|
+
* payload grande (>10k bytes). Gera cápsula TL;DR + bullets com refs.
|
|
7
|
+
*
|
|
8
|
+
* Pra agent: já é um sumário gerado por outro Claude — comprimir o
|
|
9
|
+
* sumário em sumário curto é seguro (não perde primários, eles ja
|
|
10
|
+
* são derivados).
|
|
11
|
+
*
|
|
12
|
+
* Pra WebFetch: comprime HTML/MD em snippet limpo.
|
|
13
|
+
*
|
|
14
|
+
* Original arquivado em ~/.nuxs/archive/.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=agent-compress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-compress.d.ts","sourceRoot":"","sources":["../src/agent-compress.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook PostToolUse — `nuxs-capsule-agent-compress`.
|
|
4
|
+
*
|
|
5
|
+
* Intercepta result do tool Task (Agent) ou WebFetch quando retorna
|
|
6
|
+
* payload grande (>10k bytes). Gera cápsula TL;DR + bullets com refs.
|
|
7
|
+
*
|
|
8
|
+
* Pra agent: já é um sumário gerado por outro Claude — comprimir o
|
|
9
|
+
* sumário em sumário curto é seguro (não perde primários, eles ja
|
|
10
|
+
* são derivados).
|
|
11
|
+
*
|
|
12
|
+
* Pra WebFetch: comprime HTML/MD em snippet limpo.
|
|
13
|
+
*
|
|
14
|
+
* Original arquivado em ~/.nuxs/archive/.
|
|
15
|
+
*/
|
|
16
|
+
import { archiveOriginal } from './lib/archive.js';
|
|
17
|
+
import { trackUsage } from './telemetry/usage.js';
|
|
18
|
+
import { getMachineFingerprint } from './lib/fingerprint.js';
|
|
19
|
+
import { estimateTokensFromBytes, estimateCostUsd } from './lib/tokenCount.js';
|
|
20
|
+
const THRESHOLD_BYTES = 10_000;
|
|
21
|
+
async function readStdin() {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
let data = '';
|
|
24
|
+
process.stdin.setEncoding('utf8');
|
|
25
|
+
process.stdin.on('data', (c) => { data += c; });
|
|
26
|
+
process.stdin.on('end', () => resolve(data));
|
|
27
|
+
setTimeout(() => resolve(data), 4000);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function extractToolText(resp) {
|
|
31
|
+
if (typeof resp === 'string')
|
|
32
|
+
return resp;
|
|
33
|
+
if (!resp)
|
|
34
|
+
return '';
|
|
35
|
+
const c = resp.content;
|
|
36
|
+
if (typeof c === 'string')
|
|
37
|
+
return c;
|
|
38
|
+
if (Array.isArray(c))
|
|
39
|
+
return c.map((x) => (x && typeof x === 'object' && 'text' in x ? x.text : '')).join('\n');
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Comprime sumário gerado por agent.
|
|
44
|
+
* Estratégia: manter primeiras N linhas (geralmente conclusão),
|
|
45
|
+
* extrair bullets e file:line refs, descartar prosa.
|
|
46
|
+
*/
|
|
47
|
+
function compressAgentOutput(text) {
|
|
48
|
+
const lines = text.split('\n');
|
|
49
|
+
const head = lines.slice(0, 10);
|
|
50
|
+
// Coleta bullets (markdown - ou *)
|
|
51
|
+
const bullets = lines.filter((l) => /^\s*[-*•]\s/.test(l)).slice(0, 30);
|
|
52
|
+
// Coleta refs file:line (típico em sumários de Explore)
|
|
53
|
+
const refRegex = /([\w/.-]+\.(ts|tsx|js|jsx|py|md|json|sql|yml|yaml)):(\d+)/g;
|
|
54
|
+
const refs = new Set();
|
|
55
|
+
let m;
|
|
56
|
+
while ((m = refRegex.exec(text)) !== null) {
|
|
57
|
+
refs.add(`${m[1]}:${m[3]}`);
|
|
58
|
+
if (refs.size >= 50)
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
const out = [];
|
|
62
|
+
out.push('[NUXS CAPSULE: agent output comprimido]');
|
|
63
|
+
out.push(`Original: ${lines.length} linhas, ${Buffer.byteLength(text, 'utf8')} bytes.`);
|
|
64
|
+
out.push('');
|
|
65
|
+
out.push('── TL;DR (primeiras 10 linhas) ──');
|
|
66
|
+
out.push(...head);
|
|
67
|
+
if (bullets.length > 0) {
|
|
68
|
+
out.push('');
|
|
69
|
+
out.push('── bullets ──');
|
|
70
|
+
out.push(...bullets);
|
|
71
|
+
}
|
|
72
|
+
if (refs.size > 0) {
|
|
73
|
+
out.push('');
|
|
74
|
+
out.push('── refs encontradas ──');
|
|
75
|
+
out.push(...Array.from(refs));
|
|
76
|
+
}
|
|
77
|
+
return out.join('\n');
|
|
78
|
+
}
|
|
79
|
+
async function main() {
|
|
80
|
+
const stdin = await readStdin();
|
|
81
|
+
if (!stdin) {
|
|
82
|
+
process.exit(0);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
let input;
|
|
86
|
+
try {
|
|
87
|
+
input = JSON.parse(stdin);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
process.exit(0);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const toolName = input.tool_name ?? '';
|
|
94
|
+
// Aplica em Task (subagents) e WebFetch
|
|
95
|
+
if (!/^(Task|WebFetch)$/.test(toolName)) {
|
|
96
|
+
process.exit(0);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const text = extractToolText(input.tool_response);
|
|
100
|
+
const bytes = Buffer.byteLength(text, 'utf8');
|
|
101
|
+
if (bytes < THRESHOLD_BYTES) {
|
|
102
|
+
process.exit(0);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const source = toolName === 'Task'
|
|
106
|
+
? `agent:${input.tool_input?.subagent_type ?? 'unknown'}:${(input.tool_input?.description ?? '').slice(0, 40)}`
|
|
107
|
+
: `webfetch:${input.tool_input?.url ?? 'unknown'}`;
|
|
108
|
+
const compressed = compressAgentOutput(text);
|
|
109
|
+
const compressedBytes = Buffer.byteLength(compressed, 'utf8');
|
|
110
|
+
const tokensOriginal = estimateTokensFromBytes(bytes);
|
|
111
|
+
const tokensCompressed = estimateTokensFromBytes(compressedBytes);
|
|
112
|
+
const tokensSaved = tokensOriginal - tokensCompressed;
|
|
113
|
+
if (tokensSaved <= 0) {
|
|
114
|
+
process.exit(0);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
let archivePath;
|
|
118
|
+
try {
|
|
119
|
+
archivePath = archiveOriginal('agent', source, text, {
|
|
120
|
+
tokens_original: tokensOriginal,
|
|
121
|
+
tokens_compressed: tokensCompressed
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch { /* */ }
|
|
125
|
+
const fingerprint = getMachineFingerprint();
|
|
126
|
+
const record = {
|
|
127
|
+
timestamp: new Date().toISOString(),
|
|
128
|
+
capsule_type: 'api',
|
|
129
|
+
input_size_bytes: bytes,
|
|
130
|
+
output_size_bytes: compressedBytes,
|
|
131
|
+
input_tokens_estimated: tokensOriginal,
|
|
132
|
+
output_tokens_estimated: tokensCompressed,
|
|
133
|
+
tokens_saved: tokensSaved,
|
|
134
|
+
provider_used: 'algorithm',
|
|
135
|
+
cost_saved_usd: estimateCostUsd(tokensSaved, 0, 'sonnet-4-6'),
|
|
136
|
+
agent_detected: 'claude_code',
|
|
137
|
+
latency_ms: 0,
|
|
138
|
+
machine_fingerprint: fingerprint
|
|
139
|
+
};
|
|
140
|
+
try {
|
|
141
|
+
await trackUsage(record);
|
|
142
|
+
}
|
|
143
|
+
catch { /* */ }
|
|
144
|
+
const refLine = archivePath ? `\n📦 Original: ${archivePath}` : '';
|
|
145
|
+
const out = {
|
|
146
|
+
hookSpecificOutput: {
|
|
147
|
+
hookEventName: 'PostToolUse',
|
|
148
|
+
additionalContext: compressed + refLine
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
process.stdout.write(JSON.stringify(out));
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
main().catch(() => process.exit(0));
|
|
155
|
+
//# sourceMappingURL=agent-compress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-compress.js","sourceRoot":"","sources":["../src/agent-compress.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arquivamento MD — backup do conteúdo original ANTES de comprimir.
|
|
3
|
+
*
|
|
4
|
+
* Toda cápsula nova (Read compressor, Agent compressor, sticky injector,
|
|
5
|
+
* proxy histórico) chama archiveOriginal() antes de gerar a versão
|
|
6
|
+
* comprimida. Resultado: usuário (ou eu) pode SEMPRE recuperar o original
|
|
7
|
+
* lendo o arquivo MD da pasta, evitando "perda de contexto" mesmo quando
|
|
8
|
+
* a cápsula erra resumo.
|
|
9
|
+
*
|
|
10
|
+
* Estrutura:
|
|
11
|
+
* ~/.nuxs/archive/
|
|
12
|
+
* 2026-05-28/
|
|
13
|
+
* sticky-bff3a01-1716902400.md
|
|
14
|
+
* read-page-tsx-1716902404.md
|
|
15
|
+
* agent-explore-1716902412.md
|
|
16
|
+
* _index.jsonl ← uma linha por arquivo, fácil de listar
|
|
17
|
+
*
|
|
18
|
+
* Granularidade: 1 arquivo por evento (não agrupado por sessão). Mantém
|
|
19
|
+
* busca/limpeza simples. Cleanup: archives > 30 dias removidos no flush.
|
|
20
|
+
*/
|
|
21
|
+
export interface ArchiveEntry {
|
|
22
|
+
kind: 'sticky' | 'read' | 'agent' | 'history' | 'tool_result';
|
|
23
|
+
source: string;
|
|
24
|
+
timestamp: string;
|
|
25
|
+
tokens_original: number;
|
|
26
|
+
tokens_compressed: number;
|
|
27
|
+
archive_path: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Salva conteúdo original num MD. Retorna o path completo.
|
|
31
|
+
*
|
|
32
|
+
* @param kind — tipo da cápsula
|
|
33
|
+
* @param source — identificador legível (ex: "page.tsx", "agent:explore-rotas")
|
|
34
|
+
* @param content — conteúdo cru a preservar
|
|
35
|
+
* @param meta — tokens estimados original e comprimido (pra dashboard)
|
|
36
|
+
*/
|
|
37
|
+
export declare function archiveOriginal(kind: ArchiveEntry['kind'], source: string, content: string, meta: {
|
|
38
|
+
tokens_original: number;
|
|
39
|
+
tokens_compressed: number;
|
|
40
|
+
}): string;
|
|
41
|
+
/**
|
|
42
|
+
* Hash curto de conteúdo — útil pra dedup quando o mesmo conteúdo é
|
|
43
|
+
* arquivado várias vezes (mesmo Read no mesmo arquivo).
|
|
44
|
+
*/
|
|
45
|
+
export declare function contentHash(content: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Cleanup automático — chamado pelo flush. Remove archives > N dias.
|
|
48
|
+
* Idempotente, silencioso em erros.
|
|
49
|
+
*/
|
|
50
|
+
export declare function cleanOldArchives(retentionDays?: number): {
|
|
51
|
+
removed: number;
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=archive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/lib/archive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAWH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,EAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAC3D,MAAM,CAyCR;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,SAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CA4BpF"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arquivamento MD — backup do conteúdo original ANTES de comprimir.
|
|
3
|
+
*
|
|
4
|
+
* Toda cápsula nova (Read compressor, Agent compressor, sticky injector,
|
|
5
|
+
* proxy histórico) chama archiveOriginal() antes de gerar a versão
|
|
6
|
+
* comprimida. Resultado: usuário (ou eu) pode SEMPRE recuperar o original
|
|
7
|
+
* lendo o arquivo MD da pasta, evitando "perda de contexto" mesmo quando
|
|
8
|
+
* a cápsula erra resumo.
|
|
9
|
+
*
|
|
10
|
+
* Estrutura:
|
|
11
|
+
* ~/.nuxs/archive/
|
|
12
|
+
* 2026-05-28/
|
|
13
|
+
* sticky-bff3a01-1716902400.md
|
|
14
|
+
* read-page-tsx-1716902404.md
|
|
15
|
+
* agent-explore-1716902412.md
|
|
16
|
+
* _index.jsonl ← uma linha por arquivo, fácil de listar
|
|
17
|
+
*
|
|
18
|
+
* Granularidade: 1 arquivo por evento (não agrupado por sessão). Mantém
|
|
19
|
+
* busca/limpeza simples. Cleanup: archives > 30 dias removidos no flush.
|
|
20
|
+
*/
|
|
21
|
+
import { writeFileSync, existsSync, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync } from 'node:fs';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { createHash } from 'node:crypto';
|
|
25
|
+
import { ensureDir } from './fileStat.js';
|
|
26
|
+
const ARCHIVE_DIR = join(homedir(), '.nuxs', 'archive');
|
|
27
|
+
const RETENTION_DAYS = 30;
|
|
28
|
+
/**
|
|
29
|
+
* Salva conteúdo original num MD. Retorna o path completo.
|
|
30
|
+
*
|
|
31
|
+
* @param kind — tipo da cápsula
|
|
32
|
+
* @param source — identificador legível (ex: "page.tsx", "agent:explore-rotas")
|
|
33
|
+
* @param content — conteúdo cru a preservar
|
|
34
|
+
* @param meta — tokens estimados original e comprimido (pra dashboard)
|
|
35
|
+
*/
|
|
36
|
+
export function archiveOriginal(kind, source, content, meta) {
|
|
37
|
+
const date = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
38
|
+
const dayDir = join(ARCHIVE_DIR, date);
|
|
39
|
+
ensureDir(dayDir);
|
|
40
|
+
const ts = Date.now();
|
|
41
|
+
const slug = source
|
|
42
|
+
.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
43
|
+
.replace(/^-+|-+$/g, '')
|
|
44
|
+
.slice(0, 60) || 'unknown';
|
|
45
|
+
const filename = `${kind}-${slug}-${ts}.md`;
|
|
46
|
+
const archivePath = join(dayDir, filename);
|
|
47
|
+
const header = [
|
|
48
|
+
`<!-- nuxs-capsule archive -->`,
|
|
49
|
+
`# ${kind}: ${source}`,
|
|
50
|
+
``,
|
|
51
|
+
`- **Timestamp:** ${new Date(ts).toISOString()}`,
|
|
52
|
+
`- **Tokens original:** ${meta.tokens_original}`,
|
|
53
|
+
`- **Tokens comprimido:** ${meta.tokens_compressed}`,
|
|
54
|
+
`- **Redução:** ${meta.tokens_original > 0 ? Math.round((1 - meta.tokens_compressed / meta.tokens_original) * 100) : 0}%`,
|
|
55
|
+
``,
|
|
56
|
+
`---`,
|
|
57
|
+
``
|
|
58
|
+
].join('\n');
|
|
59
|
+
writeFileSync(archivePath, header + content);
|
|
60
|
+
// Append no índice diário (1 linha JSONL por entry)
|
|
61
|
+
const indexPath = join(dayDir, '_index.jsonl');
|
|
62
|
+
const entry = {
|
|
63
|
+
kind,
|
|
64
|
+
source,
|
|
65
|
+
timestamp: new Date(ts).toISOString(),
|
|
66
|
+
tokens_original: meta.tokens_original,
|
|
67
|
+
tokens_compressed: meta.tokens_compressed,
|
|
68
|
+
archive_path: archivePath
|
|
69
|
+
};
|
|
70
|
+
appendFileSync(indexPath, JSON.stringify(entry) + '\n');
|
|
71
|
+
return archivePath;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Hash curto de conteúdo — útil pra dedup quando o mesmo conteúdo é
|
|
75
|
+
* arquivado várias vezes (mesmo Read no mesmo arquivo).
|
|
76
|
+
*/
|
|
77
|
+
export function contentHash(content) {
|
|
78
|
+
return createHash('sha256').update(content).digest('hex').slice(0, 12);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Cleanup automático — chamado pelo flush. Remove archives > N dias.
|
|
82
|
+
* Idempotente, silencioso em erros.
|
|
83
|
+
*/
|
|
84
|
+
export function cleanOldArchives(retentionDays = RETENTION_DAYS) {
|
|
85
|
+
if (!existsSync(ARCHIVE_DIR))
|
|
86
|
+
return { removed: 0 };
|
|
87
|
+
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
|
|
88
|
+
let removed = 0;
|
|
89
|
+
try {
|
|
90
|
+
const days = readdirSync(ARCHIVE_DIR);
|
|
91
|
+
for (const day of days) {
|
|
92
|
+
const dayPath = join(ARCHIVE_DIR, day);
|
|
93
|
+
let dayStat;
|
|
94
|
+
try {
|
|
95
|
+
dayStat = statSync(dayPath);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (!dayStat.isDirectory())
|
|
101
|
+
continue;
|
|
102
|
+
if (dayStat.mtimeMs > cutoff)
|
|
103
|
+
continue;
|
|
104
|
+
// Limpa arquivos do dia
|
|
105
|
+
try {
|
|
106
|
+
for (const f of readdirSync(dayPath)) {
|
|
107
|
+
try {
|
|
108
|
+
unlinkSync(join(dayPath, f));
|
|
109
|
+
removed++;
|
|
110
|
+
}
|
|
111
|
+
catch { /* ignore */ }
|
|
112
|
+
}
|
|
113
|
+
rmdirSync(dayPath);
|
|
114
|
+
}
|
|
115
|
+
catch { /* ignore */ }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch { /* ignore */ }
|
|
119
|
+
return { removed };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=archive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/lib/archive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,aAAa,EAAgB,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChI,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC;AACxD,MAAM,cAAc,GAAG,EAAE,CAAC;AAW1B;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,IAA0B,EAC1B,MAAc,EACd,OAAe,EACf,IAA4D;IAE5D,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa;IACjE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IACvC,SAAS,CAAC,MAAM,CAAC,CAAC;IAElB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG,MAAM;SAChB,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC;SACjC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;IAC7B,MAAM,QAAQ,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,EAAE,KAAK,CAAC;IAC5C,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG;QACb,+BAA+B;QAC/B,KAAK,IAAI,KAAK,MAAM,EAAE;QACtB,EAAE;QACF,oBAAoB,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE;QAChD,0BAA0B,IAAI,CAAC,eAAe,EAAE;QAChD,4BAA4B,IAAI,CAAC,iBAAiB,EAAE;QACpD,kBAAkB,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QACzH,EAAE;QACF,KAAK;QACL,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC;IAE7C,oDAAoD;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAiB;QAC1B,IAAI;QACJ,MAAM;QACN,SAAS,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE;QACrC,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;QACzC,YAAY,EAAE,WAAW;KAC1B,CAAC;IACF,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IAExD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,aAAa,GAAG,cAAc;IAC7D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAChE,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QACtC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YACvC,IAAI,OAAO,CAAC;YACZ,IAAI,CAAC;gBAAC,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,SAAS;YAAC,CAAC;YACxD,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE;gBAAE,SAAS;YACrC,IAAI,OAAO,CAAC,OAAO,GAAG,MAAM;gBAAE,SAAS;YAEvC,wBAAwB;YACxB,IAAI,CAAC;gBACH,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrC,IAAI,CAAC;wBACH,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC7B,OAAO,EAAE,CAAC;oBACZ,CAAC;oBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;gBAC1B,CAAC;gBACD,SAAS,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAExB,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stickies — regras críticas do projeto que sempre acompanham o contexto.
|
|
3
|
+
*
|
|
4
|
+
* Carregadas de duas fontes (uniões):
|
|
5
|
+
*
|
|
6
|
+
* 1. `~/.nuxs/stickies.json` — lista explícita criada pelo user/setup.
|
|
7
|
+
* 2. `~/.claude/projects/.../memory/MEMORY.md` — entradas marcadas como
|
|
8
|
+
* sticky via `[STICKY]` no início da linha OU memórias do tipo
|
|
9
|
+
* `feedback` (regras duráveis que o user já reforçou).
|
|
10
|
+
*
|
|
11
|
+
* Uso: hook UserPromptSubmit lê esta lista e injeta como pre-message
|
|
12
|
+
* pra cada turno. Mantém eu (Claude) lembrado de "DeepSeek != Haiku",
|
|
13
|
+
* "ctrl+c+v != recriar", "1 conta por provider", etc.
|
|
14
|
+
*
|
|
15
|
+
* Custo: 100-300 tokens/turno, ganho: zero retrabalho por esquecimento.
|
|
16
|
+
*/
|
|
17
|
+
export interface Sticky {
|
|
18
|
+
source: 'manual' | 'memory-feedback' | 'memory-tagged';
|
|
19
|
+
text: string;
|
|
20
|
+
origin?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Lê stickies de ambas fontes, faz dedup, limita tamanho.
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadStickies(): Sticky[];
|
|
26
|
+
/**
|
|
27
|
+
* Adiciona uma sticky manual (chamada via CLI `nuxs-capsule sticky add "..."`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function addManualSticky(text: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Formata stickies como bloco pra anexar a um prompt.
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatStickiesBlock(stickies: Sticky[]): string;
|
|
34
|
+
//# sourceMappingURL=stickies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stickies.d.ts","sourceRoot":"","sources":["../../src/lib/stickies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,QAAQ,GAAG,iBAAiB,GAAG,eAAe,CAAC;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,EAAE,CA6CvC;AA0DD;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CASlD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAS9D"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stickies — regras críticas do projeto que sempre acompanham o contexto.
|
|
3
|
+
*
|
|
4
|
+
* Carregadas de duas fontes (uniões):
|
|
5
|
+
*
|
|
6
|
+
* 1. `~/.nuxs/stickies.json` — lista explícita criada pelo user/setup.
|
|
7
|
+
* 2. `~/.claude/projects/.../memory/MEMORY.md` — entradas marcadas como
|
|
8
|
+
* sticky via `[STICKY]` no início da linha OU memórias do tipo
|
|
9
|
+
* `feedback` (regras duráveis que o user já reforçou).
|
|
10
|
+
*
|
|
11
|
+
* Uso: hook UserPromptSubmit lê esta lista e injeta como pre-message
|
|
12
|
+
* pra cada turno. Mantém eu (Claude) lembrado de "DeepSeek != Haiku",
|
|
13
|
+
* "ctrl+c+v != recriar", "1 conta por provider", etc.
|
|
14
|
+
*
|
|
15
|
+
* Custo: 100-300 tokens/turno, ganho: zero retrabalho por esquecimento.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, existsSync, writeFileSync, readdirSync } from 'node:fs';
|
|
18
|
+
import { homedir } from 'node:os';
|
|
19
|
+
import { join } from 'node:path';
|
|
20
|
+
import { ensureDir } from './fileStat.js';
|
|
21
|
+
const NUXS_DIR = join(homedir(), '.nuxs');
|
|
22
|
+
const STICKIES_FILE = join(NUXS_DIR, 'stickies.json');
|
|
23
|
+
const MEMORY_BASE = join(homedir(), '.claude', 'projects');
|
|
24
|
+
const MAX_STICKIES = 12; // limite duro
|
|
25
|
+
const MAX_TOTAL_TOKENS = 600; // ~ orçamento token por turno
|
|
26
|
+
/**
|
|
27
|
+
* Lê stickies de ambas fontes, faz dedup, limita tamanho.
|
|
28
|
+
*/
|
|
29
|
+
export function loadStickies() {
|
|
30
|
+
const stickies = [];
|
|
31
|
+
// 1. ~/.nuxs/stickies.json (lista manual)
|
|
32
|
+
if (existsSync(STICKIES_FILE)) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(readFileSync(STICKIES_FILE, 'utf8'));
|
|
35
|
+
if (Array.isArray(raw)) {
|
|
36
|
+
for (const entry of raw) {
|
|
37
|
+
if (typeof entry === 'string') {
|
|
38
|
+
stickies.push({ source: 'manual', text: entry });
|
|
39
|
+
}
|
|
40
|
+
else if (entry && typeof entry.text === 'string') {
|
|
41
|
+
stickies.push({ source: 'manual', text: entry.text });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch { /* arquivo corrupto, ignora */ }
|
|
47
|
+
}
|
|
48
|
+
// 2. MEMORY.md — extrai linhas de feedback (regras duráveis)
|
|
49
|
+
try {
|
|
50
|
+
stickies.push(...loadFromMemory());
|
|
51
|
+
}
|
|
52
|
+
catch { /* memory dir não existe */ }
|
|
53
|
+
// Dedup por texto
|
|
54
|
+
const seen = new Set();
|
|
55
|
+
const deduped = [];
|
|
56
|
+
for (const s of stickies) {
|
|
57
|
+
const key = s.text.trim().toLowerCase();
|
|
58
|
+
if (seen.has(key))
|
|
59
|
+
continue;
|
|
60
|
+
seen.add(key);
|
|
61
|
+
deduped.push(s);
|
|
62
|
+
}
|
|
63
|
+
// Limite token aproximado (~4 chars/token)
|
|
64
|
+
let totalChars = 0;
|
|
65
|
+
const result = [];
|
|
66
|
+
for (const s of deduped) {
|
|
67
|
+
if (result.length >= MAX_STICKIES)
|
|
68
|
+
break;
|
|
69
|
+
const chars = s.text.length;
|
|
70
|
+
if (totalChars + chars > MAX_TOTAL_TOKENS * 4)
|
|
71
|
+
break;
|
|
72
|
+
totalChars += chars;
|
|
73
|
+
result.push(s);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Lê MEMORY.md do projeto atual (cwd) e extrai linhas de feedback.
|
|
79
|
+
* MEMORY.md tem formato `- [Title](file.md) — hook`. Pegamos hook.
|
|
80
|
+
*/
|
|
81
|
+
function loadFromMemory() {
|
|
82
|
+
if (!existsSync(MEMORY_BASE))
|
|
83
|
+
return [];
|
|
84
|
+
// Encontra o projeto que tem cwd no path
|
|
85
|
+
const cwd = process.cwd();
|
|
86
|
+
const projects = readdirSync(MEMORY_BASE);
|
|
87
|
+
let memoryDir = null;
|
|
88
|
+
for (const proj of projects) {
|
|
89
|
+
// Nome do projeto na pasta `.claude/projects` é o path escapado
|
|
90
|
+
// ex: "-Users-josuca-Documents-pixeldesk" pra /Users/josuca/Documents/pixeldesk
|
|
91
|
+
const restored = proj.replace(/^-/, '/').replace(/-/g, '/');
|
|
92
|
+
if (cwd.startsWith(restored)) {
|
|
93
|
+
const candidate = join(MEMORY_BASE, proj, 'memory', 'MEMORY.md');
|
|
94
|
+
if (existsSync(candidate)) {
|
|
95
|
+
memoryDir = join(MEMORY_BASE, proj, 'memory');
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!memoryDir)
|
|
101
|
+
return [];
|
|
102
|
+
const memoryFile = join(memoryDir, 'MEMORY.md');
|
|
103
|
+
let content;
|
|
104
|
+
try {
|
|
105
|
+
content = readFileSync(memoryFile, 'utf8');
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
const out = [];
|
|
111
|
+
const lines = content.split('\n');
|
|
112
|
+
for (const line of lines) {
|
|
113
|
+
// formato: - [Title](file.md) — hook texto curto
|
|
114
|
+
const m = line.match(/^-\s+\[([^\]]+)\]\(([^)]+\.md)\)\s+[—\-–]\s+(.+)$/);
|
|
115
|
+
if (!m)
|
|
116
|
+
continue;
|
|
117
|
+
const filename = m[2];
|
|
118
|
+
const hook = m[3].trim();
|
|
119
|
+
// Carrega arquivo pra ver tipo
|
|
120
|
+
const targetFile = join(memoryDir, filename);
|
|
121
|
+
if (!existsSync(targetFile)) {
|
|
122
|
+
// hook do índice já vale como sticky
|
|
123
|
+
out.push({ source: 'memory-tagged', text: hook, origin: filename });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const raw = readFileSync(targetFile, 'utf8');
|
|
128
|
+
const isFeedback = /type:\s*feedback/.test(raw);
|
|
129
|
+
if (isFeedback) {
|
|
130
|
+
out.push({ source: 'memory-feedback', text: hook, origin: filename });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch { /* ignora */ }
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Adiciona uma sticky manual (chamada via CLI `nuxs-capsule sticky add "..."`).
|
|
139
|
+
*/
|
|
140
|
+
export function addManualSticky(text) {
|
|
141
|
+
ensureDir(NUXS_DIR);
|
|
142
|
+
let list = [];
|
|
143
|
+
if (existsSync(STICKIES_FILE)) {
|
|
144
|
+
try {
|
|
145
|
+
list = JSON.parse(readFileSync(STICKIES_FILE, 'utf8'));
|
|
146
|
+
}
|
|
147
|
+
catch { /* */ }
|
|
148
|
+
}
|
|
149
|
+
if (!Array.isArray(list))
|
|
150
|
+
list = [];
|
|
151
|
+
list.push({ text });
|
|
152
|
+
writeFileSync(STICKIES_FILE, JSON.stringify(list, null, 2));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Formata stickies como bloco pra anexar a um prompt.
|
|
156
|
+
*/
|
|
157
|
+
export function formatStickiesBlock(stickies) {
|
|
158
|
+
if (stickies.length === 0)
|
|
159
|
+
return '';
|
|
160
|
+
const lines = [
|
|
161
|
+
'⚠️ AVISOS DURÁVEIS DESSE PROJETO (não esquecer):'
|
|
162
|
+
];
|
|
163
|
+
for (const s of stickies) {
|
|
164
|
+
lines.push(`• ${s.text}`);
|
|
165
|
+
}
|
|
166
|
+
return lines.join('\n');
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=stickies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stickies.js","sourceRoot":"","sources":["../../src/lib/stickies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;AACtD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AAE3D,MAAM,YAAY,GAAG,EAAE,CAAC,CAAK,cAAc;AAC3C,MAAM,gBAAgB,GAAG,GAAG,CAAC,CAAC,8BAA8B;AAQ5D;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,0CAA0C;IAC1C,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;YAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,KAAK,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;oBACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;wBAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;oBACnD,CAAC;yBAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;wBACnD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;oBACxD,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,8BAA8B,CAAC,CAAC;IAC5C,CAAC;IAED,6DAA6D;IAC7D,IAAI,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,GAAG,cAAc,EAAE,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC,CAAC,2BAA2B,CAAC,CAAC;IAEvC,kBAAkB;IAClB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,2CAA2C;IAC3C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,MAAM,CAAC,MAAM,IAAI,YAAY;YAAE,MAAM;QACzC,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;QAC5B,IAAI,UAAU,GAAG,KAAK,GAAG,gBAAgB,GAAG,CAAC;YAAE,MAAM;QACrD,UAAU,IAAI,KAAK,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc;IACrB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,yCAAyC;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,SAAS,GAAkB,IAAI,CAAC;IAEpC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,gEAAgE;QAChE,gFAAgF;QAChF,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;YACjE,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1B,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAC9C,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAChD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QAAC,OAAO,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;IAExE,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,iDAAiD;QACjD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAC;QAC1E,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;QAC1B,+BAA+B;QAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,qCAAqC;YACrC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QACD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChD,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YACxE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,SAAS,CAAC,QAAQ,CAAC,CAAC;IACpB,IAAI,IAAI,GAAqC,EAAE,CAAC;IAChD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,IAAI,GAAG,EAAE,CAAC;IACpC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACpB,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAkB;IACpD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG;QACZ,kDAAkD;KACnD,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook PostToolUse — `nuxs-capsule-read-compress`.
|
|
4
|
+
*
|
|
5
|
+
* Intercepta o RESULT do tool Read após Claude Code ter lido um arquivo.
|
|
6
|
+
* Se o conteúdo é grande (> THRESHOLD), substitui pela cápsula estrutural:
|
|
7
|
+
* - imports intactos
|
|
8
|
+
* - assinaturas de funções/classes + 1 linha de cabeçalho cada
|
|
9
|
+
* - exports intactos
|
|
10
|
+
* - índice "func X linha Y"
|
|
11
|
+
*
|
|
12
|
+
* Original é salvo em ~/.nuxs/archive/. Se Claude precisar de detalhe,
|
|
13
|
+
* faz Read offset=N limit=M com base no índice.
|
|
14
|
+
*
|
|
15
|
+
* Lê stdin: { tool_name, tool_input, tool_response, ... } (PostToolUse).
|
|
16
|
+
* Devolve stdout: { hookSpecificOutput: { hookEventName: 'PostToolUse',
|
|
17
|
+
* additionalContext: "<cápsula>" } }.
|
|
18
|
+
*
|
|
19
|
+
* NÃO devolvemos `tool_response` substituído porque Anthropic ainda
|
|
20
|
+
* não documenta esse caminho — usamos additionalContext (anexa, não
|
|
21
|
+
* substitui).
|
|
22
|
+
*/
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=read-compress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-compress.d.ts","sourceRoot":"","sources":["../src/read-compress.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;GAoBG"}
|