glooit 0.5.3 → 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 +80 -20
- package/dist/index.js +3443 -15
- package/dist/types/index.d.ts +13 -3
- package/package.json +6 -4
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("./")) {
|
|
@@ -2856,7 +2886,23 @@ class ConfigLoader {
|
|
|
2856
2886
|
throw new Error(`No configuration file found. Looking for: ${this.DEFAULT_CONFIG_PATHS.join(", ")}`);
|
|
2857
2887
|
}
|
|
2858
2888
|
try {
|
|
2859
|
-
const
|
|
2889
|
+
const fullPath = join4(process.cwd(), configPath);
|
|
2890
|
+
let configModule;
|
|
2891
|
+
if (configPath.endsWith(".ts")) {
|
|
2892
|
+
try {
|
|
2893
|
+
const { createJiti } = await import("jiti");
|
|
2894
|
+
const jiti = createJiti(import.meta.url);
|
|
2895
|
+
configModule = await jiti.import(fullPath);
|
|
2896
|
+
} catch (jitiError) {
|
|
2897
|
+
throw new Error(`Failed to load TypeScript config. Please either:
|
|
2898
|
+
1. Use 'bunx glooit' instead of 'npx glooit'
|
|
2899
|
+
2. Rename ${configPath} to ${configPath.replace(".ts", ".js")}
|
|
2900
|
+
3. Use JavaScript config: npx glooit init --js
|
|
2901
|
+
Original error: ${jitiError}`);
|
|
2902
|
+
}
|
|
2903
|
+
} else {
|
|
2904
|
+
configModule = await import(fullPath);
|
|
2905
|
+
}
|
|
2860
2906
|
const config = configModule.default || configModule;
|
|
2861
2907
|
if (typeof config === "function") {
|
|
2862
2908
|
const result = config();
|
|
@@ -2898,6 +2944,7 @@ class ConfigLoader {
|
|
|
2898
2944
|
});
|
|
2899
2945
|
c.configDir = c.configDir || ".glooit";
|
|
2900
2946
|
c.mergeMcps = c.mergeMcps ?? true;
|
|
2947
|
+
c.gitignore = c.gitignore ?? true;
|
|
2901
2948
|
if (c.backup) {
|
|
2902
2949
|
const backup = c.backup;
|
|
2903
2950
|
backup.enabled = backup.enabled ?? true;
|
|
@@ -2917,8 +2964,10 @@ class ConfigLoader {
|
|
|
2917
2964
|
throw new Error(`Rule at index ${index}: Rule must be an object`);
|
|
2918
2965
|
}
|
|
2919
2966
|
const r = rule;
|
|
2920
|
-
|
|
2921
|
-
|
|
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`);
|
|
2922
2971
|
}
|
|
2923
2972
|
if (typeof r.to !== "string") {
|
|
2924
2973
|
throw new Error(`Rule at index ${index}: Rule.to must be a string`);
|
|
@@ -2939,6 +2988,14 @@ class ConfigLoader {
|
|
|
2939
2988
|
})) {
|
|
2940
2989
|
throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgentNames.join(", ")}, or objects with {name, to?}`);
|
|
2941
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
|
+
}
|
|
2942
2999
|
}
|
|
2943
3000
|
static createInitialConfig() {
|
|
2944
3001
|
return this.createTypedConfig();
|
|
@@ -2982,12 +3039,15 @@ class ConfigValidator {
|
|
|
2982
3039
|
static async validate(config) {
|
|
2983
3040
|
const errors = [];
|
|
2984
3041
|
for (const [index, rule] of config.rules.entries()) {
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
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
|
+
}
|
|
2991
3051
|
}
|
|
2992
3052
|
if (rule.targets.length === 0) {
|
|
2993
3053
|
errors.push({
|
|
@@ -3335,7 +3395,7 @@ function constructCommand(value, args) {
|
|
|
3335
3395
|
import { execSync } from "child_process";
|
|
3336
3396
|
import { createInterface } from "readline";
|
|
3337
3397
|
// package.json
|
|
3338
|
-
var version = "0.5.
|
|
3398
|
+
var version = "0.5.6";
|
|
3339
3399
|
|
|
3340
3400
|
// src/cli/index.ts
|
|
3341
3401
|
var args = process.argv.slice(2);
|