@orderful/droid 0.39.1 → 0.40.1

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 (43) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/bun.lock +73 -0
  3. package/dist/bin/droid.js +891 -386
  4. package/dist/commands/integrations.d.ts +10 -0
  5. package/dist/commands/integrations.d.ts.map +1 -0
  6. package/dist/commands/tui/components/IntegrationsDetails.d.ts +6 -0
  7. package/dist/commands/tui/components/IntegrationsDetails.d.ts.map +1 -0
  8. package/dist/commands/tui/components/Markdown.d.ts.map +1 -1
  9. package/dist/commands/tui/types.d.ts +1 -1
  10. package/dist/commands/tui/types.d.ts.map +1 -1
  11. package/dist/commands/tui.d.ts.map +1 -1
  12. package/dist/index.js +29 -1
  13. package/dist/integrations/slack/index.d.ts +42 -0
  14. package/dist/integrations/slack/index.d.ts.map +1 -0
  15. package/dist/integrations/slack/index.ts +148 -0
  16. package/dist/integrations/slack/references/setup.md +53 -0
  17. package/dist/lib/config.d.ts.map +1 -1
  18. package/dist/lib/types.d.ts +9 -0
  19. package/dist/lib/types.d.ts.map +1 -1
  20. package/dist/tools/status-update/.claude-plugin/plugin.json +1 -1
  21. package/dist/tools/status-update/TOOL.yaml +2 -6
  22. package/dist/tools/status-update/skills/status-update/SKILL.md +14 -26
  23. package/package.json +2 -1
  24. package/scripts/build.ts +4 -0
  25. package/src/bin/droid.ts +27 -13
  26. package/src/commands/integrations.ts +431 -0
  27. package/src/commands/tui/components/IntegrationsDetails.tsx +95 -0
  28. package/src/commands/tui/components/Markdown.tsx +68 -3
  29. package/src/commands/tui/types.ts +1 -1
  30. package/src/commands/tui.tsx +81 -1
  31. package/src/integrations/slack/index.ts +148 -0
  32. package/src/integrations/slack/references/setup.md +53 -0
  33. package/src/lib/config.ts +49 -1
  34. package/src/lib/types.ts +11 -0
  35. package/src/tools/status-update/.claude-plugin/plugin.json +1 -1
  36. package/src/tools/status-update/TOOL.yaml +2 -6
  37. package/src/tools/status-update/skills/status-update/SKILL.md +14 -26
  38. package/dist/commands/auth.d.ts +0 -3
  39. package/dist/commands/auth.d.ts.map +0 -1
  40. package/dist/lib/secrets.d.ts +0 -7
  41. package/dist/lib/secrets.d.ts.map +0 -1
  42. package/src/commands/auth.ts +0 -150
  43. package/src/lib/secrets.ts +0 -12
package/dist/bin/droid.js CHANGED
@@ -123,6 +123,33 @@ function migrateToolConfigs(config) {
123
123
  return false;
124
124
  }
125
125
  }
126
+ function migrateIntegrations(config) {
127
+ if (config.migrations?.integrations_consolidated) {
128
+ return false;
129
+ }
130
+ const statusUpdateConfig = config.tools?.["status-update"];
131
+ if (statusUpdateConfig && "default_crosspost_channel" in statusUpdateConfig) {
132
+ const channel = statusUpdateConfig.default_crosspost_channel;
133
+ if (!config.integrations) {
134
+ config.integrations = {};
135
+ }
136
+ if (!config.integrations.slack) {
137
+ config.integrations.slack = {};
138
+ }
139
+ if (!config.integrations.slack.crosspost_channel && channel) {
140
+ config.integrations.slack.crosspost_channel = channel;
141
+ }
142
+ delete statusUpdateConfig.default_crosspost_channel;
143
+ if (Object.keys(statusUpdateConfig).length === 0) {
144
+ delete config.tools["status-update"];
145
+ }
146
+ }
147
+ if (!config.migrations) {
148
+ config.migrations = {};
149
+ }
150
+ config.migrations.integrations_consolidated = true;
151
+ return true;
152
+ }
126
153
  function ensureConfigDir() {
127
154
  if (!existsSync(CONFIG_DIR)) {
128
155
  mkdirSync(CONFIG_DIR, { recursive: true });
@@ -142,7 +169,8 @@ function loadConfig() {
142
169
  const needsLegacyMigration = "ai_tool" in rawConfig && !("platform" in rawConfig);
143
170
  const config = migrateConfig(rawConfig);
144
171
  const needsToolMigration = migrateToolConfigs(config);
145
- if (needsLegacyMigration || needsToolMigration) {
172
+ const needsIntegrationsMigration = migrateIntegrations(config);
173
+ if (needsLegacyMigration || needsToolMigration || needsIntegrationsMigration) {
146
174
  saveConfig(config);
147
175
  }
148
176
  return config;
@@ -2502,8 +2530,12 @@ async function updateCommand(tool, options) {
2502
2530
  }
2503
2531
 
2504
2532
  // src/commands/tui.tsx
2505
- import { render, Box as Box15, Text as Text16, useInput as useInput9, useApp as useApp2 } from "ink";
2533
+ import { render, Box as Box16, Text as Text17, useInput as useInput9, useApp as useApp2 } from "ink";
2506
2534
  import { useState as useState10, useEffect } from "react";
2535
+ import { readFileSync as readFileSync9 } from "fs";
2536
+ import { spawnSync as spawnSync3 } from "child_process";
2537
+ import { join as join11, dirname as dirname7 } from "path";
2538
+ import { fileURLToPath as fileURLToPath5 } from "url";
2507
2539
 
2508
2540
  // src/commands/tui/constants.ts
2509
2541
  var colors = {
@@ -2786,9 +2818,78 @@ function SettingsDetails({
2786
2818
  ] });
2787
2819
  }
2788
2820
 
2789
- // src/commands/tui/components/PlatformBadges.tsx
2821
+ // src/commands/tui/components/IntegrationsDetails.tsx
2790
2822
  import { Box as Box6, Text as Text6 } from "ink";
2791
2823
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
2824
+ function IntegrationsDetails({
2825
+ isFocused,
2826
+ selectedAction
2827
+ }) {
2828
+ const hasToken = !!process.env.SLACK_USER_TOKEN;
2829
+ const hasClientId = !!process.env.SLACK_CLIENT_ID;
2830
+ const hasClientSecret = !!process.env.SLACK_CLIENT_SECRET;
2831
+ const isConnected = hasToken;
2832
+ const canSetup = hasClientId && hasClientSecret;
2833
+ const guideActionIndex = canSetup && !isConnected ? 1 : 0;
2834
+ return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", paddingLeft: 2, flexGrow: 1, children: [
2835
+ /* @__PURE__ */ jsx6(Text6, { color: colors.text, bold: true, children: "Slack" }),
2836
+ /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", marginTop: 1, children: [
2837
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, bold: true, children: "Credentials" }),
2838
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2839
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " SLACK_CLIENT_ID " }),
2840
+ /* @__PURE__ */ jsx6(Text6, { color: hasClientId ? colors.success : "#fbbf24", children: hasClientId ? "\u2713" : "\u2717" })
2841
+ ] }),
2842
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2843
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " SLACK_CLIENT_SECRET " }),
2844
+ /* @__PURE__ */ jsx6(Text6, { color: hasClientSecret ? colors.success : "#fbbf24", children: hasClientSecret ? "\u2713" : "\u2717" })
2845
+ ] }),
2846
+ /* @__PURE__ */ jsxs6(Text6, { children: [
2847
+ /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: " SLACK_USER_TOKEN " }),
2848
+ /* @__PURE__ */ jsx6(Text6, { color: hasToken ? colors.success : "#fbbf24", children: hasToken ? "\u2713" : "\u2717" })
2849
+ ] })
2850
+ ] }),
2851
+ /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { children: isConnected ? /* @__PURE__ */ jsx6(Text6, { color: colors.success, children: "Connected" }) : hasClientId && hasClientSecret ? /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Ready to connect" }) : /* @__PURE__ */ jsx6(Text6, { color: "#fbbf24", children: "Not configured" }) }) }),
2852
+ !isConnected && !canSetup && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "Add SLACK_CLIENT_ID and SLACK_CLIENT_SECRET to your shell rc first" }) }),
2853
+ isFocused && /* @__PURE__ */ jsxs6(Box6, { marginTop: 2, flexDirection: "row", gap: 2, children: [
2854
+ canSetup && !isConnected && /* @__PURE__ */ jsxs6(
2855
+ Text6,
2856
+ {
2857
+ backgroundColor: selectedAction === 0 ? colors.primary : void 0,
2858
+ color: selectedAction === 0 ? "#ffffff" : colors.textDim,
2859
+ bold: selectedAction === 0,
2860
+ children: [
2861
+ " ",
2862
+ "Run Setup",
2863
+ " "
2864
+ ]
2865
+ }
2866
+ ),
2867
+ /* @__PURE__ */ jsxs6(
2868
+ Text6,
2869
+ {
2870
+ backgroundColor: selectedAction === guideActionIndex ? colors.primary : void 0,
2871
+ color: selectedAction === guideActionIndex ? "#ffffff" : colors.textDim,
2872
+ bold: selectedAction === guideActionIndex,
2873
+ children: [
2874
+ " ",
2875
+ "Setup Guide",
2876
+ " "
2877
+ ]
2878
+ }
2879
+ )
2880
+ ] }),
2881
+ isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs6(Text6, { color: colors.textDim, children: [
2882
+ "enter confirm ",
2883
+ "\xB7",
2884
+ " esc back"
2885
+ ] }) }),
2886
+ !isFocused && /* @__PURE__ */ jsx6(Box6, { marginTop: 2, children: /* @__PURE__ */ jsx6(Text6, { color: colors.textDim, children: "press enter for options" }) })
2887
+ ] });
2888
+ }
2889
+
2890
+ // src/commands/tui/components/PlatformBadges.tsx
2891
+ import { Box as Box7, Text as Text7 } from "ink";
2892
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2792
2893
  var PLATFORM_INFO = {
2793
2894
  ["claude-code" /* ClaudeCode */]: { color: "#da7756", label: "Claude" },
2794
2895
  ["cursor" /* Cursor */]: { color: "#22d3ee", label: "Cursor" },
@@ -2801,19 +2902,19 @@ function PlatformBadges({
2801
2902
  }) {
2802
2903
  const active = detected.filter((p) => !ignored.includes(p));
2803
2904
  if (active.length === 0) {
2804
- return /* @__PURE__ */ jsx6(Text6, { color: "#6a6a6a", children: "No platforms detected" });
2905
+ return /* @__PURE__ */ jsx7(Text7, { color: "#6a6a6a", children: "No platforms detected" });
2805
2906
  }
2806
- return /* @__PURE__ */ jsx6(Box6, { children: active.map((p, i) => {
2907
+ return /* @__PURE__ */ jsx7(Box7, { children: active.map((p, i) => {
2807
2908
  const info = PLATFORM_INFO[p];
2808
- return /* @__PURE__ */ jsxs6(Text6, { children: [
2809
- /* @__PURE__ */ jsx6(Text6, { color: info.color, children: info.label }),
2810
- i < active.length - 1 && /* @__PURE__ */ jsx6(Text6, { color: "#6a6a6a", children: " \xB7 " })
2909
+ return /* @__PURE__ */ jsxs7(Text7, { children: [
2910
+ /* @__PURE__ */ jsx7(Text7, { color: info.color, children: info.label }),
2911
+ i < active.length - 1 && /* @__PURE__ */ jsx7(Text7, { color: "#6a6a6a", children: " \xB7 " })
2811
2912
  ] }, p);
2812
2913
  }) });
2813
2914
  }
2814
2915
 
2815
2916
  // src/commands/tui/views/WelcomeScreen.tsx
2816
- import { Box as Box7, Text as Text7, useInput } from "ink";
2917
+ import { Box as Box8, Text as Text8, useInput } from "ink";
2817
2918
  import { useState, useMemo } from "react";
2818
2919
 
2819
2920
  // src/lib/quotes.ts
@@ -2834,7 +2935,7 @@ function getRandomQuote() {
2834
2935
  }
2835
2936
 
2836
2937
  // src/commands/tui/views/WelcomeScreen.tsx
2837
- import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2938
+ import { Fragment, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2838
2939
  function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isUpdating }) {
2839
2940
  const [selectedButton, setSelectedButton] = useState(0);
2840
2941
  const welcomeQuote = useMemo(() => getRandomQuote(), []);
@@ -2869,8 +2970,8 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2869
2970
  }
2870
2971
  });
2871
2972
  const hasUpdate = updateInfo.hasUpdate && updateInfo.latestVersion;
2872
- return /* @__PURE__ */ jsx7(Box7, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs7(
2873
- Box7,
2973
+ return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs8(
2974
+ Box8,
2874
2975
  {
2875
2976
  flexDirection: "column",
2876
2977
  alignItems: "center",
@@ -2879,42 +2980,42 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2879
2980
  paddingX: 4,
2880
2981
  paddingY: 1,
2881
2982
  children: [
2882
- /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
2883
- /* @__PURE__ */ jsxs7(Text7, { children: [
2884
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
2885
- /* @__PURE__ */ jsx7(Text7, { color: colors.text, children: "droid" }),
2886
- /* @__PURE__ */ jsxs7(Text7, { color: colors.textDim, children: [
2983
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
2984
+ /* @__PURE__ */ jsxs8(Text8, { children: [
2985
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
2986
+ /* @__PURE__ */ jsx8(Text8, { color: colors.text, children: "droid" }),
2987
+ /* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
2887
2988
  " v",
2888
2989
  updateInfo.currentVersion
2889
2990
  ] })
2890
2991
  ] }),
2891
- /* @__PURE__ */ jsxs7(Text7, { children: [
2892
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u2551 " }),
2893
- /* @__PURE__ */ jsx7(Text7, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
2894
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: " " }),
2895
- /* @__PURE__ */ jsx7(Text7, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
2896
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: " \u2551 " }),
2897
- /* @__PURE__ */ jsx7(Text7, { color: colors.textMuted, children: "Droid, teaching your AI new tricks" })
2992
+ /* @__PURE__ */ jsxs8(Text8, { children: [
2993
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2551 " }),
2994
+ /* @__PURE__ */ jsx8(Text8, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
2995
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " " }),
2996
+ /* @__PURE__ */ jsx8(Text8, { color: hasUpdate ? "#eab308" : colors.primary, children: "\u25CF" }),
2997
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " \u2551 " }),
2998
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textMuted, children: "Droid, teaching your AI new tricks" })
2898
2999
  ] }),
2899
- /* @__PURE__ */ jsxs7(Text7, { children: [
2900
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D " }),
2901
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "github.com/Orderful/droid" })
3000
+ /* @__PURE__ */ jsxs8(Text8, { children: [
3001
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D " }),
3002
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "github.com/Orderful/droid" })
2902
3003
  ] })
2903
3004
  ] }),
2904
- hasUpdate ? /* @__PURE__ */ jsxs7(Fragment, { children: [
2905
- /* @__PURE__ */ jsxs7(Box7, { marginTop: 2, marginBottom: 1, flexDirection: "column", alignItems: "center", children: [
2906
- /* @__PURE__ */ jsx7(Text7, { color: "#eab308", italic: true, children: '"The odds of functioning optimally without this' }),
2907
- /* @__PURE__ */ jsx7(Text7, { color: "#eab308", italic: true, children: 'update are approximately 3,720 to 1."' })
3005
+ hasUpdate ? /* @__PURE__ */ jsxs8(Fragment, { children: [
3006
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 2, marginBottom: 1, flexDirection: "column", alignItems: "center", children: [
3007
+ /* @__PURE__ */ jsx8(Text8, { color: "#eab308", italic: true, children: '"The odds of functioning optimally without this' }),
3008
+ /* @__PURE__ */ jsx8(Text8, { color: "#eab308", italic: true, children: 'update are approximately 3,720 to 1."' })
2908
3009
  ] }),
2909
- /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: colors.textMuted, children: [
3010
+ /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: colors.textMuted, children: [
2910
3011
  "v",
2911
3012
  updateInfo.currentVersion,
2912
3013
  " \u2192 v",
2913
3014
  updateInfo.latestVersion
2914
3015
  ] }) }),
2915
- isUpdating ? /* @__PURE__ */ jsx7(Text7, { color: "#eab308", children: "Updating..." }) : /* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", children: [
2916
- /* @__PURE__ */ jsxs7(
2917
- Text7,
3016
+ isUpdating ? /* @__PURE__ */ jsx8(Text8, { color: "#eab308", children: "Updating..." }) : /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
3017
+ /* @__PURE__ */ jsxs8(
3018
+ Text8,
2918
3019
  {
2919
3020
  backgroundColor: selectedButton === 0 ? "#eab308" : colors.bgSelected,
2920
3021
  color: selectedButton === 0 ? "#000000" : colors.textMuted,
@@ -2926,9 +3027,9 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2926
3027
  ]
2927
3028
  }
2928
3029
  ),
2929
- /* @__PURE__ */ jsx7(Text7, { children: " " }),
2930
- /* @__PURE__ */ jsxs7(
2931
- Text7,
3030
+ /* @__PURE__ */ jsx8(Text8, { children: " " }),
3031
+ /* @__PURE__ */ jsxs8(
3032
+ Text8,
2932
3033
  {
2933
3034
  backgroundColor: selectedButton === 1 ? "#eab308" : colors.bgSelected,
2934
3035
  color: selectedButton === 1 ? "#000000" : colors.textMuted,
@@ -2940,9 +3041,9 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2940
3041
  ]
2941
3042
  }
2942
3043
  ),
2943
- /* @__PURE__ */ jsx7(Text7, { children: " " }),
2944
- /* @__PURE__ */ jsxs7(
2945
- Text7,
3044
+ /* @__PURE__ */ jsx8(Text8, { children: " " }),
3045
+ /* @__PURE__ */ jsxs8(
3046
+ Text8,
2946
3047
  {
2947
3048
  backgroundColor: selectedButton === 2 ? colors.bgSelected : void 0,
2948
3049
  color: selectedButton === 2 ? colors.text : colors.textMuted,
@@ -2955,10 +3056,10 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2955
3056
  }
2956
3057
  )
2957
3058
  ] }),
2958
- /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
2959
- ] }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
2960
- /* @__PURE__ */ jsx7(Box7, { marginTop: 2, marginBottom: 1, children: /* @__PURE__ */ jsx7(Text7, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: ` ${welcomeQuote} ` }) }),
2961
- /* @__PURE__ */ jsx7(Text7, { color: colors.textDim, children: "press enter" })
3059
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
3060
+ ] }) : /* @__PURE__ */ jsxs8(Fragment, { children: [
3061
+ /* @__PURE__ */ jsx8(Box8, { marginTop: 2, marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: ` ${welcomeQuote} ` }) }),
3062
+ /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "press enter" })
2962
3063
  ] })
2963
3064
  ]
2964
3065
  }
@@ -2966,9 +3067,9 @@ function WelcomeScreen({ onContinue, onUpdate, onAlways, onExit, updateInfo, isU
2966
3067
  }
2967
3068
 
2968
3069
  // src/commands/tui/views/ToolUpdatePrompt.tsx
2969
- import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
3070
+ import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
2970
3071
  import { useState as useState2 } from "react";
2971
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
3072
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2972
3073
  function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdating }) {
2973
3074
  const [selectedButton, setSelectedButton] = useState2(0);
2974
3075
  useInput2((input, key) => {
@@ -2997,8 +3098,8 @@ function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdati
2997
3098
  { label: "Always", action: onAlways },
2998
3099
  { label: "Skip", action: onSkip }
2999
3100
  ];
3000
- return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs8(
3001
- Box8,
3101
+ return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 18, children: /* @__PURE__ */ jsxs9(
3102
+ Box9,
3002
3103
  {
3003
3104
  flexDirection: "column",
3004
3105
  alignItems: "center",
@@ -3007,46 +3108,46 @@ function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdati
3007
3108
  paddingX: 4,
3008
3109
  paddingY: 1,
3009
3110
  children: [
3010
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3011
- /* @__PURE__ */ jsxs8(Text8, { children: [
3012
- /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
3013
- /* @__PURE__ */ jsx8(Text8, { color: colors.text, children: "Tool Updates" })
3111
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
3112
+ /* @__PURE__ */ jsxs9(Text9, { children: [
3113
+ /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2557 " }),
3114
+ /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: "Tool Updates" })
3014
3115
  ] }),
3015
- /* @__PURE__ */ jsxs8(Text8, { children: [
3016
- /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u2551 " }),
3017
- /* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "\u25CF" }),
3018
- /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " " }),
3019
- /* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "\u25CF" }),
3020
- /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: " \u2551 " }),
3021
- /* @__PURE__ */ jsxs8(Text8, { color: colors.textMuted, children: [
3116
+ /* @__PURE__ */ jsxs9(Text9, { children: [
3117
+ /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "\u2551 " }),
3118
+ /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "\u25CF" }),
3119
+ /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: " " }),
3120
+ /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "\u25CF" }),
3121
+ /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: " \u2551 " }),
3122
+ /* @__PURE__ */ jsxs9(Text9, { color: colors.textMuted, children: [
3022
3123
  toolUpdates.length,
3023
3124
  " tool",
3024
3125
  toolUpdates.length > 1 ? "s have" : " has",
3025
3126
  " updates available"
3026
3127
  ] })
3027
3128
  ] }),
3028
- /* @__PURE__ */ jsx8(Text8, { children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D" }) })
3129
+ /* @__PURE__ */ jsx9(Text9, { children: /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "\u255A\u2550\u2566\u2550\u2566\u2550\u255D" }) })
3029
3130
  ] }),
3030
- /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [
3031
- toolUpdates.slice(0, 5).map((tool) => /* @__PURE__ */ jsxs8(Text8, { color: colors.textMuted, children: [
3032
- /* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "\u2191" }),
3131
+ /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, marginBottom: 1, flexDirection: "column", children: [
3132
+ toolUpdates.slice(0, 5).map((tool) => /* @__PURE__ */ jsxs9(Text9, { color: colors.textMuted, children: [
3133
+ /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "\u2191" }),
3033
3134
  " ",
3034
3135
  tool.name,
3035
- /* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
3136
+ /* @__PURE__ */ jsxs9(Text9, { color: colors.textDim, children: [
3036
3137
  " ",
3037
3138
  tool.installedVersion,
3038
3139
  " \u2192 ",
3039
3140
  tool.bundledVersion
3040
3141
  ] })
3041
3142
  ] }, tool.name)),
3042
- toolUpdates.length > 5 && /* @__PURE__ */ jsxs8(Text8, { color: colors.textDim, children: [
3143
+ toolUpdates.length > 5 && /* @__PURE__ */ jsxs9(Text9, { color: colors.textDim, children: [
3043
3144
  "...and ",
3044
3145
  toolUpdates.length - 5,
3045
3146
  " more"
3046
3147
  ] })
3047
3148
  ] }),
3048
- isUpdating ? /* @__PURE__ */ jsx8(Text8, { color: colors.primary, children: "Updating tools..." }) : /* @__PURE__ */ jsx8(Box8, { flexDirection: "row", children: buttons.map((button, index) => /* @__PURE__ */ jsxs8(
3049
- Text8,
3149
+ isUpdating ? /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "Updating tools..." }) : /* @__PURE__ */ jsx9(Box9, { flexDirection: "row", children: buttons.map((button, index) => /* @__PURE__ */ jsxs9(
3150
+ Text9,
3050
3151
  {
3051
3152
  backgroundColor: selectedButton === index ? colors.primary : colors.bgSelected,
3052
3153
  color: selectedButton === index ? "#000000" : colors.textMuted,
@@ -3059,17 +3160,17 @@ function ToolUpdatePrompt({ toolUpdates, onUpdateAll, onAlways, onSkip, isUpdati
3059
3160
  },
3060
3161
  button.label
3061
3162
  )) }),
3062
- /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
3163
+ /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: '\u2190\u2192 select \xB7 enter \xB7 "Always" enables auto-update' }) })
3063
3164
  ]
3064
3165
  }
3065
3166
  ) });
3066
3167
  }
3067
3168
 
3068
3169
  // src/commands/tui/views/SetupScreen.tsx
3069
- import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
3170
+ import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
3070
3171
  import TextInput from "ink-text-input";
3071
3172
  import { useState as useState3 } from "react";
3072
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3173
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3073
3174
  var PLATFORM_LABELS3 = {
3074
3175
  ["claude-code" /* ClaudeCode */]: "Claude Code",
3075
3176
  ["cursor" /* Cursor */]: "Cursor",
@@ -3147,14 +3248,14 @@ function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
3147
3248
  }
3148
3249
  }
3149
3250
  }, { isActive: step !== "user_mention" });
3150
- const renderHeader = () => /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
3151
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "[" }),
3152
- /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "\u25CF" }),
3153
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: " " }),
3154
- /* @__PURE__ */ jsx9(Text9, { color: colors.primary, children: "\u25CF" }),
3155
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "] " }),
3156
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, bold: true, children: "droid setup" }),
3157
- /* @__PURE__ */ jsxs9(Text9, { color: colors.textDim, children: [
3251
+ const renderHeader = () => /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ jsxs10(Text10, { children: [
3252
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "[" }),
3253
+ /* @__PURE__ */ jsx10(Text10, { color: colors.primary, children: "\u25CF" }),
3254
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: " " }),
3255
+ /* @__PURE__ */ jsx10(Text10, { color: colors.primary, children: "\u25CF" }),
3256
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "] " }),
3257
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, bold: true, children: "droid setup" }),
3258
+ /* @__PURE__ */ jsxs10(Text10, { color: colors.textDim, children: [
3158
3259
  " \xB7 Step ",
3159
3260
  Math.min(stepIndex + 1, totalSteps),
3160
3261
  " of ",
@@ -3162,12 +3263,12 @@ function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
3162
3263
  ] })
3163
3264
  ] }) });
3164
3265
  if (step === "user_mention") {
3165
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
3266
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
3166
3267
  renderHeader(),
3167
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: "What @mention should be used for you?" }),
3168
- /* @__PURE__ */ jsxs9(Box9, { marginTop: 1, children: [
3169
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "> " }),
3170
- /* @__PURE__ */ jsx9(
3268
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: "What @mention should be used for you?" }),
3269
+ /* @__PURE__ */ jsxs10(Box10, { marginTop: 1, children: [
3270
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "> " }),
3271
+ /* @__PURE__ */ jsx10(
3171
3272
  TextInput,
3172
3273
  {
3173
3274
  value: userMention,
@@ -3177,42 +3278,42 @@ function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
3177
3278
  }
3178
3279
  )
3179
3280
  ] }),
3180
- error && /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: colors.error, children: error }) }),
3181
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "enter next \xB7 esc back" }) })
3281
+ error && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: colors.error, children: error }) }),
3282
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "enter next \xB7 esc back" }) })
3182
3283
  ] });
3183
3284
  }
3184
3285
  if (step === "auto_update") {
3185
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
3286
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
3186
3287
  renderHeader(),
3187
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: "Configure auto-update behaviour" }),
3188
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
3189
- /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text9, { children: [
3190
- /* @__PURE__ */ jsxs9(Text9, { color: colors.textDim, children: [
3288
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: "Configure auto-update behaviour" }),
3289
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
3290
+ /* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text10, { children: [
3291
+ /* @__PURE__ */ jsxs10(Text10, { color: colors.textDim, children: [
3191
3292
  autoUpdateSelectedIndex === 0 ? ">" : " ",
3192
3293
  " "
3193
3294
  ] }),
3194
- /* @__PURE__ */ jsxs9(Text9, { color: autoUpdateSelectedIndex === 0 ? colors.text : colors.textMuted, children: [
3295
+ /* @__PURE__ */ jsxs10(Text10, { color: autoUpdateSelectedIndex === 0 ? colors.text : colors.textMuted, children: [
3195
3296
  "[",
3196
3297
  autoUpdateTools ? "x" : " ",
3197
3298
  "] Auto-update tools"
3198
3299
  ] })
3199
3300
  ] }) }),
3200
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: " Update tools automatically when droid starts" }),
3201
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { children: [
3202
- /* @__PURE__ */ jsxs9(Text9, { color: colors.textDim, children: [
3301
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: " Update tools automatically when droid starts" }),
3302
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { children: [
3303
+ /* @__PURE__ */ jsxs10(Text10, { color: colors.textDim, children: [
3203
3304
  autoUpdateSelectedIndex === 1 ? ">" : " ",
3204
3305
  " "
3205
3306
  ] }),
3206
- /* @__PURE__ */ jsxs9(Text9, { color: autoUpdateSelectedIndex === 1 ? colors.text : colors.textMuted, children: [
3307
+ /* @__PURE__ */ jsxs10(Text10, { color: autoUpdateSelectedIndex === 1 ? colors.text : colors.textMuted, children: [
3207
3308
  "[",
3208
3309
  autoUpdateApp ? "x" : " ",
3209
3310
  "] Auto-update app"
3210
3311
  ] })
3211
3312
  ] }) }),
3212
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: " Update droid automatically when a new version is available" })
3313
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: " Update droid automatically when a new version is available" })
3213
3314
  ] }),
3214
- /* @__PURE__ */ jsx9(Box9, { marginTop: 2, children: /* @__PURE__ */ jsxs9(
3215
- Text9,
3315
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 2, children: /* @__PURE__ */ jsxs10(
3316
+ Text10,
3216
3317
  {
3217
3318
  backgroundColor: autoUpdateSelectedIndex === 2 ? colors.primary : colors.bgSelected,
3218
3319
  color: autoUpdateSelectedIndex === 2 ? "#ffffff" : colors.textMuted,
@@ -3224,80 +3325,126 @@ function SetupScreen({ onComplete, onSkip, initialConfig, detectedPlatforms }) {
3224
3325
  ]
3225
3326
  }
3226
3327
  ) }),
3227
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "\u2191\u2193 select \xB7 enter toggle/next \xB7 esc back" }) })
3328
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "\u2191\u2193 select \xB7 enter toggle/next \xB7 esc back" }) })
3228
3329
  ] });
3229
3330
  }
3230
3331
  const platformsText = detectedPlatforms.length > 0 ? detectedPlatforms.map((p) => PLATFORM_LABELS3[p]).join(", ") : "None detected";
3231
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
3332
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
3232
3333
  renderHeader(),
3233
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, bold: true, children: "Review your settings" }),
3234
- /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", marginTop: 1, children: [
3235
- /* @__PURE__ */ jsxs9(Text9, { children: [
3236
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "Platforms: " }),
3237
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: platformsText })
3334
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, bold: true, children: "Review your settings" }),
3335
+ /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
3336
+ /* @__PURE__ */ jsxs10(Text10, { children: [
3337
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "Platforms: " }),
3338
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: platformsText })
3238
3339
  ] }),
3239
- /* @__PURE__ */ jsxs9(Text9, { children: [
3240
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "Your @mention: " }),
3241
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: userMention })
3340
+ /* @__PURE__ */ jsxs10(Text10, { children: [
3341
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "Your @mention: " }),
3342
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: userMention })
3242
3343
  ] }),
3243
- /* @__PURE__ */ jsxs9(Text9, { children: [
3244
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "Auto-update tools: " }),
3245
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: autoUpdateTools ? "enabled" : "disabled" })
3344
+ /* @__PURE__ */ jsxs10(Text10, { children: [
3345
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "Auto-update tools: " }),
3346
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: autoUpdateTools ? "enabled" : "disabled" })
3246
3347
  ] }),
3247
- /* @__PURE__ */ jsxs9(Text9, { children: [
3248
- /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "Auto-update app: " }),
3249
- /* @__PURE__ */ jsx9(Text9, { color: colors.text, children: autoUpdateApp ? "enabled" : "disabled" })
3348
+ /* @__PURE__ */ jsxs10(Text10, { children: [
3349
+ /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "Auto-update app: " }),
3350
+ /* @__PURE__ */ jsx10(Text10, { color: colors.text, children: autoUpdateApp ? "enabled" : "disabled" })
3250
3351
  ] })
3251
3352
  ] }),
3252
- /* @__PURE__ */ jsx9(Box9, { marginTop: 2, children: /* @__PURE__ */ jsxs9(Text9, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: [
3353
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 2, children: /* @__PURE__ */ jsxs10(Text10, { backgroundColor: colors.primary, color: "#ffffff", bold: true, children: [
3253
3354
  " ",
3254
3355
  "Save",
3255
3356
  " "
3256
3357
  ] }) }),
3257
- /* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: colors.textDim, children: "enter save \xB7 esc back" }) })
3358
+ /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: "enter save \xB7 esc back" }) })
3258
3359
  ] });
3259
3360
  }
3260
3361
 
3261
3362
  // src/commands/tui/views/ReadmeViewer.tsx
3262
- import { Box as Box10, Text as Text11, useInput as useInput4 } from "ink";
3363
+ import { Box as Box11, Text as Text12, useInput as useInput4 } from "ink";
3263
3364
  import { useState as useState4, useMemo as useMemo2 } from "react";
3264
3365
 
3265
3366
  // src/commands/tui/components/Markdown.tsx
3266
- import { Text as Text10 } from "ink";
3267
- import { jsx as jsx10 } from "react/jsx-runtime";
3367
+ import { Text as Text11 } from "ink";
3368
+ import { jsx as jsx11 } from "react/jsx-runtime";
3369
+ function renderInline(text, baseColor) {
3370
+ const parts = [];
3371
+ let remaining = text;
3372
+ let key = 0;
3373
+ while (remaining.length > 0) {
3374
+ const boldMatch = remaining.match(/^(.*?)\*\*(.+?)\*\*(.*)/s);
3375
+ if (boldMatch) {
3376
+ if (boldMatch[1]) {
3377
+ parts.push(...renderInlineSimple(boldMatch[1], baseColor, key));
3378
+ key += boldMatch[1].length;
3379
+ }
3380
+ parts.push(/* @__PURE__ */ jsx11(Text11, { color: baseColor, bold: true, children: boldMatch[2] }, key++));
3381
+ remaining = boldMatch[3];
3382
+ continue;
3383
+ }
3384
+ const codeMatch = remaining.match(/^(.*?)`(.+?)`(.*)/s);
3385
+ if (codeMatch) {
3386
+ if (codeMatch[1]) {
3387
+ parts.push(...renderInlineSimple(codeMatch[1], baseColor, key));
3388
+ key += codeMatch[1].length;
3389
+ }
3390
+ parts.push(/* @__PURE__ */ jsx11(Text11, { color: "#a5d6ff", children: codeMatch[2] }, key++));
3391
+ remaining = codeMatch[3];
3392
+ continue;
3393
+ }
3394
+ const italicMatch = remaining.match(/^(.*?)\*(.+?)\*(.*)/s);
3395
+ if (italicMatch) {
3396
+ if (italicMatch[1]) {
3397
+ parts.push(...renderInlineSimple(italicMatch[1], baseColor, key));
3398
+ key += italicMatch[1].length;
3399
+ }
3400
+ parts.push(/* @__PURE__ */ jsx11(Text11, { color: baseColor, italic: true, children: italicMatch[2] }, key++));
3401
+ remaining = italicMatch[3];
3402
+ continue;
3403
+ }
3404
+ parts.push(/* @__PURE__ */ jsx11(Text11, { color: baseColor, children: remaining }, key));
3405
+ break;
3406
+ }
3407
+ return parts;
3408
+ }
3409
+ function renderInlineSimple(text, color, startKey) {
3410
+ return [/* @__PURE__ */ jsx11(Text11, { color, children: text }, startKey)];
3411
+ }
3268
3412
  function MarkdownLine({ line, inCodeBlock }) {
3269
3413
  if (inCodeBlock) {
3270
- return /* @__PURE__ */ jsx10(Text10, { color: "#a5d6ff", children: line || " " });
3414
+ return /* @__PURE__ */ jsx11(Text11, { color: "#a5d6ff", children: line || " " });
3271
3415
  }
3272
3416
  if (line.startsWith("```")) {
3273
- return /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: line });
3417
+ return /* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: line });
3274
3418
  }
3275
3419
  if (line.startsWith("# ")) {
3276
- return /* @__PURE__ */ jsx10(Text10, { color: colors.text, bold: true, children: line.slice(2) });
3420
+ return /* @__PURE__ */ jsx11(Text11, { color: colors.text, bold: true, children: line.slice(2) });
3277
3421
  }
3278
3422
  if (line.startsWith("## ")) {
3279
- return /* @__PURE__ */ jsx10(Text10, { color: colors.text, bold: true, children: line.slice(3) });
3423
+ return /* @__PURE__ */ jsx11(Text11, { color: colors.text, bold: true, children: line.slice(3) });
3280
3424
  }
3281
3425
  if (line.startsWith("### ")) {
3282
- return /* @__PURE__ */ jsx10(Text10, { color: "#c9d1d9", bold: true, children: line.slice(4) });
3426
+ return /* @__PURE__ */ jsx11(Text11, { color: "#c9d1d9", bold: true, children: line.slice(4) });
3283
3427
  }
3284
3428
  if (line === "---") {
3285
- return /* @__PURE__ */ jsx10(Text10, { color: colors.textDim, children: line });
3429
+ return /* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: line });
3430
+ }
3431
+ if (line.match(/^[\s]*\d+\.\s/)) {
3432
+ return /* @__PURE__ */ jsx11(Text11, { children: renderInline(line, colors.textMuted) });
3286
3433
  }
3287
3434
  if (line.match(/^[\s]*[-*]\s/)) {
3288
- return /* @__PURE__ */ jsx10(Text10, { color: colors.textMuted, children: line });
3435
+ return /* @__PURE__ */ jsx11(Text11, { children: renderInline(line, colors.textMuted) });
3289
3436
  }
3290
3437
  if (line.startsWith(">")) {
3291
- return /* @__PURE__ */ jsx10(Text10, { color: "#8b949e", italic: true, children: line });
3438
+ return /* @__PURE__ */ jsx11(Text11, { color: "#8b949e", italic: true, children: line });
3292
3439
  }
3293
3440
  if (line.includes("|")) {
3294
- return /* @__PURE__ */ jsx10(Text10, { color: colors.textMuted, children: line });
3441
+ return /* @__PURE__ */ jsx11(Text11, { color: colors.textMuted, children: line });
3295
3442
  }
3296
- return /* @__PURE__ */ jsx10(Text10, { color: colors.textMuted, children: line || " " });
3443
+ return /* @__PURE__ */ jsx11(Text11, { children: renderInline(line || " ", colors.textMuted) });
3297
3444
  }
3298
3445
 
3299
3446
  // src/commands/tui/views/ReadmeViewer.tsx
3300
- import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
3447
+ import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3301
3448
  function ReadmeViewer({ title, content, onClose }) {
3302
3449
  const [scrollOffset, setScrollOffset] = useState4(0);
3303
3450
  const lines = useMemo2(() => content.split("\n"), [content]);
@@ -3340,30 +3487,30 @@ function ReadmeViewer({ title, content, onClose }) {
3340
3487
  const showBottomIndicator = endIndex < lines.length;
3341
3488
  const actualContentLines = contentLines - (showBottomIndicator ? 1 : 0);
3342
3489
  const visibleLines = lines.slice(scrollOffset, scrollOffset + actualContentLines);
3343
- return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
3344
- /* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
3345
- /* @__PURE__ */ jsx11(Text11, { color: colors.text, bold: true, children: title }),
3346
- /* @__PURE__ */ jsxs10(Text11, { color: colors.textDim, children: [
3490
+ return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
3491
+ /* @__PURE__ */ jsxs11(Box11, { marginBottom: 1, children: [
3492
+ /* @__PURE__ */ jsx12(Text12, { color: colors.text, bold: true, children: title }),
3493
+ /* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
3347
3494
  " \xB7 ",
3348
3495
  lines.length,
3349
3496
  " lines"
3350
3497
  ] })
3351
3498
  ] }),
3352
- /* @__PURE__ */ jsxs10(
3353
- Box10,
3499
+ /* @__PURE__ */ jsxs11(
3500
+ Box11,
3354
3501
  {
3355
3502
  flexDirection: "column",
3356
3503
  borderStyle: "single",
3357
3504
  borderColor: colors.border,
3358
3505
  paddingX: 1,
3359
3506
  children: [
3360
- showTopIndicator && /* @__PURE__ */ jsxs10(Text11, { color: colors.textDim, children: [
3507
+ showTopIndicator && /* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
3361
3508
  "\u2191 ",
3362
3509
  scrollOffset,
3363
3510
  " more lines"
3364
3511
  ] }),
3365
- visibleLines.map((line, i) => /* @__PURE__ */ jsx11(MarkdownLine, { line, inCodeBlock: lineStates[scrollOffset + i] }, scrollOffset + i)),
3366
- showBottomIndicator && /* @__PURE__ */ jsxs10(Text11, { color: colors.textDim, children: [
3512
+ visibleLines.map((line, i) => /* @__PURE__ */ jsx12(MarkdownLine, { line, inCodeBlock: lineStates[scrollOffset + i] }, scrollOffset + i)),
3513
+ showBottomIndicator && /* @__PURE__ */ jsxs11(Text12, { color: colors.textDim, children: [
3367
3514
  "\u2193 ",
3368
3515
  lines.length - scrollOffset - actualContentLines,
3369
3516
  " more lines"
@@ -3371,16 +3518,16 @@ function ReadmeViewer({ title, content, onClose }) {
3371
3518
  ]
3372
3519
  }
3373
3520
  ),
3374
- /* @__PURE__ */ jsx11(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: colors.textDim, children: "\u2191\u2193 scroll \xB7 space/pgdn page \xB7 esc back" }) })
3521
+ /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "\u2191\u2193 scroll \xB7 space/pgdn page \xB7 esc back" }) })
3375
3522
  ] });
3376
3523
  }
3377
3524
 
3378
3525
  // src/commands/tui/views/ToolExplorer.tsx
3379
- import { Box as Box11, Text as Text12, useInput as useInput5 } from "ink";
3526
+ import { Box as Box12, Text as Text13, useInput as useInput5 } from "ink";
3380
3527
  import { useState as useState5, useMemo as useMemo3 } from "react";
3381
3528
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
3382
3529
  import { join as join10 } from "path";
3383
- import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3530
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3384
3531
  function ToolExplorer({ tool, onViewSource, onClose }) {
3385
3532
  const [selectedIndex, setSelectedIndex] = useState5(0);
3386
3533
  const items = useMemo3(() => {
@@ -3439,23 +3586,23 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
3439
3586
  const commandItems = items.filter((i) => i.type === "command");
3440
3587
  const agentItems = items.filter((i) => i.type === "agent");
3441
3588
  const getItemIndex = (item) => items.indexOf(item);
3442
- return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
3443
- /* @__PURE__ */ jsx12(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text12, { children: [
3444
- /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "[" }),
3445
- /* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
3446
- /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: " " }),
3447
- /* @__PURE__ */ jsx12(Text12, { color: colors.primary, children: "\u25CF" }),
3448
- /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "] " }),
3449
- /* @__PURE__ */ jsx12(Text12, { color: colors.text, bold: true, children: tool.name })
3589
+ return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", padding: 1, children: [
3590
+ /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
3591
+ /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "[" }),
3592
+ /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3593
+ /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " " }),
3594
+ /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3595
+ /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "] " }),
3596
+ /* @__PURE__ */ jsx13(Text13, { color: colors.text, bold: true, children: tool.name })
3450
3597
  ] }) }),
3451
- /* @__PURE__ */ jsx12(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.textMuted, children: tool.description }) }),
3452
- skillItems.length > 0 && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
3453
- /* @__PURE__ */ jsx12(Text12, { color: colors.skill, bold: true, children: "Skills" }),
3454
- /* @__PURE__ */ jsx12(Box11, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: skillItems.map((item) => {
3598
+ /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textMuted, children: tool.description }) }),
3599
+ skillItems.length > 0 && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
3600
+ /* @__PURE__ */ jsx13(Text13, { color: colors.skill, bold: true, children: "Skills" }),
3601
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: skillItems.map((item) => {
3455
3602
  const idx = getItemIndex(item);
3456
3603
  const isSelected = selectedIndex === idx;
3457
- return /* @__PURE__ */ jsx12(Box11, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx12(
3458
- Text12,
3604
+ return /* @__PURE__ */ jsx13(Box12, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx13(
3605
+ Text13,
3459
3606
  {
3460
3607
  backgroundColor: isSelected ? colors.skill : colors.bgSelected,
3461
3608
  color: isSelected ? "#000000" : colors.skill,
@@ -3465,13 +3612,13 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
3465
3612
  ) }, item.name);
3466
3613
  }) })
3467
3614
  ] }),
3468
- commandItems.length > 0 && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
3469
- /* @__PURE__ */ jsx12(Text12, { color: colors.command, bold: true, children: "Commands" }),
3470
- /* @__PURE__ */ jsx12(Box11, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: commandItems.map((item) => {
3615
+ commandItems.length > 0 && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
3616
+ /* @__PURE__ */ jsx13(Text13, { color: colors.command, bold: true, children: "Commands" }),
3617
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: commandItems.map((item) => {
3471
3618
  const idx = getItemIndex(item);
3472
3619
  const isSelected = selectedIndex === idx;
3473
- return /* @__PURE__ */ jsx12(Box11, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx12(
3474
- Text12,
3620
+ return /* @__PURE__ */ jsx13(Box12, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx13(
3621
+ Text13,
3475
3622
  {
3476
3623
  backgroundColor: isSelected ? colors.command : colors.bgSelected,
3477
3624
  color: isSelected ? "#000000" : colors.command,
@@ -3481,13 +3628,13 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
3481
3628
  ) }, item.name);
3482
3629
  }) })
3483
3630
  ] }),
3484
- agentItems.length > 0 && /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
3485
- /* @__PURE__ */ jsx12(Text12, { color: colors.agent, bold: true, children: "Agents" }),
3486
- /* @__PURE__ */ jsx12(Box11, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: agentItems.map((item) => {
3631
+ agentItems.length > 0 && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
3632
+ /* @__PURE__ */ jsx13(Text13, { color: colors.agent, bold: true, children: "Agents" }),
3633
+ /* @__PURE__ */ jsx13(Box12, { flexDirection: "row", flexWrap: "wrap", marginTop: 1, children: agentItems.map((item) => {
3487
3634
  const idx = getItemIndex(item);
3488
3635
  const isSelected = selectedIndex === idx;
3489
- return /* @__PURE__ */ jsx12(Box11, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx12(
3490
- Text12,
3636
+ return /* @__PURE__ */ jsx13(Box12, { marginRight: 1, marginBottom: 1, children: /* @__PURE__ */ jsx13(
3637
+ Text13,
3491
3638
  {
3492
3639
  backgroundColor: isSelected ? colors.agent : colors.bgSelected,
3493
3640
  color: isSelected ? "#000000" : colors.agent,
@@ -3497,15 +3644,15 @@ function ToolExplorer({ tool, onViewSource, onClose }) {
3497
3644
  ) }, item.name);
3498
3645
  }) })
3499
3646
  ] }),
3500
- /* @__PURE__ */ jsx12(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: colors.textDim, children: "\u2190\u2192 navigate \xB7 enter view source \xB7 esc back" }) })
3647
+ /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "\u2190\u2192 navigate \xB7 enter view source \xB7 esc back" }) })
3501
3648
  ] });
3502
3649
  }
3503
3650
 
3504
3651
  // src/commands/tui/views/SkillConfigScreen.tsx
3505
- import { Box as Box12, Text as Text13, useInput as useInput6 } from "ink";
3652
+ import { Box as Box13, Text as Text14, useInput as useInput6 } from "ink";
3506
3653
  import TextInput2 from "ink-text-input";
3507
3654
  import { useState as useState6, useMemo as useMemo4 } from "react";
3508
- import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
3655
+ import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3509
3656
  function SkillConfigScreen({ skill, onComplete, onCancel }) {
3510
3657
  const configSchema = skill.config_schema || {};
3511
3658
  const configKeys = Object.keys(configSchema);
@@ -3605,20 +3752,20 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3605
3752
  }
3606
3753
  }, { isActive: editingField === null && editingSelect === null });
3607
3754
  if (configKeys.length === 0) {
3608
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", padding: 1, children: [
3609
- /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
3610
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "[" }),
3611
- /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3612
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " " }),
3613
- /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3614
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "] " }),
3615
- /* @__PURE__ */ jsxs12(Text13, { color: colors.text, bold: true, children: [
3755
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 1, children: [
3756
+ /* @__PURE__ */ jsx14(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
3757
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "[" }),
3758
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: "\u25CF" }),
3759
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: " " }),
3760
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: "\u25CF" }),
3761
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "] " }),
3762
+ /* @__PURE__ */ jsxs13(Text14, { color: colors.text, bold: true, children: [
3616
3763
  "configure ",
3617
3764
  skill.name
3618
3765
  ] })
3619
3766
  ] }) }),
3620
- /* @__PURE__ */ jsx13(Text13, { color: colors.textMuted, children: "This skill has no configuration options." }),
3621
- /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "esc to go back" }) })
3767
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textMuted, children: "This skill has no configuration options." }),
3768
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "esc to go back" }) })
3622
3769
  ] });
3623
3770
  }
3624
3771
  const visibleEndIndex = Math.min(scrollOffset + MAX_VISIBLE_CONFIG_ITEMS, totalItems);
@@ -3626,20 +3773,20 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3626
3773
  const showSaveButton = visibleEndIndex > configKeys.length || scrollOffset + MAX_VISIBLE_CONFIG_ITEMS > configKeys.length;
3627
3774
  const showTopIndicator = scrollOffset > 0;
3628
3775
  const showBottomIndicator = scrollOffset + MAX_VISIBLE_CONFIG_ITEMS < totalItems;
3629
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", padding: 1, children: [
3630
- /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
3631
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "[" }),
3632
- /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3633
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " " }),
3634
- /* @__PURE__ */ jsx13(Text13, { color: colors.primary, children: "\u25CF" }),
3635
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "] " }),
3636
- /* @__PURE__ */ jsxs12(Text13, { color: colors.text, bold: true, children: [
3776
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 1, children: [
3777
+ /* @__PURE__ */ jsx14(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
3778
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "[" }),
3779
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: "\u25CF" }),
3780
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: " " }),
3781
+ /* @__PURE__ */ jsx14(Text14, { color: colors.primary, children: "\u25CF" }),
3782
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "] " }),
3783
+ /* @__PURE__ */ jsxs13(Text14, { color: colors.text, bold: true, children: [
3637
3784
  "configure ",
3638
3785
  skill.name
3639
3786
  ] })
3640
3787
  ] }) }),
3641
- /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
3642
- showTopIndicator && /* @__PURE__ */ jsx13(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
3788
+ /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
3789
+ showTopIndicator && /* @__PURE__ */ jsx14(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3643
3790
  " \u2191 ",
3644
3791
  scrollOffset,
3645
3792
  " more"
@@ -3649,33 +3796,33 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3649
3796
  const option = configSchema[configKey];
3650
3797
  const isSelected = selectedIndex === actualIndex;
3651
3798
  const isEditing = editingField === configKey;
3652
- return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginBottom: 1, children: [
3653
- /* @__PURE__ */ jsxs12(Text13, { children: [
3654
- /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
3799
+ return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginBottom: 1, children: [
3800
+ /* @__PURE__ */ jsxs13(Text14, { children: [
3801
+ /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3655
3802
  isSelected ? ">" : " ",
3656
3803
  " "
3657
3804
  ] }),
3658
- /* @__PURE__ */ jsx13(Text13, { color: isSelected ? colors.text : colors.textMuted, children: configKey })
3805
+ /* @__PURE__ */ jsx14(Text14, { color: isSelected ? colors.text : colors.textMuted, children: configKey })
3659
3806
  ] }),
3660
- /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
3807
+ /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3661
3808
  " ",
3662
3809
  option.description
3663
3810
  ] }),
3664
- /* @__PURE__ */ jsxs12(Box12, { children: [
3665
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " " }),
3666
- option.type === "boolean" /* Boolean */ ? /* @__PURE__ */ jsxs12(Text13, { color: colors.text, children: [
3811
+ /* @__PURE__ */ jsxs13(Box13, { children: [
3812
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: " " }),
3813
+ option.type === "boolean" /* Boolean */ ? /* @__PURE__ */ jsxs13(Text14, { color: colors.text, children: [
3667
3814
  "[",
3668
3815
  values[configKey] ? "x" : " ",
3669
3816
  "] ",
3670
3817
  values[configKey] ? "enabled" : "disabled"
3671
- ] }) : option.type === "select" /* Select */ && option.options ? /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: option.options.map((opt, i) => {
3818
+ ] }) : option.type === "select" /* Select */ && option.options ? /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: option.options.map((opt, i) => {
3672
3819
  const isCurrentValue = String(values[configKey]) === opt;
3673
3820
  const isEditingThis = editingSelect === configKey;
3674
3821
  const isHighlighted = isEditingThis && selectOptionIndex === i;
3675
- return /* @__PURE__ */ jsxs12(Text13, { children: [
3676
- i > 0 && /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: " \xB7 " }),
3677
- /* @__PURE__ */ jsx13(
3678
- Text13,
3822
+ return /* @__PURE__ */ jsxs13(Text14, { children: [
3823
+ i > 0 && /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: " \xB7 " }),
3824
+ /* @__PURE__ */ jsx14(
3825
+ Text14,
3679
3826
  {
3680
3827
  color: isHighlighted ? "#ffffff" : isCurrentValue ? colors.primary : colors.textMuted,
3681
3828
  backgroundColor: isHighlighted ? colors.primary : void 0,
@@ -3683,9 +3830,9 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3683
3830
  }
3684
3831
  )
3685
3832
  ] }, opt);
3686
- }) }) : isEditing ? /* @__PURE__ */ jsxs12(Box12, { children: [
3687
- /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: "> " }),
3688
- /* @__PURE__ */ jsx13(
3833
+ }) }) : isEditing ? /* @__PURE__ */ jsxs13(Box13, { children: [
3834
+ /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "> " }),
3835
+ /* @__PURE__ */ jsx14(
3689
3836
  TextInput2,
3690
3837
  {
3691
3838
  value: editValue,
@@ -3693,17 +3840,17 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3693
3840
  onSubmit: handleSubmitEdit
3694
3841
  }
3695
3842
  )
3696
- ] }) : /* @__PURE__ */ jsx13(Text13, { color: colors.text, children: String(values[configKey]) || "(not set)" })
3843
+ ] }) : /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: String(values[configKey]) || "(not set)" })
3697
3844
  ] })
3698
3845
  ] }, configKey);
3699
3846
  }),
3700
- showSaveButton && /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { children: [
3701
- /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
3847
+ showSaveButton && /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { children: [
3848
+ /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3702
3849
  selectedIndex === configKeys.length ? ">" : " ",
3703
3850
  " "
3704
3851
  ] }),
3705
- /* @__PURE__ */ jsxs12(
3706
- Text13,
3852
+ /* @__PURE__ */ jsxs13(
3853
+ Text14,
3707
3854
  {
3708
3855
  backgroundColor: selectedIndex === configKeys.length ? colors.primary : void 0,
3709
3856
  color: selectedIndex === configKeys.length ? "#ffffff" : colors.textMuted,
@@ -3716,21 +3863,21 @@ function SkillConfigScreen({ skill, onComplete, onCancel }) {
3716
3863
  }
3717
3864
  )
3718
3865
  ] }) }),
3719
- showBottomIndicator && /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text13, { color: colors.textDim, children: [
3866
+ showBottomIndicator && /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3720
3867
  " \u2193 ",
3721
3868
  totalItems - scrollOffset - MAX_VISIBLE_CONFIG_ITEMS,
3722
3869
  " more"
3723
3870
  ] }) })
3724
3871
  ] }),
3725
- /* @__PURE__ */ jsx13(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: colors.textDim, children: editingField ? "enter save \xB7 esc cancel" : editingSelect ? "\u2190\u2192 choose \xB7 enter select \xB7 esc cancel" : "\u2191\u2193 select \xB7 enter toggle/edit \xB7 esc back" }) })
3872
+ /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: editingField ? "enter save \xB7 esc cancel" : editingSelect ? "\u2190\u2192 choose \xB7 enter select \xB7 esc cancel" : "\u2191\u2193 select \xB7 enter toggle/edit \xB7 esc back" }) })
3726
3873
  ] });
3727
3874
  }
3728
3875
 
3729
3876
  // src/commands/tui/views/ReposManagementScreen.tsx
3730
- import { Box as Box13, Text as Text14, useInput as useInput7 } from "ink";
3877
+ import { Box as Box14, Text as Text15, useInput as useInput7 } from "ink";
3731
3878
  import TextInput3 from "ink-text-input";
3732
3879
  import { useState as useState7 } from "react";
3733
- import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
3880
+ import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
3734
3881
  function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3735
3882
  const [repos2, setRepos] = useState7(() => getRepos());
3736
3883
  const [selectedIndex, setSelectedIndex] = useState7(0);
@@ -3818,11 +3965,11 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3818
3965
  }
3819
3966
  }, { isActive: screen === "confirm-delete" });
3820
3967
  if (screen === "add-name") {
3821
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 2, children: [
3822
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, bold: true, children: "Add Repository" }),
3823
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3824
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "Repository name: " }),
3825
- /* @__PURE__ */ jsx14(
3968
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
3969
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Add Repository" }),
3970
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
3971
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "Repository name: " }),
3972
+ /* @__PURE__ */ jsx15(
3826
3973
  TextInput3,
3827
3974
  {
3828
3975
  value: newRepoName,
@@ -3835,23 +3982,23 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3835
3982
  }
3836
3983
  )
3837
3984
  ] }),
3838
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "esc cancel" }) })
3985
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "esc cancel" }) })
3839
3986
  ] });
3840
3987
  }
3841
3988
  if (screen === "add-path") {
3842
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 2, children: [
3843
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, bold: true, children: "Add Repository" }),
3844
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3845
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "Name: " }),
3846
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: newRepoName })
3989
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
3990
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Add Repository" }),
3991
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
3992
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "Name: " }),
3993
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, children: newRepoName })
3847
3994
  ] }),
3848
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3849
- /* @__PURE__ */ jsxs13(Text14, { color: colors.textDim, children: [
3995
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
3996
+ /* @__PURE__ */ jsxs14(Text15, { color: colors.textDim, children: [
3850
3997
  "Path (e.g., ~/src/github.com/",
3851
3998
  newRepoName,
3852
3999
  "): "
3853
4000
  ] }),
3854
- /* @__PURE__ */ jsx14(
4001
+ /* @__PURE__ */ jsx15(
3855
4002
  TextInput3,
3856
4003
  {
3857
4004
  value: newRepoPath,
@@ -3865,23 +4012,23 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3865
4012
  }
3866
4013
  )
3867
4014
  ] }),
3868
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "esc back" }) })
4015
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "esc back" }) })
3869
4016
  ] });
3870
4017
  }
3871
4018
  if (screen === "add-desc") {
3872
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 2, children: [
3873
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, bold: true, children: "Add Repository" }),
3874
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3875
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "Name: " }),
3876
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: newRepoName })
4019
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
4020
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Add Repository" }),
4021
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
4022
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "Name: " }),
4023
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, children: newRepoName })
3877
4024
  ] }),
3878
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3879
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "Path: " }),
3880
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: newRepoPath })
4025
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
4026
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "Path: " }),
4027
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, children: newRepoPath })
3881
4028
  ] }),
3882
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, children: [
3883
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "Description (optional): " }),
3884
- /* @__PURE__ */ jsx14(
4029
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
4030
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "Description (optional): " }),
4031
+ /* @__PURE__ */ jsx15(
3885
4032
  TextInput3,
3886
4033
  {
3887
4034
  value: newRepoDesc,
@@ -3890,28 +4037,28 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3890
4037
  }
3891
4038
  )
3892
4039
  ] }),
3893
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "enter save \xB7 esc back" }) })
4040
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "enter save \xB7 esc back" }) })
3894
4041
  ] });
3895
4042
  }
3896
4043
  if (screen === "confirm-delete" && repoToDelete) {
3897
4044
  const repo = repos2.find((r) => r.name === repoToDelete);
3898
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 2, children: [
3899
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, bold: true, children: "Remove Repository" }),
3900
- /* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
3901
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, children: repo?.name }),
3902
- /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: repo?.path })
4045
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
4046
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Remove Repository" }),
4047
+ /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, flexDirection: "column", children: [
4048
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, children: repo?.name }),
4049
+ /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: repo?.path })
3903
4050
  ] }),
3904
- /* @__PURE__ */ jsx14(Box13, { marginTop: 2, children: /* @__PURE__ */ jsx14(Text14, { color: colors.error, children: "Remove this repository from the registry?" }) }),
3905
- /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "y yes \xB7 n no" }) })
4051
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 2, children: /* @__PURE__ */ jsx15(Text15, { color: colors.error, children: "Remove this repository from the registry?" }) }),
4052
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "y yes \xB7 n no" }) })
3906
4053
  ] });
3907
4054
  }
3908
- return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 2, children: [
3909
- /* @__PURE__ */ jsx14(Text14, { color: colors.text, bold: true, children: "Manage Repositories" }),
3910
- message && /* @__PURE__ */ jsx14(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: message.type === "success" ? colors.success : colors.error, children: message.text }) }),
3911
- /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: 1, children: [
3912
- repos2.length === 0 ? /* @__PURE__ */ jsx14(Box13, { marginY: 1, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: "No repos configured" }) }) : repos2.map((repo, index) => /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", marginTop: index > 0 ? 1 : 0, children: [
3913
- /* @__PURE__ */ jsx14(Box13, { children: /* @__PURE__ */ jsxs13(
3914
- Text14,
4055
+ return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
4056
+ /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Manage Repositories" }),
4057
+ message && /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: message.type === "success" ? colors.success : colors.error, children: message.text }) }),
4058
+ /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: 1, children: [
4059
+ repos2.length === 0 ? /* @__PURE__ */ jsx15(Box14, { marginY: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "No repos configured" }) }) : repos2.map((repo, index) => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: index > 0 ? 1 : 0, children: [
4060
+ /* @__PURE__ */ jsx15(Box14, { children: /* @__PURE__ */ jsxs14(
4061
+ Text15,
3915
4062
  {
3916
4063
  color: selectedIndex === index ? colors.primary : colors.text,
3917
4064
  bold: selectedIndex === index,
@@ -3921,11 +4068,11 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3921
4068
  ]
3922
4069
  }
3923
4070
  ) }),
3924
- /* @__PURE__ */ jsx14(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: repo.path }) }),
3925
- repo.description && /* @__PURE__ */ jsx14(Box13, { paddingLeft: 2, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: repo.description }) })
4071
+ /* @__PURE__ */ jsx15(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: repo.path }) }),
4072
+ repo.description && /* @__PURE__ */ jsx15(Box14, { paddingLeft: 2, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: repo.description }) })
3926
4073
  ] }, repo.name)),
3927
- /* @__PURE__ */ jsx14(Box13, { marginTop: repos2.length > 0 ? 2 : 0, children: /* @__PURE__ */ jsxs13(
3928
- Text14,
4074
+ /* @__PURE__ */ jsx15(Box14, { marginTop: repos2.length > 0 ? 2 : 0, children: /* @__PURE__ */ jsxs14(
4075
+ Text15,
3929
4076
  {
3930
4077
  color: selectedIndex === repos2.length ? colors.primary : colors.textDim,
3931
4078
  bold: selectedIndex === repos2.length,
@@ -3936,13 +4083,13 @@ function ReposManagementScreen({ onComplete: _onComplete, onCancel }) {
3936
4083
  }
3937
4084
  ) })
3938
4085
  ] }),
3939
- /* @__PURE__ */ jsx14(Box13, { marginTop: 2, children: /* @__PURE__ */ jsx14(Text14, { color: colors.textDim, children: repos2.length > 0 && selectedIndex < repos2.length ? "enter remove \xB7 esc back" : "\u2191\u2193 navigate \xB7 enter select \xB7 esc back" }) })
4086
+ /* @__PURE__ */ jsx15(Box14, { marginTop: 2, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: repos2.length > 0 && selectedIndex < repos2.length ? "enter remove \xB7 esc back" : "\u2191\u2193 navigate \xB7 enter select \xB7 esc back" }) })
3940
4087
  ] });
3941
4088
  }
3942
4089
 
3943
4090
  // src/commands/tui/views/ReposViewerScreen.tsx
3944
- import { Box as Box14, Text as Text15, useInput as useInput8 } from "ink";
3945
- import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
4091
+ import { Box as Box15, Text as Text16, useInput as useInput8 } from "ink";
4092
+ import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
3946
4093
  function ReposViewerScreen({ onClose }) {
3947
4094
  const repos2 = getRepos();
3948
4095
  useInput8((input, key) => {
@@ -3950,17 +4097,17 @@ function ReposViewerScreen({ onClose }) {
3950
4097
  onClose();
3951
4098
  }
3952
4099
  });
3953
- return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 2, children: [
3954
- /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: "Repositories" }),
3955
- repos2.length === 0 ? /* @__PURE__ */ jsx15(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "No repos configured" }) }) : /* @__PURE__ */ jsx15(Box14, { flexDirection: "column", marginTop: 1, children: repos2.map((repo, index) => /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", marginTop: index > 0 ? 2 : 0, children: [
3956
- /* @__PURE__ */ jsx15(Text15, { color: colors.text, bold: true, children: repo.name }),
3957
- /* @__PURE__ */ jsx15(Box14, { marginTop: 0, children: /* @__PURE__ */ jsxs14(Text15, { color: colors.textDim, children: [
4100
+ return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 2, children: [
4101
+ /* @__PURE__ */ jsx16(Text16, { color: colors.text, bold: true, children: "Repositories" }),
4102
+ repos2.length === 0 ? /* @__PURE__ */ jsx16(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: "No repos configured" }) }) : /* @__PURE__ */ jsx16(Box15, { flexDirection: "column", marginTop: 1, children: repos2.map((repo, index) => /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginTop: index > 0 ? 2 : 0, children: [
4103
+ /* @__PURE__ */ jsx16(Text16, { color: colors.text, bold: true, children: repo.name }),
4104
+ /* @__PURE__ */ jsx16(Box15, { marginTop: 0, children: /* @__PURE__ */ jsxs15(Text16, { color: colors.textDim, children: [
3958
4105
  "Path: ",
3959
4106
  repo.path
3960
4107
  ] }) }),
3961
- repo.description && /* @__PURE__ */ jsx15(Box14, { marginTop: 0, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: repo.description }) })
4108
+ repo.description && /* @__PURE__ */ jsx16(Box15, { marginTop: 0, children: /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: repo.description }) })
3962
4109
  ] }, repo.name)) }),
3963
- /* @__PURE__ */ jsx15(Box14, { marginTop: 2, children: /* @__PURE__ */ jsx15(Text15, { color: colors.textDim, children: "esc back" }) })
4110
+ /* @__PURE__ */ jsx16(Box15, { marginTop: 2, children: /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: "esc back" }) })
3964
4111
  ] });
3965
4112
  }
3966
4113
 
@@ -4097,12 +4244,23 @@ function useToolUpdates({ onUpdateComplete }) {
4097
4244
  }
4098
4245
 
4099
4246
  // src/commands/tui.tsx
4100
- import { Fragment as Fragment2, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
4247
+ import { Fragment as Fragment2, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
4101
4248
  var exitMessage = null;
4249
+ var exitCommand = null;
4250
+ var __tui_dirname = dirname7(fileURLToPath5(import.meta.url));
4251
+ var INTEGRATIONS_DIR = join11(__tui_dirname, "../integrations");
4252
+ function loadIntegrationReference(integration, filename) {
4253
+ try {
4254
+ return readFileSync9(join11(INTEGRATIONS_DIR, integration, "references", filename), "utf-8");
4255
+ } catch {
4256
+ return null;
4257
+ }
4258
+ }
4102
4259
  function App() {
4103
4260
  const { exit } = useApp2();
4104
4261
  const tabs = [
4105
4262
  { id: "tools", label: "Tools" },
4263
+ { id: "integrations", label: "Integrations" },
4106
4264
  { id: "settings", label: "Settings" }
4107
4265
  ];
4108
4266
  const [activeTab, setActiveTab] = useState10("tools");
@@ -4217,7 +4375,7 @@ function App() {
4217
4375
  setSelectedAction(0);
4218
4376
  }
4219
4377
  if (key.downArrow) {
4220
- const maxIndex = activeTab === "tools" ? tools.length - 1 : 0;
4378
+ const maxIndex = activeTab === "tools" ? tools.length - 1 : activeTab === "integrations" ? 0 : 0;
4221
4379
  setSelectedIndex((prev) => {
4222
4380
  const newIndex = Math.min(maxIndex, prev + 1);
4223
4381
  if (newIndex >= scrollOffset + MAX_VISIBLE_ITEMS) {
@@ -4230,6 +4388,8 @@ function App() {
4230
4388
  if (key.return) {
4231
4389
  if (activeTab === "tools" && tools.length > 0) {
4232
4390
  setView("detail");
4391
+ } else if (activeTab === "integrations") {
4392
+ setView("detail");
4233
4393
  } else if (activeTab === "settings") {
4234
4394
  setView("detail");
4235
4395
  }
@@ -4239,6 +4399,37 @@ function App() {
4239
4399
  setView("menu");
4240
4400
  setSelectedAction(0);
4241
4401
  }
4402
+ if (activeTab === "integrations") {
4403
+ const hasClientCreds = !!process.env.SLACK_CLIENT_ID && !!process.env.SLACK_CLIENT_SECRET;
4404
+ const canRunSetup = hasClientCreds && !process.env.SLACK_USER_TOKEN;
4405
+ const intActions = [
4406
+ ...canRunSetup ? [{ id: "setup" }] : [],
4407
+ { id: "guide" }
4408
+ ];
4409
+ const maxIntAction = intActions.length - 1;
4410
+ if (key.leftArrow) {
4411
+ setSelectedAction((prev) => Math.max(0, prev - 1));
4412
+ }
4413
+ if (key.rightArrow) {
4414
+ setSelectedAction((prev) => Math.min(maxIntAction, prev + 1));
4415
+ }
4416
+ if (key.return) {
4417
+ const actionId = intActions[selectedAction]?.id;
4418
+ if (actionId === "setup") {
4419
+ exitCommand = ["droid", "integrations", "setup", "slack"];
4420
+ exit();
4421
+ } else if (actionId === "guide") {
4422
+ const content = loadIntegrationReference("slack", "setup.md");
4423
+ if (content) {
4424
+ setPreviousView("detail");
4425
+ setReadmeContent({ title: "Slack Integration Setup", content });
4426
+ setView("readme");
4427
+ } else {
4428
+ setMessage({ text: "Could not load setup guide", type: "error" });
4429
+ }
4430
+ }
4431
+ }
4432
+ }
4242
4433
  if (activeTab === "settings") {
4243
4434
  if (key.leftArrow) {
4244
4435
  setSelectedAction((prev) => Math.max(0, prev - 1));
@@ -4340,7 +4531,7 @@ function App() {
4340
4531
  (s) => s.name === (selectedTool.includes.skills.find((sk) => sk.required)?.name || selectedTool.name)
4341
4532
  ) : null;
4342
4533
  if (view === "welcome") {
4343
- return /* @__PURE__ */ jsx16(
4534
+ return /* @__PURE__ */ jsx17(
4344
4535
  WelcomeScreen,
4345
4536
  {
4346
4537
  updateInfo,
@@ -4353,7 +4544,7 @@ function App() {
4353
4544
  );
4354
4545
  }
4355
4546
  if (view === "tool-updates" && toolUpdates.length > 0) {
4356
- return /* @__PURE__ */ jsx16(
4547
+ return /* @__PURE__ */ jsx17(
4357
4548
  ToolUpdatePrompt,
4358
4549
  {
4359
4550
  toolUpdates,
@@ -4365,7 +4556,7 @@ function App() {
4365
4556
  );
4366
4557
  }
4367
4558
  if (view === "setup") {
4368
- return /* @__PURE__ */ jsx16(
4559
+ return /* @__PURE__ */ jsx17(
4369
4560
  SetupScreen,
4370
4561
  {
4371
4562
  onComplete: () => {
@@ -4382,7 +4573,7 @@ function App() {
4382
4573
  );
4383
4574
  }
4384
4575
  if (view === "readme" && readmeContent) {
4385
- return /* @__PURE__ */ jsx16(
4576
+ return /* @__PURE__ */ jsx17(
4386
4577
  ReadmeViewer,
4387
4578
  {
4388
4579
  title: readmeContent.title,
@@ -4395,7 +4586,7 @@ function App() {
4395
4586
  );
4396
4587
  }
4397
4588
  if (view === "explorer" && selectedTool) {
4398
- return /* @__PURE__ */ jsx16(
4589
+ return /* @__PURE__ */ jsx17(
4399
4590
  ToolExplorer,
4400
4591
  {
4401
4592
  tool: selectedTool,
@@ -4411,7 +4602,7 @@ function App() {
4411
4602
  );
4412
4603
  }
4413
4604
  if (view === "configure" && selectedSkillForConfig) {
4414
- return /* @__PURE__ */ jsx16(
4605
+ return /* @__PURE__ */ jsx17(
4415
4606
  SkillConfigScreen,
4416
4607
  {
4417
4608
  skill: selectedSkillForConfig,
@@ -4429,7 +4620,7 @@ function App() {
4429
4620
  );
4430
4621
  }
4431
4622
  if (view === "view-repos") {
4432
- return /* @__PURE__ */ jsx16(
4623
+ return /* @__PURE__ */ jsx17(
4433
4624
  ReposViewerScreen,
4434
4625
  {
4435
4626
  onClose: () => {
@@ -4439,7 +4630,7 @@ function App() {
4439
4630
  );
4440
4631
  }
4441
4632
  if (view === "repos") {
4442
- return /* @__PURE__ */ jsx16(
4633
+ return /* @__PURE__ */ jsx17(
4443
4634
  ReposManagementScreen,
4444
4635
  {
4445
4636
  onComplete: () => {
@@ -4455,43 +4646,43 @@ function App() {
4455
4646
  }
4456
4647
  );
4457
4648
  }
4458
- return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "row", padding: 1, children: [
4459
- /* @__PURE__ */ jsxs15(
4460
- Box15,
4649
+ return /* @__PURE__ */ jsxs16(Box16, { flexDirection: "row", padding: 1, children: [
4650
+ /* @__PURE__ */ jsxs16(
4651
+ Box16,
4461
4652
  {
4462
4653
  flexDirection: "column",
4463
4654
  width: 44,
4464
4655
  borderStyle: "single",
4465
4656
  borderColor: colors.border,
4466
4657
  children: [
4467
- /* @__PURE__ */ jsx16(Box15, { paddingX: 1, children: /* @__PURE__ */ jsxs15(Text16, { children: [
4468
- /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: "[" }),
4469
- /* @__PURE__ */ jsx16(Text16, { color: colors.primary, children: "\u25CF" }),
4470
- /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: " " }),
4471
- /* @__PURE__ */ jsx16(Text16, { color: colors.primary, children: "\u25CF" }),
4472
- /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: "] " }),
4473
- /* @__PURE__ */ jsx16(Text16, { color: colors.textMuted, children: "droid" }),
4474
- /* @__PURE__ */ jsxs15(Text16, { color: colors.textDim, children: [
4658
+ /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { children: [
4659
+ /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: "[" }),
4660
+ /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: "\u25CF" }),
4661
+ /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: " " }),
4662
+ /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: "\u25CF" }),
4663
+ /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: "] " }),
4664
+ /* @__PURE__ */ jsx17(Text17, { color: colors.textMuted, children: "droid" }),
4665
+ /* @__PURE__ */ jsxs16(Text17, { color: colors.textDim, children: [
4475
4666
  " v",
4476
4667
  getVersion()
4477
4668
  ] })
4478
4669
  ] }) }),
4479
- /* @__PURE__ */ jsx16(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx16(
4670
+ /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx17(
4480
4671
  PlatformBadges,
4481
4672
  {
4482
4673
  detected: detectedPlatforms,
4483
4674
  ignored: loadConfig().ignored_platforms ?? []
4484
4675
  }
4485
4676
  ) }),
4486
- /* @__PURE__ */ jsx16(Box15, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx16(TabBar, { tabs, activeTab }) }),
4487
- /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", marginTop: 1, children: [
4488
- activeTab === "tools" && /* @__PURE__ */ jsxs15(Fragment2, { children: [
4489
- scrollOffset > 0 && /* @__PURE__ */ jsx16(Box15, { paddingX: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: colors.textDim, children: [
4677
+ /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx17(TabBar, { tabs, activeTab }) }),
4678
+ /* @__PURE__ */ jsxs16(Box16, { flexDirection: "column", marginTop: 1, children: [
4679
+ activeTab === "tools" && /* @__PURE__ */ jsxs16(Fragment2, { children: [
4680
+ scrollOffset > 0 && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: colors.textDim, children: [
4490
4681
  "\u2191 ",
4491
4682
  scrollOffset,
4492
4683
  " more"
4493
4684
  ] }) }),
4494
- tools.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS).map((tool, index) => /* @__PURE__ */ jsx16(
4685
+ tools.slice(scrollOffset, scrollOffset + MAX_VISIBLE_ITEMS).map((tool, index) => /* @__PURE__ */ jsx17(
4495
4686
  ToolItem,
4496
4687
  {
4497
4688
  tool,
@@ -4501,30 +4692,35 @@ function App() {
4501
4692
  },
4502
4693
  tool.name
4503
4694
  )),
4504
- scrollOffset + MAX_VISIBLE_ITEMS < tools.length && /* @__PURE__ */ jsx16(Box15, { paddingX: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: colors.textDim, children: [
4695
+ scrollOffset + MAX_VISIBLE_ITEMS < tools.length && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: colors.textDim, children: [
4505
4696
  "\u2193 ",
4506
4697
  tools.length - scrollOffset - MAX_VISIBLE_ITEMS,
4507
4698
  " more"
4508
4699
  ] }) }),
4509
- tools.length > MAX_VISIBLE_ITEMS && /* @__PURE__ */ jsx16(Box15, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { color: colors.textDim, children: [
4700
+ tools.length > MAX_VISIBLE_ITEMS && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsxs16(Text17, { color: colors.textDim, children: [
4510
4701
  tools.length,
4511
4702
  " tools total"
4512
4703
  ] }) })
4513
4704
  ] }),
4514
- activeTab === "settings" && /* @__PURE__ */ jsx16(Box15, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: "View and edit config" }) })
4705
+ activeTab === "integrations" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsxs16(Text17, { children: [
4706
+ selectedIndex === 0 ? /* @__PURE__ */ jsx17(Text17, { color: colors.primary, children: ">" }) : /* @__PURE__ */ jsx17(Text17, { children: " " }),
4707
+ /* @__PURE__ */ jsx17(Text17, { children: " Slack" }),
4708
+ process.env.SLACK_USER_TOKEN ? /* @__PURE__ */ jsx17(Text17, { color: colors.success, children: " \u2713" }) : /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: " \u2717" })
4709
+ ] }) }),
4710
+ activeTab === "settings" && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: "View and edit config" }) })
4515
4711
  ] }),
4516
- message && /* @__PURE__ */ jsx16(Box15, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx16(
4517
- Text16,
4712
+ message && /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx17(
4713
+ Text17,
4518
4714
  {
4519
4715
  color: message.type === "success" ? colors.success : colors.error,
4520
4716
  children: message.text
4521
4717
  }
4522
4718
  ) }),
4523
- /* @__PURE__ */ jsx16(Box15, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { color: colors.textDim, children: view === "menu" ? "\u2190\u2192 \u2191\u2193 enter q" : "\u2190\u2192 enter esc q" }) })
4719
+ /* @__PURE__ */ jsx17(Box16, { paddingX: 1, marginTop: 1, children: /* @__PURE__ */ jsx17(Text17, { color: colors.textDim, children: view === "menu" ? "\u2190\u2192 \u2191\u2193 enter q" : "\u2190\u2192 enter esc q" }) })
4524
4720
  ]
4525
4721
  }
4526
4722
  ),
4527
- activeTab === "tools" && /* @__PURE__ */ jsx16(
4723
+ activeTab === "tools" && /* @__PURE__ */ jsx17(
4528
4724
  ToolDetails,
4529
4725
  {
4530
4726
  tool: selectedTool,
@@ -4532,7 +4728,14 @@ function App() {
4532
4728
  selectedAction
4533
4729
  }
4534
4730
  ),
4535
- activeTab === "settings" && /* @__PURE__ */ jsx16(
4731
+ activeTab === "integrations" && /* @__PURE__ */ jsx17(
4732
+ IntegrationsDetails,
4733
+ {
4734
+ isFocused: view === "detail",
4735
+ selectedAction
4736
+ }
4737
+ ),
4738
+ activeTab === "settings" && /* @__PURE__ */ jsx17(
4536
4739
  SettingsDetails,
4537
4740
  {
4538
4741
  isFocused: view === "detail",
@@ -4546,9 +4749,15 @@ function App() {
4546
4749
  async function tuiCommand() {
4547
4750
  process.stdout.write("\x1B[?1049h");
4548
4751
  process.stdout.write("\x1B[H");
4549
- const { waitUntilExit } = render(/* @__PURE__ */ jsx16(App, {}));
4752
+ const { waitUntilExit } = render(/* @__PURE__ */ jsx17(App, {}));
4550
4753
  await waitUntilExit();
4551
4754
  process.stdout.write("\x1B[?1049l");
4755
+ if (exitCommand) {
4756
+ const [cmd, ...args] = exitCommand;
4757
+ exitCommand = null;
4758
+ spawnSync3(cmd, args, { stdio: "inherit" });
4759
+ return;
4760
+ }
4552
4761
  if (exitMessage) {
4553
4762
  console.log(exitMessage);
4554
4763
  exitMessage = null;
@@ -4559,7 +4768,7 @@ async function tuiCommand() {
4559
4768
  import chalk9 from "chalk";
4560
4769
  import { spawn } from "child_process";
4561
4770
  import { existsSync as existsSync10 } from "fs";
4562
- import { join as join11 } from "path";
4771
+ import { join as join12 } from "path";
4563
4772
  function getRuntime(toolPath) {
4564
4773
  if (toolPath.endsWith(".ts") || toolPath.endsWith(".js")) {
4565
4774
  return { cmd: "bun", args: ["run", toolPath] };
@@ -4572,12 +4781,12 @@ function getRuntime(toolPath) {
4572
4781
  async function execCommand(tool, script, args) {
4573
4782
  const config = loadConfig();
4574
4783
  const skillsPath = getSkillsPath(config.platform);
4575
- const toolDir = join11(skillsPath, tool);
4784
+ const toolDir = join12(skillsPath, tool);
4576
4785
  if (!existsSync10(toolDir)) {
4577
4786
  console.error(chalk9.red(`Tool '${tool}' not found at ${toolDir}`));
4578
4787
  process.exit(1);
4579
4788
  }
4580
- const scriptsDir = join11(toolDir, "scripts");
4789
+ const scriptsDir = join12(toolDir, "scripts");
4581
4790
  if (!existsSync10(scriptsDir)) {
4582
4791
  console.error(chalk9.red(`No scripts directory in tool '${tool}'`));
4583
4792
  process.exit(1);
@@ -4585,7 +4794,7 @@ async function execCommand(tool, script, args) {
4585
4794
  const extensions = [".ts", ".js", ".py"];
4586
4795
  let scriptPath = null;
4587
4796
  for (const ext of extensions) {
4588
- const candidate = join11(scriptsDir, script + ext);
4797
+ const candidate = join12(scriptsDir, script + ext);
4589
4798
  if (existsSync10(candidate)) {
4590
4799
  scriptPath = candidate;
4591
4800
  break;
@@ -4748,30 +4957,246 @@ async function reposGetCommand(name) {
4748
4957
  console.log(JSON.stringify(repo, null, 2));
4749
4958
  }
4750
4959
 
4751
- // src/commands/auth.ts
4960
+ // src/commands/integrations.ts
4752
4961
  import inquirer5 from "inquirer";
4753
4962
  import chalk11 from "chalk";
4754
4963
  import { execSync as execSync6 } from "child_process";
4964
+ import { createServer } from "https";
4965
+ import { readFileSync as readFileSync10, mkdirSync as mkdirSync7, appendFileSync as appendFileSync2, writeFileSync as writeFileSync6 } from "fs";
4966
+ import { join as join13 } from "path";
4755
4967
 
4756
- // src/lib/secrets.ts
4968
+ // src/integrations/slack/index.ts
4969
+ import { WebClient } from "@slack/web-api";
4970
+ function getSlackToken() {
4971
+ return process.env.SLACK_USER_TOKEN;
4972
+ }
4757
4973
  function hasSlackToken() {
4758
4974
  return !!process.env.SLACK_USER_TOKEN;
4759
4975
  }
4976
+ async function postMessage(params) {
4977
+ const token = getSlackToken();
4978
+ if (!token) {
4979
+ return { ok: false, error: "SLACK_USER_TOKEN not set. Run: droid integrations setup slack" };
4980
+ }
4981
+ const client = new WebClient(token);
4982
+ try {
4983
+ const result = await client.chat.postMessage({
4984
+ channel: params.channel,
4985
+ text: params.text,
4986
+ unfurl_links: params.unfurl_links
4987
+ });
4988
+ return {
4989
+ ok: result.ok ?? false,
4990
+ ts: result.ts,
4991
+ channel: result.channel
4992
+ };
4993
+ } catch (error) {
4994
+ return {
4995
+ ok: false,
4996
+ error: error instanceof Error ? error.message : "Unknown error"
4997
+ };
4998
+ }
4999
+ }
5000
+ async function editCanvas(params) {
5001
+ const token = getSlackToken();
5002
+ if (!token) {
5003
+ return { ok: false, error: "SLACK_USER_TOKEN not set. Run: droid integrations setup slack" };
5004
+ }
5005
+ const client = new WebClient(token);
5006
+ try {
5007
+ const result = await client.canvases.edit({
5008
+ canvas_id: params.canvas_id,
5009
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5010
+ changes: params.changes
5011
+ });
5012
+ return { ok: result.ok ?? false };
5013
+ } catch (error) {
5014
+ return {
5015
+ ok: false,
5016
+ error: error instanceof Error ? error.message : "Unknown error"
5017
+ };
5018
+ }
5019
+ }
5020
+ async function exchangeOAuthCode(clientId, clientSecret, code, redirectUri) {
5021
+ try {
5022
+ const client = new WebClient();
5023
+ const result = await client.oauth.v2.access({
5024
+ client_id: clientId,
5025
+ client_secret: clientSecret,
5026
+ code,
5027
+ redirect_uri: redirectUri
5028
+ });
5029
+ if (!result.ok) {
5030
+ return { ok: false, error: String(result.error ?? "Unknown error") };
5031
+ }
5032
+ const token = result.authed_user?.access_token;
5033
+ if (!token) {
5034
+ return { ok: false, error: "No user token in response" };
5035
+ }
5036
+ return { ok: true, token };
5037
+ } catch (error) {
5038
+ return {
5039
+ ok: false,
5040
+ error: error instanceof Error ? error.message : "Unknown error"
5041
+ };
5042
+ }
5043
+ }
4760
5044
 
4761
- // src/commands/auth.ts
5045
+ // src/commands/integrations.ts
4762
5046
  var SLACK_SCOPES = "chat:write,canvases:write";
4763
- var REDIRECT_URI = "https://localhost:9876/callback";
4764
- function getShellConfig() {
5047
+ var CALLBACK_PORT = 9876;
5048
+ var REDIRECT_URI = `https://localhost:${CALLBACK_PORT}/callback`;
5049
+ function getShellInfo() {
4765
5050
  const shell = process.env.SHELL || "/bin/zsh";
4766
- if (shell.includes("zsh")) return "~/.zshrc";
4767
- if (shell.includes("bash")) return "~/.bashrc";
4768
- if (shell.includes("fish")) return "~/.config/fish/config.fish";
4769
- return "~/.profile";
5051
+ if (shell.includes("fish")) {
5052
+ return { rcPath: "~/.config/fish/config.fish", isFish: true };
5053
+ }
5054
+ if (shell.includes("zsh")) return { rcPath: "~/.zshrc", isFish: false };
5055
+ if (shell.includes("bash")) return { rcPath: "~/.bashrc", isFish: false };
5056
+ return { rcPath: "~/.profile", isFish: false };
5057
+ }
5058
+ function expandHome(path) {
5059
+ if (path.startsWith("~/")) {
5060
+ return path.replace("~", process.env.HOME || "");
5061
+ }
5062
+ return path;
5063
+ }
5064
+ function buildExportLine(key, value, isFish) {
5065
+ if (isFish) {
5066
+ return `set -gx ${key} "${value}"`;
5067
+ }
5068
+ return `export ${key}="${value}"`;
5069
+ }
5070
+ var SUCCESS_HTML = `<!DOCTYPE html>
5071
+ <html>
5072
+ <head>
5073
+ <title>Droid - Slack Connected</title>
5074
+ <style>
5075
+ body { font-family: -apple-system, system-ui, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; background: #1a1a2e; colour: #eee; }
5076
+ .card { text-align: center; padding: 3rem; border-radius: 12px; background: #16213e; box-shadow: 0 4px 24px rgba(0,0,0,0.3); }
5077
+ h1 { color: #4ecca3; margin-bottom: 0.5rem; }
5078
+ p { color: #a0a0b0; }
5079
+ </style>
5080
+ </head>
5081
+ <body>
5082
+ <div class="card">
5083
+ <h1>Connected!</h1>
5084
+ <p>Slack is now linked to Droid. You can close this tab.</p>
5085
+ </div>
5086
+ </body>
5087
+ </html>`;
5088
+ var CERT_DIR = join13(process.env.HOME || "", ".droid", "certs");
5089
+ var CERT_KEY_PATH = join13(CERT_DIR, "localhost-key.pem");
5090
+ var CERT_PATH = join13(CERT_DIR, "localhost.pem");
5091
+ function getOrCreateCert() {
5092
+ try {
5093
+ const key = readFileSync10(CERT_KEY_PATH, "utf-8");
5094
+ const cert = readFileSync10(CERT_PATH, "utf-8");
5095
+ if (key && cert) return { key, cert };
5096
+ } catch {
5097
+ }
5098
+ mkdirSync7(CERT_DIR, { recursive: true });
5099
+ execSync6(
5100
+ `openssl req -x509 -newkey rsa:2048 -keyout "${CERT_KEY_PATH}" -out "${CERT_PATH}" -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" 2>/dev/null`,
5101
+ { stdio: "ignore" }
5102
+ );
5103
+ return {
5104
+ key: readFileSync10(CERT_KEY_PATH, "utf-8"),
5105
+ cert: readFileSync10(CERT_PATH, "utf-8")
5106
+ };
5107
+ }
5108
+ function trustCertInKeychain() {
5109
+ try {
5110
+ execSync6(
5111
+ `security add-trusted-cert -p ssl -r trustRoot -k ~/Library/Keychains/login.keychain-db "${CERT_PATH}"`,
5112
+ { stdio: "pipe" }
5113
+ );
5114
+ return true;
5115
+ } catch {
5116
+ return false;
5117
+ }
5118
+ }
5119
+ function waitForCallback() {
5120
+ const { key, cert } = getOrCreateCert();
5121
+ return new Promise((resolve, reject) => {
5122
+ const server = createServer({ key, cert }, (req, res) => {
5123
+ const url = new URL(req.url || "", `https://localhost:${CALLBACK_PORT}`);
5124
+ if (url.pathname === "/callback") {
5125
+ const code = url.searchParams.get("code");
5126
+ const error = url.searchParams.get("error");
5127
+ if (error) {
5128
+ clearTimeout(timeout);
5129
+ res.writeHead(400, { "Content-Type": "text/html" });
5130
+ res.end(`<h1>Error</h1><p>${error}</p>`);
5131
+ server.close();
5132
+ reject(new Error(`Slack authorization denied: ${error}`));
5133
+ return;
5134
+ }
5135
+ if (code) {
5136
+ clearTimeout(timeout);
5137
+ res.writeHead(200, { "Content-Type": "text/html" });
5138
+ res.end(SUCCESS_HTML);
5139
+ server.close();
5140
+ resolve(code);
5141
+ return;
5142
+ }
5143
+ clearTimeout(timeout);
5144
+ res.writeHead(400, { "Content-Type": "text/plain" });
5145
+ res.end("Missing code parameter");
5146
+ server.close();
5147
+ reject(new Error("No code in callback"));
5148
+ }
5149
+ });
5150
+ server.listen(CALLBACK_PORT, () => {
5151
+ });
5152
+ server.on("error", (err) => {
5153
+ clearTimeout(timeout);
5154
+ reject(new Error(`Could not start callback server on port ${CALLBACK_PORT}: ${err.message}`));
5155
+ });
5156
+ const timeout = setTimeout(() => {
5157
+ server.close();
5158
+ reject(new Error("Timed out waiting for Slack authorization (2 minutes)"));
5159
+ }, 12e4);
5160
+ });
4770
5161
  }
4771
- async function authSlackCommand() {
4772
- console.log(chalk11.bold("\n\u{1F510} Slack Authentication Setup\n"));
5162
+ function writeTokenToShellRc(key, value) {
5163
+ const { rcPath, isFish } = getShellInfo();
5164
+ const exportLine = buildExportLine(key, value, isFish);
5165
+ const fullPath = expandHome(rcPath);
5166
+ try {
5167
+ const existing = readFileSync10(fullPath, "utf-8");
5168
+ if (existing.includes(`${key}=`) || existing.includes(`${key} "`)) {
5169
+ const lines = existing.split("\n");
5170
+ const pattern = isFish ? new RegExp(`^set\\s+-gx\\s+${key}\\s+`) : new RegExp(`^export\\s+${key}=`);
5171
+ let replaced = false;
5172
+ const updated = lines.map((line) => {
5173
+ if (pattern.test(line) && !replaced) {
5174
+ replaced = true;
5175
+ return exportLine;
5176
+ }
5177
+ return line;
5178
+ });
5179
+ if (replaced) {
5180
+ writeFileSync6(fullPath, updated.join("\n"), "utf-8");
5181
+ } else {
5182
+ appendFileSync2(fullPath, `
5183
+ ${exportLine}
5184
+ `, "utf-8");
5185
+ }
5186
+ } else {
5187
+ appendFileSync2(fullPath, `
5188
+ ${exportLine}
5189
+ `, "utf-8");
5190
+ }
5191
+ return true;
5192
+ } catch {
5193
+ return false;
5194
+ }
5195
+ }
5196
+ async function integrationsSetupSlackCommand() {
5197
+ console.log(chalk11.bold("\nSlack Integration Setup\n"));
4773
5198
  if (hasSlackToken()) {
4774
- console.log(chalk11.green("\u2713 SLACK_USER_TOKEN is already set in your environment\n"));
5199
+ console.log(chalk11.green(" SLACK_USER_TOKEN is already set in your environment\n"));
4775
5200
  const { reauth } = await inquirer5.prompt([
4776
5201
  {
4777
5202
  type: "confirm",
@@ -4788,93 +5213,170 @@ async function authSlackCommand() {
4788
5213
  const clientId = process.env.SLACK_CLIENT_ID;
4789
5214
  const clientSecret = process.env.SLACK_CLIENT_SECRET;
4790
5215
  if (!clientId || !clientSecret) {
4791
- const shellConfig = getShellConfig();
4792
- console.log(chalk11.yellow("Missing Slack app credentials.\n"));
4793
- console.log(chalk11.gray('Get Client ID and Client Secret from 1Password ("Droid Slack App")'));
4794
- console.log(chalk11.gray(`and add to your ${shellConfig}:
5216
+ const { rcPath: rcPath2 } = getShellInfo();
5217
+ console.log(chalk11.yellow(" Missing Slack app credentials.\n"));
5218
+ console.log(chalk11.gray(' Get Client ID and Client Secret from 1Password ("Droid Slack App")'));
5219
+ console.log(chalk11.gray(` and add to your ${rcPath2}:
4795
5220
  `));
4796
- console.log(chalk11.white(' export SLACK_CLIENT_ID="your-client-id"'));
4797
- console.log(chalk11.white(' export SLACK_CLIENT_SECRET="your-client-secret"\n'));
4798
- console.log(chalk11.gray("Then reload and run this again:\n"));
4799
- console.log(chalk11.white(` source ${shellConfig} && droid auth slack
5221
+ console.log(chalk11.white(' export SLACK_CLIENT_ID="your-client-id"'));
5222
+ console.log(chalk11.white(' export SLACK_CLIENT_SECRET="your-client-secret"\n'));
5223
+ console.log(chalk11.gray(" Then reload and run this again:\n"));
5224
+ console.log(chalk11.white(` source ${rcPath2} && droid integrations setup slack
4800
5225
  `));
4801
5226
  return;
4802
5227
  }
5228
+ console.log(chalk11.gray(" Checking credentials...") + chalk11.green(" SLACK_CLIENT_ID found"));
4803
5229
  const authorizeUrl = `https://slack.com/oauth/v2/authorize?client_id=${clientId}&user_scope=${SLACK_SCOPES}&redirect_uri=${encodeURIComponent(REDIRECT_URI)}`;
4804
- console.log(chalk11.yellow("Step 1: Authorize in Slack\n"));
4805
- console.log(chalk11.gray(' Go to the "Droid Setup" canvas in #rnd-updates and click "Install Droid Slack App"'));
4806
- console.log(chalk11.gray(" Or open this URL:\n"));
4807
- console.log(chalk11.cyan(` ${authorizeUrl}
4808
- `));
4809
- console.log(chalk11.gray(" After clicking Allow, you'll be redirected to a URL that won't load."));
4810
- console.log(chalk11.gray(' Copy the "code" parameter from the URL bar.\n'));
4811
- const { code } = await inquirer5.prompt([
4812
- {
4813
- type: "input",
4814
- name: "code",
4815
- message: "Paste the code:",
4816
- validate: (input) => input.length > 0 || "Code is required"
5230
+ getOrCreateCert();
5231
+ const certTrusted = getConfigValue("integrations.slack.cert_trusted");
5232
+ if (!certTrusted) {
5233
+ console.log(chalk11.gray(" The OAuth callback uses a local HTTPS certificate."));
5234
+ const { trust } = await inquirer5.prompt([
5235
+ {
5236
+ type: "confirm",
5237
+ name: "trust",
5238
+ message: "Trust the local certificate? (avoids browser warnings)",
5239
+ default: true
5240
+ }
5241
+ ]);
5242
+ if (trust) {
5243
+ console.log(chalk11.gray(" Adding certificate to Keychain (you may be prompted for your password)..."));
5244
+ if (trustCertInKeychain()) {
5245
+ console.log(chalk11.green(" Certificate trusted."));
5246
+ setConfigValue("integrations.slack.cert_trusted", true);
5247
+ } else {
5248
+ console.log(chalk11.yellow(" Could not trust certificate \u2014 you may see a browser warning."));
5249
+ }
4817
5250
  }
4818
- ]);
4819
- console.log(chalk11.yellow("\nStep 2: Exchanging code for token...\n"));
5251
+ console.log("");
5252
+ }
5253
+ console.log(chalk11.gray(" Opening browser for Slack authorisation..."));
5254
+ const callbackPromise = waitForCallback();
4820
5255
  try {
4821
- const response = execSync6(
4822
- `curl -s -X POST https://slack.com/api/oauth.v2.access -d "client_id=${clientId}" -d "client_secret=${clientSecret}" -d "code=${code}" -d "redirect_uri=${REDIRECT_URI}"`,
4823
- { encoding: "utf-8" }
4824
- );
4825
- const result = JSON.parse(response);
4826
- if (!result.ok) {
4827
- console.log(chalk11.red(`\u2717 Slack API error: ${result.error}`));
4828
- if (result.error === "invalid_code") {
4829
- console.log(chalk11.gray(" The code may have expired. Try again with a fresh code."));
4830
- }
4831
- return;
5256
+ execSync6(`open "${authorizeUrl}"`, { stdio: "ignore" });
5257
+ } catch {
5258
+ console.log(chalk11.gray("\n Could not open browser. Open this URL manually:\n"));
5259
+ console.log(chalk11.cyan(` ${authorizeUrl}
5260
+ `));
5261
+ }
5262
+ console.log(chalk11.gray(" Waiting for callback..."));
5263
+ let code;
5264
+ try {
5265
+ code = await callbackPromise;
5266
+ } catch (error) {
5267
+ console.log(chalk11.red(`
5268
+ ${error instanceof Error ? error.message : "Failed to receive callback"}`));
5269
+ return;
5270
+ }
5271
+ console.log(chalk11.green(" Authorisation received"));
5272
+ const result = await exchangeOAuthCode(clientId, clientSecret, code, REDIRECT_URI);
5273
+ if (!result.ok || !result.token) {
5274
+ console.log(chalk11.red(`
5275
+ Slack API error: ${result.error}`));
5276
+ if (result.error === "invalid_code") {
5277
+ console.log(chalk11.gray(" The code may have expired. Try again."));
4832
5278
  }
4833
- const token = result.authed_user?.access_token;
4834
- if (!token) {
4835
- console.log(chalk11.red("\u2717 No user token in response"));
4836
- console.log(chalk11.gray(" Response:"), JSON.stringify(result, null, 2));
4837
- return;
5279
+ return;
5280
+ }
5281
+ console.log(chalk11.green(" Token exchanged successfully"));
5282
+ const { rcPath } = getShellInfo();
5283
+ const { writeToRc } = await inquirer5.prompt([
5284
+ {
5285
+ type: "confirm",
5286
+ name: "writeToRc",
5287
+ message: `Write SLACK_USER_TOKEN to ${rcPath}?`,
5288
+ default: true
4838
5289
  }
4839
- const shellConfig = getShellConfig();
4840
- console.log(chalk11.green("\u2713 Got token!\n"));
4841
- console.log(chalk11.yellow("Step 3: Save the token\n"));
4842
- console.log(chalk11.gray(` Add to your ${shellConfig}:
4843
- `));
4844
- console.log(chalk11.white(` export SLACK_USER_TOKEN="${token}"
5290
+ ]);
5291
+ if (writeToRc) {
5292
+ const written = writeTokenToShellRc("SLACK_USER_TOKEN", result.token);
5293
+ if (written) {
5294
+ console.log(chalk11.green(`
5295
+ Token saved to ${rcPath}`));
5296
+ console.log(chalk11.gray(" Source your shell or open a new tab to use it."));
5297
+ } else {
5298
+ console.log(chalk11.yellow(`
5299
+ Could not write to ${rcPath}. Add manually:
4845
5300
  `));
4846
- console.log(chalk11.gray(" Then reload your shell:\n"));
4847
- console.log(chalk11.white(` source ${shellConfig}
5301
+ const { isFish } = getShellInfo();
5302
+ console.log(chalk11.white(` ${buildExportLine("SLACK_USER_TOKEN", result.token, isFish)}
4848
5303
  `));
4849
- } catch (error) {
4850
- console.log(chalk11.red("\u2717 Failed to exchange code for token"));
4851
- if (error instanceof Error) {
4852
- console.log(chalk11.gray(` ${error.message}`));
4853
5304
  }
5305
+ } else {
5306
+ const { isFish } = getShellInfo();
5307
+ console.log(chalk11.yellow("\n Add to your shell config manually:\n"));
5308
+ console.log(chalk11.white(` ${buildExportLine("SLACK_USER_TOKEN", result.token, isFish)}
5309
+ `));
4854
5310
  }
5311
+ setConfigValue("integrations.slack.configured", true);
5312
+ console.log(chalk11.green("\n Slack integration configured!\n"));
4855
5313
  }
4856
- async function authStatusCommand() {
4857
- console.log(chalk11.bold("\n\u{1F510} Auth Status\n"));
5314
+ async function integrationsStatusCommand() {
5315
+ console.log(chalk11.bold("\nIntegrations Status\n"));
5316
+ console.log(chalk11.bold(" Slack"));
5317
+ const configured = getConfigValue("integrations.slack.configured");
4858
5318
  const clientId = process.env.SLACK_CLIENT_ID;
4859
5319
  const clientSecret = process.env.SLACK_CLIENT_SECRET;
4860
5320
  const hasCredentials = clientId && clientSecret;
4861
5321
  if (hasCredentials) {
4862
- console.log(chalk11.green("\u2713 Slack app credentials configured"));
5322
+ console.log(chalk11.green(" App credentials configured"));
4863
5323
  } else {
4864
- console.log(chalk11.yellow("\u2717 Slack app credentials missing"));
4865
- console.log(chalk11.gray(" Run: droid auth slack"));
5324
+ console.log(chalk11.yellow(" App credentials missing"));
5325
+ console.log(chalk11.gray(" Run: droid integrations setup slack"));
4866
5326
  }
4867
5327
  if (hasSlackToken()) {
4868
5328
  const token = process.env.SLACK_USER_TOKEN;
4869
5329
  const masked = token.slice(0, 10) + "..." + token.slice(-4);
4870
- console.log(chalk11.green("\u2713 Slack user token configured"));
4871
- console.log(chalk11.gray(` Token: ${masked}`));
5330
+ console.log(chalk11.green(" User token configured"));
5331
+ console.log(chalk11.gray(` Token: ${masked}`));
4872
5332
  } else {
4873
- console.log(chalk11.yellow("\u2717 Slack user token missing"));
5333
+ console.log(chalk11.yellow(" User token missing"));
4874
5334
  if (hasCredentials) {
4875
- console.log(chalk11.gray(" Run: droid auth slack"));
5335
+ console.log(chalk11.gray(" Run: droid integrations setup slack"));
4876
5336
  }
4877
5337
  }
5338
+ if (configured) {
5339
+ console.log(chalk11.green(" Status: configured"));
5340
+ } else {
5341
+ console.log(chalk11.yellow(" Status: not configured"));
5342
+ }
5343
+ console.log("");
5344
+ }
5345
+ async function slackPostCommand(options) {
5346
+ let input;
5347
+ try {
5348
+ input = readFileSync10(0, "utf-8").trim();
5349
+ } catch {
5350
+ console.log(JSON.stringify({ ok: false, error: "Failed to read from stdin. Pipe JSON payload via stdin." }));
5351
+ process.exit(1);
5352
+ }
5353
+ if (!input) {
5354
+ console.log(JSON.stringify({ ok: false, error: "Empty stdin. Pipe JSON payload via stdin." }));
5355
+ process.exit(1);
5356
+ }
5357
+ let payload;
5358
+ try {
5359
+ payload = JSON.parse(input);
5360
+ } catch {
5361
+ console.log(JSON.stringify({ ok: false, error: "Invalid JSON on stdin." }));
5362
+ process.exit(1);
5363
+ }
5364
+ if (options.canvas) {
5365
+ const result = await editCanvas({
5366
+ canvas_id: payload.canvas_id,
5367
+ changes: payload.changes
5368
+ });
5369
+ console.log(JSON.stringify(result));
5370
+ if (!result.ok) process.exit(1);
5371
+ } else {
5372
+ const result = await postMessage({
5373
+ channel: payload.channel,
5374
+ text: payload.text,
5375
+ unfurl_links: payload.unfurl_links
5376
+ });
5377
+ console.log(JSON.stringify(result));
5378
+ if (!result.ok) process.exit(1);
5379
+ }
4878
5380
  }
4879
5381
 
4880
5382
  // src/bin/droid.ts
@@ -4896,10 +5398,13 @@ program.command("uninstall <tool>").description("Uninstall a tool").action(unins
4896
5398
  program.command("update").description("Update droid and installed tools").option("--tools", "Only update tools").option("--cli", "Only update the CLI").argument("[tool]", "Update a specific tool").action(updateCommand);
4897
5399
  program.command("tui").description("Launch interactive TUI dashboard").action(tuiCommand);
4898
5400
  program.command("exec <tool> <script>").description("Execute a tool script").argument("[args...]", "Arguments to pass to the script").allowUnknownOption().action(execCommand);
4899
- var auth = program.command("auth").description("Set up authentication for external services");
4900
- auth.command("slack").description("Set up Slack authentication for status updates").action(authSlackCommand);
4901
- auth.command("status").description("Show authentication status").action(authStatusCommand);
4902
- auth.action(authStatusCommand);
5401
+ var integrations = program.command("integrations").description("Manage external service integrations");
5402
+ var integrationsSetup = integrations.command("setup").description("Set up an integration");
5403
+ integrationsSetup.command("slack").description("Set up Slack integration").action(integrationsSetupSlackCommand);
5404
+ integrations.command("status").description("Show integration status").action(integrationsStatusCommand);
5405
+ var integrationsSlack = integrations.command("slack").description("Slack integration commands");
5406
+ integrationsSlack.command("post").description("Post a message or edit a canvas (JSON from stdin)").option("--canvas", "Edit a canvas instead of posting a message").action((options) => slackPostCommand(options));
5407
+ integrations.action(integrationsStatusCommand);
4903
5408
  if (configExists()) {
4904
5409
  const config = loadConfig();
4905
5410
  const newPlatforms = syncNewPlatforms(config);