@clawpify/skills 1.0.5 → 1.0.7

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/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 reference document for a specific domain. Available Shopify references: 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. Available document references: docx, pdf, pdf-forms, pdf-reference, pptx, pptx-editing, pptx-creating, xlsx. Use this BEFORE writing queries or processing documents to get the correct syntax and workflow.",
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', 'docx', 'pdf', 'xlsx')"
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 "shopify_graphql"`);
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
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASpE"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawpify/skills",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Shopify Agent SDK — query and manage Shopify stores via GraphQL Admin API with AI agents and MCP",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
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 reference document for a specific domain. Available Shopify references: 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. Available document references: docx, pdf, pdf-forms, pdf-reference, pptx, pptx-editing, pptx-creating, xlsx. Use this BEFORE writing queries or processing documents to get the correct syntax and workflow.",
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', 'docx', 'pdf', 'xlsx')",
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 "shopify_graphql"`
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 { loadSkills } from "./skills";
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[]