oh-my-opencode 2.4.4 → 2.4.6
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.ja.md +4 -4
- package/README.ko.md +4 -4
- package/README.md +4 -4
- package/README.zh-cn.md +4 -4
- package/dist/hooks/non-interactive-env/constants.d.ts +32 -0
- package/dist/hooks/non-interactive-env/index.d.ts +1 -0
- package/dist/index.js +281 -404
- package/dist/tools/index.d.ts +0 -9
- package/package.json +1 -1
- package/dist/tools/skill/index.d.ts +0 -2
- package/dist/tools/skill/tools.d.ts +0 -10
- package/dist/tools/skill/types.d.ts +0 -41
package/dist/index.js
CHANGED
|
@@ -2674,7 +2674,7 @@ You are a technical writer who creates documentation that developers actually wa
|
|
|
2674
2674
|
var multimodalLookerAgent = {
|
|
2675
2675
|
description: "Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents.",
|
|
2676
2676
|
mode: "subagent",
|
|
2677
|
-
model: "google/gemini-
|
|
2677
|
+
model: "google/gemini-3-flash",
|
|
2678
2678
|
temperature: 0.1,
|
|
2679
2679
|
tools: { write: false, edit: false, bash: false, background_task: false },
|
|
2680
2680
|
prompt: `You interpret media files that cannot be read as plain text.
|
|
@@ -6061,6 +6061,10 @@ function createAnthropicAutoCompactHook(ctx, options) {
|
|
|
6061
6061
|
event: eventHandler
|
|
6062
6062
|
};
|
|
6063
6063
|
}
|
|
6064
|
+
// src/hooks/preemptive-compaction/index.ts
|
|
6065
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5 } from "fs";
|
|
6066
|
+
import { join as join19 } from "path";
|
|
6067
|
+
|
|
6064
6068
|
// src/hooks/preemptive-compaction/constants.ts
|
|
6065
6069
|
var DEFAULT_THRESHOLD = 0.85;
|
|
6066
6070
|
var MIN_TOKENS_FOR_COMPACTION = 50000;
|
|
@@ -6072,6 +6076,19 @@ var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
|
|
|
6072
6076
|
function isSupportedModel(modelID) {
|
|
6073
6077
|
return CLAUDE_MODEL_PATTERN.test(modelID);
|
|
6074
6078
|
}
|
|
6079
|
+
function getMessageDir4(sessionID) {
|
|
6080
|
+
if (!existsSync15(MESSAGE_STORAGE))
|
|
6081
|
+
return null;
|
|
6082
|
+
const directPath = join19(MESSAGE_STORAGE, sessionID);
|
|
6083
|
+
if (existsSync15(directPath))
|
|
6084
|
+
return directPath;
|
|
6085
|
+
for (const dir of readdirSync5(MESSAGE_STORAGE)) {
|
|
6086
|
+
const sessionPath = join19(MESSAGE_STORAGE, dir, sessionID);
|
|
6087
|
+
if (existsSync15(sessionPath))
|
|
6088
|
+
return sessionPath;
|
|
6089
|
+
}
|
|
6090
|
+
return null;
|
|
6091
|
+
}
|
|
6075
6092
|
function createState() {
|
|
6076
6093
|
return {
|
|
6077
6094
|
lastCompactionTime: new Map,
|
|
@@ -6211,6 +6228,19 @@ function createPreemptiveCompactionHook(ctx, options) {
|
|
|
6211
6228
|
if (assistants.length === 0)
|
|
6212
6229
|
return;
|
|
6213
6230
|
const lastAssistant = assistants[assistants.length - 1];
|
|
6231
|
+
if (!lastAssistant.providerID || !lastAssistant.modelID) {
|
|
6232
|
+
const messageDir = getMessageDir4(sessionID);
|
|
6233
|
+
const storedMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
6234
|
+
if (storedMessage?.model?.providerID && storedMessage?.model?.modelID) {
|
|
6235
|
+
lastAssistant.providerID = storedMessage.model.providerID;
|
|
6236
|
+
lastAssistant.modelID = storedMessage.model.modelID;
|
|
6237
|
+
log("[preemptive-compaction] using stored message model info", {
|
|
6238
|
+
sessionID,
|
|
6239
|
+
providerID: lastAssistant.providerID,
|
|
6240
|
+
modelID: lastAssistant.modelID
|
|
6241
|
+
});
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6214
6244
|
await checkAndTriggerCompaction(sessionID, lastAssistant);
|
|
6215
6245
|
} catch {}
|
|
6216
6246
|
}
|
|
@@ -6534,8 +6564,8 @@ function createThinkModeHook() {
|
|
|
6534
6564
|
}
|
|
6535
6565
|
// src/hooks/claude-code-hooks/config.ts
|
|
6536
6566
|
import { homedir as homedir6 } from "os";
|
|
6537
|
-
import { join as
|
|
6538
|
-
import { existsSync as
|
|
6567
|
+
import { join as join20 } from "path";
|
|
6568
|
+
import { existsSync as existsSync16 } from "fs";
|
|
6539
6569
|
function normalizeHookMatcher(raw) {
|
|
6540
6570
|
return {
|
|
6541
6571
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -6560,11 +6590,11 @@ function normalizeHooksConfig(raw) {
|
|
|
6560
6590
|
function getClaudeSettingsPaths(customPath) {
|
|
6561
6591
|
const home = homedir6();
|
|
6562
6592
|
const paths = [
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6593
|
+
join20(home, ".claude", "settings.json"),
|
|
6594
|
+
join20(process.cwd(), ".claude", "settings.json"),
|
|
6595
|
+
join20(process.cwd(), ".claude", "settings.local.json")
|
|
6566
6596
|
];
|
|
6567
|
-
if (customPath &&
|
|
6597
|
+
if (customPath && existsSync16(customPath)) {
|
|
6568
6598
|
paths.unshift(customPath);
|
|
6569
6599
|
}
|
|
6570
6600
|
return paths;
|
|
@@ -6588,7 +6618,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6588
6618
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
6589
6619
|
let mergedConfig = {};
|
|
6590
6620
|
for (const settingsPath of paths) {
|
|
6591
|
-
if (
|
|
6621
|
+
if (existsSync16(settingsPath)) {
|
|
6592
6622
|
try {
|
|
6593
6623
|
const content = await Bun.file(settingsPath).text();
|
|
6594
6624
|
const settings = JSON.parse(content);
|
|
@@ -6605,15 +6635,15 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
6605
6635
|
}
|
|
6606
6636
|
|
|
6607
6637
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
6608
|
-
import { existsSync as
|
|
6638
|
+
import { existsSync as existsSync17 } from "fs";
|
|
6609
6639
|
import { homedir as homedir7 } from "os";
|
|
6610
|
-
import { join as
|
|
6611
|
-
var USER_CONFIG_PATH =
|
|
6640
|
+
import { join as join21 } from "path";
|
|
6641
|
+
var USER_CONFIG_PATH = join21(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
6612
6642
|
function getProjectConfigPath() {
|
|
6613
|
-
return
|
|
6643
|
+
return join21(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
6614
6644
|
}
|
|
6615
6645
|
async function loadConfigFromPath(path4) {
|
|
6616
|
-
if (!
|
|
6646
|
+
if (!existsSync17(path4)) {
|
|
6617
6647
|
return null;
|
|
6618
6648
|
}
|
|
6619
6649
|
try {
|
|
@@ -6792,16 +6822,16 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
6792
6822
|
}
|
|
6793
6823
|
|
|
6794
6824
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
6795
|
-
import { join as
|
|
6796
|
-
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as
|
|
6825
|
+
import { join as join22 } from "path";
|
|
6826
|
+
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync18, writeFileSync as writeFileSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
6797
6827
|
import { homedir as homedir8, tmpdir as tmpdir5 } from "os";
|
|
6798
6828
|
import { randomUUID } from "crypto";
|
|
6799
|
-
var TRANSCRIPT_DIR =
|
|
6829
|
+
var TRANSCRIPT_DIR = join22(homedir8(), ".claude", "transcripts");
|
|
6800
6830
|
function getTranscriptPath(sessionId) {
|
|
6801
|
-
return
|
|
6831
|
+
return join22(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
6802
6832
|
}
|
|
6803
6833
|
function ensureTranscriptDir() {
|
|
6804
|
-
if (!
|
|
6834
|
+
if (!existsSync18(TRANSCRIPT_DIR)) {
|
|
6805
6835
|
mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
|
|
6806
6836
|
}
|
|
6807
6837
|
}
|
|
@@ -6888,7 +6918,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6888
6918
|
}
|
|
6889
6919
|
};
|
|
6890
6920
|
entries.push(JSON.stringify(currentEntry));
|
|
6891
|
-
const tempPath =
|
|
6921
|
+
const tempPath = join22(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6892
6922
|
writeFileSync6(tempPath, entries.join(`
|
|
6893
6923
|
`) + `
|
|
6894
6924
|
`);
|
|
@@ -6908,7 +6938,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
6908
6938
|
]
|
|
6909
6939
|
}
|
|
6910
6940
|
};
|
|
6911
|
-
const tempPath =
|
|
6941
|
+
const tempPath = join22(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
6912
6942
|
writeFileSync6(tempPath, JSON.stringify(currentEntry) + `
|
|
6913
6943
|
`);
|
|
6914
6944
|
return tempPath;
|
|
@@ -7120,11 +7150,11 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
7120
7150
|
}
|
|
7121
7151
|
|
|
7122
7152
|
// src/hooks/claude-code-hooks/todo.ts
|
|
7123
|
-
import { join as
|
|
7153
|
+
import { join as join23 } from "path";
|
|
7124
7154
|
import { homedir as homedir9 } from "os";
|
|
7125
|
-
var TODO_DIR =
|
|
7155
|
+
var TODO_DIR = join23(homedir9(), ".claude", "todos");
|
|
7126
7156
|
function getTodoPath(sessionId) {
|
|
7127
|
-
return
|
|
7157
|
+
return join23(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
7128
7158
|
}
|
|
7129
7159
|
|
|
7130
7160
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -7462,17 +7492,17 @@ import { relative as relative3, resolve as resolve4 } from "path";
|
|
|
7462
7492
|
|
|
7463
7493
|
// src/hooks/rules-injector/finder.ts
|
|
7464
7494
|
import {
|
|
7465
|
-
existsSync as
|
|
7466
|
-
readdirSync as
|
|
7495
|
+
existsSync as existsSync19,
|
|
7496
|
+
readdirSync as readdirSync6,
|
|
7467
7497
|
realpathSync,
|
|
7468
7498
|
statSync as statSync2
|
|
7469
7499
|
} from "fs";
|
|
7470
|
-
import { dirname as dirname4, join as
|
|
7500
|
+
import { dirname as dirname4, join as join25, relative } from "path";
|
|
7471
7501
|
|
|
7472
7502
|
// src/hooks/rules-injector/constants.ts
|
|
7473
|
-
import { join as
|
|
7474
|
-
var OPENCODE_STORAGE6 =
|
|
7475
|
-
var RULES_INJECTOR_STORAGE =
|
|
7503
|
+
import { join as join24 } from "path";
|
|
7504
|
+
var OPENCODE_STORAGE6 = join24(xdgData2 ?? "", "opencode", "storage");
|
|
7505
|
+
var RULES_INJECTOR_STORAGE = join24(OPENCODE_STORAGE6, "rules-injector");
|
|
7476
7506
|
var PROJECT_MARKERS = [
|
|
7477
7507
|
".git",
|
|
7478
7508
|
"pyproject.toml",
|
|
@@ -7499,8 +7529,8 @@ function findProjectRoot(startPath) {
|
|
|
7499
7529
|
}
|
|
7500
7530
|
while (true) {
|
|
7501
7531
|
for (const marker of PROJECT_MARKERS) {
|
|
7502
|
-
const markerPath =
|
|
7503
|
-
if (
|
|
7532
|
+
const markerPath = join25(current, marker);
|
|
7533
|
+
if (existsSync19(markerPath)) {
|
|
7504
7534
|
return current;
|
|
7505
7535
|
}
|
|
7506
7536
|
}
|
|
@@ -7512,12 +7542,12 @@ function findProjectRoot(startPath) {
|
|
|
7512
7542
|
}
|
|
7513
7543
|
}
|
|
7514
7544
|
function findRuleFilesRecursive(dir, results) {
|
|
7515
|
-
if (!
|
|
7545
|
+
if (!existsSync19(dir))
|
|
7516
7546
|
return;
|
|
7517
7547
|
try {
|
|
7518
|
-
const entries =
|
|
7548
|
+
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
7519
7549
|
for (const entry of entries) {
|
|
7520
|
-
const fullPath =
|
|
7550
|
+
const fullPath = join25(dir, entry.name);
|
|
7521
7551
|
if (entry.isDirectory()) {
|
|
7522
7552
|
findRuleFilesRecursive(fullPath, results);
|
|
7523
7553
|
} else if (entry.isFile()) {
|
|
@@ -7543,7 +7573,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7543
7573
|
let distance = 0;
|
|
7544
7574
|
while (true) {
|
|
7545
7575
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
7546
|
-
const ruleDir =
|
|
7576
|
+
const ruleDir = join25(currentDir, parent, subdir);
|
|
7547
7577
|
const files = [];
|
|
7548
7578
|
findRuleFilesRecursive(ruleDir, files);
|
|
7549
7579
|
for (const filePath of files) {
|
|
@@ -7567,7 +7597,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
7567
7597
|
currentDir = parentDir;
|
|
7568
7598
|
distance++;
|
|
7569
7599
|
}
|
|
7570
|
-
const userRuleDir =
|
|
7600
|
+
const userRuleDir = join25(homeDir, USER_RULE_DIR);
|
|
7571
7601
|
const userFiles = [];
|
|
7572
7602
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
7573
7603
|
for (const filePath of userFiles) {
|
|
@@ -7756,19 +7786,19 @@ function mergeGlobs(existing, newValue) {
|
|
|
7756
7786
|
|
|
7757
7787
|
// src/hooks/rules-injector/storage.ts
|
|
7758
7788
|
import {
|
|
7759
|
-
existsSync as
|
|
7789
|
+
existsSync as existsSync20,
|
|
7760
7790
|
mkdirSync as mkdirSync7,
|
|
7761
7791
|
readFileSync as readFileSync9,
|
|
7762
7792
|
writeFileSync as writeFileSync7,
|
|
7763
7793
|
unlinkSync as unlinkSync6
|
|
7764
7794
|
} from "fs";
|
|
7765
|
-
import { join as
|
|
7795
|
+
import { join as join26 } from "path";
|
|
7766
7796
|
function getStoragePath3(sessionID) {
|
|
7767
|
-
return
|
|
7797
|
+
return join26(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
7768
7798
|
}
|
|
7769
7799
|
function loadInjectedRules(sessionID) {
|
|
7770
7800
|
const filePath = getStoragePath3(sessionID);
|
|
7771
|
-
if (!
|
|
7801
|
+
if (!existsSync20(filePath))
|
|
7772
7802
|
return { contentHashes: new Set, realPaths: new Set };
|
|
7773
7803
|
try {
|
|
7774
7804
|
const content = readFileSync9(filePath, "utf-8");
|
|
@@ -7782,7 +7812,7 @@ function loadInjectedRules(sessionID) {
|
|
|
7782
7812
|
}
|
|
7783
7813
|
}
|
|
7784
7814
|
function saveInjectedRules(sessionID, data) {
|
|
7785
|
-
if (!
|
|
7815
|
+
if (!existsSync20(RULES_INJECTOR_STORAGE)) {
|
|
7786
7816
|
mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
7787
7817
|
}
|
|
7788
7818
|
const storageData = {
|
|
@@ -7795,7 +7825,7 @@ function saveInjectedRules(sessionID, data) {
|
|
|
7795
7825
|
}
|
|
7796
7826
|
function clearInjectedRules(sessionID) {
|
|
7797
7827
|
const filePath = getStoragePath3(sessionID);
|
|
7798
|
-
if (
|
|
7828
|
+
if (existsSync20(filePath)) {
|
|
7799
7829
|
unlinkSync6(filePath);
|
|
7800
7830
|
}
|
|
7801
7831
|
}
|
|
@@ -8365,18 +8395,18 @@ async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
|
|
|
8365
8395
|
}
|
|
8366
8396
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8367
8397
|
import {
|
|
8368
|
-
existsSync as
|
|
8398
|
+
existsSync as existsSync23,
|
|
8369
8399
|
mkdirSync as mkdirSync8,
|
|
8370
8400
|
readFileSync as readFileSync13,
|
|
8371
8401
|
writeFileSync as writeFileSync10,
|
|
8372
8402
|
unlinkSync as unlinkSync7
|
|
8373
8403
|
} from "fs";
|
|
8374
|
-
import { join as
|
|
8404
|
+
import { join as join31 } from "path";
|
|
8375
8405
|
|
|
8376
8406
|
// src/hooks/agent-usage-reminder/constants.ts
|
|
8377
|
-
import { join as
|
|
8378
|
-
var OPENCODE_STORAGE7 =
|
|
8379
|
-
var AGENT_USAGE_REMINDER_STORAGE =
|
|
8407
|
+
import { join as join30 } from "path";
|
|
8408
|
+
var OPENCODE_STORAGE7 = join30(xdgData2 ?? "", "opencode", "storage");
|
|
8409
|
+
var AGENT_USAGE_REMINDER_STORAGE = join30(OPENCODE_STORAGE7, "agent-usage-reminder");
|
|
8380
8410
|
var TARGET_TOOLS = new Set([
|
|
8381
8411
|
"grep",
|
|
8382
8412
|
"safe_grep",
|
|
@@ -8421,11 +8451,11 @@ ALWAYS prefer: Multiple parallel background_task calls > Direct tool calls
|
|
|
8421
8451
|
|
|
8422
8452
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
8423
8453
|
function getStoragePath4(sessionID) {
|
|
8424
|
-
return
|
|
8454
|
+
return join31(AGENT_USAGE_REMINDER_STORAGE, `${sessionID}.json`);
|
|
8425
8455
|
}
|
|
8426
8456
|
function loadAgentUsageState(sessionID) {
|
|
8427
8457
|
const filePath = getStoragePath4(sessionID);
|
|
8428
|
-
if (!
|
|
8458
|
+
if (!existsSync23(filePath))
|
|
8429
8459
|
return null;
|
|
8430
8460
|
try {
|
|
8431
8461
|
const content = readFileSync13(filePath, "utf-8");
|
|
@@ -8435,7 +8465,7 @@ function loadAgentUsageState(sessionID) {
|
|
|
8435
8465
|
}
|
|
8436
8466
|
}
|
|
8437
8467
|
function saveAgentUsageState(state2) {
|
|
8438
|
-
if (!
|
|
8468
|
+
if (!existsSync23(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
8439
8469
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
8440
8470
|
}
|
|
8441
8471
|
const filePath = getStoragePath4(state2.sessionID);
|
|
@@ -8443,7 +8473,7 @@ function saveAgentUsageState(state2) {
|
|
|
8443
8473
|
}
|
|
8444
8474
|
function clearAgentUsageState(sessionID) {
|
|
8445
8475
|
const filePath = getStoragePath4(sessionID);
|
|
8446
|
-
if (
|
|
8476
|
+
if (existsSync23(filePath)) {
|
|
8447
8477
|
unlinkSync7(filePath);
|
|
8448
8478
|
}
|
|
8449
8479
|
}
|
|
@@ -8643,10 +8673,65 @@ var NON_INTERACTIVE_ENV = {
|
|
|
8643
8673
|
VISUAL: "true",
|
|
8644
8674
|
GIT_SEQUENCE_EDITOR: "true",
|
|
8645
8675
|
GIT_PAGER: "cat",
|
|
8646
|
-
PAGER: "cat"
|
|
8676
|
+
PAGER: "cat",
|
|
8677
|
+
npm_config_yes: "true",
|
|
8678
|
+
PIP_NO_INPUT: "1",
|
|
8679
|
+
YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
|
|
8680
|
+
};
|
|
8681
|
+
var SHELL_COMMAND_PATTERNS = {
|
|
8682
|
+
npm: {
|
|
8683
|
+
bad: ["npm init", "npm install (prompts)"],
|
|
8684
|
+
good: ["npm init -y", "npm install --yes"]
|
|
8685
|
+
},
|
|
8686
|
+
apt: {
|
|
8687
|
+
bad: ["apt-get install pkg"],
|
|
8688
|
+
good: ["apt-get install -y pkg", "DEBIAN_FRONTEND=noninteractive apt-get install pkg"]
|
|
8689
|
+
},
|
|
8690
|
+
pip: {
|
|
8691
|
+
bad: ["pip install pkg (with prompts)"],
|
|
8692
|
+
good: ["pip install --no-input pkg", "PIP_NO_INPUT=1 pip install pkg"]
|
|
8693
|
+
},
|
|
8694
|
+
git: {
|
|
8695
|
+
bad: ["git commit", "git merge branch", "git add -p", "git rebase -i"],
|
|
8696
|
+
good: ["git commit -m 'msg'", "git merge --no-edit branch", "git add .", "git rebase --no-edit"]
|
|
8697
|
+
},
|
|
8698
|
+
system: {
|
|
8699
|
+
bad: ["rm file (prompts)", "cp a b (prompts)", "ssh host"],
|
|
8700
|
+
good: ["rm -f file", "cp -f a b", "ssh -o BatchMode=yes host", "unzip -o file.zip"]
|
|
8701
|
+
},
|
|
8702
|
+
banned: [
|
|
8703
|
+
"vim",
|
|
8704
|
+
"nano",
|
|
8705
|
+
"vi",
|
|
8706
|
+
"emacs",
|
|
8707
|
+
"less",
|
|
8708
|
+
"more",
|
|
8709
|
+
"man",
|
|
8710
|
+
"python (REPL)",
|
|
8711
|
+
"node (REPL)",
|
|
8712
|
+
"git add -p",
|
|
8713
|
+
"git rebase -i"
|
|
8714
|
+
],
|
|
8715
|
+
workarounds: {
|
|
8716
|
+
yesPipe: "yes | ./script.sh",
|
|
8717
|
+
heredoc: `./script.sh <<EOF
|
|
8718
|
+
option1
|
|
8719
|
+
option2
|
|
8720
|
+
EOF`,
|
|
8721
|
+
expectAlternative: "Use environment variables or config files instead of expect"
|
|
8722
|
+
}
|
|
8647
8723
|
};
|
|
8648
8724
|
|
|
8649
8725
|
// src/hooks/non-interactive-env/index.ts
|
|
8726
|
+
var BANNED_COMMAND_PATTERNS = SHELL_COMMAND_PATTERNS.banned.filter((cmd) => !cmd.includes("(")).map((cmd) => new RegExp(`\\b${cmd}\\b`));
|
|
8727
|
+
function detectBannedCommand(command) {
|
|
8728
|
+
for (let i = 0;i < BANNED_COMMAND_PATTERNS.length; i++) {
|
|
8729
|
+
if (BANNED_COMMAND_PATTERNS[i].test(command)) {
|
|
8730
|
+
return SHELL_COMMAND_PATTERNS.banned[i];
|
|
8731
|
+
}
|
|
8732
|
+
}
|
|
8733
|
+
return;
|
|
8734
|
+
}
|
|
8650
8735
|
function createNonInteractiveEnvHook(_ctx) {
|
|
8651
8736
|
return {
|
|
8652
8737
|
"tool.execute.before": async (input, output) => {
|
|
@@ -8661,6 +8746,10 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
8661
8746
|
...output.args.env,
|
|
8662
8747
|
...NON_INTERACTIVE_ENV
|
|
8663
8748
|
};
|
|
8749
|
+
const bannedCmd = detectBannedCommand(command);
|
|
8750
|
+
if (bannedCmd) {
|
|
8751
|
+
output.message = `\u26A0\uFE0F Warning: '${bannedCmd}' is an interactive command that may hang in non-interactive environments.`;
|
|
8752
|
+
}
|
|
8664
8753
|
log(`[${HOOK_NAME2}] Set non-interactive environment variables`, {
|
|
8665
8754
|
sessionID: input.sessionID,
|
|
8666
8755
|
env: NON_INTERACTIVE_ENV
|
|
@@ -8670,18 +8759,18 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
8670
8759
|
}
|
|
8671
8760
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8672
8761
|
import {
|
|
8673
|
-
existsSync as
|
|
8762
|
+
existsSync as existsSync24,
|
|
8674
8763
|
mkdirSync as mkdirSync9,
|
|
8675
8764
|
readFileSync as readFileSync14,
|
|
8676
8765
|
writeFileSync as writeFileSync11,
|
|
8677
8766
|
unlinkSync as unlinkSync8
|
|
8678
8767
|
} from "fs";
|
|
8679
|
-
import { join as
|
|
8768
|
+
import { join as join33 } from "path";
|
|
8680
8769
|
|
|
8681
8770
|
// src/hooks/interactive-bash-session/constants.ts
|
|
8682
|
-
import { join as
|
|
8683
|
-
var OPENCODE_STORAGE8 =
|
|
8684
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
8771
|
+
import { join as join32 } from "path";
|
|
8772
|
+
var OPENCODE_STORAGE8 = join32(xdgData2 ?? "", "opencode", "storage");
|
|
8773
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join32(OPENCODE_STORAGE8, "interactive-bash-session");
|
|
8685
8774
|
var OMO_SESSION_PREFIX = "omo-";
|
|
8686
8775
|
function buildSessionReminderMessage(sessions) {
|
|
8687
8776
|
if (sessions.length === 0)
|
|
@@ -8693,11 +8782,11 @@ function buildSessionReminderMessage(sessions) {
|
|
|
8693
8782
|
|
|
8694
8783
|
// src/hooks/interactive-bash-session/storage.ts
|
|
8695
8784
|
function getStoragePath5(sessionID) {
|
|
8696
|
-
return
|
|
8785
|
+
return join33(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
8697
8786
|
}
|
|
8698
8787
|
function loadInteractiveBashSessionState(sessionID) {
|
|
8699
8788
|
const filePath = getStoragePath5(sessionID);
|
|
8700
|
-
if (!
|
|
8789
|
+
if (!existsSync24(filePath))
|
|
8701
8790
|
return null;
|
|
8702
8791
|
try {
|
|
8703
8792
|
const content = readFileSync14(filePath, "utf-8");
|
|
@@ -8712,7 +8801,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
8712
8801
|
}
|
|
8713
8802
|
}
|
|
8714
8803
|
function saveInteractiveBashSessionState(state2) {
|
|
8715
|
-
if (!
|
|
8804
|
+
if (!existsSync24(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
8716
8805
|
mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
8717
8806
|
}
|
|
8718
8807
|
const filePath = getStoragePath5(state2.sessionID);
|
|
@@ -8725,7 +8814,7 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
8725
8814
|
}
|
|
8726
8815
|
function clearInteractiveBashSessionState(sessionID) {
|
|
8727
8816
|
const filePath = getStoragePath5(sessionID);
|
|
8728
|
-
if (
|
|
8817
|
+
if (existsSync24(filePath)) {
|
|
8729
8818
|
unlinkSync8(filePath);
|
|
8730
8819
|
}
|
|
8731
8820
|
}
|
|
@@ -10669,19 +10758,19 @@ async function createGoogleAntigravityAuthPlugin({
|
|
|
10669
10758
|
};
|
|
10670
10759
|
}
|
|
10671
10760
|
// src/features/claude-code-command-loader/loader.ts
|
|
10672
|
-
import { existsSync as
|
|
10761
|
+
import { existsSync as existsSync25, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "fs";
|
|
10673
10762
|
import { homedir as homedir12 } from "os";
|
|
10674
|
-
import { join as
|
|
10763
|
+
import { join as join34, basename } from "path";
|
|
10675
10764
|
function loadCommandsFromDir(commandsDir, scope) {
|
|
10676
|
-
if (!
|
|
10765
|
+
if (!existsSync25(commandsDir)) {
|
|
10677
10766
|
return [];
|
|
10678
10767
|
}
|
|
10679
|
-
const entries =
|
|
10768
|
+
const entries = readdirSync7(commandsDir, { withFileTypes: true });
|
|
10680
10769
|
const commands = [];
|
|
10681
10770
|
for (const entry of entries) {
|
|
10682
10771
|
if (!isMarkdownFile(entry))
|
|
10683
10772
|
continue;
|
|
10684
|
-
const commandPath =
|
|
10773
|
+
const commandPath = join34(commandsDir, entry.name);
|
|
10685
10774
|
const commandName = basename(entry.name, ".md");
|
|
10686
10775
|
try {
|
|
10687
10776
|
const content = readFileSync15(commandPath, "utf-8");
|
|
@@ -10724,44 +10813,44 @@ function commandsToRecord(commands) {
|
|
|
10724
10813
|
return result;
|
|
10725
10814
|
}
|
|
10726
10815
|
function loadUserCommands() {
|
|
10727
|
-
const userCommandsDir =
|
|
10816
|
+
const userCommandsDir = join34(homedir12(), ".claude", "commands");
|
|
10728
10817
|
const commands = loadCommandsFromDir(userCommandsDir, "user");
|
|
10729
10818
|
return commandsToRecord(commands);
|
|
10730
10819
|
}
|
|
10731
10820
|
function loadProjectCommands() {
|
|
10732
|
-
const projectCommandsDir =
|
|
10821
|
+
const projectCommandsDir = join34(process.cwd(), ".claude", "commands");
|
|
10733
10822
|
const commands = loadCommandsFromDir(projectCommandsDir, "project");
|
|
10734
10823
|
return commandsToRecord(commands);
|
|
10735
10824
|
}
|
|
10736
10825
|
function loadOpencodeGlobalCommands() {
|
|
10737
|
-
const opencodeCommandsDir =
|
|
10826
|
+
const opencodeCommandsDir = join34(homedir12(), ".config", "opencode", "command");
|
|
10738
10827
|
const commands = loadCommandsFromDir(opencodeCommandsDir, "opencode");
|
|
10739
10828
|
return commandsToRecord(commands);
|
|
10740
10829
|
}
|
|
10741
10830
|
function loadOpencodeProjectCommands() {
|
|
10742
|
-
const opencodeProjectDir =
|
|
10831
|
+
const opencodeProjectDir = join34(process.cwd(), ".opencode", "command");
|
|
10743
10832
|
const commands = loadCommandsFromDir(opencodeProjectDir, "opencode-project");
|
|
10744
10833
|
return commandsToRecord(commands);
|
|
10745
10834
|
}
|
|
10746
10835
|
// src/features/claude-code-skill-loader/loader.ts
|
|
10747
|
-
import { existsSync as
|
|
10836
|
+
import { existsSync as existsSync26, readdirSync as readdirSync8, readFileSync as readFileSync16 } from "fs";
|
|
10748
10837
|
import { homedir as homedir13 } from "os";
|
|
10749
|
-
import { join as
|
|
10838
|
+
import { join as join35 } from "path";
|
|
10750
10839
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
10751
|
-
if (!
|
|
10840
|
+
if (!existsSync26(skillsDir)) {
|
|
10752
10841
|
return [];
|
|
10753
10842
|
}
|
|
10754
|
-
const entries =
|
|
10843
|
+
const entries = readdirSync8(skillsDir, { withFileTypes: true });
|
|
10755
10844
|
const skills = [];
|
|
10756
10845
|
for (const entry of entries) {
|
|
10757
10846
|
if (entry.name.startsWith("."))
|
|
10758
10847
|
continue;
|
|
10759
|
-
const skillPath =
|
|
10848
|
+
const skillPath = join35(skillsDir, entry.name);
|
|
10760
10849
|
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
10761
10850
|
continue;
|
|
10762
10851
|
const resolvedPath = resolveSymlink(skillPath);
|
|
10763
|
-
const skillMdPath =
|
|
10764
|
-
if (!
|
|
10852
|
+
const skillMdPath = join35(resolvedPath, "SKILL.md");
|
|
10853
|
+
if (!existsSync26(skillMdPath))
|
|
10765
10854
|
continue;
|
|
10766
10855
|
try {
|
|
10767
10856
|
const content = readFileSync16(skillMdPath, "utf-8");
|
|
@@ -10798,7 +10887,7 @@ $ARGUMENTS
|
|
|
10798
10887
|
return skills;
|
|
10799
10888
|
}
|
|
10800
10889
|
function loadUserSkillsAsCommands() {
|
|
10801
|
-
const userSkillsDir =
|
|
10890
|
+
const userSkillsDir = join35(homedir13(), ".claude", "skills");
|
|
10802
10891
|
const skills = loadSkillsFromDir(userSkillsDir, "user");
|
|
10803
10892
|
return skills.reduce((acc, skill) => {
|
|
10804
10893
|
acc[skill.name] = skill.definition;
|
|
@@ -10806,7 +10895,7 @@ function loadUserSkillsAsCommands() {
|
|
|
10806
10895
|
}, {});
|
|
10807
10896
|
}
|
|
10808
10897
|
function loadProjectSkillsAsCommands() {
|
|
10809
|
-
const projectSkillsDir =
|
|
10898
|
+
const projectSkillsDir = join35(process.cwd(), ".claude", "skills");
|
|
10810
10899
|
const skills = loadSkillsFromDir(projectSkillsDir, "project");
|
|
10811
10900
|
return skills.reduce((acc, skill) => {
|
|
10812
10901
|
acc[skill.name] = skill.definition;
|
|
@@ -10814,9 +10903,9 @@ function loadProjectSkillsAsCommands() {
|
|
|
10814
10903
|
}, {});
|
|
10815
10904
|
}
|
|
10816
10905
|
// src/features/claude-code-agent-loader/loader.ts
|
|
10817
|
-
import { existsSync as
|
|
10906
|
+
import { existsSync as existsSync27, readdirSync as readdirSync9, readFileSync as readFileSync17 } from "fs";
|
|
10818
10907
|
import { homedir as homedir14 } from "os";
|
|
10819
|
-
import { join as
|
|
10908
|
+
import { join as join36, basename as basename2 } from "path";
|
|
10820
10909
|
function parseToolsConfig(toolsStr) {
|
|
10821
10910
|
if (!toolsStr)
|
|
10822
10911
|
return;
|
|
@@ -10830,15 +10919,15 @@ function parseToolsConfig(toolsStr) {
|
|
|
10830
10919
|
return result;
|
|
10831
10920
|
}
|
|
10832
10921
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
10833
|
-
if (!
|
|
10922
|
+
if (!existsSync27(agentsDir)) {
|
|
10834
10923
|
return [];
|
|
10835
10924
|
}
|
|
10836
|
-
const entries =
|
|
10925
|
+
const entries = readdirSync9(agentsDir, { withFileTypes: true });
|
|
10837
10926
|
const agents = [];
|
|
10838
10927
|
for (const entry of entries) {
|
|
10839
10928
|
if (!isMarkdownFile(entry))
|
|
10840
10929
|
continue;
|
|
10841
|
-
const agentPath =
|
|
10930
|
+
const agentPath = join36(agentsDir, entry.name);
|
|
10842
10931
|
const agentName = basename2(entry.name, ".md");
|
|
10843
10932
|
try {
|
|
10844
10933
|
const content = readFileSync17(agentPath, "utf-8");
|
|
@@ -10868,7 +10957,7 @@ function loadAgentsFromDir(agentsDir, scope) {
|
|
|
10868
10957
|
return agents;
|
|
10869
10958
|
}
|
|
10870
10959
|
function loadUserAgents() {
|
|
10871
|
-
const userAgentsDir =
|
|
10960
|
+
const userAgentsDir = join36(homedir14(), ".claude", "agents");
|
|
10872
10961
|
const agents = loadAgentsFromDir(userAgentsDir, "user");
|
|
10873
10962
|
const result = {};
|
|
10874
10963
|
for (const agent of agents) {
|
|
@@ -10877,7 +10966,7 @@ function loadUserAgents() {
|
|
|
10877
10966
|
return result;
|
|
10878
10967
|
}
|
|
10879
10968
|
function loadProjectAgents() {
|
|
10880
|
-
const projectAgentsDir =
|
|
10969
|
+
const projectAgentsDir = join36(process.cwd(), ".claude", "agents");
|
|
10881
10970
|
const agents = loadAgentsFromDir(projectAgentsDir, "project");
|
|
10882
10971
|
const result = {};
|
|
10883
10972
|
for (const agent of agents) {
|
|
@@ -10886,9 +10975,9 @@ function loadProjectAgents() {
|
|
|
10886
10975
|
return result;
|
|
10887
10976
|
}
|
|
10888
10977
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
10889
|
-
import { existsSync as
|
|
10978
|
+
import { existsSync as existsSync28 } from "fs";
|
|
10890
10979
|
import { homedir as homedir15 } from "os";
|
|
10891
|
-
import { join as
|
|
10980
|
+
import { join as join37 } from "path";
|
|
10892
10981
|
|
|
10893
10982
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
10894
10983
|
function expandEnvVars(value) {
|
|
@@ -10957,13 +11046,13 @@ function getMcpConfigPaths() {
|
|
|
10957
11046
|
const home = homedir15();
|
|
10958
11047
|
const cwd = process.cwd();
|
|
10959
11048
|
return [
|
|
10960
|
-
{ path:
|
|
10961
|
-
{ path:
|
|
10962
|
-
{ path:
|
|
11049
|
+
{ path: join37(home, ".claude", ".mcp.json"), scope: "user" },
|
|
11050
|
+
{ path: join37(cwd, ".mcp.json"), scope: "project" },
|
|
11051
|
+
{ path: join37(cwd, ".claude", ".mcp.json"), scope: "local" }
|
|
10963
11052
|
];
|
|
10964
11053
|
}
|
|
10965
11054
|
async function loadMcpConfigFile(filePath) {
|
|
10966
|
-
if (!
|
|
11055
|
+
if (!existsSync28(filePath)) {
|
|
10967
11056
|
return null;
|
|
10968
11057
|
}
|
|
10969
11058
|
try {
|
|
@@ -11298,11 +11387,11 @@ var EXT_TO_LANG = {
|
|
|
11298
11387
|
".gql": "graphql"
|
|
11299
11388
|
};
|
|
11300
11389
|
// src/tools/lsp/config.ts
|
|
11301
|
-
import { existsSync as
|
|
11302
|
-
import { join as
|
|
11390
|
+
import { existsSync as existsSync29, readFileSync as readFileSync18 } from "fs";
|
|
11391
|
+
import { join as join38 } from "path";
|
|
11303
11392
|
import { homedir as homedir16 } from "os";
|
|
11304
11393
|
function loadJsonFile(path7) {
|
|
11305
|
-
if (!
|
|
11394
|
+
if (!existsSync29(path7))
|
|
11306
11395
|
return null;
|
|
11307
11396
|
try {
|
|
11308
11397
|
return JSON.parse(readFileSync18(path7, "utf-8"));
|
|
@@ -11313,9 +11402,9 @@ function loadJsonFile(path7) {
|
|
|
11313
11402
|
function getConfigPaths2() {
|
|
11314
11403
|
const cwd = process.cwd();
|
|
11315
11404
|
return {
|
|
11316
|
-
project:
|
|
11317
|
-
user:
|
|
11318
|
-
opencode:
|
|
11405
|
+
project: join38(cwd, ".opencode", "oh-my-opencode.json"),
|
|
11406
|
+
user: join38(homedir16(), ".config", "opencode", "oh-my-opencode.json"),
|
|
11407
|
+
opencode: join38(homedir16(), ".config", "opencode", "opencode.json")
|
|
11319
11408
|
};
|
|
11320
11409
|
}
|
|
11321
11410
|
function loadAllConfigs() {
|
|
@@ -11411,21 +11500,21 @@ function isServerInstalled(command) {
|
|
|
11411
11500
|
const pathSeparator = isWindows2 ? ";" : ":";
|
|
11412
11501
|
const paths = pathEnv.split(pathSeparator);
|
|
11413
11502
|
for (const p of paths) {
|
|
11414
|
-
if (
|
|
11503
|
+
if (existsSync29(join38(p, cmd)) || existsSync29(join38(p, cmd + ext))) {
|
|
11415
11504
|
return true;
|
|
11416
11505
|
}
|
|
11417
11506
|
}
|
|
11418
11507
|
const cwd = process.cwd();
|
|
11419
11508
|
const additionalPaths = [
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11509
|
+
join38(cwd, "node_modules", ".bin", cmd),
|
|
11510
|
+
join38(cwd, "node_modules", ".bin", cmd + ext),
|
|
11511
|
+
join38(homedir16(), ".config", "opencode", "bin", cmd),
|
|
11512
|
+
join38(homedir16(), ".config", "opencode", "bin", cmd + ext),
|
|
11513
|
+
join38(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd),
|
|
11514
|
+
join38(homedir16(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
11426
11515
|
];
|
|
11427
11516
|
for (const p of additionalPaths) {
|
|
11428
|
-
if (
|
|
11517
|
+
if (existsSync29(p)) {
|
|
11429
11518
|
return true;
|
|
11430
11519
|
}
|
|
11431
11520
|
}
|
|
@@ -12020,16 +12109,16 @@ ${msg}`);
|
|
|
12020
12109
|
}
|
|
12021
12110
|
// src/tools/lsp/utils.ts
|
|
12022
12111
|
import { extname as extname2, resolve as resolve6 } from "path";
|
|
12023
|
-
import { existsSync as
|
|
12112
|
+
import { existsSync as existsSync30, readFileSync as readFileSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
12024
12113
|
function findWorkspaceRoot(filePath) {
|
|
12025
12114
|
let dir = resolve6(filePath);
|
|
12026
|
-
if (!
|
|
12115
|
+
if (!existsSync30(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
12027
12116
|
dir = __require("path").dirname(dir);
|
|
12028
12117
|
}
|
|
12029
12118
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
12030
12119
|
while (dir !== "/") {
|
|
12031
12120
|
for (const marker of markers) {
|
|
12032
|
-
if (
|
|
12121
|
+
if (existsSync30(__require("path").join(dir, marker))) {
|
|
12033
12122
|
return dir;
|
|
12034
12123
|
}
|
|
12035
12124
|
}
|
|
@@ -24954,13 +25043,13 @@ var lsp_code_action_resolve = tool({
|
|
|
24954
25043
|
});
|
|
24955
25044
|
// src/tools/ast-grep/constants.ts
|
|
24956
25045
|
import { createRequire as createRequire4 } from "module";
|
|
24957
|
-
import { dirname as dirname6, join as
|
|
24958
|
-
import { existsSync as
|
|
25046
|
+
import { dirname as dirname6, join as join40 } from "path";
|
|
25047
|
+
import { existsSync as existsSync32, statSync as statSync4 } from "fs";
|
|
24959
25048
|
|
|
24960
25049
|
// src/tools/ast-grep/downloader.ts
|
|
24961
25050
|
var {spawn: spawn5 } = globalThis.Bun;
|
|
24962
|
-
import { existsSync as
|
|
24963
|
-
import { join as
|
|
25051
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync10, chmodSync as chmodSync2, unlinkSync as unlinkSync9 } from "fs";
|
|
25052
|
+
import { join as join39 } from "path";
|
|
24964
25053
|
import { homedir as homedir17 } from "os";
|
|
24965
25054
|
import { createRequire as createRequire3 } from "module";
|
|
24966
25055
|
var REPO2 = "ast-grep/ast-grep";
|
|
@@ -24986,19 +25075,19 @@ var PLATFORM_MAP2 = {
|
|
|
24986
25075
|
function getCacheDir3() {
|
|
24987
25076
|
if (process.platform === "win32") {
|
|
24988
25077
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
24989
|
-
const base2 = localAppData ||
|
|
24990
|
-
return
|
|
25078
|
+
const base2 = localAppData || join39(homedir17(), "AppData", "Local");
|
|
25079
|
+
return join39(base2, "oh-my-opencode", "bin");
|
|
24991
25080
|
}
|
|
24992
25081
|
const xdgCache2 = process.env.XDG_CACHE_HOME;
|
|
24993
|
-
const base = xdgCache2 ||
|
|
24994
|
-
return
|
|
25082
|
+
const base = xdgCache2 || join39(homedir17(), ".cache");
|
|
25083
|
+
return join39(base, "oh-my-opencode", "bin");
|
|
24995
25084
|
}
|
|
24996
25085
|
function getBinaryName3() {
|
|
24997
25086
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
24998
25087
|
}
|
|
24999
25088
|
function getCachedBinaryPath2() {
|
|
25000
|
-
const binaryPath =
|
|
25001
|
-
return
|
|
25089
|
+
const binaryPath = join39(getCacheDir3(), getBinaryName3());
|
|
25090
|
+
return existsSync31(binaryPath) ? binaryPath : null;
|
|
25002
25091
|
}
|
|
25003
25092
|
async function extractZip2(archivePath, destDir) {
|
|
25004
25093
|
const proc = process.platform === "win32" ? spawn5([
|
|
@@ -25024,8 +25113,8 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25024
25113
|
}
|
|
25025
25114
|
const cacheDir = getCacheDir3();
|
|
25026
25115
|
const binaryName = getBinaryName3();
|
|
25027
|
-
const binaryPath =
|
|
25028
|
-
if (
|
|
25116
|
+
const binaryPath = join39(cacheDir, binaryName);
|
|
25117
|
+
if (existsSync31(binaryPath)) {
|
|
25029
25118
|
return binaryPath;
|
|
25030
25119
|
}
|
|
25031
25120
|
const { arch, os: os5 } = platformInfo;
|
|
@@ -25033,21 +25122,21 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
25033
25122
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
25034
25123
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
25035
25124
|
try {
|
|
25036
|
-
if (!
|
|
25125
|
+
if (!existsSync31(cacheDir)) {
|
|
25037
25126
|
mkdirSync10(cacheDir, { recursive: true });
|
|
25038
25127
|
}
|
|
25039
25128
|
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
25040
25129
|
if (!response2.ok) {
|
|
25041
25130
|
throw new Error(`HTTP ${response2.status}: ${response2.statusText}`);
|
|
25042
25131
|
}
|
|
25043
|
-
const archivePath =
|
|
25132
|
+
const archivePath = join39(cacheDir, assetName);
|
|
25044
25133
|
const arrayBuffer = await response2.arrayBuffer();
|
|
25045
25134
|
await Bun.write(archivePath, arrayBuffer);
|
|
25046
25135
|
await extractZip2(archivePath, cacheDir);
|
|
25047
|
-
if (
|
|
25136
|
+
if (existsSync31(archivePath)) {
|
|
25048
25137
|
unlinkSync9(archivePath);
|
|
25049
25138
|
}
|
|
25050
|
-
if (process.platform !== "win32" &&
|
|
25139
|
+
if (process.platform !== "win32" && existsSync31(binaryPath)) {
|
|
25051
25140
|
chmodSync2(binaryPath, 493);
|
|
25052
25141
|
}
|
|
25053
25142
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -25098,8 +25187,8 @@ function findSgCliPathSync() {
|
|
|
25098
25187
|
const require2 = createRequire4(import.meta.url);
|
|
25099
25188
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
25100
25189
|
const cliDir = dirname6(cliPkgPath);
|
|
25101
|
-
const sgPath =
|
|
25102
|
-
if (
|
|
25190
|
+
const sgPath = join40(cliDir, binaryName);
|
|
25191
|
+
if (existsSync32(sgPath) && isValidBinary(sgPath)) {
|
|
25103
25192
|
return sgPath;
|
|
25104
25193
|
}
|
|
25105
25194
|
} catch {}
|
|
@@ -25110,8 +25199,8 @@ function findSgCliPathSync() {
|
|
|
25110
25199
|
const pkgPath = require2.resolve(`${platformPkg}/package.json`);
|
|
25111
25200
|
const pkgDir = dirname6(pkgPath);
|
|
25112
25201
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
25113
|
-
const binaryPath =
|
|
25114
|
-
if (
|
|
25202
|
+
const binaryPath = join40(pkgDir, astGrepName);
|
|
25203
|
+
if (existsSync32(binaryPath) && isValidBinary(binaryPath)) {
|
|
25115
25204
|
return binaryPath;
|
|
25116
25205
|
}
|
|
25117
25206
|
} catch {}
|
|
@@ -25119,7 +25208,7 @@ function findSgCliPathSync() {
|
|
|
25119
25208
|
if (process.platform === "darwin") {
|
|
25120
25209
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
25121
25210
|
for (const path7 of homebrewPaths) {
|
|
25122
|
-
if (
|
|
25211
|
+
if (existsSync32(path7) && isValidBinary(path7)) {
|
|
25123
25212
|
return path7;
|
|
25124
25213
|
}
|
|
25125
25214
|
}
|
|
@@ -25175,11 +25264,11 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
25175
25264
|
|
|
25176
25265
|
// src/tools/ast-grep/cli.ts
|
|
25177
25266
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
25178
|
-
import { existsSync as
|
|
25267
|
+
import { existsSync as existsSync33 } from "fs";
|
|
25179
25268
|
var resolvedCliPath3 = null;
|
|
25180
25269
|
var initPromise2 = null;
|
|
25181
25270
|
async function getAstGrepPath() {
|
|
25182
|
-
if (resolvedCliPath3 !== null &&
|
|
25271
|
+
if (resolvedCliPath3 !== null && existsSync33(resolvedCliPath3)) {
|
|
25183
25272
|
return resolvedCliPath3;
|
|
25184
25273
|
}
|
|
25185
25274
|
if (initPromise2) {
|
|
@@ -25187,7 +25276,7 @@ async function getAstGrepPath() {
|
|
|
25187
25276
|
}
|
|
25188
25277
|
initPromise2 = (async () => {
|
|
25189
25278
|
const syncPath = findSgCliPathSync();
|
|
25190
|
-
if (syncPath &&
|
|
25279
|
+
if (syncPath && existsSync33(syncPath)) {
|
|
25191
25280
|
resolvedCliPath3 = syncPath;
|
|
25192
25281
|
setSgCliPath(syncPath);
|
|
25193
25282
|
return syncPath;
|
|
@@ -25221,7 +25310,7 @@ async function runSg(options) {
|
|
|
25221
25310
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
25222
25311
|
args.push(...paths);
|
|
25223
25312
|
let cliPath = getSgCliPath();
|
|
25224
|
-
if (!
|
|
25313
|
+
if (!existsSync33(cliPath) && cliPath !== "sg") {
|
|
25225
25314
|
const downloadedPath = await getAstGrepPath();
|
|
25226
25315
|
if (downloadedPath) {
|
|
25227
25316
|
cliPath = downloadedPath;
|
|
@@ -25485,24 +25574,24 @@ var ast_grep_replace = tool({
|
|
|
25485
25574
|
var {spawn: spawn7 } = globalThis.Bun;
|
|
25486
25575
|
|
|
25487
25576
|
// src/tools/grep/constants.ts
|
|
25488
|
-
import { existsSync as
|
|
25489
|
-
import { join as
|
|
25577
|
+
import { existsSync as existsSync35 } from "fs";
|
|
25578
|
+
import { join as join42, dirname as dirname7 } from "path";
|
|
25490
25579
|
import { spawnSync } from "child_process";
|
|
25491
25580
|
|
|
25492
25581
|
// src/tools/grep/downloader.ts
|
|
25493
|
-
import { existsSync as
|
|
25494
|
-
import { join as
|
|
25582
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync11, chmodSync as chmodSync3, unlinkSync as unlinkSync10, readdirSync as readdirSync10 } from "fs";
|
|
25583
|
+
import { join as join41 } from "path";
|
|
25495
25584
|
function getInstallDir() {
|
|
25496
25585
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
25497
|
-
return
|
|
25586
|
+
return join41(homeDir, ".cache", "oh-my-opencode", "bin");
|
|
25498
25587
|
}
|
|
25499
25588
|
function getRgPath() {
|
|
25500
25589
|
const isWindows2 = process.platform === "win32";
|
|
25501
|
-
return
|
|
25590
|
+
return join41(getInstallDir(), isWindows2 ? "rg.exe" : "rg");
|
|
25502
25591
|
}
|
|
25503
25592
|
function getInstalledRipgrepPath() {
|
|
25504
25593
|
const rgPath = getRgPath();
|
|
25505
|
-
return
|
|
25594
|
+
return existsSync34(rgPath) ? rgPath : null;
|
|
25506
25595
|
}
|
|
25507
25596
|
|
|
25508
25597
|
// src/tools/grep/constants.ts
|
|
@@ -25525,13 +25614,13 @@ function getOpenCodeBundledRg() {
|
|
|
25525
25614
|
const isWindows2 = process.platform === "win32";
|
|
25526
25615
|
const rgName = isWindows2 ? "rg.exe" : "rg";
|
|
25527
25616
|
const candidates = [
|
|
25528
|
-
|
|
25529
|
-
|
|
25530
|
-
|
|
25531
|
-
|
|
25617
|
+
join42(execDir, rgName),
|
|
25618
|
+
join42(execDir, "bin", rgName),
|
|
25619
|
+
join42(execDir, "..", "bin", rgName),
|
|
25620
|
+
join42(execDir, "..", "libexec", rgName)
|
|
25532
25621
|
];
|
|
25533
25622
|
for (const candidate of candidates) {
|
|
25534
|
-
if (
|
|
25623
|
+
if (existsSync35(candidate)) {
|
|
25535
25624
|
return candidate;
|
|
25536
25625
|
}
|
|
25537
25626
|
}
|
|
@@ -25934,19 +26023,19 @@ var glob = tool({
|
|
|
25934
26023
|
}
|
|
25935
26024
|
});
|
|
25936
26025
|
// src/tools/slashcommand/tools.ts
|
|
25937
|
-
import { existsSync as
|
|
26026
|
+
import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync21 } from "fs";
|
|
25938
26027
|
import { homedir as homedir18 } from "os";
|
|
25939
|
-
import { join as
|
|
26028
|
+
import { join as join43, basename as basename3, dirname as dirname8 } from "path";
|
|
25940
26029
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
25941
|
-
if (!
|
|
26030
|
+
if (!existsSync36(commandsDir)) {
|
|
25942
26031
|
return [];
|
|
25943
26032
|
}
|
|
25944
|
-
const entries =
|
|
26033
|
+
const entries = readdirSync11(commandsDir, { withFileTypes: true });
|
|
25945
26034
|
const commands = [];
|
|
25946
26035
|
for (const entry of entries) {
|
|
25947
26036
|
if (!isMarkdownFile(entry))
|
|
25948
26037
|
continue;
|
|
25949
|
-
const commandPath =
|
|
26038
|
+
const commandPath = join43(commandsDir, entry.name);
|
|
25950
26039
|
const commandName = basename3(entry.name, ".md");
|
|
25951
26040
|
try {
|
|
25952
26041
|
const content = readFileSync21(commandPath, "utf-8");
|
|
@@ -25974,10 +26063,10 @@ function discoverCommandsFromDir(commandsDir, scope) {
|
|
|
25974
26063
|
return commands;
|
|
25975
26064
|
}
|
|
25976
26065
|
function discoverCommandsSync() {
|
|
25977
|
-
const userCommandsDir =
|
|
25978
|
-
const projectCommandsDir =
|
|
25979
|
-
const opencodeGlobalDir =
|
|
25980
|
-
const opencodeProjectDir =
|
|
26066
|
+
const userCommandsDir = join43(homedir18(), ".claude", "commands");
|
|
26067
|
+
const projectCommandsDir = join43(process.cwd(), ".claude", "commands");
|
|
26068
|
+
const opencodeGlobalDir = join43(homedir18(), ".config", "opencode", "command");
|
|
26069
|
+
const opencodeProjectDir = join43(process.cwd(), ".opencode", "command");
|
|
25981
26070
|
const userCommands = discoverCommandsFromDir(userCommandsDir, "user");
|
|
25982
26071
|
const opencodeGlobalCommands = discoverCommandsFromDir(opencodeGlobalDir, "opencode");
|
|
25983
26072
|
const projectCommands = discoverCommandsFromDir(projectCommandsDir, "project");
|
|
@@ -26100,246 +26189,6 @@ Provide a command name to execute.`;
|
|
|
26100
26189
|
Try a different command name.`;
|
|
26101
26190
|
}
|
|
26102
26191
|
});
|
|
26103
|
-
// src/tools/skill/types.ts
|
|
26104
|
-
var SkillFrontmatterSchema = exports_external.object({
|
|
26105
|
-
name: exports_external.string().regex(/^[a-z0-9-]+$/, "Name must be lowercase alphanumeric with hyphens only").min(1, "Name cannot be empty"),
|
|
26106
|
-
description: exports_external.string().min(20, "Description must be at least 20 characters for discoverability"),
|
|
26107
|
-
license: exports_external.string().optional(),
|
|
26108
|
-
"allowed-tools": exports_external.array(exports_external.string()).optional(),
|
|
26109
|
-
metadata: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
26110
|
-
});
|
|
26111
|
-
// src/tools/skill/tools.ts
|
|
26112
|
-
import { existsSync as existsSync36, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
|
|
26113
|
-
import { homedir as homedir19 } from "os";
|
|
26114
|
-
import { join as join43, basename as basename4 } from "path";
|
|
26115
|
-
function parseSkillFrontmatter(data) {
|
|
26116
|
-
return {
|
|
26117
|
-
name: typeof data.name === "string" ? data.name : "",
|
|
26118
|
-
description: typeof data.description === "string" ? data.description : "",
|
|
26119
|
-
license: typeof data.license === "string" ? data.license : undefined,
|
|
26120
|
-
"allowed-tools": Array.isArray(data["allowed-tools"]) ? data["allowed-tools"] : undefined,
|
|
26121
|
-
metadata: typeof data.metadata === "object" && data.metadata !== null ? data.metadata : undefined
|
|
26122
|
-
};
|
|
26123
|
-
}
|
|
26124
|
-
function discoverSkillsFromDir(skillsDir, scope) {
|
|
26125
|
-
if (!existsSync36(skillsDir)) {
|
|
26126
|
-
return [];
|
|
26127
|
-
}
|
|
26128
|
-
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
26129
|
-
const skills = [];
|
|
26130
|
-
for (const entry of entries) {
|
|
26131
|
-
if (entry.name.startsWith("."))
|
|
26132
|
-
continue;
|
|
26133
|
-
const skillPath = join43(skillsDir, entry.name);
|
|
26134
|
-
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
26135
|
-
const resolvedPath = resolveSymlink(skillPath);
|
|
26136
|
-
const skillMdPath = join43(resolvedPath, "SKILL.md");
|
|
26137
|
-
if (!existsSync36(skillMdPath))
|
|
26138
|
-
continue;
|
|
26139
|
-
try {
|
|
26140
|
-
const content = readFileSync22(skillMdPath, "utf-8");
|
|
26141
|
-
const { data } = parseFrontmatter(content);
|
|
26142
|
-
skills.push({
|
|
26143
|
-
name: data.name || entry.name,
|
|
26144
|
-
description: data.description || "",
|
|
26145
|
-
scope
|
|
26146
|
-
});
|
|
26147
|
-
} catch {
|
|
26148
|
-
continue;
|
|
26149
|
-
}
|
|
26150
|
-
}
|
|
26151
|
-
}
|
|
26152
|
-
return skills;
|
|
26153
|
-
}
|
|
26154
|
-
function discoverSkillsSync() {
|
|
26155
|
-
const userSkillsDir = join43(homedir19(), ".claude", "skills");
|
|
26156
|
-
const projectSkillsDir = join43(process.cwd(), ".claude", "skills");
|
|
26157
|
-
const userSkills = discoverSkillsFromDir(userSkillsDir, "user");
|
|
26158
|
-
const projectSkills = discoverSkillsFromDir(projectSkillsDir, "project");
|
|
26159
|
-
return [...projectSkills, ...userSkills];
|
|
26160
|
-
}
|
|
26161
|
-
var availableSkills = discoverSkillsSync();
|
|
26162
|
-
var skillListForDescription = availableSkills.map((s) => `- ${s.name}: ${s.description} (${s.scope})`).join(`
|
|
26163
|
-
`);
|
|
26164
|
-
async function parseSkillMd(skillPath) {
|
|
26165
|
-
const resolvedPath = resolveSymlink(skillPath);
|
|
26166
|
-
const skillMdPath = join43(resolvedPath, "SKILL.md");
|
|
26167
|
-
if (!existsSync36(skillMdPath)) {
|
|
26168
|
-
return null;
|
|
26169
|
-
}
|
|
26170
|
-
try {
|
|
26171
|
-
let content = readFileSync22(skillMdPath, "utf-8");
|
|
26172
|
-
content = await resolveCommandsInText(content);
|
|
26173
|
-
const { data, body } = parseFrontmatter(content);
|
|
26174
|
-
const frontmatter2 = parseSkillFrontmatter(data);
|
|
26175
|
-
const metadata = {
|
|
26176
|
-
name: frontmatter2.name || basename4(skillPath),
|
|
26177
|
-
description: frontmatter2.description,
|
|
26178
|
-
license: frontmatter2.license,
|
|
26179
|
-
allowedTools: frontmatter2["allowed-tools"],
|
|
26180
|
-
metadata: frontmatter2.metadata
|
|
26181
|
-
};
|
|
26182
|
-
const referencesDir = join43(resolvedPath, "references");
|
|
26183
|
-
const scriptsDir = join43(resolvedPath, "scripts");
|
|
26184
|
-
const assetsDir = join43(resolvedPath, "assets");
|
|
26185
|
-
const references = existsSync36(referencesDir) ? readdirSync11(referencesDir).filter((f) => !f.startsWith(".")) : [];
|
|
26186
|
-
const scripts = existsSync36(scriptsDir) ? readdirSync11(scriptsDir).filter((f) => !f.startsWith(".") && !f.startsWith("__")) : [];
|
|
26187
|
-
const assets = existsSync36(assetsDir) ? readdirSync11(assetsDir).filter((f) => !f.startsWith(".")) : [];
|
|
26188
|
-
return {
|
|
26189
|
-
name: metadata.name,
|
|
26190
|
-
path: resolvedPath,
|
|
26191
|
-
basePath: resolvedPath,
|
|
26192
|
-
metadata,
|
|
26193
|
-
content: body,
|
|
26194
|
-
references,
|
|
26195
|
-
scripts,
|
|
26196
|
-
assets
|
|
26197
|
-
};
|
|
26198
|
-
} catch {
|
|
26199
|
-
return null;
|
|
26200
|
-
}
|
|
26201
|
-
}
|
|
26202
|
-
async function discoverSkillsFromDirAsync(skillsDir) {
|
|
26203
|
-
if (!existsSync36(skillsDir)) {
|
|
26204
|
-
return [];
|
|
26205
|
-
}
|
|
26206
|
-
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
26207
|
-
const skills = [];
|
|
26208
|
-
for (const entry of entries) {
|
|
26209
|
-
if (entry.name.startsWith("."))
|
|
26210
|
-
continue;
|
|
26211
|
-
const skillPath = join43(skillsDir, entry.name);
|
|
26212
|
-
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
26213
|
-
const skillInfo = await parseSkillMd(skillPath);
|
|
26214
|
-
if (skillInfo) {
|
|
26215
|
-
skills.push(skillInfo);
|
|
26216
|
-
}
|
|
26217
|
-
}
|
|
26218
|
-
}
|
|
26219
|
-
return skills;
|
|
26220
|
-
}
|
|
26221
|
-
async function discoverSkills() {
|
|
26222
|
-
const userSkillsDir = join43(homedir19(), ".claude", "skills");
|
|
26223
|
-
const projectSkillsDir = join43(process.cwd(), ".claude", "skills");
|
|
26224
|
-
const userSkills = await discoverSkillsFromDirAsync(userSkillsDir);
|
|
26225
|
-
const projectSkills = await discoverSkillsFromDirAsync(projectSkillsDir);
|
|
26226
|
-
return [...projectSkills, ...userSkills];
|
|
26227
|
-
}
|
|
26228
|
-
function findMatchingSkills(skills, query) {
|
|
26229
|
-
const queryLower = query.toLowerCase();
|
|
26230
|
-
const queryTerms = queryLower.split(/\s+/).filter(Boolean);
|
|
26231
|
-
return skills.map((skill) => {
|
|
26232
|
-
let score = 0;
|
|
26233
|
-
const nameLower = skill.metadata.name.toLowerCase();
|
|
26234
|
-
const descLower = skill.metadata.description.toLowerCase();
|
|
26235
|
-
if (nameLower === queryLower)
|
|
26236
|
-
score += 100;
|
|
26237
|
-
if (nameLower.includes(queryLower))
|
|
26238
|
-
score += 50;
|
|
26239
|
-
for (const term of queryTerms) {
|
|
26240
|
-
if (nameLower.includes(term))
|
|
26241
|
-
score += 20;
|
|
26242
|
-
if (descLower.includes(term))
|
|
26243
|
-
score += 10;
|
|
26244
|
-
}
|
|
26245
|
-
return { skill, score };
|
|
26246
|
-
}).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score).map(({ skill }) => skill);
|
|
26247
|
-
}
|
|
26248
|
-
async function loadSkillWithReferences(skill, includeRefs) {
|
|
26249
|
-
const referencesLoaded = [];
|
|
26250
|
-
if (includeRefs && skill.references.length > 0) {
|
|
26251
|
-
for (const ref of skill.references) {
|
|
26252
|
-
const refPath = join43(skill.path, "references", ref);
|
|
26253
|
-
try {
|
|
26254
|
-
let content = readFileSync22(refPath, "utf-8");
|
|
26255
|
-
content = await resolveCommandsInText(content);
|
|
26256
|
-
referencesLoaded.push({ path: ref, content });
|
|
26257
|
-
} catch {}
|
|
26258
|
-
}
|
|
26259
|
-
}
|
|
26260
|
-
return {
|
|
26261
|
-
name: skill.name,
|
|
26262
|
-
metadata: skill.metadata,
|
|
26263
|
-
basePath: skill.basePath,
|
|
26264
|
-
body: skill.content,
|
|
26265
|
-
referencesLoaded
|
|
26266
|
-
};
|
|
26267
|
-
}
|
|
26268
|
-
function formatSkillList(skills) {
|
|
26269
|
-
if (skills.length === 0) {
|
|
26270
|
-
return "No skills found in ~/.claude/skills/";
|
|
26271
|
-
}
|
|
26272
|
-
const lines = [`# Available Skills
|
|
26273
|
-
`];
|
|
26274
|
-
for (const skill of skills) {
|
|
26275
|
-
lines.push(`- **${skill.metadata.name}**: ${skill.metadata.description || "(no description)"}`);
|
|
26276
|
-
}
|
|
26277
|
-
lines.push(`
|
|
26278
|
-
**Total**: ${skills.length} skills`);
|
|
26279
|
-
return lines.join(`
|
|
26280
|
-
`);
|
|
26281
|
-
}
|
|
26282
|
-
function formatLoadedSkills(loadedSkills) {
|
|
26283
|
-
if (loadedSkills.length === 0) {
|
|
26284
|
-
return "No skills loaded.";
|
|
26285
|
-
}
|
|
26286
|
-
const skill = loadedSkills[0];
|
|
26287
|
-
const sections = [];
|
|
26288
|
-
sections.push(`Base directory for this skill: ${skill.basePath}/`);
|
|
26289
|
-
sections.push("");
|
|
26290
|
-
sections.push(skill.body.trim());
|
|
26291
|
-
if (skill.referencesLoaded.length > 0) {
|
|
26292
|
-
sections.push(`
|
|
26293
|
-
---
|
|
26294
|
-
### Loaded References
|
|
26295
|
-
`);
|
|
26296
|
-
for (const ref of skill.referencesLoaded) {
|
|
26297
|
-
sections.push(`#### ${ref.path}
|
|
26298
|
-
`);
|
|
26299
|
-
sections.push("```");
|
|
26300
|
-
sections.push(ref.content.trim());
|
|
26301
|
-
sections.push("```\n");
|
|
26302
|
-
}
|
|
26303
|
-
}
|
|
26304
|
-
sections.push(`
|
|
26305
|
-
---
|
|
26306
|
-
**Launched skill**: ${skill.metadata.name}`);
|
|
26307
|
-
return sections.join(`
|
|
26308
|
-
`);
|
|
26309
|
-
}
|
|
26310
|
-
var skill = tool({
|
|
26311
|
-
description: `Execute a skill within the main conversation.
|
|
26312
|
-
|
|
26313
|
-
When you invoke a skill, the skill's prompt will expand and provide detailed instructions on how to complete the task.
|
|
26314
|
-
|
|
26315
|
-
Available Skills:
|
|
26316
|
-
${skillListForDescription}`,
|
|
26317
|
-
args: {
|
|
26318
|
-
skill: tool.schema.string().describe("The skill name or search query to find and load. Can be exact skill name (e.g., 'python-programmer') or keywords (e.g., 'python', 'plan').")
|
|
26319
|
-
},
|
|
26320
|
-
async execute(args) {
|
|
26321
|
-
const skills = await discoverSkills();
|
|
26322
|
-
if (!args.skill) {
|
|
26323
|
-
return formatSkillList(skills) + `
|
|
26324
|
-
|
|
26325
|
-
Provide a skill name to load.`;
|
|
26326
|
-
}
|
|
26327
|
-
const matchingSkills = findMatchingSkills(skills, args.skill);
|
|
26328
|
-
if (matchingSkills.length === 0) {
|
|
26329
|
-
return `No skills found matching "${args.skill}".
|
|
26330
|
-
|
|
26331
|
-
` + formatSkillList(skills) + `
|
|
26332
|
-
|
|
26333
|
-
Try a different skill name.`;
|
|
26334
|
-
}
|
|
26335
|
-
const loadedSkills = [];
|
|
26336
|
-
for (const skillInfo of matchingSkills.slice(0, 3)) {
|
|
26337
|
-
const loaded = await loadSkillWithReferences(skillInfo, true);
|
|
26338
|
-
loadedSkills.push(loaded);
|
|
26339
|
-
}
|
|
26340
|
-
return formatLoadedSkills(loadedSkills);
|
|
26341
|
-
}
|
|
26342
|
-
});
|
|
26343
26192
|
// src/tools/interactive-bash/constants.ts
|
|
26344
26193
|
var DEFAULT_TIMEOUT_MS4 = 60000;
|
|
26345
26194
|
var BLOCKED_TMUX_SUBCOMMANDS = [
|
|
@@ -26920,6 +26769,30 @@ session_id: ${sessionID}
|
|
|
26920
26769
|
var MULTIMODAL_LOOKER_AGENT = "multimodal-looker";
|
|
26921
26770
|
var LOOK_AT_DESCRIPTION = `Analyze media files (PDFs, images, diagrams) via Gemini 2.5 Flash in separate context. Saves main context tokens.`;
|
|
26922
26771
|
// src/tools/look-at/tools.ts
|
|
26772
|
+
import { extname as extname3, basename as basename4 } from "path";
|
|
26773
|
+
function inferMimeType(filePath) {
|
|
26774
|
+
const ext = extname3(filePath).toLowerCase();
|
|
26775
|
+
const mimeTypes = {
|
|
26776
|
+
".jpg": "image/jpeg",
|
|
26777
|
+
".jpeg": "image/jpeg",
|
|
26778
|
+
".png": "image/png",
|
|
26779
|
+
".gif": "image/gif",
|
|
26780
|
+
".webp": "image/webp",
|
|
26781
|
+
".svg": "image/svg+xml",
|
|
26782
|
+
".bmp": "image/bmp",
|
|
26783
|
+
".ico": "image/x-icon",
|
|
26784
|
+
".pdf": "application/pdf",
|
|
26785
|
+
".txt": "text/plain",
|
|
26786
|
+
".md": "text/markdown",
|
|
26787
|
+
".json": "application/json",
|
|
26788
|
+
".xml": "application/xml",
|
|
26789
|
+
".html": "text/html",
|
|
26790
|
+
".css": "text/css",
|
|
26791
|
+
".js": "text/javascript",
|
|
26792
|
+
".ts": "text/typescript"
|
|
26793
|
+
};
|
|
26794
|
+
return mimeTypes[ext] || "application/octet-stream";
|
|
26795
|
+
}
|
|
26923
26796
|
function createLookAt(ctx) {
|
|
26924
26797
|
return tool({
|
|
26925
26798
|
description: LOOK_AT_DESCRIPTION,
|
|
@@ -26929,12 +26802,13 @@ function createLookAt(ctx) {
|
|
|
26929
26802
|
},
|
|
26930
26803
|
async execute(args, toolContext) {
|
|
26931
26804
|
log(`[look_at] Analyzing file: ${args.file_path}, goal: ${args.goal}`);
|
|
26805
|
+
const mimeType = inferMimeType(args.file_path);
|
|
26806
|
+
const filename = basename4(args.file_path);
|
|
26932
26807
|
const prompt = `Analyze this file and extract the requested information.
|
|
26933
26808
|
|
|
26934
|
-
File path: ${args.file_path}
|
|
26935
26809
|
Goal: ${args.goal}
|
|
26936
26810
|
|
|
26937
|
-
|
|
26811
|
+
Provide ONLY the extracted information that matches the goal.
|
|
26938
26812
|
Be thorough on what was requested, concise on everything else.
|
|
26939
26813
|
If the requested information is not found, clearly state what is missing.`;
|
|
26940
26814
|
log(`[look_at] Creating session with parent: ${toolContext.sessionID}`);
|
|
@@ -26950,7 +26824,7 @@ If the requested information is not found, clearly state what is missing.`;
|
|
|
26950
26824
|
}
|
|
26951
26825
|
const sessionID = createResult.data.id;
|
|
26952
26826
|
log(`[look_at] Created session: ${sessionID}`);
|
|
26953
|
-
log(`[look_at] Sending prompt to session ${sessionID}`);
|
|
26827
|
+
log(`[look_at] Sending prompt with file passthrough to session ${sessionID}`);
|
|
26954
26828
|
await ctx.client.session.prompt({
|
|
26955
26829
|
path: { id: sessionID },
|
|
26956
26830
|
body: {
|
|
@@ -26958,9 +26832,13 @@ If the requested information is not found, clearly state what is missing.`;
|
|
|
26958
26832
|
tools: {
|
|
26959
26833
|
task: false,
|
|
26960
26834
|
call_omo_agent: false,
|
|
26961
|
-
look_at: false
|
|
26835
|
+
look_at: false,
|
|
26836
|
+
read: false
|
|
26962
26837
|
},
|
|
26963
|
-
parts: [
|
|
26838
|
+
parts: [
|
|
26839
|
+
{ type: "text", text: prompt },
|
|
26840
|
+
{ type: "file", mime: mimeType, url: `file://${args.file_path}`, filename }
|
|
26841
|
+
]
|
|
26964
26842
|
}
|
|
26965
26843
|
});
|
|
26966
26844
|
log(`[look_at] Prompt sent, fetching messages...`);
|
|
@@ -27011,13 +26889,12 @@ var builtinTools = {
|
|
|
27011
26889
|
ast_grep_replace,
|
|
27012
26890
|
grep,
|
|
27013
26891
|
glob,
|
|
27014
|
-
slashcommand
|
|
27015
|
-
skill
|
|
26892
|
+
slashcommand
|
|
27016
26893
|
};
|
|
27017
26894
|
// src/features/background-agent/manager.ts
|
|
27018
26895
|
import { existsSync as existsSync37, readdirSync as readdirSync12 } from "fs";
|
|
27019
26896
|
import { join as join44 } from "path";
|
|
27020
|
-
function
|
|
26897
|
+
function getMessageDir5(sessionID) {
|
|
27021
26898
|
if (!existsSync37(MESSAGE_STORAGE))
|
|
27022
26899
|
return null;
|
|
27023
26900
|
const directPath = join44(MESSAGE_STORAGE, sessionID);
|
|
@@ -27261,7 +27138,7 @@ class BackgroundManager {
|
|
|
27261
27138
|
log("[background-agent] Sending notification to parent session:", { parentSessionID: task.parentSessionID });
|
|
27262
27139
|
setTimeout(async () => {
|
|
27263
27140
|
try {
|
|
27264
|
-
const messageDir =
|
|
27141
|
+
const messageDir = getMessageDir5(task.parentSessionID);
|
|
27265
27142
|
const prevMessage = messageDir ? findNearestMessageWithFields(messageDir) : null;
|
|
27266
27143
|
await this.client.session.prompt({
|
|
27267
27144
|
path: { id: task.parentSessionID },
|