@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.
Files changed (2) hide show
  1. package/dist/index.js +1676 -397
  2. 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 as join2 } from "node:path";
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 entries = readdirSync(commandsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
75
- for (const entry of entries) {
76
- if (entry.isDirectory()) {
77
- const commandPath = join2(commandsDir, entry.name, "COMMAND.md");
78
- if (existsSync(commandPath)) {
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
- if (!frontmatter.name || !frontmatter.description) {
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: frontmatter.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 join3 } from "node:path";
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 = join3(capabilityPath, "definition.md");
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 = join3(capabilityPath, "docs");
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 = join3(docsDir, entry.name);
139
+ const docPath = join2(docsDir, entry.name);
131
140
  const content = await readFile2(docPath, "utf-8");
132
141
  docs.push({
133
- name: basename(entry.name, ".md"),
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
- import { join as join4 } from "node:path";
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 = join4(capabilityPath, HOOKS_DIRECTORY);
1718
- const configPath = join4(hooksDir, HOOKS_CONFIG_FILENAME);
1719
- if (!existsSync4(configPath)) {
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 = readFileSync(configPath, "utf-8");
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: validation.valid ? parsed : createEmptyHooksConfig(),
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 configPath = join4(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
1806
- return existsSync4(configPath);
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 join4(capabilityPath, HOOKS_DIRECTORY);
2400
+ return join3(capabilityPath, HOOKS_DIRECTORY);
1810
2401
  }
1811
2402
  function getHooksConfigPath(capabilityPath) {
1812
- return join4(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
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 existsSync5, readdirSync as readdirSync3 } from "node:fs";
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 basename2, join as join5 } from "node:path";
2417
+ import { basename as basename3, join as join4 } from "node:path";
1825
2418
  async function loadRules(capabilityPath, capabilityId) {
1826
- const rulesDir = join5(capabilityPath, "rules");
1827
- if (!existsSync5(rulesDir)) {
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 = join5(rulesDir, entry.name);
2427
+ const rulePath = join4(rulesDir, entry.name);
1835
2428
  const content = await readFile3(rulePath, "utf-8");
1836
2429
  rules.push({
1837
- name: basename2(entry.name, ".md"),
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 existsSync6, readdirSync as readdirSync4 } from "node:fs";
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 join6 } from "node:path";
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 entries = readdirSync4(skillsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1858
- for (const entry of entries) {
1859
- if (entry.isDirectory()) {
1860
- const skillPath = join6(skillsDir, entry.name, "SKILL.md");
1861
- if (existsSync6(skillPath)) {
1862
- const skill = await parseSkillFile(skillPath, capabilityId);
1863
- skills.push(skill);
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 existsSync7, readdirSync as readdirSync5 } from "node:fs";
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 join7 } from "node:path";
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 entries = readdirSync5(subagentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
1900
- for (const entry of entries) {
1901
- if (entry.isDirectory()) {
1902
- const subagentPath = join7(subagentsDir, entry.name, "SUBAGENT.md");
1903
- if (existsSync7(subagentPath)) {
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
- if (!frontmatter.name || !frontmatter.description) {
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: frontmatter.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 existsSync8, readdirSync as readdirSync6 } from "node:fs";
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 join8 } from "node:path";
2564
+ import { join as join7 } from "node:path";
1957
2565
  async function discoverCapabilities() {
1958
2566
  const capabilities = [];
1959
- if (existsSync8(CAPABILITIES_DIR)) {
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 = join8(CAPABILITIES_DIR, entry.name);
1964
- const configPath = join8(entryPath, "capability.toml");
1965
- if (existsSync8(configPath)) {
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 = join8(capabilityPath, "capability.toml");
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 = join8(capabilityPath, "dist", "index.js");
1981
- const jsIndexPath = join8(capabilityPath, "index.js");
1982
- const tsIndexPath = join8(capabilityPath, "index.ts");
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 (existsSync8(builtIndexPath)) {
2592
+ if (existsSync9(builtIndexPath)) {
1985
2593
  indexPath = builtIndexPath;
1986
- } else if (existsSync8(jsIndexPath)) {
2594
+ } else if (existsSync9(jsIndexPath)) {
1987
2595
  indexPath = jsIndexPath;
1988
- } else if (existsSync8(tsIndexPath)) {
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 = join8(process.cwd(), indexPath);
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 = join8(capabilityPath, "types.d.ts");
2016
- if (!existsSync8(typesPath)) {
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 skills = "skills" in exports && Array.isArray(exportsAny.skills) ? convertSkillExports(exportsAny.skills, id) : await loadSkills(capabilityPath, id);
2202
- const rules = "rules" in exports && Array.isArray(exportsAny.rules) ? convertRuleExports(exportsAny.rules, id) : await loadRules(capabilityPath, id);
2203
- const docs = "docs" in exports && Array.isArray(exportsAny.docs) ? convertDocExports(exportsAny.docs, id) : await loadDocs(capabilityPath, id);
2204
- const subagents = "subagents" in exports && Array.isArray(exportsAny.subagents) ? convertSubagentExports(exportsAny.subagents, id) : await loadSubagents(capabilityPath, id);
2205
- const commands = "commands" in exports && Array.isArray(exportsAny.commands) ? convertCommandExports(exportsAny.commands, id) : await loadCommands(capabilityPath, id);
2206
- const typeDefinitionsFromExports = "typeDefinitions" in exports && typeof exportsAny.typeDefinitions === "string" ? exportsAny.typeDefinitions : undefined;
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 gitignore = "gitignore" in exports && Array.isArray(exportsAny.gitignore) ? exportsAny.gitignore : undefined;
2209
- const hooks = loadCapabilityHooks(id, capabilityPath);
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 existsSync9 } from "node:fs";
2245
- import { readFile as readFile7, writeFile as writeFile2 } from "node:fs/promises";
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 (existsSync9(CONFIG_PATH)) {
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 (existsSync9(LOCAL_CONFIG)) {
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 writeFile2(CONFIG_PATH, content, "utf-8");
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 existsSync10, mkdirSync } from "node:fs";
2436
- import { readFile as readFile8, unlink, writeFile as writeFile3 } from "node:fs/promises";
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 (!existsSync10(ACTIVE_PROFILE_PATH)) {
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 writeFile3(ACTIVE_PROFILE_PATH, profileName, "utf-8");
3122
+ await writeFile2(ACTIVE_PROFILE_PATH, profileName, "utf-8");
2452
3123
  }
2453
3124
  async function clearActiveProfileState() {
2454
- if (existsSync10(ACTIVE_PROFILE_PATH)) {
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
- return [...new Set([...expandedAlways, ...expandedProfile])];
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 existsSync11 } from "node:fs";
3369
+ import { existsSync as existsSync12 } from "node:fs";
2696
3370
  import { spawn } from "node:child_process";
2697
- import { cp, mkdir as mkdir2, readdir, readFile as readFile9, rename, rm, stat, writeFile as writeFile4 } from "node:fs/promises";
2698
- import { join as join9 } from "node:path";
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 = join9(capabilityPath, "capability.toml");
2756
- if (existsSync11(tomlPath)) {
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 join9(OMNI_LOCAL, "capabilities", id);
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 (!existsSync11(lockPath)) {
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 mkdir2(join9(OMNI_LOCAL, "capabilities"), { recursive: true });
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 writeFile4(lockPath, content, "utf-8");
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 = join9(currentPath, entry.name);
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 = join9(dirPath, "capability.toml");
2934
- if (existsSync11(capTomlPath)) {
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 = join9(dirPath, ".claude-plugin", "plugin.json");
2945
- if (existsSync11(pluginJsonPath)) {
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 = join9(dirPath, "package.json");
2955
- if (existsSync11(pkgJsonPath)) {
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 = join9(OMNI_LOCAL, "_temp", `_pin-detect-${Date.now()}`);
3736
+ const tempPath = join8(OMNI_LOCAL, "_temp", `_pin-detect-${Date.now()}`);
2969
3737
  try {
2970
- await mkdir2(join9(tempPath, ".."), { recursive: true });
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 ? join9(tempPath, subPath) : tempPath;
2978
- const capTomlPath = join9(checkPath, "capability.toml");
2979
- if (existsSync11(capTomlPath)) {
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 = join9(checkPath, ".claude-plugin", "plugin.json");
2990
- if (existsSync11(pluginJsonPath)) {
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 (existsSync11(tempPath)) {
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 mkdir2(join9(targetPath, ".."), { recursive: true });
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 fetchResult = await spawnCapture("git", ["fetch", "--depth", "1", "origin"], {
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
- const targetRef = ref || "HEAD";
3027
- const lsResult = await spawnCapture("git", ["ls-remote", "origin", targetRef], {
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 (lsResult.exitCode !== 0) {
3031
- throw new Error(`Failed to ls-remote in ${repoPath}: ${lsResult.stderr.trim()}`);
3817
+ if (revParseResult.exitCode !== 0) {
3818
+ throw new Error(`Failed to resolve ${resetTarget} in ${repoPath}: ${revParseResult.stderr.trim()}`);
3032
3819
  }
3033
- const remoteCommit = lsResult.stdout.split("\t")[0];
3034
- if (currentCommit === remoteCommit) {
3820
+ const targetCommit = revParseResult.stdout.trim();
3821
+ if (currentCommit === targetCommit) {
3035
3822
  return false;
3036
3823
  }
3037
- const pullResult = await spawnCapture("git", ["pull", "--ff-only"], { cwd: repoPath });
3038
- if (pullResult.exitCode !== 0) {
3039
- throw new Error(`Failed to pull in ${repoPath}: ${pullResult.stderr.trim()}`);
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 existsSync11(join9(dirPath, "capability.toml"));
3833
+ return existsSync12(join8(dirPath, "capability.toml"));
3045
3834
  }
3046
3835
  async function shouldWrapDirectory(dirPath) {
3047
- if (existsSync11(join9(dirPath, ".claude-plugin", "plugin.json"))) {
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 = join9(dirPath, dirName);
3053
- if (existsSync11(checkPath)) {
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 = join9(basePath, name);
3065
- if (existsSync11(dirPath)) {
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 (!existsSync11(dirPath)) {
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 = join9(dirPath, entry.name);
3870
+ const entryPath = join8(dirPath, entry.name);
3082
3871
  if (entry.isDirectory()) {
3083
3872
  for (const pattern of filePatterns) {
3084
- if (existsSync11(join9(entryPath, pattern))) {
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 = join9(dirPath, ".claude-plugin", "plugin.json");
3106
- if (!existsSync11(pluginJsonPath)) {
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 = join9(dirPath, "README.md");
3131
- if (!existsSync11(readmePath)) {
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 = join9(repoPath, from);
3173
- const toPath = join9(repoPath, to);
3174
- if (existsSync11(fromPath) && !existsSync11(toPath)) {
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 writeFile4(join9(repoPath, "capability.toml"), tomlContent, "utf-8");
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 = join9(OMNI_LOCAL, "_temp", `${id}-repo`);
3274
- if (existsSync11(join9(tempPath, ".git"))) {
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 mkdir2(join9(tempPath, ".."), { recursive: true });
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 = join9(tempPath, config.path);
3284
- if (!existsSync11(sourcePath)) {
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 (existsSync11(targetPath)) {
4074
+ if (existsSync12(targetPath)) {
3288
4075
  await rm(targetPath, { recursive: true });
3289
4076
  }
3290
- await mkdir2(join9(targetPath, ".."), { recursive: true });
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 (existsSync11(join9(targetPath, ".git"))) {
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 (!existsSync11(sourcePath)) {
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 = existsSync11(join9(sourcePath, "capability.toml"));
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 (existsSync11(targetPath)) {
4152
+ if (existsSync12(targetPath)) {
3366
4153
  await rm(targetPath, { recursive: true });
3367
4154
  }
3368
- await mkdir2(join9(targetPath, ".."), { recursive: true });
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 writeFile4(join9(targetPath, "capability.toml"), tomlContent, "utf-8");
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 writeFile4(join9(targetPath, "capability.toml"), tomlContent, "utf-8");
4308
+ await writeFile3(join8(targetPath, "capability.toml"), tomlContent, "utf-8");
3522
4309
  }
3523
4310
  async function isGeneratedMcpCapability(capabilityDir) {
3524
- const tomlPath = join9(capabilityDir, "capability.toml");
3525
- if (!existsSync11(tomlPath)) {
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 = join9(OMNI_LOCAL, "capabilities");
3541
- if (!existsSync11(capabilitiesDir)) {
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 = join9(capabilitiesDir, entry.name);
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 = join9(OMNI_LOCAL, "capabilities");
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 = join9(mcpCapabilitiesDir, id);
4350
+ const targetPath = join8(mcpCapabilitiesDir, id);
3564
4351
  currentMcpIds.add(id);
3565
- await mkdir2(targetPath, { recursive: true });
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 = join9(OMNI_LOCAL, "_temp");
3573
- if (existsSync11(tempDir)) {
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 (!existsSync11(join9(targetPath, ".git"))) {
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 (!existsSync11(capabilityPath)) {
4489
+ if (!existsSync12(capabilityPath)) {
3703
4490
  return "capability directory missing";
3704
4491
  }
3705
4492
  if (lockEntry.commit) {
3706
- const gitDir = join9(capabilityPath, ".git");
3707
- if (existsSync11(gitDir)) {
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 existsSync12 } from "node:fs";
3776
- import { readFile as readFile10, writeFile as writeFile5 } from "node:fs/promises";
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 (!existsSync12(PROVIDER_CONFIG_PATH)) {
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 writeFile5(PROVIDER_CONFIG_PATH, `${lines.join(`
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 existsSync13 } from "node:fs";
3829
- import { readFile as readFile11, writeFile as writeFile6 } from "node:fs/promises";
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 (!existsSync13(CONFIG_PATH2)) {
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 writeFile6(CONFIG_PATH2, content, "utf-8");
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 existsSync14 } from "node:fs";
4057
- import { readFile as readFile12, writeFile as writeFile7 } from "node:fs/promises";
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 (!existsSync14(MCP_JSON_PATH)) {
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 writeFile7(MCP_JSON_PATH, `${JSON.stringify(config2, null, 2)}
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 existsSync15, mkdirSync as mkdirSync2, rmSync } from "node:fs";
4146
- import { readFile as readFile13, writeFile as writeFile8 } from "node:fs/promises";
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 (!existsSync15(MANIFEST_PATH)) {
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 writeFile8(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
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 (existsSync15(skillDir)) {
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 (existsSync15(rulePath)) {
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 existsSync16, mkdirSync as mkdirSync3 } from "node:fs";
4215
- import { readFile as readFile14, writeFile as writeFile9 } from "node:fs/promises";
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 (!existsSync16(PROVIDERS_PATH)) {
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 writeFile9(PROVIDERS_PATH, `${JSON.stringify(state, null, 2)}
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 existsSync17, mkdirSync as mkdirSync4 } from "node:fs";
4257
- import { readFile as readFile15, writeFile as writeFile10 } from "node:fs/promises";
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 (!existsSync17(SECURITY_PATH)) {
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 writeFile10(SECURITY_PATH, `${JSON.stringify(state, null, 2)}
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: existsSync18, readdirSync: readdirSync7, readFileSync: readFileSync2 } = await import("node:fs");
4361
- const { join: join10 } = await import("node:path");
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 (!existsSync18(capabilitiesDir)) {
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 = join10(capabilitiesDir, entry.name);
4384
- const packageJsonPath = join10(capabilityPath, "package.json");
4385
- if (!existsSync18(packageJsonPath)) {
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
- await new Promise((resolve2, reject) => {
4389
- const useNpmCi = hasNpm && existsSync18(join10(capabilityPath, "package-lock.json"));
4390
- const cmd = hasBun ? "bun" : "npm";
4391
- const args = hasBun ? ["install"] : useNpmCi ? ["ci"] : ["install"];
4392
- const proc = spawn2(cmd, args, {
4393
- cwd: capabilityPath,
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
- proc.on("error", (error) => {
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(readFileSync2(packageJsonPath, "utf-8"));
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
- console.warn(`Warning: Capability at ${capabilityPath} has index.ts but no build script.
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 existsSync18 } from "node:fs";
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 join10, relative, resolve as resolve2 } from "node:path";
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(join10(symlinkPath, "..", linkTarget));
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 (!existsSync18(capabilityPath)) {
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 = join10(dirPath, entry.name);
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 existsSync28 } from "node:fs";
6126
+ import { existsSync as existsSync29 } from "node:fs";
5315
6127
  import { createRequire as createRequire2 } from "node:module";
5316
- import { join as join18 } from "node:path";
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 existsSync21 } from "node:fs";
5321
- import { basename as basename3, resolve as resolve3 } from "node:path";
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/cursor-rules.ts
5324
- import { mkdir, writeFile } from "node:fs/promises";
5325
- import { join } from "node:path";
5326
- var CursorRulesWriter = {
5327
- id: "cursor-rules",
5328
- async write(bundle, ctx) {
5329
- const rulesDir = join(ctx.projectRoot, ctx.outputPath);
5330
- await mkdir(rulesDir, { recursive: true });
5331
- const filesWritten = [];
5332
- for (const rule of bundle.rules) {
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
- return {
5338
- filesWritten
5339
- };
6146
+ seen.add(key);
6147
+ uniqueConfigs.push(config);
5340
6148
  }
5341
- };
5342
- // ../adapters/src/writers/hooks.ts
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 existsSync19 } from "node:fs";
5345
- import { mkdir as mkdir3, readFile as readFile17, writeFile as writeFile11 } from "node:fs/promises";
5346
- import { dirname, join as join11 } from "node:path";
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 = join11(ctx.projectRoot, ctx.outputPath);
6173
+ const settingsPath = join10(ctx.projectRoot, ctx.outputPath);
5354
6174
  const parentDir = dirname(settingsPath);
5355
- await mkdir3(parentDir, { recursive: true });
6175
+ await mkdir2(parentDir, { recursive: true });
5356
6176
  const claudeHooks = transformHooksConfig(bundle.hooks, "toClaude");
5357
6177
  let existingSettings = {};
5358
- if (existsSync19(settingsPath)) {
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 writeFile11(settingsPath, `${JSON.stringify(newSettings, null, 2)}
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 existsSync20 } from "node:fs";
5379
- import { mkdir as mkdir4, readFile as readFile18, writeFile as writeFile12 } from "node:fs/promises";
5380
- import { dirname as dirname2, join as join12 } from "node:path";
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 = join12(ctx.projectRoot, ctx.outputPath);
6204
+ const outputFullPath = join11(ctx.projectRoot, ctx.outputPath);
5385
6205
  const parentDir = dirname2(outputFullPath);
5386
6206
  if (parentDir !== ctx.projectRoot) {
5387
- await mkdir4(parentDir, { recursive: true });
6207
+ await mkdir3(parentDir, { recursive: true });
5388
6208
  }
5389
- const omniMdPath = join12(ctx.projectRoot, "OMNI.md");
6209
+ const omniMdPath = join11(ctx.projectRoot, "OMNI.md");
5390
6210
  let omniMdContent = "";
5391
- if (existsSync20(omniMdPath)) {
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 writeFile12(outputFullPath, content, "utf-8");
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 mkdir5, writeFile as writeFile13 } from "node:fs/promises";
5409
- import { join as join13 } from "node:path";
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 = join13(ctx.projectRoot, ctx.outputPath);
5414
- await mkdir5(skillsDir, { recursive: true });
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 = join13(skillsDir, skill.name);
5418
- await mkdir5(skillDir, { recursive: true });
5419
- const skillPath = join13(skillDir, "SKILL.md");
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 writeFile13(skillPath, content, "utf-8");
5427
- filesWritten.push(join13(ctx.outputPath, skill.name, "SKILL.md"));
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/executor.ts
5435
- async function executeWriters(writerConfigs, bundle, projectRoot) {
5436
- const seen = new Set;
5437
- const uniqueConfigs = [];
5438
- let deduplicatedCount = 0;
5439
- for (const config3 of writerConfigs) {
5440
- const key = `${config3.writer.id}:${config3.outputPath}`;
5441
- if (seen.has(key)) {
5442
- deduplicatedCount++;
5443
- continue;
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
- seen.add(key);
5446
- uniqueConfigs.push(config3);
6285
+ return {
6286
+ filesWritten
6287
+ };
5447
6288
  }
5448
- const allFilesWritten = [];
5449
- for (const config3 of uniqueConfigs) {
5450
- const result = await config3.writer.write(bundle, {
5451
- outputPath: config3.outputPath,
5452
- projectRoot
5453
- });
5454
- allFilesWritten.push(...result.filesWritten);
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
- return {
5457
- filesWritten: allFilesWritten,
5458
- deduplicatedCount
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 join14 } from "node:path";
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 = join14(ctx.projectRoot, ".codex");
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 join15 } from "node:path";
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: ".claude/skills/" },
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 = join15(ctx.projectRoot, ".cursor", "rules");
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 join16 } from "node:path";
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 = join16(ctx.projectRoot, ".opencode");
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 basename3(resolvedPath);
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 (!existsSync21("omni.toml")) {
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 (!existsSync21(localPath)) {
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 (!existsSync21("omni.toml")) {
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 existsSync22, mkdirSync as mkdirSync9 } from "node:fs";
5988
- import { writeFile as writeFile14 } from "node:fs/promises";
5989
- import { join as join17 } from "node:path";
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 (!existsSync22(".omni")) {
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 (existsSync22(capabilityDir)) {
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 writeFile14(join17(capabilityDir, "capability.toml"), capabilityToml, "utf-8");
6206
- const skillDir = join17(capabilityDir, "skills", "getting-started");
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 writeFile14(join17(skillDir, "SKILL.md"), generateSkillTemplate("getting-started"), "utf-8");
6209
- const rulesDir = join17(capabilityDir, "rules");
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 writeFile14(join17(rulesDir, "coding-standards.md"), generateRuleTemplate("coding-standards"), "utf-8");
6212
- const hooksDir = join17(capabilityDir, "hooks");
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 writeFile14(join17(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
6215
- await writeFile14(join17(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
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 writeFile14(join17(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
6218
- await writeFile14(join17(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
6219
- await writeFile14(join17(capabilityDir, ".gitignore"), generateGitignore(), "utf-8");
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 existsSync23 } from "node:fs";
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 = existsSync23(".omni");
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 (!existsSync23(configPath)) {
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 (!existsSync23(gitignorePath)) {
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 (!existsSync23(capabilitiesDirPath)) {
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 existsSync24, mkdirSync as mkdirSync10 } from "node:fs";
6560
- import { readFile as readFile20, writeFile as writeFile15 } from "node:fs/promises";
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 (!existsSync24("omni.toml")) {
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 (!existsSync24("OMNI.md")) {
6646
- await writeFile15("OMNI.md", generateOmniMdTemplate(), "utf-8");
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 (existsSync24(gitignorePath)) {
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 writeFile15(gitignorePath, content + section, "utf-8");
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 existsSync25 } from "node:fs";
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 (!existsSync25("omni.toml")) {
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 (!existsSync25("omni.toml")) {
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 existsSync26 } from "node:fs";
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 (!existsSync26("omni.toml")) {
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 existsSync27 } from "node:fs";
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 (!existsSync27(PROVIDERS_STATE_PATH) || adapters.length === 0) {
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 = join18(process.cwd(), "omni.toml");
7589
- debug("Checking for config", { configPath, exists: existsSync28(configPath), cwd: process.cwd() });
7590
- if (existsSync28(configPath)) {
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 = join18(process.cwd(), capability3.path);
7666
- const builtIndexPath = join18(capabilityPath, "dist", "index.js");
7667
- const jsIndexPath = join18(capabilityPath, "index.js");
7668
- const tsIndexPath = join18(capabilityPath, "index.ts");
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: existsSync28(builtIndexPath),
8951
+ builtExists: existsSync29(builtIndexPath),
7673
8952
  jsIndexPath,
7674
- jsExists: existsSync28(jsIndexPath),
8953
+ jsExists: existsSync29(jsIndexPath),
7675
8954
  tsIndexPath,
7676
- tsExists: existsSync28(tsIndexPath)
8955
+ tsExists: existsSync29(tsIndexPath)
7677
8956
  });
7678
8957
  let indexPath = null;
7679
- if (existsSync28(builtIndexPath)) {
8958
+ if (existsSync29(builtIndexPath)) {
7680
8959
  indexPath = builtIndexPath;
7681
- } else if (existsSync28(jsIndexPath)) {
8960
+ } else if (existsSync29(jsIndexPath)) {
7682
8961
  indexPath = jsIndexPath;
7683
- } else if (existsSync28(tsIndexPath)) {
8962
+ } else if (existsSync29(tsIndexPath)) {
7684
8963
  indexPath = tsIndexPath;
7685
8964
  }
7686
8965
  if (!indexPath) {