mcpField
H
hub-creator

hub-creator

github.com/datexDevelopment93 installs
Repository

About hub-creator

hub-creator is a skill for authoring new Datex Studio hub configurations on a branch. It guides the creation and validation of hubs with filters, tabs, mounted components, toolbar actions, and flows, including checks for reference contracts.

Key features

  1. 01Create new hub configurations
  2. 02Set up filters, tabs, and mounted components
  3. 03Configure toolbar buttons and flows
  4. 04Check component reference contracts
  5. 05Validate and push hub configurations

Use cases

  1. 01Build a filtered hub with grid tabs
  2. 02Add toolbar actions that open forms
  3. 03Create a hub with access-gated behavior
  4. 04Validate component bindings before pushing

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/datex/skills --skill hub-creator

After installing, type/hub-creatorin Claude Code to confirm it loaded.

Bundled filesevals/evals.jsonreferences/hubs.md

Details

From the project README

Hub Creator

Author a NEW Datex Studio hub configuration (configurationTypeId=2) on a branch — top-level UI containers with filters, grouping options, tabs that mount grids/editors/forms/custom Angular components, toolbar buttons, and click flows.

See also: hub-editor — modifying an EXISTING hub's toolbar/flows on a branch (this skill is for authoring NEW hubs).

References

Dependencies

  • requirements-gathering skill — invoked to produce a requirements brief if one doesn't already exist in the conversation context
  • grid-creator / selector-creator / form-creator / editor-creator skills — invoked when a tab grid, filter-backing selector, or dialog form/editor referenced by the hub doesn't exist yet on the branch
  • component-wiring-check skill — invoked to audit configParameters ↔ target inParams contracts before push (the dead-wiring trap)

CLI Lifecycle

Hub authoring goes through dxs configuration — the generic CRUD primitive over every platform configuration type. There is no dxs hub subcommand and no field-level patching; you build (or fetch + extract) the whole JSON body, edit it, and push the whole thing back.

Create a new hub:

# 1. Build body.json from scratch (see references/hubs.md → Minimal Valid Skeleton)
# 2. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate hub -b <branchId> -D body.json
# 3. Create
dxs configuration upsert hub -b <branchId> -D body.json

Edit an existing hub (only if scope creeps from "new" into "modify mid-authoring"):

# 1. Fetch — note the envelope wrapper
dxs configuration get hub <configId> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate hub -b <branchId> -D body.json
# 5. Push
dxs configuration upsert hub -b <branchId> -D body.json

For purely-modify workflows (toolbar/flows changes on an existing hub), stop here and switch to the hub-editor skill — it carries the toolbar/click-flow editing patterns and the role-gating recipes.

Round-trip rule (critical)

When editing an existing config, never pipe the envelope.json directly into dxs configuration upsert — it silently destroys configuration content. The corrected sequence above (extract inner .json with jq before editing) is mandatory for any round-trip. See ../datex-studio-shared/configuration-roundtrip.md for the canonical round-trip and the underlying bug.

Workflow

[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
        |
[requirements brief in context?]
  +-----+-----+
  |            |
 YES          NO -> invoke `requirements-gathering`
  |            |
  +-----+------+
        |
[Phase 2: Author hub body]
Decide what the hub IS:
  - which filters scope the experience
  - which tabs (grids / other components) mount inside it
  - role-gating, on_init access guard, hub-local flows
Consult references/hubs.md for file shape, $hub runtime,
required top-level fields, common patterns
        |
[Phase 3: Wire toolbar / flows]
Toolbar buttons + click flows; dialog openers via $shell
Audit dead-wiring trap (hub filter -> tab configParameters
-> grid inParams MUST stay in sync) — invoke
`component-wiring-check` if mounted grids/selectors
need extension
        |
[Phase 4: Validate + push]
dxs configuration validate hub -b <branchId> -D body.json
        |
   +----+----+
   |         |
  CREATE   MODIFY-EXISTING
   |         |
   |         use the corrected round-trip
   |         (get -O envelope -> jq .json -> body)
   |         |
   +----+----+
        |
        v
dxs configuration upsert hub -b <branchId> -D body.json
   (upsert creates or updates by referenceName — one command for both)
        |
[Phase 5: Verify in Studio (optional)]
Reload the hub in the running app; confirm filters render,
tabs query, toolbar buttons fire
        |
[invoke `post-edit-verification`; then `component-validator`]

Phase Details

Phase 1: Setup + Requirements
  1. Follow ../datex-studio-shared/branch-setup.md for branch and connection selection. Never assume a branch ID — ask the user to confirm.
  2. Check whether a requirements brief already exists in the conversation context (produced by requirements-gathering or another calling skill).
    • Brief exists — use it. The brief provides intent (what the hub is for), the scoping filters users need, the tabs/grids that should mount inside, role-gating rules, and any toolbar actions.
    • No brief — invoke the requirements-gathering skill first. Hubs are top-level entry points; getting the filter set and tab layout right up front saves rework.
Phase 2: Author hub body

Decide what the hub IS, then build body.json:

  1. File basics. Per the Pre-Flight Checklist below + ../datex-studio-conventions/universal-checklist.md; see references/hubs.md → File Location & Naming for the -hub.json file shape.
  2. Filters. Each filter field's configId / moduleId must resolve to a real selector (*-selector.json). If the selector doesn't exist on the branch yet, invoke selector-creator first. Every selector configParameter gets an entry in the filter's dropdownConfig.configParameters (use value: "" for ones that don't apply).
  3. Tabs. Every tab declares a contentConfig pointing at a grid (or other component) on the branch. If the target grid doesn't exist yet, invoke grid-creator first. The tab's configParameters array must mirror the target's inParams exactly — see the Phase 3 dead-wiring check.
  4. vars array. Every $hub.vars.<id> written in hub flow code must be declared at the top-level vars array. See references/hubs.md → Runtime Globals and ../component-wiring-check/references/component-wiring.md.
  5. onInitFlowConfig. Common home for initial-filter defaulting, context loading, and the access-gated hub pattern (permission check → $hub.close() on deny). See references/hubs.md → Common Patterns.
  6. TypeScript-expression encoding. hubTitle, hubDescription, any value / tooltip / placeholder / label follows the three-encoding rule — display text wrapped in backticks: "`Widget overview`". See ../datex-studio-conventions/file-format.md → Declarative String Values Are TypeScript Expressions.
Phase 3: Wire toolbar / flows
  1. Toolbar buttons + click flows. Each toolbar button entry has a clickFlowConfig pointing at a hub-local flow or an external function. From hub code, invoke functions via $flows.<Package>.<fn> and open forms/editors via $shell.<Package>.open<referenceName>Dialog(...). Never call actions directly — wrap them in a function. See ../datex-studio-runtime/calling-conventions.md and references/hubs.md → Common Patterns for the engine-toggle, configure-options-dialog, access-gated on_init, and shared-local-flow patterns.

  2. Dead-wiring trap audit. The single most common hub bug: a hub filter binds into a tab's configParameters for an id the mounted grid doesn't actually declare as an inParam. The binding looks wired but the grid never receives the value.

    The rule: every binding in a tab's configParameters must be named in the mounted grid's inParams — and every inParam the grid declares must have an entry in the tab's configParameters (unused entries use value: "" or value: null).

    When a hub filter exists to scope a specific tab's grid (e.g. a projects filter scoping a rules grid by project_ids, or a contract filter scoping a documents grid by contract_id), audit the mounted grid's top-level inParams in the same edit. If a binding is needed and the grid doesn't declare a matching inParam, extend the grid in the same edit (and the underlying datasource, since grid flows read $grid.inParams.<id> when building the datasource call).

    Invoke component-wiring-check to audit the full reference contract — moduleId, configParameters mirror, vars declared — before push. See references/hubs.md → Invocation Contract and ../component-wiring-check/references/component-wiring.md.

  3. Toolbar / tab IDs are unique within the hub. Flow code addresses them by id ($hub.toolbar.save.control..., $hub.tabs.rules.hidden = ...) — collisions silently overwrite.

Phase 4: Validate + push
# Validate the body locally against the branch. Exit 1 = validation found errors
# (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate hub -b <branchId> -D body.json

# For a new hub
dxs configuration upsert hub -b <branchId> -D body.json

# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get hub <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert hub -b <branchId> -D body.json

Validation surfaces missing required fields, malformed configParameters shapes, and selector/grid reference errors before push. It does not catch the dead-wiring trap (configParameters whose id is absent from the mounted grid's inParams) — that's a contract concern between two components, audited via component-wiring-check and the Phase 3 rule above.

Phase 5: Verify in Studio (optional)

Reload the hub in the running app:

  • Filters render and the dropdowns populate (selectors wired correctly).
  • Tabs query when filters change (the tab configParameters ↔ grid inParams contract holds).
  • Toolbar buttons fire their click flows; dialog openers return; $hub.refresh() propagates state.
  • Role-gated tabs hide for unauthorized users (access-gated on_init flow runs).

If the running app isn't available, re-fetch the config and diff against body.json (using the corrected jq .json extract pattern) to confirm the push landed.

Pre-Flight Checklist

Before push, walk the full checklist in references/hubs.md → Pre-Flight Checklist. The fast version:

  1. File basics: configurationTypeId: 2, referenceName ends _hub — plus the universal checks (../datex-studio-conventions/universal-checklist.md).
  2. Tabs carry full configParameters contracts (mirrors the mounted grid's inParams exactly — no extras, no missing entries).
  3. Filter bindings are contract-complete (selector references resolve; every selector configParameter has an entry).
  4. Vars declared (every $hub.vars.<id> in flow code is in top-level vars).
  5. Calling-tier compliance (functions via $flows.<Package>.<fn>; dialog openers via $shell.<Package>.open<referenceName>Dialog(...); no direct action calls).
  6. Toolbar/tab IDs are unique within the hub.
  7. TypeScript-expression strings wrapped correctly; dynamic tooltips go through declared $hub.vars.<name>.

Common Mistakes

MistakeFix
Tab configParameters binds an id the grid doesn't declare as an inParamDead wiring — the value is silently dropped. Either drop the binding, or extend the grid's inParams (and underlying datasource) in the same edit. See Phase 3.
Piping dxs configuration get -O envelope.json directly into dxs configuration upsert -D envelope.jsonSilently destroys config content. Always jq .json envelope.json > body.json before editing. See "Round-trip rule" above.
Writing $hub.vars.<id> = ... in flow code without declaring <id> in the top-level vars arrayThe var is undeclared — write fails silently or runtime error. Declare every var.
Calling an action directly from hub code ($flows.<Package>.<action_name>(...))UI-tier rule: invoke functions only; wrap actions in a function. See ../datex-studio-runtime/calling-conventions.md.
Assigning to a filter or toolbar button's .control.tooltip from flow codeNo-op for dynamic tooltips — must bind to a declared $hub.vars.<name> instead. See references/hubs.md → Pre-Flight Checklist item 7.
description exceeds 100 charsSQL column limit — push will fail validation. Tighten.
referenceName doesn't end in _hub or doesn't match filename stemImport / lookup breaks. Snake_case, _hub suffix, filename stem matches.
Tab moduleId set to the hub's package instead of the mounted grid'sCross-component reference rule — moduleId is always the target's package. See ../component-wiring-check/references/component-wiring.md.
Two toolbar buttons (or two tabs) sharing the same idSilent overwrite — flow code can only address one of them. Make IDs unique within the hub.

After your edit, invoke post-edit-verification to surface description/JSON/schema violations. For a final review, invoke component-validator.

Comments

Markdown supported