@triedotdev/mcp 1.0.78 → 1.0.80
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 +27 -2
- package/dist/{chunk-UPKBO5EM.js → chunk-432E2RYB.js} +29 -20
- package/dist/chunk-432E2RYB.js.map +1 -0
- package/dist/{chunk-RRDDAD5N.js → chunk-45NUFTNV.js} +6 -6
- package/dist/{chunk-35FAFFHE.js → chunk-75J4HQTD.js} +2 -2
- package/dist/{chunk-53URTRWH.js → chunk-D3F7VKCN.js} +2 -2
- package/dist/{chunk-P6VLSYXN.js → chunk-EWIEXQES.js} +2 -2
- package/dist/{chunk-LNLLZQWH.js → chunk-KCAWTZ7P.js} +12 -11
- package/dist/{chunk-LNLLZQWH.js.map → chunk-KCAWTZ7P.js.map} +1 -1
- package/dist/{chunk-3RKY55HZ.js → chunk-LKXDJESG.js} +671 -81
- package/dist/chunk-LKXDJESG.js.map +1 -0
- package/dist/{chunk-AIC4HOOQ.js → chunk-U5P3O5G5.js} +3 -3
- package/dist/{chunk-6QKDEGWR.js → chunk-WGECLUDQ.js} +4 -4
- package/dist/chunk-WGECLUDQ.js.map +1 -0
- package/dist/cli/main.js +115 -7
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/yolo-daemon.js +8 -8
- package/dist/{goal-manager-NI4LJ2SX.js → goal-manager-NHPEUWFY.js} +4 -4
- package/dist/{guardian-agent-R5HX7UWJ.js → guardian-agent-UPLAQWJK.js} +6 -6
- package/dist/index.js +39 -41
- package/dist/index.js.map +1 -1
- package/dist/{issue-store-MULGOF6B.js → issue-store-RKJVOKSJ.js} +2 -2
- package/dist/ui/memory-viewer.html +4 -4
- package/dist/ui/pr-review.html +4 -4
- package/dist/ui/scan-dashboard.html +4 -4
- package/dist/ui/visual-qa.html +4 -4
- package/dist/workers/agent-worker.js +3 -3
- package/package.json +1 -1
- package/dist/chunk-3RKY55HZ.js.map +0 -1
- package/dist/chunk-6QKDEGWR.js.map +0 -1
- package/dist/chunk-UPKBO5EM.js.map +0 -1
- /package/dist/{chunk-RRDDAD5N.js.map → chunk-45NUFTNV.js.map} +0 -0
- /package/dist/{chunk-35FAFFHE.js.map → chunk-75J4HQTD.js.map} +0 -0
- /package/dist/{chunk-53URTRWH.js.map → chunk-D3F7VKCN.js.map} +0 -0
- /package/dist/{chunk-P6VLSYXN.js.map → chunk-EWIEXQES.js.map} +0 -0
- /package/dist/{chunk-AIC4HOOQ.js.map → chunk-U5P3O5G5.js.map} +0 -0
- /package/dist/{goal-manager-NI4LJ2SX.js.map → goal-manager-NHPEUWFY.js.map} +0 -0
- /package/dist/{guardian-agent-R5HX7UWJ.js.map → guardian-agent-UPLAQWJK.js.map} +0 -0
- /package/dist/{issue-store-MULGOF6B.js.map → issue-store-RKJVOKSJ.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
recordToGlobalMemory,
|
|
3
3
|
updateGlobalMemoryMd
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EWIEXQES.js";
|
|
5
5
|
import {
|
|
6
6
|
checkFileLevelIssues,
|
|
7
7
|
getVibeCodeTrie,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
atomicWriteFile,
|
|
21
21
|
atomicWriteJSON,
|
|
22
22
|
storeIssues
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-WGECLUDQ.js";
|
|
24
24
|
import {
|
|
25
25
|
getWorkingDirectory
|
|
26
26
|
} from "./chunk-CM7EHNQK.js";
|
|
@@ -8849,10 +8849,8 @@ Output STRICT JSON:
|
|
|
8849
8849
|
};
|
|
8850
8850
|
|
|
8851
8851
|
// src/skills/installer.ts
|
|
8852
|
-
import { mkdir as
|
|
8853
|
-
import { join as
|
|
8854
|
-
import { exec } from "child_process";
|
|
8855
|
-
import { promisify } from "util";
|
|
8852
|
+
import { mkdir as mkdir3, rm, writeFile as writeFile3, readdir as readdir3, readFile as readFile5, access, cp, stat as stat2 } from "fs/promises";
|
|
8853
|
+
import { join as join5, basename as basename2, extname as extname2 } from "path";
|
|
8856
8854
|
import { homedir } from "os";
|
|
8857
8855
|
import { existsSync as existsSync3 } from "fs";
|
|
8858
8856
|
|
|
@@ -8901,13 +8899,551 @@ function parseYamlFrontmatter(yaml) {
|
|
|
8901
8899
|
return result;
|
|
8902
8900
|
}
|
|
8903
8901
|
|
|
8904
|
-
// src/skills/
|
|
8902
|
+
// src/skills/security-scanner.ts
|
|
8903
|
+
import { readFile as readFile3, readdir, stat } from "fs/promises";
|
|
8904
|
+
import { join as join3, relative } from "path";
|
|
8905
|
+
var DANGEROUS_PATTERNS = [
|
|
8906
|
+
// Network exfiltration
|
|
8907
|
+
{
|
|
8908
|
+
pattern: /curl\s+.*-X\s+POST.*https?:\/\/(?!github\.com|githubusercontent\.com|npmjs\.org|pypi\.org)/gi,
|
|
8909
|
+
severity: "critical",
|
|
8910
|
+
category: "exfiltration",
|
|
8911
|
+
description: "HTTP POST to external domain - possible data exfiltration",
|
|
8912
|
+
recommendation: "Review the destination URL. Only allow posts to trusted domains."
|
|
8913
|
+
},
|
|
8914
|
+
{
|
|
8915
|
+
pattern: /wget\s+.*--post/gi,
|
|
8916
|
+
severity: "critical",
|
|
8917
|
+
category: "exfiltration",
|
|
8918
|
+
description: "wget POST request - possible data exfiltration",
|
|
8919
|
+
recommendation: "Verify the destination and data being sent."
|
|
8920
|
+
},
|
|
8921
|
+
// Credential access
|
|
8922
|
+
{
|
|
8923
|
+
pattern: /\.env(?!\.example)/gi,
|
|
8924
|
+
severity: "critical",
|
|
8925
|
+
category: "credential-access",
|
|
8926
|
+
description: "Access to .env file - may expose secrets",
|
|
8927
|
+
recommendation: "Skills should not access .env files. Use explicit configuration instead."
|
|
8928
|
+
},
|
|
8929
|
+
{
|
|
8930
|
+
pattern: /\.ssh[\/\\](?:id_rsa|id_ed25519|authorized_keys)/gi,
|
|
8931
|
+
severity: "critical",
|
|
8932
|
+
category: "credential-access",
|
|
8933
|
+
description: "Access to SSH keys - credential theft attempt",
|
|
8934
|
+
recommendation: "Block this skill. SSH key access is never legitimate for code analysis."
|
|
8935
|
+
},
|
|
8936
|
+
{
|
|
8937
|
+
pattern: /\.aws[\/\\]credentials/gi,
|
|
8938
|
+
severity: "critical",
|
|
8939
|
+
category: "credential-access",
|
|
8940
|
+
description: "Access to AWS credentials",
|
|
8941
|
+
recommendation: "Block this skill. AWS credential access indicates malicious intent."
|
|
8942
|
+
},
|
|
8943
|
+
{
|
|
8944
|
+
pattern: /(?:AWS|ANTHROPIC|OPENAI|GITHUB)_(?:ACCESS_)?(?:SECRET_)?KEY/gi,
|
|
8945
|
+
severity: "high",
|
|
8946
|
+
category: "credential-access",
|
|
8947
|
+
description: "API key access pattern detected",
|
|
8948
|
+
recommendation: "Verify this is necessary for skill functionality."
|
|
8949
|
+
},
|
|
8950
|
+
// Persistence mechanisms
|
|
8951
|
+
{
|
|
8952
|
+
pattern: /crontab\s+-e|crontab.*<<.*EOF/gi,
|
|
8953
|
+
severity: "critical",
|
|
8954
|
+
category: "persistence",
|
|
8955
|
+
description: "Crontab modification - persistence mechanism",
|
|
8956
|
+
recommendation: "Block this skill. Cron modification indicates malware behavior."
|
|
8957
|
+
},
|
|
8958
|
+
{
|
|
8959
|
+
pattern: /\.(?:bashrc|zshrc|profile|bash_profile)\s*>>/gi,
|
|
8960
|
+
severity: "critical",
|
|
8961
|
+
category: "persistence",
|
|
8962
|
+
description: "Shell profile modification - persistence mechanism",
|
|
8963
|
+
recommendation: "Block this skill. Shell profile modification is malicious."
|
|
8964
|
+
},
|
|
8965
|
+
// Shell execution (context-dependent)
|
|
8966
|
+
{
|
|
8967
|
+
pattern: /(?:bash|sh)\s+-c\s+["'].*(?:curl|wget)/gi,
|
|
8968
|
+
severity: "high",
|
|
8969
|
+
category: "shell-execution",
|
|
8970
|
+
description: "Shell command with network access",
|
|
8971
|
+
recommendation: "Review the command. Should use proper APIs instead of shell."
|
|
8972
|
+
},
|
|
8973
|
+
{
|
|
8974
|
+
pattern: /eval\s*\(/gi,
|
|
8975
|
+
severity: "high",
|
|
8976
|
+
category: "shell-execution",
|
|
8977
|
+
description: "eval() usage - potential code injection",
|
|
8978
|
+
recommendation: "eval() should be avoided. Review for necessity."
|
|
8979
|
+
},
|
|
8980
|
+
// Reconnaissance
|
|
8981
|
+
{
|
|
8982
|
+
pattern: /\b(?:hostname|whoami|uname|id)\b(?:\s|$|;|\||&)/gi,
|
|
8983
|
+
severity: "medium",
|
|
8984
|
+
category: "reconnaissance",
|
|
8985
|
+
description: "System reconnaissance command",
|
|
8986
|
+
recommendation: "Review if system info is necessary for skill functionality."
|
|
8987
|
+
},
|
|
8988
|
+
{
|
|
8989
|
+
pattern: /env\s*(?:$|;|\||>>)/gim,
|
|
8990
|
+
severity: "medium",
|
|
8991
|
+
category: "reconnaissance",
|
|
8992
|
+
description: "Environment variable enumeration",
|
|
8993
|
+
recommendation: "Skills should not need to enumerate all env variables."
|
|
8994
|
+
},
|
|
8995
|
+
// File enumeration
|
|
8996
|
+
{
|
|
8997
|
+
pattern: /find\s+.*-name\s+['"].*\.(?:env|pem|key|credentials)/gi,
|
|
8998
|
+
severity: "high",
|
|
8999
|
+
category: "credential-access",
|
|
9000
|
+
description: "Searching for credential files",
|
|
9001
|
+
recommendation: "Block this skill. Credential file enumeration is malicious."
|
|
9002
|
+
},
|
|
9003
|
+
{
|
|
9004
|
+
pattern: /grep\s+-r.*(?:password|secret|token|key)/gi,
|
|
9005
|
+
severity: "high",
|
|
9006
|
+
category: "credential-access",
|
|
9007
|
+
description: "Recursive search for secrets",
|
|
9008
|
+
recommendation: "Skills should use static analysis, not grep for secrets."
|
|
9009
|
+
},
|
|
9010
|
+
// Encoding/obfuscation (often used for evasion)
|
|
9011
|
+
{
|
|
9012
|
+
pattern: /base64.*\|\s*(?:curl|wget|bash|sh)/gi,
|
|
9013
|
+
severity: "critical",
|
|
9014
|
+
category: "exfiltration",
|
|
9015
|
+
description: "Base64 encoding with command execution - obfuscation technique",
|
|
9016
|
+
recommendation: "Block this skill. Base64 piping indicates malicious behavior."
|
|
9017
|
+
},
|
|
9018
|
+
{
|
|
9019
|
+
pattern: /tar\s+.*\|\s*(?:curl|wget)/gi,
|
|
9020
|
+
severity: "critical",
|
|
9021
|
+
category: "exfiltration",
|
|
9022
|
+
description: "Archiving and sending data externally",
|
|
9023
|
+
recommendation: "Block this skill. Tar piping indicates data exfiltration."
|
|
9024
|
+
},
|
|
9025
|
+
// Network access to suspicious TLDs
|
|
9026
|
+
{
|
|
9027
|
+
pattern: /https?:\/\/[^\s'"]+\.(?:tk|ml|ga|cf|gq|top|xyz)\b/gi,
|
|
9028
|
+
severity: "high",
|
|
9029
|
+
category: "network-access",
|
|
9030
|
+
description: "Access to domain with suspicious TLD",
|
|
9031
|
+
recommendation: "Free TLDs are often used for malicious infrastructure. Verify legitimacy."
|
|
9032
|
+
}
|
|
9033
|
+
];
|
|
9034
|
+
var SAFE_DOMAINS = [
|
|
9035
|
+
"github.com",
|
|
9036
|
+
"githubusercontent.com",
|
|
9037
|
+
"api.github.com",
|
|
9038
|
+
"raw.githubusercontent.com",
|
|
9039
|
+
"npmjs.org",
|
|
9040
|
+
"registry.npmjs.org",
|
|
9041
|
+
"pypi.org",
|
|
9042
|
+
"anthropic.com",
|
|
9043
|
+
"openai.com"
|
|
9044
|
+
];
|
|
9045
|
+
async function findAllFiles(dir, files = []) {
|
|
9046
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
9047
|
+
for (const entry of entries) {
|
|
9048
|
+
const fullPath = join3(dir, entry.name);
|
|
9049
|
+
if (entry.isDirectory()) {
|
|
9050
|
+
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
9051
|
+
await findAllFiles(fullPath, files);
|
|
9052
|
+
}
|
|
9053
|
+
} else if (entry.isFile()) {
|
|
9054
|
+
const ext = entry.name.split(".").pop()?.toLowerCase();
|
|
9055
|
+
if (ext && ["md", "txt", "js", "ts", "py", "sh", "bash", "json", "yaml", "yml"].includes(ext)) {
|
|
9056
|
+
files.push(fullPath);
|
|
9057
|
+
}
|
|
9058
|
+
}
|
|
9059
|
+
}
|
|
9060
|
+
return files;
|
|
9061
|
+
}
|
|
9062
|
+
function getLineNumber(text, index) {
|
|
9063
|
+
return text.substring(0, index).split("\n").length;
|
|
9064
|
+
}
|
|
9065
|
+
function extractUrls(text) {
|
|
9066
|
+
const urlPattern = /https?:\/\/[^\s'"<>)]+/gi;
|
|
9067
|
+
return Array.from(text.matchAll(urlPattern), (m) => m[0]);
|
|
9068
|
+
}
|
|
9069
|
+
function isSafeDomain(url) {
|
|
9070
|
+
try {
|
|
9071
|
+
const hostname = new URL(url).hostname.toLowerCase();
|
|
9072
|
+
return SAFE_DOMAINS.some(
|
|
9073
|
+
(safe) => hostname === safe || hostname.endsWith("." + safe)
|
|
9074
|
+
);
|
|
9075
|
+
} catch {
|
|
9076
|
+
return false;
|
|
9077
|
+
}
|
|
9078
|
+
}
|
|
9079
|
+
async function scanSkillForThreats(skillPath) {
|
|
9080
|
+
const startTime = Date.now();
|
|
9081
|
+
const risks = [];
|
|
9082
|
+
const permissions = /* @__PURE__ */ new Set();
|
|
9083
|
+
const files = await findAllFiles(skillPath);
|
|
9084
|
+
for (const file of files) {
|
|
9085
|
+
const relPath = relative(skillPath, file);
|
|
9086
|
+
try {
|
|
9087
|
+
const stats = await stat(file);
|
|
9088
|
+
if (stats.size > 1024 * 1024) {
|
|
9089
|
+
continue;
|
|
9090
|
+
}
|
|
9091
|
+
const content = await readFile3(file, "utf-8");
|
|
9092
|
+
for (const rule of DANGEROUS_PATTERNS) {
|
|
9093
|
+
const matches = content.matchAll(rule.pattern);
|
|
9094
|
+
for (const match of matches) {
|
|
9095
|
+
const lineNum = getLineNumber(content, match.index ?? 0);
|
|
9096
|
+
risks.push({
|
|
9097
|
+
severity: rule.severity,
|
|
9098
|
+
category: rule.category,
|
|
9099
|
+
description: rule.description,
|
|
9100
|
+
location: `${relPath}:${lineNum}`,
|
|
9101
|
+
pattern: match[0].substring(0, 100),
|
|
9102
|
+
// Limit pattern length
|
|
9103
|
+
recommendation: rule.recommendation
|
|
9104
|
+
});
|
|
9105
|
+
if (rule.category === "shell-execution") {
|
|
9106
|
+
permissions.add("shell:execute");
|
|
9107
|
+
} else if (rule.category === "network-access") {
|
|
9108
|
+
permissions.add("network:access");
|
|
9109
|
+
} else if (rule.category === "credential-access") {
|
|
9110
|
+
permissions.add("credential:read");
|
|
9111
|
+
}
|
|
9112
|
+
}
|
|
9113
|
+
}
|
|
9114
|
+
const urls = extractUrls(content);
|
|
9115
|
+
for (const url of urls) {
|
|
9116
|
+
if (!isSafeDomain(url)) {
|
|
9117
|
+
risks.push({
|
|
9118
|
+
severity: "medium",
|
|
9119
|
+
category: "network-access",
|
|
9120
|
+
description: `External URL: ${url}`,
|
|
9121
|
+
location: relPath,
|
|
9122
|
+
pattern: url,
|
|
9123
|
+
recommendation: "Verify this domain is necessary and trustworthy."
|
|
9124
|
+
});
|
|
9125
|
+
permissions.add("network:access");
|
|
9126
|
+
}
|
|
9127
|
+
}
|
|
9128
|
+
} catch (error) {
|
|
9129
|
+
continue;
|
|
9130
|
+
}
|
|
9131
|
+
}
|
|
9132
|
+
const criticalRisks = risks.filter((r) => r.severity === "critical");
|
|
9133
|
+
const highRisks = risks.filter((r) => r.severity === "high");
|
|
9134
|
+
const safe = criticalRisks.length === 0 && highRisks.length < 3;
|
|
9135
|
+
const requiredPermissions = Array.from(permissions).map((perm) => ({
|
|
9136
|
+
name: perm,
|
|
9137
|
+
description: getPermissionDescription(perm),
|
|
9138
|
+
required: true
|
|
9139
|
+
}));
|
|
9140
|
+
return {
|
|
9141
|
+
safe,
|
|
9142
|
+
risks: risks.sort((a, b) => {
|
|
9143
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
9144
|
+
return severityOrder[a.severity] - severityOrder[b.severity];
|
|
9145
|
+
}),
|
|
9146
|
+
requiredPermissions,
|
|
9147
|
+
filesScanned: files.length,
|
|
9148
|
+
scanDuration: Date.now() - startTime
|
|
9149
|
+
};
|
|
9150
|
+
}
|
|
9151
|
+
function getPermissionDescription(permission) {
|
|
9152
|
+
const descriptions = {
|
|
9153
|
+
"shell:execute": "Execute shell commands on your system",
|
|
9154
|
+
"network:access": "Make network requests to external servers",
|
|
9155
|
+
"credential:read": "Access credential files and environment variables",
|
|
9156
|
+
"file:write": "Modify files in your project",
|
|
9157
|
+
"file:read": "Read files in your project"
|
|
9158
|
+
};
|
|
9159
|
+
return descriptions[permission] || permission;
|
|
9160
|
+
}
|
|
9161
|
+
function formatSecuritySummary(result) {
|
|
9162
|
+
if (result.risks.length === 0) {
|
|
9163
|
+
return "\u2705 No security risks detected";
|
|
9164
|
+
}
|
|
9165
|
+
const critical = result.risks.filter((r) => r.severity === "critical").length;
|
|
9166
|
+
const high = result.risks.filter((r) => r.severity === "high").length;
|
|
9167
|
+
const medium = result.risks.filter((r) => r.severity === "medium").length;
|
|
9168
|
+
const low = result.risks.filter((r) => r.severity === "low").length;
|
|
9169
|
+
const parts = [];
|
|
9170
|
+
if (critical > 0) parts.push(`\u{1F534} ${critical} critical`);
|
|
9171
|
+
if (high > 0) parts.push(`\u{1F7E0} ${high} high`);
|
|
9172
|
+
if (medium > 0) parts.push(`\u{1F7E1} ${medium} medium`);
|
|
9173
|
+
if (low > 0) parts.push(`\u{1F535} ${low} low`);
|
|
9174
|
+
return parts.join(", ") + " risk" + (result.risks.length === 1 ? "" : "s");
|
|
9175
|
+
}
|
|
9176
|
+
|
|
9177
|
+
// src/utils/command-runner.ts
|
|
9178
|
+
import { exec, execFile, execSync } from "child_process";
|
|
9179
|
+
import { promisify } from "util";
|
|
9180
|
+
|
|
9181
|
+
// src/skills/audit-logger.ts
|
|
9182
|
+
import { writeFile as writeFile2, mkdir as mkdir2, readdir as readdir2, readFile as readFile4 } from "fs/promises";
|
|
9183
|
+
import { join as join4 } from "path";
|
|
9184
|
+
async function logSkillExecution(execution) {
|
|
9185
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9186
|
+
const auditDir = join4(workDir, ".trie", "audit");
|
|
9187
|
+
await mkdir2(auditDir, { recursive: true });
|
|
9188
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9189
|
+
const safeSkillName = execution.skillName.replace(/[^a-z0-9-]/gi, "_");
|
|
9190
|
+
const filename = `${timestamp}_${safeSkillName}.json`;
|
|
9191
|
+
const filepath = join4(auditDir, filename);
|
|
9192
|
+
if (!execution.endTime) {
|
|
9193
|
+
execution.endTime = Date.now();
|
|
9194
|
+
}
|
|
9195
|
+
if (!execution.duration) {
|
|
9196
|
+
execution.duration = execution.endTime - execution.startTime;
|
|
9197
|
+
}
|
|
9198
|
+
await writeFile2(filepath, JSON.stringify(execution, null, 2), "utf-8");
|
|
9199
|
+
}
|
|
9200
|
+
function createAuditEntry(skillName, skillSource, triggeredBy, targetPath) {
|
|
9201
|
+
return {
|
|
9202
|
+
skillName,
|
|
9203
|
+
skillSource,
|
|
9204
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9205
|
+
startTime: Date.now(),
|
|
9206
|
+
success: false,
|
|
9207
|
+
triggeredBy,
|
|
9208
|
+
targetPath,
|
|
9209
|
+
commands: [],
|
|
9210
|
+
networkCalls: [],
|
|
9211
|
+
filesAccessed: [],
|
|
9212
|
+
filesModified: []
|
|
9213
|
+
};
|
|
9214
|
+
}
|
|
9215
|
+
function completeAuditEntry(entry, success, error) {
|
|
9216
|
+
const base = {
|
|
9217
|
+
...entry,
|
|
9218
|
+
success,
|
|
9219
|
+
endTime: Date.now(),
|
|
9220
|
+
duration: Date.now() - entry.startTime
|
|
9221
|
+
};
|
|
9222
|
+
return error ? { ...base, error } : base;
|
|
9223
|
+
}
|
|
9224
|
+
async function getRecentAuditLogs(limit = 10) {
|
|
9225
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9226
|
+
const auditDir = join4(workDir, ".trie", "audit");
|
|
9227
|
+
try {
|
|
9228
|
+
const files = await readdir2(auditDir);
|
|
9229
|
+
const sorted = files.filter((f) => f.endsWith(".json")).sort().reverse().slice(0, limit);
|
|
9230
|
+
const logs = [];
|
|
9231
|
+
for (const file of sorted) {
|
|
9232
|
+
try {
|
|
9233
|
+
const content = await readFile4(join4(auditDir, file), "utf-8");
|
|
9234
|
+
logs.push(JSON.parse(content));
|
|
9235
|
+
} catch {
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
return logs;
|
|
9239
|
+
} catch {
|
|
9240
|
+
return [];
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
async function getSkillAuditLogs(skillName) {
|
|
9244
|
+
const workDir = getWorkingDirectory(void 0, true);
|
|
9245
|
+
const auditDir = join4(workDir, ".trie", "audit");
|
|
9246
|
+
const safeSkillName = skillName.replace(/[^a-z0-9-]/gi, "_");
|
|
9247
|
+
try {
|
|
9248
|
+
const files = await readdir2(auditDir);
|
|
9249
|
+
const skillFiles = files.filter(
|
|
9250
|
+
(f) => f.includes(`_${safeSkillName}.json`)
|
|
9251
|
+
);
|
|
9252
|
+
const logs = [];
|
|
9253
|
+
for (const file of skillFiles) {
|
|
9254
|
+
try {
|
|
9255
|
+
const content = await readFile4(join4(auditDir, file), "utf-8");
|
|
9256
|
+
logs.push(JSON.parse(content));
|
|
9257
|
+
} catch {
|
|
9258
|
+
}
|
|
9259
|
+
}
|
|
9260
|
+
return logs.sort(
|
|
9261
|
+
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
9262
|
+
);
|
|
9263
|
+
} catch {
|
|
9264
|
+
return [];
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
async function getAuditStatistics() {
|
|
9268
|
+
const logs = await getRecentAuditLogs(1e3);
|
|
9269
|
+
const skills = /* @__PURE__ */ new Set();
|
|
9270
|
+
let totalCommands = 0;
|
|
9271
|
+
let totalNetworkCalls = 0;
|
|
9272
|
+
let blockedCommands = 0;
|
|
9273
|
+
let blockedNetworkCalls = 0;
|
|
9274
|
+
for (const log of logs) {
|
|
9275
|
+
skills.add(log.skillName);
|
|
9276
|
+
totalCommands += log.commands?.length ?? 0;
|
|
9277
|
+
totalNetworkCalls += log.networkCalls?.length ?? 0;
|
|
9278
|
+
blockedCommands += log.commands?.filter((c) => c.blockedBy).length ?? 0;
|
|
9279
|
+
blockedNetworkCalls += log.networkCalls?.filter((c) => c.blocked).length ?? 0;
|
|
9280
|
+
}
|
|
9281
|
+
return {
|
|
9282
|
+
totalExecutions: logs.length,
|
|
9283
|
+
successfulExecutions: logs.filter((l) => l.success).length,
|
|
9284
|
+
failedExecutions: logs.filter((l) => !l.success).length,
|
|
9285
|
+
uniqueSkills: skills.size,
|
|
9286
|
+
totalCommands,
|
|
9287
|
+
totalNetworkCalls,
|
|
9288
|
+
blockedCommands,
|
|
9289
|
+
blockedNetworkCalls
|
|
9290
|
+
};
|
|
9291
|
+
}
|
|
9292
|
+
function formatAuditLog(log) {
|
|
9293
|
+
const lines = [];
|
|
9294
|
+
const status = log.success ? "\u2705" : "\u274C";
|
|
9295
|
+
const duration = log.duration ? `${log.duration}ms` : "unknown";
|
|
9296
|
+
lines.push(`${status} ${log.skillName} (${duration})`);
|
|
9297
|
+
lines.push(` Time: ${new Date(log.timestamp).toLocaleString()}`);
|
|
9298
|
+
lines.push(` Triggered by: ${log.triggeredBy}`);
|
|
9299
|
+
lines.push(` Target: ${log.targetPath}`);
|
|
9300
|
+
if (log.commands && log.commands.length > 0) {
|
|
9301
|
+
lines.push(` Commands executed: ${log.commands.length}`);
|
|
9302
|
+
const blocked = log.commands.filter((c) => c.blockedBy).length;
|
|
9303
|
+
if (blocked > 0) {
|
|
9304
|
+
lines.push(` \u26A0\uFE0F Commands blocked: ${blocked}`);
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
if (log.networkCalls && log.networkCalls.length > 0) {
|
|
9308
|
+
lines.push(` Network calls: ${log.networkCalls.length}`);
|
|
9309
|
+
const blocked = log.networkCalls.filter((c) => c.blocked).length;
|
|
9310
|
+
if (blocked > 0) {
|
|
9311
|
+
lines.push(` \u26A0\uFE0F Network calls blocked: ${blocked}`);
|
|
9312
|
+
}
|
|
9313
|
+
}
|
|
9314
|
+
if (log.error) {
|
|
9315
|
+
lines.push(` Error: ${log.error}`);
|
|
9316
|
+
}
|
|
9317
|
+
return lines.join("\n");
|
|
9318
|
+
}
|
|
9319
|
+
|
|
9320
|
+
// src/utils/command-runner.ts
|
|
8905
9321
|
var execAsync = promisify(exec);
|
|
9322
|
+
var execFileAsync = promisify(execFile);
|
|
9323
|
+
function redact(text) {
|
|
9324
|
+
return text.replace(/\b(AWS|ANTHROPIC|OPENAI|GITHUB)_[A-Z0-9_]*\s*=\s*([^\s"'`]+)/gi, "$1_<REDACTED>=<REDACTED>").replace(/\bBearer\s+[A-Za-z0-9\-._~+/]+=*\b/g, "Bearer <REDACTED>").replace(/\bghp_[A-Za-z0-9]{20,}\b/g, "ghp_<REDACTED>").replace(/\b(?:xox[baprs]-)[A-Za-z0-9-]{10,}\b/g, "<REDACTED_SLACK_TOKEN>").replace(/\bAKIA[0-9A-Z]{16}\b/g, "AKIA<REDACTED>");
|
|
9325
|
+
}
|
|
9326
|
+
function clampOutput(text, maxChars) {
|
|
9327
|
+
if (text.length <= maxChars) return text;
|
|
9328
|
+
return text.slice(0, maxChars) + `
|
|
9329
|
+
\u2026(truncated ${text.length - maxChars} chars)`;
|
|
9330
|
+
}
|
|
9331
|
+
function buildCommandRecord(command) {
|
|
9332
|
+
return {
|
|
9333
|
+
command,
|
|
9334
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9335
|
+
};
|
|
9336
|
+
}
|
|
9337
|
+
async function finalizeAndWrite(entry, cmd, outcome, options) {
|
|
9338
|
+
const duration = Date.now() - outcome.startedAt;
|
|
9339
|
+
cmd.duration = duration;
|
|
9340
|
+
if (outcome.exitCode !== void 0) {
|
|
9341
|
+
cmd.exitCode = outcome.exitCode;
|
|
9342
|
+
}
|
|
9343
|
+
const captureOutput = options?.captureOutput ?? false;
|
|
9344
|
+
const redactOutput = options?.redactOutput ?? true;
|
|
9345
|
+
const maxOutputChars = options?.maxOutputChars ?? 2e3;
|
|
9346
|
+
if (captureOutput) {
|
|
9347
|
+
const out = outcome.stdout ?? "";
|
|
9348
|
+
const err = outcome.stderr ?? "";
|
|
9349
|
+
cmd.stdout = redactOutput ? redact(clampOutput(out, maxOutputChars)) : clampOutput(out, maxOutputChars);
|
|
9350
|
+
cmd.stderr = redactOutput ? redact(clampOutput(err, maxOutputChars)) : clampOutput(err, maxOutputChars);
|
|
9351
|
+
}
|
|
9352
|
+
const completed = completeAuditEntry(entry, outcome.success, outcome.error);
|
|
9353
|
+
await logSkillExecution(completed);
|
|
9354
|
+
}
|
|
9355
|
+
async function runShellCommand(command, audit, options) {
|
|
9356
|
+
const startedAt = Date.now();
|
|
9357
|
+
const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
|
|
9358
|
+
const cmd = buildCommandRecord(command);
|
|
9359
|
+
entry.commands?.push(cmd);
|
|
9360
|
+
try {
|
|
9361
|
+
const { stdout, stderr } = await execAsync(command, {
|
|
9362
|
+
cwd: options?.cwd,
|
|
9363
|
+
timeout: options?.timeoutMs,
|
|
9364
|
+
maxBuffer: options?.maxBuffer
|
|
9365
|
+
});
|
|
9366
|
+
await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr, startedAt }, options);
|
|
9367
|
+
return { stdout: stdout ?? "", stderr: stderr ?? "", exitCode: 0 };
|
|
9368
|
+
} catch (e) {
|
|
9369
|
+
const err = e;
|
|
9370
|
+
const stdout = typeof err.stdout === "string" ? err.stdout : "";
|
|
9371
|
+
const stderr = typeof err.stderr === "string" ? err.stderr : "";
|
|
9372
|
+
const exitCode = typeof err.code === "number" ? err.code : 1;
|
|
9373
|
+
await finalizeAndWrite(
|
|
9374
|
+
entry,
|
|
9375
|
+
cmd,
|
|
9376
|
+
{ success: false, exitCode, stdout, stderr, error: err.message, startedAt },
|
|
9377
|
+
// Capture output for failures by default (so audits are useful)
|
|
9378
|
+
{ ...options, captureOutput: options?.captureOutput ?? true }
|
|
9379
|
+
);
|
|
9380
|
+
return { stdout, stderr, exitCode };
|
|
9381
|
+
}
|
|
9382
|
+
}
|
|
9383
|
+
function runShellCommandSync(command, audit, options) {
|
|
9384
|
+
const startedAt = Date.now();
|
|
9385
|
+
const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
|
|
9386
|
+
const cmd = buildCommandRecord(command);
|
|
9387
|
+
entry.commands?.push(cmd);
|
|
9388
|
+
try {
|
|
9389
|
+
const stdout = execSync(command, {
|
|
9390
|
+
cwd: options?.cwd,
|
|
9391
|
+
timeout: options?.timeoutMs,
|
|
9392
|
+
maxBuffer: options?.maxBuffer,
|
|
9393
|
+
encoding: "utf-8",
|
|
9394
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9395
|
+
});
|
|
9396
|
+
void finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout, stderr: "", startedAt }, options);
|
|
9397
|
+
return { stdout: stdout ?? "", exitCode: 0 };
|
|
9398
|
+
} catch (e) {
|
|
9399
|
+
const err = e;
|
|
9400
|
+
const stdout = typeof err.stdout === "string" ? err.stdout : "";
|
|
9401
|
+
const stderr = typeof err.stderr === "string" ? err.stderr : "";
|
|
9402
|
+
const exitCode = typeof err.status === "number" ? err.status : 1;
|
|
9403
|
+
void finalizeAndWrite(
|
|
9404
|
+
entry,
|
|
9405
|
+
cmd,
|
|
9406
|
+
{ success: false, exitCode, stdout, stderr, error: err.message, startedAt },
|
|
9407
|
+
{ ...options, captureOutput: options?.captureOutput ?? true }
|
|
9408
|
+
);
|
|
9409
|
+
return { stdout, exitCode };
|
|
9410
|
+
}
|
|
9411
|
+
}
|
|
9412
|
+
async function runExecFile(file, args, audit, options) {
|
|
9413
|
+
const startedAt = Date.now();
|
|
9414
|
+
const command = [file, ...args].join(" ");
|
|
9415
|
+
const entry = createAuditEntry(audit.actor, audit.source ?? "trie", audit.triggeredBy, audit.targetPath);
|
|
9416
|
+
const cmd = buildCommandRecord(command);
|
|
9417
|
+
entry.commands?.push(cmd);
|
|
9418
|
+
try {
|
|
9419
|
+
const { stdout, stderr } = await execFileAsync(file, args, {
|
|
9420
|
+
cwd: options?.cwd,
|
|
9421
|
+
timeout: options?.timeoutMs,
|
|
9422
|
+
maxBuffer: options?.maxBuffer
|
|
9423
|
+
});
|
|
9424
|
+
await finalizeAndWrite(entry, cmd, { success: true, exitCode: 0, stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), startedAt }, options);
|
|
9425
|
+
return { stdout: String(stdout ?? ""), stderr: String(stderr ?? ""), exitCode: 0 };
|
|
9426
|
+
} catch (e) {
|
|
9427
|
+
const err = e;
|
|
9428
|
+
const stdout = typeof err.stdout === "string" ? err.stdout : "";
|
|
9429
|
+
const stderr = typeof err.stderr === "string" ? err.stderr : "";
|
|
9430
|
+
const exitCode = typeof err.code === "number" ? err.code : 1;
|
|
9431
|
+
await finalizeAndWrite(
|
|
9432
|
+
entry,
|
|
9433
|
+
cmd,
|
|
9434
|
+
{ success: false, exitCode, stdout, stderr, error: err.message, startedAt },
|
|
9435
|
+
{ ...options, captureOutput: options?.captureOutput ?? true }
|
|
9436
|
+
);
|
|
9437
|
+
return { stdout, stderr, exitCode };
|
|
9438
|
+
}
|
|
9439
|
+
}
|
|
9440
|
+
|
|
9441
|
+
// src/skills/installer.ts
|
|
8906
9442
|
var GLOBAL_SKILLS_DIRS = {
|
|
8907
|
-
trie:
|
|
8908
|
-
cursor:
|
|
8909
|
-
claude:
|
|
8910
|
-
opencode:
|
|
9443
|
+
trie: join5(homedir(), ".trie", "skills"),
|
|
9444
|
+
cursor: join5(homedir(), ".cursor", "skills"),
|
|
9445
|
+
claude: join5(homedir(), ".claude", "skills"),
|
|
9446
|
+
opencode: join5(homedir(), ".config", "opencode", "skills")
|
|
8911
9447
|
};
|
|
8912
9448
|
var GLOBAL_SKILLS_DIR = GLOBAL_SKILLS_DIRS.trie;
|
|
8913
9449
|
function getAllGlobalSkillDirs() {
|
|
@@ -8941,26 +9477,74 @@ async function installSkill(source, skillName) {
|
|
|
8941
9477
|
}
|
|
8942
9478
|
const { owner, repo, skill: parsedSkill } = parsed;
|
|
8943
9479
|
const specifiedSkill = skillName || parsedSkill;
|
|
8944
|
-
const skillsDir =
|
|
8945
|
-
const tempDir =
|
|
9480
|
+
const skillsDir = join5(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
9481
|
+
const tempDir = join5(skillsDir, `.temp-${Date.now()}`);
|
|
8946
9482
|
try {
|
|
8947
|
-
await
|
|
9483
|
+
await mkdir3(skillsDir, { recursive: true });
|
|
9484
|
+
console.log("Cloning repository...");
|
|
8948
9485
|
const repoUrl = `https://github.com/${owner}/${repo}.git`;
|
|
8949
|
-
|
|
9486
|
+
if (!repoUrl.startsWith("https://github.com/") || repoUrl.includes("..") || repoUrl.includes("~")) {
|
|
9487
|
+
throw new Error("Invalid repository URL");
|
|
9488
|
+
}
|
|
9489
|
+
await runShellCommand(
|
|
9490
|
+
`git clone --depth 1 "${repoUrl}" "${tempDir}"`,
|
|
9491
|
+
{ actor: "internal:skills-installer", triggeredBy: "manual", targetPath: getWorkingDirectory(void 0, true) },
|
|
9492
|
+
{ timeoutMs: 6e4, captureOutput: false }
|
|
9493
|
+
);
|
|
8950
9494
|
const sourcePath = await findSkillPath(tempDir, specifiedSkill);
|
|
8951
9495
|
if (!sourcePath) {
|
|
8952
9496
|
throw new Error(`SKILL.md not found in repository. Searched in: root, skills/, ${specifiedSkill || "subdirectories"}`);
|
|
8953
9497
|
}
|
|
9498
|
+
console.log("Running security scan...");
|
|
9499
|
+
const securityScan = await scanSkillForThreats(sourcePath);
|
|
9500
|
+
console.log(`Security scan: ${formatSecuritySummary(securityScan)}`);
|
|
9501
|
+
if (securityScan.risks.length > 0) {
|
|
9502
|
+
console.warn("\n\u26A0\uFE0F SECURITY RISKS DETECTED\n");
|
|
9503
|
+
const criticalRisks = securityScan.risks.filter((r) => r.severity === "critical");
|
|
9504
|
+
const highRisks = securityScan.risks.filter((r) => r.severity === "high");
|
|
9505
|
+
for (const risk of criticalRisks) {
|
|
9506
|
+
console.warn(`\u{1F534} CRITICAL: ${risk.description}`);
|
|
9507
|
+
console.warn(` ${risk.location}: ${risk.pattern}`);
|
|
9508
|
+
console.warn(` \u2192 ${risk.recommendation}
|
|
9509
|
+
`);
|
|
9510
|
+
}
|
|
9511
|
+
for (const risk of highRisks) {
|
|
9512
|
+
console.warn(`\u{1F7E0} HIGH: ${risk.description}`);
|
|
9513
|
+
console.warn(` ${risk.location}`);
|
|
9514
|
+
console.warn(` \u2192 ${risk.recommendation}
|
|
9515
|
+
`);
|
|
9516
|
+
}
|
|
9517
|
+
if (criticalRisks.length > 0) {
|
|
9518
|
+
console.warn("\u26A0\uFE0F This skill has CRITICAL security risks. Use with extreme caution.\n");
|
|
9519
|
+
} else {
|
|
9520
|
+
console.warn("\u26A0\uFE0F Review these risks before using this skill.\n");
|
|
9521
|
+
}
|
|
9522
|
+
}
|
|
8954
9523
|
const parsed2 = await parseSkillMd(sourcePath);
|
|
8955
9524
|
const name = parsed2.frontmatter.name;
|
|
8956
|
-
const targetPath =
|
|
9525
|
+
const targetPath = join5(skillsDir, name);
|
|
8957
9526
|
await rm(targetPath, { recursive: true, force: true });
|
|
8958
9527
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
8959
|
-
await
|
|
9528
|
+
await writeFile3(join5(targetPath, ".installed.json"), JSON.stringify({
|
|
8960
9529
|
installedFrom: source,
|
|
8961
9530
|
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8962
|
-
repository: `${owner}/${repo}
|
|
9531
|
+
repository: `${owner}/${repo}`,
|
|
9532
|
+
securityScan: {
|
|
9533
|
+
safe: securityScan.safe,
|
|
9534
|
+
riskCount: securityScan.risks.length,
|
|
9535
|
+
criticalRisks: securityScan.risks.filter((r) => r.severity === "critical").length,
|
|
9536
|
+
highRisks: securityScan.risks.filter((r) => r.severity === "high").length,
|
|
9537
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9538
|
+
},
|
|
9539
|
+
risks: securityScan.risks,
|
|
9540
|
+
// Store full risk details
|
|
9541
|
+
permissions: securityScan.requiredPermissions
|
|
8963
9542
|
}, null, 2));
|
|
9543
|
+
if (securityScan.risks.length > 0) {
|
|
9544
|
+
console.log(`
|
|
9545
|
+
\u26A0\uFE0F View security details with: trie skills info ${name}
|
|
9546
|
+
`);
|
|
9547
|
+
}
|
|
8964
9548
|
return { success: true, name, path: targetPath };
|
|
8965
9549
|
} catch (error) {
|
|
8966
9550
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -8974,22 +9558,22 @@ async function findSkillPath(repoPath, skillName) {
|
|
|
8974
9558
|
const searchPaths = [];
|
|
8975
9559
|
if (skillName) {
|
|
8976
9560
|
searchPaths.push(
|
|
8977
|
-
|
|
8978
|
-
|
|
9561
|
+
join5(repoPath, "skills", skillName),
|
|
9562
|
+
join5(repoPath, skillName)
|
|
8979
9563
|
);
|
|
8980
9564
|
}
|
|
8981
9565
|
searchPaths.push(
|
|
8982
9566
|
repoPath,
|
|
8983
|
-
|
|
9567
|
+
join5(repoPath, "skill")
|
|
8984
9568
|
);
|
|
8985
9569
|
if (!skillName) {
|
|
8986
9570
|
try {
|
|
8987
|
-
const skillsSubdir =
|
|
9571
|
+
const skillsSubdir = join5(repoPath, "skills");
|
|
8988
9572
|
await access(skillsSubdir);
|
|
8989
|
-
const entries = await
|
|
9573
|
+
const entries = await readdir3(skillsSubdir, { withFileTypes: true });
|
|
8990
9574
|
for (const entry of entries) {
|
|
8991
9575
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
8992
|
-
searchPaths.push(
|
|
9576
|
+
searchPaths.push(join5(skillsSubdir, entry.name));
|
|
8993
9577
|
}
|
|
8994
9578
|
}
|
|
8995
9579
|
} catch {
|
|
@@ -9019,10 +9603,10 @@ async function findClaudeCodeSubagent(repoPath, skillName) {
|
|
|
9019
9603
|
skillName.replace(/-expert$/, "-expert.md")
|
|
9020
9604
|
];
|
|
9021
9605
|
for (const fileName of searchNames) {
|
|
9022
|
-
const filePath =
|
|
9606
|
+
const filePath = join5(repoPath, fileName);
|
|
9023
9607
|
try {
|
|
9024
9608
|
await access(filePath);
|
|
9025
|
-
const content = await
|
|
9609
|
+
const content = await readFile5(filePath, "utf-8");
|
|
9026
9610
|
if (content.startsWith("---")) {
|
|
9027
9611
|
return await convertClaudeCodeToSkillMd(repoPath, filePath, skillName);
|
|
9028
9612
|
}
|
|
@@ -9030,7 +9614,7 @@ async function findClaudeCodeSubagent(repoPath, skillName) {
|
|
|
9030
9614
|
}
|
|
9031
9615
|
}
|
|
9032
9616
|
try {
|
|
9033
|
-
const entries = await
|
|
9617
|
+
const entries = await readdir3(repoPath);
|
|
9034
9618
|
const mdFiles = entries.filter((e) => e.endsWith(".md") && !e.toLowerCase().includes("readme") && !e.toLowerCase().includes("license"));
|
|
9035
9619
|
if (mdFiles.length > 0) {
|
|
9036
9620
|
const availableSkills = mdFiles.map((f) => f.replace(".md", "")).join(", ");
|
|
@@ -9044,7 +9628,7 @@ async function findClaudeCodeSubagent(repoPath, skillName) {
|
|
|
9044
9628
|
return null;
|
|
9045
9629
|
}
|
|
9046
9630
|
async function convertClaudeCodeToSkillMd(repoPath, filePath, skillName) {
|
|
9047
|
-
const content = await
|
|
9631
|
+
const content = await readFile5(filePath, "utf-8");
|
|
9048
9632
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
9049
9633
|
if (!fmMatch) {
|
|
9050
9634
|
throw new Error("Invalid frontmatter format");
|
|
@@ -9057,8 +9641,8 @@ async function convertClaudeCodeToSkillMd(repoPath, filePath, skillName) {
|
|
|
9057
9641
|
const name = nameMatch ? nameMatch[1].trim() : skillName;
|
|
9058
9642
|
const description = descMatch ? descMatch[1].trim() : `Skill from Claude Code subagent: ${name}`;
|
|
9059
9643
|
const model = modelMatch ? modelMatch[1].trim() : void 0;
|
|
9060
|
-
const tempSkillDir =
|
|
9061
|
-
await
|
|
9644
|
+
const tempSkillDir = join5(repoPath, ".converted-skill", name);
|
|
9645
|
+
await mkdir3(tempSkillDir, { recursive: true });
|
|
9062
9646
|
let newContent = `---
|
|
9063
9647
|
name: ${name}
|
|
9064
9648
|
description: ${description}
|
|
@@ -9070,23 +9654,23 @@ tags: [claude-code, subagent${model ? `, ${model}` : ""}]
|
|
|
9070
9654
|
|
|
9071
9655
|
${body}
|
|
9072
9656
|
`;
|
|
9073
|
-
await
|
|
9657
|
+
await writeFile3(join5(tempSkillDir, "SKILL.md"), newContent, "utf-8");
|
|
9074
9658
|
return tempSkillDir;
|
|
9075
9659
|
}
|
|
9076
9660
|
async function listInstalledSkills() {
|
|
9077
|
-
const skillsDir =
|
|
9661
|
+
const skillsDir = join5(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
9078
9662
|
const skills = [];
|
|
9079
9663
|
try {
|
|
9080
|
-
const entries = await
|
|
9664
|
+
const entries = await readdir3(skillsDir, { withFileTypes: true });
|
|
9081
9665
|
for (const entry of entries) {
|
|
9082
9666
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
9083
|
-
const skillPath =
|
|
9667
|
+
const skillPath = join5(skillsDir, entry.name);
|
|
9084
9668
|
try {
|
|
9085
9669
|
const parsed = await parseSkillMd(skillPath);
|
|
9086
|
-
const metaPath =
|
|
9670
|
+
const metaPath = join5(skillPath, ".installed.json");
|
|
9087
9671
|
let meta = { installedFrom: "unknown", installedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9088
9672
|
try {
|
|
9089
|
-
meta = JSON.parse(await
|
|
9673
|
+
meta = JSON.parse(await readFile5(metaPath, "utf-8"));
|
|
9090
9674
|
} catch {
|
|
9091
9675
|
}
|
|
9092
9676
|
skills.push({
|
|
@@ -9104,8 +9688,8 @@ async function listInstalledSkills() {
|
|
|
9104
9688
|
return skills;
|
|
9105
9689
|
}
|
|
9106
9690
|
async function removeSkill(skillName) {
|
|
9107
|
-
const skillsDir =
|
|
9108
|
-
const skillPath =
|
|
9691
|
+
const skillsDir = join5(getWorkingDirectory(void 0, true), ".trie", "skills");
|
|
9692
|
+
const skillPath = join5(skillsDir, skillName);
|
|
9109
9693
|
try {
|
|
9110
9694
|
await rm(skillPath, { recursive: true });
|
|
9111
9695
|
return true;
|
|
@@ -9116,16 +9700,16 @@ async function removeSkill(skillName) {
|
|
|
9116
9700
|
async function listGlobalSkills() {
|
|
9117
9701
|
const skills = [];
|
|
9118
9702
|
try {
|
|
9119
|
-
const entries = await
|
|
9703
|
+
const entries = await readdir3(GLOBAL_SKILLS_DIR, { withFileTypes: true });
|
|
9120
9704
|
for (const entry of entries) {
|
|
9121
9705
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
9122
|
-
const skillPath =
|
|
9706
|
+
const skillPath = join5(GLOBAL_SKILLS_DIR, entry.name);
|
|
9123
9707
|
try {
|
|
9124
9708
|
const parsed = await parseSkillMd(skillPath);
|
|
9125
|
-
const metaPath =
|
|
9709
|
+
const metaPath = join5(skillPath, ".installed.json");
|
|
9126
9710
|
let meta = { installedFrom: "custom", installedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
9127
9711
|
try {
|
|
9128
|
-
meta = JSON.parse(await
|
|
9712
|
+
meta = JSON.parse(await readFile5(metaPath, "utf-8"));
|
|
9129
9713
|
} catch {
|
|
9130
9714
|
}
|
|
9131
9715
|
skills.push({
|
|
@@ -9147,14 +9731,14 @@ async function writeSkillToAllDirs(name, skillMdContent, metadata) {
|
|
|
9147
9731
|
let primaryPath = "";
|
|
9148
9732
|
for (const [tool, baseDir] of Object.entries(GLOBAL_SKILLS_DIRS)) {
|
|
9149
9733
|
try {
|
|
9150
|
-
await
|
|
9151
|
-
const skillPath =
|
|
9734
|
+
await mkdir3(baseDir, { recursive: true });
|
|
9735
|
+
const skillPath = join5(baseDir, name);
|
|
9152
9736
|
if (existsSync3(skillPath)) {
|
|
9153
9737
|
continue;
|
|
9154
9738
|
}
|
|
9155
|
-
await
|
|
9156
|
-
await
|
|
9157
|
-
await
|
|
9739
|
+
await mkdir3(skillPath, { recursive: true });
|
|
9740
|
+
await writeFile3(join5(skillPath, "SKILL.md"), skillMdContent, "utf-8");
|
|
9741
|
+
await writeFile3(join5(skillPath, ".installed.json"), JSON.stringify(metadata, null, 2));
|
|
9158
9742
|
writtenTo.push(tool);
|
|
9159
9743
|
if (tool === "trie") {
|
|
9160
9744
|
primaryPath = skillPath;
|
|
@@ -9165,25 +9749,25 @@ async function writeSkillToAllDirs(name, skillMdContent, metadata) {
|
|
|
9165
9749
|
if (!primaryPath && writtenTo.length > 0) {
|
|
9166
9750
|
const firstTool = writtenTo[0];
|
|
9167
9751
|
if (firstTool) {
|
|
9168
|
-
primaryPath =
|
|
9752
|
+
primaryPath = join5(GLOBAL_SKILLS_DIRS[firstTool], name);
|
|
9169
9753
|
}
|
|
9170
9754
|
}
|
|
9171
9755
|
return { primaryPath, writtenTo };
|
|
9172
9756
|
}
|
|
9173
9757
|
async function createSkillFromFile(filePath, skillName, description) {
|
|
9174
9758
|
try {
|
|
9175
|
-
const stats = await
|
|
9759
|
+
const stats = await stat2(filePath);
|
|
9176
9760
|
if (!stats.isFile()) {
|
|
9177
9761
|
return { success: false, name: "unknown", error: "Path is not a file" };
|
|
9178
9762
|
}
|
|
9179
|
-
const content = await
|
|
9763
|
+
const content = await readFile5(filePath, "utf-8");
|
|
9180
9764
|
if (!content.trim()) {
|
|
9181
9765
|
return { success: false, name: "unknown", error: "File is empty" };
|
|
9182
9766
|
}
|
|
9183
9767
|
const ext = extname2(filePath);
|
|
9184
9768
|
const baseFileName = basename2(filePath, ext);
|
|
9185
9769
|
const name = skillName || baseFileName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
|
|
9186
|
-
const primarySkillPath =
|
|
9770
|
+
const primarySkillPath = join5(GLOBAL_SKILLS_DIR, name);
|
|
9187
9771
|
if (existsSync3(primarySkillPath)) {
|
|
9188
9772
|
return { success: false, name, error: `Skill "${name}" already exists. Use a different name or remove the existing skill.` };
|
|
9189
9773
|
}
|
|
@@ -9223,7 +9807,7 @@ ${content}
|
|
|
9223
9807
|
async function removeGlobalSkill(skillName) {
|
|
9224
9808
|
let removed = false;
|
|
9225
9809
|
for (const baseDir of getAllGlobalSkillDirs()) {
|
|
9226
|
-
const skillPath =
|
|
9810
|
+
const skillPath = join5(baseDir, skillName);
|
|
9227
9811
|
try {
|
|
9228
9812
|
if (existsSync3(skillPath)) {
|
|
9229
9813
|
await rm(skillPath, { recursive: true });
|
|
@@ -9236,18 +9820,18 @@ async function removeGlobalSkill(skillName) {
|
|
|
9236
9820
|
}
|
|
9237
9821
|
|
|
9238
9822
|
// src/utils/context-state.ts
|
|
9239
|
-
import { readFile as
|
|
9823
|
+
import { readFile as readFile6, mkdir as mkdir4 } from "fs/promises";
|
|
9240
9824
|
import { existsSync as existsSync4 } from "fs";
|
|
9241
|
-
import { join as
|
|
9825
|
+
import { join as join6, basename as basename3 } from "path";
|
|
9242
9826
|
var AGENTS_MD_PATH = ".trie/AGENTS.md";
|
|
9243
9827
|
var STATE_JSON_PATH = ".trie/state.json";
|
|
9244
9828
|
async function loadContextState() {
|
|
9245
9829
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9246
|
-
const statePath =
|
|
9830
|
+
const statePath = join6(workDir, STATE_JSON_PATH);
|
|
9247
9831
|
const defaults = getDefaultState();
|
|
9248
9832
|
try {
|
|
9249
9833
|
if (existsSync4(statePath)) {
|
|
9250
|
-
const content = await
|
|
9834
|
+
const content = await readFile6(statePath, "utf-8");
|
|
9251
9835
|
const loaded = JSON.parse(content);
|
|
9252
9836
|
return {
|
|
9253
9837
|
...defaults,
|
|
@@ -9261,9 +9845,9 @@ async function loadContextState() {
|
|
|
9261
9845
|
}
|
|
9262
9846
|
async function saveContextState(state) {
|
|
9263
9847
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9264
|
-
const trieDir =
|
|
9265
|
-
const statePath =
|
|
9266
|
-
await
|
|
9848
|
+
const trieDir = join6(workDir, ".trie");
|
|
9849
|
+
const statePath = join6(workDir, STATE_JSON_PATH);
|
|
9850
|
+
await mkdir4(trieDir, { recursive: true });
|
|
9267
9851
|
await atomicWriteJSON(statePath, state);
|
|
9268
9852
|
}
|
|
9269
9853
|
async function updateContextAfterScan(results, filesScanned, contextSignals, duration) {
|
|
@@ -9322,10 +9906,10 @@ async function updateContextAfterScan(results, filesScanned, contextSignals, dur
|
|
|
9322
9906
|
}
|
|
9323
9907
|
async function updateAgentsMd(state) {
|
|
9324
9908
|
const workDir = getWorkingDirectory(void 0, true);
|
|
9325
|
-
const mdPath =
|
|
9909
|
+
const mdPath = join6(workDir, AGENTS_MD_PATH);
|
|
9326
9910
|
let content;
|
|
9327
9911
|
try {
|
|
9328
|
-
content = await
|
|
9912
|
+
content = await readFile6(mdPath, "utf-8");
|
|
9329
9913
|
} catch {
|
|
9330
9914
|
content = getAgentsMdTemplate();
|
|
9331
9915
|
}
|
|
@@ -9658,9 +10242,8 @@ async function getContextForAI() {
|
|
|
9658
10242
|
|
|
9659
10243
|
// src/skills/gating.ts
|
|
9660
10244
|
import { existsSync as existsSync5 } from "fs";
|
|
9661
|
-
import { readFile as
|
|
9662
|
-
import { join as
|
|
9663
|
-
import { execSync } from "child_process";
|
|
10245
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
10246
|
+
import { join as join7 } from "path";
|
|
9664
10247
|
async function checkSkillRequirements(frontmatter, projectDir) {
|
|
9665
10248
|
const result = { allowed: true };
|
|
9666
10249
|
if (frontmatter.requires && frontmatter.requires.length > 0) {
|
|
@@ -9716,7 +10299,7 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
9716
10299
|
}
|
|
9717
10300
|
}
|
|
9718
10301
|
if (reqs.configFiles && reqs.configFiles.length > 0) {
|
|
9719
|
-
const missing = reqs.configFiles.filter((file) => !existsSync5(
|
|
10302
|
+
const missing = reqs.configFiles.filter((file) => !existsSync5(join7(projectDir, file)));
|
|
9720
10303
|
if (missing.length > 0) {
|
|
9721
10304
|
result.allowed = false;
|
|
9722
10305
|
result.missingConfigs = missing;
|
|
@@ -9728,11 +10311,11 @@ async function checkSkillRequirements(frontmatter, projectDir) {
|
|
|
9728
10311
|
}
|
|
9729
10312
|
async function getProjectDependencies(projectDir) {
|
|
9730
10313
|
try {
|
|
9731
|
-
const pkgPath =
|
|
10314
|
+
const pkgPath = join7(projectDir, "package.json");
|
|
9732
10315
|
if (!existsSync5(pkgPath)) {
|
|
9733
10316
|
return /* @__PURE__ */ new Set();
|
|
9734
10317
|
}
|
|
9735
|
-
const pkg = JSON.parse(await
|
|
10318
|
+
const pkg = JSON.parse(await readFile7(pkgPath, "utf-8"));
|
|
9736
10319
|
return /* @__PURE__ */ new Set([
|
|
9737
10320
|
...Object.keys(pkg.dependencies || {}),
|
|
9738
10321
|
...Object.keys(pkg.devDependencies || {})
|
|
@@ -9742,13 +10325,13 @@ async function getProjectDependencies(projectDir) {
|
|
|
9742
10325
|
}
|
|
9743
10326
|
}
|
|
9744
10327
|
function isBinaryAvailable(binary) {
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
10328
|
+
const command = process.platform === "win32" ? `where ${binary}` : `which ${binary}`;
|
|
10329
|
+
const { exitCode } = runShellCommandSync(
|
|
10330
|
+
command,
|
|
10331
|
+
{ actor: "internal:skill-gating", triggeredBy: "scan", targetPath: process.cwd() },
|
|
10332
|
+
{ captureOutput: false }
|
|
10333
|
+
);
|
|
10334
|
+
return exitCode === 0;
|
|
9752
10335
|
}
|
|
9753
10336
|
function formatGatingReason(result) {
|
|
9754
10337
|
if (result.allowed) return "Allowed";
|
|
@@ -10198,8 +10781,8 @@ var CustomSkill = class extends BaseSkill {
|
|
|
10198
10781
|
};
|
|
10199
10782
|
|
|
10200
10783
|
// src/skills/built-in/registry.ts
|
|
10201
|
-
import { readdir as
|
|
10202
|
-
import { join as
|
|
10784
|
+
import { readdir as readdir4, readFile as readFile8 } from "fs/promises";
|
|
10785
|
+
import { join as join8 } from "path";
|
|
10203
10786
|
var SkillRegistryImpl = class {
|
|
10204
10787
|
skills = /* @__PURE__ */ new Map();
|
|
10205
10788
|
customSkillsLoaded = false;
|
|
@@ -10255,14 +10838,14 @@ var SkillRegistryImpl = class {
|
|
|
10255
10838
|
async loadCustomSkills() {
|
|
10256
10839
|
if (this.customSkillsLoaded) return;
|
|
10257
10840
|
try {
|
|
10258
|
-
const skillsDir =
|
|
10259
|
-
const files = await
|
|
10841
|
+
const skillsDir = join8(getWorkingDirectory(void 0, true), ".trie", "agents");
|
|
10842
|
+
const files = await readdir4(skillsDir);
|
|
10260
10843
|
const jsonFiles = files.filter((f) => f.endsWith(".json"));
|
|
10261
10844
|
let loadedCount = 0;
|
|
10262
10845
|
for (const file of jsonFiles) {
|
|
10263
10846
|
try {
|
|
10264
|
-
const configPath =
|
|
10265
|
-
const content = await
|
|
10847
|
+
const configPath = join8(skillsDir, file);
|
|
10848
|
+
const content = await readFile8(configPath, "utf-8");
|
|
10266
10849
|
const config = JSON.parse(content);
|
|
10267
10850
|
const skill = new CustomSkill(config);
|
|
10268
10851
|
this.skills.set(skill.name, skill);
|
|
@@ -10430,6 +11013,13 @@ function getSkillRegistry() {
|
|
|
10430
11013
|
export {
|
|
10431
11014
|
SuperReviewerSkill,
|
|
10432
11015
|
CRITICAL_REVIEW_CHECKLIST,
|
|
11016
|
+
getRecentAuditLogs,
|
|
11017
|
+
getSkillAuditLogs,
|
|
11018
|
+
getAuditStatistics,
|
|
11019
|
+
formatAuditLog,
|
|
11020
|
+
runShellCommand,
|
|
11021
|
+
runShellCommandSync,
|
|
11022
|
+
runExecFile,
|
|
10433
11023
|
installSkill,
|
|
10434
11024
|
listInstalledSkills,
|
|
10435
11025
|
removeSkill,
|
|
@@ -10451,4 +11041,4 @@ export {
|
|
|
10451
11041
|
CustomSkill,
|
|
10452
11042
|
getSkillRegistry
|
|
10453
11043
|
};
|
|
10454
|
-
//# sourceMappingURL=chunk-
|
|
11044
|
+
//# sourceMappingURL=chunk-LKXDJESG.js.map
|