lesson-book 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lesson Book contributors
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.
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.4
2
+ Name: lesson-book
3
+ Version: 0.1.0
4
+ Summary: Tuition memory for traders: a local-first, deterministic mistake ledger with situation matching — before you act, it reminds you where you paid before.
5
+ License-Expression: MIT
6
+ Keywords: trading,journal,mistakes,lessons,discipline,cli,local-first
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Financial and Insurance Industry
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Topic :: Office/Business :: Financial :: Investment
15
+ Requires-Python: >=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Dynamic: license-file
19
+
20
+ # lesson-book
21
+
22
+ **Tuition memory for traders.** A local-first, deterministic mistake ledger:
23
+ record what each mistake cost you, tag it, and `lb match` reminds you of it
24
+ the next time the same situation shows up — **before** you act. Python
25
+ 3.11+, **zero dependencies**, Windows / Linux / macOS. No LLM, no cloud, no
26
+ statistics: the reminder is reproducible and auditable.
27
+
28
+ **Status:** v0.1 — alpha. The matching logic is distilled from a production
29
+ trading system's lesson-matching module; this standalone package is new.
30
+
31
+ ## Why this exists
32
+
33
+ Your trading system has a memory problem: it forgets. The mistake you paid
34
+ 1,200 for last month looks like a fresh opportunity today, because nothing
35
+ stood between the idea and the order. Trading journals solve the *recording*
36
+ half — they are ledgers of what happened. `lesson-book` solves the
37
+ *retrieval* half: it keeps the tuition in a form that can speak up when the
38
+ same situation appears again.
39
+
40
+ Two design commitments make it different from journaling apps and LLM
41
+ "memory" systems:
42
+
43
+ 1. **Deterministic, not statistical.** Scoring is a fixed weighted formula
44
+ (code +3, industry +2, market cap +1, volatility proximity bonus, tag
45
+ overlap bonus). Same situation, same reminder, every time — the opposite
46
+ of an LLM memory that improvises.
47
+ 2. **Local-first and verifiable.** The book is a plain append-only JSONL
48
+ file in your repo. `git log` on it *is* your audit trail; nothing ever
49
+ leaves your machine.
50
+
51
+ ## Philosophy
52
+
53
+ **Tuition is capital — the system does not forget what you paid for, and it
54
+ reminds you before you pay again.**
55
+
56
+ This is the checklist culture of aviation and medicine, applied to trading:
57
+ Gawande's [*The Checklist Manifesto*](https://en.wikipedia.org/wiki/The_Checklist_Manifesto)
58
+ is the canonical argument that simple checklists reduce catastrophic error
59
+ rates by an order of magnitude. And it is the **premortem** in reverse:
60
+ [Klein (2007), "Performing a Project Premortem"](https://hbr.org/2007/09/performing-a-project-premortem)
61
+ asks you to imagine, before acting, that you already failed and explain why.
62
+ `lb match` is the automated premortem: it surfaces the historical answers to
63
+ "why will this fail?" without you having to ask.
64
+
65
+ The [IOM (1999), *To Err Is Human*](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system)
66
+ framing applies directly: errors are a system problem, not a character
67
+ flaw. The book exists to improve the system — classification, review and
68
+ retrieval — never to punish the person. That is why records carry `cost`
69
+ (a number, not a shame) and why the tool classifies but never enforces:
70
+ rules, positions and limits stay with you.
71
+
72
+ ## Quick start
73
+
74
+ ```bash
75
+ # install from PyPI (once published)
76
+ pip install lesson-book
77
+
78
+ # or run without installing anything:
79
+ # PYTHONPATH=src python -m lesson_book --help
80
+
81
+ python examples/demo.py # record, match, review on a scratch book
82
+ ```
83
+
84
+ Your own book:
85
+
86
+ ```bash
87
+ # record a mistake — classification happens via the rule table
88
+ lb add --book book.jsonl \
89
+ --title "bought into the open gap" \
90
+ --issue execution_failed --error-category price_limit \
91
+ --date 2026-08-01 --code 600000 --industry banking \
92
+ --volatility 0.03 --tags gap,limit-up --cost 1200 \
93
+ --lesson "never chase the open gap; wait for the retest"
94
+
95
+ # before acting tomorrow: ask the book
96
+ lb match --book book.jsonl --industry banking --volatility 0.03 \
97
+ --code 600000 --tags gap
98
+ # -> the 2026-08-01 record, score 8.0, lesson: "never chase the open gap..."
99
+
100
+ # import an existing ##-style markdown knowledge base
101
+ lb import-lessons --from LESSONS.md --book book.jsonl
102
+
103
+ # daily review of what the day cost
104
+ lb review --book book.jsonl --day 2026-08-01 --out reviews/
105
+ ```
106
+
107
+ ## Commands
108
+
109
+ | Command | What it does |
110
+ | --- | --- |
111
+ | `add` | Record a mistake: `--title`, `--issue` (required), `--error-category`, context fields (`--code`, `--industry`, `--volatility`, `--market-cap`, `--tags`), `--cost`, `--lesson`, `--situation`. Classified via the rule table (P1/P2, fail-closed to P2) |
112
+ | `import-lessons` | Parse a `##`-headed markdown knowledge base with `**Field:**` metadata (Code/Industry/Volatility/Market Cap/Tags/Cost/Lesson/...) into the book. Idempotent by title |
113
+ | `match` | Rank lessons relevant to the current situation; exit 1 when nothing matches (a pre-action hook can fail-closed on it) |
114
+ | `review` | Daily review: cards grouped by category and priority, total cost, markdown output |
115
+ | `version` | Print version |
116
+
117
+ ## The book
118
+
119
+ `book.jsonl` — append-only, one JSON record per line:
120
+
121
+ ```json
122
+ {"schema_version": "lesson_book.lesson.v1", "record_id": "...",
123
+ "title": "bought into the open gap", "date": "2026-08-01",
124
+ "code": "600000", "industry": "banking", "volatility": 0.03,
125
+ "market_cap": "large", "tags": ["gap", "limit-up"],
126
+ "category": "price_limit_rejected", "priority": "P1",
127
+ "cost": 1200.0, "lesson": "never chase the open gap",
128
+ "situation": "", "recorded_at": "..."}
129
+ ```
130
+
131
+ Matching score (deterministic):
132
+
133
+ | Signal | Weight |
134
+ | --- | --- |
135
+ | same code | +3.0 |
136
+ | same industry | +2.0 |
137
+ | same market cap | +1.0 |
138
+ | volatility proximity | up to +1.5 (decays 5× the gap) |
139
+ | tag overlap | +0.5 per tag, capped at +1.5 |
140
+
141
+ A primary match (code / industry / market cap) is required for a non-zero
142
+ score — the book never speaks up about situations it has no grounds to
143
+ compare.
144
+
145
+ ## Classification rules
146
+
147
+ `add` classifies `issue` + `error_category` through a plain rule table
148
+ (`category`, `priority`, `action`), fully overridable in code. Defaults:
149
+
150
+ | issue | error_category | category | priority |
151
+ | --- | --- | --- | --- |
152
+ | `execution_without_action_plan` | — | `planning_gap` | P1 |
153
+ | `execution_failed` | `price_limit` | `price_limit_rejected` | P1 |
154
+ | `execution_failed` | `trading_time_closed` | `trading_time_closed` | P1 |
155
+ | `execution_failed` | `receipt_reader_error` | `receipt_reader_error` | P1 |
156
+ | `execution_failed` | — | `execution_failure` | P1 |
157
+ | anything else | — | `unclassified` | P2 |
158
+
159
+ Unclassified records are P2 with "review manually and extend the rule table"
160
+ — the taxonomy grows with you, never silently.
161
+
162
+ ## Development
163
+
164
+ ```bash
165
+ python -m pip install -e . pytest
166
+ python -m pytest
167
+ ```
168
+
169
+ CI runs the full test suite on Ubuntu, Windows and macOS with Python 3.11
170
+ and 3.12. Issues are handled on weekends; pull requests are welcome.
171
+
172
+ ## Related work
173
+
174
+ - [Klein (2007), Performing a Project Premortem (HBR)](https://hbr.org/2007/09/performing-a-project-premortem) — imagine the failure before it happens
175
+ - [Gawande (2009), The Checklist Manifesto](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) — checklists as error-rate reduction
176
+ - [IOM (1999), To Err Is Human](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system) — errors as system problems
177
+
178
+ ## License
179
+
180
+ MIT
@@ -0,0 +1,161 @@
1
+ # lesson-book
2
+
3
+ **Tuition memory for traders.** A local-first, deterministic mistake ledger:
4
+ record what each mistake cost you, tag it, and `lb match` reminds you of it
5
+ the next time the same situation shows up — **before** you act. Python
6
+ 3.11+, **zero dependencies**, Windows / Linux / macOS. No LLM, no cloud, no
7
+ statistics: the reminder is reproducible and auditable.
8
+
9
+ **Status:** v0.1 — alpha. The matching logic is distilled from a production
10
+ trading system's lesson-matching module; this standalone package is new.
11
+
12
+ ## Why this exists
13
+
14
+ Your trading system has a memory problem: it forgets. The mistake you paid
15
+ 1,200 for last month looks like a fresh opportunity today, because nothing
16
+ stood between the idea and the order. Trading journals solve the *recording*
17
+ half — they are ledgers of what happened. `lesson-book` solves the
18
+ *retrieval* half: it keeps the tuition in a form that can speak up when the
19
+ same situation appears again.
20
+
21
+ Two design commitments make it different from journaling apps and LLM
22
+ "memory" systems:
23
+
24
+ 1. **Deterministic, not statistical.** Scoring is a fixed weighted formula
25
+ (code +3, industry +2, market cap +1, volatility proximity bonus, tag
26
+ overlap bonus). Same situation, same reminder, every time — the opposite
27
+ of an LLM memory that improvises.
28
+ 2. **Local-first and verifiable.** The book is a plain append-only JSONL
29
+ file in your repo. `git log` on it *is* your audit trail; nothing ever
30
+ leaves your machine.
31
+
32
+ ## Philosophy
33
+
34
+ **Tuition is capital — the system does not forget what you paid for, and it
35
+ reminds you before you pay again.**
36
+
37
+ This is the checklist culture of aviation and medicine, applied to trading:
38
+ Gawande's [*The Checklist Manifesto*](https://en.wikipedia.org/wiki/The_Checklist_Manifesto)
39
+ is the canonical argument that simple checklists reduce catastrophic error
40
+ rates by an order of magnitude. And it is the **premortem** in reverse:
41
+ [Klein (2007), "Performing a Project Premortem"](https://hbr.org/2007/09/performing-a-project-premortem)
42
+ asks you to imagine, before acting, that you already failed and explain why.
43
+ `lb match` is the automated premortem: it surfaces the historical answers to
44
+ "why will this fail?" without you having to ask.
45
+
46
+ The [IOM (1999), *To Err Is Human*](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system)
47
+ framing applies directly: errors are a system problem, not a character
48
+ flaw. The book exists to improve the system — classification, review and
49
+ retrieval — never to punish the person. That is why records carry `cost`
50
+ (a number, not a shame) and why the tool classifies but never enforces:
51
+ rules, positions and limits stay with you.
52
+
53
+ ## Quick start
54
+
55
+ ```bash
56
+ # install from PyPI (once published)
57
+ pip install lesson-book
58
+
59
+ # or run without installing anything:
60
+ # PYTHONPATH=src python -m lesson_book --help
61
+
62
+ python examples/demo.py # record, match, review on a scratch book
63
+ ```
64
+
65
+ Your own book:
66
+
67
+ ```bash
68
+ # record a mistake — classification happens via the rule table
69
+ lb add --book book.jsonl \
70
+ --title "bought into the open gap" \
71
+ --issue execution_failed --error-category price_limit \
72
+ --date 2026-08-01 --code 600000 --industry banking \
73
+ --volatility 0.03 --tags gap,limit-up --cost 1200 \
74
+ --lesson "never chase the open gap; wait for the retest"
75
+
76
+ # before acting tomorrow: ask the book
77
+ lb match --book book.jsonl --industry banking --volatility 0.03 \
78
+ --code 600000 --tags gap
79
+ # -> the 2026-08-01 record, score 8.0, lesson: "never chase the open gap..."
80
+
81
+ # import an existing ##-style markdown knowledge base
82
+ lb import-lessons --from LESSONS.md --book book.jsonl
83
+
84
+ # daily review of what the day cost
85
+ lb review --book book.jsonl --day 2026-08-01 --out reviews/
86
+ ```
87
+
88
+ ## Commands
89
+
90
+ | Command | What it does |
91
+ | --- | --- |
92
+ | `add` | Record a mistake: `--title`, `--issue` (required), `--error-category`, context fields (`--code`, `--industry`, `--volatility`, `--market-cap`, `--tags`), `--cost`, `--lesson`, `--situation`. Classified via the rule table (P1/P2, fail-closed to P2) |
93
+ | `import-lessons` | Parse a `##`-headed markdown knowledge base with `**Field:**` metadata (Code/Industry/Volatility/Market Cap/Tags/Cost/Lesson/...) into the book. Idempotent by title |
94
+ | `match` | Rank lessons relevant to the current situation; exit 1 when nothing matches (a pre-action hook can fail-closed on it) |
95
+ | `review` | Daily review: cards grouped by category and priority, total cost, markdown output |
96
+ | `version` | Print version |
97
+
98
+ ## The book
99
+
100
+ `book.jsonl` — append-only, one JSON record per line:
101
+
102
+ ```json
103
+ {"schema_version": "lesson_book.lesson.v1", "record_id": "...",
104
+ "title": "bought into the open gap", "date": "2026-08-01",
105
+ "code": "600000", "industry": "banking", "volatility": 0.03,
106
+ "market_cap": "large", "tags": ["gap", "limit-up"],
107
+ "category": "price_limit_rejected", "priority": "P1",
108
+ "cost": 1200.0, "lesson": "never chase the open gap",
109
+ "situation": "", "recorded_at": "..."}
110
+ ```
111
+
112
+ Matching score (deterministic):
113
+
114
+ | Signal | Weight |
115
+ | --- | --- |
116
+ | same code | +3.0 |
117
+ | same industry | +2.0 |
118
+ | same market cap | +1.0 |
119
+ | volatility proximity | up to +1.5 (decays 5× the gap) |
120
+ | tag overlap | +0.5 per tag, capped at +1.5 |
121
+
122
+ A primary match (code / industry / market cap) is required for a non-zero
123
+ score — the book never speaks up about situations it has no grounds to
124
+ compare.
125
+
126
+ ## Classification rules
127
+
128
+ `add` classifies `issue` + `error_category` through a plain rule table
129
+ (`category`, `priority`, `action`), fully overridable in code. Defaults:
130
+
131
+ | issue | error_category | category | priority |
132
+ | --- | --- | --- | --- |
133
+ | `execution_without_action_plan` | — | `planning_gap` | P1 |
134
+ | `execution_failed` | `price_limit` | `price_limit_rejected` | P1 |
135
+ | `execution_failed` | `trading_time_closed` | `trading_time_closed` | P1 |
136
+ | `execution_failed` | `receipt_reader_error` | `receipt_reader_error` | P1 |
137
+ | `execution_failed` | — | `execution_failure` | P1 |
138
+ | anything else | — | `unclassified` | P2 |
139
+
140
+ Unclassified records are P2 with "review manually and extend the rule table"
141
+ — the taxonomy grows with you, never silently.
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ python -m pip install -e . pytest
147
+ python -m pytest
148
+ ```
149
+
150
+ CI runs the full test suite on Ubuntu, Windows and macOS with Python 3.11
151
+ and 3.12. Issues are handled on weekends; pull requests are welcome.
152
+
153
+ ## Related work
154
+
155
+ - [Klein (2007), Performing a Project Premortem (HBR)](https://hbr.org/2007/09/performing-a-project-premortem) — imagine the failure before it happens
156
+ - [Gawande (2009), The Checklist Manifesto](https://en.wikipedia.org/wiki/The_Checklist_Manifesto) — checklists as error-rate reduction
157
+ - [IOM (1999), To Err Is Human](https://nap.nationalacademies.org/catalog/9728/to-err-is-human-building-a-safer-health-system) — errors as system problems
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lesson-book"
7
+ version = "0.1.0"
8
+ description = "Tuition memory for traders: a local-first, deterministic mistake ledger with situation matching — before you act, it reminds you where you paid before."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ keywords = ["trading", "journal", "mistakes", "lessons", "discipline", "cli", "local-first"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Environment :: Console",
16
+ "Intended Audience :: Financial and Insurance Industry",
17
+ "Operating System :: OS Independent",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Topic :: Office/Business :: Financial :: Investment",
22
+ ]
23
+
24
+ [project.scripts]
25
+ lb = "lesson_book.cli:main"
26
+ lesson-book = "lesson_book.cli:main"
27
+
28
+ [tool.setuptools.packages.find]
29
+ where = ["src"]
30
+
31
+ [tool.pytest.ini_options]
32
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """Tuition memory: a local-first, deterministic mistake ledger.
2
+
3
+ Record what each mistake cost you, tag it, and let ``match`` remind you of
4
+ it the next time the same situation shows up — before you act. No LLM, no
5
+ cloud, no statistics: just an append-only JSONL book and deterministic
6
+ scoring, so the reminder is reproducible and auditable.
7
+ """
8
+
9
+ from .lessons import import_lessons_markdown, match_lessons, score_lesson
10
+ from .mistakes import (
11
+ add_mistake,
12
+ classify,
13
+ default_rules,
14
+ load_book,
15
+ review_day,
16
+ )
17
+
18
+ __version__ = "0.1.0"
19
+
20
+ __all__ = [
21
+ "add_mistake",
22
+ "classify",
23
+ "default_rules",
24
+ "import_lessons_markdown",
25
+ "load_book",
26
+ "match_lessons",
27
+ "review_day",
28
+ "score_lesson",
29
+ ]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ raise SystemExit(main())
@@ -0,0 +1,136 @@
1
+ """Command-line interface for lesson-book.
2
+
3
+ Subcommands:
4
+
5
+ - ``add`` record a mistake (classified via the rule table)
6
+ - ``import-lessons`` parse a ``##``-style markdown knowledge base into the book
7
+ - ``match`` rank lessons relevant to a current situation (pre-action)
8
+ - ``review`` daily review markdown of one day's mistakes
9
+ - ``version`` print version
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import sys
17
+ from typing import Any
18
+
19
+ from . import __version__
20
+ from .lessons import import_lessons_markdown, match_lessons
21
+ from .mistakes import add_mistake, review_day
22
+
23
+
24
+ def _print_json(body: Any) -> None:
25
+ print(json.dumps(body, ensure_ascii=False, indent=2))
26
+
27
+
28
+ def build_parser() -> argparse.ArgumentParser:
29
+ parser = argparse.ArgumentParser(
30
+ prog="lb",
31
+ description="Tuition memory: a local-first, deterministic mistake ledger.",
32
+ )
33
+ sub = parser.add_subparsers(dest="command", required=True)
34
+
35
+ add = sub.add_parser("add", help="record a mistake")
36
+ add.add_argument("--book", required=True, help="JSONL book path")
37
+ add.add_argument("--title", required=True)
38
+ add.add_argument("--issue", required=True)
39
+ add.add_argument("--error-category", default=None)
40
+ add.add_argument("--date", default=None)
41
+ add.add_argument("--code", default="")
42
+ add.add_argument("--industry", default="")
43
+ add.add_argument("--volatility", type=float, default=None)
44
+ add.add_argument("--market-cap", default="")
45
+ add.add_argument("--tags", default="", help="comma-separated tags")
46
+ add.add_argument("--cost", type=float, default=None)
47
+ add.add_argument("--lesson", default="")
48
+ add.add_argument("--situation", default="")
49
+
50
+ imp = sub.add_parser("import-lessons", help="import a markdown knowledge base")
51
+ imp.add_argument("--from", dest="from_path", required=True, help="markdown lessons file")
52
+ imp.add_argument("--book", required=True, help="JSONL book path")
53
+
54
+ match = sub.add_parser("match", help="match the current situation against the book")
55
+ match.add_argument("--book", required=True)
56
+ match.add_argument("--code", default="")
57
+ match.add_argument("--industry", required=True)
58
+ match.add_argument("--volatility", type=float, required=True)
59
+ match.add_argument("--market-cap", default="")
60
+ match.add_argument("--tags", default="", help="comma-separated tags")
61
+ match.add_argument("--top-n", type=int, default=3)
62
+ match.add_argument("--days-window", type=int, default=None)
63
+
64
+ review = sub.add_parser("review", help="daily review of one day's mistakes")
65
+ review.add_argument("--book", required=True)
66
+ review.add_argument("--day", required=True, help="YYYY-MM-DD")
67
+ review.add_argument("--out", default=None, help="output directory for the markdown")
68
+
69
+ sub.add_parser("version", help="print version")
70
+ return parser
71
+
72
+
73
+ def _split_tags(value: str) -> tuple[str, ...]:
74
+ return tuple(tag.strip() for tag in value.split(",") if tag.strip())
75
+
76
+
77
+ def main(argv: list[str] | None = None) -> int:
78
+ parser = build_parser()
79
+ args = parser.parse_args(argv)
80
+
81
+ if args.command == "version":
82
+ print(__version__)
83
+ return 0
84
+
85
+ if args.command == "add":
86
+ record = add_mistake(
87
+ args.book,
88
+ title=args.title,
89
+ issue=args.issue,
90
+ error_category=args.error_category,
91
+ date=args.date,
92
+ code=args.code,
93
+ industry=args.industry,
94
+ volatility=args.volatility,
95
+ market_cap=args.market_cap,
96
+ tags=_split_tags(args.tags),
97
+ cost=args.cost,
98
+ lesson=args.lesson,
99
+ situation=args.situation,
100
+ )
101
+ _print_json(record)
102
+ return 0
103
+
104
+ if args.command == "import-lessons":
105
+ result = import_lessons_markdown(args.from_path, args.book)
106
+ print(f"import: {result['imported']} imported, {result['skipped']} skipped")
107
+ return 0
108
+
109
+ if args.command == "match":
110
+ matches = match_lessons(
111
+ args.code,
112
+ args.industry,
113
+ args.volatility,
114
+ market_cap=args.market_cap,
115
+ tags=_split_tags(args.tags),
116
+ top_n=args.top_n,
117
+ path=args.book,
118
+ days_window=args.days_window,
119
+ )
120
+ if not matches:
121
+ print("match: no relevant lessons (this situation is new tuition)")
122
+ return 1
123
+ _print_json({"matches": matches})
124
+ return 0
125
+
126
+ if args.command == "review":
127
+ payload = review_day(args.book, args.day, out_dir=args.out)
128
+ _print_json(payload)
129
+ return 0
130
+
131
+ parser.error(f"unknown command: {args.command}")
132
+ return 2
133
+
134
+
135
+ if __name__ == "__main__":
136
+ sys.exit(main())