@happyvertical/smrt-dev-mcp 0.40.37 → 0.40.39

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/AGENTS.md CHANGED
@@ -8,7 +8,7 @@ deterministic SMRT ecosystem knowledge, and portable agent workflows.
8
8
  | Tool | Purpose |
9
9
  |------|---------|
10
10
  | `generate-smrt-class` | Generates `@smrt()` class with fields, decorator config, imports |
11
- | `introspect-project` | Scans project directory for SMRT objects, returns class/field/relationship report |
11
+ | `introspect-project` | Scans project directory for SMRT objects; compact summary by default, `detail: 'full'` for field/schema/method detail |
12
12
  | `review-smrt-project` | Advisory downstream ecosystem alignment review for dependencies, storage, app shell, auth/tenancy, and manifest generation |
13
13
  | `reflect-knowledge` | Reports deterministic SMRT + HappyVertical SDK knowledge coverage and freshness |
14
14
  | `reflect-domain-knowledge` | Reports downstream/domain artifact coverage and freshness |
@@ -23,6 +23,46 @@ deterministic SMRT ecosystem knowledge, and portable agent workflows.
23
23
  | `list-agent-skills` | Lists bundled harness-agnostic agent skills |
24
24
  | `get-agent-skill` | Returns a bundled agent skill as Markdown plus optional references |
25
25
 
26
+ ## Knowledge Discovery (#2143)
27
+
28
+ Discovery reads the workspace globs instead of assuming `<root>/packages`:
29
+ `pnpm-workspace.yaml` `packages:` (literals, `dir/*`, `**`, `!` negations), then
30
+ `package.json#workspaces`, then a `packages/*` fallback. `findProjectRoot` looks
31
+ for a workspace declaration only — requiring a literal `packages/` directory is
32
+ what mis-rooted every `apps/*` product. The workspace root is indexed whenever it
33
+ has a `package.json`, which is how single-package repos resolve; it is scanned
34
+ with the member package directories excluded so it can own objects without
35
+ absorbing theirs.
36
+
37
+ Workspace globs are a filesystem trust boundary. Absolute paths and `..`
38
+ segments are rejected, and every matched directory is realpath-confined to the
39
+ workspace root before its manifest is read. All positive globs share a 10,000
40
+ directory-entry traversal budget; exceeding it is a fatal diagnostic that
41
+ returns no partially discovered package set. This cardinality limit preserves
42
+ valid deeply nested `**` workspaces without allowing repeated globstars to
43
+ amplify into unbounded filesystem work. Discovery also stops before package
44
+ reads when more than 512 packages match, revalidates confinement immediately
45
+ before reading each package, and runs at most eight scanner fallbacks at once.
46
+
47
+ Per package, objects resolve in this order, and the winner is recorded in
48
+ `objectSource` with a reason:
49
+
50
+ 1. `domain-artifact` — `.smrt/`, `dist/`, or `src/manifest/smrt-knowledge.json`
51
+ 2. `manifest` — package-local manifest, after ownership validation
52
+ 3. `scanner` — `OxcScanner` + `ManifestAdapter` over the package's sources
53
+ 4. `none` — with a machine-readable reason
54
+
55
+ The index exposes `coverage` and `diagnostics` (`schemaVersion: 2`). Zero
56
+ discovered objects is an **error-grade** diagnostic that names the roots and
57
+ artifact paths checked plus the fix, and it propagates into
58
+ `smrt-architecture`/`smrt-review`/`reflect-*` results and prompt bundles.
59
+
60
+ A workspace-root package has an empty `relativeDirectory`, so package paths are
61
+ built with `packageRelativePath`/`packageDocPaths` rather than interpolation —
62
+ otherwise emitted docs read `/AGENTS.md` and changed-file matching compares
63
+ against a leading `/` and selects nothing. The root owns any changed path no
64
+ member package owns, mirroring the member-excluded scan.
65
+
26
66
  ## Agent Skills
27
67
 
28
68
  Bundled skills live under `agent-skills/`. They are plain Markdown procedures
@@ -55,7 +95,7 @@ launcher or a small wrapper script with an absolute Node path.
55
95
  ## Key Files
56
96
 
57
97
  - `src/index.ts` — MCP server setup, tool registration
58
- - `src/knowledge/index.ts` — deterministic SMRT, SDK, and downstream domain knowledge discovery
98
+ - `src/knowledge/index.ts` — deterministic SMRT, SDK, and downstream domain knowledge discovery; workspace-glob expansion, provenance, coverage, and diagnostics
59
99
  - `src/agent-skills.ts` — bundled skill registry and file loader
60
100
  - `agent-skills/smrt-code-review/SKILL.md` — downstream SMRT review procedure
61
101
  - `src/tools/generate-smrt-class.ts` — class generation logic and package-ready templates
@@ -73,3 +113,50 @@ launcher or a small wrapper script with an absolute Node path.
73
113
  - **Skill loading**: bundled skills must be included in `package.json` `files`
74
114
  because runtime reads them from the installed package directory
75
115
  - **Field type mapping**: `text`, `integer`, `decimal`, `boolean`, `datetime`, `json` — maps to SMRT field helpers (but prefer TypeScript defaults per framework convention)
116
+ - **Never call `ManifestGenerator` from a tool path**: `generateSchemas` and
117
+ friends write progress lines to **stdout** through the SDK logger, which is the
118
+ MCP server's JSON-RPC channel. Suppressing `console.log` is not enough. The
119
+ knowledge scanner fallback therefore skips schema enrichment, so
120
+ scanner-provenance packages carry no `columnType` and contribute 0 to the
121
+ `uuidColumns` fact — `objectSource` makes that visible.
122
+ - **Aggregate manifests poison relationship facts**: a runtime
123
+ `.smrt/manifest.json` frequently registers a package's dependencies too.
124
+ Objects whose `packageName`/`qualifiedName` disagree with the owning package are
125
+ rejected with a diagnostic. A *consuming* package's artifact can also
126
+ re-qualify its dependency's objects under its own name, which prefixes cannot
127
+ detect. `summarizeRelationshipsV2` therefore collapses a shared
128
+ `className::tableName` **only across a dependency edge** (a package can only
129
+ restate its own dependencies' objects) and reports
130
+ `duplicate-object-identity`. Two unrelated packages that happen to share a
131
+ class and table name — `Account::accounts` exists in both smrt-messages and
132
+ smrt-ledgers — are distinct objects and both keep contributing.
133
+ - Grouping is by **connected component**, not a greedy pairwise match: two
134
+ independent consumers restating one shared dependency have no edge to each
135
+ other, so a greedy pass would keep both and double-count.
136
+ - The surviving copy is the one the others **depend on**, since ownership
137
+ follows the dependency direction. Provenance is not a sufficient signal —
138
+ preferring a `scanner` copy kept `smrt-support`'s compatibility subtype over
139
+ the canonical `smrt-projects` model and changed the facts.
140
+ - **Budgets are enforced at the transport boundary**: `KnowledgePackage` carries
141
+ the full `agentDoc` and domain manifest, so the MCP handlers project a compact
142
+ package record unless `detail: 'full'`. Library callers (the CLI) still receive
143
+ the full objects.
144
+ - **Private packages skip packaging checks**: the `files` allowlist governs npm
145
+ publication, so `private: true` packages are exempt; authored docs are still
146
+ required. A workspace root is exempt from both — but **only when member
147
+ packages exist**. In a single-package repo the root is the published package,
148
+ so exempting every root would make the freshness gate a no-op for exactly the
149
+ layout #2143 added support for.
150
+ - **Nested workspace members own no canonical docs**: instruction chains are
151
+ additive, so a package nested inside another workspace package (
152
+ `packages/smrt-playground/host`) must not carry `AGENTS.md`/`CLAUDE.md` — an
153
+ agent would load the parent's and its own. Those two checks are skipped for it
154
+ and a `nested-agents-md` error fires if one reappears; the expertise belongs in
155
+ the parent's linked `agents/<module>.md`.
156
+ - **`maxChars` budgets the objects payload, not the whole response**: project
157
+ metadata and diagnostics are always returned in full, because a diagnostic is
158
+ how discovery reports that it found nothing (#2143). Budgeting it away would
159
+ hide the failure the caller needs to see.
160
+ - **Coverage and diagnostics are computed before scope filtering**: they answer
161
+ "did discovery work", which is a whole-workspace property. Deriving them from
162
+ the scoped subset made `scope: 'sdk'` report a false discovery failure.
package/README.md CHANGED
@@ -57,6 +57,70 @@ args = []
57
57
 
58
58
  Set `DEBUG=true` in the environment to enable diagnostic logging.
59
59
 
60
+ ## Workspace Discovery And Coverage
61
+
62
+ Package discovery reads the workspace globs, so `apps/*` products are indexed
63
+ the same as `packages/*` ones. Resolution order:
64
+
65
+ 1. `pnpm-workspace.yaml` `packages:` — literals, `dir/*`, `**`, and `!` negations
66
+ 2. `package.json#workspaces` (array or `{ packages: [...] }`)
67
+ 3. `packages/*` as a last-resort fallback
68
+
69
+ Workspace globs must remain relative to the declared root: absolute paths and
70
+ `..` segments are rejected, and matched directories are realpath-confined before
71
+ their manifests are read. Positive globs share a 10,000 directory-entry
72
+ traversal budget. Exceeding it stops discovery with an error diagnostic and no
73
+ partial package set; deeply nested valid `**` matches remain supported because
74
+ the limit is based on work performed rather than directory depth. A separate
75
+ 512-package cap prevents broad matches from fanning out package reads, package
76
+ paths are revalidated immediately before those reads, and scanner fallbacks run
77
+ with at most eight concurrent scanners.
78
+
79
+ The workspace root is also indexed when it has a `package.json`, which is how
80
+ single-package repositories work; it is scanned with the member package
81
+ directories excluded so it can own objects without absorbing theirs. Per package, objects resolve from a domain
82
+ artifact, then a package-local manifest, then a source scan
83
+ (`@happyvertical/smrt-scanner`) — the same fallback `introspect-project` uses, so
84
+ both paths agree on one root. Every package records `objectSource`
85
+ (`domain-artifact | manifest | scanner | none`) plus a reason.
86
+
87
+ Manifest objects that belong to another package are rejected rather than counted:
88
+ a runtime `.smrt/manifest.json` is often an aggregate registering a package's
89
+ dependencies too, and counting those inflates the relationship facts. A consuming
90
+ package's artifact can also restate its dependency's objects under its own name,
91
+ so `Relationships-v2` collapses a shared `className::tableName` across a
92
+ dependency edge — by connected component, keeping the copy the others depend on —
93
+ and reports `duplicate-object-identity`. Unrelated packages that share a class
94
+ name stay distinct.
95
+
96
+ The index therefore carries two extra blocks (`schemaVersion: 2`):
97
+
98
+ - `coverage` — `workspaceGlobs`, `workspaceGlobSource`, `packageDirs`,
99
+ `packagesWithObjects`, and `packagesWithoutObjects` with a reason, the artifact
100
+ paths checked, and a remedy for each
101
+ - `diagnostics` — discovery problems. Discovering **zero** objects is an
102
+ error-grade diagnostic naming the roots and artifact paths checked plus the
103
+ commands to fix it, so an unseen project is never reported as a project with no
104
+ model. `smrt-architecture`, `smrt-review`, and the `reflect-*` tools surface it.
105
+
106
+ ## Response Budgets
107
+
108
+ Knowledge and introspection tools return a **summary** by default and accept
109
+ `detail: "full"` for the complete payload.
110
+
111
+ - `introspect-project` summary returns one compact record per object
112
+ (`className`, `qualifiedName`, `extends`, `tableName`, `tenantScope`,
113
+ `fieldCount`, compact relationship strings, `mcpOperations`). A response that
114
+ exceeds its character budget reports a `truncated` block with the omitted count
115
+ and filter guidance instead of being silently cut. `maxChars` overrides the
116
+ budget.
117
+ - `smrt-architecture`, `smrt-review`, and the `build-*-context` tools list
118
+ authored `AGENTS.md` and module docs **by path** rather than embedding them,
119
+ and return compact package records.
120
+
121
+ The `smrt dev:knowledge-*` CLI commands request `detail: "full"`, so their output
122
+ is unchanged.
123
+
60
124
  ## Knowledge Boundary
61
125
 
62
126
  `smrt-dev-mcp` is model-agnostic. Its review and architecture tools do not call
@@ -157,9 +221,11 @@ available it falls back to `@happyvertical/smrt-scanner`.
157
221
  |-----------|------|----------|-------------|
158
222
  | `directory` | `string` | No | Project directory (default: cwd) |
159
223
  | `manifestPath` | `string` | No | Explicit manifest artifact path |
160
- | `includeFields` | `boolean` | No | Include field details |
161
- | `includeRelationships` | `boolean` | No | Analyze relationships |
162
- | `includeMethods` | `boolean` | No | Include public method details |
224
+ | `detail` | `'summary' \| 'full'` | No | Default `summary`. `full` returns field, schema, and method detail |
225
+ | `maxChars` | `number` | No | Response character budget; overflow is reported under `truncated` |
226
+ | `includeFields` | `boolean` | No | Include field details (`detail: "full"` only) |
227
+ | `includeRelationships` | `boolean` | No | Analyze relationships (`detail: "full"` only) |
228
+ | `includeMethods` | `boolean` | No | Include public method details (`detail: "full"` only) |
163
229
 
164
230
  ### `review-smrt-project`
165
231
 
@@ -277,6 +343,7 @@ risks, questions, and the reusable architecture prompt bundle.
277
343
  | `idea` | `string` | No | Product or implementation idea |
278
344
  | `documentation` | `string` | No | Existing docs or requirements |
279
345
  | `focus` | `string` | No | Architecture concern to prioritize |
346
+ | `detail` | `'summary' \| 'full'` | No | Default `summary`; `full` embeds authored docs and full package records |
280
347
 
281
348
  ### `list-agent-skills`
282
349
 
package/dist/index.d.ts CHANGED
@@ -154,6 +154,8 @@ export declare const TOOLS: ({
154
154
  };
155
155
  directory?: undefined;
156
156
  manifestPath?: undefined;
157
+ detail?: undefined;
158
+ maxChars?: undefined;
157
159
  includeFields?: undefined;
158
160
  includeRelationships?: undefined;
159
161
  includeMethods?: undefined;
@@ -188,6 +190,16 @@ export declare const TOOLS: ({
188
190
  type: string;
189
191
  description: string;
190
192
  };
193
+ detail: {
194
+ type: string;
195
+ enum: string[];
196
+ default: string;
197
+ description: string;
198
+ };
199
+ maxChars: {
200
+ type: string;
201
+ description: string;
202
+ };
191
203
  includeFields: {
192
204
  type: string;
193
205
  description: string;
@@ -267,6 +279,8 @@ export declare const TOOLS: ({
267
279
  includeMcpConfig?: undefined;
268
280
  includeCliConfig?: undefined;
269
281
  manifestPath?: undefined;
282
+ detail?: undefined;
283
+ maxChars?: undefined;
270
284
  includeFields?: undefined;
271
285
  includeRelationships?: undefined;
272
286
  includeMethods?: undefined;
@@ -309,6 +323,8 @@ export declare const TOOLS: ({
309
323
  includeCliConfig?: undefined;
310
324
  directory?: undefined;
311
325
  manifestPath?: undefined;
326
+ detail?: undefined;
327
+ maxChars?: undefined;
312
328
  includeFields?: undefined;
313
329
  includeRelationships?: undefined;
314
330
  includeMethods?: undefined;
@@ -361,6 +377,8 @@ export declare const TOOLS: ({
361
377
  includeCliConfig?: undefined;
362
378
  directory?: undefined;
363
379
  manifestPath?: undefined;
380
+ detail?: undefined;
381
+ maxChars?: undefined;
364
382
  includeFields?: undefined;
365
383
  includeRelationships?: undefined;
366
384
  includeMethods?: undefined;
@@ -411,6 +429,8 @@ export declare const TOOLS: ({
411
429
  includeCliConfig?: undefined;
412
430
  directory?: undefined;
413
431
  manifestPath?: undefined;
432
+ detail?: undefined;
433
+ maxChars?: undefined;
414
434
  includeFields?: undefined;
415
435
  includeRelationships?: undefined;
416
436
  includeMethods?: undefined;
@@ -469,6 +489,8 @@ export declare const TOOLS: ({
469
489
  includeCliConfig?: undefined;
470
490
  directory?: undefined;
471
491
  manifestPath?: undefined;
492
+ detail?: undefined;
493
+ maxChars?: undefined;
472
494
  includeFields?: undefined;
473
495
  includeRelationships?: undefined;
474
496
  includeMethods?: undefined;
@@ -506,6 +528,12 @@ export declare const TOOLS: ({
506
528
  documentation: {
507
529
  type: string;
508
530
  };
531
+ detail: {
532
+ type: string;
533
+ enum: string[];
534
+ default: string;
535
+ description: string;
536
+ };
509
537
  className?: undefined;
510
538
  properties?: undefined;
511
539
  baseClass?: undefined;
@@ -521,6 +549,7 @@ export declare const TOOLS: ({
521
549
  includeCliConfig?: undefined;
522
550
  directory?: undefined;
523
551
  manifestPath?: undefined;
552
+ maxChars?: undefined;
524
553
  includeFields?: undefined;
525
554
  includeRelationships?: undefined;
526
555
  includeMethods?: undefined;
@@ -567,6 +596,12 @@ export declare const TOOLS: ({
567
596
  package: {
568
597
  type: string;
569
598
  };
599
+ detail: {
600
+ type: string;
601
+ enum: string[];
602
+ default: string;
603
+ description: string;
604
+ };
570
605
  className?: undefined;
571
606
  properties?: undefined;
572
607
  baseClass?: undefined;
@@ -582,6 +617,7 @@ export declare const TOOLS: ({
582
617
  includeCliConfig?: undefined;
583
618
  directory?: undefined;
584
619
  manifestPath?: undefined;
620
+ maxChars?: undefined;
585
621
  includeFields?: undefined;
586
622
  includeRelationships?: undefined;
587
623
  includeMethods?: undefined;
@@ -623,6 +659,12 @@ export declare const TOOLS: ({
623
659
  enum: string[];
624
660
  default: string;
625
661
  };
662
+ detail: {
663
+ type: string;
664
+ enum: string[];
665
+ default: string;
666
+ description: string;
667
+ };
626
668
  className?: undefined;
627
669
  properties?: undefined;
628
670
  baseClass?: undefined;
@@ -638,6 +680,7 @@ export declare const TOOLS: ({
638
680
  includeCliConfig?: undefined;
639
681
  directory?: undefined;
640
682
  manifestPath?: undefined;
683
+ maxChars?: undefined;
641
684
  includeFields?: undefined;
642
685
  includeRelationships?: undefined;
643
686
  includeMethods?: undefined;
@@ -672,6 +715,12 @@ export declare const TOOLS: ({
672
715
  focus: {
673
716
  type: string;
674
717
  };
718
+ detail: {
719
+ type: string;
720
+ enum: string[];
721
+ default: string;
722
+ description: string;
723
+ };
675
724
  className?: undefined;
676
725
  properties?: undefined;
677
726
  baseClass?: undefined;
@@ -687,6 +736,7 @@ export declare const TOOLS: ({
687
736
  includeCliConfig?: undefined;
688
737
  directory?: undefined;
689
738
  manifestPath?: undefined;
739
+ maxChars?: undefined;
690
740
  includeFields?: undefined;
691
741
  includeRelationships?: undefined;
692
742
  includeMethods?: undefined;
@@ -730,6 +780,12 @@ export declare const TOOLS: ({
730
780
  package: {
731
781
  type: string;
732
782
  };
783
+ detail: {
784
+ type: string;
785
+ enum: string[];
786
+ default: string;
787
+ description: string;
788
+ };
733
789
  className?: undefined;
734
790
  properties?: undefined;
735
791
  baseClass?: undefined;
@@ -745,6 +801,7 @@ export declare const TOOLS: ({
745
801
  includeCliConfig?: undefined;
746
802
  directory?: undefined;
747
803
  manifestPath?: undefined;
804
+ maxChars?: undefined;
748
805
  includeFields?: undefined;
749
806
  includeRelationships?: undefined;
750
807
  includeMethods?: undefined;
@@ -780,6 +837,8 @@ export declare const TOOLS: ({
780
837
  includeCliConfig?: undefined;
781
838
  directory?: undefined;
782
839
  manifestPath?: undefined;
840
+ detail?: undefined;
841
+ maxChars?: undefined;
783
842
  includeFields?: undefined;
784
843
  includeRelationships?: undefined;
785
844
  includeMethods?: undefined;
@@ -831,6 +890,8 @@ export declare const TOOLS: ({
831
890
  includeCliConfig?: undefined;
832
891
  directory?: undefined;
833
892
  manifestPath?: undefined;
893
+ detail?: undefined;
894
+ maxChars?: undefined;
834
895
  includeFields?: undefined;
835
896
  includeRelationships?: undefined;
836
897
  includeMethods?: undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmCH,eAAO,MAAM,cAAc,QAAuB,CAAC;AAcnD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAyXjB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoCH,eAAO,MAAM,cAAc,QAAuB,CAAC;AAcnD,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAgbjB,CAAC"}