agentwaste-core 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/README.md +13 -0
- package/package.json +26 -0
- package/src/cli.js +331 -0
- package/src/index.js +4 -0
- package/src/report.js +544 -0
- package/src/scanner.js +1217 -0
- package/src/tools.js +803 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# agentwaste-core
|
|
2
|
+
|
|
3
|
+
Shared scanner/report engine used by the `agentmess` focused CLI tools.
|
|
4
|
+
|
|
5
|
+
This package is meant as a dependency for:
|
|
6
|
+
|
|
7
|
+
- `suckytraces`
|
|
8
|
+
- `keybarf`
|
|
9
|
+
- `tokengoblin`
|
|
10
|
+
- `tooltantrum`
|
|
11
|
+
- `agentmess`
|
|
12
|
+
|
|
13
|
+
It has no public `npx` command of its own.
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentwaste-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Shared local coding-agent scanner powering agentmess and the tiny gremlin CLI squad.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ai",
|
|
8
|
+
"agents",
|
|
9
|
+
"cli",
|
|
10
|
+
"observability",
|
|
11
|
+
"developer-tools"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": "./src/index.js",
|
|
21
|
+
"./cli": "./src/cli.js",
|
|
22
|
+
"./report": "./src/report.js",
|
|
23
|
+
"./scanner": "./src/scanner.js",
|
|
24
|
+
"./tools": "./src/tools.js"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { buildReport, calculationGuide } from "./report.js";
|
|
4
|
+
import { scanAgentWaste } from "./scanner.js";
|
|
5
|
+
import { buildToolReport, explainTool, getToolConfig, listToolConfigs } from "./tools.js";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_PACKAGE = { name: "agentmess", version: "0.1.0" };
|
|
8
|
+
|
|
9
|
+
export async function runAgentmessCli(argv, packageInfo = DEFAULT_PACKAGE) {
|
|
10
|
+
const parsed = parseArgs(argv, {
|
|
11
|
+
defaultCommand: "scan",
|
|
12
|
+
commands: new Set(["scan", "explain", "tools", "squad"]),
|
|
13
|
+
aliases: new Map([["list", "tools"]]),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
if (parsed.help) {
|
|
17
|
+
console.log(agentmessHelp(packageInfo));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (parsed.version) {
|
|
21
|
+
console.log(packageInfo.version);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (parsed.options.command === "tools" || parsed.options.command === "squad") {
|
|
25
|
+
console.log(toolSquadText(packageInfo));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (parsed.options.command === "explain") {
|
|
29
|
+
console.log(calculationGuide({ version: packageInfo.version, json: parsed.options.json }));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const startedAt = Date.now();
|
|
34
|
+
const spinner = startScanSpinner(parsed.options, "agentmess");
|
|
35
|
+
let stats;
|
|
36
|
+
try {
|
|
37
|
+
stats = await scanAgentWaste(parsed.options);
|
|
38
|
+
} finally {
|
|
39
|
+
spinner.stop();
|
|
40
|
+
}
|
|
41
|
+
const elapsedMs = Date.now() - startedAt;
|
|
42
|
+
const report = buildReport(stats, {
|
|
43
|
+
version: packageInfo.version,
|
|
44
|
+
elapsedMs,
|
|
45
|
+
color: parsed.options.color,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (parsed.options.json) {
|
|
49
|
+
console.log(JSON.stringify(report.json, null, 2));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log(report.text);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function runToolCli(argv, toolId, packageInfo = { name: toolId, version: "0.1.0" }) {
|
|
57
|
+
const config = getToolConfig(toolId);
|
|
58
|
+
const parsed = parseArgs(argv, {
|
|
59
|
+
defaultCommand: "scan",
|
|
60
|
+
commands: new Set(["scan", "show", "sniff", "roast", "explain"]),
|
|
61
|
+
aliases: new Map([
|
|
62
|
+
["show", "scan"],
|
|
63
|
+
["sniff", "scan"],
|
|
64
|
+
["roast", "scan"],
|
|
65
|
+
]),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (parsed.help) {
|
|
69
|
+
console.log(toolHelp(config, packageInfo));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (parsed.version) {
|
|
73
|
+
console.log(packageInfo.version);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (parsed.options.command === "explain") {
|
|
77
|
+
console.log(explainTool(toolId, { version: packageInfo.version, json: parsed.options.json }));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const startedAt = Date.now();
|
|
82
|
+
const spinner = startScanSpinner(parsed.options, toolId);
|
|
83
|
+
let stats;
|
|
84
|
+
try {
|
|
85
|
+
stats = await scanAgentWaste(parsed.options);
|
|
86
|
+
} finally {
|
|
87
|
+
spinner.stop();
|
|
88
|
+
}
|
|
89
|
+
const elapsedMs = Date.now() - startedAt;
|
|
90
|
+
const report = buildToolReport(stats, {
|
|
91
|
+
toolId,
|
|
92
|
+
version: packageInfo.version,
|
|
93
|
+
elapsedMs,
|
|
94
|
+
color: parsed.options.color,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (parsed.options.json) {
|
|
98
|
+
console.log(JSON.stringify(report.json, null, 2));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(report.text);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseArgs(argv, config) {
|
|
106
|
+
const args = argv.slice(2);
|
|
107
|
+
const options = {
|
|
108
|
+
command: config.defaultCommand,
|
|
109
|
+
homeDir: undefined,
|
|
110
|
+
projectDir: process.cwd(),
|
|
111
|
+
days: 365,
|
|
112
|
+
maxFiles: Number.MAX_SAFE_INTEGER,
|
|
113
|
+
json: false,
|
|
114
|
+
color: process.stdout.isTTY,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (args[0] && !args[0].startsWith("-")) {
|
|
118
|
+
const rawCommand = args.shift();
|
|
119
|
+
if (!config.commands.has(rawCommand)) throw new Error(`unknown command ${rawCommand}`);
|
|
120
|
+
options.command = config.aliases.get(rawCommand) ?? rawCommand;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let maxFilesExplicit = false;
|
|
124
|
+
|
|
125
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
126
|
+
const arg = args[i];
|
|
127
|
+
if (arg === "--help" || arg === "-h") return { help: true, options };
|
|
128
|
+
if (arg === "--version" || arg === "-v") return { version: true, options };
|
|
129
|
+
if (arg === "--json") {
|
|
130
|
+
options.json = true;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (arg === "--no-color") {
|
|
134
|
+
options.color = false;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (arg === "--all") {
|
|
138
|
+
options.days = "all";
|
|
139
|
+
if (!maxFilesExplicit) options.maxFiles = Number.MAX_SAFE_INTEGER;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (arg === "--home") {
|
|
143
|
+
i += 1;
|
|
144
|
+
if (!args[i]) throw new Error("--home requires a path");
|
|
145
|
+
options.homeDir = resolve(args[i]);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (arg === "--path") {
|
|
149
|
+
i += 1;
|
|
150
|
+
if (!args[i]) throw new Error("--path requires a path");
|
|
151
|
+
options.projectDir = resolve(args[i]);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (arg === "--days") {
|
|
155
|
+
i += 1;
|
|
156
|
+
if (!args[i]) throw new Error("--days requires a number or all");
|
|
157
|
+
options.days = parseDays(args[i], "--days");
|
|
158
|
+
if (options.days === "all" && !maxFilesExplicit) options.maxFiles = Number.MAX_SAFE_INTEGER;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (arg === "--max-files") {
|
|
162
|
+
i += 1;
|
|
163
|
+
if (!args[i]) throw new Error("--max-files requires a number");
|
|
164
|
+
maxFilesExplicit = true;
|
|
165
|
+
options.maxFiles = parsePositiveInt(args[i], "--max-files");
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
throw new Error(`unknown option ${arg}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { options };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function parseDays(value, flag) {
|
|
175
|
+
if (String(value).toLowerCase() === "all") return "all";
|
|
176
|
+
return parsePositiveInt(value, flag);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function parsePositiveInt(value, flag) {
|
|
180
|
+
const parsed = Number.parseInt(value, 10);
|
|
181
|
+
if (!Number.isFinite(parsed) || parsed <= 0) throw new Error(`${flag} must be a positive integer`);
|
|
182
|
+
return parsed;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function agentmessHelp(packageInfo) {
|
|
186
|
+
return `agentmess v${packageInfo.version}
|
|
187
|
+
|
|
188
|
+
Usage:
|
|
189
|
+
agentmess [scan] [options]
|
|
190
|
+
agentmess explain [options]
|
|
191
|
+
agentmess tools
|
|
192
|
+
|
|
193
|
+
Commands:
|
|
194
|
+
scan Full local agent postmortem (default)
|
|
195
|
+
explain Explain every report formula without scanning logs
|
|
196
|
+
tools Show the focused npx CLIs
|
|
197
|
+
|
|
198
|
+
Options:
|
|
199
|
+
--home <path> Home directory to scan (default: current user's home)
|
|
200
|
+
--path <path> Project directory to scan (default: cwd)
|
|
201
|
+
--days <number|all> Lookback window in days (default: 365)
|
|
202
|
+
--all Scan all available sessions (same as --days all)
|
|
203
|
+
--max-files <number> Maximum session files per source (default: unlimited)
|
|
204
|
+
--json Print machine-readable JSON
|
|
205
|
+
--no-color Disable ANSI color
|
|
206
|
+
--version Print version
|
|
207
|
+
--help Show help
|
|
208
|
+
|
|
209
|
+
Focused tools:
|
|
210
|
+
${listToolConfigs().map((tool) => ` npx ${tool.binName.padEnd(16)} ${tool.emoji} ${tool.npc}`).join("\n")}
|
|
211
|
+
|
|
212
|
+
The scanner runs locally. It counts redacted fingerprints and aggregate metrics;
|
|
213
|
+
it does not upload transcripts or print raw secret values.`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function toolHelp(config, packageInfo) {
|
|
217
|
+
return `${config.packageName} v${packageInfo.version}
|
|
218
|
+
|
|
219
|
+
${config.emoji} ${config.npc}: ${config.tagline}
|
|
220
|
+
|
|
221
|
+
Usage:
|
|
222
|
+
${config.binName} [scan] [options]
|
|
223
|
+
${config.binName} explain [options]
|
|
224
|
+
|
|
225
|
+
Aliases:
|
|
226
|
+
${config.binName} show
|
|
227
|
+
${config.binName} sniff
|
|
228
|
+
${config.binName} roast
|
|
229
|
+
|
|
230
|
+
Options:
|
|
231
|
+
--home <path> Home directory to scan (default: current user's home)
|
|
232
|
+
--path <path> Project directory to scan (default: cwd)
|
|
233
|
+
--days <number|all> Lookback window in days (default: 365)
|
|
234
|
+
--all Scan all available sessions (same as --days all)
|
|
235
|
+
--max-files <number> Maximum session files per source (default: unlimited)
|
|
236
|
+
--json Print machine-readable JSON
|
|
237
|
+
--no-color Disable ANSI color
|
|
238
|
+
--version Print version
|
|
239
|
+
--help Show help
|
|
240
|
+
|
|
241
|
+
Try:
|
|
242
|
+
npx ${config.binName}
|
|
243
|
+
npx ${config.binName} --days 7
|
|
244
|
+
npx ${config.binName} --json`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function toolSquadText(packageInfo) {
|
|
248
|
+
return [
|
|
249
|
+
`agentmess focused tools v${packageInfo.version}`,
|
|
250
|
+
"",
|
|
251
|
+
...listToolConfigs().map((tool) => `npx ${tool.binName.padEnd(16)} ${tool.emoji} ${tool.npc} — ${tool.tagline}`),
|
|
252
|
+
"",
|
|
253
|
+
"npx agentmess consolidated report for the focused checks",
|
|
254
|
+
].join("\n");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function narrate(options, toolId) {
|
|
258
|
+
if (options.json || !process.stdout.isTTY) return;
|
|
259
|
+
for (const line of narrationMessages(toolId)) {
|
|
260
|
+
process.stdout.write(`${ansi(options.color, "38;2;148;163;184", ` ${line}`)}\n`);
|
|
261
|
+
await sleep(170);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function narrationMessages(toolId) {
|
|
266
|
+
switch (toolId) {
|
|
267
|
+
case "suckytraces":
|
|
268
|
+
return ["scanning session logs.", "computing trace coverage."];
|
|
269
|
+
case "keybarf":
|
|
270
|
+
return ["scanning logs and config for secret fingerprints.", "checking whether fingerprints appear in model-facing records."];
|
|
271
|
+
case "tokengoblin":
|
|
272
|
+
return ["reading token usage records.", "computing fresh input, cache hit rate, and excess context."];
|
|
273
|
+
case "tooltantrum":
|
|
274
|
+
return ["counting tool calls and failures.", "computing retry recovery and unresolved failures."];
|
|
275
|
+
default:
|
|
276
|
+
return ["scanned the agent logs.", "numbers first, feelings later."];
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function sleep(ms) {
|
|
281
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function startScanSpinner(options, toolId) {
|
|
285
|
+
if (options.json || !process.stdout.isTTY) return { stop() {} };
|
|
286
|
+
|
|
287
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
288
|
+
const messages = spinnerMessages(toolId);
|
|
289
|
+
let index = 0;
|
|
290
|
+
|
|
291
|
+
const render = () => {
|
|
292
|
+
const frame = frames[index % frames.length];
|
|
293
|
+
const message = messages[Math.floor(index / 8) % messages.length];
|
|
294
|
+
process.stdout.write(`\r${ansi(options.color, "38;2;147;197;253", frame)} ${ansi(options.color, "38;2;148;163;184", message)} `);
|
|
295
|
+
index += 1;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
render();
|
|
299
|
+
const timer = setInterval(render, 80);
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
stop() {
|
|
303
|
+
clearInterval(timer);
|
|
304
|
+
process.stdout.write("\r\x1b[2K");
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function spinnerMessages(toolId) {
|
|
310
|
+
switch (toolId) {
|
|
311
|
+
case "suckytraces":
|
|
312
|
+
return ["scanning session logs...", "checking trace identifiers...", "computing trace coverage..."];
|
|
313
|
+
case "keybarf":
|
|
314
|
+
return ["scanning secret patterns...", "fingerprinting matches...", "checking model-facing records..."];
|
|
315
|
+
case "tokengoblin":
|
|
316
|
+
return ["reading token usage...", "computing fresh input...", "checking cache hit rate..."];
|
|
317
|
+
case "tooltantrum":
|
|
318
|
+
return ["counting tool calls...", "measuring failure rate...", "checking retry recovery..."];
|
|
319
|
+
default:
|
|
320
|
+
return ["scanning sessions...", "checking token usage...", "checking secrets...", "checking tool reliability...", "checking trace coverage..."];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function ansi(enabled, code, value) {
|
|
325
|
+
if (!enabled) return value;
|
|
326
|
+
return `\x1b[${code}m${value}\x1b[0m`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function color(code, value) {
|
|
330
|
+
return `\x1b[${code}m${value}\x1b[0m`;
|
|
331
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { runAgentmessCli, runToolCli } from "./cli.js";
|
|
2
|
+
export { buildReport, calculationGuide, toReportJson } from "./report.js";
|
|
3
|
+
export { scanAgentWaste } from "./scanner.js";
|
|
4
|
+
export { TOOL_IDS, buildToolReport, explainTool, getToolConfig, listToolConfigs } from "./tools.js";
|