rulesync 6.6.3 → 6.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -48
- package/dist/index.cjs +88 -139
- package/dist/index.js +88 -139
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,26 +118,29 @@ Get-FileHash rulesync.exe -Algorithm SHA256 | ForEach-Object {
|
|
|
118
118
|
## Getting Started
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
|
+
# Install rulesync globally
|
|
122
|
+
npm install -g rulesync
|
|
123
|
+
|
|
121
124
|
# Create necessary directories, sample rule files, and configuration file
|
|
122
|
-
|
|
125
|
+
rulesync init
|
|
123
126
|
|
|
124
127
|
# Install official skills (recommended)
|
|
125
|
-
|
|
128
|
+
rulesync fetch dyoshikawa/rulesync --features skills
|
|
126
129
|
```
|
|
127
130
|
|
|
128
131
|
On the other hand, if you already have AI tool configurations:
|
|
129
132
|
|
|
130
133
|
```bash
|
|
131
134
|
# Import existing files (to .rulesync/**/*)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
rulesync import --targets claudecode # From CLAUDE.md
|
|
136
|
+
rulesync import --targets cursor # From .cursorrules
|
|
137
|
+
rulesync import --targets copilot # From .github/copilot-instructions.md
|
|
138
|
+
rulesync import --targets claudecode --features rules,mcp,commands,subagents
|
|
136
139
|
|
|
137
140
|
# And more tool supports
|
|
138
141
|
|
|
139
142
|
# Generate unified configurations with all features
|
|
140
|
-
|
|
143
|
+
rulesync generate --targets "*" --features "*"
|
|
141
144
|
```
|
|
142
145
|
|
|
143
146
|
## Supported Tools and Features
|
|
@@ -211,46 +214,46 @@ Rulesync is trusted by leading companies and recognized by the industry:
|
|
|
211
214
|
|
|
212
215
|
```bash
|
|
213
216
|
# Initialize new project (recommended: organized rules structure)
|
|
214
|
-
|
|
217
|
+
rulesync init
|
|
215
218
|
|
|
216
219
|
# Import existing configurations (to .rulesync/rules/ by default)
|
|
217
|
-
|
|
220
|
+
rulesync import --targets claudecode --features rules,ignore,mcp,commands,subagents,skills
|
|
218
221
|
|
|
219
222
|
# Fetch configurations from a Git repository
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
+
rulesync fetch owner/repo
|
|
224
|
+
rulesync fetch owner/repo@v1.0.0 --features rules,commands
|
|
225
|
+
rulesync fetch https://github.com/owner/repo --conflict skip
|
|
223
226
|
|
|
224
227
|
# Generate all features for all tools (new preferred syntax)
|
|
225
|
-
|
|
228
|
+
rulesync generate --targets "*" --features "*"
|
|
226
229
|
|
|
227
230
|
# Generate specific features for specific tools
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
rulesync generate --targets copilot,cursor,cline --features rules,mcp
|
|
232
|
+
rulesync generate --targets claudecode --features rules,subagents
|
|
230
233
|
|
|
231
234
|
# Generate only rules (no MCP, ignore files, commands, or subagents)
|
|
232
|
-
|
|
235
|
+
rulesync generate --targets "*" --features rules
|
|
233
236
|
|
|
234
237
|
# Generate simulated commands and subagents
|
|
235
|
-
|
|
238
|
+
rulesync generate --targets copilot,cursor,codexcli --features commands,subagents --simulate-commands --simulate-subagents
|
|
236
239
|
|
|
237
240
|
# Dry run: show changes without writing files
|
|
238
|
-
|
|
241
|
+
rulesync generate --dry-run --targets claudecode --features rules
|
|
239
242
|
|
|
240
243
|
# Check if files are up to date (for CI/CD pipelines)
|
|
241
|
-
|
|
244
|
+
rulesync generate --check --targets "*" --features "*"
|
|
242
245
|
|
|
243
246
|
# Add generated files to .gitignore
|
|
244
|
-
|
|
247
|
+
rulesync gitignore
|
|
245
248
|
|
|
246
249
|
# Update rulesync to the latest version (single-binary installs)
|
|
247
|
-
|
|
250
|
+
rulesync update
|
|
248
251
|
|
|
249
252
|
# Check for updates without installing
|
|
250
|
-
|
|
253
|
+
rulesync update --check
|
|
251
254
|
|
|
252
255
|
# Force update even if already at latest version
|
|
253
|
-
|
|
256
|
+
rulesync update --force
|
|
254
257
|
```
|
|
255
258
|
|
|
256
259
|
## Dry Run
|
|
@@ -262,7 +265,7 @@ Rulesync provides two dry run options for the `generate` command that allow you
|
|
|
262
265
|
Show what would be written or deleted without actually writing any files. Changes are displayed with a `[DRY RUN]` prefix.
|
|
263
266
|
|
|
264
267
|
```bash
|
|
265
|
-
|
|
268
|
+
rulesync generate --dry-run --targets claudecode --features rules
|
|
266
269
|
```
|
|
267
270
|
|
|
268
271
|
### `--check`
|
|
@@ -271,7 +274,7 @@ Same as `--dry-run`, but exits with code 1 if files are not up to date. This is
|
|
|
271
274
|
|
|
272
275
|
```bash
|
|
273
276
|
# In your CI pipeline
|
|
274
|
-
|
|
277
|
+
rulesync generate --check --targets "*" --features "*"
|
|
275
278
|
echo $? # 0 if up to date, 1 if changes needed
|
|
276
279
|
```
|
|
277
280
|
|
|
@@ -291,20 +294,20 @@ The `fetch` command allows you to fetch configuration files directly from a Git
|
|
|
291
294
|
|
|
292
295
|
```bash
|
|
293
296
|
# Full URL format
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
297
|
+
rulesync fetch https://github.com/owner/repo
|
|
298
|
+
rulesync fetch https://github.com/owner/repo/tree/branch
|
|
299
|
+
rulesync fetch https://github.com/owner/repo/tree/branch/path/to/subdir
|
|
300
|
+
rulesync fetch https://gitlab.com/owner/repo # GitLab (planned)
|
|
298
301
|
|
|
299
302
|
# Prefix format
|
|
300
|
-
|
|
301
|
-
|
|
303
|
+
rulesync fetch github:owner/repo
|
|
304
|
+
rulesync fetch gitlab:owner/repo # GitLab (planned)
|
|
302
305
|
|
|
303
306
|
# Shorthand format (defaults to GitHub)
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
307
|
+
rulesync fetch owner/repo
|
|
308
|
+
rulesync fetch owner/repo@ref # Specify branch/tag/commit
|
|
309
|
+
rulesync fetch owner/repo:path # Specify subdirectory
|
|
310
|
+
rulesync fetch owner/repo@ref:path # Both ref and path
|
|
308
311
|
```
|
|
309
312
|
|
|
310
313
|
### Options
|
|
@@ -323,27 +326,27 @@ npx rulesync fetch owner/repo@ref:path # Both ref and path
|
|
|
323
326
|
|
|
324
327
|
```bash
|
|
325
328
|
# Fetch skills from external repositories
|
|
326
|
-
|
|
327
|
-
|
|
329
|
+
rulesync fetch vercel-labs/agent-skills --features skills
|
|
330
|
+
rulesync fetch anthropics/skills --features skills
|
|
328
331
|
|
|
329
332
|
# Fetch all features from a public repository
|
|
330
|
-
|
|
333
|
+
rulesync fetch dyoshikawa/rulesync --path .rulesync
|
|
331
334
|
|
|
332
335
|
# Fetch only rules and commands from a specific tag
|
|
333
|
-
|
|
336
|
+
rulesync fetch owner/repo@v1.0.0 --features rules,commands
|
|
334
337
|
|
|
335
338
|
# Fetch from a private repository (uses GITHUB_TOKEN env var)
|
|
336
339
|
export GITHUB_TOKEN=ghp_xxxx
|
|
337
|
-
|
|
340
|
+
rulesync fetch owner/private-repo
|
|
338
341
|
|
|
339
342
|
# Or use GitHub CLI to get the token
|
|
340
|
-
GITHUB_TOKEN=$(gh auth token)
|
|
343
|
+
GITHUB_TOKEN=$(gh auth token) rulesync fetch owner/private-repo
|
|
341
344
|
|
|
342
345
|
# Preserve existing files (skip conflicts)
|
|
343
|
-
|
|
346
|
+
rulesync fetch owner/repo --conflict skip
|
|
344
347
|
|
|
345
348
|
# Fetch from a monorepo subdirectory
|
|
346
|
-
|
|
349
|
+
rulesync fetch owner/repo:packages/my-package
|
|
347
350
|
```
|
|
348
351
|
|
|
349
352
|
## Configuration
|
|
@@ -695,7 +698,7 @@ Currently, supports rules and commands generation for Claude Code. Import for gl
|
|
|
695
698
|
2. Initialize files for global files in the directory.
|
|
696
699
|
```bash
|
|
697
700
|
cd ~/.aiglobal
|
|
698
|
-
|
|
701
|
+
rulesync init
|
|
699
702
|
```
|
|
700
703
|
3. Edit `~/.aiglobal/rulesync.jsonc` to enable global mode.
|
|
701
704
|
```jsonc
|
|
@@ -718,7 +721,7 @@ Currently, supports rules and commands generation for Claude Code. Import for gl
|
|
|
718
721
|
5. Generate rules for global settings.
|
|
719
722
|
```bash
|
|
720
723
|
# Run in the `~/.aiglobal` directory
|
|
721
|
-
|
|
724
|
+
rulesync generate
|
|
722
725
|
```
|
|
723
726
|
|
|
724
727
|
> [!NOTE]
|
|
@@ -735,7 +738,7 @@ Simulated commands, subagents and skills allow you to generate simulated feature
|
|
|
735
738
|
1. Prepare `.rulesync/commands/*.md`, `.rulesync/subagents/*.md` and `.rulesync/skills/*/SKILL.md` for your purposes.
|
|
736
739
|
2. Generate simulated commands, subagents and skills for specific tools that are included in cursor, codexcli and etc.
|
|
737
740
|
```bash
|
|
738
|
-
|
|
741
|
+
rulesync generate \
|
|
739
742
|
--targets copilot,cursor,codexcli \
|
|
740
743
|
--features commands,subagents,skills \
|
|
741
744
|
--simulate-commands \
|
|
@@ -762,7 +765,7 @@ Rulesync supports compressing tokens consumed by MCP servers [d-kimuson/modular-
|
|
|
762
765
|
|
|
763
766
|
```bash
|
|
764
767
|
# Enable modular-mcp via CLI
|
|
765
|
-
|
|
768
|
+
rulesync generate --targets claudecode --features mcp --modular-mcp
|
|
766
769
|
|
|
767
770
|
# Or via configuration file
|
|
768
771
|
{
|
|
@@ -906,7 +909,7 @@ So, in this case, approximately 92% reduction in MCP tools consumption!
|
|
|
906
909
|
Rulesync provides official skills that you can install using the fetch command:
|
|
907
910
|
|
|
908
911
|
```bash
|
|
909
|
-
|
|
912
|
+
rulesync fetch dyoshikawa/rulesync --features skills
|
|
910
913
|
```
|
|
911
914
|
|
|
912
915
|
This will install the Rulesync documentation skill to your project.
|
package/dist/index.cjs
CHANGED
|
@@ -321,16 +321,22 @@ var FeatureProcessor = class {
|
|
|
321
321
|
* Returns the number of files written.
|
|
322
322
|
*/
|
|
323
323
|
async writeAiFiles(aiFiles) {
|
|
324
|
+
let changedCount = 0;
|
|
324
325
|
for (const aiFile of aiFiles) {
|
|
325
326
|
const filePath = aiFile.getFilePath();
|
|
327
|
+
const contentWithNewline = addTrailingNewline(aiFile.getFileContent());
|
|
328
|
+
const existingContent = await readFileContentOrNull(filePath);
|
|
329
|
+
if (existingContent === contentWithNewline) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
326
332
|
if (this.dryRun) {
|
|
327
333
|
logger.info(`[DRY RUN] Would write: ${filePath}`);
|
|
328
334
|
} else {
|
|
329
|
-
const contentWithNewline = addTrailingNewline(aiFile.getFileContent());
|
|
330
335
|
await writeFileContent(filePath, contentWithNewline);
|
|
331
336
|
}
|
|
337
|
+
changedCount++;
|
|
332
338
|
}
|
|
333
|
-
return
|
|
339
|
+
return changedCount;
|
|
334
340
|
}
|
|
335
341
|
async removeAiFiles(aiFiles) {
|
|
336
342
|
for (const aiFile of aiFiles) {
|
|
@@ -352,6 +358,7 @@ var FeatureProcessor = class {
|
|
|
352
358
|
await removeFile(filePath);
|
|
353
359
|
}
|
|
354
360
|
}
|
|
361
|
+
return orphanFiles.length;
|
|
355
362
|
}
|
|
356
363
|
};
|
|
357
364
|
|
|
@@ -5445,7 +5452,7 @@ var OpencodeMcp = class _OpencodeMcp extends ToolMcp {
|
|
|
5445
5452
|
static getSettablePaths({ global } = {}) {
|
|
5446
5453
|
if (global) {
|
|
5447
5454
|
return {
|
|
5448
|
-
relativeDirPath: ".",
|
|
5455
|
+
relativeDirPath: (0, import_node_path48.join)(".config", "opencode"),
|
|
5449
5456
|
relativeFilePath: "opencode.json"
|
|
5450
5457
|
};
|
|
5451
5458
|
}
|
|
@@ -6422,37 +6429,71 @@ var DirFeatureProcessor = class {
|
|
|
6422
6429
|
/**
|
|
6423
6430
|
* Once converted to rulesync/tool dirs, write them to the filesystem.
|
|
6424
6431
|
* Returns the number of directories written.
|
|
6432
|
+
*
|
|
6433
|
+
* Note: This method uses directory-level change detection. If any file within
|
|
6434
|
+
* a directory has changed, ALL files in that directory are rewritten. This is
|
|
6435
|
+
* an intentional design decision to ensure consistency within directory units.
|
|
6425
6436
|
*/
|
|
6426
6437
|
async writeAiDirs(aiDirs) {
|
|
6438
|
+
let changedCount = 0;
|
|
6427
6439
|
for (const aiDir of aiDirs) {
|
|
6428
6440
|
const dirPath = aiDir.getDirPath();
|
|
6441
|
+
let dirHasChanges = false;
|
|
6442
|
+
const mainFile = aiDir.getMainFile();
|
|
6443
|
+
let mainFileContent;
|
|
6444
|
+
if (mainFile) {
|
|
6445
|
+
const mainFilePath = (0, import_node_path56.join)(dirPath, mainFile.name);
|
|
6446
|
+
const content = stringifyFrontmatter(mainFile.body, mainFile.frontmatter);
|
|
6447
|
+
mainFileContent = addTrailingNewline(content);
|
|
6448
|
+
const existingContent = await readFileContentOrNull(mainFilePath);
|
|
6449
|
+
if (existingContent !== mainFileContent) {
|
|
6450
|
+
dirHasChanges = true;
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
const otherFiles = aiDir.getOtherFiles();
|
|
6454
|
+
const otherFileContents = [];
|
|
6455
|
+
for (const file of otherFiles) {
|
|
6456
|
+
const contentWithNewline = addTrailingNewline(file.fileBuffer.toString("utf-8"));
|
|
6457
|
+
otherFileContents.push(contentWithNewline);
|
|
6458
|
+
if (!dirHasChanges) {
|
|
6459
|
+
const filePath = (0, import_node_path56.join)(dirPath, file.relativeFilePathToDirPath);
|
|
6460
|
+
const existingContent = await readFileContentOrNull(filePath);
|
|
6461
|
+
if (existingContent !== contentWithNewline) {
|
|
6462
|
+
dirHasChanges = true;
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
}
|
|
6466
|
+
if (!dirHasChanges) {
|
|
6467
|
+
continue;
|
|
6468
|
+
}
|
|
6429
6469
|
if (this.dryRun) {
|
|
6430
6470
|
logger.info(`[DRY RUN] Would create directory: ${dirPath}`);
|
|
6431
|
-
const mainFile = aiDir.getMainFile();
|
|
6432
6471
|
if (mainFile) {
|
|
6433
6472
|
logger.info(`[DRY RUN] Would write: ${(0, import_node_path56.join)(dirPath, mainFile.name)}`);
|
|
6434
6473
|
}
|
|
6435
|
-
for (const file of
|
|
6474
|
+
for (const file of otherFiles) {
|
|
6436
6475
|
logger.info(`[DRY RUN] Would write: ${(0, import_node_path56.join)(dirPath, file.relativeFilePathToDirPath)}`);
|
|
6437
6476
|
}
|
|
6438
6477
|
} else {
|
|
6439
6478
|
await ensureDir(dirPath);
|
|
6440
|
-
|
|
6441
|
-
if (mainFile) {
|
|
6479
|
+
if (mainFile && mainFileContent) {
|
|
6442
6480
|
const mainFilePath = (0, import_node_path56.join)(dirPath, mainFile.name);
|
|
6443
|
-
|
|
6444
|
-
const contentWithNewline = addTrailingNewline(content);
|
|
6445
|
-
await writeFileContent(mainFilePath, contentWithNewline);
|
|
6481
|
+
await writeFileContent(mainFilePath, mainFileContent);
|
|
6446
6482
|
}
|
|
6447
|
-
const
|
|
6448
|
-
for (const file of otherFiles) {
|
|
6483
|
+
for (const [i, file] of otherFiles.entries()) {
|
|
6449
6484
|
const filePath = (0, import_node_path56.join)(dirPath, file.relativeFilePathToDirPath);
|
|
6450
|
-
const
|
|
6451
|
-
|
|
6485
|
+
const content = otherFileContents[i];
|
|
6486
|
+
if (content === void 0) {
|
|
6487
|
+
throw new Error(
|
|
6488
|
+
`Internal error: content for file ${file.relativeFilePathToDirPath} is undefined. This indicates a synchronization issue between otherFiles and otherFileContents arrays.`
|
|
6489
|
+
);
|
|
6490
|
+
}
|
|
6491
|
+
await writeFileContent(filePath, content);
|
|
6452
6492
|
}
|
|
6453
6493
|
}
|
|
6494
|
+
changedCount++;
|
|
6454
6495
|
}
|
|
6455
|
-
return
|
|
6496
|
+
return changedCount;
|
|
6456
6497
|
}
|
|
6457
6498
|
async removeAiDirs(aiDirs) {
|
|
6458
6499
|
for (const aiDir of aiDirs) {
|
|
@@ -6474,6 +6515,7 @@ var DirFeatureProcessor = class {
|
|
|
6474
6515
|
await removeDirectory(dirPath);
|
|
6475
6516
|
}
|
|
6476
6517
|
}
|
|
6518
|
+
return orphanDirs.length;
|
|
6477
6519
|
}
|
|
6478
6520
|
};
|
|
6479
6521
|
|
|
@@ -14701,19 +14743,13 @@ async function generateRulesCore(params) {
|
|
|
14701
14743
|
});
|
|
14702
14744
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14703
14745
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14704
|
-
if (isPreviewMode) {
|
|
14705
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14706
|
-
if (fileDiff) hasDiff = true;
|
|
14707
|
-
}
|
|
14708
14746
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14709
14747
|
totalCount += writtenCount;
|
|
14748
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14710
14749
|
if (config.getDelete()) {
|
|
14711
14750
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14712
|
-
|
|
14713
|
-
|
|
14714
|
-
if (orphanDiff) hasDiff = true;
|
|
14715
|
-
}
|
|
14716
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14751
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14752
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14717
14753
|
}
|
|
14718
14754
|
}
|
|
14719
14755
|
}
|
|
@@ -14741,26 +14777,18 @@ async function generateIgnoreCore(params) {
|
|
|
14741
14777
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14742
14778
|
if (rulesyncFiles.length > 0) {
|
|
14743
14779
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14744
|
-
if (isPreviewMode) {
|
|
14745
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14746
|
-
if (fileDiff) hasDiff = true;
|
|
14747
|
-
}
|
|
14748
14780
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14749
14781
|
totalCount += writtenCount;
|
|
14782
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14750
14783
|
if (config.getDelete()) {
|
|
14751
14784
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
if (orphanDiff) hasDiff = true;
|
|
14755
|
-
}
|
|
14756
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14785
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14786
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14757
14787
|
}
|
|
14758
14788
|
} else if (config.getDelete()) {
|
|
14759
14789
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14760
|
-
|
|
14761
|
-
|
|
14762
|
-
}
|
|
14763
|
-
await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14790
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14791
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14764
14792
|
}
|
|
14765
14793
|
} catch (error) {
|
|
14766
14794
|
logger.warn(
|
|
@@ -14795,19 +14823,13 @@ async function generateMcpCore(params) {
|
|
|
14795
14823
|
});
|
|
14796
14824
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14797
14825
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14798
|
-
if (isPreviewMode) {
|
|
14799
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14800
|
-
if (fileDiff) hasDiff = true;
|
|
14801
|
-
}
|
|
14802
14826
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14803
14827
|
totalCount += writtenCount;
|
|
14828
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14804
14829
|
if (config.getDelete()) {
|
|
14805
14830
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
if (orphanDiff) hasDiff = true;
|
|
14809
|
-
}
|
|
14810
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14831
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14832
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14811
14833
|
}
|
|
14812
14834
|
}
|
|
14813
14835
|
}
|
|
@@ -14838,19 +14860,13 @@ async function generateCommandsCore(params) {
|
|
|
14838
14860
|
});
|
|
14839
14861
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14840
14862
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14841
|
-
if (isPreviewMode) {
|
|
14842
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14843
|
-
if (fileDiff) hasDiff = true;
|
|
14844
|
-
}
|
|
14845
14863
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14846
14864
|
totalCount += writtenCount;
|
|
14865
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14847
14866
|
if (config.getDelete()) {
|
|
14848
14867
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
if (orphanDiff) hasDiff = true;
|
|
14852
|
-
}
|
|
14853
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14868
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14869
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14854
14870
|
}
|
|
14855
14871
|
}
|
|
14856
14872
|
}
|
|
@@ -14881,19 +14897,13 @@ async function generateSubagentsCore(params) {
|
|
|
14881
14897
|
});
|
|
14882
14898
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14883
14899
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14884
|
-
if (isPreviewMode) {
|
|
14885
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14886
|
-
if (fileDiff) hasDiff = true;
|
|
14887
|
-
}
|
|
14888
14900
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14889
14901
|
totalCount += writtenCount;
|
|
14902
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14890
14903
|
if (config.getDelete()) {
|
|
14891
14904
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
if (orphanDiff) hasDiff = true;
|
|
14895
|
-
}
|
|
14896
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14905
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14906
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14897
14907
|
}
|
|
14898
14908
|
}
|
|
14899
14909
|
}
|
|
@@ -14930,19 +14940,13 @@ async function generateSkillsCore(params) {
|
|
|
14930
14940
|
}
|
|
14931
14941
|
}
|
|
14932
14942
|
const toolDirs = await processor.convertRulesyncDirsToToolDirs(rulesyncDirs);
|
|
14933
|
-
if (isPreviewMode) {
|
|
14934
|
-
const dirDiff = await detectDirDiff(toolDirs);
|
|
14935
|
-
if (dirDiff) hasDiff = true;
|
|
14936
|
-
}
|
|
14937
14943
|
const writtenCount = await processor.writeAiDirs(toolDirs);
|
|
14938
14944
|
totalCount += writtenCount;
|
|
14945
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14939
14946
|
if (config.getDelete()) {
|
|
14940
14947
|
const existingToolDirs = await processor.loadToolDirsToDelete();
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
if (orphanDiff) hasDiff = true;
|
|
14944
|
-
}
|
|
14945
|
-
await processor.removeOrphanAiDirs(existingToolDirs, toolDirs);
|
|
14948
|
+
const orphanCount = await processor.removeOrphanAiDirs(existingToolDirs, toolDirs);
|
|
14949
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14946
14950
|
}
|
|
14947
14951
|
}
|
|
14948
14952
|
}
|
|
@@ -14972,77 +14976,24 @@ async function generateHooksCore(params) {
|
|
|
14972
14976
|
if (rulesyncFiles.length === 0) {
|
|
14973
14977
|
if (config.getDelete()) {
|
|
14974
14978
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
}
|
|
14978
|
-
await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14979
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14980
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14979
14981
|
}
|
|
14980
14982
|
continue;
|
|
14981
14983
|
}
|
|
14982
14984
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14983
|
-
if (isPreviewMode) {
|
|
14984
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14985
|
-
if (fileDiff) hasDiff = true;
|
|
14986
|
-
}
|
|
14987
14985
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14988
14986
|
totalCount += writtenCount;
|
|
14987
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14989
14988
|
if (config.getDelete()) {
|
|
14990
14989
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
if (orphanDiff) hasDiff = true;
|
|
14994
|
-
}
|
|
14995
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14990
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14991
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14996
14992
|
}
|
|
14997
14993
|
}
|
|
14998
14994
|
}
|
|
14999
14995
|
return { count: totalCount, hasDiff };
|
|
15000
14996
|
}
|
|
15001
|
-
async function detectFileDiff(aiFiles) {
|
|
15002
|
-
for (const aiFile of aiFiles) {
|
|
15003
|
-
const filePath = aiFile.getFilePath();
|
|
15004
|
-
const newContent = addTrailingNewline(aiFile.getFileContent());
|
|
15005
|
-
const existingContent = await readFileContentOrNull(filePath);
|
|
15006
|
-
if (existingContent !== newContent) {
|
|
15007
|
-
return true;
|
|
15008
|
-
}
|
|
15009
|
-
}
|
|
15010
|
-
return false;
|
|
15011
|
-
}
|
|
15012
|
-
async function detectOrphanFileDiff(existingFiles, generatedFiles) {
|
|
15013
|
-
const generatedPaths = new Set(generatedFiles.map((f) => f.getFilePath()));
|
|
15014
|
-
const orphanFiles = existingFiles.filter((f) => !generatedPaths.has(f.getFilePath()));
|
|
15015
|
-
return orphanFiles.length > 0;
|
|
15016
|
-
}
|
|
15017
|
-
async function detectDirDiff(aiDirs) {
|
|
15018
|
-
for (const aiDir of aiDirs) {
|
|
15019
|
-
const mainFile = aiDir.getMainFile();
|
|
15020
|
-
if (mainFile) {
|
|
15021
|
-
const mainFilePath = (0, import_node_path109.join)(aiDir.getDirPath(), mainFile.name);
|
|
15022
|
-
const newContent = addTrailingNewline(
|
|
15023
|
-
stringifyFrontmatter(mainFile.body, mainFile.frontmatter)
|
|
15024
|
-
);
|
|
15025
|
-
const existingContent = await readFileContentOrNull(mainFilePath);
|
|
15026
|
-
if (existingContent !== newContent) {
|
|
15027
|
-
return true;
|
|
15028
|
-
}
|
|
15029
|
-
}
|
|
15030
|
-
for (const file of aiDir.getOtherFiles()) {
|
|
15031
|
-
const filePath = (0, import_node_path109.join)(aiDir.getDirPath(), file.relativeFilePathToDirPath);
|
|
15032
|
-
const newContent = addTrailingNewline(file.fileBuffer.toString("utf-8"));
|
|
15033
|
-
const existingContent = await readFileContentOrNull(filePath);
|
|
15034
|
-
if (existingContent !== newContent) {
|
|
15035
|
-
return true;
|
|
15036
|
-
}
|
|
15037
|
-
}
|
|
15038
|
-
}
|
|
15039
|
-
return false;
|
|
15040
|
-
}
|
|
15041
|
-
async function detectOrphanDirDiff(existingDirs, generatedDirs) {
|
|
15042
|
-
const generatedPaths = new Set(generatedDirs.map((d) => d.getDirPath()));
|
|
15043
|
-
const orphanDirs = existingDirs.filter((d) => !generatedPaths.has(d.getDirPath()));
|
|
15044
|
-
return orphanDirs.length > 0;
|
|
15045
|
-
}
|
|
15046
14997
|
|
|
15047
14998
|
// src/utils/result.ts
|
|
15048
14999
|
function calculateTotalCount(result) {
|
|
@@ -15054,9 +15005,9 @@ function logFeatureResult(params) {
|
|
|
15054
15005
|
const { count, featureName, isPreview, modePrefix } = params;
|
|
15055
15006
|
if (count > 0) {
|
|
15056
15007
|
if (isPreview) {
|
|
15057
|
-
logger.info(`${modePrefix} Would
|
|
15008
|
+
logger.info(`${modePrefix} Would write ${count} ${featureName}`);
|
|
15058
15009
|
} else {
|
|
15059
|
-
logger.success(`
|
|
15010
|
+
logger.success(`Written ${count} ${featureName}`);
|
|
15060
15011
|
}
|
|
15061
15012
|
}
|
|
15062
15013
|
}
|
|
@@ -15151,7 +15102,7 @@ async function generateCommand(options) {
|
|
|
15151
15102
|
const totalGenerated = calculateTotalCount(result);
|
|
15152
15103
|
if (totalGenerated === 0) {
|
|
15153
15104
|
const enabledFeatures = features.join(", ");
|
|
15154
|
-
logger.
|
|
15105
|
+
logger.info(`\u2713 All files are up to date (${enabledFeatures})`);
|
|
15155
15106
|
return;
|
|
15156
15107
|
}
|
|
15157
15108
|
const parts = [];
|
|
@@ -15163,11 +15114,9 @@ async function generateCommand(options) {
|
|
|
15163
15114
|
if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
|
|
15164
15115
|
if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);
|
|
15165
15116
|
if (isPreview) {
|
|
15166
|
-
logger.info(
|
|
15167
|
-
`${modePrefix} Would generate ${totalGenerated} file(s) total (${parts.join(" + ")})`
|
|
15168
|
-
);
|
|
15117
|
+
logger.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
15169
15118
|
} else {
|
|
15170
|
-
logger.success(`\u{1F389} All done!
|
|
15119
|
+
logger.success(`\u{1F389} All done! Written ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
15171
15120
|
}
|
|
15172
15121
|
if (check) {
|
|
15173
15122
|
if (result.hasDiff) {
|
|
@@ -17593,7 +17542,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17593
17542
|
}
|
|
17594
17543
|
|
|
17595
17544
|
// src/cli/index.ts
|
|
17596
|
-
var getVersion = () => "6.
|
|
17545
|
+
var getVersion = () => "6.7.1";
|
|
17597
17546
|
var main = async () => {
|
|
17598
17547
|
const program = new import_commander.Command();
|
|
17599
17548
|
const version = getVersion();
|
package/dist/index.js
CHANGED
|
@@ -298,16 +298,22 @@ var FeatureProcessor = class {
|
|
|
298
298
|
* Returns the number of files written.
|
|
299
299
|
*/
|
|
300
300
|
async writeAiFiles(aiFiles) {
|
|
301
|
+
let changedCount = 0;
|
|
301
302
|
for (const aiFile of aiFiles) {
|
|
302
303
|
const filePath = aiFile.getFilePath();
|
|
304
|
+
const contentWithNewline = addTrailingNewline(aiFile.getFileContent());
|
|
305
|
+
const existingContent = await readFileContentOrNull(filePath);
|
|
306
|
+
if (existingContent === contentWithNewline) {
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
303
309
|
if (this.dryRun) {
|
|
304
310
|
logger.info(`[DRY RUN] Would write: ${filePath}`);
|
|
305
311
|
} else {
|
|
306
|
-
const contentWithNewline = addTrailingNewline(aiFile.getFileContent());
|
|
307
312
|
await writeFileContent(filePath, contentWithNewline);
|
|
308
313
|
}
|
|
314
|
+
changedCount++;
|
|
309
315
|
}
|
|
310
|
-
return
|
|
316
|
+
return changedCount;
|
|
311
317
|
}
|
|
312
318
|
async removeAiFiles(aiFiles) {
|
|
313
319
|
for (const aiFile of aiFiles) {
|
|
@@ -329,6 +335,7 @@ var FeatureProcessor = class {
|
|
|
329
335
|
await removeFile(filePath);
|
|
330
336
|
}
|
|
331
337
|
}
|
|
338
|
+
return orphanFiles.length;
|
|
332
339
|
}
|
|
333
340
|
};
|
|
334
341
|
|
|
@@ -5422,7 +5429,7 @@ var OpencodeMcp = class _OpencodeMcp extends ToolMcp {
|
|
|
5422
5429
|
static getSettablePaths({ global } = {}) {
|
|
5423
5430
|
if (global) {
|
|
5424
5431
|
return {
|
|
5425
|
-
relativeDirPath: ".",
|
|
5432
|
+
relativeDirPath: join47(".config", "opencode"),
|
|
5426
5433
|
relativeFilePath: "opencode.json"
|
|
5427
5434
|
};
|
|
5428
5435
|
}
|
|
@@ -6399,37 +6406,71 @@ var DirFeatureProcessor = class {
|
|
|
6399
6406
|
/**
|
|
6400
6407
|
* Once converted to rulesync/tool dirs, write them to the filesystem.
|
|
6401
6408
|
* Returns the number of directories written.
|
|
6409
|
+
*
|
|
6410
|
+
* Note: This method uses directory-level change detection. If any file within
|
|
6411
|
+
* a directory has changed, ALL files in that directory are rewritten. This is
|
|
6412
|
+
* an intentional design decision to ensure consistency within directory units.
|
|
6402
6413
|
*/
|
|
6403
6414
|
async writeAiDirs(aiDirs) {
|
|
6415
|
+
let changedCount = 0;
|
|
6404
6416
|
for (const aiDir of aiDirs) {
|
|
6405
6417
|
const dirPath = aiDir.getDirPath();
|
|
6418
|
+
let dirHasChanges = false;
|
|
6419
|
+
const mainFile = aiDir.getMainFile();
|
|
6420
|
+
let mainFileContent;
|
|
6421
|
+
if (mainFile) {
|
|
6422
|
+
const mainFilePath = join55(dirPath, mainFile.name);
|
|
6423
|
+
const content = stringifyFrontmatter(mainFile.body, mainFile.frontmatter);
|
|
6424
|
+
mainFileContent = addTrailingNewline(content);
|
|
6425
|
+
const existingContent = await readFileContentOrNull(mainFilePath);
|
|
6426
|
+
if (existingContent !== mainFileContent) {
|
|
6427
|
+
dirHasChanges = true;
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
const otherFiles = aiDir.getOtherFiles();
|
|
6431
|
+
const otherFileContents = [];
|
|
6432
|
+
for (const file of otherFiles) {
|
|
6433
|
+
const contentWithNewline = addTrailingNewline(file.fileBuffer.toString("utf-8"));
|
|
6434
|
+
otherFileContents.push(contentWithNewline);
|
|
6435
|
+
if (!dirHasChanges) {
|
|
6436
|
+
const filePath = join55(dirPath, file.relativeFilePathToDirPath);
|
|
6437
|
+
const existingContent = await readFileContentOrNull(filePath);
|
|
6438
|
+
if (existingContent !== contentWithNewline) {
|
|
6439
|
+
dirHasChanges = true;
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
if (!dirHasChanges) {
|
|
6444
|
+
continue;
|
|
6445
|
+
}
|
|
6406
6446
|
if (this.dryRun) {
|
|
6407
6447
|
logger.info(`[DRY RUN] Would create directory: ${dirPath}`);
|
|
6408
|
-
const mainFile = aiDir.getMainFile();
|
|
6409
6448
|
if (mainFile) {
|
|
6410
6449
|
logger.info(`[DRY RUN] Would write: ${join55(dirPath, mainFile.name)}`);
|
|
6411
6450
|
}
|
|
6412
|
-
for (const file of
|
|
6451
|
+
for (const file of otherFiles) {
|
|
6413
6452
|
logger.info(`[DRY RUN] Would write: ${join55(dirPath, file.relativeFilePathToDirPath)}`);
|
|
6414
6453
|
}
|
|
6415
6454
|
} else {
|
|
6416
6455
|
await ensureDir(dirPath);
|
|
6417
|
-
|
|
6418
|
-
if (mainFile) {
|
|
6456
|
+
if (mainFile && mainFileContent) {
|
|
6419
6457
|
const mainFilePath = join55(dirPath, mainFile.name);
|
|
6420
|
-
|
|
6421
|
-
const contentWithNewline = addTrailingNewline(content);
|
|
6422
|
-
await writeFileContent(mainFilePath, contentWithNewline);
|
|
6458
|
+
await writeFileContent(mainFilePath, mainFileContent);
|
|
6423
6459
|
}
|
|
6424
|
-
const
|
|
6425
|
-
for (const file of otherFiles) {
|
|
6460
|
+
for (const [i, file] of otherFiles.entries()) {
|
|
6426
6461
|
const filePath = join55(dirPath, file.relativeFilePathToDirPath);
|
|
6427
|
-
const
|
|
6428
|
-
|
|
6462
|
+
const content = otherFileContents[i];
|
|
6463
|
+
if (content === void 0) {
|
|
6464
|
+
throw new Error(
|
|
6465
|
+
`Internal error: content for file ${file.relativeFilePathToDirPath} is undefined. This indicates a synchronization issue between otherFiles and otherFileContents arrays.`
|
|
6466
|
+
);
|
|
6467
|
+
}
|
|
6468
|
+
await writeFileContent(filePath, content);
|
|
6429
6469
|
}
|
|
6430
6470
|
}
|
|
6471
|
+
changedCount++;
|
|
6431
6472
|
}
|
|
6432
|
-
return
|
|
6473
|
+
return changedCount;
|
|
6433
6474
|
}
|
|
6434
6475
|
async removeAiDirs(aiDirs) {
|
|
6435
6476
|
for (const aiDir of aiDirs) {
|
|
@@ -6451,6 +6492,7 @@ var DirFeatureProcessor = class {
|
|
|
6451
6492
|
await removeDirectory(dirPath);
|
|
6452
6493
|
}
|
|
6453
6494
|
}
|
|
6495
|
+
return orphanDirs.length;
|
|
6454
6496
|
}
|
|
6455
6497
|
};
|
|
6456
6498
|
|
|
@@ -14678,19 +14720,13 @@ async function generateRulesCore(params) {
|
|
|
14678
14720
|
});
|
|
14679
14721
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14680
14722
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14681
|
-
if (isPreviewMode) {
|
|
14682
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14683
|
-
if (fileDiff) hasDiff = true;
|
|
14684
|
-
}
|
|
14685
14723
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14686
14724
|
totalCount += writtenCount;
|
|
14725
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14687
14726
|
if (config.getDelete()) {
|
|
14688
14727
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
if (orphanDiff) hasDiff = true;
|
|
14692
|
-
}
|
|
14693
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14728
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14729
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14694
14730
|
}
|
|
14695
14731
|
}
|
|
14696
14732
|
}
|
|
@@ -14718,26 +14754,18 @@ async function generateIgnoreCore(params) {
|
|
|
14718
14754
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14719
14755
|
if (rulesyncFiles.length > 0) {
|
|
14720
14756
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14721
|
-
if (isPreviewMode) {
|
|
14722
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14723
|
-
if (fileDiff) hasDiff = true;
|
|
14724
|
-
}
|
|
14725
14757
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14726
14758
|
totalCount += writtenCount;
|
|
14759
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14727
14760
|
if (config.getDelete()) {
|
|
14728
14761
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14729
|
-
|
|
14730
|
-
|
|
14731
|
-
if (orphanDiff) hasDiff = true;
|
|
14732
|
-
}
|
|
14733
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14762
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14763
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14734
14764
|
}
|
|
14735
14765
|
} else if (config.getDelete()) {
|
|
14736
14766
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
}
|
|
14740
|
-
await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14767
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14768
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14741
14769
|
}
|
|
14742
14770
|
} catch (error) {
|
|
14743
14771
|
logger.warn(
|
|
@@ -14772,19 +14800,13 @@ async function generateMcpCore(params) {
|
|
|
14772
14800
|
});
|
|
14773
14801
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14774
14802
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14775
|
-
if (isPreviewMode) {
|
|
14776
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14777
|
-
if (fileDiff) hasDiff = true;
|
|
14778
|
-
}
|
|
14779
14803
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14780
14804
|
totalCount += writtenCount;
|
|
14805
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14781
14806
|
if (config.getDelete()) {
|
|
14782
14807
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14783
|
-
|
|
14784
|
-
|
|
14785
|
-
if (orphanDiff) hasDiff = true;
|
|
14786
|
-
}
|
|
14787
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14808
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14809
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14788
14810
|
}
|
|
14789
14811
|
}
|
|
14790
14812
|
}
|
|
@@ -14815,19 +14837,13 @@ async function generateCommandsCore(params) {
|
|
|
14815
14837
|
});
|
|
14816
14838
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14817
14839
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14818
|
-
if (isPreviewMode) {
|
|
14819
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14820
|
-
if (fileDiff) hasDiff = true;
|
|
14821
|
-
}
|
|
14822
14840
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14823
14841
|
totalCount += writtenCount;
|
|
14842
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14824
14843
|
if (config.getDelete()) {
|
|
14825
14844
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14826
|
-
|
|
14827
|
-
|
|
14828
|
-
if (orphanDiff) hasDiff = true;
|
|
14829
|
-
}
|
|
14830
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14845
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14846
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14831
14847
|
}
|
|
14832
14848
|
}
|
|
14833
14849
|
}
|
|
@@ -14858,19 +14874,13 @@ async function generateSubagentsCore(params) {
|
|
|
14858
14874
|
});
|
|
14859
14875
|
const rulesyncFiles = await processor.loadRulesyncFiles();
|
|
14860
14876
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14861
|
-
if (isPreviewMode) {
|
|
14862
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14863
|
-
if (fileDiff) hasDiff = true;
|
|
14864
|
-
}
|
|
14865
14877
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14866
14878
|
totalCount += writtenCount;
|
|
14879
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14867
14880
|
if (config.getDelete()) {
|
|
14868
14881
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
if (orphanDiff) hasDiff = true;
|
|
14872
|
-
}
|
|
14873
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14882
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14883
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14874
14884
|
}
|
|
14875
14885
|
}
|
|
14876
14886
|
}
|
|
@@ -14907,19 +14917,13 @@ async function generateSkillsCore(params) {
|
|
|
14907
14917
|
}
|
|
14908
14918
|
}
|
|
14909
14919
|
const toolDirs = await processor.convertRulesyncDirsToToolDirs(rulesyncDirs);
|
|
14910
|
-
if (isPreviewMode) {
|
|
14911
|
-
const dirDiff = await detectDirDiff(toolDirs);
|
|
14912
|
-
if (dirDiff) hasDiff = true;
|
|
14913
|
-
}
|
|
14914
14920
|
const writtenCount = await processor.writeAiDirs(toolDirs);
|
|
14915
14921
|
totalCount += writtenCount;
|
|
14922
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14916
14923
|
if (config.getDelete()) {
|
|
14917
14924
|
const existingToolDirs = await processor.loadToolDirsToDelete();
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
if (orphanDiff) hasDiff = true;
|
|
14921
|
-
}
|
|
14922
|
-
await processor.removeOrphanAiDirs(existingToolDirs, toolDirs);
|
|
14925
|
+
const orphanCount = await processor.removeOrphanAiDirs(existingToolDirs, toolDirs);
|
|
14926
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14923
14927
|
}
|
|
14924
14928
|
}
|
|
14925
14929
|
}
|
|
@@ -14949,77 +14953,24 @@ async function generateHooksCore(params) {
|
|
|
14949
14953
|
if (rulesyncFiles.length === 0) {
|
|
14950
14954
|
if (config.getDelete()) {
|
|
14951
14955
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
}
|
|
14955
|
-
await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14956
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, []);
|
|
14957
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14956
14958
|
}
|
|
14957
14959
|
continue;
|
|
14958
14960
|
}
|
|
14959
14961
|
const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
|
|
14960
|
-
if (isPreviewMode) {
|
|
14961
|
-
const fileDiff = await detectFileDiff(toolFiles);
|
|
14962
|
-
if (fileDiff) hasDiff = true;
|
|
14963
|
-
}
|
|
14964
14962
|
const writtenCount = await processor.writeAiFiles(toolFiles);
|
|
14965
14963
|
totalCount += writtenCount;
|
|
14964
|
+
if (writtenCount > 0) hasDiff = true;
|
|
14966
14965
|
if (config.getDelete()) {
|
|
14967
14966
|
const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
if (orphanDiff) hasDiff = true;
|
|
14971
|
-
}
|
|
14972
|
-
await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14967
|
+
const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
|
|
14968
|
+
if (orphanCount > 0) hasDiff = true;
|
|
14973
14969
|
}
|
|
14974
14970
|
}
|
|
14975
14971
|
}
|
|
14976
14972
|
return { count: totalCount, hasDiff };
|
|
14977
14973
|
}
|
|
14978
|
-
async function detectFileDiff(aiFiles) {
|
|
14979
|
-
for (const aiFile of aiFiles) {
|
|
14980
|
-
const filePath = aiFile.getFilePath();
|
|
14981
|
-
const newContent = addTrailingNewline(aiFile.getFileContent());
|
|
14982
|
-
const existingContent = await readFileContentOrNull(filePath);
|
|
14983
|
-
if (existingContent !== newContent) {
|
|
14984
|
-
return true;
|
|
14985
|
-
}
|
|
14986
|
-
}
|
|
14987
|
-
return false;
|
|
14988
|
-
}
|
|
14989
|
-
async function detectOrphanFileDiff(existingFiles, generatedFiles) {
|
|
14990
|
-
const generatedPaths = new Set(generatedFiles.map((f) => f.getFilePath()));
|
|
14991
|
-
const orphanFiles = existingFiles.filter((f) => !generatedPaths.has(f.getFilePath()));
|
|
14992
|
-
return orphanFiles.length > 0;
|
|
14993
|
-
}
|
|
14994
|
-
async function detectDirDiff(aiDirs) {
|
|
14995
|
-
for (const aiDir of aiDirs) {
|
|
14996
|
-
const mainFile = aiDir.getMainFile();
|
|
14997
|
-
if (mainFile) {
|
|
14998
|
-
const mainFilePath = join108(aiDir.getDirPath(), mainFile.name);
|
|
14999
|
-
const newContent = addTrailingNewline(
|
|
15000
|
-
stringifyFrontmatter(mainFile.body, mainFile.frontmatter)
|
|
15001
|
-
);
|
|
15002
|
-
const existingContent = await readFileContentOrNull(mainFilePath);
|
|
15003
|
-
if (existingContent !== newContent) {
|
|
15004
|
-
return true;
|
|
15005
|
-
}
|
|
15006
|
-
}
|
|
15007
|
-
for (const file of aiDir.getOtherFiles()) {
|
|
15008
|
-
const filePath = join108(aiDir.getDirPath(), file.relativeFilePathToDirPath);
|
|
15009
|
-
const newContent = addTrailingNewline(file.fileBuffer.toString("utf-8"));
|
|
15010
|
-
const existingContent = await readFileContentOrNull(filePath);
|
|
15011
|
-
if (existingContent !== newContent) {
|
|
15012
|
-
return true;
|
|
15013
|
-
}
|
|
15014
|
-
}
|
|
15015
|
-
}
|
|
15016
|
-
return false;
|
|
15017
|
-
}
|
|
15018
|
-
async function detectOrphanDirDiff(existingDirs, generatedDirs) {
|
|
15019
|
-
const generatedPaths = new Set(generatedDirs.map((d) => d.getDirPath()));
|
|
15020
|
-
const orphanDirs = existingDirs.filter((d) => !generatedPaths.has(d.getDirPath()));
|
|
15021
|
-
return orphanDirs.length > 0;
|
|
15022
|
-
}
|
|
15023
14974
|
|
|
15024
14975
|
// src/utils/result.ts
|
|
15025
14976
|
function calculateTotalCount(result) {
|
|
@@ -15031,9 +14982,9 @@ function logFeatureResult(params) {
|
|
|
15031
14982
|
const { count, featureName, isPreview, modePrefix } = params;
|
|
15032
14983
|
if (count > 0) {
|
|
15033
14984
|
if (isPreview) {
|
|
15034
|
-
logger.info(`${modePrefix} Would
|
|
14985
|
+
logger.info(`${modePrefix} Would write ${count} ${featureName}`);
|
|
15035
14986
|
} else {
|
|
15036
|
-
logger.success(`
|
|
14987
|
+
logger.success(`Written ${count} ${featureName}`);
|
|
15037
14988
|
}
|
|
15038
14989
|
}
|
|
15039
14990
|
}
|
|
@@ -15128,7 +15079,7 @@ async function generateCommand(options) {
|
|
|
15128
15079
|
const totalGenerated = calculateTotalCount(result);
|
|
15129
15080
|
if (totalGenerated === 0) {
|
|
15130
15081
|
const enabledFeatures = features.join(", ");
|
|
15131
|
-
logger.
|
|
15082
|
+
logger.info(`\u2713 All files are up to date (${enabledFeatures})`);
|
|
15132
15083
|
return;
|
|
15133
15084
|
}
|
|
15134
15085
|
const parts = [];
|
|
@@ -15140,11 +15091,9 @@ async function generateCommand(options) {
|
|
|
15140
15091
|
if (result.skillsCount > 0) parts.push(`${result.skillsCount} skills`);
|
|
15141
15092
|
if (result.hooksCount > 0) parts.push(`${result.hooksCount} hooks`);
|
|
15142
15093
|
if (isPreview) {
|
|
15143
|
-
logger.info(
|
|
15144
|
-
`${modePrefix} Would generate ${totalGenerated} file(s) total (${parts.join(" + ")})`
|
|
15145
|
-
);
|
|
15094
|
+
logger.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
15146
15095
|
} else {
|
|
15147
|
-
logger.success(`\u{1F389} All done!
|
|
15096
|
+
logger.success(`\u{1F389} All done! Written ${totalGenerated} file(s) total (${parts.join(" + ")})`);
|
|
15148
15097
|
}
|
|
15149
15098
|
if (check) {
|
|
15150
15099
|
if (result.hasDiff) {
|
|
@@ -17570,7 +17519,7 @@ async function updateCommand(currentVersion, options) {
|
|
|
17570
17519
|
}
|
|
17571
17520
|
|
|
17572
17521
|
// src/cli/index.ts
|
|
17573
|
-
var getVersion = () => "6.
|
|
17522
|
+
var getVersion = () => "6.7.1";
|
|
17574
17523
|
var main = async () => {
|
|
17575
17524
|
const program = new Command();
|
|
17576
17525
|
const version = getVersion();
|