@matimo/core 0.1.0-alpha.12.1 → 0.1.0-alpha.14
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 +169 -8
- 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 +29 -8
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/core/schema.js +10 -3
- 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/types.d.ts +155 -6
- package/dist/core/types.d.ts.map +1 -1
- package/dist/errors/matimo-error.d.ts +3 -1
- package/dist/errors/matimo-error.d.ts.map +1 -1
- package/dist/errors/matimo-error.js +2 -0
- package/dist/errors/matimo-error.js.map +1 -1
- package/dist/executors/command-executor.d.ts.map +1 -1
- package/dist/executors/command-executor.js +13 -2
- package/dist/executors/command-executor.js.map +1 -1
- package/dist/executors/function-executor.d.ts.map +1 -1
- package/dist/executors/function-executor.js +33 -20
- package/dist/executors/function-executor.js.map +1 -1
- package/dist/index.d.ts +20 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -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 +66 -0
- package/dist/integrations/langchain.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 +171 -6
- package/dist/matimo-instance.d.ts.map +1 -1
- package/dist/matimo-instance.js +602 -13
- package/dist/matimo-instance.js.map +1 -1
- package/dist/mcp/mcp-server.d.ts +25 -0
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +128 -21
- package/dist/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/tool-converter.d.ts.map +1 -1
- package/dist/mcp/tool-converter.js +10 -1
- package/dist/mcp/tool-converter.js.map +1 -1
- package/dist/policy/approval-manifest.d.ts +74 -0
- package/dist/policy/approval-manifest.d.ts.map +1 -0
- package/dist/policy/approval-manifest.js +183 -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 +153 -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 +43 -0
- package/dist/policy/risk-classifier.js.map +1 -0
- package/dist/policy/types.d.ts +126 -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 +5 -5
- 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_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_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/shared/skill-validation.ts +335 -0
- package/LICENSE +0 -21
|
@@ -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,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
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import {
|
|
5
|
+
validateToolDefinition,
|
|
6
|
+
classifyRisk,
|
|
7
|
+
getTierForTool,
|
|
8
|
+
ApprovalManifest,
|
|
9
|
+
getGlobalMatimoLogger,
|
|
10
|
+
} from '@matimo/core';
|
|
11
|
+
|
|
12
|
+
interface StatusParams {
|
|
13
|
+
name: string;
|
|
14
|
+
tool_dir?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface StatusResult {
|
|
18
|
+
found: boolean;
|
|
19
|
+
name?: string;
|
|
20
|
+
status?: string;
|
|
21
|
+
riskLevel?: string;
|
|
22
|
+
approvalState?: 'pending' | 'auto-approved' | 'approved' | 'rejected';
|
|
23
|
+
approvedAt?: string;
|
|
24
|
+
approvedBy?: string;
|
|
25
|
+
message: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default async function matimoGetToolStatus(
|
|
29
|
+
params: StatusParams,
|
|
30
|
+
context?: { credentials?: Record<string, string> },
|
|
31
|
+
): Promise<StatusResult> {
|
|
32
|
+
const logger = getGlobalMatimoLogger();
|
|
33
|
+
const toolDir = params.tool_dir || './matimo-tools';
|
|
34
|
+
|
|
35
|
+
const defPath = path.join(toolDir, params.name, 'definition.yaml');
|
|
36
|
+
if (!fs.existsSync(defPath)) {
|
|
37
|
+
logger.warn('matimo_get_tool_status: tool not found', { name: params.name, path: defPath });
|
|
38
|
+
return { found: false, message: `Tool "${params.name}" not found at ${defPath}` };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const yamlContent = fs.readFileSync(defPath, 'utf-8');
|
|
42
|
+
|
|
43
|
+
let tool;
|
|
44
|
+
try {
|
|
45
|
+
const parsed = yaml.load(yamlContent);
|
|
46
|
+
tool = validateToolDefinition(parsed);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
return {
|
|
49
|
+
found: true,
|
|
50
|
+
name: params.name,
|
|
51
|
+
message: `Tool YAML is invalid: ${(err as Error).message}`,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const riskLevel = classifyRisk(tool);
|
|
56
|
+
const tier = getTierForTool(tool);
|
|
57
|
+
|
|
58
|
+
// Determine approval state from manifest
|
|
59
|
+
const approvalDir = path.resolve(toolDir);
|
|
60
|
+
const manifest = new ApprovalManifest(
|
|
61
|
+
approvalDir,
|
|
62
|
+
context?.credentials?.MATIMO_APPROVAL_SECRET,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const hash = manifest.computeHash(yamlContent);
|
|
66
|
+
const approvalRecord = manifest.getApproval(params.name);
|
|
67
|
+
const isApproved = approvalRecord ? manifest.isApproved(params.name, hash) : false;
|
|
68
|
+
const pendingTools = manifest.getPendingTools();
|
|
69
|
+
|
|
70
|
+
let approvalState: StatusResult['approvalState'];
|
|
71
|
+
if (tool.status === 'deprecated') {
|
|
72
|
+
approvalState = 'rejected';
|
|
73
|
+
} else if (isApproved) {
|
|
74
|
+
approvalState = 'approved';
|
|
75
|
+
} else if (tier === 'auto') {
|
|
76
|
+
approvalState = 'auto-approved';
|
|
77
|
+
} else if (pendingTools.includes(params.name)) {
|
|
78
|
+
approvalState = 'pending';
|
|
79
|
+
} else {
|
|
80
|
+
// Tool exists on disk but no pending record and not approved — treat as pending
|
|
81
|
+
approvalState = 'pending';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
logger.debug('matimo_get_tool_status: status retrieved', {
|
|
85
|
+
name: params.name,
|
|
86
|
+
status: tool.status,
|
|
87
|
+
riskLevel,
|
|
88
|
+
approvalState,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
found: true,
|
|
93
|
+
name: params.name,
|
|
94
|
+
status: tool.status ?? 'draft',
|
|
95
|
+
riskLevel,
|
|
96
|
+
approvalState,
|
|
97
|
+
approvedAt: approvalRecord?.approvedAt,
|
|
98
|
+
approvedBy: approvalRecord?.approvedBy,
|
|
99
|
+
message: `Tool "${params.name}" is ${approvalState} (${riskLevel} risk)`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: matimo_list_skills
|
|
2
|
+
version: '1.0.0'
|
|
3
|
+
description: >-
|
|
4
|
+
Level 1 metadata discovery — list all skills available to the system.
|
|
5
|
+
|
|
6
|
+
Skills are auto-discovered in this order (priority):
|
|
7
|
+
1. From global Matimo instance (if initialized)
|
|
8
|
+
2. From @matimo/* packages in node_modules (auto-discovery like tools)
|
|
9
|
+
3. From core skills bundled with @matimo/core
|
|
10
|
+
4. From optional explicit skills_dir (if provided)
|
|
11
|
+
|
|
12
|
+
Returns each skill's name, description, and optional frontmatter fields
|
|
13
|
+
(license, compatibility, metadata) per the Agent Skills specification.
|
|
14
|
+
|
|
15
|
+
requires_approval: false
|
|
16
|
+
tags:
|
|
17
|
+
- matimo
|
|
18
|
+
- meta
|
|
19
|
+
- skill
|
|
20
|
+
- discovery
|
|
21
|
+
|
|
22
|
+
parameters:
|
|
23
|
+
skills_dir:
|
|
24
|
+
type: string
|
|
25
|
+
required: false
|
|
26
|
+
description: >-
|
|
27
|
+
Optional directory to scan for skills. If provided, loads skills directly
|
|
28
|
+
from this path (each skill is a subdirectory containing a SKILL.md file
|
|
29
|
+
with YAML frontmatter).
|
|
30
|
+
|
|
31
|
+
Paths are resolved relative to the current working directory where the tool
|
|
32
|
+
is executed. For reliability, use absolute paths or verify relative paths
|
|
33
|
+
are correct from the execution context.
|
|
34
|
+
|
|
35
|
+
If not provided, returns skills from the global Matimo instance with
|
|
36
|
+
auto-discovered @matimo/*/skills and user-created skills.
|
|
37
|
+
|
|
38
|
+
execution:
|
|
39
|
+
type: function
|
|
40
|
+
code: './matimo_list_skills.ts'
|
|
41
|
+
|
|
42
|
+
output_schema:
|
|
43
|
+
type: object
|
|
44
|
+
properties:
|
|
45
|
+
skills:
|
|
46
|
+
type: array
|
|
47
|
+
description: >-
|
|
48
|
+
List of discovered skills with name, description, path, and optional
|
|
49
|
+
fields (license, compatibility, metadata)
|
|
50
|
+
total:
|
|
51
|
+
type: number
|
|
52
|
+
description: Total number of skills found
|