glooit 0.5.4 โ 0.5.6
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 +38 -6
- package/bin/glooit-linux +0 -0
- package/bin/glooit-macos +0 -0
- package/bin/glooit-windows.exe +0 -0
- package/dist/cli/index.js +63 -19
- package/dist/index.js +66 -14
- package/dist/types/index.d.ts +13 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -29,17 +29,37 @@ glooit init
|
|
|
29
29
|
This creates a `glooit.config.ts` file:
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
import {
|
|
32
|
+
import { defineRules } from 'glooit';
|
|
33
33
|
|
|
34
|
-
export default {
|
|
34
|
+
export default defineRules({
|
|
35
|
+
configDir: '.glooit',
|
|
35
36
|
rules: [
|
|
36
37
|
{
|
|
37
|
-
|
|
38
|
+
name: 'main',
|
|
39
|
+
file: '.glooit/main.md',
|
|
38
40
|
to: './',
|
|
39
41
|
targets: ['claude', 'cursor', 'codex']
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'frontend',
|
|
45
|
+
file: '.glooit/frontend.md',
|
|
46
|
+
to: './apps/frontend',
|
|
47
|
+
globs: './apps/frontend/{src,tests}/**/*.{ts,tsx,js,jsx,md}',
|
|
48
|
+
targets: [
|
|
49
|
+
'claude',
|
|
50
|
+
'codex',
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'server',
|
|
55
|
+
file: '.glooit/server.md',
|
|
56
|
+
to: './apps/server',
|
|
57
|
+
globs: './apps/server/{src,tests}/**/*.{ts,tsx,js,jsx,md}',
|
|
58
|
+
targets: ['claude', 'codex']
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
mcps: [],
|
|
62
|
+
});
|
|
43
63
|
```
|
|
44
64
|
|
|
45
65
|
### 2. Sync Your Rules
|
|
@@ -179,12 +199,14 @@ export default defineRules({
|
|
|
179
199
|
```
|
|
180
200
|
|
|
181
201
|
**๐ `addTimestamp`** - Replace `__TIMESTAMP__` with current date/time
|
|
202
|
+
|
|
182
203
|
```markdown
|
|
183
204
|
Last updated: __TIMESTAMP__
|
|
184
205
|
<!-- Becomes: Last updated: December 24, 2024, 03:30 PM -->
|
|
185
206
|
```
|
|
186
207
|
|
|
187
208
|
**๐ `replaceEnv`** - Replace `__ENV_VAR__` patterns with environment variables
|
|
209
|
+
|
|
188
210
|
```markdown
|
|
189
211
|
Database: __ENV_DATABASE_URL__
|
|
190
212
|
API Key: __ENV_API_KEY__
|
|
@@ -192,6 +214,7 @@ API Key: __ENV_API_KEY__
|
|
|
192
214
|
```
|
|
193
215
|
|
|
194
216
|
**๐ `replaceStructure`** - Replace `__STRUCTURE__` with project tree
|
|
217
|
+
|
|
195
218
|
```markdown
|
|
196
219
|
Project structure:
|
|
197
220
|
__STRUCTURE__
|
|
@@ -199,6 +222,7 @@ __STRUCTURE__
|
|
|
199
222
|
```
|
|
200
223
|
|
|
201
224
|
**๐๏ธ `compact`** - Clean up and compress your markdown
|
|
225
|
+
|
|
202
226
|
```typescript
|
|
203
227
|
hooks.compact({
|
|
204
228
|
maxConsecutiveNewlines: 2,
|
|
@@ -278,3 +302,11 @@ glooit backup list # List backups
|
|
|
278
302
|
## License
|
|
279
303
|
|
|
280
304
|
MIT
|
|
305
|
+
|
|
306
|
+
## Credits
|
|
307
|
+
|
|
308
|
+
Without these amazing projects, this project would not be possible.
|
|
309
|
+
|
|
310
|
+
- [Antfu](https://antfu.me) for the amazing packages
|
|
311
|
+
- [Ruler](https://github.com/intellectronica/ruler) Inspiration for the project & ideias
|
|
312
|
+
- [Claude](https://www.anthropic.com/home/claude) for the amazing AI that made this project possible in a few hours.
|
package/bin/glooit-linux
CHANGED
|
Binary file
|
package/bin/glooit-macos
CHANGED
|
Binary file
|
package/bin/glooit-windows.exe
CHANGED
|
Binary file
|
package/dist/cli/index.js
CHANGED
|
@@ -2214,7 +2214,8 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
2214
2214
|
|
|
2215
2215
|
class CursorWriter {
|
|
2216
2216
|
formatContent(content, rule) {
|
|
2217
|
-
const
|
|
2217
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
2218
|
+
const ruleName = this.extractRuleName(firstFile);
|
|
2218
2219
|
const hasGlobs = rule.globs && rule.globs.length > 0;
|
|
2219
2220
|
const frontmatter = [
|
|
2220
2221
|
"---",
|
|
@@ -2336,7 +2337,8 @@ class AgentDistributor {
|
|
|
2336
2337
|
this.config = config;
|
|
2337
2338
|
}
|
|
2338
2339
|
async distributeRule(rule) {
|
|
2339
|
-
const
|
|
2340
|
+
const filePath = Array.isArray(rule.file) ? rule.file : [rule.file];
|
|
2341
|
+
const content = this.loadRuleContent(filePath);
|
|
2340
2342
|
for (const agent of rule.targets) {
|
|
2341
2343
|
await this.distributeToAgent(agent, rule, content);
|
|
2342
2344
|
}
|
|
@@ -2354,7 +2356,8 @@ class AgentDistributor {
|
|
|
2354
2356
|
if (customPath) {
|
|
2355
2357
|
targetPath = customPath;
|
|
2356
2358
|
} else {
|
|
2357
|
-
const
|
|
2359
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
2360
|
+
const ruleName = this.extractRuleName(firstFile);
|
|
2358
2361
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
2359
2362
|
targetPath = join2(rule.to, agentPath);
|
|
2360
2363
|
}
|
|
@@ -2371,11 +2374,27 @@ class AgentDistributor {
|
|
|
2371
2374
|
const finalContent = await this.applyHooks(context);
|
|
2372
2375
|
writeFileSync(targetPath, finalContent, "utf-8");
|
|
2373
2376
|
}
|
|
2374
|
-
loadRuleContent(
|
|
2377
|
+
loadRuleContent(filePaths) {
|
|
2375
2378
|
try {
|
|
2376
|
-
|
|
2379
|
+
if (filePaths.length === 1) {
|
|
2380
|
+
return readFileSync3(filePaths[0], "utf-8");
|
|
2381
|
+
}
|
|
2382
|
+
const mergedContent = [];
|
|
2383
|
+
for (let i = 0;i < filePaths.length; i++) {
|
|
2384
|
+
const filePath = filePaths[i];
|
|
2385
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
2386
|
+
mergedContent.push(`<!-- Source: ${filePath} -->`);
|
|
2387
|
+
mergedContent.push(content);
|
|
2388
|
+
if (i < filePaths.length - 1) {
|
|
2389
|
+
mergedContent.push(`
|
|
2390
|
+
---
|
|
2391
|
+
`);
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
return mergedContent.join(`
|
|
2395
|
+
`);
|
|
2377
2396
|
} catch (error) {
|
|
2378
|
-
throw new Error(`Failed to read rule file
|
|
2397
|
+
throw new Error(`Failed to read rule file(s): ${error}`);
|
|
2379
2398
|
}
|
|
2380
2399
|
}
|
|
2381
2400
|
extractRuleName(filePath) {
|
|
@@ -2556,15 +2575,22 @@ class GitIgnoreManager {
|
|
|
2556
2575
|
writeFileSync3(this.gitignorePath, cleanedContent, "utf-8");
|
|
2557
2576
|
}
|
|
2558
2577
|
collectGeneratedPaths() {
|
|
2578
|
+
if (this.config.gitignore === false) {
|
|
2579
|
+
return [];
|
|
2580
|
+
}
|
|
2559
2581
|
const paths = new Set;
|
|
2560
2582
|
for (const rule of this.config.rules) {
|
|
2583
|
+
if (rule.gitignore === false) {
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2561
2586
|
for (const agent of rule.targets) {
|
|
2562
2587
|
const agentName = this.getAgentName(agent);
|
|
2563
2588
|
const customPath = this.getCustomPath(agent);
|
|
2564
2589
|
if (customPath) {
|
|
2565
2590
|
paths.add(customPath);
|
|
2566
2591
|
} else {
|
|
2567
|
-
const
|
|
2592
|
+
const filePath = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
2593
|
+
const ruleName = this.extractRuleName(filePath);
|
|
2568
2594
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
2569
2595
|
const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
|
|
2570
2596
|
paths.add(fullPath);
|
|
@@ -2706,8 +2732,11 @@ class AIRulesCore {
|
|
|
2706
2732
|
async validate() {
|
|
2707
2733
|
try {
|
|
2708
2734
|
for (const rule of this.config.rules) {
|
|
2709
|
-
|
|
2710
|
-
|
|
2735
|
+
const files = Array.isArray(rule.file) ? rule.file : [rule.file];
|
|
2736
|
+
for (const file of files) {
|
|
2737
|
+
if (!existsSync5(file)) {
|
|
2738
|
+
throw new Error(`Rule file not found: ${file}`);
|
|
2739
|
+
}
|
|
2711
2740
|
}
|
|
2712
2741
|
}
|
|
2713
2742
|
if (this.config.commands) {
|
|
@@ -2801,7 +2830,8 @@ class AIRulesCore {
|
|
|
2801
2830
|
if (customPath) {
|
|
2802
2831
|
paths.push(customPath);
|
|
2803
2832
|
} else {
|
|
2804
|
-
const
|
|
2833
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
2834
|
+
const ruleName = firstFile.split("/").pop()?.replace(".md", "") || "rule";
|
|
2805
2835
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
2806
2836
|
let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
|
|
2807
2837
|
if (fullPath.startsWith("./")) {
|
|
@@ -2914,6 +2944,7 @@ Original error: ${jitiError}`);
|
|
|
2914
2944
|
});
|
|
2915
2945
|
c.configDir = c.configDir || ".glooit";
|
|
2916
2946
|
c.mergeMcps = c.mergeMcps ?? true;
|
|
2947
|
+
c.gitignore = c.gitignore ?? true;
|
|
2917
2948
|
if (c.backup) {
|
|
2918
2949
|
const backup = c.backup;
|
|
2919
2950
|
backup.enabled = backup.enabled ?? true;
|
|
@@ -2933,8 +2964,10 @@ Original error: ${jitiError}`);
|
|
|
2933
2964
|
throw new Error(`Rule at index ${index}: Rule must be an object`);
|
|
2934
2965
|
}
|
|
2935
2966
|
const r = rule;
|
|
2936
|
-
|
|
2937
|
-
|
|
2967
|
+
const isFileString = typeof r.file === "string";
|
|
2968
|
+
const isFileArray = Array.isArray(r.file) && r.file.length > 0 && r.file.every((f) => typeof f === "string");
|
|
2969
|
+
if (!isFileString && !isFileArray) {
|
|
2970
|
+
throw new Error(`Rule at index ${index}: Rule.file must be a string or a non-empty array of strings`);
|
|
2938
2971
|
}
|
|
2939
2972
|
if (typeof r.to !== "string") {
|
|
2940
2973
|
throw new Error(`Rule at index ${index}: Rule.to must be a string`);
|
|
@@ -2955,6 +2988,14 @@ Original error: ${jitiError}`);
|
|
|
2955
2988
|
})) {
|
|
2956
2989
|
throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgentNames.join(", ")}, or objects with {name, to?}`);
|
|
2957
2990
|
}
|
|
2991
|
+
if (isFileArray) {
|
|
2992
|
+
const allTargetsAreObjects = r.targets.every((agent) => {
|
|
2993
|
+
return typeof agent === "object" && agent !== null && "name" in agent && "to" in agent;
|
|
2994
|
+
});
|
|
2995
|
+
if (!allTargetsAreObjects) {
|
|
2996
|
+
throw new Error(`Rule at index ${index}: When using file array (merge mode), all targets must be objects with {name, to} properties`);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2958
2999
|
}
|
|
2959
3000
|
static createInitialConfig() {
|
|
2960
3001
|
return this.createTypedConfig();
|
|
@@ -2998,12 +3039,15 @@ class ConfigValidator {
|
|
|
2998
3039
|
static async validate(config) {
|
|
2999
3040
|
const errors = [];
|
|
3000
3041
|
for (const [index, rule] of config.rules.entries()) {
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3042
|
+
const files = Array.isArray(rule.file) ? rule.file : [rule.file];
|
|
3043
|
+
for (const file of files) {
|
|
3044
|
+
if (!existsSync7(file)) {
|
|
3045
|
+
errors.push({
|
|
3046
|
+
field: `rules[${index}].file`,
|
|
3047
|
+
message: `Rule file not found: ${file}`,
|
|
3048
|
+
path: file
|
|
3049
|
+
});
|
|
3050
|
+
}
|
|
3007
3051
|
}
|
|
3008
3052
|
if (rule.targets.length === 0) {
|
|
3009
3053
|
errors.push({
|
|
@@ -3351,7 +3395,7 @@ function constructCommand(value, args) {
|
|
|
3351
3395
|
import { execSync } from "child_process";
|
|
3352
3396
|
import { createInterface } from "readline";
|
|
3353
3397
|
// package.json
|
|
3354
|
-
var version = "0.5.
|
|
3398
|
+
var version = "0.5.6";
|
|
3355
3399
|
|
|
3356
3400
|
// src/cli/index.ts
|
|
3357
3401
|
var args = process.argv.slice(2);
|
package/dist/index.js
CHANGED
|
@@ -3388,8 +3388,10 @@ function validateRule(rule) {
|
|
|
3388
3388
|
throw new Error("Rule must be an object");
|
|
3389
3389
|
}
|
|
3390
3390
|
const r = rule;
|
|
3391
|
-
|
|
3392
|
-
|
|
3391
|
+
const isFileString = typeof r.file === "string";
|
|
3392
|
+
const isFileArray = Array.isArray(r.file) && r.file.length > 0 && r.file.every((f) => typeof f === "string");
|
|
3393
|
+
if (!isFileString && !isFileArray) {
|
|
3394
|
+
throw new Error("Rule.file must be a string or a non-empty array of strings");
|
|
3393
3395
|
}
|
|
3394
3396
|
if (typeof r.to !== "string") {
|
|
3395
3397
|
throw new Error("Rule.to must be a string");
|
|
@@ -3400,6 +3402,14 @@ function validateRule(rule) {
|
|
|
3400
3402
|
if (!r.targets.every(isValidAgent)) {
|
|
3401
3403
|
throw new Error("Rule.targets must contain valid agents: claude, cursor, codex, roocode, generic, or objects with {name, to?}");
|
|
3402
3404
|
}
|
|
3405
|
+
if (isFileArray) {
|
|
3406
|
+
const allTargetsAreObjects = r.targets.every((target) => {
|
|
3407
|
+
return typeof target === "object" && target !== null && "name" in target && "to" in target;
|
|
3408
|
+
});
|
|
3409
|
+
if (!allTargetsAreObjects) {
|
|
3410
|
+
throw new Error("When using file array (merge mode), all targets must be objects with {name, to} properties");
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3403
3413
|
}
|
|
3404
3414
|
function validateConfig(config) {
|
|
3405
3415
|
if (!config || typeof config !== "object") {
|
|
@@ -3418,6 +3428,7 @@ function validateConfig(config) {
|
|
|
3418
3428
|
});
|
|
3419
3429
|
c.configDir = c.configDir || ".glooit";
|
|
3420
3430
|
c.mergeMcps = c.mergeMcps ?? true;
|
|
3431
|
+
c.gitignore = c.gitignore ?? true;
|
|
3421
3432
|
if (c.backup) {
|
|
3422
3433
|
const backup = c.backup;
|
|
3423
3434
|
backup.enabled = backup.enabled ?? true;
|
|
@@ -3522,7 +3533,8 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
|
3522
3533
|
|
|
3523
3534
|
class CursorWriter {
|
|
3524
3535
|
formatContent(content, rule) {
|
|
3525
|
-
const
|
|
3536
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
3537
|
+
const ruleName = this.extractRuleName(firstFile);
|
|
3526
3538
|
const hasGlobs = rule.globs && rule.globs.length > 0;
|
|
3527
3539
|
const frontmatter = [
|
|
3528
3540
|
"---",
|
|
@@ -3644,7 +3656,8 @@ class AgentDistributor {
|
|
|
3644
3656
|
this.config = config;
|
|
3645
3657
|
}
|
|
3646
3658
|
async distributeRule(rule) {
|
|
3647
|
-
const
|
|
3659
|
+
const filePath = Array.isArray(rule.file) ? rule.file : [rule.file];
|
|
3660
|
+
const content = this.loadRuleContent(filePath);
|
|
3648
3661
|
for (const agent of rule.targets) {
|
|
3649
3662
|
await this.distributeToAgent(agent, rule, content);
|
|
3650
3663
|
}
|
|
@@ -3662,7 +3675,8 @@ class AgentDistributor {
|
|
|
3662
3675
|
if (customPath) {
|
|
3663
3676
|
targetPath = customPath;
|
|
3664
3677
|
} else {
|
|
3665
|
-
const
|
|
3678
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
3679
|
+
const ruleName = this.extractRuleName(firstFile);
|
|
3666
3680
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
3667
3681
|
targetPath = join2(rule.to, agentPath);
|
|
3668
3682
|
}
|
|
@@ -3679,11 +3693,27 @@ class AgentDistributor {
|
|
|
3679
3693
|
const finalContent = await this.applyHooks(context);
|
|
3680
3694
|
writeFileSync(targetPath, finalContent, "utf-8");
|
|
3681
3695
|
}
|
|
3682
|
-
loadRuleContent(
|
|
3696
|
+
loadRuleContent(filePaths) {
|
|
3683
3697
|
try {
|
|
3684
|
-
|
|
3698
|
+
if (filePaths.length === 1) {
|
|
3699
|
+
return readFileSync3(filePaths[0], "utf-8");
|
|
3700
|
+
}
|
|
3701
|
+
const mergedContent = [];
|
|
3702
|
+
for (let i = 0;i < filePaths.length; i++) {
|
|
3703
|
+
const filePath = filePaths[i];
|
|
3704
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
3705
|
+
mergedContent.push(`<!-- Source: ${filePath} -->`);
|
|
3706
|
+
mergedContent.push(content);
|
|
3707
|
+
if (i < filePaths.length - 1) {
|
|
3708
|
+
mergedContent.push(`
|
|
3709
|
+
---
|
|
3710
|
+
`);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
return mergedContent.join(`
|
|
3714
|
+
`);
|
|
3685
3715
|
} catch (error) {
|
|
3686
|
-
throw new Error(`Failed to read rule file
|
|
3716
|
+
throw new Error(`Failed to read rule file(s): ${error}`);
|
|
3687
3717
|
}
|
|
3688
3718
|
}
|
|
3689
3719
|
extractRuleName(filePath) {
|
|
@@ -3864,15 +3894,22 @@ class GitIgnoreManager {
|
|
|
3864
3894
|
writeFileSync3(this.gitignorePath, cleanedContent, "utf-8");
|
|
3865
3895
|
}
|
|
3866
3896
|
collectGeneratedPaths() {
|
|
3897
|
+
if (this.config.gitignore === false) {
|
|
3898
|
+
return [];
|
|
3899
|
+
}
|
|
3867
3900
|
const paths = new Set;
|
|
3868
3901
|
for (const rule of this.config.rules) {
|
|
3902
|
+
if (rule.gitignore === false) {
|
|
3903
|
+
continue;
|
|
3904
|
+
}
|
|
3869
3905
|
for (const agent of rule.targets) {
|
|
3870
3906
|
const agentName = this.getAgentName(agent);
|
|
3871
3907
|
const customPath = this.getCustomPath(agent);
|
|
3872
3908
|
if (customPath) {
|
|
3873
3909
|
paths.add(customPath);
|
|
3874
3910
|
} else {
|
|
3875
|
-
const
|
|
3911
|
+
const filePath = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
3912
|
+
const ruleName = this.extractRuleName(filePath);
|
|
3876
3913
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
3877
3914
|
const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
|
|
3878
3915
|
paths.add(fullPath);
|
|
@@ -4014,8 +4051,11 @@ class AIRulesCore {
|
|
|
4014
4051
|
async validate() {
|
|
4015
4052
|
try {
|
|
4016
4053
|
for (const rule of this.config.rules) {
|
|
4017
|
-
|
|
4018
|
-
|
|
4054
|
+
const files = Array.isArray(rule.file) ? rule.file : [rule.file];
|
|
4055
|
+
for (const file of files) {
|
|
4056
|
+
if (!existsSync5(file)) {
|
|
4057
|
+
throw new Error(`Rule file not found: ${file}`);
|
|
4058
|
+
}
|
|
4019
4059
|
}
|
|
4020
4060
|
}
|
|
4021
4061
|
if (this.config.commands) {
|
|
@@ -4109,7 +4149,8 @@ class AIRulesCore {
|
|
|
4109
4149
|
if (customPath) {
|
|
4110
4150
|
paths.push(customPath);
|
|
4111
4151
|
} else {
|
|
4112
|
-
const
|
|
4152
|
+
const firstFile = Array.isArray(rule.file) ? rule.file[0] : rule.file;
|
|
4153
|
+
const ruleName = firstFile.split("/").pop()?.replace(".md", "") || "rule";
|
|
4113
4154
|
const agentPath = getAgentPath(agentName, ruleName);
|
|
4114
4155
|
let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
|
|
4115
4156
|
if (fullPath.startsWith("./")) {
|
|
@@ -4221,6 +4262,7 @@ Original error: ${jitiError}`);
|
|
|
4221
4262
|
});
|
|
4222
4263
|
c.configDir = c.configDir || ".glooit";
|
|
4223
4264
|
c.mergeMcps = c.mergeMcps ?? true;
|
|
4265
|
+
c.gitignore = c.gitignore ?? true;
|
|
4224
4266
|
if (c.backup) {
|
|
4225
4267
|
const backup = c.backup;
|
|
4226
4268
|
backup.enabled = backup.enabled ?? true;
|
|
@@ -4240,8 +4282,10 @@ Original error: ${jitiError}`);
|
|
|
4240
4282
|
throw new Error(`Rule at index ${index}: Rule must be an object`);
|
|
4241
4283
|
}
|
|
4242
4284
|
const r = rule;
|
|
4243
|
-
|
|
4244
|
-
|
|
4285
|
+
const isFileString = typeof r.file === "string";
|
|
4286
|
+
const isFileArray = Array.isArray(r.file) && r.file.length > 0 && r.file.every((f) => typeof f === "string");
|
|
4287
|
+
if (!isFileString && !isFileArray) {
|
|
4288
|
+
throw new Error(`Rule at index ${index}: Rule.file must be a string or a non-empty array of strings`);
|
|
4245
4289
|
}
|
|
4246
4290
|
if (typeof r.to !== "string") {
|
|
4247
4291
|
throw new Error(`Rule at index ${index}: Rule.to must be a string`);
|
|
@@ -4262,6 +4306,14 @@ Original error: ${jitiError}`);
|
|
|
4262
4306
|
})) {
|
|
4263
4307
|
throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgentNames.join(", ")}, or objects with {name, to?}`);
|
|
4264
4308
|
}
|
|
4309
|
+
if (isFileArray) {
|
|
4310
|
+
const allTargetsAreObjects = r.targets.every((agent) => {
|
|
4311
|
+
return typeof agent === "object" && agent !== null && "name" in agent && "to" in agent;
|
|
4312
|
+
});
|
|
4313
|
+
if (!allTargetsAreObjects) {
|
|
4314
|
+
throw new Error(`Rule at index ${index}: When using file array (merge mode), all targets must be objects with {name, to} properties`);
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4265
4317
|
}
|
|
4266
4318
|
static createInitialConfig() {
|
|
4267
4319
|
return this.createTypedConfig();
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,14 +4,22 @@ export interface AgentTarget {
|
|
|
4
4
|
to?: string;
|
|
5
5
|
}
|
|
6
6
|
export type Agent = AgentName | AgentTarget;
|
|
7
|
-
|
|
7
|
+
interface BaseRule {
|
|
8
8
|
name?: string;
|
|
9
|
-
file: string;
|
|
10
9
|
to: string;
|
|
11
10
|
globs?: string;
|
|
12
|
-
targets: Agent[];
|
|
13
11
|
hooks?: string[];
|
|
12
|
+
gitignore?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface SingleFileRule extends BaseRule {
|
|
15
|
+
file: string;
|
|
16
|
+
targets: Agent[];
|
|
17
|
+
}
|
|
18
|
+
export interface MergedFileRule extends BaseRule {
|
|
19
|
+
file: string[];
|
|
20
|
+
targets: AgentTarget[];
|
|
14
21
|
}
|
|
22
|
+
export type Rule = SingleFileRule | MergedFileRule;
|
|
15
23
|
export interface Command {
|
|
16
24
|
command: string;
|
|
17
25
|
file: string;
|
|
@@ -56,6 +64,7 @@ export interface Config {
|
|
|
56
64
|
mergeMcps?: boolean;
|
|
57
65
|
hooks?: Hooks;
|
|
58
66
|
backup?: BackupConfig;
|
|
67
|
+
gitignore?: boolean;
|
|
59
68
|
}
|
|
60
69
|
export interface AgentMapping {
|
|
61
70
|
path: string;
|
|
@@ -78,3 +87,4 @@ export interface BackupEntry {
|
|
|
78
87
|
}[];
|
|
79
88
|
}
|
|
80
89
|
export declare function defineRules(config: Config): Config;
|
|
90
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glooit",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "๐งด Sync your AI agent configurations and rules across platforms with ease",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"format": "oxlint src tests --fix",
|
|
25
25
|
"check": "bun run typecheck && bun run lint && bun run test",
|
|
26
26
|
"taze": "bunx taze",
|
|
27
|
+
"release": "bun run check && npx bumpp && git push --follow-tags",
|
|
27
28
|
"prepublishOnly": "bun run check && bun run build",
|
|
28
29
|
"install:local": "bun run build:binary && sudo mv bin/glooit /usr/local/bin/"
|
|
29
30
|
},
|