Skip to content

Commit 967066c

Browse files
committed
Updated refactor.md + adjusted Paths for refactor.md
1 parent 9b227dc commit 967066c

8 files changed

Lines changed: 196 additions & 13 deletions

File tree

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,23 @@ If your editor supports MCP (Model Context Protocol), configure it to use the Bo
6060

6161
To run a full codebase refactor against all guidelines:
6262

63-
1. Ensure `.cursor/commands/` exists in your project
64-
2. Copy the refactor prompt:
65-
- From package: `cp vendor/codebar-ag/coding-guidelines/refactor.md .cursor/commands/refactor.md`
66-
- Or from guidelines (if synced): `cp guidelines/refactor.md .cursor/commands/refactor.md`
63+
1. Install or update this package with Composer
64+
2. The package automatically syncs `refactor.md` to `.cursor/commands/refactor.md`
6765
3. In Cursor, type `/refactor` and run the command
6866

6967
The prompt instructs the AI to discover all skills, map them to your codebase, analyze for violations, and refactor for full compliance. Use it when newly adopting guidelines or to cross-check AI implementations.
7068

69+
If Composer plugins are restricted in your environment, run the fallback command manually:
70+
71+
```bash
72+
php artisan guidelines:sync-refactor-command
73+
```
74+
75+
You may need to allow the plugin once in Composer config:
76+
`composer config allow-plugins.codebar-ag/coding-guidelines true`
77+
78+
If `/refactor` does not appear in Cursor immediately, reload the Cursor window once.
79+
7180
### Step 6: Override skills locally (optional)
7281

7382
To customize a skill for your project, create a file at `.ai/skills/{skill-name}/SKILL.md`. Your local version takes precedence over the package default.
@@ -94,6 +103,8 @@ To sync the complete guidelines repo (`.github/prompts`, `RULES.md`, etc.) into
94103

95104
Then run `composer sync-guidelines`. This clones or pulls the repo into `guidelines/` at your project root and copies `refactor.md` to `.cursor/commands/refactor.md` for use with Cursor slash commands.
96105

106+
This sync script is optional and mainly useful when you mirror the full guidelines repo; normal package install/update already auto-syncs `/refactor`.
107+
97108
---
98109

99110
## How It Works

composer.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"name": "codebar-ag/coding-guidelines",
3+
"type": "composer-plugin",
34
"description": "Shared Laravel coding guidelines and skills for codebar-ag projects. Composer package for Laravel Boost.",
45
"keywords": [
56
"laravel",
@@ -10,6 +11,8 @@
1011
],
1112
"license": "MIT",
1213
"require": {
14+
"composer-plugin-api": "^2.0",
15+
"illuminate/console": "^12.0",
1316
"illuminate/support": "^12.0",
1417
"php": "^8.4"
1518
},
@@ -27,6 +30,7 @@
2730
}
2831
},
2932
"extra": {
33+
"class": "CodebarAg\\CodingGuidelines\\Composer\\RefactorCommandPlugin",
3034
"laravel": {
3135
"providers": [
3236
"CodebarAg\\CodingGuidelines\\CodingGuidelinesServiceProvider"
@@ -41,5 +45,8 @@
4145
"sort-packages": true
4246
},
4347
"minimum-stability": "dev",
44-
"prefer-stable": true
48+
"prefer-stable": true,
49+
"require-dev": {
50+
"laravel/pint": "^1.27"
51+
}
4552
}

refactor.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ You are refactoring a Laravel codebase to achieve 100% alignment with **codebar-
88

99
Locate skills from one of these paths (check in order; first existing wins):
1010

11+
- `resources/boost/skills/` (when running inside this guidelines package repo)
1112
- `vendor/codebar-ag/coding-guidelines/resources/boost/skills/`
1213
- `guidelines/resources/boost/skills/`
1314

15+
If none exist, search the workspace for `**/resources/boost/skills/*/SKILL.md` and use the nearest matching root as the active skill source path.
16+
1417
Project overrides in `.ai/skills/{skill-name}/SKILL.md` take precedence over package defaults.
1518

1619
**Action:** List all `SKILL.md` files. For each skill, extract:
@@ -63,6 +66,15 @@ For each skill in order:
6366

6467
## 5. Output Format
6568

69+
### Phase 0: Plan and Confirm (mandatory)
70+
71+
Before making any code changes:
72+
73+
- Enter planning mode and produce a concrete implementation plan.
74+
- Include: applicable skills, target files, prioritized violation groups, and intended edit batches.
75+
- Present the plan to the user and request explicit approval.
76+
- Do not edit files, run formatters, or execute write operations until the user confirms the plan.
77+
6678
### Phase A: Compliance Report (before edits)
6779

6880
Produce a concise report:

scripts/validate-skills.php

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,31 @@
1212
*
1313
* Exits with code 1 if any skill is invalid.
1414
*/
15-
16-
$skillsDir = __DIR__ . '/../resources/boost/skills';
17-
if (!is_dir($skillsDir)) {
15+
$skillsDir = __DIR__.'/../resources/boost/skills';
16+
if (! is_dir($skillsDir)) {
1817
fwrite(STDERR, "Skills directory not found: {$skillsDir}\n");
1918
exit(1);
2019
}
2120

22-
$skillDirs = glob($skillsDir . '/*', GLOB_ONLYDIR);
21+
$skillDirs = glob($skillsDir.'/*', GLOB_ONLYDIR);
2322
$errors = [];
2423
$expectedCount = 36;
2524

2625
foreach ($skillDirs as $dir) {
27-
$skillFile = $dir . '/SKILL.md';
26+
$skillFile = $dir.'/SKILL.md';
2827
$skillName = basename($dir);
2928

30-
if (!file_exists($skillFile)) {
29+
if (! file_exists($skillFile)) {
3130
$errors[] = "{$skillName}: Missing SKILL.md";
31+
3232
continue;
3333
}
3434

3535
$content = file_get_contents($skillFile);
3636

37-
if (!preg_match('/^---\s*\n(.*?)\n---\s*\n/s', $content, $matches)) {
37+
if (! preg_match('/^---\s*\n(.*?)\n---\s*\n/s', $content, $matches)) {
3838
$errors[] = "{$skillName}: Missing or invalid YAML frontmatter (must start with --- and end with ---)";
39+
3940
continue;
4041
}
4142

@@ -56,7 +57,7 @@
5657
}
5758
}
5859

59-
if (!empty($errors)) {
60+
if (! empty($errors)) {
6061
foreach ($errors as $err) {
6162
fwrite(STDERR, "ERROR: {$err}\n");
6263
}

src/CodingGuidelinesServiceProvider.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace CodebarAg\CodingGuidelines;
66

7+
use CodebarAg\CodingGuidelines\Console\SyncRefactorCommand;
78
use Illuminate\Support\ServiceProvider;
89

910
class CodingGuidelinesServiceProvider extends ServiceProvider
@@ -23,5 +24,10 @@ public function boot(): void
2324
{
2425
// Skills are auto-discovered by Laravel Boost from resources/boost/skills/
2526
// No registration needed — Boost v2.2+ reads vendor/{pkg}/resources/boost/skills/
27+
if ($this->app->runningInConsole()) {
28+
$this->commands([
29+
SyncRefactorCommand::class,
30+
]);
31+
}
2632
}
2733
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodebarAg\CodingGuidelines\Composer;
6+
7+
use CodebarAg\CodingGuidelines\Support\RefactorCommandSynchronizer;
8+
use Composer\Composer;
9+
use Composer\EventDispatcher\EventSubscriberInterface;
10+
use Composer\IO\IOInterface;
11+
use Composer\Plugin\PluginInterface;
12+
use Composer\Script\Event;
13+
use Composer\Script\ScriptEvents;
14+
15+
final class RefactorCommandPlugin implements EventSubscriberInterface, PluginInterface
16+
{
17+
private ?Composer $composer = null;
18+
19+
private ?IOInterface $io = null;
20+
21+
public function activate(Composer $composer, IOInterface $io): void
22+
{
23+
$this->composer = $composer;
24+
$this->io = $io;
25+
}
26+
27+
public function deactivate(Composer $composer, IOInterface $io): void
28+
{
29+
// Nothing to clean up.
30+
}
31+
32+
public function uninstall(Composer $composer, IOInterface $io): void
33+
{
34+
// Keep user's .cursor/commands/refactor.md file untouched on uninstall.
35+
}
36+
37+
/**
38+
* @return array<string, string>
39+
*/
40+
public static function getSubscribedEvents(): array
41+
{
42+
return [
43+
ScriptEvents::POST_INSTALL_CMD => 'syncRefactorCommand',
44+
ScriptEvents::POST_UPDATE_CMD => 'syncRefactorCommand',
45+
ScriptEvents::POST_AUTOLOAD_DUMP => 'syncRefactorCommand',
46+
];
47+
}
48+
49+
public function syncRefactorCommand(Event $event): void
50+
{
51+
$composer = $this->composer ?? $event->getComposer();
52+
$io = $this->io ?? $event->getIO();
53+
54+
$vendorDir = (string) $composer->getConfig()->get('vendor-dir');
55+
$projectRoot = dirname($vendorDir);
56+
$packageRoot = dirname(__DIR__, 2);
57+
58+
RefactorCommandSynchronizer::sync($projectRoot, $packageRoot, $io);
59+
}
60+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodebarAg\CodingGuidelines\Console;
6+
7+
use CodebarAg\CodingGuidelines\Support\RefactorCommandSynchronizer;
8+
use Illuminate\Console\Command;
9+
10+
final class SyncRefactorCommand extends Command
11+
{
12+
protected $signature = 'guidelines:sync-refactor-command';
13+
14+
protected $description = 'Sync .cursor/commands/refactor.md from the coding-guidelines package';
15+
16+
public function handle(): int
17+
{
18+
$projectRoot = getcwd() ?: '.';
19+
$packageRoot = dirname(__DIR__, 2);
20+
21+
$synced = RefactorCommandSynchronizer::sync($projectRoot, $packageRoot);
22+
23+
if ($synced) {
24+
$this->info('Refactor command is ready at .cursor/commands/refactor.md');
25+
26+
return 0;
27+
}
28+
29+
$this->warn('Could not sync refactor command.');
30+
31+
return 1;
32+
}
33+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace CodebarAg\CodingGuidelines\Support;
6+
7+
final class RefactorCommandSynchronizer
8+
{
9+
public static function sync(string $projectRoot, string $packageRoot, mixed $io = null): bool
10+
{
11+
$source = $packageRoot.'/refactor.md';
12+
$commandsDir = $projectRoot.'/.cursor/commands';
13+
$destination = $commandsDir.'/refactor.md';
14+
15+
if (! is_file($source)) {
16+
self::write($io, '<warning>[coding-guidelines]</warning> refactor.md not found, skipping sync.');
17+
18+
return false;
19+
}
20+
21+
if (! is_dir($commandsDir) && ! mkdir($commandsDir, 0755, true) && ! is_dir($commandsDir)) {
22+
self::write($io, '<warning>[coding-guidelines]</warning> Could not create .cursor/commands directory.');
23+
24+
return false;
25+
}
26+
27+
$sourceHash = hash_file('sha256', $source);
28+
$destinationHash = is_file($destination) ? hash_file('sha256', $destination) : null;
29+
30+
if ($sourceHash === $destinationHash) {
31+
self::write($io, '<info>[coding-guidelines]</info> /refactor command already up to date.');
32+
33+
return true;
34+
}
35+
36+
if (! copy($source, $destination)) {
37+
self::write($io, '<warning>[coding-guidelines]</warning> Failed to copy refactor.md.');
38+
39+
return false;
40+
}
41+
42+
self::write($io, '<info>[coding-guidelines]</info> Synced /refactor command to .cursor/commands/refactor.md');
43+
44+
return true;
45+
}
46+
47+
private static function write(mixed $io, string $message): void
48+
{
49+
if (is_object($io) && method_exists($io, 'write')) {
50+
$io->write($message);
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)