opendevbrowser 0.0.20 → 0.0.22

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 (53) hide show
  1. package/README.md +18 -8
  2. package/dist/chunk-3ILXPKSJ.js +86 -0
  3. package/dist/chunk-3ILXPKSJ.js.map +1 -0
  4. package/dist/{chunk-LMNFPJRI.js → chunk-OGE5KJ4X.js} +18 -12
  5. package/dist/{chunk-LMNFPJRI.js.map → chunk-OGE5KJ4X.js.map} +1 -1
  6. package/dist/chunk-QVWOPIZJ.js +612 -0
  7. package/dist/chunk-QVWOPIZJ.js.map +1 -0
  8. package/dist/{chunk-3VA6XR25.js → chunk-STGGGVYT.js} +23 -100
  9. package/dist/chunk-STGGGVYT.js.map +1 -0
  10. package/dist/{chunk-C6QUKABT.js → chunk-ZE2E7ZGH.js} +243 -37
  11. package/dist/chunk-ZE2E7ZGH.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/help.d.ts.map +1 -1
  16. package/dist/cli/index.js +296 -617
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/installers/postinstall-skill-sync.d.ts +15 -0
  19. package/dist/cli/installers/postinstall-skill-sync.d.ts.map +1 -0
  20. package/dist/cli/installers/postinstall-skill-sync.js +48 -0
  21. package/dist/cli/installers/postinstall-skill-sync.js.map +1 -0
  22. package/dist/cli/installers/skills.d.ts +9 -14
  23. package/dist/cli/installers/skills.d.ts.map +1 -1
  24. package/dist/cli/skill-lifecycle.d.ts +26 -0
  25. package/dist/cli/skill-lifecycle.d.ts.map +1 -0
  26. package/dist/cli/update-skill-modes.d.ts +3 -0
  27. package/dist/cli/update-skill-modes.d.ts.map +1 -0
  28. package/dist/cli/utils/workflow-message.d.ts.map +1 -1
  29. package/dist/index.js +9 -8
  30. package/dist/index.js.map +1 -1
  31. package/dist/inspiredesign/handoff.d.ts +34 -0
  32. package/dist/inspiredesign/handoff.d.ts.map +1 -0
  33. package/dist/opendevbrowser.js +9 -8
  34. package/dist/opendevbrowser.js.map +1 -1
  35. package/dist/providers/inspiredesign-contract.d.ts +33 -0
  36. package/dist/providers/inspiredesign-contract.d.ts.map +1 -1
  37. package/dist/providers/renderer.d.ts +3 -1
  38. package/dist/providers/renderer.d.ts.map +1 -1
  39. package/dist/providers/workflow-handoff.d.ts +14 -0
  40. package/dist/providers/workflow-handoff.d.ts.map +1 -0
  41. package/dist/providers/workflows.d.ts.map +1 -1
  42. package/dist/{providers-3VBLTNAX.js → providers-ZIVHHH4F.js} +2 -2
  43. package/dist/public-surface/generated-manifest.d.ts +3 -3
  44. package/dist/public-surface/generated-manifest.d.ts.map +1 -1
  45. package/dist/public-surface/source.d.ts +4 -4
  46. package/dist/skills/skill-loader.js +2 -1
  47. package/extension/manifest.json +1 -1
  48. package/package.json +6 -4
  49. package/scripts/postinstall-sync-skills.mjs +33 -0
  50. package/skills/opendevbrowser-product-presentation-asset/SKILL.md +2 -2
  51. package/dist/chunk-3VA6XR25.js.map +0 -1
  52. package/dist/chunk-C6QUKABT.js.map +0 -1
  53. /package/dist/{providers-3VBLTNAX.js.map → providers-ZIVHHH4F.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -31,22 +31,39 @@ import {
31
31
  resolveExitCode,
32
32
  startDaemon,
33
33
  toCliError
34
- } from "../chunk-LMNFPJRI.js";
34
+ } from "../chunk-OGE5KJ4X.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";
42
57
  import "../chunk-Y2KL55OG.js";
43
58
  import {
59
+ INSPIREDESIGN_HANDOFF_COMMANDS,
60
+ INSPIREDESIGN_HANDOFF_GUIDANCE,
44
61
  cleanupExpiredArtifacts,
45
62
  isChallengeAutomationMode,
46
63
  setDefaultLogSink,
47
64
  stderrSink,
48
65
  summarizePrimaryProviderIssue
49
- } from "../chunk-C6QUKABT.js";
66
+ } from "../chunk-ZE2E7ZGH.js";
50
67
  import "../chunk-FUSXMW3G.js";
51
68
 
52
69
  // src/cli/args.ts
@@ -449,7 +466,7 @@ var HELP_CAPABILITY_ENTRIES = [
449
466
  description: "Control the bounded browser-scoped computer-use challenge lane with --challenge-automation-mode; the optional helper is not a desktop agent.",
450
467
  details: [
451
468
  { label: "flag:", value: "--challenge-automation-mode off|browser|browser_with_helper" },
452
- { label: "works:", value: "research run, shopping run, product-video run, macro-resolve --execute" },
469
+ { label: "works:", value: "research run, shopping run, product-video run, inspiredesign run, macro-resolve --execute" },
453
470
  { label: "entry:", value: onboarding_metadata_default.quickStartCommands.computerUseEntry },
454
471
  { label: "proof:", value: "review, session-inspector, workflow fallback metadata" }
455
472
  ]
@@ -486,6 +503,16 @@ var HELP_ONBOARDING_ENTRIES = [
486
503
  description: "Start deal hunting with explicit providers in managed mode and only trust regional comparisons when the result reports `region_authoritative=true`.",
487
504
  details: [{ label: "cli:", value: onboarding_metadata_default.quickStartCommands.validatedShopping }]
488
505
  },
506
+ {
507
+ label: "inspiredesign_followthrough",
508
+ description: "After inspiredesign finishes, continue in Canvas with the emitted request template and load the canvas-contract design-agent lane before patching.",
509
+ details: [
510
+ { label: "quick:", value: INSPIREDESIGN_HANDOFF_COMMANDS.loadBestPractices },
511
+ { label: "design:", value: INSPIREDESIGN_HANDOFF_COMMANDS.loadDesignAgent },
512
+ { label: "prep:", value: INSPIREDESIGN_HANDOFF_GUIDANCE.prepareCanvasPlanRequest },
513
+ { label: "run:", value: INSPIREDESIGN_HANDOFF_COMMANDS.continueInCanvas }
514
+ ]
515
+ },
489
516
  {
490
517
  label: "computer_use_entry",
491
518
  description: "Enter browser-scoped computer use from a workflow run, not from a separate desktop command family.",
@@ -515,6 +542,7 @@ var HELP_ONBOARDING_ENTRIES = [
515
542
  ];
516
543
  var HELP_REFERENCE_ENTRIES = [
517
544
  { label: "src/cli/onboarding-metadata.json", description: "Canonical first-contact onboarding metadata shared by help, nudges, and proof lanes." },
545
+ { label: "src/inspiredesign/handoff.ts", description: "Shared inspiredesign follow-through commands, artifact names, and Canvas continuation guidance." },
518
546
  { label: "src/public-surface/source.ts", description: "Authoritative command, usage, flag, and tool surface metadata." },
519
547
  { label: "src/public-surface/generated-manifest.ts", description: "Checked-in generated public-surface snapshot consumed by help and parity tests." },
520
548
  { label: "src/public-surface/generated-manifest.json", description: "Checked-in generated public-surface snapshot consumed by inventory scripts." },
@@ -693,86 +721,12 @@ function getCommand(name) {
693
721
  }
694
722
 
695
723
  // src/cli/installers/global.ts
696
- import * as fs3 from "fs";
724
+ import * as fs2 from "fs";
697
725
 
698
- // src/cli/utils/config.ts
726
+ // src/cli/templates/config.ts
699
727
  import * as fs from "fs";
700
728
  import * as path from "path";
701
729
  import * as os from "os";
702
- import { parse as parseJsonc, modify, applyEdits } from "jsonc-parser";
703
- var PLUGIN_NAME = "opendevbrowser";
704
- var SCHEMA_URL = "https://opencode.ai/config.json";
705
- function getGlobalConfigPath() {
706
- const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(os.homedir(), ".config", "opencode");
707
- return path.join(configDir, "opencode.json");
708
- }
709
- function getLocalConfigPath() {
710
- return path.join(process.cwd(), "opencode.json");
711
- }
712
- function ensureDir(dirPath) {
713
- if (!fs.existsSync(dirPath)) {
714
- fs.mkdirSync(dirPath, { recursive: true });
715
- }
716
- }
717
- function readConfig(configPath) {
718
- if (!fs.existsSync(configPath)) {
719
- return { content: "", config: {} };
720
- }
721
- const content = fs.readFileSync(configPath, "utf-8");
722
- const errors = [];
723
- const parsed = parseJsonc(content, errors, { allowTrailingComma: true });
724
- if (errors.length > 0) {
725
- const firstError = errors[0];
726
- throw new Error(`Invalid JSONC at ${configPath}: parse error at offset ${firstError?.offset ?? 0}`);
727
- }
728
- return { content, config: parsed ?? {} };
729
- }
730
- function hasPlugin(config, pluginName = PLUGIN_NAME) {
731
- return config.plugin?.includes(pluginName) ?? false;
732
- }
733
- function createConfigWithPlugin(pluginName = PLUGIN_NAME) {
734
- return {
735
- $schema: SCHEMA_URL,
736
- plugin: [pluginName]
737
- };
738
- }
739
- function updateConfigContent(content, pluginName = PLUGIN_NAME) {
740
- if (!content.trim()) {
741
- return JSON.stringify(createConfigWithPlugin(pluginName), null, 2) + "\n";
742
- }
743
- const parsed = parseJsonc(content, [], { allowTrailingComma: true }) ?? {};
744
- if (parsed.plugin?.includes(pluginName)) {
745
- return content;
746
- }
747
- let result = content;
748
- if (!parsed.$schema) {
749
- const edits2 = modify(result, ["$schema"], SCHEMA_URL, { formattingOptions: { tabSize: 2, insertSpaces: true } });
750
- result = applyEdits(result, edits2);
751
- }
752
- const newPlugins = parsed.plugin ? [...parsed.plugin, pluginName] : [pluginName];
753
- const edits = modify(result, ["plugin"], newPlugins, { formattingOptions: { tabSize: 2, insertSpaces: true } });
754
- result = applyEdits(result, edits);
755
- return result;
756
- }
757
- function removePluginFromContent(content, pluginName = PLUGIN_NAME) {
758
- if (!content.trim()) {
759
- return content;
760
- }
761
- const parsed = parseJsonc(content, [], { allowTrailingComma: true }) ?? {};
762
- if (!parsed.plugin?.includes(pluginName)) {
763
- return content;
764
- }
765
- const newPlugins = parsed.plugin.filter((p) => p !== pluginName);
766
- const edits = modify(content, ["plugin"], newPlugins.length > 0 ? newPlugins : void 0, {
767
- formattingOptions: { tabSize: 2, insertSpaces: true }
768
- });
769
- return applyEdits(content, edits);
770
- }
771
-
772
- // src/cli/templates/config.ts
773
- import * as fs2 from "fs";
774
- import * as path2 from "path";
775
- import * as os2 from "os";
776
730
  function buildConfigTemplate(relayToken, daemonToken) {
777
731
  return `{
778
732
  // OpenDevBrowser Plugin Configuration
@@ -849,19 +803,19 @@ function buildConfigTemplate(relayToken, daemonToken) {
849
803
  }
850
804
  function getPluginConfigPath(mode) {
851
805
  if (mode === "global") {
852
- const configDir = process.env.OPENCODE_CONFIG_DIR || path2.join(os2.homedir(), ".config", "opencode");
853
- return path2.join(configDir, "opendevbrowser.jsonc");
806
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path.join(os.homedir(), ".config", "opencode");
807
+ return path.join(configDir, "opendevbrowser.jsonc");
854
808
  }
855
- return path2.join(process.cwd(), "opendevbrowser.jsonc");
809
+ return path.join(process.cwd(), "opendevbrowser.jsonc");
856
810
  }
857
811
  function createPluginConfig(mode) {
858
812
  const configPath = getPluginConfigPath(mode);
859
- if (fs2.existsSync(configPath)) {
813
+ if (fs.existsSync(configPath)) {
860
814
  return { created: false, path: configPath };
861
815
  }
862
- const dir = path2.dirname(configPath);
863
- if (!fs2.existsSync(dir)) {
864
- fs2.mkdirSync(dir, { recursive: true });
816
+ const dir = path.dirname(configPath);
817
+ if (!fs.existsSync(dir)) {
818
+ fs.mkdirSync(dir, { recursive: true });
865
819
  }
866
820
  const relayToken = generateSecureToken();
867
821
  const daemonToken = generateSecureToken();
@@ -885,7 +839,7 @@ function installGlobal(withConfig = false) {
885
839
  }
886
840
  const newContent = updateConfigContent(content, "opendevbrowser");
887
841
  ensureDir(configPath.replace(/[/\\][^/\\]+$/, ""));
888
- fs3.writeFileSync(configPath, newContent, "utf-8");
842
+ fs2.writeFileSync(configPath, newContent, "utf-8");
889
843
  if (withConfig) {
890
844
  createPluginConfig("global");
891
845
  }
@@ -909,7 +863,7 @@ function installGlobal(withConfig = false) {
909
863
  }
910
864
 
911
865
  // src/cli/installers/local.ts
912
- import * as fs4 from "fs";
866
+ import * as fs3 from "fs";
913
867
  function installLocal(withConfig = false) {
914
868
  const configPath = getLocalConfigPath();
915
869
  try {
@@ -924,7 +878,7 @@ function installLocal(withConfig = false) {
924
878
  };
925
879
  }
926
880
  const newContent = updateConfigContent(content, "opendevbrowser");
927
- fs4.writeFileSync(configPath, newContent, "utf-8");
881
+ fs3.writeFileSync(configPath, newContent, "utf-8");
928
882
  if (withConfig) {
929
883
  createPluginConfig("local");
930
884
  }
@@ -947,407 +901,32 @@ function installLocal(withConfig = false) {
947
901
  }
948
902
  }
949
903
 
950
- // src/cli/installers/skills.ts
951
- import * as crypto from "crypto";
952
- import * as fs5 from "fs";
953
- import * as path4 from "path";
954
-
955
- // src/cli/utils/skills.ts
956
- import * as path3 from "path";
957
- import * as os3 from "os";
958
- var SKILL_DIR_NAME = "skill";
959
- var SKILLS_DIR_NAME = "skills";
960
- function getGlobalSkillDir() {
961
- const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
962
- return path3.join(configDir, SKILL_DIR_NAME);
963
- }
964
- function getLocalSkillDir() {
965
- return path3.join(process.cwd(), ".opencode", SKILL_DIR_NAME);
966
- }
967
- function getCodexHomeDir() {
968
- return process.env.CODEX_HOME || path3.join(os3.homedir(), ".codex");
969
- }
970
- function getClaudeCodeHomeDir() {
971
- return process.env.CLAUDECODE_HOME || path3.join(os3.homedir(), ".claude");
972
- }
973
- function getAmpHomeDir() {
974
- return process.env.AMP_CLI_HOME || path3.join(os3.homedir(), ".amp");
975
- }
976
- function dedupeTargets(targets) {
977
- const deduped = /* @__PURE__ */ new Map();
978
- for (const target of targets) {
979
- const key = path3.resolve(target.dir);
980
- const existing = deduped.get(key);
981
- if (existing) {
982
- if (!existing.agents.includes(target.agent)) {
983
- existing.agents.push(target.agent);
984
- }
985
- continue;
986
- }
987
- deduped.set(key, { agents: [target.agent], dir: target.dir });
988
- }
989
- return Array.from(deduped.values());
990
- }
991
- function getGlobalSkillTargets() {
992
- const claudeSkillsDir = path3.join(getClaudeCodeHomeDir(), SKILLS_DIR_NAME);
993
- const ampSkillsDir = path3.join(getAmpHomeDir(), SKILLS_DIR_NAME);
994
- return dedupeTargets([
995
- { agent: "opencode", dir: getGlobalSkillDir() },
996
- { agent: "codex", dir: path3.join(getCodexHomeDir(), SKILLS_DIR_NAME) },
997
- { agent: "claudecode", dir: claudeSkillsDir },
998
- { agent: "ampcli", dir: ampSkillsDir }
999
- ]);
1000
- }
1001
- function getLocalSkillTargets() {
1002
- const localClaudeSkillsDir = path3.join(process.cwd(), ".claude", SKILLS_DIR_NAME);
1003
- const localAmpSkillsDir = path3.join(process.cwd(), ".amp", SKILLS_DIR_NAME);
1004
- return dedupeTargets([
1005
- { agent: "opencode", dir: getLocalSkillDir() },
1006
- { agent: "codex", dir: path3.join(process.cwd(), ".codex", SKILLS_DIR_NAME) },
1007
- { agent: "claudecode", dir: localClaudeSkillsDir },
1008
- { agent: "ampcli", dir: localAmpSkillsDir }
1009
- ]);
1010
- }
1011
-
1012
- // src/cli/installers/skills.ts
1013
- var LEGACY_ALIAS_DIRS = [
1014
- { name: "research", canonical: "opendevbrowser-research" },
1015
- { name: "shopping", canonical: "opendevbrowser-shopping" }
1016
- ];
1017
- function getTargets(mode) {
1018
- return mode === "global" ? getGlobalSkillTargets() : getLocalSkillTargets();
1019
- }
1020
- function getCanonicalBundledSkillNames() {
1021
- return listBundledSkillDirectories().map((entry) => entry.name);
1022
- }
1023
- function hasCanonicalBundledSkillInTarget(targetDir, packNames) {
1024
- return packNames.some((packName) => fs5.existsSync(path4.join(targetDir, packName)));
1025
- }
1026
- function formatSummary(parts, totalTargets, failures) {
1027
- const summary = parts.length > 0 ? parts.join(", ") : "no lifecycle changes";
1028
- const failureSummary = failures > 0 ? `, ${failures} failed` : "";
1029
- return `${summary} across ${totalTargets} targets${failureSummary}`;
1030
- }
1031
- function hashDirectoryTree(dirPath) {
1032
- const hash = crypto.createHash("sha256");
1033
- const visit = (currentPath, relativePath) => {
1034
- const entries = fs5.readdirSync(currentPath, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
1035
- for (const entry of entries) {
1036
- const absolutePath = path4.join(currentPath, entry.name);
1037
- const entryRelativePath = relativePath ? path4.posix.join(relativePath, entry.name) : entry.name;
1038
- if (entry.isDirectory()) {
1039
- hash.update(`D:${entryRelativePath}\0`);
1040
- visit(absolutePath, entryRelativePath);
1041
- continue;
1042
- }
1043
- if (entry.isFile()) {
1044
- hash.update(`F:${entryRelativePath}\0`);
1045
- hash.update(fs5.readFileSync(absolutePath));
1046
- hash.update("\0");
1047
- continue;
1048
- }
1049
- if (entry.isSymbolicLink()) {
1050
- hash.update(`L:${entryRelativePath}\0${fs5.readlinkSync(absolutePath)}\0`);
1051
- }
1052
- }
1053
- };
1054
- visit(dirPath, "");
1055
- return hash.digest("hex");
1056
- }
1057
- function syncSkillDirectory(sourcePath, targetPath, sourceFingerprint) {
1058
- if (!fs5.existsSync(targetPath)) {
1059
- fs5.cpSync(sourcePath, targetPath, { recursive: true });
1060
- return "installed";
1061
- }
1062
- const targetFingerprint = hashDirectoryTree(targetPath);
1063
- if (targetFingerprint === sourceFingerprint) {
1064
- return "unchanged";
1065
- }
1066
- const parentDir = path4.dirname(targetPath);
1067
- const targetName = path4.basename(targetPath);
1068
- const stagingRoot = fs5.mkdtempSync(path4.join(parentDir, `.${targetName}-sync-`));
1069
- const stagedPath = path4.join(stagingRoot, targetName);
1070
- const backupPath = path4.join(stagingRoot, `${targetName}-backup`);
1071
- try {
1072
- fs5.cpSync(sourcePath, stagedPath, { recursive: true });
1073
- fs5.renameSync(targetPath, backupPath);
1074
- try {
1075
- fs5.renameSync(stagedPath, targetPath);
1076
- } catch (error) {
1077
- if (fs5.existsSync(backupPath) && !fs5.existsSync(targetPath)) {
1078
- fs5.renameSync(backupPath, targetPath);
1079
- }
1080
- throw error;
1081
- }
1082
- fs5.rmSync(backupPath, { recursive: true, force: true });
1083
- return "refreshed";
1084
- } finally {
1085
- if (fs5.existsSync(stagedPath)) {
1086
- fs5.rmSync(stagedPath, { recursive: true, force: true });
1087
- }
1088
- if (fs5.existsSync(backupPath)) {
1089
- fs5.rmSync(backupPath, { recursive: true, force: true });
1090
- }
1091
- fs5.rmSync(stagingRoot, { recursive: true, force: true });
1092
- }
1093
- }
1094
- function cleanupLegacyAlias(targetDir, aliasName) {
1095
- const aliasPath = path4.join(targetDir, aliasName);
1096
- if (!fs5.existsSync(aliasPath)) {
1097
- return { removed: [], preserved: [] };
1098
- }
1099
- let stats;
1100
- try {
1101
- stats = fs5.statSync(aliasPath);
1102
- } catch {
1103
- return {
1104
- removed: [],
1105
- preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1106
- };
1107
- }
1108
- if (!stats.isDirectory()) {
1109
- return {
1110
- removed: [],
1111
- preserved: [{ targetDir, name: aliasName, reason: "unknown_layout" }]
1112
- };
1113
- }
1114
- if (fs5.existsSync(path4.join(aliasPath, "SKILL.md"))) {
1115
- return {
1116
- removed: [],
1117
- preserved: [{ targetDir, name: aliasName, reason: "contains_skill_md" }]
1118
- };
1119
- }
1120
- const entries = fs5.readdirSync(aliasPath);
1121
- if (entries.length === 0) {
1122
- fs5.rmSync(aliasPath, { recursive: true, force: true });
1123
- return { removed: [aliasName], preserved: [] };
1124
- }
1125
- return {
1126
- removed: [],
1127
- preserved: [{ targetDir, name: aliasName, reason: "non_empty" }]
1128
- };
1129
- }
1130
- function cleanupLegacyAliases(targetDir) {
1131
- const removed = [];
1132
- const preserved = [];
1133
- for (const alias of LEGACY_ALIAS_DIRS) {
1134
- const result = cleanupLegacyAlias(targetDir, alias.name);
1135
- removed.push(...result.removed);
1136
- preserved.push(...result.preserved);
1137
- }
1138
- return { removed, preserved };
1139
- }
1140
- function buildSyncMessage(mode, result) {
1141
- return `Skills ${mode} sync: ${formatSummary(
1142
- [
1143
- result.installed.length > 0 ? `${result.installed.length} installed` : "",
1144
- result.refreshed.length > 0 ? `${result.refreshed.length} refreshed` : "",
1145
- result.unchanged.length > 0 ? `${result.unchanged.length} unchanged` : "",
1146
- result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1147
- result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1148
- ].filter(Boolean),
1149
- result.targets.length,
1150
- result.targets.filter((entry) => !entry.success).length
1151
- )}`;
1152
- }
1153
- function buildRemovalMessage(mode, result) {
1154
- return `Skills ${mode} removal: ${formatSummary(
1155
- [
1156
- result.removed.length > 0 ? `${result.removed.length} removed` : "",
1157
- result.missing.length > 0 ? `${result.missing.length} already absent` : "",
1158
- result.removedLegacyAliases.length > 0 ? `${result.removedLegacyAliases.length} legacy aliases removed` : "",
1159
- result.preservedLegacyAliases.length > 0 ? `${result.preservedLegacyAliases.length} legacy aliases preserved` : ""
1160
- ].filter(Boolean),
1161
- result.targets.length,
1162
- result.targets.filter((entry) => !entry.success).length
1163
- )}`;
1164
- }
1165
- function syncBundledSkills(mode) {
1166
- const targets = getTargets(mode);
1167
- const targetResults = [];
1168
- try {
1169
- const sourceDir = getBundledSkillsDir();
1170
- const packNames = getCanonicalBundledSkillNames();
1171
- const bundledFingerprints = /* @__PURE__ */ new Map();
1172
- for (const packName of packNames) {
1173
- const sourcePath = path4.join(sourceDir, packName);
1174
- if (!fs5.existsSync(sourcePath)) {
1175
- throw new Error(`Bundled skill directory missing: ${packName}`);
1176
- }
1177
- bundledFingerprints.set(packName, hashDirectoryTree(sourcePath));
1178
- }
1179
- for (const target of targets) {
1180
- const installed = [];
1181
- const refreshed = [];
1182
- const unchanged = [];
1183
- const removedLegacyAliases = [];
1184
- const preservedLegacyAliases = [];
1185
- try {
1186
- ensureDir(target.dir);
1187
- for (const packName of packNames) {
1188
- const sourcePath = path4.join(sourceDir, packName);
1189
- const targetPath = path4.join(target.dir, packName);
1190
- const sourceFingerprint = bundledFingerprints.get(packName);
1191
- if (!sourceFingerprint) {
1192
- throw new Error(`Bundled fingerprint missing: ${packName}`);
1193
- }
1194
- const outcome = syncSkillDirectory(sourcePath, targetPath, sourceFingerprint);
1195
- if (outcome === "installed") {
1196
- installed.push(packName);
1197
- } else if (outcome === "refreshed") {
1198
- refreshed.push(packName);
1199
- } else {
1200
- unchanged.push(packName);
1201
- }
1202
- }
1203
- const legacyCleanup = cleanupLegacyAliases(target.dir);
1204
- removedLegacyAliases.push(...legacyCleanup.removed);
1205
- preservedLegacyAliases.push(...legacyCleanup.preserved);
1206
- targetResults.push({
1207
- agents: target.agents,
1208
- targetDir: target.dir,
1209
- installed,
1210
- refreshed,
1211
- unchanged,
1212
- removedLegacyAliases,
1213
- preservedLegacyAliases,
1214
- success: true
1215
- });
1216
- } catch (error) {
1217
- const message = error instanceof Error ? error.message : String(error);
1218
- targetResults.push({
1219
- agents: target.agents,
1220
- targetDir: target.dir,
1221
- installed,
1222
- refreshed,
1223
- unchanged,
1224
- removedLegacyAliases,
1225
- preservedLegacyAliases,
1226
- success: false,
1227
- error: message
1228
- });
1229
- }
1230
- }
1231
- const result = {
1232
- success: targetResults.every((entry) => entry.success),
1233
- message: "",
1234
- mode,
1235
- targets: targetResults,
1236
- installed: targetResults.flatMap((entry) => entry.installed),
1237
- refreshed: targetResults.flatMap((entry) => entry.refreshed),
1238
- unchanged: targetResults.flatMap((entry) => entry.unchanged),
1239
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1240
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1241
- };
1242
- result.message = buildSyncMessage(mode, result);
1243
- return result;
1244
- } catch (error) {
1245
- const message = error instanceof Error ? error.message : String(error);
1246
- const result = {
1247
- success: false,
1248
- message: "",
1249
- mode,
1250
- targets: targetResults,
1251
- installed: targetResults.flatMap((entry) => entry.installed),
1252
- refreshed: targetResults.flatMap((entry) => entry.refreshed),
1253
- unchanged: targetResults.flatMap((entry) => entry.unchanged),
1254
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1255
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1256
- };
1257
- result.message = `Failed to sync skills (${mode}): ${message}`;
1258
- return result;
1259
- }
1260
- }
1261
- function removeBundledSkills(mode) {
1262
- const targets = getTargets(mode);
1263
- const packNames = getCanonicalBundledSkillNames();
1264
- const targetResults = [];
1265
- for (const target of targets) {
1266
- const removed = [];
1267
- const missing = [];
1268
- const removedLegacyAliases = [];
1269
- const preservedLegacyAliases = [];
1270
- try {
1271
- for (const packName of packNames) {
1272
- const targetPath = path4.join(target.dir, packName);
1273
- if (fs5.existsSync(targetPath)) {
1274
- fs5.rmSync(targetPath, { recursive: true, force: true });
1275
- removed.push(packName);
1276
- } else {
1277
- missing.push(packName);
1278
- }
1279
- }
1280
- const legacyCleanup = cleanupLegacyAliases(target.dir);
1281
- removedLegacyAliases.push(...legacyCleanup.removed);
1282
- preservedLegacyAliases.push(...legacyCleanup.preserved);
1283
- targetResults.push({
1284
- agents: target.agents,
1285
- targetDir: target.dir,
1286
- removed,
1287
- missing,
1288
- removedLegacyAliases,
1289
- preservedLegacyAliases,
1290
- success: true
1291
- });
1292
- } catch (error) {
1293
- const message = error instanceof Error ? error.message : String(error);
1294
- targetResults.push({
1295
- agents: target.agents,
1296
- targetDir: target.dir,
1297
- removed,
1298
- missing,
1299
- removedLegacyAliases,
1300
- preservedLegacyAliases,
1301
- success: false,
1302
- error: message
1303
- });
1304
- }
1305
- }
1306
- const result = {
1307
- success: targetResults.every((entry) => entry.success),
1308
- message: "",
1309
- mode,
1310
- targets: targetResults,
1311
- removed: targetResults.flatMap((entry) => entry.removed),
1312
- missing: targetResults.flatMap((entry) => entry.missing),
1313
- removedLegacyAliases: targetResults.flatMap((entry) => entry.removedLegacyAliases),
1314
- preservedLegacyAliases: targetResults.flatMap((entry) => entry.preservedLegacyAliases)
1315
- };
1316
- result.message = buildRemovalMessage(mode, result);
1317
- return result;
1318
- }
1319
- function hasBundledSkillArtifacts(mode) {
1320
- const packNames = getCanonicalBundledSkillNames();
1321
- const targets = getTargets(mode);
1322
- return targets.some((target) => hasCanonicalBundledSkillInTarget(target.dir, packNames));
1323
- }
1324
-
1325
904
  // src/cli/commands/update.ts
1326
- import * as fs6 from "fs";
1327
- import * as path5 from "path";
1328
- import * as os4 from "os";
1329
- var PLUGIN_NAME2 = "opendevbrowser";
905
+ import * as fs4 from "fs";
906
+ import * as path2 from "path";
907
+ import * as os2 from "os";
908
+ var PLUGIN_NAME = "opendevbrowser";
1330
909
  function getCacheDir() {
1331
- return process.env.OPENCODE_CACHE_DIR || path5.join(os4.homedir(), ".cache", "opencode");
910
+ return process.env.OPENCODE_CACHE_DIR || path2.join(os2.homedir(), ".cache", "opencode");
1332
911
  }
1333
912
  function rmdir(dirPath) {
1334
913
  const cacheDir = getCacheDir();
1335
- const resolvedCache = path5.resolve(cacheDir);
1336
- const resolvedPath = path5.resolve(dirPath);
1337
- if (!resolvedPath.startsWith(resolvedCache + path5.sep) || resolvedPath === resolvedCache) {
914
+ const resolvedCache = path2.resolve(cacheDir);
915
+ const resolvedPath = path2.resolve(dirPath);
916
+ if (!resolvedPath.startsWith(resolvedCache + path2.sep) || resolvedPath === resolvedCache) {
1338
917
  throw new Error(`Security: refusing to delete path outside cache directory: ${dirPath}`);
1339
918
  }
1340
- if (fs6.existsSync(dirPath)) {
1341
- fs6.rmSync(dirPath, { recursive: true, force: true });
919
+ if (fs4.existsSync(dirPath)) {
920
+ fs4.rmSync(dirPath, { recursive: true, force: true });
1342
921
  }
1343
922
  }
1344
923
  function runUpdate() {
1345
924
  const cacheDir = getCacheDir();
1346
- const nodeModulesDir = path5.join(cacheDir, "node_modules");
1347
- const pluginCacheDir = path5.join(nodeModulesDir, PLUGIN_NAME2);
925
+ const nodeModulesDir = path2.join(cacheDir, "node_modules");
926
+ const pluginCacheDir = path2.join(nodeModulesDir, PLUGIN_NAME);
1348
927
  try {
1349
- if (!fs6.existsSync(pluginCacheDir)) {
1350
- if (fs6.existsSync(nodeModulesDir)) {
928
+ if (!fs4.existsSync(pluginCacheDir)) {
929
+ if (fs4.existsSync(nodeModulesDir)) {
1351
930
  rmdir(nodeModulesDir);
1352
931
  return {
1353
932
  success: true,
@@ -1378,20 +957,29 @@ function runUpdate() {
1378
957
  }
1379
958
 
1380
959
  // src/cli/commands/uninstall.ts
1381
- import * as fs7 from "fs";
1382
- import * as path6 from "path";
1383
- import * as os5 from "os";
960
+ import * as fs5 from "fs";
961
+ import * as path3 from "path";
962
+ import * as os3 from "os";
963
+ function hasInstalledConfig(mode) {
964
+ const configPath = mode === "global" ? getGlobalConfigPath() : getLocalConfigPath();
965
+ try {
966
+ const { config } = readConfig(configPath);
967
+ return hasPlugin(config);
968
+ } catch {
969
+ return false;
970
+ }
971
+ }
1384
972
  function getPluginConfigPath2(mode) {
1385
973
  if (mode === "global") {
1386
- const configDir = process.env.OPENCODE_CONFIG_DIR || path6.join(os5.homedir(), ".config", "opencode");
1387
- return path6.join(configDir, "opendevbrowser.jsonc");
974
+ const configDir = process.env.OPENCODE_CONFIG_DIR || path3.join(os3.homedir(), ".config", "opencode");
975
+ return path3.join(configDir, "opendevbrowser.jsonc");
1388
976
  }
1389
- return path6.join(process.cwd(), "opendevbrowser.jsonc");
977
+ return path3.join(process.cwd(), "opendevbrowser.jsonc");
1390
978
  }
1391
979
  function removePluginConfigFile(mode) {
1392
980
  const configPath = getPluginConfigPath2(mode);
1393
- if (fs7.existsSync(configPath)) {
1394
- fs7.unlinkSync(configPath);
981
+ if (fs5.existsSync(configPath)) {
982
+ fs5.unlinkSync(configPath);
1395
983
  return true;
1396
984
  }
1397
985
  return false;
@@ -1403,14 +991,14 @@ function runUninstall(mode, deleteConfigFile = false) {
1403
991
  if (!hasPlugin(config)) {
1404
992
  return {
1405
993
  success: true,
1406
- message: `opendevbrowser is not installed in ${configPath}`,
994
+ message: `No plugin config found in ${configPath}`,
1407
995
  configPath,
1408
996
  removed: false,
1409
997
  configFileDeleted: false
1410
998
  };
1411
999
  }
1412
1000
  const newContent = removePluginFromContent(content, "opendevbrowser");
1413
- fs7.writeFileSync(configPath, newContent, "utf-8");
1001
+ fs5.writeFileSync(configPath, newContent, "utf-8");
1414
1002
  let configFileDeleted = false;
1415
1003
  if (deleteConfigFile) {
1416
1004
  configFileDeleted = removePluginConfigFile(mode);
@@ -1434,19 +1022,10 @@ function runUninstall(mode, deleteConfigFile = false) {
1434
1022
  }
1435
1023
  }
1436
1024
  function findInstalledConfigs() {
1437
- let global = false;
1438
- let local = false;
1439
- try {
1440
- const { config: globalConfig } = readConfig(getGlobalConfigPath());
1441
- global = hasPlugin(globalConfig);
1442
- } catch {
1443
- }
1444
- try {
1445
- const { config: localConfig } = readConfig(getLocalConfigPath());
1446
- local = hasPlugin(localConfig);
1447
- } catch {
1448
- }
1449
- return { global, local };
1025
+ return {
1026
+ global: hasInstalledConfig("global") || hasManagedBundledSkillInstall("global") || hasBundledSkillArtifacts("global"),
1027
+ local: hasInstalledConfig("local") || hasManagedBundledSkillInstall("local") || hasBundledSkillArtifacts("local")
1028
+ };
1450
1029
  }
1451
1030
 
1452
1031
  // src/cli/commands/serve.ts
@@ -1536,8 +1115,8 @@ function parseRepeatedStringFlag(rawArgs, flag) {
1536
1115
  }
1537
1116
 
1538
1117
  // src/cli/commands/native.ts
1539
- import * as fs8 from "fs";
1540
- import * as path7 from "path";
1118
+ import * as fs6 from "fs";
1119
+ import * as path4 from "path";
1541
1120
  import { execFileSync } from "child_process";
1542
1121
  import { fileURLToPath } from "url";
1543
1122
  var EXTENSION_ID_RE = /^[a-p]{32}$/;
@@ -1572,33 +1151,33 @@ var parseNativeArgs = (rawArgs) => {
1572
1151
  };
1573
1152
  var getManifestDir = () => {
1574
1153
  if (process.platform === "darwin") {
1575
- return path7.join(process.env.HOME || "", "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
1154
+ return path4.join(process.env.HOME || "", "Library", "Application Support", "Google", "Chrome", "NativeMessagingHosts");
1576
1155
  }
1577
1156
  if (process.platform === "linux") {
1578
- return path7.join(process.env.HOME || "", ".config", "google-chrome", "NativeMessagingHosts");
1157
+ return path4.join(process.env.HOME || "", ".config", "google-chrome", "NativeMessagingHosts");
1579
1158
  }
1580
1159
  if (process.platform === "win32") {
1581
- const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path7.join(process.env.USERPROFILE, "AppData", "Local") : "");
1160
+ const base = process.env.LOCALAPPDATA || (process.env.USERPROFILE ? path4.join(process.env.USERPROFILE, "AppData", "Local") : "");
1582
1161
  if (!base) {
1583
1162
  throw createUsageError("LOCALAPPDATA is not set. Unable to locate NativeMessagingHosts directory.");
1584
1163
  }
1585
- return path7.join(base, "Google", "Chrome", "User Data", "NativeMessagingHosts");
1164
+ return path4.join(base, "Google", "Chrome", "User Data", "NativeMessagingHosts");
1586
1165
  }
1587
1166
  throw createUsageError(`Native messaging is not supported on ${process.platform}.`);
1588
1167
  };
1589
1168
  var getScriptsDir = () => {
1590
1169
  const __filename = fileURLToPath(import.meta.url);
1591
- const startDir = path7.dirname(__filename);
1170
+ const startDir = path4.dirname(__filename);
1592
1171
  const rootsToScan = [startDir, process.cwd()];
1593
1172
  for (const root of rootsToScan) {
1594
- let current = path7.resolve(root);
1173
+ let current = path4.resolve(root);
1595
1174
  while (true) {
1596
- const scriptsDir = path7.join(current, "scripts", "native");
1597
- const packageJsonPath = path7.join(current, "package.json");
1598
- if (fs8.existsSync(scriptsDir) && fs8.existsSync(packageJsonPath)) {
1175
+ const scriptsDir = path4.join(current, "scripts", "native");
1176
+ const packageJsonPath = path4.join(current, "package.json");
1177
+ if (fs6.existsSync(scriptsDir) && fs6.existsSync(packageJsonPath)) {
1599
1178
  return scriptsDir;
1600
1179
  }
1601
- const parent = path7.dirname(current);
1180
+ const parent = path4.dirname(current);
1602
1181
  if (parent === current) {
1603
1182
  break;
1604
1183
  }
@@ -1608,18 +1187,18 @@ var getScriptsDir = () => {
1608
1187
  throw createUsageError("Unable to locate scripts/native directory.");
1609
1188
  };
1610
1189
  var getHostScriptPath = () => {
1611
- return path7.join(getScriptsDir(), "host.cjs");
1190
+ return path4.join(getScriptsDir(), "host.cjs");
1612
1191
  };
1613
1192
  var getManifestPath = () => {
1614
- return path7.join(getManifestDir(), "com.opendevbrowser.native.json");
1193
+ return path4.join(getManifestDir(), "com.opendevbrowser.native.json");
1615
1194
  };
1616
1195
  var getWrapperPath = () => {
1617
1196
  const wrapperName = process.platform === "win32" ? "com.opendevbrowser.native.cmd" : "com.opendevbrowser.native.sh";
1618
- return path7.join(getManifestDir(), wrapperName);
1197
+ return path4.join(getManifestDir(), wrapperName);
1619
1198
  };
1620
1199
  var readManifest = (manifestPath) => {
1621
1200
  try {
1622
- const raw = fs8.readFileSync(manifestPath, "utf8");
1201
+ const raw = fs6.readFileSync(manifestPath, "utf8");
1623
1202
  const data = JSON.parse(raw);
1624
1203
  const origins = Array.isArray(data.allowed_origins) ? data.allowed_origins : [];
1625
1204
  const match = origins.find((origin) => origin.startsWith("chrome-extension://"));
@@ -1656,9 +1235,9 @@ var readRegistryPath = () => {
1656
1235
  };
1657
1236
  var normalizePath = (value) => {
1658
1237
  try {
1659
- return fs8.realpathSync(value);
1238
+ return fs6.realpathSync(value);
1660
1239
  } catch {
1661
- return path7.resolve(value);
1240
+ return path4.resolve(value);
1662
1241
  }
1663
1242
  };
1664
1243
  var findExtensionIdInCommands = (preferences) => {
@@ -1719,8 +1298,8 @@ var getExtensionPathCandidates = () => {
1719
1298
  if (primary) {
1720
1299
  candidates.add(normalizePath(primary));
1721
1300
  }
1722
- const cwdExtension = path7.join(process.cwd(), "extension");
1723
- if (fs8.existsSync(path7.join(cwdExtension, "manifest.json"))) {
1301
+ const cwdExtension = path4.join(process.cwd(), "extension");
1302
+ if (fs6.existsSync(path4.join(cwdExtension, "manifest.json"))) {
1724
1303
  candidates.add(normalizePath(cwdExtension));
1725
1304
  }
1726
1305
  if (candidates.size === 0) {
@@ -1805,13 +1384,13 @@ var getNativeStatusSnapshot = () => {
1805
1384
  let manifestExists = false;
1806
1385
  let wrapperExists = false;
1807
1386
  let extensionIdValue = null;
1808
- if (fs8.existsSync(manifestPath)) {
1387
+ if (fs6.existsSync(manifestPath)) {
1809
1388
  manifestExists = true;
1810
1389
  installed = true;
1811
1390
  const manifest = readManifest(manifestPath);
1812
1391
  extensionIdValue = manifest.extensionId;
1813
1392
  }
1814
- if (fs8.existsSync(wrapperPath)) {
1393
+ if (fs6.existsSync(wrapperPath)) {
1815
1394
  wrapperExists = true;
1816
1395
  }
1817
1396
  if (!manifestExists || !wrapperExists) {
@@ -1871,7 +1450,7 @@ function installNativeHost(extensionId) {
1871
1450
  };
1872
1451
  }
1873
1452
  const hostScript = getHostScriptPath();
1874
- if (!fs8.existsSync(hostScript)) {
1453
+ if (!fs6.existsSync(hostScript)) {
1875
1454
  return {
1876
1455
  success: false,
1877
1456
  message: `Native host not found at ${hostScript}.`,
@@ -1880,7 +1459,7 @@ function installNativeHost(extensionId) {
1880
1459
  }
1881
1460
  const scriptsDir = getScriptsDir();
1882
1461
  const manifestPath = getManifestPath();
1883
- const installScript = process.platform === "win32" ? path7.join(scriptsDir, "install.ps1") : path7.join(scriptsDir, "install.sh");
1462
+ const installScript = process.platform === "win32" ? path4.join(scriptsDir, "install.ps1") : path4.join(scriptsDir, "install.sh");
1884
1463
  try {
1885
1464
  runScript(installScript, [normalized]);
1886
1465
  return {
@@ -1900,7 +1479,7 @@ function installNativeHost(extensionId) {
1900
1479
  async function runNativeCommand(args) {
1901
1480
  const { subcommand, extensionId } = parseNativeArgs(args.rawArgs);
1902
1481
  const scriptsDir = getScriptsDir();
1903
- const uninstallScript = process.platform === "win32" ? path7.join(scriptsDir, "uninstall.ps1") : path7.join(scriptsDir, "uninstall.sh");
1482
+ const uninstallScript = process.platform === "win32" ? path4.join(scriptsDir, "uninstall.ps1") : path4.join(scriptsDir, "uninstall.sh");
1904
1483
  if (subcommand === "install") {
1905
1484
  return installNativeHost(extensionId);
1906
1485
  }
@@ -2276,9 +1855,9 @@ ${nativeMessage}${fingerprintNote}${staleNote}` : `${baseMessage}${fingerprintNo
2276
1855
 
2277
1856
  // src/cli/daemon-autostart.ts
2278
1857
  import { execFileSync as execFileSync2 } from "child_process";
2279
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
2280
- import { homedir as homedir6, tmpdir } from "os";
2281
- import { dirname as dirname5, isAbsolute, join as join8, relative, resolve as resolve4 } from "path";
1858
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
1859
+ import { homedir as homedir4, tmpdir } from "os";
1860
+ import { dirname as dirname3, isAbsolute, join as join5, relative, resolve as resolve3 } from "path";
2282
1861
  import { fileURLToPath as fileURLToPath2 } from "url";
2283
1862
  var MAC_LABEL = "com.opendevbrowser.daemon";
2284
1863
  var WIN_TASK_NAME = "OpenDevBrowser Daemon";
@@ -2288,9 +1867,9 @@ var defaultDeps = () => ({
2288
1867
  argv1: process.argv[1] ?? "",
2289
1868
  moduleUrl: import.meta.url,
2290
1869
  uid: typeof process.getuid === "function" ? process.getuid() : 0,
2291
- homedir: homedir6,
2292
- existsSync: existsSync7,
2293
- mkdirSync: mkdirSync3,
1870
+ homedir: homedir4,
1871
+ existsSync: existsSync5,
1872
+ mkdirSync: mkdirSync2,
2294
1873
  writeFileSync: writeFileSync4,
2295
1874
  unlinkSync: unlinkSync2,
2296
1875
  execFileSync: execFileSync2,
@@ -2302,14 +1881,14 @@ var formatCommand = (programArguments) => {
2302
1881
  };
2303
1882
  var resolveCliPathFromModule = (moduleUrl, exists) => {
2304
1883
  const modulePath = fileURLToPath2(moduleUrl);
2305
- const candidate = resolve4(dirname5(modulePath), "..", "index.js");
1884
+ const candidate = resolve3(dirname3(modulePath), "..", "index.js");
2306
1885
  if (!exists(candidate)) {
2307
1886
  throw new Error(`CLI entrypoint not found at ${candidate}`);
2308
1887
  }
2309
1888
  return candidate;
2310
1889
  };
2311
1890
  var normalizeComparisonPath = (value, platform) => {
2312
- const resolvedPath = resolve4(value);
1891
+ const resolvedPath = resolve3(value);
2313
1892
  return platform === "win32" ? resolvedPath.toLowerCase() : resolvedPath;
2314
1893
  };
2315
1894
  var isPathInsideRoot = (candidate, root, platform) => {
@@ -2344,7 +1923,7 @@ var resolveCliEntrypoint = (deps = {}) => {
2344
1923
  let cliPath = null;
2345
1924
  let source = "module";
2346
1925
  if (resolved.argv1) {
2347
- const candidate = resolve4(resolved.argv1);
1926
+ const candidate = resolve3(resolved.argv1);
2348
1927
  if (exists(candidate)) {
2349
1928
  cliPath = candidate;
2350
1929
  source = "argv1";
@@ -2359,13 +1938,13 @@ var resolveCliEntrypoint = (deps = {}) => {
2359
1938
  const isTransient = isTransientCliPath(cliPath, resolved);
2360
1939
  return { nodePath, cliPath, args, command, source, isTransient };
2361
1940
  };
2362
- var getLaunchAgentPath = (home = homedir6()) => {
2363
- return join8(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
1941
+ var getLaunchAgentPath = (home = homedir4()) => {
1942
+ return join5(home, "Library", "LaunchAgents", `${MAC_LABEL}.plist`);
2364
1943
  };
2365
1944
  var buildLaunchAgentPlist = (entrypoint, options = {}) => {
2366
1945
  const label = options.label ?? MAC_LABEL;
2367
- const stdoutPath = options.stdoutPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.log");
2368
- const stderrPath = options.stderrPath ?? join8(homedir6(), "Library", "Logs", "opendevbrowser-daemon.err.log");
1946
+ const stdoutPath = options.stdoutPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.log");
1947
+ const stderrPath = options.stderrPath ?? join5(homedir4(), "Library", "Logs", "opendevbrowser-daemon.err.log");
2369
1948
  const programArgs = [entrypoint.nodePath, ...entrypoint.args].map((value) => ` <string>${value}</string>`).join("\n");
2370
1949
  return [
2371
1950
  '<?xml version="1.0" encoding="UTF-8"?>',
@@ -2570,10 +2149,10 @@ var installMacAutostart = (deps = {}) => {
2570
2149
  assertPersistentEntrypoint(entrypoint);
2571
2150
  const home = resolved.homedir();
2572
2151
  const plistPath = getLaunchAgentPath(home);
2573
- const stdoutPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.log");
2574
- const stderrPath = join8(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2575
- const logsDir = dirname5(stdoutPath);
2576
- resolved.mkdirSync(dirname5(plistPath), { recursive: true });
2152
+ const stdoutPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.log");
2153
+ const stderrPath = join5(home, "Library", "Logs", "opendevbrowser-daemon.err.log");
2154
+ const logsDir = dirname3(stdoutPath);
2155
+ resolved.mkdirSync(dirname3(plistPath), { recursive: true });
2577
2156
  resolved.mkdirSync(logsDir, { recursive: true });
2578
2157
  resolved.writeFileSync(
2579
2158
  plistPath,
@@ -2950,7 +2529,7 @@ async function runDaemonCommand(args) {
2950
2529
  }
2951
2530
 
2952
2531
  // src/cli/commands/artifacts.ts
2953
- import { join as join9, resolve as resolve5 } from "path";
2532
+ import { join as join6, resolve as resolve4 } from "path";
2954
2533
  import { tmpdir as tmpdir2 } from "os";
2955
2534
  var PASSTHROUGH_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
2956
2535
  "--with-config",
@@ -3039,7 +2618,7 @@ var parseArtifactsArgs = (rawArgs) => {
3039
2618
  };
3040
2619
  async function runArtifactsCommand(args) {
3041
2620
  const parsed = parseArtifactsArgs(args.rawArgs);
3042
- const rootDir = parsed.outputDir ? resolve5(parsed.outputDir) : join9(tmpdir2(), "opendevbrowser");
2621
+ const rootDir = parsed.outputDir ? resolve4(parsed.outputDir) : join6(tmpdir2(), "opendevbrowser");
3043
2622
  const cleaned = await cleanupExpiredArtifacts(rootDir);
3044
2623
  return {
3045
2624
  success: true,
@@ -3150,34 +2729,34 @@ function formatAutostartReconciliationMessage(result) {
3150
2729
  }
3151
2730
 
3152
2731
  // src/cli/commands/run.ts
3153
- import { readFileSync as readFileSync4 } from "fs";
2732
+ import { readFileSync as readFileSync2 } from "fs";
3154
2733
 
3155
2734
  // src/cli/output.ts
3156
2735
  var normalizeExitCode = (code) => {
3157
2736
  return Number.isInteger(code) ? Number(code) : 0;
3158
2737
  };
3159
2738
  var flushStream = (stream) => {
3160
- return new Promise((resolve6) => {
2739
+ return new Promise((resolve5) => {
3161
2740
  if (!stream || typeof stream.write !== "function") {
3162
- resolve6();
2741
+ resolve5();
3163
2742
  return;
3164
2743
  }
3165
2744
  try {
3166
- stream.write("", () => resolve6());
2745
+ stream.write("", () => resolve5());
3167
2746
  } catch {
3168
- resolve6();
2747
+ resolve5();
3169
2748
  }
3170
2749
  });
3171
2750
  };
3172
2751
  async function flushOutputAndExit(code, proc = process, timeoutMs = 250) {
3173
2752
  const finalCode = normalizeExitCode(code);
3174
2753
  proc.exitCode = finalCode;
3175
- await new Promise((resolve6) => {
2754
+ await new Promise((resolve5) => {
3176
2755
  let settled = false;
3177
2756
  const finish = () => {
3178
2757
  if (settled) return;
3179
2758
  settled = true;
3180
- resolve6();
2759
+ resolve5();
3181
2760
  };
3182
2761
  const timer = setTimeout(finish, Math.max(0, timeoutMs));
3183
2762
  timer.unref?.();
@@ -3299,13 +2878,13 @@ function parseRunArgs(rawArgs) {
3299
2878
  return parsed;
3300
2879
  }
3301
2880
  function readScriptFromStdin() {
3302
- return new Promise((resolve6, reject) => {
2881
+ return new Promise((resolve5, reject) => {
3303
2882
  let data = "";
3304
2883
  process.stdin.setEncoding("utf8");
3305
2884
  process.stdin.on("data", (chunk) => {
3306
2885
  data += chunk;
3307
2886
  });
3308
- process.stdin.on("end", () => resolve6(data));
2887
+ process.stdin.on("end", () => resolve5(data));
3309
2888
  process.stdin.on("error", reject);
3310
2889
  });
3311
2890
  }
@@ -3314,7 +2893,7 @@ async function runScriptCommand(args) {
3314
2893
  const outputOptions = { format: args.outputFormat, quiet: args.quiet };
3315
2894
  let scriptRaw = "";
3316
2895
  if (runArgs.scriptPath) {
3317
- scriptRaw = readFileSync4(runArgs.scriptPath, "utf-8");
2896
+ scriptRaw = readFileSync2(runArgs.scriptPath, "utf-8");
3318
2897
  } else if (!process.stdin.isTTY) {
3319
2898
  scriptRaw = await readScriptFromStdin();
3320
2899
  } else {
@@ -3539,16 +3118,16 @@ function promptYesNo(question, defaultYes) {
3539
3118
  return Promise.resolve(false);
3540
3119
  }
3541
3120
  const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
3542
- return new Promise((resolve6) => {
3121
+ return new Promise((resolve5) => {
3543
3122
  process.stdout.write(`${question}${suffix}`);
3544
3123
  process.stdin.setEncoding("utf8");
3545
3124
  process.stdin.once("data", (data) => {
3546
3125
  const input = data.toString().trim().toLowerCase();
3547
3126
  if (!input) {
3548
- resolve6(defaultYes);
3127
+ resolve5(defaultYes);
3549
3128
  return;
3550
3129
  }
3551
- resolve6(input === "y" || input === "yes");
3130
+ resolve5(input === "y" || input === "yes");
3552
3131
  });
3553
3132
  });
3554
3133
  }
@@ -3696,6 +3275,10 @@ var readFailures = (data) => {
3696
3275
  const failures = meta.failures;
3697
3276
  return Array.isArray(failures) ? failures.filter((entry) => Boolean(entry) && typeof entry === "object") : [];
3698
3277
  };
3278
+ var readFollowthroughSummary = (data) => {
3279
+ const record = asRecord(data);
3280
+ return readNonEmptyString(record?.followthroughSummary) ?? readNonEmptyString(readMeta(data)?.followthroughSummary);
3281
+ };
3699
3282
  var readSuggestedSteps = (data) => {
3700
3283
  const steps = asRecord(data)?.suggestedSteps;
3701
3284
  return Array.isArray(steps) ? steps.flatMap((step) => {
@@ -3737,6 +3320,13 @@ var buildWorkflowCompletionMessage = (workflowLabel, data) => {
3737
3320
  inferred.guidance?.recommendedNextCommands[0] ?? null
3738
3321
  );
3739
3322
  }
3323
+ const followthroughSummary = readFollowthroughSummary(data);
3324
+ if (followthroughSummary) {
3325
+ return buildNextStepMessage(
3326
+ `${workflowLabel} completed. ${followthroughSummary}`,
3327
+ readSuggestedNextAction(data) ?? readSuggestedStepReason(data)
3328
+ );
3329
+ }
3740
3330
  return `${workflowLabel} completed.`;
3741
3331
  };
3742
3332
 
@@ -4495,7 +4085,7 @@ async function runAnnotate(args) {
4495
4085
  }
4496
4086
 
4497
4087
  // src/cli/commands/canvas.ts
4498
- import { readFileSync as readFileSync5 } from "fs";
4088
+ import { readFileSync as readFileSync3 } from "fs";
4499
4089
  var DEFAULT_FEEDBACK_STREAM_TIMEOUT_MS = 3e4;
4500
4090
  var isRecord = (value) => {
4501
4091
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4696,7 +4286,7 @@ var resolveCanvasParams = (canvasArgs) => {
4696
4286
  if (hasParamsFile) {
4697
4287
  let raw = "";
4698
4288
  try {
4699
- raw = readFileSync5(canvasArgs.paramsFile ?? "", "utf8");
4289
+ raw = readFileSync3(canvasArgs.paramsFile ?? "", "utf8");
4700
4290
  } catch (error) {
4701
4291
  const message = error instanceof Error ? error.message : "Unable to read file";
4702
4292
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -4765,7 +4355,7 @@ async function runCanvas(args) {
4765
4355
  }
4766
4356
 
4767
4357
  // src/cli/commands/rpc.ts
4768
- import { readFileSync as readFileSync6 } from "fs";
4358
+ import { readFileSync as readFileSync4 } from "fs";
4769
4359
  var requireValue4 = (value, flag) => {
4770
4360
  if (!value) throw createUsageError(`Missing value for ${flag}`);
4771
4361
  return value;
@@ -4845,7 +4435,7 @@ var resolveRpcParams = (rpcArgs) => {
4845
4435
  if (hasParamsFile) {
4846
4436
  let raw = "";
4847
4437
  try {
4848
- raw = readFileSync6(rpcArgs.paramsFile ?? "", "utf8");
4438
+ raw = readFileSync4(rpcArgs.paramsFile ?? "", "utf8");
4849
4439
  } catch (error) {
4850
4440
  const message = error instanceof Error ? error.message : "Unable to read file";
4851
4441
  throw createUsageError(`Invalid --params-file: ${message}`);
@@ -6225,11 +5815,11 @@ function parseScreenshotArgs(rawArgs) {
6225
5815
  return parsed;
6226
5816
  }
6227
5817
  async function runScreenshot(args) {
6228
- const { sessionId, targetId, path: path8, ref, fullPage, timeoutMs } = parseScreenshotArgs(args.rawArgs);
5818
+ const { sessionId, targetId, path: path5, ref, fullPage, timeoutMs } = parseScreenshotArgs(args.rawArgs);
6229
5819
  if (!sessionId) throw createUsageError("Missing --session-id");
6230
5820
  const params = {
6231
5821
  sessionId,
6232
- ...typeof path8 === "string" ? { path: path8 } : {},
5822
+ ...typeof path5 === "string" ? { path: path5 } : {},
6233
5823
  ...typeof targetId === "string" ? { targetId } : {},
6234
5824
  ...typeof ref === "string" ? { ref } : {},
6235
5825
  ...fullPage === true ? { fullPage: true } : {}
@@ -6628,7 +6218,7 @@ async function runDesktopAccessibilitySnapshot(args) {
6628
6218
  }
6629
6219
 
6630
6220
  // src/cli/commands/session/cookie-import.ts
6631
- import { readFileSync as readFileSync7 } from "fs";
6221
+ import { readFileSync as readFileSync5 } from "fs";
6632
6222
  var requireValue5 = (value, flag) => {
6633
6223
  if (!value) {
6634
6224
  throw createUsageError(`Missing value for ${flag}`);
@@ -6743,7 +6333,7 @@ var resolveCookies = (parsed) => {
6743
6333
  }
6744
6334
  let raw = "";
6745
6335
  try {
6746
- raw = readFileSync7(parsed.cookiesFile ?? "", "utf8");
6336
+ raw = readFileSync5(parsed.cookiesFile ?? "", "utf8");
6747
6337
  } catch (error) {
6748
6338
  const message = error instanceof Error ? error.message : "Unable to read file";
6749
6339
  throw createUsageError(`Invalid --cookies-file: ${message}`);
@@ -6874,6 +6464,26 @@ var requireValue7 = (value, flag) => {
6874
6464
  }
6875
6465
  return value;
6876
6466
  };
6467
+ var asRecord2 = (value) => {
6468
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
6469
+ return null;
6470
+ }
6471
+ return value;
6472
+ };
6473
+ var hasExecutionBlocker = (result) => {
6474
+ const execution = asRecord2(asRecord2(result)?.execution);
6475
+ const meta = asRecord2(execution?.meta);
6476
+ return asRecord2(meta?.blocker) !== null;
6477
+ };
6478
+ var buildMacroResolveMessage = (execute, result) => {
6479
+ if (!execute) {
6480
+ return "Macro resolved.";
6481
+ }
6482
+ if (hasExecutionBlocker(result)) {
6483
+ return "Macro resolved, but execution is blocked and needs follow-up.";
6484
+ }
6485
+ return "Macro resolved and executed.";
6486
+ };
6877
6487
  var parseMacroResolveArgs = (rawArgs) => {
6878
6488
  const parsed = {};
6879
6489
  for (let index = 0; index < rawArgs.length; index += 1) {
@@ -6952,7 +6562,7 @@ async function runMacroResolve(args) {
6952
6562
  }) : await callDaemon("macro.resolve", params);
6953
6563
  return {
6954
6564
  success: true,
6955
- message: parsed.execute ? "Macro resolved and executed." : "Macro resolved.",
6565
+ message: buildMacroResolveMessage(parsed.execute ?? false, result),
6956
6566
  data: result
6957
6567
  };
6958
6568
  }
@@ -7759,7 +7369,7 @@ async function runInspiredesignCommand(args) {
7759
7369
  // package.json
7760
7370
  var package_default = {
7761
7371
  name: "opendevbrowser",
7762
- version: "0.0.20",
7372
+ version: "0.0.22",
7763
7373
  description: "Browser automation runtime with snapshot-refs-actions, browser replay screencasts, public read-only desktop observation, and browser-scoped computer-use orchestration",
7764
7374
  type: "module",
7765
7375
  main: "dist/index.js",
@@ -7770,6 +7380,7 @@ var package_default = {
7770
7380
  files: [
7771
7381
  "dist",
7772
7382
  "skills",
7383
+ "scripts/postinstall-sync-skills.mjs",
7773
7384
  "scripts/native",
7774
7385
  "extension/canvas.html",
7775
7386
  "extension/manifest.json",
@@ -7804,8 +7415,8 @@ var package_default = {
7804
7415
  node: ">=18"
7805
7416
  },
7806
7417
  scripts: {
7807
- 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",
7808
- dev: "tsup src/index.ts src/cli/index.ts src/skills/skill-loader.ts --format esm --dts --watch",
7418
+ 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",
7419
+ 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",
7809
7420
  lint: 'node scripts/run-package-tool.mjs eslint "src/**/*.ts" "tests/**/*.ts"',
7810
7421
  typecheck: "node scripts/run-package-tool.mjs tsc --noEmit -p tsconfig.json",
7811
7422
  test: "node scripts/run-vitest-coverage.mjs",
@@ -7820,7 +7431,8 @@ var package_default = {
7820
7431
  "extension:pack": "cd extension && zip -r ../opendevbrowser-extension.zip manifest.json popup.html canvas.html dist/ icons/",
7821
7432
  "extension:store": "node scripts/chrome-store-publish.mjs",
7822
7433
  "version:check": "node scripts/verify-versions.mjs",
7823
- prepack: "npm run version:check && npm run build && npm run extension:build"
7434
+ prepack: "npm run version:check && npm run build && npm run extension:build",
7435
+ postinstall: "node scripts/postinstall-sync-skills.mjs"
7824
7436
  },
7825
7437
  dependencies: {
7826
7438
  "@opencode-ai/plugin": "^1.2.25",
@@ -7848,8 +7460,10 @@ var package_default = {
7848
7460
  }
7849
7461
  };
7850
7462
 
7851
- // src/cli/index.ts
7852
- var VERSION = typeof package_default.version === "string" ? package_default.version : "0.0.0";
7463
+ // src/cli/update-skill-modes.ts
7464
+ function shouldRefreshManagedSkills(mode) {
7465
+ return hasManagedBundledSkillInstall(mode) || hasBundledSkillArtifacts(mode);
7466
+ }
7853
7467
  function resolveUpdateSkillModes(args) {
7854
7468
  if (args.rawArgs.includes("--no-skills")) {
7855
7469
  return [];
@@ -7863,22 +7477,102 @@ function resolveUpdateSkillModes(args) {
7863
7477
  if (args.mode) {
7864
7478
  return [args.mode];
7865
7479
  }
7866
- const installed = findInstalledConfigs();
7867
7480
  const modes = [];
7868
- if (installed.global || hasBundledSkillArtifacts("global")) {
7481
+ if (shouldRefreshManagedSkills("global")) {
7869
7482
  modes.push("global");
7870
7483
  }
7871
- if (installed.local || hasBundledSkillArtifacts("local")) {
7484
+ if (shouldRefreshManagedSkills("local")) {
7872
7485
  modes.push("local");
7873
7486
  }
7874
7487
  return modes;
7875
7488
  }
7489
+
7490
+ // src/cli/skill-lifecycle.ts
7491
+ function shouldSkipSkills(args) {
7492
+ return args.rawArgs.includes("--no-skills");
7493
+ }
7494
+ function getUpdateTargets(args, mode, deps) {
7495
+ if (deps.hasInstalledConfig(mode)) {
7496
+ return deps.getBundledSkillTargets(mode);
7497
+ }
7498
+ return deps.getBundledSkillLifecycleTargets(mode, {
7499
+ includeLegacyArtifacts: deps.hasBundledSkillArtifacts(mode)
7500
+ });
7501
+ }
7502
+ function collectUpdateSkillResults(args, deps, modes) {
7503
+ return modes.flatMap((mode) => {
7504
+ const targets = getUpdateTargets(args, mode, deps);
7505
+ return targets.length > 0 ? [deps.syncBundledSkillsForTargets(mode, targets)] : [];
7506
+ });
7507
+ }
7508
+ function getResolvedUpdateSkillModes(args, result, deps) {
7509
+ return result.success ? deps.resolveUpdateSkillModes(args) : [];
7510
+ }
7511
+ function buildUpdateSkillMessage(args, result, skillResults) {
7512
+ if (!result.success) {
7513
+ return "";
7514
+ }
7515
+ if (shouldSkipSkills(args)) {
7516
+ return "Managed skill refresh skipped (--no-skills).";
7517
+ }
7518
+ if (skillResults.length === 0) {
7519
+ return "No managed skill packs required refresh.";
7520
+ }
7521
+ return skillResults.map((entry) => entry.message).join("\n");
7522
+ }
7523
+ function buildUpdateCommandResult(args, result, deps) {
7524
+ const skillModes = getResolvedUpdateSkillModes(args, result, deps);
7525
+ const skillResults = result.success && !shouldSkipSkills(args) ? collectUpdateSkillResults(args, deps, skillModes) : [];
7526
+ const message = [result.message, buildUpdateSkillMessage(args, result, skillResults)].filter(Boolean).join("\n");
7527
+ return {
7528
+ success: result.success && skillResults.every((entry) => entry.success),
7529
+ message,
7530
+ data: {
7531
+ cacheCleared: result.cleared,
7532
+ skillModes,
7533
+ skills: skillResults
7534
+ }
7535
+ };
7536
+ }
7537
+ function buildUninstallSkillMessage(args, result, skillsResult) {
7538
+ if (!result.success) {
7539
+ return "";
7540
+ }
7541
+ if (shouldSkipSkills(args)) {
7542
+ return "Managed skill cleanup skipped (--no-skills).";
7543
+ }
7544
+ return skillsResult?.message ?? "No managed skill packs required cleanup.";
7545
+ }
7546
+ function getUninstallTargets(args, mode, result, deps) {
7547
+ if (!result.success || shouldSkipSkills(args)) {
7548
+ return [];
7549
+ }
7550
+ return deps.getBundledSkillLifecycleTargets(mode, {
7551
+ includeLegacyArtifacts: result.removed || deps.hasBundledSkillArtifacts(mode)
7552
+ });
7553
+ }
7554
+ function buildUninstallCommandResult(args, mode, result, deps) {
7555
+ const targets = getUninstallTargets(args, mode, result, deps);
7556
+ const skillsResult = result.success && !shouldSkipSkills(args) ? targets.length > 0 ? deps.removeBundledSkillsForTargets(mode, targets) : createNoOpSkillRemovalResult(mode) : void 0;
7557
+ const skillMessage = buildUninstallSkillMessage(args, result, skillsResult);
7558
+ return {
7559
+ success: result.success && (skillsResult?.success ?? true),
7560
+ message: [result.message, skillMessage].filter(Boolean).join("\n"),
7561
+ data: {
7562
+ config: result,
7563
+ skills: skillsResult
7564
+ }
7565
+ };
7566
+ }
7567
+
7568
+ // src/cli/index.ts
7569
+ var VERSION = typeof package_default.version === "string" ? package_default.version : "0.0.0";
7876
7570
  async function promptInstallMode() {
7877
7571
  if (!process.stdin.isTTY) {
7878
7572
  console.log("Non-interactive mode detected. Using global install.");
7879
7573
  return "global";
7880
7574
  }
7881
- return new Promise((resolve6) => {
7575
+ return new Promise((resolve5) => {
7882
7576
  console.log("\nWhere would you like to install opendevbrowser?\n");
7883
7577
  console.log(" 1. Global (~/.config/opencode/opencode.json)");
7884
7578
  console.log(" 2. Local (./opencode.json in this project)\n");
@@ -7898,23 +7592,23 @@ async function promptInstallMode() {
7898
7592
  resolved = true;
7899
7593
  const input = data.toString().trim();
7900
7594
  if (input === "2") {
7901
- resolve6("local");
7595
+ resolve5("local");
7902
7596
  } else {
7903
- resolve6("global");
7597
+ resolve5("global");
7904
7598
  }
7905
7599
  });
7906
7600
  process.stdin.once("close", () => {
7907
7601
  cleanup();
7908
7602
  if (resolved) return;
7909
7603
  resolved = true;
7910
- resolve6("global");
7604
+ resolve5("global");
7911
7605
  });
7912
7606
  timeoutId = setTimeout(() => {
7913
7607
  timeoutId = null;
7914
7608
  if (resolved) return;
7915
7609
  resolved = true;
7916
7610
  console.log("\nTimeout - using global install.");
7917
- resolve6("global");
7611
+ resolve5("global");
7918
7612
  }, 3e4);
7919
7613
  });
7920
7614
  }
@@ -7934,7 +7628,7 @@ async function promptUninstallMode() {
7934
7628
  console.log("Plugin found in both global and local configs. Use --global or --local flag.");
7935
7629
  return null;
7936
7630
  }
7937
- return new Promise((resolve6) => {
7631
+ return new Promise((resolve5) => {
7938
7632
  console.log("\nopendevbrowser is installed in multiple locations:\n");
7939
7633
  console.log(" 1. Global (~/.config/opencode/opencode.json)");
7940
7634
  console.log(" 2. Local (./opencode.json)");
@@ -7944,15 +7638,15 @@ async function promptUninstallMode() {
7944
7638
  process.stdin.once("data", (data) => {
7945
7639
  const input = data.toString().trim();
7946
7640
  if (input === "1") {
7947
- resolve6("global");
7641
+ resolve5("global");
7948
7642
  } else if (input === "2") {
7949
- resolve6("local");
7643
+ resolve5("local");
7950
7644
  } else {
7951
- resolve6(null);
7645
+ resolve5(null);
7952
7646
  }
7953
7647
  });
7954
7648
  process.stdin.once("close", () => {
7955
- resolve6(null);
7649
+ resolve5(null);
7956
7650
  });
7957
7651
  });
7958
7652
  }
@@ -8010,22 +7704,14 @@ async function main() {
8010
7704
  registerCommand({
8011
7705
  name: "update",
8012
7706
  description: "Clear cached plugin and refresh managed skill packs",
8013
- run: () => {
8014
- const result2 = runUpdate();
8015
- const skillModes = result2.success ? resolveUpdateSkillModes(args) : [];
8016
- const skillResults = result2.success ? skillModes.map((mode) => syncBundledSkills(mode)) : [];
8017
- 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." : "";
8018
- const message = [result2.message, skillMessage].filter(Boolean).join("\n");
8019
- return {
8020
- success: result2.success && skillResults.every((entry) => entry.success),
8021
- message,
8022
- data: {
8023
- cacheCleared: result2.cleared,
8024
- skillModes,
8025
- skills: skillResults
8026
- }
8027
- };
8028
- }
7707
+ run: () => buildUpdateCommandResult(args, runUpdate(), {
7708
+ resolveUpdateSkillModes,
7709
+ hasInstalledConfig,
7710
+ hasBundledSkillArtifacts,
7711
+ getBundledSkillTargets,
7712
+ getBundledSkillLifecycleTargets,
7713
+ syncBundledSkillsForTargets
7714
+ })
8029
7715
  });
8030
7716
  registerCommand({
8031
7717
  name: "uninstall",
@@ -8041,18 +7727,11 @@ async function main() {
8041
7727
  if (!mode) {
8042
7728
  return { success: false, message: "Error: Please specify --global or --local for uninstall.", exitCode: EXIT_USAGE };
8043
7729
  }
8044
- const result2 = runUninstall(mode);
8045
- const skipSkills = args.rawArgs.includes("--no-skills");
8046
- const skillsResult = result2.success && !skipSkills ? removeBundledSkills(mode) : void 0;
8047
- const skillMessage = skipSkills ? "Managed skill cleanup skipped (--no-skills)." : skillsResult?.message ?? "";
8048
- return {
8049
- success: result2.success && (skillsResult?.success ?? true),
8050
- message: [result2.message, skillMessage].filter(Boolean).join("\n"),
8051
- data: {
8052
- config: result2,
8053
- skills: skillsResult
8054
- }
8055
- };
7730
+ return buildUninstallCommandResult(args, mode, runUninstall(mode), {
7731
+ hasBundledSkillArtifacts,
7732
+ getBundledSkillLifecycleTargets,
7733
+ removeBundledSkillsForTargets
7734
+ });
8056
7735
  }
8057
7736
  });
8058
7737
  registerCommand({