@siliconoid/agentkit 0.1.0 → 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)
@@ -86,7 +89,8 @@ export async function runInit(opts = {}) {
86
89
  platforms = answers.platforms
87
90
  workflowPacks = answers.workflowPacks
88
91
  skillPacks = answers.skillPacks
89
- skillBundles = answers.skillBundles || SKILL_BUNDLES.map(b => b.id)
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
@@ -1,7 +1,7 @@
1
1
  import { input, select, checkbox, confirm } from '@inquirer/prompts'
2
2
  import { existsSync, readFileSync } from 'fs'
3
3
  import { join, basename } from 'path'
4
- import { PLATFORMS, WORKFLOW_PACKS, detectPlatforms, discoverSkillPacks } from './utils.mjs'
4
+ import { PLATFORMS, WORKFLOW_PACKS, SKILL_BUNDLES, detectPlatforms, discoverSkillPacks } from './utils.mjs'
5
5
 
6
6
  // ---------------------------------------------------------------------------
7
7
  // Auto-detection helpers
@@ -235,13 +235,57 @@ export async function askPlatforms(detected) {
235
235
  }
236
236
 
237
237
  export async function askWorkflowPacks() {
238
- return checkbox({
238
+ // Combine workflow packs and skill bundles into one prompt
239
+ const workflowChoices = WORKFLOW_PACKS.map(w => ({
240
+ name: `${w.name} — ${w.description}`,
241
+ value: { type: 'workflow', id: w.id },
242
+ checked: true,
243
+ }))
244
+
245
+ const bundleChoices = SKILL_BUNDLES.map(b => ({
246
+ name: `${b.name} — ${b.description}`,
247
+ value: { type: 'bundle', id: b.id },
248
+ checked: true,
249
+ }))
250
+
251
+ const choices = [...workflowChoices, ...bundleChoices]
252
+ if (choices.length === 0) return { workflowPacks: [], skillBundles: [], superpowersScope: 'project' }
253
+
254
+ const selected = await checkbox({
239
255
  message: 'Install workflow packs? (space to toggle, enter to confirm)',
240
- choices: WORKFLOW_PACKS.map(w => ({
241
- name: `${w.name} — ${w.description}`,
242
- value: w.id,
243
- checked: true,
244
- })),
256
+ choices,
257
+ })
258
+
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()
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',
245
289
  })
246
290
  }
247
291
 
@@ -311,10 +355,10 @@ export async function runInteractivePrompts(projectRoot) {
311
355
  platforms = await askPlatforms(detected.platforms)
312
356
  }
313
357
 
314
- const workflowPacks = await askWorkflowPacks()
358
+ const { workflowPacks, skillBundles, superpowersScope } = await askWorkflowPacks()
315
359
  const skillPacks = await askSkillPacks()
316
360
 
317
- return { projectName, projectDescription, framework, platforms, workflowPacks, skillPacks }
361
+ return { projectName, projectDescription, framework, platforms, workflowPacks, skillBundles, skillPacks, superpowersScope }
318
362
  }
319
363
 
320
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.0",
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": {