@nano-step/skill-manager 4.0.0 → 5.0.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.
Files changed (28) hide show
  1. package/README.md +79 -174
  2. package/dist/config.d.ts +5 -0
  3. package/dist/config.js +59 -0
  4. package/dist/index.js +147 -21
  5. package/dist/installer.d.ts +4 -0
  6. package/dist/installer.js +92 -0
  7. package/dist/registry.d.ts +3 -0
  8. package/dist/registry.js +63 -0
  9. package/dist/state.d.ts +4 -0
  10. package/dist/state.js +52 -0
  11. package/dist/utils.d.ts +25 -18
  12. package/dist/utils.js +7 -106
  13. package/package.json +2 -2
  14. package/skills/graphql-inspector/SKILL.md +170 -0
  15. package/skills/graphql-inspector/skill.json +9 -0
  16. package/{templates/skill → skills/skill-management}/SKILL.md +18 -18
  17. package/{templates/command-refresh.md → skills/skill-management/skill-refresh.md} +8 -8
  18. package/skills/skill-management/skill.json +18 -0
  19. package/templates/agent.json +0 -11
  20. package/templates/command-workflow.md +0 -188
  21. /package/{templates/skill → skills/skill-management}/assets/tools-template.json +0 -0
  22. /package/{templates/skill → skills/skill-management}/assets/workflow-schema.json +0 -0
  23. /package/{templates/skill → skills/skill-management}/assets/workflow-templates.json +0 -0
  24. /package/{templates/skill → skills/skill-management}/references/error-handling.md +0 -0
  25. /package/{templates/skill → skills/skill-management}/references/result-handling.md +0 -0
  26. /package/{templates/skill → skills/skill-management}/references/tool-categories.md +0 -0
  27. /package/{templates/skill → skills/skill-management}/references/tool-execution.md +0 -0
  28. /package/{templates/skill → skills/skill-management}/references/workflows.md +0 -0
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadCatalog = loadCatalog;
7
+ exports.getSkillManifest = getSkillManifest;
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_extra_1 = __importDefault(require("fs-extra"));
10
+ const chalk_1 = __importDefault(require("chalk"));
11
+ function isValidManifest(data) {
12
+ if (typeof data !== "object" || data === null)
13
+ return false;
14
+ const obj = data;
15
+ return (typeof obj.name === "string" &&
16
+ typeof obj.version === "string" &&
17
+ typeof obj.description === "string");
18
+ }
19
+ async function loadCatalog(packageSkillsDir) {
20
+ const exists = await fs_extra_1.default.pathExists(packageSkillsDir);
21
+ if (!exists) {
22
+ return [];
23
+ }
24
+ const entries = await fs_extra_1.default.readdir(packageSkillsDir, { withFileTypes: true });
25
+ const catalog = [];
26
+ for (const entry of entries) {
27
+ if (!entry.isDirectory())
28
+ continue;
29
+ const manifestPath = path_1.default.join(packageSkillsDir, entry.name, "skill.json");
30
+ const manifestExists = await fs_extra_1.default.pathExists(manifestPath);
31
+ if (!manifestExists)
32
+ continue;
33
+ try {
34
+ const raw = await fs_extra_1.default.readFile(manifestPath, "utf8");
35
+ const data = JSON.parse(raw);
36
+ if (!isValidManifest(data)) {
37
+ console.error(chalk_1.default.yellow(`Warning: Skipping ${entry.name} — skill.json missing required fields (name, version, description)`));
38
+ continue;
39
+ }
40
+ catalog.push(data);
41
+ }
42
+ catch {
43
+ console.error(chalk_1.default.yellow(`Warning: Skipping ${entry.name} — failed to parse skill.json`));
44
+ }
45
+ }
46
+ return catalog.sort((a, b) => a.name.localeCompare(b.name));
47
+ }
48
+ async function getSkillManifest(packageSkillsDir, name) {
49
+ const manifestPath = path_1.default.join(packageSkillsDir, name, "skill.json");
50
+ const exists = await fs_extra_1.default.pathExists(manifestPath);
51
+ if (!exists)
52
+ return null;
53
+ try {
54
+ const raw = await fs_extra_1.default.readFile(manifestPath, "utf8");
55
+ const data = JSON.parse(raw);
56
+ if (!isValidManifest(data))
57
+ return null;
58
+ return data;
59
+ }
60
+ catch {
61
+ return null;
62
+ }
63
+ }
@@ -0,0 +1,4 @@
1
+ import { ManagerState } from "./utils";
2
+ export declare function loadState(stateFilePath: string): Promise<ManagerState>;
3
+ export declare function saveState(stateFilePath: string, state: ManagerState): Promise<void>;
4
+ export declare function migrateV4State(configDir: string, stateFilePath: string, skillsDir: string): Promise<void>;
package/dist/state.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.loadState = loadState;
7
+ exports.saveState = saveState;
8
+ exports.migrateV4State = migrateV4State;
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const chalk_1 = __importDefault(require("chalk"));
12
+ const utils_1 = require("./utils");
13
+ function createEmptyState() {
14
+ return { version: 1, managerVersion: utils_1.MANAGER_VERSION, skills: {} };
15
+ }
16
+ async function loadState(stateFilePath) {
17
+ const exists = await fs_extra_1.default.pathExists(stateFilePath);
18
+ if (!exists) {
19
+ return createEmptyState();
20
+ }
21
+ const data = await (0, utils_1.readJsonFile)(stateFilePath, createEmptyState());
22
+ return data;
23
+ }
24
+ async function saveState(stateFilePath, state) {
25
+ state.managerVersion = utils_1.MANAGER_VERSION;
26
+ await (0, utils_1.writeJsonFile)(stateFilePath, state);
27
+ }
28
+ async function migrateV4State(configDir, stateFilePath, skillsDir) {
29
+ const oldVersionFile = path_1.default.join(configDir, ".agent-skill-version.json");
30
+ const [oldExists, newExists] = await Promise.all([
31
+ fs_extra_1.default.pathExists(oldVersionFile),
32
+ fs_extra_1.default.pathExists(stateFilePath),
33
+ ]);
34
+ if (!oldExists || newExists) {
35
+ return;
36
+ }
37
+ console.log(chalk_1.default.cyan("Migrating from v4 state format..."));
38
+ const state = createEmptyState();
39
+ const skillPath = path_1.default.join(skillsDir, "skill-management", "SKILL.md");
40
+ const mcpSkillPath = path_1.default.join(skillsDir, "mcp-management", "SKILL.md");
41
+ const skillInstalled = await fs_extra_1.default.pathExists(skillPath) || await fs_extra_1.default.pathExists(mcpSkillPath);
42
+ if (skillInstalled) {
43
+ state.skills["skill-management"] = {
44
+ version: "1.0.0",
45
+ installedAt: new Date().toISOString(),
46
+ location: configDir.includes(".config/opencode") ? "global" : "project",
47
+ };
48
+ }
49
+ await (0, utils_1.writeJsonFile)(stateFilePath, state);
50
+ await fs_extra_1.default.remove(oldVersionFile);
51
+ console.log(chalk_1.default.green("Migration complete. State saved to .skill-manager.json"));
52
+ }
package/dist/utils.d.ts CHANGED
@@ -1,22 +1,33 @@
1
- export declare const PACKAGE_VERSION = "4.0.0";
2
- export declare const AGENT_ID = "mcp-manager";
3
- export declare const SKILL_DIR_NAME = "mcp-management";
1
+ export declare const MANAGER_VERSION = "5.0.0";
2
+ export interface SkillManifest {
3
+ name: string;
4
+ version: string;
5
+ description: string;
6
+ compatibility?: string;
7
+ agent?: {
8
+ id: string;
9
+ config: Record<string, unknown>;
10
+ } | null;
11
+ commands?: string[];
12
+ tags?: string[];
13
+ }
14
+ export interface InstalledSkillInfo {
15
+ version: string;
16
+ installedAt: string;
17
+ location: "global" | "project";
18
+ }
19
+ export interface ManagerState {
20
+ version: number;
21
+ managerVersion: string;
22
+ skills: Record<string, InstalledSkillInfo>;
23
+ }
4
24
  export interface OpenCodePaths {
5
25
  configDir: string;
6
- projectDir: string;
7
26
  commandDir: string;
8
27
  skillsDir: string;
9
28
  agentConfigPath: string;
10
- versionFilePath: string;
11
- templateSkillDir: string;
12
- templateCommandPath: string;
13
- templateAgentPath: string;
14
- }
15
- export interface InstallationState {
16
- installedVersion: string | null;
17
- skillInstalled: boolean;
18
- commandInstalled: boolean;
19
- agentInstalled: boolean;
29
+ stateFilePath: string;
30
+ packageSkillsDir: string;
20
31
  }
21
32
  export declare function detectOpenCodePaths(): Promise<OpenCodePaths>;
22
33
  export declare function ensureDirExists(dirPath: string): Promise<void>;
@@ -24,7 +35,3 @@ export declare function readJsonFile<T>(filePath: string, fallback: T): Promise<
24
35
  export declare function writeJsonFile(filePath: string, data: unknown): Promise<void>;
25
36
  export declare function readText(filePath: string): Promise<string>;
26
37
  export declare function writeText(filePath: string, data: string): Promise<void>;
27
- export declare function backupFile(filePath: string, suffix: string): Promise<string>;
28
- export declare function fileDiffersFromTemplate(filePath: string, templatePath: string): Promise<boolean>;
29
- export declare function directoryDiffersFromTemplate(dirPath: string, templateDir: string): Promise<boolean>;
30
- export declare function getInstallationState(paths: OpenCodePaths): Promise<InstallationState>;
package/dist/utils.js CHANGED
@@ -3,23 +3,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.SKILL_DIR_NAME = exports.AGENT_ID = exports.PACKAGE_VERSION = void 0;
6
+ exports.MANAGER_VERSION = void 0;
7
7
  exports.detectOpenCodePaths = detectOpenCodePaths;
8
8
  exports.ensureDirExists = ensureDirExists;
9
9
  exports.readJsonFile = readJsonFile;
10
10
  exports.writeJsonFile = writeJsonFile;
11
11
  exports.readText = readText;
12
12
  exports.writeText = writeText;
13
- exports.backupFile = backupFile;
14
- exports.fileDiffersFromTemplate = fileDiffersFromTemplate;
15
- exports.directoryDiffersFromTemplate = directoryDiffersFromTemplate;
16
- exports.getInstallationState = getInstallationState;
17
13
  const path_1 = __importDefault(require("path"));
18
14
  const os_1 = __importDefault(require("os"));
19
15
  const fs_extra_1 = __importDefault(require("fs-extra"));
20
- exports.PACKAGE_VERSION = "4.0.0";
21
- exports.AGENT_ID = "mcp-manager";
22
- exports.SKILL_DIR_NAME = "mcp-management";
16
+ exports.MANAGER_VERSION = "5.0.0";
23
17
  async function detectOpenCodePaths() {
24
18
  const homeConfig = path_1.default.join(os_1.default.homedir(), ".config", "opencode");
25
19
  const cwd = process.cwd();
@@ -30,24 +24,13 @@ async function detectOpenCodePaths() {
30
24
  throw new Error("OpenCode config not found. Expected ~/.config/opencode or .opencode in project.");
31
25
  }
32
26
  const configDir = hasProjectConfig ? projectConfig : homeConfig;
33
- const commandDir = path_1.default.join(configDir, "command");
34
- const skillsDir = path_1.default.join(configDir, "skills");
35
- const agentConfigPath = path_1.default.join(configDir, "oh-my-opencode.json");
36
- const packageRoot = path_1.default.join(__dirname, "..");
37
- const templateSkillDir = path_1.default.join(packageRoot, "templates", "skill");
38
- const templateCommandPath = path_1.default.join(packageRoot, "templates", "command-refresh.md");
39
- const templateAgentPath = path_1.default.join(packageRoot, "templates", "agent.json");
40
- const versionFilePath = path_1.default.join(configDir, ".agent-skill-version.json");
41
27
  return {
42
28
  configDir,
43
- projectDir: cwd,
44
- commandDir,
45
- skillsDir,
46
- agentConfigPath,
47
- versionFilePath,
48
- templateSkillDir,
49
- templateCommandPath,
50
- templateAgentPath,
29
+ commandDir: path_1.default.join(configDir, "command"),
30
+ skillsDir: path_1.default.join(configDir, "skills"),
31
+ agentConfigPath: path_1.default.join(configDir, "oh-my-opencode.json"),
32
+ stateFilePath: path_1.default.join(configDir, ".skill-manager.json"),
33
+ packageSkillsDir: path_1.default.join(__dirname, "..", "skills"),
51
34
  };
52
35
  }
53
36
  async function ensureDirExists(dirPath) {
@@ -70,85 +53,3 @@ async function readText(filePath) {
70
53
  async function writeText(filePath, data) {
71
54
  await fs_extra_1.default.writeFile(filePath, data, "utf8");
72
55
  }
73
- async function backupFile(filePath, suffix) {
74
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
75
- const backupPath = `${filePath}.${suffix}-${timestamp}.bak`;
76
- await fs_extra_1.default.copy(filePath, backupPath);
77
- return backupPath;
78
- }
79
- async function fileDiffersFromTemplate(filePath, templatePath) {
80
- const exists = await fs_extra_1.default.pathExists(filePath);
81
- if (!exists) {
82
- return false;
83
- }
84
- const [current, template] = await Promise.all([readText(filePath), readText(templatePath)]);
85
- return current.trim() !== template.trim();
86
- }
87
- async function listFilesRecursively(dirPath, baseDir) {
88
- const entries = await fs_extra_1.default.readdir(dirPath, { withFileTypes: true });
89
- const files = [];
90
- for (const entry of entries) {
91
- const fullPath = path_1.default.join(dirPath, entry.name);
92
- if (entry.isDirectory()) {
93
- files.push(...(await listFilesRecursively(fullPath, baseDir)));
94
- }
95
- else if (entry.isFile()) {
96
- files.push(path_1.default.relative(baseDir, fullPath));
97
- }
98
- }
99
- return files;
100
- }
101
- async function directoryDiffersFromTemplate(dirPath, templateDir) {
102
- const [dirExists, templateExists] = await Promise.all([
103
- fs_extra_1.default.pathExists(dirPath),
104
- fs_extra_1.default.pathExists(templateDir),
105
- ]);
106
- if (!dirExists) {
107
- return false;
108
- }
109
- if (!templateExists) {
110
- return true;
111
- }
112
- const [currentFiles, templateFiles] = await Promise.all([
113
- listFilesRecursively(dirPath, dirPath),
114
- listFilesRecursively(templateDir, templateDir),
115
- ]);
116
- currentFiles.sort();
117
- templateFiles.sort();
118
- if (currentFiles.length !== templateFiles.length) {
119
- return true;
120
- }
121
- for (let index = 0; index < currentFiles.length; index += 1) {
122
- if (currentFiles[index] !== templateFiles[index]) {
123
- return true;
124
- }
125
- }
126
- for (const relativePath of currentFiles) {
127
- const [currentBuffer, templateBuffer] = await Promise.all([
128
- fs_extra_1.default.readFile(path_1.default.join(dirPath, relativePath)),
129
- fs_extra_1.default.readFile(path_1.default.join(templateDir, relativePath)),
130
- ]);
131
- if (!currentBuffer.equals(templateBuffer)) {
132
- return true;
133
- }
134
- }
135
- return false;
136
- }
137
- async function getInstallationState(paths) {
138
- const skillPath = path_1.default.join(paths.skillsDir, exports.SKILL_DIR_NAME, "SKILL.md");
139
- const commandPath = path_1.default.join(paths.commandDir, "agent-skill-refresh.md");
140
- const [versionData, skillInstalled, commandInstalled] = await Promise.all([
141
- readJsonFile(paths.versionFilePath, {}),
142
- fs_extra_1.default.pathExists(skillPath),
143
- fs_extra_1.default.pathExists(commandPath),
144
- ]);
145
- const agentConfig = await readJsonFile(paths.agentConfigPath, {});
146
- const agents = agentConfig.agents;
147
- const agentInstalled = agents ? Object.prototype.hasOwnProperty.call(agents, exports.AGENT_ID) : false;
148
- return {
149
- installedVersion: typeof versionData.version === "string" ? versionData.version : null,
150
- skillInstalled,
151
- commandInstalled,
152
- agentInstalled,
153
- };
154
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nano-step/skill-manager",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "CLI tool that installs and manages AI agent skills, MCP tool routing, and workflow configurations.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,7 +10,7 @@
10
10
  "files": [
11
11
  "bin",
12
12
  "dist",
13
- "templates"
13
+ "skills"
14
14
  ],
15
15
  "scripts": {
16
16
  "build": "tsc",
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: graphql-inspector
3
+ description: >-
4
+ GraphQL schema inspection and query execution skill. Guides AI agents through
5
+ progressive schema discovery — scan first, drill down second, execute last.
6
+ Enforces read-only by default with token-efficient workflows.
7
+ compatibility: "OpenCode with graphql-tools MCP server"
8
+ metadata:
9
+ author: kokorolx
10
+ version: "1.0.0"
11
+ ---
12
+
13
+ # GraphQL Inspector Skill
14
+
15
+ **Version**: 1.0.0 | **MCP Server**: `graphql-tools` | **Default Mode**: Read-Only
16
+
17
+ ## Overview
18
+
19
+ This skill teaches AI agents the optimal workflow for exploring and querying GraphQL APIs via the `graphql-tools` MCP server. The core principle is **progressive disclosure** — start cheap, drill down only when needed.
20
+
21
+ ## When to Use This Skill
22
+
23
+ | Trigger | Action |
24
+ |---------|--------|
25
+ | "What queries/mutations are available?" | `filter_queries` / `filter_mutations` with `detailed=false` |
26
+ | "How do I call X query?" | `get_field_details` for args + return type |
27
+ | "What fields does Type Y have?" | `get_type_details` |
28
+ | "Run this query" | `execute_query` (read-only) |
29
+ | "What does the schema look like?" | `filter_queries` + `filter_mutations` + `filter_types` (NOT `get_graphql_schema`) |
30
+
31
+ ## Golden Rule: Progressive Disclosure
32
+
33
+ **NEVER start with `get_graphql_schema`.** It dumps the entire schema and wastes tokens.
34
+
35
+ Follow this flow instead:
36
+
37
+ ```
38
+ Step 1: SCAN (cheap, small payload)
39
+ filter_queries(detailed=false) → list query names + descriptions
40
+ filter_mutations(detailed=false) → list mutation names + descriptions
41
+
42
+ Step 2: DRILL DOWN (only for fields you need)
43
+ get_field_details(field_name="targetField", operation_type="query")
44
+ → get arguments, return type, deprecation info
45
+
46
+ Step 3: UNDERSTAND TYPES (only if return/input types are complex)
47
+ get_type_details(type_name="ComplexType")
48
+ → get fields, enums, interfaces
49
+ filter_types(kind="INPUT_OBJECT", search="CreateInput")
50
+ → find input types for mutations
51
+
52
+ Step 4: EXECUTE (only after understanding the schema)
53
+ execute_query(query="query { ... }")
54
+ → run read-only query with known structure
55
+ ```
56
+
57
+ ## Available Tools
58
+
59
+ ### Inspection Tools (always available, all read-only)
60
+
61
+ | Tool | Purpose | When to Use |
62
+ |------|---------|-------------|
63
+ | `filter_queries` | List available queries | **First step** — scan what's available |
64
+ | `filter_mutations` | List available mutations | **First step** — scan what's available |
65
+ | `filter_types` | List types by kind/search | Find INPUT_OBJECT types for mutation args |
66
+ | `get_field_details` | Deep dive into one field | **Second step** — get args + return type |
67
+ | `get_type_details` | Deep dive into one type | Understand complex return/input types |
68
+ | `get_graphql_schema` | Full schema dump (SDL) | **Last resort only** — very large payload |
69
+
70
+ ### Execution Tools
71
+
72
+ | Tool | Purpose | Availability |
73
+ |------|---------|-------------|
74
+ | `execute_query` | Run read-only GraphQL query | Always available |
75
+ | `execute_mutation` | Run GraphQL mutation | Only when `ALLOW_MUTATIONS=true` |
76
+
77
+ ## Workflow Examples
78
+
79
+ ### Example 1: "What can I query?"
80
+
81
+ ```
82
+ 1. filter_queries(detailed=false)
83
+ → Returns: [{ name: "getUser", description: "Fetch user by ID" }, ...]
84
+
85
+ 2. Found interesting query "getUser", need args:
86
+ get_field_details(field_name="getUser", operation_type="query")
87
+ → Returns: args=[{name: "id", type: "ID!"}], return_type="User"
88
+
89
+ 3. Need to know User fields:
90
+ get_type_details(type_name="User")
91
+ → Returns: fields=[{name: "id"}, {name: "email"}, {name: "name"}, ...]
92
+
93
+ 4. Now execute with confidence:
94
+ execute_query(query="query { getUser(id: \"123\") { id name email } }")
95
+ ```
96
+
97
+ ### Example 2: "Find queries related to orders"
98
+
99
+ ```
100
+ 1. filter_queries(search="order", detailed=false)
101
+ → Returns matching queries with names + descriptions
102
+
103
+ 2. If need more detail on a specific one:
104
+ get_field_details(field_name="getOrderById", operation_type="query")
105
+ ```
106
+
107
+ ### Example 3: "What input does createUser mutation need?"
108
+
109
+ ```
110
+ 1. get_field_details(field_name="createUser", operation_type="mutation")
111
+ → Returns: args=[{name: "input", type: "CreateUserInput!"}]
112
+
113
+ 2. filter_types(kind="INPUT_OBJECT", search="CreateUserInput")
114
+ → Returns input type with all fields and their types
115
+ ```
116
+
117
+ ### Example 4: "Show me all enum types"
118
+
119
+ ```
120
+ 1. filter_types(kind="ENUM", detailed=true)
121
+ → Returns all enums with their values
122
+ ```
123
+
124
+ ## Common Parameters
125
+
126
+ All tools accept these optional auth parameters:
127
+
128
+ | Parameter | Description |
129
+ |-----------|-------------|
130
+ | `endpoint` | GraphQL endpoint URL (default from server config) |
131
+ | `username` | Basic auth username |
132
+ | `password` | Basic auth password |
133
+ | `bearer_token` | Bearer token for auth |
134
+
135
+ ## Anti-Patterns (DO NOT)
136
+
137
+ | Bad Practice | Why | Do Instead |
138
+ |-------------|-----|------------|
139
+ | Start with `get_graphql_schema` | Dumps entire schema, wastes tokens | `filter_queries(detailed=false)` |
140
+ | Use `detailed=true` on first call | Returns args/types for ALL fields | `detailed=false` first, then `get_field_details` for specific fields |
141
+ | Execute query without understanding schema | Likely wrong field names or args | Scan → drill down → execute |
142
+ | Call `execute_mutation` without checking availability | Will error if mutations disabled | Check if tool is listed first |
143
+ | Guess field names in queries | Will get GraphQL errors | Always inspect schema first |
144
+
145
+ ## Safety Model
146
+
147
+ The server enforces 3 layers of protection:
148
+
149
+ 1. **Tool Listing**: `execute_mutation` is hidden when `ALLOW_MUTATIONS` is not set
150
+ 2. **Execution Guard**: `execute_mutation` rejects at runtime even if called directly
151
+ 3. **AST Validation**: GraphQL documents are parsed — operation type verified at AST level
152
+
153
+ **Default is read-only.** Mutations require explicit `ALLOW_MUTATIONS=true` env var on the server.
154
+
155
+ ## Token Efficiency Tips
156
+
157
+ - `filter_queries(detailed=false)` returns ~50 bytes per field vs ~500 bytes with `detailed=true`
158
+ - Use `search` parameter to narrow results before drilling down
159
+ - `get_field_details` for ONE field is cheaper than `filter_queries(detailed=true)` for ALL fields
160
+ - Cache awareness: repeated calls to same endpoint use 5-minute server cache
161
+
162
+ ## Troubleshooting
163
+
164
+ | Issue | Solution |
165
+ |-------|----------|
166
+ | "Unknown tool: execute_mutation" | Mutations disabled (default). Set `ALLOW_MUTATIONS=true` on server |
167
+ | "Mutations are not allowed in read-only mode" | Query sent to `execute_query` contains a mutation. Use `execute_mutation` instead |
168
+ | "Subscriptions are not supported" | Subscriptions are intentionally blocked |
169
+ | Empty results from filter | Check endpoint is correct and accessible |
170
+ | Auth errors | Verify credentials — try `get_graphql_schema` with auth params first |
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "graphql-inspector",
3
+ "version": "1.0.0",
4
+ "description": "GraphQL schema inspection and query execution — progressive discovery workflow for token-efficient schema exploration",
5
+ "compatibility": "OpenCode with graphql-tools MCP server",
6
+ "agent": null,
7
+ "commands": [],
8
+ "tags": ["graphql", "schema", "inspection", "query"]
9
+ }
@@ -1,22 +1,22 @@
1
1
  ---
2
- name: agent-skill-management
2
+ name: skill-management
3
3
  description: >-
4
- Agent skill routing and execution. Enables efficient routing of MCP tools
4
+ Skill routing and execution. Enables efficient routing of tools
5
5
  with token-saving isolation. Uses semantic AI categorization to support ANY
6
- MCP configuration. Cache at .opencode/agent-skill-tools.json for fast tool discovery.
6
+ tool configuration. Cache at .opencode/skill-tools.json for fast tool discovery.
7
7
  compatibility: "OpenCode with any MCP server"
8
8
  metadata:
9
- author: Sisyphus
10
- version: "4.0.0"
9
+ author: nano-step
10
+ version: "5.0.0"
11
11
  ---
12
12
 
13
- # Agent Skill Management Skill
13
+ # Skill Management
14
14
 
15
- **Version**: 4.0.0 | **Architecture**: 3-Tier Progressive Disclosure | **Categorization**: Semantic AI
15
+ **Version**: 5.0.0 | **Architecture**: 3-Tier Progressive Disclosure | **Categorization**: Semantic AI
16
16
 
17
17
  ## Overview
18
18
 
19
- This skill enables the agent-skill-manager agent to efficiently route and execute MCP (Model Context Protocol) tools. By isolating MCP tool definitions in a dedicated subagent context, we reduce main agent token usage by 80-95%.
19
+ This skill enables the skill-manager agent to efficiently route and execute tools. By isolating tool definitions in a dedicated subagent context, we reduce main agent token usage by 80-95%.
20
20
 
21
21
  ## When to Use This Skill
22
22
 
@@ -32,7 +32,7 @@ This skill enables the agent-skill-manager agent to efficiently route and execut
32
32
 
33
33
  ### Tool Categories (AI-Generated)
34
34
 
35
- Categories are generated by `/agent-skill-refresh` using semantic AI analysis. Common categories include:
35
+ Categories are generated by `/skill-refresh` using semantic AI analysis. Common categories include:
36
36
 
37
37
  | Category | Description | Example Tools |
38
38
  |----------|-------------|---------------|
@@ -42,7 +42,7 @@ Categories are generated by `/agent-skill-refresh` using semantic AI analysis. C
42
42
  | communication | Messaging, notifications | post_message, list_channels |
43
43
  | database | SQL queries, data operations | query, insert, update |
44
44
 
45
- **Note**: Your actual categories depend on your MCP configuration. Run `/agent-skill-refresh` to generate.
45
+ **Note**: Your actual categories depend on your tool configuration. Run `/skill-refresh` to generate.
46
46
 
47
47
  ### Most Common Tools
48
48
 
@@ -79,7 +79,7 @@ Task received
79
79
  ├─ Explicit tool name mentioned?
80
80
  │ └─ YES → Use that tool directly
81
81
 
82
- ├─ Read .opencode/agent-skill-tools.json cache
82
+ ├─ Read .opencode/skill-tools.json cache
83
83
  │ └─ Match task keywords against category keywords
84
84
 
85
85
  ├─ Multiple categories match?
@@ -156,13 +156,13 @@ Workflows enforce best practices by requiring prerequisite steps before tool exe
156
156
 
157
157
  ```bash
158
158
  # Add a workflow from template
159
- /agent-skill-workflow add --template database
159
+ /skill-workflow add --template database
160
160
 
161
161
  # List active workflows
162
- /agent-skill-workflow list
162
+ /skill-workflow list
163
163
 
164
164
  # Disable temporarily
165
- /agent-skill-workflow disable database-safe-query
165
+ /skill-workflow disable database-safe-query
166
166
  ```
167
167
 
168
168
  ### Built-in Templates
@@ -185,16 +185,16 @@ Workflows enforce best practices by requiring prerequisite steps before tool exe
185
185
 
186
186
  ## Cache Usage
187
187
 
188
- **Location**: `.opencode/agent-skill-tools.json`
188
+ **Location**: `.opencode/skill-tools.json`
189
189
 
190
190
  ### If Cache Exists
191
191
  1. Read and parse JSON (v2.0.0 schema)
192
192
  2. Use `categories` for keyword-based routing
193
193
  3. Use `tools` map for O(1) tool lookup
194
- 4. Check `generated_at` - if >24h old, suggest `/agent-skill-refresh`
194
+ 4. Check `generated_at` - if >24h old, suggest `/skill-refresh`
195
195
 
196
196
  ### If Cache Missing
197
- 1. Inform user: "Run `/agent-skill-refresh` to create tool cache"
197
+ 1. Inform user: "Run `/skill-refresh` to create tool cache"
198
198
  2. Fall back to dynamic tool discovery (slower)
199
199
 
200
200
  **Full cache schema**: [tool-execution.md](references/tool-execution.md#cache-schema)
@@ -237,7 +237,7 @@ Workflows enforce best practices by requiring prerequisite steps before tool exe
237
237
  | Tool not found | Suggest similar tools |
238
238
  | Execution failed | Include context + retry suggestions |
239
239
  | Timeout | Report partial result + suggestions |
240
- | Cache missing | Prompt `/agent-skill-refresh` |
240
+ | Cache missing | Prompt `/skill-refresh` |
241
241
 
242
242
  **Full recovery procedures**: [error-handling.md](references/error-handling.md)
243
243
 
@@ -1,10 +1,10 @@
1
- # /agent-skill-refresh
1
+ # /skill-refresh
2
2
 
3
- Re-index and categorize all available MCP tools for intelligent routing.
3
+ Re-index and categorize all available tools for intelligent routing.
4
4
 
5
5
  ## Purpose
6
6
 
7
- Create a semantic tool index in `.opencode/agent-skill-tools.json` by analyzing **all available tools** in the current agent context. Categorization is AI-only and based on tool names + descriptions (no prefixes).
7
+ Create a semantic tool index in `.opencode/skill-tools.json` by analyzing **all available tools** in the current agent context. Categorization is AI-only and based on tool names + descriptions (no prefixes).
8
8
 
9
9
  ## When to Run
10
10
 
@@ -15,10 +15,10 @@ Create a semantic tool index in `.opencode/agent-skill-tools.json` by analyzing
15
15
 
16
16
  ## Auto-Refresh Triggers
17
17
 
18
- The agent-skill-manager will suggest running `/agent-skill-refresh` automatically when:
18
+ The skill-manager will suggest running `/skill-refresh` automatically when:
19
19
 
20
20
  - **Tool not found**: A requested tool doesn't exist in the cache
21
- - **Cache missing**: The `.opencode/agent-skill-tools.json` file doesn't exist
21
+ - **Cache missing**: The `.opencode/skill-tools.json` file doesn't exist
22
22
  - **Cache stale**: The cache is older than 24 hours
23
23
  - **Tool count mismatch**: Available tools differ from cached count
24
24
 
@@ -59,7 +59,7 @@ Output as JSON.
59
59
 
60
60
  ### Step 3: Write Cache File (v2.0.0)
61
61
 
62
- Write `.opencode/agent-skill-tools.json` with this schema:
62
+ Write `.opencode/skill-tools.json` with this schema:
63
63
 
64
64
  ```json
65
65
  {
@@ -86,7 +86,7 @@ Write `.opencode/agent-skill-tools.json` with this schema:
86
86
  ### Step 4: Report Summary
87
87
 
88
88
  ```
89
- Agent Skill Tools Index Refreshed:
89
+ Skill Tools Index Refreshed:
90
90
 
91
91
  Categories:
92
92
  - browser-automation: 25 tools
@@ -96,5 +96,5 @@ Categories:
96
96
  Summary:
97
97
  - Total tools: 75
98
98
  - Categories: 3
99
- - Cache: .opencode/agent-skill-tools.json
99
+ - Cache: .opencode/skill-tools.json
100
100
  ```