@temet/cli 0.2.0 → 0.3.1
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/dist/audit.d.ts +34 -0
- package/dist/audit.js +531 -0
- package/dist/index.js +85 -40
- package/dist/lib/analysis-types.d.ts +7 -0
- package/dist/lib/analysis-types.js +1 -0
- package/dist/lib/audit-tracking.d.ts +55 -0
- package/dist/lib/audit-tracking.js +185 -0
- package/dist/lib/cli-args.d.ts +22 -0
- package/dist/lib/cli-args.js +65 -0
- package/dist/lib/editorial-taxonomy.d.ts +17 -0
- package/dist/lib/editorial-taxonomy.js +91 -0
- package/dist/lib/heuristics.d.ts +15 -0
- package/dist/lib/heuristics.js +341 -0
- package/dist/lib/hook-installer.d.ts +13 -0
- package/dist/lib/hook-installer.js +130 -0
- package/dist/lib/narrator-lite.d.ts +25 -0
- package/dist/lib/narrator-lite.js +231 -0
- package/dist/lib/notifier.d.ts +9 -0
- package/dist/lib/notifier.js +57 -0
- package/dist/lib/path-resolver.d.ts +39 -0
- package/dist/lib/path-resolver.js +152 -0
- package/dist/lib/profile-report.d.ts +18 -0
- package/dist/lib/profile-report.js +148 -0
- package/dist/lib/report-writer.d.ts +7 -0
- package/dist/lib/report-writer.js +73 -0
- package/dist/lib/session-audit.d.ts +24 -0
- package/dist/lib/session-audit.js +94 -0
- package/dist/lib/session-parser.d.ts +35 -0
- package/dist/lib/session-parser.js +130 -0
- package/dist/lib/skill-mapper.d.ts +3 -0
- package/dist/lib/skill-mapper.js +173 -0
- package/dist/lib/skill-naming.d.ts +1 -0
- package/dist/lib/skill-naming.js +50 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/workflow-detector.d.ts +11 -0
- package/dist/lib/workflow-detector.js +125 -0
- package/package.json +2 -2
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ---------- Named Patterns ----------
|
|
2
|
+
const NAMED_PATTERNS = [
|
|
3
|
+
{ tools: ["Grep", "Read", "Edit"], name: "Search-Understand-Modify" },
|
|
4
|
+
{ tools: ["Read", "Edit", "Bash"], name: "Read-Modify-Test" },
|
|
5
|
+
{ tools: ["Glob", "Read", "Edit"], name: "Find-Read-Edit" },
|
|
6
|
+
{ tools: ["Read", "Edit", "Read"], name: "Read-Edit-Verify" },
|
|
7
|
+
{ tools: ["Bash", "Read", "Edit"], name: "Run-Read-Fix" },
|
|
8
|
+
{ tools: ["Grep", "Read", "Edit", "Bash"], name: "Search-Read-Edit-Test" },
|
|
9
|
+
];
|
|
10
|
+
function nameWorkflow(sequence) {
|
|
11
|
+
for (const p of NAMED_PATTERNS) {
|
|
12
|
+
if (p.tools.length === sequence.length &&
|
|
13
|
+
p.tools.every((t, i) => t === sequence[i])) {
|
|
14
|
+
return p.name;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return sequence.join(" \u2192 ");
|
|
18
|
+
}
|
|
19
|
+
// ---------- Helpers ----------
|
|
20
|
+
/** Collapse consecutive identical tool names: [Read, Read, Read, Edit] → [Read, Edit] */
|
|
21
|
+
function dedup(tools) {
|
|
22
|
+
const result = [];
|
|
23
|
+
for (const t of tools) {
|
|
24
|
+
if (result[result.length - 1] !== t) {
|
|
25
|
+
result.push(t);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
function extractToolNames(toolCalls) {
|
|
31
|
+
return toolCalls.map((tc) => tc.name);
|
|
32
|
+
}
|
|
33
|
+
function ngramKey(seq) {
|
|
34
|
+
return seq.join("::");
|
|
35
|
+
}
|
|
36
|
+
function generateNgrams(tools, n, sessionIdx, acc) {
|
|
37
|
+
for (let i = 0; i <= tools.length - n; i++) {
|
|
38
|
+
const gram = tools.slice(i, i + n);
|
|
39
|
+
const key = ngramKey(gram);
|
|
40
|
+
const existing = acc.get(key);
|
|
41
|
+
if (existing) {
|
|
42
|
+
existing.occurrences++;
|
|
43
|
+
existing.sessions.add(sessionIdx);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
acc.set(key, { occurrences: 1, sessions: new Set([sessionIdx]) });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Remove n-grams that are fully explained by a single longer qualifying n-gram.
|
|
52
|
+
* Uses max (not sum) of explanations from individual longer patterns to avoid
|
|
53
|
+
* double-counting when overlapping longer n-grams explain the same instances.
|
|
54
|
+
*/
|
|
55
|
+
function collapseSubsumed(ngrams) {
|
|
56
|
+
const keys = [...ngrams.keys()].sort((a, b) => b.split("::").length - a.split("::").length);
|
|
57
|
+
const kept = new Map();
|
|
58
|
+
// Track the max occurrences any single longer n-gram can explain for each shorter one
|
|
59
|
+
const maxExplained = new Map();
|
|
60
|
+
for (const key of keys) {
|
|
61
|
+
const info = ngrams.get(key);
|
|
62
|
+
const explained = maxExplained.get(key) ?? 0;
|
|
63
|
+
// If a single longer n-gram accounts for all occurrences, skip
|
|
64
|
+
if (explained >= info.occurrences)
|
|
65
|
+
continue;
|
|
66
|
+
kept.set(key, info);
|
|
67
|
+
// Record how many occurrences this pattern can explain for each sub-sequence
|
|
68
|
+
const parts = key.split("::");
|
|
69
|
+
for (let len = parts.length - 1; len >= 3; len--) {
|
|
70
|
+
for (let start = 0; start <= parts.length - len; start++) {
|
|
71
|
+
const sub = ngramKey(parts.slice(start, start + len));
|
|
72
|
+
if (ngrams.has(sub)) {
|
|
73
|
+
const current = maxExplained.get(sub) ?? 0;
|
|
74
|
+
if (info.occurrences > current) {
|
|
75
|
+
maxExplained.set(sub, info.occurrences);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return kept;
|
|
82
|
+
}
|
|
83
|
+
// ---------- Main ----------
|
|
84
|
+
export function detectWorkflows(allToolCalls) {
|
|
85
|
+
if (allToolCalls.length === 0)
|
|
86
|
+
return [];
|
|
87
|
+
const ngrams = new Map();
|
|
88
|
+
for (let si = 0; si < allToolCalls.length; si++) {
|
|
89
|
+
const tools = dedup(extractToolNames(allToolCalls[si]));
|
|
90
|
+
for (const n of [5, 4, 3]) {
|
|
91
|
+
generateNgrams(tools, n, si, ngrams);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Filter: 3+ occurrences AND 2+ distinct sessions
|
|
95
|
+
for (const [key, info] of ngrams) {
|
|
96
|
+
if (info.occurrences < 3 || info.sessions.size < 2) {
|
|
97
|
+
ngrams.delete(key);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const collapsed = collapseSubsumed(ngrams);
|
|
101
|
+
const workflows = [];
|
|
102
|
+
for (const [key, info] of collapsed) {
|
|
103
|
+
const sequence = key.split("::");
|
|
104
|
+
const confidence = Math.min(0.5 + info.occurrences * 0.05 + info.sessions.size * 0.1, 0.95);
|
|
105
|
+
workflows.push({
|
|
106
|
+
sequence,
|
|
107
|
+
occurrences: info.occurrences,
|
|
108
|
+
sessions: info.sessions.size,
|
|
109
|
+
confidence,
|
|
110
|
+
description: nameWorkflow(sequence),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Sort by confidence desc
|
|
114
|
+
workflows.sort((a, b) => b.confidence - a.confidence);
|
|
115
|
+
return workflows;
|
|
116
|
+
}
|
|
117
|
+
export function workflowsToSignals(workflows) {
|
|
118
|
+
return workflows.map((w) => ({
|
|
119
|
+
type: "workflow",
|
|
120
|
+
skill: w.description,
|
|
121
|
+
confidence: w.confidence,
|
|
122
|
+
evidence: `Workflow [${w.sequence.join(" \u2192 ")}] detected ${w.occurrences} times across ${w.sessions} sessions`,
|
|
123
|
+
category: "methodology",
|
|
124
|
+
}));
|
|
125
|
+
}
|
package/package.json
CHANGED