@matimo/core 0.1.0-alpha.8 → 0.1.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 (173) hide show
  1. package/README.md +341 -14
  2. package/dist/approval/approval-handler.d.ts +5 -1
  3. package/dist/approval/approval-handler.d.ts.map +1 -1
  4. package/dist/approval/approval-handler.js +6 -0
  5. package/dist/approval/approval-handler.js.map +1 -1
  6. package/dist/core/schema.d.ts +41 -10
  7. package/dist/core/schema.d.ts.map +1 -1
  8. package/dist/core/schema.js +40 -4
  9. package/dist/core/schema.js.map +1 -1
  10. package/dist/core/skill-content-parser.d.ts +91 -0
  11. package/dist/core/skill-content-parser.d.ts.map +1 -0
  12. package/dist/core/skill-content-parser.js +248 -0
  13. package/dist/core/skill-content-parser.js.map +1 -0
  14. package/dist/core/skill-loader.d.ts +46 -0
  15. package/dist/core/skill-loader.d.ts.map +1 -0
  16. package/dist/core/skill-loader.js +310 -0
  17. package/dist/core/skill-loader.js.map +1 -0
  18. package/dist/core/skill-registry.d.ts +131 -0
  19. package/dist/core/skill-registry.d.ts.map +1 -0
  20. package/dist/core/skill-registry.js +316 -0
  21. package/dist/core/skill-registry.js.map +1 -0
  22. package/dist/core/tfidf-embedding.d.ts +45 -0
  23. package/dist/core/tfidf-embedding.d.ts.map +1 -0
  24. package/dist/core/tfidf-embedding.js +199 -0
  25. package/dist/core/tfidf-embedding.js.map +1 -0
  26. package/dist/core/tool-loader.d.ts +3 -1
  27. package/dist/core/tool-loader.d.ts.map +1 -1
  28. package/dist/core/tool-loader.js +33 -10
  29. package/dist/core/tool-loader.js.map +1 -1
  30. package/dist/core/types.d.ts +203 -6
  31. package/dist/core/types.d.ts.map +1 -1
  32. package/dist/encodings/parameter-encoding.d.ts +1 -1
  33. package/dist/encodings/parameter-encoding.d.ts.map +1 -1
  34. package/dist/encodings/parameter-encoding.js +9 -4
  35. package/dist/encodings/parameter-encoding.js.map +1 -1
  36. package/dist/errors/matimo-error.d.ts +11 -2
  37. package/dist/errors/matimo-error.d.ts.map +1 -1
  38. package/dist/errors/matimo-error.js +25 -1
  39. package/dist/errors/matimo-error.js.map +1 -1
  40. package/dist/executors/command-executor.d.ts +9 -2
  41. package/dist/executors/command-executor.d.ts.map +1 -1
  42. package/dist/executors/command-executor.js +29 -5
  43. package/dist/executors/command-executor.js.map +1 -1
  44. package/dist/executors/function-executor.d.ts +10 -3
  45. package/dist/executors/function-executor.d.ts.map +1 -1
  46. package/dist/executors/function-executor.js +44 -24
  47. package/dist/executors/function-executor.js.map +1 -1
  48. package/dist/executors/http-executor.d.ts +79 -4
  49. package/dist/executors/http-executor.d.ts.map +1 -1
  50. package/dist/executors/http-executor.js +232 -28
  51. package/dist/executors/http-executor.js.map +1 -1
  52. package/dist/index.d.ts +25 -3
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +19 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/integrations/langchain.d.ts +55 -0
  57. package/dist/integrations/langchain.d.ts.map +1 -1
  58. package/dist/integrations/langchain.js +71 -4
  59. package/dist/integrations/langchain.js.map +1 -1
  60. package/dist/logging/logger.d.ts +8 -2
  61. package/dist/logging/logger.d.ts.map +1 -1
  62. package/dist/logging/logger.js.map +1 -1
  63. package/dist/logging/winston-logger.d.ts.map +1 -1
  64. package/dist/logging/winston-logger.js +9 -1
  65. package/dist/logging/winston-logger.js.map +1 -1
  66. package/dist/matimo-instance.d.ts +230 -18
  67. package/dist/matimo-instance.d.ts.map +1 -1
  68. package/dist/matimo-instance.js +739 -40
  69. package/dist/matimo-instance.js.map +1 -1
  70. package/dist/mcp/index.d.ts +18 -0
  71. package/dist/mcp/index.d.ts.map +1 -0
  72. package/dist/mcp/index.js +24 -0
  73. package/dist/mcp/index.js.map +1 -0
  74. package/dist/mcp/mcp-server.d.ts +141 -0
  75. package/dist/mcp/mcp-server.d.ts.map +1 -0
  76. package/dist/mcp/mcp-server.js +754 -0
  77. package/dist/mcp/mcp-server.js.map +1 -0
  78. package/dist/mcp/secrets/aws-resolver.d.ts +41 -0
  79. package/dist/mcp/secrets/aws-resolver.d.ts.map +1 -0
  80. package/dist/mcp/secrets/aws-resolver.js +141 -0
  81. package/dist/mcp/secrets/aws-resolver.js.map +1 -0
  82. package/dist/mcp/secrets/dotenv-resolver.d.ts +23 -0
  83. package/dist/mcp/secrets/dotenv-resolver.d.ts.map +1 -0
  84. package/dist/mcp/secrets/dotenv-resolver.js +94 -0
  85. package/dist/mcp/secrets/dotenv-resolver.js.map +1 -0
  86. package/dist/mcp/secrets/env-resolver.d.ts +14 -0
  87. package/dist/mcp/secrets/env-resolver.d.ts.map +1 -0
  88. package/dist/mcp/secrets/env-resolver.js +27 -0
  89. package/dist/mcp/secrets/env-resolver.js.map +1 -0
  90. package/dist/mcp/secrets/index.d.ts +14 -0
  91. package/dist/mcp/secrets/index.d.ts.map +1 -0
  92. package/dist/mcp/secrets/index.js +13 -0
  93. package/dist/mcp/secrets/index.js.map +1 -0
  94. package/dist/mcp/secrets/resolver-chain.d.ts +34 -0
  95. package/dist/mcp/secrets/resolver-chain.d.ts.map +1 -0
  96. package/dist/mcp/secrets/resolver-chain.js +141 -0
  97. package/dist/mcp/secrets/resolver-chain.js.map +1 -0
  98. package/dist/mcp/secrets/types.d.ts +73 -0
  99. package/dist/mcp/secrets/types.d.ts.map +1 -0
  100. package/dist/mcp/secrets/types.js +8 -0
  101. package/dist/mcp/secrets/types.js.map +1 -0
  102. package/dist/mcp/secrets/vault-resolver.d.ts +43 -0
  103. package/dist/mcp/secrets/vault-resolver.d.ts.map +1 -0
  104. package/dist/mcp/secrets/vault-resolver.js +127 -0
  105. package/dist/mcp/secrets/vault-resolver.js.map +1 -0
  106. package/dist/mcp/tool-converter.d.ts +40 -0
  107. package/dist/mcp/tool-converter.d.ts.map +1 -0
  108. package/dist/mcp/tool-converter.js +185 -0
  109. package/dist/mcp/tool-converter.js.map +1 -0
  110. package/dist/policy/approval-manifest.d.ts +76 -0
  111. package/dist/policy/approval-manifest.d.ts.map +1 -0
  112. package/dist/policy/approval-manifest.js +197 -0
  113. package/dist/policy/approval-manifest.js.map +1 -0
  114. package/dist/policy/content-validator.d.ts +19 -0
  115. package/dist/policy/content-validator.d.ts.map +1 -0
  116. package/dist/policy/content-validator.js +196 -0
  117. package/dist/policy/content-validator.js.map +1 -0
  118. package/dist/policy/default-policy.d.ts +46 -0
  119. package/dist/policy/default-policy.d.ts.map +1 -0
  120. package/dist/policy/default-policy.js +241 -0
  121. package/dist/policy/default-policy.js.map +1 -0
  122. package/dist/policy/events.d.ts +71 -0
  123. package/dist/policy/events.d.ts.map +1 -0
  124. package/dist/policy/events.js +8 -0
  125. package/dist/policy/events.js.map +1 -0
  126. package/dist/policy/index.d.ts +13 -0
  127. package/dist/policy/index.d.ts.map +1 -0
  128. package/dist/policy/index.js +9 -0
  129. package/dist/policy/index.js.map +1 -0
  130. package/dist/policy/integrity-tracker.d.ts +62 -0
  131. package/dist/policy/integrity-tracker.d.ts.map +1 -0
  132. package/dist/policy/integrity-tracker.js +79 -0
  133. package/dist/policy/integrity-tracker.js.map +1 -0
  134. package/dist/policy/policy-loader.d.ts +58 -0
  135. package/dist/policy/policy-loader.d.ts.map +1 -0
  136. package/dist/policy/policy-loader.js +156 -0
  137. package/dist/policy/policy-loader.js.map +1 -0
  138. package/dist/policy/risk-classifier.d.ts +18 -0
  139. package/dist/policy/risk-classifier.d.ts.map +1 -0
  140. package/dist/policy/risk-classifier.js +47 -0
  141. package/dist/policy/risk-classifier.js.map +1 -0
  142. package/dist/policy/types.d.ts +131 -0
  143. package/dist/policy/types.d.ts.map +1 -0
  144. package/dist/policy/types.js +8 -0
  145. package/dist/policy/types.js.map +1 -0
  146. package/package.json +22 -6
  147. package/tools/matimo_approve_tool/definition.yaml +36 -0
  148. package/tools/matimo_approve_tool/matimo_approve_tool.ts +90 -0
  149. package/tools/matimo_create_skill/definition.yaml +46 -0
  150. package/tools/matimo_create_skill/matimo_create_skill.ts +75 -0
  151. package/tools/matimo_create_tool/definition.yaml +48 -0
  152. package/tools/matimo_create_tool/matimo_create_tool.ts +137 -0
  153. package/tools/matimo_get_skill/definition.yaml +60 -0
  154. package/tools/matimo_get_skill/matimo_get_skill.ts +182 -0
  155. package/tools/matimo_get_tool/definition.yaml +36 -0
  156. package/tools/matimo_get_tool/matimo_get_tool.ts +56 -0
  157. package/tools/matimo_get_tool_status/definition.yaml +42 -0
  158. package/tools/matimo_get_tool_status/matimo_get_tool_status.ts +101 -0
  159. package/tools/matimo_list_skills/definition.yaml +52 -0
  160. package/tools/matimo_list_skills/matimo_list_skills.ts +138 -0
  161. package/tools/matimo_list_user_tools/definition.yaml +32 -0
  162. package/tools/matimo_list_user_tools/matimo_list_user_tools.ts +74 -0
  163. package/tools/matimo_reload_tools/definition.yaml +35 -0
  164. package/tools/matimo_reload_tools/matimo_reload_tools.ts +29 -0
  165. package/tools/matimo_search_tools/definition.yaml +32 -0
  166. package/tools/matimo_search_tools/matimo_search_tools.ts +82 -0
  167. package/tools/matimo_validate_skill/definition.yaml +43 -0
  168. package/tools/matimo_validate_skill/matimo_validate_skill.ts +137 -0
  169. package/tools/matimo_validate_tool/definition.yaml +34 -0
  170. package/tools/matimo_validate_tool/matimo_validate_tool.ts +168 -0
  171. package/tools/read/read.ts +0 -2
  172. package/tools/shared/skill-validation.ts +335 -0
  173. package/LICENSE +0 -21
@@ -0,0 +1,75 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getGlobalMatimoLogger } from '@matimo/core';
4
+ import {
5
+ validateSkillName,
6
+ parseSkillContent,
7
+ validateFrontmatter,
8
+ } from '../shared/skill-validation';
9
+
10
+ interface CreateSkillParams {
11
+ name: string;
12
+ content: string;
13
+ target_dir?: string;
14
+ }
15
+
16
+ interface CreateSkillResult {
17
+ success: boolean;
18
+ path?: string;
19
+ message: string;
20
+ }
21
+
22
+ /**
23
+ * Create a new skill following the Agent Skills specification.
24
+ *
25
+ * Validates the name (lowercase, hyphens, max 64 chars), ensures frontmatter
26
+ * has required fields (name, description), and enforces that the frontmatter
27
+ * name matches the directory name.
28
+ *
29
+ * @see https://agentskills.io/specification
30
+ */
31
+ export default async function matimoCreateSkill(
32
+ params: CreateSkillParams,
33
+ ): Promise<CreateSkillResult> {
34
+ const logger = getGlobalMatimoLogger();
35
+ const targetDir = params.target_dir || './matimo-tools/skills';
36
+
37
+ // Step 1: Validate the skill name against Agent Skills spec
38
+ const nameResult = validateSkillName(params.name);
39
+ if (!nameResult.valid) {
40
+ return { success: false, message: nameResult.error! };
41
+ }
42
+
43
+ // Step 2: Parse and validate frontmatter
44
+ const parseResult = parseSkillContent(params.content);
45
+ if (!parseResult.success) {
46
+ return { success: false, message: parseResult.error! };
47
+ }
48
+
49
+ const { frontmatter } = parseResult.parsed!;
50
+
51
+ // Step 3: Validate frontmatter fields + name must match directory
52
+ const fmResult = validateFrontmatter(frontmatter, params.name);
53
+ if (!fmResult.valid) {
54
+ const firstError = fmResult.issues.find(i => i.severity === 'error');
55
+ return { success: false, message: firstError!.message };
56
+ }
57
+
58
+ // Step 4: Write to disk
59
+ const skillDirPath = path.resolve(targetDir, params.name);
60
+ fs.mkdirSync(skillDirPath, { recursive: true });
61
+
62
+ const filePath = path.join(skillDirPath, 'SKILL.md');
63
+ fs.writeFileSync(filePath, params.content, 'utf-8');
64
+
65
+ logger.info('matimo_create_skill: skill created', {
66
+ name: params.name,
67
+ path: filePath,
68
+ });
69
+
70
+ return {
71
+ success: true,
72
+ path: filePath,
73
+ message: `Skill "${params.name}" created successfully.`,
74
+ };
75
+ }
@@ -0,0 +1,48 @@
1
+ name: matimo_create_tool
2
+ version: '1.0.0'
3
+ description: Create a new tool definition on disk. Validates the YAML, forces draft status and requires_approval, and writes the tool to the target directory.
4
+ requires_approval: true
5
+ tags:
6
+ - matimo
7
+ - meta
8
+ - creation
9
+
10
+ parameters:
11
+ name:
12
+ type: string
13
+ required: true
14
+ description: Name for the new tool (snake_case, no path traversal)
15
+ yaml_content:
16
+ type: string
17
+ required: true
18
+ description: The YAML content of the tool definition
19
+ target_dir:
20
+ type: string
21
+ required: false
22
+ description: Directory to create the tool in (default ./matimo-tools)
23
+ proposed_by:
24
+ type: string
25
+ required: false
26
+ description: Identifier of who proposed this tool
27
+ justification:
28
+ type: string
29
+ required: false
30
+ description: Reason for creating this tool
31
+
32
+ execution:
33
+ type: function
34
+ code: './matimo_create_tool.ts'
35
+
36
+ output_schema:
37
+ type: object
38
+ properties:
39
+ success:
40
+ type: boolean
41
+ path:
42
+ type: string
43
+ riskLevel:
44
+ type: string
45
+ status:
46
+ type: string
47
+ message:
48
+ type: string
@@ -0,0 +1,137 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import {
5
+ validateToolDefinition,
6
+ validateToolContent,
7
+ classifyRisk,
8
+ getTierForTool,
9
+ getGlobalMatimoLogger,
10
+ } from '@matimo/core';
11
+ import type { Violation } from '@matimo/core';
12
+
13
+ interface CreateParams {
14
+ name: string;
15
+ yaml_content: string;
16
+ target_dir?: string;
17
+ proposed_by?: string;
18
+ justification?: string;
19
+ }
20
+
21
+ interface CreateResult {
22
+ success: boolean;
23
+ path?: string;
24
+ riskLevel?: string;
25
+ status?: string;
26
+ /** Signals what approval state the tool is in after creation.
27
+ * - `pending`: requires human approval before execution (untrusted source, non-trivial risk)
28
+ * - `auto-approved`: low-risk read-only GET tool, can be used immediately
29
+ * - `approved`: manually approved (set externally by matimo_approve_tool)
30
+ * - `rejected`: policy blocked the tool
31
+ */
32
+ approvalState?: 'pending' | 'auto-approved' | 'approved' | 'rejected';
33
+ message: string;
34
+ errors?: string[];
35
+ }
36
+
37
+ const UNSAFE_NAME_PATTERN = /[/\\]|\.\.|[\x00-\x1f]/;
38
+
39
+ export default async function matimoCreateTool(
40
+ params: CreateParams,
41
+ ): Promise<CreateResult> {
42
+ const logger = getGlobalMatimoLogger();
43
+ const targetDir = params.target_dir || './matimo-tools';
44
+
45
+ // Step 1: Sanitize name
46
+ if (!params.name || params.name.trim().length === 0) {
47
+ return { success: false, message: 'Tool name is required' };
48
+ }
49
+ if (UNSAFE_NAME_PATTERN.test(params.name)) {
50
+ return { success: false, message: 'Tool name contains invalid characters (path traversal, backslash, or control characters)' };
51
+ }
52
+ if (params.name.startsWith('matimo_')) {
53
+ return { success: false, message: 'Tool name cannot start with reserved namespace "matimo_"' };
54
+ }
55
+
56
+ // Step 2: Parse YAML
57
+ let parsed: Record<string, unknown>;
58
+ try {
59
+ parsed = yaml.load(params.yaml_content) as Record<string, unknown>;
60
+ if (!parsed || typeof parsed !== 'object') {
61
+ return { success: false, message: 'YAML must parse to an object' };
62
+ }
63
+ } catch (err) {
64
+ return { success: false, message: `YAML parse error: ${(err as Error).message}` };
65
+ }
66
+
67
+ // Step 3: Force safety fields
68
+ parsed.name = params.name;
69
+ parsed.requires_approval = true;
70
+ parsed.status = 'draft';
71
+
72
+ // Step 4: Validate against schema
73
+ const yamlStr = yaml.dump(parsed);
74
+ try {
75
+ validateToolDefinition(yaml.load(yamlStr));
76
+ } catch (err) {
77
+ return { success: false, message: `Schema validation failed: ${(err as Error).message}` };
78
+ }
79
+
80
+ // Step 5: Run content validator
81
+ const tool = validateToolDefinition(yaml.load(yamlStr));
82
+ const validation = validateToolContent(tool, { source: 'untrusted' });
83
+ const criticalOrHigh = validation.violations.filter(
84
+ (v: Violation) => v.severity === 'critical' || v.severity === 'high',
85
+ );
86
+ if (criticalOrHigh.length > 0) {
87
+ return {
88
+ success: false,
89
+ message: 'Tool failed policy validation',
90
+ errors: criticalOrHigh.map((v: Violation) => `[${v.severity}] ${v.rule}: ${v.message}`),
91
+ };
92
+ }
93
+
94
+ // Step 6: Classify risk + tier
95
+ const riskLevel = classifyRisk(tool);
96
+ const tier = getTierForTool(tool);
97
+ const approvalState: CreateResult['approvalState'] = tier === 'auto' ? 'auto-approved' : 'pending';
98
+
99
+ // Step 7: Write to disk
100
+ const toolDirPath = path.resolve(targetDir, params.name);
101
+ fs.mkdirSync(toolDirPath, { recursive: true });
102
+
103
+ let header = '';
104
+ if (params.proposed_by) {
105
+ header += `# Proposed by: ${params.proposed_by}\n`;
106
+ }
107
+ if (params.justification) {
108
+ header += `# Justification: ${params.justification}\n`;
109
+ }
110
+ if (header) {
111
+ header += '\n';
112
+ }
113
+
114
+ const filePath = path.join(toolDirPath, 'definition.yaml');
115
+ fs.writeFileSync(filePath, header + yamlStr, 'utf-8');
116
+
117
+ logger.info('matimo_create_tool: tool created', {
118
+ name: params.name,
119
+ path: filePath,
120
+ riskLevel,
121
+ approvalState,
122
+ });
123
+
124
+ const message =
125
+ approvalState === 'auto-approved'
126
+ ? 'Tool created and auto-approved (low-risk read-only). Ready for use.'
127
+ : 'Tool created as draft. Requires approval before execution. Use matimo_approve_tool to promote.';
128
+
129
+ return {
130
+ success: true,
131
+ path: filePath,
132
+ riskLevel,
133
+ status: 'draft',
134
+ approvalState,
135
+ message,
136
+ };
137
+ }
@@ -0,0 +1,60 @@
1
+ name: matimo_get_skill
2
+ version: '1.0.0'
3
+ description: >-
4
+ Level 2 activation / Level 3 resource access — read a skill's SKILL.md
5
+ content and metadata, or read a specific bundled resource file (scripts,
6
+ references, assets). Returns frontmatter fields, markdown instructions, and
7
+ a listing of bundled resources per the Agent Skills specification.
8
+ requires_approval: false
9
+ tags:
10
+ - matimo
11
+ - meta
12
+ - skill
13
+ parameters:
14
+ name:
15
+ type: string
16
+ required: true
17
+ description: Name of the skill to retrieve (must match the skill's directory name)
18
+ skills_dir:
19
+ type: string
20
+ required: false
21
+ description: Directory containing skills (default ./matimo-tools/skills)
22
+ file:
23
+ type: string
24
+ required: false
25
+ description: >-
26
+ Optional path to a bundled resource file within the skill directory
27
+ (e.g. "scripts/extract.py" or "references/FORMS.md"). If provided,
28
+ returns that file's content instead of SKILL.md (Level 3 access).
29
+
30
+ execution:
31
+ type: function
32
+ code: './matimo_get_skill.ts'
33
+
34
+ output_schema:
35
+ type: object
36
+ properties:
37
+ success:
38
+ type: boolean
39
+ name:
40
+ type: string
41
+ description:
42
+ type: string
43
+ content:
44
+ type: string
45
+ description: Full content of SKILL.md or the requested resource file
46
+ path:
47
+ type: string
48
+ license:
49
+ type: string
50
+ compatibility:
51
+ type: string
52
+ metadata:
53
+ type: object
54
+ resources:
55
+ type: object
56
+ description: >-
57
+ Bundled resources listing (scripts, references, assets, other) —
58
+ only included when reading SKILL.md, not when reading a specific file
59
+ message:
60
+ type: string
@@ -0,0 +1,182 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getGlobalMatimoLogger, getGlobalMatimoInstance, ToolLoader, SkillSummary } from '@matimo/core';
4
+ import { parseSkillContent, listBundledResources, type BundledResources } from '../shared/skill-validation';
5
+
6
+ interface GetSkillParams {
7
+ name: string;
8
+ skills_dir?: string;
9
+ file?: string;
10
+ }
11
+
12
+ interface GetSkillResult {
13
+ success: boolean;
14
+ name?: string;
15
+ description?: string;
16
+ content?: string;
17
+ path?: string;
18
+ message: string;
19
+ license?: string;
20
+ compatibility?: string;
21
+ metadata?: Record<string, string>;
22
+ resources?: BundledResources;
23
+ }
24
+
25
+ /** Path traversal detection — defense-in-depth. */
26
+ const UNSAFE_NAME_PATTERN = /[/\\]|\.\.|[\x00-\x1f]/;
27
+
28
+ /**
29
+ * Helper: Find skill directory using auto-discovery (like matimo_list_skills)
30
+ */
31
+ function findSkillDir(skillName: string, explicitSkillsDir?: string): string | null {
32
+ // Try explicit skills_dir first
33
+ if (explicitSkillsDir) {
34
+ const skillPath = path.join(explicitSkillsDir, skillName, 'SKILL.md');
35
+ if (fs.existsSync(skillPath)) {
36
+ return path.join(explicitSkillsDir, skillName);
37
+ }
38
+ }
39
+
40
+ // Try MatimoInstance
41
+ try {
42
+ const matimo = getGlobalMatimoInstance();
43
+ if (matimo) {
44
+ const skills = matimo.listSkills();
45
+ const found = skills?.find((s) => s.name === skillName);
46
+ if (found) {
47
+ const skillPath = (found as SkillSummary & { _path?: string })._path;
48
+ if (skillPath && fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
49
+ return skillPath;
50
+ }
51
+ }
52
+ }
53
+ } catch {
54
+ // Fall through
55
+ }
56
+
57
+ // Auto-discover from @matimo/* packages
58
+ try {
59
+ const toolLoader = new ToolLoader();
60
+ const discoveredPaths = toolLoader.autoDiscoverPackages();
61
+ for (const toolPath of discoveredPaths) {
62
+ const pkgDir = path.dirname(toolPath);
63
+ const skillPath = path.join(pkgDir, 'skills', skillName, 'SKILL.md');
64
+ if (fs.existsSync(skillPath)) {
65
+ return path.join(pkgDir, 'skills', skillName);
66
+ }
67
+ }
68
+ } catch {
69
+ // Fall through
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Read a skill's content by name — Level 2 activation (SKILL.md) or
77
+ * Level 3 resource access (bundled file).
78
+ *
79
+ * When called without `file`, returns SKILL.md content + metadata + resource listing.
80
+ * When called with `file`, returns the contents of that bundled resource file.
81
+ *
82
+ * Skills are discovered in this order (priority):
83
+ * 1. Explicit skills_dir if provided
84
+ * 2. Global MatimoInstance (if initialized)
85
+ * 3. Auto-discovered @matimo/* packages
86
+ *
87
+ * @see https://agentskills.io/specification
88
+ */
89
+ export default async function matimoGetSkill(
90
+ params: GetSkillParams,
91
+ ): Promise<GetSkillResult> {
92
+ const logger = getGlobalMatimoLogger();
93
+
94
+ if (!params.name || params.name.trim().length === 0) {
95
+ return { success: false, message: 'Skill name is required' };
96
+ }
97
+
98
+ if (UNSAFE_NAME_PATTERN.test(params.name)) {
99
+ return { success: false, message: 'Skill name contains invalid characters' };
100
+ }
101
+
102
+ // Find skill directory using auto-discovery
103
+ const skillDir = findSkillDir(params.name, params.skills_dir);
104
+ if (!skillDir) {
105
+ return { success: false, message: `Skill "${params.name}" not found` };
106
+ }
107
+
108
+ const skillPath = path.join(skillDir, 'SKILL.md');
109
+
110
+ // Level 3: Read a specific bundled resource file
111
+ if (params.file) {
112
+ // For file paths, allow forward slashes but reject path traversal
113
+ if (/\.\.|\\/u.test(params.file) || /[\x00-\x1f]/.test(params.file)) {
114
+ return { success: false, message: 'File path contains invalid characters' };
115
+ }
116
+
117
+ const resourcePath = path.join(skillDir, params.file);
118
+ // Verify the resolved path stays within the skill directory
119
+ const resolvedPath = path.resolve(resourcePath);
120
+ const resolvedSkillDir = path.resolve(skillDir);
121
+ if (!resolvedPath.startsWith(resolvedSkillDir + path.sep) && resolvedPath !== resolvedSkillDir) {
122
+ return { success: false, message: 'File path escapes the skill directory' };
123
+ }
124
+
125
+ if (!fs.existsSync(resourcePath)) {
126
+ return { success: false, message: `Resource file "${params.file}" not found in skill "${params.name}"` };
127
+ }
128
+
129
+ try {
130
+ const fileContent = fs.readFileSync(resourcePath, 'utf-8');
131
+ return {
132
+ success: true,
133
+ name: params.name,
134
+ content: fileContent,
135
+ path: resourcePath,
136
+ message: `Resource file "${params.file}" retrieved successfully.`,
137
+ };
138
+ } catch (err) {
139
+ return { success: false, message: `Failed to read resource file: ${(err as Error).message}` };
140
+ }
141
+ }
142
+
143
+ // Level 2: Read SKILL.md + metadata + resource listing
144
+ try {
145
+ const rawContent = fs.readFileSync(skillPath, 'utf-8');
146
+ const parseResult = parseSkillContent(rawContent);
147
+
148
+ const result: GetSkillResult = {
149
+ success: true,
150
+ name: params.name,
151
+ content: rawContent,
152
+ path: skillPath,
153
+ message: 'Skill retrieved successfully.',
154
+ };
155
+
156
+ if (parseResult.success && parseResult.parsed) {
157
+ const { frontmatter } = parseResult.parsed;
158
+ result.name = frontmatter.name || params.name;
159
+ result.description = frontmatter.description || '';
160
+ if (frontmatter.license) result.license = frontmatter.license;
161
+ if (frontmatter.compatibility) result.compatibility = frontmatter.compatibility;
162
+ if (frontmatter.metadata) result.metadata = frontmatter.metadata;
163
+ }
164
+
165
+ // List bundled resources (Level 3 discovery)
166
+ result.resources = listBundledResources(skillDir);
167
+
168
+ logger.info('matimo_get_skill: skill retrieved', {
169
+ name: params.name,
170
+ path: skillPath,
171
+ });
172
+
173
+ return result;
174
+ } catch (err) {
175
+ const errorMsg = (err as Error).message;
176
+ logger.error('matimo_get_skill: failed to read skill', {
177
+ name: params.name,
178
+ error: errorMsg,
179
+ });
180
+ return { success: false, message: `Failed to read skill: ${errorMsg}` };
181
+ }
182
+ }
@@ -0,0 +1,36 @@
1
+ name: matimo_get_tool
2
+ version: '1.0.0'
3
+ description: Retrieve the full definition of a tool — raw YAML content and parsed fields. Use before editing or cloning a tool.
4
+ requires_approval: false
5
+ tags:
6
+ - matimo
7
+ - meta
8
+ - discovery
9
+
10
+ parameters:
11
+ name:
12
+ type: string
13
+ required: true
14
+ description: Name of the tool to retrieve
15
+ tool_dir:
16
+ type: string
17
+ required: false
18
+ description: Directory containing the tool (default ./matimo-tools)
19
+
20
+ execution:
21
+ type: function
22
+ code: './matimo_get_tool.ts'
23
+
24
+ output_schema:
25
+ type: object
26
+ properties:
27
+ found:
28
+ type: boolean
29
+ name:
30
+ type: string
31
+ yaml_content:
32
+ type: string
33
+ definition:
34
+ type: object
35
+ message:
36
+ type: string
@@ -0,0 +1,56 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { validateToolDefinition, getGlobalMatimoLogger } from '@matimo/core';
5
+
6
+ interface GetToolParams {
7
+ name: string;
8
+ tool_dir?: string;
9
+ }
10
+
11
+ interface GetToolResult {
12
+ found: boolean;
13
+ name?: string;
14
+ yaml_content?: string;
15
+ definition?: Record<string, unknown>;
16
+ message: string;
17
+ }
18
+
19
+ export default async function matimoGetTool(params: GetToolParams): Promise<GetToolResult> {
20
+ const logger = getGlobalMatimoLogger();
21
+ const toolDir = params.tool_dir ?? './matimo-tools';
22
+
23
+ const defPath = path.join(toolDir, params.name, 'definition.yaml');
24
+ if (!fs.existsSync(defPath)) {
25
+ logger.warn('matimo_get_tool: tool not found', { name: params.name, path: defPath });
26
+ return { found: false, message: `Tool "${params.name}" not found at ${defPath}` };
27
+ }
28
+
29
+ const yamlContent = fs.readFileSync(defPath, 'utf-8');
30
+
31
+ let definition: Record<string, unknown>;
32
+ try {
33
+ const parsed = yaml.load(yamlContent);
34
+ const validated = validateToolDefinition(parsed);
35
+ // Omit internal _definitionPath from the returned object
36
+ const { _definitionPath: _, ...rest } = validated as Record<string, unknown>;
37
+ definition = rest;
38
+ } catch (err) {
39
+ return {
40
+ found: true,
41
+ name: params.name,
42
+ yaml_content: yamlContent,
43
+ message: `Tool YAML is invalid: ${(err as Error).message}`,
44
+ };
45
+ }
46
+
47
+ logger.debug('matimo_get_tool: retrieved', { name: params.name });
48
+
49
+ return {
50
+ found: true,
51
+ name: params.name,
52
+ yaml_content: yamlContent,
53
+ definition,
54
+ message: `Tool "${params.name}" retrieved successfully`,
55
+ };
56
+ }
@@ -0,0 +1,42 @@
1
+ name: matimo_get_tool_status
2
+ version: '1.0.0'
3
+ description: Get the current status, risk level, and approval state of a tool. Works for both pending (agent-created) and approved tools.
4
+ requires_approval: true
5
+ tags:
6
+ - matimo
7
+ - meta
8
+ - approval
9
+
10
+ parameters:
11
+ name:
12
+ type: string
13
+ required: true
14
+ description: Name of the tool to check
15
+ tool_dir:
16
+ type: string
17
+ required: false
18
+ description: Directory containing the tool (default ./matimo-tools)
19
+
20
+ execution:
21
+ type: function
22
+ code: './matimo_get_tool_status.ts'
23
+
24
+ output_schema:
25
+ type: object
26
+ properties:
27
+ found:
28
+ type: boolean
29
+ name:
30
+ type: string
31
+ status:
32
+ type: string
33
+ riskLevel:
34
+ type: string
35
+ approvalState:
36
+ type: string
37
+ approvedAt:
38
+ type: string
39
+ approvedBy:
40
+ type: string
41
+ message:
42
+ type: string