@siliconoid/agentkit 0.1.1 → 0.1.2

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/bin/cli.mjs CHANGED
@@ -93,6 +93,17 @@ program
93
93
  await runAddSkillPack(pack)
94
94
  })
95
95
 
96
+ program
97
+ .command('add-skill-bundle')
98
+ .description('Add a skill bundle (e.g. superpowers) to an existing project')
99
+ .argument('<bundle>', 'Skill bundle ID (e.g. superpowers)')
100
+ .option('--global', 'Install to ~/.claude/skills/ (works across all projects)')
101
+ .option('--project', 'Install to project platform dirs (default)')
102
+ .action(async (bundle, opts) => {
103
+ const { runAddSkillBundle } = await import('../lib/commands/add-skill-bundle.mjs')
104
+ await runAddSkillBundle(bundle, opts)
105
+ })
106
+
96
107
  program
97
108
  .command('doctor')
98
109
  .description('Check project configuration for issues')
@@ -2,7 +2,7 @@ import { join } from 'path'
2
2
  import {
3
3
  findProjectRoot, readConfig, writeConfig, PLATFORMS,
4
4
  } from '../utils.mjs'
5
- import { scaffoldPlatforms, scaffoldWorkflows, scaffoldSkills } from '../scaffold.mjs'
5
+ import { scaffoldPlatforms, scaffoldWorkflows, scaffoldSkills, scaffoldSkillBundles } from '../scaffold.mjs'
6
6
  import { runSync } from './sync.mjs'
7
7
  import log from '../log.mjs'
8
8
 
@@ -58,6 +58,14 @@ export async function runAddPlatform(platformId) {
58
58
  }
59
59
  }
60
60
 
61
+ // Install skill bundles if any (project-level only)
62
+ if (config.skillBundles?.length > 0) {
63
+ const bundleFiles = scaffoldSkillBundles(projectRoot, [platformId], config.skillBundles)
64
+ if (bundleFiles.length > 0) {
65
+ log.success(` ${bundleFiles.length} skill bundle files installed`)
66
+ }
67
+ }
68
+
61
69
  // Sync portable skills
62
70
  await runSync({ _projectRoot: projectRoot, _platforms: [platformId], _vars: vars })
63
71
 
@@ -0,0 +1,71 @@
1
+ import {
2
+ findProjectRoot, readConfig, writeConfig, SKILL_BUNDLES,
3
+ } from '../utils.mjs'
4
+ import { scaffoldSkillBundles, scaffoldSkillBundlesGlobal } from '../scaffold.mjs'
5
+ import log from '../log.mjs'
6
+
7
+ export async function runAddSkillBundle(bundleId, opts = {}) {
8
+ const validIds = SKILL_BUNDLES.map(b => b.id)
9
+
10
+ if (!validIds.includes(bundleId)) {
11
+ log.error(`Unknown skill bundle "${bundleId}". Available: ${validIds.join(', ')}`)
12
+ process.exit(1)
13
+ }
14
+
15
+ try {
16
+ const projectRoot = findProjectRoot()
17
+ const config = readConfig(projectRoot)
18
+
19
+ if (!config) {
20
+ log.error('No .ai/agentkit.json found. Run `agentkit init` first.')
21
+ process.exit(1)
22
+ }
23
+
24
+ if (config.skillBundles.includes(bundleId)) {
25
+ log.warn(`Skill bundle "${bundleId}" is already installed.`)
26
+ return
27
+ }
28
+
29
+ const bundle = SKILL_BUNDLES.find(b => b.id === bundleId)
30
+
31
+ // Determine scope from flags, config, or default
32
+ let scope = 'project'
33
+ if (opts.global && opts.project) scope = 'both'
34
+ else if (opts.global) scope = 'global'
35
+ else if (opts.project) scope = 'project'
36
+
37
+ // Add to config
38
+ config.skillBundles.push(bundleId)
39
+ config.superpowersScope = scope
40
+
41
+ log.header(`Adding skill bundle: ${bundle.name} (${scope})...`)
42
+
43
+ let totalFiles = 0
44
+
45
+ // Project-level install
46
+ if (scope === 'project' || scope === 'both') {
47
+ const files = scaffoldSkillBundles(projectRoot, config.platforms, [bundleId])
48
+ totalFiles += files.length
49
+ }
50
+
51
+ // Global install
52
+ if (scope === 'global' || scope === 'both') {
53
+ const files = scaffoldSkillBundlesGlobal([bundleId])
54
+ totalFiles += files.length
55
+ log.info(` Global: ${files.length} files → ~/.claude/skills/`)
56
+ }
57
+
58
+ if (totalFiles > 0) {
59
+ log.success(` ${totalFiles} file(s) installed`)
60
+ }
61
+
62
+ // Update config
63
+ writeConfig(projectRoot, config)
64
+ log.success(`\nSkill bundle "${bundleId}" added.`)
65
+ log.plain('')
66
+ } catch (err) {
67
+ log.error(`Add skill bundle failed: ${err.message}`)
68
+ log.verbose(err.stack)
69
+ process.exit(1)
70
+ }
71
+ }
@@ -7,7 +7,7 @@ import {
7
7
  } from '../prompts.mjs'
8
8
  import {
9
9
  scaffoldBase, scaffoldFramework, scaffoldPlatforms,
10
- scaffoldWorkflows, scaffoldSkills, scaffoldSkillBundles,
10
+ scaffoldWorkflows, scaffoldSkills, scaffoldSkillBundles, scaffoldSkillBundlesGlobal,
11
11
  runTemplateReplace, generateQuickStart,
12
12
  } from '../scaffold.mjs'
13
13
  import { runSync } from './sync.mjs'
@@ -28,7 +28,7 @@ export async function runInit(opts = {}) {
28
28
  }
29
29
 
30
30
  // Gather configuration
31
- let projectName, projectDescription, framework, platforms, workflowPacks, skillPacks, skillBundles
31
+ let projectName, projectDescription, framework, platforms, workflowPacks, skillPacks, skillBundles, superpowersScope
32
32
 
33
33
  if (opts.smart) {
34
34
  // --smart: zero-question fully auto-detected init
@@ -46,6 +46,7 @@ export async function runInit(opts = {}) {
46
46
  )
47
47
  workflowPacks = WORKFLOW_PACKS.map(w => w.id)
48
48
  skillBundles = SKILL_BUNDLES.map(b => b.id)
49
+ superpowersScope = 'project'
49
50
 
50
51
  log.info(' Smart mode: auto-detecting project configuration...')
51
52
  log.plain(` Name: ${projectName}`)
@@ -67,6 +68,7 @@ export async function runInit(opts = {}) {
67
68
  workflowPacks = []
68
69
  skillPacks = []
69
70
  skillBundles = []
71
+ superpowersScope = 'project'
70
72
  } else if (opts.yes) {
71
73
  // -y: Auto-detect as much as possible, fill remaining with defaults
72
74
  const detected = autoDetect(projectRoot)
@@ -77,6 +79,7 @@ export async function runInit(opts = {}) {
77
79
  workflowPacks = WORKFLOW_PACKS.map(w => w.id)
78
80
  skillPacks = discoverSkillPacks().map(s => s.id)
79
81
  skillBundles = SKILL_BUNDLES.map(b => b.id)
82
+ superpowersScope = 'project'
80
83
  } else {
81
84
  // Interactive flow with auto-detection
82
85
  const answers = await runInteractivePrompts(projectRoot)
@@ -87,6 +90,7 @@ export async function runInit(opts = {}) {
87
90
  workflowPacks = answers.workflowPacks
88
91
  skillPacks = answers.skillPacks
89
92
  skillBundles = answers.skillBundles
93
+ superpowersScope = answers.superpowersScope || 'project'
90
94
  }
91
95
 
92
96
  // Build extended vars from auto-detection for template replacement
@@ -198,12 +202,28 @@ export async function runInit(opts = {}) {
198
202
 
199
203
  // Step 6b: Install skill bundles (e.g. Superpowers)
200
204
  if (skillBundles.length > 0) {
205
+ const scope = superpowersScope || 'project'
201
206
  for (const bundleId of skillBundles) {
202
207
  const bundle = SKILL_BUNDLES.find(b => b.id === bundleId)
203
208
  if (!bundle) continue
204
- log.header(`Installing ${bundle.name}...`)
205
- const bundleFiles = scaffoldSkillBundles(projectRoot, platforms, [bundleId])
206
- log.success(` ${bundleFiles.length} files (14 skills + agents)`)
209
+ log.header(`Installing ${bundle.name} (${scope})...`)
210
+
211
+ let totalFiles = 0
212
+
213
+ // Project-level install
214
+ if (scope === 'project' || scope === 'both') {
215
+ const bundleFiles = scaffoldSkillBundles(projectRoot, platforms, [bundleId])
216
+ totalFiles += bundleFiles.length
217
+ }
218
+
219
+ // Global install (~/.claude/skills/)
220
+ if (scope === 'global' || scope === 'both') {
221
+ const globalFiles = scaffoldSkillBundlesGlobal([bundleId])
222
+ totalFiles += globalFiles.length
223
+ log.info(` Global: ${globalFiles.length} files → ~/.claude/skills/`)
224
+ }
225
+
226
+ log.success(` ${totalFiles} files (14 skills + agents)`)
207
227
  }
208
228
  }
209
229
 
@@ -217,6 +237,7 @@ export async function runInit(opts = {}) {
217
237
  workflowPacks,
218
238
  skillPacks,
219
239
  skillBundles,
240
+ superpowersScope: superpowersScope || 'project',
220
241
  detected: {
221
242
  packageManager: vars.packageManager,
222
243
  primaryLanguage: vars.primaryLanguage,
@@ -1,6 +1,6 @@
1
1
  import { join } from 'path'
2
2
  import { existsSync, readdirSync, readFileSync } from 'fs'
3
- import { findProjectRoot, readConfig, PLATFORMS, WORKFLOW_PACKS, discoverSkills, discoverSkillPacks } from '../utils.mjs'
3
+ import { findProjectRoot, readConfig, PLATFORMS, WORKFLOW_PACKS, SKILL_BUNDLES, discoverSkills, discoverSkillPacks } from '../utils.mjs'
4
4
  import log from '../log.mjs'
5
5
  import pc from 'picocolors'
6
6
 
@@ -56,6 +56,20 @@ export async function runList(opts = {}) {
56
56
  log.dim(' No skill packs installed')
57
57
  }
58
58
 
59
+ // Skill Bundles
60
+ log.header('Skill Bundles')
61
+ if (config.skillBundles?.length > 0) {
62
+ const scope = config.superpowersScope || 'project'
63
+ for (const bundleId of config.skillBundles) {
64
+ const bundle = SKILL_BUNDLES.find(b => b.id === bundleId)
65
+ const name = bundle ? bundle.name : bundleId
66
+ const desc = bundle ? pc.dim(`— ${bundle.description}`) : ''
67
+ log.plain(` ${pc.green('✓')} ${name} ${desc} ${pc.dim(`(${scope})`)}`)
68
+ }
69
+ } else {
70
+ log.dim(' No skill bundles installed')
71
+ }
72
+
59
73
  // Workflow Packs
60
74
  log.header('Workflow Packs')
61
75
  if (config.workflowPacks?.length > 0) {
package/lib/prompts.mjs CHANGED
@@ -249,17 +249,44 @@ export async function askWorkflowPacks() {
249
249
  }))
250
250
 
251
251
  const choices = [...workflowChoices, ...bundleChoices]
252
- if (choices.length === 0) return { workflowPacks: [], skillBundles: [] }
252
+ if (choices.length === 0) return { workflowPacks: [], skillBundles: [], superpowersScope: 'project' }
253
253
 
254
254
  const selected = await checkbox({
255
255
  message: 'Install workflow packs? (space to toggle, enter to confirm)',
256
256
  choices,
257
257
  })
258
258
 
259
- return {
260
- workflowPacks: selected.filter(s => s.type === 'workflow').map(s => s.id),
261
- skillBundles: selected.filter(s => s.type === 'bundle').map(s => s.id),
259
+ const workflowPacks = selected.filter(s => s.type === 'workflow').map(s => s.id)
260
+ const skillBundles = selected.filter(s => s.type === 'bundle').map(s => s.id)
261
+
262
+ // Ask install scope if Superpowers was selected
263
+ let superpowersScope = 'project'
264
+ if (skillBundles.includes('superpowers')) {
265
+ superpowersScope = await askSuperpowersScope()
262
266
  }
267
+
268
+ return { workflowPacks, skillBundles, superpowersScope }
269
+ }
270
+
271
+ export async function askSuperpowersScope() {
272
+ return select({
273
+ message: 'Superpowers install scope:',
274
+ choices: [
275
+ {
276
+ name: 'Project-level (recommended) — Install to project platform dirs',
277
+ value: 'project',
278
+ },
279
+ {
280
+ name: 'Global (~/.claude/skills/) — Install to user home, works across all projects',
281
+ value: 'global',
282
+ },
283
+ {
284
+ name: 'Both — Install to both locations',
285
+ value: 'both',
286
+ },
287
+ ],
288
+ default: 'project',
289
+ })
263
290
  }
264
291
 
265
292
  export async function askSkillPacks() {
@@ -328,10 +355,10 @@ export async function runInteractivePrompts(projectRoot) {
328
355
  platforms = await askPlatforms(detected.platforms)
329
356
  }
330
357
 
331
- const { workflowPacks, skillBundles } = await askWorkflowPacks()
358
+ const { workflowPacks, skillBundles, superpowersScope } = await askWorkflowPacks()
332
359
  const skillPacks = await askSkillPacks()
333
360
 
334
- return { projectName, projectDescription, framework, platforms, workflowPacks, skillBundles, skillPacks }
361
+ return { projectName, projectDescription, framework, platforms, workflowPacks, skillBundles, skillPacks, superpowersScope }
335
362
  }
336
363
 
337
364
  /**
package/lib/scaffold.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { join, relative } from 'path'
2
+ import { homedir } from 'os'
2
3
  import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'
3
4
  import {
4
5
  copyDir, ensureDir, writeIfNotExists, writeWithMarker, templateReplace, getTemplatesDir, MARKERS,
@@ -282,6 +283,43 @@ export function scaffoldSkillBundles(projectRoot, platforms, bundleIds) {
282
283
  return created
283
284
  }
284
285
 
286
+ /**
287
+ * Install skill bundles globally to ~/.claude/skills/ and ~/.claude/agents/.
288
+ * Works across all projects (Claude Code only).
289
+ */
290
+ export function scaffoldSkillBundlesGlobal(bundleIds) {
291
+ const templatesDir = getTemplatesDir()
292
+ const home = homedir()
293
+ const created = []
294
+
295
+ for (const bundleId of bundleIds) {
296
+ const bundle = SKILL_BUNDLES.find(b => b.id === bundleId)
297
+ if (!bundle) continue
298
+
299
+ const bundleDir = join(templatesDir, bundle.templateDir)
300
+ if (!existsSync(bundleDir)) continue
301
+
302
+ // Install each skill subdirectory to ~/.claude/skills/
303
+ for (const entry of readdirSync(bundleDir, { withFileTypes: true })) {
304
+ if (!entry.isDirectory()) continue
305
+ if (entry.name === '_agents') continue
306
+
307
+ const skillSrc = join(bundleDir, entry.name)
308
+ const dest = join(home, '.claude', 'skills', entry.name)
309
+ copyRecursiveIfNotExists(skillSrc, dest, home, created)
310
+ }
311
+
312
+ // Install agents to ~/.claude/agents/
313
+ const agentsDir = join(bundleDir, '_agents')
314
+ if (existsSync(agentsDir)) {
315
+ const destDir = join(home, '.claude', 'agents')
316
+ copyRecursiveIfNotExists(agentsDir, destDir, home, created)
317
+ }
318
+ }
319
+
320
+ return created
321
+ }
322
+
285
323
  /** Helper: recursively copy files, skip existing */
286
324
  function copyRecursiveIfNotExists(src, dest, projectRoot, created) {
287
325
  ensureDir(dest)
package/lib/schema.mjs CHANGED
@@ -65,6 +65,13 @@ export function validateConfig(config) {
65
65
  }
66
66
  }
67
67
 
68
+ // Superpowers scope
69
+ if (config.superpowersScope !== undefined) {
70
+ if (!['project', 'global', 'both'].includes(config.superpowersScope)) {
71
+ warnings.push(`"superpowersScope" should be one of: project, global, both`)
72
+ }
73
+ }
74
+
68
75
  // Workflow packs validation
69
76
  if (Array.isArray(config.workflowPacks)) {
70
77
  for (const w of config.workflowPacks) {
@@ -97,7 +104,7 @@ export function validateConfig(config) {
97
104
  // Warn on unknown top-level keys
98
105
  const knownKeys = new Set([
99
106
  'version', 'projectName', 'projectDescription', 'framework',
100
- 'platforms', 'workflowPacks', 'skillPacks', 'skillBundles', 'packages', 'detected',
107
+ 'platforms', 'workflowPacks', 'skillPacks', 'skillBundles', 'superpowersScope', 'packages', 'detected',
101
108
  ])
102
109
  for (const key of Object.keys(config)) {
103
110
  if (!knownKeys.has(key)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siliconoid/agentkit",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "CLI tool to initialize and manage AI coding rules across multiple platforms",
5
5
  "type": "module",
6
6
  "bin": {