@fro.bot/systematic 2.14.1 → 2.14.2

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 (2) hide show
  1. package/dist/index.js +75 -34
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -774,6 +774,28 @@ async function getAvailableModels(client, options = {}) {
774
774
  }
775
775
 
776
776
  // src/lib/config-handler.ts
777
+ function isSystematicAgentConfig(agent) {
778
+ const description = agent?.description;
779
+ return typeof description === "string" && /\(.* - Systematic\)$/.test(description);
780
+ }
781
+ function isSystematicCommandConfig(command) {
782
+ const description = command?.description;
783
+ return typeof description === "string" && (description.startsWith("(Systematic) ") || description.startsWith("(Systematic - Skill) "));
784
+ }
785
+ function mergeSystematicEntries(existing, emitted, shouldDropExisting) {
786
+ const merged = { ...existing ?? {} };
787
+ for (const [key, value] of Object.entries(existing ?? {})) {
788
+ if (shouldDropExisting(key, value)) {
789
+ delete merged[key];
790
+ }
791
+ }
792
+ for (const [key, value] of Object.entries(emitted)) {
793
+ if (Object.hasOwn(merged, key))
794
+ continue;
795
+ merged[key] = value;
796
+ }
797
+ return merged;
798
+ }
777
799
  function toTitleCase(name) {
778
800
  return name.split("-").map((segment) => segment.length > 0 ? segment.charAt(0).toUpperCase() + segment.slice(1) : segment).join("-");
779
801
  }
@@ -912,30 +934,37 @@ function applyAgentOverlays(config, agentInfo, overlays, availabilitySet) {
912
934
  addPermissionRules(permissionRules, config.permission);
913
935
  }
914
936
  result.temperature = inferBuiltInTemperature(agentInfo.name, result.description);
915
- if (agentInfo.category && availabilitySet !== undefined) {
916
- const resolved = resolveSourceModel(agentInfo.category, availabilitySet);
917
- result.model = `${resolved.provider}/${resolved.model}`;
918
- if (resolved.variant !== undefined) {
919
- result.variant = resolved.variant;
920
- } else {
921
- delete result.variant;
922
- }
923
- }
924
- if (categoryOverlay) {
925
- applyOverlayObjectWithVariantClearing(result, categoryOverlay.value, permissionRules);
926
- }
927
- if (exactOverlay) {
928
- applyOverlayObjectWithVariantClearing(result, exactOverlay.value, permissionRules);
937
+ applySourceModelDefault(result, agentInfo, availabilitySet);
938
+ applyAgentOverlay(result, categoryOverlay?.value, permissionRules);
939
+ applyAgentOverlay(result, exactOverlay?.value, permissionRules);
940
+ applyPermissionOverlay(result, permissionRules, hasPermissionOverlay);
941
+ return result;
942
+ }
943
+ function applySourceModelDefault(target, agentInfo, availabilitySet) {
944
+ if (!agentInfo.category || availabilitySet === undefined)
945
+ return;
946
+ const resolved = resolveSourceModel(agentInfo.category, availabilitySet);
947
+ target.model = `${resolved.provider}/${resolved.model}`;
948
+ if (resolved.variant !== undefined) {
949
+ target.variant = resolved.variant;
950
+ } else {
951
+ delete target.variant;
929
952
  }
930
- if (hasPermissionOverlay) {
931
- const permission = permissionFromRules(permissionRules);
932
- if (permission) {
933
- result.permission = permission;
934
- } else {
935
- delete result.permission;
936
- }
953
+ }
954
+ function applyAgentOverlay(target, overlay, permissionRules) {
955
+ if (overlay === undefined)
956
+ return;
957
+ applyOverlayObjectWithVariantClearing(target, overlay, permissionRules);
958
+ }
959
+ function applyPermissionOverlay(target, permissionRules, hasPermissionOverlay) {
960
+ if (!hasPermissionOverlay)
961
+ return;
962
+ const permission = permissionFromRules(permissionRules);
963
+ if (permission) {
964
+ target.permission = permission;
965
+ } else {
966
+ delete target.permission;
937
967
  }
938
- return result;
939
968
  }
940
969
  function overlayControlsPermission(overlay) {
941
970
  return overlay !== undefined && (Object.hasOwn(overlay, "permission") || Object.hasOwn(overlay, "skills"));
@@ -1055,6 +1084,7 @@ function createConfigHandler(deps) {
1055
1084
  const { config: systematicConfig, overlays } = loadConfigWithSources(directory);
1056
1085
  const existingAgents = { ...config.agent ?? {} };
1057
1086
  const existingCommands = { ...config.command ?? {} };
1087
+ const nativeAgents = Object.fromEntries(Object.entries(existingAgents).filter(([, agent]) => !isSystematicAgentConfig(agent)));
1058
1088
  const bundledSkills = collectSkillsAsCommands(bundledSkillsDir, systematicConfig.disabled_skills);
1059
1089
  const enabledSkillNames = collectEnabledSkillNames(bundledSkillsDir, systematicConfig.disabled_skills);
1060
1090
  const inventory = buildBundledAgentInventory(bundledAgentsDir2, systematicConfig.disabled_agents);
@@ -1062,35 +1092,46 @@ function createConfigHandler(deps) {
1062
1092
  const validatedOverlays = validateAgentOverlays({
1063
1093
  inventory,
1064
1094
  overlays,
1065
- nativeAgents: existingAgents,
1095
+ nativeAgents,
1066
1096
  enabledSkills: enabledSkillNames
1067
1097
  });
1068
1098
  const resolvedOverlays = resolveAgentOverlaySet(validatedOverlays);
1069
1099
  const availability = deps.client ? await getAvailableModels(deps.client) : undefined;
1070
1100
  const availabilitySet = availability && availability.status !== "unknown" ? availability.models : undefined;
1071
- const bundledAgents = collectAgents(bundledAgentsDir2, systematicConfig.disabled_agents, existingAgents, resolvedOverlays, availabilitySet);
1101
+ const bundledAgents = collectAgents(bundledAgentsDir2, systematicConfig.disabled_agents, nativeAgents, resolvedOverlays, availabilitySet);
1072
1102
  const bundledCommands = collectCommands(bundledCommandsDir, systematicConfig.disabled_commands);
1073
- config.agent = {
1074
- ...bundledAgents,
1075
- ...existingAgents
1076
- };
1077
- config.command = {
1078
- ...bundledCommands,
1079
- ...bundledSkills,
1080
- ...existingCommands
1081
- };
1103
+ const bundledAgentKeys = new Set(Object.keys(bundledAgents));
1104
+ config.agent = mergeSystematicEntries(existingAgents, bundledAgents, (key, agent) => bundledAgentKeys.has(key) && isSystematicAgentConfig(agent));
1105
+ const emittedCommands = { ...bundledCommands, ...bundledSkills };
1106
+ const emittedCommandKeys = new Set(Object.keys(emittedCommands));
1107
+ config.command = mergeSystematicEntries(existingCommands, emittedCommands, (key, command) => isSystematicCommandConfig(command) && (emittedCommandKeys.has(key) || isSystematicOwnedCommandKey(key)));
1082
1108
  registerSkillsPaths(config, bundledSkillsDir);
1083
1109
  };
1084
1110
  }
1085
1111
  function registerSkillsPaths(config, skillsDir) {
1086
1112
  const extended = config;
1087
1113
  const paths = extended.skills?.paths ?? [];
1088
- const nextPaths = paths.includes(skillsDir) ? [...paths] : [...paths, skillsDir];
1114
+ const nextPaths = removeSystematicSkillPaths(paths);
1115
+ if (!nextPaths.includes(skillsDir))
1116
+ nextPaths.push(skillsDir);
1089
1117
  extended.skills = {
1090
1118
  ...extended.skills,
1091
1119
  paths: nextPaths
1092
1120
  };
1093
1121
  }
1122
+ function removeSystematicSkillPaths(paths) {
1123
+ return paths.filter((path6) => !isSystematicSkillPath(path6));
1124
+ }
1125
+ function isSystematicSkillPath(path6) {
1126
+ const normalizedPath = normalizePath(path6);
1127
+ return normalizedPath.endsWith("/.config/opencode/systematic/skills") || normalizedPath.endsWith("/.cache/opencode/systematic/skills") || normalizedPath.endsWith("/.local/share/opencode/systematic/skills") || normalizedPath.endsWith("/.opencode/systematic/skills") || /(?:^|\/)\.cache\/opencode\/packages\/@fro\.bot\/systematic@[^/]+\/node_modules\/@fro\.bot\/systematic\/skills(?:$|\/)/u.test(normalizedPath);
1128
+ }
1129
+ function normalizePath(path6) {
1130
+ return path6.replaceAll("\\", "/").replace(/\/+$/u, "");
1131
+ }
1132
+ function isSystematicOwnedCommandKey(key) {
1133
+ return key.startsWith("systematic:") || key.startsWith("ce:");
1134
+ }
1094
1135
 
1095
1136
  // src/lib/skill-tool.ts
1096
1137
  import fs4 from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fro.bot/systematic",
3
- "version": "2.14.1",
3
+ "version": "2.14.2",
4
4
  "description": "Structured engineering workflows for OpenCode",
5
5
  "type": "module",
6
6
  "homepage": "https://fro.bot/systematic",