@fro.bot/systematic 2.14.0 → 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.
- package/README.md +1 -1
- package/dist/index.js +83 -49
- package/package.json +3 -3
- package/skills/using-systematic/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -402,7 +402,7 @@ The plugin exposes one tool to OpenCode:
|
|
|
402
402
|
|------|-------------|
|
|
403
403
|
| `systematic_skill` | Load Systematic bundled skills by name. Lists available skills in its description and returns formatted skill content when invoked. |
|
|
404
404
|
|
|
405
|
-
For non-Systematic skills (project or user-level), use OpenCode's
|
|
405
|
+
For non-Systematic skills (project or user-level), use OpenCode's `skill` tool.
|
|
406
406
|
|
|
407
407
|
## How It Works
|
|
408
408
|
|
package/dist/index.js
CHANGED
|
@@ -188,22 +188,15 @@ var applyBootstrapContent = (output, content) => {
|
|
|
188
188
|
|
|
189
189
|
${content}` : content;
|
|
190
190
|
};
|
|
191
|
-
function
|
|
192
|
-
return `**
|
|
193
|
-
|
|
194
|
-
-
|
|
195
|
-
- \`Task\` tool with subagents \u2192 Use OpenCode's subagent system (@mention)
|
|
196
|
-
- \`Skill\` tool \u2192 OpenCode's native \`skill\` tool
|
|
197
|
-
- \`SystematicSkill\` tool \u2192 \`systematic_skill\` (Systematic plugin skills)
|
|
198
|
-
- \`Read\`, \`Write\`, \`Edit\`, \`Bash\` \u2192 Your native tools
|
|
199
|
-
|
|
200
|
-
**Skills naming:**
|
|
201
|
-
- Bundled skills use the \`systematic:\` prefix (e.g., \`systematic:brainstorming\`)
|
|
191
|
+
function getSkillUsageTemplate() {
|
|
192
|
+
return `**Skills naming:**
|
|
193
|
+
- Systematic bundled skills use the \`systematic:\` prefix (e.g., \`systematic:setup\`)
|
|
194
|
+
- Workflow skills with their own namespace keep it (e.g., \`ce:brainstorm\`)
|
|
202
195
|
- Skills can also be invoked without prefix if unambiguous
|
|
203
196
|
|
|
204
197
|
**Skills usage:**
|
|
205
198
|
- Use \`systematic_skill\` to load Systematic bundled skills
|
|
206
|
-
- Use the
|
|
199
|
+
- Use the \`skill\` tool for non-Systematic skills
|
|
207
200
|
|
|
208
201
|
**Skills location:**
|
|
209
202
|
Bundled skills ship with the Systematic plugin and are discoverable via \`systematic_skill\`.`;
|
|
@@ -224,7 +217,7 @@ function getBootstrapContent(config, deps) {
|
|
|
224
217
|
const fullContent = fs.readFileSync(usingSystematicPath, "utf8");
|
|
225
218
|
const { body } = parseFrontmatter(fullContent);
|
|
226
219
|
const content = body.trim();
|
|
227
|
-
const
|
|
220
|
+
const skillUsage = getSkillUsageTemplate();
|
|
228
221
|
const catalog = renderCatalogVerbose({
|
|
229
222
|
bundledSkillsDir,
|
|
230
223
|
disabledSkills: config.disabled_skills
|
|
@@ -233,13 +226,13 @@ function getBootstrapContent(config, deps) {
|
|
|
233
226
|
|
|
234
227
|
${catalog}` : "";
|
|
235
228
|
return `<SYSTEMATIC_WORKFLOWS>
|
|
236
|
-
You have access to structured engineering workflows via the
|
|
229
|
+
You have access to structured engineering workflows via the Systematic plugin.
|
|
237
230
|
|
|
238
231
|
**IMPORTANT: The using-systematic skill content is included below. It is ALREADY LOADED - you are currently following it. Do NOT use the systematic_skill tool to load "using-systematic" again - that would be redundant.**
|
|
239
232
|
|
|
240
233
|
${content}
|
|
241
234
|
|
|
242
|
-
${
|
|
235
|
+
${skillUsage}${catalogSection}
|
|
243
236
|
</SYSTEMATIC_WORKFLOWS>`;
|
|
244
237
|
}
|
|
245
238
|
|
|
@@ -781,6 +774,28 @@ async function getAvailableModels(client, options = {}) {
|
|
|
781
774
|
}
|
|
782
775
|
|
|
783
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
|
+
}
|
|
784
799
|
function toTitleCase(name) {
|
|
785
800
|
return name.split("-").map((segment) => segment.length > 0 ? segment.charAt(0).toUpperCase() + segment.slice(1) : segment).join("-");
|
|
786
801
|
}
|
|
@@ -919,30 +934,37 @@ function applyAgentOverlays(config, agentInfo, overlays, availabilitySet) {
|
|
|
919
934
|
addPermissionRules(permissionRules, config.permission);
|
|
920
935
|
}
|
|
921
936
|
result.temperature = inferBuiltInTemperature(agentInfo.name, result.description);
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
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;
|
|
936
952
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
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;
|
|
944
967
|
}
|
|
945
|
-
return result;
|
|
946
968
|
}
|
|
947
969
|
function overlayControlsPermission(overlay) {
|
|
948
970
|
return overlay !== undefined && (Object.hasOwn(overlay, "permission") || Object.hasOwn(overlay, "skills"));
|
|
@@ -1062,6 +1084,7 @@ function createConfigHandler(deps) {
|
|
|
1062
1084
|
const { config: systematicConfig, overlays } = loadConfigWithSources(directory);
|
|
1063
1085
|
const existingAgents = { ...config.agent ?? {} };
|
|
1064
1086
|
const existingCommands = { ...config.command ?? {} };
|
|
1087
|
+
const nativeAgents = Object.fromEntries(Object.entries(existingAgents).filter(([, agent]) => !isSystematicAgentConfig(agent)));
|
|
1065
1088
|
const bundledSkills = collectSkillsAsCommands(bundledSkillsDir, systematicConfig.disabled_skills);
|
|
1066
1089
|
const enabledSkillNames = collectEnabledSkillNames(bundledSkillsDir, systematicConfig.disabled_skills);
|
|
1067
1090
|
const inventory = buildBundledAgentInventory(bundledAgentsDir2, systematicConfig.disabled_agents);
|
|
@@ -1069,35 +1092,46 @@ function createConfigHandler(deps) {
|
|
|
1069
1092
|
const validatedOverlays = validateAgentOverlays({
|
|
1070
1093
|
inventory,
|
|
1071
1094
|
overlays,
|
|
1072
|
-
nativeAgents
|
|
1095
|
+
nativeAgents,
|
|
1073
1096
|
enabledSkills: enabledSkillNames
|
|
1074
1097
|
});
|
|
1075
1098
|
const resolvedOverlays = resolveAgentOverlaySet(validatedOverlays);
|
|
1076
1099
|
const availability = deps.client ? await getAvailableModels(deps.client) : undefined;
|
|
1077
1100
|
const availabilitySet = availability && availability.status !== "unknown" ? availability.models : undefined;
|
|
1078
|
-
const bundledAgents = collectAgents(bundledAgentsDir2, systematicConfig.disabled_agents,
|
|
1101
|
+
const bundledAgents = collectAgents(bundledAgentsDir2, systematicConfig.disabled_agents, nativeAgents, resolvedOverlays, availabilitySet);
|
|
1079
1102
|
const bundledCommands = collectCommands(bundledCommandsDir, systematicConfig.disabled_commands);
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
config.command =
|
|
1085
|
-
...bundledCommands,
|
|
1086
|
-
...bundledSkills,
|
|
1087
|
-
...existingCommands
|
|
1088
|
-
};
|
|
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)));
|
|
1089
1108
|
registerSkillsPaths(config, bundledSkillsDir);
|
|
1090
1109
|
};
|
|
1091
1110
|
}
|
|
1092
1111
|
function registerSkillsPaths(config, skillsDir) {
|
|
1093
1112
|
const extended = config;
|
|
1094
1113
|
const paths = extended.skills?.paths ?? [];
|
|
1095
|
-
const nextPaths = paths
|
|
1114
|
+
const nextPaths = removeSystematicSkillPaths(paths);
|
|
1115
|
+
if (!nextPaths.includes(skillsDir))
|
|
1116
|
+
nextPaths.push(skillsDir);
|
|
1096
1117
|
extended.skills = {
|
|
1097
1118
|
...extended.skills,
|
|
1098
1119
|
paths: nextPaths
|
|
1099
1120
|
};
|
|
1100
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
|
+
}
|
|
1101
1135
|
|
|
1102
1136
|
// src/lib/skill-tool.ts
|
|
1103
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.
|
|
3
|
+
"version": "2.14.2",
|
|
4
4
|
"description": "Structured engineering workflows for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@biomejs/biome": "2.4.15",
|
|
67
|
-
"@opencode-ai/plugin": "1.14.
|
|
68
|
-
"@opencode-ai/sdk": "1.14.
|
|
67
|
+
"@opencode-ai/plugin": "1.14.48",
|
|
68
|
+
"@opencode-ai/sdk": "1.14.48",
|
|
69
69
|
"@types/bun": "latest",
|
|
70
70
|
"@types/js-yaml": "4.0.9",
|
|
71
71
|
"@types/node": "24.12.3",
|
|
@@ -13,7 +13,7 @@ This is not negotiable. This is not optional. You cannot rationalize your way ou
|
|
|
13
13
|
|
|
14
14
|
## How to Access Skills
|
|
15
15
|
|
|
16
|
-
Use the `systematic_skill` tool for Systematic bundled skills. Use the
|
|
16
|
+
Use the `systematic_skill` tool for Systematic bundled skills. Use the `skill` tool for non-Systematic skills. When you invoke a skill, its content is loaded and presented to you—follow it directly.
|
|
17
17
|
|
|
18
18
|
# Using Skills
|
|
19
19
|
|
|
@@ -94,4 +94,4 @@ Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.
|
|
|
94
94
|
|
|
95
95
|
## Skill Resolution
|
|
96
96
|
|
|
97
|
-
Systematic bundled skills are listed in the `systematic_skill` tool description. Use the
|
|
97
|
+
Systematic bundled skills are listed in the `systematic_skill` tool description. Use the `skill` tool for skills outside the Systematic plugin.
|