ai-ide-config 0.1.0 → 0.1.2
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 +117 -117
- package/bin/cli.mjs +186 -186
- package/package.json +3 -3
- package/src/init.mjs +195 -195
- package/src/package-manager.mjs +95 -95
- package/src/paths.mjs +20 -20
- package/src/prompt.mjs +49 -49
- package/src/skills.mjs +46 -46
- package/src/stacks.mjs +20 -20
- package/templates/angular/AGENTS.md +50 -50
- package/templates/angular/pnpm-workspace.yaml +5 -5
- package/templates/shared/.cursor/commands/generate-pr-description.md +70 -70
- package/templates/shared/.cursor/rules/conventional-commits.mdc +63 -63
package/README.md
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
# ai-ide-config
|
|
2
|
-
|
|
3
|
-
Scaffold shared Cursor IDE / agent setup into a project: shared `.cursor` rules/commands, plus stack-specific `AGENTS.md` and stack rules when needed.
|
|
4
|
-
|
|
5
|
-
## Install / run
|
|
6
|
-
|
|
7
|
-
No global install required:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npx ai-ide-config init
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
From a local checkout:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
cd ai-ide-config
|
|
17
|
-
npm link
|
|
18
|
-
cd /path/to/your-project
|
|
19
|
-
npx ai-ide-config init
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Usage
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Interactive: pick a stack
|
|
26
|
-
npx ai-ide-config init
|
|
27
|
-
|
|
28
|
-
# Non-interactive
|
|
29
|
-
npx ai-ide-config init --stack angular
|
|
30
|
-
npx ai-ide-config init-angular
|
|
31
|
-
|
|
32
|
-
# Options
|
|
33
|
-
npx ai-ide-config init --force # overwrite existing files
|
|
34
|
-
npx ai-ide-config init --dry-run # preview only
|
|
35
|
-
npx ai-ide-config init --skip-skills # skip
|
|
36
|
-
npx ai-ide-config init ./apps/web # target directory
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Existing template files are **skipped** unless you pass `--force`.
|
|
40
|
-
|
|
41
|
-
`.gitignore` is handled specially:
|
|
42
|
-
|
|
43
|
-
- If missing → create it with `.cursor/` and `.agents/`
|
|
44
|
-
- If present → append `.cursor/` and/or `.agents/` only when missing
|
|
45
|
-
|
|
46
|
-
If `package.json` exists, init pins an **exact** `packageManager` / `devEngines.packageManager` to the installed pnpm version. That avoids Corepack failing on ranges like `^11.13.0` from `pnpm init`.
|
|
47
|
-
|
|
48
|
-
After scaffolding, the CLI runs:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
pnpx skills add
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
(`pnpx` is used instead of `npx` because the skills package requires pnpm via `devEngines`.)
|
|
55
|
-
|
|
56
|
-
Use `--skip-skills` to skip that step. On `--dry-run`, the command is only printed.
|
|
57
|
-
|
|
58
|
-
In CI or non-TTY shells, pass `--stack` (or use `init-<stack>`). Interactive prompts require a terminal.
|
|
59
|
-
|
|
60
|
-
## What gets written
|
|
61
|
-
|
|
62
|
-
| Path | Source |
|
|
63
|
-
|------|--------|
|
|
64
|
-
| `.cursor/rules/conventional-commits.mdc` | shared |
|
|
65
|
-
| `.cursor/commands/generate-pr-description.md` | shared |
|
|
66
|
-
| `.cursor/rules/angular-20.mdc` | angular ([angular.dev](https://angular.dev/assets/context/angular-20.mdc)) |
|
|
67
|
-
| `AGENTS.md` | angular |
|
|
68
|
-
| `pnpm-workspace.yaml` | angular (`blockExoticSubdeps`, `minimumReleaseAge: 4320`) |
|
|
69
|
-
| `.gitignore` | create or append `.cursor/` and `.agents/` |
|
|
70
|
-
|
|
71
|
-
After init, edit `AGENTS.md` placeholders (`<PROJECT_NAME>`, package manager, domains, paths) for the target repo.
|
|
72
|
-
|
|
73
|
-
## Stacks
|
|
74
|
-
|
|
75
|
-
| Id | Label |
|
|
76
|
-
|----|-------|
|
|
77
|
-
| `angular` | Angular |
|
|
78
|
-
|
|
79
|
-
### Adding a stack
|
|
80
|
-
|
|
81
|
-
1. Add `templates/<stack>/AGENTS.md`
|
|
82
|
-
2. Register it in [`src/stacks.mjs`](src/stacks.mjs)
|
|
83
|
-
3. Document it here
|
|
84
|
-
|
|
85
|
-
Shared Cursor rules/commands stay under `templates/shared/`.
|
|
86
|
-
|
|
87
|
-
## Publishing to npm
|
|
88
|
-
|
|
89
|
-
CD publishes to npm when you create a **GitHub Release** (workflow: [`.github/workflows/publish.yml`](.github/workflows/publish.yml)).
|
|
90
|
-
|
|
91
|
-
### One-time setup
|
|
92
|
-
|
|
93
|
-
1. Create an [npmjs.com](https://www.npmjs.com) account and verify your email.
|
|
94
|
-
2. Create the package on npm (first publish can also create it), or claim the name `ai-ide-config`.
|
|
95
|
-
3. In the package settings on npm → **Trusted Publisher**:
|
|
96
|
-
- **Organization or user:** `iraldoad`
|
|
97
|
-
- **Repository:** `ai-ide-config`
|
|
98
|
-
- **Workflow filename:** `publish.yml`
|
|
99
|
-
- Allow **npm publish**
|
|
100
|
-
4. No `NPM_TOKEN` secret is required (OIDC Trusted Publishing).
|
|
101
|
-
|
|
102
|
-
### Release a version
|
|
103
|
-
|
|
104
|
-
1. Bump `version` in `package.json` (e.g. `0.1.1`).
|
|
105
|
-
2. Commit and push.
|
|
106
|
-
3. Create a GitHub Release for that commit (e.g. tag `v0.1.1`).
|
|
107
|
-
4. The workflow runs `npm publish --access public --provenance`.
|
|
108
|
-
|
|
109
|
-
After the first successful publish:
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx ai-ide-config init
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## License
|
|
116
|
-
|
|
117
|
-
MIT
|
|
1
|
+
# ai-ide-config
|
|
2
|
+
|
|
3
|
+
Scaffold shared Cursor IDE / agent setup into a project: shared `.cursor` rules/commands, plus stack-specific `AGENTS.md` and stack rules when needed.
|
|
4
|
+
|
|
5
|
+
## Install / run
|
|
6
|
+
|
|
7
|
+
No global install required:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx ai-ide-config init
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
From a local checkout:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd ai-ide-config
|
|
17
|
+
npm link
|
|
18
|
+
cd /path/to/your-project
|
|
19
|
+
npx ai-ide-config init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Interactive: pick a stack
|
|
26
|
+
npx ai-ide-config init
|
|
27
|
+
|
|
28
|
+
# Non-interactive
|
|
29
|
+
npx ai-ide-config init --stack angular
|
|
30
|
+
npx ai-ide-config init-angular
|
|
31
|
+
|
|
32
|
+
# Options
|
|
33
|
+
npx ai-ide-config init --force # overwrite existing files
|
|
34
|
+
npx ai-ide-config init --dry-run # preview only
|
|
35
|
+
npx ai-ide-config init --skip-skills # skip midudev/autoskills install
|
|
36
|
+
npx ai-ide-config init ./apps/web # target directory
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Existing template files are **skipped** unless you pass `--force`.
|
|
40
|
+
|
|
41
|
+
`.gitignore` is handled specially:
|
|
42
|
+
|
|
43
|
+
- If missing → create it with `.cursor/` and `.agents/`
|
|
44
|
+
- If present → append `.cursor/` and/or `.agents/` only when missing
|
|
45
|
+
|
|
46
|
+
If `package.json` exists, init pins an **exact** `packageManager` / `devEngines.packageManager` to the installed pnpm version. That avoids Corepack failing on ranges like `^11.13.0` from `pnpm init`.
|
|
47
|
+
|
|
48
|
+
After scaffolding, the CLI runs:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pnpx skills add midudev/autoskills
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
(`pnpx` is used instead of `npx` because the skills package requires pnpm via `devEngines`.)
|
|
55
|
+
|
|
56
|
+
Use `--skip-skills` to skip that step. On `--dry-run`, the command is only printed.
|
|
57
|
+
|
|
58
|
+
In CI or non-TTY shells, pass `--stack` (or use `init-<stack>`). Interactive prompts require a terminal.
|
|
59
|
+
|
|
60
|
+
## What gets written
|
|
61
|
+
|
|
62
|
+
| Path | Source |
|
|
63
|
+
|------|--------|
|
|
64
|
+
| `.cursor/rules/conventional-commits.mdc` | shared |
|
|
65
|
+
| `.cursor/commands/generate-pr-description.md` | shared |
|
|
66
|
+
| `.cursor/rules/angular-20.mdc` | angular ([angular.dev](https://angular.dev/assets/context/angular-20.mdc)) |
|
|
67
|
+
| `AGENTS.md` | angular |
|
|
68
|
+
| `pnpm-workspace.yaml` | angular (`blockExoticSubdeps`, `minimumReleaseAge: 4320`) |
|
|
69
|
+
| `.gitignore` | create or append `.cursor/` and `.agents/` |
|
|
70
|
+
|
|
71
|
+
After init, edit `AGENTS.md` placeholders (`<PROJECT_NAME>`, package manager, domains, paths) for the target repo.
|
|
72
|
+
|
|
73
|
+
## Stacks
|
|
74
|
+
|
|
75
|
+
| Id | Label |
|
|
76
|
+
|----|-------|
|
|
77
|
+
| `angular` | Angular |
|
|
78
|
+
|
|
79
|
+
### Adding a stack
|
|
80
|
+
|
|
81
|
+
1. Add `templates/<stack>/AGENTS.md`
|
|
82
|
+
2. Register it in [`src/stacks.mjs`](src/stacks.mjs)
|
|
83
|
+
3. Document it here
|
|
84
|
+
|
|
85
|
+
Shared Cursor rules/commands stay under `templates/shared/`.
|
|
86
|
+
|
|
87
|
+
## Publishing to npm
|
|
88
|
+
|
|
89
|
+
CD publishes to npm when you create a **GitHub Release** (workflow: [`.github/workflows/publish.yml`](.github/workflows/publish.yml)).
|
|
90
|
+
|
|
91
|
+
### One-time setup
|
|
92
|
+
|
|
93
|
+
1. Create an [npmjs.com](https://www.npmjs.com) account and verify your email.
|
|
94
|
+
2. Create the package on npm (first publish can also create it), or claim the name `ai-ide-config`.
|
|
95
|
+
3. In the package settings on npm → **Trusted Publisher**:
|
|
96
|
+
- **Organization or user:** `iraldoad`
|
|
97
|
+
- **Repository:** `ai-ide-config`
|
|
98
|
+
- **Workflow filename:** `publish.yml`
|
|
99
|
+
- Allow **npm publish**
|
|
100
|
+
4. No `NPM_TOKEN` secret is required (OIDC Trusted Publishing).
|
|
101
|
+
|
|
102
|
+
### Release a version
|
|
103
|
+
|
|
104
|
+
1. Bump `version` in `package.json` (e.g. `0.1.1`).
|
|
105
|
+
2. Commit and push.
|
|
106
|
+
3. Create a GitHub Release for that commit (e.g. tag `v0.1.1`).
|
|
107
|
+
4. The workflow runs `npm publish --access public --provenance`.
|
|
108
|
+
|
|
109
|
+
After the first successful publish:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx ai-ide-config init
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/bin/cli.mjs
CHANGED
|
@@ -1,186 +1,186 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getStack, listStacks, stackIds } from '../src/stacks.mjs';
|
|
5
|
-
import { promptForStack } from '../src/prompt.mjs';
|
|
6
|
-
import { initStack, printReport } from '../src/init.mjs';
|
|
7
|
-
import { runSkillsAdd, SKILLS_ADD_COMMAND } from '../src/skills.mjs';
|
|
8
|
-
|
|
9
|
-
function printUsage() {
|
|
10
|
-
const stacks = listStacks()
|
|
11
|
-
.map((s) => ` init-${s.id}`.padEnd(18) + `# shortcut for init --stack ${s.id}`)
|
|
12
|
-
.join('\n');
|
|
13
|
-
|
|
14
|
-
console.log(`Usage:
|
|
15
|
-
ai-ide-config init [dir] [--stack <name>] [--force] [--dry-run] [--skip-skills]
|
|
16
|
-
ai-ide-config init-<stack> [dir] [--force] [--dry-run] [--skip-skills]
|
|
17
|
-
|
|
18
|
-
Options:
|
|
19
|
-
--stack <name> Skip interactive prompt (e.g. angular)
|
|
20
|
-
--force Overwrite existing files
|
|
21
|
-
--dry-run Show what would be written
|
|
22
|
-
--skip-skills Do not run: ${SKILLS_ADD_COMMAND}
|
|
23
|
-
|
|
24
|
-
Stacks:
|
|
25
|
-
${listStacks()
|
|
26
|
-
.map((s) => ` ${s.id.padEnd(12)} ${s.label}`)
|
|
27
|
-
.join('\n')}
|
|
28
|
-
|
|
29
|
-
Shortcuts:
|
|
30
|
-
${stacks}
|
|
31
|
-
`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @param {string[]} argv
|
|
36
|
-
*/
|
|
37
|
-
function parseArgs(argv) {
|
|
38
|
-
const args = [...argv];
|
|
39
|
-
/** @type {{ command: string | null, targetDir: string, stackId: string | null, force: boolean, dryRun: boolean, skipSkills: boolean, help: boolean }} */
|
|
40
|
-
const result = {
|
|
41
|
-
command: null,
|
|
42
|
-
targetDir: process.cwd(),
|
|
43
|
-
stackId: null,
|
|
44
|
-
force: false,
|
|
45
|
-
dryRun: false,
|
|
46
|
-
skipSkills: false,
|
|
47
|
-
help: false,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const positionals = [];
|
|
51
|
-
|
|
52
|
-
while (args.length > 0) {
|
|
53
|
-
const token = args.shift();
|
|
54
|
-
|
|
55
|
-
if (token === '--help' || token === '-h') {
|
|
56
|
-
result.help = true;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (token === '--force') {
|
|
60
|
-
result.force = true;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (token === '--dry-run') {
|
|
64
|
-
result.dryRun = true;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
if (token === '--skip-skills') {
|
|
68
|
-
result.skipSkills = true;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (token === '--stack') {
|
|
72
|
-
const value = args.shift();
|
|
73
|
-
if (!value) {
|
|
74
|
-
throw new Error('--stack requires a value');
|
|
75
|
-
}
|
|
76
|
-
result.stackId = value;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (token.startsWith('-')) {
|
|
80
|
-
throw new Error(`Unknown option: ${token}`);
|
|
81
|
-
}
|
|
82
|
-
positionals.push(token);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
result.command = positionals[0] ?? null;
|
|
86
|
-
if (positionals[1]) {
|
|
87
|
-
result.targetDir = path.resolve(positionals[1]);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return result;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Resolve stack id from command name like init-angular.
|
|
95
|
-
* @param {string} command
|
|
96
|
-
* @returns {string | null}
|
|
97
|
-
*/
|
|
98
|
-
function stackFromInitAlias(command) {
|
|
99
|
-
const match = /^init-(.+)$/.exec(command);
|
|
100
|
-
if (!match) return null;
|
|
101
|
-
return match[1];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async function main() {
|
|
105
|
-
let parsed;
|
|
106
|
-
try {
|
|
107
|
-
parsed = parseArgs(process.argv.slice(2));
|
|
108
|
-
} catch (err) {
|
|
109
|
-
console.error(err.message);
|
|
110
|
-
printUsage();
|
|
111
|
-
process.exitCode = 1;
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (parsed.help || !parsed.command) {
|
|
116
|
-
printUsage();
|
|
117
|
-
process.exitCode = parsed.help ? 0 : 1;
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let stackId = parsed.stackId;
|
|
122
|
-
|
|
123
|
-
if (parsed.command === 'init') {
|
|
124
|
-
// stackId may already be set via --stack
|
|
125
|
-
} else {
|
|
126
|
-
const aliasStack = stackFromInitAlias(parsed.command);
|
|
127
|
-
if (aliasStack) {
|
|
128
|
-
if (stackId && stackId !== aliasStack) {
|
|
129
|
-
console.error(
|
|
130
|
-
`Conflicting stacks: command is init-${aliasStack} but --stack ${stackId} was passed.`,
|
|
131
|
-
);
|
|
132
|
-
process.exitCode = 1;
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
stackId = aliasStack;
|
|
136
|
-
} else {
|
|
137
|
-
console.error(`Unknown command: ${parsed.command}`);
|
|
138
|
-
printUsage();
|
|
139
|
-
process.exitCode = 1;
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (!stackId) {
|
|
145
|
-
try {
|
|
146
|
-
stackId = await promptForStack();
|
|
147
|
-
} catch (err) {
|
|
148
|
-
console.error(err.message);
|
|
149
|
-
process.exitCode = 1;
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const stack = getStack(stackId);
|
|
155
|
-
if (!stack) {
|
|
156
|
-
console.error(
|
|
157
|
-
`Unknown stack "${stackId}". Available: ${stackIds().join(', ')}`,
|
|
158
|
-
);
|
|
159
|
-
process.exitCode = 1;
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
const report = initStack({
|
|
165
|
-
stack,
|
|
166
|
-
targetDir: parsed.targetDir,
|
|
167
|
-
force: parsed.force,
|
|
168
|
-
dryRun: parsed.dryRun,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
console.log(
|
|
172
|
-
`${parsed.dryRun ? '[dry-run] ' : ''}Scaffolding ${stack.label} AI IDE config into ${parsed.targetDir}`,
|
|
173
|
-
);
|
|
174
|
-
printReport(report, { dryRun: parsed.dryRun });
|
|
175
|
-
|
|
176
|
-
runSkillsAdd(parsed.targetDir, {
|
|
177
|
-
dryRun: parsed.dryRun,
|
|
178
|
-
skip: parsed.skipSkills,
|
|
179
|
-
});
|
|
180
|
-
} catch (err) {
|
|
181
|
-
console.error(err.message);
|
|
182
|
-
process.exitCode = 1;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { getStack, listStacks, stackIds } from '../src/stacks.mjs';
|
|
5
|
+
import { promptForStack } from '../src/prompt.mjs';
|
|
6
|
+
import { initStack, printReport } from '../src/init.mjs';
|
|
7
|
+
import { runSkillsAdd, SKILLS_ADD_COMMAND } from '../src/skills.mjs';
|
|
8
|
+
|
|
9
|
+
function printUsage() {
|
|
10
|
+
const stacks = listStacks()
|
|
11
|
+
.map((s) => ` init-${s.id}`.padEnd(18) + `# shortcut for init --stack ${s.id}`)
|
|
12
|
+
.join('\n');
|
|
13
|
+
|
|
14
|
+
console.log(`Usage:
|
|
15
|
+
ai-ide-config init [dir] [--stack <name>] [--force] [--dry-run] [--skip-skills]
|
|
16
|
+
ai-ide-config init-<stack> [dir] [--force] [--dry-run] [--skip-skills]
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--stack <name> Skip interactive prompt (e.g. angular)
|
|
20
|
+
--force Overwrite existing files
|
|
21
|
+
--dry-run Show what would be written
|
|
22
|
+
--skip-skills Do not run: ${SKILLS_ADD_COMMAND}
|
|
23
|
+
|
|
24
|
+
Stacks:
|
|
25
|
+
${listStacks()
|
|
26
|
+
.map((s) => ` ${s.id.padEnd(12)} ${s.label}`)
|
|
27
|
+
.join('\n')}
|
|
28
|
+
|
|
29
|
+
Shortcuts:
|
|
30
|
+
${stacks}
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string[]} argv
|
|
36
|
+
*/
|
|
37
|
+
function parseArgs(argv) {
|
|
38
|
+
const args = [...argv];
|
|
39
|
+
/** @type {{ command: string | null, targetDir: string, stackId: string | null, force: boolean, dryRun: boolean, skipSkills: boolean, help: boolean }} */
|
|
40
|
+
const result = {
|
|
41
|
+
command: null,
|
|
42
|
+
targetDir: process.cwd(),
|
|
43
|
+
stackId: null,
|
|
44
|
+
force: false,
|
|
45
|
+
dryRun: false,
|
|
46
|
+
skipSkills: false,
|
|
47
|
+
help: false,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const positionals = [];
|
|
51
|
+
|
|
52
|
+
while (args.length > 0) {
|
|
53
|
+
const token = args.shift();
|
|
54
|
+
|
|
55
|
+
if (token === '--help' || token === '-h') {
|
|
56
|
+
result.help = true;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (token === '--force') {
|
|
60
|
+
result.force = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (token === '--dry-run') {
|
|
64
|
+
result.dryRun = true;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (token === '--skip-skills') {
|
|
68
|
+
result.skipSkills = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (token === '--stack') {
|
|
72
|
+
const value = args.shift();
|
|
73
|
+
if (!value) {
|
|
74
|
+
throw new Error('--stack requires a value');
|
|
75
|
+
}
|
|
76
|
+
result.stackId = value;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (token.startsWith('-')) {
|
|
80
|
+
throw new Error(`Unknown option: ${token}`);
|
|
81
|
+
}
|
|
82
|
+
positionals.push(token);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
result.command = positionals[0] ?? null;
|
|
86
|
+
if (positionals[1]) {
|
|
87
|
+
result.targetDir = path.resolve(positionals[1]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Resolve stack id from command name like init-angular.
|
|
95
|
+
* @param {string} command
|
|
96
|
+
* @returns {string | null}
|
|
97
|
+
*/
|
|
98
|
+
function stackFromInitAlias(command) {
|
|
99
|
+
const match = /^init-(.+)$/.exec(command);
|
|
100
|
+
if (!match) return null;
|
|
101
|
+
return match[1];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function main() {
|
|
105
|
+
let parsed;
|
|
106
|
+
try {
|
|
107
|
+
parsed = parseArgs(process.argv.slice(2));
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error(err.message);
|
|
110
|
+
printUsage();
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (parsed.help || !parsed.command) {
|
|
116
|
+
printUsage();
|
|
117
|
+
process.exitCode = parsed.help ? 0 : 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let stackId = parsed.stackId;
|
|
122
|
+
|
|
123
|
+
if (parsed.command === 'init') {
|
|
124
|
+
// stackId may already be set via --stack
|
|
125
|
+
} else {
|
|
126
|
+
const aliasStack = stackFromInitAlias(parsed.command);
|
|
127
|
+
if (aliasStack) {
|
|
128
|
+
if (stackId && stackId !== aliasStack) {
|
|
129
|
+
console.error(
|
|
130
|
+
`Conflicting stacks: command is init-${aliasStack} but --stack ${stackId} was passed.`,
|
|
131
|
+
);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
stackId = aliasStack;
|
|
136
|
+
} else {
|
|
137
|
+
console.error(`Unknown command: ${parsed.command}`);
|
|
138
|
+
printUsage();
|
|
139
|
+
process.exitCode = 1;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!stackId) {
|
|
145
|
+
try {
|
|
146
|
+
stackId = await promptForStack();
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error(err.message);
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const stack = getStack(stackId);
|
|
155
|
+
if (!stack) {
|
|
156
|
+
console.error(
|
|
157
|
+
`Unknown stack "${stackId}". Available: ${stackIds().join(', ')}`,
|
|
158
|
+
);
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const report = initStack({
|
|
165
|
+
stack,
|
|
166
|
+
targetDir: parsed.targetDir,
|
|
167
|
+
force: parsed.force,
|
|
168
|
+
dryRun: parsed.dryRun,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
console.log(
|
|
172
|
+
`${parsed.dryRun ? '[dry-run] ' : ''}Scaffolding ${stack.label} AI IDE config into ${parsed.targetDir}`,
|
|
173
|
+
);
|
|
174
|
+
printReport(report, { dryRun: parsed.dryRun });
|
|
175
|
+
|
|
176
|
+
runSkillsAdd(parsed.targetDir, {
|
|
177
|
+
dryRun: parsed.dryRun,
|
|
178
|
+
skip: parsed.skipSkills,
|
|
179
|
+
});
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error(err.message);
|
|
182
|
+
process.exitCode = 1;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
main();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-ide-config",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Scaffold shared Cursor IDE / agent config (rules, commands, stack AGENTS.md)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"ai-ide-config": "
|
|
7
|
+
"ai-ide-config": "bin/cli.mjs"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"bin",
|
|
@@ -31,4 +31,4 @@
|
|
|
31
31
|
"angular"
|
|
32
32
|
],
|
|
33
33
|
"license": "MIT"
|
|
34
|
-
}
|
|
34
|
+
}
|