oh-my-opencode 2.12.2 → 2.12.3
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 +32 -7
- package/README.ko.md +32 -7
- package/README.md +17 -8
- package/README.zh-cn.md +32 -7
- package/dist/agents/sisyphus-prompt-builder.d.ts +1 -0
- package/dist/cli/config-manager.d.ts +9 -0
- package/dist/cli/index.js +185 -81
- package/dist/features/claude-code-command-loader/loader.d.ts +5 -0
- package/dist/features/context-injector/collector.d.ts +11 -0
- package/dist/features/context-injector/collector.test.d.ts +1 -0
- package/dist/features/context-injector/index.d.ts +3 -0
- package/dist/features/context-injector/injector.d.ts +39 -0
- package/dist/features/context-injector/injector.test.d.ts +1 -0
- package/dist/features/context-injector/types.d.ts +83 -0
- package/dist/features/opencode-skill-loader/async-loader.d.ts +6 -0
- package/dist/features/opencode-skill-loader/async-loader.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/blocking.d.ts +2 -0
- package/dist/features/opencode-skill-loader/blocking.test.d.ts +1 -0
- package/dist/features/opencode-skill-loader/discover-worker.d.ts +1 -0
- package/dist/features/opencode-skill-loader/loader.d.ts +5 -0
- package/dist/features/opencode-skill-loader/types.d.ts +6 -0
- package/dist/features/skill-mcp-manager/env-cleaner.d.ts +2 -0
- package/dist/features/skill-mcp-manager/env-cleaner.test.d.ts +1 -0
- package/dist/features/skill-mcp-manager/manager.d.ts +8 -0
- package/dist/hooks/ralph-loop/types.d.ts +1 -0
- package/dist/hooks/tool-output-truncator.test.d.ts +1 -0
- package/dist/index.js +1143 -444
- package/dist/shared/frontmatter.d.ts +2 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/opencode-config-dir.d.ts +19 -0
- package/dist/shared/opencode-config-dir.test.d.ts +1 -0
- package/dist/shared/opencode-version.d.ts +10 -0
- package/dist/shared/opencode-version.test.d.ts +1 -0
- package/dist/shared/permission-compat.d.ts +12 -0
- package/dist/shared/permission-compat.test.d.ts +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -549,7 +549,7 @@ var require_scan = __commonJS((exports, module) => {
|
|
|
549
549
|
|
|
550
550
|
// node_modules/picomatch/lib/parse.js
|
|
551
551
|
var require_parse = __commonJS((exports, module) => {
|
|
552
|
-
var
|
|
552
|
+
var constants2 = require_constants();
|
|
553
553
|
var utils = require_utils();
|
|
554
554
|
var {
|
|
555
555
|
MAX_LENGTH,
|
|
@@ -557,7 +557,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
557
557
|
REGEX_NON_SPECIAL_CHARS,
|
|
558
558
|
REGEX_SPECIAL_CHARS_BACKREF,
|
|
559
559
|
REPLACEMENTS
|
|
560
|
-
} =
|
|
560
|
+
} = constants2;
|
|
561
561
|
var expandRange = (args, options) => {
|
|
562
562
|
if (typeof options.expandRange === "function") {
|
|
563
563
|
return options.expandRange(...args, options);
|
|
@@ -588,8 +588,8 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
588
588
|
const bos = { type: "bos", value: "", output: opts.prepend || "" };
|
|
589
589
|
const tokens = [bos];
|
|
590
590
|
const capture = opts.capture ? "" : "?:";
|
|
591
|
-
const PLATFORM_CHARS =
|
|
592
|
-
const EXTGLOB_CHARS =
|
|
591
|
+
const PLATFORM_CHARS = constants2.globChars(opts.windows);
|
|
592
|
+
const EXTGLOB_CHARS = constants2.extglobChars(PLATFORM_CHARS);
|
|
593
593
|
const {
|
|
594
594
|
DOT_LITERAL,
|
|
595
595
|
PLUS_LITERAL,
|
|
@@ -1267,7 +1267,7 @@ var require_parse = __commonJS((exports, module) => {
|
|
|
1267
1267
|
NO_DOTS_SLASH,
|
|
1268
1268
|
STAR,
|
|
1269
1269
|
START_ANCHOR
|
|
1270
|
-
} =
|
|
1270
|
+
} = constants2.globChars(opts.windows);
|
|
1271
1271
|
const nodot = opts.dot ? NO_DOTS : NO_DOT;
|
|
1272
1272
|
const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
|
|
1273
1273
|
const capture = opts.capture ? "" : "?:";
|
|
@@ -1325,7 +1325,7 @@ var require_picomatch = __commonJS((exports, module) => {
|
|
|
1325
1325
|
var scan = require_scan();
|
|
1326
1326
|
var parse3 = require_parse();
|
|
1327
1327
|
var utils = require_utils();
|
|
1328
|
-
var
|
|
1328
|
+
var constants2 = require_constants();
|
|
1329
1329
|
var isObject2 = (val) => val && typeof val === "object" && !Array.isArray(val);
|
|
1330
1330
|
var picomatch = (glob, options, returnState = false) => {
|
|
1331
1331
|
if (Array.isArray(glob)) {
|
|
@@ -1456,7 +1456,7 @@ var require_picomatch = __commonJS((exports, module) => {
|
|
|
1456
1456
|
return /$^/;
|
|
1457
1457
|
}
|
|
1458
1458
|
};
|
|
1459
|
-
picomatch.constants =
|
|
1459
|
+
picomatch.constants = constants2;
|
|
1460
1460
|
module.exports = picomatch;
|
|
1461
1461
|
});
|
|
1462
1462
|
|
|
@@ -7925,12 +7925,12 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
7925
7925
|
throw new Error(`Unknown format "${name}"`);
|
|
7926
7926
|
return f;
|
|
7927
7927
|
};
|
|
7928
|
-
function addFormats(ajv, list,
|
|
7928
|
+
function addFormats(ajv, list, fs10, exportName) {
|
|
7929
7929
|
var _a;
|
|
7930
7930
|
var _b;
|
|
7931
7931
|
(_a = (_b = ajv.opts.code).formats) !== null && _a !== undefined || (_b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`);
|
|
7932
7932
|
for (const f of list)
|
|
7933
|
-
ajv.addFormat(f,
|
|
7933
|
+
ajv.addFormat(f, fs10[f]);
|
|
7934
7934
|
}
|
|
7935
7935
|
module.exports = exports = formatsPlugin;
|
|
7936
7936
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -7941,7 +7941,7 @@ var require_dist = __commonJS((exports, module) => {
|
|
|
7941
7941
|
var require_windows = __commonJS((exports, module) => {
|
|
7942
7942
|
module.exports = isexe;
|
|
7943
7943
|
isexe.sync = sync;
|
|
7944
|
-
var
|
|
7944
|
+
var fs10 = __require("fs");
|
|
7945
7945
|
function checkPathExt(path7, options) {
|
|
7946
7946
|
var pathext = options.pathExt !== undefined ? options.pathExt : process.env.PATHEXT;
|
|
7947
7947
|
if (!pathext) {
|
|
@@ -7966,12 +7966,12 @@ var require_windows = __commonJS((exports, module) => {
|
|
|
7966
7966
|
return checkPathExt(path7, options);
|
|
7967
7967
|
}
|
|
7968
7968
|
function isexe(path7, options, cb) {
|
|
7969
|
-
|
|
7969
|
+
fs10.stat(path7, function(er, stat2) {
|
|
7970
7970
|
cb(er, er ? false : checkStat(stat2, path7, options));
|
|
7971
7971
|
});
|
|
7972
7972
|
}
|
|
7973
7973
|
function sync(path7, options) {
|
|
7974
|
-
return checkStat(
|
|
7974
|
+
return checkStat(fs10.statSync(path7), path7, options);
|
|
7975
7975
|
}
|
|
7976
7976
|
});
|
|
7977
7977
|
|
|
@@ -7979,14 +7979,14 @@ var require_windows = __commonJS((exports, module) => {
|
|
|
7979
7979
|
var require_mode = __commonJS((exports, module) => {
|
|
7980
7980
|
module.exports = isexe;
|
|
7981
7981
|
isexe.sync = sync;
|
|
7982
|
-
var
|
|
7982
|
+
var fs10 = __require("fs");
|
|
7983
7983
|
function isexe(path7, options, cb) {
|
|
7984
|
-
|
|
7984
|
+
fs10.stat(path7, function(er, stat2) {
|
|
7985
7985
|
cb(er, er ? false : checkStat(stat2, options));
|
|
7986
7986
|
});
|
|
7987
7987
|
}
|
|
7988
7988
|
function sync(path7, options) {
|
|
7989
|
-
return checkStat(
|
|
7989
|
+
return checkStat(fs10.statSync(path7), options);
|
|
7990
7990
|
}
|
|
7991
7991
|
function checkStat(stat2, options) {
|
|
7992
7992
|
return stat2.isFile() && checkMode(stat2, options);
|
|
@@ -8008,7 +8008,7 @@ var require_mode = __commonJS((exports, module) => {
|
|
|
8008
8008
|
|
|
8009
8009
|
// node_modules/isexe/index.js
|
|
8010
8010
|
var require_isexe = __commonJS((exports, module) => {
|
|
8011
|
-
var
|
|
8011
|
+
var fs10 = __require("fs");
|
|
8012
8012
|
var core3;
|
|
8013
8013
|
if (process.platform === "win32" || global.TESTING_WINDOWS) {
|
|
8014
8014
|
core3 = require_windows();
|
|
@@ -8247,16 +8247,16 @@ var require_shebang_command = __commonJS((exports, module) => {
|
|
|
8247
8247
|
|
|
8248
8248
|
// node_modules/cross-spawn/lib/util/readShebang.js
|
|
8249
8249
|
var require_readShebang = __commonJS((exports, module) => {
|
|
8250
|
-
var
|
|
8250
|
+
var fs10 = __require("fs");
|
|
8251
8251
|
var shebangCommand = require_shebang_command();
|
|
8252
8252
|
function readShebang(command) {
|
|
8253
8253
|
const size = 150;
|
|
8254
8254
|
const buffer = Buffer.alloc(size);
|
|
8255
8255
|
let fd;
|
|
8256
8256
|
try {
|
|
8257
|
-
fd =
|
|
8258
|
-
|
|
8259
|
-
|
|
8257
|
+
fd = fs10.openSync(command, "r");
|
|
8258
|
+
fs10.readSync(fd, buffer, 0, size, 0);
|
|
8259
|
+
fs10.closeSync(fd);
|
|
8260
8260
|
} catch (e) {}
|
|
8261
8261
|
return shebangCommand(buffer.toString());
|
|
8262
8262
|
}
|
|
@@ -8571,29 +8571,21 @@ function getMessageDir(sessionID) {
|
|
|
8571
8571
|
}
|
|
8572
8572
|
return null;
|
|
8573
8573
|
}
|
|
8574
|
-
function isAbortError(error) {
|
|
8575
|
-
if (!error)
|
|
8576
|
-
return false;
|
|
8577
|
-
if (typeof error === "object") {
|
|
8578
|
-
const errObj = error;
|
|
8579
|
-
const name = errObj.name;
|
|
8580
|
-
const message = errObj.message?.toLowerCase() ?? "";
|
|
8581
|
-
if (name === "MessageAbortedError" || name === "AbortError")
|
|
8582
|
-
return true;
|
|
8583
|
-
if (name === "DOMException" && message.includes("abort"))
|
|
8584
|
-
return true;
|
|
8585
|
-
if (message.includes("aborted") || message.includes("cancelled") || message.includes("interrupted"))
|
|
8586
|
-
return true;
|
|
8587
|
-
}
|
|
8588
|
-
if (typeof error === "string") {
|
|
8589
|
-
const lower = error.toLowerCase();
|
|
8590
|
-
return lower.includes("abort") || lower.includes("cancel") || lower.includes("interrupt");
|
|
8591
|
-
}
|
|
8592
|
-
return false;
|
|
8593
|
-
}
|
|
8594
8574
|
function getIncompleteCount(todos) {
|
|
8595
8575
|
return todos.filter((t) => t.status !== "completed" && t.status !== "cancelled").length;
|
|
8596
8576
|
}
|
|
8577
|
+
function isLastAssistantMessageAborted(messages) {
|
|
8578
|
+
if (!messages || messages.length === 0)
|
|
8579
|
+
return false;
|
|
8580
|
+
const assistantMessages = messages.filter((m) => m.info?.role === "assistant");
|
|
8581
|
+
if (assistantMessages.length === 0)
|
|
8582
|
+
return false;
|
|
8583
|
+
const lastAssistant = assistantMessages[assistantMessages.length - 1];
|
|
8584
|
+
const errorName = lastAssistant.info?.error?.name;
|
|
8585
|
+
if (!errorName)
|
|
8586
|
+
return false;
|
|
8587
|
+
return errorName === "MessageAbortedError" || errorName === "AbortError";
|
|
8588
|
+
}
|
|
8597
8589
|
function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
8598
8590
|
const { backgroundManager } = options;
|
|
8599
8591
|
const sessions = new Map;
|
|
@@ -8726,11 +8718,8 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8726
8718
|
const sessionID = props?.sessionID;
|
|
8727
8719
|
if (!sessionID)
|
|
8728
8720
|
return;
|
|
8729
|
-
const state2 = getState(sessionID);
|
|
8730
|
-
const isAbort = isAbortError(props?.error);
|
|
8731
|
-
state2.lastEventWasAbortError = isAbort;
|
|
8732
8721
|
cancelCountdown(sessionID);
|
|
8733
|
-
log(`[${HOOK_NAME}] session.error`, { sessionID
|
|
8722
|
+
log(`[${HOOK_NAME}] session.error`, { sessionID });
|
|
8734
8723
|
return;
|
|
8735
8724
|
}
|
|
8736
8725
|
if (event.type === "session.idle") {
|
|
@@ -8750,16 +8739,24 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8750
8739
|
log(`[${HOOK_NAME}] Skipped: in recovery`, { sessionID });
|
|
8751
8740
|
return;
|
|
8752
8741
|
}
|
|
8753
|
-
if (state2.lastEventWasAbortError) {
|
|
8754
|
-
state2.lastEventWasAbortError = false;
|
|
8755
|
-
log(`[${HOOK_NAME}] Skipped: abort error immediately before idle`, { sessionID });
|
|
8756
|
-
return;
|
|
8757
|
-
}
|
|
8758
8742
|
const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((t) => t.status === "running") : false;
|
|
8759
8743
|
if (hasRunningBgTasks) {
|
|
8760
8744
|
log(`[${HOOK_NAME}] Skipped: background tasks running`, { sessionID });
|
|
8761
8745
|
return;
|
|
8762
8746
|
}
|
|
8747
|
+
try {
|
|
8748
|
+
const messagesResp = await ctx.client.session.messages({
|
|
8749
|
+
path: { id: sessionID },
|
|
8750
|
+
query: { directory: ctx.directory }
|
|
8751
|
+
});
|
|
8752
|
+
const messages = messagesResp.data ?? [];
|
|
8753
|
+
if (isLastAssistantMessageAborted(messages)) {
|
|
8754
|
+
log(`[${HOOK_NAME}] Skipped: last assistant message was aborted`, { sessionID });
|
|
8755
|
+
return;
|
|
8756
|
+
}
|
|
8757
|
+
} catch (err) {
|
|
8758
|
+
log(`[${HOOK_NAME}] Messages fetch failed, continuing`, { sessionID, error: String(err) });
|
|
8759
|
+
}
|
|
8763
8760
|
let todos = [];
|
|
8764
8761
|
try {
|
|
8765
8762
|
const response = await ctx.client.session.todo({ path: { id: sessionID } });
|
|
@@ -8786,11 +8783,8 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8786
8783
|
const role = info?.role;
|
|
8787
8784
|
if (!sessionID)
|
|
8788
8785
|
return;
|
|
8789
|
-
const state2 = sessions.get(sessionID);
|
|
8790
|
-
if (state2) {
|
|
8791
|
-
state2.lastEventWasAbortError = false;
|
|
8792
|
-
}
|
|
8793
8786
|
if (role === "user") {
|
|
8787
|
+
const state2 = sessions.get(sessionID);
|
|
8794
8788
|
if (state2?.countdownStartedAt) {
|
|
8795
8789
|
const elapsed = Date.now() - state2.countdownStartedAt;
|
|
8796
8790
|
if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
|
|
@@ -8799,7 +8793,6 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8799
8793
|
}
|
|
8800
8794
|
}
|
|
8801
8795
|
cancelCountdown(sessionID);
|
|
8802
|
-
log(`[${HOOK_NAME}] User message: cleared abort state`, { sessionID });
|
|
8803
8796
|
}
|
|
8804
8797
|
if (role === "assistant") {
|
|
8805
8798
|
cancelCountdown(sessionID);
|
|
@@ -8811,10 +8804,6 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8811
8804
|
const sessionID = info?.sessionID;
|
|
8812
8805
|
const role = info?.role;
|
|
8813
8806
|
if (sessionID && role === "assistant") {
|
|
8814
|
-
const state2 = sessions.get(sessionID);
|
|
8815
|
-
if (state2) {
|
|
8816
|
-
state2.lastEventWasAbortError = false;
|
|
8817
|
-
}
|
|
8818
8807
|
cancelCountdown(sessionID);
|
|
8819
8808
|
}
|
|
8820
8809
|
return;
|
|
@@ -8822,10 +8811,6 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8822
8811
|
if (event.type === "tool.execute.before" || event.type === "tool.execute.after") {
|
|
8823
8812
|
const sessionID = props?.sessionID;
|
|
8824
8813
|
if (sessionID) {
|
|
8825
|
-
const state2 = sessions.get(sessionID);
|
|
8826
|
-
if (state2) {
|
|
8827
|
-
state2.lastEventWasAbortError = false;
|
|
8828
|
-
}
|
|
8829
8814
|
cancelCountdown(sessionID);
|
|
8830
8815
|
}
|
|
8831
8816
|
return;
|
|
@@ -8847,7 +8832,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
8847
8832
|
}
|
|
8848
8833
|
// src/hooks/context-window-monitor.ts
|
|
8849
8834
|
var ANTHROPIC_DISPLAY_LIMIT = 1e6;
|
|
8850
|
-
var ANTHROPIC_ACTUAL_LIMIT = 200000;
|
|
8835
|
+
var ANTHROPIC_ACTUAL_LIMIT = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
|
|
8851
8836
|
var CONTEXT_WARNING_THRESHOLD = 0.7;
|
|
8852
8837
|
var CONTEXT_REMINDER = `[SYSTEM REMINDER - 1M Context Window]
|
|
8853
8838
|
|
|
@@ -10210,7 +10195,7 @@ ${result.message}`;
|
|
|
10210
10195
|
}
|
|
10211
10196
|
}
|
|
10212
10197
|
// src/shared/dynamic-truncator.ts
|
|
10213
|
-
var ANTHROPIC_ACTUAL_LIMIT2 = 200000;
|
|
10198
|
+
var ANTHROPIC_ACTUAL_LIMIT2 = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
|
|
10214
10199
|
var CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
10215
10200
|
var DEFAULT_TARGET_MAX_TOKENS = 50000;
|
|
10216
10201
|
function estimateTokens(text) {
|
|
@@ -10319,6 +10304,8 @@ function createDynamicTruncator(ctx) {
|
|
|
10319
10304
|
}
|
|
10320
10305
|
|
|
10321
10306
|
// src/hooks/tool-output-truncator.ts
|
|
10307
|
+
var DEFAULT_MAX_TOKENS = 50000;
|
|
10308
|
+
var WEBFETCH_MAX_TOKENS = 1e4;
|
|
10322
10309
|
var TRUNCATABLE_TOOLS = [
|
|
10323
10310
|
"grep",
|
|
10324
10311
|
"Grep",
|
|
@@ -10337,6 +10324,10 @@ var TRUNCATABLE_TOOLS = [
|
|
|
10337
10324
|
"webfetch",
|
|
10338
10325
|
"WebFetch"
|
|
10339
10326
|
];
|
|
10327
|
+
var TOOL_SPECIFIC_MAX_TOKENS = {
|
|
10328
|
+
webfetch: WEBFETCH_MAX_TOKENS,
|
|
10329
|
+
WebFetch: WEBFETCH_MAX_TOKENS
|
|
10330
|
+
};
|
|
10340
10331
|
function createToolOutputTruncatorHook(ctx, options) {
|
|
10341
10332
|
const truncator = createDynamicTruncator(ctx);
|
|
10342
10333
|
const truncateAll = options?.experimental?.truncate_all_tool_outputs ?? false;
|
|
@@ -10344,7 +10335,8 @@ function createToolOutputTruncatorHook(ctx, options) {
|
|
|
10344
10335
|
if (!truncateAll && !TRUNCATABLE_TOOLS.includes(input.tool))
|
|
10345
10336
|
return;
|
|
10346
10337
|
try {
|
|
10347
|
-
const
|
|
10338
|
+
const targetMaxTokens = TOOL_SPECIFIC_MAX_TOKENS[input.tool] ?? DEFAULT_MAX_TOKENS;
|
|
10339
|
+
const { result, truncated } = await truncator.truncate(input.sessionID, output.output, { targetMaxTokens });
|
|
10348
10340
|
if (truncated) {
|
|
10349
10341
|
output.output = result;
|
|
10350
10342
|
}
|
|
@@ -12077,7 +12069,7 @@ var COMPACTION_COOLDOWN_MS = 60000;
|
|
|
12077
12069
|
|
|
12078
12070
|
// src/hooks/preemptive-compaction/index.ts
|
|
12079
12071
|
var CLAUDE_MODEL_PATTERN = /claude-(opus|sonnet|haiku)/i;
|
|
12080
|
-
var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
|
|
12072
|
+
var CLAUDE_DEFAULT_CONTEXT_LIMIT = process.env.ANTHROPIC_1M_CONTEXT === "true" || process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true" ? 1e6 : 200000;
|
|
12081
12073
|
function isSupportedModel(modelID) {
|
|
12082
12074
|
return CLAUDE_MODEL_PATTERN.test(modelID);
|
|
12083
12075
|
}
|
|
@@ -12410,6 +12402,16 @@ function extractPromptText(parts) {
|
|
|
12410
12402
|
}
|
|
12411
12403
|
|
|
12412
12404
|
// src/hooks/think-mode/switcher.ts
|
|
12405
|
+
function extractModelPrefix(modelID) {
|
|
12406
|
+
const slashIndex = modelID.indexOf("/");
|
|
12407
|
+
if (slashIndex === -1) {
|
|
12408
|
+
return { prefix: "", base: modelID };
|
|
12409
|
+
}
|
|
12410
|
+
return {
|
|
12411
|
+
prefix: modelID.slice(0, slashIndex + 1),
|
|
12412
|
+
base: modelID.slice(slashIndex + 1)
|
|
12413
|
+
};
|
|
12414
|
+
}
|
|
12413
12415
|
function normalizeModelID(modelID) {
|
|
12414
12416
|
return modelID.replace(/\.(\d+)/g, "-$1");
|
|
12415
12417
|
}
|
|
@@ -12495,20 +12497,27 @@ var THINKING_CAPABLE_MODELS = {
|
|
|
12495
12497
|
};
|
|
12496
12498
|
function getHighVariant(modelID) {
|
|
12497
12499
|
const normalized = normalizeModelID(modelID);
|
|
12498
|
-
|
|
12500
|
+
const { prefix, base } = extractModelPrefix(normalized);
|
|
12501
|
+
if (ALREADY_HIGH.has(base) || base.endsWith("-high")) {
|
|
12502
|
+
return null;
|
|
12503
|
+
}
|
|
12504
|
+
const highBase = HIGH_VARIANT_MAP[base];
|
|
12505
|
+
if (!highBase) {
|
|
12499
12506
|
return null;
|
|
12500
12507
|
}
|
|
12501
|
-
return
|
|
12508
|
+
return prefix + highBase;
|
|
12502
12509
|
}
|
|
12503
12510
|
function isAlreadyHighVariant(modelID) {
|
|
12504
12511
|
const normalized = normalizeModelID(modelID);
|
|
12505
|
-
|
|
12512
|
+
const { base } = extractModelPrefix(normalized);
|
|
12513
|
+
return ALREADY_HIGH.has(base) || base.endsWith("-high");
|
|
12506
12514
|
}
|
|
12507
12515
|
function isThinkingProvider(provider) {
|
|
12508
12516
|
return provider in THINKING_CONFIGS;
|
|
12509
12517
|
}
|
|
12510
12518
|
function getThinkingConfig(providerID, modelID) {
|
|
12511
12519
|
const normalized = normalizeModelID(modelID);
|
|
12520
|
+
const { base } = extractModelPrefix(normalized);
|
|
12512
12521
|
if (isAlreadyHighVariant(normalized)) {
|
|
12513
12522
|
return null;
|
|
12514
12523
|
}
|
|
@@ -12518,8 +12527,8 @@ function getThinkingConfig(providerID, modelID) {
|
|
|
12518
12527
|
}
|
|
12519
12528
|
const config = THINKING_CONFIGS[resolvedProvider];
|
|
12520
12529
|
const capablePatterns = THINKING_CAPABLE_MODELS[resolvedProvider];
|
|
12521
|
-
const
|
|
12522
|
-
const isCapable = capablePatterns.some((pattern) =>
|
|
12530
|
+
const baseLower = base.toLowerCase();
|
|
12531
|
+
const isCapable = capablePatterns.some((pattern) => baseLower.includes(pattern.toLowerCase()));
|
|
12523
12532
|
return isCapable ? config : null;
|
|
12524
12533
|
}
|
|
12525
12534
|
|
|
@@ -15213,16 +15222,16 @@ function parseFrontmatter(content) {
|
|
|
15213
15222
|
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n?---\r?\n([\s\S]*)$/;
|
|
15214
15223
|
const match = content.match(frontmatterRegex);
|
|
15215
15224
|
if (!match) {
|
|
15216
|
-
return { data: {}, body: content };
|
|
15225
|
+
return { data: {}, body: content, hadFrontmatter: false, parseError: false };
|
|
15217
15226
|
}
|
|
15218
15227
|
const yamlContent = match[1];
|
|
15219
15228
|
const body = match[2];
|
|
15220
15229
|
try {
|
|
15221
15230
|
const parsed = jsYaml.load(yamlContent, { schema: jsYaml.JSON_SCHEMA });
|
|
15222
15231
|
const data = parsed ?? {};
|
|
15223
|
-
return { data, body };
|
|
15232
|
+
return { data, body, hadFrontmatter: true, parseError: false };
|
|
15224
15233
|
} catch {
|
|
15225
|
-
return { data: {}, body };
|
|
15234
|
+
return { data: {}, body, hadFrontmatter: true, parseError: true };
|
|
15226
15235
|
}
|
|
15227
15236
|
}
|
|
15228
15237
|
// src/shared/command-executor.ts
|
|
@@ -16535,6 +16544,167 @@ function migrateConfigFile(configPath, rawConfig) {
|
|
|
16535
16544
|
}
|
|
16536
16545
|
return needsWrite;
|
|
16537
16546
|
}
|
|
16547
|
+
// src/shared/opencode-config-dir.ts
|
|
16548
|
+
import { existsSync as existsSync21 } from "fs";
|
|
16549
|
+
import { homedir as homedir6 } from "os";
|
|
16550
|
+
import { join as join26 } from "path";
|
|
16551
|
+
var TAURI_APP_IDENTIFIER = "ai.opencode.desktop";
|
|
16552
|
+
var TAURI_APP_IDENTIFIER_DEV = "ai.opencode.desktop.dev";
|
|
16553
|
+
function isDevBuild(version) {
|
|
16554
|
+
if (!version)
|
|
16555
|
+
return false;
|
|
16556
|
+
return version.includes("-dev") || version.includes(".dev");
|
|
16557
|
+
}
|
|
16558
|
+
function getTauriConfigDir(identifier) {
|
|
16559
|
+
const platform2 = process.platform;
|
|
16560
|
+
switch (platform2) {
|
|
16561
|
+
case "darwin":
|
|
16562
|
+
return join26(homedir6(), "Library", "Application Support", identifier);
|
|
16563
|
+
case "win32": {
|
|
16564
|
+
const appData = process.env.APPDATA || join26(homedir6(), "AppData", "Roaming");
|
|
16565
|
+
return join26(appData, identifier);
|
|
16566
|
+
}
|
|
16567
|
+
case "linux":
|
|
16568
|
+
default: {
|
|
16569
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join26(homedir6(), ".config");
|
|
16570
|
+
return join26(xdgConfig, identifier);
|
|
16571
|
+
}
|
|
16572
|
+
}
|
|
16573
|
+
}
|
|
16574
|
+
function getCliConfigDir() {
|
|
16575
|
+
if (process.platform === "win32") {
|
|
16576
|
+
const crossPlatformDir = join26(homedir6(), ".config", "opencode");
|
|
16577
|
+
const crossPlatformConfig = join26(crossPlatformDir, "opencode.json");
|
|
16578
|
+
if (existsSync21(crossPlatformConfig)) {
|
|
16579
|
+
return crossPlatformDir;
|
|
16580
|
+
}
|
|
16581
|
+
const appData = process.env.APPDATA || join26(homedir6(), "AppData", "Roaming");
|
|
16582
|
+
const appdataDir = join26(appData, "opencode");
|
|
16583
|
+
const appdataConfig = join26(appdataDir, "opencode.json");
|
|
16584
|
+
if (existsSync21(appdataConfig)) {
|
|
16585
|
+
return appdataDir;
|
|
16586
|
+
}
|
|
16587
|
+
return crossPlatformDir;
|
|
16588
|
+
}
|
|
16589
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join26(homedir6(), ".config");
|
|
16590
|
+
return join26(xdgConfig, "opencode");
|
|
16591
|
+
}
|
|
16592
|
+
function getOpenCodeConfigDir(options) {
|
|
16593
|
+
const { binary: binary2, version, checkExisting = true } = options;
|
|
16594
|
+
if (binary2 === "opencode") {
|
|
16595
|
+
return getCliConfigDir();
|
|
16596
|
+
}
|
|
16597
|
+
const identifier = isDevBuild(version) ? TAURI_APP_IDENTIFIER_DEV : TAURI_APP_IDENTIFIER;
|
|
16598
|
+
const tauriDir = getTauriConfigDir(identifier);
|
|
16599
|
+
if (checkExisting) {
|
|
16600
|
+
const legacyDir = getCliConfigDir();
|
|
16601
|
+
const legacyConfig = join26(legacyDir, "opencode.json");
|
|
16602
|
+
const legacyConfigC = join26(legacyDir, "opencode.jsonc");
|
|
16603
|
+
if (existsSync21(legacyConfig) || existsSync21(legacyConfigC)) {
|
|
16604
|
+
return legacyDir;
|
|
16605
|
+
}
|
|
16606
|
+
}
|
|
16607
|
+
return tauriDir;
|
|
16608
|
+
}
|
|
16609
|
+
function getOpenCodeConfigPaths(options) {
|
|
16610
|
+
const configDir = getOpenCodeConfigDir(options);
|
|
16611
|
+
return {
|
|
16612
|
+
configDir,
|
|
16613
|
+
configJson: join26(configDir, "opencode.json"),
|
|
16614
|
+
configJsonc: join26(configDir, "opencode.jsonc"),
|
|
16615
|
+
packageJson: join26(configDir, "package.json"),
|
|
16616
|
+
omoConfig: join26(configDir, "oh-my-opencode.json")
|
|
16617
|
+
};
|
|
16618
|
+
}
|
|
16619
|
+
// src/shared/opencode-version.ts
|
|
16620
|
+
import { execSync } from "child_process";
|
|
16621
|
+
var PERMISSION_BREAKING_VERSION = "1.1.1";
|
|
16622
|
+
var NOT_CACHED = Symbol("NOT_CACHED");
|
|
16623
|
+
var cachedVersion = NOT_CACHED;
|
|
16624
|
+
function parseVersion(version) {
|
|
16625
|
+
const cleaned = version.replace(/^v/, "").split("-")[0];
|
|
16626
|
+
return cleaned.split(".").map((n) => parseInt(n, 10) || 0);
|
|
16627
|
+
}
|
|
16628
|
+
function compareVersions(a, b) {
|
|
16629
|
+
const partsA = parseVersion(a);
|
|
16630
|
+
const partsB = parseVersion(b);
|
|
16631
|
+
const maxLen = Math.max(partsA.length, partsB.length);
|
|
16632
|
+
for (let i2 = 0;i2 < maxLen; i2++) {
|
|
16633
|
+
const numA = partsA[i2] ?? 0;
|
|
16634
|
+
const numB = partsB[i2] ?? 0;
|
|
16635
|
+
if (numA < numB)
|
|
16636
|
+
return -1;
|
|
16637
|
+
if (numA > numB)
|
|
16638
|
+
return 1;
|
|
16639
|
+
}
|
|
16640
|
+
return 0;
|
|
16641
|
+
}
|
|
16642
|
+
function isVersionGte(a, b) {
|
|
16643
|
+
return compareVersions(a, b) >= 0;
|
|
16644
|
+
}
|
|
16645
|
+
function getOpenCodeVersion() {
|
|
16646
|
+
if (cachedVersion !== NOT_CACHED) {
|
|
16647
|
+
return cachedVersion;
|
|
16648
|
+
}
|
|
16649
|
+
try {
|
|
16650
|
+
const result = execSync("opencode --version", {
|
|
16651
|
+
encoding: "utf-8",
|
|
16652
|
+
timeout: 5000,
|
|
16653
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
16654
|
+
}).trim();
|
|
16655
|
+
const versionMatch = result.match(/(\d+\.\d+\.\d+(?:-[\w.]+)?)/);
|
|
16656
|
+
cachedVersion = versionMatch?.[1] ?? null;
|
|
16657
|
+
return cachedVersion;
|
|
16658
|
+
} catch {
|
|
16659
|
+
cachedVersion = null;
|
|
16660
|
+
return null;
|
|
16661
|
+
}
|
|
16662
|
+
}
|
|
16663
|
+
function supportsNewPermissionSystem() {
|
|
16664
|
+
const version = getOpenCodeVersion();
|
|
16665
|
+
if (!version)
|
|
16666
|
+
return true;
|
|
16667
|
+
return isVersionGte(version, PERMISSION_BREAKING_VERSION);
|
|
16668
|
+
}
|
|
16669
|
+
// src/shared/permission-compat.ts
|
|
16670
|
+
function createAgentToolRestrictions(denyTools) {
|
|
16671
|
+
if (supportsNewPermissionSystem()) {
|
|
16672
|
+
return {
|
|
16673
|
+
permission: Object.fromEntries(denyTools.map((tool) => [tool, "deny"]))
|
|
16674
|
+
};
|
|
16675
|
+
}
|
|
16676
|
+
return {
|
|
16677
|
+
tools: Object.fromEntries(denyTools.map((tool) => [tool, false]))
|
|
16678
|
+
};
|
|
16679
|
+
}
|
|
16680
|
+
function migrateToolsToPermission(tools) {
|
|
16681
|
+
return Object.fromEntries(Object.entries(tools).map(([key, value]) => [
|
|
16682
|
+
key,
|
|
16683
|
+
value ? "allow" : "deny"
|
|
16684
|
+
]));
|
|
16685
|
+
}
|
|
16686
|
+
function migratePermissionToTools(permission) {
|
|
16687
|
+
return Object.fromEntries(Object.entries(permission).filter(([, value]) => value !== "ask").map(([key, value]) => [key, value === "allow"]));
|
|
16688
|
+
}
|
|
16689
|
+
function migrateAgentConfig(config) {
|
|
16690
|
+
const result = { ...config };
|
|
16691
|
+
if (supportsNewPermissionSystem()) {
|
|
16692
|
+
if (result.tools && typeof result.tools === "object") {
|
|
16693
|
+
const existingPermission = result.permission || {};
|
|
16694
|
+
const migratedPermission = migrateToolsToPermission(result.tools);
|
|
16695
|
+
result.permission = { ...migratedPermission, ...existingPermission };
|
|
16696
|
+
delete result.tools;
|
|
16697
|
+
}
|
|
16698
|
+
} else {
|
|
16699
|
+
if (result.permission && typeof result.permission === "object") {
|
|
16700
|
+
const existingTools = result.tools || {};
|
|
16701
|
+
const migratedTools = migratePermissionToTools(result.permission);
|
|
16702
|
+
result.tools = { ...migratedTools, ...existingTools };
|
|
16703
|
+
delete result.permission;
|
|
16704
|
+
}
|
|
16705
|
+
}
|
|
16706
|
+
return result;
|
|
16707
|
+
}
|
|
16538
16708
|
// src/hooks/think-mode/index.ts
|
|
16539
16709
|
var thinkModeState = new Map;
|
|
16540
16710
|
function createThinkModeHook() {
|
|
@@ -16598,8 +16768,8 @@ function createThinkModeHook() {
|
|
|
16598
16768
|
};
|
|
16599
16769
|
}
|
|
16600
16770
|
// src/hooks/claude-code-hooks/config.ts
|
|
16601
|
-
import { join as
|
|
16602
|
-
import { existsSync as
|
|
16771
|
+
import { join as join27 } from "path";
|
|
16772
|
+
import { existsSync as existsSync22 } from "fs";
|
|
16603
16773
|
function normalizeHookMatcher(raw) {
|
|
16604
16774
|
return {
|
|
16605
16775
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
|
@@ -16625,11 +16795,11 @@ function normalizeHooksConfig(raw) {
|
|
|
16625
16795
|
function getClaudeSettingsPaths(customPath) {
|
|
16626
16796
|
const claudeConfigDir = getClaudeConfigDir();
|
|
16627
16797
|
const paths = [
|
|
16628
|
-
|
|
16629
|
-
|
|
16630
|
-
|
|
16798
|
+
join27(claudeConfigDir, "settings.json"),
|
|
16799
|
+
join27(process.cwd(), ".claude", "settings.json"),
|
|
16800
|
+
join27(process.cwd(), ".claude", "settings.local.json")
|
|
16631
16801
|
];
|
|
16632
|
-
if (customPath &&
|
|
16802
|
+
if (customPath && existsSync22(customPath)) {
|
|
16633
16803
|
paths.unshift(customPath);
|
|
16634
16804
|
}
|
|
16635
16805
|
return paths;
|
|
@@ -16654,7 +16824,7 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
16654
16824
|
const paths = getClaudeSettingsPaths(customSettingsPath);
|
|
16655
16825
|
let mergedConfig = {};
|
|
16656
16826
|
for (const settingsPath of paths) {
|
|
16657
|
-
if (
|
|
16827
|
+
if (existsSync22(settingsPath)) {
|
|
16658
16828
|
try {
|
|
16659
16829
|
const content = await Bun.file(settingsPath).text();
|
|
16660
16830
|
const settings = JSON.parse(content);
|
|
@@ -16671,15 +16841,15 @@ async function loadClaudeHooksConfig(customSettingsPath) {
|
|
|
16671
16841
|
}
|
|
16672
16842
|
|
|
16673
16843
|
// src/hooks/claude-code-hooks/config-loader.ts
|
|
16674
|
-
import { existsSync as
|
|
16675
|
-
import { homedir as
|
|
16676
|
-
import { join as
|
|
16677
|
-
var USER_CONFIG_PATH =
|
|
16844
|
+
import { existsSync as existsSync23 } from "fs";
|
|
16845
|
+
import { homedir as homedir7 } from "os";
|
|
16846
|
+
import { join as join28 } from "path";
|
|
16847
|
+
var USER_CONFIG_PATH = join28(homedir7(), ".config", "opencode", "opencode-cc-plugin.json");
|
|
16678
16848
|
function getProjectConfigPath() {
|
|
16679
|
-
return
|
|
16849
|
+
return join28(process.cwd(), ".opencode", "opencode-cc-plugin.json");
|
|
16680
16850
|
}
|
|
16681
16851
|
async function loadConfigFromPath(path4) {
|
|
16682
|
-
if (!
|
|
16852
|
+
if (!existsSync23(path4)) {
|
|
16683
16853
|
return null;
|
|
16684
16854
|
}
|
|
16685
16855
|
try {
|
|
@@ -16859,16 +17029,16 @@ async function executePreToolUseHooks(ctx, config, extendedConfig) {
|
|
|
16859
17029
|
}
|
|
16860
17030
|
|
|
16861
17031
|
// src/hooks/claude-code-hooks/transcript.ts
|
|
16862
|
-
import { join as
|
|
16863
|
-
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as
|
|
17032
|
+
import { join as join29 } from "path";
|
|
17033
|
+
import { mkdirSync as mkdirSync6, appendFileSync as appendFileSync5, existsSync as existsSync24, writeFileSync as writeFileSync8, unlinkSync as unlinkSync5 } from "fs";
|
|
16864
17034
|
import { tmpdir as tmpdir5 } from "os";
|
|
16865
17035
|
import { randomUUID } from "crypto";
|
|
16866
|
-
var TRANSCRIPT_DIR =
|
|
17036
|
+
var TRANSCRIPT_DIR = join29(getClaudeConfigDir(), "transcripts");
|
|
16867
17037
|
function getTranscriptPath(sessionId) {
|
|
16868
|
-
return
|
|
17038
|
+
return join29(TRANSCRIPT_DIR, `${sessionId}.jsonl`);
|
|
16869
17039
|
}
|
|
16870
17040
|
function ensureTranscriptDir() {
|
|
16871
|
-
if (!
|
|
17041
|
+
if (!existsSync24(TRANSCRIPT_DIR)) {
|
|
16872
17042
|
mkdirSync6(TRANSCRIPT_DIR, { recursive: true });
|
|
16873
17043
|
}
|
|
16874
17044
|
}
|
|
@@ -16955,7 +17125,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
16955
17125
|
}
|
|
16956
17126
|
};
|
|
16957
17127
|
entries.push(JSON.stringify(currentEntry));
|
|
16958
|
-
const tempPath =
|
|
17128
|
+
const tempPath = join29(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
16959
17129
|
writeFileSync8(tempPath, entries.join(`
|
|
16960
17130
|
`) + `
|
|
16961
17131
|
`);
|
|
@@ -16975,7 +17145,7 @@ async function buildTranscriptFromSession(client, sessionId, directory, currentT
|
|
|
16975
17145
|
]
|
|
16976
17146
|
}
|
|
16977
17147
|
};
|
|
16978
|
-
const tempPath =
|
|
17148
|
+
const tempPath = join29(tmpdir5(), `opencode-transcript-${sessionId}-${randomUUID()}.jsonl`);
|
|
16979
17149
|
writeFileSync8(tempPath, JSON.stringify(currentEntry) + `
|
|
16980
17150
|
`);
|
|
16981
17151
|
return tempPath;
|
|
@@ -17187,10 +17357,10 @@ ${USER_PROMPT_SUBMIT_TAG_CLOSE}`);
|
|
|
17187
17357
|
}
|
|
17188
17358
|
|
|
17189
17359
|
// src/hooks/claude-code-hooks/todo.ts
|
|
17190
|
-
import { join as
|
|
17191
|
-
var TODO_DIR =
|
|
17360
|
+
import { join as join30 } from "path";
|
|
17361
|
+
var TODO_DIR = join30(getClaudeConfigDir(), "todos");
|
|
17192
17362
|
function getTodoPath(sessionId) {
|
|
17193
|
-
return
|
|
17363
|
+
return join30(TODO_DIR, `${sessionId}-agent-${sessionId}.json`);
|
|
17194
17364
|
}
|
|
17195
17365
|
|
|
17196
17366
|
// src/hooks/claude-code-hooks/stop.ts
|
|
@@ -17349,6 +17519,172 @@ setInterval(() => {
|
|
|
17349
17519
|
}
|
|
17350
17520
|
}, CACHE_TTL);
|
|
17351
17521
|
|
|
17522
|
+
// src/hooks/keyword-detector/constants.ts
|
|
17523
|
+
var CODE_BLOCK_PATTERN2 = /```[\s\S]*?```/g;
|
|
17524
|
+
var INLINE_CODE_PATTERN2 = /`[^`]+`/g;
|
|
17525
|
+
var KEYWORD_DETECTORS = [
|
|
17526
|
+
{
|
|
17527
|
+
pattern: /(ultrawork|ulw)/i,
|
|
17528
|
+
message: `<ultrawork-mode>
|
|
17529
|
+
|
|
17530
|
+
## TODO IS YOUR LIFELINE (NON-NEGOTIABLE)
|
|
17531
|
+
|
|
17532
|
+
**USE TodoWrite OBSESSIVELY. This is the #1 most important tool.**
|
|
17533
|
+
|
|
17534
|
+
### TODO Rules
|
|
17535
|
+
1. **BEFORE any action**: Create TODOs FIRST. Break down into atomic, granular steps.
|
|
17536
|
+
2. **Be excessively detailed**: 10 small TODOs > 3 vague TODOs. Err on the side of too many.
|
|
17537
|
+
3. **Real-time updates**: Mark \`in_progress\` before starting, \`completed\` IMMEDIATELY after. NEVER batch.
|
|
17538
|
+
4. **One at a time**: Only ONE TODO should be \`in_progress\` at any moment.
|
|
17539
|
+
5. **Sub-tasks**: Complex TODO? Break it into sub-TODOs. Keep granularity high.
|
|
17540
|
+
6. **Questions too**: User asks a question? TODO: "Answer with evidence: [question]"
|
|
17541
|
+
|
|
17542
|
+
### Example TODO Granularity
|
|
17543
|
+
BAD: "Implement user auth"
|
|
17544
|
+
GOOD:
|
|
17545
|
+
- "Read existing auth patterns in codebase"
|
|
17546
|
+
- "Create auth schema types"
|
|
17547
|
+
- "Implement login endpoint"
|
|
17548
|
+
- "Implement token validation middleware"
|
|
17549
|
+
- "Add auth tests - login success case"
|
|
17550
|
+
- "Add auth tests - login failure case"
|
|
17551
|
+
- "Verify LSP diagnostics clean"
|
|
17552
|
+
|
|
17553
|
+
**YOUR WORK IS INVISIBLE WITHOUT TODOs. USE THEM.**
|
|
17554
|
+
|
|
17555
|
+
## TDD WORKFLOW (MANDATORY when tests exist)
|
|
17556
|
+
|
|
17557
|
+
Check for test infrastructure FIRST. If exists, follow strictly:
|
|
17558
|
+
|
|
17559
|
+
1. **RED**: Write failing test FIRST \u2192 \`bun test\` must FAIL
|
|
17560
|
+
2. **GREEN**: Write MINIMAL code to pass \u2192 \`bun test\` must PASS
|
|
17561
|
+
3. **REFACTOR**: Clean up, tests stay green \u2192 \`bun test\` still PASS
|
|
17562
|
+
4. **REPEAT**: Next test case, loop until complete
|
|
17563
|
+
|
|
17564
|
+
**NEVER write implementation before test. NEVER delete failing tests.**
|
|
17565
|
+
|
|
17566
|
+
## AGENT DEPLOYMENT
|
|
17567
|
+
|
|
17568
|
+
Fire available agents in PARALLEL via background tasks. Use explore/librarian agents liberally (multiple concurrent if needed).
|
|
17569
|
+
|
|
17570
|
+
## EVIDENCE-BASED ANSWERS
|
|
17571
|
+
|
|
17572
|
+
- Every claim: code snippet + file path + line number
|
|
17573
|
+
- No "I think..." - find and SHOW actual code
|
|
17574
|
+
- Local search fails? \u2192 librarian for external sources
|
|
17575
|
+
- **NEVER acceptable**: "I couldn't find it"
|
|
17576
|
+
|
|
17577
|
+
## ZERO TOLERANCE FOR SHORTCUTS (RIGOROUS & HONEST EXECUTION)
|
|
17578
|
+
|
|
17579
|
+
**CORE PRINCIPLE**: Execute user's ORIGINAL INTENT with maximum rigor. No shortcuts. No compromises. No matter how large the task.
|
|
17580
|
+
|
|
17581
|
+
### ABSOLUTE PROHIBITIONS
|
|
17582
|
+
| Violation | Why It's Forbidden |
|
|
17583
|
+
|-----------|-------------------|
|
|
17584
|
+
| **Mocking/Stubbing** | Never use mocks, stubs, or fake implementations unless explicitly requested. Real implementation only. |
|
|
17585
|
+
| **Scope Reduction** | Never make "demo", "skeleton", "simplified", "basic", "minimal" versions. Deliver FULL implementation. |
|
|
17586
|
+
| **Partial Completion** | Never stop at 60-80% saying "you can extend this...", "as an exercise...", "you can add...". Finish 100%. |
|
|
17587
|
+
| **Lazy Placeholders** | Never use "// TODO", "...", "etc.", "and so on" in actual code. Complete everything. |
|
|
17588
|
+
| **Assumed Shortcuts** | Never skip requirements deemed "optional" or "can be added later". All requirements are mandatory. |
|
|
17589
|
+
| **Test Deletion** | Never delete or skip failing tests. Fix the code, not the tests. |
|
|
17590
|
+
| **Evidence-Free Claims** | Never say "I think...", "probably...", "should work...". Show actual code/output. |
|
|
17591
|
+
|
|
17592
|
+
### RIGOROUS EXECUTION MANDATE
|
|
17593
|
+
1. **Parse Original Intent**: What did the user ACTUALLY want? Not what's convenient. The REAL, COMPLETE request.
|
|
17594
|
+
2. **No Task Too Large**: If the task requires 100 files, modify 100 files. If it needs 1000 lines, write 1000 lines. Size is irrelevant.
|
|
17595
|
+
3. **Honest Assessment**: If you cannot complete something, say so BEFORE starting. Don't fake completion.
|
|
17596
|
+
4. **Evidence-Based Verification**: Every claim backed by code snippets, file paths, line numbers, and actual outputs.
|
|
17597
|
+
5. **Complete Verification**: Re-read original request after completion. Check EVERY requirement was met.
|
|
17598
|
+
|
|
17599
|
+
### FAILURE RECOVERY
|
|
17600
|
+
If you realize you've taken shortcuts:
|
|
17601
|
+
1. STOP immediately
|
|
17602
|
+
2. Identify what you skipped/faked
|
|
17603
|
+
3. Create TODOs for ALL remaining work
|
|
17604
|
+
4. Execute to TRUE completion - not "good enough"
|
|
17605
|
+
|
|
17606
|
+
**THE USER ASKED FOR X. DELIVER EXACTLY X. COMPLETELY. HONESTLY. NO MATTER THE SIZE.**
|
|
17607
|
+
|
|
17608
|
+
## SUCCESS = All TODOs Done + All Requirements Met + Evidence Provided
|
|
17609
|
+
|
|
17610
|
+
</ultrawork-mode>
|
|
17611
|
+
|
|
17612
|
+
---
|
|
17613
|
+
|
|
17614
|
+
`
|
|
17615
|
+
},
|
|
17616
|
+
{
|
|
17617
|
+
pattern: /\b(search|find|locate|lookup|look\s*up|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all|\uAC80\uC0C9|\uCC3E\uC544|\uD0D0\uC0C9|\uC870\uD68C|\uC2A4\uCE94|\uC11C\uCE58|\uB4A4\uC838|\uCC3E\uAE30|\uC5B4\uB514|\uCD94\uC801|\uD0D0\uC9C0|\uCC3E\uC544\uBD10|\uCC3E\uC544\uB0B4|\uBCF4\uC5EC\uC918|\uBAA9\uB85D|\u691C\u7D22|\u63A2\u3057\u3066|\u898B\u3064\u3051\u3066|\u30B5\u30FC\u30C1|\u63A2\u7D22|\u30B9\u30AD\u30E3\u30F3|\u3069\u3053|\u767A\u898B|\u635C\u7D22|\u898B\u3064\u3051\u51FA\u3059|\u4E00\u89A7|\u641C\u7D22|\u67E5\u627E|\u5BFB\u627E|\u67E5\u8BE2|\u68C0\u7D22|\u5B9A\u4F4D|\u626B\u63CF|\u53D1\u73B0|\u5728\u54EA\u91CC|\u627E\u51FA\u6765|\u5217\u51FA|t\u00ECm ki\u1EBFm|tra c\u1EE9u|\u0111\u1ECBnh v\u1ECB|qu\u00E9t|ph\u00E1t hi\u1EC7n|truy t\u00ECm|t\u00ECm ra|\u1EDF \u0111\u00E2u|li\u1EC7t k\u00EA/i,
|
|
17618
|
+
message: `[search-mode]
|
|
17619
|
+
MAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:
|
|
17620
|
+
- explore agents (codebase patterns, file structures, ast-grep)
|
|
17621
|
+
- librarian agents (remote repos, official docs, GitHub examples)
|
|
17622
|
+
Plus direct tools: Grep, ripgrep (rg), ast-grep (sg)
|
|
17623
|
+
NEVER stop at first result - be exhaustive.`
|
|
17624
|
+
},
|
|
17625
|
+
{
|
|
17626
|
+
pattern: /\b(analyze|analyse|investigate|examine|research|study|deep[\s-]?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to|\uBD84\uC11D|\uC870\uC0AC|\uD30C\uC545|\uC5F0\uAD6C|\uAC80\uD1A0|\uC9C4\uB2E8|\uC774\uD574|\uC124\uBA85|\uC6D0\uC778|\uC774\uC720|\uB72F\uC5B4\uBD10|\uB530\uC838\uBD10|\uD3C9\uAC00|\uD574\uC11D|\uB514\uBC84\uAE45|\uB514\uBC84\uADF8|\uC5B4\uB5BB\uAC8C|\uC65C|\uC0B4\uD3B4|\u5206\u6790|\u8ABF\u67FB|\u89E3\u6790|\u691C\u8A0E|\u7814\u7A76|\u8A3A\u65AD|\u7406\u89E3|\u8AAC\u660E|\u691C\u8A3C|\u7CBE\u67FB|\u7A76\u660E|\u30C7\u30D0\u30C3\u30B0|\u306A\u305C|\u3069\u3046|\u4ED5\u7D44\u307F|\u8C03\u67E5|\u68C0\u67E5|\u5256\u6790|\u6DF1\u5165|\u8BCA\u65AD|\u89E3\u91CA|\u8C03\u8BD5|\u4E3A\u4EC0\u4E48|\u539F\u7406|\u641E\u6E05\u695A|\u5F04\u660E\u767D|ph\u00E2n t\u00EDch|\u0111i\u1EC1u tra|nghi\u00EAn c\u1EE9u|ki\u1EC3m tra|xem x\u00E9t|ch\u1EA9n \u0111o\u00E1n|gi\u1EA3i th\u00EDch|t\u00ECm hi\u1EC3u|g\u1EE1 l\u1ED7i|t\u1EA1i sao/i,
|
|
17627
|
+
message: `[analyze-mode]
|
|
17628
|
+
ANALYSIS MODE. Gather context before diving deep:
|
|
17629
|
+
|
|
17630
|
+
CONTEXT GATHERING (parallel):
|
|
17631
|
+
- 1-2 explore agents (codebase patterns, implementations)
|
|
17632
|
+
- 1-2 librarian agents (if external library involved)
|
|
17633
|
+
- Direct tools: Grep, AST-grep, LSP for targeted searches
|
|
17634
|
+
|
|
17635
|
+
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
17636
|
+
- Consult oracle for strategic guidance
|
|
17637
|
+
|
|
17638
|
+
SYNTHESIZE findings before proceeding.`
|
|
17639
|
+
}
|
|
17640
|
+
];
|
|
17641
|
+
|
|
17642
|
+
// src/hooks/keyword-detector/detector.ts
|
|
17643
|
+
function removeCodeBlocks2(text) {
|
|
17644
|
+
return text.replace(CODE_BLOCK_PATTERN2, "").replace(INLINE_CODE_PATTERN2, "");
|
|
17645
|
+
}
|
|
17646
|
+
function detectKeywordsWithType(text) {
|
|
17647
|
+
const textWithoutCode = removeCodeBlocks2(text);
|
|
17648
|
+
const types3 = ["ultrawork", "search", "analyze"];
|
|
17649
|
+
return KEYWORD_DETECTORS.map(({ pattern, message }, index) => ({
|
|
17650
|
+
matches: pattern.test(textWithoutCode),
|
|
17651
|
+
type: types3[index],
|
|
17652
|
+
message
|
|
17653
|
+
})).filter((result) => result.matches).map(({ type: type2, message }) => ({ type: type2, message }));
|
|
17654
|
+
}
|
|
17655
|
+
function extractPromptText2(parts) {
|
|
17656
|
+
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join(" ");
|
|
17657
|
+
}
|
|
17658
|
+
|
|
17659
|
+
// src/hooks/keyword-detector/index.ts
|
|
17660
|
+
function createKeywordDetectorHook(ctx) {
|
|
17661
|
+
return {
|
|
17662
|
+
"chat.message": async (input, output) => {
|
|
17663
|
+
const promptText = extractPromptText2(output.parts);
|
|
17664
|
+
const detectedKeywords = detectKeywordsWithType(removeCodeBlocks2(promptText));
|
|
17665
|
+
if (detectedKeywords.length === 0) {
|
|
17666
|
+
return;
|
|
17667
|
+
}
|
|
17668
|
+
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork");
|
|
17669
|
+
if (hasUltrawork) {
|
|
17670
|
+
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID });
|
|
17671
|
+
ctx.client.tui.showToast({
|
|
17672
|
+
body: {
|
|
17673
|
+
title: "Ultrawork Mode Activated",
|
|
17674
|
+
message: "Maximum precision engaged. All agents at your disposal.",
|
|
17675
|
+
variant: "success",
|
|
17676
|
+
duration: 3000
|
|
17677
|
+
}
|
|
17678
|
+
}).catch((err) => log(`[keyword-detector] Failed to show toast`, { error: err, sessionID: input.sessionID }));
|
|
17679
|
+
}
|
|
17680
|
+
log(`[keyword-detector] Detected ${detectedKeywords.length} keywords`, {
|
|
17681
|
+
sessionID: input.sessionID,
|
|
17682
|
+
types: detectedKeywords.map((k) => k.type)
|
|
17683
|
+
});
|
|
17684
|
+
}
|
|
17685
|
+
};
|
|
17686
|
+
}
|
|
17687
|
+
|
|
17352
17688
|
// src/hooks/claude-code-hooks/index.ts
|
|
17353
17689
|
var sessionFirstMessageProcessed = new Set;
|
|
17354
17690
|
var sessionErrorState = new Map;
|
|
@@ -17423,11 +17759,20 @@ function createClaudeCodeHooksHook(ctx, config = {}) {
|
|
|
17423
17759
|
log("chat.message injection skipped - interrupted during hooks", { sessionID: input.sessionID });
|
|
17424
17760
|
return;
|
|
17425
17761
|
}
|
|
17426
|
-
|
|
17427
|
-
|
|
17762
|
+
const detectedKeywords = detectKeywordsWithType(removeCodeBlocks2(prompt));
|
|
17763
|
+
const keywordMessages = detectedKeywords.map((k) => k.message);
|
|
17764
|
+
if (keywordMessages.length > 0) {
|
|
17765
|
+
log("[claude-code-hooks] Detected keywords", {
|
|
17766
|
+
sessionID: input.sessionID,
|
|
17767
|
+
types: detectedKeywords.map((k) => k.type)
|
|
17768
|
+
});
|
|
17769
|
+
}
|
|
17770
|
+
const allMessages = [...keywordMessages, ...result.messages];
|
|
17771
|
+
if (allMessages.length > 0) {
|
|
17772
|
+
const hookContent = allMessages.join(`
|
|
17428
17773
|
|
|
17429
17774
|
`);
|
|
17430
|
-
log(`[claude-code-hooks] Injecting ${result.messages.length} hook
|
|
17775
|
+
log(`[claude-code-hooks] Injecting ${allMessages.length} messages (${keywordMessages.length} keyword + ${result.messages.length} hook)`, { sessionID: input.sessionID, contentLength: hookContent.length, isFirstMessage });
|
|
17431
17776
|
if (isFirstMessage) {
|
|
17432
17777
|
const idx = output.parts.findIndex((p) => p.type === "text" && p.text);
|
|
17433
17778
|
if (idx >= 0) {
|
|
@@ -17621,22 +17966,22 @@ ${result.message}`;
|
|
|
17621
17966
|
}
|
|
17622
17967
|
// src/hooks/rules-injector/index.ts
|
|
17623
17968
|
import { readFileSync as readFileSync15 } from "fs";
|
|
17624
|
-
import { homedir as
|
|
17969
|
+
import { homedir as homedir8 } from "os";
|
|
17625
17970
|
import { relative as relative3, resolve as resolve4 } from "path";
|
|
17626
17971
|
|
|
17627
17972
|
// src/hooks/rules-injector/finder.ts
|
|
17628
17973
|
import {
|
|
17629
|
-
existsSync as
|
|
17974
|
+
existsSync as existsSync25,
|
|
17630
17975
|
readdirSync as readdirSync10,
|
|
17631
17976
|
realpathSync,
|
|
17632
17977
|
statSync as statSync2
|
|
17633
17978
|
} from "fs";
|
|
17634
|
-
import { dirname as dirname4, join as
|
|
17979
|
+
import { dirname as dirname4, join as join32, relative } from "path";
|
|
17635
17980
|
|
|
17636
17981
|
// src/hooks/rules-injector/constants.ts
|
|
17637
|
-
import { join as
|
|
17982
|
+
import { join as join31 } from "path";
|
|
17638
17983
|
var OPENCODE_STORAGE6 = getOpenCodeStorageDir();
|
|
17639
|
-
var RULES_INJECTOR_STORAGE =
|
|
17984
|
+
var RULES_INJECTOR_STORAGE = join31(OPENCODE_STORAGE6, "rules-injector");
|
|
17640
17985
|
var PROJECT_MARKERS = [
|
|
17641
17986
|
".git",
|
|
17642
17987
|
"pyproject.toml",
|
|
@@ -17677,8 +18022,8 @@ function findProjectRoot(startPath) {
|
|
|
17677
18022
|
}
|
|
17678
18023
|
while (true) {
|
|
17679
18024
|
for (const marker of PROJECT_MARKERS) {
|
|
17680
|
-
const markerPath =
|
|
17681
|
-
if (
|
|
18025
|
+
const markerPath = join32(current, marker);
|
|
18026
|
+
if (existsSync25(markerPath)) {
|
|
17682
18027
|
return current;
|
|
17683
18028
|
}
|
|
17684
18029
|
}
|
|
@@ -17690,12 +18035,12 @@ function findProjectRoot(startPath) {
|
|
|
17690
18035
|
}
|
|
17691
18036
|
}
|
|
17692
18037
|
function findRuleFilesRecursive(dir, results) {
|
|
17693
|
-
if (!
|
|
18038
|
+
if (!existsSync25(dir))
|
|
17694
18039
|
return;
|
|
17695
18040
|
try {
|
|
17696
18041
|
const entries = readdirSync10(dir, { withFileTypes: true });
|
|
17697
18042
|
for (const entry of entries) {
|
|
17698
|
-
const fullPath =
|
|
18043
|
+
const fullPath = join32(dir, entry.name);
|
|
17699
18044
|
if (entry.isDirectory()) {
|
|
17700
18045
|
findRuleFilesRecursive(fullPath, results);
|
|
17701
18046
|
} else if (entry.isFile()) {
|
|
@@ -17720,7 +18065,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
17720
18065
|
let distance = 0;
|
|
17721
18066
|
while (true) {
|
|
17722
18067
|
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
17723
|
-
const ruleDir =
|
|
18068
|
+
const ruleDir = join32(currentDir, parent, subdir);
|
|
17724
18069
|
const files = [];
|
|
17725
18070
|
findRuleFilesRecursive(ruleDir, files);
|
|
17726
18071
|
for (const filePath of files) {
|
|
@@ -17746,8 +18091,8 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
17746
18091
|
}
|
|
17747
18092
|
if (projectRoot) {
|
|
17748
18093
|
for (const ruleFile of PROJECT_RULE_FILES) {
|
|
17749
|
-
const filePath =
|
|
17750
|
-
if (
|
|
18094
|
+
const filePath = join32(projectRoot, ruleFile);
|
|
18095
|
+
if (existsSync25(filePath)) {
|
|
17751
18096
|
try {
|
|
17752
18097
|
const stat = statSync2(filePath);
|
|
17753
18098
|
if (stat.isFile()) {
|
|
@@ -17767,7 +18112,7 @@ function findRuleFiles(projectRoot, homeDir, currentFile) {
|
|
|
17767
18112
|
}
|
|
17768
18113
|
}
|
|
17769
18114
|
}
|
|
17770
|
-
const userRuleDir =
|
|
18115
|
+
const userRuleDir = join32(homeDir, USER_RULE_DIR);
|
|
17771
18116
|
const userFiles = [];
|
|
17772
18117
|
findRuleFilesRecursive(userRuleDir, userFiles);
|
|
17773
18118
|
for (const filePath of userFiles) {
|
|
@@ -17956,19 +18301,19 @@ function mergeGlobs(existing, newValue) {
|
|
|
17956
18301
|
|
|
17957
18302
|
// src/hooks/rules-injector/storage.ts
|
|
17958
18303
|
import {
|
|
17959
|
-
existsSync as
|
|
18304
|
+
existsSync as existsSync26,
|
|
17960
18305
|
mkdirSync as mkdirSync7,
|
|
17961
18306
|
readFileSync as readFileSync14,
|
|
17962
18307
|
writeFileSync as writeFileSync9,
|
|
17963
18308
|
unlinkSync as unlinkSync6
|
|
17964
18309
|
} from "fs";
|
|
17965
|
-
import { join as
|
|
18310
|
+
import { join as join33 } from "path";
|
|
17966
18311
|
function getStoragePath3(sessionID) {
|
|
17967
|
-
return
|
|
18312
|
+
return join33(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
17968
18313
|
}
|
|
17969
18314
|
function loadInjectedRules(sessionID) {
|
|
17970
18315
|
const filePath = getStoragePath3(sessionID);
|
|
17971
|
-
if (!
|
|
18316
|
+
if (!existsSync26(filePath))
|
|
17972
18317
|
return { contentHashes: new Set, realPaths: new Set };
|
|
17973
18318
|
try {
|
|
17974
18319
|
const content = readFileSync14(filePath, "utf-8");
|
|
@@ -17982,7 +18327,7 @@ function loadInjectedRules(sessionID) {
|
|
|
17982
18327
|
}
|
|
17983
18328
|
}
|
|
17984
18329
|
function saveInjectedRules(sessionID, data) {
|
|
17985
|
-
if (!
|
|
18330
|
+
if (!existsSync26(RULES_INJECTOR_STORAGE)) {
|
|
17986
18331
|
mkdirSync7(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
17987
18332
|
}
|
|
17988
18333
|
const storageData = {
|
|
@@ -17995,7 +18340,7 @@ function saveInjectedRules(sessionID, data) {
|
|
|
17995
18340
|
}
|
|
17996
18341
|
function clearInjectedRules(sessionID) {
|
|
17997
18342
|
const filePath = getStoragePath3(sessionID);
|
|
17998
|
-
if (
|
|
18343
|
+
if (existsSync26(filePath)) {
|
|
17999
18344
|
unlinkSync6(filePath);
|
|
18000
18345
|
}
|
|
18001
18346
|
}
|
|
@@ -18025,7 +18370,7 @@ function createRulesInjectorHook(ctx) {
|
|
|
18025
18370
|
return;
|
|
18026
18371
|
const projectRoot = findProjectRoot(resolved);
|
|
18027
18372
|
const cache2 = getSessionCache(sessionID);
|
|
18028
|
-
const home =
|
|
18373
|
+
const home = homedir8();
|
|
18029
18374
|
const ruleFileCandidates = findRuleFiles(projectRoot, home, resolved);
|
|
18030
18375
|
const toInject = [];
|
|
18031
18376
|
for (const candidate of ruleFileCandidates) {
|
|
@@ -18450,13 +18795,17 @@ function invalidatePackage(packageName = PACKAGE_NAME) {
|
|
|
18450
18795
|
}
|
|
18451
18796
|
|
|
18452
18797
|
// src/cli/config-manager.ts
|
|
18453
|
-
|
|
18454
|
-
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
|
|
18798
|
+
var configContext = null;
|
|
18799
|
+
function getConfigContext() {
|
|
18800
|
+
if (!configContext) {
|
|
18801
|
+
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null });
|
|
18802
|
+
configContext = { binary: "opencode", version: null, paths };
|
|
18803
|
+
}
|
|
18804
|
+
return configContext;
|
|
18805
|
+
}
|
|
18806
|
+
function getConfigDir() {
|
|
18807
|
+
return getConfigContext().paths.configDir;
|
|
18808
|
+
}
|
|
18460
18809
|
var BUN_INSTALL_TIMEOUT_SECONDS = 60;
|
|
18461
18810
|
var BUN_INSTALL_TIMEOUT_MS = BUN_INSTALL_TIMEOUT_SECONDS * 1000;
|
|
18462
18811
|
async function runBunInstall() {
|
|
@@ -18466,7 +18815,7 @@ async function runBunInstall() {
|
|
|
18466
18815
|
async function runBunInstallWithDetails() {
|
|
18467
18816
|
try {
|
|
18468
18817
|
const proc = Bun.spawn(["bun", "install"], {
|
|
18469
|
-
cwd:
|
|
18818
|
+
cwd: getConfigDir(),
|
|
18470
18819
|
stdout: "pipe",
|
|
18471
18820
|
stderr: "pipe"
|
|
18472
18821
|
});
|
|
@@ -18524,9 +18873,9 @@ v${latestVersion} available. Restart OpenCode to apply.` : `OpenCode is now on S
|
|
|
18524
18873
|
return;
|
|
18525
18874
|
hasChecked = true;
|
|
18526
18875
|
setTimeout(async () => {
|
|
18527
|
-
const
|
|
18876
|
+
const cachedVersion2 = getCachedVersion();
|
|
18528
18877
|
const localDevVersion = getLocalDevVersion(ctx.directory);
|
|
18529
|
-
const displayVersion = localDevVersion ??
|
|
18878
|
+
const displayVersion = localDevVersion ?? cachedVersion2;
|
|
18530
18879
|
await showConfigErrorsIfAny(ctx);
|
|
18531
18880
|
if (localDevVersion) {
|
|
18532
18881
|
if (showStartupToast) {
|
|
@@ -18551,8 +18900,8 @@ async function runBackgroundUpdateCheck(ctx, autoUpdate, getToastMessage) {
|
|
|
18551
18900
|
log("[auto-update-checker] Plugin not found in config");
|
|
18552
18901
|
return;
|
|
18553
18902
|
}
|
|
18554
|
-
const
|
|
18555
|
-
const currentVersion =
|
|
18903
|
+
const cachedVersion2 = getCachedVersion();
|
|
18904
|
+
const currentVersion = cachedVersion2 ?? pluginInfo.pinnedVersion;
|
|
18556
18905
|
if (!currentVersion) {
|
|
18557
18906
|
log("[auto-update-checker] No version found (cached or pinned)");
|
|
18558
18907
|
return;
|
|
@@ -18671,7 +19020,7 @@ async function showLocalDevToast(ctx, version, isSisyphusEnabled) {
|
|
|
18671
19020
|
}
|
|
18672
19021
|
// src/hooks/agent-usage-reminder/storage.ts
|
|
18673
19022
|
import {
|
|
18674
|
-
existsSync as
|
|
19023
|
+
existsSync as existsSync30,
|
|
18675
19024
|
mkdirSync as mkdirSync8,
|
|
18676
19025
|
readFileSync as readFileSync18,
|
|
18677
19026
|
writeFileSync as writeFileSync12,
|
|
@@ -18731,7 +19080,7 @@ function getStoragePath4(sessionID) {
|
|
|
18731
19080
|
}
|
|
18732
19081
|
function loadAgentUsageState(sessionID) {
|
|
18733
19082
|
const filePath = getStoragePath4(sessionID);
|
|
18734
|
-
if (!
|
|
19083
|
+
if (!existsSync30(filePath))
|
|
18735
19084
|
return null;
|
|
18736
19085
|
try {
|
|
18737
19086
|
const content = readFileSync18(filePath, "utf-8");
|
|
@@ -18741,7 +19090,7 @@ function loadAgentUsageState(sessionID) {
|
|
|
18741
19090
|
}
|
|
18742
19091
|
}
|
|
18743
19092
|
function saveAgentUsageState(state2) {
|
|
18744
|
-
if (!
|
|
19093
|
+
if (!existsSync30(AGENT_USAGE_REMINDER_STORAGE)) {
|
|
18745
19094
|
mkdirSync8(AGENT_USAGE_REMINDER_STORAGE, { recursive: true });
|
|
18746
19095
|
}
|
|
18747
19096
|
const filePath = getStoragePath4(state2.sessionID);
|
|
@@ -18749,7 +19098,7 @@ function saveAgentUsageState(state2) {
|
|
|
18749
19098
|
}
|
|
18750
19099
|
function clearAgentUsageState(sessionID) {
|
|
18751
19100
|
const filePath = getStoragePath4(sessionID);
|
|
18752
|
-
if (
|
|
19101
|
+
if (existsSync30(filePath)) {
|
|
18753
19102
|
unlinkSync7(filePath);
|
|
18754
19103
|
}
|
|
18755
19104
|
}
|
|
@@ -18819,163 +19168,6 @@ function createAgentUsageReminderHook(_ctx) {
|
|
|
18819
19168
|
event: eventHandler
|
|
18820
19169
|
};
|
|
18821
19170
|
}
|
|
18822
|
-
// src/hooks/keyword-detector/constants.ts
|
|
18823
|
-
var CODE_BLOCK_PATTERN2 = /```[\s\S]*?```/g;
|
|
18824
|
-
var INLINE_CODE_PATTERN2 = /`[^`]+`/g;
|
|
18825
|
-
var KEYWORD_DETECTORS = [
|
|
18826
|
-
{
|
|
18827
|
-
pattern: /(ultrawork|ulw)/i,
|
|
18828
|
-
message: `<ultrawork-mode>
|
|
18829
|
-
[CODE RED] Maximum precision required. Ultrathink before acting.
|
|
18830
|
-
|
|
18831
|
-
YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
|
|
18832
|
-
TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
|
|
18833
|
-
|
|
18834
|
-
## AGENT UTILIZATION PRINCIPLES (by capability, not by name)
|
|
18835
|
-
- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS for file patterns, internal implementations, project structure
|
|
18836
|
-
- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS for API references, examples, external library docs
|
|
18837
|
-
- **Planning & Strategy**: NEVER plan yourself - ALWAYS spawn a dedicated planning agent for work breakdown
|
|
18838
|
-
- **High-IQ Reasoning**: Leverage specialized agents for architecture decisions, code review, strategic planning
|
|
18839
|
-
- **Frontend/UI Tasks**: Delegate to UI-specialized agents for design and implementation
|
|
18840
|
-
|
|
18841
|
-
## EXECUTION RULES
|
|
18842
|
-
- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each.
|
|
18843
|
-
- **PARALLEL**: Fire independent agent calls simultaneously via background_task - NEVER wait sequentially.
|
|
18844
|
-
- **BACKGROUND FIRST**: Use background_task for exploration/research agents (10+ concurrent if needed).
|
|
18845
|
-
- **VERIFY**: Re-read request after completion. Check ALL requirements met before reporting done.
|
|
18846
|
-
- **DELEGATE**: Don't do everything yourself - orchestrate specialized agents for their strengths.
|
|
18847
|
-
|
|
18848
|
-
## WORKFLOW
|
|
18849
|
-
1. Analyze the request and identify required capabilities
|
|
18850
|
-
2. Spawn exploration/librarian agents via background_task in PARALLEL (10+ if needed)
|
|
18851
|
-
3. Always Use Plan agent with gathered context to create detailed work breakdown
|
|
18852
|
-
4. Execute with continuous verification against original requirements
|
|
18853
|
-
|
|
18854
|
-
## TDD (if test infrastructure exists)
|
|
18855
|
-
|
|
18856
|
-
1. Write spec (requirements)
|
|
18857
|
-
2. Write tests (failing)
|
|
18858
|
-
3. RED: tests fail
|
|
18859
|
-
4. Implement minimal code
|
|
18860
|
-
5. GREEN: tests pass
|
|
18861
|
-
6. Refactor if needed (must stay green)
|
|
18862
|
-
7. Next feature, repeat
|
|
18863
|
-
|
|
18864
|
-
## ZERO TOLERANCE FAILURES
|
|
18865
|
-
- **NO Scope Reduction**: Never make "demo", "skeleton", "simplified", "basic" versions - deliver FULL implementation
|
|
18866
|
-
- **NO MockUp Work**: When user asked you to do "port A", you must "port A", fully, 100%. No Extra feature, No reduced feature, no mock data, fully working 100% port.
|
|
18867
|
-
- **NO Partial Completion**: Never stop at 60-80% saying "you can extend this..." - finish 100%
|
|
18868
|
-
- **NO Assumed Shortcuts**: Never skip requirements you deem "optional" or "can be added later"
|
|
18869
|
-
- **NO Premature Stopping**: Never declare done until ALL TODOs are completed and verified
|
|
18870
|
-
- **NO TEST DELETION**: Never delete or skip failing tests to make the build pass. Fix the code, not the tests.
|
|
18871
|
-
|
|
18872
|
-
THE USER ASKED FOR X. DELIVER EXACTLY X. NOT A SUBSET. NOT A DEMO. NOT A STARTING POINT.
|
|
18873
|
-
|
|
18874
|
-
</ultrawork-mode>
|
|
18875
|
-
|
|
18876
|
-
---
|
|
18877
|
-
|
|
18878
|
-
`
|
|
18879
|
-
},
|
|
18880
|
-
{
|
|
18881
|
-
pattern: /\b(search|find|locate|lookup|look\s*up|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all|\uAC80\uC0C9|\uCC3E\uC544|\uD0D0\uC0C9|\uC870\uD68C|\uC2A4\uCE94|\uC11C\uCE58|\uB4A4\uC838|\uCC3E\uAE30|\uC5B4\uB514|\uCD94\uC801|\uD0D0\uC9C0|\uCC3E\uC544\uBD10|\uCC3E\uC544\uB0B4|\uBCF4\uC5EC\uC918|\uBAA9\uB85D|\u691C\u7D22|\u63A2\u3057\u3066|\u898B\u3064\u3051\u3066|\u30B5\u30FC\u30C1|\u63A2\u7D22|\u30B9\u30AD\u30E3\u30F3|\u3069\u3053|\u767A\u898B|\u635C\u7D22|\u898B\u3064\u3051\u51FA\u3059|\u4E00\u89A7|\u641C\u7D22|\u67E5\u627E|\u5BFB\u627E|\u67E5\u8BE2|\u68C0\u7D22|\u5B9A\u4F4D|\u626B\u63CF|\u53D1\u73B0|\u5728\u54EA\u91CC|\u627E\u51FA\u6765|\u5217\u51FA|t\u00ECm ki\u1EBFm|tra c\u1EE9u|\u0111\u1ECBnh v\u1ECB|qu\u00E9t|ph\u00E1t hi\u1EC7n|truy t\u00ECm|t\u00ECm ra|\u1EDF \u0111\u00E2u|li\u1EC7t k\u00EA/i,
|
|
18882
|
-
message: `[search-mode]
|
|
18883
|
-
MAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:
|
|
18884
|
-
- explore agents (codebase patterns, file structures, ast-grep)
|
|
18885
|
-
- librarian agents (remote repos, official docs, GitHub examples)
|
|
18886
|
-
Plus direct tools: Grep, ripgrep (rg), ast-grep (sg)
|
|
18887
|
-
NEVER stop at first result - be exhaustive.`
|
|
18888
|
-
},
|
|
18889
|
-
{
|
|
18890
|
-
pattern: /\b(analyze|analyse|investigate|examine|research|study|deep[\s-]?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to|\uBD84\uC11D|\uC870\uC0AC|\uD30C\uC545|\uC5F0\uAD6C|\uAC80\uD1A0|\uC9C4\uB2E8|\uC774\uD574|\uC124\uBA85|\uC6D0\uC778|\uC774\uC720|\uB72F\uC5B4\uBD10|\uB530\uC838\uBD10|\uD3C9\uAC00|\uD574\uC11D|\uB514\uBC84\uAE45|\uB514\uBC84\uADF8|\uC5B4\uB5BB\uAC8C|\uC65C|\uC0B4\uD3B4|\u5206\u6790|\u8ABF\u67FB|\u89E3\u6790|\u691C\u8A0E|\u7814\u7A76|\u8A3A\u65AD|\u7406\u89E3|\u8AAC\u660E|\u691C\u8A3C|\u7CBE\u67FB|\u7A76\u660E|\u30C7\u30D0\u30C3\u30B0|\u306A\u305C|\u3069\u3046|\u4ED5\u7D44\u307F|\u8C03\u67E5|\u68C0\u67E5|\u5256\u6790|\u6DF1\u5165|\u8BCA\u65AD|\u89E3\u91CA|\u8C03\u8BD5|\u4E3A\u4EC0\u4E48|\u539F\u7406|\u641E\u6E05\u695A|\u5F04\u660E\u767D|ph\u00E2n t\u00EDch|\u0111i\u1EC1u tra|nghi\u00EAn c\u1EE9u|ki\u1EC3m tra|xem x\u00E9t|ch\u1EA9n \u0111o\u00E1n|gi\u1EA3i th\u00EDch|t\u00ECm hi\u1EC3u|g\u1EE1 l\u1ED7i|t\u1EA1i sao/i,
|
|
18891
|
-
message: `[analyze-mode]
|
|
18892
|
-
ANALYSIS MODE. Gather context before diving deep:
|
|
18893
|
-
|
|
18894
|
-
CONTEXT GATHERING (parallel):
|
|
18895
|
-
- 1-2 explore agents (codebase patterns, implementations)
|
|
18896
|
-
- 1-2 librarian agents (if external library involved)
|
|
18897
|
-
- Direct tools: Grep, AST-grep, LSP for targeted searches
|
|
18898
|
-
|
|
18899
|
-
IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
|
|
18900
|
-
- Consult oracle for strategic guidance
|
|
18901
|
-
|
|
18902
|
-
SYNTHESIZE findings before proceeding.`
|
|
18903
|
-
}
|
|
18904
|
-
];
|
|
18905
|
-
|
|
18906
|
-
// src/hooks/keyword-detector/detector.ts
|
|
18907
|
-
function removeCodeBlocks2(text) {
|
|
18908
|
-
return text.replace(CODE_BLOCK_PATTERN2, "").replace(INLINE_CODE_PATTERN2, "");
|
|
18909
|
-
}
|
|
18910
|
-
function detectKeywordsWithType(text) {
|
|
18911
|
-
const textWithoutCode = removeCodeBlocks2(text);
|
|
18912
|
-
const types3 = ["ultrawork", "search", "analyze"];
|
|
18913
|
-
return KEYWORD_DETECTORS.map(({ pattern, message }, index) => ({
|
|
18914
|
-
matches: pattern.test(textWithoutCode),
|
|
18915
|
-
type: types3[index],
|
|
18916
|
-
message
|
|
18917
|
-
})).filter((result) => result.matches).map(({ type: type2, message }) => ({ type: type2, message }));
|
|
18918
|
-
}
|
|
18919
|
-
function extractPromptText2(parts) {
|
|
18920
|
-
return parts.filter((p) => p.type === "text").map((p) => p.text || "").join(" ");
|
|
18921
|
-
}
|
|
18922
|
-
|
|
18923
|
-
// src/hooks/keyword-detector/index.ts
|
|
18924
|
-
var sessionFirstMessageProcessed2 = new Set;
|
|
18925
|
-
var sessionUltraworkNotified = new Set;
|
|
18926
|
-
function createKeywordDetectorHook(ctx) {
|
|
18927
|
-
return {
|
|
18928
|
-
"chat.message": async (input, output) => {
|
|
18929
|
-
const isFirstMessage = !sessionFirstMessageProcessed2.has(input.sessionID);
|
|
18930
|
-
sessionFirstMessageProcessed2.add(input.sessionID);
|
|
18931
|
-
const promptText = extractPromptText2(output.parts);
|
|
18932
|
-
const detectedKeywords = detectKeywordsWithType(promptText);
|
|
18933
|
-
const messages = detectedKeywords.map((k) => k.message);
|
|
18934
|
-
if (messages.length === 0) {
|
|
18935
|
-
return;
|
|
18936
|
-
}
|
|
18937
|
-
const hasUltrawork = detectedKeywords.some((k) => k.type === "ultrawork");
|
|
18938
|
-
if (hasUltrawork && !sessionUltraworkNotified.has(input.sessionID)) {
|
|
18939
|
-
sessionUltraworkNotified.add(input.sessionID);
|
|
18940
|
-
log(`[keyword-detector] Ultrawork mode activated`, { sessionID: input.sessionID });
|
|
18941
|
-
ctx.client.tui.showToast({
|
|
18942
|
-
body: {
|
|
18943
|
-
title: "Ultrawork Mode Activated",
|
|
18944
|
-
message: "Maximum precision engaged. All agents at your disposal.",
|
|
18945
|
-
variant: "success",
|
|
18946
|
-
duration: 3000
|
|
18947
|
-
}
|
|
18948
|
-
}).catch((err) => log(`[keyword-detector] Failed to show toast`, { error: err, sessionID: input.sessionID }));
|
|
18949
|
-
}
|
|
18950
|
-
const context = messages.join(`
|
|
18951
|
-
`);
|
|
18952
|
-
if (isFirstMessage) {
|
|
18953
|
-
log(`Keywords detected on first message, transforming parts directly`, { sessionID: input.sessionID, keywordCount: messages.length });
|
|
18954
|
-
const idx = output.parts.findIndex((p) => p.type === "text" && p.text);
|
|
18955
|
-
if (idx >= 0) {
|
|
18956
|
-
output.parts[idx].text = `${context}
|
|
18957
|
-
|
|
18958
|
-
---
|
|
18959
|
-
|
|
18960
|
-
${output.parts[idx].text ?? ""}`;
|
|
18961
|
-
}
|
|
18962
|
-
return;
|
|
18963
|
-
}
|
|
18964
|
-
log(`Keywords detected: ${messages.length}`, { sessionID: input.sessionID });
|
|
18965
|
-
const message = output.message;
|
|
18966
|
-
log(`[keyword-detector] Injecting context for ${messages.length} keywords`, { sessionID: input.sessionID, contextLength: context.length });
|
|
18967
|
-
const success = injectHookMessage(input.sessionID, context, {
|
|
18968
|
-
agent: message.agent,
|
|
18969
|
-
model: message.model,
|
|
18970
|
-
path: message.path,
|
|
18971
|
-
tools: message.tools
|
|
18972
|
-
});
|
|
18973
|
-
if (success) {
|
|
18974
|
-
log("Keyword context injected", { sessionID: input.sessionID });
|
|
18975
|
-
}
|
|
18976
|
-
}
|
|
18977
|
-
};
|
|
18978
|
-
}
|
|
18979
19171
|
// src/hooks/non-interactive-env/constants.ts
|
|
18980
19172
|
var HOOK_NAME2 = "non-interactive-env";
|
|
18981
19173
|
var NON_INTERACTIVE_ENV = {
|
|
@@ -19058,7 +19250,8 @@ function shellEscape(value) {
|
|
|
19058
19250
|
return value;
|
|
19059
19251
|
}
|
|
19060
19252
|
function buildEnvPrefix(env) {
|
|
19061
|
-
|
|
19253
|
+
const exports = Object.entries(env).map(([key, value]) => `${key}=${shellEscape(value)}`).join(" ");
|
|
19254
|
+
return `export ${exports};`;
|
|
19062
19255
|
}
|
|
19063
19256
|
function createNonInteractiveEnvHook(_ctx) {
|
|
19064
19257
|
return {
|
|
@@ -19089,7 +19282,7 @@ function createNonInteractiveEnvHook(_ctx) {
|
|
|
19089
19282
|
}
|
|
19090
19283
|
// src/hooks/interactive-bash-session/storage.ts
|
|
19091
19284
|
import {
|
|
19092
|
-
existsSync as
|
|
19285
|
+
existsSync as existsSync31,
|
|
19093
19286
|
mkdirSync as mkdirSync9,
|
|
19094
19287
|
readFileSync as readFileSync19,
|
|
19095
19288
|
writeFileSync as writeFileSync13,
|
|
@@ -19116,7 +19309,7 @@ function getStoragePath5(sessionID) {
|
|
|
19116
19309
|
}
|
|
19117
19310
|
function loadInteractiveBashSessionState(sessionID) {
|
|
19118
19311
|
const filePath = getStoragePath5(sessionID);
|
|
19119
|
-
if (!
|
|
19312
|
+
if (!existsSync31(filePath))
|
|
19120
19313
|
return null;
|
|
19121
19314
|
try {
|
|
19122
19315
|
const content = readFileSync19(filePath, "utf-8");
|
|
@@ -19131,7 +19324,7 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
19131
19324
|
}
|
|
19132
19325
|
}
|
|
19133
19326
|
function saveInteractiveBashSessionState(state2) {
|
|
19134
|
-
if (!
|
|
19327
|
+
if (!existsSync31(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
19135
19328
|
mkdirSync9(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
19136
19329
|
}
|
|
19137
19330
|
const filePath = getStoragePath5(state2.sessionID);
|
|
@@ -19144,7 +19337,7 @@ function saveInteractiveBashSessionState(state2) {
|
|
|
19144
19337
|
}
|
|
19145
19338
|
function clearInteractiveBashSessionState(sessionID) {
|
|
19146
19339
|
const filePath = getStoragePath5(sessionID);
|
|
19147
|
-
if (
|
|
19340
|
+
if (existsSync31(filePath)) {
|
|
19148
19341
|
unlinkSync8(filePath);
|
|
19149
19342
|
}
|
|
19150
19343
|
}
|
|
@@ -19475,10 +19668,10 @@ function createThinkingBlockValidatorHook() {
|
|
|
19475
19668
|
};
|
|
19476
19669
|
}
|
|
19477
19670
|
// src/hooks/ralph-loop/index.ts
|
|
19478
|
-
import { existsSync as
|
|
19671
|
+
import { existsSync as existsSync33, readFileSync as readFileSync21 } from "fs";
|
|
19479
19672
|
|
|
19480
19673
|
// src/hooks/ralph-loop/storage.ts
|
|
19481
|
-
import { existsSync as
|
|
19674
|
+
import { existsSync as existsSync32, readFileSync as readFileSync20, writeFileSync as writeFileSync14, unlinkSync as unlinkSync9, mkdirSync as mkdirSync10 } from "fs";
|
|
19482
19675
|
import { dirname as dirname6, join as join41 } from "path";
|
|
19483
19676
|
|
|
19484
19677
|
// src/hooks/ralph-loop/constants.ts
|
|
@@ -19493,7 +19686,7 @@ function getStateFilePath(directory, customPath) {
|
|
|
19493
19686
|
}
|
|
19494
19687
|
function readState(directory, customPath) {
|
|
19495
19688
|
const filePath = getStateFilePath(directory, customPath);
|
|
19496
|
-
if (!
|
|
19689
|
+
if (!existsSync32(filePath)) {
|
|
19497
19690
|
return null;
|
|
19498
19691
|
}
|
|
19499
19692
|
try {
|
|
@@ -19530,7 +19723,7 @@ function writeState(directory, state2, customPath) {
|
|
|
19530
19723
|
const filePath = getStateFilePath(directory, customPath);
|
|
19531
19724
|
try {
|
|
19532
19725
|
const dir = dirname6(filePath);
|
|
19533
|
-
if (!
|
|
19726
|
+
if (!existsSync32(dir)) {
|
|
19534
19727
|
mkdirSync10(dir, { recursive: true });
|
|
19535
19728
|
}
|
|
19536
19729
|
const sessionIdLine = state2.session_id ? `session_id: "${state2.session_id}"
|
|
@@ -19553,7 +19746,7 @@ ${state2.prompt}
|
|
|
19553
19746
|
function clearState(directory, customPath) {
|
|
19554
19747
|
const filePath = getStateFilePath(directory, customPath);
|
|
19555
19748
|
try {
|
|
19556
|
-
if (
|
|
19749
|
+
if (existsSync32(filePath)) {
|
|
19557
19750
|
unlinkSync9(filePath);
|
|
19558
19751
|
}
|
|
19559
19752
|
return true;
|
|
@@ -19592,6 +19785,7 @@ function createRalphLoopHook(ctx, options) {
|
|
|
19592
19785
|
const stateDir = config?.state_dir;
|
|
19593
19786
|
const getTranscriptPath2 = options?.getTranscriptPath ?? getTranscriptPath;
|
|
19594
19787
|
const apiTimeout = options?.apiTimeout ?? DEFAULT_API_TIMEOUT;
|
|
19788
|
+
const checkSessionExists = options?.checkSessionExists;
|
|
19595
19789
|
function getSessionState(sessionID) {
|
|
19596
19790
|
let state2 = sessions.get(sessionID);
|
|
19597
19791
|
if (!state2) {
|
|
@@ -19604,7 +19798,7 @@ function createRalphLoopHook(ctx, options) {
|
|
|
19604
19798
|
if (!transcriptPath)
|
|
19605
19799
|
return false;
|
|
19606
19800
|
try {
|
|
19607
|
-
if (!
|
|
19801
|
+
if (!existsSync33(transcriptPath))
|
|
19608
19802
|
return false;
|
|
19609
19803
|
const content = readFileSync21(transcriptPath, "utf-8");
|
|
19610
19804
|
const pattern = new RegExp(`<promise>\\s*${escapeRegex(promise)}\\s*</promise>`, "is");
|
|
@@ -19693,6 +19887,24 @@ function createRalphLoopHook(ctx, options) {
|
|
|
19693
19887
|
return;
|
|
19694
19888
|
}
|
|
19695
19889
|
if (state2.session_id && state2.session_id !== sessionID) {
|
|
19890
|
+
if (checkSessionExists) {
|
|
19891
|
+
try {
|
|
19892
|
+
const originalSessionExists = await checkSessionExists(state2.session_id);
|
|
19893
|
+
if (!originalSessionExists) {
|
|
19894
|
+
clearState(ctx.directory, stateDir);
|
|
19895
|
+
log(`[${HOOK_NAME3}] Cleared orphaned state from deleted session`, {
|
|
19896
|
+
orphanedSessionId: state2.session_id,
|
|
19897
|
+
currentSessionId: sessionID
|
|
19898
|
+
});
|
|
19899
|
+
return;
|
|
19900
|
+
}
|
|
19901
|
+
} catch (err) {
|
|
19902
|
+
log(`[${HOOK_NAME3}] Failed to check session existence`, {
|
|
19903
|
+
sessionId: state2.session_id,
|
|
19904
|
+
error: String(err)
|
|
19905
|
+
});
|
|
19906
|
+
}
|
|
19907
|
+
}
|
|
19696
19908
|
return;
|
|
19697
19909
|
}
|
|
19698
19910
|
const transcriptPath = getTranscriptPath2(sessionID);
|
|
@@ -19861,11 +20073,12 @@ function extractPromptText3(parts) {
|
|
|
19861
20073
|
}
|
|
19862
20074
|
|
|
19863
20075
|
// src/hooks/auto-slash-command/executor.ts
|
|
19864
|
-
import { existsSync as
|
|
20076
|
+
import { existsSync as existsSync36, readdirSync as readdirSync12, readFileSync as readFileSync24 } from "fs";
|
|
19865
20077
|
import { join as join43, basename as basename2, dirname as dirname8 } from "path";
|
|
19866
20078
|
import { homedir as homedir13 } from "os";
|
|
19867
20079
|
// src/features/opencode-skill-loader/loader.ts
|
|
19868
|
-
import { existsSync as
|
|
20080
|
+
import { existsSync as existsSync34, readdirSync as readdirSync11, readFileSync as readFileSync22 } from "fs";
|
|
20081
|
+
import { promises as fs9 } from "fs";
|
|
19869
20082
|
import { join as join42, basename } from "path";
|
|
19870
20083
|
import { homedir as homedir11 } from "os";
|
|
19871
20084
|
function parseSkillMcpConfigFromFrontmatter(content) {
|
|
@@ -19884,7 +20097,7 @@ function parseSkillMcpConfigFromFrontmatter(content) {
|
|
|
19884
20097
|
}
|
|
19885
20098
|
function loadMcpJsonFromDir(skillDir) {
|
|
19886
20099
|
const mcpJsonPath = join42(skillDir, "mcp.json");
|
|
19887
|
-
if (!
|
|
20100
|
+
if (!existsSync34(mcpJsonPath))
|
|
19888
20101
|
return;
|
|
19889
20102
|
try {
|
|
19890
20103
|
const content = readFileSync22(mcpJsonPath, "utf-8");
|
|
@@ -19911,7 +20124,66 @@ function parseAllowedTools(allowedTools) {
|
|
|
19911
20124
|
function loadSkillFromPath(skillPath, resolvedPath, defaultName, scope) {
|
|
19912
20125
|
try {
|
|
19913
20126
|
const content = readFileSync22(skillPath, "utf-8");
|
|
19914
|
-
const { data
|
|
20127
|
+
const { data } = parseFrontmatter(content);
|
|
20128
|
+
const frontmatterMcp = parseSkillMcpConfigFromFrontmatter(content);
|
|
20129
|
+
const mcpJsonMcp = loadMcpJsonFromDir(resolvedPath);
|
|
20130
|
+
const mcpConfig = mcpJsonMcp || frontmatterMcp;
|
|
20131
|
+
const skillName = data.name || defaultName;
|
|
20132
|
+
const originalDescription = data.description || "";
|
|
20133
|
+
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
20134
|
+
const formattedDescription = `(${scope} - Skill) ${originalDescription}`;
|
|
20135
|
+
const lazyContent = {
|
|
20136
|
+
loaded: false,
|
|
20137
|
+
content: undefined,
|
|
20138
|
+
load: async () => {
|
|
20139
|
+
if (!lazyContent.loaded) {
|
|
20140
|
+
const fileContent = await fs9.readFile(skillPath, "utf-8");
|
|
20141
|
+
const { body } = parseFrontmatter(fileContent);
|
|
20142
|
+
lazyContent.content = `<skill-instruction>
|
|
20143
|
+
Base directory for this skill: ${resolvedPath}/
|
|
20144
|
+
File references (@path) in this skill are relative to this directory.
|
|
20145
|
+
|
|
20146
|
+
${body.trim()}
|
|
20147
|
+
</skill-instruction>
|
|
20148
|
+
|
|
20149
|
+
<user-request>
|
|
20150
|
+
$ARGUMENTS
|
|
20151
|
+
</user-request>`;
|
|
20152
|
+
lazyContent.loaded = true;
|
|
20153
|
+
}
|
|
20154
|
+
return lazyContent.content;
|
|
20155
|
+
}
|
|
20156
|
+
};
|
|
20157
|
+
const definition = {
|
|
20158
|
+
name: skillName,
|
|
20159
|
+
description: formattedDescription,
|
|
20160
|
+
template: "",
|
|
20161
|
+
model: sanitizeModelField(data.model, isOpencodeSource ? "opencode" : "claude-code"),
|
|
20162
|
+
agent: data.agent,
|
|
20163
|
+
subtask: data.subtask,
|
|
20164
|
+
argumentHint: data["argument-hint"]
|
|
20165
|
+
};
|
|
20166
|
+
return {
|
|
20167
|
+
name: skillName,
|
|
20168
|
+
path: skillPath,
|
|
20169
|
+
resolvedPath,
|
|
20170
|
+
definition,
|
|
20171
|
+
scope,
|
|
20172
|
+
license: data.license,
|
|
20173
|
+
compatibility: data.compatibility,
|
|
20174
|
+
metadata: data.metadata,
|
|
20175
|
+
allowedTools: parseAllowedTools(data["allowed-tools"]),
|
|
20176
|
+
mcpConfig,
|
|
20177
|
+
lazyContent
|
|
20178
|
+
};
|
|
20179
|
+
} catch {
|
|
20180
|
+
return null;
|
|
20181
|
+
}
|
|
20182
|
+
}
|
|
20183
|
+
async function loadSkillFromPathAsync(skillPath, resolvedPath, defaultName, scope) {
|
|
20184
|
+
try {
|
|
20185
|
+
const content = await fs9.readFile(skillPath, "utf-8");
|
|
20186
|
+
const { data } = parseFrontmatter(content);
|
|
19915
20187
|
const frontmatterMcp = parseSkillMcpConfigFromFrontmatter(content);
|
|
19916
20188
|
const mcpJsonMcp = loadMcpJsonFromDir(resolvedPath);
|
|
19917
20189
|
const mcpConfig = mcpJsonMcp || frontmatterMcp;
|
|
@@ -19919,7 +20191,14 @@ function loadSkillFromPath(skillPath, resolvedPath, defaultName, scope) {
|
|
|
19919
20191
|
const originalDescription = data.description || "";
|
|
19920
20192
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
19921
20193
|
const formattedDescription = `(${scope} - Skill) ${originalDescription}`;
|
|
19922
|
-
const
|
|
20194
|
+
const lazyContent = {
|
|
20195
|
+
loaded: false,
|
|
20196
|
+
content: undefined,
|
|
20197
|
+
load: async () => {
|
|
20198
|
+
if (!lazyContent.loaded) {
|
|
20199
|
+
const fileContent = await fs9.readFile(skillPath, "utf-8");
|
|
20200
|
+
const { body } = parseFrontmatter(fileContent);
|
|
20201
|
+
lazyContent.content = `<skill-instruction>
|
|
19923
20202
|
Base directory for this skill: ${resolvedPath}/
|
|
19924
20203
|
File references (@path) in this skill are relative to this directory.
|
|
19925
20204
|
|
|
@@ -19929,10 +20208,15 @@ ${body.trim()}
|
|
|
19929
20208
|
<user-request>
|
|
19930
20209
|
$ARGUMENTS
|
|
19931
20210
|
</user-request>`;
|
|
20211
|
+
lazyContent.loaded = true;
|
|
20212
|
+
}
|
|
20213
|
+
return lazyContent.content;
|
|
20214
|
+
}
|
|
20215
|
+
};
|
|
19932
20216
|
const definition = {
|
|
19933
20217
|
name: skillName,
|
|
19934
20218
|
description: formattedDescription,
|
|
19935
|
-
template:
|
|
20219
|
+
template: "",
|
|
19936
20220
|
model: sanitizeModelField(data.model, isOpencodeSource ? "opencode" : "claude-code"),
|
|
19937
20221
|
agent: data.agent,
|
|
19938
20222
|
subtask: data.subtask,
|
|
@@ -19948,14 +20232,15 @@ $ARGUMENTS
|
|
|
19948
20232
|
compatibility: data.compatibility,
|
|
19949
20233
|
metadata: data.metadata,
|
|
19950
20234
|
allowedTools: parseAllowedTools(data["allowed-tools"]),
|
|
19951
|
-
mcpConfig
|
|
20235
|
+
mcpConfig,
|
|
20236
|
+
lazyContent
|
|
19952
20237
|
};
|
|
19953
20238
|
} catch {
|
|
19954
20239
|
return null;
|
|
19955
20240
|
}
|
|
19956
20241
|
}
|
|
19957
20242
|
function loadSkillsFromDir(skillsDir, scope) {
|
|
19958
|
-
if (!
|
|
20243
|
+
if (!existsSync34(skillsDir)) {
|
|
19959
20244
|
return [];
|
|
19960
20245
|
}
|
|
19961
20246
|
const entries = readdirSync11(skillsDir, { withFileTypes: true });
|
|
@@ -19968,14 +20253,14 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
19968
20253
|
const resolvedPath = resolveSymlink(entryPath);
|
|
19969
20254
|
const dirName = entry.name;
|
|
19970
20255
|
const skillMdPath = join42(resolvedPath, "SKILL.md");
|
|
19971
|
-
if (
|
|
20256
|
+
if (existsSync34(skillMdPath)) {
|
|
19972
20257
|
const skill = loadSkillFromPath(skillMdPath, resolvedPath, dirName, scope);
|
|
19973
20258
|
if (skill)
|
|
19974
20259
|
skills.push(skill);
|
|
19975
20260
|
continue;
|
|
19976
20261
|
}
|
|
19977
20262
|
const namedSkillMdPath = join42(resolvedPath, `${dirName}.md`);
|
|
19978
|
-
if (
|
|
20263
|
+
if (existsSync34(namedSkillMdPath)) {
|
|
19979
20264
|
const skill = loadSkillFromPath(namedSkillMdPath, resolvedPath, dirName, scope);
|
|
19980
20265
|
if (skill)
|
|
19981
20266
|
skills.push(skill);
|
|
@@ -19992,6 +20277,43 @@ function loadSkillsFromDir(skillsDir, scope) {
|
|
|
19992
20277
|
}
|
|
19993
20278
|
return skills;
|
|
19994
20279
|
}
|
|
20280
|
+
async function loadSkillsFromDirAsync(skillsDir, scope) {
|
|
20281
|
+
const entries = await fs9.readdir(skillsDir, { withFileTypes: true }).catch(() => []);
|
|
20282
|
+
const skills = [];
|
|
20283
|
+
for (const entry of entries) {
|
|
20284
|
+
if (entry.name.startsWith("."))
|
|
20285
|
+
continue;
|
|
20286
|
+
const entryPath = join42(skillsDir, entry.name);
|
|
20287
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
20288
|
+
const resolvedPath = resolveSymlink(entryPath);
|
|
20289
|
+
const dirName = entry.name;
|
|
20290
|
+
const skillMdPath = join42(resolvedPath, "SKILL.md");
|
|
20291
|
+
try {
|
|
20292
|
+
await fs9.access(skillMdPath);
|
|
20293
|
+
const skill = await loadSkillFromPathAsync(skillMdPath, resolvedPath, dirName, scope);
|
|
20294
|
+
if (skill)
|
|
20295
|
+
skills.push(skill);
|
|
20296
|
+
continue;
|
|
20297
|
+
} catch {}
|
|
20298
|
+
const namedSkillMdPath = join42(resolvedPath, `${dirName}.md`);
|
|
20299
|
+
try {
|
|
20300
|
+
await fs9.access(namedSkillMdPath);
|
|
20301
|
+
const skill = await loadSkillFromPathAsync(namedSkillMdPath, resolvedPath, dirName, scope);
|
|
20302
|
+
if (skill)
|
|
20303
|
+
skills.push(skill);
|
|
20304
|
+
continue;
|
|
20305
|
+
} catch {}
|
|
20306
|
+
continue;
|
|
20307
|
+
}
|
|
20308
|
+
if (isMarkdownFile(entry)) {
|
|
20309
|
+
const skillName = basename(entry.name, ".md");
|
|
20310
|
+
const skill = await loadSkillFromPathAsync(entryPath, skillsDir, skillName, scope);
|
|
20311
|
+
if (skill)
|
|
20312
|
+
skills.push(skill);
|
|
20313
|
+
}
|
|
20314
|
+
}
|
|
20315
|
+
return skills;
|
|
20316
|
+
}
|
|
19995
20317
|
function skillsToRecord(skills) {
|
|
19996
20318
|
const result = {};
|
|
19997
20319
|
for (const skill of skills) {
|
|
@@ -20046,24 +20368,24 @@ function discoverSkills(options = {}) {
|
|
|
20046
20368
|
const userSkills = loadSkillsFromDir(userDir, "user");
|
|
20047
20369
|
return [...opencodeProjectSkills, ...projectSkills, ...opencodeGlobalSkills, ...userSkills];
|
|
20048
20370
|
}
|
|
20049
|
-
function
|
|
20371
|
+
async function discoverUserClaudeSkillsAsync() {
|
|
20050
20372
|
const userSkillsDir = join42(getClaudeConfigDir(), "skills");
|
|
20051
|
-
return
|
|
20373
|
+
return loadSkillsFromDirAsync(userSkillsDir, "user");
|
|
20052
20374
|
}
|
|
20053
|
-
function
|
|
20375
|
+
async function discoverProjectClaudeSkillsAsync() {
|
|
20054
20376
|
const projectSkillsDir = join42(process.cwd(), ".claude", "skills");
|
|
20055
|
-
return
|
|
20377
|
+
return loadSkillsFromDirAsync(projectSkillsDir, "project");
|
|
20056
20378
|
}
|
|
20057
|
-
function
|
|
20379
|
+
async function discoverOpencodeGlobalSkillsAsync() {
|
|
20058
20380
|
const opencodeSkillsDir = join42(homedir11(), ".config", "opencode", "skill");
|
|
20059
|
-
return
|
|
20381
|
+
return loadSkillsFromDirAsync(opencodeSkillsDir, "opencode");
|
|
20060
20382
|
}
|
|
20061
|
-
function
|
|
20383
|
+
async function discoverOpencodeProjectSkillsAsync() {
|
|
20062
20384
|
const opencodeProjectDir = join42(process.cwd(), ".opencode", "skill");
|
|
20063
|
-
return
|
|
20385
|
+
return loadSkillsFromDirAsync(opencodeProjectDir, "opencode-project");
|
|
20064
20386
|
}
|
|
20065
20387
|
// src/features/opencode-skill-loader/merger.ts
|
|
20066
|
-
import { readFileSync as readFileSync23, existsSync as
|
|
20388
|
+
import { readFileSync as readFileSync23, existsSync as existsSync35 } from "fs";
|
|
20067
20389
|
import { dirname as dirname7, resolve as resolve5, isAbsolute as isAbsolute2 } from "path";
|
|
20068
20390
|
import { homedir as homedir12 } from "os";
|
|
20069
20391
|
var SCOPE_PRIORITY = {
|
|
@@ -20111,7 +20433,7 @@ function resolveFilePath2(from, configDir) {
|
|
|
20111
20433
|
}
|
|
20112
20434
|
function loadSkillFromFile(filePath) {
|
|
20113
20435
|
try {
|
|
20114
|
-
if (!
|
|
20436
|
+
if (!existsSync35(filePath))
|
|
20115
20437
|
return null;
|
|
20116
20438
|
const content = readFileSync23(filePath, "utf-8");
|
|
20117
20439
|
const { data, body } = parseFrontmatter(content);
|
|
@@ -20267,7 +20589,7 @@ function mergeSkills(builtinSkills, config, userClaudeSkills, userOpencodeSkills
|
|
|
20267
20589
|
}
|
|
20268
20590
|
// src/hooks/auto-slash-command/executor.ts
|
|
20269
20591
|
function discoverCommandsFromDir(commandsDir, scope) {
|
|
20270
|
-
if (!
|
|
20592
|
+
if (!existsSync36(commandsDir)) {
|
|
20271
20593
|
return [];
|
|
20272
20594
|
}
|
|
20273
20595
|
const entries = readdirSync12(commandsDir, { withFileTypes: true });
|
|
@@ -20490,6 +20812,147 @@ ${EDIT_ERROR_REMINDER}`;
|
|
|
20490
20812
|
}
|
|
20491
20813
|
};
|
|
20492
20814
|
}
|
|
20815
|
+
// src/features/context-injector/collector.ts
|
|
20816
|
+
var PRIORITY_ORDER = {
|
|
20817
|
+
critical: 0,
|
|
20818
|
+
high: 1,
|
|
20819
|
+
normal: 2,
|
|
20820
|
+
low: 3
|
|
20821
|
+
};
|
|
20822
|
+
var CONTEXT_SEPARATOR = `
|
|
20823
|
+
|
|
20824
|
+
---
|
|
20825
|
+
|
|
20826
|
+
`;
|
|
20827
|
+
|
|
20828
|
+
class ContextCollector {
|
|
20829
|
+
sessions = new Map;
|
|
20830
|
+
register(sessionID, options) {
|
|
20831
|
+
if (!this.sessions.has(sessionID)) {
|
|
20832
|
+
this.sessions.set(sessionID, new Map);
|
|
20833
|
+
}
|
|
20834
|
+
const sessionMap = this.sessions.get(sessionID);
|
|
20835
|
+
const key = `${options.source}:${options.id}`;
|
|
20836
|
+
const entry = {
|
|
20837
|
+
id: options.id,
|
|
20838
|
+
source: options.source,
|
|
20839
|
+
content: options.content,
|
|
20840
|
+
priority: options.priority ?? "normal",
|
|
20841
|
+
timestamp: Date.now(),
|
|
20842
|
+
metadata: options.metadata
|
|
20843
|
+
};
|
|
20844
|
+
sessionMap.set(key, entry);
|
|
20845
|
+
}
|
|
20846
|
+
getPending(sessionID) {
|
|
20847
|
+
const sessionMap = this.sessions.get(sessionID);
|
|
20848
|
+
if (!sessionMap || sessionMap.size === 0) {
|
|
20849
|
+
return {
|
|
20850
|
+
merged: "",
|
|
20851
|
+
entries: [],
|
|
20852
|
+
hasContent: false
|
|
20853
|
+
};
|
|
20854
|
+
}
|
|
20855
|
+
const entries = this.sortEntries([...sessionMap.values()]);
|
|
20856
|
+
const merged = entries.map((e) => e.content).join(CONTEXT_SEPARATOR);
|
|
20857
|
+
return {
|
|
20858
|
+
merged,
|
|
20859
|
+
entries,
|
|
20860
|
+
hasContent: entries.length > 0
|
|
20861
|
+
};
|
|
20862
|
+
}
|
|
20863
|
+
consume(sessionID) {
|
|
20864
|
+
const pending = this.getPending(sessionID);
|
|
20865
|
+
this.clear(sessionID);
|
|
20866
|
+
return pending;
|
|
20867
|
+
}
|
|
20868
|
+
clear(sessionID) {
|
|
20869
|
+
this.sessions.delete(sessionID);
|
|
20870
|
+
}
|
|
20871
|
+
hasPending(sessionID) {
|
|
20872
|
+
const sessionMap = this.sessions.get(sessionID);
|
|
20873
|
+
return sessionMap !== undefined && sessionMap.size > 0;
|
|
20874
|
+
}
|
|
20875
|
+
sortEntries(entries) {
|
|
20876
|
+
return entries.sort((a, b) => {
|
|
20877
|
+
const priorityDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
|
|
20878
|
+
if (priorityDiff !== 0)
|
|
20879
|
+
return priorityDiff;
|
|
20880
|
+
return a.timestamp - b.timestamp;
|
|
20881
|
+
});
|
|
20882
|
+
}
|
|
20883
|
+
}
|
|
20884
|
+
var contextCollector = new ContextCollector;
|
|
20885
|
+
// src/features/context-injector/injector.ts
|
|
20886
|
+
function createContextInjectorHook(collector) {
|
|
20887
|
+
return {
|
|
20888
|
+
"chat.message": async (_input, _output) => {}
|
|
20889
|
+
};
|
|
20890
|
+
}
|
|
20891
|
+
function createContextInjectorMessagesTransformHook(collector) {
|
|
20892
|
+
return {
|
|
20893
|
+
"experimental.chat.messages.transform": async (_input, output) => {
|
|
20894
|
+
const { messages } = output;
|
|
20895
|
+
if (messages.length === 0) {
|
|
20896
|
+
return;
|
|
20897
|
+
}
|
|
20898
|
+
let lastUserMessageIndex = -1;
|
|
20899
|
+
for (let i2 = messages.length - 1;i2 >= 0; i2--) {
|
|
20900
|
+
if (messages[i2].info.role === "user") {
|
|
20901
|
+
lastUserMessageIndex = i2;
|
|
20902
|
+
break;
|
|
20903
|
+
}
|
|
20904
|
+
}
|
|
20905
|
+
if (lastUserMessageIndex === -1) {
|
|
20906
|
+
return;
|
|
20907
|
+
}
|
|
20908
|
+
const lastUserMessage = messages[lastUserMessageIndex];
|
|
20909
|
+
const sessionID = lastUserMessage.info.sessionID;
|
|
20910
|
+
if (!sessionID) {
|
|
20911
|
+
return;
|
|
20912
|
+
}
|
|
20913
|
+
if (!collector.hasPending(sessionID)) {
|
|
20914
|
+
return;
|
|
20915
|
+
}
|
|
20916
|
+
const pending = collector.consume(sessionID);
|
|
20917
|
+
if (!pending.hasContent) {
|
|
20918
|
+
return;
|
|
20919
|
+
}
|
|
20920
|
+
const refInfo = lastUserMessage.info;
|
|
20921
|
+
const syntheticMessageId = `synthetic_ctx_${Date.now()}`;
|
|
20922
|
+
const syntheticPartId = `synthetic_ctx_part_${Date.now()}`;
|
|
20923
|
+
const now = Date.now();
|
|
20924
|
+
const syntheticMessage = {
|
|
20925
|
+
info: {
|
|
20926
|
+
id: syntheticMessageId,
|
|
20927
|
+
sessionID,
|
|
20928
|
+
role: "user",
|
|
20929
|
+
time: { created: now },
|
|
20930
|
+
agent: refInfo.agent ?? "Sisyphus",
|
|
20931
|
+
model: refInfo.model ?? { providerID: "unknown", modelID: "unknown" },
|
|
20932
|
+
path: refInfo.path ?? { cwd: "/", root: "/" }
|
|
20933
|
+
},
|
|
20934
|
+
parts: [
|
|
20935
|
+
{
|
|
20936
|
+
id: syntheticPartId,
|
|
20937
|
+
sessionID,
|
|
20938
|
+
messageID: syntheticMessageId,
|
|
20939
|
+
type: "text",
|
|
20940
|
+
text: pending.merged,
|
|
20941
|
+
synthetic: true,
|
|
20942
|
+
time: { start: now, end: now }
|
|
20943
|
+
}
|
|
20944
|
+
]
|
|
20945
|
+
};
|
|
20946
|
+
messages.splice(lastUserMessageIndex, 0, syntheticMessage);
|
|
20947
|
+
log("[context-injector] Injected synthetic message from collector", {
|
|
20948
|
+
sessionID,
|
|
20949
|
+
insertIndex: lastUserMessageIndex,
|
|
20950
|
+
contextLength: pending.merged.length,
|
|
20951
|
+
newMessageCount: messages.length
|
|
20952
|
+
});
|
|
20953
|
+
}
|
|
20954
|
+
};
|
|
20955
|
+
}
|
|
20493
20956
|
// src/auth/antigravity/constants.ts
|
|
20494
20957
|
var ANTIGRAVITY_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
|
|
20495
20958
|
var ANTIGRAVITY_CLIENT_SECRET = "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";
|
|
@@ -22337,7 +22800,7 @@ function createBuiltinSkills() {
|
|
|
22337
22800
|
return [playwrightSkill];
|
|
22338
22801
|
}
|
|
22339
22802
|
// src/features/claude-code-mcp-loader/loader.ts
|
|
22340
|
-
import { existsSync as
|
|
22803
|
+
import { existsSync as existsSync37, readFileSync as readFileSync25 } from "fs";
|
|
22341
22804
|
import { join as join44 } from "path";
|
|
22342
22805
|
|
|
22343
22806
|
// src/features/claude-code-mcp-loader/env-expander.ts
|
|
@@ -22413,7 +22876,7 @@ function getMcpConfigPaths() {
|
|
|
22413
22876
|
];
|
|
22414
22877
|
}
|
|
22415
22878
|
async function loadMcpConfigFile(filePath) {
|
|
22416
|
-
if (!
|
|
22879
|
+
if (!existsSync37(filePath)) {
|
|
22417
22880
|
return null;
|
|
22418
22881
|
}
|
|
22419
22882
|
try {
|
|
@@ -22428,7 +22891,7 @@ function getSystemMcpServerNames() {
|
|
|
22428
22891
|
const names = new Set;
|
|
22429
22892
|
const paths = getMcpConfigPaths();
|
|
22430
22893
|
for (const { path: path7 } of paths) {
|
|
22431
|
-
if (!
|
|
22894
|
+
if (!existsSync37(path7))
|
|
22432
22895
|
continue;
|
|
22433
22896
|
try {
|
|
22434
22897
|
const content = readFileSync25(path7, "utf-8");
|
|
@@ -22541,7 +23004,18 @@ var LSP_INSTALL_HINTS = {
|
|
|
22541
23004
|
"lua-ls": "See https://github.com/LuaLS/lua-language-server",
|
|
22542
23005
|
php: "npm install -g intelephense",
|
|
22543
23006
|
dart: "Included with Dart SDK",
|
|
22544
|
-
"terraform-ls": "See https://github.com/hashicorp/terraform-ls"
|
|
23007
|
+
"terraform-ls": "See https://github.com/hashicorp/terraform-ls",
|
|
23008
|
+
terraform: "See https://github.com/hashicorp/terraform-ls",
|
|
23009
|
+
prisma: "npm install -g prisma",
|
|
23010
|
+
"ocaml-lsp": "opam install ocaml-lsp-server",
|
|
23011
|
+
texlab: "See https://github.com/latex-lsp/texlab",
|
|
23012
|
+
dockerfile: "npm install -g dockerfile-language-server-nodejs",
|
|
23013
|
+
gleam: "See https://gleam.run/getting-started/installing/",
|
|
23014
|
+
"clojure-lsp": "See https://clojure-lsp.io/installation/",
|
|
23015
|
+
nixd: "nix profile install nixpkgs#nixd",
|
|
23016
|
+
tinymist: "See https://github.com/Myriad-Dreamin/tinymist",
|
|
23017
|
+
"haskell-language-server": "ghcup install hls",
|
|
23018
|
+
bash: "npm install -g bash-language-server"
|
|
22545
23019
|
};
|
|
22546
23020
|
var BUILTIN_SERVERS = {
|
|
22547
23021
|
typescript: {
|
|
@@ -22646,6 +23120,10 @@ var BUILTIN_SERVERS = {
|
|
|
22646
23120
|
command: ["astro-ls", "--stdio"],
|
|
22647
23121
|
extensions: [".astro"]
|
|
22648
23122
|
},
|
|
23123
|
+
bash: {
|
|
23124
|
+
command: ["bash-language-server", "start"],
|
|
23125
|
+
extensions: [".sh", ".bash", ".zsh", ".ksh"]
|
|
23126
|
+
},
|
|
22649
23127
|
"bash-ls": {
|
|
22650
23128
|
command: ["bash-language-server", "start"],
|
|
22651
23129
|
extensions: [".sh", ".bash", ".zsh", ".ksh"]
|
|
@@ -22670,9 +23148,49 @@ var BUILTIN_SERVERS = {
|
|
|
22670
23148
|
command: ["dart", "language-server", "--lsp"],
|
|
22671
23149
|
extensions: [".dart"]
|
|
22672
23150
|
},
|
|
23151
|
+
terraform: {
|
|
23152
|
+
command: ["terraform-ls", "serve"],
|
|
23153
|
+
extensions: [".tf", ".tfvars"]
|
|
23154
|
+
},
|
|
22673
23155
|
"terraform-ls": {
|
|
22674
23156
|
command: ["terraform-ls", "serve"],
|
|
22675
23157
|
extensions: [".tf", ".tfvars"]
|
|
23158
|
+
},
|
|
23159
|
+
prisma: {
|
|
23160
|
+
command: ["prisma", "language-server"],
|
|
23161
|
+
extensions: [".prisma"]
|
|
23162
|
+
},
|
|
23163
|
+
"ocaml-lsp": {
|
|
23164
|
+
command: ["ocamllsp"],
|
|
23165
|
+
extensions: [".ml", ".mli"]
|
|
23166
|
+
},
|
|
23167
|
+
texlab: {
|
|
23168
|
+
command: ["texlab"],
|
|
23169
|
+
extensions: [".tex", ".bib"]
|
|
23170
|
+
},
|
|
23171
|
+
dockerfile: {
|
|
23172
|
+
command: ["docker-langserver", "--stdio"],
|
|
23173
|
+
extensions: [".dockerfile"]
|
|
23174
|
+
},
|
|
23175
|
+
gleam: {
|
|
23176
|
+
command: ["gleam", "lsp"],
|
|
23177
|
+
extensions: [".gleam"]
|
|
23178
|
+
},
|
|
23179
|
+
"clojure-lsp": {
|
|
23180
|
+
command: ["clojure-lsp", "listen"],
|
|
23181
|
+
extensions: [".clj", ".cljs", ".cljc", ".edn"]
|
|
23182
|
+
},
|
|
23183
|
+
nixd: {
|
|
23184
|
+
command: ["nixd"],
|
|
23185
|
+
extensions: [".nix"]
|
|
23186
|
+
},
|
|
23187
|
+
tinymist: {
|
|
23188
|
+
command: ["tinymist"],
|
|
23189
|
+
extensions: [".typ", ".typc"]
|
|
23190
|
+
},
|
|
23191
|
+
"haskell-language-server": {
|
|
23192
|
+
command: ["haskell-language-server-wrapper", "--lsp"],
|
|
23193
|
+
extensions: [".hs", ".lhs"]
|
|
22676
23194
|
}
|
|
22677
23195
|
};
|
|
22678
23196
|
var EXT_TO_LANG = {
|
|
@@ -22788,6 +23306,14 @@ var EXT_TO_LANG = {
|
|
|
22788
23306
|
".tf": "terraform",
|
|
22789
23307
|
".tfvars": "terraform-vars",
|
|
22790
23308
|
".hcl": "hcl",
|
|
23309
|
+
".nix": "nix",
|
|
23310
|
+
".typ": "typst",
|
|
23311
|
+
".typc": "typst",
|
|
23312
|
+
".ets": "typescript",
|
|
23313
|
+
".lhs": "haskell",
|
|
23314
|
+
".kt": "kotlin",
|
|
23315
|
+
".kts": "kotlin",
|
|
23316
|
+
".prisma": "prisma",
|
|
22791
23317
|
".h": "c",
|
|
22792
23318
|
".hpp": "cpp",
|
|
22793
23319
|
".hh": "cpp",
|
|
@@ -22800,11 +23326,11 @@ var EXT_TO_LANG = {
|
|
|
22800
23326
|
".gql": "graphql"
|
|
22801
23327
|
};
|
|
22802
23328
|
// src/tools/lsp/config.ts
|
|
22803
|
-
import { existsSync as
|
|
23329
|
+
import { existsSync as existsSync38, readFileSync as readFileSync26 } from "fs";
|
|
22804
23330
|
import { join as join45 } from "path";
|
|
22805
23331
|
import { homedir as homedir14 } from "os";
|
|
22806
23332
|
function loadJsonFile(path7) {
|
|
22807
|
-
if (!
|
|
23333
|
+
if (!existsSync38(path7))
|
|
22808
23334
|
return null;
|
|
22809
23335
|
try {
|
|
22810
23336
|
return JSON.parse(readFileSync26(path7, "utf-8"));
|
|
@@ -22930,7 +23456,7 @@ function isServerInstalled(command) {
|
|
|
22930
23456
|
return false;
|
|
22931
23457
|
const cmd = command[0];
|
|
22932
23458
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
22933
|
-
if (
|
|
23459
|
+
if (existsSync38(cmd))
|
|
22934
23460
|
return true;
|
|
22935
23461
|
}
|
|
22936
23462
|
const isWindows2 = process.platform === "win32";
|
|
@@ -22939,7 +23465,7 @@ function isServerInstalled(command) {
|
|
|
22939
23465
|
const pathSeparator = isWindows2 ? ";" : ":";
|
|
22940
23466
|
const paths = pathEnv.split(pathSeparator);
|
|
22941
23467
|
for (const p of paths) {
|
|
22942
|
-
if (
|
|
23468
|
+
if (existsSync38(join45(p, cmd)) || existsSync38(join45(p, cmd + ext))) {
|
|
22943
23469
|
return true;
|
|
22944
23470
|
}
|
|
22945
23471
|
}
|
|
@@ -22953,7 +23479,7 @@ function isServerInstalled(command) {
|
|
|
22953
23479
|
join45(homedir14(), ".config", "opencode", "node_modules", ".bin", cmd + ext)
|
|
22954
23480
|
];
|
|
22955
23481
|
for (const p of additionalPaths) {
|
|
22956
|
-
if (
|
|
23482
|
+
if (existsSync38(p)) {
|
|
22957
23483
|
return true;
|
|
22958
23484
|
}
|
|
22959
23485
|
}
|
|
@@ -23552,16 +24078,16 @@ ${msg}`);
|
|
|
23552
24078
|
// src/tools/lsp/utils.ts
|
|
23553
24079
|
import { extname as extname2, resolve as resolve7 } from "path";
|
|
23554
24080
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
23555
|
-
import { existsSync as
|
|
24081
|
+
import { existsSync as existsSync39, readFileSync as readFileSync28, writeFileSync as writeFileSync15 } from "fs";
|
|
23556
24082
|
function findWorkspaceRoot(filePath) {
|
|
23557
24083
|
let dir = resolve7(filePath);
|
|
23558
|
-
if (!
|
|
24084
|
+
if (!existsSync39(dir) || !__require("fs").statSync(dir).isDirectory()) {
|
|
23559
24085
|
dir = __require("path").dirname(dir);
|
|
23560
24086
|
}
|
|
23561
24087
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
23562
24088
|
while (dir !== "/") {
|
|
23563
24089
|
for (const marker of markers) {
|
|
23564
|
-
if (
|
|
24090
|
+
if (existsSync39(__require("path").join(dir, marker))) {
|
|
23565
24091
|
return dir;
|
|
23566
24092
|
}
|
|
23567
24093
|
}
|
|
@@ -36525,11 +37051,11 @@ var lsp_code_action_resolve = tool({
|
|
|
36525
37051
|
// src/tools/ast-grep/constants.ts
|
|
36526
37052
|
import { createRequire as createRequire4 } from "module";
|
|
36527
37053
|
import { dirname as dirname9, join as join47 } from "path";
|
|
36528
|
-
import { existsSync as
|
|
37054
|
+
import { existsSync as existsSync41, statSync as statSync4 } from "fs";
|
|
36529
37055
|
|
|
36530
37056
|
// src/tools/ast-grep/downloader.ts
|
|
36531
37057
|
var {spawn: spawn6 } = globalThis.Bun;
|
|
36532
|
-
import { existsSync as
|
|
37058
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync11, chmodSync as chmodSync2, unlinkSync as unlinkSync10 } from "fs";
|
|
36533
37059
|
import { join as join46 } from "path";
|
|
36534
37060
|
import { homedir as homedir15 } from "os";
|
|
36535
37061
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -36568,7 +37094,7 @@ function getBinaryName3() {
|
|
|
36568
37094
|
}
|
|
36569
37095
|
function getCachedBinaryPath2() {
|
|
36570
37096
|
const binaryPath = join46(getCacheDir3(), getBinaryName3());
|
|
36571
|
-
return
|
|
37097
|
+
return existsSync40(binaryPath) ? binaryPath : null;
|
|
36572
37098
|
}
|
|
36573
37099
|
async function extractZip2(archivePath, destDir) {
|
|
36574
37100
|
const proc = process.platform === "win32" ? spawn6([
|
|
@@ -36595,7 +37121,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
36595
37121
|
const cacheDir = getCacheDir3();
|
|
36596
37122
|
const binaryName = getBinaryName3();
|
|
36597
37123
|
const binaryPath = join46(cacheDir, binaryName);
|
|
36598
|
-
if (
|
|
37124
|
+
if (existsSync40(binaryPath)) {
|
|
36599
37125
|
return binaryPath;
|
|
36600
37126
|
}
|
|
36601
37127
|
const { arch, os: os6 } = platformInfo;
|
|
@@ -36603,7 +37129,7 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
36603
37129
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version2}/${assetName}`;
|
|
36604
37130
|
console.log(`[oh-my-opencode] Downloading ast-grep binary...`);
|
|
36605
37131
|
try {
|
|
36606
|
-
if (!
|
|
37132
|
+
if (!existsSync40(cacheDir)) {
|
|
36607
37133
|
mkdirSync11(cacheDir, { recursive: true });
|
|
36608
37134
|
}
|
|
36609
37135
|
const response2 = await fetch(downloadUrl, { redirect: "follow" });
|
|
@@ -36614,10 +37140,10 @@ async function downloadAstGrep(version2 = DEFAULT_VERSION) {
|
|
|
36614
37140
|
const arrayBuffer = await response2.arrayBuffer();
|
|
36615
37141
|
await Bun.write(archivePath, arrayBuffer);
|
|
36616
37142
|
await extractZip2(archivePath, cacheDir);
|
|
36617
|
-
if (
|
|
37143
|
+
if (existsSync40(archivePath)) {
|
|
36618
37144
|
unlinkSync10(archivePath);
|
|
36619
37145
|
}
|
|
36620
|
-
if (process.platform !== "win32" &&
|
|
37146
|
+
if (process.platform !== "win32" && existsSync40(binaryPath)) {
|
|
36621
37147
|
chmodSync2(binaryPath, 493);
|
|
36622
37148
|
}
|
|
36623
37149
|
console.log(`[oh-my-opencode] ast-grep binary ready.`);
|
|
@@ -36669,7 +37195,7 @@ function findSgCliPathSync() {
|
|
|
36669
37195
|
const cliPkgPath = require2.resolve("@ast-grep/cli/package.json");
|
|
36670
37196
|
const cliDir = dirname9(cliPkgPath);
|
|
36671
37197
|
const sgPath = join47(cliDir, binaryName);
|
|
36672
|
-
if (
|
|
37198
|
+
if (existsSync41(sgPath) && isValidBinary(sgPath)) {
|
|
36673
37199
|
return sgPath;
|
|
36674
37200
|
}
|
|
36675
37201
|
} catch {}
|
|
@@ -36681,7 +37207,7 @@ function findSgCliPathSync() {
|
|
|
36681
37207
|
const pkgDir = dirname9(pkgPath);
|
|
36682
37208
|
const astGrepName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
36683
37209
|
const binaryPath = join47(pkgDir, astGrepName);
|
|
36684
|
-
if (
|
|
37210
|
+
if (existsSync41(binaryPath) && isValidBinary(binaryPath)) {
|
|
36685
37211
|
return binaryPath;
|
|
36686
37212
|
}
|
|
36687
37213
|
} catch {}
|
|
@@ -36689,7 +37215,7 @@ function findSgCliPathSync() {
|
|
|
36689
37215
|
if (process.platform === "darwin") {
|
|
36690
37216
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
36691
37217
|
for (const path7 of homebrewPaths) {
|
|
36692
|
-
if (
|
|
37218
|
+
if (existsSync41(path7) && isValidBinary(path7)) {
|
|
36693
37219
|
return path7;
|
|
36694
37220
|
}
|
|
36695
37221
|
}
|
|
@@ -36744,11 +37270,11 @@ var DEFAULT_MAX_MATCHES = 500;
|
|
|
36744
37270
|
|
|
36745
37271
|
// src/tools/ast-grep/cli.ts
|
|
36746
37272
|
var {spawn: spawn7 } = globalThis.Bun;
|
|
36747
|
-
import { existsSync as
|
|
37273
|
+
import { existsSync as existsSync42 } from "fs";
|
|
36748
37274
|
var resolvedCliPath3 = null;
|
|
36749
37275
|
var initPromise2 = null;
|
|
36750
37276
|
async function getAstGrepPath() {
|
|
36751
|
-
if (resolvedCliPath3 !== null &&
|
|
37277
|
+
if (resolvedCliPath3 !== null && existsSync42(resolvedCliPath3)) {
|
|
36752
37278
|
return resolvedCliPath3;
|
|
36753
37279
|
}
|
|
36754
37280
|
if (initPromise2) {
|
|
@@ -36756,7 +37282,7 @@ async function getAstGrepPath() {
|
|
|
36756
37282
|
}
|
|
36757
37283
|
initPromise2 = (async () => {
|
|
36758
37284
|
const syncPath = findSgCliPathSync();
|
|
36759
|
-
if (syncPath &&
|
|
37285
|
+
if (syncPath && existsSync42(syncPath)) {
|
|
36760
37286
|
resolvedCliPath3 = syncPath;
|
|
36761
37287
|
setSgCliPath(syncPath);
|
|
36762
37288
|
return syncPath;
|
|
@@ -36790,7 +37316,7 @@ async function runSg(options) {
|
|
|
36790
37316
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
36791
37317
|
args.push(...paths);
|
|
36792
37318
|
let cliPath = getSgCliPath();
|
|
36793
|
-
if (!
|
|
37319
|
+
if (!existsSync42(cliPath) && cliPath !== "sg") {
|
|
36794
37320
|
const downloadedPath = await getAstGrepPath();
|
|
36795
37321
|
if (downloadedPath) {
|
|
36796
37322
|
cliPath = downloadedPath;
|
|
@@ -37054,12 +37580,12 @@ var ast_grep_replace = tool({
|
|
|
37054
37580
|
var {spawn: spawn9 } = globalThis.Bun;
|
|
37055
37581
|
|
|
37056
37582
|
// src/tools/grep/constants.ts
|
|
37057
|
-
import { existsSync as
|
|
37583
|
+
import { existsSync as existsSync44 } from "fs";
|
|
37058
37584
|
import { join as join49, dirname as dirname10 } from "path";
|
|
37059
37585
|
import { spawnSync } from "child_process";
|
|
37060
37586
|
|
|
37061
37587
|
// src/tools/grep/downloader.ts
|
|
37062
|
-
import { existsSync as
|
|
37588
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync12, chmodSync as chmodSync3, unlinkSync as unlinkSync11, readdirSync as readdirSync13 } from "fs";
|
|
37063
37589
|
import { join as join48 } from "path";
|
|
37064
37590
|
var {spawn: spawn8 } = globalThis.Bun;
|
|
37065
37591
|
function findFileRecursive(dir, filename) {
|
|
@@ -37169,7 +37695,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
37169
37695
|
}
|
|
37170
37696
|
const installDir = getInstallDir();
|
|
37171
37697
|
const rgPath = getRgPath();
|
|
37172
|
-
if (
|
|
37698
|
+
if (existsSync43(rgPath)) {
|
|
37173
37699
|
return rgPath;
|
|
37174
37700
|
}
|
|
37175
37701
|
mkdirSync12(installDir, { recursive: true });
|
|
@@ -37186,12 +37712,12 @@ async function downloadAndInstallRipgrep() {
|
|
|
37186
37712
|
if (process.platform !== "win32") {
|
|
37187
37713
|
chmodSync3(rgPath, 493);
|
|
37188
37714
|
}
|
|
37189
|
-
if (!
|
|
37715
|
+
if (!existsSync43(rgPath)) {
|
|
37190
37716
|
throw new Error("ripgrep binary not found after extraction");
|
|
37191
37717
|
}
|
|
37192
37718
|
return rgPath;
|
|
37193
37719
|
} finally {
|
|
37194
|
-
if (
|
|
37720
|
+
if (existsSync43(archivePath)) {
|
|
37195
37721
|
try {
|
|
37196
37722
|
unlinkSync11(archivePath);
|
|
37197
37723
|
} catch {}
|
|
@@ -37200,7 +37726,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
37200
37726
|
}
|
|
37201
37727
|
function getInstalledRipgrepPath() {
|
|
37202
37728
|
const rgPath = getRgPath();
|
|
37203
|
-
return
|
|
37729
|
+
return existsSync43(rgPath) ? rgPath : null;
|
|
37204
37730
|
}
|
|
37205
37731
|
|
|
37206
37732
|
// src/tools/grep/constants.ts
|
|
@@ -37231,7 +37757,7 @@ function getOpenCodeBundledRg() {
|
|
|
37231
37757
|
join49(execDir, "..", "libexec", rgName)
|
|
37232
37758
|
];
|
|
37233
37759
|
for (const candidate of candidates) {
|
|
37234
|
-
if (
|
|
37760
|
+
if (existsSync44(candidate)) {
|
|
37235
37761
|
return candidate;
|
|
37236
37762
|
}
|
|
37237
37763
|
}
|
|
@@ -37684,10 +38210,10 @@ var glob = tool({
|
|
|
37684
38210
|
}
|
|
37685
38211
|
});
|
|
37686
38212
|
// src/tools/slashcommand/tools.ts
|
|
37687
|
-
import { existsSync as
|
|
38213
|
+
import { existsSync as existsSync45, readdirSync as readdirSync14, readFileSync as readFileSync29 } from "fs";
|
|
37688
38214
|
import { join as join50, basename as basename3, dirname as dirname11 } from "path";
|
|
37689
38215
|
function discoverCommandsFromDir2(commandsDir, scope) {
|
|
37690
|
-
if (!
|
|
38216
|
+
if (!existsSync45(commandsDir)) {
|
|
37691
38217
|
return [];
|
|
37692
38218
|
}
|
|
37693
38219
|
const entries = readdirSync14(commandsDir, { withFileTypes: true });
|
|
@@ -37936,11 +38462,11 @@ Has Todos: Yes (12 items, 8 completed)
|
|
|
37936
38462
|
Has Transcript: Yes (234 entries)`;
|
|
37937
38463
|
|
|
37938
38464
|
// src/tools/session-manager/storage.ts
|
|
37939
|
-
import { existsSync as
|
|
38465
|
+
import { existsSync as existsSync46, readdirSync as readdirSync15 } from "fs";
|
|
37940
38466
|
import { readdir, readFile } from "fs/promises";
|
|
37941
38467
|
import { join as join52 } from "path";
|
|
37942
38468
|
async function getMainSessions(options) {
|
|
37943
|
-
if (!
|
|
38469
|
+
if (!existsSync46(SESSION_STORAGE))
|
|
37944
38470
|
return [];
|
|
37945
38471
|
const sessions = [];
|
|
37946
38472
|
try {
|
|
@@ -37972,7 +38498,7 @@ async function getMainSessions(options) {
|
|
|
37972
38498
|
return sessions.sort((a, b) => b.time.updated - a.time.updated);
|
|
37973
38499
|
}
|
|
37974
38500
|
async function getAllSessions() {
|
|
37975
|
-
if (!
|
|
38501
|
+
if (!existsSync46(MESSAGE_STORAGE4))
|
|
37976
38502
|
return [];
|
|
37977
38503
|
const sessions = [];
|
|
37978
38504
|
async function scanDirectory(dir) {
|
|
@@ -37997,16 +38523,16 @@ async function getAllSessions() {
|
|
|
37997
38523
|
return [...new Set(sessions)];
|
|
37998
38524
|
}
|
|
37999
38525
|
function getMessageDir9(sessionID) {
|
|
38000
|
-
if (!
|
|
38526
|
+
if (!existsSync46(MESSAGE_STORAGE4))
|
|
38001
38527
|
return "";
|
|
38002
38528
|
const directPath = join52(MESSAGE_STORAGE4, sessionID);
|
|
38003
|
-
if (
|
|
38529
|
+
if (existsSync46(directPath)) {
|
|
38004
38530
|
return directPath;
|
|
38005
38531
|
}
|
|
38006
38532
|
try {
|
|
38007
38533
|
for (const dir of readdirSync15(MESSAGE_STORAGE4)) {
|
|
38008
38534
|
const sessionPath = join52(MESSAGE_STORAGE4, dir, sessionID);
|
|
38009
|
-
if (
|
|
38535
|
+
if (existsSync46(sessionPath)) {
|
|
38010
38536
|
return sessionPath;
|
|
38011
38537
|
}
|
|
38012
38538
|
}
|
|
@@ -38020,7 +38546,7 @@ function sessionExists(sessionID) {
|
|
|
38020
38546
|
}
|
|
38021
38547
|
async function readSessionMessages(sessionID) {
|
|
38022
38548
|
const messageDir = getMessageDir9(sessionID);
|
|
38023
|
-
if (!messageDir || !
|
|
38549
|
+
if (!messageDir || !existsSync46(messageDir))
|
|
38024
38550
|
return [];
|
|
38025
38551
|
const messages = [];
|
|
38026
38552
|
try {
|
|
@@ -38056,7 +38582,7 @@ async function readSessionMessages(sessionID) {
|
|
|
38056
38582
|
}
|
|
38057
38583
|
async function readParts2(messageID) {
|
|
38058
38584
|
const partDir = join52(PART_STORAGE4, messageID);
|
|
38059
|
-
if (!
|
|
38585
|
+
if (!existsSync46(partDir))
|
|
38060
38586
|
return [];
|
|
38061
38587
|
const parts = [];
|
|
38062
38588
|
try {
|
|
@@ -38077,7 +38603,7 @@ async function readParts2(messageID) {
|
|
|
38077
38603
|
return parts.sort((a, b) => a.id.localeCompare(b.id));
|
|
38078
38604
|
}
|
|
38079
38605
|
async function readSessionTodos(sessionID) {
|
|
38080
|
-
if (!
|
|
38606
|
+
if (!existsSync46(TODO_DIR2))
|
|
38081
38607
|
return [];
|
|
38082
38608
|
try {
|
|
38083
38609
|
const allFiles = await readdir(TODO_DIR2);
|
|
@@ -38104,10 +38630,10 @@ async function readSessionTodos(sessionID) {
|
|
|
38104
38630
|
return [];
|
|
38105
38631
|
}
|
|
38106
38632
|
async function readSessionTranscript(sessionID) {
|
|
38107
|
-
if (!
|
|
38633
|
+
if (!existsSync46(TRANSCRIPT_DIR2))
|
|
38108
38634
|
return 0;
|
|
38109
38635
|
const transcriptFile = join52(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
38110
|
-
if (!
|
|
38636
|
+
if (!existsSync46(transcriptFile))
|
|
38111
38637
|
return 0;
|
|
38112
38638
|
try {
|
|
38113
38639
|
const content = await readFile(transcriptFile, "utf-8");
|
|
@@ -38487,6 +39013,12 @@ async function getTmuxPath() {
|
|
|
38487
39013
|
function getCachedTmuxPath() {
|
|
38488
39014
|
return tmuxPath;
|
|
38489
39015
|
}
|
|
39016
|
+
function startBackgroundCheck2() {
|
|
39017
|
+
if (!initPromise3) {
|
|
39018
|
+
initPromise3 = getTmuxPath();
|
|
39019
|
+
initPromise3.catch(() => {});
|
|
39020
|
+
}
|
|
39021
|
+
}
|
|
38490
39022
|
|
|
38491
39023
|
// src/tools/interactive-bash/tools.ts
|
|
38492
39024
|
function tokenizeCommand2(cmd) {
|
|
@@ -38614,7 +39146,12 @@ function formatSkillsXml(skills) {
|
|
|
38614
39146
|
${skillsXml}
|
|
38615
39147
|
</available_skills>`;
|
|
38616
39148
|
}
|
|
38617
|
-
function extractSkillBody(skill) {
|
|
39149
|
+
async function extractSkillBody(skill) {
|
|
39150
|
+
if (skill.lazyContent) {
|
|
39151
|
+
const fullTemplate = await skill.lazyContent.load();
|
|
39152
|
+
const templateMatch2 = fullTemplate.match(/<skill-instruction>([\s\S]*?)<\/skill-instruction>/);
|
|
39153
|
+
return templateMatch2 ? templateMatch2[1].trim() : fullTemplate;
|
|
39154
|
+
}
|
|
38618
39155
|
if (skill.path) {
|
|
38619
39156
|
const content = readFileSync30(skill.path, "utf-8");
|
|
38620
39157
|
const { body } = parseFrontmatter(content);
|
|
@@ -38698,7 +39235,7 @@ function createSkillTool(options = {}) {
|
|
|
38698
39235
|
const available = skills.map((s) => s.name).join(", ");
|
|
38699
39236
|
throw new Error(`Skill "${args.name}" not found. Available skills: ${available || "none"}`);
|
|
38700
39237
|
}
|
|
38701
|
-
const body = extractSkillBody(skill);
|
|
39238
|
+
const body = await extractSkillBody(skill);
|
|
38702
39239
|
const dir = skill.path ? dirname12(skill.path) : skill.resolvedPath || process.cwd();
|
|
38703
39240
|
const output = [
|
|
38704
39241
|
`## Skill: ${skill.name}`,
|
|
@@ -38864,7 +39401,7 @@ function createSkillMcpTool(options) {
|
|
|
38864
39401
|
});
|
|
38865
39402
|
}
|
|
38866
39403
|
// src/tools/background-task/tools.ts
|
|
38867
|
-
import { existsSync as
|
|
39404
|
+
import { existsSync as existsSync47, readdirSync as readdirSync16 } from "fs";
|
|
38868
39405
|
import { join as join53 } from "path";
|
|
38869
39406
|
|
|
38870
39407
|
// src/tools/background-task/constants.ts
|
|
@@ -38876,14 +39413,14 @@ var BACKGROUND_CANCEL_DESCRIPTION = `Cancel running background task(s). Use all=
|
|
|
38876
39413
|
|
|
38877
39414
|
// src/tools/background-task/tools.ts
|
|
38878
39415
|
function getMessageDir10(sessionID) {
|
|
38879
|
-
if (!
|
|
39416
|
+
if (!existsSync47(MESSAGE_STORAGE))
|
|
38880
39417
|
return null;
|
|
38881
39418
|
const directPath = join53(MESSAGE_STORAGE, sessionID);
|
|
38882
|
-
if (
|
|
39419
|
+
if (existsSync47(directPath))
|
|
38883
39420
|
return directPath;
|
|
38884
39421
|
for (const dir of readdirSync16(MESSAGE_STORAGE)) {
|
|
38885
39422
|
const sessionPath = join53(MESSAGE_STORAGE, dir, sessionID);
|
|
38886
|
-
if (
|
|
39423
|
+
if (existsSync47(sessionPath))
|
|
38887
39424
|
return sessionPath;
|
|
38888
39425
|
}
|
|
38889
39426
|
return null;
|
|
@@ -39469,18 +40006,18 @@ var builtinTools = {
|
|
|
39469
40006
|
session_info
|
|
39470
40007
|
};
|
|
39471
40008
|
// src/features/background-agent/manager.ts
|
|
39472
|
-
import { existsSync as
|
|
40009
|
+
import { existsSync as existsSync48, readdirSync as readdirSync17 } from "fs";
|
|
39473
40010
|
import { join as join54 } from "path";
|
|
39474
40011
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
39475
40012
|
function getMessageDir11(sessionID) {
|
|
39476
|
-
if (!
|
|
40013
|
+
if (!existsSync48(MESSAGE_STORAGE))
|
|
39477
40014
|
return null;
|
|
39478
40015
|
const directPath = join54(MESSAGE_STORAGE, sessionID);
|
|
39479
|
-
if (
|
|
40016
|
+
if (existsSync48(directPath))
|
|
39480
40017
|
return directPath;
|
|
39481
40018
|
for (const dir of readdirSync17(MESSAGE_STORAGE)) {
|
|
39482
40019
|
const sessionPath = join54(MESSAGE_STORAGE, dir, sessionID);
|
|
39483
|
-
if (
|
|
40020
|
+
if (existsSync48(sessionPath))
|
|
39484
40021
|
return sessionPath;
|
|
39485
40022
|
}
|
|
39486
40023
|
return null;
|
|
@@ -42399,21 +42936,89 @@ function isElectron() {
|
|
|
42399
42936
|
return "type" in process2;
|
|
42400
42937
|
}
|
|
42401
42938
|
|
|
42939
|
+
// src/features/skill-mcp-manager/env-cleaner.ts
|
|
42940
|
+
var EXCLUDED_ENV_PATTERNS = [
|
|
42941
|
+
/^NPM_CONFIG_/i,
|
|
42942
|
+
/^npm_config_/,
|
|
42943
|
+
/^YARN_/,
|
|
42944
|
+
/^PNPM_/,
|
|
42945
|
+
/^NO_UPDATE_NOTIFIER$/
|
|
42946
|
+
];
|
|
42947
|
+
function createCleanMcpEnvironment(customEnv = {}) {
|
|
42948
|
+
const cleanEnv = {};
|
|
42949
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
42950
|
+
if (value === undefined)
|
|
42951
|
+
continue;
|
|
42952
|
+
const shouldExclude = EXCLUDED_ENV_PATTERNS.some((pattern) => pattern.test(key));
|
|
42953
|
+
if (!shouldExclude) {
|
|
42954
|
+
cleanEnv[key] = value;
|
|
42955
|
+
}
|
|
42956
|
+
}
|
|
42957
|
+
Object.assign(cleanEnv, customEnv);
|
|
42958
|
+
return cleanEnv;
|
|
42959
|
+
}
|
|
42960
|
+
|
|
42402
42961
|
// src/features/skill-mcp-manager/manager.ts
|
|
42403
42962
|
class SkillMcpManager {
|
|
42404
42963
|
clients = new Map;
|
|
42964
|
+
pendingConnections = new Map;
|
|
42965
|
+
cleanupRegistered = false;
|
|
42966
|
+
cleanupInterval = null;
|
|
42967
|
+
IDLE_TIMEOUT = 5 * 60 * 1000;
|
|
42405
42968
|
getClientKey(info) {
|
|
42406
42969
|
return `${info.sessionID}:${info.skillName}:${info.serverName}`;
|
|
42407
42970
|
}
|
|
42971
|
+
registerProcessCleanup() {
|
|
42972
|
+
if (this.cleanupRegistered)
|
|
42973
|
+
return;
|
|
42974
|
+
this.cleanupRegistered = true;
|
|
42975
|
+
const cleanup = async () => {
|
|
42976
|
+
for (const [, managed] of this.clients) {
|
|
42977
|
+
try {
|
|
42978
|
+
await managed.client.close();
|
|
42979
|
+
} catch {}
|
|
42980
|
+
try {
|
|
42981
|
+
await managed.transport.close();
|
|
42982
|
+
} catch {}
|
|
42983
|
+
}
|
|
42984
|
+
this.clients.clear();
|
|
42985
|
+
this.pendingConnections.clear();
|
|
42986
|
+
};
|
|
42987
|
+
process.on("SIGINT", async () => {
|
|
42988
|
+
await cleanup();
|
|
42989
|
+
process.exit(0);
|
|
42990
|
+
});
|
|
42991
|
+
process.on("SIGTERM", async () => {
|
|
42992
|
+
await cleanup();
|
|
42993
|
+
process.exit(0);
|
|
42994
|
+
});
|
|
42995
|
+
if (process.platform === "win32") {
|
|
42996
|
+
process.on("SIGBREAK", async () => {
|
|
42997
|
+
await cleanup();
|
|
42998
|
+
process.exit(0);
|
|
42999
|
+
});
|
|
43000
|
+
}
|
|
43001
|
+
}
|
|
42408
43002
|
async getOrCreateClient(info, config3) {
|
|
42409
43003
|
const key = this.getClientKey(info);
|
|
42410
43004
|
const existing = this.clients.get(key);
|
|
42411
43005
|
if (existing) {
|
|
43006
|
+
existing.lastUsedAt = Date.now();
|
|
42412
43007
|
return existing.client;
|
|
42413
43008
|
}
|
|
43009
|
+
const pending = this.pendingConnections.get(key);
|
|
43010
|
+
if (pending) {
|
|
43011
|
+
return pending;
|
|
43012
|
+
}
|
|
42414
43013
|
const expandedConfig = expandEnvVarsInObject(config3);
|
|
42415
|
-
const
|
|
42416
|
-
|
|
43014
|
+
const connectionPromise = this.createClient(info, expandedConfig);
|
|
43015
|
+
this.pendingConnections.set(key, connectionPromise);
|
|
43016
|
+
try {
|
|
43017
|
+
const client2 = await connectionPromise;
|
|
43018
|
+
return client2;
|
|
43019
|
+
} finally {
|
|
43020
|
+
this.pendingConnections.delete(key);
|
|
43021
|
+
}
|
|
42417
43022
|
}
|
|
42418
43023
|
async createClient(info, config3) {
|
|
42419
43024
|
const key = this.getClientKey(info);
|
|
@@ -42430,14 +43035,8 @@ class SkillMcpManager {
|
|
|
42430
43035
|
}
|
|
42431
43036
|
const command = config3.command;
|
|
42432
43037
|
const args = config3.args || [];
|
|
42433
|
-
const mergedEnv =
|
|
42434
|
-
|
|
42435
|
-
if (value !== undefined)
|
|
42436
|
-
mergedEnv[key2] = value;
|
|
42437
|
-
}
|
|
42438
|
-
if (config3.env) {
|
|
42439
|
-
Object.assign(mergedEnv, config3.env);
|
|
42440
|
-
}
|
|
43038
|
+
const mergedEnv = createCleanMcpEnvironment(config3.env);
|
|
43039
|
+
this.registerProcessCleanup();
|
|
42441
43040
|
const transport = new StdioClientTransport({
|
|
42442
43041
|
command,
|
|
42443
43042
|
args,
|
|
@@ -42448,6 +43047,9 @@ class SkillMcpManager {
|
|
|
42448
43047
|
try {
|
|
42449
43048
|
await client2.connect(transport);
|
|
42450
43049
|
} catch (error45) {
|
|
43050
|
+
try {
|
|
43051
|
+
await transport.close();
|
|
43052
|
+
} catch {}
|
|
42451
43053
|
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
42452
43054
|
throw new Error(`Failed to connect to MCP server "${info.serverName}".
|
|
42453
43055
|
|
|
@@ -42459,7 +43061,8 @@ class SkillMcpManager {
|
|
|
42459
43061
|
` + ` - Check if the MCP server package exists
|
|
42460
43062
|
` + ` - Verify the args are correct for this server`);
|
|
42461
43063
|
}
|
|
42462
|
-
this.clients.set(key, { client: client2, transport, skillName: info.skillName });
|
|
43064
|
+
this.clients.set(key, { client: client2, transport, skillName: info.skillName, lastUsedAt: Date.now() });
|
|
43065
|
+
this.startCleanupTimer();
|
|
42463
43066
|
return client2;
|
|
42464
43067
|
}
|
|
42465
43068
|
async disconnectSession(sessionID) {
|
|
@@ -42467,22 +43070,56 @@ class SkillMcpManager {
|
|
|
42467
43070
|
for (const [key, managed] of this.clients.entries()) {
|
|
42468
43071
|
if (key.startsWith(`${sessionID}:`)) {
|
|
42469
43072
|
keysToRemove.push(key);
|
|
43073
|
+
this.clients.delete(key);
|
|
42470
43074
|
try {
|
|
42471
43075
|
await managed.client.close();
|
|
42472
43076
|
} catch {}
|
|
43077
|
+
try {
|
|
43078
|
+
await managed.transport.close();
|
|
43079
|
+
} catch {}
|
|
42473
43080
|
}
|
|
42474
43081
|
}
|
|
42475
|
-
for (const key of keysToRemove) {
|
|
42476
|
-
this.clients.delete(key);
|
|
42477
|
-
}
|
|
42478
43082
|
}
|
|
42479
43083
|
async disconnectAll() {
|
|
42480
|
-
|
|
43084
|
+
this.stopCleanupTimer();
|
|
43085
|
+
const clients = Array.from(this.clients.values());
|
|
43086
|
+
this.clients.clear();
|
|
43087
|
+
for (const managed of clients) {
|
|
42481
43088
|
try {
|
|
42482
43089
|
await managed.client.close();
|
|
42483
43090
|
} catch {}
|
|
43091
|
+
try {
|
|
43092
|
+
await managed.transport.close();
|
|
43093
|
+
} catch {}
|
|
43094
|
+
}
|
|
43095
|
+
}
|
|
43096
|
+
startCleanupTimer() {
|
|
43097
|
+
if (this.cleanupInterval)
|
|
43098
|
+
return;
|
|
43099
|
+
this.cleanupInterval = setInterval(() => {
|
|
43100
|
+
this.cleanupIdleClients();
|
|
43101
|
+
}, 60000);
|
|
43102
|
+
this.cleanupInterval.unref();
|
|
43103
|
+
}
|
|
43104
|
+
stopCleanupTimer() {
|
|
43105
|
+
if (this.cleanupInterval) {
|
|
43106
|
+
clearInterval(this.cleanupInterval);
|
|
43107
|
+
this.cleanupInterval = null;
|
|
43108
|
+
}
|
|
43109
|
+
}
|
|
43110
|
+
async cleanupIdleClients() {
|
|
43111
|
+
const now = Date.now();
|
|
43112
|
+
for (const [key, managed] of this.clients) {
|
|
43113
|
+
if (now - managed.lastUsedAt > this.IDLE_TIMEOUT) {
|
|
43114
|
+
this.clients.delete(key);
|
|
43115
|
+
try {
|
|
43116
|
+
await managed.client.close();
|
|
43117
|
+
} catch {}
|
|
43118
|
+
try {
|
|
43119
|
+
await managed.transport.close();
|
|
43120
|
+
} catch {}
|
|
43121
|
+
}
|
|
42484
43122
|
}
|
|
42485
|
-
this.clients.clear();
|
|
42486
43123
|
}
|
|
42487
43124
|
async listTools(info, context) {
|
|
42488
43125
|
const client2 = await this.getOrCreateClientWithRetry(info, context.config);
|
|
@@ -42521,10 +43158,13 @@ class SkillMcpManager {
|
|
|
42521
43158
|
const key = this.getClientKey(info);
|
|
42522
43159
|
const existing = this.clients.get(key);
|
|
42523
43160
|
if (existing) {
|
|
43161
|
+
this.clients.delete(key);
|
|
42524
43162
|
try {
|
|
42525
43163
|
await existing.client.close();
|
|
42526
43164
|
} catch {}
|
|
42527
|
-
|
|
43165
|
+
try {
|
|
43166
|
+
await existing.transport.close();
|
|
43167
|
+
} catch {}
|
|
42528
43168
|
return await this.getOrCreateClient(info, config3);
|
|
42529
43169
|
}
|
|
42530
43170
|
throw error45;
|
|
@@ -42538,7 +43178,7 @@ class SkillMcpManager {
|
|
|
42538
43178
|
}
|
|
42539
43179
|
}
|
|
42540
43180
|
// src/plugin-config.ts
|
|
42541
|
-
import * as
|
|
43181
|
+
import * as fs10 from "fs";
|
|
42542
43182
|
import * as path7 from "path";
|
|
42543
43183
|
|
|
42544
43184
|
// src/mcp/types.ts
|
|
@@ -42758,8 +43398,8 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
42758
43398
|
// src/plugin-config.ts
|
|
42759
43399
|
function loadConfigFromPath2(configPath, ctx) {
|
|
42760
43400
|
try {
|
|
42761
|
-
if (
|
|
42762
|
-
const content =
|
|
43401
|
+
if (fs10.existsSync(configPath)) {
|
|
43402
|
+
const content = fs10.readFileSync(configPath, "utf-8");
|
|
42763
43403
|
const rawConfig = parseJsonc(content);
|
|
42764
43404
|
migrateConfigFile(configPath, rawConfig);
|
|
42765
43405
|
const result = OhMyOpenCodeConfigSchema.safeParse(rawConfig);
|
|
@@ -43663,12 +44303,18 @@ Organize your final answer in three tiers:
|
|
|
43663
44303
|
|
|
43664
44304
|
Your response goes directly to the user with no intermediate processing. Make your final message self-contained: a clear recommendation they can act on immediately, covering both what to do and why.`;
|
|
43665
44305
|
function createOracleAgent(model = DEFAULT_MODEL2) {
|
|
44306
|
+
const restrictions = createAgentToolRestrictions([
|
|
44307
|
+
"write",
|
|
44308
|
+
"edit",
|
|
44309
|
+
"task",
|
|
44310
|
+
"background_task"
|
|
44311
|
+
]);
|
|
43666
44312
|
const base = {
|
|
43667
44313
|
description: "Expert technical advisor with deep reasoning for architecture decisions, code analysis, and engineering guidance.",
|
|
43668
44314
|
mode: "subagent",
|
|
43669
44315
|
model,
|
|
43670
44316
|
temperature: 0.1,
|
|
43671
|
-
|
|
44317
|
+
...restrictions,
|
|
43672
44318
|
prompt: ORACLE_SYSTEM_PROMPT
|
|
43673
44319
|
};
|
|
43674
44320
|
if (isGptModel(model)) {
|
|
@@ -43697,12 +44343,17 @@ var LIBRARIAN_PROMPT_METADATA = {
|
|
|
43697
44343
|
]
|
|
43698
44344
|
};
|
|
43699
44345
|
function createLibrarianAgent(model = DEFAULT_MODEL3) {
|
|
44346
|
+
const restrictions = createAgentToolRestrictions([
|
|
44347
|
+
"write",
|
|
44348
|
+
"edit",
|
|
44349
|
+
"background_task"
|
|
44350
|
+
]);
|
|
43700
44351
|
return {
|
|
43701
44352
|
description: "Specialized codebase understanding agent for multi-repository analysis, searching remote codebases, retrieving official documentation, and finding implementation examples using GitHub CLI, Context7, and Web Search. MUST BE USED when users ask to look up code in remote repositories, explain library internals, or find usage examples in open source.",
|
|
43702
44353
|
mode: "subagent",
|
|
43703
44354
|
model,
|
|
43704
44355
|
temperature: 0.1,
|
|
43705
|
-
|
|
44356
|
+
...restrictions,
|
|
43706
44357
|
prompt: `# THE LIBRARIAN
|
|
43707
44358
|
|
|
43708
44359
|
You are **THE LIBRARIAN**, a specialized open-source codebase understanding agent.
|
|
@@ -43959,12 +44610,17 @@ var EXPLORE_PROMPT_METADATA = {
|
|
|
43959
44610
|
]
|
|
43960
44611
|
};
|
|
43961
44612
|
function createExploreAgent(model = DEFAULT_MODEL4) {
|
|
44613
|
+
const restrictions = createAgentToolRestrictions([
|
|
44614
|
+
"write",
|
|
44615
|
+
"edit",
|
|
44616
|
+
"background_task"
|
|
44617
|
+
]);
|
|
43962
44618
|
return {
|
|
43963
44619
|
description: 'Contextual grep for codebases. Answers "Where is X?", "Which file has Y?", "Find the code that does Z". Fire multiple in parallel for broad searches. Specify thoroughness: "quick" for basic, "medium" for moderate, "very thorough" for comprehensive analysis.',
|
|
43964
44620
|
mode: "subagent",
|
|
43965
44621
|
model,
|
|
43966
44622
|
temperature: 0.1,
|
|
43967
|
-
|
|
44623
|
+
...restrictions,
|
|
43968
44624
|
prompt: `You are a codebase search specialist. Your job: find files and code, return actionable results.
|
|
43969
44625
|
|
|
43970
44626
|
## Your Mission
|
|
@@ -44065,11 +44721,12 @@ var FRONTEND_PROMPT_METADATA = {
|
|
|
44065
44721
|
]
|
|
44066
44722
|
};
|
|
44067
44723
|
function createFrontendUiUxEngineerAgent(model = DEFAULT_MODEL5) {
|
|
44724
|
+
const restrictions = createAgentToolRestrictions(["background_task"]);
|
|
44068
44725
|
return {
|
|
44069
44726
|
description: "A designer-turned-developer who crafts stunning UI/UX even without design mockups. Code may be a bit messy, but the visual output is always fire.",
|
|
44070
44727
|
mode: "subagent",
|
|
44071
44728
|
model,
|
|
44072
|
-
|
|
44729
|
+
...restrictions,
|
|
44073
44730
|
prompt: `# Role: Designer-Turned-Developer
|
|
44074
44731
|
|
|
44075
44732
|
You are a designer who learned to code. You see what pure developers miss\u2014spacing, color harmony, micro-interactions, that indefinable "feel" that makes interfaces memorable. Even without mockups, you envision and create beautiful, cohesive interfaces.
|
|
@@ -44158,11 +44815,12 @@ var DOCUMENT_WRITER_PROMPT_METADATA = {
|
|
|
44158
44815
|
]
|
|
44159
44816
|
};
|
|
44160
44817
|
function createDocumentWriterAgent(model = DEFAULT_MODEL6) {
|
|
44818
|
+
const restrictions = createAgentToolRestrictions(["background_task"]);
|
|
44161
44819
|
return {
|
|
44162
44820
|
description: "A technical writer who crafts clear, comprehensive documentation. Specializes in README files, API docs, architecture docs, and user guides. MUST BE USED when executing documentation tasks from ai-todo list plans.",
|
|
44163
44821
|
mode: "subagent",
|
|
44164
44822
|
model,
|
|
44165
|
-
|
|
44823
|
+
...restrictions,
|
|
44166
44824
|
prompt: `<role>
|
|
44167
44825
|
You are a TECHNICAL WRITER with deep engineering background who transforms complex codebases into crystal-clear documentation. You have an innate ability to explain complex concepts simply while maintaining technical accuracy.
|
|
44168
44826
|
|
|
@@ -44370,12 +45028,18 @@ var MULTIMODAL_LOOKER_PROMPT_METADATA = {
|
|
|
44370
45028
|
triggers: []
|
|
44371
45029
|
};
|
|
44372
45030
|
function createMultimodalLookerAgent(model = DEFAULT_MODEL7) {
|
|
45031
|
+
const restrictions = createAgentToolRestrictions([
|
|
45032
|
+
"write",
|
|
45033
|
+
"edit",
|
|
45034
|
+
"bash",
|
|
45035
|
+
"background_task"
|
|
45036
|
+
]);
|
|
44373
45037
|
return {
|
|
44374
45038
|
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.",
|
|
44375
45039
|
mode: "subagent",
|
|
44376
45040
|
model,
|
|
44377
45041
|
temperature: 0.1,
|
|
44378
|
-
|
|
45042
|
+
...restrictions,
|
|
44379
45043
|
prompt: `You interpret media files that cannot be read as plain text.
|
|
44380
45044
|
|
|
44381
45045
|
Your job: examine the attached file and extract ONLY what was requested.
|
|
@@ -44506,10 +45170,10 @@ function createBuiltinAgents(disabledAgents = [], agentOverrides = {}, directory
|
|
|
44506
45170
|
return result;
|
|
44507
45171
|
}
|
|
44508
45172
|
// src/features/claude-code-command-loader/loader.ts
|
|
44509
|
-
import { existsSync as
|
|
45173
|
+
import { existsSync as existsSync50, readdirSync as readdirSync18, readFileSync as readFileSync32, realpathSync as realpathSync2 } from "fs";
|
|
44510
45174
|
import { join as join56, basename as basename5 } from "path";
|
|
44511
45175
|
function loadCommandsFromDir(commandsDir, scope, visited = new Set, prefix = "") {
|
|
44512
|
-
if (!
|
|
45176
|
+
if (!existsSync50(commandsDir)) {
|
|
44513
45177
|
return [];
|
|
44514
45178
|
}
|
|
44515
45179
|
let realPath;
|
|
@@ -44993,7 +45657,7 @@ function loadBuiltinCommands(disabledCommands) {
|
|
|
44993
45657
|
return commands;
|
|
44994
45658
|
}
|
|
44995
45659
|
// src/features/claude-code-agent-loader/loader.ts
|
|
44996
|
-
import { existsSync as
|
|
45660
|
+
import { existsSync as existsSync51, readdirSync as readdirSync19, readFileSync as readFileSync33 } from "fs";
|
|
44997
45661
|
import { join as join57, basename as basename6 } from "path";
|
|
44998
45662
|
function parseToolsConfig(toolsStr) {
|
|
44999
45663
|
if (!toolsStr)
|
|
@@ -45008,7 +45672,7 @@ function parseToolsConfig(toolsStr) {
|
|
|
45008
45672
|
return result;
|
|
45009
45673
|
}
|
|
45010
45674
|
function loadAgentsFromDir(agentsDir, scope) {
|
|
45011
|
-
if (!
|
|
45675
|
+
if (!existsSync51(agentsDir)) {
|
|
45012
45676
|
return [];
|
|
45013
45677
|
}
|
|
45014
45678
|
const entries = readdirSync19(agentsDir, { withFileTypes: true });
|
|
@@ -45064,7 +45728,7 @@ function loadProjectAgents() {
|
|
|
45064
45728
|
return result;
|
|
45065
45729
|
}
|
|
45066
45730
|
// src/features/claude-code-plugin-loader/loader.ts
|
|
45067
|
-
import { existsSync as
|
|
45731
|
+
import { existsSync as existsSync52, readdirSync as readdirSync20, readFileSync as readFileSync34 } from "fs";
|
|
45068
45732
|
import { homedir as homedir16 } from "os";
|
|
45069
45733
|
import { join as join58, basename as basename7 } from "path";
|
|
45070
45734
|
var CLAUDE_PLUGIN_ROOT_VAR = "${CLAUDE_PLUGIN_ROOT}";
|
|
@@ -45100,7 +45764,7 @@ function resolvePluginPaths(obj, pluginRoot) {
|
|
|
45100
45764
|
}
|
|
45101
45765
|
function loadInstalledPlugins() {
|
|
45102
45766
|
const dbPath = getInstalledPluginsPath();
|
|
45103
|
-
if (!
|
|
45767
|
+
if (!existsSync52(dbPath)) {
|
|
45104
45768
|
return null;
|
|
45105
45769
|
}
|
|
45106
45770
|
try {
|
|
@@ -45119,7 +45783,7 @@ function getClaudeSettingsPath() {
|
|
|
45119
45783
|
}
|
|
45120
45784
|
function loadClaudeSettings() {
|
|
45121
45785
|
const settingsPath = getClaudeSettingsPath();
|
|
45122
|
-
if (!
|
|
45786
|
+
if (!existsSync52(settingsPath)) {
|
|
45123
45787
|
return null;
|
|
45124
45788
|
}
|
|
45125
45789
|
try {
|
|
@@ -45132,7 +45796,7 @@ function loadClaudeSettings() {
|
|
|
45132
45796
|
}
|
|
45133
45797
|
function loadPluginManifest(installPath) {
|
|
45134
45798
|
const manifestPath = join58(installPath, ".claude-plugin", "plugin.json");
|
|
45135
|
-
if (!
|
|
45799
|
+
if (!existsSync52(manifestPath)) {
|
|
45136
45800
|
return null;
|
|
45137
45801
|
}
|
|
45138
45802
|
try {
|
|
@@ -45183,7 +45847,7 @@ function discoverInstalledPlugins(options) {
|
|
|
45183
45847
|
continue;
|
|
45184
45848
|
}
|
|
45185
45849
|
const { installPath, scope, version: version2 } = installation;
|
|
45186
|
-
if (!
|
|
45850
|
+
if (!existsSync52(installPath)) {
|
|
45187
45851
|
errors3.push({
|
|
45188
45852
|
pluginKey,
|
|
45189
45853
|
installPath,
|
|
@@ -45201,21 +45865,21 @@ function discoverInstalledPlugins(options) {
|
|
|
45201
45865
|
pluginKey,
|
|
45202
45866
|
manifest: manifest ?? undefined
|
|
45203
45867
|
};
|
|
45204
|
-
if (
|
|
45868
|
+
if (existsSync52(join58(installPath, "commands"))) {
|
|
45205
45869
|
loadedPlugin.commandsDir = join58(installPath, "commands");
|
|
45206
45870
|
}
|
|
45207
|
-
if (
|
|
45871
|
+
if (existsSync52(join58(installPath, "agents"))) {
|
|
45208
45872
|
loadedPlugin.agentsDir = join58(installPath, "agents");
|
|
45209
45873
|
}
|
|
45210
|
-
if (
|
|
45874
|
+
if (existsSync52(join58(installPath, "skills"))) {
|
|
45211
45875
|
loadedPlugin.skillsDir = join58(installPath, "skills");
|
|
45212
45876
|
}
|
|
45213
45877
|
const hooksPath = join58(installPath, "hooks", "hooks.json");
|
|
45214
|
-
if (
|
|
45878
|
+
if (existsSync52(hooksPath)) {
|
|
45215
45879
|
loadedPlugin.hooksPath = hooksPath;
|
|
45216
45880
|
}
|
|
45217
45881
|
const mcpPath = join58(installPath, ".mcp.json");
|
|
45218
|
-
if (
|
|
45882
|
+
if (existsSync52(mcpPath)) {
|
|
45219
45883
|
loadedPlugin.mcpPath = mcpPath;
|
|
45220
45884
|
}
|
|
45221
45885
|
plugins.push(loadedPlugin);
|
|
@@ -45226,7 +45890,7 @@ function discoverInstalledPlugins(options) {
|
|
|
45226
45890
|
function loadPluginCommands(plugins) {
|
|
45227
45891
|
const commands2 = {};
|
|
45228
45892
|
for (const plugin2 of plugins) {
|
|
45229
|
-
if (!plugin2.commandsDir || !
|
|
45893
|
+
if (!plugin2.commandsDir || !existsSync52(plugin2.commandsDir))
|
|
45230
45894
|
continue;
|
|
45231
45895
|
const entries = readdirSync20(plugin2.commandsDir, { withFileTypes: true });
|
|
45232
45896
|
for (const entry of entries) {
|
|
@@ -45268,7 +45932,7 @@ $ARGUMENTS
|
|
|
45268
45932
|
function loadPluginSkillsAsCommands(plugins) {
|
|
45269
45933
|
const skills = {};
|
|
45270
45934
|
for (const plugin2 of plugins) {
|
|
45271
|
-
if (!plugin2.skillsDir || !
|
|
45935
|
+
if (!plugin2.skillsDir || !existsSync52(plugin2.skillsDir))
|
|
45272
45936
|
continue;
|
|
45273
45937
|
const entries = readdirSync20(plugin2.skillsDir, { withFileTypes: true });
|
|
45274
45938
|
for (const entry of entries) {
|
|
@@ -45279,7 +45943,7 @@ function loadPluginSkillsAsCommands(plugins) {
|
|
|
45279
45943
|
continue;
|
|
45280
45944
|
const resolvedPath = resolveSymlink(skillPath);
|
|
45281
45945
|
const skillMdPath = join58(resolvedPath, "SKILL.md");
|
|
45282
|
-
if (!
|
|
45946
|
+
if (!existsSync52(skillMdPath))
|
|
45283
45947
|
continue;
|
|
45284
45948
|
try {
|
|
45285
45949
|
const content = readFileSync34(skillMdPath, "utf-8");
|
|
@@ -45329,7 +45993,7 @@ function parseToolsConfig2(toolsStr) {
|
|
|
45329
45993
|
function loadPluginAgents(plugins) {
|
|
45330
45994
|
const agents = {};
|
|
45331
45995
|
for (const plugin2 of plugins) {
|
|
45332
|
-
if (!plugin2.agentsDir || !
|
|
45996
|
+
if (!plugin2.agentsDir || !existsSync52(plugin2.agentsDir))
|
|
45333
45997
|
continue;
|
|
45334
45998
|
const entries = readdirSync20(plugin2.agentsDir, { withFileTypes: true });
|
|
45335
45999
|
for (const entry of entries) {
|
|
@@ -45365,7 +46029,7 @@ function loadPluginAgents(plugins) {
|
|
|
45365
46029
|
async function loadPluginMcpServers(plugins) {
|
|
45366
46030
|
const servers = {};
|
|
45367
46031
|
for (const plugin2 of plugins) {
|
|
45368
|
-
if (!plugin2.mcpPath || !
|
|
46032
|
+
if (!plugin2.mcpPath || !existsSync52(plugin2.mcpPath))
|
|
45369
46033
|
continue;
|
|
45370
46034
|
try {
|
|
45371
46035
|
const content = await Bun.file(plugin2.mcpPath).text();
|
|
@@ -45397,7 +46061,7 @@ async function loadPluginMcpServers(plugins) {
|
|
|
45397
46061
|
function loadPluginHooksConfigs(plugins) {
|
|
45398
46062
|
const configs = [];
|
|
45399
46063
|
for (const plugin2 of plugins) {
|
|
45400
|
-
if (!plugin2.hooksPath || !
|
|
46064
|
+
if (!plugin2.hooksPath || !existsSync52(plugin2.hooksPath))
|
|
45401
46065
|
continue;
|
|
45402
46066
|
try {
|
|
45403
46067
|
const content = readFileSync34(plugin2.hooksPath, "utf-8");
|
|
@@ -45413,11 +46077,13 @@ function loadPluginHooksConfigs(plugins) {
|
|
|
45413
46077
|
}
|
|
45414
46078
|
async function loadAllPluginComponents(options) {
|
|
45415
46079
|
const { plugins, errors: errors3 } = discoverInstalledPlugins(options);
|
|
45416
|
-
const commands2 =
|
|
45417
|
-
|
|
45418
|
-
|
|
45419
|
-
|
|
45420
|
-
|
|
46080
|
+
const [commands2, skills, agents, mcpServers, hooksConfigs] = await Promise.all([
|
|
46081
|
+
Promise.resolve(loadPluginCommands(plugins)),
|
|
46082
|
+
Promise.resolve(loadPluginSkillsAsCommands(plugins)),
|
|
46083
|
+
Promise.resolve(loadPluginAgents(plugins)),
|
|
46084
|
+
loadPluginMcpServers(plugins),
|
|
46085
|
+
Promise.resolve(loadPluginHooksConfigs(plugins))
|
|
46086
|
+
]);
|
|
45421
46087
|
log(`Loaded ${plugins.length} plugins with ${Object.keys(commands2).length} commands, ${Object.keys(skills).length} skills, ${Object.keys(agents).length} agents, ${Object.keys(mcpServers).length} MCP servers`);
|
|
45422
46088
|
return {
|
|
45423
46089
|
commands: commands2,
|
|
@@ -45577,9 +46243,21 @@ function createConfigHandler(deps) {
|
|
|
45577
46243
|
log(`Plugin load errors`, { errors: pluginComponents.errors });
|
|
45578
46244
|
}
|
|
45579
46245
|
const builtinAgents = createBuiltinAgents(pluginConfig.disabled_agents, pluginConfig.agents, ctx.directory, config3.model);
|
|
45580
|
-
const
|
|
45581
|
-
const
|
|
45582
|
-
const
|
|
46246
|
+
const rawUserAgents = pluginConfig.claude_code?.agents ?? true ? loadUserAgents() : {};
|
|
46247
|
+
const rawProjectAgents = pluginConfig.claude_code?.agents ?? true ? loadProjectAgents() : {};
|
|
46248
|
+
const rawPluginAgents = pluginComponents.agents;
|
|
46249
|
+
const userAgents = Object.fromEntries(Object.entries(rawUserAgents).map(([k, v]) => [
|
|
46250
|
+
k,
|
|
46251
|
+
v ? migrateAgentConfig(v) : v
|
|
46252
|
+
]));
|
|
46253
|
+
const projectAgents = Object.fromEntries(Object.entries(rawProjectAgents).map(([k, v]) => [
|
|
46254
|
+
k,
|
|
46255
|
+
v ? migrateAgentConfig(v) : v
|
|
46256
|
+
]));
|
|
46257
|
+
const pluginAgents = Object.fromEntries(Object.entries(rawPluginAgents).map(([k, v]) => [
|
|
46258
|
+
k,
|
|
46259
|
+
v ? migrateAgentConfig(v) : v
|
|
46260
|
+
]));
|
|
45583
46261
|
const isSisyphusEnabled = pluginConfig.sisyphus_agent?.disabled !== true;
|
|
45584
46262
|
const builderEnabled = pluginConfig.sisyphus_agent?.default_builder_enabled ?? false;
|
|
45585
46263
|
const plannerEnabled = pluginConfig.sisyphus_agent?.planner_enabled ?? true;
|
|
@@ -45592,18 +46270,21 @@ function createConfigHandler(deps) {
|
|
|
45592
46270
|
};
|
|
45593
46271
|
if (builderEnabled) {
|
|
45594
46272
|
const { name: _buildName, ...buildConfigWithoutName } = configAgent?.build ?? {};
|
|
46273
|
+
const migratedBuildConfig = migrateAgentConfig(buildConfigWithoutName);
|
|
45595
46274
|
const openCodeBuilderOverride = pluginConfig.agents?.["OpenCode-Builder"];
|
|
45596
46275
|
const openCodeBuilderBase = {
|
|
45597
|
-
...
|
|
46276
|
+
...migratedBuildConfig,
|
|
45598
46277
|
description: `${configAgent?.build?.description ?? "Build agent"} (OpenCode default)`
|
|
45599
46278
|
};
|
|
45600
46279
|
agentConfig["OpenCode-Builder"] = openCodeBuilderOverride ? { ...openCodeBuilderBase, ...openCodeBuilderOverride } : openCodeBuilderBase;
|
|
45601
46280
|
}
|
|
45602
46281
|
if (plannerEnabled) {
|
|
45603
46282
|
const { name: _planName, ...planConfigWithoutName } = configAgent?.plan ?? {};
|
|
46283
|
+
const migratedPlanConfig = migrateAgentConfig(planConfigWithoutName);
|
|
45604
46284
|
const plannerSisyphusOverride = pluginConfig.agents?.["Planner-Sisyphus"];
|
|
45605
46285
|
const plannerSisyphusBase = {
|
|
45606
|
-
...
|
|
46286
|
+
...migratedPlanConfig,
|
|
46287
|
+
mode: "primary",
|
|
45607
46288
|
prompt: PLAN_SYSTEM_PROMPT,
|
|
45608
46289
|
permission: PLAN_PERMISSION,
|
|
45609
46290
|
description: `${configAgent?.plan?.description ?? "Plan agent"} (OhMyOpenCode version)`,
|
|
@@ -45617,7 +46298,12 @@ function createConfigHandler(deps) {
|
|
|
45617
46298
|
if (key === "plan" && replacePlan)
|
|
45618
46299
|
return false;
|
|
45619
46300
|
return true;
|
|
45620
|
-
}))
|
|
46301
|
+
}).map(([key, value]) => [
|
|
46302
|
+
key,
|
|
46303
|
+
value ? migrateAgentConfig(value) : value
|
|
46304
|
+
])) : {};
|
|
46305
|
+
const migratedBuild = configAgent?.build ? migrateAgentConfig(configAgent.build) : {};
|
|
46306
|
+
const planDemoteConfig = replacePlan ? { disable: true } : undefined;
|
|
45621
46307
|
config3.agent = {
|
|
45622
46308
|
...agentConfig,
|
|
45623
46309
|
...Object.fromEntries(Object.entries(builtinAgents).filter(([k]) => k !== "Sisyphus")),
|
|
@@ -45625,8 +46311,8 @@ function createConfigHandler(deps) {
|
|
|
45625
46311
|
...projectAgents,
|
|
45626
46312
|
...pluginAgents,
|
|
45627
46313
|
...filteredConfigAgents,
|
|
45628
|
-
build: { ...
|
|
45629
|
-
...
|
|
46314
|
+
build: { ...migratedBuild, mode: "subagent", hidden: true },
|
|
46315
|
+
...planDemoteConfig ? { plan: planDemoteConfig } : {}
|
|
45630
46316
|
};
|
|
45631
46317
|
} else {
|
|
45632
46318
|
config3.agent = {
|
|
@@ -45703,6 +46389,7 @@ function createConfigHandler(deps) {
|
|
|
45703
46389
|
}
|
|
45704
46390
|
// src/index.ts
|
|
45705
46391
|
var OhMyOpenCodePlugin = async (ctx) => {
|
|
46392
|
+
startBackgroundCheck2();
|
|
45706
46393
|
const pluginConfig = loadPluginConfig(ctx.directory, ctx);
|
|
45707
46394
|
const disabledHooks = new Set(pluginConfig.disabled_hooks ?? []);
|
|
45708
46395
|
const isHookEnabled = (hookName) => !disabledHooks.has(hookName);
|
|
@@ -45738,12 +46425,17 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
45738
46425
|
autoUpdate: pluginConfig.auto_update ?? true
|
|
45739
46426
|
}) : null;
|
|
45740
46427
|
const keywordDetector = isHookEnabled("keyword-detector") ? createKeywordDetectorHook(ctx) : null;
|
|
46428
|
+
const contextInjector = createContextInjectorHook(contextCollector);
|
|
46429
|
+
const contextInjectorMessagesTransform = createContextInjectorMessagesTransformHook(contextCollector);
|
|
45741
46430
|
const agentUsageReminder = isHookEnabled("agent-usage-reminder") ? createAgentUsageReminderHook(ctx) : null;
|
|
45742
46431
|
const nonInteractiveEnv = isHookEnabled("non-interactive-env") ? createNonInteractiveEnvHook(ctx) : null;
|
|
45743
46432
|
const interactiveBashSession = isHookEnabled("interactive-bash-session") ? createInteractiveBashSessionHook(ctx) : null;
|
|
45744
46433
|
const emptyMessageSanitizer = isHookEnabled("empty-message-sanitizer") ? createEmptyMessageSanitizerHook() : null;
|
|
45745
46434
|
const thinkingBlockValidator = isHookEnabled("thinking-block-validator") ? createThinkingBlockValidatorHook() : null;
|
|
45746
|
-
const ralphLoop = isHookEnabled("ralph-loop") ? createRalphLoopHook(ctx, {
|
|
46435
|
+
const ralphLoop = isHookEnabled("ralph-loop") ? createRalphLoopHook(ctx, {
|
|
46436
|
+
config: pluginConfig.ralph_loop,
|
|
46437
|
+
checkSessionExists: async (sessionId) => sessionExists(sessionId)
|
|
46438
|
+
}) : null;
|
|
45747
46439
|
const autoSlashCommand = isHookEnabled("auto-slash-command") ? createAutoSlashCommandHook() : null;
|
|
45748
46440
|
const editErrorRecovery = isHookEnabled("edit-error-recovery") ? createEditErrorRecoveryHook(ctx) : null;
|
|
45749
46441
|
const backgroundManager = new BackgroundManager(ctx);
|
|
@@ -45770,7 +46462,13 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
45770
46462
|
return true;
|
|
45771
46463
|
});
|
|
45772
46464
|
const includeClaudeSkills = pluginConfig.claude_code?.skills !== false;
|
|
45773
|
-
const
|
|
46465
|
+
const [userSkills, globalSkills, projectSkills, opencodeProjectSkills] = await Promise.all([
|
|
46466
|
+
includeClaudeSkills ? discoverUserClaudeSkillsAsync() : Promise.resolve([]),
|
|
46467
|
+
discoverOpencodeGlobalSkillsAsync(),
|
|
46468
|
+
includeClaudeSkills ? discoverProjectClaudeSkillsAsync() : Promise.resolve([]),
|
|
46469
|
+
discoverOpencodeProjectSkillsAsync()
|
|
46470
|
+
]);
|
|
46471
|
+
const mergedSkills = mergeSkills(builtinSkills, pluginConfig.skills, userSkills, globalSkills, projectSkills, opencodeProjectSkills);
|
|
45774
46472
|
const skillMcpManager = new SkillMcpManager;
|
|
45775
46473
|
const getSessionIDForMcp = () => getMainSessionID() || "";
|
|
45776
46474
|
const skillTool = createSkillTool({
|
|
@@ -45784,7 +46482,6 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
45784
46482
|
getSessionID: getSessionIDForMcp
|
|
45785
46483
|
});
|
|
45786
46484
|
const googleAuthHooks = pluginConfig.google_auth !== false ? await createGoogleAntigravityAuthPlugin(ctx) : null;
|
|
45787
|
-
const tmuxAvailable = await getTmuxPath();
|
|
45788
46485
|
const configHandler = createConfigHandler({
|
|
45789
46486
|
ctx,
|
|
45790
46487
|
pluginConfig,
|
|
@@ -45799,11 +46496,12 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
45799
46496
|
look_at: lookAt,
|
|
45800
46497
|
skill: skillTool,
|
|
45801
46498
|
skill_mcp: skillMcpTool,
|
|
45802
|
-
|
|
46499
|
+
interactive_bash
|
|
45803
46500
|
},
|
|
45804
46501
|
"chat.message": async (input, output) => {
|
|
45805
46502
|
await claudeCodeHooks["chat.message"]?.(input, output);
|
|
45806
46503
|
await keywordDetector?.["chat.message"]?.(input, output);
|
|
46504
|
+
await contextInjector["chat.message"]?.(input, output);
|
|
45807
46505
|
await autoSlashCommand?.["chat.message"]?.(input, output);
|
|
45808
46506
|
if (ralphLoop) {
|
|
45809
46507
|
const parts = output.parts;
|
|
@@ -45835,6 +46533,7 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
45835
46533
|
}
|
|
45836
46534
|
},
|
|
45837
46535
|
"experimental.chat.messages.transform": async (input, output) => {
|
|
46536
|
+
await contextInjectorMessagesTransform?.["experimental.chat.messages.transform"]?.(input, output);
|
|
45838
46537
|
await thinkingBlockValidator?.["experimental.chat.messages.transform"]?.(input, output);
|
|
45839
46538
|
await emptyMessageSanitizer?.["experimental.chat.messages.transform"]?.(input, output);
|
|
45840
46539
|
},
|