learn_bash_from_session_data 1.0.5 → 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.
- package/package.json +7 -3
- package/scripts/html_generator.py +19 -25
- package/scripts/knowledge_base.py +5624 -1593
- package/scripts/quiz_generator.py +82 -28
- package/vectors.db +0 -0
|
@@ -18,6 +18,45 @@ import random
|
|
|
18
18
|
import re
|
|
19
19
|
import hashlib
|
|
20
20
|
|
|
21
|
+
try:
|
|
22
|
+
from scripts.knowledge_base import COMMAND_DB, get_command_info, get_flags_for_command
|
|
23
|
+
except ImportError:
|
|
24
|
+
try:
|
|
25
|
+
from knowledge_base import COMMAND_DB, get_command_info, get_flags_for_command
|
|
26
|
+
except ImportError:
|
|
27
|
+
COMMAND_DB = {}
|
|
28
|
+
def get_command_info(name): return None
|
|
29
|
+
def get_flags_for_command(command): return {}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _get_flags_for_cmd(cmd: str) -> dict[str, str]:
|
|
33
|
+
"""Get merged flags for a command from knowledge_base (primary) and local FLAG_DATABASE (fallback).
|
|
34
|
+
|
|
35
|
+
Knowledge_base.py COMMAND_DB is the authoritative source. FLAG_DATABASE provides
|
|
36
|
+
additional coverage for commands not yet in knowledge_base.
|
|
37
|
+
"""
|
|
38
|
+
flags = {}
|
|
39
|
+
# Primary source: knowledge_base COMMAND_DB
|
|
40
|
+
kb_flags = get_flags_for_command(cmd)
|
|
41
|
+
if kb_flags:
|
|
42
|
+
flags.update(kb_flags)
|
|
43
|
+
# Fallback/supplement: local FLAG_DATABASE
|
|
44
|
+
if cmd in FLAG_DATABASE:
|
|
45
|
+
for flag, desc in FLAG_DATABASE[cmd].items():
|
|
46
|
+
if flag not in flags:
|
|
47
|
+
flags[flag] = desc
|
|
48
|
+
return flags
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _get_all_flagged_commands() -> set[str]:
|
|
52
|
+
"""Get the set of all commands that have flag data from any source."""
|
|
53
|
+
cmds = set()
|
|
54
|
+
for cmd, info in COMMAND_DB.items():
|
|
55
|
+
if info.get("flags"):
|
|
56
|
+
cmds.add(cmd)
|
|
57
|
+
cmds.update(FLAG_DATABASE.keys())
|
|
58
|
+
return cmds
|
|
59
|
+
|
|
21
60
|
|
|
22
61
|
class QuizType(Enum):
|
|
23
62
|
"""Types of quiz questions."""
|
|
@@ -499,6 +538,19 @@ def _describe_single_command(cmd: str) -> str:
|
|
|
499
538
|
tokens = cmd.split()
|
|
500
539
|
base_cmd = tokens[0] if tokens else ''
|
|
501
540
|
|
|
541
|
+
# Get args (skip flags) for knowledge_base fallback
|
|
542
|
+
args = [t for t in tokens[1:] if not t.startswith('-')]
|
|
543
|
+
|
|
544
|
+
# Check knowledge_base COMMAND_DB for rich description
|
|
545
|
+
if base_cmd and base_cmd in COMMAND_DB:
|
|
546
|
+
cmd_info = COMMAND_DB[base_cmd]
|
|
547
|
+
kb_desc = cmd_info.get('description', '')
|
|
548
|
+
if kb_desc:
|
|
549
|
+
# Use knowledge base description but make it contextual with args
|
|
550
|
+
if args:
|
|
551
|
+
return f"{kb_desc.lower()} ({' '.join(args[:2])})"
|
|
552
|
+
return kb_desc.lower()
|
|
553
|
+
|
|
502
554
|
# Common command descriptions with bash focus
|
|
503
555
|
descriptions = {
|
|
504
556
|
'cd': lambda args: f"changes directory to {args[0] if args else 'specified path'}",
|
|
@@ -637,35 +689,35 @@ def _parse_command(cmd_string: str) -> dict:
|
|
|
637
689
|
|
|
638
690
|
|
|
639
691
|
def _get_flag_description(cmd: str, flag: str) -> Optional[str]:
|
|
640
|
-
"""Get description for a flag of a command."""
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
return FLAG_DATABASE[cmd][single_flag]
|
|
692
|
+
"""Get description for a flag of a command from merged sources."""
|
|
693
|
+
merged = _get_flags_for_cmd(cmd)
|
|
694
|
+
if flag in merged:
|
|
695
|
+
return merged[flag]
|
|
696
|
+
# Try individual characters for combined flags (e.g., -la -> -l, -a)
|
|
697
|
+
if len(flag) > 2 and flag.startswith("-") and not flag.startswith("--"):
|
|
698
|
+
for char in flag[1:]:
|
|
699
|
+
single_flag = f"-{char}"
|
|
700
|
+
if single_flag in merged:
|
|
701
|
+
return merged[single_flag]
|
|
651
702
|
return None
|
|
652
703
|
|
|
653
704
|
|
|
654
705
|
def _generate_distractor_flags(cmd: str, correct_flag: str, count: int = 3) -> list[str]:
|
|
655
|
-
"""Generate plausible distractor flags."""
|
|
706
|
+
"""Generate plausible distractor flags from merged knowledge sources."""
|
|
656
707
|
distractors = []
|
|
657
708
|
|
|
658
|
-
# Get other flags from the same command
|
|
659
|
-
|
|
660
|
-
|
|
709
|
+
# Get other flags from the same command (merged sources)
|
|
710
|
+
cmd_flags = _get_flags_for_cmd(cmd)
|
|
711
|
+
if cmd_flags:
|
|
712
|
+
other_flags = [f for f in cmd_flags.keys() if f != correct_flag]
|
|
661
713
|
random.shuffle(other_flags)
|
|
662
714
|
distractors.extend(other_flags[:count])
|
|
663
715
|
|
|
664
716
|
# If we need more, get common flags from other commands
|
|
665
717
|
if len(distractors) < count:
|
|
666
|
-
for other_cmd
|
|
718
|
+
for other_cmd in _get_all_flagged_commands():
|
|
667
719
|
if other_cmd != cmd:
|
|
668
|
-
for flag in
|
|
720
|
+
for flag in _get_flags_for_cmd(other_cmd):
|
|
669
721
|
if flag not in distractors and flag != correct_flag:
|
|
670
722
|
distractors.append(flag)
|
|
671
723
|
if len(distractors) >= count:
|
|
@@ -680,10 +732,10 @@ def _generate_distractor_descriptions(correct_desc: str, count: int = 3) -> list
|
|
|
680
732
|
"""Generate plausible wrong descriptions."""
|
|
681
733
|
distractors = []
|
|
682
734
|
|
|
683
|
-
# Collect all descriptions from
|
|
735
|
+
# Collect all descriptions from merged sources
|
|
684
736
|
all_descriptions = []
|
|
685
|
-
for
|
|
686
|
-
all_descriptions.extend(
|
|
737
|
+
for cmd in _get_all_flagged_commands():
|
|
738
|
+
all_descriptions.extend(_get_flags_for_cmd(cmd).values())
|
|
687
739
|
|
|
688
740
|
# Remove duplicates and the correct answer
|
|
689
741
|
all_descriptions = list(set(all_descriptions))
|
|
@@ -797,16 +849,17 @@ def generate_which_flag_quiz(
|
|
|
797
849
|
parsed = _parse_command(cmd_string)
|
|
798
850
|
base_cmd = parsed["base"]
|
|
799
851
|
|
|
800
|
-
|
|
852
|
+
cmd_flags = _get_flags_for_cmd(base_cmd)
|
|
853
|
+
if not cmd_flags or not parsed["flags"]:
|
|
801
854
|
return None
|
|
802
855
|
|
|
803
856
|
# Pick a flag to quiz on
|
|
804
|
-
available_flags = [f for f in parsed["flags"] if f in
|
|
857
|
+
available_flags = [f for f in parsed["flags"] if f in cmd_flags]
|
|
805
858
|
if not available_flags:
|
|
806
859
|
return None
|
|
807
860
|
|
|
808
861
|
target_flag = random.choice(available_flags)
|
|
809
|
-
flag_desc =
|
|
862
|
+
flag_desc = cmd_flags[target_flag]
|
|
810
863
|
|
|
811
864
|
# Generate distractor flags
|
|
812
865
|
distractor_flags = _generate_distractor_flags(base_cmd, target_flag, 3)
|
|
@@ -831,13 +884,13 @@ def generate_which_flag_quiz(
|
|
|
831
884
|
correct_id = opt_id
|
|
832
885
|
|
|
833
886
|
# Get description for option explanation
|
|
834
|
-
flag_explanation =
|
|
887
|
+
flag_explanation = cmd_flags.get(flag, "Unknown flag")
|
|
835
888
|
|
|
836
889
|
options.append(QuizOption(
|
|
837
890
|
id=opt_id,
|
|
838
891
|
text=flag,
|
|
839
892
|
is_correct=is_correct,
|
|
840
|
-
explanation=f"{flag}: {flag_explanation}" if flag in
|
|
893
|
+
explanation=f"{flag}: {flag_explanation}" if flag in cmd_flags else f"{flag}: Not a standard flag for {base_cmd}"
|
|
841
894
|
))
|
|
842
895
|
|
|
843
896
|
question_id = _generate_id(f"which_flag_{base_cmd}_{target_flag}")
|
|
@@ -896,7 +949,7 @@ def generate_build_command_quiz(
|
|
|
896
949
|
distractors.append(" ".join(missing_flag))
|
|
897
950
|
|
|
898
951
|
# Distractor 3: Wrong flag
|
|
899
|
-
if parsed["flags"] and base_cmd
|
|
952
|
+
if parsed["flags"] and _get_flags_for_cmd(base_cmd):
|
|
900
953
|
wrong_flags = _generate_distractor_flags(base_cmd, parsed["flags"][0], 1)
|
|
901
954
|
if wrong_flags:
|
|
902
955
|
wrong_flag_cmd = [base_cmd] + [wrong_flags[0]] + parsed["flags"][1:] + parsed["args"]
|
|
@@ -1065,14 +1118,15 @@ def _create_similar_command_variant(command: dict) -> Optional[dict]:
|
|
|
1065
1118
|
parsed = _parse_command(cmd_string)
|
|
1066
1119
|
base_cmd = parsed["base"]
|
|
1067
1120
|
|
|
1068
|
-
|
|
1121
|
+
variant_flags = _get_flags_for_cmd(base_cmd)
|
|
1122
|
+
if not variant_flags:
|
|
1069
1123
|
return None
|
|
1070
1124
|
|
|
1071
1125
|
# Strategy: add, remove, or change a flag
|
|
1072
1126
|
strategies = []
|
|
1073
1127
|
|
|
1074
1128
|
# Can add a flag
|
|
1075
|
-
available_flags = [f for f in
|
|
1129
|
+
available_flags = [f for f in variant_flags.keys() if f not in parsed["flags"]]
|
|
1076
1130
|
if available_flags:
|
|
1077
1131
|
strategies.append("add")
|
|
1078
1132
|
|
package/vectors.db
ADDED
|
Binary file
|