aicoding-rules 0.1.0
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 +95 -0
- package/bin/cli.js +60 -0
- package/package.json +24 -0
- package/src/auth.js +26 -0
- package/src/config.js +52 -0
- package/src/download.js +40 -0
- package/src/interactive.js +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# aicoding-rules
|
|
2
|
+
|
|
3
|
+
A CLI tool to download and install AI coding rules for various AI assistants.
|
|
4
|
+
|
|
5
|
+
## Supported Tools
|
|
6
|
+
|
|
7
|
+
- **Cursor** - IDE-specific rules for Cursor editor
|
|
8
|
+
- **Claude** - Rules for Claude AI assistant
|
|
9
|
+
- **Antigravity** - Antigravity platform rules
|
|
10
|
+
- **Copilot** - GitHub Copilot prompts
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g aicoding-rules
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or run directly with npx:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx aicoding-rules
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Interactive Mode
|
|
27
|
+
|
|
28
|
+
Run without arguments to select rules interactively:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx aicoding-rules
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
You'll see a checkbox menu:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
? Select rules to install: (Press <space> to select, <a> to toggle all)
|
|
38
|
+
◯ Cursor rules
|
|
39
|
+
◯ Claude rules
|
|
40
|
+
◯ Antigravity rules
|
|
41
|
+
◯ Copilot prompts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Direct Commands
|
|
45
|
+
|
|
46
|
+
Install specific rules directly:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx aicoding-rules cursor
|
|
50
|
+
npx aicoding-rules claude
|
|
51
|
+
npx aicoding-rules antigravity
|
|
52
|
+
npx aicoding-rules copilot
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Install all rules at once:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npx aicoding-rules all
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Authentication
|
|
62
|
+
|
|
63
|
+
The tool uses Plus4U OIDC authentication. On first run, a browser window will open for you to log in. The authentication token is cached for the session.
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
Create `~/.aicoding-rules.json` to override default settings:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"documentOid": "your-document-oid",
|
|
72
|
+
"baseUrl": "https://your-custom-url.com",
|
|
73
|
+
"rules": {
|
|
74
|
+
"cursor": {
|
|
75
|
+
"fileOid": "custom-file-oid",
|
|
76
|
+
"name": "Custom Cursor Rules"
|
|
77
|
+
},
|
|
78
|
+
"custom-tool": {
|
|
79
|
+
"fileOid": "another-file-oid",
|
|
80
|
+
"name": "My Custom Tool"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Custom rules defined in the config file will appear in the interactive menu.
|
|
87
|
+
|
|
88
|
+
## Output
|
|
89
|
+
|
|
90
|
+
Rules are extracted to the current working directory. Run the command from the root of your project.
|
|
91
|
+
|
|
92
|
+
## Requirements
|
|
93
|
+
|
|
94
|
+
- Node.js >= 18.0.0
|
|
95
|
+
- Plus4U account with access to the rules repository
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { loadConfig, getRuleKeys } from "../src/config.js";
|
|
5
|
+
import { downloadAndExtract, downloadMultiple } from "../src/download.js";
|
|
6
|
+
import { selectRules } from "../src/interactive.js";
|
|
7
|
+
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
const targetDir = process.cwd();
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.name("aicoding-rules")
|
|
15
|
+
.description("Download and install AI coding rules for Cursor, Claude, Antigravity, and Copilot")
|
|
16
|
+
.version("1.0.0");
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.command("cursor")
|
|
20
|
+
.description("Install Cursor rules")
|
|
21
|
+
.action(async () => {
|
|
22
|
+
await downloadAndExtract(config, "cursor", targetDir);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
program
|
|
26
|
+
.command("claude")
|
|
27
|
+
.description("Install Claude rules")
|
|
28
|
+
.action(async () => {
|
|
29
|
+
await downloadAndExtract(config, "claude", targetDir);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
program
|
|
33
|
+
.command("antigravity")
|
|
34
|
+
.description("Install Antigravity rules")
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await downloadAndExtract(config, "antigravity", targetDir);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
program
|
|
40
|
+
.command("copilot")
|
|
41
|
+
.description("Install Copilot prompts")
|
|
42
|
+
.action(async () => {
|
|
43
|
+
await downloadAndExtract(config, "copilot", targetDir);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
program
|
|
47
|
+
.command("all")
|
|
48
|
+
.description("Install all rules")
|
|
49
|
+
.action(async () => {
|
|
50
|
+
const allKeys = getRuleKeys(config);
|
|
51
|
+
await downloadMultiple(config, allKeys, targetDir);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
program
|
|
55
|
+
.action(async () => {
|
|
56
|
+
const selectedKeys = await selectRules(config);
|
|
57
|
+
await downloadMultiple(config, selectedKeys, targetDir);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "aicoding-rules",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Download and install AI coding rules for Cursor, Claude, Antigravity, and Copilot",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "src/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"aicoding-rules": "bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"uu_appg01_devkit-common": "^6.5.0",
|
|
20
|
+
"adm-zip": "^0.5.16",
|
|
21
|
+
"commander": "^12.0.0",
|
|
22
|
+
"inquirer": "^9.0.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/auth.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const OidcClient = require("uu_appg01_devkit-common/src/scripts/oidc-client");
|
|
5
|
+
const AppClient = require("uu_appg01_devkit-common/src/scripts/misc/app-client");
|
|
6
|
+
|
|
7
|
+
let cachedAppClient = null;
|
|
8
|
+
|
|
9
|
+
export async function getAuthenticatedClient() {
|
|
10
|
+
if (cachedAppClient) {
|
|
11
|
+
return cachedAppClient;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
console.log("Starting Plus4U authentication...");
|
|
15
|
+
|
|
16
|
+
const token = await OidcClient.interactiveLogin();
|
|
17
|
+
|
|
18
|
+
if (!token) {
|
|
19
|
+
throw new Error("Authentication failed - no token received");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
cachedAppClient = new AppClient({ get: () => `Bearer ${token}` });
|
|
23
|
+
console.log("Authentication successful!\n");
|
|
24
|
+
|
|
25
|
+
return cachedAppClient;
|
|
26
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
|
|
5
|
+
const CONFIG_FILE = ".aicoding-rules.json";
|
|
6
|
+
|
|
7
|
+
const DEFAULT_CONFIG = {
|
|
8
|
+
documentOid: "67eab558899a1711f6cbf55f",
|
|
9
|
+
baseUrl: "https://uuapp.plus4u.net/uu-managementkit-maing02/9d3f27636f2e439f98529b1e12231924",
|
|
10
|
+
rules: {
|
|
11
|
+
cursor: { fileOid: "67eab7b2899a1711f6cc1580", name: "Cursor rules" },
|
|
12
|
+
claude: { fileOid: "68872b563e3eb464f2214263", name: "Claude rules" },
|
|
13
|
+
antigravity: { fileOid: "69202dcfacf31067955cfc1c", name: "Antigravity rules" },
|
|
14
|
+
copilot: { fileOid: "67eab73e899a1711f6cc106f", name: "Copilot prompts" }
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function deepMerge(target, source) {
|
|
19
|
+
const result = { ...target };
|
|
20
|
+
for (const key of Object.keys(source)) {
|
|
21
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
|
|
22
|
+
result[key] = deepMerge(target[key] || {}, source[key]);
|
|
23
|
+
} else {
|
|
24
|
+
result[key] = source[key];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function loadConfig() {
|
|
31
|
+
const configPath = join(homedir(), CONFIG_FILE);
|
|
32
|
+
|
|
33
|
+
if (!existsSync(configPath)) {
|
|
34
|
+
return DEFAULT_CONFIG;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const userConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
39
|
+
return deepMerge(DEFAULT_CONFIG, userConfig);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.warn(`Warning: Failed to parse ${configPath}, using defaults.`);
|
|
42
|
+
return DEFAULT_CONFIG;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getRuleKeys(config) {
|
|
47
|
+
return Object.keys(config.rules);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function getRule(config, key) {
|
|
51
|
+
return config.rules[key];
|
|
52
|
+
}
|
package/src/download.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import AdmZip from "adm-zip";
|
|
2
|
+
import { getAuthenticatedClient } from "./auth.js";
|
|
3
|
+
|
|
4
|
+
function extractZipBuffer(buffer, outputPath) {
|
|
5
|
+
const zip = new AdmZip(buffer);
|
|
6
|
+
zip.extractAllTo(outputPath, true);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function downloadAndExtract(config, ruleKey, targetDir) {
|
|
10
|
+
const rule = config.rules[ruleKey];
|
|
11
|
+
if (!rule) {
|
|
12
|
+
throw new Error(`Unknown rule: ${ruleKey}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const appClient = await getAuthenticatedClient();
|
|
16
|
+
|
|
17
|
+
const url = `${config.baseUrl}/document/ebc/file/getDataByOid?oid=${config.documentOid}&uuEbcData.fileOid=${rule.fileOid}`;
|
|
18
|
+
|
|
19
|
+
console.log(`Downloading ${rule.name}...`);
|
|
20
|
+
|
|
21
|
+
const response = await appClient.exchange(
|
|
22
|
+
url,
|
|
23
|
+
"get",
|
|
24
|
+
{},
|
|
25
|
+
{},
|
|
26
|
+
0,
|
|
27
|
+
{ responseType: "buffer" }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const attachmentBuffer = response.body;
|
|
31
|
+
extractZipBuffer(attachmentBuffer, targetDir);
|
|
32
|
+
|
|
33
|
+
console.log(`${rule.name} installed to ${targetDir}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function downloadMultiple(config, ruleKeys, targetDir) {
|
|
37
|
+
for (const key of ruleKeys) {
|
|
38
|
+
await downloadAndExtract(config, key, targetDir);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { getRuleKeys, getRule } from "./config.js";
|
|
3
|
+
|
|
4
|
+
export async function selectRules(config) {
|
|
5
|
+
const ruleKeys = getRuleKeys(config);
|
|
6
|
+
|
|
7
|
+
const choices = ruleKeys.map(key => ({
|
|
8
|
+
name: getRule(config, key).name,
|
|
9
|
+
value: key
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const { selectedRules } = await inquirer.prompt([
|
|
13
|
+
{
|
|
14
|
+
type: "checkbox",
|
|
15
|
+
name: "selectedRules",
|
|
16
|
+
message: "Select rules to install:",
|
|
17
|
+
choices,
|
|
18
|
+
validate: (answer) => {
|
|
19
|
+
if (answer.length === 0) {
|
|
20
|
+
return "You must select at least one rule.";
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
return selectedRules;
|
|
28
|
+
}
|