@jaypie/mcp 0.6.6 → 0.7.1

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.
@@ -1,4 +1,5 @@
1
1
  import { fabricService } from '@jaypie/fabric';
2
+ import { createMarkdownStore, normalizeAlias, isValidAlias } from '@jaypie/tildeskill';
2
3
  import * as fs from 'node:fs/promises';
3
4
  import * as path from 'node:path';
4
5
  import { fileURLToPath } from 'node:url';
@@ -8,22 +9,15 @@ import { gt } from 'semver';
8
9
  /**
9
10
  * Docs Suite - Documentation services (skill, version, release_notes)
10
11
  */
11
- const BUILD_VERSION_STRING = "@jaypie/mcp@0.6.6#c35efb78"
12
+ const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.1#82d19d8a"
12
13
  ;
13
14
  const __filename$1 = fileURLToPath(import.meta.url);
14
15
  const __dirname$1 = path.dirname(__filename$1);
15
16
  // From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live
16
17
  const RELEASE_NOTES_PATH = path.join(__dirname$1, "..", "..", "..", "release-notes");
17
18
  const SKILLS_PATH = path.join(__dirname$1, "..", "..", "..", "skills");
18
- function isValidSkillAlias(alias) {
19
- const normalized = alias.toLowerCase().trim();
20
- if (normalized.includes("/") ||
21
- normalized.includes("\\") ||
22
- normalized.includes("..")) {
23
- return false;
24
- }
25
- return /^[a-z0-9_-]+$/.test(normalized);
26
- }
19
+ // Create skill store using tildeskill
20
+ const skillStore = createMarkdownStore({ path: SKILLS_PATH });
27
21
  async function parseReleaseNoteFile(filePath) {
28
22
  try {
29
23
  const content = await fs.readFile(filePath, "utf-8");
@@ -55,24 +49,6 @@ function formatReleaseNoteListItem(note) {
55
49
  }
56
50
  return parts.join(" ");
57
51
  }
58
- async function parseSkillFile(filePath) {
59
- try {
60
- const content = await fs.readFile(filePath, "utf-8");
61
- const alias = path.basename(filePath, ".md");
62
- if (content.startsWith("---")) {
63
- const parsed = matter(content);
64
- const frontMatter = parsed.data;
65
- return {
66
- alias,
67
- description: frontMatter.description,
68
- };
69
- }
70
- return { alias };
71
- }
72
- catch {
73
- return { alias: path.basename(filePath, ".md") };
74
- }
75
- }
76
52
  function formatSkillListItem(skill) {
77
53
  const { alias, description } = skill;
78
54
  if (description) {
@@ -130,40 +106,30 @@ const skillService = fabricService({
130
106
  },
131
107
  },
132
108
  service: async ({ alias: inputAlias }) => {
133
- const alias = (inputAlias || "index").toLowerCase().trim();
134
- if (!isValidSkillAlias(alias)) {
109
+ const alias = normalizeAlias(inputAlias || "index");
110
+ if (!isValidAlias(alias)) {
135
111
  throw new Error(`Invalid skill alias "${alias}". Use alphanumeric characters, hyphens, and underscores only.`);
136
112
  }
137
113
  if (alias === "index") {
138
- const indexPath = path.join(SKILLS_PATH, "index.md");
139
- let indexContent = "";
140
- try {
141
- indexContent = await fs.readFile(indexPath, "utf-8");
142
- if (indexContent.startsWith("---")) {
143
- const parsed = matter(indexContent);
144
- indexContent = parsed.content.trim();
145
- }
146
- }
147
- catch {
148
- // Index file doesn't exist, will just show skill list
149
- }
150
- const files = await fs.readdir(SKILLS_PATH);
151
- const mdFiles = files.filter((file) => file.endsWith(".md") && file !== "index.md");
152
- const skills = await Promise.all(mdFiles.map((file) => parseSkillFile(path.join(SKILLS_PATH, file))));
153
- skills.sort((a, b) => a.alias.localeCompare(b.alias));
114
+ // Get index content
115
+ const indexRecord = await skillStore.get("index");
116
+ const indexContent = indexRecord?.content || "";
117
+ // List all skills except index
118
+ const allSkills = await skillStore.list();
119
+ const skills = allSkills.filter((s) => s.alias !== "index");
154
120
  const skillList = skills.map(formatSkillListItem).join("\n");
155
121
  if (indexContent) {
156
122
  return `${indexContent}\n\n## Available Skills\n\n${skillList}`;
157
123
  }
158
124
  return `# Jaypie Skills\n\n## Available Skills\n\n${skillList}`;
159
125
  }
160
- const skillPath = path.join(SKILLS_PATH, `${alias}.md`);
161
- try {
162
- return await fs.readFile(skillPath, "utf-8");
163
- }
164
- catch {
126
+ const skill = await skillStore.get(alias);
127
+ if (!skill) {
165
128
  throw new Error(`Skill "${alias}" not found. Use skill("index") to list available skills.`);
166
129
  }
130
+ // Return raw file content for non-index skills (preserve frontmatter)
131
+ const skillPath = path.join(SKILLS_PATH, `${alias}.md`);
132
+ return fs.readFile(skillPath, "utf-8");
167
133
  },
168
134
  });
169
135
  // =============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\nimport { gt } from \"semver\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live\nconst RELEASE_NOTES_PATH = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"release-notes\",\n);\nconst SKILLS_PATH = path.join(__dirname, \"..\", \"..\", \"..\", \"skills\");\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\ninterface SkillFrontMatter {\n description?: string;\n}\n\nfunction isValidSkillAlias(alias: string): boolean {\n const normalized = alias.toLowerCase().trim();\n if (\n normalized.includes(\"/\") ||\n normalized.includes(\"\\\\\") ||\n normalized.includes(\"..\")\n ) {\n return false;\n }\n return /^[a-z0-9_-]+$/.test(normalized);\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as ReleaseNoteFrontMatter;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nasync function parseSkillFile(filePath: string): Promise<{\n alias: string;\n description?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const alias = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as SkillFrontMatter;\n return {\n alias,\n description: frontMatter.description,\n };\n }\n\n return { alias };\n } catch {\n return { alias: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatSkillListItem(skill: {\n alias: string;\n description?: string;\n}): string {\n const { alias, description } = skill;\n if (description) {\n return `* ${alias} - ${description}`;\n }\n return `* ${alias}`;\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\nexport const skillService = fabricService({\n alias: \"skill\",\n description:\n \"Access Jaypie development documentation. Pass a skill alias (e.g., 'aws', 'tests', 'errors') to get that documentation. Pass 'index' or no argument to list all available skills.\",\n input: {\n alias: {\n description:\n \"Skill alias (e.g., 'aws', 'tests'). Omit or use 'index' to list all skills.\",\n required: false,\n type: String,\n },\n },\n service: async ({ alias: inputAlias }: { alias?: string }) => {\n const alias = (inputAlias || \"index\").toLowerCase().trim();\n\n if (!isValidSkillAlias(alias)) {\n throw new Error(\n `Invalid skill alias \"${alias}\". Use alphanumeric characters, hyphens, and underscores only.`,\n );\n }\n\n if (alias === \"index\") {\n const indexPath = path.join(SKILLS_PATH, \"index.md\");\n let indexContent = \"\";\n\n try {\n indexContent = await fs.readFile(indexPath, \"utf-8\");\n if (indexContent.startsWith(\"---\")) {\n const parsed = matter(indexContent);\n indexContent = parsed.content.trim();\n }\n } catch {\n // Index file doesn't exist, will just show skill list\n }\n\n const files = await fs.readdir(SKILLS_PATH);\n const mdFiles = files.filter(\n (file) => file.endsWith(\".md\") && file !== \"index.md\",\n );\n\n const skills = await Promise.all(\n mdFiles.map((file) => parseSkillFile(path.join(SKILLS_PATH, file))),\n );\n\n skills.sort((a, b) => a.alias.localeCompare(b.alias));\n\n const skillList = skills.map(formatSkillListItem).join(\"\\n\");\n\n if (indexContent) {\n return `${indexContent}\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n return `# Jaypie Skills\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n\n const skillPath = path.join(SKILLS_PATH, `${alias}.md`);\n try {\n return await fs.readFile(skillPath, \"utf-8\");\n } catch {\n throw new Error(\n `Skill \"${alias}\" not found. Use skill(\"index\") to list available skills.`,\n );\n }\n },\n});\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\nasync function getReleaseNotesHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"release-notes\", \"help.md\"), \"utf-8\");\n}\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return getReleaseNotesHelp();\n }\n\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new Error(\"package is required\");\n if (!p.version) throw new Error(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new Error(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":["__filename","__dirname"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/suites/docs/index.ts"],"sourcesContent":["/**\n * Docs Suite - Documentation services (skill, version, release_notes)\n */\nimport { fabricService } from \"@jaypie/fabric\";\nimport {\n createMarkdownStore,\n isValidAlias,\n normalizeAlias,\n type SkillRecord,\n} from \"@jaypie/tildeskill\";\nimport * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport matter from \"gray-matter\";\nimport { gt } from \"semver\";\n\n// Build-time constants\ndeclare const __BUILD_VERSION_STRING__: string;\nconst BUILD_VERSION_STRING =\n typeof __BUILD_VERSION_STRING__ !== \"undefined\"\n ? __BUILD_VERSION_STRING__\n : \"@jaypie/mcp@0.0.0\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n// From dist/suites/docs/, go up 3 levels to package root where skills/ and release-notes/ live\nconst RELEASE_NOTES_PATH = path.join(\n __dirname,\n \"..\",\n \"..\",\n \"..\",\n \"release-notes\",\n);\nconst SKILLS_PATH = path.join(__dirname, \"..\", \"..\", \"..\", \"skills\");\n\n// Create skill store using tildeskill\nconst skillStore = createMarkdownStore({ path: SKILLS_PATH });\n\n// =============================================================================\n// HELPER FUNCTIONS\n// =============================================================================\n\ninterface ReleaseNoteFrontMatter {\n date?: string;\n summary?: string;\n version?: string;\n}\n\nasync function parseReleaseNoteFile(filePath: string): Promise<{\n date?: string;\n filename: string;\n summary?: string;\n version?: string;\n}> {\n try {\n const content = await fs.readFile(filePath, \"utf-8\");\n const filename = path.basename(filePath, \".md\");\n\n if (content.startsWith(\"---\")) {\n const parsed = matter(content);\n const frontMatter = parsed.data as ReleaseNoteFrontMatter;\n return {\n date: frontMatter.date,\n filename,\n summary: frontMatter.summary,\n version: frontMatter.version || filename,\n };\n }\n\n return { filename, version: filename };\n } catch {\n return { filename: path.basename(filePath, \".md\") };\n }\n}\n\nfunction formatReleaseNoteListItem(note: {\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}): string {\n const { date, packageName, summary, version } = note;\n const parts = [`* ${packageName}@${version}`];\n\n if (date) {\n parts.push(`(${date})`);\n }\n\n if (summary) {\n parts.push(`- ${summary}`);\n }\n\n return parts.join(\" \");\n}\n\nfunction formatSkillListItem(skill: SkillRecord): string {\n const { alias, description } = skill;\n if (description) {\n return `* ${alias} - ${description}`;\n }\n return `* ${alias}`;\n}\n\nasync function getPackageReleaseNotes(packageName: string): Promise<\n Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>\n> {\n const packageDir = path.join(RELEASE_NOTES_PATH, packageName);\n try {\n const files = await fs.readdir(packageDir);\n const mdFiles = files.filter((file) => file.endsWith(\".md\"));\n\n const notes = await Promise.all(\n mdFiles.map(async (file) => {\n const parsed = await parseReleaseNoteFile(path.join(packageDir, file));\n return { ...parsed, packageName };\n }),\n );\n\n return notes.sort((a, b) => {\n if (!a.version || !b.version) return 0;\n try {\n return gt(a.version, b.version) ? -1 : 1;\n } catch {\n return b.version.localeCompare(a.version);\n }\n });\n } catch {\n return [];\n }\n}\n\nfunction filterReleaseNotesSince(\n notes: Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n }>,\n sinceVersion: string,\n): Array<{\n date?: string;\n filename: string;\n packageName: string;\n summary?: string;\n version?: string;\n}> {\n return notes.filter((note) => {\n if (!note.version) return false;\n try {\n return gt(note.version, sinceVersion);\n } catch {\n return false;\n }\n });\n}\n\n// =============================================================================\n// SKILL SERVICE\n// =============================================================================\n\nexport const skillService = fabricService({\n alias: \"skill\",\n description:\n \"Access Jaypie development documentation. Pass a skill alias (e.g., 'aws', 'tests', 'errors') to get that documentation. Pass 'index' or no argument to list all available skills.\",\n input: {\n alias: {\n description:\n \"Skill alias (e.g., 'aws', 'tests'). Omit or use 'index' to list all skills.\",\n required: false,\n type: String,\n },\n },\n service: async ({ alias: inputAlias }: { alias?: string }) => {\n const alias = normalizeAlias(inputAlias || \"index\");\n\n if (!isValidAlias(alias)) {\n throw new Error(\n `Invalid skill alias \"${alias}\". Use alphanumeric characters, hyphens, and underscores only.`,\n );\n }\n\n if (alias === \"index\") {\n // Get index content\n const indexRecord = await skillStore.get(\"index\");\n const indexContent = indexRecord?.content || \"\";\n\n // List all skills except index\n const allSkills = await skillStore.list();\n const skills = allSkills.filter((s) => s.alias !== \"index\");\n const skillList = skills.map(formatSkillListItem).join(\"\\n\");\n\n if (indexContent) {\n return `${indexContent}\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n return `# Jaypie Skills\\n\\n## Available Skills\\n\\n${skillList}`;\n }\n\n const skill = await skillStore.get(alias);\n if (!skill) {\n throw new Error(\n `Skill \"${alias}\" not found. Use skill(\"index\") to list available skills.`,\n );\n }\n\n // Return raw file content for non-index skills (preserve frontmatter)\n const skillPath = path.join(SKILLS_PATH, `${alias}.md`);\n return fs.readFile(skillPath, \"utf-8\");\n },\n});\n\n// =============================================================================\n// VERSION SERVICE\n// =============================================================================\n\nexport const versionService = fabricService({\n alias: \"version\",\n description: `Prints the current version and hash, \\`${BUILD_VERSION_STRING}\\``,\n input: {},\n service: async () => BUILD_VERSION_STRING,\n});\n\n// =============================================================================\n// RELEASE NOTES SERVICE\n// =============================================================================\n\nasync function getReleaseNotesHelp(): Promise<string> {\n return fs.readFile(path.join(__dirname, \"release-notes\", \"help.md\"), \"utf-8\");\n}\n\ninterface ReleaseNotesInput {\n package?: string;\n since_version?: string;\n version?: string;\n}\n\nexport const releaseNotesService = fabricService({\n alias: \"release_notes\",\n description:\n \"Browse Jaypie package release notes. Commands: list, read. Call with no args for help.\",\n input: {\n command: {\n description: \"Command to execute (omit for help)\",\n required: false,\n type: String,\n },\n input: {\n description: \"Command parameters\",\n required: false,\n type: Object,\n },\n },\n service: async ({\n command,\n input: params,\n }: {\n command?: string;\n input?: ReleaseNotesInput;\n }) => {\n if (!command || command === \"help\") {\n return getReleaseNotesHelp();\n }\n\n const p = params || {};\n\n switch (command) {\n case \"list\": {\n const entries = await fs.readdir(RELEASE_NOTES_PATH, {\n withFileTypes: true,\n });\n const packageDirs = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n const packagesToList = p.package\n ? packageDirs.filter((pkg) => pkg === p.package)\n : packageDirs;\n\n if (packagesToList.length === 0 && p.package) {\n return `No release notes found for package \"${p.package}\".`;\n }\n\n const allNotes = await Promise.all(\n packagesToList.map((pkg) => getPackageReleaseNotes(pkg)),\n );\n let flatNotes = allNotes.flat();\n\n if (p.since_version) {\n flatNotes = filterReleaseNotesSince(flatNotes, p.since_version);\n }\n\n if (flatNotes.length === 0) {\n const filterDesc = p.since_version\n ? ` newer than ${p.since_version}`\n : \"\";\n return `No release notes found${filterDesc}.`;\n }\n\n return flatNotes.map(formatReleaseNoteListItem).join(\"\\n\");\n }\n\n case \"read\": {\n if (!p.package) throw new Error(\"package is required\");\n if (!p.version) throw new Error(\"version is required\");\n const filePath = path.join(\n RELEASE_NOTES_PATH,\n p.package,\n `${p.version}.md`,\n );\n return fs.readFile(filePath, \"utf-8\");\n }\n\n default:\n throw new Error(\n `Unknown command: ${command}. Use release_notes() for help.`,\n );\n }\n },\n});\n"],"names":["__filename","__dirname"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.6.6",
3
+ "version": "0.7.1",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,6 +41,7 @@
41
41
  "dependencies": {
42
42
  "@jaypie/fabric": "*",
43
43
  "@jaypie/llm": "*",
44
+ "@jaypie/tildeskill": "*",
44
45
  "@modelcontextprotocol/sdk": "^1.17.0",
45
46
  "commander": "^14.0.0",
46
47
  "gray-matter": "^4.0.3",
@@ -0,0 +1,11 @@
1
+ ---
2
+ version: 1.2.25
3
+ date: 2025-01-28
4
+ summary: Add streaming support to JaypieNextJs construct
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Add `streaming` prop to `JaypieNextJs` construct for Lambda response streaming
10
+ - When `streaming: true`, enables faster Time To First Byte (TTFB) via Lambda response streaming
11
+ - Passes through to underlying `cdk-nextjs-standalone` Nextjs construct
@@ -0,0 +1,17 @@
1
+ ---
2
+ version: 0.7.0
3
+ date: 2025-01-26
4
+ summary: Integrate @jaypie/tildeskill for skill storage abstraction
5
+ ---
6
+
7
+ # @jaypie/mcp v0.7.0
8
+
9
+ ## Changes
10
+
11
+ - **New dependency**: Uses `@jaypie/tildeskill` for skill management
12
+ - **Refactored skill service**: Migrated from inline implementation to tildeskill's `createMarkdownStore`
13
+ - **Improved validation**: Uses tildeskill's `isValidAlias()` and `normalizeAlias()` utilities
14
+
15
+ ## Breaking Changes
16
+
17
+ None - API remains unchanged. The skill service still works exactly the same way.
@@ -0,0 +1,10 @@
1
+ ---
2
+ version: 0.7.1
3
+ date: 2025-01-28
4
+ summary: Add tildeskill documentation
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Add `tildeskill` skill documentation for the new `@jaypie/tildeskill` package
10
+ - Update skills index with tildeskill reference
@@ -0,0 +1,12 @@
1
+ ---
2
+ version: 1.2.20
3
+ date: 2025-01-28
4
+ summary: Updated tildeskill mocks for extended schema
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - Updated `createMemoryStore` and `createMarkdownStore` mocks with new methods:
10
+ - `getByNickname`: Returns `null` by default
11
+ - `search`: Returns empty array by default
12
+ - Added `expandIncludes` mock that returns `record.content` by default
@@ -0,0 +1,36 @@
1
+ ---
2
+ version: 0.1.0
3
+ date: 2025-01-26
4
+ summary: Initial release with markdown and memory storage backends
5
+ ---
6
+
7
+ # @jaypie/tildeskill v0.1.0
8
+
9
+ Initial release of the skill/vocabulary management package with pluggable storage backends.
10
+
11
+ ## Features
12
+
13
+ - **Core types**: `SkillRecord` and `SkillStore` interfaces for skill management
14
+ - **Markdown store**: `createMarkdownStore({ path })` reads skill files from a directory with gray-matter frontmatter support
15
+ - **Memory store**: `createMemoryStore(initial?)` provides in-memory storage for testing
16
+ - **Validation utilities**: `isValidAlias()` and `validateAlias()` for secure alias handling
17
+ - **Normalization utilities**: `normalizeAlias()` and `parseList()` for consistent alias formatting
18
+
19
+ ## Usage
20
+
21
+ ```typescript
22
+ import { createMarkdownStore, createMemoryStore, isValidAlias } from "@jaypie/tildeskill";
23
+
24
+ // File-based store
25
+ const store = createMarkdownStore({ path: "./skills" });
26
+
27
+ // Memory store for testing
28
+ const testStore = createMemoryStore([
29
+ { alias: "test", content: "# Test", description: "Test skill" }
30
+ ]);
31
+
32
+ // Validate aliases
33
+ if (isValidAlias(userInput)) {
34
+ const skill = await store.get(userInput);
35
+ }
36
+ ```
@@ -0,0 +1,28 @@
1
+ ---
2
+ version: 0.2.0
3
+ date: 2025-01-28
4
+ summary: Extended schema with nicknames, tags, includes and enhanced store interface
5
+ ---
6
+
7
+ ## New Features
8
+
9
+ ### Extended SkillRecord Schema
10
+
11
+ - `name`: Display title for the skill
12
+ - `nicknames`: Alternate lookup keys for `getByNickname()`
13
+ - `tags`: Categorization tags for filtering
14
+ - `includes`: Auto-expand other skill content on lookup
15
+
16
+ ### Enhanced SkillStore Interface
17
+
18
+ - `getByNickname(nickname)`: Look up skills by alternate names
19
+ - `list(filter)`: Filter by namespace prefix or tag
20
+ - `search(term)`: Search across alias, name, description, content, and tags
21
+
22
+ ### Include Expansion
23
+
24
+ New `expandIncludes(store, record)` utility that:
25
+ - Prepends included skills' content before the record's content
26
+ - Recursively expands nested includes
27
+ - Prevents circular references
28
+ - Skips missing includes silently
package/skills/skills.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: About the Jaypie skill system
3
- related: agents, index
3
+ related: agents, index, tildeskill
4
4
  ---
5
5
 
6
6
  # Jaypie Skills
@@ -0,0 +1,172 @@
1
+ ---
2
+ description: Skill/vocabulary storage with pluggable backends (pre-1.0)
3
+ related: fabric, mcp, tools
4
+ ---
5
+
6
+ # @jaypie/tildeskill
7
+
8
+ Skill/vocabulary management with pluggable storage backends for AI assistants and documentation systems.
9
+
10
+ ## Overview
11
+
12
+ This package provides a storage abstraction for skill/vocabulary documents with markdown frontmatter support. It enables:
13
+ - Loading skills from markdown files with YAML frontmatter
14
+ - In-memory storage for testing
15
+ - Consistent alias normalization and validation
16
+ - Filtering by namespace and tags
17
+ - Searching across alias, name, description, content, and tags
18
+ - Include expansion for composable skills
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @jaypie/tildeskill
24
+ ```
25
+
26
+ ## Core Types
27
+
28
+ ```typescript
29
+ interface SkillRecord {
30
+ alias: string; // Lookup key (normalized lowercase)
31
+ content: string; // Markdown body
32
+ description?: string; // Brief description from frontmatter
33
+ includes?: string[]; // Auto-expand these skill aliases on lookup
34
+ name?: string; // Display title for the skill
35
+ nicknames?: string[]; // Alternate lookup keys for getByNickname
36
+ related?: string[]; // Related skill aliases
37
+ tags?: string[]; // Categorization tags
38
+ }
39
+
40
+ interface ListFilter {
41
+ namespace?: string; // Namespace prefix matching (e.g., "kit:*")
42
+ tag?: string; // Filter by tag
43
+ }
44
+
45
+ interface SkillStore {
46
+ get(alias: string): Promise<SkillRecord | null>;
47
+ getByNickname(nickname: string): Promise<SkillRecord | null>;
48
+ list(filter?: ListFilter): Promise<SkillRecord[]>;
49
+ put(record: SkillRecord): Promise<SkillRecord>;
50
+ search(term: string): Promise<SkillRecord[]>;
51
+ }
52
+ ```
53
+
54
+ ## Store Factories
55
+
56
+ ### Markdown Store (File-based)
57
+
58
+ ```typescript
59
+ import { createMarkdownStore } from "@jaypie/tildeskill";
60
+
61
+ const store = createMarkdownStore({ path: "./skills" });
62
+
63
+ // Get a specific skill
64
+ const skill = await store.get("aws");
65
+ if (skill) {
66
+ console.log(skill.content);
67
+ }
68
+
69
+ // List all skills
70
+ const skills = await store.list();
71
+ skills.forEach(s => console.log(`${s.alias}: ${s.description}`));
72
+ ```
73
+
74
+ ### Memory Store (Testing)
75
+
76
+ ```typescript
77
+ import { createMemoryStore } from "@jaypie/tildeskill";
78
+
79
+ const store = createMemoryStore([
80
+ { alias: "test", content: "# Test\n\nContent", description: "Test skill" }
81
+ ]);
82
+
83
+ const skill = await store.get("test");
84
+ ```
85
+
86
+ ## Include Expansion
87
+
88
+ ```typescript
89
+ import { expandIncludes, createMemoryStore } from "@jaypie/tildeskill";
90
+
91
+ const store = createMemoryStore([
92
+ { alias: "base", content: "Base content" },
93
+ { alias: "main", content: "Main content", includes: ["base"] },
94
+ ]);
95
+
96
+ const record = await store.get("main");
97
+ const expanded = await expandIncludes(store, record);
98
+ // expanded = "Base content\n\nMain content"
99
+ ```
100
+
101
+ ## Filtering and Search
102
+
103
+ ```typescript
104
+ // Filter by namespace prefix
105
+ const kitSkills = await store.list({ namespace: "kit:" });
106
+
107
+ // Filter by tag
108
+ const cloudSkills = await store.list({ tag: "cloud" });
109
+
110
+ // Search across alias, name, description, content, and tags
111
+ const results = await store.search("lambda");
112
+
113
+ // Lookup by nickname
114
+ const skill = await store.getByNickname("amazon");
115
+ ```
116
+
117
+ ## Validation Utilities
118
+
119
+ ```typescript
120
+ import { isValidAlias, validateAlias, normalizeAlias } from "@jaypie/tildeskill";
121
+
122
+ // Check validity
123
+ isValidAlias("my-skill"); // true
124
+ isValidAlias("../../etc"); // false (path traversal)
125
+
126
+ // Normalize to lowercase
127
+ normalizeAlias("MY-Skill"); // "my-skill"
128
+
129
+ // Validate and normalize (throws on invalid)
130
+ validateAlias("valid"); // returns "valid"
131
+ validateAlias("../bad"); // throws BadRequestError
132
+ ```
133
+
134
+ ## Skill File Format
135
+
136
+ Skill files use YAML frontmatter:
137
+
138
+ ```yaml
139
+ ---
140
+ description: Brief description shown in listings
141
+ includes: base-skill, common-utils
142
+ name: Display Title
143
+ nicknames: alt-name, another-alias
144
+ related: alias1, alias2, alias3
145
+ tags: category1, category2
146
+ ---
147
+
148
+ # Skill Title
149
+
150
+ Markdown content...
151
+ ```
152
+
153
+ All frontmatter fields accept either comma-separated strings or YAML arrays.
154
+
155
+ ## Testing with Mocks
156
+
157
+ ```typescript
158
+ import { mockTildeskill, restoreTildeskill } from "@jaypie/testkit";
159
+
160
+ beforeEach(() => {
161
+ mockTildeskill();
162
+ });
163
+
164
+ afterEach(() => {
165
+ restoreTildeskill();
166
+ });
167
+ ```
168
+
169
+ ## See Also
170
+
171
+ - **`skill("fabric")`** - Service patterns that use tildeskill
172
+ - **`skill("mcp")`** - MCP server that uses tildeskill for skill storage