prjct-cli 0.35.3 → 0.36.1

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +63 -618
  3. package/bin/prjct.ts +116 -17
  4. package/core/cli/start.ts +387 -0
  5. package/core/commands/analysis.ts +58 -32
  6. package/core/commands/command-data.ts +11 -50
  7. package/core/commands/commands.ts +18 -21
  8. package/core/commands/context.ts +238 -0
  9. package/core/commands/register.ts +7 -5
  10. package/core/commands/setup.ts +1 -17
  11. package/core/index.ts +103 -39
  12. package/core/infrastructure/ai-provider.ts +312 -0
  13. package/core/infrastructure/command-installer.ts +49 -5
  14. package/core/infrastructure/editors-config.ts +20 -6
  15. package/core/infrastructure/setup.ts +227 -62
  16. package/core/services/index.ts +2 -0
  17. package/core/services/skill-service.ts +52 -16
  18. package/core/services/sync-service.ts +1080 -0
  19. package/core/types/commands.ts +0 -12
  20. package/core/types/index.ts +12 -1
  21. package/core/types/provider.ts +110 -0
  22. package/core/utils/branding.ts +20 -3
  23. package/dist/bin/prjct.mjs +1053 -1426
  24. package/dist/core/infrastructure/command-installer.js +33 -2
  25. package/dist/core/infrastructure/editors-config.js +13 -3
  26. package/dist/core/infrastructure/setup.js +293 -73
  27. package/package.json +10 -16
  28. package/scripts/postinstall.js +17 -119
  29. package/templates/agentic/agent-routing.md +22 -88
  30. package/templates/agentic/agents/uxui.md +42 -197
  31. package/templates/agentic/context-filtering.md +14 -56
  32. package/templates/agentic/orchestrator.md +26 -302
  33. package/templates/agentic/skill-integration.md +37 -289
  34. package/templates/agentic/subagent-generation.md +31 -104
  35. package/templates/agentic/task-fragmentation.md +39 -273
  36. package/templates/agents/AGENTS.md +33 -181
  37. package/templates/commands/bug.md +22 -520
  38. package/templates/commands/dash.md +26 -161
  39. package/templates/commands/done.md +19 -250
  40. package/templates/commands/enrich.md +21 -732
  41. package/templates/commands/idea.md +18 -160
  42. package/templates/commands/init.md +20 -209
  43. package/templates/commands/merge.md +21 -185
  44. package/templates/commands/next.md +21 -103
  45. package/templates/commands/p.md +1 -1
  46. package/templates/commands/p.toml +37 -0
  47. package/templates/commands/pause.md +21 -272
  48. package/templates/commands/resume.md +18 -411
  49. package/templates/commands/setup.md +0 -1
  50. package/templates/commands/ship.md +30 -627
  51. package/templates/commands/sync.md +11 -1448
  52. package/templates/commands/task.md +17 -439
  53. package/templates/commands/test.md +30 -259
  54. package/templates/global/CLAUDE.md +33 -1
  55. package/templates/global/GEMINI.md +265 -0
  56. package/templates/global/STORAGE-SPEC.md +256 -0
  57. package/templates/global/docs/agents.md +88 -0
  58. package/templates/global/docs/architecture.md +103 -0
  59. package/templates/global/docs/commands.md +96 -0
  60. package/templates/global/docs/validation.md +95 -0
  61. package/CLAUDE.md +0 -211
  62. package/packages/shared/package.json +0 -29
  63. package/packages/shared/src/index.ts +0 -10
  64. package/packages/shared/src/schemas.ts +0 -124
  65. package/packages/shared/src/types.ts +0 -187
  66. package/packages/shared/src/unified.ts +0 -174
  67. package/packages/shared/src/utils.ts +0 -148
  68. package/packages/shared/tsconfig.json +0 -18
@@ -2,10 +2,15 @@
2
2
  * Setup Module - Core installation logic
3
3
  *
4
4
  * Executes ALL setup needed for prjct-cli:
5
- * 1. Install Claude Code CLI if missing
6
- * 2. Sync commands to ~/.claude/commands/p/
7
- * 3. Install global config ~/.claude/CLAUDE.md
8
- * 4. Save version in editors-config
5
+ * 1. Detect AI provider (Claude Code or Gemini CLI)
6
+ * 2. Install CLI if missing
7
+ * 3. Sync commands to provider's commands directory
8
+ * 4. Install global config (CLAUDE.md or GEMINI.md)
9
+ * 5. Save version in editors-config
10
+ *
11
+ * Supports multiple AI CLI agents:
12
+ * - Claude Code: ~/.claude/commands/p/, CLAUDE.md
13
+ * - Gemini CLI: ~/.gemini/commands/p/, GEMINI.md
9
14
  *
10
15
  * This module is called from:
11
16
  * - core/index.js (on first CLI use)
@@ -20,6 +25,13 @@ import installer from './command-installer'
20
25
  import editorsConfig from './editors-config'
21
26
  import { VERSION, getPackageRoot } from '../utils/version'
22
27
  import { isNotFoundError } from '../types/fs'
28
+ import {
29
+ selectProvider,
30
+ detectProvider,
31
+ detectAllProviders,
32
+ Providers,
33
+ } from './ai-provider'
34
+ import type { AIProviderName, AIProviderConfig } from '../types/provider'
23
35
 
24
36
  // Colors
25
37
  const GREEN = '\x1b[32m'
@@ -27,102 +39,161 @@ const YELLOW = '\x1b[33m'
27
39
  const DIM = '\x1b[2m'
28
40
  const NC = '\x1b[0m'
29
41
 
42
+ interface ProviderSetupResult {
43
+ provider: AIProviderName
44
+ cliInstalled: boolean
45
+ commandsAdded: number
46
+ commandsUpdated: number
47
+ configAction: string | null
48
+ }
49
+
30
50
  interface SetupResults {
31
- claudeInstalled: boolean
51
+ provider: AIProviderName // Primary provider (for backward compat)
52
+ providers: ProviderSetupResult[] // All installed providers
53
+ cliInstalled: boolean
32
54
  commandsAdded: number
33
55
  commandsUpdated: number
34
56
  configAction: string | null
35
57
  }
36
58
 
37
59
  /**
38
- * Check if Claude Code CLI is installed
60
+ * Check if an AI CLI is installed
39
61
  */
40
- async function hasClaudeCodeCLI(): Promise<boolean> {
41
- try {
42
- execSync('which claude', { stdio: 'ignore' })
43
- return true
44
- } catch (_error) {
45
- return false
46
- }
62
+ async function hasAICLI(provider: AIProviderConfig): Promise<boolean> {
63
+ const detection = detectProvider(provider.name)
64
+ return detection.installed
47
65
  }
48
66
 
49
67
  /**
50
- * Install Claude Code CLI
68
+ * Install AI CLI for the specified provider
51
69
  */
52
- async function installClaudeCode(): Promise<boolean> {
70
+ async function installAICLI(provider: AIProviderConfig): Promise<boolean> {
71
+ const packageName = provider.name === 'claude'
72
+ ? '@anthropic-ai/claude-code'
73
+ : '@google/gemini-cli'
74
+
53
75
  try {
54
- console.log(`${YELLOW}📦 Claude Code not found. Installing...${NC}`)
76
+ console.log(`${YELLOW}📦 ${provider.displayName} not found. Installing...${NC}`)
55
77
  console.log('')
56
- execSync('npm install -g @anthropic-ai/claude-code', { stdio: 'inherit' })
78
+ execSync(`npm install -g ${packageName}`, { stdio: 'inherit' })
57
79
  console.log('')
58
- console.log(`${GREEN}✓${NC} Claude Code installed successfully`)
80
+ console.log(`${GREEN}✓${NC} ${provider.displayName} installed successfully`)
59
81
  console.log('')
60
82
  return true
61
83
  } catch (error) {
62
- console.log(`${YELLOW}⚠️ Failed to install Claude Code: ${(error as Error).message}${NC}`)
63
- console.log(`${DIM}Please install manually: npm install -g @anthropic-ai/claude-code${NC}`)
84
+ console.log(`${YELLOW}⚠️ Failed to install ${provider.displayName}: ${(error as Error).message}${NC}`)
85
+ console.log(`${DIM}Please install manually: npm install -g ${packageName}${NC}`)
64
86
  console.log('')
65
87
  return false
66
88
  }
67
89
  }
68
90
 
69
91
  /**
70
- * Main setup function
92
+ * Main setup function - installs for ALL detected providers
71
93
  */
72
94
  export async function run(): Promise<SetupResults> {
95
+ // Step 0: Detect all available providers
96
+ const detection = detectAllProviders()
97
+ const selection = selectProvider()
98
+ const primaryProvider = Providers[selection.provider]
99
+
73
100
  const results: SetupResults = {
74
- claudeInstalled: false,
101
+ provider: selection.provider,
102
+ providers: [],
103
+ cliInstalled: false,
75
104
  commandsAdded: 0,
76
105
  commandsUpdated: 0,
77
106
  configAction: null,
78
107
  }
79
108
 
80
- // Step 1: Ensure Claude Code CLI is installed
81
- const hasClaude = await hasClaudeCodeCLI()
109
+ // Step 1: Install for each detected provider
110
+ const providerNames: AIProviderName[] = ['claude', 'gemini']
82
111
 
83
- if (!hasClaude) {
84
- const installed = await installClaudeCode()
85
- if (installed) {
86
- results.claudeInstalled = true
87
- } else {
88
- throw new Error('Claude Code installation failed')
112
+ for (const providerName of providerNames) {
113
+ const providerConfig = Providers[providerName]
114
+ const providerDetection = detection[providerName]
115
+
116
+ const providerResult: ProviderSetupResult = {
117
+ provider: providerName,
118
+ cliInstalled: false,
119
+ commandsAdded: 0,
120
+ commandsUpdated: 0,
121
+ configAction: null,
122
+ }
123
+
124
+ // Check if CLI is installed
125
+ if (!providerDetection.installed) {
126
+ // Only prompt to install the primary (selected) provider
127
+ if (providerName === selection.provider) {
128
+ const installed = await installAICLI(providerConfig)
129
+ if (installed) {
130
+ providerResult.cliInstalled = true
131
+ results.cliInstalled = true
132
+ } else {
133
+ throw new Error(`${providerConfig.displayName} installation failed`)
134
+ }
135
+ } else {
136
+ // Skip non-primary providers that aren't installed
137
+ continue
138
+ }
89
139
  }
90
- }
91
140
 
92
- // Step 2: Detect Claude directory (for commands)
93
- const claudeDetected = await installer.detectClaude()
141
+ // Step 2: Install commands and config for this provider
142
+ if (providerName === 'claude') {
143
+ const claudeDetected = await installer.detectClaude()
144
+
145
+ if (claudeDetected) {
146
+ // Sync commands
147
+ const syncResult = await installer.syncCommands()
148
+ if (syncResult.success) {
149
+ providerResult.commandsAdded = syncResult.added
150
+ providerResult.commandsUpdated = syncResult.updated
151
+ results.commandsAdded += syncResult.added
152
+ results.commandsUpdated += syncResult.updated
153
+ }
94
154
 
95
- if (claudeDetected) {
96
- // Step 3: Sync commands
97
- const syncResult = await installer.syncCommands()
155
+ // Install global configuration
156
+ const configResult = await installer.installGlobalConfig()
157
+ if (configResult.success) {
158
+ providerResult.configAction = configResult.action
159
+ if (!results.configAction) {
160
+ results.configAction = configResult.action
161
+ }
162
+ }
98
163
 
99
- if (syncResult.success) {
100
- results.commandsAdded = syncResult.added
101
- results.commandsUpdated = syncResult.updated
102
- }
164
+ // Install documentation files
165
+ await installer.installDocs()
103
166
 
104
- // Step 4: Install global configuration
105
- const configResult = await installer.installGlobalConfig()
167
+ // Install status line (Claude only)
168
+ await installStatusLine()
169
+ }
170
+ } else if (providerName === 'gemini') {
171
+ // Gemini provider - install router and global config
172
+ const geminiInstalled = await installGeminiRouter()
173
+ if (geminiInstalled) {
174
+ providerResult.commandsAdded = 1
175
+ results.commandsAdded += 1
176
+ }
106
177
 
107
- if (configResult.success) {
108
- results.configAction = configResult.action
178
+ const geminiConfigResult = await installGeminiGlobalConfig()
179
+ if (geminiConfigResult.success) {
180
+ providerResult.configAction = geminiConfigResult.action
181
+ }
109
182
  }
110
183
 
111
- // Step 4b: Install documentation files
112
- await installer.installDocs()
113
-
114
- // Step 4c: Install status line with version check
115
- await installStatusLine()
184
+ results.providers.push(providerResult)
116
185
  }
117
186
 
118
- // Step 5: Save version in editors-config
119
- await editorsConfig.saveConfig(VERSION, installer.getInstallPath())
187
+ // Step 3: Save version in editors-config
188
+ await editorsConfig.saveConfig(VERSION, installer.getInstallPath(), selection.provider)
120
189
 
121
- // Step 6: Migrate existing projects to add cliVersion
190
+ // Step 4: Migrate existing projects to add cliVersion
122
191
  await migrateProjectsCliVersion()
123
192
 
124
- // Show results
125
- showResults(results)
193
+ // Show results for all providers
194
+ for (const providerResult of results.providers) {
195
+ showResults(providerResult, Providers[providerResult.provider])
196
+ }
126
197
 
127
198
  return results
128
199
  }
@@ -130,6 +201,100 @@ export async function run(): Promise<SetupResults> {
130
201
  // Default export for CommonJS require
131
202
  export default { run }
132
203
 
204
+ /**
205
+ * Install the p.toml router for Gemini CLI
206
+ */
207
+ async function installGeminiRouter(): Promise<boolean> {
208
+ try {
209
+ const geminiCommandsDir = path.join(os.homedir(), '.gemini', 'commands')
210
+ const routerSource = path.join(getPackageRoot(), 'templates', 'commands', 'p.toml')
211
+ const routerDest = path.join(geminiCommandsDir, 'p.toml')
212
+
213
+ // Ensure commands directory exists
214
+ fs.mkdirSync(geminiCommandsDir, { recursive: true })
215
+
216
+ // Copy router
217
+ if (fs.existsSync(routerSource)) {
218
+ fs.copyFileSync(routerSource, routerDest)
219
+ return true
220
+ }
221
+ return false
222
+ } catch (error) {
223
+ console.error(`Gemini router warning: ${(error as Error).message}`)
224
+ return false
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Install or update global GEMINI.md configuration
230
+ */
231
+ async function installGeminiGlobalConfig(): Promise<{ success: boolean; action: string | null }> {
232
+ try {
233
+ const geminiDir = path.join(os.homedir(), '.gemini')
234
+ const globalConfigPath = path.join(geminiDir, 'GEMINI.md')
235
+ const templatePath = path.join(getPackageRoot(), 'templates', 'global', 'GEMINI.md')
236
+
237
+ // Ensure ~/.gemini directory exists
238
+ fs.mkdirSync(geminiDir, { recursive: true })
239
+
240
+ // Read template content
241
+ const templateContent = fs.readFileSync(templatePath, 'utf-8')
242
+
243
+ // Check if global config already exists
244
+ let existingContent = ''
245
+ let fileExists = false
246
+
247
+ try {
248
+ existingContent = fs.readFileSync(globalConfigPath, 'utf-8')
249
+ fileExists = true
250
+ } catch (error) {
251
+ if (isNotFoundError(error)) {
252
+ fileExists = false
253
+ } else {
254
+ throw error
255
+ }
256
+ }
257
+
258
+ if (!fileExists) {
259
+ // Create new file with full template
260
+ fs.writeFileSync(globalConfigPath, templateContent, 'utf-8')
261
+ return { success: true, action: 'created' }
262
+ }
263
+
264
+ // File exists - perform intelligent merge
265
+ const startMarker = '<!-- prjct:start - DO NOT REMOVE THIS MARKER -->'
266
+ const endMarker = '<!-- prjct:end - DO NOT REMOVE THIS MARKER -->'
267
+
268
+ const hasMarkers = existingContent.includes(startMarker) && existingContent.includes(endMarker)
269
+
270
+ if (!hasMarkers) {
271
+ // No markers - append prjct section at the end
272
+ const updatedContent = existingContent + '\n\n' + templateContent
273
+ fs.writeFileSync(globalConfigPath, updatedContent, 'utf-8')
274
+ return { success: true, action: 'appended' }
275
+ }
276
+
277
+ // Markers exist - replace content between markers
278
+ const beforeMarker = existingContent.substring(0, existingContent.indexOf(startMarker))
279
+ const afterMarker = existingContent.substring(
280
+ existingContent.indexOf(endMarker) + endMarker.length
281
+ )
282
+
283
+ // Extract prjct section from template
284
+ const prjctSection = templateContent.substring(
285
+ templateContent.indexOf(startMarker),
286
+ templateContent.indexOf(endMarker) + endMarker.length
287
+ )
288
+
289
+ const updatedContent = beforeMarker + prjctSection + afterMarker
290
+ fs.writeFileSync(globalConfigPath, updatedContent, 'utf-8')
291
+ return { success: true, action: 'updated' }
292
+ } catch (error) {
293
+ console.error(`Gemini config warning: ${(error as Error).message}`)
294
+ return { success: false, action: null }
295
+ }
296
+ }
297
+
133
298
  /**
134
299
  * Migrate existing projects to add cliVersion field
135
300
  * This clears the status line warning after npm update
@@ -415,15 +580,15 @@ function ensureStatusLineSymlink(linkPath: string, targetPath: string): void {
415
580
  }
416
581
 
417
582
  /**
418
- * Show setup results
583
+ * Show setup results for a single provider
419
584
  */
420
- function showResults(results: SetupResults): void {
585
+ function showResults(results: ProviderSetupResult, provider: AIProviderConfig): void {
421
586
  console.log('')
422
587
 
423
- if (results.claudeInstalled) {
424
- console.log(` ${GREEN}✓${NC} Claude Code CLI installed`)
588
+ if (results.cliInstalled) {
589
+ console.log(` ${GREEN}✓${NC} ${provider.displayName} CLI installed`)
425
590
  } else {
426
- console.log(` ${GREEN}✓${NC} Claude Code CLI found`)
591
+ console.log(` ${GREEN}✓${NC} ${provider.displayName} CLI found`)
427
592
  }
428
593
 
429
594
  const totalCommands = results.commandsAdded + results.commandsUpdated
@@ -437,11 +602,11 @@ function showResults(results: SetupResults): void {
437
602
  }
438
603
 
439
604
  if (results.configAction === 'created') {
440
- console.log(` ${GREEN}✓${NC} Global config created`)
605
+ console.log(` ${GREEN}✓${NC} Global config created (${provider.contextFile})`)
441
606
  } else if (results.configAction === 'updated') {
442
- console.log(` ${GREEN}✓${NC} Global config updated`)
607
+ console.log(` ${GREEN}✓${NC} Global config updated (${provider.contextFile})`)
443
608
  } else if (results.configAction === 'appended') {
444
- console.log(` ${GREEN}✓${NC} Global config merged`)
609
+ console.log(` ${GREEN}✓${NC} Global config merged (${provider.contextFile})`)
445
610
  }
446
611
 
447
612
  console.log('')
@@ -9,6 +9,8 @@ export { agentService, AgentService } from './agent-service'
9
9
  export { projectService, ProjectService } from './project-service'
10
10
  export { memoryService, MemoryService } from './memory-service'
11
11
  export { breakdownService, BreakdownService } from './breakdown-service'
12
+ export { syncService, SyncService } from './sync-service'
13
+ export type { SyncResult } from './sync-service'
12
14
 
13
15
  // Re-export types from canonical source
14
16
  export type {
@@ -6,10 +6,14 @@
6
6
  *
7
7
  * Skill sources (in priority order):
8
8
  * 1. Project: .prjct/skills/*.md
9
- * 2. Global: ~/.prjct-cli/skills/*.md
10
- * 3. Built-in: templates/skills/*.md
9
+ * 2. Provider: ~/.claude/skills/* or ~/.gemini/skills/* (SKILL.md format)
10
+ * 3. Global: ~/.prjct-cli/skills/*.md
11
+ * 4. Built-in: templates/skills/*.md
11
12
  *
12
- * @version 1.0.0
13
+ * Note: Claude Code and Gemini CLI use identical SKILL.md format,
14
+ * so skills are compatible between both providers.
15
+ *
16
+ * @version 1.1.0
13
17
  */
14
18
 
15
19
  import fs from 'fs/promises'
@@ -17,6 +21,7 @@ import path from 'path'
17
21
  import { glob } from 'glob'
18
22
 
19
23
  import type { SkillMetadata, Skill, SkillSearchResult } from '../types'
24
+ import type { AIProviderName } from '../types/provider'
20
25
 
21
26
  /**
22
27
  * Parse YAML-like frontmatter from markdown
@@ -57,9 +62,17 @@ function parseFrontmatter(content: string): { metadata: Record<string, unknown>;
57
62
 
58
63
  /**
59
64
  * Convert filename to skill ID
65
+ * For SKILL.md files, uses the parent directory name
60
66
  */
61
67
  function fileToSkillId(filePath: string): string {
62
68
  const basename = path.basename(filePath, '.md')
69
+
70
+ // For SKILL.md files (provider format), use parent directory name
71
+ if (basename.toUpperCase() === 'SKILL') {
72
+ const dirName = path.basename(path.dirname(filePath))
73
+ return dirName.toLowerCase().replace(/[^a-z0-9]+/g, '-')
74
+ }
75
+
63
76
  return basename.toLowerCase().replace(/[^a-z0-9]+/g, '-')
64
77
  }
65
78
 
@@ -70,16 +83,27 @@ class SkillService {
70
83
  /**
71
84
  * Get all skill directories in order of priority
72
85
  */
73
- private getSkillDirs(projectPath?: string): Array<{ dir: string; source: Skill['source'] }> {
86
+ private getSkillDirs(projectPath?: string, provider?: AIProviderName): Array<{ dir: string; source: Skill['source']; isProviderSkill?: boolean }> {
74
87
  const homeDir = process.env.HOME || process.env.USERPROFILE || '~'
75
- const dirs: Array<{ dir: string; source: Skill['source'] }> = []
88
+ const dirs: Array<{ dir: string; source: Skill['source']; isProviderSkill?: boolean }> = []
76
89
 
77
90
  // Project skills (highest priority)
78
91
  if (projectPath) {
79
92
  dirs.push({ dir: path.join(projectPath, '.prjct', 'skills'), source: 'project' })
80
93
  }
81
94
 
82
- // Global skills
95
+ // Provider skills (Claude or Gemini)
96
+ // Both use SKILL.md format, so skills are compatible
97
+ if (provider) {
98
+ const providerDir = provider === 'gemini' ? '.gemini' : '.claude'
99
+ dirs.push({ dir: path.join(homeDir, providerDir, 'skills'), source: 'global', isProviderSkill: true })
100
+ } else {
101
+ // Check both providers if no specific one is set
102
+ dirs.push({ dir: path.join(homeDir, '.claude', 'skills'), source: 'global', isProviderSkill: true })
103
+ dirs.push({ dir: path.join(homeDir, '.gemini', 'skills'), source: 'global', isProviderSkill: true })
104
+ }
105
+
106
+ // prjct global skills
83
107
  dirs.push({ dir: path.join(homeDir, '.prjct-cli', 'skills'), source: 'global' })
84
108
 
85
109
  // Built-in skills (lowest priority)
@@ -123,19 +147,31 @@ class SkillService {
123
147
  /**
124
148
  * Load all skills from all sources
125
149
  */
126
- async loadSkills(projectPath?: string): Promise<void> {
150
+ async loadSkills(projectPath?: string, provider?: AIProviderName): Promise<void> {
127
151
  this.skills.clear()
128
- const dirs = this.getSkillDirs(projectPath)
152
+ const dirs = this.getSkillDirs(projectPath, provider)
129
153
 
130
- for (const { dir, source } of dirs) {
154
+ for (const { dir, source, isProviderSkill } of dirs) {
131
155
  try {
132
- const files = await glob('*.md', { cwd: dir, absolute: true })
133
-
134
- for (const file of files) {
135
- const skill = await this.loadSkill(file, source)
136
- if (skill && !this.skills.has(skill.id)) {
137
- // Don't override higher priority skills
138
- this.skills.set(skill.id, skill)
156
+ if (isProviderSkill) {
157
+ // Provider skills use SKILL.md in subdirectories
158
+ // e.g., ~/.claude/skills/my-skill/SKILL.md
159
+ const skillDirs = await glob('*/SKILL.md', { cwd: dir, absolute: true })
160
+ for (const file of skillDirs) {
161
+ const skill = await this.loadSkill(file, source)
162
+ if (skill && !this.skills.has(skill.id)) {
163
+ this.skills.set(skill.id, skill)
164
+ }
165
+ }
166
+ } else {
167
+ // Regular .md files in directory
168
+ const files = await glob('*.md', { cwd: dir, absolute: true })
169
+ for (const file of files) {
170
+ const skill = await this.loadSkill(file, source)
171
+ if (skill && !this.skills.has(skill.id)) {
172
+ // Don't override higher priority skills
173
+ this.skills.set(skill.id, skill)
174
+ }
139
175
  }
140
176
  }
141
177
  } catch (_error) {