ctxjev-cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/cost.d.ts +2 -0
- package/dist/cost.js +6 -0
- package/dist/cost.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/report.d.ts +2 -0
- package/dist/report.js +39 -0
- package/dist/report.js.map +1 -0
- package/dist/transcript.d.ts +15 -0
- package/dist/transcript.js +40 -0
- package/dist/transcript.js.map +1 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 x96x64
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# ctxjev-cli
|
|
2
|
+
|
|
3
|
+
Analyze an AI agent transcript and see what [Jev](https://typesafe.ai) would keep, drop, or
|
|
4
|
+
summarize — plain-text report or `--json`.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g ctxjev-cli
|
|
8
|
+
export TYPESAFE_API_KEY=... # console.typesafe.ai/settings/keys — no waitlist
|
|
9
|
+
|
|
10
|
+
ctxjev analyze transcript.jsonl --goal "Fix the checkout double-charge bug."
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Accepts two formats, auto-detected: ctxjev's own `{ goal?, entries }` JSON, or a real Claude Code
|
|
14
|
+
session `.jsonl` transcript (goal inferred from your most recent chat message unless `--goal`
|
|
15
|
+
overrides it).
|
|
16
|
+
|
|
17
|
+
Full docs, design notes, and example transcripts live in the main repo:
|
|
18
|
+
**[github.com/x96x64/ctxjev](https://github.com/x96x64/ctxjev)**.
|
|
19
|
+
|
|
20
|
+
## License
|
|
21
|
+
|
|
22
|
+
MIT
|
package/dist/cost.d.ts
ADDED
package/dist/cost.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Jev's published pricing: $0.042 per million input tokens, output free. */
|
|
2
|
+
const USD_PER_MILLION_INPUT_TOKENS = 0.042;
|
|
3
|
+
export function estimateCostUsd(usage) {
|
|
4
|
+
return (usage.inputTokens / 1_000_000) * USD_PER_MILLION_INPUT_TOKENS;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=cost.js.map
|
package/dist/cost.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,MAAM,4BAA4B,GAAG,KAAK,CAAA;AAE1C,MAAM,UAAU,eAAe,CAAC,KAAe;IAC7C,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC,GAAG,4BAA4B,CAAA;AACvE,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { DEFAULT_POLICY, pruneContext, summarizeSavings } from 'ctxjev-core';
|
|
6
|
+
import { formatReport } from './report.js';
|
|
7
|
+
import { parseTranscript } from './transcript.js';
|
|
8
|
+
const VERSION = '0.0.0';
|
|
9
|
+
const HELP = `${pc.bold('ctxjev')} — score and prune AI agent context with Jev
|
|
10
|
+
|
|
11
|
+
${pc.bold('Usage')}
|
|
12
|
+
ctxjev analyze <transcript> --goal "<current task>" [options]
|
|
13
|
+
|
|
14
|
+
${pc.bold('Options')}
|
|
15
|
+
--goal <text> Overrides the transcript's own goal (or the inferred one), if any.
|
|
16
|
+
--drop-below <0-1> Relevance floor below which an entry is dropped. (default ${DEFAULT_POLICY.dropBelow})
|
|
17
|
+
--summarize-below <0-1> Relevance floor below which an entry is summarized. (default ${DEFAULT_POLICY.summarizeBelow})
|
|
18
|
+
--json Print machine-readable JSON instead of the report.
|
|
19
|
+
--help Show this help.
|
|
20
|
+
--version Print the installed version.
|
|
21
|
+
|
|
22
|
+
${pc.bold('Transcript formats (auto-detected)')}
|
|
23
|
+
ctxjev's own: { "goal": "...", "entries": [{ "id", "role", "toolName"?, "content", "timestamp" }] }
|
|
24
|
+
Claude Code: a real session .jsonl (transcript_path, or ~/.claude/projects/*/*.jsonl) — the
|
|
25
|
+
goal is inferred from your most recent chat message unless --goal overrides it.
|
|
26
|
+
See examples/sample-transcripts in the ctxjev repo for one of each.
|
|
27
|
+
`;
|
|
28
|
+
function fail(message) {
|
|
29
|
+
console.error(`${pc.red('✖')} ${message}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
async function runAnalyze(argv) {
|
|
33
|
+
const { positionals, values } = parseArgs({
|
|
34
|
+
args: argv,
|
|
35
|
+
allowPositionals: true,
|
|
36
|
+
options: {
|
|
37
|
+
goal: { type: 'string' },
|
|
38
|
+
'drop-below': { type: 'string' },
|
|
39
|
+
'summarize-below': { type: 'string' },
|
|
40
|
+
json: { type: 'boolean', default: false },
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const [file] = positionals;
|
|
44
|
+
if (!file)
|
|
45
|
+
fail('missing <transcript.json> — see `ctxjev --help`');
|
|
46
|
+
if (!process.env.TYPESAFE_API_KEY) {
|
|
47
|
+
fail('TYPESAFE_API_KEY is not set — get one at console.typesafe.ai/settings/keys');
|
|
48
|
+
}
|
|
49
|
+
let raw;
|
|
50
|
+
try {
|
|
51
|
+
raw = await readFile(file, 'utf8');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
fail(`couldn't read ${file}`);
|
|
55
|
+
}
|
|
56
|
+
const transcript = parseTranscript(raw);
|
|
57
|
+
const goal = values.goal ?? transcript.goal;
|
|
58
|
+
if (!goal)
|
|
59
|
+
fail('no goal — pass --goal or set "goal" in the transcript file');
|
|
60
|
+
const policy = {
|
|
61
|
+
dropBelow: values['drop-below'] ? Number(values['drop-below']) : DEFAULT_POLICY.dropBelow,
|
|
62
|
+
summarizeBelow: values['summarize-below'] ? Number(values['summarize-below']) : DEFAULT_POLICY.summarizeBelow,
|
|
63
|
+
recencyWeight: DEFAULT_POLICY.recencyWeight,
|
|
64
|
+
};
|
|
65
|
+
const usage = { inputTokens: 0, outputTokens: 0 };
|
|
66
|
+
const decisions = await pruneContext(transcript.entries, goal, policy, {
|
|
67
|
+
onUsage: (chunkUsage) => {
|
|
68
|
+
usage.inputTokens += chunkUsage.inputTokens;
|
|
69
|
+
usage.outputTokens += chunkUsage.outputTokens;
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const savings = summarizeSavings(transcript.entries, decisions);
|
|
73
|
+
if (values.json) {
|
|
74
|
+
console.log(JSON.stringify({ decisions, savings, usage }, null, 2));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
console.log(formatReport(transcript.entries, decisions, savings, usage));
|
|
78
|
+
}
|
|
79
|
+
async function main() {
|
|
80
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
81
|
+
if (!command || command === '--help' || command === '-h') {
|
|
82
|
+
console.log(HELP);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (command === '--version' || command === '-v') {
|
|
86
|
+
console.log(VERSION);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (command === 'analyze') {
|
|
90
|
+
await runAnalyze(rest);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
fail(`unknown command "${command}" — see \`ctxjev --help\``);
|
|
94
|
+
}
|
|
95
|
+
main().catch((err) => {
|
|
96
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
97
|
+
});
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/report.d.ts
ADDED
package/dist/report.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { estimateCostUsd } from './cost.js';
|
|
3
|
+
const ACTION_COLOR = {
|
|
4
|
+
keep: pc.green,
|
|
5
|
+
summarize: pc.yellow,
|
|
6
|
+
drop: pc.red,
|
|
7
|
+
};
|
|
8
|
+
const ACTION_WIDTH = Math.max(...Object.keys(ACTION_COLOR).map((a) => a.length));
|
|
9
|
+
function truncate(text, max = 60) {
|
|
10
|
+
const oneLine = text.replace(/\s+/g, ' ').trim();
|
|
11
|
+
return oneLine.length > max ? `${oneLine.slice(0, max - 1)}…` : oneLine;
|
|
12
|
+
}
|
|
13
|
+
export function formatReport(entries, decisions, savings, usage) {
|
|
14
|
+
const decisionByEntryId = new Map(decisions.map((d) => [d.entryId, d]));
|
|
15
|
+
const lines = [];
|
|
16
|
+
const idWidth = Math.max(2, ...entries.map((e) => e.id.length));
|
|
17
|
+
const roleWidth = Math.max(4, ...entries.map((e) => (e.toolName ?? e.role).length));
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const decision = decisionByEntryId.get(entry.id);
|
|
20
|
+
if (!decision)
|
|
21
|
+
continue;
|
|
22
|
+
const id = entry.id.padEnd(idWidth);
|
|
23
|
+
const role = (entry.toolName ?? entry.role).padEnd(roleWidth);
|
|
24
|
+
const score = decision.combinedScore.toFixed(2);
|
|
25
|
+
const action = ACTION_COLOR[decision.action](decision.action.padEnd(ACTION_WIDTH));
|
|
26
|
+
lines.push(` ${pc.dim(id)} ${role} ${action} ${pc.dim(`score ${score}`)} ${truncate(entry.content)}`);
|
|
27
|
+
}
|
|
28
|
+
lines.push('');
|
|
29
|
+
lines.push(`${pc.bold(String(savings.keptEntries))} kept, ` +
|
|
30
|
+
`${pc.bold(String(savings.summarizedEntries))} summarized, ` +
|
|
31
|
+
`${pc.bold(String(savings.droppedEntries))} dropped ` +
|
|
32
|
+
`${pc.dim(`(of ${savings.totalEntries} entries)`)}`);
|
|
33
|
+
const savedPct = savings.totalTokens === 0 ? 0 : Math.round((savings.savedTokens / savings.totalTokens) * 100);
|
|
34
|
+
lines.push(pc.dim(`~${savings.savedTokens.toLocaleString()} / ${savings.totalTokens.toLocaleString()} tokens saved (${savedPct}%)`));
|
|
35
|
+
const costUsd = estimateCostUsd(usage);
|
|
36
|
+
lines.push(pc.dim(`Jev cost: ${usage.inputTokens.toLocaleString()} input tokens, ${usage.outputTokens.toLocaleString()} output tokens (free) — ~$${costUsd.toFixed(6)}`));
|
|
37
|
+
return lines.join('\n');
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAE3C,MAAM,YAAY,GAA2D;IAC3E,IAAI,EAAE,EAAE,CAAC,KAAK;IACd,SAAS,EAAE,EAAE,CAAC,MAAM;IACpB,IAAI,EAAE,EAAE,CAAC,GAAG;CACb,CAAA;AACD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAEhF,SAAS,QAAQ,CAAC,IAAY,EAAE,GAAG,GAAG,EAAE;IACtC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;IAChD,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgB,EAAE,SAA0B,EAAE,OAAsB,EAAE,KAAe;IAChH,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACvE,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;IAEnF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAChD,IAAI,CAAC,QAAQ;YAAE,SAAQ;QAEvB,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;QACnC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC/C,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAA;QAElF,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,MAAM,KAAK,EAAE,CAAC,GAAG,CAAC,SAAS,KAAK,EAAE,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC5G,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CACR,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,SAAS;QAC9C,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,eAAe;QAC5D,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,WAAW;QACrD,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,OAAO,CAAC,YAAY,WAAW,CAAC,EAAE,CACtD,CAAA;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,CAAA;IAC9G,KAAK,CAAC,IAAI,CACR,EAAE,CAAC,GAAG,CACJ,IAAI,OAAO,CAAC,WAAW,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,cAAc,EAAE,kBAAkB,QAAQ,IAAI,CACjH,CACF,CAAA;IAED,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACtC,KAAK,CAAC,IAAI,CACR,EAAE,CAAC,GAAG,CACJ,aAAa,KAAK,CAAC,WAAW,CAAC,cAAc,EAAE,kBAAkB,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,6BAA6B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CACtJ,CACF,CAAA;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Entry } from 'ctxjev-core';
|
|
2
|
+
export type TranscriptFile = {
|
|
3
|
+
/** Optional — omit to require --goal on the command line instead. */
|
|
4
|
+
goal?: string;
|
|
5
|
+
entries: Entry[];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Accepts two formats, auto-detected: ctxjev's own `{ goal?, entries }` (a single JSON document
|
|
9
|
+
* — see examples/sample-transcripts) or a real Claude Code session transcript (`.jsonl`, one
|
|
10
|
+
* record per line, from `transcript_path` or `~/.claude/projects/*/*.jsonl`). ctxjev's own format
|
|
11
|
+
* parses as one JSON value across the whole file; a `.jsonl` file does not (multiple top-level
|
|
12
|
+
* values, one per line), so a `JSON.parse` failure is what triggers the Claude Code path rather
|
|
13
|
+
* than needing a file extension or an explicit flag.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseTranscript(raw: string): TranscriptFile;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { inferGoalFromEntries, parseClaudeCodeTranscript } from 'ctxjev-core';
|
|
2
|
+
/**
|
|
3
|
+
* Accepts two formats, auto-detected: ctxjev's own `{ goal?, entries }` (a single JSON document
|
|
4
|
+
* — see examples/sample-transcripts) or a real Claude Code session transcript (`.jsonl`, one
|
|
5
|
+
* record per line, from `transcript_path` or `~/.claude/projects/*/*.jsonl`). ctxjev's own format
|
|
6
|
+
* parses as one JSON value across the whole file; a `.jsonl` file does not (multiple top-level
|
|
7
|
+
* values, one per line), so a `JSON.parse` failure is what triggers the Claude Code path rather
|
|
8
|
+
* than needing a file extension or an explicit flag.
|
|
9
|
+
*/
|
|
10
|
+
export function parseTranscript(raw) {
|
|
11
|
+
let parsedJson;
|
|
12
|
+
try {
|
|
13
|
+
parsedJson = JSON.parse(raw);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
// Not a single JSON document at all — most likely a Claude Code .jsonl transcript instead.
|
|
17
|
+
// A genuinely malformed ctxjev-format file (valid JSON, wrong shape) never reaches this
|
|
18
|
+
// branch; parseCtxjevFormat below throws its own specific error for that case.
|
|
19
|
+
const entries = parseClaudeCodeTranscript(raw);
|
|
20
|
+
if (entries.length === 0) {
|
|
21
|
+
throw new Error('could not parse this file as either a ctxjev transcript (a single JSON document with an "entries" array) or a Claude Code session .jsonl');
|
|
22
|
+
}
|
|
23
|
+
return { goal: inferGoalFromEntries(entries), entries };
|
|
24
|
+
}
|
|
25
|
+
return parseCtxjevFormat(parsedJson);
|
|
26
|
+
}
|
|
27
|
+
function parseCtxjevFormat(parsed) {
|
|
28
|
+
if (typeof parsed !== 'object' || parsed === null || !('entries' in parsed)) {
|
|
29
|
+
throw new Error('transcript file must be a JSON object with an "entries" array — see examples/sample-transcripts');
|
|
30
|
+
}
|
|
31
|
+
const { entries, goal } = parsed;
|
|
32
|
+
if (!Array.isArray(entries)) {
|
|
33
|
+
throw new Error('"entries" must be an array');
|
|
34
|
+
}
|
|
35
|
+
if (goal !== undefined && typeof goal !== 'string') {
|
|
36
|
+
throw new Error('"goal" must be a string when present');
|
|
37
|
+
}
|
|
38
|
+
return { goal, entries: entries };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=transcript.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.js","sourceRoot":"","sources":["../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAc,MAAM,aAAa,CAAA;AAQzF;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,UAAmB,CAAA;IACvB,IAAI,CAAC;QACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,2FAA2F;QAC3F,wFAAwF;QACxF,+EAA+E;QAC/E,MAAM,OAAO,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAA;QAC9C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,0IAA0I,CAC3I,CAAA;QACH,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAA;IACzD,CAAC;IAED,OAAO,iBAAiB,CAAC,UAAU,CAAC,CAAA;AACtC,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAe;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,EAAE,CAAC;QAC5E,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAA;IACpH,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAA8C,CAAA;IAExE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAA;IAC/C,CAAC;IAED,IAAI,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;IACzD,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAkB,EAAE,CAAA;AAC9C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ctxjev-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for ctxjev — analyze an agent transcript and report what Jev would keep, drop, or summarize.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/x96x64/ctxjev.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/x96x64/ctxjev/tree/main/packages/cli#readme",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"bin": {
|
|
15
|
+
"ctxjev": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"picocolors": "^1.1.1",
|
|
22
|
+
"ctxjev-core": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^22",
|
|
26
|
+
"typescript": "^5.6.0",
|
|
27
|
+
"vitest": "^2.1.9"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -b",
|
|
31
|
+
"test": "vitest run"
|
|
32
|
+
}
|
|
33
|
+
}
|