@claude-collective/cli 0.2.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +178 -0
- package/README.md +1 -1
- package/dist/chunk-3HBTELJN.js +114 -0
- package/dist/chunk-3HBTELJN.js.map +1 -0
- package/dist/chunk-3ZCB5K33.js +54 -0
- package/dist/chunk-3ZCB5K33.js.map +1 -0
- package/dist/chunk-66UDJBF6.js +96 -0
- package/dist/chunk-66UDJBF6.js.map +1 -0
- package/dist/chunk-6LS7XO3H.js +31 -0
- package/dist/chunk-6LS7XO3H.js.map +1 -0
- package/dist/chunk-A3J6IAXK.js +57 -0
- package/dist/chunk-A3J6IAXK.js.map +1 -0
- package/dist/chunk-A65SBAAJ.js +69 -0
- package/dist/chunk-A65SBAAJ.js.map +1 -0
- package/dist/chunk-ALEPJ6YN.js +80 -0
- package/dist/chunk-ALEPJ6YN.js.map +1 -0
- package/dist/chunk-C4ZTIYFR.js +84 -0
- package/dist/chunk-C4ZTIYFR.js.map +1 -0
- package/dist/chunk-CIY5UBRB.js +453 -0
- package/dist/chunk-CIY5UBRB.js.map +1 -0
- package/dist/chunk-DHET7RCE.js +50 -0
- package/dist/chunk-DHET7RCE.js.map +1 -0
- package/dist/chunk-DHFFRMF6.js +31 -0
- package/dist/chunk-DHFFRMF6.js.map +1 -0
- package/dist/chunk-DKGL77IY.js +307 -0
- package/dist/chunk-DKGL77IY.js.map +1 -0
- package/dist/chunk-ED73HCW2.js +315 -0
- package/dist/chunk-ED73HCW2.js.map +1 -0
- package/dist/chunk-FNOYEXUE.js +308 -0
- package/dist/chunk-FNOYEXUE.js.map +1 -0
- package/dist/chunk-G2FBJOZG.js +141 -0
- package/dist/chunk-G2FBJOZG.js.map +1 -0
- package/dist/chunk-HNDT5QRB.js +120 -0
- package/dist/chunk-HNDT5QRB.js.map +1 -0
- package/dist/chunk-K7PTOVX4.js +158 -0
- package/dist/chunk-K7PTOVX4.js.map +1 -0
- package/dist/chunk-LQTST4WY.js +91 -0
- package/dist/chunk-LQTST4WY.js.map +1 -0
- package/dist/chunk-LVKRVFYR.js +54 -0
- package/dist/chunk-LVKRVFYR.js.map +1 -0
- package/dist/chunk-M7YCPFIX.js +108 -0
- package/dist/chunk-M7YCPFIX.js.map +1 -0
- package/dist/chunk-MJSFR562.js +57 -0
- package/dist/chunk-MJSFR562.js.map +1 -0
- package/dist/chunk-MMDXNZPF.js +69 -0
- package/dist/chunk-MMDXNZPF.js.map +1 -0
- package/dist/chunk-MYAVQ23U.js +356 -0
- package/dist/chunk-MYAVQ23U.js.map +1 -0
- package/dist/chunk-NGBFJJ7Q.js +124 -0
- package/dist/chunk-NGBFJJ7Q.js.map +1 -0
- package/dist/chunk-OLBOTK3O.js +64 -0
- package/dist/chunk-OLBOTK3O.js.map +1 -0
- package/dist/chunk-PPNTD5LO.js +330 -0
- package/dist/chunk-PPNTD5LO.js.map +1 -0
- package/dist/chunk-Q2LH2DAB.js +392 -0
- package/dist/chunk-Q2LH2DAB.js.map +1 -0
- package/dist/chunk-Q6DR5QUH.js +547 -0
- package/dist/chunk-Q6DR5QUH.js.map +1 -0
- package/dist/chunk-QESUUPOE.js +241 -0
- package/dist/chunk-QESUUPOE.js.map +1 -0
- package/dist/chunk-QGGSLMO3.js +607 -0
- package/dist/chunk-QGGSLMO3.js.map +1 -0
- package/dist/chunk-SEBPPFUW.js +478 -0
- package/dist/chunk-SEBPPFUW.js.map +1 -0
- package/dist/chunk-SYQ7R2JO.js +95 -0
- package/dist/chunk-SYQ7R2JO.js.map +1 -0
- package/dist/chunk-TOPAIL5W.js +22 -0
- package/dist/chunk-TOPAIL5W.js.map +1 -0
- package/dist/chunk-U4VYHKPM.js +110 -0
- package/dist/chunk-U4VYHKPM.js.map +1 -0
- package/dist/chunk-UOWHJ6BE.js +83 -0
- package/dist/chunk-UOWHJ6BE.js.map +1 -0
- package/dist/chunk-XKEG3SCV.js +86 -0
- package/dist/chunk-XKEG3SCV.js.map +1 -0
- package/dist/chunk-XY3XDVMI.js +15599 -0
- package/dist/chunk-XY3XDVMI.js.map +1 -0
- package/dist/chunk-Y3V43XCU.js +76 -0
- package/dist/chunk-Y3V43XCU.js.map +1 -0
- package/dist/chunk-YKXBGCFD.js +129 -0
- package/dist/chunk-YKXBGCFD.js.map +1 -0
- package/dist/cli-v2/defaults/agent-mappings.yaml +185 -0
- package/dist/commands/build/marketplace.js +254 -0
- package/dist/commands/build/marketplace.js.map +1 -0
- package/dist/commands/build/plugins.js +324 -0
- package/dist/commands/build/plugins.js.map +1 -0
- package/dist/commands/build/stack.js +169 -0
- package/dist/commands/build/stack.js.map +1 -0
- package/dist/commands/compile.js +461 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/config/get.js +60 -0
- package/dist/commands/config/get.js.map +1 -0
- package/dist/commands/config/index.js +22 -0
- package/dist/commands/config/index.js.map +1 -0
- package/dist/commands/config/path.js +35 -0
- package/dist/commands/config/path.js.map +1 -0
- package/dist/commands/config/set-project.js +61 -0
- package/dist/commands/config/set-project.js.map +1 -0
- package/dist/commands/config/set.js +60 -0
- package/dist/commands/config/set.js.map +1 -0
- package/dist/commands/config/show.js +13 -0
- package/dist/commands/config/show.js.map +1 -0
- package/dist/commands/config/unset-project.js +57 -0
- package/dist/commands/config/unset-project.js.map +1 -0
- package/dist/commands/config/unset.js +56 -0
- package/dist/commands/config/unset.js.map +1 -0
- package/dist/commands/diff.js +755 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.js +413 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit.js +254 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/eject.js +208 -0
- package/dist/commands/eject.js.map +1 -0
- package/dist/commands/info.js +205 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.js +915 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.js +44 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new/agent.js +230 -0
- package/dist/commands/new/agent.js.map +1 -0
- package/dist/commands/new/skill.js +204 -0
- package/dist/commands/new/skill.js.map +1 -0
- package/dist/commands/outdated.js +242 -0
- package/dist/commands/outdated.js.map +1 -0
- package/dist/commands/search.js +115 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/test-imports.js +92 -0
- package/dist/commands/test-imports.js.map +1 -0
- package/dist/commands/uninstall.js +309 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +428 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/validate.js +375 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/commands/version/bump.js +95 -0
- package/dist/commands/version/bump.js.map +1 -0
- package/dist/commands/version/index.js +70 -0
- package/dist/commands/version/index.js.map +1 -0
- package/dist/commands/version/set.js +101 -0
- package/dist/commands/version/set.js.map +1 -0
- package/dist/commands/version/show.js +70 -0
- package/dist/commands/version/show.js.map +1 -0
- package/dist/components/common/confirm.js +9 -0
- package/dist/components/common/confirm.js.map +1 -0
- package/dist/components/common/message.js +24 -0
- package/dist/components/common/message.js.map +1 -0
- package/dist/components/common/spinner.js +14 -0
- package/dist/components/common/spinner.js.map +1 -0
- package/dist/components/wizard/category-grid.js +9 -0
- package/dist/components/wizard/category-grid.js.map +1 -0
- package/dist/components/wizard/category-grid.test.js +728 -0
- package/dist/components/wizard/category-grid.test.js.map +1 -0
- package/dist/components/wizard/section-progress.js +9 -0
- package/dist/components/wizard/section-progress.js.map +1 -0
- package/dist/components/wizard/section-progress.test.js +281 -0
- package/dist/components/wizard/section-progress.test.js.map +1 -0
- package/dist/components/wizard/step-approach.js +11 -0
- package/dist/components/wizard/step-approach.js.map +1 -0
- package/dist/components/wizard/step-build.js +15 -0
- package/dist/components/wizard/step-build.js.map +1 -0
- package/dist/components/wizard/step-build.test.js +729 -0
- package/dist/components/wizard/step-build.test.js.map +1 -0
- package/dist/components/wizard/step-confirm.js +9 -0
- package/dist/components/wizard/step-confirm.js.map +1 -0
- package/dist/components/wizard/step-refine.js +9 -0
- package/dist/components/wizard/step-refine.js.map +1 -0
- package/dist/components/wizard/step-refine.test.js +235 -0
- package/dist/components/wizard/step-refine.test.js.map +1 -0
- package/dist/components/wizard/step-stack-options.js +11 -0
- package/dist/components/wizard/step-stack-options.js.map +1 -0
- package/dist/components/wizard/step-stack.js +11 -0
- package/dist/components/wizard/step-stack.js.map +1 -0
- package/dist/components/wizard/wizard-tabs.js +11 -0
- package/dist/components/wizard/wizard-tabs.js.map +1 -0
- package/dist/components/wizard/wizard.js +20 -0
- package/dist/components/wizard/wizard.js.map +1 -0
- package/dist/hooks/init.js +41 -0
- package/dist/hooks/init.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/magic-string.es-RGXYGAW3.js +1316 -0
- package/dist/magic-string.es-RGXYGAW3.js.map +1 -0
- package/dist/stores/wizard-store.js +10 -0
- package/dist/stores/wizard-store.js.map +1 -0
- package/dist/stores/wizard-store.test.js +405 -0
- package/dist/stores/wizard-store.test.js.map +1 -0
- package/package.json +44 -25
- package/dist/cli/index.js +0 -6314
- package/dist/cli/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-v2/lib/project-config.ts"],"sourcesContent":["import path from \"path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { readFile, fileExists } from \"../utils/fs\";\nimport { verbose } from \"../utils/logger\";\nimport type {\n ProjectConfig,\n StackConfig,\n SkillAssignment,\n SkillEntry,\n AgentSkillConfig,\n ValidationResult,\n CustomAgentConfig,\n} from \"../../types\";\n\nconst CONFIG_PATH = \".claude/config.yaml\";\n\nexport interface LoadedProjectConfig {\n config: ProjectConfig;\n configPath: string;\n /** true if was StackConfig format (legacy) */\n isLegacy: boolean;\n}\n\n/**\n * Load project config from .claude/config.yaml\n */\nexport async function loadProjectConfig(\n projectDir: string,\n): Promise<LoadedProjectConfig | null> {\n const configPath = path.join(projectDir, CONFIG_PATH);\n\n if (!(await fileExists(configPath))) {\n verbose(`Project config not found at ${configPath}`);\n return null;\n }\n\n try {\n const content = await readFile(configPath);\n const parsed = parseYaml(content);\n\n if (!parsed || typeof parsed !== \"object\") {\n verbose(`Invalid project config structure at ${configPath}`);\n return null;\n }\n\n // Detect if this is legacy StackConfig format\n const isLegacy = isLegacyStackConfig(parsed);\n\n if (isLegacy) {\n verbose(`Detected legacy StackConfig format at ${configPath}`);\n const normalized = normalizeStackConfig(parsed as StackConfig);\n return {\n config: normalized,\n configPath,\n isLegacy: true,\n };\n }\n\n verbose(`Loaded project config from ${configPath}`);\n return {\n config: parsed as ProjectConfig,\n configPath,\n isLegacy: false,\n };\n } catch (error) {\n verbose(`Failed to parse project config: ${error}`);\n return null;\n }\n}\n\n/**\n * Check if a config object is in legacy StackConfig format.\n * Detection logic:\n * - If version is semver (e.g., \"1.0.0\") -> legacy\n * - If version is \"1\" -> new format\n * - If has 'id' field -> legacy\n * - If has 'created' or 'updated' fields -> legacy\n */\nexport function isLegacyStackConfig(config: unknown): boolean {\n if (!config || typeof config !== \"object\") return false;\n\n const obj = config as Record<string, unknown>;\n\n // If version looks like semver (x.y.z), it's legacy\n if (typeof obj.version === \"string\" && obj.version.includes(\".\")) {\n return true;\n }\n\n // If has legacy-only fields, it's legacy\n if (\n obj.id !== undefined ||\n obj.created !== undefined ||\n obj.updated !== undefined\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Normalize StackConfig to ProjectConfig (for backward compatibility)\n */\nexport function normalizeStackConfig(stackConfig: StackConfig): ProjectConfig {\n const config: ProjectConfig = {\n name: stackConfig.name,\n agents: stackConfig.agents,\n };\n\n // Copy optional fields\n if (stackConfig.description) {\n config.description = stackConfig.description;\n }\n\n if (stackConfig.skills && stackConfig.skills.length > 0) {\n config.skills = stackConfig.skills;\n }\n\n if (stackConfig.agent_skills) {\n // StackConfig agent_skills is always categorized format\n config.agent_skills = stackConfig.agent_skills;\n }\n\n if (stackConfig.hooks) {\n config.hooks = stackConfig.hooks;\n }\n\n if (stackConfig.author) {\n config.author = stackConfig.author;\n }\n\n if (stackConfig.framework) {\n config.framework = stackConfig.framework;\n }\n\n if (stackConfig.philosophy) {\n config.philosophy = stackConfig.philosophy;\n }\n\n if (stackConfig.principles && stackConfig.principles.length > 0) {\n config.principles = stackConfig.principles;\n }\n\n if (stackConfig.tags && stackConfig.tags.length > 0) {\n config.tags = stackConfig.tags;\n }\n\n return config;\n}\n\n/**\n * Validate project config structure.\n * Returns validation result with errors and warnings.\n */\nexport function validateProjectConfig(config: unknown): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n if (!config || typeof config !== \"object\") {\n return { valid: false, errors: [\"Config must be an object\"], warnings: [] };\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: name\n if (!c.name || typeof c.name !== \"string\") {\n errors.push(\"name is required and must be a string\");\n }\n\n // Required: agents (for compilation)\n if (!c.agents || !Array.isArray(c.agents)) {\n errors.push(\"agents is required and must be an array\");\n } else {\n for (const agent of c.agents) {\n if (typeof agent !== \"string\") {\n errors.push(`agents must contain strings, found: ${typeof agent}`);\n }\n }\n }\n\n // Optional: version\n if (c.version !== undefined && c.version !== \"1\") {\n errors.push('version must be \"1\" (or omitted for default)');\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(\"skills must be an array\");\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(skillError);\n }\n }\n }\n }\n\n // Optional: agent_skills\n if (c.agent_skills !== undefined) {\n if (typeof c.agent_skills !== \"object\" || c.agent_skills === null) {\n errors.push(\"agent_skills must be an object\");\n } else {\n for (const [agentName, agentSkills] of Object.entries(c.agent_skills)) {\n const agentSkillsError = validateAgentSkillConfig(\n agentName,\n agentSkills,\n );\n if (agentSkillsError) {\n errors.push(agentSkillsError);\n }\n }\n }\n }\n\n // Optional: preload_patterns\n if (c.preload_patterns !== undefined) {\n if (typeof c.preload_patterns !== \"object\" || c.preload_patterns === null) {\n errors.push(\"preload_patterns must be an object\");\n } else {\n for (const [agentName, patterns] of Object.entries(c.preload_patterns)) {\n if (!Array.isArray(patterns)) {\n errors.push(\n `preload_patterns.${agentName} must be an array of strings`,\n );\n } else {\n for (const pattern of patterns) {\n if (typeof pattern !== \"string\") {\n errors.push(\n `preload_patterns.${agentName} must contain only strings`,\n );\n break;\n }\n }\n }\n }\n }\n }\n\n // Optional: custom_agents\n if (c.custom_agents !== undefined) {\n const customAgentsErrors = validateCustomAgents(c.custom_agents, c.agents);\n errors.push(...customAgentsErrors);\n }\n\n // Warnings for deprecated patterns\n if (c.id !== undefined) {\n warnings.push(\"id field is deprecated in project config\");\n }\n if (c.created !== undefined) {\n warnings.push(\"created field is deprecated in project config\");\n }\n if (c.updated !== undefined) {\n warnings.push(\"updated field is deprecated in project config\");\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/** Maximum number of custom agents allowed per project */\nconst MAX_CUSTOM_AGENTS = 20;\n\n/** Valid model values for custom agents */\nconst VALID_MODELS = [\"sonnet\", \"opus\", \"haiku\", \"inherit\"];\n\n/** Valid permission modes for custom agents */\nconst VALID_PERMISSION_MODES = [\n \"default\",\n \"acceptEdits\",\n \"dontAsk\",\n \"bypassPermissions\",\n \"plan\",\n \"delegate\",\n];\n\n/**\n * Validate custom_agents section of config\n */\nfunction validateCustomAgents(\n customAgents: unknown,\n agents: unknown,\n): string[] {\n const errors: string[] = [];\n\n if (typeof customAgents !== \"object\" || customAgents === null) {\n errors.push(\"custom_agents must be an object\");\n return errors;\n }\n\n const customAgentEntries = Object.entries(customAgents);\n\n // Check maximum limit\n if (customAgentEntries.length > MAX_CUSTOM_AGENTS) {\n errors.push(\n `custom_agents cannot exceed ${MAX_CUSTOM_AGENTS} agents (found ${customAgentEntries.length})`,\n );\n }\n\n // Collect custom agent names for circular reference check\n const customAgentNames = new Set(customAgentEntries.map(([name]) => name));\n\n for (const [agentName, agentConfig] of customAgentEntries) {\n const agentErrors = validateCustomAgentConfig(\n agentName,\n agentConfig,\n customAgentNames,\n );\n errors.push(...agentErrors);\n }\n\n return errors;\n}\n\n/**\n * Validate a single custom agent configuration\n */\nfunction validateCustomAgentConfig(\n agentName: string,\n config: unknown,\n customAgentNames: Set<string>,\n): string[] {\n const errors: string[] = [];\n const prefix = `custom_agents.${agentName}`;\n\n if (typeof config !== \"object\" || config === null) {\n errors.push(`${prefix} must be an object`);\n return errors;\n }\n\n const c = config as Record<string, unknown>;\n\n // Required: title\n if (!c.title || typeof c.title !== \"string\") {\n errors.push(`${prefix}.title is required and must be a string`);\n }\n\n // Required: description\n if (!c.description || typeof c.description !== \"string\") {\n errors.push(`${prefix}.description is required and must be a string`);\n }\n\n // Optional: extends\n if (c.extends !== undefined) {\n if (typeof c.extends !== \"string\") {\n errors.push(`${prefix}.extends must be a string`);\n } else if (customAgentNames.has(c.extends)) {\n // Custom agents cannot extend other custom agents\n errors.push(\n `${prefix}.extends cannot reference another custom agent \"${c.extends}\"`,\n );\n }\n }\n\n // Optional: model\n if (c.model !== undefined) {\n if (typeof c.model !== \"string\" || !VALID_MODELS.includes(c.model)) {\n errors.push(`${prefix}.model must be one of: ${VALID_MODELS.join(\", \")}`);\n }\n }\n\n // Optional: tools\n if (c.tools !== undefined) {\n if (!Array.isArray(c.tools)) {\n errors.push(`${prefix}.tools must be an array`);\n } else {\n for (const tool of c.tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: disallowed_tools\n if (c.disallowed_tools !== undefined) {\n if (!Array.isArray(c.disallowed_tools)) {\n errors.push(`${prefix}.disallowed_tools must be an array`);\n } else {\n for (const tool of c.disallowed_tools) {\n if (typeof tool !== \"string\") {\n errors.push(`${prefix}.disallowed_tools must contain only strings`);\n break;\n }\n }\n }\n }\n\n // Optional: permission_mode\n if (c.permission_mode !== undefined) {\n if (\n typeof c.permission_mode !== \"string\" ||\n !VALID_PERMISSION_MODES.includes(c.permission_mode)\n ) {\n errors.push(\n `${prefix}.permission_mode must be one of: ${VALID_PERMISSION_MODES.join(\", \")}`,\n );\n }\n }\n\n // Optional: skills\n if (c.skills !== undefined) {\n if (!Array.isArray(c.skills)) {\n errors.push(`${prefix}.skills must be an array`);\n } else {\n for (const skill of c.skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n errors.push(`${prefix}.skills: ${skillError}`);\n }\n }\n }\n }\n\n // Optional: hooks\n if (c.hooks !== undefined) {\n if (typeof c.hooks !== \"object\" || c.hooks === null) {\n errors.push(`${prefix}.hooks must be an object`);\n }\n // Note: Detailed hook validation could be added later if needed\n }\n\n return errors;\n}\n\n/**\n * Validate a single skill entry\n */\nfunction validateSkillEntry(skill: unknown): string | null {\n if (typeof skill === \"string\") {\n return null; // String skill IDs are valid\n }\n\n if (typeof skill !== \"object\" || skill === null) {\n return \"skills must be strings or objects\";\n }\n\n const s = skill as Record<string, unknown>;\n\n if (!s.id || typeof s.id !== \"string\") {\n return \"skill object must have an id string\";\n }\n\n if (s.local === true && !s.path) {\n return `local skill \"${s.id}\" must have a path`;\n }\n\n if (s.preloaded !== undefined && typeof s.preloaded !== \"boolean\") {\n return `skill \"${s.id}\" preloaded must be a boolean`;\n }\n\n if (s.local !== undefined && typeof s.local !== \"boolean\") {\n return `skill \"${s.id}\" local must be a boolean`;\n }\n\n if (s.path !== undefined && typeof s.path !== \"string\") {\n return `skill \"${s.id}\" path must be a string`;\n }\n\n return null;\n}\n\n/**\n * Validate agent skill config (can be simple list or categorized)\n */\nfunction validateAgentSkillConfig(\n agentName: string,\n agentSkills: unknown,\n): string | null {\n // Check if it's a simple list (array)\n if (Array.isArray(agentSkills)) {\n for (const skill of agentSkills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}: ${skillError}`;\n }\n }\n return null;\n }\n\n // Check if it's categorized (object with array values)\n if (typeof agentSkills === \"object\" && agentSkills !== null) {\n for (const [category, skills] of Object.entries(agentSkills)) {\n if (!Array.isArray(skills)) {\n return `agent_skills.${agentName}.${category} must be an array`;\n }\n for (const skill of skills) {\n const skillError = validateSkillEntry(skill);\n if (skillError) {\n return `agent_skills.${agentName}.${category}: ${skillError}`;\n }\n }\n }\n return null;\n }\n\n return `agent_skills.${agentName} must be an array or object`;\n}\n\n/**\n * Check if agent_skills value is in simple list format (array)\n */\nexport function isSimpleAgentSkills(value: unknown): value is SkillEntry[] {\n return Array.isArray(value);\n}\n\n/**\n * Normalize a skill entry to SkillAssignment\n */\nexport function normalizeSkillEntry(entry: SkillEntry): SkillAssignment {\n if (typeof entry === \"string\") {\n return { id: entry };\n }\n return entry;\n}\n\n/**\n * Normalize agent_skills to always be categorized format for internal use.\n * Simple lists are placed under an \"uncategorized\" key.\n */\nexport function normalizeAgentSkills(\n agentSkills: Record<string, AgentSkillConfig>,\n): Record<string, Record<string, SkillAssignment[]>> {\n const result: Record<string, Record<string, SkillAssignment[]>> = {};\n\n for (const [agentName, skills] of Object.entries(agentSkills)) {\n if (isSimpleAgentSkills(skills)) {\n // Simple list -> put under \"uncategorized\"\n result[agentName] = {\n uncategorized: skills.map(normalizeSkillEntry),\n };\n } else {\n // Already categorized -> normalize entries\n result[agentName] = {};\n for (const [category, categorySkills] of Object.entries(skills)) {\n result[agentName][category] = categorySkills.map(normalizeSkillEntry);\n }\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
isLocalSource
|
|
4
|
+
} from "./chunk-QESUUPOE.js";
|
|
5
|
+
import {
|
|
6
|
+
CACHE_DIR
|
|
7
|
+
} from "./chunk-A3J6IAXK.js";
|
|
8
|
+
import {
|
|
9
|
+
verbose
|
|
10
|
+
} from "./chunk-TOPAIL5W.js";
|
|
11
|
+
import {
|
|
12
|
+
directoryExists,
|
|
13
|
+
ensureDir
|
|
14
|
+
} from "./chunk-MMDXNZPF.js";
|
|
15
|
+
import {
|
|
16
|
+
init_esm_shims
|
|
17
|
+
} from "./chunk-DHET7RCE.js";
|
|
18
|
+
|
|
19
|
+
// src/cli-v2/lib/source-fetcher.ts
|
|
20
|
+
init_esm_shims();
|
|
21
|
+
import path from "path";
|
|
22
|
+
import { downloadTemplate } from "giget";
|
|
23
|
+
function sanitizeSourceForCache(source) {
|
|
24
|
+
return source.replace(/:/g, "-").replace(/[\/]/g, "-").replace(/--+/g, "-").replace(/^-|-$/g, "");
|
|
25
|
+
}
|
|
26
|
+
function getCacheDir(source) {
|
|
27
|
+
const sanitized = sanitizeSourceForCache(source);
|
|
28
|
+
return path.join(CACHE_DIR, "sources", sanitized);
|
|
29
|
+
}
|
|
30
|
+
async function fetchFromSource(source, options = {}) {
|
|
31
|
+
const { forceRefresh = false, subdir } = options;
|
|
32
|
+
if (isLocalSource(source)) {
|
|
33
|
+
return fetchFromLocalSource(source, subdir);
|
|
34
|
+
}
|
|
35
|
+
return fetchFromRemoteSource(source, { forceRefresh, subdir });
|
|
36
|
+
}
|
|
37
|
+
async function fetchFromLocalSource(source, subdir) {
|
|
38
|
+
const fullPath = subdir ? path.join(source, subdir) : source;
|
|
39
|
+
const absolutePath = path.isAbsolute(fullPath) ? fullPath : path.resolve(process.cwd(), fullPath);
|
|
40
|
+
if (!await directoryExists(absolutePath)) {
|
|
41
|
+
throw new Error(`Local source not found: ${absolutePath}`);
|
|
42
|
+
}
|
|
43
|
+
verbose(`Using local source: ${absolutePath}`);
|
|
44
|
+
return {
|
|
45
|
+
path: absolutePath,
|
|
46
|
+
fromCache: false,
|
|
47
|
+
source
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function fetchFromRemoteSource(source, options) {
|
|
51
|
+
const { forceRefresh = false, subdir } = options;
|
|
52
|
+
const cacheDir = getCacheDir(source);
|
|
53
|
+
const fullSource = subdir ? `${source}/${subdir}` : source;
|
|
54
|
+
verbose(`Fetching from remote: ${fullSource}`);
|
|
55
|
+
verbose(`Cache directory: ${cacheDir}`);
|
|
56
|
+
await ensureDir(path.dirname(cacheDir));
|
|
57
|
+
try {
|
|
58
|
+
const result = await downloadTemplate(fullSource, {
|
|
59
|
+
dir: cacheDir,
|
|
60
|
+
force: forceRefresh,
|
|
61
|
+
offline: false,
|
|
62
|
+
preferOffline: !forceRefresh
|
|
63
|
+
});
|
|
64
|
+
verbose(`Downloaded to: ${result.dir}`);
|
|
65
|
+
return {
|
|
66
|
+
path: result.dir,
|
|
67
|
+
fromCache: false,
|
|
68
|
+
source: fullSource
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw wrapGigetError(error, source);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function wrapGigetError(error, source) {
|
|
75
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
76
|
+
if (message.includes("404") || message.includes("Not Found")) {
|
|
77
|
+
return new Error(
|
|
78
|
+
`Repository not found: ${source}
|
|
79
|
+
|
|
80
|
+
This could mean:
|
|
81
|
+
- The repository doesn't exist
|
|
82
|
+
- The repository is private and you need to set authentication
|
|
83
|
+
- There's a typo in the URL
|
|
84
|
+
|
|
85
|
+
For private repositories, set the GIGET_AUTH environment variable:
|
|
86
|
+
export GIGET_AUTH=ghp_your_github_token`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (message.includes("401") || message.includes("Unauthorized")) {
|
|
90
|
+
return new Error(
|
|
91
|
+
`Authentication required for: ${source}
|
|
92
|
+
|
|
93
|
+
Set the GIGET_AUTH environment variable with a GitHub token:
|
|
94
|
+
export GIGET_AUTH=ghp_your_github_token
|
|
95
|
+
|
|
96
|
+
Create a token at: https://github.com/settings/tokens
|
|
97
|
+
Required scope: repo (for private repos) or public_repo (for public)`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (message.includes("403") || message.includes("Forbidden")) {
|
|
101
|
+
return new Error(
|
|
102
|
+
`Access denied to: ${source}
|
|
103
|
+
|
|
104
|
+
Your token may not have sufficient permissions.
|
|
105
|
+
Ensure your GIGET_AUTH token has the 'repo' scope for private repositories.`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (message.includes("ENOTFOUND") || message.includes("ETIMEDOUT") || message.includes("network")) {
|
|
109
|
+
return new Error(
|
|
110
|
+
`Network error fetching: ${source}
|
|
111
|
+
|
|
112
|
+
Please check your internet connection.
|
|
113
|
+
If you're behind a corporate proxy, you may need to set:
|
|
114
|
+
export HTTPS_PROXY=http://your-proxy:port
|
|
115
|
+
export FORCE_NODE_FETCH=true # Required for Node 20+`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return new Error(`Failed to fetch ${source}: ${message}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export {
|
|
122
|
+
fetchFromSource
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-NGBFJJ7Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-v2/lib/source-fetcher.ts"],"sourcesContent":["import path from \"path\";\nimport { downloadTemplate } from \"giget\";\nimport { verbose } from \"../utils/logger\";\nimport { CACHE_DIR } from \"../consts\";\nimport { ensureDir, directoryExists, readFile } from \"../utils/fs\";\nimport { isLocalSource } from \"./config\";\nimport type { Marketplace, MarketplaceFetchResult } from \"../../types\";\n\nexport interface FetchOptions {\n forceRefresh?: boolean;\n subdir?: string;\n}\n\nexport interface FetchResult {\n path: string;\n fromCache: boolean;\n source: string;\n}\n\nexport function sanitizeSourceForCache(source: string): string {\n return source\n .replace(/:/g, \"-\")\n .replace(/[\\/]/g, \"-\")\n .replace(/--+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\nfunction getCacheDir(source: string): string {\n const sanitized = sanitizeSourceForCache(source);\n return path.join(CACHE_DIR, \"sources\", sanitized);\n}\n\nexport async function fetchFromSource(\n source: string,\n options: FetchOptions = {},\n): Promise<FetchResult> {\n const { forceRefresh = false, subdir } = options;\n\n if (isLocalSource(source)) {\n return fetchFromLocalSource(source, subdir);\n }\n\n return fetchFromRemoteSource(source, { forceRefresh, subdir });\n}\n\nasync function fetchFromLocalSource(\n source: string,\n subdir?: string,\n): Promise<FetchResult> {\n const fullPath = subdir ? path.join(source, subdir) : source;\n const absolutePath = path.isAbsolute(fullPath)\n ? fullPath\n : path.resolve(process.cwd(), fullPath);\n\n if (!(await directoryExists(absolutePath))) {\n throw new Error(`Local source not found: ${absolutePath}`);\n }\n\n verbose(`Using local source: ${absolutePath}`);\n\n return {\n path: absolutePath,\n fromCache: false,\n source,\n };\n}\n\nasync function fetchFromRemoteSource(\n source: string,\n options: FetchOptions,\n): Promise<FetchResult> {\n const { forceRefresh = false, subdir } = options;\n const cacheDir = getCacheDir(source);\n\n const fullSource = subdir ? `${source}/${subdir}` : source;\n\n verbose(`Fetching from remote: ${fullSource}`);\n verbose(`Cache directory: ${cacheDir}`);\n\n await ensureDir(path.dirname(cacheDir));\n\n try {\n const result = await downloadTemplate(fullSource, {\n dir: cacheDir,\n force: forceRefresh,\n offline: false,\n preferOffline: !forceRefresh,\n });\n\n verbose(`Downloaded to: ${result.dir}`);\n\n return {\n path: result.dir,\n fromCache: false,\n source: fullSource,\n };\n } catch (error) {\n throw wrapGigetError(error, source);\n }\n}\n\nfunction wrapGigetError(error: unknown, source: string): Error {\n const message = error instanceof Error ? error.message : String(error);\n\n if (message.includes(\"404\") || message.includes(\"Not Found\")) {\n return new Error(\n `Repository not found: ${source}\\n\\n` +\n `This could mean:\\n` +\n ` - The repository doesn't exist\\n` +\n ` - The repository is private and you need to set authentication\\n` +\n ` - There's a typo in the URL\\n\\n` +\n `For private repositories, set the GIGET_AUTH environment variable:\\n` +\n ` export GIGET_AUTH=ghp_your_github_token`,\n );\n }\n\n if (message.includes(\"401\") || message.includes(\"Unauthorized\")) {\n return new Error(\n `Authentication required for: ${source}\\n\\n` +\n `Set the GIGET_AUTH environment variable with a GitHub token:\\n` +\n ` export GIGET_AUTH=ghp_your_github_token\\n\\n` +\n `Create a token at: https://github.com/settings/tokens\\n` +\n `Required scope: repo (for private repos) or public_repo (for public)`,\n );\n }\n\n if (message.includes(\"403\") || message.includes(\"Forbidden\")) {\n return new Error(\n `Access denied to: ${source}\\n\\n` +\n `Your token may not have sufficient permissions.\\n` +\n `Ensure your GIGET_AUTH token has the 'repo' scope for private repositories.`,\n );\n }\n\n if (\n message.includes(\"ENOTFOUND\") ||\n message.includes(\"ETIMEDOUT\") ||\n message.includes(\"network\")\n ) {\n return new Error(\n `Network error fetching: ${source}\\n\\n` +\n `Please check your internet connection.\\n` +\n `If you're behind a corporate proxy, you may need to set:\\n` +\n ` export HTTPS_PROXY=http://your-proxy:port\\n` +\n ` export FORCE_NODE_FETCH=true # Required for Node 20+`,\n );\n }\n\n return new Error(`Failed to fetch ${source}: ${message}`);\n}\n\nexport async function fetchMarketplace(\n source: string,\n options: FetchOptions = {},\n): Promise<MarketplaceFetchResult> {\n const result = await fetchFromSource(source, {\n forceRefresh: options.forceRefresh,\n subdir: \"\", // Root of repo\n });\n\n const marketplacePath = path.join(\n result.path,\n \".claude-plugin\",\n \"marketplace.json\",\n );\n\n if (!(await directoryExists(path.dirname(marketplacePath)))) {\n throw new Error(\n `Marketplace not found at: ${marketplacePath}\\n\\n` +\n `Expected .claude-plugin/marketplace.json in the source repository.`,\n );\n }\n\n const content = await readFile(marketplacePath);\n const marketplace = JSON.parse(content) as Marketplace;\n\n verbose(`Loaded marketplace: ${marketplace.name} v${marketplace.version}`);\n\n return {\n marketplace,\n sourcePath: result.path,\n fromCache: result.fromCache ?? false,\n cacheKey: sanitizeSourceForCache(source),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA,OAAO,UAAU;AACjB,SAAS,wBAAwB;AAkB1B,SAAS,uBAAuB,QAAwB;AAC7D,SAAO,OACJ,QAAQ,MAAM,GAAG,EACjB,QAAQ,SAAS,GAAG,EACpB,QAAQ,QAAQ,GAAG,EACnB,QAAQ,UAAU,EAAE;AACzB;AAEA,SAAS,YAAY,QAAwB;AAC3C,QAAM,YAAY,uBAAuB,MAAM;AAC/C,SAAO,KAAK,KAAK,WAAW,WAAW,SAAS;AAClD;AAEA,eAAsB,gBACpB,QACA,UAAwB,CAAC,GACH;AACtB,QAAM,EAAE,eAAe,OAAO,OAAO,IAAI;AAEzC,MAAI,cAAc,MAAM,GAAG;AACzB,WAAO,qBAAqB,QAAQ,MAAM;AAAA,EAC5C;AAEA,SAAO,sBAAsB,QAAQ,EAAE,cAAc,OAAO,CAAC;AAC/D;AAEA,eAAe,qBACb,QACA,QACsB;AACtB,QAAM,WAAW,SAAS,KAAK,KAAK,QAAQ,MAAM,IAAI;AACtD,QAAM,eAAe,KAAK,WAAW,QAAQ,IACzC,WACA,KAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ;AAExC,MAAI,CAAE,MAAM,gBAAgB,YAAY,GAAI;AAC1C,UAAM,IAAI,MAAM,2BAA2B,YAAY,EAAE;AAAA,EAC3D;AAEA,UAAQ,uBAAuB,YAAY,EAAE;AAE7C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW;AAAA,IACX;AAAA,EACF;AACF;AAEA,eAAe,sBACb,QACA,SACsB;AACtB,QAAM,EAAE,eAAe,OAAO,OAAO,IAAI;AACzC,QAAM,WAAW,YAAY,MAAM;AAEnC,QAAM,aAAa,SAAS,GAAG,MAAM,IAAI,MAAM,KAAK;AAEpD,UAAQ,yBAAyB,UAAU,EAAE;AAC7C,UAAQ,oBAAoB,QAAQ,EAAE;AAEtC,QAAM,UAAU,KAAK,QAAQ,QAAQ,CAAC;AAEtC,MAAI;AACF,UAAM,SAAS,MAAM,iBAAiB,YAAY;AAAA,MAChD,KAAK;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,eAAe,CAAC;AAAA,IAClB,CAAC;AAED,YAAQ,kBAAkB,OAAO,GAAG,EAAE;AAEtC,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,WAAW;AAAA,MACX,QAAQ;AAAA,IACV;AAAA,EACF,SAAS,OAAO;AACd,UAAM,eAAe,OAAO,MAAM;AAAA,EACpC;AACF;AAEA,SAAS,eAAe,OAAgB,QAAuB;AAC7D,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAErE,MAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG;AAC5D,WAAO,IAAI;AAAA,MACT,yBAAyB,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOjC;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,cAAc,GAAG;AAC/D,WAAO,IAAI;AAAA,MACT,gCAAgC,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKxC;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,KAAK,KAAK,QAAQ,SAAS,WAAW,GAAG;AAC5D,WAAO,IAAI;AAAA,MACT,qBAAqB,MAAM;AAAA;AAAA;AAAA;AAAA,IAG7B;AAAA,EACF;AAEA,MACE,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,WAAW,KAC5B,QAAQ,SAAS,SAAS,GAC1B;AACA,WAAO,IAAI;AAAA,MACT,2BAA2B,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKnC;AAAA,EACF;AAEA,SAAO,IAAI,MAAM,mBAAmB,MAAM,KAAK,OAAO,EAAE;AAC1D;","names":[]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_VERSION,
|
|
4
|
+
PLUGIN_MANIFEST_DIR,
|
|
5
|
+
PLUGIN_MANIFEST_FILE
|
|
6
|
+
} from "./chunk-A3J6IAXK.js";
|
|
7
|
+
import {
|
|
8
|
+
readFile,
|
|
9
|
+
writeFile
|
|
10
|
+
} from "./chunk-MMDXNZPF.js";
|
|
11
|
+
import {
|
|
12
|
+
init_esm_shims
|
|
13
|
+
} from "./chunk-DHET7RCE.js";
|
|
14
|
+
|
|
15
|
+
// src/cli-v2/lib/plugin-version.ts
|
|
16
|
+
init_esm_shims();
|
|
17
|
+
import path from "path";
|
|
18
|
+
function parseVersion(version) {
|
|
19
|
+
const parts = version.split(".").map(Number);
|
|
20
|
+
return [parts[0] || 1, parts[1] || 0, parts[2] || 0];
|
|
21
|
+
}
|
|
22
|
+
async function bumpPluginVersion(pluginDir, type) {
|
|
23
|
+
const manifestPath = path.join(
|
|
24
|
+
pluginDir,
|
|
25
|
+
PLUGIN_MANIFEST_DIR,
|
|
26
|
+
PLUGIN_MANIFEST_FILE
|
|
27
|
+
);
|
|
28
|
+
const content = await readFile(manifestPath);
|
|
29
|
+
const manifest = JSON.parse(content);
|
|
30
|
+
const [major, minor, patch] = parseVersion(
|
|
31
|
+
manifest.version || DEFAULT_VERSION
|
|
32
|
+
);
|
|
33
|
+
let newVersion;
|
|
34
|
+
switch (type) {
|
|
35
|
+
case "major":
|
|
36
|
+
newVersion = `${major + 1}.0.0`;
|
|
37
|
+
break;
|
|
38
|
+
case "minor":
|
|
39
|
+
newVersion = `${major}.${minor + 1}.0`;
|
|
40
|
+
break;
|
|
41
|
+
case "patch":
|
|
42
|
+
newVersion = `${major}.${minor}.${patch + 1}`;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
manifest.version = newVersion;
|
|
46
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
47
|
+
return newVersion;
|
|
48
|
+
}
|
|
49
|
+
async function getPluginVersion(pluginDir) {
|
|
50
|
+
const manifestPath = path.join(
|
|
51
|
+
pluginDir,
|
|
52
|
+
PLUGIN_MANIFEST_DIR,
|
|
53
|
+
PLUGIN_MANIFEST_FILE
|
|
54
|
+
);
|
|
55
|
+
const content = await readFile(manifestPath);
|
|
56
|
+
const manifest = JSON.parse(content);
|
|
57
|
+
return manifest.version || DEFAULT_VERSION;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
bumpPluginVersion,
|
|
62
|
+
getPluginVersion
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=chunk-OLBOTK3O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-v2/lib/plugin-version.ts"],"sourcesContent":["import path from \"path\";\nimport { readFile, writeFile } from \"../utils/fs\";\nimport {\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n DEFAULT_VERSION,\n} from \"../consts\";\nimport type { PluginManifest } from \"../../types\";\n\nexport type VersionBumpType = \"major\" | \"minor\" | \"patch\";\n\nfunction parseVersion(version: string): [number, number, number] {\n const parts = version.split(\".\").map(Number);\n return [parts[0] || 1, parts[1] || 0, parts[2] || 0];\n}\n\nexport async function bumpPluginVersion(\n pluginDir: string,\n type: VersionBumpType,\n): Promise<string> {\n const manifestPath = path.join(\n pluginDir,\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n );\n const content = await readFile(manifestPath);\n const manifest = JSON.parse(content) as PluginManifest;\n\n const [major, minor, patch] = parseVersion(\n manifest.version || DEFAULT_VERSION,\n );\n\n let newVersion: string;\n switch (type) {\n case \"major\":\n newVersion = `${major + 1}.0.0`;\n break;\n case \"minor\":\n newVersion = `${major}.${minor + 1}.0`;\n break;\n case \"patch\":\n newVersion = `${major}.${minor}.${patch + 1}`;\n break;\n }\n\n manifest.version = newVersion;\n await writeFile(manifestPath, JSON.stringify(manifest, null, 2));\n\n return newVersion;\n}\n\nexport async function getPluginVersion(pluginDir: string): Promise<string> {\n const manifestPath = path.join(\n pluginDir,\n PLUGIN_MANIFEST_DIR,\n PLUGIN_MANIFEST_FILE,\n );\n const content = await readFile(manifestPath);\n const manifest = JSON.parse(content) as PluginManifest;\n return manifest.version || DEFAULT_VERSION;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA,OAAO,UAAU;AAWjB,SAAS,aAAa,SAA2C;AAC/D,QAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,IAAI,MAAM;AAC3C,SAAO,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;AACrD;AAEA,eAAsB,kBACpB,WACA,MACiB;AACjB,QAAM,eAAe,KAAK;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU,MAAM,SAAS,YAAY;AAC3C,QAAM,WAAW,KAAK,MAAM,OAAO;AAEnC,QAAM,CAAC,OAAO,OAAO,KAAK,IAAI;AAAA,IAC5B,SAAS,WAAW;AAAA,EACtB;AAEA,MAAI;AACJ,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,mBAAa,GAAG,QAAQ,CAAC;AACzB;AAAA,IACF,KAAK;AACH,mBAAa,GAAG,KAAK,IAAI,QAAQ,CAAC;AAClC;AAAA,IACF,KAAK;AACH,mBAAa,GAAG,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAC3C;AAAA,EACJ;AAEA,WAAS,UAAU;AACnB,QAAM,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAE/D,SAAO;AACT;AAEA,eAAsB,iBAAiB,WAAoC;AACzE,QAAM,eAAe,KAAK;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,UAAU,MAAM,SAAS,YAAY;AAC3C,QAAM,WAAW,KAAK,MAAM,OAAO;AACnC,SAAO,SAAS,WAAW;AAC7B;","names":[]}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
init_esm_shims
|
|
4
|
+
} from "./chunk-DHET7RCE.js";
|
|
5
|
+
|
|
6
|
+
// src/cli-v2/components/wizard/category-grid.tsx
|
|
7
|
+
init_esm_shims();
|
|
8
|
+
import { useCallback, useEffect } from "react";
|
|
9
|
+
import { Box, Text, useInput } from "ink";
|
|
10
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
var SYMBOL_SELECTED = "\u25CF";
|
|
12
|
+
var SYMBOL_UNSELECTED = "\u25CB";
|
|
13
|
+
var SYMBOL_DISABLED = "\u2717";
|
|
14
|
+
var SYMBOL_RECOMMENDED = "\u2B50";
|
|
15
|
+
var SYMBOL_DISCOURAGED = "\u26A0";
|
|
16
|
+
var SYMBOL_FOCUS = ">";
|
|
17
|
+
var SYMBOL_REQUIRED = "*";
|
|
18
|
+
var MIN_OPTION_WIDTH = 12;
|
|
19
|
+
var getOptionSymbol = (option) => {
|
|
20
|
+
if (option.state === "disabled") {
|
|
21
|
+
return SYMBOL_DISABLED;
|
|
22
|
+
}
|
|
23
|
+
return option.selected ? SYMBOL_SELECTED : SYMBOL_UNSELECTED;
|
|
24
|
+
};
|
|
25
|
+
var getStateIndicator = (option) => {
|
|
26
|
+
if (option.state === "recommended") {
|
|
27
|
+
return SYMBOL_RECOMMENDED;
|
|
28
|
+
}
|
|
29
|
+
if (option.state === "discouraged") {
|
|
30
|
+
return SYMBOL_DISCOURAGED;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
var getSymbolColor = (option) => {
|
|
35
|
+
if (option.state === "disabled") {
|
|
36
|
+
return "gray";
|
|
37
|
+
}
|
|
38
|
+
if (option.selected) {
|
|
39
|
+
return "green";
|
|
40
|
+
}
|
|
41
|
+
return void 0;
|
|
42
|
+
};
|
|
43
|
+
var getLabelColor = (option, isFocused) => {
|
|
44
|
+
if (option.state === "disabled") {
|
|
45
|
+
return "gray";
|
|
46
|
+
}
|
|
47
|
+
if (isFocused) {
|
|
48
|
+
return "cyan";
|
|
49
|
+
}
|
|
50
|
+
if (option.state === "recommended") {
|
|
51
|
+
return "green";
|
|
52
|
+
}
|
|
53
|
+
if (option.state === "discouraged") {
|
|
54
|
+
return "yellow";
|
|
55
|
+
}
|
|
56
|
+
return void 0;
|
|
57
|
+
};
|
|
58
|
+
var sortOptions = (options, expertMode) => {
|
|
59
|
+
if (expertMode) {
|
|
60
|
+
return options;
|
|
61
|
+
}
|
|
62
|
+
const stateOrder = {
|
|
63
|
+
recommended: 0,
|
|
64
|
+
normal: 1,
|
|
65
|
+
discouraged: 2,
|
|
66
|
+
disabled: 3
|
|
67
|
+
};
|
|
68
|
+
return [...options].sort((a, b) => {
|
|
69
|
+
return stateOrder[a.state] - stateOrder[b.state];
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
var findNextValidOption = (options, currentIndex, direction, wrap = true) => {
|
|
73
|
+
const length = options.length;
|
|
74
|
+
if (length === 0) return currentIndex;
|
|
75
|
+
let index = currentIndex;
|
|
76
|
+
let attempts = 0;
|
|
77
|
+
while (attempts < length) {
|
|
78
|
+
index += direction;
|
|
79
|
+
if (wrap) {
|
|
80
|
+
if (index < 0) index = length - 1;
|
|
81
|
+
if (index >= length) index = 0;
|
|
82
|
+
} else {
|
|
83
|
+
if (index < 0) index = 0;
|
|
84
|
+
if (index >= length) index = length - 1;
|
|
85
|
+
}
|
|
86
|
+
if (options[index] && options[index].state !== "disabled") {
|
|
87
|
+
return index;
|
|
88
|
+
}
|
|
89
|
+
attempts++;
|
|
90
|
+
}
|
|
91
|
+
return currentIndex;
|
|
92
|
+
};
|
|
93
|
+
var findValidStartColumn = (options) => {
|
|
94
|
+
for (let i = 0; i < options.length; i++) {
|
|
95
|
+
if (options[i] && options[i].state !== "disabled") {
|
|
96
|
+
return i;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
};
|
|
101
|
+
var HeaderRow = ({
|
|
102
|
+
showDescriptions,
|
|
103
|
+
expertMode
|
|
104
|
+
}) => {
|
|
105
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", justifyContent: "flex-end", marginBottom: 1, gap: 2, children: [
|
|
106
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
107
|
+
"[Tab] Show descriptions: ",
|
|
108
|
+
showDescriptions ? "ON" : "OFF"
|
|
109
|
+
] }),
|
|
110
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
111
|
+
"[e] Expert Mode: ",
|
|
112
|
+
expertMode ? "ON" : "OFF"
|
|
113
|
+
] })
|
|
114
|
+
] });
|
|
115
|
+
};
|
|
116
|
+
var OptionCell = ({
|
|
117
|
+
option,
|
|
118
|
+
isFocused,
|
|
119
|
+
showDescription
|
|
120
|
+
}) => {
|
|
121
|
+
const symbol = getOptionSymbol(option);
|
|
122
|
+
const symbolColor = getSymbolColor(option);
|
|
123
|
+
const labelColor = getLabelColor(option, isFocused);
|
|
124
|
+
const stateIndicator = getStateIndicator(option);
|
|
125
|
+
const isDimmed = option.state === "disabled" || option.state === "discouraged";
|
|
126
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", minWidth: MIN_OPTION_WIDTH, marginRight: 1, children: [
|
|
127
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
128
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: isFocused ? SYMBOL_FOCUS : " " }),
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
130
|
+
/* @__PURE__ */ jsx(
|
|
131
|
+
Text,
|
|
132
|
+
{
|
|
133
|
+
color: symbolColor,
|
|
134
|
+
dimColor: isDimmed && option.state === "discouraged",
|
|
135
|
+
children: symbol
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
Text,
|
|
141
|
+
{
|
|
142
|
+
color: labelColor,
|
|
143
|
+
dimColor: option.state === "disabled",
|
|
144
|
+
bold: isFocused,
|
|
145
|
+
strikethrough: option.state === "disabled",
|
|
146
|
+
children: option.label
|
|
147
|
+
}
|
|
148
|
+
),
|
|
149
|
+
stateIndicator && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
150
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
151
|
+
/* @__PURE__ */ jsx(Text, { color: option.state === "recommended" ? "green" : "yellow", children: stateIndicator })
|
|
152
|
+
] })
|
|
153
|
+
] }),
|
|
154
|
+
showDescription && option.stateReason && /* @__PURE__ */ jsx(Box, { marginLeft: 4, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "truncate-end", children: option.stateReason }) })
|
|
155
|
+
] });
|
|
156
|
+
};
|
|
157
|
+
var CategoryRowComponent = ({
|
|
158
|
+
category,
|
|
159
|
+
options,
|
|
160
|
+
focusedCol,
|
|
161
|
+
isRowFocused,
|
|
162
|
+
showDescriptions
|
|
163
|
+
}) => {
|
|
164
|
+
return /* @__PURE__ */ jsxs(
|
|
165
|
+
Box,
|
|
166
|
+
{
|
|
167
|
+
flexDirection: "row",
|
|
168
|
+
alignItems: "flex-start",
|
|
169
|
+
marginBottom: showDescriptions ? 1 : 0,
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsx(Box, { minWidth: 16, marginRight: 2, children: /* @__PURE__ */ jsxs(Text, { bold: isRowFocused, color: isRowFocused ? "cyan" : void 0, children: [
|
|
172
|
+
category.name,
|
|
173
|
+
category.required && /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
174
|
+
" ",
|
|
175
|
+
SYMBOL_REQUIRED
|
|
176
|
+
] })
|
|
177
|
+
] }) }),
|
|
178
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "row", flexWrap: "wrap", children: options.map((option, index) => /* @__PURE__ */ jsx(
|
|
179
|
+
OptionCell,
|
|
180
|
+
{
|
|
181
|
+
option,
|
|
182
|
+
isFocused: isRowFocused && index === focusedCol,
|
|
183
|
+
showDescription: showDescriptions
|
|
184
|
+
},
|
|
185
|
+
option.id
|
|
186
|
+
)) }),
|
|
187
|
+
!category.required && /* @__PURE__ */ jsx(Box, { marginLeft: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(optional)" }) })
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
var LegendRow = () => {
|
|
193
|
+
return /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
194
|
+
"Legend: ",
|
|
195
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: SYMBOL_SELECTED }),
|
|
196
|
+
" selected",
|
|
197
|
+
" ",
|
|
198
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: SYMBOL_RECOMMENDED }),
|
|
199
|
+
" recommended",
|
|
200
|
+
" ",
|
|
201
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: SYMBOL_DISCOURAGED }),
|
|
202
|
+
" discouraged",
|
|
203
|
+
" ",
|
|
204
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: SYMBOL_DISABLED }),
|
|
205
|
+
" disabled"
|
|
206
|
+
] }) });
|
|
207
|
+
};
|
|
208
|
+
var CategoryGrid = ({
|
|
209
|
+
categories,
|
|
210
|
+
focusedRow,
|
|
211
|
+
focusedCol,
|
|
212
|
+
showDescriptions,
|
|
213
|
+
expertMode,
|
|
214
|
+
onToggle,
|
|
215
|
+
onFocusChange,
|
|
216
|
+
onToggleDescriptions,
|
|
217
|
+
onToggleExpertMode
|
|
218
|
+
}) => {
|
|
219
|
+
const processedCategories = categories.map((category) => ({
|
|
220
|
+
...category,
|
|
221
|
+
sortedOptions: sortOptions(category.options, expertMode)
|
|
222
|
+
}));
|
|
223
|
+
const currentRow = processedCategories[focusedRow];
|
|
224
|
+
const currentOptions = currentRow?.sortedOptions || [];
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (!currentRow) return;
|
|
227
|
+
const maxCol = currentOptions.length - 1;
|
|
228
|
+
if (focusedCol > maxCol) {
|
|
229
|
+
const newCol = Math.max(0, maxCol);
|
|
230
|
+
onFocusChange(focusedRow, newCol);
|
|
231
|
+
} else if (currentOptions[focusedCol]?.state === "disabled") {
|
|
232
|
+
const validCol = findValidStartColumn(currentOptions);
|
|
233
|
+
if (validCol !== focusedCol) {
|
|
234
|
+
onFocusChange(focusedRow, validCol);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, [focusedRow, currentOptions, focusedCol, onFocusChange, currentRow]);
|
|
238
|
+
useInput(
|
|
239
|
+
useCallback(
|
|
240
|
+
(input, key) => {
|
|
241
|
+
if (key.tab) {
|
|
242
|
+
onToggleDescriptions();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
if (input === "e" || input === "E") {
|
|
246
|
+
onToggleExpertMode();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (input === " ") {
|
|
250
|
+
const currentOption = currentOptions[focusedCol];
|
|
251
|
+
if (currentOption && currentOption.state !== "disabled") {
|
|
252
|
+
onToggle(currentRow.id, currentOption.id);
|
|
253
|
+
}
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
const isLeft = key.leftArrow || input === "h";
|
|
257
|
+
const isRight = key.rightArrow || input === "l";
|
|
258
|
+
const isUp = key.upArrow || input === "k";
|
|
259
|
+
const isDown = key.downArrow || input === "j";
|
|
260
|
+
if (isLeft) {
|
|
261
|
+
const newCol = findNextValidOption(
|
|
262
|
+
currentOptions,
|
|
263
|
+
focusedCol,
|
|
264
|
+
-1,
|
|
265
|
+
true
|
|
266
|
+
);
|
|
267
|
+
onFocusChange(focusedRow, newCol);
|
|
268
|
+
} else if (isRight) {
|
|
269
|
+
const newCol = findNextValidOption(
|
|
270
|
+
currentOptions,
|
|
271
|
+
focusedCol,
|
|
272
|
+
1,
|
|
273
|
+
true
|
|
274
|
+
);
|
|
275
|
+
onFocusChange(focusedRow, newCol);
|
|
276
|
+
} else if (isUp) {
|
|
277
|
+
const newRow = focusedRow <= 0 ? processedCategories.length - 1 : focusedRow - 1;
|
|
278
|
+
const newRowOptions = processedCategories[newRow]?.sortedOptions || [];
|
|
279
|
+
let newCol = Math.min(focusedCol, newRowOptions.length - 1);
|
|
280
|
+
if (newRowOptions[newCol]?.state === "disabled") {
|
|
281
|
+
newCol = findValidStartColumn(newRowOptions);
|
|
282
|
+
}
|
|
283
|
+
onFocusChange(newRow, newCol);
|
|
284
|
+
} else if (isDown) {
|
|
285
|
+
const newRow = focusedRow >= processedCategories.length - 1 ? 0 : focusedRow + 1;
|
|
286
|
+
const newRowOptions = processedCategories[newRow]?.sortedOptions || [];
|
|
287
|
+
let newCol = Math.min(focusedCol, newRowOptions.length - 1);
|
|
288
|
+
if (newRowOptions[newCol]?.state === "disabled") {
|
|
289
|
+
newCol = findValidStartColumn(newRowOptions);
|
|
290
|
+
}
|
|
291
|
+
onFocusChange(newRow, newCol);
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
[
|
|
295
|
+
focusedRow,
|
|
296
|
+
focusedCol,
|
|
297
|
+
currentOptions,
|
|
298
|
+
currentRow,
|
|
299
|
+
processedCategories,
|
|
300
|
+
onToggle,
|
|
301
|
+
onFocusChange,
|
|
302
|
+
onToggleDescriptions,
|
|
303
|
+
onToggleExpertMode
|
|
304
|
+
]
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
if (categories.length === 0) {
|
|
308
|
+
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No categories to display." }) });
|
|
309
|
+
}
|
|
310
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
311
|
+
/* @__PURE__ */ jsx(HeaderRow, { showDescriptions, expertMode }),
|
|
312
|
+
processedCategories.map((category, rowIndex) => /* @__PURE__ */ jsx(
|
|
313
|
+
CategoryRowComponent,
|
|
314
|
+
{
|
|
315
|
+
category,
|
|
316
|
+
options: category.sortedOptions,
|
|
317
|
+
focusedCol,
|
|
318
|
+
isRowFocused: rowIndex === focusedRow,
|
|
319
|
+
showDescriptions
|
|
320
|
+
},
|
|
321
|
+
category.id
|
|
322
|
+
)),
|
|
323
|
+
/* @__PURE__ */ jsx(LegendRow, {})
|
|
324
|
+
] });
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export {
|
|
328
|
+
CategoryGrid
|
|
329
|
+
};
|
|
330
|
+
//# sourceMappingURL=chunk-PPNTD5LO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-v2/components/wizard/category-grid.tsx"],"sourcesContent":["/**\n * CategoryGrid component - 2D grid selection for wizard Build step.\n *\n * Displays categories as rows with technology options as columns.\n * Supports keyboard navigation (arrows, vim keys) and visual states.\n *\n * Visual states:\n * - ● selected (green)\n * - ⭐ recommended (green text, shows hint)\n * - ⚠ discouraged (yellow/dim, shows warning)\n * - ✗ disabled (gray + strikethrough-like)\n * - ○ normal (white)\n * - > focus indicator (cyan/bold)\n */\nimport React, { useCallback, useEffect } from \"react\";\nimport { Box, Text, useInput } from \"ink\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type OptionState = \"normal\" | \"recommended\" | \"discouraged\" | \"disabled\";\n\nexport interface CategoryOption {\n id: string;\n label: string;\n state: OptionState;\n stateReason?: string;\n selected: boolean;\n}\n\nexport interface CategoryRow {\n id: string;\n name: string;\n required: boolean;\n exclusive: boolean;\n options: CategoryOption[];\n}\n\nexport interface CategoryGridProps {\n /** Categories to display (filtered by domain from matrix) */\n categories: CategoryRow[];\n /** Focused row index */\n focusedRow: number;\n /** Focused column index */\n focusedCol: number;\n /** Show descriptions under each technology */\n showDescriptions: boolean;\n /** Expert mode - shows all options, disables smart ordering */\n expertMode: boolean;\n /** Called when user toggles a technology */\n onToggle: (categoryId: string, technologyId: string) => void;\n /** Called when focus changes */\n onFocusChange: (row: number, col: number) => void;\n /** Called when show descriptions is toggled */\n onToggleDescriptions: () => void;\n /** Called when expert mode is toggled */\n onToggleExpertMode: () => void;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\n/** Symbol for selected option */\nconst SYMBOL_SELECTED = \"\\u25CF\"; // ●\n\n/** Symbol for unselected option */\nconst SYMBOL_UNSELECTED = \"\\u25CB\"; // ○\n\n/** Symbol for disabled option */\nconst SYMBOL_DISABLED = \"\\u2717\"; // ✗\n\n/** Symbol for recommended option */\nconst SYMBOL_RECOMMENDED = \"\\u2B50\"; // ⭐\n\n/** Symbol for discouraged option */\nconst SYMBOL_DISCOURAGED = \"\\u26A0\"; // ⚠\n\n/** Focus indicator */\nconst SYMBOL_FOCUS = \">\";\n\n/** Required indicator */\nconst SYMBOL_REQUIRED = \"*\";\n\n/** Minimum column width for option labels */\nconst MIN_OPTION_WIDTH = 12;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Get the visual symbol for an option based on its state and selection.\n */\nconst getOptionSymbol = (option: CategoryOption): string => {\n if (option.state === \"disabled\") {\n return SYMBOL_DISABLED;\n }\n return option.selected ? SYMBOL_SELECTED : SYMBOL_UNSELECTED;\n};\n\n/**\n * Get the state indicator symbol (if any) for an option.\n */\nconst getStateIndicator = (option: CategoryOption): string | null => {\n if (option.state === \"recommended\") {\n return SYMBOL_RECOMMENDED;\n }\n if (option.state === \"discouraged\") {\n return SYMBOL_DISCOURAGED;\n }\n return null;\n};\n\n/**\n * Get the color for the option symbol based on state and selection.\n */\nconst getSymbolColor = (option: CategoryOption): string | undefined => {\n if (option.state === \"disabled\") {\n return \"gray\";\n }\n if (option.selected) {\n return \"green\";\n }\n return undefined;\n};\n\n/**\n * Get the color for the option label based on state.\n */\nconst getLabelColor = (\n option: CategoryOption,\n isFocused: boolean,\n): string | undefined => {\n if (option.state === \"disabled\") {\n return \"gray\";\n }\n if (isFocused) {\n return \"cyan\";\n }\n if (option.state === \"recommended\") {\n return \"green\";\n }\n if (option.state === \"discouraged\") {\n return \"yellow\";\n }\n return undefined;\n};\n\n/**\n * Sort options based on state (recommended first, discouraged last).\n * In expert mode, returns options as-is.\n */\nconst sortOptions = (\n options: CategoryOption[],\n expertMode: boolean,\n): CategoryOption[] => {\n if (expertMode) {\n return options;\n }\n\n const stateOrder: Record<OptionState, number> = {\n recommended: 0,\n normal: 1,\n discouraged: 2,\n disabled: 3,\n };\n\n return [...options].sort((a, b) => {\n return stateOrder[a.state] - stateOrder[b.state];\n });\n};\n\n/**\n * Find the next non-disabled option index in a direction.\n * Returns current index if no valid option found.\n */\nconst findNextValidOption = (\n options: CategoryOption[],\n currentIndex: number,\n direction: 1 | -1,\n wrap: boolean = true,\n): number => {\n const length = options.length;\n if (length === 0) return currentIndex;\n\n let index = currentIndex;\n let attempts = 0;\n\n while (attempts < length) {\n index += direction;\n\n if (wrap) {\n // Wrap around\n if (index < 0) index = length - 1;\n if (index >= length) index = 0;\n } else {\n // Clamp to bounds\n if (index < 0) index = 0;\n if (index >= length) index = length - 1;\n }\n\n if (options[index] && options[index].state !== \"disabled\") {\n return index;\n }\n\n attempts++;\n }\n\n // All options are disabled, return current\n return currentIndex;\n};\n\n/**\n * Find a valid starting column for a row.\n * Returns 0 if all options are disabled.\n */\nconst findValidStartColumn = (options: CategoryOption[]): number => {\n for (let i = 0; i < options.length; i++) {\n if (options[i] && options[i].state !== \"disabled\") {\n return i;\n }\n }\n return 0;\n};\n\n// =============================================================================\n// Sub-Components\n// =============================================================================\n\ninterface HeaderRowProps {\n showDescriptions: boolean;\n expertMode: boolean;\n}\n\nconst HeaderRow: React.FC<HeaderRowProps> = ({\n showDescriptions,\n expertMode,\n}) => {\n return (\n <Box flexDirection=\"row\" justifyContent=\"flex-end\" marginBottom={1} gap={2}>\n <Text dimColor>\n [Tab] Show descriptions: {showDescriptions ? \"ON\" : \"OFF\"}\n </Text>\n <Text dimColor>[e] Expert Mode: {expertMode ? \"ON\" : \"OFF\"}</Text>\n </Box>\n );\n};\n\ninterface OptionCellProps {\n option: CategoryOption;\n isFocused: boolean;\n showDescription: boolean;\n}\n\nconst OptionCell: React.FC<OptionCellProps> = ({\n option,\n isFocused,\n showDescription,\n}) => {\n const symbol = getOptionSymbol(option);\n const symbolColor = getSymbolColor(option);\n const labelColor = getLabelColor(option, isFocused);\n const stateIndicator = getStateIndicator(option);\n const isDimmed =\n option.state === \"disabled\" || option.state === \"discouraged\";\n\n return (\n <Box flexDirection=\"column\" minWidth={MIN_OPTION_WIDTH} marginRight={1}>\n <Box flexDirection=\"row\">\n {/* Focus indicator */}\n <Text color=\"cyan\" bold>\n {isFocused ? SYMBOL_FOCUS : \" \"}\n </Text>\n <Text> </Text>\n\n {/* Selection symbol */}\n <Text\n color={symbolColor}\n dimColor={isDimmed && option.state === \"discouraged\"}\n >\n {symbol}\n </Text>\n <Text> </Text>\n\n {/* Label */}\n <Text\n color={labelColor}\n dimColor={option.state === \"disabled\"}\n bold={isFocused}\n strikethrough={option.state === \"disabled\"}\n >\n {option.label}\n </Text>\n\n {/* State indicator */}\n {stateIndicator && (\n <>\n <Text> </Text>\n <Text color={option.state === \"recommended\" ? \"green\" : \"yellow\"}>\n {stateIndicator}\n </Text>\n </>\n )}\n </Box>\n\n {/* Description/reason (shown when descriptions enabled) */}\n {showDescription && option.stateReason && (\n <Box marginLeft={4}>\n <Text dimColor wrap=\"truncate-end\">\n {option.stateReason}\n </Text>\n </Box>\n )}\n </Box>\n );\n};\n\ninterface CategoryRowComponentProps {\n category: CategoryRow;\n options: CategoryOption[];\n focusedCol: number;\n isRowFocused: boolean;\n showDescriptions: boolean;\n}\n\nconst CategoryRowComponent: React.FC<CategoryRowComponentProps> = ({\n category,\n options,\n focusedCol,\n isRowFocused,\n showDescriptions,\n}) => {\n return (\n <Box\n flexDirection=\"row\"\n alignItems=\"flex-start\"\n marginBottom={showDescriptions ? 1 : 0}\n >\n {/* Category name column */}\n <Box minWidth={16} marginRight={2}>\n <Text bold={isRowFocused} color={isRowFocused ? \"cyan\" : undefined}>\n {category.name}\n {category.required && <Text color=\"red\"> {SYMBOL_REQUIRED}</Text>}\n </Text>\n </Box>\n\n {/* Options */}\n <Box flexDirection=\"row\" flexWrap=\"wrap\">\n {options.map((option, index) => (\n <OptionCell\n key={option.id}\n option={option}\n isFocused={isRowFocused && index === focusedCol}\n showDescription={showDescriptions}\n />\n ))}\n </Box>\n\n {/* Optional indicator */}\n {!category.required && (\n <Box marginLeft={1}>\n <Text dimColor>(optional)</Text>\n </Box>\n )}\n </Box>\n );\n};\n\nconst LegendRow: React.FC = () => {\n return (\n <Box marginTop={1}>\n <Text dimColor>\n Legend: <Text color=\"green\">{SYMBOL_SELECTED}</Text> selected{\" \"}\n <Text color=\"green\">{SYMBOL_RECOMMENDED}</Text> recommended{\" \"}\n <Text color=\"yellow\">{SYMBOL_DISCOURAGED}</Text> discouraged{\" \"}\n <Text color=\"gray\">{SYMBOL_DISABLED}</Text> disabled\n </Text>\n </Box>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n focusedRow,\n focusedCol,\n showDescriptions,\n expertMode,\n onToggle,\n onFocusChange,\n onToggleDescriptions,\n onToggleExpertMode,\n}) => {\n // Process categories with sorted options\n const processedCategories = categories.map((category) => ({\n ...category,\n sortedOptions: sortOptions(category.options, expertMode),\n }));\n\n // Get current row and its options\n const currentRow = processedCategories[focusedRow];\n const currentOptions = currentRow?.sortedOptions || [];\n\n // Ensure focusedCol is valid when row changes or options change\n useEffect(() => {\n if (!currentRow) return;\n\n const maxCol = currentOptions.length - 1;\n if (focusedCol > maxCol) {\n // Clamp to max column\n const newCol = Math.max(0, maxCol);\n onFocusChange(focusedRow, newCol);\n } else if (currentOptions[focusedCol]?.state === \"disabled\") {\n // Current option is disabled, find a valid one\n const validCol = findValidStartColumn(currentOptions);\n if (validCol !== focusedCol) {\n onFocusChange(focusedRow, validCol);\n }\n }\n }, [focusedRow, currentOptions, focusedCol, onFocusChange, currentRow]);\n\n // Handle keyboard navigation\n useInput(\n useCallback(\n (\n input: string,\n key: {\n leftArrow: boolean;\n rightArrow: boolean;\n upArrow: boolean;\n downArrow: boolean;\n tab: boolean;\n },\n ) => {\n // Toggle descriptions with Tab\n if (key.tab) {\n onToggleDescriptions();\n return;\n }\n\n // Toggle expert mode with 'e'\n if (input === \"e\" || input === \"E\") {\n onToggleExpertMode();\n return;\n }\n\n // Toggle selection with Space\n if (input === \" \") {\n const currentOption = currentOptions[focusedCol];\n if (currentOption && currentOption.state !== \"disabled\") {\n onToggle(currentRow.id, currentOption.id);\n }\n return;\n }\n\n // Navigation\n const isLeft = key.leftArrow || input === \"h\";\n const isRight = key.rightArrow || input === \"l\";\n const isUp = key.upArrow || input === \"k\";\n const isDown = key.downArrow || input === \"j\";\n\n if (isLeft) {\n const newCol = findNextValidOption(\n currentOptions,\n focusedCol,\n -1,\n true,\n );\n onFocusChange(focusedRow, newCol);\n } else if (isRight) {\n const newCol = findNextValidOption(\n currentOptions,\n focusedCol,\n 1,\n true,\n );\n onFocusChange(focusedRow, newCol);\n } else if (isUp) {\n // Move to previous row\n const newRow =\n focusedRow <= 0 ? processedCategories.length - 1 : focusedRow - 1;\n const newRowOptions =\n processedCategories[newRow]?.sortedOptions || [];\n // Try to keep same column, or find valid one\n let newCol = Math.min(focusedCol, newRowOptions.length - 1);\n if (newRowOptions[newCol]?.state === \"disabled\") {\n newCol = findValidStartColumn(newRowOptions);\n }\n onFocusChange(newRow, newCol);\n } else if (isDown) {\n // Move to next row\n const newRow =\n focusedRow >= processedCategories.length - 1 ? 0 : focusedRow + 1;\n const newRowOptions =\n processedCategories[newRow]?.sortedOptions || [];\n // Try to keep same column, or find valid one\n let newCol = Math.min(focusedCol, newRowOptions.length - 1);\n if (newRowOptions[newCol]?.state === \"disabled\") {\n newCol = findValidStartColumn(newRowOptions);\n }\n onFocusChange(newRow, newCol);\n }\n },\n [\n focusedRow,\n focusedCol,\n currentOptions,\n currentRow,\n processedCategories,\n onToggle,\n onFocusChange,\n onToggleDescriptions,\n onToggleExpertMode,\n ],\n ),\n );\n\n if (categories.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Text dimColor>No categories to display.</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n {/* Header with toggles */}\n <HeaderRow showDescriptions={showDescriptions} expertMode={expertMode} />\n\n {/* Category rows */}\n {processedCategories.map((category, rowIndex) => (\n <CategoryRowComponent\n key={category.id}\n category={category}\n options={category.sortedOptions}\n focusedCol={focusedCol}\n isRowFocused={rowIndex === focusedRow}\n showDescriptions={showDescriptions}\n />\n ))}\n\n {/* Legend */}\n <LegendRow />\n </Box>\n );\n};\n"],"mappings":"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","names":[]}
|