@omnidev-ai/cli 0.13.4 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1676 -397
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -64,18 +64,25 @@ function parseFrontmatterWithMarkdown(content) {
|
|
|
64
64
|
// ../core/src/capability/commands.ts
|
|
65
65
|
import { existsSync, readdirSync } from "node:fs";
|
|
66
66
|
import { readFile } from "node:fs/promises";
|
|
67
|
-
import { join
|
|
67
|
+
import { basename, join } from "node:path";
|
|
68
68
|
async function loadCommands(capabilityPath, capabilityId) {
|
|
69
|
-
const commandsDir = join2(capabilityPath, "commands");
|
|
70
|
-
if (!existsSync(commandsDir)) {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
69
|
const commands = [];
|
|
74
|
-
const
|
|
75
|
-
for (const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
const possibleDirNames = ["commands", "command"];
|
|
71
|
+
for (const dirName of possibleDirNames) {
|
|
72
|
+
const dir = join(capabilityPath, dirName);
|
|
73
|
+
if (!existsSync(dir)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const entries = readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (entry.isDirectory()) {
|
|
79
|
+
const commandPath = join(dir, entry.name, "COMMAND.md");
|
|
80
|
+
if (existsSync(commandPath)) {
|
|
81
|
+
const command = await parseCommandFile(commandPath, capabilityId);
|
|
82
|
+
commands.push(command);
|
|
83
|
+
}
|
|
84
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
85
|
+
const commandPath = join(dir, entry.name);
|
|
79
86
|
const command = await parseCommandFile(commandPath, capabilityId);
|
|
80
87
|
commands.push(command);
|
|
81
88
|
}
|
|
@@ -91,11 +98,13 @@ async function parseCommandFile(filePath, capabilityId) {
|
|
|
91
98
|
}
|
|
92
99
|
const frontmatter = parsed.frontmatter;
|
|
93
100
|
const prompt = parsed.markdown;
|
|
94
|
-
|
|
101
|
+
const inferredName = basename(filePath, ".md").replace(/^COMMAND$/i, "");
|
|
102
|
+
const name = frontmatter.name || inferredName;
|
|
103
|
+
if (!name || !frontmatter.description) {
|
|
95
104
|
throw new Error(`Invalid COMMAND.md at ${filePath}: name and description required`);
|
|
96
105
|
}
|
|
97
106
|
const result = {
|
|
98
|
-
name
|
|
107
|
+
name,
|
|
99
108
|
description: frontmatter.description,
|
|
100
109
|
prompt: prompt.trim(),
|
|
101
110
|
capabilityId
|
|
@@ -110,10 +119,10 @@ var init_commands = () => {};
|
|
|
110
119
|
// ../core/src/capability/docs.ts
|
|
111
120
|
import { existsSync as existsSync2, readdirSync as readdirSync2 } from "node:fs";
|
|
112
121
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
113
|
-
import { basename, join as
|
|
122
|
+
import { basename as basename2, join as join2 } from "node:path";
|
|
114
123
|
async function loadDocs(capabilityPath, capabilityId) {
|
|
115
124
|
const docs = [];
|
|
116
|
-
const definitionPath =
|
|
125
|
+
const definitionPath = join2(capabilityPath, "definition.md");
|
|
117
126
|
if (existsSync2(definitionPath)) {
|
|
118
127
|
const content = await readFile2(definitionPath, "utf-8");
|
|
119
128
|
docs.push({
|
|
@@ -122,15 +131,15 @@ async function loadDocs(capabilityPath, capabilityId) {
|
|
|
122
131
|
capabilityId
|
|
123
132
|
});
|
|
124
133
|
}
|
|
125
|
-
const docsDir =
|
|
134
|
+
const docsDir = join2(capabilityPath, "docs");
|
|
126
135
|
if (existsSync2(docsDir)) {
|
|
127
136
|
const entries = readdirSync2(docsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
128
137
|
for (const entry of entries) {
|
|
129
138
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
130
|
-
const docPath =
|
|
139
|
+
const docPath = join2(docsDir, entry.name);
|
|
131
140
|
const content = await readFile2(docPath, "utf-8");
|
|
132
141
|
docs.push({
|
|
133
|
-
name:
|
|
142
|
+
name: basename2(entry.name, ".md"),
|
|
134
143
|
content: content.trim(),
|
|
135
144
|
capabilityId
|
|
136
145
|
});
|
|
@@ -1057,6 +1066,151 @@ var init_parse = __esm(() => {
|
|
|
1057
1066
|
});
|
|
1058
1067
|
|
|
1059
1068
|
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/stringify.js
|
|
1069
|
+
function extendedTypeOf(obj) {
|
|
1070
|
+
let type = typeof obj;
|
|
1071
|
+
if (type === "object") {
|
|
1072
|
+
if (Array.isArray(obj))
|
|
1073
|
+
return "array";
|
|
1074
|
+
if (obj instanceof Date)
|
|
1075
|
+
return "date";
|
|
1076
|
+
}
|
|
1077
|
+
return type;
|
|
1078
|
+
}
|
|
1079
|
+
function isArrayOfTables(obj) {
|
|
1080
|
+
for (let i = 0;i < obj.length; i++) {
|
|
1081
|
+
if (extendedTypeOf(obj[i]) !== "object")
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
return obj.length != 0;
|
|
1085
|
+
}
|
|
1086
|
+
function formatString(s) {
|
|
1087
|
+
return JSON.stringify(s).replace(/\x7f/g, "\\u007f");
|
|
1088
|
+
}
|
|
1089
|
+
function stringifyValue(val, type, depth, numberAsFloat) {
|
|
1090
|
+
if (depth === 0) {
|
|
1091
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
1092
|
+
}
|
|
1093
|
+
if (type === "number") {
|
|
1094
|
+
if (isNaN(val))
|
|
1095
|
+
return "nan";
|
|
1096
|
+
if (val === Infinity)
|
|
1097
|
+
return "inf";
|
|
1098
|
+
if (val === -Infinity)
|
|
1099
|
+
return "-inf";
|
|
1100
|
+
if (numberAsFloat && Number.isInteger(val))
|
|
1101
|
+
return val.toFixed(1);
|
|
1102
|
+
return val.toString();
|
|
1103
|
+
}
|
|
1104
|
+
if (type === "bigint" || type === "boolean") {
|
|
1105
|
+
return val.toString();
|
|
1106
|
+
}
|
|
1107
|
+
if (type === "string") {
|
|
1108
|
+
return formatString(val);
|
|
1109
|
+
}
|
|
1110
|
+
if (type === "date") {
|
|
1111
|
+
if (isNaN(val.getTime())) {
|
|
1112
|
+
throw new TypeError("cannot serialize invalid date");
|
|
1113
|
+
}
|
|
1114
|
+
return val.toISOString();
|
|
1115
|
+
}
|
|
1116
|
+
if (type === "object") {
|
|
1117
|
+
return stringifyInlineTable(val, depth, numberAsFloat);
|
|
1118
|
+
}
|
|
1119
|
+
if (type === "array") {
|
|
1120
|
+
return stringifyArray(val, depth, numberAsFloat);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
function stringifyInlineTable(obj, depth, numberAsFloat) {
|
|
1124
|
+
let keys = Object.keys(obj);
|
|
1125
|
+
if (keys.length === 0)
|
|
1126
|
+
return "{}";
|
|
1127
|
+
let res = "{ ";
|
|
1128
|
+
for (let i = 0;i < keys.length; i++) {
|
|
1129
|
+
let k = keys[i];
|
|
1130
|
+
if (i)
|
|
1131
|
+
res += ", ";
|
|
1132
|
+
res += BARE_KEY.test(k) ? k : formatString(k);
|
|
1133
|
+
res += " = ";
|
|
1134
|
+
res += stringifyValue(obj[k], extendedTypeOf(obj[k]), depth - 1, numberAsFloat);
|
|
1135
|
+
}
|
|
1136
|
+
return res + " }";
|
|
1137
|
+
}
|
|
1138
|
+
function stringifyArray(array, depth, numberAsFloat) {
|
|
1139
|
+
if (array.length === 0)
|
|
1140
|
+
return "[]";
|
|
1141
|
+
let res = "[ ";
|
|
1142
|
+
for (let i = 0;i < array.length; i++) {
|
|
1143
|
+
if (i)
|
|
1144
|
+
res += ", ";
|
|
1145
|
+
if (array[i] === null || array[i] === undefined) {
|
|
1146
|
+
throw new TypeError("arrays cannot contain null or undefined values");
|
|
1147
|
+
}
|
|
1148
|
+
res += stringifyValue(array[i], extendedTypeOf(array[i]), depth - 1, numberAsFloat);
|
|
1149
|
+
}
|
|
1150
|
+
return res + " ]";
|
|
1151
|
+
}
|
|
1152
|
+
function stringifyArrayTable(array, key, depth, numberAsFloat) {
|
|
1153
|
+
if (depth === 0) {
|
|
1154
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
1155
|
+
}
|
|
1156
|
+
let res = "";
|
|
1157
|
+
for (let i = 0;i < array.length; i++) {
|
|
1158
|
+
res += `${res && `
|
|
1159
|
+
`}[[${key}]]
|
|
1160
|
+
`;
|
|
1161
|
+
res += stringifyTable(0, array[i], key, depth, numberAsFloat);
|
|
1162
|
+
}
|
|
1163
|
+
return res;
|
|
1164
|
+
}
|
|
1165
|
+
function stringifyTable(tableKey, obj, prefix, depth, numberAsFloat) {
|
|
1166
|
+
if (depth === 0) {
|
|
1167
|
+
throw new Error("Could not stringify the object: maximum object depth exceeded");
|
|
1168
|
+
}
|
|
1169
|
+
let preamble = "";
|
|
1170
|
+
let tables = "";
|
|
1171
|
+
let keys = Object.keys(obj);
|
|
1172
|
+
for (let i = 0;i < keys.length; i++) {
|
|
1173
|
+
let k = keys[i];
|
|
1174
|
+
if (obj[k] !== null && obj[k] !== undefined) {
|
|
1175
|
+
let type = extendedTypeOf(obj[k]);
|
|
1176
|
+
if (type === "symbol" || type === "function") {
|
|
1177
|
+
throw new TypeError(`cannot serialize values of type '${type}'`);
|
|
1178
|
+
}
|
|
1179
|
+
let key = BARE_KEY.test(k) ? k : formatString(k);
|
|
1180
|
+
if (type === "array" && isArrayOfTables(obj[k])) {
|
|
1181
|
+
tables += (tables && `
|
|
1182
|
+
`) + stringifyArrayTable(obj[k], prefix ? `${prefix}.${key}` : key, depth - 1, numberAsFloat);
|
|
1183
|
+
} else if (type === "object") {
|
|
1184
|
+
let tblKey = prefix ? `${prefix}.${key}` : key;
|
|
1185
|
+
tables += (tables && `
|
|
1186
|
+
`) + stringifyTable(tblKey, obj[k], tblKey, depth - 1, numberAsFloat);
|
|
1187
|
+
} else {
|
|
1188
|
+
preamble += key;
|
|
1189
|
+
preamble += " = ";
|
|
1190
|
+
preamble += stringifyValue(obj[k], type, depth, numberAsFloat);
|
|
1191
|
+
preamble += `
|
|
1192
|
+
`;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
if (tableKey && (preamble || !tables))
|
|
1197
|
+
preamble = preamble ? `[${tableKey}]
|
|
1198
|
+
${preamble}` : `[${tableKey}]`;
|
|
1199
|
+
return preamble && tables ? `${preamble}
|
|
1200
|
+
${tables}` : preamble || tables;
|
|
1201
|
+
}
|
|
1202
|
+
function stringify(obj, { maxDepth = 1000, numbersAsFloat = false } = {}) {
|
|
1203
|
+
if (extendedTypeOf(obj) !== "object") {
|
|
1204
|
+
throw new TypeError("stringify can only be called with an object");
|
|
1205
|
+
}
|
|
1206
|
+
let str = stringifyTable(0, obj, "", maxDepth, numbersAsFloat);
|
|
1207
|
+
if (str[str.length - 1] !== `
|
|
1208
|
+
`)
|
|
1209
|
+
return str + `
|
|
1210
|
+
`;
|
|
1211
|
+
return str;
|
|
1212
|
+
}
|
|
1213
|
+
var BARE_KEY;
|
|
1060
1214
|
var init_stringify = __esm(() => {
|
|
1061
1215
|
/*!
|
|
1062
1216
|
* Copyright (c) Squirrel Chat et al., All rights reserved.
|
|
@@ -1085,9 +1239,19 @@ var init_stringify = __esm(() => {
|
|
|
1085
1239
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
1086
1240
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
1087
1241
|
*/
|
|
1242
|
+
BARE_KEY = /^[a-z0-9-_]+$/i;
|
|
1088
1243
|
});
|
|
1089
1244
|
|
|
1090
1245
|
// ../../node_modules/.bun/smol-toml@1.6.0/node_modules/smol-toml/dist/index.js
|
|
1246
|
+
var exports_dist = {};
|
|
1247
|
+
__export(exports_dist, {
|
|
1248
|
+
stringify: () => stringify,
|
|
1249
|
+
parse: () => parse,
|
|
1250
|
+
default: () => dist_default,
|
|
1251
|
+
TomlError: () => TomlError,
|
|
1252
|
+
TomlDate: () => TomlDate
|
|
1253
|
+
});
|
|
1254
|
+
var dist_default;
|
|
1091
1255
|
var init_dist = __esm(() => {
|
|
1092
1256
|
init_parse();
|
|
1093
1257
|
init_stringify();
|
|
@@ -1120,6 +1284,7 @@ var init_dist = __esm(() => {
|
|
|
1120
1284
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
1121
1285
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
1122
1286
|
*/
|
|
1287
|
+
dist_default = { parse, stringify, TomlDate, TomlError };
|
|
1123
1288
|
});
|
|
1124
1289
|
|
|
1125
1290
|
// ../core/src/config/parser.ts
|
|
@@ -1162,7 +1327,7 @@ var init_parser = __esm(() => {
|
|
|
1162
1327
|
});
|
|
1163
1328
|
|
|
1164
1329
|
// ../core/src/hooks/constants.ts
|
|
1165
|
-
var HOOK_EVENTS, MATCHER_EVENTS, PROMPT_HOOK_EVENTS, HOOK_TYPES, COMMON_TOOL_MATCHERS, NOTIFICATION_MATCHERS, SESSION_START_MATCHERS, PRE_COMPACT_MATCHERS, DEFAULT_COMMAND_TIMEOUT = 60, DEFAULT_PROMPT_TIMEOUT = 30, VARIABLE_MAPPINGS, HOOKS_CONFIG_FILENAME = "hooks.toml", HOOKS_DIRECTORY = "hooks";
|
|
1330
|
+
var HOOK_EVENTS, MATCHER_EVENTS, PROMPT_HOOK_EVENTS, HOOK_TYPES, COMMON_TOOL_MATCHERS, NOTIFICATION_MATCHERS, SESSION_START_MATCHERS, PRE_COMPACT_MATCHERS, DEFAULT_COMMAND_TIMEOUT = 60, DEFAULT_PROMPT_TIMEOUT = 30, VARIABLE_MAPPINGS, HOOKS_CONFIG_FILENAME = "hooks.toml", CLAUDE_HOOKS_CONFIG_FILENAME = "hooks.json", HOOKS_DIRECTORY = "hooks";
|
|
1166
1331
|
var init_constants = __esm(() => {
|
|
1167
1332
|
HOOK_EVENTS = [
|
|
1168
1333
|
"PreToolUse",
|
|
@@ -1698,34 +1863,369 @@ function containsOmnidevVariables(content) {
|
|
|
1698
1863
|
}
|
|
1699
1864
|
return false;
|
|
1700
1865
|
}
|
|
1866
|
+
function resolveCapabilityRoot(content, capabilityPath) {
|
|
1867
|
+
let result = content;
|
|
1868
|
+
const variables = ["OMNIDEV_CAPABILITY_ROOT", "CLAUDE_PLUGIN_ROOT"];
|
|
1869
|
+
for (const varName of variables) {
|
|
1870
|
+
result = result.replace(new RegExp(`\\$\\{${varName}\\}`, "g"), capabilityPath);
|
|
1871
|
+
result = result.replace(new RegExp(`\\$${varName}(?![A-Za-z0-9_])`, "g"), capabilityPath);
|
|
1872
|
+
}
|
|
1873
|
+
return result;
|
|
1874
|
+
}
|
|
1875
|
+
function resolveCapabilityRootInConfig(config, capabilityPath) {
|
|
1876
|
+
const result = {};
|
|
1877
|
+
if (config.description !== undefined) {
|
|
1878
|
+
result.description = config.description;
|
|
1879
|
+
}
|
|
1880
|
+
const events = [
|
|
1881
|
+
"PreToolUse",
|
|
1882
|
+
"PostToolUse",
|
|
1883
|
+
"PermissionRequest",
|
|
1884
|
+
"UserPromptSubmit",
|
|
1885
|
+
"Stop",
|
|
1886
|
+
"SubagentStop",
|
|
1887
|
+
"Notification",
|
|
1888
|
+
"SessionStart",
|
|
1889
|
+
"SessionEnd",
|
|
1890
|
+
"PreCompact"
|
|
1891
|
+
];
|
|
1892
|
+
for (const event of events) {
|
|
1893
|
+
const matchers = config[event];
|
|
1894
|
+
if (matchers) {
|
|
1895
|
+
result[event] = matchers.map((matcher) => ({
|
|
1896
|
+
...matcher,
|
|
1897
|
+
hooks: matcher.hooks.map((hook) => {
|
|
1898
|
+
if (hook.type === "command") {
|
|
1899
|
+
return {
|
|
1900
|
+
...hook,
|
|
1901
|
+
command: resolveCapabilityRoot(hook.command, capabilityPath)
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
if (hook.type === "prompt") {
|
|
1905
|
+
return {
|
|
1906
|
+
...hook,
|
|
1907
|
+
prompt: resolveCapabilityRoot(hook.prompt, capabilityPath)
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
return hook;
|
|
1911
|
+
})
|
|
1912
|
+
}));
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return result;
|
|
1916
|
+
}
|
|
1701
1917
|
var REVERSE_MAPPINGS;
|
|
1702
1918
|
var init_variables = __esm(() => {
|
|
1703
1919
|
init_constants();
|
|
1704
1920
|
REVERSE_MAPPINGS = Object.fromEntries(Object.entries(VARIABLE_MAPPINGS).map(([omni, claude]) => [claude, omni]));
|
|
1705
1921
|
});
|
|
1706
1922
|
|
|
1707
|
-
// ../core/src/hooks/loader.ts
|
|
1923
|
+
// ../core/src/hooks/json-loader.ts
|
|
1708
1924
|
import { existsSync as existsSync4, readFileSync } from "node:fs";
|
|
1709
|
-
|
|
1925
|
+
function loadHooksJson(configPath) {
|
|
1926
|
+
const unknownFieldWarnings = [];
|
|
1927
|
+
if (!existsSync4(configPath)) {
|
|
1928
|
+
return {
|
|
1929
|
+
config: createEmptyHooksConfig(),
|
|
1930
|
+
validation: createEmptyValidationResult(),
|
|
1931
|
+
found: false,
|
|
1932
|
+
configPath,
|
|
1933
|
+
unknownFieldWarnings: []
|
|
1934
|
+
};
|
|
1935
|
+
}
|
|
1936
|
+
let rawContent;
|
|
1937
|
+
try {
|
|
1938
|
+
rawContent = readFileSync(configPath, "utf-8");
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
return {
|
|
1941
|
+
config: createEmptyHooksConfig(),
|
|
1942
|
+
validation: {
|
|
1943
|
+
valid: false,
|
|
1944
|
+
errors: [
|
|
1945
|
+
{
|
|
1946
|
+
severity: "error",
|
|
1947
|
+
code: "HOOKS_INVALID_TOML",
|
|
1948
|
+
message: `Failed to read hooks.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
1949
|
+
path: configPath
|
|
1950
|
+
}
|
|
1951
|
+
],
|
|
1952
|
+
warnings: []
|
|
1953
|
+
},
|
|
1954
|
+
found: true,
|
|
1955
|
+
configPath,
|
|
1956
|
+
loadError: `Failed to read: ${error instanceof Error ? error.message : String(error)}`,
|
|
1957
|
+
unknownFieldWarnings: []
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
let parsed;
|
|
1961
|
+
try {
|
|
1962
|
+
parsed = JSON.parse(rawContent);
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
return {
|
|
1965
|
+
config: createEmptyHooksConfig(),
|
|
1966
|
+
validation: {
|
|
1967
|
+
valid: false,
|
|
1968
|
+
errors: [
|
|
1969
|
+
{
|
|
1970
|
+
severity: "error",
|
|
1971
|
+
code: "HOOKS_INVALID_TOML",
|
|
1972
|
+
message: `Invalid JSON syntax: ${error instanceof Error ? error.message : String(error)}`,
|
|
1973
|
+
path: configPath
|
|
1974
|
+
}
|
|
1975
|
+
],
|
|
1976
|
+
warnings: []
|
|
1977
|
+
},
|
|
1978
|
+
found: true,
|
|
1979
|
+
configPath,
|
|
1980
|
+
loadError: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
1981
|
+
unknownFieldWarnings: []
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
const result = convertClaudeHooksToConfig(parsed, unknownFieldWarnings);
|
|
1985
|
+
return {
|
|
1986
|
+
config: result.config,
|
|
1987
|
+
validation: result.validation,
|
|
1988
|
+
found: true,
|
|
1989
|
+
configPath,
|
|
1990
|
+
unknownFieldWarnings
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
function convertClaudeHooksToConfig(claudeHooks, unknownFieldWarnings) {
|
|
1994
|
+
const config = {};
|
|
1995
|
+
const errors = [];
|
|
1996
|
+
const warnings = [];
|
|
1997
|
+
if (typeof claudeHooks !== "object" || claudeHooks === null || Array.isArray(claudeHooks)) {
|
|
1998
|
+
errors.push({
|
|
1999
|
+
severity: "error",
|
|
2000
|
+
code: "HOOKS_INVALID_TOML",
|
|
2001
|
+
message: "hooks.json must be an object"
|
|
2002
|
+
});
|
|
2003
|
+
return { config: createEmptyHooksConfig(), validation: { valid: false, errors, warnings } };
|
|
2004
|
+
}
|
|
2005
|
+
for (const [eventName, matchers] of Object.entries(claudeHooks)) {
|
|
2006
|
+
if (!isHookEvent(eventName)) {
|
|
2007
|
+
unknownFieldWarnings.push(`Unknown event "${eventName}" in hooks.json. Valid events: ${HOOK_EVENTS.join(", ")}`);
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
const event = eventName;
|
|
2011
|
+
if (!Array.isArray(matchers)) {
|
|
2012
|
+
errors.push({
|
|
2013
|
+
severity: "error",
|
|
2014
|
+
code: "HOOKS_INVALID_TOML",
|
|
2015
|
+
event,
|
|
2016
|
+
message: `${event} must be an array of matchers`
|
|
2017
|
+
});
|
|
2018
|
+
continue;
|
|
2019
|
+
}
|
|
2020
|
+
const convertedMatchers = [];
|
|
2021
|
+
for (let i = 0;i < matchers.length; i++) {
|
|
2022
|
+
const matcher = matchers[i];
|
|
2023
|
+
if (!matcher)
|
|
2024
|
+
continue;
|
|
2025
|
+
if (typeof matcher === "object" && matcher !== null) {
|
|
2026
|
+
for (const key of Object.keys(matcher)) {
|
|
2027
|
+
if (!KNOWN_MATCHER_FIELDS.has(key)) {
|
|
2028
|
+
unknownFieldWarnings.push(`Unknown field "${key}" in ${event}[${i}]. Known fields: ${[...KNOWN_MATCHER_FIELDS].join(", ")}`);
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
const convertedMatcher = convertMatcher(matcher, event, i, errors, unknownFieldWarnings);
|
|
2033
|
+
if (convertedMatcher) {
|
|
2034
|
+
convertedMatchers.push(convertedMatcher);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
if (convertedMatchers.length > 0) {
|
|
2038
|
+
config[event] = convertedMatchers;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
return {
|
|
2042
|
+
config,
|
|
2043
|
+
validation: {
|
|
2044
|
+
valid: errors.length === 0,
|
|
2045
|
+
errors,
|
|
2046
|
+
warnings
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
function convertMatcher(matcher, event, matcherIndex, errors, unknownFieldWarnings) {
|
|
2051
|
+
if (typeof matcher !== "object" || matcher === null || Array.isArray(matcher)) {
|
|
2052
|
+
errors.push({
|
|
2053
|
+
severity: "error",
|
|
2054
|
+
code: "HOOKS_INVALID_TOML",
|
|
2055
|
+
event,
|
|
2056
|
+
matcherIndex,
|
|
2057
|
+
message: `Matcher at index ${matcherIndex} must be an object`
|
|
2058
|
+
});
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
const hooks = [];
|
|
2062
|
+
const hooksArray = matcher.hooks;
|
|
2063
|
+
if (!hooksArray) {
|
|
2064
|
+
errors.push({
|
|
2065
|
+
severity: "error",
|
|
2066
|
+
code: "HOOKS_INVALID_HOOKS_ARRAY",
|
|
2067
|
+
event,
|
|
2068
|
+
matcherIndex,
|
|
2069
|
+
message: "Matcher must have a 'hooks' array"
|
|
2070
|
+
});
|
|
2071
|
+
return null;
|
|
2072
|
+
}
|
|
2073
|
+
if (!Array.isArray(hooksArray)) {
|
|
2074
|
+
errors.push({
|
|
2075
|
+
severity: "error",
|
|
2076
|
+
code: "HOOKS_INVALID_HOOKS_ARRAY",
|
|
2077
|
+
event,
|
|
2078
|
+
matcherIndex,
|
|
2079
|
+
message: "'hooks' must be an array"
|
|
2080
|
+
});
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
for (let i = 0;i < hooksArray.length; i++) {
|
|
2084
|
+
const hookEntry = hooksArray[i];
|
|
2085
|
+
if (!hookEntry)
|
|
2086
|
+
continue;
|
|
2087
|
+
if (typeof hookEntry === "object" && hookEntry !== null) {
|
|
2088
|
+
for (const key of Object.keys(hookEntry)) {
|
|
2089
|
+
if (!KNOWN_HOOK_FIELDS.has(key)) {
|
|
2090
|
+
unknownFieldWarnings.push(`Unknown field "${key}" in ${event}[${matcherIndex}].hooks[${i}]. Known fields: ${[...KNOWN_HOOK_FIELDS].join(", ")}`);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
const hook = convertHook(hookEntry, event, matcherIndex, i, errors);
|
|
2095
|
+
if (hook) {
|
|
2096
|
+
hooks.push(hook);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
if (hooks.length === 0) {
|
|
2100
|
+
return null;
|
|
2101
|
+
}
|
|
2102
|
+
const result = { hooks };
|
|
2103
|
+
if (matcher.matcher !== undefined) {
|
|
2104
|
+
result.matcher = matcher.matcher;
|
|
2105
|
+
}
|
|
2106
|
+
return result;
|
|
2107
|
+
}
|
|
2108
|
+
function convertHook(hookEntry, event, matcherIndex, hookIndex, errors) {
|
|
2109
|
+
if (typeof hookEntry !== "object" || hookEntry === null || Array.isArray(hookEntry)) {
|
|
2110
|
+
errors.push({
|
|
2111
|
+
severity: "error",
|
|
2112
|
+
code: "HOOKS_INVALID_TOML",
|
|
2113
|
+
event,
|
|
2114
|
+
matcherIndex,
|
|
2115
|
+
hookIndex,
|
|
2116
|
+
message: "Hook must be an object"
|
|
2117
|
+
});
|
|
2118
|
+
return null;
|
|
2119
|
+
}
|
|
2120
|
+
const hookType = hookEntry.type;
|
|
2121
|
+
if (!hookType) {
|
|
2122
|
+
errors.push({
|
|
2123
|
+
severity: "error",
|
|
2124
|
+
code: "HOOKS_INVALID_TYPE",
|
|
2125
|
+
event,
|
|
2126
|
+
matcherIndex,
|
|
2127
|
+
hookIndex,
|
|
2128
|
+
message: "Hook must have a 'type' field"
|
|
2129
|
+
});
|
|
2130
|
+
return null;
|
|
2131
|
+
}
|
|
2132
|
+
if (!isHookType(hookType)) {
|
|
2133
|
+
errors.push({
|
|
2134
|
+
severity: "error",
|
|
2135
|
+
code: "HOOKS_INVALID_TYPE",
|
|
2136
|
+
event,
|
|
2137
|
+
matcherIndex,
|
|
2138
|
+
hookIndex,
|
|
2139
|
+
message: `Invalid hook type: "${hookType}". Must be "command" or "prompt"`
|
|
2140
|
+
});
|
|
2141
|
+
return null;
|
|
2142
|
+
}
|
|
2143
|
+
if (hookType === "command") {
|
|
2144
|
+
if (typeof hookEntry.command !== "string") {
|
|
2145
|
+
errors.push({
|
|
2146
|
+
severity: "error",
|
|
2147
|
+
code: "HOOKS_MISSING_COMMAND",
|
|
2148
|
+
event,
|
|
2149
|
+
matcherIndex,
|
|
2150
|
+
hookIndex,
|
|
2151
|
+
message: "Command hook must have a 'command' string field"
|
|
2152
|
+
});
|
|
2153
|
+
return null;
|
|
2154
|
+
}
|
|
2155
|
+
const hook = {
|
|
2156
|
+
type: "command",
|
|
2157
|
+
command: hookEntry.command
|
|
2158
|
+
};
|
|
2159
|
+
if (typeof hookEntry.timeout === "number") {
|
|
2160
|
+
hook.timeout = hookEntry.timeout;
|
|
2161
|
+
}
|
|
2162
|
+
return hook;
|
|
2163
|
+
}
|
|
2164
|
+
if (hookType === "prompt") {
|
|
2165
|
+
if (typeof hookEntry.prompt !== "string") {
|
|
2166
|
+
errors.push({
|
|
2167
|
+
severity: "error",
|
|
2168
|
+
code: "HOOKS_MISSING_PROMPT",
|
|
2169
|
+
event,
|
|
2170
|
+
matcherIndex,
|
|
2171
|
+
hookIndex,
|
|
2172
|
+
message: "Prompt hook must have a 'prompt' string field"
|
|
2173
|
+
});
|
|
2174
|
+
return null;
|
|
2175
|
+
}
|
|
2176
|
+
const hook = {
|
|
2177
|
+
type: "prompt",
|
|
2178
|
+
prompt: hookEntry.prompt
|
|
2179
|
+
};
|
|
2180
|
+
if (typeof hookEntry.timeout === "number") {
|
|
2181
|
+
hook.timeout = hookEntry.timeout;
|
|
2182
|
+
}
|
|
2183
|
+
return hook;
|
|
2184
|
+
}
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
var KNOWN_HOOK_FIELDS, KNOWN_MATCHER_FIELDS;
|
|
2188
|
+
var init_json_loader = __esm(() => {
|
|
2189
|
+
init_constants();
|
|
2190
|
+
init_types();
|
|
2191
|
+
init_validation();
|
|
2192
|
+
KNOWN_HOOK_FIELDS = new Set(["type", "command", "prompt", "timeout"]);
|
|
2193
|
+
KNOWN_MATCHER_FIELDS = new Set(["matcher", "hooks"]);
|
|
2194
|
+
});
|
|
2195
|
+
|
|
2196
|
+
// ../core/src/hooks/loader.ts
|
|
2197
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
2198
|
+
import { join as join3 } from "node:path";
|
|
1710
2199
|
function loadHooksFromCapability(capabilityPath, options) {
|
|
1711
2200
|
const opts = {
|
|
1712
2201
|
transformVariables: true,
|
|
1713
2202
|
validate: true,
|
|
1714
2203
|
checkScripts: false,
|
|
2204
|
+
resolveCapabilityRoot: false,
|
|
1715
2205
|
...options
|
|
1716
2206
|
};
|
|
1717
|
-
const hooksDir =
|
|
1718
|
-
const
|
|
1719
|
-
if (
|
|
2207
|
+
const hooksDir = join3(capabilityPath, HOOKS_DIRECTORY);
|
|
2208
|
+
const tomlConfigPath = join3(hooksDir, HOOKS_CONFIG_FILENAME);
|
|
2209
|
+
if (existsSync5(tomlConfigPath)) {
|
|
2210
|
+
return loadTomlHooks(capabilityPath, tomlConfigPath, hooksDir, opts);
|
|
2211
|
+
}
|
|
2212
|
+
const hooksJsonInDir = join3(hooksDir, CLAUDE_HOOKS_CONFIG_FILENAME);
|
|
2213
|
+
const hooksJsonAtRoot = join3(capabilityPath, CLAUDE_HOOKS_CONFIG_FILENAME);
|
|
2214
|
+
const hooksJsonDirExists = existsSync5(hooksJsonInDir);
|
|
2215
|
+
const hooksJsonRootExists = existsSync5(hooksJsonAtRoot);
|
|
2216
|
+
if (!hooksJsonDirExists && !hooksJsonRootExists) {
|
|
1720
2217
|
return {
|
|
1721
2218
|
config: createEmptyHooksConfig(),
|
|
1722
2219
|
validation: createEmptyValidationResult(),
|
|
1723
2220
|
found: false
|
|
1724
2221
|
};
|
|
1725
2222
|
}
|
|
2223
|
+
return loadJsonHooksFiles(capabilityPath, hooksJsonInDir, hooksJsonAtRoot, hooksDir, opts);
|
|
2224
|
+
}
|
|
2225
|
+
function loadTomlHooks(capabilityPath, configPath, hooksDir, opts) {
|
|
1726
2226
|
let rawContent;
|
|
1727
2227
|
try {
|
|
1728
|
-
rawContent =
|
|
2228
|
+
rawContent = readFileSync2(configPath, "utf-8");
|
|
1729
2229
|
} catch (error) {
|
|
1730
2230
|
return {
|
|
1731
2231
|
config: createEmptyHooksConfig(),
|
|
@@ -1782,13 +2282,102 @@ function loadHooksFromCapability(capabilityPath, options) {
|
|
|
1782
2282
|
} else {
|
|
1783
2283
|
validation = createEmptyValidationResult();
|
|
1784
2284
|
}
|
|
2285
|
+
let config = validation.valid ? parsed : createEmptyHooksConfig();
|
|
2286
|
+
if (opts.resolveCapabilityRoot && validation.valid) {
|
|
2287
|
+
config = resolveCapabilityRootInConfig(config, capabilityPath);
|
|
2288
|
+
}
|
|
1785
2289
|
return {
|
|
1786
|
-
config
|
|
2290
|
+
config,
|
|
1787
2291
|
validation,
|
|
1788
2292
|
found: true,
|
|
1789
2293
|
configPath
|
|
1790
2294
|
};
|
|
1791
2295
|
}
|
|
2296
|
+
function loadJsonHooksFiles(capabilityPath, hooksJsonInDir, hooksJsonAtRoot, hooksDir, opts) {
|
|
2297
|
+
const configs = [];
|
|
2298
|
+
const allErrors = [];
|
|
2299
|
+
const allWarnings = [];
|
|
2300
|
+
const unknownFieldWarnings = [];
|
|
2301
|
+
let foundPath;
|
|
2302
|
+
if (existsSync5(hooksJsonInDir)) {
|
|
2303
|
+
const result2 = loadHooksJson(hooksJsonInDir);
|
|
2304
|
+
if (result2.found) {
|
|
2305
|
+
foundPath = result2.configPath;
|
|
2306
|
+
if (result2.loadError) {
|
|
2307
|
+
allErrors.push(...result2.validation.errors);
|
|
2308
|
+
} else {
|
|
2309
|
+
configs.push(result2.config);
|
|
2310
|
+
allErrors.push(...result2.validation.errors);
|
|
2311
|
+
allWarnings.push(...result2.validation.warnings);
|
|
2312
|
+
}
|
|
2313
|
+
unknownFieldWarnings.push(...result2.unknownFieldWarnings);
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
if (existsSync5(hooksJsonAtRoot)) {
|
|
2317
|
+
const result2 = loadHooksJson(hooksJsonAtRoot);
|
|
2318
|
+
if (result2.found) {
|
|
2319
|
+
if (!foundPath) {
|
|
2320
|
+
foundPath = result2.configPath;
|
|
2321
|
+
}
|
|
2322
|
+
if (result2.loadError) {
|
|
2323
|
+
allErrors.push(...result2.validation.errors);
|
|
2324
|
+
} else {
|
|
2325
|
+
configs.push(result2.config);
|
|
2326
|
+
allErrors.push(...result2.validation.errors);
|
|
2327
|
+
allWarnings.push(...result2.validation.warnings);
|
|
2328
|
+
}
|
|
2329
|
+
unknownFieldWarnings.push(...result2.unknownFieldWarnings);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
for (const warning of unknownFieldWarnings) {
|
|
2333
|
+
console.warn(`[hooks] Warning: ${warning}`);
|
|
2334
|
+
}
|
|
2335
|
+
let mergedConfig = createEmptyHooksConfig();
|
|
2336
|
+
if (configs.length > 0) {
|
|
2337
|
+
mergedConfig = mergeRawHooksConfigs(configs);
|
|
2338
|
+
}
|
|
2339
|
+
let validation;
|
|
2340
|
+
if (opts.validate && allErrors.length === 0) {
|
|
2341
|
+
validation = validateHooksConfig(mergedConfig, {
|
|
2342
|
+
basePath: hooksDir,
|
|
2343
|
+
checkScripts: opts.checkScripts ?? false
|
|
2344
|
+
});
|
|
2345
|
+
} else {
|
|
2346
|
+
validation = {
|
|
2347
|
+
valid: allErrors.length === 0,
|
|
2348
|
+
errors: allErrors,
|
|
2349
|
+
warnings: allWarnings
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
if (opts.resolveCapabilityRoot && validation.valid) {
|
|
2353
|
+
mergedConfig = resolveCapabilityRootInConfig(mergedConfig, capabilityPath);
|
|
2354
|
+
}
|
|
2355
|
+
const result = {
|
|
2356
|
+
config: validation.valid ? mergedConfig : createEmptyHooksConfig(),
|
|
2357
|
+
validation,
|
|
2358
|
+
found: true
|
|
2359
|
+
};
|
|
2360
|
+
if (foundPath) {
|
|
2361
|
+
result.configPath = foundPath;
|
|
2362
|
+
}
|
|
2363
|
+
return result;
|
|
2364
|
+
}
|
|
2365
|
+
function mergeRawHooksConfigs(configs) {
|
|
2366
|
+
const result = {};
|
|
2367
|
+
for (const event of HOOK_EVENTS) {
|
|
2368
|
+
const allMatchers = [];
|
|
2369
|
+
for (const config of configs) {
|
|
2370
|
+
const matchers = config[event];
|
|
2371
|
+
if (matchers && matchers.length > 0) {
|
|
2372
|
+
allMatchers.push(...matchers);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
if (allMatchers.length > 0) {
|
|
2376
|
+
result[event] = allMatchers;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
return result;
|
|
2380
|
+
}
|
|
1792
2381
|
function loadCapabilityHooks(capabilityName, capabilityPath, options) {
|
|
1793
2382
|
const result = loadHooksFromCapability(capabilityPath, options);
|
|
1794
2383
|
if (!result.found) {
|
|
@@ -1802,39 +2391,43 @@ function loadCapabilityHooks(capabilityName, capabilityPath, options) {
|
|
|
1802
2391
|
};
|
|
1803
2392
|
}
|
|
1804
2393
|
function hasHooks(capabilityPath) {
|
|
1805
|
-
const
|
|
1806
|
-
|
|
2394
|
+
const tomlPath = join3(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
|
|
2395
|
+
const jsonInDir = join3(capabilityPath, HOOKS_DIRECTORY, CLAUDE_HOOKS_CONFIG_FILENAME);
|
|
2396
|
+
const jsonAtRoot = join3(capabilityPath, CLAUDE_HOOKS_CONFIG_FILENAME);
|
|
2397
|
+
return existsSync5(tomlPath) || existsSync5(jsonInDir) || existsSync5(jsonAtRoot);
|
|
1807
2398
|
}
|
|
1808
2399
|
function getHooksDirectory(capabilityPath) {
|
|
1809
|
-
return
|
|
2400
|
+
return join3(capabilityPath, HOOKS_DIRECTORY);
|
|
1810
2401
|
}
|
|
1811
2402
|
function getHooksConfigPath(capabilityPath) {
|
|
1812
|
-
return
|
|
2403
|
+
return join3(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
|
|
1813
2404
|
}
|
|
1814
2405
|
var init_loader = __esm(() => {
|
|
1815
2406
|
init_dist();
|
|
1816
2407
|
init_constants();
|
|
1817
2408
|
init_validation();
|
|
1818
2409
|
init_variables();
|
|
2410
|
+
init_json_loader();
|
|
2411
|
+
init_constants();
|
|
1819
2412
|
});
|
|
1820
2413
|
|
|
1821
2414
|
// ../core/src/capability/rules.ts
|
|
1822
|
-
import { existsSync as
|
|
2415
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3 } from "node:fs";
|
|
1823
2416
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
1824
|
-
import { basename as
|
|
2417
|
+
import { basename as basename3, join as join4 } from "node:path";
|
|
1825
2418
|
async function loadRules(capabilityPath, capabilityId) {
|
|
1826
|
-
const rulesDir =
|
|
1827
|
-
if (!
|
|
2419
|
+
const rulesDir = join4(capabilityPath, "rules");
|
|
2420
|
+
if (!existsSync6(rulesDir)) {
|
|
1828
2421
|
return [];
|
|
1829
2422
|
}
|
|
1830
2423
|
const rules = [];
|
|
1831
2424
|
const entries = readdirSync3(rulesDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1832
2425
|
for (const entry of entries) {
|
|
1833
2426
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
1834
|
-
const rulePath =
|
|
2427
|
+
const rulePath = join4(rulesDir, entry.name);
|
|
1835
2428
|
const content = await readFile3(rulePath, "utf-8");
|
|
1836
2429
|
rules.push({
|
|
1837
|
-
name:
|
|
2430
|
+
name: basename3(entry.name, ".md"),
|
|
1838
2431
|
content: content.trim(),
|
|
1839
2432
|
capabilityId
|
|
1840
2433
|
});
|
|
@@ -1845,22 +2438,25 @@ async function loadRules(capabilityPath, capabilityId) {
|
|
|
1845
2438
|
var init_rules = () => {};
|
|
1846
2439
|
|
|
1847
2440
|
// ../core/src/capability/skills.ts
|
|
1848
|
-
import { existsSync as
|
|
2441
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
1849
2442
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
1850
|
-
import { join as
|
|
2443
|
+
import { join as join5 } from "node:path";
|
|
1851
2444
|
async function loadSkills(capabilityPath, capabilityId) {
|
|
1852
|
-
const skillsDir = join6(capabilityPath, "skills");
|
|
1853
|
-
if (!existsSync6(skillsDir)) {
|
|
1854
|
-
return [];
|
|
1855
|
-
}
|
|
1856
2445
|
const skills = [];
|
|
1857
|
-
const
|
|
1858
|
-
for (const
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2446
|
+
const possibleDirNames = ["skills", "skill"];
|
|
2447
|
+
for (const dirName of possibleDirNames) {
|
|
2448
|
+
const dir = join5(capabilityPath, dirName);
|
|
2449
|
+
if (!existsSync7(dir)) {
|
|
2450
|
+
continue;
|
|
2451
|
+
}
|
|
2452
|
+
const entries = readdirSync4(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
2453
|
+
for (const entry of entries) {
|
|
2454
|
+
if (entry.isDirectory()) {
|
|
2455
|
+
const skillPath = join5(dir, entry.name, "SKILL.md");
|
|
2456
|
+
if (existsSync7(skillPath)) {
|
|
2457
|
+
const skill = await parseSkillFile(skillPath, capabilityId);
|
|
2458
|
+
skills.push(skill);
|
|
2459
|
+
}
|
|
1864
2460
|
}
|
|
1865
2461
|
}
|
|
1866
2462
|
}
|
|
@@ -1875,7 +2471,7 @@ async function parseSkillFile(filePath, capabilityId) {
|
|
|
1875
2471
|
const frontmatter = parsed.frontmatter;
|
|
1876
2472
|
const instructions = parsed.markdown;
|
|
1877
2473
|
if (!frontmatter.name || !frontmatter.description) {
|
|
1878
|
-
throw new Error(`Invalid SKILL.md at ${filePath}: name and description required`);
|
|
2474
|
+
throw new Error(`Invalid SKILL.md at ${filePath}: name and description required in frontmatter`);
|
|
1879
2475
|
}
|
|
1880
2476
|
return {
|
|
1881
2477
|
name: frontmatter.name,
|
|
@@ -1887,20 +2483,30 @@ async function parseSkillFile(filePath, capabilityId) {
|
|
|
1887
2483
|
var init_skills = () => {};
|
|
1888
2484
|
|
|
1889
2485
|
// ../core/src/capability/subagents.ts
|
|
1890
|
-
import { existsSync as
|
|
2486
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "node:fs";
|
|
1891
2487
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
1892
|
-
import { join as
|
|
2488
|
+
import { basename as basename4, join as join6 } from "node:path";
|
|
1893
2489
|
async function loadSubagents(capabilityPath, capabilityId) {
|
|
1894
|
-
const subagentsDir = join7(capabilityPath, "subagents");
|
|
1895
|
-
if (!existsSync7(subagentsDir)) {
|
|
1896
|
-
return [];
|
|
1897
|
-
}
|
|
1898
2490
|
const subagents = [];
|
|
1899
|
-
const
|
|
1900
|
-
for (const
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
2491
|
+
const possibleDirNames = ["subagents", "agents", "agent", "subagent"];
|
|
2492
|
+
for (const dirName of possibleDirNames) {
|
|
2493
|
+
const dir = join6(capabilityPath, dirName);
|
|
2494
|
+
if (!existsSync8(dir)) {
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
const entries = readdirSync5(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
2498
|
+
for (const entry of entries) {
|
|
2499
|
+
if (entry.isDirectory()) {
|
|
2500
|
+
let subagentPath = join6(dir, entry.name, "SUBAGENT.md");
|
|
2501
|
+
if (!existsSync8(subagentPath)) {
|
|
2502
|
+
subagentPath = join6(dir, entry.name, "AGENT.md");
|
|
2503
|
+
}
|
|
2504
|
+
if (existsSync8(subagentPath)) {
|
|
2505
|
+
const subagent = await parseSubagentFile(subagentPath, capabilityId);
|
|
2506
|
+
subagents.push(subagent);
|
|
2507
|
+
}
|
|
2508
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2509
|
+
const subagentPath = join6(dir, entry.name);
|
|
1904
2510
|
const subagent = await parseSubagentFile(subagentPath, capabilityId);
|
|
1905
2511
|
subagents.push(subagent);
|
|
1906
2512
|
}
|
|
@@ -1916,11 +2522,13 @@ async function parseSubagentFile(filePath, capabilityId) {
|
|
|
1916
2522
|
}
|
|
1917
2523
|
const frontmatter = parsed.frontmatter;
|
|
1918
2524
|
const systemPrompt = parsed.markdown;
|
|
1919
|
-
|
|
2525
|
+
const inferredName = basename4(filePath, ".md").replace(/^SUBAGENT$/i, "").replace(/^AGENT$/i, "");
|
|
2526
|
+
const name = frontmatter.name || inferredName;
|
|
2527
|
+
if (!name || !frontmatter.description) {
|
|
1920
2528
|
throw new Error(`Invalid SUBAGENT.md at ${filePath}: name and description required`);
|
|
1921
2529
|
}
|
|
1922
2530
|
const result = {
|
|
1923
|
-
name
|
|
2531
|
+
name,
|
|
1924
2532
|
description: frontmatter.description,
|
|
1925
2533
|
systemPrompt: systemPrompt.trim(),
|
|
1926
2534
|
capabilityId
|
|
@@ -1951,18 +2559,18 @@ function parseCommaSeparatedList(value) {
|
|
|
1951
2559
|
var init_subagents = () => {};
|
|
1952
2560
|
|
|
1953
2561
|
// ../core/src/capability/loader.ts
|
|
1954
|
-
import { existsSync as
|
|
2562
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6 } from "node:fs";
|
|
1955
2563
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
1956
|
-
import { join as
|
|
2564
|
+
import { join as join7 } from "node:path";
|
|
1957
2565
|
async function discoverCapabilities() {
|
|
1958
2566
|
const capabilities = [];
|
|
1959
|
-
if (
|
|
2567
|
+
if (existsSync9(CAPABILITIES_DIR)) {
|
|
1960
2568
|
const entries = readdirSync6(CAPABILITIES_DIR, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
1961
2569
|
for (const entry of entries) {
|
|
1962
2570
|
if (entry.isDirectory()) {
|
|
1963
|
-
const entryPath =
|
|
1964
|
-
const configPath =
|
|
1965
|
-
if (
|
|
2571
|
+
const entryPath = join7(CAPABILITIES_DIR, entry.name);
|
|
2572
|
+
const configPath = join7(entryPath, "capability.toml");
|
|
2573
|
+
if (existsSync9(configPath)) {
|
|
1966
2574
|
capabilities.push(entryPath);
|
|
1967
2575
|
}
|
|
1968
2576
|
}
|
|
@@ -1971,28 +2579,28 @@ async function discoverCapabilities() {
|
|
|
1971
2579
|
return capabilities;
|
|
1972
2580
|
}
|
|
1973
2581
|
async function loadCapabilityConfig(capabilityPath) {
|
|
1974
|
-
const configPath =
|
|
2582
|
+
const configPath = join7(capabilityPath, "capability.toml");
|
|
1975
2583
|
const content = await readFile6(configPath, "utf-8");
|
|
1976
2584
|
const config = parseCapabilityConfig(content);
|
|
1977
2585
|
return config;
|
|
1978
2586
|
}
|
|
1979
2587
|
async function importCapabilityExports(capabilityPath) {
|
|
1980
|
-
const builtIndexPath =
|
|
1981
|
-
const jsIndexPath =
|
|
1982
|
-
const tsIndexPath =
|
|
2588
|
+
const builtIndexPath = join7(capabilityPath, "dist", "index.js");
|
|
2589
|
+
const jsIndexPath = join7(capabilityPath, "index.js");
|
|
2590
|
+
const tsIndexPath = join7(capabilityPath, "index.ts");
|
|
1983
2591
|
let indexPath = null;
|
|
1984
|
-
if (
|
|
2592
|
+
if (existsSync9(builtIndexPath)) {
|
|
1985
2593
|
indexPath = builtIndexPath;
|
|
1986
|
-
} else if (
|
|
2594
|
+
} else if (existsSync9(jsIndexPath)) {
|
|
1987
2595
|
indexPath = jsIndexPath;
|
|
1988
|
-
} else if (
|
|
2596
|
+
} else if (existsSync9(tsIndexPath)) {
|
|
1989
2597
|
indexPath = tsIndexPath;
|
|
1990
2598
|
}
|
|
1991
2599
|
if (!indexPath) {
|
|
1992
2600
|
return {};
|
|
1993
2601
|
}
|
|
1994
2602
|
try {
|
|
1995
|
-
const absolutePath =
|
|
2603
|
+
const absolutePath = join7(process.cwd(), indexPath);
|
|
1996
2604
|
const module = await import(absolutePath);
|
|
1997
2605
|
return module;
|
|
1998
2606
|
} catch (error) {
|
|
@@ -2012,8 +2620,8 @@ If this is a project-specific capability, install dependencies or remove it from
|
|
|
2012
2620
|
}
|
|
2013
2621
|
}
|
|
2014
2622
|
async function loadTypeDefinitions(capabilityPath) {
|
|
2015
|
-
const typesPath =
|
|
2016
|
-
if (!
|
|
2623
|
+
const typesPath = join7(capabilityPath, "types.d.ts");
|
|
2624
|
+
if (!existsSync9(typesPath)) {
|
|
2017
2625
|
return;
|
|
2018
2626
|
}
|
|
2019
2627
|
return readFile6(typesPath, "utf-8");
|
|
@@ -2193,20 +2801,57 @@ function convertCommandExports(commandExports, capabilityId) {
|
|
|
2193
2801
|
return result;
|
|
2194
2802
|
});
|
|
2195
2803
|
}
|
|
2804
|
+
function mergeByName(fileBased, programmatic) {
|
|
2805
|
+
const byName = new Map;
|
|
2806
|
+
for (const item of fileBased) {
|
|
2807
|
+
byName.set(item.name, item);
|
|
2808
|
+
}
|
|
2809
|
+
for (const item of programmatic) {
|
|
2810
|
+
byName.set(item.name, item);
|
|
2811
|
+
}
|
|
2812
|
+
return Array.from(byName.values());
|
|
2813
|
+
}
|
|
2196
2814
|
async function loadCapability(capabilityPath) {
|
|
2197
2815
|
const config = await loadCapabilityConfig(capabilityPath);
|
|
2198
2816
|
const id = config.capability.id;
|
|
2199
2817
|
const exports = await importCapabilityExports(capabilityPath);
|
|
2200
2818
|
const exportsAny = exports;
|
|
2201
|
-
const
|
|
2202
|
-
const
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2819
|
+
const defaultExport = exportsAny.default ?? {};
|
|
2820
|
+
const getExportValue = (key) => {
|
|
2821
|
+
if (key in exports && exports[key] !== undefined) {
|
|
2822
|
+
return exportsAny[key];
|
|
2823
|
+
}
|
|
2824
|
+
if (key in defaultExport && defaultExport[key] !== undefined) {
|
|
2825
|
+
return defaultExport[key];
|
|
2826
|
+
}
|
|
2827
|
+
return;
|
|
2828
|
+
};
|
|
2829
|
+
const skillsExport = getExportValue("skills");
|
|
2830
|
+
const programmaticSkills = Array.isArray(skillsExport) ? convertSkillExports(skillsExport, id) : [];
|
|
2831
|
+
const fileSkills = await loadSkills(capabilityPath, id);
|
|
2832
|
+
const skills = mergeByName(fileSkills, programmaticSkills);
|
|
2833
|
+
const rulesExport = getExportValue("rules");
|
|
2834
|
+
const programmaticRules = Array.isArray(rulesExport) ? convertRuleExports(rulesExport, id) : [];
|
|
2835
|
+
const fileRules = await loadRules(capabilityPath, id);
|
|
2836
|
+
const rules = mergeByName(fileRules, programmaticRules);
|
|
2837
|
+
const docsExport = getExportValue("docs");
|
|
2838
|
+
const programmaticDocs = Array.isArray(docsExport) ? convertDocExports(docsExport, id) : [];
|
|
2839
|
+
const fileDocs = await loadDocs(capabilityPath, id);
|
|
2840
|
+
const docs = mergeByName(fileDocs, programmaticDocs);
|
|
2841
|
+
const subagentsExport = getExportValue("subagents");
|
|
2842
|
+
const programmaticSubagents = Array.isArray(subagentsExport) ? convertSubagentExports(subagentsExport, id) : [];
|
|
2843
|
+
const fileSubagents = await loadSubagents(capabilityPath, id);
|
|
2844
|
+
const subagents = mergeByName(fileSubagents, programmaticSubagents);
|
|
2845
|
+
const commandsExport = getExportValue("commands");
|
|
2846
|
+
const programmaticCommands = Array.isArray(commandsExport) ? convertCommandExports(commandsExport, id) : [];
|
|
2847
|
+
const fileCommands = await loadCommands(capabilityPath, id);
|
|
2848
|
+
const commands = mergeByName(fileCommands, programmaticCommands);
|
|
2849
|
+
const typeDefinitionsExport = getExportValue("typeDefinitions");
|
|
2850
|
+
const typeDefinitionsFromExports = typeof typeDefinitionsExport === "string" ? typeDefinitionsExport : undefined;
|
|
2207
2851
|
const typeDefinitions = typeDefinitionsFromExports !== undefined ? typeDefinitionsFromExports : await loadTypeDefinitions(capabilityPath);
|
|
2208
|
-
const
|
|
2209
|
-
const
|
|
2852
|
+
const gitignoreExport = getExportValue("gitignore");
|
|
2853
|
+
const gitignore = Array.isArray(gitignoreExport) ? gitignoreExport : undefined;
|
|
2854
|
+
const hooks = loadCapabilityHooks(id, capabilityPath, { resolveCapabilityRoot: true });
|
|
2210
2855
|
const result = {
|
|
2211
2856
|
id,
|
|
2212
2857
|
path: capabilityPath,
|
|
@@ -2241,8 +2886,8 @@ var init_loader2 = __esm(() => {
|
|
|
2241
2886
|
});
|
|
2242
2887
|
|
|
2243
2888
|
// ../core/src/config/config.ts
|
|
2244
|
-
import { existsSync as
|
|
2245
|
-
import { readFile as readFile7, writeFile
|
|
2889
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
2890
|
+
import { readFile as readFile7, writeFile } from "node:fs/promises";
|
|
2246
2891
|
function mergeConfigs(base, override) {
|
|
2247
2892
|
const merged = { ...base, ...override };
|
|
2248
2893
|
merged.profiles = { ...base.profiles };
|
|
@@ -2255,10 +2900,36 @@ function mergeConfigs(base, override) {
|
|
|
2255
2900
|
if (base.mcps || override.mcps) {
|
|
2256
2901
|
merged.mcps = { ...base.mcps, ...override.mcps };
|
|
2257
2902
|
}
|
|
2903
|
+
if (base.capabilities || override.capabilities) {
|
|
2904
|
+
merged.capabilities = {
|
|
2905
|
+
...base.capabilities,
|
|
2906
|
+
...override.capabilities,
|
|
2907
|
+
sources: { ...base.capabilities?.sources, ...override.capabilities?.sources },
|
|
2908
|
+
groups: { ...base.capabilities?.groups, ...override.capabilities?.groups },
|
|
2909
|
+
always_enabled: [
|
|
2910
|
+
...new Set([
|
|
2911
|
+
...base.capabilities?.always_enabled ?? [],
|
|
2912
|
+
...override.capabilities?.always_enabled ?? []
|
|
2913
|
+
])
|
|
2914
|
+
],
|
|
2915
|
+
always_disabled: [
|
|
2916
|
+
...new Set([
|
|
2917
|
+
...base.capabilities?.always_disabled ?? [],
|
|
2918
|
+
...override.capabilities?.always_disabled ?? []
|
|
2919
|
+
])
|
|
2920
|
+
]
|
|
2921
|
+
};
|
|
2922
|
+
if (merged.capabilities.always_enabled?.length === 0) {
|
|
2923
|
+
delete merged.capabilities.always_enabled;
|
|
2924
|
+
}
|
|
2925
|
+
if (merged.capabilities.always_disabled?.length === 0) {
|
|
2926
|
+
delete merged.capabilities.always_disabled;
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2258
2929
|
return merged;
|
|
2259
2930
|
}
|
|
2260
2931
|
async function loadBaseConfig() {
|
|
2261
|
-
if (
|
|
2932
|
+
if (existsSync10(CONFIG_PATH)) {
|
|
2262
2933
|
const content = await readFile7(CONFIG_PATH, "utf-8");
|
|
2263
2934
|
return parseOmniConfig(content);
|
|
2264
2935
|
}
|
|
@@ -2267,7 +2938,7 @@ async function loadBaseConfig() {
|
|
|
2267
2938
|
async function loadConfig() {
|
|
2268
2939
|
const baseConfig = await loadBaseConfig();
|
|
2269
2940
|
let localConfig = {};
|
|
2270
|
-
if (
|
|
2941
|
+
if (existsSync10(LOCAL_CONFIG)) {
|
|
2271
2942
|
const content = await readFile7(LOCAL_CONFIG, "utf-8");
|
|
2272
2943
|
localConfig = parseOmniConfig(content);
|
|
2273
2944
|
}
|
|
@@ -2275,7 +2946,7 @@ async function loadConfig() {
|
|
|
2275
2946
|
}
|
|
2276
2947
|
async function writeConfig(config) {
|
|
2277
2948
|
const content = generateConfigToml(config);
|
|
2278
|
-
await
|
|
2949
|
+
await writeFile(CONFIG_PATH, content, "utf-8");
|
|
2279
2950
|
}
|
|
2280
2951
|
function generateConfigToml(config) {
|
|
2281
2952
|
const lines = [];
|
|
@@ -2432,10 +3103,10 @@ var init_config = __esm(() => {
|
|
|
2432
3103
|
});
|
|
2433
3104
|
|
|
2434
3105
|
// ../core/src/state/active-profile.ts
|
|
2435
|
-
import { existsSync as
|
|
2436
|
-
import { readFile as readFile8, unlink, writeFile as
|
|
3106
|
+
import { existsSync as existsSync11, mkdirSync } from "node:fs";
|
|
3107
|
+
import { readFile as readFile8, unlink, writeFile as writeFile2 } from "node:fs/promises";
|
|
2437
3108
|
async function readActiveProfileState() {
|
|
2438
|
-
if (!
|
|
3109
|
+
if (!existsSync11(ACTIVE_PROFILE_PATH)) {
|
|
2439
3110
|
return null;
|
|
2440
3111
|
}
|
|
2441
3112
|
try {
|
|
@@ -2448,10 +3119,10 @@ async function readActiveProfileState() {
|
|
|
2448
3119
|
}
|
|
2449
3120
|
async function writeActiveProfileState(profileName) {
|
|
2450
3121
|
mkdirSync(STATE_DIR, { recursive: true });
|
|
2451
|
-
await
|
|
3122
|
+
await writeFile2(ACTIVE_PROFILE_PATH, profileName, "utf-8");
|
|
2452
3123
|
}
|
|
2453
3124
|
async function clearActiveProfileState() {
|
|
2454
|
-
if (
|
|
3125
|
+
if (existsSync11(ACTIVE_PROFILE_PATH)) {
|
|
2455
3126
|
await unlink(ACTIVE_PROFILE_PATH);
|
|
2456
3127
|
}
|
|
2457
3128
|
}
|
|
@@ -2471,6 +3142,7 @@ function resolveEnabledCapabilities(config, profileName) {
|
|
|
2471
3142
|
const profile = profileName ? config.profiles?.[profileName] : config.profiles?.["default"];
|
|
2472
3143
|
const profileCapabilities = profile?.capabilities ?? [];
|
|
2473
3144
|
const alwaysEnabled = config.capabilities?.always_enabled ?? [];
|
|
3145
|
+
const alwaysDisabled = config.capabilities?.always_disabled ?? [];
|
|
2474
3146
|
const groups = config.capabilities?.groups ?? {};
|
|
2475
3147
|
const expandCapabilities = (caps) => {
|
|
2476
3148
|
return caps.flatMap((cap) => {
|
|
@@ -2488,7 +3160,9 @@ function resolveEnabledCapabilities(config, profileName) {
|
|
|
2488
3160
|
};
|
|
2489
3161
|
const expandedAlways = expandCapabilities(alwaysEnabled);
|
|
2490
3162
|
const expandedProfile = expandCapabilities(profileCapabilities);
|
|
2491
|
-
|
|
3163
|
+
const expandedDisabled = new Set(expandCapabilities(alwaysDisabled));
|
|
3164
|
+
const allEnabled = [...new Set([...expandedAlways, ...expandedProfile])];
|
|
3165
|
+
return allEnabled.filter((cap) => !expandedDisabled.has(cap));
|
|
2492
3166
|
}
|
|
2493
3167
|
async function loadProfileConfig(profileName) {
|
|
2494
3168
|
const config = await loadConfig();
|
|
@@ -2692,10 +3366,10 @@ function getActiveProviders(config) {
|
|
|
2692
3366
|
var init_types2 = () => {};
|
|
2693
3367
|
|
|
2694
3368
|
// ../core/src/capability/sources.ts
|
|
2695
|
-
import { existsSync as
|
|
3369
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
2696
3370
|
import { spawn } from "node:child_process";
|
|
2697
|
-
import { cp, mkdir
|
|
2698
|
-
import { join as
|
|
3371
|
+
import { cp, mkdir, readdir, readFile as readFile9, rename, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
|
|
3372
|
+
import { join as join8 } from "node:path";
|
|
2699
3373
|
import { createHash } from "node:crypto";
|
|
2700
3374
|
async function spawnCapture(command, args, options) {
|
|
2701
3375
|
const timeout = options?.timeout ?? 60000;
|
|
@@ -2752,8 +3426,8 @@ function parseFileSourcePath(source) {
|
|
|
2752
3426
|
return source.slice(7);
|
|
2753
3427
|
}
|
|
2754
3428
|
async function readCapabilityIdFromPath(capabilityPath) {
|
|
2755
|
-
const tomlPath =
|
|
2756
|
-
if (
|
|
3429
|
+
const tomlPath = join8(capabilityPath, "capability.toml");
|
|
3430
|
+
if (existsSync12(tomlPath)) {
|
|
2757
3431
|
try {
|
|
2758
3432
|
const content = await readFile9(tomlPath, "utf-8");
|
|
2759
3433
|
const parsed = parse(content);
|
|
@@ -2808,14 +3482,14 @@ function sourceToGitUrl(source) {
|
|
|
2808
3482
|
return source;
|
|
2809
3483
|
}
|
|
2810
3484
|
function getSourceCapabilityPath(id) {
|
|
2811
|
-
return
|
|
3485
|
+
return join8(OMNI_LOCAL, "capabilities", id);
|
|
2812
3486
|
}
|
|
2813
3487
|
function getLockFilePath() {
|
|
2814
3488
|
return "omni.lock.toml";
|
|
2815
3489
|
}
|
|
2816
3490
|
async function loadLockFile() {
|
|
2817
3491
|
const lockPath = getLockFilePath();
|
|
2818
|
-
if (!
|
|
3492
|
+
if (!existsSync12(lockPath)) {
|
|
2819
3493
|
return { capabilities: {} };
|
|
2820
3494
|
}
|
|
2821
3495
|
try {
|
|
@@ -2877,14 +3551,14 @@ function stringifyLockFile(lockFile) {
|
|
|
2877
3551
|
}
|
|
2878
3552
|
async function saveLockFile(lockFile) {
|
|
2879
3553
|
const lockPath = getLockFilePath();
|
|
2880
|
-
await
|
|
3554
|
+
await mkdir(join8(OMNI_LOCAL, "capabilities"), { recursive: true });
|
|
2881
3555
|
const header = `# Auto-generated by OmniDev - DO NOT EDIT
|
|
2882
3556
|
# Records installed capability versions for reproducibility
|
|
2883
3557
|
# Last updated: ${new Date().toISOString()}
|
|
2884
3558
|
|
|
2885
3559
|
`;
|
|
2886
3560
|
const content = header + stringifyLockFile(lockFile);
|
|
2887
|
-
await
|
|
3561
|
+
await writeFile3(lockPath, content, "utf-8");
|
|
2888
3562
|
}
|
|
2889
3563
|
async function getRepoCommit(repoPath) {
|
|
2890
3564
|
const { exitCode, stdout, stderr } = await spawnCapture("git", ["rev-parse", "HEAD"], {
|
|
@@ -2901,6 +3575,9 @@ function shortCommit(commit) {
|
|
|
2901
3575
|
function shortContentHash(hash) {
|
|
2902
3576
|
return hash.substring(0, 12);
|
|
2903
3577
|
}
|
|
3578
|
+
function escapeTomlString(value) {
|
|
3579
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
3580
|
+
}
|
|
2904
3581
|
async function computeContentHash(dirPath, excludePatterns = CONTENT_HASH_EXCLUDES) {
|
|
2905
3582
|
const hash = createHash("sha256");
|
|
2906
3583
|
const files = [];
|
|
@@ -2908,7 +3585,7 @@ async function computeContentHash(dirPath, excludePatterns = CONTENT_HASH_EXCLUD
|
|
|
2908
3585
|
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
2909
3586
|
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
2910
3587
|
for (const entry of entries) {
|
|
2911
|
-
const fullPath =
|
|
3588
|
+
const fullPath = join8(currentPath, entry.name);
|
|
2912
3589
|
const relativePath = fullPath.slice(relativeTo.length + 1);
|
|
2913
3590
|
if (excludePatterns.some((pattern) => entry.name === pattern || relativePath.startsWith(`${pattern}/`))) {
|
|
2914
3591
|
continue;
|
|
@@ -2930,8 +3607,8 @@ async function computeContentHash(dirPath, excludePatterns = CONTENT_HASH_EXCLUD
|
|
|
2930
3607
|
return hash.digest("hex");
|
|
2931
3608
|
}
|
|
2932
3609
|
async function detectDisplayVersion(dirPath, fallback, fallbackSource) {
|
|
2933
|
-
const capTomlPath =
|
|
2934
|
-
if (
|
|
3610
|
+
const capTomlPath = join8(dirPath, "capability.toml");
|
|
3611
|
+
if (existsSync12(capTomlPath)) {
|
|
2935
3612
|
try {
|
|
2936
3613
|
const content = await readFile9(capTomlPath, "utf-8");
|
|
2937
3614
|
const parsed = parse(content);
|
|
@@ -2941,8 +3618,8 @@ async function detectDisplayVersion(dirPath, fallback, fallbackSource) {
|
|
|
2941
3618
|
}
|
|
2942
3619
|
} catch {}
|
|
2943
3620
|
}
|
|
2944
|
-
const pluginJsonPath =
|
|
2945
|
-
if (
|
|
3621
|
+
const pluginJsonPath = join8(dirPath, ".claude-plugin", "plugin.json");
|
|
3622
|
+
if (existsSync12(pluginJsonPath)) {
|
|
2946
3623
|
try {
|
|
2947
3624
|
const content = await readFile9(pluginJsonPath, "utf-8");
|
|
2948
3625
|
const parsed = JSON.parse(content);
|
|
@@ -2951,8 +3628,8 @@ async function detectDisplayVersion(dirPath, fallback, fallbackSource) {
|
|
|
2951
3628
|
}
|
|
2952
3629
|
} catch {}
|
|
2953
3630
|
}
|
|
2954
|
-
const pkgJsonPath =
|
|
2955
|
-
if (
|
|
3631
|
+
const pkgJsonPath = join8(dirPath, "package.json");
|
|
3632
|
+
if (existsSync12(pkgJsonPath)) {
|
|
2956
3633
|
try {
|
|
2957
3634
|
const content = await readFile9(pkgJsonPath, "utf-8");
|
|
2958
3635
|
const parsed = JSON.parse(content);
|
|
@@ -2963,20 +3640,111 @@ async function detectDisplayVersion(dirPath, fallback, fallbackSource) {
|
|
|
2963
3640
|
}
|
|
2964
3641
|
return { version: fallback, source: fallbackSource };
|
|
2965
3642
|
}
|
|
3643
|
+
async function validateGitCapability(sourceUrl, subPath) {
|
|
3644
|
+
const gitUrl = sourceToGitUrl(sourceUrl);
|
|
3645
|
+
const tempPath = join8(OMNI_LOCAL, "_temp", `_validate-${Date.now()}`);
|
|
3646
|
+
try {
|
|
3647
|
+
await mkdir(join8(tempPath, ".."), { recursive: true });
|
|
3648
|
+
const args = ["clone", "--depth", "1", gitUrl, tempPath];
|
|
3649
|
+
const { exitCode, stderr } = await spawnCapture("git", args, { timeout: 30000 });
|
|
3650
|
+
if (exitCode !== 0) {
|
|
3651
|
+
const stderrLower = stderr.toLowerCase();
|
|
3652
|
+
if (stderrLower.includes("not found") || stderrLower.includes("repository not found") || stderrLower.includes("does not exist")) {
|
|
3653
|
+
return {
|
|
3654
|
+
valid: false,
|
|
3655
|
+
hasCapabilityToml: false,
|
|
3656
|
+
canBeWrapped: false,
|
|
3657
|
+
error: "Repository not found"
|
|
3658
|
+
};
|
|
3659
|
+
}
|
|
3660
|
+
if (stderrLower.includes("could not resolve host")) {
|
|
3661
|
+
return {
|
|
3662
|
+
valid: false,
|
|
3663
|
+
hasCapabilityToml: false,
|
|
3664
|
+
canBeWrapped: false,
|
|
3665
|
+
error: "Could not resolve host - check your network connection"
|
|
3666
|
+
};
|
|
3667
|
+
}
|
|
3668
|
+
if (stderrLower.includes("authentication") || stderrLower.includes("permission denied")) {
|
|
3669
|
+
return {
|
|
3670
|
+
valid: false,
|
|
3671
|
+
hasCapabilityToml: false,
|
|
3672
|
+
canBeWrapped: false,
|
|
3673
|
+
error: "Authentication failed - repository may be private"
|
|
3674
|
+
};
|
|
3675
|
+
}
|
|
3676
|
+
return {
|
|
3677
|
+
valid: false,
|
|
3678
|
+
hasCapabilityToml: false,
|
|
3679
|
+
canBeWrapped: false,
|
|
3680
|
+
error: `Failed to clone repository: ${stderr.trim()}`
|
|
3681
|
+
};
|
|
3682
|
+
}
|
|
3683
|
+
const checkPath = subPath ? join8(tempPath, subPath) : tempPath;
|
|
3684
|
+
if (subPath && !existsSync12(checkPath)) {
|
|
3685
|
+
return {
|
|
3686
|
+
valid: false,
|
|
3687
|
+
hasCapabilityToml: false,
|
|
3688
|
+
canBeWrapped: false,
|
|
3689
|
+
error: `Path '${subPath}' not found in repository`
|
|
3690
|
+
};
|
|
3691
|
+
}
|
|
3692
|
+
const hasCapToml = hasCapabilityToml(checkPath);
|
|
3693
|
+
let capabilityId;
|
|
3694
|
+
if (hasCapToml) {
|
|
3695
|
+
const tomlPath = join8(checkPath, "capability.toml");
|
|
3696
|
+
try {
|
|
3697
|
+
const content = await readFile9(tomlPath, "utf-8");
|
|
3698
|
+
const parsed = parse(content);
|
|
3699
|
+
const capability = parsed["capability"];
|
|
3700
|
+
if (capability?.["id"] && typeof capability["id"] === "string") {
|
|
3701
|
+
capabilityId = capability["id"];
|
|
3702
|
+
}
|
|
3703
|
+
} catch {}
|
|
3704
|
+
const result = {
|
|
3705
|
+
valid: true,
|
|
3706
|
+
hasCapabilityToml: true,
|
|
3707
|
+
canBeWrapped: true
|
|
3708
|
+
};
|
|
3709
|
+
if (capabilityId) {
|
|
3710
|
+
result.capabilityId = capabilityId;
|
|
3711
|
+
}
|
|
3712
|
+
return result;
|
|
3713
|
+
}
|
|
3714
|
+
const canWrap = await shouldWrapDirectory(checkPath);
|
|
3715
|
+
if (!canWrap) {
|
|
3716
|
+
return {
|
|
3717
|
+
valid: false,
|
|
3718
|
+
hasCapabilityToml: false,
|
|
3719
|
+
canBeWrapped: false,
|
|
3720
|
+
error: "Repository does not contain a capability.toml and cannot be auto-wrapped (no skills, agents, commands, rules, docs, or .claude-plugin found)"
|
|
3721
|
+
};
|
|
3722
|
+
}
|
|
3723
|
+
return {
|
|
3724
|
+
valid: true,
|
|
3725
|
+
hasCapabilityToml: false,
|
|
3726
|
+
canBeWrapped: true
|
|
3727
|
+
};
|
|
3728
|
+
} finally {
|
|
3729
|
+
if (existsSync12(tempPath)) {
|
|
3730
|
+
await rm(tempPath, { recursive: true });
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
2966
3734
|
async function detectPinVersion(sourceUrl, subPath) {
|
|
2967
3735
|
const gitUrl = sourceToGitUrl(sourceUrl);
|
|
2968
|
-
const tempPath =
|
|
3736
|
+
const tempPath = join8(OMNI_LOCAL, "_temp", `_pin-detect-${Date.now()}`);
|
|
2969
3737
|
try {
|
|
2970
|
-
await
|
|
3738
|
+
await mkdir(join8(tempPath, ".."), { recursive: true });
|
|
2971
3739
|
const args = ["clone", "--depth", "1", gitUrl, tempPath];
|
|
2972
3740
|
const { exitCode, stderr } = await spawnCapture("git", args);
|
|
2973
3741
|
if (exitCode !== 0) {
|
|
2974
3742
|
throw new Error(`Failed to clone ${gitUrl}: ${stderr.trim()}`);
|
|
2975
3743
|
}
|
|
2976
3744
|
const commit = await getRepoCommit(tempPath);
|
|
2977
|
-
const checkPath = subPath ?
|
|
2978
|
-
const capTomlPath =
|
|
2979
|
-
if (
|
|
3745
|
+
const checkPath = subPath ? join8(tempPath, subPath) : tempPath;
|
|
3746
|
+
const capTomlPath = join8(checkPath, "capability.toml");
|
|
3747
|
+
if (existsSync12(capTomlPath)) {
|
|
2980
3748
|
try {
|
|
2981
3749
|
const content = await readFile9(capTomlPath, "utf-8");
|
|
2982
3750
|
const parsed = parse(content);
|
|
@@ -2986,8 +3754,8 @@ async function detectPinVersion(sourceUrl, subPath) {
|
|
|
2986
3754
|
}
|
|
2987
3755
|
} catch {}
|
|
2988
3756
|
}
|
|
2989
|
-
const pluginJsonPath =
|
|
2990
|
-
if (
|
|
3757
|
+
const pluginJsonPath = join8(checkPath, ".claude-plugin", "plugin.json");
|
|
3758
|
+
if (existsSync12(pluginJsonPath)) {
|
|
2991
3759
|
try {
|
|
2992
3760
|
const content = await readFile9(pluginJsonPath, "utf-8");
|
|
2993
3761
|
const parsed = JSON.parse(content);
|
|
@@ -2998,13 +3766,13 @@ async function detectPinVersion(sourceUrl, subPath) {
|
|
|
2998
3766
|
}
|
|
2999
3767
|
return commit;
|
|
3000
3768
|
} finally {
|
|
3001
|
-
if (
|
|
3769
|
+
if (existsSync12(tempPath)) {
|
|
3002
3770
|
await rm(tempPath, { recursive: true });
|
|
3003
3771
|
}
|
|
3004
3772
|
}
|
|
3005
3773
|
}
|
|
3006
3774
|
async function cloneRepo(gitUrl, targetPath, ref) {
|
|
3007
|
-
await
|
|
3775
|
+
await mkdir(join8(targetPath, ".."), { recursive: true });
|
|
3008
3776
|
const args = ["clone", "--depth", "1"];
|
|
3009
3777
|
if (ref) {
|
|
3010
3778
|
args.push("--branch", ref);
|
|
@@ -3016,41 +3784,62 @@ async function cloneRepo(gitUrl, targetPath, ref) {
|
|
|
3016
3784
|
}
|
|
3017
3785
|
}
|
|
3018
3786
|
async function fetchRepo(repoPath, ref) {
|
|
3019
|
-
const
|
|
3787
|
+
const fetchArgs = ["fetch", "--depth", "1", "origin"];
|
|
3788
|
+
if (ref) {
|
|
3789
|
+
fetchArgs.push(ref);
|
|
3790
|
+
}
|
|
3791
|
+
const fetchResult = await spawnCapture("git", fetchArgs, {
|
|
3020
3792
|
cwd: repoPath
|
|
3021
3793
|
});
|
|
3022
3794
|
if (fetchResult.exitCode !== 0) {
|
|
3023
3795
|
throw new Error(`Failed to fetch in ${repoPath}: ${fetchResult.stderr.trim()}`);
|
|
3024
3796
|
}
|
|
3025
3797
|
const currentCommit = await getRepoCommit(repoPath);
|
|
3026
|
-
|
|
3027
|
-
|
|
3798
|
+
let resetTarget;
|
|
3799
|
+
if (ref) {
|
|
3800
|
+
resetTarget = "FETCH_HEAD";
|
|
3801
|
+
} else {
|
|
3802
|
+
const remoteShowResult = await spawnCapture("git", ["remote", "show", "origin"], {
|
|
3803
|
+
cwd: repoPath
|
|
3804
|
+
});
|
|
3805
|
+
let defaultBranch = "main";
|
|
3806
|
+
if (remoteShowResult.exitCode === 0) {
|
|
3807
|
+
const match = remoteShowResult.stdout.match(/HEAD branch:\s*(\S+)/);
|
|
3808
|
+
if (match?.[1]) {
|
|
3809
|
+
defaultBranch = match[1];
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
resetTarget = `origin/${defaultBranch}`;
|
|
3813
|
+
}
|
|
3814
|
+
const revParseResult = await spawnCapture("git", ["rev-parse", resetTarget], {
|
|
3028
3815
|
cwd: repoPath
|
|
3029
3816
|
});
|
|
3030
|
-
if (
|
|
3031
|
-
throw new Error(`Failed to
|
|
3817
|
+
if (revParseResult.exitCode !== 0) {
|
|
3818
|
+
throw new Error(`Failed to resolve ${resetTarget} in ${repoPath}: ${revParseResult.stderr.trim()}`);
|
|
3032
3819
|
}
|
|
3033
|
-
const
|
|
3034
|
-
if (currentCommit ===
|
|
3820
|
+
const targetCommit = revParseResult.stdout.trim();
|
|
3821
|
+
if (currentCommit === targetCommit) {
|
|
3035
3822
|
return false;
|
|
3036
3823
|
}
|
|
3037
|
-
const
|
|
3038
|
-
|
|
3039
|
-
|
|
3824
|
+
const resetResult = await spawnCapture("git", ["reset", "--hard", resetTarget], {
|
|
3825
|
+
cwd: repoPath
|
|
3826
|
+
});
|
|
3827
|
+
if (resetResult.exitCode !== 0) {
|
|
3828
|
+
throw new Error(`Failed to reset in ${repoPath}: ${resetResult.stderr.trim()}`);
|
|
3040
3829
|
}
|
|
3041
3830
|
return true;
|
|
3042
3831
|
}
|
|
3043
3832
|
function hasCapabilityToml(dirPath) {
|
|
3044
|
-
return
|
|
3833
|
+
return existsSync12(join8(dirPath, "capability.toml"));
|
|
3045
3834
|
}
|
|
3046
3835
|
async function shouldWrapDirectory(dirPath) {
|
|
3047
|
-
if (
|
|
3836
|
+
if (existsSync12(join8(dirPath, ".claude-plugin", "plugin.json"))) {
|
|
3048
3837
|
return true;
|
|
3049
3838
|
}
|
|
3050
3839
|
const allDirs = [...SKILL_DIRS, ...AGENT_DIRS, ...COMMAND_DIRS, ...RULE_DIRS, ...DOC_DIRS];
|
|
3051
3840
|
for (const dirName of allDirs) {
|
|
3052
|
-
const checkPath =
|
|
3053
|
-
if (
|
|
3841
|
+
const checkPath = join8(dirPath, dirName);
|
|
3842
|
+
if (existsSync12(checkPath)) {
|
|
3054
3843
|
const stats = await stat(checkPath);
|
|
3055
3844
|
if (stats.isDirectory()) {
|
|
3056
3845
|
return true;
|
|
@@ -3061,8 +3850,8 @@ async function shouldWrapDirectory(dirPath) {
|
|
|
3061
3850
|
}
|
|
3062
3851
|
async function findMatchingDirs(basePath, names) {
|
|
3063
3852
|
for (const name of names) {
|
|
3064
|
-
const dirPath =
|
|
3065
|
-
if (
|
|
3853
|
+
const dirPath = join8(basePath, name);
|
|
3854
|
+
if (existsSync12(dirPath)) {
|
|
3066
3855
|
const stats = await stat(dirPath);
|
|
3067
3856
|
if (stats.isDirectory()) {
|
|
3068
3857
|
return dirPath;
|
|
@@ -3073,15 +3862,15 @@ async function findMatchingDirs(basePath, names) {
|
|
|
3073
3862
|
}
|
|
3074
3863
|
async function findContentItems(dirPath, filePatterns) {
|
|
3075
3864
|
const items = [];
|
|
3076
|
-
if (!
|
|
3865
|
+
if (!existsSync12(dirPath)) {
|
|
3077
3866
|
return items;
|
|
3078
3867
|
}
|
|
3079
3868
|
const entries = (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
3080
3869
|
for (const entry of entries) {
|
|
3081
|
-
const entryPath =
|
|
3870
|
+
const entryPath = join8(dirPath, entry.name);
|
|
3082
3871
|
if (entry.isDirectory()) {
|
|
3083
3872
|
for (const pattern of filePatterns) {
|
|
3084
|
-
if (
|
|
3873
|
+
if (existsSync12(join8(entryPath, pattern))) {
|
|
3085
3874
|
items.push({
|
|
3086
3875
|
name: entry.name,
|
|
3087
3876
|
path: entryPath,
|
|
@@ -3102,8 +3891,8 @@ async function findContentItems(dirPath, filePatterns) {
|
|
|
3102
3891
|
return items;
|
|
3103
3892
|
}
|
|
3104
3893
|
async function parsePluginJson(dirPath) {
|
|
3105
|
-
const pluginJsonPath =
|
|
3106
|
-
if (!
|
|
3894
|
+
const pluginJsonPath = join8(dirPath, ".claude-plugin", "plugin.json");
|
|
3895
|
+
if (!existsSync12(pluginJsonPath)) {
|
|
3107
3896
|
return null;
|
|
3108
3897
|
}
|
|
3109
3898
|
try {
|
|
@@ -3127,8 +3916,8 @@ async function parsePluginJson(dirPath) {
|
|
|
3127
3916
|
}
|
|
3128
3917
|
}
|
|
3129
3918
|
async function readReadmeDescription(dirPath) {
|
|
3130
|
-
const readmePath =
|
|
3131
|
-
if (!
|
|
3919
|
+
const readmePath = join8(dirPath, "README.md");
|
|
3920
|
+
if (!existsSync12(readmePath)) {
|
|
3132
3921
|
return null;
|
|
3133
3922
|
}
|
|
3134
3923
|
try {
|
|
@@ -3164,14 +3953,12 @@ async function normalizeFolderNames(repoPath) {
|
|
|
3164
3953
|
const renameMappings = [
|
|
3165
3954
|
{ from: "skill", to: "skills" },
|
|
3166
3955
|
{ from: "command", to: "commands" },
|
|
3167
|
-
{ from: "rule", to: "rules" }
|
|
3168
|
-
{ from: "agent", to: "agents" },
|
|
3169
|
-
{ from: "subagent", to: "subagents" }
|
|
3956
|
+
{ from: "rule", to: "rules" }
|
|
3170
3957
|
];
|
|
3171
3958
|
for (const { from, to } of renameMappings) {
|
|
3172
|
-
const fromPath =
|
|
3173
|
-
const toPath =
|
|
3174
|
-
if (
|
|
3959
|
+
const fromPath = join8(repoPath, from);
|
|
3960
|
+
const toPath = join8(repoPath, to);
|
|
3961
|
+
if (existsSync12(fromPath) && !existsSync12(toPath)) {
|
|
3175
3962
|
try {
|
|
3176
3963
|
const stats = await stat(fromPath);
|
|
3177
3964
|
if (stats.isDirectory()) {
|
|
@@ -3236,31 +4023,31 @@ async function generateCapabilityToml(id, repoPath, source, commit, content) {
|
|
|
3236
4023
|
# This capability was wrapped from an external repository
|
|
3237
4024
|
|
|
3238
4025
|
[capability]
|
|
3239
|
-
id = "${id}"
|
|
3240
|
-
name = "${name}"
|
|
3241
|
-
version = "${version}"
|
|
3242
|
-
description = "${description}"
|
|
4026
|
+
id = "${escapeTomlString(id)}"
|
|
4027
|
+
name = "${escapeTomlString(name)}"
|
|
4028
|
+
version = "${escapeTomlString(version)}"
|
|
4029
|
+
description = "${escapeTomlString(description)}"
|
|
3243
4030
|
`;
|
|
3244
4031
|
if (pluginMeta?.author?.name || pluginMeta?.author?.email) {
|
|
3245
4032
|
tomlContent += `
|
|
3246
4033
|
[capability.author]
|
|
3247
4034
|
`;
|
|
3248
4035
|
if (pluginMeta.author.name) {
|
|
3249
|
-
tomlContent += `name = "${pluginMeta.author.name}"
|
|
4036
|
+
tomlContent += `name = "${escapeTomlString(pluginMeta.author.name)}"
|
|
3250
4037
|
`;
|
|
3251
4038
|
}
|
|
3252
4039
|
if (pluginMeta.author.email) {
|
|
3253
|
-
tomlContent += `email = "${pluginMeta.author.email}"
|
|
4040
|
+
tomlContent += `email = "${escapeTomlString(pluginMeta.author.email)}"
|
|
3254
4041
|
`;
|
|
3255
4042
|
}
|
|
3256
4043
|
}
|
|
3257
4044
|
tomlContent += `
|
|
3258
4045
|
[capability.metadata]
|
|
3259
|
-
repository = "${repoUrl}"
|
|
4046
|
+
repository = "${escapeTomlString(repoUrl)}"
|
|
3260
4047
|
wrapped = true
|
|
3261
4048
|
commit = "${commit}"
|
|
3262
4049
|
`;
|
|
3263
|
-
await
|
|
4050
|
+
await writeFile3(join8(repoPath, "capability.toml"), tomlContent, "utf-8");
|
|
3264
4051
|
}
|
|
3265
4052
|
async function fetchGitCapabilitySource(id, config, options) {
|
|
3266
4053
|
const gitUrl = sourceToGitUrl(config.source);
|
|
@@ -3270,29 +4057,29 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
3270
4057
|
let repoPath;
|
|
3271
4058
|
const gitRef = config.version && config.version !== "latest" ? config.version : undefined;
|
|
3272
4059
|
if (config.path) {
|
|
3273
|
-
const tempPath =
|
|
3274
|
-
if (
|
|
4060
|
+
const tempPath = join8(OMNI_LOCAL, "_temp", `${id}-repo`);
|
|
4061
|
+
if (existsSync12(join8(tempPath, ".git"))) {
|
|
3275
4062
|
updated = await fetchRepo(tempPath, gitRef);
|
|
3276
4063
|
commit = await getRepoCommit(tempPath);
|
|
3277
4064
|
} else {
|
|
3278
|
-
await
|
|
4065
|
+
await mkdir(join8(tempPath, ".."), { recursive: true });
|
|
3279
4066
|
await cloneRepo(gitUrl, tempPath, gitRef);
|
|
3280
4067
|
commit = await getRepoCommit(tempPath);
|
|
3281
4068
|
updated = true;
|
|
3282
4069
|
}
|
|
3283
|
-
const sourcePath =
|
|
3284
|
-
if (!
|
|
4070
|
+
const sourcePath = join8(tempPath, config.path);
|
|
4071
|
+
if (!existsSync12(sourcePath)) {
|
|
3285
4072
|
throw new Error(`Path not found in repository: ${config.path}`);
|
|
3286
4073
|
}
|
|
3287
|
-
if (
|
|
4074
|
+
if (existsSync12(targetPath)) {
|
|
3288
4075
|
await rm(targetPath, { recursive: true });
|
|
3289
4076
|
}
|
|
3290
|
-
await
|
|
4077
|
+
await mkdir(join8(targetPath, ".."), { recursive: true });
|
|
3291
4078
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
3292
4079
|
await rm(tempPath, { recursive: true });
|
|
3293
4080
|
repoPath = targetPath;
|
|
3294
4081
|
} else {
|
|
3295
|
-
if (
|
|
4082
|
+
if (existsSync12(join8(targetPath, ".git"))) {
|
|
3296
4083
|
if (!options?.silent) {
|
|
3297
4084
|
console.log(` Checking ${id}...`);
|
|
3298
4085
|
}
|
|
@@ -3343,7 +4130,7 @@ async function fetchGitCapabilitySource(id, config, options) {
|
|
|
3343
4130
|
async function fetchFileCapabilitySource(id, config, options) {
|
|
3344
4131
|
const sourcePath = parseFileSourcePath(config.source);
|
|
3345
4132
|
const targetPath = getSourceCapabilityPath(id);
|
|
3346
|
-
if (!
|
|
4133
|
+
if (!existsSync12(sourcePath)) {
|
|
3347
4134
|
throw new Error(`File source not found: ${sourcePath}`);
|
|
3348
4135
|
}
|
|
3349
4136
|
const sourceStats = await stat(sourcePath);
|
|
@@ -3351,7 +4138,7 @@ async function fetchFileCapabilitySource(id, config, options) {
|
|
|
3351
4138
|
throw new Error(`File source must be a directory: ${sourcePath}`);
|
|
3352
4139
|
}
|
|
3353
4140
|
const contentHash = await computeContentHash(sourcePath);
|
|
3354
|
-
const hasCapToml =
|
|
4141
|
+
const hasCapToml = existsSync12(join8(sourcePath, "capability.toml"));
|
|
3355
4142
|
let needsWrap = false;
|
|
3356
4143
|
if (!hasCapToml) {
|
|
3357
4144
|
needsWrap = await shouldWrapDirectory(sourcePath);
|
|
@@ -3362,10 +4149,10 @@ async function fetchFileCapabilitySource(id, config, options) {
|
|
|
3362
4149
|
if (!options?.silent) {
|
|
3363
4150
|
console.log(` Copying ${id} from ${sourcePath}...`);
|
|
3364
4151
|
}
|
|
3365
|
-
if (
|
|
4152
|
+
if (existsSync12(targetPath)) {
|
|
3366
4153
|
await rm(targetPath, { recursive: true });
|
|
3367
4154
|
}
|
|
3368
|
-
await
|
|
4155
|
+
await mkdir(join8(targetPath, ".."), { recursive: true });
|
|
3369
4156
|
await cp(sourcePath, targetPath, { recursive: true });
|
|
3370
4157
|
if (needsWrap) {
|
|
3371
4158
|
await normalizeFolderNames(targetPath);
|
|
@@ -3422,30 +4209,30 @@ async function generateFileSourceCapabilityToml(id, source, hashVersion, content
|
|
|
3422
4209
|
# This capability was wrapped from a local directory
|
|
3423
4210
|
|
|
3424
4211
|
[capability]
|
|
3425
|
-
id = "${id}"
|
|
3426
|
-
name = "${name}"
|
|
3427
|
-
version = "${version}"
|
|
3428
|
-
description = "${description}"
|
|
4212
|
+
id = "${escapeTomlString(id)}"
|
|
4213
|
+
name = "${escapeTomlString(name)}"
|
|
4214
|
+
version = "${escapeTomlString(version)}"
|
|
4215
|
+
description = "${escapeTomlString(description)}"
|
|
3429
4216
|
`;
|
|
3430
4217
|
if (pluginMeta?.author?.name || pluginMeta?.author?.email) {
|
|
3431
4218
|
tomlContent += `
|
|
3432
4219
|
[capability.author]
|
|
3433
4220
|
`;
|
|
3434
4221
|
if (pluginMeta.author.name) {
|
|
3435
|
-
tomlContent += `name = "${pluginMeta.author.name}"
|
|
4222
|
+
tomlContent += `name = "${escapeTomlString(pluginMeta.author.name)}"
|
|
3436
4223
|
`;
|
|
3437
4224
|
}
|
|
3438
4225
|
if (pluginMeta.author.email) {
|
|
3439
|
-
tomlContent += `email = "${pluginMeta.author.email}"
|
|
4226
|
+
tomlContent += `email = "${escapeTomlString(pluginMeta.author.email)}"
|
|
3440
4227
|
`;
|
|
3441
4228
|
}
|
|
3442
4229
|
}
|
|
3443
4230
|
tomlContent += `
|
|
3444
4231
|
[capability.metadata]
|
|
3445
4232
|
wrapped = true
|
|
3446
|
-
source = "${source}"
|
|
4233
|
+
source = "${escapeTomlString(source)}"
|
|
3447
4234
|
`;
|
|
3448
|
-
await
|
|
4235
|
+
await writeFile3(join8(targetPath, "capability.toml"), tomlContent, "utf-8");
|
|
3449
4236
|
}
|
|
3450
4237
|
async function fetchCapabilitySource(id, sourceConfig, options) {
|
|
3451
4238
|
const config = parseSourceConfig(sourceConfig);
|
|
@@ -3518,11 +4305,11 @@ generated_from_omni_toml = true
|
|
|
3518
4305
|
}
|
|
3519
4306
|
async function generateMcpCapabilityToml(id, mcpConfig, targetPath) {
|
|
3520
4307
|
const tomlContent = generateMcpCapabilityTomlContent(id, mcpConfig);
|
|
3521
|
-
await
|
|
4308
|
+
await writeFile3(join8(targetPath, "capability.toml"), tomlContent, "utf-8");
|
|
3522
4309
|
}
|
|
3523
4310
|
async function isGeneratedMcpCapability(capabilityDir) {
|
|
3524
|
-
const tomlPath =
|
|
3525
|
-
if (!
|
|
4311
|
+
const tomlPath = join8(capabilityDir, "capability.toml");
|
|
4312
|
+
if (!existsSync12(tomlPath)) {
|
|
3526
4313
|
console.warn("no capability.toml found in", capabilityDir);
|
|
3527
4314
|
return false;
|
|
3528
4315
|
}
|
|
@@ -3537,14 +4324,14 @@ async function isGeneratedMcpCapability(capabilityDir) {
|
|
|
3537
4324
|
}
|
|
3538
4325
|
}
|
|
3539
4326
|
async function cleanupStaleMcpCapabilities(currentMcpIds) {
|
|
3540
|
-
const capabilitiesDir =
|
|
3541
|
-
if (!
|
|
4327
|
+
const capabilitiesDir = join8(OMNI_LOCAL, "capabilities");
|
|
4328
|
+
if (!existsSync12(capabilitiesDir)) {
|
|
3542
4329
|
return;
|
|
3543
4330
|
}
|
|
3544
4331
|
const entries = await readdir(capabilitiesDir, { withFileTypes: true });
|
|
3545
4332
|
for (const entry of entries) {
|
|
3546
4333
|
if (entry.isDirectory()) {
|
|
3547
|
-
const capDir =
|
|
4334
|
+
const capDir = join8(capabilitiesDir, entry.name);
|
|
3548
4335
|
const isGenerated = await isGeneratedMcpCapability(capDir);
|
|
3549
4336
|
if (isGenerated && !currentMcpIds.has(entry.name)) {
|
|
3550
4337
|
await rm(capDir, { recursive: true });
|
|
@@ -3557,20 +4344,20 @@ async function generateMcpCapabilities(config) {
|
|
|
3557
4344
|
await cleanupStaleMcpCapabilities(new Set);
|
|
3558
4345
|
return;
|
|
3559
4346
|
}
|
|
3560
|
-
const mcpCapabilitiesDir =
|
|
4347
|
+
const mcpCapabilitiesDir = join8(OMNI_LOCAL, "capabilities");
|
|
3561
4348
|
const currentMcpIds = new Set;
|
|
3562
4349
|
for (const [id, mcpConfig] of Object.entries(config.mcps)) {
|
|
3563
|
-
const targetPath =
|
|
4350
|
+
const targetPath = join8(mcpCapabilitiesDir, id);
|
|
3564
4351
|
currentMcpIds.add(id);
|
|
3565
|
-
await
|
|
4352
|
+
await mkdir(targetPath, { recursive: true });
|
|
3566
4353
|
await generateMcpCapabilityToml(id, mcpConfig, targetPath);
|
|
3567
4354
|
}
|
|
3568
4355
|
await cleanupStaleMcpCapabilities(currentMcpIds);
|
|
3569
4356
|
}
|
|
3570
4357
|
async function fetchAllCapabilitySources(config, options) {
|
|
3571
4358
|
await generateMcpCapabilities(config);
|
|
3572
|
-
const tempDir =
|
|
3573
|
-
if (
|
|
4359
|
+
const tempDir = join8(OMNI_LOCAL, "_temp");
|
|
4360
|
+
if (existsSync12(tempDir)) {
|
|
3574
4361
|
await rm(tempDir, { recursive: true });
|
|
3575
4362
|
}
|
|
3576
4363
|
const sources = config.capabilities?.sources;
|
|
@@ -3656,7 +4443,7 @@ async function checkForUpdates(config) {
|
|
|
3656
4443
|
continue;
|
|
3657
4444
|
}
|
|
3658
4445
|
const gitConfig = sourceConfig;
|
|
3659
|
-
if (!
|
|
4446
|
+
if (!existsSync12(join8(targetPath, ".git"))) {
|
|
3660
4447
|
updates.push({
|
|
3661
4448
|
id,
|
|
3662
4449
|
source: gitConfig.source,
|
|
@@ -3699,12 +4486,12 @@ function checkVersionMismatch(lockEntry, currentCommit, currentVersion) {
|
|
|
3699
4486
|
}
|
|
3700
4487
|
async function verifyIntegrity(id, lockEntry) {
|
|
3701
4488
|
const capabilityPath = getSourceCapabilityPath(id);
|
|
3702
|
-
if (!
|
|
4489
|
+
if (!existsSync12(capabilityPath)) {
|
|
3703
4490
|
return "capability directory missing";
|
|
3704
4491
|
}
|
|
3705
4492
|
if (lockEntry.commit) {
|
|
3706
|
-
const gitDir =
|
|
3707
|
-
if (
|
|
4493
|
+
const gitDir = join8(capabilityPath, ".git");
|
|
4494
|
+
if (existsSync12(gitDir)) {
|
|
3708
4495
|
try {
|
|
3709
4496
|
const currentCommit = await getRepoCommit(capabilityPath);
|
|
3710
4497
|
if (currentCommit !== lockEntry.commit) {
|
|
@@ -3769,13 +4556,14 @@ var init_hooks = __esm(() => {
|
|
|
3769
4556
|
init_variables();
|
|
3770
4557
|
init_loader();
|
|
3771
4558
|
init_merger();
|
|
4559
|
+
init_json_loader();
|
|
3772
4560
|
});
|
|
3773
4561
|
|
|
3774
4562
|
// ../core/src/config/provider.ts
|
|
3775
|
-
import { existsSync as
|
|
3776
|
-
import { readFile as readFile10, writeFile as
|
|
4563
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
4564
|
+
import { readFile as readFile10, writeFile as writeFile4 } from "node:fs/promises";
|
|
3777
4565
|
async function loadProviderConfig() {
|
|
3778
|
-
if (!
|
|
4566
|
+
if (!existsSync13(PROVIDER_CONFIG_PATH)) {
|
|
3779
4567
|
return { provider: "claude" };
|
|
3780
4568
|
}
|
|
3781
4569
|
const content = await readFile10(PROVIDER_CONFIG_PATH, "utf-8");
|
|
@@ -3805,7 +4593,7 @@ async function writeProviderConfig(config) {
|
|
|
3805
4593
|
lines.push("# Default: Claude");
|
|
3806
4594
|
lines.push('provider = "claude"');
|
|
3807
4595
|
}
|
|
3808
|
-
await
|
|
4596
|
+
await writeFile4(PROVIDER_CONFIG_PATH, `${lines.join(`
|
|
3809
4597
|
`)}
|
|
3810
4598
|
`, "utf-8");
|
|
3811
4599
|
}
|
|
@@ -3825,16 +4613,16 @@ var init_provider = __esm(() => {
|
|
|
3825
4613
|
});
|
|
3826
4614
|
|
|
3827
4615
|
// ../core/src/config/toml-patcher.ts
|
|
3828
|
-
import { existsSync as
|
|
3829
|
-
import { readFile as readFile11, writeFile as
|
|
4616
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
4617
|
+
import { readFile as readFile11, writeFile as writeFile5 } from "node:fs/promises";
|
|
3830
4618
|
async function readConfigFile() {
|
|
3831
|
-
if (!
|
|
4619
|
+
if (!existsSync14(CONFIG_PATH2)) {
|
|
3832
4620
|
return "";
|
|
3833
4621
|
}
|
|
3834
4622
|
return readFile11(CONFIG_PATH2, "utf-8");
|
|
3835
4623
|
}
|
|
3836
4624
|
async function writeConfigFile(content) {
|
|
3837
|
-
await
|
|
4625
|
+
await writeFile5(CONFIG_PATH2, content, "utf-8");
|
|
3838
4626
|
}
|
|
3839
4627
|
function findSection(lines, sectionPattern) {
|
|
3840
4628
|
return lines.findIndex((line) => sectionPattern.test(line.trim()));
|
|
@@ -4053,10 +4841,10 @@ var init_config2 = __esm(() => {
|
|
|
4053
4841
|
});
|
|
4054
4842
|
|
|
4055
4843
|
// ../core/src/mcp-json/manager.ts
|
|
4056
|
-
import { existsSync as
|
|
4057
|
-
import { readFile as readFile12, writeFile as
|
|
4844
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
4845
|
+
import { readFile as readFile12, writeFile as writeFile6 } from "node:fs/promises";
|
|
4058
4846
|
async function readMcpJson() {
|
|
4059
|
-
if (!
|
|
4847
|
+
if (!existsSync15(MCP_JSON_PATH)) {
|
|
4060
4848
|
return { mcpServers: {} };
|
|
4061
4849
|
}
|
|
4062
4850
|
try {
|
|
@@ -4070,7 +4858,7 @@ async function readMcpJson() {
|
|
|
4070
4858
|
}
|
|
4071
4859
|
}
|
|
4072
4860
|
async function writeMcpJson(config2) {
|
|
4073
|
-
await
|
|
4861
|
+
await writeFile6(MCP_JSON_PATH, `${JSON.stringify(config2, null, 2)}
|
|
4074
4862
|
`, "utf-8");
|
|
4075
4863
|
}
|
|
4076
4864
|
function buildMcpServerConfig(mcp) {
|
|
@@ -4142,10 +4930,10 @@ var init_mcp_json = __esm(() => {
|
|
|
4142
4930
|
});
|
|
4143
4931
|
|
|
4144
4932
|
// ../core/src/state/manifest.ts
|
|
4145
|
-
import { existsSync as
|
|
4146
|
-
import { readFile as readFile13, writeFile as
|
|
4933
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync2, rmSync } from "node:fs";
|
|
4934
|
+
import { readFile as readFile13, writeFile as writeFile7 } from "node:fs/promises";
|
|
4147
4935
|
async function loadManifest() {
|
|
4148
|
-
if (!
|
|
4936
|
+
if (!existsSync16(MANIFEST_PATH)) {
|
|
4149
4937
|
return {
|
|
4150
4938
|
version: CURRENT_VERSION,
|
|
4151
4939
|
syncedAt: new Date().toISOString(),
|
|
@@ -4157,7 +4945,7 @@ async function loadManifest() {
|
|
|
4157
4945
|
}
|
|
4158
4946
|
async function saveManifest(manifest) {
|
|
4159
4947
|
mkdirSync2(".omni/state", { recursive: true });
|
|
4160
|
-
await
|
|
4948
|
+
await writeFile7(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
|
|
4161
4949
|
`, "utf-8");
|
|
4162
4950
|
}
|
|
4163
4951
|
function buildManifestFromCapabilities(capabilities2) {
|
|
@@ -4192,14 +4980,14 @@ async function cleanupStaleResources(previousManifest, currentCapabilityIds) {
|
|
|
4192
4980
|
}
|
|
4193
4981
|
for (const skillName of resources.skills) {
|
|
4194
4982
|
const skillDir = `.claude/skills/${skillName}`;
|
|
4195
|
-
if (
|
|
4983
|
+
if (existsSync16(skillDir)) {
|
|
4196
4984
|
rmSync(skillDir, { recursive: true });
|
|
4197
4985
|
result.deletedSkills.push(skillName);
|
|
4198
4986
|
}
|
|
4199
4987
|
}
|
|
4200
4988
|
for (const ruleName of resources.rules) {
|
|
4201
4989
|
const rulePath = `.cursor/rules/omnidev-${ruleName}.mdc`;
|
|
4202
|
-
if (
|
|
4990
|
+
if (existsSync16(rulePath)) {
|
|
4203
4991
|
rmSync(rulePath);
|
|
4204
4992
|
result.deletedRules.push(ruleName);
|
|
4205
4993
|
}
|
|
@@ -4211,10 +4999,10 @@ var MANIFEST_PATH = ".omni/state/manifest.json", CURRENT_VERSION = 1;
|
|
|
4211
4999
|
var init_manifest = () => {};
|
|
4212
5000
|
|
|
4213
5001
|
// ../core/src/state/providers.ts
|
|
4214
|
-
import { existsSync as
|
|
4215
|
-
import { readFile as readFile14, writeFile as
|
|
5002
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync3 } from "node:fs";
|
|
5003
|
+
import { readFile as readFile14, writeFile as writeFile8 } from "node:fs/promises";
|
|
4216
5004
|
async function readEnabledProviders() {
|
|
4217
|
-
if (!
|
|
5005
|
+
if (!existsSync17(PROVIDERS_PATH)) {
|
|
4218
5006
|
return DEFAULT_PROVIDERS;
|
|
4219
5007
|
}
|
|
4220
5008
|
try {
|
|
@@ -4228,7 +5016,7 @@ async function readEnabledProviders() {
|
|
|
4228
5016
|
async function writeEnabledProviders(providers) {
|
|
4229
5017
|
mkdirSync3(STATE_DIR2, { recursive: true });
|
|
4230
5018
|
const state = { enabled: providers };
|
|
4231
|
-
await
|
|
5019
|
+
await writeFile8(PROVIDERS_PATH, `${JSON.stringify(state, null, 2)}
|
|
4232
5020
|
`, "utf-8");
|
|
4233
5021
|
}
|
|
4234
5022
|
async function enableProvider(providerId) {
|
|
@@ -4253,10 +5041,10 @@ var init_providers = __esm(() => {
|
|
|
4253
5041
|
});
|
|
4254
5042
|
|
|
4255
5043
|
// ../core/src/state/security-allows.ts
|
|
4256
|
-
import { existsSync as
|
|
4257
|
-
import { readFile as readFile15, writeFile as
|
|
5044
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync4 } from "node:fs";
|
|
5045
|
+
import { readFile as readFile15, writeFile as writeFile9 } from "node:fs/promises";
|
|
4258
5046
|
async function readSecurityAllows() {
|
|
4259
|
-
if (!
|
|
5047
|
+
if (!existsSync18(SECURITY_PATH)) {
|
|
4260
5048
|
return { ...DEFAULT_STATE };
|
|
4261
5049
|
}
|
|
4262
5050
|
try {
|
|
@@ -4270,7 +5058,7 @@ async function readSecurityAllows() {
|
|
|
4270
5058
|
async function writeSecurityAllows(state) {
|
|
4271
5059
|
mkdirSync4(OMNI_DIR, { recursive: true });
|
|
4272
5060
|
state.modifiedAt = new Date().toISOString();
|
|
4273
|
-
await
|
|
5061
|
+
await writeFile9(SECURITY_PATH, `${JSON.stringify(state, null, 2)}
|
|
4274
5062
|
`, "utf-8");
|
|
4275
5063
|
}
|
|
4276
5064
|
async function addSecurityAllow(capabilityId, findingType) {
|
|
@@ -4357,10 +5145,11 @@ var init_state = __esm(() => {
|
|
|
4357
5145
|
import { spawn as spawn2 } from "node:child_process";
|
|
4358
5146
|
import { mkdirSync as mkdirSync5 } from "node:fs";
|
|
4359
5147
|
async function installCapabilityDependencies(silent) {
|
|
4360
|
-
const { existsSync:
|
|
4361
|
-
const { join:
|
|
5148
|
+
const { existsSync: existsSync19, readdirSync: readdirSync7, readFileSync: readFileSync3 } = await import("node:fs");
|
|
5149
|
+
const { join: join9 } = await import("node:path");
|
|
5150
|
+
const { parse: parse2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
|
|
4362
5151
|
const capabilitiesDir = ".omni/capabilities";
|
|
4363
|
-
if (!
|
|
5152
|
+
if (!existsSync19(capabilitiesDir)) {
|
|
4364
5153
|
return;
|
|
4365
5154
|
}
|
|
4366
5155
|
const entries = readdirSync7(capabilitiesDir, { withFileTypes: true });
|
|
@@ -4380,41 +5169,50 @@ async function installCapabilityDependencies(silent) {
|
|
|
4380
5169
|
if (!entry.isDirectory()) {
|
|
4381
5170
|
continue;
|
|
4382
5171
|
}
|
|
4383
|
-
const capabilityPath =
|
|
4384
|
-
const packageJsonPath =
|
|
4385
|
-
|
|
5172
|
+
const capabilityPath = join9(capabilitiesDir, entry.name);
|
|
5173
|
+
const packageJsonPath = join9(capabilityPath, "package.json");
|
|
5174
|
+
const capabilityTomlPath = join9(capabilityPath, "capability.toml");
|
|
5175
|
+
if (!existsSync19(packageJsonPath)) {
|
|
4386
5176
|
continue;
|
|
4387
5177
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
stdio: "pipe"
|
|
4395
|
-
});
|
|
4396
|
-
let stderr = "";
|
|
4397
|
-
proc.stderr?.on("data", (data) => {
|
|
4398
|
-
stderr += data.toString();
|
|
4399
|
-
});
|
|
4400
|
-
proc.on("close", (code) => {
|
|
4401
|
-
if (code === 0) {
|
|
4402
|
-
resolve2();
|
|
4403
|
-
} else {
|
|
4404
|
-
reject(new Error(`Failed to install dependencies for ${capabilityPath}:
|
|
4405
|
-
${stderr}`));
|
|
5178
|
+
if (existsSync19(capabilityTomlPath)) {
|
|
5179
|
+
try {
|
|
5180
|
+
const tomlContent = readFileSync3(capabilityTomlPath, "utf-8");
|
|
5181
|
+
const parsed = parse2(tomlContent);
|
|
5182
|
+
if (parsed.capability?.metadata?.wrapped === true) {
|
|
5183
|
+
continue;
|
|
4406
5184
|
}
|
|
5185
|
+
} catch {}
|
|
5186
|
+
}
|
|
5187
|
+
try {
|
|
5188
|
+
await new Promise((resolve2, reject) => {
|
|
5189
|
+
const useNpmCi = hasNpm && existsSync19(join9(capabilityPath, "package-lock.json"));
|
|
5190
|
+
const cmd = hasBun ? "bun" : "npm";
|
|
5191
|
+
const args = hasBun ? ["install"] : useNpmCi ? ["ci"] : ["install"];
|
|
5192
|
+
const proc = spawn2(cmd, args, {
|
|
5193
|
+
cwd: capabilityPath,
|
|
5194
|
+
stdio: "pipe"
|
|
5195
|
+
});
|
|
5196
|
+
let stderr = "";
|
|
5197
|
+
proc.stderr?.on("data", (data) => {
|
|
5198
|
+
stderr += data.toString();
|
|
5199
|
+
});
|
|
5200
|
+
proc.on("close", (code) => {
|
|
5201
|
+
if (code === 0) {
|
|
5202
|
+
resolve2();
|
|
5203
|
+
} else {
|
|
5204
|
+
reject(new Error(`Failed to install dependencies for ${capabilityPath}:
|
|
5205
|
+
${stderr}`));
|
|
5206
|
+
}
|
|
5207
|
+
});
|
|
5208
|
+
proc.on("error", (error) => {
|
|
5209
|
+
reject(error);
|
|
5210
|
+
});
|
|
4407
5211
|
});
|
|
4408
|
-
|
|
4409
|
-
reject(error);
|
|
4410
|
-
});
|
|
4411
|
-
});
|
|
4412
|
-
const hasIndexTs = existsSync18(join10(capabilityPath, "index.ts"));
|
|
4413
|
-
const hasBuiltIndex = existsSync18(join10(capabilityPath, "dist", "index.js"));
|
|
4414
|
-
if (hasIndexTs && !hasBuiltIndex) {
|
|
5212
|
+
const hasIndexTs = existsSync19(join9(capabilityPath, "index.ts"));
|
|
4415
5213
|
let hasBuildScript = false;
|
|
4416
5214
|
try {
|
|
4417
|
-
const pkgJson = JSON.parse(
|
|
5215
|
+
const pkgJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
4418
5216
|
hasBuildScript = Boolean(pkgJson.scripts?.build);
|
|
4419
5217
|
} catch {}
|
|
4420
5218
|
if (hasBuildScript) {
|
|
@@ -4441,10 +5239,16 @@ ${stderr}`));
|
|
|
4441
5239
|
reject(error);
|
|
4442
5240
|
});
|
|
4443
5241
|
});
|
|
4444
|
-
} else if (!silent) {
|
|
4445
|
-
|
|
5242
|
+
} else if (hasIndexTs && !silent) {
|
|
5243
|
+
const hasBuiltIndex = existsSync19(join9(capabilityPath, "dist", "index.js"));
|
|
5244
|
+
if (!hasBuiltIndex) {
|
|
5245
|
+
console.warn(`Warning: Capability at ${capabilityPath} has index.ts but no build script.
|
|
4446
5246
|
Add a "build" script to package.json (e.g., "build": "tsc") to compile TypeScript.`);
|
|
5247
|
+
}
|
|
4447
5248
|
}
|
|
5249
|
+
} catch (error) {
|
|
5250
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5251
|
+
console.warn(`Warning: ${errorMessage}`);
|
|
4448
5252
|
}
|
|
4449
5253
|
}
|
|
4450
5254
|
}
|
|
@@ -4579,9 +5383,9 @@ var init_types3 = __esm(() => {
|
|
|
4579
5383
|
});
|
|
4580
5384
|
|
|
4581
5385
|
// ../core/src/security/scanner.ts
|
|
4582
|
-
import { existsSync as
|
|
5386
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
4583
5387
|
import { lstat, readdir as readdir2, readFile as readFile16, readlink, realpath } from "node:fs/promises";
|
|
4584
|
-
import { join as
|
|
5388
|
+
import { join as join9, relative, resolve as resolve2 } from "node:path";
|
|
4585
5389
|
async function scanFileForUnicode(filePath, relativePath) {
|
|
4586
5390
|
const findings = [];
|
|
4587
5391
|
try {
|
|
@@ -4665,7 +5469,7 @@ async function scanFileForScripts(filePath, relativePath) {
|
|
|
4665
5469
|
async function checkSymlink(symlinkPath, relativePath, capabilityRoot) {
|
|
4666
5470
|
try {
|
|
4667
5471
|
const linkTarget = await readlink(symlinkPath);
|
|
4668
|
-
const resolvedTarget = resolve2(
|
|
5472
|
+
const resolvedTarget = resolve2(join9(symlinkPath, "..", linkTarget));
|
|
4669
5473
|
const normalizedRoot = await realpath(capabilityRoot);
|
|
4670
5474
|
if (linkTarget.startsWith("/")) {
|
|
4671
5475
|
return {
|
|
@@ -4700,7 +5504,7 @@ function isTextFile(filePath) {
|
|
|
4700
5504
|
async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_SCAN_SETTINGS) {
|
|
4701
5505
|
const startTime = Date.now();
|
|
4702
5506
|
const findings = [];
|
|
4703
|
-
if (!
|
|
5507
|
+
if (!existsSync19(capabilityPath)) {
|
|
4704
5508
|
return {
|
|
4705
5509
|
capabilityId,
|
|
4706
5510
|
path: capabilityPath,
|
|
@@ -4712,7 +5516,7 @@ async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_S
|
|
|
4712
5516
|
async function scanDirectory(dirPath) {
|
|
4713
5517
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
4714
5518
|
for (const entry of entries) {
|
|
4715
|
-
const fullPath =
|
|
5519
|
+
const fullPath = join9(dirPath, entry.name);
|
|
4716
5520
|
const relativePath = relative(capabilityPath, fullPath);
|
|
4717
5521
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "__pycache__") {
|
|
4718
5522
|
continue;
|
|
@@ -5003,13 +5807,16 @@ function generateAgentsTemplate() {
|
|
|
5003
5807
|
}
|
|
5004
5808
|
|
|
5005
5809
|
// ../core/src/templates/capability.ts
|
|
5810
|
+
function escapeTomlString2(value) {
|
|
5811
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
5812
|
+
}
|
|
5006
5813
|
function generateCapabilityToml2(options) {
|
|
5007
5814
|
const description = options.description || "TODO: Add a description for your capability";
|
|
5008
5815
|
return `[capability]
|
|
5009
|
-
id = "${options.id}"
|
|
5010
|
-
name = "${options.name}"
|
|
5816
|
+
id = "${escapeTomlString2(options.id)}"
|
|
5817
|
+
name = "${escapeTomlString2(options.name)}"
|
|
5011
5818
|
version = "0.1.0"
|
|
5012
|
-
description = "${description}"
|
|
5819
|
+
description = "${escapeTomlString2(description)}"
|
|
5013
5820
|
|
|
5014
5821
|
# Optional author information
|
|
5015
5822
|
# [capability.author]
|
|
@@ -5172,6 +5979,7 @@ __export(exports_src, {
|
|
|
5172
5979
|
verifyIntegrity: () => verifyIntegrity,
|
|
5173
5980
|
validateHooksConfig: () => validateHooksConfig,
|
|
5174
5981
|
validateHook: () => validateHook,
|
|
5982
|
+
validateGitCapability: () => validateGitCapability,
|
|
5175
5983
|
transformToOmnidev: () => transformToOmnidev,
|
|
5176
5984
|
transformToClaude: () => transformToClaude,
|
|
5177
5985
|
transformHooksConfig: () => transformHooksConfig,
|
|
@@ -5185,6 +5993,8 @@ __export(exports_src, {
|
|
|
5185
5993
|
saveManifest: () => saveManifest,
|
|
5186
5994
|
saveLockFile: () => saveLockFile,
|
|
5187
5995
|
resolveEnabledCapabilities: () => resolveEnabledCapabilities,
|
|
5996
|
+
resolveCapabilityRootInConfig: () => resolveCapabilityRootInConfig,
|
|
5997
|
+
resolveCapabilityRoot: () => resolveCapabilityRoot,
|
|
5188
5998
|
removeSecurityAllow: () => removeSecurityAllow,
|
|
5189
5999
|
readSecurityAllows: () => readSecurityAllows,
|
|
5190
6000
|
readMcpJson: () => readMcpJson,
|
|
@@ -5208,6 +6018,7 @@ __export(exports_src, {
|
|
|
5208
6018
|
loadProfileConfig: () => loadProfileConfig,
|
|
5209
6019
|
loadManifest: () => loadManifest,
|
|
5210
6020
|
loadLockFile: () => loadLockFile,
|
|
6021
|
+
loadHooksJson: () => loadHooksJson,
|
|
5211
6022
|
loadHooksFromCapability: () => loadHooksFromCapability,
|
|
5212
6023
|
loadDocs: () => loadDocs,
|
|
5213
6024
|
loadConfig: () => loadConfig,
|
|
@@ -5290,7 +6101,8 @@ __export(exports_src, {
|
|
|
5290
6101
|
DEFAULT_SCAN_SETTINGS: () => DEFAULT_SCAN_SETTINGS,
|
|
5291
6102
|
DEFAULT_PROMPT_TIMEOUT: () => DEFAULT_PROMPT_TIMEOUT,
|
|
5292
6103
|
DEFAULT_COMMAND_TIMEOUT: () => DEFAULT_COMMAND_TIMEOUT,
|
|
5293
|
-
COMMON_TOOL_MATCHERS: () => COMMON_TOOL_MATCHERS
|
|
6104
|
+
COMMON_TOOL_MATCHERS: () => COMMON_TOOL_MATCHERS,
|
|
6105
|
+
CLAUDE_HOOKS_CONFIG_FILENAME: () => CLAUDE_HOOKS_CONFIG_FILENAME
|
|
5294
6106
|
});
|
|
5295
6107
|
function getVersion() {
|
|
5296
6108
|
return version;
|
|
@@ -5311,51 +6123,59 @@ var init_src = __esm(() => {
|
|
|
5311
6123
|
import { run } from "@stricli/core";
|
|
5312
6124
|
|
|
5313
6125
|
// src/lib/dynamic-app.ts
|
|
5314
|
-
import { existsSync as
|
|
6126
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
5315
6127
|
import { createRequire as createRequire2 } from "node:module";
|
|
5316
|
-
import { join as
|
|
6128
|
+
import { join as join26 } from "node:path";
|
|
5317
6129
|
import { buildApplication, buildRouteMap as buildRouteMap7 } from "@stricli/core";
|
|
5318
6130
|
|
|
5319
6131
|
// src/commands/add.ts
|
|
5320
|
-
import { existsSync as
|
|
5321
|
-
import { basename as
|
|
6132
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
6133
|
+
import { basename as basename5, resolve as resolve3 } from "node:path";
|
|
5322
6134
|
|
|
5323
|
-
// ../adapters/src/writers/
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
const
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
const rulePath = join(rulesDir, `omnidev-${rule.name}.mdc`);
|
|
5334
|
-
await writeFile(rulePath, rule.content, "utf-8");
|
|
5335
|
-
filesWritten.push(join(ctx.outputPath, `omnidev-${rule.name}.mdc`));
|
|
6135
|
+
// ../adapters/src/writers/generic/executor.ts
|
|
6136
|
+
async function executeWriters(writerConfigs, bundle, projectRoot) {
|
|
6137
|
+
const seen = new Set;
|
|
6138
|
+
const uniqueConfigs = [];
|
|
6139
|
+
let deduplicatedCount = 0;
|
|
6140
|
+
for (const config of writerConfigs) {
|
|
6141
|
+
const key = `${config.writer.id}:${config.outputPath}`;
|
|
6142
|
+
if (seen.has(key)) {
|
|
6143
|
+
deduplicatedCount++;
|
|
6144
|
+
continue;
|
|
5336
6145
|
}
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
};
|
|
6146
|
+
seen.add(key);
|
|
6147
|
+
uniqueConfigs.push(config);
|
|
5340
6148
|
}
|
|
5341
|
-
|
|
5342
|
-
|
|
6149
|
+
const allFilesWritten = [];
|
|
6150
|
+
for (const config of uniqueConfigs) {
|
|
6151
|
+
const result = await config.writer.write(bundle, {
|
|
6152
|
+
outputPath: config.outputPath,
|
|
6153
|
+
projectRoot
|
|
6154
|
+
});
|
|
6155
|
+
allFilesWritten.push(...result.filesWritten);
|
|
6156
|
+
}
|
|
6157
|
+
return {
|
|
6158
|
+
filesWritten: allFilesWritten,
|
|
6159
|
+
deduplicatedCount
|
|
6160
|
+
};
|
|
6161
|
+
}
|
|
6162
|
+
// ../adapters/src/writers/generic/hooks.ts
|
|
5343
6163
|
init_src();
|
|
5344
|
-
import { existsSync as
|
|
5345
|
-
import { mkdir as
|
|
5346
|
-
import { dirname, join as
|
|
6164
|
+
import { existsSync as existsSync20 } from "node:fs";
|
|
6165
|
+
import { mkdir as mkdir2, readFile as readFile17, writeFile as writeFile10 } from "node:fs/promises";
|
|
6166
|
+
import { dirname, join as join10 } from "node:path";
|
|
5347
6167
|
var HooksWriter = {
|
|
5348
6168
|
id: "hooks",
|
|
5349
6169
|
async write(bundle, ctx) {
|
|
5350
6170
|
if (!bundle.hooks) {
|
|
5351
6171
|
return { filesWritten: [] };
|
|
5352
6172
|
}
|
|
5353
|
-
const settingsPath =
|
|
6173
|
+
const settingsPath = join10(ctx.projectRoot, ctx.outputPath);
|
|
5354
6174
|
const parentDir = dirname(settingsPath);
|
|
5355
|
-
await
|
|
6175
|
+
await mkdir2(parentDir, { recursive: true });
|
|
5356
6176
|
const claudeHooks = transformHooksConfig(bundle.hooks, "toClaude");
|
|
5357
6177
|
let existingSettings = {};
|
|
5358
|
-
if (
|
|
6178
|
+
if (existsSync20(settingsPath)) {
|
|
5359
6179
|
try {
|
|
5360
6180
|
const content = await readFile17(settingsPath, "utf-8");
|
|
5361
6181
|
existingSettings = JSON.parse(content);
|
|
@@ -5367,28 +6187,28 @@ var HooksWriter = {
|
|
|
5367
6187
|
...existingSettings,
|
|
5368
6188
|
hooks: claudeHooks
|
|
5369
6189
|
};
|
|
5370
|
-
await
|
|
6190
|
+
await writeFile10(settingsPath, `${JSON.stringify(newSettings, null, 2)}
|
|
5371
6191
|
`, "utf-8");
|
|
5372
6192
|
return {
|
|
5373
6193
|
filesWritten: [ctx.outputPath]
|
|
5374
6194
|
};
|
|
5375
6195
|
}
|
|
5376
6196
|
};
|
|
5377
|
-
// ../adapters/src/writers/instructions-md.ts
|
|
5378
|
-
import { existsSync as
|
|
5379
|
-
import { mkdir as
|
|
5380
|
-
import { dirname as dirname2, join as
|
|
6197
|
+
// ../adapters/src/writers/generic/instructions-md.ts
|
|
6198
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
6199
|
+
import { mkdir as mkdir3, readFile as readFile18, writeFile as writeFile11 } from "node:fs/promises";
|
|
6200
|
+
import { dirname as dirname2, join as join11 } from "node:path";
|
|
5381
6201
|
var InstructionsMdWriter = {
|
|
5382
6202
|
id: "instructions-md",
|
|
5383
6203
|
async write(bundle, ctx) {
|
|
5384
|
-
const outputFullPath =
|
|
6204
|
+
const outputFullPath = join11(ctx.projectRoot, ctx.outputPath);
|
|
5385
6205
|
const parentDir = dirname2(outputFullPath);
|
|
5386
6206
|
if (parentDir !== ctx.projectRoot) {
|
|
5387
|
-
await
|
|
6207
|
+
await mkdir3(parentDir, { recursive: true });
|
|
5388
6208
|
}
|
|
5389
|
-
const omniMdPath =
|
|
6209
|
+
const omniMdPath = join11(ctx.projectRoot, "OMNI.md");
|
|
5390
6210
|
let omniMdContent = "";
|
|
5391
|
-
if (
|
|
6211
|
+
if (existsSync21(omniMdPath)) {
|
|
5392
6212
|
omniMdContent = await readFile18(omniMdPath, "utf-8");
|
|
5393
6213
|
}
|
|
5394
6214
|
let content = omniMdContent;
|
|
@@ -5398,66 +6218,121 @@ var InstructionsMdWriter = {
|
|
|
5398
6218
|
${bundle.instructionsContent}
|
|
5399
6219
|
`;
|
|
5400
6220
|
}
|
|
5401
|
-
await
|
|
6221
|
+
await writeFile11(outputFullPath, content, "utf-8");
|
|
5402
6222
|
return {
|
|
5403
6223
|
filesWritten: [ctx.outputPath]
|
|
5404
6224
|
};
|
|
5405
6225
|
}
|
|
5406
6226
|
};
|
|
5407
|
-
// ../adapters/src/writers/skills.ts
|
|
5408
|
-
import { mkdir as
|
|
5409
|
-
import { join as
|
|
6227
|
+
// ../adapters/src/writers/generic/skills.ts
|
|
6228
|
+
import { mkdir as mkdir4, writeFile as writeFile12 } from "node:fs/promises";
|
|
6229
|
+
import { join as join12 } from "node:path";
|
|
5410
6230
|
var SkillsWriter = {
|
|
5411
6231
|
id: "skills",
|
|
5412
6232
|
async write(bundle, ctx) {
|
|
5413
|
-
const skillsDir =
|
|
5414
|
-
await
|
|
6233
|
+
const skillsDir = join12(ctx.projectRoot, ctx.outputPath);
|
|
6234
|
+
await mkdir4(skillsDir, { recursive: true });
|
|
5415
6235
|
const filesWritten = [];
|
|
5416
6236
|
for (const skill of bundle.skills) {
|
|
5417
|
-
const skillDir =
|
|
5418
|
-
await
|
|
5419
|
-
const skillPath =
|
|
6237
|
+
const skillDir = join12(skillsDir, skill.name);
|
|
6238
|
+
await mkdir4(skillDir, { recursive: true });
|
|
6239
|
+
const skillPath = join12(skillDir, "SKILL.md");
|
|
5420
6240
|
const content = `---
|
|
5421
6241
|
name: ${skill.name}
|
|
5422
6242
|
description: "${skill.description}"
|
|
5423
6243
|
---
|
|
5424
6244
|
|
|
5425
6245
|
${skill.instructions}`;
|
|
5426
|
-
await
|
|
5427
|
-
filesWritten.push(
|
|
6246
|
+
await writeFile12(skillPath, content, "utf-8");
|
|
6247
|
+
filesWritten.push(join12(ctx.outputPath, skill.name, "SKILL.md"));
|
|
5428
6248
|
}
|
|
5429
6249
|
return {
|
|
5430
6250
|
filesWritten
|
|
5431
6251
|
};
|
|
5432
6252
|
}
|
|
5433
6253
|
};
|
|
5434
|
-
// ../adapters/src/writers/
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
6254
|
+
// ../adapters/src/writers/generic/commands-as-skills.ts
|
|
6255
|
+
import { mkdir as mkdir5, writeFile as writeFile13 } from "node:fs/promises";
|
|
6256
|
+
import { join as join13 } from "node:path";
|
|
6257
|
+
function generateSkillFrontmatter(command) {
|
|
6258
|
+
const lines = ["---"];
|
|
6259
|
+
lines.push(`name: ${command.name}`);
|
|
6260
|
+
lines.push(`description: "${command.description.replace(/"/g, "\\\"")}"`);
|
|
6261
|
+
if (command.allowedTools) {
|
|
6262
|
+
lines.push(`allowed_tools: "${command.allowedTools}"`);
|
|
6263
|
+
}
|
|
6264
|
+
lines.push("---");
|
|
6265
|
+
return lines.join(`
|
|
6266
|
+
`);
|
|
6267
|
+
}
|
|
6268
|
+
var CommandsAsSkillsWriter = {
|
|
6269
|
+
id: "commands-as-skills",
|
|
6270
|
+
async write(bundle, ctx) {
|
|
6271
|
+
const skillsDir = join13(ctx.projectRoot, ctx.outputPath);
|
|
6272
|
+
await mkdir5(skillsDir, { recursive: true });
|
|
6273
|
+
const filesWritten = [];
|
|
6274
|
+
for (const command of bundle.commands) {
|
|
6275
|
+
const commandSkillDir = join13(skillsDir, command.name);
|
|
6276
|
+
await mkdir5(commandSkillDir, { recursive: true });
|
|
6277
|
+
const frontmatter = generateSkillFrontmatter(command);
|
|
6278
|
+
const content = `${frontmatter}
|
|
6279
|
+
|
|
6280
|
+
${command.prompt}`;
|
|
6281
|
+
const skillPath = join13(commandSkillDir, "SKILL.md");
|
|
6282
|
+
await writeFile13(skillPath, content, "utf-8");
|
|
6283
|
+
filesWritten.push(join13(ctx.outputPath, command.name, "SKILL.md"));
|
|
5444
6284
|
}
|
|
5445
|
-
|
|
5446
|
-
|
|
6285
|
+
return {
|
|
6286
|
+
filesWritten
|
|
6287
|
+
};
|
|
5447
6288
|
}
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
6289
|
+
};
|
|
6290
|
+
// ../adapters/src/writers/claude/agents.ts
|
|
6291
|
+
import { mkdir as mkdir6, writeFile as writeFile14 } from "node:fs/promises";
|
|
6292
|
+
import { join as join14 } from "node:path";
|
|
6293
|
+
function generateFrontmatter(agent) {
|
|
6294
|
+
const lines = ["---"];
|
|
6295
|
+
lines.push(`name: ${agent.name}`);
|
|
6296
|
+
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
6297
|
+
if (agent.tools && agent.tools.length > 0) {
|
|
6298
|
+
lines.push(`tools: ${agent.tools.join(", ")}`);
|
|
5455
6299
|
}
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
6300
|
+
if (agent.disallowedTools && agent.disallowedTools.length > 0) {
|
|
6301
|
+
lines.push(`disallowedTools: ${agent.disallowedTools.join(", ")}`);
|
|
6302
|
+
}
|
|
6303
|
+
if (agent.model && agent.model !== "inherit") {
|
|
6304
|
+
lines.push(`model: ${agent.model}`);
|
|
6305
|
+
}
|
|
6306
|
+
if (agent.permissionMode && agent.permissionMode !== "default") {
|
|
6307
|
+
lines.push(`permissionMode: ${agent.permissionMode}`);
|
|
6308
|
+
}
|
|
6309
|
+
if (agent.skills && agent.skills.length > 0) {
|
|
6310
|
+
lines.push(`skills: ${agent.skills.join(", ")}`);
|
|
6311
|
+
}
|
|
6312
|
+
lines.push("---");
|
|
6313
|
+
return lines.join(`
|
|
6314
|
+
`);
|
|
5460
6315
|
}
|
|
6316
|
+
var ClaudeAgentsWriter = {
|
|
6317
|
+
id: "claude-agents",
|
|
6318
|
+
async write(bundle, ctx) {
|
|
6319
|
+
const agentsDir = join14(ctx.projectRoot, ctx.outputPath);
|
|
6320
|
+
await mkdir6(agentsDir, { recursive: true });
|
|
6321
|
+
const filesWritten = [];
|
|
6322
|
+
for (const agent of bundle.subagents) {
|
|
6323
|
+
const frontmatter = generateFrontmatter(agent);
|
|
6324
|
+
const content = `${frontmatter}
|
|
6325
|
+
|
|
6326
|
+
${agent.systemPrompt}`;
|
|
6327
|
+
const agentPath = join14(agentsDir, `${agent.name}.md`);
|
|
6328
|
+
await writeFile14(agentPath, content, "utf-8");
|
|
6329
|
+
filesWritten.push(join14(ctx.outputPath, `${agent.name}.md`));
|
|
6330
|
+
}
|
|
6331
|
+
return {
|
|
6332
|
+
filesWritten
|
|
6333
|
+
};
|
|
6334
|
+
}
|
|
6335
|
+
};
|
|
5461
6336
|
// ../adapters/src/claude-code/index.ts
|
|
5462
6337
|
var claudeCodeAdapter = {
|
|
5463
6338
|
id: "claude-code",
|
|
@@ -5465,6 +6340,8 @@ var claudeCodeAdapter = {
|
|
|
5465
6340
|
writers: [
|
|
5466
6341
|
{ writer: InstructionsMdWriter, outputPath: "CLAUDE.md" },
|
|
5467
6342
|
{ writer: SkillsWriter, outputPath: ".claude/skills/" },
|
|
6343
|
+
{ writer: ClaudeAgentsWriter, outputPath: ".claude/agents/" },
|
|
6344
|
+
{ writer: CommandsAsSkillsWriter, outputPath: ".claude/skills/" },
|
|
5468
6345
|
{ writer: HooksWriter, outputPath: ".claude/settings.json" }
|
|
5469
6346
|
],
|
|
5470
6347
|
async init(_ctx) {
|
|
@@ -5483,16 +6360,97 @@ var claudeCodeAdapter = {
|
|
|
5483
6360
|
};
|
|
5484
6361
|
// ../adapters/src/codex/index.ts
|
|
5485
6362
|
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
5486
|
-
import { join as
|
|
6363
|
+
import { join as join16 } from "node:path";
|
|
6364
|
+
|
|
6365
|
+
// ../adapters/src/writers/codex/toml.ts
|
|
6366
|
+
init_dist();
|
|
6367
|
+
import { mkdir as mkdir7, writeFile as writeFile15 } from "node:fs/promises";
|
|
6368
|
+
import { dirname as dirname3, join as join15 } from "node:path";
|
|
6369
|
+
var FILE_HEADER = `# Generated by OmniDev - DO NOT EDIT
|
|
6370
|
+
# Run \`omnidev sync\` to regenerate
|
|
6371
|
+
|
|
6372
|
+
`;
|
|
6373
|
+
function buildCodexMcpConfig(id, mcp) {
|
|
6374
|
+
const transport = mcp.transport ?? "stdio";
|
|
6375
|
+
if (transport === "sse") {
|
|
6376
|
+
console.warn(` Warning: Skipping MCP "${id}" - SSE transport is not supported by Codex`);
|
|
6377
|
+
return null;
|
|
6378
|
+
}
|
|
6379
|
+
const config3 = {};
|
|
6380
|
+
if (transport === "http") {
|
|
6381
|
+
if (mcp.url) {
|
|
6382
|
+
config3.url = mcp.url;
|
|
6383
|
+
}
|
|
6384
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
6385
|
+
config3.http_headers = mcp.headers;
|
|
6386
|
+
}
|
|
6387
|
+
} else {
|
|
6388
|
+
if (mcp.command) {
|
|
6389
|
+
config3.command = mcp.command;
|
|
6390
|
+
}
|
|
6391
|
+
if (mcp.args && mcp.args.length > 0) {
|
|
6392
|
+
config3.args = mcp.args;
|
|
6393
|
+
}
|
|
6394
|
+
if (mcp.env && Object.keys(mcp.env).length > 0) {
|
|
6395
|
+
config3.env = mcp.env;
|
|
6396
|
+
}
|
|
6397
|
+
if (mcp.cwd) {
|
|
6398
|
+
config3.cwd = mcp.cwd;
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
6401
|
+
return config3;
|
|
6402
|
+
}
|
|
6403
|
+
function collectMcps(bundle) {
|
|
6404
|
+
const mcps = new Map;
|
|
6405
|
+
for (const capability3 of bundle.capabilities) {
|
|
6406
|
+
if (capability3.config.mcp) {
|
|
6407
|
+
mcps.set(capability3.id, capability3.config.mcp);
|
|
6408
|
+
}
|
|
6409
|
+
}
|
|
6410
|
+
return mcps;
|
|
6411
|
+
}
|
|
6412
|
+
var CodexTomlWriter = {
|
|
6413
|
+
id: "codex-toml",
|
|
6414
|
+
async write(bundle, ctx) {
|
|
6415
|
+
const mcps = collectMcps(bundle);
|
|
6416
|
+
if (mcps.size === 0) {
|
|
6417
|
+
return { filesWritten: [] };
|
|
6418
|
+
}
|
|
6419
|
+
const configPath = join15(ctx.projectRoot, ctx.outputPath);
|
|
6420
|
+
const parentDir = dirname3(configPath);
|
|
6421
|
+
await mkdir7(parentDir, { recursive: true });
|
|
6422
|
+
const mcpServers = {};
|
|
6423
|
+
for (const [id, mcp] of mcps) {
|
|
6424
|
+
const converted = buildCodexMcpConfig(id, mcp);
|
|
6425
|
+
if (converted) {
|
|
6426
|
+
mcpServers[id] = converted;
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
if (Object.keys(mcpServers).length === 0) {
|
|
6430
|
+
return { filesWritten: [] };
|
|
6431
|
+
}
|
|
6432
|
+
const codexConfig = {
|
|
6433
|
+
mcp_servers: mcpServers
|
|
6434
|
+
};
|
|
6435
|
+
const tomlContent = FILE_HEADER + stringify(codexConfig);
|
|
6436
|
+
await writeFile15(configPath, tomlContent, "utf-8");
|
|
6437
|
+
return {
|
|
6438
|
+
filesWritten: [ctx.outputPath]
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
};
|
|
6442
|
+
// ../adapters/src/codex/index.ts
|
|
5487
6443
|
var codexAdapter = {
|
|
5488
6444
|
id: "codex",
|
|
5489
6445
|
displayName: "Codex",
|
|
5490
6446
|
writers: [
|
|
5491
6447
|
{ writer: InstructionsMdWriter, outputPath: "AGENTS.md" },
|
|
5492
|
-
{ writer: SkillsWriter, outputPath: ".codex/skills/" }
|
|
6448
|
+
{ writer: SkillsWriter, outputPath: ".codex/skills/" },
|
|
6449
|
+
{ writer: CommandsAsSkillsWriter, outputPath: ".codex/skills/" },
|
|
6450
|
+
{ writer: CodexTomlWriter, outputPath: ".codex/config.toml" }
|
|
5493
6451
|
],
|
|
5494
6452
|
async init(ctx) {
|
|
5495
|
-
const codexDir =
|
|
6453
|
+
const codexDir = join16(ctx.projectRoot, ".codex");
|
|
5496
6454
|
mkdirSync6(codexDir, { recursive: true });
|
|
5497
6455
|
return {
|
|
5498
6456
|
filesCreated: [".codex/"],
|
|
@@ -5509,17 +6467,186 @@ var codexAdapter = {
|
|
|
5509
6467
|
};
|
|
5510
6468
|
// ../adapters/src/cursor/index.ts
|
|
5511
6469
|
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
5512
|
-
import { join as
|
|
6470
|
+
import { join as join21 } from "node:path";
|
|
6471
|
+
|
|
6472
|
+
// ../adapters/src/writers/cursor/agents.ts
|
|
6473
|
+
import { mkdir as mkdir8, writeFile as writeFile16 } from "node:fs/promises";
|
|
6474
|
+
import { join as join17 } from "node:path";
|
|
6475
|
+
function mapModelToCursor(model) {
|
|
6476
|
+
if (!model || model === "inherit")
|
|
6477
|
+
return "inherit";
|
|
6478
|
+
const modelMap = {
|
|
6479
|
+
haiku: "fast",
|
|
6480
|
+
sonnet: "inherit",
|
|
6481
|
+
opus: "inherit"
|
|
6482
|
+
};
|
|
6483
|
+
return modelMap[model] ?? "inherit";
|
|
6484
|
+
}
|
|
6485
|
+
function generateFrontmatter2(agent) {
|
|
6486
|
+
const lines = ["---"];
|
|
6487
|
+
lines.push(`name: ${agent.name}`);
|
|
6488
|
+
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
6489
|
+
const model = mapModelToCursor(agent.model);
|
|
6490
|
+
if (model) {
|
|
6491
|
+
lines.push(`model: ${model}`);
|
|
6492
|
+
}
|
|
6493
|
+
if (agent.permissionMode === "plan") {
|
|
6494
|
+
lines.push("readonly: true");
|
|
6495
|
+
}
|
|
6496
|
+
if (agent.isBackground) {
|
|
6497
|
+
lines.push("is_background: true");
|
|
6498
|
+
}
|
|
6499
|
+
lines.push("---");
|
|
6500
|
+
return lines.join(`
|
|
6501
|
+
`);
|
|
6502
|
+
}
|
|
6503
|
+
var CursorAgentsWriter = {
|
|
6504
|
+
id: "cursor-agents",
|
|
6505
|
+
async write(bundle, ctx) {
|
|
6506
|
+
const agentsDir = join17(ctx.projectRoot, ctx.outputPath);
|
|
6507
|
+
await mkdir8(agentsDir, { recursive: true });
|
|
6508
|
+
const filesWritten = [];
|
|
6509
|
+
for (const agent of bundle.subagents) {
|
|
6510
|
+
const frontmatter = generateFrontmatter2(agent);
|
|
6511
|
+
const content = `${frontmatter}
|
|
6512
|
+
|
|
6513
|
+
${agent.systemPrompt}`;
|
|
6514
|
+
const agentPath = join17(agentsDir, `${agent.name}.md`);
|
|
6515
|
+
await writeFile16(agentPath, content, "utf-8");
|
|
6516
|
+
filesWritten.push(join17(ctx.outputPath, `${agent.name}.md`));
|
|
6517
|
+
}
|
|
6518
|
+
return {
|
|
6519
|
+
filesWritten
|
|
6520
|
+
};
|
|
6521
|
+
}
|
|
6522
|
+
};
|
|
6523
|
+
// ../adapters/src/writers/cursor/commands.ts
|
|
6524
|
+
import { mkdir as mkdir9, writeFile as writeFile17 } from "node:fs/promises";
|
|
6525
|
+
import { join as join18 } from "node:path";
|
|
6526
|
+
var CursorCommandsWriter = {
|
|
6527
|
+
id: "cursor-commands",
|
|
6528
|
+
async write(bundle, ctx) {
|
|
6529
|
+
const commandsDir = join18(ctx.projectRoot, ctx.outputPath);
|
|
6530
|
+
await mkdir9(commandsDir, { recursive: true });
|
|
6531
|
+
const filesWritten = [];
|
|
6532
|
+
for (const command of bundle.commands) {
|
|
6533
|
+
const content = `# ${command.name}
|
|
6534
|
+
|
|
6535
|
+
${command.description}
|
|
6536
|
+
|
|
6537
|
+
${command.prompt}`;
|
|
6538
|
+
const commandPath = join18(commandsDir, `${command.name}.md`);
|
|
6539
|
+
await writeFile17(commandPath, content, "utf-8");
|
|
6540
|
+
filesWritten.push(join18(ctx.outputPath, `${command.name}.md`));
|
|
6541
|
+
}
|
|
6542
|
+
return {
|
|
6543
|
+
filesWritten
|
|
6544
|
+
};
|
|
6545
|
+
}
|
|
6546
|
+
};
|
|
6547
|
+
// ../adapters/src/writers/cursor/mcp-json.ts
|
|
6548
|
+
import { mkdir as mkdir10, writeFile as writeFile18 } from "node:fs/promises";
|
|
6549
|
+
import { dirname as dirname4, join as join19 } from "node:path";
|
|
6550
|
+
function buildCursorMcpConfig(mcp) {
|
|
6551
|
+
const transport = mcp.transport ?? "stdio";
|
|
6552
|
+
if (transport === "http" || transport === "sse") {
|
|
6553
|
+
if (!mcp.url) {
|
|
6554
|
+
return null;
|
|
6555
|
+
}
|
|
6556
|
+
const config4 = {
|
|
6557
|
+
url: mcp.url
|
|
6558
|
+
};
|
|
6559
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
6560
|
+
config4.headers = mcp.headers;
|
|
6561
|
+
}
|
|
6562
|
+
return config4;
|
|
6563
|
+
}
|
|
6564
|
+
if (!mcp.command) {
|
|
6565
|
+
return null;
|
|
6566
|
+
}
|
|
6567
|
+
const config3 = {
|
|
6568
|
+
command: mcp.command
|
|
6569
|
+
};
|
|
6570
|
+
if (mcp.args && mcp.args.length > 0) {
|
|
6571
|
+
config3.args = mcp.args;
|
|
6572
|
+
}
|
|
6573
|
+
if (mcp.env && Object.keys(mcp.env).length > 0) {
|
|
6574
|
+
config3.env = mcp.env;
|
|
6575
|
+
}
|
|
6576
|
+
return config3;
|
|
6577
|
+
}
|
|
6578
|
+
function collectMcps2(bundle) {
|
|
6579
|
+
const mcps = new Map;
|
|
6580
|
+
for (const capability3 of bundle.capabilities) {
|
|
6581
|
+
if (capability3.config.mcp) {
|
|
6582
|
+
mcps.set(capability3.id, capability3.config.mcp);
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
return mcps;
|
|
6586
|
+
}
|
|
6587
|
+
var CursorMcpJsonWriter = {
|
|
6588
|
+
id: "cursor-mcp-json",
|
|
6589
|
+
async write(bundle, ctx) {
|
|
6590
|
+
const mcps = collectMcps2(bundle);
|
|
6591
|
+
if (mcps.size === 0) {
|
|
6592
|
+
return { filesWritten: [] };
|
|
6593
|
+
}
|
|
6594
|
+
const configPath = join19(ctx.projectRoot, ctx.outputPath);
|
|
6595
|
+
const parentDir = dirname4(configPath);
|
|
6596
|
+
await mkdir10(parentDir, { recursive: true });
|
|
6597
|
+
const mcpServers = {};
|
|
6598
|
+
for (const [id, mcp] of mcps) {
|
|
6599
|
+
const converted = buildCursorMcpConfig(mcp);
|
|
6600
|
+
if (converted) {
|
|
6601
|
+
mcpServers[id] = converted;
|
|
6602
|
+
}
|
|
6603
|
+
}
|
|
6604
|
+
if (Object.keys(mcpServers).length === 0) {
|
|
6605
|
+
return { filesWritten: [] };
|
|
6606
|
+
}
|
|
6607
|
+
const cursorMcpJson = {
|
|
6608
|
+
mcpServers
|
|
6609
|
+
};
|
|
6610
|
+
await writeFile18(configPath, `${JSON.stringify(cursorMcpJson, null, 2)}
|
|
6611
|
+
`, "utf-8");
|
|
6612
|
+
return {
|
|
6613
|
+
filesWritten: [ctx.outputPath]
|
|
6614
|
+
};
|
|
6615
|
+
}
|
|
6616
|
+
};
|
|
6617
|
+
// ../adapters/src/writers/cursor/rules.ts
|
|
6618
|
+
import { mkdir as mkdir11, writeFile as writeFile19 } from "node:fs/promises";
|
|
6619
|
+
import { join as join20 } from "node:path";
|
|
6620
|
+
var CursorRulesWriter = {
|
|
6621
|
+
id: "cursor-rules",
|
|
6622
|
+
async write(bundle, ctx) {
|
|
6623
|
+
const rulesDir = join20(ctx.projectRoot, ctx.outputPath);
|
|
6624
|
+
await mkdir11(rulesDir, { recursive: true });
|
|
6625
|
+
const filesWritten = [];
|
|
6626
|
+
for (const rule of bundle.rules) {
|
|
6627
|
+
const rulePath = join20(rulesDir, `omnidev-${rule.name}.mdc`);
|
|
6628
|
+
await writeFile19(rulePath, rule.content, "utf-8");
|
|
6629
|
+
filesWritten.push(join20(ctx.outputPath, `omnidev-${rule.name}.mdc`));
|
|
6630
|
+
}
|
|
6631
|
+
return {
|
|
6632
|
+
filesWritten
|
|
6633
|
+
};
|
|
6634
|
+
}
|
|
6635
|
+
};
|
|
6636
|
+
// ../adapters/src/cursor/index.ts
|
|
5513
6637
|
var cursorAdapter = {
|
|
5514
6638
|
id: "cursor",
|
|
5515
6639
|
displayName: "Cursor",
|
|
5516
6640
|
writers: [
|
|
5517
6641
|
{ writer: InstructionsMdWriter, outputPath: "CLAUDE.md" },
|
|
5518
|
-
{ writer: SkillsWriter, outputPath: ".
|
|
5519
|
-
{ writer: CursorRulesWriter, outputPath: ".cursor/rules/" }
|
|
6642
|
+
{ writer: SkillsWriter, outputPath: ".cursor/skills/" },
|
|
6643
|
+
{ writer: CursorRulesWriter, outputPath: ".cursor/rules/" },
|
|
6644
|
+
{ writer: CursorAgentsWriter, outputPath: ".cursor/agents/" },
|
|
6645
|
+
{ writer: CursorCommandsWriter, outputPath: ".cursor/commands/" },
|
|
6646
|
+
{ writer: CursorMcpJsonWriter, outputPath: ".cursor/mcp.json" }
|
|
5520
6647
|
],
|
|
5521
6648
|
async init(ctx) {
|
|
5522
|
-
const rulesDir =
|
|
6649
|
+
const rulesDir = join21(ctx.projectRoot, ".cursor", "rules");
|
|
5523
6650
|
mkdirSync7(rulesDir, { recursive: true });
|
|
5524
6651
|
return {
|
|
5525
6652
|
filesCreated: [".cursor/rules/"],
|
|
@@ -5536,16 +6663,153 @@ var cursorAdapter = {
|
|
|
5536
6663
|
};
|
|
5537
6664
|
// ../adapters/src/opencode/index.ts
|
|
5538
6665
|
import { mkdirSync as mkdirSync8 } from "node:fs";
|
|
5539
|
-
import { join as
|
|
6666
|
+
import { join as join24 } from "node:path";
|
|
6667
|
+
|
|
6668
|
+
// ../adapters/src/writers/opencode/agents.ts
|
|
6669
|
+
import { mkdir as mkdir12, writeFile as writeFile20 } from "node:fs/promises";
|
|
6670
|
+
import { join as join22 } from "node:path";
|
|
6671
|
+
function mapModelToOpenCode(model) {
|
|
6672
|
+
if (!model || model === "inherit")
|
|
6673
|
+
return;
|
|
6674
|
+
const modelMap = {
|
|
6675
|
+
sonnet: "anthropic/claude-sonnet-4",
|
|
6676
|
+
opus: "anthropic/claude-opus-4",
|
|
6677
|
+
haiku: "anthropic/claude-haiku-3-5"
|
|
6678
|
+
};
|
|
6679
|
+
return modelMap[model];
|
|
6680
|
+
}
|
|
6681
|
+
function mapPermissionsToOpenCode(permissionMode) {
|
|
6682
|
+
if (!permissionMode || permissionMode === "default")
|
|
6683
|
+
return;
|
|
6684
|
+
const permissionMap = {
|
|
6685
|
+
acceptEdits: { edit: "allow", bash: { "*": "ask" } },
|
|
6686
|
+
dontAsk: { edit: "allow", bash: { "*": "allow" } },
|
|
6687
|
+
bypassPermissions: { edit: "allow", bash: { "*": "allow" }, webfetch: "allow" },
|
|
6688
|
+
plan: { edit: "deny", bash: { "*": "deny" } }
|
|
6689
|
+
};
|
|
6690
|
+
return permissionMap[permissionMode];
|
|
6691
|
+
}
|
|
6692
|
+
function mapToolsToOpenCode(tools) {
|
|
6693
|
+
if (!tools || tools.length === 0)
|
|
6694
|
+
return;
|
|
6695
|
+
const toolsObject = {};
|
|
6696
|
+
for (const tool of tools) {
|
|
6697
|
+
toolsObject[tool.toLowerCase()] = true;
|
|
6698
|
+
}
|
|
6699
|
+
return toolsObject;
|
|
6700
|
+
}
|
|
6701
|
+
function generateFrontmatter3(agent) {
|
|
6702
|
+
const lines = ["---"];
|
|
6703
|
+
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
6704
|
+
const modelId = agent.modelId ?? mapModelToOpenCode(agent.model);
|
|
6705
|
+
if (modelId) {
|
|
6706
|
+
lines.push(`model: ${modelId}`);
|
|
6707
|
+
}
|
|
6708
|
+
if (agent.mode) {
|
|
6709
|
+
lines.push(`mode: ${agent.mode}`);
|
|
6710
|
+
}
|
|
6711
|
+
if (agent.temperature !== undefined) {
|
|
6712
|
+
lines.push(`temperature: ${agent.temperature}`);
|
|
6713
|
+
}
|
|
6714
|
+
if (agent.maxSteps !== undefined) {
|
|
6715
|
+
lines.push(`maxSteps: ${agent.maxSteps}`);
|
|
6716
|
+
}
|
|
6717
|
+
if (agent.hidden !== undefined) {
|
|
6718
|
+
lines.push(`hidden: ${agent.hidden}`);
|
|
6719
|
+
}
|
|
6720
|
+
const toolsObj = agent.toolPermissions ?? mapToolsToOpenCode(agent.tools);
|
|
6721
|
+
if (toolsObj) {
|
|
6722
|
+
lines.push("tools:");
|
|
6723
|
+
for (const [tool, enabled] of Object.entries(toolsObj)) {
|
|
6724
|
+
lines.push(` ${tool}: ${enabled}`);
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
const permissions = agent.permissions ?? mapPermissionsToOpenCode(agent.permissionMode);
|
|
6728
|
+
if (permissions) {
|
|
6729
|
+
lines.push("permissions:");
|
|
6730
|
+
for (const [key, value] of Object.entries(permissions)) {
|
|
6731
|
+
if (typeof value === "object") {
|
|
6732
|
+
lines.push(` ${key}:`);
|
|
6733
|
+
for (const [subKey, subValue] of Object.entries(value)) {
|
|
6734
|
+
lines.push(` ${subKey}: ${subValue}`);
|
|
6735
|
+
}
|
|
6736
|
+
} else {
|
|
6737
|
+
lines.push(` ${key}: ${value}`);
|
|
6738
|
+
}
|
|
6739
|
+
}
|
|
6740
|
+
}
|
|
6741
|
+
lines.push("---");
|
|
6742
|
+
return lines.join(`
|
|
6743
|
+
`);
|
|
6744
|
+
}
|
|
6745
|
+
var OpenCodeAgentsWriter = {
|
|
6746
|
+
id: "opencode-agents",
|
|
6747
|
+
async write(bundle, ctx) {
|
|
6748
|
+
const agentsDir = join22(ctx.projectRoot, ctx.outputPath);
|
|
6749
|
+
await mkdir12(agentsDir, { recursive: true });
|
|
6750
|
+
const filesWritten = [];
|
|
6751
|
+
for (const agent of bundle.subagents) {
|
|
6752
|
+
const frontmatter = generateFrontmatter3(agent);
|
|
6753
|
+
const content = `${frontmatter}
|
|
6754
|
+
|
|
6755
|
+
${agent.systemPrompt}`;
|
|
6756
|
+
const agentPath = join22(agentsDir, `${agent.name}.md`);
|
|
6757
|
+
await writeFile20(agentPath, content, "utf-8");
|
|
6758
|
+
filesWritten.push(join22(ctx.outputPath, `${agent.name}.md`));
|
|
6759
|
+
}
|
|
6760
|
+
return {
|
|
6761
|
+
filesWritten
|
|
6762
|
+
};
|
|
6763
|
+
}
|
|
6764
|
+
};
|
|
6765
|
+
// ../adapters/src/writers/opencode/commands.ts
|
|
6766
|
+
import { mkdir as mkdir13, writeFile as writeFile21 } from "node:fs/promises";
|
|
6767
|
+
import { join as join23 } from "node:path";
|
|
6768
|
+
function generateFrontmatter4(command) {
|
|
6769
|
+
const lines = ["---"];
|
|
6770
|
+
lines.push(`description: "${command.description.replace(/"/g, "\\\"")}"`);
|
|
6771
|
+
if (command.modelId) {
|
|
6772
|
+
lines.push(`model: ${command.modelId}`);
|
|
6773
|
+
}
|
|
6774
|
+
if (command.agent) {
|
|
6775
|
+
lines.push(`agent: ${command.agent}`);
|
|
6776
|
+
}
|
|
6777
|
+
lines.push("---");
|
|
6778
|
+
return lines.join(`
|
|
6779
|
+
`);
|
|
6780
|
+
}
|
|
6781
|
+
var OpenCodeCommandsWriter = {
|
|
6782
|
+
id: "opencode-commands",
|
|
6783
|
+
async write(bundle, ctx) {
|
|
6784
|
+
const commandsDir = join23(ctx.projectRoot, ctx.outputPath);
|
|
6785
|
+
await mkdir13(commandsDir, { recursive: true });
|
|
6786
|
+
const filesWritten = [];
|
|
6787
|
+
for (const command of bundle.commands) {
|
|
6788
|
+
const frontmatter = generateFrontmatter4(command);
|
|
6789
|
+
const content = `${frontmatter}
|
|
6790
|
+
|
|
6791
|
+
${command.prompt}`;
|
|
6792
|
+
const commandPath = join23(commandsDir, `${command.name}.md`);
|
|
6793
|
+
await writeFile21(commandPath, content, "utf-8");
|
|
6794
|
+
filesWritten.push(join23(ctx.outputPath, `${command.name}.md`));
|
|
6795
|
+
}
|
|
6796
|
+
return {
|
|
6797
|
+
filesWritten
|
|
6798
|
+
};
|
|
6799
|
+
}
|
|
6800
|
+
};
|
|
6801
|
+
// ../adapters/src/opencode/index.ts
|
|
5540
6802
|
var opencodeAdapter = {
|
|
5541
6803
|
id: "opencode",
|
|
5542
6804
|
displayName: "OpenCode",
|
|
5543
6805
|
writers: [
|
|
5544
6806
|
{ writer: InstructionsMdWriter, outputPath: "AGENTS.md" },
|
|
5545
|
-
{ writer: SkillsWriter, outputPath: ".opencode/skills/" }
|
|
6807
|
+
{ writer: SkillsWriter, outputPath: ".opencode/skills/" },
|
|
6808
|
+
{ writer: OpenCodeAgentsWriter, outputPath: ".opencode/agents/" },
|
|
6809
|
+
{ writer: OpenCodeCommandsWriter, outputPath: ".opencode/commands/" }
|
|
5546
6810
|
],
|
|
5547
6811
|
async init(ctx) {
|
|
5548
|
-
const opencodeDir =
|
|
6812
|
+
const opencodeDir = join24(ctx.projectRoot, ".opencode");
|
|
5549
6813
|
mkdirSync8(opencodeDir, { recursive: true });
|
|
5550
6814
|
return {
|
|
5551
6815
|
filesCreated: [".opencode/"],
|
|
@@ -5587,7 +6851,7 @@ async function inferCapabilityId(source, sourceType) {
|
|
|
5587
6851
|
if (id) {
|
|
5588
6852
|
return id;
|
|
5589
6853
|
}
|
|
5590
|
-
return
|
|
6854
|
+
return basename5(resolvedPath);
|
|
5591
6855
|
}
|
|
5592
6856
|
const parts = source.replace("github:", "").split("/");
|
|
5593
6857
|
if (parts.length >= 2) {
|
|
@@ -5597,7 +6861,7 @@ async function inferCapabilityId(source, sourceType) {
|
|
|
5597
6861
|
}
|
|
5598
6862
|
async function runAddCap(flags, name) {
|
|
5599
6863
|
try {
|
|
5600
|
-
if (!
|
|
6864
|
+
if (!existsSync22("omni.toml")) {
|
|
5601
6865
|
console.log("✗ No config file found");
|
|
5602
6866
|
console.log(" Run: omnidev init");
|
|
5603
6867
|
process.exit(1);
|
|
@@ -5620,7 +6884,7 @@ async function runAddCap(flags, name) {
|
|
|
5620
6884
|
sourceType = "local";
|
|
5621
6885
|
const localPath = flags.local.startsWith("file://") ? flags.local.slice(7) : flags.local;
|
|
5622
6886
|
source = `file://${localPath}`;
|
|
5623
|
-
if (!
|
|
6887
|
+
if (!existsSync22(localPath)) {
|
|
5624
6888
|
console.error(`✗ Local path not found: ${localPath}`);
|
|
5625
6889
|
process.exit(1);
|
|
5626
6890
|
}
|
|
@@ -5633,6 +6897,17 @@ async function runAddCap(flags, name) {
|
|
|
5633
6897
|
process.exit(1);
|
|
5634
6898
|
}
|
|
5635
6899
|
source = `github:${flags.github}`;
|
|
6900
|
+
console.log(` Validating repository ${flags.github}...`);
|
|
6901
|
+
const validation = await validateGitCapability(source, flags.path);
|
|
6902
|
+
if (!validation.valid) {
|
|
6903
|
+
console.error(`✗ ${validation.error}`);
|
|
6904
|
+
process.exit(1);
|
|
6905
|
+
}
|
|
6906
|
+
if (validation.hasCapabilityToml) {
|
|
6907
|
+
console.log(` Found capability.toml`);
|
|
6908
|
+
} else {
|
|
6909
|
+
console.log(` Repository can be auto-wrapped as capability`);
|
|
6910
|
+
}
|
|
5636
6911
|
} else {
|
|
5637
6912
|
throw new Error("Unreachable: no source specified");
|
|
5638
6913
|
}
|
|
@@ -5701,7 +6976,7 @@ async function runAddCap(flags, name) {
|
|
|
5701
6976
|
}
|
|
5702
6977
|
async function runAddMcp(flags, name) {
|
|
5703
6978
|
try {
|
|
5704
|
-
if (!
|
|
6979
|
+
if (!existsSync22("omni.toml")) {
|
|
5705
6980
|
console.log("✗ No config file found");
|
|
5706
6981
|
console.log(" Run: omnidev init");
|
|
5707
6982
|
process.exit(1);
|
|
@@ -5832,12 +7107,16 @@ If the capability name is omitted, it will be inferred from:
|
|
|
5832
7107
|
- For local sources: the ID in capability.toml or directory name
|
|
5833
7108
|
- For GitHub sources: the repository name or last path segment
|
|
5834
7109
|
|
|
7110
|
+
Claude plugins (.claude-plugin/plugin.json) are automatically wrapped as OmniDev capabilities.
|
|
7111
|
+
Hooks defined in hooks.json are also supported and will be synced to .claude/settings.json.
|
|
7112
|
+
|
|
5835
7113
|
Examples:
|
|
5836
7114
|
omnidev add cap my-cap --github expo/skills # Uses version = "latest"
|
|
5837
7115
|
omnidev add cap --github expo/skills # Infers name as "skills"
|
|
5838
7116
|
omnidev add cap --github expo/skills --pin # Pins to detected version
|
|
5839
7117
|
omnidev add cap --local ./capabilities/my-cap # Infers name from capability.toml
|
|
5840
|
-
omnidev add cap custom-name --local ./capabilities/my-cap
|
|
7118
|
+
omnidev add cap custom-name --local ./capabilities/my-cap
|
|
7119
|
+
omnidev add cap --github user/claude-plugin # Auto-wraps Claude plugins`
|
|
5841
7120
|
},
|
|
5842
7121
|
parameters: {
|
|
5843
7122
|
flags: {
|
|
@@ -5984,9 +7263,9 @@ var addRoutes = buildRouteMap({
|
|
|
5984
7263
|
});
|
|
5985
7264
|
|
|
5986
7265
|
// src/commands/capability.ts
|
|
5987
|
-
import { existsSync as
|
|
5988
|
-
import { writeFile as
|
|
5989
|
-
import { join as
|
|
7266
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync9 } from "node:fs";
|
|
7267
|
+
import { writeFile as writeFile22 } from "node:fs/promises";
|
|
7268
|
+
import { join as join25 } from "node:path";
|
|
5990
7269
|
import { input } from "@inquirer/prompts";
|
|
5991
7270
|
init_src();
|
|
5992
7271
|
import { buildCommand as buildCommand2, buildRouteMap as buildRouteMap2 } from "@stricli/core";
|
|
@@ -6171,7 +7450,7 @@ node_modules/
|
|
|
6171
7450
|
}
|
|
6172
7451
|
async function runCapabilityNew(flags, capabilityId) {
|
|
6173
7452
|
try {
|
|
6174
|
-
if (!
|
|
7453
|
+
if (!existsSync23(".omni")) {
|
|
6175
7454
|
console.error("✗ OmniDev is not initialized in this directory.");
|
|
6176
7455
|
console.log("");
|
|
6177
7456
|
console.log(" Run: omnidev init");
|
|
@@ -6195,28 +7474,28 @@ async function runCapabilityNew(flags, capabilityId) {
|
|
|
6195
7474
|
default: defaultPath
|
|
6196
7475
|
});
|
|
6197
7476
|
}
|
|
6198
|
-
if (
|
|
7477
|
+
if (existsSync23(capabilityDir)) {
|
|
6199
7478
|
console.error(`✗ Directory already exists at ${capabilityDir}`);
|
|
6200
7479
|
process.exit(1);
|
|
6201
7480
|
}
|
|
6202
7481
|
const name = toTitleCase(id);
|
|
6203
7482
|
mkdirSync9(capabilityDir, { recursive: true });
|
|
6204
7483
|
const capabilityToml = generateCapabilityToml2({ id, name });
|
|
6205
|
-
await
|
|
6206
|
-
const skillDir =
|
|
7484
|
+
await writeFile22(join25(capabilityDir, "capability.toml"), capabilityToml, "utf-8");
|
|
7485
|
+
const skillDir = join25(capabilityDir, "skills", "getting-started");
|
|
6207
7486
|
mkdirSync9(skillDir, { recursive: true });
|
|
6208
|
-
await
|
|
6209
|
-
const rulesDir =
|
|
7487
|
+
await writeFile22(join25(skillDir, "SKILL.md"), generateSkillTemplate("getting-started"), "utf-8");
|
|
7488
|
+
const rulesDir = join25(capabilityDir, "rules");
|
|
6210
7489
|
mkdirSync9(rulesDir, { recursive: true });
|
|
6211
|
-
await
|
|
6212
|
-
const hooksDir =
|
|
7490
|
+
await writeFile22(join25(rulesDir, "coding-standards.md"), generateRuleTemplate("coding-standards"), "utf-8");
|
|
7491
|
+
const hooksDir = join25(capabilityDir, "hooks");
|
|
6213
7492
|
mkdirSync9(hooksDir, { recursive: true });
|
|
6214
|
-
await
|
|
6215
|
-
await
|
|
7493
|
+
await writeFile22(join25(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
|
|
7494
|
+
await writeFile22(join25(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
|
|
6216
7495
|
if (flags.programmatic) {
|
|
6217
|
-
await
|
|
6218
|
-
await
|
|
6219
|
-
await
|
|
7496
|
+
await writeFile22(join25(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
|
|
7497
|
+
await writeFile22(join25(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
|
|
7498
|
+
await writeFile22(join25(capabilityDir, ".gitignore"), generateGitignore(), "utf-8");
|
|
6220
7499
|
}
|
|
6221
7500
|
console.log(`✓ Created capability: ${name}`);
|
|
6222
7501
|
console.log(` Location: ${capabilityDir}`);
|
|
@@ -6372,7 +7651,7 @@ var capabilityRoutes = buildRouteMap2({
|
|
|
6372
7651
|
});
|
|
6373
7652
|
|
|
6374
7653
|
// src/commands/doctor.ts
|
|
6375
|
-
import { existsSync as
|
|
7654
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
6376
7655
|
import { execFile } from "node:child_process";
|
|
6377
7656
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
6378
7657
|
import { promisify } from "node:util";
|
|
@@ -6462,7 +7741,7 @@ async function checkPackageManager() {
|
|
|
6462
7741
|
}
|
|
6463
7742
|
}
|
|
6464
7743
|
async function checkOmniLocalDir() {
|
|
6465
|
-
const exists =
|
|
7744
|
+
const exists = existsSync24(".omni");
|
|
6466
7745
|
if (!exists) {
|
|
6467
7746
|
return {
|
|
6468
7747
|
name: ".omni/ directory",
|
|
@@ -6479,7 +7758,7 @@ async function checkOmniLocalDir() {
|
|
|
6479
7758
|
}
|
|
6480
7759
|
async function checkConfig() {
|
|
6481
7760
|
const configPath = "omni.toml";
|
|
6482
|
-
if (!
|
|
7761
|
+
if (!existsSync24(configPath)) {
|
|
6483
7762
|
return {
|
|
6484
7763
|
name: "Configuration",
|
|
6485
7764
|
passed: false,
|
|
@@ -6506,7 +7785,7 @@ async function checkConfig() {
|
|
|
6506
7785
|
}
|
|
6507
7786
|
async function checkRootGitignore() {
|
|
6508
7787
|
const gitignorePath = ".gitignore";
|
|
6509
|
-
if (!
|
|
7788
|
+
if (!existsSync24(gitignorePath)) {
|
|
6510
7789
|
return {
|
|
6511
7790
|
name: "Root .gitignore",
|
|
6512
7791
|
passed: false,
|
|
@@ -6540,7 +7819,7 @@ async function checkRootGitignore() {
|
|
|
6540
7819
|
}
|
|
6541
7820
|
async function checkCapabilitiesDir() {
|
|
6542
7821
|
const capabilitiesDirPath = ".omni/capabilities";
|
|
6543
|
-
if (!
|
|
7822
|
+
if (!existsSync24(capabilitiesDirPath)) {
|
|
6544
7823
|
return {
|
|
6545
7824
|
name: "Capabilities Directory",
|
|
6546
7825
|
passed: true,
|
|
@@ -6556,8 +7835,8 @@ async function checkCapabilitiesDir() {
|
|
|
6556
7835
|
|
|
6557
7836
|
// src/commands/init.ts
|
|
6558
7837
|
import { exec } from "node:child_process";
|
|
6559
|
-
import { existsSync as
|
|
6560
|
-
import { readFile as readFile20, writeFile as
|
|
7838
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync10 } from "node:fs";
|
|
7839
|
+
import { readFile as readFile20, writeFile as writeFile23 } from "node:fs/promises";
|
|
6561
7840
|
import { promisify as promisify2 } from "node:util";
|
|
6562
7841
|
init_src();
|
|
6563
7842
|
import { buildCommand as buildCommand4 } from "@stricli/core";
|
|
@@ -6626,7 +7905,7 @@ async function runInit(_flags, providerArg) {
|
|
|
6626
7905
|
}
|
|
6627
7906
|
}
|
|
6628
7907
|
await writeEnabledProviders(providerIds);
|
|
6629
|
-
if (!
|
|
7908
|
+
if (!existsSync25("omni.toml")) {
|
|
6630
7909
|
await writeConfig({
|
|
6631
7910
|
profiles: {
|
|
6632
7911
|
default: {
|
|
@@ -6642,8 +7921,8 @@ async function runInit(_flags, providerArg) {
|
|
|
6642
7921
|
});
|
|
6643
7922
|
await setActiveProfile("default");
|
|
6644
7923
|
}
|
|
6645
|
-
if (!
|
|
6646
|
-
await
|
|
7924
|
+
if (!existsSync25("OMNI.md")) {
|
|
7925
|
+
await writeFile23("OMNI.md", generateOmniMdTemplate(), "utf-8");
|
|
6647
7926
|
}
|
|
6648
7927
|
const config3 = await loadConfig();
|
|
6649
7928
|
const ctx = {
|
|
@@ -6720,7 +7999,7 @@ async function addProviderFilesToGitignore(entries) {
|
|
|
6720
7999
|
async function addToGitignore(entriesToAdd, sectionHeader) {
|
|
6721
8000
|
const gitignorePath = ".gitignore";
|
|
6722
8001
|
let content = "";
|
|
6723
|
-
if (
|
|
8002
|
+
if (existsSync25(gitignorePath)) {
|
|
6724
8003
|
content = await readFile20(gitignorePath, "utf-8");
|
|
6725
8004
|
}
|
|
6726
8005
|
const lines = content.split(`
|
|
@@ -6736,7 +8015,7 @@ async function addToGitignore(entriesToAdd, sectionHeader) {
|
|
|
6736
8015
|
${missingEntries.join(`
|
|
6737
8016
|
`)}
|
|
6738
8017
|
`;
|
|
6739
|
-
await
|
|
8018
|
+
await writeFile23(gitignorePath, content + section, "utf-8");
|
|
6740
8019
|
}
|
|
6741
8020
|
async function getTrackedProviderFiles(files) {
|
|
6742
8021
|
const tracked = [];
|
|
@@ -6752,7 +8031,7 @@ async function getTrackedProviderFiles(files) {
|
|
|
6752
8031
|
}
|
|
6753
8032
|
|
|
6754
8033
|
// src/commands/profile.ts
|
|
6755
|
-
import { existsSync as
|
|
8034
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
6756
8035
|
init_src();
|
|
6757
8036
|
import { buildCommand as buildCommand5, buildRouteMap as buildRouteMap3 } from "@stricli/core";
|
|
6758
8037
|
var listCommand2 = buildCommand5({
|
|
@@ -6796,7 +8075,7 @@ var profileRoutes = buildRouteMap3({
|
|
|
6796
8075
|
});
|
|
6797
8076
|
async function runProfileList() {
|
|
6798
8077
|
try {
|
|
6799
|
-
if (!
|
|
8078
|
+
if (!existsSync26("omni.toml")) {
|
|
6800
8079
|
console.log("✗ No config file found");
|
|
6801
8080
|
console.log(" Run: omnidev init");
|
|
6802
8081
|
process.exit(1);
|
|
@@ -6836,7 +8115,7 @@ async function runProfileList() {
|
|
|
6836
8115
|
}
|
|
6837
8116
|
async function runProfileSet(profileName) {
|
|
6838
8117
|
try {
|
|
6839
|
-
if (!
|
|
8118
|
+
if (!existsSync26("omni.toml")) {
|
|
6840
8119
|
console.log("✗ No config file found");
|
|
6841
8120
|
console.log(" Run: omnidev init");
|
|
6842
8121
|
process.exit(1);
|
|
@@ -6985,7 +8264,7 @@ var providerRoutes = buildRouteMap4({
|
|
|
6985
8264
|
|
|
6986
8265
|
// src/commands/security.ts
|
|
6987
8266
|
init_src();
|
|
6988
|
-
import { existsSync as
|
|
8267
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
6989
8268
|
import { buildCommand as buildCommand7, buildRouteMap as buildRouteMap5 } from "@stricli/core";
|
|
6990
8269
|
var VALID_FINDING_TYPES = [
|
|
6991
8270
|
"unicode_bidi",
|
|
@@ -7102,7 +8381,7 @@ function formatFindingsWithHints(summary) {
|
|
|
7102
8381
|
}
|
|
7103
8382
|
async function runSecurityIssues(flags = {}) {
|
|
7104
8383
|
try {
|
|
7105
|
-
if (!
|
|
8384
|
+
if (!existsSync27("omni.toml")) {
|
|
7106
8385
|
console.log("No config file found");
|
|
7107
8386
|
console.log(" Run: omnidev init");
|
|
7108
8387
|
process.exit(1);
|
|
@@ -7357,7 +8636,7 @@ var securityRoutes = buildRouteMap5({
|
|
|
7357
8636
|
});
|
|
7358
8637
|
|
|
7359
8638
|
// src/commands/sync.ts
|
|
7360
|
-
import { existsSync as
|
|
8639
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
7361
8640
|
init_src();
|
|
7362
8641
|
import { buildCommand as buildCommand8 } from "@stricli/core";
|
|
7363
8642
|
var PROVIDERS_STATE_PATH = ".omni/state/providers.json";
|
|
@@ -7375,7 +8654,7 @@ async function runSync() {
|
|
|
7375
8654
|
const config3 = await loadConfig();
|
|
7376
8655
|
const activeProfile = await getActiveProfile() ?? "default";
|
|
7377
8656
|
let adapters = await getEnabledAdapters();
|
|
7378
|
-
if (!
|
|
8657
|
+
if (!existsSync28(PROVIDERS_STATE_PATH) || adapters.length === 0) {
|
|
7379
8658
|
console.log("No providers configured yet. Select your provider(s):");
|
|
7380
8659
|
const providerIds = await promptForProviders();
|
|
7381
8660
|
await writeEnabledProviders(providerIds);
|
|
@@ -7585,9 +8864,9 @@ async function buildDynamicApp() {
|
|
|
7585
8864
|
security: securityRoutes
|
|
7586
8865
|
};
|
|
7587
8866
|
debug("Core routes registered", Object.keys(routes));
|
|
7588
|
-
const configPath =
|
|
7589
|
-
debug("Checking for config", { configPath, exists:
|
|
7590
|
-
if (
|
|
8867
|
+
const configPath = join26(process.cwd(), "omni.toml");
|
|
8868
|
+
debug("Checking for config", { configPath, exists: existsSync29(configPath), cwd: process.cwd() });
|
|
8869
|
+
if (existsSync29(configPath)) {
|
|
7591
8870
|
try {
|
|
7592
8871
|
debug("Loading capability commands...");
|
|
7593
8872
|
const capabilityCommands = await loadCapabilityCommands();
|
|
@@ -7662,25 +8941,25 @@ async function loadCapabilityCommands() {
|
|
|
7662
8941
|
return commands;
|
|
7663
8942
|
}
|
|
7664
8943
|
async function loadCapabilityExport(capability3) {
|
|
7665
|
-
const capabilityPath =
|
|
7666
|
-
const builtIndexPath =
|
|
7667
|
-
const jsIndexPath =
|
|
7668
|
-
const tsIndexPath =
|
|
8944
|
+
const capabilityPath = join26(process.cwd(), capability3.path);
|
|
8945
|
+
const builtIndexPath = join26(capabilityPath, "dist", "index.js");
|
|
8946
|
+
const jsIndexPath = join26(capabilityPath, "index.js");
|
|
8947
|
+
const tsIndexPath = join26(capabilityPath, "index.ts");
|
|
7669
8948
|
debug(`Checking entry points for '${capability3.id}'`, {
|
|
7670
8949
|
capabilityPath,
|
|
7671
8950
|
builtIndexPath,
|
|
7672
|
-
builtExists:
|
|
8951
|
+
builtExists: existsSync29(builtIndexPath),
|
|
7673
8952
|
jsIndexPath,
|
|
7674
|
-
jsExists:
|
|
8953
|
+
jsExists: existsSync29(jsIndexPath),
|
|
7675
8954
|
tsIndexPath,
|
|
7676
|
-
tsExists:
|
|
8955
|
+
tsExists: existsSync29(tsIndexPath)
|
|
7677
8956
|
});
|
|
7678
8957
|
let indexPath = null;
|
|
7679
|
-
if (
|
|
8958
|
+
if (existsSync29(builtIndexPath)) {
|
|
7680
8959
|
indexPath = builtIndexPath;
|
|
7681
|
-
} else if (
|
|
8960
|
+
} else if (existsSync29(jsIndexPath)) {
|
|
7682
8961
|
indexPath = jsIndexPath;
|
|
7683
|
-
} else if (
|
|
8962
|
+
} else if (existsSync29(tsIndexPath)) {
|
|
7684
8963
|
indexPath = tsIndexPath;
|
|
7685
8964
|
}
|
|
7686
8965
|
if (!indexPath) {
|