ultimate-jekyll-manager 1.8.0 → 1.9.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/CHANGELOG.md +27 -0
- package/CLAUDE.md +3 -2
- package/dist/assets/js/libs/form-manager.js +7 -1
- package/dist/assets/js/pages/token/index.js +37 -3
- package/docs/audit.md +71 -20
- package/docs/cdp-debugging.md +48 -0
- package/docs/javascript-libraries.md +8 -0
- package/docs/local-development.md +1 -1
- package/docs/themes.md +3 -2
- package/logs/test.log +38 -38
- package/package.json +9 -8
- package/.claude/scheduled_tasks.lock +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
---
|
|
18
|
+
## [1.9.0] - 2026-06-17
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Token page: MCP OAuth flow.** When the `/token` page receives `?mcp=true&redirect_uri=...&state=...`, it completes an OAuth-style handshake — the user signs in via Firebase Auth, the page obtains a fresh ID token, and redirects back to the `redirect_uri` with `code=<idToken>&state=<state>`. This lets Claude (or any MCP client) authenticate users through the existing sign-in UI without a custom OAuth server.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- **Dev-URL guidance updated** — docs (local-development.md, themes.md, cdp-debugging.md, CLAUDE.md) now say "prefer `https://localhost:4000`; fall back to the network IP if localhost doesn't connect" instead of hardcoding a specific IP.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
## [1.8.2] - 2026-06-14
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **FormManager: `data-fm-keep-disabled` opt-out for permanently-disabled fields.** `_setDisabled` blanket-toggles `disabled` on every control in the form (loading/submitting ↔ ready), which silently re-enabled fields meant to STAY disabled — e.g. "coming soon" radio options rendered inside a managed form. Controls carrying `data-fm-keep-disabled` are now always forced to `disabled = true` and never re-enabled. Documented in `docs/javascript-libraries.md` (FormManager section).
|
|
34
|
+
- **`docs/cdp-debugging.md` — launching a controllable browser (mirrored across UJM/BEM/BXM/EM).** The canonical Chrome launch for agents and humans: CDP port + REQUIRED dedicated `--user-data-dir` (Chrome 136+ silently ignores the debug port on the default profile — verified on 149), the persistent agent profile (`~/Library/Application Support/chrome-profiles/agent` — log in once, state survives relaunches, verified), the shared-instance model (CDP is multi-client — agents share the one logged-in Chrome on one port, one tab each; a second profile/port only for a second identity), safe quit by profile match, and driving via the `chrome-devtools` MCP (`CHROME_CDP_PORT` set before the session) or any CDP client. UJM flavor: point it at the dev server for the edit → live-reload → screenshot/console/network loop. Indexed in CLAUDE.md.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
## [1.8.1] - 2026-06-11
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- **`docs/audit.md` rewritten as the full-audit check catalog (`/omega:ujm audit`).** The two-stage site audit became an ID'd catalog: mirrored universal checks (U-01..U-14 — tests at every layer, XSS, secrets, config canon, doc parity, dead code, dep health, …), UJM-specific checks (UJM-01..UJM-10 — inline-script ban, theme-prefixed classes, content writing rules, SEO meta, purge safelist, reads-vs-writes, page-module pattern, images, a11y), and framework-repo checks (F-01..F-04 — sister parity, defaults sync, docs completeness, green framework suite), with scope auto-detect (consumer vs framework via package.json), a persisted findings report (`.temp/audit/claude-audit.md`), a severity-ordered TodoWrite fix loop, and the `npx mgr audit` automated stage absorbed from the old doc. Wired to the `omega:ujm` router's Audit process; `docs/audit.md` is mirrored across BEM/BXM/EM. CLAUDE.md's docs index updated to match.
|
|
42
|
+
- **package.json `keywords` corrected** — replaced the stale template list (`Autoprefixer`, `imagemin` — not used; `Browsersync` — true but noise) with accurate, discovery-oriented ones (`jekyll`, `static-site`, `static-site-generator`, `website`, `seo`, `gulp`, `sass`, `webpack`, `postcss`). npm-listing metadata only; no behavior change. Mirrored across BEM/BXM/EM.
|
|
43
|
+
|
|
17
44
|
---
|
|
18
45
|
## [1.8.0] - 2026-06-11
|
|
19
46
|
|
package/CLAUDE.md
CHANGED
|
@@ -174,7 +174,7 @@ Note: `-t` short alias belongs to `translation`. The `test` command uses `--test
|
|
|
174
174
|
## Development Workflow
|
|
175
175
|
|
|
176
176
|
- **🚫 NEVER run `npm start` in a consumer project** — the user runs the dev server; running it again kills theirs. Assume it's already running; if it isn't, **instruct the user to run it** rather than running it yourself. Instead, **check `logs/dev.log`** after editing files to confirm the watcher recompiled successfully (`Reloading Browsers...` = success; `errored` = fix the error) — never tail/attach to the process. If editing multiple files, check the log once after the last edit. A change that breaks the build is not a completed change. Running `npx mgr test` is fine.
|
|
177
|
-
- **Live-test UI changes via CDP.** After code changes compile, use the `chrome-devtools` MCP tools (screenshots, click, evaluate JS, console logs) to verify the change works in the running browser. This is the primary way to confirm UI changes — type-checking and test suites verify code correctness, not feature correctness. See `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
|
|
177
|
+
- **Live-test UI changes via CDP.** After code changes compile, use the `chrome-devtools` MCP tools (screenshots, click, evaluate JS, console logs) to verify the change works in the running browser. This is the primary way to confirm UI changes — type-checking and test suites verify code correctness, not feature correctness. Read the URL from the consumer's `.temp/_config_browsersync.yml`. Prefer `https://localhost:4000`; fall back to the local network IP (e.g. `https://192.168.x.x:4000`) if localhost doesn't connect. See [docs/cdp-debugging.md](docs/cdp-debugging.md) + `~/.claude/mcp-server/servers/chrome-devtools/CLAUDE.md`.
|
|
178
178
|
|
|
179
179
|
## File Conventions
|
|
180
180
|
|
|
@@ -209,7 +209,7 @@ Deep references live in `docs/`. Treat docs as a first-class deliverable. **When
|
|
|
209
209
|
- [docs/test-boot-layer.md](docs/test-boot-layer.md) — boot layer deep-dive (_site/ discovery, HTTP server, fixture vs consumer)
|
|
210
210
|
- [docs/environment-detection.md](docs/environment-detection.md) — `isTesting`/`isDevelopment`/`isProduction`/`getVersion`
|
|
211
211
|
- [docs/jekyll-plugin.md](docs/jekyll-plugin.md) — UJ Powertools gem: filters, tags, page variables (`page.resolved`, `uj_icon`, `uj_hash`, `iftruthy`, etc.)
|
|
212
|
-
- [docs/audit.md](docs/audit.md) —
|
|
212
|
+
- [docs/audit.md](docs/audit.md) — full-audit check catalog (U-xx universal / UJM-xx / F-xx IDs with severity + scope), protocol + fix loop, `npx mgr audit` automated stage
|
|
213
213
|
- [docs/migration.md](docs/migration.md) — full migration (old UJ → latest UJM base), `_config.yml` quick-fix schema, revert-posts procedure
|
|
214
214
|
|
|
215
215
|
### Project & dev environment
|
|
@@ -218,6 +218,7 @@ Deep references live in `docs/`. Treat docs as a first-class deliverable. **When
|
|
|
218
218
|
- [docs/build-system.md](docs/build-system.md) — gulp pipeline (15 tasks), config flow, build modes, pure helpers
|
|
219
219
|
- [docs/templating.md](docs/templating.md) — node-powertools bracket conventions, Liquid coexistence
|
|
220
220
|
- [docs/local-development.md](docs/local-development.md) — browsersync URL, Firebase emulator connect, PurgeCSS safelist
|
|
221
|
+
- [docs/cdp-debugging.md](docs/cdp-debugging.md) — launching a controllable Chrome (CDP) at the dev server, persistent agent profile (one-time logins), driving via MCP/CDP (screenshots, clicks, network)
|
|
221
222
|
- [docs/logging.md](docs/logging.md) — `dev.log` / `build.log` / `test.log` tee, CI skip
|
|
222
223
|
- [docs/common-mistakes.md](docs/common-mistakes.md) — the canonical "don't do this" list
|
|
223
224
|
- [docs/assets.md](docs/assets.md) — UJM vs consumer file layout, section config (nav/footer/account), frontmatter-driven page customization, webpack aliases, page module pattern
|
|
@@ -782,7 +782,9 @@ export class FormManager {
|
|
|
782
782
|
}
|
|
783
783
|
|
|
784
784
|
/**
|
|
785
|
-
* Enable/disable form controls
|
|
785
|
+
* Enable/disable form controls. Fields marked data-fm-keep-disabled stay
|
|
786
|
+
* disabled permanently (e.g. "coming soon" options rendered inside a
|
|
787
|
+
* managed form) — the toggle never re-enables them.
|
|
786
788
|
*/
|
|
787
789
|
_setDisabled(disabled) {
|
|
788
790
|
/* @dev-only:start */
|
|
@@ -792,6 +794,10 @@ export class FormManager {
|
|
|
792
794
|
/* @dev-only:end */
|
|
793
795
|
|
|
794
796
|
this.$form.querySelectorAll('button, input, select, textarea').forEach(($el) => {
|
|
797
|
+
if ($el.dataset.fmKeepDisabled !== undefined) {
|
|
798
|
+
$el.disabled = true;
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
795
801
|
$el.disabled = disabled;
|
|
796
802
|
});
|
|
797
803
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// This file is required by /token page to generate custom auth tokens for extensions/apps
|
|
2
|
+
// Also handles MCP OAuth flow: user signs in → Firebase ID token sent back to Claude as auth code
|
|
2
3
|
import authorizedFetch from '__main_assets__/js/libs/authorized-fetch.js';
|
|
3
4
|
import webManager from 'web-manager';
|
|
4
5
|
|
|
@@ -11,19 +12,27 @@ export default function () {
|
|
|
11
12
|
// Get URL params
|
|
12
13
|
const url = new URL(window.location.href);
|
|
13
14
|
const authReturnUrl = url.searchParams.get('authReturnUrl');
|
|
15
|
+
const mcpRedirectUri = url.searchParams.get('redirect_uri');
|
|
16
|
+
const mcpState = url.searchParams.get('state');
|
|
17
|
+
const isMcp = url.searchParams.get('mcp') === 'true';
|
|
14
18
|
|
|
15
19
|
// Handle DOM ready
|
|
16
20
|
webManager.dom().ready()
|
|
17
21
|
.then(async () => {
|
|
18
22
|
// Log
|
|
19
|
-
console.log('[Token] Initialized. authReturnUrl:', authReturnUrl);
|
|
23
|
+
console.log('[Token] Initialized.', isMcp ? 'MCP OAuth flow' : 'Standard flow', 'authReturnUrl:', authReturnUrl);
|
|
20
24
|
|
|
21
|
-
// Validate
|
|
25
|
+
// Validate redirect URLs
|
|
22
26
|
if (authReturnUrl && !webManager.isValidRedirectUrl(authReturnUrl)) {
|
|
23
27
|
showError('Invalid redirect URL');
|
|
24
28
|
return;
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
if (isMcp && !mcpRedirectUri) {
|
|
32
|
+
showError('Missing redirect_uri for MCP OAuth flow');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
// Wait for auth to be ready and get user
|
|
28
37
|
webManager.auth().listen({ once: true }, async (state) => {
|
|
29
38
|
const user = state.user;
|
|
@@ -35,7 +44,32 @@ export default function () {
|
|
|
35
44
|
}
|
|
36
45
|
|
|
37
46
|
try {
|
|
38
|
-
//
|
|
47
|
+
// MCP OAuth flow: return Firebase ID token as the authorization code
|
|
48
|
+
if (isMcp && mcpRedirectUri) {
|
|
49
|
+
updateStatus('Completing MCP authorization...');
|
|
50
|
+
|
|
51
|
+
const idToken = await webManager.auth().getIdToken(true);
|
|
52
|
+
const returnUrl = new URL(mcpRedirectUri);
|
|
53
|
+
returnUrl.searchParams.set('code', idToken);
|
|
54
|
+
|
|
55
|
+
if (mcpState) {
|
|
56
|
+
returnUrl.searchParams.set('state', mcpState);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const redirectUrl = returnUrl.toString();
|
|
60
|
+
console.log('[Token] MCP redirect to:', redirectUrl);
|
|
61
|
+
|
|
62
|
+
updateStatus('Redirecting to Claude...');
|
|
63
|
+
|
|
64
|
+
setTimeout(() => {
|
|
65
|
+
updateStatus('If you were not redirected, <a href="' + webManager.utilities().escapeHTML(redirectUrl) + '">click here to try again</a>.', true);
|
|
66
|
+
}, 3000);
|
|
67
|
+
|
|
68
|
+
window.location.href = redirectUrl;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Standard flow: generate custom token via BEM API
|
|
39
73
|
updateStatus('Generating secure token...');
|
|
40
74
|
const token = await generateCustomToken();
|
|
41
75
|
|
package/docs/audit.md
CHANGED
|
@@ -1,31 +1,82 @@
|
|
|
1
1
|
# Audit Workflow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Full-project audit for UJM — runs against a CONSUMER site or the FRAMEWORK repo itself (scope auto-detected). Invoked via the `omega:ujm` skill (`/omega:ujm audit`) or any "audit this site/project" request.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Every check has a stable ID, a severity, and a scope. Findings are reported as `ID @ file:line`, fixed one at a time, then re-verified. The tables below do NOT restate the rules — each check links to the doc that owns the rule and the fix.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
2. **Enforce content conventions** — headings start with action verbs, sentence case, headline/accent structure (see [seo.md](seo.md#content-writing-rules-applies-to-all-pages)). Skip front matter (`meta.title` etc. are controlled by blueprints/layouts), test pages, and blog posts.
|
|
9
|
-
3. **Fix spelling and grammar** in body text — skip code blocks, attributes, URLs.
|
|
10
|
-
4. **XSS / HTML escaping audit** — flag unsafe `innerHTML` assignments; fix with `webManager.utilities().escapeHTML()` (+ `sanitizeURL` for URL sinks). See [xss-prevention.md](xss-prevention.md).
|
|
11
|
-
5. **Inline `<script>` audit (HARD RULE)** — scan all HTML under `src/` for inline script bodies and move them per the playbook in [no-inline-scripts.md](no-inline-scripts.md).
|
|
12
|
-
6. **Summarize** — list files scanned and fixes applied before moving to stage 2.
|
|
7
|
+
## Protocol
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
1. **Detect scope** — read `package.json`: `name` is `ultimate-jekyll-manager` → **framework audit** (U + UJM + F checks); `ultimate-jekyll-manager` in (dev)dependencies → **consumer audit** (U + UJM checks).
|
|
10
|
+
2. **Run the catalog** — every check matching the scope. Search with Grep/Glob/Read over `src/` (+ `test/`, config files); ALWAYS exclude `dist/`, `node_modules/`, `_site/`, `_legacy/`, `_backup/`, `.temp/`, `.cache/`. Record each finding as `ID @ file:line` + a one-line description.
|
|
11
|
+
3. **Persist the report** — write the findings list to `.temp/audit/claude-audit.md` (the same scratch dir `npx mgr audit` uses) so a long fix loop survives session breaks. Summarize counts by severity in chat.
|
|
12
|
+
4. **Fix loop** — TodoWrite per finding, highest severity first, ONE at a time: mark in-progress → root cause → fix → verify → complete. Ask before structural or destructive fixes (file deletions, layout swaps, data changes).
|
|
13
|
+
5. **Re-verify** — re-run every check that produced findings until clean; finish with `npx mgr test` (must be green).
|
|
14
|
+
6. **Doc parity** — if fixes changed behavior, update README / CLAUDE.md / `docs/<topic>.md` / CHANGELOG in the same change set.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
3. **Create a TODO list** — break fixes into atomic tasks, organized by category and priority.
|
|
19
|
-
4. **Fix systematically** — one issue at a time: mark in-progress → navigate → understand root cause → fix → verify → mark complete. Work one category at a time; do NOT attempt to fix everything at once.
|
|
20
|
-
5. **Re-run `npx mgr audit`** after each batch — confirm fixed issues are resolved and no new issues appeared.
|
|
16
|
+
Severity: **CRIT** security or broken functionality · **HIGH** hard-rule violation · **MED** convention drift · **LOW** optional improvement.
|
|
17
|
+
Scope: **C** consumer · **F** framework repo · **B** both.
|
|
21
18
|
|
|
22
|
-
##
|
|
19
|
+
## Universal checks (U-xx)
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
Mirrored across all four OMEGA frameworks (UJM / BEM / BXM / EM) — same ID means the same check everywhere.
|
|
22
|
+
|
|
23
|
+
| ID | Sev | Scope | Check |
|
|
24
|
+
|----|-----|-------|-------|
|
|
25
|
+
| U-01 | HIGH | B | Every feature has tests at EVERY layer it surfaces (build / page / boot) — never mocked ([test-framework.md](test-framework.md)) |
|
|
26
|
+
| U-02 | HIGH | B | Test hygiene — real-external-API tests gated behind `TEST_EXTENDED_MODE` in-source (not mocked); no tests that assert nothing ([test-framework.md](test-framework.md)) |
|
|
27
|
+
| U-03 | CRIT | B | XSS — inline `webManager.utilities().escapeHTML(value)` at EVERY DOM sink, `sanitizeURL(url)` at executable URL sinks, zero local escape helpers ([xss-prevention.md](xss-prevention.md)) |
|
|
28
|
+
| U-04 | HIGH | B | web-manager owns Firebase — no direct `firebase` imports anywhere; `webManager.auth()` / `.firestore()` ([javascript-libraries.md](javascript-libraries.md)) |
|
|
29
|
+
| U-05 | HIGH | C | No UJM transitive deps installed in the consumer `package.json` (`firebase`, `web-manager`, …) — webpack `resolve.modules` resolves them ([common-mistakes.md](common-mistakes.md)) |
|
|
30
|
+
| U-06 | HIGH | B | Env behavior gated on the INTENTIONAL check — `isProduction()` or `isDevelopment() \|\| isTesting()`, never `!isDevelopment()`; no ad-hoc `process.env.UJ_*` reads where a helper exists ([environment-detection.md](environment-detection.md)) |
|
|
31
|
+
| U-07 | HIGH | B | Config canon — `src/_config.yml` + `config/ultimate-jekyll-manager.json` match the documented shapes; canonical cross-framework blocks (`brand`, flat 8-key `firebaseConfig`, …) not reinvented ([CLAUDE.md](../CLAUDE.md) §Config flow) |
|
|
32
|
+
| U-08 | CRIT | B | No private credentials committed — `.env`, tokens, secret keys; `.gitignore` covers them. (The Firebase WEB `apiKey` is public by design — do NOT flag it.) |
|
|
33
|
+
| U-09 | HIGH | B | Source discipline — nothing edited in `dist/` / `_site/`; no live code referencing `_legacy/` / `_backup/` ([common-mistakes.md](common-mistakes.md)) |
|
|
34
|
+
| U-10 | MED | B | Doc parity — README / CLAUDE.md / `docs/` / CHANGELOG match shipped behavior; CLAUDE.md < 250 lines; the docs index lists every `docs/*.md`; no stale names for renamed commands/patterns |
|
|
35
|
+
| U-11 | MED | B | SSOT/DRY — no duplicated constants/config/logic; one authoritative home per value, imported everywhere else |
|
|
36
|
+
| U-12 | MED | B | JS conventions — file structure, JSDoc, short-circuit returns, leading logical operators, `fs-jetpack`, one `module.exports` per file (global `js:patterns` skill + [CLAUDE.md](../CLAUDE.md) §File Conventions) |
|
|
37
|
+
| U-13 | MED | B | Dead code & stale patterns — no orphaned `src/` files nothing imports; no leftovers of migrated-away formats ([migration.md](migration.md)); inventory TODO/FIXME (report only) |
|
|
38
|
+
| U-14 | LOW | B | Dependency health — review `npm outdated` / `npm audit`; apply fixes via the `general:update-packages` workflow (includes supply-chain checks) |
|
|
39
|
+
|
|
40
|
+
## UJM-specific checks
|
|
41
|
+
|
|
42
|
+
| ID | Sev | Scope | Check |
|
|
43
|
+
|----|-----|-------|-------|
|
|
44
|
+
| UJM-01 | CRIT | B | ZERO inline `<script>` bodies in HTML under `src/` — JS belongs in page modules / `main.js`; migrate per the playbook ([no-inline-scripts.md](no-inline-scripts.md)) |
|
|
45
|
+
| UJM-02 | HIGH | B | Bootstrap-first markup; NO theme-prefixed (`<themeid>-*`) classes in pages/includes — theme SCSS restyles universal classes ([themes.md](themes.md), [css.md](css.md)) |
|
|
46
|
+
| UJM-03 | MED | C | Content writing rules — action-verb headings, sentence case, headline/accent structure; skip front matter, test pages, and blog posts ([seo.md](seo.md#content-writing-rules-applies-to-all-pages)) |
|
|
47
|
+
| UJM-04 | MED | C | Spelling and grammar in body text — skip code blocks, attributes, URLs |
|
|
48
|
+
| UJM-05 | HIGH | C | SEO meta — custom pages carry `meta.title` / `meta.description`; default pages customize via frontmatter per the per-page levels table ([layouts-and-pages.md](layouts-and-pages.md), [seo.md](seo.md)) |
|
|
49
|
+
| UJM-06 | HIGH | B | PurgeCSS — every dynamically-added JS class is safelisted ([purgecss.md](purgecss.md)) |
|
|
50
|
+
| UJM-07 | HIGH | C | Reads-vs-writes — Firestore SDK for dashboard READS only; all WRITES go through Cloud Functions ([javascript-libraries.md](javascript-libraries.md)) |
|
|
51
|
+
| UJM-08 | HIGH | B | Page JS pattern — modules at `src/assets/js/pages/<path>/index.js` with element-existence guards; forms via FormManager; no Liquid in JS (use `data-*` / `<template>` bridges) ([assets.md](assets.md), [page-loading.md](page-loading.md), [no-inline-scripts.md](no-inline-scripts.md)) |
|
|
52
|
+
| UJM-09 | MED | C | Images — imagemin source-size constraints respected, `data-lazy` lazy loading used, `@post/` shortcut in blog posts ([images.md](images.md), [lazy-loading.md](lazy-loading.md)) |
|
|
53
|
+
| UJM-10 | MED | B | Accessibility basics — meaningful `alt` text, labeled form fields, real `<button>`/`<a>` elements (no clickable `div`s) |
|
|
54
|
+
|
|
55
|
+
## Automated stage: `npx mgr audit`
|
|
56
|
+
|
|
57
|
+
After (or alongside) the catalog, run UJM's built-in audit tool — HTML validation + spellcheck + optional Lighthouse:
|
|
58
|
+
|
|
59
|
+
1. **Ask the user** whether to run it or whether they've already run it; run `npx mgr audit` if needed.
|
|
60
|
+
2. **Read EVERY file in `.temp/audit/` COMPLETELY** — audit outputs are large; don't plan from a skim.
|
|
61
|
+
3. **Fold the findings into the same TodoWrite fix loop** — one category at a time; do NOT attempt everything at once.
|
|
62
|
+
4. **Re-run `npx mgr audit` after each batch** — confirm fixed issues resolved and no new ones appeared.
|
|
63
|
+
|
|
64
|
+
Implementation: [`src/gulp/tasks/audit.js`](../src/gulp/tasks/audit.js); results land in `<projectRoot>/.temp/audit/`.
|
|
65
|
+
|
|
66
|
+
## Framework-repo checks (F-xx)
|
|
67
|
+
|
|
68
|
+
Only when auditing the UJM repo itself. Mirrored across the four frameworks.
|
|
69
|
+
|
|
70
|
+
| ID | Sev | Check |
|
|
71
|
+
|----|-----|-------|
|
|
72
|
+
| F-01 | MED | Sister parity — mirrored sections (config shapes, test contract, CLAUDE.md skeleton, shared env/test conventions) in sync with BEM / BXM / EM; deviations are deliberate and documented |
|
|
73
|
+
| F-02 | HIGH | Consumer-shipped defaults in sync — what `npx mgr setup` scaffolds matches current conventions and docs |
|
|
74
|
+
| F-03 | MED | Docs completeness — every `docs/*.md` indexed in CLAUDE.md; every subsystem has a doc; no "(planned)" links for things that have shipped |
|
|
75
|
+
| F-04 | HIGH | `npx mgr test mgr:` green before treating the audit as complete |
|
|
26
76
|
|
|
27
77
|
## See also
|
|
28
78
|
|
|
29
|
-
- [seo.md](seo.md) — the content conventions
|
|
30
|
-
- [xss-prevention.md](xss-prevention.md) — escaping rules
|
|
31
|
-
- [no-inline-scripts.md](no-inline-scripts.md) — the
|
|
79
|
+
- [seo.md](seo.md) — the content conventions UJM-03 enforces
|
|
80
|
+
- [xss-prevention.md](xss-prevention.md) — the escaping rules behind U-03
|
|
81
|
+
- [no-inline-scripts.md](no-inline-scripts.md) — the UJM-01 migration playbook
|
|
82
|
+
- [test-framework.md](test-framework.md) — the layers behind U-01 / U-02
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# CDP Debugging (driving a live browser)
|
|
2
|
+
|
|
3
|
+
How to launch a browser you can CONTROL — see the site live, screenshot it, click, type, read console logs, inspect network requests — for agents (Claude via MCP/CDP) and humans.
|
|
4
|
+
|
|
5
|
+
> Mirrored across the four sister frameworks (UJM / BEM / BXM / EM) — same core section, framework-flavored. Edit all four together.
|
|
6
|
+
|
|
7
|
+
## Launching a controllable Chrome (the canonical command)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
open -gna "Google Chrome" --args \
|
|
11
|
+
--remote-debugging-port=9223 \
|
|
12
|
+
--user-data-dir="$HOME/Library/Application Support/chrome-profiles/agent" \
|
|
13
|
+
--no-first-run --no-default-browser-check \
|
|
14
|
+
--disable-background-timer-throttling \
|
|
15
|
+
--disable-backgrounding-occluded-windows \
|
|
16
|
+
--disable-renderer-backgrounding \
|
|
17
|
+
https://localhost:4000 # ← the UJM dev server (or the IP from .temp/_config_browsersync.yml if localhost doesn't connect)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Verify it's up: `curl -s http://127.0.0.1:9223/json/version`
|
|
21
|
+
|
|
22
|
+
The rules that make this work (each one learned the hard way):
|
|
23
|
+
|
|
24
|
+
- **`open -gna` launches WITHOUT stealing focus.** `-g` = don't bring to foreground, `-n` = new instance (required — without it `open` just activates the already-running daily Chrome and the `--args` are ignored). Launching the Chrome binary directly ALWAYS activates the app and steals focus. Do NOT use `-j`/`--hide` — animations need a visible window; instead the three `--disable-*` flags keep timers/rAF/rendering at FULL speed while the window sits behind your work (verified: rAF at the display's native 120fps while backgrounded, focus never moved).
|
|
25
|
+
- **`--user-data-dir` is REQUIRED, not optional.** Chrome 136+ **silently ignores** `--remote-debugging-port` on the default profile — no error, no port, nothing (verified on Chrome 149). This is the #1 "why isn't CDP up" trap.
|
|
26
|
+
- **The profile dir IS the persistent login state.** Cookies + localStorage survive relaunches (verified by round-trip). **Log into sites once in the agent profile and every agent reuses the authenticated state.** Ecosystem convention: ONE shared profile at `~/Library/Application Support/chrome-profiles/agent` across all four frameworks, so logins are a one-time setup.
|
|
27
|
+
- **One Chrome instance per profile dir — but MANY agents per instance.** CDP is multi-client (verified: two concurrent clients driving different tabs of one instance): agents and sessions attach to the SAME port, each drives its own tab, and all share the profile's logins. One agent per tab is the only rule. A second launch with the same dir just opens a window in the existing instance and **ignores the new debug port** — attach to the running one instead. Reach for a second profile + port (`…/b` on 9224) only for a different IDENTITY (a different account = a different cookie jar) or hard isolation.
|
|
28
|
+
- It runs **side-by-side with the daily Chrome** — a different `--user-data-dir` is a fully separate instance.
|
|
29
|
+
- **Quit by profile match, never by app name**: `pkill -f "chrome-profiles/agent"`. (`osascript 'tell app "Google Chrome" to quit'` hits the daily browser too — same app name.)
|
|
30
|
+
|
|
31
|
+
## Driving it
|
|
32
|
+
|
|
33
|
+
| Client | Good for | Port handoff |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `chrome-devtools` MCP | rich interaction — click, fill, type, screenshots, network requests, console messages, performance traces | `CHROME_CDP_PORT` env var, **expanded ONCE when the Claude session spawns its MCP — set it BEFORE launching `claude`** (mid-session changes do nothing) |
|
|
36
|
+
| Any CDP client — including EM's `npx mgr cdp` run from any EM project | quick JS eval, per-renderer screenshots | per invocation: `EM_CDP_PORT=9223 npx mgr cdp eval ":4000" 'document.title'` |
|
|
37
|
+
|
|
38
|
+
Port conventions: **9222** = Electron apps (EM), **9223+** = Chrome instances.
|
|
39
|
+
|
|
40
|
+
## UJM specifics
|
|
41
|
+
|
|
42
|
+
<<<<<<< HEAD
|
|
43
|
+
- **The dev server URL is NEVER `localhost`.** BrowserSync serves on the machine's local network IP over HTTPS (e.g. `https://192.168.86.69:4000`) — `localhost:4000` is refused. The port also varies (4001, …) when multiple sites run simultaneously. The exact served URL is written to `.temp/_config_browsersync.yml` at the root of the WEBSITE project being served (the UJM consumer — e.g. `<brand>-website/.temp/_config_browsersync.yml`) — read that file FIRST, every time, before navigating.
|
|
44
|
+
=======
|
|
45
|
+
- **The dev server is HTTPS.** BrowserSync serves over HTTPS (self-signed cert). Prefer `https://localhost:4000`; fall back to the machine's local network IP (e.g. `https://192.168.x.x:4000`) if localhost doesn't connect. Port 4000 by default, increments to 4001+ when multiple sites run. The exact URL is in `.temp/_config_browsersync.yml` at the root of the WEBSITE project (the UJM consumer — e.g. `<brand>-website/.temp/_config_browsersync.yml`) — read that file FIRST, every time, before navigating.
|
|
46
|
+
>>>>>>> df950b1 (v1.9.0: MCP OAuth flow on /token page, CDP debugging docs, dev-URL updates)
|
|
47
|
+
- Launch it pointed at the dev server — UJM's live reload keeps the tab current as you edit, so the agent sees every change without relaunching anything.
|
|
48
|
+
- Typical loop: edit → reload happens → MCP `take_screenshot` / `list_console_messages` / `list_network_requests` to verify the page, requests to the backend, and console cleanliness.
|
|
@@ -387,6 +387,14 @@ Errors display with Bootstrap's `is-invalid` class and `.invalid-feedback` eleme
|
|
|
387
387
|
|
|
388
388
|
When the form transitions to `ready` state, FormManager automatically focuses the field with the `autofocus` attribute (if present and not disabled).
|
|
389
389
|
|
|
390
|
+
**Permanently-disabled fields (`data-fm-keep-disabled`):**
|
|
391
|
+
|
|
392
|
+
FormManager blanket-toggles `disabled` on every control in the form while loading/submitting and re-enables them on `ready`/error. Fields that must STAY disabled (e.g. "coming soon" options rendered inside a managed form) opt out with the `data-fm-keep-disabled` attribute — the toggle always forces them to `disabled = true` and never re-enables them:
|
|
393
|
+
|
|
394
|
+
```html
|
|
395
|
+
<input type="radio" name="plan" value="enterprise" disabled data-fm-keep-disabled>
|
|
396
|
+
```
|
|
397
|
+
|
|
390
398
|
**Methods:**
|
|
391
399
|
|
|
392
400
|
| Method | Description |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Local Development
|
|
2
2
|
|
|
3
|
-
The local
|
|
3
|
+
The exact local dev server URL is written to `.temp/_config_browsersync.yml` in the consuming project's root directory — always read it, never guess. BrowserSync serves over HTTPS (self-signed cert). Prefer `https://localhost:4000`; fall back to the machine's local network IP (e.g. `https://192.168.x.x:4000`) if localhost doesn't connect. Port 4000 by default, increments to 4001+ when multiple sites run simultaneously.
|
|
4
4
|
|
|
5
5
|
## Log Files
|
|
6
6
|
|
package/docs/themes.md
CHANGED
|
@@ -711,8 +711,9 @@ UJM cannot run a dev server itself (it runs inside a consumer). To verify a them
|
|
|
711
711
|
|
|
712
712
|
1. In UJM: `npm run prepare` (or `npm start` for watch) to publish `src/`→`dist/`.
|
|
713
713
|
2. In a consumer wired to local UJM (`"ultimate-jekyll-manager": "file:../ultimate-jekyll-manager"`),
|
|
714
|
-
set `theme.id` and run `npm start`.
|
|
715
|
-
`https://localhost:4000
|
|
714
|
+
set `theme.id` and run `npm start`. Read the BrowserSync URL from the
|
|
715
|
+
consumer's `.temp/_config_browsersync.yml`. Prefer `https://localhost:4000`;
|
|
716
|
+
fall back to the local network IP (e.g. `https://192.168.x.x:4000`) if localhost doesn't connect.
|
|
716
717
|
3. Screenshot the key pages (home, pricing, signin, signup) in **both** light and
|
|
717
718
|
dark (`document.documentElement.setAttribute('data-bs-theme','dark')`) — e.g.
|
|
718
719
|
via the chrome-devtools MCP. Check the console for errors and that your theme's
|
package/logs/test.log
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
# ujm log — 2026-06-
|
|
2
|
-
[
|
|
3
|
-
[
|
|
1
|
+
# ujm log — 2026-06-17T22:01:06.463Z — pid=85523
|
|
2
|
+
[15:01:06] 'test': Running tests (layer=all)
|
|
3
|
+
[15:01:06] 'test': Test mode: normal (external APIs skipped)
|
|
4
4
|
|
|
5
5
|
Ultimate Jekyll Manager Tests
|
|
6
6
|
|
|
7
7
|
Framework Tests
|
|
8
8
|
⤷ attach-log-file — tee stdout/stderr to a file
|
|
9
9
|
✓ exports the expected surface (0ms)
|
|
10
|
-
✓ stripAnsi removes color escape codes (
|
|
10
|
+
✓ stripAnsi removes color escape codes (1ms)
|
|
11
11
|
hello world
|
|
12
12
|
colored line
|
|
13
|
-
✓ attach + stdout.write + detach: file contains the writes (
|
|
13
|
+
✓ attach + stdout.write + detach: file contains the writes (0ms)
|
|
14
14
|
✓ idempotent: attaching twice with same name returns same fd (0ms)
|
|
15
15
|
✓ attach with falsy name returns null and does nothing (0ms)
|
|
16
16
|
⤷ CLI alias resolution
|
|
17
|
-
✓ cli.js exports a Main class (
|
|
17
|
+
✓ cli.js exports a Main class (1ms)
|
|
18
18
|
✓ all expected commands exist on disk (0ms)
|
|
19
|
-
✓ each command module exports an async function (
|
|
19
|
+
✓ each command module exports an async function (8ms)
|
|
20
20
|
⤷ collectTextNodes (utils/collectTextNodes.js)
|
|
21
|
-
✓ extracts page title (
|
|
21
|
+
✓ extracts page title (134ms)
|
|
22
22
|
✓ skips <script> and <style> (1ms)
|
|
23
|
-
✓ spellcheck dictionary (utils/dictionary.js) (
|
|
23
|
+
✓ spellcheck dictionary (utils/dictionary.js) (0ms)
|
|
24
24
|
⤷ expect() matcher set
|
|
25
25
|
✓ toBe + toEqual basics (0ms)
|
|
26
26
|
✓ .not negates (0ms)
|
|
27
27
|
✓ toContain works on arrays and strings (0ms)
|
|
28
|
-
✓ toThrow catches sync + async throws (
|
|
28
|
+
✓ toThrow catches sync + async throws (1ms)
|
|
29
29
|
✓ toBeGreaterThan / toBeLessThan (0ms)
|
|
30
30
|
✓ failing assertions throw AssertionError (0ms)
|
|
31
31
|
✓ package.json exports resolve to real files in dist/ (0ms)
|
|
@@ -42,70 +42,70 @@ colored line
|
|
|
42
42
|
✓ isServer reflects UJ_IS_SERVER env (0ms)
|
|
43
43
|
✓ getEnvironment maps to development/testing/production (0ms)
|
|
44
44
|
✓ actLikeProduction is true when isBuildMode OR UJ_AUDIT_FORCE (0ms)
|
|
45
|
-
✓ getRootPath("package") points at UJM root (
|
|
45
|
+
✓ getRootPath("package") points at UJM root (0ms)
|
|
46
46
|
✓ getMemoryUsage returns shape with MB-sized numbers (0ms)
|
|
47
|
-
✓ getArguments returns object with _ array + boolean defaults (
|
|
47
|
+
✓ getArguments returns object with _ array + boolean defaults (1ms)
|
|
48
48
|
✓ logger returns object with log/error/warn/info methods (0ms)
|
|
49
49
|
✓ processBatches processes items in chunks and returns flat results (0ms)
|
|
50
50
|
⤷ mergeJekyllConfigs (utils/merge-jekyll-configs.js)
|
|
51
|
-
✓ merges collections from both configs (project additions win) (
|
|
52
|
-
✓ dedups defaults by scope key (project wins) (
|
|
53
|
-
✓ returns null when there is nothing to merge (
|
|
51
|
+
✓ merges collections from both configs (project additions win) (3ms)
|
|
52
|
+
✓ dedups defaults by scope key (project wins) (1ms)
|
|
53
|
+
✓ returns null when there is nothing to merge (0ms)
|
|
54
54
|
⤷ mode-helpers (isTesting / isDevelopment / isProduction / getVersion)
|
|
55
55
|
✓ helpers attach to Manager statically AND on prototype (0ms)
|
|
56
|
-
✓ isTesting reflects UJ_TEST_MODE env (
|
|
56
|
+
✓ isTesting reflects UJ_TEST_MODE env (1ms)
|
|
57
57
|
✓ isDevelopment false / isProduction true when UJ_BUILD_MODE=true (and not testing) (0ms)
|
|
58
58
|
✓ environments are mutually exclusive — testing wins under UJ_TEST_MODE (0ms)
|
|
59
|
-
✓ invariant: is*() exactly matches getEnvironment() + mutually exclusive (every scenario) (
|
|
59
|
+
✓ invariant: is*() exactly matches getEnvironment() + mutually exclusive (every scenario) (0ms)
|
|
60
60
|
✓ getVersion returns a non-empty string when run from a package (0ms)
|
|
61
61
|
⤷ createTemplateTransform (utils/template-transform.js)
|
|
62
|
-
✓ replaces [site.theme.id] with config value in .html files (
|
|
62
|
+
✓ replaces [site.theme.id] with config value in .html files (1ms)
|
|
63
63
|
✓ leaves non-matching extensions untouched (e.g. .css) (0ms)
|
|
64
64
|
✓ passes directories through untouched (0ms)
|
|
65
65
|
⤷ node-powertools templating brackets ({} and [])
|
|
66
66
|
✓ default { } brackets resolve nested keys (0ms)
|
|
67
|
-
✓ [ ] brackets resolve nested keys when explicitly configured (
|
|
67
|
+
✓ [ ] brackets resolve nested keys when explicitly configured (0ms)
|
|
68
68
|
✓ [ ] brackets leave Jekyll {{ }} placeholders alone (0ms)
|
|
69
69
|
⤷ theme contract (structure, swappability, cross-theme JS contracts)
|
|
70
70
|
✓ _template: entry files + config contract (0ms)
|
|
71
71
|
✓ classy: entry files + config contract (0ms)
|
|
72
|
-
✓ neobrutalism: entry files + config contract (
|
|
73
|
-
✓ newsflash: entry files + config contract (
|
|
72
|
+
✓ neobrutalism: entry files + config contract (1ms)
|
|
73
|
+
✓ newsflash: entry files + config contract (0ms)
|
|
74
74
|
✓ _template: layouts swappable, markup clean (1ms)
|
|
75
|
-
✓ classy: layouts swappable, markup clean (
|
|
76
|
-
✓ neobrutalism: layouts swappable, markup clean (
|
|
75
|
+
✓ classy: layouts swappable, markup clean (14ms)
|
|
76
|
+
✓ neobrutalism: layouts swappable, markup clean (1ms)
|
|
77
77
|
✓ newsflash: layouts swappable, markup clean (3ms)
|
|
78
78
|
✓ _template: cross-theme JS contracts (0ms)
|
|
79
|
-
✓ classy: cross-theme JS contracts (
|
|
79
|
+
✓ classy: cross-theme JS contracts (0ms)
|
|
80
80
|
✓ neobrutalism: cross-theme JS contracts (1ms)
|
|
81
81
|
✓ newsflash: cross-theme JS contracts (0ms)
|
|
82
|
-
✓ page asset files match a declared asset_path shape (
|
|
82
|
+
✓ page asset files match a declared asset_path shape (4ms)
|
|
83
83
|
⤷ validateYAMLFrontMatter (utils/_validate-yaml.js)
|
|
84
|
-
✓ returns { valid: true } for a file with valid frontmatter (
|
|
84
|
+
✓ returns { valid: true } for a file with valid frontmatter (1ms)
|
|
85
85
|
✓ returns { valid: true } when no frontmatter present (0ms)
|
|
86
|
-
✓ flags malformed YAML frontmatter as invalid with error message (
|
|
86
|
+
✓ flags malformed YAML frontmatter as invalid with error message (0ms)
|
|
87
87
|
⤷ page-layer baseline (DOM + fetch + storage)
|
|
88
88
|
✓ document is interactive or complete (0ms)
|
|
89
|
-
✓ fetch() works against the local harness server (
|
|
90
|
-
✓ localStorage is available (
|
|
89
|
+
✓ fetch() works against the local harness server (2ms)
|
|
90
|
+
✓ localStorage is available (0ms)
|
|
91
91
|
⤷ harness globals (window.Configuration + dataset)
|
|
92
92
|
✓ window.Configuration has brand + theme + web_manager (0ms)
|
|
93
93
|
✓ document.documentElement.dataset.pagePath is set (0ms)
|
|
94
94
|
✓ UJ_TEST_MODE is signalled on globalThis (0ms)
|
|
95
95
|
⤷ prerendered icons template lookup
|
|
96
|
-
✓ template#prerendered-icons exists and has the test icon (
|
|
96
|
+
✓ template#prerendered-icons exists and has the test icon (0ms)
|
|
97
97
|
✓ looking up a missing icon returns null (0ms)
|
|
98
98
|
⤷ boot tests (consumer _site/)
|
|
99
|
-
✓ /service-worker.js served with javascript content type (
|
|
100
|
-
✓ index.html registers SW and reaches activated state (
|
|
101
|
-
✓ SW responds to get-cache-name message with brand-id pattern (
|
|
102
|
-
✓ home page renders with title + body content (
|
|
103
|
-
✓ /about resolves via Jekyll-style .html fallback (
|
|
104
|
-
✓ build.json is served with brand metadata (
|
|
105
|
-
✓ CSS bundle served with text/css content type (
|
|
99
|
+
✓ /service-worker.js served with javascript content type (94ms)
|
|
100
|
+
✓ index.html registers SW and reaches activated state (146ms)
|
|
101
|
+
✓ SW responds to get-cache-name message with brand-id pattern (76ms)
|
|
102
|
+
✓ home page renders with title + body content (171ms)
|
|
103
|
+
✓ /about resolves via Jekyll-style .html fallback (75ms)
|
|
104
|
+
✓ build.json is served with brand metadata (67ms)
|
|
105
|
+
✓ CSS bundle served with text/css content type (70ms)
|
|
106
106
|
|
|
107
107
|
Results
|
|
108
108
|
80 passing
|
|
109
109
|
|
|
110
|
-
Total: 80 tests in
|
|
110
|
+
Total: 80 tests in 6044ms
|
|
111
111
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-jekyll-manager",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Ultimate Jekyll dependency manager",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -47,14 +47,15 @@
|
|
|
47
47
|
"url": "https://github.com/itw-creative-works/ultimate-jekyll.git"
|
|
48
48
|
},
|
|
49
49
|
"keywords": [
|
|
50
|
-
"
|
|
51
|
-
"
|
|
50
|
+
"jekyll",
|
|
51
|
+
"static-site",
|
|
52
|
+
"static-site-generator",
|
|
53
|
+
"website",
|
|
54
|
+
"seo",
|
|
52
55
|
"gulp",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"Sass",
|
|
57
|
-
"Webpack"
|
|
56
|
+
"sass",
|
|
57
|
+
"webpack",
|
|
58
|
+
"postcss"
|
|
58
59
|
],
|
|
59
60
|
"author": "ITW Creative Works",
|
|
60
61
|
"license": "MIT",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"sessionId":"76fefdf0-10f3-4795-b5eb-56e35f724e5e","pid":53082,"procStart":"Wed Jun 10 11:57:56 2026","acquiredAt":1781141001890}
|