arkaos 2.19.0 → 2.19.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
@@ -161,7 +161,7 @@ ArkaOS doesn't just execute — it **learns, dreams, and researches**.
161
161
  Every solution you implement is captured and indexed. When you need authentication in a new Laravel project, ArkaOS already knows how you did it in the last three projects — with the exact pattern, configuration, and lessons learned.
162
162
 
163
163
  - **Dual-write**: Obsidian (human-readable) + Vector DB (semantic search)
164
- - **Cross-project**: Knowledge from Rockport applies to ClubeFashion
164
+ - **Cross-project**: Knowledge from ClientRetail applies to ClientFashion
165
165
  - **Confidence scoring**: Patterns validated 3+ times become "validated patterns"
166
166
 
167
167
  ### Dreaming (runs at 02:00)
@@ -185,7 +185,7 @@ Pending reflections from Dreaming:
185
185
 
186
186
  2. [technical] Sync retry — improve
187
187
  Fixed backoff can cause thundering herd. Use exponential
188
- backoff with jitter (validated pattern from Rockport).
188
+ backoff with jitter (validated pattern from ClientRetail).
189
189
 
190
190
  Want me to elaborate?
191
191
  ```
@@ -204,11 +204,11 @@ Intelligence Briefing — 2026-04-10
204
204
 
205
205
  ACTION REQUIRED:
206
206
  - Laravel 12.1.3 security patch — SQL injection in whereHas.
207
- Affects: ClubeFashion, Fovory. Fix: composer update laravel/framework.
207
+ Affects: ClientFashion, ClientCommerce. Fix: composer update laravel/framework.
208
208
 
209
209
  OPPORTUNITIES:
210
- - Shopify Winter '26 bulk product API — Fovory sync could be 10x faster.
211
- - Nuxt 4 RC2 migration guide published — start preparing ZugaTV.
210
+ - Shopify Winter '26 bulk product API — ClientCommerce sync could be 10x faster.
211
+ - Nuxt 4 RC2 migration guide published — start preparing ClientVideo.
212
212
 
213
213
  COMPETITOR WATCH:
214
214
  - CrewAI v3 launched memory layer — similar to our Cognitive Layer
@@ -233,9 +233,9 @@ arkaos scheduler logs # View logs
233
233
  ArkaOS manages client projects as **ecosystems** — groups of related projects with dedicated squads.
234
234
 
235
235
  ```
236
- /rockportRockport ecosystem (4 projects: API, frontend, admin, docs)
237
- /fovoryFovory ecosystem (supplier sync + Shopify theme)
238
- /clubefashionClubeFashion (6 projects: CRM, store, API, migration...)
236
+ /client_retailClientRetail ecosystem (4 projects: API, frontend, admin, docs)
237
+ /client_commerceClientCommerce ecosystem (supplier sync + Shopify theme)
238
+ /client_fashionClientFashion (6 projects: CRM, store, API, migration...)
239
239
  /edp → EDP (3 projects: portal, API, analytics)
240
240
  ```
241
241
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.19.0
1
+ 2.19.2
package/arka/SKILL.md CHANGED
@@ -12,6 +12,50 @@ allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Agent, WebFetch, WebSearch]
12
12
  > **The Operating System for AI Agent Teams**
13
13
  > 65 agents. 17 departments. 244+ skills. Multi-runtime. Dashboard. Knowledge RAG.
14
14
 
15
+ ## ⛔ Enforcement contract (read before responding)
16
+
17
+ This overrides every default. If the UserPromptSubmit hook injected a
18
+ `[ARKA:WORKFLOW-REQUIRED]` tag, you MUST, on the first line of your reply:
19
+
20
+ ```
21
+ [arka:routing] <department-slug> -> <lead-agent>
22
+ ```
23
+
24
+ Example first lines (pick the right department for the ask):
25
+
26
+ - `[arka:routing] dev -> Paulo` — code, features, refactors, tests
27
+ - `[arka:routing] brand -> Valentina` — identity, design, logos, voice
28
+ - `[arka:routing] kb -> Clara` — knowledge base, research, Obsidian
29
+ - `[arka:routing] mkt -> Luna` — marketing, growth, SEO, campaigns
30
+ - `[arka:routing] content -> Rafael` — content, video, social, copy
31
+ - `[arka:routing] landing -> Ines` — landing pages, funnels, offers
32
+ - `[arka:routing] ecom -> Ricardo` — e-commerce, stores, conversion
33
+ - `[arka:routing] saas -> Tiago` — SaaS, validation, PLG, metrics
34
+ - `[arka:routing] sales -> Miguel` — pipeline, discovery, negotiation
35
+ - `[arka:routing] pm -> Carolina` — roadmap, sprints, backlog, stories
36
+ - `[arka:routing] ops -> Daniel` — automation, SOPs, workflows
37
+ - `[arka:routing] strat -> Tomas` — strategy, positioning, moats
38
+ - `[arka:routing] fin -> Helena` — finance, modeling, budgets
39
+ - `[arka:routing] lead -> Rodrigo` — team health, feedback, hiring
40
+ - `[arka:routing] org -> Sofia` — org design, COO, operations
41
+ - `[arka:routing] community -> Beatriz` — communities, platforms, retention
42
+
43
+ After the routing line, in order:
44
+
45
+ 1. State the workflow name and its phase count.
46
+ 2. Run Phase 1 (usually spec via `arka-spec` or plan via `arka-forge`) BEFORE writing any code.
47
+ 3. Execute sequential phases with visibility (one at a time, report status).
48
+ 4. Run the Quality Gate (Marta CQO + Eduardo Copy + Francisca Tech, model Opus) BEFORE marking done.
49
+
50
+ The only exception is a trivial 1-file edit under 10 lines. In that case emit:
51
+
52
+ ```
53
+ [arka:trivial] <one-sentence reason>
54
+ ```
55
+
56
+ and proceed directly. Anything else without a routing line is a constitution
57
+ violation (squad-routing, arka-supremacy, spec-driven, mandatory-qa).
58
+
15
59
  ## System Commands
16
60
 
17
61
  | Command | Description |
@@ -70,7 +70,7 @@ vector = VectorWriter(os.path.expanduser("~/.arkaos/knowledge.db"))
70
70
 
71
71
  - Compare today's errors with past errors — if same error type appears > 2 times, create Anti-Pattern entry
72
72
  - Compare today's solutions with past solutions — if same pattern appears > 2 times, promote to Validated Pattern
73
- - Detect inconsistencies between projects ("In Rockport used X, in ClubeFashion used Y for same problem")
73
+ - Detect inconsistencies between projects ("In ClientRetail used X, in ClientFashion used Y for same problem")
74
74
 
75
75
  ## Phase 4: Curation and Consolidation
76
76
 
@@ -123,7 +123,7 @@ quality_score: 75
123
123
  entries_created: 4
124
124
  entries_updated: 2
125
125
  insights_generated: 3
126
- projects_active: [fovory, rockport, clubefashion]
126
+ projects_active: [client_commerce, client_retail, client_fashion]
127
127
  ---
128
128
 
129
129
  # Dreaming Report — YYYY-MM-DD
@@ -199,7 +199,7 @@ Write structured metrics to `~/.arkaos/logs/dreaming/YYYY-MM-DD.json`:
199
199
  "entries_updated": 2,
200
200
  "insights_generated": 3,
201
201
  "captures_processed": 15,
202
- "projects_reviewed": ["fovory", "rockport"]
202
+ "projects_reviewed": ["client_commerce", "client_retail"]
203
203
  }
204
204
  ```
205
205
 
@@ -275,8 +275,41 @@ NO generic assistant replies. Announce the squad before responding.
275
275
  When [knowledge:N chunks] is present, cite at least one source.
276
276
  If [knowledge:N chunks] is absent on a non-trivial ArkaOS topic, query Obsidian first."
277
277
 
278
+ # ─── Workflow Classifier (hard enforcement for creation/implementation) ──
279
+ # Classifies the user prompt. If it looks like a creation/implementation/
280
+ # modification request that is NOT already routed with an explicit /prefix,
281
+ # emits a directive that the agent MUST acknowledge with [arka:routing]
282
+ # BEFORE using any write tool. Trivial quick questions pass through
283
+ # untouched. Explicit slash commands pass through untouched.
284
+ _WORKFLOW_DIRECTIVE=""
285
+ if [ -n "$user_input" ]; then
286
+ # Skip: explicit slash command (already routed)
287
+ _FIRST_CHAR=$(echo "$user_input" | head -c 1)
288
+ if [ "$_FIRST_CHAR" != "/" ] && [ "$_FIRST_CHAR" != "!" ]; then
289
+ # Match creation/implementation verbs in EN and PT (case-insensitive).
290
+ _VERB_PATTERN='(criar?|crie[ms]?|cria[mr]?|adicionar?|adiciona[mr]?|implementar?|implementa[mr]?|desenvolver?|desenvolve[mr]?|construir?|constru[ií]a?[mr]?|fazer?|faz[ae][mr]?|refactor(izar?)?|corrigir?|corrige[mr]?|consertar?|conserta[mr]?|create[sd]?|creating|build(s|ing)?|add(s|ed|ing)?|implement(s|ed|ing)?|develop(s|ed|ing)?|fix(es|ed|ing)?|refactor(s|ed|ing)?|make[sd]?|making)'
291
+ _NOUN_PATTERN='(feature|funcionalidade|skill|squad|agent[e]?|workflow|endpoint|api|component[e]?|module|m[oó]dulo|page|p[aá]gina|hook|pipeline|integration|integra[cç][aã]o|dashboard|report|report[eó]|script|test[es]?)'
292
+ if echo "$user_input" | grep -qiE "\b${_VERB_PATTERN}\b"; then
293
+ _WORKFLOW_DIRECTIVE="
294
+ [ARKA:WORKFLOW-REQUIRED] Your user request matched a CREATION/IMPLEMENTATION pattern.
295
+ You MUST, before using any Write, Edit, Bash with side-effects, or Agent tool:
296
+ 1. Output on the first line: [arka:routing] <department-slug> -> <lead-agent>
297
+ (e.g. [arka:routing] dev -> Paulo, [arka:routing] brand -> Valentina,
298
+ [arka:routing] kb -> Clara, [arka:routing] mkt -> Luna)
299
+ 2. State the workflow name and phase count in one short sentence.
300
+ 3. Begin phase 1 (spec or plan) BEFORE any code is written.
301
+ 4. Run the Quality Gate (Marta + Eduardo + Francisca, Opus) before claiming done.
302
+ Trivial override: if the request is a single-file edit under 10 lines AND the user
303
+ used an imperative like 'rename X', 'fix typo', you MAY emit [arka:trivial] <reason>
304
+ and proceed directly. Anything else requires routing. This is enforced, not advisory.
305
+ Skipping routing violates constitution rules squad-routing, arka-supremacy,
306
+ spec-driven, mandatory-qa, sequential-validation."
307
+ fi
308
+ fi
309
+ fi
310
+
278
311
  # ─── Output ──────────────────────────────────────────────────────────────
279
- _OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER} $python_result"
312
+ _OUT_CONTEXT="${_ARKA_GREETING:-}${_SYNC_NOTICE:-}${_ROUTE_REMINDER}${_WORKFLOW_DIRECTIVE} $python_result"
280
313
  [ -n "$_HYGIENE" ] && _OUT_CONTEXT="$_OUT_CONTEXT $_HYGIENE"
281
314
  # Escape for JSON
282
315
  _OUT_JSON=$(python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" <<< "$_OUT_CONTEXT" 2>/dev/null)
@@ -184,14 +184,14 @@ def _discover_projects(arkaos_home: Path, skills_dir: Path) -> list:
184
184
  """
185
185
  del skills_dir # retained for signature stability; unused.
186
186
 
187
- descriptor_dir = resolve_projects_dir()
188
- ecosystems_path = resolve_ecosystems_file()
189
-
190
- # discover_all_projects treats missing paths as empty; pass a stable
191
- # sentinel when the resolver returned None so downstream .exists()
192
- # checks short-circuit cleanly.
193
- descriptor_dir = descriptor_dir or (Path.home() / ".arkaos" / "projects")
194
- ecosystems_path = ecosystems_path or (Path.home() / ".arkaos" / "ecosystems.json")
187
+ # resolve_*() returns None when neither the new nor legacy path exists.
188
+ # `discover_all_projects` requires concrete Path objects but calls
189
+ # `.exists()` internally, so substituting the (non-existent) canonical
190
+ # path keeps the contract: missing returns an empty project list.
191
+ descriptor_dir = resolve_projects_dir() or (Path.home() / ".arkaos" / "projects")
192
+ ecosystems_path = resolve_ecosystems_file() or (
193
+ Path.home() / ".arkaos" / "ecosystems.json"
194
+ )
195
195
 
196
196
  scan_dirs = _load_scan_dirs_from_profile(arkaos_home)
197
197
 
@@ -105,19 +105,20 @@ export default {
105
105
  ];
106
106
 
107
107
  // Statusline — ArkaOS branded status bar
108
+ // Claude Code reads the camelCase "statusLine" key; the lowercase
109
+ // "statusline" variant is silently ignored.
108
110
  const configDir = join(installDir, "config");
109
111
  const statuslineFile = IS_WINDOWS ? "statusline.ps1" : "statusline.sh";
110
112
  const statuslinePath = join(configDir, statuslineFile);
111
113
  if (existsSync(statuslinePath)) {
112
- if (IS_WINDOWS) {
113
- settings.statusline = {
114
- command: `powershell -NoProfile -ExecutionPolicy Bypass -File "${statuslinePath}"`,
115
- };
116
- } else {
117
- settings.statusline = {
118
- command: statuslinePath,
119
- };
120
- }
114
+ const command = IS_WINDOWS
115
+ ? `powershell -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "${statuslinePath}"`
116
+ : statuslinePath;
117
+ settings.statusLine = {
118
+ type: "command",
119
+ command,
120
+ padding: 2,
121
+ };
121
122
  }
122
123
 
123
124
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
@@ -259,6 +259,27 @@ export async function install({ runtime, path, force }) {
259
259
  };
260
260
  writeFileSync(join(installDir, "install-manifest.json"), JSON.stringify(manifest, null, 2));
261
261
 
262
+ // Seed sync-state.json so session-start.sh does not read a missing file as
263
+ // version drift and permanently show [arka:update-available] on a fresh
264
+ // install. Schema aligned with core/sync/reporter.py write_sync_state.
265
+ const syncStatePath = join(installDir, "sync-state.json");
266
+ if (!existsSync(syncStatePath)) {
267
+ writeFileSync(
268
+ syncStatePath,
269
+ JSON.stringify(
270
+ {
271
+ version: VERSION,
272
+ last_sync: new Date().toISOString(),
273
+ projects_synced: 0,
274
+ skills_synced: 0,
275
+ errors: [],
276
+ },
277
+ null,
278
+ 2,
279
+ ),
280
+ );
281
+ }
282
+
262
283
  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
263
284
 
264
285
  console.log(`
@@ -19,81 +19,16 @@ const LOGS_DIR = join(USER_DATA_ROOT, "logs");
19
19
  * @returns {{ moved: string[], skipped: string[], conflicts: string[], logPath: string|null }}
20
20
  */
21
21
  export function migrateUserData({ dryRun = false } = {}) {
22
- const moved = [];
23
- const skipped = [];
24
- const conflicts = [];
22
+ ensureDataRoot();
25
23
 
26
- if (!existsSync(USER_DATA_ROOT)) {
27
- mkdirSync(USER_DATA_ROOT, { recursive: true });
28
- }
29
- if (!existsSync(NEW_PROJECTS)) {
30
- mkdirSync(NEW_PROJECTS, { recursive: true });
31
- }
24
+ const projectsResult = migrateProjects({ dryRun });
25
+ const ecosystemsResult = migrateEcosystems({ dryRun });
32
26
 
33
- if (existsSync(LEGACY_PROJECTS) && statSync(LEGACY_PROJECTS).isDirectory()) {
34
- for (const entry of readdirSync(LEGACY_PROJECTS)) {
35
- const src = join(LEGACY_PROJECTS, entry);
36
- const dst = join(NEW_PROJECTS, entry);
37
- if (existsSync(dst)) {
38
- conflicts.push(`projects/${entry}: destination already present, left source untouched`);
39
- continue;
40
- }
41
- if (dryRun) {
42
- moved.push(`projects/${entry} (dry-run)`);
43
- } else {
44
- try {
45
- renameSync(src, dst);
46
- moved.push(`projects/${entry}`);
47
- } catch (err) {
48
- conflicts.push(`projects/${entry}: ${err.message}`);
49
- }
50
- }
51
- }
52
- } else {
53
- skipped.push("projects/: legacy directory absent");
54
- }
55
-
56
- if (existsSync(LEGACY_ECOSYSTEMS)) {
57
- if (existsSync(NEW_ECOSYSTEMS)) {
58
- conflicts.push("ecosystems.json: destination already present, left source untouched");
59
- } else if (dryRun) {
60
- moved.push("ecosystems.json (dry-run)");
61
- } else {
62
- try {
63
- renameSync(LEGACY_ECOSYSTEMS, NEW_ECOSYSTEMS);
64
- moved.push("ecosystems.json");
65
- } catch (err) {
66
- conflicts.push(`ecosystems.json: ${err.message}`);
67
- }
68
- }
69
- } else {
70
- skipped.push("ecosystems.json: legacy file absent");
71
- }
72
-
73
- let logPath = null;
74
- if (moved.length > 0 || conflicts.length > 0) {
75
- if (!existsSync(LOGS_DIR)) mkdirSync(LOGS_DIR, { recursive: true });
76
- const stamp = new Date().toISOString().replace(/[:.]/g, "-");
77
- logPath = join(LOGS_DIR, `migration-${stamp}.log`);
78
- const body = [
79
- `ArkaOS user-data migration — ${new Date().toISOString()}`,
80
- `Source: ${LEGACY_SKILLS_ROOT}`,
81
- `Destination: ${USER_DATA_ROOT}`,
82
- `Dry-run: ${dryRun}`,
83
- "",
84
- `Moved (${moved.length}):`,
85
- ...moved.map(m => ` - ${m}`),
86
- "",
87
- `Conflicts (${conflicts.length}):`,
88
- ...conflicts.map(c => ` - ${c}`),
89
- "",
90
- `Skipped (${skipped.length}):`,
91
- ...skipped.map(s => ` - ${s}`),
92
- "",
93
- ].join("\n");
94
- if (!dryRun) writeFileSync(logPath, body);
95
- }
27
+ const moved = [...projectsResult.moved, ...ecosystemsResult.moved];
28
+ const skipped = [...projectsResult.skipped, ...ecosystemsResult.skipped];
29
+ const conflicts = [...projectsResult.conflicts, ...ecosystemsResult.conflicts];
96
30
 
31
+ const logPath = writeReport({ moved, skipped, conflicts, dryRun });
97
32
  return { moved, skipped, conflicts, logPath };
98
33
  }
99
34
 
@@ -108,3 +43,90 @@ export function printMigrationReport(result) {
108
43
  for (const c of conflicts) console.log(` ! ${c}`);
109
44
  if (logPath) console.log(` Log: ${logPath}`);
110
45
  }
46
+
47
+ function ensureDataRoot() {
48
+ if (!existsSync(USER_DATA_ROOT)) mkdirSync(USER_DATA_ROOT, { recursive: true });
49
+ if (!existsSync(NEW_PROJECTS)) mkdirSync(NEW_PROJECTS, { recursive: true });
50
+ }
51
+
52
+ function migrateProjects({ dryRun }) {
53
+ const moved = [];
54
+ const skipped = [];
55
+ const conflicts = [];
56
+
57
+ if (!existsSync(LEGACY_PROJECTS) || !statSync(LEGACY_PROJECTS).isDirectory()) {
58
+ skipped.push("projects/: legacy directory absent");
59
+ return { moved, skipped, conflicts };
60
+ }
61
+
62
+ for (const entry of readdirSync(LEGACY_PROJECTS)) {
63
+ const src = join(LEGACY_PROJECTS, entry);
64
+ const dst = join(NEW_PROJECTS, entry);
65
+ if (existsSync(dst)) {
66
+ conflicts.push(`projects/${entry}: destination already present, left source untouched`);
67
+ continue;
68
+ }
69
+ if (dryRun) {
70
+ moved.push(`projects/${entry} (dry-run)`);
71
+ continue;
72
+ }
73
+ try {
74
+ renameSync(src, dst);
75
+ moved.push(`projects/${entry}`);
76
+ } catch (err) {
77
+ conflicts.push(`projects/${entry}: ${err.message}`);
78
+ }
79
+ }
80
+ return { moved, skipped, conflicts };
81
+ }
82
+
83
+ function migrateEcosystems({ dryRun }) {
84
+ const moved = [];
85
+ const skipped = [];
86
+ const conflicts = [];
87
+
88
+ if (!existsSync(LEGACY_ECOSYSTEMS)) {
89
+ skipped.push("ecosystems.json: legacy file absent");
90
+ return { moved, skipped, conflicts };
91
+ }
92
+ if (existsSync(NEW_ECOSYSTEMS)) {
93
+ conflicts.push("ecosystems.json: destination already present, left source untouched");
94
+ return { moved, skipped, conflicts };
95
+ }
96
+ if (dryRun) {
97
+ moved.push("ecosystems.json (dry-run)");
98
+ return { moved, skipped, conflicts };
99
+ }
100
+ try {
101
+ renameSync(LEGACY_ECOSYSTEMS, NEW_ECOSYSTEMS);
102
+ moved.push("ecosystems.json");
103
+ } catch (err) {
104
+ conflicts.push(`ecosystems.json: ${err.message}`);
105
+ }
106
+ return { moved, skipped, conflicts };
107
+ }
108
+
109
+ function writeReport({ moved, skipped, conflicts, dryRun }) {
110
+ if (moved.length === 0 && conflicts.length === 0) return null;
111
+ if (!existsSync(LOGS_DIR)) mkdirSync(LOGS_DIR, { recursive: true });
112
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
113
+ const logPath = join(LOGS_DIR, `migration-${stamp}.log`);
114
+ const body = [
115
+ `ArkaOS user-data migration — ${new Date().toISOString()}`,
116
+ `Source: ${LEGACY_SKILLS_ROOT}`,
117
+ `Destination: ${USER_DATA_ROOT}`,
118
+ `Dry-run: ${dryRun}`,
119
+ "",
120
+ `Moved (${moved.length}):`,
121
+ ...moved.map(m => ` - ${m}`),
122
+ "",
123
+ `Conflicts (${conflicts.length}):`,
124
+ ...conflicts.map(c => ` - ${c}`),
125
+ "",
126
+ `Skipped (${skipped.length}):`,
127
+ ...skipped.map(s => ` - ${s}`),
128
+ "",
129
+ ].join("\n");
130
+ if (!dryRun) writeFileSync(logPath, body);
131
+ return logPath;
132
+ }
@@ -267,7 +267,7 @@ export async function update() {
267
267
  // main `/arka` skill, so any department skill (arka-dev, arka-brand,
268
268
  // etc.) or sub-skill (arka-code-review, arka-viral, etc.) or agent
269
269
  // persona added after the original install was silently missing on
270
- // upgrade. Discovered during Marlon's bake-in: 233 top-level arka-*
270
+ // upgrade. Discovered during ClientAdvisory's bake-in: 233 top-level arka-*
271
271
  // skills on his WSL (deployed long ago by install.sh) vs 1 skill on
272
272
  // his Windows install (only the main /arka). The Node installer
273
273
  // never deployed anything else.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkaos",
3
- "version": "2.19.0",
3
+ "version": "2.19.2",
4
4
  "description": "The Operating System for AI Agent Teams",
5
5
  "type": "module",
6
6
  "bin": {
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "arkaos-core"
3
- version = "2.19.0"
3
+ version = "2.19.2"
4
4
  description = "Core engine for ArkaOS — The Operating System for AI Agent Teams"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}