glooit 0.5.1 โ†’ 0.5.3

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
@@ -7,11 +7,15 @@
7
7
  ## Quick Start
8
8
 
9
9
  ```bash
10
- # Install globally
11
- npm install -g glooitit
10
+ # Install
11
+ npm install -g glooit
12
+ bun install -g glooit
13
+ pnpm install -g glooit
12
14
 
13
- # Or run directly
14
- npx glooitit init
15
+ # Run
16
+ npx glooit init
17
+ bunx glooit init
18
+ pnpx glooit init
15
19
  ```
16
20
 
17
21
  ## Basic Usage
@@ -30,9 +34,9 @@ import { Config } from 'glooit';
30
34
  export default {
31
35
  rules: [
32
36
  {
33
- file: 'my-coding-rules.md',
37
+ file: 'main.md',
34
38
  to: './',
35
- targets: ['claude', 'cursor']
39
+ targets: ['claude', 'cursor', 'codex']
36
40
  }
37
41
  ]
38
42
  } satisfies Config;
@@ -45,8 +49,10 @@ glooit sync
45
49
  ```
46
50
 
47
51
  This automatically creates:
52
+
48
53
  - `CLAUDE.md` (for Claude Code)
49
- - `.cursor/rules/my-coding-rules.md` (for Cursor)
54
+ - `.cursor/rules/main.md` (for Cursor)
55
+ - `AGENTS.md` (for Codex)
50
56
 
51
57
  ### 3. Add MCP Configurations
52
58
 
@@ -73,36 +79,179 @@ export default {
73
79
 
74
80
  ### Supported Agents
75
81
 
76
- - **Claude Code**: `.mcp.json`
77
- - **Cursor**: `~/.cursor/mcp.json`
78
- - **Roo Code/Cline**: `.roo/mcp.json`
82
+ - **Claude Code**
83
+ - **Cursor**
84
+ - **Roo Code/Cline**
85
+ - **Codex**
86
+ - **Generic**
79
87
 
80
- ### Example Configuration
88
+ ### Full Configuration
81
89
 
82
90
  ```typescript
83
- import { Config } from 'glooit';
91
+ import { defineRules } from 'glooit';
84
92
 
85
- export default {
93
+ export default defineRules({
86
94
  configDir: '.glooit',
95
+ targets: ['claude', 'cursor'],
87
96
  rules: [
88
97
  {
89
- file: 'coding-standards.md',
98
+ name: 'main',
99
+ file: '.glooit/main.md',
90
100
  to: './',
91
- targets: ['claude', 'cursor', 'roocode']
101
+ targets: ['claude', 'cursor']
92
102
  }
93
103
  ],
104
+ mcps: [],
105
+ mergeMcps: true,
106
+ backup: {
107
+ enabled: true,
108
+ retention: 10
109
+ }
110
+ });
111
+ ```
112
+
113
+ ## Advanced Usage
114
+
115
+ ### Custom File Destinations
116
+
117
+ ```typescript
118
+ export default defineRules({
119
+ rules: [
120
+ {
121
+ name: 'backend-rules',
122
+ file: '.glooit/backend.md',
123
+ to: './',
124
+ targets: [
125
+ 'claude',
126
+ { name: 'cursor', to: './backend/.cursor/rules/backend.md' },
127
+ { name: 'generic', to: './docs/backend-standards.md' }
128
+ ]
129
+ }
130
+ ]
131
+ });
132
+ ```
133
+
134
+ ### Hooks (Before & After Sync)
135
+
136
+ ```typescript
137
+ import { defineRules, hooks } from 'glooit';
138
+
139
+ export default defineRules({
140
+ rules: [/* your rules */],
141
+ hooks: {
142
+ before: [
143
+ async ({ config }) => {
144
+ console.log('๐Ÿš€ Starting sync...');
145
+ // Run tests, validate env, etc.
146
+ }
147
+ ],
148
+ after: [
149
+ async (context) => {
150
+ console.log('โœ… Sync complete!');
151
+ // Commit changes, notify team, etc.
152
+ return `Synced ${context.rule.name} successfully`;
153
+ },
154
+ hooks.addTimestamp,
155
+ hooks.replaceEnv
156
+ ]
157
+ }
158
+ });
159
+ ```
160
+
161
+ ### Built-in Hooks
162
+
163
+ glooit comes with handy hooks to supercharge your sync process:
164
+
165
+ ```typescript
166
+ import { defineRules, hooks } from 'glooit';
167
+
168
+ export default defineRules({
169
+ rules: [/* your rules */],
170
+ hooks: {
171
+ after: [
172
+ hooks.addTimestamp,
173
+ hooks.replaceEnv,
174
+ hooks.replaceStructure,
175
+ hooks.compact()
176
+ ]
177
+ }
178
+ });
179
+ ```
180
+
181
+ **๐Ÿ•’ `addTimestamp`** - Replace `__TIMESTAMP__` with current date/time
182
+ ```markdown
183
+ Last updated: __TIMESTAMP__
184
+ <!-- Becomes: Last updated: December 24, 2024, 03:30 PM -->
185
+ ```
186
+
187
+ **๐ŸŒ `replaceEnv`** - Replace `__ENV_VAR__` patterns with environment variables
188
+ ```markdown
189
+ Database: __ENV_DATABASE_URL__
190
+ API Key: __ENV_API_KEY__
191
+ <!-- Uses your actual env vars -->
192
+ ```
193
+
194
+ **๐Ÿ“ `replaceStructure`** - Replace `__STRUCTURE__` with project tree
195
+ ```markdown
196
+ Project structure:
197
+ __STRUCTURE__
198
+ <!-- Becomes a nice ASCII tree of your project -->
199
+ ```
200
+
201
+ **๐Ÿ—œ๏ธ `compact`** - Clean up and compress your markdown
202
+ ```typescript
203
+ hooks.compact({
204
+ maxConsecutiveNewlines: 2,
205
+ removeFillerWords: true, // Removes "basically", "literally", etc.
206
+ compactLists: true // Tightens up list spacing
207
+ })
208
+ ```
209
+
210
+ ### MCP Configuration
211
+
212
+ ```typescript
213
+ export default defineRules({
214
+ rules: [/* your rules */],
94
215
  mcps: [
95
216
  {
96
- name: 'database',
217
+ name: 'filesystem',
97
218
  config: {
98
219
  command: 'npx',
99
- args: ['db-mcp-server']
220
+ args: ['@modelcontextprotocol/server-filesystem', process.cwd()]
100
221
  },
101
222
  targets: ['claude']
223
+ },
224
+ {
225
+ name: 'postgres',
226
+ config: {
227
+ command: 'uvx',
228
+ args: ['mcp-server-postgres'],
229
+ env: { DATABASE_URL: process.env.DATABASE_URL }
230
+ },
231
+ targets: ['claude', 'cursor']
102
232
  }
103
- ],
104
- mergeMcps: true
105
- } satisfies Config;
233
+ ]
234
+ });
235
+ ```
236
+
237
+ ### Monorepos & AGENTS.md
238
+
239
+ When using `AGENTS.md` in subdirectories, Cursor automatically detects them - no need to enable Cursor for those specific paths. Perfect for monorepos where different services need different rules.
240
+
241
+ ### File Formats by Project Type
242
+
243
+ **JavaScript/TypeScript projects:**
244
+
245
+ ```typescript
246
+ // glooit.config.ts - with TypeScript support
247
+ export default defineRules({ /* config */ });
248
+ ```
249
+
250
+ **Non-JS projects:**
251
+
252
+ ```javascript
253
+ // glooit.config.js - plain JavaScript / typescript
254
+ export default { /* config */ };
106
255
  ```
107
256
 
108
257
  ## Commands
@@ -120,25 +269,12 @@ glooit backup list # List backups
120
269
 
121
270
  - ๐Ÿ”„ **Multi-platform sync** - Works with all major AI coding assistants
122
271
  - ๐Ÿ“ฆ **MCP support** - Model Context Protocol configuration management
123
- - ๐ŸŽฏ **Selective targeting** - Choose which agents get which rules
272
+ - ๐ŸŽฏ **Selective targeting** - Choose which agents get which rules and wht folders, perfect for complex projects & monorepos
124
273
  - ๐Ÿ”™ **Backup system** - Automatic backups before changes
125
274
  - ๐Ÿงน **Clean management** - Easy cleanup and reset options
126
- - โšก **Fast** - Built with Bun for maximum performance
127
-
128
- ## Installation
129
-
130
- ```bash
131
- # NPM
132
- npm install -g glooit
133
-
134
- # Bun
135
- bun install -g glooit
136
-
137
- # Direct execution
138
- npx glooitit init
139
- bunx glooitit sync
140
- ```
275
+ - ๐Ÿ“ **Auto .gitignore** - Automatically manages generated files in .gitignore
276
+ - โšก **Fast** - Built with Bun for.... you know... bun?
141
277
 
142
278
  ## License
143
279
 
144
- MIT
280
+ MIT
package/bin/glooit-linux CHANGED
Binary file
package/bin/glooit-macos CHANGED
Binary file
Binary file
@@ -3,6 +3,8 @@ export declare class AgentDistributor {
3
3
  private config;
4
4
  constructor(config: Config);
5
5
  distributeRule(rule: Rule): Promise<void>;
6
+ private getAgentName;
7
+ private getCustomPath;
6
8
  private distributeToAgent;
7
9
  private loadRuleContent;
8
10
  private extractRuleName;
@@ -1,5 +1,5 @@
1
- import type { Agent, AgentMapping } from '../types';
2
- export declare const AGENT_MAPPINGS: Record<Agent, AgentMapping>;
3
- export declare function getAgentPath(agent: Agent, name?: string): string;
4
- export declare function getAgentDirectory(agent: Agent): string | undefined;
5
- export declare function getAgentMcpPath(agent: Agent): string;
1
+ import type { AgentName, AgentMapping } from '../types';
2
+ export declare const AGENT_MAPPINGS: Record<AgentName, AgentMapping>;
3
+ export declare function getAgentPath(agent: AgentName, name?: string): string;
4
+ export declare function getAgentDirectory(agent: AgentName): string | undefined;
5
+ export declare function getAgentMcpPath(agent: AgentName): string;
@@ -1,8 +1,8 @@
1
- import type { Agent, Rule, ResolvedMcp } from '../../types';
1
+ import type { AgentName, Rule, ResolvedMcp } from '../../types';
2
2
  export interface AgentWriter {
3
3
  formatContent(content: string, rule: Rule): string;
4
4
  formatMcp?(mcp: ResolvedMcp, merge: boolean): string;
5
5
  }
6
6
  export declare class AgentWriterFactory {
7
- static createWriter(agent: Agent): AgentWriter;
7
+ static createWriter(agent: AgentName): AgentWriter;
8
8
  }
package/dist/cli/index.js CHANGED
@@ -2141,7 +2141,7 @@ var AGENT_MAPPINGS = {
2141
2141
  mcpPath: ".mcp.json"
2142
2142
  },
2143
2143
  cursor: {
2144
- path: ".cursor/rules/{name}.md",
2144
+ path: ".cursor/rules/{name}.mdc",
2145
2145
  format: "frontmatter",
2146
2146
  directory: ".cursor/rules",
2147
2147
  mcpPath: "~/.cursor/mcp.json"
@@ -2156,6 +2156,11 @@ var AGENT_MAPPINGS = {
2156
2156
  format: "markdown",
2157
2157
  directory: ".roo/rules",
2158
2158
  mcpPath: ".roo/mcp.json"
2159
+ },
2160
+ generic: {
2161
+ path: "{name}.md",
2162
+ format: "markdown",
2163
+ mcpPath: "mcp.json"
2159
2164
  }
2160
2165
  };
2161
2166
  function getAgentPath(agent, name = "global") {
@@ -2210,11 +2215,12 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2210
2215
  class CursorWriter {
2211
2216
  formatContent(content, rule) {
2212
2217
  const ruleName = this.extractRuleName(rule.file);
2218
+ const hasGlobs = rule.globs && rule.globs.length > 0;
2213
2219
  const frontmatter = [
2214
2220
  "---",
2215
2221
  `description: AI Rules - ${ruleName}`,
2216
- `globs: ${rule.globs || "**/*"}`,
2217
- "alwaysApply: true",
2222
+ `globs: ${hasGlobs ? rule.globs : "**/*"}`,
2223
+ "alwaysApply: " + (hasGlobs ? "false" : "true"),
2218
2224
  "---",
2219
2225
  ""
2220
2226
  ].join(`
@@ -2251,6 +2257,7 @@ class AgentWriterFactory {
2251
2257
  case "claude":
2252
2258
  case "codex":
2253
2259
  case "roocode":
2260
+ case "generic":
2254
2261
  return new MarkdownWriter;
2255
2262
  default:
2256
2263
  return new MarkdownWriter;
@@ -2334,25 +2341,32 @@ class AgentDistributor {
2334
2341
  await this.distributeToAgent(agent, rule, content);
2335
2342
  }
2336
2343
  }
2344
+ getAgentName(agent) {
2345
+ return typeof agent === "string" ? agent : agent.name;
2346
+ }
2347
+ getCustomPath(agent) {
2348
+ return typeof agent === "object" ? agent.to : undefined;
2349
+ }
2337
2350
  async distributeToAgent(agent, rule, content) {
2338
- const ruleName = this.extractRuleName(rule.file);
2339
- const agentPath = getAgentPath(agent, ruleName);
2340
- const targetPath = join2(rule.to, agentPath);
2341
- const agentDir = getAgentDirectory(agent);
2342
- if (agentDir) {
2343
- const fullDir = join2(rule.to, agentDir);
2344
- mkdirSync(fullDir, { recursive: true });
2351
+ const agentName = this.getAgentName(agent);
2352
+ const customPath = this.getCustomPath(agent);
2353
+ let targetPath;
2354
+ if (customPath) {
2355
+ targetPath = customPath;
2345
2356
  } else {
2346
- mkdirSync(dirname(targetPath), { recursive: true });
2357
+ const ruleName = this.extractRuleName(rule.file);
2358
+ const agentPath = getAgentPath(agentName, ruleName);
2359
+ targetPath = join2(rule.to, agentPath);
2347
2360
  }
2348
- const writer = AgentWriterFactory.createWriter(agent);
2361
+ mkdirSync(dirname(targetPath), { recursive: true });
2362
+ const writer = AgentWriterFactory.createWriter(agentName);
2349
2363
  const formattedContent = writer.formatContent(content, rule);
2350
2364
  const context = {
2351
2365
  config: this.config,
2352
2366
  rule,
2353
2367
  content: formattedContent,
2354
2368
  targetPath,
2355
- agent
2369
+ agent: agentName
2356
2370
  };
2357
2371
  const finalContent = await this.applyHooks(context);
2358
2372
  writeFileSync(targetPath, finalContent, "utf-8");
@@ -2509,6 +2523,12 @@ class GitIgnoreManager {
2509
2523
  constructor(config) {
2510
2524
  this.config = config;
2511
2525
  }
2526
+ getAgentName(agent) {
2527
+ return typeof agent === "string" ? agent : agent.name;
2528
+ }
2529
+ getCustomPath(agent) {
2530
+ return typeof agent === "object" ? agent.to : undefined;
2531
+ }
2512
2532
  async updateGitIgnore() {
2513
2533
  const paths = this.collectGeneratedPaths();
2514
2534
  if (paths.length === 0) {
@@ -2539,22 +2559,34 @@ class GitIgnoreManager {
2539
2559
  const paths = new Set;
2540
2560
  for (const rule of this.config.rules) {
2541
2561
  for (const agent of rule.targets) {
2542
- const ruleName = this.extractRuleName(rule.file);
2543
- const agentPath = getAgentPath(agent, ruleName);
2544
- const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
2545
- paths.add(fullPath);
2546
- const agentDir = getAgentDirectory(agent);
2547
- if (agentDir) {
2548
- const fullDir = `${rule.to}/${agentDir}`.replace(/\/+/g, "/");
2549
- paths.add(`${fullDir}/`);
2562
+ const agentName = this.getAgentName(agent);
2563
+ const customPath = this.getCustomPath(agent);
2564
+ if (customPath) {
2565
+ paths.add(customPath);
2566
+ } else {
2567
+ const ruleName = this.extractRuleName(rule.file);
2568
+ const agentPath = getAgentPath(agentName, ruleName);
2569
+ const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
2570
+ paths.add(fullPath);
2571
+ const agentDir = getAgentDirectory(agentName);
2572
+ if (agentDir) {
2573
+ const fullDir = `${rule.to}/${agentDir}`.replace(/\/+/g, "/");
2574
+ paths.add(`${fullDir}/`);
2575
+ }
2550
2576
  }
2551
2577
  }
2552
2578
  }
2553
2579
  if (this.config.commands) {
2554
2580
  for (const command of this.config.commands) {
2555
2581
  for (const agent of command.targets) {
2556
- const agentPath = getAgentPath(agent, command.command);
2557
- paths.add(agentPath);
2582
+ const agentName = this.getAgentName(agent);
2583
+ const customPath = this.getCustomPath(agent);
2584
+ if (customPath) {
2585
+ paths.add(customPath);
2586
+ } else {
2587
+ const agentPath = getAgentPath(agentName, command.command);
2588
+ paths.add(agentPath);
2589
+ }
2558
2590
  }
2559
2591
  }
2560
2592
  }
@@ -2754,24 +2786,42 @@ class AIRulesCore {
2754
2786
  throw new Error(`Duplicate MCP names found: ${duplicates.join(", ")}`);
2755
2787
  }
2756
2788
  }
2789
+ getAgentName(agent) {
2790
+ return typeof agent === "string" ? agent : agent.name;
2791
+ }
2792
+ getCustomPath(agent) {
2793
+ return typeof agent === "object" ? agent.to : undefined;
2794
+ }
2757
2795
  collectAllGeneratedPaths() {
2758
2796
  const paths = [];
2759
2797
  for (const rule of this.config.rules) {
2760
2798
  for (const agent of rule.targets) {
2761
- const ruleName = rule.file.split("/").pop()?.replace(".md", "") || "rule";
2762
- const agentPath = getAgentPath(agent, ruleName);
2763
- let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
2764
- if (fullPath.startsWith("./")) {
2765
- fullPath = fullPath.substring(2);
2799
+ const agentName = this.getAgentName(agent);
2800
+ const customPath = this.getCustomPath(agent);
2801
+ if (customPath) {
2802
+ paths.push(customPath);
2803
+ } else {
2804
+ const ruleName = rule.file.split("/").pop()?.replace(".md", "") || "rule";
2805
+ const agentPath = getAgentPath(agentName, ruleName);
2806
+ let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
2807
+ if (fullPath.startsWith("./")) {
2808
+ fullPath = fullPath.substring(2);
2809
+ }
2810
+ paths.push(fullPath);
2766
2811
  }
2767
- paths.push(fullPath);
2768
2812
  }
2769
2813
  }
2770
2814
  if (this.config.commands) {
2771
2815
  for (const command of this.config.commands) {
2772
2816
  for (const agent of command.targets) {
2773
- const agentPath = getAgentPath(agent, command.command);
2774
- paths.push(agentPath);
2817
+ const agentName = this.getAgentName(agent);
2818
+ const customPath = this.getCustomPath(agent);
2819
+ if (customPath) {
2820
+ paths.push(customPath);
2821
+ } else {
2822
+ const agentPath = getAgentPath(agentName, command.command);
2823
+ paths.push(agentPath);
2824
+ }
2775
2825
  }
2776
2826
  }
2777
2827
  }
@@ -2876,9 +2926,18 @@ class ConfigLoader {
2876
2926
  if (!Array.isArray(r.targets) || r.targets.length === 0) {
2877
2927
  throw new Error(`Rule at index ${index}: Rule.targets must be a non-empty array`);
2878
2928
  }
2879
- const validAgents = ["claude", "cursor", "codex", "roocode"];
2880
- if (!r.targets.every((agent) => validAgents.includes(agent))) {
2881
- throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgents.join(", ")}`);
2929
+ const validAgentNames = ["claude", "cursor", "codex", "roocode", "generic"];
2930
+ if (!r.targets.every((agent) => {
2931
+ if (typeof agent === "string") {
2932
+ return validAgentNames.includes(agent);
2933
+ }
2934
+ if (typeof agent === "object" && agent !== null) {
2935
+ const a = agent;
2936
+ return validAgentNames.includes(a.name) && (a.to === undefined || typeof a.to === "string");
2937
+ }
2938
+ return false;
2939
+ })) {
2940
+ throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgentNames.join(", ")}, or objects with {name, to?}`);
2882
2941
  }
2883
2942
  }
2884
2943
  static createInitialConfig() {
@@ -2887,127 +2946,30 @@ class ConfigLoader {
2887
2946
  static createTypedConfig() {
2888
2947
  return `import { defineRules } from 'glooit';
2889
2948
 
2890
- // defineRules() provides TypeScript IntelliSense and validation
2891
2949
  export default defineRules({
2892
- // Directory where glooit stores configuration files
2893
2950
  configDir: '.glooit',
2894
-
2895
- // Default targets for all rules (can be overridden per rule)
2896
- targets: ['claude', 'cursor'],
2897
-
2898
- // Rules define how to sync your files to different AI agents
2899
2951
  rules: [
2900
2952
  {
2901
2953
  name: 'main',
2902
- file: '.glooit/main.md', // Source file to sync
2903
- to: './', // Destination directory
2904
- targets: ['claude', 'cursor'] // Which agents to sync to
2905
- }
2906
- // Add more rules here:
2907
- // {
2908
- // name: 'coding-standards',
2909
- // file: '.glooit/coding-rules.md',
2910
- // to: './',
2911
- // targets: ['claude'],
2912
- // globs: '**/*.{ts,js,tsx,jsx}' // Optional: only apply to certain file patterns
2913
- // }
2914
- ],
2915
-
2916
- // MCP (Model Context Protocol) server configurations
2917
- mcps: [
2918
- // Example MCP server configurations:
2919
- // {
2920
- // name: 'database',
2921
- // config: {
2922
- // command: 'npx',
2923
- // args: ['@modelcontextprotocol/server-postgres'],
2924
- // env: { DATABASE_URL: process.env.DATABASE_URL }
2925
- // },
2926
- // targets: ['claude']
2927
- // },
2928
- // {
2929
- // name: 'filesystem',
2930
- // config: {
2931
- // command: 'npx',
2932
- // args: ['@modelcontextprotocol/server-filesystem', process.cwd()]
2933
- // },
2934
- // targets: ['claude', 'cursor']
2935
- // }
2954
+ file: '.glooit/main.md',
2955
+ to: './',
2956
+ targets: ['claude', 'cursor']
2957
+ }
2936
2958
  ],
2937
-
2938
- // Additional options:
2939
- // mergeMcps: true, // Merge with existing MCP configs (default: true)
2940
- // backup: { // Backup settings
2941
- // enabled: true, // Create backups before syncing (default: true)
2942
- // retention: 10 // Keep last 10 backups (default: 10)
2943
- // },
2944
- // hooks: { // Custom hooks for advanced workflows
2945
- // before: [], // Run before sync
2946
- // after: [], // Run after sync
2947
- // error: [] // Run on error
2948
- // }
2949
2959
  });
2950
2960
  `;
2951
2961
  }
2952
2962
  static createPlainConfig() {
2953
2963
  return `export default {
2954
- // Directory where glooit stores configuration files
2955
2964
  configDir: '.glooit',
2956
-
2957
- // Default targets for all rules (can be overridden per rule)
2958
- targets: ['claude', 'cursor'],
2959
-
2960
- // Rules define how to sync your files to different AI agents
2961
2965
  rules: [
2962
2966
  {
2963
2967
  name: 'main',
2964
- file: '.glooit/main.md', // Source file to sync
2965
- to: './', // Destination directory
2966
- targets: ['claude', 'cursor'] // Which agents to sync to
2967
- }
2968
- // Add more rules here:
2969
- // {
2970
- // name: 'coding-standards',
2971
- // file: '.glooit/coding-rules.md',
2972
- // to: './',
2973
- // targets: ['claude'],
2974
- // globs: '**/*.{ts,js,tsx,jsx}' // Optional: only apply to certain file patterns
2975
- // }
2968
+ file: '.glooit/main.md',
2969
+ to: './',
2970
+ targets: ['claude', 'cursor']
2971
+ }
2976
2972
  ],
2977
-
2978
- // MCP (Model Context Protocol) server configurations
2979
- mcps: [
2980
- // Example MCP server configurations:
2981
- // {
2982
- // name: 'database',
2983
- // config: {
2984
- // command: 'npx',
2985
- // args: ['@modelcontextprotocol/server-postgres'],
2986
- // env: { DATABASE_URL: process.env.DATABASE_URL }
2987
- // },
2988
- // targets: ['claude']
2989
- // },
2990
- // {
2991
- // name: 'filesystem',
2992
- // config: {
2993
- // command: 'npx',
2994
- // args: ['@modelcontextprotocol/server-filesystem', process.cwd()]
2995
- // },
2996
- // targets: ['claude', 'cursor']
2997
- // }
2998
- ]
2999
-
3000
- // Additional options:
3001
- // mergeMcps: true, // Merge with existing MCP configs (default: true)
3002
- // backup: { // Backup settings
3003
- // enabled: true, // Create backups before syncing (default: true)
3004
- // retention: 10 // Keep last 10 backups (default: 10)
3005
- // },
3006
- // hooks: { // Custom hooks for advanced workflows
3007
- // before: [], // Run before sync
3008
- // after: [], // Run after sync
3009
- // error: [] // Run on error
3010
- // }
3011
2973
  };
3012
2974
  `;
3013
2975
  }
@@ -3373,7 +3335,7 @@ function constructCommand(value, args) {
3373
3335
  import { execSync } from "child_process";
3374
3336
  import { createInterface } from "readline";
3375
3337
  // package.json
3376
- var version = "0.5.1";
3338
+ var version = "0.5.3";
3377
3339
 
3378
3340
  // src/cli/index.ts
3379
3341
  var args = process.argv.slice(2);
@@ -4,6 +4,8 @@ export declare class GitIgnoreManager {
4
4
  private gitignorePath;
5
5
  private marker;
6
6
  constructor(config: Config);
7
+ private getAgentName;
8
+ private getCustomPath;
7
9
  updateGitIgnore(): Promise<void>;
8
10
  cleanupGitIgnore(): Promise<void>;
9
11
  private collectGeneratedPaths;
@@ -16,5 +16,7 @@ export declare class AIRulesCore {
16
16
  validate(): Promise<boolean>;
17
17
  private distributeMcps;
18
18
  private validateMcpNames;
19
+ private getAgentName;
20
+ private getCustomPath;
19
21
  collectAllGeneratedPaths(): string[];
20
22
  }
package/dist/index.js CHANGED
@@ -10,8 +10,18 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/types/index.ts
13
+ function isValidAgentName(agentName) {
14
+ return ["claude", "cursor", "codex", "roocode", "generic"].includes(agentName);
15
+ }
13
16
  function isValidAgent(agent) {
14
- return ["claude", "cursor", "codex", "roocode"].includes(agent);
17
+ if (typeof agent === "string") {
18
+ return isValidAgentName(agent);
19
+ }
20
+ if (typeof agent === "object" && agent !== null) {
21
+ const a = agent;
22
+ return isValidAgentName(a.name) && (a.to === undefined || typeof a.to === "string");
23
+ }
24
+ return false;
15
25
  }
16
26
  function validateRule(rule) {
17
27
  if (!rule || typeof rule !== "object") {
@@ -28,7 +38,7 @@ function validateRule(rule) {
28
38
  throw new Error("Rule.targets must be a non-empty array");
29
39
  }
30
40
  if (!r.targets.every(isValidAgent)) {
31
- throw new Error("Rule.targets must contain valid agents: claude, cursor, codex, roocode");
41
+ throw new Error("Rule.targets must contain valid agents: claude, cursor, codex, roocode, generic, or objects with {name, to?}");
32
42
  }
33
43
  }
34
44
  function validateConfig(config) {
@@ -79,7 +89,7 @@ var AGENT_MAPPINGS = {
79
89
  mcpPath: ".mcp.json"
80
90
  },
81
91
  cursor: {
82
- path: ".cursor/rules/{name}.md",
92
+ path: ".cursor/rules/{name}.mdc",
83
93
  format: "frontmatter",
84
94
  directory: ".cursor/rules",
85
95
  mcpPath: "~/.cursor/mcp.json"
@@ -94,6 +104,11 @@ var AGENT_MAPPINGS = {
94
104
  format: "markdown",
95
105
  directory: ".roo/rules",
96
106
  mcpPath: ".roo/mcp.json"
107
+ },
108
+ generic: {
109
+ path: "{name}.md",
110
+ format: "markdown",
111
+ mcpPath: "mcp.json"
97
112
  }
98
113
  };
99
114
  function getAgentPath(agent, name = "global") {
@@ -148,11 +163,12 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
148
163
  class CursorWriter {
149
164
  formatContent(content, rule) {
150
165
  const ruleName = this.extractRuleName(rule.file);
166
+ const hasGlobs = rule.globs && rule.globs.length > 0;
151
167
  const frontmatter = [
152
168
  "---",
153
169
  `description: AI Rules - ${ruleName}`,
154
- `globs: ${rule.globs || "**/*"}`,
155
- "alwaysApply: true",
170
+ `globs: ${hasGlobs ? rule.globs : "**/*"}`,
171
+ "alwaysApply: " + (hasGlobs ? "false" : "true"),
156
172
  "---",
157
173
  ""
158
174
  ].join(`
@@ -189,6 +205,7 @@ class AgentWriterFactory {
189
205
  case "claude":
190
206
  case "codex":
191
207
  case "roocode":
208
+ case "generic":
192
209
  return new MarkdownWriter;
193
210
  default:
194
211
  return new MarkdownWriter;
@@ -272,25 +289,32 @@ class AgentDistributor {
272
289
  await this.distributeToAgent(agent, rule, content);
273
290
  }
274
291
  }
292
+ getAgentName(agent) {
293
+ return typeof agent === "string" ? agent : agent.name;
294
+ }
295
+ getCustomPath(agent) {
296
+ return typeof agent === "object" ? agent.to : undefined;
297
+ }
275
298
  async distributeToAgent(agent, rule, content) {
276
- const ruleName = this.extractRuleName(rule.file);
277
- const agentPath = getAgentPath(agent, ruleName);
278
- const targetPath = join2(rule.to, agentPath);
279
- const agentDir = getAgentDirectory(agent);
280
- if (agentDir) {
281
- const fullDir = join2(rule.to, agentDir);
282
- mkdirSync(fullDir, { recursive: true });
299
+ const agentName = this.getAgentName(agent);
300
+ const customPath = this.getCustomPath(agent);
301
+ let targetPath;
302
+ if (customPath) {
303
+ targetPath = customPath;
283
304
  } else {
284
- mkdirSync(dirname(targetPath), { recursive: true });
305
+ const ruleName = this.extractRuleName(rule.file);
306
+ const agentPath = getAgentPath(agentName, ruleName);
307
+ targetPath = join2(rule.to, agentPath);
285
308
  }
286
- const writer = AgentWriterFactory.createWriter(agent);
309
+ mkdirSync(dirname(targetPath), { recursive: true });
310
+ const writer = AgentWriterFactory.createWriter(agentName);
287
311
  const formattedContent = writer.formatContent(content, rule);
288
312
  const context = {
289
313
  config: this.config,
290
314
  rule,
291
315
  content: formattedContent,
292
316
  targetPath,
293
- agent
317
+ agent: agentName
294
318
  };
295
319
  const finalContent = await this.applyHooks(context);
296
320
  writeFileSync(targetPath, finalContent, "utf-8");
@@ -447,6 +471,12 @@ class GitIgnoreManager {
447
471
  constructor(config) {
448
472
  this.config = config;
449
473
  }
474
+ getAgentName(agent) {
475
+ return typeof agent === "string" ? agent : agent.name;
476
+ }
477
+ getCustomPath(agent) {
478
+ return typeof agent === "object" ? agent.to : undefined;
479
+ }
450
480
  async updateGitIgnore() {
451
481
  const paths = this.collectGeneratedPaths();
452
482
  if (paths.length === 0) {
@@ -477,22 +507,34 @@ class GitIgnoreManager {
477
507
  const paths = new Set;
478
508
  for (const rule of this.config.rules) {
479
509
  for (const agent of rule.targets) {
480
- const ruleName = this.extractRuleName(rule.file);
481
- const agentPath = getAgentPath(agent, ruleName);
482
- const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
483
- paths.add(fullPath);
484
- const agentDir = getAgentDirectory(agent);
485
- if (agentDir) {
486
- const fullDir = `${rule.to}/${agentDir}`.replace(/\/+/g, "/");
487
- paths.add(`${fullDir}/`);
510
+ const agentName = this.getAgentName(agent);
511
+ const customPath = this.getCustomPath(agent);
512
+ if (customPath) {
513
+ paths.add(customPath);
514
+ } else {
515
+ const ruleName = this.extractRuleName(rule.file);
516
+ const agentPath = getAgentPath(agentName, ruleName);
517
+ const fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
518
+ paths.add(fullPath);
519
+ const agentDir = getAgentDirectory(agentName);
520
+ if (agentDir) {
521
+ const fullDir = `${rule.to}/${agentDir}`.replace(/\/+/g, "/");
522
+ paths.add(`${fullDir}/`);
523
+ }
488
524
  }
489
525
  }
490
526
  }
491
527
  if (this.config.commands) {
492
528
  for (const command of this.config.commands) {
493
529
  for (const agent of command.targets) {
494
- const agentPath = getAgentPath(agent, command.command);
495
- paths.add(agentPath);
530
+ const agentName = this.getAgentName(agent);
531
+ const customPath = this.getCustomPath(agent);
532
+ if (customPath) {
533
+ paths.add(customPath);
534
+ } else {
535
+ const agentPath = getAgentPath(agentName, command.command);
536
+ paths.add(agentPath);
537
+ }
496
538
  }
497
539
  }
498
540
  }
@@ -692,24 +734,42 @@ class AIRulesCore {
692
734
  throw new Error(`Duplicate MCP names found: ${duplicates.join(", ")}`);
693
735
  }
694
736
  }
737
+ getAgentName(agent) {
738
+ return typeof agent === "string" ? agent : agent.name;
739
+ }
740
+ getCustomPath(agent) {
741
+ return typeof agent === "object" ? agent.to : undefined;
742
+ }
695
743
  collectAllGeneratedPaths() {
696
744
  const paths = [];
697
745
  for (const rule of this.config.rules) {
698
746
  for (const agent of rule.targets) {
699
- const ruleName = rule.file.split("/").pop()?.replace(".md", "") || "rule";
700
- const agentPath = getAgentPath(agent, ruleName);
701
- let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
702
- if (fullPath.startsWith("./")) {
703
- fullPath = fullPath.substring(2);
747
+ const agentName = this.getAgentName(agent);
748
+ const customPath = this.getCustomPath(agent);
749
+ if (customPath) {
750
+ paths.push(customPath);
751
+ } else {
752
+ const ruleName = rule.file.split("/").pop()?.replace(".md", "") || "rule";
753
+ const agentPath = getAgentPath(agentName, ruleName);
754
+ let fullPath = `${rule.to}/${agentPath}`.replace(/\/+/g, "/");
755
+ if (fullPath.startsWith("./")) {
756
+ fullPath = fullPath.substring(2);
757
+ }
758
+ paths.push(fullPath);
704
759
  }
705
- paths.push(fullPath);
706
760
  }
707
761
  }
708
762
  if (this.config.commands) {
709
763
  for (const command of this.config.commands) {
710
764
  for (const agent of command.targets) {
711
- const agentPath = getAgentPath(agent, command.command);
712
- paths.push(agentPath);
765
+ const agentName = this.getAgentName(agent);
766
+ const customPath = this.getCustomPath(agent);
767
+ if (customPath) {
768
+ paths.push(customPath);
769
+ } else {
770
+ const agentPath = getAgentPath(agentName, command.command);
771
+ paths.push(agentPath);
772
+ }
713
773
  }
714
774
  }
715
775
  }
@@ -813,9 +873,18 @@ class ConfigLoader {
813
873
  if (!Array.isArray(r.targets) || r.targets.length === 0) {
814
874
  throw new Error(`Rule at index ${index}: Rule.targets must be a non-empty array`);
815
875
  }
816
- const validAgents = ["claude", "cursor", "codex", "roocode"];
817
- if (!r.targets.every((agent) => validAgents.includes(agent))) {
818
- throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgents.join(", ")}`);
876
+ const validAgentNames = ["claude", "cursor", "codex", "roocode", "generic"];
877
+ if (!r.targets.every((agent) => {
878
+ if (typeof agent === "string") {
879
+ return validAgentNames.includes(agent);
880
+ }
881
+ if (typeof agent === "object" && agent !== null) {
882
+ const a = agent;
883
+ return validAgentNames.includes(a.name) && (a.to === undefined || typeof a.to === "string");
884
+ }
885
+ return false;
886
+ })) {
887
+ throw new Error(`Rule at index ${index}: Rule.targets must contain valid agents: ${validAgentNames.join(", ")}, or objects with {name, to?}`);
819
888
  }
820
889
  }
821
890
  static createInitialConfig() {
@@ -824,127 +893,30 @@ class ConfigLoader {
824
893
  static createTypedConfig() {
825
894
  return `import { defineRules } from 'glooit';
826
895
 
827
- // defineRules() provides TypeScript IntelliSense and validation
828
896
  export default defineRules({
829
- // Directory where glooit stores configuration files
830
897
  configDir: '.glooit',
831
-
832
- // Default targets for all rules (can be overridden per rule)
833
- targets: ['claude', 'cursor'],
834
-
835
- // Rules define how to sync your files to different AI agents
836
898
  rules: [
837
899
  {
838
900
  name: 'main',
839
- file: '.glooit/main.md', // Source file to sync
840
- to: './', // Destination directory
841
- targets: ['claude', 'cursor'] // Which agents to sync to
901
+ file: '.glooit/main.md',
902
+ to: './',
903
+ targets: ['claude', 'cursor']
842
904
  }
843
- // Add more rules here:
844
- // {
845
- // name: 'coding-standards',
846
- // file: '.glooit/coding-rules.md',
847
- // to: './',
848
- // targets: ['claude'],
849
- // globs: '**/*.{ts,js,tsx,jsx}' // Optional: only apply to certain file patterns
850
- // }
851
905
  ],
852
-
853
- // MCP (Model Context Protocol) server configurations
854
- mcps: [
855
- // Example MCP server configurations:
856
- // {
857
- // name: 'database',
858
- // config: {
859
- // command: 'npx',
860
- // args: ['@modelcontextprotocol/server-postgres'],
861
- // env: { DATABASE_URL: process.env.DATABASE_URL }
862
- // },
863
- // targets: ['claude']
864
- // },
865
- // {
866
- // name: 'filesystem',
867
- // config: {
868
- // command: 'npx',
869
- // args: ['@modelcontextprotocol/server-filesystem', process.cwd()]
870
- // },
871
- // targets: ['claude', 'cursor']
872
- // }
873
- ],
874
-
875
- // Additional options:
876
- // mergeMcps: true, // Merge with existing MCP configs (default: true)
877
- // backup: { // Backup settings
878
- // enabled: true, // Create backups before syncing (default: true)
879
- // retention: 10 // Keep last 10 backups (default: 10)
880
- // },
881
- // hooks: { // Custom hooks for advanced workflows
882
- // before: [], // Run before sync
883
- // after: [], // Run after sync
884
- // error: [] // Run on error
885
- // }
886
906
  });
887
907
  `;
888
908
  }
889
909
  static createPlainConfig() {
890
910
  return `export default {
891
- // Directory where glooit stores configuration files
892
911
  configDir: '.glooit',
893
-
894
- // Default targets for all rules (can be overridden per rule)
895
- targets: ['claude', 'cursor'],
896
-
897
- // Rules define how to sync your files to different AI agents
898
912
  rules: [
899
913
  {
900
914
  name: 'main',
901
- file: '.glooit/main.md', // Source file to sync
902
- to: './', // Destination directory
903
- targets: ['claude', 'cursor'] // Which agents to sync to
915
+ file: '.glooit/main.md',
916
+ to: './',
917
+ targets: ['claude', 'cursor']
904
918
  }
905
- // Add more rules here:
906
- // {
907
- // name: 'coding-standards',
908
- // file: '.glooit/coding-rules.md',
909
- // to: './',
910
- // targets: ['claude'],
911
- // globs: '**/*.{ts,js,tsx,jsx}' // Optional: only apply to certain file patterns
912
- // }
913
919
  ],
914
-
915
- // MCP (Model Context Protocol) server configurations
916
- mcps: [
917
- // Example MCP server configurations:
918
- // {
919
- // name: 'database',
920
- // config: {
921
- // command: 'npx',
922
- // args: ['@modelcontextprotocol/server-postgres'],
923
- // env: { DATABASE_URL: process.env.DATABASE_URL }
924
- // },
925
- // targets: ['claude']
926
- // },
927
- // {
928
- // name: 'filesystem',
929
- // config: {
930
- // command: 'npx',
931
- // args: ['@modelcontextprotocol/server-filesystem', process.cwd()]
932
- // },
933
- // targets: ['claude', 'cursor']
934
- // }
935
- ]
936
-
937
- // Additional options:
938
- // mergeMcps: true, // Merge with existing MCP configs (default: true)
939
- // backup: { // Backup settings
940
- // enabled: true, // Create backups before syncing (default: true)
941
- // retention: 10 // Keep last 10 backups (default: 10)
942
- // },
943
- // hooks: { // Custom hooks for advanced workflows
944
- // before: [], // Run before sync
945
- // after: [], // Run after sync
946
- // error: [] // Run on error
947
- // }
948
920
  };
949
921
  `;
950
922
  }
@@ -1,4 +1,9 @@
1
- export type Agent = 'claude' | 'cursor' | 'codex' | 'roocode';
1
+ export type AgentName = 'claude' | 'cursor' | 'codex' | 'roocode' | 'generic';
2
+ export interface AgentTarget {
3
+ name: AgentName;
4
+ to?: string;
5
+ }
6
+ export type Agent = AgentName | AgentTarget;
2
7
  export interface Rule {
3
8
  name?: string;
4
9
  file: string;
@@ -25,7 +30,7 @@ export interface McpConfig {
25
30
  export interface Mcp {
26
31
  name: string;
27
32
  config: McpConfig;
28
- targets?: Agent[];
33
+ targets?: AgentName[];
29
34
  outputPath?: string;
30
35
  }
31
36
  export interface ResolvedMcp extends Omit<Mcp, 'outputPath'> {
@@ -63,7 +68,7 @@ export interface SyncContext {
63
68
  rule: Rule;
64
69
  content: string;
65
70
  targetPath: string;
66
- agent: Agent;
71
+ agent: AgentName;
67
72
  }
68
73
  export interface BackupEntry {
69
74
  timestamp: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glooit",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
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",