learn_bash_from_session_data 1.0.6 → 1.0.8
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/README.md +141 -18
- package/bin/learn-bash.js +17 -14
- package/package.json +1 -1
- package/scripts/html_generator.py +183 -14
- package/scripts/quiz_generator.py +114 -40
- package/bash-learner-output/run-2026-02-05-154214/index.html +0 -3848
- package/bash-learner-output/run-2026-02-05-154214/summary.json +0 -148
- package/bash-learner-output/run-2026-02-05-155427/index.html +0 -3900
- package/bash-learner-output/run-2026-02-05-155427/summary.json +0 -157
- package/bash-learner-output/run-2026-02-05-155949/index.html +0 -4514
- package/bash-learner-output/run-2026-02-05-155949/summary.json +0 -163
- package/vectors.db +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# learn_bash_from_session_data
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Turn your Claude Code sessions into personalized bash lessons. This tool extracts every command you've run, enriches them with descriptions and flag breakdowns from a 402-command knowledge base, generates interactive quizzes, and produces a self-contained HTML learning resource.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,37 +8,160 @@ Learn bash from your Claude Code sessions. Extracts commands you've used, catego
|
|
|
8
8
|
npm install -g learn_bash_from_session_data
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Requirements:** Node.js >= 14.0.0 and Python >= 3.8
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
#
|
|
16
|
+
# Generate a lesson from your current project's sessions
|
|
15
17
|
learn-bash
|
|
16
18
|
|
|
17
|
-
#
|
|
19
|
+
# Process the last 5 sessions only
|
|
18
20
|
learn-bash -n 5
|
|
19
21
|
|
|
20
|
-
# List available projects
|
|
22
|
+
# List all available Claude Code projects
|
|
21
23
|
learn-bash --list
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The generated HTML opens automatically in your browser.
|
|
27
|
+
|
|
28
|
+
## CLI Reference
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
learn-bash [options]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Flag | Short | Description |
|
|
35
|
+
|------|-------|-------------|
|
|
36
|
+
| `--sessions <count>` | `-n` | Number of recent sessions to process (default: all) |
|
|
37
|
+
| `--file <path>` | `-f` | Process a specific session JSONL file |
|
|
38
|
+
| `--output <path>` | `-o` | Output directory path (default: `./bash-learner-output/`) |
|
|
39
|
+
| `--project <name>` | `-p` | Process sessions from a specific project by name |
|
|
40
|
+
| `--list` | `-l` | List available Claude Code projects with session counts |
|
|
41
|
+
| `--no-open` | | Don't auto-open the generated HTML in browser |
|
|
42
|
+
| `--help` | `-h` | Show help message |
|
|
43
|
+
|
|
44
|
+
### Examples
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Process a specific session file
|
|
48
|
+
learn-bash --file ~/.claude/projects/my-project/abc123.jsonl
|
|
49
|
+
|
|
50
|
+
# Output to a custom location without opening browser
|
|
51
|
+
learn-bash -o ./my-lessons --no-open
|
|
22
52
|
|
|
23
|
-
# Process
|
|
24
|
-
learn-bash --
|
|
53
|
+
# Process sessions from a named project
|
|
54
|
+
learn-bash --project "C--Users-me-my-app"
|
|
25
55
|
|
|
26
|
-
#
|
|
27
|
-
learn-bash
|
|
56
|
+
# Process last 3 sessions, custom output
|
|
57
|
+
learn-bash -n 3 -o ./review
|
|
28
58
|
```
|
|
29
59
|
|
|
30
|
-
##
|
|
60
|
+
## What You Get
|
|
61
|
+
|
|
62
|
+
The tool generates a single interactive HTML file with four sections:
|
|
63
|
+
|
|
64
|
+
### Commands Tab
|
|
65
|
+
Every bash command you used, organized by category with:
|
|
66
|
+
- Syntax-highlighted full command display
|
|
67
|
+
- Flag breakdowns with descriptions (e.g., `-l` = "Long format listing with permissions, size, dates")
|
|
68
|
+
- Subcommand explanations (e.g., `git add` = "Stage file contents for commit")
|
|
69
|
+
- Common usage patterns from the knowledge base
|
|
70
|
+
- Search, sort (by frequency, complexity, category, name), and category filtering
|
|
71
|
+
|
|
72
|
+
### Lessons Tab
|
|
73
|
+
Step-by-step walkthrough of commands grouped by category, with flag details and complexity indicators. Designed for sequential learning.
|
|
74
|
+
|
|
75
|
+
### Quiz Tab
|
|
76
|
+
20 auto-generated questions in four types:
|
|
77
|
+
|
|
78
|
+
| Type | Weight | What It Tests |
|
|
79
|
+
|------|--------|---------------|
|
|
80
|
+
| What does this do? | 40% | Identify a command's purpose from its syntax |
|
|
81
|
+
| Which flag? | 25% | Match a flag to its behavior |
|
|
82
|
+
| Build the command | 20% | Construct the correct command for a task |
|
|
83
|
+
| Spot the difference | 15% | Compare two similar commands |
|
|
84
|
+
|
|
85
|
+
Quizzes are **session-adaptive** (based on commands you actually used), **randomized** (different questions and answer order each run), and use plausible distractors drawn from 402 real commands.
|
|
86
|
+
|
|
87
|
+
### Summary Tab
|
|
88
|
+
Statistics on your session: total commands, category distribution, complexity breakdown, most-used commands.
|
|
89
|
+
|
|
90
|
+
## Knowledge Base
|
|
31
91
|
|
|
32
|
-
-
|
|
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
|
|
92
|
+
The built-in knowledge base powers descriptions, flag lookups, and quiz generation:
|
|
37
93
|
|
|
38
|
-
|
|
94
|
+
| Metric | Count |
|
|
95
|
+
|--------|-------|
|
|
96
|
+
| Commands documented | 402 |
|
|
97
|
+
| Flag definitions | 1,961 |
|
|
98
|
+
| Common usage patterns | 1,357 |
|
|
99
|
+
| Categories | 11 |
|
|
100
|
+
| Bash operators | 16 |
|
|
101
|
+
| Bash concepts | 6 |
|
|
39
102
|
|
|
40
|
-
|
|
41
|
-
|
|
103
|
+
### Categories
|
|
104
|
+
|
|
105
|
+
File System, Text Processing, Git, Package Management, Process & System, Networking, Permissions, Compression, Search & Navigation, Development, Shell Builtins
|
|
106
|
+
|
|
107
|
+
## How It Works
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
Claude Code session (.jsonl)
|
|
111
|
+
|
|
|
112
|
+
v
|
|
113
|
+
[Parser] --> Extract bash tool_use blocks
|
|
114
|
+
|
|
|
115
|
+
v
|
|
116
|
+
[Extractor] --> Split compound commands (pipes, &&, ;)
|
|
117
|
+
|
|
|
118
|
+
v
|
|
119
|
+
[Analyzer] --> Categorize, score complexity (1-5), count frequency
|
|
120
|
+
|
|
|
121
|
+
v
|
|
122
|
+
[Knowledge Base] --> Enrich with 402 commands, 1961 flags, 1357 patterns
|
|
123
|
+
|
|
|
124
|
+
v
|
|
125
|
+
[Quiz Generator] --> 20 randomized, session-adaptive questions
|
|
126
|
+
|
|
|
127
|
+
v
|
|
128
|
+
[HTML Generator] --> Self-contained interactive HTML (no dependencies)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Session File Location
|
|
132
|
+
|
|
133
|
+
Claude Code stores sessions at:
|
|
134
|
+
|
|
135
|
+
| Platform | Path |
|
|
136
|
+
|----------|------|
|
|
137
|
+
| macOS/Linux | `~/.claude/projects/` |
|
|
138
|
+
| Windows | `%USERPROFILE%\.claude\projects\` |
|
|
139
|
+
| WSL | Auto-detected from `/mnt/c/Users/<name>/.claude/projects/` |
|
|
140
|
+
|
|
141
|
+
Each project directory contains `.jsonl` session files that this tool reads.
|
|
142
|
+
|
|
143
|
+
## Programmatic Usage
|
|
144
|
+
|
|
145
|
+
You can also run the Python pipeline directly:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
python scripts/main.py --sessions 5 --output ./output
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or import modules in Python:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from scripts.knowledge_base import COMMAND_DB, get_flags_for_command, get_command_info
|
|
155
|
+
from scripts.quiz_generator import generate_quiz_set
|
|
156
|
+
from scripts.analyzer import analyze_commands
|
|
157
|
+
|
|
158
|
+
# Look up a command
|
|
159
|
+
info = get_command_info("grep")
|
|
160
|
+
flags = get_flags_for_command("grep")
|
|
161
|
+
|
|
162
|
+
# Generate quizzes from analyzed commands
|
|
163
|
+
quizzes = generate_quiz_set(analyzed_commands, count=10)
|
|
164
|
+
```
|
|
42
165
|
|
|
43
166
|
## License
|
|
44
167
|
|
package/bin/learn-bash.js
CHANGED
|
@@ -240,26 +240,29 @@ function checkPython() {
|
|
|
240
240
|
*/
|
|
241
241
|
function openInBrowser(filePath) {
|
|
242
242
|
const platform = os.platform();
|
|
243
|
+
const isWindows = platform === 'win32' ||
|
|
244
|
+
process.env.MSYSTEM != null ||
|
|
245
|
+
(process.env.OSTYPE && process.env.OSTYPE.includes('msys')) ||
|
|
246
|
+
isWSL();
|
|
243
247
|
let cmd;
|
|
244
248
|
let args;
|
|
245
249
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// Linux and others
|
|
257
|
-
cmd = 'xdg-open';
|
|
258
|
-
args = [filePath];
|
|
250
|
+
if (platform === 'darwin') {
|
|
251
|
+
cmd = 'open';
|
|
252
|
+
args = [filePath];
|
|
253
|
+
} else if (isWindows) {
|
|
254
|
+
// Works from native Windows, MSYS, Git Bash, and WSL
|
|
255
|
+
cmd = process.env.COMSPEC || 'cmd.exe';
|
|
256
|
+
args = ['/c', 'start', '', filePath.replace(/\//g, '\\')];
|
|
257
|
+
} else {
|
|
258
|
+
cmd = 'xdg-open';
|
|
259
|
+
args = [filePath];
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
try {
|
|
262
|
-
spawn(cmd, args, { detached: true, stdio: 'ignore' })
|
|
263
|
+
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
264
|
+
child.on('error', () => {}); // Suppress async spawn errors
|
|
265
|
+
child.unref();
|
|
263
266
|
return true;
|
|
264
267
|
} catch (e) {
|
|
265
268
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "learn_bash_from_session_data",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
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": {
|
|
@@ -12,6 +12,16 @@ from pathlib import Path
|
|
|
12
12
|
import html
|
|
13
13
|
import json
|
|
14
14
|
|
|
15
|
+
try:
|
|
16
|
+
from scripts.knowledge_base import COMMAND_DB, get_flags_for_command, get_command_info
|
|
17
|
+
except ImportError:
|
|
18
|
+
try:
|
|
19
|
+
from knowledge_base import COMMAND_DB, get_flags_for_command, get_command_info
|
|
20
|
+
except ImportError:
|
|
21
|
+
COMMAND_DB = {}
|
|
22
|
+
def get_flags_for_command(cmd): return {}
|
|
23
|
+
def get_command_info(cmd): return None
|
|
24
|
+
|
|
15
25
|
|
|
16
26
|
def _generate_html_impl(analysis_result: dict[str, Any], quizzes: list[dict[str, Any]]) -> str:
|
|
17
27
|
"""
|
|
@@ -376,7 +386,7 @@ def render_commands_tab(commands: list[dict]) -> str:
|
|
|
376
386
|
# Syntax highlighted command
|
|
377
387
|
highlighted = _syntax_highlight(cmd.get("full_command", ""))
|
|
378
388
|
|
|
379
|
-
# Flags breakdown
|
|
389
|
+
# Flags breakdown with descriptions
|
|
380
390
|
flags = cmd.get("flags", [])
|
|
381
391
|
flags_html = ""
|
|
382
392
|
if flags:
|
|
@@ -384,9 +394,27 @@ def render_commands_tab(commands: list[dict]) -> str:
|
|
|
384
394
|
for flag in flags:
|
|
385
395
|
flag_name = html.escape(flag.get("flag", ""))
|
|
386
396
|
flag_desc = html.escape(flag.get("description", ""))
|
|
387
|
-
|
|
397
|
+
if flag_desc:
|
|
398
|
+
flags_html += f'<li><code class="flag">{flag_name}</code> <span class="flag-desc">{flag_desc}</span></li>'
|
|
399
|
+
else:
|
|
400
|
+
flags_html += f'<li><code class="flag">{flag_name}</code></li>'
|
|
388
401
|
flags_html += '</ul></div>'
|
|
389
402
|
|
|
403
|
+
# Subcommand description
|
|
404
|
+
subcommand_desc = cmd.get("subcommand_desc", "")
|
|
405
|
+
subcmd_html = ""
|
|
406
|
+
if subcommand_desc:
|
|
407
|
+
subcmd_html = f'<div class="subcmd-section"><span class="subcmd-label">Subcommand:</span> {html.escape(subcommand_desc)}</div>'
|
|
408
|
+
|
|
409
|
+
# Common patterns / examples from knowledge base
|
|
410
|
+
common_patterns = cmd.get("common_patterns", [])
|
|
411
|
+
patterns_html = ""
|
|
412
|
+
if common_patterns:
|
|
413
|
+
patterns_html = '<div class="patterns-section"><h5>Common Patterns:</h5><ul class="patterns-list">'
|
|
414
|
+
for pattern in common_patterns[:5]:
|
|
415
|
+
patterns_html += f'<li><code>{html.escape(pattern)}</code></li>'
|
|
416
|
+
patterns_html += '</ul></div>'
|
|
417
|
+
|
|
390
418
|
# Output preview
|
|
391
419
|
output_preview = cmd.get("output_preview", "")
|
|
392
420
|
output_html = ""
|
|
@@ -405,7 +433,7 @@ def render_commands_tab(commands: list[dict]) -> str:
|
|
|
405
433
|
<span class="category-badge">{category}</span>
|
|
406
434
|
</div>
|
|
407
435
|
<div class="command-meta">
|
|
408
|
-
<span class="
|
|
436
|
+
<span class="cmd-preview">{description[:60]}{'...' if len(description) > 60 else ''}</span>
|
|
409
437
|
<span class="expand-icon">▼</span>
|
|
410
438
|
</div>
|
|
411
439
|
</div>
|
|
@@ -418,7 +446,9 @@ def render_commands_tab(commands: list[dict]) -> str:
|
|
|
418
446
|
<h5>Description:</h5>
|
|
419
447
|
<p>{description}</p>
|
|
420
448
|
</div>
|
|
449
|
+
{subcmd_html}
|
|
421
450
|
{flags_html}
|
|
451
|
+
{patterns_html}
|
|
422
452
|
{output_html}
|
|
423
453
|
</div>
|
|
424
454
|
</div>'''
|
|
@@ -512,13 +542,37 @@ def render_lessons_tab(categories: dict, commands: list[dict]) -> str:
|
|
|
512
542
|
complexity = cmd.get("complexity", "simple")
|
|
513
543
|
highlighted = _syntax_highlight(cmd.get("full_command", ""))
|
|
514
544
|
|
|
545
|
+
# Get flags and patterns for this command from COMMAND_DB
|
|
546
|
+
cmd_flags = cmd.get("flags", [])
|
|
547
|
+
lesson_flags_html = ""
|
|
548
|
+
if cmd_flags:
|
|
549
|
+
lesson_flags_html = '<div class="lesson-flags"><strong>Flags used:</strong> '
|
|
550
|
+
flag_parts = []
|
|
551
|
+
for flag in cmd_flags:
|
|
552
|
+
fname = html.escape(flag.get("flag", "") if isinstance(flag, dict) else str(flag))
|
|
553
|
+
fdesc = html.escape(flag.get("description", "") if isinstance(flag, dict) else "")
|
|
554
|
+
if fdesc:
|
|
555
|
+
flag_parts.append(f'<code class="flag">{fname}</code> ({fdesc})')
|
|
556
|
+
else:
|
|
557
|
+
flag_parts.append(f'<code class="flag">{fname}</code>')
|
|
558
|
+
lesson_flags_html += ', '.join(flag_parts) + '</div>'
|
|
559
|
+
|
|
560
|
+
# Subcommand info
|
|
561
|
+
subcmd_desc = cmd.get("subcommand_desc", "")
|
|
562
|
+
lesson_subcmd = ""
|
|
563
|
+
if subcmd_desc:
|
|
564
|
+
lesson_subcmd = f'<div class="lesson-subcmd"><em>{html.escape(subcmd_desc)}</em></div>'
|
|
565
|
+
|
|
515
566
|
cat_commands_html += f'''
|
|
516
567
|
<div class="lesson-command">
|
|
517
568
|
<div class="lesson-command-header">
|
|
518
569
|
<code class="cmd">{base_cmd}</code>
|
|
570
|
+
<span class="lesson-complexity complexity-{complexity}">{complexity}</span>
|
|
519
571
|
</div>
|
|
520
572
|
<pre class="syntax-highlighted">{highlighted}</pre>
|
|
521
573
|
<p class="lesson-description">{description}</p>
|
|
574
|
+
{lesson_subcmd}
|
|
575
|
+
{lesson_flags_html}
|
|
522
576
|
</div>'''
|
|
523
577
|
|
|
524
578
|
# Patterns observed
|
|
@@ -1304,9 +1358,13 @@ def get_inline_css() -> str:
|
|
|
1304
1358
|
gap: 16px;
|
|
1305
1359
|
}
|
|
1306
1360
|
|
|
1307
|
-
.
|
|
1308
|
-
font-size: 0.
|
|
1361
|
+
.cmd-preview {
|
|
1362
|
+
font-size: 0.8rem;
|
|
1309
1363
|
color: var(--text-secondary);
|
|
1364
|
+
max-width: 400px;
|
|
1365
|
+
overflow: hidden;
|
|
1366
|
+
text-overflow: ellipsis;
|
|
1367
|
+
white-space: nowrap;
|
|
1310
1368
|
}
|
|
1311
1369
|
|
|
1312
1370
|
.expand-icon {
|
|
@@ -1384,6 +1442,22 @@ def get_inline_css() -> str:
|
|
|
1384
1442
|
color: #34a853;
|
|
1385
1443
|
}
|
|
1386
1444
|
|
|
1445
|
+
.flag-desc { color: #6c757d; margin-left: 4px; }
|
|
1446
|
+
.flags-list li { margin: 4px 0; line-height: 1.5; }
|
|
1447
|
+
.subcmd-section { background: #f0f7ff; padding: 8px 12px; border-radius: 6px; margin: 8px 0; border-left: 3px solid #4a9eff; }
|
|
1448
|
+
.subcmd-label { font-weight: 600; color: #4a9eff; }
|
|
1449
|
+
.patterns-section { margin: 8px 0; }
|
|
1450
|
+
.patterns-section h5 { margin: 4px 0; color: #666; font-size: 0.85em; }
|
|
1451
|
+
.patterns-list { list-style: none; padding: 0; margin: 4px 0; }
|
|
1452
|
+
.patterns-list li { padding: 3px 0; }
|
|
1453
|
+
.patterns-list code { background: #f5f5f5; padding: 2px 6px; border-radius: 3px; font-size: 0.85em; }
|
|
1454
|
+
.lesson-flags { margin: 6px 0; font-size: 0.9em; color: #555; }
|
|
1455
|
+
.lesson-subcmd { font-size: 0.9em; color: #4a9eff; margin: 4px 0; }
|
|
1456
|
+
.lesson-complexity { font-size: 0.75em; padding: 2px 8px; border-radius: 10px; margin-left: 8px; }
|
|
1457
|
+
.complexity-simple { background: #e8f5e9; color: #2e7d32; }
|
|
1458
|
+
.complexity-intermediate { background: #fff3e0; color: #e65100; }
|
|
1459
|
+
.complexity-advanced { background: #fce4ec; color: #c62828; }
|
|
1460
|
+
|
|
1387
1461
|
.syntax-highlighted .string {
|
|
1388
1462
|
color: #ff6d01;
|
|
1389
1463
|
}
|
|
@@ -2107,27 +2181,122 @@ def generate_html_files(
|
|
|
2107
2181
|
# Transform commands to expected format
|
|
2108
2182
|
formatted_commands = []
|
|
2109
2183
|
for cmd in analyzed_commands:
|
|
2110
|
-
|
|
2184
|
+
cmd_str = cmd.get('command', '')
|
|
2185
|
+
base_cmd = cmd.get('base_command', cmd_str.split()[0] if cmd_str else '')
|
|
2186
|
+
complexity_score = cmd.get('complexity', 1)
|
|
2187
|
+
|
|
2188
|
+
# Look up COMMAND_DB info for this command
|
|
2189
|
+
cmd_info = COMMAND_DB.get(base_cmd, {})
|
|
2190
|
+
kb_flags = get_flags_for_command(base_cmd)
|
|
2191
|
+
|
|
2192
|
+
# Convert flags to expected format WITH descriptions from knowledge base
|
|
2193
|
+
# Filter out non-flag tokens: bare dashes, numeric args (-5, -30), trailing colons
|
|
2194
|
+
import re
|
|
2111
2195
|
raw_flags = cmd.get('flags', [])
|
|
2112
2196
|
formatted_flags = []
|
|
2197
|
+
seen_flags = set()
|
|
2113
2198
|
for f in raw_flags:
|
|
2114
|
-
if isinstance(f, dict)
|
|
2115
|
-
|
|
2199
|
+
flag_name = f.get('flag', '') if isinstance(f, dict) else f
|
|
2200
|
+
# Skip bare dash, numeric-only flags (-5, -30), and artifact flags with colons
|
|
2201
|
+
if not flag_name or flag_name == '-' or flag_name.endswith(':'):
|
|
2202
|
+
continue
|
|
2203
|
+
if re.match(r'^-\d+$', flag_name):
|
|
2204
|
+
continue
|
|
2205
|
+
# Deduplicate flags within same command
|
|
2206
|
+
if flag_name in seen_flags:
|
|
2207
|
+
continue
|
|
2208
|
+
seen_flags.add(flag_name)
|
|
2209
|
+
|
|
2210
|
+
if isinstance(f, dict) and 'flag' in f:
|
|
2211
|
+
flag_desc = f.get('description', '')
|
|
2212
|
+
if not flag_desc and flag_name in kb_flags:
|
|
2213
|
+
flag_desc = kb_flags[flag_name]
|
|
2214
|
+
formatted_flags.append({'flag': flag_name, 'description': flag_desc})
|
|
2116
2215
|
elif isinstance(f, str):
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2216
|
+
flag_desc = kb_flags.get(f, '')
|
|
2217
|
+
# For combined flags like -la, decompose into individual flags
|
|
2218
|
+
if not flag_desc and len(f) > 2 and f.startswith('-') and not f.startswith('--'):
|
|
2219
|
+
char_descs = []
|
|
2220
|
+
for char in f[1:]:
|
|
2221
|
+
single = f'-{char}'
|
|
2222
|
+
if single in kb_flags:
|
|
2223
|
+
char_descs.append(f'{single}: {kb_flags[single]}')
|
|
2224
|
+
if char_descs:
|
|
2225
|
+
flag_desc = '; '.join(char_descs)
|
|
2226
|
+
# For find-style flags (-name, -type, -path, -maxdepth), add descriptions
|
|
2227
|
+
if not flag_desc:
|
|
2228
|
+
find_flags = {
|
|
2229
|
+
'-name': 'Match files by name pattern',
|
|
2230
|
+
'-type': 'Filter by file type (f=file, d=directory)',
|
|
2231
|
+
'-path': 'Match files by path pattern',
|
|
2232
|
+
'-maxdepth': 'Limit directory recursion depth',
|
|
2233
|
+
'-mindepth': 'Set minimum directory depth',
|
|
2234
|
+
'-exec': 'Execute command on each match',
|
|
2235
|
+
'-not': 'Negate the following expression',
|
|
2236
|
+
'-size': 'Match files by size',
|
|
2237
|
+
'-mtime': 'Match by modification time',
|
|
2238
|
+
'-perm': 'Match by file permissions',
|
|
2239
|
+
'-ls': 'List matched files in ls -l format',
|
|
2240
|
+
'-delete': 'Delete matched files',
|
|
2241
|
+
'-print': 'Print matched file paths',
|
|
2242
|
+
}
|
|
2243
|
+
flag_desc = find_flags.get(f, '')
|
|
2244
|
+
# For common CLI flags without KB entries
|
|
2245
|
+
if not flag_desc:
|
|
2246
|
+
common_flags = {
|
|
2247
|
+
'--help': 'Show help and usage information',
|
|
2248
|
+
'--version': 'Show version number',
|
|
2249
|
+
'--verbose': 'Enable verbose output',
|
|
2250
|
+
'--dry-run': 'Preview changes without executing',
|
|
2251
|
+
'--output': 'Specify output file or directory',
|
|
2252
|
+
'--open': 'Open result in default application',
|
|
2253
|
+
'--stat': 'Show diffstat summary of changes',
|
|
2254
|
+
'--sessions': 'Number of sessions to process',
|
|
2255
|
+
'--title': 'Set custom title',
|
|
2256
|
+
'--no-open': 'Skip auto-opening in browser',
|
|
2257
|
+
'--from': 'Specify input source path',
|
|
2258
|
+
'-s': 'Silent/short output mode',
|
|
2259
|
+
'-n': 'Numeric/count or line number',
|
|
2260
|
+
'-c': 'Execute command string or count',
|
|
2261
|
+
'-g': 'Global scope',
|
|
2262
|
+
'-p': 'Preserve attributes or port',
|
|
2263
|
+
'-o': 'Output file',
|
|
2264
|
+
'-P': 'No dereference (physical path)',
|
|
2265
|
+
}
|
|
2266
|
+
flag_desc = common_flags.get(f, '')
|
|
2267
|
+
formatted_flags.append({'flag': f, 'description': flag_desc})
|
|
2268
|
+
|
|
2269
|
+
# Get educational description from COMMAND_DB if session description is empty/generic
|
|
2270
|
+
session_desc = cmd.get('description', '')
|
|
2271
|
+
kb_desc = cmd_info.get('description', '')
|
|
2272
|
+
description = session_desc if session_desc else kb_desc
|
|
2273
|
+
|
|
2274
|
+
# Get subcommand info (for commands like git, docker, npm)
|
|
2275
|
+
subcommands = cmd_info.get('subcommands', {})
|
|
2276
|
+
# Try to identify the subcommand from the full command
|
|
2277
|
+
cmd_tokens = cmd_str.split() if cmd_str else []
|
|
2278
|
+
subcommand_desc = ''
|
|
2279
|
+
if subcommands and len(cmd_tokens) > 1:
|
|
2280
|
+
for token in cmd_tokens[1:]:
|
|
2281
|
+
if not token.startswith('-') and token in subcommands:
|
|
2282
|
+
subcommand_desc = subcommands[token]
|
|
2283
|
+
break
|
|
2284
|
+
|
|
2285
|
+
# Get common patterns from COMMAND_DB
|
|
2286
|
+
common_patterns = cmd_info.get('common_patterns', [])
|
|
2121
2287
|
|
|
2122
2288
|
formatted_commands.append({
|
|
2123
|
-
'base_command':
|
|
2289
|
+
'base_command': base_cmd,
|
|
2124
2290
|
'full_command': cmd_str,
|
|
2125
2291
|
'category': cmd.get('category', 'Other'),
|
|
2126
2292
|
'complexity': complexity_to_label(complexity_score),
|
|
2127
2293
|
'complexity_score': complexity_score,
|
|
2128
2294
|
'frequency': frequency_map.get(cmd_str, 1),
|
|
2129
|
-
'description':
|
|
2295
|
+
'description': description,
|
|
2130
2296
|
'flags': formatted_flags,
|
|
2297
|
+
'subcommand_desc': subcommand_desc,
|
|
2298
|
+
'common_patterns': common_patterns[:6],
|
|
2299
|
+
'args': cmd.get('args', []),
|
|
2131
2300
|
'is_new': False,
|
|
2132
2301
|
})
|
|
2133
2302
|
|