crewkit 1.1.3 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crewkit",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Context engineering framework for AI-assisted development. One command to set up agents, skills, hooks, rules, and memory for your project.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: crewkit-setup
3
+ description: "Scan your codebase and generate a complete context engineering setup: agents, instructions, memory files, and copilot-instructions.md."
4
+ ---
5
+
6
+ # crewkit-setup
7
+
8
+ Scan the current project and generate a complete context engineering setup tailored to this codebase.
9
+
10
+ **You are the AI executing this.** Follow each phase in order. Do not ask the user questions. Scan, generate, report.
11
+
12
+ ---
13
+
14
+ ## Phase 1 — Reconnaissance
15
+
16
+ Scan the project to identify:
17
+ - **Stack & frameworks**: languages, build tools, test frameworks, package managers
18
+ - **Architecture**: monolith/microservices, key directories, entry points
19
+ - **Existing AI context**: any `.github/`, `.ai/`, `CLAUDE.md`, `AGENTS.md` files
20
+
21
+ Write findings to `.crewkit/scan-phase1-recon.md`.
22
+
23
+ ---
24
+
25
+ ## Phase 2 — Codebase Mapping
26
+
27
+ Read representative source files (3-5 per layer: models, services, controllers, tests). Identify:
28
+ - Naming conventions, code style
29
+ - Core domain concepts and entities
30
+ - Test patterns and coverage approach
31
+ - CI/CD and build commands
32
+
33
+ Write findings to `.crewkit/scan-phase2-map.md`.
34
+
35
+ ---
36
+
37
+ ## Phase 3 — Profile Compilation
38
+
39
+ Synthesize phases 1-2 into a project profile:
40
+ - Stack summary
41
+ - Architecture summary
42
+ - Key commands (build, test, lint)
43
+ - Domain concepts
44
+ - Hard rules to enforce
45
+
46
+ Write profile to `.crewkit/last-scan.md`.
47
+
48
+ ---
49
+
50
+ ## Phase 4 — File Generation
51
+
52
+ Generate all files using the profile from `.crewkit/last-scan.md`.
53
+
54
+ ### Step 1 — `.ai/memory/`
55
+
56
+ Create these files (all in English):
57
+ - `architecture.md` — system design, key components, data flow
58
+ - `conventions.md` — naming rules, patterns, anti-patterns
59
+ - `commands.md` — build, test, lint, run commands with flags
60
+ - `testing.md` — test strategy, frameworks, coverage approach
61
+ - `lessons.md` — known gotchas, non-obvious behaviors
62
+ - `state.md` — current sprint goal, recent changes, open issues
63
+
64
+ ### Step 2 — `.github/copilot-instructions.md`
65
+
66
+ ```markdown
67
+ # [PROJECT NAME] — Copilot Instructions
68
+
69
+ ## Overview
70
+ [1-2 sentences: what the project is, main stack]
71
+
72
+ **Stack:** [stacks]
73
+ **Architecture:** [key patterns]
74
+
75
+ ---
76
+
77
+ ## Hard rules (apply to every response)
78
+
79
+ [Numbered list of non-negotiable rules]
80
+
81
+ Details → `.ai/memory/conventions.md`
82
+
83
+ ---
84
+
85
+ ## Memory
86
+
87
+ - `.ai/memory/architecture.md` — system design
88
+ - `.ai/memory/conventions.md` — coding conventions
89
+ - `.ai/memory/commands.md` — build/test commands
90
+ - `.ai/memory/lessons.md` — known gotchas
91
+ ```
92
+
93
+ ### Step 3 — `.github/instructions/` (per-stack rules)
94
+
95
+ For each detected stack, create `[stack].instructions.md`:
96
+ ```markdown
97
+ ---
98
+ applyTo: "**/*.ts"
99
+ ---
100
+ [Stack-specific rules: error handling, imports, typing, testing patterns]
101
+ ```
102
+
103
+ ### Step 4 — `.github/agents/` (agent definitions)
104
+
105
+ Create one file per agent: `explorer.agent.md`, `architect.agent.md`, `coder.agent.md`, `tester.agent.md`, `reviewer.agent.md`.
106
+
107
+ Each file:
108
+ ```markdown
109
+ ---
110
+ name: [Agent Name]
111
+ description: "[One-line role description]"
112
+ ---
113
+ # [Agent Name]
114
+
115
+ **Role:** [role]
116
+ **Responsibilities:** [responsibilities]
117
+ **Approach:** [how it works]
118
+ **Output:** [what it produces]
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Phase 5 — Completion Report
124
+
125
+ After all files are generated, report to the user:
126
+
127
+ ```
128
+ crewkit-setup complete
129
+
130
+ Generated:
131
+ .ai/memory/ (6 files)
132
+ .github/copilot-instructions.md
133
+ .github/instructions/ (per-stack rules)
134
+ .github/agents/ (5 agents)
135
+
136
+ Stack detected: [stack]
137
+ Architecture: [pattern]
138
+
139
+ Next steps:
140
+ 1. Review .github/copilot-instructions.md — adjust hard rules if needed
141
+ 2. Open Copilot Chat — context is now active
142
+ ```
@@ -0,0 +1,157 @@
1
+ ---
2
+ description: "Context engineering setup — scan codebase and generate AI context files (.cursor/rules, AGENTS.md, .ai/memory)"
3
+ alwaysApply: false
4
+ ---
5
+
6
+ # crewkit-setup
7
+
8
+ Scan the current project and generate a complete context engineering setup tailored to this codebase.
9
+
10
+ **You are the AI executing this.** Follow each phase in order. Do not ask the user questions. Scan, generate, report.
11
+
12
+ ---
13
+
14
+ ## Phase 1 — Reconnaissance
15
+
16
+ Scan the project to identify:
17
+ - **Stack & frameworks**: languages, build tools, test frameworks, package managers
18
+ - **Architecture**: monolith/microservices, key directories, entry points
19
+ - **Existing AI context**: any `.cursor/`, `.github/`, `.ai/`, `CLAUDE.md`, `AGENTS.md` files
20
+
21
+ Write findings to `.crewkit/scan-phase1-recon.md`.
22
+
23
+ ---
24
+
25
+ ## Phase 2 — Codebase Mapping
26
+
27
+ Read representative source files (3-5 per layer: models, services, controllers, tests). Identify:
28
+ - Naming conventions, code style
29
+ - Core domain concepts and entities
30
+ - Test patterns and coverage approach
31
+ - CI/CD and build commands
32
+
33
+ Write findings to `.crewkit/scan-phase2-map.md`.
34
+
35
+ ---
36
+
37
+ ## Phase 3 — Profile Compilation
38
+
39
+ Synthesize phases 1-2 into a project profile:
40
+ - Stack summary
41
+ - Architecture summary
42
+ - Key commands (build, test, lint)
43
+ - Domain concepts
44
+ - Hard rules to enforce
45
+
46
+ Write profile to `.crewkit/last-scan.md`.
47
+
48
+ ---
49
+
50
+ ## Phase 4 — File Generation
51
+
52
+ Generate all files using the profile from `.crewkit/last-scan.md`.
53
+
54
+ ### Step 1 — `.ai/memory/`
55
+
56
+ Create these files (all in English):
57
+ - `architecture.md` — system design, key components, data flow
58
+ - `conventions.md` — naming rules, patterns, anti-patterns
59
+ - `commands.md` — build, test, lint, run commands with flags
60
+ - `testing.md` — test strategy, frameworks, coverage approach
61
+ - `lessons.md` — known gotchas, non-obvious behaviors
62
+ - `state.md` — current sprint goal, recent changes, open issues
63
+
64
+ ### Step 2 — `.cursor/rules/project.md`
65
+
66
+ ```
67
+ ---
68
+ description: "Project rules"
69
+ alwaysApply: true
70
+ ---
71
+ # [PROJECT NAME] — Project Rules
72
+
73
+ ## Overview
74
+ [1-2 sentences: what the project is, main stack]
75
+
76
+ **Stack:** [stacks]
77
+ **Architecture:** [key patterns]
78
+
79
+ ---
80
+
81
+ ## Hard rules
82
+
83
+ [Numbered list of non-negotiable rules from the profile]
84
+
85
+ ---
86
+
87
+ ## Memory
88
+
89
+ Reference files in `.ai/memory/` for detailed context:
90
+ - `.ai/memory/architecture.md` — system design
91
+ - `.ai/memory/conventions.md` — coding conventions
92
+ - `.ai/memory/commands.md` — build/test commands
93
+ ```
94
+
95
+ ### Step 3 — `.cursor/rules/` (per-stack rules)
96
+
97
+ For each detected stack (e.g., `typescript.md`, `python.md`), generate a rules file with:
98
+ ```
99
+ ---
100
+ description: "[Stack] coding rules"
101
+ globs: ["**/*.ts"] (or appropriate glob)
102
+ alwaysApply: false
103
+ ---
104
+ [Stack-specific rules: error handling, imports, typing, testing patterns]
105
+ ```
106
+
107
+ ### Step 4 — `AGENTS.md`
108
+
109
+ Create a single `AGENTS.md` at the project root with all agents as `##` sections:
110
+
111
+ ```markdown
112
+ # Agents
113
+
114
+ ## Explorer
115
+ Role: Codebase reconnaissance and research.
116
+ [...]
117
+
118
+ ## Architect
119
+ Role: System design and planning.
120
+ [...]
121
+
122
+ ## Coder
123
+ Role: Implementation.
124
+ [...]
125
+
126
+ ## Tester
127
+ Role: Test writing and validation.
128
+ [...]
129
+
130
+ ## Reviewer
131
+ Role: Code review and quality.
132
+ [...]
133
+ ```
134
+
135
+ Each agent section should include: Role, Responsibilities, Approach, Output format.
136
+
137
+ ---
138
+
139
+ ## Phase 5 — Completion Report
140
+
141
+ After all files are generated, report to the user:
142
+
143
+ ```
144
+ crewkit-setup complete
145
+
146
+ Generated:
147
+ .ai/memory/ (6 files)
148
+ .cursor/rules/ (project.md + per-stack rules)
149
+ AGENTS.md (5 agents)
150
+
151
+ Stack detected: [stack]
152
+ Architecture: [pattern]
153
+
154
+ Next steps:
155
+ 1. Review .cursor/rules/project.md — adjust hard rules if needed
156
+ 2. Open Cursor and start coding — context is active
157
+ ```
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "analytics",
3
+ "description": "Development metrics and post-mortem analysis",
4
+ "components": {
5
+ "skills": ["dev-metrics", "retro"]
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "quality",
3
+ "description": "Code review, blast radius analysis, and post-mortems",
4
+ "components": {
5
+ "skills": ["review-pr", "impact", "retro"]
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "security",
3
+ "description": "OWASP vulnerability scanning and security hardening",
4
+ "components": {
5
+ "skills": ["security-scan"]
6
+ }
7
+ }
package/src/add.js CHANGED
@@ -1,45 +1,109 @@
1
- import { cpSync, mkdirSync, existsSync } from 'node:fs';
1
+ import { cpSync, mkdirSync, existsSync, readdirSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
+ import { CORE_SKILLS } from './constants.js';
4
5
 
5
- const OPTIONAL_SKILLS = ['retro', 'dev-metrics', 'security-scan', 'impact'];
6
+ function getOptionalSkills() {
7
+ const skillsDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'templates', 'skills');
8
+ if (!existsSync(skillsDir)) return [];
9
+ return readdirSync(skillsDir, { withFileTypes: true })
10
+ .filter(d => d.isDirectory() && !CORE_SKILLS.includes(d.name))
11
+ .map(d => d.name);
12
+ }
6
13
 
7
- export function add(skillName) {
8
- if (!skillName) {
9
- console.error('Error: skill name is required.');
10
- console.log(`\n Available optional skills:\n${OPTIONAL_SKILLS.map(s => ` - ${s}`).join('\n')}\n`);
14
+ function getAvailablePacks() {
15
+ const packsDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'packs');
16
+ if (!existsSync(packsDir)) return [];
17
+ return readdirSync(packsDir, { withFileTypes: true })
18
+ .filter(d => d.isDirectory() && existsSync(join(packsDir, d.name, 'pack.json')))
19
+ .map(d => d.name);
20
+ }
21
+
22
+ function installSkill(skillName) {
23
+ const sourceDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'templates', 'skills', skillName);
24
+
25
+ if (!existsSync(sourceDir)) {
26
+ console.error(`Error: source not found at ${sourceDir}`);
27
+ console.log(' Make sure crewkit is installed first: npx crewkit install');
11
28
  process.exit(1);
12
29
  }
13
30
 
14
- if (!OPTIONAL_SKILLS.includes(skillName)) {
15
- console.error(`Error: "${skillName}" is not a known optional skill.`);
16
- console.log(`\n Available optional skills:\n${OPTIONAL_SKILLS.map(s => ` - ${s}`).join('\n')}\n`);
17
- process.exit(1);
31
+ const targetDir = join(process.cwd(), '.claude', 'skills', skillName);
32
+
33
+ if (existsSync(targetDir)) {
34
+ console.log(` Warning: ${targetDir} already exists. Overwriting.`);
18
35
  }
19
36
 
20
- const source = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'templates', 'skills', skillName, 'SKILL.md');
37
+ mkdirSync(targetDir, { recursive: true });
38
+ cpSync(sourceDir, targetDir, { recursive: true, force: true });
39
+
40
+ return targetDir;
41
+ }
42
+
43
+ function installPack(packName) {
44
+ const packJson = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'packs', packName, 'pack.json');
45
+ let pack;
46
+ try {
47
+ pack = JSON.parse(readFileSync(packJson, 'utf8'));
48
+ } catch {
49
+ console.error(`Error: invalid pack.json for "${packName}".`);
50
+ process.exit(1);
51
+ }
52
+ const skills = pack.components?.skills ?? [];
21
53
 
22
- if (!existsSync(source)) {
23
- console.error(`Error: source not found at ${source}`);
54
+ const templatesDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'templates', 'skills');
55
+ const missing = skills.filter(s => !existsSync(join(templatesDir, s)));
56
+ if (missing.length) {
57
+ console.error(`Error: pack "${packName}" references missing skills: ${missing.join(', ')}`);
24
58
  console.log(' Make sure crewkit is installed first: npx crewkit install');
25
59
  process.exit(1);
26
60
  }
27
61
 
28
- const targetDir = join(process.cwd(), '.claude', 'skills', skillName);
29
- const target = join(targetDir, 'SKILL.md');
62
+ const installed = [];
63
+ for (const skillName of skills) {
64
+ const targetDir = installSkill(skillName);
65
+ installed.push({ name: skillName, path: targetDir });
66
+ }
30
67
 
31
- if (existsSync(target)) {
32
- console.log(` Warning: ${target} already exists. Overwriting.`);
68
+ console.log(`
69
+ Pack "${packName}" installed (${installed.length} skill${installed.length !== 1 ? 's' : ''})
70
+ `);
71
+ for (const { name, path } of installed) {
72
+ console.log(` /${name} → ${path}`);
33
73
  }
74
+ console.log('');
75
+ }
34
76
 
35
- mkdirSync(targetDir, { recursive: true });
36
- cpSync(source, target);
77
+ export function add(skillName) {
78
+ const availablePacks = getAvailablePacks();
79
+ const optionalSkills = getOptionalSkills();
37
80
 
38
- console.log(`
81
+ if (!skillName) {
82
+ console.error('Error: skill name is required.');
83
+ console.log(`\n Available packs:\n${availablePacks.map(p => ` - ${p}`).join('\n')}`);
84
+ console.log(`\n Available optional skills:\n${optionalSkills.map(s => ` - ${s}`).join('\n')}\n`);
85
+ process.exit(1);
86
+ }
87
+
88
+ if (availablePacks.includes(skillName)) {
89
+ installPack(skillName);
90
+ return;
91
+ }
92
+
93
+ if (optionalSkills.includes(skillName)) {
94
+ const targetDir = installSkill(skillName);
95
+ console.log(`
39
96
  ✓ Skill "${skillName}" installed
40
97
 
41
- Copied to: ${target}
98
+ Copied to: ${targetDir}
42
99
 
43
100
  Use /${skillName} in Claude Code to run it.
44
101
  `);
102
+ return;
103
+ }
104
+
105
+ console.error(`Error: "${skillName}" is not a known pack or optional skill.`);
106
+ console.log(`\n Available packs:\n${availablePacks.map(p => ` - ${p}`).join('\n')}`);
107
+ console.log(`\n Available optional skills:\n${optionalSkills.map(s => ` - ${s}`).join('\n')}\n`);
108
+ process.exit(1);
45
109
  }
package/src/cli.js CHANGED
@@ -1,19 +1,23 @@
1
1
  import { install } from './install.js';
2
2
  import { update } from './update.js';
3
3
  import { add } from './add.js';
4
+ import { list } from './list.js';
4
5
 
5
6
  const HELP = `
6
7
  crewkit — Context engineering for AI-assisted development
7
8
 
8
9
  Commands:
9
- install Install crewkit skill globally (~/.claude/skills/)
10
+ install Install crewkit globally (Claude Code, Copilot, Cursor)
10
11
  update Update to latest version (re-run install)
11
- add <skill> Add an optional skill to the current project
12
+ add <name> Add an optional skill or pack to the current project
13
+ list List all available skills and packs (core + add-ons)
12
14
  help Show this message
13
15
 
14
16
  Usage:
15
17
  npx crewkit install # one-time setup
18
+ npx crewkit list # see all available skills and packs
16
19
  npx crewkit add retro # add optional skill to project
20
+ npx crewkit add quality # add a pack (multiple skills)
17
21
  /crewkit-setup # run in your IDE to scan & calibrate a project
18
22
  `;
19
23
 
@@ -22,14 +26,18 @@ export function run(args) {
22
26
 
23
27
  switch (command) {
24
28
  case 'install':
25
- install();
26
- break;
29
+ install().catch(err => { console.error(err.message); process.exit(1); });
30
+ return;
31
+
27
32
  case 'update':
28
- update();
29
- break;
33
+ update().catch(err => { console.error(err.message); process.exit(1); });
34
+ return;
30
35
  case 'add':
31
36
  add(args[1]);
32
37
  break;
38
+ case 'list':
39
+ list();
40
+ break;
33
41
  case 'help':
34
42
  case '--help':
35
43
  case '-h':
@@ -0,0 +1 @@
1
+ export const CORE_SKILLS = ['full-workflow', 'hotfix', 'explore-and-plan', 'review-pr'];
package/src/detect.js ADDED
@@ -0,0 +1,25 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+
5
+ export function detectTools() {
6
+ const tools = [];
7
+ const home = homedir();
8
+
9
+ if (existsSync(join(home, '.claude'))) {
10
+ const dest = join(home, '.claude', 'skills', 'crewkit-setup');
11
+ tools.push({ id: 'claude', name: 'Claude Code', dest, versionFile: join(dest, '.version') });
12
+ }
13
+
14
+ if (existsSync(join(home, '.copilot'))) {
15
+ const dest = join(home, '.copilot', 'agents');
16
+ tools.push({ id: 'copilot', name: 'GitHub Copilot', dest, versionFile: join(dest, 'crewkit-setup.version') });
17
+ }
18
+
19
+ if (existsSync(join(home, '.cursor'))) {
20
+ const dest = join(home, '.cursor');
21
+ tools.push({ id: 'cursor', name: 'Cursor', dest, versionFile: join(dest, 'crewkit-setup.version') });
22
+ }
23
+
24
+ return tools;
25
+ }
package/src/install.js CHANGED
@@ -1,43 +1,65 @@
1
- import { cpSync, mkdirSync, existsSync, readFileSync, writeFileSync } from 'node:fs';
1
+ import { cpSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
- import { homedir } from 'node:os';
4
3
  import { fileURLToPath } from 'node:url';
4
+ import { detectTools } from './detect.js';
5
+ import { selectTargets } from './prompt.js';
5
6
 
6
7
  const __filename = fileURLToPath(import.meta.url);
7
8
  const __dirname = dirname(__filename);
8
9
 
9
- export function install() {
10
- const skillSource = join(__dirname, '..', 'skill');
11
- const skillDest = join(homedir(), '.claude', 'skills', 'crewkit-setup');
12
-
13
- // Verify source exists
14
- if (!existsSync(skillSource)) {
15
- console.error('Error: skill/ directory not found. Package may be corrupted.');
16
- process.exit(1);
17
- }
18
-
19
- // Create destination
20
- mkdirSync(skillDest, { recursive: true });
10
+ function readVersion() {
11
+ return JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
12
+ }
21
13
 
22
- // Copy skill + templates
23
- cpSync(skillSource, skillDest, { recursive: true, force: true });
14
+ function installClaude(target, version) {
15
+ const skillSource = join(__dirname, '..', 'skill');
16
+ mkdirSync(target.dest, { recursive: true });
17
+ cpSync(skillSource, target.dest, { recursive: true, force: true });
18
+ writeFileSync(target.versionFile, version, 'utf8');
19
+ console.log(` ✓ Claude Code → ${target.dest}`);
20
+ console.log(` Run /crewkit-setup in any project\n`);
21
+ }
24
22
 
25
- // Read version
26
- const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
23
+ function installSingleFile(target, templateFile, outputFile, label, hint, version) {
24
+ const template = join(__dirname, '..', 'skill', templateFile);
25
+ mkdirSync(target.dest, { recursive: true });
26
+ copyFileSync(template, join(target.dest, outputFile));
27
+ writeFileSync(target.versionFile, version, 'utf8');
28
+ console.log(` ✓ ${label} → ${join(target.dest, outputFile)}`);
29
+ console.log(` ${hint}\n`);
30
+ }
27
31
 
28
- // Write version marker
29
- writeFileSync(join(skillDest, '.version'), pkg.version, 'utf8');
32
+ export async function install() {
33
+ const version = readVersion();
34
+ const tools = detectTools();
30
35
 
31
- console.log(`
32
- ✓ crewkit v${pkg.version} installed
36
+ if (tools.length === 0) {
37
+ console.log(`
38
+ No supported AI tools detected.
33
39
 
34
- Skill copied to: ${skillDest}
40
+ crewkit looks for: ~/.claude, ~/.copilot, ~/.cursor
41
+ Install one of these tools and re-run.
42
+ `);
43
+ process.exit(1);
44
+ }
35
45
 
36
- Next: open any project in Claude Code and run:
46
+ const targets = await selectTargets(tools);
37
47
 
38
- /crewkit-setup
48
+ console.log(`\n Installing crewkit v${version}...\n`);
39
49
 
40
- This will scan your codebase and generate a complete
41
- context engineering setup (agents, skills, hooks, rules, memory).
42
- `);
50
+ for (const target of targets) {
51
+ switch (target.id) {
52
+ case 'claude':
53
+ installClaude(target, version);
54
+ break;
55
+ case 'copilot':
56
+ installSingleFile(target, 'copilot-agent.md', 'crewkit-setup.md',
57
+ 'GitHub Copilot ', 'Use @crewkit-setup in Copilot CLI or Chat', version);
58
+ break;
59
+ case 'cursor':
60
+ installSingleFile(target, 'cursor-global.md', 'crewkit-setup.md',
61
+ 'Cursor ', 'Copy to .cursor/rules/ in your projects', version);
62
+ break;
63
+ }
64
+ }
43
65
  }
package/src/list.js ADDED
@@ -0,0 +1,75 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
4
+ import { CORE_SKILLS } from './constants.js';
5
+
6
+ function readDescription(skillDir) {
7
+ const skillMd = join(skillDir, 'SKILL.md');
8
+ if (!existsSync(skillMd)) return '';
9
+ const content = readFileSync(skillMd, 'utf8');
10
+ const match = content.match(/^---[\s\S]*?^description:\s*"?(.+?)"?\s*$/m);
11
+ if (!match) return '';
12
+ const desc = match[1].trim();
13
+ return desc.length > 45 ? desc.slice(0, 44) + '…' : desc;
14
+ }
15
+
16
+ function getAvailablePacks() {
17
+ const packsDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'packs');
18
+ if (!existsSync(packsDir)) return [];
19
+ return readdirSync(packsDir, { withFileTypes: true })
20
+ .filter(d => d.isDirectory() && existsSync(join(packsDir, d.name, 'pack.json')))
21
+ .map(d => {
22
+ try {
23
+ const pack = JSON.parse(readFileSync(join(packsDir, d.name, 'pack.json'), 'utf8'));
24
+ return { name: d.name, description: pack.description ?? '', skills: pack.components?.skills ?? [] };
25
+ } catch {
26
+ return { name: d.name, description: '(invalid pack.json)', skills: [] };
27
+ }
28
+ });
29
+ }
30
+
31
+ export function list() {
32
+ const skillsDir = join(homedir(), '.claude', 'skills', 'crewkit-setup', 'templates', 'skills');
33
+
34
+ if (!existsSync(skillsDir)) {
35
+ console.error('Error: crewkit templates not found.');
36
+ console.log(' Make sure crewkit is installed first: npx crewkit install');
37
+ process.exit(1);
38
+ }
39
+
40
+ const allDirs = readdirSync(skillsDir, { withFileTypes: true })
41
+ .filter(d => d.isDirectory())
42
+ .map(d => d.name);
43
+
44
+ const coreSkills = CORE_SKILLS.filter(s => allDirs.includes(s));
45
+ const addonSkills = allDirs.filter(s => !CORE_SKILLS.includes(s)).sort();
46
+ const packs = getAvailablePacks();
47
+
48
+ const pad = (name, width) => name + ' '.repeat(Math.max(1, width - name.length));
49
+ const nameWidth = Math.max(...[...coreSkills, ...addonSkills, ...packs.map(p => p.name)].map(s => s.length)) + 2;
50
+
51
+ console.log('\ncrewkit — available components\n');
52
+
53
+ console.log(' Core skills (installed by /crewkit-setup):');
54
+ for (const name of coreSkills) {
55
+ const desc = readDescription(join(skillsDir, name));
56
+ console.log(` ${pad(name, nameWidth)}${desc}`);
57
+ }
58
+
59
+ console.log('\n Add-on skills (install with: npx crewkit add <name>):');
60
+ for (const name of addonSkills) {
61
+ const desc = readDescription(join(skillsDir, name));
62
+ console.log(` ${pad(name, nameWidth)}${desc}`);
63
+ }
64
+
65
+ if (packs.length > 0) {
66
+ console.log('\n Packs (install with: npx crewkit add <name>):');
67
+ for (const { name, description, skills } of packs) {
68
+ const desc = description.length > 45 ? description.slice(0, 44) + '…' : description;
69
+ const contents = skills.length > 0 ? ` [${skills.join(', ')}]` : '';
70
+ console.log(` ${pad(name, nameWidth)}${desc}${contents}`);
71
+ }
72
+ }
73
+
74
+ console.log('');
75
+ }
package/src/prompt.js ADDED
@@ -0,0 +1,32 @@
1
+ import { createInterface } from 'node:readline';
2
+
3
+ function ask(question) {
4
+ return new Promise(resolve => {
5
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
6
+ rl.question(question, answer => {
7
+ rl.close();
8
+ resolve(answer.trim());
9
+ });
10
+ });
11
+ }
12
+
13
+ export async function selectTargets(tools) {
14
+ if (tools.length === 0) return [];
15
+ if (tools.length === 1) return [tools[0]];
16
+
17
+ console.log('\n Multiple AI tools detected. Where do you want to install crewkit?\n');
18
+ tools.forEach((tool, i) => {
19
+ console.log(` [${i + 1}] ${tool.name}`);
20
+ });
21
+ console.log(` [${tools.length + 1}] All of the above`);
22
+ console.log();
23
+
24
+ const answer = await ask(' Your choice: ');
25
+ const num = parseInt(answer, 10);
26
+
27
+ if (num === tools.length + 1) return tools;
28
+ if (num >= 1 && num <= tools.length) return [tools[num - 1]];
29
+
30
+ console.log(' Invalid choice. Installing to all detected tools.');
31
+ return tools;
32
+ }
package/src/update.js CHANGED
@@ -1,27 +1,32 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join, dirname } from 'node:path';
3
- import { homedir } from 'node:os';
4
3
  import { fileURLToPath } from 'node:url';
4
+ import { detectTools } from './detect.js';
5
5
  import { install } from './install.js';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = dirname(__filename);
9
9
 
10
- export function update() {
11
- const versionFile = join(homedir(), '.claude', 'skills', 'crewkit-setup', '.version');
12
- const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
10
+ function getInstalledVersion() {
11
+ for (const tool of detectTools()) {
12
+ if (existsSync(tool.versionFile)) {
13
+ return readFileSync(tool.versionFile, 'utf8').trim();
14
+ }
15
+ }
16
+ return 'unknown';
17
+ }
13
18
 
19
+ export async function update() {
20
+ const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
14
21
  const newVersion = pkg.version;
15
- const currentVersion = existsSync(versionFile)
16
- ? readFileSync(versionFile, 'utf8').trim()
17
- : 'unknown';
22
+ const currentVersion = getInstalledVersion();
18
23
 
19
24
  if (currentVersion === newVersion) {
20
25
  console.log(` Already up to date (v${newVersion})`);
21
26
  return;
22
27
  }
23
28
 
24
- install();
29
+ await install();
25
30
 
26
31
  const fromLabel = currentVersion === 'unknown' ? 'v0.1 (untracked)' : `v${currentVersion}`;
27
32
  console.log(` Updated: ${fromLabel} → v${newVersion}`);