learn_bash_from_session_data 1.0.8 → 1.0.9
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/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.9",
|
|
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": {
|
|
@@ -433,7 +433,7 @@ def render_commands_tab(commands: list[dict]) -> str:
|
|
|
433
433
|
<span class="category-badge">{category}</span>
|
|
434
434
|
</div>
|
|
435
435
|
<div class="command-meta">
|
|
436
|
-
<span class="cmd-preview">{description[:60]}{'...' if len(description) > 60 else ''}</span>
|
|
436
|
+
<span class="cmd-preview">{' '.join(description.split())[:60]}{'...' if len(' '.join(description.split())) > 60 else ''}</span>
|
|
437
437
|
<span class="expand-icon">▼</span>
|
|
438
438
|
</div>
|
|
439
439
|
</div>
|
|
@@ -2185,6 +2185,24 @@ def generate_html_files(
|
|
|
2185
2185
|
base_cmd = cmd.get('base_command', cmd_str.split()[0] if cmd_str else '')
|
|
2186
2186
|
complexity_score = cmd.get('complexity', 1)
|
|
2187
2187
|
|
|
2188
|
+
# Filter out non-bash entries (Python/JS code fragments, single chars, status text)
|
|
2189
|
+
if not base_cmd or len(base_cmd) < 2:
|
|
2190
|
+
continue
|
|
2191
|
+
# Skip entries that look like code fragments (contain parens, equals, dots as methods)
|
|
2192
|
+
if any(c in base_cmd for c in ('(', ')', '=', '{', '}')) and not base_cmd.startswith('.'):
|
|
2193
|
+
continue
|
|
2194
|
+
# Skip entries that are clearly not commands (capitalized status words, text fragments)
|
|
2195
|
+
if base_cmd[0].isupper() and base_cmd.isalpha() and base_cmd not in ('PATH', 'HOME'):
|
|
2196
|
+
continue
|
|
2197
|
+
# Skip common text fragments that get misidentified as commands
|
|
2198
|
+
junk_tokens = {'version', 'total', 'package', 'success', 'error', 'reading',
|
|
2199
|
+
'editing', 'done', 'warning', 'info', 'note', 'output'}
|
|
2200
|
+
if base_cmd.lower() in junk_tokens:
|
|
2201
|
+
continue
|
|
2202
|
+
|
|
2203
|
+
# Tokenize the command for subcommand/description generation
|
|
2204
|
+
cmd_tokens = cmd_str.split() if cmd_str else []
|
|
2205
|
+
|
|
2188
2206
|
# Look up COMMAND_DB info for this command
|
|
2189
2207
|
cmd_info = COMMAND_DB.get(base_cmd, {})
|
|
2190
2208
|
kb_flags = get_flags_for_command(base_cmd)
|
|
@@ -2266,15 +2284,74 @@ def generate_html_files(
|
|
|
2266
2284
|
flag_desc = common_flags.get(f, '')
|
|
2267
2285
|
formatted_flags.append({'flag': f, 'description': flag_desc})
|
|
2268
2286
|
|
|
2269
|
-
#
|
|
2287
|
+
# Generate a contextual description that differentiates commands with the same base
|
|
2270
2288
|
session_desc = cmd.get('description', '')
|
|
2271
2289
|
kb_desc = cmd_info.get('description', '')
|
|
2272
|
-
|
|
2290
|
+
|
|
2291
|
+
# Build a specific description from the actual command content
|
|
2292
|
+
args_list = cmd.get('args', [])
|
|
2293
|
+
flag_list = [fl.get('flag', '') if isinstance(fl, dict) else str(fl) for fl in formatted_flags]
|
|
2294
|
+
contextual_desc = ''
|
|
2295
|
+
|
|
2296
|
+
# For inline code execution (python -c, bash -c), summarize the code snippet
|
|
2297
|
+
if base_cmd in ('python', 'python3', 'bash', 'sh', 'node') and '-c' in flag_list:
|
|
2298
|
+
# Extract the inline code from the full command after -c
|
|
2299
|
+
c_idx = cmd_str.find('-c')
|
|
2300
|
+
if c_idx >= 0:
|
|
2301
|
+
raw_code = cmd_str[c_idx + 2:].strip().strip('"').strip("'")
|
|
2302
|
+
# Split on actual newlines before collapsing
|
|
2303
|
+
code_lines = [l.strip() for l in raw_code.splitlines() if l.strip()]
|
|
2304
|
+
# Find first non-import line for a distinctive preview
|
|
2305
|
+
action_lines = [l for l in code_lines if not l.startswith(('import ', 'from ', '#'))]
|
|
2306
|
+
if action_lines:
|
|
2307
|
+
code_part = ' '.join(action_lines[0].split())[:60]
|
|
2308
|
+
elif code_lines:
|
|
2309
|
+
# All imports - show what's being imported
|
|
2310
|
+
code_part = ' '.join(code_lines[0].split())[:60]
|
|
2311
|
+
else:
|
|
2312
|
+
code_part = ''
|
|
2313
|
+
if code_part:
|
|
2314
|
+
contextual_desc = f"{base_cmd} -c: {code_part}{'...' if len(code_part) >= 60 else ''}"
|
|
2315
|
+
|
|
2316
|
+
# For commands with subcommands (git, npm, docker, etc.), use subcommand context
|
|
2317
|
+
if not contextual_desc and cmd_tokens and len(cmd_tokens) > 1:
|
|
2318
|
+
subcmd_token = next((t for t in cmd_tokens[1:] if not t.startswith('-') and not t.startswith('"') and not t.startswith("'")), '')
|
|
2319
|
+
if subcmd_token and subcmd_token != base_cmd:
|
|
2320
|
+
subcmd_info = cmd_info.get('subcommands', {}).get(subcmd_token, '')
|
|
2321
|
+
if subcmd_info:
|
|
2322
|
+
contextual_desc = f"{base_cmd} {subcmd_token}: {subcmd_info}"
|
|
2323
|
+
else:
|
|
2324
|
+
contextual_desc = f"{base_cmd} {subcmd_token}"
|
|
2325
|
+
# Add meaningful args (skip very long ones, quotes, code)
|
|
2326
|
+
short_args = [a for a in args_list if len(str(a)) < 40 and a != subcmd_token and not a.startswith('"')]
|
|
2327
|
+
if short_args:
|
|
2328
|
+
contextual_desc += f" ({', '.join(short_args[:3])})"
|
|
2329
|
+
|
|
2330
|
+
# For commands with flags but no subcommand, describe with flags
|
|
2331
|
+
if not contextual_desc and flag_list:
|
|
2332
|
+
flag_summary = ', '.join(flag_list[:3])
|
|
2333
|
+
short_args = [a for a in args_list if len(str(a)) < 40]
|
|
2334
|
+
if short_args:
|
|
2335
|
+
contextual_desc = f"{base_cmd} {flag_summary} on {', '.join(short_args[:2])}"
|
|
2336
|
+
else:
|
|
2337
|
+
contextual_desc = f"{base_cmd} with {flag_summary}"
|
|
2338
|
+
|
|
2339
|
+
# For simple commands with just args
|
|
2340
|
+
if not contextual_desc and args_list:
|
|
2341
|
+
short_args = [a for a in args_list if len(str(a)) < 40]
|
|
2342
|
+
if short_args:
|
|
2343
|
+
contextual_desc = f"{base_cmd} {' '.join(short_args[:3])}"
|
|
2344
|
+
|
|
2345
|
+
# Priority: contextual > session > knowledge base
|
|
2346
|
+
if contextual_desc:
|
|
2347
|
+
description = contextual_desc
|
|
2348
|
+
elif session_desc:
|
|
2349
|
+
description = session_desc
|
|
2350
|
+
else:
|
|
2351
|
+
description = kb_desc if kb_desc else f"Run {base_cmd} command"
|
|
2273
2352
|
|
|
2274
2353
|
# Get subcommand info (for commands like git, docker, npm)
|
|
2275
2354
|
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
2355
|
subcommand_desc = ''
|
|
2279
2356
|
if subcommands and len(cmd_tokens) > 1:
|
|
2280
2357
|
for token in cmd_tokens[1:]:
|
|
@@ -91,6 +91,8 @@ CATEGORY_MAPPINGS: Dict[str, Set[str]] = {
|
|
|
91
91
|
"history", "fc", "true", "false", "test", "[", "[[", "exit",
|
|
92
92
|
"return", "break", "continue", "shift", "getopts", "trap",
|
|
93
93
|
"ulimit", "times", "let", ":", "compgen", "complete", "compopt",
|
|
94
|
+
"cmd.exe", "cmd", "start", "where", "type",
|
|
95
|
+
"session-slides", "learn-bash", "bash-learner", "claude",
|
|
94
96
|
},
|
|
95
97
|
}
|
|
96
98
|
|