opendevbrowser 0.0.21 → 0.0.23

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 (54) hide show
  1. package/README.md +13 -3
  2. package/dist/{chunk-4KVXCXV3.js → chunk-2MG7BRPF.js} +521 -84
  3. package/dist/{chunk-4KVXCXV3.js.map → chunk-2MG7BRPF.js.map} +1 -1
  4. package/dist/chunk-3ILXPKSJ.js +86 -0
  5. package/dist/chunk-3ILXPKSJ.js.map +1 -0
  6. package/dist/{chunk-ZE2E7ZGH.js → chunk-K2TEHJCV.js} +240 -33
  7. package/dist/chunk-K2TEHJCV.js.map +1 -0
  8. package/dist/chunk-QVWOPIZJ.js +612 -0
  9. package/dist/chunk-QVWOPIZJ.js.map +1 -0
  10. package/dist/{chunk-3VA6XR25.js → chunk-STGGGVYT.js} +23 -100
  11. package/dist/chunk-STGGGVYT.js.map +1 -0
  12. package/dist/cli/commands/macro-resolve.d.ts.map +1 -1
  13. package/dist/cli/commands/uninstall.d.ts +1 -0
  14. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  15. package/dist/cli/daemon-commands.d.ts.map +1 -1
  16. package/dist/cli/help.d.ts.map +1 -1
  17. package/dist/cli/index.js +298 -618
  18. package/dist/cli/index.js.map +1 -1
  19. package/dist/cli/installers/postinstall-skill-sync.d.ts +15 -0
  20. package/dist/cli/installers/postinstall-skill-sync.d.ts.map +1 -0
  21. package/dist/cli/installers/postinstall-skill-sync.js +48 -0
  22. package/dist/cli/installers/postinstall-skill-sync.js.map +1 -0
  23. package/dist/cli/installers/skills.d.ts +9 -14
  24. package/dist/cli/installers/skills.d.ts.map +1 -1
  25. package/dist/cli/skill-lifecycle.d.ts +26 -0
  26. package/dist/cli/skill-lifecycle.d.ts.map +1 -0
  27. package/dist/cli/update-skill-modes.d.ts +3 -0
  28. package/dist/cli/update-skill-modes.d.ts.map +1 -0
  29. package/dist/cli/utils/workflow-message.d.ts +1 -0
  30. package/dist/cli/utils/workflow-message.d.ts.map +1 -1
  31. package/dist/index.js +25 -9
  32. package/dist/index.js.map +1 -1
  33. package/dist/opendevbrowser.js +25 -9
  34. package/dist/opendevbrowser.js.map +1 -1
  35. package/dist/providers/social/platform.d.ts.map +1 -1
  36. package/dist/providers/social/search-quality.d.ts.map +1 -1
  37. package/dist/providers/social/youtube.d.ts.map +1 -1
  38. package/dist/providers/workflow-handoff.d.ts +27 -3
  39. package/dist/providers/workflow-handoff.d.ts.map +1 -1
  40. package/dist/providers/workflows.d.ts +1 -0
  41. package/dist/providers/workflows.d.ts.map +1 -1
  42. package/dist/{providers-ZIVHHH4F.js → providers-6YVHKTOJ.js} +2 -2
  43. package/dist/public-surface/generated-manifest.d.ts +162 -4
  44. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  45. package/dist/public-surface/source.d.ts +12 -6
  46. package/dist/public-surface/source.d.ts.map +1 -1
  47. package/dist/skills/skill-loader.js +2 -1
  48. package/dist/tools/macro_resolve.d.ts.map +1 -1
  49. package/extension/manifest.json +1 -1
  50. package/package.json +6 -4
  51. package/scripts/postinstall-sync-skills.mjs +33 -0
  52. package/dist/chunk-3VA6XR25.js.map +0 -1
  53. package/dist/chunk-ZE2E7ZGH.js.map +0 -1
  54. /package/dist/{providers-ZIVHHH4F.js.map → providers-6YVHKTOJ.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -31,11 +31,26 @@ import {
31
31
  resolveExitCode,
32
32
  startDaemon,
33
33
  toCliError
34
- } from "../chunk-4KVXCXV3.js";
34
+ } from "../chunk-2MG7BRPF.js";
35
+ import "../chunk-STGGGVYT.js";
35
36
  import {
36
- getBundledSkillsDir,
37
- listBundledSkillDirectories
38
- } from "../chunk-3VA6XR25.js";
37
+ createNoOpSkillRemovalResult,
38
+ ensureDir,
39
+ getBundledSkillLifecycleTargets,
40
+ getBundledSkillTargets,
41
+ getGlobalConfigPath,
42
+ getLocalConfigPath,
43
+ hasBundledSkillArtifacts,
44
+ hasManagedBundledSkillInstall,
45
+ hasPlugin,
46
+ readConfig,
47
+ removeBundledSkillsForTargets,
48
+ removePluginFromContent,
49
+ syncBundledSkills,
50
+ syncBundledSkillsForTargets,
51
+ updateConfigContent
52
+ } from "../chunk-QVWOPIZJ.js";
53
+ import "../chunk-3ILXPKSJ.js";
39
54
  import {
40
55
  writeFileAtomic
41
56
  } from "../chunk-TBUCZX4A.js";
@@ -48,7 +63,7 @@ import {
48
63
  setDefaultLogSink,
49
64
  stderrSink,
50
65
  summarizePrimaryProviderIssue
51
- } from "../chunk-ZE2E7ZGH.js";
66
+ } from "../chunk-K2TEHJCV.js";
52
67
  import "../chunk-FUSXMW3G.js";
53
68
 
54
69
  // src/cli/args.ts
@@ -565,6 +580,9 @@ function assertCommandCoverage() {
565
580
  if (!detail || !detail.description.trim() || !detail.usage.trim()) {
566
581
  throw new Error(`Missing command help metadata: ${command}`);
567
582
  }
583
+ if (detail.examples.length === 0) {
584
+ throw new Error(`Missing command example metadata: ${command}`);
585
+ }
568
586
  for (const flag of detail.flags) {
569
587
  if (!FLAG_SET.has(flag)) {
570
588
  throw new Error(`Command help metadata references unknown flag ${flag} for ${command}`);
@@ -605,6 +623,9 @@ function assertToolCoverage() {
605
623
  if (!entry.description.trim()) {
606
624
  throw new Error(`Help tool is missing a description: ${entry.name}`);
607
625
  }
626
+ if (!entry.example?.trim()) {
627
+ throw new Error(`Help tool is missing an example: ${entry.name}`);
628
+ }
608
629
  if (seen.has(entry.name)) {
609
630
  throw new Error(`Help tool appears multiple times: ${entry.name}`);
610
631
  }
@@ -626,7 +647,9 @@ function formatCommandGroups() {
626
647
  description: detail.description,
627
648
  details: [
628
649
  { label: "usage:", value: detail.usage },
629
- { label: "flags:", value: formatFlags(detail.flags) }
650
+ { label: "flags:", value: formatFlags(detail.flags) },
651
+ ...detail.examples.map((example) => ({ label: "example:", value: example })),
652
+ ...detail.notes.map((note) => ({ label: "note:", value: note }))
630
653
  ]
631
654
  };
632
655
  });
@@ -649,7 +672,11 @@ function formatToolEntries() {
649
672
  return formatRows(HELP_TOOL_ENTRIES.map((entry) => ({
650
673
  label: entry.name,
651
674
  description: entry.description,
652
- details: entry.cliEquivalent ? [{ label: "cli:", value: entry.cliEquivalent }] : [{ label: "scope:", value: "tool-only" }]
675
+ details: [
676
+ ...entry.cliEquivalent ? [{ label: "cli:", value: entry.cliEquivalent }] : [{ label: "scope:", value: "tool-only" }],
677
+ ...entry.example ? [{ label: "example:", value: entry.example }] : [],
678
+ ...(entry.notes ?? []).map((note) => ({ label: "note:", value: note }))
679
+ ]
653
680
  })));
654
681
  }
655
682
  function formatOnboardingEntries() {
@@ -706,86 +733,12 @@ function getCommand(name) {
706
733
  }
707
734
 
708
735
  // src/cli/installers/global.ts
709
- import * as fs3 from "fs";
736
+ import * as fs2 from "fs";
710
737
 
711
- // src/cli/utils/config.ts
738
+ // src/cli/templates/config.ts
712
739
  import * as fs from "fs";
713
740
  import * as path from "path";
714
741
  import * as os from "os";
715
- import { parse as parseJsonc, modify, applyEdits } from "jsonc-parser";
716
- var PLUGIN_NAME = "opendevbrowser";
717
- var SCHEMA_URL = "https://opencode.ai/config.json";
718
- function getGlobalConfigPath() {
719
- const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(os.homedir(), ".config", "opencode");
720
- return path.join(configDir, "opencode.json");
721
- }
722
- function getLocalConfigPath() {
723
- return path.join(process.cwd(), "opencode.json");
724
- }
725
- function ensureDir(dirPath) {
726
- if (!fs.existsSync(dirPath)) {
727
- fs.mkdirSync(dirPath, { recursive: true });
728
- }
729
- }
730
- function readConfig(configPath) {
731
- if (!fs.existsSync(configPath)) {
732
- return { content: "", config: {} };
733
- }
734
- const content = fs.readFileSync(configPath, "utf-8");
735
- const errors = [];
736
- const parsed = parseJsonc(content, errors, { allowTrailingComma: true });
737
- if (errors.length > 0) {
738
- const firstError = errors[0];
739
- throw new Error(`Invalid JSONC at ${configPath}: parse error at offset ${firstError?.offset ?? 0}`);
740
- }
741
- return { content, config: parsed ?? {} };
742
- }
743
- function hasPlugin(config, pluginName = PLUGIN_NAME) {
744
- return config.plugin?.includes(pluginName) ?? false;
745
- }
746
- function createConfigWithPlugin(pluginName = PLUGIN_NAME) {
747
- return {
748
- $schema: SCHEMA_URL,
749
- plugin: [pluginName]
750
- };
751
- }
752
- function updateConfigContent(content, pluginName = PLUGIN_NAME) {
753
- if (!content.trim()) {
754
- return JSON.stringify(createConfigWithPlugin(pluginName), null, 2) + "\n";
755
- }
756
- const parsed = parseJsonc(content, [], { allowTrailingComma: true }) ?? {};
757
- if (parsed.plugin?.includes(pluginName)) {
758
- return content;
759
- }
760
- let result = content;
761
- if (!parsed.$schema) {
762
- const edits2 = modify(result, ["$schema"], SCHEMA_URL, { formattingOptions: { tabSize: 2, insertSpaces: true } });
763
- result = applyEdits(result, edits2);
764
- }
765
- const newPlugins = parsed.plugin ? [...parsed.plugin, pluginName] : [pluginName];
766
- const edits = modify(result, ["plugin"], newPlugins, { formattingOptions: { tabSize: 2, insertSpaces: true } });
767
- result = applyEdits(result, edits);
768
- return result;
769
- }
770
- function removePluginFromContent(content, pluginName = PLUGIN_NAME) {
771
- if (!content.trim()) {
772
- return content;
773
- }
774
- const parsed = parseJsonc(content, [], { allowTrailingComma: true }) ?? {};
775
- if (!parsed.plugin?.includes(pluginName)) {
776
- return content;
777
- }
778
- const newPlugins = parsed.plugin.filter((p) => p !== pluginName);
779
- const edits = modify(content, ["plugin"], newPlugins.length > 0 ? newPlugins : void 0, {
780
- formattingOptions: { tabSize: 2, insertSpaces: true }
781
- });
782
- return applyEdits(content, edits);
783
- }
784
-
785
- // src/cli/templates/config.ts
786
- import * as fs2 from "fs";
787
- import * as path2 from "path";
788
- import * as os2 from "os";
789
742
  function buildConfigTemplate(relayToken, daemonToken) {
790
743
  return `{
791
744
  // OpenDevBrowser Plugin Configuration
@@ -862,19 +815,19 @@ function buildConfigTemplate(relayToken, daemonToken) {
862
815
  }
863
816
  function getPluginConfigPath(mode) {
864
817
  if (mode === "global") {
865
- const configDir = process.env.OPENCODE_CONFIG_DIR || path2.join(os2.homedir(), ".config", "opencode");
866
- return path2.join(configDir, "opendevbrowser.jsonc");
818
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(os.homedir(), ".config", "opencode");
819
+ return path.join(configDir, "opendevbrowser.jsonc");
867
820
  }
868
- return path2.join(process.cwd(), "opendevbrowser.jsonc");
821
+ return path.join(process.cwd(), "opendevbrowser.jsonc");
869
822
  }
870
823
  function createPluginConfig(mode) {
871
824
  const configPath = getPluginConfigPath(mode);
872
- if (fs2.existsSync(configPath)) {
825
+ if (fs.existsSync(configPath)) {
873
826
  return { created: false, path: configPath };
874
827
  }
875
- const dir = path2.dirname(configPath);
876
- if (!fs2.existsSync(dir)) {
877
- fs2.mkdirSync(dir, { recursive: true });
828
+ const dir = path.dirname(configPath);
829
+ if (!fs.existsSync(dir)) {
830
+ fs.mkdirSync(dir, { recursive: true });
878
831
  }
879
832
  const relayToken = generateSecureToken();
880
833
  const daemonToken = generateSecureToken();
@@ -898,7 +851,7 @@ function installGlobal(withConfig = false) {
898
851
  }
899
852
  const newContent = updateConfigContent(content, "opendevbrowser");
900
853
  ensureDir(configPath.replace(/[/\\][^/\\]+$/, ""));
901
- fs3.writeFileSync(configPath, newContent, "utf-8");
854
+ fs2.writeFileSync(configPath, newContent, "utf-8");
902
855
  if (withConfig) {
903
856
  createPluginConfig("global");
904
857
  }
@@ -922,7 +875,7 @@ function installGlobal(withConfig = false) {
922
875
  }
923
876
 
924
877
  // src/cli/installers/local.ts
925
- import * as fs4 from "fs";
878
+ import * as fs3 from "fs";
926
879
  function installLocal(withConfig = false) {
927
880
  const configPath = getLocalConfigPath();
928
881
  try {
@@ -937,7 +890,7 @@ function installLocal(withConfig = false) {
937
890
  };
938
891
  }
939
892
  const newContent = updateConfigContent(content, "opendevbrowser");
940
- fs4.writeFileSync(configPath, newContent, "utf-8");
893
+ fs3.writeFileSync(configPath, newContent, "utf-8");
941
894
  if (withConfig) {
942
895
  createPluginConfig("local");
943
896
  }
@@ -960,407 +913,32 @@ function installLocal(withConfig = false) {
960
913
  }
961
914
  }
962
915
 
963
- // src/cli/installers/skills.ts
964
- import * as crypto from "crypto";
965
- import * as fs5 from "fs";
966
- import * as path4 from "path";
967
-
968
- // src/cli/utils/skills.ts
969
- import * as path3 from "path";
970
- import * as os3 from "os";
971
- var SKILL_DIR_NAME = "skill";
972
- var SKILLS_DIR_NAME = "skills";
973
- function getGlobalSkillDir() {
974
- const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
975
- return path3.join(configDir, SKILL_DIR_NAME);
976
- }
977
- function getLocalSkillDir() {
978
- return path3.join(process.cwd(), ".opencode", SKILL_DIR_NAME);
979
- }
980
- function getCodexHomeDir() {
981
- return process.env.CODEX_HOME || path3.join(os3.homedir(), ".codex");
982
- }
983
- function getClaudeCodeHomeDir() {
984
- return process.env.CLAUDECODE_HOME || path3.join(os3.homedir(), ".claude");
985
- }
986
- function getAmpHomeDir() {
987
- return process.env.AMP_CLI_HOME || path3.join(os3.homedir(), ".amp");
988
- }
989
- function dedupeTargets(targets) {
990
- const deduped = /* @__PURE__ */ new Map();
991
- for (const target of targets) {
992
- const key = path3.resolve(target.dir);
993
- const existing = deduped.get(key);
994
- if (existing) {
995
- if (!existing.agents.includes(target.agent)) {
996
- existing.agents.push(target.agent);
997
- }
998
- continue;
999
- }
1000
- deduped.set(key, { agents: [target.agent], dir: target.dir });
1001
- }
1002
- return Array.from(deduped.values());
1003
- }
1004
- function getGlobalSkillTargets() {
1005
- const claudeSkillsDir = path3.join(getClaudeCodeHomeDir(), SKILLS_DIR_NAME);
1006
- const ampSkillsDir = path3.join(getAmpHomeDir(), SKILLS_DIR_NAME);
1007
- return dedupeTargets([
1008
- { agent: "opencode", dir: getGlobalSkillDir() },
1009
- { agent: "codex", dir: path3.join(getCodexHomeDir(), SKILLS_DIR_NAME) },
1010
- { agent: "claudecode", dir: claudeSkillsDir },
1011
- { agent: "ampcli", dir: ampSkillsDir }
1012
- ]);
1013
- }
1014
- function getLocalSkillTargets() {
1015
- const localClaudeSkillsDir = path3.join(process.cwd(), ".claude", SKILLS_DIR_NAME);
1016
- const localAmpSkillsDir = path3.join(process.cwd(), ".amp", SKILLS_DIR_NAME);
1017
- return dedupeTargets([
1018
- { agent: "opencode", dir: getLocalSkillDir() },
1019
- { agent: "codex", dir: path3.join(process.cwd(), ".codex", SKILLS_DIR_NAME) },
1020
- { agent: "claudecode", dir: localClaudeSkillsDir },
1021
- { agent: "ampcli", dir: localAmpSkillsDir }
1022
- ]);
1023
- }
1024
-
1025
- // src/cli/installers/skills.ts
1026
- var LEGACY_ALIAS_DIRS = [
1027
- { name: "research", canonical: "opendevbrowser-research" },
1028
- { name: "shopping", canonical: "opendevbrowser-shopping" }
1029
- ];
1030
- function getTargets(mode) {
1031
- return mode === "global" ? getGlobalSkillTargets() : getLocalSkillTargets();
1032
- }
1033
- function getCanonicalBundledSkillNames() {
1034
- return listBundledSkillDirectories().map((entry) => entry.name);
1035
- }
1036
- function hasCanonicalBundledSkillInTarget(targetDir, packNames) {
1037
- return packNames.some((packName) => fs5.existsSync(path4.join(targetDir, packName)));
1038
- }
1039
- function formatSummary(parts, totalTargets, failures) {
1040
- const summary = parts.length > 0 ? parts.join(", ") : "no lifecycle changes";
1041
- const failureSummary = failures > 0 ? `, ${failures} failed` : "";
1042
- return `${summary} across ${totalTargets} targets${failureSummary}`;
1043
- }
1044
- function hashDirectoryTree(dirPath) {
1045
- const hash = crypto.createHash("sha256");
1046
- const visit = (currentPath, relativePath) => {
1047
- const entries = fs5.readdirSync(currentPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
1048
- for (const entry of entries) {
1049
- const absolutePath = path4.join(currentPath, entry.name);
1050
- const entryRelativePath = relativePath ? path4.posix.join(relativePath, entry.name) : entry.name;
1051
- if (entry.isDirectory()) {
1052
- hash.update(`D:${entryRelativePath}\0`);
1053
- visit(absolutePath, entryRelativePath);
1054
- continue;
1055
- }
1056
- if (entry.isFile()) {
1057
- hash.update(`F:${entryRelativePath}\0`);
1058
- hash.update(fs5.readFileSync(absolutePath));
1059
- hash.update("\0");
1060
- continue;
1061
- }
1062
- if (entry.isSymbolicLink()) {
1063
- hash.update(`L:${entryRelativePath}\0${fs5.readlinkSync(absolutePath)}\0`);
1064
- }
1065
- }
1066
- };
1067
- visit(dirPath, "");
1068
- return hash.digest("hex");
1069
- }
1070
- function syncSkillDirectory(sourcePath, targetPath, sourceFingerprint) {
1071
- if (!fs5.existsSync(targetPath)) {
1072
- fs5.cpSync(sourcePath, targetPath, { recursive: true });
1073
- return "installed";
1074
- }
1075
- const targetFingerprint = hashDirectoryTree(targetPath);
1076
- if (targetFingerprint === sourceFingerprint) {
1077
- return "unchanged";
1078
- }
1079
- const parentDir = path4.dirname(targetPath);
1080
- const targetName = path4.basename(targetPath);
1081
- const stagingRoot = fs5.mkdtempSync(path4.join(parentDir, `.${targetName}-sync-`));
1082
- const stagedPath = path4.join(stagingRoot, targetName);
1083
- const backupPath = path4.join(stagingRoot, `${targetName}-backup`);
1084
- try {
1085
- fs5.cpSync(sourcePath, stagedPath, { recursive: true });
1086
- fs5.renameSync(targetPath, backupPath);
1087
- try {
1088
- fs5.renameSync(stagedPath, targetPath);
1089
- } catch (error) {
1090
- if (fs5.existsSync(backupPath) && !fs5.existsSync(targetPath)) {
1091
- fs5.renameSync(backupPath, targetPath);
1092
- }
1093
- throw error;
1094
- }
1095
- fs5.rmSync(backupPath, { recursive: true, force: true });
1096
- return "refreshed";
1097
- } finally {
1098
- if (fs5.existsSync(stagedPath)) {
1099
- fs5.rmSync(stagedPath, { recursive: true, force: true });
1100
- }
1101
- if (fs5.existsSync(backupPath)) {
1102
- fs5.rmSync(backupPath, { recursive: true, force: true });
1103
- }
1104
- fs5.rmSync(stagingRoot, { recursive: true, force: true });
1105
- }
1106
- }
1107
- function cleanupLegacyAlias(targetDir, aliasName) {
1108
- const aliasPath = path4.join(targetDir, aliasName);
1109
- if (!fs5.existsSync(aliasPath)) {
1110
- return { removed: [], preserved: [] };
1111
- }
1112
- let stats;
1113
- try {
1114
- stats = fs5.statSync(aliasPath);
1115
- } catch {
1116
- return {
1117
- removed: [],
1118
- preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1119
- };
1120
- }
1121
- if (!stats.isDirectory()) {
1122
- return {
1123
- removed: [],
1124
- preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1125
- };
1126
- }
1127
- if (fs5.existsSync(path4.join(aliasPath, "SKILL.md"))) {
1128
- return {
1129
- removed: [],
1130
- preserved: [{ targetDir, name: aliasName, reason: "contains_skill_md" }]
1131
- };
1132
- }
1133
- const entries = fs5.readdirSync(aliasPath);
1134
- if (entries.length === 0) {
1135
- fs5.rmSync(aliasPath, { recursive: true, force: true });
1136
- return { removed: [aliasName], preserved: [] };
1137
- }
1138
- return {
1139
- removed: [],
1140
- preserved: [{ targetDir, name: aliasName, reason: "non_empty" }]
1141
- };
1142
- }
1143
- function cleanupLegacyAliases(targetDir) {
1144
- const removed = [];
1145
- const preserved = [];
1146
- for (const alias of LEGACY_ALIAS_DIRS) {
1147
- const result = cleanupLegacyAlias(targetDir, alias.name);
1148
- removed.push(...result.removed);
1149
- preserved.push(...result.preserved);
1150
- }
1151
- return { removed, preserved };
1152
- }
1153
- function buildSyncMessage(mode, result) {
1154
- return `Skills ${mode} sync: ${formatSummary(
1155
- [
1156
- result.installed.length > 0 ? `${result.installed.length} installed` : "",
1157
- result.refreshed.length > 0 ? `${result.refreshed.length} refreshed` : "",
1158
- result.unchanged.length > 0 ? `${result.unchanged.length} unchanged` : "",
1159
- result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1160
- result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1161
- ].filter(Boolean),
1162
- result.targets.length,
1163
- result.targets.filter((entry) => !entry.success).length
1164
- )}`;
1165
- }
1166
- function buildRemovalMessage(mode, result) {
1167
- return `Skills ${mode} removal: ${formatSummary(
1168
- [
1169
- result.removed.length > 0 ? `${result.removed.length} removed` : "",
1170
- result.missing.length > 0 ? `${result.missing.length} already absent` : "",
1171
- result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1172
- result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1173
- ].filter(Boolean),
1174
- result.targets.length,
1175
- result.targets.filter((entry) => !entry.success).length
1176
- )}`;
1177
- }
1178
- function syncBundledSkills(mode) {
1179
- const targets = getTargets(mode);
1180
- const targetResults = [];
1181
- try {
1182
- const sourceDir = getBundledSkillsDir();
1183
- const packNames = getCanonicalBundledSkillNames();
1184
- const bundledFingerprints = /* @__PURE__ */ new Map();
1185
- for (const packName of packNames) {
1186
- const sourcePath = path4.join(sourceDir, packName);
1187
- if (!fs5.existsSync(sourcePath)) {
1188
- throw new Error(`Bundled skill directory missing: ${packName}`);
1189
- }
1190
- bundledFingerprints.set(packName, hashDirectoryTree(sourcePath));
1191
- }
1192
- for (const target of targets) {
1193
- const installed = [];
1194
- const refreshed = [];
1195
- const unchanged = [];
1196
- const removedLegacyAliases = [];
1197
- const preservedLegacyAliases = [];
1198
- try {
1199
- ensureDir(target.dir);
1200
- for (const packName of packNames) {
1201
- const sourcePath = path4.join(sourceDir, packName);
1202
- const targetPath = path4.join(target.dir, packName);
1203
- const sourceFingerprint = bundledFingerprints.get(packName);
1204
- if (!sourceFingerprint) {
1205
- throw new Error(`Bundled fingerprint missing: ${packName}`);
1206
- }
1207
- const outcome = syncSkillDirectory(sourcePath, targetPath, sourceFingerprint);
1208
- if (outcome === "installed") {
1209
- installed.push(packName);
1210
- } else if (outcome === "refreshed") {
1211
- refreshed.push(packName);
1212
- } else {
1213
- unchanged.push(packName);
1214
- }
1215
- }
1216
- const legacyCleanup = cleanupLegacyAliases(target.dir);
1217
- removedLegacyAliases.push(...legacyCleanup.removed);
1218
- preservedLegacyAliases.push(...legacyCleanup.preserved);
1219
- targetResults.push({
1220
- agents: target.agents,
1221
- targetDir: target.dir,
1222
- installed,
1223
- refreshed,
1224
- unchanged,
1225
- removedLegacyAliases,
1226
- preservedLegacyAliases,
1227
- success: true
1228
- });
1229
- } catch (error) {
1230
- const message = error instanceof Error ? error.message : String(error);
1231
- targetResults.push({
1232
- agents: target.agents,
1233
- targetDir: target.dir,
1234
- installed,
1235
- refreshed,
1236
- unchanged,
1237
- removedLegacyAliases,
1238
- preservedLegacyAliases,
1239
- success: false,
1240
- error: message
1241
- });
1242
- }
1243
- }
1244
- const result = {
1245
- success: targetResults.every((entry) => entry.success),
1246
- message: "",
1247
- mode,
1248
- targets: targetResults,
1249
- installed: targetResults.flatMap((entry) => entry.installed),
1250
- refreshed: targetResults.flatMap((entry) => entry.refreshed),
1251
- unchanged: targetResults.flatMap((entry) => entry.unchanged),
1252
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1253
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1254
- };
1255
- result.message = buildSyncMessage(mode, result);
1256
- return result;
1257
- } catch (error) {
1258
- const message = error instanceof Error ? error.message : String(error);
1259
- const result = {
1260
- success: false,
1261
- message: "",
1262
- mode,
1263
- targets: targetResults,
1264
- installed: targetResults.flatMap((entry) => entry.installed),
1265
- refreshed: targetResults.flatMap((entry) => entry.refreshed),
1266
- unchanged: targetResults.flatMap((entry) => entry.unchanged),
1267
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1268
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1269
- };
1270
- result.message = `Failed to sync skills (${mode}): ${message}`;
1271
- return result;
1272
- }
1273
- }
1274
- function removeBundledSkills(mode) {
1275
- const targets = getTargets(mode);
1276
- const packNames = getCanonicalBundledSkillNames();
1277
- const targetResults = [];
1278
- for (const target of targets) {
1279
- const removed = [];
1280
- const missing = [];
1281
- const removedLegacyAliases = [];
1282
- const preservedLegacyAliases = [];
1283
- try {
1284
- for (const packName of packNames) {
1285
- const targetPath = path4.join(target.dir, packName);
1286
- if (fs5.existsSync(targetPath)) {
1287
- fs5.rmSync(targetPath, { recursive: true, force: true });
1288
- removed.push(packName);
1289
- } else {
1290
- missing.push(packName);
1291
- }
1292
- }
1293
- const legacyCleanup = cleanupLegacyAliases(target.dir);
1294
- removedLegacyAliases.push(...legacyCleanup.removed);
1295
- preservedLegacyAliases.push(...legacyCleanup.preserved);
1296
- targetResults.push({
1297
- agents: target.agents,
1298
- targetDir: target.dir,
1299
- removed,
1300
- missing,
1301
- removedLegacyAliases,
1302
- preservedLegacyAliases,
1303
- success: true
1304
- });
1305
- } catch (error) {
1306
- const message = error instanceof Error ? error.message : String(error);
1307
- targetResults.push({
1308
- agents: target.agents,
1309
- targetDir: target.dir,
1310
- removed,
1311
- missing,
1312
- removedLegacyAliases,
1313
- preservedLegacyAliases,
1314
- success: false,
1315
- error: message
1316
- });
1317
- }
1318
- }
1319
- const result = {
1320
- success: targetResults.every((entry) => entry.success),
1321
- message: "",
1322
- mode,
1323
- targets: targetResults,
1324
- removed: targetResults.flatMap((entry) => entry.removed),
1325
- missing: targetResults.flatMap((entry) => entry.missing),
1326
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1327
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1328
- };
1329
- result.message = buildRemovalMessage(mode, result);
1330
- return result;
1331
- }
1332
- function hasBundledSkillArtifacts(mode) {
1333
- const packNames = getCanonicalBundledSkillNames();
1334
- const targets = getTargets(mode);
1335
- return targets.some((target) => hasCanonicalBundledSkillInTarget(target.dir, packNames));
1336
- }
1337
-
1338
916
  // src/cli/commands/update.ts
1339
- import * as fs6 from "fs";
1340
- import * as path5 from "path";
1341
- import * as os4 from "os";
1342
- var PLUGIN_NAME2 = "opendevbrowser";
917
+ import * as fs4 from "fs";
918
+ import * as path2 from "path";
919
+ import * as os2 from "os";
920
+ var PLUGIN_NAME = "opendevbrowser";
1343
921
  function getCacheDir() {
1344
- return process.env.OPENCODE_CACHE_DIR || path5.join(os4.homedir(), ".cache", "opencode");
922
+ return process.env.OPENCODE_CACHE_DIR || path2.join(os2.homedir(), ".cache", "opencode");
1345
923
  }
1346
924
  function rmdir(dirPath) {
1347
925
  const cacheDir = getCacheDir();
1348
- const resolvedCache = path5.resolve(cacheDir);
1349
- const resolvedPath = path5.resolve(dirPath);
1350
- if (!resolvedPath.startsWith(resolvedCache + path5.sep) || resolvedPath === resolvedCache) {
926
+ const resolvedCache = path2.resolve(cacheDir);
927
+ const resolvedPath = path2.resolve(dirPath);
928
+ if (!resolvedPath.startsWith(resolvedCache + path2.sep) || resolvedPath === resolvedCache) {
1351
929
  throw new Error(`Security: refusing to delete path outside cache directory: ${dirPath}`);
1352
930
  }
1353
- if (fs6.existsSync(dirPath)) {
1354
- fs6.rmSync(dirPath, { recursive: true, force: true });
931
+ if (fs4.existsSync(dirPath)) {
932
+ fs4.rmSync(dirPath, { recursive: true, force: true });
1355
933
  }
1356
934
  }
1357
935
  function runUpdate() {
1358
936
  const cacheDir = getCacheDir();
1359
- const nodeModulesDir = path5.join(cacheDir, "node_modules");
1360
- const pluginCacheDir = path5.join(nodeModulesDir, PLUGIN_NAME2);
937
+ const nodeModulesDir = path2.join(cacheDir, "node_modules");
938
+ const pluginCacheDir = path2.join(nodeModulesDir, PLUGIN_NAME);
1361
939
  try {
1362
- if (!fs6.existsSync(pluginCacheDir)) {
1363
- if (fs6.existsSync(nodeModulesDir)) {
940
+ if (!fs4.existsSync(pluginCacheDir)) {
941
+ if (fs4.existsSync(nodeModulesDir)) {
1364
942
  rmdir(nodeModulesDir);
1365
943
  return {
1366
944
  success: true,
@@ -1391,20 +969,29 @@ function runUpdate() {
1391
969
  }
1392
970
 
1393
971
  // src/cli/commands/uninstall.ts
1394
- import * as fs7 from "fs";
1395
- import * as path6 from "path";
1396
- import * as os5 from "os";
972
+ import * as fs5 from "fs";
973
+ import * as path3 from "path";
974
+ import * as os3 from "os";
975
+ function hasInstalledConfig(mode) {
976
+ const configPath = mode === "global" ? getGlobalConfigPath() : getLocalConfigPath();
977
+ try {
978
+ const { config } = readConfig(configPath);
979
+ return hasPlugin(config);
980
+ } catch {
981
+ return false;
982
+ }
983
+ }
1397
984
  function getPluginConfigPath2(mode) {
1398
985
  if (mode === "global") {
1399
- const configDir = process.env.OPENCODE_CONFIG_DIR || path6.join(os5.homedir(), ".config", "opencode");
1400
- return path6.join(configDir, "opendevbrowser.jsonc");
986
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
987
+ return path3.join(configDir, "opendevbrowser.jsonc");
1401
988
  }
1402
- return path6.join(process.cwd(), "opendevbrowser.jsonc");
989
+ return path3.join(process.cwd(), "opendevbrowser.jsonc");
1403
990
  }
1404
991
  function removePluginConfigFile(mode) {
1405
992
  const configPath = getPluginConfigPath2(mode);
1406
- if (fs7.existsSync(configPath)) {
1407
- fs7.unlinkSync(configPath);
993
+ if (fs5.existsSync(configPath)) {
994
+ fs5.unlinkSync(configPath);
1408
995
  return true;
1409
996
  }
1410
997
  return false;
@@ -1416,14 +1003,14 @@ function runUninstall(mode, deleteConfigFile = false) {
1416
1003
  if (!hasPlugin(config)) {
1417
1004
  return {
1418
1005
  success: true,
1419
- message: `opendevbrowser is not installed in ${configPath}`,
1006
+ message: `No plugin config found in ${configPath}`,
1420
1007
  configPath,
1421
1008
  removed: false,
1422
1009
  configFileDeleted: false
1423
1010
  };
1424
1011
  }
1425
1012
  const newContent = removePluginFromContent(content, "opendevbrowser");
1426
- fs7.writeFileSync(configPath, newContent, "utf-8");
1013
+ fs5.writeFileSync(configPath, newContent, "utf-8");
1427
1014
  let configFileDeleted = false;
1428
1015
  if (deleteConfigFile) {
1429
1016
  configFileDeleted = removePluginConfigFile(mode);
@@ -1447,19 +1034,10 @@ function runUninstall(mode, deleteConfigFile = false) {
1447
1034
  }
1448
1035
  }
1449
1036
  function findInstalledConfigs() {
1450
- let global = false;
1451
- let local = false;
1452
- try {
1453
- const { config: globalConfig } = readConfig(getGlobalConfigPath());
1454
- global = hasPlugin(globalConfig);
1455
- } catch {
1456
- }
1457
- try {
1458
- const { config: localConfig } = readConfig(getLocalConfigPath());
1459
- local = hasPlugin(localConfig);
1460
- } catch {
1461
- }
1462
- return { global, local };
1037
+ return {
1038
+ global: hasInstalledConfig("global") || hasManagedBundledSkillInstall("global") || hasBundledSkillArtifacts("global"),
1039
+ local: hasInstalledConfig("local") || hasManagedBundledSkillInstall("local") || hasBundledSkillArtifacts("local")
1040
+ };
1463
1041
  }
1464
1042
 
1465
1043
  // src/cli/commands/serve.ts
@@ -1549,8 +1127,8 @@ function parseRepeatedStringFlag(rawArgs, flag) {
1549
1127
  }
1550
1128
 
1551
1129
  // src/cli/commands/native.ts
1552
- import * as fs8 from "fs";
1553
- import * as path7 from "path";
1130
+ import * as fs6 from "fs";
1131
+ import * as path4 from "path";
1554
1132
  import { execFileSync } from "child_process";
1555
1133
  import { fileURLToPath } from "url";
1556
1134
  var EXTENSION_ID_RE = /^[a-p]{32}$/;
@@ -1585,33 +1163,33 @@ var parseNativeArgs = (rawArgs) => {
1585
1163
  };
1586
1164
  var getManifestDir = () => {
1587
1165
  if (process.platform === "darwin") {
1588
- return path7.join(process.env.HOME || "", "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
1166
+ return path4.join(process.env.HOME || "", "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
1589
1167
  }
1590
1168
  if (process.platform === "linux") {
1591
- return path7.join(process.env.HOME || "", ".config", "google-chrome", "NativeMessagingHosts");
1169
+ return path4.join(process.env.HOME || "", ".config", "google-chrome", "NativeMessagingHosts");
1592
1170
  }
1593
1171
  if (process.platform === "win32") {
1594
- const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path7.join(process.env.USERPROFILE, "AppData", "Local") : "");
1172
+ const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path4.join(process.env.USERPROFILE, "AppData", "Local") : "");
1595
1173
  if (!base) {
1596
1174
  throw createUsageError("LOCALAPPDATA is not set. Unable to locate NativeMessagingHosts directory.");
1597
1175
  }
1598
- return path7.join(base, "Google", "Chrome", "User Data", "NativeMessagingHosts");
1176
+ return path4.join(base, "Google", "Chrome", "User Data", "NativeMessagingHosts");
1599
1177
  }
1600
1178
  throw createUsageError(`Native messaging is not supported on ${process.platform}.`);
1601
1179
  };
1602
1180
  var getScriptsDir = () => {
1603
1181
  const __filename = fileURLToPath(import.meta.url);
1604
- const startDir = path7.dirname(__filename);
1182
+ const startDir = path4.dirname(__filename);
1605
1183
  const rootsToScan = [startDir, process.cwd()];
1606
1184
  for (const root of rootsToScan) {
1607
- let current = path7.resolve(root);
1185
+ let current = path4.resolve(root);
1608
1186
  while (true) {
1609
- const scriptsDir = path7.join(current, "scripts", "native");
1610
- const packageJsonPath = path7.join(current, "package.json");
1611
- if (fs8.existsSync(scriptsDir) && fs8.existsSync(packageJsonPath)) {
1187
+ const scriptsDir = path4.join(current, "scripts", "native");
1188
+ const packageJsonPath = path4.join(current, "package.json");
1189
+ if (fs6.existsSync(scriptsDir) && fs6.existsSync(packageJsonPath)) {
1612
1190
  return scriptsDir;
1613
1191
  }
1614
- const parent = path7.dirname(current);
1192
+ const parent = path4.dirname(current);
1615
1193
  if (parent === current) {
1616
1194
  break;
1617
1195
  }
@@ -1621,18 +1199,18 @@ var getScriptsDir = () => {
1621
1199
  throw createUsageError("Unable to locate scripts/native directory.");
1622
1200
  };
1623
1201
  var getHostScriptPath = () => {
1624
- return path7.join(getScriptsDir(), "host.cjs");
1202
+ return path4.join(getScriptsDir(), "host.cjs");
1625
1203
  };
1626
1204
  var getManifestPath = () => {
1627
- return path7.join(getManifestDir(), "com.opendevbrowser.native.json");
1205
+ return path4.join(getManifestDir(), "com.opendevbrowser.native.json");
1628
1206
  };
1629
1207
  var getWrapperPath = () => {
1630
1208
  const wrapperName = process.platform === "win32" ? "com.opendevbrowser.native.cmd" : "com.opendevbrowser.native.sh";
1631
- return path7.join(getManifestDir(), wrapperName);
1209
+ return path4.join(getManifestDir(), wrapperName);
1632
1210
  };
1633
1211
  var readManifest = (manifestPath) => {
1634
1212
  try {
1635
- const raw = fs8.readFileSync(manifestPath, "utf8");
1213
+ const raw = fs6.readFileSync(manifestPath, "utf8");
1636
1214
  const data = JSON.parse(raw);
1637
1215
  const origins = Array.isArray(data.allowed_origins) ? data.allowed_origins : [];
1638
1216
  const match = origins.find((origin) => origin.startsWith("chrome-extension://"));
@@ -1669,9 +1247,9 @@ var readRegistryPath = () => {
1669
1247
  };
1670
1248
  var normalizePath = (value) => {
1671
1249
  try {
1672
- return fs8.realpathSync(value);
1250
+ return fs6.realpathSync(value);
1673
1251
  } catch {
1674
- return path7.resolve(value);
1252
+ return path4.resolve(value);
1675
1253
  }
1676
1254
  };
1677
1255
  var findExtensionIdInCommands = (preferences) => {
@@ -1732,8 +1310,8 @@ var getExtensionPathCandidates = () => {
1732
1310
  if (primary) {
1733
1311
  candidates.add(normalizePath(primary));
1734
1312
  }
1735
- const cwdExtension = path7.join(process.cwd(), "extension");
1736
- if (fs8.existsSync(path7.join(cwdExtension, "manifest.json"))) {
1313
+ const cwdExtension = path4.join(process.cwd(), "extension");
1314
+ if (fs6.existsSync(path4.join(cwdExtension, "manifest.json"))) {
1737
1315
  candidates.add(normalizePath(cwdExtension));
1738
1316
  }
1739
1317
  if (candidates.size === 0) {
@@ -1818,13 +1396,13 @@ var getNativeStatusSnapshot = () => {
1818
1396
  let manifestExists = false;
1819
1397
  let wrapperExists = false;
1820
1398
  let extensionIdValue = null;
1821
- if (fs8.existsSync(manifestPath)) {
1399
+ if (fs6.existsSync(manifestPath)) {
1822
1400
  manifestExists = true;
1823
1401
  installed = true;
1824
1402
  const manifest = readManifest(manifestPath);
1825
1403
  extensionIdValue = manifest.extensionId;
1826
1404
  }
1827
- if (fs8.existsSync(wrapperPath)) {
1405
+ if (fs6.existsSync(wrapperPath)) {
1828
1406
  wrapperExists = true;
1829
1407
  }
1830
1408
  if (!manifestExists || !wrapperExists) {
@@ -1884,7 +1462,7 @@ function installNativeHost(extensionId) {
1884
1462
  };
1885
1463
  }
1886
1464
  const hostScript = getHostScriptPath();
1887
- if (!fs8.existsSync(hostScript)) {
1465
+ if (!fs6.existsSync(hostScript)) {
1888
1466
  return {
1889
1467
  success: false,
1890
1468
  message: `Native host not found at ${hostScript}.`,
@@ -1893,7 +1471,7 @@ function installNativeHost(extensionId) {
1893
1471
  }
1894
1472
  const scriptsDir = getScriptsDir();
1895
1473
  const manifestPath = getManifestPath();
1896
- const installScript = process.platform === "win32" ? path7.join(scriptsDir, "install.ps1") : path7.join(scriptsDir, "install.sh");
1474
+ const installScript = process.platform === "win32" ? path4.join(scriptsDir, "install.ps1") : path4.join(scriptsDir, "install.sh");
1897
1475
  try {
1898
1476
  runScript(installScript, [normalized]);
1899
1477
  return {
@@ -1913,7 +1491,7 @@ function installNativeHost(extensionId) {
1913
1491
  async function runNativeCommand(args) {
1914
1492
  const { subcommand, extensionId } = parseNativeArgs(args.rawArgs);
1915
1493
  const scriptsDir = getScriptsDir();
1916
- const uninstallScript = process.platform === "win32" ? path7.join(scriptsDir, "uninstall.ps1") : path7.join(scriptsDir, "uninstall.sh");
1494
+ const uninstallScript = process.platform === "win32" ? path4.join(scriptsDir, "uninstall.ps1") : path4.join(scriptsDir, "uninstall.sh");
1917
1495
  if (subcommand === "install") {
1918
1496
  return installNativeHost(extensionId);
1919
1497
  }
@@ -2289,9 +1867,9 @@ ${nativeMessage}${fingerprintNote}${staleNote}` : `${baseMessage}${fingerprintNo
2289
1867
 
2290
1868
  // src/cli/daemon-autostart.ts
2291
1869
  import { execFileSync as execFileSync2 } from "child_process";
2292
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2293
- import { homedir as homedir6, tmpdir } from "os";
2294
- import { dirname as dirname5, isAbsolute, join as join8, relative, resolve as resolve4 } from "path";
1870
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
1871
+ import { homedir as homedir4, tmpdir } from "os";
1872
+ import { dirname as dirname3, isAbsolute, join as join5, relative, resolve as resolve3 } from "path";
2295
1873
  import { fileURLToPath as fileURLToPath2 } from "url";
2296
1874
  var MAC_LABEL = "com.opendevbrowser.daemon";
2297
1875
  var WIN_TASK_NAME = "OpenDevBrowser Daemon";
@@ -2301,9 +1879,9 @@ var defaultDeps = () => ({
2301
1879
  argv1: process.argv[1] ?? "",
2302
1880
  moduleUrl: import.meta.url,
2303
1881
  uid: typeof process.getuid === "function" ? process.getuid() : 0,
2304
- homedir: homedir6,
2305
- existsSync: existsSync7,
2306
- mkdirSync: mkdirSync3,
1882
+ homedir: homedir4,
1883
+ existsSync: existsSync5,
1884
+ mkdirSync: mkdirSync2,
2307
1885
  writeFileSync: writeFileSync4,
2308
1886
  unlinkSync: unlinkSync2,
2309
1887
  execFileSync: execFileSync2,
@@ -2315,14 +1893,14 @@ var formatCommand = (programArguments) => {
2315
1893
  };
2316
1894
  var resolveCliPathFromModule = (moduleUrl, exists) => {
2317
1895
  const modulePath = fileURLToPath2(moduleUrl);
2318
- const candidate = resolve4(dirname5(modulePath), "..", "index.js");
1896
+ const candidate = resolve3(dirname3(modulePath), "..", "index.js");
2319
1897
  if (!exists(candidate)) {
2320
1898
  throw new Error(`CLI entrypoint not found at ${candidate}`);
2321
1899
  }
2322
1900
  return candidate;
2323
1901
  };
2324
1902
  var normalizeComparisonPath = (value, platform) => {
2325
- const resolvedPath = resolve4(value);
1903
+ const resolvedPath = resolve3(value);
2326
1904
  return platform === "win32" ? resolvedPath.toLowerCase() : resolvedPath;
2327
1905
  };
2328
1906
  var isPathInsideRoot = (candidate, root, platform) => {
@@ -2357,7 +1935,7 @@ var resolveCliEntrypoint = (deps = {}) => {
2357
1935
  let cliPath = null;
2358
1936
  let source = "module";
2359
1937
  if (resolved.argv1) {
2360
- const candidate = resolve4(resolved.argv1);
1938
+ const candidate = resolve3(resolved.argv1);
2361
1939
  if (exists(candidate)) {
2362
1940
  cliPath = candidate;
2363
1941
  source = "argv1";
@@ -2372,13 +1950,13 @@ var resolveCliEntrypoint = (deps = {}) => {
2372
1950
  const isTransient = isTransientCliPath(cliPath, resolved);
2373
1951
  return { nodePath, cliPath, args, command, source, isTransient };
2374
1952
  };
2375
- var getLaunchAgentPath = (home = homedir6()) => {
2376
- return join8(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
1953
+ var getLaunchAgentPath = (home = homedir4()) => {
1954
+ return join5(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2377
1955
  };
2378
1956
  var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2379
1957
  const label = options.label ?? MAC_LABEL;
2380
- const stdoutPath = options.stdoutPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.log");
2381
- const stderrPath = options.stderrPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.err.log");
1958
+ const stdoutPath = options.stdoutPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.log");
1959
+ const stderrPath = options.stderrPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.err.log");
2382
1960
  const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${value}</string>`).join("\n");
2383
1961
  return [
2384
1962
  '<?xml version="1.0" encoding="UTF-8"?>',
@@ -2583,10 +2161,10 @@ var installMacAutostart = (deps = {}) => {
2583
2161
  assertPersistentEntrypoint(entrypoint);
2584
2162
  const home = resolved.homedir();
2585
2163
  const plistPath = getLaunchAgentPath(home);
2586
- const stdoutPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.log");
2587
- const stderrPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2588
- const logsDir = dirname5(stdoutPath);
2589
- resolved.mkdirSync(dirname5(plistPath), { recursive: true });
2164
+ const stdoutPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.log");
2165
+ const stderrPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2166
+ const logsDir = dirname3(stdoutPath);
2167
+ resolved.mkdirSync(dirname3(plistPath), { recursive: true });
2590
2168
  resolved.mkdirSync(logsDir, { recursive: true });
2591
2169
  resolved.writeFileSync(
2592
2170
  plistPath,
@@ -2963,7 +2541,7 @@ async function runDaemonCommand(args) {
2963
2541
  }
2964
2542
 
2965
2543
  // src/cli/commands/artifacts.ts
2966
- import { join as join9, resolve as resolve5 } from "path";
2544
+ import { join as join6, resolve as resolve4 } from "path";
2967
2545
  import { tmpdir as tmpdir2 } from "os";
2968
2546
  var PASSTHROUGH_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2969
2547
  "--with-config",
@@ -3052,7 +2630,7 @@ var parseArtifactsArgs = (rawArgs) => {
3052
2630
  };
3053
2631
  async function runArtifactsCommand(args) {
3054
2632
  const parsed = parseArtifactsArgs(args.rawArgs);
3055
- const rootDir = parsed.outputDir ? resolve5(parsed.outputDir) : join9(tmpdir2(), "opendevbrowser");
2633
+ const rootDir = parsed.outputDir ? resolve4(parsed.outputDir) : join6(tmpdir2(), "opendevbrowser");
3056
2634
  const cleaned = await cleanupExpiredArtifacts(rootDir);
3057
2635
  return {
3058
2636
  success: true,
@@ -3163,34 +2741,34 @@ function formatAutostartReconciliationMessage(result) {
3163
2741
  }
3164
2742
 
3165
2743
  // src/cli/commands/run.ts
3166
- import { readFileSync as readFileSync4 } from "fs";
2744
+ import { readFileSync as readFileSync2 } from "fs";
3167
2745
 
3168
2746
  // src/cli/output.ts
3169
2747
  var normalizeExitCode = (code) => {
3170
2748
  return Number.isInteger(code) ? Number(code) : 0;
3171
2749
  };
3172
2750
  var flushStream = (stream) => {
3173
- return new Promise((resolve6) => {
2751
+ return new Promise((resolve5) => {
3174
2752
  if (!stream || typeof stream.write !== "function") {
3175
- resolve6();
2753
+ resolve5();
3176
2754
  return;
3177
2755
  }
3178
2756
  try {
3179
- stream.write("", () => resolve6());
2757
+ stream.write("", () => resolve5());
3180
2758
  } catch {
3181
- resolve6();
2759
+ resolve5();
3182
2760
  }
3183
2761
  });
3184
2762
  };
3185
2763
  async function flushOutputAndExit(code, proc = process, timeoutMs = 250) {
3186
2764
  const finalCode = normalizeExitCode(code);
3187
2765
  proc.exitCode = finalCode;
3188
- await new Promise((resolve6) => {
2766
+ await new Promise((resolve5) => {
3189
2767
  let settled = false;
3190
2768
  const finish = () => {
3191
2769
  if (settled) return;
3192
2770
  settled = true;
3193
- resolve6();
2771
+ resolve5();
3194
2772
  };
3195
2773
  const timer = setTimeout(finish, Math.max(0, timeoutMs));
3196
2774
  timer.unref?.();
@@ -3312,13 +2890,13 @@ function parseRunArgs(rawArgs) {
3312
2890
  return parsed;
3313
2891
  }
3314
2892
  function readScriptFromStdin() {
3315
- return new Promise((resolve6, reject) => {
2893
+ return new Promise((resolve5, reject) => {
3316
2894
  let data = "";
3317
2895
  process.stdin.setEncoding("utf8");
3318
2896
  process.stdin.on("data", (chunk) => {
3319
2897
  data += chunk;
3320
2898
  });
3321
- process.stdin.on("end", () => resolve6(data));
2899
+ process.stdin.on("end", () => resolve5(data));
3322
2900
  process.stdin.on("error", reject);
3323
2901
  });
3324
2902
  }
@@ -3327,7 +2905,7 @@ async function runScriptCommand(args) {
3327
2905
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
3328
2906
  let scriptRaw = "";
3329
2907
  if (runArgs.scriptPath) {
3330
- scriptRaw = readFileSync4(runArgs.scriptPath, "utf-8");
2908
+ scriptRaw = readFileSync2(runArgs.scriptPath, "utf-8");
3331
2909
  } else if (!process.stdin.isTTY) {
3332
2910
  scriptRaw = await readScriptFromStdin();
3333
2911
  } else {
@@ -3552,16 +3130,16 @@ function promptYesNo(question, defaultYes) {
3552
3130
  return Promise.resolve(false);
3553
3131
  }
3554
3132
  const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
3555
- return new Promise((resolve6) => {
3133
+ return new Promise((resolve5) => {
3556
3134
  process.stdout.write(`${question}${suffix}`);
3557
3135
  process.stdin.setEncoding("utf8");
3558
3136
  process.stdin.once("data", (data) => {
3559
3137
  const input = data.toString().trim().toLowerCase();
3560
3138
  if (!input) {
3561
- resolve6(defaultYes);
3139
+ resolve5(defaultYes);
3562
3140
  return;
3563
3141
  }
3564
- resolve6(input === "y" || input === "yes");
3142
+ resolve5(input === "y" || input === "yes");
3565
3143
  });
3566
3144
  });
3567
3145
  }
@@ -3681,6 +3259,12 @@ async function runSessionDisconnect(args) {
3681
3259
  // src/cli/utils/workflow-message.ts
3682
3260
  var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
3683
3261
  var readNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
3262
+ var UNRESOLVED_COMMAND_PLACEHOLDER_RE = /<[^>\n]+>/;
3263
+ var readRunnableStepCommand = (step) => {
3264
+ const command = readNonEmptyString(step.command);
3265
+ if (!command) return null;
3266
+ return UNRESOLVED_COMMAND_PLACEHOLDER_RE.test(command) ? null : command;
3267
+ };
3684
3268
  var readMeta = (data) => {
3685
3269
  return asRecord(asRecord(data)?.meta);
3686
3270
  };
@@ -3728,6 +3312,17 @@ var readSuggestedNextAction = (data) => {
3728
3312
  if (!record) return null;
3729
3313
  return readNonEmptyString(record.suggestedNextAction) ?? readNonEmptyString(asRecord(record.sessionInspector)?.suggestedNextAction);
3730
3314
  };
3315
+ var readSuggestedStepCommand = (data) => {
3316
+ let current = asRecord(data);
3317
+ while (current) {
3318
+ const command = readSuggestedSteps(current).map(readRunnableStepCommand).find((step) => Boolean(step));
3319
+ if (command) {
3320
+ return command;
3321
+ }
3322
+ current = asRecord(current.challengePlan);
3323
+ }
3324
+ return null;
3325
+ };
3731
3326
  var readSuggestedStepReason = (data) => {
3732
3327
  let current = asRecord(data);
3733
3328
  while (current) {
@@ -3758,7 +3353,7 @@ var buildWorkflowCompletionMessage = (workflowLabel, data) => {
3758
3353
  if (followthroughSummary) {
3759
3354
  return buildNextStepMessage(
3760
3355
  `${workflowLabel} completed. ${followthroughSummary}`,
3761
- readSuggestedNextAction(data) ?? readSuggestedStepReason(data)
3356
+ readSuggestedNextAction(data) ?? readSuggestedStepCommand(data) ?? readSuggestedStepReason(data)
3762
3357
  );
3763
3358
  }
3764
3359
  return `${workflowLabel} completed.`;
@@ -4519,7 +4114,7 @@ async function runAnnotate(args) {
4519
4114
  }
4520
4115
 
4521
4116
  // src/cli/commands/canvas.ts
4522
- import { readFileSync as readFileSync5 } from "fs";
4117
+ import { readFileSync as readFileSync3 } from "fs";
4523
4118
  var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
4524
4119
  var isRecord = (value) => {
4525
4120
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4720,7 +4315,7 @@ var resolveCanvasParams = (canvasArgs) => {
4720
4315
  if (hasParamsFile) {
4721
4316
  let raw = "";
4722
4317
  try {
4723
- raw = readFileSync5(canvasArgs.paramsFile ?? "", "utf8");
4318
+ raw = readFileSync3(canvasArgs.paramsFile ?? "", "utf8");
4724
4319
  } catch (error) {
4725
4320
  const message = error instanceof Error ? error.message : "Unable to read file";
4726
4321
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4789,7 +4384,7 @@ async function runCanvas(args) {
4789
4384
  }
4790
4385
 
4791
4386
  // src/cli/commands/rpc.ts
4792
- import { readFileSync as readFileSync6 } from "fs";
4387
+ import { readFileSync as readFileSync4 } from "fs";
4793
4388
  var requireValue4 = (value, flag) => {
4794
4389
  if (!value) throw createUsageError(`Missing value for ${flag}`);
4795
4390
  return value;
@@ -4869,7 +4464,7 @@ var resolveRpcParams = (rpcArgs) => {
4869
4464
  if (hasParamsFile) {
4870
4465
  let raw = "";
4871
4466
  try {
4872
- raw = readFileSync6(rpcArgs.paramsFile ?? "", "utf8");
4467
+ raw = readFileSync4(rpcArgs.paramsFile ?? "", "utf8");
4873
4468
  } catch (error) {
4874
4469
  const message = error instanceof Error ? error.message : "Unable to read file";
4875
4470
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -6249,11 +5844,11 @@ function parseScreenshotArgs(rawArgs) {
6249
5844
  return parsed;
6250
5845
  }
6251
5846
  async function runScreenshot(args) {
6252
- const { sessionId, targetId, path: path8, ref, fullPage, timeoutMs } = parseScreenshotArgs(args.rawArgs);
5847
+ const { sessionId, targetId, path: path5, ref, fullPage, timeoutMs } = parseScreenshotArgs(args.rawArgs);
6253
5848
  if (!sessionId) throw createUsageError("Missing --session-id");
6254
5849
  const params = {
6255
5850
  sessionId,
6256
- ...typeof path8 === "string" ? { path: path8 } : {},
5851
+ ...typeof path5 === "string" ? { path: path5 } : {},
6257
5852
  ...typeof targetId === "string" ? { targetId } : {},
6258
5853
  ...typeof ref === "string" ? { ref } : {},
6259
5854
  ...fullPage === true ? { fullPage: true } : {}
@@ -6652,7 +6247,7 @@ async function runDesktopAccessibilitySnapshot(args) {
6652
6247
  }
6653
6248
 
6654
6249
  // src/cli/commands/session/cookie-import.ts
6655
- import { readFileSync as readFileSync7 } from "fs";
6250
+ import { readFileSync as readFileSync5 } from "fs";
6656
6251
  var requireValue5 = (value, flag) => {
6657
6252
  if (!value) {
6658
6253
  throw createUsageError(`Missing value for ${flag}`);
@@ -6767,7 +6362,7 @@ var resolveCookies = (parsed) => {
6767
6362
  }
6768
6363
  let raw = "";
6769
6364
  try {
6770
- raw = readFileSync7(parsed.cookiesFile ?? "", "utf8");
6365
+ raw = readFileSync5(parsed.cookiesFile ?? "", "utf8");
6771
6366
  } catch (error) {
6772
6367
  const message = error instanceof Error ? error.message : "Unable to read file";
6773
6368
  throw createUsageError(`Invalid --cookies-file: ${message}`);
@@ -6904,12 +6499,28 @@ var asRecord2 = (value) => {
6904
6499
  }
6905
6500
  return value;
6906
6501
  };
6502
+ var readNonEmptyString2 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
6907
6503
  var hasExecutionBlocker = (result) => {
6908
6504
  const execution = asRecord2(asRecord2(result)?.execution);
6909
6505
  const meta = asRecord2(execution?.meta);
6910
6506
  return asRecord2(meta?.blocker) !== null;
6911
6507
  };
6508
+ var readMacroResolveSummary = (result) => readNonEmptyString2(asRecord2(result)?.followthroughSummary);
6509
+ var readMacroResolveNextStep = (result) => {
6510
+ const record = asRecord2(result);
6511
+ const explicit = readNonEmptyString2(record?.suggestedNextAction);
6512
+ if (explicit) {
6513
+ return explicit;
6514
+ }
6515
+ const [firstStep] = Array.isArray(record?.suggestedSteps) ? record.suggestedSteps.filter((step) => Boolean(step) && typeof step === "object") : [];
6516
+ return readNonEmptyString2(firstStep?.command) ?? readNonEmptyString2(firstStep?.reason);
6517
+ };
6912
6518
  var buildMacroResolveMessage = (execute, result) => {
6519
+ const summary = readMacroResolveSummary(result);
6520
+ const nextStep = readMacroResolveNextStep(result);
6521
+ if (summary) {
6522
+ return nextStep ? `${summary} Next step: ${nextStep}` : summary;
6523
+ }
6913
6524
  if (!execute) {
6914
6525
  return "Macro resolved.";
6915
6526
  }
@@ -7803,7 +7414,7 @@ async function runInspiredesignCommand(args) {
7803
7414
  // package.json
7804
7415
  var package_default = {
7805
7416
  name: "opendevbrowser",
7806
- version: "0.0.21",
7417
+ version: "0.0.23",
7807
7418
  description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
7808
7419
  type: "module",
7809
7420
  main: "dist/index.js",
@@ -7814,6 +7425,7 @@ var package_default = {
7814
7425
  files: [
7815
7426
  "dist",
7816
7427
  "skills",
7428
+ "scripts/postinstall-sync-skills.mjs",
7817
7429
  "scripts/native",
7818
7430
  "extension/canvas.html",
7819
7431
  "extension/manifest.json",
@@ -7848,8 +7460,8 @@ var package_default = {
7848
7460
  node: ">=18"
7849
7461
  },
7850
7462
  scripts: {
7851
- build: "node scripts/run-package-tool.mjs tsup src/index.ts src/cli/index.ts src/skills/skill-loader.ts --format esm --clean --sourcemap && node scripts/run-package-tool.mjs tsc --emitDeclarationOnly --declaration --declarationMap -p tsconfig.json && node scripts/postbuild-dist.mjs",
7852
- dev: "tsup src/index.ts src/cli/index.ts src/skills/skill-loader.ts --format esm --dts --watch",
7463
+ build: "node scripts/run-package-tool.mjs tsup src/index.ts src/cli/index.ts src/cli/installers/postinstall-skill-sync.ts src/skills/skill-loader.ts --format esm --clean --sourcemap && node scripts/run-package-tool.mjs tsc --emitDeclarationOnly --declaration --declarationMap -p tsconfig.json && node scripts/postbuild-dist.mjs",
7464
+ dev: "tsup src/index.ts src/cli/index.ts src/cli/installers/postinstall-skill-sync.ts src/skills/skill-loader.ts --format esm --dts --watch",
7853
7465
  lint: 'node scripts/run-package-tool.mjs eslint "src/**/*.ts" "tests/**/*.ts"',
7854
7466
  typecheck: "node scripts/run-package-tool.mjs tsc --noEmit -p tsconfig.json",
7855
7467
  test: "node scripts/run-vitest-coverage.mjs",
@@ -7864,7 +7476,8 @@ var package_default = {
7864
7476
  "extension:pack": "cd extension && zip -r ../opendevbrowser-extension.zip manifest.json popup.html canvas.html dist/ icons/",
7865
7477
  "extension:store": "node scripts/chrome-store-publish.mjs",
7866
7478
  "version:check": "node scripts/verify-versions.mjs",
7867
- prepack: "npm run version:check && npm run build && npm run extension:build"
7479
+ prepack: "npm run version:check && npm run build && npm run extension:build",
7480
+ postinstall: "node scripts/postinstall-sync-skills.mjs"
7868
7481
  },
7869
7482
  dependencies: {
7870
7483
  "@opencode-ai/plugin": "^1.2.25",
@@ -7892,8 +7505,10 @@ var package_default = {
7892
7505
  }
7893
7506
  };
7894
7507
 
7895
- // src/cli/index.ts
7896
- var VERSION = typeof package_default.version === "string" ? package_default.version : "0.0.0";
7508
+ // src/cli/update-skill-modes.ts
7509
+ function shouldRefreshManagedSkills(mode) {
7510
+ return hasManagedBundledSkillInstall(mode) || hasBundledSkillArtifacts(mode);
7511
+ }
7897
7512
  function resolveUpdateSkillModes(args) {
7898
7513
  if (args.rawArgs.includes("--no-skills")) {
7899
7514
  return [];
@@ -7907,22 +7522,102 @@ function resolveUpdateSkillModes(args) {
7907
7522
  if (args.mode) {
7908
7523
  return [args.mode];
7909
7524
  }
7910
- const installed = findInstalledConfigs();
7911
7525
  const modes = [];
7912
- if (installed.global || hasBundledSkillArtifacts("global")) {
7526
+ if (shouldRefreshManagedSkills("global")) {
7913
7527
  modes.push("global");
7914
7528
  }
7915
- if (installed.local || hasBundledSkillArtifacts("local")) {
7529
+ if (shouldRefreshManagedSkills("local")) {
7916
7530
  modes.push("local");
7917
7531
  }
7918
7532
  return modes;
7919
7533
  }
7534
+
7535
+ // src/cli/skill-lifecycle.ts
7536
+ function shouldSkipSkills(args) {
7537
+ return args.rawArgs.includes("--no-skills");
7538
+ }
7539
+ function getUpdateTargets(args, mode, deps) {
7540
+ if (deps.hasInstalledConfig(mode)) {
7541
+ return deps.getBundledSkillTargets(mode);
7542
+ }
7543
+ return deps.getBundledSkillLifecycleTargets(mode, {
7544
+ includeLegacyArtifacts: deps.hasBundledSkillArtifacts(mode)
7545
+ });
7546
+ }
7547
+ function collectUpdateSkillResults(args, deps, modes) {
7548
+ return modes.flatMap((mode) => {
7549
+ const targets = getUpdateTargets(args, mode, deps);
7550
+ return targets.length > 0 ? [deps.syncBundledSkillsForTargets(mode, targets)] : [];
7551
+ });
7552
+ }
7553
+ function getResolvedUpdateSkillModes(args, result, deps) {
7554
+ return result.success ? deps.resolveUpdateSkillModes(args) : [];
7555
+ }
7556
+ function buildUpdateSkillMessage(args, result, skillResults) {
7557
+ if (!result.success) {
7558
+ return "";
7559
+ }
7560
+ if (shouldSkipSkills(args)) {
7561
+ return "Managed skill refresh skipped (--no-skills).";
7562
+ }
7563
+ if (skillResults.length === 0) {
7564
+ return "No managed skill packs required refresh.";
7565
+ }
7566
+ return skillResults.map((entry) => entry.message).join("\n");
7567
+ }
7568
+ function buildUpdateCommandResult(args, result, deps) {
7569
+ const skillModes = getResolvedUpdateSkillModes(args, result, deps);
7570
+ const skillResults = result.success && !shouldSkipSkills(args) ? collectUpdateSkillResults(args, deps, skillModes) : [];
7571
+ const message = [result.message, buildUpdateSkillMessage(args, result, skillResults)].filter(Boolean).join("\n");
7572
+ return {
7573
+ success: result.success && skillResults.every((entry) => entry.success),
7574
+ message,
7575
+ data: {
7576
+ cacheCleared: result.cleared,
7577
+ skillModes,
7578
+ skills: skillResults
7579
+ }
7580
+ };
7581
+ }
7582
+ function buildUninstallSkillMessage(args, result, skillsResult) {
7583
+ if (!result.success) {
7584
+ return "";
7585
+ }
7586
+ if (shouldSkipSkills(args)) {
7587
+ return "Managed skill cleanup skipped (--no-skills).";
7588
+ }
7589
+ return skillsResult?.message ?? "No managed skill packs required cleanup.";
7590
+ }
7591
+ function getUninstallTargets(args, mode, result, deps) {
7592
+ if (!result.success || shouldSkipSkills(args)) {
7593
+ return [];
7594
+ }
7595
+ return deps.getBundledSkillLifecycleTargets(mode, {
7596
+ includeLegacyArtifacts: result.removed || deps.hasBundledSkillArtifacts(mode)
7597
+ });
7598
+ }
7599
+ function buildUninstallCommandResult(args, mode, result, deps) {
7600
+ const targets = getUninstallTargets(args, mode, result, deps);
7601
+ const skillsResult = result.success && !shouldSkipSkills(args) ? targets.length > 0 ? deps.removeBundledSkillsForTargets(mode, targets) : createNoOpSkillRemovalResult(mode) : void 0;
7602
+ const skillMessage = buildUninstallSkillMessage(args, result, skillsResult);
7603
+ return {
7604
+ success: result.success && (skillsResult?.success ?? true),
7605
+ message: [result.message, skillMessage].filter(Boolean).join("\n"),
7606
+ data: {
7607
+ config: result,
7608
+ skills: skillsResult
7609
+ }
7610
+ };
7611
+ }
7612
+
7613
+ // src/cli/index.ts
7614
+ var VERSION = typeof package_default.version === "string" ? package_default.version : "0.0.0";
7920
7615
  async function promptInstallMode() {
7921
7616
  if (!process.stdin.isTTY) {
7922
7617
  console.log("Non-interactive mode detected. Using global install.");
7923
7618
  return "global";
7924
7619
  }
7925
- return new Promise((resolve6) => {
7620
+ return new Promise((resolve5) => {
7926
7621
  console.log("\nWhere would you like to install opendevbrowser?\n");
7927
7622
  console.log(" 1. Global (~/.config/opencode/opencode.json)");
7928
7623
  console.log(" 2. Local (./opencode.json in this project)\n");
@@ -7942,23 +7637,23 @@ async function promptInstallMode() {
7942
7637
  resolved = true;
7943
7638
  const input = data.toString().trim();
7944
7639
  if (input === "2") {
7945
- resolve6("local");
7640
+ resolve5("local");
7946
7641
  } else {
7947
- resolve6("global");
7642
+ resolve5("global");
7948
7643
  }
7949
7644
  });
7950
7645
  process.stdin.once("close", () => {
7951
7646
  cleanup();
7952
7647
  if (resolved) return;
7953
7648
  resolved = true;
7954
- resolve6("global");
7649
+ resolve5("global");
7955
7650
  });
7956
7651
  timeoutId = setTimeout(() => {
7957
7652
  timeoutId = null;
7958
7653
  if (resolved) return;
7959
7654
  resolved = true;
7960
7655
  console.log("\nTimeout - using global install.");
7961
- resolve6("global");
7656
+ resolve5("global");
7962
7657
  }, 3e4);
7963
7658
  });
7964
7659
  }
@@ -7978,7 +7673,7 @@ async function promptUninstallMode() {
7978
7673
  console.log("Plugin found in both global and local configs. Use --global or --local flag.");
7979
7674
  return null;
7980
7675
  }
7981
- return new Promise((resolve6) => {
7676
+ return new Promise((resolve5) => {
7982
7677
  console.log("\nopendevbrowser is installed in multiple locations:\n");
7983
7678
  console.log(" 1. Global (~/.config/opencode/opencode.json)");
7984
7679
  console.log(" 2. Local (./opencode.json)");
@@ -7988,15 +7683,15 @@ async function promptUninstallMode() {
7988
7683
  process.stdin.once("data", (data) => {
7989
7684
  const input = data.toString().trim();
7990
7685
  if (input === "1") {
7991
- resolve6("global");
7686
+ resolve5("global");
7992
7687
  } else if (input === "2") {
7993
- resolve6("local");
7688
+ resolve5("local");
7994
7689
  } else {
7995
- resolve6(null);
7690
+ resolve5(null);
7996
7691
  }
7997
7692
  });
7998
7693
  process.stdin.once("close", () => {
7999
- resolve6(null);
7694
+ resolve5(null);
8000
7695
  });
8001
7696
  });
8002
7697
  }
@@ -8054,22 +7749,14 @@ async function main() {
8054
7749
  registerCommand({
8055
7750
  name: "update",
8056
7751
  description: "Clear cached plugin and refresh managed skill packs",
8057
- run: () => {
8058
- const result2 = runUpdate();
8059
- const skillModes = result2.success ? resolveUpdateSkillModes(args) : [];
8060
- const skillResults = result2.success ? skillModes.map((mode) => syncBundledSkills(mode)) : [];
8061
- const skillMessage = args.rawArgs.includes("--no-skills") ? "Managed skill refresh skipped (--no-skills)." : skillResults.length > 0 ? skillResults.map((entry) => entry.message).join("\n") : result2.success ? "No managed skill packs required refresh." : "";
8062
- const message = [result2.message, skillMessage].filter(Boolean).join("\n");
8063
- return {
8064
- success: result2.success && skillResults.every((entry) => entry.success),
8065
- message,
8066
- data: {
8067
- cacheCleared: result2.cleared,
8068
- skillModes,
8069
- skills: skillResults
8070
- }
8071
- };
8072
- }
7752
+ run: () => buildUpdateCommandResult(args, runUpdate(), {
7753
+ resolveUpdateSkillModes,
7754
+ hasInstalledConfig,
7755
+ hasBundledSkillArtifacts,
7756
+ getBundledSkillTargets,
7757
+ getBundledSkillLifecycleTargets,
7758
+ syncBundledSkillsForTargets
7759
+ })
8073
7760
  });
8074
7761
  registerCommand({
8075
7762
  name: "uninstall",
@@ -8085,18 +7772,11 @@ async function main() {
8085
7772
  if (!mode) {
8086
7773
  return { success: false, message: "Error: Please specify --global or --local for uninstall.", exitCode: EXIT_USAGE };
8087
7774
  }
8088
- const result2 = runUninstall(mode);
8089
- const skipSkills = args.rawArgs.includes("--no-skills");
8090
- const skillsResult = result2.success && !skipSkills ? removeBundledSkills(mode) : void 0;
8091
- const skillMessage = skipSkills ? "Managed skill cleanup skipped (--no-skills)." : skillsResult?.message ?? "";
8092
- return {
8093
- success: result2.success && (skillsResult?.success ?? true),
8094
- message: [result2.message, skillMessage].filter(Boolean).join("\n"),
8095
- data: {
8096
- config: result2,
8097
- skills: skillsResult
8098
- }
8099
- };
7775
+ return buildUninstallCommandResult(args, mode, runUninstall(mode), {
7776
+ hasBundledSkillArtifacts,
7777
+ getBundledSkillLifecycleTargets,
7778
+ removeBundledSkillsForTargets
7779
+ });
8100
7780
  }
8101
7781
  });
8102
7782
  registerCommand({