@deimoscloud/coreai 0.1.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/.prettierrc +9 -0
- package/AGENT_SPEC.md +347 -0
- package/ARCHITECTURE.md +547 -0
- package/DRAFT_PRD.md +1440 -0
- package/IMPLEMENTATION_PLAN.md +256 -0
- package/PRODUCT.md +473 -0
- package/README.md +303 -0
- package/WORKFLOWS.md +295 -0
- package/agents/_templates/ic-engineer.md +185 -0
- package/agents/_templates/reviewer.md +182 -0
- package/agents/backend-engineer.yaml +72 -0
- package/agents/devops-engineer.yaml +72 -0
- package/agents/engineering-manager.yaml +70 -0
- package/agents/examples/android-engineer.md +302 -0
- package/agents/examples/backend-engineer.md +320 -0
- package/agents/examples/devops-engineer.md +742 -0
- package/agents/examples/engineering-manager.md +469 -0
- package/agents/examples/frontend-engineer.md +58 -0
- package/agents/examples/product-manager.md +315 -0
- package/agents/examples/qa-engineer.md +371 -0
- package/agents/examples/security-engineer.md +525 -0
- package/agents/examples/solutions-architect.md +351 -0
- package/agents/examples/wearos-engineer.md +359 -0
- package/agents/frontend-engineer.yaml +72 -0
- package/commands/core/check-inbox.md +34 -0
- package/commands/core/delegate.md +30 -0
- package/commands/core/git-commit.md +144 -0
- package/commands/core/pr-create.md +193 -0
- package/commands/core/review.md +56 -0
- package/commands/core/sprint-status.md +65 -0
- package/commands/optional/docs-update.md +200 -0
- package/commands/optional/jira-create.md +200 -0
- package/commands/optional/jira-transition.md +184 -0
- package/commands/optional/worktree-cleanup.md +167 -0
- package/commands/optional/worktree-setup.md +110 -0
- package/dist/cli/index.js +4037 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +2978 -0
- package/dist/index.js +3867 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +29 -0
- package/jest.config.js +22 -0
- package/knowledge-library/README.md +118 -0
- package/knowledge-library/android-engineer/context/current.txt +42 -0
- package/knowledge-library/android-engineer/control/decisions.txt +9 -0
- package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/android-engineer/control/objectives.txt +26 -0
- package/knowledge-library/android-engineer/history/.gitkeep +0 -0
- package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/architecture.txt +61 -0
- package/knowledge-library/backend-engineer/context/current.txt +42 -0
- package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
- package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
- package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/context.txt +52 -0
- package/knowledge-library/devops-engineer/context/current.txt +42 -0
- package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
- package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
- package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/context/current.txt +40 -0
- package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
- package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
- package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
- package/knowledge-library/prd.txt +81 -0
- package/knowledge-library/product-manager/context/current.txt +42 -0
- package/knowledge-library/product-manager/control/decisions.txt +9 -0
- package/knowledge-library/product-manager/control/dependencies.txt +19 -0
- package/knowledge-library/product-manager/control/objectives.txt +26 -0
- package/knowledge-library/product-manager/history/.gitkeep +0 -0
- package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/product-manager/tech/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/context/current.txt +42 -0
- package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
- package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
- package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/security-engineer/context/current.txt +42 -0
- package/knowledge-library/security-engineer/control/decisions.txt +9 -0
- package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/security-engineer/control/objectives.txt +26 -0
- package/knowledge-library/security-engineer/history/.gitkeep +0 -0
- package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/context/current.txt +42 -0
- package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
- package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
- package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
- package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/context/current.txt +42 -0
- package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
- package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
- package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
- package/package.json +66 -0
- package/schemas/agent.schema.json +171 -0
- package/schemas/coreai.config.schema.json +257 -0
- package/scripts/add-agent.sh +323 -0
- package/scripts/install.sh +354 -0
- package/src/adapters/factory.test.ts +386 -0
- package/src/adapters/factory.ts +305 -0
- package/src/adapters/index.ts +113 -0
- package/src/adapters/interfaces.ts +268 -0
- package/src/adapters/mcp/client.test.ts +130 -0
- package/src/adapters/mcp/client.ts +451 -0
- package/src/adapters/mcp/discovery.test.ts +315 -0
- package/src/adapters/mcp/discovery.ts +340 -0
- package/src/adapters/mcp/index.ts +66 -0
- package/src/adapters/mcp/mapper.test.ts +218 -0
- package/src/adapters/mcp/mapper.ts +536 -0
- package/src/adapters/mcp/registry.test.ts +433 -0
- package/src/adapters/mcp/registry.ts +550 -0
- package/src/adapters/mcp/types.ts +258 -0
- package/src/adapters/native/filesystem.test.ts +350 -0
- package/src/adapters/native/filesystem.ts +393 -0
- package/src/adapters/native/github.test.ts +173 -0
- package/src/adapters/native/github.ts +627 -0
- package/src/adapters/native/index.ts +22 -0
- package/src/adapters/native/selector.test.ts +224 -0
- package/src/adapters/native/selector.ts +150 -0
- package/src/adapters/types.ts +270 -0
- package/src/agents/compiler.test.ts +399 -0
- package/src/agents/compiler.ts +359 -0
- package/src/agents/index.ts +36 -0
- package/src/agents/loader.test.ts +319 -0
- package/src/agents/loader.ts +143 -0
- package/src/agents/resolver.test.ts +282 -0
- package/src/agents/resolver.ts +262 -0
- package/src/agents/types.ts +87 -0
- package/src/cache/index.ts +38 -0
- package/src/cache/interfaces.ts +283 -0
- package/src/cache/manager.test.ts +266 -0
- package/src/cache/manager.ts +388 -0
- package/src/cache/provider.test.ts +485 -0
- package/src/cache/provider.ts +745 -0
- package/src/cache/types.test.ts +192 -0
- package/src/cache/types.ts +313 -0
- package/src/cli/commands/build.test.ts +248 -0
- package/src/cli/commands/build.ts +244 -0
- package/src/cli/commands/cache.test.ts +221 -0
- package/src/cli/commands/cache.ts +229 -0
- package/src/cli/commands/index.ts +63 -0
- package/src/cli/commands/init.test.ts +173 -0
- package/src/cli/commands/init.ts +296 -0
- package/src/cli/commands/skills.test.ts +272 -0
- package/src/cli/commands/skills.ts +348 -0
- package/src/cli/commands/status.test.ts +392 -0
- package/src/cli/commands/status.ts +332 -0
- package/src/cli/commands/sync.test.ts +213 -0
- package/src/cli/commands/sync.ts +251 -0
- package/src/cli/commands/validate.test.ts +216 -0
- package/src/cli/commands/validate.ts +340 -0
- package/src/cli/index.test.ts +190 -0
- package/src/cli/index.ts +493 -0
- package/src/commands/context.test.ts +163 -0
- package/src/commands/context.ts +111 -0
- package/src/commands/index.ts +56 -0
- package/src/commands/loader.test.ts +273 -0
- package/src/commands/loader.ts +355 -0
- package/src/commands/registry.test.ts +384 -0
- package/src/commands/registry.ts +248 -0
- package/src/commands/runner.test.ts +297 -0
- package/src/commands/runner.ts +222 -0
- package/src/commands/types.ts +361 -0
- package/src/config/index.ts +19 -0
- package/src/config/loader.test.ts +262 -0
- package/src/config/loader.ts +188 -0
- package/src/config/types.ts +154 -0
- package/src/context/index.ts +14 -0
- package/src/context/loader.test.ts +334 -0
- package/src/context/loader.ts +357 -0
- package/src/index.test.ts +13 -0
- package/src/index.ts +244 -0
- package/src/knowledge-library/index.ts +44 -0
- package/src/knowledge-library/manager.test.ts +536 -0
- package/src/knowledge-library/manager.ts +804 -0
- package/src/knowledge-library/types.ts +432 -0
- package/src/skills/generator.test.ts +602 -0
- package/src/skills/generator.ts +491 -0
- package/src/skills/index.ts +27 -0
- package/src/skills/templates.ts +520 -0
- package/src/skills/types.ts +251 -0
- package/templates/completion-report.md +72 -0
- package/templates/feedback.md +56 -0
- package/templates/project-files/CLAUDE.md.template +109 -0
- package/templates/project-files/coreai.json.example +47 -0
- package/templates/project-files/mcp.json.template +20 -0
- package/templates/review-complete.md +64 -0
- package/templates/review-request.md +67 -0
- package/templates/task-assignment.md +51 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +26 -0
- package/tsup.config.ts +23 -0
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates Claude skills from templates based on project configuration.
|
|
5
|
+
* Skills are output to .claude/commands/ directory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, statSync } from 'fs';
|
|
9
|
+
import { join, basename } from 'path';
|
|
10
|
+
import type { ResolvedCoreAIConfig } from '../config/types.js';
|
|
11
|
+
import type {
|
|
12
|
+
SkillTemplate,
|
|
13
|
+
SkillVariables,
|
|
14
|
+
GenerateSkillsOptions,
|
|
15
|
+
GenerateSkillsResult,
|
|
16
|
+
GeneratedSkill,
|
|
17
|
+
SkillDependency,
|
|
18
|
+
} from './types.js';
|
|
19
|
+
import { builtInSkills } from './templates.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Extract variables from config for template substitution
|
|
23
|
+
*/
|
|
24
|
+
export function extractVariables(config?: ResolvedCoreAIConfig | null): SkillVariables {
|
|
25
|
+
const vars: SkillVariables = {};
|
|
26
|
+
|
|
27
|
+
if (!config) {
|
|
28
|
+
return vars;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Project variables
|
|
32
|
+
vars.PROJECT_NAME = config.project.name;
|
|
33
|
+
vars.PROJECT_ROOT = config.project.root;
|
|
34
|
+
if (config.project.type) {
|
|
35
|
+
vars.PROJECT_TYPE = config.project.type;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Issue tracker variables
|
|
39
|
+
if (config.integrations?.issue_tracker) {
|
|
40
|
+
const tracker = config.integrations.issue_tracker;
|
|
41
|
+
if (tracker.config?.project_key) {
|
|
42
|
+
vars.JIRA_PROJECT = tracker.config.project_key;
|
|
43
|
+
}
|
|
44
|
+
if (tracker.config?.base_url) {
|
|
45
|
+
vars.JIRA_URL = tracker.config.base_url;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Git variables
|
|
50
|
+
if (config.integrations?.git) {
|
|
51
|
+
const git = config.integrations.git;
|
|
52
|
+
if (git.config?.repo) {
|
|
53
|
+
vars.GITHUB_REPO = git.config.repo;
|
|
54
|
+
}
|
|
55
|
+
if (git.config?.owner) {
|
|
56
|
+
vars.GITHUB_OWNER = git.config.owner;
|
|
57
|
+
}
|
|
58
|
+
if (git.config?.default_branch) {
|
|
59
|
+
vars.DEFAULT_BRANCH = git.config.default_branch;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Documentation variables
|
|
64
|
+
if (config.integrations?.documentation) {
|
|
65
|
+
const docs = config.integrations.documentation;
|
|
66
|
+
if (docs.config?.space_key) {
|
|
67
|
+
vars.CONFLUENCE_SPACE = docs.config.space_key;
|
|
68
|
+
}
|
|
69
|
+
if (docs.config?.base_url) {
|
|
70
|
+
vars.CONFLUENCE_URL = docs.config.base_url;
|
|
71
|
+
}
|
|
72
|
+
if (docs.config?.base_path) {
|
|
73
|
+
vars.DOCS_PATH = docs.config.base_path;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Quality gate commands
|
|
78
|
+
if (config.quality_gates) {
|
|
79
|
+
const gates = config.quality_gates;
|
|
80
|
+
// Map common gate names to variables
|
|
81
|
+
const gateMappings: Record<string, keyof SkillVariables> = {
|
|
82
|
+
lint: 'LINT_CMD',
|
|
83
|
+
test: 'TEST_CMD',
|
|
84
|
+
build: 'BUILD_CMD',
|
|
85
|
+
static_analysis: 'STATIC_ANALYSIS_CMD',
|
|
86
|
+
staticAnalysis: 'STATIC_ANALYSIS_CMD',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
for (const [gateName, gate] of Object.entries(gates)) {
|
|
90
|
+
const varName = gateMappings[gateName];
|
|
91
|
+
if (varName) {
|
|
92
|
+
vars[varName] = gate.command;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Tech stack
|
|
98
|
+
if (config.tech_stack?.primary_language) {
|
|
99
|
+
vars.PRIMARY_LANGUAGE = config.tech_stack.primary_language;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return vars;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Substitute variables in template content
|
|
107
|
+
*/
|
|
108
|
+
export function substituteVariables(content: string, variables: SkillVariables): string {
|
|
109
|
+
let result = content;
|
|
110
|
+
|
|
111
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
112
|
+
if (value !== undefined) {
|
|
113
|
+
// Replace {{VARIABLE}} pattern
|
|
114
|
+
const pattern = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
|
115
|
+
result = result.replace(pattern, value);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Check if a skill's dependencies are satisfied
|
|
124
|
+
*/
|
|
125
|
+
export function checkDependencies(
|
|
126
|
+
skill: SkillTemplate,
|
|
127
|
+
config?: ResolvedCoreAIConfig | null
|
|
128
|
+
): { satisfied: boolean; missing: SkillDependency[] } {
|
|
129
|
+
if (!skill.dependencies || skill.dependencies.length === 0) {
|
|
130
|
+
return { satisfied: true, missing: [] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const missing: SkillDependency[] = [];
|
|
134
|
+
|
|
135
|
+
for (const dep of skill.dependencies) {
|
|
136
|
+
if (!dep.required) {
|
|
137
|
+
// Optional dependencies don't block skill generation
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if the integration is configured
|
|
142
|
+
let hasIntegration = false;
|
|
143
|
+
|
|
144
|
+
if (config?.integrations) {
|
|
145
|
+
switch (dep.type) {
|
|
146
|
+
case 'issue_tracker':
|
|
147
|
+
hasIntegration = !!config.integrations.issue_tracker;
|
|
148
|
+
break;
|
|
149
|
+
case 'git':
|
|
150
|
+
hasIntegration = !!config.integrations.git;
|
|
151
|
+
break;
|
|
152
|
+
case 'documentation':
|
|
153
|
+
hasIntegration = !!config.integrations.documentation;
|
|
154
|
+
break;
|
|
155
|
+
case 'state':
|
|
156
|
+
hasIntegration = !!config.integrations.state;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!hasIntegration) {
|
|
162
|
+
missing.push(dep);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
satisfied: missing.length === 0,
|
|
168
|
+
missing,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Load custom skill templates from a directory
|
|
174
|
+
*/
|
|
175
|
+
export function loadCustomTemplates(templatesDir: string): SkillTemplate[] {
|
|
176
|
+
const templates: SkillTemplate[] = [];
|
|
177
|
+
|
|
178
|
+
if (!existsSync(templatesDir)) {
|
|
179
|
+
return templates;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const files = readdirSync(templatesDir);
|
|
183
|
+
|
|
184
|
+
for (const file of files) {
|
|
185
|
+
if (!file.endsWith('.md')) continue;
|
|
186
|
+
|
|
187
|
+
const filePath = join(templatesDir, file);
|
|
188
|
+
const stat = statSync(filePath);
|
|
189
|
+
if (!stat.isFile()) continue;
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
193
|
+
const template = parseSkillTemplate(file, content);
|
|
194
|
+
templates.push(template);
|
|
195
|
+
} catch {
|
|
196
|
+
// Skip invalid templates
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return templates;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Parse a skill template from markdown content
|
|
205
|
+
*/
|
|
206
|
+
function parseSkillTemplate(filename: string, content: string): SkillTemplate {
|
|
207
|
+
const name = basename(filename, '.md');
|
|
208
|
+
|
|
209
|
+
// Parse frontmatter
|
|
210
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
211
|
+
|
|
212
|
+
let description = name;
|
|
213
|
+
let argumentHint: string | undefined;
|
|
214
|
+
const dependencies: SkillDependency[] = [];
|
|
215
|
+
|
|
216
|
+
if (frontmatterMatch) {
|
|
217
|
+
const frontmatter = frontmatterMatch[1] ?? '';
|
|
218
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
219
|
+
|
|
220
|
+
for (const line of lines) {
|
|
221
|
+
const colonIndex = line.indexOf(':');
|
|
222
|
+
if (colonIndex === -1) continue;
|
|
223
|
+
|
|
224
|
+
const key = line.slice(0, colonIndex).trim();
|
|
225
|
+
let value = line.slice(colonIndex + 1).trim();
|
|
226
|
+
|
|
227
|
+
// Remove quotes
|
|
228
|
+
if (
|
|
229
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
230
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
231
|
+
) {
|
|
232
|
+
value = value.slice(1, -1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
switch (key) {
|
|
236
|
+
case 'description':
|
|
237
|
+
description = value;
|
|
238
|
+
break;
|
|
239
|
+
case 'argument-hint':
|
|
240
|
+
argumentHint = value;
|
|
241
|
+
break;
|
|
242
|
+
case 'requires':
|
|
243
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
244
|
+
const items = value
|
|
245
|
+
.slice(1, -1)
|
|
246
|
+
.split(',')
|
|
247
|
+
.map((s) => s.trim().replace(/['"]/g, ''));
|
|
248
|
+
for (const item of items) {
|
|
249
|
+
if (item) {
|
|
250
|
+
dependencies.push({
|
|
251
|
+
type: mapIntegrationName(item),
|
|
252
|
+
required: true,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
break;
|
|
258
|
+
case 'optional':
|
|
259
|
+
if (value.startsWith('[') && value.endsWith(']')) {
|
|
260
|
+
const items = value
|
|
261
|
+
.slice(1, -1)
|
|
262
|
+
.split(',')
|
|
263
|
+
.map((s) => s.trim().replace(/['"]/g, ''));
|
|
264
|
+
for (const item of items) {
|
|
265
|
+
if (item) {
|
|
266
|
+
dependencies.push({
|
|
267
|
+
type: mapIntegrationName(item),
|
|
268
|
+
required: false,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const template: SkillTemplate = {
|
|
279
|
+
name,
|
|
280
|
+
description,
|
|
281
|
+
category: 'custom',
|
|
282
|
+
content,
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
if (argumentHint) {
|
|
286
|
+
template.argumentHint = argumentHint;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (dependencies.length > 0) {
|
|
290
|
+
template.dependencies = dependencies;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return template;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Map integration name to adapter type
|
|
298
|
+
*/
|
|
299
|
+
function mapIntegrationName(name: string): 'issue_tracker' | 'git' | 'documentation' | 'state' {
|
|
300
|
+
const lower = name.toLowerCase();
|
|
301
|
+
|
|
302
|
+
if (['jira', 'linear', 'issue_tracker', 'issues', 'github-issues'].includes(lower)) {
|
|
303
|
+
return 'issue_tracker';
|
|
304
|
+
}
|
|
305
|
+
if (['git', 'github', 'gitlab', 'bitbucket'].includes(lower)) {
|
|
306
|
+
return 'git';
|
|
307
|
+
}
|
|
308
|
+
if (['docs', 'documentation', 'confluence', 'notion', 'wiki'].includes(lower)) {
|
|
309
|
+
return 'documentation';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return 'state';
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Generate skills from templates
|
|
317
|
+
*/
|
|
318
|
+
export function generateSkills(
|
|
319
|
+
config: ResolvedCoreAIConfig | undefined,
|
|
320
|
+
options: GenerateSkillsOptions = {}
|
|
321
|
+
): GenerateSkillsResult {
|
|
322
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
323
|
+
const outputDir = options.outputDir ?? join(projectRoot, '.claude', 'commands');
|
|
324
|
+
const includeCoreSkills = options.includeCoreSkills ?? true;
|
|
325
|
+
const includeOptionalSkills = options.includeOptionalSkills ?? true;
|
|
326
|
+
const overwrite = options.overwrite ?? false;
|
|
327
|
+
|
|
328
|
+
const result: GenerateSkillsResult = {
|
|
329
|
+
generated: [],
|
|
330
|
+
errors: [],
|
|
331
|
+
variables: {},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// Extract variables from config
|
|
335
|
+
const variables = extractVariables(config);
|
|
336
|
+
|
|
337
|
+
// Merge custom variables
|
|
338
|
+
if (options.variables) {
|
|
339
|
+
Object.assign(variables, options.variables);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
result.variables = variables;
|
|
343
|
+
|
|
344
|
+
// Collect templates to generate
|
|
345
|
+
let templates: SkillTemplate[] = [];
|
|
346
|
+
|
|
347
|
+
// Add built-in skills
|
|
348
|
+
if (includeCoreSkills) {
|
|
349
|
+
templates.push(...builtInSkills.filter((s) => s.category === 'core'));
|
|
350
|
+
}
|
|
351
|
+
if (includeOptionalSkills) {
|
|
352
|
+
templates.push(...builtInSkills.filter((s) => s.category === 'optional'));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Add custom templates
|
|
356
|
+
if (options.customTemplatesDir && existsSync(options.customTemplatesDir)) {
|
|
357
|
+
templates.push(...loadCustomTemplates(options.customTemplatesDir));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Filter to specific skills if requested
|
|
361
|
+
if (options.skills && options.skills.length > 0) {
|
|
362
|
+
const skillsToInclude = options.skills;
|
|
363
|
+
templates = templates.filter((t) => skillsToInclude.includes(t.name));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Ensure output directory exists
|
|
367
|
+
if (!existsSync(outputDir)) {
|
|
368
|
+
mkdirSync(outputDir, { recursive: true });
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Generate each skill
|
|
372
|
+
for (const template of templates) {
|
|
373
|
+
try {
|
|
374
|
+
const generated = generateSkill(template, variables, config, outputDir, overwrite);
|
|
375
|
+
result.generated.push(generated);
|
|
376
|
+
} catch (error) {
|
|
377
|
+
result.errors.push({
|
|
378
|
+
name: template.name,
|
|
379
|
+
error: error instanceof Error ? error.message : String(error),
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Generate a single skill from a template
|
|
389
|
+
*/
|
|
390
|
+
function generateSkill(
|
|
391
|
+
template: SkillTemplate,
|
|
392
|
+
variables: SkillVariables,
|
|
393
|
+
config: ResolvedCoreAIConfig | undefined,
|
|
394
|
+
outputDir: string,
|
|
395
|
+
overwrite: boolean
|
|
396
|
+
): GeneratedSkill {
|
|
397
|
+
const outputPath = join(outputDir, `${template.name}.md`);
|
|
398
|
+
|
|
399
|
+
// Check if file exists and we shouldn't overwrite
|
|
400
|
+
if (existsSync(outputPath) && !overwrite) {
|
|
401
|
+
return {
|
|
402
|
+
name: template.name,
|
|
403
|
+
category: template.category,
|
|
404
|
+
outputPath,
|
|
405
|
+
action: 'skipped',
|
|
406
|
+
skipReason: 'File already exists',
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Check dependencies
|
|
411
|
+
const { satisfied, missing } = checkDependencies(template, config);
|
|
412
|
+
|
|
413
|
+
if (!satisfied) {
|
|
414
|
+
const missingTypes = missing.map((d) => d.type).join(', ');
|
|
415
|
+
return {
|
|
416
|
+
name: template.name,
|
|
417
|
+
category: template.category,
|
|
418
|
+
outputPath,
|
|
419
|
+
action: 'skipped',
|
|
420
|
+
skipReason: `Missing required integrations: ${missingTypes}`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Substitute variables in content
|
|
425
|
+
const content = substituteVariables(template.content, variables);
|
|
426
|
+
|
|
427
|
+
// Check if content changed (for update vs create)
|
|
428
|
+
const isUpdate = existsSync(outputPath);
|
|
429
|
+
|
|
430
|
+
// Write the skill file
|
|
431
|
+
writeFileSync(outputPath, content, 'utf-8');
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
name: template.name,
|
|
435
|
+
category: template.category,
|
|
436
|
+
outputPath,
|
|
437
|
+
action: isUpdate ? 'updated' : 'created',
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Format skill generation result for display
|
|
443
|
+
*/
|
|
444
|
+
export function formatGenerateResult(result: GenerateSkillsResult): string {
|
|
445
|
+
const lines: string[] = [];
|
|
446
|
+
|
|
447
|
+
const created = result.generated.filter((g) => g.action === 'created');
|
|
448
|
+
const updated = result.generated.filter((g) => g.action === 'updated');
|
|
449
|
+
const skipped = result.generated.filter((g) => g.action === 'skipped');
|
|
450
|
+
|
|
451
|
+
if (created.length > 0) {
|
|
452
|
+
lines.push(`Created ${created.length} skill(s):`);
|
|
453
|
+
for (const skill of created) {
|
|
454
|
+
lines.push(` ✓ ${skill.name}`);
|
|
455
|
+
}
|
|
456
|
+
lines.push('');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (updated.length > 0) {
|
|
460
|
+
lines.push(`Updated ${updated.length} skill(s):`);
|
|
461
|
+
for (const skill of updated) {
|
|
462
|
+
lines.push(` ✓ ${skill.name}`);
|
|
463
|
+
}
|
|
464
|
+
lines.push('');
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (skipped.length > 0) {
|
|
468
|
+
lines.push(`Skipped ${skipped.length} skill(s):`);
|
|
469
|
+
for (const skill of skipped) {
|
|
470
|
+
lines.push(` - ${skill.name}: ${skill.skipReason}`);
|
|
471
|
+
}
|
|
472
|
+
lines.push('');
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (result.errors.length > 0) {
|
|
476
|
+
lines.push(`Failed to generate ${result.errors.length} skill(s):`);
|
|
477
|
+
for (const error of result.errors) {
|
|
478
|
+
lines.push(` ✗ ${error.name}: ${error.error}`);
|
|
479
|
+
}
|
|
480
|
+
lines.push('');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const total = created.length + updated.length;
|
|
484
|
+
if (total > 0) {
|
|
485
|
+
lines.push(`Generated ${total} skill(s) to .claude/commands/`);
|
|
486
|
+
} else if (result.generated.length === 0 && result.errors.length === 0) {
|
|
487
|
+
lines.push('No skills to generate.');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return lines.join('\n');
|
|
491
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills Module
|
|
3
|
+
*
|
|
4
|
+
* Generates Claude skills from templates based on project configuration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
extractVariables,
|
|
9
|
+
substituteVariables,
|
|
10
|
+
checkDependencies,
|
|
11
|
+
loadCustomTemplates,
|
|
12
|
+
generateSkills,
|
|
13
|
+
formatGenerateResult,
|
|
14
|
+
} from './generator.js';
|
|
15
|
+
|
|
16
|
+
export { builtInSkills } from './templates.js';
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
SkillTemplate,
|
|
20
|
+
SkillVariables,
|
|
21
|
+
SkillCategory,
|
|
22
|
+
SkillDependency,
|
|
23
|
+
GenerateSkillsOptions,
|
|
24
|
+
GenerateSkillsResult,
|
|
25
|
+
GeneratedSkill,
|
|
26
|
+
SkillError,
|
|
27
|
+
} from './types.js';
|