kyp-mem 0.4.1 → 0.4.2

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 CHANGED
@@ -1,140 +1,289 @@
1
1
  # KYP-MEM — Know Your Project Memory
2
2
 
3
- **Persistent knowledge base for AI agents.** Markdown vault with wikilinks, backlinks, tags, graph navigation, and auto-learning all powered by an MCP server so Claude (or any AI) can read and write project knowledge across sessions.
3
+ **Persistent memory for AI coding agents.** Your AI agent forgets everything between sessions. KYP-MEM fixes that.
4
4
 
5
- ## Install
5
+ KYP-MEM is an MCP server that gives your AI agent a knowledge base and session memory. It remembers your architecture, past bugs, decisions, and what happened last session — so you never repeat yourself.
6
+
7
+ ---
8
+
9
+ ## How It Works
10
+
11
+ KYP-MEM gives your agent two types of memory:
12
+
13
+ ### Knowledge Base — long-term project memory
14
+
15
+ Structured markdown notes organized by project. Architecture docs, API references, known bugs, key decisions, setup guides. The agent reads these before doing any work and updates them as it learns.
6
16
 
7
- ```bash
8
- npm install -g kyp-mem
9
17
  ```
18
+ MyProject/
19
+ ├── Knowledge.md # Architecture, bugs, decisions, notes
20
+ ├── API.md # Endpoints and contracts
21
+ ├── Setup.md # Environment setup guide
22
+ └── Sessions/
23
+ ```
24
+
25
+ ### Session Memory — what happened each session
10
26
 
11
- Or run directly:
27
+ Every coding session is automatically captured — files changed, commands run, prompts used. These notes are embedded into a vector database for semantic search, so the agent can find past work by meaning, not just keywords.
28
+
29
+ ```
30
+ MyProject/Sessions/
31
+ ├── 2026-05-12_143022.md # "Fixed auth bug, found rate limiter issue"
32
+ ├── 2026-05-12_091544.md # "Investigated flaky tests — race condition"
33
+ └── 2026-05-11_162301.md # "Set up CI, decided on GitHub Actions"
34
+ ```
35
+
36
+ ### The Loop
37
+
38
+ ```
39
+ Session Start
40
+ → Agent loads project knowledge + recent sessions
41
+ → Agent is grounded: knows architecture, past bugs, last session's next steps
42
+
43
+ During Work
44
+ → Agent hits a bug → searches session memory → finds it was already investigated
45
+ → Agent makes a decision → updates Knowledge.md so future sessions know
46
+
47
+ Session End
48
+ → Hooks auto-capture everything into a structured session note
49
+ → Note is embedded into the vector store for future semantic search
50
+ ```
51
+
52
+ ---
53
+
54
+ ## Install
12
55
 
13
56
  ```bash
14
- npx -y kyp-mem
57
+ pip install kyp-mem
15
58
  ```
16
59
 
17
60
  ## Setup
18
61
 
19
62
  ```bash
20
- kyp-mem init # Choose vault location
21
- kyp-mem setup-claude # Auto-configure Claude Code MCP
22
- kyp-mem install-hooks # Enable auto-learning from sessions
63
+ kyp-mem init # Choose where to store your vault
64
+ kyp-mem setup-claude # Register MCP server with Claude Code
65
+ kyp-mem install-hooks # Enable automatic session capture
23
66
  ```
24
67
 
25
- Restart Claude Code. Done — kyp-mem runs headlessly every session with 9 tools available.
68
+ Restart Claude Code. KYP-MEM runs automatically with 14 tools available to the agent.
26
69
 
27
- ## Auto-Learning
70
+ ### Quick Setup (one command)
71
+
72
+ ```bash
73
+ claude mcp add -s user -e KYP_VAULT="$HOME/.kyp-mem/vault" kyp-mem -- kyp-mem serve
74
+ ```
28
75
 
29
- KYP-MEM can automatically capture what happens in every Claude Code session:
76
+ ### Enable for All Projects
30
77
 
31
78
  ```bash
79
+ kyp-mem setup-claude --global
32
80
  kyp-mem install-hooks --global
33
81
  ```
34
82
 
35
- This installs two hooks:
36
- - **PostToolUse** — captures file edits, writes, and commands (pure Node, fast)
37
- - **Stop** compiles session activity into a vault note under `Sessions/`
83
+ ---
84
+
85
+ ## What the Agent Does Automatically
86
+
87
+ KYP-MEM embeds behavioral instructions directly into tool descriptions. The agent follows these rules without any user action:
88
+
89
+ 1. **Session start** — loads project knowledge + recent session history
90
+ 2. **Before investigating bugs** — searches session memory first to avoid duplicate work
91
+ 3. **Before making decisions** — checks if a prior session already decided this
92
+ 4. **After fixing or learning something** — updates Knowledge.md for future sessions
93
+ 5. **Never hallucinates** — if it's not in the knowledge base, it says so
94
+
95
+ No prompting needed. The agent reads the tool descriptions and follows the protocol.
96
+
97
+ ---
98
+
99
+ ## Automatic Session Capture
100
+
101
+ ```bash
102
+ kyp-mem install-hooks
103
+ ```
104
+
105
+ Installs three Claude Code hooks:
106
+
107
+ - **UserPromptSubmit** — captures every prompt you send to the agent
108
+ - **PostToolUse** — captures file edits, writes, and shell commands in real-time
109
+ - **Stop** — when the session ends, compiles all activity into a structured note
110
+
111
+ Each session note looks like this:
112
+
113
+ ```markdown
114
+ # Session 2026-05-12_143022
115
+
116
+ **Project:** MyProject
117
+ **Actions:** 15 total, 12 substantive
118
+
119
+ ## Summary
120
+ Fixed auth bug, refactored token refresh logic.
121
+
122
+ ## PROMPTS
123
+ ### 1. [14:25:01]
124
+ > fix the token refresh bug in auth.py
125
+
126
+ ### 2. [14:30:22]
127
+ > also add retry logic for expired tokens
128
+
129
+ ## INVESTIGATED
130
+ - grep for "token expired" across auth module
131
+ - read OAuth provider docs
132
+
133
+ ## LEARNED
134
+ - Refresh tokens expire after 30 days, not 90
135
+
136
+ ## COMPLETED
137
+ - Fixed token refresh in auth.py
138
+ - Added retry logic for expired tokens
139
+
140
+ ## NEXT STEPS
141
+ - Add integration test for token refresh flow
142
+ ```
38
143
 
39
144
  Sessions with fewer than 3 substantive actions are automatically skipped.
40
145
 
146
+ ---
147
+
148
+ ## Semantic Search
149
+
150
+ Session notes are embedded into a vector database (ChromaDB). This enables search by meaning:
151
+
152
+ - Searching **"authentication failing"** finds sessions about "login bug" and "OAuth token expiry"
153
+ - Searching **"deploy process"** finds sessions about "CI pipeline setup" and "release workflow"
154
+
155
+ The agent doesn't need exact keywords — it finds semantically related past work.
156
+
157
+ ---
158
+
41
159
  ## Web UI
42
160
 
43
161
  ```bash
44
162
  kyp-mem ui
45
163
  ```
46
164
 
47
- Opens at `localhost:3333` with:
48
- - Quick switcher (`Cmd+O`) — fuzzy jump to any note
49
- - Full-text search (`Cmd+K`)
50
- - Tag filtering — clickable tag cloud, AND-filter
51
- - Outline panel — heading TOC with click-to-scroll
52
- - Backlink context — shows the surrounding line
53
- - Unlinked mentions — finds references without `[[wikilinks]]`
54
- - Inline editing — edit notes directly in the browser (`Cmd+S`)
55
- - Local graph view — D3 force-directed graph of connections
56
- - Resizable panels, collapsible tree, rendered markdown
165
+ Opens at `localhost:3333` with two panels:
57
166
 
58
- ## How It Works
167
+ **Session Memory** semantic search across all sessions, grouped by project with timestamps
59
168
 
60
- ```
61
- ┌─────────────┐ ┌─────────────┐ ┌──────────────┐
62
- │ Claude Code │──stdio──▶│ kyp-mem │──read/──▶│ ~/.kyp-mem/ │
63
- │ (any AI) │◀─────────│ MCP server │ write │ vault/ │
64
- └─────────────┘ └─────────────┘ │ *.md files │
65
- └──────────────┘
66
- ```
169
+ **Knowledge Base** — file tree with folders, notes, tags. Full-text search, tag filtering, quick switcher (`Cmd+O`)
67
170
 
68
- - **Headless by default** — MCP server over stdio, no GUI needed
69
- - **Markdown on disk** — plain `.md` files with YAML frontmatter, no database
70
- - **In-memory index** — wikilinks, backlinks, tags, word-level search index
71
- - **Lightweight reads** — brief mode by default (~100 tokens), full content opt-in
72
- - **Graph navigation** — follow `[[links]]` instead of searching broadly
171
+ **Editor** — rendered markdown with `[[wikilink]]` navigation, inline editing
73
172
 
74
- ## Commands
173
+ **Graph** — knowledge graph showing connections between notes, backlinks, and related content
75
174
 
76
- | Command | What it does |
77
- |---------|-------------|
78
- | `kyp-mem init` | First-time setup — choose vault location |
79
- | `kyp-mem setup-claude` | Register MCP server with Claude Code |
80
- | `kyp-mem setup-claude --global` | Configure globally (all projects) |
81
- | `kyp-mem install-hooks` | Enable auto-learning from sessions |
82
- | `kyp-mem install-hooks --remove` | Remove auto-learning hooks |
83
- | `kyp-mem serve` | Start MCP server (used by Claude, not you) |
84
- | `kyp-mem ui` | Open web UI at localhost:3333 |
85
- | `kyp-mem stats` | Print vault statistics |
86
- | `kyp-mem tree` | Print vault tree |
87
- | `kyp-mem doctor` | Check installation health |
175
+ ---
176
+
177
+ ## MCP Tools (14 total)
178
+
179
+ ### Agent Behavior
180
+
181
+ | Tool | Purpose |
182
+ |------|---------|
183
+ | `____kyp_instructions` | Embeds behavioral rules the agent follows automatically |
184
+ | `kyp_project_context` | Loads project knowledge + recent sessions at session start |
88
185
 
89
- ## MCP Tools (9 tools)
186
+ ### Knowledge Base
90
187
 
91
- | Tool | Description |
92
- |------|-------------|
93
- | `kyp_list` | Browse folders and notes with inline tags |
94
- | `kyp_read` | Brief summary by default; `full=True` for complete content |
95
- | `kyp_write` | Create or update a note with tags and properties |
188
+ | Tool | Purpose |
189
+ |------|---------|
190
+ | `kyp_list` | Browse folders and notes |
191
+ | `kyp_read` | Read a note (summary by default, `full=True` for complete) |
192
+ | `kyp_write` | Create or update a note with tags and `[[wikilinks]]` |
96
193
  | `kyp_delete` | Delete a note |
97
194
  | `kyp_search` | Full-text search with optional tag filter |
98
195
  | `kyp_tags` | List all tags or filter notes by tag |
99
- | `kyp_related` | Find related notes by links, tags, folder proximity |
196
+ | `kyp_related` | Find related notes by links, tags, proximity |
100
197
  | `kyp_recent` | Recently modified notes |
101
198
  | `kyp_stats` | Vault statistics |
102
199
 
103
- ## Note Format
200
+ ### Session Memory
201
+
202
+ | Tool | Purpose |
203
+ |------|---------|
204
+ | `kyp_session_search` | Semantic search across all session logs |
205
+ | `kyp_session_create` | Manually create a session note |
206
+ | `kyp_sessions` | List sessions by project |
104
207
 
105
- ```markdown
106
- ---
107
- tags: [project, trading, config]
108
- created: 2026-05-12
109
208
  ---
110
209
 
111
- # Configuration
210
+ ## Adding to Your Project
112
211
 
113
- Settings are in `HedgeConfig`. See [[Risk Management]] for safety checks.
114
- ```
212
+ Add a `CLAUDE.md` to any project root:
115
213
 
116
- `[[Wikilinks]]` are parsed, indexed, and resolved into navigable backlinks automatically.
214
+ ```markdown
215
+ # Project Memory
216
+
217
+ ## Session Start (MANDATORY)
218
+ Call `kyp_project_context("PROJECT_NAME")` at the start of every session to load:
219
+ - Project knowledge base (architecture, bugs, decisions)
220
+ - Recent session history (what was done, what's next)
221
+
222
+ ## During Work
223
+ - Before investigating bugs: `kyp_session_search("error or symptom")`
224
+ - Before making decisions: `kyp_session_search("topic")`
225
+ - After fixing bugs: update Knowledge.md via `kyp_write`
226
+ - After decisions: add to Key Decisions in Knowledge.md
227
+
228
+ ## Rules
229
+ - Never hallucinate project details — check the knowledge base first.
230
+ - Use [[wikilinks]] to connect related notes.
231
+ - Sessions are captured automatically — no manual logging needed.
232
+ ```
117
233
 
118
- ## Manual Claude Code Config
234
+ A template is available at `templates/CLAUDE.md.template`.
119
235
 
120
- ```bash
121
- claude mcp add -s user -e KYP_VAULT="$HOME/.kyp-mem/vault" kyp-mem -- npx -y kyp-mem serve
122
- ```
236
+ ---
123
237
 
124
- ## Architecture
238
+ ## Vault Structure
125
239
 
126
240
  ```
127
241
  ~/.kyp-mem/
128
- ├── config.json # vault path
129
- ├── sessions/ # auto-learning session logs
242
+ ├── config.json # Vault path configuration
243
+ ├── chroma/ # Vector database for semantic search
130
244
  └── vault/
131
- ├── Project A/
132
- │ ├── Architecture.md
133
- └── Bugs.md
134
- ├── Sessions/ # auto-captured session notes
245
+ ├── ProjectA/
246
+ │ ├── Knowledge.md # Ground truth: architecture, bugs, decisions
247
+ ├── API.md
248
+ │ └── Sessions/
249
+ │ ├── 2026-05-12_143022.md
250
+ │ └── 2026-05-11_091544.md
251
+ ├── ProjectB/
252
+ │ ├── Knowledge.md
253
+ │ └── Sessions/
135
254
  └── ...
136
255
  ```
137
256
 
257
+ Notes use YAML frontmatter for tags and `[[wikilinks]]` for cross-references:
258
+
259
+ ```markdown
260
+ ---
261
+ tags: [trading, config]
262
+ created: 2026-05-12
263
+ ---
264
+ # Configuration
265
+ Settings are in `HedgeConfig`. See [[Risk Management]] for limits.
266
+ ```
267
+
268
+ ---
269
+
270
+ ## CLI Commands
271
+
272
+ | Command | What it does |
273
+ |---------|-------------|
274
+ | `kyp-mem init` | First-time setup — choose vault location |
275
+ | `kyp-mem setup-claude` | Register MCP server with Claude Code |
276
+ | `kyp-mem setup-claude --global` | Register globally (all projects) |
277
+ | `kyp-mem install-hooks` | Enable automatic session capture |
278
+ | `kyp-mem install-hooks --remove` | Remove session capture hooks |
279
+ | `kyp-mem serve` | Start MCP server (stdio, used by the agent) |
280
+ | `kyp-mem ui` | Open web UI at localhost:3333 |
281
+ | `kyp-mem stats` | Print vault statistics |
282
+ | `kyp-mem tree` | Print vault tree |
283
+ | `kyp-mem doctor` | Check installation health |
284
+
285
+ ---
286
+
138
287
  ## License
139
288
 
140
289
  MIT
@@ -1,3 +1,3 @@
1
1
  """KYP-MEM — Know Your Project Memory. Headless knowledge base for AI agents."""
2
2
 
3
- __version__ = "0.4.1"
3
+ __version__ = "0.4.2"
package/kyp_mem/cli.py CHANGED
@@ -43,6 +43,12 @@ def main():
43
43
 
44
44
  subparsers.add_parser("doctor", help="Check installation and config health")
45
45
 
46
+ hook_parser = subparsers.add_parser("hook", help="Handle Claude Code hook events (internal)")
47
+ hook_sub = hook_parser.add_subparsers(dest="hook_command")
48
+ hook_sub.add_parser("post-tool-use", help="Capture tool activity to session log")
49
+ hook_sub.add_parser("user-prompt", help="Capture user prompt to session log")
50
+ hook_sub.add_parser("stop", help="Compile session into vault note")
51
+
46
52
  args = parser.parse_args()
47
53
 
48
54
  if args.vault:
@@ -66,6 +72,14 @@ def main():
66
72
  _run_install_hooks(global_config=args.global_config, remove=args.remove)
67
73
  elif args.command == "doctor":
68
74
  _run_doctor()
75
+ elif args.command == "hook":
76
+ from .hooks import handle_post_tool_use, handle_user_prompt, handle_stop
77
+ if args.hook_command == "post-tool-use":
78
+ handle_post_tool_use()
79
+ elif args.hook_command == "user-prompt":
80
+ handle_user_prompt()
81
+ elif args.hook_command == "stop":
82
+ handle_stop()
69
83
  else:
70
84
  _print_banner()
71
85
  parser.print_help()
@@ -264,11 +278,17 @@ def _run_install_hooks(global_config: bool = False, remove: bool = False):
264
278
 
265
279
  hooks = settings.setdefault("hooks", {})
266
280
 
281
+ def _has_kyp_hook(entry):
282
+ for hook in entry.get("hooks", []):
283
+ if "kyp-mem hook" in hook.get("command", ""):
284
+ return True
285
+ return "kyp-mem hook" in entry.get("command", "")
286
+
267
287
  if remove:
268
288
  changed = False
269
- for event in ("PostToolUse", "Stop"):
289
+ for event in ("PostToolUse", "UserPromptSubmit", "Stop"):
270
290
  if event in hooks:
271
- hooks[event] = [h for h in hooks[event] if "kyp-mem hook" not in h.get("command", "")]
291
+ hooks[event] = [h for h in hooks[event] if not _has_kyp_hook(h)]
272
292
  if not hooks[event]:
273
293
  del hooks[event]
274
294
  changed = True
@@ -282,20 +302,26 @@ def _run_install_hooks(global_config: bool = False, remove: bool = False):
282
302
  return
283
303
 
284
304
  post_tool_hooks = hooks.setdefault("PostToolUse", [])
305
+ prompt_hooks = hooks.setdefault("UserPromptSubmit", [])
285
306
  stop_hooks = hooks.setdefault("Stop", [])
286
307
 
287
- post_tool_hooks = [h for h in post_tool_hooks if "kyp-mem hook" not in h.get("command", "")]
288
- stop_hooks = [h for h in stop_hooks if "kyp-mem hook" not in h.get("command", "")]
308
+ post_tool_hooks = [h for h in post_tool_hooks if not _has_kyp_hook(h)]
309
+ prompt_hooks = [h for h in prompt_hooks if not _has_kyp_hook(h)]
310
+ stop_hooks = [h for h in stop_hooks if not _has_kyp_hook(h)]
289
311
 
290
312
  post_tool_hooks.append({
291
313
  "matcher": "Edit|Write|Bash",
292
314
  "hooks": [{"type": "command", "command": f"{mcp_command} hook post-tool-use"}],
293
315
  })
316
+ prompt_hooks.append({
317
+ "hooks": [{"type": "command", "command": f"{mcp_command} hook user-prompt"}],
318
+ })
294
319
  stop_hooks.append({
295
320
  "hooks": [{"type": "command", "command": f"{mcp_command} hook stop"}],
296
321
  })
297
322
 
298
323
  hooks["PostToolUse"] = post_tool_hooks
324
+ hooks["UserPromptSubmit"] = prompt_hooks
299
325
  hooks["Stop"] = stop_hooks
300
326
 
301
327
  settings_path.write_text(json.dumps(settings, indent=2) + "\n")
package/kyp_mem/hooks.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """KYP-MEM session hooks — compile captured tool activity into vault notes."""
2
2
 
3
+ import os
3
4
  import sys
4
5
  import json
5
6
  from datetime import datetime
@@ -11,6 +12,65 @@ CURRENT_SESSION = SESSION_DIR / "current.jsonl"
11
12
  MIN_ACTIONS = 3
12
13
 
13
14
 
15
+ def handle_user_prompt():
16
+ raw = sys.stdin.read().strip()
17
+ if not raw:
18
+ return
19
+ try:
20
+ data = json.loads(raw)
21
+ except json.JSONDecodeError:
22
+ return
23
+
24
+ prompt = data.get("prompt", "").strip()
25
+ if not prompt:
26
+ return
27
+
28
+ entry = {
29
+ "ts": datetime.now().isoformat(),
30
+ "cwd": os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()),
31
+ "action": "prompt",
32
+ "prompt": prompt,
33
+ }
34
+
35
+ SESSION_DIR.mkdir(parents=True, exist_ok=True)
36
+ with open(CURRENT_SESSION, "a") as f:
37
+ f.write(json.dumps(entry) + "\n")
38
+
39
+
40
+ def handle_post_tool_use():
41
+ raw = sys.stdin.read().strip()
42
+ if not raw:
43
+ return
44
+ try:
45
+ data = json.loads(raw)
46
+ except json.JSONDecodeError:
47
+ return
48
+
49
+ tool_name = data.get("tool_name", "")
50
+ tool_input = data.get("tool_input", {})
51
+
52
+ entry = {"ts": datetime.now().isoformat()}
53
+
54
+ cwd = os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd())
55
+ entry["cwd"] = cwd
56
+
57
+ if tool_name == "Edit":
58
+ entry["action"] = "edit"
59
+ entry["file"] = tool_input.get("file_path", "")
60
+ elif tool_name == "Write":
61
+ entry["action"] = "create"
62
+ entry["file"] = tool_input.get("file_path", "")
63
+ elif tool_name == "Bash":
64
+ entry["action"] = "command"
65
+ entry["command"] = tool_input.get("command", "")
66
+ else:
67
+ return
68
+
69
+ SESSION_DIR.mkdir(parents=True, exist_ok=True)
70
+ with open(CURRENT_SESSION, "a") as f:
71
+ f.write(json.dumps(entry) + "\n")
72
+
73
+
14
74
  def handle_stop():
15
75
  if not CURRENT_SESSION.exists():
16
76
  return
@@ -43,6 +103,7 @@ def handle_stop():
43
103
  files_edited = set()
44
104
  files_created = set()
45
105
  commands = []
106
+ prompts = []
46
107
  timeline = []
47
108
 
48
109
  for e in entries:
@@ -50,7 +111,10 @@ def handle_stop():
50
111
  ts = ts_raw[11:19] if len(ts_raw) >= 19 else ""
51
112
  action = e.get("action", "")
52
113
 
53
- if action == "edit":
114
+ if action == "prompt":
115
+ prompts.append({"ts": ts, "text": e.get("prompt", "")})
116
+ timeline.append(f" {ts} — Prompt: {e.get('prompt', '')[:60]}...")
117
+ elif action == "edit":
54
118
  fp = e.get("file", "")
55
119
  files_edited.add(fp)
56
120
  timeline.append(f" {ts} — Edit `{Path(fp).name}`")
@@ -88,6 +152,14 @@ def handle_stop():
88
152
  parts.append(", ".join(summary_items) + f" in `{project_name}`." if summary_items else "")
89
153
  parts.append("")
90
154
 
155
+ parts.append("## PROMPTS")
156
+ if prompts:
157
+ for i, p in enumerate(prompts, 1):
158
+ parts.append(f"### {i}. [{p['ts']}]")
159
+ parts.append(f"> {p['text']}")
160
+ parts.append("")
161
+ parts.append("")
162
+
91
163
  parts.append("## INVESTIGATED")
92
164
  if investigated_cmds:
93
165
  for cmd in investigated_cmds[:15]:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kyp-mem",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Know Your Project — Persistent knowledge base for AI agents. MCP-powered with wikilinks, backlinks, auto-learning, and neon web UI.",
5
5
  "bin": {
6
6
  "kyp-mem": "bin/cli.mjs"
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kyp-mem"
7
- version = "0.4.1"
7
+ version = "0.4.2"
8
8
  description = "Know Your Project — Persistent knowledge base for AI agents. MCP-powered with wikilinks, backlinks, auto-learning, and neon web UI."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}