learn_bash_from_session_data 1.0.4 → 1.0.6

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.
@@ -0,0 +1,163 @@
1
+ {
2
+ "metadata": {
3
+ "generated_at": "2026-02-05T15:59:49.814630",
4
+ "run_id": "run-2026-02-05-155949",
5
+ "version": "1.0.5"
6
+ },
7
+ "input": {
8
+ "sessions_processed": 10,
9
+ "session_files": [
10
+ {
11
+ "filename": "081cfcfd-cde6-4304-89ca-6ac61faf8d85.jsonl",
12
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand-Development-Project-Workspace-active-development-session-with-chris/081cfcfd-cde6-4304-89ca-6ac61faf8d85.jsonl",
13
+ "size": "759.1 KB",
14
+ "modified": "2026-02-05 11:01:08"
15
+ },
16
+ {
17
+ "filename": "2ee58010-e794-48d8-a1b7-0ba14e06e7b7.jsonl",
18
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/2ee58010-e794-48d8-a1b7-0ba14e06e7b7.jsonl",
19
+ "size": "1.8 KB",
20
+ "modified": "2026-02-05 10:22:23"
21
+ },
22
+ {
23
+ "filename": "ef6494ae-aca1-43b1-800d-d5586069d42c.jsonl",
24
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/ef6494ae-aca1-43b1-800d-d5586069d42c.jsonl",
25
+ "size": "1.8 KB",
26
+ "modified": "2026-01-23 14:32:05"
27
+ },
28
+ {
29
+ "filename": "6b1fb6cd-5865-4a5f-97f9-5e1e46d79f81.jsonl",
30
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/6b1fb6cd-5865-4a5f-97f9-5e1e46d79f81.jsonl",
31
+ "size": "3.7 KB",
32
+ "modified": "2026-01-19 22:44:52"
33
+ },
34
+ {
35
+ "filename": "9db5f466-5bc4-4778-94ba-b10d72e8c464.jsonl",
36
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/9db5f466-5bc4-4778-94ba-b10d72e8c464.jsonl",
37
+ "size": "916.0 B",
38
+ "modified": "2026-01-19 09:25:47"
39
+ },
40
+ {
41
+ "filename": "dfc82e92-9786-4baf-a5df-44046f21f90a.jsonl",
42
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand-Development-Project-Workspace-active-development-en-consulta/dfc82e92-9786-4baf-a5df-44046f21f90a.jsonl",
43
+ "size": "1.0 KB",
44
+ "modified": "2026-01-16 06:42:34"
45
+ },
46
+ {
47
+ "filename": "b3c0facc-2728-48a5-a1d0-0fed6b049cf2.jsonl",
48
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/b3c0facc-2728-48a5-a1d0-0fed6b049cf2.jsonl",
49
+ "size": "116.0 B",
50
+ "modified": "2026-01-08 21:01:17"
51
+ },
52
+ {
53
+ "filename": "846d1c3c-05e3-4d86-9aae-c8bd266962d4.jsonl",
54
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/846d1c3c-05e3-4d86-9aae-c8bd266962d4.jsonl",
55
+ "size": "1.8 KB",
56
+ "modified": "2026-01-08 21:00:57"
57
+ },
58
+ {
59
+ "filename": "agent-a979d7c.jsonl",
60
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/agent-a979d7c.jsonl",
61
+ "size": "2.0 KB",
62
+ "modified": "2026-01-06 20:47:47"
63
+ },
64
+ {
65
+ "filename": "agent-a1fb460.jsonl",
66
+ "path": "/mnt/c/Users/brand/.claude/projects/C--Users-brand/agent-a1fb460.jsonl",
67
+ "size": "1.9 KB",
68
+ "modified": "2026-01-06 20:47:44"
69
+ }
70
+ ],
71
+ "total_entries": 438
72
+ },
73
+ "analysis": {
74
+ "raw_commands_found": 37,
75
+ "unique_commands": 53,
76
+ "categories": [
77
+ "Unknown",
78
+ "Package Management",
79
+ "Text Processing",
80
+ "File System",
81
+ "Shell Builtins",
82
+ "Development",
83
+ "Git",
84
+ "Search & Navigation",
85
+ "Process & System"
86
+ ],
87
+ "category_counts": {
88
+ "Unknown": 5,
89
+ "Package Management": 4,
90
+ "Text Processing": 3,
91
+ "File System": 7,
92
+ "Shell Builtins": 3,
93
+ "Development": 5,
94
+ "Git": 24,
95
+ "Search & Navigation": 1,
96
+ "Process & System": 1
97
+ },
98
+ "top_base_commands": [
99
+ {
100
+ "command": "cd",
101
+ "count": 18
102
+ },
103
+ {
104
+ "command": "git",
105
+ "count": 17
106
+ },
107
+ {
108
+ "command": "gh",
109
+ "count": 9
110
+ },
111
+ {
112
+ "command": "python",
113
+ "count": 5
114
+ },
115
+ {
116
+ "command": "mkdir",
117
+ "count": 3
118
+ },
119
+ {
120
+ "command": "ls",
121
+ "count": 3
122
+ },
123
+ {
124
+ "command": "echo",
125
+ "count": 3
126
+ },
127
+ {
128
+ "command": "session-slides",
129
+ "count": 3
130
+ },
131
+ {
132
+ "command": "pip",
133
+ "count": 2
134
+ },
135
+ {
136
+ "command": "head",
137
+ "count": 2
138
+ }
139
+ ],
140
+ "operators_used": {
141
+ "||": 4,
142
+ "|": 3,
143
+ "2>&1": 8,
144
+ "2>/dev/null": 5,
145
+ "&&": 29,
146
+ ">": 5,
147
+ "<": 13
148
+ },
149
+ "complexity_distribution": {
150
+ "1": 10,
151
+ "2": 23,
152
+ "3": 15,
153
+ "4": 3,
154
+ "5": 2
155
+ }
156
+ },
157
+ "output": {
158
+ "quiz_questions": 20,
159
+ "html_files": [
160
+ "bash-learner-output/run-2026-02-05-155949/index.html"
161
+ ]
162
+ }
163
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "learn_bash_from_session_data",
3
- "version": "1.0.4",
4
- "description": "Learn bash from your Claude Code sessions - extracts commands and generates interactive HTML lessons",
3
+ "version": "1.0.6",
4
+ "description": "Learn bash from your Claude Code sessions - extracts commands and generates interactive HTML lessons with 400+ commands, quizzes, and comprehensive coverage",
5
5
  "main": "bin/learn-bash.js",
6
6
  "bin": {
7
7
  "learn-bash": "bin/learn-bash.js",
@@ -16,7 +16,11 @@
16
16
  "claude",
17
17
  "cli",
18
18
  "terminal",
19
- "shell"
19
+ "shell",
20
+ "quiz",
21
+ "commands",
22
+ "interactive",
23
+ "education"
20
24
  ],
21
25
  "author": "",
22
26
  "license": "MIT",
@@ -114,19 +114,52 @@ def _generate_html_impl(analysis_result: dict[str, Any], quizzes: list[dict[str,
114
114
  </html>'''
115
115
 
116
116
 
117
+ def _generate_operators_html(operators_used: dict, operator_descriptions: dict) -> str:
118
+ """Generate HTML for the operators used section."""
119
+ if not operators_used:
120
+ return '<p class="empty-state">No bash operators detected in these commands</p>'
121
+
122
+ operators_html = ""
123
+ # Sort by count descending
124
+ sorted_ops = sorted(operators_used.items(), key=lambda x: -x[1])
125
+ max_count = sorted_ops[0][1] if sorted_ops else 1
126
+
127
+ for op, count in sorted_ops:
128
+ name, desc = operator_descriptions.get(op, (op, 'Bash operator'))
129
+ bar_width = (count / max_count) * 100
130
+ operators_html += f'''
131
+ <div class="operator-item">
132
+ <div class="operator-symbol"><code>{html.escape(op)}</code></div>
133
+ <div class="operator-info">
134
+ <div class="operator-name">{html.escape(name)}</div>
135
+ <div class="operator-desc">{html.escape(desc)}</div>
136
+ </div>
137
+ <div class="operator-bar-container">
138
+ <div class="operator-bar" style="width: {bar_width}%"></div>
139
+ </div>
140
+ <div class="operator-count">{count}</div>
141
+ </div>'''
142
+ return operators_html
143
+
144
+
117
145
  def render_overview_tab(stats: dict[str, Any], commands: list[dict], categories: dict) -> str:
118
146
  """Render the overview/dashboard tab content."""
119
147
  total_commands = stats.get("total_commands", 0)
120
148
  unique_commands = stats.get("unique_commands", 0)
121
149
  unique_utilities = stats.get("unique_utilities", 0)
122
150
  date_range = stats.get("date_range", {"start": "N/A", "end": "N/A"})
123
- complexity_dist = stats.get("complexity_distribution", {"simple": 0, "intermediate": 0, "advanced": 0})
124
-
125
- # Calculate percentages for complexity bars
126
- total_for_pct = sum(complexity_dist.values()) or 1
127
- simple_pct = (complexity_dist.get("simple", 0) / total_for_pct) * 100
128
- intermediate_pct = (complexity_dist.get("intermediate", 0) / total_for_pct) * 100
129
- advanced_pct = (complexity_dist.get("advanced", 0) / total_for_pct) * 100
151
+ # Get operators data for the "Bash Operators Used" section
152
+ operators_used = stats.get("operators_used", {})
153
+ operator_descriptions = {
154
+ '|': ('Pipe', 'Sends output of one command to input of another'),
155
+ '||': ('OR operator', 'Run next command if previous failed'),
156
+ '&&': ('AND operator', 'Run next command if previous succeeded'),
157
+ '2>&1': ('Redirect stderr', 'Combines error output with standard output'),
158
+ '2>/dev/null': ('Suppress errors', 'Discards error messages'),
159
+ '>': ('Redirect output', 'Writes output to a file (overwrites)'),
160
+ '>>': ('Append output', 'Appends output to a file'),
161
+ '<': ('Redirect input', 'Reads input from a file'),
162
+ }
130
163
 
131
164
  # Top 10 commands by frequency - use pre-computed data if available
132
165
  top_commands_data = stats.get("top_commands", [])
@@ -237,29 +270,9 @@ def render_overview_tab(stats: dict[str, Any], commands: list[dict], categories:
237
270
 
238
271
  <div class="charts-row">
239
272
  <div class="chart-card">
240
- <h3>Complexity Distribution</h3>
241
- <div class="complexity-bars">
242
- <div class="complexity-row">
243
- <span class="complexity-label simple">Simple</span>
244
- <div class="complexity-bar-bg">
245
- <div class="complexity-bar simple" style="width: {simple_pct}%"></div>
246
- </div>
247
- <span class="complexity-count">{complexity_dist.get("simple", 0)}</span>
248
- </div>
249
- <div class="complexity-row">
250
- <span class="complexity-label intermediate">Intermediate</span>
251
- <div class="complexity-bar-bg">
252
- <div class="complexity-bar intermediate" style="width: {intermediate_pct}%"></div>
253
- </div>
254
- <span class="complexity-count">{complexity_dist.get("intermediate", 0)}</span>
255
- </div>
256
- <div class="complexity-row">
257
- <span class="complexity-label advanced">Advanced</span>
258
- <div class="complexity-bar-bg">
259
- <div class="complexity-bar advanced" style="width: {advanced_pct}%"></div>
260
- </div>
261
- <span class="complexity-count">{complexity_dist.get("advanced", 0)}</span>
262
- </div>
273
+ <h3>Bash Operators Used</h3>
274
+ <div class="operators-list">
275
+ {_generate_operators_html(operators_used, operator_descriptions)}
263
276
  </div>
264
277
  </div>
265
278
 
@@ -385,11 +398,10 @@ def render_commands_tab(commands: list[dict]) -> str:
385
398
  </div>'''
386
399
 
387
400
  commands_html += f'''
388
- <div class="command-card" data-category="{category}" data-complexity="{complexity}" data-frequency="{frequency}" data-name="{base_cmd}">
401
+ <div class="command-card" data-category="{category}" data-frequency="{frequency}" data-name="{base_cmd}">
389
402
  <div class="command-header" onclick="toggleCommand('{cmd_id}')">
390
403
  <div class="command-main">
391
404
  <code class="cmd">{base_cmd}</code>
392
- <span class="complexity-badge {complexity}">{complexity}</span>
393
405
  <span class="category-badge">{category}</span>
394
406
  </div>
395
407
  <div class="command-meta">
@@ -504,7 +516,6 @@ def render_lessons_tab(categories: dict, commands: list[dict]) -> str:
504
516
  <div class="lesson-command">
505
517
  <div class="lesson-command-header">
506
518
  <code class="cmd">{base_cmd}</code>
507
- <span class="complexity-badge {complexity}">{complexity}</span>
508
519
  </div>
509
520
  <pre class="syntax-highlighted">{highlighted}</pre>
510
521
  <p class="lesson-description">{description}</p>
@@ -548,20 +559,17 @@ def render_lessons_tab(categories: dict, commands: list[dict]) -> str:
548
559
  def _get_category_concept(category: str) -> str:
549
560
  """Get concept overview for a category."""
550
561
  concepts = {
551
- "File Management": "Commands for creating, copying, moving, deleting, and organizing files and directories in the filesystem.",
552
- "Text Processing": "Tools for searching, filtering, transforming, and manipulating text content in files and streams.",
553
- "System Administration": "Commands for managing system resources, processes, users, and system configuration.",
554
- "Network": "Utilities for network communication, file transfer, and connectivity diagnostics.",
555
- "Package Management": "Tools for installing, updating, and managing software packages on the system.",
556
- "Version Control": "Git and other version control commands for tracking changes and collaborating on code.",
557
- "Process Management": "Commands for viewing, controlling, and managing running processes.",
558
- "User Management": "Tools for managing user accounts, permissions, and access control.",
559
- "Disk Management": "Utilities for managing disk space, partitions, and storage devices.",
560
- "Shell Scripting": "Built-in shell commands and constructs for scripting and automation.",
561
- "Development": "Programming tools, compilers, interpreters, and development utilities.",
562
- "Compression": "Tools for compressing, archiving, and extracting files.",
563
- "Search": "Commands for finding files, searching content, and locating resources.",
564
- "Permissions": "Tools for managing file permissions, ownership, and access control.",
562
+ "File System": "Commands for navigating, viewing, creating, and managing files and directories in the filesystem.",
563
+ "Text Processing": "Tools for viewing, searching, filtering, and transforming text content in files and streams.",
564
+ "Git": "Version control system commands for tracking changes, managing branches, and collaborating on code.",
565
+ "Package Management": "Package managers for installing, updating, and managing software dependencies across languages and platforms.",
566
+ "Process & System": "Commands for monitoring, managing, and controlling running processes and system resources.",
567
+ "Networking": "Commands for network operations, file transfers, remote access, and connectivity diagnostics.",
568
+ "Permissions": "Commands for managing file ownership, access permissions, and user/group administration.",
569
+ "Compression": "Commands for compressing, archiving, and extracting files using various algorithms.",
570
+ "Search & Navigation": "Commands for finding files, searching content, and navigating the filesystem efficiently.",
571
+ "Development": "Development tools for building, testing, compiling, and running code across languages.",
572
+ "Shell Builtins": "Built-in shell commands for scripting, variable management, and interactive shell use.",
565
573
  }
566
574
  return concepts.get(category, f"Commands related to {category.lower()} operations and utilities.")
567
575
 
@@ -569,20 +577,17 @@ def _get_category_concept(category: str) -> str:
569
577
  def _get_category_icon(category: str) -> str:
570
578
  """Get icon for a category."""
571
579
  icons = {
572
- "File Management": "&#128193;",
580
+ "File System": "&#128193;",
573
581
  "Text Processing": "&#128196;",
574
- "System Administration": "&#9881;",
575
- "Network": "&#127760;",
582
+ "Git": "&#128202;",
576
583
  "Package Management": "&#128230;",
577
- "Version Control": "&#128202;",
578
- "Process Management": "&#9654;",
579
- "User Management": "&#128100;",
580
- "Disk Management": "&#128191;",
581
- "Shell Scripting": "&#10095;",
582
- "Development": "&#128187;",
583
- "Compression": "&#128230;",
584
- "Search": "&#128269;",
584
+ "Process & System": "&#9881;",
585
+ "Networking": "&#127760;",
585
586
  "Permissions": "&#128274;",
587
+ "Compression": "&#128230;",
588
+ "Search & Navigation": "&#128269;",
589
+ "Development": "&#128187;",
590
+ "Shell Builtins": "&#10095;",
586
591
  }
587
592
  return icons.get(category, "&#128204;")
588
593
 
@@ -990,6 +995,77 @@ def get_inline_css() -> str:
990
995
  color: var(--text-secondary);
991
996
  }
992
997
 
998
+ /* Operators List */
999
+ .operators-list {
1000
+ display: flex;
1001
+ flex-direction: column;
1002
+ gap: 12px;
1003
+ }
1004
+
1005
+ .operator-item {
1006
+ display: grid;
1007
+ grid-template-columns: 80px 1fr 120px 50px;
1008
+ align-items: center;
1009
+ gap: 12px;
1010
+ padding: 8px 0;
1011
+ border-bottom: 1px solid var(--border-color);
1012
+ }
1013
+
1014
+ .operator-item:last-child {
1015
+ border-bottom: none;
1016
+ }
1017
+
1018
+ .operator-symbol {
1019
+ font-family: var(--font-mono);
1020
+ font-size: 1rem;
1021
+ font-weight: 600;
1022
+ color: var(--accent-primary);
1023
+ }
1024
+
1025
+ .operator-symbol code {
1026
+ background: var(--bg-tertiary);
1027
+ padding: 4px 8px;
1028
+ border-radius: var(--radius-sm);
1029
+ }
1030
+
1031
+ .operator-info {
1032
+ display: flex;
1033
+ flex-direction: column;
1034
+ gap: 2px;
1035
+ }
1036
+
1037
+ .operator-name {
1038
+ font-size: 0.9rem;
1039
+ font-weight: 600;
1040
+ color: var(--text-primary);
1041
+ }
1042
+
1043
+ .operator-desc {
1044
+ font-size: 0.8rem;
1045
+ color: var(--text-secondary);
1046
+ }
1047
+
1048
+ .operator-bar-container {
1049
+ height: 20px;
1050
+ background: var(--bg-tertiary);
1051
+ border-radius: var(--radius-sm);
1052
+ overflow: hidden;
1053
+ }
1054
+
1055
+ .operator-bar {
1056
+ height: 100%;
1057
+ background: var(--accent-primary);
1058
+ border-radius: var(--radius-sm);
1059
+ transition: width 0.5s ease;
1060
+ }
1061
+
1062
+ .operator-count {
1063
+ font-size: 0.9rem;
1064
+ font-weight: 600;
1065
+ text-align: right;
1066
+ color: var(--text-secondary);
1067
+ }
1068
+
993
1069
  /* Pie Chart */
994
1070
  .pie-container {
995
1071
  display: flex;
@@ -2081,6 +2157,7 @@ def generate_html_files(
2081
2157
  'complexity_avg': stats.get('average_complexity', 2),
2082
2158
  'complexity_distribution': complexity_distribution,
2083
2159
  'top_commands': top_10_commands, # Pre-computed top commands with frequencies
2160
+ 'operators_used': analysis.get('operators_used', {}), # Bash operators like ||, &&, |, 2>&1
2084
2161
  },
2085
2162
  'commands': formatted_commands,
2086
2163
  'categories': {cat: [c.get('command', '') for c in cmds] for cat, cmds in categories.items()},