@crafter/cli-tree 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 +328 -0
- package/dist/archaeology/cache.d.ts +11 -0
- package/dist/archaeology/delegate.d.ts +43 -0
- package/dist/archaeology/index.d.ts +12 -0
- package/dist/archaeology/index.js +61 -0
- package/dist/archaeology/index.js.map +9 -0
- package/dist/archaeology/llm.d.ts +1 -0
- package/dist/archaeology/merge.d.ts +3 -0
- package/dist/archaeology/orchestrator.d.ts +25 -0
- package/dist/archaeology/prompts.d.ts +13 -0
- package/dist/archaeology/types.d.ts +101 -0
- package/dist/archaeology/validate.d.ts +18 -0
- package/dist/chunk-57gtsvhb.js +434 -0
- package/dist/chunk-57gtsvhb.js.map +16 -0
- package/dist/chunk-5aahbfr2.js +293 -0
- package/dist/chunk-5aahbfr2.js.map +10 -0
- package/dist/chunk-pkfpaae1.js +678 -0
- package/dist/chunk-pkfpaae1.js.map +15 -0
- package/dist/chunk-q4se2rwe.js +181 -0
- package/dist/chunk-q4se2rwe.js.map +14 -0
- package/dist/chunk-v5w3w6bd.js +168 -0
- package/dist/chunk-v5w3w6bd.js.map +11 -0
- package/dist/chunk-ykze151b.js +770 -0
- package/dist/chunk-ykze151b.js.map +16 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +433 -0
- package/dist/cli.js.map +10 -0
- package/dist/encoders/ansi.d.ts +2 -0
- package/dist/encoders/html.d.ts +10 -0
- package/dist/encoders/string.d.ts +2 -0
- package/dist/flow/encode.d.ts +5 -0
- package/dist/flow/index.d.ts +8 -0
- package/dist/flow/index.js +25 -0
- package/dist/flow/index.js.map +9 -0
- package/dist/flow/layout.d.ts +30 -0
- package/dist/flow/parse.d.ts +2 -0
- package/dist/flow/render.d.ts +3 -0
- package/dist/flow/types.d.ts +42 -0
- package/dist/flow/validate.d.ts +3 -0
- package/dist/flow/yaml.d.ts +4 -0
- package/dist/grid.d.ts +14 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +9 -0
- package/dist/miner/history.d.ts +6 -0
- package/dist/miner/index.d.ts +18 -0
- package/dist/miner/index.js +38 -0
- package/dist/miner/index.js.map +9 -0
- package/dist/miner/sessions.d.ts +3 -0
- package/dist/miner/stats.d.ts +2 -0
- package/dist/miner/suggest.d.ts +11 -0
- package/dist/miner/transitions.d.ts +6 -0
- package/dist/miner/types.d.ts +46 -0
- package/dist/miner/workflows.d.ts +11 -0
- package/dist/parse.d.ts +3 -0
- package/dist/render.d.ts +3 -0
- package/dist/types.d.ts +39 -0
- package/package.json +85 -0
- package/skill/SKILL.md +263 -0
- package/skill/evals/evals.json +26 -0
- package/skill/install.sh +38 -0
- package/skill/references/archaeology-guide.md +157 -0
- package/skill/references/skill-template.md +120 -0
- package/src/archaeology/cache.ts +107 -0
- package/src/archaeology/delegate.ts +113 -0
- package/src/archaeology/index.ts +48 -0
- package/src/archaeology/llm.ts +10 -0
- package/src/archaeology/merge.ts +155 -0
- package/src/archaeology/orchestrator.ts +185 -0
- package/src/archaeology/prompts.ts +178 -0
- package/src/archaeology/types.ts +139 -0
- package/src/archaeology/validate.ts +157 -0
- package/src/cli.ts +451 -0
- package/src/encoders/ansi.ts +32 -0
- package/src/encoders/html.ts +78 -0
- package/src/encoders/string.ts +20 -0
- package/src/flow/encode.ts +21 -0
- package/src/flow/index.ts +15 -0
- package/src/flow/layout.ts +150 -0
- package/src/flow/parse.ts +100 -0
- package/src/flow/render.ts +186 -0
- package/src/flow/types.ts +45 -0
- package/src/flow/validate.ts +111 -0
- package/src/flow/yaml.ts +235 -0
- package/src/grid.ts +59 -0
- package/src/index.ts +24 -0
- package/src/miner/history.ts +156 -0
- package/src/miner/index.ts +76 -0
- package/src/miner/sessions.ts +39 -0
- package/src/miner/stats.ts +43 -0
- package/src/miner/suggest.ts +101 -0
- package/src/miner/transitions.ts +62 -0
- package/src/miner/types.ts +45 -0
- package/src/miner/workflows.ts +96 -0
- package/src/parse.ts +321 -0
- package/src/render.ts +182 -0
- package/src/types.ts +62 -0
- package/workflows/docker-deploy.yml +42 -0
- package/workflows/docker-parallel.yml +36 -0
- package/workflows/gh-issue-to-pr.yml +48 -0
- package/workflows/git-pr-flow.yml +36 -0
- package/workflows/kubectl-rollout.yml +37 -0
- package/workflows/npm-publish.yml +42 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
// src/miner/history.ts
|
|
2
|
+
async function readHistoryFile(path) {
|
|
3
|
+
return await Bun.file(path).text();
|
|
4
|
+
}
|
|
5
|
+
function detectHistoryFormat(text) {
|
|
6
|
+
const firstNonEmpty = text.split(`
|
|
7
|
+
`).find((l) => l.trim().length > 0);
|
|
8
|
+
if (!firstNonEmpty)
|
|
9
|
+
return "unknown";
|
|
10
|
+
if (/^: \d+:\d+;/.test(firstNonEmpty))
|
|
11
|
+
return "zsh-extended";
|
|
12
|
+
if (/^- cmd: /.test(firstNonEmpty))
|
|
13
|
+
return "fish";
|
|
14
|
+
return "bash";
|
|
15
|
+
}
|
|
16
|
+
function parseHistory(text) {
|
|
17
|
+
const format = detectHistoryFormat(text);
|
|
18
|
+
switch (format) {
|
|
19
|
+
case "zsh-extended":
|
|
20
|
+
return parseZshExtended(text);
|
|
21
|
+
case "fish":
|
|
22
|
+
return parseFishHistory(text);
|
|
23
|
+
case "bash":
|
|
24
|
+
default:
|
|
25
|
+
return parseBashHistory(text);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function parseZshExtended(text) {
|
|
29
|
+
const entries = [];
|
|
30
|
+
const lines = text.split(`
|
|
31
|
+
`);
|
|
32
|
+
let pending = null;
|
|
33
|
+
for (const rawLine of lines) {
|
|
34
|
+
const match = rawLine.match(/^: (\d+):\d+;(.*)$/);
|
|
35
|
+
if (match) {
|
|
36
|
+
if (pending) {
|
|
37
|
+
entries.push(buildEntry(pending.timestamp, pending.cmd));
|
|
38
|
+
}
|
|
39
|
+
pending = { timestamp: Number.parseInt(match[1], 10), cmd: match[2] };
|
|
40
|
+
} else if (pending) {
|
|
41
|
+
pending.cmd += `
|
|
42
|
+
` + rawLine;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (pending) {
|
|
46
|
+
entries.push(buildEntry(pending.timestamp, pending.cmd));
|
|
47
|
+
}
|
|
48
|
+
return entries.filter((e) => e.argv.length > 0);
|
|
49
|
+
}
|
|
50
|
+
function parseBashHistory(text) {
|
|
51
|
+
const entries = [];
|
|
52
|
+
const lines = text.split(`
|
|
53
|
+
`);
|
|
54
|
+
let currentTimestamp = 0;
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
const trimmed = line.trim();
|
|
57
|
+
if (!trimmed)
|
|
58
|
+
continue;
|
|
59
|
+
if (/^#\d+$/.test(trimmed)) {
|
|
60
|
+
currentTimestamp = Number.parseInt(trimmed.slice(1), 10);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
entries.push(buildEntry(currentTimestamp, trimmed));
|
|
64
|
+
}
|
|
65
|
+
return entries.filter((e) => e.argv.length > 0);
|
|
66
|
+
}
|
|
67
|
+
function parseFishHistory(text) {
|
|
68
|
+
const entries = [];
|
|
69
|
+
const blocks = text.split(/\n(?=- cmd: )/);
|
|
70
|
+
for (const block of blocks) {
|
|
71
|
+
const cmdMatch = block.match(/- cmd: (.*)/);
|
|
72
|
+
const whenMatch = block.match(/when: (\d+)/);
|
|
73
|
+
if (!cmdMatch)
|
|
74
|
+
continue;
|
|
75
|
+
const timestamp = whenMatch ? Number.parseInt(whenMatch[1], 10) : 0;
|
|
76
|
+
entries.push(buildEntry(timestamp, cmdMatch[1]));
|
|
77
|
+
}
|
|
78
|
+
return entries.filter((e) => e.argv.length > 0);
|
|
79
|
+
}
|
|
80
|
+
function buildEntry(timestamp, rawCmd) {
|
|
81
|
+
const cleaned = stripContinuations(rawCmd).trim();
|
|
82
|
+
const argv = tokenize(cleaned);
|
|
83
|
+
return { timestamp, raw: cleaned, argv };
|
|
84
|
+
}
|
|
85
|
+
function stripContinuations(cmd) {
|
|
86
|
+
return cmd.replace(/\\\n/g, " ").replace(/\n\s*/g, " ");
|
|
87
|
+
}
|
|
88
|
+
function tokenize(cmd) {
|
|
89
|
+
const tokens = [];
|
|
90
|
+
let current = "";
|
|
91
|
+
let inSingle = false;
|
|
92
|
+
let inDouble = false;
|
|
93
|
+
let escape = false;
|
|
94
|
+
for (let i = 0;i < cmd.length; i++) {
|
|
95
|
+
const ch = cmd[i];
|
|
96
|
+
if (escape) {
|
|
97
|
+
current += ch;
|
|
98
|
+
escape = false;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (ch === "\\" && !inSingle) {
|
|
102
|
+
escape = true;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (ch === "'" && !inDouble) {
|
|
106
|
+
inSingle = !inSingle;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === '"' && !inSingle) {
|
|
110
|
+
inDouble = !inDouble;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (/\s/.test(ch) && !inSingle && !inDouble) {
|
|
114
|
+
if (current) {
|
|
115
|
+
tokens.push(current);
|
|
116
|
+
current = "";
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if ((ch === "|" || ch === ";" || ch === "&") && !inSingle && !inDouble) {
|
|
121
|
+
if (current) {
|
|
122
|
+
tokens.push(current);
|
|
123
|
+
current = "";
|
|
124
|
+
}
|
|
125
|
+
return tokens;
|
|
126
|
+
}
|
|
127
|
+
current += ch;
|
|
128
|
+
}
|
|
129
|
+
if (current)
|
|
130
|
+
tokens.push(current);
|
|
131
|
+
return tokens;
|
|
132
|
+
}
|
|
133
|
+
function defaultHistoryPath() {
|
|
134
|
+
const home = process.env.HOME ?? "";
|
|
135
|
+
if (!home)
|
|
136
|
+
return null;
|
|
137
|
+
const candidates = [
|
|
138
|
+
`${home}/.zsh_history`,
|
|
139
|
+
`${home}/.bash_history`,
|
|
140
|
+
`${home}/.config/fish/fish_history`
|
|
141
|
+
];
|
|
142
|
+
return candidates[0] ?? null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/miner/sessions.ts
|
|
146
|
+
function segmentSessions(entries, gapMinutes = 10) {
|
|
147
|
+
if (entries.length === 0)
|
|
148
|
+
return [];
|
|
149
|
+
const sorted = [...entries].sort((a, b) => a.timestamp - b.timestamp);
|
|
150
|
+
const gapSeconds = gapMinutes * 60;
|
|
151
|
+
const sessions = [];
|
|
152
|
+
let current = [sorted[0]];
|
|
153
|
+
let sessionStart = sorted[0].timestamp;
|
|
154
|
+
let lastTimestamp = sorted[0].timestamp;
|
|
155
|
+
for (let i = 1;i < sorted.length; i++) {
|
|
156
|
+
const entry = sorted[i];
|
|
157
|
+
const gap = entry.timestamp - lastTimestamp;
|
|
158
|
+
if (gap > gapSeconds) {
|
|
159
|
+
sessions.push({ start: sessionStart, end: lastTimestamp, entries: current });
|
|
160
|
+
current = [entry];
|
|
161
|
+
sessionStart = entry.timestamp;
|
|
162
|
+
} else {
|
|
163
|
+
current.push(entry);
|
|
164
|
+
}
|
|
165
|
+
lastTimestamp = entry.timestamp;
|
|
166
|
+
}
|
|
167
|
+
sessions.push({ start: sessionStart, end: lastTimestamp, entries: current });
|
|
168
|
+
return sessions;
|
|
169
|
+
}
|
|
170
|
+
function filterByCli(sessions, cli) {
|
|
171
|
+
return sessions.map((session) => ({
|
|
172
|
+
...session,
|
|
173
|
+
entries: session.entries.filter((e) => e.argv[0] === cli)
|
|
174
|
+
})).filter((s) => s.entries.length > 0);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/miner/transitions.ts
|
|
178
|
+
function extractSubcommand(entry) {
|
|
179
|
+
const [, ...rest] = entry.argv;
|
|
180
|
+
const sub = rest.find((arg) => !arg.startsWith("-") && !arg.startsWith("$") && /^[a-z]/i.test(arg));
|
|
181
|
+
return sub ?? "(root)";
|
|
182
|
+
}
|
|
183
|
+
function extractSubcommandPath(entry, maxDepth = 3) {
|
|
184
|
+
const [, ...rest] = entry.argv;
|
|
185
|
+
const path = [];
|
|
186
|
+
for (const arg of rest) {
|
|
187
|
+
if (arg.startsWith("-") || arg.startsWith("$"))
|
|
188
|
+
continue;
|
|
189
|
+
if (!/^[a-z]/i.test(arg))
|
|
190
|
+
break;
|
|
191
|
+
path.push(arg);
|
|
192
|
+
if (path.length >= maxDepth)
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
return path;
|
|
196
|
+
}
|
|
197
|
+
function buildTransitions(sessions) {
|
|
198
|
+
const transitions = new Map;
|
|
199
|
+
for (const session of sessions) {
|
|
200
|
+
for (let i = 0;i < session.entries.length - 1; i++) {
|
|
201
|
+
const from = extractSubcommand(session.entries[i]);
|
|
202
|
+
const to = extractSubcommand(session.entries[i + 1]);
|
|
203
|
+
if (!transitions.has(from))
|
|
204
|
+
transitions.set(from, new Map);
|
|
205
|
+
const inner = transitions.get(from);
|
|
206
|
+
inner.set(to, (inner.get(to) ?? 0) + 1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return transitions;
|
|
210
|
+
}
|
|
211
|
+
function normalizeTransitions(raw, minSupport = 3, minConfidence = 0.2) {
|
|
212
|
+
const results = [];
|
|
213
|
+
for (const [from, nexts] of raw.entries()) {
|
|
214
|
+
const total = Array.from(nexts.values()).reduce((a, b) => a + b, 0);
|
|
215
|
+
if (total < minSupport)
|
|
216
|
+
continue;
|
|
217
|
+
for (const [to, count] of nexts.entries()) {
|
|
218
|
+
if (count < minSupport)
|
|
219
|
+
continue;
|
|
220
|
+
const confidence = count / total;
|
|
221
|
+
if (confidence < minConfidence)
|
|
222
|
+
continue;
|
|
223
|
+
results.push({ from, to, count, confidence });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return results.sort((a, b) => b.count - a.count);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/miner/workflows.ts
|
|
230
|
+
function extractPaths(sessions, minLength = 2, maxLength = 7) {
|
|
231
|
+
const pathCounts = new Map;
|
|
232
|
+
for (const session of sessions) {
|
|
233
|
+
const subcommands = session.entries.map(extractSubcommand);
|
|
234
|
+
const dedupedConsecutive = dedupeConsecutive(subcommands);
|
|
235
|
+
for (let start = 0;start < dedupedConsecutive.length; start++) {
|
|
236
|
+
const maxEnd = Math.min(start + maxLength, dedupedConsecutive.length);
|
|
237
|
+
for (let end = start + minLength;end <= maxEnd; end++) {
|
|
238
|
+
const slice = dedupedConsecutive.slice(start, end);
|
|
239
|
+
const sig = slice.join(" → ");
|
|
240
|
+
const existing = pathCounts.get(sig);
|
|
241
|
+
if (existing) {
|
|
242
|
+
existing.count += 1;
|
|
243
|
+
} else {
|
|
244
|
+
pathCounts.set(sig, { path: slice, count: 1 });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return Array.from(pathCounts.entries()).map(([signature, { path, count }]) => ({
|
|
250
|
+
signature,
|
|
251
|
+
path,
|
|
252
|
+
occurrences: count
|
|
253
|
+
})).sort((a, b) => {
|
|
254
|
+
if (b.occurrences !== a.occurrences)
|
|
255
|
+
return b.occurrences - a.occurrences;
|
|
256
|
+
return b.path.length - a.path.length;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function clusterIntoWorkflows(clusters, cli, options = {}) {
|
|
260
|
+
const minSupport = options.minSupport ?? 2;
|
|
261
|
+
const topK = options.topK ?? 10;
|
|
262
|
+
const filtered = clusters.filter((c) => c.occurrences >= minSupport);
|
|
263
|
+
const deduped = removeSubPaths(filtered);
|
|
264
|
+
const totalOccurrences = clusters.reduce((sum, c) => sum + c.occurrences, 0) || 1;
|
|
265
|
+
return deduped.slice(0, topK).map((cluster) => ({
|
|
266
|
+
name: cluster.path.join("-"),
|
|
267
|
+
cli,
|
|
268
|
+
path: [cluster.path],
|
|
269
|
+
support: cluster.occurrences,
|
|
270
|
+
confidence: cluster.occurrences / totalOccurrences,
|
|
271
|
+
source: "history"
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
function dedupeConsecutive(items) {
|
|
275
|
+
const result = [];
|
|
276
|
+
for (const item of items) {
|
|
277
|
+
if (result.length === 0 || result[result.length - 1] !== item) {
|
|
278
|
+
result.push(item);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
function removeSubPaths(clusters) {
|
|
284
|
+
const sortedByLength = [...clusters].sort((a, b) => b.path.length - a.path.length);
|
|
285
|
+
const kept = [];
|
|
286
|
+
for (const cluster of sortedByLength) {
|
|
287
|
+
const sig = cluster.signature;
|
|
288
|
+
const alreadyCoveredBy = kept.find((k) => k.signature.includes(sig) && k.occurrences >= cluster.occurrences * 0.8);
|
|
289
|
+
if (!alreadyCoveredBy) {
|
|
290
|
+
kept.push(cluster);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return kept.sort((a, b) => b.occurrences - a.occurrences);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/miner/stats.ts
|
|
297
|
+
function computeStats(entries, sessions, cli) {
|
|
298
|
+
const cliEntries = entries.filter((e) => e.argv[0] === cli);
|
|
299
|
+
const cliSessions = sessions.filter((s) => s.entries.some((e) => e.argv[0] === cli));
|
|
300
|
+
const subcommandCounts = new Map;
|
|
301
|
+
const flagCounts = new Map;
|
|
302
|
+
for (const entry of cliEntries) {
|
|
303
|
+
const sub = extractSubcommand(entry);
|
|
304
|
+
subcommandCounts.set(sub, (subcommandCounts.get(sub) ?? 0) + 1);
|
|
305
|
+
for (const arg of entry.argv.slice(1)) {
|
|
306
|
+
if (arg.startsWith("--")) {
|
|
307
|
+
const flag = arg.split("=")[0];
|
|
308
|
+
flagCounts.set(flag, (flagCounts.get(flag) ?? 0) + 1);
|
|
309
|
+
} else if (arg.startsWith("-") && arg.length === 2) {
|
|
310
|
+
flagCounts.set(arg, (flagCounts.get(arg) ?? 0) + 1);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
const topSubcommands = Array.from(subcommandCounts.entries()).map(([subcommand, count]) => ({ subcommand, count })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
315
|
+
const topFlags = Array.from(flagCounts.entries()).map(([flag, count]) => ({ flag, count })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
316
|
+
return {
|
|
317
|
+
cli,
|
|
318
|
+
totalInvocations: cliEntries.length,
|
|
319
|
+
uniqueSubcommands: subcommandCounts.size,
|
|
320
|
+
topSubcommands,
|
|
321
|
+
topFlags,
|
|
322
|
+
sessionCount: cliSessions.length
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/miner/suggest.ts
|
|
327
|
+
function suggestSkills(workflows, stats) {
|
|
328
|
+
const suggestions = [];
|
|
329
|
+
for (const wf of workflows) {
|
|
330
|
+
const path = wf.path[0] ?? [];
|
|
331
|
+
if (path.length < 2)
|
|
332
|
+
continue;
|
|
333
|
+
const suggestion = workflowToSuggestion(wf, stats);
|
|
334
|
+
if (suggestion)
|
|
335
|
+
suggestions.push(suggestion);
|
|
336
|
+
}
|
|
337
|
+
return suggestions.sort((a, b) => {
|
|
338
|
+
const priorityOrder = { high: 0, medium: 1, low: 2 };
|
|
339
|
+
if (priorityOrder[a.priority] !== priorityOrder[b.priority]) {
|
|
340
|
+
return priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
341
|
+
}
|
|
342
|
+
return b.frequency - a.frequency;
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
function workflowToSuggestion(wf, stats) {
|
|
346
|
+
const path = wf.path[0] ?? [];
|
|
347
|
+
if (path.length < 2)
|
|
348
|
+
return null;
|
|
349
|
+
const commands = path.map((sub) => `${wf.cli} ${sub}`);
|
|
350
|
+
const frequency = wf.support;
|
|
351
|
+
const priority = scorePriority(frequency, path.length, stats.totalInvocations);
|
|
352
|
+
const name = generateSkillName(wf.cli, path);
|
|
353
|
+
const description = generateDescription(wf.cli, path);
|
|
354
|
+
const reason = generateReason(frequency, path, stats);
|
|
355
|
+
return { name, description, cli: wf.cli, commands, frequency, reason, priority };
|
|
356
|
+
}
|
|
357
|
+
function scorePriority(frequency, pathLength, totalInvocations) {
|
|
358
|
+
const ratio = frequency / totalInvocations;
|
|
359
|
+
const score = frequency * pathLength * (ratio + 0.1);
|
|
360
|
+
if (score > 50)
|
|
361
|
+
return "high";
|
|
362
|
+
if (score > 15)
|
|
363
|
+
return "medium";
|
|
364
|
+
return "low";
|
|
365
|
+
}
|
|
366
|
+
function generateSkillName(cli, path) {
|
|
367
|
+
const knownPatterns = {
|
|
368
|
+
"add,commit": "ship",
|
|
369
|
+
"add,commit,push": "ship",
|
|
370
|
+
"commit,push": "push-latest",
|
|
371
|
+
"install,dev": "start",
|
|
372
|
+
"install,run": "start",
|
|
373
|
+
"login,publish": "release",
|
|
374
|
+
"publish,tag": "release",
|
|
375
|
+
"checkout,pull": "sync",
|
|
376
|
+
"fetch,pull": "sync",
|
|
377
|
+
"build,push": "deploy",
|
|
378
|
+
"build,run": "test-build",
|
|
379
|
+
"compose,compose": "compose-iterate"
|
|
380
|
+
};
|
|
381
|
+
const key = path.join(",");
|
|
382
|
+
if (knownPatterns[key])
|
|
383
|
+
return knownPatterns[key];
|
|
384
|
+
return path.join("-");
|
|
385
|
+
}
|
|
386
|
+
function generateDescription(cli, path) {
|
|
387
|
+
return `Run ${cli} ${path.join(", then ")} as one command`;
|
|
388
|
+
}
|
|
389
|
+
function generateReason(frequency, path, stats) {
|
|
390
|
+
const ratio = frequency / stats.totalInvocations;
|
|
391
|
+
const pct = (ratio * 100).toFixed(0);
|
|
392
|
+
if (ratio > 0.1) {
|
|
393
|
+
return `You run this exact sequence ${frequency} times (${pct}% of all your ${stats.cli} usage)`;
|
|
394
|
+
}
|
|
395
|
+
if (frequency >= 50) {
|
|
396
|
+
return `You run this exact sequence ${frequency} times — that's a lot of repetition`;
|
|
397
|
+
}
|
|
398
|
+
if (frequency >= 20) {
|
|
399
|
+
return `You run this exact sequence ${frequency} times`;
|
|
400
|
+
}
|
|
401
|
+
return `Detected ${frequency} times in your shell history`;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/miner/index.ts
|
|
405
|
+
async function mineCli(cli, options = {}) {
|
|
406
|
+
const path = options.historyPath ?? defaultHistoryPath();
|
|
407
|
+
if (!path) {
|
|
408
|
+
throw new Error("Could not determine shell history path. Pass historyPath explicitly.");
|
|
409
|
+
}
|
|
410
|
+
const text = await readHistoryFile(path);
|
|
411
|
+
const entries = parseHistory(text);
|
|
412
|
+
const allSessions = segmentSessions(entries, options.sessionGapMinutes ?? 10);
|
|
413
|
+
const cliSessions = filterByCli(allSessions, cli);
|
|
414
|
+
const stats = computeStats(entries, allSessions, cli);
|
|
415
|
+
const rawTransitions = buildTransitions(cliSessions);
|
|
416
|
+
const transitions = normalizeTransitions(rawTransitions, options.minSupport ?? 3, options.minConfidence ?? 0.2);
|
|
417
|
+
const paths = extractPaths(cliSessions, options.minPathLength ?? 2, options.maxPathLength ?? 6);
|
|
418
|
+
const workflows = clusterIntoWorkflows(paths, cli, {
|
|
419
|
+
minSupport: options.minSupport ?? 3
|
|
420
|
+
});
|
|
421
|
+
const suggestions = suggestSkills(workflows, stats);
|
|
422
|
+
return {
|
|
423
|
+
cli,
|
|
424
|
+
stats,
|
|
425
|
+
transitions,
|
|
426
|
+
workflows,
|
|
427
|
+
suggestions,
|
|
428
|
+
sessionsAnalyzed: cliSessions.length
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export { readHistoryFile, detectHistoryFormat, parseHistory, tokenize, defaultHistoryPath, segmentSessions, filterByCli, extractSubcommand, extractSubcommandPath, buildTransitions, normalizeTransitions, extractPaths, clusterIntoWorkflows, computeStats, suggestSkills, mineCli };
|
|
433
|
+
|
|
434
|
+
//# debugId=83F3B582B342B95E64756E2164756E21
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/miner/history.ts", "../src/miner/sessions.ts", "../src/miner/transitions.ts", "../src/miner/workflows.ts", "../src/miner/stats.ts", "../src/miner/suggest.ts", "../src/miner/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { HistoryEntry } from \"./types\";\n\nexport async function readHistoryFile(path: string): Promise<string> {\n\treturn await Bun.file(path).text();\n}\n\nexport function detectHistoryFormat(text: string): \"zsh-extended\" | \"bash\" | \"fish\" | \"unknown\" {\n\tconst firstNonEmpty = text.split(\"\\n\").find(l => l.trim().length > 0);\n\tif (!firstNonEmpty) return \"unknown\";\n\tif (/^: \\d+:\\d+;/.test(firstNonEmpty)) return \"zsh-extended\";\n\tif (/^- cmd: /.test(firstNonEmpty)) return \"fish\";\n\treturn \"bash\";\n}\n\nexport function parseHistory(text: string): HistoryEntry[] {\n\tconst format = detectHistoryFormat(text);\n\tswitch (format) {\n\t\tcase \"zsh-extended\":\n\t\t\treturn parseZshExtended(text);\n\t\tcase \"fish\":\n\t\t\treturn parseFishHistory(text);\n\t\tcase \"bash\":\n\t\tdefault:\n\t\t\treturn parseBashHistory(text);\n\t}\n}\n\nfunction parseZshExtended(text: string): HistoryEntry[] {\n\tconst entries: HistoryEntry[] = [];\n\tconst lines = text.split(\"\\n\");\n\n\tlet pending: { timestamp: number; cmd: string } | null = null;\n\n\tfor (const rawLine of lines) {\n\t\tconst match = rawLine.match(/^: (\\d+):\\d+;(.*)$/);\n\t\tif (match) {\n\t\t\tif (pending) {\n\t\t\t\tentries.push(buildEntry(pending.timestamp, pending.cmd));\n\t\t\t}\n\t\t\tpending = { timestamp: Number.parseInt(match[1]!, 10), cmd: match[2]! };\n\t\t} else if (pending) {\n\t\t\tpending.cmd += \"\\n\" + rawLine;\n\t\t}\n\t}\n\n\tif (pending) {\n\t\tentries.push(buildEntry(pending.timestamp, pending.cmd));\n\t}\n\n\treturn entries.filter(e => e.argv.length > 0);\n}\n\nfunction parseBashHistory(text: string): HistoryEntry[] {\n\tconst entries: HistoryEntry[] = [];\n\tconst lines = text.split(\"\\n\");\n\tlet currentTimestamp = 0;\n\n\tfor (const line of lines) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed) continue;\n\n\t\tif (/^#\\d+$/.test(trimmed)) {\n\t\t\tcurrentTimestamp = Number.parseInt(trimmed.slice(1), 10);\n\t\t\tcontinue;\n\t\t}\n\n\t\tentries.push(buildEntry(currentTimestamp, trimmed));\n\t}\n\n\treturn entries.filter(e => e.argv.length > 0);\n}\n\nfunction parseFishHistory(text: string): HistoryEntry[] {\n\tconst entries: HistoryEntry[] = [];\n\tconst blocks = text.split(/\\n(?=- cmd: )/);\n\n\tfor (const block of blocks) {\n\t\tconst cmdMatch = block.match(/- cmd: (.*)/);\n\t\tconst whenMatch = block.match(/when: (\\d+)/);\n\t\tif (!cmdMatch) continue;\n\t\tconst timestamp = whenMatch ? Number.parseInt(whenMatch[1]!, 10) : 0;\n\t\tentries.push(buildEntry(timestamp, cmdMatch[1]!));\n\t}\n\n\treturn entries.filter(e => e.argv.length > 0);\n}\n\nfunction buildEntry(timestamp: number, rawCmd: string): HistoryEntry {\n\tconst cleaned = stripContinuations(rawCmd).trim();\n\tconst argv = tokenize(cleaned);\n\treturn { timestamp, raw: cleaned, argv };\n}\n\nfunction stripContinuations(cmd: string): string {\n\treturn cmd.replace(/\\\\\\n/g, \" \").replace(/\\n\\s*/g, \" \");\n}\n\nexport function tokenize(cmd: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet inSingle = false;\n\tlet inDouble = false;\n\tlet escape = false;\n\n\tfor (let i = 0; i < cmd.length; i++) {\n\t\tconst ch = cmd[i]!;\n\t\tif (escape) {\n\t\t\tcurrent += ch;\n\t\t\tescape = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"\\\\\" && !inSingle) {\n\t\t\tescape = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === \"'\" && !inDouble) {\n\t\t\tinSingle = !inSingle;\n\t\t\tcontinue;\n\t\t}\n\t\tif (ch === '\"' && !inSingle) {\n\t\t\tinDouble = !inDouble;\n\t\t\tcontinue;\n\t\t}\n\t\tif (/\\s/.test(ch) && !inSingle && !inDouble) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif ((ch === \"|\" || ch === \";\" || ch === \"&\") && !inSingle && !inDouble) {\n\t\t\tif (current) {\n\t\t\t\ttokens.push(current);\n\t\t\t\tcurrent = \"\";\n\t\t\t}\n\t\t\treturn tokens;\n\t\t}\n\t\tcurrent += ch;\n\t}\n\n\tif (current) tokens.push(current);\n\treturn tokens;\n}\n\nexport function defaultHistoryPath(): string | null {\n\tconst home = process.env.HOME ?? \"\";\n\tif (!home) return null;\n\n\tconst candidates = [\n\t\t`${home}/.zsh_history`,\n\t\t`${home}/.bash_history`,\n\t\t`${home}/.config/fish/fish_history`,\n\t];\n\n\treturn candidates[0] ?? null;\n}\n",
|
|
6
|
+
"import type { HistoryEntry, Session } from \"./types\";\n\nexport function segmentSessions(entries: HistoryEntry[], gapMinutes = 10): Session[] {\n\tif (entries.length === 0) return [];\n\n\tconst sorted = [...entries].sort((a, b) => a.timestamp - b.timestamp);\n\tconst gapSeconds = gapMinutes * 60;\n\tconst sessions: Session[] = [];\n\n\tlet current: HistoryEntry[] = [sorted[0]!];\n\tlet sessionStart = sorted[0]!.timestamp;\n\tlet lastTimestamp = sorted[0]!.timestamp;\n\n\tfor (let i = 1; i < sorted.length; i++) {\n\t\tconst entry = sorted[i]!;\n\t\tconst gap = entry.timestamp - lastTimestamp;\n\n\t\tif (gap > gapSeconds) {\n\t\t\tsessions.push({ start: sessionStart, end: lastTimestamp, entries: current });\n\t\t\tcurrent = [entry];\n\t\t\tsessionStart = entry.timestamp;\n\t\t} else {\n\t\t\tcurrent.push(entry);\n\t\t}\n\t\tlastTimestamp = entry.timestamp;\n\t}\n\n\tsessions.push({ start: sessionStart, end: lastTimestamp, entries: current });\n\treturn sessions;\n}\n\nexport function filterByCli(sessions: Session[], cli: string): Session[] {\n\treturn sessions\n\t\t.map(session => ({\n\t\t\t...session,\n\t\t\tentries: session.entries.filter(e => e.argv[0] === cli),\n\t\t}))\n\t\t.filter(s => s.entries.length > 0);\n}\n",
|
|
7
|
+
"import type { Session, Transition, HistoryEntry } from \"./types\";\n\nexport function extractSubcommand(entry: HistoryEntry): string {\n\tconst [, ...rest] = entry.argv;\n\tconst sub = rest.find(arg => !arg.startsWith(\"-\") && !arg.startsWith(\"$\") && /^[a-z]/i.test(arg));\n\treturn sub ?? \"(root)\";\n}\n\nexport function extractSubcommandPath(entry: HistoryEntry, maxDepth = 3): string[] {\n\tconst [, ...rest] = entry.argv;\n\tconst path: string[] = [];\n\tfor (const arg of rest) {\n\t\tif (arg.startsWith(\"-\") || arg.startsWith(\"$\")) continue;\n\t\tif (!/^[a-z]/i.test(arg)) break;\n\t\tpath.push(arg);\n\t\tif (path.length >= maxDepth) break;\n\t}\n\treturn path;\n}\n\nexport function buildTransitions(sessions: Session[]): Map<string, Map<string, number>> {\n\tconst transitions = new Map<string, Map<string, number>>();\n\n\tfor (const session of sessions) {\n\t\tfor (let i = 0; i < session.entries.length - 1; i++) {\n\t\t\tconst from = extractSubcommand(session.entries[i]!);\n\t\t\tconst to = extractSubcommand(session.entries[i + 1]!);\n\n\t\t\tif (!transitions.has(from)) transitions.set(from, new Map());\n\t\t\tconst inner = transitions.get(from)!;\n\t\t\tinner.set(to, (inner.get(to) ?? 0) + 1);\n\t\t}\n\t}\n\n\treturn transitions;\n}\n\nexport function normalizeTransitions(\n\traw: Map<string, Map<string, number>>,\n\tminSupport = 3,\n\tminConfidence = 0.2,\n): Transition[] {\n\tconst results: Transition[] = [];\n\n\tfor (const [from, nexts] of raw.entries()) {\n\t\tconst total = Array.from(nexts.values()).reduce((a, b) => a + b, 0);\n\t\tif (total < minSupport) continue;\n\n\t\tfor (const [to, count] of nexts.entries()) {\n\t\t\tif (count < minSupport) continue;\n\t\t\tconst confidence = count / total;\n\t\t\tif (confidence < minConfidence) continue;\n\t\t\tresults.push({ from, to, count, confidence });\n\t\t}\n\t}\n\n\treturn results.sort((a, b) => b.count - a.count);\n}\n\nexport function topTransitions(transitions: Transition[], n = 10): Transition[] {\n\treturn transitions.slice(0, n);\n}\n",
|
|
8
|
+
"import type { Session, MinedWorkflow } from \"./types\";\nimport { extractSubcommand } from \"./transitions\";\n\nexport interface PathCluster {\n\tsignature: string;\n\tpath: string[];\n\toccurrences: number;\n}\n\nexport function extractPaths(\n\tsessions: Session[],\n\tminLength = 2,\n\tmaxLength = 7,\n): PathCluster[] {\n\tconst pathCounts = new Map<string, { path: string[]; count: number }>();\n\n\tfor (const session of sessions) {\n\t\tconst subcommands = session.entries.map(extractSubcommand);\n\t\tconst dedupedConsecutive = dedupeConsecutive(subcommands);\n\n\t\tfor (let start = 0; start < dedupedConsecutive.length; start++) {\n\t\t\tconst maxEnd = Math.min(start + maxLength, dedupedConsecutive.length);\n\t\t\tfor (let end = start + minLength; end <= maxEnd; end++) {\n\t\t\t\tconst slice = dedupedConsecutive.slice(start, end);\n\t\t\t\tconst sig = slice.join(\" → \");\n\t\t\t\tconst existing = pathCounts.get(sig);\n\t\t\t\tif (existing) {\n\t\t\t\t\texisting.count += 1;\n\t\t\t\t} else {\n\t\t\t\t\tpathCounts.set(sig, { path: slice, count: 1 });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Array.from(pathCounts.entries())\n\t\t.map(([signature, { path, count }]) => ({\n\t\t\tsignature,\n\t\t\tpath,\n\t\t\toccurrences: count,\n\t\t}))\n\t\t.sort((a, b) => {\n\t\t\tif (b.occurrences !== a.occurrences) return b.occurrences - a.occurrences;\n\t\t\treturn b.path.length - a.path.length;\n\t\t});\n}\n\nexport function clusterIntoWorkflows(\n\tclusters: PathCluster[],\n\tcli: string,\n\toptions: { minSupport?: number; topK?: number } = {},\n): MinedWorkflow[] {\n\tconst minSupport = options.minSupport ?? 2;\n\tconst topK = options.topK ?? 10;\n\n\tconst filtered = clusters.filter(c => c.occurrences >= minSupport);\n\tconst deduped = removeSubPaths(filtered);\n\n\tconst totalOccurrences = clusters.reduce((sum, c) => sum + c.occurrences, 0) || 1;\n\n\treturn deduped.slice(0, topK).map(cluster => ({\n\t\tname: cluster.path.join(\"-\"),\n\t\tcli,\n\t\tpath: [cluster.path],\n\t\tsupport: cluster.occurrences,\n\t\tconfidence: cluster.occurrences / totalOccurrences,\n\t\tsource: \"history\" as const,\n\t}));\n}\n\nfunction dedupeConsecutive(items: string[]): string[] {\n\tconst result: string[] = [];\n\tfor (const item of items) {\n\t\tif (result.length === 0 || result[result.length - 1] !== item) {\n\t\t\tresult.push(item);\n\t\t}\n\t}\n\treturn result;\n}\n\nfunction removeSubPaths(clusters: PathCluster[]): PathCluster[] {\n\tconst sortedByLength = [...clusters].sort((a, b) => b.path.length - a.path.length);\n\tconst kept: PathCluster[] = [];\n\n\tfor (const cluster of sortedByLength) {\n\t\tconst sig = cluster.signature;\n\t\tconst alreadyCoveredBy = kept.find(k =>\n\t\t\tk.signature.includes(sig) && k.occurrences >= cluster.occurrences * 0.8,\n\t\t);\n\t\tif (!alreadyCoveredBy) {\n\t\t\tkept.push(cluster);\n\t\t}\n\t}\n\n\treturn kept.sort((a, b) => b.occurrences - a.occurrences);\n}\n",
|
|
9
|
+
"import type { HistoryEntry, CliUsageStats, Session } from \"./types\";\nimport { extractSubcommand } from \"./transitions\";\n\nexport function computeStats(entries: HistoryEntry[], sessions: Session[], cli: string): CliUsageStats {\n\tconst cliEntries = entries.filter(e => e.argv[0] === cli);\n\tconst cliSessions = sessions.filter(s => s.entries.some(e => e.argv[0] === cli));\n\n\tconst subcommandCounts = new Map<string, number>();\n\tconst flagCounts = new Map<string, number>();\n\n\tfor (const entry of cliEntries) {\n\t\tconst sub = extractSubcommand(entry);\n\t\tsubcommandCounts.set(sub, (subcommandCounts.get(sub) ?? 0) + 1);\n\n\t\tfor (const arg of entry.argv.slice(1)) {\n\t\t\tif (arg.startsWith(\"--\")) {\n\t\t\t\tconst flag = arg.split(\"=\")[0]!;\n\t\t\t\tflagCounts.set(flag, (flagCounts.get(flag) ?? 0) + 1);\n\t\t\t} else if (arg.startsWith(\"-\") && arg.length === 2) {\n\t\t\t\tflagCounts.set(arg, (flagCounts.get(arg) ?? 0) + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst topSubcommands = Array.from(subcommandCounts.entries())\n\t\t.map(([subcommand, count]) => ({ subcommand, count }))\n\t\t.sort((a, b) => b.count - a.count)\n\t\t.slice(0, 10);\n\n\tconst topFlags = Array.from(flagCounts.entries())\n\t\t.map(([flag, count]) => ({ flag, count }))\n\t\t.sort((a, b) => b.count - a.count)\n\t\t.slice(0, 10);\n\n\treturn {\n\t\tcli,\n\t\ttotalInvocations: cliEntries.length,\n\t\tuniqueSubcommands: subcommandCounts.size,\n\t\ttopSubcommands,\n\t\ttopFlags,\n\t\tsessionCount: cliSessions.length,\n\t};\n}\n",
|
|
10
|
+
"import type { MinedWorkflow, CliUsageStats } from \"./types\";\n\nexport interface SkillSuggestion {\n\tname: string;\n\tdescription: string;\n\tcli: string;\n\tcommands: string[];\n\tfrequency: number;\n\treason: string;\n\tpriority: \"high\" | \"medium\" | \"low\";\n}\n\nexport function suggestSkills(\n\tworkflows: MinedWorkflow[],\n\tstats: CliUsageStats,\n): SkillSuggestion[] {\n\tconst suggestions: SkillSuggestion[] = [];\n\n\tfor (const wf of workflows) {\n\t\tconst path = wf.path[0] ?? [];\n\t\tif (path.length < 2) continue;\n\n\t\tconst suggestion = workflowToSuggestion(wf, stats);\n\t\tif (suggestion) suggestions.push(suggestion);\n\t}\n\n\treturn suggestions.sort((a, b) => {\n\t\tconst priorityOrder = { high: 0, medium: 1, low: 2 };\n\t\tif (priorityOrder[a.priority] !== priorityOrder[b.priority]) {\n\t\t\treturn priorityOrder[a.priority] - priorityOrder[b.priority];\n\t\t}\n\t\treturn b.frequency - a.frequency;\n\t});\n}\n\nfunction workflowToSuggestion(wf: MinedWorkflow, stats: CliUsageStats): SkillSuggestion | null {\n\tconst path = wf.path[0] ?? [];\n\tif (path.length < 2) return null;\n\n\tconst commands = path.map(sub => `${wf.cli} ${sub}`);\n\tconst frequency = wf.support;\n\n\tconst priority = scorePriority(frequency, path.length, stats.totalInvocations);\n\n\tconst name = generateSkillName(wf.cli, path);\n\tconst description = generateDescription(wf.cli, path);\n\tconst reason = generateReason(frequency, path, stats);\n\n\treturn { name, description, cli: wf.cli, commands, frequency, reason, priority };\n}\n\nfunction scorePriority(frequency: number, pathLength: number, totalInvocations: number): \"high\" | \"medium\" | \"low\" {\n\tconst ratio = frequency / totalInvocations;\n\tconst score = frequency * pathLength * (ratio + 0.1);\n\n\tif (score > 50) return \"high\";\n\tif (score > 15) return \"medium\";\n\treturn \"low\";\n}\n\nfunction generateSkillName(cli: string, path: string[]): string {\n\tconst knownPatterns: Record<string, string> = {\n\t\t\"add,commit\": \"ship\",\n\t\t\"add,commit,push\": \"ship\",\n\t\t\"commit,push\": \"push-latest\",\n\t\t\"install,dev\": \"start\",\n\t\t\"install,run\": \"start\",\n\t\t\"login,publish\": \"release\",\n\t\t\"publish,tag\": \"release\",\n\t\t\"checkout,pull\": \"sync\",\n\t\t\"fetch,pull\": \"sync\",\n\t\t\"build,push\": \"deploy\",\n\t\t\"build,run\": \"test-build\",\n\t\t\"compose,compose\": \"compose-iterate\",\n\t};\n\n\tconst key = path.join(\",\");\n\tif (knownPatterns[key]) return knownPatterns[key]!;\n\n\treturn path.join(\"-\");\n}\n\nfunction generateDescription(cli: string, path: string[]): string {\n\treturn `Run ${cli} ${path.join(\", then \")} as one command`;\n}\n\nfunction generateReason(frequency: number, path: string[], stats: CliUsageStats): string {\n\tconst ratio = frequency / stats.totalInvocations;\n\tconst pct = (ratio * 100).toFixed(0);\n\n\tif (ratio > 0.1) {\n\t\treturn `You run this exact sequence ${frequency} times (${pct}% of all your ${stats.cli} usage)`;\n\t}\n\tif (frequency >= 50) {\n\t\treturn `You run this exact sequence ${frequency} times — that's a lot of repetition`;\n\t}\n\tif (frequency >= 20) {\n\t\treturn `You run this exact sequence ${frequency} times`;\n\t}\n\treturn `Detected ${frequency} times in your shell history`;\n}\n",
|
|
11
|
+
"import { parseHistory, readHistoryFile, defaultHistoryPath, tokenize, detectHistoryFormat } from \"./history\";\nimport { segmentSessions, filterByCli } from \"./sessions\";\nimport { buildTransitions, normalizeTransitions, extractSubcommand, extractSubcommandPath } from \"./transitions\";\nimport { extractPaths, clusterIntoWorkflows } from \"./workflows\";\nimport { computeStats } from \"./stats\";\nimport { suggestSkills, type SkillSuggestion } from \"./suggest\";\nimport type { HistoryEntry, Session, Transition, MinedWorkflow, CliUsageStats, MineOptions } from \"./types\";\n\nexport type { HistoryEntry, Session, Transition, MinedWorkflow, CliUsageStats, MineOptions, SkillSuggestion };\nexport {\n\tparseHistory,\n\treadHistoryFile,\n\tdefaultHistoryPath,\n\ttokenize,\n\tdetectHistoryFormat,\n\tsegmentSessions,\n\tfilterByCli,\n\tbuildTransitions,\n\tnormalizeTransitions,\n\textractSubcommand,\n\textractSubcommandPath,\n\textractPaths,\n\tclusterIntoWorkflows,\n\tcomputeStats,\n\tsuggestSkills,\n};\n\nexport interface MineResult {\n\tcli: string;\n\tstats: CliUsageStats;\n\ttransitions: Transition[];\n\tworkflows: MinedWorkflow[];\n\tsuggestions: SkillSuggestion[];\n\tsessionsAnalyzed: number;\n}\n\nexport async function mineCli(cli: string, options: MineOptions = {}): Promise<MineResult> {\n\tconst path = options.historyPath ?? defaultHistoryPath();\n\tif (!path) {\n\t\tthrow new Error(\"Could not determine shell history path. Pass historyPath explicitly.\");\n\t}\n\n\tconst text = await readHistoryFile(path);\n\tconst entries = parseHistory(text);\n\tconst allSessions = segmentSessions(entries, options.sessionGapMinutes ?? 10);\n\tconst cliSessions = filterByCli(allSessions, cli);\n\n\tconst stats = computeStats(entries, allSessions, cli);\n\n\tconst rawTransitions = buildTransitions(cliSessions);\n\tconst transitions = normalizeTransitions(\n\t\trawTransitions,\n\t\toptions.minSupport ?? 3,\n\t\toptions.minConfidence ?? 0.2,\n\t);\n\n\tconst paths = extractPaths(\n\t\tcliSessions,\n\t\toptions.minPathLength ?? 2,\n\t\toptions.maxPathLength ?? 6,\n\t);\n\tconst workflows = clusterIntoWorkflows(paths, cli, {\n\t\tminSupport: options.minSupport ?? 3,\n\t});\n\n\tconst suggestions = suggestSkills(workflows, stats);\n\n\treturn {\n\t\tcli,\n\t\tstats,\n\t\ttransitions,\n\t\tworkflows,\n\t\tsuggestions,\n\t\tsessionsAnalyzed: cliSessions.length,\n\t};\n}\n"
|
|
12
|
+
],
|
|
13
|
+
"mappings": "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",
|
|
14
|
+
"debugId": "83F3B582B342B95E64756E2164756E21",
|
|
15
|
+
"names": []
|
|
16
|
+
}
|