diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f34712a..7f5ca426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to `cursor-rules` will be documented in this file. +All notable changes to `laravel-agent-skills` will be documented in this file. ## [Unreleased] @@ -38,7 +38,7 @@ All notable changes to `cursor-rules` will be documented in this file. - πŸ“ **Changed**: README and `composer.json` package metadata reposition the project explicitly as a PHP/Laravel Composer plugin (installs Cursor/Claude/Codex rules + agent skills), not a generic AI-rules bundle (#662). -- πŸ—‘οΈ **Removed**: bundled launcher `cursor-rules-resolve-loop.sh` and its Composer `bin` registration β€” no longer needed. The script, the README *Autonomous issue loop* section, and the `agents/daidalos.md` / `docs/agents.md` references to it are dropped. Consumers that invoked `vendor/bin/cursor-rules-resolve-loop.sh` should drive the loop directly via `claude --permission-mode auto "/loop …"` instead. +- πŸ—‘οΈ **Removed**: bundled launcher `agent-skills-resolve-loop.sh` and its Composer `bin` registration β€” no longer needed. The script, the README *Autonomous issue loop* section, and the `agents/daidalos.md` / `docs/agents.md` references to it are dropped. Consumers that invoked `vendor/bin/agent-skills-resolve-loop.sh` should drive the loop directly via `claude --permission-mode auto "/loop …"` instead. - πŸ› **Fixed**: JIRA `skills/code-review-jira/scripts/load-issue.sh` now correctly unwraps a Java/Groovy `toString` custom field whose `json={…}` payload is followed by trailing content (the outer map's closing brace). `tryParseTrimEnd` fed an invalid candidate to `fromjson?`, whose suppressed error yields `empty` in jq, and `.parsed = (empty)` collapsed the whole `until` iteration to `empty` β€” so the fallback returned `null` on the first non-parsing trim instead of trimming down to the valid JSON prefix. As a result `devSummary` (PR / branch / commit counts from the dev panel) silently degraded to `null` for every such field. Coalescing `fromjson?` to `null` keeps the iteration alive until it trims to a parseable prefix. @@ -94,22 +94,22 @@ All notable changes to `cursor-rules` will be documented in this file. - πŸ“ **Changed**: `rules/php/dependency-selection.mdc` is now also referenced from `code-review/SKILL.md` and `security-review/SKILL.md` so the *Code Review Application* contract (missing selection note = Critical finding, archived / abandoned / branch-pinned adoption = Critical finding on the spot, single-maintainer adoption without bus-factor flag = Moderate finding) is actually walked during the technical PR review. Without these Apply lines the rule existed on paper but the CR Strict-rule-compliance loop never enforced it because it only scans rules the skill itself Applies. The rule's Compatibility gate bullet 4 now also distinguishes *red CI* (disqualifies the candidate) from *no CI configured at all* (does not disqualify but downgrades the Test-surface scoring signal to zero and must be recorded in the selection note as a quality risk). A concrete rendered example of the selection note (Laravel project picking `spatie/laravel-data`) was added under the placeholder template so agents can match the contract on first try. Closes the CR follow-up to PR #515. - ✨ **Added**: new shared rule `rules/php/dependency-selection.mdc` codifies how every skill must choose a Composer dependency before recommending one. The rule defines an **Activity gate** (last commit + last release ≀ 12 months, not archived on GitHub, not abandoned on Packagist, at least one Composer-installable tagged release, responsive issue tracker), a **Compatibility gate** (PHP constraint, framework constraint, OSI-approved license, green CI on the default branch), and a **Selection process** that enumerates 2–3 candidates, tabulates the gate results, scores survivors on adoption / docs / tests / maintainer diversity / type-safety, and recommends the highest-scoring survivor with a documented audit trail. When **no** candidate passes the gates, the rule mandates a blocker report to the user instead of silently relaxing the criteria β€” agents do not adopt inactive libraries. Code Review Application section escalates a missing selection note to a **Critical** finding and any adoption of an archived / abandoned / branch-pinned package to a **Critical** finding on the spot. The rule is referenced from `resolve-issue`, `class-refactoring`, `composer-update`, and `security-threat-analysis` β€” every flow where the agent currently proposes a new Composer dependency. Per user request β€” frequently-updated-and-active library selection. - πŸ“ **Changed**: every code-review skill (`code-review-github`, `code-review-jira`, `pr-summary`, `process-code-review`) now publishes its tracker comments through a **single-comment upsert helper** keyed by the current actor identity **and** a marker namespace, so follow-up CR runs **edit one comment in place** instead of stacking new comments on the PR, the linked GitHub issue, or the JIRA ticket. New `skills/code-review-github/scripts/upsert-comment.sh []` resolves the GitHub login via `gh api user --jq .login`, appends the hidden marker `` (default `cr-comment`) to the body, lists existing comments with `gh api --paginate` (`jq -s` slurp across pages so the marker lookup is global, not page-local), and either PATCHes the marker-carrying comment or POSTs a fresh one. Companion `skills/code-review-jira/scripts/upsert-comment.sh []` resolves the acli actor, **appends** the invisible anchor `{anchor:-actor-}` at the bottom of the body (top-of-body prepend rendered an empty paragraph above `*Authors:*`), lists comments, and either edits the existing one (via `acli jira workitem comment edit`) or adds a new one β€” exit code 4 signals the calling skill to fall back to the JIRA MCP server (`editJiraIssue` / `addCommentToJiraIssue`) on acli builds that lack comment-edit support. CR wrappers leave `MARKER_KEY` at the default; `process-code-review` passes `cr-status` so its resolved-items follow-up owns a **second** per-actor slot (alongside the CR comment) that converge loops edit in place, replacing the previous *"Replying to code review from {date}"* quoting recipe. The `Previous CR Status` template section is dropped (history is preserved by the tracker's edit history on the upserted comment) and replaced by a `Last updated` header line. Pre-existing CR comments published before this convention are left untouched β€” the first marker-scoped run creates a clean comment alongside them, no migration / deletion is performed. `gh api` body payloads use `-f` (string field) instead of `-F` (typed inference) so markdown bodies that happen to start with `true` / `false` / a number stay strings; API error envelopes are no longer redirected to `/dev/null` so failures land in the calling skill's logs. Tracking: per user request β€” single-comment-per-actor consolidation. -- πŸ› **Fixed**: `bin/test-coverage-diff` is now exposed as `vendor/bin/test-coverage-diff` via the package's `bin` array in `composer.json`, so any consuming project gets the diff-scoped coverage runner immediately after `composer require pekral/cursor-rules`. The bash script also resolves its companion `coverage-diff-check.php` via the script's own physical location (with `readlink`-aware symlink chain), so the runner works correctly when invoked through Composer's `vendor/bin` symlink rather than only from the package root. CR skills (`code-review`, `code-review-github`, `code-review-jira`, `process-code-review`, `create-missing-tests-in-pr`, `create-test`) and `@rules/php/core-standards.mdc` Coverage gate updated to put `vendor/bin/test-coverage-diff` first in the discovery order and to forbid creating a new `composer test:coverage:diff` script in a consuming project. Closes #488 -- ✨ **Added**: new opt-in installer flag `--allow-bundled-scripts` that whitelists this package's bundled scripts in `~/.claude/settings.json` so Claude Code stops asking for confirmation on every run. The flag is no-op unless `--editor=claude` or `--editor=all` is set and a `HOME` / `USERPROFILE` is resolvable. Scope is narrow per Keesan12's *trusted-script provenance* observation on the issue thread β€” only the two `load-issue.sh` loaders shipped under `skills/code-review-{github,jira}/scripts/` are added, never a blanket `bin/` or `*` allow. New `Pekral\CursorRules\InstallerClaudeSettings` class encapsulates the read / merge / write flow: existing entries in `permissions.allow` are preserved, non-string entries are dropped, the merge is idempotent (re-running the installer reports `0` newly allowed permissions), unrelated keys (`theme`, `permissions.deny`, etc.) survive untouched, malformed JSON raises a clear `InstallerFailure`. Help text and `README.md` *CLI Switches* table document the flag. 17 new Pest assertions cover the merge / idempotency / failure / no-op paths. Closes #487 +- πŸ› **Fixed**: `bin/test-coverage-diff` is now exposed as `vendor/bin/test-coverage-diff` via the package's `bin` array in `composer.json`, so any consuming project gets the diff-scoped coverage runner immediately after `composer require agentic-vibes/laravel-agent-skills`. The bash script also resolves its companion `coverage-diff-check.php` via the script's own physical location (with `readlink`-aware symlink chain), so the runner works correctly when invoked through Composer's `vendor/bin` symlink rather than only from the package root. CR skills (`code-review`, `code-review-github`, `code-review-jira`, `process-code-review`, `create-missing-tests-in-pr`, `create-test`) and `@rules/php/core-standards.mdc` Coverage gate updated to put `vendor/bin/test-coverage-diff` first in the discovery order and to forbid creating a new `composer test:coverage:diff` script in a consuming project. Closes #488 +- ✨ **Added**: new opt-in installer flag `--allow-bundled-scripts` that whitelists this package's bundled scripts in `~/.claude/settings.json` so Claude Code stops asking for confirmation on every run. The flag is no-op unless `--editor=claude` or `--editor=all` is set and a `HOME` / `USERPROFILE` is resolvable. Scope is narrow per Keesan12's *trusted-script provenance* observation on the issue thread β€” only the two `load-issue.sh` loaders shipped under `skills/code-review-{github,jira}/scripts/` are added, never a blanket `bin/` or `*` allow. New `AgenticVibes\AgentSkills\InstallerClaudeSettings` class encapsulates the read / merge / write flow: existing entries in `permissions.allow` are preserved, non-string entries are dropped, the merge is idempotent (re-running the installer reports `0` newly allowed permissions), unrelated keys (`theme`, `permissions.deny`, etc.) survive untouched, malformed JSON raises a clear `InstallerFailure`. Help text and `README.md` *CLI Switches* table document the flag. 17 new Pest assertions cover the merge / idempotency / failure / no-op paths. Closes #487 - πŸ› **Fixed**: tighten `@rules/reports/general.mdc` after the initial #490 implementation produced bilingual *"KritickΓ© (Critical)"*-style mixed comments. The rule now (1) bans mixing two natural languages inside the same tracker comment as the headline constraint, including bilingual parenthetical glosses, and (2) introduces a single, narrowly scoped *Exception β€” technical CR findings on the GitHub PR* β€” the technical PR comments from `code-review-github` / `code-review-jira` / `process-code-review` / `security-review` / `security-threat-analysis` / `resolve-issue` stay in canonical English (machine-greppable severity labels, structured field labels, rule references), while the **non-technical** mirror published on the linked GitHub issue / JIRA ticket via `@skills/pr-summary/SKILL.md` (and `assignment-compliance-check` / `tester-cookbook` comments) still follows the assignment language. Each CR-wrapper skill's `Apply @rules/reports/general.mdc` clause is updated to cite the exception. Closes #494 - πŸ› **Fixed**: every skill that publishes a report to an issue tracker (GitHub PR / issue comments via `gh`, JIRA comments via `acli`, Bugsnag-linked GitHub mirrors) now writes the report in the **language of the source assignment** β€” i.e., a Czech issue / JIRA description produces a Czech comment, an English one produces an English comment. The previous *"All output must be in English"* / *"All output posted to GitHub must be in English"* / *"GitHub output must be in English"* / *"All CR output must be written in English"* constraints in `code-review`, `code-review-github`, `code-review-jira`, `process-code-review`, `security-review`, and `security-threat-analysis` are replaced by an `Apply @rules/reports/general.mdc` reference. The same rule is referenced from `pr-summary`, `assignment-compliance-check`, `resolve-issue`, `test-like-human`, `tester-cookbook`, and `prepare-issue-context`. New shared rule `rules/reports/general.mdc` carries the canonical statement (assignment-language detection via the deterministic loaders, severity-label translation, code identifiers / PR titles / commit messages stay verbatim per `@rules/git/general.mdc`, in-conversation status may stay in English). Closes #490 ## [0.8.1] - 2026-05-18 - πŸ”§ **Changed**: PHPStan, Pint, PHPCS, and Rector now also analyse the `bin/` directory (added to `phpstan.neon` paths, both `phpcs-check`/`phpcs-fix` and `pint-check`/`pint-fix` Composer scripts, and `rector.php` paths). Closes a CR gap where `bin/coverage-diff-check.php` shipped as 79 lines of production PHP without any static-analysis coverage; regressions in the CLI wrapper now fail the CI quality gate instead of slipping through to runtime. -- ✨ **Added**: new `Pekral\CursorRules\CoverageDiffCheckFailure` named-constructor exception class (extends `RuntimeException`, with `::invalidCloverXml(string $reason)` factory), mirroring the existing `InstallerFailure` convention. `CoverageDiffCheck::findGapsInClover` now throws the specific exception instead of a generic `RuntimeException`, so consumers can catch the actual error type rather than relying on string matching the message. Pest tests updated to assert the new exception class. +- ✨ **Added**: new `AgenticVibes\AgentSkills\CoverageDiffCheckFailure` named-constructor exception class (extends `RuntimeException`, with `::invalidCloverXml(string $reason)` factory), mirroring the existing `InstallerFailure` convention. `CoverageDiffCheck::findGapsInClover` now throws the specific exception instead of a generic `RuntimeException`, so consumers can catch the actual error type rather than relying on string matching the message. Pest tests updated to assert the new exception class. - ✨ **Added**: new Agent skill `prepare-issue-context` β€” front-loads the data and codebase context an implementing agent needs to act on an assignment without hallucinating. The skill loads the issue via the deterministic loader, extracts every scenario from the *Jak otestovat* / acceptance-criteria section (trigger, literal inputs, expected outcome, edge constraints), maps each scenario to a concrete code path (entry point, business-logic layer, persistence shape, boundary integrations), enumerates the records the scenarios depend on, seeds the **development** database via the project's existing seeder / factory mechanism (scratch namespace, never staging / production), and runs a one-shot reproduction so the bug is observable on the seeded state before any code is written. Three modes: `resolve-issue` (default β€” full seed), `tdd` (seed only the scoped RED scenarios), `cr` (audit-only β€” no inserts, the dev-DB state is queried instead). Returns `ready` or `blocked: open gap(s)` with categorised gaps (mapping / data / behavioral / external-dependency). Now invoked as a mandatory pre-flight from `@skills/resolve-issue/SKILL.md` (before *Problem analysis*), `@skills/test-driven-development/SKILL.md` (before the first RED), and the CR skills (`@skills/code-review/SKILL.md` β€” first specialised review, surfacing the gap count in the PR comment summary and escalating behavioral gaps to Critical findings). - πŸ“ **Changed**: code review skill (`@skills/code-review/SKILL.md`) gains a **Translation completeness** Output rule. Auto-detects whether the project ships translations (Laravel `lang/` / `resources/lang/`, Symfony `translations/`, JSON / PO / MO locale files, `__()` / `trans()` / `Lang::get()` / `@lang` / `t()` / `i18next.t()` / `useTranslation()` / `$t(` / `$tc(` call sites). When translations are in use, every user-facing string added or modified by the diff (Blade / Livewire / Filament / Vue / React labels, validation messages, Notifications, Mailables, FormRequest `messages()`, enum `getLabel()`, log / exception strings surfaced to humans) must exist in **every** locale shipped with the project, and hard-coded literals on surfaces that elsewhere use `__()` / `t()` are flagged. Severity defaults to Moderate, escalated to Critical on primary-flow surfaces or when CI asserts translation parity. The section is skipped entirely when none of the i18n signals match. Suggested Fix carries the literal translation entry for the missing locale (or a `TODO()` marker when the agent cannot translate confidently). - πŸ“ **Changed**: every skill that publishes a non-technical comment to an issue tracker now delegates the comment to `@skills/pr-summary/SKILL.md`. `test-like-human` no longer authors its own JIRA / GitHub comment template; it hands raw scenario results to `pr-summary` and the published comment carries the uniform *Authors / Available behind / Summary of changes / How to test* contract with the executed scenarios folded into `How to test`. `code-review-github` and `code-review-jira` already delegated the linked-issue summary to `pr-summary` and now explicitly pass the PR / git authorship and the test-parameter-gating context through to it. Specialised non-technical reports (`assignment-compliance-check`, `tester-cookbook`) keep their bespoke structure but adopt the same `Authors` + `Available behind` metadata lines. Technical PR comments (CR findings on the GitHub PR) remain exempt and continue to carry the full detail programmers need. - πŸ“ **Changed**: every tracker-facing comment **must credit the real change author(s)** instead of the agent / CR identity. `@skills/pr-summary/SKILL.md` resolves authors from `git log --pretty='%an <%ae>' base..HEAD` plus PR `author.login` + `commits[].author.login` (GitHub handle preferred for GitHub targets, JIRA display name preferred for JIRA targets). The pr-summary templates (`pr-summary-github.md`, `pr-summary-jira.md`), the `assignment-compliance-check` template, and the `tester-cookbook` comment now expose the dedicated `Authors` line. When authorship cannot be determined, the line says *"unknown β€” git history did not yield a recognisable identity"* rather than being silently dropped. - πŸ“ **Changed**: every tracker-facing comment **must surface test-parameter gating**. When the diff hides the change behind a feature flag, ENV switch, query-string parameter, request header, A/B variant, admin toggle, or allow-list, `pr-summary` adds an `Available behind` line naming the gating toggle and the value required to reach it, and the first `How to test` step enables / supplies that toggle. `assignment-compliance-check` and `tester-cookbook` carry the same conditional line. The line is omitted entirely when the change is reachable for every user unconditionally. - πŸ“ **Changed**: code review skills (`code-review`, `code-review-github`, `code-review-jira`) require `@skills/mysql-problem-solver/SKILL.md` to run **whenever the diff touches database operations** (raw SQL, Eloquent / query-builder calls, eager loads, model scopes, ModelManager / Repository methods, migrations, seeders, DynamoDB / NoSQL access). The skill is moved from "Always run" to a mandatory conditional trigger, and its findings appear in the published GitHub PR comment under a dedicated `## Database Analysis` section (before `## Coverage`) β€” never silently folded into the Critical / Moderate / Minor buckets. The JIRA non-technical comment (produced by `pr-summary`) stays plain-language and does not carry this section. `templates/review-output.md` reflects the new section. -- ♻️ **Refactored**: extracted the diff-scoped coverage logic out of `bin/coverage-diff-check.php` into a first-class `Pekral\CursorRules\CoverageDiffCheck` class under `src/`. The `bin/` entry-point is now a thin CLI wrapper that loads Composer autoloader, parses arguments, and delegates to the class. Adds Pest tests covering every code path of `parseUnifiedDiff` (data provider with 8 cases), `findGapsInClover` (covered / uncovered / non-stmt / out-of-diff / sorted-deduped / absolute-path / nested `` / malformed XML / missing-project-root), and `discoverChangedLines` (current-repo smoke + temp-repo untracked-file fixture). `bin/test-coverage-diff` now also picks up untracked PHP files via `git ls-files --others --exclude-standard`, and the PHP helper diffs them through `git diff --no-index /dev/null ` so they participate in the changed-line map. Bug-fix: `bin/test-coverage-diff` previously aborted under `set -euo pipefail` because `php -m | grep -q` triggered SIGPIPE in `php -m`; the driver-detection step now captures `php -m` into a variable first. +- ♻️ **Refactored**: extracted the diff-scoped coverage logic out of `bin/coverage-diff-check.php` into a first-class `AgenticVibes\AgentSkills\CoverageDiffCheck` class under `src/`. The `bin/` entry-point is now a thin CLI wrapper that loads Composer autoloader, parses arguments, and delegates to the class. Adds Pest tests covering every code path of `parseUnifiedDiff` (data provider with 8 cases), `findGapsInClover` (covered / uncovered / non-stmt / out-of-diff / sorted-deduped / absolute-path / nested `` / malformed XML / missing-project-root), and `discoverChangedLines` (current-repo smoke + temp-repo untracked-file fixture). `bin/test-coverage-diff` now also picks up untracked PHP files via `git ls-files --others --exclude-standard`, and the PHP helper diffs them through `git diff --no-index /dev/null ` so they participate in the changed-line map. Bug-fix: `bin/test-coverage-diff` previously aborted under `set -euo pipefail` because `php -m | grep -q` triggered SIGPIPE in `php -m`; the driver-detection step now captures `php -m` into a variable first. - ✨ **Added** / πŸ“ **Changed**: CR coverage gate is now **diff-scoped only**. New `bin/test-coverage-diff` shell entry-point + `bin/coverage-diff-check.php` Clover parser, wired up as Composer script `composer test:coverage:diff`. The shell entry-point auto-picks PCOV when loaded and falls back to Xdebug in coverage mode, and auto-detects Laravel projects (artisan + `laravel/framework` in `composer.json` β†’ covers `app/` + `src/`) versus non-framework projects (covers `src/`). All CR / pre-push skills (`code-review`, `code-review-github`, `code-review-jira`, `create-missing-tests-in-pr`, `create-test`, `process-code-review`) and the shared `rules/php/core-standards.mdc` Testing section require the diff-scoped command during code review and explicitly forbid substituting the full-suite `composer test:coverage` / Phing `coverage` / `pest --coverage --min=100`-on-whole-suite as a fallback. The published `## Coverage` section in CR PR comments now reports the diff-scoped command and result (or "diff-scoped tooling unavailable" with reason). Full-suite coverage remains the release / CI gate. - πŸ“ **Changed**: review skills (`code-review`, `code-review-github`, `code-review-jira`, `security-review`) carry an explicit **read-only** constraint that bars every git write operation (`git add`, `git commit`, `git push`, `git reset`, `git checkout -- …`) on top of the existing "never modify code" rule. Branch switching and `git pull` to read the latest diff remain allowed; publishing is limited to PR / linked-issue comments via `gh` and to JIRA ticket comments via `acli`. README skill descriptions for `code-review-github`, `code-review-jira`, and `security-review` now surface the read-only guarantee to match `code-review`. - ✨ **Added**: new Agent skill `security-threat-analysis` β€” analyzes a specific security threat from a referenced source (CVE, GHSA, vendor advisory, blog post, or write-up) and produces a remediation report that is both human-readable and directly actionable by an AI agent. The skill loads the source, classifies the threat (OWASP/CWE, severity, attack vector, preconditions), matches it against the current project (lockfile versions for dependency CVEs, grep hits for pattern-based threats, framework/web-server settings for configuration threats), drafts an ordered remediation playbook with exact files and commands, and adds a verification step (manual check + automated test) that proves the threat is closed. Complements `@skills/security-review/SKILL.md` (whole-project audit) without duplicating it (#482) diff --git a/README.md b/README.md index 600f0767..71a08935 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ # Cursor Rules for PHP and Laravel β€” Composer Plugin

- Packagist Version - Total Downloads - PHP Version - License + Packagist Version + Total Downloads + PHP Version + License by pekral.cz

@@ -26,8 +26,8 @@ ## Installation ```bash -composer require pekral/cursor-rules --dev -vendor/bin/cursor-rules install --editor=cursor +composer require agentic-vibes/laravel-agent-skills --dev +vendor/bin/agent-skills install --editor=cursor ``` The `--editor` flag is **required**. Use it to choose the target agent: @@ -45,11 +45,11 @@ The `--editor` flag is **required**. Use it to choose the target agent: | Skills | βœ… | βœ… | βœ… | | Agents | β€” | βœ… | β€” | -Agents are installed only for `--editor=claude` and `--editor=all`; they are skipped for `cursor` and `codex` because only Claude Code has a native subagent format. Codex rules support is listed as experimental / unverified β€” the installer copies the files into `.codex/rules`, but native rule loading by Codex has not been confirmed; report your findings in [#663](https://github.com/pekral/cursor-rules/issues/663). +Agents are installed only for `--editor=claude` and `--editor=all`; they are skipped for `cursor` and `codex` because only Claude Code has a native subagent format. Codex rules support is listed as experimental / unverified β€” the installer copies the files into `.codex/rules`, but native rule loading by Codex has not been confirmed; report your findings in [#663](https://github.com/agentic-vibes/laravel-agent-skills/issues/663). -When the package is required via Composer, sources are read from `vendor/pekral/cursor-rules/rules` and `vendor/pekral/cursor-rules/skills`. +When the package is required via Composer, sources are read from `vendor/agentic-vibes/laravel-agent-skills/rules` and `vendor/agentic-vibes/laravel-agent-skills/skills`. -**Important:** By default, the installer only copies missing files and keeps existing content untouched. Use the `--force` flag to overwrite existing files: `vendor/bin/cursor-rules install --force`. This is particularly useful when you want to update rules to their latest versions or when you've made local changes that should be replaced. The file `.cursor/rules/project.mdc` and `CLAUDE.md` are never overwritten once they exist in the target project, so you can safely customize them. +**Important:** By default, the installer only copies missing files and keeps existing content untouched. Use the `--force` flag to overwrite existing files: `vendor/bin/agent-skills install --force`. This is particularly useful when you want to update rules to their latest versions or when you've made local changes that should be replaced. The file `.cursor/rules/project.mdc` and `CLAUDE.md` are never overwritten once they exist in the target project, so you can safely customize them. ### Automatic Installation via Composer Plugin @@ -58,7 +58,7 @@ By default, the Composer plugin does **not** auto-install rules on `composer ins ```json { "extra": { - "cursor-rules": { + "agent-skills": { "auto-install": true, "editor": "claude" } @@ -71,7 +71,7 @@ By default, the Composer plugin does **not** auto-install rules on `composer ins | `auto-install` | Enable automatic install on `composer install/update`. | `false` | | `editor` | Target editor for auto-install (`cursor`, `claude`, `codex`, `all`). | `cursor` | -If you prefer manual control, simply call `vendor/bin/cursor-rules install` in your Composer `post-update-cmd` scripts with the desired flags. +If you prefer manual control, simply call `vendor/bin/agent-skills install` in your Composer `post-update-cmd` scripts with the desired flags. ### Installing rules from GitHub (Cursor only) @@ -80,27 +80,27 @@ You can use this repository as a **Remote Rule** in Cursor without installing th 1. Open **Cursor Settings** β†’ **Rules**. 2. In the **Project Rules** section, click **Add Rule**. 3. Select **Remote Rule (Github)**. -4. Enter the repository URL: `https://github.com/pekral/cursor-rules`. +4. Enter the repository URL: `https://github.com/agentic-vibes/laravel-agent-skills`. Cursor will fetch and apply the rules from the repository. Note: this method provides rules only; Agent skills are installed into your project when you use the Composer-based installation above. ### Available Commands ```bash -vendor/bin/cursor-rules help # print help -vendor/bin/cursor-rules install --editor=cursor # install for Cursor -vendor/bin/cursor-rules install --editor=claude # install for Claude -vendor/bin/cursor-rules install --editor=codex # install for Codex -vendor/bin/cursor-rules install --editor=all # install for Cursor, Claude, and Codex -vendor/bin/cursor-rules install --editor=cursor --force # overwrite existing files -vendor/bin/cursor-rules install --editor=cursor --symlink # prefer symlinks (fallback to copy) -vendor/bin/cursor-rules install --editor=claude --allow-bundled-scripts # whitelist this package's bundled scripts in ~/.claude/settings.json +vendor/bin/agent-skills help # print help +vendor/bin/agent-skills install --editor=cursor # install for Cursor +vendor/bin/agent-skills install --editor=claude # install for Claude +vendor/bin/agent-skills install --editor=codex # install for Codex +vendor/bin/agent-skills install --editor=all # install for Cursor, Claude, and Codex +vendor/bin/agent-skills install --editor=cursor --force # overwrite existing files +vendor/bin/agent-skills install --editor=cursor --symlink # prefer symlinks (fallback to copy) +vendor/bin/agent-skills install --editor=claude --allow-bundled-scripts # whitelist this package's bundled scripts in ~/.claude/settings.json ``` ### Installer Flow 1. Determine the project root by walking up from the current directory until `composer.json` is found. -2. Resolve the rules source (local `rules/` or `vendor/pekral/cursor-rules/rules`). +2. Resolve the rules source (local `rules/` or `vendor/agentic-vibes/laravel-agent-skills/rules`). 3. Install rules into the target directory(ies) for the chosen editor (see `--editor`). 4. If present, resolve the skills source and install into the corresponding skill directory(ies). 5. For `--editor=claude` or `--editor=all`: copy `CLAUDE.md` to the project root (never overwrites existing). @@ -246,7 +246,7 @@ Agents = specialised orchestration roles over multiple skills 1. Install for Claude Code (or every editor): ```bash - vendor/bin/cursor-rules install --editor=claude # or --editor=all + vendor/bin/agent-skills install --editor=claude # or --editor=all ``` Agents land in `.claude/agents/`. They are **not** installed for `--editor=cursor` or `--editor=codex`. @@ -351,7 +351,7 @@ All `.mdc` and `.md` files are ready for automatic injection by Cursor so every ```bash composer check # run full quality check (skill-check, normalize, phpcs, pint, rector, phpstan, audit, tests) composer fix # run all automatic fixes (skill-check-fix, normalize, rector, pint, phpcs) -composer build # install (cursor-rules install --force) then fix then check +composer build # install (agent-skills install --force) then fix then check composer analyse # run PHPStan static analysis composer test:coverage # run tests with 100% coverage composer coverage # alias for test:coverage diff --git a/SECURITY.md b/SECURITY.md index e0c6af43..3f2bb381 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,14 +2,14 @@ ## Plugin trust model -`pekral/cursor-rules` is a **Composer plugin** (`"type": "composer-plugin"`). Composer requires explicit opt-in before running any plugin β€” including this one β€” to guard against supply-chain attacks from unknown packages. +`agentic-vibes/laravel-agent-skills` is a **Composer plugin** (`"type": "composer-plugin"`). Composer requires explicit opt-in before running any plugin β€” including this one β€” to guard against supply-chain attacks from unknown packages. ### `allow-plugins` in `composer.json` -When you `composer require pekral/cursor-rules`, Composer may ask: +When you `composer require agentic-vibes/laravel-agent-skills`, Composer may ask: ``` -Do you trust "pekral/cursor-rules" to execute code and wish to enable it now? (yes/no) +Do you trust "agentic-vibes/laravel-agent-skills" to execute code and wish to enable it now? (yes/no) ``` If you answer `yes`, Composer writes the following entry to your project's `composer.json`: @@ -18,7 +18,7 @@ If you answer `yes`, Composer writes the following entry to your project's `comp { "config": { "allow-plugins": { - "pekral/cursor-rules": true + "agentic-vibes/laravel-agent-skills": true } } } @@ -29,8 +29,8 @@ This is the **standard Composer plugin-trust mechanism** (`allow-plugins`). It i If you prefer to give a non-interactive answer (e.g. in CI), you can pass the flag explicitly: ```bash -composer require pekral/cursor-rules --dev --no-plugins # skip the plugin during install -composer config allow-plugins.pekral/cursor-rules true # then grant trust manually +composer require agentic-vibes/laravel-agent-skills --dev --no-plugins # skip the plugin during install +composer config allow-plugins.agentic-vibes/laravel-agent-skills true # then grant trust manually ``` ### Auto-install hook @@ -40,7 +40,7 @@ Granting `allow-plugins: true` also enables the package's Composer plugin to rea ```json { "extra": { - "cursor-rules": { + "agent-skills": { "auto-install": true, "editor": "claude" } @@ -48,7 +48,7 @@ Granting `allow-plugins: true` also enables the package's Composer plugin to rea } ``` -When `auto-install` is `true`, every `composer install` or `composer update` automatically runs `Installer::run(['cursor-rules', 'install', '--force', '--editor='])` β€” the same installer that you would call manually, with `--force` and without any opt-in flags (`--allow-bundled-scripts`, `--allow-subagent-writes`). **Security implication:** any package that ships a `post-install-cmd` / `post-update-cmd` hook and is trusted via `allow-plugins` can trigger code execution during a routine `composer install`. Review the `extra.cursor-rules` block in your `composer.json` before enabling `auto-install`, and treat it the same way you treat other Composer script hooks. +When `auto-install` is `true`, every `composer install` or `composer update` automatically runs `Installer::run(['agent-skills', 'install', '--force', '--editor='])` β€” the same installer that you would call manually, with `--force` and without any opt-in flags (`--allow-bundled-scripts`, `--allow-subagent-writes`). **Security implication:** any package that ships a `post-install-cmd` / `post-update-cmd` hook and is trusted via `allow-plugins` can trigger code execution during a routine `composer install`. Review the `extra.agent-skills` block in your `composer.json` before enabling `auto-install`, and treat it the same way you treat other Composer script hooks. See also: [README β€” Automatic Installation via Composer Plugin](README.md#automatic-installation-via-composer-plugin). @@ -110,6 +110,6 @@ The installer never writes outside the project directory and the user's home dir If you discover a security issue in this package, please report it privately so it can be addressed before public disclosure. -**Contact:** open a [GitHub Security Advisory](https://github.com/pekral/cursor-rules/security/advisories/new) (preferred) or email `kral.petr.88@gmail.com`. +**Contact:** open a [GitHub Security Advisory](https://github.com/agentic-vibes/laravel-agent-skills/security/advisories/new) (preferred) or email `kral.petr.88@gmail.com`. Please include a description of the issue, reproduction steps, and the potential impact. You will receive a response within a reasonable time. Public disclosure is coordinated after a fix is available. diff --git a/bin/cursor-rules b/bin/agent-skills similarity index 93% rename from bin/cursor-rules rename to bin/agent-skills index 40748657..f96e2e28 100755 --- a/bin/cursor-rules +++ b/bin/agent-skills @@ -22,4 +22,4 @@ if (!$autoloadFound) { exit(1); } -Pekral\CursorRules\Installer::run($argv); \ No newline at end of file +AgenticVibes\AgentSkills\Installer::run($argv); \ No newline at end of file diff --git a/composer.json b/composer.json index 7233ddaf..4ec7175f 100644 --- a/composer.json +++ b/composer.json @@ -1,15 +1,15 @@ { - "name": "pekral/cursor-rules", + "name": "agentic-vibes/laravel-agent-skills", "description": "Composer plugin for PHP and Laravel projects that installs Cursor/Claude/Codex rules and agent skills β€” coding standards, testing, and conventions", "license": "MIT", "type": "composer-plugin", "keywords": [ "composer-plugin", "cursor", - "cursor-rules", + "agent-skills", "cursor-editor", - "php-cursor-rules", - "laravel-cursor-rules", + "php-agent-skills", + "laravel-agent-skills", "php", "laravel", "coding-standards", @@ -27,9 +27,9 @@ ], "homepage": "https://pekral.cz", "support": { - "issues": "https://github.com/pekral/cursor-rules/issues", - "source": "https://github.com/pekral/cursor-rules", - "docs": "https://github.com/pekral/cursor-rules#readme" + "issues": "https://github.com/agentic-vibes/laravel-agent-skills/issues", + "source": "https://github.com/agentic-vibes/laravel-agent-skills", + "docs": "https://github.com/agentic-vibes/laravel-agent-skills#readme" }, "require": { "composer-plugin-api": "^2.0" @@ -54,7 +54,7 @@ }, "autoload": { "psr-4": { - "Pekral\\CursorRules\\": "src/" + "AgenticVibes\\AgentSkills\\": "src/" } }, "autoload-dev": { @@ -63,7 +63,7 @@ } }, "bin": [ - "bin/cursor-rules" + "bin/agent-skills" ], "archive": { "exclude": [ @@ -72,15 +72,15 @@ }, "config": { "allow-plugins": { + "agentic-vibes/laravel-agent-skills": true, "dealerdirect/phpcodesniffer-composer-installer": true, "ergebnis/composer-normalize": true, - "pekral/cursor-rules": true, "pestphp/pest-plugin": true, "phpstan/extension-installer": true } }, "extra": { - "class": "Pekral\\CursorRules\\ComposerPlugin" + "class": "AgenticVibes\\AgentSkills\\ComposerPlugin" }, "scripts": { "analyse": [ @@ -88,7 +88,7 @@ "vendor/bin/phpstan analyse --memory-limit=2G" ], "build": [ - "bin/cursor-rules install --force", + "bin/agent-skills install --force", "@fix", "@check" ], diff --git a/docs/memory/PROJECT_MEMORY.md b/docs/memory/PROJECT_MEMORY.md index 01a4f903..c388e617 100644 --- a/docs/memory/PROJECT_MEMORY.md +++ b/docs/memory/PROJECT_MEMORY.md @@ -1,46 +1,46 @@ -# Project memory β€” cursor-rules +# Project memory β€” laravel-agent-skills ### auto-mode-external-write-blocked β€” Post-convergence publication to linked issue silently blocked in auto-mode environments - Trigger: an agent (e.g. `argos`, `apollon`) attempts to publish a comment to a GitHub issue or JIRA ticket (the source tracker) in an environment where auto-mode write classification is active β€” typically when `pr-summary` or `upsert-comment.sh` targets a linked issue rather than the PR itself. - Rule: In auto-mode environments the external-write classifier may block the publication silently β€” no error is thrown, the comment simply never appears. This affects `pr-summary` linked-issue mirrors and any post-convergence feedback step that targets the originating tracker item. Always verify that the publication step actually posted (check the tracker URL, not just the agent handoff). If blocked: publish manually, or re-run with explicit linked-issue write permission enabled. Document the outcome in the agent handoff (`Blocked: external-write blocked by auto-mode classifier`) so the next human knows a manual step is required. - Example: `argos` post-convergence `pr-summary` mirror on issue #629 was blocked β€” the handoff noted `status: failed to post on issue #629: external-write blocked by auto-mode classifier`; the comment was posted manually to the PR instead. The netechnical summary on #629 required a separate manual action. -- Source: https://github.com/pekral/cursor-rules/pull/636 Added: 2026-06-20 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/636 Added: 2026-06-20 ### agent-file-vs-registration β€” Adding agents/.md does not make the agent dispatchable - Trigger: a daidalos run tries to dispatch a newly documented agent (e.g. `apollon`) via the Task tool, or any orchestrator step assumes a new `agents/.md` entry is immediately executable as a subagent. - Rule: An `agents/.md` file is documentation only. For an agent to be dispatchable in Claude tooling the agent type must also be installed/registered (the installer syncs copies into `.claude/`). Until that happens, the agent cannot be spawned and the orchestrator must fall back to available registered agents or treat the step as blocked. Document the dependency explicitly in the agent's own file and in the issue that introduces the agent. - Example: `agents/apollon.md` was added in #628; daidalos correctly noted "agent type `apollon` is not registered in this environment" and continued with `metis / talos / argos`. The push-level gate becomes effective only after `apollon` is installed. **Update (#654):** `apollon` has since been registered as a dispatchable agent β€” the registration status changed, so this premise is point-in-time, not permanent (see `verify-agent-registration-premise`). -- Source: https://github.com/pekral/cursor-rules/pull/633 Added: 2026-06-20 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/633 Added: 2026-06-20 ### parallel-agent-publication-contract β€” Parallel-dispatched agents must route findings through the shared brief, not publish directly - Trigger: a new CR / security / review agent is introduced that daidalos dispatches in parallel with an existing agent (e.g. `athena` alongside `argos`); the new agent's output step uses raw `gh pr comment` / `gh issue comment` to publish its findings. - Rule: Any agent dispatched in parallel must hand off findings via the shared task brief so the consolidating agent (e.g. `argos`) can merge and publish them as a single report. Direct publication is permitted only in standalone mode (no parallel dispatch), and even then must go through the canonical `upsert-comment.sh` wrapper β€” never raw `gh pr comment` or `gh issue comment`. Writing raw comment commands in a parallel-dispatch context breaks the consolidation contract and produces duplicate / uncoordinated comment threads. - Example: `agents/athena.md` step 5 originally used `gh pr comment` to publish directly; argos flagged this as Moderate in PR #638 (commit `82abc16`); fixed to hand off via shared brief when dispatched with argos, and use `upsert-comment.sh` in standalone mode. -- Source: https://github.com/pekral/cursor-rules/pull/638 Added: 2026-06-20 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/638 Added: 2026-06-20 ### agent-new-mode-status-result-parity β€” Adding a new run-mode to an agent requires extending both Status and Result in the handoff section - Trigger: a new run-mode or output branch is added to an agent definition (e.g. a "Decomposition done" path that returns before a PR is opened); the author updates `Result:` with the new value but leaves the `Status:` line unchanged. - Rule: Every new run-mode that produces a distinct output must appear in **both** the `Status:` line **and** the `Result:` list in the agent's *Output β€” handoff* section, and must be consistent with the status values defined in every cross-file peer (e.g. `daidalos.md` ↔ `metis.md`). Update all affected files atomically in the same commit; a missing `Status` value for a new mode is an incomplete contract that the CR loop will flag as Moderate. - Example: `agents/daidalos.md` *Output β€” handoff* `Status` line omitted `Decomposition done` while `agents/metis.md` defined it and issue #639 step 4 required it; argos caught this as Moderate in iteration 1 of PR #640 (fix commit `392203d`). -- Source: https://github.com/pekral/cursor-rules/pull/640 Added: 2026-06-20 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/640 Added: 2026-06-20 ### cr-rule-severity-collision β€” Adding a new detection rule for an antipattern already covered by an existing (gated) rule with a different severity creates a non-deterministic severity conflict - Trigger: a PR adds a new detection bullet (e.g. Moderate) to `skills/code-review/SKILL.md` or a `rules/**` file for an antipattern that an existing bullet already covers at a different severity (e.g. Critical), typically gated on an optional package; no dedup/gating clause is present. - Rule: Apply the canonical dedup/gating pattern from `skills/code-review/SKILL.md` "Inline validation guards" β€” "raise one finding per violation, never both". Gate the two bullets with mutually exclusive conditions (e.g. package installed β†’ Critical; package absent β†’ Moderate; never both). Add the gating clause to every file that carries either half of the conflicting pair so the severity is deterministic regardless of project context. **Distinguishing true collision from non-collision:** two bullets at different severities are only a collision when they can fire on the *same* code line. If the existing Critical bullet targets one antipattern (e.g. un-extracted inline mapping statements written in the controller body) and the new Moderate bullet targets a different antipattern (e.g. a misplaced but encapsulated factory call like `SomeData::from($request)` in the controller), the bullets cannot fire simultaneously on identical code β€” no gating clause is required. Test the collision by mentally placing a single code line under both bullets; if it does not match both, there is no collision. - Example: PR #646 added a Moderate bullet for inline Eloquent chains in `skills/code-review/SKILL.md` (line ~115) and a Moderate entry in `rules/laravel/architecture.mdc` (line ~279) without gating; existing Critical bullets in the same files covered the same surface. `composer build` stayed green, but argos flagged a Moderate severity-collision in iteration 1. Fix commit `2b1ebe4` added symmetric gating clauses ("without the package raise a Moderate … never both" / "when the package is installed the Critical rule applies instead … never both") to both files; re-review converged at 0 Critical + 0 Moderate. Counter-example (no collision): PR #703 added an ungated Moderate bullet for `SomeData::from($request)` called in the controller body alongside an existing arch-app-services-gated Critical "Inline data mapping" bullet β€” argos confirmed no collision in iteration 1 (0 fix loop iterations) because the Critical bullet targets un-extracted inline mapping logic, not a moved factory call. -- Source: https://github.com/pekral/cursor-rules/pull/646 Added: 2026-06-20 Updated: 2026-06-23 (PR #703) +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/646 Added: 2026-06-20 Updated: 2026-06-23 (PR #703) ### agent-rename-sync-points β€” Renaming an agent must atomically update its pinned InstallerTest phrases and redirect any reserved-name note - Trigger: an agent is renamed (e.g. `keryx` β†’ `hermes`) and the author updates the obvious files (`agents/.md`, `docs/agents.md`, `README.md`) but forgets one of two non-obvious sync points: the verbatim phrases pinned in `tests/InstallerTest.php`, or a reserved-name note elsewhere in `docs/agents.md` that now collides with the new name. - Rule: Renaming an agent touches more than the four obvious files. (1) `git mv agents/.md agents/.md` to preserve history, and rewrite `name:`, prose, and handoff phrases inside. (2) `tests/InstallerTest.php` pins agent prose verbatim (delegation/handoff contract, per-agent test, shared-task-brief agent list) β€” every renamed phrase must stay byte-identical to the new agent file or `composer build` fails. (3) Grep the whole repo for the old slug (all case variants) and confirm 0 occurrences. (4) If `docs/agents.md` reserves the *new* name for a future agent, redirect that reservation to another free Greek name (e.g. `iris`) so the name is not both reserved and live. Do all of it in one commit; a binary avatar at `assets/agents/.png` is committed and its `` reference in `docs/agents.md` swapped from `placeholder.svg` per the existing per-agent pattern. - Example: PR #647 renamed `keryx` β†’ `hermes` and added the avatar. The non-obvious work: pinned phrases in `tests/InstallerTest.php` rewritten in lockstep with `agents/hermes.md`, and the `hermes` reservation note (~line 95 of `docs/agents.md`, originally held for a future delivery/merge agent) redirected to `iris`. `argos` converged in iteration 1 (0/0/0); `grep -ri keryx` returned 0 outside `.claude/`. -- Source: https://github.com/pekral/cursor-rules/pull/647 Added: 2026-06-20 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/647 Added: 2026-06-20 ### verify-agent-registration-premise β€” Verify an agent's registration status against the live roster before relying on a recorded premise about it @@ -48,14 +48,14 @@ - Rule: Registration status is point-in-time, not permanent β€” an agent that was docs-only when a lesson was written may since have become dispatchable (or vice versa). Before implementing anything scoped to "every agent", verify the premise against the current state: `ls .claude/agents/` (and `agents/`) plus a grep of `tests/InstallerTest.php` for the agent set, and treat the live roster as source of truth rather than inheriting the claim from memory or the issue text. A stale premise produces incomplete parity that the CR loop flags as Moderate. - Example: issue #653 assumed `apollon` was documentation-only (per the older `agent-file-vs-registration` entry); by #654 `apollon` was a registered dispatchable agent, so the first implementation missed per-role memory parity for it. Surfaced as 2 Moderate CR findings, fixed in commit `43b6c07`. - Role: shared -- Source: https://github.com/pekral/cursor-rules/pull/654 Added: 2026-06-21 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/654 Added: 2026-06-21 ### installer-security-doc-source-of-truth β€” Security docs must enumerate unconditional installer writes, not only opt-in-gated ones - Trigger: writing or reviewing security / trust-model documentation (SECURITY.md, README CLI Switches, installer trust model) for a PHP Composer installer that also ships a ComposerPlugin; the author describes which files the installer writes and gates all of them behind opt-in CLI flags. -- Rule: The source of truth for what the installer writes is `src/Installer.php`, `src/InstallerClaudeSettings.php`, and `src/ComposerPlugin.php` β€” not the CLI flag list. At minimum two surfaces must be named explicitly: (1) `Installer::install()` calls `applyCoAuthoredByPreference()` unconditionally for `--editor=claude|all`, writing `includeCoAuthoredBy: false` into `~/.claude/settings.json` when the key is absent (never overwrites); (2) `allow-plugins: true` in `composer.json` unlocks the `ComposerPlugin` `post-install-cmd` / `post-update-cmd` hook which, when `extra.cursor-rules.auto-install: true` is set, runs the installer with `--force` on every `composer install` / `composer update` β€” a code-execution surface not gated behind any explicit user invocation. Omitting either surface lets a CR flag it as Critical (home-dir write) or Moderate (auto-install hook). +- Rule: The source of truth for what the installer writes is `src/Installer.php`, `src/InstallerClaudeSettings.php`, and `src/ComposerPlugin.php` β€” not the CLI flag list. At minimum two surfaces must be named explicitly: (1) `Installer::install()` calls `applyCoAuthoredByPreference()` unconditionally for `--editor=claude|all`, writing `includeCoAuthoredBy: false` into `~/.claude/settings.json` when the key is absent (never overwrites); (2) `allow-plugins: true` in `composer.json` unlocks the `ComposerPlugin` `post-install-cmd` / `post-update-cmd` hook which, when `extra.agent-skills.auto-install: true` is set, runs the installer with `--force` on every `composer install` / `composer update` β€” a code-execution surface not gated behind any explicit user invocation. Omitting either surface lets a CR flag it as Critical (home-dir write) or Moderate (auto-install hook). - Example: PR #672 (issue #664) introduced SECURITY.md that gated all home-dir writes behind opt-in flags; argos found the unconditional `includeCoAuthoredBy` write (Critical) and the `allow-plugins` auto-install hook (Moderate) were missing. Source references: `src/Installer.php:91`, `src/InstallerClaudeSettings.php:60-95`, `src/ComposerPlugin.php:43-69`. -- Source: https://github.com/pekral/cursor-rules/pull/672 Added: 2026-06-22 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/672 Added: 2026-06-22 - Role: shared ### skills-tree-verbatim-distribution β€” Any file added under skills/ is distributed verbatim into consumer trees by the installer @@ -63,7 +63,7 @@ - Trigger: a PR adds a non-`SKILL.md` file under `skills/` (dataset, fixture, payload, README) β€” in particular any file whose content could be parsed or executed by a runtime (e.g. a `.php` file with `` block that passed `php -l`; argos caught it as Moderate in PR #685 iteration 1. Fix commit `a940708` neutered the block to plain text and added a regression guard in `tests/Installer/SecurityContentTest.php` (`no dataset file in malicious-uploads/ contains a PHP open tag`). Source of truth for what the installer ships: `src/Installer.php`. Dataset inertness is declared in per-directory READMEs and per-file INERT headers. -- Source: https://github.com/pekral/cursor-rules/pull/685 Added: 2026-06-22 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/685 Added: 2026-06-22 - Role: shared ### os-branch-coverage-ignore-test-via-observable-api β€” Test OS-gated branches wrapped in @codeCoverageIgnore via observable behaviour, not by rewriting PHP_OS @@ -71,7 +71,7 @@ - Trigger: a new OS-conditional branch in `src/Installer.php` (or similar) is wrapped in `@codeCoverageIgnoreStart/End` because the deciding value is a PHP constant (`PHP_OS`) that cannot be overwritten in a test; the task requires a smoke test for the branch without breaking `--min=100` coverage. - Rule: Do not try to rewrite `PHP_OS`, inject a fake OS parameter, or use runkit/uopz β€” these are brittle and break simplicity-first. Instead: (1) leave the branch `@codeCoverageIgnore` (it does not count against `--min=100`); (2) write the test against the public API (`Installer::run`) to verify observable behaviour; (3) use the existing `installerSymlinkUnsupported()` helper (`tests/Pest.php:65`) as a gate β€” on Windows-like hosts assert the copy-fallback hard (`is_link === false`), on other hosts assert the real symlink output (`is_link === true`). Never leave a branch-conditional test with an empty assertion β€” both paths must assert something concrete. - Example: `tests/InstallerTest.php` β€” "install creates regular files (copy fallback), never symlinks, when symlinks are unsupported (Windows-like)" (added in #665); `tests/Pest.php:65` `installerSymlinkUnsupported()` helper; `src/Installer.php:351-360` `canSymlink()` Windows branch with `@codeCoverageIgnoreStart/End`. -- Source: https://github.com/pekral/cursor-rules/pull/673 Added: 2026-06-22 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/673 Added: 2026-06-22 - Role: talos ### cross-cutting-rule-belongs-in-compound-engineering β€” A cross-cutting contract for all agents and skills belongs in rules/compound-engineering/general.mdc, not in skills/ or per-agent copy-paste @@ -79,7 +79,7 @@ - Trigger: a new rule or contract must apply to every agent and every skill uniformly β€” e.g. a cleanup obligation, a memory-files exception, a shared-brief protocol β€” and the implementer considers (a) adding a new file under `skills/`, (b) copy-pasting prose into each `agents/*.md`, or (c) creating a new standalone rule file. - Rule: Place the rule in `rules/compound-engineering/general.mdc` (frontmatter `alwaysApply: true`, globs `*`), which is already the project's single source of truth for cross-cutting agent/skill contracts (memory lifecycle, shared task brief, draft PR, etc.). Do not add to `skills/` β€” `src/Installer.php` distributes every file under `skills/` verbatim to consumer trees (see `skills-tree-verbatim-distribution`), making prose rules an unintended delivery artifact. Do not copy-paste into each `agents/*.md` β€” a one-liner reference from each agent to the canonical rule is sufficient and keeps the source of truth in one place. Use the existing `tests/Installer/CompoundEngineeringContentTest.php` to add a pinning assertion for the new rule text so regressions are caught automatically. - Example: issue #694 required a temporary-file hygiene contract; adding it to `general.mdc` as `## Temporary-file hygiene (clean up on completion)` (PR #697) with a one-sentence reference from each agent and a pinning test in `CompoundEngineeringContentTest.php` converged with 0 CR findings in iteration 1. -- Source: https://github.com/pekral/cursor-rules/pull/697 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/697 Added: 2026-06-23 - Role: shared ### agent-shared-task-brief-section-append-only β€” Adding text to Shared task brief sections in agents/*.md must not overwrite or reorder pinned phrases @@ -87,7 +87,7 @@ - Trigger: a task requires adding one or more sentences to the *Shared task brief* section (or any other prose section) of every `agents/*.md` file β€” e.g. a new cross-cutting rule reference β€” and the implementer edits the section freely, not knowing which exact phrases are pinned verbatim by `tests/Installer/AgentsTest.php` and/or `tests/Installer/CompoundEngineeringContentTest.php`. - Rule: Before editing any section of an `agents/*.md` file, grep `tests/Installer/AgentsTest.php` and `tests/Installer/CompoundEngineeringContentTest.php` for the section heading and the surrounding prose to identify every pinned phrase. Append new sentences at the end of the section (or insert at a clearly unpinned position); never reorder existing sentences, split pinned paragraphs, or change wording of already-pinned lines. After editing, run `composer build` locally β€” a pinning assertion failure (`toContain`) is the precise error that surfaces a broken phrase. - Example: PR #697 added a one-sentence hygiene-rule reference to the *Shared task brief* section of 7 agent files; `composer build` passed (295/295, 100 % coverage) because each sentence was appended after the existing pinned prose without reordering. The guard is `tests/Installer/CompoundEngineeringContentTest.php` for `general.mdc` content and `tests/Installer/AgentsTest.php` for per-agent delegation contract phrases. -- Source: https://github.com/pekral/cursor-rules/pull/697 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/697 Added: 2026-06-23 - Role: talos ### skills-tree-convention-removal-grep-full-tree β€” Removing or renaming a shared convention across skills/ requires grepping the full tree, not only the named files @@ -95,7 +95,7 @@ - Trigger: a task removes or renames a shared convention (marker text, function name, section title, or anchor pattern) that is referenced across multiple `skills/` files; the implementer updates only the explicitly named files (e.g. three SKILL.md files listed in the issue body) without searching for every reference. - Rule: Before opening the PR, run `grep -r '' skills/` across the whole `skills/` tree to find every occurrence of the convention being removed or renamed β€” including verbatim-distributed templates (`skills/code-review/templates/`), cross-skill SKILL.md files, and helper scripts. Any file that still mentions the old convention after the change is a live doc artifact shipped to consumer trees by `src/Installer.php` and will produce a Moderate CR finding. Pin the absence of the old pattern in `tests/Installer/CodeReviewContentTest.php` (or the relevant installer content test) with a `not->toContain(...)` assertion so regressions are caught automatically. - Example: PR #700 removed `{anchor:cr-comment-actor-}` from three SKILL.md files, but `skills/code-review/templates/review-output.md` and `skills/process-code-review/SKILL.md` still referenced the anchor and the in-place-edit claim β€” both missed because the search covered only the named file list. Two Moderate findings in argos iteration 1 caught the drift; fixed in commit `197a442` after a full-tree grep. Pin: `not->toContain('{anchor:')` + `not->toContain('edit that comment in place')` added to `tests/Installer/CodeReviewContentTest.php`. -- Source: https://github.com/pekral/cursor-rules/pull/700 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/700 Added: 2026-06-23 - Role: talos ### laravel-rules-tracked-source β€” The canonical tracked source of Laravel rules is rules/laravel/architecture.mdc at the repo root, not .claude/rules/ @@ -103,14 +103,14 @@ - Trigger: a task requires adding or modifying a Laravel CR rule β€” a new detection bullet, severity entry, or convention text β€” and the implementer looks for the file to edit. - Rule: The canonical, git-tracked source is `rules/laravel/architecture.mdc` in the repo root. The copy at `.claude/rules/laravel/architecture.mdc` is git-ignored and generated by the installer; editing it changes nothing in the repo. Always edit the root `rules/` file. Additionally, any new phrase added to `rules/laravel/architecture.mdc` must be byte-identically reflected in a pinning assertion in `tests/Installer/LaravelRulesContentTest.php` β€” the test uses `toContain()` against exact strings; a one-character mismatch causes `composer build` to fail. Similarly, if a companion detection bullet is added to `skills/code-review/SKILL.md`, pin it in `tests/Installer/CodeReviewContentTest.php`. Run `composer build` before opening the PR to catch any mismatch early. - Example: PR #703 (issue #698) β€” daidalos/gather step had to explicitly confirm which file was tracked; both paths had identical content, making the distinction non-obvious. The brief recorded: "KANONICKÝ TRACKED ZDROJ: `rules/laravel/architecture.mdc` (koΕ™en repa), NE `.claude/rules/...`". Pinning tests in `tests/Installer/LaravelRulesContentTest.php` and `tests/Installer/CodeReviewContentTest.php` required four byte-identical phrases each; all passed on the first `composer build` run. -- Source: https://github.com/pekral/cursor-rules/pull/703 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/703 Added: 2026-06-23 - Role: talos ### post-convergence-comment-publish-needs-explicit-scope β€” Posting the feedback comment to the source tracker is blocked when the user only asked to "report back" - Trigger: a full-delivery run reaches the post-convergence reporting step (step 6a) and dispatches apollon/pr-summary to publish a "Hotovo β€” co se zmΔ›nilo a jak otestovat" comment on the source issue/PR. - Rule: publishing an external comment under the user's identity is a separate consent surface from resolving+merging. When the request says "report back" (to the user) without asking to post on the tracker, the auto-mode classifier denies the publish. Fall back to the in-chat summary and re-dispatch apollon for the final scoped validation only, carrying the How-to-test summary into the final report yourself. Don't retry the publish. - Example: gh-699 run; apollon dispatch denied with "[External System Writes] ... user only asked to report back ... not to post on the issue". -- Source: https://github.com/pekral/cursor-rules/pull/702 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/702 Added: 2026-06-23 - Role: daidalos ### per-tracker-claim-belongs-in-resolve-issue-and-autoresolve β€” A "claim before work" mechanism needs an idempotent abort-on-conflict claim AND a matching selection-exclusion filter, not a claim alone @@ -118,21 +118,21 @@ - Trigger: a task asks to mark a tracker issue "In progress" / claimed at the start of work so two AI agents do not pick the same task in parallel; the naive implementation only sets a status at work-start. - Rule: A claim alone does not prevent the collision β€” two runs can both set the same status before either notices. The real guard is two-sided: (1) the claim step must be idempotent, apply-and-verify (re-read, never trust the write exit code β€” `auto-mode-external-write-blocked`), and ABORT when the issue is already claimed by another run; AND (2) the selection step that picks the next issue must EXCLUDE already-claimed issues. For GitHub that is a claim label (`Resolve_by_AI:in-progress`) + a `-label:"${CLAIM_LABEL}"` negation in the `autoresolve` QUERY; for JIRA a second sanctioned transition helper (clone of `transition-to-code-review.sh`, `progress` name guard, idempotent no-op, acli false-positive re-verify); Bugsnag stays hands-off (documented limit). Release the claim on Blocked/abort BEFORE the PR opens so the issue is pickable again; keep it on success. The cross-cutting principle lives in `rules/compound-engineering/general.mdc`; per-tracker mechanics live in `resolve-issue` step 2 + `autoresolve` selection (per `cross-cutting-rule-belongs-in-compound-engineering`). - Example: issue #704 / PR #706 β€” `rules/compound-engineering/general.mdc` gained `## Claim a tracker issue before working on it`; `skills/code-review-jira/scripts/transition-to-in-progress.sh` (new, exit 4 = already-past-In-Progress collision, exit 5 = acli false-positive); `skills/resolve-issue/SKILL.md` claim sub-step; `skills/autoresolve-oldest-github-issue/SKILL.md` QUERY negation + line-17 sanctioned-write exception. Converged argos+athena 0/0/0 in iteration 1. -- Source: https://github.com/pekral/cursor-rules/pull/706 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/706 Added: 2026-06-23 - Role: shared ### second-sanctioned-jira-transition-clones-the-first β€” A new sanctioned JIRA transition helper should clone the existing one verbatim rather than extract a shared lib - Trigger: adding a second auto-allowed JIRA status transition (e.g. an "In Progress" claim alongside the existing "Code Review") and the implementer is tempted to extract the shared defensive logic into a sourced `lib.sh`. - Rule: Keep each transition helper a self-contained standalone script that mirrors `transition-to-code-review.sh` (anchored KEY regex, name guard, idempotent no-op, acli false-positive re-verify). Do NOT extract a sourced `lib.sh`: every file under `skills/` is distributed verbatim into consumer trees by `src/Installer.php` (`skills-tree-verbatim-distribution`), and a sourced library would break the self-contained convention the sibling relies on. Also update `rules/jira/general.mdc` to enumerate BOTH sanctioned transitions ("two exceptions") β€” the prior "single sanctioned transition" wording is now wrong; grep for the pinned phrase in the installer content tests before rewording it. -- Source: https://github.com/pekral/cursor-rules/pull/706 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/706 Added: 2026-06-23 - Role: talos ### claim-mechanism-converges-clean-when-it-mirrors-an-existing-pattern β€” daidalos: a feature that mirrors an already-reviewed sibling pattern converges in one CR iteration - Trigger: orchestrating a feature whose core artifact is structurally near-identical to an existing, already-reviewed artifact (here: a new JIRA transition helper cloning the existing one; a claim label mirroring the existing `ready for review` follow-up). - Rule: Route through metis first when the *mechanism* is ambiguous (which signal, where the contract lives) even if the *code* is a clone β€” the ambiguity is in the design, not the implementation. Once metis fixes the design, the implementation is low-risk and argos+athena converge in iteration 1. Worth recording so a similar "claim / status / follow-up" request is scoped as metis-then-clone rather than treated as net-new high-risk work. -- Source: https://github.com/pekral/cursor-rules/pull/706 Added: 2026-06-23 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/706 Added: 2026-06-23 - Role: daidalos ### github-sub-issues-only-via-graphql β€” GitHub native sub-issues are reachable only through GraphQL, not `gh ... --json` @@ -140,7 +140,7 @@ - Trigger: extending `skills/code-review-github/scripts/load-issue.sh` (or any GitHub loader) to read native sub-issues / parent-child issue relations. - Rule: `gh issue view --json subIssues` fails with `Unknown JSON field: "subIssues"` β€” the `gh` CLI projection does not expose the relation. Fetch it via `gh api graphql` against `repository.issue.subIssues` (the `subIssues(first:N){ nodes{ ... } }` connection; REST `/issues/{n}/sub_issues` also works). Bind owner/repo/number as GraphQL variables with `-F`/`-f` (no string interpolation into the query). Sub-issues exist on issues only (the GraphQL `issue(number)` is null for a PR number), so gate on `kind == "issue"` and default to `[]` on any failure. JIRA already exposes `subtasks` shallowly via `acli ... view --fields '*all'`, but full subtask body/comments/attachments need one extra `acli ... view` + `comment list` per subtask. GitHub issues have no attachment field β€” uploads are inline URLs in body/comments. - Example: issue #721 / PR #723 β€” added `subIssues[]` (full body + comments + labels via GraphQL) to the GitHub loader and deepened JIRA `subtasks[]` with description/comments/attachments. Both reviews (argos quality + athena security) converged 0/0/0 in iteration 1; `composer build` green (315 tests, 100% coverage). -- Source: https://github.com/pekral/cursor-rules/pull/723 Added: 2026-06-29 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/723 Added: 2026-06-29 - Role: shared ### shared-skills-helper-dir-and-readme-skill-count β€” a non-skill helper dir under `skills/` needs the README count test to gate on SKILL.md @@ -148,7 +148,7 @@ - Trigger: adding a shared helper directory under `skills/` (e.g. `skills/_shared/` with sourced libs / standalone scripts) reused by more than one skill, instead of duplicating logic or a per-skill `_lib.sh`. - Rule: Two gotchas. (1) The README skill-count test in `tests/Installer/SkillsContentTest.php` (`readme reports the current skill count …`) counted **every** directory under `skills/`, so a non-skill helper dir inflates the count and breaks the README assertions. Fix it to count only directories that ship a `SKILL.md` β€” that matches `skill-check`'s own definition of a skill (it reported 62 and ignored `_shared/`). (2) Cross-skill sourcing via `${SCRIPT_DIR}/../../_shared/lib.sh` resolves in consumer trees too: `src/Installer.php` copies the whole `skills/` tree verbatim (see [[skills-tree-verbatim-distribution]]), so all skills land under the same `skills/` parent and the relative path holds after install. A shared `skills/_shared/` lib is therefore compatible with verbatim distribution β€” the self-contained convention only applied to the JIRA transition-helper siblings. - Example: issue #725 / PR #726 β€” `skills/_shared/attachments.sh` (sourced download lib) + `skills/_shared/scan-attachments.sh` (standalone gate) reused by all three `download-attachments.sh` wrappers; auth token kept out of argv by writing it only into a 0600 curl `--config` file (`header = "Authorization: …"`), TLS pinned with `--proto`/`--proto-redir '=https'`. Scripts have no exec tests (test-isolation rule) β€” the fixture proof lives in `scan-attachments.sh --self-test` and its outcomes are content-pinned in Pest. -- Source: https://github.com/pekral/cursor-rules/pull/726 Added: 2026-06-29 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/726 Added: 2026-06-29 - Role: talos ### attachment-download-urls-need-an-ssrf-host-guard β€” fetching tracker-supplied URLs must block non-public hosts before the request @@ -156,5 +156,13 @@ - Trigger: writing or reviewing any skill/script that downloads a URL taken from issue-tracker content (attachment `contentUrl`, a URL scraped from comment/body Markdown, a webhook payload) β€” especially when the URL is user-controllable (e.g. a Bugsnag comment link, a GitHub issue body). - Rule: TLS-on + size-cap + quarantine is not enough β€” an attacker-supplied URL is an SSRF vector. Block loopback / link-local (incl. the `169.254.169.254` cloud-metadata endpoint) / RFC-1918 / ULA hosts **before** issuing the request, and record the rejection in the manifest rather than fetching. Put the guard in the shared download path (`skills/_shared/attachments.sh` `att_host_block_reason`, called from `att_run`) so all trackers inherit it. Give self-hosted trackers an explicit opt-out (`ATT_ALLOW_PRIVATE_HOSTS=1`) β€” a blanket private-IP block would break a JIRA/GitHub-Enterprise install on an internal network. Host-pinning by regex (GitHub) or server-issued URLs (JIRA `contentUrl`) already constrain those trackers; the open surface is whichever wrapper scrapes free-text URLs (Bugsnag). DNS-rebinding (public name β†’ private IP) needs `curl --resolve` pinning and is the residual gap to note, not silently ignore. - Example: issue #725 / PR #726 β€” CR raised the Bugsnag SSRF as Moderate; fixed with `att_host_block_reason` + a pinning Pest test. Converged 0 Critical / 0 Moderate in 2 process-code-review iterations; `composer build` green (323 tests). Pairs with the token-out-of-argv-via-curl-`--config` pattern recorded in [[shared-skills-helper-dir-and-readme-skill-count]]. -- Source: https://github.com/pekral/cursor-rules/pull/726 Added: 2026-06-29 +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/726 Added: 2026-06-29 - Role: shared + +### brand-rename-completeness-grep-all-token-forms β€” Brand/namespace rename completeness grep must enumerate all token forms including bare PascalCase symbols + +- Trigger: a task renames a brand, package, or PHP namespace across a repository and a completeness gate (`git grep`) is defined to verify zero remaining references. +- Rule: The grep must enumerate every token form of the old brand, not only the most visible one. For a PHP package rename, that means: (1) hyphen-slug `old/package-name`, (2) backslash-namespace `Old\Namespace`, AND (3) the bare PascalCase token `OldPascal` as it appears in PHP symbol names (method names, property names, variable names β€” e.g. `getOldPascalConfig()`). Omitting form (3) lets stale symbol names pass the gate while their bodies already use the new identifier. Recommended gate: `git grep -niI -e '' -e '' -e '' -- src/ bin/ tests/` β†’ 0 occurrences. +- Example: PR #731 β€” metis's completeness grep covered `pekral/cursor-rules` and `Pekral\CursorRules` but not the bare token `CursorRules`. The method `getCursorRulesConfig()` in `src/ComposerPlugin.php` passed the gate (body already reading `$extra['agent-skills']`) and surfaced as Moderate in argos iteration 1. Fix commit `6ebe8c9` renamed the method to `getAgentSkillsConfig()`. +- Source: https://github.com/agentic-vibes/laravel-agent-skills/pull/731 Added: 2026-06-30 +- Role: metis diff --git a/rules/code-testing/general.mdc b/rules/code-testing/general.mdc index 87c33b80..4ab8d6fd 100644 --- a/rules/code-testing/general.mdc +++ b/rules/code-testing/general.mdc @@ -55,7 +55,7 @@ A flaky test fails inconsistently, is hard to reproduce, and is often "fixed" by - Ensure new or modified tests follow existing project conventions. ## Test Organization -- Place every new test file under a directory path that mirrors the namespace of the production class it covers (e.g. `App\Service\Billing\InvoiceCalculator` β†’ `tests/Service/Billing/InvoiceCalculatorTest.php`, `Pekral\CursorRules\InstallerPath` β†’ `tests/InstallerPathTest.php`). A reviewer must be able to walk from a class to its tests by walking the parallel folder tree without searching. +- Place every new test file under a directory path that mirrors the namespace of the production class it covers (e.g. `App\Service\Billing\InvoiceCalculator` β†’ `tests/Service/Billing/InvoiceCalculatorTest.php`, `AgenticVibes\AgentSkills\InstallerPath` β†’ `tests/InstallerPathTest.php`). A reviewer must be able to walk from a class to its tests by walking the parallel folder tree without searching. - One production class maps to one primary test file. The file name is `{ClassName}Test.php` (Pest functional test file using the same base name as the SUT plus the `Test` suffix). Helper / scenario test files extracted from a primary test file keep the same directory and a descriptive `{ClassName}{Scenario}Test.php` name so the relationship stays explicit. - Cross-cutting tests that do not target a single production class (full-stack feature flows, contract tests for an external API, install / boot scripts) go under an intent-named directory at the top of `tests/` (`tests/Feature/`, `tests/Contract/`, `tests/Integration/`). Do not park them next to an unrelated class just to satisfy a parallel path. - Group multiple `it()` / `test()` blocks for the same SUT in the same test file when they share setup and subject. Do not split one SUT across many files unless the per-file size genuinely hurts readability (then use the `{ClassName}{Scenario}Test.php` form above). diff --git a/skills/automation-audit-ops/SKILL.md b/skills/automation-audit-ops/SKILL.md index dbe21a8e..6db79d3a 100644 --- a/skills/automation-audit-ops/SKILL.md +++ b/skills/automation-audit-ops/SKILL.md @@ -28,7 +28,7 @@ Number each finding and carry its proof through to the report. Work surface by s - **Claude Code config & hooks** β€” `.claude/settings.json`, `.claude/settings.local.json`, and any `hooks` blocks within them; project skill rules under `.claude/rules/` and `rules/`. - **MCP servers** β€” any `mcpServers` block in `.mcp.json`, `.claude/settings*.json`, or other configured JSON. - **Composer scripts** β€” the `scripts` map in `composer.json` (`build`, `check`, `fix`, `analyse`, `skill-check`, `security-audit`, `test:coverage`, normalize/pint/rector/phpcs entries). - - **Bundled CLI installer** β€” `bin/cursor-rules` and the install/sync logic it drives. + - **Bundled CLI installer** β€” `bin/agent-skills` and the install/sync logic it drives. - **Skills catalog** β€” `skills/*/SKILL.md` and their bundled `scripts/`, treated as automations themselves. - **Scheduler / cron** β€” any cron entry, Laravel scheduler definition, or `.claude/scheduled_tasks*` lock/registry. 2. **Classify live state per item.** Assign exactly one state, each backed by proof: diff --git a/skills/security-review/datasets/malicious-uploads/README.md b/skills/security-review/datasets/malicious-uploads/README.md index 306b5715..a70f51f4 100644 --- a/skills/security-review/datasets/malicious-uploads/README.md +++ b/skills/security-review/datasets/malicious-uploads/README.md @@ -4,7 +4,7 @@ This directory contains **inert test fixtures** representing malicious file-upload payloads. **INERT** means they are data only β€” they must never be executed, rendered in a browser, opened in a spreadsheet application, or served by the repository itself. They are used when writing tests for file-upload security controls, to verify that application code correctly sanitizes, rejects, or safely serves user-uploaded content. -These files exist as data β€” they are never executed, rendered, or served by the repository itself. They are distributed as part of the `cursor-rules` package so that project teams can reference them when writing security test cases. +These files exist as data β€” they are never executed, rendered, or served by the repository itself. They are distributed as part of the `laravel-agent-skills` package so that project teams can reference them when writing security test cases. ## Warning diff --git a/src/ComposerPlugin.php b/src/ComposerPlugin.php index a64cb63d..a577738b 100644 --- a/src/ComposerPlugin.php +++ b/src/ComposerPlugin.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use Composer\Composer; use Composer\EventDispatcher\EventSubscriberInterface; @@ -47,7 +47,7 @@ public function runInstaller(): void } $editor = $this->resolveEditorFromConfig(); - Installer::run(['cursor-rules', 'install', '--force', '--editor=' . $editor]); + Installer::run(['agent-skills', 'install', '--force', '--editor=' . $editor]); } /** @@ -63,14 +63,14 @@ public static function getSubscribedEvents(): array private function isAutoInstallEnabled(): bool { - $config = $this->getCursorRulesConfig(); + $config = $this->getAgentSkillsConfig(); return ($config['auto-install'] ?? false) === true; } private function resolveEditorFromConfig(): string { - $config = $this->getCursorRulesConfig(); + $config = $this->getAgentSkillsConfig(); $editor = $config['editor'] ?? InstallerPath::EDITOR_CURSOR; return in_array($editor, InstallerPath::getAllowedEditors(), true) @@ -81,14 +81,14 @@ private function resolveEditorFromConfig(): string /** * @return array */ - private function getCursorRulesConfig(): array + private function getAgentSkillsConfig(): array { if ($this->composer === null) { return []; } $extra = $this->composer->getPackage()->getExtra(); - $config = $extra['cursor-rules'] ?? []; + $config = $extra['agent-skills'] ?? []; return is_array($config) ? array_change_key_case($config, CASE_LOWER) : []; } diff --git a/src/InstallOptions.php b/src/InstallOptions.php index 09597518..340ecea9 100644 --- a/src/InstallOptions.php +++ b/src/InstallOptions.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; final readonly class InstallOptions { diff --git a/src/Installer.php b/src/Installer.php index 918365ef..ad099a67 100644 --- a/src/Installer.php +++ b/src/Installer.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use FilesystemIterator; use RecursiveDirectoryIterator; @@ -68,7 +68,7 @@ private static function parseEditor(array $argv): ?string private static function showHelp(): int { echo "Usage:\n"; - echo " vendor/bin/cursor-rules install --editor=EDITOR [--force] [--symlink] [--prune] [--allow-bundled-scripts] [--allow-subagent-writes]\n\n"; + echo " vendor/bin/agent-skills install --editor=EDITOR [--force] [--symlink] [--prune] [--allow-bundled-scripts] [--allow-subagent-writes]\n\n"; echo "Options:\n --editor=EDITOR Target editor (required): cursor, claude, codex, all.\n"; echo " --force Overwrite existing files.\n"; echo " --symlink Create symlinks instead of copying (falls back to copy on Windows).\n"; diff --git a/src/InstallerClaudeSettings.php b/src/InstallerClaudeSettings.php index ac8a1fd8..b51c3bdc 100644 --- a/src/InstallerClaudeSettings.php +++ b/src/InstallerClaudeSettings.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use JsonException; use stdClass; diff --git a/src/InstallerFailure.php b/src/InstallerFailure.php index 1cfca579..142e6930 100644 --- a/src/InstallerFailure.php +++ b/src/InstallerFailure.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use RuntimeException; diff --git a/src/InstallerHumanizer.php b/src/InstallerHumanizer.php index 8b365ca3..7abc3f78 100644 --- a/src/InstallerHumanizer.php +++ b/src/InstallerHumanizer.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; /** * Appends the humanizer directive to installed SKILL.md files. diff --git a/src/InstallerPath.php b/src/InstallerPath.php index 66dd233a..66e04e1c 100644 --- a/src/InstallerPath.php +++ b/src/InstallerPath.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use JsonException; @@ -252,7 +252,7 @@ public static function resolveAllSkillsTargetDirectories(string $root): array } /** - * Reads the editor setting from composer.json extra.cursor-rules.editor. + * Reads the editor setting from composer.json extra.agent-skills.editor. */ public static function resolveEditorFromComposerJson(string $projectRoot): ?string { @@ -268,7 +268,7 @@ public static function resolveEditorFromComposerJson(string $projectRoot): ?stri return null; } - $config = $extra['cursor-rules'] ?? []; + $config = $extra['agent-skills'] ?? []; if (!is_array($config)) { return null; diff --git a/src/InstallerPruner.php b/src/InstallerPruner.php index 55bbf413..a3d8cce7 100644 --- a/src/InstallerPruner.php +++ b/src/InstallerPruner.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -namespace Pekral\CursorRules; +namespace AgenticVibes\AgentSkills; use FilesystemIterator; use RecursiveDirectoryIterator; diff --git a/tests/Installer/AgentsTest.php b/tests/Installer/AgentsTest.php index 465208c0..b67308fe 100644 --- a/tests/Installer/AgentsTest.php +++ b/tests/Installer/AgentsTest.php @@ -2,8 +2,8 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerPath; test('resolveAgentsSource returns the package agents directory when it exists', function (): void { $packageDir = dirname(__DIR__, 2); @@ -52,7 +52,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); ob_end_clean(); expect(is_file($root . '/.claude/agents/argos.md'))->toBeTrue(); @@ -80,7 +80,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(is_dir($root . '/.cursor/agents'))->toBeFalse(); diff --git a/tests/Installer/ClaudeMdTest.php b/tests/Installer/ClaudeMdTest.php index 1e308cc1..d65f6e9f 100644 --- a/tests/Installer/ClaudeMdTest.php +++ b/tests/Installer/ClaudeMdTest.php @@ -2,8 +2,8 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerPath; test('resolveClaudeMdSource returns path to CLAUDE.md in package', function (): void { $source = InstallerPath::resolveClaudeMdSource(); @@ -35,7 +35,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); ob_end_clean(); $claudeMd = $root . '/CLAUDE.md'; @@ -58,7 +58,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(is_file($root . '/CLAUDE.md'))->toBeFalse(); @@ -87,7 +87,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=all']); + Installer::run(['agent-skills', 'install', '--editor=all']); ob_end_clean(); $claudeMd = $root . '/CLAUDE.md'; @@ -108,7 +108,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); ob_end_clean(); expect(file_get_contents($claudeMd))->toBe('my custom CLAUDE.md'); @@ -131,7 +131,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--force']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--force']); ob_end_clean(); expect(file_get_contents($claudeMd))->toBe('my custom CLAUDE.md'); diff --git a/tests/Installer/ClaudeSettingsTest.php b/tests/Installer/ClaudeSettingsTest.php index 49e6594f..b16d327e 100644 --- a/tests/Installer/ClaudeSettingsTest.php +++ b/tests/Installer/ClaudeSettingsTest.php @@ -2,9 +2,9 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerClaudeSettings; -use Pekral\CursorRules\InstallerFailure; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerClaudeSettings; +use AgenticVibes\AgentSkills\InstallerFailure; test('InstallerClaudeSettings exposes the two bundled-script permission patterns', function (): void { $patterns = InstallerClaudeSettings::getBundledScriptPermissions(); @@ -208,7 +208,7 @@ }); test('ensureSubagentWritesEnabled writes scoped Edit/Write entries into a fresh settings.local.json', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); try { $written = InstallerClaudeSettings::ensureSubagentWritesEnabled($root); @@ -232,7 +232,7 @@ }); test('ensureSubagentWritesEnabled prepends to existing allow without dropping unrelated entries', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); $settingsPath = $root . '/.claude/settings.local.json'; installerWriteFile($settingsPath, (string) json_encode([ 'theme' => 'dark', @@ -262,7 +262,7 @@ }); test('ensureSubagentWritesEnabled is idempotent when both entries are already present', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); $settingsPath = $root . '/.claude/settings.local.json'; installerWriteFile($settingsPath, (string) json_encode([ 'permissions' => ['allow' => [ @@ -280,7 +280,7 @@ }); test('ensureSubagentWritesEnabled adds only the missing entry when one is already present', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); $settingsPath = $root . '/.claude/settings.local.json'; installerWriteFile($settingsPath, (string) json_encode([ 'permissions' => ['allow' => [sprintf('Write(/%s/**)', $root)]], @@ -303,7 +303,7 @@ }); test('ensureSubagentWritesEnabled recovers when permissions.allow is the wrong shape and drops non-strings', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); $settingsPath = $root . '/.claude/settings.local.json'; installerWriteFile($settingsPath, (string) json_encode([ 'permissions' => ['allow' => ['Bash(git status:*)', 42, null]], @@ -327,7 +327,7 @@ }); test('ensureSubagentWritesEnabled recovers when permissions key is the wrong shape', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); $settingsPath = $root . '/.claude/settings.local.json'; installerWriteFile($settingsPath, (string) json_encode(['permissions' => 'not-an-object'])); @@ -348,21 +348,21 @@ }); test('applySubagentWritesIfRequested returns false when the flag is not set', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); expect(InstallerClaudeSettings::applySubagentWritesIfRequested(allowSubagentWrites: false, editor: 'claude', projectRoot: $root))->toBeFalse(); expect(is_file($root . '/.claude/settings.local.json'))->toBeFalse(); }); test('applySubagentWritesIfRequested returns false for a non-claude editor', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); expect(InstallerClaudeSettings::applySubagentWritesIfRequested(allowSubagentWrites: true, editor: 'cursor', projectRoot: $root))->toBeFalse(); expect(is_file($root . '/.claude/settings.local.json'))->toBeFalse(); }); test('applySubagentWritesIfRequested writes the allow entries for editor=claude when requested', function (): void { - $root = sys_get_temp_dir() . '/cursor-rules-saw-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-saw-' . bin2hex(random_bytes(4)); try { expect(InstallerClaudeSettings::applySubagentWritesIfRequested(allowSubagentWrites: true, editor: 'claude', projectRoot: $root))->toBeTrue(); @@ -406,7 +406,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--allow-bundled-scripts']); $output = (string) ob_get_clean(); expect($output)->toContain('Allowed 2 bundled-script permission(s) in ~/.claude/settings.json.'); @@ -436,7 +436,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=all', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=all', '--allow-bundled-scripts']); ob_end_clean(); $settingsPath = $root . '/.claude/settings.json'; @@ -462,7 +462,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--allow-bundled-scripts']); $output = (string) ob_get_clean(); expect($output)->not->toContain('Allowed'); @@ -488,7 +488,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); $output = (string) ob_get_clean(); expect($output)->not->toContain('Allowed'); @@ -521,7 +521,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--allow-subagent-writes']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--allow-subagent-writes']); $output = (string) ob_get_clean(); expect($output)->toContain('Allowed subagent file writes (Edit/Write on the working tree) in .claude/settings.local.json.'); @@ -560,7 +560,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--allow-subagent-writes']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--allow-subagent-writes']); $output = (string) ob_get_clean(); expect($output)->not->toContain('Allowed subagent file writes'); @@ -586,7 +586,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); $output = (string) ob_get_clean(); expect($output)->not->toContain('Allowed subagent file writes'); @@ -609,7 +609,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--allow-bundled-scripts']); $output = (string) ob_get_clean(); expect($output)->not->toContain('Allowed'); @@ -646,11 +646,11 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--allow-bundled-scripts']); ob_end_clean(); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude', '--allow-bundled-scripts']); + Installer::run(['agent-skills', 'install', '--editor=claude', '--allow-bundled-scripts']); $secondOutput = (string) ob_get_clean(); expect($secondOutput)->not->toContain('Allowed'); diff --git a/tests/Installer/CliRunTest.php b/tests/Installer/CliRunTest.php index b4d07ca1..909b53b5 100644 --- a/tests/Installer/CliRunTest.php +++ b/tests/Installer/CliRunTest.php @@ -2,12 +2,12 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerPath; test('run shows help when executed without arguments', function (): void { ob_start(); - $exitCode = Installer::run(['cursor-rules']); + $exitCode = Installer::run(['agent-skills']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -16,20 +16,20 @@ }); test('run returns error code for unknown command', function (): void { - $exitCode = Installer::run(['cursor-rules', 'unknown']); + $exitCode = Installer::run(['agent-skills', 'unknown']); expect($exitCode)->toBe(1); }); test('run returns error code for invalid editor', function (): void { - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=invalid']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=invalid']); expect($exitCode)->toBe(1); }); test('run shows prune option in help output', function (): void { ob_start(); - $exitCode = Installer::run(['cursor-rules']); + $exitCode = Installer::run(['agent-skills']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -38,7 +38,7 @@ test('help text documents the --allow-bundled-scripts flag', function (): void { ob_start(); - $exitCode = Installer::run(['cursor-rules']); + $exitCode = Installer::run(['agent-skills']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -48,7 +48,7 @@ }); test('normalizeCliArguments splits --allow-bundled-scripts from a concatenated argv blob', function (): void { - $normalized = InstallerPath::normalizeCliArguments(['cursor-rules', 'install', '--editor=claude--allow-bundled-scripts']); + $normalized = InstallerPath::normalizeCliArguments(['agent-skills', 'install', '--editor=claude--allow-bundled-scripts']); expect($normalized)->toContain('--editor=claude'); expect($normalized)->toContain('--allow-bundled-scripts'); @@ -56,7 +56,7 @@ test('help text documents the --allow-subagent-writes flag', function (): void { ob_start(); - $exitCode = Installer::run(['cursor-rules']); + $exitCode = Installer::run(['agent-skills']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -65,14 +65,14 @@ }); test('normalizeCliArguments splits --allow-subagent-writes from a concatenated argv blob', function (): void { - $normalized = InstallerPath::normalizeCliArguments(['cursor-rules', 'install', '--editor=claude--allow-subagent-writes']); + $normalized = InstallerPath::normalizeCliArguments(['agent-skills', 'install', '--editor=claude--allow-subagent-writes']); expect($normalized)->toContain('--editor=claude'); expect($normalized)->toContain('--allow-subagent-writes'); }); test('install without --editor returns error', function (): void { - $exitCode = Installer::run(['cursor-rules', 'install']); + $exitCode = Installer::run(['agent-skills', 'install']); expect($exitCode)->toBe(1); }); diff --git a/tests/Installer/EditorFromComposerJsonTest.php b/tests/Installer/EditorFromComposerJsonTest.php index 0cd4a738..57747d6f 100644 --- a/tests/Installer/EditorFromComposerJsonTest.php +++ b/tests/Installer/EditorFromComposerJsonTest.php @@ -2,13 +2,13 @@ declare(strict_types = 1); -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\InstallerPath; test('resolveEditorFromComposerJson returns editor when configured', function (): void { $root = installerCreateProjectRoot(); file_put_contents($root . '/composer.json', json_encode([ 'extra' => [ - 'cursor-rules' => [ + 'agent-skills' => [ 'editor' => 'claude', ], ], @@ -39,7 +39,7 @@ $root = installerCreateProjectRoot(); file_put_contents($root . '/composer.json', json_encode([ 'extra' => [ - 'cursor-rules' => [ + 'agent-skills' => [ 'editor' => 'invalid', ], ], @@ -71,7 +71,7 @@ $root = installerCreateProjectRoot(); file_put_contents($root . '/composer.json', json_encode([ 'extra' => [ - 'cursor-rules' => [ + 'agent-skills' => [ 'Editor' => 'Claude', ], ], @@ -101,11 +101,11 @@ } }); -test('resolveEditorFromComposerJson returns null when cursor-rules config is not an array', function (): void { +test('resolveEditorFromComposerJson returns null when agent-skills config is not an array', function (): void { $root = installerCreateProjectRoot(); file_put_contents($root . '/composer.json', json_encode([ 'extra' => [ - 'cursor-rules' => 'not-an-array', + 'agent-skills' => 'not-an-array', ], ])); diff --git a/tests/Installer/InstallCopyTest.php b/tests/Installer/InstallCopyTest.php index 2e077c46..3d6c3428 100644 --- a/tests/Installer/InstallCopyTest.php +++ b/tests/Installer/InstallCopyTest.php @@ -2,8 +2,8 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerPath; test('package directory points to correct location', function (): void { $packageDir = dirname(__DIR__, 2); @@ -32,7 +32,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect($exitCode)->toBe(0); @@ -59,7 +59,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect($exitCode)->toBe(0); @@ -98,7 +98,7 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $originalContent = file_get_contents($installedFile); expect($originalContent)->toBeString(); @@ -106,12 +106,12 @@ file_put_contents($installedFile, 'modified content'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(file_get_contents($installedFile))->toBe('modified content'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--force']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--force']); ob_end_clean(); expect(file_get_contents($installedFile))->toBe($originalContent); } finally { @@ -135,12 +135,12 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(file_get_contents($installedFile))->toBe('my project-specific content'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--force']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--force']); ob_end_clean(); expect(file_get_contents($installedFile))->toBe('my project-specific content'); } finally { @@ -166,7 +166,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--symlink']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--symlink']); ob_end_clean(); $target = $root . '/.cursor/rules/php/core-standards.mdc'; @@ -190,7 +190,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $target = $root . '/.cursor/rules/php/core-standards.mdc'; @@ -221,7 +221,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--symlink']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--symlink']); ob_end_clean(); $target = $root . '/.cursor/rules/php/core-standards.mdc'; @@ -251,7 +251,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $installedFile = $root . '/.cursor/rules/laravel/architecture.mdc'; @@ -275,7 +275,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(is_file($root . '/.cursor/rules/php/core-standards.mdc'))->toBeTrue(); @@ -307,7 +307,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=all']); + Installer::run(['agent-skills', 'install', '--editor=all']); ob_end_clean(); foreach (InstallerPath::resolveSkillsTargetDirectories($root, InstallerPath::EDITOR_ALL) as $targetDir) { @@ -328,7 +328,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $installedSkill = $root . '/.cursor/skills/code-review/SKILL.md'; @@ -355,7 +355,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $installedSkill = $root . '/.cursor/skills/code-review/SKILL.md'; @@ -402,7 +402,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=all']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=all']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -438,7 +438,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_get_clean(); expect($exitCode)->toBe(1); @@ -461,7 +461,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor', '--force']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor', '--force']); ob_get_clean(); expect($exitCode)->toBe(1); @@ -485,7 +485,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_get_clean(); expect($exitCode)->toBe(1); @@ -506,7 +506,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $securityDir = $root . '/.cursor/rules/security'; @@ -537,7 +537,7 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $securityFile = $root . '/.cursor/rules/security/backend.md'; @@ -547,7 +547,7 @@ file_put_contents($regularFile, 'old rules content'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(file_get_contents($securityFile))->toBe($originalSecurityContent); @@ -569,7 +569,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); ob_end_clean(); expect(is_file($root . '/.claude/rules/php/core-standards.mdc'))->toBeTrue(); @@ -596,14 +596,14 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=claude']); + Installer::run(['agent-skills', 'install', '--editor=claude']); ob_end_clean(); $ruleFile = $root . '/.claude/rules/php/core-standards.mdc'; file_put_contents($ruleFile, 'old rules'); ob_start(); - Installer::run(['cursor-rules', 'install', '--force--editor=claude']); + Installer::run(['agent-skills', 'install', '--force--editor=claude']); ob_end_clean(); expect(file_get_contents($ruleFile))->toBe($originalContent); @@ -626,7 +626,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=codex']); + Installer::run(['agent-skills', 'install', '--editor=codex']); ob_end_clean(); expect(is_file($root . '/.codex/rules/php/core-standards.mdc'))->toBeTrue(); @@ -657,7 +657,7 @@ try { chdir($packageRoot); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); $output = (string) ob_get_clean(); expect($exitCode)->toBe(0); @@ -691,7 +691,7 @@ chdir($root); ob_start(); set_error_handler(static fn (): bool => true); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor']); restore_error_handler(); ob_get_clean(); @@ -726,7 +726,7 @@ chdir($root); ob_start(); set_error_handler(static fn (): bool => true); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor', '--force']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor', '--force']); restore_error_handler(); ob_get_clean(); @@ -750,7 +750,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); $installedScripts = [ @@ -775,7 +775,7 @@ $root = installerCreateProjectRoot(); file_put_contents($root . '/composer.json', json_encode([ 'extra' => [ - 'cursor-rules' => [ + 'agent-skills' => [ 'editor' => 'claude', ], ], @@ -786,7 +786,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(is_file($root . '/.cursor/rules/php/core-standards.mdc'))->toBeTrue(); @@ -809,7 +809,7 @@ try { chdir($root); ob_start(); - $exitCode = Installer::run(['cursor-rules', 'install', '--editor=cursor', '--prune']); + $exitCode = Installer::run(['agent-skills', 'install', '--editor=cursor', '--prune']); ob_end_clean(); expect($exitCode)->toBe(0); @@ -838,7 +838,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=all']); + Installer::run(['agent-skills', 'install', '--editor=all']); ob_end_clean(); foreach (['.cursor', '.claude', '.codex'] as $editorDir) { diff --git a/tests/Installer/InstallerFailureTest.php b/tests/Installer/InstallerFailureTest.php index b6238fb8..5c3f0e22 100644 --- a/tests/Installer/InstallerFailureTest.php +++ b/tests/Installer/InstallerFailureTest.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -use Pekral\CursorRules\InstallerFailure; +use AgenticVibes\AgentSkills\InstallerFailure; test('InstallerFailure missingSource creates exception with correct message', function (): void { $exception = InstallerFailure::missingSource('/dev/path', '/vendor/path'); diff --git a/tests/Installer/PrunerTest.php b/tests/Installer/PrunerTest.php index 6c48a7ab..b7b95044 100644 --- a/tests/Installer/PrunerTest.php +++ b/tests/Installer/PrunerTest.php @@ -2,8 +2,8 @@ declare(strict_types = 1); -use Pekral\CursorRules\Installer; -use Pekral\CursorRules\InstallerPruner; +use AgenticVibes\AgentSkills\Installer; +use AgenticVibes\AgentSkills\InstallerPruner; test('install with prune removes files from target that no longer exist in source', function (): void { $root = installerCreateProjectRoot(); @@ -14,13 +14,13 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); installerWriteFile($root . '/.cursor/skills/orphaned-skill/SKILL.md', 'orphaned content'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--prune']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--prune']); ob_end_clean(); expect(is_file($root . '/.cursor/skills/code-review/SKILL.md'))->toBeTrue(); @@ -44,7 +44,7 @@ try { chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); expect(is_file($root . '/.cursor/skills/orphaned-skill/SKILL.md'))->toBeTrue(); @@ -66,13 +66,13 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); installerWriteFile($root . '/.cursor/rules/removed.mdc', 'removed rule'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--prune']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--prune']); ob_end_clean(); expect(is_file($root . '/.cursor/rules/php/core-standards.mdc'))->toBeTrue(); @@ -95,13 +95,13 @@ chdir($root); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor']); + Installer::run(['agent-skills', 'install', '--editor=cursor']); ob_end_clean(); installerWriteFile($root . '/.cursor/skills/drop-skill/SKILL.md', 'drop'); ob_start(); - Installer::run(['cursor-rules', 'install', '--editor=cursor', '--prune']); + Installer::run(['agent-skills', 'install', '--editor=cursor', '--prune']); $output = (string) ob_get_clean(); expect($output)->toContain('1 pruned'); diff --git a/tests/Installer/ResolvePathsTest.php b/tests/Installer/ResolvePathsTest.php index 19030ecf..578f1d3d 100644 --- a/tests/Installer/ResolvePathsTest.php +++ b/tests/Installer/ResolvePathsTest.php @@ -2,7 +2,7 @@ declare(strict_types = 1); -use Pekral\CursorRules\InstallerPath; +use AgenticVibes\AgentSkills\InstallerPath; test('resolveRulesSource always uses package directory', function (): void { $root = installerCreateProjectRoot(); diff --git a/tests/Installer/SkillsContentTest.php b/tests/Installer/SkillsContentTest.php index 5e37c030..d882013a 100644 --- a/tests/Installer/SkillsContentTest.php +++ b/tests/Installer/SkillsContentTest.php @@ -146,7 +146,7 @@ expect($content)->toContain('Report **only Critical**'); expect($content)->toContain('must not** write any output to disk'); expect($content)->toContain('No files were created on disk'); - expect($content)->not->toContain('.cursor-rules-reports'); + expect($content)->not->toContain('.agent-skills-reports'); }); test('assignment-compliance-check returns markdown to the caller without publishing on its own', function (): void { diff --git a/tests/Pest.php b/tests/Pest.php index ea4ede23..152422cc 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -13,7 +13,7 @@ function installerEnsureDirectory(string $directory): void function installerCreateProjectRoot(): string { - $root = sys_get_temp_dir() . '/cursor-rules-' . bin2hex(random_bytes(4)); + $root = sys_get_temp_dir() . '/agent-skills-' . bin2hex(random_bytes(4)); installerEnsureDirectory($root); file_put_contents($root . '/composer.json', '{}');