create-standards 1.0.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 +89 -0
- package/bin/create-standards.js +3 -0
- package/package.json +32 -0
- package/src/cli.js +355 -0
- package/src/discover.js +113 -0
- package/src/ide-config.js +57 -0
- package/src/install.js +77 -0
- package/standards/nodejs-express-standards/README.md +59 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/SKILL.md +40 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/antigravity/.agents/skills/nodejs-express-standards/references/08-code-style-general.md +9 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/SKILL.md +40 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/claude-code/.claude/skills/nodejs-express-standards/references/08-code-style-general.md +9 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/00-index.mdc +25 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/01-project-structure.mdc +38 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/02-api-design.mdc +46 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/03-error-handling-validation.mdc +32 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/04-security.mdc +22 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/05-database-models.mdc +15 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/06-testing.mdc +15 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/07-new-component-checklist.mdc +21 -0
- package/standards/nodejs-express-standards/cursor/.cursor/rules/08-code-style.mdc +14 -0
- package/standards/nodejs-express-standards/shared-reference/01-project-structure.md +33 -0
- package/standards/nodejs-express-standards/shared-reference/02-api-design-standards.md +41 -0
- package/standards/nodejs-express-standards/shared-reference/03-error-handling-validation.md +27 -0
- package/standards/nodejs-express-standards/shared-reference/04-security-standards.md +17 -0
- package/standards/nodejs-express-standards/shared-reference/05-database-model-standards.md +10 -0
- package/standards/nodejs-express-standards/shared-reference/06-testing-standards.md +10 -0
- package/standards/nodejs-express-standards/shared-reference/07-new-component-checklist.md +16 -0
- package/standards/nodejs-express-standards/shared-reference/08-code-style-general.md +8 -0
- package/standards/rails-standards/README.md +86 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/SKILL.md +66 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/01-project-structure.md +44 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/02-api-design-standards.md +46 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/04-security-standards.md +32 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/05-database-model-standards.md +25 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/06-testing-standards.md +26 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/antigravity/.agents/skills/rails-standards/references/08-code-style-general.md +47 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/SKILL.md +66 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/01-project-structure.md +44 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/02-api-design-standards.md +46 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/04-security-standards.md +32 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/05-database-model-standards.md +25 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/06-testing-standards.md +26 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/claude-code/.claude/skills/rails-standards/references/08-code-style-general.md +47 -0
- package/standards/rails-standards/cursor/.cursor/rules/00-always-on.mdc +26 -0
- package/standards/rails-standards/cursor/.cursor/rules/01-project-structure.mdc +49 -0
- package/standards/rails-standards/cursor/.cursor/rules/02-api-design.mdc +51 -0
- package/standards/rails-standards/cursor/.cursor/rules/03-error-handling-validation.mdc +40 -0
- package/standards/rails-standards/cursor/.cursor/rules/04-security.mdc +37 -0
- package/standards/rails-standards/cursor/.cursor/rules/05-database-models.mdc +30 -0
- package/standards/rails-standards/cursor/.cursor/rules/06-testing.mdc +31 -0
- package/standards/rails-standards/cursor/.cursor/rules/07-new-component-checklist.mdc +31 -0
- package/standards/rails-standards/cursor/.cursor/rules/08-code-style.mdc +52 -0
- package/standards/rails-standards/cursor/.cursor/rules/09-quick-scaffold.mdc +88 -0
- package/standards/rails-standards/shared-reference/00-quick-scaffold.md +83 -0
- package/standards/rails-standards/shared-reference/01-project-structure.md +44 -0
- package/standards/rails-standards/shared-reference/02-api-design-standards.md +46 -0
- package/standards/rails-standards/shared-reference/03-error-handling-validation.md +35 -0
- package/standards/rails-standards/shared-reference/04-security-standards.md +32 -0
- package/standards/rails-standards/shared-reference/05-database-model-standards.md +25 -0
- package/standards/rails-standards/shared-reference/06-testing-standards.md +26 -0
- package/standards/rails-standards/shared-reference/07-new-component-checklist.md +26 -0
- package/standards/rails-standards/shared-reference/08-code-style-general.md +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# create-standards
|
|
2
|
+
|
|
3
|
+
Install coding standards for known tech stacks into your project. Copies Cursor rules, Claude Code skills, and Antigravity skills so each tool picks them up automatically — no extra config.
|
|
4
|
+
|
|
5
|
+
## Install / run
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# one-shot
|
|
9
|
+
npx create-standards --list
|
|
10
|
+
npx create-standards rails
|
|
11
|
+
npx create-standards rails --cursor
|
|
12
|
+
|
|
13
|
+
# or via npm create
|
|
14
|
+
npm create standards -- --list
|
|
15
|
+
npm create standards -- rails
|
|
16
|
+
npm create standards -- rails --cursor
|
|
17
|
+
|
|
18
|
+
# global
|
|
19
|
+
npm i -g create-standards
|
|
20
|
+
create-standards rails --claude
|
|
21
|
+
create-standards rails --cursor
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
| Command | What it does |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `create-standards --list` | List stacks and example commands |
|
|
29
|
+
| `create-standards <stack>` | Install **all** IDE packs + `shared-reference/` |
|
|
30
|
+
| `create-standards <stack> --cursor` | Cursor only → `.cursor/` |
|
|
31
|
+
| `create-standards <stack> --claude` | Claude Code only → `.claude/` |
|
|
32
|
+
| `create-standards <stack> --antigravity` | Antigravity only → `.agents/` |
|
|
33
|
+
| `create-standards <stack> --cursor --claude` | Multiple IDEs |
|
|
34
|
+
| `create-standards` (no args, TTY) | Interactive picker |
|
|
35
|
+
|
|
36
|
+
Aliases: `--available-stack` for `--list`, `--claude-code` for `--claude`.
|
|
37
|
+
|
|
38
|
+
### Options
|
|
39
|
+
|
|
40
|
+
| Flag | Meaning |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `--path <dir>` / `-C <dir>` | Install into another directory (default: cwd) |
|
|
43
|
+
| `--force` | Overwrite existing destinations |
|
|
44
|
+
| `--dry-run` | Print destinations without writing |
|
|
45
|
+
| `--no-shared` | Skip `shared-reference/` |
|
|
46
|
+
| `-h` / `--help` | Help |
|
|
47
|
+
|
|
48
|
+
## What gets created
|
|
49
|
+
|
|
50
|
+
For a full install of `rails`:
|
|
51
|
+
|
|
52
|
+
```text
|
|
53
|
+
.cursor/ # from standards/rails-standards/cursor/.cursor
|
|
54
|
+
.claude/ # from standards/rails-standards/claude-code/.claude
|
|
55
|
+
.agents/ # from standards/rails-standards/antigravity/.agents
|
|
56
|
+
shared-reference/ # human-readable source docs
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
These paths are what Cursor, Claude Code, and Antigravity already look for — no parent wrapper folder.
|
|
60
|
+
|
|
61
|
+
## Available stacks
|
|
62
|
+
|
|
63
|
+
Stacks are discovered automatically from the package’s `standards/` folder. Adding a new directory (for example `standards/django-standards/`) with the same IDE layout makes it show up in `--list` and installable as `create-standards django` after you publish a new version.
|
|
64
|
+
|
|
65
|
+
Current packs in this repo:
|
|
66
|
+
|
|
67
|
+
- `rails` (`rails-standards`)
|
|
68
|
+
- `nodejs-express` (`nodejs-express-standards`)
|
|
69
|
+
|
|
70
|
+
## Local development
|
|
71
|
+
|
|
72
|
+
From this repo (without publishing):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node bin/create-standards.js --list
|
|
76
|
+
node bin/create-standards.js rails --dry-run
|
|
77
|
+
node bin/create-standards.js rails --cursor --path /tmp/test-app --force
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Publish
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm login
|
|
84
|
+
npm publish
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-standards",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Install coding standards (Cursor, Claude Code, Antigravity) for known tech stacks into your project",
|
|
5
|
+
"main": "src/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-standards": "./bin/create-standards.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"standards"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node bin/create-standards.js",
|
|
16
|
+
"test": "node bin/create-standards.js --list"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"standards",
|
|
20
|
+
"cursor",
|
|
21
|
+
"claude",
|
|
22
|
+
"antigravity",
|
|
23
|
+
"coding-standards",
|
|
24
|
+
"create-standards"
|
|
25
|
+
],
|
|
26
|
+
"author": "Sakshi Jain",
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"type": "commonjs",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const { listAvailableStacks, findStackByName } = require('./discover');
|
|
5
|
+
const { getIdeFolderFromFlag } = require('./ide-config');
|
|
6
|
+
const { copyStandardsIntoProject } = require('./install');
|
|
7
|
+
|
|
8
|
+
const RESERVED_FLAGS = new Set([
|
|
9
|
+
'list',
|
|
10
|
+
'available-stack',
|
|
11
|
+
'available-stacks',
|
|
12
|
+
'help',
|
|
13
|
+
'h',
|
|
14
|
+
'dry-run',
|
|
15
|
+
'force',
|
|
16
|
+
'path',
|
|
17
|
+
'C',
|
|
18
|
+
'no-shared',
|
|
19
|
+
'stack',
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const FLAG_DISPLAY_ORDER = ['cursor', 'claude', 'claude-code', 'antigravity'];
|
|
23
|
+
|
|
24
|
+
function sortIdeFlags(flags) {
|
|
25
|
+
return [...flags].sort((a, b) => {
|
|
26
|
+
const orderA = FLAG_DISPLAY_ORDER.indexOf(a);
|
|
27
|
+
const orderB = FLAG_DISPLAY_ORDER.indexOf(b);
|
|
28
|
+
const rankA = orderA === -1 ? Number.MAX_SAFE_INTEGER : orderA;
|
|
29
|
+
const rankB = orderB === -1 ? Number.MAX_SAFE_INTEGER : orderB;
|
|
30
|
+
if (rankA !== rankB) return rankA - rankB;
|
|
31
|
+
return a.localeCompare(b);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getDisplayFlagsForStack(stack) {
|
|
36
|
+
const flags = sortIdeFlags(stack.idePacks.flatMap((pack) => pack.flags));
|
|
37
|
+
return flags.map((flag) => `--${flag}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function printHelp(stacks) {
|
|
41
|
+
const stackLines =
|
|
42
|
+
stacks.length === 0
|
|
43
|
+
? ' (no stacks found under standards/)'
|
|
44
|
+
: stacks
|
|
45
|
+
.map((stack) => {
|
|
46
|
+
const flags = getDisplayFlagsForStack(stack);
|
|
47
|
+
return (
|
|
48
|
+
` ${stack.shortName} (${stack.folderName})\n` +
|
|
49
|
+
` Flags: ${flags.length ? flags.join(', ') : '(none)'}\n` +
|
|
50
|
+
` create-standards ${stack.shortName}\n` +
|
|
51
|
+
` create-standards ${stack.shortName}${flags[0] ? ` ${flags[0]}` : ''}`
|
|
52
|
+
);
|
|
53
|
+
})
|
|
54
|
+
.join('\n\n');
|
|
55
|
+
|
|
56
|
+
console.log(`Usage:
|
|
57
|
+
create-standards --list
|
|
58
|
+
create-standards <stack> [--cursor] [--claude] [--antigravity] [options]
|
|
59
|
+
create-standards --stack <stack> [options]
|
|
60
|
+
|
|
61
|
+
Install coding standards into the current project directory.
|
|
62
|
+
Without IDE flags, all available IDEs for that stack are installed.
|
|
63
|
+
|
|
64
|
+
Options:
|
|
65
|
+
--list, --available-stack List stacks and example commands
|
|
66
|
+
--stack <name> Stack to install (alternative to positional)
|
|
67
|
+
--cursor Install Cursor rules only (.cursor/)
|
|
68
|
+
--claude, --claude-code Install Claude Code skill only (.claude/)
|
|
69
|
+
--antigravity Install Antigravity skill only (.agents/)
|
|
70
|
+
--path <dir>, -C <dir> Target directory (default: cwd)
|
|
71
|
+
--force Overwrite existing destinations
|
|
72
|
+
--dry-run Show what would be copied
|
|
73
|
+
--no-shared Skip shared-reference/
|
|
74
|
+
-h, --help Show help
|
|
75
|
+
|
|
76
|
+
Available stacks:
|
|
77
|
+
|
|
78
|
+
${stackLines}
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function printAvailableStacks(stacks) {
|
|
83
|
+
if (stacks.length === 0) {
|
|
84
|
+
console.log('No stacks found under standards/.');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log('Available stacks:\n');
|
|
89
|
+
for (const stack of stacks) {
|
|
90
|
+
const flags = getDisplayFlagsForStack(stack);
|
|
91
|
+
console.log(` ${stack.shortName} (${stack.folderName})`);
|
|
92
|
+
console.log(` Flags: ${flags.length ? flags.join(', ') : '(none)'}`);
|
|
93
|
+
console.log(` create-standards ${stack.shortName}`);
|
|
94
|
+
if (flags[0]) {
|
|
95
|
+
console.log(` create-standards ${stack.shortName} ${flags[0]}`);
|
|
96
|
+
}
|
|
97
|
+
console.log('');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseCommandLineArgs(argv) {
|
|
102
|
+
const options = {
|
|
103
|
+
help: false,
|
|
104
|
+
list: false,
|
|
105
|
+
stackName: null,
|
|
106
|
+
ideFlags: [],
|
|
107
|
+
targetFolder: process.cwd(),
|
|
108
|
+
force: false,
|
|
109
|
+
dryRun: false,
|
|
110
|
+
includeSharedReference: true,
|
|
111
|
+
extraArgs: [],
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < argv.length; i++) {
|
|
115
|
+
const arg = argv[i];
|
|
116
|
+
|
|
117
|
+
if (arg === '--help' || arg === '-h') {
|
|
118
|
+
options.help = true;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (arg === '--list' || arg === '--available-stack' || arg === '--available-stacks') {
|
|
122
|
+
options.list = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (arg === '--force') {
|
|
126
|
+
options.force = true;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (arg === '--dry-run') {
|
|
130
|
+
options.dryRun = true;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (arg === '--no-shared') {
|
|
134
|
+
options.includeSharedReference = false;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (arg === '--path' || arg === '-C') {
|
|
138
|
+
const value = argv[++i];
|
|
139
|
+
if (!value) throw new Error(`${arg} requires a directory path`);
|
|
140
|
+
options.targetFolder = path.resolve(value);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (arg.startsWith('--path=')) {
|
|
144
|
+
options.targetFolder = path.resolve(arg.slice('--path='.length));
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (arg === '--stack') {
|
|
148
|
+
const value = argv[++i];
|
|
149
|
+
if (!value) throw new Error('--stack requires a name');
|
|
150
|
+
options.stackName = value;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (arg.startsWith('--stack=')) {
|
|
154
|
+
options.stackName = arg.slice('--stack='.length);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (arg.startsWith('--')) {
|
|
159
|
+
const flag = arg.slice(2);
|
|
160
|
+
if (RESERVED_FLAGS.has(flag)) {
|
|
161
|
+
throw new Error(`Unexpected use of --${flag}`);
|
|
162
|
+
}
|
|
163
|
+
options.ideFlags.push(flag);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (arg.startsWith('-') && arg.length > 1) {
|
|
168
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
options.extraArgs.push(arg);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!options.stackName && options.extraArgs.length > 0) {
|
|
175
|
+
options.stackName = options.extraArgs[0];
|
|
176
|
+
}
|
|
177
|
+
if (options.extraArgs.length > 1) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`Unexpected arguments: ${options.extraArgs.slice(1).join(', ')}. ` +
|
|
180
|
+
'Pass IDE choices as flags, e.g. --cursor'
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return options;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Empty array means install every IDE for the stack
|
|
188
|
+
function resolveSelectedIdeFolders(ideFlags, stack) {
|
|
189
|
+
if (ideFlags.length === 0) return [];
|
|
190
|
+
|
|
191
|
+
const selected = [];
|
|
192
|
+
const seen = new Set();
|
|
193
|
+
|
|
194
|
+
for (const flag of ideFlags) {
|
|
195
|
+
const ideFolder = getIdeFolderFromFlag(flag);
|
|
196
|
+
const match = stack.idePacks.find(
|
|
197
|
+
(pack) => pack.ideFolder === ideFolder || pack.flags.includes(flag)
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
if (!match) {
|
|
201
|
+
const available = stack.idePacks.flatMap((pack) =>
|
|
202
|
+
pack.flags.map((name) => `--${name}`)
|
|
203
|
+
);
|
|
204
|
+
throw new Error(
|
|
205
|
+
`Unknown or unavailable IDE flag --${flag} for stack "${stack.shortName}".\n` +
|
|
206
|
+
`Available: ${available.length ? available.join(', ') : '(none)'}`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!seen.has(match.ideFolder)) {
|
|
211
|
+
seen.add(match.ideFolder);
|
|
212
|
+
selected.push(match.ideFolder);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return selected;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function askQuestion(rl, prompt) {
|
|
220
|
+
return new Promise((resolve) => {
|
|
221
|
+
rl.question(prompt, (answer) => resolve(answer.trim()));
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function promptForStackAndIde(stacks) {
|
|
226
|
+
if (stacks.length === 0) {
|
|
227
|
+
throw new Error('No stacks found under standards/.');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const rl = readline.createInterface({
|
|
231
|
+
input: process.stdin,
|
|
232
|
+
output: process.stdout,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
console.log('Available stacks:\n');
|
|
237
|
+
stacks.forEach((stack, index) => {
|
|
238
|
+
console.log(` ${index + 1}. ${stack.shortName}`);
|
|
239
|
+
});
|
|
240
|
+
console.log('');
|
|
241
|
+
|
|
242
|
+
const stackAnswer = await askQuestion(rl, `Choose a stack [1-${stacks.length}]: `);
|
|
243
|
+
const stackIndex = Number.parseInt(stackAnswer, 10);
|
|
244
|
+
if (!Number.isInteger(stackIndex) || stackIndex < 1 || stackIndex > stacks.length) {
|
|
245
|
+
throw new Error('Invalid stack selection.');
|
|
246
|
+
}
|
|
247
|
+
const stack = stacks[stackIndex - 1];
|
|
248
|
+
|
|
249
|
+
console.log('\nInstall target:\n');
|
|
250
|
+
console.log(' 1. All IDEs');
|
|
251
|
+
stack.idePacks.forEach((pack, index) => {
|
|
252
|
+
console.log(
|
|
253
|
+
` ${index + 2}. ${pack.ideFolder} (${pack.flags.map((flag) => `--${flag}`).join(', ')})`
|
|
254
|
+
);
|
|
255
|
+
});
|
|
256
|
+
console.log('');
|
|
257
|
+
|
|
258
|
+
const ideAnswer = await askQuestion(
|
|
259
|
+
rl,
|
|
260
|
+
`Choose IDE [1-${stack.idePacks.length + 1}] (default 1): `
|
|
261
|
+
);
|
|
262
|
+
const ideIndex = ideAnswer === '' ? 1 : Number.parseInt(ideAnswer, 10);
|
|
263
|
+
if (
|
|
264
|
+
!Number.isInteger(ideIndex) ||
|
|
265
|
+
ideIndex < 1 ||
|
|
266
|
+
ideIndex > stack.idePacks.length + 1
|
|
267
|
+
) {
|
|
268
|
+
throw new Error('Invalid IDE selection.');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
stackName: stack.shortName,
|
|
273
|
+
selectedIdeFolders: ideIndex === 1 ? [] : [stack.idePacks[ideIndex - 2].ideFolder],
|
|
274
|
+
};
|
|
275
|
+
} finally {
|
|
276
|
+
rl.close();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function installAndReport(stack, selectedIdeFolders, options) {
|
|
281
|
+
const { createdPaths } = copyStandardsIntoProject({
|
|
282
|
+
stack,
|
|
283
|
+
selectedIdeFolders,
|
|
284
|
+
targetFolder: options.targetFolder,
|
|
285
|
+
force: options.force,
|
|
286
|
+
dryRun: options.dryRun,
|
|
287
|
+
includeSharedReference: options.includeSharedReference,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
console.log(options.dryRun ? '[dry-run] Would create:' : 'Created:');
|
|
291
|
+
for (const createdPath of createdPaths) {
|
|
292
|
+
console.log(` ${createdPath}`);
|
|
293
|
+
}
|
|
294
|
+
if (!options.dryRun) {
|
|
295
|
+
console.log(
|
|
296
|
+
`\nInstalled "${stack.shortName}" standards into ${path.resolve(options.targetFolder)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function ensureTargetFolderExists(targetFolder) {
|
|
302
|
+
if (!fs.existsSync(targetFolder)) {
|
|
303
|
+
fs.mkdirSync(targetFolder, { recursive: true });
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function run(argv) {
|
|
308
|
+
try {
|
|
309
|
+
const stacks = listAvailableStacks();
|
|
310
|
+
const options = parseCommandLineArgs(argv);
|
|
311
|
+
|
|
312
|
+
if (options.help) {
|
|
313
|
+
printHelp(stacks);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (options.list) {
|
|
318
|
+
printAvailableStacks(stacks);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!options.stackName) {
|
|
323
|
+
if (!process.stdin.isTTY) {
|
|
324
|
+
printHelp(stacks);
|
|
325
|
+
process.exitCode = 1;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const choice = await promptForStackAndIde(stacks);
|
|
330
|
+
const stack = findStackByName(choice.stackName);
|
|
331
|
+
if (!stack) throw new Error(`Unknown stack: ${choice.stackName}`);
|
|
332
|
+
|
|
333
|
+
ensureTargetFolderExists(options.targetFolder);
|
|
334
|
+
installAndReport(stack, choice.selectedIdeFolders, options);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const stack = findStackByName(options.stackName);
|
|
339
|
+
if (!stack) {
|
|
340
|
+
const names = stacks.map((item) => item.shortName).join(', ');
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Unknown stack "${options.stackName}". Available: ${names || '(none)'}`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const selectedIdeFolders = resolveSelectedIdeFolders(options.ideFlags, stack);
|
|
347
|
+
ensureTargetFolderExists(options.targetFolder);
|
|
348
|
+
installAndReport(stack, selectedIdeFolders, options);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error(`Error: ${error.message}`);
|
|
351
|
+
process.exitCode = 1;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
module.exports = { run, parseCommandLineArgs };
|
package/src/discover.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const {
|
|
4
|
+
IDE_CONFIG,
|
|
5
|
+
getStackShortName,
|
|
6
|
+
getFlagsForIde,
|
|
7
|
+
getInstallPathsForIde,
|
|
8
|
+
} = require('./ide-config');
|
|
9
|
+
|
|
10
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
11
|
+
const STANDARDS_FOLDER = path.join(PACKAGE_ROOT, 'standards');
|
|
12
|
+
|
|
13
|
+
function isDirectory(filePath) {
|
|
14
|
+
return fs.existsSync(filePath) && fs.statSync(filePath).isDirectory();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Find IDE packs inside one stack folder (cursor/, claude-code/, antigravity/, …)
|
|
18
|
+
function findIdePacksInStack(stackPath) {
|
|
19
|
+
if (!isDirectory(stackPath)) return [];
|
|
20
|
+
|
|
21
|
+
const idePacks = [];
|
|
22
|
+
|
|
23
|
+
for (const entry of fs.readdirSync(stackPath, { withFileTypes: true })) {
|
|
24
|
+
if (!entry.isDirectory()) continue;
|
|
25
|
+
if (entry.name === 'shared-reference' || entry.name.startsWith('.')) continue;
|
|
26
|
+
|
|
27
|
+
const ideFolder = entry.name;
|
|
28
|
+
const idePath = path.join(stackPath, ideFolder);
|
|
29
|
+
|
|
30
|
+
if (IDE_CONFIG[ideFolder]) {
|
|
31
|
+
const { sourceFolder, installAs } = getInstallPathsForIde(ideFolder);
|
|
32
|
+
const sourcePath = path.join(idePath, sourceFolder);
|
|
33
|
+
if (isDirectory(sourcePath)) {
|
|
34
|
+
idePacks.push({
|
|
35
|
+
ideFolder,
|
|
36
|
+
sourcePath,
|
|
37
|
+
installAs,
|
|
38
|
+
flags: getFlagsForIde(ideFolder),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Newer IDE folders: look for .<name> or a single hidden nested folder
|
|
45
|
+
const preferredSource = path.join(idePath, `.${ideFolder}`);
|
|
46
|
+
if (isDirectory(preferredSource)) {
|
|
47
|
+
idePacks.push({
|
|
48
|
+
ideFolder,
|
|
49
|
+
sourcePath: preferredSource,
|
|
50
|
+
installAs: `.${ideFolder}`,
|
|
51
|
+
flags: getFlagsForIde(ideFolder),
|
|
52
|
+
});
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const hiddenFolders = fs
|
|
57
|
+
.readdirSync(idePath, { withFileTypes: true })
|
|
58
|
+
.filter((child) => child.isDirectory() && child.name.startsWith('.'));
|
|
59
|
+
|
|
60
|
+
if (hiddenFolders.length === 1) {
|
|
61
|
+
idePacks.push({
|
|
62
|
+
ideFolder,
|
|
63
|
+
sourcePath: path.join(idePath, hiddenFolders[0].name),
|
|
64
|
+
installAs: hiddenFolders[0].name,
|
|
65
|
+
flags: getFlagsForIde(ideFolder),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return idePacks.sort((a, b) => a.ideFolder.localeCompare(b.ideFolder));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function listAvailableStacks() {
|
|
74
|
+
if (!isDirectory(STANDARDS_FOLDER)) return [];
|
|
75
|
+
|
|
76
|
+
return fs
|
|
77
|
+
.readdirSync(STANDARDS_FOLDER, { withFileTypes: true })
|
|
78
|
+
.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
79
|
+
.map((entry) => {
|
|
80
|
+
const stackPath = path.join(STANDARDS_FOLDER, entry.name);
|
|
81
|
+
const sharedReferencePath = path.join(stackPath, 'shared-reference');
|
|
82
|
+
const hasSharedReference = isDirectory(sharedReferencePath);
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
folderName: entry.name,
|
|
86
|
+
shortName: getStackShortName(entry.name),
|
|
87
|
+
path: stackPath,
|
|
88
|
+
idePacks: findIdePacksInStack(stackPath),
|
|
89
|
+
hasSharedReference,
|
|
90
|
+
sharedReferencePath: hasSharedReference ? sharedReferencePath : null,
|
|
91
|
+
};
|
|
92
|
+
})
|
|
93
|
+
.sort((a, b) => a.shortName.localeCompare(b.shortName));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function findStackByName(name) {
|
|
97
|
+
const normalized = name.trim().toLowerCase();
|
|
98
|
+
return (
|
|
99
|
+
listAvailableStacks().find(
|
|
100
|
+
(stack) =>
|
|
101
|
+
stack.shortName.toLowerCase() === normalized ||
|
|
102
|
+
stack.folderName.toLowerCase() === normalized
|
|
103
|
+
) || null
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
PACKAGE_ROOT,
|
|
109
|
+
STANDARDS_FOLDER,
|
|
110
|
+
listAvailableStacks,
|
|
111
|
+
findIdePacksInStack,
|
|
112
|
+
findStackByName,
|
|
113
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// Maps each IDE folder under a stack to CLI flags and where files land in the project.
|
|
2
|
+
const IDE_CONFIG = {
|
|
3
|
+
cursor: {
|
|
4
|
+
folder: 'cursor',
|
|
5
|
+
sourceFolder: '.cursor',
|
|
6
|
+
installAs: '.cursor',
|
|
7
|
+
flags: ['cursor'],
|
|
8
|
+
},
|
|
9
|
+
'claude-code': {
|
|
10
|
+
folder: 'claude-code',
|
|
11
|
+
sourceFolder: '.claude',
|
|
12
|
+
installAs: '.claude',
|
|
13
|
+
flags: ['claude', 'claude-code'],
|
|
14
|
+
},
|
|
15
|
+
antigravity: {
|
|
16
|
+
folder: 'antigravity',
|
|
17
|
+
sourceFolder: '.agents',
|
|
18
|
+
installAs: '.agents',
|
|
19
|
+
flags: ['antigravity'],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// "rails-skills" → "rails"
|
|
24
|
+
function getStackShortName(folderName) {
|
|
25
|
+
return folderName.endsWith('-skills')
|
|
26
|
+
? folderName.slice(0, -'-skills'.length)
|
|
27
|
+
: folderName;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// "--claude" → "claude-code"; unknown flags map 1:1 to a folder name
|
|
31
|
+
function getIdeFolderFromFlag(flag) {
|
|
32
|
+
for (const ide of Object.values(IDE_CONFIG)) {
|
|
33
|
+
if (ide.flags.includes(flag)) return ide.folder;
|
|
34
|
+
}
|
|
35
|
+
return flag || null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getFlagsForIde(ideFolder) {
|
|
39
|
+
const known = IDE_CONFIG[ideFolder];
|
|
40
|
+
return known ? known.flags : [ideFolder];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getInstallPathsForIde(ideFolder) {
|
|
44
|
+
const known = IDE_CONFIG[ideFolder];
|
|
45
|
+
if (known) {
|
|
46
|
+
return { sourceFolder: known.sourceFolder, installAs: known.installAs };
|
|
47
|
+
}
|
|
48
|
+
return { sourceFolder: `.${ideFolder}`, installAs: `.${ideFolder}` };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = {
|
|
52
|
+
IDE_CONFIG,
|
|
53
|
+
getStackShortName,
|
|
54
|
+
getIdeFolderFromFlag,
|
|
55
|
+
getFlagsForIde,
|
|
56
|
+
getInstallPathsForIde,
|
|
57
|
+
};
|