@intentic/claude-plugin 1.317.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/.claude-plugin/plugin.json +82 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/bin/fileq +9 -0
- package/bin/intentic-notes-evidence +3 -0
- package/bin/intentic-stats +3 -0
- package/bin/retrieve-output +3 -0
- package/dist/fileq.mjs +147667 -0
- package/dist/notes-evidence.mjs +749 -0
- package/dist/post-bash.mjs +927 -0
- package/dist/post-edit.mjs +252 -0
- package/dist/prompt-submit.mjs +220 -0
- package/dist/retrieve-output.mjs +38 -0
- package/dist/session-start.mjs +1658 -0
- package/dist/stats.mjs +1847 -0
- package/generated/iq-nudge.txt +3 -0
- package/generated/output-styles/intentic.md +13 -0
- package/generated/skills/field-notes/SKILL.md +40 -0
- package/generated/skills/fileq/SKILL.md +56 -0
- package/generated/skills/iq/SKILL.md +38 -0
- package/generated/skills/stats/SKILL.md +11 -0
- package/hooks/hooks.json +49 -0
- package/package.json +60 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { createRequire as __intenticRequire } from 'node:module'; const require = __intenticRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/notes-evidence.ts
|
|
4
|
+
import { readdirSync as readdirSync2, statSync } from "node:fs";
|
|
5
|
+
import { join as join3 } from "node:path";
|
|
6
|
+
|
|
7
|
+
// ../agent-context/src/claude-transcript.ts
|
|
8
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
9
|
+
import { basename, dirname, join } from "node:path";
|
|
10
|
+
|
|
11
|
+
// ../agent-context/src/tool-calls.ts
|
|
12
|
+
import { isAbsolute, relative } from "node:path";
|
|
13
|
+
var DISPLAY_NAMES = {
|
|
14
|
+
bash: "Bash",
|
|
15
|
+
edit: "Edit",
|
|
16
|
+
write: "Write",
|
|
17
|
+
read: "Read",
|
|
18
|
+
grep: "Grep",
|
|
19
|
+
glob: "Glob",
|
|
20
|
+
list: "LS",
|
|
21
|
+
webfetch: "WebFetch",
|
|
22
|
+
websearch: "WebSearch",
|
|
23
|
+
task: "Task",
|
|
24
|
+
patch: "Edit"
|
|
25
|
+
};
|
|
26
|
+
var BROWSER_TOOL = /^mcp__.+__browser_(.+)$/;
|
|
27
|
+
var BROWSER_VERB_NAMES = { take_screenshot: "screenshot" };
|
|
28
|
+
var browserDisplayName = (raw) => {
|
|
29
|
+
const verb = BROWSER_TOOL.exec(raw)?.[1];
|
|
30
|
+
return verb === void 0 ? void 0 : `Browser ${(BROWSER_VERB_NAMES[verb] ?? verb).replaceAll("_", " ")}`;
|
|
31
|
+
};
|
|
32
|
+
var displayNameOf = (raw) => DISPLAY_NAMES[raw] ?? browserDisplayName(raw) ?? raw;
|
|
33
|
+
var CATEGORIES = [
|
|
34
|
+
["read", "read"],
|
|
35
|
+
["edit", "edit"],
|
|
36
|
+
["write", "edit"],
|
|
37
|
+
["multiedit", "edit"],
|
|
38
|
+
["notebookedit", "edit"],
|
|
39
|
+
["bash", "execute"],
|
|
40
|
+
["bashoutput", "execute"],
|
|
41
|
+
["killshell", "execute"],
|
|
42
|
+
["grep", "search"],
|
|
43
|
+
["glob", "search"],
|
|
44
|
+
["ls", "search"],
|
|
45
|
+
["websearch", "search"],
|
|
46
|
+
["webfetch", "fetch"],
|
|
47
|
+
["task", "other"]
|
|
48
|
+
];
|
|
49
|
+
var CATEGORY_BY_NAME = new Map(CATEGORIES);
|
|
50
|
+
var MCP_VERBS = [
|
|
51
|
+
["edit", "edit"],
|
|
52
|
+
["write", "edit"],
|
|
53
|
+
["read", "read"],
|
|
54
|
+
["search", "search"],
|
|
55
|
+
["fetch", "fetch"],
|
|
56
|
+
["delete", "delete"],
|
|
57
|
+
["move", "move"],
|
|
58
|
+
["run", "execute"],
|
|
59
|
+
["exec", "execute"]
|
|
60
|
+
];
|
|
61
|
+
var BROWSER_VERB_KINDS = {
|
|
62
|
+
navigate: "fetch",
|
|
63
|
+
navigate_back: "fetch",
|
|
64
|
+
snapshot: "read",
|
|
65
|
+
take_screenshot: "read",
|
|
66
|
+
console_messages: "read",
|
|
67
|
+
network_requests: "read",
|
|
68
|
+
network_request: "read",
|
|
69
|
+
tabs: "read"
|
|
70
|
+
};
|
|
71
|
+
var toolCategoryOf = (name) => {
|
|
72
|
+
const exact = CATEGORY_BY_NAME.get(name.toLowerCase());
|
|
73
|
+
if (exact !== void 0) {
|
|
74
|
+
return exact;
|
|
75
|
+
}
|
|
76
|
+
const browserVerb = BROWSER_TOOL.exec(name)?.[1];
|
|
77
|
+
if (browserVerb !== void 0) {
|
|
78
|
+
return BROWSER_VERB_KINDS[browserVerb] ?? "execute";
|
|
79
|
+
}
|
|
80
|
+
const segment = name.includes("__") ? name.split("__").pop() : name.includes(".") ? name.split(".").pop() : void 0;
|
|
81
|
+
if (segment === void 0) {
|
|
82
|
+
return "other";
|
|
83
|
+
}
|
|
84
|
+
const verb = segment.toLowerCase();
|
|
85
|
+
for (const [suffix, kind] of MCP_VERBS) {
|
|
86
|
+
if (verb === suffix || verb.endsWith(suffix)) {
|
|
87
|
+
return kind;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return "other";
|
|
91
|
+
};
|
|
92
|
+
var TARGET_KEYS = ["file_path", "filePath", "notebook_path", "command", "pattern", "url", "element", "path", "query"];
|
|
93
|
+
var toolTarget = (input) => {
|
|
94
|
+
if (typeof input !== "object" || input === null) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
const record = input;
|
|
98
|
+
for (const key of TARGET_KEYS) {
|
|
99
|
+
const value = record[key];
|
|
100
|
+
if (typeof value === "string") {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return void 0;
|
|
105
|
+
};
|
|
106
|
+
var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path"];
|
|
107
|
+
var toolPathsUnder = (input, root) => {
|
|
108
|
+
if (typeof input !== "object" || input === null) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
const record = input;
|
|
112
|
+
for (const key of PATH_KEYS) {
|
|
113
|
+
const value = record[key];
|
|
114
|
+
if (typeof value !== "string") {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const rel = isAbsolute(value) ? relative(root, value) : value;
|
|
118
|
+
return rel === "" || rel === "." || rel === ".." || rel.startsWith("../") || isAbsolute(rel) ? void 0 : [{ path: rel.split("\\").join("/") }];
|
|
119
|
+
}
|
|
120
|
+
return void 0;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// ../agent-context/src/claude-transcript.ts
|
|
124
|
+
var FAILURE_TEXT_CHARS = 400;
|
|
125
|
+
var stampOf = (line) => {
|
|
126
|
+
const at = line.timestamp === void 0 ? Number.NaN : Date.parse(line.timestamp);
|
|
127
|
+
return Number.isNaN(at) ? void 0 : at;
|
|
128
|
+
};
|
|
129
|
+
var textOf = (content) => {
|
|
130
|
+
if (typeof content === "string") {
|
|
131
|
+
return content;
|
|
132
|
+
}
|
|
133
|
+
if (!Array.isArray(content)) {
|
|
134
|
+
return "";
|
|
135
|
+
}
|
|
136
|
+
return content.map((block) => block["type"] === "text" && typeof block["text"] === "string" ? block["text"] : "").filter((text) => text !== "").join("\n");
|
|
137
|
+
};
|
|
138
|
+
var parse = (raw) => {
|
|
139
|
+
if (!raw.includes(`"type":"user"`) && !raw.includes(`"type":"assistant"`)) {
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(raw);
|
|
144
|
+
} catch {
|
|
145
|
+
return void 0;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var isMissing = (error) => {
|
|
149
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
150
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
151
|
+
};
|
|
152
|
+
var promptOf = (line) => {
|
|
153
|
+
if (line.type !== "user" || line.isMeta === true || line.isCompactSummary === true) {
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
156
|
+
const content = line.message?.content;
|
|
157
|
+
if (Array.isArray(content) && content.some((block) => block["type"] === "tool_result")) {
|
|
158
|
+
return void 0;
|
|
159
|
+
}
|
|
160
|
+
const text = textOf(content);
|
|
161
|
+
return text.startsWith("<local-command-") ? void 0 : text;
|
|
162
|
+
};
|
|
163
|
+
var callsOf = (line, options, subagent) => {
|
|
164
|
+
const content = line.message?.content;
|
|
165
|
+
if (line.type !== "assistant" || !Array.isArray(content)) {
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
const locate = options.locate ?? toolPathsUnder;
|
|
169
|
+
const at = stampOf(line);
|
|
170
|
+
return content.flatMap((block, index) => {
|
|
171
|
+
const raw = block["name"];
|
|
172
|
+
if (block["type"] !== "tool_use" || typeof raw !== "string") {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
const name = displayNameOf(raw);
|
|
176
|
+
const target = toolTarget(block["input"]);
|
|
177
|
+
const locations = locate(block["input"], options.root);
|
|
178
|
+
return [
|
|
179
|
+
{
|
|
180
|
+
id: typeof block["id"] === "string" ? block["id"] : `${at ?? 0}-${index}`,
|
|
181
|
+
name,
|
|
182
|
+
category: toolCategoryOf(name),
|
|
183
|
+
at,
|
|
184
|
+
subagent,
|
|
185
|
+
...target !== void 0 ? { target } : {},
|
|
186
|
+
...locations !== void 0 ? { locations } : {}
|
|
187
|
+
}
|
|
188
|
+
];
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
var failuresOf = (line) => {
|
|
192
|
+
const content = line.message?.content;
|
|
193
|
+
if (line.type !== "user" || !Array.isArray(content)) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
return content.flatMap(
|
|
197
|
+
(block) => block["type"] === "tool_result" && block["is_error"] === true && typeof block["tool_use_id"] === "string" ? [{ id: block["tool_use_id"], text: (typeof block["content"] === "string" ? block["content"] : textOf(block["content"])).slice(0, FAILURE_TEXT_CHARS) }] : []
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
var readClaudeTranscript = (text, options) => {
|
|
201
|
+
const turns = [];
|
|
202
|
+
let sessionId;
|
|
203
|
+
let cwd;
|
|
204
|
+
for (const raw of text.split("\n")) {
|
|
205
|
+
const line = parse(raw);
|
|
206
|
+
if (line === void 0) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
sessionId ??= line.sessionId;
|
|
210
|
+
cwd ??= line.cwd;
|
|
211
|
+
const prompt = promptOf(line);
|
|
212
|
+
if (prompt !== void 0) {
|
|
213
|
+
turns.push({ index: turns.length, at: stampOf(line) ?? turns.at(-1)?.at ?? 0, prompt, calls: [], failures: [] });
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
const turn = turns.at(-1);
|
|
217
|
+
if (turn === void 0) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
turn.calls.push(...callsOf(line, options, false));
|
|
221
|
+
turn.failures.push(...failuresOf(line));
|
|
222
|
+
}
|
|
223
|
+
return { sessionId, cwd, turns };
|
|
224
|
+
};
|
|
225
|
+
var subagentActivity = (file, options) => {
|
|
226
|
+
const dir = join(dirname(file), basename(file, ".jsonl"), "subagents");
|
|
227
|
+
let names;
|
|
228
|
+
try {
|
|
229
|
+
names = readdirSync(dir).filter((name) => name.endsWith(".jsonl"));
|
|
230
|
+
} catch {
|
|
231
|
+
return { calls: [], failures: [] };
|
|
232
|
+
}
|
|
233
|
+
const calls = [];
|
|
234
|
+
const failures = [];
|
|
235
|
+
for (const name of names) {
|
|
236
|
+
let text;
|
|
237
|
+
try {
|
|
238
|
+
text = readFileSync(join(dir, name), "utf8");
|
|
239
|
+
} catch {
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
for (const raw of text.split("\n")) {
|
|
243
|
+
const line = parse(raw);
|
|
244
|
+
if (line === void 0) {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
calls.push(...callsOf(line, options, true));
|
|
248
|
+
failures.push(...failuresOf(line).map((failure) => ({ ...failure, at: stampOf(line) })));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return { calls, failures };
|
|
252
|
+
};
|
|
253
|
+
var turnAt = (turns, at) => {
|
|
254
|
+
if (at === void 0) {
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
let found;
|
|
258
|
+
for (const turn of turns) {
|
|
259
|
+
if (turn.at <= at) {
|
|
260
|
+
found = turn;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return found;
|
|
264
|
+
};
|
|
265
|
+
var readClaudeSession = (file, options) => {
|
|
266
|
+
let text;
|
|
267
|
+
try {
|
|
268
|
+
text = readFileSync(file, "utf8");
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (isMissing(error)) {
|
|
271
|
+
return void 0;
|
|
272
|
+
}
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
const session = readClaudeTranscript(text, options);
|
|
276
|
+
if (session.turns.length === 0) {
|
|
277
|
+
return session;
|
|
278
|
+
}
|
|
279
|
+
const activity = subagentActivity(file, options);
|
|
280
|
+
for (const call of activity.calls) {
|
|
281
|
+
turnAt(session.turns, call.at)?.calls.push(call);
|
|
282
|
+
}
|
|
283
|
+
for (const { at, ...failure } of activity.failures) {
|
|
284
|
+
turnAt(session.turns, at)?.failures.push(failure);
|
|
285
|
+
}
|
|
286
|
+
for (const turn of session.turns) {
|
|
287
|
+
turn.calls.sort((left, right) => (left.at ?? 0) - (right.at ?? 0));
|
|
288
|
+
}
|
|
289
|
+
return session;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// ../output-cleaners/src/cleaners.mjs
|
|
293
|
+
var strip = (id, match, patterns) => ({
|
|
294
|
+
id,
|
|
295
|
+
match,
|
|
296
|
+
apply: (lines) => lines.filter((line) => !patterns.some((re) => re.test(line)))
|
|
297
|
+
});
|
|
298
|
+
var invocation = (word) => new RegExp(String.raw`(?<![\w.\-/])(?:${word})(?![\w.-])`);
|
|
299
|
+
var bodyBytes = (lines) => lines.length === 0 ? 0 : lines.reduce((sum, line) => sum + line.length, 0) + lines.length - 1;
|
|
300
|
+
var humanSize = (bytes) => bytes >= 1048576 ? `${(bytes / 1048576).toFixed(1)}M` : bytes >= 1024 ? `${(bytes / 1024).toFixed(1)}K` : `${bytes}B`;
|
|
301
|
+
var EXECUTABLE = /* @__PURE__ */ new Set(["x", "s", "t"]);
|
|
302
|
+
var permsToOctal = (perms) => {
|
|
303
|
+
const triad = (read, write, exec) => (perms[read] === "r" ? 4 : 0) + (perms[write] === "w" ? 2 : 0) + (EXECUTABLE.has(perms[exec]) ? 1 : 0);
|
|
304
|
+
const special = ("sS".includes(perms[3]) ? 4 : 0) + ("sS".includes(perms[6]) ? 2 : 0) + ("tT".includes(perms[9]) ? 1 : 0);
|
|
305
|
+
const mode = `${triad(1, 2, 3)}${triad(4, 5, 6)}${triad(7, 8, 9)}`;
|
|
306
|
+
return special > 0 ? `${special}${mode}` : mode;
|
|
307
|
+
};
|
|
308
|
+
var LS_DATE = /\s(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2}\s+(?:\d{4}|\d{1,2}:\d{2})|\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2})\s/;
|
|
309
|
+
var LS_MODE = /^[-dlbcps][rwxSsTt-]{9}[.+@]?$/;
|
|
310
|
+
var parseListingLine = (line) => {
|
|
311
|
+
const date = LS_DATE.exec(line);
|
|
312
|
+
if (date === null) {
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
const head = line.slice(0, date.index).split(/\s+/).filter((token) => token !== "");
|
|
316
|
+
if (head.length < 4 || !LS_MODE.test(head[0])) {
|
|
317
|
+
return void 0;
|
|
318
|
+
}
|
|
319
|
+
const size = head.reduceRight((found, token) => found === void 0 && /^\d+$/.test(token) ? Number(token) : found, void 0);
|
|
320
|
+
return { mode: permsToOctal(head[0]), directory: head[0].startsWith("d"), size, name: line.slice(date.index + date[0].length) };
|
|
321
|
+
};
|
|
322
|
+
var compactListing = (lines) => {
|
|
323
|
+
let parsed = 0;
|
|
324
|
+
const out = lines.flatMap((line) => {
|
|
325
|
+
if (/^total \d+$/.test(line)) {
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
const entry = parseListingLine(line);
|
|
329
|
+
if (entry === void 0) {
|
|
330
|
+
return [line];
|
|
331
|
+
}
|
|
332
|
+
parsed++;
|
|
333
|
+
if (entry.name === "." || entry.name === "..") {
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
if (entry.directory) {
|
|
337
|
+
return [`${entry.mode} ${entry.name}/`];
|
|
338
|
+
}
|
|
339
|
+
return [`${entry.mode} ${entry.name}${entry.size === void 0 ? "" : ` ${humanSize(entry.size)}`}`];
|
|
340
|
+
});
|
|
341
|
+
return parsed === 0 ? lines : out;
|
|
342
|
+
};
|
|
343
|
+
var PATH_RUN_MIN = 10;
|
|
344
|
+
var PATH_RUN_DIRS = 60;
|
|
345
|
+
var PATH_RUN_NAMES = 40;
|
|
346
|
+
var isPathLine = (line) => line !== "" && !/\s/.test(line) && !/:\d/.test(line) && line.length < 300;
|
|
347
|
+
var sharedRoot = (directories) => {
|
|
348
|
+
if (directories.length < 2) {
|
|
349
|
+
return "";
|
|
350
|
+
}
|
|
351
|
+
let prefix = directories[0];
|
|
352
|
+
for (const directory of directories) {
|
|
353
|
+
while (prefix !== "" && !directory.startsWith(prefix)) {
|
|
354
|
+
const shorter = prefix.slice(0, prefix.lastIndexOf("/", prefix.length - 2) + 1);
|
|
355
|
+
prefix = shorter === prefix ? "" : shorter;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return prefix;
|
|
359
|
+
};
|
|
360
|
+
var foldPaths = (run) => {
|
|
361
|
+
if (run.filter((line) => line.includes("/")).length * 5 < run.length * 3) {
|
|
362
|
+
return run;
|
|
363
|
+
}
|
|
364
|
+
const byDirectory = /* @__PURE__ */ new Map();
|
|
365
|
+
for (const path of run) {
|
|
366
|
+
const cut = path.lastIndexOf("/");
|
|
367
|
+
const directory = cut === -1 ? "./" : path.slice(0, cut + 1);
|
|
368
|
+
const names = byDirectory.get(directory);
|
|
369
|
+
if (names === void 0) {
|
|
370
|
+
byDirectory.set(directory, [path.slice(cut + 1)]);
|
|
371
|
+
} else {
|
|
372
|
+
names.push(path.slice(cut + 1));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const root = sharedRoot([...byDirectory.keys()]);
|
|
376
|
+
const trim = root.length > 8 ? root.length : 0;
|
|
377
|
+
const folded = [`${run.length} paths in ${byDirectory.size} directories${trim === 0 ? "" : ` under ${root}`}:`];
|
|
378
|
+
let shown = 0;
|
|
379
|
+
for (const [directory, names] of [...byDirectory].slice(0, PATH_RUN_DIRS)) {
|
|
380
|
+
const kept = names.slice(0, PATH_RUN_NAMES);
|
|
381
|
+
shown += kept.length;
|
|
382
|
+
folded.push(`${directory.slice(trim) === "" ? "./" : directory.slice(trim)} ${kept.join(" ")}`);
|
|
383
|
+
}
|
|
384
|
+
if (shown < run.length) {
|
|
385
|
+
folded.push(`\u2026 ${run.length - shown} more paths elided`);
|
|
386
|
+
}
|
|
387
|
+
return bodyBytes(folded) < bodyBytes(run) ? folded : run;
|
|
388
|
+
};
|
|
389
|
+
var foldPathRuns = (lines) => {
|
|
390
|
+
const out = [];
|
|
391
|
+
for (let i = 0; i < lines.length; ) {
|
|
392
|
+
let j = i;
|
|
393
|
+
while (j < lines.length && isPathLine(lines[j])) {
|
|
394
|
+
j++;
|
|
395
|
+
}
|
|
396
|
+
if (j === i) {
|
|
397
|
+
out.push(lines[i]);
|
|
398
|
+
i++;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
out.push(...j - i >= PATH_RUN_MIN ? foldPaths(lines.slice(i, j)) : lines.slice(i, j));
|
|
402
|
+
i = j;
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
};
|
|
406
|
+
var HIT_LINE = /^([^\s:][^:]{0,240}):(\d+):(?:(\d+):)?(.*)$/;
|
|
407
|
+
var HIT_RUN_MIN = 6;
|
|
408
|
+
var isHitKey = (key) => key.includes("/") || key.includes(".");
|
|
409
|
+
var parseHit = (line) => {
|
|
410
|
+
const match = HIT_LINE.exec(line);
|
|
411
|
+
return match !== null && isHitKey(match[1]) ? match : void 0;
|
|
412
|
+
};
|
|
413
|
+
var foldHitRun = (run) => {
|
|
414
|
+
const folded = [];
|
|
415
|
+
for (let start = 0; start < run.length; ) {
|
|
416
|
+
const file = parseHit(run[start])[1];
|
|
417
|
+
folded.push(run[start]);
|
|
418
|
+
let next = start + 1;
|
|
419
|
+
while (next < run.length && parseHit(run[next])[1] === file) {
|
|
420
|
+
const hit = parseHit(run[next]);
|
|
421
|
+
folded.push(` ${hit[2]}${hit[3] === void 0 ? "" : `:${hit[3]}`}:${hit[4]}`);
|
|
422
|
+
next++;
|
|
423
|
+
}
|
|
424
|
+
start = next;
|
|
425
|
+
}
|
|
426
|
+
return bodyBytes(folded) < bodyBytes(run) ? folded : run;
|
|
427
|
+
};
|
|
428
|
+
var foldHitRuns = (lines) => {
|
|
429
|
+
const out = [];
|
|
430
|
+
for (let i = 0; i < lines.length; ) {
|
|
431
|
+
let end = i;
|
|
432
|
+
while (end < lines.length && parseHit(lines[end]) !== void 0) {
|
|
433
|
+
end++;
|
|
434
|
+
}
|
|
435
|
+
const stop = Math.max(end, i + 1);
|
|
436
|
+
out.push(...end - i >= HIT_RUN_MIN ? foldHitRun(lines.slice(i, end)) : lines.slice(i, stop));
|
|
437
|
+
i = stop;
|
|
438
|
+
}
|
|
439
|
+
return out;
|
|
440
|
+
};
|
|
441
|
+
var GENERATED_PATH = /(?:^|\/)(?:[^/]*\.lock(?:\.json)?|[^/]*-lock\.json|pnpm-lock\.yaml|go\.sum|[^/]*\.min\.(?:js|css|mjs)|[^/]*\.map)$/;
|
|
442
|
+
var DIFF_HEADER = /^diff --git a\/(\S+) b\/(\S+)$/;
|
|
443
|
+
var isAdded = (line) => line.startsWith("+") && !line.startsWith("+++");
|
|
444
|
+
var isRemoved = (line) => line.startsWith("-") && !line.startsWith("---");
|
|
445
|
+
var foldGeneratedDiffs = (lines) => {
|
|
446
|
+
const out = [];
|
|
447
|
+
let folding = false;
|
|
448
|
+
let added = 0;
|
|
449
|
+
let removed = 0;
|
|
450
|
+
let dropped = 0;
|
|
451
|
+
const flush = () => {
|
|
452
|
+
if (folding && dropped > 0) {
|
|
453
|
+
out.push(`\u2026 ${dropped} lines of generated-file diff elided (+${added} \u2212${removed}) \u2026`);
|
|
454
|
+
}
|
|
455
|
+
folding = false;
|
|
456
|
+
added = 0;
|
|
457
|
+
removed = 0;
|
|
458
|
+
dropped = 0;
|
|
459
|
+
};
|
|
460
|
+
for (const line of lines) {
|
|
461
|
+
const header = DIFF_HEADER.exec(line);
|
|
462
|
+
if (header !== null) {
|
|
463
|
+
flush();
|
|
464
|
+
folding = GENERATED_PATH.test(header[2]);
|
|
465
|
+
out.push(line);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (!folding) {
|
|
469
|
+
out.push(line);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
dropped++;
|
|
473
|
+
added += isAdded(line) ? 1 : 0;
|
|
474
|
+
removed += isRemoved(line) ? 1 : 0;
|
|
475
|
+
}
|
|
476
|
+
flush();
|
|
477
|
+
return bodyBytes(out) < bodyBytes(lines) ? out : lines;
|
|
478
|
+
};
|
|
479
|
+
var COMMAND_CLEANERS = [
|
|
480
|
+
strip("pnpm", invocation(String.raw`pnpm`), [
|
|
481
|
+
/^\s*Progress: /,
|
|
482
|
+
/^Packages: [+-]/,
|
|
483
|
+
/^Downloading /,
|
|
484
|
+
/^\s*[.+]+\s*$/,
|
|
485
|
+
/^Virtual store is at/,
|
|
486
|
+
/^Lockfile is up to date/
|
|
487
|
+
]),
|
|
488
|
+
// `apt-get` before `apt`: ordered alternation, or the guard rejects the longer spelling at its hyphen.
|
|
489
|
+
strip("apt", invocation(String.raw`apt-get|apt`), [
|
|
490
|
+
/^(?:Get:|Hit:|Ign:|Fetched |Selecting |Preparing to unpack|Unpacking |Setting up |Processing triggers)/
|
|
491
|
+
]),
|
|
492
|
+
// Test runners: on a passing run (exit 0) per-test PASS lines are noise, dropped, keeping the summary. Failures skip
|
|
493
|
+
// all command cleaners, so failing tests survive verbatim.
|
|
494
|
+
strip("test", invocation(String.raw`bun\s+test|suites|vitest|jest|pytest|rspec|mocha|phpunit|go\s+test|cargo\s+test`), [
|
|
495
|
+
/^\s*[✓√]\s/,
|
|
496
|
+
// per-test pass (vitest/jest/mocha)
|
|
497
|
+
/^\(pass\)\s/,
|
|
498
|
+
// bun test per-test pass
|
|
499
|
+
/^--- PASS:/,
|
|
500
|
+
// go test per-test
|
|
501
|
+
/^\s*test .+\.\.\. ok$/,
|
|
502
|
+
// cargo test per-test
|
|
503
|
+
/^PASS\s+\S/,
|
|
504
|
+
// jest per-file PASS header
|
|
505
|
+
/^\s*[.·]+\s*$/
|
|
506
|
+
// pytest/mocha dot progress
|
|
507
|
+
]),
|
|
508
|
+
// `diff` reads the whole output's structure, the three after it read runs of lines; folding the generated hunks away
|
|
509
|
+
// first keeps those three from ever looking inside one.
|
|
510
|
+
{ id: "diff", apply: foldGeneratedDiffs },
|
|
511
|
+
{ id: "ls", apply: compactListing },
|
|
512
|
+
{ id: "files", apply: foldPathRuns },
|
|
513
|
+
{ id: "hits", apply: foldHitRuns }
|
|
514
|
+
];
|
|
515
|
+
var CLEANERS = [...COMMAND_CLEANERS.map((cleaner) => cleaner.id), "dedup", "wide", "cap", "redact", "cache"];
|
|
516
|
+
var WIDE_RUN = 400;
|
|
517
|
+
var WIDE_PATTERN = new RegExp(String.raw`\S{${WIDE_RUN + 1},}`, "g");
|
|
518
|
+
var SECRET_NAME = String.raw`[A-Za-z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASSWD|API[_-]?KEY|ACCESS[_-]?KEY|PRIVATE[_-]?KEY)[A-Za-z0-9_]*`;
|
|
519
|
+
var ASSIGN = String.raw`["']?\s*[:=](?![=>])\s*`;
|
|
520
|
+
var QUOTED_VALUE = String.raw`(["'\x60])([^"'\x60\n]{6,})\2`;
|
|
521
|
+
var BARE_VALUE = String.raw`[\w+/=~-]{6,}(?![\w+/=~(-])`;
|
|
522
|
+
var ISSUER_PREFIX = /^(?:sk-|pk-|rk-|ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|xox[abposr]-|AKIA|ASIA|eyJ|AIza|ya29\.|glpat-|dop_v1_|shpat_|SG\.|npm_)/;
|
|
523
|
+
var NUMERIC_VALUE = /^[\d_,.]+$/;
|
|
524
|
+
var STRUCTURAL_VALUE = /[\s/\\${}]/;
|
|
525
|
+
var ENV_NAME_VALUE = /^[A-Z0-9_]+$/;
|
|
526
|
+
var ENTROPIC_VALUE = /^(?=[^\n]*\d)(?=[^\n]*[A-Za-z])/;
|
|
527
|
+
var HANDWRITTEN_MAX = 12;
|
|
528
|
+
var looksLikeCredential = (value) => ISSUER_PREFIX.test(value) || value.length > HANDWRITTEN_MAX && ENTROPIC_VALUE.test(value) && !NUMERIC_VALUE.test(value) && !STRUCTURAL_VALUE.test(value) && !ENV_NAME_VALUE.test(value);
|
|
529
|
+
var SECRET_PATTERNS = [
|
|
530
|
+
[
|
|
531
|
+
new RegExp(`\\b(${SECRET_NAME}${ASSIGN})(?:${QUOTED_VALUE}|(${BARE_VALUE}))`, "gi"),
|
|
532
|
+
// Quoted and bare arrive in different groups because only the quoted one has a quote to put back.
|
|
533
|
+
(match, assignment, quote, quoted, bare) => looksLikeCredential(quoted ?? bare) ? `${assignment}${quote ?? ""}***${quote ?? ""}` : match
|
|
534
|
+
],
|
|
535
|
+
[/\bAKIA[0-9A-Z]{16}\b/g, "***"],
|
|
536
|
+
// Same rule for a bearer value: prose about tokens carries no digit, every real bearer token does.
|
|
537
|
+
[/\b(Bearer\s+)(?=[\w.-]*\d)[\w.-]{8,}/gi, "$1***"],
|
|
538
|
+
[/\b(https?:\/\/[^:@\s/]+:)[^@\s]+@/gi, "$1***@"]
|
|
539
|
+
];
|
|
540
|
+
var redactText = (text, values = []) => text.split("\n").map((line) => redactLine(line, values)).join("\n");
|
|
541
|
+
var redactLine = (line, values = []) => {
|
|
542
|
+
let masked = line;
|
|
543
|
+
for (const { target, replacement } of values) {
|
|
544
|
+
if (masked.includes(target)) {
|
|
545
|
+
masked = masked.split(target).join(replacement);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return SECRET_PATTERNS.reduce((acc, [pattern, replacement]) => acc.replace(pattern, replacement), masked);
|
|
549
|
+
};
|
|
550
|
+
var GIT_OPTIONS = String.raw`(?:(?:-[Cc]|--(?:git-dir|work-tree|namespace|exec-path|super-prefix))\s+\S+\s+|-\S+\s+)*`;
|
|
551
|
+
var READ_COMMAND = new RegExp(
|
|
552
|
+
String.raw`(?<![\w.-])(?:cat|bat|sed\s+-n|awk|git\s+${GIT_OPTIONS}(?:diff|show)\b|git\s+${GIT_OPTIONS}log\s+(?:[^;&|]*\s)?-p)\b`
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
// ../output-cleaners/src/filter-stats.mjs
|
|
556
|
+
var isPrefixWord = (word, previous) => /^[A-Za-z_]\w*=/.test(word) || word === "sudo" || word === "timeout" || previous === "timeout" && /^\d+[smhd]?$/.test(word);
|
|
557
|
+
var SHELL_NOISE = /* @__PURE__ */ new Set(["cd", "echo", "true", "set", "export", "source", "time", "for", "do", "done", "while", "until", "if", "then", "else", "elif", "fi", "case", "esac"]);
|
|
558
|
+
var SUBCOMMAND_VERBS = /* @__PURE__ */ new Set(["git", "pnpm", "npm", "npx", "yarn", "docker", "cargo", "go", "gh", "kubectl", "systemctl", "apt", "apt-get", "pip", "poetry"]);
|
|
559
|
+
var segmentVerb = (segment) => {
|
|
560
|
+
const words = segment.trim().split(/\s+/);
|
|
561
|
+
let index = 0;
|
|
562
|
+
while (index < words.length && isPrefixWord(words[index], words[index - 1])) {
|
|
563
|
+
index++;
|
|
564
|
+
}
|
|
565
|
+
const word = (words[index] ?? "").replace(/^[({]+/, "");
|
|
566
|
+
const verb = word.slice(word.lastIndexOf("/") + 1);
|
|
567
|
+
if (verb === "" || SHELL_NOISE.has(verb)) {
|
|
568
|
+
return "";
|
|
569
|
+
}
|
|
570
|
+
const next = words[index + 1];
|
|
571
|
+
return SUBCOMMAND_VERBS.has(verb) && next !== void 0 && /^[a-z][\w-]*$/.test(next) ? `${verb} ${next}` : verb;
|
|
572
|
+
};
|
|
573
|
+
var commandSignature = (command) => {
|
|
574
|
+
for (const segment of String(command).split(/[|;&]+/)) {
|
|
575
|
+
const verb = segmentVerb(segment);
|
|
576
|
+
if (verb !== "") {
|
|
577
|
+
return verb;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return "";
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
// src/hook-io.ts
|
|
584
|
+
import { homedir } from "node:os";
|
|
585
|
+
import { join as join2 } from "node:path";
|
|
586
|
+
var fieldNotesFile = (project) => join2(project, ".claude", "intentic", "field-notes.toon");
|
|
587
|
+
var transcriptDir = (project, env = process.env) => join2(env["CLAUDE_CONFIG_DIR"] ?? join2(homedir(), ".claude"), "projects", project.replace(/[^a-zA-Z0-9]/g, "-"));
|
|
588
|
+
|
|
589
|
+
// src/runtime.ts
|
|
590
|
+
var isMissing2 = (error) => {
|
|
591
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
592
|
+
return code === "ENOENT" || code === "ENOTDIR";
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
// src/notes-evidence.ts
|
|
596
|
+
var MAX_SESSIONS = 200;
|
|
597
|
+
var BUDGET_CHARS = 12e3;
|
|
598
|
+
var tally = (map, key) => {
|
|
599
|
+
const found = map.get(key);
|
|
600
|
+
if (found !== void 0) {
|
|
601
|
+
return found;
|
|
602
|
+
}
|
|
603
|
+
const created = { runs: 0, failures: 0, sessions: /* @__PURE__ */ new Set(), examples: /* @__PURE__ */ new Set() };
|
|
604
|
+
map.set(key, created);
|
|
605
|
+
return created;
|
|
606
|
+
};
|
|
607
|
+
var signatureOf = (call) => call.name === "Bash" && call.target !== void 0 ? `\`${commandSignature(call.target) || call.target.slice(0, 40)}\`` : call.name;
|
|
608
|
+
var openingOf = (prompt) => {
|
|
609
|
+
const name = /<command-name>([^<]*)<\/command-name>/.exec(prompt)?.[1]?.trim();
|
|
610
|
+
if (name === void 0 || name === "") {
|
|
611
|
+
return (prompt.split("\n")[0] ?? "").slice(0, 140);
|
|
612
|
+
}
|
|
613
|
+
const args = /<command-args>([^<]*)<\/command-args>/.exec(prompt)?.[1]?.trim() ?? "";
|
|
614
|
+
return `${name.startsWith("/") ? name : `/${name}`}${args === "" ? "" : ` ${args.slice(0, 120)}`}`;
|
|
615
|
+
};
|
|
616
|
+
var exampleOf = (text) => {
|
|
617
|
+
const first = text.split("\n").map((line) => line.trim()).find((line) => line !== "" && !/^Exit code \d+$/.test(line)) ?? text.trim();
|
|
618
|
+
return first.length > 160 ? `${first.slice(0, 157)}\u2026` : first;
|
|
619
|
+
};
|
|
620
|
+
var tallyCalls = (calls, session, commands, edited) => {
|
|
621
|
+
for (const call of calls) {
|
|
622
|
+
if (call.name === "Bash") {
|
|
623
|
+
const entry = tally(commands, signatureOf(call));
|
|
624
|
+
entry.runs += 1;
|
|
625
|
+
entry.sessions.add(session);
|
|
626
|
+
}
|
|
627
|
+
for (const { path } of call.category === "edit" ? call.locations ?? [] : []) {
|
|
628
|
+
const entry = tally(edited, path);
|
|
629
|
+
entry.runs += 1;
|
|
630
|
+
entry.sessions.add(session);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
var digestOf = (sessions) => {
|
|
635
|
+
const failed = /* @__PURE__ */ new Map();
|
|
636
|
+
const commands = /* @__PURE__ */ new Map();
|
|
637
|
+
const edited = /* @__PURE__ */ new Map();
|
|
638
|
+
const prompts = [];
|
|
639
|
+
let turns = 0;
|
|
640
|
+
let calls = 0;
|
|
641
|
+
let failures = 0;
|
|
642
|
+
let from;
|
|
643
|
+
let to;
|
|
644
|
+
for (const [index, session] of sessions.entries()) {
|
|
645
|
+
const id = session.sessionId ?? String(index);
|
|
646
|
+
const first = session.turns[0];
|
|
647
|
+
if (first !== void 0) {
|
|
648
|
+
from = Math.min(from ?? first.at, first.at);
|
|
649
|
+
to = Math.max(to ?? first.at, session.turns.at(-1)?.at ?? first.at);
|
|
650
|
+
prompts.push(openingOf(first.prompt));
|
|
651
|
+
}
|
|
652
|
+
for (const turn of session.turns) {
|
|
653
|
+
turns += 1;
|
|
654
|
+
calls += turn.calls.length;
|
|
655
|
+
failures += turn.failures.length;
|
|
656
|
+
const byId = new Map(turn.calls.map((call) => [call.id, call]));
|
|
657
|
+
tallyCalls(turn.calls, id, commands, edited);
|
|
658
|
+
for (const failure of turn.failures) {
|
|
659
|
+
const call = byId.get(failure.id);
|
|
660
|
+
const key = call === void 0 ? "a subagent's call" : signatureOf(call);
|
|
661
|
+
const entry = tally(failed, key);
|
|
662
|
+
entry.failures += 1;
|
|
663
|
+
entry.sessions.add(id);
|
|
664
|
+
if (entry.examples.size < 2) {
|
|
665
|
+
entry.examples.add(exampleOf(failure.text));
|
|
666
|
+
}
|
|
667
|
+
if (call?.name === "Bash") {
|
|
668
|
+
tally(commands, key).failures += 1;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return { sessions: sessions.length, from, to, turns, calls, failures, failed, commands, edited, prompts };
|
|
674
|
+
};
|
|
675
|
+
var bySessions = (map) => [...map.entries()].toSorted((left, right) => right[1].sessions.size - left[1].sessions.size || right[1].runs - left[1].runs);
|
|
676
|
+
var utcDay = (at) => at === void 0 ? "?" : new Date(at).toISOString().slice(0, 10);
|
|
677
|
+
var renderDigest = (digest, project, notesFile) => {
|
|
678
|
+
const head = [
|
|
679
|
+
`Corpus: ${digest.sessions} sessions of this project (${utcDay(digest.from)} to ${utcDay(digest.to)}), ${digest.turns} turns, ${digest.calls} tool calls, ${digest.failures} of them failed.`,
|
|
680
|
+
`Notes file: \`${notesFile}\` (read it first if it exists; this digest does not include it).`
|
|
681
|
+
];
|
|
682
|
+
const sections = [
|
|
683
|
+
[
|
|
684
|
+
"Failures, by how many sessions hit them",
|
|
685
|
+
bySessions(digest.failed).slice(0, 25).map(([key, entry]) => `- ${key}: ${entry.failures} failures in ${entry.sessions.size} sessions \u2014 e.g. ${[...entry.examples].map((text) => `"${text}"`).join("; ")}`)
|
|
686
|
+
],
|
|
687
|
+
[
|
|
688
|
+
"Commands run, with how often they failed",
|
|
689
|
+
bySessions(digest.commands).slice(0, 20).map(([key, entry]) => `- ${key}: ${entry.runs} runs in ${entry.sessions.size} sessions, ${entry.failures} failed`)
|
|
690
|
+
],
|
|
691
|
+
["Files edited in the most sessions", bySessions(digest.edited).slice(0, 12).map(([path, entry]) => `- ${path}: ${entry.sessions.size} sessions`)],
|
|
692
|
+
["How recent sessions opened (first line of the first prompt)", digest.prompts.slice(0, 15).map((prompt) => `- ${prompt}`)]
|
|
693
|
+
];
|
|
694
|
+
const lines = [...head];
|
|
695
|
+
let used = head.join("\n").length;
|
|
696
|
+
for (const [title, items] of sections) {
|
|
697
|
+
const heading = `
|
|
698
|
+
### ${title}`;
|
|
699
|
+
used += heading.length;
|
|
700
|
+
lines.push(heading);
|
|
701
|
+
for (const item of items.length === 0 ? ["- none"] : items) {
|
|
702
|
+
if (used + item.length + 1 > BUDGET_CHARS) {
|
|
703
|
+
lines.push("- \u2026 cut to fit");
|
|
704
|
+
break;
|
|
705
|
+
}
|
|
706
|
+
used += item.length + 1;
|
|
707
|
+
lines.push(item);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
return redactText(`${lines.join("\n")}
|
|
711
|
+
|
|
712
|
+
Transcripts: \`${transcriptDir(project)}\` (one JSON object per line).
|
|
713
|
+
`);
|
|
714
|
+
};
|
|
715
|
+
var sessionFiles = (dir) => {
|
|
716
|
+
let names;
|
|
717
|
+
try {
|
|
718
|
+
names = readdirSync2(dir).filter((name) => name.endsWith(".jsonl"));
|
|
719
|
+
} catch (error) {
|
|
720
|
+
if (isMissing2(error)) {
|
|
721
|
+
return [];
|
|
722
|
+
}
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
return names.map((name) => {
|
|
726
|
+
try {
|
|
727
|
+
return { path: join3(dir, name), at: statSync(join3(dir, name)).mtimeMs };
|
|
728
|
+
} catch {
|
|
729
|
+
return { path: join3(dir, name), at: 0 };
|
|
730
|
+
}
|
|
731
|
+
}).toSorted((left, right) => right.at - left.at).slice(0, MAX_SESSIONS).map((entry) => entry.path);
|
|
732
|
+
};
|
|
733
|
+
var notesEvidence = (project) => {
|
|
734
|
+
const sessions = sessionFiles(transcriptDir(project)).flatMap((file) => {
|
|
735
|
+
const session = readClaudeSession(file, { root: project });
|
|
736
|
+
return session === void 0 || session.turns.length === 0 ? [] : [session];
|
|
737
|
+
});
|
|
738
|
+
return renderDigest(digestOf(sessions), project, fieldNotesFile(project));
|
|
739
|
+
};
|
|
740
|
+
if (process.argv[1]?.endsWith("notes-evidence.mjs")) {
|
|
741
|
+
const at = process.argv.indexOf("--project");
|
|
742
|
+
process.stdout.write(notesEvidence(at === -1 ? process.cwd() : process.argv[at + 1] ?? process.cwd()));
|
|
743
|
+
}
|
|
744
|
+
export {
|
|
745
|
+
digestOf,
|
|
746
|
+
notesEvidence,
|
|
747
|
+
openingOf,
|
|
748
|
+
renderDigest
|
|
749
|
+
};
|