@smartsoft001-mobilems/claude-plugins 2.86.0 → 2.88.0
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/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-articles/SKILL.md +108 -0
- package/plugins/mobilems/skills/mobilems-articles/references/lib-contents.md +164 -0
- package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +146 -0
- package/plugins/mobilems/skills/mobilems-articles/references/shared-components.md +56 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +61 -0
- package/plugins/mobilems/skills/mobilems-events/SKILL.md +65 -0
- package/plugins/mobilems/skills/mobilems-events/references/lib-contents.md +212 -0
- package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +113 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +59 -0
- package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +77 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +76 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +116 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/shared-components.md +61 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +44 -0
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +80 -0
- package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +68 -0
- package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +108 -0
- package/plugins/mobilems/skills/mobilems-objects/references/shared-components.md +69 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +51 -0
- package/plugins/mobilems/skills/mobilems-partners/SKILL.md +65 -0
- package/plugins/mobilems/skills/mobilems-partners/references/lib-contents.md +160 -0
- package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +125 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +59 -0
- package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +151 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +115 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +44 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +203 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/npm-deps.md +66 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +124 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +97 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +125 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +112 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/classify-shared-components.py +114 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/detect-base.sh +49 -0
- package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +81 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +60 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +134 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +107 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/detect-init.sh +29 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +56 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Build the foundation shared library
|
|
2
|
+
|
|
3
|
+
The shared library `libs/shared/angular` (alias `@<PREFIX>/angular`) holds the museum's shared
|
|
4
|
+
Angular building blocks: the `App` shell, header, footer, the generic page renderer, filters,
|
|
5
|
+
gallery, map, slider, carousels, content widgets, WCAG + SEO services, interceptors, pipes,
|
|
6
|
+
directives, tools, models, and `configs.module.ts`. Build it by copying from the reference repo's
|
|
7
|
+
`libs/shared/angular/src/lib` and threading `<PREFIX>` (the reference uses `@msr`).
|
|
8
|
+
|
|
9
|
+
The library starts as the base shell that exports only `environment` + `EnvironmentService` with
|
|
10
|
+
empty `lib/` placeholder dirs — fill those dirs with the **foundation shared components**.
|
|
11
|
+
|
|
12
|
+
## Foundation shared components vs feature shared components
|
|
13
|
+
|
|
14
|
+
The shared library contains two kinds of shared component, separated by a simple structural rule:
|
|
15
|
+
|
|
16
|
+
- A **foundation shared component** depends only on Angular, the smartsoft framework packages, and
|
|
17
|
+
other foundation shared components. **Build these here.**
|
|
18
|
+
- A **feature shared component** imports a feature domain package
|
|
19
|
+
(`@smartsoft001-mobilems/objects-domain`, `…/articles-domain`, `…/public-collections-domain`) to
|
|
20
|
+
render that entity, e.g. the per-entity grid/list cards and the object/article gallery widgets.
|
|
21
|
+
Each of these belongs to its feature's module skill (`mobilems-objects`,
|
|
22
|
+
`mobilems-object-collections`, `mobilems-articles`), which builds it alongside the feature's npm
|
|
23
|
+
package. The foundation does not build them, because the foundation does not install those
|
|
24
|
+
packages.
|
|
25
|
+
|
|
26
|
+
This rule keeps the layering clean and lets the foundation library build on its own. The home-page
|
|
27
|
+
carousels (`new-objects-carousel`, `news-carousel`, `partners-carousel`) and the home page are
|
|
28
|
+
**foundation** shared components — they take `IObject` / `IArticle` / `IPartner` from the shared
|
|
29
|
+
`models/data.interface.ts` and never import a feature package, so build them here.
|
|
30
|
+
|
|
31
|
+
## Identify the foundation set
|
|
32
|
+
|
|
33
|
+
Run `python3 scripts/classify-shared-components.py <reference-repo>/libs/shared/angular/src/lib/components`.
|
|
34
|
+
It prints two lists — **FOUNDATION** (build here) and **FEATURE-OWNED** (built by a module skill) —
|
|
35
|
+
by following imports: any component that imports a feature package, or a component that does, is
|
|
36
|
+
FEATURE-OWNED. A typical FEATURE-OWNED set (confirm with the classifier on your reference app) is:
|
|
37
|
+
|
|
38
|
+
| Feature shared component | Built by |
|
|
39
|
+
| ------------------------------------------ | ------------------------------ |
|
|
40
|
+
| `objects-grid-item`, `objects-list-item` | `mobilems-objects` |
|
|
41
|
+
| `widget/gallery-object` | `mobilems-objects` |
|
|
42
|
+
| `collections-grid-item` | `mobilems-object-collections` |
|
|
43
|
+
| `articles-grid-item`, `articles-list-item` | `mobilems-articles` (articles) |
|
|
44
|
+
| `news-grid-item`, `news-list-item` | `mobilems-articles` (news) |
|
|
45
|
+
| `widget/gallery-article` | `mobilems-articles` |
|
|
46
|
+
|
|
47
|
+
Build everything else — `app`, `header`, `footer`, `page`, the `filter*` family, `breadcrumbs`,
|
|
48
|
+
`pagination`, `searchbar`, `loader`, `error-retry`, `scroll-top`, `skip-links`, `wcag`, `gallery`,
|
|
49
|
+
`map`, `slider`, `carousel`, the three carousels above, `category-dropdown`, `dropdown`, `menu-item`,
|
|
50
|
+
`next-page`, `calendar`, `confirmation-modal`, `newsletter-*`, `image-info-panel`, `icons`, `tabs`,
|
|
51
|
+
and the `widget` container with its foundation widgets.
|
|
52
|
+
|
|
53
|
+
> Trust the script over this table for the **leaf** components (the grid/list cards and the
|
|
54
|
+
> `widget/gallery-*` sub-widgets) — if the reference diverges, the script is authoritative there.
|
|
55
|
+
> **One expected exception:** the script also lists the `widget` **container** (`components/widget`)
|
|
56
|
+
> as FEATURE-OWNED, because the reference container imports the feature gallery sub-widgets. That is
|
|
57
|
+
> not a signal to skip it — the container is foundation infrastructure. Build it (see "The widget
|
|
58
|
+
> container" below) registering only the foundation widgets.
|
|
59
|
+
|
|
60
|
+
## The widget container
|
|
61
|
+
|
|
62
|
+
`components/widget/widget.component.ts` renders content blocks by mapping an `ArticleWidgetType` to a
|
|
63
|
+
component. The classifier flags this container FEATURE-OWNED (the reference one imports the feature
|
|
64
|
+
gallery sub-widgets), but build it here — it is foundation infrastructure the page renderer needs.
|
|
65
|
+
Build it registering only the **foundation widgets** — `text`, `image`, `video`, `audio`,
|
|
66
|
+
`gallery-image`, `media` — and their type-map entries; do not import or register the
|
|
67
|
+
`gallery-object` / `gallery-article` sub-widgets (those don't exist in the foundation). The
|
|
68
|
+
`mobilems-objects` / `mobilems-articles` skills add their `GALLERY_OBJECT` / `GALLERY_ARTICLE` imports
|
|
69
|
+
and type-map entries when they build those widgets. Build the widget barrel
|
|
70
|
+
(`components/widget/index.ts`) exporting the foundation widgets only.
|
|
71
|
+
|
|
72
|
+
## models/objects.model.ts
|
|
73
|
+
|
|
74
|
+
This file holds foundation model types the shared components use (`SortType`, `KeyValue` → `dropdown`,
|
|
75
|
+
`AdvancedFilterType` → `filter-dictionary-input`, `FileType`, `MULTIMEDIA_DICTIONARY`,
|
|
76
|
+
`IModel3DObject`, `IAnimationObject`, …). Build it with those types. The one object-specific type,
|
|
77
|
+
`ISpecificObject extends MsObject`, is a feature type — the `mobilems-objects` skill adds it (with
|
|
78
|
+
the `MsObject` import) when it builds the objects module. Keep `models/index.ts` exporting
|
|
79
|
+
`./objects.model`.
|
|
80
|
+
|
|
81
|
+
## The rest of the lib
|
|
82
|
+
|
|
83
|
+
Build `services/`, `pipes/`, `directives/`, `tools/`, `models/` (incl. `data.interface.ts` with
|
|
84
|
+
`IObject`/`IArticle`/`IPartner`), `interceptors/`, `environments/` (with the `multimediaUrl` field —
|
|
85
|
+
see `server-env.md`), and `configs.module.ts`. These are foundation code. Confirm with the
|
|
86
|
+
build-safety grep below; if any of these files imports a feature package on your reference, it is
|
|
87
|
+
feature code and belongs to a module skill — leave it out.
|
|
88
|
+
|
|
89
|
+
## Barrels
|
|
90
|
+
|
|
91
|
+
- `src/index.ts` — the full barrel: re-export `./lib/components`, `./lib/services`,
|
|
92
|
+
`./lib/configs.module`, `./lib/models`, `./lib/pipes`, `./lib/tools`, `./lib/interceptors`,
|
|
93
|
+
`./lib/directives`, `./lib/environments`.
|
|
94
|
+
- `components/index.ts` — export the foundation shared components (and keep the `COMPONENTS` array,
|
|
95
|
+
which holds `AppComponent`/`FooterComponent`/`HeaderComponent`). Module skills add their feature
|
|
96
|
+
shared components' exports when they build them.
|
|
97
|
+
|
|
98
|
+
## Build-safety invariant
|
|
99
|
+
|
|
100
|
+
The foundation library must build with no feature package installed. The check is positive and
|
|
101
|
+
authoritative — this grep over the workspace's shared lib returns nothing:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
grep -rlnE "from '@[^']*/(objects|articles|public-collections)-(domain|shell-angular|shell-dtos)'" \
|
|
105
|
+
libs/shared/angular/src
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`verify-init.sh` runs it, plus checks the `widget` container imports no gallery-object/article and
|
|
109
|
+
`objects.model.ts` carries no `MsObject` import.
|
|
110
|
+
|
|
111
|
+
## project.json / build config
|
|
112
|
+
|
|
113
|
+
Keep the base's normalized `libs/shared/angular/project.json`, `ng-package.json`, `jest.config.ts`,
|
|
114
|
+
and tsconfigs (a full base already fixed the generator's `packages/shared/angular` leftover). Verify
|
|
115
|
+
`tsconfig.lib.json` keeps `"types": ["node"]` (the env code uses `process.env`). If the base's shared
|
|
116
|
+
lib is minimal and ships only `src/` (no `project.json`/`ng-package.json`/`jest.config.ts`/tsconfigs),
|
|
117
|
+
copy those from the reference and normalize their `dest`/`coverageDirectory`/paths to
|
|
118
|
+
`libs/shared/angular/...`.
|
|
119
|
+
|
|
120
|
+
## Thread the prefix everywhere — including the lib's own package.json
|
|
121
|
+
|
|
122
|
+
After copying, the only reference-prefix string left should be gone. Beyond the import statements,
|
|
123
|
+
remember to thread `<PREFIX>` in `libs/shared/angular/package.json` `"name"` (it reads
|
|
124
|
+
`@<reference-prefix>/angular` in the reference → set it to `@<PREFIX>/angular`). A workspace-wide grep
|
|
125
|
+
for the reference prefix (`@msr/`) should return nothing.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Tailwind (smart- prefix) layered onto scss
|
|
2
|
+
|
|
3
|
+
The base is scss-only. MobilEMS adds Tailwind with the **`smart-`** class prefix (so utilities read
|
|
4
|
+
`smart-flex`, `smart-container`, …) and a museum global stylesheet. Tailwind processes the scss via
|
|
5
|
+
postcss, so scss and Tailwind coexist — no migration.
|
|
6
|
+
|
|
7
|
+
## 1. `tailwind.config.js` (workspace root — create verbatim, no placeholders)
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
/** @type {import('tailwindcss').Config} */
|
|
11
|
+
module.exports = {
|
|
12
|
+
content: ['./apps/web/src/**/*.{html,ts}', './libs/**/*.{html,ts}'],
|
|
13
|
+
prefix: 'smart-',
|
|
14
|
+
darkMode: ['class', '[class~="contrast-high"]'],
|
|
15
|
+
theme: {
|
|
16
|
+
extend: {
|
|
17
|
+
colors: {
|
|
18
|
+
primary: {
|
|
19
|
+
DEFAULT: '#414141',
|
|
20
|
+
active: 'rgb(223 223 223)',
|
|
21
|
+
red: 'rgb(193 18 31)',
|
|
22
|
+
},
|
|
23
|
+
dark: {
|
|
24
|
+
DEFAULT: '#100f0d',
|
|
25
|
+
yellow: '#ffff00',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
gridTemplateColumns: {
|
|
29
|
+
'auto-fit': 'repeat(auto-fit, minmax(min(350px, 100%), 1fr))',
|
|
30
|
+
},
|
|
31
|
+
boxShadow: {
|
|
32
|
+
custom:
|
|
33
|
+
'0px 5px 5px rgba(0, 0, 0, 0.2), 0 10px 10px rgba(0, 0, 0, 0.1), 0 20px 20px rgba(0, 0, 0, 0.15)',
|
|
34
|
+
},
|
|
35
|
+
fontFamily: {
|
|
36
|
+
satoshi: [
|
|
37
|
+
'Satoshi',
|
|
38
|
+
'Satoshi Fallback',
|
|
39
|
+
'Satoshi Fallback Android',
|
|
40
|
+
'sans-serif',
|
|
41
|
+
],
|
|
42
|
+
oswald: [
|
|
43
|
+
'Oswald',
|
|
44
|
+
'Oswald Fallback',
|
|
45
|
+
'Oswald Fallback Android',
|
|
46
|
+
'sans-serif',
|
|
47
|
+
],
|
|
48
|
+
'fira-sans': [
|
|
49
|
+
'Fira Sans',
|
|
50
|
+
'Fira Sans Fallback',
|
|
51
|
+
'Fira Sans Fallback Android',
|
|
52
|
+
'sans-serif',
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
fontWeight: { extralight: '200' },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
plugins: [],
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
> The `content` globs already cover `libs/**`, so feature libs added later are picked up — no edit
|
|
63
|
+
> needed when modules arrive. `darkMode` keys off `contrast-high` for the WCAG high-contrast toggle.
|
|
64
|
+
|
|
65
|
+
> **QA gotcha (fonts):** when the project's design swaps in its own heading font, verify it covers
|
|
66
|
+
> Polish diacritics (`ą ć ę ł ń ś ź ż` + uppercase). Bacasime Antique, Prata and Italiana lack them
|
|
67
|
+
> — titles silently render in a fallback font and QA rejects it (verified replacements: Gilda
|
|
68
|
+
> Display, Playfair Display). Never leave a `fontFamily` entry as an empty array — it falls back to
|
|
69
|
+
> system sans. See `references/qa-hardening.md` §5.
|
|
70
|
+
|
|
71
|
+
## 2. postcss
|
|
72
|
+
|
|
73
|
+
Add Tailwind + autoprefixer to postcss. If the base has a `postcss.config.js`/`.json`, add the
|
|
74
|
+
plugins; otherwise create `postcss.config.js`:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
module.exports = {
|
|
78
|
+
plugins: {
|
|
79
|
+
tailwindcss: {},
|
|
80
|
+
autoprefixer: {},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Add `tailwindcss` + `autoprefixer` to `devDependencies` (resolve current lines via `npm view`). The
|
|
86
|
+
base `.eslintignore` already ignores `**/tailwind.config.js`.
|
|
87
|
+
|
|
88
|
+
## 3. `apps/web/src/styles.scss` (overwrite)
|
|
89
|
+
|
|
90
|
+
**Reference mode:** copy `apps/web/src/styles.scss` from the reference repo verbatim. It is ~500
|
|
91
|
+
lines of pure CSS/scss — font-face metric-override fallbacks (CLS mitigation), the Tailwind
|
|
92
|
+
directives (`@tailwind base/components/utilities`), the swiper + leaflet css imports, `.smart-container*`
|
|
93
|
+
layout utilities, WCAG body classes (`text-small`/`text-large`/letter+word spacing/grayscale),
|
|
94
|
+
masonry column widths, fullscreen-`dialog` rules, and the Windows `forced-colors` block. It targets
|
|
95
|
+
some feature selectors (e.g. `app-thematic-paths-menu`) but that is harmless — CSS for absent
|
|
96
|
+
elements is a no-op, so copy it whole.
|
|
97
|
+
|
|
98
|
+
**Foundation-only mode:** write a minimal `styles.scss` with just the Tailwind directives + the
|
|
99
|
+
`@import` lines for swiper/leaflet and the `.smart-container` utility; note the rest in the TODO doc.
|
|
100
|
+
|
|
101
|
+
The structure is:
|
|
102
|
+
|
|
103
|
+
```scss
|
|
104
|
+
@import 'swiper/swiper-bundle.css';
|
|
105
|
+
@import 'leaflet/dist/leaflet.css';
|
|
106
|
+
|
|
107
|
+
@tailwind base;
|
|
108
|
+
@tailwind components;
|
|
109
|
+
@tailwind utilities;
|
|
110
|
+
|
|
111
|
+
/* font-face fallbacks, @layer utilities { .smart-container … }, WCAG, masonry, dialogs, forced-colors */
|
|
112
|
+
```
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""classify-shared-components.py <shared-lib components dir>
|
|
3
|
+
|
|
4
|
+
Split a MobilEMS shared-lib `components` tree into FOUNDATION shared components (mobilems-project-init
|
|
5
|
+
builds these) and FEATURE-OWNED shared components (built by the owning mobilems-<module> skill,
|
|
6
|
+
because they depend on a feature package).
|
|
7
|
+
|
|
8
|
+
A "component" is a directory holding a non-spec *.component.ts. It is FEATURE-OWNED when it:
|
|
9
|
+
1. imports a feature package (objects/articles/public-collections/news/partners domain|shell,
|
|
10
|
+
or a @.../museum-<x> subpath), OR
|
|
11
|
+
2. imports another FEATURE-OWNED component — by relative path or by that component's exported
|
|
12
|
+
symbol (transitive, to a fixpoint).
|
|
13
|
+
|
|
14
|
+
Trust this over any hand-maintained table: if the reference repo diverges, this is authoritative.
|
|
15
|
+
Exit 0 on success; prints two sections (FEATURE-OWNED, FOUNDATION), one path per line, relative to <dir>.
|
|
16
|
+
"""
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
FEATURE_RE = re.compile(
|
|
22
|
+
r"(objects|articles|public-collections|news|partners)-(domain|shell-angular|shell-dtos)"
|
|
23
|
+
r"|/museum-[a-z-]+"
|
|
24
|
+
)
|
|
25
|
+
EXPORT_RE = re.compile(r"export\s+(?:class|const|interface|type)\s+([A-Za-z0-9_]+)")
|
|
26
|
+
IMPORT_LINE_RE = re.compile(r"^\s*import\b|from\s+['\"]")
|
|
27
|
+
REL_IMPORT_RE = re.compile(r"from\s+['\"](\.\.?/[^'\"]+)['\"]")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def unit_ts_files(unit):
|
|
31
|
+
return [
|
|
32
|
+
os.path.join(unit, f)
|
|
33
|
+
for f in os.listdir(unit)
|
|
34
|
+
if f.endswith(".ts") and not f.endswith(".spec.ts")
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def read(files):
|
|
39
|
+
out = []
|
|
40
|
+
for f in files:
|
|
41
|
+
try:
|
|
42
|
+
with open(f, encoding="utf-8") as fh:
|
|
43
|
+
out.append(fh.read())
|
|
44
|
+
except OSError:
|
|
45
|
+
pass
|
|
46
|
+
return "\n".join(out)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def main():
|
|
50
|
+
if len(sys.argv) != 2:
|
|
51
|
+
sys.exit("usage: classify-shared-components.py <shared-lib/src/lib/components>")
|
|
52
|
+
root = os.path.abspath(sys.argv[1])
|
|
53
|
+
if not os.path.isdir(root):
|
|
54
|
+
sys.exit(f"ERROR: not a directory: {root}")
|
|
55
|
+
|
|
56
|
+
# discover units (dirs with a non-spec *.component.ts)
|
|
57
|
+
units = sorted(
|
|
58
|
+
{
|
|
59
|
+
dirpath
|
|
60
|
+
for dirpath, _, files in os.walk(root)
|
|
61
|
+
if any(f.endswith(".component.ts") and not f.endswith(".spec.ts") for f in files)
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
text = {u: read(unit_ts_files(u)) for u in units}
|
|
66
|
+
imports = {u: "\n".join(l for l in text[u].splitlines() if IMPORT_LINE_RE.search(l)) for u in units}
|
|
67
|
+
exports = {
|
|
68
|
+
u: set(EXPORT_RE.findall(text[u])) for u in units
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
feature_owned = {u for u in units if FEATURE_RE.search(imports[u])}
|
|
72
|
+
|
|
73
|
+
changed = True
|
|
74
|
+
while changed:
|
|
75
|
+
changed = False
|
|
76
|
+
feature_owned_symbols = {}
|
|
77
|
+
for d in feature_owned:
|
|
78
|
+
for s in exports[d]:
|
|
79
|
+
feature_owned_symbols.setdefault(s, d)
|
|
80
|
+
for u in units:
|
|
81
|
+
if u in feature_owned:
|
|
82
|
+
continue
|
|
83
|
+
hit = False
|
|
84
|
+
# (a) relative import into a feature_owned unit's directory
|
|
85
|
+
for rel in REL_IMPORT_RE.findall(imports[u]):
|
|
86
|
+
target = os.path.normpath(os.path.join(u, rel))
|
|
87
|
+
if any(target == d or target.startswith(d + os.sep) for d in feature_owned):
|
|
88
|
+
hit = True
|
|
89
|
+
break
|
|
90
|
+
# (b) imports a symbol exported by a feature_owned unit
|
|
91
|
+
if not hit:
|
|
92
|
+
for s in feature_owned_symbols:
|
|
93
|
+
if re.search(r"\b" + re.escape(s) + r"\b", imports[u]):
|
|
94
|
+
hit = True
|
|
95
|
+
break
|
|
96
|
+
if hit:
|
|
97
|
+
feature_owned.add(u)
|
|
98
|
+
changed = True
|
|
99
|
+
|
|
100
|
+
def rel(u):
|
|
101
|
+
return os.path.relpath(u, root)
|
|
102
|
+
|
|
103
|
+
print("FEATURE-OWNED:")
|
|
104
|
+
for u in units:
|
|
105
|
+
if u in feature_owned:
|
|
106
|
+
print(" " + rel(u))
|
|
107
|
+
print("FOUNDATION:")
|
|
108
|
+
for u in units:
|
|
109
|
+
if u not in feature_owned:
|
|
110
|
+
print(" " + rel(u))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
main()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# detect-base.sh <workspace-root>
|
|
4
|
+
#
|
|
5
|
+
# Precondition gate for mobilems-project-init. Confirms the target is a scaffold-nx-workspace BASE
|
|
6
|
+
# (generic Nx + Angular-SSR shell) that has NOT yet had the MobilEMS delta applied. Portable to the
|
|
7
|
+
# macOS system bash (3.2) — no bash-4 features.
|
|
8
|
+
#
|
|
9
|
+
# Exit 0 → base detected, OK to apply the delta.
|
|
10
|
+
# Exit 3 → no base shell found (tell the user to run /smart:scaffold-nx-workspace first).
|
|
11
|
+
# Exit 4 → MobilEMS delta already present (init looks already applied).
|
|
12
|
+
set -eu
|
|
13
|
+
|
|
14
|
+
ROOT="${1:?usage: detect-base.sh <workspace-root>}"
|
|
15
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
16
|
+
|
|
17
|
+
fail_no_base() {
|
|
18
|
+
echo "NOT A BASE: $1" >&2
|
|
19
|
+
echo "Run /smart:scaffold-nx-workspace first to create the generic Nx + Angular-SSR shell." >&2
|
|
20
|
+
exit 3
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# --- base shell signals ---
|
|
24
|
+
[ -f apps/web/src/server.ts ] || fail_no_base "apps/web/src/server.ts missing"
|
|
25
|
+
[ -f apps/web/src/app/app.config.ts ] || fail_no_base "apps/web/src/app/app.config.ts missing"
|
|
26
|
+
[ -f apps/web/src/app/app.routes.ts ] || fail_no_base "apps/web/src/app/app.routes.ts missing"
|
|
27
|
+
[ -f libs/shared/angular/src/index.ts ] || fail_no_base "libs/shared/angular/src/index.ts missing"
|
|
28
|
+
[ -f tsconfig.base.json ] || fail_no_base "tsconfig.base.json missing"
|
|
29
|
+
|
|
30
|
+
# base app.routes.ts has no feature loadChildren yet
|
|
31
|
+
if grep -qE "loadChildren" apps/web/src/app/app.routes.ts; then
|
|
32
|
+
echo "WARNING: app.routes.ts already has loadChildren — base may already be extended." >&2
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# --- already-applied signals (any one → likely already MobilEMS) ---
|
|
36
|
+
applied=0
|
|
37
|
+
[ -f tailwind.config.js ] && grep -q "smart-" tailwind.config.js 2>/dev/null && applied=1
|
|
38
|
+
grep -q "@smartsoft001-mobilems/angular" package.json 2>/dev/null && applied=1
|
|
39
|
+
[ -d libs/museum-objects ] && applied=1
|
|
40
|
+
if [ "$applied" = 1 ]; then
|
|
41
|
+
echo "ALREADY APPLIED: MobilEMS signals found (tailwind smart- prefix / mobilems dep / libs/museum-*)." >&2
|
|
42
|
+
echo "If you meant to re-run, revert the delta first." >&2
|
|
43
|
+
exit 4
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# --- report the detected prefix (from the single tsconfig path) ---
|
|
47
|
+
PREFIX="$(grep -oE '@[a-z0-9-]+/angular' tsconfig.base.json | head -1 | sed -E 's#@([a-z0-9-]+)/angular#\1#')"
|
|
48
|
+
echo "BASE OK. Detected prefix: ${PREFIX:-<unknown — inspect tsconfig.base.json paths>}"
|
|
49
|
+
exit 0
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# verify-init.sh <workspace-root> <PREFIX>
|
|
4
|
+
#
|
|
5
|
+
# Post-condition check for mobilems-project-init. Asserts the MobilEMS delta landed AND the hard
|
|
6
|
+
# foundation is self-contained (no feature modules / no feature routes / shared lib imports no
|
|
7
|
+
# feature package).
|
|
8
|
+
# Portable to macOS system bash (3.2). Exit 0 = all pass; exit 1 = one or more checks failed.
|
|
9
|
+
set -eu
|
|
10
|
+
|
|
11
|
+
ROOT="${1:?usage: verify-init.sh <workspace-root> <PREFIX>}"
|
|
12
|
+
PREFIX="${2:?usage: verify-init.sh <workspace-root> <PREFIX>}"
|
|
13
|
+
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
14
|
+
|
|
15
|
+
fail=0
|
|
16
|
+
pass() { echo " PASS: $1"; }
|
|
17
|
+
bad() { echo " FAIL: $1" >&2; fail=1; }
|
|
18
|
+
|
|
19
|
+
echo "== Delta applied =="
|
|
20
|
+
[ -f tailwind.config.js ] && grep -q "prefix: 'smart-'" tailwind.config.js && pass "tailwind smart- prefix" || bad "tailwind.config.js with smart- prefix"
|
|
21
|
+
grep -q "@smartsoft001-mobilems/angular" package.json && pass "mobilems framework dep" || bad "@smartsoft001-mobilems/angular in package.json"
|
|
22
|
+
grep -q "@ngrx/store" package.json && pass "ngrx dep" || bad "@ngrx/store in package.json"
|
|
23
|
+
grep -q "SharedModule.forRoot" apps/web/src/app/app.config.ts && pass "SharedModule.forRoot in app.config" || bad "SharedModule.forRoot in app.config.ts"
|
|
24
|
+
grep -q "SEARCH_CONFIG_TOKEN" apps/web/src/app/app.config.ts && pass "SEARCH_CONFIG_TOKEN" || bad "SEARCH_CONFIG_TOKEN in app.config.ts"
|
|
25
|
+
grep -q "multimediaUrl" apps/web/src/server.ts && pass "multimediaUrl in server.ts" || bad "multimediaUrl in server.ts"
|
|
26
|
+
grep -q "/api/translations" apps/web/src/server.ts && pass "/api/translations endpoint" || bad "/api/translations in server.ts"
|
|
27
|
+
|
|
28
|
+
echo "== QA hardening (references/qa-hardening.md) =="
|
|
29
|
+
# dynamic sitemap/robots endpoints (static copies shipped another museum's domain in past projects)
|
|
30
|
+
grep -q "sitemap.xml" apps/web/src/server.ts && pass "dynamic /sitemap.xml endpoint" || bad "/sitemap.xml endpoint in server.ts (SITE_URL-driven, never a static copy)"
|
|
31
|
+
grep -q "robots.txt" apps/web/src/server.ts && pass "dynamic /robots.txt endpoint" || bad "/robots.txt endpoint in server.ts (SITE_URL-driven, never a static copy)"
|
|
32
|
+
if ls apps/web/public/favicon.ico apps/web/src/favicon.ico >/dev/null 2>&1; then pass "favicon present"; else bad "favicon.ico missing (QA bug 'Brak ikony' in two projects)"; fi
|
|
33
|
+
# an empty fontFamily array silently falls back to system sans (QA-rejected typography)
|
|
34
|
+
if grep -qE ":\s*\[\s*\]" tailwind.config.js 2>/dev/null; then bad "empty fontFamily array in tailwind.config.js (heading font falls back to system sans)"; else pass "no empty fontFamily entries"; fi
|
|
35
|
+
grep -q "./lib/components" libs/shared/angular/src/index.ts && pass "shared-lib components barrel populated" || bad "components barrel in shared-lib index.ts"
|
|
36
|
+
[ -f .claude/settings.json ] && grep -q "flow@mobilems" .claude/settings.json && pass "flow@mobilems enabled" || bad "flow@mobilems in .claude/settings.json"
|
|
37
|
+
|
|
38
|
+
echo "== Foundation scope (no feature modules yet) =="
|
|
39
|
+
if ls -d libs/museum-* >/dev/null 2>&1; then bad "libs/museum-* present (feature modules are built by the module skills)"; else pass "no libs/museum-*"; fi
|
|
40
|
+
# feature routes reference a museum-* lib; loadChildren + the import often span lines, so match museum- anywhere
|
|
41
|
+
if grep -qE "museum-" apps/web/src/app/app.routes.ts; then bad "feature route (museum-*) in app.routes.ts"; else pass "no feature routes"; fi
|
|
42
|
+
|
|
43
|
+
echo "== Foundation shared library is self-contained =="
|
|
44
|
+
COMP=libs/shared/angular/src/lib/components
|
|
45
|
+
# the widget container registers only foundation widgets (feature gallery widgets are added by module skills)
|
|
46
|
+
WID="$COMP/widget/widget.component.ts"
|
|
47
|
+
if [ -f "$WID" ]; then
|
|
48
|
+
if grep -qE "WidgetGalleryObjectComponent|WidgetGalleryArticleComponent" "$WID"; then
|
|
49
|
+
bad "widget container references a feature gallery widget — build it with foundation widgets only"
|
|
50
|
+
else
|
|
51
|
+
pass "widget container has foundation widgets only"
|
|
52
|
+
fi
|
|
53
|
+
else
|
|
54
|
+
bad "widget container missing"
|
|
55
|
+
fi
|
|
56
|
+
# objects.model holds only the foundation model types (the object-specific ISpecificObject is feature code)
|
|
57
|
+
OBJMODEL=libs/shared/angular/src/lib/models/objects.model.ts
|
|
58
|
+
if [ -f "$OBJMODEL" ]; then
|
|
59
|
+
grep -qE "from '[^']*objects-domain'" "$OBJMODEL" && bad "objects.model imports MsObject — foundation holds only the generic types" || pass "objects.model holds foundation types only"
|
|
60
|
+
grep -qE "AdvancedFilterType|KeyValue" "$OBJMODEL" && pass "foundation model types present" || bad "foundation model types missing from objects.model.ts"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
echo "== Build-safety invariant: shared lib imports no feature package =="
|
|
64
|
+
LEAK="$(grep -rlnE "from '@[^']*/(objects|articles|public-collections)-(domain|shell-angular|shell-dtos)'" libs/shared/angular/src 2>/dev/null || true)"
|
|
65
|
+
if [ -n "$LEAK" ]; then
|
|
66
|
+
bad "shared lib still imports a feature package — foundation will not build without it:"
|
|
67
|
+
printf ' %s\n' $LEAK >&2
|
|
68
|
+
else
|
|
69
|
+
pass "shared lib imports no feature package"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo "== Prefix threaded =="
|
|
73
|
+
if grep -rq "@msr/" libs/shared/angular/src apps/web/src 2>/dev/null && [ "$PREFIX" != "msr" ]; then
|
|
74
|
+
bad "stray @msr/ imports (prefix not fully threaded to @$PREFIX/)"
|
|
75
|
+
else
|
|
76
|
+
pass "no stray reference-prefix imports"
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
echo
|
|
80
|
+
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
81
|
+
exit "$fail"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-thematic-publications
|
|
3
|
+
description: Add the museum THEMATIC PATHS / PUBLICATIONS feature module (ścieżki tematyczne) to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the thematic paths / thematic publications module, the "ścieżki tematyczne" pages (list + paged publication reader with widgets), or asks to "dodaj moduł ścieżek tematycznych / publikacji tematycznych". Creates libs/museum-thematic-paths (domain + shell/angular), registers the /sciezki-tematyczne route, and adds the Articles npm packages it reuses. Requires the MobilEMS foundation to exist first.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MobilEMS thematic paths / publications module
|
|
9
|
+
|
|
10
|
+
Add the **thematic paths / publications** (ścieżki tematyczne) feature module on top of a MobilEMS
|
|
11
|
+
foundation produced by `mobilems-project-init`. This module's entire UI lives inside its own
|
|
12
|
+
`libs/museum-thematic-paths` library, so this skill builds that library and registers a single
|
|
13
|
+
top-level route.
|
|
14
|
+
|
|
15
|
+
> **Naming heads-up.** This skill is named _thematic-publications_ (the museum-domain term users say),
|
|
16
|
+
> but the artifact it generates is **thematic-paths**: folder `libs/museum-thematic-paths`, class
|
|
17
|
+
> `MuseumThematicPathsModule`, route `sciezki-tematyczne`, entity `'thematicPaths'`. Always emit the
|
|
18
|
+
> _thematic-paths_ names — "publications" is only the spoken alias.
|
|
19
|
+
|
|
20
|
+
The module is a list/detail feature where each entry is a multi-page publication: a `ListComponent`
|
|
21
|
+
of thematic paths plus an `ItemComponent` that renders a paged reader (intro, menu, page, widgets —
|
|
22
|
+
page-header, text, image, video, audio, image-gallery) backed by `ArticlesSharedModule.forFeature`
|
|
23
|
+
over the `MuseumThematicPath` entity. It REUSES the Articles shared module (like news) but with its
|
|
24
|
+
OWN domain entity, its OWN reducer (`getReducer`), and **no `baseQuery`**.
|
|
25
|
+
|
|
26
|
+
## Precondition
|
|
27
|
+
|
|
28
|
+
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
29
|
+
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
30
|
+
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
|
|
31
|
+
`@msr`) and the reference repo path (same one init used).
|
|
32
|
+
|
|
33
|
+
## Steps
|
|
34
|
+
|
|
35
|
+
1. **npm deps** — add `@smartsoft001-mobilems/articles-domain`,
|
|
36
|
+
`@smartsoft001-mobilems/articles-shell-angular`, `@smartsoft001-mobilems/articles-shell-dtos`
|
|
37
|
+
(this module reuses the Articles shared module — `ArticlesSharedModule` + `ArticlesService`).
|
|
38
|
+
Match the installed mobilems line; resolve versions at run time; do not `npm install` unless
|
|
39
|
+
asked. Adding is idempotent — if `mobilems-articles` already installed these, nothing changes.
|
|
40
|
+
2. **Library** — create `libs/museum-thematic-paths/{domain,shell/angular}` per
|
|
41
|
+
`references/lib-contents.md`: copy the domain entity/interfaces/enums and the shell
|
|
42
|
+
(`MuseumThematicPathsModule`, routes, `+state/*`, `components/*`, `pages/{list,item}`, `services`,
|
|
43
|
+
`directives`), thread `<PREFIX>`. Add the two `tsconfig.base.json` paths and the
|
|
44
|
+
`museum-thematic-paths` commit scope.
|
|
45
|
+
3. **Module + routes** — see `references/module-and-routes.md`: `MuseumThematicPathsModule` wraps
|
|
46
|
+
`ArticlesSharedModule.forFeature({ routing: false, config: { reducerFactory: getReducer, type: MuseumThematicPath, apiUrl, entity: 'thematicPaths' } })`
|
|
47
|
+
(note: **no baseQuery**) with the `Masonry` SSR factory and provides `ThematicPathsFacade` +
|
|
48
|
+
`SERVICES`. Add the lazy `sciezki-tematyczne` route to the `appRoutes` array in
|
|
49
|
+
`apps/web/src/app/app.routes.ts` as a single top-level route.
|
|
50
|
+
4. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib + entity
|
|
51
|
+
exist, the tsconfig path is added, `ArticlesSharedModule.forFeature` is wired with
|
|
52
|
+
`entity: 'thematicPaths'` + `MuseumThematicPath` + `reducerFactory`, `ThematicPathsFacade` is
|
|
53
|
+
provided, the `+state` files are present, the `sciezki-tematyczne` route is registered as a single
|
|
54
|
+
top-level route, and the three Articles deps are in `package.json`. Fix and re-run.
|
|
55
|
+
|
|
56
|
+
## Notes
|
|
57
|
+
|
|
58
|
+
- This skill ONLY touches the thematic-paths module + its route. It never adds other feature modules,
|
|
59
|
+
never re-runs the foundation, and never edits `libs/shared/angular`.
|
|
60
|
+
- Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
|