@claude-collective/cli 0.29.5 → 0.30.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{chunk-4DX47646.js → chunk-34E7MWJI.js} +12 -37
  3. package/dist/chunk-34E7MWJI.js.map +1 -0
  4. package/dist/{chunk-J3J2WPI2.js → chunk-5P6RUVA7.js} +3 -3
  5. package/dist/{chunk-73E5CQUK.js → chunk-DH6F7EOJ.js} +2 -2
  6. package/dist/chunk-EOJTMX7T.js +183 -0
  7. package/dist/chunk-EOJTMX7T.js.map +1 -0
  8. package/dist/{chunk-Z6UK6MI2.js → chunk-FFZSN5C5.js} +3 -3
  9. package/dist/chunk-FFZSN5C5.js.map +1 -0
  10. package/dist/{chunk-VBOBVQQW.js → chunk-HMZPUOWU.js} +4 -4
  11. package/dist/{chunk-HJS2FWLA.js → chunk-MYLRA7NI.js} +2 -2
  12. package/dist/{chunk-PRY6MLN3.js → chunk-QR2ONHDW.js} +2 -2
  13. package/dist/{chunk-ABK6CSWI.js → chunk-QUL7R35E.js} +306 -503
  14. package/dist/chunk-QUL7R35E.js.map +1 -0
  15. package/dist/{chunk-ZCIWRQQ4.js → chunk-URUSBWWI.js} +2 -2
  16. package/dist/{chunk-4JSKL5SD.js → chunk-VHVRPLDY.js} +2 -2
  17. package/dist/{chunk-DDNK5UNE.js → chunk-WWLEF4MQ.js} +2 -2
  18. package/dist/{chunk-DALU3Y3U.js → chunk-WXW6SQ5K.js} +2 -2
  19. package/dist/{chunk-JDQDYGGC.js → chunk-X5AD7WWC.js} +9 -9
  20. package/dist/commands/build/plugins.js +2 -2
  21. package/dist/commands/build/stack.js +2 -2
  22. package/dist/commands/compile.js +20 -69
  23. package/dist/commands/compile.js.map +1 -1
  24. package/dist/commands/config/get.js +1 -1
  25. package/dist/commands/config/index.js +2 -2
  26. package/dist/commands/config/path.js +1 -1
  27. package/dist/commands/config/set-project.js +1 -1
  28. package/dist/commands/config/show.js +2 -2
  29. package/dist/commands/config/unset-project.js +1 -1
  30. package/dist/commands/diff.js +1 -1
  31. package/dist/commands/doctor.js +1 -1
  32. package/dist/commands/edit.js +30 -61
  33. package/dist/commands/edit.js.map +1 -1
  34. package/dist/commands/eject.js +1 -1
  35. package/dist/commands/import/skill.js +1 -1
  36. package/dist/commands/info.js +1 -1
  37. package/dist/commands/init.js +69 -99
  38. package/dist/commands/init.js.map +1 -1
  39. package/dist/commands/list.js +1 -1
  40. package/dist/commands/new/agent.js +4 -2
  41. package/dist/commands/new/agent.js.map +1 -1
  42. package/dist/commands/new/skill.js +1 -1
  43. package/dist/commands/outdated.js +1 -1
  44. package/dist/commands/search.js +1 -1
  45. package/dist/commands/uninstall.js +40 -28
  46. package/dist/commands/uninstall.js.map +1 -1
  47. package/dist/commands/update.js +3 -5
  48. package/dist/commands/update.js.map +1 -1
  49. package/dist/commands/validate.js +1 -1
  50. package/dist/commands/version/bump.js +1 -1
  51. package/dist/commands/version/index.js +1 -1
  52. package/dist/commands/version/set.js +1 -1
  53. package/dist/commands/version/show.js +1 -1
  54. package/dist/components/wizard/domain-selection.js +3 -3
  55. package/dist/components/wizard/stack-selection.js +3 -3
  56. package/dist/components/wizard/step-approach.js +3 -3
  57. package/dist/components/wizard/step-approach.test.js +3 -3
  58. package/dist/components/wizard/step-build.js +2 -2
  59. package/dist/components/wizard/step-build.test.js +2 -2
  60. package/dist/components/wizard/step-confirm.test.js +2 -2
  61. package/dist/components/wizard/step-settings.js +2 -2
  62. package/dist/components/wizard/step-settings.test.js +6 -6
  63. package/dist/components/wizard/step-settings.test.js.map +1 -1
  64. package/dist/components/wizard/step-sources.js +3 -3
  65. package/dist/components/wizard/step-sources.test.js +3 -3
  66. package/dist/components/wizard/step-stack.js +5 -5
  67. package/dist/components/wizard/step-stack.test.js +5 -5
  68. package/dist/components/wizard/wizard-layout.js +3 -3
  69. package/dist/components/wizard/wizard.js +11 -11
  70. package/dist/hooks/init.js +1 -1
  71. package/dist/{source-manager-JUPEIBMY.js → source-manager-S5XTC6RW.js} +2 -2
  72. package/dist/stores/wizard-store.js +2 -2
  73. package/dist/stores/wizard-store.test.js +2 -2
  74. package/package.json +1 -1
  75. package/dist/chunk-4DX47646.js.map +0 -1
  76. package/dist/chunk-ABK6CSWI.js.map +0 -1
  77. package/dist/chunk-Z6UK6MI2.js.map +0 -1
  78. /package/dist/{chunk-J3J2WPI2.js.map → chunk-5P6RUVA7.js.map} +0 -0
  79. /package/dist/{chunk-73E5CQUK.js.map → chunk-DH6F7EOJ.js.map} +0 -0
  80. /package/dist/{chunk-VBOBVQQW.js.map → chunk-HMZPUOWU.js.map} +0 -0
  81. /package/dist/{chunk-HJS2FWLA.js.map → chunk-MYLRA7NI.js.map} +0 -0
  82. /package/dist/{chunk-PRY6MLN3.js.map → chunk-QR2ONHDW.js.map} +0 -0
  83. /package/dist/{chunk-ZCIWRQQ4.js.map → chunk-URUSBWWI.js.map} +0 -0
  84. /package/dist/{chunk-4JSKL5SD.js.map → chunk-VHVRPLDY.js.map} +0 -0
  85. /package/dist/{chunk-DDNK5UNE.js.map → chunk-WWLEF4MQ.js.map} +0 -0
  86. /package/dist/{chunk-DALU3Y3U.js.map → chunk-WXW6SQ5K.js.map} +0 -0
  87. /package/dist/{chunk-JDQDYGGC.js.map → chunk-X5AD7WWC.js.map} +0 -0
  88. /package/dist/{source-manager-JUPEIBMY.js.map → source-manager-S5XTC6RW.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.30.0] - 2026-02-16
9
+
10
+ ### Added
11
+
12
+ - **Settings.json-based plugin discovery** — New plugin discovery system that reads `.claude/settings.json` to find enabled plugins and resolves their install paths from the global plugin registry (`~/.claude/plugins/installed_plugins.json`). Replaces project-local `.claude/plugins/` directory scanning.
13
+ - **plugin-settings.ts module** — Core module providing `getEnabledPluginKeys`, `resolvePluginInstallPaths`, and `getVerifiedPluginInstallPaths` for settings.json-based plugin resolution with Zod validation and size limits.
14
+ - **plugin-discovery.ts module** — Provides `discoverAllPluginSkills`, `hasIndividualPlugins`, and `listPluginNames` for discovering skills from enabled plugins via the global cache.
15
+ - **Comprehensive test coverage** — 37 new tests covering all plugin discovery scenarios, edge cases, and error handling.
16
+
17
+ ### Changed
18
+
19
+ - **Plugin discovery mechanism** — All commands and library code migrated from collective plugin mode to individual plugin discovery via settings.json.
20
+ - `compile`: Uses `discoverAllPluginSkills` instead of local directory scan
21
+ - `uninstall`: Supports multiple individual plugins with updated messaging
22
+ - `multi-source-loader`: Uses settings.json-based discovery for skill source tagging
23
+ - `plugin-info`: Uses `discoverAllPluginSkills` for skill counting
24
+ - `local-installer`: Added `installPluginConfig` for plugin-only setup
25
+ - **Plugin reference formats clarified** — `@` format (`plugin-name@marketplace`) for installation/registry, `:` format (`skill-id:skill-id`) for runtime skill invocation.
26
+ - **Resolution priority** — Project-scoped plugin installations take precedence over user-scoped installations.
27
+
28
+ ### Removed
29
+
30
+ - **Collective plugin mode** — Removed `getCollectivePluginDir`, `collectPluginSkillIds`, and related collective plugin infrastructure. All plugin discovery now uses individual plugins via settings.json.
31
+ - **DEFAULT_PLUGIN_NAME constant** — Removed from uninstall command as it's no longer needed for individual plugin management.
32
+
33
+ ### Fixed
34
+
35
+ - **Plugin.json path verification** — Fixed verification to check `.claude-plugin/plugin.json` subdirectory instead of root directory.
36
+
8
37
  ## [0.29.5] - 2026-02-15
9
38
 
10
39
  ### Fixed
@@ -5,13 +5,13 @@ import {
5
5
  computeStringHash,
6
6
  createLiquidEngine,
7
7
  determinePluginVersion,
8
+ discoverAllPluginSkills,
8
9
  extractFrontmatter,
9
10
  fetchFromSource,
10
11
  generateAgentPluginManifest,
11
12
  getPluginAgentsDir,
12
13
  getPluginManifestPath,
13
14
  loadAllAgents,
14
- loadPluginSkills,
15
15
  loadProjectAgents,
16
16
  loadProjectConfig,
17
17
  resolveAgents,
@@ -19,18 +19,16 @@ import {
19
19
  typedKeys,
20
20
  writeContentHash,
21
21
  writePluginManifest
22
- } from "./chunk-ABK6CSWI.js";
22
+ } from "./chunk-QUL7R35E.js";
23
23
  import {
24
24
  agentFrontmatterValidationSchema,
25
25
  copy,
26
26
  directoryExists,
27
27
  ensureDir,
28
- fileExists,
29
28
  formatZodErrors,
30
29
  getErrorMessage,
31
30
  glob,
32
31
  log,
33
- projectConfigLoaderSchema,
34
32
  readFile,
35
33
  verbose,
36
34
  warn,
@@ -39,8 +37,7 @@ import {
39
37
  import {
40
38
  CLAUDE_DIR,
41
39
  DIRS,
42
- PROJECT_ROOT,
43
- STANDARD_FILES
40
+ PROJECT_ROOT
44
41
  } from "./chunk-52XVP55K.js";
45
42
  import {
46
43
  init_esm_shims
@@ -106,36 +103,11 @@ async function fetchAgentDefinitionsFromRemote(source, options = {}) {
106
103
  // src/cli/lib/agents/agent-recompiler.ts
107
104
  init_esm_shims();
108
105
  import path2 from "path";
109
- import { parse as parseYaml } from "yaml";
110
106
  async function getExistingAgentNames(pluginDir) {
111
107
  const agentsDir = getPluginAgentsDir(pluginDir);
112
108
  const files = await glob("*.md", agentsDir);
113
109
  return files.map((f) => path2.basename(f, ".md"));
114
110
  }
115
- async function loadConfigWithFallback(pluginDir) {
116
- const legacyConfigPath = path2.join(pluginDir, STANDARD_FILES.CONFIG_YAML);
117
- if (await fileExists(legacyConfigPath)) {
118
- try {
119
- const content = await readFile(legacyConfigPath);
120
- const parsed = parseYaml(content);
121
- const result = projectConfigLoaderSchema.safeParse(parsed);
122
- if (result.success) {
123
- verbose(`Loaded config.yaml from ${legacyConfigPath}`);
124
- return {
125
- // Loader schema validates field types but allows partial configs;
126
- // required field validation happens in validateProjectConfig()
127
- config: result.data,
128
- configPath: legacyConfigPath
129
- };
130
- } else {
131
- verbose(`Invalid config.yaml at ${legacyConfigPath}: ${result.error.message}`);
132
- }
133
- } catch (error) {
134
- verbose(`Failed to parse config.yaml: ${error}`);
135
- }
136
- }
137
- return loadProjectConfig(pluginDir);
138
- }
139
111
  async function resolveAgentNames(params) {
140
112
  const { specifiedAgents, projectConfig, allAgents, outputDir, pluginDir } = params;
141
113
  if (specifiedAgents) {
@@ -192,10 +164,8 @@ async function recompileAgents(options) {
192
164
  failed: [],
193
165
  warnings: []
194
166
  };
195
- let loadedConfig = await loadConfigWithFallback(pluginDir);
196
- if (!loadedConfig && projectDir) {
197
- loadedConfig = await loadConfigWithFallback(projectDir);
198
- }
167
+ const configDir = projectDir ?? pluginDir;
168
+ const loadedConfig = await loadProjectConfig(configDir);
199
169
  const projectConfig = loadedConfig?.config ?? null;
200
170
  const builtinAgents = await loadAllAgents(sourcePath);
201
171
  const projectAgents = projectDir ? await loadProjectAgents(projectDir) : {};
@@ -215,7 +185,12 @@ async function recompileAgents(options) {
215
185
  return result;
216
186
  }
217
187
  verbose(`Recompiling ${agentNames.length} agents in ${outputDir ?? pluginDir}`);
218
- const pluginSkills = providedSkills ?? await loadPluginSkills(pluginDir);
188
+ let pluginSkills;
189
+ if (providedSkills) {
190
+ pluginSkills = providedSkills;
191
+ } else {
192
+ pluginSkills = await discoverAllPluginSkills(projectDir ?? pluginDir);
193
+ }
219
194
  const { compileConfig, warnings } = buildCompileConfig({
220
195
  agentNames,
221
196
  allAgents,
@@ -329,4 +304,4 @@ export {
329
304
  compileAllAgentPlugins,
330
305
  printAgentCompilationSummary
331
306
  };
332
- //# sourceMappingURL=chunk-4DX47646.js.map
307
+ //# sourceMappingURL=chunk-34E7MWJI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/lib/agents/agent-fetcher.ts","../src/cli/lib/agents/agent-recompiler.ts","../src/cli/lib/agents/agent-plugin-compiler.ts","../src/cli/lib/agents/index.ts"],"sourcesContent":["import path from \"path\";\nimport { directoryExists } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { PROJECT_ROOT, DIRS, CLAUDE_DIR } from \"../../consts\";\nimport { fetchFromSource, type FetchOptions } from \"../loading\";\nimport type { AgentSourcePaths } from \"../../types\";\n\nexport type AgentDefinitionOptions = FetchOptions & {\n projectDir?: string;\n};\n\nexport async function getAgentDefinitions(\n remoteSource?: string,\n options: AgentDefinitionOptions = {},\n): Promise<AgentSourcePaths> {\n if (remoteSource) {\n return fetchAgentDefinitionsFromRemote(remoteSource, options);\n }\n return getLocalAgentDefinitions(options);\n}\n\nexport async function getLocalAgentDefinitions(\n options: AgentDefinitionOptions = {},\n): Promise<AgentSourcePaths> {\n const agentsDir = path.join(PROJECT_ROOT, DIRS.agents);\n let templatesDir = path.join(PROJECT_ROOT, DIRS.templates);\n\n if (!(await directoryExists(agentsDir))) {\n throw new Error(\n `Agent partials not found at '${agentsDir}'. Ensure the CLI is properly installed.`,\n );\n }\n\n if (options.projectDir) {\n const localTemplatesDir = path.join(options.projectDir, CLAUDE_DIR, \"templates\");\n if (await directoryExists(localTemplatesDir)) {\n verbose(`Using local templates from: ${localTemplatesDir}`);\n templatesDir = localTemplatesDir;\n }\n }\n\n if (!(await directoryExists(templatesDir))) {\n verbose(`Templates directory not found: ${templatesDir}`);\n }\n\n verbose(`Agent partials loaded from CLI: ${agentsDir}`);\n verbose(`Templates directory: ${templatesDir}`);\n\n return {\n agentsDir,\n templatesDir,\n sourcePath: PROJECT_ROOT,\n };\n}\n\nexport async function fetchAgentDefinitionsFromRemote(\n source: string,\n options: FetchOptions & { agentsDir?: string } = {},\n): Promise<AgentSourcePaths> {\n verbose(`Fetching agent partials from remote: ${source}`);\n\n const result = await fetchFromSource(source, {\n forceRefresh: options.forceRefresh,\n subdir: \"\",\n });\n\n const agentsDir = path.join(result.path, options.agentsDir ?? DIRS.agents);\n const templatesDir = path.join(agentsDir, \"_templates\");\n\n if (!(await directoryExists(agentsDir))) {\n throw new Error(`Agent partials not found at '${agentsDir}'`);\n }\n\n if (!(await directoryExists(templatesDir))) {\n verbose(`Templates directory not found: ${templatesDir}`);\n }\n\n verbose(`Agent partials fetched from: ${result.path}`);\n\n return {\n agentsDir,\n templatesDir,\n sourcePath: result.path,\n };\n}\n","import type { Liquid } from \"liquidjs\";\nimport path from \"path\";\n\nimport { getErrorMessage } from \"../../utils/errors\";\nimport type {\n AgentConfig,\n AgentDefinition,\n AgentName,\n CompileAgentConfig,\n CompileConfig,\n ProjectConfig,\n SkillDefinition,\n SkillId,\n} from \"../../types\";\nimport { glob, writeFile, ensureDir } from \"../../utils/fs\";\nimport { verbose } from \"../../utils/logger\";\nimport { typedEntries, typedKeys } from \"../../utils/typed-object\";\nimport { createLiquidEngine } from \"../compiler\";\nimport { loadProjectConfig } from \"../configuration\";\nimport { loadAllAgents, loadProjectAgents } from \"../loading\";\nimport { getPluginAgentsDir } from \"../plugins\";\nimport { discoverAllPluginSkills } from \"../plugins/plugin-discovery\";\nimport { resolveAgents, buildSkillRefsFromConfig } from \"../resolver\";\nimport { compileAgentForPlugin } from \"../stacks\";\n\nexport type RecompileAgentsOptions = {\n pluginDir: string;\n sourcePath: string;\n agents?: AgentName[];\n skills?: Partial<Record<SkillId, SkillDefinition>>;\n projectDir?: string;\n outputDir?: string;\n};\n\nexport type RecompileAgentsResult = {\n compiled: AgentName[];\n failed: AgentName[];\n warnings: string[];\n};\n\nasync function getExistingAgentNames(pluginDir: string): Promise<AgentName[]> {\n const agentsDir = getPluginAgentsDir(pluginDir);\n const files = await glob(\"*.md\", agentsDir);\n // Boundary cast: directory names from filesystem are agent names by convention\n return files.map((f) => path.basename(f, \".md\") as AgentName);\n}\n\ntype ResolveAgentNamesParams = {\n specifiedAgents?: AgentName[];\n projectConfig: ProjectConfig | null;\n allAgents: Record<AgentName, AgentDefinition>;\n outputDir?: string;\n pluginDir: string;\n};\n\nasync function resolveAgentNames(params: ResolveAgentNamesParams): Promise<AgentName[]> {\n const { specifiedAgents, projectConfig, allAgents, outputDir, pluginDir } = params;\n\n if (specifiedAgents) {\n return specifiedAgents;\n }\n\n if (projectConfig?.agents) {\n verbose(`Using agents from config.yaml: ${projectConfig.agents.join(\", \")}`);\n return projectConfig.agents;\n }\n\n if (outputDir) {\n const names = typedKeys<AgentName>(allAgents);\n verbose(`Using all available agents from source: ${names.join(\", \")}`);\n return names;\n }\n\n return getExistingAgentNames(pluginDir);\n}\n\ntype BuildCompileConfigParams = {\n agentNames: AgentName[];\n allAgents: Record<AgentName, AgentDefinition>;\n projectConfig: ProjectConfig | null;\n pluginDir: string;\n};\n\ntype BuildCompileConfigResult = {\n compileConfig: CompileConfig;\n warnings: string[];\n};\n\nfunction buildCompileConfig(params: BuildCompileConfigParams): BuildCompileConfigResult {\n const { agentNames, allAgents, projectConfig, pluginDir } = params;\n const warnings: string[] = [];\n\n // Store initialization: accumulator filled below for each agent in agentNames\n const compileAgents = {} as Record<AgentName, CompileAgentConfig>;\n for (const agentName of agentNames) {\n if (allAgents[agentName]) {\n const agentStack = projectConfig?.stack?.[agentName];\n compileAgents[agentName] = agentStack ? { skills: buildSkillRefsFromConfig(agentStack) } : {};\n } else {\n warnings.push(`Agent \"${agentName}\" not found in source definitions`);\n }\n }\n\n const compileConfig: CompileConfig = {\n name: projectConfig?.name || path.basename(pluginDir),\n description: projectConfig?.description || \"Recompiled plugin\",\n agents: compileAgents,\n };\n\n return { compileConfig, warnings };\n}\n\ntype CompileAndWriteParams = {\n resolvedAgents: Record<AgentName, AgentConfig>;\n agentsDir: string;\n sourcePath: string;\n engine: Liquid;\n installMode: ProjectConfig[\"installMode\"];\n};\n\nasync function compileAndWriteAgents(\n params: CompileAndWriteParams,\n result: RecompileAgentsResult,\n): Promise<void> {\n const { resolvedAgents, agentsDir, sourcePath, engine, installMode } = params;\n\n for (const [agentName, agent] of typedEntries<AgentName, AgentConfig>(resolvedAgents)) {\n try {\n const output = await compileAgentForPlugin(agentName, agent, sourcePath, engine, installMode);\n await writeFile(path.join(agentsDir, `${agentName}.md`), output);\n result.compiled.push(agentName);\n verbose(` Recompiled: ${agentName}`);\n } catch (error) {\n result.failed.push(agentName);\n result.warnings.push(`Failed to compile ${agentName}: ${getErrorMessage(error)}`);\n }\n }\n}\n\nexport async function recompileAgents(\n options: RecompileAgentsOptions,\n): Promise<RecompileAgentsResult> {\n const { pluginDir, sourcePath, skills: providedSkills, projectDir, outputDir } = options;\n\n const result: RecompileAgentsResult = {\n compiled: [],\n failed: [],\n warnings: [],\n };\n\n const configDir = projectDir ?? pluginDir;\n const loadedConfig = await loadProjectConfig(configDir);\n const projectConfig = loadedConfig?.config ?? null;\n\n const builtinAgents = await loadAllAgents(sourcePath);\n const projectAgents = projectDir ? await loadProjectAgents(projectDir) : {};\n\n // Boundary cast: loadAllAgents returns Record<string, AgentDefinition>, agent dirs are AgentName by convention\n // Priority: project agents > built-in agents\n const allAgents = {\n ...builtinAgents,\n ...projectAgents,\n } as Record<AgentName, AgentDefinition>;\n\n const agentNames = await resolveAgentNames({\n specifiedAgents: options.agents,\n projectConfig,\n allAgents,\n outputDir,\n pluginDir,\n });\n\n if (agentNames.length === 0) {\n result.warnings.push(\"No agents found to recompile\");\n return result;\n }\n\n verbose(`Recompiling ${agentNames.length} agents in ${outputDir ?? pluginDir}`);\n\n // When skills are not provided, discover from all plugin directories.\n let pluginSkills: Partial<Record<SkillId, SkillDefinition>>;\n if (providedSkills) {\n pluginSkills = providedSkills;\n } else {\n pluginSkills = await discoverAllPluginSkills(projectDir ?? pluginDir);\n }\n\n const { compileConfig, warnings } = buildCompileConfig({\n agentNames,\n allAgents,\n projectConfig,\n pluginDir,\n });\n result.warnings.push(...warnings);\n\n const engine = await createLiquidEngine(projectDir);\n const resolvedAgents = await resolveAgents(allAgents, pluginSkills, compileConfig, sourcePath);\n\n const agentsDir = outputDir ?? getPluginAgentsDir(pluginDir);\n await ensureDir(agentsDir);\n\n await compileAndWriteAgents(\n {\n resolvedAgents,\n agentsDir,\n sourcePath,\n engine,\n installMode: projectConfig?.installMode,\n },\n result,\n );\n\n return result;\n}\n","import path from \"path\";\nimport { getErrorMessage } from \"../../utils/errors\";\nimport { readFile, ensureDir, glob, copy } from \"../../utils/fs\";\nimport { log, verbose, warn } from \"../../utils/logger\";\nimport {\n generateAgentPluginManifest,\n writePluginManifest,\n getPluginManifestPath,\n} from \"../plugins\";\nimport { computeStringHash, determinePluginVersion, writeContentHash } from \"../versioning\";\nimport { extractFrontmatter } from \"../../utils/frontmatter\";\nimport type { PluginManifest } from \"../../types\";\nimport { agentFrontmatterValidationSchema, formatZodErrors } from \"../schemas\";\n\nexport type AgentPluginOptions = {\n agentPath: string;\n outputDir: string;\n};\n\nexport type CompiledAgentPlugin = {\n pluginPath: string;\n manifest: PluginManifest;\n agentName: string;\n};\n\nfunction parseAgentFrontmatter(\n content: string,\n filePath: string,\n): { name: string; description: string } | null {\n const raw = extractFrontmatter(content);\n if (!raw) {\n return null;\n }\n\n const result = agentFrontmatterValidationSchema.safeParse(raw);\n if (!result.success) {\n warn(`Invalid agent frontmatter in ${filePath}: ${formatZodErrors(result.error.issues)}`);\n return null;\n }\n\n return { name: result.data.name, description: result.data.description };\n}\n\nexport async function compileAgentPlugin(\n options: AgentPluginOptions,\n): Promise<CompiledAgentPlugin> {\n const { agentPath, outputDir } = options;\n const fileName = path.basename(agentPath);\n\n const content = await readFile(agentPath);\n const frontmatter = parseAgentFrontmatter(content, agentPath);\n\n if (!frontmatter) {\n throw new Error(\n `Agent '${fileName}' has invalid or missing YAML frontmatter. ` +\n `Required fields: 'name' and 'description'. File: ${agentPath}`,\n );\n }\n\n const agentName = frontmatter.name;\n\n verbose(`Compiling agent plugin: ${agentName} from ${agentPath}`);\n\n const pluginDir = path.join(outputDir, `agent-${agentName}`);\n const agentsDir = path.join(pluginDir, \"agents\");\n\n await ensureDir(pluginDir);\n await ensureDir(agentsDir);\n\n const newHash = computeStringHash(content);\n const { version, contentHash } = await determinePluginVersion(\n newHash,\n pluginDir,\n getPluginManifestPath,\n );\n\n const manifest = generateAgentPluginManifest({\n agentName,\n description: frontmatter.description,\n version,\n });\n\n await writePluginManifest(pluginDir, manifest);\n\n await writeContentHash(pluginDir, contentHash, getPluginManifestPath);\n\n verbose(` Wrote plugin.json for ${agentName} (v${version})`);\n\n await copy(agentPath, path.join(agentsDir, `${agentName}.md`));\n verbose(` Copied agent ${fileName} -> agents/${agentName}.md`);\n\n return {\n pluginPath: pluginDir,\n manifest,\n agentName,\n };\n}\n\nexport async function compileAllAgentPlugins(\n agentsDir: string,\n outputDir: string,\n): Promise<CompiledAgentPlugin[]> {\n const results: CompiledAgentPlugin[] = [];\n\n const agentMdFiles = await glob(\"*.md\", agentsDir);\n\n for (const agentFile of agentMdFiles) {\n const agentPath = path.join(agentsDir, agentFile);\n\n try {\n const result = await compileAgentPlugin({\n agentPath,\n outputDir,\n });\n results.push(result);\n log(` [OK] agent-${result.agentName}`);\n } catch (error) {\n const errorMessage = getErrorMessage(error);\n warn(`Failed to compile agent from '${agentFile}': ${errorMessage}`);\n }\n }\n\n return results;\n}\n\nexport function printAgentCompilationSummary(results: CompiledAgentPlugin[]): void {\n log(`\\nCompiled ${results.length} agent plugins:`);\n for (const result of results) {\n log(` - agent-${result.agentName} (v${result.manifest.version})`);\n }\n}\n","export {\n type AgentDefinitionOptions,\n getAgentDefinitions,\n getLocalAgentDefinitions,\n fetchAgentDefinitionsFromRemote,\n} from \"./agent-fetcher\";\n\nexport {\n type RecompileAgentsOptions,\n type RecompileAgentsResult,\n recompileAgents,\n} from \"./agent-recompiler\";\n\nexport {\n type AgentPluginOptions,\n type CompiledAgentPlugin,\n compileAgentPlugin,\n compileAllAgentPlugins,\n printAgentCompilationSummary,\n} from \"./agent-plugin-compiler\";\n"],"mappings":"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","names":["path","path","path","path"]}
@@ -7,11 +7,11 @@ import {
7
7
  } from "./chunk-AGNT3FUE.js";
8
8
  import {
9
9
  useWizardStore
10
- } from "./chunk-DDNK5UNE.js";
10
+ } from "./chunk-WWLEF4MQ.js";
11
11
  import {
12
12
  resolveAllSources,
13
13
  searchExtraSources
14
- } from "./chunk-ABK6CSWI.js";
14
+ } from "./chunk-QUL7R35E.js";
15
15
  import {
16
16
  CLI_COLORS
17
17
  } from "./chunk-52XVP55K.js";
@@ -181,4 +181,4 @@ var StepSources = ({
181
181
  export {
182
182
  StepSources
183
183
  };
184
- //# sourceMappingURL=chunk-J3J2WPI2.js.map
184
+ //# sourceMappingURL=chunk-5P6RUVA7.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-JWIH7YQE.js";
9
9
  import {
10
10
  useWizardStore
11
- } from "./chunk-DDNK5UNE.js";
11
+ } from "./chunk-WWLEF4MQ.js";
12
12
  import {
13
13
  CLI_COLORS
14
14
  } from "./chunk-52XVP55K.js";
@@ -139,4 +139,4 @@ var WizardLayout = ({
139
139
  export {
140
140
  WizardLayout
141
141
  };
142
- //# sourceMappingURL=chunk-73E5CQUK.js.map
142
+ //# sourceMappingURL=chunk-DH6F7EOJ.js.map
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getErrorMessage,
4
+ warn
5
+ } from "./chunk-5WIHSJRO.js";
6
+ import {
7
+ init_esm_shims
8
+ } from "./chunk-AWKZ5BDL.js";
9
+
10
+ // src/cli/utils/exec.ts
11
+ init_esm_shims();
12
+ import { spawn } from "child_process";
13
+ var MAX_PLUGIN_PATH_LENGTH = 1024;
14
+ var MAX_PLUGIN_NAME_LENGTH = 256;
15
+ var MAX_MARKETPLACE_SOURCE_LENGTH = 1024;
16
+ var SAFE_NAME_PATTERN = /^[a-zA-Z0-9._@/-]+$/;
17
+ var SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;
18
+ var CONTROL_CHAR_PATTERN = /[\x00-\x08\x0E-\x1F\x7F]/u;
19
+ function validatePluginPath(pluginPath) {
20
+ if (!pluginPath || pluginPath.trim().length === 0) {
21
+ throw new Error("Plugin path must not be empty.");
22
+ }
23
+ if (pluginPath.length > MAX_PLUGIN_PATH_LENGTH) {
24
+ throw new Error(
25
+ `Plugin path is too long (${pluginPath.length} characters, max ${MAX_PLUGIN_PATH_LENGTH}).`
26
+ );
27
+ }
28
+ if (CONTROL_CHAR_PATTERN.test(pluginPath)) {
29
+ throw new Error("Plugin path contains invalid control characters.");
30
+ }
31
+ if (!SAFE_PLUGIN_PATH_PATTERN.test(pluginPath)) {
32
+ throw new Error(
33
+ `Plugin path contains invalid characters: "${pluginPath}"
34
+ Plugin paths may only contain alphanumeric characters, dashes, underscores, dots, slashes, @, and colons.`
35
+ );
36
+ }
37
+ }
38
+ function validateMarketplaceSource(source) {
39
+ if (!source || source.trim().length === 0) {
40
+ throw new Error("Marketplace source must not be empty.");
41
+ }
42
+ if (source.length > MAX_MARKETPLACE_SOURCE_LENGTH) {
43
+ throw new Error(
44
+ `Marketplace source is too long (${source.length} characters, max ${MAX_MARKETPLACE_SOURCE_LENGTH}).`
45
+ );
46
+ }
47
+ if (CONTROL_CHAR_PATTERN.test(source)) {
48
+ throw new Error("Marketplace source contains invalid control characters.");
49
+ }
50
+ if (!SAFE_PLUGIN_PATH_PATTERN.test(source)) {
51
+ throw new Error(
52
+ `Marketplace source contains invalid characters: "${source}"
53
+ Source may only contain alphanumeric characters, dashes, underscores, dots, slashes, @, and colons.`
54
+ );
55
+ }
56
+ }
57
+ function validatePluginName(pluginName) {
58
+ if (!pluginName || pluginName.trim().length === 0) {
59
+ throw new Error("Plugin name must not be empty.");
60
+ }
61
+ if (pluginName.length > MAX_PLUGIN_NAME_LENGTH) {
62
+ throw new Error(
63
+ `Plugin name is too long (${pluginName.length} characters, max ${MAX_PLUGIN_NAME_LENGTH}).`
64
+ );
65
+ }
66
+ if (CONTROL_CHAR_PATTERN.test(pluginName)) {
67
+ throw new Error("Plugin name contains invalid control characters.");
68
+ }
69
+ if (!SAFE_NAME_PATTERN.test(pluginName)) {
70
+ throw new Error(
71
+ `Plugin name contains invalid characters: "${pluginName}"
72
+ Names may only contain alphanumeric characters, dashes, underscores, dots, @, and slashes.`
73
+ );
74
+ }
75
+ }
76
+ async function execCommand(command, args, options) {
77
+ return new Promise((resolve, reject) => {
78
+ const proc = spawn(command, args, {
79
+ cwd: options?.cwd,
80
+ env: { ...process.env, ...options?.env },
81
+ stdio: ["ignore", "pipe", "pipe"]
82
+ });
83
+ let stdout = "";
84
+ let stderr = "";
85
+ proc.stdout.on("data", (data) => {
86
+ stdout += data.toString();
87
+ });
88
+ proc.stderr.on("data", (data) => {
89
+ stderr += data.toString();
90
+ });
91
+ proc.on("close", (code) => {
92
+ resolve({
93
+ stdout,
94
+ stderr,
95
+ exitCode: code ?? 1
96
+ });
97
+ });
98
+ proc.on("error", (err) => {
99
+ reject(err);
100
+ });
101
+ });
102
+ }
103
+ async function claudePluginInstall(pluginPath, scope, projectDir) {
104
+ validatePluginPath(pluginPath);
105
+ const args = ["plugin", "install", pluginPath, "--scope", scope];
106
+ const result = await execCommand("claude", args, { cwd: projectDir });
107
+ if (result.exitCode !== 0) {
108
+ const errorMessage = result.stderr || result.stdout || "Unknown error";
109
+ throw new Error(`Plugin installation failed: ${errorMessage.trim()}`);
110
+ }
111
+ }
112
+ async function isClaudeCLIAvailable() {
113
+ try {
114
+ const result = await execCommand("claude", ["--version"], {});
115
+ return result.exitCode === 0;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+ async function claudePluginMarketplaceList() {
121
+ try {
122
+ const result = await execCommand("claude", ["plugin", "marketplace", "list", "--json"], {});
123
+ if (result.exitCode !== 0) {
124
+ return [];
125
+ }
126
+ let parsed;
127
+ try {
128
+ parsed = JSON.parse(result.stdout);
129
+ } catch {
130
+ warn("Failed to parse marketplace list output as JSON");
131
+ return [];
132
+ }
133
+ if (!Array.isArray(parsed)) {
134
+ warn("Unexpected marketplace list format \u2014 expected an array");
135
+ return [];
136
+ }
137
+ return parsed;
138
+ } catch {
139
+ return [];
140
+ }
141
+ }
142
+ async function claudePluginMarketplaceExists(name) {
143
+ const marketplaces = await claudePluginMarketplaceList();
144
+ return marketplaces.some((m) => m.name === name);
145
+ }
146
+ async function claudePluginMarketplaceAdd(source) {
147
+ validateMarketplaceSource(source);
148
+ const args = ["plugin", "marketplace", "add", source];
149
+ let result;
150
+ try {
151
+ result = await execCommand("claude", args, {});
152
+ } catch (err) {
153
+ throw new Error(`Failed to add marketplace: ${getErrorMessage(err)}`);
154
+ }
155
+ if (result.exitCode !== 0) {
156
+ const errorMessage = result.stderr || result.stdout || "Unknown error";
157
+ if (errorMessage.includes("already installed")) {
158
+ return;
159
+ }
160
+ throw new Error(`Failed to add marketplace: ${errorMessage.trim()}`);
161
+ }
162
+ }
163
+ async function claudePluginUninstall(pluginName, scope, projectDir) {
164
+ validatePluginName(pluginName);
165
+ const args = ["plugin", "uninstall", pluginName, "--scope", scope];
166
+ const result = await execCommand("claude", args, { cwd: projectDir });
167
+ if (result.exitCode !== 0) {
168
+ const errorMessage = result.stderr || result.stdout || "Unknown error";
169
+ if (errorMessage.includes("not installed") || errorMessage.includes("not found")) {
170
+ return;
171
+ }
172
+ throw new Error(`Plugin uninstall failed: ${errorMessage.trim()}`);
173
+ }
174
+ }
175
+
176
+ export {
177
+ claudePluginInstall,
178
+ isClaudeCLIAvailable,
179
+ claudePluginMarketplaceExists,
180
+ claudePluginMarketplaceAdd,
181
+ claudePluginUninstall
182
+ };
183
+ //# sourceMappingURL=chunk-EOJTMX7T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/utils/exec.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport { getErrorMessage } from \"./errors\";\nimport { warn } from \"./logger\";\n\n// Argument length limits to prevent oversized CLI arguments\nconst MAX_PLUGIN_PATH_LENGTH = 1024;\nconst MAX_PLUGIN_NAME_LENGTH = 256;\nconst MAX_MARKETPLACE_SOURCE_LENGTH = 1024;\n\n// Marketplace/plugin names: alphanumeric, dashes, underscores, dots, @\nconst SAFE_NAME_PATTERN = /^[a-zA-Z0-9._@/-]+$/;\n\n// Plugin path/ref: alphanumeric, dashes, underscores, dots, slashes, @, colons (for marketplace refs like skill@marketplace)\nconst SAFE_PLUGIN_PATH_PATTERN = /^[a-zA-Z0-9._@/:~-]+$/;\n\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHAR_PATTERN = /[\\x00-\\x08\\x0E-\\x1F\\x7F]/u;\n\nfunction validatePluginPath(pluginPath: string): void {\n if (!pluginPath || pluginPath.trim().length === 0) {\n throw new Error(\"Plugin path must not be empty.\");\n }\n\n if (pluginPath.length > MAX_PLUGIN_PATH_LENGTH) {\n throw new Error(\n `Plugin path is too long (${pluginPath.length} characters, max ${MAX_PLUGIN_PATH_LENGTH}).`,\n );\n }\n\n if (CONTROL_CHAR_PATTERN.test(pluginPath)) {\n throw new Error(\"Plugin path contains invalid control characters.\");\n }\n\n if (!SAFE_PLUGIN_PATH_PATTERN.test(pluginPath)) {\n throw new Error(\n `Plugin path contains invalid characters: \"${pluginPath}\"\\n` +\n \"Plugin paths may only contain alphanumeric characters, dashes, underscores, dots, slashes, @, and colons.\",\n );\n }\n}\n\nfunction validateMarketplaceSource(source: string): void {\n if (!source || source.trim().length === 0) {\n throw new Error(\"Marketplace source must not be empty.\");\n }\n\n if (source.length > MAX_MARKETPLACE_SOURCE_LENGTH) {\n throw new Error(\n `Marketplace source is too long (${source.length} characters, max ${MAX_MARKETPLACE_SOURCE_LENGTH}).`,\n );\n }\n\n if (CONTROL_CHAR_PATTERN.test(source)) {\n throw new Error(\"Marketplace source contains invalid control characters.\");\n }\n\n if (!SAFE_PLUGIN_PATH_PATTERN.test(source)) {\n throw new Error(\n `Marketplace source contains invalid characters: \"${source}\"\\n` +\n \"Source may only contain alphanumeric characters, dashes, underscores, dots, slashes, @, and colons.\",\n );\n }\n}\n\nfunction validatePluginName(pluginName: string): void {\n if (!pluginName || pluginName.trim().length === 0) {\n throw new Error(\"Plugin name must not be empty.\");\n }\n\n if (pluginName.length > MAX_PLUGIN_NAME_LENGTH) {\n throw new Error(\n `Plugin name is too long (${pluginName.length} characters, max ${MAX_PLUGIN_NAME_LENGTH}).`,\n );\n }\n\n if (CONTROL_CHAR_PATTERN.test(pluginName)) {\n throw new Error(\"Plugin name contains invalid control characters.\");\n }\n\n if (!SAFE_NAME_PATTERN.test(pluginName)) {\n throw new Error(\n `Plugin name contains invalid characters: \"${pluginName}\"\\n` +\n \"Names may only contain alphanumeric characters, dashes, underscores, dots, @, and slashes.\",\n );\n }\n}\n\nexport type ExecResult = {\n stdout: string;\n stderr: string;\n exitCode: number;\n};\n\nexport async function execCommand(\n command: string,\n args: string[],\n options?: { cwd?: string; env?: NodeJS.ProcessEnv },\n): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args, {\n cwd: options?.cwd,\n env: { ...process.env, ...options?.env },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout.on(\"data\", (data) => {\n stdout += data.toString();\n });\n\n proc.stderr.on(\"data\", (data) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code) => {\n resolve({\n stdout,\n stderr,\n exitCode: code ?? 1,\n });\n });\n\n proc.on(\"error\", (err) => {\n reject(err);\n });\n });\n}\n\nexport async function claudePluginInstall(\n pluginPath: string,\n scope: \"project\" | \"user\",\n projectDir: string,\n): Promise<void> {\n validatePluginPath(pluginPath);\n\n const args = [\"plugin\", \"install\", pluginPath, \"--scope\", scope];\n const result = await execCommand(\"claude\", args, { cwd: projectDir });\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n throw new Error(`Plugin installation failed: ${errorMessage.trim()}`);\n }\n}\n\nexport async function isClaudeCLIAvailable(): Promise<boolean> {\n try {\n const result = await execCommand(\"claude\", [\"--version\"], {});\n return result.exitCode === 0;\n } catch {\n return false;\n }\n}\n\nexport type MarketplaceInfo = {\n name: string;\n source: string;\n repo?: string;\n path?: string;\n};\n\nexport async function claudePluginMarketplaceList(): Promise<MarketplaceInfo[]> {\n try {\n const result = await execCommand(\"claude\", [\"plugin\", \"marketplace\", \"list\", \"--json\"], {});\n\n if (result.exitCode !== 0) {\n return [];\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(result.stdout);\n } catch {\n warn(\"Failed to parse marketplace list output as JSON\");\n return [];\n }\n\n if (!Array.isArray(parsed)) {\n warn(\"Unexpected marketplace list format — expected an array\");\n return [];\n }\n\n return parsed as MarketplaceInfo[];\n } catch {\n return [];\n }\n}\n\nexport async function claudePluginMarketplaceExists(name: string): Promise<boolean> {\n const marketplaces = await claudePluginMarketplaceList();\n return marketplaces.some((m) => m.name === name);\n}\n\nexport async function claudePluginMarketplaceAdd(source: string): Promise<void> {\n validateMarketplaceSource(source);\n\n const args = [\"plugin\", \"marketplace\", \"add\", source];\n let result;\n try {\n result = await execCommand(\"claude\", args, {});\n } catch (err) {\n throw new Error(`Failed to add marketplace: ${getErrorMessage(err)}`);\n }\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n if (errorMessage.includes(\"already installed\")) {\n return;\n }\n throw new Error(`Failed to add marketplace: ${errorMessage.trim()}`);\n }\n}\n\nexport async function claudePluginUninstall(\n pluginName: string,\n scope: \"project\" | \"user\",\n projectDir: string,\n): Promise<void> {\n validatePluginName(pluginName);\n\n const args = [\"plugin\", \"uninstall\", pluginName, \"--scope\", scope];\n const result = await execCommand(\"claude\", args, { cwd: projectDir });\n\n if (result.exitCode !== 0) {\n const errorMessage = result.stderr || result.stdout || \"Unknown error\";\n // Ignore \"not installed\" errors - plugin may already be removed\n if (errorMessage.includes(\"not installed\") || errorMessage.includes(\"not found\")) {\n return;\n }\n throw new Error(`Plugin uninstall failed: ${errorMessage.trim()}`);\n }\n}\n"],"mappings":"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","names":[]}
@@ -16,7 +16,7 @@ import {
16
16
  addSource,
17
17
  getSourceSummary,
18
18
  removeSource
19
- } from "./chunk-ABK6CSWI.js";
19
+ } from "./chunk-QUL7R35E.js";
20
20
  import {
21
21
  getErrorMessage
22
22
  } from "./chunk-5WIHSJRO.js";
@@ -220,7 +220,7 @@ var StepSettings = ({ projectDir, onClose }) => {
220
220
  /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
221
221
  "Plugins: ",
222
222
  summary?.pluginSkillCount ?? 0,
223
- " in .claude/plugins/"
223
+ " from installed plugins"
224
224
  ] })
225
225
  ] }),
226
226
  /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: addModal.isOpen ? "ENTER submit ESC cancel" : "A add DEL remove ESC close" }) })
@@ -230,4 +230,4 @@ var StepSettings = ({ projectDir, onClose }) => {
230
230
  export {
231
231
  StepSettings
232
232
  };
233
- //# sourceMappingURL=chunk-Z6UK6MI2.js.map
233
+ //# sourceMappingURL=chunk-FFZSN5C5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/components/wizard/step-settings.tsx","../src/cli/components/hooks/use-source-operations.ts"],"sourcesContent":["import React, { useState, useEffect, useCallback } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { ViewTitle } from \"./view-title.js\";\nimport { getSourceSummary, type SourceSummary } from \"../../lib/configuration/source-manager.js\";\nimport { DEFAULT_SOURCE } from \"../../lib/configuration/config.js\";\nimport { useKeyboardNavigation } from \"../hooks/use-keyboard-navigation.js\";\nimport { useModalState } from \"../hooks/use-modal-state.js\";\nimport { useSourceOperations } from \"../hooks/use-source-operations.js\";\nimport { useTextInput } from \"../hooks/use-text-input.js\";\n\nconst DEFAULT_SOURCE_NAME = \"public\";\n\nexport type StepSettingsProps = {\n projectDir: string;\n onClose: () => void;\n};\n\nexport const StepSettings: React.FC<StepSettingsProps> = ({ projectDir, onClose }) => {\n const [summary, setSummary] = useState<SourceSummary | null>(null);\n const addModal = useModalState();\n const {\n value: addSourceInput,\n setValue: setAddSourceInput,\n handleInput: handleTextInput,\n } = useTextInput(\"\");\n const [isLoading, setIsLoading] = useState(true);\n\n const loadSummary = useCallback(async () => {\n try {\n const result = await getSourceSummary(projectDir);\n setSummary(result);\n } catch {\n setSummary({\n sources: [{ name: DEFAULT_SOURCE_NAME, url: DEFAULT_SOURCE, enabled: true }],\n localSkillCount: 0,\n pluginSkillCount: 0,\n });\n }\n setIsLoading(false);\n }, [projectDir]);\n\n useEffect(() => {\n void loadSummary();\n }, [loadSummary]);\n\n const { handleAdd, handleRemove, statusMessage, clearStatus } = useSourceOperations(\n projectDir,\n loadSummary,\n );\n\n const sourceCount = summary?.sources.length ?? 0;\n\n const { focusedIndex, setFocusedIndex } = useKeyboardNavigation(\n sourceCount,\n { onEscape: onClose },\n { wrap: false, vimKeys: false, active: !addModal.isOpen },\n );\n\n useInput((input, key) => {\n if (statusMessage) {\n clearStatus();\n }\n\n if (addModal.isOpen) {\n if (key.escape) {\n addModal.close();\n setAddSourceInput(\"\");\n return;\n }\n\n if (key.return) {\n if (addSourceInput.trim()) {\n addModal.close();\n setAddSourceInput(\"\");\n void handleAdd(addSourceInput.trim());\n }\n return;\n }\n\n handleTextInput(input, key);\n return;\n }\n\n // Non-adding mode: up/down/escape handled by useKeyboardNavigation hook\n\n if (key.return) {\n // Toggle enabled/disabled is a placeholder for future enabledSources store integration\n return;\n }\n\n if (key.backspace || key.delete) {\n if (summary?.sources[focusedIndex]) {\n const source = summary.sources[focusedIndex];\n if (source.name !== DEFAULT_SOURCE_NAME) {\n void handleRemove(source.name).then((success) => {\n if (success) {\n setFocusedIndex((prev) => Math.max(0, prev - 1));\n }\n });\n }\n }\n return;\n }\n\n if (input === \"a\" || input === \"A\") {\n addModal.open(true);\n setAddSourceInput(\"\");\n }\n });\n\n if (isLoading) {\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <ViewTitle>Skill Sources</ViewTitle>\n <Text dimColor>Loading sources...</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\" paddingX={2}>\n <ViewTitle>Skill Sources</ViewTitle>\n <Box marginTop={1} />\n\n <Text bold>Configured marketplaces:</Text>\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={CLI_COLORS.NEUTRAL}\n paddingX={1}\n marginTop={1}\n >\n {summary?.sources.map((source, index) => {\n const isFocused = index === focusedIndex && !addModal.isOpen;\n const isDefault = source.name === DEFAULT_SOURCE_NAME;\n const checkmark = source.enabled ? \"\\u2713\" : \" \";\n const displayName = isDefault ? \"Public\" : source.name;\n const suffix = isDefault ? \" (default)\" : \"\";\n\n return (\n <Box key={source.name}>\n <Text color={isFocused ? CLI_COLORS.PRIMARY : undefined} bold={isFocused}>\n {isFocused ? \">\" : \" \"} {checkmark} {displayName}\n </Text>\n <Text dimColor>\n {\" \"}\n {source.url}\n {suffix}\n </Text>\n </Box>\n );\n })}\n </Box>\n\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={addModal.isOpen ? CLI_COLORS.PRIMARY : CLI_COLORS.NEUTRAL}\n paddingX={1}\n marginTop={1}\n >\n <Text color={addModal.isOpen ? CLI_COLORS.PRIMARY : undefined}>\n + Add source: {addModal.isOpen ? addSourceInput : \"\"}\n {addModal.isOpen ? \"\\u2588\" : \"\"}\n </Text>\n </Box>\n\n {statusMessage && (\n <Box marginTop={1}>\n <Text color={statusMessage.color as \"red\" | \"green\"}>{statusMessage.text}</Text>\n </Box>\n )}\n\n <Box marginTop={1} flexDirection=\"column\">\n <Text dimColor>Local skills: {summary?.localSkillCount ?? 0} in .claude/skills/</Text>\n <Text dimColor>Plugins: {summary?.pluginSkillCount ?? 0} from installed plugins</Text>\n </Box>\n\n <Box marginTop={1}>\n <Text dimColor>\n {addModal.isOpen ? \"ENTER submit ESC cancel\" : \"A add DEL remove ESC close\"}\n </Text>\n </Box>\n </Box>\n );\n};\n","import { useState, useCallback } from \"react\";\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { getErrorMessage } from \"../../utils/errors.js\";\nimport { addSource, removeSource } from \"../../lib/configuration/source-manager.js\";\n\ntype StatusMessage = { text: string; color: string } | null;\n\ntype UseSourceOperationsResult = {\n handleAdd: (url: string) => Promise<void>;\n handleRemove: (name: string) => Promise<boolean>;\n statusMessage: StatusMessage;\n clearStatus: () => void;\n};\n\nexport function useSourceOperations(\n projectDir: string,\n onReload: () => Promise<void>,\n): UseSourceOperationsResult {\n const [statusMessage, setStatusMessage] = useState<StatusMessage>(null);\n\n const handleAdd = useCallback(\n async (url: string) => {\n try {\n const result = await addSource(projectDir, url);\n setStatusMessage({\n text: `Added \"${result.name}\" (${result.skillCount} skills)`,\n color: CLI_COLORS.SUCCESS,\n });\n await onReload();\n } catch (error) {\n const message = getErrorMessage(error);\n setStatusMessage({ text: `Failed to add source: ${message}`, color: CLI_COLORS.ERROR });\n }\n },\n [projectDir, onReload],\n );\n\n const handleRemove = useCallback(\n async (name: string): Promise<boolean> => {\n try {\n await removeSource(projectDir, name);\n setStatusMessage({ text: `Removed \"${name}\"`, color: CLI_COLORS.SUCCESS });\n await onReload();\n return true;\n } catch (error) {\n const message = getErrorMessage(error);\n setStatusMessage({ text: `Failed to remove: ${message}`, color: CLI_COLORS.ERROR });\n return false;\n }\n },\n [projectDir, onReload],\n );\n\n const clearStatus = useCallback(() => {\n setStatusMessage(null);\n }, []);\n\n return { handleAdd, handleRemove, statusMessage, clearStatus };\n}\n"],"mappings":"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","names":["useState","useCallback","useState","useCallback"]}
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  StackSelection
4
- } from "./chunk-HJS2FWLA.js";
4
+ } from "./chunk-MYLRA7NI.js";
5
5
  import {
6
6
  DomainSelection
7
- } from "./chunk-DALU3Y3U.js";
7
+ } from "./chunk-WXW6SQ5K.js";
8
8
  import {
9
9
  useWizardStore
10
- } from "./chunk-DDNK5UNE.js";
10
+ } from "./chunk-WWLEF4MQ.js";
11
11
  import {
12
12
  init_esm_shims
13
13
  } from "./chunk-AWKZ5BDL.js";
@@ -26,4 +26,4 @@ var StepStack = ({ matrix }) => {
26
26
  export {
27
27
  StepStack
28
28
  };
29
- //# sourceMappingURL=chunk-VBOBVQQW.js.map
29
+ //# sourceMappingURL=chunk-HMZPUOWU.js.map
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-LY5HM34M.js";
8
8
  import {
9
9
  useWizardStore
10
- } from "./chunk-DDNK5UNE.js";
10
+ } from "./chunk-WWLEF4MQ.js";
11
11
  import {
12
12
  init_esm_shims
13
13
  } from "./chunk-AWKZ5BDL.js";
@@ -72,4 +72,4 @@ var StackSelection = ({ matrix }) => {
72
72
  export {
73
73
  StackSelection
74
74
  };
75
- //# sourceMappingURL=chunk-HJS2FWLA.js.map
75
+ //# sourceMappingURL=chunk-MYLRA7NI.js.map
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  getAvailableSkills,
13
13
  resolveAlias
14
- } from "./chunk-ABK6CSWI.js";
14
+ } from "./chunk-QUL7R35E.js";
15
15
  import {
16
16
  CLI_COLORS,
17
17
  UI_SYMBOLS
@@ -280,4 +280,4 @@ export {
280
280
  getSkillDisplayLabel,
281
281
  StepBuild
282
282
  };
283
- //# sourceMappingURL=chunk-PRY6MLN3.js.map
283
+ //# sourceMappingURL=chunk-QR2ONHDW.js.map