@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.
- package/README.md +341 -14
- package/dist/approval/approval-handler.d.ts +5 -1
- package/dist/approval/approval-handler.d.ts.map +1 -1
- package/dist/approval/approval-handler.js +6 -0
- package/dist/approval/approval-handler.js.map +1 -1
- package/dist/core/schema.d.ts +41 -10
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +40 -4
- package/dist/core/schema.js.map +1 -1
- package/dist/core/skill-content-parser.d.ts +91 -0
- package/dist/core/skill-content-parser.d.ts.map +1 -0
- package/dist/core/skill-content-parser.js +248 -0
- package/dist/core/skill-content-parser.js.map +1 -0
- package/dist/core/skill-loader.d.ts +46 -0
- package/dist/core/skill-loader.d.ts.map +1 -0
- package/dist/core/skill-loader.js +310 -0
- package/dist/core/skill-loader.js.map +1 -0
- package/dist/core/skill-registry.d.ts +131 -0
- package/dist/core/skill-registry.d.ts.map +1 -0
- package/dist/core/skill-registry.js +316 -0
- package/dist/core/skill-registry.js.map +1 -0
- package/dist/core/tfidf-embedding.d.ts +45 -0
- package/dist/core/tfidf-embedding.d.ts.map +1 -0
- package/dist/core/tfidf-embedding.js +199 -0
- package/dist/core/tfidf-embedding.js.map +1 -0
- package/dist/core/tool-loader.d.ts +3 -1
- package/dist/core/tool-loader.d.ts.map +1 -1
- package/dist/core/tool-loader.js +33 -10
- package/dist/core/tool-loader.js.map +1 -1
- package/dist/core/types.d.ts +203 -6
- package/dist/core/types.d.ts.map +1 -1
- package/dist/encodings/parameter-encoding.d.ts +1 -1
- package/dist/encodings/parameter-encoding.d.ts.map +1 -1
- package/dist/encodings/parameter-encoding.js +9 -4
- package/dist/encodings/parameter-encoding.js.map +1 -1
- package/dist/errors/matimo-error.d.ts +11 -2
- package/dist/errors/matimo-error.d.ts.map +1 -1
- package/dist/errors/matimo-error.js +25 -1
- package/dist/errors/matimo-error.js.map +1 -1
- package/dist/executors/command-executor.d.ts +9 -2
- package/dist/executors/command-executor.d.ts.map +1 -1
- package/dist/executors/command-executor.js +29 -5
- package/dist/executors/command-executor.js.map +1 -1
- package/dist/executors/function-executor.d.ts +10 -3
- package/dist/executors/function-executor.d.ts.map +1 -1
- package/dist/executors/function-executor.js +44 -24
- package/dist/executors/function-executor.js.map +1 -1
- package/dist/executors/http-executor.d.ts +79 -4
- package/dist/executors/http-executor.d.ts.map +1 -1
- package/dist/executors/http-executor.js +232 -28
- package/dist/executors/http-executor.js.map +1 -1
- package/dist/index.d.ts +25 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/langchain.d.ts +55 -0
- package/dist/integrations/langchain.d.ts.map +1 -1
- package/dist/integrations/langchain.js +71 -4
- package/dist/integrations/langchain.js.map +1 -1
- package/dist/logging/logger.d.ts +8 -2
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js.map +1 -1
- package/dist/logging/winston-logger.d.ts.map +1 -1
- package/dist/logging/winston-logger.js +9 -1
- package/dist/logging/winston-logger.js.map +1 -1
- package/dist/matimo-instance.d.ts +230 -18
- package/dist/matimo-instance.d.ts.map +1 -1
- package/dist/matimo-instance.js +739 -40
- package/dist/matimo-instance.js.map +1 -1
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +24 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/mcp-server.d.ts +141 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -0
- package/dist/mcp/mcp-server.js +754 -0
- package/dist/mcp/mcp-server.js.map +1 -0
- package/dist/mcp/secrets/aws-resolver.d.ts +41 -0
- package/dist/mcp/secrets/aws-resolver.d.ts.map +1 -0
- package/dist/mcp/secrets/aws-resolver.js +141 -0
- package/dist/mcp/secrets/aws-resolver.js.map +1 -0
- package/dist/mcp/secrets/dotenv-resolver.d.ts +23 -0
- package/dist/mcp/secrets/dotenv-resolver.d.ts.map +1 -0
- package/dist/mcp/secrets/dotenv-resolver.js +94 -0
- package/dist/mcp/secrets/dotenv-resolver.js.map +1 -0
- package/dist/mcp/secrets/env-resolver.d.ts +14 -0
- package/dist/mcp/secrets/env-resolver.d.ts.map +1 -0
- package/dist/mcp/secrets/env-resolver.js +27 -0
- package/dist/mcp/secrets/env-resolver.js.map +1 -0
- package/dist/mcp/secrets/index.d.ts +14 -0
- package/dist/mcp/secrets/index.d.ts.map +1 -0
- package/dist/mcp/secrets/index.js +13 -0
- package/dist/mcp/secrets/index.js.map +1 -0
- package/dist/mcp/secrets/resolver-chain.d.ts +34 -0
- package/dist/mcp/secrets/resolver-chain.d.ts.map +1 -0
- package/dist/mcp/secrets/resolver-chain.js +141 -0
- package/dist/mcp/secrets/resolver-chain.js.map +1 -0
- package/dist/mcp/secrets/types.d.ts +73 -0
- package/dist/mcp/secrets/types.d.ts.map +1 -0
- package/dist/mcp/secrets/types.js +8 -0
- package/dist/mcp/secrets/types.js.map +1 -0
- package/dist/mcp/secrets/vault-resolver.d.ts +43 -0
- package/dist/mcp/secrets/vault-resolver.d.ts.map +1 -0
- package/dist/mcp/secrets/vault-resolver.js +127 -0
- package/dist/mcp/secrets/vault-resolver.js.map +1 -0
- package/dist/mcp/tool-converter.d.ts +40 -0
- package/dist/mcp/tool-converter.d.ts.map +1 -0
- package/dist/mcp/tool-converter.js +185 -0
- package/dist/mcp/tool-converter.js.map +1 -0
- package/dist/policy/approval-manifest.d.ts +76 -0
- package/dist/policy/approval-manifest.d.ts.map +1 -0
- package/dist/policy/approval-manifest.js +197 -0
- package/dist/policy/approval-manifest.js.map +1 -0
- package/dist/policy/content-validator.d.ts +19 -0
- package/dist/policy/content-validator.d.ts.map +1 -0
- package/dist/policy/content-validator.js +196 -0
- package/dist/policy/content-validator.js.map +1 -0
- package/dist/policy/default-policy.d.ts +46 -0
- package/dist/policy/default-policy.d.ts.map +1 -0
- package/dist/policy/default-policy.js +241 -0
- package/dist/policy/default-policy.js.map +1 -0
- package/dist/policy/events.d.ts +71 -0
- package/dist/policy/events.d.ts.map +1 -0
- package/dist/policy/events.js +8 -0
- package/dist/policy/events.js.map +1 -0
- package/dist/policy/index.d.ts +13 -0
- package/dist/policy/index.d.ts.map +1 -0
- package/dist/policy/index.js +9 -0
- package/dist/policy/index.js.map +1 -0
- package/dist/policy/integrity-tracker.d.ts +62 -0
- package/dist/policy/integrity-tracker.d.ts.map +1 -0
- package/dist/policy/integrity-tracker.js +79 -0
- package/dist/policy/integrity-tracker.js.map +1 -0
- package/dist/policy/policy-loader.d.ts +58 -0
- package/dist/policy/policy-loader.d.ts.map +1 -0
- package/dist/policy/policy-loader.js +156 -0
- package/dist/policy/policy-loader.js.map +1 -0
- package/dist/policy/risk-classifier.d.ts +18 -0
- package/dist/policy/risk-classifier.d.ts.map +1 -0
- package/dist/policy/risk-classifier.js +47 -0
- package/dist/policy/risk-classifier.js.map +1 -0
- package/dist/policy/types.d.ts +131 -0
- package/dist/policy/types.d.ts.map +1 -0
- package/dist/policy/types.js +8 -0
- package/dist/policy/types.js.map +1 -0
- package/package.json +22 -6
- package/tools/matimo_approve_tool/definition.yaml +36 -0
- package/tools/matimo_approve_tool/matimo_approve_tool.ts +90 -0
- package/tools/matimo_create_skill/definition.yaml +46 -0
- package/tools/matimo_create_skill/matimo_create_skill.ts +75 -0
- package/tools/matimo_create_tool/definition.yaml +48 -0
- package/tools/matimo_create_tool/matimo_create_tool.ts +137 -0
- package/tools/matimo_get_skill/definition.yaml +60 -0
- package/tools/matimo_get_skill/matimo_get_skill.ts +182 -0
- package/tools/matimo_get_tool/definition.yaml +36 -0
- package/tools/matimo_get_tool/matimo_get_tool.ts +56 -0
- package/tools/matimo_get_tool_status/definition.yaml +42 -0
- package/tools/matimo_get_tool_status/matimo_get_tool_status.ts +101 -0
- package/tools/matimo_list_skills/definition.yaml +52 -0
- package/tools/matimo_list_skills/matimo_list_skills.ts +138 -0
- package/tools/matimo_list_user_tools/definition.yaml +32 -0
- package/tools/matimo_list_user_tools/matimo_list_user_tools.ts +74 -0
- package/tools/matimo_reload_tools/definition.yaml +35 -0
- package/tools/matimo_reload_tools/matimo_reload_tools.ts +29 -0
- package/tools/matimo_search_tools/definition.yaml +32 -0
- package/tools/matimo_search_tools/matimo_search_tools.ts +82 -0
- package/tools/matimo_validate_skill/definition.yaml +43 -0
- package/tools/matimo_validate_skill/matimo_validate_skill.ts +137 -0
- package/tools/matimo_validate_tool/definition.yaml +34 -0
- package/tools/matimo_validate_tool/matimo_validate_tool.ts +168 -0
- package/tools/read/read.ts +0 -2
- package/tools/shared/skill-validation.ts +335 -0
- 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
|