acek-skills 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nur Azis Pakaya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,268 @@
1
+ <div align="center">
2
+
3
+ # acek-skills
4
+
5
+ **Installable [Claude Code](https://claude.com/claude-code) skills for Salesforce work.**
6
+ Admin • Development • Testing • DevOps • Security Review • Data Migration • Business Analysis • Ideation
7
+
8
+ [![npm version](https://img.shields.io/npm/v/acek-skills.svg)](https://www.npmjs.com/package/acek-skills)
9
+ [![license](https://img.shields.io/npm/l/acek-skills.svg)](./LICENSE)
10
+ [![node](https://img.shields.io/node/v/acek-skills.svg)](https://nodejs.org)
11
+
12
+ </div>
13
+
14
+ ---
15
+
16
+ ## Contents
17
+
18
+ - [Why this exists](#why-this-exists)
19
+ - [Quick start](#quick-start)
20
+ - [How the wizard works](#how-the-wizard-works)
21
+ - [Non-interactive install](#non-interactive-install)
22
+ - [Org alias templating](#org-alias-templating)
23
+ - [Install targets](#install-targets)
24
+ - [Skills reference](#skills-reference)
25
+ - [CLI reference](#cli-reference)
26
+ - [Project structure](#project-structure)
27
+ - [Adding a new skill](#adding-a-new-skill)
28
+ - [FAQ](#faq)
29
+ - [License](#license)
30
+
31
+ ---
32
+
33
+ ## Why this exists
34
+
35
+ Every Salesforce engagement re-derives the same rules — governor limits, CRUD/FLS checks, deploy
36
+ checklists, test coverage thresholds — usually from memory, tribal knowledge, or a wiki page
37
+ nobody opens. **acek-skills** packages that knowledge as installable [Claude Code
38
+ skills](https://docs.claude.com/en/docs/claude-code/skills): Markdown files with trigger
39
+ conditions that Claude reads automatically and applies only when the task actually calls for them.
40
+
41
+ One package, eight roles, install only what you need, into whichever tool you actually use.
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ npx acek-skills install
47
+ ```
48
+
49
+ That's it — no global install, no cloning. `npx` fetches the package, runs the wizard, and exits.
50
+
51
+ ## How the wizard works
52
+
53
+ Running `install` with no arguments walks you through three prompts:
54
+
55
+ ```
56
+ ? Select the skills to install
57
+ - Space to select, Enter to confirm, a to toggle all
58
+ ◉ sf-admin
59
+ ◉ sf-ba
60
+ ◉ sf-data-migration
61
+ ◉ sf-dev
62
+ ◉ sf-devops
63
+ ◉ sf-ideation
64
+ ◉ sf-security-review
65
+ ◉ sf-testing
66
+
67
+ ? Select the target IDE(s) / tool(s)
68
+ ◉ Claude Code — project (./.claude/skills/user)
69
+ ◯ Claude Code — global (~/.claude/skills/user)
70
+ ◯ Cursor (./.cursor/rules)
71
+ ◯ Windsurf (./.windsurf/rules)
72
+ ◯ GitHub Copilot (./.github/instructions)
73
+
74
+ ? Production org alias (e.g. Acme_Production) — leave blank to fill in later
75
+ ? Sandbox / dev org alias (e.g. Acme_Dev) — leave blank to fill in later
76
+ ```
77
+
78
+ 1. **Skills** — all selected by default; deselect anything you don't need.
79
+ 2. **Targets** — pick one or more. Each selected skill is installed to *every* selected target,
80
+ converted into that tool's native rule format.
81
+ 3. **Org aliases** — only asked if one of the selected skills references them (currently
82
+ `sf-devops` and `sf-data-migration`). Leave blank to fill in later by hand.
83
+
84
+ ## Non-interactive install
85
+
86
+ For scripting, CI, or dotfile setups:
87
+
88
+ ```bash
89
+ # Install everything to Claude Code (project-level), no prompts
90
+ npx acek-skills install --all
91
+
92
+ # Install a single skill
93
+ npx acek-skills install sf-devops
94
+
95
+ # List available skills
96
+ npx acek-skills list
97
+ ```
98
+
99
+ Non-interactive installs always target Claude Code (project-level). For other tools, use the
100
+ interactive wizard.
101
+
102
+ ## Org alias templating
103
+
104
+ `sf-devops` and `sf-data-migration` reference your org setup (production alias, sandbox/dev
105
+ alias) instead of hardcoding one. Internally these are placeholders —
106
+ `{{PROD_ORG_ALIAS}}` / `{{DEV_ORG_ALIAS}}` — resolved at install time so the package never ships
107
+ with a real client or org name baked in.
108
+
109
+ | How you install | What fills the placeholder |
110
+ |---|---|
111
+ | Interactive wizard | Whatever you type at the alias prompts |
112
+ | `install --all` / `install <name>` | `ACEK_PROD_ORG_ALIAS` / `ACEK_DEV_ORG_ALIAS` env vars, if set |
113
+ | Left blank / not set | Literal `<PROD_ORG_ALIAS>` / `<DEV_ORG_ALIAS>` — find-and-replace later |
114
+
115
+ ```bash
116
+ ACEK_PROD_ORG_ALIAS=Acme_Production ACEK_DEV_ORG_ALIAS=Acme_Dev npx acek-skills install --all
117
+ ```
118
+
119
+ The CLI prints a reminder after install if any placeholder was left unresolved.
120
+
121
+ ## Install targets
122
+
123
+ | Target | Destination | Trigger behavior |
124
+ |---|---|---|
125
+ | **Claude Code** (project) | `./.claude/skills/user/<skill>/SKILL.md` | Auto-triggered — Claude reads the `description` frontmatter and invokes the skill only when the task matches |
126
+ | **Claude Code** (global) | `~/.claude/skills/user/<skill>/SKILL.md` | Same as above, applies across every project on your machine |
127
+ | **Cursor** | `./.cursor/rules/<skill>.mdc` | Project rule (`alwaysApply: false`) — Cursor decides relevance from the `description` field |
128
+ | **Windsurf** | `./.windsurf/rules/<skill>.md` | Cascade rule (`trigger: model_decision`) — same idea, Windsurf's own matching |
129
+ | **GitHub Copilot** | `./.github/instructions/<skill>.instructions.md` | Custom instructions (`applyTo: "**"`) — applied repo-wide, no per-task trigger |
130
+
131
+ Only Claude Code has a purpose-built skill system with dynamic, description-based triggering.
132
+ Cursor and Windsurf approximate it with their own rule-matching; Copilot's custom instructions
133
+ apply more broadly since it has no equivalent trigger mechanism. Check each tool's docs if the
134
+ matching behavior matters for your workflow.
135
+
136
+ ## Skills reference
137
+
138
+ <details>
139
+ <summary><strong>sf-admin</strong> — declarative configuration</summary>
140
+
141
+ Custom objects/fields, picklists, page layouts, record types, profiles, permission sets, roles,
142
+ sharing rules, validation rules, workflow rules, flows (Screen/Record-Triggered/Schedule),
143
+ approval processes, reports, dashboards, email templates, org setup, security model (OWD), and
144
+ data management (import/export/data loader) — anything declarative, no code involved.
145
+ </details>
146
+
147
+ <details>
148
+ <summary><strong>sf-ba</strong> — business analysis documentation</summary>
149
+
150
+ Writes PRDs, user stories, feature specs, and functional requirements — structured Markdown docs
151
+ intended to hand off to an admin or developer. Triggers on "write a PRD", "document requirements",
152
+ "write user stories", or when a business problem needs to become executable documentation.
153
+ </details>
154
+
155
+ <details>
156
+ <summary><strong>sf-data-migration</strong> — bulk data operations</summary>
157
+
158
+ Import/export strategy, Data Loader automation, upsert with External IDs, bulk SOQL exports, data
159
+ cleansing, bulk delete/mass update, and error handling for large-volume loads between orgs or from
160
+ external systems.
161
+ </details>
162
+
163
+ <details>
164
+ <summary><strong>sf-dev</strong> — custom development</summary>
165
+
166
+ Apex classes, triggers, batch jobs, test classes, SOQL, LWC (HTML/JS/CSS), Aura components, and
167
+ integration patterns. Covers governor limits, CRUD/FLS patterns, `@AuraEnabled` conventions, LWC
168
+ brand tokens + version badges, and REST/SOAP callout patterns.
169
+ </details>
170
+
171
+ <details>
172
+ <summary><strong>sf-devops</strong> — deployment & release process</summary>
173
+
174
+ Code review checklists, scoped `package.xml` manifests, dry-run validation, Change Requests,
175
+ deploy documentation, and the full sandbox-to-production pipeline via SF CLI. References your
176
+ production/sandbox [org aliases](#org-alias-templating).
177
+ </details>
178
+
179
+ <details>
180
+ <summary><strong>sf-ideation</strong> — enhancement brainstorming</summary>
181
+
182
+ Generates enhancement ideas for existing components/features or net-new concepts — for LWC,
183
+ Apex, flows, or the platform generally. Also used proactively when reviewing a component to spot
184
+ improvement opportunities.
185
+ </details>
186
+
187
+ <details>
188
+ <summary><strong>sf-security-review</strong> — security audits</summary>
189
+
190
+ CRUD/FLS checks, sharing model review, `with sharing` enforcement, SOQL injection, XSS in LWC,
191
+ hardcoded credential detection, and a production sign-off checklist. Also covers Shield,
192
+ encryption, and audit trail questions.
193
+ </details>
194
+
195
+ <details>
196
+ <summary><strong>sf-testing</strong> — Apex test strategy</summary>
197
+
198
+ Test classes, `TestDataFactory` patterns, mocking HTTP callouts and platform events, test
199
+ assertions, coverage reports, and debugging org-only test failures. Enforces the 85% coverage
200
+ floor and `AuraHandledException` assertion conventions.
201
+ </details>
202
+
203
+ Each skill's full content — including code patterns, checklists, and exact trigger keywords —
204
+ lives in its `SKILL.md`.
205
+
206
+ ## CLI reference
207
+
208
+ ```
209
+ acek-skills install Interactive: pick skills, target IDE(s)/tool(s), and org aliases
210
+ acek-skills install --all Install all skills to Claude Code (project), no prompts
211
+ acek-skills install <name> Install a single skill to Claude Code (project), no prompts
212
+ acek-skills list List available skills
213
+ ```
214
+
215
+ ## Project structure
216
+
217
+ ```
218
+ skills/
219
+ sf-admin/SKILL.md
220
+ sf-ba/SKILL.md
221
+ sf-data-migration/SKILL.md
222
+ sf-dev/SKILL.md
223
+ sf-devops/SKILL.md
224
+ sf-ideation/SKILL.md
225
+ sf-security-review/SKILL.md
226
+ sf-testing/SKILL.md
227
+ bin/
228
+ cli.js install wizard / CLI entry point (acek-skills)
229
+ ```
230
+
231
+ ## Adding a new skill
232
+
233
+ 1. Create `skills/<name>/SKILL.md` with YAML frontmatter:
234
+ ```markdown
235
+ ---
236
+ name: <name>
237
+ description: >
238
+ What this skill covers and the exact phrases/keywords that should trigger it.
239
+ ---
240
+
241
+ # Skill content — rules, checklists, code patterns
242
+ ```
243
+ 2. No CLI changes needed — `list` and `install` pick up any folder under `skills/` automatically.
244
+ 3. If the skill needs org-specific values, use the `{{PROD_ORG_ALIAS}}` / `{{DEV_ORG_ALIAS}}`
245
+ placeholders (see [Org alias templating](#org-alias-templating)) rather than hardcoding one.
246
+
247
+ ## FAQ
248
+
249
+ **Does this modify my Salesforce org?**
250
+ No. Every skill is a Markdown instruction file — it shapes how Claude (or Cursor/Windsurf/Copilot)
251
+ responds, and doesn't touch any org, sandbox, or production data on its own.
252
+
253
+ **Can I install only some skills?**
254
+ Yes — deselect any skill in the interactive wizard, or use `install <name>` for a single one.
255
+
256
+ **Can I re-run install to update after a new version is published?**
257
+ Yes, `npx` always resolves the latest version unless you pin one (`npx acek-skills@1.2.0 install`).
258
+ Re-running overwrites previously installed files for the skills/targets you select.
259
+
260
+ **Why do only Claude Code skills auto-trigger?**
261
+ Claude Code has a native skill system that matches tasks against each skill's `description`
262
+ frontmatter. Cursor, Windsurf, and Copilot don't expose the same mechanism, so their versions are
263
+ converted to each tool's closest equivalent (project rules / custom instructions) — see
264
+ [Install targets](#install-targets).
265
+
266
+ ## License
267
+
268
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const os = require('os');
6
+
7
+ const args = process.argv.slice(2);
8
+ const command = args[0];
9
+ const target = args[1]; // optional: specific skill name, or "--all"
10
+
11
+ const SKILLS_SRC = path.join(__dirname, '../skills');
12
+
13
+ // Where each target/tool expects its instruction files, and how to format them.
14
+ const TARGETS = {
15
+ 'claude-project': {
16
+ label: 'Claude Code — project (./.claude/skills/user)',
17
+ dir: (cwd) => path.join(cwd, '.claude/skills/user'),
18
+ format: 'claude',
19
+ selected: true,
20
+ },
21
+ 'claude-global': {
22
+ label: 'Claude Code — global (~/.claude/skills/user)',
23
+ dir: () => path.join(os.homedir(), '.claude/skills/user'),
24
+ format: 'claude',
25
+ selected: false,
26
+ },
27
+ cursor: {
28
+ label: 'Cursor (./.cursor/rules)',
29
+ dir: (cwd) => path.join(cwd, '.cursor/rules'),
30
+ format: 'cursor',
31
+ selected: false,
32
+ },
33
+ windsurf: {
34
+ label: 'Windsurf (./.windsurf/rules)',
35
+ dir: (cwd) => path.join(cwd, '.windsurf/rules'),
36
+ format: 'windsurf',
37
+ selected: false,
38
+ },
39
+ copilot: {
40
+ label: 'GitHub Copilot (./.github/instructions)',
41
+ dir: (cwd) => path.join(cwd, '.github/instructions'),
42
+ format: 'copilot',
43
+ selected: false,
44
+ },
45
+ };
46
+
47
+ // Copies a skill directory, substituting org-alias placeholders in any
48
+ // Markdown file along the way (binary/other files are copied as-is).
49
+ function copySkillDir(src, dest, aliases) {
50
+ fs.mkdirSync(dest, { recursive: true });
51
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
52
+ const srcPath = path.join(src, entry.name);
53
+ const destPath = path.join(dest, entry.name);
54
+ if (entry.isDirectory()) {
55
+ copySkillDir(srcPath, destPath, aliases);
56
+ } else if (entry.name.endsWith('.md')) {
57
+ const content = applyAliases(fs.readFileSync(srcPath, 'utf8'), aliases);
58
+ fs.writeFileSync(destPath, content);
59
+ } else {
60
+ fs.copyFileSync(srcPath, destPath);
61
+ }
62
+ }
63
+ }
64
+
65
+ // Skills reference the reader's own org setup via {{PROD_ORG_ALIAS}} /
66
+ // {{DEV_ORG_ALIAS}} placeholders instead of a hardcoded org alias.
67
+ function hasAliasPlaceholders(content) {
68
+ return /\{\{(PROD|DEV)_ORG_ALIAS\}\}/.test(content);
69
+ }
70
+
71
+ function applyAliases(content, aliases = {}) {
72
+ return content
73
+ .replace(/\{\{PROD_ORG_ALIAS\}\}/g, aliases.prod || '<PROD_ORG_ALIAS>')
74
+ .replace(/\{\{DEV_ORG_ALIAS\}\}/g, aliases.dev || '<DEV_ORG_ALIAS>');
75
+ }
76
+
77
+ function listSkills() {
78
+ return fs
79
+ .readdirSync(SKILLS_SRC, { withFileTypes: true })
80
+ .filter((e) => e.isDirectory())
81
+ .map((e) => e.name);
82
+ }
83
+
84
+ // Pulls `name`/`description` out of the SKILL.md frontmatter (YAML folded
85
+ // scalar style: "description: >") so non-Claude formats can reuse them.
86
+ function parseSkill(skillName) {
87
+ const raw = fs.readFileSync(
88
+ path.join(SKILLS_SRC, skillName, 'SKILL.md'),
89
+ 'utf8',
90
+ );
91
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
92
+ if (!match) return { name: skillName, description: '', body: raw, raw };
93
+
94
+ const fmLines = match[1].split(/\r?\n/);
95
+ const body = match[2].replace(/^\r?\n/, '');
96
+ let description = '';
97
+
98
+ for (let i = 0; i < fmLines.length; i++) {
99
+ if (/^description:\s*>-?\s*$/.test(fmLines[i])) {
100
+ const descLines = [];
101
+ let j = i + 1;
102
+ for (; j < fmLines.length; j++) {
103
+ if (/^\S/.test(fmLines[j])) break;
104
+ descLines.push(fmLines[j].trim());
105
+ }
106
+ description = descLines.filter(Boolean).join(' ');
107
+ break;
108
+ }
109
+ const inline = fmLines[i].match(/^description:\s*(.+)$/);
110
+ if (inline) {
111
+ description = inline[1].trim();
112
+ break;
113
+ }
114
+ }
115
+
116
+ return { name: skillName, description, body, raw };
117
+ }
118
+
119
+ const FORMATTERS = {
120
+ cursor: (skill) =>
121
+ `---\ndescription: ${JSON.stringify(skill.description)}\nalwaysApply: false\n---\n\n${skill.body}`,
122
+ windsurf: (skill) =>
123
+ `---\ntrigger: model_decision\ndescription: ${JSON.stringify(skill.description)}\n---\n\n${skill.body}`,
124
+ copilot: (skill) =>
125
+ `---\napplyTo: "**"\ndescription: ${JSON.stringify(skill.description)}\n---\n\n${skill.body}`,
126
+ };
127
+
128
+ const EXT = { cursor: '.mdc', windsurf: '.md', copilot: '.instructions.md' };
129
+
130
+ function installSkillToTarget(skillName, targetKey, cwd, aliases = {}) {
131
+ const targetDef = TARGETS[targetKey];
132
+ const destDir = targetDef.dir(cwd);
133
+ fs.mkdirSync(destDir, { recursive: true });
134
+
135
+ if (targetDef.format === 'claude') {
136
+ copySkillDir(
137
+ path.join(SKILLS_SRC, skillName),
138
+ path.join(destDir, skillName),
139
+ aliases,
140
+ );
141
+ return;
142
+ }
143
+
144
+ const skill = parseSkill(skillName);
145
+ skill.body = applyAliases(skill.body, aliases);
146
+ const content = FORMATTERS[targetDef.format](skill);
147
+ fs.writeFileSync(
148
+ path.join(destDir, `${skillName}${EXT[targetDef.format]}`),
149
+ content,
150
+ );
151
+ }
152
+
153
+ function aliasesFromEnv() {
154
+ return {
155
+ prod: process.env.ACEK_PROD_ORG_ALIAS || '',
156
+ dev: process.env.ACEK_DEV_ORG_ALIAS || '',
157
+ };
158
+ }
159
+
160
+ function warnIfAliasesUnresolved(skillNames, aliases) {
161
+ if (aliases.prod && aliases.dev) return;
162
+ const needsAliases = skillNames.some((s) =>
163
+ hasAliasPlaceholders(parseSkill(s).raw),
164
+ );
165
+ if (!needsAliases) return;
166
+ console.log(
167
+ '\nℹ️ Some installed skills reference your org aliases. Set them with\n' +
168
+ ' ACEK_PROD_ORG_ALIAS=<alias> ACEK_DEV_ORG_ALIAS=<alias> before installing,\n' +
169
+ ' or find-and-replace <PROD_ORG_ALIAS> / <DEV_ORG_ALIAS> afterwards.',
170
+ );
171
+ }
172
+
173
+ async function runInteractiveInstall() {
174
+ let prompts;
175
+ try {
176
+ prompts = require('prompts');
177
+ } catch {
178
+ console.error(
179
+ '❌ Interactive mode requires the "prompts" package, which should have been installed automatically.\n' +
180
+ ' Try: npx acek-skills@latest install',
181
+ );
182
+ process.exit(1);
183
+ }
184
+
185
+ const onCancel = () => {
186
+ console.log('\n👋 Cancelled, nothing was installed.');
187
+ process.exit(0);
188
+ };
189
+
190
+ const available = listSkills();
191
+ const { skills } = await prompts(
192
+ {
193
+ type: 'multiselect',
194
+ name: 'skills',
195
+ message: 'Select the skills to install',
196
+ instructions: false,
197
+ hint: '- Space to select, Enter to confirm, a to toggle all',
198
+ choices: available.map((s) => ({ title: s, value: s, selected: true })),
199
+ min: 1,
200
+ },
201
+ { onCancel },
202
+ );
203
+
204
+ const { targets } = await prompts(
205
+ {
206
+ type: 'multiselect',
207
+ name: 'targets',
208
+ message: 'Select the target IDE(s) / tool(s)',
209
+ instructions: false,
210
+ hint: '- Space to select, Enter to confirm',
211
+ choices: Object.entries(TARGETS).map(([value, t]) => ({
212
+ title: t.label,
213
+ value,
214
+ selected: t.selected,
215
+ })),
216
+ min: 1,
217
+ },
218
+ { onCancel },
219
+ );
220
+
221
+ let aliases = {};
222
+ const needsAliases = skills.some((s) =>
223
+ hasAliasPlaceholders(parseSkill(s).raw),
224
+ );
225
+ if (needsAliases) {
226
+ aliases = await prompts(
227
+ [
228
+ {
229
+ type: 'text',
230
+ name: 'prod',
231
+ message: 'Production org alias (e.g. Acme_Production) — leave blank to fill in later',
232
+ },
233
+ {
234
+ type: 'text',
235
+ name: 'dev',
236
+ message: 'Sandbox / dev org alias (e.g. Acme_Dev) — leave blank to fill in later',
237
+ },
238
+ ],
239
+ { onCancel },
240
+ );
241
+ }
242
+
243
+ const cwd = process.cwd();
244
+ for (const targetKey of targets) {
245
+ for (const skillName of skills) {
246
+ installSkillToTarget(skillName, targetKey, cwd, aliases);
247
+ }
248
+ console.log(`✅ ${skills.length} skill(s) installed to ${TARGETS[targetKey].label}`);
249
+ }
250
+ warnIfAliasesUnresolved(skills, aliases);
251
+ console.log('\n🎉 Done!');
252
+ }
253
+
254
+ async function main() {
255
+ if (command === 'install') {
256
+ if (target === '--all') {
257
+ const available = listSkills();
258
+ const aliases = aliasesFromEnv();
259
+ for (const skill of available) {
260
+ installSkillToTarget(skill, 'claude-project', process.cwd(), aliases);
261
+ }
262
+ console.log(
263
+ `✅ All ${available.length} skill(s) installed to ${TARGETS['claude-project'].label}`,
264
+ );
265
+ warnIfAliasesUnresolved(available, aliases);
266
+ } else if (target) {
267
+ const available = listSkills();
268
+ if (!available.includes(target)) {
269
+ console.error(
270
+ `❌ Skill "${target}" not found. Available: ${available.join(', ')}`,
271
+ );
272
+ process.exit(1);
273
+ }
274
+ const aliases = aliasesFromEnv();
275
+ installSkillToTarget(target, 'claude-project', process.cwd(), aliases);
276
+ console.log(`✅ Installed skill: ${target} → ${TARGETS['claude-project'].label}`);
277
+ warnIfAliasesUnresolved([target], aliases);
278
+ } else {
279
+ await runInteractiveInstall();
280
+ }
281
+ } else if (command === 'list') {
282
+ console.log('📦 Available skills:');
283
+ listSkills().forEach((s) => console.log(` - ${s}`));
284
+ } else {
285
+ console.log(`
286
+ Usage:
287
+ acek-skills install Interactive: pick skills, target IDE(s)/tool(s), and org aliases
288
+ acek-skills install --all Install all skills to Claude Code (project), no prompts
289
+ acek-skills install <name> Install a single skill to Claude Code (project), no prompts
290
+
291
+ acek-skills list List available skills
292
+
293
+ Some skills reference your Salesforce org aliases. For non-interactive installs,
294
+ set these env vars to fill them in automatically:
295
+ ACEK_PROD_ORG_ALIAS=<alias> ACEK_DEV_ORG_ALIAS=<alias> acek-skills install --all
296
+ `);
297
+ }
298
+ }
299
+
300
+ main();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "acek-skills",
3
+ "version": "1.0.0",
4
+ "description": "Installable Claude Code skills for Salesforce development (admin, dev, BA, testing, devops, security review, data migration, ideation)",
5
+ "bin": {
6
+ "acek-skills": "bin/cli.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "skills/"
11
+ ],
12
+ "keywords": [
13
+ "claude",
14
+ "claude-code",
15
+ "skills",
16
+ "salesforce"
17
+ ],
18
+ "engines": {
19
+ "node": ">=14"
20
+ },
21
+ "dependencies": {
22
+ "prompts": "^2.4.2"
23
+ },
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/AcekBecek16/acek-sf-skills.git"
28
+ },
29
+ "homepage": "https://github.com/AcekBecek16/acek-sf-skills#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/AcekBecek16/acek-sf-skills/issues"
32
+ }
33
+ }