@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 CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  findCommandsInDir,
7
7
  findSkillsInDir,
8
8
  getConfigPaths
9
- } from "./index-ek6rskkw.js";
9
+ } from "./index-6evyges6.js";
10
10
 
11
11
  // src/cli.ts
12
12
  import fs from "fs";
@@ -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-ek6rskkw.js";
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;
@@ -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.18.0",
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.14.50",
69
- "@opencode-ai/sdk": "1.14.50",
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.