agents-inc 0.159.0 → 0.161.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 (151) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +1 -1
  3. package/dist/{chunk-PEHYVSJG.js → chunk-264XDRU7.js} +2 -2
  4. package/dist/{chunk-WVKGO3Y5.js → chunk-2MPXMJ2O.js} +7 -7
  5. package/dist/{chunk-IO2DPRDS.js → chunk-3H42U56X.js} +2 -2
  6. package/dist/{chunk-FBRLHNUT.js → chunk-3M75HVIM.js} +3 -3
  7. package/dist/{chunk-YKRPWZ3G.js → chunk-3TM4FHTG.js} +2 -2
  8. package/dist/{chunk-5YOSLSQJ.js → chunk-5DWDT6GM.js} +115 -96
  9. package/dist/chunk-5DWDT6GM.js.map +1 -0
  10. package/dist/{chunk-4APXK7ZY.js → chunk-5MML3K36.js} +2 -2
  11. package/dist/{chunk-F35G42TF.js → chunk-6PVWDTEM.js} +12 -12
  12. package/dist/{chunk-VCUXNKOY.js → chunk-A2NA377T.js} +6 -6
  13. package/dist/{chunk-4RYWFQK7.js → chunk-ADDYIMWN.js} +3 -3
  14. package/dist/{chunk-V625APR6.js → chunk-AMZO4YJK.js} +10789 -18786
  15. package/dist/chunk-AMZO4YJK.js.map +1 -0
  16. package/dist/{chunk-4VMLCWLD.js → chunk-AO6DXJUK.js} +3 -3
  17. package/dist/{chunk-G7TYOPON.js → chunk-B3YSTZZC.js} +3 -3
  18. package/dist/{chunk-THLQS2AW.js → chunk-B7YTQVAF.js} +4 -4
  19. package/dist/{chunk-7Z4N4JWQ.js → chunk-BLGETWYI.js} +6 -113
  20. package/dist/chunk-BLGETWYI.js.map +1 -0
  21. package/dist/{chunk-ITEJARWI.js → chunk-BRHU6J3C.js} +3 -3
  22. package/dist/{chunk-AU5FS3QK.js → chunk-DGXW2ZFY.js} +3 -3
  23. package/dist/{chunk-LSSGS2QS.js → chunk-DHRW3MDM.js} +3 -3
  24. package/dist/{chunk-OJIQOVWB.js → chunk-DTCUZL7S.js} +2 -2
  25. package/dist/{chunk-SKDTGHCD.js → chunk-EEFTUZHZ.js} +5 -5
  26. package/dist/{chunk-BQJVHD6Y.js → chunk-EERGAAGE.js} +5 -10
  27. package/dist/chunk-EERGAAGE.js.map +1 -0
  28. package/dist/{chunk-Z6G24ROK.js → chunk-EQSMVZGS.js} +10 -10
  29. package/dist/{chunk-2Q65K6E3.js → chunk-EY7Z34Y4.js} +15 -15
  30. package/dist/{chunk-BNHD2GIB.js → chunk-F6LS2BQD.js} +11 -11
  31. package/dist/{chunk-SITF5EZM.js → chunk-HB3YIAFD.js} +2 -2
  32. package/dist/{chunk-GPZQCLZJ.js → chunk-LZPRLHUE.js} +9 -9
  33. package/dist/{chunk-YAHTF4NO.js → chunk-MED6GNV3.js} +288 -203
  34. package/dist/chunk-MED6GNV3.js.map +1 -0
  35. package/dist/chunk-N6CILY5A.js +35 -0
  36. package/dist/chunk-N6CILY5A.js.map +1 -0
  37. package/dist/{chunk-42PQKBVU.js → chunk-ODUACQLV.js} +6 -6
  38. package/dist/{chunk-VZRO7RYT.js → chunk-OI2H6M5I.js} +1 -103
  39. package/dist/chunk-OI2H6M5I.js.map +1 -0
  40. package/dist/{chunk-MPLWPWL4.js → chunk-QKMT6OOP.js} +2 -2
  41. package/dist/{chunk-WVBJRFFM.js → chunk-SX57ZIGM.js} +4 -4
  42. package/dist/{chunk-62XKTOUI.js → chunk-T4RVCFWN.js} +5 -15
  43. package/dist/chunk-T4RVCFWN.js.map +1 -0
  44. package/dist/{chunk-4VVKVOOP.js → chunk-UMYEY6BA.js} +2 -2
  45. package/dist/{chunk-X5PLVOJS.js → chunk-W2RXRWDB.js} +2 -2
  46. package/dist/{chunk-JV4P7BL4.js → chunk-WVCF7KFV.js} +5 -5
  47. package/dist/chunk-XA4SWA2Z.js +591 -0
  48. package/dist/chunk-XA4SWA2Z.js.map +1 -0
  49. package/dist/{chunk-LVA6XJGR.js → chunk-YOVUXONB.js} +9 -43
  50. package/dist/chunk-YOVUXONB.js.map +1 -0
  51. package/dist/chunk-YSQ76LWG.js +8734 -0
  52. package/dist/chunk-YSQ76LWG.js.map +1 -0
  53. package/dist/{chunk-Z6KROWH4.js → chunk-ZQZHDYHQ.js} +7 -7
  54. package/dist/commands/build/marketplace.js +8 -8
  55. package/dist/commands/build/plugins.js +8 -8
  56. package/dist/commands/compile.js +14 -14
  57. package/dist/commands/doctor.js +13 -13
  58. package/dist/commands/edit.js +45 -45
  59. package/dist/commands/eject.js +11 -11
  60. package/dist/commands/init.js +37 -37
  61. package/dist/commands/list.js +9 -9
  62. package/dist/commands/new/marketplace.js +8 -9
  63. package/dist/commands/new/marketplace.js.map +1 -1
  64. package/dist/commands/search.js +11 -11
  65. package/dist/commands/share.js +11 -10
  66. package/dist/commands/share.js.map +1 -1
  67. package/dist/commands/uninstall.js +12 -12
  68. package/dist/commands/update.js +7 -7
  69. package/dist/components/common/removal-plan-confirm.js +2 -2
  70. package/dist/components/common/select-list.js +2 -2
  71. package/dist/components/wizard/category-grid.js +4 -4
  72. package/dist/components/wizard/checkbox-grid.js +4 -4
  73. package/dist/components/wizard/domain-selection.js +10 -10
  74. package/dist/components/wizard/run-wizard-session.js +27 -27
  75. package/dist/components/wizard/scroll-affordance.js +2 -2
  76. package/dist/components/wizard/skill-agent-summary.js +6 -6
  77. package/dist/components/wizard/source-grid.js +5 -5
  78. package/dist/components/wizard/stack-selection.js +8 -8
  79. package/dist/components/wizard/step-agents.js +9 -9
  80. package/dist/components/wizard/step-build.js +8 -8
  81. package/dist/components/wizard/step-confirm.js +10 -10
  82. package/dist/components/wizard/step-sources.js +9 -9
  83. package/dist/components/wizard/step-stack.js +9 -9
  84. package/dist/components/wizard/summary-panel.js +9 -9
  85. package/dist/components/wizard/toast.js +2 -2
  86. package/dist/components/wizard/wizard-layout.js +13 -13
  87. package/dist/components/wizard/wizard-tabs.js +2 -2
  88. package/dist/components/wizard/wizard.js +26 -26
  89. package/dist/config-exports.js +1 -1
  90. package/dist/hooks/init.js +37 -37
  91. package/dist/load-agent-defs-LDSND43G.js +13 -0
  92. package/dist/recompile-project-agents-GQLODH24.js +17 -0
  93. package/dist/{source-loader-CC6ZWKTI.js → source-loader-TSF5VS6V.js} +5 -5
  94. package/dist/src/agents/_templates/methodologies/anti-over-engineering.liquid +14 -0
  95. package/dist/src/agents/_templates/methodologies/context-management.liquid +7 -154
  96. package/dist/src/agents/_templates/methodologies/investigation-requirements.liquid +12 -0
  97. package/dist/src/agents/_templates/methodologies/success-criteria.liquid +10 -0
  98. package/dist/stores/wizard-store.js +5 -5
  99. package/package.json +9 -4
  100. package/src/agents/_templates/methodologies/anti-over-engineering.liquid +14 -0
  101. package/src/agents/_templates/methodologies/context-management.liquid +7 -154
  102. package/src/agents/_templates/methodologies/investigation-requirements.liquid +12 -0
  103. package/src/agents/_templates/methodologies/success-criteria.liquid +10 -0
  104. package/dist/chunk-5KB4I5J7.js +0 -262
  105. package/dist/chunk-5KB4I5J7.js.map +0 -1
  106. package/dist/chunk-5YOSLSQJ.js.map +0 -1
  107. package/dist/chunk-62XKTOUI.js.map +0 -1
  108. package/dist/chunk-7Z4N4JWQ.js.map +0 -1
  109. package/dist/chunk-BQJVHD6Y.js.map +0 -1
  110. package/dist/chunk-LVA6XJGR.js.map +0 -1
  111. package/dist/chunk-NS5COBTT.js +0 -254
  112. package/dist/chunk-NS5COBTT.js.map +0 -1
  113. package/dist/chunk-V625APR6.js.map +0 -1
  114. package/dist/chunk-VZRO7RYT.js.map +0 -1
  115. package/dist/chunk-YAHTF4NO.js.map +0 -1
  116. package/dist/chunk-YVQSDBTK.js +0 -169
  117. package/dist/chunk-YVQSDBTK.js.map +0 -1
  118. package/dist/load-agent-defs-2H42B2OO.js +0 -13
  119. package/dist/recompile-project-agents-QIAMBCM4.js +0 -17
  120. /package/dist/{chunk-PEHYVSJG.js.map → chunk-264XDRU7.js.map} +0 -0
  121. /package/dist/{chunk-WVKGO3Y5.js.map → chunk-2MPXMJ2O.js.map} +0 -0
  122. /package/dist/{chunk-IO2DPRDS.js.map → chunk-3H42U56X.js.map} +0 -0
  123. /package/dist/{chunk-FBRLHNUT.js.map → chunk-3M75HVIM.js.map} +0 -0
  124. /package/dist/{chunk-YKRPWZ3G.js.map → chunk-3TM4FHTG.js.map} +0 -0
  125. /package/dist/{chunk-4APXK7ZY.js.map → chunk-5MML3K36.js.map} +0 -0
  126. /package/dist/{chunk-F35G42TF.js.map → chunk-6PVWDTEM.js.map} +0 -0
  127. /package/dist/{chunk-VCUXNKOY.js.map → chunk-A2NA377T.js.map} +0 -0
  128. /package/dist/{chunk-4RYWFQK7.js.map → chunk-ADDYIMWN.js.map} +0 -0
  129. /package/dist/{chunk-4VMLCWLD.js.map → chunk-AO6DXJUK.js.map} +0 -0
  130. /package/dist/{chunk-G7TYOPON.js.map → chunk-B3YSTZZC.js.map} +0 -0
  131. /package/dist/{chunk-THLQS2AW.js.map → chunk-B7YTQVAF.js.map} +0 -0
  132. /package/dist/{chunk-ITEJARWI.js.map → chunk-BRHU6J3C.js.map} +0 -0
  133. /package/dist/{chunk-AU5FS3QK.js.map → chunk-DGXW2ZFY.js.map} +0 -0
  134. /package/dist/{chunk-LSSGS2QS.js.map → chunk-DHRW3MDM.js.map} +0 -0
  135. /package/dist/{chunk-OJIQOVWB.js.map → chunk-DTCUZL7S.js.map} +0 -0
  136. /package/dist/{chunk-SKDTGHCD.js.map → chunk-EEFTUZHZ.js.map} +0 -0
  137. /package/dist/{chunk-Z6G24ROK.js.map → chunk-EQSMVZGS.js.map} +0 -0
  138. /package/dist/{chunk-2Q65K6E3.js.map → chunk-EY7Z34Y4.js.map} +0 -0
  139. /package/dist/{chunk-BNHD2GIB.js.map → chunk-F6LS2BQD.js.map} +0 -0
  140. /package/dist/{chunk-SITF5EZM.js.map → chunk-HB3YIAFD.js.map} +0 -0
  141. /package/dist/{chunk-GPZQCLZJ.js.map → chunk-LZPRLHUE.js.map} +0 -0
  142. /package/dist/{chunk-42PQKBVU.js.map → chunk-ODUACQLV.js.map} +0 -0
  143. /package/dist/{chunk-MPLWPWL4.js.map → chunk-QKMT6OOP.js.map} +0 -0
  144. /package/dist/{chunk-WVBJRFFM.js.map → chunk-SX57ZIGM.js.map} +0 -0
  145. /package/dist/{chunk-4VVKVOOP.js.map → chunk-UMYEY6BA.js.map} +0 -0
  146. /package/dist/{chunk-X5PLVOJS.js.map → chunk-W2RXRWDB.js.map} +0 -0
  147. /package/dist/{chunk-JV4P7BL4.js.map → chunk-WVCF7KFV.js.map} +0 -0
  148. /package/dist/{chunk-Z6KROWH4.js.map → chunk-ZQZHDYHQ.js.map} +0 -0
  149. /package/dist/{load-agent-defs-2H42B2OO.js.map → load-agent-defs-LDSND43G.js.map} +0 -0
  150. /package/dist/{recompile-project-agents-QIAMBCM4.js.map → recompile-project-agents-GQLODH24.js.map} +0 -0
  151. /package/dist/{source-loader-CC6ZWKTI.js.map → source-loader-TSF5VS6V.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/components/wizard/category-grid.tsx","../src/cli/components/hooks/use-category-grid-input.ts"],"sourcesContent":["import React, { useCallback, useEffect } from \"react\";\n\nimport { Box, Text } from \"ink\";\n\nimport { CLI_COLORS } from \"../../consts.js\";\nimport { getSkillById } from \"../../lib/matrix/matrix-provider.js\";\nimport type { Category, OptionState, SkillId, SkillScope } from \"../../types/index.js\";\nimport { useCategoryGridInput } from \"../hooks/use-category-grid-input.js\";\nimport { useFocusedListItem } from \"../hooks/use-focused-list-item.js\";\nimport { useSectionScroll } from \"../hooks/use-section-scroll.js\";\n\nexport type CategoryOption = {\n id: SkillId;\n state: OptionState;\n selected: boolean;\n local?: boolean;\n installed?: boolean;\n scope?: SkillScope;\n /** Secondary scope badge shown alongside primary (e.g. after G->P toggle, excluded tombstone) */\n secondaryScope?: SkillScope;\n source?: string;\n /** True when selected but has unmet dependency requirements (shown dimmed) */\n hasUnmetRequirements?: boolean;\n /** Explains unmet requirements (shown in label when D pressed) */\n unmetRequirementsReason?: string;\n /** Display name of the skill that requires this one (e.g. \"Next.js\") */\n requiredBy?: string;\n};\n\nexport type CategoryRow = {\n id: Category;\n displayName: string;\n required: boolean;\n exclusive: boolean;\n options: CategoryOption[];\n};\n\nexport type CategoryGridProps = {\n categories: CategoryRow[];\n /** Available height in terminal lines for the scrollable viewport. 0 = no constraint. */\n availableHeight?: number;\n showLabels: boolean;\n onToggle: (categoryId: Category, technologyId: SkillId) => void;\n onToggleLabels: () => void;\n /** Optional initial focus row (default: 0). Use with React `key` to reset. */\n defaultFocusedRow?: number;\n /** Optional initial focus col (default: 0). Use with React `key` to reset. */\n defaultFocusedCol?: number;\n /** Optional callback fired whenever the focused position changes */\n onFocusChange?: (row: number, col: number) => void;\n /** Optional callback fired with the resolved SkillId of the focused cell */\n onFocusedSkillChange?: (skillId: SkillId | null) => void;\n};\n\nconst SYMBOL_REQUIRED = \"*\";\n\n/** Next option index stepping cyclically from `currentIndex`; empty rows stay put. */\nconst wrapOptionIndex = (length: number, currentIndex: number, direction: 1 | -1): number => {\n if (length === 0) return currentIndex;\n return (currentIndex + direction + length) % length;\n};\n\ntype SkillTagProps = {\n option: CategoryOption;\n isFocused: boolean;\n showLabels: boolean;\n};\n\n/**\n * The verdict word and the reason behind it — `(incompatible: conflicts with Vue\n * Composition Api)`. Every advisory state but `normal` carries a reason, so the\n * status discriminant narrows to the two that have one to state.\n *\n * The word leads rather than the reason: it is the only carrier of the verdict\n * once colour is off, a `discourages` rule's reason is free-form author prose\n * that need not identify itself as a discouragement, and `CELL_ANNOTATION` in\n * `e2e/pages/steps/build-step.ts` anchors cell-label parsing on that keyword.\n *\n * Nothing is elided. A long reason grows the tag and wraps inside its border,\n * which is what `unmetRequirementsReason` above already does.\n */\nconst advisoryAnnotation = (state: OptionState): string | null =>\n state.status === \"normal\" ? null : `(${state.status}: ${state.reason})`;\n\nconst getCompatibilityLabel = (option: CategoryOption): string | null => {\n if (option.selected && option.hasUnmetRequirements && option.unmetRequirementsReason) {\n return `(${option.unmetRequirementsReason})`;\n }\n if (option.selected) return null;\n if (option.requiredBy) return `(required by ${option.requiredBy})`;\n return advisoryAnnotation(option.state);\n};\n\nconst STATUS_COLORS: Partial<Record<OptionState[\"status\"], string>> = {\n incompatible: CLI_COLORS.ERROR,\n discouraged: CLI_COLORS.WARNING,\n};\n\n/** Selected wins, then advisory status; `fallback` covers the plain \"normal\" state. */\nfunction resolveTagColor(option: CategoryOption, fallback: string): string {\n if (option.selected) return CLI_COLORS.PRIMARY;\n return STATUS_COLORS[option.state.status] ?? fallback;\n}\n\nconst SkillTag: React.FC<SkillTagProps> = ({ option, isFocused, showLabels }) => {\n const textColor = resolveTagColor(option, CLI_COLORS.DIM);\n const hasRequiredBy = option.selected && !!option.requiredBy;\n const hasUnmetDeps = option.selected && !!option.hasUnmetRequirements;\n const isDimmed = hasUnmetDeps || hasRequiredBy;\n const showCompatibility = hasRequiredBy || hasUnmetDeps || (showLabels && isFocused);\n const compatibilityLabel = showCompatibility ? getCompatibilityLabel(option) : null;\n\n return (\n <Box\n marginRight={1}\n borderColor={isFocused ? resolveTagColor(option, CLI_COLORS.UNFOCUSED) : textColor}\n borderDimColor={!isFocused}\n borderStyle=\"single\"\n flexShrink={0}\n paddingLeft={1}\n flexDirection=\"row\"\n >\n <>\n {option.scope && (\n <>\n {[option.scope, option.secondaryScope].filter(Boolean).map((badgeScope, i) => (\n <Text key={i} color={CLI_COLORS.WARNING} backgroundColor={CLI_COLORS.LABEL_BG}>\n {badgeScope === \"global\" ? \" G \" : \" P \"}\n </Text>\n ))}\n <Text> </Text>\n </>\n )}\n <Text color={textColor} bold dimColor={isDimmed}>\n {getSkillById(option.id).displayName}{\" \"}\n </Text>\n {compatibilityLabel && (\n <Text color={textColor} dimColor>\n {compatibilityLabel}{\" \"}\n </Text>\n )}\n </>\n </Box>\n );\n};\n\ntype CategorySectionProps = {\n isFirst: boolean;\n category: CategoryRow;\n isFocused: boolean;\n focusedOptionIndex: number;\n showLabels: boolean;\n};\n\nconst CategorySection: React.FC<CategorySectionProps> = ({\n isFirst,\n category,\n isFocused,\n focusedOptionIndex,\n showLabels,\n}) => {\n const { options } = category;\n const selectedCount = options.filter((o) => o.selected).length;\n\n const selectionCounter = category.exclusive ? `(${selectedCount} of 1)` : null;\n\n return (\n <Box flexDirection=\"column\" marginTop={isFirst ? 0 : 1}>\n <Box flexDirection=\"row\">\n {isFocused ? (\n <Text color={CLI_COLORS.WHITE} backgroundColor={CLI_COLORS.LABEL_BG}>\n {` ${category.displayName}${category.required ? ` ${SYMBOL_REQUIRED}` : \"\"}${selectionCounter ? ` ${selectionCounter}` : \"\"} `}\n </Text>\n ) : (\n <>\n <Text color={CLI_COLORS.NEUTRAL}>{category.displayName}</Text>\n {category.required && <Text color={CLI_COLORS.NEUTRAL}> {SYMBOL_REQUIRED}</Text>}\n {selectionCounter && <Text dimColor> {selectionCounter}</Text>}\n </>\n )}\n </Box>\n\n <Box flexDirection=\"row\" flexWrap=\"wrap\" marginTop={0}>\n {options.map((option, index) => (\n <SkillTag\n key={option.id}\n option={option}\n isFocused={isFocused && index === focusedOptionIndex}\n showLabels={showLabels}\n />\n ))}\n </Box>\n </Box>\n );\n};\n\nexport const CategoryGrid: React.FC<CategoryGridProps> = ({\n categories,\n availableHeight = 0,\n showLabels,\n onToggle,\n onToggleLabels,\n defaultFocusedRow = 0,\n defaultFocusedCol = 0,\n onFocusChange,\n onFocusedSkillChange,\n}) => {\n const getColCount = useCallback(\n (row: number): number => categories[row]?.options.length ?? 0,\n [categories],\n );\n\n const findValidCol = useCallback(\n (row: number, currentCol: number, direction: 1 | -1): number =>\n wrapOptionIndex(categories[row]?.options.length ?? 0, currentCol, direction),\n [categories],\n );\n\n const handleFocusChange = useCallback(\n (row: number, col: number) => {\n if (showLabels) onToggleLabels();\n onFocusChange?.(row, col);\n },\n [showLabels, onToggleLabels, onFocusChange],\n );\n\n const { focusedRow, focusedCol, setFocused, moveFocus } = useFocusedListItem(\n categories.length,\n getColCount,\n {\n wrap: true,\n findValidCol,\n onChange: handleFocusChange,\n initialRow: defaultFocusedRow,\n initialCol: defaultFocusedCol,\n },\n );\n\n useCategoryGridInput({\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\n });\n\n // Report the focused cell's skill on mount and whenever it changes — including\n // category reshapes that shift the cell without a navigation event. useFocusedListItem\n // only fires onChange during navigation, so the initially highlighted cell would\n // otherwise never reach the store, leaving focusedSkillId out of sync with the visual\n // focus and making the `s` scope hotkey no-op until the first arrow key.\n const focusedSkillId = categories[focusedRow]?.options[focusedCol]?.id ?? null;\n useEffect(() => {\n onFocusedSkillChange?.(focusedSkillId);\n }, [focusedSkillId, onFocusedSkillChange]);\n\n // `hiddenAbove` / `hiddenBelow` are discarded ON PURPOSE — the build grid has\n // no `ScrollAffordance` and is not getting one (owner decision). On a grid\n // this dense a half-cut card already says \"there is more\"; a counted hint\n // would be noise, and it would cost the viewport a row to say what the\n // clipped card says for free. Do not add one on the strength of the counts\n // merely being available here.\n const { setSectionRef, scrollEnabled, scrollTopPx } = useSectionScroll({\n sectionCount: categories.length,\n focusedIndex: focusedRow,\n availableHeight,\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 const noShrink = scrollEnabled ? { flexShrink: 0 } : {};\n\n const sectionElements = categories.map((category, index) => (\n <Box key={category.id} ref={(el) => setSectionRef(index, el)} {...noShrink}>\n <CategorySection\n category={category}\n isFocused={index === focusedRow}\n focusedOptionIndex={focusedCol}\n showLabels={showLabels}\n isFirst={index === 0}\n />\n </Box>\n ));\n\n return (\n <Box\n flexDirection=\"column\"\n {...(scrollEnabled\n ? { height: availableHeight, overflow: \"hidden\" as const }\n : { flexGrow: 1 })}\n >\n <Box flexDirection=\"column\" marginTop={scrollTopPx > 0 ? -scrollTopPx : 0} {...noShrink}>\n {sectionElements}\n </Box>\n </Box>\n );\n};\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useInput, type Key } from \"ink\";\n\nimport type { Category, SkillId } from \"../../types/index.js\";\nimport type { CategoryRow } from \"../wizard/category-grid.js\";\nimport type { Direction } from \"./use-focused-list-item.js\";\nimport { HOTKEY_TOGGLE_LABELS, KEY_SPACE, isHotkey } from \"../wizard/hotkeys.js\";\n\n/** Find next section index (wrapping forward) */\nconst findNextIndex = (items: { length: number }, currentIndex: number): number => {\n const length = items.length;\n if (length === 0) return currentIndex;\n return (currentIndex + 1) % length;\n};\n\ntype UseCategoryGridInputOptions = {\n categories: CategoryRow[];\n focusedRow: number;\n focusedCol: number;\n setFocused: (row: number, col: number) => void;\n moveFocus: (direction: Direction) => void;\n onToggle: (categoryId: Category, technologyId: SkillId) => void;\n onToggleLabels: () => void;\n};\n\nexport function useCategoryGridInput({\n categories,\n focusedRow,\n focusedCol,\n setFocused,\n moveFocus,\n onToggle,\n onToggleLabels,\n}: UseCategoryGridInputOptions): void {\n const currentRow = categories[focusedRow];\n // Memoised so the effect below keys on the row actually changing — the `[]`\n // fallback used to be a fresh array every render, re-running the effect for\n // nothing whenever no row was focused.\n const currentOptions = useMemo(() => currentRow?.options ?? [], [currentRow]);\n\n // Adjust column when current row's options change externally (e.g. option becomes disabled)\n useEffect(() => {\n if (!currentRow) return;\n\n const maxCol = currentOptions.length - 1;\n if (focusedCol > maxCol) {\n const newCol = Math.max(0, maxCol);\n setFocused(focusedRow, newCol);\n }\n }, [focusedRow, currentOptions, focusedCol, setFocused, currentRow]);\n\n // Store the latest handler in a ref so that the useInput effect never needs to\n // re-register on the event emitter. This avoids a stale-closure race condition\n // where, after a domain switch (CategoryGrid remount via key={activeDomain}),\n // the useInput effect may not yet have re-registered the updated handler when\n // the first keypress arrives — causing the first space press to be silently lost.\n const handlerRef = useRef<((input: string, key: Key) => void) | null>(null);\n handlerRef.current = (input: string, key: Key) => {\n if (key.tab && key.shift) {\n onToggleLabels();\n return;\n }\n\n if (key.tab && !key.shift) {\n const nextSection = findNextIndex(categories, focusedRow);\n if (nextSection !== focusedRow) {\n setFocused(nextSection, 0);\n }\n return;\n }\n\n if (isHotkey(input, HOTKEY_TOGGLE_LABELS)) {\n onToggleLabels();\n return;\n }\n\n if (input === KEY_SPACE) {\n const currentOption = currentOptions[focusedCol];\n if (currentRow && currentOption) {\n onToggle(currentRow.id, currentOption.id);\n }\n return;\n }\n\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 moveFocus(\"left\");\n } else if (isRight) {\n moveFocus(\"right\");\n } else if (isUp) {\n moveFocus(\"up\");\n } else if (isDown) {\n moveFocus(\"down\");\n }\n };\n\n // Stable handler reference — never changes, so useInput's effect registers once\n const stableHandler = useCallback((input: string, key: Key) => {\n handlerRef.current?.(input, key);\n }, []);\n\n useInput(stableHandler);\n}\n"],"mappings":"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","names":["useCallback","useEffect","useCallback","useEffect"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli/stores/wizard-store.ts","../src/cli/lib/wizard/build-step-logic.ts","../src/cli/lib/wizard/scope-diff.ts","../src/cli/lib/wizard/domain-order.ts","../src/cli/lib/wizard/agent-roster.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport {\n CLI_INVOKE_COMMAND,\n DEFAULT_PUBLIC_SOURCE_NAME,\n DEFAULT_SCRATCH_DOMAINS,\n EJECT_SOURCE,\n FALLBACK_DOMAIN,\n INSTALL_MODES,\n SKILL_CATEGORIES_PATH,\n} from \"../consts.js\";\nimport type { InstallMode } from \"../lib/installation/index.js\";\nimport { deriveInstallMode as sharedDeriveInstallMode } from \"../lib/installation/installation.js\";\nimport type { AgentScopeConfig, SkillConfig, SkillScope } from \"../types/config.js\";\nimport {\n isActiveAt,\n isGlobalTombstone,\n isProjectOwned,\n} from \"../lib/configuration/scope-predicates.js\";\nimport { isLocalOnlySkill } from \"../lib/loading/multi-source-loader.js\";\nimport { matrix, getSkillById, getCategoryDomain } from \"../lib/matrix/matrix-provider.js\";\nimport {\n agentSlotKey,\n buildCategoriesForDomain,\n firstFocusableAgent,\n orderDomains,\n skillSlotKey,\n} from \"../lib/wizard/index.js\";\nimport type {\n AgentName,\n Domain,\n DomainSelections,\n SkillId,\n Category,\n CategorySelections,\n ResolvedSkill,\n} from \"../types/index.js\";\nimport type { SourceOption, SourceRow } from \"../components/wizard/source-grid.js\";\nimport { disableBuffering, drainBuffer, enableBuffering, warn } from \"../utils/logger.js\";\nimport { typedEntries, typedFromEntries, typedKeys, typedValues } from \"../utils/typed-object.js\";\n\n/** The whole of what a skill no marketplace carries can be installed as. */\nconst LOCAL_ONLY_INSTALL_MODES = [\"eject\"] as const satisfies readonly Exclude<\n InstallMode,\n \"mixed\"\n>[];\n\n/** Toast strings surfaced by scope/selection guards (E2E asserts these verbatim). */\nconst TOAST_MESSAGES = {\n GLOBAL_SKILLS_LOCKED: \"Global skills cannot be changed from project scope\",\n ONLY_SKILL_IN_CATEGORY: \"Cannot deselect the only skill in this category\",\n ALREADY_EJECTED_AT_GLOBAL: \"Already exists as ejected skill at global scope\",\n GLOBAL_AGENTS_LOCKED: \"Global agents cannot be changed from project scope\",\n} as const;\n\n/**\n * What a population run past the mount could not place, in one line.\n *\n * Short by necessity rather than by preference: `Toast` in `components/wizard/toast.tsx` paints\n * its message on one row plus padding, absolutely positioned at the foot of a frame already\n * sized to the terminal,\n * so a toast as wide as the warning it stands for would wrap and break the frame it is drawn\n * in. Nothing is discarded by keeping it short — `unresolvableSkillIds` still carries every id,\n * which is what `edit` names each one from afterwards.\n *\n * It states the outcome rather than the cause, because two causes arrive here: a skill the\n * loaded source does not carry, and one whose category no domain claims. Both end the same way,\n * which is the sentence both warnings already close on — the wizard has no screen to put the\n * skill on, so it is left out of this session's selection.\n */\nfunction skillsLeftOutToast(count: number): string {\n return count === 1\n ? \"1 skill was left out — this source cannot place it\"\n : `${count} skills were left out — this source cannot place them`;\n}\n\n/** First defined source among candidates, else the default public source. */\nfunction resolveEffectiveSource(...candidates: Array<string | undefined>): string {\n return candidates.find((source) => source !== undefined) ?? DEFAULT_PUBLIC_SOURCE_NAME;\n}\n\n/** Name of a skill's primary available source, if any. */\nfunction primarySourceName(skill: ResolvedSkill | undefined): string | undefined {\n return skill?.availableSources?.find((s) => s.primary)?.name;\n}\n\n/** The one marketplace a skill can be plugin-installed from, when one carries it. */\nfunction marketplaceSourceName(skill: ResolvedSkill | undefined): string | undefined {\n return skill?.availableSources?.find((source) => source.type !== \"local\")?.name;\n}\n\n/**\n * The origin a skill takes when nothing saved names one: the marketplace that carries\n * it, or the project's own copy when none does. A marketplace origin on a skill no\n * marketplace has names an install that cannot happen, which is what made a\n * locally-written skill default to a plugin it could never be.\n */\nfunction defaultOriginFor(skill: ResolvedSkill | undefined): string {\n if (isLocalOnlySkill(skill)) return EJECT_SOURCE;\n\n return primarySourceName(skill) ?? DEFAULT_PUBLIC_SOURCE_NAME;\n}\n\nfunction createDefaultSkillConfig(id: SkillId): SkillConfig {\n return { id, scope: \"global\", origin: defaultOriginFor(matrix.skills[id]) };\n}\n\n/** True when configs hold an active (non-excluded) project-scope entry for the id. */\nfunction hasProjectActive(configs: SkillConfig[], id: SkillId): boolean {\n return configs.some((sc) => sc.id === id && isActiveAt(sc, \"project\"));\n}\n\n/** The active (non-excluded) global-scope entry for the id, when configs hold one. */\nfunction findGlobalActive(configs: SkillConfig[], id: SkillId): SkillConfig | undefined {\n return configs.find((sc) => sc.id === id && isActiveAt(sc, \"global\"));\n}\n\n/** True when configs hold an active (non-excluded) global-scope entry for the id. */\nfunction hasGlobalActive(configs: SkillConfig[], id: SkillId): boolean {\n return findGlobalActive(configs, id) !== undefined;\n}\n\n/** True when configs hold an excluded global-scope tombstone for the id. */\nfunction hasGlobalTombstone(configs: SkillConfig[], id: SkillId): boolean {\n return configs.some((sc) => sc.id === id && isGlobalTombstone(sc));\n}\n\n/**\n * A dual-scope pair — an active project entry plus a global tombstone for\n * the same skill. The shape the `[P][G]` row renders from; removal collapses it to a single\n * inherited-global entry, re-selection rebuilds it.\n */\nfunction isDualScopePair(configs: SkillConfig[], id: SkillId): boolean {\n return hasProjectActive(configs, id) && hasGlobalTombstone(configs, id);\n}\n\n/**\n * True when a SELECTED skill must not be deselected at project scope.\n *\n * The lock protects GLOBAL-OWNED halves and nothing else. Two arms: an active global entry in\n * the hydration snapshot (genuinely global-only — the long-standing read-only behaviour); and a\n * snapshot tombstone paired with a LIVE plain active global entry (the stale state a persisted\n * `[P][G]` reaches after an in-session collapse, whose deselect would silently tombstone the\n * still-real global install).\n *\n * A persisted `[P][G]` pair matches neither: its live global half is a tombstone, and its project\n * half is the project's own install to drop — `applySkillRemoval` collapses the pair to the\n * inherited global entry, so removing it never touches the global install it masks. A skill\n * freshly added this session (absent from the snapshot) is likewise nobody's install yet and\n * stays freely deselectable.\n */\nfunction isGloballyLockedSkill(\n installed: SkillConfig[],\n liveConfigs: SkillConfig[],\n id: SkillId,\n): boolean {\n return (\n hasGlobalActive(installed, id) ||\n (hasGlobalTombstone(installed, id) && hasGlobalActive(liveConfigs, id))\n );\n}\n\n/**\n * True when an exclusive-category radio swap must not drop this skill at project scope.\n *\n * The deselect lock, plus one case it deliberately allows on its own row: a live `[P][G]` pair.\n * Dropping a pair's project half leaves the global install it masks ACTIVE, so a swap that did it\n * implicitly would seat the new pick beside a still-active sibling in a category that permits one.\n * Removing the pair stays the project's to do — with the selection key on its own row, where\n * nothing takes the freed slot.\n */\nfunction blocksExclusiveSwap(\n installed: SkillConfig[],\n liveConfigs: SkillConfig[],\n id: SkillId,\n): boolean {\n return isGloballyLockedSkill(installed, liveConfigs, id) || isDualScopePair(liveConfigs, id);\n}\n\n/**\n * True when re-scoping this project-eject entry to global would overwrite an ejected global\n * install that the live config does not already override with a tombstone.\n */\nfunction wouldOverwriteGlobalEject(\n config: SkillConfig,\n liveConfigs: SkillConfig[],\n installedSkillConfigs: SkillConfig[] | null,\n): boolean {\n if (config.scope !== \"project\" || config.origin !== EJECT_SOURCE) return false;\n const globalEjectInstalled = installedSkillConfigs?.some(\n (sc) =>\n sc.id === config.id && sc.scope === \"global\" && sc.origin === EJECT_SOURCE && !sc.excluded,\n );\n if (!globalEjectInstalled) return false;\n return !liveConfigs.some((sc) => sc.id === config.id && sc.excluded);\n}\n\n/**\n * True when the slot a Sources-step call targets is a global install this session INHERITED —\n * an active global entry the hydration snapshot already carried.\n *\n * Keyed on the `(id, scope)` SLOT, never on the id: an id legitimately occupies slots at both\n * scopes at once, and the project half of a `[P][G]` pair is the project's own install to\n * configure. An id-keyed test would freeze the half the Sources grid deliberately leaves\n * editable.\n *\n * Read off the SNAPSHOT, never off the live entry's scope: a skill this session added at global\n * scope — and every skill in a first `init`, where the snapshot is `null` — is nobody's install\n * yet, so nothing is inherited and every row is the session's own.\n */\nfunction isInheritedGlobalSlot(\n installed: SkillConfig[] | null,\n id: SkillId,\n scope: SkillScope | undefined,\n): boolean {\n if (installed === null) return false;\n return scope === \"global\" && hasGlobalActive(installed, id);\n}\n\n/**\n * Rewrites the origin of the ACTIVE entry at (id, scope), leaving every other entry untouched —\n * in particular a dual-scope skill's excluded global tombstone, which keeps describing the\n * masked global install.\n */\nfunction withActiveEntryOrigin(\n configs: SkillConfig[],\n id: SkillId,\n scope: SkillScope | undefined,\n origin: string,\n): SkillConfig[] {\n return configs.map((sc) =>\n sc.id === id && !sc.excluded && sc.scope === scope ? { ...sc, origin } : sc,\n );\n}\n\n/**\n * Removes deselected skills, honouring what the project is allowed to remove.\n *\n * An entry the project OWNS — project-scoped, or the project's own global tombstone — is\n * dropped. An entry the project merely INHERITS (an active global-scope entry the hydration\n * snapshot already carried) is immutable from project scope and survives BYTE-IDENTICAL: a\n * project edit neither uninstalls a global install nor mints a tombstone for it. A skill this\n * session added itself is absent from the snapshot, is nobody's install yet, and is dropped\n * like any other so an accidental add stays undoable.\n *\n * `installedSkillConfigs` is `null` when editing FROM global scope, where the config being\n * edited IS the global one: nothing is inherited, so every removal is a genuine uninstall.\n *\n * Dual-scope entries (an active project entry paired with a global tombstone) collapse to a\n * single inherited-global entry so the `[G]` badge keeps rendering after the project half is\n * dropped.\n */\nfunction applySkillRemoval(\n configs: SkillConfig[],\n removedIds: Iterable<SkillId>,\n installedSkillConfigs: SkillConfig[] | null,\n): SkillConfig[] {\n // Annotated because `instanceof Set` narrows an `Iterable<SkillId>` to `Set<any>`,\n // which then spreads out as `any[]` below.\n const removed: ReadonlySet<SkillId> =\n removedIds instanceof Set ? removedIds : new Set(removedIds);\n const installedIds = new Set((installedSkillConfigs ?? []).map((s) => s.id));\n\n /**\n * A removed entry the project may not drop: a global install that existed before this session\n * and that the project does not own. Dual-scope pairs are excluded — both their halves go, and\n * the surviving global install is re-surfaced as a single inherited entry below.\n */\n const survivesRemoval = (entry: SkillConfig): boolean =>\n !isDualScopePair(configs, entry.id) && !isProjectOwned(entry) && installedIds.has(entry.id);\n\n const retained = configs.filter((sc) => !removed.has(sc.id) || survivesRemoval(sc));\n const resurfacedInheritedGlobals = [...removed]\n .filter((id) => isDualScopePair(configs, id))\n .map((id) => ({\n id,\n scope: \"global\" as const,\n origin:\n configs.find((sc) => sc.id === id && sc.scope === \"global\")?.origin ??\n DEFAULT_PUBLIC_SOURCE_NAME,\n }));\n\n return [...retained, ...resurfacedInheritedGlobals];\n}\n\n/** All skill IDs across a domain's category selections, in category order. */\nfunction flattenCategorySelections(selections: CategorySelections): SkillId[] {\n return typedValues(selections).flat();\n}\n\n/** Collects all skill IDs from a domain's category selections. */\nfunction collectSkillIdsFromSelections(selections: CategorySelections): Set<SkillId> {\n return new Set(flattenCategorySelections(selections));\n}\n\n/** Reconciles skill configs after selection changes: drops project-owned removals, leaves inherited global installs untouched, restores excluded on re-select, adds new defaults. */\nfunction reconcileSkillConfigs(\n configs: SkillConfig[],\n added: SkillId[],\n removed: SkillId[],\n installedSkillConfigs: SkillConfig[] | null,\n isEditingFromGlobalScope: boolean,\n): SkillConfig[] {\n // Editing from global scope has no project overlay, so a removal is a genuine uninstall.\n // Pass null so applySkillRemoval DROPS the skill rather than retaining it as an inherited\n // global install the project may not touch.\n const effectiveInstalled = isEditingFromGlobalScope ? null : installedSkillConfigs;\n let result = applySkillRemoval(configs, removed, effectiveInstalled);\n\n for (const id of added) {\n // Dual-scope restore: re-selecting a skill that is globally installed (recorded as a\n // tombstone in the project snapshot) with no current project entry re-creates BOTH a\n // fresh project entry and a global tombstone — mirroring toggleSkillScope's G->P path\n // so the row renders `[P][G]` again on a second spacebar.\n if (\n !isEditingFromGlobalScope &&\n hasGlobalTombstone(installedSkillConfigs ?? [], id) &&\n !hasProjectActive(result, id)\n ) {\n const globalEntry =\n (installedSkillConfigs ?? []).find((sc) => sc.id === id && sc.scope === \"global\") ??\n result.find((sc) => sc.id === id && sc.scope === \"global\");\n const origin = globalEntry?.origin ?? DEFAULT_PUBLIC_SOURCE_NAME;\n result = [\n ...result.filter((sc) => sc.id !== id),\n { id, scope: \"project\", origin },\n { id, scope: \"global\", excluded: true, origin },\n ];\n continue;\n }\n\n const existingExcluded = result.find((sc) => sc.id === id && sc.excluded);\n if (existingExcluded) {\n // Un-excluding drops the tombstone flag rather than setting it to `undefined`:\n // an absent `excluded` is what \"not excluded\" means everywhere else in the config.\n result = result.map((sc) => {\n if (sc.id !== id || !sc.excluded) return sc;\n const { excluded: _excluded, ...active } = sc;\n return active;\n });\n } else if (!result.some((sc) => sc.id === id)) {\n // Re-selecting a skill that the hydration snapshot still holds is a RESTORE, not a fresh\n // add: it must come back with its persisted scope and source rather than the wizard\n // defaults (a project-scoped eject skill would otherwise round-trip to global+marketplace\n // and trigger a bogus scope/source migration). buildSkillConfigForId degrades to exactly\n // createDefaultSkillConfig's output when the snapshot has no entry, so genuinely-new\n // selections are unaffected.\n result = [...result, buildSkillConfigForId(id, installedSkillConfigs)];\n }\n }\n\n return result;\n}\n\n/** True when configs hold an active (non-excluded) project-scope entry for the agent. */\nfunction agentHasProjectActive(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && isActiveAt(ac, \"project\"));\n}\n\n/** True when configs hold an active (non-excluded) global-scope entry for the agent. */\nfunction agentHasGlobalActive(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && isActiveAt(ac, \"global\"));\n}\n\n/** True when configs hold an excluded global-scope tombstone for the agent. */\nfunction agentHasGlobalTombstone(configs: AgentScopeConfig[], name: AgentName): boolean {\n return configs.some((ac) => ac.name === name && isGlobalTombstone(ac));\n}\n\n/** Agent side of isDualScopePair: active project entry + global tombstone for the same agent. */\nfunction isDualScopeAgentPair(configs: AgentScopeConfig[], name: AgentName): boolean {\n return agentHasProjectActive(configs, name) && agentHasGlobalTombstone(configs, name);\n}\n\n/**\n * Excluded tombstone entries. Preserved across reconcile/preselect merges so a dual-scope\n * pair's tombstone half survives and the pair can be restored later.\n */\nfunction collectTombstones<T extends { excluded?: boolean }>(configs: T[]): T[] {\n return configs.filter((entry) => entry.excluded);\n}\n\n/** The `(name, scope)` slots a rebuilt agent roster occupies. */\nfunction filledAgentSlots(configs: AgentScopeConfig[]): ReadonlySet<string> {\n return new Set(configs.map((ac) => agentSlotKey(ac.name, ac.scope)));\n}\n\n/**\n * Saved tombstones that still mask something once preselection has rebuilt the\n * active entries.\n *\n * A tombstone says \"the install at this scope is silenced\", which is only\n * meaningful while the active entry it silences sits at the OTHER scope — that\n * pair is the whole of the dual-scope indicator. A tombstone left at a slot the\n * rebuild has just filled with an active entry silences nothing, and the config\n * merge keys an active and an excluded entry apart, so both would be written.\n */\nfunction agentTombstonesOutsideRebuild(\n saved: AgentScopeConfig[],\n rebuilt: AgentScopeConfig[],\n): AgentScopeConfig[] {\n const filled = filledAgentSlots(rebuilt);\n return collectTombstones(saved).filter((ac) => !filled.has(agentSlotKey(ac.name, ac.scope)));\n}\n\n/** The skill-side twin of {@link agentTombstonesOutsideRebuild}, keyed on `(id, scope)`. */\nfunction skillTombstonesOutsideRebuild(\n saved: SkillConfig[],\n rebuilt: SkillConfig[],\n): SkillConfig[] {\n const filled = new Set(rebuilt.map((sc) => skillSlotKey(sc.id, sc.scope)));\n return collectTombstones(saved).filter((sc) => !filled.has(skillSlotKey(sc.id, sc.scope)));\n}\n\n/**\n * Applies an agent toggle: deselect removes the agent's entries; select restores an excluded\n * entry or adds a new one.\n *\n * A deselect never masks a globally-installed agent. Every reachable deselect here is\n * one the project owns — an active global install and a live `[P][G]` pair are both refused\n * upstream in `toggleAgent`, and a global-scope edit owns everything it can see — so the only\n * correct outcome is a clean removal.\n *\n * A newly added agent is `scope: \"global\"` DELIBERATELY, and a project-scoped installation\n * therefore compiling it with an empty stack is the CORRECT outcome, not a defect (ruled\n * 2026-08-19, after a finding filed against this line was graded wrong and deleted). Global is\n * the default scope for an agent, and `isScopePairCompatible` in\n * `lib/configuration/config-generator.ts` is the reason the stack comes out empty: project\n * skills never reach global agents, so a global agent paired with project-scoped skills is\n * given none of them. Re-scoping the agent to match the skills would put a global-by-default\n * agent under the project's ownership on nothing but a toggle. Anyone reading this line as a\n * missing `scope` derivation is re-deriving a reading that has already been rejected.\n */\nfunction applyAgentToggle(\n configs: AgentScopeConfig[],\n agent: AgentName,\n isSelected: boolean,\n): AgentScopeConfig[] {\n if (isSelected) {\n return configs.filter((ac) => ac.name !== agent);\n }\n\n const existingExcluded = configs.find((ac) => ac.name === agent && ac.excluded);\n if (existingExcluded) {\n return configs.map((ac) => {\n if (ac.name !== agent || !ac.excluded) return ac;\n const { excluded: _excluded, ...active } = ac;\n return active;\n });\n }\n return [...configs, { name: agent, scope: \"global\" as const }];\n}\n\n/**\n * True when an existing agent entry must outlive a domain-roster rebuild, which merges rather than\n * replaces. Two kinds survive:\n *\n * - Every excluded tombstone whose slot the rebuild leaves free, so a dual-scope pair (active\n * entry plus a tombstone at the other scope) survives preselection — the agent-side mirror of\n * the dual-scope fix in `populateFromSkillIds`. Deliberately NOT filtered by roster\n * membership; the slot test is what keeps a tombstone from landing beside an active entry it\n * cannot be masking.\n * - Every entry the project does not own, so a globally-installed agent outside the selected\n * domains' roster is never silently uninstalled by a project edit.\n *\n * Entries the roster itself rebuilds are excluded — `buildAgentConfigForName` re-derives those.\n */\nfunction survivesRosterRebuild(\n agentConfig: AgentScopeConfig,\n roster: ReadonlySet<AgentName>,\n rebuiltSlots: ReadonlySet<string>,\n): boolean {\n if (agentConfig.excluded) {\n return !rebuiltSlots.has(agentSlotKey(agentConfig.name, agentConfig.scope));\n }\n return !isProjectOwned(agentConfig) && !roster.has(agentConfig.name);\n}\n\n/**\n * Restores the `[P][G]` pair when re-selecting an inherited-global agent row whose\n * global install is recorded as a tombstone in the project snapshot — mirrors\n * reconcileSkillConfigs' restore branch.\n */\nfunction restoreDualScopeAgent(\n selectedAgents: AgentName[],\n agentConfigs: AgentScopeConfig[],\n agent: AgentName,\n) {\n return {\n selectedAgents: [...selectedAgents, agent],\n agentConfigs: [\n ...agentConfigs.filter((ac) => ac.name !== agent),\n { name: agent, scope: \"project\" as const },\n { name: agent, scope: \"global\" as const, excluded: true },\n ],\n };\n}\n\n/**\n * Next selectedAgents after a toggle. A deselect drops the agent; re-enabling a tombstoned\n * agent is already listed; a fresh add appends.\n */\nfunction nextSelectedAgents(\n selectedAgents: AgentName[],\n agent: AgentName,\n flags: { isSelected: boolean; isInList: boolean },\n): AgentName[] {\n if (flags.isSelected) return selectedAgents.filter((a) => a !== agent);\n if (flags.isInList) return selectedAgents;\n return [...selectedAgents, agent];\n}\n\n/** Builds a SkillConfig for a resolved skill ID, preferring saved config values. */\nfunction buildSkillConfigForId(id: SkillId, savedConfigs?: SkillConfig[] | null): SkillConfig {\n // Prefer project-scoped entry over global when duplicates exist (defensive)\n const saved =\n savedConfigs?.find((sc) => sc.id === id && !sc.excluded && sc.scope === \"project\") ??\n savedConfigs?.find((sc) => sc.id === id && !sc.excluded);\n return {\n id,\n scope: saved?.scope ?? \"global\",\n origin: saved?.origin ?? defaultOriginFor(matrix.skills[id]),\n };\n}\n\n/**\n * Builds an active AgentScopeConfig for a name, preferring a saved project-scoped\n * active entry. Mirrors buildSkillConfigForId on the agent path so preselection can\n * separate active-entry construction from tombstone preservation.\n */\nfunction buildAgentConfigForName(\n name: AgentName,\n savedConfigs?: AgentScopeConfig[],\n): AgentScopeConfig {\n // Prefer project-scoped active entry over global when duplicates exist.\n const saved =\n savedConfigs?.find((ac) => ac.name === name && !ac.excluded && ac.scope === \"project\") ??\n savedConfigs?.find((ac) => ac.name === name && !ac.excluded);\n return {\n name,\n scope: saved?.scope ?? \"global\",\n // Model and effort are the user's deliberate choice, not something the roster re-derives —\n // they survive the rebuild on the same terms as scope.\n ...(saved?.model !== undefined && { model: saved.model }),\n ...(saved?.effort !== undefined && { effort: saved.effort }),\n };\n}\n\n/** Restores skill configs for a domain: clears excluded flags on restored skills, adds new defaults for unknown skills. */\nfunction restoreSkillConfigs(\n existingConfigs: SkillConfig[],\n restoredIds: SkillId[],\n): SkillConfig[] {\n const restoredSet = new Set(restoredIds);\n const existingIds = new Set(existingConfigs.map((sc) => sc.id));\n\n const updated = existingConfigs.map((sc) => {\n if (!restoredSet.has(sc.id) || !sc.excluded) return sc;\n const { excluded: _excluded, ...active } = sc;\n return active;\n });\n\n const newConfigs = restoredIds.filter((id) => !existingIds.has(id)).map(createDefaultSkillConfig);\n\n return [...updated, ...newConfigs];\n}\n\n/**\n * Built-in agent names grouped by domain prefix. Custom domains return no preselected agents.\n * The consolidated `pm` and `reviewer` are cross-domain by role, so every domain rosters both;\n * the preselection union dedupes when several selected domains bring them.\n *\n * Exported ahead of a second production caller for the reason CLAUDE.md's Code Style exception\n * names: this is an identity roster more than one surface has to agree on. It, the grid's\n * `BUILT_IN_AGENT_GROUPS` and the generated `AGENT_NAMES` are three hand-or-machine-written\n * lists of the same set, and `tsc` only ever reconciled them for REMOVALS — an agent added to\n * the catalogue and to neither roster shipped unreachable through the wizard, six at a time.\n * `lib/wizard/agent-roster.test.ts` is the surface that reconciles them now.\n */\nexport const DOMAIN_AGENTS: Partial<Record<Domain, AgentName[]>> = {\n web: [\"web-developer\", \"web-researcher\", \"web-tester\", \"pm\", \"reviewer\"],\n api: [\"api-developer\", \"api-researcher\", \"api-tester\", \"pm\", \"reviewer\"],\n cli: [\"cli-developer\", \"cli-tester\", \"cli-researcher\", \"pm\", \"reviewer\"],\n ai: [\"ai-developer\", \"ai-researcher\", \"ai-tester\", \"pm\", \"reviewer\"],\n};\n\n/**\n * What an id this catalogue does not carry means, and what changes it.\n *\n * The outcome first, because it is the part the user cannot see: the skill reaches no screen,\n * so it is absent from everything the session goes on to write. Then the one reading a user can\n * act on — a marketplace this installation has not refreshed since the entry was written.\n *\n * `edit` classifies the same event far better downstream: `removalReason` in\n * `lib/skills/unresolved-skill-entries.ts` tells a marketplace drop from a local install whose\n * files are gone, and names the directory it looked in. None of that is reachable from here —\n * that classification is asynchronous and reads the filesystem, while this is a synchronous\n * store function holding only the matrix — so this line offers a remedy rather than a guess at\n * a reason it cannot establish.\n */\nfunction absentFromSourceWarning(skillId: SkillId): string {\n return (\n `Installed skill '${skillId}' is not present in the loaded source — it may have been ` +\n `removed or renamed. It is left out of this session's selection. Run ` +\n `'${CLI_INVOKE_COMMAND} update' to refresh the marketplace if you expect it to still be ` +\n `carried there.`\n );\n}\n\n/**\n * The same for a skill the catalogue DOES carry and no domain claims.\n *\n * {@link getCategoryDomain} reads `categories[category]?.domain`, so an undeclared category and\n * one declared without a `domain` are one answer here — and the wizard is organised by domain,\n * which is why an unplaceable category costs the skill its screen. Both ways out are the\n * source's: declare the category properly, or take the version of the source that already does.\n */\nfunction unplaceableCategoryWarning(skillId: SkillId, category: string): string {\n return (\n `Installed skill '${skillId}' has unknown category '${category}' — skipping. No domain in ` +\n `this source claims that category, so the wizard has no screen to place the skill on. ` +\n `Declare it with a 'domain' in the source's '${SKILL_CATEGORIES_PATH}', or run ` +\n `'${CLI_INVOKE_COMMAND} update' to refresh the marketplace.`\n );\n}\n\nfunction resolveSkillForPopulation(\n skillId: SkillId,\n): { domain: Domain; subcat: Category; techId: SkillId } | null {\n const { skills } = matrix;\n const skill = skills[skillId];\n if (!skill?.category) {\n warn(absentFromSourceWarning(skillId));\n return null;\n }\n\n const domain = getCategoryDomain(skill.category);\n if (!domain) {\n warn(unplaceableCategoryWarning(skillId, skill.category));\n return null;\n }\n\n // Boundary cast: domain lookup confirmed category exists in matrix\n const subcat = skill.category as Category;\n return { domain, subcat, techId: skillId };\n}\n\n/** Adds a skill to selections[domain][category] if absent; true when newly added. */\nfunction addToDomainSelections(\n selections: DomainSelections,\n domain: Domain,\n category: Category,\n skillId: SkillId,\n): boolean {\n const categorySelections = (selections[domain] ??= {});\n const skillList = (categorySelections[category] ??= []);\n if (skillList.includes(skillId)) return false;\n skillList.push(skillId);\n return true;\n}\n\n/** The `SkillConfig.origin` value an install mode writes for this skill. */\nfunction sourceForInstallMode(skill: ResolvedSkill, mode: Exclude<InstallMode, \"mixed\">): string {\n if (mode === \"eject\") return EJECT_SOURCE;\n\n return marketplaceSourceName(skill) ?? DEFAULT_PUBLIC_SOURCE_NAME;\n}\n\n/**\n * The install mode a persisted `origin` value stands for. The per-skill half of\n * `deriveInstallMode`, which asks the same question of a whole selection: `eject` names the\n * project's own copy and every other value names the marketplace the plugin comes from.\n */\nfunction installModeOfSource(origin: string | undefined): Exclude<InstallMode, \"mixed\"> {\n return origin === EJECT_SOURCE ? \"eject\" : \"plugin\";\n}\n\n/**\n * The cells of one skill's install-mode control, with the current mode selected.\n *\n * A skill nothing but the copy on disk backs gets the local cell ALONE — the grid cannot\n * express a plugin install that no marketplace could serve, so the refusal downstream is\n * a guard against callers reaching past this surface rather than the user's first\n * encounter with it.\n */\nfunction buildInstallModeOptions(\n skill: ResolvedSkill | undefined,\n selectedMode: Exclude<InstallMode, \"mixed\">,\n): SourceOption[] {\n const modes = isLocalOnlySkill(skill) ? LOCAL_ONLY_INSTALL_MODES : INSTALL_MODES;\n return modes.map((mode) => ({ mode, selected: mode === selectedMode }));\n}\n\n/**\n * The snapshot slots this session has emptied — an `(id, scope)` the hydration snapshot\n * holds ACTIVE that no live config entry occupies any more. The emptying leaves nothing behind to\n * render from: a PROJECT skill is dropped outright (applySkillRemoval leaves no tombstone, unlike\n * the global case), and so is ANY skill deselected while editing from global scope, where\n * `reconcileSkillConfigs` passes `null` so `applySkillRemoval` drops rather than tombstones. In\n * both the snapshot is the only surviving record, so without this set the row would vanish and the\n * user would lose sight of what saving is about to remove. Deliberately ungated on edit context:\n * `computeScopeDiff`, which the confirm step classifies removals with, has no global-scope gate\n * either, so neither surface may have one.\n *\n * Keyed per `(id, scope)` SLOT, mirroring `computeScopeDiff`'s removal match — removal is a\n * property of the slot, not of the id. Collapsing a dual-scope `[P][G]` pair to `[G]` empties the\n * PROJECT slot while the global one survives, so the skill renders twice on BOTH surfaces (`-` at\n * Project, `•`/lock at Global) instead of the survivor masking the loss. A tombstone OCCUPIES its\n * slot, so re-activating a masked global install is never a removal. Selection is not consulted:\n * `skillConfigs` is the same input the confirm step diffs, which is what keeps the two agreeing.\n *\n * Snapshot TOMBSTONES are deliberately not removal candidates — narrower than `computeScopeDiff`,\n * whose removal filter counts them. A tombstone is a MASK over a global install, not an install of\n * its own: dropping it deletes nothing (the global copy survives), so a red pending-removal row for\n * one would announce a deletion that never happens. No store transition reaches that shape anyway —\n * every path that drops a tombstone fills the same slot with an active entry (`toggleSkillScope`\n * P→G, `applySkillRemoval`'s dual-scope collapse).\n */\nfunction collectRemovedInstalledEntries(\n installedSkillConfigs: SkillConfig[] | null,\n skillConfigs: SkillConfig[],\n): SkillConfig[] {\n if (!installedSkillConfigs) return [];\n const occupiedSlots = new Set(skillConfigs.map((sc) => skillSlotKey(sc.id, sc.scope)));\n return installedSkillConfigs.filter(\n (sc) =>\n !sc.excluded &&\n !occupiedSlots.has(skillSlotKey(sc.id, sc.scope)) &&\n // A skill the marketplace no longer carries has nothing to render — hydration already\n // skipped it into unresolvableSkillIds, and getSkillById would throw on it downstream.\n !!matrix.skills[sc.id],\n );\n}\n\n/**\n * Every `(id, scope)` slot the hydration snapshot occupies — the baseline a Sources row is\n * \"added\" against, and the direct counterpart of `computeScopeDiff`'s `prevSkillKeySet`. Tombstones\n * count as occupied, exactly as that set does, so re-reading a stored tombstone never flags\n * a spurious addition.\n *\n * A missing snapshot (a first `init`, nothing installed at either scope) collapses to the EMPTY set\n * rather than to a distinct \"no baseline\" state, because the two are the same answer: an empty\n * baseline occupies no slot, so every row is new — which is what `classifyDiffRow` already tells the\n * confirm step for a null baseline, and what an empty snapshot already told this surface.\n */\nfunction collectInstalledSkillSlots(\n installedSkillConfigs: SkillConfig[] | null,\n): ReadonlySet<string> {\n if (!installedSkillConfigs) return new Set();\n return new Set(installedSkillConfigs.map((sc) => skillSlotKey(sc.id, sc.scope)));\n}\n\n/**\n * The `{ added: true }` fragment for a row whose SLOT is new this session, `{}` otherwise —\n * spread into the row so the Sources tab flags it with the info panel's added-diff marker (`+`).\n * Keying on the id alone would miss a skill adopted at project scope while it stays installed\n * globally: the id is old, but the project slot is new — which is exactly how `classifyDiffRow`\n * classifies the same change on the confirm step. Never gated on `isEditingFromGlobalScope`: an\n * addition is an addition regardless of edit context.\n */\nfunction addedSlotFlag(\n installedSkillSlots: ReadonlySet<string>,\n id: SkillId,\n scope: SkillScope | undefined,\n): { added?: true } {\n return installedSkillSlots.has(skillSlotKey(id, scope)) ? {} : { added: true };\n}\n\n/** Copy of the options with the persisted source's mode selected — for rows pinned to it. */\nfunction withSelectedMode(options: SourceOption[], source: string | undefined): SourceOption[] {\n const mode = installModeOfSource(source);\n return options.map((option) => ({ ...option, selected: option.mode === mode }));\n}\n\n/**\n * The per-skill inputs every source row shares: the canonical id, the live config entry driving\n * scope/lock classification, and the source options with the effective source preselected. A\n * pending-removal row re-pins that selection to its persisted origin (`withSelectedMode`).\n */\nfunction resolveSkillRowInputs(\n id: SkillId,\n skillConfigs: SkillConfig[],\n): { skillId: SkillId; configEntry: SkillConfig | undefined; options: SourceOption[] } {\n const skill = getSkillById(id);\n const configEntry = skillConfigs.find((sc) => sc.id === skill.id);\n const selectedSource = resolveEffectiveSource(\n configEntry?.origin,\n skill.activeSource?.name,\n primarySourceName(skill),\n );\n return {\n skillId: skill.id,\n configEntry,\n options: buildInstallModeOptions(skill, installModeOfSource(selectedSource)),\n };\n}\n\ntype SourceRowContext = {\n configEntry: SkillConfig | undefined;\n installedSkillConfigs: SkillConfig[] | null;\n isEditingFromGlobalScope: boolean;\n /** `(id, scope)` slots the snapshot occupies — the baseline each row's `+` derives from. */\n installedSkillSlots: ReadonlySet<string>;\n};\n\n/**\n * The inert row for a snapshot slot this session emptied. Carries the PERSISTED scope and\n * source so the row stays visible and shows what saving removes. Deliberately NOT readOnly: that\n * renders a lock, which reads as \"installed globally\" rather than \"about to be removed\". Carries no\n * added flag either — the slot it renders comes FROM the snapshot, so it can never be new this\n * session.\n */\nfunction toPendingRemovalRow(\n skillId: SkillId,\n options: SourceOption[],\n removedInstalledEntry: SkillConfig,\n): SourceRow {\n return {\n skillId,\n options: withSelectedMode(options, removedInstalledEntry.origin),\n scope: removedInstalledEntry.scope,\n disabled: true,\n };\n}\n\n/**\n * True when an already-emitted row renders the snapshot entry's `(id, scope)` slot, which makes a\n * pending-removal row for it a duplicate rather than a loss.\n *\n * The one shape that reaches this: a global install adopted at project scope WITHOUT a tombstone\n * (`[G]` snapshot, `[P]` live — the historical no-tombstone dual-scope shape `computeScopeDiff`\n * calls an inherited global). Its global slot is unoccupied in the live config, yet the locked\n * global row already renders it FROM the snapshot, so the install is inherited, not removed.\n */\nfunction isSlotAlreadyRendered(rows: SourceRow[], removedInstalledEntry: SkillConfig): boolean {\n const slot = skillSlotKey(removedInstalledEntry.id, removedInstalledEntry.scope);\n return rows.some((row) => skillSlotKey(row.skillId, row.scope) === slot);\n}\n\n/**\n * The locked row for a global install this project may not change from here — the lock reads\n * \"installed globally, not yours to edit at project scope\". Its source is pinned to the installed\n * one, and its added flag derives from the GLOBAL slot, so an install the project merely inherits\n * stays a plain lock rather than reading as new.\n */\nfunction toLockedGlobalRow(\n skillId: SkillId,\n options: SourceOption[],\n installedSource: string | undefined,\n installedSkillSlots: ReadonlySet<string>,\n): SourceRow {\n return {\n skillId,\n options: withSelectedMode(options, installedSource),\n scope: \"global\" as const,\n readOnly: true,\n ...addedSlotFlag(installedSkillSlots, skillId, \"global\"),\n };\n}\n\n/**\n * Classifies one skill's LIVE config entry into its source-grid rows: a locked global row for\n * excluded-global entries, locked global + editable project rows for skills re-scoped\n * global→project this session, or a single row otherwise. Pending-removal rows are not derived\n * here — they belong to snapshot slots with no live entry left (see `toPendingRemovalRow`).\n */\nfunction classifySkillSourceRows(\n skillId: SkillId,\n options: SourceOption[],\n context: SourceRowContext,\n): SourceRow[] {\n const { configEntry, installedSkillConfigs, isEditingFromGlobalScope, installedSkillSlots } =\n context;\n const installedGlobalConfig = findGlobalActive(installedSkillConfigs ?? [], skillId);\n\n const isExcludedGlobal = configEntry?.excluded && configEntry.scope === \"global\";\n if (isExcludedGlobal && !isEditingFromGlobalScope) {\n return [\n toLockedGlobalRow(\n skillId,\n options,\n installedGlobalConfig?.origin ?? configEntry.origin,\n installedSkillSlots,\n ),\n ];\n }\n\n if (!isEditingFromGlobalScope && installedGlobalConfig && configEntry?.scope === \"project\") {\n // Skill toggled from global to project — emit locked global copy + editable project copy. Each\n // half derives its own added flag from its own slot, so the `+` lands on the newly occupied\n // PROJECT row while the still-installed global row stays a plain lock.\n return [\n toLockedGlobalRow(skillId, options, installedGlobalConfig.origin, installedSkillSlots),\n {\n skillId,\n options,\n scope: \"project\" as const,\n ...addedSlotFlag(installedSkillSlots, skillId, \"project\"),\n },\n ];\n }\n\n const readOnly = !isEditingFromGlobalScope && !!installedGlobalConfig;\n return [\n {\n skillId,\n options,\n ...(configEntry?.scope !== undefined && { scope: configEntry.scope }),\n ...(readOnly ? { readOnly: true as const } : {}),\n ...addedSlotFlag(installedSkillSlots, skillId, configEntry?.scope),\n },\n ];\n}\n\n/**\n * Visual grouping order in source-grid: global readOnly, global editable, then project. A\n * pending-removal (disabled) row is NOT a separate tier — it sorts by its own scope so it renders\n * inline with the other rows of that scope, matching the info panel, which shows removals in place\n * rather than in a dedicated section. Its trailing position within a scope section falls out of the\n * removal rows being appended after every live row, which the stable sort preserves.\n */\nfunction sourceRowSortTier(row: SourceRow): number {\n if (row.scope === \"global\" && row.readOnly) return 0;\n if (row.scope === \"global\") return 1;\n return 2;\n}\n\n/**\n * Wizard step identifiers for the multi-step init/edit flow.\n *\n * Progression: stack -> domains -> build -> sources -> agents -> confirm\n * The \"stack\" step shows all stacks + \"Start from scratch\" in a unified list.\n * The \"domains\" step shows domain selection (web, api, cli, mobile, shared).\n * Navigation is tracked via the `history` stack for goBack() support.\n */\nexport type WizardStep =\n | \"stack\" // Select stack or \"Start from scratch\"\n | \"domains\" // Select domains to configure\n | \"build\" // CategoryGrid for technology selection\n | \"sources\" // Choose skill sources (recommended vs custom)\n | \"agents\" // Select which agents to compile\n | \"confirm\"; // Final confirmation\n\n/** How the user builds their config: from a pre-built stack, or skill-by-skill. */\ntype WizardApproach = \"stack\" | \"scratch\";\n\n/** For the stack approach: use the stack's defaults, or customize its skills. */\ntype StackAction = \"defaults\" | \"customize\";\n\nexport const WIZARD_STEP_ORDER = [\n \"stack\",\n \"domains\",\n \"build\",\n \"sources\",\n \"agents\",\n \"confirm\",\n] as const satisfies readonly WizardStep[];\n\n/** Whether the loaded source offers any stack to choose between. */\nfunction offersStacks(): boolean {\n return matrix.suggestedStacks.length > 0;\n}\n\n/**\n * The steps this session's wizard actually runs, in order.\n *\n * `WIZARD_STEP_ORDER` is every step the wizard has; this is the ones it has\n * THIS time. The stack step exists only where there is a stack to choose, so a\n * source that ships none — the built-in catalogue standing in for the default\n * public marketplace alone — drops it: hydration opens past it (see\n * {@link FIRST_STEP_WITHOUT_STACKS}) and the tab bar is drawn without it. One\n * answer to \"does this run have a stack step\", so the tabs cannot advertise a\n * step the flow does not have.\n */\nexport function getActiveStepFlow(): WizardStep[] {\n if (offersStacks()) return [...WIZARD_STEP_ORDER];\n return WIZARD_STEP_ORDER.filter((step) => step !== \"stack\");\n}\n\n/** The flow's steps before the active one; none when the step is outside the flow. */\nfunction stepsBefore(flow: WizardStep[], step: WizardStep): WizardStep[] {\n const index = flow.indexOf(step);\n return index < 0 ? [] : flow.slice(0, index);\n}\n\n/**\n * Wizard store state and actions.\n *\n * The store uses a composition pattern: small, focused actions that each mutate\n * one or two state fields. Wizard step components compose these actions to build\n * up the full selection state incrementally (domains -> categories -> skills -> sources).\n *\n * State flow: stack/scratch selection -> domain selection -> per-domain skill\n * selection (build step) -> source customization -> agent selection -> confirmation.\n */\nexport type WizardState = {\n step: WizardStep;\n\n approach: WizardApproach | null;\n selectedStackId: string | null;\n stackAction: StackAction | null;\n\n selectedDomains: Domain[];\n\n currentDomainIndex: number;\n domainSelections: DomainSelections;\n /** Snapshot of stack-provided domain selections for restoration on domain re-toggle */\n _stackDomainSelections: DomainSelections | null;\n\n showLabels: boolean;\n\n skillConfigs: SkillConfig[];\n focusedSkillId: SkillId | null;\n\n /**\n * Skill ids from the saved config that could NOT be resolved against the currently-loaded\n * source matrix this session (`populateFromSkillIds` skipped them, warning per skill). The\n * wizard cannot represent them, so they are absent from its result and the merge removes them\n * like any other absent entry — this list is what lets `edit` name each one and say why it went\n * rather than dropping it in silence.\n */\n unresolvableSkillIds: SkillId[];\n\n showInfo: boolean;\n\n selectedAgents: AgentName[];\n agentConfigs: AgentScopeConfig[];\n focusedAgentId: AgentName | null;\n\n /** Snapshot of configs that were installed before the wizard opened, used for diff rendering */\n installedSkillConfigs: SkillConfig[] | null;\n installedAgentConfigs: AgentScopeConfig[] | null;\n\n /** True when running init (first-time setup), false when editing an existing installation */\n isInitMode: boolean;\n\n /** When true, scope toggling is disabled (editing from ~/.claude/ with no project to move items to) */\n isEditingFromGlobalScope: boolean;\n\n /** Temporary toast message shown in the wizard (auto-cleared after timeout) */\n toastMessage: string | null;\n\n /** Global skill configs to pre-select when a stack or scratch is chosen in init. Set by use-wizard-initialization. */\n globalPreselections: SkillConfig[] | null;\n\n /** Global agent preselections to restore after selectStack wipes selectedAgents/agentConfigs. Set by use-wizard-initialization. */\n globalAgentPreselections: { agents: AgentName[]; configs: AgentScopeConfig[] } | null;\n\n history: WizardStep[];\n\n /**\n * Navigate to a wizard step, pushing the current step onto history.\n * @param step - Target step to navigate to\n *\n * Side effects: sets `step`, appends previous step to `history`\n */\n setStep: (step: WizardStep) => void;\n /**\n * Set the wizard approach (stack-based or build-from-scratch).\n * @param approach - \"stack\" to use a pre-built template, \"scratch\" to select skills manually, null to reset\n *\n * Side effects: sets `approach`\n */\n setApproach: (approach: WizardApproach | null) => void;\n /**\n * Select a stack by ID, or null to deselect.\n * @param stackId - Stack identifier from suggestedStacks, or null to clear\n *\n * Side effects: sets `selectedStackId`\n */\n selectStack: (stackId: string | null) => void;\n /**\n * Set how to apply the selected stack.\n * @param action - \"defaults\" to use stack as-is, \"customize\" to enter the build step\n *\n * Side effects: sets `stackAction`\n */\n setStackAction: (action: StackAction) => void;\n /**\n * Prepare a build with no stack behind it — what \"Start from scratch\" means.\n *\n * Two surfaces arrive here and must agree on what it is: the stack step's\n * scratch row, and the hydration that opens the wizard past a stack step the\n * loaded source gave nothing to show on.\n *\n * Side effects: clears every stack-derived selection (via `selectStack(null)`),\n * sets `approach` to \"scratch\", restores `selectedAgents` / `agentConfigs` from\n * `globalAgentPreselections`, populates selections from `globalPreselections`,\n * and selects the scratch domains. Does NOT navigate — the caller owns the step.\n */\n startFromScratch: () => void;\n /**\n * Pre-populate domainSelections from a flat list of installed skill IDs.\n *\n * Used by `npx agents-inc edit` to restore wizard state from existing project config.\n * Looks up each skill's category and domain, warns for unresolvable skills.\n *\n * @param skillIds - Flat array of currently installed skill IDs\n * @param skills - Skill lookup providing category and displayName per skill ID\n * @param categories - Category definitions used to resolve category -> domain mapping\n *\n * Side effects: sets `domainSelections`, sets `selectedDomains` to domains found in the provided skill IDs\n */\n populateFromSkillIds: (skillIds: SkillId[], savedConfigs?: SkillConfig[]) => void;\n /**\n * Toggle a domain on or off in the selectedDomains list.\n * @param domain - Domain to toggle\n *\n * Side effects: adds or removes from `selectedDomains`\n */\n toggleDomain: (domain: Domain) => void;\n /**\n * Toggle a skill selection within a domain's category.\n *\n * When exclusive is true (radio behavior), selecting a new skill replaces any\n * existing selection in that category. When false (checkbox behavior),\n * the skill is added to or removed from the selection array.\n *\n * @param domain - Domain containing the category\n * @param category - Category within the domain\n * @param technology - Skill ID to toggle\n * @param exclusive - If true, only one skill can be selected per category (radio)\n *\n * Side effects: updates `domainSelections[domain][category]`\n */\n toggleTechnology: (\n domain: Domain,\n category: Category,\n technology: SkillId,\n exclusive: boolean,\n ) => void;\n /**\n * Advance to the next domain in the build step.\n * @returns true if advanced, false if already at the last domain\n *\n * Side effects: increments `currentDomainIndex`\n */\n nextDomain: () => boolean;\n /**\n * Go back to the previous domain in the build step.\n * @returns true if moved back, false if already at the first domain\n *\n * Side effects: decrements `currentDomainIndex`\n */\n prevDomain: () => boolean;\n /**\n * Set the current domain index directly.\n * @param index - Index to set (0-based, must be within selectedDomains range)\n *\n * Side effects: sets `currentDomainIndex` if index is valid, otherwise no-op\n */\n setCurrentDomainIndex: (index: number) => void;\n /** Toggle compatibility label visibility on skill tags in the build step grid. */\n toggleShowLabels: () => void;\n /**\n * Derive the install mode from skillConfigs source values.\n * If all skills use \"eject\" source, returns \"eject\". If all use non-eject, returns \"plugin\".\n * If mixed, returns \"mixed\". Returns \"eject\" when no skills are configured.\n */\n deriveInstallMode: () => InstallMode;\n /**\n * Toggle the scope of a specific skill between \"project\" and \"global\".\n * @param skillId - Skill to toggle scope for\n *\n * Side effects: updates `skillConfigs` entry for the skill\n */\n toggleSkillScope: (skillId: SkillId) => void;\n /**\n * Set the currently focused skill ID in the build step (for S hotkey).\n * @param id - Skill ID to focus, or null to clear\n *\n * Side effects: sets `focusedSkillId`\n */\n setFocusedSkillId: (id: SkillId | null) => void;\n /**\n * Seed `focusedSkillId` to the skill the build-step grid focuses first for the\n * active domain (row 0, col 0 of the filtered category rows). Runs synchronously\n * at build-step entry and on every domain change so the `s` scope hotkey — which\n * reads `focusedSkillId` — always resolves the visually-focused skill, with no\n * dependency on CategoryGrid's post-mount effect.\n *\n * Side effects: sets `focusedSkillId` (null when the active domain has no skills)\n */\n seedFocusedSkillForActiveDomain: () => void;\n /**\n * Set how a specific skill is installed: the project's own copy, or the marketplace plugin.\n * @param skillId - Skill to configure the install mode for\n * @param mode - `eject` for a local copy, `plugin` for the one marketplace\n * @param scope - Acting scope from the Sources row: only the active entry at this scope is\n * updated; a masked global tombstone for the same id keeps its source.\n *\n * Refuses a project-context call that targets a global slot the hydration snapshot already\n * owns ({@link isInheritedGlobalSlot}) — a project edit may not change how a global install is\n * installed, the same authority the Sources grid enforces by rendering that row inert.\n *\n * Side effects: updates the active `skillConfigs` entry for the skill at `scope`, writing the\n * `origin` value the mode resolves to. No-op with a warning on an empty skill id; silent no-op\n * on a refused global slot.\n */\n setInstallMode: (\n skillId: SkillId,\n mode: Exclude<InstallMode, \"mixed\">,\n scope: SkillScope | undefined,\n ) => void;\n /** Toggle the info overlay (selected skills and agents). */\n toggleInfo: () => void;\n /** Set a temporary toast message, or null to clear it. */\n setToastMessage: (message: string | null) => void;\n /**\n * Navigate to the previous wizard step using the history stack.\n * No-op when history is empty (e.g., edit flow starting at a mid-wizard step).\n *\n * Side effects: pops from `history`, sets `step` to the popped value\n */\n goBack: () => void;\n /**\n * Toggle an agent on or off in the selectedAgents list.\n * @param agent - Agent name to toggle\n *\n * Side effects: adds or removes from `selectedAgents`, syncs `agentConfigs`\n */\n toggleAgent: (agent: AgentName) => void;\n /**\n * Toggle the scope of a specific agent between \"project\" and \"global\".\n * @param agentName - Agent to toggle scope for\n *\n * Side effects: updates `agentConfigs` entry for the agent\n */\n toggleAgentScope: (agentName: AgentName) => void;\n /**\n * Set the currently focused agent ID in the agents step (for S hotkey).\n * @param id - Agent name to focus, or null to clear\n *\n * Side effects: sets `focusedAgentId`\n */\n setFocusedAgentId: (id: AgentName | null) => void;\n /**\n * Seed `focusedAgentId` to the row the agents grid focuses first, unless a\n * focused agent is already recorded — the grid restores that one instead.\n *\n * Runs on entry to the agents step so the value is written before any frame\n * renders or input is processed. The `s` scope toggle in `wizard.tsx` reads\n * `focusedAgentId` synchronously, and terminal input is not ordered against\n * render-phase effects: a post-mount seed can lose to a buffered keystroke.\n *\n * Side effects: sets `focusedAgentId`\n */\n seedFocusedAgent: () => void;\n /**\n * Preselect agents based on selected domains from the first wizard step.\n * Matches domains against DOMAIN_AGENTS mapping.\n * Optional agents (meta/pattern) are excluded.\n *\n * The from-scratch path only: with a stack chosen, its declared roster stands and\n * this is a no-op. Selecting a stack is the statement of which sub-agents an\n * installation gets, and the user edits that roster in the agents step.\n *\n * Side effects: replaces `selectedAgents` with computed preselection, unless a\n * stack is selected\n */\n preselectAgentsFromDomains: () => void;\n /**\n * Preselect agents for a chosen stack: merges the stack's agent keys with global\n * agent preselections, preserving dual-scope tombstones. Parallels\n * populateFromSkillIds on the skill path.\n *\n * This is the roster the install gets — nothing downstream re-derives it.\n *\n * Side effects: replaces `selectedAgents` and `agentConfigs` with the merged result\n */\n preselectAgentsFromStack: (stackAgents: AgentName[]) => void;\n /** Reset all wizard state to initial values. */\n reset: () => void;\n\n /**\n * Collect all selected skill IDs across all domains and categories.\n * @returns Flat array of every selected SkillId (may contain duplicates if shared across domains)\n */\n getAllSelectedTechnologies: () => SkillId[];\n /**\n * Group selected skill IDs by domain.\n * @returns Partial record mapping each domain with selections to its skill ID array\n */\n getSelectedTechnologiesPerDomain: () => Partial<Record<Domain, SkillId[]>>;\n /**\n * Get the domain currently visible in the build step.\n * @returns The domain at currentDomainIndex, or null if no domains are selected\n */\n getCurrentDomain: () => Domain | null;\n /**\n * Count total selected technologies across all domains.\n * @returns Number of selected skill IDs\n */\n getTechnologyCount: () => number;\n /**\n * Compute which wizard steps are completed and which are skipped.\n * Used by WizardTabs to render step progress indicators. Both lists are read\n * against {@link getActiveStepFlow} — a step this run never has cannot be one\n * it completed.\n * @returns Object with completedSteps and skippedSteps string arrays\n */\n getStepProgress: () => { completedSteps: WizardStep[]; skippedSteps: WizardStep[] };\n /** @returns true if there is a next domain after the current one */\n canGoToNextDomain: () => boolean;\n /** @returns true if there is a previous domain before the current one */\n canGoToPreviousDomain: () => boolean;\n\n /**\n * Build the source selection rows for the sources step UI.\n *\n * For each selected technology, resolves the canonical skill ID and marks which of the two\n * install modes its persisted `origin` stands for.\n *\n * @returns Array of row objects, one per selected technology, each containing:\n * - `skillId` - Canonical resolved skill ID\n * - `options` - The two install-mode cells, with the current one selected\n */\n buildSourceRows: () => SourceRow[];\n};\n\n/** State-only fields from WizardState (excludes actions/getters). Used to type createInitialState(). */\ntype WizardStateData = Pick<\n WizardState,\n | \"step\"\n | \"approach\"\n | \"selectedStackId\"\n | \"stackAction\"\n | \"selectedDomains\"\n | \"currentDomainIndex\"\n | \"domainSelections\"\n | \"_stackDomainSelections\"\n | \"showLabels\"\n | \"skillConfigs\"\n | \"focusedSkillId\"\n | \"unresolvableSkillIds\"\n | \"showInfo\"\n | \"selectedAgents\"\n | \"agentConfigs\"\n | \"focusedAgentId\"\n | \"installedSkillConfigs\"\n | \"installedAgentConfigs\"\n | \"isInitMode\"\n | \"isEditingFromGlobalScope\"\n | \"toastMessage\"\n | \"globalPreselections\"\n | \"globalAgentPreselections\"\n | \"history\"\n>;\n\nexport const createInitialState = (overrides?: Partial<WizardStateData>): WizardStateData => ({\n step: \"stack\",\n approach: null,\n selectedStackId: null,\n stackAction: null,\n selectedDomains: [],\n currentDomainIndex: 0,\n domainSelections: {},\n /** Snapshot of domainSelections from populateFromSkillIds, used to restore on domain re-toggle */\n _stackDomainSelections: null,\n showLabels: false,\n skillConfigs: [],\n focusedSkillId: null,\n unresolvableSkillIds: [],\n showInfo: false,\n selectedAgents: [],\n agentConfigs: [],\n focusedAgentId: null,\n installedSkillConfigs: null,\n installedAgentConfigs: null,\n isInitMode: false,\n isEditingFromGlobalScope: false,\n toastMessage: null,\n globalPreselections: null,\n globalAgentPreselections: null,\n history: [],\n ...overrides,\n});\n\n/**\n * Writes the focus each step opens on, synchronously, as the step is entered.\n *\n * Both grids highlight a row from their first frame, and both scope toggles read\n * the focused id out of this store the moment a key arrives. Terminal input is not\n * ordered against render-phase effects, so a focus seeded after mount can lose to\n * a keystroke that was already buffered — which is why entry seeds it instead.\n */\nfunction seedFocusForStep(\n step: WizardStep,\n actions: Pick<WizardState, \"seedFocusedSkillForActiveDomain\" | \"seedFocusedAgent\">,\n): void {\n if (step === \"build\") actions.seedFocusedSkillForActiveDomain();\n if (step === \"agents\") actions.seedFocusedAgent();\n}\n\nexport const useWizardStore = create<WizardState>((set, get) => ({\n ...createInitialState(),\n\n setStep: (step) => {\n set((state) => ({\n step,\n history: [...state.history, state.step],\n }));\n seedFocusForStep(step, get());\n },\n\n setApproach: (approach) => set({ approach }),\n\n selectStack: (stackId) =>\n set({\n selectedStackId: stackId,\n domainSelections: {},\n _stackDomainSelections: null,\n selectedDomains: [],\n skillConfigs: [],\n selectedAgents: [],\n agentConfigs: [],\n currentDomainIndex: 0,\n stackAction: null,\n }),\n\n setStackAction: (action) => set({ stackAction: action }),\n\n startFromScratch: () => {\n // selectStack(null) wipes the agent selections along with the stack's, so the\n // global ones are put back rather than left as collateral.\n get().selectStack(null);\n get().setApproach(\"scratch\");\n\n const { globalAgentPreselections, globalPreselections } = get();\n if (globalAgentPreselections) {\n set({\n selectedAgents: globalAgentPreselections.agents,\n agentConfigs: globalAgentPreselections.configs,\n });\n }\n if (globalPreselections?.length) {\n get().populateFromSkillIds(\n globalPreselections.map((skill) => skill.id),\n globalPreselections,\n );\n }\n\n // Additive: a scratch domain the preselections already brought stays on.\n for (const domain of DEFAULT_SCRATCH_DOMAINS) {\n if (!get().selectedDomains.includes(domain)) get().toggleDomain(domain);\n }\n },\n\n populateFromSkillIds: (skillIds, savedConfigs) =>\n set(() => {\n const domainSelections: DomainSelections = {};\n const resolvedSkillIds: SkillId[] = [];\n const unresolvableSkillIds: SkillId[] = [];\n\n for (const skillId of skillIds) {\n const resolved = resolveSkillForPopulation(skillId);\n if (!resolved) {\n unresolvableSkillIds.push(skillId);\n continue;\n }\n\n const { domain, subcat, techId } = resolved;\n if (addToDomainSelections(domainSelections, domain, subcat, techId)) {\n resolvedSkillIds.push(techId);\n }\n }\n\n const selectedDomains = orderDomains(typedKeys<Domain>(domainSelections));\n\n const skillConfigs: SkillConfig[] = resolvedSkillIds.map((id) =>\n buildSkillConfigForId(id, savedConfigs),\n );\n\n // Preserve excluded entries so they flow through to wizard result.\n // Allow an excluded tombstone to coexist with an active entry for the\n // same skill id at a different scope (render layer computes secondaryScope\n // from the pair) — and only at a different one.\n const excludedConfigs = skillTombstonesOutsideRebuild(savedConfigs ?? [], skillConfigs);\n\n return {\n domainSelections,\n _stackDomainSelections: structuredClone(domainSelections),\n selectedDomains,\n skillConfigs: [...skillConfigs, ...excludedConfigs],\n unresolvableSkillIds,\n };\n }),\n\n toggleDomain: (domain) =>\n set((state) => {\n const isSelected = state.selectedDomains.includes(domain);\n if (isSelected) {\n const { [domain]: _removed, ...remainingSelections } = state.domainSelections;\n const removedSkillIds = _removed\n ? collectSkillIdsFromSelections(_removed)\n : new Set<SkillId>();\n\n return {\n selectedDomains: state.selectedDomains.filter((d) => d !== domain),\n domainSelections: remainingSelections,\n // Global-scope edit: no overlay, so deselecting a domain uninstalls its skills cleanly\n // rather than tombstoning them in the global config.\n skillConfigs: applySkillRemoval(\n state.skillConfigs,\n removedSkillIds,\n state.isEditingFromGlobalScope ? null : state.installedSkillConfigs,\n ),\n };\n }\n\n // Restore stack selections for this domain if a stack snapshot exists\n const stackSelections = state._stackDomainSelections?.[domain];\n if (stackSelections) {\n const restoredSkillIds = [...collectSkillIdsFromSelections(stackSelections)];\n\n return {\n selectedDomains: orderDomains([...state.selectedDomains, domain]),\n domainSelections: {\n ...state.domainSelections,\n [domain]: structuredClone(stackSelections),\n },\n skillConfigs: restoreSkillConfigs(state.skillConfigs, restoredSkillIds),\n };\n }\n\n return {\n selectedDomains: orderDomains([...state.selectedDomains, domain]),\n };\n }),\n\n toggleTechnology: (domain, category, technology, exclusive) =>\n set((state) => {\n const installed = state.installedSkillConfigs ?? [];\n const currentSelections = state.domainSelections[domain]?.[category] || [];\n const isSelected = currentSelections.includes(technology);\n\n // Block a GLOBAL-OWNED entry from being changed at project scope. On a SELECT only the\n // active-global arm applies (an inherited-global row is read-only in both directions); the\n // stale-collapse arm is a deselect guard, kept behind isSelected so the re-select restore\n // path (reconcileSkillConfigs rebuilds `[P][G]`) still runs. The project half of a\n // persisted `[P][G]` pair is project-owned and passes both ways.\n const isGlobalLocked = isSelected\n ? isGloballyLockedSkill(installed, state.skillConfigs, technology)\n : hasGlobalActive(installed, technology);\n if (isGlobalLocked && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_SKILLS_LOCKED };\n }\n\n if (isSelected) {\n const categoryDef = matrix.categories[category];\n if (categoryDef?.exclusive && categoryDef.required) {\n const categorySkillCount = typedValues(matrix.skills).filter(\n (s) => s.category === category,\n ).length;\n if (categorySkillCount <= 1) {\n return { toastMessage: TOAST_MESSAGES.ONLY_SKILL_IN_CATEGORY };\n }\n }\n }\n\n // In exclusive mode, selecting a new skill replaces the current one. Block if that would\n // implicitly drop a skill this scope may not drop THIS WAY: a radio swap must never\n // tombstone a global install, and never unmask one either.\n if (exclusive && !isSelected) {\n const wouldDropLockedSkill = currentSelections.some((selectedId) =>\n blocksExclusiveSwap(installed, state.skillConfigs, selectedId),\n );\n if (wouldDropLockedSkill && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_SKILLS_LOCKED };\n }\n }\n\n let newSelections: SkillId[];\n if (exclusive) {\n newSelections = isSelected ? [] : [technology];\n } else {\n newSelections = isSelected\n ? currentSelections.filter((t) => t !== technology)\n : [...currentSelections, technology];\n }\n\n const removed = currentSelections.filter((id) => !newSelections.includes(id));\n const added = newSelections.filter((id) => !currentSelections.includes(id));\n\n const skillConfigs = reconcileSkillConfigs(\n state.skillConfigs,\n added,\n removed,\n state.installedSkillConfigs,\n state.isEditingFromGlobalScope,\n );\n\n // A dual-scope deselect collapses to a single active inherited-global entry, so the\n // skill is still genuinely active. Keep it in the domain selection (mirroring what a\n // save-and-reopen re-derives via populateFromSkillIds) instead of dropping it purely\n // because it left the pre-removal selection array. A genuine full removal leaves no\n // active entry and still drops normally.\n const stillActiveAfterRemoval =\n isSelected &&\n !newSelections.includes(technology) &&\n (hasProjectActive(skillConfigs, technology) || hasGlobalActive(skillConfigs, technology));\n const resolvedSelections = stillActiveAfterRemoval\n ? [...newSelections, technology]\n : newSelections;\n\n return {\n skillConfigs,\n domainSelections: {\n ...state.domainSelections,\n [domain]: {\n ...state.domainSelections[domain],\n [category]: resolvedSelections,\n },\n },\n };\n }),\n\n nextDomain: () => {\n const state = get();\n if (state.currentDomainIndex < state.selectedDomains.length - 1) {\n set({\n currentDomainIndex: state.currentDomainIndex + 1,\n });\n get().seedFocusedSkillForActiveDomain();\n return true;\n }\n return false;\n },\n\n prevDomain: () => {\n const state = get();\n if (state.currentDomainIndex > 0) {\n set({\n currentDomainIndex: state.currentDomainIndex - 1,\n });\n get().seedFocusedSkillForActiveDomain();\n return true;\n }\n return false;\n },\n\n setCurrentDomainIndex: (index) => {\n const state = get();\n if (index >= 0 && index < state.selectedDomains.length) {\n set({ currentDomainIndex: index });\n get().seedFocusedSkillForActiveDomain();\n }\n },\n\n toggleShowLabels: () => set((state) => ({ showLabels: !state.showLabels })),\n\n deriveInstallMode: (): InstallMode => {\n const { skillConfigs } = get();\n return sharedDeriveInstallMode(skillConfigs);\n },\n\n toggleSkillScope: (skillId) =>\n set((state) => {\n // Unreachable in production and deliberately uncovered here. `toggleFocusedScope` in\n // components/wizard/wizard.tsx is the only caller of this action and of toggleAgentScope,\n // and it emits the refusal toast and returns before either is called — so a unit spec\n // aimed at this line pins a state the product cannot enter, and stays green if the\n // component guard is deleted. The refusal a user can reach is asserted through the `s`\n // key in e2e/interactive/edit-wizard-navigation.e2e.test.ts, beside the project-scope\n // toggle it must not swallow.\n if (state.isEditingFromGlobalScope) return state;\n\n const config = state.skillConfigs.find((sc) => sc.id === skillId && !sc.excluded);\n if (!config) return state;\n\n // Guard: block project eject → global when global eject already exists (would overwrite)\n if (wouldOverwriteGlobalEject(config, state.skillConfigs, state.installedSkillConfigs)) {\n return { toastMessage: TOAST_MESSAGES.ALREADY_EJECTED_AT_GLOBAL };\n }\n\n // A real global install exists when the snapshot carries EITHER an active global entry\n // or a global tombstone: a tombstone means the skill IS installed globally and this\n // project was overriding it. Both must re-create the tombstone on a G→P toggle, so an\n // in-session collapse→`s` restores a genuine `[P][G]` pair rather than a bare `[P]` that\n // loses the still-real global install. (Matches the agent-path derivation, which already\n // counts tombstones.)\n const wasInstalledGlobally =\n state.installedSkillConfigs?.some((sc) => sc.id === skillId && sc.scope === \"global\") ??\n false;\n const newScope = config.scope === \"project\" ? (\"global\" as const) : (\"project\" as const);\n\n const rescoped = state.skillConfigs.map((sc) =>\n sc.id === skillId && !sc.excluded ? { ...sc, scope: newScope } : sc,\n );\n\n if (newScope === \"project\") {\n // Moving global → project: add the excluded global tombstone if not already there.\n // Gated on wasInstalledGlobally so fresh init toggles don't create spurious tombstones.\n const needsTombstone =\n wasInstalledGlobally && !rescoped.some((sc) => sc.id === skillId && sc.excluded);\n return {\n skillConfigs: needsTombstone\n ? [\n ...rescoped,\n { id: skillId, scope: \"global\" as const, excluded: true, origin: config.origin },\n ]\n : rescoped,\n };\n }\n\n // Moving project → global: always drop any excluded global tombstone for this id.\n // An active entry at global scope supersedes any tombstone at the same scope — the\n // invariant \"no active + tombstone at the same (id, scope)\" must hold. Unconditional\n // removal (not gated on wasInstalledGlobally) heals the case where the prior\n // G→P produced a tombstone that installedSkillConfigs-derived wasInstalledGlobally\n // cannot see (because its `!sc.excluded` filter ignores the tombstone itself).\n return { skillConfigs: rescoped.filter((sc) => !(sc.id === skillId && sc.excluded)) };\n }),\n\n setFocusedSkillId: (id) => set({ focusedSkillId: id }),\n\n seedFocusedSkillForActiveDomain: () => {\n const state = get();\n // Mirror the build-step renderer's domain fallback (use-build-step-props.ts):\n // when no domain is selected the grid still renders FALLBACK_DOMAIN and\n // highlights a cell, so the seed must resolve that same domain — never null\n // while a cell is visibly focused — or the cold `s` toggle no-ops on a desync.\n const domain = state.getCurrentDomain() ?? FALLBACK_DOMAIN;\n const categories = buildCategoriesForDomain(\n domain,\n state.getAllSelectedTechnologies(),\n undefined,\n state.skillConfigs,\n );\n set({ focusedSkillId: categories[0]?.options[0]?.id ?? null });\n },\n\n setInstallMode: (skillId, mode, scope) =>\n set((state) => {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- SkillId is a string union with no empty member, so the type rules this out; the guard is for a runtime caller handing over an empty id\n if (!skillId) {\n warn(\"Ignoring setInstallMode call with empty skillId\");\n return state;\n }\n // Block a GLOBAL-OWNED slot from being reconfigured at project scope — the authority the\n // Sources grid already enforces by rendering that row inert, held here so it does not rest\n // on the UI alone. Silent: the grid never offers the row, so a refused call is a caller\n // reaching past it.\n if (\n isInheritedGlobalSlot(state.installedSkillConfigs, skillId, scope) &&\n !state.isEditingFromGlobalScope\n ) {\n return state;\n }\n const origin = sourceForInstallMode(getSkillById(skillId), mode);\n return {\n skillConfigs: withActiveEntryOrigin(state.skillConfigs, skillId, scope, origin),\n };\n }),\n\n toggleInfo: () => set((state) => ({ showInfo: !state.showInfo })),\n\n setToastMessage: (message) => set({ toastMessage: message }),\n\n goBack: () => {\n const previousStep = get().history.at(-1);\n if (previousStep === undefined) return;\n set((state) => ({ step: previousStep, history: state.history.slice(0, -1) }));\n seedFocusForStep(previousStep, get());\n },\n\n toggleAgent: (agent) =>\n set((state) => {\n const installed = state.installedAgentConfigs ?? [];\n\n // A live dual-scope `[P][G]` row is locked to `s`: the selection key is inert on it and\n // emits the same global-locked toast an inherited-global row shows. Mirrors the skill-side\n // dual-scope arm in toggleTechnology — `s` alone collapses and restores the pair.\n const isDualScopePairRow =\n !state.isEditingFromGlobalScope && isDualScopeAgentPair(state.agentConfigs, agent);\n if (isDualScopePairRow) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_AGENTS_LOCKED };\n }\n\n const isInheritedGlobalReselect =\n !state.isEditingFromGlobalScope &&\n agentHasGlobalActive(state.agentConfigs, agent) &&\n !agentHasProjectActive(state.agentConfigs, agent) &&\n !state.selectedAgents.includes(agent) &&\n agentHasGlobalTombstone(installed, agent);\n if (isInheritedGlobalReselect) {\n return restoreDualScopeAgent(state.selectedAgents, state.agentConfigs, agent);\n }\n\n // Block a globally-installed agent from being changed at project scope. Mirrors the\n // skill-path guard in toggleTechnology: fire on an active global entry in the snapshot,\n // or on a snapshot tombstone paired with a LIVE plain active global entry (the stale\n // state a persisted `[P][G]` reaches after an in-session collapse). A freshly-added\n // global agent (absent from the snapshot) stays freely deselectable.\n const isActiveGlobal =\n agentHasGlobalActive(installed, agent) ||\n (agentHasGlobalTombstone(installed, agent) &&\n agentHasGlobalActive(state.agentConfigs, agent));\n if (isActiveGlobal && !state.isEditingFromGlobalScope) {\n return { toastMessage: TOAST_MESSAGES.GLOBAL_AGENTS_LOCKED };\n }\n\n const isInList = state.selectedAgents.includes(agent);\n const hasExcludedTombstone = state.agentConfigs.some(\n (ac) => ac.name === agent && ac.excluded,\n );\n // An agent is effectively selected only if it's in the list WITHOUT an excluded tombstone.\n // When it has an excluded tombstone, it's visually \"off\" and toggling means \"re-enable\".\n const isSelected = isInList && !hasExcludedTombstone;\n\n return {\n selectedAgents: nextSelectedAgents(state.selectedAgents, agent, { isSelected, isInList }),\n agentConfigs: applyAgentToggle(state.agentConfigs, agent, isSelected),\n };\n }),\n\n toggleAgentScope: (agentName) =>\n set((state) => {\n // Unreachable in production for the same reason as toggleSkillScope's — see it.\n if (state.isEditingFromGlobalScope) return state;\n\n const config = state.agentConfigs.find((ac) => ac.name === agentName && !ac.excluded);\n if (!config) return state;\n\n // Counts a global tombstone as \"installed globally\" (a tombstone means a real global\n // install this project overrides), so an in-session collapse→`s` restores a genuine\n // `[P][G]` pair.\n const wasInstalledGlobally =\n state.installedAgentConfigs?.some((ac) => ac.name === agentName && ac.scope === \"global\") ??\n false;\n const newScope = config.scope === \"project\" ? (\"global\" as const) : (\"project\" as const);\n\n const rescoped = state.agentConfigs.map((ac) =>\n ac.name === agentName && !ac.excluded ? { ...ac, scope: newScope } : ac,\n );\n\n if (newScope === \"project\") {\n // Moving global → project: add the excluded global tombstone if not already there.\n // Gated on wasInstalledGlobally so fresh init toggles don't create spurious tombstones.\n const needsTombstone =\n wasInstalledGlobally && !rescoped.some((ac) => ac.name === agentName && ac.excluded);\n return {\n agentConfigs: needsTombstone\n ? [...rescoped, { name: agentName, scope: \"global\" as const, excluded: true }]\n : rescoped,\n };\n }\n\n // Moving project → global: always drop any excluded global tombstone for this name.\n // Symmetric with toggleSkillScope — see its comment for the invariant.\n return { agentConfigs: rescoped.filter((ac) => !(ac.name === agentName && ac.excluded)) };\n }),\n\n setFocusedAgentId: (id) => set({ focusedAgentId: id }),\n\n seedFocusedAgent: () => {\n if (get().focusedAgentId !== null) return;\n set({ focusedAgentId: firstFocusableAgent() });\n },\n\n preselectAgentsFromDomains: () =>\n set((state) => {\n // A stack declares its own roster, and that declaration is the whole of what\n // choosing it means. Deriving one from the domains would run later in the step\n // order and overwrite it, so the from-scratch path stops here.\n if (state.selectedStackId) return state;\n\n // Deduped: several selected domains each roster the shared `reviewer`.\n const sorted = [\n ...new Set(state.selectedDomains.flatMap((domain) => DOMAIN_AGENTS[domain] ?? [])),\n ].sort();\n const roster = new Set(sorted);\n const merged = sorted.map((name) => buildAgentConfigForName(name, state.agentConfigs));\n const rebuiltSlots = filledAgentSlots(merged);\n const retained = state.agentConfigs.filter((ac) =>\n survivesRosterRebuild(ac, roster, rebuiltSlots),\n );\n return {\n selectedAgents: sorted,\n agentConfigs: [...merged, ...retained],\n };\n }),\n\n preselectAgentsFromStack: (stackAgents) =>\n set((state) => {\n const savedConfigs = state.globalAgentPreselections?.configs ?? [];\n const globalAgents = state.globalAgentPreselections?.agents ?? [];\n const mergedAgents = [...new Set([...stackAgents, ...globalAgents])].sort();\n const merged = mergedAgents.map((name) => buildAgentConfigForName(name, savedConfigs));\n // Preserve dual-scope tombstones, same invariant as preselectAgentsFromDomains.\n const excludedConfigs = agentTombstonesOutsideRebuild(savedConfigs, merged);\n return {\n selectedAgents: mergedAgents,\n agentConfigs: [...merged, ...excludedConfigs],\n };\n }),\n\n reset: () => set(createInitialState()),\n\n getAllSelectedTechnologies: () => {\n return typedValues(get().domainSelections).flatMap(flattenCategorySelections);\n },\n\n getSelectedTechnologiesPerDomain: () => {\n const entries = typedEntries(get().domainSelections)\n .map(([domain, domainSel]) => [domain, flattenCategorySelections(domainSel)] as const)\n .filter(([, techs]) => techs.length > 0);\n return typedFromEntries(entries);\n },\n\n getCurrentDomain: () => {\n const state = get();\n return state.selectedDomains[state.currentDomainIndex] || null;\n },\n\n getTechnologyCount: () => {\n return get().getAllSelectedTechnologies().length;\n },\n\n getStepProgress: () => {\n const state = get();\n const isStackDefaults =\n state.approach === \"stack\" && !!state.selectedStackId && state.stackAction === \"defaults\";\n const skippedSteps: WizardStep[] = isStackDefaults ? [\"build\", \"sources\", \"agents\"] : [];\n const precedingSteps = stepsBefore(getActiveStepFlow(), state.step);\n const completedSteps = precedingSteps.filter((step) => !skippedSteps.includes(step));\n return { completedSteps, skippedSteps };\n },\n\n canGoToNextDomain: () => {\n const state = get();\n return state.currentDomainIndex < state.selectedDomains.length - 1;\n },\n\n canGoToPreviousDomain: () => {\n const state = get();\n return state.currentDomainIndex > 0;\n },\n\n buildSourceRows: () => {\n const state = get();\n const selectedTechnologies = get().getAllSelectedTechnologies();\n const { skillConfigs } = state;\n\n // Include inherited global skills (in skillConfigs but not in domainSelections)\n const selectedSet = new Set(selectedTechnologies);\n const inheritedSkillIds = skillConfigs\n .filter((sc) => !sc.excluded && !selectedSet.has(sc.id))\n .map((sc) => sc.id);\n const allActiveIds = new Set([...inheritedSkillIds, ...selectedTechnologies]);\n const excludedGlobalIds = skillConfigs\n .filter((sc) => isGlobalTombstone(sc) && !allActiveIds.has(sc.id))\n .map((sc) => sc.id);\n // Every skill with a LIVE config entry or selection. Internally disjoint by construction, so no\n // skill contributes live rows twice.\n const liveSkillIds = [...inheritedSkillIds, ...selectedTechnologies, ...excludedGlobalIds];\n // The slot baseline additions are measured against. Each emitted row checks its OWN\n // `(id, scope)` slot, so a skill adopted at a second scope registers as added on the newly\n // occupied row alone — the same classification the confirm step's computeScopeDiff makes.\n const installedSkillSlots = collectInstalledSkillSlots(state.installedSkillConfigs);\n const resolveRowInputs = (id: SkillId) => resolveSkillRowInputs(id, skillConfigs);\n\n const liveRows: SourceRow[] = liveSkillIds.flatMap((id) => {\n const { skillId, configEntry, options } = resolveRowInputs(id);\n return classifySkillSourceRows(skillId, options, {\n configEntry,\n installedSkillConfigs: state.installedSkillConfigs,\n isEditingFromGlobalScope: state.isEditingFromGlobalScope,\n installedSkillSlots,\n });\n });\n\n // Slots the session emptied. A skill may appear in BOTH lists — a collapsed\n // dual-scope pair keeps its surviving global row and gains a project pending-removal row — so\n // rows are no longer disjoint per SKILL. They stay disjoint per `(id, scope)` SLOT: a snapshot\n // slot a live row already renders never gets a removal row on top of it.\n const pendingRemovalRows: SourceRow[] = collectRemovedInstalledEntries(\n state.installedSkillConfigs,\n skillConfigs,\n )\n .filter((entry) => !isSlotAlreadyRendered(liveRows, entry))\n .map((entry) => {\n const { skillId, options } = resolveRowInputs(entry.id);\n return toPendingRemovalRow(skillId, options, entry);\n });\n\n // Stable sort by scope tier: global readOnly, global editable, then project. Pending-removal\n // rows carry no dedicated tier — they sort by their own scope and, being appended last, trail\n // inline within that scope section. Matches source-grid grouping so navigation indices align\n // with render order.\n const rows = [...liveRows, ...pendingRemovalRows];\n rows.sort((a, b) => sourceRowSortTier(a) - sourceRowSortTier(b));\n\n return rows;\n },\n}));\n\n/**\n * Runs a change the user triggered from a painted frame, with anything it warned about shown as\n * a toast.\n *\n * `warn()` writes to stderr, and stderr under a mounted wizard is where a message goes to be\n * lost: the frame is sized to the terminal and starts at the top of an empty buffer, so the next\n * repaint pushes the line off the top of the screen. That is the same loss the startup band\n * exists to prevent before the mount, and the one `assertWizardScreenIsWhollyVisible` in\n * `e2e/pages/base-step.ts` fails the interactive suite on. The band is deliberately the wrong\n * home for it — a message raised mid-session is not a startup message — so it takes the\n * wizard's own transient channel instead.\n *\n * FOR CALL SITES PAST THE MOUNT ONLY, which is why the routing lives at the call site rather\n * than inside the actions themselves. {@link WizardState.populateFromSkillIds} and\n * {@link WizardState.startFromScratch} are each reached from hydration as well, where buffering is\n * already on and the band IS the right home; a window opened around either one there would reset\n * the buffer the load filled and close it again before hydration had finished speaking into it.\n */\nexport function showWarningsAsToast(change: () => void): void {\n enableBuffering();\n try {\n change();\n const warnings = drainBuffer();\n if (warnings.length > 0) {\n useWizardStore.getState().setToastMessage(skillsLeftOutToast(warnings.length));\n }\n } finally {\n disableBuffering();\n }\n}\n\nexport type HydrateOptions = {\n initialStep?: WizardStep;\n initialDomains?: Domain[];\n initialAgents?: AgentName[];\n installedSkillIds?: SkillId[];\n installedSkillConfigs?: SkillConfig[];\n installedAgentConfigs?: AgentScopeConfig[];\n isEditingFromGlobalScope?: boolean;\n};\n\n/**\n * Imperatively hydrates the wizard store before the first render.\n *\n * Must be called BEFORE render(<Wizard ... />) so React captures the correct\n * initial snapshot on the first frame. Running this inside a render-phase hook\n * causes a one-frame flash of the default \"stack\" step before the jump to the\n * intended step is committed to stdout.\n */\nexport function hydrateWizardStore(options: HydrateOptions): void {\n useWizardStore.setState(createInitialState());\n\n const { initialStep } = options;\n const isEditFlow = initialStep !== undefined;\n if (isEditFlow) {\n hydrateForEdit(initialStep, options);\n } else {\n hydrateForInit(options);\n }\n}\n\n/** Edit flow: populate from installed skills, then jump straight to the saved step. */\nfunction hydrateForEdit(initialStep: WizardStep, options: HydrateOptions): void {\n const {\n initialDomains,\n initialAgents,\n installedSkillIds,\n installedSkillConfigs,\n installedAgentConfigs,\n isEditingFromGlobalScope,\n } = options;\n\n if (installedSkillIds?.length) {\n useWizardStore.getState().populateFromSkillIds(installedSkillIds, installedSkillConfigs);\n }\n\n useWizardStore.setState({\n isInitMode: false,\n // Jump directly to initialStep with empty history. The user is starting\n // fresh at this step — no prior steps to navigate back through.\n step: initialStep,\n history: [],\n approach: \"scratch\",\n // Saved domains/agents from config override what populateFromSkillIds derived.\n ...(initialDomains?.length ? { selectedDomains: initialDomains, currentDomainIndex: 0 } : {}),\n ...(initialAgents?.length ? { selectedAgents: initialAgents } : {}),\n ...(initialAgents?.length && installedAgentConfigs?.length\n ? { agentConfigs: installedAgentConfigs }\n : {}),\n // Snapshot installed configs for diff rendering in SkillAgentSummary\n ...(installedSkillConfigs?.length || installedAgentConfigs?.length\n ? {\n installedSkillConfigs: installedSkillConfigs ?? null,\n installedAgentConfigs: installedAgentConfigs ?? null,\n }\n : {}),\n ...(isEditingFromGlobalScope ? { isEditingFromGlobalScope: true } : {}),\n });\n\n useWizardStore.getState().seedFocusedSkillForActiveDomain();\n}\n\n/** Where an init opens when the loaded source offers no stack to choose. */\nconst FIRST_STEP_WITHOUT_STACKS: WizardStep = \"domains\";\n\n/**\n * Init flow: skills are not populated yet — the stack step runs first, so saved\n * selections are stored as preselections for stack-selection.tsx to merge after\n * the stack/scratch choice (selectStack wipes agents).\n *\n * Unless the loaded source offers no stacks at all — a custom marketplace that\n * ships none, since the built-in catalogue stands in for the default public\n * marketplace alone. That step would then hold nothing but its own \"Start from\n * scratch\" row, so the wizard opens where that row leads, having done what\n * pressing it does.\n */\nfunction hydrateForInit(options: HydrateOptions): void {\n const {\n initialDomains,\n initialAgents,\n installedSkillConfigs,\n installedAgentConfigs,\n isEditingFromGlobalScope,\n } = options;\n\n useWizardStore.setState({\n isInitMode: true,\n ...(initialDomains?.length ? { selectedDomains: initialDomains, currentDomainIndex: 0 } : {}),\n ...(initialAgents?.length ? { selectedAgents: initialAgents } : {}),\n ...(initialAgents?.length && installedAgentConfigs?.length\n ? { agentConfigs: installedAgentConfigs }\n : {}),\n // Snapshot installed configs for diff rendering in SkillAgentSummary\n ...(installedSkillConfigs?.length || installedAgentConfigs?.length\n ? {\n installedSkillConfigs: installedSkillConfigs ?? null,\n installedAgentConfigs: installedAgentConfigs ?? null,\n }\n : {}),\n ...(isEditingFromGlobalScope ? { isEditingFromGlobalScope: true } : {}),\n ...(installedSkillConfigs?.length ? { globalPreselections: installedSkillConfigs } : {}),\n ...(initialAgents?.length || installedAgentConfigs?.length\n ? {\n globalAgentPreselections: {\n agents: initialAgents ?? [],\n configs: installedAgentConfigs ?? [],\n },\n }\n : {}),\n });\n\n if (!offersStacks()) {\n useWizardStore.getState().startFromScratch();\n // Nothing behind this step: the stack step it would have been pushed onto\n // never rendered, so the history it would have gone into stays empty.\n useWizardStore.setState({ step: FIRST_STEP_WITHOUT_STACKS, history: [] });\n }\n\n useWizardStore.getState().seedFocusedSkillForActiveDomain();\n}\n","import { sortBy } from \"remeda\";\nimport { typedValues } from \"../../utils/typed-object.js\";\nimport type { CategorySelections, Domain, SkillId } from \"../../types/index.js\";\nimport type { SkillConfig } from \"../../types/config.js\";\nimport { getAvailableSkills, getCellState, getUnmetRequiredBy } from \"../matrix/index.js\";\nimport { matrix, getSkillById } from \"../matrix/matrix-provider.js\";\nimport { deriveScopeBadges } from \"./scope-diff.js\";\nimport type { CategoryRow, CategoryOption } from \"../../components/wizard/category-grid.js\";\n\n/**\n * Whether every required category on the build grid holds a selection — and, when one does not,\n * the single sentence naming it.\n *\n * A union rather than a `boolean` beside an optional `message`, because the shape it replaced\n * answered `valid: true` on BOTH branches. That is the same defect `validateSelection` in\n * `matrix/matrix-resolver.ts` carries a note against: a literal `true` beside a populated message\n * is a comment with a type annotation, and the first `if (validation.valid)` written over it\n * takes the happy path on every failure there is. The union makes \"invalid\" and \"carries a\n * message\" one fact, so neither half can be stated without the other.\n */\nexport type BuildStepValidation = { valid: true } | { valid: false; message: string };\n\n/**\n * ADVISORY, and deliberately so — `valid: false` names an empty required category, it does not\n * refuse to leave the step.\n *\n * Every other validation the wizard runs is advisory: `validateSelection` reports genuine\n * conflicts, unmet requirements and exclusivity breaches, `ValidationError` is typed as\n * non-blocking, and `BaseCommand.reportValidationErrors` prints the lot as warnings that no exit\n * code turns on. Blocking here would make the mildest constraint the only fatal one, and it would\n * strand anyone who opened a domain to reach a skill in one of its OPTIONAL categories — the\n * required row would have no bearing on what they came for and no way past it but ESC.\n *\n * The first empty required category is the whole answer: the toast that carries it paints one\n * row, and the next press reports the next one.\n */\nexport function validateBuildStep(\n categories: CategoryRow[],\n selections: CategorySelections,\n): BuildStepValidation {\n const emptyRequired = categories.find(\n (category) => category.required && !selections[category.id]?.length,\n );\n if (!emptyRequired) return { valid: true };\n\n return {\n valid: false,\n message: `No skills selected in ${emptyRequired.displayName} (required category)`,\n };\n}\n\n// Build CategoryRow[] from matrix for a domain\nexport function buildCategoriesForDomain(\n domain: Domain,\n allSelections: SkillId[],\n installedSkillIds?: SkillId[],\n skillConfigs?: SkillConfig[],\n): CategoryRow[] {\n const categories = sortBy(\n typedValues(matrix.categories).filter((cat) => cat.domain === domain),\n (cat) => cat.order,\n );\n\n const categoryRows: CategoryRow[] = categories.map((cat) => {\n const skillOptions = getAvailableSkills(cat.id, allSelections);\n\n // Sort options by displayName so the grid order is deterministic across\n // machines and source types (readdir/insertion order is otherwise unstable).\n // Lowercased ordinal comparison keeps the order locale-independent.\n const sortedOptions = sortBy(skillOptions, (skill) =>\n getSkillById(skill.id).displayName.toLowerCase(),\n );\n\n const options: CategoryOption[] = sortedOptions.map((skill) => {\n const activeConfig = skillConfigs?.find((sc) => sc.id === skill.id && !sc.excluded);\n const excludedConfig = skillConfigs?.find((sc) => sc.id === skill.id && sc.excluded);\n const { secondaryScope } = deriveScopeBadges(activeConfig, excludedConfig);\n const local = getSkillById(skill.id).local;\n const requiredBy = skill.selected ? undefined : getUnmetRequiredBy(skill.id, allSelections);\n return {\n id: skill.id,\n // The cell verdict, not the raw advisory: inside a pick-one category\n // the shared semantics forgive what the swap resolves.\n state: getCellState(skill.id, allSelections),\n selected: skill.selected,\n ...(local !== undefined && { local }),\n installed: installedSkillIds?.includes(skill.id) || false,\n ...(activeConfig?.scope !== undefined && { scope: activeConfig.scope }),\n ...(secondaryScope !== undefined && { secondaryScope }),\n ...(activeConfig?.origin !== undefined && { source: activeConfig.origin }),\n hasUnmetRequirements: skill.hasUnmetRequirements,\n ...(skill.unmetRequirementsReason !== undefined && {\n unmetRequirementsReason: skill.unmetRequirementsReason,\n }),\n ...(requiredBy !== undefined && { requiredBy }),\n };\n });\n\n return {\n id: cat.id,\n displayName: cat.displayName,\n required: cat.required,\n exclusive: cat.exclusive,\n options,\n };\n });\n\n return categoryRows.filter((row) => row.options.length > 0);\n}\n","import type { AgentScopeConfig, SkillConfig, SkillScope } from \"../../types/config\";\nimport type { AgentName, SkillId } from \"../../types/index\";\n\nexport type ScopeDiffInput = {\n currentSkills: SkillConfig[];\n currentAgents: AgentScopeConfig[];\n installedSkillConfigs: SkillConfig[] | null;\n installedAgentConfigs: AgentScopeConfig[] | null;\n isInitMode: boolean;\n};\n\nexport type DiffRowStatus = \"added\" | \"mode-changed\" | \"removed\" | \"unchanged\";\n\nexport type SkillDiffRow = {\n id: SkillId;\n source: string;\n status: DiffRowStatus;\n};\n\nexport type AgentDiffRow = {\n name: AgentName;\n status: Exclude<DiffRowStatus, \"mode-changed\">;\n};\n\nexport type ScopeDiff = {\n projectSkillRows: SkillDiffRow[];\n globalSkillRows: SkillDiffRow[];\n projectAgentRows: AgentDiffRow[];\n globalAgentRows: AgentDiffRow[];\n hasContent: boolean;\n};\n\n/**\n * Computes the per-scope diff rows the confirm-step summary renders.\n *\n * The diff baseline keeps tombstones as first-class entries. A tombstone\n * occupies the (id, scope) slot: it means \"the global install still exists but\n * is silenced at project scope\" — the dual-scope indicator. Treating the slot\n * as occupied — for both the previous-key set (isNew detection) and removal\n * (slot-occupancy match) — prevents a dual-scope G→P toggle from rendering a\n * spurious `-` at Global or a spurious `+` at Global on the next edit\n * when the stored tombstone is re-read; mode-change (`~`) tracking filters to active baseline\n * entries because tombstones don't represent a live install.\n *\n * Occupying a slot is not the same as filling it, which is why removal reads the\n * ACTIVE baseline alone and every re-surfaced global row is deduped against the\n * inherited set. A baseline tombstone masks an install rather than being one, and\n * an inherited global can be admitted as inherited and matched as removed at once.\n */\nexport function computeScopeDiff(input: ScopeDiffInput): ScopeDiff {\n const { currentSkills, currentAgents, installedSkillConfigs, installedAgentConfigs, isInitMode } =\n input;\n\n const projectSkills = currentSkills.filter((s) => s.scope === \"project\" && !s.excluded);\n const globalSkills = currentSkills.filter((s) => s.scope === \"global\" && !s.excluded);\n const projectAgents = currentAgents.filter((a) => a.scope === \"project\" && !a.excluded);\n const globalAgents = currentAgents.filter((a) => a.scope === \"global\" && !a.excluded);\n const excludedGlobalSkills = currentSkills.filter((s) => s.scope === \"global\" && !!s.excluded);\n const excludedGlobalAgents = currentAgents.filter((a) => a.scope === \"global\" && !!a.excluded);\n\n const prevSkillKeySet = installedSkillConfigs\n ? new Set(installedSkillConfigs.map((s) => skillSlotKey(s.id, s.scope)))\n : null;\n const prevSourceMap = installedSkillConfigs\n ? new Map(\n installedSkillConfigs\n .filter((s) => !s.excluded)\n .map((s) => [skillSlotKey(s.id, s.scope), s.origin]),\n )\n : null;\n const prevAgentKeySet = installedAgentConfigs\n ? new Set(installedAgentConfigs.map((a) => agentSlotKey(a.name, a.scope)))\n : null;\n\n // The installs the baseline actually records. Both readers below want this\n // rather than the raw snapshot: `inheritedGlobal*` re-surfaces an active global\n // the project overrides without a tombstone — a historical no-tombstone\n // dual-scope shape, current tombstones being handled by\n // `uniqueExcludedGlobalSkills` — and removal reports only what an install left.\n const activeSkillBaseline = installedSkillConfigs\n ? installedSkillConfigs.filter((s) => !s.excluded)\n : [];\n const activeAgentBaseline = installedAgentConfigs\n ? installedAgentConfigs.filter((a) => !a.excluded)\n : [];\n const inheritedGlobalSkills = activeSkillBaseline.filter(\n (s) =>\n s.scope === \"global\" &&\n !globalSkills.some((g) => g.id === s.id) &&\n !excludedGlobalSkills.some((e) => e.id === s.id) &&\n projectSkills.some((p) => p.id === s.id),\n );\n const inheritedGlobalAgents = activeAgentBaseline.filter(\n (a) =>\n a.scope === \"global\" &&\n !globalAgents.some((g) => g.name === a.name) &&\n !excludedGlobalAgents.some((e) => e.name === a.name) &&\n projectAgents.some((p) => p.name === a.name),\n );\n\n // Slot-occupancy match over the ACTIVE baseline: an installed entry at\n // (id, scope) is considered removed only if nothing — active OR tombstone —\n // occupies that slot in current. A current tombstone at the same key keeps the\n // slot occupied (dual-scope indicator, not a removal).\n //\n // A baseline TOMBSTONE is never a removal candidate. It masks a global install\n // rather than being one, so its slot held nothing to delete and dropping it\n // takes nothing away — a `-` there announces a deletion that never happens.\n const removedSkills = activeSkillBaseline.filter(\n (s) => !currentSkills.some((c) => c.id === s.id && c.scope === s.scope),\n );\n const removedAgents = activeAgentBaseline.filter(\n (a) => !currentAgents.some((c) => c.name === a.name && c.scope === a.scope),\n );\n\n // The Global section renders at most one row per skill, so both of the ways a\n // baseline global entry can re-surface are deduped against the inherited set:\n // `uniqueExcludedGlobalSkills` for the current tombstone, and the removed-global\n // rows below for the removal match. The second is not covered by the first —\n // an inherited global that the project claims WITHOUT a tombstone occupies no\n // slot in current, so it is admitted as inherited and matched as removed at\n // once, and the same skill lands under Global twice.\n const inheritedSkillIdSet = new Set(inheritedGlobalSkills.map((s) => s.id));\n const uniqueExcludedGlobalSkills = excludedGlobalSkills.filter(\n (s) => !inheritedSkillIdSet.has(s.id),\n );\n const allGlobalSkills = [\n ...globalSkills,\n ...inheritedGlobalSkills,\n ...uniqueExcludedGlobalSkills,\n ];\n const inheritedAgentNameSet = new Set(inheritedGlobalAgents.map((a) => a.name));\n const uniqueExcludedGlobalAgents = excludedGlobalAgents.filter(\n (a) => !inheritedAgentNameSet.has(a.name),\n );\n const allGlobalAgents = [\n ...globalAgents,\n ...inheritedGlobalAgents,\n ...uniqueExcludedGlobalAgents,\n ];\n\n const removedGlobalSkills = isInitMode\n ? []\n : removedSkills.filter((s) => s.scope === \"global\" && !inheritedSkillIdSet.has(s.id));\n const removedProjectSkills = removedSkills.filter((s) => s.scope === \"project\");\n const removedGlobalAgents = isInitMode\n ? []\n : removedAgents.filter((a) => a.scope === \"global\" && !inheritedAgentNameSet.has(a.name));\n const removedProjectAgents = removedAgents.filter((a) => a.scope === \"project\");\n\n const projectSkillRows = [\n ...projectSkills.map((s) => classifyDiffRow(s, prevSkillKeySet, prevSourceMap)),\n ...removedProjectSkills.map(toRemovedSkillRow),\n ];\n const globalSkillRows = [\n ...allGlobalSkills.map((s) => classifyDiffRow(s, prevSkillKeySet, prevSourceMap)),\n ...removedGlobalSkills.map(toRemovedSkillRow),\n ];\n const projectAgentRows = [\n ...projectAgents.map((a) => classifyAgentDiffRow(a, prevAgentKeySet)),\n ...removedProjectAgents.map(toRemovedAgentRow),\n ];\n const globalAgentRows = [\n ...allGlobalAgents.map((a) => classifyAgentDiffRow(a, prevAgentKeySet)),\n ...removedGlobalAgents.map(toRemovedAgentRow),\n ];\n\n const hasContent =\n projectSkillRows.length > 0 ||\n globalSkillRows.length > 0 ||\n projectAgentRows.length > 0 ||\n globalAgentRows.length > 0;\n\n return { projectSkillRows, globalSkillRows, projectAgentRows, globalAgentRows, hasContent };\n}\n\n/**\n * The `(id, scope)` SLOT key this module diffs on. Exported so every surface that computes its own\n * session diff — notably the wizard store's Sources tab — keys on the same slot rather than on the\n * id alone, which is what let one skill read as added on one surface and unchanged on the other.\n */\nexport function skillSlotKey(id: SkillId, scope: SkillScope | undefined): string {\n return `${id}:${scope}`;\n}\n\n/**\n * The `(name, scope)` SLOT key for an agent — the agent-side counterpart of {@link skillSlotKey},\n * exported for the same reason. Today only this module diffs agent slots, so there is nothing to\n * disagree with; the helper exists so that a second surface routes through it from the start rather\n * than re-deriving the key on `name` alone, which is precisely how the skill side ended up with\n * the Sources tab and the confirm step disagreeing about whether the same row was added.\n */\nexport function agentSlotKey(name: AgentName, scope: SkillScope | undefined): string {\n return `${name}:${scope}`;\n}\n\nexport type ScopeBadges = {\n scope: SkillScope | undefined;\n secondaryScope: SkillScope | undefined;\n};\n\n/** Bracketed scope badge: `[G]` for global, `[P]` for project. */\nexport function formatScopeTag(scope: SkillScope): \"[G]\" | \"[P]\" {\n return scope === \"global\" ? \"[G]\" : \"[P]\";\n}\n\n/**\n * Derives the primary + secondary scope badges for a row from its active\n * entry and its excluded tombstone. A tombstone at the OTHER scope renders as a\n * secondary badge (`[P][G]`); a same-scope tombstone renders nothing extra.\n */\nexport function deriveScopeBadges(\n activeConfig: { scope: SkillScope } | undefined,\n excludedConfig: { scope: SkillScope } | undefined,\n): ScopeBadges {\n const secondaryScope =\n excludedConfig && activeConfig && excludedConfig.scope !== activeConfig.scope\n ? excludedConfig.scope\n : undefined;\n return { scope: activeConfig?.scope, secondaryScope };\n}\n\n/**\n * Classifies an active skill entry against the baseline: added, mode-changed, or unchanged.\n *\n * The comparison is on `origin`, because that field IS where a skill's install mode is\n * recorded: `eject` means the project's own copy and anything else names the marketplace the\n * plugin comes from. With no second marketplace to move between, an `origin` that changed is a\n * mode that changed, which is what the `~` marker has always meant to a reader.\n */\nfunction classifyDiffRow(\n skill: SkillConfig,\n prevKeySet: Set<string> | null,\n prevSourceMap: Map<string, string> | null,\n): SkillDiffRow {\n const key = skillSlotKey(skill.id, skill.scope);\n const isNew = prevKeySet === null || !prevKeySet.has(key);\n const prevSource = prevSourceMap?.get(key);\n const modeChanged = !isNew && prevSource != null && prevSource !== skill.origin;\n if (modeChanged) {\n return { id: skill.id, source: skill.origin, status: \"mode-changed\" };\n }\n return { id: skill.id, source: skill.origin, status: isNew ? \"added\" : \"unchanged\" };\n}\n\nfunction classifyAgentDiffRow(\n agent: AgentScopeConfig,\n prevKeySet: Set<string> | null,\n): AgentDiffRow {\n const isNew = prevKeySet === null || !prevKeySet.has(agentSlotKey(agent.name, agent.scope));\n return { name: agent.name, status: isNew ? \"added\" : \"unchanged\" };\n}\n\nfunction toRemovedSkillRow(skill: SkillConfig): SkillDiffRow {\n return { id: skill.id, source: skill.origin, status: \"removed\" };\n}\n\nfunction toRemovedAgentRow(agent: AgentScopeConfig): AgentDiffRow {\n return { name: agent.name, status: \"removed\" };\n}\n","import { DOMAIN_ORDER } from \"@workspace/matrix\";\nimport type { Domain } from \"../../types/index.js\";\n\n/**\n * Sort domains into canonical display order: custom domains first (alphabetically),\n * then built-in domains in the shared DOMAIN_ORDER.\n */\nexport function orderDomains(domains: Domain[]): Domain[] {\n const builtInSet = new Set<Domain>(DOMAIN_ORDER);\n return [\n ...domains.filter((d) => !builtInSet.has(d)).sort(),\n ...DOMAIN_ORDER.filter((d) => domains.includes(d)),\n ];\n}\n","import type { CheckboxItem } from \"../../components/wizard/checkbox-grid.js\";\nimport type { AgentName } from \"../../types/index.js\";\n\nexport type AgentItem = CheckboxItem<AgentName>;\n\nexport type AgentGroup = {\n label: string;\n items: AgentItem[];\n};\n\n/**\n * The agents the wizard offers, in the order its grid lists them.\n *\n * Lives here rather than in `step-agents.tsx` because the grid is not the only\n * surface that has to know which row it opens on: the store seeds `focusedAgentId`\n * from the same roster before the first frame, so that a keystroke buffered ahead\n * of that frame resolves the same agent the user is looking at.\n */\nexport const BUILT_IN_AGENT_GROUPS: AgentGroup[] = [\n {\n label: \"Web\",\n items: [\n {\n id: \"web-developer\",\n label: \"Web Developer\",\n description: \"Frontend features, components, TypeScript\",\n },\n { id: \"web-researcher\", label: \"Web Researcher\", description: \"Frontend pattern discovery\" },\n {\n id: \"web-tester\",\n label: \"Web Tester\",\n description: \"Frontend tests, E2E, component tests\",\n },\n ],\n },\n {\n label: \"API\",\n items: [\n {\n id: \"api-developer\",\n label: \"API Developer\",\n description: \"Backend routes, database, middleware\",\n },\n { id: \"api-researcher\", label: \"API Researcher\", description: \"Backend pattern discovery\" },\n {\n id: \"api-tester\",\n label: \"API Tester\",\n description: \"Endpoint, database, and auth flow tests\",\n },\n ],\n },\n {\n label: \"AI\",\n items: [\n {\n id: \"ai-developer\",\n label: \"AI Developer\",\n description: \"RAG pipelines, agent loops, tool calling\",\n },\n {\n id: \"ai-researcher\",\n label: \"AI Researcher\",\n description: \"Prompt, model, and RAG pipeline discovery\",\n },\n {\n id: \"ai-tester\",\n label: \"AI Tester\",\n description: \"LLM mocking, prompt regression, eval harnesses\",\n },\n ],\n },\n {\n label: \"CLI\",\n items: [\n {\n id: \"cli-developer\",\n label: \"CLI Developer\",\n description: \"CLI commands, interactive prompts\",\n },\n { id: \"cli-tester\", label: \"CLI Tester\", description: \"CLI application tests\" },\n {\n id: \"cli-researcher\",\n label: \"CLI Researcher\",\n description: \"CLI command and config pattern discovery\",\n },\n ],\n },\n {\n label: \"Meta\",\n items: [\n {\n id: \"pm\",\n label: \"PM\",\n description: \"Cross-domain implementation specs; domain frameworks via skills\",\n },\n {\n id: \"reviewer\",\n label: \"Reviewer\",\n description: \"Cross-domain code review; domain knowledge via skills\",\n },\n { id: \"agent-summoner\", label: \"Agent Summoner\", description: \"Create and improve agents\" },\n {\n id: \"skill-summoner\",\n label: \"Skill Summoner\",\n description: \"Create technology-specific skills\",\n },\n { id: \"codex-keeper\", label: \"Codex Keeper\", description: \"AI-focused documentation\" },\n ],\n },\n];\n\n/** IDs of all built-in agents for fast lookup. */\nexport const BUILT_IN_AGENT_IDS = new Set<string>(\n BUILT_IN_AGENT_GROUPS.flatMap((group) => group.items.map((agent) => agent.id)),\n);\n\n/**\n * The agent the grid focuses when nothing is focused yet.\n *\n * Always a built-in: a marketplace's own agents are appended after these groups,\n * so the head of the list does not move with the loaded source.\n */\nexport function firstFocusableAgent(): AgentName {\n const first = BUILT_IN_AGENT_GROUPS[0]?.items[0];\n if (first === undefined) throw new Error(\"The built-in agent roster is empty\");\n return first.id;\n}\n"],"mappings":"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","names":[]}