@within-7/minto 0.3.9 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agents/AgentsCommand.js +459 -655
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/types.js +1 -0
- package/dist/commands/agents/types.js.map +2 -2
- package/dist/commands/agents/utils/fileOperations.js +96 -36
- package/dist/commands/agents/utils/fileOperations.js.map +3 -3
- package/dist/commands/agents/utils/index.js +3 -1
- package/dist/commands/agents/utils/index.js.map +2 -2
- package/dist/commands/context.js +54 -23
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/export.js +673 -93
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/language.js +19 -46
- package/dist/commands/language.js.map +2 -2
- package/dist/commands/mcp-interactive.js +419 -217
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +415 -66
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/permissions.js +75 -49
- package/dist/commands/permissions.js.map +2 -2
- package/dist/commands/plugin.js +882 -185
- package/dist/commands/plugin.js.map +3 -3
- package/dist/commands/resume.js +1 -1
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/sandbox.js +168 -70
- package/dist/commands/sandbox.js.map +2 -2
- package/dist/commands/setup.js +593 -107
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +188 -131
- package/dist/commands/stats.js.map +2 -2
- package/dist/commands/status.js +75 -13
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +138 -174
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js.map +1 -1
- package/dist/components/Help.js +165 -32
- package/dist/components/Help.js.map +2 -2
- package/dist/components/InfoPanel/InfoPanel.js +123 -0
- package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
- package/dist/components/InfoPanel/index.js +5 -0
- package/dist/components/InfoPanel/index.js.map +7 -0
- package/dist/components/InfoPanel/types.js +1 -0
- package/dist/components/InfoPanel/types.js.map +7 -0
- package/dist/components/ModelSelector/BrandTextInput.js +43 -0
- package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +419 -501
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/ModelSelector/WizardContainer.js +45 -0
- package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
- package/dist/components/ModelSelector/index.js +1 -3
- package/dist/components/ModelSelector/index.js.map +2 -2
- package/dist/components/PromptInput.js +5 -5
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
- package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
- package/dist/components/SimpleSelector/index.js +5 -0
- package/dist/components/SimpleSelector/index.js.map +7 -0
- package/dist/components/SimpleSelector/types.js +1 -0
- package/dist/components/SimpleSelector/types.js.map +7 -0
- package/dist/components/StatusOverlayContent.js +21 -0
- package/dist/components/StatusOverlayContent.js.map +7 -0
- package/dist/components/TabbedListView/ScrollableList.js +31 -5
- package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +122 -47
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/core/backupHook.js +29 -0
- package/dist/core/backupHook.js.map +7 -0
- package/dist/core/config/defaults.js +8 -2
- package/dist/core/config/defaults.js.map +2 -2
- package/dist/core/config/schema.js +14 -2
- package/dist/core/config/schema.js.map +2 -2
- package/dist/core/costTracker.js +0 -16
- package/dist/core/costTracker.js.map +2 -2
- package/dist/cost-tracker.js +0 -16
- package/dist/cost-tracker.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +3 -1
- package/dist/entrypoints/bootstrap.js.map +2 -2
- package/dist/entrypoints/cli.js +32 -0
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/i18n/locales/en.js +300 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +301 -2
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/services/customCommands.js +30 -8
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/plugins/lspServers.js +1 -1
- package/dist/services/plugins/lspServers.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +2 -1
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +10 -3
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/plugins/skillMarketplace.js +16 -8
- package/dist/services/plugins/skillMarketplace.js.map +2 -2
- package/dist/services/systemReminder.js +17 -6
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/FileEditTool/FileEditTool.js +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +9 -6
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/types/PermissionMode.js.map +1 -1
- package/dist/types/plugin.js +2 -4
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +1 -4
- package/dist/utils/agentHookExecutor.js.map +2 -2
- package/dist/utils/agentLoader.js +67 -13
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js.map +2 -2
- package/dist/utils/claudeCodeSync.js +439 -0
- package/dist/utils/claudeCodeSync.js.map +7 -0
- package/dist/utils/config.js +1 -23
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/execFileNoThrow.js +2 -1
- package/dist/utils/execFileNoThrow.js.map +2 -2
- package/dist/utils/marketplaceManager.js +80 -43
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messages.js +2 -2
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/pluginInstaller.js +34 -24
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +48 -25
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/repoFetcher.js +110 -0
- package/dist/utils/repoFetcher.js.map +7 -0
- package/dist/utils/skillLoader.js +18 -6
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stringSubstitution.js +4 -5
- package/dist/utils/stringSubstitution.js.map +2 -2
- package/dist/utils/teamConfig.js +153 -13
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/terminal.js +1 -1
- package/dist/utils/terminal.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import { join, dirname, isAbsolute } from "path";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { execFileNoThrow } from "./execFileNoThrow.js";
|
|
5
|
+
async function fetchRepo(source, targetDir) {
|
|
6
|
+
if (source.type === "github") {
|
|
7
|
+
return fetchGitHubRepo(source.repo, source.ref, targetDir);
|
|
8
|
+
}
|
|
9
|
+
const ghMatch = source.url.match(/github\.com[/:]([^/]+\/[^/.]+?)(?:\.git)?$/);
|
|
10
|
+
if (ghMatch) {
|
|
11
|
+
return fetchGitHubRepo(ghMatch[1], source.ref, targetDir);
|
|
12
|
+
}
|
|
13
|
+
await gitCloneFallback(source.url, source.ref, targetDir);
|
|
14
|
+
}
|
|
15
|
+
async function fetchGitHubRepo(repo, ref, targetDir) {
|
|
16
|
+
try {
|
|
17
|
+
await fetchGitHubTarball(repo, ref, targetDir);
|
|
18
|
+
} catch {
|
|
19
|
+
const url = `https://github.com/${repo}.git`;
|
|
20
|
+
await gitCloneFallback(url, ref, targetDir);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function fetchGitHubTarball(repo, ref, targetDir) {
|
|
24
|
+
const url = ref ? `https://api.github.com/repos/${repo}/tarball/${ref}` : `https://api.github.com/repos/${repo}/tarball`;
|
|
25
|
+
await downloadAndExtractTarball(url, targetDir);
|
|
26
|
+
}
|
|
27
|
+
async function downloadAndExtractTarball(url, targetDir) {
|
|
28
|
+
if (!existsSync(targetDir)) {
|
|
29
|
+
mkdirSync(targetDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
const tmpFile = join(
|
|
32
|
+
tmpdir(),
|
|
33
|
+
`minto-tarball-${Date.now()}-${Math.random().toString(36).slice(2)}.tar.gz`
|
|
34
|
+
);
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(url, {
|
|
37
|
+
redirect: "follow",
|
|
38
|
+
signal: AbortSignal.timeout(6e4)
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
42
|
+
}
|
|
43
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
44
|
+
writeFileSync(tmpFile, buffer);
|
|
45
|
+
const result = await execFileNoThrow("tar", [
|
|
46
|
+
"xzf",
|
|
47
|
+
tmpFile,
|
|
48
|
+
"-C",
|
|
49
|
+
targetDir,
|
|
50
|
+
"--strip-components=1"
|
|
51
|
+
]);
|
|
52
|
+
if (result.code !== 0) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`tar extraction failed: ${result.stderr || result.stdout}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
} finally {
|
|
58
|
+
try {
|
|
59
|
+
if (existsSync(tmpFile)) {
|
|
60
|
+
rmSync(tmpFile, { force: true });
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function gitCloneFallback(url, ref, targetDir) {
|
|
67
|
+
await ensureGitEnv();
|
|
68
|
+
if (existsSync(targetDir)) {
|
|
69
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
const args = ["clone", "--depth", "1"];
|
|
72
|
+
if (ref) {
|
|
73
|
+
args.push("--branch", ref);
|
|
74
|
+
}
|
|
75
|
+
args.push(url, targetDir);
|
|
76
|
+
const result = await execFileNoThrow("git", args);
|
|
77
|
+
if (result.code !== 0) {
|
|
78
|
+
throw new Error(`Git clone failed: ${result.stderr || result.stdout}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let gitEnvFixed = false;
|
|
82
|
+
async function ensureGitEnv() {
|
|
83
|
+
if (gitEnvFixed) return;
|
|
84
|
+
gitEnvFixed = true;
|
|
85
|
+
if (process.env.GIT_EXEC_PATH) return;
|
|
86
|
+
try {
|
|
87
|
+
const result = await execFileNoThrow("git", ["--exec-path"]);
|
|
88
|
+
const execPath = result.stdout.trim();
|
|
89
|
+
if (execPath && (execPath.startsWith("//") || !isAbsolute(execPath))) {
|
|
90
|
+
const whichResult = await execFileNoThrow("which", ["git"]);
|
|
91
|
+
const gitBin = whichResult.stdout.trim();
|
|
92
|
+
if (gitBin) {
|
|
93
|
+
const prefix = dirname(dirname(gitBin));
|
|
94
|
+
const fixedExecPath = join(prefix, "libexec", "git-core");
|
|
95
|
+
if (existsSync(fixedExecPath)) {
|
|
96
|
+
process.env.GIT_EXEC_PATH = fixedExecPath;
|
|
97
|
+
const templateDir = join(prefix, "share", "git-core", "templates");
|
|
98
|
+
if (existsSync(templateDir) && !process.env.GIT_TEMPLATE_DIR) {
|
|
99
|
+
process.env.GIT_TEMPLATE_DIR = templateDir;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
fetchRepo
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=repoFetcher.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/repoFetcher.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Repo Fetcher\n *\n * Unified repository fetching utility that replaces git clone with HTTP tarball\n * downloads for GitHub sources (zero git dependency). Falls back to git clone\n * for non-GitHub URLs.\n *\n * GitHub tarball URL pattern: https://github.com/{owner}/{repo}/archive/{ref}.tar.gz\n * macOS/Linux systems have /usr/bin/tar built-in, so no extra dependencies needed.\n */\n\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs'\nimport { join, dirname, isAbsolute } from 'path'\nimport { tmpdir } from 'os'\nimport { execFileNoThrow } from './execFileNoThrow'\n\n/**\n * Source descriptor for fetchRepo\n */\nexport type RepoSource =\n | { type: 'github'; repo: string; ref?: string }\n | { type: 'url'; url: string; ref?: string }\n\n/**\n * Fetch a repository into targetDir.\n *\n * - GitHub sources use HTTP tarball download (no git required)\n * - Non-GitHub URL sources fall back to git clone\n * - On GitHub tarball failure, falls back to git clone\n */\nexport async function fetchRepo(\n source: RepoSource,\n targetDir: string,\n): Promise<void> {\n if (source.type === 'github') {\n return fetchGitHubRepo(source.repo, source.ref, targetDir)\n }\n\n // URL source: check if it's a github.com URL\n const ghMatch = source.url.match(/github\\.com[/:]([^/]+\\/[^/.]+?)(?:\\.git)?$/)\n if (ghMatch) {\n return fetchGitHubRepo(ghMatch[1], source.ref, targetDir)\n }\n\n // Non-GitHub URL: git clone only\n await gitCloneFallback(source.url, source.ref, targetDir)\n}\n\n/**\n * Fetch a GitHub repo: try tarball first, fall back to git clone\n */\nasync function fetchGitHubRepo(\n repo: string,\n ref: string | undefined,\n targetDir: string,\n): Promise<void> {\n try {\n await fetchGitHubTarball(repo, ref, targetDir)\n } catch {\n // Tarball failed \u2014 fall back to git clone\n const url = `https://github.com/${repo}.git`\n await gitCloneFallback(url, ref, targetDir)\n }\n}\n\n/**\n * Download and extract a GitHub tarball into targetDir.\n *\n * GitHub tarballs contain a single top-level directory like `{repo}-{ref}/`.\n * We use `--strip-components=1` to remove that prefix.\n */\nasync function fetchGitHubTarball(\n repo: string,\n ref: string | undefined,\n targetDir: string,\n): Promise<void> {\n // Use GitHub API tarball endpoint \u2014 it auto-resolves the default branch\n // when no ref is given (unlike /archive/ which requires an explicit ref).\n const url = ref\n ? `https://api.github.com/repos/${repo}/tarball/${ref}`\n : `https://api.github.com/repos/${repo}/tarball`\n await downloadAndExtractTarball(url, targetDir)\n}\n\n/**\n * Download a .tar.gz URL to a temp file, then extract into targetDir\n * using the system `tar` command.\n */\nasync function downloadAndExtractTarball(\n url: string,\n targetDir: string,\n): Promise<void> {\n // Ensure target directory exists\n if (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true })\n }\n\n const tmpFile = join(\n tmpdir(),\n `minto-tarball-${Date.now()}-${Math.random().toString(36).slice(2)}.tar.gz`,\n )\n\n try {\n // Download with fetch (built-in in Node 18+ / Bun)\n const response = await fetch(url, {\n redirect: 'follow',\n signal: AbortSignal.timeout(60_000),\n })\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} ${response.statusText}`)\n }\n\n const buffer = Buffer.from(await response.arrayBuffer())\n writeFileSync(tmpFile, buffer)\n\n // Extract with system tar\n const result = await execFileNoThrow('tar', [\n 'xzf',\n tmpFile,\n '-C',\n targetDir,\n '--strip-components=1',\n ])\n\n if (result.code !== 0) {\n throw new Error(\n `tar extraction failed: ${result.stderr || result.stdout}`,\n )\n }\n } finally {\n // Clean up temp file\n try {\n if (existsSync(tmpFile)) {\n rmSync(tmpFile, { force: true })\n }\n } catch {\n // Ignore cleanup errors\n }\n }\n}\n\n/**\n * Git clone fallback for non-GitHub URLs.\n * Includes ensureGitEnv() to fix broken bundled git installations.\n */\nasync function gitCloneFallback(\n url: string,\n ref: string | undefined,\n targetDir: string,\n): Promise<void> {\n await ensureGitEnv()\n\n // git clone requires the target to not exist or be empty.\n // Remove it first (may contain partial tarball extraction artifacts).\n if (existsSync(targetDir)) {\n rmSync(targetDir, { recursive: true, force: true })\n }\n\n const args = ['clone', '--depth', '1']\n if (ref) {\n args.push('--branch', ref)\n }\n args.push(url, targetDir)\n\n const result = await execFileNoThrow('git', args)\n\n if (result.code !== 0) {\n throw new Error(`Git clone failed: ${result.stderr || result.stdout}`)\n }\n}\n\n/**\n * Ensure GIT_EXEC_PATH is correctly set in the environment.\n *\n * Some bundled git binaries (e.g., dugite inside Electron apps) have a broken\n * compiled-in exec-path that resolves to `//libexec/git-core` instead of an\n * absolute path. We detect this and derive the correct path from the git\n * binary's location.\n */\nlet gitEnvFixed = false\nasync function ensureGitEnv(): Promise<void> {\n if (gitEnvFixed) return\n gitEnvFixed = true\n\n // Skip if user already set GIT_EXEC_PATH\n if (process.env.GIT_EXEC_PATH) return\n\n try {\n const result = await execFileNoThrow('git', ['--exec-path'])\n const execPath = result.stdout.trim()\n\n // Detect broken path: starts with // or is not absolute\n if (execPath && (execPath.startsWith('//') || !isAbsolute(execPath))) {\n const whichResult = await execFileNoThrow('which', ['git'])\n const gitBin = whichResult.stdout.trim()\n\n if (gitBin) {\n const prefix = dirname(dirname(gitBin))\n const fixedExecPath = join(prefix, 'libexec', 'git-core')\n\n if (existsSync(fixedExecPath)) {\n process.env.GIT_EXEC_PATH = fixedExecPath\n\n const templateDir = join(prefix, 'share', 'git-core', 'templates')\n if (existsSync(templateDir) && !process.env.GIT_TEMPLATE_DIR) {\n process.env.GIT_TEMPLATE_DIR = templateDir\n }\n }\n }\n }\n } catch {\n // Best-effort; if detection fails, proceed without fixing\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -73,14 +73,26 @@ function scanStandaloneSkillsDirectory(dirPath, source) {
|
|
|
73
73
|
function loadStandaloneSkills() {
|
|
74
74
|
const home = homedir();
|
|
75
75
|
const cwd = getCwd();
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
const
|
|
80
|
-
|
|
76
|
+
const userClaudeDir = join(home, ".claude", "skills");
|
|
77
|
+
const userMintoDir = join(home, ".minto", "skills");
|
|
78
|
+
const projectClaudeDir = join(cwd, ".claude", "skills");
|
|
79
|
+
const projectMintoDir = join(cwd, ".minto", "skills");
|
|
80
|
+
const userClaudeSkills = scanStandaloneSkillsDirectory(userClaudeDir, "user");
|
|
81
|
+
const userMintoSkills = scanStandaloneSkillsDirectory(userMintoDir, "user");
|
|
82
|
+
const projectClaudeSkills = scanStandaloneSkillsDirectory(
|
|
83
|
+
projectClaudeDir,
|
|
81
84
|
"project"
|
|
82
85
|
);
|
|
83
|
-
|
|
86
|
+
const projectMintoSkills = scanStandaloneSkillsDirectory(
|
|
87
|
+
projectMintoDir,
|
|
88
|
+
"project"
|
|
89
|
+
);
|
|
90
|
+
return [
|
|
91
|
+
...userClaudeSkills,
|
|
92
|
+
...userMintoSkills,
|
|
93
|
+
...projectClaudeSkills,
|
|
94
|
+
...projectMintoSkills
|
|
95
|
+
];
|
|
84
96
|
}
|
|
85
97
|
function loadAllSkills() {
|
|
86
98
|
if (skillsCache !== null) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/skillLoader.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Skill Loader\n *\n * Progressive disclosure system for loading skills from plugins and standalone directories.\n * Implements Claude Code skill specification for plugin and standalone skills.\n *\n * Directory Priority (later overrides earlier):\n * 1. Plugin skills\n * 2. ~/.minto/skills/ (user directory)\n * 3. ./.minto/skills/ (project directory - highest priority)\n *\n * Supports two file patterns:\n * - skill-name/SKILL.md (subdirectory pattern)\n * - skill-name.md (flat file pattern)\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'fs'\nimport { join, basename, dirname } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport { LoadedSkill, SkillConfig } from '../types/plugin'\nimport { loadAllPlugins } from './pluginLoader'\nimport { getCwd } from './state'\n\n/**\n * In-memory cache for loaded skills\n * Maps skill name to skill object\n */\nlet skillsCache: Map<string, LoadedSkill> | null = null\n\n/**\n * Cache for fully-loaded skill content\n * Maps skill name to full markdown content\n */\nconst contentCache = new Map<string, string>()\n\n/**\n * Parse a skill file and extract configuration\n * Supports Claude Code skill specification frontmatter fields\n */\nfunction parseSkillFile(\n filePath: string,\n source: 'user' | 'project',\n): LoadedSkill | null {\n try {\n if (!existsSync(filePath)) return null\n\n const fileContent = readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content } = matter(fileContent)\n\n // Determine skill name from frontmatter or filename\n const fileName = basename(filePath, '.md')\n const name =\n frontmatter.name ||\n (fileName === 'SKILL' ? basename(dirname(filePath)) : fileName)\n\n // Parse Claude Code spec fields\n const config: SkillConfig = {\n name,\n description: frontmatter.description || '',\n argumentHint: frontmatter['argument-hint'] || frontmatter.argumentHint,\n disableModelInvocation:\n frontmatter['disable-model-invocation'] ??\n frontmatter.disableModelInvocation,\n userInvocable:\n frontmatter['user-invocable'] ?? frontmatter.userInvocable ?? true,\n allowedTools: parseStringOrArray(\n frontmatter['allowed-tools'] || frontmatter.allowedTools,\n ),\n model: frontmatter.model,\n context: frontmatter.context,\n agent: frontmatter.agent,\n content: content.trim(),\n }\n\n return {\n name,\n filePath,\n config,\n pluginName: 'standalone',\n source,\n }\n } catch (error) {\n console.warn(`Failed to parse skill file ${filePath}:`, error)\n return null\n }\n}\n\n/**\n * Parse a value that could be a string, array, or comma-separated string\n */\nfunction parseStringOrArray(value: unknown): string[] | undefined {\n if (!value) return undefined\n if (Array.isArray(value)) return value.map(String)\n if (typeof value === 'string') {\n return value.split(',').map(s => s.trim())\n }\n return undefined\n}\n\n/**\n * Scan a standalone skills directory\n * Supports both patterns:\n * - skill-name/SKILL.md (subdirectory pattern)\n * - skill-name.md (flat file pattern)\n */\nfunction scanStandaloneSkillsDirectory(\n dirPath: string,\n source: 'user' | 'project',\n): LoadedSkill[] {\n const skills: LoadedSkill[] = []\n\n if (!existsSync(dirPath)) return skills\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true })\n\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n // Pattern 1: skill-name/SKILL.md\n const skillMdPath = join(entryPath, 'SKILL.md')\n if (existsSync(skillMdPath)) {\n const skill = parseSkillFile(skillMdPath, source)\n if (skill) skills.push(skill)\n }\n } else if (entry.name.endsWith('.md') && entry.name !== 'README.md') {\n // Pattern 2: skill-name.md\n const skill = parseSkillFile(entryPath, source)\n if (skill) skills.push(skill)\n }\n }\n } catch (error) {\n console.warn(`Failed to scan skills directory ${dirPath}:`, error)\n }\n\n return skills\n}\n\n/**\n * Load all standalone skills from user and project directories\n */\nfunction loadStandaloneSkills(): LoadedSkill[] {\n const home = homedir()\n const cwd = getCwd()\n\n const
|
|
5
|
-
"mappings": "AAgBA,SAAS,YAAY,cAAc,mBAA6B;AAChE,SAAS,MAAM,UAAU,eAAe;AACxC,SAAS,eAAe;AACxB,OAAO,YAAY;AAEnB,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AAMvB,IAAI,cAA+C;AAMnD,MAAM,eAAe,oBAAI,IAAoB;AAM7C,SAAS,eACP,UACA,QACoB;AACpB,MAAI;AACF,QAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,UAAM,cAAc,aAAa,UAAU,OAAO;AAClD,UAAM,EAAE,MAAM,aAAa,QAAQ,IAAI,OAAO,WAAW;AAGzD,UAAM,WAAW,SAAS,UAAU,KAAK;AACzC,UAAM,OACJ,YAAY,SACX,aAAa,UAAU,SAAS,QAAQ,QAAQ,CAAC,IAAI;AAGxD,UAAM,SAAsB;AAAA,MAC1B;AAAA,MACA,aAAa,YAAY,eAAe;AAAA,MACxC,cAAc,YAAY,eAAe,KAAK,YAAY;AAAA,MAC1D,wBACE,YAAY,0BAA0B,KACtC,YAAY;AAAA,MACd,eACE,YAAY,gBAAgB,KAAK,YAAY,iBAAiB;AAAA,MAChE,cAAc;AAAA,QACZ,YAAY,eAAe,KAAK,YAAY;AAAA,MAC9C;AAAA,MACA,OAAO,YAAY;AAAA,MACnB,SAAS,YAAY;AAAA,MACrB,OAAO,YAAY;AAAA,MACnB,SAAS,QAAQ,KAAK;AAAA,IACxB;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,8BAA8B,QAAQ,KAAK,KAAK;AAC7D,WAAO;AAAA,EACT;AACF;AAKA,SAAS,mBAAmB,OAAsC;AAChE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,MAAM;AACjD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,MAAM,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAQA,SAAS,8BACP,SACA,QACe;AACf,QAAM,SAAwB,CAAC;AAE/B,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,UAAU,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE5D,eAAW,SAAS,SAAS;AAC3B,YAAM,YAAY,KAAK,SAAS,MAAM,IAAI;AAE1C,UAAI,MAAM,YAAY,GAAG;AAEvB,cAAM,cAAc,KAAK,WAAW,UAAU;AAC9C,YAAI,WAAW,WAAW,GAAG;AAC3B,gBAAM,QAAQ,eAAe,aAAa,MAAM;AAChD,cAAI,MAAO,QAAO,KAAK,KAAK;AAAA,QAC9B;AAAA,MACF,WAAW,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,SAAS,aAAa;AAEnE,cAAM,QAAQ,eAAe,WAAW,MAAM;AAC9C,YAAI,MAAO,QAAO,KAAK,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,mCAAmC,OAAO,KAAK,KAAK;AAAA,EACnE;AAEA,SAAO;AACT;AAKA,SAAS,uBAAsC;AAC7C,QAAM,OAAO,QAAQ;AACrB,QAAM,MAAM,OAAO;
|
|
4
|
+
"sourcesContent": ["/**\n * Skill Loader\n *\n * Progressive disclosure system for loading skills from plugins and standalone directories.\n * Implements Claude Code skill specification for plugin and standalone skills.\n *\n * Directory Priority (later overrides earlier):\n * 1. Plugin skills\n * 2. ~/.minto/skills/ (user directory)\n * 3. ./.minto/skills/ (project directory - highest priority)\n *\n * Supports two file patterns:\n * - skill-name/SKILL.md (subdirectory pattern)\n * - skill-name.md (flat file pattern)\n */\n\nimport { existsSync, readFileSync, readdirSync, statSync } from 'fs'\nimport { join, basename, dirname } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport { LoadedSkill, SkillConfig } from '../types/plugin'\nimport { loadAllPlugins } from './pluginLoader'\nimport { getCwd } from './state'\n\n/**\n * In-memory cache for loaded skills\n * Maps skill name to skill object\n */\nlet skillsCache: Map<string, LoadedSkill> | null = null\n\n/**\n * Cache for fully-loaded skill content\n * Maps skill name to full markdown content\n */\nconst contentCache = new Map<string, string>()\n\n/**\n * Parse a skill file and extract configuration\n * Supports Claude Code skill specification frontmatter fields\n */\nfunction parseSkillFile(\n filePath: string,\n source: 'user' | 'project',\n): LoadedSkill | null {\n try {\n if (!existsSync(filePath)) return null\n\n const fileContent = readFileSync(filePath, 'utf-8')\n const { data: frontmatter, content } = matter(fileContent)\n\n // Determine skill name from frontmatter or filename\n const fileName = basename(filePath, '.md')\n const name =\n frontmatter.name ||\n (fileName === 'SKILL' ? basename(dirname(filePath)) : fileName)\n\n // Parse Claude Code spec fields\n const config: SkillConfig = {\n name,\n description: frontmatter.description || '',\n argumentHint: frontmatter['argument-hint'] || frontmatter.argumentHint,\n disableModelInvocation:\n frontmatter['disable-model-invocation'] ??\n frontmatter.disableModelInvocation,\n userInvocable:\n frontmatter['user-invocable'] ?? frontmatter.userInvocable ?? true,\n allowedTools: parseStringOrArray(\n frontmatter['allowed-tools'] || frontmatter.allowedTools,\n ),\n model: frontmatter.model,\n context: frontmatter.context,\n agent: frontmatter.agent,\n content: content.trim(),\n }\n\n return {\n name,\n filePath,\n config,\n pluginName: 'standalone',\n source,\n }\n } catch (error) {\n console.warn(`Failed to parse skill file ${filePath}:`, error)\n return null\n }\n}\n\n/**\n * Parse a value that could be a string, array, or comma-separated string\n */\nfunction parseStringOrArray(value: unknown): string[] | undefined {\n if (!value) return undefined\n if (Array.isArray(value)) return value.map(String)\n if (typeof value === 'string') {\n return value.split(',').map(s => s.trim())\n }\n return undefined\n}\n\n/**\n * Scan a standalone skills directory\n * Supports both patterns:\n * - skill-name/SKILL.md (subdirectory pattern)\n * - skill-name.md (flat file pattern)\n */\nfunction scanStandaloneSkillsDirectory(\n dirPath: string,\n source: 'user' | 'project',\n): LoadedSkill[] {\n const skills: LoadedSkill[] = []\n\n if (!existsSync(dirPath)) return skills\n\n try {\n const entries = readdirSync(dirPath, { withFileTypes: true })\n\n for (const entry of entries) {\n const entryPath = join(dirPath, entry.name)\n\n if (entry.isDirectory()) {\n // Pattern 1: skill-name/SKILL.md\n const skillMdPath = join(entryPath, 'SKILL.md')\n if (existsSync(skillMdPath)) {\n const skill = parseSkillFile(skillMdPath, source)\n if (skill) skills.push(skill)\n }\n } else if (entry.name.endsWith('.md') && entry.name !== 'README.md') {\n // Pattern 2: skill-name.md\n const skill = parseSkillFile(entryPath, source)\n if (skill) skills.push(skill)\n }\n }\n } catch (error) {\n console.warn(`Failed to scan skills directory ${dirPath}:`, error)\n }\n\n return skills\n}\n\n/**\n * Load all standalone skills from user and project directories\n */\nfunction loadStandaloneSkills(): LoadedSkill[] {\n const home = homedir()\n const cwd = getCwd()\n\n // .claude/ dirs are legacy fallbacks, .minto/ dirs take precedence\n const userClaudeDir = join(home, '.claude', 'skills')\n const userMintoDir = join(home, '.minto', 'skills')\n const projectClaudeDir = join(cwd, '.claude', 'skills')\n const projectMintoDir = join(cwd, '.minto', 'skills')\n\n const userClaudeSkills = scanStandaloneSkillsDirectory(userClaudeDir, 'user')\n const userMintoSkills = scanStandaloneSkillsDirectory(userMintoDir, 'user')\n const projectClaudeSkills = scanStandaloneSkillsDirectory(\n projectClaudeDir,\n 'project',\n )\n const projectMintoSkills = scanStandaloneSkillsDirectory(\n projectMintoDir,\n 'project',\n )\n\n return [\n ...userClaudeSkills,\n ...userMintoSkills,\n ...projectClaudeSkills,\n ...projectMintoSkills,\n ]\n}\n\n/**\n * Load all skills from plugins and standalone directories\n * Uses progressive disclosure - only loads metadata (name, description)\n * Content is lazy-loaded when needed\n *\n * Priority: plugin < user < project (later overrides earlier)\n */\nexport function loadAllSkills(): LoadedSkill[] {\n // Return cached skills if available\n if (skillsCache !== null) {\n return Array.from(skillsCache.values())\n }\n\n const pluginSkills: LoadedSkill[] = []\n const plugins = loadAllPlugins()\n\n // Collect all skills from all plugins\n for (const plugin of plugins) {\n if (!plugin.enabled) continue\n\n for (const skill of plugin.skills) {\n // Ensure plugin skills have source field\n pluginSkills.push({\n ...skill,\n source: 'plugin',\n })\n }\n }\n\n // Load standalone skills\n const standaloneSkills = loadStandaloneSkills()\n\n // Build cache map for fast lookups (later entries override earlier ones)\n skillsCache = new Map()\n\n // Add in priority order: plugin < standalone (user < project)\n for (const skill of pluginSkills) {\n skillsCache.set(skill.name, skill)\n }\n for (const skill of standaloneSkills) {\n skillsCache.set(skill.name, skill)\n }\n\n return Array.from(skillsCache.values())\n}\n\n/**\n * Get a specific skill by name\n * Returns undefined if skill not found\n */\nexport function getSkill(name: string): LoadedSkill | undefined {\n // Ensure cache is populated\n if (skillsCache === null) {\n loadAllSkills()\n }\n\n return skillsCache?.get(name)\n}\n\n/**\n * Load the full content of a skill\n * Uses caching to avoid repeated file reads\n */\nexport async function loadSkillContent(skill: LoadedSkill): Promise<string> {\n // Check content cache first\n if (contentCache.has(skill.name)) {\n return contentCache.get(skill.name)!\n }\n\n // If content was already loaded in the skill object, use it\n if (skill.config.content) {\n contentCache.set(skill.name, skill.config.content)\n return skill.config.content\n }\n\n // Otherwise, read from file\n try {\n const fileContent = readFileSync(skill.filePath, 'utf-8')\n const { content } = matter(fileContent)\n const trimmedContent = content.trim()\n\n // Cache the content\n contentCache.set(skill.name, trimmedContent)\n\n // Update the skill object\n skill.config.content = trimmedContent\n\n return trimmedContent\n } catch (error) {\n throw new Error(\n `Failed to load skill content for \"${skill.name}\": ${\n error instanceof Error ? error.message : String(error)\n }`,\n )\n }\n}\n\n/**\n * Search skills by name or description\n * Returns skills matching the query (case-insensitive)\n */\nexport function searchSkills(query: string): LoadedSkill[] {\n const allSkills = loadAllSkills()\n const lowerQuery = query.toLowerCase()\n\n return allSkills.filter(\n skill =>\n skill.name.toLowerCase().includes(lowerQuery) ||\n skill.config.description.toLowerCase().includes(lowerQuery) ||\n skill.pluginName.toLowerCase().includes(lowerQuery),\n )\n}\n\n/**\n * Get skills from a specific plugin\n */\nexport function getSkillsByPlugin(pluginName: string): LoadedSkill[] {\n const allSkills = loadAllSkills()\n return allSkills.filter(skill => skill.pluginName === pluginName)\n}\n\n/**\n * Clear the skills cache (useful for hot-reloading)\n */\nexport function clearSkillsCache(): void {\n skillsCache = null\n contentCache.clear()\n}\n\n/**\n * Get count of available skills\n */\nexport function getSkillCount(): number {\n const skills = loadAllSkills()\n return skills.length\n}\n"],
|
|
5
|
+
"mappings": "AAgBA,SAAS,YAAY,cAAc,mBAA6B;AAChE,SAAS,MAAM,UAAU,eAAe;AACxC,SAAS,eAAe;AACxB,OAAO,YAAY;AAEnB,SAAS,sBAAsB;AAC/B,SAAS,cAAc;AAMvB,IAAI,cAA+C;AAMnD,MAAM,eAAe,oBAAI,IAAoB;AAM7C,SAAS,eACP,UACA,QACoB;AACpB,MAAI;AACF,QAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,UAAM,cAAc,aAAa,UAAU,OAAO;AAClD,UAAM,EAAE,MAAM,aAAa,QAAQ,IAAI,OAAO,WAAW;AAGzD,UAAM,WAAW,SAAS,UAAU,KAAK;AACzC,UAAM,OACJ,YAAY,SACX,aAAa,UAAU,SAAS,QAAQ,QAAQ,CAAC,IAAI;AAGxD,UAAM,SAAsB;AAAA,MAC1B;AAAA,MACA,aAAa,YAAY,eAAe;AAAA,MACxC,cAAc,YAAY,eAAe,KAAK,YAAY;AAAA,MAC1D,wBACE,YAAY,0BAA0B,KACtC,YAAY;AAAA,MACd,eACE,YAAY,gBAAgB,KAAK,YAAY,iBAAiB;AAAA,MAChE,cAAc;AAAA,QACZ,YAAY,eAAe,KAAK,YAAY;AAAA,MAC9C;AAAA,MACA,OAAO,YAAY;AAAA,MACnB,SAAS,YAAY;AAAA,MACrB,OAAO,YAAY;AAAA,MACnB,SAAS,QAAQ,KAAK;AAAA,IACxB;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,8BAA8B,QAAQ,KAAK,KAAK;AAC7D,WAAO;AAAA,EACT;AACF;AAKA,SAAS,mBAAmB,OAAsC;AAChE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,MAAM;AACjD,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,MAAM,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAQA,SAAS,8BACP,SACA,QACe;AACf,QAAM,SAAwB,CAAC;AAE/B,MAAI,CAAC,WAAW,OAAO,EAAG,QAAO;AAEjC,MAAI;AACF,UAAM,UAAU,YAAY,SAAS,EAAE,eAAe,KAAK,CAAC;AAE5D,eAAW,SAAS,SAAS;AAC3B,YAAM,YAAY,KAAK,SAAS,MAAM,IAAI;AAE1C,UAAI,MAAM,YAAY,GAAG;AAEvB,cAAM,cAAc,KAAK,WAAW,UAAU;AAC9C,YAAI,WAAW,WAAW,GAAG;AAC3B,gBAAM,QAAQ,eAAe,aAAa,MAAM;AAChD,cAAI,MAAO,QAAO,KAAK,KAAK;AAAA,QAC9B;AAAA,MACF,WAAW,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,SAAS,aAAa;AAEnE,cAAM,QAAQ,eAAe,WAAW,MAAM;AAC9C,YAAI,MAAO,QAAO,KAAK,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,mCAAmC,OAAO,KAAK,KAAK;AAAA,EACnE;AAEA,SAAO;AACT;AAKA,SAAS,uBAAsC;AAC7C,QAAM,OAAO,QAAQ;AACrB,QAAM,MAAM,OAAO;AAGnB,QAAM,gBAAgB,KAAK,MAAM,WAAW,QAAQ;AACpD,QAAM,eAAe,KAAK,MAAM,UAAU,QAAQ;AAClD,QAAM,mBAAmB,KAAK,KAAK,WAAW,QAAQ;AACtD,QAAM,kBAAkB,KAAK,KAAK,UAAU,QAAQ;AAEpD,QAAM,mBAAmB,8BAA8B,eAAe,MAAM;AAC5E,QAAM,kBAAkB,8BAA8B,cAAc,MAAM;AAC1E,QAAM,sBAAsB;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AACA,QAAM,qBAAqB;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;AASO,SAAS,gBAA+B;AAE7C,MAAI,gBAAgB,MAAM;AACxB,WAAO,MAAM,KAAK,YAAY,OAAO,CAAC;AAAA,EACxC;AAEA,QAAM,eAA8B,CAAC;AACrC,QAAM,UAAU,eAAe;AAG/B,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,OAAO,QAAS;AAErB,eAAW,SAAS,OAAO,QAAQ;AAEjC,mBAAa,KAAK;AAAA,QAChB,GAAG;AAAA,QACH,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,mBAAmB,qBAAqB;AAG9C,gBAAc,oBAAI,IAAI;AAGtB,aAAW,SAAS,cAAc;AAChC,gBAAY,IAAI,MAAM,MAAM,KAAK;AAAA,EACnC;AACA,aAAW,SAAS,kBAAkB;AACpC,gBAAY,IAAI,MAAM,MAAM,KAAK;AAAA,EACnC;AAEA,SAAO,MAAM,KAAK,YAAY,OAAO,CAAC;AACxC;AAMO,SAAS,SAAS,MAAuC;AAE9D,MAAI,gBAAgB,MAAM;AACxB,kBAAc;AAAA,EAChB;AAEA,SAAO,aAAa,IAAI,IAAI;AAC9B;AAMA,eAAsB,iBAAiB,OAAqC;AAE1E,MAAI,aAAa,IAAI,MAAM,IAAI,GAAG;AAChC,WAAO,aAAa,IAAI,MAAM,IAAI;AAAA,EACpC;AAGA,MAAI,MAAM,OAAO,SAAS;AACxB,iBAAa,IAAI,MAAM,MAAM,MAAM,OAAO,OAAO;AACjD,WAAO,MAAM,OAAO;AAAA,EACtB;AAGA,MAAI;AACF,UAAM,cAAc,aAAa,MAAM,UAAU,OAAO;AACxD,UAAM,EAAE,QAAQ,IAAI,OAAO,WAAW;AACtC,UAAM,iBAAiB,QAAQ,KAAK;AAGpC,iBAAa,IAAI,MAAM,MAAM,cAAc;AAG3C,UAAM,OAAO,UAAU;AAEvB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,qCAAqC,MAAM,IAAI,MAC7C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACvD;AAAA,IACF;AAAA,EACF;AACF;AAMO,SAAS,aAAa,OAA8B;AACzD,QAAM,YAAY,cAAc;AAChC,QAAM,aAAa,MAAM,YAAY;AAErC,SAAO,UAAU;AAAA,IACf,WACE,MAAM,KAAK,YAAY,EAAE,SAAS,UAAU,KAC5C,MAAM,OAAO,YAAY,YAAY,EAAE,SAAS,UAAU,KAC1D,MAAM,WAAW,YAAY,EAAE,SAAS,UAAU;AAAA,EACtD;AACF;AAKO,SAAS,kBAAkB,YAAmC;AACnE,QAAM,YAAY,cAAc;AAChC,SAAO,UAAU,OAAO,WAAS,MAAM,eAAe,UAAU;AAClE;AAKO,SAAS,mBAAyB;AACvC,gBAAc;AACd,eAAa,MAAM;AACrB;AAKO,SAAS,gBAAwB;AACtC,QAAM,SAAS,cAAc;AAC7B,SAAO,OAAO;AAChB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -50,10 +50,7 @@ async function substituteVariables(content, args, context = {}) {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
} else {
|
|
53
|
-
result = result.replace(
|
|
54
|
-
/!\`([^`]+)\`/g,
|
|
55
|
-
"(dynamic commands disabled)"
|
|
56
|
-
);
|
|
53
|
+
result = result.replace(/!\`([^`]+)\`/g, "(dynamic commands disabled)");
|
|
57
54
|
}
|
|
58
55
|
return result;
|
|
59
56
|
}
|
|
@@ -86,7 +83,9 @@ function extractSubstitutionPatterns(content) {
|
|
|
86
83
|
for (const match of cmdMatches) {
|
|
87
84
|
patterns.commands.push(match[1]);
|
|
88
85
|
}
|
|
89
|
-
patterns.indexedArgs = [...new Set(patterns.indexedArgs)].sort(
|
|
86
|
+
patterns.indexedArgs = [...new Set(patterns.indexedArgs)].sort(
|
|
87
|
+
(a, b) => a - b
|
|
88
|
+
);
|
|
90
89
|
patterns.variables = [...new Set(patterns.variables)];
|
|
91
90
|
return patterns;
|
|
92
91
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/stringSubstitution.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * String Substitution Engine\n *\n * Implements Claude Code skill specification for variable substitution.\n * Supports the following patterns:\n *\n * - $ARGUMENTS - All arguments as a single string\n * - $ARGUMENTS[N] - Indexed argument (0-based)\n * - $N - Shorthand for $ARGUMENTS[N]\n * - ${VARIABLE_NAME} - Environment variable or context variable\n * - !`command` - Dynamic command execution (with security controls)\n */\n\nimport { exec } from 'child_process'\nimport { promisify } from 'util'\n\nconst execAsync = promisify(exec)\n\n/**\n * Context for string substitution\n */\nexport interface SubstitutionContext {\n sessionId?: string\n cwd?: string\n env?: Record<string, string>\n /**\n * Allow dynamic command execution (!`command`)\n * Default: false (security consideration)\n */\n allowDynamicCommands?: boolean\n /**\n * Timeout for dynamic command execution in milliseconds\n * Default: 10000 (10 seconds)\n */\n commandTimeout?: number\n}\n\n/**\n * Default substitution context\n */\nconst DEFAULT_CONTEXT: Required<SubstitutionContext> = {\n sessionId: '',\n cwd: process.cwd(),\n env: {},\n allowDynamicCommands: false,\n commandTimeout: 10000,\n}\n\n/**\n * Substitute variables in a string\n *\n * @param content - The content to substitute variables in\n * @param args - Arguments string (space-separated)\n * @param context - Substitution context\n * @returns The content with variables substituted\n */\nexport async function substituteVariables(\n content: string,\n args: string,\n context: SubstitutionContext = {},\n): Promise<string> {\n const ctx = { ...DEFAULT_CONTEXT, ...context }\n let result = content\n const argValues = args.trim() ? args.trim().split(/\\s+/) : []\n\n // 1. $ARGUMENTS - All arguments as a single string\n // Use negative lookahead to avoid matching $ARGUMENTS[N]\n result = result.replace(/\\$ARGUMENTS(?!\\[)/g, args)\n\n // 2. $ARGUMENTS[N] - Indexed argument (0-based)\n result = result.replace(/\\$ARGUMENTS\\[(\\d+)\\]/g, (_, indexStr) => {\n const index = parseInt(indexStr, 10)\n return argValues[index] ?? ''\n })\n\n // 3. $N - Shorthand for indexed argument (0-based)\n // Use negative lookahead to avoid matching multi-digit or $ARGUMENTS\n result = result.replace(/\\$(\\d+)(?!\\d)/g, (_, indexStr) => {\n const index = parseInt(indexStr, 10)\n return argValues[index] ?? ''\n })\n\n // 4. ${VARIABLE_NAME} - Context and environment variables\n result = result.replace(/\\$\\{([A-Z_][A-Z0-9_]*)\\}/gi, (_, varName) => {\n // Built-in context variables (Claude Code spec)\n switch (varName.toUpperCase()) {\n case 'CLAUDE_SESSION_ID':\n case 'SESSION_ID':\n return ctx.sessionId || ''\n case 'CWD':\n case 'PWD':\n return ctx.cwd\n default:\n // Check custom context env first, then process.env\n return ctx.env[varName] ?? process.env[varName] ?? ''\n }\n })\n\n // 5. !`command` - Dynamic command execution\n // Only execute if explicitly allowed (security consideration)\n if (ctx.allowDynamicCommands) {\n const commandPattern = /!\\`([^`]+)\\`/g\n const matches = [...result.matchAll(commandPattern)]\n\n for (const match of matches) {\n const command = match[1]\n try {\n const { stdout } = await execAsync(command, {\n timeout: ctx.commandTimeout,\n cwd: ctx.cwd,\n })\n result = result.replace(match[0], stdout.trim())\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n result = result.replace(match[0], `(error: ${errorMessage})`)\n }\n }\n } else {\n // Replace !`command` with placeholder when not allowed\n result = result.replace(
|
|
5
|
-
"mappings": "AAaA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,MAAM,YAAY,UAAU,IAAI;AAwBhC,MAAM,kBAAiD;AAAA,EACrD,WAAW;AAAA,EACX,KAAK,QAAQ,IAAI;AAAA,EACjB,KAAK,CAAC;AAAA,EACN,sBAAsB;AAAA,EACtB,gBAAgB;AAClB;AAUA,eAAsB,oBACpB,SACA,MACA,UAA+B,CAAC,GACf;AACjB,QAAM,MAAM,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAC7C,MAAI,SAAS;AACb,QAAM,YAAY,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAI5D,WAAS,OAAO,QAAQ,sBAAsB,IAAI;AAGlD,WAAS,OAAO,QAAQ,yBAAyB,CAAC,GAAG,aAAa;AAChE,UAAM,QAAQ,SAAS,UAAU,EAAE;AACnC,WAAO,UAAU,KAAK,KAAK;AAAA,EAC7B,CAAC;AAID,WAAS,OAAO,QAAQ,kBAAkB,CAAC,GAAG,aAAa;AACzD,UAAM,QAAQ,SAAS,UAAU,EAAE;AACnC,WAAO,UAAU,KAAK,KAAK;AAAA,EAC7B,CAAC;AAGD,WAAS,OAAO,QAAQ,8BAA8B,CAAC,GAAG,YAAY;AAEpE,YAAQ,QAAQ,YAAY,GAAG;AAAA,MAC7B,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,aAAa;AAAA,MAC1B,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI;AAAA,MACb;AAEE,eAAO,IAAI,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK;AAAA,IACvD;AAAA,EACF,CAAC;AAID,MAAI,IAAI,sBAAsB;AAC5B,UAAM,iBAAiB;AACvB,UAAM,UAAU,CAAC,GAAG,OAAO,SAAS,cAAc,CAAC;AAEnD,eAAW,SAAS,SAAS;AAC3B,YAAM,UAAU,MAAM,CAAC;AACvB,UAAI;AACF,cAAM,EAAE,OAAO,IAAI,MAAM,UAAU,SAAS;AAAA,UAC1C,SAAS,IAAI;AAAA,UACb,KAAK,IAAI;AAAA,QACX,CAAC;AACD,iBAAS,OAAO,QAAQ,MAAM,CAAC,GAAG,OAAO,KAAK,CAAC;AAAA,MACjD,SAAS,OAAO;AACd,cAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACvD,iBAAS,OAAO,QAAQ,MAAM,CAAC,GAAG,WAAW,YAAY,GAAG;AAAA,MAC9D;AAAA,IACF;AAAA,EACF,OAAO;AAEL,aAAS,OAAO
|
|
4
|
+
"sourcesContent": ["/**\n * String Substitution Engine\n *\n * Implements Claude Code skill specification for variable substitution.\n * Supports the following patterns:\n *\n * - $ARGUMENTS - All arguments as a single string\n * - $ARGUMENTS[N] - Indexed argument (0-based)\n * - $N - Shorthand for $ARGUMENTS[N]\n * - ${VARIABLE_NAME} - Environment variable or context variable\n * - !`command` - Dynamic command execution (with security controls)\n */\n\nimport { exec } from 'child_process'\nimport { promisify } from 'util'\n\nconst execAsync = promisify(exec)\n\n/**\n * Context for string substitution\n */\nexport interface SubstitutionContext {\n sessionId?: string\n cwd?: string\n env?: Record<string, string>\n /**\n * Allow dynamic command execution (!`command`)\n * Default: false (security consideration)\n */\n allowDynamicCommands?: boolean\n /**\n * Timeout for dynamic command execution in milliseconds\n * Default: 10000 (10 seconds)\n */\n commandTimeout?: number\n}\n\n/**\n * Default substitution context\n */\nconst DEFAULT_CONTEXT: Required<SubstitutionContext> = {\n sessionId: '',\n cwd: process.cwd(),\n env: {},\n allowDynamicCommands: false,\n commandTimeout: 10000,\n}\n\n/**\n * Substitute variables in a string\n *\n * @param content - The content to substitute variables in\n * @param args - Arguments string (space-separated)\n * @param context - Substitution context\n * @returns The content with variables substituted\n */\nexport async function substituteVariables(\n content: string,\n args: string,\n context: SubstitutionContext = {},\n): Promise<string> {\n const ctx = { ...DEFAULT_CONTEXT, ...context }\n let result = content\n const argValues = args.trim() ? args.trim().split(/\\s+/) : []\n\n // 1. $ARGUMENTS - All arguments as a single string\n // Use negative lookahead to avoid matching $ARGUMENTS[N]\n result = result.replace(/\\$ARGUMENTS(?!\\[)/g, args)\n\n // 2. $ARGUMENTS[N] - Indexed argument (0-based)\n result = result.replace(/\\$ARGUMENTS\\[(\\d+)\\]/g, (_, indexStr) => {\n const index = parseInt(indexStr, 10)\n return argValues[index] ?? ''\n })\n\n // 3. $N - Shorthand for indexed argument (0-based)\n // Use negative lookahead to avoid matching multi-digit or $ARGUMENTS\n result = result.replace(/\\$(\\d+)(?!\\d)/g, (_, indexStr) => {\n const index = parseInt(indexStr, 10)\n return argValues[index] ?? ''\n })\n\n // 4. ${VARIABLE_NAME} - Context and environment variables\n result = result.replace(/\\$\\{([A-Z_][A-Z0-9_]*)\\}/gi, (_, varName) => {\n // Built-in context variables (Claude Code spec)\n switch (varName.toUpperCase()) {\n case 'CLAUDE_SESSION_ID':\n case 'SESSION_ID':\n return ctx.sessionId || ''\n case 'CWD':\n case 'PWD':\n return ctx.cwd\n default:\n // Check custom context env first, then process.env\n return ctx.env[varName] ?? process.env[varName] ?? ''\n }\n })\n\n // 5. !`command` - Dynamic command execution\n // Only execute if explicitly allowed (security consideration)\n if (ctx.allowDynamicCommands) {\n const commandPattern = /!\\`([^`]+)\\`/g\n const matches = [...result.matchAll(commandPattern)]\n\n for (const match of matches) {\n const command = match[1]\n try {\n const { stdout } = await execAsync(command, {\n timeout: ctx.commandTimeout,\n cwd: ctx.cwd,\n })\n result = result.replace(match[0], stdout.trim())\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n result = result.replace(match[0], `(error: ${errorMessage})`)\n }\n }\n } else {\n // Replace !`command` with placeholder when not allowed\n result = result.replace(/!\\`([^`]+)\\`/g, '(dynamic commands disabled)')\n }\n\n return result\n}\n\n/**\n * Check if a string contains substitution patterns\n * Useful for optimization - skip substitution if no patterns present\n */\nexport function hasSubstitutionPatterns(content: string): boolean {\n return /\\$(?:ARGUMENTS|\\d+|\\{[A-Z_][A-Z0-9_]*\\})|!\\`/.test(content)\n}\n\n/**\n * Extract all substitution patterns from a string\n * Useful for validation and documentation\n */\nexport function extractSubstitutionPatterns(content: string): {\n arguments: string[]\n indexedArgs: number[]\n variables: string[]\n commands: string[]\n} {\n const patterns = {\n arguments: [] as string[],\n indexedArgs: [] as number[],\n variables: [] as string[],\n commands: [] as string[],\n }\n\n // $ARGUMENTS\n if (/\\$ARGUMENTS(?!\\[)/.test(content)) {\n patterns.arguments.push('$ARGUMENTS')\n }\n\n // $ARGUMENTS[N]\n const argIndexMatches = content.matchAll(/\\$ARGUMENTS\\[(\\d+)\\]/g)\n for (const match of argIndexMatches) {\n patterns.indexedArgs.push(parseInt(match[1], 10))\n }\n\n // $N\n const shortIndexMatches = content.matchAll(/\\$(\\d+)(?!\\d)/g)\n for (const match of shortIndexMatches) {\n patterns.indexedArgs.push(parseInt(match[1], 10))\n }\n\n // ${VARIABLE}\n const varMatches = content.matchAll(/\\$\\{([A-Z_][A-Z0-9_]*)\\}/gi)\n for (const match of varMatches) {\n patterns.variables.push(match[1])\n }\n\n // !`command`\n const cmdMatches = content.matchAll(/!\\`([^`]+)\\`/g)\n for (const match of cmdMatches) {\n patterns.commands.push(match[1])\n }\n\n // Deduplicate indexed args\n patterns.indexedArgs = [...new Set(patterns.indexedArgs)].sort(\n (a, b) => a - b,\n )\n patterns.variables = [...new Set(patterns.variables)]\n\n return patterns\n}\n\n/**\n * Validate arguments against expected patterns\n * Returns error message if validation fails, null if valid\n */\nexport function validateArguments(\n args: string,\n patterns: ReturnType<typeof extractSubstitutionPatterns>,\n): string | null {\n const argValues = args.trim() ? args.trim().split(/\\s+/) : []\n const maxIndex = Math.max(...patterns.indexedArgs, -1)\n\n if (maxIndex >= 0 && argValues.length <= maxIndex) {\n return `Expected at least ${maxIndex + 1} argument(s), got ${argValues.length}`\n }\n\n return null\n}\n"],
|
|
5
|
+
"mappings": "AAaA,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAE1B,MAAM,YAAY,UAAU,IAAI;AAwBhC,MAAM,kBAAiD;AAAA,EACrD,WAAW;AAAA,EACX,KAAK,QAAQ,IAAI;AAAA,EACjB,KAAK,CAAC;AAAA,EACN,sBAAsB;AAAA,EACtB,gBAAgB;AAClB;AAUA,eAAsB,oBACpB,SACA,MACA,UAA+B,CAAC,GACf;AACjB,QAAM,MAAM,EAAE,GAAG,iBAAiB,GAAG,QAAQ;AAC7C,MAAI,SAAS;AACb,QAAM,YAAY,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAI5D,WAAS,OAAO,QAAQ,sBAAsB,IAAI;AAGlD,WAAS,OAAO,QAAQ,yBAAyB,CAAC,GAAG,aAAa;AAChE,UAAM,QAAQ,SAAS,UAAU,EAAE;AACnC,WAAO,UAAU,KAAK,KAAK;AAAA,EAC7B,CAAC;AAID,WAAS,OAAO,QAAQ,kBAAkB,CAAC,GAAG,aAAa;AACzD,UAAM,QAAQ,SAAS,UAAU,EAAE;AACnC,WAAO,UAAU,KAAK,KAAK;AAAA,EAC7B,CAAC;AAGD,WAAS,OAAO,QAAQ,8BAA8B,CAAC,GAAG,YAAY;AAEpE,YAAQ,QAAQ,YAAY,GAAG;AAAA,MAC7B,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI,aAAa;AAAA,MAC1B,KAAK;AAAA,MACL,KAAK;AACH,eAAO,IAAI;AAAA,MACb;AAEE,eAAO,IAAI,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK;AAAA,IACvD;AAAA,EACF,CAAC;AAID,MAAI,IAAI,sBAAsB;AAC5B,UAAM,iBAAiB;AACvB,UAAM,UAAU,CAAC,GAAG,OAAO,SAAS,cAAc,CAAC;AAEnD,eAAW,SAAS,SAAS;AAC3B,YAAM,UAAU,MAAM,CAAC;AACvB,UAAI;AACF,cAAM,EAAE,OAAO,IAAI,MAAM,UAAU,SAAS;AAAA,UAC1C,SAAS,IAAI;AAAA,UACb,KAAK,IAAI;AAAA,QACX,CAAC;AACD,iBAAS,OAAO,QAAQ,MAAM,CAAC,GAAG,OAAO,KAAK,CAAC;AAAA,MACjD,SAAS,OAAO;AACd,cAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACvD,iBAAS,OAAO,QAAQ,MAAM,CAAC,GAAG,WAAW,YAAY,GAAG;AAAA,MAC9D;AAAA,IACF;AAAA,EACF,OAAO;AAEL,aAAS,OAAO,QAAQ,iBAAiB,6BAA6B;AAAA,EACxE;AAEA,SAAO;AACT;AAMO,SAAS,wBAAwB,SAA0B;AAChE,SAAO,+CAA+C,KAAK,OAAO;AACpE;AAMO,SAAS,4BAA4B,SAK1C;AACA,QAAM,WAAW;AAAA,IACf,WAAW,CAAC;AAAA,IACZ,aAAa,CAAC;AAAA,IACd,WAAW,CAAC;AAAA,IACZ,UAAU,CAAC;AAAA,EACb;AAGA,MAAI,oBAAoB,KAAK,OAAO,GAAG;AACrC,aAAS,UAAU,KAAK,YAAY;AAAA,EACtC;AAGA,QAAM,kBAAkB,QAAQ,SAAS,uBAAuB;AAChE,aAAW,SAAS,iBAAiB;AACnC,aAAS,YAAY,KAAK,SAAS,MAAM,CAAC,GAAG,EAAE,CAAC;AAAA,EAClD;AAGA,QAAM,oBAAoB,QAAQ,SAAS,gBAAgB;AAC3D,aAAW,SAAS,mBAAmB;AACrC,aAAS,YAAY,KAAK,SAAS,MAAM,CAAC,GAAG,EAAE,CAAC;AAAA,EAClD;AAGA,QAAM,aAAa,QAAQ,SAAS,4BAA4B;AAChE,aAAW,SAAS,YAAY;AAC9B,aAAS,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA,EAClC;AAGA,QAAM,aAAa,QAAQ,SAAS,eAAe;AACnD,aAAW,SAAS,YAAY;AAC9B,aAAS,SAAS,KAAK,MAAM,CAAC,CAAC;AAAA,EACjC;AAGA,WAAS,cAAc,CAAC,GAAG,IAAI,IAAI,SAAS,WAAW,CAAC,EAAE;AAAA,IACxD,CAAC,GAAG,MAAM,IAAI;AAAA,EAChB;AACA,WAAS,YAAY,CAAC,GAAG,IAAI,IAAI,SAAS,SAAS,CAAC;AAEpD,SAAO;AACT;AAMO,SAAS,kBACd,MACA,UACe;AACf,QAAM,YAAY,KAAK,KAAK,IAAI,KAAK,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAC5D,QAAM,WAAW,KAAK,IAAI,GAAG,SAAS,aAAa,EAAE;AAErD,MAAI,YAAY,KAAK,UAAU,UAAU,UAAU;AACjD,WAAO,qBAAqB,WAAW,CAAC,qBAAqB,UAAU,MAAM;AAAA,EAC/E;AAEA,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/utils/teamConfig.js
CHANGED
|
@@ -288,16 +288,19 @@ async function installPlugins(pluginNames, targetDir) {
|
|
|
288
288
|
const { join } = await import("path");
|
|
289
289
|
const { homedir } = await import("os");
|
|
290
290
|
const installDir = targetDir || join(homedir(), ".minto", "plugins");
|
|
291
|
-
for (const
|
|
291
|
+
for (const pluginSpec of pluginNames) {
|
|
292
|
+
const atIndex = pluginSpec.indexOf("@");
|
|
293
|
+
const pluginName = atIndex !== -1 ? pluginSpec.slice(0, atIndex) : pluginSpec;
|
|
294
|
+
const marketplaceName = atIndex !== -1 ? pluginSpec.slice(atIndex + 1) : void 0;
|
|
292
295
|
try {
|
|
293
296
|
debugLogger.state("TEAM_CONFIG_INSTALL_PLUGIN", {
|
|
294
297
|
pluginName,
|
|
298
|
+
marketplace: marketplaceName,
|
|
295
299
|
targetDir: installDir
|
|
296
300
|
});
|
|
297
301
|
await installPluginFromMarketplace(
|
|
298
302
|
pluginName,
|
|
299
|
-
|
|
300
|
-
// Search all marketplaces
|
|
303
|
+
marketplaceName,
|
|
301
304
|
join(installDir, pluginName)
|
|
302
305
|
);
|
|
303
306
|
installed++;
|
|
@@ -314,14 +317,14 @@ async function installPlugins(pluginNames, targetDir) {
|
|
|
314
317
|
}
|
|
315
318
|
return { installed, failed, errors };
|
|
316
319
|
}
|
|
317
|
-
async function installRemoteAgents(agentUrls) {
|
|
320
|
+
async function installRemoteAgents(agentUrls, targetDir) {
|
|
318
321
|
let installed = 0;
|
|
319
322
|
let failed = 0;
|
|
320
323
|
const errors = [];
|
|
321
324
|
const { join } = await import("path");
|
|
322
325
|
const { homedir } = await import("os");
|
|
323
326
|
const { writeFileSync, mkdirSync, existsSync: existsSync2 } = await import("fs");
|
|
324
|
-
const agentsDir = join(homedir(), ".minto", "agents");
|
|
327
|
+
const agentsDir = targetDir || join(homedir(), ".minto", "agents");
|
|
325
328
|
if (!existsSync2(agentsDir)) {
|
|
326
329
|
mkdirSync(agentsDir, { recursive: true });
|
|
327
330
|
}
|
|
@@ -353,14 +356,14 @@ async function installRemoteAgents(agentUrls) {
|
|
|
353
356
|
}
|
|
354
357
|
return { installed, failed, errors };
|
|
355
358
|
}
|
|
356
|
-
async function installInlineAgents(agents) {
|
|
359
|
+
async function installInlineAgents(agents, targetDir) {
|
|
357
360
|
let installed = 0;
|
|
358
361
|
let failed = 0;
|
|
359
362
|
const errors = [];
|
|
360
363
|
const { join } = await import("path");
|
|
361
364
|
const { homedir } = await import("os");
|
|
362
365
|
const { writeFileSync, mkdirSync, existsSync: existsSync2 } = await import("fs");
|
|
363
|
-
const agentsDir = join(homedir(), ".minto", "agents");
|
|
366
|
+
const agentsDir = targetDir || join(homedir(), ".minto", "agents");
|
|
364
367
|
if (!existsSync2(agentsDir)) {
|
|
365
368
|
mkdirSync(agentsDir, { recursive: true });
|
|
366
369
|
}
|
|
@@ -400,17 +403,17 @@ ${agent.systemPrompt}
|
|
|
400
403
|
}
|
|
401
404
|
return { installed, failed, errors };
|
|
402
405
|
}
|
|
403
|
-
async function installTeamHooks(hooks) {
|
|
404
|
-
const { join } = await import("path");
|
|
406
|
+
async function installTeamHooks(hooks, targetPath) {
|
|
407
|
+
const { join, dirname } = await import("path");
|
|
405
408
|
const { homedir } = await import("os");
|
|
406
409
|
const { writeFileSync, mkdirSync, existsSync: existsSync2, readFileSync: readFileSync2 } = await import("fs");
|
|
407
410
|
try {
|
|
408
411
|
debugLogger.state("TEAM_CONFIG_INSTALL_HOOKS", {});
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
+
const hooksPath = targetPath || join(homedir(), ".minto", "hooks.json");
|
|
413
|
+
const hooksDir = dirname(hooksPath);
|
|
414
|
+
if (!existsSync2(hooksDir)) {
|
|
415
|
+
mkdirSync(hooksDir, { recursive: true });
|
|
412
416
|
}
|
|
413
|
-
const hooksPath = join(mintoDir, "hooks.json");
|
|
414
417
|
const hooksConfig = {
|
|
415
418
|
description: "Team-configured hooks",
|
|
416
419
|
hooks: {}
|
|
@@ -461,6 +464,141 @@ async function installTeamHooks(hooks) {
|
|
|
461
464
|
return { installed: false, error: errorMsg };
|
|
462
465
|
}
|
|
463
466
|
}
|
|
467
|
+
async function installInlineSkills(skills, targetDir) {
|
|
468
|
+
let installed = 0;
|
|
469
|
+
let failed = 0;
|
|
470
|
+
const errors = [];
|
|
471
|
+
const { join } = await import("path");
|
|
472
|
+
const { homedir } = await import("os");
|
|
473
|
+
const { writeFileSync, mkdirSync, existsSync: existsSync2 } = await import("fs");
|
|
474
|
+
const skillsDir = targetDir || join(homedir(), ".minto", "skills");
|
|
475
|
+
if (!existsSync2(skillsDir)) {
|
|
476
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
477
|
+
}
|
|
478
|
+
for (const skill of skills) {
|
|
479
|
+
try {
|
|
480
|
+
debugLogger.state("TEAM_CONFIG_INSTALL_SKILL", { name: skill.name });
|
|
481
|
+
const frontmatterLines = [
|
|
482
|
+
`name: ${skill.name}`,
|
|
483
|
+
`description: "${skill.description.replace(/"/g, '\\"')}"`
|
|
484
|
+
];
|
|
485
|
+
if (skill.argumentHint) {
|
|
486
|
+
frontmatterLines.push(`argument-hint: "${skill.argumentHint}"`);
|
|
487
|
+
}
|
|
488
|
+
if (skill.disableModelInvocation !== void 0) {
|
|
489
|
+
frontmatterLines.push(
|
|
490
|
+
`disable-model-invocation: ${skill.disableModelInvocation}`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
if (skill.userInvocable !== void 0) {
|
|
494
|
+
frontmatterLines.push(`user-invocable: ${skill.userInvocable}`);
|
|
495
|
+
}
|
|
496
|
+
if (skill.allowedTools && skill.allowedTools.length > 0) {
|
|
497
|
+
frontmatterLines.push(
|
|
498
|
+
`allowed-tools: ${JSON.stringify(skill.allowedTools)}`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
if (skill.model) {
|
|
502
|
+
frontmatterLines.push(`model: ${skill.model}`);
|
|
503
|
+
}
|
|
504
|
+
if (skill.context) {
|
|
505
|
+
frontmatterLines.push(`context: "${skill.context}"`);
|
|
506
|
+
}
|
|
507
|
+
if (skill.agent) {
|
|
508
|
+
frontmatterLines.push(`agent: "${skill.agent}"`);
|
|
509
|
+
}
|
|
510
|
+
const content = `---
|
|
511
|
+
${frontmatterLines.join("\n")}
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
${skill.content}
|
|
515
|
+
`;
|
|
516
|
+
const filename = `${skill.name}.md`;
|
|
517
|
+
const targetPath = join(skillsDir, filename);
|
|
518
|
+
writeFileSync(targetPath, content, "utf-8");
|
|
519
|
+
installed++;
|
|
520
|
+
debugLogger.state("TEAM_CONFIG_INSTALL_SKILL_SUCCESS", {
|
|
521
|
+
name: skill.name,
|
|
522
|
+
targetPath
|
|
523
|
+
});
|
|
524
|
+
} catch (error) {
|
|
525
|
+
failed++;
|
|
526
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
527
|
+
errors.push(`${skill.name}: ${errorMsg}`);
|
|
528
|
+
debugLogger.error("TEAM_CONFIG_INSTALL_SKILL_ERROR", {
|
|
529
|
+
name: skill.name,
|
|
530
|
+
error: errorMsg
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return { installed, failed, errors };
|
|
535
|
+
}
|
|
536
|
+
async function installInlineCommands(commands, targetDir) {
|
|
537
|
+
let installed = 0;
|
|
538
|
+
let failed = 0;
|
|
539
|
+
const errors = [];
|
|
540
|
+
const { join } = await import("path");
|
|
541
|
+
const { homedir } = await import("os");
|
|
542
|
+
const { writeFileSync, mkdirSync, existsSync: existsSync2 } = await import("fs");
|
|
543
|
+
const commandsDir = targetDir || join(homedir(), ".minto", "commands");
|
|
544
|
+
if (!existsSync2(commandsDir)) {
|
|
545
|
+
mkdirSync(commandsDir, { recursive: true });
|
|
546
|
+
}
|
|
547
|
+
for (const cmd of commands) {
|
|
548
|
+
try {
|
|
549
|
+
debugLogger.state("TEAM_CONFIG_INSTALL_COMMAND", { name: cmd.name });
|
|
550
|
+
const frontmatterLines = [`name: ${cmd.name}`];
|
|
551
|
+
if (cmd.description) {
|
|
552
|
+
frontmatterLines.push(
|
|
553
|
+
`description: "${cmd.description.replace(/"/g, '\\"')}"`
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
if (cmd.aliases && cmd.aliases.length > 0) {
|
|
557
|
+
frontmatterLines.push(`aliases: ${JSON.stringify(cmd.aliases)}`);
|
|
558
|
+
}
|
|
559
|
+
if (cmd.enabled !== void 0) {
|
|
560
|
+
frontmatterLines.push(`enabled: ${cmd.enabled}`);
|
|
561
|
+
}
|
|
562
|
+
if (cmd.hidden !== void 0) {
|
|
563
|
+
frontmatterLines.push(`hidden: ${cmd.hidden}`);
|
|
564
|
+
}
|
|
565
|
+
if (cmd.progressMessage) {
|
|
566
|
+
frontmatterLines.push(`progressMessage: "${cmd.progressMessage}"`);
|
|
567
|
+
}
|
|
568
|
+
if (cmd.argNames && cmd.argNames.length > 0) {
|
|
569
|
+
frontmatterLines.push(`argNames: ${JSON.stringify(cmd.argNames)}`);
|
|
570
|
+
}
|
|
571
|
+
if (cmd.allowedTools && cmd.allowedTools.length > 0) {
|
|
572
|
+
frontmatterLines.push(
|
|
573
|
+
`allowed-tools: ${JSON.stringify(cmd.allowedTools)}`
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const content = `---
|
|
577
|
+
${frontmatterLines.join("\n")}
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
${cmd.content}
|
|
581
|
+
`;
|
|
582
|
+
const filename = `${cmd.name}.md`;
|
|
583
|
+
const targetPath = join(commandsDir, filename);
|
|
584
|
+
writeFileSync(targetPath, content, "utf-8");
|
|
585
|
+
installed++;
|
|
586
|
+
debugLogger.state("TEAM_CONFIG_INSTALL_COMMAND_SUCCESS", {
|
|
587
|
+
name: cmd.name,
|
|
588
|
+
targetPath
|
|
589
|
+
});
|
|
590
|
+
} catch (error) {
|
|
591
|
+
failed++;
|
|
592
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
593
|
+
errors.push(`${cmd.name}: ${errorMsg}`);
|
|
594
|
+
debugLogger.error("TEAM_CONFIG_INSTALL_COMMAND_ERROR", {
|
|
595
|
+
name: cmd.name,
|
|
596
|
+
error: errorMsg
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return { installed, failed, errors };
|
|
601
|
+
}
|
|
464
602
|
export {
|
|
465
603
|
addMarketplaces,
|
|
466
604
|
applyTeamConfig,
|
|
@@ -468,6 +606,8 @@ export {
|
|
|
468
606
|
convertTeamModelProfile,
|
|
469
607
|
fetchTeamConfig,
|
|
470
608
|
installInlineAgents,
|
|
609
|
+
installInlineCommands,
|
|
610
|
+
installInlineSkills,
|
|
471
611
|
installPlugins,
|
|
472
612
|
installRemoteAgents,
|
|
473
613
|
installTeamHooks,
|