pybao-cli 1.3.3
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/LICENSE +201 -0
- package/README.md +440 -0
- package/README.zh-CN.md +338 -0
- package/cli-acp.js +82 -0
- package/cli.js +105 -0
- package/dist/REPL-WPV32MTF.js +42 -0
- package/dist/REPL-WPV32MTF.js.map +7 -0
- package/dist/acp-75HO2LBV.js +1357 -0
- package/dist/acp-75HO2LBV.js.map +7 -0
- package/dist/agentsValidate-6Z57ARKC.js +373 -0
- package/dist/agentsValidate-6Z57ARKC.js.map +7 -0
- package/dist/ask-NXXXCGY4.js +125 -0
- package/dist/ask-NXXXCGY4.js.map +7 -0
- package/dist/autoUpdater-PJMGNPUG.js +17 -0
- package/dist/autoUpdater-PJMGNPUG.js.map +7 -0
- package/dist/chunk-27GYWUY2.js +72 -0
- package/dist/chunk-27GYWUY2.js.map +7 -0
- package/dist/chunk-3DFBSQIT.js +23 -0
- package/dist/chunk-3DFBSQIT.js.map +7 -0
- package/dist/chunk-3KNGJX7Q.js +794 -0
- package/dist/chunk-3KNGJX7Q.js.map +7 -0
- package/dist/chunk-3PDD7M4T.js +164 -0
- package/dist/chunk-3PDD7M4T.js.map +7 -0
- package/dist/chunk-3ZNSAB7B.js +515 -0
- package/dist/chunk-3ZNSAB7B.js.map +7 -0
- package/dist/chunk-4SNFQYCY.js +511 -0
- package/dist/chunk-4SNFQYCY.js.map +7 -0
- package/dist/chunk-4XPNRLJG.js +1609 -0
- package/dist/chunk-4XPNRLJG.js.map +7 -0
- package/dist/chunk-5P7HBXTD.js +12 -0
- package/dist/chunk-5P7HBXTD.js.map +7 -0
- package/dist/chunk-6RZIUY5K.js +191 -0
- package/dist/chunk-6RZIUY5K.js.map +7 -0
- package/dist/chunk-6WELHKDA.js +240 -0
- package/dist/chunk-6WELHKDA.js.map +7 -0
- package/dist/chunk-7AAE6EO2.js +145 -0
- package/dist/chunk-7AAE6EO2.js.map +7 -0
- package/dist/chunk-A3BVXXA3.js +47 -0
- package/dist/chunk-A3BVXXA3.js.map +7 -0
- package/dist/chunk-A6PUMROK.js +152 -0
- package/dist/chunk-A6PUMROK.js.map +7 -0
- package/dist/chunk-BH3Y62E3.js +11 -0
- package/dist/chunk-BH3Y62E3.js.map +7 -0
- package/dist/chunk-BJSWTHRM.js +16 -0
- package/dist/chunk-BJSWTHRM.js.map +7 -0
- package/dist/chunk-BQA2EOUU.js +124 -0
- package/dist/chunk-BQA2EOUU.js.map +7 -0
- package/dist/chunk-CZZKRPE2.js +19 -0
- package/dist/chunk-CZZKRPE2.js.map +7 -0
- package/dist/chunk-ERMQRV55.js +24 -0
- package/dist/chunk-ERMQRV55.js.map +7 -0
- package/dist/chunk-HB2P6645.js +34 -0
- package/dist/chunk-HB2P6645.js.map +7 -0
- package/dist/chunk-HIRIJ2LQ.js +1256 -0
- package/dist/chunk-HIRIJ2LQ.js.map +7 -0
- package/dist/chunk-ICTEVBLN.js +735 -0
- package/dist/chunk-ICTEVBLN.js.map +7 -0
- package/dist/chunk-JKGOGSFT.js +128 -0
- package/dist/chunk-JKGOGSFT.js.map +7 -0
- package/dist/chunk-JZDE77EH.js +836 -0
- package/dist/chunk-JZDE77EH.js.map +7 -0
- package/dist/chunk-M624LT6O.js +17 -0
- package/dist/chunk-M624LT6O.js.map +7 -0
- package/dist/chunk-OMELVAJD.js +96 -0
- package/dist/chunk-OMELVAJD.js.map +7 -0
- package/dist/chunk-OUXHGDLH.js +95 -0
- package/dist/chunk-OUXHGDLH.js.map +7 -0
- package/dist/chunk-PCXUZ6AT.js +249 -0
- package/dist/chunk-PCXUZ6AT.js.map +7 -0
- package/dist/chunk-Q24ZGKIE.js +1097 -0
- package/dist/chunk-Q24ZGKIE.js.map +7 -0
- package/dist/chunk-QBHEERCF.js +30254 -0
- package/dist/chunk-QBHEERCF.js.map +7 -0
- package/dist/chunk-QIHB5PYM.js +472 -0
- package/dist/chunk-QIHB5PYM.js.map +7 -0
- package/dist/chunk-RQVLBMP7.js +24 -0
- package/dist/chunk-RQVLBMP7.js.map +7 -0
- package/dist/chunk-SWYJOV5E.js +490 -0
- package/dist/chunk-SWYJOV5E.js.map +7 -0
- package/dist/chunk-T6GVXTNQ.js +21 -0
- package/dist/chunk-T6GVXTNQ.js.map +7 -0
- package/dist/chunk-T7GPUZVK.js +766 -0
- package/dist/chunk-T7GPUZVK.js.map +7 -0
- package/dist/chunk-TXFCNQDE.js +2934 -0
- package/dist/chunk-TXFCNQDE.js.map +7 -0
- package/dist/chunk-UNNVICVU.js +95 -0
- package/dist/chunk-UNNVICVU.js.map +7 -0
- package/dist/chunk-UUNVJZWA.js +515 -0
- package/dist/chunk-UUNVJZWA.js.map +7 -0
- package/dist/chunk-VRGR4ZTQ.js +49 -0
- package/dist/chunk-VRGR4ZTQ.js.map +7 -0
- package/dist/chunk-VTVTEE5N.js +2613 -0
- package/dist/chunk-VTVTEE5N.js.map +7 -0
- package/dist/chunk-WPTPPOYN.js +936 -0
- package/dist/chunk-WPTPPOYN.js.map +7 -0
- package/dist/chunk-XXFY63TM.js +196 -0
- package/dist/chunk-XXFY63TM.js.map +7 -0
- package/dist/chunk-Z3HMXDXP.js +654 -0
- package/dist/chunk-Z3HMXDXP.js.map +7 -0
- package/dist/chunk-ZJGXEWKF.js +138 -0
- package/dist/chunk-ZJGXEWKF.js.map +7 -0
- package/dist/cli-RFYBXM7F.js +3917 -0
- package/dist/cli-RFYBXM7F.js.map +7 -0
- package/dist/commands-YOXMODDO.js +46 -0
- package/dist/commands-YOXMODDO.js.map +7 -0
- package/dist/config-5OPX3H2K.js +81 -0
- package/dist/config-5OPX3H2K.js.map +7 -0
- package/dist/context-THRRBPFP.js +30 -0
- package/dist/context-THRRBPFP.js.map +7 -0
- package/dist/costTracker-ELNBZ2DN.js +19 -0
- package/dist/costTracker-ELNBZ2DN.js.map +7 -0
- package/dist/customCommands-4XOZH44N.js +25 -0
- package/dist/customCommands-4XOZH44N.js.map +7 -0
- package/dist/env-EL4KBHMB.js +22 -0
- package/dist/env-EL4KBHMB.js.map +7 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +7 -0
- package/dist/kodeAgentSessionId-PROTVRBR.js +13 -0
- package/dist/kodeAgentSessionId-PROTVRBR.js.map +7 -0
- package/dist/kodeAgentSessionLoad-UMPV7MC3.js +18 -0
- package/dist/kodeAgentSessionLoad-UMPV7MC3.js.map +7 -0
- package/dist/kodeAgentSessionResume-YJS4FVQM.js +16 -0
- package/dist/kodeAgentSessionResume-YJS4FVQM.js.map +7 -0
- package/dist/kodeAgentStreamJson-3T26CHCP.js +13 -0
- package/dist/kodeAgentStreamJson-3T26CHCP.js.map +7 -0
- package/dist/kodeAgentStreamJsonSession-BZS2VDCY.js +131 -0
- package/dist/kodeAgentStreamJsonSession-BZS2VDCY.js.map +7 -0
- package/dist/kodeAgentStructuredStdio-TNB6U6SP.js +10 -0
- package/dist/kodeAgentStructuredStdio-TNB6U6SP.js.map +7 -0
- package/dist/kodeHooks-VUAWIY2D.js +36 -0
- package/dist/kodeHooks-VUAWIY2D.js.map +7 -0
- package/dist/llm-A3BCM4Q2.js +3118 -0
- package/dist/llm-A3BCM4Q2.js.map +7 -0
- package/dist/llmLazy-ZJSRLZVD.js +15 -0
- package/dist/llmLazy-ZJSRLZVD.js.map +7 -0
- package/dist/loader-HZQBWO74.js +28 -0
- package/dist/loader-HZQBWO74.js.map +7 -0
- package/dist/mcp-XKOJ55B2.js +49 -0
- package/dist/mcp-XKOJ55B2.js.map +7 -0
- package/dist/mentionProcessor-ANYU5MLF.js +211 -0
- package/dist/mentionProcessor-ANYU5MLF.js.map +7 -0
- package/dist/messages-75DL5XBP.js +63 -0
- package/dist/messages-75DL5XBP.js.map +7 -0
- package/dist/model-OPJGJZRC.js +30 -0
- package/dist/model-OPJGJZRC.js.map +7 -0
- package/dist/openai-DT54BAFP.js +29 -0
- package/dist/openai-DT54BAFP.js.map +7 -0
- package/dist/outputStyles-TPFVI52O.js +28 -0
- package/dist/outputStyles-TPFVI52O.js.map +7 -0
- package/dist/package.json +4 -0
- package/dist/pluginRuntime-W74PYSZ4.js +218 -0
- package/dist/pluginRuntime-W74PYSZ4.js.map +7 -0
- package/dist/pluginValidation-FALYRVI2.js +17 -0
- package/dist/pluginValidation-FALYRVI2.js.map +7 -0
- package/dist/prompts-J4TPRMJ3.js +48 -0
- package/dist/prompts-J4TPRMJ3.js.map +7 -0
- package/dist/query-K3QKBVDN.js +50 -0
- package/dist/query-K3QKBVDN.js.map +7 -0
- package/dist/responsesStreaming-HMB74TRD.js +10 -0
- package/dist/responsesStreaming-HMB74TRD.js.map +7 -0
- package/dist/ripgrep-XJGSUBG7.js +17 -0
- package/dist/ripgrep-XJGSUBG7.js.map +7 -0
- package/dist/skillMarketplace-AUGKNCPW.js +37 -0
- package/dist/skillMarketplace-AUGKNCPW.js.map +7 -0
- package/dist/state-DQYRXKTG.js +16 -0
- package/dist/state-DQYRXKTG.js.map +7 -0
- package/dist/theme-MS5HDUBJ.js +14 -0
- package/dist/theme-MS5HDUBJ.js.map +7 -0
- package/dist/toolPermissionContext-GYD5LYFK.js +17 -0
- package/dist/toolPermissionContext-GYD5LYFK.js.map +7 -0
- package/dist/toolPermissionSettings-4MPZVYDR.js +18 -0
- package/dist/toolPermissionSettings-4MPZVYDR.js.map +7 -0
- package/dist/tools-QW6SIJLJ.js +47 -0
- package/dist/tools-QW6SIJLJ.js.map +7 -0
- package/dist/userInput-F2PGBRFU.js +311 -0
- package/dist/userInput-F2PGBRFU.js.map +7 -0
- package/dist/uuid-GYYCQ6QK.js +9 -0
- package/dist/uuid-GYYCQ6QK.js.map +7 -0
- package/dist/yoga.wasm +0 -0
- package/package.json +136 -0
- package/scripts/binary-utils.cjs +62 -0
- package/scripts/cli-acp-wrapper.cjs +82 -0
- package/scripts/cli-wrapper.cjs +105 -0
- package/scripts/postinstall.js +144 -0
- package/yoga.wasm +0 -0
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
import { createRequire as __pybCreateRequire } from "node:module";
|
|
2
|
+
const require = __pybCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
getSessionPlugins
|
|
5
|
+
} from "./chunk-BJSWTHRM.js";
|
|
6
|
+
import {
|
|
7
|
+
debug
|
|
8
|
+
} from "./chunk-3KNGJX7Q.js";
|
|
9
|
+
import {
|
|
10
|
+
getCwd,
|
|
11
|
+
getKodeBaseDir,
|
|
12
|
+
logError
|
|
13
|
+
} from "./chunk-TXFCNQDE.js";
|
|
14
|
+
|
|
15
|
+
// src/services/plugins/customCommands.ts
|
|
16
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
17
|
+
import { basename, dirname, join, relative, sep } from "path";
|
|
18
|
+
import { homedir } from "os";
|
|
19
|
+
import { memoize } from "lodash-es";
|
|
20
|
+
import { execFile } from "child_process";
|
|
21
|
+
import { promisify } from "util";
|
|
22
|
+
import matter from "gray-matter";
|
|
23
|
+
import yaml from "js-yaml";
|
|
24
|
+
var execFileAsync = promisify(execFile);
|
|
25
|
+
async function executeBashCommands(content) {
|
|
26
|
+
const bashCommandRegex = /!\`([^`]+)\`/g;
|
|
27
|
+
const matches = [...content.matchAll(bashCommandRegex)];
|
|
28
|
+
if (matches.length === 0) {
|
|
29
|
+
return content;
|
|
30
|
+
}
|
|
31
|
+
let result = content;
|
|
32
|
+
for (const match of matches) {
|
|
33
|
+
const fullMatch = match[0];
|
|
34
|
+
const command = match[1].trim();
|
|
35
|
+
try {
|
|
36
|
+
const parts = command.split(/\s+/);
|
|
37
|
+
const cmd = parts[0];
|
|
38
|
+
const args = parts.slice(1);
|
|
39
|
+
const { stdout, stderr } = await execFileAsync(cmd, args, {
|
|
40
|
+
timeout: 5e3,
|
|
41
|
+
encoding: "utf8",
|
|
42
|
+
cwd: getCwd()
|
|
43
|
+
});
|
|
44
|
+
const output = stdout.trim() || stderr.trim() || "(no output)";
|
|
45
|
+
result = result.replace(fullMatch, output);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logError(error);
|
|
48
|
+
debug.warn("CUSTOM_COMMAND_BASH_EXEC_FAILED", {
|
|
49
|
+
command,
|
|
50
|
+
error: error instanceof Error ? error.message : String(error)
|
|
51
|
+
});
|
|
52
|
+
result = result.replace(fullMatch, `(error executing: ${command})`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
async function resolveFileReferences(content) {
|
|
58
|
+
const fileRefRegex = /@([a-zA-Z0-9/._-]+(?:\.[a-zA-Z0-9]+)?)/g;
|
|
59
|
+
const matches = [...content.matchAll(fileRefRegex)];
|
|
60
|
+
if (matches.length === 0) {
|
|
61
|
+
return content;
|
|
62
|
+
}
|
|
63
|
+
let result = content;
|
|
64
|
+
for (const match of matches) {
|
|
65
|
+
const fullMatch = match[0];
|
|
66
|
+
const filePath = match[1];
|
|
67
|
+
if (filePath.startsWith("agent-")) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const fullPath = join(getCwd(), filePath);
|
|
72
|
+
if (existsSync(fullPath)) {
|
|
73
|
+
const fileContent = readFileSync(fullPath, { encoding: "utf-8" });
|
|
74
|
+
const formattedContent = `
|
|
75
|
+
|
|
76
|
+
## File: ${filePath}
|
|
77
|
+
\`\`\`
|
|
78
|
+
${fileContent}
|
|
79
|
+
\`\`\`
|
|
80
|
+
`;
|
|
81
|
+
result = result.replace(fullMatch, formattedContent);
|
|
82
|
+
} else {
|
|
83
|
+
result = result.replace(fullMatch, `(file not found: ${filePath})`);
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
logError(error);
|
|
87
|
+
debug.warn("CUSTOM_COMMAND_FILE_READ_FAILED", {
|
|
88
|
+
filePath,
|
|
89
|
+
error: error instanceof Error ? error.message : String(error)
|
|
90
|
+
});
|
|
91
|
+
result = result.replace(fullMatch, `(error reading: ${filePath})`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function parseFrontmatter(content) {
|
|
97
|
+
const yamlSchema = yaml.JSON_SCHEMA;
|
|
98
|
+
const parsed = matter(content, {
|
|
99
|
+
engines: {
|
|
100
|
+
yaml: {
|
|
101
|
+
parse: (input) => yaml.load(input, yamlSchema ? { schema: yamlSchema } : void 0) ?? {}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
frontmatter: parsed.data ?? {},
|
|
107
|
+
content: parsed.content ?? ""
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function isSkillMarkdownFile(filePath) {
|
|
111
|
+
return /^skill\.md$/i.test(basename(filePath));
|
|
112
|
+
}
|
|
113
|
+
function getUserKodeBaseDir() {
|
|
114
|
+
return getKodeBaseDir();
|
|
115
|
+
}
|
|
116
|
+
function toBoolean(value) {
|
|
117
|
+
if (typeof value === "boolean") return value;
|
|
118
|
+
if (typeof value === "string") {
|
|
119
|
+
const normalized = value.trim().toLowerCase();
|
|
120
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
121
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
function parseAllowedTools(value) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
return value.map((v) => String(v).trim()).filter(Boolean);
|
|
128
|
+
}
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
const trimmed = value.trim();
|
|
131
|
+
if (!trimmed) return [];
|
|
132
|
+
return trimmed.split(/\s+/).map((v) => v.trim()).filter(Boolean);
|
|
133
|
+
}
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
function parseMaxThinkingTokens(frontmatter) {
|
|
137
|
+
const raw = frontmatter.maxThinkingTokens ?? frontmatter.max_thinking_tokens ?? frontmatter["max-thinking-tokens"] ?? frontmatter["max_thinking_tokens"];
|
|
138
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
139
|
+
const value = typeof raw === "number" ? raw : Number(String(raw).trim());
|
|
140
|
+
if (!Number.isFinite(value) || value < 0) return void 0;
|
|
141
|
+
return Math.floor(value);
|
|
142
|
+
}
|
|
143
|
+
function sourceLabel(source) {
|
|
144
|
+
if (source === "localSettings") return "project";
|
|
145
|
+
if (source === "userSettings") return "user";
|
|
146
|
+
if (source === "pluginDir") return "plugin";
|
|
147
|
+
return "unknown";
|
|
148
|
+
}
|
|
149
|
+
function extractDescriptionFromMarkdown(markdown, fallback) {
|
|
150
|
+
const lines = markdown.split(/\r?\n/);
|
|
151
|
+
for (const line of lines) {
|
|
152
|
+
const trimmed = line.trim();
|
|
153
|
+
if (!trimmed) continue;
|
|
154
|
+
const heading = trimmed.match(/^#{1,6}\s+(.*)$/);
|
|
155
|
+
if (heading?.[1]) return heading[1].trim();
|
|
156
|
+
return trimmed.length > 120 ? `${trimmed.slice(0, 117)}...` : trimmed;
|
|
157
|
+
}
|
|
158
|
+
return fallback;
|
|
159
|
+
}
|
|
160
|
+
function namespaceFromDirPath(dirPath, baseDir) {
|
|
161
|
+
const relPath = relative(baseDir, dirPath);
|
|
162
|
+
if (!relPath || relPath === "." || relPath.startsWith("..")) return "";
|
|
163
|
+
return relPath.split(sep).join(":");
|
|
164
|
+
}
|
|
165
|
+
function nameForCommandFile(filePath, baseDir) {
|
|
166
|
+
if (isSkillMarkdownFile(filePath)) {
|
|
167
|
+
const skillDir = dirname(filePath);
|
|
168
|
+
const parentDir = dirname(skillDir);
|
|
169
|
+
const skillName = basename(skillDir);
|
|
170
|
+
const namespace2 = namespaceFromDirPath(parentDir, baseDir);
|
|
171
|
+
return namespace2 ? `${namespace2}:${skillName}` : skillName;
|
|
172
|
+
}
|
|
173
|
+
const dir = dirname(filePath);
|
|
174
|
+
const namespace = namespaceFromDirPath(dir, baseDir);
|
|
175
|
+
const fileName = basename(filePath).replace(/\.md$/i, "");
|
|
176
|
+
return namespace ? `${namespace}:${fileName}` : fileName;
|
|
177
|
+
}
|
|
178
|
+
function buildPluginQualifiedName(pluginName, localName) {
|
|
179
|
+
const p = pluginName.trim();
|
|
180
|
+
const l = localName.trim();
|
|
181
|
+
if (!p) return l;
|
|
182
|
+
if (!l || l === p) return p;
|
|
183
|
+
return `${p}:${l}`;
|
|
184
|
+
}
|
|
185
|
+
function nameForPluginCommandFile(filePath, commandsDir, pluginName) {
|
|
186
|
+
const rel = relative(commandsDir, filePath);
|
|
187
|
+
const noExt = rel.replace(/\.md$/i, "");
|
|
188
|
+
const localName = noExt.split(sep).filter(Boolean).join(":");
|
|
189
|
+
return buildPluginQualifiedName(pluginName, localName);
|
|
190
|
+
}
|
|
191
|
+
function createPluginPromptCommandFromFile(record) {
|
|
192
|
+
const name = nameForPluginCommandFile(
|
|
193
|
+
record.filePath,
|
|
194
|
+
record.commandsDir,
|
|
195
|
+
record.pluginName
|
|
196
|
+
);
|
|
197
|
+
if (!name) return null;
|
|
198
|
+
const descriptionText = record.frontmatter.description ?? extractDescriptionFromMarkdown(record.content, "Custom command");
|
|
199
|
+
const allowedTools = parseAllowedTools(record.frontmatter["allowed-tools"]);
|
|
200
|
+
const maxThinkingTokens = parseMaxThinkingTokens(record.frontmatter);
|
|
201
|
+
const argumentHint = record.frontmatter["argument-hint"];
|
|
202
|
+
const whenToUse = record.frontmatter.when_to_use;
|
|
203
|
+
const version = record.frontmatter.version;
|
|
204
|
+
const disableModelInvocation = toBoolean(
|
|
205
|
+
record.frontmatter["disable-model-invocation"]
|
|
206
|
+
);
|
|
207
|
+
const model = record.frontmatter.model === "inherit" ? void 0 : record.frontmatter.model;
|
|
208
|
+
return {
|
|
209
|
+
type: "prompt",
|
|
210
|
+
name,
|
|
211
|
+
description: `${descriptionText} (${sourceLabel("pluginDir")})`,
|
|
212
|
+
isEnabled: true,
|
|
213
|
+
isHidden: false,
|
|
214
|
+
filePath: record.filePath,
|
|
215
|
+
aliases: [],
|
|
216
|
+
progressMessage: "running",
|
|
217
|
+
allowedTools,
|
|
218
|
+
maxThinkingTokens,
|
|
219
|
+
argumentHint,
|
|
220
|
+
whenToUse,
|
|
221
|
+
version,
|
|
222
|
+
model,
|
|
223
|
+
isSkill: false,
|
|
224
|
+
disableModelInvocation,
|
|
225
|
+
hasUserSpecifiedDescription: !!record.frontmatter.description,
|
|
226
|
+
source: "pluginDir",
|
|
227
|
+
scope: "project",
|
|
228
|
+
userFacingName() {
|
|
229
|
+
return name;
|
|
230
|
+
},
|
|
231
|
+
async getPromptForCommand(args) {
|
|
232
|
+
let prompt = record.content;
|
|
233
|
+
const trimmedArgs = args.trim();
|
|
234
|
+
if (trimmedArgs) {
|
|
235
|
+
if (prompt.includes("$ARGUMENTS")) {
|
|
236
|
+
prompt = prompt.replaceAll("$ARGUMENTS", trimmedArgs);
|
|
237
|
+
} else {
|
|
238
|
+
prompt = `${prompt}
|
|
239
|
+
|
|
240
|
+
ARGUMENTS: ${trimmedArgs}`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return [{ role: "user", content: prompt }];
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function loadPluginCommandsFromDir(args) {
|
|
248
|
+
let commandsBaseDir = args.commandsDir;
|
|
249
|
+
let files = [];
|
|
250
|
+
try {
|
|
251
|
+
const st = statSync(args.commandsDir);
|
|
252
|
+
if (st.isFile()) {
|
|
253
|
+
if (!args.commandsDir.toLowerCase().endsWith(".md")) return [];
|
|
254
|
+
files = [args.commandsDir];
|
|
255
|
+
commandsBaseDir = dirname(args.commandsDir);
|
|
256
|
+
} else if (st.isDirectory()) {
|
|
257
|
+
files = listMarkdownFilesRecursively(args.commandsDir, args.signal);
|
|
258
|
+
} else {
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
} catch {
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
const out = [];
|
|
265
|
+
for (const filePath of files) {
|
|
266
|
+
if (args.signal.aborted) break;
|
|
267
|
+
try {
|
|
268
|
+
const raw = readFileSync(filePath, "utf8");
|
|
269
|
+
const { frontmatter, content } = parseFrontmatter(raw);
|
|
270
|
+
const cmd = createPluginPromptCommandFromFile({
|
|
271
|
+
pluginName: args.pluginName,
|
|
272
|
+
commandsDir: commandsBaseDir,
|
|
273
|
+
filePath,
|
|
274
|
+
frontmatter,
|
|
275
|
+
content
|
|
276
|
+
});
|
|
277
|
+
if (cmd) out.push(cmd);
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
function loadPluginSkillDirectoryCommandsFromBaseDir(args) {
|
|
284
|
+
if (!existsSync(args.skillsDir)) return [];
|
|
285
|
+
const out = [];
|
|
286
|
+
let entries;
|
|
287
|
+
try {
|
|
288
|
+
entries = readdirSync(args.skillsDir, { withFileTypes: true });
|
|
289
|
+
} catch {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
const strictMode = toBoolean(process.env.KODE_SKILLS_STRICT);
|
|
293
|
+
const validateName = (skillName) => {
|
|
294
|
+
if (skillName.length < 1 || skillName.length > 64) return false;
|
|
295
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(skillName);
|
|
296
|
+
};
|
|
297
|
+
for (const entry of entries) {
|
|
298
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
299
|
+
const skillDir = join(args.skillsDir, entry.name);
|
|
300
|
+
const skillFileCandidates = [
|
|
301
|
+
join(skillDir, "SKILL.md"),
|
|
302
|
+
join(skillDir, "skill.md")
|
|
303
|
+
];
|
|
304
|
+
const skillFile = skillFileCandidates.find((p) => existsSync(p));
|
|
305
|
+
if (!skillFile) continue;
|
|
306
|
+
try {
|
|
307
|
+
const raw = readFileSync(skillFile, "utf8");
|
|
308
|
+
const { frontmatter, content } = parseFrontmatter(raw);
|
|
309
|
+
const dirName = entry.name;
|
|
310
|
+
const declaredName = typeof frontmatter.name === "string" ? String(frontmatter.name).trim() : "";
|
|
311
|
+
const effectiveDeclaredName = declaredName && declaredName === dirName ? declaredName : "";
|
|
312
|
+
if (declaredName && declaredName !== dirName) {
|
|
313
|
+
if (strictMode) continue;
|
|
314
|
+
debug.warn("CUSTOM_COMMAND_SKILL_NAME_MISMATCH", {
|
|
315
|
+
dirName,
|
|
316
|
+
declaredName,
|
|
317
|
+
skillFile
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
const name = buildPluginQualifiedName(args.pluginName, dirName);
|
|
321
|
+
if (!validateName(dirName)) {
|
|
322
|
+
if (strictMode) continue;
|
|
323
|
+
debug.warn("CUSTOM_COMMAND_SKILL_DIR_INVALID", { dirName, skillFile });
|
|
324
|
+
}
|
|
325
|
+
const descriptionText = frontmatter.description ?? extractDescriptionFromMarkdown(content, "Skill");
|
|
326
|
+
if (strictMode) {
|
|
327
|
+
const d = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
328
|
+
if (!d || d.length > 1024) continue;
|
|
329
|
+
}
|
|
330
|
+
const allowedTools = parseAllowedTools(frontmatter["allowed-tools"]);
|
|
331
|
+
const maxThinkingTokens = parseMaxThinkingTokens(frontmatter);
|
|
332
|
+
const argumentHint = frontmatter["argument-hint"];
|
|
333
|
+
const whenToUse = frontmatter.when_to_use;
|
|
334
|
+
const version = frontmatter.version;
|
|
335
|
+
const disableModelInvocation = toBoolean(
|
|
336
|
+
frontmatter["disable-model-invocation"]
|
|
337
|
+
);
|
|
338
|
+
const model = frontmatter.model === "inherit" ? void 0 : frontmatter.model;
|
|
339
|
+
out.push({
|
|
340
|
+
type: "prompt",
|
|
341
|
+
name,
|
|
342
|
+
description: `${descriptionText} (${sourceLabel("pluginDir")})`,
|
|
343
|
+
isEnabled: true,
|
|
344
|
+
isHidden: true,
|
|
345
|
+
aliases: [],
|
|
346
|
+
filePath: skillFile,
|
|
347
|
+
progressMessage: "loading",
|
|
348
|
+
allowedTools,
|
|
349
|
+
maxThinkingTokens,
|
|
350
|
+
argumentHint,
|
|
351
|
+
whenToUse,
|
|
352
|
+
version,
|
|
353
|
+
model,
|
|
354
|
+
isSkill: true,
|
|
355
|
+
disableModelInvocation,
|
|
356
|
+
hasUserSpecifiedDescription: !!frontmatter.description,
|
|
357
|
+
source: "pluginDir",
|
|
358
|
+
scope: "project",
|
|
359
|
+
userFacingName() {
|
|
360
|
+
return effectiveDeclaredName ? buildPluginQualifiedName(args.pluginName, effectiveDeclaredName) : name;
|
|
361
|
+
},
|
|
362
|
+
async getPromptForCommand(argsText) {
|
|
363
|
+
let prompt = `Base directory for this skill: ${skillDir}
|
|
364
|
+
|
|
365
|
+
${content}`;
|
|
366
|
+
const trimmedArgs = argsText.trim();
|
|
367
|
+
if (trimmedArgs) {
|
|
368
|
+
if (prompt.includes("$ARGUMENTS")) {
|
|
369
|
+
prompt = prompt.replaceAll("$ARGUMENTS", trimmedArgs);
|
|
370
|
+
} else {
|
|
371
|
+
prompt = `${prompt}
|
|
372
|
+
|
|
373
|
+
ARGUMENTS: ${trimmedArgs}`;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return [{ role: "user", content: prompt }];
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
} catch {
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return out;
|
|
383
|
+
}
|
|
384
|
+
function applySkillFilePreference(files) {
|
|
385
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
386
|
+
for (const file of files) {
|
|
387
|
+
const key = dirname(file.filePath);
|
|
388
|
+
const existing = grouped.get(key) ?? [];
|
|
389
|
+
existing.push(file);
|
|
390
|
+
grouped.set(key, existing);
|
|
391
|
+
}
|
|
392
|
+
const result = [];
|
|
393
|
+
for (const group of grouped.values()) {
|
|
394
|
+
const skillFiles = group.filter((f) => isSkillMarkdownFile(f.filePath));
|
|
395
|
+
if (skillFiles.length > 0) {
|
|
396
|
+
result.push(skillFiles[0]);
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
result.push(...group);
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
function createPromptCommandFromFile(record) {
|
|
404
|
+
const isSkill = isSkillMarkdownFile(record.filePath);
|
|
405
|
+
const name = nameForCommandFile(record.filePath, record.baseDir);
|
|
406
|
+
if (!name) return null;
|
|
407
|
+
const descriptionText = record.frontmatter.description ?? extractDescriptionFromMarkdown(
|
|
408
|
+
record.content,
|
|
409
|
+
isSkill ? "Skill" : "Custom command"
|
|
410
|
+
);
|
|
411
|
+
const allowedTools = parseAllowedTools(record.frontmatter["allowed-tools"]);
|
|
412
|
+
const maxThinkingTokens = parseMaxThinkingTokens(record.frontmatter);
|
|
413
|
+
const argumentHint = record.frontmatter["argument-hint"];
|
|
414
|
+
const whenToUse = record.frontmatter.when_to_use;
|
|
415
|
+
const version = record.frontmatter.version;
|
|
416
|
+
const disableModelInvocation = toBoolean(
|
|
417
|
+
record.frontmatter["disable-model-invocation"]
|
|
418
|
+
);
|
|
419
|
+
const model = record.frontmatter.model === "inherit" ? void 0 : record.frontmatter.model;
|
|
420
|
+
const description = `${descriptionText} (${sourceLabel(record.source)})`;
|
|
421
|
+
const progressMessage = isSkill ? "loading" : "running";
|
|
422
|
+
const skillBaseDir = isSkill ? dirname(record.filePath) : void 0;
|
|
423
|
+
return {
|
|
424
|
+
type: "prompt",
|
|
425
|
+
name,
|
|
426
|
+
description,
|
|
427
|
+
isEnabled: true,
|
|
428
|
+
isHidden: false,
|
|
429
|
+
filePath: record.filePath,
|
|
430
|
+
aliases: [],
|
|
431
|
+
progressMessage,
|
|
432
|
+
allowedTools,
|
|
433
|
+
maxThinkingTokens,
|
|
434
|
+
argumentHint,
|
|
435
|
+
whenToUse,
|
|
436
|
+
version,
|
|
437
|
+
model,
|
|
438
|
+
isSkill,
|
|
439
|
+
disableModelInvocation,
|
|
440
|
+
hasUserSpecifiedDescription: !!record.frontmatter.description,
|
|
441
|
+
source: record.source,
|
|
442
|
+
scope: record.scope,
|
|
443
|
+
userFacingName() {
|
|
444
|
+
return name;
|
|
445
|
+
},
|
|
446
|
+
async getPromptForCommand(args) {
|
|
447
|
+
let prompt = record.content;
|
|
448
|
+
if (isSkill && skillBaseDir) {
|
|
449
|
+
prompt = `Base directory for this skill: ${skillBaseDir}
|
|
450
|
+
|
|
451
|
+
${prompt}`;
|
|
452
|
+
}
|
|
453
|
+
const trimmedArgs = args.trim();
|
|
454
|
+
if (trimmedArgs) {
|
|
455
|
+
if (prompt.includes("$ARGUMENTS")) {
|
|
456
|
+
prompt = prompt.replaceAll("$ARGUMENTS", trimmedArgs);
|
|
457
|
+
} else {
|
|
458
|
+
prompt = `${prompt}
|
|
459
|
+
|
|
460
|
+
ARGUMENTS: ${trimmedArgs}`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return [{ role: "user", content: prompt }];
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function listMarkdownFilesRecursively(baseDir, signal) {
|
|
468
|
+
const results = [];
|
|
469
|
+
const queue = [baseDir];
|
|
470
|
+
while (queue.length > 0) {
|
|
471
|
+
if (signal.aborted) break;
|
|
472
|
+
const currentDir = queue.pop();
|
|
473
|
+
let entries;
|
|
474
|
+
try {
|
|
475
|
+
entries = readdirSync(currentDir, { withFileTypes: true });
|
|
476
|
+
} catch {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
for (const entry of entries) {
|
|
480
|
+
if (signal.aborted) break;
|
|
481
|
+
const fullPath = join(currentDir, entry.name);
|
|
482
|
+
if (entry.isDirectory()) {
|
|
483
|
+
queue.push(fullPath);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
487
|
+
results.push(fullPath);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return results;
|
|
492
|
+
}
|
|
493
|
+
function loadCommandMarkdownFilesFromBaseDir(baseDir, source, scope, signal) {
|
|
494
|
+
if (!existsSync(baseDir)) return [];
|
|
495
|
+
const files = listMarkdownFilesRecursively(baseDir, signal);
|
|
496
|
+
const records = [];
|
|
497
|
+
for (const filePath of files) {
|
|
498
|
+
if (signal.aborted) break;
|
|
499
|
+
try {
|
|
500
|
+
const raw = readFileSync(filePath, "utf8");
|
|
501
|
+
const { frontmatter, content } = parseFrontmatter(raw);
|
|
502
|
+
records.push({ baseDir, filePath, frontmatter, content, source, scope });
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return records;
|
|
507
|
+
}
|
|
508
|
+
function loadSkillDirectoryCommandsFromBaseDir(skillsDir, source, scope) {
|
|
509
|
+
if (!existsSync(skillsDir)) return [];
|
|
510
|
+
const out = [];
|
|
511
|
+
let entries;
|
|
512
|
+
try {
|
|
513
|
+
entries = readdirSync(skillsDir, { withFileTypes: true });
|
|
514
|
+
} catch {
|
|
515
|
+
return [];
|
|
516
|
+
}
|
|
517
|
+
const strictMode = toBoolean(process.env.KODE_SKILLS_STRICT);
|
|
518
|
+
const validateName = (skillName) => {
|
|
519
|
+
if (skillName.length < 1 || skillName.length > 64) return false;
|
|
520
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(skillName);
|
|
521
|
+
};
|
|
522
|
+
for (const entry of entries) {
|
|
523
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
524
|
+
const skillDir = join(skillsDir, entry.name);
|
|
525
|
+
const skillFileCandidates = [
|
|
526
|
+
join(skillDir, "SKILL.md"),
|
|
527
|
+
join(skillDir, "skill.md")
|
|
528
|
+
];
|
|
529
|
+
const skillFile = skillFileCandidates.find((p) => existsSync(p));
|
|
530
|
+
if (!skillFile) continue;
|
|
531
|
+
try {
|
|
532
|
+
const raw = readFileSync(skillFile, "utf8");
|
|
533
|
+
const { frontmatter, content } = parseFrontmatter(raw);
|
|
534
|
+
const dirName = entry.name;
|
|
535
|
+
const declaredName = typeof frontmatter.name === "string" ? String(frontmatter.name).trim() : "";
|
|
536
|
+
const effectiveDeclaredName = declaredName && declaredName === dirName ? declaredName : "";
|
|
537
|
+
if (declaredName && declaredName !== dirName) {
|
|
538
|
+
if (strictMode) continue;
|
|
539
|
+
debug.warn("CUSTOM_COMMAND_SKILL_NAME_MISMATCH", {
|
|
540
|
+
dirName,
|
|
541
|
+
declaredName,
|
|
542
|
+
skillFile
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const name = dirName;
|
|
546
|
+
if (!validateName(name)) {
|
|
547
|
+
if (strictMode) continue;
|
|
548
|
+
debug.warn("CUSTOM_COMMAND_SKILL_DIR_INVALID", { name, skillFile });
|
|
549
|
+
}
|
|
550
|
+
const descriptionText = frontmatter.description ?? extractDescriptionFromMarkdown(content, "Skill");
|
|
551
|
+
if (strictMode) {
|
|
552
|
+
const d = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
|
|
553
|
+
if (!d || d.length > 1024) continue;
|
|
554
|
+
}
|
|
555
|
+
const allowedTools = parseAllowedTools(frontmatter["allowed-tools"]);
|
|
556
|
+
const maxThinkingTokens = parseMaxThinkingTokens(frontmatter);
|
|
557
|
+
const argumentHint = frontmatter["argument-hint"];
|
|
558
|
+
const whenToUse = frontmatter.when_to_use;
|
|
559
|
+
const version = frontmatter.version;
|
|
560
|
+
const disableModelInvocation = toBoolean(
|
|
561
|
+
frontmatter["disable-model-invocation"]
|
|
562
|
+
);
|
|
563
|
+
const model = frontmatter.model === "inherit" ? void 0 : frontmatter.model;
|
|
564
|
+
out.push({
|
|
565
|
+
type: "prompt",
|
|
566
|
+
name,
|
|
567
|
+
description: `${descriptionText} (${sourceLabel(source)})`,
|
|
568
|
+
isEnabled: true,
|
|
569
|
+
isHidden: true,
|
|
570
|
+
aliases: [],
|
|
571
|
+
filePath: skillFile,
|
|
572
|
+
progressMessage: "loading",
|
|
573
|
+
allowedTools,
|
|
574
|
+
maxThinkingTokens,
|
|
575
|
+
argumentHint,
|
|
576
|
+
whenToUse,
|
|
577
|
+
version,
|
|
578
|
+
model,
|
|
579
|
+
isSkill: true,
|
|
580
|
+
disableModelInvocation,
|
|
581
|
+
hasUserSpecifiedDescription: !!frontmatter.description,
|
|
582
|
+
source,
|
|
583
|
+
scope,
|
|
584
|
+
userFacingName() {
|
|
585
|
+
return effectiveDeclaredName || name;
|
|
586
|
+
},
|
|
587
|
+
async getPromptForCommand(args) {
|
|
588
|
+
let prompt = `Base directory for this skill: ${skillDir}
|
|
589
|
+
|
|
590
|
+
${content}`;
|
|
591
|
+
const trimmedArgs = args.trim();
|
|
592
|
+
if (trimmedArgs) {
|
|
593
|
+
if (prompt.includes("$ARGUMENTS")) {
|
|
594
|
+
prompt = prompt.replaceAll("$ARGUMENTS", trimmedArgs);
|
|
595
|
+
} else {
|
|
596
|
+
prompt = `${prompt}
|
|
597
|
+
|
|
598
|
+
ARGUMENTS: ${trimmedArgs}`;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return [{ role: "user", content: prompt }];
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
} catch {
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return out;
|
|
608
|
+
}
|
|
609
|
+
var loadCustomCommands = memoize(
|
|
610
|
+
async () => {
|
|
611
|
+
const cwd = getCwd();
|
|
612
|
+
const userKodeBaseDir = getUserKodeBaseDir();
|
|
613
|
+
const sessionPlugins = getSessionPlugins();
|
|
614
|
+
const projectLegacyCommandsDir = join(cwd, ".claude", "commands");
|
|
615
|
+
const userLegacyCommandsDir = join(homedir(), ".claude", "commands");
|
|
616
|
+
const projectKodeCommandsDir = join(cwd, ".kode", "commands");
|
|
617
|
+
const userKodeCommandsDir = join(userKodeBaseDir, "commands");
|
|
618
|
+
const projectLegacySkillsDir = join(cwd, ".claude", "skills");
|
|
619
|
+
const userLegacySkillsDir = join(homedir(), ".claude", "skills");
|
|
620
|
+
const projectKodeSkillsDir = join(cwd, ".kode", "skills");
|
|
621
|
+
const userKodeSkillsDir = join(userKodeBaseDir, "skills");
|
|
622
|
+
const abortController = new AbortController();
|
|
623
|
+
const timeout = setTimeout(() => abortController.abort(), 3e3);
|
|
624
|
+
try {
|
|
625
|
+
const commandFiles = applySkillFilePreference([
|
|
626
|
+
...loadCommandMarkdownFilesFromBaseDir(
|
|
627
|
+
projectLegacyCommandsDir,
|
|
628
|
+
"localSettings",
|
|
629
|
+
"project",
|
|
630
|
+
abortController.signal
|
|
631
|
+
),
|
|
632
|
+
...loadCommandMarkdownFilesFromBaseDir(
|
|
633
|
+
projectKodeCommandsDir,
|
|
634
|
+
"localSettings",
|
|
635
|
+
"project",
|
|
636
|
+
abortController.signal
|
|
637
|
+
),
|
|
638
|
+
...loadCommandMarkdownFilesFromBaseDir(
|
|
639
|
+
userLegacyCommandsDir,
|
|
640
|
+
"userSettings",
|
|
641
|
+
"user",
|
|
642
|
+
abortController.signal
|
|
643
|
+
),
|
|
644
|
+
...loadCommandMarkdownFilesFromBaseDir(
|
|
645
|
+
userKodeCommandsDir,
|
|
646
|
+
"userSettings",
|
|
647
|
+
"user",
|
|
648
|
+
abortController.signal
|
|
649
|
+
)
|
|
650
|
+
]);
|
|
651
|
+
const fileCommands = commandFiles.map(createPromptCommandFromFile).filter((cmd) => cmd !== null);
|
|
652
|
+
const skillDirCommands = [
|
|
653
|
+
...loadSkillDirectoryCommandsFromBaseDir(
|
|
654
|
+
projectLegacySkillsDir,
|
|
655
|
+
"localSettings",
|
|
656
|
+
"project"
|
|
657
|
+
),
|
|
658
|
+
...loadSkillDirectoryCommandsFromBaseDir(
|
|
659
|
+
projectKodeSkillsDir,
|
|
660
|
+
"localSettings",
|
|
661
|
+
"project"
|
|
662
|
+
),
|
|
663
|
+
...loadSkillDirectoryCommandsFromBaseDir(
|
|
664
|
+
userLegacySkillsDir,
|
|
665
|
+
"userSettings",
|
|
666
|
+
"user"
|
|
667
|
+
),
|
|
668
|
+
...loadSkillDirectoryCommandsFromBaseDir(
|
|
669
|
+
userKodeSkillsDir,
|
|
670
|
+
"userSettings",
|
|
671
|
+
"user"
|
|
672
|
+
)
|
|
673
|
+
];
|
|
674
|
+
const pluginCommands = [];
|
|
675
|
+
if (sessionPlugins.length > 0) {
|
|
676
|
+
for (const plugin of sessionPlugins) {
|
|
677
|
+
for (const commandsDir of plugin.commandsDirs) {
|
|
678
|
+
pluginCommands.push(
|
|
679
|
+
...loadPluginCommandsFromDir({
|
|
680
|
+
pluginName: plugin.name,
|
|
681
|
+
commandsDir,
|
|
682
|
+
signal: abortController.signal
|
|
683
|
+
})
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
for (const skillsDir of plugin.skillsDirs) {
|
|
687
|
+
pluginCommands.push(
|
|
688
|
+
...loadPluginSkillDirectoryCommandsFromBaseDir({
|
|
689
|
+
pluginName: plugin.name,
|
|
690
|
+
skillsDir
|
|
691
|
+
})
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const ordered = [
|
|
697
|
+
...fileCommands,
|
|
698
|
+
...skillDirCommands,
|
|
699
|
+
...pluginCommands
|
|
700
|
+
].filter((cmd) => cmd.isEnabled);
|
|
701
|
+
const seen = /* @__PURE__ */ new Set();
|
|
702
|
+
const unique = [];
|
|
703
|
+
for (const cmd of ordered) {
|
|
704
|
+
const key = cmd.userFacingName();
|
|
705
|
+
if (seen.has(key)) continue;
|
|
706
|
+
seen.add(key);
|
|
707
|
+
unique.push(cmd);
|
|
708
|
+
}
|
|
709
|
+
return unique;
|
|
710
|
+
} catch (error) {
|
|
711
|
+
logError(error);
|
|
712
|
+
debug.warn("CUSTOM_COMMANDS_LOAD_FAILED", {
|
|
713
|
+
error: error instanceof Error ? error.message : String(error)
|
|
714
|
+
});
|
|
715
|
+
return [];
|
|
716
|
+
} finally {
|
|
717
|
+
clearTimeout(timeout);
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
() => {
|
|
721
|
+
const cwd = getCwd();
|
|
722
|
+
const userKodeBaseDir = getUserKodeBaseDir();
|
|
723
|
+
const dirs = [
|
|
724
|
+
join(homedir(), ".claude", "commands"),
|
|
725
|
+
join(cwd, ".claude", "commands"),
|
|
726
|
+
join(userKodeBaseDir, "commands"),
|
|
727
|
+
join(cwd, ".kode", "commands"),
|
|
728
|
+
join(homedir(), ".claude", "skills"),
|
|
729
|
+
join(cwd, ".claude", "skills"),
|
|
730
|
+
join(userKodeBaseDir, "skills"),
|
|
731
|
+
join(cwd, ".kode", "skills")
|
|
732
|
+
];
|
|
733
|
+
const exists = dirs.map((d) => existsSync(d) ? "1" : "0").join("");
|
|
734
|
+
return `${cwd}:${exists}:${Math.floor(Date.now() / 6e4)}`;
|
|
735
|
+
}
|
|
736
|
+
);
|
|
737
|
+
var reloadCustomCommands = () => {
|
|
738
|
+
loadCustomCommands.cache.clear();
|
|
739
|
+
};
|
|
740
|
+
function getCustomCommandDirectories() {
|
|
741
|
+
const userKodeBaseDir = getUserKodeBaseDir();
|
|
742
|
+
return {
|
|
743
|
+
userClaudeCommands: join(homedir(), ".claude", "commands"),
|
|
744
|
+
projectClaudeCommands: join(getCwd(), ".claude", "commands"),
|
|
745
|
+
userClaudeSkills: join(homedir(), ".claude", "skills"),
|
|
746
|
+
projectClaudeSkills: join(getCwd(), ".claude", "skills"),
|
|
747
|
+
userKodeCommands: join(userKodeBaseDir, "commands"),
|
|
748
|
+
projectKodeCommands: join(getCwd(), ".kode", "commands"),
|
|
749
|
+
userKodeSkills: join(userKodeBaseDir, "skills"),
|
|
750
|
+
projectKodeSkills: join(getCwd(), ".kode", "skills")
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function hasCustomCommands() {
|
|
754
|
+
const dirs = getCustomCommandDirectories();
|
|
755
|
+
return existsSync(dirs.userClaudeCommands) || existsSync(dirs.projectClaudeCommands) || existsSync(dirs.userClaudeSkills) || existsSync(dirs.projectClaudeSkills) || existsSync(dirs.userKodeCommands) || existsSync(dirs.projectKodeCommands) || existsSync(dirs.userKodeSkills) || existsSync(dirs.projectKodeSkills);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export {
|
|
759
|
+
executeBashCommands,
|
|
760
|
+
resolveFileReferences,
|
|
761
|
+
parseFrontmatter,
|
|
762
|
+
loadCustomCommands,
|
|
763
|
+
reloadCustomCommands,
|
|
764
|
+
getCustomCommandDirectories,
|
|
765
|
+
hasCustomCommands
|
|
766
|
+
};
|