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 CHANGED
@@ -29,17 +29,37 @@ glooit init
29
29
  This creates a `glooit.config.ts` file:
30
30
 
31
31
  ```typescript
32
- import { Config } from 'glooit';
32
+ import { defineRules } from 'glooit';
33
33
 
34
- export default {
34
+ export default defineRules({
35
+ configDir: '.glooit',
35
36
  rules: [
36
37
  {
37
- file: 'main.md',
38
+ name: 'main',
39
+ file: '.glooit/main.md',
38
40
  to: './',
39
41
  targets: ['claude', 'cursor', 'codex']
40
- }
41
- ]
42
- } satisfies Config;
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
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 ruleName = this.extractRuleName(rule.file);
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 content = this.loadRuleContent(rule.file);
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 ruleName = this.extractRuleName(rule.file);
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(filePath) {
2377
+ loadRuleContent(filePaths) {
2375
2378
  try {
2376
- return readFileSync3(filePath, "utf-8");
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 ${filePath}: ${error}`);
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 ruleName = this.extractRuleName(rule.file);
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
- if (!existsSync5(rule.file)) {
2710
- throw new Error(`Rule file not found: ${rule.file}`);
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 ruleName = rule.file.split("/").pop()?.replace(".md", "") || "rule";
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 configModule = await import(join4(process.cwd(), configPath));
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
- if (typeof r.file !== "string") {
2921
- throw new Error(`Rule at index ${index}: Rule.file must be a string`);
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
- if (!existsSync7(rule.file)) {
2986
- errors.push({
2987
- field: `rules[${index}].file`,
2988
- message: `Rule file not found: ${rule.file}`,
2989
- path: rule.file
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.3";
3398
+ var version = "0.5.6";
3339
3399
 
3340
3400
  // src/cli/index.ts
3341
3401
  var args = process.argv.slice(2);