@fro.bot/systematic 2.18.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/dist/{index-ek6rskkw.js → index-6evyges6.js} +17 -0
- package/dist/index.js +24 -2
- package/dist/lib/skill-loader.d.ts +2 -1
- package/dist/lib/skills.d.ts +9 -1
- package/package.json +3 -3
- package/skills/claude-permissions-optimizer/SKILL.md +4 -0
- package/skills/orchestrating-swarms/SKILL.md +5 -0
- package/skills/using-systematic/SKILL.md +14 -0
package/dist/cli.js
CHANGED
|
@@ -500,6 +500,21 @@ function extractFrontmatter(filePath) {
|
|
|
500
500
|
metadata = Object.fromEntries(entries);
|
|
501
501
|
}
|
|
502
502
|
}
|
|
503
|
+
const deprecatedRaw = data.deprecated;
|
|
504
|
+
let deprecated;
|
|
505
|
+
if (isRecord(deprecatedRaw)) {
|
|
506
|
+
const since = typeof deprecatedRaw.since === "string" && deprecatedRaw.since !== "" ? deprecatedRaw.since : undefined;
|
|
507
|
+
const removal = typeof deprecatedRaw.removal === "string" && deprecatedRaw.removal !== "" ? deprecatedRaw.removal : undefined;
|
|
508
|
+
if (since !== undefined && removal !== undefined) {
|
|
509
|
+
deprecated = { since, removal };
|
|
510
|
+
if (typeof deprecatedRaw.replacement === "string") {
|
|
511
|
+
deprecated.replacement = deprecatedRaw.replacement;
|
|
512
|
+
}
|
|
513
|
+
if (typeof deprecatedRaw.reason === "string") {
|
|
514
|
+
deprecated.reason = deprecatedRaw.reason;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
503
518
|
const argumentHintRaw = extractNonEmptyString(data, "argument-hint");
|
|
504
519
|
const argumentHint = argumentHintRaw?.replace(/^["']|["']$/g, "") || undefined;
|
|
505
520
|
return {
|
|
@@ -508,6 +523,7 @@ function extractFrontmatter(filePath) {
|
|
|
508
523
|
license: extractNonEmptyString(data, "license"),
|
|
509
524
|
compatibility: extractNonEmptyString(data, "compatibility"),
|
|
510
525
|
metadata,
|
|
526
|
+
deprecated,
|
|
511
527
|
disableModelInvocation: extractBoolean(data, "disable-model-invocation"),
|
|
512
528
|
userInvocable: extractBoolean(data, "user-invocable"),
|
|
513
529
|
subtask: data.context === "fork" ? true : extractBoolean(data, "subtask") ?? undefined,
|
|
@@ -538,6 +554,7 @@ function findSkillsInDir(dir, maxDepth = 3) {
|
|
|
538
554
|
license: frontmatter.license,
|
|
539
555
|
compatibility: frontmatter.compatibility,
|
|
540
556
|
metadata: frontmatter.metadata,
|
|
557
|
+
deprecated: frontmatter.deprecated,
|
|
541
558
|
disableModelInvocation: frontmatter.disableModelInvocation,
|
|
542
559
|
userInvocable: frontmatter.userInvocable,
|
|
543
560
|
subtask: frontmatter.subtask,
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
loadConfig,
|
|
14
14
|
loadConfigWithSources,
|
|
15
15
|
parseFrontmatter
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-6evyges6.js";
|
|
17
17
|
|
|
18
18
|
// src/index.ts
|
|
19
19
|
import fs5 from "fs";
|
|
@@ -81,7 +81,8 @@ function loadSkill(skillInfo) {
|
|
|
81
81
|
subtask: skillInfo.subtask,
|
|
82
82
|
agent: skillInfo.agent,
|
|
83
83
|
model: skillInfo.model,
|
|
84
|
-
argumentHint: skillInfo.argumentHint
|
|
84
|
+
argumentHint: skillInfo.argumentHint,
|
|
85
|
+
deprecated: skillInfo.deprecated
|
|
85
86
|
};
|
|
86
87
|
} catch {
|
|
87
88
|
return null;
|
|
@@ -1186,8 +1187,25 @@ function discoverSkillFiles(dir, limit = 10) {
|
|
|
1186
1187
|
return files.map((file) => ` <file>${file}</file>`).join(`
|
|
1187
1188
|
`);
|
|
1188
1189
|
}
|
|
1190
|
+
function formatDeprecationMessage(name, deprecated) {
|
|
1191
|
+
let msg = `[systematic] skill "${name}" is deprecated since ${deprecated.since}; will be removed in ${deprecated.removal}.`;
|
|
1192
|
+
if (deprecated.replacement) {
|
|
1193
|
+
msg += ` Replacement: ${deprecated.replacement}`;
|
|
1194
|
+
if (!deprecated.replacement.endsWith(".")) {
|
|
1195
|
+
msg += ".";
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
if (deprecated.reason) {
|
|
1199
|
+
msg += ` Reason: ${deprecated.reason}`;
|
|
1200
|
+
if (!deprecated.reason.endsWith(".")) {
|
|
1201
|
+
msg += ".";
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
return msg;
|
|
1205
|
+
}
|
|
1189
1206
|
function createSkillTool(options) {
|
|
1190
1207
|
const { bundledSkillsDir, disabledSkills } = options;
|
|
1208
|
+
const warnedSkills = new Set;
|
|
1191
1209
|
const getAllSkills = () => {
|
|
1192
1210
|
return findSkillsInDir(bundledSkillsDir).filter((s) => !disabledSkills.includes(s.name)).map((skillInfo) => loadSkill(skillInfo)).filter((s) => s !== null).sort((a, b) => a.name.localeCompare(b.name));
|
|
1193
1211
|
};
|
|
@@ -1238,6 +1256,10 @@ ${catalog}`;
|
|
|
1238
1256
|
}).map((s) => s.prefixedName);
|
|
1239
1257
|
throw new Error(`Skill "${requestedName}" not found. Available systematic skills: ${availableSystematic.join(", ")}`);
|
|
1240
1258
|
}
|
|
1259
|
+
if (matchedSkill.deprecated && !warnedSkills.has(matchedSkill.name)) {
|
|
1260
|
+
console.warn(formatDeprecationMessage(matchedSkill.name, matchedSkill.deprecated));
|
|
1261
|
+
warnedSkills.add(matchedSkill.name);
|
|
1262
|
+
}
|
|
1241
1263
|
const body = extractSkillBody(matchedSkill.wrappedTemplate);
|
|
1242
1264
|
const dir = path6.dirname(matchedSkill.skillFile);
|
|
1243
1265
|
const base = pathToFileURL2(dir).href;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SkillInfo } from './skills.js';
|
|
1
|
+
import type { SkillDeprecated, SkillInfo } from './skills.js';
|
|
2
2
|
export interface LoadedSkill {
|
|
3
3
|
name: string;
|
|
4
4
|
prefixedName: string;
|
|
@@ -12,6 +12,7 @@ export interface LoadedSkill {
|
|
|
12
12
|
agent?: string;
|
|
13
13
|
model?: string;
|
|
14
14
|
argumentHint?: string;
|
|
15
|
+
deprecated?: SkillDeprecated;
|
|
15
16
|
}
|
|
16
17
|
export declare function formatSkillCommandName(name: string): string;
|
|
17
18
|
export declare function formatSkillDescription(description: string, fallbackName: string): string;
|
package/dist/lib/skills.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
export interface SkillDeprecated {
|
|
2
|
+
since: string;
|
|
3
|
+
removal: string;
|
|
4
|
+
replacement?: string;
|
|
5
|
+
reason?: string;
|
|
6
|
+
}
|
|
1
7
|
export interface SkillFrontmatter {
|
|
2
8
|
name: string;
|
|
3
9
|
description: string;
|
|
4
10
|
license?: string;
|
|
5
11
|
compatibility?: string;
|
|
6
12
|
metadata?: Record<string, string>;
|
|
13
|
+
deprecated?: SkillDeprecated;
|
|
7
14
|
disableModelInvocation?: boolean;
|
|
8
15
|
userInvocable?: boolean;
|
|
9
16
|
subtask?: boolean;
|
|
@@ -20,6 +27,7 @@ export interface SkillInfo {
|
|
|
20
27
|
license?: string;
|
|
21
28
|
compatibility?: string;
|
|
22
29
|
metadata?: Record<string, string>;
|
|
30
|
+
deprecated?: SkillDeprecated;
|
|
23
31
|
disableModelInvocation?: boolean;
|
|
24
32
|
userInvocable?: boolean;
|
|
25
33
|
subtask?: boolean;
|
|
@@ -28,6 +36,6 @@ export interface SkillInfo {
|
|
|
28
36
|
argumentHint?: string;
|
|
29
37
|
allowedTools?: string;
|
|
30
38
|
}
|
|
31
|
-
export declare const SKILL_FRONTMATTER_FIELDS: readonly ["name", "description", "argument-hint", "disable-model-invocation", "allowed-tools", "license", "compatibility", "metadata", "user-invocable", "agent", "model", "context", "subtask"];
|
|
39
|
+
export declare const SKILL_FRONTMATTER_FIELDS: readonly ["name", "description", "argument-hint", "disable-model-invocation", "allowed-tools", "license", "compatibility", "metadata", "deprecated", "user-invocable", "agent", "model", "context", "subtask"];
|
|
32
40
|
export declare function extractFrontmatter(filePath: string): SkillFrontmatter;
|
|
33
41
|
export declare function findSkillsInDir(dir: string, maxDepth?: number): SkillInfo[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"description": "Structured engineering workflows for OpenCode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@biomejs/biome": "2.4.15",
|
|
68
|
-
"@opencode-ai/plugin": "1.
|
|
69
|
-
"@opencode-ai/sdk": "1.
|
|
68
|
+
"@opencode-ai/plugin": "1.15.0",
|
|
69
|
+
"@opencode-ai/sdk": "1.15.0",
|
|
70
70
|
"@types/bun": "latest",
|
|
71
71
|
"@types/js-yaml": "4.0.9",
|
|
72
72
|
"@types/node": "24.12.4",
|
|
@@ -3,6 +3,10 @@ name: claude-permissions-optimizer
|
|
|
3
3
|
context: fork
|
|
4
4
|
description: Optimize Claude Code permissions by finding safe Bash commands from session history and auto-applying them to settings.json. Can run from any coding agent but targets Claude Code specifically. Use when experiencing permission fatigue, too many permission prompts, wanting to optimize permissions, or needing to set up allowlists. Triggers on "optimize permissions", "reduce permission prompts", "allowlist commands", "too many permission prompts", "permission fatigue", "permission setup", or complaints about clicking approve too often.
|
|
5
5
|
subtask: true
|
|
6
|
+
deprecated:
|
|
7
|
+
since: v2.19.0
|
|
8
|
+
removal: v3.0.0
|
|
9
|
+
reason: "Targets Claude Code's ~/.claude/settings.json allowlist mechanism. OpenCode uses a SQLite-backed session-scoped permission model with built-in always-persistence via the always button. No prompt-fatigue analog exists in OpenCode; there is no equivalent allowlist to optimize."
|
|
6
10
|
---
|
|
7
11
|
|
|
8
12
|
# Claude Permissions Optimizer
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
name: orchestrating-swarms
|
|
3
3
|
description: Use when orchestrating multi-agent swarms, coordinating parallel code reviews, creating pipeline workflows with dependencies, building self-organizing task queues, or any task benefiting from divide-and-conquer patterns.
|
|
4
4
|
disable-model-invocation: true
|
|
5
|
+
deprecated:
|
|
6
|
+
since: v2.19.0
|
|
7
|
+
removal: v3.0.0
|
|
8
|
+
replacement: orchestrating-subagents
|
|
9
|
+
reason: "Documents the CEP Teammate API (TeammateTool, spawnTeam, TaskCreate/TaskList/TaskGet, team_name parameter on task(), CC subagent_type names). These primitives do not exist in OpenCode. A replacement skill will cover OpenCode-native parallel task() orchestration."
|
|
5
10
|
---
|
|
6
11
|
|
|
7
12
|
# Swarm Orchestration
|
|
@@ -3,6 +3,10 @@ name: using-systematic
|
|
|
3
3
|
description: Use when starting any conversation - establishes how to find and use skills, requiring skill tool invocation before ANY response including clarifying questions
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
<SUBAGENT-STOP>
|
|
7
|
+
If you were dispatched as a subagent to execute a specific task, skip this skill.
|
|
8
|
+
</SUBAGENT-STOP>
|
|
9
|
+
|
|
6
10
|
<EXTREMELY-IMPORTANT>
|
|
7
11
|
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.
|
|
8
12
|
|
|
@@ -11,6 +15,16 @@ IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.
|
|
|
11
15
|
This is not negotiable. This is not optional. You cannot rationalize your way out of this.
|
|
12
16
|
</EXTREMELY-IMPORTANT>
|
|
13
17
|
|
|
18
|
+
## Instruction Priority
|
|
19
|
+
|
|
20
|
+
Systematic skills override default system prompt behavior, but **user instructions always take precedence**:
|
|
21
|
+
|
|
22
|
+
1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
|
|
23
|
+
2. **Systematic skills** — override default system behavior where they conflict
|
|
24
|
+
3. **Default system prompt** — lowest priority
|
|
25
|
+
|
|
26
|
+
If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.
|
|
27
|
+
|
|
14
28
|
## How to Access Skills
|
|
15
29
|
|
|
16
30
|
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.
|