@memoarchitect/tools 0.6.5 → 0.6.7

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.
Files changed (106) hide show
  1. package/README.md +42 -10
  2. package/package.json +4 -2
  3. package/packages/tools/lib/analysis-starters.d.ts +4 -0
  4. package/packages/tools/lib/analysis-starters.d.ts.map +1 -0
  5. package/packages/tools/lib/analysis-starters.js +425 -0
  6. package/packages/tools/lib/analysis-starters.js.map +1 -0
  7. package/packages/tools/lib/commands/ask.d.ts.map +1 -1
  8. package/packages/tools/lib/commands/ask.js +1 -18
  9. package/packages/tools/lib/commands/ask.js.map +1 -1
  10. package/packages/tools/lib/commands/check.d.ts.map +1 -1
  11. package/packages/tools/lib/commands/check.js +2 -19
  12. package/packages/tools/lib/commands/check.js.map +1 -1
  13. package/packages/tools/lib/commands/dev.d.ts +2 -0
  14. package/packages/tools/lib/commands/dev.d.ts.map +1 -1
  15. package/packages/tools/lib/commands/dev.js +17 -27
  16. package/packages/tools/lib/commands/dev.js.map +1 -1
  17. package/packages/tools/lib/commands/dhf-draft.d.ts.map +1 -1
  18. package/packages/tools/lib/commands/dhf-draft.js +1 -17
  19. package/packages/tools/lib/commands/dhf-draft.js.map +1 -1
  20. package/packages/tools/lib/commands/dhf.d.ts.map +1 -1
  21. package/packages/tools/lib/commands/dhf.js +2 -18
  22. package/packages/tools/lib/commands/dhf.js.map +1 -1
  23. package/packages/tools/lib/commands/export.d.ts.map +1 -1
  24. package/packages/tools/lib/commands/export.js +2 -19
  25. package/packages/tools/lib/commands/export.js.map +1 -1
  26. package/packages/tools/lib/commands/import.d.ts.map +1 -1
  27. package/packages/tools/lib/commands/import.js +2 -19
  28. package/packages/tools/lib/commands/import.js.map +1 -1
  29. package/packages/tools/lib/commands/init.d.ts.map +1 -1
  30. package/packages/tools/lib/commands/init.js +3 -0
  31. package/packages/tools/lib/commands/init.js.map +1 -1
  32. package/packages/tools/lib/commands/pack.d.ts.map +1 -1
  33. package/packages/tools/lib/commands/pack.js +5 -2
  34. package/packages/tools/lib/commands/pack.js.map +1 -1
  35. package/packages/tools/lib/commands/plugin.d.ts.map +1 -1
  36. package/packages/tools/lib/commands/plugin.js +2 -17
  37. package/packages/tools/lib/commands/plugin.js.map +1 -1
  38. package/packages/tools/lib/commands/roundtrip.d.ts.map +1 -1
  39. package/packages/tools/lib/commands/roundtrip.js +2 -19
  40. package/packages/tools/lib/commands/roundtrip.js.map +1 -1
  41. package/packages/tools/lib/commands/rules.d.ts.map +1 -1
  42. package/packages/tools/lib/commands/rules.js +1 -19
  43. package/packages/tools/lib/commands/rules.js.map +1 -1
  44. package/packages/tools/lib/commands/validate.d.ts.map +1 -1
  45. package/packages/tools/lib/commands/validate.js +2 -22
  46. package/packages/tools/lib/commands/validate.js.map +1 -1
  47. package/packages/tools/lib/language/generated/ast.js +1 -1
  48. package/packages/tools/lib/language/generated/ast.js.map +1 -1
  49. package/packages/tools/lib/language/generated/grammar.d.ts.map +1 -1
  50. package/packages/tools/lib/language/generated/grammar.js +7 -0
  51. package/packages/tools/lib/language/generated/grammar.js.map +1 -1
  52. package/packages/tools/lib/mcp/model-loader.d.ts +2 -1
  53. package/packages/tools/lib/mcp/model-loader.d.ts.map +1 -1
  54. package/packages/tools/lib/mcp/model-loader.js +7 -22
  55. package/packages/tools/lib/mcp/model-loader.js.map +1 -1
  56. package/packages/tools/lib/model/builder.d.ts.map +1 -1
  57. package/packages/tools/lib/model/builder.js +33 -6
  58. package/packages/tools/lib/model/builder.js.map +1 -1
  59. package/packages/tools/lib/model/kind-registry.d.ts +14 -0
  60. package/packages/tools/lib/model/kind-registry.d.ts.map +1 -1
  61. package/packages/tools/lib/model/kind-registry.js +66 -0
  62. package/packages/tools/lib/model/kind-registry.js.map +1 -1
  63. package/packages/tools/lib/model/package-registry.d.ts.map +1 -1
  64. package/packages/tools/lib/model/package-registry.js +11 -0
  65. package/packages/tools/lib/model/package-registry.js.map +1 -1
  66. package/packages/tools/lib/model/relationship-legality.d.ts +2 -0
  67. package/packages/tools/lib/model/relationship-legality.d.ts.map +1 -1
  68. package/packages/tools/lib/model/relationship-legality.js.map +1 -1
  69. package/packages/tools/lib/model/semantic.d.ts +21 -0
  70. package/packages/tools/lib/model/semantic.d.ts.map +1 -1
  71. package/packages/tools/lib/model/semantic.js.map +1 -1
  72. package/packages/tools/lib/model/short-id.d.ts +8 -7
  73. package/packages/tools/lib/model/short-id.d.ts.map +1 -1
  74. package/packages/tools/lib/model/short-id.js +24 -17
  75. package/packages/tools/lib/model/short-id.js.map +1 -1
  76. package/packages/tools/lib/model/sysml-files.d.ts +12 -0
  77. package/packages/tools/lib/model/sysml-files.d.ts.map +1 -0
  78. package/packages/tools/lib/model/sysml-files.js +71 -0
  79. package/packages/tools/lib/model/sysml-files.js.map +1 -0
  80. package/packages/tools/lib/model/view-deriver.d.ts.map +1 -1
  81. package/packages/tools/lib/model/view-deriver.js +31 -0
  82. package/packages/tools/lib/model/view-deriver.js.map +1 -1
  83. package/packages/tools/lib/operations/project-snapshot.d.ts.map +1 -1
  84. package/packages/tools/lib/operations/project-snapshot.js +1 -19
  85. package/packages/tools/lib/operations/project-snapshot.js.map +1 -1
  86. package/packages/tools/lib/protocol/messages.d.ts +13 -0
  87. package/packages/tools/lib/protocol/messages.d.ts.map +1 -1
  88. package/packages/tools/lib/server/dev-server.d.ts +8 -0
  89. package/packages/tools/lib/server/dev-server.d.ts.map +1 -1
  90. package/packages/tools/lib/server/dev-server.js +18 -3
  91. package/packages/tools/lib/server/dev-server.js.map +1 -1
  92. package/packages/tools/lib/server/relationship-writer.d.ts +1 -1
  93. package/packages/tools/lib/server/relationship-writer.d.ts.map +1 -1
  94. package/packages/tools/lib/server/relationship-writer.js +8 -4
  95. package/packages/tools/lib/server/relationship-writer.js.map +1 -1
  96. package/packages/tools/lib/server/view-layout-store.d.ts.map +1 -1
  97. package/packages/tools/lib/server/view-layout-store.js +2 -0
  98. package/packages/tools/lib/server/view-layout-store.js.map +1 -1
  99. package/packages/tools/lib/validator/architecture-validator.d.ts +10 -0
  100. package/packages/tools/lib/validator/architecture-validator.d.ts.map +1 -0
  101. package/packages/tools/lib/validator/architecture-validator.js +77 -0
  102. package/packages/tools/lib/validator/architecture-validator.js.map +1 -0
  103. package/packages/tools/lib/validator/rule-engine.d.ts.map +1 -1
  104. package/packages/tools/lib/validator/rule-engine.js +4 -2
  105. package/packages/tools/lib/validator/rule-engine.js.map +1 -1
  106. package/packages/tools/src/grammar/memo-sysml.langium +3 -1
package/README.md CHANGED
@@ -9,7 +9,14 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <code>memo-tools 0.6.4</code> &middot; MIT &middot; SysML v2 &middot; ISO 14971 &middot; IEC 62304 &middot; ISO/IEC/IEEE 42010
12
+ <code>memo-tools 0.6.6</code> &middot; MIT &middot; SysML v2 &middot; ISO 14971 &middot; IEC 62304 &middot; ISO/IEC/IEEE 42010
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://memoarchitect.com/memo-tools.html">Product website</a> &middot;
17
+ <a href="https://memoarchitect.com/memo-tools/">Documentation</a> &middot;
18
+ <a href="https://github.com/memoarchitect/memo-tools">GitHub repository</a> &middot;
19
+ <a href="https://www.npmjs.com/package/@memoarchitect/tools">npm package</a>
13
20
  </p>
14
21
 
15
22
  ---
@@ -21,9 +28,14 @@
21
28
  ## Version compatibility
22
29
 
23
30
  `memo`, `memo-tools`, and `memo-architect` share a `MAJOR.MINOR` compatibility
24
- line. Any `0.5.x` release is intended to work with the other `0.5.x` products;
31
+ line. Any `0.6.x` release is intended to work with the other `0.6.x` products;
25
32
  patch versions may advance independently for fixes and additive changes.
26
33
 
34
+ `VERSION` is the sole release-version source for this repository. Run
35
+ `pnpm version:sync` after changing it; CI rejects unsynchronized generated
36
+ version fields and automatically creates the matching `v<version>` tag when
37
+ the change reaches `main`.
38
+
27
39
  ## The meMO stack
28
40
 
29
41
  meMO is a four-layer stack — adopt what you need
@@ -31,10 +43,10 @@ meMO is a four-layer stack — adopt what you need
31
43
 
32
44
  | Layer | What | Where |
33
45
  |---|---|---|
34
- | 01 Ontology | Typed SysML v2 elements, Arcadia-inspired architecture layers | [memoarchitect/memo](https://github.com/memoarchitect/memo) |
35
- | 02 Methodology | Profiles, viewpoints, rules, workflow gates | [memoarchitect/memo](https://github.com/memoarchitect/memo) |
36
- | **03 Tools** | **Model engine + `memo` CLI — this repo** | memoarchitect/memo-tools |
37
- | 04 Architect | Web workbench over the same model | [memoarchitect/memo-architect](https://github.com/memoarchitect/memo-architect) |
46
+ | 01 Ontology | Typed SysML v2 elements and architecture definitions | [Docs](https://memoarchitect.com/memo/) · [Repository](https://github.com/memoarchitect/memo) |
47
+ | 02 Methodology | Profiles, viewpoints, rules, workflow gates | [Docs](https://memoarchitect.com/memo/) · [Repository](https://github.com/memoarchitect/memo) |
48
+ | **03 Tools** | **Model engine + `memo` CLI — this repo** | [Docs](https://memoarchitect.com/memo-tools/) · [Repository](https://github.com/memoarchitect/memo-tools) |
49
+ | 04 Architect | Web workbench over the same model | [Docs](https://memoarchitect.com/memo-architect/) · [Repository](https://github.com/memoarchitect/memo-architect) |
38
50
 
39
51
  ## Supported tools
40
52
 
@@ -77,11 +89,31 @@ memo rules coverage
77
89
 
78
90
  Run `memo --help` or `memo <command> --help` for the complete command surface.
79
91
 
92
+ ### Jupyter analysis samples
93
+
94
+ Every `memo init` project includes `analysis/Samples/` with seven portable
95
+ Syside notebooks. They cover model overview and quality, architecture hotspots,
96
+ change-impact exploration, semantic charts, an ownership graph, and an HTML/CSV
97
+ inventory table. The notebooks discover either `model/` or `src/` automatically,
98
+ so the same samples work with default templates and worked examples.
99
+
100
+ Run them in a licensed Syside environment:
101
+
102
+ ```bash
103
+ python -m venv analysis/.venv
104
+ source analysis/.venv/bin/activate
105
+ python -m pip install jupyterlab syside
106
+ cd analysis
107
+ jupyter lab --port 8888
108
+ ```
109
+
80
110
  ## Documentation
81
111
 
82
- The [task-oriented documentation](docs/index.md) starts with installation and a
112
+ The [published documentation](https://memoarchitect.com/memo-tools/)
113
+ starts with installation and a
83
114
  first useful workflow, then covers validation, import, export, CI, configuration,
84
- and the library API. Build it locally with:
115
+ and the library API. Its source starts at [`docs/index.md`](docs/index.md). Build
116
+ it locally with:
85
117
 
86
118
  ```bash
87
119
  python3 -m pip install "mkdocs>=1.6" "mkdocs-material>=9.5" "pymdown-extensions>=10.0"
@@ -125,7 +157,7 @@ React does not import core internals directly.
125
157
  import { buildMemoModel } from '@memoarchitect/tools';
126
158
  ```
127
159
 
128
- The API is pre-stable. Pin an exact `0.5.x` patch when embedding it directly.
160
+ The API is pre-stable. Pin an exact `0.6.x` patch when embedding it directly.
129
161
 
130
162
  ### Maintainer checks
131
163
 
@@ -154,7 +186,7 @@ dependency to the sibling `memo` checkout for iterative development.
154
186
 
155
187
  ## Current repository relationship
156
188
 
157
- - A standalone clone installs `@memoarchitect/ontology@0.6.2` from npm.
189
+ - A standalone clone installs `@memoarchitect/ontology@0.6.3` from npm.
158
190
  - The Tools repository contains no ontology git submodule.
159
191
  - The private `memo-meta` workspace keeps `memo` and `memo-tools` as sibling
160
192
  submodules and applies local pnpm overrides for cross-repository work.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memoarchitect/tools",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
4
4
  "description": "MEMO tools — SysML v2 parser, semantic model, validation, project operations, and CLI",
5
5
  "type": "module",
6
6
  "main": "./packages/tools/lib/index.js",
@@ -38,11 +38,13 @@
38
38
  "example:validate": "node scripts/validate-gpca-example.mjs",
39
39
  "docs:serve": "python3 -m mkdocs serve",
40
40
  "docs:build": "python3 -m mkdocs build --strict",
41
+ "version:sync": "node scripts/sync-version.mjs",
42
+ "version:check": "node scripts/sync-version.mjs --check",
41
43
  "ontology:lint": "node tools/ontology-tools/lint.mjs",
42
44
  "ontology:compat": "node tools/ontology-tools/syside-compat.mjs"
43
45
  },
44
46
  "dependencies": {
45
- "@memoarchitect/ontology": "0.6.3",
47
+ "@memoarchitect/ontology": "0.6.5",
46
48
  "chalk": "^5.3.0",
47
49
  "chokidar": "^4.0.0",
48
50
  "commander": "^12.1.0",
@@ -0,0 +1,4 @@
1
+ /** Add model-independent Jupyter samples to a newly initialized project. */
2
+ export declare function scaffoldAnalysisSamples(projectDir: string): void;
3
+ export declare const analysisSampleNames: string[];
4
+ //# sourceMappingURL=analysis-starters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis-starters.d.ts","sourceRoot":"","sources":["../src/analysis-starters.ts"],"names":[],"mappings":"AAibA,4EAA4E;AAC5E,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAShE;AAED,eAAO,MAAM,mBAAmB,UAA4C,CAAC"}
@@ -0,0 +1,425 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ const LOAD_MODEL = `from pathlib import Path
4
+ from collections import Counter, defaultdict
5
+ import syside
6
+
7
+ def find_sysml_root(start=Path.cwd()):
8
+ """Find the nearest model/ or src/ folder containing textual SysML."""
9
+ for directory in (start, *start.parents):
10
+ for folder_name in ('model', 'src'):
11
+ candidate = directory / folder_name
12
+ if candidate.is_dir() and next(candidate.rglob('*.sysml'), None):
13
+ return candidate
14
+ raise FileNotFoundError('No model/ or src/ directory containing .sysml files was found')
15
+
16
+ SYSML_ROOT = find_sysml_root()
17
+ SYSML_FILES = sorted(SYSML_ROOT.rglob('*.sysml'))
18
+ model, diagnostics = syside.try_load_model([str(path) for path in SYSML_FILES])
19
+ print(f'Loaded {len(SYSML_FILES)} SysML files from {SYSML_ROOT}')`;
20
+ const SAMPLES_README = `# MEMO analysis samples
21
+
22
+ These notebooks are created by \`memo init\` and are designed to work with any
23
+ MEMO textual SysML v2 project. They locate the nearest \`model/\` or \`src/\`
24
+ directory automatically and query it through the typed Syside API.
25
+
26
+ ## Prerequisites
27
+
28
+ Use Python 3.12 or newer with JupyterLab and Syside installed and licensed:
29
+
30
+ \`\`\`bash
31
+ python -m venv analysis/.venv
32
+ source analysis/.venv/bin/activate
33
+ python -m pip install jupyterlab syside
34
+ cd analysis
35
+ jupyter lab --port 8888
36
+ \`\`\`
37
+
38
+ Configure the Syside license using its supported keyring or environment setup.
39
+ MEMO Architect's **Analysis → Jupyter Notebooks** menu opens the local server at
40
+ \`http://127.0.0.1:8888\`.
41
+
42
+ ## Included notebooks
43
+
44
+ | Notebook | Purpose |
45
+ |---|---|
46
+ | \`01-model-overview.ipynb\` | Model size, semantic composition, and diagnostic summary. |
47
+ | \`02-architecture-hotspots.ipynb\` | Busy containers, deep ownership paths, and dominant types. |
48
+ | \`03-model-quality.ipynb\` | Diagnostics, unnamed elements, repeated names, and empty definitions. |
49
+ | \`04-change-impact-explorer.ipynb\` | Search an element and inspect its structural neighborhood. |
50
+ | \`05-model-charts.ipynb\` | Bar and donut charts rendered without extra plotting packages. |
51
+ | \`06-ownership-graph.ipynb\` | SVG network graph of model ownership relationships. |
52
+ | \`07-model-inventory-table.ipynb\` | Filterable inventory parameters, HTML table, and CSV export. |
53
+
54
+ The notebooks use \`syside.try_load_model()\`, so partial analysis remains
55
+ available while the project contains parser or semantic diagnostics. Review the
56
+ reported diagnostics before treating analysis results as release evidence.
57
+ `;
58
+ const STARTERS = [
59
+ {
60
+ filename: '01-model-overview.ipynb',
61
+ title: 'Model overview',
62
+ description: 'Visual summary of model size, semantic content, and diagnostics.',
63
+ codeCells: [
64
+ `from IPython.display import HTML, display
65
+
66
+ semantic_types = [
67
+ 'Package', 'PartDefinition', 'PartUsage', 'RequirementDefinition',
68
+ 'RequirementUsage', 'ActionDefinition', 'ActionUsage', 'PortDefinition',
69
+ 'PortUsage', 'ConnectionDefinition', 'ConnectionUsage', 'StateDefinition',
70
+ 'StateUsage', 'AttributeDefinition', 'AttributeUsage'
71
+ ]
72
+ counts = {}
73
+ for type_name in semantic_types:
74
+ node_type = getattr(syside, type_name, None)
75
+ if node_type is not None:
76
+ counts[type_name] = len(list(model.elements(node_type)))
77
+
78
+ largest = max(counts.values(), default=1)
79
+ rows = ''.join(
80
+ f'<tr><td>{name}</td><td>{count:,}</td><td><div style="background:#2d8d70;height:12px;width:{max(2, count * 320 / largest):.0f}px"></div></td></tr>'
81
+ for name, count in sorted(counts.items(), key=lambda item: item[1], reverse=True)
82
+ )
83
+ display(HTML(f'<h3>Semantic inventory</h3><table><tr><th>Element type</th><th>Count</th><th>Relative size</th></tr>{rows}</table>'))
84
+
85
+ {
86
+ 'SysML files': len(SYSML_FILES),
87
+ 'Errors': len(list(diagnostics.errors)),
88
+ 'Warnings': len(list(diagnostics.warnings)),
89
+ 'Information': len(list(diagnostics.infos)),
90
+ }`,
91
+ ],
92
+ },
93
+ {
94
+ filename: '02-architecture-hotspots.ipynb',
95
+ title: 'Architecture hotspots',
96
+ description: 'Find busy containers, deep ownership paths, and dominant semantic types.',
97
+ codeCells: [
98
+ `from IPython.display import display
99
+
100
+ semantic_elements = (
101
+ list(model.elements(syside.Usage, include_subtypes=True))
102
+ + list(model.elements(syside.Definition, include_subtypes=True))
103
+ )
104
+
105
+ def safe_name(element):
106
+ value = element.qualified_name or element.name or element.declared_name
107
+ return str(value) if value else '<unnamed>'
108
+
109
+ def ownership_depth(element):
110
+ depth, current, seen = 0, element.owner, set()
111
+ while current is not None and id(current) not in seen:
112
+ seen.add(id(current))
113
+ depth += 1
114
+ current = getattr(current, 'owner', None)
115
+ return depth
116
+
117
+ containers = []
118
+ for element in semantic_elements:
119
+ children = [child for child in element.owned_elements if isinstance(child, (syside.Usage, syside.Definition))]
120
+ if children:
121
+ containers.append((len(children), type(element).__name__, safe_name(element)))
122
+
123
+ print('Top containers by directly owned semantic elements')
124
+ display(sorted(containers, reverse=True)[:25])
125
+
126
+ print('Deepest ownership paths')
127
+ display(sorted(
128
+ ((ownership_depth(item), type(item).__name__, safe_name(item)) for item in semantic_elements),
129
+ reverse=True,
130
+ )[:25])
131
+
132
+ print('Most common semantic types')
133
+ Counter(type(item).__name__ for item in semantic_elements).most_common(30)`,
134
+ ],
135
+ },
136
+ {
137
+ filename: '03-model-quality.ipynb',
138
+ title: 'Model quality review',
139
+ description: 'Review diagnostics, unnamed elements, repeated names, and empty definitions.',
140
+ codeCells: [
141
+ `from IPython.display import display
142
+
143
+ usages = list(model.elements(syside.Usage, include_subtypes=True))
144
+ definitions = list(model.elements(syside.Definition, include_subtypes=True))
145
+ semantic_elements = usages + definitions
146
+
147
+ unnamed = [item for item in semantic_elements if not (item.name or item.declared_name)]
148
+ by_name = defaultdict(list)
149
+ for item in semantic_elements:
150
+ value = item.qualified_name or item.name or item.declared_name
151
+ name = str(value) if value else None
152
+ if name:
153
+ by_name[name].append(item)
154
+ duplicates = [(name, len(items), sorted({type(item).__name__ for item in items})) for name, items in by_name.items() if len(items) > 1]
155
+ empty_definitions = [
156
+ item for item in definitions
157
+ if not any(isinstance(child, (syside.Usage, syside.Definition)) for child in item.owned_elements)
158
+ ]
159
+
160
+ display({
161
+ 'parser/semantic errors': len(list(diagnostics.errors)),
162
+ 'warnings': len(list(diagnostics.warnings)),
163
+ 'unnamed usages or definitions': len(unnamed),
164
+ 'repeated qualified names': len(duplicates),
165
+ 'definitions with no owned usages/definitions': len(empty_definitions),
166
+ })
167
+
168
+ print('Repeated qualified names (first 30)')
169
+ display(sorted(duplicates, key=lambda item: item[1], reverse=True)[:30])
170
+
171
+ print('Empty definition candidates (first 30)')
172
+ [(type(item).__name__, item.qualified_name or item.name) for item in empty_definitions[:30]]`,
173
+ `print('First diagnostics to investigate')
174
+ for diagnostic in list(diagnostics.errors)[:20] + list(diagnostics.warnings)[:20]:
175
+ print(diagnostic)`,
176
+ ],
177
+ },
178
+ {
179
+ filename: '04-change-impact-explorer.ipynb',
180
+ title: 'Change-impact explorer',
181
+ description: 'Search any element and inspect its owner, children, and directly related elements.',
182
+ codeCells: [
183
+ `QUERY = '' # Example: 'pump', 'controller', 'safety', or a requirement name
184
+
185
+ semantic_elements = (
186
+ list(model.elements(syside.Usage, include_subtypes=True))
187
+ + list(model.elements(syside.Definition, include_subtypes=True))
188
+ )
189
+
190
+ def label(element):
191
+ value = element.qualified_name or element.name or element.declared_name
192
+ return str(value) if value else '<unnamed>'
193
+
194
+ if not QUERY:
195
+ print('Set QUERY to a name fragment. Suggestions:')
196
+ for suggestion in [label(item) for item in semantic_elements if item.name][:20]:
197
+ print(' -', suggestion)
198
+ matches = []
199
+ else:
200
+ matches = [item for item in semantic_elements if QUERY.casefold() in label(item).casefold()]
201
+ print(f'{len(matches)} match(es) for {QUERY!r}')
202
+
203
+ [(type(item).__name__, label(item)) for item in matches[:100]]`,
204
+ `def neighborhood(element):
205
+ owner = getattr(element, 'owner', None)
206
+ children = [child for child in element.owned_elements if isinstance(child, (syside.Usage, syside.Definition))]
207
+ related = []
208
+ for relationship in element.owned_relationships:
209
+ for candidate in relationship.related_elements:
210
+ if candidate is not element:
211
+ related.append(candidate)
212
+ return {
213
+ 'selected': (type(element).__name__, label(element)),
214
+ 'owner': None if owner is None else (type(owner).__name__, label(owner)),
215
+ 'children': [(type(child).__name__, label(child)) for child in children[:50]],
216
+ 'directly related': [(type(item).__name__, label(item)) for item in related[:50]],
217
+ }
218
+
219
+ [neighborhood(item) for item in matches[:10]]`,
220
+ ],
221
+ },
222
+ {
223
+ filename: '05-model-charts.ipynb',
224
+ title: 'Model charts',
225
+ description: 'Render semantic composition and diagnostic charts without external plotting packages.',
226
+ codeCells: [
227
+ `from html import escape
228
+ from IPython.display import HTML, display
229
+
230
+ type_names = [
231
+ 'PartUsage', 'RequirementUsage', 'ActionUsage', 'PortUsage',
232
+ 'ConnectionUsage', 'StateUsage', 'AttributeUsage', 'ItemUsage',
233
+ 'PartDefinition', 'RequirementDefinition', 'ActionDefinition', 'PortDefinition'
234
+ ]
235
+ counts = {}
236
+ for type_name in type_names:
237
+ node_type = getattr(syside, type_name, None)
238
+ if node_type is not None:
239
+ counts[type_name] = len(list(model.elements(node_type)))
240
+
241
+ palette = ['#2d8d70', '#3887c7', '#e58a2b', '#8357b6', '#cf5c62', '#4f9d9d']
242
+ largest = max(counts.values(), default=1)
243
+ bars = []
244
+ for index, (name, count) in enumerate(sorted(counts.items(), key=lambda item: item[1], reverse=True)):
245
+ width = max(1, count * 100 / largest)
246
+ color = palette[index % len(palette)]
247
+ bars.append(f'''<div style="display:grid;grid-template-columns:180px 1fr 70px;gap:12px;align-items:center;margin:7px 0">
248
+ <div>{escape(name)}</div><div style="background:#edf2f4;border-radius:6px;overflow:hidden"><div style="width:{width:.1f}%;height:20px;background:{color}"></div></div><strong>{count:,}</strong>
249
+ </div>''')
250
+
251
+ error_count = len(list(diagnostics.errors))
252
+ warning_count = len(list(diagnostics.warnings))
253
+ info_count = len(list(diagnostics.infos))
254
+ total_diagnostics = max(1, error_count + warning_count + info_count)
255
+ diagnostic_gradient = (
256
+ f'#cf5c62 0 {error_count / total_diagnostics * 100:.1f}%, '
257
+ f'#e5a52b {error_count / total_diagnostics * 100:.1f}% {(error_count + warning_count) / total_diagnostics * 100:.1f}%, '
258
+ f'#3887c7 {(error_count + warning_count) / total_diagnostics * 100:.1f}% 100%'
259
+ )
260
+
261
+ display(HTML(f'''<div style="font-family:system-ui;max-width:900px">
262
+ <h2>Semantic composition</h2>{''.join(bars)}
263
+ <h2 style="margin-top:28px">Diagnostics</h2>
264
+ <div style="display:flex;align-items:center;gap:24px">
265
+ <div style="width:150px;height:150px;border-radius:50%;background:conic-gradient({diagnostic_gradient});position:relative">
266
+ <div style="position:absolute;inset:28px;background:white;border-radius:50%;display:grid;place-items:center;font-size:24px;font-weight:700">{error_count + warning_count + info_count}</div>
267
+ </div>
268
+ <table><tr><th>Severity</th><th>Count</th></tr><tr><td>Errors</td><td>{error_count}</td></tr><tr><td>Warnings</td><td>{warning_count}</td></tr><tr><td>Information</td><td>{info_count}</td></tr></table>
269
+ </div>
270
+ </div>'''))`,
271
+ ],
272
+ },
273
+ {
274
+ filename: '06-ownership-graph.ipynb',
275
+ title: 'Ownership graph',
276
+ description: 'Draw an SVG network of model elements and their ownership relationships.',
277
+ codeCells: [
278
+ `from html import escape
279
+ from IPython.display import SVG, display
280
+
281
+ semantic_elements = (
282
+ list(model.elements(syside.Usage, include_subtypes=True))
283
+ + list(model.elements(syside.Definition, include_subtypes=True))
284
+ )
285
+
286
+ def node_name(element):
287
+ value = element.name or element.declared_name or element.qualified_name
288
+ return str(value) if value else '<unnamed>'
289
+
290
+ # Prefer named elements with semantic children, then fill with other named elements.
291
+ ranked = sorted(
292
+ (item for item in semantic_elements if item.name or item.declared_name),
293
+ key=lambda item: sum(isinstance(child, (syside.Usage, syside.Definition)) for child in item.owned_elements),
294
+ reverse=True,
295
+ )
296
+ selected = ranked[:60]
297
+ selected_ids = {id(item) for item in selected}
298
+
299
+ def depth(element):
300
+ value, current, seen = 0, element.owner, set()
301
+ while current is not None and id(current) not in seen:
302
+ seen.add(id(current)); value += 1; current = getattr(current, 'owner', None)
303
+ return value
304
+
305
+ columns = defaultdict(list)
306
+ for item in selected:
307
+ columns[depth(item)].append(item)
308
+ depths = sorted(columns)
309
+ positions = {}
310
+ for column_index, level in enumerate(depths):
311
+ for row_index, item in enumerate(columns[level]):
312
+ positions[id(item)] = (30 + column_index * 240, 30 + row_index * 58)
313
+
314
+ width = max(500, 260 * max(1, len(depths)))
315
+ height = max(240, 70 + 58 * max((len(items) for items in columns.values()), default=1))
316
+ edges = []
317
+ nodes = []
318
+ colors = {'PartUsage':'#dff4ee', 'RequirementUsage':'#e8eefb', 'ActionUsage':'#fff0dc', 'PortUsage':'#efe5fa'}
319
+ for item in selected:
320
+ x, y = positions[id(item)]
321
+ owner = getattr(item, 'owner', None)
322
+ if owner is not None and id(owner) in selected_ids:
323
+ ox, oy = positions[id(owner)]
324
+ edges.append(f'<path d="M {ox + 190} {oy + 20} C {ox + 215} {oy + 20}, {x - 25} {y + 20}, {x} {y + 20}" fill="none" stroke="#91a7b3" stroke-width="1.5"/>')
325
+ kind = type(item).__name__
326
+ fill = colors.get(kind, '#f4f6f7')
327
+ label = escape(node_name(item)[:27])
328
+ nodes.append(f'<g><rect x="{x}" y="{y}" width="190" height="40" rx="7" fill="{fill}" stroke="#547180"/><text x="{x + 9}" y="{y + 17}" font-size="11" font-weight="700">{label}</text><text x="{x + 9}" y="{y + 32}" font-size="9" fill="#587487">{kind}</text></g>')
329
+
330
+ svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 {width} {height}" style="background:#fbfcfc;border:1px solid #d9e3e8;border-radius:10px">{''.join(edges)}{''.join(nodes)}</svg>'''
331
+ display(SVG(svg))
332
+ print(f'Displayed {len(selected)} elements and {len(edges)} ownership edges')`,
333
+ ],
334
+ },
335
+ {
336
+ filename: '07-model-inventory-table.ipynb',
337
+ title: 'Model inventory table',
338
+ description: 'Filter a model-independent element table and export the current result to CSV.',
339
+ codeCells: [
340
+ `from html import escape
341
+ from IPython.display import HTML, display
342
+
343
+ KIND_FILTER = '' # Example: 'PartUsage' or 'Requirement'
344
+ NAME_FILTER = '' # Case-insensitive name fragment
345
+ ROW_LIMIT = 250
346
+
347
+ semantic_elements = (
348
+ list(model.elements(syside.Usage, include_subtypes=True))
349
+ + list(model.elements(syside.Definition, include_subtypes=True))
350
+ )
351
+
352
+ def text(value):
353
+ return str(value) if value else ''
354
+
355
+ records = []
356
+ for item in semantic_elements:
357
+ kind = type(item).__name__
358
+ name = text(item.name or item.declared_name)
359
+ qualified_name = text(item.qualified_name)
360
+ owner = getattr(item, 'owner', None)
361
+ owner_name = text(getattr(owner, 'qualified_name', None) or getattr(owner, 'name', None))
362
+ if KIND_FILTER and KIND_FILTER.casefold() not in kind.casefold():
363
+ continue
364
+ if NAME_FILTER and NAME_FILTER.casefold() not in (name + ' ' + qualified_name).casefold():
365
+ continue
366
+ records.append({'kind': kind, 'name': name, 'qualified_name': qualified_name, 'owner': owner_name})
367
+
368
+ records.sort(key=lambda row: (row['kind'], row['qualified_name'], row['name']))
369
+ visible = records[:ROW_LIMIT]
370
+ rows = ''.join(
371
+ '<tr>' + ''.join(f'<td>{escape(row[key])}</td>' for key in ('kind', 'name', 'qualified_name', 'owner')) + '</tr>'
372
+ for row in visible
373
+ )
374
+ display(HTML(f'''<p><strong>{len(records):,}</strong> matching elements; showing {len(visible):,}.</p>
375
+ <div style="max-height:620px;overflow:auto"><table style="font-size:12px"><thead style="position:sticky;top:0;background:white"><tr><th>Kind</th><th>Name</th><th>Qualified name</th><th>Owner</th></tr></thead><tbody>{rows}</tbody></table></div>'''))`,
376
+ `import csv
377
+
378
+ OUTPUT = Path('model-inventory.csv')
379
+ with OUTPUT.open('w', newline='', encoding='utf-8') as stream:
380
+ writer = csv.DictWriter(stream, fieldnames=['kind', 'name', 'qualified_name', 'owner'])
381
+ writer.writeheader()
382
+ writer.writerows(records)
383
+ print(f'Exported {len(records):,} rows to {OUTPUT.resolve()}')`,
384
+ ],
385
+ },
386
+ ];
387
+ function markdownCell(starter) {
388
+ return {
389
+ cell_type: 'markdown',
390
+ metadata: {},
391
+ source: [`# ${starter.title}\n\n${starter.description}\n\nThis sample uses only standard SysML v2 concepts and automatically discovers the project's \`model/\` or \`src/\` directory.`],
392
+ };
393
+ }
394
+ function codeCell(source) {
395
+ return { cell_type: 'code', metadata: {}, source: [source], execution_count: null, outputs: [] };
396
+ }
397
+ function notebook(starter) {
398
+ const cells = [markdownCell(starter), codeCell(LOAD_MODEL), ...starter.codeCells.map(codeCell)]
399
+ .map((cell, index) => ({ ...cell, id: `memo-${index + 1}` }));
400
+ return {
401
+ cells,
402
+ metadata: {
403
+ kernelspec: { display_name: 'Python 3 (ipykernel)', language: 'python', name: 'python3' },
404
+ language_info: { name: 'python', version: '3' },
405
+ memo: { starter: true, description: starter.description },
406
+ },
407
+ nbformat: 4,
408
+ nbformat_minor: 5,
409
+ };
410
+ }
411
+ /** Add model-independent Jupyter samples to a newly initialized project. */
412
+ export function scaffoldAnalysisSamples(projectDir) {
413
+ const samplesDir = resolve(projectDir, 'analysis', 'Samples');
414
+ mkdirSync(samplesDir, { recursive: true });
415
+ const readmePath = resolve(samplesDir, 'README.md');
416
+ if (!existsSync(readmePath))
417
+ writeFileSync(readmePath, SAMPLES_README);
418
+ for (const starter of STARTERS) {
419
+ const path = resolve(samplesDir, starter.filename);
420
+ if (!existsSync(path))
421
+ writeFileSync(path, JSON.stringify(notebook(starter), null, 2) + '\n');
422
+ }
423
+ }
424
+ export const analysisSampleNames = STARTERS.map(starter => starter.filename);
425
+ //# sourceMappingURL=analysis-starters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis-starters.js","sourceRoot":"","sources":["../src/analysis-starters.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAmCA,MAAM,WAAW,iBAAiB;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD5F"}
1
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,iBAAiB;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqD5F"}
@@ -6,27 +6,10 @@
6
6
  // memo ask "Show trace from REQ-001 to verification"
7
7
  // memo ask "Which layers are least complete?"
8
8
  // ─────────────────────────────────────────────────────────────────────────────
9
- import { readdirSync } from 'node:fs';
10
- import { resolve } from 'node:path';
11
9
  import chalk from 'chalk';
12
10
  import { findConfigFile, parseFiles, buildMemoModel, loadOntologyRegistries, validateModel, computeCompleteness, createQueryContext, resolveLLMConfig, createProvider, askModel, } from '@memoarchitect/tools';
13
11
  import { loadAndResolveConfig } from '../server/config-resolver.js';
14
- function findSysmlFiles(dir) {
15
- const files = [];
16
- try {
17
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
18
- const full = resolve(dir, entry.name);
19
- if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.memo') {
20
- files.push(...findSysmlFiles(full));
21
- }
22
- else if (entry.name.endsWith('.sysml')) {
23
- files.push(full);
24
- }
25
- }
26
- }
27
- catch { /* skip */ }
28
- return files;
29
- }
12
+ import { findSysmlFiles } from '../model/sysml-files.js';
30
13
  export async function askCommand(question, options) {
31
14
  // 1. Resolve LLM provider
32
15
  const llmConfig = resolveLLMConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ask.js","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAgJA,wBAAsB,YAAY,CAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA8Gf"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAiIA,wBAAsB,YAAY,CAC9B,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA8Gf"}
@@ -1,8 +1,9 @@
1
1
  import { resolve } from 'node:path';
2
- import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
3
  import chalk from 'chalk';
4
4
  import { findConfigFile, parseFiles, buildMemoModel, loadOntologyRegistries } from '@memoarchitect/tools';
5
5
  import { loadAndResolveConfig } from '../server/config-resolver.js';
6
+ import { findSysmlFiles } from '../model/sysml-files.js';
6
7
  const STANDARD_CONSTRUCTS = new Set([
7
8
  'part', 'requirement', 'action', 'item', 'port',
8
9
  'interface', 'connection', 'attribute', 'constraint',
@@ -14,24 +15,6 @@ const STANDARD_KEYWORDS = new Set([
14
15
  'viewpoint', 'view', 'flow', 'allocate', 'connect', 'first', 'then',
15
16
  'in', 'out', 'inout', 'end', 'ref', 'doc', 'library',
16
17
  ]);
17
- function findSysmlFiles(dir) {
18
- const files = [];
19
- try {
20
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
21
- const full = resolve(dir, entry.name);
22
- if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.memo') {
23
- files.push(...findSysmlFiles(full));
24
- }
25
- else if (entry.name.endsWith('.sysml')) {
26
- files.push(full);
27
- }
28
- }
29
- }
30
- catch {
31
- // skip
32
- }
33
- return files;
34
- }
35
18
  function checkExplicitMultiplicity(sysmlFiles) {
36
19
  const findings = [];
37
20
  const attrRegex = /attribute\s+(\w+)\s*:\s*(\w+)\s*(?:\[([^\]]*)\])?\s*[;{=]/g;