trickle-cli 0.1.200 → 0.1.202

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.
@@ -98,7 +98,7 @@ async function runDemo() {
98
98
  console.log('');
99
99
  console.log(chalk_1.default.bold.cyan(' ╔══════════════════════════════════════════╗'));
100
100
  console.log(chalk_1.default.bold.cyan(' ║ trickle — Live Demo ║'));
101
- console.log(chalk_1.default.bold.cyan(' ║ Runtime Observability in 60 Seconds ║'));
101
+ console.log(chalk_1.default.bold.cyan(' ║ Zero-Code Observability in 60 Seconds ║'));
102
102
  console.log(chalk_1.default.bold.cyan(' ╚══════════════════════════════════════════╝'));
103
103
  // Step 1: Create demo project
104
104
  printSection('1. Creating demo project');
@@ -208,9 +208,15 @@ async function runDemo() {
208
208
  console.log(chalk_1.default.bold.cyan(' ║ ║'));
209
209
  console.log(chalk_1.default.bold.cyan(' ║ npm install -g trickle-cli ║'));
210
210
  console.log(chalk_1.default.bold.cyan(' ║ trickle init ║'));
211
- console.log(chalk_1.default.bold.cyan(' ║ trickle run ║'));
211
+ console.log(chalk_1.default.bold.cyan(' ║ trickle run node app.js ║'));
212
212
  console.log(chalk_1.default.bold.cyan(' ║ ║'));
213
- console.log(chalk_1.default.bold.cyan(' ║ 31 MCP tools | 16 integrations ║'));
213
+ console.log(chalk_1.default.bold.cyan(' ║ Also try: ║'));
214
+ console.log(chalk_1.default.bold.cyan(' ║ trickle eval — A-F grading ║'));
215
+ console.log(chalk_1.default.bold.cyan(' ║ trickle why — root cause ║'));
216
+ console.log(chalk_1.default.bold.cyan(' ║ trickle cost-report — LLM costs ║'));
217
+ console.log(chalk_1.default.bold.cyan(' ║ trickle summarize — trace summary ║'));
218
+ console.log(chalk_1.default.bold.cyan(' ║ ║'));
219
+ console.log(chalk_1.default.bold.cyan(' ║ 38 MCP tools | Free, local, zero-code ║'));
214
220
  console.log(chalk_1.default.bold.cyan(' ║ github.com/yiheinchai/trickle ║'));
215
221
  console.log(chalk_1.default.bold.cyan(' ╚══════════════════════════════════════════╝'));
216
222
  console.log('');
@@ -0,0 +1,11 @@
1
+ /**
2
+ * trickle playback — Step-by-step replay of agent execution.
3
+ *
4
+ * Reads agents.jsonl, llm.jsonl, mcp.jsonl and plays back events
5
+ * in chronological order with timing. Local-first: instant replay
6
+ * from local JSONL files, no server needed.
7
+ */
8
+ export declare function playbackCommand(opts: {
9
+ json?: boolean;
10
+ speed?: string;
11
+ }): void;
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ /**
3
+ * trickle playback — Step-by-step replay of agent execution.
4
+ *
5
+ * Reads agents.jsonl, llm.jsonl, mcp.jsonl and plays back events
6
+ * in chronological order with timing. Local-first: instant replay
7
+ * from local JSONL files, no server needed.
8
+ */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
42
+ var __importDefault = (this && this.__importDefault) || function (mod) {
43
+ return (mod && mod.__esModule) ? mod : { "default": mod };
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.playbackCommand = playbackCommand;
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const chalk_1 = __importDefault(require("chalk"));
50
+ function readJsonl(fp) {
51
+ if (!fs.existsSync(fp))
52
+ return [];
53
+ return fs.readFileSync(fp, 'utf-8').split('\n').filter(Boolean)
54
+ .map(l => { try {
55
+ return JSON.parse(l);
56
+ }
57
+ catch {
58
+ return null;
59
+ } }).filter(Boolean);
60
+ }
61
+ function playbackCommand(opts) {
62
+ const dir = process.env.TRICKLE_LOCAL_DIR || path.join(process.cwd(), '.trickle');
63
+ const events = [];
64
+ // Collect events from all sources
65
+ for (const e of readJsonl(path.join(dir, 'agents.jsonl'))) {
66
+ const evt = e.event || '?';
67
+ const name = e.chain || e.tool || '';
68
+ let icon = '→';
69
+ if (evt.includes('start'))
70
+ icon = '▶';
71
+ if (evt.includes('end'))
72
+ icon = '■';
73
+ if (evt.includes('error'))
74
+ icon = '✗';
75
+ if (evt === 'action')
76
+ icon = '⚒';
77
+ if (evt === 'finish')
78
+ icon = '✔';
79
+ let detail = '';
80
+ if (e.toolInput)
81
+ detail = `→ ${String(e.toolInput).substring(0, 80)}`;
82
+ if (e.output)
83
+ detail = `← ${String(e.output).substring(0, 80)}`;
84
+ if (e.thought)
85
+ detail = `💭 ${String(e.thought).substring(0, 80)}`;
86
+ if (e.error)
87
+ detail = `✗ ${String(e.error).substring(0, 80)}`;
88
+ events.push({
89
+ timestamp: e.timestamp || 0,
90
+ source: `agent:${evt}`,
91
+ icon,
92
+ title: `${name} [${evt}]`,
93
+ detail,
94
+ durationMs: e.durationMs,
95
+ });
96
+ }
97
+ for (const c of readJsonl(path.join(dir, 'llm.jsonl'))) {
98
+ const err = c.error ? ` ERR: ${String(c.error).substring(0, 40)}` : '';
99
+ events.push({
100
+ timestamp: c.timestamp || 0,
101
+ source: 'llm',
102
+ icon: '✦',
103
+ title: `${c.provider}/${c.model}`,
104
+ detail: `${c.totalTokens || 0}tok ${c.estimatedCostUsd ? '$' + c.estimatedCostUsd.toFixed(4) : ''} → ${(c.inputPreview || '').substring(0, 50)}${err}`,
105
+ durationMs: c.durationMs,
106
+ cost: c.estimatedCostUsd,
107
+ });
108
+ }
109
+ for (const m of readJsonl(path.join(dir, 'mcp.jsonl'))) {
110
+ if (m.tool === '__list_tools')
111
+ continue;
112
+ const dir2 = m.direction === 'outgoing' ? '→' : '←';
113
+ events.push({
114
+ timestamp: m.timestamp || 0,
115
+ source: 'mcp',
116
+ icon: dir2,
117
+ title: `MCP: ${m.tool}`,
118
+ detail: m.resultPreview ? `← ${String(m.resultPreview).substring(0, 60)}` : '',
119
+ durationMs: m.durationMs,
120
+ });
121
+ }
122
+ if (events.length === 0) {
123
+ console.log(chalk_1.default.yellow(' No agent/LLM/MCP events to replay.'));
124
+ return;
125
+ }
126
+ // Sort chronologically
127
+ events.sort((a, b) => a.timestamp - b.timestamp);
128
+ if (opts.json) {
129
+ console.log(JSON.stringify(events, null, 2));
130
+ return;
131
+ }
132
+ // Playback
133
+ console.log('');
134
+ console.log(chalk_1.default.bold(' trickle playback'));
135
+ console.log(chalk_1.default.gray(' ' + '─'.repeat(60)));
136
+ console.log(chalk_1.default.gray(` ${events.length} events | replaying chronologically`));
137
+ console.log('');
138
+ const startTs = events[0].timestamp;
139
+ let cumulativeCost = 0;
140
+ for (let i = 0; i < events.length; i++) {
141
+ const e = events[i];
142
+ const elapsed = e.timestamp - startTs;
143
+ const timeStr = formatTime(elapsed);
144
+ const durStr = e.durationMs ? chalk_1.default.gray(` (${e.durationMs.toFixed(0)}ms)`) : '';
145
+ if (e.cost)
146
+ cumulativeCost += e.cost;
147
+ const iconColor = e.source === 'llm' ? chalk_1.default.magenta : e.source === 'mcp' ? chalk_1.default.green :
148
+ e.icon === '✗' ? chalk_1.default.red : e.icon === '✔' ? chalk_1.default.green : chalk_1.default.blue;
149
+ console.log(` ${chalk_1.default.gray(timeStr)} ${iconColor(e.icon)} ${chalk_1.default.bold(e.title)}${durStr}`);
150
+ if (e.detail) {
151
+ console.log(` ${chalk_1.default.gray(' ')} ${chalk_1.default.gray(e.detail)}`);
152
+ }
153
+ }
154
+ // Summary
155
+ const totalDuration = events[events.length - 1].timestamp - startTs;
156
+ console.log('');
157
+ console.log(chalk_1.default.gray(' ' + '─'.repeat(60)));
158
+ console.log(` ${events.length} events in ${formatTime(totalDuration)}${cumulativeCost > 0 ? ` | $${cumulativeCost.toFixed(4)} total cost` : ''}`);
159
+ console.log('');
160
+ }
161
+ function formatTime(ms) {
162
+ if (ms >= 60000)
163
+ return `${(ms / 60000).toFixed(1)}m`;
164
+ if (ms >= 1000)
165
+ return `${(ms / 1000).toFixed(1)}s`;
166
+ return `${ms}ms`;
167
+ }
package/dist/index.js CHANGED
@@ -920,6 +920,15 @@ program
920
920
  const { whyCommand } = await Promise.resolve().then(() => __importStar(require("./commands/why")));
921
921
  whyCommand(query, opts);
922
922
  });
923
+ // trickle playback
924
+ program
925
+ .command("playback")
926
+ .description("Replay agent execution step-by-step — chronological timeline of all decisions")
927
+ .option("--json", "Output structured JSON")
928
+ .action(async (opts) => {
929
+ const { playbackCommand } = await Promise.resolve().then(() => __importStar(require("./commands/playback")));
930
+ playbackCommand(opts);
931
+ });
923
932
  // trickle summarize
924
933
  program
925
934
  .command("summarize")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trickle-cli",
3
- "version": "0.1.200",
3
+ "version": "0.1.202",
4
4
  "description": "Zero-code runtime observability for JS/Python + AI agent debugging. Traces LangChain, CrewAI, OpenAI, Anthropic, Gemini. Eval, security, compliance, cost tracking. Free, local-first.",
5
5
  "keywords": ["observability", "tracing", "llm", "openai", "anthropic", "langchain", "crewai", "agent", "mcp", "debugging", "typescript", "python", "security", "eval", "compliance"],
6
6
  "bin": {
@@ -65,7 +65,7 @@ export async function runDemo(): Promise<void> {
65
65
  console.log('');
66
66
  console.log(chalk.bold.cyan(' ╔══════════════════════════════════════════╗'));
67
67
  console.log(chalk.bold.cyan(' ║ trickle — Live Demo ║'));
68
- console.log(chalk.bold.cyan(' ║ Runtime Observability in 60 Seconds ║'));
68
+ console.log(chalk.bold.cyan(' ║ Zero-Code Observability in 60 Seconds ║'));
69
69
  console.log(chalk.bold.cyan(' ╚══════════════════════════════════════════╝'));
70
70
 
71
71
  // Step 1: Create demo project
@@ -175,9 +175,15 @@ export async function runDemo(): Promise<void> {
175
175
  console.log(chalk.bold.cyan(' ║ ║'));
176
176
  console.log(chalk.bold.cyan(' ║ npm install -g trickle-cli ║'));
177
177
  console.log(chalk.bold.cyan(' ║ trickle init ║'));
178
- console.log(chalk.bold.cyan(' ║ trickle run ║'));
178
+ console.log(chalk.bold.cyan(' ║ trickle run node app.js ║'));
179
179
  console.log(chalk.bold.cyan(' ║ ║'));
180
- console.log(chalk.bold.cyan(' ║ 31 MCP tools | 16 integrations ║'));
180
+ console.log(chalk.bold.cyan(' ║ Also try: ║'));
181
+ console.log(chalk.bold.cyan(' ║ trickle eval — A-F grading ║'));
182
+ console.log(chalk.bold.cyan(' ║ trickle why — root cause ║'));
183
+ console.log(chalk.bold.cyan(' ║ trickle cost-report — LLM costs ║'));
184
+ console.log(chalk.bold.cyan(' ║ trickle summarize — trace summary ║'));
185
+ console.log(chalk.bold.cyan(' ║ ║'));
186
+ console.log(chalk.bold.cyan(' ║ 38 MCP tools | Free, local, zero-code ║'));
181
187
  console.log(chalk.bold.cyan(' ║ github.com/yiheinchai/trickle ║'));
182
188
  console.log(chalk.bold.cyan(' ╚══════════════════════════════════════════╝'));
183
189
  console.log('');
@@ -0,0 +1,137 @@
1
+ /**
2
+ * trickle playback — Step-by-step replay of agent execution.
3
+ *
4
+ * Reads agents.jsonl, llm.jsonl, mcp.jsonl and plays back events
5
+ * in chronological order with timing. Local-first: instant replay
6
+ * from local JSONL files, no server needed.
7
+ */
8
+
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import chalk from 'chalk';
12
+
13
+ function readJsonl(fp: string): any[] {
14
+ if (!fs.existsSync(fp)) return [];
15
+ return fs.readFileSync(fp, 'utf-8').split('\n').filter(Boolean)
16
+ .map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
17
+ }
18
+
19
+ interface PlaybackEvent {
20
+ timestamp: number;
21
+ source: string;
22
+ icon: string;
23
+ title: string;
24
+ detail: string;
25
+ durationMs?: number;
26
+ cost?: number;
27
+ }
28
+
29
+ export function playbackCommand(opts: { json?: boolean; speed?: string }): void {
30
+ const dir = process.env.TRICKLE_LOCAL_DIR || path.join(process.cwd(), '.trickle');
31
+ const events: PlaybackEvent[] = [];
32
+
33
+ // Collect events from all sources
34
+ for (const e of readJsonl(path.join(dir, 'agents.jsonl'))) {
35
+ const evt = e.event || '?';
36
+ const name = e.chain || e.tool || '';
37
+ let icon = '→';
38
+ if (evt.includes('start')) icon = '▶';
39
+ if (evt.includes('end')) icon = '■';
40
+ if (evt.includes('error')) icon = '✗';
41
+ if (evt === 'action') icon = '⚒';
42
+ if (evt === 'finish') icon = '✔';
43
+
44
+ let detail = '';
45
+ if (e.toolInput) detail = `→ ${String(e.toolInput).substring(0, 80)}`;
46
+ if (e.output) detail = `← ${String(e.output).substring(0, 80)}`;
47
+ if (e.thought) detail = `💭 ${String(e.thought).substring(0, 80)}`;
48
+ if (e.error) detail = `✗ ${String(e.error).substring(0, 80)}`;
49
+
50
+ events.push({
51
+ timestamp: e.timestamp || 0,
52
+ source: `agent:${evt}`,
53
+ icon,
54
+ title: `${name} [${evt}]`,
55
+ detail,
56
+ durationMs: e.durationMs,
57
+ });
58
+ }
59
+
60
+ for (const c of readJsonl(path.join(dir, 'llm.jsonl'))) {
61
+ const err = c.error ? ` ERR: ${String(c.error).substring(0, 40)}` : '';
62
+ events.push({
63
+ timestamp: c.timestamp || 0,
64
+ source: 'llm',
65
+ icon: '✦',
66
+ title: `${c.provider}/${c.model}`,
67
+ detail: `${c.totalTokens || 0}tok ${c.estimatedCostUsd ? '$' + c.estimatedCostUsd.toFixed(4) : ''} → ${(c.inputPreview || '').substring(0, 50)}${err}`,
68
+ durationMs: c.durationMs,
69
+ cost: c.estimatedCostUsd,
70
+ });
71
+ }
72
+
73
+ for (const m of readJsonl(path.join(dir, 'mcp.jsonl'))) {
74
+ if (m.tool === '__list_tools') continue;
75
+ const dir2 = m.direction === 'outgoing' ? '→' : '←';
76
+ events.push({
77
+ timestamp: m.timestamp || 0,
78
+ source: 'mcp',
79
+ icon: dir2,
80
+ title: `MCP: ${m.tool}`,
81
+ detail: m.resultPreview ? `← ${String(m.resultPreview).substring(0, 60)}` : '',
82
+ durationMs: m.durationMs,
83
+ });
84
+ }
85
+
86
+ if (events.length === 0) {
87
+ console.log(chalk.yellow(' No agent/LLM/MCP events to replay.'));
88
+ return;
89
+ }
90
+
91
+ // Sort chronologically
92
+ events.sort((a, b) => a.timestamp - b.timestamp);
93
+
94
+ if (opts.json) {
95
+ console.log(JSON.stringify(events, null, 2));
96
+ return;
97
+ }
98
+
99
+ // Playback
100
+ console.log('');
101
+ console.log(chalk.bold(' trickle playback'));
102
+ console.log(chalk.gray(' ' + '─'.repeat(60)));
103
+ console.log(chalk.gray(` ${events.length} events | replaying chronologically`));
104
+ console.log('');
105
+
106
+ const startTs = events[0].timestamp;
107
+ let cumulativeCost = 0;
108
+
109
+ for (let i = 0; i < events.length; i++) {
110
+ const e = events[i];
111
+ const elapsed = e.timestamp - startTs;
112
+ const timeStr = formatTime(elapsed);
113
+ const durStr = e.durationMs ? chalk.gray(` (${e.durationMs.toFixed(0)}ms)`) : '';
114
+ if (e.cost) cumulativeCost += e.cost;
115
+
116
+ const iconColor = e.source === 'llm' ? chalk.magenta : e.source === 'mcp' ? chalk.green :
117
+ e.icon === '✗' ? chalk.red : e.icon === '✔' ? chalk.green : chalk.blue;
118
+
119
+ console.log(` ${chalk.gray(timeStr)} ${iconColor(e.icon)} ${chalk.bold(e.title)}${durStr}`);
120
+ if (e.detail) {
121
+ console.log(` ${chalk.gray(' ')} ${chalk.gray(e.detail)}`);
122
+ }
123
+ }
124
+
125
+ // Summary
126
+ const totalDuration = events[events.length - 1].timestamp - startTs;
127
+ console.log('');
128
+ console.log(chalk.gray(' ' + '─'.repeat(60)));
129
+ console.log(` ${events.length} events in ${formatTime(totalDuration)}${cumulativeCost > 0 ? ` | $${cumulativeCost.toFixed(4)} total cost` : ''}`);
130
+ console.log('');
131
+ }
132
+
133
+ function formatTime(ms: number): string {
134
+ if (ms >= 60000) return `${(ms / 60000).toFixed(1)}m`;
135
+ if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`;
136
+ return `${ms}ms`;
137
+ }
package/src/index.ts CHANGED
@@ -953,6 +953,16 @@ program
953
953
  whyCommand(query, opts);
954
954
  });
955
955
 
956
+ // trickle playback
957
+ program
958
+ .command("playback")
959
+ .description("Replay agent execution step-by-step — chronological timeline of all decisions")
960
+ .option("--json", "Output structured JSON")
961
+ .action(async (opts) => {
962
+ const { playbackCommand } = await import("./commands/playback");
963
+ playbackCommand(opts);
964
+ });
965
+
956
966
  // trickle summarize
957
967
  program
958
968
  .command("summarize")