learn_bash_from_session_data 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # learn_bash_from_session_data
2
+
3
+ Learn bash from your Claude Code sessions. Extracts commands you've used, categorizes them, and generates an interactive HTML learning resource with quizzes.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g learn_bash_from_session_data
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ # Analyze all sessions from current project
15
+ learn-bash
16
+
17
+ # Analyze last 5 sessions
18
+ learn-bash -n 5
19
+
20
+ # List available projects
21
+ learn-bash --list
22
+
23
+ # Process specific session file
24
+ learn-bash --file ~/.claude/projects/.../session.jsonl
25
+
26
+ # Custom output path
27
+ learn-bash --output ./my-lessons.html
28
+ ```
29
+
30
+ ## Features
31
+
32
+ - **Command Extraction**: Parses Claude Code session JSONL files
33
+ - **Categorization**: Groups commands by category (Git, File System, Text Processing, etc.)
34
+ - **Complexity Scoring**: Rates commands from 1-5 based on complexity
35
+ - **Interactive Quizzes**: Test your knowledge with auto-generated quizzes
36
+ - **Self-Contained HTML**: No external dependencies, works offline
37
+
38
+ ## Requirements
39
+
40
+ - Node.js >= 14.0.0
41
+ - Python >= 3.8
42
+
43
+ ## License
44
+
45
+ MIT
@@ -0,0 +1,328 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * learn-bash CLI
5
+ * Learn bash from your Claude Code sessions - extracts commands and generates interactive HTML lessons
6
+ *
7
+ * Usage:
8
+ * learn-bash [options]
9
+ * learn-bash --list List available Claude Code projects
10
+ * learn-bash --sessions 5 Process last 5 sessions from current project
11
+ * learn-bash --file path/to/session Process a specific session file
12
+ * learn-bash --project "project-name" Process sessions from a specific project
13
+ * learn-bash --output ./my-lesson.html Specify output file location
14
+ * learn-bash --no-open Don't auto-open the generated HTML
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const { spawn, execSync } = require('child_process');
20
+ const os = require('os');
21
+
22
+ // ANSI color codes for terminal output
23
+ const colors = {
24
+ reset: '\x1b[0m',
25
+ bright: '\x1b[1m',
26
+ red: '\x1b[31m',
27
+ green: '\x1b[32m',
28
+ yellow: '\x1b[33m',
29
+ blue: '\x1b[34m',
30
+ cyan: '\x1b[36m'
31
+ };
32
+
33
+ /**
34
+ * Parse command line arguments (minimist-style, no dependencies)
35
+ */
36
+ function parseArgs(args) {
37
+ const result = {
38
+ _: [],
39
+ sessions: null,
40
+ file: null,
41
+ output: null,
42
+ list: false,
43
+ 'no-open': false,
44
+ project: null,
45
+ help: false
46
+ };
47
+
48
+ for (let i = 0; i < args.length; i++) {
49
+ const arg = args[i];
50
+ const nextArg = args[i + 1];
51
+
52
+ if (arg === '--help' || arg === '-h') {
53
+ result.help = true;
54
+ } else if (arg === '--list' || arg === '-l') {
55
+ result.list = true;
56
+ } else if (arg === '--no-open') {
57
+ result['no-open'] = true;
58
+ } else if (arg === '--sessions' || arg === '-n') {
59
+ result.sessions = parseInt(nextArg, 10);
60
+ i++;
61
+ } else if (arg === '--file' || arg === '-f') {
62
+ result.file = nextArg;
63
+ i++;
64
+ } else if (arg === '--output' || arg === '-o') {
65
+ result.output = nextArg;
66
+ i++;
67
+ } else if (arg === '--project' || arg === '-p') {
68
+ result.project = nextArg;
69
+ i++;
70
+ } else if (!arg.startsWith('-')) {
71
+ result._.push(arg);
72
+ }
73
+ }
74
+
75
+ return result;
76
+ }
77
+
78
+ /**
79
+ * Print usage information
80
+ */
81
+ function printHelp() {
82
+ console.log(`
83
+ ${colors.bright}${colors.cyan}learn-bash${colors.reset} - Learn bash from your Claude Code sessions
84
+
85
+ ${colors.bright}USAGE:${colors.reset}
86
+ learn-bash [options]
87
+
88
+ ${colors.bright}OPTIONS:${colors.reset}
89
+ -n, --sessions <count> Number of recent sessions to process (default: all)
90
+ -f, --file <path> Process a specific session file
91
+ -o, --output <path> Output HTML file path (default: ./bash-lesson.html)
92
+ -p, --project <name> Process sessions from a specific project
93
+ -l, --list List available Claude Code projects
94
+ --no-open Don't auto-open the generated HTML in browser
95
+ -h, --help Show this help message
96
+
97
+ ${colors.bright}EXAMPLES:${colors.reset}
98
+ ${colors.green}learn-bash${colors.reset} Process all sessions from current project
99
+ ${colors.green}learn-bash -n 5${colors.reset} Process last 5 sessions
100
+ ${colors.green}learn-bash --list${colors.reset} List available projects
101
+ ${colors.green}learn-bash -p "my-project"${colors.reset} Process sessions from specific project
102
+ ${colors.green}learn-bash -f ~/.claude/sessions/abc.json${colors.reset}
103
+ ${colors.green}learn-bash -o ./lesson.html --no-open${colors.reset}
104
+
105
+ ${colors.bright}SESSION LOCATION:${colors.reset}
106
+ Claude Code sessions are stored at: ${colors.yellow}~/.claude/projects/${colors.reset}
107
+ `);
108
+ }
109
+
110
+ /**
111
+ * Get the Claude projects directory path
112
+ */
113
+ function getClaudeProjectsDir() {
114
+ const homeDir = os.homedir();
115
+ return path.join(homeDir, '.claude', 'projects');
116
+ }
117
+
118
+ /**
119
+ * List available Claude Code projects
120
+ */
121
+ function listProjects() {
122
+ const projectsDir = getClaudeProjectsDir();
123
+
124
+ if (!fs.existsSync(projectsDir)) {
125
+ console.log(`${colors.yellow}No Claude Code projects directory found at:${colors.reset}`);
126
+ console.log(` ${projectsDir}`);
127
+ console.log(`\n${colors.cyan}Make sure you have used Claude Code at least once.${colors.reset}`);
128
+ return [];
129
+ }
130
+
131
+ const entries = fs.readdirSync(projectsDir, { withFileTypes: true });
132
+ const projects = entries
133
+ .filter(entry => entry.isDirectory())
134
+ .map(entry => {
135
+ const projectPath = path.join(projectsDir, entry.name);
136
+ const sessionsPath = path.join(projectPath, 'sessions');
137
+ let sessionCount = 0;
138
+
139
+ if (fs.existsSync(sessionsPath)) {
140
+ const sessions = fs.readdirSync(sessionsPath)
141
+ .filter(f => f.endsWith('.json') || f.endsWith('.jsonl'));
142
+ sessionCount = sessions.length;
143
+ }
144
+
145
+ return {
146
+ name: entry.name,
147
+ path: projectPath,
148
+ sessionCount
149
+ };
150
+ })
151
+ .filter(p => p.sessionCount > 0);
152
+
153
+ if (projects.length === 0) {
154
+ console.log(`${colors.yellow}No projects with sessions found.${colors.reset}`);
155
+ return [];
156
+ }
157
+
158
+ console.log(`\n${colors.bright}${colors.cyan}Available Claude Code Projects:${colors.reset}\n`);
159
+ projects.forEach((project, index) => {
160
+ console.log(` ${colors.green}${index + 1}.${colors.reset} ${project.name}`);
161
+ console.log(` ${colors.yellow}Sessions:${colors.reset} ${project.sessionCount}`);
162
+ console.log(` ${colors.yellow}Path:${colors.reset} ${project.path}\n`);
163
+ });
164
+
165
+ return projects;
166
+ }
167
+
168
+ /**
169
+ * Check if Python 3 is available
170
+ */
171
+ function checkPython() {
172
+ const pythonCommands = ['python3', 'python'];
173
+
174
+ for (const cmd of pythonCommands) {
175
+ try {
176
+ const version = execSync(`${cmd} --version 2>&1`, { encoding: 'utf8' });
177
+ if (version.includes('Python 3')) {
178
+ return cmd;
179
+ }
180
+ } catch (e) {
181
+ // Command not found, try next
182
+ }
183
+ }
184
+
185
+ return null;
186
+ }
187
+
188
+ /**
189
+ * Open a file in the default browser
190
+ */
191
+ function openInBrowser(filePath) {
192
+ const platform = os.platform();
193
+ let cmd;
194
+ let args;
195
+
196
+ switch (platform) {
197
+ case 'darwin':
198
+ cmd = 'open';
199
+ args = [filePath];
200
+ break;
201
+ case 'win32':
202
+ cmd = 'cmd';
203
+ args = ['/c', 'start', '', filePath];
204
+ break;
205
+ default:
206
+ // Linux and others
207
+ cmd = 'xdg-open';
208
+ args = [filePath];
209
+ }
210
+
211
+ try {
212
+ spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref();
213
+ return true;
214
+ } catch (e) {
215
+ return false;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Find the Python main.py script
221
+ */
222
+ function findPythonScript() {
223
+ // Check relative to this script's location
224
+ const scriptDir = path.dirname(__filename);
225
+ const possiblePaths = [
226
+ path.join(scriptDir, '..', 'scripts', 'main.py'),
227
+ path.join(scriptDir, 'scripts', 'main.py'),
228
+ path.join(process.cwd(), 'scripts', 'main.py')
229
+ ];
230
+
231
+ for (const p of possiblePaths) {
232
+ if (fs.existsSync(p)) {
233
+ return path.resolve(p);
234
+ }
235
+ }
236
+
237
+ return null;
238
+ }
239
+
240
+ /**
241
+ * Main entry point
242
+ */
243
+ function main() {
244
+ const args = parseArgs(process.argv.slice(2));
245
+
246
+ // Show help
247
+ if (args.help) {
248
+ printHelp();
249
+ process.exit(0);
250
+ }
251
+
252
+ // List projects
253
+ if (args.list) {
254
+ listProjects();
255
+ process.exit(0);
256
+ }
257
+
258
+ // Check Python availability
259
+ const pythonCmd = checkPython();
260
+ if (!pythonCmd) {
261
+ console.error(`${colors.red}${colors.bright}Error:${colors.reset} Python 3 is required but not found.`);
262
+ console.error(`Please install Python 3 from: ${colors.cyan}https://www.python.org/downloads/${colors.reset}`);
263
+ process.exit(1);
264
+ }
265
+
266
+ // Find the Python script
267
+ const pythonScript = findPythonScript();
268
+ if (!pythonScript) {
269
+ console.error(`${colors.red}${colors.bright}Error:${colors.reset} Could not find scripts/main.py`);
270
+ console.error(`Make sure you're running from the package directory or it's properly installed.`);
271
+ process.exit(1);
272
+ }
273
+
274
+ // Build Python script arguments
275
+ const pythonArgs = [pythonScript];
276
+
277
+ if (args.sessions) {
278
+ pythonArgs.push('--sessions', args.sessions.toString());
279
+ }
280
+
281
+ if (args.file) {
282
+ pythonArgs.push('--file', args.file);
283
+ }
284
+
285
+ if (args.output) {
286
+ pythonArgs.push('--output', args.output);
287
+ }
288
+
289
+ if (args.project) {
290
+ pythonArgs.push('--project', args.project);
291
+ }
292
+
293
+ // Default output path if not specified
294
+ const outputPath = args.output || path.join(process.cwd(), 'bash-lesson.html');
295
+
296
+ console.log(`\n${colors.bright}${colors.cyan}learn-bash${colors.reset} - Extracting bash commands from Claude Code sessions...\n`);
297
+
298
+ // Spawn Python process
299
+ const pythonProcess = spawn(pythonCmd, pythonArgs, {
300
+ stdio: 'inherit',
301
+ cwd: process.cwd()
302
+ });
303
+
304
+ pythonProcess.on('close', (code) => {
305
+ if (code === 0) {
306
+ console.log(`\n${colors.green}${colors.bright}Success!${colors.reset} Generated: ${colors.cyan}${outputPath}${colors.reset}`);
307
+
308
+ // Open in browser unless --no-open specified
309
+ if (!args['no-open'] && fs.existsSync(outputPath)) {
310
+ console.log(`${colors.yellow}Opening in browser...${colors.reset}`);
311
+ if (!openInBrowser(outputPath)) {
312
+ console.log(`${colors.yellow}Could not auto-open. Please open manually:${colors.reset} ${outputPath}`);
313
+ }
314
+ }
315
+ } else {
316
+ console.error(`\n${colors.red}${colors.bright}Error:${colors.reset} Python script exited with code ${code}`);
317
+ process.exit(code);
318
+ }
319
+ });
320
+
321
+ pythonProcess.on('error', (err) => {
322
+ console.error(`${colors.red}${colors.bright}Error:${colors.reset} Failed to start Python process:`, err.message);
323
+ process.exit(1);
324
+ });
325
+ }
326
+
327
+ // Run main
328
+ main();
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "learn_bash_from_session_data",
3
+ "version": "1.0.0",
4
+ "description": "Learn bash from your Claude Code sessions - extracts commands and generates interactive HTML lessons",
5
+ "main": "bin/learn-bash.js",
6
+ "bin": {
7
+ "learn-bash": "./bin/learn-bash.js",
8
+ "bash-learner": "./bin/learn-bash.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node tests/test-runner.js"
12
+ },
13
+ "keywords": ["bash", "learning", "claude", "cli", "terminal", "shell"],
14
+ "author": "",
15
+ "license": "MIT",
16
+ "engines": {
17
+ "node": ">=14.0.0"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/bjpl/learn_bash_from_session_data"
22
+ }
23
+ }
@@ -0,0 +1,34 @@
1
+ """
2
+ Scripts package for learn_bash_from_session_data.
3
+
4
+ Provides JSONL extraction and bash command parsing utilities.
5
+ """
6
+
7
+ from .extractor import (
8
+ ExtractedCommand,
9
+ JSONLExtractor,
10
+ extract_commands_from_jsonl,
11
+ extract_commands_from_sessions,
12
+ )
13
+
14
+ from .parser import (
15
+ ParsedCommand,
16
+ CommandCategory,
17
+ BashParser,
18
+ parse_command,
19
+ parse_commands,
20
+ )
21
+
22
+ __all__ = [
23
+ # Extractor
24
+ "ExtractedCommand",
25
+ "JSONLExtractor",
26
+ "extract_commands_from_jsonl",
27
+ "extract_commands_from_sessions",
28
+ # Parser
29
+ "ParsedCommand",
30
+ "CommandCategory",
31
+ "BashParser",
32
+ "parse_command",
33
+ "parse_commands",
34
+ ]