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 +1 -1
- package/skill/copilot-agent.md +142 -0
- package/skill/cursor-global.md +157 -0
- package/skill/packs/analytics/pack.json +7 -0
- package/skill/packs/quality/pack.json +7 -0
- package/skill/packs/security/pack.json +7 -0
- package/src/add.js +85 -21
- package/src/cli.js +14 -6
- package/src/constants.js +1 -0
- package/src/detect.js +25 -0
- package/src/install.js +50 -28
- package/src/list.js +75 -0
- package/src/prompt.js +32 -0
- package/src/update.js +13 -8
package/package.json
CHANGED
|
@@ -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
|
+
```
|
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
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
29
|
-
const
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
36
|
-
|
|
77
|
+
export function add(skillName) {
|
|
78
|
+
const availablePacks = getAvailablePacks();
|
|
79
|
+
const optionalSkills = getOptionalSkills();
|
|
37
80
|
|
|
38
|
-
|
|
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: ${
|
|
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
|
|
10
|
+
install Install crewkit globally (Claude Code, Copilot, Cursor)
|
|
10
11
|
update Update to latest version (re-run install)
|
|
11
|
-
add <
|
|
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
|
-
|
|
29
|
+
install().catch(err => { console.error(err.message); process.exit(1); });
|
|
30
|
+
return;
|
|
31
|
+
|
|
27
32
|
case 'update':
|
|
28
|
-
update();
|
|
29
|
-
|
|
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':
|
package/src/constants.js
ADDED
|
@@ -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,
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
const
|
|
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
|
-
|
|
29
|
-
|
|
32
|
+
export async function install() {
|
|
33
|
+
const version = readVersion();
|
|
34
|
+
const tools = detectTools();
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
if (tools.length === 0) {
|
|
37
|
+
console.log(`
|
|
38
|
+
No supported AI tools detected.
|
|
33
39
|
|
|
34
|
-
|
|
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
|
-
|
|
46
|
+
const targets = await selectTargets(tools);
|
|
37
47
|
|
|
38
|
-
|
|
48
|
+
console.log(`\n Installing crewkit v${version}...\n`);
|
|
39
49
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
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 =
|
|
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}`);
|