oh-my-opencode 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +1 -0
- package/README.md +1 -0
- package/dist/hooks/directory-readme-injector/constants.d.ts +3 -0
- package/dist/hooks/directory-readme-injector/index.d.ts +22 -0
- package/dist/hooks/directory-readme-injector/storage.d.ts +3 -0
- package/dist/hooks/directory-readme-injector/types.d.ts +5 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.js +435 -261
- package/dist/tools/skill/tools.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +17 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1831,6 +1831,147 @@ ${content}`;
|
|
|
1831
1831
|
event: eventHandler
|
|
1832
1832
|
};
|
|
1833
1833
|
}
|
|
1834
|
+
// src/hooks/directory-readme-injector/index.ts
|
|
1835
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
1836
|
+
import { dirname as dirname3, join as join10, resolve as resolve2 } from "path";
|
|
1837
|
+
|
|
1838
|
+
// src/hooks/directory-readme-injector/storage.ts
|
|
1839
|
+
import {
|
|
1840
|
+
existsSync as existsSync7,
|
|
1841
|
+
mkdirSync as mkdirSync4,
|
|
1842
|
+
readFileSync as readFileSync4,
|
|
1843
|
+
writeFileSync as writeFileSync3,
|
|
1844
|
+
unlinkSync as unlinkSync4
|
|
1845
|
+
} from "fs";
|
|
1846
|
+
import { join as join9 } from "path";
|
|
1847
|
+
|
|
1848
|
+
// src/hooks/directory-readme-injector/constants.ts
|
|
1849
|
+
import { join as join8 } from "path";
|
|
1850
|
+
var OPENCODE_STORAGE3 = join8(xdgData ?? "", "opencode", "storage");
|
|
1851
|
+
var README_INJECTOR_STORAGE = join8(OPENCODE_STORAGE3, "directory-readme");
|
|
1852
|
+
var README_FILENAME = "README.md";
|
|
1853
|
+
|
|
1854
|
+
// src/hooks/directory-readme-injector/storage.ts
|
|
1855
|
+
function getStoragePath2(sessionID) {
|
|
1856
|
+
return join9(README_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
1857
|
+
}
|
|
1858
|
+
function loadInjectedPaths2(sessionID) {
|
|
1859
|
+
const filePath = getStoragePath2(sessionID);
|
|
1860
|
+
if (!existsSync7(filePath))
|
|
1861
|
+
return new Set;
|
|
1862
|
+
try {
|
|
1863
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
1864
|
+
const data = JSON.parse(content);
|
|
1865
|
+
return new Set(data.injectedPaths);
|
|
1866
|
+
} catch {
|
|
1867
|
+
return new Set;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
function saveInjectedPaths2(sessionID, paths) {
|
|
1871
|
+
if (!existsSync7(README_INJECTOR_STORAGE)) {
|
|
1872
|
+
mkdirSync4(README_INJECTOR_STORAGE, { recursive: true });
|
|
1873
|
+
}
|
|
1874
|
+
const data = {
|
|
1875
|
+
sessionID,
|
|
1876
|
+
injectedPaths: [...paths],
|
|
1877
|
+
updatedAt: Date.now()
|
|
1878
|
+
};
|
|
1879
|
+
writeFileSync3(getStoragePath2(sessionID), JSON.stringify(data, null, 2));
|
|
1880
|
+
}
|
|
1881
|
+
function clearInjectedPaths2(sessionID) {
|
|
1882
|
+
const filePath = getStoragePath2(sessionID);
|
|
1883
|
+
if (existsSync7(filePath)) {
|
|
1884
|
+
unlinkSync4(filePath);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// src/hooks/directory-readme-injector/index.ts
|
|
1889
|
+
function createDirectoryReadmeInjectorHook(ctx) {
|
|
1890
|
+
const sessionCaches = new Map;
|
|
1891
|
+
function getSessionCache(sessionID) {
|
|
1892
|
+
if (!sessionCaches.has(sessionID)) {
|
|
1893
|
+
sessionCaches.set(sessionID, loadInjectedPaths2(sessionID));
|
|
1894
|
+
}
|
|
1895
|
+
return sessionCaches.get(sessionID);
|
|
1896
|
+
}
|
|
1897
|
+
function resolveFilePath(title) {
|
|
1898
|
+
if (!title)
|
|
1899
|
+
return null;
|
|
1900
|
+
if (title.startsWith("/"))
|
|
1901
|
+
return title;
|
|
1902
|
+
return resolve2(ctx.directory, title);
|
|
1903
|
+
}
|
|
1904
|
+
function findReadmeMdUp(startDir) {
|
|
1905
|
+
const found = [];
|
|
1906
|
+
let current = startDir;
|
|
1907
|
+
while (true) {
|
|
1908
|
+
const readmePath = join10(current, README_FILENAME);
|
|
1909
|
+
if (existsSync8(readmePath)) {
|
|
1910
|
+
found.push(readmePath);
|
|
1911
|
+
}
|
|
1912
|
+
if (current === ctx.directory)
|
|
1913
|
+
break;
|
|
1914
|
+
const parent = dirname3(current);
|
|
1915
|
+
if (parent === current)
|
|
1916
|
+
break;
|
|
1917
|
+
if (!parent.startsWith(ctx.directory))
|
|
1918
|
+
break;
|
|
1919
|
+
current = parent;
|
|
1920
|
+
}
|
|
1921
|
+
return found.reverse();
|
|
1922
|
+
}
|
|
1923
|
+
const toolExecuteAfter = async (input, output) => {
|
|
1924
|
+
if (input.tool.toLowerCase() !== "read")
|
|
1925
|
+
return;
|
|
1926
|
+
const filePath = resolveFilePath(output.title);
|
|
1927
|
+
if (!filePath)
|
|
1928
|
+
return;
|
|
1929
|
+
const dir = dirname3(filePath);
|
|
1930
|
+
const cache = getSessionCache(input.sessionID);
|
|
1931
|
+
const readmePaths = findReadmeMdUp(dir);
|
|
1932
|
+
const toInject = [];
|
|
1933
|
+
for (const readmePath of readmePaths) {
|
|
1934
|
+
const readmeDir = dirname3(readmePath);
|
|
1935
|
+
if (cache.has(readmeDir))
|
|
1936
|
+
continue;
|
|
1937
|
+
try {
|
|
1938
|
+
const content = readFileSync5(readmePath, "utf-8");
|
|
1939
|
+
toInject.push({ path: readmePath, content });
|
|
1940
|
+
cache.add(readmeDir);
|
|
1941
|
+
} catch {}
|
|
1942
|
+
}
|
|
1943
|
+
if (toInject.length === 0)
|
|
1944
|
+
return;
|
|
1945
|
+
for (const { path: path2, content } of toInject) {
|
|
1946
|
+
output.output += `
|
|
1947
|
+
|
|
1948
|
+
[Project README: ${path2}]
|
|
1949
|
+
${content}`;
|
|
1950
|
+
}
|
|
1951
|
+
saveInjectedPaths2(input.sessionID, cache);
|
|
1952
|
+
};
|
|
1953
|
+
const eventHandler = async ({ event }) => {
|
|
1954
|
+
const props = event.properties;
|
|
1955
|
+
if (event.type === "session.deleted") {
|
|
1956
|
+
const sessionInfo = props?.info;
|
|
1957
|
+
if (sessionInfo?.id) {
|
|
1958
|
+
sessionCaches.delete(sessionInfo.id);
|
|
1959
|
+
clearInjectedPaths2(sessionInfo.id);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
if (event.type === "session.compacted") {
|
|
1963
|
+
const sessionID = props?.sessionID ?? props?.info?.id;
|
|
1964
|
+
if (sessionID) {
|
|
1965
|
+
sessionCaches.delete(sessionID);
|
|
1966
|
+
clearInjectedPaths2(sessionID);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
};
|
|
1970
|
+
return {
|
|
1971
|
+
"tool.execute.after": toolExecuteAfter,
|
|
1972
|
+
event: eventHandler
|
|
1973
|
+
};
|
|
1974
|
+
}
|
|
1834
1975
|
// src/hooks/empty-task-response-detector.ts
|
|
1835
1976
|
var EMPTY_RESPONSE_WARNING = `[Task Empty Response Warning]
|
|
1836
1977
|
|
|
@@ -2306,8 +2447,8 @@ function createThinkModeHook() {
|
|
|
2306
2447
|
}
|
|
2307
2448
|
// src/hooks/claude-code-hooks/config.ts
|
|
2308
2449
|
import { homedir as homedir2 } from "os";
|
|
2309
|
-
import { join as
|
|
2310
|
-
import { existsSync as
|
|
2450
|
+
import { join as join11 } from "path";
|
|
2451
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2311
2452
|
function normalizeHookMatcher(raw) {
|
|
2312
2453
|
return {
|
|
2313
2454
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -2332,11 +2473,11 @@ function normalizeHooksConfig(raw) {
|
|
|
2332
2473
|
function getClaudeSettingsPaths(customPath) {
|
|
2333
2474
|
const home = homedir2();
|
|
2334
2475
|
const paths = [
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2476
|
+
join11(home, ".claude", "settings.json"),
|
|
2477
|
+
join11(process.cwd(), ".claude", "settings.json"),
|
|
2478
|
+
join11(process.cwd(), ".claude", "settings.local.json")
|
|
2338
2479
|
];
|
|
2339
|
-
if (customPath &&
|
|
2480
|
+
if (customPath && existsSync9(customPath)) {
|
|
2340
2481
|
paths.unshift(customPath);
|
|
2341
2482
|
}
|
|
2342
2483
|
return paths;
|
|
@@ -2360,7 +2501,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
2360
2501
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
2361
2502
|
let mergedConfig = {};
|
|
2362
2503
|
for (const settingsPath of paths) {
|
|
2363
|
-
if (
|
|
2504
|
+
if (existsSync9(settingsPath)) {
|
|
2364
2505
|
try {
|
|
2365
2506
|
const content = await Bun.file(settingsPath).text();
|
|
2366
2507
|
const settings = JSON.parse(content);
|
|
@@ -2377,9 +2518,9 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
2377
2518
|
}
|
|
2378
2519
|
|
|
2379
2520
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
2380
|
-
import { existsSync as
|
|
2521
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2381
2522
|
import { homedir as homedir3 } from "os";
|
|
2382
|
-
import { join as
|
|
2523
|
+
import { join as join13 } from "path";
|
|
2383
2524
|
|
|
2384
2525
|
// src/shared/logger.ts
|
|
2385
2526
|
import * as fs3 from "fs";
|
|
@@ -2396,12 +2537,12 @@ function log(message, data) {
|
|
|
2396
2537
|
}
|
|
2397
2538
|
|
|
2398
2539
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
2399
|
-
var USER_CONFIG_PATH =
|
|
2540
|
+
var USER_CONFIG_PATH = join13(homedir3(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
2400
2541
|
function getProjectConfigPath() {
|
|
2401
|
-
return
|
|
2542
|
+
return join13(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
2402
2543
|
}
|
|
2403
2544
|
async function loadConfigFromPath(path3) {
|
|
2404
|
-
if (!
|
|
2545
|
+
if (!existsSync10(path3)) {
|
|
2405
2546
|
return null;
|
|
2406
2547
|
}
|
|
2407
2548
|
try {
|
|
@@ -2494,14 +2635,14 @@ function parseFrontmatter(content) {
|
|
|
2494
2635
|
import { spawn as spawn3 } from "child_process";
|
|
2495
2636
|
import { exec } from "child_process";
|
|
2496
2637
|
import { promisify } from "util";
|
|
2497
|
-
import { existsSync as
|
|
2638
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2498
2639
|
var DEFAULT_ZSH_PATHS = ["/bin/zsh", "/usr/bin/zsh", "/usr/local/bin/zsh"];
|
|
2499
2640
|
function findZshPath(customZshPath) {
|
|
2500
|
-
if (customZshPath &&
|
|
2641
|
+
if (customZshPath && existsSync11(customZshPath)) {
|
|
2501
2642
|
return customZshPath;
|
|
2502
2643
|
}
|
|
2503
2644
|
for (const path3 of DEFAULT_ZSH_PATHS) {
|
|
2504
|
-
if (
|
|
2645
|
+
if (existsSync11(path3)) {
|
|
2505
2646
|
return path3;
|
|
2506
2647
|
}
|
|
2507
2648
|
}
|
|
@@ -2519,7 +2660,7 @@ async function executeHookCommand(command, stdin, cwd, options) {
|
|
|
2519
2660
|
finalCommand = `${zshPath} -lc '${escapedCommand}'`;
|
|
2520
2661
|
}
|
|
2521
2662
|
}
|
|
2522
|
-
return new Promise((
|
|
2663
|
+
return new Promise((resolve3) => {
|
|
2523
2664
|
const proc = spawn3(finalCommand, {
|
|
2524
2665
|
cwd,
|
|
2525
2666
|
shell: true,
|
|
@@ -2536,14 +2677,14 @@ async function executeHookCommand(command, stdin, cwd, options) {
|
|
|
2536
2677
|
proc.stdin?.write(stdin);
|
|
2537
2678
|
proc.stdin?.end();
|
|
2538
2679
|
proc.on("close", (code) => {
|
|
2539
|
-
|
|
2680
|
+
resolve3({
|
|
2540
2681
|
exitCode: code ?? 0,
|
|
2541
2682
|
stdout: stdout.trim(),
|
|
2542
2683
|
stderr: stderr.trim()
|
|
2543
2684
|
});
|
|
2544
2685
|
});
|
|
2545
2686
|
proc.on("error", (err) => {
|
|
2546
|
-
|
|
2687
|
+
resolve3({
|
|
2547
2688
|
exitCode: 1,
|
|
2548
2689
|
stderr: err.message
|
|
2549
2690
|
});
|
|
@@ -2619,8 +2760,8 @@ async function resolveCommandsInText(text, depth = 0, maxDepth = 3) {
|
|
|
2619
2760
|
return resolved;
|
|
2620
2761
|
}
|
|
2621
2762
|
// src/shared/file-reference-resolver.ts
|
|
2622
|
-
import { existsSync as
|
|
2623
|
-
import { join as
|
|
2763
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, statSync } from "fs";
|
|
2764
|
+
import { join as join14, isAbsolute } from "path";
|
|
2624
2765
|
var FILE_REFERENCE_PATTERN = /@([^\s@]+)/g;
|
|
2625
2766
|
function findFileReferences(text) {
|
|
2626
2767
|
const matches = [];
|
|
@@ -2640,17 +2781,17 @@ function resolveFilePath(filePath, cwd) {
|
|
|
2640
2781
|
if (isAbsolute(filePath)) {
|
|
2641
2782
|
return filePath;
|
|
2642
2783
|
}
|
|
2643
|
-
return
|
|
2784
|
+
return join14(cwd, filePath);
|
|
2644
2785
|
}
|
|
2645
2786
|
function readFileContent(resolvedPath) {
|
|
2646
|
-
if (!
|
|
2787
|
+
if (!existsSync12(resolvedPath)) {
|
|
2647
2788
|
return `[file not found: ${resolvedPath}]`;
|
|
2648
2789
|
}
|
|
2649
2790
|
const stat = statSync(resolvedPath);
|
|
2650
2791
|
if (stat.isDirectory()) {
|
|
2651
2792
|
return `[cannot read directory: ${resolvedPath}]`;
|
|
2652
2793
|
}
|
|
2653
|
-
const content =
|
|
2794
|
+
const content = readFileSync6(resolvedPath, "utf-8");
|
|
2654
2795
|
return content;
|
|
2655
2796
|
}
|
|
2656
2797
|
async function resolveFileReferencesInText(text, cwd = process.cwd(), depth = 0, maxDepth = 3) {
|
|
@@ -2873,17 +3014,17 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
2873
3014
|
}
|
|
2874
3015
|
|
|
2875
3016
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
2876
|
-
import { join as
|
|
2877
|
-
import { mkdirSync as
|
|
3017
|
+
import { join as join15 } from "path";
|
|
3018
|
+
import { mkdirSync as mkdirSync5, appendFileSync as appendFileSync5, existsSync as existsSync13, writeFileSync as writeFileSync4, unlinkSync as unlinkSync5 } from "fs";
|
|
2878
3019
|
import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
|
|
2879
3020
|
import { randomUUID } from "crypto";
|
|
2880
|
-
var TRANSCRIPT_DIR =
|
|
3021
|
+
var TRANSCRIPT_DIR = join15(homedir4(), ".claude", "transcripts");
|
|
2881
3022
|
function getTranscriptPath(sessionId) {
|
|
2882
|
-
return
|
|
3023
|
+
return join15(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
2883
3024
|
}
|
|
2884
3025
|
function ensureTranscriptDir() {
|
|
2885
|
-
if (!
|
|
2886
|
-
|
|
3026
|
+
if (!existsSync13(TRANSCRIPT_DIR)) {
|
|
3027
|
+
mkdirSync5(TRANSCRIPT_DIR, { recursive: true });
|
|
2887
3028
|
}
|
|
2888
3029
|
}
|
|
2889
3030
|
function appendTranscriptEntry(sessionId, entry) {
|
|
@@ -2969,8 +3110,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
2969
3110
|
}
|
|
2970
3111
|
};
|
|
2971
3112
|
entries.push(JSON.stringify(currentEntry));
|
|
2972
|
-
const tempPath =
|
|
2973
|
-
|
|
3113
|
+
const tempPath = join15(tmpdir2(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
3114
|
+
writeFileSync4(tempPath, entries.join(`
|
|
2974
3115
|
`) + `
|
|
2975
3116
|
`);
|
|
2976
3117
|
return tempPath;
|
|
@@ -2989,8 +3130,8 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
2989
3130
|
]
|
|
2990
3131
|
}
|
|
2991
3132
|
};
|
|
2992
|
-
const tempPath =
|
|
2993
|
-
|
|
3133
|
+
const tempPath = join15(tmpdir2(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
3134
|
+
writeFileSync4(tempPath, JSON.stringify(currentEntry) + `
|
|
2994
3135
|
`);
|
|
2995
3136
|
return tempPath;
|
|
2996
3137
|
} catch {
|
|
@@ -3002,7 +3143,7 @@ function deleteTempTranscript(path3) {
|
|
|
3002
3143
|
if (!path3)
|
|
3003
3144
|
return;
|
|
3004
3145
|
try {
|
|
3005
|
-
|
|
3146
|
+
unlinkSync5(path3);
|
|
3006
3147
|
} catch {}
|
|
3007
3148
|
}
|
|
3008
3149
|
|
|
@@ -3201,11 +3342,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
3201
3342
|
}
|
|
3202
3343
|
|
|
3203
3344
|
// src/hooks/claude-code-hooks/todo.ts
|
|
3204
|
-
import { join as
|
|
3345
|
+
import { join as join16 } from "path";
|
|
3205
3346
|
import { homedir as homedir5 } from "os";
|
|
3206
|
-
var TODO_DIR =
|
|
3347
|
+
var TODO_DIR = join16(homedir5(), ".claude", "todos");
|
|
3207
3348
|
function getTodoPath(sessionId) {
|
|
3208
|
-
return
|
|
3349
|
+
return join16(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
3209
3350
|
}
|
|
3210
3351
|
|
|
3211
3352
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -3297,16 +3438,16 @@ setInterval(() => {
|
|
|
3297
3438
|
}, CACHE_TTL);
|
|
3298
3439
|
|
|
3299
3440
|
// src/features/hook-message-injector/injector.ts
|
|
3300
|
-
import { existsSync as
|
|
3301
|
-
import { join as
|
|
3441
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
3442
|
+
import { join as join18 } from "path";
|
|
3302
3443
|
|
|
3303
3444
|
// src/features/hook-message-injector/constants.ts
|
|
3304
|
-
import { join as
|
|
3445
|
+
import { join as join17 } from "path";
|
|
3305
3446
|
import { homedir as homedir6 } from "os";
|
|
3306
|
-
var xdgData2 = process.env.XDG_DATA_HOME ||
|
|
3307
|
-
var
|
|
3308
|
-
var MESSAGE_STORAGE2 =
|
|
3309
|
-
var PART_STORAGE2 =
|
|
3447
|
+
var xdgData2 = process.env.XDG_DATA_HOME || join17(homedir6(), ".local", "share");
|
|
3448
|
+
var OPENCODE_STORAGE4 = join17(xdgData2, "opencode", "storage");
|
|
3449
|
+
var MESSAGE_STORAGE2 = join17(OPENCODE_STORAGE4, "message");
|
|
3450
|
+
var PART_STORAGE2 = join17(OPENCODE_STORAGE4, "part");
|
|
3310
3451
|
|
|
3311
3452
|
// src/features/hook-message-injector/injector.ts
|
|
3312
3453
|
function findNearestMessageWithFields(messageDir) {
|
|
@@ -3314,7 +3455,7 @@ function findNearestMessageWithFields(messageDir) {
|
|
|
3314
3455
|
const files = readdirSync2(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
|
|
3315
3456
|
for (const file of files) {
|
|
3316
3457
|
try {
|
|
3317
|
-
const content =
|
|
3458
|
+
const content = readFileSync7(join18(messageDir, file), "utf-8");
|
|
3318
3459
|
const msg = JSON.parse(content);
|
|
3319
3460
|
if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
|
|
3320
3461
|
return msg;
|
|
@@ -3339,20 +3480,20 @@ function generatePartId2() {
|
|
|
3339
3480
|
return `prt_${timestamp}${random}`;
|
|
3340
3481
|
}
|
|
3341
3482
|
function getOrCreateMessageDir(sessionID) {
|
|
3342
|
-
if (!
|
|
3343
|
-
|
|
3483
|
+
if (!existsSync14(MESSAGE_STORAGE2)) {
|
|
3484
|
+
mkdirSync6(MESSAGE_STORAGE2, { recursive: true });
|
|
3344
3485
|
}
|
|
3345
|
-
const directPath =
|
|
3346
|
-
if (
|
|
3486
|
+
const directPath = join18(MESSAGE_STORAGE2, sessionID);
|
|
3487
|
+
if (existsSync14(directPath)) {
|
|
3347
3488
|
return directPath;
|
|
3348
3489
|
}
|
|
3349
3490
|
for (const dir of readdirSync2(MESSAGE_STORAGE2)) {
|
|
3350
|
-
const sessionPath =
|
|
3351
|
-
if (
|
|
3491
|
+
const sessionPath = join18(MESSAGE_STORAGE2, dir, sessionID);
|
|
3492
|
+
if (existsSync14(sessionPath)) {
|
|
3352
3493
|
return sessionPath;
|
|
3353
3494
|
}
|
|
3354
3495
|
}
|
|
3355
|
-
|
|
3496
|
+
mkdirSync6(directPath, { recursive: true });
|
|
3356
3497
|
return directPath;
|
|
3357
3498
|
}
|
|
3358
3499
|
function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
@@ -3393,12 +3534,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
|
|
|
3393
3534
|
sessionID
|
|
3394
3535
|
};
|
|
3395
3536
|
try {
|
|
3396
|
-
|
|
3397
|
-
const partDir =
|
|
3398
|
-
if (!
|
|
3399
|
-
|
|
3537
|
+
writeFileSync5(join18(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
|
|
3538
|
+
const partDir = join18(PART_STORAGE2, messageID);
|
|
3539
|
+
if (!existsSync14(partDir)) {
|
|
3540
|
+
mkdirSync6(partDir, { recursive: true });
|
|
3400
3541
|
}
|
|
3401
|
-
|
|
3542
|
+
writeFileSync5(join18(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
|
|
3402
3543
|
return true;
|
|
3403
3544
|
} catch {
|
|
3404
3545
|
return false;
|
|
@@ -3644,14 +3785,14 @@ ${result.message}`;
|
|
|
3644
3785
|
};
|
|
3645
3786
|
}
|
|
3646
3787
|
// src/features/claude-code-command-loader/loader.ts
|
|
3647
|
-
import { existsSync as
|
|
3788
|
+
import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync8 } from "fs";
|
|
3648
3789
|
import { homedir as homedir7 } from "os";
|
|
3649
|
-
import { join as
|
|
3790
|
+
import { join as join19, basename } from "path";
|
|
3650
3791
|
function isMarkdownFile(entry) {
|
|
3651
3792
|
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile();
|
|
3652
3793
|
}
|
|
3653
3794
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
3654
|
-
if (!
|
|
3795
|
+
if (!existsSync15(commandsDir)) {
|
|
3655
3796
|
return [];
|
|
3656
3797
|
}
|
|
3657
3798
|
const entries = readdirSync3(commandsDir, { withFileTypes: true });
|
|
@@ -3659,10 +3800,10 @@ function loadCommandsFromDir(commandsDir, scope) {
|
|
|
3659
3800
|
for (const entry of entries) {
|
|
3660
3801
|
if (!isMarkdownFile(entry))
|
|
3661
3802
|
continue;
|
|
3662
|
-
const commandPath =
|
|
3803
|
+
const commandPath = join19(commandsDir, entry.name);
|
|
3663
3804
|
const commandName = basename(entry.name, ".md");
|
|
3664
3805
|
try {
|
|
3665
|
-
const content =
|
|
3806
|
+
const content = readFileSync8(commandPath, "utf-8");
|
|
3666
3807
|
const { data, body } = parseFrontmatter(content);
|
|
3667
3808
|
const wrappedTemplate = `<command-instruction>
|
|
3668
3809
|
${body.trim()}
|
|
@@ -3701,31 +3842,31 @@ function commandsToRecord(commands) {
|
|
|
3701
3842
|
return result;
|
|
3702
3843
|
}
|
|
3703
3844
|
function loadUserCommands() {
|
|
3704
|
-
const userCommandsDir =
|
|
3845
|
+
const userCommandsDir = join19(homedir7(), ".claude", "commands");
|
|
3705
3846
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
3706
3847
|
return commandsToRecord(commands);
|
|
3707
3848
|
}
|
|
3708
3849
|
function loadProjectCommands() {
|
|
3709
|
-
const projectCommandsDir =
|
|
3850
|
+
const projectCommandsDir = join19(process.cwd(), ".claude", "commands");
|
|
3710
3851
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
3711
3852
|
return commandsToRecord(commands);
|
|
3712
3853
|
}
|
|
3713
3854
|
function loadOpencodeGlobalCommands() {
|
|
3714
|
-
const opencodeCommandsDir =
|
|
3855
|
+
const opencodeCommandsDir = join19(homedir7(), ".config", "opencode", "command");
|
|
3715
3856
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
3716
3857
|
return commandsToRecord(commands);
|
|
3717
3858
|
}
|
|
3718
3859
|
function loadOpencodeProjectCommands() {
|
|
3719
|
-
const opencodeProjectDir =
|
|
3860
|
+
const opencodeProjectDir = join19(process.cwd(), ".opencode", "command");
|
|
3720
3861
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
3721
3862
|
return commandsToRecord(commands);
|
|
3722
3863
|
}
|
|
3723
3864
|
// src/features/claude-code-skill-loader/loader.ts
|
|
3724
|
-
import { existsSync as
|
|
3865
|
+
import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2, readlinkSync } from "fs";
|
|
3725
3866
|
import { homedir as homedir8 } from "os";
|
|
3726
|
-
import { join as
|
|
3867
|
+
import { join as join20, resolve as resolve3 } from "path";
|
|
3727
3868
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
3728
|
-
if (!
|
|
3869
|
+
if (!existsSync16(skillsDir)) {
|
|
3729
3870
|
return [];
|
|
3730
3871
|
}
|
|
3731
3872
|
const entries = readdirSync4(skillsDir, { withFileTypes: true });
|
|
@@ -3733,18 +3874,18 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
3733
3874
|
for (const entry of entries) {
|
|
3734
3875
|
if (entry.name.startsWith("."))
|
|
3735
3876
|
continue;
|
|
3736
|
-
const skillPath =
|
|
3877
|
+
const skillPath = join20(skillsDir, entry.name);
|
|
3737
3878
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
3738
3879
|
continue;
|
|
3739
3880
|
let resolvedPath = skillPath;
|
|
3740
3881
|
if (statSync2(skillPath, { throwIfNoEntry: false })?.isSymbolicLink()) {
|
|
3741
|
-
resolvedPath =
|
|
3882
|
+
resolvedPath = resolve3(skillPath, "..", readlinkSync(skillPath));
|
|
3742
3883
|
}
|
|
3743
|
-
const skillMdPath =
|
|
3744
|
-
if (!
|
|
3884
|
+
const skillMdPath = join20(resolvedPath, "SKILL.md");
|
|
3885
|
+
if (!existsSync16(skillMdPath))
|
|
3745
3886
|
continue;
|
|
3746
3887
|
try {
|
|
3747
|
-
const content =
|
|
3888
|
+
const content = readFileSync9(skillMdPath, "utf-8");
|
|
3748
3889
|
const { data, body } = parseFrontmatter(content);
|
|
3749
3890
|
const skillName = data.name || entry.name;
|
|
3750
3891
|
const originalDescription = data.description || "";
|
|
@@ -3775,7 +3916,7 @@ $ARGUMENTS
|
|
|
3775
3916
|
return skills;
|
|
3776
3917
|
}
|
|
3777
3918
|
function loadUserSkillsAsCommands() {
|
|
3778
|
-
const userSkillsDir =
|
|
3919
|
+
const userSkillsDir = join20(homedir8(), ".claude", "skills");
|
|
3779
3920
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
3780
3921
|
return skills.reduce((acc, skill) => {
|
|
3781
3922
|
acc[skill.name] = skill.definition;
|
|
@@ -3783,7 +3924,7 @@ function loadUserSkillsAsCommands() {
|
|
|
3783
3924
|
}, {});
|
|
3784
3925
|
}
|
|
3785
3926
|
function loadProjectSkillsAsCommands() {
|
|
3786
|
-
const projectSkillsDir =
|
|
3927
|
+
const projectSkillsDir = join20(process.cwd(), ".claude", "skills");
|
|
3787
3928
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
3788
3929
|
return skills.reduce((acc, skill) => {
|
|
3789
3930
|
acc[skill.name] = skill.definition;
|
|
@@ -3791,9 +3932,9 @@ function loadProjectSkillsAsCommands() {
|
|
|
3791
3932
|
}, {});
|
|
3792
3933
|
}
|
|
3793
3934
|
// src/features/claude-code-agent-loader/loader.ts
|
|
3794
|
-
import { existsSync as
|
|
3935
|
+
import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
|
|
3795
3936
|
import { homedir as homedir9 } from "os";
|
|
3796
|
-
import { join as
|
|
3937
|
+
import { join as join21, basename as basename2 } from "path";
|
|
3797
3938
|
function parseToolsConfig(toolsStr) {
|
|
3798
3939
|
if (!toolsStr)
|
|
3799
3940
|
return;
|
|
@@ -3810,7 +3951,7 @@ function isMarkdownFile2(entry) {
|
|
|
3810
3951
|
return !entry.name.startsWith(".") && entry.name.endsWith(".md") && entry.isFile();
|
|
3811
3952
|
}
|
|
3812
3953
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
3813
|
-
if (!
|
|
3954
|
+
if (!existsSync17(agentsDir)) {
|
|
3814
3955
|
return [];
|
|
3815
3956
|
}
|
|
3816
3957
|
const entries = readdirSync5(agentsDir, { withFileTypes: true });
|
|
@@ -3818,10 +3959,10 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
3818
3959
|
for (const entry of entries) {
|
|
3819
3960
|
if (!isMarkdownFile2(entry))
|
|
3820
3961
|
continue;
|
|
3821
|
-
const agentPath =
|
|
3962
|
+
const agentPath = join21(agentsDir, entry.name);
|
|
3822
3963
|
const agentName = basename2(entry.name, ".md");
|
|
3823
3964
|
try {
|
|
3824
|
-
const content =
|
|
3965
|
+
const content = readFileSync10(agentPath, "utf-8");
|
|
3825
3966
|
const { data, body } = parseFrontmatter(content);
|
|
3826
3967
|
const name = data.name || agentName;
|
|
3827
3968
|
const originalDescription = data.description || "";
|
|
@@ -3848,7 +3989,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
3848
3989
|
return agents;
|
|
3849
3990
|
}
|
|
3850
3991
|
function loadUserAgents() {
|
|
3851
|
-
const userAgentsDir =
|
|
3992
|
+
const userAgentsDir = join21(homedir9(), ".claude", "agents");
|
|
3852
3993
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
3853
3994
|
const result = {};
|
|
3854
3995
|
for (const agent of agents) {
|
|
@@ -3857,7 +3998,7 @@ function loadUserAgents() {
|
|
|
3857
3998
|
return result;
|
|
3858
3999
|
}
|
|
3859
4000
|
function loadProjectAgents() {
|
|
3860
|
-
const projectAgentsDir =
|
|
4001
|
+
const projectAgentsDir = join21(process.cwd(), ".claude", "agents");
|
|
3861
4002
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
3862
4003
|
const result = {};
|
|
3863
4004
|
for (const agent of agents) {
|
|
@@ -3866,9 +4007,9 @@ function loadProjectAgents() {
|
|
|
3866
4007
|
return result;
|
|
3867
4008
|
}
|
|
3868
4009
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
3869
|
-
import { existsSync as
|
|
4010
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3870
4011
|
import { homedir as homedir10 } from "os";
|
|
3871
|
-
import { join as
|
|
4012
|
+
import { join as join22 } from "path";
|
|
3872
4013
|
|
|
3873
4014
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
3874
4015
|
function expandEnvVars(value) {
|
|
@@ -3937,13 +4078,13 @@ function getMcpConfigPaths() {
|
|
|
3937
4078
|
const home = homedir10();
|
|
3938
4079
|
const cwd = process.cwd();
|
|
3939
4080
|
return [
|
|
3940
|
-
{ path:
|
|
3941
|
-
{ path:
|
|
3942
|
-
{ path:
|
|
4081
|
+
{ path: join22(home, ".claude", ".mcp.json"), scope: "user" },
|
|
4082
|
+
{ path: join22(cwd, ".mcp.json"), scope: "project" },
|
|
4083
|
+
{ path: join22(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
3943
4084
|
];
|
|
3944
4085
|
}
|
|
3945
4086
|
async function loadMcpConfigFile(filePath) {
|
|
3946
|
-
if (!
|
|
4087
|
+
if (!existsSync18(filePath)) {
|
|
3947
4088
|
return null;
|
|
3948
4089
|
}
|
|
3949
4090
|
try {
|
|
@@ -4229,14 +4370,14 @@ var EXT_TO_LANG = {
|
|
|
4229
4370
|
".tfvars": "terraform"
|
|
4230
4371
|
};
|
|
4231
4372
|
// src/tools/lsp/config.ts
|
|
4232
|
-
import { existsSync as
|
|
4233
|
-
import { join as
|
|
4373
|
+
import { existsSync as existsSync19, readFileSync as readFileSync11 } from "fs";
|
|
4374
|
+
import { join as join23 } from "path";
|
|
4234
4375
|
import { homedir as homedir11 } from "os";
|
|
4235
4376
|
function loadJsonFile(path3) {
|
|
4236
|
-
if (!
|
|
4377
|
+
if (!existsSync19(path3))
|
|
4237
4378
|
return null;
|
|
4238
4379
|
try {
|
|
4239
|
-
return JSON.parse(
|
|
4380
|
+
return JSON.parse(readFileSync11(path3, "utf-8"));
|
|
4240
4381
|
} catch {
|
|
4241
4382
|
return null;
|
|
4242
4383
|
}
|
|
@@ -4244,9 +4385,9 @@ function loadJsonFile(path3) {
|
|
|
4244
4385
|
function getConfigPaths() {
|
|
4245
4386
|
const cwd = process.cwd();
|
|
4246
4387
|
return {
|
|
4247
|
-
project:
|
|
4248
|
-
user:
|
|
4249
|
-
opencode:
|
|
4388
|
+
project: join23(cwd, ".opencode", "oh-my-opencode.json"),
|
|
4389
|
+
user: join23(homedir11(), ".config", "opencode", "oh-my-opencode.json"),
|
|
4390
|
+
opencode: join23(homedir11(), ".config", "opencode", "opencode.json")
|
|
4250
4391
|
};
|
|
4251
4392
|
}
|
|
4252
4393
|
function loadAllConfigs() {
|
|
@@ -4339,7 +4480,7 @@ function isServerInstalled(command) {
|
|
|
4339
4480
|
const pathEnv = process.env.PATH || "";
|
|
4340
4481
|
const paths = pathEnv.split(":");
|
|
4341
4482
|
for (const p of paths) {
|
|
4342
|
-
if (
|
|
4483
|
+
if (existsSync19(join23(p, cmd))) {
|
|
4343
4484
|
return true;
|
|
4344
4485
|
}
|
|
4345
4486
|
}
|
|
@@ -4389,8 +4530,8 @@ function getAllServers() {
|
|
|
4389
4530
|
}
|
|
4390
4531
|
// src/tools/lsp/client.ts
|
|
4391
4532
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
4392
|
-
import { readFileSync as
|
|
4393
|
-
import { extname, resolve as
|
|
4533
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
4534
|
+
import { extname, resolve as resolve4 } from "path";
|
|
4394
4535
|
class LSPServerManager {
|
|
4395
4536
|
static instance;
|
|
4396
4537
|
clients = new Map;
|
|
@@ -4540,7 +4681,7 @@ class LSPClient {
|
|
|
4540
4681
|
}
|
|
4541
4682
|
this.startReading();
|
|
4542
4683
|
this.startStderrReading();
|
|
4543
|
-
await new Promise((
|
|
4684
|
+
await new Promise((resolve5) => setTimeout(resolve5, 100));
|
|
4544
4685
|
if (this.proc.exitCode !== null) {
|
|
4545
4686
|
const stderr = this.stderrBuffer.join(`
|
|
4546
4687
|
`);
|
|
@@ -4677,8 +4818,8 @@ stderr: ${stderr}` : ""));
|
|
|
4677
4818
|
\r
|
|
4678
4819
|
`;
|
|
4679
4820
|
this.proc.stdin.write(header + msg);
|
|
4680
|
-
return new Promise((
|
|
4681
|
-
this.pending.set(id, { resolve:
|
|
4821
|
+
return new Promise((resolve5, reject) => {
|
|
4822
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
4682
4823
|
setTimeout(() => {
|
|
4683
4824
|
if (this.pending.has(id)) {
|
|
4684
4825
|
this.pending.delete(id);
|
|
@@ -4786,10 +4927,10 @@ ${msg}`);
|
|
|
4786
4927
|
await new Promise((r) => setTimeout(r, 300));
|
|
4787
4928
|
}
|
|
4788
4929
|
async openFile(filePath) {
|
|
4789
|
-
const absPath =
|
|
4930
|
+
const absPath = resolve4(filePath);
|
|
4790
4931
|
if (this.openedFiles.has(absPath))
|
|
4791
4932
|
return;
|
|
4792
|
-
const text =
|
|
4933
|
+
const text = readFileSync12(absPath, "utf-8");
|
|
4793
4934
|
const ext = extname(absPath);
|
|
4794
4935
|
const languageId = getLanguageId(ext);
|
|
4795
4936
|
this.notify("textDocument/didOpen", {
|
|
@@ -4804,7 +4945,7 @@ ${msg}`);
|
|
|
4804
4945
|
await new Promise((r) => setTimeout(r, 1000));
|
|
4805
4946
|
}
|
|
4806
4947
|
async hover(filePath, line, character) {
|
|
4807
|
-
const absPath =
|
|
4948
|
+
const absPath = resolve4(filePath);
|
|
4808
4949
|
await this.openFile(absPath);
|
|
4809
4950
|
return this.send("textDocument/hover", {
|
|
4810
4951
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4812,7 +4953,7 @@ ${msg}`);
|
|
|
4812
4953
|
});
|
|
4813
4954
|
}
|
|
4814
4955
|
async definition(filePath, line, character) {
|
|
4815
|
-
const absPath =
|
|
4956
|
+
const absPath = resolve4(filePath);
|
|
4816
4957
|
await this.openFile(absPath);
|
|
4817
4958
|
return this.send("textDocument/definition", {
|
|
4818
4959
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4820,7 +4961,7 @@ ${msg}`);
|
|
|
4820
4961
|
});
|
|
4821
4962
|
}
|
|
4822
4963
|
async references(filePath, line, character, includeDeclaration = true) {
|
|
4823
|
-
const absPath =
|
|
4964
|
+
const absPath = resolve4(filePath);
|
|
4824
4965
|
await this.openFile(absPath);
|
|
4825
4966
|
return this.send("textDocument/references", {
|
|
4826
4967
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4829,7 +4970,7 @@ ${msg}`);
|
|
|
4829
4970
|
});
|
|
4830
4971
|
}
|
|
4831
4972
|
async documentSymbols(filePath) {
|
|
4832
|
-
const absPath =
|
|
4973
|
+
const absPath = resolve4(filePath);
|
|
4833
4974
|
await this.openFile(absPath);
|
|
4834
4975
|
return this.send("textDocument/documentSymbol", {
|
|
4835
4976
|
textDocument: { uri: `file://${absPath}` }
|
|
@@ -4839,7 +4980,7 @@ ${msg}`);
|
|
|
4839
4980
|
return this.send("workspace/symbol", { query });
|
|
4840
4981
|
}
|
|
4841
4982
|
async diagnostics(filePath) {
|
|
4842
|
-
const absPath =
|
|
4983
|
+
const absPath = resolve4(filePath);
|
|
4843
4984
|
const uri = `file://${absPath}`;
|
|
4844
4985
|
await this.openFile(absPath);
|
|
4845
4986
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -4854,7 +4995,7 @@ ${msg}`);
|
|
|
4854
4995
|
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
4855
4996
|
}
|
|
4856
4997
|
async prepareRename(filePath, line, character) {
|
|
4857
|
-
const absPath =
|
|
4998
|
+
const absPath = resolve4(filePath);
|
|
4858
4999
|
await this.openFile(absPath);
|
|
4859
5000
|
return this.send("textDocument/prepareRename", {
|
|
4860
5001
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4862,7 +5003,7 @@ ${msg}`);
|
|
|
4862
5003
|
});
|
|
4863
5004
|
}
|
|
4864
5005
|
async rename(filePath, line, character, newName) {
|
|
4865
|
-
const absPath =
|
|
5006
|
+
const absPath = resolve4(filePath);
|
|
4866
5007
|
await this.openFile(absPath);
|
|
4867
5008
|
return this.send("textDocument/rename", {
|
|
4868
5009
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4871,7 +5012,7 @@ ${msg}`);
|
|
|
4871
5012
|
});
|
|
4872
5013
|
}
|
|
4873
5014
|
async codeAction(filePath, startLine, startChar, endLine, endChar, only) {
|
|
4874
|
-
const absPath =
|
|
5015
|
+
const absPath = resolve4(filePath);
|
|
4875
5016
|
await this.openFile(absPath);
|
|
4876
5017
|
return this.send("textDocument/codeAction", {
|
|
4877
5018
|
textDocument: { uri: `file://${absPath}` },
|
|
@@ -4903,26 +5044,26 @@ ${msg}`);
|
|
|
4903
5044
|
}
|
|
4904
5045
|
}
|
|
4905
5046
|
// src/tools/lsp/utils.ts
|
|
4906
|
-
import { extname as extname2, resolve as
|
|
4907
|
-
import { existsSync as
|
|
5047
|
+
import { extname as extname2, resolve as resolve5 } from "path";
|
|
5048
|
+
import { existsSync as existsSync20, readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "fs";
|
|
4908
5049
|
function findWorkspaceRoot(filePath) {
|
|
4909
|
-
let dir =
|
|
4910
|
-
if (!
|
|
5050
|
+
let dir = resolve5(filePath);
|
|
5051
|
+
if (!existsSync20(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
4911
5052
|
dir = __require("path").dirname(dir);
|
|
4912
5053
|
}
|
|
4913
5054
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
4914
5055
|
while (dir !== "/") {
|
|
4915
5056
|
for (const marker of markers) {
|
|
4916
|
-
if (
|
|
5057
|
+
if (existsSync20(__require("path").join(dir, marker))) {
|
|
4917
5058
|
return dir;
|
|
4918
5059
|
}
|
|
4919
5060
|
}
|
|
4920
5061
|
dir = __require("path").dirname(dir);
|
|
4921
5062
|
}
|
|
4922
|
-
return __require("path").dirname(
|
|
5063
|
+
return __require("path").dirname(resolve5(filePath));
|
|
4923
5064
|
}
|
|
4924
5065
|
async function withLspClient(filePath, fn) {
|
|
4925
|
-
const absPath =
|
|
5066
|
+
const absPath = resolve5(filePath);
|
|
4926
5067
|
const ext = extname2(absPath);
|
|
4927
5068
|
const server = findServerForExtension(ext);
|
|
4928
5069
|
if (!server) {
|
|
@@ -5071,7 +5212,7 @@ function formatCodeActions(actions) {
|
|
|
5071
5212
|
}
|
|
5072
5213
|
function applyTextEditsToFile(filePath, edits) {
|
|
5073
5214
|
try {
|
|
5074
|
-
let content =
|
|
5215
|
+
let content = readFileSync13(filePath, "utf-8");
|
|
5075
5216
|
const lines = content.split(`
|
|
5076
5217
|
`);
|
|
5077
5218
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -5096,7 +5237,7 @@ function applyTextEditsToFile(filePath, edits) {
|
|
|
5096
5237
|
`));
|
|
5097
5238
|
}
|
|
5098
5239
|
}
|
|
5099
|
-
|
|
5240
|
+
writeFileSync6(filePath, lines.join(`
|
|
5100
5241
|
`), "utf-8");
|
|
5101
5242
|
return { success: true, editCount: edits.length };
|
|
5102
5243
|
} catch (err) {
|
|
@@ -5127,7 +5268,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
5127
5268
|
if (change.kind === "create") {
|
|
5128
5269
|
try {
|
|
5129
5270
|
const filePath = change.uri.replace("file://", "");
|
|
5130
|
-
|
|
5271
|
+
writeFileSync6(filePath, "", "utf-8");
|
|
5131
5272
|
result.filesModified.push(filePath);
|
|
5132
5273
|
} catch (err) {
|
|
5133
5274
|
result.success = false;
|
|
@@ -5137,8 +5278,8 @@ function applyWorkspaceEdit(edit) {
|
|
|
5137
5278
|
try {
|
|
5138
5279
|
const oldPath = change.oldUri.replace("file://", "");
|
|
5139
5280
|
const newPath = change.newUri.replace("file://", "");
|
|
5140
|
-
const content =
|
|
5141
|
-
|
|
5281
|
+
const content = readFileSync13(oldPath, "utf-8");
|
|
5282
|
+
writeFileSync6(newPath, content, "utf-8");
|
|
5142
5283
|
__require("fs").unlinkSync(oldPath);
|
|
5143
5284
|
result.filesModified.push(newPath);
|
|
5144
5285
|
} catch (err) {
|
|
@@ -17838,13 +17979,13 @@ var lsp_code_action_resolve = tool({
|
|
|
17838
17979
|
});
|
|
17839
17980
|
// src/tools/ast-grep/constants.ts
|
|
17840
17981
|
import { createRequire as createRequire4 } from "module";
|
|
17841
|
-
import { dirname as
|
|
17842
|
-
import { existsSync as
|
|
17982
|
+
import { dirname as dirname4, join as join25 } from "path";
|
|
17983
|
+
import { existsSync as existsSync22, statSync as statSync3 } from "fs";
|
|
17843
17984
|
|
|
17844
17985
|
// src/tools/ast-grep/downloader.ts
|
|
17845
17986
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
17846
|
-
import { existsSync as
|
|
17847
|
-
import { join as
|
|
17987
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync7, chmodSync as chmodSync2, unlinkSync as unlinkSync6 } from "fs";
|
|
17988
|
+
import { join as join24 } from "path";
|
|
17848
17989
|
import { homedir as homedir12 } from "os";
|
|
17849
17990
|
import { createRequire as createRequire3 } from "module";
|
|
17850
17991
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -17870,19 +18011,19 @@ var PLATFORM_MAP2 = {
|
|
|
17870
18011
|
function getCacheDir2() {
|
|
17871
18012
|
if (process.platform === "win32") {
|
|
17872
18013
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
17873
|
-
const base2 = localAppData ||
|
|
17874
|
-
return
|
|
18014
|
+
const base2 = localAppData || join24(homedir12(), "AppData", "Local");
|
|
18015
|
+
return join24(base2, "oh-my-opencode", "bin");
|
|
17875
18016
|
}
|
|
17876
18017
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
17877
|
-
const base = xdgCache2 ||
|
|
17878
|
-
return
|
|
18018
|
+
const base = xdgCache2 || join24(homedir12(), ".cache");
|
|
18019
|
+
return join24(base, "oh-my-opencode", "bin");
|
|
17879
18020
|
}
|
|
17880
18021
|
function getBinaryName3() {
|
|
17881
18022
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
17882
18023
|
}
|
|
17883
18024
|
function getCachedBinaryPath2() {
|
|
17884
|
-
const binaryPath =
|
|
17885
|
-
return
|
|
18025
|
+
const binaryPath = join24(getCacheDir2(), getBinaryName3());
|
|
18026
|
+
return existsSync21(binaryPath) ? binaryPath : null;
|
|
17886
18027
|
}
|
|
17887
18028
|
async function extractZip2(archivePath, destDir) {
|
|
17888
18029
|
const proc = process.platform === "win32" ? spawn5([
|
|
@@ -17908,8 +18049,8 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
17908
18049
|
}
|
|
17909
18050
|
const cacheDir = getCacheDir2();
|
|
17910
18051
|
const binaryName = getBinaryName3();
|
|
17911
|
-
const binaryPath =
|
|
17912
|
-
if (
|
|
18052
|
+
const binaryPath = join24(cacheDir, binaryName);
|
|
18053
|
+
if (existsSync21(binaryPath)) {
|
|
17913
18054
|
return binaryPath;
|
|
17914
18055
|
}
|
|
17915
18056
|
const { arch, os: os3 } = platformInfo;
|
|
@@ -17917,21 +18058,21 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
17917
18058
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
17918
18059
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
17919
18060
|
try {
|
|
17920
|
-
if (!
|
|
17921
|
-
|
|
18061
|
+
if (!existsSync21(cacheDir)) {
|
|
18062
|
+
mkdirSync7(cacheDir, { recursive: true });
|
|
17922
18063
|
}
|
|
17923
18064
|
const response = await fetch(downloadUrl, { redirect: "follow" });
|
|
17924
18065
|
if (!response.ok) {
|
|
17925
18066
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
17926
18067
|
}
|
|
17927
|
-
const archivePath =
|
|
18068
|
+
const archivePath = join24(cacheDir, assetName);
|
|
17928
18069
|
const arrayBuffer = await response.arrayBuffer();
|
|
17929
18070
|
await Bun.write(archivePath, arrayBuffer);
|
|
17930
18071
|
await extractZip2(archivePath, cacheDir);
|
|
17931
|
-
if (
|
|
17932
|
-
|
|
18072
|
+
if (existsSync21(archivePath)) {
|
|
18073
|
+
unlinkSync6(archivePath);
|
|
17933
18074
|
}
|
|
17934
|
-
if (process.platform !== "win32" &&
|
|
18075
|
+
if (process.platform !== "win32" && existsSync21(binaryPath)) {
|
|
17935
18076
|
chmodSync2(binaryPath, 493);
|
|
17936
18077
|
}
|
|
17937
18078
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -17981,9 +18122,9 @@ function findSgCliPathSync() {
|
|
|
17981
18122
|
try {
|
|
17982
18123
|
const require2 = createRequire4(import.meta.url);
|
|
17983
18124
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
17984
|
-
const cliDir =
|
|
17985
|
-
const sgPath =
|
|
17986
|
-
if (
|
|
18125
|
+
const cliDir = dirname4(cliPkgPath);
|
|
18126
|
+
const sgPath = join25(cliDir, binaryName);
|
|
18127
|
+
if (existsSync22(sgPath) && isValidBinary(sgPath)) {
|
|
17987
18128
|
return sgPath;
|
|
17988
18129
|
}
|
|
17989
18130
|
} catch {}
|
|
@@ -17992,10 +18133,10 @@ function findSgCliPathSync() {
|
|
|
17992
18133
|
try {
|
|
17993
18134
|
const require2 = createRequire4(import.meta.url);
|
|
17994
18135
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
17995
|
-
const pkgDir =
|
|
18136
|
+
const pkgDir = dirname4(pkgPath);
|
|
17996
18137
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
17997
|
-
const binaryPath =
|
|
17998
|
-
if (
|
|
18138
|
+
const binaryPath = join25(pkgDir, astGrepName);
|
|
18139
|
+
if (existsSync22(binaryPath) && isValidBinary(binaryPath)) {
|
|
17999
18140
|
return binaryPath;
|
|
18000
18141
|
}
|
|
18001
18142
|
} catch {}
|
|
@@ -18003,7 +18144,7 @@ function findSgCliPathSync() {
|
|
|
18003
18144
|
if (process.platform === "darwin") {
|
|
18004
18145
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
18005
18146
|
for (const path3 of homebrewPaths) {
|
|
18006
|
-
if (
|
|
18147
|
+
if (existsSync22(path3) && isValidBinary(path3)) {
|
|
18007
18148
|
return path3;
|
|
18008
18149
|
}
|
|
18009
18150
|
}
|
|
@@ -18059,11 +18200,11 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
18059
18200
|
|
|
18060
18201
|
// src/tools/ast-grep/cli.ts
|
|
18061
18202
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
18062
|
-
import { existsSync as
|
|
18203
|
+
import { existsSync as existsSync23 } from "fs";
|
|
18063
18204
|
var resolvedCliPath3 = null;
|
|
18064
18205
|
var initPromise2 = null;
|
|
18065
18206
|
async function getAstGrepPath() {
|
|
18066
|
-
if (resolvedCliPath3 !== null &&
|
|
18207
|
+
if (resolvedCliPath3 !== null && existsSync23(resolvedCliPath3)) {
|
|
18067
18208
|
return resolvedCliPath3;
|
|
18068
18209
|
}
|
|
18069
18210
|
if (initPromise2) {
|
|
@@ -18071,7 +18212,7 @@ async function getAstGrepPath() {
|
|
|
18071
18212
|
}
|
|
18072
18213
|
initPromise2 = (async () => {
|
|
18073
18214
|
const syncPath = findSgCliPathSync();
|
|
18074
|
-
if (syncPath &&
|
|
18215
|
+
if (syncPath && existsSync23(syncPath)) {
|
|
18075
18216
|
resolvedCliPath3 = syncPath;
|
|
18076
18217
|
setSgCliPath(syncPath);
|
|
18077
18218
|
return syncPath;
|
|
@@ -18105,7 +18246,7 @@ async function runSg(options) {
|
|
|
18105
18246
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
18106
18247
|
args.push(...paths);
|
|
18107
18248
|
let cliPath = getSgCliPath();
|
|
18108
|
-
if (!
|
|
18249
|
+
if (!existsSync23(cliPath) && cliPath !== "sg") {
|
|
18109
18250
|
const downloadedPath = await getAstGrepPath();
|
|
18110
18251
|
if (downloadedPath) {
|
|
18111
18252
|
cliPath = downloadedPath;
|
|
@@ -18369,8 +18510,8 @@ var ast_grep_replace = tool({
|
|
|
18369
18510
|
var {spawn: spawn7 } = globalThis.Bun;
|
|
18370
18511
|
|
|
18371
18512
|
// src/tools/grep/constants.ts
|
|
18372
|
-
import { existsSync as
|
|
18373
|
-
import { join as
|
|
18513
|
+
import { existsSync as existsSync24 } from "fs";
|
|
18514
|
+
import { join as join26, dirname as dirname5 } from "path";
|
|
18374
18515
|
import { spawnSync } from "child_process";
|
|
18375
18516
|
var cachedCli = null;
|
|
18376
18517
|
function findExecutable(name) {
|
|
@@ -18387,17 +18528,17 @@ function findExecutable(name) {
|
|
|
18387
18528
|
}
|
|
18388
18529
|
function getOpenCodeBundledRg() {
|
|
18389
18530
|
const execPath = process.execPath;
|
|
18390
|
-
const execDir =
|
|
18531
|
+
const execDir = dirname5(execPath);
|
|
18391
18532
|
const isWindows = process.platform === "win32";
|
|
18392
18533
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
18393
18534
|
const candidates = [
|
|
18394
|
-
|
|
18395
|
-
|
|
18396
|
-
|
|
18397
|
-
|
|
18535
|
+
join26(execDir, rgName),
|
|
18536
|
+
join26(execDir, "bin", rgName),
|
|
18537
|
+
join26(execDir, "..", "bin", rgName),
|
|
18538
|
+
join26(execDir, "..", "libexec", rgName)
|
|
18398
18539
|
];
|
|
18399
18540
|
for (const candidate of candidates) {
|
|
18400
|
-
if (
|
|
18541
|
+
if (existsSync24(candidate)) {
|
|
18401
18542
|
return candidate;
|
|
18402
18543
|
}
|
|
18403
18544
|
}
|
|
@@ -18795,11 +18936,11 @@ var glob = tool({
|
|
|
18795
18936
|
}
|
|
18796
18937
|
});
|
|
18797
18938
|
// src/tools/slashcommand/tools.ts
|
|
18798
|
-
import { existsSync as
|
|
18939
|
+
import { existsSync as existsSync25, readdirSync as readdirSync6, readFileSync as readFileSync14 } from "fs";
|
|
18799
18940
|
import { homedir as homedir13 } from "os";
|
|
18800
|
-
import { join as
|
|
18941
|
+
import { join as join27, basename as basename3, dirname as dirname6 } from "path";
|
|
18801
18942
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
18802
|
-
if (!
|
|
18943
|
+
if (!existsSync25(commandsDir)) {
|
|
18803
18944
|
return [];
|
|
18804
18945
|
}
|
|
18805
18946
|
const entries = readdirSync6(commandsDir, { withFileTypes: true });
|
|
@@ -18811,10 +18952,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
18811
18952
|
continue;
|
|
18812
18953
|
if (!entry.isFile())
|
|
18813
18954
|
continue;
|
|
18814
|
-
const commandPath =
|
|
18955
|
+
const commandPath = join27(commandsDir, entry.name);
|
|
18815
18956
|
const commandName = basename3(entry.name, ".md");
|
|
18816
18957
|
try {
|
|
18817
|
-
const content =
|
|
18958
|
+
const content = readFileSync14(commandPath, "utf-8");
|
|
18818
18959
|
const { data, body } = parseFrontmatter(content);
|
|
18819
18960
|
const metadata = {
|
|
18820
18961
|
name: commandName,
|
|
@@ -18838,10 +18979,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
18838
18979
|
return commands;
|
|
18839
18980
|
}
|
|
18840
18981
|
function discoverCommandsSync() {
|
|
18841
|
-
const userCommandsDir =
|
|
18842
|
-
const projectCommandsDir =
|
|
18843
|
-
const opencodeGlobalDir =
|
|
18844
|
-
const opencodeProjectDir =
|
|
18982
|
+
const userCommandsDir = join27(homedir13(), ".claude", "commands");
|
|
18983
|
+
const projectCommandsDir = join27(process.cwd(), ".claude", "commands");
|
|
18984
|
+
const opencodeGlobalDir = join27(homedir13(), ".config", "opencode", "command");
|
|
18985
|
+
const opencodeProjectDir = join27(process.cwd(), ".opencode", "command");
|
|
18845
18986
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
18846
18987
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
18847
18988
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -18884,7 +19025,7 @@ async function formatLoadedCommand(cmd) {
|
|
|
18884
19025
|
`);
|
|
18885
19026
|
sections.push(`## Command Instructions
|
|
18886
19027
|
`);
|
|
18887
|
-
const commandDir =
|
|
19028
|
+
const commandDir = dirname6(cmd.path);
|
|
18888
19029
|
const withFileRefs = await resolveFileReferencesInText(cmd.content, commandDir);
|
|
18889
19030
|
const resolvedContent = await resolveCommandsInText(withFileRefs);
|
|
18890
19031
|
sections.push(resolvedContent.trim());
|
|
@@ -18964,12 +19105,29 @@ Provide a command name to execute.`;
|
|
|
18964
19105
|
Try a different command name.`;
|
|
18965
19106
|
}
|
|
18966
19107
|
});
|
|
19108
|
+
// src/tools/skill/types.ts
|
|
19109
|
+
var SkillFrontmatterSchema = exports_external.object({
|
|
19110
|
+
name: exports_external.string().regex(/^[a-z0-9-]+$/, "Name must be lowercase alphanumeric with hyphens only").min(1, "Name cannot be empty"),
|
|
19111
|
+
description: exports_external.string().min(20, "Description must be at least 20 characters for discoverability"),
|
|
19112
|
+
license: exports_external.string().optional(),
|
|
19113
|
+
"allowed-tools": exports_external.array(exports_external.string()).optional(),
|
|
19114
|
+
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
19115
|
+
});
|
|
18967
19116
|
// src/tools/skill/tools.ts
|
|
18968
|
-
import { existsSync as
|
|
19117
|
+
import { existsSync as existsSync26, readdirSync as readdirSync7, statSync as statSync4, readlinkSync as readlinkSync2, readFileSync as readFileSync15 } from "fs";
|
|
18969
19118
|
import { homedir as homedir14 } from "os";
|
|
18970
|
-
import { join as
|
|
19119
|
+
import { join as join28, resolve as resolve6, basename as basename4 } from "path";
|
|
19120
|
+
function parseSkillFrontmatter(data) {
|
|
19121
|
+
return {
|
|
19122
|
+
name: typeof data.name === "string" ? data.name : "",
|
|
19123
|
+
description: typeof data.description === "string" ? data.description : "",
|
|
19124
|
+
license: typeof data.license === "string" ? data.license : undefined,
|
|
19125
|
+
"allowed-tools": Array.isArray(data["allowed-tools"]) ? data["allowed-tools"] : undefined,
|
|
19126
|
+
metadata: typeof data.metadata === "object" && data.metadata !== null ? data.metadata : undefined
|
|
19127
|
+
};
|
|
19128
|
+
}
|
|
18971
19129
|
function discoverSkillsFromDir(skillsDir, scope) {
|
|
18972
|
-
if (!
|
|
19130
|
+
if (!existsSync26(skillsDir)) {
|
|
18973
19131
|
return [];
|
|
18974
19132
|
}
|
|
18975
19133
|
const entries = readdirSync7(skillsDir, { withFileTypes: true });
|
|
@@ -18977,22 +19135,22 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
18977
19135
|
for (const entry of entries) {
|
|
18978
19136
|
if (entry.name.startsWith("."))
|
|
18979
19137
|
continue;
|
|
18980
|
-
const skillPath =
|
|
19138
|
+
const skillPath = join28(skillsDir, entry.name);
|
|
18981
19139
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
18982
19140
|
let resolvedPath = skillPath;
|
|
18983
19141
|
try {
|
|
18984
19142
|
const stats = statSync4(skillPath, { throwIfNoEntry: false });
|
|
18985
19143
|
if (stats?.isSymbolicLink()) {
|
|
18986
|
-
resolvedPath =
|
|
19144
|
+
resolvedPath = resolve6(skillPath, "..", readlinkSync2(skillPath));
|
|
18987
19145
|
}
|
|
18988
19146
|
} catch {
|
|
18989
19147
|
continue;
|
|
18990
19148
|
}
|
|
18991
|
-
const skillMdPath =
|
|
18992
|
-
if (!
|
|
19149
|
+
const skillMdPath = join28(resolvedPath, "SKILL.md");
|
|
19150
|
+
if (!existsSync26(skillMdPath))
|
|
18993
19151
|
continue;
|
|
18994
19152
|
try {
|
|
18995
|
-
const content =
|
|
19153
|
+
const content = readFileSync15(skillMdPath, "utf-8");
|
|
18996
19154
|
const { data } = parseFrontmatter(content);
|
|
18997
19155
|
skills.push({
|
|
18998
19156
|
name: data.name || entry.name,
|
|
@@ -19007,8 +19165,8 @@ function discoverSkillsFromDir(skillsDir, scope) {
|
|
|
19007
19165
|
return skills;
|
|
19008
19166
|
}
|
|
19009
19167
|
function discoverSkillsSync() {
|
|
19010
|
-
const userSkillsDir =
|
|
19011
|
-
const projectSkillsDir =
|
|
19168
|
+
const userSkillsDir = join28(homedir14(), ".claude", "skills");
|
|
19169
|
+
const projectSkillsDir = join28(process.cwd(), ".claude", "skills");
|
|
19012
19170
|
const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
|
|
19013
19171
|
const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
|
|
19014
19172
|
return [...projectSkills, ...userSkills];
|
|
@@ -19020,7 +19178,7 @@ function resolveSymlink(skillPath) {
|
|
|
19020
19178
|
try {
|
|
19021
19179
|
const stats = statSync4(skillPath, { throwIfNoEntry: false });
|
|
19022
19180
|
if (stats?.isSymbolicLink()) {
|
|
19023
|
-
return
|
|
19181
|
+
return resolve6(skillPath, "..", readlinkSync2(skillPath));
|
|
19024
19182
|
}
|
|
19025
19183
|
return skillPath;
|
|
19026
19184
|
} catch {
|
|
@@ -19029,28 +19187,32 @@ function resolveSymlink(skillPath) {
|
|
|
19029
19187
|
}
|
|
19030
19188
|
async function parseSkillMd(skillPath) {
|
|
19031
19189
|
const resolvedPath = resolveSymlink(skillPath);
|
|
19032
|
-
const skillMdPath =
|
|
19033
|
-
if (!
|
|
19190
|
+
const skillMdPath = join28(resolvedPath, "SKILL.md");
|
|
19191
|
+
if (!existsSync26(skillMdPath)) {
|
|
19034
19192
|
return null;
|
|
19035
19193
|
}
|
|
19036
19194
|
try {
|
|
19037
|
-
let content =
|
|
19195
|
+
let content = readFileSync15(skillMdPath, "utf-8");
|
|
19038
19196
|
content = await resolveCommandsInText(content);
|
|
19039
19197
|
const { data, body } = parseFrontmatter(content);
|
|
19198
|
+
const frontmatter2 = parseSkillFrontmatter(data);
|
|
19040
19199
|
const metadata = {
|
|
19041
|
-
name:
|
|
19042
|
-
description:
|
|
19043
|
-
license:
|
|
19200
|
+
name: frontmatter2.name || basename4(skillPath),
|
|
19201
|
+
description: frontmatter2.description,
|
|
19202
|
+
license: frontmatter2.license,
|
|
19203
|
+
allowedTools: frontmatter2["allowed-tools"],
|
|
19204
|
+
metadata: frontmatter2.metadata
|
|
19044
19205
|
};
|
|
19045
|
-
const referencesDir =
|
|
19046
|
-
const scriptsDir =
|
|
19047
|
-
const assetsDir =
|
|
19048
|
-
const references =
|
|
19049
|
-
const scripts =
|
|
19050
|
-
const assets =
|
|
19206
|
+
const referencesDir = join28(resolvedPath, "references");
|
|
19207
|
+
const scriptsDir = join28(resolvedPath, "scripts");
|
|
19208
|
+
const assetsDir = join28(resolvedPath, "assets");
|
|
19209
|
+
const references = existsSync26(referencesDir) ? readdirSync7(referencesDir).filter((f) => !f.startsWith(".")) : [];
|
|
19210
|
+
const scripts = existsSync26(scriptsDir) ? readdirSync7(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
|
|
19211
|
+
const assets = existsSync26(assetsDir) ? readdirSync7(assetsDir).filter((f) => !f.startsWith(".")) : [];
|
|
19051
19212
|
return {
|
|
19052
19213
|
name: metadata.name,
|
|
19053
19214
|
path: resolvedPath,
|
|
19215
|
+
basePath: resolvedPath,
|
|
19054
19216
|
metadata,
|
|
19055
19217
|
content: body,
|
|
19056
19218
|
references,
|
|
@@ -19062,7 +19224,7 @@ async function parseSkillMd(skillPath) {
|
|
|
19062
19224
|
}
|
|
19063
19225
|
}
|
|
19064
19226
|
async function discoverSkillsFromDirAsync(skillsDir) {
|
|
19065
|
-
if (!
|
|
19227
|
+
if (!existsSync26(skillsDir)) {
|
|
19066
19228
|
return [];
|
|
19067
19229
|
}
|
|
19068
19230
|
const entries = readdirSync7(skillsDir, { withFileTypes: true });
|
|
@@ -19070,7 +19232,7 @@ async function discoverSkillsFromDirAsync(skillsDir) {
|
|
|
19070
19232
|
for (const entry of entries) {
|
|
19071
19233
|
if (entry.name.startsWith("."))
|
|
19072
19234
|
continue;
|
|
19073
|
-
const skillPath =
|
|
19235
|
+
const skillPath = join28(skillsDir, entry.name);
|
|
19074
19236
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
19075
19237
|
const skillInfo = await parseSkillMd(skillPath);
|
|
19076
19238
|
if (skillInfo) {
|
|
@@ -19081,8 +19243,8 @@ async function discoverSkillsFromDirAsync(skillsDir) {
|
|
|
19081
19243
|
return skills;
|
|
19082
19244
|
}
|
|
19083
19245
|
async function discoverSkills() {
|
|
19084
|
-
const userSkillsDir =
|
|
19085
|
-
const projectSkillsDir =
|
|
19246
|
+
const userSkillsDir = join28(homedir14(), ".claude", "skills");
|
|
19247
|
+
const projectSkillsDir = join28(process.cwd(), ".claude", "skills");
|
|
19086
19248
|
const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
|
|
19087
19249
|
const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
|
|
19088
19250
|
return [...projectSkills, ...userSkills];
|
|
@@ -19111,9 +19273,9 @@ async function loadSkillWithReferences(skill, includeRefs) {
|
|
|
19111
19273
|
const referencesLoaded = [];
|
|
19112
19274
|
if (includeRefs && skill.references.length > 0) {
|
|
19113
19275
|
for (const ref of skill.references) {
|
|
19114
|
-
const refPath =
|
|
19276
|
+
const refPath = join28(skill.path, "references", ref);
|
|
19115
19277
|
try {
|
|
19116
|
-
let content =
|
|
19278
|
+
let content = readFileSync15(refPath, "utf-8");
|
|
19117
19279
|
content = await resolveCommandsInText(content);
|
|
19118
19280
|
referencesLoaded.push({ path: ref, content });
|
|
19119
19281
|
} catch {}
|
|
@@ -19122,6 +19284,7 @@ async function loadSkillWithReferences(skill, includeRefs) {
|
|
|
19122
19284
|
return {
|
|
19123
19285
|
name: skill.name,
|
|
19124
19286
|
metadata: skill.metadata,
|
|
19287
|
+
basePath: skill.basePath,
|
|
19125
19288
|
body: skill.content,
|
|
19126
19289
|
referencesLoaded
|
|
19127
19290
|
};
|
|
@@ -19144,62 +19307,34 @@ function formatLoadedSkills(loadedSkills) {
|
|
|
19144
19307
|
if (loadedSkills.length === 0) {
|
|
19145
19308
|
return "No skills loaded.";
|
|
19146
19309
|
}
|
|
19147
|
-
const
|
|
19148
|
-
|
|
19149
|
-
for
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
19153
|
-
`
|
|
19154
|
-
|
|
19155
|
-
`);
|
|
19156
|
-
sections.push(skill.body.trim());
|
|
19157
|
-
if (skill.referencesLoaded.length > 0) {
|
|
19158
|
-
sections.push(`
|
|
19310
|
+
const skill = loadedSkills[0];
|
|
19311
|
+
const sections = [];
|
|
19312
|
+
sections.push(`Base directory for this skill: ${skill.basePath}/`);
|
|
19313
|
+
sections.push("");
|
|
19314
|
+
sections.push(skill.body.trim());
|
|
19315
|
+
if (skill.referencesLoaded.length > 0) {
|
|
19316
|
+
sections.push(`
|
|
19317
|
+
---
|
|
19159
19318
|
### Loaded References
|
|
19160
19319
|
`);
|
|
19161
|
-
|
|
19162
|
-
|
|
19320
|
+
for (const ref of skill.referencesLoaded) {
|
|
19321
|
+
sections.push(`#### ${ref.path}
|
|
19163
19322
|
`);
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
}
|
|
19323
|
+
sections.push("```");
|
|
19324
|
+
sections.push(ref.content.trim());
|
|
19325
|
+
sections.push("```\n");
|
|
19168
19326
|
}
|
|
19169
|
-
sections.push(`
|
|
19170
|
-
---
|
|
19171
|
-
`);
|
|
19172
19327
|
}
|
|
19173
|
-
const skillNames = loadedSkills.map((s) => s.metadata.name).join(", ");
|
|
19174
|
-
sections.push(`**Skills loaded**: ${skillNames}`);
|
|
19175
|
-
sections.push(`**Total**: ${loadedSkills.length} skill(s)`);
|
|
19176
19328
|
sections.push(`
|
|
19177
|
-
|
|
19329
|
+
---
|
|
19330
|
+
**Launched skill**: ${skill.metadata.name}`);
|
|
19178
19331
|
return sections.join(`
|
|
19179
19332
|
`);
|
|
19180
19333
|
}
|
|
19181
19334
|
var skill = tool({
|
|
19182
19335
|
description: `Execute a skill within the main conversation.
|
|
19183
19336
|
|
|
19184
|
-
When
|
|
19185
|
-
|
|
19186
|
-
How to use skills:
|
|
19187
|
-
- Invoke skills using this tool with the skill name only (no arguments)
|
|
19188
|
-
- When you invoke a skill, the skill's prompt will expand and provide detailed instructions on how to complete the task
|
|
19189
|
-
|
|
19190
|
-
Important:
|
|
19191
|
-
- Only use skills listed in Available Skills below
|
|
19192
|
-
- Do not invoke a skill that is already running
|
|
19193
|
-
|
|
19194
|
-
Skills are loaded from:
|
|
19195
|
-
- ~/.claude/skills/ (user scope - global skills)
|
|
19196
|
-
- ./.claude/skills/ (project scope - project-specific skills)
|
|
19197
|
-
|
|
19198
|
-
Each skill contains:
|
|
19199
|
-
- SKILL.md: Main instructions with YAML frontmatter (name, description)
|
|
19200
|
-
- references/: Documentation files loaded into context as needed
|
|
19201
|
-
- scripts/: Executable code for deterministic operations
|
|
19202
|
-
- assets/: Files used in output (templates, icons, etc.)
|
|
19337
|
+
When you invoke a skill, the skill's prompt will expand and provide detailed instructions on how to complete the task.
|
|
19203
19338
|
|
|
19204
19339
|
Available Skills:
|
|
19205
19340
|
${skillListForDescription}`,
|
|
@@ -19324,26 +19459,62 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
19324
19459
|
// src/index.ts
|
|
19325
19460
|
import * as fs4 from "fs";
|
|
19326
19461
|
import * as path3 from "path";
|
|
19462
|
+
import * as os3 from "os";
|
|
19463
|
+
function loadConfigFromPath2(configPath) {
|
|
19464
|
+
try {
|
|
19465
|
+
if (fs4.existsSync(configPath)) {
|
|
19466
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
19467
|
+
const rawConfig = JSON.parse(content);
|
|
19468
|
+
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
|
|
19469
|
+
if (!result.success) {
|
|
19470
|
+
log(`Config validation error in ${configPath}:`, result.error.issues);
|
|
19471
|
+
return null;
|
|
19472
|
+
}
|
|
19473
|
+
log(`Config loaded from ${configPath}`, { agents: result.data.agents });
|
|
19474
|
+
return result.data;
|
|
19475
|
+
}
|
|
19476
|
+
} catch (err) {
|
|
19477
|
+
log(`Error loading config from ${configPath}:`, err);
|
|
19478
|
+
}
|
|
19479
|
+
return null;
|
|
19480
|
+
}
|
|
19481
|
+
function mergeConfigs(base, override) {
|
|
19482
|
+
return {
|
|
19483
|
+
...base,
|
|
19484
|
+
...override,
|
|
19485
|
+
agents: override.agents !== undefined ? { ...base.agents ?? {}, ...override.agents } : base.agents,
|
|
19486
|
+
disabled_agents: [
|
|
19487
|
+
...new Set([...base.disabled_agents ?? [], ...override.disabled_agents ?? []])
|
|
19488
|
+
],
|
|
19489
|
+
disabled_mcps: [
|
|
19490
|
+
...new Set([...base.disabled_mcps ?? [], ...override.disabled_mcps ?? []])
|
|
19491
|
+
]
|
|
19492
|
+
};
|
|
19493
|
+
}
|
|
19327
19494
|
function loadPluginConfig(directory) {
|
|
19328
|
-
const
|
|
19329
|
-
path3.join(
|
|
19330
|
-
path3.join(directory, ".oh-my-opencode.json")
|
|
19495
|
+
const userConfigPaths = [
|
|
19496
|
+
path3.join(os3.homedir(), ".config", "opencode", "oh-my-opencode.json")
|
|
19331
19497
|
];
|
|
19332
|
-
|
|
19333
|
-
|
|
19334
|
-
|
|
19335
|
-
|
|
19336
|
-
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19342
|
-
|
|
19343
|
-
|
|
19344
|
-
|
|
19498
|
+
const projectConfigPaths = [
|
|
19499
|
+
path3.join(directory, ".opencode", "oh-my-opencode.json")
|
|
19500
|
+
];
|
|
19501
|
+
let config3 = {};
|
|
19502
|
+
for (const configPath of userConfigPaths) {
|
|
19503
|
+
const userConfig = loadConfigFromPath2(configPath);
|
|
19504
|
+
if (userConfig) {
|
|
19505
|
+
config3 = userConfig;
|
|
19506
|
+
break;
|
|
19507
|
+
}
|
|
19508
|
+
}
|
|
19509
|
+
for (const configPath of projectConfigPaths) {
|
|
19510
|
+
const projectConfig = loadConfigFromPath2(configPath);
|
|
19511
|
+
if (projectConfig) {
|
|
19512
|
+
config3 = mergeConfigs(config3, projectConfig);
|
|
19513
|
+
break;
|
|
19514
|
+
}
|
|
19345
19515
|
}
|
|
19346
|
-
|
|
19516
|
+
log("Final merged config", { agents: config3.agents, disabled_agents: config3.disabled_agents, disabled_mcps: config3.disabled_mcps });
|
|
19517
|
+
return config3;
|
|
19347
19518
|
}
|
|
19348
19519
|
var OhMyOpenCodePlugin = async (ctx) => {
|
|
19349
19520
|
const pluginConfig = loadPluginConfig(ctx.directory);
|
|
@@ -19353,6 +19524,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
19353
19524
|
const commentChecker = createCommentCheckerHooks();
|
|
19354
19525
|
const grepOutputTruncator = createGrepOutputTruncatorHook(ctx);
|
|
19355
19526
|
const directoryAgentsInjector = createDirectoryAgentsInjectorHook(ctx);
|
|
19527
|
+
const directoryReadmeInjector = createDirectoryReadmeInjectorHook(ctx);
|
|
19356
19528
|
const emptyTaskResponseDetector = createEmptyTaskResponseDetectorHook(ctx);
|
|
19357
19529
|
const thinkMode = createThinkModeHook();
|
|
19358
19530
|
const claudeCodeHooks = createClaudeCodeHooksHook(ctx, {});
|
|
@@ -19404,6 +19576,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
19404
19576
|
await todoContinuationEnforcer(input);
|
|
19405
19577
|
await contextWindowMonitor.event(input);
|
|
19406
19578
|
await directoryAgentsInjector.event(input);
|
|
19579
|
+
await directoryReadmeInjector.event(input);
|
|
19407
19580
|
await thinkMode.event(input);
|
|
19408
19581
|
await anthropicAutoCompact.event(input);
|
|
19409
19582
|
const { event } = input;
|
|
@@ -19503,6 +19676,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
19503
19676
|
await contextWindowMonitor["tool.execute.after"](input, output);
|
|
19504
19677
|
await commentChecker["tool.execute.after"](input, output);
|
|
19505
19678
|
await directoryAgentsInjector["tool.execute.after"](input, output);
|
|
19679
|
+
await directoryReadmeInjector["tool.execute.after"](input, output);
|
|
19506
19680
|
await emptyTaskResponseDetector["tool.execute.after"](input, output);
|
|
19507
19681
|
if (input.sessionID === getMainSessionID()) {
|
|
19508
19682
|
updateTerminalTitle({
|