@acmeacmeio/setup-sh 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -0
- package/package.json +39 -0
- package/src/cli.mjs +614 -0
- package/templates/.claude/agents/code-simplifier.md +52 -0
- package/templates/.claude/commands/auto.md +85 -0
- package/templates/.claude/commands/clean-copy.md +93 -0
- package/templates/.claude/commands/fix-issue.md +34 -0
- package/templates/.claude/commands/review.md +46 -0
- package/templates/.claude/router/classify.js +241 -0
- package/templates/.claude/router/registry.json +49 -0
- package/templates/.claude/settings.json +113 -0
- package/templates/.claude/skills/api-design/SKILL.md +77 -0
- package/templates/.claude/skills/security-review/SKILL.md +65 -0
- package/templates/.codex/config.toml +31 -0
- package/templates/.codex/skills/api-design/SKILL.md +77 -0
- package/templates/.codex/skills/security-review/SKILL.md +65 -0
- package/templates/.cursor/commands/auto.md +55 -0
- package/templates/.cursor/commands/clean-copy.md +80 -0
- package/templates/.cursor/commands/code-simplifier.md +28 -0
- package/templates/.cursor/commands/fix-issue.md +28 -0
- package/templates/.cursor/commands/review.md +41 -0
- package/templates/.cursor/hooks.json +11 -0
- package/templates/.cursor/rules/api-design.mdc +80 -0
- package/templates/.cursor/rules/git-workflow.mdc +73 -0
- package/templates/.cursor/rules/security.mdc +69 -0
- package/templates/.cursor/rules/tdd.mdc +35 -0
- package/templates/.cursor/rules/typescript.mdc +82 -0
- package/templates/.cursorignore.template +20 -0
- package/templates/.gitignore.template +10 -0
- package/templates/.mcp.json +16 -0
- package/templates/AGENTS.md +118 -0
- package/templates/CLAUDE.md +138 -0
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync, spawn } from "node:child_process";
|
|
4
|
+
import {
|
|
5
|
+
existsSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
cpSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { join, dirname, resolve, isAbsolute } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { createInterface } from "node:readline";
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
const TEMPLATES_DIR = join(__dirname, "..", "templates");
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// ANSI Colors (no dependencies)
|
|
21
|
+
// ============================================================================
|
|
22
|
+
const colors = {
|
|
23
|
+
reset: "\x1b[0m",
|
|
24
|
+
bold: "\x1b[1m",
|
|
25
|
+
dim: "\x1b[2m",
|
|
26
|
+
red: "\x1b[31m",
|
|
27
|
+
green: "\x1b[32m",
|
|
28
|
+
yellow: "\x1b[33m",
|
|
29
|
+
blue: "\x1b[34m",
|
|
30
|
+
magenta: "\x1b[35m",
|
|
31
|
+
cyan: "\x1b[36m",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const log = {
|
|
35
|
+
info: (msg) => console.log(`${colors.blue}info${colors.reset} ${msg}`),
|
|
36
|
+
success: (msg) => console.log(`${colors.green}done${colors.reset} ${msg}`),
|
|
37
|
+
warn: (msg) => console.log(`${colors.yellow}warn${colors.reset} ${msg}`),
|
|
38
|
+
error: (msg) => console.log(`${colors.red}error${colors.reset} ${msg}`),
|
|
39
|
+
step: (msg) => console.log(`${colors.cyan} -> ${colors.reset}${msg}`),
|
|
40
|
+
title: (msg) =>
|
|
41
|
+
console.log(`\n${colors.bold}${colors.magenta}${msg}${colors.reset}\n`),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Platform Detection
|
|
46
|
+
// ============================================================================
|
|
47
|
+
function detectPlatform() {
|
|
48
|
+
const platform = process.platform;
|
|
49
|
+
|
|
50
|
+
if (platform === "darwin") {
|
|
51
|
+
return { os: "macos", pm: "brew" };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (platform === "linux") {
|
|
55
|
+
try {
|
|
56
|
+
const osRelease = readFileSync("/etc/os-release", "utf8");
|
|
57
|
+
if (osRelease.includes("Ubuntu") || osRelease.includes("Debian")) {
|
|
58
|
+
return { os: "debian", pm: "apt" };
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
osRelease.includes("Fedora") ||
|
|
62
|
+
osRelease.includes("RHEL") ||
|
|
63
|
+
osRelease.includes("CentOS")
|
|
64
|
+
) {
|
|
65
|
+
return { os: "fedora", pm: "dnf" };
|
|
66
|
+
}
|
|
67
|
+
if (osRelease.includes("Arch")) {
|
|
68
|
+
return { os: "arch", pm: "pacman" };
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// ignore
|
|
72
|
+
}
|
|
73
|
+
return { os: "linux", pm: "unknown" };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (platform === "win32") {
|
|
77
|
+
return { os: "windows", pm: "winget" };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { os: "unknown", pm: "unknown" };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Command Execution Helpers
|
|
85
|
+
// ============================================================================
|
|
86
|
+
function commandExists(cmd) {
|
|
87
|
+
try {
|
|
88
|
+
execSync(`command -v ${cmd}`, { stdio: "ignore" });
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function exec(cmd, options = {}) {
|
|
96
|
+
try {
|
|
97
|
+
return execSync(cmd, {
|
|
98
|
+
encoding: "utf8",
|
|
99
|
+
stdio: options.silent ? "pipe" : "inherit",
|
|
100
|
+
...options,
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (!options.ignoreError) {
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// Interactive Prompts
|
|
112
|
+
// ============================================================================
|
|
113
|
+
function createPrompt(autoYes = false) {
|
|
114
|
+
const rl = createInterface({
|
|
115
|
+
input: process.stdin,
|
|
116
|
+
output: process.stdout,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
ask: (question) =>
|
|
121
|
+
new Promise((resolve) => {
|
|
122
|
+
rl.question(question, (answer) => resolve(answer));
|
|
123
|
+
}),
|
|
124
|
+
confirm: async (question, defaultYes = true) => {
|
|
125
|
+
if (autoYes) {
|
|
126
|
+
console.log(`${question} [Y/n] y (auto)`);
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
130
|
+
const answer = await new Promise((resolve) => {
|
|
131
|
+
rl.question(`${question} ${suffix} `, resolve);
|
|
132
|
+
});
|
|
133
|
+
if (answer === "") return defaultYes;
|
|
134
|
+
return answer.toLowerCase() === "y";
|
|
135
|
+
},
|
|
136
|
+
select: async (question, options) => {
|
|
137
|
+
console.log(`\n${question}`);
|
|
138
|
+
options.forEach((opt, i) => {
|
|
139
|
+
console.log(
|
|
140
|
+
` ${colors.cyan}${i + 1}${colors.reset}) ${opt.label}${opt.default ? ` ${colors.dim}(default)${colors.reset}` : ""}`,
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
const answer = await new Promise((resolve) => {
|
|
144
|
+
rl.question(`\nSelect [1-${options.length}]: `, resolve);
|
|
145
|
+
});
|
|
146
|
+
const idx = parseInt(answer, 10) - 1;
|
|
147
|
+
if (isNaN(idx) || idx < 0 || idx >= options.length) {
|
|
148
|
+
const defaultOpt = options.find((o) => o.default);
|
|
149
|
+
return defaultOpt ? defaultOpt.value : options[0].value;
|
|
150
|
+
}
|
|
151
|
+
return options[idx].value;
|
|
152
|
+
},
|
|
153
|
+
close: () => rl.close(),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Tool Installation
|
|
159
|
+
// ============================================================================
|
|
160
|
+
const TOOLS = {
|
|
161
|
+
gh: {
|
|
162
|
+
name: "GitHub CLI",
|
|
163
|
+
check: () => commandExists("gh"),
|
|
164
|
+
install: {
|
|
165
|
+
macos: "brew install gh",
|
|
166
|
+
debian: "sudo apt install -y gh",
|
|
167
|
+
fedora: "sudo dnf install -y gh",
|
|
168
|
+
arch: "sudo pacman -S --noconfirm github-cli",
|
|
169
|
+
windows: "winget install GitHub.cli",
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
node: {
|
|
173
|
+
name: "Node.js",
|
|
174
|
+
check: () => commandExists("node"),
|
|
175
|
+
install: {
|
|
176
|
+
macos: "brew install node",
|
|
177
|
+
debian:
|
|
178
|
+
"curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - && sudo apt install -y nodejs",
|
|
179
|
+
fedora: "sudo dnf install -y nodejs",
|
|
180
|
+
arch: "sudo pacman -S --noconfirm nodejs npm",
|
|
181
|
+
windows: "winget install OpenJS.NodeJS.LTS",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
pnpm: {
|
|
185
|
+
name: "pnpm",
|
|
186
|
+
check: () => commandExists("pnpm"),
|
|
187
|
+
install: {
|
|
188
|
+
macos: "brew install pnpm",
|
|
189
|
+
debian: "npm install -g pnpm",
|
|
190
|
+
fedora: "npm install -g pnpm",
|
|
191
|
+
arch: "npm install -g pnpm",
|
|
192
|
+
windows: "npm install -g pnpm",
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
python: {
|
|
196
|
+
name: "Python 3.11+",
|
|
197
|
+
check: () => commandExists("python3.11") || commandExists("python3"),
|
|
198
|
+
install: {
|
|
199
|
+
macos: "brew install python@3.11",
|
|
200
|
+
debian: "sudo apt install -y python3.11 python3.11-venv",
|
|
201
|
+
fedora: "sudo dnf install -y python3.11",
|
|
202
|
+
arch: "sudo pacman -S --noconfirm python",
|
|
203
|
+
windows: "winget install Python.Python.3.11",
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
uv: {
|
|
207
|
+
name: "uv (Python package manager)",
|
|
208
|
+
check: () => commandExists("uv"),
|
|
209
|
+
install: {
|
|
210
|
+
macos: "curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
211
|
+
debian: "curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
212
|
+
fedora: "curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
213
|
+
arch: "curl -LsSf https://astral.sh/uv/install.sh | sh",
|
|
214
|
+
windows: 'powershell -c "irm https://astral.sh/uv/install.ps1 | iex"',
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
"browser-use": {
|
|
218
|
+
name: "browser-use (Browser automation)",
|
|
219
|
+
check: () => commandExists("browser-use"),
|
|
220
|
+
install: {
|
|
221
|
+
macos: "uv tool install browser-use && uvx browser-use install",
|
|
222
|
+
debian: "uv tool install browser-use && uvx browser-use install",
|
|
223
|
+
fedora: "uv tool install browser-use && uvx browser-use install",
|
|
224
|
+
arch: "uv tool install browser-use && uvx browser-use install",
|
|
225
|
+
windows: "uv tool install browser-use && uvx browser-use install",
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
async function installTools(platform, prompt) {
|
|
231
|
+
log.title("Checking Required Tools");
|
|
232
|
+
|
|
233
|
+
const missing = [];
|
|
234
|
+
|
|
235
|
+
for (const [key, tool] of Object.entries(TOOLS)) {
|
|
236
|
+
if (tool.check()) {
|
|
237
|
+
log.success(`${tool.name} is installed`);
|
|
238
|
+
} else {
|
|
239
|
+
log.warn(`${tool.name} is not installed`);
|
|
240
|
+
missing.push(key);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (missing.length === 0) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const install = await prompt.confirm(
|
|
249
|
+
`\nInstall missing tools (${missing.join(", ")})?`,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (!install) {
|
|
253
|
+
log.warn("Skipping tool installation. Some features may not work.");
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for (const key of missing) {
|
|
258
|
+
const tool = TOOLS[key];
|
|
259
|
+
const cmd = tool.install[platform.os];
|
|
260
|
+
|
|
261
|
+
if (!cmd) {
|
|
262
|
+
log.error(`No install command for ${tool.name} on ${platform.os}`);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
log.step(`Installing ${tool.name}...`);
|
|
267
|
+
try {
|
|
268
|
+
exec(cmd);
|
|
269
|
+
log.success(`${tool.name} installed`);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
log.error(`Failed to install ${tool.name}: ${error.message}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// Skills Installation
|
|
280
|
+
// ============================================================================
|
|
281
|
+
const SKILL_SOURCES = [
|
|
282
|
+
// Official Anthropic skills
|
|
283
|
+
{ repo: "anthropics/anthropic-skills", skills: ["frontend-design"] },
|
|
284
|
+
// Browser automation
|
|
285
|
+
{ repo: "browser-use/browser-use", skills: ["browser-use"] },
|
|
286
|
+
// TDD workflow
|
|
287
|
+
{ repo: "obra/superpowers", skills: ["test-driven-development"] },
|
|
288
|
+
// Supabase Postgres
|
|
289
|
+
{ repo: "supabase/agent-skills", skills: ["supabase-postgres-best-practices"] },
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
async function installSkills(prompt, agentChoice) {
|
|
293
|
+
// Skills installation only applies to Claude Code
|
|
294
|
+
// Codex uses the bundled skills from .codex/skills/
|
|
295
|
+
// Cursor uses MDC rules bundled in .cursor/rules/
|
|
296
|
+
if (agentChoice === "codex") {
|
|
297
|
+
log.info("Codex skills are bundled in .codex/skills/ directory");
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (agentChoice === "cursor") {
|
|
302
|
+
log.info("Cursor rules are bundled in .cursor/rules/ directory");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
log.title("Installing Claude Code Skills");
|
|
307
|
+
|
|
308
|
+
const install = await prompt.confirm("Install recommended external skills?");
|
|
309
|
+
|
|
310
|
+
if (!install) {
|
|
311
|
+
log.warn("Skipping skill installation. Bundled skills in .claude/skills/ are ready to use.");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
for (const source of SKILL_SOURCES) {
|
|
316
|
+
for (const skill of source.skills) {
|
|
317
|
+
log.step(`Installing ${skill} from ${source.repo}...`);
|
|
318
|
+
try {
|
|
319
|
+
exec(`npx add-skill ${source.repo} ${skill}`, {
|
|
320
|
+
silent: true,
|
|
321
|
+
ignoreError: true,
|
|
322
|
+
});
|
|
323
|
+
log.success(`Installed ${skill}`);
|
|
324
|
+
} catch {
|
|
325
|
+
log.warn(`Could not install ${skill} (may require manual setup)`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ============================================================================
|
|
332
|
+
// Template Files
|
|
333
|
+
// ============================================================================
|
|
334
|
+
function copyTemplates(targetDir, agentChoice) {
|
|
335
|
+
log.title("Setting Up Workspace");
|
|
336
|
+
|
|
337
|
+
const shouldCopyClaude = agentChoice === "claude" || agentChoice === "all";
|
|
338
|
+
const shouldCopyCodex = agentChoice === "codex" || agentChoice === "all";
|
|
339
|
+
const shouldCopyCursor = agentChoice === "cursor" || agentChoice === "all";
|
|
340
|
+
|
|
341
|
+
// Copy Claude Code templates
|
|
342
|
+
if (shouldCopyClaude) {
|
|
343
|
+
const claudeDir = join(targetDir, ".claude");
|
|
344
|
+
if (!existsSync(claudeDir)) {
|
|
345
|
+
mkdirSync(claudeDir, { recursive: true });
|
|
346
|
+
}
|
|
347
|
+
cpSync(join(TEMPLATES_DIR, ".claude"), claudeDir, { recursive: true });
|
|
348
|
+
log.step("Created .claude/ directory with settings, commands, skills, and agents");
|
|
349
|
+
|
|
350
|
+
// Copy CLAUDE.md
|
|
351
|
+
cpSync(
|
|
352
|
+
join(TEMPLATES_DIR, "CLAUDE.md"),
|
|
353
|
+
join(targetDir, "CLAUDE.md"),
|
|
354
|
+
);
|
|
355
|
+
log.step("Created CLAUDE.md");
|
|
356
|
+
|
|
357
|
+
// Copy .mcp.json (Claude Code MCP config)
|
|
358
|
+
cpSync(
|
|
359
|
+
join(TEMPLATES_DIR, ".mcp.json"),
|
|
360
|
+
join(targetDir, ".mcp.json"),
|
|
361
|
+
);
|
|
362
|
+
log.step("Created .mcp.json");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Copy Codex templates
|
|
366
|
+
if (shouldCopyCodex) {
|
|
367
|
+
const codexDir = join(targetDir, ".codex");
|
|
368
|
+
if (!existsSync(codexDir)) {
|
|
369
|
+
mkdirSync(codexDir, { recursive: true });
|
|
370
|
+
}
|
|
371
|
+
cpSync(join(TEMPLATES_DIR, ".codex"), codexDir, { recursive: true });
|
|
372
|
+
log.step("Created .codex/ directory with config and skills");
|
|
373
|
+
|
|
374
|
+
// Copy AGENTS.md
|
|
375
|
+
cpSync(
|
|
376
|
+
join(TEMPLATES_DIR, "AGENTS.md"),
|
|
377
|
+
join(targetDir, "AGENTS.md"),
|
|
378
|
+
);
|
|
379
|
+
log.step("Created AGENTS.md");
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Copy Cursor templates
|
|
383
|
+
if (shouldCopyCursor) {
|
|
384
|
+
const cursorDir = join(targetDir, ".cursor");
|
|
385
|
+
if (!existsSync(cursorDir)) {
|
|
386
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
387
|
+
}
|
|
388
|
+
cpSync(join(TEMPLATES_DIR, ".cursor"), cursorDir, { recursive: true });
|
|
389
|
+
log.step("Created .cursor/ directory with rules, commands, and hooks");
|
|
390
|
+
|
|
391
|
+
// Copy .cursorignore
|
|
392
|
+
if (existsSync(join(TEMPLATES_DIR, ".cursorignore.template"))) {
|
|
393
|
+
cpSync(
|
|
394
|
+
join(TEMPLATES_DIR, ".cursorignore.template"),
|
|
395
|
+
join(targetDir, ".cursorignore"),
|
|
396
|
+
);
|
|
397
|
+
log.step("Created .cursorignore");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Cursor reads CLAUDE.md natively - copy if not already present
|
|
401
|
+
const claudeMdPath = join(targetDir, "CLAUDE.md");
|
|
402
|
+
if (!existsSync(claudeMdPath) && !shouldCopyClaude) {
|
|
403
|
+
cpSync(
|
|
404
|
+
join(TEMPLATES_DIR, "CLAUDE.md"),
|
|
405
|
+
claudeMdPath,
|
|
406
|
+
);
|
|
407
|
+
log.step("Created CLAUDE.md (Cursor reads this natively)");
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Copy AGENTS.md if not already present (Cursor reads this too)
|
|
411
|
+
const agentsMdPath = join(targetDir, "AGENTS.md");
|
|
412
|
+
if (!existsSync(agentsMdPath) && !shouldCopyCodex) {
|
|
413
|
+
cpSync(
|
|
414
|
+
join(TEMPLATES_DIR, "AGENTS.md"),
|
|
415
|
+
agentsMdPath,
|
|
416
|
+
);
|
|
417
|
+
log.step("Created AGENTS.md (Cursor reads this natively)");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Copy shared templates (.gitignore)
|
|
422
|
+
if (existsSync(join(TEMPLATES_DIR, ".gitignore.template"))) {
|
|
423
|
+
const gitignorePath = join(targetDir, ".gitignore");
|
|
424
|
+
const gitignoreContent = existsSync(gitignorePath)
|
|
425
|
+
? readFileSync(gitignorePath, "utf8")
|
|
426
|
+
: "";
|
|
427
|
+
|
|
428
|
+
const templateContent = readFileSync(
|
|
429
|
+
join(TEMPLATES_DIR, ".gitignore.template"),
|
|
430
|
+
"utf8",
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
// Add new entries that don't already exist
|
|
434
|
+
const additions = templateContent
|
|
435
|
+
.split("\n")
|
|
436
|
+
.filter((line) => line.trim() && !gitignoreContent.includes(line.trim()));
|
|
437
|
+
|
|
438
|
+
if (additions.length > 0) {
|
|
439
|
+
writeFileSync(
|
|
440
|
+
gitignorePath,
|
|
441
|
+
gitignoreContent + (gitignoreContent.endsWith("\n") ? "" : "\n") + additions.join("\n") + "\n",
|
|
442
|
+
);
|
|
443
|
+
log.step("Updated .gitignore");
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
log.success("Workspace templates copied");
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ============================================================================
|
|
451
|
+
// Main CLI
|
|
452
|
+
// ============================================================================
|
|
453
|
+
function parseArgs(argv) {
|
|
454
|
+
const args = { flags: {}, positional: [] };
|
|
455
|
+
for (const arg of argv) {
|
|
456
|
+
if (arg === "--yes" || arg === "-y") {
|
|
457
|
+
args.flags.yes = true;
|
|
458
|
+
} else if (arg.startsWith("--agent=")) {
|
|
459
|
+
const agent = arg.split("=")[1];
|
|
460
|
+
if (["claude", "codex", "cursor", "all"].includes(agent)) {
|
|
461
|
+
args.flags.agent = agent;
|
|
462
|
+
} else {
|
|
463
|
+
log.warn(`Unknown agent "${agent}". Valid options: claude, codex, cursor, all`);
|
|
464
|
+
}
|
|
465
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
466
|
+
args.flags.help = true;
|
|
467
|
+
} else if (arg.startsWith("-")) {
|
|
468
|
+
// ignore unknown flags
|
|
469
|
+
} else {
|
|
470
|
+
args.positional.push(arg);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return args;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function showHelp() {
|
|
477
|
+
console.log(`
|
|
478
|
+
${colors.bold}Usage:${colors.reset}
|
|
479
|
+
npx @acmeacmeio/setup-sh [directory] [options]
|
|
480
|
+
|
|
481
|
+
${colors.bold}Options:${colors.reset}
|
|
482
|
+
--agent=<agent> Select AI coding agent (claude, codex, cursor, all)
|
|
483
|
+
-y, --yes Auto-confirm all prompts
|
|
484
|
+
-h, --help Show this help message
|
|
485
|
+
|
|
486
|
+
${colors.bold}Examples:${colors.reset}
|
|
487
|
+
npx @acmeacmeio/setup-sh # Current directory, interactive
|
|
488
|
+
npx @acmeacmeio/setup-sh my-project # New directory, interactive
|
|
489
|
+
npx @acmeacmeio/setup-sh . --agent=claude # Claude Code only
|
|
490
|
+
npx @acmeacmeio/setup-sh . --agent=codex # Codex only
|
|
491
|
+
npx @acmeacmeio/setup-sh . --agent=cursor # Cursor IDE only
|
|
492
|
+
npx @acmeacmeio/setup-sh . --agent=all # All agents
|
|
493
|
+
`);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function main() {
|
|
497
|
+
const { flags, positional } = parseArgs(process.argv.slice(2));
|
|
498
|
+
|
|
499
|
+
if (flags.help) {
|
|
500
|
+
showHelp();
|
|
501
|
+
process.exit(0);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const autoYes = flags.yes;
|
|
505
|
+
const targetDir = positional[0]
|
|
506
|
+
? isAbsolute(positional[0])
|
|
507
|
+
? positional[0]
|
|
508
|
+
: resolve(process.cwd(), positional[0])
|
|
509
|
+
: process.cwd();
|
|
510
|
+
|
|
511
|
+
console.log(`
|
|
512
|
+
${colors.bold}${colors.magenta}
|
|
513
|
+
███████╗███████╗████████╗██╗ ██╗██████╗ ███████╗██╗ ██╗
|
|
514
|
+
██╔════╝██╔════╝╚══██╔══╝██║ ██║██╔══██╗ ██╔════╝██║ ██║
|
|
515
|
+
███████╗█████╗ ██║ ██║ ██║██████╔╝ █████╗███████╗███████║
|
|
516
|
+
╚════██║██╔══╝ ██║ ██║ ██║██╔═══╝ ╚════╝╚════██║██╔══██║
|
|
517
|
+
███████║███████╗ ██║ ╚██████╔╝██║ ███████║██║ ██║
|
|
518
|
+
╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
|
|
519
|
+
${colors.reset}
|
|
520
|
+
${colors.dim}Multi-Agent Workspace Initializer${colors.reset}
|
|
521
|
+
`);
|
|
522
|
+
|
|
523
|
+
// Ensure target directory exists
|
|
524
|
+
if (!existsSync(targetDir)) {
|
|
525
|
+
mkdirSync(targetDir, { recursive: true });
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const platform = detectPlatform();
|
|
529
|
+
log.info(`Detected platform: ${platform.os} (${platform.pm})`);
|
|
530
|
+
log.info(`Target directory: ${targetDir}`);
|
|
531
|
+
|
|
532
|
+
const prompt = createPrompt(autoYes);
|
|
533
|
+
|
|
534
|
+
try {
|
|
535
|
+
// Step 0: Select AI coding agent
|
|
536
|
+
let agentChoice = flags.agent;
|
|
537
|
+
if (!agentChoice) {
|
|
538
|
+
agentChoice = await prompt.select(
|
|
539
|
+
"Which AI coding agent are you using?",
|
|
540
|
+
[
|
|
541
|
+
{ value: "claude", label: "Claude Code (Anthropic)", default: true },
|
|
542
|
+
{ value: "codex", label: "Codex (OpenAI)" },
|
|
543
|
+
{ value: "cursor", label: "Cursor IDE" },
|
|
544
|
+
{ value: "all", label: "All agents (generate all configs)" },
|
|
545
|
+
],
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
log.info(`Selected agent: ${agentChoice}`);
|
|
549
|
+
|
|
550
|
+
// Step 1: Install system tools
|
|
551
|
+
await installTools(platform, prompt);
|
|
552
|
+
|
|
553
|
+
// Step 2: Copy/create workspace files based on agent selection
|
|
554
|
+
copyTemplates(targetDir, agentChoice);
|
|
555
|
+
|
|
556
|
+
// Step 3: Install external skills (Claude Code only)
|
|
557
|
+
await installSkills(prompt, agentChoice);
|
|
558
|
+
|
|
559
|
+
// Done! Show appropriate next steps based on agent choice
|
|
560
|
+
const nextSteps = getNextSteps(agentChoice);
|
|
561
|
+
console.log(`
|
|
562
|
+
${colors.green}${colors.bold}
|
|
563
|
+
Workspace is ready!${colors.reset}
|
|
564
|
+
|
|
565
|
+
${colors.cyan}Next steps:${colors.reset}
|
|
566
|
+
${nextSteps}
|
|
567
|
+
|
|
568
|
+
${colors.dim}Tips:${colors.reset}
|
|
569
|
+
${getTips(agentChoice)}
|
|
570
|
+
`);
|
|
571
|
+
} catch (error) {
|
|
572
|
+
log.error(`Setup failed: ${error.message}`);
|
|
573
|
+
process.exit(1);
|
|
574
|
+
} finally {
|
|
575
|
+
prompt.close();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function getNextSteps(agentChoice) {
|
|
580
|
+
const claudeSteps = ` - Review and customize ${colors.bold}CLAUDE.md${colors.reset} for your project
|
|
581
|
+
- Run ${colors.bold}claude${colors.reset} to start coding with AI assistance`;
|
|
582
|
+
|
|
583
|
+
const codexSteps = ` - Review and customize ${colors.bold}AGENTS.md${colors.reset} for your project
|
|
584
|
+
- Run ${colors.bold}codex${colors.reset} to start coding with AI assistance`;
|
|
585
|
+
|
|
586
|
+
const cursorSteps = ` - Review and customize ${colors.bold}CLAUDE.md${colors.reset} for your project (Cursor reads this)
|
|
587
|
+
- Add custom rules in ${colors.bold}.cursor/rules/${colors.reset}
|
|
588
|
+
- Open the project in Cursor IDE`;
|
|
589
|
+
|
|
590
|
+
if (agentChoice === "claude") return claudeSteps;
|
|
591
|
+
if (agentChoice === "codex") return codexSteps;
|
|
592
|
+
if (agentChoice === "cursor") return cursorSteps;
|
|
593
|
+
return claudeSteps + "\n" + codexSteps + "\n" + cursorSteps;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function getTips(agentChoice) {
|
|
597
|
+
const claudeTips = ` - Create ${colors.bold}CLAUDE.local.md${colors.reset} for personal preferences (gitignored)
|
|
598
|
+
- Edit ${colors.bold}.claude/settings.local.json${colors.reset} for local overrides`;
|
|
599
|
+
|
|
600
|
+
const codexTips = ` - Create ${colors.bold}AGENTS.local.md${colors.reset} for personal preferences (gitignored)
|
|
601
|
+
- Edit ${colors.bold}.codex/config.local.toml${colors.reset} for local overrides`;
|
|
602
|
+
|
|
603
|
+
const cursorTips = ` - Add ${colors.bold}alwaysApply: true${colors.reset} to rules that should always be active
|
|
604
|
+
- Use ${colors.bold}globs${colors.reset} in rules for file-specific context`;
|
|
605
|
+
|
|
606
|
+
const sharedTips = ` - Run ${colors.bold}gh auth login${colors.reset} if GitHub CLI needs authentication`;
|
|
607
|
+
|
|
608
|
+
if (agentChoice === "claude") return claudeTips + "\n" + sharedTips;
|
|
609
|
+
if (agentChoice === "codex") return codexTips + "\n" + sharedTips;
|
|
610
|
+
if (agentChoice === "cursor") return cursorTips + "\n" + sharedTips;
|
|
611
|
+
return claudeTips + "\n" + codexTips + "\n" + cursorTips + "\n" + sharedTips;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
main();
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-simplifier
|
|
3
|
+
description: Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Focuses on recently modified code unless instructed otherwise.
|
|
4
|
+
model: opus
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Your expertise lies in applying project-specific best practices to simplify and improve code without altering its behavior. You prioritize readable, explicit code over overly compact solutions. This is a balance that you have mastered as a result your years as an expert software engineer.
|
|
8
|
+
|
|
9
|
+
You will analyze recently modified code and apply refinements that:
|
|
10
|
+
|
|
11
|
+
1. **Preserve Functionality**: Never change what the code does - only how it does it. All original features, outputs, and behaviors must remain intact.
|
|
12
|
+
|
|
13
|
+
2. **Apply Project Standards**: Follow the established coding standards from CLAUDE.md including:
|
|
14
|
+
|
|
15
|
+
- Use ES modules with proper import sorting and extensions
|
|
16
|
+
- Prefer `function` keyword over arrow functions
|
|
17
|
+
- Use explicit return type annotations for top-level functions
|
|
18
|
+
- Follow proper React component patterns with explicit Props types
|
|
19
|
+
- Use proper error handling patterns (avoid try/catch when possible)
|
|
20
|
+
- Maintain consistent naming conventions
|
|
21
|
+
|
|
22
|
+
3. **Enhance Clarity**: Simplify code structure by:
|
|
23
|
+
|
|
24
|
+
- Reducing unnecessary complexity and nesting
|
|
25
|
+
- Eliminating redundant code and abstractions
|
|
26
|
+
- Improving readability through clear variable and function names
|
|
27
|
+
- Consolidating related logic
|
|
28
|
+
- Removing unnecessary comments that describe obvious code
|
|
29
|
+
- IMPORTANT: Avoid nested ternary operators - prefer switch statements or if/else chains for multiple conditions
|
|
30
|
+
- Choose clarity over brevity - explicit code is often better than overly compact code
|
|
31
|
+
|
|
32
|
+
4. **Maintain Balance**: Avoid over-simplification that could:
|
|
33
|
+
|
|
34
|
+
- Reduce code clarity or maintainability
|
|
35
|
+
- Create overly clever solutions that are hard to understand
|
|
36
|
+
- Combine too many concerns into single functions or components
|
|
37
|
+
- Remove helpful abstractions that improve code organization
|
|
38
|
+
- Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
|
|
39
|
+
- Make the code harder to debug or extend
|
|
40
|
+
|
|
41
|
+
5. **Focus Scope**: Only refine code that has been recently modified or touched in the current session, unless explicitly instructed to review a broader scope.
|
|
42
|
+
|
|
43
|
+
Your refinement process:
|
|
44
|
+
|
|
45
|
+
1. Identify the recently modified code sections
|
|
46
|
+
2. Analyze for opportunities to improve elegance and consistency
|
|
47
|
+
3. Apply project-specific best practices and coding standards
|
|
48
|
+
4. Ensure all functionality remains unchanged
|
|
49
|
+
5. Verify the refined code is simpler and more maintainable
|
|
50
|
+
6. Document only significant changes that affect understanding
|
|
51
|
+
|
|
52
|
+
You operate autonomously and proactively, refining code immediately after it's written or modified without requiring explicit requests. Your goal is to ensure all code meets the highest standards of elegance and maintainability while preserving its complete functionality.
|