@clawpify/skills 1.0.5 → 1.0.6
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/clawpify/SKILL.md +20 -118
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +76 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +79 -34
- package/dist/skills.d.ts +17 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +20 -1
- package/package.json +1 -1
- package/src/agent.ts +33 -2
- package/src/index.ts +6 -1
- package/src/skills.ts +50 -0
package/clawpify/SKILL.md
CHANGED
|
@@ -1,134 +1,36 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: clawpify
|
|
3
3
|
description: Query and manage Shopify stores via GraphQL Admin API. Use for products, orders, customers, inventory, discounts, and all Shopify data operations.
|
|
4
|
-
dependencies:
|
|
5
|
-
- Tool: shopify_graphql (from MCP server or custom function)
|
|
6
4
|
---
|
|
7
5
|
|
|
8
6
|
# Shopify GraphQL Admin API
|
|
9
7
|
|
|
10
|
-
A comprehensive skill for interacting with Shopify's GraphQL Admin API. This skill enables Claude to query and manage all aspects of Shopify store data.
|
|
11
|
-
|
|
12
|
-
## When to Use This Skill
|
|
13
|
-
|
|
14
|
-
Use this skill when the user asks about:
|
|
15
|
-
- Products (list, search, create, update, delete)
|
|
16
|
-
- Orders (view, cancel, fulfill)
|
|
17
|
-
- Customers (list, create, update)
|
|
18
|
-
- Inventory (check levels, adjust quantities)
|
|
19
|
-
- Discounts (create codes, manage promotions)
|
|
20
|
-
- Any other Shopify store operations
|
|
21
|
-
|
|
22
|
-
## Critical Operations Requiring Permission
|
|
23
|
-
|
|
24
|
-
IMPORTANT: Before executing any of the following operations, you MUST ask for explicit user permission:
|
|
25
|
-
|
|
26
|
-
- Refunds: Create refunds (permanent financial transactions)
|
|
27
|
-
- Order Cancellations: Cancel orders (may trigger refunds)
|
|
28
|
-
- Gift Card Deactivation: Permanently disable gift cards
|
|
29
|
-
- Inventory Adjustments: Modify stock levels
|
|
30
|
-
- Product Deletions: Permanently remove products
|
|
31
|
-
- Discount Activations: Change pricing for customers
|
|
32
|
-
|
|
33
|
-
Always show what will be changed and wait for user confirmation.
|
|
34
|
-
|
|
35
8
|
## How to Use
|
|
36
9
|
|
|
37
|
-
1. Use
|
|
38
|
-
2.
|
|
39
|
-
3.
|
|
10
|
+
1. Use `load_skill_reference` to load the relevant reference FIRST
|
|
11
|
+
2. Use `shopify_graphql` to execute queries based on the loaded reference
|
|
12
|
+
3. Check for `errors` and `userErrors` in responses
|
|
40
13
|
4. Format all IDs as: `gid://shopify/Resource/123`
|
|
41
14
|
|
|
42
|
-
##
|
|
43
|
-
|
|
44
|
-
For detailed patterns and examples, refer to the reference documents:
|
|
45
|
-
- products.md - Products and variants management
|
|
46
|
-
- orders.md - Order operations
|
|
47
|
-
- customers.md - Customer management
|
|
48
|
-
- inventory.md - Inventory and locations
|
|
49
|
-
- discounts.md - Discount codes and promotions
|
|
50
|
-
- collections.md - Product collections
|
|
51
|
-
- fulfillments.md - Order fulfillment and shipping
|
|
52
|
-
- refunds.md - Process refunds
|
|
53
|
-
- draft-orders.md - Draft order creation
|
|
54
|
-
- gift-cards.md - Gift card management
|
|
55
|
-
- webhooks.md - Event subscriptions
|
|
56
|
-
- locations.md - Store locations
|
|
57
|
-
- marketing.md - Marketing activities
|
|
58
|
-
- markets.md - Multi-market setup
|
|
59
|
-
- menus.md - Navigation menus
|
|
60
|
-
- metafields.md - Custom data fields
|
|
61
|
-
- pages.md - Store pages
|
|
62
|
-
- blogs.md - Blog management
|
|
63
|
-
- files.md - File uploads
|
|
64
|
-
- shipping.md - Shipping configuration
|
|
65
|
-
- shop.md - Store information
|
|
66
|
-
- subscriptions.md - Subscription management
|
|
67
|
-
- translations.md - Content translations
|
|
68
|
-
- segments.md - Customer segments
|
|
69
|
-
- bulk-operations.md - Bulk data operations
|
|
70
|
-
|
|
71
|
-
## Quick Examples
|
|
72
|
-
|
|
73
|
-
### List Recent Orders
|
|
74
|
-
```graphql
|
|
75
|
-
query {
|
|
76
|
-
orders(first: 10, sortKey: CREATED_AT, reverse: true) {
|
|
77
|
-
nodes {
|
|
78
|
-
id
|
|
79
|
-
name
|
|
80
|
-
totalPriceSet {
|
|
81
|
-
shopMoney { amount currencyCode }
|
|
82
|
-
}
|
|
83
|
-
customer { displayName }
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Search Products
|
|
90
|
-
```graphql
|
|
91
|
-
query {
|
|
92
|
-
products(first: 10, query: "title:*shirt* AND status:ACTIVE") {
|
|
93
|
-
nodes {
|
|
94
|
-
id
|
|
95
|
-
title
|
|
96
|
-
status
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
```
|
|
15
|
+
## Critical Operations Requiring Permission
|
|
101
16
|
|
|
102
|
-
|
|
103
|
-
```graphql
|
|
104
|
-
query GetInventory($id: ID!) {
|
|
105
|
-
inventoryItem(id: $id) {
|
|
106
|
-
id
|
|
107
|
-
inventoryLevels(first: 5) {
|
|
108
|
-
nodes {
|
|
109
|
-
quantities(names: ["available"]) {
|
|
110
|
-
name
|
|
111
|
-
quantity
|
|
112
|
-
}
|
|
113
|
-
location { name }
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
```
|
|
17
|
+
IMPORTANT: Before executing any of the following, you MUST ask for explicit user permission:
|
|
119
18
|
|
|
120
|
-
|
|
19
|
+
- Refunds, order cancellations, gift card deactivation
|
|
20
|
+
- Inventory adjustments, product deletions, discount activations
|
|
121
21
|
|
|
122
|
-
Always
|
|
123
|
-
- `errors` array = GraphQL syntax issues
|
|
124
|
-
- `userErrors` in mutations = validation problems
|
|
22
|
+
Always show what will be changed and wait for confirmation.
|
|
125
23
|
|
|
126
|
-
##
|
|
24
|
+
## Available References
|
|
127
25
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
26
|
+
Load the reference for your domain using `load_skill_reference` before writing queries:
|
|
27
|
+
|
|
28
|
+
**Store**: [shop](reference/shop.md) | [locations](reference/locations.md) | [markets](reference/markets.md)
|
|
29
|
+
**Catalog**: [products](reference/products.md) | [collections](reference/collections.md) | [inventory](reference/inventory.md)
|
|
30
|
+
**Sales**: [orders](reference/orders.md) | [draft-orders](reference/draft-orders.md) | [fulfillments](reference/fulfillments.md) | [refunds](reference/refunds.md)
|
|
31
|
+
**Customers**: [customers](reference/customers.md) | [segments](reference/segments.md) | [gift-cards](reference/gift-cards.md)
|
|
32
|
+
**Pricing**: [discounts](reference/discounts.md) | [subscriptions](reference/subscriptions.md)
|
|
33
|
+
**Content**: [pages](reference/pages.md) | [blogs](reference/blogs.md) | [menus](reference/menus.md) | [translations](reference/translations.md) | [files](reference/files.md)
|
|
34
|
+
**Custom data**: [metafields](reference/metafields.md)
|
|
35
|
+
**Automation**: [webhooks](reference/webhooks.md) | [bulk-operations](reference/bulk-operations.md)
|
|
36
|
+
**Growth**: [marketing](reference/marketing.md) | [shipping](reference/shipping.md)
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.js
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
+
// src/skills.ts
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
async function loadSkills(customDir) {
|
|
6
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
7
|
+
const skillParts = [];
|
|
8
|
+
await collectMarkdown(baseDir, skillParts);
|
|
9
|
+
return skillParts.join(`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
`);
|
|
14
|
+
}
|
|
15
|
+
async function loadSkillMetadata(customDir) {
|
|
16
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
17
|
+
const skillPath = join(baseDir, "SKILL.md");
|
|
18
|
+
return readFile(skillPath, "utf-8");
|
|
19
|
+
}
|
|
20
|
+
async function loadSkillReference(name, customDir) {
|
|
21
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
22
|
+
const refPath = join(baseDir, "references", name.endsWith(".md") ? name : `${name}.md`);
|
|
23
|
+
return readFile(refPath, "utf-8");
|
|
24
|
+
}
|
|
25
|
+
async function listSkillReferences(customDir) {
|
|
26
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
27
|
+
const refDir = join(baseDir, "references");
|
|
28
|
+
const entries = await readdir(refDir);
|
|
29
|
+
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
30
|
+
}
|
|
31
|
+
async function collectMarkdown(dir, parts) {
|
|
32
|
+
let entries;
|
|
33
|
+
try {
|
|
34
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const fullPath = join(dir, entry.name);
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
await collectMarkdown(fullPath, parts);
|
|
42
|
+
} else if (entry.name.endsWith(".md")) {
|
|
43
|
+
const content = await readFile(fullPath, "utf-8");
|
|
44
|
+
parts.push(content);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
1
49
|
// src/agent.ts
|
|
2
50
|
import Anthropic from "@anthropic-ai/sdk";
|
|
3
51
|
var DEFAULT_SYSTEM_INSTRUCTION = "You're Clawpify. You help run a Shopify store. The merchant texts you to get stuff done";
|
|
@@ -19,6 +67,20 @@ var SHOPIFY_GRAPHQL_TOOL = {
|
|
|
19
67
|
required: ["query"]
|
|
20
68
|
}
|
|
21
69
|
};
|
|
70
|
+
var LOAD_SKILL_REFERENCE_TOOL = {
|
|
71
|
+
name: "load_skill_reference",
|
|
72
|
+
description: "Load a Shopify GraphQL reference document for a specific domain. Available: products, orders, customers, inventory, discounts, collections, fulfillments, refunds, draft-orders, gift-cards, webhooks, locations, marketing, markets, menus, metafields, pages, blogs, files, shipping, shop, subscriptions, translations, segments, bulk-operations. Use this BEFORE writing GraphQL queries to get the correct syntax.",
|
|
73
|
+
input_schema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
reference: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "Name of the reference to load (e.g. 'orders', 'products', 'inventory')"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
required: ["reference"]
|
|
82
|
+
}
|
|
83
|
+
};
|
|
22
84
|
var DEFAULT_PRICING = {
|
|
23
85
|
inputPerMillion: 3,
|
|
24
86
|
outputPerMillion: 15,
|
|
@@ -98,8 +160,8 @@ class ShopifyAgent {
|
|
|
98
160
|
}
|
|
99
161
|
registerPlugin(plugin) {
|
|
100
162
|
const name = plugin.tool.name;
|
|
101
|
-
if (name === "shopify_graphql") {
|
|
102
|
-
throw new Error(`Cannot register plugin with reserved tool name "
|
|
163
|
+
if (name === "shopify_graphql" || name === "load_skill_reference") {
|
|
164
|
+
throw new Error(`Cannot register plugin with reserved tool name "${name}"`);
|
|
103
165
|
}
|
|
104
166
|
if (this.plugins.has(name)) {
|
|
105
167
|
throw new Error(`Plugin with tool name "${name}" is already registered`);
|
|
@@ -119,6 +181,7 @@ class ShopifyAgent {
|
|
|
119
181
|
buildTools() {
|
|
120
182
|
const allTools = [
|
|
121
183
|
SHOPIFY_GRAPHQL_TOOL,
|
|
184
|
+
LOAD_SKILL_REFERENCE_TOOL,
|
|
122
185
|
...[...this.plugins.values()].map((p) => p.tool)
|
|
123
186
|
];
|
|
124
187
|
if (allTools.length > 0) {
|
|
@@ -156,6 +219,17 @@ class ShopifyAgent {
|
|
|
156
219
|
return { content: content2, isError: true };
|
|
157
220
|
}
|
|
158
221
|
}
|
|
222
|
+
if (name === "load_skill_reference") {
|
|
223
|
+
try {
|
|
224
|
+
const content2 = await loadSkillReference(input.reference);
|
|
225
|
+
await safeHook(this.hooks.onToolResult, name, content2, false);
|
|
226
|
+
return { content: content2, isError: false };
|
|
227
|
+
} catch (error) {
|
|
228
|
+
const content2 = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
229
|
+
await safeHook(this.hooks.onToolResult, name, content2, true);
|
|
230
|
+
return { content: content2, isError: true };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
159
233
|
if (this.plugins.has(name)) {
|
|
160
234
|
const plugin = this.plugins.get(name);
|
|
161
235
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -39,6 +39,6 @@ export { ShopifyAgent, DEFAULT_SYSTEM_INSTRUCTION } from "./agent";
|
|
|
39
39
|
export type { AgentConfig, AgentHooks, AgentPlugin, ChatResult, ModelPricing, StreamEvent, ThinkingConfig, TokenUsage, } from "./agent";
|
|
40
40
|
export { InMemoryStore } from "./memory";
|
|
41
41
|
export type { MemoryStore } from "./memory";
|
|
42
|
-
export { loadSkills } from "./skills";
|
|
42
|
+
export { loadSkills, loadSkillMetadata, loadSkillReference, listSkillReferences, } from "./skills";
|
|
43
43
|
export type { ShopifyClientConfig, GraphQLResponse } from "./shopify";
|
|
44
44
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,QAAQ,CAAC;AAGhB,OAAO,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACnE,YAAY,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAGlB,YAAY,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -87,6 +87,54 @@ async function createAuthenticatedConfig() {
|
|
|
87
87
|
` + " - SHOPIFY_CLIENT_ID and SHOPIFY_CLIENT_SECRET (OAuth client credentials)");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
// src/skills.ts
|
|
91
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
92
|
+
import { join } from "node:path";
|
|
93
|
+
import { fileURLToPath } from "node:url";
|
|
94
|
+
async function loadSkills(customDir) {
|
|
95
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
96
|
+
const skillParts = [];
|
|
97
|
+
await collectMarkdown(baseDir, skillParts);
|
|
98
|
+
return skillParts.join(`
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
`);
|
|
103
|
+
}
|
|
104
|
+
async function loadSkillMetadata(customDir) {
|
|
105
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
106
|
+
const skillPath = join(baseDir, "SKILL.md");
|
|
107
|
+
return readFile(skillPath, "utf-8");
|
|
108
|
+
}
|
|
109
|
+
async function loadSkillReference(name, customDir) {
|
|
110
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
111
|
+
const refPath = join(baseDir, "references", name.endsWith(".md") ? name : `${name}.md`);
|
|
112
|
+
return readFile(refPath, "utf-8");
|
|
113
|
+
}
|
|
114
|
+
async function listSkillReferences(customDir) {
|
|
115
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
116
|
+
const refDir = join(baseDir, "references");
|
|
117
|
+
const entries = await readdir(refDir);
|
|
118
|
+
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
119
|
+
}
|
|
120
|
+
async function collectMarkdown(dir, parts) {
|
|
121
|
+
let entries;
|
|
122
|
+
try {
|
|
123
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
124
|
+
} catch {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
const fullPath = join(dir, entry.name);
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
await collectMarkdown(fullPath, parts);
|
|
131
|
+
} else if (entry.name.endsWith(".md")) {
|
|
132
|
+
const content = await readFile(fullPath, "utf-8");
|
|
133
|
+
parts.push(content);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
90
138
|
// src/agent.ts
|
|
91
139
|
import Anthropic from "@anthropic-ai/sdk";
|
|
92
140
|
var DEFAULT_SYSTEM_INSTRUCTION = "You're Clawpify. You help run a Shopify store. The merchant texts you to get stuff done";
|
|
@@ -108,6 +156,20 @@ var SHOPIFY_GRAPHQL_TOOL = {
|
|
|
108
156
|
required: ["query"]
|
|
109
157
|
}
|
|
110
158
|
};
|
|
159
|
+
var LOAD_SKILL_REFERENCE_TOOL = {
|
|
160
|
+
name: "load_skill_reference",
|
|
161
|
+
description: "Load a Shopify GraphQL reference document for a specific domain. Available: products, orders, customers, inventory, discounts, collections, fulfillments, refunds, draft-orders, gift-cards, webhooks, locations, marketing, markets, menus, metafields, pages, blogs, files, shipping, shop, subscriptions, translations, segments, bulk-operations. Use this BEFORE writing GraphQL queries to get the correct syntax.",
|
|
162
|
+
input_schema: {
|
|
163
|
+
type: "object",
|
|
164
|
+
properties: {
|
|
165
|
+
reference: {
|
|
166
|
+
type: "string",
|
|
167
|
+
description: "Name of the reference to load (e.g. 'orders', 'products', 'inventory')"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
required: ["reference"]
|
|
171
|
+
}
|
|
172
|
+
};
|
|
111
173
|
var DEFAULT_PRICING = {
|
|
112
174
|
inputPerMillion: 3,
|
|
113
175
|
outputPerMillion: 15,
|
|
@@ -187,8 +249,8 @@ class ShopifyAgent {
|
|
|
187
249
|
}
|
|
188
250
|
registerPlugin(plugin) {
|
|
189
251
|
const name = plugin.tool.name;
|
|
190
|
-
if (name === "shopify_graphql") {
|
|
191
|
-
throw new Error(`Cannot register plugin with reserved tool name "
|
|
252
|
+
if (name === "shopify_graphql" || name === "load_skill_reference") {
|
|
253
|
+
throw new Error(`Cannot register plugin with reserved tool name "${name}"`);
|
|
192
254
|
}
|
|
193
255
|
if (this.plugins.has(name)) {
|
|
194
256
|
throw new Error(`Plugin with tool name "${name}" is already registered`);
|
|
@@ -208,6 +270,7 @@ class ShopifyAgent {
|
|
|
208
270
|
buildTools() {
|
|
209
271
|
const allTools = [
|
|
210
272
|
SHOPIFY_GRAPHQL_TOOL,
|
|
273
|
+
LOAD_SKILL_REFERENCE_TOOL,
|
|
211
274
|
...[...this.plugins.values()].map((p) => p.tool)
|
|
212
275
|
];
|
|
213
276
|
if (allTools.length > 0) {
|
|
@@ -245,6 +308,17 @@ class ShopifyAgent {
|
|
|
245
308
|
return { content: content2, isError: true };
|
|
246
309
|
}
|
|
247
310
|
}
|
|
311
|
+
if (name === "load_skill_reference") {
|
|
312
|
+
try {
|
|
313
|
+
const content2 = await loadSkillReference(input.reference);
|
|
314
|
+
await safeHook(this.hooks.onToolResult, name, content2, false);
|
|
315
|
+
return { content: content2, isError: false };
|
|
316
|
+
} catch (error) {
|
|
317
|
+
const content2 = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
318
|
+
await safeHook(this.hooks.onToolResult, name, content2, true);
|
|
319
|
+
return { content: content2, isError: true };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
248
322
|
if (this.plugins.has(name)) {
|
|
249
323
|
const plugin = this.plugins.get(name);
|
|
250
324
|
try {
|
|
@@ -488,40 +562,11 @@ class InMemoryStore {
|
|
|
488
562
|
this.store.delete(sessionId);
|
|
489
563
|
}
|
|
490
564
|
}
|
|
491
|
-
|
|
492
|
-
// src/skills.ts
|
|
493
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
494
|
-
import { join } from "node:path";
|
|
495
|
-
import { fileURLToPath } from "node:url";
|
|
496
|
-
async function loadSkills(customDir) {
|
|
497
|
-
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
498
|
-
const skillParts = [];
|
|
499
|
-
await collectMarkdown(baseDir, skillParts);
|
|
500
|
-
return skillParts.join(`
|
|
501
|
-
|
|
502
|
-
---
|
|
503
|
-
|
|
504
|
-
`);
|
|
505
|
-
}
|
|
506
|
-
async function collectMarkdown(dir, parts) {
|
|
507
|
-
let entries;
|
|
508
|
-
try {
|
|
509
|
-
entries = await readdir(dir, { withFileTypes: true });
|
|
510
|
-
} catch {
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
for (const entry of entries) {
|
|
514
|
-
const fullPath = join(dir, entry.name);
|
|
515
|
-
if (entry.isDirectory()) {
|
|
516
|
-
await collectMarkdown(fullPath, parts);
|
|
517
|
-
} else if (entry.name.endsWith(".md")) {
|
|
518
|
-
const content = await readFile(fullPath, "utf-8");
|
|
519
|
-
parts.push(content);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
565
|
export {
|
|
524
566
|
loadSkills,
|
|
567
|
+
loadSkillReference,
|
|
568
|
+
loadSkillMetadata,
|
|
569
|
+
listSkillReferences,
|
|
525
570
|
getAccessToken,
|
|
526
571
|
createShopifyClient,
|
|
527
572
|
createAuthenticatedConfig,
|
package/dist/skills.d.ts
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
* Works both locally and when installed as an npm package.
|
|
4
4
|
*
|
|
5
5
|
* Optionally provide a custom directory path to load skills from.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Use `loadSkillMetadata()` for the system prompt and
|
|
8
|
+
* `loadSkillReference(name)` to load individual references on demand.
|
|
6
9
|
*/
|
|
7
10
|
export declare function loadSkills(customDir?: string): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Load only the SKILL.md metadata file (Level 1+2).
|
|
13
|
+
* Use this for the system prompt instead of `loadSkills()` to keep context small.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadSkillMetadata(customDir?: string): Promise<string>;
|
|
16
|
+
/**
|
|
17
|
+
* Load a specific reference file on demand (Level 3).
|
|
18
|
+
* Returns the content of `clawpify/references/{name}.md`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadSkillReference(name: string, customDir?: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* List available reference file names (without the `.md` extension).
|
|
23
|
+
*/
|
|
24
|
+
export declare function listSkillReferences(customDir?: string): Promise<string[]>;
|
|
8
25
|
//# sourceMappingURL=skills.d.ts.map
|
package/dist/skills.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASpE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAOnB"}
|
package/dist/skills.js
CHANGED
|
@@ -12,6 +12,22 @@ async function loadSkills(customDir) {
|
|
|
12
12
|
|
|
13
13
|
`);
|
|
14
14
|
}
|
|
15
|
+
async function loadSkillMetadata(customDir) {
|
|
16
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
17
|
+
const skillPath = join(baseDir, "SKILL.md");
|
|
18
|
+
return readFile(skillPath, "utf-8");
|
|
19
|
+
}
|
|
20
|
+
async function loadSkillReference(name, customDir) {
|
|
21
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
22
|
+
const refPath = join(baseDir, "references", name.endsWith(".md") ? name : `${name}.md`);
|
|
23
|
+
return readFile(refPath, "utf-8");
|
|
24
|
+
}
|
|
25
|
+
async function listSkillReferences(customDir) {
|
|
26
|
+
const baseDir = customDir ?? join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
27
|
+
const refDir = join(baseDir, "references");
|
|
28
|
+
const entries = await readdir(refDir);
|
|
29
|
+
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
30
|
+
}
|
|
15
31
|
async function collectMarkdown(dir, parts) {
|
|
16
32
|
let entries;
|
|
17
33
|
try {
|
|
@@ -30,5 +46,8 @@ async function collectMarkdown(dir, parts) {
|
|
|
30
46
|
}
|
|
31
47
|
}
|
|
32
48
|
export {
|
|
33
|
-
loadSkills
|
|
49
|
+
loadSkills,
|
|
50
|
+
loadSkillReference,
|
|
51
|
+
loadSkillMetadata,
|
|
52
|
+
listSkillReferences
|
|
34
53
|
};
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import { ShopifyClient } from "./shopify";
|
|
3
3
|
import type { MemoryStore } from "./memory";
|
|
4
|
+
import { loadSkillReference } from "./skills";
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
6
7
|
// Types
|
|
@@ -148,6 +149,23 @@ const SHOPIFY_GRAPHQL_TOOL: Anthropic.Tool = {
|
|
|
148
149
|
},
|
|
149
150
|
};
|
|
150
151
|
|
|
152
|
+
const LOAD_SKILL_REFERENCE_TOOL: Anthropic.Tool = {
|
|
153
|
+
name: "load_skill_reference",
|
|
154
|
+
description:
|
|
155
|
+
"Load a Shopify GraphQL reference document for a specific domain. Available: products, orders, customers, inventory, discounts, collections, fulfillments, refunds, draft-orders, gift-cards, webhooks, locations, marketing, markets, menus, metafields, pages, blogs, files, shipping, shop, subscriptions, translations, segments, bulk-operations. Use this BEFORE writing GraphQL queries to get the correct syntax.",
|
|
156
|
+
input_schema: {
|
|
157
|
+
type: "object" as const,
|
|
158
|
+
properties: {
|
|
159
|
+
reference: {
|
|
160
|
+
type: "string",
|
|
161
|
+
description:
|
|
162
|
+
"Name of the reference to load (e.g. 'orders', 'products', 'inventory')",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
required: ["reference"],
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
151
169
|
/** Default pricing for claude-sonnet-4-5 (USD per million tokens). */
|
|
152
170
|
const DEFAULT_PRICING: ModelPricing = {
|
|
153
171
|
inputPerMillion: 3,
|
|
@@ -266,9 +284,9 @@ export class ShopifyAgent {
|
|
|
266
284
|
/** Register a plugin at runtime. Throws if a tool with the same name already exists. */
|
|
267
285
|
registerPlugin(plugin: AgentPlugin): void {
|
|
268
286
|
const name = plugin.tool.name;
|
|
269
|
-
if (name === "shopify_graphql") {
|
|
287
|
+
if (name === "shopify_graphql" || name === "load_skill_reference") {
|
|
270
288
|
throw new Error(
|
|
271
|
-
`Cannot register plugin with reserved tool name "
|
|
289
|
+
`Cannot register plugin with reserved tool name "${name}"`
|
|
272
290
|
);
|
|
273
291
|
}
|
|
274
292
|
if (this.plugins.has(name)) {
|
|
@@ -297,6 +315,7 @@ export class ShopifyAgent {
|
|
|
297
315
|
private buildTools(): Anthropic.Tool[] {
|
|
298
316
|
const allTools: Anthropic.Tool[] = [
|
|
299
317
|
SHOPIFY_GRAPHQL_TOOL,
|
|
318
|
+
LOAD_SKILL_REFERENCE_TOOL,
|
|
300
319
|
...[...this.plugins.values()].map((p) => p.tool),
|
|
301
320
|
];
|
|
302
321
|
if (allTools.length > 0) {
|
|
@@ -348,6 +367,18 @@ export class ShopifyAgent {
|
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
369
|
|
|
370
|
+
if (name === "load_skill_reference") {
|
|
371
|
+
try {
|
|
372
|
+
const content = await loadSkillReference(input.reference);
|
|
373
|
+
await safeHook(this.hooks.onToolResult, name, content, false);
|
|
374
|
+
return { content, isError: false };
|
|
375
|
+
} catch (error) {
|
|
376
|
+
const content = `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
377
|
+
await safeHook(this.hooks.onToolResult, name, content, true);
|
|
378
|
+
return { content, isError: true };
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
351
382
|
if (this.plugins.has(name)) {
|
|
352
383
|
const plugin = this.plugins.get(name)!;
|
|
353
384
|
try {
|
package/src/index.ts
CHANGED
|
@@ -63,7 +63,12 @@ export { InMemoryStore } from "./memory";
|
|
|
63
63
|
export type { MemoryStore } from "./memory";
|
|
64
64
|
|
|
65
65
|
// Skills loader
|
|
66
|
-
export {
|
|
66
|
+
export {
|
|
67
|
+
loadSkills,
|
|
68
|
+
loadSkillMetadata,
|
|
69
|
+
loadSkillReference,
|
|
70
|
+
listSkillReferences,
|
|
71
|
+
} from "./skills";
|
|
67
72
|
|
|
68
73
|
// Re-export types for convenience
|
|
69
74
|
export type { ShopifyClientConfig, GraphQLResponse } from "./shopify";
|
package/src/skills.ts
CHANGED
|
@@ -7,6 +7,9 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
* Works both locally and when installed as an npm package.
|
|
8
8
|
*
|
|
9
9
|
* Optionally provide a custom directory path to load skills from.
|
|
10
|
+
*
|
|
11
|
+
* @deprecated Use `loadSkillMetadata()` for the system prompt and
|
|
12
|
+
* `loadSkillReference(name)` to load individual references on demand.
|
|
10
13
|
*/
|
|
11
14
|
export async function loadSkills(customDir?: string): Promise<string> {
|
|
12
15
|
const baseDir =
|
|
@@ -19,6 +22,53 @@ export async function loadSkills(customDir?: string): Promise<string> {
|
|
|
19
22
|
return skillParts.join("\n\n---\n\n");
|
|
20
23
|
}
|
|
21
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Load only the SKILL.md metadata file (Level 1+2).
|
|
27
|
+
* Use this for the system prompt instead of `loadSkills()` to keep context small.
|
|
28
|
+
*/
|
|
29
|
+
export async function loadSkillMetadata(
|
|
30
|
+
customDir?: string
|
|
31
|
+
): Promise<string> {
|
|
32
|
+
const baseDir =
|
|
33
|
+
customDir ??
|
|
34
|
+
join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
35
|
+
const skillPath = join(baseDir, "SKILL.md");
|
|
36
|
+
return readFile(skillPath, "utf-8");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Load a specific reference file on demand (Level 3).
|
|
41
|
+
* Returns the content of `clawpify/references/{name}.md`.
|
|
42
|
+
*/
|
|
43
|
+
export async function loadSkillReference(
|
|
44
|
+
name: string,
|
|
45
|
+
customDir?: string
|
|
46
|
+
): Promise<string> {
|
|
47
|
+
const baseDir =
|
|
48
|
+
customDir ??
|
|
49
|
+
join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
50
|
+
const refPath = join(
|
|
51
|
+
baseDir,
|
|
52
|
+
"references",
|
|
53
|
+
name.endsWith(".md") ? name : `${name}.md`
|
|
54
|
+
);
|
|
55
|
+
return readFile(refPath, "utf-8");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* List available reference file names (without the `.md` extension).
|
|
60
|
+
*/
|
|
61
|
+
export async function listSkillReferences(
|
|
62
|
+
customDir?: string
|
|
63
|
+
): Promise<string[]> {
|
|
64
|
+
const baseDir =
|
|
65
|
+
customDir ??
|
|
66
|
+
join(fileURLToPath(import.meta.url), "..", "..", "clawpify");
|
|
67
|
+
const refDir = join(baseDir, "references");
|
|
68
|
+
const entries = await readdir(refDir);
|
|
69
|
+
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
70
|
+
}
|
|
71
|
+
|
|
22
72
|
async function collectMarkdown(
|
|
23
73
|
dir: string,
|
|
24
74
|
parts: string[]
|