@omnidev-ai/cli 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1091 -340
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1833,47 +1833,23 @@ function resolveCapabilityRoot(content, capabilityPath) {
|
|
|
1833
1833
|
}
|
|
1834
1834
|
return result;
|
|
1835
1835
|
}
|
|
1836
|
-
function
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
result.description = config.description;
|
|
1836
|
+
function resolveCapabilityRootInValue(value, capabilityPath) {
|
|
1837
|
+
if (typeof value === "string") {
|
|
1838
|
+
return resolveCapabilityRoot(value, capabilityPath);
|
|
1840
1839
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
"PostToolUse",
|
|
1844
|
-
"PermissionRequest",
|
|
1845
|
-
"UserPromptSubmit",
|
|
1846
|
-
"Stop",
|
|
1847
|
-
"SubagentStop",
|
|
1848
|
-
"Notification",
|
|
1849
|
-
"SessionStart",
|
|
1850
|
-
"SessionEnd",
|
|
1851
|
-
"PreCompact"
|
|
1852
|
-
];
|
|
1853
|
-
for (const event of events) {
|
|
1854
|
-
const matchers = config[event];
|
|
1855
|
-
if (matchers) {
|
|
1856
|
-
result[event] = matchers.map((matcher) => ({
|
|
1857
|
-
...matcher,
|
|
1858
|
-
hooks: matcher.hooks.map((hook) => {
|
|
1859
|
-
if (hook.type === "command") {
|
|
1860
|
-
return {
|
|
1861
|
-
...hook,
|
|
1862
|
-
command: resolveCapabilityRoot(hook.command, capabilityPath)
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
if (hook.type === "prompt") {
|
|
1866
|
-
return {
|
|
1867
|
-
...hook,
|
|
1868
|
-
prompt: resolveCapabilityRoot(hook.prompt, capabilityPath)
|
|
1869
|
-
};
|
|
1870
|
-
}
|
|
1871
|
-
return hook;
|
|
1872
|
-
})
|
|
1873
|
-
}));
|
|
1874
|
-
}
|
|
1840
|
+
if (Array.isArray(value)) {
|
|
1841
|
+
return value.map((entry) => resolveCapabilityRootInValue(entry, capabilityPath));
|
|
1875
1842
|
}
|
|
1876
|
-
|
|
1843
|
+
if (value && typeof value === "object") {
|
|
1844
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [
|
|
1845
|
+
key,
|
|
1846
|
+
resolveCapabilityRootInValue(entry, capabilityPath)
|
|
1847
|
+
]));
|
|
1848
|
+
}
|
|
1849
|
+
return value;
|
|
1850
|
+
}
|
|
1851
|
+
function resolveCapabilityRootInConfig(config, capabilityPath) {
|
|
1852
|
+
return resolveCapabilityRootInValue(config, capabilityPath);
|
|
1877
1853
|
}
|
|
1878
1854
|
var REVERSE_MAPPINGS;
|
|
1879
1855
|
var init_variables = __esm(() => {
|
|
@@ -2183,6 +2159,55 @@ function loadHooksFromCapability(capabilityPath, options) {
|
|
|
2183
2159
|
}
|
|
2184
2160
|
return loadJsonHooksFiles(capabilityPath, hooksJsonInDir, hooksJsonAtRoot, hooksDir, opts);
|
|
2185
2161
|
}
|
|
2162
|
+
function splitHooksTomlConfig(parsed) {
|
|
2163
|
+
const topLevelIssues = [];
|
|
2164
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2165
|
+
return {
|
|
2166
|
+
sharedConfig: parsed,
|
|
2167
|
+
providerConfigs: undefined,
|
|
2168
|
+
topLevelIssues
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
const parsedObj = parsed;
|
|
2172
|
+
const sharedConfig = {};
|
|
2173
|
+
const providerConfigs = {};
|
|
2174
|
+
for (const [key, value] of Object.entries(parsedObj)) {
|
|
2175
|
+
if (key === "description" || HOOK_EVENTS.includes(key)) {
|
|
2176
|
+
sharedConfig[key] = value;
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
if (PROVIDER_SECTION_KEYS.includes(key)) {
|
|
2180
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2181
|
+
providerConfigs[key] = value;
|
|
2182
|
+
} else {
|
|
2183
|
+
topLevelIssues.push({
|
|
2184
|
+
severity: "error",
|
|
2185
|
+
code: "HOOKS_INVALID_TOML",
|
|
2186
|
+
message: `[${key}] must be a table`
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
2189
|
+
continue;
|
|
2190
|
+
}
|
|
2191
|
+
topLevelIssues.push({
|
|
2192
|
+
severity: "error",
|
|
2193
|
+
code: "HOOKS_UNKNOWN_EVENT",
|
|
2194
|
+
message: `Unknown hook event: "${key}"`,
|
|
2195
|
+
suggestion: `Valid events are: ${HOOK_EVENTS.join(", ")}, plus [claude] and [codex] sections`
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
return {
|
|
2199
|
+
sharedConfig,
|
|
2200
|
+
providerConfigs: Object.keys(providerConfigs).length > 0 ? providerConfigs : undefined,
|
|
2201
|
+
topLevelIssues
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
function prefixValidationIssues(issues, sectionLabel) {
|
|
2205
|
+
return issues.map((issue) => ({
|
|
2206
|
+
...issue,
|
|
2207
|
+
message: `[${sectionLabel}] ${issue.message}`,
|
|
2208
|
+
...issue.suggestion ? { suggestion: `[${sectionLabel}] ${issue.suggestion}` } : {}
|
|
2209
|
+
}));
|
|
2210
|
+
}
|
|
2186
2211
|
function loadTomlHooks(capabilityPath, configPath, hooksDir, opts) {
|
|
2187
2212
|
let rawContent;
|
|
2188
2213
|
try {
|
|
@@ -2234,25 +2259,56 @@ function loadTomlHooks(capabilityPath, configPath, hooksDir, opts) {
|
|
|
2234
2259
|
loadError: `Invalid TOML: ${error instanceof Error ? error.message : String(error)}`
|
|
2235
2260
|
};
|
|
2236
2261
|
}
|
|
2262
|
+
const {
|
|
2263
|
+
sharedConfig,
|
|
2264
|
+
providerConfigs: rawProviderConfigs,
|
|
2265
|
+
topLevelIssues
|
|
2266
|
+
} = splitHooksTomlConfig(parsed);
|
|
2237
2267
|
let validation;
|
|
2238
2268
|
if (opts.validate) {
|
|
2239
|
-
|
|
2269
|
+
const sharedValidation = validateHooksConfig(sharedConfig, {
|
|
2240
2270
|
basePath: hooksDir,
|
|
2241
2271
|
checkScripts: opts.checkScripts ?? false
|
|
2242
2272
|
});
|
|
2273
|
+
const providerErrors = [];
|
|
2274
|
+
const providerWarnings = [];
|
|
2275
|
+
for (const [provider, providerConfig] of Object.entries(rawProviderConfigs ?? {})) {
|
|
2276
|
+
const providerValidation = validateHooksConfig(providerConfig, {
|
|
2277
|
+
basePath: hooksDir,
|
|
2278
|
+
checkScripts: opts.checkScripts ?? false
|
|
2279
|
+
});
|
|
2280
|
+
providerErrors.push(...prefixValidationIssues(providerValidation.errors, provider));
|
|
2281
|
+
providerWarnings.push(...prefixValidationIssues(providerValidation.warnings, provider));
|
|
2282
|
+
}
|
|
2283
|
+
validation = {
|
|
2284
|
+
valid: sharedValidation.valid && topLevelIssues.length === 0 && providerErrors.length === 0,
|
|
2285
|
+
errors: [...topLevelIssues, ...sharedValidation.errors, ...providerErrors],
|
|
2286
|
+
warnings: [...sharedValidation.warnings, ...providerWarnings]
|
|
2287
|
+
};
|
|
2243
2288
|
} else {
|
|
2244
2289
|
validation = createEmptyValidationResult();
|
|
2245
2290
|
}
|
|
2246
|
-
let config = validation.valid ?
|
|
2291
|
+
let config = validation.valid ? sharedConfig : createEmptyHooksConfig();
|
|
2292
|
+
let providerConfigs = validation.valid ? rawProviderConfigs : undefined;
|
|
2247
2293
|
if (opts.resolveCapabilityRoot && validation.valid) {
|
|
2248
2294
|
config = resolveCapabilityRootInConfig(config, capabilityPath);
|
|
2295
|
+
if (providerConfigs) {
|
|
2296
|
+
providerConfigs = Object.fromEntries(Object.entries(providerConfigs).map(([provider, providerConfig]) => [
|
|
2297
|
+
provider,
|
|
2298
|
+
resolveCapabilityRootInValue(providerConfig, capabilityPath)
|
|
2299
|
+
]));
|
|
2300
|
+
}
|
|
2249
2301
|
}
|
|
2250
|
-
|
|
2302
|
+
const result = {
|
|
2251
2303
|
config,
|
|
2252
2304
|
validation,
|
|
2253
2305
|
found: true,
|
|
2254
2306
|
configPath
|
|
2255
2307
|
};
|
|
2308
|
+
if (providerConfigs) {
|
|
2309
|
+
result.providerConfigs = providerConfigs;
|
|
2310
|
+
}
|
|
2311
|
+
return result;
|
|
2256
2312
|
}
|
|
2257
2313
|
function loadJsonHooksFiles(capabilityPath, hooksJsonInDir, hooksJsonAtRoot, hooksDir, opts) {
|
|
2258
2314
|
const configs = [];
|
|
@@ -2348,6 +2404,7 @@ function loadCapabilityHooks(capabilityName, capabilityPath, options) {
|
|
|
2348
2404
|
capabilityName,
|
|
2349
2405
|
capabilityPath,
|
|
2350
2406
|
config: result.config,
|
|
2407
|
+
...result.providerConfigs ? { providerConfigs: result.providerConfigs } : {},
|
|
2351
2408
|
validation: result.validation
|
|
2352
2409
|
};
|
|
2353
2410
|
}
|
|
@@ -2363,6 +2420,7 @@ function getHooksDirectory(capabilityPath) {
|
|
|
2363
2420
|
function getHooksConfigPath(capabilityPath) {
|
|
2364
2421
|
return join3(capabilityPath, HOOKS_DIRECTORY, HOOKS_CONFIG_FILENAME);
|
|
2365
2422
|
}
|
|
2423
|
+
var PROVIDER_SECTION_KEYS;
|
|
2366
2424
|
var init_loader = __esm(() => {
|
|
2367
2425
|
init_dist();
|
|
2368
2426
|
init_constants();
|
|
@@ -2370,6 +2428,7 @@ var init_loader = __esm(() => {
|
|
|
2370
2428
|
init_variables();
|
|
2371
2429
|
init_json_loader();
|
|
2372
2430
|
init_constants();
|
|
2431
|
+
PROVIDER_SECTION_KEYS = ["claude", "codex"];
|
|
2373
2432
|
});
|
|
2374
2433
|
|
|
2375
2434
|
// ../core/src/providers.ts
|
|
@@ -2638,7 +2697,10 @@ async function loadSkills(capabilityPath, capabilityId, variables) {
|
|
|
2638
2697
|
const skillPath = join6(dir, entry.name, "SKILL.md");
|
|
2639
2698
|
if (existsSync8(skillPath)) {
|
|
2640
2699
|
const skill = await parseSkillFile(skillPath, capabilityId, resolvedVariables, `skill file ${skillPath}`);
|
|
2641
|
-
skills.push(
|
|
2700
|
+
skills.push({
|
|
2701
|
+
...skill,
|
|
2702
|
+
sourcePath: join6(dir, entry.name)
|
|
2703
|
+
});
|
|
2642
2704
|
}
|
|
2643
2705
|
}
|
|
2644
2706
|
}
|
|
@@ -2665,75 +2727,235 @@ import { readFile as readFile6 } from "node:fs/promises";
|
|
|
2665
2727
|
import { basename as basename4, join as join7 } from "node:path";
|
|
2666
2728
|
async function loadSubagents(capabilityPath, capabilityId) {
|
|
2667
2729
|
const subagents = [];
|
|
2668
|
-
const
|
|
2669
|
-
for (const dirName of possibleDirNames) {
|
|
2730
|
+
for (const dirName of POSSIBLE_DIR_NAMES) {
|
|
2670
2731
|
const dir = join7(capabilityPath, dirName);
|
|
2671
2732
|
if (!existsSync9(dir)) {
|
|
2672
2733
|
continue;
|
|
2673
2734
|
}
|
|
2735
|
+
const rootSubagent = await parseSubagentDirectory(dir, capabilityId);
|
|
2736
|
+
if (rootSubagent) {
|
|
2737
|
+
subagents.push(rootSubagent);
|
|
2738
|
+
}
|
|
2674
2739
|
const entries = readdirSync5(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
2675
2740
|
for (const entry of entries) {
|
|
2676
2741
|
if (entry.isDirectory()) {
|
|
2677
|
-
|
|
2678
|
-
if (
|
|
2679
|
-
subagentPath = join7(dir, entry.name, "AGENT.md");
|
|
2680
|
-
}
|
|
2681
|
-
if (existsSync9(subagentPath)) {
|
|
2682
|
-
const subagent = await parseSubagentFile(subagentPath, capabilityId);
|
|
2742
|
+
const subagent = await parseSubagentDirectory(join7(dir, entry.name), capabilityId);
|
|
2743
|
+
if (subagent) {
|
|
2683
2744
|
subagents.push(subagent);
|
|
2684
2745
|
}
|
|
2685
2746
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
2747
|
+
if (PROMPT_FILE_NAMES.includes(entry.name) || LEGACY_AGENT_FILES.includes(entry.name)) {
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2686
2750
|
const subagentPath = join7(dir, entry.name);
|
|
2687
|
-
const
|
|
2688
|
-
subagents.push(
|
|
2751
|
+
const content = await readFile6(subagentPath, "utf-8");
|
|
2752
|
+
subagents.push(parseLegacySubagentMarkdown(content, capabilityId, subagentPath, basename4(subagentPath, ".md")));
|
|
2689
2753
|
}
|
|
2690
2754
|
}
|
|
2691
2755
|
}
|
|
2692
2756
|
return subagents;
|
|
2693
2757
|
}
|
|
2694
|
-
|
|
2695
|
-
const content = await readFile6(filePath, "utf-8");
|
|
2758
|
+
function parseLegacySubagentMarkdown(content, capabilityId, sourceLabel, inferredName) {
|
|
2696
2759
|
const parsed = parseFrontmatterWithMarkdown(content);
|
|
2697
2760
|
if (!parsed) {
|
|
2698
|
-
throw new Error(`Invalid SUBAGENT.md format at ${
|
|
2761
|
+
throw new Error(`Invalid SUBAGENT.md format at ${sourceLabel}: missing YAML frontmatter`);
|
|
2699
2762
|
}
|
|
2700
2763
|
const frontmatter = parsed.frontmatter;
|
|
2701
|
-
const
|
|
2702
|
-
const inferredName = basename4(filePath, ".md").replace(/^SUBAGENT$/i, "").replace(/^AGENT$/i, "");
|
|
2703
|
-
const name = frontmatter.name || inferredName;
|
|
2764
|
+
const name = frontmatter.name || inferredName?.replace(/^SUBAGENT$/i, "").replace(/^AGENT$/i, "") || undefined;
|
|
2704
2765
|
if (!name || !frontmatter.description) {
|
|
2705
|
-
throw new Error(`Invalid SUBAGENT.md at ${
|
|
2766
|
+
throw new Error(`Invalid SUBAGENT.md at ${sourceLabel}: name and description required`);
|
|
2706
2767
|
}
|
|
2707
|
-
const
|
|
2708
|
-
name,
|
|
2709
|
-
description: frontmatter.description,
|
|
2710
|
-
systemPrompt: systemPrompt.trim(),
|
|
2711
|
-
capabilityId
|
|
2712
|
-
};
|
|
2768
|
+
const claude = {};
|
|
2713
2769
|
if (frontmatter.tools) {
|
|
2714
|
-
|
|
2770
|
+
claude.tools = parseCommaSeparatedList(frontmatter.tools);
|
|
2715
2771
|
}
|
|
2716
2772
|
if (frontmatter.disallowedTools) {
|
|
2717
|
-
|
|
2773
|
+
claude.disallowedTools = parseCommaSeparatedList(frontmatter.disallowedTools);
|
|
2718
2774
|
}
|
|
2719
2775
|
if (frontmatter.model) {
|
|
2720
|
-
|
|
2776
|
+
claude.model = frontmatter.model;
|
|
2721
2777
|
}
|
|
2722
2778
|
if (frontmatter.permissionMode) {
|
|
2723
|
-
|
|
2779
|
+
claude.permissionMode = frontmatter.permissionMode;
|
|
2724
2780
|
}
|
|
2725
2781
|
if (frontmatter.skills) {
|
|
2726
|
-
|
|
2782
|
+
claude.skills = parseCommaSeparatedList(frontmatter.skills);
|
|
2727
2783
|
}
|
|
2728
2784
|
if (frontmatter.hooks) {
|
|
2729
|
-
|
|
2785
|
+
claude.hooks = frontmatter.hooks;
|
|
2730
2786
|
}
|
|
2731
|
-
|
|
2787
|
+
const subagent = {
|
|
2788
|
+
name,
|
|
2789
|
+
description: frontmatter.description,
|
|
2790
|
+
systemPrompt: parsed.markdown.trim(),
|
|
2791
|
+
capabilityId
|
|
2792
|
+
};
|
|
2793
|
+
if (hasClaudeConfig(claude)) {
|
|
2794
|
+
subagent.claude = claude;
|
|
2795
|
+
}
|
|
2796
|
+
return withClaudeCompatibilityAliases(subagent);
|
|
2797
|
+
}
|
|
2798
|
+
function parseSubagentManifest(agentTomlContent, promptContent, capabilityId, sourceLabel) {
|
|
2799
|
+
let parsed;
|
|
2800
|
+
try {
|
|
2801
|
+
parsed = parse(agentTomlContent);
|
|
2802
|
+
} catch (error) {
|
|
2803
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2804
|
+
throw new Error(`Invalid agent.toml at ${sourceLabel}: ${message}`);
|
|
2805
|
+
}
|
|
2806
|
+
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
2807
|
+
throw new Error(`Invalid agent.toml at ${sourceLabel}: name is required`);
|
|
2808
|
+
}
|
|
2809
|
+
if (typeof parsed.description !== "string" || parsed.description.trim().length === 0) {
|
|
2810
|
+
throw new Error(`Invalid agent.toml at ${sourceLabel}: description is required`);
|
|
2811
|
+
}
|
|
2812
|
+
const claude = parseClaudeConfig(parsed.claude, sourceLabel);
|
|
2813
|
+
const codex = parseCodexConfig(parsed.codex, sourceLabel);
|
|
2814
|
+
const subagent = {
|
|
2815
|
+
name: parsed.name.trim(),
|
|
2816
|
+
description: parsed.description.trim(),
|
|
2817
|
+
systemPrompt: promptContent.trim(),
|
|
2818
|
+
capabilityId
|
|
2819
|
+
};
|
|
2820
|
+
if (claude) {
|
|
2821
|
+
subagent.claude = claude;
|
|
2822
|
+
}
|
|
2823
|
+
if (codex) {
|
|
2824
|
+
subagent.codex = codex;
|
|
2825
|
+
}
|
|
2826
|
+
return withClaudeCompatibilityAliases(subagent);
|
|
2827
|
+
}
|
|
2828
|
+
async function parseSubagentDirectory(dirPath, capabilityId) {
|
|
2829
|
+
const manifestPath = findFirstExisting(dirPath, MANIFEST_FILE_NAMES);
|
|
2830
|
+
const promptPath = findFirstExisting(dirPath, PROMPT_FILE_NAMES);
|
|
2831
|
+
if (manifestPath || promptPath) {
|
|
2832
|
+
if (!manifestPath || !promptPath) {
|
|
2833
|
+
throw new Error(`Invalid subagent directory at ${dirPath}: agent.toml and prompt.md must both be present`);
|
|
2834
|
+
}
|
|
2835
|
+
const [agentTomlContent, promptContent] = await Promise.all([
|
|
2836
|
+
readFile6(manifestPath, "utf-8"),
|
|
2837
|
+
readFile6(promptPath, "utf-8")
|
|
2838
|
+
]);
|
|
2839
|
+
return parseSubagentManifest(agentTomlContent, promptContent, capabilityId, manifestPath);
|
|
2840
|
+
}
|
|
2841
|
+
const legacyPath = findFirstExisting(dirPath, LEGACY_AGENT_FILES);
|
|
2842
|
+
if (!legacyPath) {
|
|
2843
|
+
return null;
|
|
2844
|
+
}
|
|
2845
|
+
const content = await readFile6(legacyPath, "utf-8");
|
|
2846
|
+
return parseLegacySubagentMarkdown(content, capabilityId, legacyPath);
|
|
2847
|
+
}
|
|
2848
|
+
function findFirstExisting(dirPath, fileNames) {
|
|
2849
|
+
for (const fileName of fileNames) {
|
|
2850
|
+
const filePath = join7(dirPath, fileName);
|
|
2851
|
+
if (existsSync9(filePath)) {
|
|
2852
|
+
return filePath;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
function parseClaudeConfig(value, sourceLabel) {
|
|
2858
|
+
if (!value || typeof value !== "object") {
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
const claude = {};
|
|
2862
|
+
if (value.tools !== undefined) {
|
|
2863
|
+
claude.tools = readStringArray(value.tools, `${sourceLabel} [claude].tools`);
|
|
2864
|
+
}
|
|
2865
|
+
if (value.disallowed_tools !== undefined) {
|
|
2866
|
+
claude.disallowedTools = readStringArray(value.disallowed_tools, `${sourceLabel} [claude].disallowed_tools`);
|
|
2867
|
+
}
|
|
2868
|
+
if (value.model !== undefined) {
|
|
2869
|
+
claude.model = readEnum(value.model, ["sonnet", "opus", "haiku", "inherit"], `${sourceLabel} [claude].model`);
|
|
2870
|
+
}
|
|
2871
|
+
if (value.permission_mode !== undefined) {
|
|
2872
|
+
claude.permissionMode = readEnum(value.permission_mode, ["default", "acceptEdits", "dontAsk", "bypassPermissions", "plan"], `${sourceLabel} [claude].permission_mode`);
|
|
2873
|
+
}
|
|
2874
|
+
if (value.skills !== undefined) {
|
|
2875
|
+
claude.skills = readStringArray(value.skills, `${sourceLabel} [claude].skills`);
|
|
2876
|
+
}
|
|
2877
|
+
if (value.hooks !== undefined) {
|
|
2878
|
+
if (typeof value.hooks !== "object" || value.hooks === null) {
|
|
2879
|
+
throw new Error(`Invalid ${sourceLabel} [claude].hooks: expected a table`);
|
|
2880
|
+
}
|
|
2881
|
+
claude.hooks = value.hooks;
|
|
2882
|
+
}
|
|
2883
|
+
return hasClaudeConfig(claude) ? claude : undefined;
|
|
2884
|
+
}
|
|
2885
|
+
function parseCodexConfig(value, sourceLabel) {
|
|
2886
|
+
if (!value || typeof value !== "object") {
|
|
2887
|
+
return;
|
|
2888
|
+
}
|
|
2889
|
+
const codex = {};
|
|
2890
|
+
if (value.model !== undefined) {
|
|
2891
|
+
codex.model = readString(value.model, `${sourceLabel} [codex].model`);
|
|
2892
|
+
}
|
|
2893
|
+
if (value.model_reasoning_effort !== undefined) {
|
|
2894
|
+
codex.modelReasoningEffort = readEnum(value.model_reasoning_effort, ["low", "medium", "high", "xhigh"], `${sourceLabel} [codex].model_reasoning_effort`);
|
|
2895
|
+
}
|
|
2896
|
+
if (value.sandbox_mode !== undefined) {
|
|
2897
|
+
codex.sandboxMode = readEnum(value.sandbox_mode, ["read-only", "workspace-write", "danger-full-access"], `${sourceLabel} [codex].sandbox_mode`);
|
|
2898
|
+
}
|
|
2899
|
+
if (value.nickname_candidates !== undefined) {
|
|
2900
|
+
codex.nicknameCandidates = readStringArray(value.nickname_candidates, `${sourceLabel} [codex].nickname_candidates`);
|
|
2901
|
+
}
|
|
2902
|
+
return Object.keys(codex).length > 0 ? codex : undefined;
|
|
2903
|
+
}
|
|
2904
|
+
function withClaudeCompatibilityAliases(subagent) {
|
|
2905
|
+
if (subagent.claude) {
|
|
2906
|
+
if (subagent.claude.tools) {
|
|
2907
|
+
subagent.tools = subagent.claude.tools;
|
|
2908
|
+
}
|
|
2909
|
+
if (subagent.claude.disallowedTools) {
|
|
2910
|
+
subagent.disallowedTools = subagent.claude.disallowedTools;
|
|
2911
|
+
}
|
|
2912
|
+
if (subagent.claude.model) {
|
|
2913
|
+
subagent.model = subagent.claude.model;
|
|
2914
|
+
}
|
|
2915
|
+
if (subagent.claude.permissionMode) {
|
|
2916
|
+
subagent.permissionMode = subagent.claude.permissionMode;
|
|
2917
|
+
}
|
|
2918
|
+
if (subagent.claude.skills) {
|
|
2919
|
+
subagent.skills = subagent.claude.skills;
|
|
2920
|
+
}
|
|
2921
|
+
if (subagent.claude.hooks) {
|
|
2922
|
+
subagent.hooks = subagent.claude.hooks;
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
return subagent;
|
|
2926
|
+
}
|
|
2927
|
+
function hasClaudeConfig(config) {
|
|
2928
|
+
return Object.keys(config).length > 0;
|
|
2929
|
+
}
|
|
2930
|
+
function readString(value, fieldName) {
|
|
2931
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2932
|
+
throw new Error(`Invalid ${fieldName}: expected a non-empty string`);
|
|
2933
|
+
}
|
|
2934
|
+
return value.trim();
|
|
2935
|
+
}
|
|
2936
|
+
function readStringArray(value, fieldName) {
|
|
2937
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
2938
|
+
throw new Error(`Invalid ${fieldName}: expected an array of strings`);
|
|
2939
|
+
}
|
|
2940
|
+
return value.map((item) => item.trim()).filter(Boolean);
|
|
2941
|
+
}
|
|
2942
|
+
function readEnum(value, allowed, fieldName) {
|
|
2943
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
2944
|
+
throw new Error(`Invalid ${fieldName}: expected one of ${allowed.join(", ")}`);
|
|
2945
|
+
}
|
|
2946
|
+
return value;
|
|
2732
2947
|
}
|
|
2733
2948
|
function parseCommaSeparatedList(value) {
|
|
2734
2949
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2735
2950
|
}
|
|
2736
|
-
var
|
|
2951
|
+
var POSSIBLE_DIR_NAMES, LEGACY_AGENT_FILES, MANIFEST_FILE_NAMES, PROMPT_FILE_NAMES;
|
|
2952
|
+
var init_subagents = __esm(() => {
|
|
2953
|
+
init_dist();
|
|
2954
|
+
POSSIBLE_DIR_NAMES = ["subagents", "agents", "agent", "subagent"];
|
|
2955
|
+
LEGACY_AGENT_FILES = ["SUBAGENT.md", "subagent.md", "AGENT.md", "agent.md", "Agent.md"];
|
|
2956
|
+
MANIFEST_FILE_NAMES = ["agent.toml", "AGENT.toml"];
|
|
2957
|
+
PROMPT_FILE_NAMES = ["prompt.md", "PROMPT.md"];
|
|
2958
|
+
});
|
|
2737
2959
|
|
|
2738
2960
|
// ../core/src/capability/loader.ts
|
|
2739
2961
|
import { existsSync as existsSync10, readdirSync as readdirSync6 } from "node:fs";
|
|
@@ -2832,75 +3054,19 @@ function convertDocExports(docExports, capabilityId) {
|
|
|
2832
3054
|
});
|
|
2833
3055
|
}
|
|
2834
3056
|
function convertSubagentExports(subagentExports, capabilityId) {
|
|
2835
|
-
return subagentExports.map((subagentExport) => {
|
|
3057
|
+
return subagentExports.map((subagentExport, index) => {
|
|
2836
3058
|
const exportObj = subagentExport;
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
let systemPrompt = exportObj.subagentMd;
|
|
2842
|
-
let tools;
|
|
2843
|
-
let disallowedTools;
|
|
2844
|
-
let model;
|
|
2845
|
-
let permissionMode;
|
|
2846
|
-
let skills;
|
|
2847
|
-
if (lines[0]?.trim() === "---") {
|
|
2848
|
-
const endIndex = lines.findIndex((line, i) => i > 0 && line.trim() === "---");
|
|
2849
|
-
if (endIndex > 0) {
|
|
2850
|
-
const frontmatter = lines.slice(1, endIndex);
|
|
2851
|
-
systemPrompt = lines.slice(endIndex + 1).join(`
|
|
2852
|
-
`).trim();
|
|
2853
|
-
for (const line of frontmatter) {
|
|
2854
|
-
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
2855
|
-
if (match?.[1] && match[2]) {
|
|
2856
|
-
const key = match[1];
|
|
2857
|
-
const value = match[2].replace(/^["']|["']$/g, "");
|
|
2858
|
-
switch (key) {
|
|
2859
|
-
case "name":
|
|
2860
|
-
name = value;
|
|
2861
|
-
break;
|
|
2862
|
-
case "description":
|
|
2863
|
-
description = value;
|
|
2864
|
-
break;
|
|
2865
|
-
case "tools":
|
|
2866
|
-
tools = value.split(",").map((t) => t.trim());
|
|
2867
|
-
break;
|
|
2868
|
-
case "disallowedTools":
|
|
2869
|
-
disallowedTools = value.split(",").map((t) => t.trim());
|
|
2870
|
-
break;
|
|
2871
|
-
case "model":
|
|
2872
|
-
model = value;
|
|
2873
|
-
break;
|
|
2874
|
-
case "permissionMode":
|
|
2875
|
-
permissionMode = value;
|
|
2876
|
-
break;
|
|
2877
|
-
case "skills":
|
|
2878
|
-
skills = value.split(",").map((s) => s.trim());
|
|
2879
|
-
break;
|
|
2880
|
-
}
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
3059
|
+
const sourceLabel = `programmatic subagent export[${index}]`;
|
|
3060
|
+
if (typeof exportObj.agentToml === "string" || typeof exportObj.promptMd === "string") {
|
|
3061
|
+
if (typeof exportObj.agentToml !== "string" || typeof exportObj.promptMd !== "string") {
|
|
3062
|
+
throw new Error(`Invalid ${sourceLabel}: agentToml and promptMd must both be provided`);
|
|
2883
3063
|
}
|
|
3064
|
+
return parseSubagentManifest(exportObj.agentToml, exportObj.promptMd, capabilityId, sourceLabel);
|
|
2884
3065
|
}
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
capabilityId
|
|
2890
|
-
};
|
|
2891
|
-
if (tools)
|
|
2892
|
-
result.tools = tools;
|
|
2893
|
-
if (disallowedTools)
|
|
2894
|
-
result.disallowedTools = disallowedTools;
|
|
2895
|
-
if (model) {
|
|
2896
|
-
result.model = model;
|
|
2897
|
-
}
|
|
2898
|
-
if (permissionMode) {
|
|
2899
|
-
result.permissionMode = permissionMode;
|
|
2900
|
-
}
|
|
2901
|
-
if (skills)
|
|
2902
|
-
result.skills = skills;
|
|
2903
|
-
return result;
|
|
3066
|
+
if (typeof exportObj.subagentMd === "string") {
|
|
3067
|
+
return parseLegacySubagentMarkdown(exportObj.subagentMd, capabilityId, sourceLabel, `subagent-${index + 1}`);
|
|
3068
|
+
}
|
|
3069
|
+
throw new Error(`Invalid ${sourceLabel}: expected agentToml + promptMd or subagentMd`);
|
|
2904
3070
|
});
|
|
2905
3071
|
}
|
|
2906
3072
|
function convertCommandExports(commandExports, capabilityId) {
|
|
@@ -3524,7 +3690,7 @@ import { existsSync as existsSync13 } from "node:fs";
|
|
|
3524
3690
|
import { spawn } from "node:child_process";
|
|
3525
3691
|
import { cp, mkdir, readdir, readFile as readFile10, rename, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
|
|
3526
3692
|
import { join as join9 } from "node:path";
|
|
3527
|
-
import { createHash } from "node:crypto";
|
|
3693
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
3528
3694
|
async function spawnCapture(command, args, options) {
|
|
3529
3695
|
const timeout = options?.timeout ?? 60000;
|
|
3530
3696
|
return await new Promise((resolve2, reject) => {
|
|
@@ -3733,7 +3899,7 @@ function escapeTomlString(value) {
|
|
|
3733
3899
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
3734
3900
|
}
|
|
3735
3901
|
async function computeContentHash(dirPath, excludePatterns = CONTENT_HASH_EXCLUDES) {
|
|
3736
|
-
const hash =
|
|
3902
|
+
const hash = createHash2("sha256");
|
|
3737
3903
|
const files = [];
|
|
3738
3904
|
async function collectFiles(currentPath, relativeTo) {
|
|
3739
3905
|
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
@@ -4758,7 +4924,15 @@ var init_sources = __esm(() => {
|
|
|
4758
4924
|
/^copying(?:\..+)?$/i
|
|
4759
4925
|
];
|
|
4760
4926
|
SKILL_FILES = ["SKILL.md", "skill.md", "Skill.md"];
|
|
4761
|
-
AGENT_FILES = [
|
|
4927
|
+
AGENT_FILES = [
|
|
4928
|
+
"AGENT.md",
|
|
4929
|
+
"agent.md",
|
|
4930
|
+
"Agent.md",
|
|
4931
|
+
"SUBAGENT.md",
|
|
4932
|
+
"subagent.md",
|
|
4933
|
+
"agent.toml",
|
|
4934
|
+
"AGENT.toml"
|
|
4935
|
+
];
|
|
4762
4936
|
COMMAND_FILES = ["COMMAND.md", "command.md", "Command.md"];
|
|
4763
4937
|
CONTENT_HASH_EXCLUDES = [
|
|
4764
4938
|
".git",
|
|
@@ -4786,6 +4960,211 @@ var init_capability = __esm(() => {
|
|
|
4786
4960
|
init_subagents();
|
|
4787
4961
|
});
|
|
4788
4962
|
|
|
4963
|
+
// ../core/src/hooks/provider-config.ts
|
|
4964
|
+
function getProviderDisplayName(providerId) {
|
|
4965
|
+
switch (providerId) {
|
|
4966
|
+
case "claude-code":
|
|
4967
|
+
return "Claude Code";
|
|
4968
|
+
case "codex":
|
|
4969
|
+
return "Codex";
|
|
4970
|
+
case "cursor":
|
|
4971
|
+
return "Cursor";
|
|
4972
|
+
case "opencode":
|
|
4973
|
+
return "OpenCode";
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
function hasOwnKey(value, key) {
|
|
4977
|
+
return Object.hasOwn(value, key);
|
|
4978
|
+
}
|
|
4979
|
+
function hasHooksInConfig(config) {
|
|
4980
|
+
if (!config) {
|
|
4981
|
+
return false;
|
|
4982
|
+
}
|
|
4983
|
+
for (const event of HOOK_EVENTS) {
|
|
4984
|
+
const matchers = config[event];
|
|
4985
|
+
if (Array.isArray(matchers) && matchers.length > 0) {
|
|
4986
|
+
return true;
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
return false;
|
|
4990
|
+
}
|
|
4991
|
+
function matcherMatchesAny(matcher, values) {
|
|
4992
|
+
if (matcher === undefined || matcher === "" || matcher === "*") {
|
|
4993
|
+
return true;
|
|
4994
|
+
}
|
|
4995
|
+
try {
|
|
4996
|
+
const regex = new RegExp(matcher);
|
|
4997
|
+
return values.some((value) => regex.test(value));
|
|
4998
|
+
} catch {
|
|
4999
|
+
return false;
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
function cloneMatcher(matcher) {
|
|
5003
|
+
return {
|
|
5004
|
+
...matcher.matcher !== undefined ? { matcher: matcher.matcher } : {},
|
|
5005
|
+
hooks: matcher.hooks.map((hook) => ({ ...hook }))
|
|
5006
|
+
};
|
|
5007
|
+
}
|
|
5008
|
+
function filterSharedHooksForCodex(config, capabilityName) {
|
|
5009
|
+
const warnings = [];
|
|
5010
|
+
if (process.platform === "win32") {
|
|
5011
|
+
if (hasHooksInConfig(config)) {
|
|
5012
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" has shared hooks, but Codex hooks are currently disabled on Windows. Skipping Codex hook output.`);
|
|
5013
|
+
}
|
|
5014
|
+
return { config: {}, warnings };
|
|
5015
|
+
}
|
|
5016
|
+
const filtered = {};
|
|
5017
|
+
for (const event of HOOK_EVENTS) {
|
|
5018
|
+
const matchers = config[event];
|
|
5019
|
+
if (!matchers || matchers.length === 0) {
|
|
5020
|
+
continue;
|
|
5021
|
+
}
|
|
5022
|
+
if (!CODEX_SUPPORTED_EVENTS.has(event)) {
|
|
5023
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" shared event "${event}" is not supported by Codex and was skipped.`);
|
|
5024
|
+
continue;
|
|
5025
|
+
}
|
|
5026
|
+
const filteredMatchers = [];
|
|
5027
|
+
for (const matcher of matchers) {
|
|
5028
|
+
const nextMatcher = cloneMatcher(matcher);
|
|
5029
|
+
const originalHookCount = nextMatcher.hooks.length;
|
|
5030
|
+
nextMatcher.hooks = nextMatcher.hooks.filter((hook, hookIndex) => {
|
|
5031
|
+
if (hook.type === "command") {
|
|
5032
|
+
return true;
|
|
5033
|
+
}
|
|
5034
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" shared ${event} hook ${hookIndex + 1} uses prompt hooks, which Codex does not support. It was skipped.`);
|
|
5035
|
+
return false;
|
|
5036
|
+
});
|
|
5037
|
+
if (nextMatcher.hooks.length === 0) {
|
|
5038
|
+
continue;
|
|
5039
|
+
}
|
|
5040
|
+
if (event === "UserPromptSubmit" || event === "Stop") {
|
|
5041
|
+
if (nextMatcher.matcher !== undefined && nextMatcher.matcher !== "" && nextMatcher.matcher !== "*") {
|
|
5042
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" shared ${event} matcher "${nextMatcher.matcher}" is ignored by Codex. The matcher was dropped.`);
|
|
5043
|
+
delete nextMatcher.matcher;
|
|
5044
|
+
}
|
|
5045
|
+
filteredMatchers.push(nextMatcher);
|
|
5046
|
+
continue;
|
|
5047
|
+
}
|
|
5048
|
+
if (event === "PreToolUse" || event === "PostToolUse") {
|
|
5049
|
+
if (!matcherMatchesAny(nextMatcher.matcher, ["Bash"])) {
|
|
5050
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" shared ${event} matcher "${nextMatcher.matcher ?? ""}" does not match Codex's current tool runtime ("Bash") and was skipped.`);
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
filteredMatchers.push(nextMatcher);
|
|
5054
|
+
continue;
|
|
5055
|
+
}
|
|
5056
|
+
if (event === "SessionStart") {
|
|
5057
|
+
if (!matcherMatchesAny(nextMatcher.matcher, ["startup", "resume"])) {
|
|
5058
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" shared SessionStart matcher "${nextMatcher.matcher ?? ""}" does not match Codex's current sources ("startup" or "resume") and was skipped.`);
|
|
5059
|
+
continue;
|
|
5060
|
+
}
|
|
5061
|
+
}
|
|
5062
|
+
if (nextMatcher.hooks.length !== originalHookCount || nextMatcher.matcher !== matcher.matcher) {
|
|
5063
|
+
filteredMatchers.push(nextMatcher);
|
|
5064
|
+
} else {
|
|
5065
|
+
filteredMatchers.push(matcher);
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
if (filteredMatchers.length > 0) {
|
|
5069
|
+
filtered[event] = filteredMatchers;
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
return { config: filtered, warnings };
|
|
5073
|
+
}
|
|
5074
|
+
function getProviderOverrideWarnings(providerId, capabilityName, providerConfigs) {
|
|
5075
|
+
if (!providerConfigs) {
|
|
5076
|
+
return [];
|
|
5077
|
+
}
|
|
5078
|
+
if (providerId === "claude-code" && providerConfigs.codex) {
|
|
5079
|
+
return [
|
|
5080
|
+
`[hooks] Warning: Capability "${capabilityName}" defines [codex] hooks; they are not used by Claude Code.`
|
|
5081
|
+
];
|
|
5082
|
+
}
|
|
5083
|
+
if (providerId === "codex" && providerConfigs.claude) {
|
|
5084
|
+
return [
|
|
5085
|
+
`[hooks] Warning: Capability "${capabilityName}" defines [claude] hooks; they are not used by Codex.`
|
|
5086
|
+
];
|
|
5087
|
+
}
|
|
5088
|
+
return [];
|
|
5089
|
+
}
|
|
5090
|
+
function applyProviderOverrides(baseConfig, providerId, capabilityName, providerConfigs) {
|
|
5091
|
+
const warnings = getProviderOverrideWarnings(providerId, capabilityName, providerConfigs);
|
|
5092
|
+
const providerSection = PROVIDER_SECTION_BY_ID[providerId];
|
|
5093
|
+
if (!providerSection) {
|
|
5094
|
+
return { config: baseConfig, warnings };
|
|
5095
|
+
}
|
|
5096
|
+
const overrideConfig = providerConfigs?.[providerSection];
|
|
5097
|
+
if (!overrideConfig) {
|
|
5098
|
+
return { config: baseConfig, warnings };
|
|
5099
|
+
}
|
|
5100
|
+
const result = { ...baseConfig };
|
|
5101
|
+
for (const event of HOOK_EVENTS) {
|
|
5102
|
+
if (!hasOwnKey(overrideConfig, event)) {
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
const eventValue = overrideConfig[event];
|
|
5106
|
+
if (!Array.isArray(eventValue)) {
|
|
5107
|
+
warnings.push(`[hooks] Warning: Capability "${capabilityName}" [${providerSection}].${event} must be an array to be emitted for ${getProviderDisplayName(providerId)}. The override was skipped.`);
|
|
5108
|
+
continue;
|
|
5109
|
+
}
|
|
5110
|
+
result[event] = eventValue;
|
|
5111
|
+
}
|
|
5112
|
+
return { config: result, warnings };
|
|
5113
|
+
}
|
|
5114
|
+
function mergeCapabilityHookConfig(target, capabilityConfig) {
|
|
5115
|
+
for (const event of HOOK_EVENTS) {
|
|
5116
|
+
const eventValue = capabilityConfig[event];
|
|
5117
|
+
if (!Array.isArray(eventValue) || eventValue.length === 0) {
|
|
5118
|
+
continue;
|
|
5119
|
+
}
|
|
5120
|
+
const existing = target[event];
|
|
5121
|
+
target[event] = Array.isArray(existing) ? [...existing, ...eventValue] : [...eventValue];
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
function composeHooksForProvider(capabilityHooks, providerId) {
|
|
5125
|
+
if (providerId !== "claude-code" && providerId !== "codex") {
|
|
5126
|
+
return { config: {}, warnings: [] };
|
|
5127
|
+
}
|
|
5128
|
+
const warnings = [];
|
|
5129
|
+
const mergedConfig = {};
|
|
5130
|
+
for (const capHooks of capabilityHooks) {
|
|
5131
|
+
if (providerId === "codex" && process.platform === "win32") {
|
|
5132
|
+
if (hasHooksInConfig(capHooks.config) || hasHooksInConfig(capHooks.providerConfigs?.codex)) {
|
|
5133
|
+
warnings.push(`[hooks] Warning: Capability "${capHooks.capabilityName}" has Codex hooks configured, but Codex hooks are currently disabled on Windows. Skipping Codex hook output.`);
|
|
5134
|
+
}
|
|
5135
|
+
continue;
|
|
5136
|
+
}
|
|
5137
|
+
let capabilityConfig = capHooks.config;
|
|
5138
|
+
if (providerId === "codex") {
|
|
5139
|
+
const filtered = filterSharedHooksForCodex(capabilityConfig, capHooks.capabilityName);
|
|
5140
|
+
capabilityConfig = filtered.config;
|
|
5141
|
+
warnings.push(...filtered.warnings);
|
|
5142
|
+
}
|
|
5143
|
+
const withOverrides = applyProviderOverrides(capabilityConfig, providerId, capHooks.capabilityName, capHooks.providerConfigs);
|
|
5144
|
+
warnings.push(...withOverrides.warnings);
|
|
5145
|
+
mergeCapabilityHookConfig(mergedConfig, withOverrides.config);
|
|
5146
|
+
}
|
|
5147
|
+
return {
|
|
5148
|
+
config: mergedConfig,
|
|
5149
|
+
warnings
|
|
5150
|
+
};
|
|
5151
|
+
}
|
|
5152
|
+
var CODEX_SUPPORTED_EVENTS, PROVIDER_SECTION_BY_ID;
|
|
5153
|
+
var init_provider_config = __esm(() => {
|
|
5154
|
+
init_constants();
|
|
5155
|
+
CODEX_SUPPORTED_EVENTS = new Set([
|
|
5156
|
+
"SessionStart",
|
|
5157
|
+
"PreToolUse",
|
|
5158
|
+
"PostToolUse",
|
|
5159
|
+
"UserPromptSubmit",
|
|
5160
|
+
"Stop"
|
|
5161
|
+
]);
|
|
5162
|
+
PROVIDER_SECTION_BY_ID = {
|
|
5163
|
+
"claude-code": "claude",
|
|
5164
|
+
codex: "codex"
|
|
5165
|
+
};
|
|
5166
|
+
});
|
|
5167
|
+
|
|
4789
5168
|
// ../core/src/hooks/index.ts
|
|
4790
5169
|
var init_hooks = __esm(() => {
|
|
4791
5170
|
init_constants();
|
|
@@ -4794,6 +5173,7 @@ var init_hooks = __esm(() => {
|
|
|
4794
5173
|
init_variables();
|
|
4795
5174
|
init_loader();
|
|
4796
5175
|
init_merger();
|
|
5176
|
+
init_provider_config();
|
|
4797
5177
|
init_json_loader();
|
|
4798
5178
|
});
|
|
4799
5179
|
|
|
@@ -5168,29 +5548,107 @@ var init_mcp_json = __esm(() => {
|
|
|
5168
5548
|
});
|
|
5169
5549
|
|
|
5170
5550
|
// ../core/src/state/manifest.ts
|
|
5171
|
-
import {
|
|
5551
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
5552
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync2, readdirSync as readdirSync7, rmSync } from "node:fs";
|
|
5553
|
+
import { dirname, join as join10 } from "node:path";
|
|
5172
5554
|
import { readFile as readFile14, writeFile as writeFile7 } from "node:fs/promises";
|
|
5173
|
-
|
|
5174
|
-
|
|
5555
|
+
function createEmptyManifest() {
|
|
5556
|
+
return {
|
|
5557
|
+
version: CURRENT_VERSION,
|
|
5558
|
+
syncedAt: new Date().toISOString(),
|
|
5559
|
+
capabilities: {},
|
|
5560
|
+
providers: {}
|
|
5561
|
+
};
|
|
5562
|
+
}
|
|
5563
|
+
function normalizeManifest(parsed) {
|
|
5564
|
+
if (parsed.version === CURRENT_VERSION && "providers" in parsed) {
|
|
5175
5565
|
return {
|
|
5176
5566
|
version: CURRENT_VERSION,
|
|
5177
|
-
syncedAt:
|
|
5178
|
-
capabilities:
|
|
5567
|
+
syncedAt: parsed.syncedAt,
|
|
5568
|
+
capabilities: parsed.capabilities,
|
|
5569
|
+
providers: parsed.providers ?? {}
|
|
5179
5570
|
};
|
|
5180
5571
|
}
|
|
5572
|
+
return {
|
|
5573
|
+
version: CURRENT_VERSION,
|
|
5574
|
+
syncedAt: parsed.syncedAt,
|
|
5575
|
+
capabilities: parsed.capabilities,
|
|
5576
|
+
providers: {}
|
|
5577
|
+
};
|
|
5578
|
+
}
|
|
5579
|
+
function hashContent2(content) {
|
|
5580
|
+
return createHash3("sha256").update(content).digest("hex");
|
|
5581
|
+
}
|
|
5582
|
+
function normalizePath(path) {
|
|
5583
|
+
return path.replace(/\\/g, "/");
|
|
5584
|
+
}
|
|
5585
|
+
function isDirectoryEmpty(path) {
|
|
5586
|
+
return readdirSync7(path).length === 0;
|
|
5587
|
+
}
|
|
5588
|
+
async function cleanupManagedOutput(output) {
|
|
5589
|
+
const outputPath = join10(process.cwd(), output.path);
|
|
5590
|
+
if (!existsSync17(outputPath)) {
|
|
5591
|
+
return { deleted: false };
|
|
5592
|
+
}
|
|
5593
|
+
if (output.cleanupStrategy === "remove-json-key") {
|
|
5594
|
+
if (!output.jsonKey) {
|
|
5595
|
+
return { deleted: false, reason: `missing jsonKey metadata for ${output.path}` };
|
|
5596
|
+
}
|
|
5597
|
+
let parsed;
|
|
5598
|
+
try {
|
|
5599
|
+
parsed = JSON.parse(await readFile14(outputPath, "utf-8"));
|
|
5600
|
+
} catch {
|
|
5601
|
+
return { deleted: false, reason: `could not parse JSON at ${output.path}` };
|
|
5602
|
+
}
|
|
5603
|
+
const currentValue = parsed[output.jsonKey];
|
|
5604
|
+
if (currentValue === undefined) {
|
|
5605
|
+
return { deleted: false };
|
|
5606
|
+
}
|
|
5607
|
+
if (hashContent2(JSON.stringify(currentValue)) !== output.hash) {
|
|
5608
|
+
return { deleted: false, reason: `managed section changed at ${output.path}` };
|
|
5609
|
+
}
|
|
5610
|
+
delete parsed[output.jsonKey];
|
|
5611
|
+
if (Object.keys(parsed).length === 0) {
|
|
5612
|
+
rmSync(outputPath);
|
|
5613
|
+
} else {
|
|
5614
|
+
await writeFile7(outputPath, `${JSON.stringify(parsed, null, 2)}
|
|
5615
|
+
`, "utf-8");
|
|
5616
|
+
}
|
|
5617
|
+
return { deleted: true };
|
|
5618
|
+
}
|
|
5619
|
+
const currentContent = await readFile14(outputPath, "utf-8");
|
|
5620
|
+
if (hashContent2(currentContent) !== output.hash) {
|
|
5621
|
+
return { deleted: false, reason: `managed file changed at ${output.path}` };
|
|
5622
|
+
}
|
|
5623
|
+
rmSync(outputPath);
|
|
5624
|
+
if (output.cleanupStrategy === "delete-file-and-prune-empty-parents" && output.pruneRoot) {
|
|
5625
|
+
const pruneRoot = join10(process.cwd(), output.pruneRoot);
|
|
5626
|
+
let currentDir = dirname(outputPath);
|
|
5627
|
+
while (normalizePath(currentDir).startsWith(normalizePath(pruneRoot)) && normalizePath(currentDir) !== normalizePath(pruneRoot) && existsSync17(currentDir) && isDirectoryEmpty(currentDir)) {
|
|
5628
|
+
rmSync(currentDir, { recursive: true });
|
|
5629
|
+
currentDir = dirname(currentDir);
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
return { deleted: true };
|
|
5633
|
+
}
|
|
5634
|
+
async function loadManifest() {
|
|
5635
|
+
if (!existsSync17(MANIFEST_PATH)) {
|
|
5636
|
+
return createEmptyManifest();
|
|
5637
|
+
}
|
|
5181
5638
|
const content = await readFile14(MANIFEST_PATH, "utf-8");
|
|
5182
|
-
return JSON.parse(content);
|
|
5639
|
+
return normalizeManifest(JSON.parse(content));
|
|
5183
5640
|
}
|
|
5184
5641
|
async function saveManifest(manifest) {
|
|
5185
5642
|
mkdirSync2(".omni/state", { recursive: true });
|
|
5186
5643
|
await writeFile7(MANIFEST_PATH, `${JSON.stringify(manifest, null, 2)}
|
|
5187
5644
|
`, "utf-8");
|
|
5188
5645
|
}
|
|
5189
|
-
function buildManifestFromCapabilities(capabilities2) {
|
|
5646
|
+
function buildManifestFromCapabilities(capabilities2, providerOutputs = new Map) {
|
|
5190
5647
|
const manifest = {
|
|
5191
5648
|
version: CURRENT_VERSION,
|
|
5192
5649
|
syncedAt: new Date().toISOString(),
|
|
5193
|
-
capabilities: {}
|
|
5650
|
+
capabilities: {},
|
|
5651
|
+
providers: {}
|
|
5194
5652
|
};
|
|
5195
5653
|
for (const cap of capabilities2) {
|
|
5196
5654
|
const resources = {
|
|
@@ -5202,38 +5660,59 @@ function buildManifestFromCapabilities(capabilities2) {
|
|
|
5202
5660
|
};
|
|
5203
5661
|
manifest.capabilities[cap.id] = resources;
|
|
5204
5662
|
}
|
|
5663
|
+
for (const [providerId, outputs] of providerOutputs) {
|
|
5664
|
+
manifest.providers[providerId] = {
|
|
5665
|
+
outputs: Object.fromEntries(outputs.map((output) => [output.path, output]))
|
|
5666
|
+
};
|
|
5667
|
+
}
|
|
5205
5668
|
return manifest;
|
|
5206
5669
|
}
|
|
5207
|
-
async function cleanupStaleResources(
|
|
5208
|
-
|
|
5670
|
+
async function cleanupStaleResources(_previousManifest, _currentCapabilityIds) {
|
|
5671
|
+
return {
|
|
5209
5672
|
deletedSkills: [],
|
|
5210
5673
|
deletedRules: [],
|
|
5211
5674
|
deletedCommands: [],
|
|
5212
5675
|
deletedSubagents: [],
|
|
5213
5676
|
deletedMcps: []
|
|
5214
5677
|
};
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5678
|
+
}
|
|
5679
|
+
async function cleanupStaleManagedOutputs(previousManifest, nextProviders) {
|
|
5680
|
+
const result = {
|
|
5681
|
+
deletedPaths: [],
|
|
5682
|
+
skippedPaths: []
|
|
5683
|
+
};
|
|
5684
|
+
const claimedPaths = new Set;
|
|
5685
|
+
for (const outputs of nextProviders.values()) {
|
|
5686
|
+
for (const output of outputs) {
|
|
5687
|
+
claimedPaths.add(output.path);
|
|
5218
5688
|
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5689
|
+
}
|
|
5690
|
+
for (const [providerId, providerState] of Object.entries(previousManifest.providers)) {
|
|
5691
|
+
const nextPaths = new Set((nextProviders.get(providerId) ?? []).map((output) => output.path));
|
|
5692
|
+
for (const output of Object.values(providerState.outputs)) {
|
|
5693
|
+
if (nextPaths.has(output.path)) {
|
|
5694
|
+
continue;
|
|
5224
5695
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
result.
|
|
5696
|
+
if (claimedPaths.has(output.path)) {
|
|
5697
|
+
continue;
|
|
5698
|
+
}
|
|
5699
|
+
const cleanup = await cleanupManagedOutput(output);
|
|
5700
|
+
if (cleanup.deleted) {
|
|
5701
|
+
result.deletedPaths.push(output.path);
|
|
5702
|
+
} else if (cleanup.reason) {
|
|
5703
|
+
result.skippedPaths.push({
|
|
5704
|
+
path: output.path,
|
|
5705
|
+
reason: cleanup.reason
|
|
5706
|
+
});
|
|
5231
5707
|
}
|
|
5232
5708
|
}
|
|
5233
5709
|
}
|
|
5234
5710
|
return result;
|
|
5235
5711
|
}
|
|
5236
|
-
|
|
5712
|
+
function getProviderManagedOutputs(manifest, providerId) {
|
|
5713
|
+
return Object.values(manifest.providers[providerId]?.outputs ?? {});
|
|
5714
|
+
}
|
|
5715
|
+
var MANIFEST_PATH = ".omni/state/manifest.json", CURRENT_VERSION = 2;
|
|
5237
5716
|
var init_manifest = () => {};
|
|
5238
5717
|
|
|
5239
5718
|
// ../core/src/state/providers.ts
|
|
@@ -5382,7 +5861,7 @@ var init_state = __esm(() => {
|
|
|
5382
5861
|
// ../core/src/sync.ts
|
|
5383
5862
|
import { spawn as spawn2 } from "node:child_process";
|
|
5384
5863
|
import { existsSync as existsSync20, mkdirSync as mkdirSync5, readFileSync as readFileSync3 } from "node:fs";
|
|
5385
|
-
import { join as
|
|
5864
|
+
import { join as join11 } from "node:path";
|
|
5386
5865
|
function getDeclaredPackageManager(packageManager) {
|
|
5387
5866
|
if (typeof packageManager !== "string" || packageManager.trim().length === 0) {
|
|
5388
5867
|
return;
|
|
@@ -5391,8 +5870,8 @@ function getDeclaredPackageManager(packageManager) {
|
|
|
5391
5870
|
return atIndex === -1 ? packageManager : packageManager.slice(0, atIndex);
|
|
5392
5871
|
}
|
|
5393
5872
|
function resolveCapabilityInstallCommand(capabilityPath, options) {
|
|
5394
|
-
const packageJsonPath =
|
|
5395
|
-
const packageLockPath =
|
|
5873
|
+
const packageJsonPath = join11(capabilityPath, "package.json");
|
|
5874
|
+
const packageLockPath = join11(capabilityPath, "package-lock.json");
|
|
5396
5875
|
let packageManager;
|
|
5397
5876
|
try {
|
|
5398
5877
|
const pkgJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
@@ -5410,13 +5889,13 @@ function resolveCapabilityInstallCommand(capabilityPath, options) {
|
|
|
5410
5889
|
};
|
|
5411
5890
|
}
|
|
5412
5891
|
async function installCapabilityDependencies(silent) {
|
|
5413
|
-
const { readdirSync:
|
|
5892
|
+
const { readdirSync: readdirSync8 } = await import("node:fs");
|
|
5414
5893
|
const { parse: parse2 } = await Promise.resolve().then(() => (init_dist(), exports_dist));
|
|
5415
5894
|
const capabilitiesDir = ".omni/capabilities";
|
|
5416
5895
|
if (!existsSync20(capabilitiesDir)) {
|
|
5417
5896
|
return;
|
|
5418
5897
|
}
|
|
5419
|
-
const entries =
|
|
5898
|
+
const entries = readdirSync8(capabilitiesDir, { withFileTypes: true });
|
|
5420
5899
|
async function commandExists(cmd) {
|
|
5421
5900
|
return await new Promise((resolve2) => {
|
|
5422
5901
|
const proc = spawn2(cmd, ["--version"], { stdio: "ignore" });
|
|
@@ -5432,9 +5911,9 @@ async function installCapabilityDependencies(silent) {
|
|
|
5432
5911
|
if (!entry.isDirectory()) {
|
|
5433
5912
|
continue;
|
|
5434
5913
|
}
|
|
5435
|
-
const capabilityPath =
|
|
5436
|
-
const packageJsonPath =
|
|
5437
|
-
const capabilityTomlPath =
|
|
5914
|
+
const capabilityPath = join11(capabilitiesDir, entry.name);
|
|
5915
|
+
const packageJsonPath = join11(capabilityPath, "package.json");
|
|
5916
|
+
const capabilityTomlPath = join11(capabilityPath, "capability.toml");
|
|
5438
5917
|
if (!existsSync20(packageJsonPath)) {
|
|
5439
5918
|
continue;
|
|
5440
5919
|
}
|
|
@@ -5472,7 +5951,7 @@ ${stderr}`));
|
|
|
5472
5951
|
reject(error);
|
|
5473
5952
|
});
|
|
5474
5953
|
});
|
|
5475
|
-
const hasIndexTs = existsSync20(
|
|
5954
|
+
const hasIndexTs = existsSync20(join11(capabilityPath, "index.ts"));
|
|
5476
5955
|
let hasBuildScript = false;
|
|
5477
5956
|
try {
|
|
5478
5957
|
const pkgJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
|
|
@@ -5501,7 +5980,7 @@ ${stderr}`));
|
|
|
5501
5980
|
});
|
|
5502
5981
|
});
|
|
5503
5982
|
} else if (hasIndexTs && !silent) {
|
|
5504
|
-
const hasBuiltIndex = existsSync20(
|
|
5983
|
+
const hasBuiltIndex = existsSync20(join11(capabilityPath, "dist", "index.js"));
|
|
5505
5984
|
if (!hasBuiltIndex) {
|
|
5506
5985
|
console.warn(`Warning: Capability at ${capabilityPath} has index.ts but no build script.
|
|
5507
5986
|
Add a "build" script to package.json (e.g., "build": "tsc") to compile TypeScript.`);
|
|
@@ -5572,8 +6051,8 @@ async function syncAgentConfiguration(options) {
|
|
|
5572
6051
|
}
|
|
5573
6052
|
mkdirSync5(".omni", { recursive: true });
|
|
5574
6053
|
await syncMcpJson(capabilities2, previousManifest);
|
|
5575
|
-
const
|
|
5576
|
-
|
|
6054
|
+
const enabledProviderIds = new Set(adapters.map((adapter) => String(adapter.id)));
|
|
6055
|
+
const successfulProviderOutputs = new Map;
|
|
5577
6056
|
if (adapters.length > 0) {
|
|
5578
6057
|
const config2 = await loadConfig();
|
|
5579
6058
|
const ctx = {
|
|
@@ -5582,12 +6061,33 @@ async function syncAgentConfiguration(options) {
|
|
|
5582
6061
|
};
|
|
5583
6062
|
for (const adapter of adapters) {
|
|
5584
6063
|
try {
|
|
5585
|
-
await adapter.sync(bundle, ctx);
|
|
6064
|
+
const adapterResult = await adapter.sync(bundle, ctx);
|
|
6065
|
+
successfulProviderOutputs.set(String(adapter.id), adapterResult.managedOutputs ?? []);
|
|
5586
6066
|
} catch (error) {
|
|
5587
6067
|
console.error(`Error running ${adapter.displayName} adapter:`, error);
|
|
5588
6068
|
}
|
|
5589
6069
|
}
|
|
5590
6070
|
}
|
|
6071
|
+
const nextProviderOutputs = new Map;
|
|
6072
|
+
if (adapters.length === 0) {
|
|
6073
|
+
for (const providerId of Object.keys(previousManifest.providers)) {
|
|
6074
|
+
nextProviderOutputs.set(providerId, getProviderManagedOutputs(previousManifest, providerId));
|
|
6075
|
+
}
|
|
6076
|
+
} else {
|
|
6077
|
+
for (const providerId of enabledProviderIds) {
|
|
6078
|
+
if (successfulProviderOutputs.has(providerId)) {
|
|
6079
|
+
nextProviderOutputs.set(providerId, successfulProviderOutputs.get(providerId) ?? []);
|
|
6080
|
+
continue;
|
|
6081
|
+
}
|
|
6082
|
+
nextProviderOutputs.set(providerId, getProviderManagedOutputs(previousManifest, providerId));
|
|
6083
|
+
}
|
|
6084
|
+
}
|
|
6085
|
+
const cleanupResult = await cleanupStaleManagedOutputs(previousManifest, nextProviderOutputs);
|
|
6086
|
+
for (const skipped of cleanupResult.skippedPaths) {
|
|
6087
|
+
console.warn(`Warning: skipped cleanup for ${skipped.path} (${skipped.reason})`);
|
|
6088
|
+
}
|
|
6089
|
+
const newManifest = buildManifestFromCapabilities(capabilities2, nextProviderOutputs);
|
|
6090
|
+
await saveManifest(newManifest);
|
|
5591
6091
|
const result = {
|
|
5592
6092
|
capabilities: capabilities2.map((c) => c.id),
|
|
5593
6093
|
skillCount: bundle.skills.length,
|
|
@@ -5648,7 +6148,7 @@ var init_types3 = __esm(() => {
|
|
|
5648
6148
|
// ../core/src/security/scanner.ts
|
|
5649
6149
|
import { existsSync as existsSync21 } from "node:fs";
|
|
5650
6150
|
import { lstat, readdir as readdir2, readFile as readFile17, readlink, realpath } from "node:fs/promises";
|
|
5651
|
-
import { join as
|
|
6151
|
+
import { join as join12, relative, resolve as resolve2 } from "node:path";
|
|
5652
6152
|
async function scanFileForUnicode(filePath, relativePath) {
|
|
5653
6153
|
const findings = [];
|
|
5654
6154
|
try {
|
|
@@ -5835,7 +6335,7 @@ async function scanFileForNetworkRequests(filePath, relativePath) {
|
|
|
5835
6335
|
async function checkSymlink(symlinkPath, relativePath, capabilityRoot) {
|
|
5836
6336
|
try {
|
|
5837
6337
|
const linkTarget = await readlink(symlinkPath);
|
|
5838
|
-
const resolvedTarget = resolve2(
|
|
6338
|
+
const resolvedTarget = resolve2(join12(symlinkPath, "..", linkTarget));
|
|
5839
6339
|
const normalizedRoot = await realpath(capabilityRoot);
|
|
5840
6340
|
if (linkTarget.startsWith("/")) {
|
|
5841
6341
|
return {
|
|
@@ -5882,7 +6382,7 @@ async function scanCapability(capabilityId, capabilityPath, settings = DEFAULT_S
|
|
|
5882
6382
|
async function scanDirectory(dirPath) {
|
|
5883
6383
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
5884
6384
|
for (const entry of entries) {
|
|
5885
|
-
const fullPath =
|
|
6385
|
+
const fullPath = join12(dirPath, entry.name);
|
|
5886
6386
|
const relativePath = relative(capabilityPath, fullPath);
|
|
5887
6387
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "__pycache__") {
|
|
5888
6388
|
continue;
|
|
@@ -6245,7 +6745,9 @@ var init_security = __esm(() => {
|
|
|
6245
6745
|
|
|
6246
6746
|
// ../core/src/templates/agents.ts
|
|
6247
6747
|
function generateAgentsTemplate() {
|
|
6248
|
-
return
|
|
6748
|
+
return `> IMPORTANT: Do not edit this file directly. Edit \`OMNI.md\` instead. This file is a generated copy of \`OMNI.md\` and is ephemeral. Any persistent change must be made in \`OMNI.md\`.
|
|
6749
|
+
|
|
6750
|
+
# Project Instructions
|
|
6249
6751
|
|
|
6250
6752
|
<!-- Add your project-specific instructions here -->
|
|
6251
6753
|
|
|
@@ -6372,7 +6874,9 @@ function formatDisplayName(kebabCase) {
|
|
|
6372
6874
|
|
|
6373
6875
|
// ../core/src/templates/claude.ts
|
|
6374
6876
|
function generateClaudeTemplate() {
|
|
6375
|
-
return
|
|
6877
|
+
return `> IMPORTANT: Do not edit this file directly. Edit \`OMNI.md\` instead. This file is a generated copy of \`OMNI.md\` and is ephemeral. Any persistent change must be made in \`OMNI.md\`.
|
|
6878
|
+
|
|
6879
|
+
# Project Instructions
|
|
6376
6880
|
|
|
6377
6881
|
<!-- Add your project-specific instructions here -->
|
|
6378
6882
|
`;
|
|
@@ -6442,6 +6946,7 @@ __export(exports_src, {
|
|
|
6442
6946
|
saveManifest: () => saveManifest,
|
|
6443
6947
|
saveLockFile: () => saveLockFile,
|
|
6444
6948
|
resolveEnabledCapabilities: () => resolveEnabledCapabilities,
|
|
6949
|
+
resolveCapabilityRootInValue: () => resolveCapabilityRootInValue,
|
|
6445
6950
|
resolveCapabilityRootInConfig: () => resolveCapabilityRootInConfig,
|
|
6446
6951
|
resolveCapabilityRoot: () => resolveCapabilityRoot,
|
|
6447
6952
|
resolveCapabilityInstallCommand: () => resolveCapabilityInstallCommand,
|
|
@@ -6492,10 +6997,12 @@ __export(exports_src, {
|
|
|
6492
6997
|
isFileSourceConfig: () => isFileSourceConfig,
|
|
6493
6998
|
isFileSource: () => isFileSource,
|
|
6494
6999
|
installCapabilityDependencies: () => installCapabilityDependencies,
|
|
7000
|
+
hasHooksInConfig: () => hasHooksInConfig,
|
|
6495
7001
|
hasHooks: () => hasHooks,
|
|
6496
7002
|
hasAnyHooks: () => hasAnyHooks,
|
|
6497
7003
|
getVersion: () => getVersion,
|
|
6498
7004
|
getSourceCapabilityPath: () => getSourceCapabilityPath,
|
|
7005
|
+
getProviderManagedOutputs: () => getProviderManagedOutputs,
|
|
6499
7006
|
getLockFilePath: () => getLockFilePath,
|
|
6500
7007
|
getHooksDirectory: () => getHooksDirectory,
|
|
6501
7008
|
getHooksConfigPath: () => getHooksConfigPath,
|
|
@@ -6529,10 +7036,12 @@ __export(exports_src, {
|
|
|
6529
7036
|
countHooks: () => countHooks,
|
|
6530
7037
|
containsOmnidevVariables: () => containsOmnidevVariables,
|
|
6531
7038
|
containsClaudeVariables: () => containsClaudeVariables,
|
|
7039
|
+
composeHooksForProvider: () => composeHooksForProvider,
|
|
6532
7040
|
clearCapabilityAllows: () => clearCapabilityAllows,
|
|
6533
7041
|
clearAllSecurityAllows: () => clearAllSecurityAllows,
|
|
6534
7042
|
clearActiveProfileState: () => clearActiveProfileState,
|
|
6535
7043
|
cleanupStaleResources: () => cleanupStaleResources,
|
|
7044
|
+
cleanupStaleManagedOutputs: () => cleanupStaleManagedOutputs,
|
|
6536
7045
|
checkVersionMismatch: () => checkVersionMismatch,
|
|
6537
7046
|
checkForUpdates: () => checkForUpdates,
|
|
6538
7047
|
buildSyncBundle: () => buildSyncBundle,
|
|
@@ -6576,15 +7085,33 @@ var init_src = __esm(() => {
|
|
|
6576
7085
|
import { run } from "@stricli/core";
|
|
6577
7086
|
|
|
6578
7087
|
// src/lib/dynamic-app.ts
|
|
6579
|
-
import { existsSync as
|
|
7088
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
6580
7089
|
import { createRequire as createRequire2 } from "node:module";
|
|
6581
|
-
import { join as
|
|
7090
|
+
import { join as join31 } from "node:path";
|
|
6582
7091
|
import { buildApplication, buildRouteMap as buildRouteMap7 } from "@stricli/core";
|
|
6583
7092
|
|
|
6584
7093
|
// src/commands/add.ts
|
|
6585
7094
|
import { existsSync as existsSync24 } from "node:fs";
|
|
6586
7095
|
import { basename as basename5, resolve as resolve3 } from "node:path";
|
|
6587
7096
|
|
|
7097
|
+
// ../adapters/src/writers/generic/managed-outputs.ts
|
|
7098
|
+
import { createHash } from "node:crypto";
|
|
7099
|
+
function hashContent(content) {
|
|
7100
|
+
return createHash("sha256").update(content).digest("hex");
|
|
7101
|
+
}
|
|
7102
|
+
function trimTrailingSlash(path) {
|
|
7103
|
+
return path.endsWith("/") ? path.slice(0, -1) : path;
|
|
7104
|
+
}
|
|
7105
|
+
function createManagedOutput(path, writerId, content, options) {
|
|
7106
|
+
return {
|
|
7107
|
+
path,
|
|
7108
|
+
writerId,
|
|
7109
|
+
hash: hashContent(content),
|
|
7110
|
+
cleanupStrategy: options?.cleanupStrategy ?? "delete-file",
|
|
7111
|
+
...options?.pruneRoot ? { pruneRoot: trimTrailingSlash(options.pruneRoot) } : {},
|
|
7112
|
+
...options?.jsonKey ? { jsonKey: options.jsonKey } : {}
|
|
7113
|
+
};
|
|
7114
|
+
}
|
|
6588
7115
|
// ../adapters/src/writers/generic/executor.ts
|
|
6589
7116
|
async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
|
|
6590
7117
|
const seen = new Set;
|
|
@@ -6600,6 +7127,7 @@ async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
|
|
|
6600
7127
|
uniqueConfigs.push(config);
|
|
6601
7128
|
}
|
|
6602
7129
|
const allFilesWritten = [];
|
|
7130
|
+
const allManagedOutputs = [];
|
|
6603
7131
|
for (const config of uniqueConfigs) {
|
|
6604
7132
|
const result = await config.writer.write(bundle, {
|
|
6605
7133
|
outputPath: config.outputPath,
|
|
@@ -6607,27 +7135,29 @@ async function executeWriters(writerConfigs, bundle, projectRoot, providerId) {
|
|
|
6607
7135
|
...providerId ? { providerId } : {}
|
|
6608
7136
|
});
|
|
6609
7137
|
allFilesWritten.push(...result.filesWritten);
|
|
7138
|
+
allManagedOutputs.push(...result.managedOutputs ?? []);
|
|
6610
7139
|
}
|
|
6611
7140
|
return {
|
|
6612
7141
|
filesWritten: allFilesWritten,
|
|
6613
|
-
deduplicatedCount
|
|
7142
|
+
deduplicatedCount,
|
|
7143
|
+
managedOutputs: allManagedOutputs
|
|
6614
7144
|
};
|
|
6615
7145
|
}
|
|
6616
7146
|
// ../adapters/src/writers/generic/hooks.ts
|
|
6617
7147
|
init_src();
|
|
6618
7148
|
import { existsSync as existsSync22 } from "node:fs";
|
|
6619
7149
|
import { mkdir as mkdir2, readFile as readFile18, writeFile as writeFile10 } from "node:fs/promises";
|
|
6620
|
-
import { dirname, join as
|
|
7150
|
+
import { dirname as dirname2, join as join13 } from "node:path";
|
|
6621
7151
|
var HooksWriter = {
|
|
6622
7152
|
id: "hooks",
|
|
6623
7153
|
async write(bundle, ctx) {
|
|
6624
7154
|
if (!bundle.hooks) {
|
|
6625
7155
|
return { filesWritten: [] };
|
|
6626
7156
|
}
|
|
6627
|
-
const settingsPath = join12(ctx.projectRoot, ctx.outputPath);
|
|
6628
|
-
const parentDir = dirname(settingsPath);
|
|
6629
|
-
await mkdir2(parentDir, { recursive: true });
|
|
6630
7157
|
const claudeHooks = transformHooksConfig(bundle.hooks, "toClaude");
|
|
7158
|
+
const settingsPath = join13(ctx.projectRoot, ctx.outputPath);
|
|
7159
|
+
const parentDir = dirname2(settingsPath);
|
|
7160
|
+
await mkdir2(parentDir, { recursive: true });
|
|
6631
7161
|
let existingSettings = {};
|
|
6632
7162
|
if (existsSync22(settingsPath)) {
|
|
6633
7163
|
try {
|
|
@@ -6644,14 +7174,20 @@ var HooksWriter = {
|
|
|
6644
7174
|
await writeFile10(settingsPath, `${JSON.stringify(newSettings, null, 2)}
|
|
6645
7175
|
`, "utf-8");
|
|
6646
7176
|
return {
|
|
6647
|
-
filesWritten: [ctx.outputPath]
|
|
7177
|
+
filesWritten: [ctx.outputPath],
|
|
7178
|
+
managedOutputs: [
|
|
7179
|
+
createManagedOutput(ctx.outputPath, this.id, JSON.stringify(claudeHooks), {
|
|
7180
|
+
cleanupStrategy: "remove-json-key",
|
|
7181
|
+
jsonKey: "hooks"
|
|
7182
|
+
})
|
|
7183
|
+
]
|
|
6648
7184
|
};
|
|
6649
7185
|
}
|
|
6650
7186
|
};
|
|
6651
7187
|
// ../adapters/src/writers/generic/instructions-md.ts
|
|
6652
7188
|
import { existsSync as existsSync23 } from "node:fs";
|
|
6653
7189
|
import { mkdir as mkdir3, readFile as readFile19, writeFile as writeFile11 } from "node:fs/promises";
|
|
6654
|
-
import { dirname as
|
|
7190
|
+
import { dirname as dirname3, join as join14 } from "node:path";
|
|
6655
7191
|
|
|
6656
7192
|
// ../adapters/src/writers/generic/omni-md.ts
|
|
6657
7193
|
init_src();
|
|
@@ -6673,20 +7209,32 @@ function renderOmniMdForProvider(content, providerId) {
|
|
|
6673
7209
|
}
|
|
6674
7210
|
|
|
6675
7211
|
// ../adapters/src/writers/generic/instructions-md.ts
|
|
7212
|
+
function getGeneratedCopyNotice(outputPath) {
|
|
7213
|
+
if (outputPath !== "AGENTS.md" && outputPath !== "CLAUDE.md") {
|
|
7214
|
+
return "";
|
|
7215
|
+
}
|
|
7216
|
+
return "> IMPORTANT: Do not edit this file directly. Edit `OMNI.md` instead. This file is a generated copy of `OMNI.md` and is ephemeral. Any persistent change must be made in `OMNI.md`.";
|
|
7217
|
+
}
|
|
6676
7218
|
var InstructionsMdWriter = {
|
|
6677
7219
|
id: "instructions-md",
|
|
6678
7220
|
async write(bundle, ctx) {
|
|
6679
|
-
const outputFullPath =
|
|
6680
|
-
const parentDir =
|
|
7221
|
+
const outputFullPath = join14(ctx.projectRoot, ctx.outputPath);
|
|
7222
|
+
const parentDir = dirname3(outputFullPath);
|
|
6681
7223
|
if (parentDir !== ctx.projectRoot) {
|
|
6682
7224
|
await mkdir3(parentDir, { recursive: true });
|
|
6683
7225
|
}
|
|
6684
|
-
const omniMdPath =
|
|
7226
|
+
const omniMdPath = join14(ctx.projectRoot, "OMNI.md");
|
|
6685
7227
|
let omniMdContent = "";
|
|
6686
7228
|
if (existsSync23(omniMdPath)) {
|
|
6687
7229
|
omniMdContent = renderOmniMdForProvider(await readFile19(omniMdPath, "utf-8"), ctx.providerId);
|
|
6688
7230
|
}
|
|
6689
7231
|
let content = omniMdContent;
|
|
7232
|
+
const generatedCopyNotice = getGeneratedCopyNotice(ctx.outputPath);
|
|
7233
|
+
if (generatedCopyNotice) {
|
|
7234
|
+
content = content ? `${generatedCopyNotice}
|
|
7235
|
+
|
|
7236
|
+
${content}` : generatedCopyNotice;
|
|
7237
|
+
}
|
|
6690
7238
|
if (bundle.instructionsContent) {
|
|
6691
7239
|
content += `
|
|
6692
7240
|
|
|
@@ -6695,23 +7243,30 @@ ${bundle.instructionsContent}
|
|
|
6695
7243
|
}
|
|
6696
7244
|
await writeFile11(outputFullPath, content, "utf-8");
|
|
6697
7245
|
return {
|
|
6698
|
-
filesWritten: [ctx.outputPath]
|
|
7246
|
+
filesWritten: [ctx.outputPath],
|
|
7247
|
+
managedOutputs: [createManagedOutput(ctx.outputPath, this.id, content)]
|
|
6699
7248
|
};
|
|
6700
7249
|
}
|
|
6701
7250
|
};
|
|
6702
7251
|
// ../adapters/src/writers/generic/skills.ts
|
|
6703
|
-
import { mkdir as mkdir4, writeFile as writeFile12 } from "node:fs/promises";
|
|
6704
|
-
import { join as
|
|
7252
|
+
import { cp as cp2, mkdir as mkdir4, readdir as readdir3, rm as rm2, writeFile as writeFile12 } from "node:fs/promises";
|
|
7253
|
+
import { join as join15, relative as relative2 } from "node:path";
|
|
6705
7254
|
var SkillsWriter = {
|
|
6706
7255
|
id: "skills",
|
|
6707
7256
|
async write(bundle, ctx) {
|
|
6708
|
-
const skillsDir =
|
|
7257
|
+
const skillsDir = join15(ctx.projectRoot, ctx.outputPath);
|
|
6709
7258
|
await mkdir4(skillsDir, { recursive: true });
|
|
6710
7259
|
const filesWritten = [];
|
|
7260
|
+
const managedOutputs = [];
|
|
6711
7261
|
for (const skill of bundle.skills) {
|
|
6712
|
-
const skillDir =
|
|
6713
|
-
await
|
|
6714
|
-
|
|
7262
|
+
const skillDir = join15(skillsDir, skill.name);
|
|
7263
|
+
await rm2(skillDir, { recursive: true, force: true });
|
|
7264
|
+
if (skill.sourcePath) {
|
|
7265
|
+
await cp2(skill.sourcePath, skillDir, { recursive: true });
|
|
7266
|
+
} else {
|
|
7267
|
+
await mkdir4(skillDir, { recursive: true });
|
|
7268
|
+
}
|
|
7269
|
+
const skillPath = join15(skillDir, "SKILL.md");
|
|
6715
7270
|
const content = `---
|
|
6716
7271
|
name: ${skill.name}
|
|
6717
7272
|
description: "${skill.description}"
|
|
@@ -6719,16 +7274,43 @@ description: "${skill.description}"
|
|
|
6719
7274
|
|
|
6720
7275
|
${skill.instructions}`;
|
|
6721
7276
|
await writeFile12(skillPath, content, "utf-8");
|
|
6722
|
-
|
|
7277
|
+
const relativePaths = skill.sourcePath ? await listRelativeFiles(skillDir) : ["SKILL.md"];
|
|
7278
|
+
for (const relativeFile of relativePaths) {
|
|
7279
|
+
filesWritten.push(join15(ctx.outputPath, skill.name, relativeFile));
|
|
7280
|
+
}
|
|
7281
|
+
const relativePath = join15(ctx.outputPath, skill.name, "SKILL.md");
|
|
7282
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content, {
|
|
7283
|
+
cleanupStrategy: "delete-file-and-prune-empty-parents",
|
|
7284
|
+
pruneRoot: ctx.outputPath
|
|
7285
|
+
}));
|
|
6723
7286
|
}
|
|
6724
7287
|
return {
|
|
6725
|
-
filesWritten
|
|
7288
|
+
filesWritten,
|
|
7289
|
+
managedOutputs
|
|
6726
7290
|
};
|
|
6727
7291
|
}
|
|
6728
7292
|
};
|
|
7293
|
+
async function listRelativeFiles(basePath) {
|
|
7294
|
+
const files = [];
|
|
7295
|
+
const entries = await readdir3(basePath, { withFileTypes: true });
|
|
7296
|
+
for (const entry of entries) {
|
|
7297
|
+
const entryPath = join15(basePath, entry.name);
|
|
7298
|
+
if (entry.isDirectory()) {
|
|
7299
|
+
const nestedFiles = await listRelativeFiles(entryPath);
|
|
7300
|
+
for (const nestedFile of nestedFiles) {
|
|
7301
|
+
files.push(relative2(basePath, join15(entryPath, nestedFile)));
|
|
7302
|
+
}
|
|
7303
|
+
continue;
|
|
7304
|
+
}
|
|
7305
|
+
if (entry.isFile()) {
|
|
7306
|
+
files.push(entry.name);
|
|
7307
|
+
}
|
|
7308
|
+
}
|
|
7309
|
+
return files.sort((a, b) => a.localeCompare(b));
|
|
7310
|
+
}
|
|
6729
7311
|
// ../adapters/src/writers/generic/commands-as-skills.ts
|
|
6730
7312
|
import { mkdir as mkdir5, writeFile as writeFile13 } from "node:fs/promises";
|
|
6731
|
-
import { join as
|
|
7313
|
+
import { join as join16 } from "node:path";
|
|
6732
7314
|
function generateSkillFrontmatter(command) {
|
|
6733
7315
|
const lines = ["---"];
|
|
6734
7316
|
lines.push(`name: ${command.name}`);
|
|
@@ -6743,22 +7325,29 @@ function generateSkillFrontmatter(command) {
|
|
|
6743
7325
|
var CommandsAsSkillsWriter = {
|
|
6744
7326
|
id: "commands-as-skills",
|
|
6745
7327
|
async write(bundle, ctx) {
|
|
6746
|
-
const skillsDir =
|
|
7328
|
+
const skillsDir = join16(ctx.projectRoot, ctx.outputPath);
|
|
6747
7329
|
await mkdir5(skillsDir, { recursive: true });
|
|
6748
7330
|
const filesWritten = [];
|
|
7331
|
+
const managedOutputs = [];
|
|
6749
7332
|
for (const command of bundle.commands) {
|
|
6750
|
-
const commandSkillDir =
|
|
7333
|
+
const commandSkillDir = join16(skillsDir, command.name);
|
|
6751
7334
|
await mkdir5(commandSkillDir, { recursive: true });
|
|
6752
7335
|
const frontmatter = generateSkillFrontmatter(command);
|
|
6753
7336
|
const content = `${frontmatter}
|
|
6754
7337
|
|
|
6755
7338
|
${command.prompt}`;
|
|
6756
|
-
const skillPath =
|
|
7339
|
+
const skillPath = join16(commandSkillDir, "SKILL.md");
|
|
6757
7340
|
await writeFile13(skillPath, content, "utf-8");
|
|
6758
|
-
|
|
7341
|
+
const relativePath = join16(ctx.outputPath, command.name, "SKILL.md");
|
|
7342
|
+
filesWritten.push(relativePath);
|
|
7343
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content, {
|
|
7344
|
+
cleanupStrategy: "delete-file-and-prune-empty-parents",
|
|
7345
|
+
pruneRoot: ctx.outputPath
|
|
7346
|
+
}));
|
|
6759
7347
|
}
|
|
6760
7348
|
return {
|
|
6761
|
-
filesWritten
|
|
7349
|
+
filesWritten,
|
|
7350
|
+
managedOutputs
|
|
6762
7351
|
};
|
|
6763
7352
|
}
|
|
6764
7353
|
};
|
|
@@ -6802,34 +7391,47 @@ function createProviderScopedBundle(bundle, providerId) {
|
|
|
6802
7391
|
subagents,
|
|
6803
7392
|
instructionsContent: generateInstructionsContent2(rules, docs)
|
|
6804
7393
|
};
|
|
6805
|
-
const
|
|
6806
|
-
|
|
6807
|
-
|
|
7394
|
+
const composedHooks = composeHooksForProvider(capabilities2.flatMap((capability3) => capability3.hooks ? [capability3.hooks] : []), providerId);
|
|
7395
|
+
for (const warning of composedHooks.warnings) {
|
|
7396
|
+
console.warn(warning);
|
|
7397
|
+
}
|
|
7398
|
+
if (hasHooksInConfig(composedHooks.config)) {
|
|
7399
|
+
scopedBundle.hooks = composedHooks.config;
|
|
6808
7400
|
}
|
|
6809
7401
|
return scopedBundle;
|
|
6810
7402
|
}
|
|
6811
7403
|
|
|
6812
7404
|
// ../adapters/src/writers/claude/agents.ts
|
|
6813
7405
|
import { mkdir as mkdir6, writeFile as writeFile14 } from "node:fs/promises";
|
|
6814
|
-
import { join as
|
|
7406
|
+
import { join as join17 } from "node:path";
|
|
7407
|
+
function getClaudeConfig(agent) {
|
|
7408
|
+
return {
|
|
7409
|
+
tools: agent.claude?.tools ?? agent.tools,
|
|
7410
|
+
disallowedTools: agent.claude?.disallowedTools ?? agent.disallowedTools,
|
|
7411
|
+
model: agent.claude?.model ?? agent.model,
|
|
7412
|
+
permissionMode: agent.claude?.permissionMode ?? agent.permissionMode,
|
|
7413
|
+
skills: agent.claude?.skills ?? agent.skills
|
|
7414
|
+
};
|
|
7415
|
+
}
|
|
6815
7416
|
function generateFrontmatter(agent) {
|
|
7417
|
+
const claude2 = getClaudeConfig(agent);
|
|
6816
7418
|
const lines = ["---"];
|
|
6817
7419
|
lines.push(`name: ${agent.name}`);
|
|
6818
7420
|
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
6819
|
-
if (
|
|
6820
|
-
lines.push(`tools: ${
|
|
7421
|
+
if (claude2.tools && claude2.tools.length > 0) {
|
|
7422
|
+
lines.push(`tools: ${claude2.tools.join(", ")}`);
|
|
6821
7423
|
}
|
|
6822
|
-
if (
|
|
6823
|
-
lines.push(`disallowedTools: ${
|
|
7424
|
+
if (claude2.disallowedTools && claude2.disallowedTools.length > 0) {
|
|
7425
|
+
lines.push(`disallowedTools: ${claude2.disallowedTools.join(", ")}`);
|
|
6824
7426
|
}
|
|
6825
|
-
if (
|
|
6826
|
-
lines.push(`model: ${
|
|
7427
|
+
if (claude2.model && claude2.model !== "inherit") {
|
|
7428
|
+
lines.push(`model: ${claude2.model}`);
|
|
6827
7429
|
}
|
|
6828
|
-
if (
|
|
6829
|
-
lines.push(`permissionMode: ${
|
|
7430
|
+
if (claude2.permissionMode && claude2.permissionMode !== "default") {
|
|
7431
|
+
lines.push(`permissionMode: ${claude2.permissionMode}`);
|
|
6830
7432
|
}
|
|
6831
|
-
if (
|
|
6832
|
-
lines.push(`skills: ${
|
|
7433
|
+
if (claude2.skills && claude2.skills.length > 0) {
|
|
7434
|
+
lines.push(`skills: ${claude2.skills.join(", ")}`);
|
|
6833
7435
|
}
|
|
6834
7436
|
lines.push("---");
|
|
6835
7437
|
return lines.join(`
|
|
@@ -6838,20 +7440,24 @@ function generateFrontmatter(agent) {
|
|
|
6838
7440
|
var ClaudeAgentsWriter = {
|
|
6839
7441
|
id: "claude-agents",
|
|
6840
7442
|
async write(bundle, ctx) {
|
|
6841
|
-
const agentsDir =
|
|
7443
|
+
const agentsDir = join17(ctx.projectRoot, ctx.outputPath);
|
|
6842
7444
|
await mkdir6(agentsDir, { recursive: true });
|
|
6843
7445
|
const filesWritten = [];
|
|
7446
|
+
const managedOutputs = [];
|
|
6844
7447
|
for (const agent of bundle.subagents) {
|
|
6845
7448
|
const frontmatter = generateFrontmatter(agent);
|
|
6846
7449
|
const content = `${frontmatter}
|
|
6847
7450
|
|
|
6848
7451
|
${agent.systemPrompt}`;
|
|
6849
|
-
const agentPath =
|
|
7452
|
+
const agentPath = join17(agentsDir, `${agent.name}.md`);
|
|
6850
7453
|
await writeFile14(agentPath, content, "utf-8");
|
|
6851
|
-
|
|
7454
|
+
const relativePath = join17(ctx.outputPath, `${agent.name}.md`);
|
|
7455
|
+
filesWritten.push(relativePath);
|
|
7456
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
6852
7457
|
}
|
|
6853
7458
|
return {
|
|
6854
|
-
filesWritten
|
|
7459
|
+
filesWritten,
|
|
7460
|
+
managedOutputs
|
|
6855
7461
|
};
|
|
6856
7462
|
}
|
|
6857
7463
|
};
|
|
@@ -6878,18 +7484,89 @@ var claudeCodeAdapter = {
|
|
|
6878
7484
|
const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
|
|
6879
7485
|
return {
|
|
6880
7486
|
filesWritten: result.filesWritten,
|
|
6881
|
-
filesDeleted: []
|
|
7487
|
+
filesDeleted: [],
|
|
7488
|
+
managedOutputs: result.managedOutputs
|
|
6882
7489
|
};
|
|
6883
7490
|
}
|
|
6884
7491
|
};
|
|
6885
7492
|
// ../adapters/src/codex/index.ts
|
|
6886
7493
|
import { mkdirSync as mkdirSync6 } from "node:fs";
|
|
6887
|
-
import { join as
|
|
7494
|
+
import { join as join21 } from "node:path";
|
|
6888
7495
|
|
|
6889
|
-
// ../adapters/src/writers/codex/
|
|
7496
|
+
// ../adapters/src/writers/codex/agents.ts
|
|
6890
7497
|
init_dist();
|
|
6891
7498
|
import { mkdir as mkdir7, writeFile as writeFile15 } from "node:fs/promises";
|
|
6892
|
-
import {
|
|
7499
|
+
import { join as join18 } from "node:path";
|
|
7500
|
+
function buildCodexAgentFile(agent) {
|
|
7501
|
+
const codex = agent.codex ?? {};
|
|
7502
|
+
const file = {
|
|
7503
|
+
name: agent.name,
|
|
7504
|
+
description: agent.description,
|
|
7505
|
+
developer_instructions: agent.systemPrompt
|
|
7506
|
+
};
|
|
7507
|
+
if (codex.model) {
|
|
7508
|
+
file.model = codex.model;
|
|
7509
|
+
}
|
|
7510
|
+
if (codex.modelReasoningEffort) {
|
|
7511
|
+
file.model_reasoning_effort = codex.modelReasoningEffort;
|
|
7512
|
+
}
|
|
7513
|
+
if (codex.sandboxMode) {
|
|
7514
|
+
file.sandbox_mode = codex.sandboxMode;
|
|
7515
|
+
}
|
|
7516
|
+
if (codex.nicknameCandidates && codex.nicknameCandidates.length > 0) {
|
|
7517
|
+
file.nickname_candidates = codex.nicknameCandidates;
|
|
7518
|
+
}
|
|
7519
|
+
return file;
|
|
7520
|
+
}
|
|
7521
|
+
var CodexAgentsWriter = {
|
|
7522
|
+
id: "codex-agents",
|
|
7523
|
+
async write(bundle, ctx) {
|
|
7524
|
+
if (bundle.subagents.length === 0) {
|
|
7525
|
+
return { filesWritten: [] };
|
|
7526
|
+
}
|
|
7527
|
+
const agentsDir = join18(ctx.projectRoot, ctx.outputPath);
|
|
7528
|
+
await mkdir7(agentsDir, { recursive: true });
|
|
7529
|
+
const filesWritten = [];
|
|
7530
|
+
const managedOutputs = [];
|
|
7531
|
+
for (const agent of bundle.subagents) {
|
|
7532
|
+
const content = stringify(buildCodexAgentFile(agent));
|
|
7533
|
+
const relativePath = join18(ctx.outputPath, `${agent.name}.toml`);
|
|
7534
|
+
const filePath = join18(ctx.projectRoot, relativePath);
|
|
7535
|
+
await writeFile15(filePath, content, "utf-8");
|
|
7536
|
+
filesWritten.push(relativePath);
|
|
7537
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
7538
|
+
}
|
|
7539
|
+
return {
|
|
7540
|
+
filesWritten,
|
|
7541
|
+
managedOutputs
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
};
|
|
7545
|
+
// ../adapters/src/writers/codex/hooks.ts
|
|
7546
|
+
import { mkdir as mkdir8, writeFile as writeFile16 } from "node:fs/promises";
|
|
7547
|
+
import { dirname as dirname4, join as join19 } from "node:path";
|
|
7548
|
+
var CodexHooksWriter = {
|
|
7549
|
+
id: "codex-hooks",
|
|
7550
|
+
async write(bundle, ctx) {
|
|
7551
|
+
if (!bundle.hooks) {
|
|
7552
|
+
return { filesWritten: [] };
|
|
7553
|
+
}
|
|
7554
|
+
const hooksPath = join19(ctx.projectRoot, ctx.outputPath);
|
|
7555
|
+
await mkdir8(dirname4(hooksPath), { recursive: true });
|
|
7556
|
+
const content = `${JSON.stringify({ hooks: bundle.hooks }, null, 2)}
|
|
7557
|
+
`;
|
|
7558
|
+
await writeFile16(hooksPath, content, "utf-8");
|
|
7559
|
+
return {
|
|
7560
|
+
filesWritten: [ctx.outputPath],
|
|
7561
|
+
managedOutputs: [createManagedOutput(ctx.outputPath, this.id, content)]
|
|
7562
|
+
};
|
|
7563
|
+
}
|
|
7564
|
+
};
|
|
7565
|
+
// ../adapters/src/writers/codex/toml.ts
|
|
7566
|
+
init_src();
|
|
7567
|
+
init_dist();
|
|
7568
|
+
import { mkdir as mkdir9, writeFile as writeFile17 } from "node:fs/promises";
|
|
7569
|
+
import { dirname as dirname5, join as join20 } from "node:path";
|
|
6893
7570
|
var FILE_HEADER = `# Generated by OmniDev - DO NOT EDIT
|
|
6894
7571
|
# Run \`omnidev sync\` to regenerate
|
|
6895
7572
|
|
|
@@ -6937,12 +7614,13 @@ var CodexTomlWriter = {
|
|
|
6937
7614
|
id: "codex-toml",
|
|
6938
7615
|
async write(bundle, ctx) {
|
|
6939
7616
|
const mcps = collectMcps(bundle);
|
|
6940
|
-
|
|
7617
|
+
const hasHooks2 = hasHooksInConfig(bundle.hooks);
|
|
7618
|
+
if (mcps.size === 0 && !hasHooks2) {
|
|
6941
7619
|
return { filesWritten: [] };
|
|
6942
7620
|
}
|
|
6943
|
-
const configPath =
|
|
6944
|
-
const parentDir =
|
|
6945
|
-
await
|
|
7621
|
+
const configPath = join20(ctx.projectRoot, ctx.outputPath);
|
|
7622
|
+
const parentDir = dirname5(configPath);
|
|
7623
|
+
await mkdir9(parentDir, { recursive: true });
|
|
6946
7624
|
const mcpServers = {};
|
|
6947
7625
|
for (const [id, mcp] of mcps) {
|
|
6948
7626
|
const converted = buildCodexMcpConfig(id, mcp);
|
|
@@ -6950,16 +7628,21 @@ var CodexTomlWriter = {
|
|
|
6950
7628
|
mcpServers[id] = converted;
|
|
6951
7629
|
}
|
|
6952
7630
|
}
|
|
6953
|
-
if (Object.keys(mcpServers).length === 0) {
|
|
7631
|
+
if (Object.keys(mcpServers).length === 0 && !hasHooks2) {
|
|
6954
7632
|
return { filesWritten: [] };
|
|
6955
7633
|
}
|
|
6956
|
-
const codexConfig = {
|
|
6957
|
-
|
|
6958
|
-
|
|
7634
|
+
const codexConfig = {};
|
|
7635
|
+
if (hasHooks2) {
|
|
7636
|
+
codexConfig.features = { codex_hooks: true };
|
|
7637
|
+
}
|
|
7638
|
+
if (Object.keys(mcpServers).length > 0) {
|
|
7639
|
+
codexConfig.mcp_servers = mcpServers;
|
|
7640
|
+
}
|
|
6959
7641
|
const tomlContent = FILE_HEADER + stringify(codexConfig);
|
|
6960
|
-
await
|
|
7642
|
+
await writeFile17(configPath, tomlContent, "utf-8");
|
|
6961
7643
|
return {
|
|
6962
|
-
filesWritten: [ctx.outputPath]
|
|
7644
|
+
filesWritten: [ctx.outputPath],
|
|
7645
|
+
managedOutputs: [createManagedOutput(ctx.outputPath, this.id, tomlContent)]
|
|
6963
7646
|
};
|
|
6964
7647
|
}
|
|
6965
7648
|
};
|
|
@@ -6971,10 +7654,12 @@ var codexAdapter = {
|
|
|
6971
7654
|
{ writer: InstructionsMdWriter, outputPath: "AGENTS.md" },
|
|
6972
7655
|
{ writer: SkillsWriter, outputPath: ".codex/skills/" },
|
|
6973
7656
|
{ writer: CommandsAsSkillsWriter, outputPath: ".codex/skills/" },
|
|
7657
|
+
{ writer: CodexAgentsWriter, outputPath: ".codex/agents/" },
|
|
7658
|
+
{ writer: CodexHooksWriter, outputPath: ".codex/hooks.json" },
|
|
6974
7659
|
{ writer: CodexTomlWriter, outputPath: ".codex/config.toml" }
|
|
6975
7660
|
],
|
|
6976
7661
|
async init(ctx) {
|
|
6977
|
-
const codexDir =
|
|
7662
|
+
const codexDir = join21(ctx.projectRoot, ".codex");
|
|
6978
7663
|
mkdirSync6(codexDir, { recursive: true });
|
|
6979
7664
|
return {
|
|
6980
7665
|
filesCreated: [".codex/"],
|
|
@@ -6987,17 +7672,24 @@ var codexAdapter = {
|
|
|
6987
7672
|
const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
|
|
6988
7673
|
return {
|
|
6989
7674
|
filesWritten: result.filesWritten,
|
|
6990
|
-
filesDeleted: []
|
|
7675
|
+
filesDeleted: [],
|
|
7676
|
+
managedOutputs: result.managedOutputs
|
|
6991
7677
|
};
|
|
6992
7678
|
}
|
|
6993
7679
|
};
|
|
6994
7680
|
// ../adapters/src/cursor/index.ts
|
|
6995
7681
|
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
6996
|
-
import { join as
|
|
7682
|
+
import { join as join26 } from "node:path";
|
|
6997
7683
|
|
|
6998
7684
|
// ../adapters/src/writers/cursor/agents.ts
|
|
6999
|
-
import { mkdir as
|
|
7000
|
-
import { join as
|
|
7685
|
+
import { mkdir as mkdir10, writeFile as writeFile18 } from "node:fs/promises";
|
|
7686
|
+
import { join as join22 } from "node:path";
|
|
7687
|
+
function getClaudeConfig2(agent) {
|
|
7688
|
+
return {
|
|
7689
|
+
model: agent.claude?.model ?? agent.model,
|
|
7690
|
+
permissionMode: agent.claude?.permissionMode ?? agent.permissionMode
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7001
7693
|
function mapModelToCursor(model) {
|
|
7002
7694
|
if (!model || model === "inherit")
|
|
7003
7695
|
return "inherit";
|
|
@@ -7009,14 +7701,15 @@ function mapModelToCursor(model) {
|
|
|
7009
7701
|
return modelMap[model] ?? "inherit";
|
|
7010
7702
|
}
|
|
7011
7703
|
function generateFrontmatter2(agent) {
|
|
7704
|
+
const claude2 = getClaudeConfig2(agent);
|
|
7012
7705
|
const lines = ["---"];
|
|
7013
7706
|
lines.push(`name: ${agent.name}`);
|
|
7014
7707
|
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
7015
|
-
const model = mapModelToCursor(
|
|
7708
|
+
const model = mapModelToCursor(claude2.model);
|
|
7016
7709
|
if (model) {
|
|
7017
7710
|
lines.push(`model: ${model}`);
|
|
7018
7711
|
}
|
|
7019
|
-
if (
|
|
7712
|
+
if (claude2.permissionMode === "plan") {
|
|
7020
7713
|
lines.push("readonly: true");
|
|
7021
7714
|
}
|
|
7022
7715
|
if (agent.isBackground) {
|
|
@@ -7029,50 +7722,58 @@ function generateFrontmatter2(agent) {
|
|
|
7029
7722
|
var CursorAgentsWriter = {
|
|
7030
7723
|
id: "cursor-agents",
|
|
7031
7724
|
async write(bundle, ctx) {
|
|
7032
|
-
const agentsDir =
|
|
7033
|
-
await
|
|
7725
|
+
const agentsDir = join22(ctx.projectRoot, ctx.outputPath);
|
|
7726
|
+
await mkdir10(agentsDir, { recursive: true });
|
|
7034
7727
|
const filesWritten = [];
|
|
7728
|
+
const managedOutputs = [];
|
|
7035
7729
|
for (const agent of bundle.subagents) {
|
|
7036
7730
|
const frontmatter = generateFrontmatter2(agent);
|
|
7037
7731
|
const content = `${frontmatter}
|
|
7038
7732
|
|
|
7039
7733
|
${agent.systemPrompt}`;
|
|
7040
|
-
const agentPath =
|
|
7041
|
-
await
|
|
7042
|
-
|
|
7734
|
+
const agentPath = join22(agentsDir, `${agent.name}.md`);
|
|
7735
|
+
await writeFile18(agentPath, content, "utf-8");
|
|
7736
|
+
const relativePath = join22(ctx.outputPath, `${agent.name}.md`);
|
|
7737
|
+
filesWritten.push(relativePath);
|
|
7738
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
7043
7739
|
}
|
|
7044
7740
|
return {
|
|
7045
|
-
filesWritten
|
|
7741
|
+
filesWritten,
|
|
7742
|
+
managedOutputs
|
|
7046
7743
|
};
|
|
7047
7744
|
}
|
|
7048
7745
|
};
|
|
7049
7746
|
// ../adapters/src/writers/cursor/commands.ts
|
|
7050
|
-
import { mkdir as
|
|
7051
|
-
import { join as
|
|
7747
|
+
import { mkdir as mkdir11, writeFile as writeFile19 } from "node:fs/promises";
|
|
7748
|
+
import { join as join23 } from "node:path";
|
|
7052
7749
|
var CursorCommandsWriter = {
|
|
7053
7750
|
id: "cursor-commands",
|
|
7054
7751
|
async write(bundle, ctx) {
|
|
7055
|
-
const commandsDir =
|
|
7056
|
-
await
|
|
7752
|
+
const commandsDir = join23(ctx.projectRoot, ctx.outputPath);
|
|
7753
|
+
await mkdir11(commandsDir, { recursive: true });
|
|
7057
7754
|
const filesWritten = [];
|
|
7755
|
+
const managedOutputs = [];
|
|
7058
7756
|
for (const command of bundle.commands) {
|
|
7059
7757
|
const content = `# ${command.name}
|
|
7060
7758
|
|
|
7061
7759
|
${command.description}
|
|
7062
7760
|
|
|
7063
7761
|
${command.prompt}`;
|
|
7064
|
-
const commandPath =
|
|
7065
|
-
await
|
|
7066
|
-
|
|
7762
|
+
const commandPath = join23(commandsDir, `${command.name}.md`);
|
|
7763
|
+
await writeFile19(commandPath, content, "utf-8");
|
|
7764
|
+
const relativePath = join23(ctx.outputPath, `${command.name}.md`);
|
|
7765
|
+
filesWritten.push(relativePath);
|
|
7766
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
7067
7767
|
}
|
|
7068
7768
|
return {
|
|
7069
|
-
filesWritten
|
|
7769
|
+
filesWritten,
|
|
7770
|
+
managedOutputs
|
|
7070
7771
|
};
|
|
7071
7772
|
}
|
|
7072
7773
|
};
|
|
7073
7774
|
// ../adapters/src/writers/cursor/mcp-json.ts
|
|
7074
|
-
import { mkdir as
|
|
7075
|
-
import { dirname as
|
|
7775
|
+
import { mkdir as mkdir12, writeFile as writeFile20 } from "node:fs/promises";
|
|
7776
|
+
import { dirname as dirname6, join as join24 } from "node:path";
|
|
7076
7777
|
function buildCursorMcpConfig(mcp) {
|
|
7077
7778
|
const transport = mcp.transport ?? "stdio";
|
|
7078
7779
|
if (transport === "http" || transport === "sse") {
|
|
@@ -7117,9 +7818,9 @@ var CursorMcpJsonWriter = {
|
|
|
7117
7818
|
if (mcps.size === 0) {
|
|
7118
7819
|
return { filesWritten: [] };
|
|
7119
7820
|
}
|
|
7120
|
-
const configPath =
|
|
7121
|
-
const parentDir =
|
|
7122
|
-
await
|
|
7821
|
+
const configPath = join24(ctx.projectRoot, ctx.outputPath);
|
|
7822
|
+
const parentDir = dirname6(configPath);
|
|
7823
|
+
await mkdir12(parentDir, { recursive: true });
|
|
7123
7824
|
const mcpServers = {};
|
|
7124
7825
|
for (const [id, mcp] of mcps) {
|
|
7125
7826
|
const converted = buildCursorMcpConfig(mcp);
|
|
@@ -7133,29 +7834,35 @@ var CursorMcpJsonWriter = {
|
|
|
7133
7834
|
const cursorMcpJson = {
|
|
7134
7835
|
mcpServers
|
|
7135
7836
|
};
|
|
7136
|
-
|
|
7137
|
-
|
|
7837
|
+
const content = `${JSON.stringify(cursorMcpJson, null, 2)}
|
|
7838
|
+
`;
|
|
7839
|
+
await writeFile20(configPath, content, "utf-8");
|
|
7138
7840
|
return {
|
|
7139
|
-
filesWritten: [ctx.outputPath]
|
|
7841
|
+
filesWritten: [ctx.outputPath],
|
|
7842
|
+
managedOutputs: [createManagedOutput(ctx.outputPath, this.id, content)]
|
|
7140
7843
|
};
|
|
7141
7844
|
}
|
|
7142
7845
|
};
|
|
7143
7846
|
// ../adapters/src/writers/cursor/rules.ts
|
|
7144
|
-
import { mkdir as
|
|
7145
|
-
import { join as
|
|
7847
|
+
import { mkdir as mkdir13, writeFile as writeFile21 } from "node:fs/promises";
|
|
7848
|
+
import { join as join25 } from "node:path";
|
|
7146
7849
|
var CursorRulesWriter = {
|
|
7147
7850
|
id: "cursor-rules",
|
|
7148
7851
|
async write(bundle, ctx) {
|
|
7149
|
-
const rulesDir =
|
|
7150
|
-
await
|
|
7852
|
+
const rulesDir = join25(ctx.projectRoot, ctx.outputPath);
|
|
7853
|
+
await mkdir13(rulesDir, { recursive: true });
|
|
7151
7854
|
const filesWritten = [];
|
|
7855
|
+
const managedOutputs = [];
|
|
7152
7856
|
for (const rule of bundle.rules) {
|
|
7153
|
-
const rulePath =
|
|
7154
|
-
await
|
|
7155
|
-
|
|
7857
|
+
const rulePath = join25(rulesDir, `omnidev-${rule.name}.mdc`);
|
|
7858
|
+
await writeFile21(rulePath, rule.content, "utf-8");
|
|
7859
|
+
const relativePath = join25(ctx.outputPath, `omnidev-${rule.name}.mdc`);
|
|
7860
|
+
filesWritten.push(relativePath);
|
|
7861
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, rule.content));
|
|
7156
7862
|
}
|
|
7157
7863
|
return {
|
|
7158
|
-
filesWritten
|
|
7864
|
+
filesWritten,
|
|
7865
|
+
managedOutputs
|
|
7159
7866
|
};
|
|
7160
7867
|
}
|
|
7161
7868
|
};
|
|
@@ -7172,7 +7879,7 @@ var cursorAdapter = {
|
|
|
7172
7879
|
{ writer: CursorMcpJsonWriter, outputPath: ".cursor/mcp.json" }
|
|
7173
7880
|
],
|
|
7174
7881
|
async init(ctx) {
|
|
7175
|
-
const rulesDir =
|
|
7882
|
+
const rulesDir = join26(ctx.projectRoot, ".cursor", "rules");
|
|
7176
7883
|
mkdirSync7(rulesDir, { recursive: true });
|
|
7177
7884
|
return {
|
|
7178
7885
|
filesCreated: [".cursor/rules/"],
|
|
@@ -7192,17 +7899,25 @@ var cursorAdapter = {
|
|
|
7192
7899
|
filesWritten: [
|
|
7193
7900
|
...new Set([...instructionsResult.filesWritten, ...cursorResult.filesWritten])
|
|
7194
7901
|
],
|
|
7195
|
-
filesDeleted: []
|
|
7902
|
+
filesDeleted: [],
|
|
7903
|
+
managedOutputs: [...instructionsResult.managedOutputs, ...cursorResult.managedOutputs]
|
|
7196
7904
|
};
|
|
7197
7905
|
}
|
|
7198
7906
|
};
|
|
7199
7907
|
// ../adapters/src/opencode/index.ts
|
|
7200
7908
|
import { mkdirSync as mkdirSync8 } from "node:fs";
|
|
7201
|
-
import { join as
|
|
7909
|
+
import { join as join29 } from "node:path";
|
|
7202
7910
|
|
|
7203
7911
|
// ../adapters/src/writers/opencode/agents.ts
|
|
7204
|
-
import { mkdir as
|
|
7205
|
-
import { join as
|
|
7912
|
+
import { mkdir as mkdir14, writeFile as writeFile22 } from "node:fs/promises";
|
|
7913
|
+
import { join as join27 } from "node:path";
|
|
7914
|
+
function getClaudeConfig3(agent) {
|
|
7915
|
+
return {
|
|
7916
|
+
tools: agent.claude?.tools ?? agent.tools,
|
|
7917
|
+
model: agent.claude?.model ?? agent.model,
|
|
7918
|
+
permissionMode: agent.claude?.permissionMode ?? agent.permissionMode
|
|
7919
|
+
};
|
|
7920
|
+
}
|
|
7206
7921
|
function mapModelToOpenCode(model) {
|
|
7207
7922
|
if (!model || model === "inherit")
|
|
7208
7923
|
return;
|
|
@@ -7234,9 +7949,10 @@ function mapToolsToOpenCode(tools) {
|
|
|
7234
7949
|
return toolsObject;
|
|
7235
7950
|
}
|
|
7236
7951
|
function generateFrontmatter3(agent) {
|
|
7952
|
+
const claude2 = getClaudeConfig3(agent);
|
|
7237
7953
|
const lines = ["---"];
|
|
7238
7954
|
lines.push(`description: "${agent.description.replace(/"/g, "\\\"")}"`);
|
|
7239
|
-
const modelId = agent.modelId ?? mapModelToOpenCode(
|
|
7955
|
+
const modelId = agent.modelId ?? mapModelToOpenCode(claude2.model);
|
|
7240
7956
|
if (modelId) {
|
|
7241
7957
|
lines.push(`model: ${modelId}`);
|
|
7242
7958
|
}
|
|
@@ -7252,14 +7968,14 @@ function generateFrontmatter3(agent) {
|
|
|
7252
7968
|
if (agent.hidden !== undefined) {
|
|
7253
7969
|
lines.push(`hidden: ${agent.hidden}`);
|
|
7254
7970
|
}
|
|
7255
|
-
const toolsObj = agent.toolPermissions ?? mapToolsToOpenCode(
|
|
7971
|
+
const toolsObj = agent.toolPermissions ?? mapToolsToOpenCode(claude2.tools);
|
|
7256
7972
|
if (toolsObj) {
|
|
7257
7973
|
lines.push("tools:");
|
|
7258
7974
|
for (const [tool, enabled] of Object.entries(toolsObj)) {
|
|
7259
7975
|
lines.push(` ${tool}: ${enabled}`);
|
|
7260
7976
|
}
|
|
7261
7977
|
}
|
|
7262
|
-
const permissions = agent.permissions ?? mapPermissionsToOpenCode(
|
|
7978
|
+
const permissions = agent.permissions ?? mapPermissionsToOpenCode(claude2.permissionMode);
|
|
7263
7979
|
if (permissions) {
|
|
7264
7980
|
lines.push("permissions:");
|
|
7265
7981
|
for (const [key, value] of Object.entries(permissions)) {
|
|
@@ -7280,26 +7996,30 @@ function generateFrontmatter3(agent) {
|
|
|
7280
7996
|
var OpenCodeAgentsWriter = {
|
|
7281
7997
|
id: "opencode-agents",
|
|
7282
7998
|
async write(bundle, ctx) {
|
|
7283
|
-
const agentsDir =
|
|
7284
|
-
await
|
|
7999
|
+
const agentsDir = join27(ctx.projectRoot, ctx.outputPath);
|
|
8000
|
+
await mkdir14(agentsDir, { recursive: true });
|
|
7285
8001
|
const filesWritten = [];
|
|
8002
|
+
const managedOutputs = [];
|
|
7286
8003
|
for (const agent of bundle.subagents) {
|
|
7287
8004
|
const frontmatter = generateFrontmatter3(agent);
|
|
7288
8005
|
const content = `${frontmatter}
|
|
7289
8006
|
|
|
7290
8007
|
${agent.systemPrompt}`;
|
|
7291
|
-
const agentPath =
|
|
7292
|
-
await
|
|
7293
|
-
|
|
8008
|
+
const agentPath = join27(agentsDir, `${agent.name}.md`);
|
|
8009
|
+
await writeFile22(agentPath, content, "utf-8");
|
|
8010
|
+
const relativePath = join27(ctx.outputPath, `${agent.name}.md`);
|
|
8011
|
+
filesWritten.push(relativePath);
|
|
8012
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
7294
8013
|
}
|
|
7295
8014
|
return {
|
|
7296
|
-
filesWritten
|
|
8015
|
+
filesWritten,
|
|
8016
|
+
managedOutputs
|
|
7297
8017
|
};
|
|
7298
8018
|
}
|
|
7299
8019
|
};
|
|
7300
8020
|
// ../adapters/src/writers/opencode/commands.ts
|
|
7301
|
-
import { mkdir as
|
|
7302
|
-
import { join as
|
|
8021
|
+
import { mkdir as mkdir15, writeFile as writeFile23 } from "node:fs/promises";
|
|
8022
|
+
import { join as join28 } from "node:path";
|
|
7303
8023
|
function generateFrontmatter4(command) {
|
|
7304
8024
|
const lines = ["---"];
|
|
7305
8025
|
lines.push(`description: "${command.description.replace(/"/g, "\\\"")}"`);
|
|
@@ -7316,20 +8036,24 @@ function generateFrontmatter4(command) {
|
|
|
7316
8036
|
var OpenCodeCommandsWriter = {
|
|
7317
8037
|
id: "opencode-commands",
|
|
7318
8038
|
async write(bundle, ctx) {
|
|
7319
|
-
const commandsDir =
|
|
7320
|
-
await
|
|
8039
|
+
const commandsDir = join28(ctx.projectRoot, ctx.outputPath);
|
|
8040
|
+
await mkdir15(commandsDir, { recursive: true });
|
|
7321
8041
|
const filesWritten = [];
|
|
8042
|
+
const managedOutputs = [];
|
|
7322
8043
|
for (const command of bundle.commands) {
|
|
7323
8044
|
const frontmatter = generateFrontmatter4(command);
|
|
7324
8045
|
const content = `${frontmatter}
|
|
7325
8046
|
|
|
7326
8047
|
${command.prompt}`;
|
|
7327
|
-
const commandPath =
|
|
7328
|
-
await
|
|
7329
|
-
|
|
8048
|
+
const commandPath = join28(commandsDir, `${command.name}.md`);
|
|
8049
|
+
await writeFile23(commandPath, content, "utf-8");
|
|
8050
|
+
const relativePath = join28(ctx.outputPath, `${command.name}.md`);
|
|
8051
|
+
filesWritten.push(relativePath);
|
|
8052
|
+
managedOutputs.push(createManagedOutput(relativePath, this.id, content));
|
|
7330
8053
|
}
|
|
7331
8054
|
return {
|
|
7332
|
-
filesWritten
|
|
8055
|
+
filesWritten,
|
|
8056
|
+
managedOutputs
|
|
7333
8057
|
};
|
|
7334
8058
|
}
|
|
7335
8059
|
};
|
|
@@ -7344,7 +8068,7 @@ var opencodeAdapter = {
|
|
|
7344
8068
|
{ writer: OpenCodeCommandsWriter, outputPath: ".opencode/commands/" }
|
|
7345
8069
|
],
|
|
7346
8070
|
async init(ctx) {
|
|
7347
|
-
const opencodeDir =
|
|
8071
|
+
const opencodeDir = join29(ctx.projectRoot, ".opencode");
|
|
7348
8072
|
mkdirSync8(opencodeDir, { recursive: true });
|
|
7349
8073
|
return {
|
|
7350
8074
|
filesCreated: [".opencode/"],
|
|
@@ -7357,7 +8081,8 @@ var opencodeAdapter = {
|
|
|
7357
8081
|
const result = await executeWriters(this.writers, providerBundle, ctx.projectRoot, providerId);
|
|
7358
8082
|
return {
|
|
7359
8083
|
filesWritten: result.filesWritten,
|
|
7360
|
-
filesDeleted: []
|
|
8084
|
+
filesDeleted: [],
|
|
8085
|
+
managedOutputs: result.managedOutputs
|
|
7361
8086
|
};
|
|
7362
8087
|
}
|
|
7363
8088
|
};
|
|
@@ -7645,7 +8370,7 @@ If the capability name is omitted, it will be inferred from:
|
|
|
7645
8370
|
- For GitHub sources: the repository name or last path segment
|
|
7646
8371
|
|
|
7647
8372
|
Claude plugins (.claude-plugin/plugin.json) are automatically wrapped as OmniDev capabilities.
|
|
7648
|
-
Hooks defined in hooks.json are also supported and will be synced
|
|
8373
|
+
Hooks defined in hooks.json are also supported and will be synced into OmniDev's hook pipeline.
|
|
7649
8374
|
|
|
7650
8375
|
Examples:
|
|
7651
8376
|
omnidev add cap my-cap --github expo/skills # Uses version = "latest"
|
|
@@ -7801,8 +8526,8 @@ var addRoutes = buildRouteMap({
|
|
|
7801
8526
|
|
|
7802
8527
|
// src/commands/capability.ts
|
|
7803
8528
|
import { existsSync as existsSync25, mkdirSync as mkdirSync9 } from "node:fs";
|
|
7804
|
-
import { writeFile as
|
|
7805
|
-
import { join as
|
|
8529
|
+
import { writeFile as writeFile24 } from "node:fs/promises";
|
|
8530
|
+
import { join as join30 } from "node:path";
|
|
7806
8531
|
import { input } from "@inquirer/prompts";
|
|
7807
8532
|
init_src();
|
|
7808
8533
|
import { buildCommand as buildCommand2, buildRouteMap as buildRouteMap2 } from "@stricli/core";
|
|
@@ -8022,21 +8747,21 @@ async function runCapabilityNew(flags, capabilityId) {
|
|
|
8022
8747
|
const name = toTitleCase(id);
|
|
8023
8748
|
mkdirSync9(capabilityDir, { recursive: true });
|
|
8024
8749
|
const capabilityToml = generateCapabilityToml2({ id, name });
|
|
8025
|
-
await
|
|
8026
|
-
const skillDir =
|
|
8750
|
+
await writeFile24(join30(capabilityDir, "capability.toml"), capabilityToml, "utf-8");
|
|
8751
|
+
const skillDir = join30(capabilityDir, "skills", "getting-started");
|
|
8027
8752
|
mkdirSync9(skillDir, { recursive: true });
|
|
8028
|
-
await
|
|
8029
|
-
const rulesDir =
|
|
8753
|
+
await writeFile24(join30(skillDir, "SKILL.md"), generateSkillTemplate("getting-started"), "utf-8");
|
|
8754
|
+
const rulesDir = join30(capabilityDir, "rules");
|
|
8030
8755
|
mkdirSync9(rulesDir, { recursive: true });
|
|
8031
|
-
await
|
|
8032
|
-
const hooksDir =
|
|
8756
|
+
await writeFile24(join30(rulesDir, "coding-standards.md"), generateRuleTemplate("coding-standards"), "utf-8");
|
|
8757
|
+
const hooksDir = join30(capabilityDir, "hooks");
|
|
8033
8758
|
mkdirSync9(hooksDir, { recursive: true });
|
|
8034
|
-
await
|
|
8035
|
-
await
|
|
8036
|
-
await
|
|
8759
|
+
await writeFile24(join30(hooksDir, "hooks.toml"), generateHooksTemplate(), "utf-8");
|
|
8760
|
+
await writeFile24(join30(hooksDir, "example-hook.sh"), generateHookScript(), "utf-8");
|
|
8761
|
+
await writeFile24(join30(capabilityDir, ".gitignore"), generateGitignore(Boolean(flags.programmatic)), "utf-8");
|
|
8037
8762
|
if (flags.programmatic) {
|
|
8038
|
-
await
|
|
8039
|
-
await
|
|
8763
|
+
await writeFile24(join30(capabilityDir, "package.json"), generatePackageJson(id), "utf-8");
|
|
8764
|
+
await writeFile24(join30(capabilityDir, "index.ts"), generateIndexTs(id, name), "utf-8");
|
|
8040
8765
|
}
|
|
8041
8766
|
console.log(`✓ Created capability: ${name}`);
|
|
8042
8767
|
console.log(` Location: ${capabilityDir}`);
|
|
@@ -8379,7 +9104,7 @@ async function checkCapabilitiesDir() {
|
|
|
8379
9104
|
// src/commands/init.ts
|
|
8380
9105
|
import { exec } from "node:child_process";
|
|
8381
9106
|
import { existsSync as existsSync27, mkdirSync as mkdirSync10 } from "node:fs";
|
|
8382
|
-
import { readFile as readFile21, writeFile as
|
|
9107
|
+
import { readFile as readFile21, writeFile as writeFile25 } from "node:fs/promises";
|
|
8383
9108
|
import { promisify as promisify2 } from "node:util";
|
|
8384
9109
|
init_src();
|
|
8385
9110
|
import { buildCommand as buildCommand4 } from "@stricli/core";
|
|
@@ -8465,7 +9190,7 @@ async function runInit(_flags, providerArg) {
|
|
|
8465
9190
|
await setActiveProfile("default");
|
|
8466
9191
|
}
|
|
8467
9192
|
if (!existsSync27("OMNI.md")) {
|
|
8468
|
-
await
|
|
9193
|
+
await writeFile25("OMNI.md", generateOmniMdTemplate(), "utf-8");
|
|
8469
9194
|
}
|
|
8470
9195
|
const config3 = await loadConfig();
|
|
8471
9196
|
const ctx = {
|
|
@@ -8558,7 +9283,7 @@ async function addToGitignore(entriesToAdd, sectionHeader) {
|
|
|
8558
9283
|
${missingEntries.join(`
|
|
8559
9284
|
`)}
|
|
8560
9285
|
`;
|
|
8561
|
-
await
|
|
9286
|
+
await writeFile25(gitignorePath, content + section, "utf-8");
|
|
8562
9287
|
}
|
|
8563
9288
|
async function getTrackedProviderFiles(files) {
|
|
8564
9289
|
const tracked = [];
|
|
@@ -8691,6 +9416,8 @@ async function runProfileSet(profileName) {
|
|
|
8691
9416
|
}
|
|
8692
9417
|
|
|
8693
9418
|
// src/commands/provider.ts
|
|
9419
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
9420
|
+
import { readFile as readFile22 } from "node:fs/promises";
|
|
8694
9421
|
init_src();
|
|
8695
9422
|
import { buildCommand as buildCommand6, buildRouteMap as buildRouteMap4 } from "@stricli/core";
|
|
8696
9423
|
async function runProviderList() {
|
|
@@ -8724,6 +9451,7 @@ async function runProviderEnable(_flags, providerId) {
|
|
|
8724
9451
|
}
|
|
8725
9452
|
await enableProvider(providerId);
|
|
8726
9453
|
console.log(`✓ Enabled provider: ${adapter.displayName}`);
|
|
9454
|
+
await maybeRemindAboutProviderGitignore(providerId);
|
|
8727
9455
|
const enabledAdapters = await getEnabledAdapters();
|
|
8728
9456
|
await syncAgentConfiguration({ silent: false, adapters: enabledAdapters });
|
|
8729
9457
|
}
|
|
@@ -8804,10 +9532,33 @@ var providerRoutes = buildRouteMap4({
|
|
|
8804
9532
|
brief: "Manage AI provider adapters"
|
|
8805
9533
|
}
|
|
8806
9534
|
});
|
|
9535
|
+
async function maybeRemindAboutProviderGitignore(providerId) {
|
|
9536
|
+
const missingEntries = await getMissingGitignoreEntries(getProviderGitignoreFiles([providerId]));
|
|
9537
|
+
if (missingEntries.length === 0) {
|
|
9538
|
+
return;
|
|
9539
|
+
}
|
|
9540
|
+
console.log("");
|
|
9541
|
+
console.log("Also update your .gitignore to ignore provider files if you do not want to commit them:");
|
|
9542
|
+
for (const entry of missingEntries) {
|
|
9543
|
+
console.log(` - ${entry}`);
|
|
9544
|
+
}
|
|
9545
|
+
}
|
|
9546
|
+
async function getMissingGitignoreEntries(entries) {
|
|
9547
|
+
if (entries.length === 0) {
|
|
9548
|
+
return [];
|
|
9549
|
+
}
|
|
9550
|
+
let content = "";
|
|
9551
|
+
if (existsSync29(".gitignore")) {
|
|
9552
|
+
content = await readFile22(".gitignore", "utf-8");
|
|
9553
|
+
}
|
|
9554
|
+
const lines = new Set(content.split(`
|
|
9555
|
+
`).map((line) => line.trim()));
|
|
9556
|
+
return [...new Set(entries)].filter((entry) => !lines.has(entry));
|
|
9557
|
+
}
|
|
8807
9558
|
|
|
8808
9559
|
// src/commands/security.ts
|
|
8809
9560
|
init_src();
|
|
8810
|
-
import { existsSync as
|
|
9561
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
8811
9562
|
import { buildCommand as buildCommand7, buildRouteMap as buildRouteMap5 } from "@stricli/core";
|
|
8812
9563
|
var VALID_FINDING_TYPES = [
|
|
8813
9564
|
"unicode_bidi",
|
|
@@ -8926,7 +9677,7 @@ function formatFindingsWithHints(summary) {
|
|
|
8926
9677
|
}
|
|
8927
9678
|
async function runSecurityIssues(flags = {}) {
|
|
8928
9679
|
try {
|
|
8929
|
-
if (!
|
|
9680
|
+
if (!existsSync30("omni.toml")) {
|
|
8930
9681
|
console.log("No config file found");
|
|
8931
9682
|
console.log(" Run: omnidev init");
|
|
8932
9683
|
process.exit(1);
|
|
@@ -9181,7 +9932,7 @@ var securityRoutes = buildRouteMap5({
|
|
|
9181
9932
|
});
|
|
9182
9933
|
|
|
9183
9934
|
// src/commands/sync.ts
|
|
9184
|
-
import { existsSync as
|
|
9935
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
9185
9936
|
init_src();
|
|
9186
9937
|
import { buildCommand as buildCommand8 } from "@stricli/core";
|
|
9187
9938
|
var PROVIDERS_STATE_PATH = ".omni/state/providers.json";
|
|
@@ -9199,7 +9950,7 @@ async function runSync() {
|
|
|
9199
9950
|
const config3 = await loadConfig();
|
|
9200
9951
|
const activeProfile = await getActiveProfile() ?? "default";
|
|
9201
9952
|
let adapters = await getEnabledAdapters();
|
|
9202
|
-
if (!
|
|
9953
|
+
if (!existsSync31(PROVIDERS_STATE_PATH) || adapters.length === 0) {
|
|
9203
9954
|
console.log("No providers configured yet. Select your provider(s):");
|
|
9204
9955
|
const providerIds = await promptForProviders();
|
|
9205
9956
|
await writeEnabledProviders(providerIds);
|
|
@@ -9409,9 +10160,9 @@ async function buildDynamicApp() {
|
|
|
9409
10160
|
security: securityRoutes
|
|
9410
10161
|
};
|
|
9411
10162
|
debug("Core routes registered", Object.keys(routes));
|
|
9412
|
-
const configPath =
|
|
9413
|
-
debug("Checking for config", { configPath, exists:
|
|
9414
|
-
if (
|
|
10163
|
+
const configPath = join31(process.cwd(), "omni.toml");
|
|
10164
|
+
debug("Checking for config", { configPath, exists: existsSync32(configPath), cwd: process.cwd() });
|
|
10165
|
+
if (existsSync32(configPath)) {
|
|
9415
10166
|
try {
|
|
9416
10167
|
debug("Loading capability commands...");
|
|
9417
10168
|
const capabilityCommands = await loadCapabilityCommands();
|
|
@@ -9486,25 +10237,25 @@ async function loadCapabilityCommands() {
|
|
|
9486
10237
|
return commands;
|
|
9487
10238
|
}
|
|
9488
10239
|
async function loadCapabilityExport(capability3) {
|
|
9489
|
-
const capabilityPath =
|
|
9490
|
-
const builtIndexPath =
|
|
9491
|
-
const jsIndexPath =
|
|
9492
|
-
const tsIndexPath =
|
|
10240
|
+
const capabilityPath = join31(process.cwd(), capability3.path);
|
|
10241
|
+
const builtIndexPath = join31(capabilityPath, "dist", "index.js");
|
|
10242
|
+
const jsIndexPath = join31(capabilityPath, "index.js");
|
|
10243
|
+
const tsIndexPath = join31(capabilityPath, "index.ts");
|
|
9493
10244
|
debug(`Checking entry points for '${capability3.id}'`, {
|
|
9494
10245
|
capabilityPath,
|
|
9495
10246
|
builtIndexPath,
|
|
9496
|
-
builtExists:
|
|
10247
|
+
builtExists: existsSync32(builtIndexPath),
|
|
9497
10248
|
jsIndexPath,
|
|
9498
|
-
jsExists:
|
|
10249
|
+
jsExists: existsSync32(jsIndexPath),
|
|
9499
10250
|
tsIndexPath,
|
|
9500
|
-
tsExists:
|
|
10251
|
+
tsExists: existsSync32(tsIndexPath)
|
|
9501
10252
|
});
|
|
9502
10253
|
let indexPath = null;
|
|
9503
|
-
if (
|
|
10254
|
+
if (existsSync32(builtIndexPath)) {
|
|
9504
10255
|
indexPath = builtIndexPath;
|
|
9505
|
-
} else if (
|
|
10256
|
+
} else if (existsSync32(jsIndexPath)) {
|
|
9506
10257
|
indexPath = jsIndexPath;
|
|
9507
|
-
} else if (
|
|
10258
|
+
} else if (existsSync32(tsIndexPath)) {
|
|
9508
10259
|
indexPath = tsIndexPath;
|
|
9509
10260
|
}
|
|
9510
10261
|
if (!indexPath) {
|