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.8",
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">&#9660;</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
- # Get educational description from COMMAND_DB if session description is empty/generic
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
- description = session_desc if session_desc else kb_desc
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