@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.
Files changed (47) hide show
  1. package/package.json +1 -1
  2. package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
  3. package/plugins/mobilems/skills/mobilems-articles/SKILL.md +108 -0
  4. package/plugins/mobilems/skills/mobilems-articles/references/lib-contents.md +164 -0
  5. package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +146 -0
  6. package/plugins/mobilems/skills/mobilems-articles/references/shared-components.md +56 -0
  7. package/plugins/mobilems/skills/mobilems-articles/scripts/detect-init.sh +29 -0
  8. package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +61 -0
  9. package/plugins/mobilems/skills/mobilems-events/SKILL.md +65 -0
  10. package/plugins/mobilems/skills/mobilems-events/references/lib-contents.md +212 -0
  11. package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +113 -0
  12. package/plugins/mobilems/skills/mobilems-events/scripts/detect-init.sh +29 -0
  13. package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +59 -0
  14. package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +77 -0
  15. package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +76 -0
  16. package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +116 -0
  17. package/plugins/mobilems/skills/mobilems-object-collections/references/shared-components.md +61 -0
  18. package/plugins/mobilems/skills/mobilems-object-collections/scripts/detect-init.sh +29 -0
  19. package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +44 -0
  20. package/plugins/mobilems/skills/mobilems-objects/SKILL.md +80 -0
  21. package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +68 -0
  22. package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +108 -0
  23. package/plugins/mobilems/skills/mobilems-objects/references/shared-components.md +69 -0
  24. package/plugins/mobilems/skills/mobilems-objects/scripts/detect-init.sh +29 -0
  25. package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +51 -0
  26. package/plugins/mobilems/skills/mobilems-partners/SKILL.md +65 -0
  27. package/plugins/mobilems/skills/mobilems-partners/references/lib-contents.md +160 -0
  28. package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +125 -0
  29. package/plugins/mobilems/skills/mobilems-partners/scripts/detect-init.sh +29 -0
  30. package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +59 -0
  31. package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +151 -0
  32. package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +115 -0
  33. package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +44 -0
  34. package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +203 -0
  35. package/plugins/mobilems/skills/mobilems-project-init/references/npm-deps.md +66 -0
  36. package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +124 -0
  37. package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +97 -0
  38. package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +125 -0
  39. package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +112 -0
  40. package/plugins/mobilems/skills/mobilems-project-init/scripts/classify-shared-components.py +114 -0
  41. package/plugins/mobilems/skills/mobilems-project-init/scripts/detect-base.sh +49 -0
  42. package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +81 -0
  43. package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +60 -0
  44. package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +134 -0
  45. package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +107 -0
  46. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/detect-init.sh +29 -0
  47. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +56 -0
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: mobilems-partners
3
+ description: Add the museum PARTNERS feature module (partnerzy) to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the partners (partnerzy) module, the museum partners list + detail pages (partner cards, map, connected objects), or asks to "dodaj moduł partnerów / partnerzy". Creates libs/museum-partners (domain + shell/angular), registers the /partnerzy route (top-level + search child), and adds the Articles + objects-domain 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 partners module
9
+
10
+ Add the **partners** (partnerzy) feature module on top of a MobilEMS foundation produced by
11
+ `mobilems-project-init`. This module's entire UI lives inside its own `libs/museum-partners`
12
+ library, so this skill builds that library and registers the `partnerzy` route.
13
+
14
+ Partners is a list/detail feature where each entry is a partnering institution: a `ListComponent`
15
+ with grid/list partner cards (sortable, paged, masonry layout) plus an `ItemComponent` that renders
16
+ the partner detail (logo, description, map location, and the museum objects connected to that
17
+ partner). It is backed by `ArticlesSharedModule.forFeature` over the Articles `Article` type — it
18
+ REUSES the Articles shared module (like the thematic-paths module) but with its OWN domain entity
19
+ (`MuseumPartner`), its OWN reducer (`getReducer`), and **no `baseQuery`**.
20
+
21
+ ## Precondition
22
+
23
+ Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
24
+ (`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
25
+ run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
26
+ `@msr`) and the reference repo path (same one init used).
27
+
28
+ ## Steps
29
+
30
+ 1. **npm deps** — add `@smartsoft001-mobilems/articles-domain`,
31
+ `@smartsoft001-mobilems/articles-shell-angular`, `@smartsoft001-mobilems/articles-shell-dtos`
32
+ (this module reuses the Articles shared module — `ArticlesSharedModule` + `ArticlesService`) and
33
+ `@smartsoft001-mobilems/objects-domain` (the `MuseumPartner` entity types its `connections` with
34
+ `IMsObjectConnection` from objects-domain). Also add `lodash-decorators` (the list page uses
35
+ `@debounce` for its date filter). Grep the copied pages/components for any other third-party
36
+ imports and add those too. Match the installed mobilems line; resolve versions at run time; do
37
+ not `npm install` unless asked. Adding is idempotent — if a sibling module already installed
38
+ these, nothing changes. (`@smartsoft001-mobilems/{angular,models}` and
39
+ `@thisissoon/angular-masonry` are already foundation deps.)
40
+ 2. **Library** — create `libs/museum-partners/{domain,shell/angular}` per
41
+ `references/lib-contents.md`: copy the domain entity (`MuseumPartner`) and the shell
42
+ (`MuseumPartnersModule`, routes, `+state/partners.reducer.ts`, `components/{grid-item,list-item}`,
43
+ `pages/{list,item}`, `services/partners`), thread `<PREFIX>`. Add the two `tsconfig.base.json`
44
+ paths and the `museum-partners` commit scope.
45
+ 3. **Module + routes** — see `references/module-and-routes.md`: `MuseumPartnersModule` wraps
46
+ `ArticlesSharedModule.forFeature({ routing: false, config: { reducerFactory: getReducer, type: Article, apiUrl, entity: 'partners' } })`
47
+ (note: **no baseQuery**) with the `Masonry` SSR factory and provides `SERVICES`. Add the lazy
48
+ `partnerzy` route to the `appRoutes` array in `apps/web/src/app/app.routes.ts` as a top-level
49
+ route, and a matching child under the `wyszukaj/:text` search route (partners appears in site
50
+ search).
51
+ 4. **Verify** — run `scripts/verify-module.sh <workspace-root> <PREFIX>`: asserts the lib + entity
52
+ exist, both tsconfig paths are added, `ArticlesSharedModule.forFeature` is wired with
53
+ `entity: 'partners'` + `reducerFactory: getReducer`, the `+state/partners.reducer.ts` exports
54
+ `getReducer`, the `components/{grid-item,list-item}` cards are present in the partners lib, the
55
+ `partnerzy` route is registered (top-level + search child), and the Articles + objects-domain deps
56
+ are in `package.json`. Fix and re-run.
57
+
58
+ ## Notes
59
+
60
+ - Partners contributes nothing to `libs/shared/angular`: its grid/list cards live in its own lib, and
61
+ the `partners-carousel` foundation component is already built by `mobilems-project-init`. So this
62
+ skill builds only the partners module + its route — there is no shared-components reference.
63
+ - This skill ONLY touches the partners module + its route. It never adds other feature modules,
64
+ never re-runs the foundation, and never edits `libs/shared/angular`.
65
+ - Bundled `references/` + `scripts/` ship automatically. Do not hand-edit `plugin.json`.
@@ -0,0 +1,160 @@
1
+ # libs/museum-partners — structure to create
2
+
3
+ Copy from the reference MobilEMS app's `libs/museum-partners` and thread `<PREFIX>`. The lib has two
4
+ projects: a `domain` (the entity) and a `shell/angular` (the Angular module + NgRx `+state` + UI).
5
+
6
+ ```
7
+ libs/museum-partners/
8
+ ├── domain/ # project: museum-partners-domain
9
+ │ ├── package.json # "name": "museum-partners-domain"
10
+ │ ├── jest.config.js # displayName + coverageDirectory under libs/museum-partners/domain
11
+ │ └── src/
12
+ │ ├── index.ts # export * from './lib/entities';
13
+ │ ├── features.ts # export const DOMAIN_SERVICES = [];
14
+ │ └── lib/
15
+ │ └── entities/ # museum-partner.entity.ts (MuseumPartner) + index
16
+ └── shell/angular/ # project: museum-partners-shell-angular
17
+ └── src/
18
+ ├── index.ts # export * from './lib/pages'; export * from './lib/partners.module';
19
+ └── lib/
20
+ ├── index.ts # export * from './pages'; './components'; './partners.module';
21
+ ├── partners.module.ts # MuseumPartnersModule (references/module-and-routes.md)
22
+ ├── partners.routes.ts
23
+ ├── +state/ # partners.reducer.ts (getReducer)
24
+ ├── components/ # grid-item (GridItemComponent), list-item (ListItemComponent) + index
25
+ ├── pages/ # list (ListComponent), item (ItemComponent) + index
26
+ └── services/ # partners/partners.service.ts + index (SERVICES)
27
+ ```
28
+
29
+ > The partner cards (`grid-item`, `list-item`) live in **this** lib, not in `libs/shared/angular`.
30
+ > Copy only what the reference has — there is no `+state/*.facade.ts`/`*.selectors.ts` and no
31
+ > `directives/` dir in this module; the only `+state` file is `partners.reducer.ts`.
32
+
33
+ ## Domain entity (verbatim — thread nothing; pure types)
34
+
35
+ This entity `implements IEntity<string>` — it does NOT extend the Articles `Article` base class. It
36
+ pulls `MsFile` from `@smartsoft001-mobilems/models` and **`IMsObjectConnection` from
37
+ `@smartsoft001-mobilems/objects-domain`** (the cross-module type that drives the SKILL.md
38
+ objects-domain dep). Copy `domain/src/lib/entities/museum-partner.entity.ts` verbatim:
39
+
40
+ ```ts
41
+ import { IEntity } from '@smartsoft001/domain-core';
42
+ import { Model, Field } from '@smartsoft001/models';
43
+ import { MsFile } from '@smartsoft001-mobilems/models';
44
+ import { IMsObjectConnection } from '@smartsoft001-mobilems/objects-domain';
45
+
46
+ @Model({})
47
+ export class MuseumPartner implements IEntity<string> {
48
+ id!: string;
49
+
50
+ @Field()
51
+ name!: string;
52
+
53
+ @Field()
54
+ additionalInfo!: string;
55
+
56
+ @Field()
57
+ geoLocation!: any;
58
+
59
+ @Field()
60
+ latitude!: number;
61
+
62
+ @Field()
63
+ longitude!: number;
64
+
65
+ @Field()
66
+ objectCount!: number;
67
+
68
+ @Field()
69
+ extraData!: {
70
+ name: string;
71
+ phone: string;
72
+ emailAddress?: string;
73
+ description: string;
74
+ shortDescription: string;
75
+ address: string;
76
+ openingHours: number;
77
+ ticketPrices: number;
78
+ logo: MsFile;
79
+ image: MsFile;
80
+ website: string;
81
+ };
82
+
83
+ @Field()
84
+ connections?: Array<IMsObjectConnection>;
85
+ }
86
+ ```
87
+
88
+ ## Shell `+state` (NgRx — copy verbatim, thread `<PREFIX>`)
89
+
90
+ - `partners.reducer.ts` exports `getReducer()` — wraps `@smartsoft001/crud-shell-angular`'s
91
+ `getReducer('partners')` and returns a `(state, action) => …` reducer. This is the function passed
92
+ as `reducerFactory` in the module config. The reducer's `State` type is `SmartState<Article>`
93
+ (from `@smartsoft001-mobilems/articles-domain`), since the module is configured over `Article`.
94
+
95
+ ## Services — `services/index.ts` exports a `SERVICES` array (copy verbatim)
96
+
97
+ `PartnersService extends CrudBaseService<MuseumPartner>` (from `@smartsoft001-mobilems/angular`) and
98
+ points its list/detail URLs at the partners (`dictionary/Department`) endpoints, enriching list items
99
+ with geodata. It is bound to BOTH `CrudService` and `ArticlesService` (the reused Articles shared
100
+ module resolves `ArticlesService`):
101
+
102
+ ```ts
103
+ import { CrudService } from '@smartsoft001/crud-shell-angular';
104
+ import { ArticlesService } from '@smartsoft001-mobilems/articles-shell-angular';
105
+
106
+ import { PartnersService } from './partners/partners.service';
107
+
108
+ export * from './partners/partners.service';
109
+
110
+ export const SERVICES = [
111
+ PartnersService,
112
+ {
113
+ provide: CrudService,
114
+ useClass: PartnersService,
115
+ },
116
+ {
117
+ provide: ArticlesService,
118
+ useClass: PartnersService,
119
+ },
120
+ ];
121
+ ```
122
+
123
+ ## UI components & pages
124
+
125
+ - `components/grid-item` (`GridItemComponent`, selector `app-partners-grid-item`) and
126
+ `components/list-item` (`ListItemComponent`, selector `app-partners-list-item`) are the partner
127
+ cards used by the list page; `components/index.ts` re-exports both. Copy verbatim, thread
128
+ `<PREFIX>`.
129
+ - `pages/list` (`ListComponent extends ArticlesListComponent`) renders the sortable/paged masonry
130
+ grid; it uses `@debounce` from `lodash-decorators` (the SKILL.md dep) for its date filter.
131
+ - `pages/item` (`ItemComponent extends ArticlesItemComponent`) renders the partner detail with map
132
+ and connected objects (it composes foundation components from `@<PREFIX>/angular` such as
133
+ `MapComponent` and `ObjectsGridItemComponent`).
134
+
135
+ ## Wiring
136
+
137
+ - **tsconfig.base.json paths** (add both):
138
+ ```json
139
+ "@<PREFIX>/museum-partners/domain": ["libs/museum-partners/domain/src/index.ts"],
140
+ "@<PREFIX>/museum-partners/shell/angular": ["libs/museum-partners/shell/angular/src/index.ts"]
141
+ ```
142
+ - **project.json / build config** — copy each project's config (names `museum-partners-domain`,
143
+ `museum-partners-shell-angular`) and normalize every path field the reference baked to its origin
144
+ location — `sourceRoot`, `dest`, `outputPath`, `coverageDirectory`, `jestConfig`,
145
+ `lintFilePatterns`, and the jest `displayName` — to `libs/museum-partners/...`. Some reference
146
+ fields are stale and need fixing: the shell `project.json` `sourceRoot` reads
147
+ `libs/museum-partners/src` (correct it to `libs/museum-partners/shell/angular/src`), and the shell
148
+ `jest.config.ts` `coverageDirectory` points at a sibling's `coverage/packages/...` path (correct it
149
+ to `coverage/libs/museum-partners/shell/angular`); the domain `package.json` `name` reads
150
+ `museum-objects-domain` (correct it to `museum-partners-domain`). Others are already correct (the
151
+ domain `jest.config.js` `displayName`/`coverageDirectory` are right) — verify each against a
152
+ known-good sibling and fix only the stale ones. A `domain` with no `project.json` (Nx-inferred) is
153
+ fine — just set its `package.json` `name`.
154
+ - **commit scope** — `commitlint.config.js` reads `libs/` dir names, so `museum-partners` becomes a
155
+ valid scope automatically; no edit needed.
156
+
157
+ This module reuses the Articles shared module, so it needs the three `@smartsoft001-mobilems/articles-*`
158
+ npm packages plus `@smartsoft001-mobilems/objects-domain` (for `IMsObjectConnection`) and
159
+ `lodash-decorators` (see SKILL.md step 1). `@angular/cdk`, `@thisissoon/angular-masonry`,
160
+ `@ngx-translate/core`, and `@smartsoft001-mobilems/{angular,models}` are already foundation deps.
@@ -0,0 +1,125 @@
1
+ # MuseumPartnersModule + route registration
2
+
3
+ ## `libs/museum-partners/shell/angular/src/lib/partners.module.ts`
4
+
5
+ Thread `<PREFIX>` (the `environment` import). This module REUSES `ArticlesSharedModule` (the same
6
+ shared module the news/articles/thematic-paths modules use) but configures it over the Articles
7
+ `Article` type with its OWN `getReducer` and **no `baseQuery`**. The `Masonry` factory returns the
8
+ real library in the browser and a no-op mock during SSR (identical to objects). It provides the
9
+ `SERVICES` array (which binds both `CrudService` and `ArticlesService` to `PartnersService`).
10
+
11
+ ```ts
12
+ import { NgModule } from '@angular/core';
13
+ import { FormsModule } from '@angular/forms';
14
+ import { RouterModule } from '@angular/router';
15
+ import { Article } from '@smartsoft001-mobilems/articles-domain';
16
+ import { ArticlesSharedModule } from '@smartsoft001-mobilems/articles-shell-angular';
17
+ import { Masonry } from '@thisissoon/angular-masonry';
18
+
19
+ import { environment } from '@<PREFIX>/angular';
20
+
21
+ import { getReducer } from './+state/partners.reducer';
22
+ import { routes } from './partners.routes';
23
+ import { SERVICES } from './services';
24
+
25
+ @NgModule({
26
+ providers: [
27
+ ...SERVICES,
28
+ {
29
+ provide: Masonry,
30
+ useFactory: () => {
31
+ if (typeof window !== 'undefined' && (window as any)['Masonry']) {
32
+ return (window as any)['Masonry'];
33
+ }
34
+ // Return a mock for SSR
35
+ return class {
36
+ constructor() {
37
+ // no-op
38
+ }
39
+ layout() {
40
+ return;
41
+ }
42
+ reloadItems() {
43
+ return;
44
+ }
45
+ destroy() {
46
+ return;
47
+ }
48
+ };
49
+ },
50
+ },
51
+ ],
52
+ imports: [
53
+ FormsModule,
54
+ ArticlesSharedModule.forFeature({
55
+ routing: false,
56
+ config: {
57
+ reducerFactory: getReducer,
58
+ type: Article,
59
+ apiUrl: environment.apiUrl,
60
+ entity: 'partners',
61
+ },
62
+ }),
63
+ RouterModule.forChild(routes),
64
+ ],
65
+ })
66
+ export class MuseumPartnersModule {}
67
+ ```
68
+
69
+ > Contrast with `mobilems-objects` (`ObjectsSharedModule.forFeature` over `MsObject`, no
70
+ > `reducerFactory`): here the module reuses `ArticlesSharedModule`, supplies `type: Article` with
71
+ > `reducerFactory: getReducer`, and there is deliberately **no `baseQuery`** in the config.
72
+
73
+ ## `partners.routes.ts`
74
+
75
+ ```ts
76
+ import { Route } from '@angular/router';
77
+
78
+ import { ItemComponent, ListComponent } from './pages';
79
+
80
+ export const routes: Route[] = [
81
+ {
82
+ path: ':id',
83
+ component: ItemComponent,
84
+ },
85
+ {
86
+ path: ':id/:slug',
87
+ component: ItemComponent,
88
+ },
89
+ {
90
+ path: '',
91
+ component: ListComponent,
92
+ },
93
+ ];
94
+ ```
95
+
96
+ ## Register in `apps/web/src/app/app.routes.ts`
97
+
98
+ Add the partners route to the `appRoutes` array (idempotently — if a `partnerzy` route already
99
+ exists, skip). Add it as a top-level route, and as a child of the `wyszukaj/:text` search route so
100
+ `/wyszukaj/:text/partnerzy` works and partners shows up among the search results.
101
+
102
+ Top-level — add to `appRoutes`:
103
+
104
+ ```ts
105
+ {
106
+ path: 'partnerzy',
107
+ loadChildren: () =>
108
+ import('@<PREFIX>/museum-partners/shell/angular').then(
109
+ (m) => m.MuseumPartnersModule,
110
+ ),
111
+ },
112
+ ```
113
+
114
+ Search child — add to the `children` of the `wyszukaj/:text` route, before the
115
+ `{ path: '', redirectTo: 'obiekty' }` default:
116
+
117
+ ```ts
118
+ {
119
+ path: 'partnerzy',
120
+ loadChildren: () =>
121
+ import('@<PREFIX>/museum-partners/shell/angular').then(
122
+ (m) => m.MuseumPartnersModule,
123
+ ),
124
+ },
125
+ ```
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # detect-init.sh <workspace-root>
4
+ #
5
+ # Precondition gate for mobilems-objects (and the other module skills follow the same shape):
6
+ # confirm the MobilEMS foundation from mobilems-project-init is present. Portable to macOS bash 3.2.
7
+ #
8
+ # Exit 0 → foundation present, OK to add the module.
9
+ # Exit 3 → foundation missing (tell the user to run /mobilems:project-init first).
10
+ set -eu
11
+
12
+ ROOT="${1:?usage: detect-init.sh <workspace-root>}"
13
+ cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
14
+
15
+ miss() {
16
+ echo "FOUNDATION MISSING: $1" >&2
17
+ echo "Run /mobilems:project-init first to lay down the MobilEMS foundation." >&2
18
+ exit 3
19
+ }
20
+
21
+ [ -f apps/web/src/app/app.config.ts ] || miss "app.config.ts missing"
22
+ grep -q "SharedModule.forRoot" apps/web/src/app/app.config.ts || miss "SharedModule.forRoot not in app.config.ts (foundation not applied)"
23
+ grep -q "./lib/components" libs/shared/angular/src/index.ts 2>/dev/null || miss "shared-lib components barrel not populated"
24
+ [ -f apps/web/src/app/app.routes.ts ] || miss "app.routes.ts missing"
25
+
26
+ PREFIX="$(grep -oE '@[a-z0-9-]+/angular' tsconfig.base.json | head -1 | sed -E 's#@([a-z0-9-]+)/angular#\1#')"
27
+ echo "FOUNDATION OK. Detected prefix: ${PREFIX:-<unknown>}"
28
+ [ -d libs/museum-objects ] && echo "NOTE: libs/museum-objects already exists — build steps must be idempotent." >&2
29
+ exit 0
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # verify-module.sh <workspace-root> <PREFIX>
4
+ #
5
+ # Post-condition check for mobilems-partners: library created, ArticlesSharedModule wired over the
6
+ # Article type with its own reducer, the partner cards present in the partners lib, the partnerzy
7
+ # route registered (top-level + search child), and the Articles + objects-domain deps installed.
8
+ # macOS bash 3.2 safe.
9
+ set -eu
10
+
11
+ ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX>}"
12
+ PREFIX="${2:?usage: verify-module.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
+ MOD=libs/museum-partners/shell/angular/src/lib/partners.module.ts
20
+
21
+ echo "== Library =="
22
+ [ -f "$MOD" ] && pass "MuseumPartnersModule present" || bad "partners.module.ts missing"
23
+ [ -f libs/museum-partners/domain/src/lib/entities/museum-partner.entity.ts ] && pass "MuseumPartner entity present" || bad "domain entity missing"
24
+ grep -q "class MuseumPartner" libs/museum-partners/domain/src/lib/entities/museum-partner.entity.ts 2>/dev/null && pass "MuseumPartner class declared" || bad "MuseumPartner class not declared"
25
+ grep -q "museum-partners/shell/angular" tsconfig.base.json && pass "tsconfig shell path added" || bad "tsconfig shell path missing"
26
+ grep -q "museum-partners/domain" tsconfig.base.json && pass "tsconfig domain path added" || bad "tsconfig domain path missing"
27
+
28
+ echo "== Module wiring =="
29
+ grep -q "ArticlesSharedModule.forFeature" "$MOD" 2>/dev/null && pass "ArticlesSharedModule.forFeature wired" || bad "ArticlesSharedModule.forFeature missing"
30
+ grep -q "entity: 'partners'" "$MOD" 2>/dev/null && pass "entity 'partners' configured" || bad "entity 'partners' missing"
31
+ grep -q "reducerFactory: getReducer" "$MOD" 2>/dev/null && pass "reducerFactory: getReducer configured" || bad "reducerFactory: getReducer missing"
32
+
33
+ echo "== +state =="
34
+ STATE=libs/museum-partners/shell/angular/src/lib/+state
35
+ [ -f "$STATE/partners.reducer.ts" ] && pass "reducer present" || bad "partners.reducer.ts missing"
36
+ grep -q "export function getReducer" "$STATE/partners.reducer.ts" 2>/dev/null && pass "getReducer exported" || bad "getReducer not exported in reducer"
37
+
38
+ echo "== UI components =="
39
+ COMP=libs/museum-partners/shell/angular/src/lib/components
40
+ [ -f "$COMP/grid-item/grid-item.component.ts" ] && pass "grid-item card present" || bad "components/grid-item missing"
41
+ [ -f "$COMP/list-item/list-item.component.ts" ] && pass "list-item card present" || bad "components/list-item missing"
42
+
43
+ echo "== Route registration =="
44
+ ROUTES=apps/web/src/app/app.routes.ts
45
+ grep -q "museum-partners/shell/angular" "$ROUTES" && pass "partners route registered" || bad "partners route not in app.routes.ts"
46
+ # top-level + search child — expect at least 2 occurrences
47
+ n=$(grep -c "museum-partners/shell/angular" "$ROUTES" || true)
48
+ [ "${n:-0}" -ge 2 ] && pass "registered top-level + search child ($n)" || bad "expected >=2 route registrations (top-level + search child), found $n"
49
+ grep -q "partnerzy" "$ROUTES" && pass "partnerzy path present" || bad "partnerzy path missing"
50
+
51
+ echo "== Deps =="
52
+ grep -q "@smartsoft001-mobilems/articles-domain" package.json && pass "articles-domain dep" || bad "articles-domain dep missing"
53
+ grep -q "@smartsoft001-mobilems/articles-shell-angular" package.json && pass "articles-shell-angular dep" || bad "articles-shell-angular dep missing"
54
+ grep -q "@smartsoft001-mobilems/articles-shell-dtos" package.json && pass "articles-shell-dtos dep" || bad "articles-shell-dtos dep missing"
55
+ grep -q "@smartsoft001-mobilems/objects-domain" package.json && pass "objects-domain dep" || bad "objects-domain dep missing"
56
+
57
+ echo
58
+ if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
59
+ exit "$fail"
@@ -0,0 +1,151 @@
1
+ ---
2
+ name: mobilems-project-init
3
+ description: Turn a generic scaffold-nx-workspace base into a MobilEMS museum web-app foundation — the MobilEMS DELTA (deps, Tailwind, runtime env, app.config providers, shared lib, static pages, app shell, .claude). Use whenever the user wants to initialize / bootstrap / set up a NEW MobilEMS or MuzaCMS museum app, "skonfiguruj projekt muzealny / mobilems", port a museum frontend, or asks to add the MobilEMS foundation on top of an existing Nx + Angular SSR scaffold. Produces the foundation ONLY — NO feature modules (objects, collections, articles, thematic publications); those are added by the mobilems-<module> skills afterwards.
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
6
+ ---
7
+
8
+ # MobilEMS project init
9
+
10
+ Apply the **MobilEMS delta** on top of a base produced by `scaffold-nx-workspace` (the generic
11
+ smartsoft Nx + Angular-SSR shell). The base gives you a buildable empty workspace: prefix already
12
+ threaded, scss-only, minimal `app.config.ts`, empty `app.routes.ts`, a generic `server.ts`, and a
13
+ shared lib that exports only `environment` + `EnvironmentService`. This skill upgrades that shell
14
+ into a MobilEMS museum **foundation** — everything the museum app needs _except_ the feature
15
+ modules.
16
+
17
+ **Why a delta (not a from-scratch generator):** the museum shared lib is ~15k lines of bespoke
18
+ Angular (header, footer, page renderer, filters, gallery, map, widgets, WCAG, SEO, …) that is
19
+ **not** published to npm. It cannot be regenerated from prose, so reference mode copies it from an
20
+ existing MobilEMS repo and threads the workspace prefix. Foundation-only mode is a degraded
21
+ fallback when no reference is available.
22
+
23
+ ## Scope — the foundation only
24
+
25
+ This skill builds the museum **foundation**: the workspace it produces builds and runs on its own,
26
+ with **no `libs/museum-*` libraries, no feature shared components, and no feature routes** yet. The
27
+ feature modules are built by `mobilems-objects`, `mobilems-object-collections`, `mobilems-articles`,
28
+ and `mobilems-thematic-publications`, each of which adds its own library, shared components, npm
29
+ package, and routes. Keeping feature code out of the foundation is the layering rule that lets the
30
+ foundation build standalone (its shared library imports no feature package).
31
+
32
+ ## Steps
33
+
34
+ ### 0. Precondition — confirm a scaffold-nx-workspace base
35
+
36
+ Run `scripts/detect-base.sh <workspace-root>`. It checks for the base shell (`apps/web/src/server.ts`,
37
+ an empty-ish `app.routes.ts`, a shared lib barrel exporting only environment/EnvironmentService) and
38
+ that the MobilEMS delta is **not** already applied. If the base is missing, stop and tell the user to
39
+ run `/smart:scaffold-nx-workspace` first — do not try to create the Nx workspace here.
40
+
41
+ ### 1. Detect the prefix (never re-propose)
42
+
43
+ The base already derived/confirmed/threaded the import-alias prefix. Read it from
44
+ `tsconfig.base.json` `paths` — the key `@<PREFIX>/angular`. Hold `<PREFIX>`; thread it into every
45
+ copied file (the reference repo uses `@msr`, so replacing `@msr` → `@<PREFIX>` is the main rewrite).
46
+
47
+ ### 2. Choose mode
48
+
49
+ Default = **reference mode**. Ask the user (`AskUserQuestion`) for the path/URL of an existing
50
+ MobilEMS repo to copy the shared lib + static pages from (a local clone of one of the team's museum
51
+ apps). If they decline, or if invoked with a `foundation-only` argument, use **foundation-only** mode
52
+ (see "Modes" below).
53
+
54
+ ### 3. npm dependencies
55
+
56
+ Add the MobilEMS framework + state + i18n + shared-UI deps per `references/npm-deps.md`. Versions
57
+ are resolved at run time to match the installed `@smartsoft001/angular` line — **never hardcode**.
58
+ Do NOT add the per-feature packages (`*-objects-*`, `*-articles-*`, `*-public-collections-*`) or
59
+ `online-3d-viewer` — those belong to the module skills.
60
+
61
+ ### 4. Tailwind on top of scss
62
+
63
+ Layer Tailwind (class prefix `smart-`) onto the base's scss per `references/tailwind-styles.md`:
64
+ add `tailwind.config.js` + postcss wiring, and overwrite `apps/web/src/styles.scss` with the museum
65
+ global stylesheet (font-fallbacks, `.smart-container`, WCAG, masonry, fullscreen-dialog rules). The
66
+ stylesheet is pure CSS — copying it verbatim is safe even before the components it targets exist.
67
+
68
+ ### 5. Runtime env — multimediaUrl + translations
69
+
70
+ Extend the base runtime-env infra per `references/server-env.md`: add `multimediaUrl` to
71
+ `environment.ts`/`.prod.ts`, the `/env.js` template, and the SSR allowed-hosts list; and add the
72
+ `/api/translations` endpoint to `server.ts`.
73
+
74
+ ### 6. app.config.ts → MobilEMS providers
75
+
76
+ Replace the base's minimal providers with the full MobilEMS provider set per
77
+ `references/config-providers.md` (ngrx root + devtools, ngx-translate + `provideAppInitializer`
78
+ translation bootstrap, `MasonryModule`, `SharedModule.forRoot`, the three HTTP interceptors, the
79
+ `StyleService`/`AppService`→`NoOpAppService`/`ErrorService` overrides, `SEARCH_CONFIG_TOKEN`, and
80
+ `withI18nSupport()` on hydration).
81
+
82
+ ### 7. Shared lib — populate the placeholders (reference mode)
83
+
84
+ Follow `references/shared-lib.md`. Build the **foundation shared components** — every shared
85
+ component that depends only on Angular + the framework packages + other foundation shared components.
86
+ Run `python3 scripts/classify-shared-components.py` against the reference's
87
+ `libs/shared/angular/src/lib/components` to list which shared components are FOUNDATION (build here)
88
+ vs FEATURE-OWNED (built by a module skill, because they import a feature package). Build the
89
+ foundation components (including the home-page carousels) + all services/pipes/directives/tools/models
90
+
91
+ - `configs.module.ts`, thread `<PREFIX>`, export them from the barrels, and build the `widget`
92
+ container + `models/objects.model.ts` with their foundation parts. A shared component that imports a
93
+ feature domain package is feature code — a module skill builds it.
94
+
95
+ ### 8. App shell + static pages + routes
96
+
97
+ Follow `references/app-shell.md`: copy the `App` root component (`app.ts`/`app.html`/`app.scss`) and
98
+ the static pages (`home`, `page`, `contact`, `search`, `not-found`) + their resolvers, thread
99
+ `<PREFIX>`, and build `app.routes.ts` with the app's static routes (home, pages, contact, search,
100
+ catch-all). Each module skill adds its own feature route to the `appRoutes` array when it runs
101
+ (`article-preview` is articles-coupled — `mobilems-articles` adds it, not the foundation).
102
+
103
+ ### 9. QA hardening — SEO, i18n, WCAG, branding
104
+
105
+ Apply `references/qa-hardening.md` — a checklist distilled from real QA bug reports on live museum
106
+ deployments. The copied foundation carries the _reference museum's_ identity, so this step removes
107
+ leftovers and closes the recurring gaps QA always finds:
108
+
109
+ - dynamic `/sitemap.xml` + `/robots.txt` SSR endpoints driven by `SITE_URL` (never static copies —
110
+ they shipped another museum's domain twice),
111
+ - og/twitter images + URLs on the target domain, favicon set, skip links rewritten for this app,
112
+ - meta description + OpenGraph on home in both languages; `og:locale` follows the active language,
113
+ - default language = Polish, `<html lang>` synced on switch, zero hardcoded UI strings,
114
+ - WCAG: one `h1`, no skipped heading levels, contrast ≥4.5:1 (reference greys fail), heading font
115
+ with full Polish diacritics,
116
+ - warning-clean build & console (NG8107/NG8112/NG8113/NG0100/NG0913, Swiper loop, duplicate API
117
+ calls).
118
+
119
+ ### 10. .claude extension
120
+
121
+ Apply `references/claude-config.md`: add `flow@mobilems` to `enabledPlugins`, grow the
122
+ MobilEMS-specific permissions / MCP allow-lists, and seed `tools/specs` + `tools/prompts` pointers.
123
+
124
+ ### 11. Verify
125
+
126
+ Run `scripts/verify-init.sh <workspace-root> <PREFIX>`. It asserts the delta landed (Tailwind,
127
+ multimediaUrl, MobilEMS providers, populated shared-lib barrel), the QA-hardening basics (sitemap +
128
+ robots endpoints, favicon, non-empty heading font), AND the foundation is self-contained (no
129
+ `libs/museum-*`, no feature routes, and the build-safety invariant — no file under
130
+ `libs/shared/angular/src` imports a feature package). Fix and re-run until it passes. Gate
131
+ `npm install` behind an explicit ask — skip it during self-validation/eval.
132
+
133
+ ## Modes
134
+
135
+ - **Reference mode (default):** copies the shared lib + pages from a real MobilEMS repo and threads
136
+ the prefix. This is the only mode that regenerates the full foundation, because the shared lib is
137
+ not in npm.
138
+ - **Foundation-only (fallback):** do steps 1, 3, 4, 5, 9 (deps, Tailwind, env, .claude) and scaffold a
139
+ reduced `app.config.ts` that does NOT import local shared-lib code (omit the interceptors + the
140
+ `App` shell that need it). Leave a `MOBILEMS-FOUNDATION-TODO.md` listing what reference mode would
141
+ have copied (shared lib, pages, full app.config, app shell) so the user can supply a reference
142
+ later. Be honest that this mode does not produce a runnable museum UI on its own.
143
+
144
+ ## Notes
145
+
146
+ - This skill assumes the base Nx + Angular-SSR workspace already exists — it patches MobilEMS onto
147
+ it and never runs `create-nx-workspace`.
148
+ - Bundled `references/` + `scripts/` ship automatically (the whole `src/plugins/**` tree is copied to
149
+ dist). Do NOT hand-edit `plugin.json` — the version is auto-synced at build.
150
+ - The feature work — feature libs, their npm packages, routes, and feature shared components — is
151
+ built by the `mobilems-<module>` skills, not here.