@smartsoft001-mobilems/claude-plugins 2.88.0 → 2.90.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/README.md +27 -2
- package/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/README.md +21 -2
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-articles/SKILL.md +52 -20
- package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-devops-config/SKILL.md +141 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/docker.md +58 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/env-and-lint.md +59 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/github-hygiene.md +54 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/tooling.md +105 -0
- package/plugins/mobilems/skills/mobilems-devops-config/references/workflows.md +410 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/detect-foundation.sh +47 -0
- package/plugins/mobilems/skills/mobilems-devops-config/scripts/verify-devops.sh +96 -0
- package/plugins/mobilems/skills/mobilems-events/SKILL.md +27 -1
- package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +43 -0
- package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +46 -14
- package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +54 -0
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +42 -10
- package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-partners/SKILL.md +30 -2
- package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +24 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/check-translations.js +90 -0
- package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +53 -0
- package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +68 -8
- package/plugins/mobilems/skills/mobilems-project-init/references/branding-reset.md +112 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +7 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +1 -1
- package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +30 -30
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +42 -5
- package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +33 -23
- package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +62 -9
- package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +25 -1
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +11 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +26 -0
- package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +43 -0
package/README.md
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
# @smartsoft001-mobilems/claude-plugins
|
|
2
2
|
|
|
3
|
-
Claude Code plugins for MobileMS / MuzaCMS projects. Ships
|
|
3
|
+
Claude Code plugins for MobileMS / MuzaCMS projects. Ships four plugins:
|
|
4
4
|
|
|
5
|
-
- `
|
|
5
|
+
- `mobilems` — the museum scaffolding skills (see below)
|
|
6
|
+
- `flow` — modern Angular 20+ projects (primary flow plugin)
|
|
6
7
|
- `flow-legacy` — Angular 14 projects
|
|
7
8
|
- `flow-external` — standalone Angular 6–11 projects
|
|
8
9
|
|
|
10
|
+
The package's only TypeScript export is `CLAUDE_PLUGINS_VERSION`; the plugins themselves are the
|
|
11
|
+
MD/JSON files under `src/plugins/**`, copied to `dist` as build assets.
|
|
12
|
+
|
|
13
|
+
## `mobilems` skills
|
|
14
|
+
|
|
15
|
+
Run them in this order for a new museum app — `/smart:scaffold-nx-workspace` →
|
|
16
|
+
`mobilems-project-init` → the feature skills → `mobilems-devops-config`.
|
|
17
|
+
|
|
18
|
+
| Skill | Builds |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `mobilems-project-init` | The MobilEMS foundation: deps, Tailwind, runtime env, `app.config` providers, shared lib, static pages, app shell, `.claude`. No feature modules. |
|
|
21
|
+
| `mobilems-objects` | `libs/museum-objects` + the `/obiekty` route |
|
|
22
|
+
| `mobilems-object-collections` | `libs/museum-collections` + the `/kolekcje` route |
|
|
23
|
+
| `mobilems-articles` | A parametrized (NAME + TEMPLATE) article module — news, education, … |
|
|
24
|
+
| `mobilems-thematic-publications` | `libs/museum-thematic-paths` + the `/sciezki-tematyczne` route |
|
|
25
|
+
| `mobilems-events` | `libs/museum-events` + the `/wydarzenia` route (booking flow) |
|
|
26
|
+
| `mobilems-partners` | `libs/museum-partners` + the `/partnerzy` route |
|
|
27
|
+
| `mobilems-devops-config` | The ops layer: Dockerfile, GitHub Actions, update tooling, env/lint/node, `.github` hygiene |
|
|
28
|
+
| `mobilems-docs-reader` | Answers MuzaCMS indexer-API questions from a freshly fetched OpenAPI spec (read-only) |
|
|
29
|
+
|
|
30
|
+
Every scaffolding skill opens with a **Framework first — MANDATORY** section pointing at the
|
|
31
|
+
[framework API catalog](../angular/README.md); they reuse framework exports rather than
|
|
32
|
+
re-implementing them.
|
|
33
|
+
|
|
9
34
|
## MuzaCMS API access (required for `mobilems-docs-reader` skill)
|
|
10
35
|
|
|
11
36
|
The `mobilems-docs-reader` skill answers questions about the MuzaCMS / MobileMS indexer API by fetching its Swagger spec from a live MuzaCMS instance. The MuzaCMS docs page is behind a Symfony form-login, so the skill needs credentials. The refresh script lives next to the skill (`plugins/<variant>/skills/mobilems-docs-reader/refresh-mobilems-api-spec.sh`) and caches fetches per Claude Code session (with an 8-hour TTL) — see [Caching](#caching) below for details.
|
package/package.json
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
# Mobilems Plugin for Claude Code
|
|
2
2
|
|
|
3
|
-
MobileMS-specific skills
|
|
3
|
+
MobileMS-specific skills: the museum app scaffolding suite plus the MuzaCMS / MobileMS indexer API reader. This plugin complements the generic `flow@smart-pro` workflow plugins — it provides the skills tied to the MobileMS backend and to the museum app architecture.
|
|
4
4
|
|
|
5
5
|
## Contents
|
|
6
6
|
|
|
7
|
+
### Scaffolding
|
|
8
|
+
|
|
9
|
+
Run order for a new museum app: `/smart:scaffold-nx-workspace` → `mobilems-project-init` → the feature skills (any order, any subset) → `mobilems-devops-config`.
|
|
10
|
+
|
|
11
|
+
| Type | Name | Purpose |
|
|
12
|
+
|------|------|---------|
|
|
13
|
+
| Skill | `mobilems-project-init` | The MobilEMS foundation — deps, Tailwind, SSR + translations runtime, `app.config` providers, shared lib, static pages, app shell, `.claude`. Produces NO feature modules |
|
|
14
|
+
| Skill | `mobilems-objects` | Museum objects module — `libs/museum-objects` (domain + shell/angular) + the `/obiekty` route |
|
|
15
|
+
| Skill | `mobilems-object-collections` | Collections module — `libs/museum-collections` + the `/kolekcje` route |
|
|
16
|
+
| Skill | `mobilems-articles` | A parametrized (NAME + TEMPLATE) article-backed content module; run once per content type |
|
|
17
|
+
| Skill | `mobilems-thematic-publications` | Thematic paths — `libs/museum-thematic-paths` + the `/sciezki-tematyczne` route |
|
|
18
|
+
| Skill | `mobilems-events` | Events + booking flow — `libs/museum-events` + the `/wydarzenia` route |
|
|
19
|
+
| Skill | `mobilems-partners` | Partners — `libs/museum-partners` + the `/partnerzy` route |
|
|
20
|
+
| Skill | `mobilems-devops-config` | The ops layer — Dockerfile, GitHub Actions, update tooling, `.env.dev`/lint/node, `.github` hygiene |
|
|
21
|
+
|
|
22
|
+
Each of these opens with a **Framework first — MANDATORY** section pointing at the [Framework API index](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api): reuse framework exports (`MasonryGridComponent`, `FiltersBaseComponent` / `FiltersContext`, `FileUrlService`, `SeoService`, …) rather than re-implementing them. Their bundled `scripts/verify-*.sh` fail the run on hand-rolled masonry grids and on filters that bypass `FiltersBaseComponent`.
|
|
23
|
+
|
|
24
|
+
### API docs
|
|
25
|
+
|
|
7
26
|
| Type | Name | Purpose |
|
|
8
27
|
|------|------|---------|
|
|
9
28
|
| Skill | `mobilems-docs-reader` | Answer questions about the MuzaCMS / MobileMS indexer OpenAPI spec; fetches a fresh copy via the bundled refresh script |
|
|
10
29
|
|
|
11
|
-
|
|
30
|
+
That skill ships with `scripts/refresh-mobilems-api-spec.sh`, which logs into `https://develop-indexer.mobilems.pl` using `MOBILEMS_LOGIN` / `MOBILEMS_PASSWORD` (read from env or `.env`) and writes the spec to `shared-assets/mobilems-api.json` inside the installed package. The script caches the fetched spec per Claude Code session (8h TTL).
|
|
12
31
|
|
|
13
32
|
## Installation
|
|
14
33
|
|
|
@@ -39,9 +39,33 @@ gave them). The first two are the essentials:
|
|
|
39
39
|
|
|
40
40
|
Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
|
|
41
41
|
(`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
|
|
42
|
-
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json
|
|
42
|
+
run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`)
|
|
43
43
|
and the reference repo path (same one init used).
|
|
44
44
|
|
|
45
|
+
## Framework first — MANDATORY
|
|
46
|
+
|
|
47
|
+
Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
|
|
48
|
+
list — re-implementing framework code is a defect, not a shortcut:
|
|
49
|
+
|
|
50
|
+
0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
|
|
51
|
+
its catalog. Start here when you do not already know which package owns what.
|
|
52
|
+
1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
|
|
53
|
+
`node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
|
|
54
|
+
This is the path `scripts/verify-module.sh` names when it fails.
|
|
55
|
+
2. [`packages/articles/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/articles/README.md) — this module's package catalog
|
|
56
|
+
3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
|
|
57
|
+
|
|
58
|
+
| Do not hand-roll | Use |
|
|
59
|
+
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider only feeds it |
|
|
61
|
+
| filter state / hand-built `query` arrays | `FiltersBaseComponent<Article>` + its `FiltersContext` accessors (`searchText`, `authors`, `cycles`, `publicationDateFrom/To`, `page`, `sort`, …) |
|
|
62
|
+
| list/detail plumbing (facade wiring, id-from-route, PDF) | the `ArticlesListComponent` / `ArticlesItemComponent` base classes |
|
|
63
|
+
| media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
|
|
64
|
+
| a content-block renderer | `WidgetComponent` + the `ArticleWidgetType` map |
|
|
65
|
+
|
|
66
|
+
Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
|
|
67
|
+
README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
|
|
68
|
+
|
|
45
69
|
## Steps
|
|
46
70
|
|
|
47
71
|
1. **Gather the parameters** above. Ask for the **name** and **template** at minimum; default `route`
|
|
@@ -54,11 +78,16 @@ and the reference repo path (same one init used).
|
|
|
54
78
|
Also add any **extra third-party packages the copied pages import** that aren't already in the
|
|
55
79
|
workspace — e.g. the reference list page uses `lodash-decorators` (`@debounce`); grep the pages you
|
|
56
80
|
copy for non-`@angular`/`@smartsoft001` imports and add the missing ones.
|
|
57
|
-
3. **Library** —
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
81
|
+
3. **Library** — scaffold `libs/museum-news` with this package's Nx generator (deterministic — do
|
|
82
|
+
NOT copy files by hand when the generator is available):
|
|
83
|
+
```bash
|
|
84
|
+
npx nx g @smartsoft001-mobilems/articles-shell-angular:museum-module \
|
|
85
|
+
--referenceRoot=<reference-repo> --prefix=<PREFIX>
|
|
86
|
+
```
|
|
87
|
+
It copies the lib from the reference, threads `@<PREFIX>/`, adds both `tsconfig.base.json`
|
|
88
|
+
paths, normalizes `project.json` paths and copies the module's translation groups (pl+en).
|
|
89
|
+
Fallback (older framework line without the generator): do the same by hand per
|
|
90
|
+
`references/lib-contents.md`. For a NON-news article module (different `<name>`/entity), the generator's `news` output is the starting point — rename per `references/lib-contents.md` after generating, or fall back to the manual path entirely.
|
|
62
91
|
4. **Module + route** — see `references/module-and-routes.md`: `Museum<Name>Module` wraps
|
|
63
92
|
`ArticlesSharedModule.forFeature` with `type: Article`, the chosen `entity`, and
|
|
64
93
|
`baseQuery: [{ key: 'template', value: '<template>', type: '=', hidden: true }]`, plus the `Masonry`
|
|
@@ -71,29 +100,32 @@ and the reference repo path (same one init used).
|
|
|
71
100
|
and list cards (`<name>-grid-item`, `<name>-list-item`) in `libs/shared/angular` and export them; if
|
|
72
101
|
the module renders article-gallery content blocks, also build the article gallery widget and register
|
|
73
102
|
it in the widget content-type map (build once, idempotent across article modules).
|
|
74
|
-
6. **
|
|
103
|
+
6. **Translations** — copy the module's translation groups (`NEWS` + `ARTICLES` for the aktualności instance (override with `TRANS_GROUPS` when the module uses different groups)) from the reference
|
|
104
|
+
repo's `apps/web/public/translations.json` into the app's file, for BOTH `pl` and `en`.
|
|
105
|
+
Complete group = every reference key present; every key referenced in the module's code
|
|
106
|
+
must exist in both languages — a raw `GROUP.key` rendered in the UI is a defect.
|
|
107
|
+
7. **Verify** — run `REF_ROOT=<reference-repo> scripts/verify-module.sh <workspace-root> <PREFIX> <name> [template]`: asserts the
|
|
75
108
|
library exists, the `forFeature` carries the chosen `entity` (and template, if given), the route is
|
|
76
109
|
registered, and the article card shared components are built and exported. Fix and re-run.
|
|
77
110
|
|
|
78
|
-
## Known
|
|
111
|
+
## Known pitfalls (fix while copying, not after)
|
|
79
112
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`references/qa-hardening.md`; these are the article-module-specific ones:
|
|
113
|
+
The cross-cutting rules live in `mobilems-project-init` `references/qa-hardening.md`; these are the
|
|
114
|
+
article-module-specific ones:
|
|
83
115
|
|
|
84
116
|
- **Author filter dictionary key is lowercase `authors`.** `dictionaryKey="authors"` routes to
|
|
85
117
|
`api/article/authors/...`; `"Author"` silently falls through to `api/dictionary/Author/...` →
|
|
86
|
-
HTTP 409 and an empty options list
|
|
87
|
-
- **alt from CMS titles must strip HTML.**
|
|
118
|
+
HTTP 409 and an empty options list.
|
|
119
|
+
- **alt from CMS titles must strip HTML.** Otherwise the detail header image gets
|
|
88
120
|
`alt="<p>Tytuł</p>"` — pipe the title through `smartRemoveHtml` before binding it to `alt`.
|
|
89
|
-
- **Detail page structure:** an `h1` (article title — copied reference
|
|
90
|
-
(`/ Artykuły / {tytuł}`), and gallery image captions rendered as text, NOT `h3` (an
|
|
91
|
-
jump is
|
|
92
|
-
- **List item field order per mockup:** date ABOVE the title
|
|
93
|
-
the
|
|
121
|
+
- **Detail page structure:** an `h1` (article title — a copied reference may have only `h2`),
|
|
122
|
+
breadcrumbs (`/ Artykuły / {tytuł}`), and gallery image captions rendered as text, NOT `h3` (an
|
|
123
|
+
`h1 → h3` jump is an axe-core finding).
|
|
124
|
+
- **List item field order per mockup:** date ABOVE the title, keywords as inline links styled per
|
|
125
|
+
the design, and mockup-exact sort labels
|
|
94
126
|
("Sortowanie: Od najnowszych", not "Sortuj: Data publikacji - od najnowszych").
|
|
95
|
-
- **List spacing:** in list view the items need a real gap (`gap-4`
|
|
96
|
-
|
|
127
|
+
- **List spacing:** in list view the items need a real gap (`gap-4`); without it they read as one
|
|
128
|
+
block.
|
|
97
129
|
- **Search debounce 250 ms** (`@debounce(250)`), total count + shared `NextPageComponent` on the
|
|
98
130
|
list — consistent with objects/collections.
|
|
99
131
|
- **Warning-clean templates:** remove needless `?.` on non-nullable signals (NG8107) and unused
|
|
@@ -61,6 +61,30 @@ import { routes } from './<name>.routes';
|
|
|
61
61
|
export class Museum<Name>Module {}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
> **The `Masonry` provider exists only to feed the framework's `MasonryGridComponent`.** It is DI
|
|
65
|
+
> plumbing, not a grid — never call `new Masonry(...)`, keep a `MasonryInstance`, or re-implement the
|
|
66
|
+
> layout/`reloadItems` dance in a page component. A hand-rolled CSS-grid or column layout is the same
|
|
67
|
+
> defect. `scripts/verify-module.sh` fails the run on both.
|
|
68
|
+
|
|
69
|
+
The list page renders the grid like this — copy this shape, it is the required one:
|
|
70
|
+
|
|
71
|
+
```html
|
|
72
|
+
<smart-mobilems-masonry-grid [list]="list()" [options]="{ gutter: 24 }">
|
|
73
|
+
<ng-template #masonryContentTpl>
|
|
74
|
+
<div class="masonry-grid-sizer"></div>
|
|
75
|
+
@for (item of list(); track item.id) {
|
|
76
|
+
<div class="masonry-item"><smart-<name>-grid-item [item]="item" /></div>
|
|
77
|
+
}
|
|
78
|
+
</ng-template>
|
|
79
|
+
</smart-mobilems-masonry-grid>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`.masonry-grid-sizer` and `.masonry-item` are required — they are the component's default
|
|
83
|
+
`columnWidth` / `itemSelector`. The card (shared-components.md) is the only module-specific part. Inputs,
|
|
84
|
+
public methods and the SSR provider are documented in the framework catalog,
|
|
85
|
+
[`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md#masonrygridcomponent)
|
|
86
|
+
(installed as `node_modules/@smartsoft001-mobilems/angular/README.md`).
|
|
87
|
+
|
|
64
88
|
The **`baseQuery`** is what scopes the module to its content: `key` is always `'template'`, `type` is
|
|
65
89
|
`'='`, `hidden` is `true`, and `value` is the module's `<template>` (the API then returns only records
|
|
66
90
|
matching that template). Change only the `value` between modules.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-translations.js "<GROUP GROUP …>" [reference-root]
|
|
4
|
+
*
|
|
5
|
+
* Run from the workspace root (verify-module.sh cd's there). Verifies the
|
|
6
|
+
* module's translation groups in apps/web/public/translations.json:
|
|
7
|
+
* 1. each group exists and is non-empty in BOTH pl and en,
|
|
8
|
+
* 2. every literal GROUP.* key referenced in libs/ or apps/web/src exists
|
|
9
|
+
* in both languages (dead references = defect),
|
|
10
|
+
* 3. with a reference root: every reference key of the group exists in the
|
|
11
|
+
* generated file — missing keys are listed (the module is incomplete).
|
|
12
|
+
* Prints PASS/FAIL lines in the verify-module.sh idiom; exit 1 on any FAIL.
|
|
13
|
+
*/
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
|
|
16
|
+
const [groupsArg, refRoot] = process.argv.slice(2);
|
|
17
|
+
const groups = (groupsArg || "").split(/\s+/).filter(Boolean);
|
|
18
|
+
if (!groups.length) { console.log(" FAIL: no translation groups given"); process.exit(1); }
|
|
19
|
+
|
|
20
|
+
const TRANS = "apps/web/public/translations.json";
|
|
21
|
+
let fail = 0;
|
|
22
|
+
const pass = (m) => console.log(` PASS: ${m}`);
|
|
23
|
+
const bad = (m) => { console.log(` FAIL: ${m}`); fail = 1; };
|
|
24
|
+
|
|
25
|
+
const flat = (obj, prefix) => Object.entries(obj || {}).flatMap(([k, v]) =>
|
|
26
|
+
v && typeof v === "object" ? flat(v, `${prefix}.${k}`) : [`${prefix}.${k}`]);
|
|
27
|
+
|
|
28
|
+
const readTrans = (p) => { try { return JSON.parse(fs.readFileSync(p, "utf8")).data.translations; } catch { return null; } };
|
|
29
|
+
|
|
30
|
+
const gen = readTrans(TRANS);
|
|
31
|
+
if (!gen) { bad(`cannot read ${TRANS}`); process.exit(1); }
|
|
32
|
+
|
|
33
|
+
// 1. groups present + non-empty in both languages
|
|
34
|
+
const genKeys = { pl: new Set(), en: new Set() };
|
|
35
|
+
for (const lang of ["pl", "en"]) {
|
|
36
|
+
for (const g of groups) {
|
|
37
|
+
const keys = flat(gen[lang]?.[g], g);
|
|
38
|
+
keys.forEach((k) => genKeys[lang].add(k));
|
|
39
|
+
if (keys.length) pass(`${g} present in ${lang} (${keys.length} keys)`);
|
|
40
|
+
else bad(`${g} missing or empty in ${lang}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 2. dead references — literal GROUP.* keys used in sources must exist.
|
|
45
|
+
// Dynamic tails (`GROUP.${x}`) don't match the literal regex, and a matched
|
|
46
|
+
// PREFIX that is a parent of existing keys counts as present.
|
|
47
|
+
const walk = (dir) => {
|
|
48
|
+
let out = [];
|
|
49
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
50
|
+
if (e.name === "node_modules" || e.name.startsWith(".")) continue;
|
|
51
|
+
const p = `${dir}/${e.name}`;
|
|
52
|
+
if (e.isDirectory()) out = out.concat(walk(p));
|
|
53
|
+
else if (/\.(ts|html)$/.test(e.name)) out.push(p);
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
};
|
|
57
|
+
let src = "";
|
|
58
|
+
for (const d of ["libs", "apps/web/src"]) {
|
|
59
|
+
if (fs.existsSync(d)) src += walk(d).map((f) => { try { return fs.readFileSync(f, "utf8"); } catch { return ""; } }).join("\n");
|
|
60
|
+
}
|
|
61
|
+
const re = new RegExp(`\\b(?:${groups.join("|")})(?:\\.[A-Za-z0-9_]+)+`, "g");
|
|
62
|
+
const used = new Set(src.match(re) || []);
|
|
63
|
+
const dead = { pl: [], en: [] };
|
|
64
|
+
for (const key of used) {
|
|
65
|
+
for (const lang of ["pl", "en"]) {
|
|
66
|
+
const ks = genKeys[lang];
|
|
67
|
+
if (!ks.has(key) && ![...ks].some((k) => k.startsWith(`${key}.`))) dead[lang].push(key);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
for (const lang of ["pl", "en"]) {
|
|
71
|
+
if (dead[lang].length) bad(`${lang}: keys referenced in code but missing in ${TRANS}: ${dead[lang].slice(0, 8).join(", ")}${dead[lang].length > 8 ? ` (+${dead[lang].length - 8})` : ""}`);
|
|
72
|
+
else pass(`no dead ${lang} references (${used.size} literal keys checked)`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. completeness vs reference
|
|
76
|
+
if (refRoot) {
|
|
77
|
+
const ref = readTrans(`${refRoot}/apps/web/public/translations.json`);
|
|
78
|
+
if (!ref) bad(`cannot read reference translations under ${refRoot}`);
|
|
79
|
+
else {
|
|
80
|
+
for (const lang of ["pl", "en"]) {
|
|
81
|
+
const missing = groups.flatMap((g) => flat(ref[lang]?.[g], g)).filter((k) => !genKeys[lang].has(k));
|
|
82
|
+
if (missing.length) bad(`${lang}: ${missing.length} reference keys missing: ${missing.slice(0, 8).join(", ")}${missing.length > 8 ? " …" : ""}`);
|
|
83
|
+
else pass(`${lang}: all reference keys covered`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
console.log(" SKIP: no reference root given — completeness vs reference not checked");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
process.exit(fail);
|
|
@@ -11,6 +11,7 @@ ROOT="${1:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
|
11
11
|
PREFIX="${2:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
12
12
|
NAME="${3:?usage: verify-module.sh <workspace-root> <PREFIX> <name> [template]}"
|
|
13
13
|
TEMPLATE="${4:-}"
|
|
14
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
14
15
|
cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
|
|
15
16
|
|
|
16
17
|
LIBF="museum-$NAME"
|
|
@@ -56,6 +57,58 @@ for p in articles-domain articles-shell-angular articles-shell-dtos; do
|
|
|
56
57
|
grep -q "@smartsoft001-mobilems/$p" package.json && pass "$p dep" || bad "$p dep missing"
|
|
57
58
|
done
|
|
58
59
|
|
|
60
|
+
echo "== Framework first (packages/shared/angular/README.md) =="
|
|
61
|
+
LIBDIR="libs/$LIBF/shell/angular/src/lib"
|
|
62
|
+
# 1. masonry belongs to the framework's MasonryGridComponent — no module-level instances
|
|
63
|
+
MAS="$(grep -rlnE "new Masonry\(|MasonryInstance" libs apps/web/src 2>/dev/null || true)"
|
|
64
|
+
if [ -n "$MAS" ]; then
|
|
65
|
+
bad "hand-rolled masonry — render grids with <smart-mobilems-masonry-grid> (MasonryGridComponent):"
|
|
66
|
+
printf ' %s\n' $MAS >&2
|
|
67
|
+
else
|
|
68
|
+
pass "no hand-rolled masonry instances"
|
|
69
|
+
fi
|
|
70
|
+
# 2. the Masonry provider exists ONLY to feed MasonryGridComponent. Providing it without ever
|
|
71
|
+
# rendering the component means the grid was hand-rolled some other way (CSS columns, flex, …),
|
|
72
|
+
# which the `new Masonry(` grep above cannot see.
|
|
73
|
+
if grep -rq "provide: Masonry" "$LIBDIR" 2>/dev/null; then
|
|
74
|
+
if grep -rq "smart-mobilems-masonry-grid" "$LIBDIR" 2>/dev/null; then
|
|
75
|
+
pass "grids render via <smart-mobilems-masonry-grid>"
|
|
76
|
+
else
|
|
77
|
+
bad "module provides Masonry but never renders <smart-mobilems-masonry-grid> — hand-rolled grid"
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
# 3. a list page must render through the framework grid
|
|
81
|
+
LISTP="$(find "$LIBDIR/pages" -maxdepth 1 -type d -name 'list*' 2>/dev/null || true)"
|
|
82
|
+
if [ -n "$LISTP" ]; then
|
|
83
|
+
if grep -rq "smart-mobilems-masonry-grid" $LISTP 2>/dev/null; then
|
|
84
|
+
pass "list page renders via <smart-mobilems-masonry-grid>"
|
|
85
|
+
else
|
|
86
|
+
bad "list page does not render <smart-mobilems-masonry-grid> (README.md#masonrygridcomponent)"
|
|
87
|
+
fi
|
|
88
|
+
fi
|
|
89
|
+
# 4. filters must EXTEND the framework base class — `context.` alone proves nothing, and a
|
|
90
|
+
# differently-named dir must not silently skip the check
|
|
91
|
+
FILT="$(find "$LIBDIR/components" -maxdepth 1 -type d \( -name 'filters*' -o -name 'search*' -o -name 'facet*' \) 2>/dev/null || true)"
|
|
92
|
+
if [ -n "$FILT" ]; then
|
|
93
|
+
if grep -rqE "FiltersBaseComponent" $FILT 2>/dev/null; then
|
|
94
|
+
pass "filters use the framework filter API"
|
|
95
|
+
else
|
|
96
|
+
bad "filters do not extend FiltersBaseComponent — no ad-hoc query building:"
|
|
97
|
+
printf ' %s\n' $FILT >&2
|
|
98
|
+
fi
|
|
99
|
+
else
|
|
100
|
+
pass "no filters component in this module"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
echo "== Translations (REF_ROOT=${REF_ROOT:-unset}) =="
|
|
104
|
+
# Groups: present in pl+en, no dead references, complete vs the reference repo
|
|
105
|
+
# (export REF_ROOT=<reference-repo-root> to enable the completeness diff).
|
|
106
|
+
if node "$SCRIPT_DIR/check-translations.js" "${TRANS_GROUPS:-NEWS ARTICLES}" "${REF_ROOT:-}"; then
|
|
107
|
+
pass "translation groups complete"
|
|
108
|
+
else
|
|
109
|
+
bad "translation gaps — fix the FAIL lines above"
|
|
110
|
+
fi
|
|
111
|
+
|
|
59
112
|
echo
|
|
60
113
|
if [ "$fail" = 0 ]; then echo "VERIFY: all checks passed."; else echo "VERIFY: FAILED — fix the items above and re-run." >&2; fi
|
|
61
114
|
exit "$fail"
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobilems-devops-config
|
|
3
|
+
description: Configure the ops / deployment layer of a MobilEMS museum web-app — Dockerfile, GitHub Actions CI/CD (development, e2e, push-to-client, ai-plan, ai-impl, cached-deps action), dependency-update tooling (tools/scripts/update.js + npm "update"), env/lint/node config (.env.dev, .eslintignore, .nvmrc), and .github hygiene (removing or relocating skills/agents/prompts that scaffolding left in .github). Use whenever the user wants to add / scaffold / fill in Docker, CI/CD, GitHub Actions, the deployment pipeline, or update tooling for a museum app, "skonfiguruj docker / github actions / ci dla projektu muzealnego", or bring an existing museum app's ops config up to standard. Idempotent — creates what's missing, reports drift, never silently overwrites. Requires the MobilEMS foundation (mobilems-project-init) to exist first. Does NOT touch SSR / translations / app.config (project-init's job) or feature modules.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# MobilEMS devops config
|
|
9
|
+
|
|
10
|
+
Install the MobilEMS museum app **ops / deployment layer** — the configuration that lives outside
|
|
11
|
+
the Angular app code: the Docker image, the GitHub Actions CI/CD pipeline, the dependency-update
|
|
12
|
+
tooling, and the env / lint / node-version files. This is the layer that `scaffold-nx-workspace`
|
|
13
|
+
(the generic Nx toolchain) and `mobilems-project-init` (the app-runtime foundation) leave out.
|
|
14
|
+
|
|
15
|
+
This skill is **self-contained**: the canonical file contents live in `references/` and are
|
|
16
|
+
written directly, so it works on a brand-new museum app (e.g. Łowicz) with no reference repo
|
|
17
|
+
present. It is also **idempotent and additive** — it creates what is missing and, when a file
|
|
18
|
+
already exists but differs from the canonical template, it reports the drift and asks before
|
|
19
|
+
overwriting. It never silently clobbers a customized file.
|
|
20
|
+
|
|
21
|
+
## Scope
|
|
22
|
+
|
|
23
|
+
**In scope (this skill):**
|
|
24
|
+
|
|
25
|
+
- `apps/web/Dockerfile` (+ a recommended `.dockerignore`)
|
|
26
|
+
- `.github/workflows/`: `development.yml`, `e2e.yml`, `push-to-client.yml`, `ai-plan.yml`,
|
|
27
|
+
`ai-impl.yml`, `ci.yml.disabled`, and the `.github/actions/cached-deps/action.yml` composite
|
|
28
|
+
- `.github` hygiene — agent config (`skills/`, `agents/`, `prompts/`, …) does not belong in
|
|
29
|
+
`.github`; this skill removes or relocates it
|
|
30
|
+
- `tools/scripts/update.js` + the `package.json` `update` / `postinstall` scripts
|
|
31
|
+
- `.env.dev`, `.eslintignore`, `.nvmrc` — and aligning the node version across `.nvmrc`,
|
|
32
|
+
`Dockerfile`, and the workflows
|
|
33
|
+
|
|
34
|
+
**Out of scope (handled elsewhere):**
|
|
35
|
+
|
|
36
|
+
- SSR (`server.ts`, `/env.js`, `/api/translations`), translations runtime, Tailwind, and
|
|
37
|
+
`app.config.ts` — these belong to **`mobilems-project-init`**. This skill only _verifies_ they
|
|
38
|
+
exist (precondition) and tells the user to run project-init if they don't.
|
|
39
|
+
- Feature modules — the `mobilems-<module>` skills.
|
|
40
|
+
- The companion **`<project>-deploy`** Kubernetes repo (scaffolded separately from
|
|
41
|
+
`mobilems-muzeum-template-deploy`). Mention it as a follow-up; do not create it here.
|
|
42
|
+
|
|
43
|
+
## Steps
|
|
44
|
+
|
|
45
|
+
### 0. Precondition — confirm a MobilEMS foundation
|
|
46
|
+
|
|
47
|
+
Run `scripts/detect-foundation.sh <workspace-root>`. It checks this is a MobilEMS museum app:
|
|
48
|
+
`apps/web` exists, SSR is set up (`apps/web/src/server.ts` with `/api/translations`),
|
|
49
|
+
`@smartsoft001-mobilems/*` is in `package.json`, and `apps/web/public/translations.json` exists.
|
|
50
|
+
If the SSR / translations foundation is missing, **stop** and tell the user to run
|
|
51
|
+
`/mobilems:mobilems-project-init` first — this skill only adds the ops layer on top.
|
|
52
|
+
|
|
53
|
+
### 1. Collect parameters (`AskUserQuestion`, with smart defaults)
|
|
54
|
+
|
|
55
|
+
- **Node version** — default to the existing `.nvmrc` if present; otherwise read the node version
|
|
56
|
+
already used in `apps/web/Dockerfile` / workflows; otherwise prompt (fall back to the team
|
|
57
|
+
standard `24.5.0`). This one value threads into `.nvmrc`, the Dockerfile base image, and every
|
|
58
|
+
workflow's `node-version`.
|
|
59
|
+
- **`.env.dev` values** — `API_URL`, `SITE_URL`, `MULTIMEDIA_URL` (and `APP_VERSION`). Read them
|
|
60
|
+
from an existing `.env` / `.env.dev`, or from `apps/web/src/environments/environment.prod.ts`
|
|
61
|
+
(`apiUrl` / `multimediaUrl`), or prompt. Never invent client URLs silently.
|
|
62
|
+
|
|
63
|
+
### 2. Docker
|
|
64
|
+
|
|
65
|
+
Per `references/docker.md`: write `apps/web/Dockerfile`, threading the node version into the base
|
|
66
|
+
image (`node:<version>-alpine`). Add the recommended `.dockerignore` (the reference repo has none,
|
|
67
|
+
but it keeps the buildx context small). If a Dockerfile already exists and differs, show a diff and
|
|
68
|
+
ask before overwriting.
|
|
69
|
+
|
|
70
|
+
### 3. GitHub Actions
|
|
71
|
+
|
|
72
|
+
Per `references/workflows.md`: write each workflow and the composite action, threading the node
|
|
73
|
+
version:
|
|
74
|
+
|
|
75
|
+
- `.github/workflows/development.yml` — build + Docker buildx push to `dockerregistry.mobilems.pl`
|
|
76
|
+
- auto-commit/tag + dispatch `deploy.yml` to the deploy repo.
|
|
77
|
+
- `.github/workflows/e2e.yml` — Playwright (chromium + webkit) + report artifact.
|
|
78
|
+
- `.github/workflows/push-to-client.yml` — GitLab mirror push + OpenVPN + SSH `docker stack deploy`.
|
|
79
|
+
- `.github/workflows/ai-plan.yml` / `ai-impl.yml` — call the shared
|
|
80
|
+
`smartsoft001/github-actions-shared` reusable workflows.
|
|
81
|
+
- `.github/workflows/ci.yml.disabled` — Nx-Cloud lint/test/build/e2e template, kept disabled.
|
|
82
|
+
- `.github/actions/cached-deps/action.yml` — composite: setup-node + cache `node_modules` + `npm ci`.
|
|
83
|
+
|
|
84
|
+
Then **print the required GitHub secrets table** from `references/workflows.md` (GitLab registry,
|
|
85
|
+
deploy dispatch, OpenVPN/SSH, Claude/Linear). These are configured in the repo's Actions settings,
|
|
86
|
+
**never** committed.
|
|
87
|
+
|
|
88
|
+
For each existing-but-divergent workflow (e.g. a stale Node-20 `ci.yml`), report it and ask before
|
|
89
|
+
overwriting — do not silently replace a workflow the team may have customized.
|
|
90
|
+
|
|
91
|
+
### 3b. `.github` hygiene — nothing but GitHub config in `.github`
|
|
92
|
+
|
|
93
|
+
Per `references/github-hygiene.md`: `.github/skills/`, `.github/agents/`, `.github/prompts/`,
|
|
94
|
+
`.github/instructions/`, `.github/chatmodes/` and `.github/copilot-instructions.md` are scaffolding
|
|
95
|
+
leftovers in the Copilot layout — agent config targets the Claude Code architecture and belongs under
|
|
96
|
+
`.claude/` (`skills/`, `agents/`, `commands/`, `settings.json`). Classify each entry, then:
|
|
97
|
+
|
|
98
|
+
- **delete** what duplicates a plugin already enabled in `.claude/settings.json` — the whole `nx-*`
|
|
99
|
+
family and `monitor-ci` come from `nx@nx-claude-plugins`;
|
|
100
|
+
- **move** anything genuinely project-specific into `.claude/` (`skills/`, `agents/`, `commands/`)
|
|
101
|
+
with `git mv`;
|
|
102
|
+
- keep `workflows/`, `actions/` and GitHub's own metadata.
|
|
103
|
+
|
|
104
|
+
Always show the delete/move plan and **ask before deleting**. Report what was removed or relocated in
|
|
105
|
+
the final summary.
|
|
106
|
+
|
|
107
|
+
### 4. Update tooling
|
|
108
|
+
|
|
109
|
+
Per `references/tooling.md`: write `tools/scripts/update.js`; ensure `package.json` has
|
|
110
|
+
`"update": "node ./tools/scripts/update.js"` and `"postinstall": "husky install"` (merge into the
|
|
111
|
+
existing `scripts` block — do not drop existing scripts).
|
|
112
|
+
|
|
113
|
+
### 5. Env / lint / node
|
|
114
|
+
|
|
115
|
+
Per `references/env-and-lint.md`: write `.env.dev` (with the collected URLs), `.eslintignore`, and
|
|
116
|
+
`.nvmrc`. Confirm the node version is identical across `.nvmrc`, `apps/web/Dockerfile`, and all
|
|
117
|
+
workflows; fix any mismatch.
|
|
118
|
+
|
|
119
|
+
### 6. Verify
|
|
120
|
+
|
|
121
|
+
Run `scripts/verify-devops.sh <workspace-root> <NODE_VERSION>`. It asserts every artifact landed and
|
|
122
|
+
that the node version is consistent across `.nvmrc` / Dockerfile / workflows. Fix and re-run until it
|
|
123
|
+
passes. Print a final summary of what was **created** vs **already present** vs **drift reported**.
|
|
124
|
+
Gate `npm install` behind an explicit ask — skip it during self-validation / eval.
|
|
125
|
+
|
|
126
|
+
### 7. Follow-up note
|
|
127
|
+
|
|
128
|
+
Remind the user that production deployment also needs the companion **`<project>-deploy`** repo
|
|
129
|
+
(Kubernetes/Kustomize, from `mobilems-muzeum-template-deploy`) and the GitHub secrets from step 3 —
|
|
130
|
+
both are separate, manual follow-ups outside this skill.
|
|
131
|
+
|
|
132
|
+
## Notes
|
|
133
|
+
|
|
134
|
+
- Docker-only deployment (no nginx / docker-compose); SSR served by `node server/server.mjs` on
|
|
135
|
+
port 80.
|
|
136
|
+
- All client-specific values stay as **GitHub Actions secrets** — the workflow files contain only
|
|
137
|
+
`${{ secrets.* }}` references, never literals.
|
|
138
|
+
- Bundled `references/` + `scripts/` ship automatically (the whole `src/plugins/**` tree is copied
|
|
139
|
+
to dist). Do NOT hand-edit `plugin.json` — the version is auto-synced at build.
|
|
140
|
+
- Run order for a fresh app: `/smart:scaffold-nx-workspace` → `/mobilems:mobilems-project-init` →
|
|
141
|
+
feature skills → **`/mobilems:mobilems-devops-config`**.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Docker
|
|
2
|
+
|
|
3
|
+
Docker-only deployment (no nginx, no docker-compose). The image runs the Angular SSR server
|
|
4
|
+
(`node server/server.mjs`) on port 80. The application is built in CI **before** the image is
|
|
5
|
+
built; the Dockerfile only installs production deps and copies the prebuilt `dist/apps/web`.
|
|
6
|
+
|
|
7
|
+
`{{NODE_VERSION}}` = the node version collected in step 1 (default from `.nvmrc`, else the team
|
|
8
|
+
standard `24.5.0`). Keep it identical to `.nvmrc` and the workflows.
|
|
9
|
+
|
|
10
|
+
## `apps/web/Dockerfile`
|
|
11
|
+
|
|
12
|
+
```dockerfile
|
|
13
|
+
FROM node:{{NODE_VERSION}}-alpine
|
|
14
|
+
|
|
15
|
+
# Set working directory
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
|
|
18
|
+
# Copy package files and install production dependencies
|
|
19
|
+
COPY package.json package-lock.json ./
|
|
20
|
+
RUN npm ci --omit=dev --ignore-scripts
|
|
21
|
+
|
|
22
|
+
# Copy the built application from dist folder (already built in CI)
|
|
23
|
+
COPY dist/apps/web ./
|
|
24
|
+
|
|
25
|
+
# Build argument for version
|
|
26
|
+
ARG VERSION
|
|
27
|
+
ENV VERSION=$VERSION
|
|
28
|
+
|
|
29
|
+
# Set environment variables
|
|
30
|
+
ENV NODE_ENV=production
|
|
31
|
+
ENV PORT=80
|
|
32
|
+
|
|
33
|
+
# Expose the application port
|
|
34
|
+
EXPOSE 80
|
|
35
|
+
|
|
36
|
+
# Start the SSR server
|
|
37
|
+
CMD ["node", "server/server.mjs"]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## `.dockerignore` (recommended — the reference repo has none)
|
|
41
|
+
|
|
42
|
+
The `development.yml` build context is the whole repo (`docker buildx build ... .`). The Dockerfile
|
|
43
|
+
only `COPY`s `package*.json` and `dist/apps/web`, so a `.dockerignore` doesn't change the image, but
|
|
44
|
+
it keeps the build context small/fast. Add it unless the team objects:
|
|
45
|
+
|
|
46
|
+
```gitignore
|
|
47
|
+
node_modules
|
|
48
|
+
.git
|
|
49
|
+
.github
|
|
50
|
+
.nx
|
|
51
|
+
.angular
|
|
52
|
+
coverage
|
|
53
|
+
dist/.playwright
|
|
54
|
+
**/*.spec.ts
|
|
55
|
+
*.log
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Note: do **not** ignore `dist/` — the build context must include `dist/apps/web`.
|