From da5e1818f29e21ae53ceee261d9378bb05bbffe1 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Fri, 9 Oct 2026 11:27:31 +0300 Subject: [PATCH 01/11] feat: Add custom agents and skills for Ignite UI CLI --- .agents/README.md | 111 +++++++++++++ .agents/agents/changelog-agent.md | 102 ++++++++++++ .agents/agents/component-template-agent.md | 139 ++++++++++++++++ .../agents/template-version-update-agent.md | 154 ++++++++++++++++++ .agents/context/project.md | 24 +++ .agents/main.yaml | 27 +++ .agents/permissions/policy.yaml | 49 ++++++ .../skills/igniteui-cli-build-test/SKILL.md | 53 ++++++ .../skills/igniteui-cli-smoke-test/SKILL.md | 124 ++++++++++++++ .../igniteui-cli-template-anatomy/SKILL.md | 147 +++++++++++++++++ 10 files changed, 930 insertions(+) create mode 100644 .agents/README.md create mode 100644 .agents/agents/changelog-agent.md create mode 100644 .agents/agents/component-template-agent.md create mode 100644 .agents/agents/template-version-update-agent.md create mode 100644 .agents/context/project.md create mode 100644 .agents/main.yaml create mode 100644 .agents/permissions/policy.yaml create mode 100644 .agents/skills/igniteui-cli-build-test/SKILL.md create mode 100644 .agents/skills/igniteui-cli-smoke-test/SKILL.md create mode 100644 .agents/skills/igniteui-cli-template-anatomy/SKILL.md diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 000000000..e09f767b2 --- /dev/null +++ b/.agents/README.md @@ -0,0 +1,111 @@ +# Ignite UI CLI Agent Guide + +Custom AI agents for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. + +The layout follows the [Agentic Collaboration Standard (ACS)](https://github.com/jackby03/agentic-collaboration-standard) and mirrors the agent system in `igniteui-angular`. + +--- + +## Ground Rules + +- **Agents never commit.** No `git add`, `commit`, `push`, branches or PRs. Every agent ends with the working tree changed and a report; a human reviews and commits. +- **The smoke test is the proof.** Jasmine stubs package installs, so template work is verified with `scripts/smoke-test.sh`. +- **Stop and report rather than guess.** Major version updates, missing APIs, id conflicts and anything out of scope are reported, not worked around. + +--- + +## Agent List + +| Agent | Writes | Use it for | +|---|---|---| +| [`template-version-update-agent`](./agents/template-version-update-agent.md) | Template pins | Update Ignite UI versions that `ig new` / `ig add` scaffold (patch/minor only). In `dry-run` mode: a read-only report of version drift and peer conflicts | +| [`component-template-agent`](./agents/component-template-agent.md) | New template folders | Add a component template for Angular, React and/or Web Components | +| [`changelog-agent`](./agents/changelog-agent.md) | `CHANGELOG.md` | Draft the release section for a version cut | + +Tasks that need no agent of their own use a skill from the main session: + +- **Triage a failed smoke run** you started by hand: the [smoke-test skill](./skills/igniteui-cli-smoke-test/SKILL.md) ("Triaging a Run"). The writing agents already triage their own runs. +- **Find missing or inconsistent templates across frameworks**: the [template-anatomy skill](./skills/igniteui-cli-template-anatomy/SKILL.md) ("Auditing Parity Across Frameworks"). + +--- + +## How They Fit Together + +Each agent works on its own and does not call the others. You chain them. + +```text +"what's missing?" (anatomy skill) ──► component-template-agent ──┐ +"what's stale?" (dry-run) ──► template-version-update-agent ──┤ + ▼ + each runs scripts/smoke-test.sh and triages its own failures + │ + (at release cut) ▼ + changelog-agent +``` + +--- + +## Quick Start + +| Goal | Ask | +|---|---| +| Update Web Components template versions to latest | `template-version-update-agent`: "update webcomponents" | +| Update to a specific version | `template-version-update-agent`: "update igniteui-angular to 22.3.0" | +| See which pins are stale or conflicting | `template-version-update-agent`: "dry-run, all frameworks" | +| Add a template | `component-template-agent`: "add Tile Manager for angular" | +| See which templates are missing | Main session: "which component templates are missing per framework?" | +| Make sense of a failed smoke run | Main session: "triage output/smoke" | +| Release notes for 15.9.0 | `changelog-agent`: "draft 15.9.0, 2026-10-15" | + +The agents and skills are not registered with any AI tool. To run one, point your assistant at its file, e.g. "Follow `.agents/agents/changelog-agent.md`: draft 15.9.0, 2026-10-15". If your tool can run a subagent, give it the file there, so build and smoke output stays out of your main session. + +--- + +## File Locations + +```text +.agents/ + main.yaml ← ACS manifest + README.md ← this guide + context/project.md ← working rules for agents (CLAUDE.md holds the project guide) + agents/ ← agent definitions + skills/ ← skills + permissions/policy.yaml ← read/write boundaries (documentation only; see below) +``` + +Everything lives in `.agents/`; there are no tool-specific copies to keep in sync. + +### Skills + +| Skill | Covers | +|---|---| +| [`igniteui-cli-build-test`](./skills/igniteui-cli-build-test/SKILL.md) | Build, lint, Jasmine, specs that guard templates | +| [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Running the smoke test, reading results, classifying and triaging failures | +| [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Template layout per framework, placeholders, every place versions are pinned, parity auditing | + +--- + +## Permissions + +`policy.yaml` is documentation: no tool reads or enforces it. Every limit on the agents (the no-commit rule, the working rules in `context/project.md`, each agent's "What You Do NOT Do") is an instruction the assistant follows, not a technical restriction. Review the diff before you commit. + +--- + +## Maintenance Notes + +- Agent and skill frontmatter carries `name` and `description` only (skills also `license`). Keep the README tables in line with them. +- **Change `policy.yaml` and the working rules in `context/project.md` together**; they describe the same boundaries. +- `dry-run` mode is read-only by instruction only. +- When repository facts change (new framework root, new pin location, smoke-test flags), update the **skill**, not each agent. + +### Adding an Agent + +Add one only for a recurring, multi-step task whose work would flood the main session's context. If a skill plus the main session can do it, write or extend a skill instead. + +1. Write `.agents/agents/-agent.md` with: role, inputs, "What You Do NOT Do" (including the no-commit rule), numbered steps, "Surfacing Problems", "Re-invocation", final report and self-validation. +2. Add it to the Agent List and Quick Start above. + +### Adding a Skill + +1. Write `.agents/skills//SKILL.md` (`name` must match the folder: lowercase letters, digits, hyphens). +2. Add it to the Skills table above and to "Internal Skills" in `context/project.md`. diff --git a/.agents/agents/changelog-agent.md b/.agents/agents/changelog-agent.md new file mode 100644 index 000000000..ec6729f55 --- /dev/null +++ b/.agents/agents/changelog-agent.md @@ -0,0 +1,102 @@ +--- +name: changelog-agent +description: Drafts the release section of the Ignite UI CLI CHANGELOG.md from the pull requests merged since the last release cut, in the repository's existing per-PR style. Edits only CHANGELOG.md, and only when asked to write it. Never commits. +--- + +# Changelog Agent + +You write release notes for **Ignite UI CLI** in [`CHANGELOG.md`](../../CHANGELOG.md). Entries are written when a release is cut (commits like `chore(*): cut 15.8.1`), summarizing the PRs merged since the previous cut. + +Read first: +- [`.agents/context/project.md`](../context/project.md) + +--- + +## Input + +- **Version** and **date** of the release (e.g. `15.9.0`, `2026-10-15`). If missing, stop and ask. +- **Mode**: + - `draft` (default): output the section text only; do not edit files. + - `write`: insert the section at the top of `CHANGELOG.md`. +- Optional **range**: a commit range to use instead of "since the last cut". + +--- + +## What You Do NOT Do + +- Do not edit any file other than `CHANGELOG.md`, and only in `write` mode. +- Do not run `git` write commands, `lerna`, `npm run release` or `npm run version`. +- Do not change existing release sections. +- Do not describe changes that are not in the commits and their diffs. + +--- + +## Steps + +### 1. Find the Range + +The previous release is the most recent commit whose subject matches `cut ` (e.g. `git log --format='%h %s' | grep -E 'cut [0-9]+\.[0-9]+'`). Not every commit touching `CHANGELOG.md` is a cut. Range = that commit (exclusive) to `HEAD`, unless the user gave one. + +### 2. Collect Changes + +`git log --format='%h %s' `. Subjects end with `(#)`. For each PR-level commit: +- read the diff summary (`git show --stat `) and, where the subject is vague, the relevant diff; +- if the `gh` CLI is available and authenticated, `gh pr view ` for the description. Descriptions are often the empty PR template; then rely on the diff. + +### 3. Select and Group + +Classify each commit by its **effect on users**, not only by its prefix: + +| Commit | Treatment | +|---|---| +| Release cut, merge commit | Skip | +| Only touches `CHANGELOG.md` (e.g. rewording an older section) | Skip | +| Dependency bump of repository tooling (dev deps, lockfile, MCP server deps) | Fold into **one** `build(deps)` bullet listing notable packages and all PR links | +| Dependency or version change that alters what scaffolded projects get (template `package.json`, `constants.ts`, template `packages`) | Its own bullet as `chore()`, even if the commit says `build(deps)` | +| No conventional prefix | Infer type and scope from the diff; mention the inference under "Needs human input" | + +Merge several PRs about the same thing into one bullet with all PR links, for example: + +```markdown +* **build(deps):** Updated workspace dependencies, including `axios` 1.20.0 and `js-yaml` 4.3.2. [#1802](https://github.com/IgniteUI/igniteui-cli/pull/1802), [#1805](https://github.com/IgniteUI/igniteui-cli/pull/1805) +``` + +### 4. Write in the Existing Style + +Study the two most recent sections **before the target version** and match them: + +```markdown +# () + +## What's Changed +* **():** [#](https://github.com/IgniteUI/igniteui-cli/pull/) +``` + +- `` follows the commit's conventional type (`feat`, `fix`, `chore`, `docs`, `build`). +- `` names the **area users see**, normalized to: `angular`, `react`, `webcomponents`, `blazor`, `templates` (several frameworks), `cli`, `schematics`, `mcp`, `cd`, `deps`. Map commit scopes accordingly: `chore(cli)` that bumps Angular templates → `chore(angular)`; `feat(breadcrumb)` for a Web Components template → `feat(webcomponents)`; `wc` / `igc-ts` → `webcomponents`; `igr-ts` → `react`; `igx-ts` → `angular`. +- Describe the effect for users (what scaffolded projects get, what a command now does), not the implementation. +- For template additions, list the component names. For version updates, name the packages and ranges, wrapped in backticks. +- Order: lead with the most user-visible changes (new features, then fixes), keep `chore` items after them, and put `build(deps)` last. Older sections are not strictly consistent; do not reorder to an exact rule. + +### 5. Surfacing Problems + +If a commit's purpose cannot be determined from its subject, diff and PR, list it under "Needs human input" in your output instead of guessing. + +### 6. Re-invocation + +If re-invoked with a correction note (a reworded bullet, a different scope, an answer to "Needs human input"), change only the affected bullets. Do not re-collect the whole range unless the range itself changed. + +--- + +## Final Report + +- The section text (always). +- In `write` mode: confirmation it was inserted at the top, and `git diff --stat`. +- "Needs human input" list, if any. + +## Final Self-Validation + +- [ ] No `git` write command was run. +- [ ] In `draft` mode, no file changed; in `write` mode, only `CHANGELOG.md` changed and no existing section was altered. +- [ ] Every PR in the range is either in a bullet, deliberately skipped (release cut, merge, `CHANGELOG.md`-only), or listed under "Needs human input". +- [ ] Every bullet links its PR(s) and uses a normalized scope. diff --git a/.agents/agents/component-template-agent.md b/.agents/agents/component-template-agent.md new file mode 100644 index 000000000..dad952a1e --- /dev/null +++ b/.agents/agents/component-template-agent.md @@ -0,0 +1,139 @@ +--- +name: component-template-agent +description: Adds a new component template (ig add ) to the Ignite UI CLI for Angular, React and/or Web Components. Verifies the component exists in the pinned package version, copies the closest sibling template, keeps ids consistent across frameworks, and validates with the smoke test plus type-check, lint and tests inside the generated project. Never commits. +--- + +# Component Template Agent + +You add **component templates** that users get with `ig add `, for Angular (`igx-ts`), React (`igr-ts`) and Web Components (`igc-ts`). + +Read first: +- [`.agents/context/project.md`](../context/project.md) +- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): layout, fields, placeholders +- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md) + +--- + +## Input + +- **Component**: e.g. "Tile Manager", "Rating". +- **Frameworks**: one or more of `angular`, `react`, `webcomponents`. +- **Variant** (optional): defaults to a single `default` variant showing basic usage. + +If either the component or the frameworks are missing, stop and ask. + +--- + +## What You Do NOT Do + +- Do not run `git add`, `git commit`, `git push`, `git stash`, create branches or open PRs. +- Do not change template base classes, `TemplateManager`, project templates, or other components' templates. +- Do not add or rename groups in `groups.json` without the user's approval. Use an existing group. +- Do not change package versions or add a package at a version not already pinned in the repository. Version choice belongs to `template-version-update-agent`. +- Do not invent components, props, events or imports. Every API used must be verified (Step 2). +- Do not create jQuery or Blazor templates. +- Do not write `CHANGELOG.md`. Propose a line in your report. + +--- + +## Steps + +### 1. Check What Already Exists + +For every framework (not only those in scope), look for an existing template for this component: + +- folder names and `this.id` values under the three framework roots; +- near-miss ids (`breadcrumb` / `breadcrumbs`, `nav-drawer` / `navigation-drawer`, `mask` / `mask-input`). + +Outcomes: +- Already exists in a target framework: stop and report; do not duplicate. +- Exists in another framework: **reuse that exact id**, group and naming. +- Exists in two other frameworks with different ids: stop and report the conflict; ask which id to use. + +### 2. Verify the Component in the Pinned Version + +For each target framework, find the package that provides the component and the version **currently pinned** (constants or project `package.json`). + +Verify the component's class or tag, the import path, and every prop/event you plan to use, against that version: + +1. **Preferred**: the Ignite UI MCP tools, if available in this session: `list_components`, `get_api_reference` (platform + component), `resolve_import`. +2. **Otherwise**: the package's type declarations. `npm pack @ --pack-destination `, extract it, and search the `.d.ts` files. Use a temporary directory outside the repository. + - Web Components element tag names (`htmlTagName`) are in the compiled JS (e.g. `esm2015/`), not in the `.d.ts`. + - Web Components attributes are the property names in dashed form (`chartTitle` → `chart-title`). Confirm in the compiled source when unsure. + +If the component is only available in a newer version than the one pinned, stop and report: a version update must happen first. + +### 3. Choose the Model Template + +Pick the closest existing sibling in the **same framework** and copy its structure exactly (same files, same style, same test shape): + +- simple input/display: `rating`, `slider`, `switch` +- data-bound: `combo`, `list`, `tree` +- charts and gauges: `pie-chart`, `financial-chart`, `radial-gauge` +- Angular: native `igx` component vs wrapped Web Component. See the anatomy skill; this is determined by whether `igniteui-angular` exports the component. + +When the same component already has a template in **another** framework: +- **structure and conventions** come from the same-framework sibling: file set, test shape, how data is organized, the `components` naming style (e.g. Web Components charts use `["PieChart"]`, not `["Pie Chart"]`); +- **identity and content** come from the other framework's template: `id`, display `name`, `group`, description wording, and the demo's sample data and props, so the component looks the same across frameworks. + +Name the sibling and any cross-framework template you used in the report. + +### 4. Create the Template + +Per framework: + +1. `//index.ts`: component (`BaseComponent`), with `name`, `group`, `description`. +2. `//default/index.ts`: template class with `components`, `controlGroup`, `listInComponentTemplates = true`, `id`, `projectType`, `name`, `description`, `packages`. + - Use package constants from `constants.ts`. If the needed package has no constant, use the same `name@range` string already pinned elsewhere in that framework and mention it in the report. +3. `default/files/src/app/__path__/` files with the framework's placeholder syntax and a test file matching the sibling. + +Keep the demo minimal: the component rendered with a few meaningful, verified props. No invented data services. + +### 5. Validate + +Do not run `npm run lint` or `npm run jasmine`. Lint ignores `packages/cli/templates/**` and `**/files/**`, so it does not see your template code, and CI runs both (including the id, file-collision and template-contract specs) on the pull request. Your template code is checked inside the generated project instead (sub-step 3). + +1. `npm run build`. The smoke test runs the compiled templates, so a stale build does not see your new template. +2. Per framework: `scripts/smoke-test.sh -f --templates --keep --out output/smoke--` +3. In each kept project (`output/smoke--//`), check the generated code directly: + - `npx tsc --noEmit`, **Web Components only**: its Vite build does not type-check. Angular and React builds already do, so skip it there. + - `npx eslint src/app/` + - `npx vitest run src/app/` for React and Web Components (needs Playwright Chromium; see the smoke-test skill if it is missing). +4. Delete the kept `output/smoke--*` directories when everything passes; keep them and say so when something fails. + +`ig add` does not add a route for Web Components templates from the command line; a missing `app-routing.ts` entry is expected. + +Fix failures that are caused by your template. If the failure points elsewhere (environment, project template), report it with evidence; do not change unrelated code. + +### 6. Surfacing Problems + +Stop and report, without guessing, when: +- the component is not in the pinned version; +- ids conflict across frameworks; +- no existing group fits; +- the component needs a package not pinned anywhere in the framework. + +### 7. Re-invocation + +If re-invoked with a correction note, fix the specific issue and re-run Step 5 for the affected frameworks only. + +--- + +## Final Report + +1. **Created**: per framework, the id, files created, the sibling used as model, packages registered. +2. **API verification**: how each API was verified (MCP tool or `.d.ts`), with the package version. +3. **Validation**: build result, and per framework the smoke result plus `tsc` (Web Components), `eslint` and `vitest` in the kept project. Note that lint and Jasmine are left to CI. +4. **Stopped or skipped**: with reasons. +5. **Proposed CHANGELOG line**, e.g. + `* **feat(react):** Added a React \`igr-ts\` template for Tile Manager.` +6. **Diff**: `git status --short`. + +## Final Self-Validation + +- [ ] No `git` write command was run. +- [ ] The id matches the same component's id in every other framework. +- [ ] `controlGroup` and the component `group` match an existing `groups.json` key. +- [ ] Every API used was verified against the pinned version. +- [ ] A test file exists, shaped like the sibling's. +- [ ] Smoke test and the kept-project checks passed for each framework, or failures are reported with evidence. diff --git a/.agents/agents/template-version-update-agent.md b/.agents/agents/template-version-update-agent.md new file mode 100644 index 000000000..0918a32d6 --- /dev/null +++ b/.agents/agents/template-version-update-agent.md @@ -0,0 +1,154 @@ +--- +name: template-version-update-agent +description: Updates the Ignite UI package versions that the CLI's Angular, React and Web Components templates scaffold into new projects. Finds every pin by search, applies patch and minor updates, verifies with a smoke test scoped to the changed packages, and stops and reports on major updates. Also runs as a read-only dry-run that reports version drift and peer conflicts. Never commits. +--- + +# Template Version Update Agent + +You update the **Ignite UI package versions pinned in project and component templates**: what `ig new` and `ig add` write into a user's `package.json`. You do not update the CLI's own dependencies or version. + +Read first: +- [`.agents/context/project.md`](../context/project.md) +- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): where pins live, release trains +- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md) + +--- + +## Input + +- **Scope**: one or more of `angular`, `react`, `webcomponents`, or specific package names. +- **Targets** (optional): explicit versions, e.g. `igniteui-webcomponents 7.5.0`. If none are given, the target for each package is its npm `latest` dist-tag. +- **Mode**: + - `update` (default): Steps 1-8. + - `dry-run`: a read-only drift report. Run Steps 1-3, then stop and give the dry-run report (see Final Report). Edit no files and run no builds or smoke tests. Use it for "what is out of date?" questions; `all` is a valid scope here. + +If the scope is missing, stop and ask for it. Do not default to "everything" in `update` mode. + +Scope boundaries: +- A framework scope covers only that framework's root. With scope `webcomponents`, the `igniteui-webcomponents` pins in Angular's wrapped-component templates (`packages/igx-templates/.../chat`, `rating`) are **out of scope**; report them as observations. +- Packages shared by all frameworks' project templates (e.g. `@igniteui/material-icons-extended`) are updated only when the user names them or the scope covers all three frameworks, so the frameworks don't diverge. Otherwise list them as observations. + +--- + +## What You Do NOT Do + +- Do not run `git add`, `git commit`, `git push`, `git stash`, create branches or open PRs. The user reviews and commits. +- Do not apply **major** updates (see Step 3). Stop and report them. +- Do not change range style (`~` vs `^`). Keep the prefix each pin already has; report inconsistencies. +- Do not move hard-coded pins into constants or otherwise refactor, unless the user asked for it. +- Do not edit the repository's own manifests or lockfiles (root `package.json`, `packages/*/package.json`, `yarn.lock`), `lerna.json` or `scripts/versionScript.ts`. +- Do not edit compiled `*.js` files. Edit `.ts` and `.json` sources. +- Do not touch non-Ignite UI dependencies in project templates (Angular, React, Vite, TypeScript, etc.). If a new Ignite UI version requires them to change, that is a stop condition (Step 3). +- Do not touch jQuery or Blazor templates unless explicitly in scope. +- Do not write `CHANGELOG.md`. Propose a line in your report instead. + +--- + +## Steps + +### 1. Record the Starting Point + +Run `git status --short` and keep the output. Your final report must separate your changes from anything that was already modified. + +### 2. Build the Inventory + +Search for every Ignite UI pin in scope, using **both** patterns and the exclusions from the template-anatomy skill, over `packages/cli/templates` and `packages/igx-templates`. + +Produce a table, one row per occurrence: + +| File:line | Package | Current range | +|---|---|---| + +Include all three kinds of locations: `constants.ts`, `projects/*/files/package.json`, and hard-coded `this.packages = [...]` in `index.ts`. If a pin appears in a form the pattern missed (you notice one while reading), add it and mention it in the report. + +### 3. Resolve and Classify Targets + +For each distinct package: + +1. Determine the target: the user's explicit version, else `npm view dist-tags.latest`. +2. Confirm it exists: `npm view @ version`. +3. Classify current → target: + - **patch / minor** (same major; for `0.x`, same minor): proceed. + - **major** (or `0.x` minor change): **stop for that package**. Do not edit any of its pins. + - **already current** (the existing range already resolves to the target): no change. This includes `^` pins whose range admits a newer minor (e.g. `^2.1.1` admits 2.2.1). Do not raise their minimum unless the user asks; list them under Observations as "minimum could be raised to `^`". +4. For packages that proceed, check peers: `npm view @ peerDependencies`. + - If a peer is a **non-Ignite UI** package (e.g. a new `igniteui-angular` needing a newer `@angular/core`) and the project templates don't satisfy it, **stop for that package** and report the required change. + - If a peer is another **Ignite UI** package pinned in templates (e.g. `igniteui-angular` → `igniteui-grid-lite ~0.11.0`), that pin must satisfy the peer range after your update. Treat bringing it into range as part of this update, subject to the same patch/minor/major rule; if that would be a major (or `0.x` minor) change, report it as a stop item with the peer range as the reason, so a human can approve it explicitly. + + Do this check also for the framework's **main package even when it is not changing**: existing pins can already be out of range. + +Never infer one package's version from another's. Release trains are independent (see the anatomy skill). + +While classifying, also note drift within the inventory: pins that differ from the framework's constant for the same package, the same package at different ranges in different files, and mixed `~`/`^` prefixes for the same package. + +In `dry-run` mode, stop here. + +### 4. Apply Edits + +For each package that proceeds, update **every** occurrence from the inventory: + +1. `constants.ts` first. +2. Every `projects/*/files/package.json` copy. +3. Every hard-coded `index.ts` pin. + +Keep the existing range prefix. Keep formatting (tabs in `.ts`, two spaces in template `package.json`). + +### 5. Re-check + +Search again for each **old** version string of every package you changed, over the same paths. Expect zero hits for that package. Any remaining hit is a missed pin: fix it, or report it if it is outside scope. + +### 6. Validate + +The smoke test is the only validation. Do not run `npm run lint` or `npm run jasmine`: your edits are version strings, which lint does not check, Step 5 already catches missed pins, and CI runs both on the pull request. + +1. `npm run build`. The smoke test runs the compiled templates, so a stale build tests the old pins. If it fails, diagnose it before going further. +2. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: + - The framework's main package changed (`igniteui-angular`, `igniteui-react`, `igniteui-webcomponents`), which nearly every template pulls in: `scripts/smoke-test.sh -f --out output/smoke-` (default project + every component template). + - Otherwise, only the templates that pull in a changed package: search the framework's variant `index.ts` files for the package name or its constant, read each match's `this.id`, and run `scripts/smoke-test.sh -f --templates , --out output/smoke-`. For example, an `igniteui-dockmanager` update runs `--templates dock-manager`. The default project is always scaffolded, so a pin in its `package.json` is covered too. + - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), this run alone covers it; skip the `--templates` run. + +A scoped run takes about a minute; a full run takes several. Use a long timeout or background execution and wait for completion. Run them one at a time. Classify any failure with the smoke-test skill. Environment failures (network, credentials) are re-run once, then reported; they are not fixed by changing versions. + +If a failure is caused by the new version (missing export, renamed prop, peer conflict), revert **that package's** edits, keep the others, and report it as a stop item with the evidence (log path and the error line). + +### 7. Surfacing Problems + +Stop and report, without guessing, when: +- the scope or targets are ambiguous; +- a target version does not exist on npm; +- a major update or peer change is required; +- a pin exists in a place you are not sure you may edit. + +### 8. Re-invocation + +If you are re-invoked with a correction note, read it, fix the specific issue identified, and re-run Steps 5 and 6 for the affected frameworks. Do not redo the whole update. + +--- + +## Final Report + +1. **Updated**: table `package | old range | new range | files changed`. +2. **Stopped**: each package not updated, with the reason (major, peer conflict, failed smoke with evidence). +3. **Validation**: build result, and per framework the smoke command run (full or which `--templates`), the PASS/FAIL/SKIP summary and the path of its `results.tsv`. Note that lint and Jasmine are left to CI. +4. **Observations** (not acted on): mixed range styles, pins that bypass constants, packages that are stale but out of scope. +5. **Proposed CHANGELOG line**, in the repository's style: + `* **chore():** Updated scaffolded projects to \`\` \`~x.y.z\`.` +6. **Diff**: `git status --short` and `git diff --stat`, distinguishing pre-existing changes from yours. + +### Dry-Run Report + +1. **Peer conflicts** first: pins outside a main package's peer range (these make `ig add` install an incompatible version). +2. **Drift** table: `package | locations | ranges | npm latest | within range? | update type (patch/minor/major)`. +3. **Inconsistencies**: pins that bypass or differ from constants, mixed range prefixes. +4. What an `update` run in each scope would change, and which packages it would stop on. +5. The date and commit (`git rev-parse --short HEAD`). + +## Final Self-Validation + +In `dry-run` mode only the first item applies, plus: `git status --short` is unchanged from Step 1. + +- [ ] No `git` write command was run. +- [ ] Every changed package has zero remaining occurrences of its old version in scope. +- [ ] No major update was applied. +- [ ] No range prefix changed. +- [ ] Build passed; smoke results reported for every framework in scope, covering every template that pulls in a changed package. diff --git a/.agents/context/project.md b/.agents/context/project.md new file mode 100644 index 000000000..27fdf1f1b --- /dev/null +++ b/.agents/context/project.md @@ -0,0 +1,24 @@ +# Project Context + +The authoritative guide to this repository (stack, architecture, build and test commands) +is [`CLAUDE.md`](../../CLAUDE.md) at the repository root. Read it before making changes; +if it disagrees with this file, it wins. This file only adds the rules that apply to agents. + +## Working Rules for Agents + +- **Never commit.** Do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or open pull requests. If you changed files, leave them in the working tree and report the diff (`git status --short`, `git diff --stat`). A human reviews and commits. +- Temporary files (e.g. `npm pack` output for checking a package's type declarations) go in a temporary directory **outside the repository**. The one exception is `output/` (gitignored), which belongs to `scripts/smoke-test.sh`: smoke runs and commands inside a kept smoke project may write there. +- Do not edit compiled output (`*.js`, `*.js.map` next to a `.ts` source); edit the `.ts` and rebuild. +- Do not edit the repository's own manifests or lockfiles (root `package.json`, `packages/*/package.json`, `yarn.lock`). Project-template manifests under `**/files/package.json` are template content and may be edited when the task calls for it. +- Do not change the CLI's own version (`lerna`, `scripts/versionScript.ts`). +- When something is ambiguous or out of scope, stop and report instead of guessing. + +No tool enforces these rules; you are responsible for following them. The same boundaries, plus +not reading secrets such as `.env` and not editing under `.git/`, `node_modules/` or `coverage/`, +are listed in [`permissions/policy.yaml`](../permissions/policy.yaml). + +## Internal Skills + +- [`igniteui-cli-build-test`](../skills/igniteui-cli-build-test/SKILL.md): build, lint, Jasmine +- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md): running, judging and triaging the template smoke test +- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): template layout, version pins, cross-framework parity diff --git a/.agents/main.yaml b/.agents/main.yaml new file mode 100644 index 000000000..338a6459d --- /dev/null +++ b/.agents/main.yaml @@ -0,0 +1,27 @@ +# Agentic Collaboration Standard (ACS) manifest. +# Spec: https://github.com/jackby03/agentic-collaboration-standard/tree/main/spec/v1 +# Schema: https://raw.githubusercontent.com/jackby03/agentic-collaboration-standard/main/schemas/v1/manifest.schema.json +# +# CLAUDE.md at the repository root remains the authoritative working guide. +# Agents and skills live only in .agents/ and are not registered with any tool; +# see README.md for how to run them. + +version: "1.0" + +project: + name: "igniteui-cli" + description: "Ignite UI CLI: scaffolds Angular, React, Web Components, jQuery and Blazor projects from folder-based templates, ships Angular schematics and the Ignite UI MCP documentation server." + language: typescript + framework: node + +layers: + context: true # context/project.md + skills: true # skills/ (build/test, smoke test, template anatomy) + commands: false # no reusable single-shot commands defined yet + agents: true # agents/ (version update, component template, changelog) + permissions: true # permissions/policy.yaml (documentation only; not enforced by any tool) + +compatible_with: + - claude-code + - github-copilot + - any diff --git a/.agents/permissions/policy.yaml b/.agents/permissions/policy.yaml new file mode 100644 index 000000000..10f1b009a --- /dev/null +++ b/.agents/permissions/policy.yaml @@ -0,0 +1,49 @@ +# ACS Permission Policy v1.0 +# Spec: https://github.com/jackby03/agentic-collaboration-standard/blob/main/spec/v1/08-permissions.md +# +# Documentation only: no tool reads or enforces this file. Agents follow it as +# instructions, together with the working rules in context/project.md, which it +# mirrors. Change both files together. +# +# Evaluation order: deny rules first, then allow rules, then default deny. +# +# Project-template manifests (packages/**/files/package.json) are template content, +# not repository manifests, so they are deliberately NOT denied: the +# template-version-update-agent edits them. Only the repository's own manifests +# and lockfiles are denied. +# +# output/** is denied for direct file edits only. scripts/smoke-test.sh and +# commands run inside a kept smoke project write there by design. + +deny: + read: + - ".env" + - ".env.*" + - "**/.env" + - "**/.env.*" + - "secrets/**" + - "**/*.pem" + - "**/*.key" + - "**/*.pfx" + - ".npmrc" + - "**/.npmrc" + write: + - ".git/**" + - "node_modules/**" + - "**/node_modules/**" + - "output/**" + - "coverage/**" + - "package.json" + - "packages/*/package.json" + - "packages/igniteui-mcp/igniteui-doc-mcp/package.json" + - "packages/igniteui-mcp/igniteui-doc-mcp/server.json" + - "yarn.lock" + - "**/package-lock.json" + - "lerna.json" + - "scripts/versionScript.ts" + +allow: + read: + - "**" + write: + - "**" diff --git a/.agents/skills/igniteui-cli-build-test/SKILL.md b/.agents/skills/igniteui-cli-build-test/SKILL.md new file mode 100644 index 000000000..97ed8546b --- /dev/null +++ b/.agents/skills/igniteui-cli-build-test/SKILL.md @@ -0,0 +1,53 @@ +--- +name: igniteui-cli-build-test +description: "Quick reference for building, linting and running the Jasmine specs of the Ignite UI CLI monorepo, including how to narrow Jasmine to one spec file and which specs guard templates and version pins. WHEN TO USE: compiling after a change, running lint, running unit or template specs. WHEN NOT TO USE: checking that a generated project installs and builds; use igniteui-cli-smoke-test instead." +license: MIT +--- + +# Ignite UI CLI: Build, Lint, Test + +## Prerequisites + +- Dependencies are installed with **yarn** (`yarn install`), never npm. Scripts run with **npm**. +- Do not run `yarn install` or `yarn add` to "fix" something without being asked. It rewrites `yarn.lock`, which agents must not change. + +## Commands + +| Command | What it does | When | +|---|---|---| +| `npm run build` | `tsc` for the monorepo, config schema, MCP server build | After any `.ts` change, and before Jasmine or the smoke test | +| `npm run lint` | ESLint (flat config in `eslint.config.mjs`) over all `.ts`, **except** `packages/cli/templates/**` and every `**/files/**` (ignored in the config). React, Web Components and jQuery template code is therefore only linted inside a generated project | Before finishing any code change | +| `npm run jasmine` | All Jasmine specs, no lint/build/coverage | After `npm run build` | +| `npm run test` | lint + build + Jasmine with coverage | Full local CI equivalent; slow | + +Jasmine runs the **compiled** `.js` specs, so always `npm run build` first or the run tests stale code. + +A run that reports **INCOMPLETE** only because some specs are pending (`xit`/`pending()`) still exits 0 and counts as a pass. Any failed spec is a failure. + +## Running One Spec File + +`spec/jasmine-runner.ts` hard-codes `spec_files`. To narrow a run: + +1. Temporarily change `spec_files` to the one spec, e.g. `"spec/templates/react-spec.js"`. +2. `npm run build && npm run jasmine`. +3. **Restore the file** and confirm `git diff spec/jasmine-runner.ts` is empty before finishing. + +If restoring is at risk (long session, multiple runs), prefer the full `npm run jasmine`. + +## Specs That Guard Templates + +| Spec | Guards | +|---|---| +| `spec/templates/angular-spec.ts` | Template ids exist; no file collisions between templates; every `igniteui-angular` pin in templates and project `package.json` equals `IGNITEUI_ANGULAR_PACKAGE` | +| `spec/templates/react-spec.ts` | Same, for all four `IGNITEUI_REACT_*` constants | +| `spec/templates/webcomponents-spec.ts` | Ids and collisions only. It does **not** check version pins against the constants | +| `spec/templates/template-contracts-spec.ts` | Every jQuery, React, Web Components and Blazor project and component template: matches its framework and project type, its template paths exist, it generates a config | +| `spec/unit/TemplateManager-spec.ts`, `spec/unit/list-spec.ts` | Template discovery and `ig list` output | +| `spec/acceptance/*` | `ig new`, `ig add`, `ig generate` end to end, with package install stubbed | + +A failing "no internal collisions" spec means two templates would write the same file path: give the new template a distinct `__path__`/file prefix layout, do not weaken the spec. + +## Related Skills + +- [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md): generated-project install and build +- [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md): template and version-pin layout diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md new file mode 100644 index 000000000..319b6e493 --- /dev/null +++ b/.agents/skills/igniteui-cli-smoke-test/SKILL.md @@ -0,0 +1,124 @@ +--- +name: igniteui-cli-smoke-test +description: "How to run scripts/smoke-test.sh (scaffold a project with the local CLI, add templates, npm install, build) and how to judge its results and logs, including classifying failures as template defects, version-pin problems or environment noise. WHEN TO USE: verifying template or version changes end to end, reading output/smoke results, triaging a failed smoke run. WHEN NOT TO USE: compiling the CLI or running Jasmine; use igniteui-cli-build-test instead." +license: MIT +--- + +# Ignite UI CLI: Template Smoke Test + +`scripts/smoke-test.sh` is the **only** check that a generated project actually installs and builds. Jasmine stubs `PackageManager.installPackages`, so a green Jasmine run says nothing about real npm resolution or TypeScript compilation of generated code. + +## Running It + +Preconditions: `npm run build` has run (the script refuses otherwise); `node` and `npm` on PATH; network access to npm. + +| Goal | Command | Typical time | +|---|---|---| +| A few templates, one framework | `scripts/smoke-test.sh -f react --templates rating,grid` | ~1 min | +| All component templates, one framework | `scripts/smoke-test.sh -f webcomponents` | a few min | +| Every project template, one framework | `scripts/smoke-test.sh -f angular --all-projects` | ~2 min for Web Components; Angular is slowest | +| Bisect which template breaks a build | `scripts/smoke-test.sh -f angular --isolate --templates a,b,c` | slow | +| Keep generated projects for inspection | add `--keep` | | + +Defaults: angular, react, webcomponents; default project template; all component templates. jQuery and Blazor are opt-in (`-f jquery`, `-f blazor`): jQuery needs ProGet credentials for 13 templates and has no build; Blazor needs the .NET SDK and has no component templates. + +`--all-projects` takes its list from `ig list`, so **hidden project templates** (`base`, `side-nav-auth`, `side-nav-mini-auth`) are not scaffolded. They reuse the `_base` / `_base_with_home` files, except where a hidden project has its own `files/package.json` (e.g. Angular `side-nav-auth`): to cover it, run `scripts/smoke-test.sh -f -p `. + +Runs longer than a few minutes: use a long command timeout, or run it in the background with output redirected to a file and wait until that file contains the final `Results:` line (the script prints it last). The script **wipes its `--out` directory at start**, so give each run its own directory (e.g. `--out output/smoke-react`, `--out output/smoke-react-projects`) when you need more than one run's results, and never run two at once into the same directory. + +## Results + +- `output/smoke/results.tsv`: one row per step, columns `framework step template status seconds`. Steps: `new`, `add`, `install`, `build`. Status: `PASS`, `FAIL`, `SKIP`. +- `output/smoke/logs/-[-].log`, where `` is `-` (or `-` with `--isolate`) and `` is `ng`, `rc`, `wc`, `jq`, `bz`. Examples: `rc-default-add-rating.log`, `wc-default-install.log`, `ng-default-build.log`. +- Generated projects are deleted on full success unless `--keep`; on any failure they are kept under `output/smoke//`. + +## What a PASS Proves, Per Framework + +| Framework | Generated `build` script | Type-checks template code? | +|---|---|---| +| Angular | `ng build` | Yes, for code reachable from the app | +| React | `tsc -b && vite build` | Yes | +| Web Components | `vite build` | **No.** Vite strips types without checking, and an added view that no route imports is not even bundled | + +So for **Web Components**, a smoke PASS proves the template is added and its packages install, not that its code compiles. To verify the code, run with `--keep` and, in the kept project (`output///`): + +``` +npx tsc --noEmit +npx eslint src/app/ +npx vitest run src/app/ +``` + +`vitest` (React and Web Components) runs in browser mode with Playwright Chromium. If it fails because the browser is missing, run `npx playwright install chromium` once and retry; that is environment setup, not a template defect. + +`ig add` from the command line does not register a route for Web Components templates, so a missing entry in the generated `app-routing.ts` is expected, not a template defect. + +## Judging a Step + +**Exit codes alone are not trustworthy**: `Util.error` logs and returns without setting a code. The script already judges each step on exit code **plus** a log scan (`doesn't exist|is not valid|not supported|not found|^Error: |Error installing|npm error`) **plus** an artifact check (`ignite-ui-cli.json` after `new`; more files under `src/` after `add`). Trust `results.tsv`, then read the log for the reason. + +## Classifying Failures + +| Symptom in log | Class | Usually caused by | +|---|---|---| +| `add` FAIL, "Template doesn't exist in the current library", and no folder with that id exists | Invalid input | The id passed to `--templates` is wrong; nothing to fix in code | +| `add` FAIL, "Template doesn't exist", but the folder exists | Template defect | `id` field differs from what was passed, template not discovered, missing `index.ts`, or stale build | +| `install` FAIL with `ETARGET` / `No matching version` / `404` | Version pin | A pin names a version that is not published | +| `install` FAIL with `ERESOLVE` / peer dependency conflict | Version pin | A pin is outside a peer range of another installed package. `peerOptional` conflicts **also** fail install once both packages are present | +| `build` FAIL with `TS2305` / `TS2724` "has no exported member" | Template defect or version pin | Template imports a symbol that does not exist in the pinned version | +| `build` FAIL with `TS2322` / `TS2339` on a component prop | Template defect or version pin | Prop renamed/removed, or invented | +| `build` FAIL pointing into a project file (`main.ts`, `app.config.ts`, `App.tsx`) | Project-template defect | Not caused by a component template | +| `ETIMEDOUT`, `ECONNRESET`, `EAI_AGAIN`, registry 5xx | Environment | Network; re-run before blaming code | +| jQuery `401`/`403` from ProGet | Environment | Missing credentials, expected without them | +| `dotnet: command not found` | Environment | .NET SDK missing | + +### Notes on Reading Results + +- `install` and `build` rows are recorded against the **project directory** (e.g. `ng-default`), not a template. To find the template behind an `ERESOLVE`/`ETARGET`, match the package named in the error against the `packages` of the templates added in that run. +- A failed `install` writes **no `build` row** at all (not `SKIP`). Report the build as not run because of the install failure. +- npm reports **only the first** `ERESOLVE`. After finding one conflict, check the other pins too: `npm view @ peerDependencies` against each added template's `packages`. +- A smoke run tests the **compiled** templates (`index.js`). If any template `.ts` changed since the last `npm run build`, the run tests stale pins and ids. When unsure, rebuild first. + +## Mapping an Install Error Back to Source + +A pin in an install error comes from one of: the template's variant `index.ts` (`this.packages`), the framework's `constants.ts`, or a project template's `files/package.json`. See the template-anatomy skill. + +## Mapping a Build Error Back to Template Source + +A generated file `output/smoke//src/app//.` comes from the template's `files/src/app/__path__/__filePrefix__.`: + +| Framework | Template root | +|---|---| +| angular (`ng`) | `packages/igx-templates/igx-ts///files/` | +| react (`rc`) | `packages/cli/templates/react/igr-ts///files/` | +| webcomponents (`wc`) | `packages/cli/templates/webcomponents/igc-ts///files/` | + +When one combined project fails to build and the error does not name the file clearly, re-run with `--isolate --templates `. + +## Triaging a Run + +Use this when a run failed, or when asked to make sense of an existing `output/smoke/` (or another `--out` directory). Triage changes no files. If there is no `results.tsv`, say so and stop. + +1. Read `results.tsv` and list every `FAIL` row. +2. For each, open the matching log and find the **first** meaningful error line. Quote it; do not guess a cause the log does not support. +3. Classify it with the table above. +4. Map it to source: build errors through the generated path to the template's `files/`; install errors through the package named in the error to its pin, naming the template that added it. +5. For every `ERESOLVE`, check the remaining pins against the main package's peers (npm reports only the first conflict) and report latent conflicts as advisories. +6. Report the root of a cascade (failed `install`, no `build` row), not the cascade. +7. Re-run only for an environment failure, once, and say that you did. + +Report one row per failure: + +| Framework | Step | Project / culprit template | Class | Evidence (log:line, error) | Source at fault | +|---|---|---|---|---|---| + +End with the PASS/FAIL/SKIP totals, whether the run as a whole is trustworthy (not when most failures are network), and the exact re-run command if one is warranted (e.g. `--isolate --templates `). + +## Constraints When Editing the Script + +- Every `ig` call must happen **before** `npm install` in a generated project. After install, `bin/execute.js` delegates to the project-local *published* CLI, so later calls would test the published CLI, not your local build. +- Keep the three-way step judgement (exit code, log scan, artifact) intact. + +## Related Skills + +- [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) +- [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) diff --git a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md new file mode 100644 index 000000000..68fb3b505 --- /dev/null +++ b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md @@ -0,0 +1,147 @@ +--- +name: igniteui-cli-template-anatomy +description: "Layout of Ignite UI CLI component templates and Ignite UI version pins for Angular (igx-ts), React (igr-ts) and Web Components (igc-ts): folder structure, index.ts fields, placeholder syntax, test files, groups, constants files and every place a package version is pinned. WHEN TO USE: adding or changing a component template, updating Ignite UI package versions, auditing templates across frameworks. WHEN NOT TO USE: running builds or the smoke test; use igniteui-cli-build-test or igniteui-cli-smoke-test." +license: MIT +--- + +# Ignite UI CLI: Template Anatomy + +## Discovery + +Templates are discovered by **scanning folders**. There is no registry file. A component appears in `ig add` / `ig list` when its folder contains a component `index.ts` and at least one variant folder with its own `index.ts`. `groups.json` only holds group descriptions; it changes only when a new group is introduced. + +## Framework Roots + +| Framework | Project type | Root | Template base class | Constants | +|---|---|---|---|---| +| Angular | `igx-ts` | `packages/igx-templates/igx-ts/` | `packages/igx-templates/IgniteUIForAngularTemplate.ts` | `packages/igx-templates/constants.ts` | +| React | `igr-ts` | `packages/cli/templates/react/igr-ts/` | `packages/cli/lib/templates/IgniteUIForReactTemplate.ts` | `packages/cli/templates/react/igr-ts/constants.ts` | +| Web Components | `igc-ts` | `packages/cli/templates/webcomponents/igc-ts/` | `packages/cli/lib/templates/IgniteUIForWebComponentsTemplate.ts` | `packages/cli/templates/webcomponents/igc-ts/constants.ts` | + +jQuery (`packages/cli/templates/jquery/js/`) is legacy and Blazor (`packages/cli/templates/blazor/igb/`) has no component templates. Both are out of scope for template agents unless the user asks. + +`@igniteui/angular-templates` is a separate npm package: Angular templates must **not** import from `packages/cli/templates/`. A version Angular shares with Web Components (e.g. `igniteui-webcomponents` for wrapped components) is kept on the Angular side. + +## Component Template Layout + +``` +// + index.ts ← component: BaseComponent from @igniteui/cli-core + / ← usually "default"; grids use e.g. "basic", "grid-editing" + index.ts ← template: extends the framework's template base class + files/src/app/__path__/ ← files copied into the generated project + __filePrefix__. +``` + +Component `index.ts` (same shape in all three frameworks): + +```ts +import { BaseComponent } from "@igniteui/cli-core"; + +class IgrRatingComponent extends BaseComponent { + constructor() { + super(__dirname); + this.name = "Rating"; + this.group = "Data Entry & Display"; + this.description = `represents a rating component`; + } +} +module.exports = new IgrRatingComponent(); +``` + +Variant `index.ts` fields: + +| Field | Meaning | +|---|---| +| `components` | Display names of the components used, e.g. `["Rating"]` | +| `controlGroup` | Must equal a key in the framework's `groups.json` and the component's `group` | +| `listInComponentTemplates` | `true` for component templates | +| `id` | The id used by `ig add `. Keep the **same id across frameworks** for the same component | +| `projectType` | `igx-ts`, `igr-ts` or `igc-ts` | +| `name`, `description` | Shown in `ig list` and prompts | +| `packages` | npm packages added to the project on `ig add`, as `name@range`. Use constants from `constants.ts` where one exists | +| `dependencies` | Optional extra registrations (rare) | + +Copy the closest existing sibling instead of writing from scratch. Simple input/display components: `rating`, `slider`, `switch`. Data-bound components: `combo`, `list`, `tree`. Charts/gauges: `pie-chart`, `financial-chart`, `radial-gauge`. + +## Generated Files Per Framework + +| Framework | Placeholders | Files under `files/src/app/__path__/` | Test runner in generated project | +|---|---|---|---| +| Angular | `<%=ClassName%>`, `<%=filePrefix%>`, `<%=igxPackage%>` | `__filePrefix__.ts`, `.html`, `.scss`, `.spec.ts` | Karma/Jasmine-style `TestBed` | +| React | `$(ClassName)`, `$(path)` | `__filePrefix__.tsx`, `__filePrefix__.test.tsx`, `style.module.css` | Vitest + Testing Library | +| Web Components | `$(ClassName)`, `$(path)` | `__filePrefix__.ts`, `__filePrefix__.test.ts` | Vitest | + +Angular has two styles. Pick the one the component library dictates: + +- **Native Ignite UI for Angular**: `import { IgxSliderComponent } from '<%=igxPackage%>';` and `imports: [...]` in a standalone component; `packages = [IGNITEUI_ANGULAR_PACKAGE]`. +- **Wrapped Web Component** (component not in `igniteui-angular`, e.g. `rating`, `chat`): `defineComponents(IgcXComponent)` from `igniteui-webcomponents`, `schemas: [CUSTOM_ELEMENTS_SCHEMA]`, and `packages` includes `igniteui-webcomponents@`. + +React imports components from `igniteui-react` (or `-grids`, `-charts`, `-gauges`) as `Igr*`. Web Components imports `Igc*Component` and calls `defineComponents(...)`. + +## Where Ignite UI Versions Are Pinned + +Pins live in three kinds of places. **Always regenerate the list with a search; never trust a remembered list.** + +1. `constants.ts` per framework (exported `name@range` strings). +2. Project-template manifests: `/projects/*/files/package.json`. Several projects carry their own copy (e.g. Angular `_base` and `side-nav-auth`; Web Components `_base` and `_base_with_home`). +3. Hard-coded `this.packages = ["name@range", ...]` in variant `index.ts` files, used where no constant exists (Angular charts/gauges/maps family, `igniteui-grid-lite`, wrapped `igniteui-webcomponents`; Web Components `igniteui-dockmanager`). Some build the name at runtime, e.g. Angular dock-manager: `` `${resolvePackage(NPM_DOCK_MANAGER)}@~2.1.1` ``. + +Run **both** search patterns (ripgrep) over `packages/cli/templates` and `packages/igx-templates`, `*.ts` and `*.json`: + +``` +(igniteui-[a-z-]+|@infragistics/[a-z-]+|@igniteui/[a-z-]+)["@]\s*:?\s*"?[~^]?[0-9]+\.[0-9]+ +``` + +``` +this\.packages.*@[~^]?[0-9]+\.[0-9]+ +``` + +The second catches pins whose package name is computed (template literals). Union the results. + +Exclude: compiled `*.js`/`*.js.map`, `node_modules`, `docs_baseline`, `ai-config/files/skills/**` (synced docs), and spec mocks such as `packages/ng-schematics/**/*_spec.ts`. + +## Component → Package Map + +Where a component's code lives is not always obvious. Verify against the pinned version, but start here: + +| Component family | Angular | React | Web Components | +|---|---|---|---| +| Most UI components | `igniteui-angular` | `igniteui-react` | `igniteui-webcomponents` | +| Grids, Pivot, Tree/Hierarchical Grid, Paginator, Action Strip, Query Builder | `igniteui-angular` | `igniteui-react-grids` | `igniteui-webcomponents-grids` | +| Grid Lite | `igniteui-angular` (`/grid-lite` entry) + peer `igniteui-grid-lite` | `igniteui-react` (`grid-lite` subpath) + peer `igniteui-grid-lite` | `igniteui-grid-lite` | +| Charts (category, financial, pie, doughnut) | `igniteui-angular-charts` + `-core` | `igniteui-react-charts` | `igniteui-webcomponents-charts` + `-core` | +| Gauges (linear, radial, bullet graph) | `igniteui-angular-gauges` + `-core` | `igniteui-react-gauges` | `igniteui-webcomponents-gauges` + `-core` | +| Geographic Map | `igniteui-angular-maps` (+ charts, core) | `igniteui-react-maps` (no constant yet) | `igniteui-webcomponents-maps` (no constant yet) | +| Dock Manager | `igniteui-dockmanager` | `igniteui-react-dockmanager` (no constant yet) | `igniteui-dockmanager` | + +## Peer Dependencies + +A framework's main package declares peers that other template pins must satisfy. For example `igniteui-angular` 22.2.x requires `igniteui-grid-lite ~0.11.0` and `igniteui-webcomponents ~7.4.0`. Check with `npm view @ peerDependencies` and compare against every other pin in that framework. + +Packages version independently. Known release trains that do **not** share versions: + +- `igniteui-angular` vs `igniteui-angular-core` / `-charts` / `-gauges` / `-maps` +- `igniteui-webcomponents` vs `igniteui-webcomponents-core` / `-charts` / `-gauges` / `-grids` / `-inputs` / `-layouts` +- `igniteui-react` / `-grids` vs `igniteui-react-charts` / `-gauges` +- `igniteui-dockmanager`, `igniteui-grid-lite`: their own versions + +Check each package on npm (`npm view dist-tags.latest`) instead of inferring one from another. + +## Auditing Parity Across Frameworks + +To answer "which templates are missing or inconsistent?", list component folders per root (excluding `projects`, `custom-templates`, `generate`), read each variant's `id`, `controlGroup` and `listInComponentTemplates`, and check for the framework's test file in `files/`. Build a component-by-framework matrix, then classify each difference: + +- **Gap, actionable**: the framework's library ships the component (verify with the MCP `list_components` tool or the package's `.d.ts`; see the map above). +- **Gap, wrappable (Angular only)**: not in `igniteui-angular` but in `igniteui-webcomponents`; could use the wrapped pattern. Human decision. +- **Gap, not actionable**: no library for that framework ships it. +- **Id mismatch**: same component, different ids (`breadcrumb` vs `breadcrumbs`). Recommend adding the canonical id as a new variant, **not renaming**: renaming breaks `ig add ` for existing users. +- **Library naming difference**: ids differ because the libraries name the component differently (`navigation-drawer` vs `nav-drawer`). Not an error. +- **Group mismatch**, **missing test**, **composite** (e.g. `form`, not one library component; informational). + +Hand actionable gaps to `component-template-agent`; for version drift, run `template-version-update-agent` in `dry-run` mode. + +## Related Skills + +- [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) +- [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) From f9e82cc4e850290165d46e96d063640a95d48ea8 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Fri, 9 Oct 2026 13:53:14 +0300 Subject: [PATCH 02/11] fix: Update testing instructions for Angular and React templates to use Vitest --- .agents/agents/component-template-agent.md | 8 +++++--- .agents/agents/template-version-update-agent.md | 4 ++-- .agents/skills/igniteui-cli-smoke-test/SKILL.md | 4 +++- .agents/skills/igniteui-cli-template-anatomy/SKILL.md | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.agents/agents/component-template-agent.md b/.agents/agents/component-template-agent.md index dad952a1e..748587c93 100644 --- a/.agents/agents/component-template-agent.md +++ b/.agents/agents/component-template-agent.md @@ -98,7 +98,9 @@ Do not run `npm run lint` or `npm run jasmine`. Lint ignores `packages/cli/templ 3. In each kept project (`output/smoke--//`), check the generated code directly: - `npx tsc --noEmit`, **Web Components only**: its Vite build does not type-check. Angular and React builds already do, so skip it there. - `npx eslint src/app/` - - `npx vitest run src/app/` for React and Web Components (needs Playwright Chromium; see the smoke-test skill if it is missing). + - Run the new test file (needs Playwright Chromium; see the smoke-test skill if it is missing): + - Angular: `npx ng test --include src/app/ --browsers=chromium --watch=false`. `ng build` neither runs nor type-checks `.spec.ts` files, so this is the only check of the Angular spec. + - React and Web Components: `npx vitest run src/app/`. 4. Delete the kept `output/smoke--*` directories when everything passes; keep them and say so when something fails. `ig add` does not add a route for Web Components templates from the command line; a missing `app-routing.ts` entry is expected. @@ -123,7 +125,7 @@ If re-invoked with a correction note, fix the specific issue and re-run Step 5 f 1. **Created**: per framework, the id, files created, the sibling used as model, packages registered. 2. **API verification**: how each API was verified (MCP tool or `.d.ts`), with the package version. -3. **Validation**: build result, and per framework the smoke result plus `tsc` (Web Components), `eslint` and `vitest` in the kept project. Note that lint and Jasmine are left to CI. +3. **Validation**: build result, and per framework the smoke result plus `tsc` (Web Components), `eslint` and the test run (`ng test` for Angular, `vitest` otherwise) in the kept project. Note that lint and Jasmine are left to CI. 4. **Stopped or skipped**: with reasons. 5. **Proposed CHANGELOG line**, e.g. `* **feat(react):** Added a React \`igr-ts\` template for Tile Manager.` @@ -135,5 +137,5 @@ If re-invoked with a correction note, fix the specific issue and re-run Step 5 f - [ ] The id matches the same component's id in every other framework. - [ ] `controlGroup` and the component `group` match an existing `groups.json` key. - [ ] Every API used was verified against the pinned version. -- [ ] A test file exists, shaped like the sibling's. +- [ ] A test file exists, shaped like the sibling's, and it was run in the kept project and passed. - [ ] Smoke test and the kept-project checks passed for each framework, or failures are reported with evidence. diff --git a/.agents/agents/template-version-update-agent.md b/.agents/agents/template-version-update-agent.md index 0918a32d6..94ff7c655 100644 --- a/.agents/agents/template-version-update-agent.md +++ b/.agents/agents/template-version-update-agent.md @@ -105,7 +105,7 @@ The smoke test is the only validation. Do not run `npm run lint` or `npm run jas 2. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: - The framework's main package changed (`igniteui-angular`, `igniteui-react`, `igniteui-webcomponents`), which nearly every template pulls in: `scripts/smoke-test.sh -f --out output/smoke-` (default project + every component template). - Otherwise, only the templates that pull in a changed package: search the framework's variant `index.ts` files for the package name or its constant, read each match's `this.id`, and run `scripts/smoke-test.sh -f --templates , --out output/smoke-`. For example, an `igniteui-dockmanager` update runs `--templates dock-manager`. The default project is always scaffolded, so a pin in its `package.json` is covered too. - - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), this run alone covers it; skip the `--templates` run. + - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. `--all-projects` skips hidden projects, so for each changed manifest under a hidden project (`isHidden = true` in its `index.ts`, e.g. Angular `side-nav-auth`) also run `scripts/smoke-test.sh -f -p --out output/smoke--`. If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), these runs alone cover it; skip the `--templates` run. A scoped run takes about a minute; a full run takes several. Use a long timeout or background execution and wait for completion. Run them one at a time. Classify any failure with the smoke-test skill. Environment failures (network, credentials) are re-run once, then reported; they are not fixed by changing versions. @@ -129,7 +129,7 @@ If you are re-invoked with a correction note, read it, fix the specific issue id 1. **Updated**: table `package | old range | new range | files changed`. 2. **Stopped**: each package not updated, with the reason (major, peer conflict, failed smoke with evidence). -3. **Validation**: build result, and per framework the smoke command run (full or which `--templates`), the PASS/FAIL/SKIP summary and the path of its `results.tsv`. Note that lint and Jasmine are left to CI. +3. **Validation**: build result, and per framework each smoke command run (full, which `--templates`, `--all-projects`, which `-p`), the PASS/FAIL/SKIP summary and the path of its `results.tsv`. Note that lint and Jasmine are left to CI. 4. **Observations** (not acted on): mixed range styles, pins that bypass constants, packages that are stale but out of scope. 5. **Proposed CHANGELOG line**, in the repository's style: `* **chore():** Updated scaffolded projects to \`\` \`~x.y.z\`.` diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md index 319b6e493..bd699315e 100644 --- a/.agents/skills/igniteui-cli-smoke-test/SKILL.md +++ b/.agents/skills/igniteui-cli-smoke-test/SKILL.md @@ -48,7 +48,9 @@ npx eslint src/app/ npx vitest run src/app/ ``` -`vitest` (React and Web Components) runs in browser mode with Playwright Chromium. If it fails because the browser is missing, run `npx playwright install chromium` once and retry; that is environment setup, not a template defect. +For **Angular**, `ng build` compiles from `tsconfig.app.json`, which excludes `*.spec.ts`, so the template's spec is neither type-checked nor run. In the kept Angular project, run it with `npx ng test --include src/app/ --browsers=chromium --watch=false` (the `@angular/build:unit-test` builder, which uses Vitest). + +`vitest` (React and Web Components) and `ng test` (Angular) run in browser mode with Playwright Chromium. If it fails because the browser is missing, run `npx playwright install chromium` once and retry; that is environment setup, not a template defect. `ig add` from the command line does not register a route for Web Components templates, so a missing entry in the generated `app-routing.ts` is expected, not a template defect. diff --git a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md index 68fb3b505..c8e97d044 100644 --- a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md +++ b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md @@ -68,7 +68,7 @@ Copy the closest existing sibling instead of writing from scratch. Simple input/ | Framework | Placeholders | Files under `files/src/app/__path__/` | Test runner in generated project | |---|---|---|---| -| Angular | `<%=ClassName%>`, `<%=filePrefix%>`, `<%=igxPackage%>` | `__filePrefix__.ts`, `.html`, `.scss`, `.spec.ts` | Karma/Jasmine-style `TestBed` | +| Angular | `<%=ClassName%>`, `<%=filePrefix%>`, `<%=igxPackage%>` | `__filePrefix__.ts`, `.html`, `.scss`, `.spec.ts` | Vitest (via `@angular/build:unit-test`) with Angular `TestBed` | | React | `$(ClassName)`, `$(path)` | `__filePrefix__.tsx`, `__filePrefix__.test.tsx`, `style.module.css` | Vitest + Testing Library | | Web Components | `$(ClassName)`, `$(path)` | `__filePrefix__.ts`, `__filePrefix__.test.ts` | Vitest | From 32dbc30add93ee935c7748429eaca4da4e5e36b8 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Mon, 12 Oct 2026 12:03:43 +0300 Subject: [PATCH 03/11] chore: removing custom agents and adding skills --- .agents/README.md | 111 -------------- .agents/agents/component-template-agent.md | 141 ------------------ .agents/context/project.md | 24 --- .agents/main.yaml | 27 ---- .agents/permissions/policy.yaml | 49 ------ .claude/README.md | 102 +++++++++++++ .../skills/igniteui-cli-build-test/SKILL.md | 24 ++- .../skills/igniteui-cli-changelog/SKILL.md | 27 ++-- .../igniteui-cli-component-template/SKILL.md | 116 ++++++++++++++ .../skills/igniteui-cli-smoke-test/SKILL.md | 24 +-- .../igniteui-cli-template-anatomy/SKILL.md | 22 ++- .../SKILL.md | 76 ++++------ AGENTS.md | 90 +++++++++++ CLAUDE.md | 91 +---------- 14 files changed, 397 insertions(+), 527 deletions(-) delete mode 100644 .agents/README.md delete mode 100644 .agents/agents/component-template-agent.md delete mode 100644 .agents/context/project.md delete mode 100644 .agents/main.yaml delete mode 100644 .agents/permissions/policy.yaml create mode 100644 .claude/README.md rename {.agents => .claude}/skills/igniteui-cli-build-test/SKILL.md (68%) rename .agents/agents/changelog-agent.md => .claude/skills/igniteui-cli-changelog/SKILL.md (76%) create mode 100644 .claude/skills/igniteui-cli-component-template/SKILL.md rename {.agents => .claude}/skills/igniteui-cli-smoke-test/SKILL.md (82%) rename {.agents => .claude}/skills/igniteui-cli-template-anatomy/SKILL.md (83%) rename .agents/agents/template-version-update-agent.md => .claude/skills/igniteui-cli-template-version-update/SKILL.md (64%) create mode 100644 AGENTS.md diff --git a/.agents/README.md b/.agents/README.md deleted file mode 100644 index e09f767b2..000000000 --- a/.agents/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Ignite UI CLI Agent Guide - -Custom AI agents for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. - -The layout follows the [Agentic Collaboration Standard (ACS)](https://github.com/jackby03/agentic-collaboration-standard) and mirrors the agent system in `igniteui-angular`. - ---- - -## Ground Rules - -- **Agents never commit.** No `git add`, `commit`, `push`, branches or PRs. Every agent ends with the working tree changed and a report; a human reviews and commits. -- **The smoke test is the proof.** Jasmine stubs package installs, so template work is verified with `scripts/smoke-test.sh`. -- **Stop and report rather than guess.** Major version updates, missing APIs, id conflicts and anything out of scope are reported, not worked around. - ---- - -## Agent List - -| Agent | Writes | Use it for | -|---|---|---| -| [`template-version-update-agent`](./agents/template-version-update-agent.md) | Template pins | Update Ignite UI versions that `ig new` / `ig add` scaffold (patch/minor only). In `dry-run` mode: a read-only report of version drift and peer conflicts | -| [`component-template-agent`](./agents/component-template-agent.md) | New template folders | Add a component template for Angular, React and/or Web Components | -| [`changelog-agent`](./agents/changelog-agent.md) | `CHANGELOG.md` | Draft the release section for a version cut | - -Tasks that need no agent of their own use a skill from the main session: - -- **Triage a failed smoke run** you started by hand: the [smoke-test skill](./skills/igniteui-cli-smoke-test/SKILL.md) ("Triaging a Run"). The writing agents already triage their own runs. -- **Find missing or inconsistent templates across frameworks**: the [template-anatomy skill](./skills/igniteui-cli-template-anatomy/SKILL.md) ("Auditing Parity Across Frameworks"). - ---- - -## How They Fit Together - -Each agent works on its own and does not call the others. You chain them. - -```text -"what's missing?" (anatomy skill) ──► component-template-agent ──┐ -"what's stale?" (dry-run) ──► template-version-update-agent ──┤ - ▼ - each runs scripts/smoke-test.sh and triages its own failures - │ - (at release cut) ▼ - changelog-agent -``` - ---- - -## Quick Start - -| Goal | Ask | -|---|---| -| Update Web Components template versions to latest | `template-version-update-agent`: "update webcomponents" | -| Update to a specific version | `template-version-update-agent`: "update igniteui-angular to 22.3.0" | -| See which pins are stale or conflicting | `template-version-update-agent`: "dry-run, all frameworks" | -| Add a template | `component-template-agent`: "add Tile Manager for angular" | -| See which templates are missing | Main session: "which component templates are missing per framework?" | -| Make sense of a failed smoke run | Main session: "triage output/smoke" | -| Release notes for 15.9.0 | `changelog-agent`: "draft 15.9.0, 2026-10-15" | - -The agents and skills are not registered with any AI tool. To run one, point your assistant at its file, e.g. "Follow `.agents/agents/changelog-agent.md`: draft 15.9.0, 2026-10-15". If your tool can run a subagent, give it the file there, so build and smoke output stays out of your main session. - ---- - -## File Locations - -```text -.agents/ - main.yaml ← ACS manifest - README.md ← this guide - context/project.md ← working rules for agents (CLAUDE.md holds the project guide) - agents/ ← agent definitions - skills/ ← skills - permissions/policy.yaml ← read/write boundaries (documentation only; see below) -``` - -Everything lives in `.agents/`; there are no tool-specific copies to keep in sync. - -### Skills - -| Skill | Covers | -|---|---| -| [`igniteui-cli-build-test`](./skills/igniteui-cli-build-test/SKILL.md) | Build, lint, Jasmine, specs that guard templates | -| [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Running the smoke test, reading results, classifying and triaging failures | -| [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Template layout per framework, placeholders, every place versions are pinned, parity auditing | - ---- - -## Permissions - -`policy.yaml` is documentation: no tool reads or enforces it. Every limit on the agents (the no-commit rule, the working rules in `context/project.md`, each agent's "What You Do NOT Do") is an instruction the assistant follows, not a technical restriction. Review the diff before you commit. - ---- - -## Maintenance Notes - -- Agent and skill frontmatter carries `name` and `description` only (skills also `license`). Keep the README tables in line with them. -- **Change `policy.yaml` and the working rules in `context/project.md` together**; they describe the same boundaries. -- `dry-run` mode is read-only by instruction only. -- When repository facts change (new framework root, new pin location, smoke-test flags), update the **skill**, not each agent. - -### Adding an Agent - -Add one only for a recurring, multi-step task whose work would flood the main session's context. If a skill plus the main session can do it, write or extend a skill instead. - -1. Write `.agents/agents/-agent.md` with: role, inputs, "What You Do NOT Do" (including the no-commit rule), numbered steps, "Surfacing Problems", "Re-invocation", final report and self-validation. -2. Add it to the Agent List and Quick Start above. - -### Adding a Skill - -1. Write `.agents/skills//SKILL.md` (`name` must match the folder: lowercase letters, digits, hyphens). -2. Add it to the Skills table above and to "Internal Skills" in `context/project.md`. diff --git a/.agents/agents/component-template-agent.md b/.agents/agents/component-template-agent.md deleted file mode 100644 index 748587c93..000000000 --- a/.agents/agents/component-template-agent.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -name: component-template-agent -description: Adds a new component template (ig add ) to the Ignite UI CLI for Angular, React and/or Web Components. Verifies the component exists in the pinned package version, copies the closest sibling template, keeps ids consistent across frameworks, and validates with the smoke test plus type-check, lint and tests inside the generated project. Never commits. ---- - -# Component Template Agent - -You add **component templates** that users get with `ig add `, for Angular (`igx-ts`), React (`igr-ts`) and Web Components (`igc-ts`). - -Read first: -- [`.agents/context/project.md`](../context/project.md) -- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): layout, fields, placeholders -- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md) - ---- - -## Input - -- **Component**: e.g. "Tile Manager", "Rating". -- **Frameworks**: one or more of `angular`, `react`, `webcomponents`. -- **Variant** (optional): defaults to a single `default` variant showing basic usage. - -If either the component or the frameworks are missing, stop and ask. - ---- - -## What You Do NOT Do - -- Do not run `git add`, `git commit`, `git push`, `git stash`, create branches or open PRs. -- Do not change template base classes, `TemplateManager`, project templates, or other components' templates. -- Do not add or rename groups in `groups.json` without the user's approval. Use an existing group. -- Do not change package versions or add a package at a version not already pinned in the repository. Version choice belongs to `template-version-update-agent`. -- Do not invent components, props, events or imports. Every API used must be verified (Step 2). -- Do not create jQuery or Blazor templates. -- Do not write `CHANGELOG.md`. Propose a line in your report. - ---- - -## Steps - -### 1. Check What Already Exists - -For every framework (not only those in scope), look for an existing template for this component: - -- folder names and `this.id` values under the three framework roots; -- near-miss ids (`breadcrumb` / `breadcrumbs`, `nav-drawer` / `navigation-drawer`, `mask` / `mask-input`). - -Outcomes: -- Already exists in a target framework: stop and report; do not duplicate. -- Exists in another framework: **reuse that exact id**, group and naming. -- Exists in two other frameworks with different ids: stop and report the conflict; ask which id to use. - -### 2. Verify the Component in the Pinned Version - -For each target framework, find the package that provides the component and the version **currently pinned** (constants or project `package.json`). - -Verify the component's class or tag, the import path, and every prop/event you plan to use, against that version: - -1. **Preferred**: the Ignite UI MCP tools, if available in this session: `list_components`, `get_api_reference` (platform + component), `resolve_import`. -2. **Otherwise**: the package's type declarations. `npm pack @ --pack-destination `, extract it, and search the `.d.ts` files. Use a temporary directory outside the repository. - - Web Components element tag names (`htmlTagName`) are in the compiled JS (e.g. `esm2015/`), not in the `.d.ts`. - - Web Components attributes are the property names in dashed form (`chartTitle` → `chart-title`). Confirm in the compiled source when unsure. - -If the component is only available in a newer version than the one pinned, stop and report: a version update must happen first. - -### 3. Choose the Model Template - -Pick the closest existing sibling in the **same framework** and copy its structure exactly (same files, same style, same test shape): - -- simple input/display: `rating`, `slider`, `switch` -- data-bound: `combo`, `list`, `tree` -- charts and gauges: `pie-chart`, `financial-chart`, `radial-gauge` -- Angular: native `igx` component vs wrapped Web Component. See the anatomy skill; this is determined by whether `igniteui-angular` exports the component. - -When the same component already has a template in **another** framework: -- **structure and conventions** come from the same-framework sibling: file set, test shape, how data is organized, the `components` naming style (e.g. Web Components charts use `["PieChart"]`, not `["Pie Chart"]`); -- **identity and content** come from the other framework's template: `id`, display `name`, `group`, description wording, and the demo's sample data and props, so the component looks the same across frameworks. - -Name the sibling and any cross-framework template you used in the report. - -### 4. Create the Template - -Per framework: - -1. `//index.ts`: component (`BaseComponent`), with `name`, `group`, `description`. -2. `//default/index.ts`: template class with `components`, `controlGroup`, `listInComponentTemplates = true`, `id`, `projectType`, `name`, `description`, `packages`. - - Use package constants from `constants.ts`. If the needed package has no constant, use the same `name@range` string already pinned elsewhere in that framework and mention it in the report. -3. `default/files/src/app/__path__/` files with the framework's placeholder syntax and a test file matching the sibling. - -Keep the demo minimal: the component rendered with a few meaningful, verified props. No invented data services. - -### 5. Validate - -Do not run `npm run lint` or `npm run jasmine`. Lint ignores `packages/cli/templates/**` and `**/files/**`, so it does not see your template code, and CI runs both (including the id, file-collision and template-contract specs) on the pull request. Your template code is checked inside the generated project instead (sub-step 3). - -1. `npm run build`. The smoke test runs the compiled templates, so a stale build does not see your new template. -2. Per framework: `scripts/smoke-test.sh -f --templates --keep --out output/smoke--` -3. In each kept project (`output/smoke--//`), check the generated code directly: - - `npx tsc --noEmit`, **Web Components only**: its Vite build does not type-check. Angular and React builds already do, so skip it there. - - `npx eslint src/app/` - - Run the new test file (needs Playwright Chromium; see the smoke-test skill if it is missing): - - Angular: `npx ng test --include src/app/ --browsers=chromium --watch=false`. `ng build` neither runs nor type-checks `.spec.ts` files, so this is the only check of the Angular spec. - - React and Web Components: `npx vitest run src/app/`. -4. Delete the kept `output/smoke--*` directories when everything passes; keep them and say so when something fails. - -`ig add` does not add a route for Web Components templates from the command line; a missing `app-routing.ts` entry is expected. - -Fix failures that are caused by your template. If the failure points elsewhere (environment, project template), report it with evidence; do not change unrelated code. - -### 6. Surfacing Problems - -Stop and report, without guessing, when: -- the component is not in the pinned version; -- ids conflict across frameworks; -- no existing group fits; -- the component needs a package not pinned anywhere in the framework. - -### 7. Re-invocation - -If re-invoked with a correction note, fix the specific issue and re-run Step 5 for the affected frameworks only. - ---- - -## Final Report - -1. **Created**: per framework, the id, files created, the sibling used as model, packages registered. -2. **API verification**: how each API was verified (MCP tool or `.d.ts`), with the package version. -3. **Validation**: build result, and per framework the smoke result plus `tsc` (Web Components), `eslint` and the test run (`ng test` for Angular, `vitest` otherwise) in the kept project. Note that lint and Jasmine are left to CI. -4. **Stopped or skipped**: with reasons. -5. **Proposed CHANGELOG line**, e.g. - `* **feat(react):** Added a React \`igr-ts\` template for Tile Manager.` -6. **Diff**: `git status --short`. - -## Final Self-Validation - -- [ ] No `git` write command was run. -- [ ] The id matches the same component's id in every other framework. -- [ ] `controlGroup` and the component `group` match an existing `groups.json` key. -- [ ] Every API used was verified against the pinned version. -- [ ] A test file exists, shaped like the sibling's, and it was run in the kept project and passed. -- [ ] Smoke test and the kept-project checks passed for each framework, or failures are reported with evidence. diff --git a/.agents/context/project.md b/.agents/context/project.md deleted file mode 100644 index 27fdf1f1b..000000000 --- a/.agents/context/project.md +++ /dev/null @@ -1,24 +0,0 @@ -# Project Context - -The authoritative guide to this repository (stack, architecture, build and test commands) -is [`CLAUDE.md`](../../CLAUDE.md) at the repository root. Read it before making changes; -if it disagrees with this file, it wins. This file only adds the rules that apply to agents. - -## Working Rules for Agents - -- **Never commit.** Do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or open pull requests. If you changed files, leave them in the working tree and report the diff (`git status --short`, `git diff --stat`). A human reviews and commits. -- Temporary files (e.g. `npm pack` output for checking a package's type declarations) go in a temporary directory **outside the repository**. The one exception is `output/` (gitignored), which belongs to `scripts/smoke-test.sh`: smoke runs and commands inside a kept smoke project may write there. -- Do not edit compiled output (`*.js`, `*.js.map` next to a `.ts` source); edit the `.ts` and rebuild. -- Do not edit the repository's own manifests or lockfiles (root `package.json`, `packages/*/package.json`, `yarn.lock`). Project-template manifests under `**/files/package.json` are template content and may be edited when the task calls for it. -- Do not change the CLI's own version (`lerna`, `scripts/versionScript.ts`). -- When something is ambiguous or out of scope, stop and report instead of guessing. - -No tool enforces these rules; you are responsible for following them. The same boundaries, plus -not reading secrets such as `.env` and not editing under `.git/`, `node_modules/` or `coverage/`, -are listed in [`permissions/policy.yaml`](../permissions/policy.yaml). - -## Internal Skills - -- [`igniteui-cli-build-test`](../skills/igniteui-cli-build-test/SKILL.md): build, lint, Jasmine -- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md): running, judging and triaging the template smoke test -- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): template layout, version pins, cross-framework parity diff --git a/.agents/main.yaml b/.agents/main.yaml deleted file mode 100644 index 338a6459d..000000000 --- a/.agents/main.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Agentic Collaboration Standard (ACS) manifest. -# Spec: https://github.com/jackby03/agentic-collaboration-standard/tree/main/spec/v1 -# Schema: https://raw.githubusercontent.com/jackby03/agentic-collaboration-standard/main/schemas/v1/manifest.schema.json -# -# CLAUDE.md at the repository root remains the authoritative working guide. -# Agents and skills live only in .agents/ and are not registered with any tool; -# see README.md for how to run them. - -version: "1.0" - -project: - name: "igniteui-cli" - description: "Ignite UI CLI: scaffolds Angular, React, Web Components, jQuery and Blazor projects from folder-based templates, ships Angular schematics and the Ignite UI MCP documentation server." - language: typescript - framework: node - -layers: - context: true # context/project.md - skills: true # skills/ (build/test, smoke test, template anatomy) - commands: false # no reusable single-shot commands defined yet - agents: true # agents/ (version update, component template, changelog) - permissions: true # permissions/policy.yaml (documentation only; not enforced by any tool) - -compatible_with: - - claude-code - - github-copilot - - any diff --git a/.agents/permissions/policy.yaml b/.agents/permissions/policy.yaml deleted file mode 100644 index 10f1b009a..000000000 --- a/.agents/permissions/policy.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# ACS Permission Policy v1.0 -# Spec: https://github.com/jackby03/agentic-collaboration-standard/blob/main/spec/v1/08-permissions.md -# -# Documentation only: no tool reads or enforces this file. Agents follow it as -# instructions, together with the working rules in context/project.md, which it -# mirrors. Change both files together. -# -# Evaluation order: deny rules first, then allow rules, then default deny. -# -# Project-template manifests (packages/**/files/package.json) are template content, -# not repository manifests, so they are deliberately NOT denied: the -# template-version-update-agent edits them. Only the repository's own manifests -# and lockfiles are denied. -# -# output/** is denied for direct file edits only. scripts/smoke-test.sh and -# commands run inside a kept smoke project write there by design. - -deny: - read: - - ".env" - - ".env.*" - - "**/.env" - - "**/.env.*" - - "secrets/**" - - "**/*.pem" - - "**/*.key" - - "**/*.pfx" - - ".npmrc" - - "**/.npmrc" - write: - - ".git/**" - - "node_modules/**" - - "**/node_modules/**" - - "output/**" - - "coverage/**" - - "package.json" - - "packages/*/package.json" - - "packages/igniteui-mcp/igniteui-doc-mcp/package.json" - - "packages/igniteui-mcp/igniteui-doc-mcp/server.json" - - "yarn.lock" - - "**/package-lock.json" - - "lerna.json" - - "scripts/versionScript.ts" - -allow: - read: - - "**" - write: - - "**" diff --git a/.claude/README.md b/.claude/README.md new file mode 100644 index 000000000..e05afef32 --- /dev/null +++ b/.claude/README.md @@ -0,0 +1,102 @@ +# Ignite UI CLI Skills Guide + +AI skills for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. + +Each procedure is written once, as a skill in `.claude/skills/` ([Agent Skills](https://agentskills.io/specification) format), which Claude Code and VS Code Copilot both read. There are no custom agent files: you run a workflow by asking for the task, and the assistant loads the matching skill. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). + +--- + +## Ground Rules + +- **Workflows never commit.** No `git add`, `commit`, `push`, branches or PRs. Every workflow ends with the working tree changed and a report; a human reviews and commits. +- **The smoke test is the proof.** Jasmine stubs package installs, so template work is verified with `scripts/smoke-test.sh`. +- **Stop and report rather than guess.** Major version updates, missing APIs, id conflicts and anything out of scope are reported, not worked around. + +--- + +## Skills + +| Skill | Kind | Writes | Use it for | +|---|---|---|---| +| [`igniteui-cli-template-version-update`](./skills/igniteui-cli-template-version-update/SKILL.md) | Workflow | Template pins | Update Ignite UI versions that `ig new` / `ig add` scaffold (patch/minor only). In `dry-run` mode: a read-only report of version drift and peer conflicts | +| [`igniteui-cli-component-template`](./skills/igniteui-cli-component-template/SKILL.md) | Workflow | New template folders | Add a component template for Angular, React and/or Web Components | +| [`igniteui-cli-changelog`](./skills/igniteui-cli-changelog/SKILL.md) | Workflow | `CHANGELOG.md` | Draft or write the release section for a version cut | +| [`igniteui-cli-build-test`](./skills/igniteui-cli-build-test/SKILL.md) | Reference | — | Build, lint, Jasmine, running one spec file, specs that guard templates | +| [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Reference | — | Running the smoke test, checking kept projects, triaging failures ("Triaging a Run"), editing the script | +| [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Reference | — | Template layout per framework, model templates, every place versions are pinned, parity auditing ("Auditing Parity Across Frameworks") | + +The workflows triage their own smoke runs; use the smoke-test skill directly for a run you started by hand. + +--- + +## How They Fit Together + +Each workflow runs on its own and does not call the others. You chain them. + +```text +"what's missing?" (anatomy skill) ──► component-template workflow ──┐ +"what's stale?" (dry-run) ──► template-version-update workflow ──┤ + ▼ + each runs scripts/smoke-test.sh and triages its own failures + │ + (at release cut) ▼ + changelog workflow +``` + +--- + +## Quick Start + +| Goal | Ask | +|---|---| +| Update Web Components template versions to latest | "update webcomponents template versions" | +| Update to a specific version | "update template pins of igniteui-angular to 22.3.0" | +| See which pins are stale or conflicting | "dry-run template version update, all frameworks" | +| Add a template | "add a Tile Manager component template for angular" | +| See which templates are missing | "which component templates are missing per framework?" | +| Make sense of a failed smoke run | "triage output/smoke" | +| Release notes for 15.9.0 | "draft the changelog for 15.9.0, 2026-10-15" | + +The assistant picks the skill from its description. To be explicit, name it: in Claude Code, `/igniteui-cli-changelog draft 15.9.0, 2026-10-15`; in any tool, "Follow `.claude/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". + +**Keeping the session small.** The update and template workflows produce long build and smoke output. In Claude Code you can run one in a subagent so that output stays out of your session: "run the igniteui-cli-template-version-update skill in a subagent: update webcomponents". Otherwise start a fresh session per workflow. + +--- + +## File Locations + +```text +AGENTS.md ← shared conventions, incl. the rules for maintenance workflows +CLAUDE.md ← @AGENTS.md, plus Claude-only notes if any +.claude/ + README.md ← this guide + skills//SKILL.md ← every procedure and reference; read by Claude Code and Copilot +``` + +--- + +## Permissions + +Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" rules in `AGENTS.md`, each workflow's "Limits") is an instruction the assistant follows, not a technical restriction. Review the diff before you commit. + +--- + +## Maintenance Notes + +- Skill frontmatter carries `name`, `description` and `license`. Keep the Skills table in line with them. +- A skill's `description` is loaded in every session and decides when the skill is picked. Lead with the term people use for the task, keep the "WHEN TO USE / WHEN NOT TO USE" form, and name each trigger once; the procedure itself belongs in the body. +- Each fact lives in one skill. Workflows point to the reference skill (anatomy, smoke-test, build-test) at the step that needs it instead of restating it. When repository facts change (new framework root, new pin location, smoke-test flags), update that reference skill only. +- Boundaries shared by all workflows go in the "Maintenance Workflows" section of `AGENTS.md`, which is always loaded; skills do not link it. +- `dry-run` mode is read-only by instruction only. + +### Adding a Workflow + +Add one only for a recurring, multi-step task. If an existing reference skill plus a plain request covers it, extend that skill instead. + +1. Write `.claude/skills/igniteui-cli-/SKILL.md`: a one-line scope, inputs, "Limits" (only limits specific to this workflow, stated as what the workflow changes and what stops it; the shared ones, including the no-commit rule, stay in `AGENTS.md`), numbered steps with each stop condition at the step where it arises, "Follow-up Corrections" when the right fix differs from simply fixing and re-validating, final report and self-validation. Point to a reference skill at the step that uses it; put it under "Read first" only when most steps need it. +2. Add it to the Skills and Quick Start tables above. + +### Adding a Reference Skill + +1. Write `.claude/skills//SKILL.md` (`name` must match the folder: lowercase letters, digits, hyphens). +2. Add it to the Skills table above. diff --git a/.agents/skills/igniteui-cli-build-test/SKILL.md b/.claude/skills/igniteui-cli-build-test/SKILL.md similarity index 68% rename from .agents/skills/igniteui-cli-build-test/SKILL.md rename to .claude/skills/igniteui-cli-build-test/SKILL.md index 97ed8546b..d5d1e7ebe 100644 --- a/.agents/skills/igniteui-cli-build-test/SKILL.md +++ b/.claude/skills/igniteui-cli-build-test/SKILL.md @@ -1,6 +1,6 @@ --- name: igniteui-cli-build-test -description: "Quick reference for building, linting and running the Jasmine specs of the Ignite UI CLI monorepo, including how to narrow Jasmine to one spec file and which specs guard templates and version pins. WHEN TO USE: compiling after a change, running lint, running unit or template specs. WHEN NOT TO USE: checking that a generated project installs and builds; use igniteui-cli-smoke-test instead." +description: "Build, lint and Jasmine for the Ignite UI CLI monorepo: running one spec file, and which specs guard templates and version pins. WHEN TO USE: compiling after a change, running lint, running unit or template specs. WHEN NOT TO USE: checking that a generated project installs and builds (igniteui-cli-smoke-test)." license: MIT --- @@ -9,7 +9,7 @@ license: MIT ## Prerequisites - Dependencies are installed with **yarn** (`yarn install`), never npm. Scripts run with **npm**. -- Do not run `yarn install` or `yarn add` to "fix" something without being asked. It rewrites `yarn.lock`, which agents must not change. +- Run `yarn install` or `yarn add` only when asked. Either rewrites `yarn.lock`, which agents leave untouched; a build that fails on dependencies is reported, not "fixed" by reinstalling. ## Commands @@ -26,13 +26,13 @@ A run that reports **INCOMPLETE** only because some specs are pending (`xit`/`pe ## Running One Spec File -`spec/jasmine-runner.ts` hard-codes `spec_files`. To narrow a run: +Pass compiled spec files to the Jasmine CLI directly, after `npm run build`: -1. Temporarily change `spec_files` to the one spec, e.g. `"spec/templates/react-spec.js"`. -2. `npm run build && npm run jasmine`. -3. **Restore the file** and confirm `git diff spec/jasmine-runner.ts` is empty before finishing. +```bash +npx jasmine spec/templates/react-spec.js +``` -If restoring is at risk (long session, multiple runs), prefer the full `npm run jasmine`. +`npm run jasmine` ignores arguments and always runs every spec; `spec/jasmine-runner.ts` stays as it is. ## Specs That Guard Templates @@ -45,7 +45,15 @@ If restoring is at risk (long session, multiple runs), prefer the full `npm run | `spec/unit/TemplateManager-spec.ts`, `spec/unit/list-spec.ts` | Template discovery and `ig list` output | | `spec/acceptance/*` | `ig new`, `ig add`, `ig generate` end to end, with package install stubbed | -A failing "no internal collisions" spec means two templates would write the same file path: give the new template a distinct `__path__`/file prefix layout, do not weaken the spec. +The **template specs** for a framework run in about 15 seconds: + +```bash +npx jasmine spec/templates/angular-spec.js +npx jasmine spec/templates/react-spec.js spec/templates/template-contracts-spec.js +npx jasmine spec/templates/webcomponents-spec.js spec/templates/template-contracts-spec.js +``` + +A failing "no internal collisions" spec means two templates would write the same file path: give the new template a distinct `__path__`/file prefix layout and keep the spec as it is. ## Related Skills diff --git a/.agents/agents/changelog-agent.md b/.claude/skills/igniteui-cli-changelog/SKILL.md similarity index 76% rename from .agents/agents/changelog-agent.md rename to .claude/skills/igniteui-cli-changelog/SKILL.md index ec6729f55..dee74cfc3 100644 --- a/.agents/agents/changelog-agent.md +++ b/.claude/skills/igniteui-cli-changelog/SKILL.md @@ -1,14 +1,12 @@ --- -name: changelog-agent -description: Drafts the release section of the Ignite UI CLI CHANGELOG.md from the pull requests merged since the last release cut, in the repository's existing per-PR style. Edits only CHANGELOG.md, and only when asked to write it. Never commits. +name: igniteui-cli-changelog +description: "Changelog section for an Ignite UI CLI release cut, drafted from the PRs merged since the previous cut. WHEN TO USE: drafting or writing a release's CHANGELOG.md section. WHEN NOT TO USE: proposing one changelog line for your own change (the workflows' reports already do)." +license: MIT --- -# Changelog Agent +# Changelog -You write release notes for **Ignite UI CLI** in [`CHANGELOG.md`](../../CHANGELOG.md). Entries are written when a release is cut (commits like `chore(*): cut 15.8.1`), summarizing the PRs merged since the previous cut. - -Read first: -- [`.agents/context/project.md`](../context/project.md) +Release notes for **Ignite UI CLI** live in [`CHANGELOG.md`](../../../CHANGELOG.md). A section is written when a release is cut (commits like `chore(*): cut 15.8.1`), summarizing the PRs merged since the previous cut. --- @@ -16,18 +14,17 @@ Read first: - **Version** and **date** of the release (e.g. `15.9.0`, `2026-10-15`). If missing, stop and ask. - **Mode**: - - `draft` (default): output the section text only; do not edit files. + - `draft` (default): output the section text only; the working tree stays unchanged. - `write`: insert the section at the top of `CHANGELOG.md`. - Optional **range**: a commit range to use instead of "since the last cut". --- -## What You Do NOT Do +## Limits -- Do not edit any file other than `CHANGELOG.md`, and only in `write` mode. -- Do not run `git` write commands, `lerna`, `npm run release` or `npm run version`. -- Do not change existing release sections. -- Do not describe changes that are not in the commits and their diffs. +- Your one change is the new section at the top of `CHANGELOG.md`, in `write` mode only. Existing release sections stay as they are. +- Versioning and publishing belong to the release process: `npm run release` and `npm run version` are never run. +- Every bullet describes something visible in the commits and their diffs. --- @@ -82,9 +79,9 @@ Study the two most recent sections **before the target version** and match them: If a commit's purpose cannot be determined from its subject, diff and PR, list it under "Needs human input" in your output instead of guessing. -### 6. Re-invocation +### 6. Follow-up Corrections -If re-invoked with a correction note (a reworded bullet, a different scope, an answer to "Needs human input"), change only the affected bullets. Do not re-collect the whole range unless the range itself changed. +When the user corrects something after the report (a reworded bullet, a different scope, an answer to "Needs human input"), change only the affected bullets. Do not re-collect the whole range unless the range itself changed. --- diff --git a/.claude/skills/igniteui-cli-component-template/SKILL.md b/.claude/skills/igniteui-cli-component-template/SKILL.md new file mode 100644 index 000000000..34f29272c --- /dev/null +++ b/.claude/skills/igniteui-cli-component-template/SKILL.md @@ -0,0 +1,116 @@ +--- +name: igniteui-cli-component-template +description: "Add a component template (ig add ) for Angular, React and/or Web Components, verified against the pinned package version and validated end to end. WHEN TO USE: adding a component template to one or more frameworks. WHEN NOT TO USE: changing package versions (igniteui-cli-template-version-update); looking up template layout only (igniteui-cli-template-anatomy)." +license: MIT +--- + +# Component Template + +Adds the **component templates** users get with `ig add `, for Angular (`igx-ts`), React (`igr-ts`) and Web Components (`igc-ts`). + +Read first: [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) (layout, fields, placeholders, model templates). + +--- + +## Input + +- **Component**: e.g. "Tile Manager", "Rating". +- **Frameworks**: one or more of `angular`, `react`, `webcomponents`. +- **Variant** (optional): defaults to a single `default` variant showing basic usage. + +If either the component or the frameworks are missing, stop and ask. + +--- + +## Limits + +Your changes are the new template folders and nothing else. Stop and report when the work needs more than that: + +- Template base classes, `TemplateManager`, project templates and other components' templates stay as they are. +- Use an existing `groups.json` group. When none fits, stop and ask before adding or renaming one. +- Use only package versions already pinned in the framework. When the component needs a package pinned nowhere in the framework, stop and report; version choice belongs to the `igniteui-cli-template-version-update` workflow. +- Every component, prop, event and import in the template is one you verified in Step 2. +- Angular, React and Web Components only; jQuery and Blazor are out of scope. +- `CHANGELOG.md` is written at release time: propose a line in your report. + +--- + +## Steps + +### 1. Check What Already Exists + +For every framework (not only those in scope), look for an existing template for this component: + +- folder names and `this.id` values under the three framework roots; +- near-miss ids (`breadcrumb` / `breadcrumbs`, `nav-drawer` / `navigation-drawer`, `mask` / `mask-input`). + +Outcomes: +- Already exists in a target framework: stop and report; do not duplicate. +- Exists in another framework: **reuse that exact id**, group and naming. +- Exists in two other frameworks with different ids: stop and report the conflict; ask which id to use. + +### 2. Verify the Component in the Pinned Version + +For each target framework, find the package that provides the component and the version **currently pinned** (constants or project `package.json`). + +Verify the component's class or tag, the import path, and every prop/event you plan to use, against that version: + +1. **Preferred**: the Ignite UI MCP tools, if available in this session: `list_components`, `get_api_reference` (platform + component), `resolve_import`. +2. **Otherwise**: the package's type declarations. `npm pack @ --pack-destination `, extract it, and search the `.d.ts` files. Use a temporary directory outside the repository. + - Web Components element tag names (`htmlTagName`) are in the compiled JS (e.g. `esm2015/`), not in the `.d.ts`. + - Web Components attributes are the property names in dashed form (`chartTitle` → `chart-title`). Confirm in the compiled source when unsure. + +If the component is only available in a newer version than the one pinned, stop and report: a version update must happen first. + +### 3. Choose the Model Template + +Pick the closest **model template** in the **same framework** from the anatomy skill's list and copy its structure exactly (same files, same style, same test shape). For Angular, also pick native `igx` vs wrapped Web Component as the anatomy skill describes. + +When the same component already has a template in **another** framework: +- **structure and conventions** come from the same-framework sibling: file set, test shape, how data is organized, the `components` naming style (e.g. Web Components charts use `["PieChart"]`, not `["Pie Chart"]`); +- **identity and content** come from the other framework's template: `id`, display `name`, `group`, description wording, and the demo's sample data and props, so the component looks the same across frameworks. + +Name the sibling and any cross-framework template you used in the report. + +### 4. Create the Template + +Per framework, create the component `index.ts`, a `default` variant `index.ts` and its `files/`, following the anatomy skill: "Component Template Layout" for the files and every variant field, "Generated Files Per Framework" for placeholders and the test file. On top of that: + +- `listInComponentTemplates = true`. +- `packages`: when the needed package has no constant, use the same `name@range` string already pinned elsewhere in that framework and mention it in the report. +- The test file matches the sibling's shape. +- Keep the demo minimal: the component rendered with a few meaningful, verified props, and data written inline in the component. + +### 5. Validate + +Use the [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) and [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skills for the commands below. + +1. `npm run build`. The smoke test and the specs run the compiled templates, so a stale build does not see your new template. +2. The template specs for each framework in scope (build-test, "Specs That Guard Templates"): ids, file collisions, template contracts. +3. Angular only: `npx eslint packages/igx-templates/igx-ts//index.ts packages/igx-templates/igx-ts//default/index.ts`. React and Web Components `index.ts` files are ignored by the repository lint config; their code is linted in the kept project. +4. Per framework: `scripts/smoke-test.sh -f --templates --keep --out output/smoke--`. +5. In each kept project (`output/smoke--//`), run every check in smoke-test's "Checking a Kept Project" table for that framework. +6. Delete the kept `output/smoke--*` directories when everything passes; keep them and say so when something fails. + +Fix failures caused by your template. When a failure points elsewhere (environment, project template), report it with evidence and leave that code as it is. + +--- + +## Final Report + +1. **Created**: per framework, the id, files created, the sibling used as model, packages registered. +2. **API verification**: how each API was verified (MCP tool or `.d.ts`), with the package version. +3. **Validation**: build result, the template specs, the Angular `index.ts` lint, and per framework the smoke result plus each kept-project check. +4. **Stopped or skipped**: with reasons. +5. **Proposed CHANGELOG line**, e.g. + `* **feat(react):** Added a React \`igr-ts\` template for Tile Manager.` +6. **Diff**: `git status --short`. + +## Final Self-Validation + +- [ ] No `git` write command was run. +- [ ] The id matches the same component's id in every other framework. +- [ ] `controlGroup` and the component `group` match an existing `groups.json` key. +- [ ] Every API used was verified against the pinned version. +- [ ] A test file exists, shaped like the sibling's, and it was run in the kept project and passed. +- [ ] Template specs, smoke test and every kept-project check passed for each framework, or failures are reported with evidence. diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.claude/skills/igniteui-cli-smoke-test/SKILL.md similarity index 82% rename from .agents/skills/igniteui-cli-smoke-test/SKILL.md rename to .claude/skills/igniteui-cli-smoke-test/SKILL.md index bd699315e..844d5a45f 100644 --- a/.agents/skills/igniteui-cli-smoke-test/SKILL.md +++ b/.claude/skills/igniteui-cli-smoke-test/SKILL.md @@ -1,6 +1,6 @@ --- name: igniteui-cli-smoke-test -description: "How to run scripts/smoke-test.sh (scaffold a project with the local CLI, add templates, npm install, build) and how to judge its results and logs, including classifying failures as template defects, version-pin problems or environment noise. WHEN TO USE: verifying template or version changes end to end, reading output/smoke results, triaging a failed smoke run. WHEN NOT TO USE: compiling the CLI or running Jasmine; use igniteui-cli-build-test instead." +description: "Smoke test (scripts/smoke-test.sh): scaffold with the local CLI, add templates, install, build; judging results, checking kept projects, triaging failures. WHEN TO USE: verifying template or version changes end to end, reading or triaging output/smoke results, editing scripts/smoke-test.sh. WHEN NOT TO USE: compiling the CLI or running Jasmine (igniteui-cli-build-test)." license: MIT --- @@ -22,7 +22,7 @@ Preconditions: `npm run build` has run (the script refuses otherwise); `node` an Defaults: angular, react, webcomponents; default project template; all component templates. jQuery and Blazor are opt-in (`-f jquery`, `-f blazor`): jQuery needs ProGet credentials for 13 templates and has no build; Blazor needs the .NET SDK and has no component templates. -`--all-projects` takes its list from `ig list`, so **hidden project templates** (`base`, `side-nav-auth`, `side-nav-mini-auth`) are not scaffolded. They reuse the `_base` / `_base_with_home` files, except where a hidden project has its own `files/package.json` (e.g. Angular `side-nav-auth`): to cover it, run `scripts/smoke-test.sh -f -p `. +`--all-projects` takes its list from `ig list`, so **hidden project templates** (`isHidden = true` in the project's `index.ts`, e.g. `base`, `side-nav-auth`, `ai-config`) are not scaffolded. Most reuse the `_base` / `_base_with_home` files; a hidden project with its own `files/package.json` (e.g. Angular `side-nav-auth`) is covered only by `scripts/smoke-test.sh -f -p `. Runs longer than a few minutes: use a long command timeout, or run it in the background with output redirected to a file and wait until that file contains the final `Results:` line (the script prints it last). The script **wipes its `--out` directory at start**, so give each run its own directory (e.g. `--out output/smoke-react`, `--out output/smoke-react-projects`) when you need more than one run's results, and never run two at once into the same directory. @@ -40,19 +40,21 @@ Runs longer than a few minutes: use a long command timeout, or run it in the bac | React | `tsc -b && vite build` | Yes | | Web Components | `vite build` | **No.** Vite strips types without checking, and an added view that no route imports is not even bundled | -So for **Web Components**, a smoke PASS proves the template is added and its packages install, not that its code compiles. To verify the code, run with `--keep` and, in the kept project (`output///`): +So for **Web Components**, a smoke PASS proves the template is added and its packages install, not that its code compiles. For **Angular**, `ng build` compiles from `tsconfig.app.json`, which excludes `*.spec.ts`, so the template's spec is neither type-checked nor run. No build runs the template's test file. A **kept project** closes these gaps. -``` -npx tsc --noEmit -npx eslint src/app/ -npx vitest run src/app/ -``` +`ig add` from the command line does not register a route for Web Components templates, so a missing entry in the generated `app-routing.ts` is expected, not a template defect. -For **Angular**, `ng build` compiles from `tsconfig.app.json`, which excludes `*.spec.ts`, so the template's spec is neither type-checked nor run. In the kept Angular project, run it with `npx ng test --include src/app/ --browsers=chromium --watch=false` (the `@angular/build:unit-test` builder, which uses Vitest). +## Checking a Kept Project -`vitest` (React and Web Components) and `ng test` (Angular) run in browser mode with Playwright Chromium. If it fails because the browser is missing, run `npx playwright install chromium` once and retry; that is environment setup, not a template defect. +Run the smoke test with `--keep`, then in the kept project (`//`) check the template ``: -`ig add` from the command line does not register a route for Web Components templates, so a missing entry in the generated `app-routing.ts` is expected, not a template defect. +| Framework | Type-check | Lint | Test file | +|---|---|---|---| +| Angular | covered by `ng build` | `npx eslint src/app/` | `npx ng test --include src/app/ --browsers=chromium --watch=false` (the `@angular/build:unit-test` builder, which uses Vitest) | +| React | covered by `tsc -b` | `npx eslint src/app/` | `npx vitest run src/app/` | +| Web Components | `npx tsc --noEmit` | `npx eslint src/app/` | `npx vitest run src/app/` | + +The test runners use browser mode with Playwright Chromium. If one fails because the browser is missing, run `npx playwright install chromium` once and retry; that is environment setup, not a template defect. ## Judging a Step diff --git a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md b/.claude/skills/igniteui-cli-template-anatomy/SKILL.md similarity index 83% rename from .agents/skills/igniteui-cli-template-anatomy/SKILL.md rename to .claude/skills/igniteui-cli-template-anatomy/SKILL.md index c8e97d044..a1a29fd51 100644 --- a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md +++ b/.claude/skills/igniteui-cli-template-anatomy/SKILL.md @@ -1,6 +1,6 @@ --- name: igniteui-cli-template-anatomy -description: "Layout of Ignite UI CLI component templates and Ignite UI version pins for Angular (igx-ts), React (igr-ts) and Web Components (igc-ts): folder structure, index.ts fields, placeholder syntax, test files, groups, constants files and every place a package version is pinned. WHEN TO USE: adding or changing a component template, updating Ignite UI package versions, auditing templates across frameworks. WHEN NOT TO USE: running builds or the smoke test; use igniteui-cli-build-test or igniteui-cli-smoke-test." +description: "Template anatomy for Angular (igx-ts), React (igr-ts) and Web Components (igc-ts): folder layout, index.ts fields, placeholders, test files, groups, and every place an Ignite UI version is pinned. WHEN TO USE: adding or changing a component template, locating version pins, auditing template parity across frameworks. WHEN NOT TO USE: running builds or the smoke test (igniteui-cli-build-test, igniteui-cli-smoke-test)." license: MIT --- @@ -18,9 +18,9 @@ Templates are discovered by **scanning folders**. There is no registry file. A c | React | `igr-ts` | `packages/cli/templates/react/igr-ts/` | `packages/cli/lib/templates/IgniteUIForReactTemplate.ts` | `packages/cli/templates/react/igr-ts/constants.ts` | | Web Components | `igc-ts` | `packages/cli/templates/webcomponents/igc-ts/` | `packages/cli/lib/templates/IgniteUIForWebComponentsTemplate.ts` | `packages/cli/templates/webcomponents/igc-ts/constants.ts` | -jQuery (`packages/cli/templates/jquery/js/`) is legacy and Blazor (`packages/cli/templates/blazor/igb/`) has no component templates. Both are out of scope for template agents unless the user asks. +jQuery (`packages/cli/templates/jquery/js/`) is legacy and Blazor (`packages/cli/templates/blazor/igb/`) has no component templates. Both are out of scope for the template workflows unless the user asks. -`@igniteui/angular-templates` is a separate npm package: Angular templates must **not** import from `packages/cli/templates/`. A version Angular shares with Web Components (e.g. `igniteui-webcomponents` for wrapped components) is kept on the Angular side. +`@igniteui/angular-templates` is a separate npm package: Angular templates import only from `packages/igx-templates/` and `@igniteui/cli-core`, never from `packages/cli/templates/`. A version Angular shares with Web Components (e.g. `igniteui-webcomponents` for wrapped components) is kept on the Angular side. ## Component Template Layout @@ -62,7 +62,11 @@ Variant `index.ts` fields: | `packages` | npm packages added to the project on `ig add`, as `name@range`. Use constants from `constants.ts` where one exists | | `dependencies` | Optional extra registrations (rare) | -Copy the closest existing sibling instead of writing from scratch. Simple input/display components: `rating`, `slider`, `switch`. Data-bound components: `combo`, `list`, `tree`. Charts/gauges: `pie-chart`, `financial-chart`, `radial-gauge`. +Copy the closest existing sibling instead of writing from scratch. These **model templates** exist in all three frameworks: + +- simple input/display: `rating`, `slider`, `switch` +- data-bound: `combo`, `list`, `tree` +- charts and gauges: `category-chart`, `financial-chart`, `radial-gauge` ## Generated Files Per Framework @@ -112,8 +116,10 @@ Where a component's code lives is not always obvious. Verify against the pinned | Grid Lite | `igniteui-angular` (`/grid-lite` entry) + peer `igniteui-grid-lite` | `igniteui-react` (`grid-lite` subpath) + peer `igniteui-grid-lite` | `igniteui-grid-lite` | | Charts (category, financial, pie, doughnut) | `igniteui-angular-charts` + `-core` | `igniteui-react-charts` | `igniteui-webcomponents-charts` + `-core` | | Gauges (linear, radial, bullet graph) | `igniteui-angular-gauges` + `-core` | `igniteui-react-gauges` | `igniteui-webcomponents-gauges` + `-core` | -| Geographic Map | `igniteui-angular-maps` (+ charts, core) | `igniteui-react-maps` (no constant yet) | `igniteui-webcomponents-maps` (no constant yet) | -| Dock Manager | `igniteui-dockmanager` | `igniteui-react-dockmanager` (no constant yet) | `igniteui-dockmanager` | +| Geographic Map | `igniteui-angular-maps` (+ charts, core) | `igniteui-react-maps` | `igniteui-webcomponents-maps` | +| Dock Manager | `igniteui-dockmanager` | `igniteui-react-dockmanager` | `igniteui-dockmanager` | + +Whether a package has a constant is in the framework's `constants.ts`; check there before adding a pin. ## Peer Dependencies @@ -139,9 +145,11 @@ To answer "which templates are missing or inconsistent?", list component folders - **Library naming difference**: ids differ because the libraries name the component differently (`navigation-drawer` vs `nav-drawer`). Not an error. - **Group mismatch**, **missing test**, **composite** (e.g. `form`, not one library component; informational). -Hand actionable gaps to `component-template-agent`; for version drift, run `template-version-update-agent` in `dry-run` mode. +Hand actionable gaps to the [`igniteui-cli-component-template`](../igniteui-cli-component-template/SKILL.md) workflow; for version drift, run [`igniteui-cli-template-version-update`](../igniteui-cli-template-version-update/SKILL.md) in `dry-run` mode. ## Related Skills +- [`igniteui-cli-component-template`](../igniteui-cli-component-template/SKILL.md): workflow that creates a template using this layout +- [`igniteui-cli-template-version-update`](../igniteui-cli-template-version-update/SKILL.md): workflow that updates the pins listed here - [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) - [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) diff --git a/.agents/agents/template-version-update-agent.md b/.claude/skills/igniteui-cli-template-version-update/SKILL.md similarity index 64% rename from .agents/agents/template-version-update-agent.md rename to .claude/skills/igniteui-cli-template-version-update/SKILL.md index 94ff7c655..741b87677 100644 --- a/.agents/agents/template-version-update-agent.md +++ b/.claude/skills/igniteui-cli-template-version-update/SKILL.md @@ -1,16 +1,12 @@ --- -name: template-version-update-agent -description: Updates the Ignite UI package versions that the CLI's Angular, React and Web Components templates scaffold into new projects. Finds every pin by search, applies patch and minor updates, verifies with a smoke test scoped to the changed packages, and stops and reports on major updates. Also runs as a read-only dry-run that reports version drift and peer conflicts. Never commits. +name: igniteui-cli-template-version-update +description: "Update or audit the Ignite UI versions that Angular, React and Web Components templates scaffold (ig new / ig add). WHEN TO USE: updating template version pins; asking which pins are stale or conflicting (dry-run). WHEN NOT TO USE: the CLI's own dependencies or version; adding a template (igniteui-cli-component-template)." +license: MIT --- -# Template Version Update Agent +# Template Version Update -You update the **Ignite UI package versions pinned in project and component templates**: what `ig new` and `ig add` write into a user's `package.json`. You do not update the CLI's own dependencies or version. - -Read first: -- [`.agents/context/project.md`](../context/project.md) -- [`igniteui-cli-template-anatomy`](../skills/igniteui-cli-template-anatomy/SKILL.md): where pins live, release trains -- [`igniteui-cli-smoke-test`](../skills/igniteui-cli-smoke-test/SKILL.md) +Updates the **Ignite UI package versions pinned in project and component templates**: what `ig new` and `ig add` write into a user's `package.json`. The CLI's own dependencies and version are out of scope. --- @@ -19,10 +15,10 @@ Read first: - **Scope**: one or more of `angular`, `react`, `webcomponents`, or specific package names. - **Targets** (optional): explicit versions, e.g. `igniteui-webcomponents 7.5.0`. If none are given, the target for each package is its npm `latest` dist-tag. - **Mode**: - - `update` (default): Steps 1-8. + - `update` (default): Steps 1-7. - `dry-run`: a read-only drift report. Run Steps 1-3, then stop and give the dry-run report (see Final Report). Edit no files and run no builds or smoke tests. Use it for "what is out of date?" questions; `all` is a valid scope here. -If the scope is missing, stop and ask for it. Do not default to "everything" in `update` mode. +If the scope is missing, or a scope or target is ambiguous, stop and ask. In `update` mode the scope is always named explicitly; "everything" is never assumed. Scope boundaries: - A framework scope covers only that framework's root. With scope `webcomponents`, the `igniteui-webcomponents` pins in Angular's wrapped-component templates (`packages/igx-templates/.../chat`, `rating`) are **out of scope**; report them as observations. @@ -30,17 +26,16 @@ Scope boundaries: --- -## What You Do NOT Do +## Limits + +Your changes are Ignite UI version strings, applied as patch or minor updates. Everything else is reported: -- Do not run `git add`, `git commit`, `git push`, `git stash`, create branches or open PRs. The user reviews and commits. -- Do not apply **major** updates (see Step 3). Stop and report them. -- Do not change range style (`~` vs `^`). Keep the prefix each pin already has; report inconsistencies. -- Do not move hard-coded pins into constants or otherwise refactor, unless the user asked for it. -- Do not edit the repository's own manifests or lockfiles (root `package.json`, `packages/*/package.json`, `yarn.lock`), `lerna.json` or `scripts/versionScript.ts`. -- Do not edit compiled `*.js` files. Edit `.ts` and `.json` sources. -- Do not touch non-Ignite UI dependencies in project templates (Angular, React, Vite, TypeScript, etc.). If a new Ignite UI version requires them to change, that is a stop condition (Step 3). -- Do not touch jQuery or Blazor templates unless explicitly in scope. -- Do not write `CHANGELOG.md`. Propose a line in your report instead. +- **Major** updates (see Step 3) are stop items, never applied. +- Each pin keeps its range prefix (`~` or `^`); report inconsistent prefixes as observations. +- Pins stay where they are: hard-coded pins stay hard-coded unless the user asked to move them into constants. +- Non-Ignite UI dependencies in project templates (Angular, React, Vite, TypeScript, etc.) stay as they are. If a new Ignite UI version requires them to change, that is a stop item (Step 3). +- jQuery and Blazor templates are out of scope unless explicitly named. +- `CHANGELOG.md` is written at release time: propose a line in your report. --- @@ -52,32 +47,32 @@ Run `git status --short` and keep the output. Your final report must separate yo ### 2. Build the Inventory -Search for every Ignite UI pin in scope, using **both** patterns and the exclusions from the template-anatomy skill, over `packages/cli/templates` and `packages/igx-templates`. +Search for every Ignite UI pin in scope, using **both** patterns and the exclusions from [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) ("Where Ignite UI Versions Are Pinned"), over `packages/cli/templates` and `packages/igx-templates`. Produce a table, one row per occurrence: | File:line | Package | Current range | |---|---|---| -Include all three kinds of locations: `constants.ts`, `projects/*/files/package.json`, and hard-coded `this.packages = [...]` in `index.ts`. If a pin appears in a form the pattern missed (you notice one while reading), add it and mention it in the report. +Include all three kinds of locations: `constants.ts`, `projects/*/files/package.json`, and hard-coded `this.packages = [...]` in `index.ts`. If a pin appears in a form the pattern missed (you notice one while reading), add it and mention it in the report. If a pin sits in a place you are not sure you may edit, list it as a stop item. ### 3. Resolve and Classify Targets For each distinct package: 1. Determine the target: the user's explicit version, else `npm view dist-tags.latest`. -2. Confirm it exists: `npm view @ version`. +2. Confirm it exists: `npm view @ version`. A target that does not exist is a stop item for that package. 3. Classify current → target: - **patch / minor** (same major; for `0.x`, same minor): proceed. - - **major** (or `0.x` minor change): **stop for that package**. Do not edit any of its pins. - - **already current** (the existing range already resolves to the target): no change. This includes `^` pins whose range admits a newer minor (e.g. `^2.1.1` admits 2.2.1). Do not raise their minimum unless the user asks; list them under Observations as "minimum could be raised to `^`". + - **major** (or `0.x` minor change): **stop for that package**. Leave all of its pins unchanged. + - **already current** (the existing range already resolves to the target): no change. This includes `^` pins whose range admits a newer minor (e.g. `^2.1.1` admits 2.2.1). Keep their minimum unless the user asks; list them under Observations as "minimum could be raised to `^`". 4. For packages that proceed, check peers: `npm view @ peerDependencies`. - If a peer is a **non-Ignite UI** package (e.g. a new `igniteui-angular` needing a newer `@angular/core`) and the project templates don't satisfy it, **stop for that package** and report the required change. - If a peer is another **Ignite UI** package pinned in templates (e.g. `igniteui-angular` → `igniteui-grid-lite ~0.11.0`), that pin must satisfy the peer range after your update. Treat bringing it into range as part of this update, subject to the same patch/minor/major rule; if that would be a major (or `0.x` minor) change, report it as a stop item with the peer range as the reason, so a human can approve it explicitly. Do this check also for the framework's **main package even when it is not changing**: existing pins can already be out of range. -Never infer one package's version from another's. Release trains are independent (see the anatomy skill). +Look up each package's version on npm by itself. Release trains are independent (see the anatomy skill), so one package's version says nothing about another's. While classifying, also note drift within the inventory: pins that differ from the framework's constant for the same package, the same package at different ranges in different files, and mixed `~`/`^` prefixes for the same package. @@ -99,37 +94,30 @@ Search again for each **old** version string of every package you changed, over ### 6. Validate -The smoke test is the only validation. Do not run `npm run lint` or `npm run jasmine`: your edits are version strings, which lint does not check, Step 5 already catches missed pins, and CI runs both on the pull request. +Use the [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) and [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skills for the commands below. Lint is skipped: it does not check version strings. -1. `npm run build`. The smoke test runs the compiled templates, so a stale build tests the old pins. If it fails, diagnose it before going further. -2. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: +1. `npm run build`. The smoke test and the specs run the compiled templates, so a stale build tests the old pins. If it fails, diagnose it before going further. +2. The template specs for each framework in scope (build-test, "Specs That Guard Templates"). For Angular and React they check every main-package pin against its constant. +3. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: - The framework's main package changed (`igniteui-angular`, `igniteui-react`, `igniteui-webcomponents`), which nearly every template pulls in: `scripts/smoke-test.sh -f --out output/smoke-` (default project + every component template). - Otherwise, only the templates that pull in a changed package: search the framework's variant `index.ts` files for the package name or its constant, read each match's `this.id`, and run `scripts/smoke-test.sh -f --templates , --out output/smoke-`. For example, an `igniteui-dockmanager` update runs `--templates dock-manager`. The default project is always scaffolded, so a pin in its `package.json` is covered too. - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. `--all-projects` skips hidden projects, so for each changed manifest under a hidden project (`isHidden = true` in its `index.ts`, e.g. Angular `side-nav-auth`) also run `scripts/smoke-test.sh -f -p --out output/smoke--`. If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), these runs alone cover it; skip the `--templates` run. -A scoped run takes about a minute; a full run takes several. Use a long timeout or background execution and wait for completion. Run them one at a time. Classify any failure with the smoke-test skill. Environment failures (network, credentials) are re-run once, then reported; they are not fixed by changing versions. +A scoped run takes about a minute; a full run takes several. Use a long timeout or background execution and wait for completion. Run them one at a time. Classify any failure with the smoke-test skill. Environment failures (network, credentials) are re-run once, then reported; versions stay as they are for those. If a failure is caused by the new version (missing export, renamed prop, peer conflict), revert **that package's** edits, keep the others, and report it as a stop item with the evidence (log path and the error line). -### 7. Surfacing Problems - -Stop and report, without guessing, when: -- the scope or targets are ambiguous; -- a target version does not exist on npm; -- a major update or peer change is required; -- a pin exists in a place you are not sure you may edit. - -### 8. Re-invocation +### 7. Follow-up Corrections -If you are re-invoked with a correction note, read it, fix the specific issue identified, and re-run Steps 5 and 6 for the affected frameworks. Do not redo the whole update. +When the user corrects something after the report, fix that issue and re-run Steps 5 and 6 for the affected frameworks only, rather than the whole update. --- ## Final Report 1. **Updated**: table `package | old range | new range | files changed`. -2. **Stopped**: each package not updated, with the reason (major, peer conflict, failed smoke with evidence). -3. **Validation**: build result, and per framework each smoke command run (full, which `--templates`, `--all-projects`, which `-p`), the PASS/FAIL/SKIP summary and the path of its `results.tsv`. Note that lint and Jasmine are left to CI. +2. **Stopped**: each package not updated, with the reason (major, missing version, peer conflict, unclear pin location, failed smoke with evidence). +3. **Validation**: build result, template specs, and per framework each smoke command run (full, which `--templates`, `--all-projects`, which `-p`), the PASS/FAIL/SKIP summary and the path of its `results.tsv`. 4. **Observations** (not acted on): mixed range styles, pins that bypass constants, packages that are stale but out of scope. 5. **Proposed CHANGELOG line**, in the repository's style: `* **chore():** Updated scaffolded projects to \`\` \`~x.y.z\`.` @@ -151,4 +139,4 @@ In `dry-run` mode only the first item applies, plus: `git status --short` is unc - [ ] Every changed package has zero remaining occurrences of its old version in scope. - [ ] No major update was applied. - [ ] No range prefix changed. -- [ ] Build passed; smoke results reported for every framework in scope, covering every template that pulls in a changed package. +- [ ] Build and template specs passed; smoke results reported for every framework in scope, covering every template that pulls in a changed package. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..ccc329acd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,90 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. `CLAUDE.md` imports this file. + +## Build, Test, Lint + +```bash +yarn install # REQUIRED — must use yarn (not npm) for workspace resolution +npm run build # tsc + config schema generation + MCP server build +npm run test # lint + build + tests with coverage (via pretest hook) +npm run jasmine # tests only — no lint/build/coverage +npm run lint # ESLint (flat config in eslint.config.mjs) +scripts/smoke-test.sh # scaffold + add every template + install + build +``` + +Tests are Jasmine specs under `spec/` (unit + acceptance) and within individual packages. Running one spec file and the specs that guard templates: `igniteui-cli-build-test` skill. + +`scripts/smoke-test.sh` is the only check that a generated project actually installs and builds — the Jasmine specs stub `PackageManager.installPackages`. Run it after touching anything under `packages/cli/templates/` or `packages/igx-templates/`; running it, reading its results and editing the script: `igniteui-cli-smoke-test` skill. + +### MCP Server (`packages/igniteui-mcp/igniteui-doc-mcp`) + +Workspace package — part of yarn workspaces and lerna. Built automatically by `npm run build` via the `build:mcp` script. Has its own `tsconfig.json` (ES2022/ESM, separate from root CJS config). + +```bash +npm run build:mcp # build MCP server (also runs as part of npm run build) +cd packages/igniteui-mcp/igniteui-doc-mcp +npm run build:db # build SQLite DB from compressed docs (manual step) +npm run pipeline:angular # incremental doc pipeline (also: react, blazor, webcomponents) +npm run pipeline:angular:full # full rebuild pipeline +npm run inspector # test with MCP Inspector +``` + +## Architecture + +**Monorepo** managed with lerna + yarn workspaces. Five packages: + +| Package | npm | Path | +|---------|-----|------| +| CLI (entry point) | `igniteui-cli` | `packages/cli` | +| Core (shared logic) | `@igniteui/cli-core` | `packages/core` | +| Angular Templates | `@igniteui/angular-templates` | `packages/igx-templates` | +| Angular Schematics | `@igniteui/angular-schematics` | `packages/ng-schematics` | +| MCP Server | `@igniteui/mcp-server` | `packages/igniteui-mcp/igniteui-doc-mcp` | + +**Core is the dependency root** — changes to `packages/core` affect all other packages. Always rebuild the full monorepo after core changes. + +### Build System + +- TypeScript compiled with `tsc` directly (no bundler). Two configs: `tsconfig.json` (dev) and `tsconfig-pack.json` (production). +- `packages/core/types/Config.ts` is source of truth for config schema. Modifying it requires `npm run config-schema` (included in `npm run build`). +- Monorepo targets ES6/CommonJS. MCP server targets ES2022 with Node16 module resolution (`"type": "module"`). +- Path aliases in tsconfig: `@igniteui/cli-core` → `packages/core`, `@igniteui/angular-templates` → `packages/igx-templates`, etc. + +### Template System + +- Handlebars for template processing +- Templates in `packages/cli/templates/**/files/` and `packages/igx-templates/` +- Component/template IDs (e.g., "grid", "combo") used by `ig add` command +- Default framework is **jQuery** when not specified + +### MCP Server + +- `McpServer` from `@modelcontextprotocol/sdk` with stdio transport +- Two modes: **Remote** (default, proxies to `DOCS_BACKEND_URL`) and **Local** (`--local` flag, uses sql.js WASM with FTS4) +- Framework detection via component prefix: `Igx` → Angular, `Igr` → React, `Igc` → Web Components, `Igb` → Blazor +- DB build uses `better-sqlite3` (native, dev only); runtime uses `sql.js` (WASM) +- Doc compression uses OpenAI API (`OPENAI_API_KEY` in `.env`) +- Git submodules under `packages/igniteui-mcp/igniteui-doc-mcp/` provide documentation source repos +- Consult `packages/igniteui-mcp/igniteui-doc-mcp/docs/knowledgebase.md` before debugging pipeline problems + +## Key Conventions + +- **yarn for install, npm for scripts** +- CI runs on Node 20 with `yarn --frozen-lockfile` +- ESLint: `no-var`, `no-eval`, `prefer-const`, `no-console` (off in test files), `no-duplicate-imports` +- Test files match pattern `*[sS]pec.ts` — found in `spec/`, `packages/cli/migrations/`, `packages/igx-templates/`, `packages/ng-schematics/` + +## Maintenance Workflows + +Template maintenance procedures are skills in `.claude/skills/`, read by both Claude Code and Copilot. Before adding or changing a workflow, read the guide in `.claude/README.md`. + +These rules bind every run of a workflow skill, whether from the main session or a subagent. No tool enforces them. + +- **Never commit.** Leave changes in the working tree and report them with `git status --short` and `git diff --stat`; a human reviews and commits. Git stays read-only (`status`, `diff`, `log`, `show`): do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or any other git command that writes, and do not open pull requests. +- Temporary files (e.g. `npm pack` output for checking a package's type declarations) go in a temporary directory **outside the repository**. The one exception is `output/` (gitignored), which belongs to `scripts/smoke-test.sh`: smoke runs and commands inside a kept smoke project write there; files under `output/` change only through those runs. +- Edit the `.ts` source and rebuild; compiled output (`*.js`, `*.js.map` next to a `.ts`) is never edited by hand. +- Leave the repository's own manifests and lockfiles untouched: root `package.json`, `packages/*/package.json`, `packages/igniteui-mcp/igniteui-doc-mcp/package.json` and `server.json`, `yarn.lock`, any `package-lock.json`. Project-template manifests under `**/files/package.json` are template content and may be edited when the task calls for it. +- Leave the CLI's own version alone: do not run `lerna`, and do not edit `lerna.json` or `scripts/versionScript.ts`. Leave anything under `.git/`, `coverage/` or any `node_modules/` untouched. +- Leave secrets unread, at any depth: `.env`, `.env.*`, `.npmrc`, `secrets/`, `*.pem`, `*.key`, `*.pfx`. When a task seems to need one, stop and ask. +- When something is ambiguous or out of scope, stop and report instead of guessing. diff --git a/CLAUDE.md b/CLAUDE.md index adb13f529..43c994c2d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,90 +1 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Build, Test, Lint - -```bash -yarn install # REQUIRED — must use yarn (not npm) for workspace resolution -npm run build # TypeScript compilation (tsc) + config schema generation -npm run test # lint + build + tests with coverage (via pretest hook) -npm run jasmine # tests only — no lint/build/coverage -npm run lint # ESLint (flat config in eslint.config.mjs) -scripts/smoke-test.sh # scaffold + add every template + install + build (see below) -``` - -To run a single test: edit `spec/jasmine-runner.ts` to filter `spec_files`, then `npm run jasmine`. Tests are Jasmine specs under `spec/` (unit + acceptance) and within individual packages. - -### Template smoke test - -`scripts/smoke-test.sh` is the only check that a generated project actually installs and builds — -the Jasmine specs stub `PackageManager.installPackages`. Run it after touching anything under -`packages/cli/templates/` or `packages/igx-templates/`. Requires `npm run build` first. - -```bash -scripts/smoke-test.sh -f angular --templates grid,combo # fast path, ~2 min -scripts/smoke-test.sh # angular+react+webcomponents, ~20-30 min -``` - -Results in `output/smoke/results.tsv`, per-step logs in `output/smoke/logs/`. Two constraints when -editing it: every `ig` call must precede `npm install` (otherwise `bin/execute.js` delegates to the -project-local *published* CLI), and exit codes alone are not trustworthy (`Util.error` returns -without setting one) — steps are also judged on a log scan plus an artifact check. - -### MCP Server (`packages/igniteui-mcp/igniteui-doc-mcp`) - -Workspace package — part of yarn workspaces and lerna. Built automatically by `npm run build` via the `build:mcp` script. Has its own `tsconfig.json` (ES2022/ESM, separate from root CJS config). - -```bash -npm run build:mcp # build MCP server (also runs as part of npm run build) -cd packages/igniteui-mcp/igniteui-doc-mcp -npm run build:db # build SQLite DB from compressed docs (manual step) -npm run pipeline:angular # incremental doc pipeline (also: react, blazor, webcomponents) -npm run pipeline:angular:full # full rebuild pipeline -npm run inspector # test with MCP Inspector -``` - -## Architecture - -**Monorepo** managed with lerna + yarn workspaces. Five packages: - -| Package | npm | Path | -|---------|-----|------| -| CLI (entry point) | `igniteui-cli` | `packages/cli` | -| Core (shared logic) | `@igniteui/cli-core` | `packages/core` | -| Angular Templates | `@igniteui/angular-templates` | `packages/igx-templates` | -| Angular Schematics | `@igniteui/angular-schematics` | `packages/ng-schematics` | -| MCP Server | `@igniteui/mcp-server` | `packages/igniteui-mcp/igniteui-doc-mcp` | - -**Core is the dependency root** — changes to `packages/core` affect all other packages. Always rebuild the full monorepo after core changes. - -### Build System - -- TypeScript compiled with `tsc` directly (no bundler). Two configs: `tsconfig.json` (dev) and `tsconfig-pack.json` (production). -- `packages/core/types/Config.ts` is source of truth for config schema. Modifying it requires `npm run config-schema` (included in `npm run build`). -- Monorepo targets ES6/CommonJS. MCP server targets ES2022 with Node16 module resolution (`"type": "module"`). -- Path aliases in tsconfig: `@igniteui/cli-core` → `packages/core`, `@igniteui/angular-templates` → `packages/igx-templates`, etc. - -### Template System - -- Handlebars for template processing -- Templates in `packages/cli/templates/**/files/` and `packages/igx-templates/` -- Component/template IDs (e.g., "grid", "combo") used by `ig add` command -- Default framework is **jQuery** when not specified - -### MCP Server - -- `McpServer` from `@modelcontextprotocol/sdk` with stdio transport -- Two modes: **Remote** (default, proxies to `DOCS_BACKEND_URL`) and **Local** (`--local` flag, uses sql.js WASM with FTS4) -- Framework detection via component prefix: `Igx` → Angular, `Igr` → React, `Igc` → Web Components, `Igb` → Blazor -- DB build uses `better-sqlite3` (native, dev only); runtime uses `sql.js` (WASM) -- Doc compression uses OpenAI API (`OPENAI_API_KEY` in `.env`) -- Git submodules under `packages/igniteui-mcp/igniteui-doc-mcp/` provide documentation source repos -- Consult `packages/igniteui-mcp/igniteui-doc-mcp/docs/knowledgebase.md` before debugging pipeline problems - -## Key Conventions - -- **yarn for install, npm for scripts** -- CI runs on Node 20 with `yarn --frozen-lockfile` -- ESLint: `no-var`, `no-eval`, `prefer-const`, `no-console` (off in test files), `no-duplicate-imports` -- Test files match pattern `*[sS]pec.ts` — found in `spec/`, `packages/cli/migrations/`, `packages/igx-templates/`, `packages/ng-schematics/` +@AGENTS.md From 77b65c1c297d442c5b2d3d3e613533d3e9fdf219 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Mon, 12 Oct 2026 13:27:13 +0300 Subject: [PATCH 04/11] feat: add Ignite UI CLI skills for changelog, component templates, smoke testing, template anatomy, and version updates - Introduced `igniteui-cli-changelog` skill for drafting changelog sections based on merged PRs. - Added `igniteui-cli-component-template` skill to create component templates for Angular, React, and Web Components. - Created `igniteui-cli-smoke-test` skill to verify the installation and build process of generated projects. - Developed `igniteui-cli-template-anatomy` skill to outline the structure and version pinning of templates across frameworks. - Implemented `igniteui-cli-template-version-update` skill to manage and audit Ignite UI package versions in templates. --- {.claude => .agents}/README.md | 14 +++++++------- .../skills/igniteui-cli-build-test/SKILL.md | 0 .../skills/igniteui-cli-changelog/SKILL.md | 0 .../igniteui-cli-component-template/SKILL.md | 0 .../skills/igniteui-cli-smoke-test/SKILL.md | 0 .../skills/igniteui-cli-template-anatomy/SKILL.md | 0 .../igniteui-cli-template-version-update/SKILL.md | 0 AGENTS.md | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename {.claude => .agents}/README.md (84%) rename {.claude => .agents}/skills/igniteui-cli-build-test/SKILL.md (100%) rename {.claude => .agents}/skills/igniteui-cli-changelog/SKILL.md (100%) rename {.claude => .agents}/skills/igniteui-cli-component-template/SKILL.md (100%) rename {.claude => .agents}/skills/igniteui-cli-smoke-test/SKILL.md (100%) rename {.claude => .agents}/skills/igniteui-cli-template-anatomy/SKILL.md (100%) rename {.claude => .agents}/skills/igniteui-cli-template-version-update/SKILL.md (100%) diff --git a/.claude/README.md b/.agents/README.md similarity index 84% rename from .claude/README.md rename to .agents/README.md index e05afef32..c01ff9337 100644 --- a/.claude/README.md +++ b/.agents/README.md @@ -2,7 +2,7 @@ AI skills for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. -Each procedure is written once, as a skill in `.claude/skills/` ([Agent Skills](https://agentskills.io/specification) format), which Claude Code and VS Code Copilot both read. There are no custom agent files: you run a workflow by asking for the task, and the assistant loads the matching skill. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). +Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills](https://agentskills.io/specification) format), which VS Code Copilot discovers automatically: you run a workflow by asking for the task, and Copilot loads the matching skill. Claude Code reads skills only from `.claude/skills/`, so it does not discover these; in Claude Code, name the file (see Quick Start). There are no custom agent files. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). --- @@ -57,9 +57,9 @@ Each workflow runs on its own and does not call the others. You chain them. | Make sense of a failed smoke run | "triage output/smoke" | | Release notes for 15.9.0 | "draft the changelog for 15.9.0, 2026-10-15" | -The assistant picks the skill from its description. To be explicit, name it: in Claude Code, `/igniteui-cli-changelog draft 15.9.0, 2026-10-15`; in any tool, "Follow `.claude/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". +Copilot picks the skill from its description. To be explicit, or in any other assistant (including Claude Code), name the file: "Follow `.agents/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". -**Keeping the session small.** The update and template workflows produce long build and smoke output. In Claude Code you can run one in a subagent so that output stays out of your session: "run the igniteui-cli-template-version-update skill in a subagent: update webcomponents". Otherwise start a fresh session per workflow. +**Keeping the session small.** The update and template workflows produce long build and smoke output. In Claude Code you can run one in a subagent so that output stays out of your session: "in a subagent, follow `.agents/skills/igniteui-cli-template-version-update/SKILL.md`: update webcomponents". Otherwise start a fresh session per workflow. --- @@ -68,9 +68,9 @@ The assistant picks the skill from its description. To be explicit, name it: in ```text AGENTS.md ← shared conventions, incl. the rules for maintenance workflows CLAUDE.md ← @AGENTS.md, plus Claude-only notes if any -.claude/ +.agents/ README.md ← this guide - skills//SKILL.md ← every procedure and reference; read by Claude Code and Copilot + skills//SKILL.md ← every procedure and reference; discovered by Copilot; Claude Code reads them by path ``` --- @@ -93,10 +93,10 @@ Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" ru Add one only for a recurring, multi-step task. If an existing reference skill plus a plain request covers it, extend that skill instead. -1. Write `.claude/skills/igniteui-cli-/SKILL.md`: a one-line scope, inputs, "Limits" (only limits specific to this workflow, stated as what the workflow changes and what stops it; the shared ones, including the no-commit rule, stay in `AGENTS.md`), numbered steps with each stop condition at the step where it arises, "Follow-up Corrections" when the right fix differs from simply fixing and re-validating, final report and self-validation. Point to a reference skill at the step that uses it; put it under "Read first" only when most steps need it. +1. Write `.agents/skills/igniteui-cli-/SKILL.md`: a one-line scope, inputs, "Limits" (only limits specific to this workflow, stated as what the workflow changes and what stops it; the shared ones, including the no-commit rule, stay in `AGENTS.md`), numbered steps with each stop condition at the step where it arises, "Follow-up Corrections" when the right fix differs from simply fixing and re-validating, final report and self-validation. Point to a reference skill at the step that uses it; put it under "Read first" only when most steps need it. 2. Add it to the Skills and Quick Start tables above. ### Adding a Reference Skill -1. Write `.claude/skills//SKILL.md` (`name` must match the folder: lowercase letters, digits, hyphens). +1. Write `.agents/skills//SKILL.md` (`name` must match the folder: lowercase letters, digits, hyphens). 2. Add it to the Skills table above. diff --git a/.claude/skills/igniteui-cli-build-test/SKILL.md b/.agents/skills/igniteui-cli-build-test/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-build-test/SKILL.md rename to .agents/skills/igniteui-cli-build-test/SKILL.md diff --git a/.claude/skills/igniteui-cli-changelog/SKILL.md b/.agents/skills/igniteui-cli-changelog/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-changelog/SKILL.md rename to .agents/skills/igniteui-cli-changelog/SKILL.md diff --git a/.claude/skills/igniteui-cli-component-template/SKILL.md b/.agents/skills/igniteui-cli-component-template/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-component-template/SKILL.md rename to .agents/skills/igniteui-cli-component-template/SKILL.md diff --git a/.claude/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-smoke-test/SKILL.md rename to .agents/skills/igniteui-cli-smoke-test/SKILL.md diff --git a/.claude/skills/igniteui-cli-template-anatomy/SKILL.md b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-template-anatomy/SKILL.md rename to .agents/skills/igniteui-cli-template-anatomy/SKILL.md diff --git a/.claude/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md similarity index 100% rename from .claude/skills/igniteui-cli-template-version-update/SKILL.md rename to .agents/skills/igniteui-cli-template-version-update/SKILL.md diff --git a/AGENTS.md b/AGENTS.md index ccc329acd..33960a170 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,7 +77,7 @@ npm run inspector # test with MCP Inspector ## Maintenance Workflows -Template maintenance procedures are skills in `.claude/skills/`, read by both Claude Code and Copilot. Before adding or changing a workflow, read the guide in `.claude/README.md`. +Template maintenance procedures are skills in `.agents/skills/`. Before adding or changing a workflow, read the guide in `.agents/README.md`. These rules bind every run of a workflow skill, whether from the main session or a subagent. No tool enforces them. From 0dd281d3ba4739fb993435f1ea408e34f80462c3 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Mon, 12 Oct 2026 14:18:41 +0300 Subject: [PATCH 05/11] refactor: streamline skills documentation and remove obsolete build-test skill --- .agents/README.md | 14 ++--- .../skills/igniteui-cli-build-test/SKILL.md | 61 ------------------- .../igniteui-cli-component-template/SKILL.md | 4 +- .../skills/igniteui-cli-smoke-test/SKILL.md | 38 ++++++++++-- .../igniteui-cli-template-anatomy/SKILL.md | 3 +- .../SKILL.md | 4 +- AGENTS.md | 6 +- 7 files changed, 47 insertions(+), 83 deletions(-) delete mode 100644 .agents/skills/igniteui-cli-build-test/SKILL.md diff --git a/.agents/README.md b/.agents/README.md index c01ff9337..4348cd6bc 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -2,7 +2,7 @@ AI skills for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. -Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills](https://agentskills.io/specification) format), which VS Code Copilot discovers automatically: you run a workflow by asking for the task, and Copilot loads the matching skill. Claude Code reads skills only from `.claude/skills/`, so it does not discover these; in Claude Code, name the file (see Quick Start). There are no custom agent files. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). +Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills](https://agentskills.io/specification) format), which VS Code Copilot discovers automatically: you run a workflow by asking for the task, and Copilot loads the matching skill. There are no custom agent files. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). --- @@ -21,8 +21,7 @@ Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills]( | [`igniteui-cli-template-version-update`](./skills/igniteui-cli-template-version-update/SKILL.md) | Workflow | Template pins | Update Ignite UI versions that `ig new` / `ig add` scaffold (patch/minor only). In `dry-run` mode: a read-only report of version drift and peer conflicts | | [`igniteui-cli-component-template`](./skills/igniteui-cli-component-template/SKILL.md) | Workflow | New template folders | Add a component template for Angular, React and/or Web Components | | [`igniteui-cli-changelog`](./skills/igniteui-cli-changelog/SKILL.md) | Workflow | `CHANGELOG.md` | Draft or write the release section for a version cut | -| [`igniteui-cli-build-test`](./skills/igniteui-cli-build-test/SKILL.md) | Reference | — | Build, lint, Jasmine, running one spec file, specs that guard templates | -| [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Reference | — | Running the smoke test, checking kept projects, triaging failures ("Triaging a Run"), editing the script | +| [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Reference | — | Build, running one spec file, specs that guard templates, running the smoke test, checking kept projects, triaging failures ("Triaging a Run"), editing the script | | [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Reference | — | Template layout per framework, model templates, every place versions are pinned, parity auditing ("Auditing Parity Across Frameworks") | The workflows triage their own smoke runs; use the smoke-test skill directly for a run you started by hand. @@ -57,9 +56,9 @@ Each workflow runs on its own and does not call the others. You chain them. | Make sense of a failed smoke run | "triage output/smoke" | | Release notes for 15.9.0 | "draft the changelog for 15.9.0, 2026-10-15" | -Copilot picks the skill from its description. To be explicit, or in any other assistant (including Claude Code), name the file: "Follow `.agents/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". +Copilot picks the skill from its description. To be explicit, or in any other assistant, name the file: "Follow `.agents/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". -**Keeping the session small.** The update and template workflows produce long build and smoke output. In Claude Code you can run one in a subagent so that output stays out of your session: "in a subagent, follow `.agents/skills/igniteui-cli-template-version-update/SKILL.md`: update webcomponents". Otherwise start a fresh session per workflow. +**Keeping the session small.** The update and template workflows produce long build and smoke output. Start a fresh session per workflow. --- @@ -67,10 +66,9 @@ Copilot picks the skill from its description. To be explicit, or in any other as ```text AGENTS.md ← shared conventions, incl. the rules for maintenance workflows -CLAUDE.md ← @AGENTS.md, plus Claude-only notes if any .agents/ README.md ← this guide - skills//SKILL.md ← every procedure and reference; discovered by Copilot; Claude Code reads them by path + skills//SKILL.md ← every procedure and reference; discovered by Copilot ``` --- @@ -85,7 +83,7 @@ Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" ru - Skill frontmatter carries `name`, `description` and `license`. Keep the Skills table in line with them. - A skill's `description` is loaded in every session and decides when the skill is picked. Lead with the term people use for the task, keep the "WHEN TO USE / WHEN NOT TO USE" form, and name each trigger once; the procedure itself belongs in the body. -- Each fact lives in one skill. Workflows point to the reference skill (anatomy, smoke-test, build-test) at the step that needs it instead of restating it. When repository facts change (new framework root, new pin location, smoke-test flags), update that reference skill only. +- Each fact lives in one skill. Workflows point to the reference skill (anatomy, smoke-test) at the step that needs it instead of restating it. When repository facts change (new framework root, new pin location, smoke-test flags), update that reference skill only. - Boundaries shared by all workflows go in the "Maintenance Workflows" section of `AGENTS.md`, which is always loaded; skills do not link it. - `dry-run` mode is read-only by instruction only. diff --git a/.agents/skills/igniteui-cli-build-test/SKILL.md b/.agents/skills/igniteui-cli-build-test/SKILL.md deleted file mode 100644 index d5d1e7ebe..000000000 --- a/.agents/skills/igniteui-cli-build-test/SKILL.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: igniteui-cli-build-test -description: "Build, lint and Jasmine for the Ignite UI CLI monorepo: running one spec file, and which specs guard templates and version pins. WHEN TO USE: compiling after a change, running lint, running unit or template specs. WHEN NOT TO USE: checking that a generated project installs and builds (igniteui-cli-smoke-test)." -license: MIT ---- - -# Ignite UI CLI: Build, Lint, Test - -## Prerequisites - -- Dependencies are installed with **yarn** (`yarn install`), never npm. Scripts run with **npm**. -- Run `yarn install` or `yarn add` only when asked. Either rewrites `yarn.lock`, which agents leave untouched; a build that fails on dependencies is reported, not "fixed" by reinstalling. - -## Commands - -| Command | What it does | When | -|---|---|---| -| `npm run build` | `tsc` for the monorepo, config schema, MCP server build | After any `.ts` change, and before Jasmine or the smoke test | -| `npm run lint` | ESLint (flat config in `eslint.config.mjs`) over all `.ts`, **except** `packages/cli/templates/**` and every `**/files/**` (ignored in the config). React, Web Components and jQuery template code is therefore only linted inside a generated project | Before finishing any code change | -| `npm run jasmine` | All Jasmine specs, no lint/build/coverage | After `npm run build` | -| `npm run test` | lint + build + Jasmine with coverage | Full local CI equivalent; slow | - -Jasmine runs the **compiled** `.js` specs, so always `npm run build` first or the run tests stale code. - -A run that reports **INCOMPLETE** only because some specs are pending (`xit`/`pending()`) still exits 0 and counts as a pass. Any failed spec is a failure. - -## Running One Spec File - -Pass compiled spec files to the Jasmine CLI directly, after `npm run build`: - -```bash -npx jasmine spec/templates/react-spec.js -``` - -`npm run jasmine` ignores arguments and always runs every spec; `spec/jasmine-runner.ts` stays as it is. - -## Specs That Guard Templates - -| Spec | Guards | -|---|---| -| `spec/templates/angular-spec.ts` | Template ids exist; no file collisions between templates; every `igniteui-angular` pin in templates and project `package.json` equals `IGNITEUI_ANGULAR_PACKAGE` | -| `spec/templates/react-spec.ts` | Same, for all four `IGNITEUI_REACT_*` constants | -| `spec/templates/webcomponents-spec.ts` | Ids and collisions only. It does **not** check version pins against the constants | -| `spec/templates/template-contracts-spec.ts` | Every jQuery, React, Web Components and Blazor project and component template: matches its framework and project type, its template paths exist, it generates a config | -| `spec/unit/TemplateManager-spec.ts`, `spec/unit/list-spec.ts` | Template discovery and `ig list` output | -| `spec/acceptance/*` | `ig new`, `ig add`, `ig generate` end to end, with package install stubbed | - -The **template specs** for a framework run in about 15 seconds: - -```bash -npx jasmine spec/templates/angular-spec.js -npx jasmine spec/templates/react-spec.js spec/templates/template-contracts-spec.js -npx jasmine spec/templates/webcomponents-spec.js spec/templates/template-contracts-spec.js -``` - -A failing "no internal collisions" spec means two templates would write the same file path: give the new template a distinct `__path__`/file prefix layout and keep the spec as it is. - -## Related Skills - -- [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md): generated-project install and build -- [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md): template and version-pin layout diff --git a/.agents/skills/igniteui-cli-component-template/SKILL.md b/.agents/skills/igniteui-cli-component-template/SKILL.md index 34f29272c..0d36802a5 100644 --- a/.agents/skills/igniteui-cli-component-template/SKILL.md +++ b/.agents/skills/igniteui-cli-component-template/SKILL.md @@ -83,10 +83,10 @@ Per framework, create the component `index.ts`, a `default` variant `index.ts` a ### 5. Validate -Use the [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) and [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skills for the commands below. +Use the [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skill for the commands below. 1. `npm run build`. The smoke test and the specs run the compiled templates, so a stale build does not see your new template. -2. The template specs for each framework in scope (build-test, "Specs That Guard Templates"): ids, file collisions, template contracts. +2. The template specs for each framework in scope (smoke-test skill, "Specs That Guard Templates"): ids, file collisions, template contracts. 3. Angular only: `npx eslint packages/igx-templates/igx-ts//index.ts packages/igx-templates/igx-ts//default/index.ts`. React and Web Components `index.ts` files are ignored by the repository lint config; their code is linted in the kept project. 4. Per framework: `scripts/smoke-test.sh -f --templates --keep --out output/smoke--`. 5. In each kept project (`output/smoke--//`), run every check in smoke-test's "Checking a Kept Project" table for that framework. diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md index 844d5a45f..76a3f4772 100644 --- a/.agents/skills/igniteui-cli-smoke-test/SKILL.md +++ b/.agents/skills/igniteui-cli-smoke-test/SKILL.md @@ -1,14 +1,43 @@ --- name: igniteui-cli-smoke-test -description: "Smoke test (scripts/smoke-test.sh): scaffold with the local CLI, add templates, install, build; judging results, checking kept projects, triaging failures. WHEN TO USE: verifying template or version changes end to end, reading or triaging output/smoke results, editing scripts/smoke-test.sh. WHEN NOT TO USE: compiling the CLI or running Jasmine (igniteui-cli-build-test)." +description: "Validating template changes: build, the Jasmine template specs (running one spec file), and the smoke test (scripts/smoke-test.sh: scaffold with the local CLI, add templates, install, build); judging results, checking kept projects, triaging failures. WHEN TO USE: verifying template or version changes, running template specs, reading or triaging output/smoke results, editing scripts/smoke-test.sh. WHEN NOT TO USE: looking up template layout or where versions are pinned (igniteui-cli-template-anatomy)." license: MIT --- -# Ignite UI CLI: Template Smoke Test +# Ignite UI CLI: Build, Template Specs, Smoke Test -`scripts/smoke-test.sh` is the **only** check that a generated project actually installs and builds. Jasmine stubs `PackageManager.installPackages`, so a green Jasmine run says nothing about real npm resolution or TypeScript compilation of generated code. +`scripts/smoke-test.sh` is the **only** check that a generated project actually installs and builds. Jasmine stubs `PackageManager.installPackages`, so a green Jasmine run says nothing about real npm resolution or TypeScript compilation of generated code. Run the build and the template specs first: they are fast and catch id and pin mistakes before a slow smoke run. -## Running It +## Build and Template Specs + +- Dependencies are installed with **yarn**, scripts run with **npm**. Run `yarn install` or `yarn add` only when asked: either rewrites `yarn.lock`, which agents leave untouched. A build that fails on dependencies is reported, not "fixed" by reinstalling. +- `npm run build` after any `.ts` change. Jasmine and the smoke test run the **compiled** `.js`, so without a build they test stale code. +- `npm run lint` skips `packages/cli/templates/**` and every `**/files/**` (ignored in `eslint.config.mjs`). React, Web Components and jQuery template code is therefore only linted inside a generated project (see "Checking a Kept Project"). +- `npm run jasmine` ignores arguments and always runs every spec. To run one file, pass the compiled spec to the Jasmine CLI: `npx jasmine spec/templates/react-spec.js`. Leave `spec/jasmine-runner.ts` as it is. +- A run that reports **INCOMPLETE** only because some specs are pending (`xit`/`pending()`) still exits 0 and counts as a pass. Any failed spec is a failure. + +### Specs That Guard Templates + +| Spec | Guards | +|---|---| +| `spec/templates/angular-spec.ts` | Template ids exist; no file collisions between templates; every `igniteui-angular` pin in templates and project `package.json` equals `IGNITEUI_ANGULAR_PACKAGE` | +| `spec/templates/react-spec.ts` | Same, for all four `IGNITEUI_REACT_*` constants | +| `spec/templates/webcomponents-spec.ts` | Ids and collisions only. It does **not** check version pins against the constants | +| `spec/templates/template-contracts-spec.ts` | Every jQuery, React, Web Components and Blazor project and component template: matches its framework and project type, its template paths exist, it generates a config | +| `spec/unit/TemplateManager-spec.ts`, `spec/unit/list-spec.ts` | Template discovery and `ig list` output | +| `spec/acceptance/*` | `ig new`, `ig add`, `ig generate` end to end, with package install stubbed | + +The template specs for a framework run in about 15 seconds, after `npm run build`: + +```bash +npx jasmine spec/templates/angular-spec.js +npx jasmine spec/templates/react-spec.js spec/templates/template-contracts-spec.js +npx jasmine spec/templates/webcomponents-spec.js spec/templates/template-contracts-spec.js +``` + +A failing "no internal collisions" spec means two templates would write the same file path: give the new template a distinct `__path__`/file prefix layout and keep the spec as it is. + +## Running the Smoke Test Preconditions: `npm run build` has run (the script refuses otherwise); `node` and `npm` on PATH; network access to npm. @@ -124,5 +153,4 @@ End with the PASS/FAIL/SKIP totals, whether the run as a whole is trustworthy (n ## Related Skills -- [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) - [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) diff --git a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md index a1a29fd51..3ec20fd6f 100644 --- a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md +++ b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md @@ -1,6 +1,6 @@ --- name: igniteui-cli-template-anatomy -description: "Template anatomy for Angular (igx-ts), React (igr-ts) and Web Components (igc-ts): folder layout, index.ts fields, placeholders, test files, groups, and every place an Ignite UI version is pinned. WHEN TO USE: adding or changing a component template, locating version pins, auditing template parity across frameworks. WHEN NOT TO USE: running builds or the smoke test (igniteui-cli-build-test, igniteui-cli-smoke-test)." +description: "Template anatomy for Angular (igx-ts), React (igr-ts) and Web Components (igc-ts): folder layout, index.ts fields, placeholders, test files, groups, and every place an Ignite UI version is pinned. WHEN TO USE: adding or changing a component template, locating version pins, auditing template parity across frameworks. WHEN NOT TO USE: running builds, template specs or the smoke test (igniteui-cli-smoke-test)." license: MIT --- @@ -151,5 +151,4 @@ Hand actionable gaps to the [`igniteui-cli-component-template`](../igniteui-cli- - [`igniteui-cli-component-template`](../igniteui-cli-component-template/SKILL.md): workflow that creates a template using this layout - [`igniteui-cli-template-version-update`](../igniteui-cli-template-version-update/SKILL.md): workflow that updates the pins listed here -- [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) - [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) diff --git a/.agents/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md index 741b87677..25996149a 100644 --- a/.agents/skills/igniteui-cli-template-version-update/SKILL.md +++ b/.agents/skills/igniteui-cli-template-version-update/SKILL.md @@ -94,10 +94,10 @@ Search again for each **old** version string of every package you changed, over ### 6. Validate -Use the [`igniteui-cli-build-test`](../igniteui-cli-build-test/SKILL.md) and [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skills for the commands below. Lint is skipped: it does not check version strings. +Use the [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skill for the commands below. Lint is skipped: it does not check version strings. 1. `npm run build`. The smoke test and the specs run the compiled templates, so a stale build tests the old pins. If it fails, diagnose it before going further. -2. The template specs for each framework in scope (build-test, "Specs That Guard Templates"). For Angular and React they check every main-package pin against its constant. +2. The template specs for each framework in scope (smoke-test skill, "Specs That Guard Templates"). For Angular and React they check every main-package pin against its constant. 3. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: - The framework's main package changed (`igniteui-angular`, `igniteui-react`, `igniteui-webcomponents`), which nearly every template pulls in: `scripts/smoke-test.sh -f --out output/smoke-` (default project + every component template). - Otherwise, only the templates that pull in a changed package: search the framework's variant `index.ts` files for the package name or its constant, read each match's `this.id`, and run `scripts/smoke-test.sh -f --templates , --out output/smoke-`. For example, an `igniteui-dockmanager` update runs `--templates dock-manager`. The default project is always scaffolded, so a pin in its `package.json` is covered too. diff --git a/AGENTS.md b/AGENTS.md index 33960a170..5b940c955 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -Guidance for AI coding agents working in this repository. `CLAUDE.md` imports this file. +Guidance for AI coding agents working in this repository. ## Build, Test, Lint @@ -13,9 +13,9 @@ npm run lint # ESLint (flat config in eslint.config.mjs) scripts/smoke-test.sh # scaffold + add every template + install + build ``` -Tests are Jasmine specs under `spec/` (unit + acceptance) and within individual packages. Running one spec file and the specs that guard templates: `igniteui-cli-build-test` skill. +Tests are Jasmine specs under `spec/` (unit + acceptance) and within individual packages. -`scripts/smoke-test.sh` is the only check that a generated project actually installs and builds — the Jasmine specs stub `PackageManager.installPackages`. Run it after touching anything under `packages/cli/templates/` or `packages/igx-templates/`; running it, reading its results and editing the script: `igniteui-cli-smoke-test` skill. +`scripts/smoke-test.sh` is the only check that a generated project actually installs and builds — the Jasmine specs stub `PackageManager.installPackages`. Run it after touching anything under `packages/cli/templates/` or `packages/igx-templates/`. Running one spec file, the specs that guard templates, running the smoke test, reading its results and editing the script: `igniteui-cli-smoke-test` skill. ### MCP Server (`packages/igniteui-mcp/igniteui-doc-mcp`) From 143783d18daf2c108a464cc3c14cd376398158ff Mon Sep 17 00:00:00 2001 From: DKalinov Date: Mon, 12 Oct 2026 14:33:30 +0300 Subject: [PATCH 06/11] feat: enhance template version update process with new script for managing package pins --- .agents/README.md | 2 +- .../igniteui-cli-template-anatomy/SKILL.md | 2 +- .../SKILL.md | 41 ++- .../scripts/template-pins.mjs | 250 ++++++++++++++++++ 4 files changed, 286 insertions(+), 9 deletions(-) create mode 100644 .agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs diff --git a/.agents/README.md b/.agents/README.md index c01ff9337..d60db0b9f 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -85,7 +85,7 @@ Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" ru - Skill frontmatter carries `name`, `description` and `license`. Keep the Skills table in line with them. - A skill's `description` is loaded in every session and decides when the skill is picked. Lead with the term people use for the task, keep the "WHEN TO USE / WHEN NOT TO USE" form, and name each trigger once; the procedure itself belongs in the body. -- Each fact lives in one skill. Workflows point to the reference skill (anatomy, smoke-test, build-test) at the step that needs it instead of restating it. When repository facts change (new framework root, new pin location, smoke-test flags), update that reference skill only. +- Each fact lives in one skill. Workflows point to the reference skill (anatomy, smoke-test, build-test) at the step that needs it instead of restating it. When repository facts change (new framework root, new pin location, smoke-test flags), update that reference skill (and, for pin locations, `igniteui-cli-template-version-update/scripts/template-pins.mjs`, which encodes them). - Boundaries shared by all workflows go in the "Maintenance Workflows" section of `AGENTS.md`, which is always loaded; skills do not link it. - `dry-run` mode is read-only by instruction only. diff --git a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md index a1a29fd51..47aa10123 100644 --- a/.agents/skills/igniteui-cli-template-anatomy/SKILL.md +++ b/.agents/skills/igniteui-cli-template-anatomy/SKILL.md @@ -101,7 +101,7 @@ Run **both** search patterns (ripgrep) over `packages/cli/templates` and `packag this\.packages.*@[~^]?[0-9]+\.[0-9]+ ``` -The second catches pins whose package name is computed (template literals). Union the results. +The second catches pins whose package name is computed (template literals). Union the results. `igniteui-cli-template-version-update/scripts/template-pins.mjs` implements both searches; when a pin location changes here, update that script too. Exclude: compiled `*.js`/`*.js.map`, `node_modules`, `docs_baseline`, `ai-config/files/skills/**` (synced docs), and spec mocks such as `packages/ng-schematics/**/*_spec.ts`. diff --git a/.agents/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md index 741b87677..67e856d03 100644 --- a/.agents/skills/igniteui-cli-template-version-update/SKILL.md +++ b/.agents/skills/igniteui-cli-template-version-update/SKILL.md @@ -39,6 +39,33 @@ Your changes are Ignite UI version strings, applied as patch or minor updates. E --- +## Script + +Steps 2-5 are backed by [`scripts/template-pins.mjs`](./scripts/template-pins.mjs) (Node, uses the repo's `semver`; run `yarn install` first): + +```bash +node .agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs \ + [--framework angular,react,webcomponents|jquery|all] [--package ,] \ + [--target @]... [--apply] [--json] [--all-deps] +``` + +- Defaults: frameworks `angular,react,webcomponents`; Ignite UI packages only; target = npm `latest`. +- Without `--apply` it is read-only. `--apply` rewrites only `patch`/`minor` rows of Ignite UI packages, keeping each pin's prefix and the file's formatting; every other status is left alone. +- `--all-deps` adds non-Ignite UI packages to the report as observations. They are never applied. +- It reads the registry from `npm config get registry`. + +| Status | Meaning (Step 3) | +|---|---| +| `current` | The range already admits the target. `current*`: a `^` minimum that could be raised (Observation) | +| `patch` / `minor` | Proceeds; `new range` shows the result | +| `major` | Major or `0.x` minor change: stop item | +| `peer-stop` | Would proceed, but the target's peer is not met by a template pin after the update: stop item, reason in `new range` | +| `missing-target` | The target version is not published: stop item | +| `no-match` | No published version satisfies the current range: broken pin, report it first | +| `downgrade` / `error` | Target below the current minimum, or the registry lookup failed: stop item | + +The **Peer conflicts** section lists, per framework, every pin outside the peer range of an Ignite UI package's resolved `current` version or its update target (`after`). **Inconsistencies** lists different ranges, mixed prefixes and packages shared across frameworks. + ## Steps ### 1. Record the Starting Point @@ -47,7 +74,7 @@ Run `git status --short` and keep the output. Your final report must separate yo ### 2. Build the Inventory -Search for every Ignite UI pin in scope, using **both** patterns and the exclusions from [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) ("Where Ignite UI Versions Are Pinned"), over `packages/cli/templates` and `packages/igx-templates`. +Run the script for the frameworks in scope (add `--package` / `--target` from the input). It applies the search patterns and exclusions from [`igniteui-cli-template-anatomy`](../igniteui-cli-template-anatomy/SKILL.md) ("Where Ignite UI Versions Are Pinned") over `packages/cli/templates` and `packages/igx-templates`, and resolves computed names such as `` `${resolvePackage(NPM_DOCK_MANAGER)}@~2.1.1` `` through `packages/core/update/package-resolve.ts`. If something about the output looks wrong, run the anatomy searches by hand and compare. Produce a table, one row per occurrence: @@ -58,6 +85,8 @@ Include all three kinds of locations: `constants.ts`, `projects/*/files/package. ### 3. Resolve and Classify Targets +The script's output covers items 1-4 below (status, target, peer conflicts) and the drift notes. Review it against these rules rather than repeating the lookups; run `npm view` yourself only to confirm a surprising row. + For each distinct package: 1. Determine the target: the user's explicit version, else `npm view dist-tags.latest`. @@ -80,17 +109,15 @@ In `dry-run` mode, stop here. ### 4. Apply Edits -For each package that proceeds, update **every** occurrence from the inventory: +Rerun the script with the same arguments plus `--apply`. It updates every `patch`/`minor` occurrence (`constants.ts`, project `package.json` copies, hard-coded and computed `index.ts` pins) and prints each line it changed; a `FAILED` line means the pin was not found as recorded, so edit it by hand. -1. `constants.ts` first. -2. Every `projects/*/files/package.json` copy. -3. Every hard-coded `index.ts` pin. +An Ignite UI peer brought into range as part of the update (Step 3.4) is applied the same way: include it in `--package`, or give its version with `--target`. -Keep the existing range prefix. Keep formatting (tabs in `.ts`, two spaces in template `package.json`). +When editing by hand: keep the existing range prefix and the formatting (tabs in `.ts`, two spaces in template `package.json`). ### 5. Re-check -Search again for each **old** version string of every package you changed, over the same paths. Expect zero hits for that package. Any remaining hit is a missed pin: fix it, or report it if it is outside scope. +Rerun the script without `--apply`: every package you changed should now be `current`, with no new peer conflicts. Then search for each **old** version string of every package you changed, over the same paths. Expect zero hits for that package. Any remaining hit is a missed pin: fix it, or report it if it is outside scope. ### 6. Validate diff --git a/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs b/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs new file mode 100644 index 000000000..daf0b4e53 --- /dev/null +++ b/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs @@ -0,0 +1,250 @@ +#!/usr/bin/env node +// Inventory, classify and (with --apply) update the package pins in igniteui-cli templates. +// Usage: node template-pins.mjs [--framework angular,react,webcomponents|all] [--package a,b] +// [--target pkg@version]... [--all-deps] [--json] [--apply] +import { execSync } from "node:child_process"; +import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { join, relative, sep } from "node:path"; + +const root = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim(); +let semver; +try { + semver = createRequire(join(root, "package.json"))("semver"); +} catch { + console.error("semver not found in the repository's node_modules; run `yarn install` first."); + process.exit(1); +} + +const args = process.argv.slice(2); +const values = (name) => args.flatMap((a, i) => (a === name && args[i + 1] ? args[i + 1].split(",") : [])); +const flag = (name) => args.includes(name); + +const ROOTS = { + angular: "packages/igx-templates", + react: "packages/cli/templates/react", + webcomponents: "packages/cli/templates/webcomponents", + jquery: "packages/cli/templates/jquery" +}; +const fwArg = values("--framework"); +const frameworks = !fwArg.length ? ["angular", "react", "webcomponents"] : fwArg.includes("all") ? Object.keys(ROOTS) : fwArg; +const unknownFw = frameworks.filter((f) => !ROOTS[f]); +if (unknownFw.length) { + console.error(`Unknown framework: ${unknownFw.join(", ")}. Use ${Object.keys(ROOTS).join(", ")} or all (Blazor has no npm pins).`); + process.exit(1); +} +const onlyPackages = values("--package"); +const targets = Object.fromEntries(values("--target").map((t) => { + const at = t.lastIndexOf("@"); + return [t.slice(0, at), t.slice(at + 1)]; +})); +const allDeps = flag("--all-deps"); +const asJson = flag("--json"); +const apply = flag("--apply"); + +const isIgnite = (name) => /^(ignite-ui$|igniteui-|@infragistics\/|@igniteui\/)/.test(name) && name !== "igniteui-cli"; +const EXCLUDED = /(^|\/)(node_modules|docs_baseline)\/|\/ai-config\/files\/skills\//; +const SPEC = /([_-]spec|Spec)\.ts$/; + +const resolveConsts = Object.fromEntries( + [...readFileSync(join(root, "packages/core/update/package-resolve.ts"), "utf8").matchAll(/export const (\w+) = "([^"]+)"/g)] + .map((m) => [m[1], m[2]]) +); + +function walk(dir, out = []) { + for (const name of readdirSync(dir)) { + const p = join(dir, name); + if (statSync(p).isDirectory()) { + if (name !== "node_modules") walk(p, out); + } else out.push(p); + } + return out; +} + +const LITERAL = /(["'`])((?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*)@([~^]?\d[\w.+-]*)\1/g; +const COMPUTED = /`\$\{(?:resolvePackage\()?(\w+)\)?\}@([~^]?\d[\w.+-]*)`/g; +const pins = []; + +for (const fw of frameworks) { + for (const file of walk(join(root, ROOTS[fw]))) { + const rel = relative(root, file).split(sep).join("/"); + if (EXCLUDED.test(rel)) continue; + const inFiles = rel.includes("/files/"); + const lines = readFileSync(file, "utf8").split(/\r?\n/); + const add = (i, name, range, kind, token) => pins.push({ fw, file: rel, line: i + 1, name, range, kind, token }); + if (inFiles && rel.endsWith("/package.json")) { + let section = null; + lines.forEach((line, i) => { + const sec = line.match(/^\s*"(dependencies|devDependencies|peerDependencies)"\s*:/); + if (sec) return void (section = sec[1]); + if (/^\s*}/.test(line)) return void (section = null); + const m = section && line.match(/"([^"]+)"\s*:\s*"([~^]?\d[^"]*)"/); + if (m) add(i, m[1], m[2], "manifest", m[0]); + }); + } else if (!inFiles && rel.endsWith(".ts") && !SPEC.test(rel)) { + lines.forEach((line, i) => { + for (const m of line.matchAll(LITERAL)) add(i, m[2], m[3], "code", m[0]); + for (const m of line.matchAll(COMPUTED)) add(i, resolveConsts[m[1]] ?? `\${${m[1]}}`, m[2], "computed", m[0]); + }); + } + } +} + +const inScope = pins.filter((p) => (allDeps || isIgnite(p.name)) && (!onlyPackages.length || onlyPackages.includes(p.name))); + +let registry = "https://registry.npmjs.org/"; +try { + registry = execSync("npm config get registry", { encoding: "utf8" }).trim().replace(/\/?$/, "/"); +} catch { /* default registry */ } + +const cache = new Map(); +function fetchJson(path, abbreviated) { + if (!cache.has(path)) { + cache.set(path, fetch(registry + path, { + headers: abbreviated ? { accept: "application/vnd.npm.install-v1+json" } : {} + }).then((r) => (r.ok ? r.json() : { error: `HTTP ${r.status}` })).catch((e) => ({ error: e.message }))); + } + return cache.get(path); +} +const encode = (name) => name.replace("/", "%2F"); +async function packument(name) { + const body = await fetchJson(encode(name), true); + if (body.error) return body; + return { latest: body["dist-tags"]?.latest, versions: Object.keys(body.versions || {}) }; +} +async function peersOf(name, version) { + const body = await fetchJson(`${encode(name)}/${version}`, false); + return body.error ? {} : body.peerDependencies || {}; +} + +const names = [...new Set(inScope.map((p) => p.name))].sort(); +const meta = Object.fromEntries(await Promise.all(names.map(async (n) => [n, await packument(n)]))); + +const prefixOf = (range) => range.match(/^[~^]/)?.[0] ?? ""; +function classify(range, target, versions) { + const resolved = semver.maxSatisfying(versions, range); + if (!resolved) return { status: "no-match", resolved }; + if (semver.satisfies(target, range)) return { status: "current", resolved }; + const min = semver.minVersion(range); + if (semver.lt(target, min)) return { status: "downgrade", resolved }; + const breaking = semver.major(target) !== min.major || (min.major === 0 && semver.minor(target) !== min.minor); + return { status: breaking ? "major" : semver.diff(min, target) === "patch" ? "patch" : "minor", resolved }; +} + +for (const p of inScope) { + const m = meta[p.name]; + if (m.error || !m.latest) { + Object.assign(p, { status: "error", error: m.error ?? "no latest tag" }); + continue; + } + p.target = targets[p.name] ?? m.latest; + if (!m.versions.includes(p.target)) { + Object.assign(p, { status: "missing-target" }); + continue; + } + Object.assign(p, classify(p.range, p.target, m.versions)); + if (isIgnite(p.name) && (p.status === "patch" || p.status === "minor")) p.newRange = prefixOf(p.range) + p.target; + if (p.status === "current" && prefixOf(p.range) === "^" && semver.gt(p.target, semver.minVersion(p.range))) p.raisable = true; +} + +const updating = (p) => isIgnite(p.name) && (p.status === "patch" || p.status === "minor"); +const rangeAfter = (p) => (updating(p) ? p.newRange : p.range); + +const peerConflicts = []; +const igniteInScope = inScope.filter((p) => isIgnite(p.name) && p.resolved); +const checks = new Map(); +for (const p of igniteInScope) { + checks.set(`${p.fw}|${p.name}|${p.resolved}|current`, { fw: p.fw, name: p.name, version: p.resolved, when: "current" }); + if (updating(p)) checks.set(`${p.fw}|${p.name}|${p.target}|after`, { fw: p.fw, name: p.name, version: p.target, when: "after" }); +} +for (const c of checks.values()) { + const peers = await peersOf(c.name, c.version); + for (const [peer, peerRange] of Object.entries(peers)) { + for (const pin of pins.filter((x) => x.fw === c.fw && x.name === peer)) { + const pm = await packument(peer); + if (pm.error) continue; + const range = c.when === "after" ? rangeAfter(pin) : pin.range; + const resolved = semver.maxSatisfying(pm.versions, range); + if (resolved && semver.satisfies(resolved, peerRange, { includePrerelease: true })) continue; + peerConflicts.push({ ...c, peer, peerRange, pin: `${pin.file}:${pin.line}`, pinRange: range, resolved, ignitePeer: isIgnite(peer) }); + } + } +} +for (const c of peerConflicts.filter((x) => x.when === "after")) { + for (const p of inScope.filter((x) => x.fw === c.fw && x.name === c.name && updating(x))) { + p.status = "peer-stop"; + p.stopReason = `${c.peer} ${c.peerRange} not met by ${c.pin} (${c.pinRange})`; + delete p.newRange; + } +} + +const byName = {}; +for (const p of inScope) (byName[p.name] ??= []).push(p); +const inconsistencies = Object.entries(byName).flatMap(([name, list]) => { + const out = []; + const ranges = [...new Set(list.map((p) => p.range))]; + const prefixes = [...new Set(list.map((p) => prefixOf(p.range) || "exact"))]; + const fws = [...new Set(list.map((p) => p.fw))]; + if (ranges.length > 1) out.push({ name, issue: "different ranges", detail: ranges.join(", ") }); + if (prefixes.length > 1) out.push({ name, issue: "mixed prefixes", detail: prefixes.join(", ") }); + if (fws.length > 1) out.push({ name, issue: "shared across frameworks", detail: fws.join(", ") }); + return out; +}); + +const applied = []; +if (apply) { + const byFile = {}; + for (const p of inScope.filter(updating)) (byFile[p.file] ??= []).push(p); + for (const [file, list] of Object.entries(byFile)) { + const abs = join(root, file); + const text = readFileSync(abs, "utf8"); + const eol = text.includes("\r\n") ? "\r\n" : "\n"; + const lines = text.split(/\r?\n/); + for (const p of list) { + const at = p.token.lastIndexOf(p.range); + const next = p.token.slice(0, at) + p.newRange + p.token.slice(at + p.range.length); + const before = lines[p.line - 1]; + lines[p.line - 1] = before.replace(p.token, next); + applied.push({ file, line: p.line, name: p.name, from: p.range, to: p.newRange, ok: before !== lines[p.line - 1] }); + } + writeFileSync(abs, lines.join(eol)); + } +} + +const strip = ({ token, ...rest }) => rest; +if (asJson) { + console.log(JSON.stringify({ registry, frameworks, pins: inScope.map(strip), peerConflicts, inconsistencies, applied }, null, 2)); + process.exit(0); +} + +function table(head, rows) { + const w = head.map((h, i) => Math.max(h.length, ...rows.map((r) => String(r[i] ?? "").length))); + const fmt = (r) => r.map((c, i) => String(c ?? "").padEnd(w[i])).join(" ").trimEnd(); + console.log([fmt(head), w.map((n) => "-".repeat(n)).join(" "), ...rows.map(fmt)].join("\n")); +} + +console.log(`Frameworks: ${frameworks.join(", ")}${allDeps ? " (all dependencies; non-Ignite UI rows are report-only)" : ""}\n`); +console.log("Peer conflicts"); +if (peerConflicts.length) { + table(["fw", "package@version", "when", "peer", "peer range", "pin range", "resolves to", "pin location"], + peerConflicts.map((c) => [c.fw, `${c.name}@${c.version}`, c.when, c.peer, c.peerRange, c.pinRange, c.resolved ?? "nothing", c.pin])); +} else console.log(" none"); + +console.log("\nPins"); +table(["status", "package", "range", "target", "new range", "kind", "location"], + inScope.map((p) => [p.status + (p.raisable ? "*" : ""), p.name, p.range, p.target ?? p.error, p.newRange ?? p.stopReason ?? "", p.kind, `${p.file}:${p.line}`])); +if (inScope.some((p) => p.raisable)) console.log("* current, but the ^ minimum could be raised to the target"); + +if (inconsistencies.length) { + console.log("\nInconsistencies"); + table(["package", "issue", "detail"], inconsistencies.map((i) => [i.name, i.issue, i.detail])); +} + +if (apply) { + console.log("\nApplied"); + applied.forEach((a) => console.log(` ${a.ok ? "updated" : "FAILED "} ${a.file}:${a.line} ${a.name} ${a.from} -> ${a.to}`)); + if (!applied.length) console.log(" nothing to apply"); +} + +const counts = inScope.reduce((acc, p) => ((acc[p.status] = (acc[p.status] || 0) + 1), acc), {}); +console.log(`\n${inScope.length} pins, ${names.length} packages: ${Object.entries(counts).map(([k, v]) => `${k}=${v}`).join(" ")}`); From fd521d0ecc25e160b787d2837019435ca52ea953 Mon Sep 17 00:00:00 2001 From: DKalinov Date: Tue, 13 Oct 2026 06:55:52 +0300 Subject: [PATCH 07/11] docs: update SKILL.md and AGENTS.md for improved clarity on smoke testing and template processing --- .agents/skills/igniteui-cli-smoke-test/SKILL.md | 4 ++-- .agents/skills/igniteui-cli-template-version-update/SKILL.md | 2 +- AGENTS.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md index 76a3f4772..12ee603ce 100644 --- a/.agents/skills/igniteui-cli-smoke-test/SKILL.md +++ b/.agents/skills/igniteui-cli-smoke-test/SKILL.md @@ -51,9 +51,9 @@ Preconditions: `npm run build` has run (the script refuses otherwise); `node` an Defaults: angular, react, webcomponents; default project template; all component templates. jQuery and Blazor are opt-in (`-f jquery`, `-f blazor`): jQuery needs ProGet credentials for 13 templates and has no build; Blazor needs the .NET SDK and has no component templates. -`--all-projects` takes its list from `ig list`, so **hidden project templates** (`isHidden = true` in the project's `index.ts`, e.g. `base`, `side-nav-auth`, `ai-config`) are not scaffolded. Most reuse the `_base` / `_base_with_home` files; a hidden project with its own `files/package.json` (e.g. Angular `side-nav-auth`) is covered only by `scripts/smoke-test.sh -f -p `. +`--all-projects` takes its list from `ig list`, so **hidden project templates** (`isHidden = true` in the project's `index.ts`, e.g. `base`, `side-nav-auth`, `ai-config`) are not scaffolded. Most reuse the `_base` / `_base_with_home` files; a hidden project with its own `files/package.json` (e.g. Angular `side-nav-auth`) is covered only by `scripts/smoke-test.sh -f -p `. Hidden projects can also reuse another project's `files/` through `templatePaths` (Angular `side-nav-mini-auth` includes `side-nav-auth/files`, manifest included), so a change there needs a `-p` run for each project that pulls it in. -Runs longer than a few minutes: use a long command timeout, or run it in the background with output redirected to a file and wait until that file contains the final `Results:` line (the script prints it last). The script **wipes its `--out` directory at start**, so give each run its own directory (e.g. `--out output/smoke-react`, `--out output/smoke-react-projects`) when you need more than one run's results, and never run two at once into the same directory. +Runs longer than a few minutes: use a long command timeout, or run it in the background with output redirected to a file and wait for the process to exit. The `Results:` line is not the end: when nothing failed and `--keep` is not set, the script deletes the generated projects after printing it. The script **wipes its `--out` directory at start**, so give each run its own directory (e.g. `--out output/smoke-react`, `--out output/smoke-react-projects`) when you need more than one run's results, and never run two at once into the same directory. ## Results diff --git a/.agents/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md index 9c362cd16..8ec3fefc5 100644 --- a/.agents/skills/igniteui-cli-template-version-update/SKILL.md +++ b/.agents/skills/igniteui-cli-template-version-update/SKILL.md @@ -128,7 +128,7 @@ Use the [`igniteui-cli-smoke-test`](../igniteui-cli-smoke-test/SKILL.md) skill f 3. Smoke test, per framework in scope, each into its **own output directory** (a run wipes its `--out` at start, so a shared directory loses the earlier run's results). Scope the run to what changed: - The framework's main package changed (`igniteui-angular`, `igniteui-react`, `igniteui-webcomponents`), which nearly every template pulls in: `scripts/smoke-test.sh -f --out output/smoke-` (default project + every component template). - Otherwise, only the templates that pull in a changed package: search the framework's variant `index.ts` files for the package name or its constant, read each match's `this.id`, and run `scripts/smoke-test.sh -f --templates , --out output/smoke-`. For example, an `igniteui-dockmanager` update runs `--templates dock-manager`. The default project is always scaffolded, so a pin in its `package.json` is covered too. - - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. `--all-projects` skips hidden projects, so for each changed manifest under a hidden project (`isHidden = true` in its `index.ts`, e.g. Angular `side-nav-auth`) also run `scripts/smoke-test.sh -f -p --out output/smoke--`. If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), these runs alone cover it; skip the `--templates` run. + - Additionally, when any `projects/*/files/package.json` changed: `scripts/smoke-test.sh -f --all-projects --out output/smoke--projects`. `--all-projects` skips hidden projects, so for each changed manifest under a hidden project (`isHidden = true` in its `index.ts`, e.g. Angular `side-nav-auth`) also run `scripts/smoke-test.sh -f -p --out output/smoke--`, and the same for every other hidden project whose `templatePaths` includes that `files/` folder (Angular `side-nav-mini-auth` reuses `side-nav-auth/files`). If a changed package appears only in project manifests (no template pulls it in, e.g. `@igniteui/material-icons-extended`), these runs alone cover it; skip the `--templates` run. A scoped run takes about a minute; a full run takes several. Use a long timeout or background execution and wait for completion. Run them one at a time. Classify any failure with the smoke-test skill. Environment failures (network, credentials) are re-run once, then reported; versions stay as they are for those. diff --git a/AGENTS.md b/AGENTS.md index 5b940c955..7790434f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,7 +53,7 @@ npm run inspector # test with MCP Inspector ### Template System -- Handlebars for template processing +- Placeholders are substituted by the CLI, not a template engine: `<%=ClassName%>` style in Angular templates, `$(ClassName)` style in React and Web Components templates - Templates in `packages/cli/templates/**/files/` and `packages/igx-templates/` - Component/template IDs (e.g., "grid", "combo") used by `ig add` command - Default framework is **jQuery** when not specified From 1d87deaa90968dcc7846fb3e4ecc905086add969 Mon Sep 17 00:00:00 2001 From: DKalinov Date: Tue, 13 Oct 2026 07:26:51 +0300 Subject: [PATCH 08/11] feat: add ai-config skills refresh workflow to sync Ignite UI skills across frameworks --- .agents/README.md | 3 + .../SKILL.md | 113 +++++++++++ .../scripts/sync-skills.mjs | 184 ++++++++++++++++++ 3 files changed, 300 insertions(+) create mode 100644 .agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md create mode 100644 .agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs diff --git a/.agents/README.md b/.agents/README.md index 71a107f53..48f095e2a 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -21,6 +21,7 @@ Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills]( | [`igniteui-cli-template-version-update`](./skills/igniteui-cli-template-version-update/SKILL.md) | Workflow | Template pins | Update Ignite UI versions that `ig new` / `ig add` scaffold (patch/minor only). In `dry-run` mode: a read-only report of version drift and peer conflicts | | [`igniteui-cli-component-template`](./skills/igniteui-cli-component-template/SKILL.md) | Workflow | New template folders | Add a component template for Angular, React and/or Web Components | | [`igniteui-cli-changelog`](./skills/igniteui-cli-changelog/SKILL.md) | Workflow | `CHANGELOG.md` | Draft or write the release section for a version cut | +| [`igniteui-cli-ai-config-skills-refresh`](./skills/igniteui-cli-ai-config-skills-refresh/SKILL.md) | Workflow | Bundled `ai-config` skills | Mirror the framework repositories' `skills/` into the `ai-config` templates. In `dry-run` mode: what changed upstream and content issues | | [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Reference | — | Build, running one spec file, specs that guard templates, running the smoke test, checking kept projects, triaging failures ("Triaging a Run"), editing the script | | [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Reference | — | Template layout per framework, model templates, every place versions are pinned, parity auditing ("Auditing Parity Across Frameworks") | @@ -55,6 +56,8 @@ Each workflow runs on its own and does not call the others. You chain them. | See which templates are missing | "which component templates are missing per framework?" | | Make sense of a failed smoke run | "triage output/smoke" | | Release notes for 15.9.0 | "draft the changelog for 15.9.0, 2026-10-15" | +| Refresh the skills `ig ai-config` bundles | "refresh ai-config skills for all frameworks" | +| See how far bundled skills lag behind | "dry-run ai-config skills refresh, all frameworks" | Copilot picks the skill from its description. To be explicit, or in any other assistant, name the file: "Follow `.agents/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". diff --git a/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md b/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md new file mode 100644 index 000000000..a199f7504 --- /dev/null +++ b/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md @@ -0,0 +1,113 @@ +--- +name: igniteui-cli-ai-config-skills-refresh +description: "Refresh the Ignite UI AI skills bundled in the ai-config project templates (what ig ai-config copies when the project has no Ignite UI package with its own skills/) from the skills/ folders of the igniteui-angular, igniteui-react, igniteui-webcomponents and igniteui-blazor repositories. WHEN TO USE: syncing or checking the bundled ai-config skills; asking how far they lag behind a branch or tag (dry-run). WHEN NOT TO USE: this repository's own maintenance skills under .agents/skills; editing a skill's content (that belongs in the framework repository)." +license: MIT +--- + +# AI-Config Skills Refresh + +Mirrors each framework repository's `skills/` folder into the **bundled skills** of the `ai-config` project templates. `ig ai-config` copies these into a user's project when no installed Ignite UI package ships its own `skills/` (`resolveSkillsRoots` in `packages/core/util/ai-skills.ts`). + +| Framework | Source | Bundled copy | +|---|---|---| +| angular | `IgniteUI/igniteui-angular` | `packages/igx-templates/igx-ts/projects/ai-config/files/skills` | +| react | `IgniteUI/igniteui-react` | `packages/cli/templates/react/igr-ts/projects/ai-config/files/skills` | +| webcomponents | `IgniteUI/igniteui-webcomponents` | `packages/cli/templates/webcomponents/igc-ts/projects/ai-config/files/skills` | +| blazor | `IgniteUI/igniteui-blazor` | `packages/cli/templates/blazor/igb/projects/ai-config/files/skills` | + +The release build (`npm run build-pack`, run by `npm-publish.yml`) re-copies the same folders from `master` through `scripts/update-skills.ts`, so the published package always ships `master`'s skills. This workflow keeps the **committed** copies (used by local runs, specs and reviewers) in step with what ships, or pins them to another ref when asked. + +--- + +## Input + +- **Frameworks**: one or more of `angular`, `react`, `webcomponents`, `blazor`, or `all`. +- **Ref** (optional): branch, tag or commit in the source repositories. Default `master`, matching the release build. +- **Mode**: + - `update` (default): Steps 1-5. + - `dry-run`: Steps 1-2, then the dry-run report. No files change. + +If the frameworks are missing, stop and ask. A ref other than `master` is a deliberate choice: repeat it in the report, because the next release build still copies from `master`. + +--- + +## Limits + +- Your changes are the files under the bundled `skills/` folders in the table, nothing else. The `ai-config` templates' other files (`AGENTS.md`, instruction files, `index.ts`) stay as they are. +- The bundled copy is an exact mirror of the source `skills/` folder. Content problems (broken links, bad frontmatter) are reported for the framework repository to fix, not edited here. +- Do not run `scripts/update-skills.ts` or `npm run build:update-skills`: they `git fetch` and `git checkout -B` inside the documentation submodules, which writes to git and moves submodule pointers. Use the script below, which reads from GitHub and runs no git commands. + +--- + +## Script + +[`scripts/sync-skills.mjs`](./scripts/sync-skills.mjs) (Node, uses the `gh` CLI; requires `gh auth status` to pass): + +```bash +node .agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs \ + [--framework angular,react,webcomponents,blazor] [--ref ] [--apply] [--json] +``` + +- Resolves the ref to a commit, lists `skills/` through the GitHub tree API and compares each file's git blob hash with the bundled copy (CRLF in the working tree is ignored). Only changed or new files are downloaded. +- Prints `+` added, `~` modified, `-` deleted per framework, with the source commit. +- `!` lines are content issues in the result: a `SKILL.md` without frontmatter, `name` or `description`; a `name` that differs from its folder; relative Markdown links that point to a missing file or outside the `skills/` folder (they break once copied into a project). +- Without `--apply` nothing is written. `--apply` writes added and modified files, deletes removed ones and prunes empty folders. + +--- + +## Steps + +### 1. Record the Starting Point + +Run `git status --short` and keep the output, so the report separates your changes from what was already modified. + +### 2. Compare + +Run the script without `--apply` for the frameworks and ref in scope. A framework with an `ERROR` line (ref not found, no `skills/` folder, GitHub access) is a stop item for that framework; the others continue. + +In `dry-run` mode, stop here. + +### 3. Apply + +Run the script again with `--apply` and the same arguments. Then run it once more without `--apply`: every framework in scope should report `added=0 modified=0 deleted=0`. + +### 4. Validate + +1. `npm run build` if it has not run in this checkout. The spec runs compiled code. +2. `npx jasmine spec/unit/ai-config-spec.js`. +3. End to end, per framework in scope, in a new empty temporary directory **outside the repository** (no `package.json`, so the bundled skills are used): + ```bash + node /packages/cli/bin/execute.js ai-config --framework --agents claude --assistants none + ``` + It must report ` AI skill file(s) created or updated`, with `` equal to the number of files in that framework's bundled `skills/` folder, and `.claude/skills/` must contain them. Delete the temporary directory afterwards. + +The smoke test does not cover `ai-config` (hidden project), so it is not part of this workflow. + +### 5. Follow-up Corrections + +When the user asks for a different ref or framework set, rerun Steps 2-4 for the affected frameworks only. + +--- + +## Final Report + +1. **Per framework**: source repository, ref and commit (from the script), and the counts added / modified / deleted / unchanged. List renamed or removed skill folders explicitly; they change what users get. +2. **Content issues** (`!` lines), each as an item for the framework repository, with the file and the problem. +3. **Stopped**: frameworks with an error, and why. +4. **Validation**: spec result, and per framework the `ai-config` file count against the bundled folder's. +5. **Proposed CHANGELOG line**, e.g. + `* **chore(templates):** Updated the AI skills bundled with \`ig ai-config\` for Angular and Web Components.` +6. **Diff**: `git status --short` and `git diff --stat`, distinguishing pre-existing changes from yours. + +### Dry-Run Report + +Items 1-3 above, plus the date. State that nothing was written. + +## Final Self-Validation + +In `dry-run` mode only the first item applies, plus: `git status --short` is unchanged from Step 1. + +- [ ] No `git` write command was run, and `scripts/update-skills.ts` was not run. +- [ ] Only files under the bundled `skills/` folders in scope changed. +- [ ] A rerun of the script without `--apply` reports no changes for every framework in scope. +- [ ] The spec passed and each framework's `ai-config` run copied every bundled file. diff --git a/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs b/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs new file mode 100644 index 000000000..d40ae0870 --- /dev/null +++ b/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs @@ -0,0 +1,184 @@ +#!/usr/bin/env node +// Mirror skills/ from the Ignite UI framework repos into the ai-config project templates via the GitHub API (gh CLI). +// Read-only unless --apply. Never runs git. +// Usage: node sync-skills.mjs [--framework angular,react,webcomponents,blazor] [--ref ] [--apply] [--json] +import { execFile, execSync } from "node:child_process"; +import { createHash } from "node:crypto"; +import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, statSync, unlinkSync, writeFileSync } from "node:fs"; +import { dirname, join, posix, relative, sep } from "node:path"; +import { promisify } from "node:util"; + +const run = promisify(execFile); +const root = execSync("git rev-parse --show-toplevel", { encoding: "utf8" }).trim(); + +const SOURCES = { + angular: { repo: "IgniteUI/igniteui-angular", dest: "packages/igx-templates/igx-ts/projects/ai-config/files/skills" }, + react: { repo: "IgniteUI/igniteui-react", dest: "packages/cli/templates/react/igr-ts/projects/ai-config/files/skills" }, + webcomponents: { repo: "IgniteUI/igniteui-webcomponents", dest: "packages/cli/templates/webcomponents/igc-ts/projects/ai-config/files/skills" }, + blazor: { repo: "IgniteUI/igniteui-blazor", dest: "packages/cli/templates/blazor/igb/projects/ai-config/files/skills" } +}; + +const args = process.argv.slice(2); +const value = (name, def) => { + const i = args.indexOf(name); + return i > -1 && args[i + 1] && !args[i + 1].startsWith("--") ? args[i + 1] : def; +}; +const frameworks = value("--framework", Object.keys(SOURCES).join(",")).split(","); +const ref = value("--ref", "master"); +const apply = args.includes("--apply"); +const asJson = args.includes("--json"); + +const unknown = frameworks.filter((f) => !SOURCES[f]); +if (unknown.length) { + console.error(`Unknown framework: ${unknown.join(", ")}. Use ${Object.keys(SOURCES).join(", ")}.`); + process.exit(1); +} +if (!/^[\w.\-/]+$/.test(ref) || ref.startsWith("-")) { + console.error(`Invalid ref '${ref}'.`); + process.exit(1); +} + +async function gh(path) { + const { stdout } = await run("gh", ["api", path], { maxBuffer: 256 * 1024 * 1024 }); + return JSON.parse(stdout); +} + +const blobSha = (buf) => createHash("sha1").update(`blob ${buf.length}\0`).update(buf).digest("hex"); +function sameAsRemote(buf, sha) { + if (blobSha(buf) === sha) return true; + const lf = Buffer.from(buf.toString("binary").replace(/\r\n/g, "\n"), "binary"); + return lf.length !== buf.length && blobSha(lf) === sha; +} + +function walk(dir, base = dir, out = []) { + if (!existsSync(dir)) return out; + for (const name of readdirSync(dir)) { + const p = join(dir, name); + if (statSync(p).isDirectory()) walk(p, base, out); + else out.push(relative(base, p).split(sep).join("/")); + } + return out; +} + +function pruneEmptyDirs(dir, keep) { + for (const name of readdirSync(dir)) { + const p = join(dir, name); + if (statSync(p).isDirectory()) pruneEmptyDirs(p, false); + } + if (!keep && !readdirSync(dir).length) rmdirSync(dir); +} + +async function pool(items, size, fn) { + const queue = [...items]; + await Promise.all(Array.from({ length: size }, async () => { + while (queue.length) await fn(queue.shift()); + })); +} + +function validate(files) { + const issues = []; + const paths = new Set(Object.keys(files)); + for (const [path, buf] of Object.entries(files)) { + if (!path.endsWith(".md")) continue; + const text = buf.toString("utf8"); + if (posix.basename(path) === "SKILL.md") { + const fm = text.match(/^---\r?\n([\s\S]*?)\r?\n---/); + const folder = posix.basename(posix.dirname(path)); + const name = fm?.[1].match(/^name:\s*["']?([^"'\r\n]+?)["']?\s*$/m)?.[1]; + if (!fm) issues.push({ path, issue: "SKILL.md has no frontmatter" }); + else { + if (!name) issues.push({ path, issue: "frontmatter has no name" }); + else if (name !== folder) issues.push({ path, issue: `name '${name}' does not match folder '${folder}'` }); + if (!/^description:/m.test(fm[1])) issues.push({ path, issue: "frontmatter has no description" }); + } + } + const body = text.replace(/```[\s\S]*?```/g, ""); + for (const m of body.matchAll(/\]\(([^)\s#]+)(?:#[^)]*)?\)/g)) { + const target = m[1]; + if (/^[a-z]+:/i.test(target) || target.startsWith("/")) continue; + const resolved = posix.normalize(posix.join(posix.dirname(path), decodeURIComponent(target))).replace(/\/$/, ""); + if (resolved.startsWith("..")) issues.push({ path, issue: `link leaves the skills folder: ${target}` }); + else if (!paths.has(resolved) && ![...paths].some((p) => p.startsWith(resolved + "/"))) { + issues.push({ path, issue: `broken link: ${target}` }); + } + } + } + return issues; +} + +const results = []; +for (const fw of frameworks) { + const { repo, dest } = SOURCES[fw]; + const destAbs = join(root, dest); + const result = { framework: fw, repo, ref, dest, added: [], modified: [], deleted: [], unchanged: 0, issues: [] }; + results.push(result); + try { + result.commit = (await gh(`repos/${repo}/commits/${encodeURIComponent(ref)}`)).sha; + const tree = await gh(`repos/${repo}/git/trees/${result.commit}?recursive=1`); + if (tree.truncated) throw new Error("tree listing truncated by GitHub"); + const remote = Object.fromEntries(tree.tree + .filter((e) => e.type === "blob" && e.path.startsWith("skills/")) + .map((e) => [e.path.slice("skills/".length), e.sha])); + if (!Object.keys(remote).length) throw new Error(`no skills/ folder at ${ref}`); + + const local = new Set(walk(destAbs)); + const files = {}; + const fetchList = []; + for (const [path, sha] of Object.entries(remote)) { + const abs = join(destAbs, path); + if (local.has(path)) { + const buf = readFileSync(abs); + if (sameAsRemote(buf, sha)) { + result.unchanged++; + files[path] = buf; + continue; + } + result.modified.push(path); + } else result.added.push(path); + fetchList.push([path, sha]); + } + result.deleted = [...local].filter((p) => !(p in remote)).sort(); + await pool(fetchList, 6, async ([path, sha]) => { + const blob = await gh(`repos/${repo}/git/blobs/${sha}`); + files[path] = Buffer.from(blob.content, blob.encoding === "base64" ? "base64" : "utf8"); + }); + result.added.sort(); + result.modified.sort(); + result.issues = validate(files); + + if (apply) { + for (const path of [...result.added, ...result.modified]) { + const abs = join(destAbs, path); + mkdirSync(dirname(abs), { recursive: true }); + writeFileSync(abs, files[path]); + } + for (const path of result.deleted) unlinkSync(join(destAbs, path)); + if (existsSync(destAbs)) pruneEmptyDirs(destAbs, true); + result.applied = true; + } + } catch (e) { + result.error = (e.stderr || e.message || String(e)).toString().trim(); + } +} + +if (asJson) { + console.log(JSON.stringify(results, null, 2)); +} else { + for (const r of results) { + console.log(`\n== ${r.framework}: ${r.repo}@${r.ref}${r.commit ? ` (${r.commit.slice(0, 9)})` : ""} -> ${r.dest}`); + if (r.error) { + console.log(` ERROR: ${r.error}`); + continue; + } + const list = (label, items) => items.forEach((p) => console.log(` ${label} ${p}`)); + list("+", r.added); + list("~", r.modified); + list("-", r.deleted); + console.log(` added=${r.added.length} modified=${r.modified.length} deleted=${r.deleted.length} unchanged=${r.unchanged}${r.applied ? " (applied)" : ""}`); + r.issues.forEach((i) => console.log(` ! ${i.path}: ${i.issue}`)); + } + if (!apply && results.some((r) => r.added.length + r.modified.length + r.deleted.length)) { + console.log("\nDry run: no files written. Rerun with --apply to mirror these changes."); + } +} +process.exitCode = results.some((r) => r.error) ? 1 : 0; From b1d13e7a5c59900095b4498fcec394021116f750 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Tue, 13 Oct 2026 09:58:58 +0300 Subject: [PATCH 09/11] feat: add igniteui-cli-mcp-tool skill and call-tool script for MCP server interaction --- .agents/README.md | 6 +- .agents/skills/igniteui-cli-mcp-tool/SKILL.md | 174 ++++++++++++++++++ .../scripts/call-tool.mjs | 103 +++++++++++ .claude/agent-rules.md | 13 ++ AGENTS.md | 4 +- 5 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 .agents/skills/igniteui-cli-mcp-tool/SKILL.md create mode 100644 .agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs create mode 100644 .claude/agent-rules.md diff --git a/.agents/README.md b/.agents/README.md index 48f095e2a..e13e2322e 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -1,6 +1,6 @@ # Ignite UI CLI Skills Guide -AI skills for maintaining the Ignite UI CLI's **project and component templates**: what each one does, how they fit together, and how to maintain them. +AI skills for maintaining the Ignite UI CLI's **project and component templates** and the **MCP server's tools**: what each one does, how they fit together, and how to maintain them. Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills](https://agentskills.io/specification) format), which VS Code Copilot discovers automatically: you run a workflow by asking for the task, and Copilot loads the matching skill. There are no custom agent files. Shared conventions, including the rules every workflow follows, are in [`AGENTS.md`](../AGENTS.md). @@ -22,6 +22,7 @@ Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills]( | [`igniteui-cli-component-template`](./skills/igniteui-cli-component-template/SKILL.md) | Workflow | New template folders | Add a component template for Angular, React and/or Web Components | | [`igniteui-cli-changelog`](./skills/igniteui-cli-changelog/SKILL.md) | Workflow | `CHANGELOG.md` | Draft or write the release section for a version cut | | [`igniteui-cli-ai-config-skills-refresh`](./skills/igniteui-cli-ai-config-skills-refresh/SKILL.md) | Workflow | Bundled `ai-config` skills | Mirror the framework repositories' `skills/` into the `ai-config` templates. In `dry-run` mode: what changed upstream and content issues | +| [`igniteui-cli-mcp-tool`](./skills/igniteui-cli-mcp-tool/SKILL.md) | Workflow | MCP server `src/`, tests, tool docs | Add an MCP server tool or change an existing one. Its `scripts/call-tool.mjs` calls a tool on the built server over stdio | | [`igniteui-cli-smoke-test`](./skills/igniteui-cli-smoke-test/SKILL.md) | Reference | — | Build, running one spec file, specs that guard templates, running the smoke test, checking kept projects, triaging failures ("Triaging a Run"), editing the script | | [`igniteui-cli-template-anatomy`](./skills/igniteui-cli-template-anatomy/SKILL.md) | Reference | — | Template layout per framework, model templates, every place versions are pinned, parity auditing ("Auditing Parity Across Frameworks") | @@ -58,6 +59,8 @@ Each workflow runs on its own and does not call the others. You chain them. | Release notes for 15.9.0 | "draft the changelog for 15.9.0, 2026-10-15" | | Refresh the skills `ig ai-config` bundles | "refresh ai-config skills for all frameworks" | | See how far bundled skills lag behind | "dry-run ai-config skills refresh, all frameworks" | +| Add an MCP tool | "add an MCP tool that returns a component's theming variables" | +| Change an MCP tool | "let get_doc accept several doc names in one call" | Copilot picks the skill from its description. To be explicit, or in any other assistant, name the file: "Follow `.agents/skills/igniteui-cli-changelog/SKILL.md`: draft 15.9.0, 2026-10-15". @@ -72,6 +75,7 @@ AGENTS.md ← shared conventions, incl. the rules f .agents/ README.md ← this guide skills//SKILL.md ← every procedure and reference; discovered by Copilot + skills//scripts/ ← helper scripts a skill runs ``` --- diff --git a/.agents/skills/igniteui-cli-mcp-tool/SKILL.md b/.agents/skills/igniteui-cli-mcp-tool/SKILL.md new file mode 100644 index 000000000..952500f63 --- /dev/null +++ b/.agents/skills/igniteui-cli-mcp-tool/SKILL.md @@ -0,0 +1,174 @@ +--- +name: igniteui-cli-mcp-tool +description: "Add a tool to the Ignite UI MCP server (@igniteui/mcp-server) or change an existing one: input schema, handler, response text, description, tests and every doc that lists the tool. WHEN TO USE: adding, changing, renaming or removing an MCP tool or its parameters. WHEN NOT TO USE: the docs pipeline, database build or API-docs generation (the package's DEVELOPMENT.md)." +license: MIT +--- + +# MCP Tool + +Adds or changes a **tool** of the Ignite UI MCP server: `packages/igniteui-mcp/igniteui-doc-mcp`, published as `@igniteui/mcp-server` and started by `ig mcp`. Paths below are relative to that folder unless they start with `packages/`, `spec/` or `.agents/`. + +--- + +## Input + +- **Change**: a new tool, or the existing tool and what changes (a parameter, behavior, response text, description). +- For a new tool, also: the question it answers and the data behind it. + +If the change, or for a new tool its question and data, is missing, stop and ask. + +--- + +## Limits + +- Your changes are in `src/`, `spec/unit/mcp-runtime-spec.ts`, the docs in Step 6 and, for a doc tool, `../docs-backend/` and `../shared-fixtures/`. A new non-TypeScript asset under `src/tools/` (like `setup_*.md`) also needs its line in `scripts/build.ts`, the only thing that copies it into `dist/`; `constants.ts` reads those files at import, so a missing copy crashes the server at startup. +- Tool and parameter names are a public contract. Tool names are written into the AI skills `ig ai-config` copies from the upstream library repos (`scripts/update-skills.ts`), and `get_api_reference` with its `platform` / `component` / `member` parameters is baked into the docs database as `mcp:get_api_reference?…` links. For a rename or removal, stop and report every reference you find (`git grep -n ` from the repository root). +- The tool reads only data the package already ships: the `files` list in `package.json` is off limits. When the tool needs a new data file, stop and report. +- A new `DocsProvider` method needs a new endpoint in the .NET backend. Confirm that scope with the user before starting. +- `docs/*-api/` is tracked build output. When API tools load 0 entries, stop and ask before running `npm run build:docs:*`. +- `CHANGELOG.md` is written at release time: propose a line in your report. + +--- + +## Tool Anatomy + +| Part | Where | +|---|---| +| Registration | `src/index.ts`: `registerApiTools` (backed by `ApiDocLoader`) or `registerDocTools` (backed by `DocsProvider`) | +| Input schema | `src/tools/schemas.ts`: an exported `z.object` and its `z.infer` type | +| Handler | `src/tools/handlers.ts`: `createHandler(deps)` returning `(params) => Promise` | +| Description | `TOOL_DESCRIPTIONS.` in `src/tools/constants.ts` | +| Usage guide | `USAGE_GUIDE` in `src/tools/constants.ts`, served as the `igniteui-usage-guide` prompt | +| Server instructions | `instructions` in `src/index.ts`: rules that span tools only (framework detection, library boundary, `resolve_import` before imports) | +| Deprecated components | `src/config/deprecations.ts` | + +The doc tools (`list_components`, `get_doc`, `search_docs`, `get_project_setup_guide`) are registered inline in `src/index.ts` with helpers in `src/tools/doc-tools.ts`; change them in place. A new tool takes the schemas + handlers shape. `resolve_import` is the model: `git show --stat d765e6dbb` lists every file its addition touched. Its `package.json` script and new `data/` files are the parts this workflow leaves to a human (see "Limits"). + +A new tool's name is snake_case, verb first, in its family's vocabulary: `get_doc`, `search_docs`, `get_api_reference`, `search_api`, `resolve_import`. + +### Inputs + +- The framework parameter is `framework` on doc tools and `platform` on API tools; a new tool uses its family's name. Values come from `PLATFORMS` in `src/config/platforms.ts`. +- Every string parameter you add gets `.trim()`, `.min(1, '')`, `.max(, '')` and a `.describe()` with two or three concrete examples. The calling agent reads `.describe()` to fill the field and the validation message when it gets it wrong. Existing parameters keep their current validation unless the change is about them. +- Agents send near-miss input: an array serialized as a JSON string, `Component#member`, a pasted import statement, `"*"` meaning "everything". When the intent is recoverable, repair it, in a `z.preprocess` (see `extractSymbolNames`) or a hardening block at the top of the handler whose comment names the observed input, and test each shape. + +### Responses + +- Return `{ content: [{ type: "text", text }] }` with markdown text. +- Something requested does not exist: `isError: true`, and the text names the input and the tool to call next (`… not found in Angular. Use search_api to find available components.`). A search with no hits is an answer: plain text, no `isError`. +- Keep responses small. Offer narrowing parameters (`section`, `member`, `group`) and say in the description which one is cheapest. +- A tool whose input or output can name a component goes through deprecations. Answer a request for a deprecated component with `findDeprecation` + `formatDeprecationNotice`; append notices with `withDeprecationNotices(platform, text, scanned)`. `scanned` is the text whose mention of a deprecated component should trigger a notice: the caller's input by default. Whether to scan the output too is a per-tool choice (`get_doc` and `search_docs` do, `search_api` does not, because its excerpts mention deprecated types incidentally); the comment at each existing call site states the reason. Choose for your tool and leave that comment. +- A read-only tool registers `annotations: { readOnlyHint: true, openWorldHint: false }`. + +### Descriptions + +A `TOOL_DESCRIPTIONS` entry is how a calling agent picks the tool. Match the existing entries, one paragraph each: + +1. What it returns, in one sentence. +2. When to use it, and which sibling tool to use instead for the neighbouring cases. +3. What the response contains and how to narrow it. +4. Input limits and when it returns `isError`. + +### Doc Tools: Two Implementations + +`list_components`, `get_doc` and `search_docs` answer through `DocsProvider`: `LocalDocsProvider` (sql.js over the bundled database) or `RemoteDocsProvider` (HTTP to the .NET backend, `../docs-backend/docs-backend/Controllers/DocsController.cs`). `list_components` text is rendered by `src/tools/render-components.ts` and by its C# port `../docs-backend/docs-backend/ComponentRenderer.cs`, and `../shared-fixtures/list-components/*/expected.txt` pins both byte for byte. A change to what these tools return is made in both languages. + +--- + +## Steps + +### 1. Read the Neighbours + +Read the closest existing tool end to end: registration, schema, handler, description, tests. Find every tool whose description or `USAGE_GUIDE` entry routes to or away from this one; these change with it in Step 4. + +### 2. Schema and Handler + +Write the schema and handler to "Inputs" and "Responses". For a new tool, export both from `schemas.ts` / `handlers.ts` and register it in the matching `register*Tools` function with its `TOOL_DESCRIPTIONS` entry. + +For a doc tool whose output changes, mirror the change in the C# backend, then regenerate the fixtures and review the diff of every `expected.txt`: + +```bash +UPDATE_LIST_FIXTURES=1 npx vitest run src/__tests__/shared +``` + +### 3. Tests + +Vitest specs under `src/__tests__/`, with the loader or resolver faked by `vi.fn()` (`makeLoader` in `tools/handlers.test.ts`): + +- `tools/schemas.test.ts`: valid input, each rejection (empty, too long, unknown enum value), each repaired near-miss shape. +- `tools/handlers.test.ts`: success, not found (`isError` and the next-tool hint), empty result, deprecated component where it applies. +- An inline doc tool is tested through the registered handler with `McpServer` mocked, as in `index.get-doc.test.ts`. +- `spec/unit/mcp-runtime-spec.ts` (root Jasmine) imports the built `dist/`: update it when the behavior or an export it asserts changes. + +Done when every branch of the handler that returns differently has a test. + +### 4. Description, Usage Guide, Instructions + +Write or update the `TOOL_DESCRIPTIONS` entry (see "Descriptions"). Update the sibling descriptions and `USAGE_GUIDE` sections found in Step 1 so they route to the tool. Touch the server `instructions` only for a rule that spans tools. + +### 5. Validate + +PR CI (`nodejs.yml`) runs only root lint, root build and root Jasmine. The vitest suite and `dotnet test` run nowhere else, so this step is their only gate. + +From `packages/igniteui-mcp/igniteui-doc-mcp`: + +```bash +npm test # vitest +``` + +From the repository root: + +```bash +npm run lint +npm run build # includes the MCP server: tsc, then copies the database and setup_*.md into dist/ +npx jasmine spec/unit/mcp-runtime-spec.js # under a second; fails when run from another folder +``` + +For a doc tool: `dotnet test` in `../docs-backend/tests-docs-backend`. When `dotnet` is unavailable, the C# change is unverified: report it as such. + +Then call the tool on the built server the way a client does, from the repository root: + +```bash +node .agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs --list +node .agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs '' +``` + +The script starts the server in local mode, so a doc tool answers through `LocalDocsProvider` only; the remote path is verified by the shared fixtures and `dotnet test`. It prints the response text as the client receives it, after an `[isError]` line when set, and exits 1 on `isError`. Call a success case, a not-found case, an input the schema rejects and each repaired near-miss shape. Read each response as the calling agent would: a not-found response names the tool to call next, and a schema rejection names the parameter and what it needs (the SDK prefixes it with `Input validation error`; your `.min` / `.max` message is the rest). + +### 6. Documentation + +Add or update the tool in every listing: + +- `README.md`: Tools table +- `DEVELOPMENT.md`: Tools table, plus the tool's own section if it has one +- `CLAUDE.md`: registered tools list under Architecture, and its count +- `../Specification.md`: Tool Summary and Tool Definitions. Its `Version` / `Date` header stays as it is; mention in the report that it may need a bump. +- root `README.md`: MCP Server tools table + +These listings already disagree with each other. Change your tool's entry only, and list any other drift you notice in the report. + +Done when each of the five files has an entry for the tool that states its current parameters and behavior. + +### 7. Follow-up Corrections + +When the user rewords a description or response text, change the text and the tests that assert it, then rerun Step 5 for that tool. When doc-tool output changes again, regenerate the fixtures and update the C# port again. + +--- + +## Final Report + +- What the tool does now, with the `call-tool.mjs` output of one success and one failure. +- `git status --short` and `git diff --stat`. +- A proposed changelog line in the package's style: `* **feat(mcp):** .` +- `dotnet test` result, or "C# change unverified" with the reason (doc tools only). +- Doc drift noticed in Step 6, and whether `Specification.md` may need a version bump. + +## Final Self-Validation + +- [ ] No `git` write command was run. +- [ ] `package.json` and `server.json` are unchanged. +- [ ] Package `npm run build` and `npm test` pass; root `npm run lint`, `npm run build` and the MCP runtime spec pass. +- [ ] `call-tool.mjs --list` shows the tool with the intended input schema. +- [ ] Every not-found response from the handler names the tool to call next. +- [ ] Every listing in Step 6 states the tool's current parameters and behavior. +- [ ] For a doc tool: the C# backend and `shared-fixtures` match the TypeScript output, and `dotnet test` passed or the report says the C# change is unverified. diff --git a/.agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs b/.agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs new file mode 100644 index 000000000..f5056bef9 --- /dev/null +++ b/.agents/skills/igniteui-cli-mcp-tool/scripts/call-tool.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node +// Calls one tool on the built MCP server over stdio, as an MCP client would. +// node call-tool.mjs --list → every tool's name, description and input schema (JSON) +// node call-tool.mjs '' → the tool's text content as the client receives it, after an "[isError]" line when set +// Exits 0 on success, 1 on isError or a server failure, 2 on bad usage or arguments. +// Runs from any folder; build the server first. The server starts in local mode (bundled database). +import { spawn } from "child_process"; +import { dirname, resolve } from "path"; +import { fileURLToPath } from "url"; + +const packageDir = resolve(dirname(fileURLToPath(import.meta.url)), "../../../../packages/igniteui-mcp/igniteui-doc-mcp"); + +const [tool, rawArgs = "{}"] = process.argv.slice(2); +if (!tool) { + console.error("Usage: call-tool.mjs --list | ''"); + process.exit(2); +} + +let toolArgs; +if (tool !== "--list") { + try { + toolArgs = JSON.parse(rawArgs); + } catch (err) { + console.error(`Arguments are not valid JSON: ${err.message}`); + process.exit(2); + } +} + +// cwd is the package, so the server reads that package's .env as it does under `npm start`. +const server = spawn(process.execPath, ["dist/index.js"], { cwd: packageDir, stdio: ["pipe", "pipe", "inherit"] }); +const send = (msg) => server.stdin.write(JSON.stringify({ jsonrpc: "2.0", ...msg }) + "\n"); + +// Exit through exitCode, not process.exit(): on POSIX, stdout to a pipe is async and a large result would be cut off. +let finished = false; +const stop = (code) => { + finished = true; + clearTimeout(timer); + server.kill(); + process.exitCode = code; +}; + +// A server that dies on startup (missing database, bad import) never answers; its stderr above says why. +server.on("exit", (code, signal) => { + if (finished) return; + console.error(`Server exited before answering (code ${code}${signal ? `, signal ${signal}` : ""}).`); + stop(1); +}); + +let buffer = ""; +const pending = new Map(); +server.stdout.on("data", (chunk) => { + buffer += chunk; + let nl; + while ((nl = buffer.indexOf("\n")) >= 0) { + const line = buffer.slice(0, nl).trim(); + buffer = buffer.slice(nl + 1); + if (!line) continue; + const msg = JSON.parse(line); + pending.get(msg.id)?.(msg); + } +}); + +let nextId = 1; +const request = (method, params) => + new Promise((done) => { + const id = nextId++; + pending.set(id, done); + send({ id, method, params }); + }); + +const timer = setTimeout(() => { + console.error("Timed out after 60s waiting for the server."); + stop(1); +}, 60_000); + +const init = await request("initialize", { + protocolVersion: "2025-06-18", + capabilities: {}, + clientInfo: { name: "call-tool", version: "1.0.0" }, +}); +if (init.error) { + console.error(`initialize failed: ${JSON.stringify(init.error)}`); + stop(1); +} else { + send({ method: "notifications/initialized" }); + + const response = tool === "--list" + ? await request("tools/list", {}) + : await request("tools/call", { name: tool, arguments: toolArgs }); + + if (response.error) { + console.error(JSON.stringify(response.error, null, 2)); + stop(1); + } else if (tool === "--list") { + console.log(JSON.stringify(response.result, null, 2)); + stop(0); + } else { + const { content = [], isError } = response.result; + if (isError) console.log("[isError]"); + for (const item of content) console.log(item.type === "text" ? item.text : JSON.stringify(item, null, 2)); + stop(isError ? 1 : 0); + } +} diff --git a/.claude/agent-rules.md b/.claude/agent-rules.md new file mode 100644 index 000000000..766fffa7f --- /dev/null +++ b/.claude/agent-rules.md @@ -0,0 +1,13 @@ +# Working Rules for the Maintenance Agents + +These rules apply to every agent in [`agents/`](./agents/). The repository guide is +[`AGENTS.md`](../AGENTS.md) at the root; read it before making changes. This file only adds the boundaries that apply to agents. No tool enforces them; you are responsible for following them. + +- **Never commit.** Do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or open pull requests. If you changed files, leave them in the working tree and report the diff (`git status --short`, `git diff --stat`). A human reviews and commits. +- Temporary files (e.g. `npm pack` output for checking a package's type declarations) go in a temporary directory **outside the repository**. The one exception is `output/` (gitignored), which belongs to `scripts/smoke-test.sh`: smoke runs and commands inside a kept smoke project may write there. Do not edit files under `output/` directly. +- Do not edit compiled output (`*.js`, `*.js.map` next to a `.ts` source); edit the `.ts` and rebuild. +- Do not edit the repository's own manifests or lockfiles: root `package.json`, `packages/*/package.json`, `packages/igniteui-mcp/igniteui-doc-mcp/package.json` and `server.json`, `yarn.lock`, any `package-lock.json`. Project-template manifests under `**/files/package.json` are template content and may be edited when the task calls for it. +- Do not change the CLI's own version (`lerna`, `lerna.json`, `scripts/versionScript.ts`). +- Do not edit anything under `.git/`, `node_modules/` or `coverage/`. +- Do not read secrets: `.env` and `.env.*` (at any depth), `secrets/`, `*.pem`, `*.key`, `*.pfx`, `.npmrc`. +- When something is ambiguous or out of scope, stop and report instead of guessing. diff --git a/AGENTS.md b/AGENTS.md index 7790434f9..1c735e840 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,8 @@ npm run pipeline:angular:full # full rebuild pipeline npm run inspector # test with MCP Inspector ``` +Adding or changing an MCP tool (schema, handler, description, tests, every doc that lists the tool): `igniteui-cli-mcp-tool` skill. + ## Architecture **Monorepo** managed with lerna + yarn workspaces. Five packages: @@ -61,7 +63,7 @@ npm run inspector # test with MCP Inspector ### MCP Server - `McpServer` from `@modelcontextprotocol/sdk` with stdio transport -- Two modes: **Remote** (default, proxies to `DOCS_BACKEND_URL`) and **Local** (`--local` flag, uses sql.js WASM with FTS4) +- Two modes: **Local** (default, uses sql.js WASM with FTS4 over the bundled database) and **Remote** (`--remote `, proxies to the .NET docs backend; `IGNITEUI_MCP_DOCS_BACKEND_URL` supplies the URL but does not enable remote mode on its own) - Framework detection via component prefix: `Igx` → Angular, `Igr` → React, `Igc` → Web Components, `Igb` → Blazor - DB build uses `better-sqlite3` (native, dev only); runtime uses `sql.js` (WASM) - Doc compression uses OpenAI API (`OPENAI_API_KEY` in `.env`) From efb1b4eaf2da80e92b66d5a510ed1562625cd58a Mon Sep 17 00:00:00 2001 From: DKalinov Date: Tue, 13 Oct 2026 10:53:08 +0300 Subject: [PATCH 10/11] feat: update agent skills documentation and workflows for clarity and improved cloud agent integration --- .agents/README.md | 10 ++++-- .../SKILL.md | 6 ++-- .../scripts/sync-skills.mjs | 29 +++++++++++++--- .../skills/igniteui-cli-changelog/SKILL.md | 2 +- .../igniteui-cli-component-template/SKILL.md | 2 +- .agents/skills/igniteui-cli-mcp-tool/SKILL.md | 2 +- .../skills/igniteui-cli-smoke-test/SKILL.md | 2 ++ .../SKILL.md | 2 +- .github/workflows/copilot-setup-steps.yml | 34 +++++++++++++++++++ AGENTS.md | 10 ++++-- 10 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/copilot-setup-steps.yml diff --git a/.agents/README.md b/.agents/README.md index e13e2322e..ab7298bcf 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -8,7 +8,7 @@ Each procedure is written once, as a skill in `.agents/skills/` ([Agent Skills]( ## Ground Rules -- **Workflows never commit.** No `git add`, `commit`, `push`, branches or PRs. Every workflow ends with the working tree changed and a report; a human reviews and commits. +- **A human decides what gets merged.** Run locally, a workflow never commits: it ends with the working tree changed and a report, and a human reviews and commits. Run by a cloud agent on GitHub (e.g. Copilot cloud agent from an issue or the web UI), it commits to its own branch and opens a pull request whose description is the report; a human reviews and merges. The exact git rules are in `AGENTS.md`. - **The smoke test is the proof.** Jasmine stubs package installs, so template work is verified with `scripts/smoke-test.sh`. - **Stop and report rather than guess.** Major version updates, missing APIs, id conflicts and anything out of scope are reported, not worked around. @@ -82,7 +82,11 @@ AGENTS.md ← shared conventions, incl. the rules f ## Permissions -Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" rules in `AGENTS.md`, each workflow's "Limits") is an instruction the assistant follows, not a technical restriction. Review the diff before you commit. +Every limit on the workflows (the git rules, the "Maintenance Workflows" rules in `AGENTS.md`, each workflow's "Limits") is an instruction the assistant follows, not a technical restriction. Review the diff before you commit or merge the agent's pull request; branch protection on `master` is what actually keeps a cloud agent off it. + +### Cloud Agent + +Copilot cloud agent reads `AGENTS.md` and the skills in `.agents/skills/` on its own. Its environment comes from `.github/workflows/copilot-setup-steps.yml` (install, build, Playwright Chromium), which takes effect only once it is on `master`. The agent's firewall allows the npm and yarn registries and `raw.githubusercontent.com`. `igniteui-cli-ai-config-skills-refresh` also calls `api.github.com` (two requests per framework); if that host is blocked, add it to the repository's Copilot firewall allowlist. Sessions end after 59 minutes, so ask for one framework or a scoped smoke run per task. --- @@ -98,7 +102,7 @@ Every limit on the workflows (the no-commit rule, the "Maintenance Workflows" ru Add one only for a recurring, multi-step task. If an existing reference skill plus a plain request covers it, extend that skill instead. -1. Write `.agents/skills/igniteui-cli-/SKILL.md`: a one-line scope, inputs, "Limits" (only limits specific to this workflow, stated as what the workflow changes and what stops it; the shared ones, including the no-commit rule, stay in `AGENTS.md`), numbered steps with each stop condition at the step where it arises, "Follow-up Corrections" when the right fix differs from simply fixing and re-validating, final report and self-validation. Point to a reference skill at the step that uses it; put it under "Read first" only when most steps need it. +1. Write `.agents/skills/igniteui-cli-/SKILL.md`: a one-line scope, inputs, "Limits" (only limits specific to this workflow, stated as what the workflow changes and what stops it; the shared ones, including the git rules, stay in `AGENTS.md`), numbered steps with each stop condition at the step where it arises, "Follow-up Corrections" when the right fix differs from simply fixing and re-validating, final report and self-validation. Point to a reference skill at the step that uses it; put it under "Read first" only when most steps need it. 2. Add it to the Skills and Quick Start tables above. ### Adding a Reference Skill diff --git a/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md b/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md index a199f7504..50111bfae 100644 --- a/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md +++ b/.agents/skills/igniteui-cli-ai-config-skills-refresh/SKILL.md @@ -35,13 +35,13 @@ If the frameworks are missing, stop and ask. A ref other than `master` is a deli - Your changes are the files under the bundled `skills/` folders in the table, nothing else. The `ai-config` templates' other files (`AGENTS.md`, instruction files, `index.ts`) stay as they are. - The bundled copy is an exact mirror of the source `skills/` folder. Content problems (broken links, bad frontmatter) are reported for the framework repository to fix, not edited here. -- Do not run `scripts/update-skills.ts` or `npm run build:update-skills`: they `git fetch` and `git checkout -B` inside the documentation submodules, which writes to git and moves submodule pointers. Use the script below, which reads from GitHub and runs no git commands. +- Do not run `scripts/update-skills.ts` or `npm run build:update-skills`: they `git fetch` and `git checkout -B` inside the documentation submodules, which writes to git and moves submodule pointers. Use the script below, which reads from GitHub over HTTPS and runs no git commands. --- ## Script -[`scripts/sync-skills.mjs`](./scripts/sync-skills.mjs) (Node, uses the `gh` CLI; requires `gh auth status` to pass): +[`scripts/sync-skills.mjs`](./scripts/sync-skills.mjs) (Node 18+). It makes two `api.github.com` requests per framework (commit and file list), through `gh` when `gh auth status` passes, otherwise directly with `GH_TOKEN` / `GITHUB_TOKEN` if set (without a token GitHub allows 60 requests an hour). File contents come from `raw.githubusercontent.com` and are checked against the tree's hashes. In a cloud agent session, an `ERROR` naming `api.github.com` means the firewall blocks it: report that for every framework and stop. ```bash node .agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs \ @@ -107,7 +107,7 @@ Items 1-3 above, plus the date. State that nothing was written. In `dry-run` mode only the first item applies, plus: `git status --short` is unchanged from Step 1. -- [ ] No `git` write command was run, and `scripts/update-skills.ts` was not run. +- [ ] Git was used only as the `AGENTS.md` git rules allow for this session, and `scripts/update-skills.ts` was not run. - [ ] Only files under the bundled `skills/` folders in scope changed. - [ ] A rerun of the script without `--apply` reports no changes for every framework in scope. - [ ] The spec passed and each framework's `ai-config` run copied every bundled file. diff --git a/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs b/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs index d40ae0870..c786431cb 100644 --- a/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs +++ b/.agents/skills/igniteui-cli-ai-config-skills-refresh/scripts/sync-skills.mjs @@ -1,5 +1,6 @@ #!/usr/bin/env node -// Mirror skills/ from the Ignite UI framework repos into the ai-config project templates via the GitHub API (gh CLI). +// Mirror skills/ from the Ignite UI framework repos into the ai-config project templates. +// Commit and file list from api.github.com (gh CLI if logged in, else fetch with GH_TOKEN/GITHUB_TOKEN if set); file contents from raw.githubusercontent.com. // Read-only unless --apply. Never runs git. // Usage: node sync-skills.mjs [--framework angular,react,webcomponents,blazor] [--ref ] [--apply] [--json] import { execFile, execSync } from "node:child_process"; @@ -38,9 +39,26 @@ if (!/^[\w.\-/]+$/.test(ref) || ref.startsWith("-")) { process.exit(1); } +let useGh; async function gh(path) { - const { stdout } = await run("gh", ["api", path], { maxBuffer: 256 * 1024 * 1024 }); - return JSON.parse(stdout); + if (useGh === undefined) useGh = await run("gh", ["auth", "status"]).then(() => true, () => false); + if (useGh) { + const { stdout } = await run("gh", ["api", path], { maxBuffer: 256 * 1024 * 1024 }); + return JSON.parse(stdout); + } + const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN; + const res = await fetch(`https://api.github.com/${path}`, { + headers: { Accept: "application/vnd.github+json", ...(token && { Authorization: `Bearer ${token}` }) } + }); + if (!res.ok) throw new Error(`api.github.com/${path}: ${res.status} ${(await res.text()).slice(0, 200)}`); + return res.json(); +} + +async function raw(repo, commit, path) { + const url = `https://raw.githubusercontent.com/${repo}/${commit}/skills/${path.split("/").map(encodeURIComponent).join("/")}`; + const res = await fetch(url); + if (!res.ok) throw new Error(`${url}: ${res.status}`); + return Buffer.from(await res.arrayBuffer()); } const blobSha = (buf) => createHash("sha1").update(`blob ${buf.length}\0`).update(buf).digest("hex"); @@ -139,8 +157,9 @@ for (const fw of frameworks) { } result.deleted = [...local].filter((p) => !(p in remote)).sort(); await pool(fetchList, 6, async ([path, sha]) => { - const blob = await gh(`repos/${repo}/git/blobs/${sha}`); - files[path] = Buffer.from(blob.content, blob.encoding === "base64" ? "base64" : "utf8"); + const buf = await raw(repo, result.commit, path); + if (blobSha(buf) !== sha) throw new Error(`content of skills/${path} does not match its tree entry`); + files[path] = buf; }); result.added.sort(); result.modified.sort(); diff --git a/.agents/skills/igniteui-cli-changelog/SKILL.md b/.agents/skills/igniteui-cli-changelog/SKILL.md index dee74cfc3..1e84a154e 100644 --- a/.agents/skills/igniteui-cli-changelog/SKILL.md +++ b/.agents/skills/igniteui-cli-changelog/SKILL.md @@ -93,7 +93,7 @@ When the user corrects something after the report (a reworded bullet, a differen ## Final Self-Validation -- [ ] No `git` write command was run. +- [ ] Git was used only as the `AGENTS.md` git rules allow for this session. - [ ] In `draft` mode, no file changed; in `write` mode, only `CHANGELOG.md` changed and no existing section was altered. - [ ] Every PR in the range is either in a bullet, deliberately skipped (release cut, merge, `CHANGELOG.md`-only), or listed under "Needs human input". - [ ] Every bullet links its PR(s) and uses a normalized scope. diff --git a/.agents/skills/igniteui-cli-component-template/SKILL.md b/.agents/skills/igniteui-cli-component-template/SKILL.md index 0d36802a5..f06800403 100644 --- a/.agents/skills/igniteui-cli-component-template/SKILL.md +++ b/.agents/skills/igniteui-cli-component-template/SKILL.md @@ -108,7 +108,7 @@ Fix failures caused by your template. When a failure points elsewhere (environme ## Final Self-Validation -- [ ] No `git` write command was run. +- [ ] Git was used only as the `AGENTS.md` git rules allow for this session. - [ ] The id matches the same component's id in every other framework. - [ ] `controlGroup` and the component `group` match an existing `groups.json` key. - [ ] Every API used was verified against the pinned version. diff --git a/.agents/skills/igniteui-cli-mcp-tool/SKILL.md b/.agents/skills/igniteui-cli-mcp-tool/SKILL.md index 952500f63..c3604dae3 100644 --- a/.agents/skills/igniteui-cli-mcp-tool/SKILL.md +++ b/.agents/skills/igniteui-cli-mcp-tool/SKILL.md @@ -165,7 +165,7 @@ When the user rewords a description or response text, change the text and the te ## Final Self-Validation -- [ ] No `git` write command was run. +- [ ] Git was used only as the `AGENTS.md` git rules allow for this session. - [ ] `package.json` and `server.json` are unchanged. - [ ] Package `npm run build` and `npm test` pass; root `npm run lint`, `npm run build` and the MCP runtime spec pass. - [ ] `call-tool.mjs --list` shows the tool with the intended input schema. diff --git a/.agents/skills/igniteui-cli-smoke-test/SKILL.md b/.agents/skills/igniteui-cli-smoke-test/SKILL.md index 12ee603ce..fa9005388 100644 --- a/.agents/skills/igniteui-cli-smoke-test/SKILL.md +++ b/.agents/skills/igniteui-cli-smoke-test/SKILL.md @@ -55,6 +55,8 @@ Defaults: angular, react, webcomponents; default project template; all component Runs longer than a few minutes: use a long command timeout, or run it in the background with output redirected to a file and wait for the process to exit. The `Results:` line is not the end: when nothing failed and `--keep` is not set, the script deletes the generated projects after printing it. The script **wipes its `--out` directory at start**, so give each run its own directory (e.g. `--out output/smoke-react`, `--out output/smoke-react-projects`) when you need more than one run's results, and never run two at once into the same directory. +In a cloud agent session (time-limited, see `AGENTS.md`), the build is already done by the setup workflow unless you changed `.ts` sources. Scope each run with `-f ` and `--templates` / `-p`; a full run of all three frameworks can take most of the session. + ## Results - `output/smoke/results.tsv`: one row per step, columns `framework step template status seconds`. Steps: `new`, `add`, `install`, `build`. Status: `PASS`, `FAIL`, `SKIP`. diff --git a/.agents/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md index 8ec3fefc5..3267fc7ed 100644 --- a/.agents/skills/igniteui-cli-template-version-update/SKILL.md +++ b/.agents/skills/igniteui-cli-template-version-update/SKILL.md @@ -162,7 +162,7 @@ When the user corrects something after the report, fix that issue and re-run Ste In `dry-run` mode only the first item applies, plus: `git status --short` is unchanged from Step 1. -- [ ] No `git` write command was run. +- [ ] Git was used only as the `AGENTS.md` git rules allow for this session. - [ ] Every changed package has zero remaining occurrences of its old version in scope. - [ ] No major update was applied. - [ ] No range prefix changed. diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 000000000..5c33c1af5 --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,34 @@ +name: Copilot Setup Steps + +# Prepares the Copilot cloud agent environment. Takes effect only from the default branch. +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Use Node.js 24.x + uses: actions/setup-node@v6 + with: + node-version: 24.x + cache: 'yarn' + - name: Install packages + run: yarn --frozen-lockfile + - name: Build + run: npm run build + # Generated projects run their tests in Chromium; the browser download and OS packages are outside the agent firewall only here + - name: Install Playwright Chromium + run: npx -y playwright@1 install --with-deps chromium diff --git a/AGENTS.md b/AGENTS.md index 1c735e840..3336cce1e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,7 +73,7 @@ Adding or changing an MCP tool (schema, handler, description, tests, every doc t ## Key Conventions - **yarn for install, npm for scripts** -- CI runs on Node 20 with `yarn --frozen-lockfile` +- CI runs on Node 24.x and 26.x with `yarn --frozen-lockfile`; Copilot cloud agent sessions are prepared by `.github/workflows/copilot-setup-steps.yml` (Node 24, install, build, Playwright Chromium) - ESLint: `no-var`, `no-eval`, `prefer-const`, `no-console` (off in test files), `no-duplicate-imports` - Test files match pattern `*[sS]pec.ts` — found in `spec/`, `packages/cli/migrations/`, `packages/igx-templates/`, `packages/ng-schematics/` @@ -83,10 +83,14 @@ Template maintenance procedures are skills in `.agents/skills/`. Before adding o These rules bind every run of a workflow skill, whether from the main session or a subagent. No tool enforces them. -- **Never commit.** Leave changes in the working tree and report them with `git status --short` and `git diff --stat`; a human reviews and commits. Git stays read-only (`status`, `diff`, `log`, `show`): do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or any other git command that writes, and do not open pull requests. +- **Git** depends on where the workflow runs: + - **Local session** (editor or terminal, a person at the keyboard): never commit. Leave changes in the working tree and report them with `git status --short` and `git diff --stat`; a human reviews and commits. Git stays read-only (`status`, `diff`, `log`, `show`): do not run `git add`, `git commit`, `git push`, `git stash`, `git checkout -b`, `git branch`, or any other git command that writes, and do not open pull requests. Commit only when the user explicitly asks in that session. + - **Cloud agent session** (started on GitHub, e.g. Copilot cloud agent assigned an issue or asked from the web UI, expected to deliver a pull request): commit to the session's own working branch and open or update that session's pull request. Fill the PR description from the workflow's Final Report, using the sections of `.github/PULL_REQUEST_TEMPLATE.md`; the report's diff item becomes `git diff --stat ...HEAD`. Use the workflow's proposed CHANGELOG line, in conventional form, as the PR title and commit subject (e.g. `chore(angular): update scaffolded igniteui-angular to ~22.2.1`). A stop item that blocks the whole task leaves the PR as a draft with the stop items listed first; a reviewer answers in a PR comment. A `dry-run` or other read-only request commits nothing; its report is the session's answer (the PR description if the platform opened one). + - **Never, in either mode**: push to `master`, release branches or any branch other than the session's own; force-push, rebase or otherwise rewrite pushed history; merge, approve or close pull requests; create tags or releases; change other pull requests or issues; commit submodule pointer changes. - Temporary files (e.g. `npm pack` output for checking a package's type declarations) go in a temporary directory **outside the repository**. The one exception is `output/` (gitignored), which belongs to `scripts/smoke-test.sh`: smoke runs and commands inside a kept smoke project write there; files under `output/` change only through those runs. - Edit the `.ts` source and rebuild; compiled output (`*.js`, `*.js.map` next to a `.ts`) is never edited by hand. - Leave the repository's own manifests and lockfiles untouched: root `package.json`, `packages/*/package.json`, `packages/igniteui-mcp/igniteui-doc-mcp/package.json` and `server.json`, `yarn.lock`, any `package-lock.json`. Project-template manifests under `**/files/package.json` are template content and may be edited when the task calls for it. - Leave the CLI's own version alone: do not run `lerna`, and do not edit `lerna.json` or `scripts/versionScript.ts`. Leave anything under `.git/`, `coverage/` or any `node_modules/` untouched. - Leave secrets unread, at any depth: `.env`, `.env.*`, `.npmrc`, `secrets/`, `*.pem`, `*.key`, `*.pfx`. When a task seems to need one, stop and ask. -- When something is ambiguous or out of scope, stop and report instead of guessing. +- When something is ambiguous or out of scope, stop and report instead of guessing. In a cloud agent session, "stop and ask" means writing the question into the PR description and leaving the PR as a draft. +- Cloud agent sessions have a time limit (59 minutes for Copilot cloud agent) and a network firewall. Scope smoke runs to what changed (`--templates`, one framework per run) instead of the full matrix, and report a blocked host as an environment failure with the host name. From 9338fa1f54e3643e8eabd8ec6af32e7202228367 Mon Sep 17 00:00:00 2001 From: mstoyanova Date: Tue, 13 Oct 2026 15:55:27 +0300 Subject: [PATCH 11/11] feat: enhance template version update script with improved peer conflict handling and status reporting --- .../SKILL.md | 6 +- .../scripts/template-pins.mjs | 103 +++++++++++++----- 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/.agents/skills/igniteui-cli-template-version-update/SKILL.md b/.agents/skills/igniteui-cli-template-version-update/SKILL.md index 3267fc7ed..de38bd56f 100644 --- a/.agents/skills/igniteui-cli-template-version-update/SKILL.md +++ b/.agents/skills/igniteui-cli-template-version-update/SKILL.md @@ -60,11 +60,13 @@ node .agents/skills/igniteui-cli-template-version-update/scripts/template-pins.m | `patch` / `minor` | Proceeds; `new range` shows the result | | `major` | Major or `0.x` minor change: stop item | | `peer-stop` | Would proceed, but the target's peer is not met by a template pin after the update: stop item, reason in `new range` | +| `package-stop` | Would proceed, but another occurrence of the same package has a stop status (inconsistent pins): held with the rest of the package, reason in `new range` | +| `shared-skip` | Would proceed, but the package is pinned in all three frameworks and the run selects fewer without naming it (`--package` or `--target`): Observation, not applied | | `missing-target` | The target version is not published: stop item | | `no-match` | No published version satisfies the current range: broken pin, report it first | -| `downgrade` / `error` | Target below the current minimum, or the registry lookup failed: stop item | +| `downgrade` / `error` | Target below the current minimum, or a registry lookup failed (including the target's peer lookups, so its peers are unchecked): stop item | -The **Peer conflicts** section lists, per framework, every pin outside the peer range of an Ignite UI package's resolved `current` version or its update target (`after`). **Inconsistencies** lists different ranges, mixed prefixes and packages shared across frameworks. +The **Peer conflicts** section lists, per framework, every pin outside the peer range of an Ignite UI package's resolved `current` version or its update target (`after`). A failed peer lookup is listed there too, with the error under `resolves to`; for an `after` row the package becomes `error`. **Inconsistencies** lists different ranges, mixed prefixes and packages shared across frameworks. ## Steps diff --git a/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs b/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs index daf0b4e53..52026e1f5 100644 --- a/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs +++ b/.agents/skills/igniteui-cli-template-version-update/scripts/template-pins.mjs @@ -26,8 +26,9 @@ const ROOTS = { webcomponents: "packages/cli/templates/webcomponents", jquery: "packages/cli/templates/jquery" }; +const PRIMARY = ["angular", "react", "webcomponents"]; const fwArg = values("--framework"); -const frameworks = !fwArg.length ? ["angular", "react", "webcomponents"] : fwArg.includes("all") ? Object.keys(ROOTS) : fwArg; +const frameworks = !fwArg.length ? PRIMARY : fwArg.includes("all") ? Object.keys(ROOTS) : fwArg; const unknownFw = frameworks.filter((f) => !ROOTS[f]); if (unknownFw.length) { console.error(`Unknown framework: ${unknownFw.join(", ")}. Use ${Object.keys(ROOTS).join(", ")} or all (Blazor has no npm pins).`); @@ -65,7 +66,8 @@ const LITERAL = /(["'`])((?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*)@([~^]?\d[\w.+-] const COMPUTED = /`\$\{(?:resolvePackage\()?(\w+)\)?\}@([~^]?\d[\w.+-]*)`/g; const pins = []; -for (const fw of frameworks) { +// The primary roots are always scanned so packages shared by all of them are known, even when not selected. +for (const fw of new Set([...frameworks, ...PRIMARY])) { for (const file of walk(join(root, ROOTS[fw]))) { const rel = relative(root, file).split(sep).join("/"); if (EXCLUDED.test(rel)) continue; @@ -90,7 +92,8 @@ for (const fw of frameworks) { } } -const inScope = pins.filter((p) => (allDeps || isIgnite(p.name)) && (!onlyPackages.length || onlyPackages.includes(p.name))); +const inScope = pins.filter((p) => frameworks.includes(p.fw) && (allDeps || isIgnite(p.name)) && + (!onlyPackages.length || onlyPackages.includes(p.name))); let registry = "https://registry.npmjs.org/"; try { @@ -114,7 +117,7 @@ async function packument(name) { } async function peersOf(name, version) { const body = await fetchJson(`${encode(name)}/${version}`, false); - return body.error ? {} : body.peerDependencies || {}; + return body.error ? body : { peers: body.peerDependencies || {} }; } const names = [...new Set(inScope.map((p) => p.name))].sort(); @@ -147,36 +150,80 @@ for (const p of inScope) { if (p.status === "current" && prefixOf(p.range) === "^" && semver.gt(p.target, semver.minVersion(p.range))) p.raisable = true; } -const updating = (p) => isIgnite(p.name) && (p.status === "patch" || p.status === "minor"); +const proceeds = (p) => p.status === "patch" || p.status === "minor"; +const updating = (p) => isIgnite(p.name) && proceeds(p); const rangeAfter = (p) => (updating(p) ? p.newRange : p.range); +function stop(p, status, stopReason) { + Object.assign(p, { status, stopReason }); + delete p.newRange; +} + +// A package pinned in every primary framework is updated only when all of them are selected or it is named, +// so a single-framework run does not make the frameworks diverge. +const allPrimary = PRIMARY.every((fw) => frameworks.includes(fw)); +const named = (name) => onlyPackages.includes(name) || name in targets; +for (const p of inScope.filter(updating)) { + if (allPrimary || named(p.name) || !PRIMARY.every((fw) => pins.some((x) => x.fw === fw && x.name === p.name))) continue; + stop(p, "shared-skip", `pinned in ${PRIMARY.join(", ")}: select all three or name it`); +} -const peerConflicts = []; -const igniteInScope = inScope.filter((p) => isIgnite(p.name) && p.resolved); -const checks = new Map(); -for (const p of igniteInScope) { - checks.set(`${p.fw}|${p.name}|${p.resolved}|current`, { fw: p.fw, name: p.name, version: p.resolved, when: "current" }); - if (updating(p)) checks.set(`${p.fw}|${p.name}|${p.target}|after`, { fw: p.fw, name: p.name, version: p.target, when: "after" }); -} -for (const c of checks.values()) { - const peers = await peersOf(c.name, c.version); - for (const [peer, peerRange] of Object.entries(peers)) { - for (const pin of pins.filter((x) => x.fw === c.fw && x.name === peer)) { - const pm = await packument(peer); - if (pm.error) continue; - const range = c.when === "after" ? rangeAfter(pin) : pin.range; - const resolved = semver.maxSatisfying(pm.versions, range); - if (resolved && semver.satisfies(resolved, peerRange, { includePrerelease: true })) continue; - peerConflicts.push({ ...c, peer, peerRange, pin: `${pin.file}:${pin.line}`, pinRange: range, resolved, ignitePeer: isIgnite(peer) }); +async function findPeerConflicts() { + const found = []; + const checks = new Map(); + for (const p of inScope.filter((x) => isIgnite(x.name) && x.resolved)) { + checks.set(`${p.fw}|${p.name}|${p.resolved}|current`, { fw: p.fw, name: p.name, version: p.resolved, when: "current" }); + if (updating(p)) checks.set(`${p.fw}|${p.name}|${p.target}|after`, { fw: p.fw, name: p.name, version: p.target, when: "after" }); + } + // A failed lookup leaves the peers unchecked; it is reported as a conflict with an error so an update fails closed. + for (const c of checks.values()) { + const { peers, error } = await peersOf(c.name, c.version); + if (error) { + found.push({ ...c, error: `${c.name}@${c.version} lookup failed: ${error}` }); + continue; + } + for (const [peer, peerRange] of Object.entries(peers)) { + for (const pin of pins.filter((x) => x.fw === c.fw && x.name === peer)) { + const pm = await packument(peer); + const range = c.when === "after" ? rangeAfter(pin) : pin.range; + const at = { peer, peerRange, pin: `${pin.file}:${pin.line}`, pinRange: range, ignitePeer: isIgnite(peer) }; + if (pm.error) { + found.push({ ...c, ...at, error: `${peer} lookup failed: ${pm.error}` }); + continue; + } + const resolved = semver.maxSatisfying(pm.versions, range); + if (resolved && semver.satisfies(resolved, peerRange, { includePrerelease: true })) continue; + found.push({ ...c, ...at, resolved }); + } } } + return found; } -for (const c of peerConflicts.filter((x) => x.when === "after")) { - for (const p of inScope.filter((x) => x.fw === c.fw && x.name === c.name && updating(x))) { - p.status = "peer-stop"; - p.stopReason = `${c.peer} ${c.peerRange} not met by ${c.pin} (${c.pinRange})`; - delete p.newRange; + +// Stops apply to the whole package: one stopped occurrence holds every other occurrence of that package. +// Holding a pin changes the ranges other packages' peers are checked against, so repeat until nothing changes. +let peerConflicts; +const stopCauses = new Map(); +for (let changed = true; changed;) { + changed = false; + peerConflicts = await findPeerConflicts(); + for (const c of peerConflicts.filter((x) => x.when === "after")) { + stopCauses.set(JSON.stringify(c), c); + for (const p of inScope.filter((x) => x.fw === c.fw && x.name === c.name && updating(x))) { + if (c.error) stop(p, "error", `peers unchecked: ${c.error}`); + else stop(p, "peer-stop", `${c.peer} ${c.peerRange} not met by ${c.pin} (${c.pinRange})`); + changed = true; + } + } + const blockers = {}; + for (const p of inScope) if (!proceeds(p) && p.status !== "current") blockers[p.name] ??= p; + for (const p of inScope.filter((x) => updating(x) && blockers[x.name])) { + const b = blockers[p.name]; + stop(p, "package-stop", `${b.status} at ${b.file}:${b.line}`); + changed = true; } } +// The last pass has no "after" conflicts left; keep the ones that caused a stop. +peerConflicts = [...peerConflicts, ...stopCauses.values()]; const byName = {}; for (const p of inScope) (byName[p.name] ??= []).push(p); @@ -227,7 +274,7 @@ console.log(`Frameworks: ${frameworks.join(", ")}${allDeps ? " (all dependencies console.log("Peer conflicts"); if (peerConflicts.length) { table(["fw", "package@version", "when", "peer", "peer range", "pin range", "resolves to", "pin location"], - peerConflicts.map((c) => [c.fw, `${c.name}@${c.version}`, c.when, c.peer, c.peerRange, c.pinRange, c.resolved ?? "nothing", c.pin])); + peerConflicts.map((c) => [c.fw, `${c.name}@${c.version}`, c.when, c.peer, c.peerRange, c.pinRange, c.error ?? c.resolved ?? "nothing", c.pin])); } else console.log(" none"); console.log("\nPins");