@orderful/droid 0.39.0 → 0.40.0

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