opencode-matrixx 2.6.8 → 2.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/dynamic-agent-prompt-builder.d.ts +3 -0
- package/dist/hooks/directory-agents-injector/hook.d.ts +22 -3
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/post-read-injector/block.d.ts +1 -0
- package/dist/hooks/post-read-injector/constants.d.ts +4 -0
- package/dist/hooks/post-read-injector/hook.d.ts +27 -0
- package/dist/hooks/post-read-injector/index.d.ts +8 -0
- package/dist/hooks/post-read-injector/injector.d.ts +18 -0
- package/dist/hooks/post-read-injector/sections.d.ts +7 -0
- package/dist/hooks/post-read-injector/unified-walk.d.ts +6 -0
- package/dist/hooks/rules-injector/hook.d.ts +22 -23
- package/dist/index.js +968 -1042
- package/dist/plugin-handlers/agent-tool-allowlist.d.ts +11 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4550,18 +4550,18 @@ __export(exports_storage, {
|
|
|
4550
4550
|
readState: () => readState,
|
|
4551
4551
|
writeState: () => writeState
|
|
4552
4552
|
});
|
|
4553
|
-
import { existsSync as
|
|
4554
|
-
import { dirname as dirname9, join as
|
|
4553
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync14, readFileSync as readFileSync31, unlinkSync as unlinkSync8, writeFileSync as writeFileSync16 } from "fs";
|
|
4554
|
+
import { dirname as dirname9, join as join48 } from "path";
|
|
4555
4555
|
function getStateFilePath(directory, customPath) {
|
|
4556
|
-
return customPath ?
|
|
4556
|
+
return customPath ? join48(directory, customPath) : join48(directory, DEFAULT_STATE_FILE);
|
|
4557
4557
|
}
|
|
4558
4558
|
function readState(directory, customPath) {
|
|
4559
4559
|
const filePath = getStateFilePath(directory, customPath);
|
|
4560
|
-
if (!
|
|
4560
|
+
if (!existsSync38(filePath)) {
|
|
4561
4561
|
return null;
|
|
4562
4562
|
}
|
|
4563
4563
|
try {
|
|
4564
|
-
const content =
|
|
4564
|
+
const content = readFileSync31(filePath, "utf-8");
|
|
4565
4565
|
const { data, body } = parseFrontmatter(content);
|
|
4566
4566
|
const active = data.active;
|
|
4567
4567
|
const iteration = data.iteration;
|
|
@@ -4597,8 +4597,8 @@ function writeState(directory, state3, customPath) {
|
|
|
4597
4597
|
const filePath = getStateFilePath(directory, customPath);
|
|
4598
4598
|
try {
|
|
4599
4599
|
const dir = dirname9(filePath);
|
|
4600
|
-
if (!
|
|
4601
|
-
|
|
4600
|
+
if (!existsSync38(dir)) {
|
|
4601
|
+
mkdirSync14(dir, { recursive: true });
|
|
4602
4602
|
}
|
|
4603
4603
|
const sessionIdLine = state3.session_id ? `session_id: "${state3.session_id}"
|
|
4604
4604
|
` : "";
|
|
@@ -4615,7 +4615,7 @@ started_at: "${state3.started_at}"
|
|
|
4615
4615
|
${sessionIdLine}${ultraworkLine}${verificationLine}---
|
|
4616
4616
|
${state3.prompt}
|
|
4617
4617
|
`;
|
|
4618
|
-
|
|
4618
|
+
writeFileSync16(filePath, content, "utf-8");
|
|
4619
4619
|
return true;
|
|
4620
4620
|
} catch {
|
|
4621
4621
|
return false;
|
|
@@ -4624,8 +4624,8 @@ ${state3.prompt}
|
|
|
4624
4624
|
function clearState(directory, customPath) {
|
|
4625
4625
|
const filePath = getStateFilePath(directory, customPath);
|
|
4626
4626
|
try {
|
|
4627
|
-
if (
|
|
4628
|
-
|
|
4627
|
+
if (existsSync38(filePath)) {
|
|
4628
|
+
unlinkSync8(filePath);
|
|
4629
4629
|
}
|
|
4630
4630
|
return true;
|
|
4631
4631
|
} catch {
|
|
@@ -5645,8 +5645,8 @@ var require_semaphore = __commonJS(function(exports) {
|
|
|
5645
5645
|
this._waiting = [];
|
|
5646
5646
|
}
|
|
5647
5647
|
lock(thunk) {
|
|
5648
|
-
return new Promise((
|
|
5649
|
-
this._waiting.push({ thunk, resolve:
|
|
5648
|
+
return new Promise((resolve6, reject) => {
|
|
5649
|
+
this._waiting.push({ thunk, resolve: resolve6, reject });
|
|
5650
5650
|
this.runNext();
|
|
5651
5651
|
});
|
|
5652
5652
|
}
|
|
@@ -7137,9 +7137,9 @@ ${JSON.stringify(message, null, 4)}`);
|
|
|
7137
7137
|
if (typeof cancellationStrategy.sender.enableCancellation === "function") {
|
|
7138
7138
|
cancellationStrategy.sender.enableCancellation(requestMessage);
|
|
7139
7139
|
}
|
|
7140
|
-
return new Promise(async (
|
|
7140
|
+
return new Promise(async (resolve6, reject) => {
|
|
7141
7141
|
const resolveWithCleanup = (r) => {
|
|
7142
|
-
|
|
7142
|
+
resolve6(r);
|
|
7143
7143
|
cancellationStrategy.sender.cleanup(id);
|
|
7144
7144
|
disposable?.dispose();
|
|
7145
7145
|
};
|
|
@@ -7548,10 +7548,10 @@ var require_ril = __commonJS(function(exports) {
|
|
|
7548
7548
|
return api_1.Disposable.create(() => this.stream.off("end", listener));
|
|
7549
7549
|
}
|
|
7550
7550
|
write(data, encoding) {
|
|
7551
|
-
return new Promise((
|
|
7551
|
+
return new Promise((resolve6, reject) => {
|
|
7552
7552
|
const callback = (error51) => {
|
|
7553
7553
|
if (error51 === undefined || error51 === null) {
|
|
7554
|
-
|
|
7554
|
+
resolve6();
|
|
7555
7555
|
} else {
|
|
7556
7556
|
reject(error51);
|
|
7557
7557
|
}
|
|
@@ -7810,10 +7810,10 @@ var require_main = __commonJS(function(exports) {
|
|
|
7810
7810
|
exports.generateRandomPipeName = generateRandomPipeName;
|
|
7811
7811
|
function createClientPipeTransport(pipeName, encoding = "utf-8") {
|
|
7812
7812
|
let connectResolve;
|
|
7813
|
-
const connected = new Promise((
|
|
7814
|
-
connectResolve =
|
|
7813
|
+
const connected = new Promise((resolve6, _reject) => {
|
|
7814
|
+
connectResolve = resolve6;
|
|
7815
7815
|
});
|
|
7816
|
-
return new Promise((
|
|
7816
|
+
return new Promise((resolve6, reject) => {
|
|
7817
7817
|
let server = (0, net_1.createServer)((socket) => {
|
|
7818
7818
|
server.close();
|
|
7819
7819
|
connectResolve([
|
|
@@ -7824,7 +7824,7 @@ var require_main = __commonJS(function(exports) {
|
|
|
7824
7824
|
server.on("error", reject);
|
|
7825
7825
|
server.listen(pipeName, () => {
|
|
7826
7826
|
server.removeListener("error", reject);
|
|
7827
|
-
|
|
7827
|
+
resolve6({
|
|
7828
7828
|
onConnected: () => {
|
|
7829
7829
|
return connected;
|
|
7830
7830
|
}
|
|
@@ -7843,10 +7843,10 @@ var require_main = __commonJS(function(exports) {
|
|
|
7843
7843
|
exports.createServerPipeTransport = createServerPipeTransport;
|
|
7844
7844
|
function createClientSocketTransport(port, encoding = "utf-8") {
|
|
7845
7845
|
let connectResolve;
|
|
7846
|
-
const connected = new Promise((
|
|
7847
|
-
connectResolve =
|
|
7846
|
+
const connected = new Promise((resolve6, _reject) => {
|
|
7847
|
+
connectResolve = resolve6;
|
|
7848
7848
|
});
|
|
7849
|
-
return new Promise((
|
|
7849
|
+
return new Promise((resolve6, reject) => {
|
|
7850
7850
|
const server = (0, net_1.createServer)((socket) => {
|
|
7851
7851
|
server.close();
|
|
7852
7852
|
connectResolve([
|
|
@@ -7857,7 +7857,7 @@ var require_main = __commonJS(function(exports) {
|
|
|
7857
7857
|
server.on("error", reject);
|
|
7858
7858
|
server.listen(port, "127.0.0.1", () => {
|
|
7859
7859
|
server.removeListener("error", reject);
|
|
7860
|
-
|
|
7860
|
+
resolve6({
|
|
7861
7861
|
onConnected: () => {
|
|
7862
7862
|
return connected;
|
|
7863
7863
|
}
|
|
@@ -16061,8 +16061,8 @@ var require_TransformOperationExecutor = __commonJS(function(exports) {
|
|
|
16061
16061
|
return value;
|
|
16062
16062
|
return Buffer.from(value);
|
|
16063
16063
|
} else if ((0, utils_1.isPromise)(value) && !isMap) {
|
|
16064
|
-
return new Promise((
|
|
16065
|
-
value.then((data) =>
|
|
16064
|
+
return new Promise((resolve8, reject) => {
|
|
16065
|
+
value.then((data) => resolve8(this.transform(undefined, data, targetType, undefined, undefined, level + 1)), reject);
|
|
16066
16066
|
});
|
|
16067
16067
|
} else if (!isMap && value !== null && typeof value === "object" && typeof value.then === "function") {
|
|
16068
16068
|
return value;
|
|
@@ -55027,7 +55027,7 @@ var DELEGATE_TASK_ERROR_PATTERNS = [
|
|
|
55027
55027
|
{
|
|
55028
55028
|
pattern: "Cannot call primary agent",
|
|
55029
55029
|
errorType: "primary_agent",
|
|
55030
|
-
fixHint: "Primary agents cannot be called via task. Use a subagent like 'explore', 'oracle', or 'librarian'"
|
|
55030
|
+
fixHint: "Primary agents cannot be called via task. Use a subagent like 'explore', 'oracle', or 'librarian'. For 'architect', use /start-work to run Architect as session agent"
|
|
55031
55031
|
},
|
|
55032
55032
|
{
|
|
55033
55033
|
pattern: "Operation timed out",
|
|
@@ -55228,93 +55228,6 @@ ${originalText}`;
|
|
|
55228
55228
|
}
|
|
55229
55229
|
};
|
|
55230
55230
|
}
|
|
55231
|
-
// src/hooks/directory-injector/factory.ts
|
|
55232
|
-
function createDirectoryInjectorHook(ctx, processFile, clearInjectedPaths) {
|
|
55233
|
-
const sessionCaches = new Map;
|
|
55234
|
-
const truncator = createDynamicTruncator(ctx);
|
|
55235
|
-
const toolExecuteAfter = async (input, output) => {
|
|
55236
|
-
const toolName = input.tool.toLowerCase();
|
|
55237
|
-
if (toolName === "read") {
|
|
55238
|
-
await processFile({
|
|
55239
|
-
ctx,
|
|
55240
|
-
truncator,
|
|
55241
|
-
sessionCaches,
|
|
55242
|
-
filePath: output.title,
|
|
55243
|
-
sessionID: input.sessionID,
|
|
55244
|
-
output
|
|
55245
|
-
});
|
|
55246
|
-
return;
|
|
55247
|
-
}
|
|
55248
|
-
};
|
|
55249
|
-
const toolExecuteBefore = async (input, output) => {};
|
|
55250
|
-
const eventHandler = async ({ event }) => {
|
|
55251
|
-
const props = event.properties;
|
|
55252
|
-
if (event.type === "session.deleted") {
|
|
55253
|
-
const sessionInfo = props?.info;
|
|
55254
|
-
if (sessionInfo?.id) {
|
|
55255
|
-
sessionCaches.delete(sessionInfo.id);
|
|
55256
|
-
clearInjectedPaths(sessionInfo.id);
|
|
55257
|
-
}
|
|
55258
|
-
}
|
|
55259
|
-
if (event.type === "session.compacted") {
|
|
55260
|
-
const sessionID = props?.sessionID ?? props?.info?.id;
|
|
55261
|
-
if (sessionID) {
|
|
55262
|
-
sessionCaches.delete(sessionID);
|
|
55263
|
-
clearInjectedPaths(sessionID);
|
|
55264
|
-
}
|
|
55265
|
-
}
|
|
55266
|
-
};
|
|
55267
|
-
return {
|
|
55268
|
-
"tool.execute.before": toolExecuteBefore,
|
|
55269
|
-
"tool.execute.after": toolExecuteAfter,
|
|
55270
|
-
event: eventHandler
|
|
55271
|
-
};
|
|
55272
|
-
}
|
|
55273
|
-
// src/hooks/directory-agents-injector/injector.ts
|
|
55274
|
-
import { readFile } from "fs/promises";
|
|
55275
|
-
import { dirname as dirname7 } from "path";
|
|
55276
|
-
|
|
55277
|
-
// src/hooks/directory-agents-injector/finder.ts
|
|
55278
|
-
import { access } from "fs/promises";
|
|
55279
|
-
import { dirname as dirname6, join as join38, resolve as resolve2 } from "path";
|
|
55280
|
-
|
|
55281
|
-
// src/hooks/directory-agents-injector/constants.ts
|
|
55282
|
-
import { join as join37 } from "path";
|
|
55283
|
-
var AGENTS_INJECTOR_STORAGE = join37(OPENCODE_STORAGE, "directory-agents");
|
|
55284
|
-
var AGENTS_FILENAME = "AGENTS.md";
|
|
55285
|
-
|
|
55286
|
-
// src/hooks/directory-agents-injector/finder.ts
|
|
55287
|
-
function resolveFilePath2(rootDirectory, path10) {
|
|
55288
|
-
if (!path10)
|
|
55289
|
-
return null;
|
|
55290
|
-
if (path10.startsWith("/"))
|
|
55291
|
-
return path10;
|
|
55292
|
-
return resolve2(rootDirectory, path10);
|
|
55293
|
-
}
|
|
55294
|
-
async function findAgentsMdUp(input) {
|
|
55295
|
-
const found = [];
|
|
55296
|
-
let current = input.startDir;
|
|
55297
|
-
while (true) {
|
|
55298
|
-
const isRootDir = current === input.rootDir;
|
|
55299
|
-
if (!isRootDir) {
|
|
55300
|
-
const agentsPath = join38(current, AGENTS_FILENAME);
|
|
55301
|
-
const exists = await access(agentsPath).then(() => true).catch(() => false);
|
|
55302
|
-
if (exists) {
|
|
55303
|
-
found.push(agentsPath);
|
|
55304
|
-
}
|
|
55305
|
-
}
|
|
55306
|
-
if (isRootDir)
|
|
55307
|
-
break;
|
|
55308
|
-
const parent = dirname6(current);
|
|
55309
|
-
if (parent === current)
|
|
55310
|
-
break;
|
|
55311
|
-
if (!parent.startsWith(input.rootDir))
|
|
55312
|
-
break;
|
|
55313
|
-
current = parent;
|
|
55314
|
-
}
|
|
55315
|
-
return found.reverse();
|
|
55316
|
-
}
|
|
55317
|
-
|
|
55318
55231
|
// src/shared/session-injected-paths.ts
|
|
55319
55232
|
import {
|
|
55320
55233
|
existsSync as existsSync32,
|
|
@@ -55323,9 +55236,9 @@ import {
|
|
|
55323
55236
|
unlinkSync as unlinkSync4,
|
|
55324
55237
|
writeFileSync as writeFileSync12
|
|
55325
55238
|
} from "fs";
|
|
55326
|
-
import { join as
|
|
55239
|
+
import { join as join37 } from "path";
|
|
55327
55240
|
function createInjectedPathsStorage(storageDir) {
|
|
55328
|
-
const getStoragePath2 = (sessionID) =>
|
|
55241
|
+
const getStoragePath2 = (sessionID) => join37(storageDir, `${sessionID}.json`);
|
|
55329
55242
|
const loadInjectedPaths = (sessionID) => {
|
|
55330
55243
|
const filePath = getStoragePath2(sessionID);
|
|
55331
55244
|
if (!existsSync32(filePath))
|
|
@@ -55362,6 +55275,11 @@ function createInjectedPathsStorage(storageDir) {
|
|
|
55362
55275
|
};
|
|
55363
55276
|
}
|
|
55364
55277
|
|
|
55278
|
+
// src/hooks/directory-agents-injector/constants.ts
|
|
55279
|
+
import { join as join38 } from "path";
|
|
55280
|
+
var AGENTS_INJECTOR_STORAGE = join38(OPENCODE_STORAGE, "directory-agents");
|
|
55281
|
+
var AGENTS_FILENAME = "AGENTS.md";
|
|
55282
|
+
|
|
55365
55283
|
// src/hooks/directory-agents-injector/storage.ts
|
|
55366
55284
|
var {
|
|
55367
55285
|
loadInjectedPaths,
|
|
@@ -55369,47 +55287,581 @@ var {
|
|
|
55369
55287
|
clearInjectedPaths
|
|
55370
55288
|
} = createInjectedPathsStorage(AGENTS_INJECTOR_STORAGE);
|
|
55371
55289
|
|
|
55372
|
-
// src/hooks/
|
|
55373
|
-
function
|
|
55374
|
-
|
|
55375
|
-
|
|
55290
|
+
// src/hooks/rules-injector/output-path.ts
|
|
55291
|
+
function getRuleInjectionFilePath(output) {
|
|
55292
|
+
const metadata = output.metadata;
|
|
55293
|
+
const metadataFilePath = metadata && typeof metadata === "object" ? metadata.filePath : undefined;
|
|
55294
|
+
if (typeof metadataFilePath === "string" && metadataFilePath.length > 0) {
|
|
55295
|
+
return metadataFilePath;
|
|
55376
55296
|
}
|
|
55377
|
-
|
|
55297
|
+
if (typeof output.title === "string" && output.title.length > 0) {
|
|
55298
|
+
return output.title;
|
|
55299
|
+
}
|
|
55300
|
+
return null;
|
|
55378
55301
|
}
|
|
55379
|
-
|
|
55380
|
-
|
|
55381
|
-
|
|
55382
|
-
|
|
55383
|
-
|
|
55384
|
-
|
|
55385
|
-
|
|
55386
|
-
|
|
55387
|
-
|
|
55388
|
-
|
|
55389
|
-
|
|
55302
|
+
|
|
55303
|
+
// src/hooks/rules-injector/storage.ts
|
|
55304
|
+
import {
|
|
55305
|
+
existsSync as existsSync33,
|
|
55306
|
+
mkdirSync as mkdirSync10,
|
|
55307
|
+
readFileSync as readFileSync26,
|
|
55308
|
+
unlinkSync as unlinkSync5,
|
|
55309
|
+
writeFileSync as writeFileSync13
|
|
55310
|
+
} from "fs";
|
|
55311
|
+
import { join as join40 } from "path";
|
|
55312
|
+
|
|
55313
|
+
// src/hooks/rules-injector/constants.ts
|
|
55314
|
+
import { join as join39 } from "path";
|
|
55315
|
+
var RULES_INJECTOR_STORAGE = join39(OPENCODE_STORAGE, "rules-injector");
|
|
55316
|
+
var PROJECT_MARKERS = [
|
|
55317
|
+
".git",
|
|
55318
|
+
"pyproject.toml",
|
|
55319
|
+
"package.json",
|
|
55320
|
+
"Cargo.toml",
|
|
55321
|
+
"go.mod",
|
|
55322
|
+
".venv"
|
|
55323
|
+
];
|
|
55324
|
+
var PROJECT_RULE_SUBDIRS = [
|
|
55325
|
+
[".github", "instructions"],
|
|
55326
|
+
[".cursor", "rules"],
|
|
55327
|
+
[".matrixx", "rules"]
|
|
55328
|
+
];
|
|
55329
|
+
var PROJECT_RULE_FILES = [
|
|
55330
|
+
".github/copilot-instructions.md"
|
|
55331
|
+
];
|
|
55332
|
+
var GITHUB_INSTRUCTIONS_PATTERN = /\.instructions\.md$/;
|
|
55333
|
+
var RULE_EXTENSIONS = [".md", ".mdc"];
|
|
55334
|
+
|
|
55335
|
+
// src/hooks/rules-injector/storage.ts
|
|
55336
|
+
function getStoragePath2(sessionID) {
|
|
55337
|
+
return join40(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
55338
|
+
}
|
|
55339
|
+
function loadInjectedRules(sessionID) {
|
|
55340
|
+
const filePath = getStoragePath2(sessionID);
|
|
55341
|
+
if (!existsSync33(filePath))
|
|
55342
|
+
return { contentHashes: new Set, realPaths: new Set };
|
|
55343
|
+
try {
|
|
55344
|
+
const content = readFileSync26(filePath, "utf-8");
|
|
55345
|
+
const data = JSON.parse(content);
|
|
55346
|
+
return {
|
|
55347
|
+
contentHashes: new Set(data.injectedHashes),
|
|
55348
|
+
realPaths: new Set(data.injectedRealPaths ?? [])
|
|
55349
|
+
};
|
|
55350
|
+
} catch {
|
|
55351
|
+
return { contentHashes: new Set, realPaths: new Set };
|
|
55352
|
+
}
|
|
55353
|
+
}
|
|
55354
|
+
function saveInjectedRules(sessionID, data) {
|
|
55355
|
+
if (!existsSync33(RULES_INJECTOR_STORAGE)) {
|
|
55356
|
+
mkdirSync10(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
55357
|
+
}
|
|
55358
|
+
const storageData = {
|
|
55359
|
+
sessionID,
|
|
55360
|
+
injectedHashes: [...data.contentHashes],
|
|
55361
|
+
injectedRealPaths: [...data.realPaths],
|
|
55362
|
+
updatedAt: Date.now()
|
|
55363
|
+
};
|
|
55364
|
+
writeFileSync13(getStoragePath2(sessionID), JSON.stringify(storageData, null, 2));
|
|
55365
|
+
}
|
|
55366
|
+
function clearInjectedRules(sessionID) {
|
|
55367
|
+
const filePath = getStoragePath2(sessionID);
|
|
55368
|
+
if (existsSync33(filePath)) {
|
|
55369
|
+
unlinkSync5(filePath);
|
|
55370
|
+
}
|
|
55371
|
+
}
|
|
55372
|
+
|
|
55373
|
+
// src/hooks/post-read-injector/injector.ts
|
|
55374
|
+
import { readFileSync as readFileSync28 } from "fs";
|
|
55375
|
+
import { join as join43, relative as relative2, resolve as resolve2 } from "path";
|
|
55376
|
+
|
|
55377
|
+
// src/hooks/rules-injector/matcher.ts
|
|
55378
|
+
var import_picomatch = __toESM(require_picomatch2(), 1);
|
|
55379
|
+
import { createHash } from "crypto";
|
|
55380
|
+
import { relative } from "path";
|
|
55381
|
+
function shouldApplyRule(metadata, currentFilePath, projectRoot) {
|
|
55382
|
+
if (metadata.alwaysApply === true) {
|
|
55383
|
+
return { applies: true, reason: "alwaysApply" };
|
|
55384
|
+
}
|
|
55385
|
+
const globs = metadata.globs;
|
|
55386
|
+
if (!globs) {
|
|
55387
|
+
return { applies: false };
|
|
55388
|
+
}
|
|
55389
|
+
const patterns = Array.isArray(globs) ? globs : [globs];
|
|
55390
|
+
if (patterns.length === 0) {
|
|
55391
|
+
return { applies: false };
|
|
55392
|
+
}
|
|
55393
|
+
const relativePath = projectRoot ? relative(projectRoot, currentFilePath) : currentFilePath;
|
|
55394
|
+
for (const pattern of patterns) {
|
|
55395
|
+
if (import_picomatch.default.isMatch(relativePath, pattern, { dot: true, bash: true })) {
|
|
55396
|
+
return { applies: true, reason: `glob: ${pattern}` };
|
|
55397
|
+
}
|
|
55398
|
+
}
|
|
55399
|
+
return { applies: false };
|
|
55400
|
+
}
|
|
55401
|
+
function isDuplicateByRealPath(realPath, cache2) {
|
|
55402
|
+
return cache2.has(realPath);
|
|
55403
|
+
}
|
|
55404
|
+
function createContentHash(content) {
|
|
55405
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
55406
|
+
}
|
|
55407
|
+
function isDuplicateByContentHash(hash2, cache2) {
|
|
55408
|
+
return cache2.has(hash2);
|
|
55409
|
+
}
|
|
55410
|
+
|
|
55411
|
+
// src/hooks/rules-injector/parser.ts
|
|
55412
|
+
function parseRuleFrontmatter(content) {
|
|
55413
|
+
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
55414
|
+
const match = content.match(frontmatterRegex);
|
|
55415
|
+
if (!match) {
|
|
55416
|
+
return { metadata: {}, body: content };
|
|
55417
|
+
}
|
|
55418
|
+
const yamlContent = match[1];
|
|
55419
|
+
const body = match[2];
|
|
55420
|
+
try {
|
|
55421
|
+
const metadata = parseYamlContent(yamlContent);
|
|
55422
|
+
return { metadata, body };
|
|
55423
|
+
} catch {
|
|
55424
|
+
return { metadata: {}, body: content };
|
|
55425
|
+
}
|
|
55426
|
+
}
|
|
55427
|
+
function parseYamlContent(yamlContent) {
|
|
55428
|
+
const lines = yamlContent.split(`
|
|
55429
|
+
`);
|
|
55430
|
+
const metadata = {};
|
|
55431
|
+
let i = 0;
|
|
55432
|
+
while (i < lines.length) {
|
|
55433
|
+
const line = lines[i];
|
|
55434
|
+
const colonIndex = line.indexOf(":");
|
|
55435
|
+
if (colonIndex === -1) {
|
|
55436
|
+
i++;
|
|
55390
55437
|
continue;
|
|
55391
|
-
|
|
55392
|
-
|
|
55393
|
-
|
|
55394
|
-
|
|
55438
|
+
}
|
|
55439
|
+
const key = line.slice(0, colonIndex).trim();
|
|
55440
|
+
const rawValue = line.slice(colonIndex + 1).trim();
|
|
55441
|
+
if (key === "description") {
|
|
55442
|
+
metadata.description = parseStringValue(rawValue);
|
|
55443
|
+
} else if (key === "alwaysApply") {
|
|
55444
|
+
metadata.alwaysApply = rawValue === "true";
|
|
55445
|
+
} else if (key === "globs" || key === "applyTo") {
|
|
55446
|
+
const { value, consumed } = parseArrayOrStringValue(rawValue, lines, i);
|
|
55447
|
+
metadata.globs = mergeGlobs(metadata.globs, value);
|
|
55448
|
+
i += consumed;
|
|
55449
|
+
continue;
|
|
55450
|
+
}
|
|
55451
|
+
i++;
|
|
55452
|
+
}
|
|
55453
|
+
return metadata;
|
|
55454
|
+
}
|
|
55455
|
+
function parseStringValue(value) {
|
|
55456
|
+
if (!value)
|
|
55457
|
+
return "";
|
|
55458
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
55459
|
+
return value.slice(1, -1);
|
|
55460
|
+
}
|
|
55461
|
+
return value;
|
|
55462
|
+
}
|
|
55463
|
+
function parseArrayOrStringValue(rawValue, lines, currentIndex) {
|
|
55464
|
+
if (rawValue.startsWith("[")) {
|
|
55465
|
+
return { value: parseInlineArray(rawValue), consumed: 1 };
|
|
55466
|
+
}
|
|
55467
|
+
if (!rawValue || rawValue === "") {
|
|
55468
|
+
const arrayItems = [];
|
|
55469
|
+
let consumed = 1;
|
|
55470
|
+
for (let j = currentIndex + 1;j < lines.length; j++) {
|
|
55471
|
+
const nextLine = lines[j];
|
|
55472
|
+
const arrayMatch = nextLine.match(/^\s+-\s*(.*)$/);
|
|
55473
|
+
if (arrayMatch) {
|
|
55474
|
+
const itemValue = parseStringValue(arrayMatch[1].trim());
|
|
55475
|
+
if (itemValue) {
|
|
55476
|
+
arrayItems.push(itemValue);
|
|
55477
|
+
}
|
|
55478
|
+
consumed++;
|
|
55479
|
+
} else if (nextLine.trim() === "") {
|
|
55480
|
+
consumed++;
|
|
55481
|
+
} else {
|
|
55482
|
+
break;
|
|
55483
|
+
}
|
|
55484
|
+
}
|
|
55485
|
+
if (arrayItems.length > 0) {
|
|
55486
|
+
return { value: arrayItems, consumed };
|
|
55487
|
+
}
|
|
55488
|
+
}
|
|
55489
|
+
const stringValue = parseStringValue(rawValue);
|
|
55490
|
+
if (stringValue.includes(",")) {
|
|
55491
|
+
const items = stringValue.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
55492
|
+
return { value: items, consumed: 1 };
|
|
55493
|
+
}
|
|
55494
|
+
return { value: stringValue, consumed: 1 };
|
|
55495
|
+
}
|
|
55496
|
+
function parseInlineArray(value) {
|
|
55497
|
+
const content = value.slice(1, value.lastIndexOf("]")).trim();
|
|
55498
|
+
if (!content)
|
|
55499
|
+
return [];
|
|
55500
|
+
const items = [];
|
|
55501
|
+
let current = "";
|
|
55502
|
+
let inQuote = false;
|
|
55503
|
+
let quoteChar = "";
|
|
55504
|
+
for (let i = 0;i < content.length; i++) {
|
|
55505
|
+
const char = content[i];
|
|
55506
|
+
if (!inQuote && (char === '"' || char === "'")) {
|
|
55507
|
+
inQuote = true;
|
|
55508
|
+
quoteChar = char;
|
|
55509
|
+
} else if (inQuote && char === quoteChar) {
|
|
55510
|
+
inQuote = false;
|
|
55511
|
+
quoteChar = "";
|
|
55512
|
+
} else if (!inQuote && char === ",") {
|
|
55513
|
+
const trimmed2 = current.trim();
|
|
55514
|
+
if (trimmed2) {
|
|
55515
|
+
items.push(parseStringValue(trimmed2));
|
|
55516
|
+
}
|
|
55517
|
+
current = "";
|
|
55518
|
+
} else {
|
|
55519
|
+
current += char;
|
|
55520
|
+
}
|
|
55521
|
+
}
|
|
55522
|
+
const trimmed = current.trim();
|
|
55523
|
+
if (trimmed) {
|
|
55524
|
+
items.push(parseStringValue(trimmed));
|
|
55525
|
+
}
|
|
55526
|
+
return items;
|
|
55527
|
+
}
|
|
55528
|
+
function mergeGlobs(existing, newValue) {
|
|
55529
|
+
if (!existing)
|
|
55530
|
+
return newValue;
|
|
55531
|
+
const existingArray = Array.isArray(existing) ? existing : [existing];
|
|
55532
|
+
const newArray = Array.isArray(newValue) ? newValue : [newValue];
|
|
55533
|
+
return [...existingArray, ...newArray];
|
|
55534
|
+
}
|
|
55535
|
+
|
|
55536
|
+
// src/hooks/rules-injector/project-root-finder.ts
|
|
55537
|
+
import { existsSync as existsSync34, statSync as statSync4 } from "fs";
|
|
55538
|
+
import { dirname as dirname6, join as join41 } from "path";
|
|
55539
|
+
function findProjectRoot(startPath) {
|
|
55540
|
+
let current;
|
|
55541
|
+
try {
|
|
55542
|
+
const stat = statSync4(startPath);
|
|
55543
|
+
current = stat.isDirectory() ? startPath : dirname6(startPath);
|
|
55544
|
+
} catch {
|
|
55545
|
+
current = dirname6(startPath);
|
|
55546
|
+
}
|
|
55547
|
+
while (true) {
|
|
55548
|
+
for (const marker of PROJECT_MARKERS) {
|
|
55549
|
+
const markerPath = join41(current, marker);
|
|
55550
|
+
if (existsSync34(markerPath)) {
|
|
55551
|
+
return current;
|
|
55552
|
+
}
|
|
55553
|
+
}
|
|
55554
|
+
const parent = dirname6(current);
|
|
55555
|
+
if (parent === current) {
|
|
55556
|
+
return null;
|
|
55557
|
+
}
|
|
55558
|
+
current = parent;
|
|
55559
|
+
}
|
|
55560
|
+
}
|
|
55561
|
+
|
|
55562
|
+
// src/hooks/rules-injector/rule-file-scanner.ts
|
|
55563
|
+
import { existsSync as existsSync35, readdirSync as readdirSync11, realpathSync as realpathSync2 } from "fs";
|
|
55564
|
+
import { join as join42 } from "path";
|
|
55565
|
+
function isGitHubInstructionsDir(dir) {
|
|
55566
|
+
return dir.includes(".github/instructions") || dir.endsWith(".github/instructions");
|
|
55567
|
+
}
|
|
55568
|
+
function isValidRuleFile(fileName, dir) {
|
|
55569
|
+
if (isGitHubInstructionsDir(dir)) {
|
|
55570
|
+
return GITHUB_INSTRUCTIONS_PATTERN.test(fileName);
|
|
55571
|
+
}
|
|
55572
|
+
return RULE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
|
|
55573
|
+
}
|
|
55574
|
+
function findRuleFilesRecursive(dir, results) {
|
|
55575
|
+
if (!existsSync35(dir))
|
|
55576
|
+
return;
|
|
55577
|
+
try {
|
|
55578
|
+
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
55579
|
+
for (const entry of entries) {
|
|
55580
|
+
const fullPath = join42(dir, entry.name);
|
|
55581
|
+
if (entry.isDirectory()) {
|
|
55582
|
+
findRuleFilesRecursive(fullPath, results);
|
|
55583
|
+
} else if (entry.isFile()) {
|
|
55584
|
+
if (isValidRuleFile(entry.name, dir)) {
|
|
55585
|
+
results.push(fullPath);
|
|
55586
|
+
}
|
|
55587
|
+
}
|
|
55588
|
+
}
|
|
55589
|
+
} catch {}
|
|
55590
|
+
}
|
|
55591
|
+
function safeRealpathSync(filePath) {
|
|
55592
|
+
try {
|
|
55593
|
+
return realpathSync2(filePath);
|
|
55594
|
+
} catch {
|
|
55595
|
+
return filePath;
|
|
55596
|
+
}
|
|
55597
|
+
}
|
|
55598
|
+
|
|
55599
|
+
// src/hooks/post-read-injector/constants.ts
|
|
55600
|
+
var MAX_DIRECTORY_CONTEXT_BYTES = 6144;
|
|
55601
|
+
var DIRECTORY_CONTEXT_HEADER = "Directory Context";
|
|
55602
|
+
var DIRECTORY_CONTEXT_SEPARATOR = `
|
|
55603
|
+
---
|
|
55604
|
+
`;
|
|
55605
|
+
function buildTruncationMarker(omittedChars) {
|
|
55606
|
+
return `
|
|
55607
|
+
[${DIRECTORY_CONTEXT_HEADER} truncated: ${omittedChars} chars omitted]`;
|
|
55608
|
+
}
|
|
55609
|
+
|
|
55610
|
+
// src/hooks/post-read-injector/block.ts
|
|
55611
|
+
function buildDirectoryContextBlock(sections) {
|
|
55612
|
+
const header = `
|
|
55613
|
+
|
|
55614
|
+
[${DIRECTORY_CONTEXT_HEADER}]
|
|
55615
|
+
`;
|
|
55616
|
+
const fullBody = sections.join(DIRECTORY_CONTEXT_SEPARATOR);
|
|
55617
|
+
const full = `${header}${fullBody}`;
|
|
55618
|
+
if (full.length <= MAX_DIRECTORY_CONTEXT_BYTES)
|
|
55619
|
+
return full;
|
|
55620
|
+
const probeMarker = buildTruncationMarker(fullBody.length);
|
|
55621
|
+
const allowedBody = MAX_DIRECTORY_CONTEXT_BYTES - header.length - probeMarker.length;
|
|
55622
|
+
const sliced = fullBody.slice(0, Math.max(0, allowedBody));
|
|
55623
|
+
const omitted = fullBody.length - sliced.length;
|
|
55624
|
+
const marker = buildTruncationMarker(omitted);
|
|
55625
|
+
const block = `${header}${sliced}${marker}`;
|
|
55626
|
+
if (block.length <= MAX_DIRECTORY_CONTEXT_BYTES)
|
|
55627
|
+
return block;
|
|
55628
|
+
const overflow = block.length - MAX_DIRECTORY_CONTEXT_BYTES;
|
|
55629
|
+
return `${header}${sliced.slice(0, Math.max(0, sliced.length - overflow))}${marker}`;
|
|
55630
|
+
}
|
|
55631
|
+
|
|
55632
|
+
// src/hooks/post-read-injector/sections.ts
|
|
55633
|
+
import { readFileSync as readFileSync27 } from "fs";
|
|
55634
|
+
import { access, readFile } from "fs/promises";
|
|
55635
|
+
async function readAgentsSection(agentsPath, sessionID, truncator) {
|
|
55636
|
+
const exists = await access(agentsPath).then(() => true).catch(() => false);
|
|
55637
|
+
if (!exists)
|
|
55638
|
+
return null;
|
|
55639
|
+
try {
|
|
55640
|
+
const content = await readFile(agentsPath, "utf-8");
|
|
55641
|
+
const { result, truncated } = await truncator.truncate(sessionID, content);
|
|
55642
|
+
const notice = truncated ? `
|
|
55395
55643
|
|
|
55396
55644
|
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${agentsPath}]` : "";
|
|
55397
|
-
|
|
55645
|
+
return `[Directory Context: ${agentsPath}]
|
|
55646
|
+
${result}${notice}`;
|
|
55647
|
+
} catch {
|
|
55648
|
+
return null;
|
|
55649
|
+
}
|
|
55650
|
+
}
|
|
55651
|
+
async function readRuleSection(candidate, rel, reason, sessionID, truncator, rulesCache) {
|
|
55652
|
+
try {
|
|
55653
|
+
const raw = readFileSync27(candidate, "utf-8");
|
|
55654
|
+
const { body } = parseRuleFrontmatter(raw);
|
|
55655
|
+
const hash2 = createContentHash(body);
|
|
55656
|
+
if (isDuplicateByContentHash(hash2, rulesCache.contentHashes))
|
|
55657
|
+
return null;
|
|
55658
|
+
const { result, truncated } = await truncator.truncate(sessionID, body);
|
|
55659
|
+
const notice = truncated ? `
|
|
55660
|
+
|
|
55661
|
+
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${rel}]` : "";
|
|
55662
|
+
rulesCache.contentHashes.add(hash2);
|
|
55663
|
+
return `[Rule: ${rel}]
|
|
55664
|
+
[Match: ${reason}]
|
|
55665
|
+
${result}${notice}`;
|
|
55666
|
+
} catch {
|
|
55667
|
+
return null;
|
|
55668
|
+
}
|
|
55669
|
+
}
|
|
55398
55670
|
|
|
55399
|
-
|
|
55400
|
-
|
|
55401
|
-
|
|
55402
|
-
|
|
55403
|
-
|
|
55671
|
+
// src/hooks/post-read-injector/unified-walk.ts
|
|
55672
|
+
import { dirname as dirname7 } from "path";
|
|
55673
|
+
function normalize(dir) {
|
|
55674
|
+
return dir.endsWith("/") && dir.length > 1 ? dir.slice(0, -1) : dir;
|
|
55675
|
+
}
|
|
55676
|
+
function collectAncestorDirs(filePath, rootDir, projectRoot, onVisit) {
|
|
55677
|
+
let visit2;
|
|
55678
|
+
if (typeof rootDir === "function")
|
|
55679
|
+
visit2 = rootDir;
|
|
55680
|
+
else if (typeof projectRoot === "function")
|
|
55681
|
+
visit2 = projectRoot;
|
|
55682
|
+
if (typeof onVisit === "function")
|
|
55683
|
+
visit2 = onVisit;
|
|
55684
|
+
let current;
|
|
55685
|
+
try {
|
|
55686
|
+
current = normalize(dirname7(filePath));
|
|
55687
|
+
} catch {
|
|
55688
|
+
return [];
|
|
55404
55689
|
}
|
|
55405
|
-
|
|
55406
|
-
|
|
55690
|
+
const dirs = [];
|
|
55691
|
+
const seen = new Set;
|
|
55692
|
+
while (true) {
|
|
55693
|
+
if (!seen.has(current)) {
|
|
55694
|
+
seen.add(current);
|
|
55695
|
+
dirs.push(current);
|
|
55696
|
+
visit2?.(current);
|
|
55697
|
+
}
|
|
55698
|
+
const parent = normalize(dirname7(current));
|
|
55699
|
+
if (parent === current)
|
|
55700
|
+
break;
|
|
55701
|
+
current = parent;
|
|
55407
55702
|
}
|
|
55703
|
+
return dirs;
|
|
55704
|
+
}
|
|
55705
|
+
|
|
55706
|
+
// src/hooks/post-read-injector/injector.ts
|
|
55707
|
+
function resolveUnifiedFilePath(rootDirectory, filePath) {
|
|
55708
|
+
if (!filePath)
|
|
55709
|
+
return null;
|
|
55710
|
+
if (filePath.startsWith("/"))
|
|
55711
|
+
return filePath;
|
|
55712
|
+
return resolve2(rootDirectory, filePath);
|
|
55713
|
+
}
|
|
55714
|
+
function createUnifiedPostReadProcessor(deps) {
|
|
55715
|
+
const { workspaceDirectory, truncator, onVisitDir } = deps;
|
|
55716
|
+
const agentsCaches = new Map;
|
|
55717
|
+
const rulesCaches = new Map;
|
|
55718
|
+
function getAgentsCache(sessionID) {
|
|
55719
|
+
let cache2 = agentsCaches.get(sessionID);
|
|
55720
|
+
if (!cache2) {
|
|
55721
|
+
cache2 = loadInjectedPaths(sessionID);
|
|
55722
|
+
agentsCaches.set(sessionID, cache2);
|
|
55723
|
+
}
|
|
55724
|
+
return cache2;
|
|
55725
|
+
}
|
|
55726
|
+
function getRulesCache(sessionID) {
|
|
55727
|
+
let cache2 = rulesCaches.get(sessionID);
|
|
55728
|
+
if (!cache2) {
|
|
55729
|
+
cache2 = loadInjectedRules(sessionID);
|
|
55730
|
+
rulesCaches.set(sessionID, cache2);
|
|
55731
|
+
}
|
|
55732
|
+
return cache2;
|
|
55733
|
+
}
|
|
55734
|
+
function clearSessionCache(sessionID) {
|
|
55735
|
+
agentsCaches.delete(sessionID);
|
|
55736
|
+
rulesCaches.delete(sessionID);
|
|
55737
|
+
}
|
|
55738
|
+
async function processFilePathForInjection(filePath, sessionID, output) {
|
|
55739
|
+
if (output.output.includes(`[${DIRECTORY_CONTEXT_HEADER}]`))
|
|
55740
|
+
return;
|
|
55741
|
+
const resolved = resolveUnifiedFilePath(workspaceDirectory, filePath);
|
|
55742
|
+
if (!resolved)
|
|
55743
|
+
return;
|
|
55744
|
+
const projectRoot = findProjectRoot(resolved);
|
|
55745
|
+
const agentsCache = getAgentsCache(sessionID);
|
|
55746
|
+
const rulesCache = getRulesCache(sessionID);
|
|
55747
|
+
const dirs = collectAncestorDirs(resolved, workspaceDirectory, projectRoot, onVisitDir);
|
|
55748
|
+
const sections = [];
|
|
55749
|
+
const seenRulePaths = new Set;
|
|
55750
|
+
let agentsDirty = false;
|
|
55751
|
+
let rulesDirty = false;
|
|
55752
|
+
for (const dir of dirs) {
|
|
55753
|
+
if (dir !== workspaceDirectory && (dir === workspaceDirectory || dir.startsWith(`${workspaceDirectory}/`))) {
|
|
55754
|
+
if (!agentsCache.has(dir)) {
|
|
55755
|
+
const section = await readAgentsSection(join43(dir, AGENTS_FILENAME), sessionID, truncator);
|
|
55756
|
+
if (section) {
|
|
55757
|
+
sections.push(section);
|
|
55758
|
+
agentsCache.add(dir);
|
|
55759
|
+
agentsDirty = true;
|
|
55760
|
+
}
|
|
55761
|
+
}
|
|
55762
|
+
}
|
|
55763
|
+
const inScope = !projectRoot || dir === projectRoot || dir.startsWith(`${projectRoot}/`) || projectRoot.startsWith(`${dir}/`);
|
|
55764
|
+
if (!inScope)
|
|
55765
|
+
continue;
|
|
55766
|
+
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
55767
|
+
const files = [];
|
|
55768
|
+
findRuleFilesRecursive(join43(dir, parent, subdir), files);
|
|
55769
|
+
for (const candidate of files) {
|
|
55770
|
+
const realPath = safeRealpathSync(candidate);
|
|
55771
|
+
if (seenRulePaths.has(realPath))
|
|
55772
|
+
continue;
|
|
55773
|
+
seenRulePaths.add(realPath);
|
|
55774
|
+
if (isDuplicateByRealPath(realPath, rulesCache.realPaths))
|
|
55775
|
+
continue;
|
|
55776
|
+
const { metadata } = parseRuleFrontmatter(readFileSync28(candidate, "utf-8"));
|
|
55777
|
+
const match = shouldApplyRule(metadata, resolved, projectRoot);
|
|
55778
|
+
if (!match.applies)
|
|
55779
|
+
continue;
|
|
55780
|
+
const rel = projectRoot ? relative2(projectRoot, candidate) : candidate;
|
|
55781
|
+
const section = await readRuleSection(candidate, rel, match.reason ?? "matched", sessionID, truncator, rulesCache);
|
|
55782
|
+
if (section) {
|
|
55783
|
+
sections.push(section);
|
|
55784
|
+
rulesCache.realPaths.add(realPath);
|
|
55785
|
+
rulesDirty = true;
|
|
55786
|
+
}
|
|
55787
|
+
}
|
|
55788
|
+
}
|
|
55789
|
+
if (projectRoot && dir === projectRoot) {
|
|
55790
|
+
for (const single of PROJECT_RULE_FILES) {
|
|
55791
|
+
const candidate = join43(projectRoot, single);
|
|
55792
|
+
const realPath = safeRealpathSync(candidate);
|
|
55793
|
+
if (seenRulePaths.has(realPath))
|
|
55794
|
+
continue;
|
|
55795
|
+
seenRulePaths.add(realPath);
|
|
55796
|
+
if (isDuplicateByRealPath(realPath, rulesCache.realPaths))
|
|
55797
|
+
continue;
|
|
55798
|
+
const section = await readRuleSection(candidate, single, "copilot-instructions (always apply)", sessionID, truncator, rulesCache);
|
|
55799
|
+
if (section) {
|
|
55800
|
+
sections.push(section);
|
|
55801
|
+
rulesCache.realPaths.add(realPath);
|
|
55802
|
+
rulesDirty = true;
|
|
55803
|
+
}
|
|
55804
|
+
}
|
|
55805
|
+
}
|
|
55806
|
+
}
|
|
55807
|
+
if (sections.length === 0)
|
|
55808
|
+
return;
|
|
55809
|
+
output.output += buildDirectoryContextBlock(sections);
|
|
55810
|
+
if (agentsDirty)
|
|
55811
|
+
saveInjectedPaths(sessionID, agentsCache);
|
|
55812
|
+
if (rulesDirty)
|
|
55813
|
+
saveInjectedRules(sessionID, rulesCache);
|
|
55814
|
+
}
|
|
55815
|
+
return { processFilePathForInjection, clearSessionCache };
|
|
55816
|
+
}
|
|
55817
|
+
|
|
55818
|
+
// src/hooks/post-read-injector/hook.ts
|
|
55819
|
+
var DEFAULT_TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
|
|
55820
|
+
function createPostReadInjectorHook(ctx, options) {
|
|
55821
|
+
const truncator = createDynamicTruncator(ctx);
|
|
55822
|
+
const { processFilePathForInjection, clearSessionCache } = createUnifiedPostReadProcessor({
|
|
55823
|
+
workspaceDirectory: ctx.directory,
|
|
55824
|
+
truncator
|
|
55825
|
+
});
|
|
55826
|
+
const tracked = (options?.trackedTools ?? DEFAULT_TRACKED_TOOLS).map((tool) => tool.toLowerCase());
|
|
55827
|
+
const toolExecuteAfter = async (input, output) => {
|
|
55828
|
+
if (!tracked.includes(input.tool.toLowerCase()))
|
|
55829
|
+
return;
|
|
55830
|
+
const filePath = getRuleInjectionFilePath({ title: output.title, metadata: output.metadata });
|
|
55831
|
+
if (!filePath)
|
|
55832
|
+
return;
|
|
55833
|
+
await processFilePathForInjection(filePath, input.sessionID, output);
|
|
55834
|
+
};
|
|
55835
|
+
const toolExecuteBefore = async (input, output) => {};
|
|
55836
|
+
const eventHandler = async ({ event }) => {
|
|
55837
|
+
const props = event.properties;
|
|
55838
|
+
if (event.type === "session.deleted") {
|
|
55839
|
+
const info = props?.info;
|
|
55840
|
+
if (info?.id) {
|
|
55841
|
+
clearSessionCache(info.id);
|
|
55842
|
+
clearInjectedPaths(info.id);
|
|
55843
|
+
clearInjectedRules(info.id);
|
|
55844
|
+
}
|
|
55845
|
+
}
|
|
55846
|
+
if (event.type === "session.compacted") {
|
|
55847
|
+
const sessionID = props?.sessionID ?? props?.info?.id;
|
|
55848
|
+
if (sessionID) {
|
|
55849
|
+
clearSessionCache(sessionID);
|
|
55850
|
+
clearInjectedPaths(sessionID);
|
|
55851
|
+
clearInjectedRules(sessionID);
|
|
55852
|
+
}
|
|
55853
|
+
}
|
|
55854
|
+
};
|
|
55855
|
+
return {
|
|
55856
|
+
"tool.execute.before": toolExecuteBefore,
|
|
55857
|
+
"tool.execute.after": toolExecuteAfter,
|
|
55858
|
+
event: eventHandler
|
|
55859
|
+
};
|
|
55408
55860
|
}
|
|
55409
55861
|
|
|
55410
55862
|
// src/hooks/directory-agents-injector/hook.ts
|
|
55411
55863
|
function createDirectoryAgentsInjectorHook(ctx) {
|
|
55412
|
-
return
|
|
55864
|
+
return createPostReadInjectorHook(ctx, { trackedTools: ["read"] });
|
|
55413
55865
|
}
|
|
55414
55866
|
// src/hooks/edit-error-recovery/hook.ts
|
|
55415
55867
|
var EDIT_ERROR_PATTERNS = [
|
|
@@ -55531,7 +55983,7 @@ ${envContext}`;
|
|
|
55531
55983
|
};
|
|
55532
55984
|
}
|
|
55533
55985
|
// src/hooks/env-file-write-guard/sensitive-file-matcher.ts
|
|
55534
|
-
var
|
|
55986
|
+
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
55535
55987
|
import { basename as basename3 } from "path";
|
|
55536
55988
|
var DEFAULT_BLOCKED_PATTERNS = [
|
|
55537
55989
|
".env",
|
|
@@ -55551,7 +56003,7 @@ var DEFAULT_BLOCKED_PATTERNS = [
|
|
|
55551
56003
|
];
|
|
55552
56004
|
function createSensitiveFileMatcher(blockedPatterns, allowedPaths) {
|
|
55553
56005
|
const patterns = blockedPatterns ?? DEFAULT_BLOCKED_PATTERNS;
|
|
55554
|
-
const isBlocked =
|
|
56006
|
+
const isBlocked = import_picomatch2.default(patterns, { dot: true, bash: true });
|
|
55555
56007
|
const allowSet = new Set(allowedPaths ?? []);
|
|
55556
56008
|
return function isSensitiveFile(filePath) {
|
|
55557
56009
|
if (allowSet.has(filePath))
|
|
@@ -58864,8 +59316,8 @@ ${originalText}`;
|
|
|
58864
59316
|
init_logger();
|
|
58865
59317
|
|
|
58866
59318
|
// src/hooks/interactive-bash-session/constants.ts
|
|
58867
|
-
import { join as
|
|
58868
|
-
var INTERACTIVE_BASH_SESSION_STORAGE =
|
|
59319
|
+
import { join as join46 } from "path";
|
|
59320
|
+
var INTERACTIVE_BASH_SESSION_STORAGE = join46(OPENCODE_STORAGE, "interactive-bash-session");
|
|
58869
59321
|
var MATRIXX_SESSION_PREFIX = "matrixx-";
|
|
58870
59322
|
function buildSessionReminderMessage(sessions) {
|
|
58871
59323
|
if (sessions.length === 0)
|
|
@@ -58960,22 +59412,22 @@ function findSubcommand(tokens) {
|
|
|
58960
59412
|
|
|
58961
59413
|
// src/hooks/interactive-bash-session/storage.ts
|
|
58962
59414
|
import {
|
|
58963
|
-
existsSync as
|
|
58964
|
-
mkdirSync as
|
|
58965
|
-
readFileSync as
|
|
58966
|
-
unlinkSync as
|
|
58967
|
-
writeFileSync as
|
|
59415
|
+
existsSync as existsSync37,
|
|
59416
|
+
mkdirSync as mkdirSync13,
|
|
59417
|
+
readFileSync as readFileSync30,
|
|
59418
|
+
unlinkSync as unlinkSync7,
|
|
59419
|
+
writeFileSync as writeFileSync15
|
|
58968
59420
|
} from "fs";
|
|
58969
|
-
import { join as
|
|
58970
|
-
function
|
|
58971
|
-
return
|
|
59421
|
+
import { join as join47 } from "path";
|
|
59422
|
+
function getStoragePath3(sessionID) {
|
|
59423
|
+
return join47(INTERACTIVE_BASH_SESSION_STORAGE, `${sessionID}.json`);
|
|
58972
59424
|
}
|
|
58973
59425
|
function loadInteractiveBashSessionState(sessionID) {
|
|
58974
|
-
const filePath =
|
|
58975
|
-
if (!
|
|
59426
|
+
const filePath = getStoragePath3(sessionID);
|
|
59427
|
+
if (!existsSync37(filePath))
|
|
58976
59428
|
return null;
|
|
58977
59429
|
try {
|
|
58978
|
-
const content =
|
|
59430
|
+
const content = readFileSync30(filePath, "utf-8");
|
|
58979
59431
|
const serialized = JSON.parse(content);
|
|
58980
59432
|
return {
|
|
58981
59433
|
sessionID: serialized.sessionID,
|
|
@@ -58987,21 +59439,21 @@ function loadInteractiveBashSessionState(sessionID) {
|
|
|
58987
59439
|
}
|
|
58988
59440
|
}
|
|
58989
59441
|
function saveInteractiveBashSessionState(state3) {
|
|
58990
|
-
if (!
|
|
58991
|
-
|
|
59442
|
+
if (!existsSync37(INTERACTIVE_BASH_SESSION_STORAGE)) {
|
|
59443
|
+
mkdirSync13(INTERACTIVE_BASH_SESSION_STORAGE, { recursive: true });
|
|
58992
59444
|
}
|
|
58993
|
-
const filePath =
|
|
59445
|
+
const filePath = getStoragePath3(state3.sessionID);
|
|
58994
59446
|
const serialized = {
|
|
58995
59447
|
sessionID: state3.sessionID,
|
|
58996
59448
|
tmuxSessions: Array.from(state3.tmuxSessions),
|
|
58997
59449
|
updatedAt: state3.updatedAt
|
|
58998
59450
|
};
|
|
58999
|
-
|
|
59451
|
+
writeFileSync15(filePath, JSON.stringify(serialized, null, 2));
|
|
59000
59452
|
}
|
|
59001
59453
|
function clearInteractiveBashSessionState(sessionID) {
|
|
59002
|
-
const filePath =
|
|
59003
|
-
if (
|
|
59004
|
-
|
|
59454
|
+
const filePath = getStoragePath3(sessionID);
|
|
59455
|
+
if (existsSync37(filePath)) {
|
|
59456
|
+
unlinkSync7(filePath);
|
|
59005
59457
|
}
|
|
59006
59458
|
}
|
|
59007
59459
|
|
|
@@ -59344,7 +59796,7 @@ init_logger();
|
|
|
59344
59796
|
|
|
59345
59797
|
// src/hooks/matrix-loop/completion-promise-detector.ts
|
|
59346
59798
|
init_logger();
|
|
59347
|
-
import { existsSync as
|
|
59799
|
+
import { existsSync as existsSync39, readFileSync as readFileSync32 } from "fs";
|
|
59348
59800
|
function escapeRegex2(str) {
|
|
59349
59801
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
59350
59802
|
}
|
|
@@ -59355,9 +59807,9 @@ function detectCompletionInTranscript(transcriptPath, promise2) {
|
|
|
59355
59807
|
if (!transcriptPath)
|
|
59356
59808
|
return false;
|
|
59357
59809
|
try {
|
|
59358
|
-
if (!
|
|
59810
|
+
if (!existsSync39(transcriptPath))
|
|
59359
59811
|
return false;
|
|
59360
|
-
const content =
|
|
59812
|
+
const content = readFileSync32(transcriptPath, "utf-8");
|
|
59361
59813
|
const pattern = buildPromisePattern(promise2);
|
|
59362
59814
|
const lines = content.split(`
|
|
59363
59815
|
`).filter((line) => line.trim());
|
|
@@ -60063,10 +60515,10 @@ async function getAgentFromSession(sessionID, directory, client) {
|
|
|
60063
60515
|
}
|
|
60064
60516
|
|
|
60065
60517
|
// src/hooks/oracle-md-only/path-policy.ts
|
|
60066
|
-
import { isAbsolute as isAbsolute2, relative, resolve as resolve4 } from "path";
|
|
60518
|
+
import { isAbsolute as isAbsolute2, relative as relative3, resolve as resolve4 } from "path";
|
|
60067
60519
|
function isAllowedFile(filePath, workspaceRoot) {
|
|
60068
60520
|
const resolved = resolve4(workspaceRoot, filePath);
|
|
60069
|
-
const rel =
|
|
60521
|
+
const rel = relative3(workspaceRoot, resolved);
|
|
60070
60522
|
if (rel.startsWith("..") || isAbsolute2(rel)) {
|
|
60071
60523
|
return false;
|
|
60072
60524
|
}
|
|
@@ -60138,13 +60590,13 @@ function createOracleMdOnlyHook(ctx) {
|
|
|
60138
60590
|
};
|
|
60139
60591
|
}
|
|
60140
60592
|
// src/hooks/plan-persister/hook.ts
|
|
60141
|
-
import { existsSync as
|
|
60593
|
+
import { existsSync as existsSync41, readdirSync as readdirSync13 } from "fs";
|
|
60142
60594
|
|
|
60143
60595
|
// src/features/task-storage/storage.ts
|
|
60144
60596
|
init_logger();
|
|
60145
60597
|
import { randomUUID } from "crypto";
|
|
60146
|
-
import { existsSync as
|
|
60147
|
-
import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute3, join as
|
|
60598
|
+
import { existsSync as existsSync40, mkdirSync as mkdirSync15, readdirSync as readdirSync12, readFileSync as readFileSync33, renameSync as renameSync3, unlinkSync as unlinkSync9, writeFileSync as writeFileSync17 } from "fs";
|
|
60599
|
+
import { basename as basename5, dirname as dirname10, isAbsolute as isAbsolute3, join as join49 } from "path";
|
|
60148
60600
|
|
|
60149
60601
|
// src/shared/task-system-gating.ts
|
|
60150
60602
|
var TASK_SYSTEM_DEFAULT = true;
|
|
@@ -60168,18 +60620,18 @@ function isTaskSystemEnabled(config2) {
|
|
|
60168
60620
|
|
|
60169
60621
|
// src/features/task-storage/storage.ts
|
|
60170
60622
|
function getProjectTaskDir(directory) {
|
|
60171
|
-
return
|
|
60623
|
+
return join49(directory, ".matrixx", "tasks");
|
|
60172
60624
|
}
|
|
60173
60625
|
function getTaskDir(config2 = {}, directory) {
|
|
60174
60626
|
const tasksConfig = resolveTasksConfig(config2);
|
|
60175
60627
|
const storagePath = tasksConfig.storage_path;
|
|
60176
60628
|
if (storagePath) {
|
|
60177
|
-
return isAbsolute3(storagePath) ? storagePath :
|
|
60629
|
+
return isAbsolute3(storagePath) ? storagePath : join49(directory ?? process.cwd(), storagePath);
|
|
60178
60630
|
}
|
|
60179
60631
|
if (tasksConfig.scope === "global" || !directory) {
|
|
60180
60632
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
60181
60633
|
const listId = resolveTaskListId(config2);
|
|
60182
|
-
return
|
|
60634
|
+
return join49(configDir, "tasks", listId);
|
|
60183
60635
|
}
|
|
60184
60636
|
return getProjectTaskDir(directory);
|
|
60185
60637
|
}
|
|
@@ -60196,16 +60648,16 @@ function resolveTaskListId(config2 = {}) {
|
|
|
60196
60648
|
return sanitizePathSegment(basename5(process.cwd()));
|
|
60197
60649
|
}
|
|
60198
60650
|
function ensureDir2(dirPath) {
|
|
60199
|
-
if (!
|
|
60200
|
-
|
|
60651
|
+
if (!existsSync40(dirPath)) {
|
|
60652
|
+
mkdirSync15(dirPath, { recursive: true });
|
|
60201
60653
|
}
|
|
60202
60654
|
}
|
|
60203
60655
|
function readJsonSafe(filePath, schema) {
|
|
60204
60656
|
try {
|
|
60205
|
-
if (!
|
|
60657
|
+
if (!existsSync40(filePath)) {
|
|
60206
60658
|
return null;
|
|
60207
60659
|
}
|
|
60208
|
-
const content =
|
|
60660
|
+
const content = readFileSync33(filePath, "utf-8");
|
|
60209
60661
|
const parsed = JSON.parse(content);
|
|
60210
60662
|
const result = schema.safeParse(parsed);
|
|
60211
60663
|
if (!result.success) {
|
|
@@ -60221,12 +60673,12 @@ function writeJsonAtomic(filePath, data) {
|
|
|
60221
60673
|
ensureDir2(dir);
|
|
60222
60674
|
const tempPath = `${filePath}.tmp.${Date.now()}`;
|
|
60223
60675
|
try {
|
|
60224
|
-
|
|
60676
|
+
writeFileSync17(tempPath, JSON.stringify(data, null, 2), "utf-8");
|
|
60225
60677
|
renameSync3(tempPath, filePath);
|
|
60226
60678
|
} catch (error51) {
|
|
60227
60679
|
try {
|
|
60228
|
-
if (
|
|
60229
|
-
|
|
60680
|
+
if (existsSync40(tempPath)) {
|
|
60681
|
+
unlinkSync9(tempPath);
|
|
60230
60682
|
}
|
|
60231
60683
|
} catch {}
|
|
60232
60684
|
throw error51;
|
|
@@ -60249,21 +60701,21 @@ function generateTaskId() {
|
|
|
60249
60701
|
}
|
|
60250
60702
|
function migrateLegacyTasksIfNeeded(config2, directory) {
|
|
60251
60703
|
const projectDir = getProjectTaskDir(directory);
|
|
60252
|
-
const legacyDir =
|
|
60253
|
-
if (
|
|
60704
|
+
const legacyDir = join49(getOpenCodeConfigDir({ binary: "opencode" }), "tasks", resolveTaskListId(config2));
|
|
60705
|
+
if (existsSync40(projectDir)) {
|
|
60254
60706
|
try {
|
|
60255
|
-
const existing =
|
|
60707
|
+
const existing = readdirSync12(projectDir).filter((f) => f.startsWith("T-") && f.endsWith(".json"));
|
|
60256
60708
|
if (existing.length > 0) {
|
|
60257
60709
|
return { migrated: 0, legacyDir, projectDir };
|
|
60258
60710
|
}
|
|
60259
60711
|
} catch {}
|
|
60260
60712
|
}
|
|
60261
|
-
if (!
|
|
60713
|
+
if (!existsSync40(legacyDir)) {
|
|
60262
60714
|
return { migrated: 0, legacyDir, projectDir };
|
|
60263
60715
|
}
|
|
60264
60716
|
let files = [];
|
|
60265
60717
|
try {
|
|
60266
|
-
files =
|
|
60718
|
+
files = readdirSync12(legacyDir).filter((f) => f.startsWith("T-") && f.endsWith(".json"));
|
|
60267
60719
|
} catch {
|
|
60268
60720
|
return { migrated: 0, legacyDir, projectDir };
|
|
60269
60721
|
}
|
|
@@ -60273,13 +60725,13 @@ function migrateLegacyTasksIfNeeded(config2, directory) {
|
|
|
60273
60725
|
ensureDir2(projectDir);
|
|
60274
60726
|
let migrated = 0;
|
|
60275
60727
|
for (const file2 of files) {
|
|
60276
|
-
const src =
|
|
60277
|
-
const dest =
|
|
60278
|
-
if (
|
|
60728
|
+
const src = join49(legacyDir, file2);
|
|
60729
|
+
const dest = join49(projectDir, file2);
|
|
60730
|
+
if (existsSync40(dest))
|
|
60279
60731
|
continue;
|
|
60280
60732
|
try {
|
|
60281
|
-
const content =
|
|
60282
|
-
|
|
60733
|
+
const content = readFileSync33(src, "utf-8");
|
|
60734
|
+
writeFileSync17(dest, content, "utf-8");
|
|
60283
60735
|
migrated++;
|
|
60284
60736
|
} catch {}
|
|
60285
60737
|
}
|
|
@@ -60289,17 +60741,17 @@ function migrateLegacyTasksIfNeeded(config2, directory) {
|
|
|
60289
60741
|
return { migrated, legacyDir, projectDir };
|
|
60290
60742
|
}
|
|
60291
60743
|
function acquireLock(dirPath) {
|
|
60292
|
-
const lockPath =
|
|
60744
|
+
const lockPath = join49(dirPath, ".lock");
|
|
60293
60745
|
const lockId = randomUUID();
|
|
60294
60746
|
const createLock = (timestamp) => {
|
|
60295
|
-
|
|
60747
|
+
writeFileSync17(lockPath, JSON.stringify({ id: lockId, timestamp }), {
|
|
60296
60748
|
encoding: "utf-8",
|
|
60297
60749
|
flag: "wx"
|
|
60298
60750
|
});
|
|
60299
60751
|
};
|
|
60300
60752
|
const isStale = () => {
|
|
60301
60753
|
try {
|
|
60302
|
-
const lockContent =
|
|
60754
|
+
const lockContent = readFileSync33(lockPath, "utf-8");
|
|
60303
60755
|
const lockData = JSON.parse(lockContent);
|
|
60304
60756
|
const lockAge = Date.now() - lockData.timestamp;
|
|
60305
60757
|
return lockAge > STALE_LOCK_THRESHOLD_MS;
|
|
@@ -60323,7 +60775,7 @@ function acquireLock(dirPath) {
|
|
|
60323
60775
|
let acquired = tryAcquire();
|
|
60324
60776
|
if (!acquired && isStale()) {
|
|
60325
60777
|
try {
|
|
60326
|
-
|
|
60778
|
+
unlinkSync9(lockPath);
|
|
60327
60779
|
} catch {}
|
|
60328
60780
|
acquired = tryAcquire();
|
|
60329
60781
|
}
|
|
@@ -60337,13 +60789,13 @@ function acquireLock(dirPath) {
|
|
|
60337
60789
|
acquired: true,
|
|
60338
60790
|
release: () => {
|
|
60339
60791
|
try {
|
|
60340
|
-
if (!
|
|
60792
|
+
if (!existsSync40(lockPath))
|
|
60341
60793
|
return;
|
|
60342
|
-
const lockContent =
|
|
60794
|
+
const lockContent = readFileSync33(lockPath, "utf-8");
|
|
60343
60795
|
const lockData = JSON.parse(lockContent);
|
|
60344
60796
|
if (lockData.id !== lockId)
|
|
60345
60797
|
return;
|
|
60346
|
-
|
|
60798
|
+
unlinkSync9(lockPath);
|
|
60347
60799
|
} catch {}
|
|
60348
60800
|
}
|
|
60349
60801
|
};
|
|
@@ -60479,13 +60931,13 @@ function createPlanPersister(ctx, options) {
|
|
|
60479
60931
|
if (!mission.session_ids?.includes(sessionID))
|
|
60480
60932
|
return;
|
|
60481
60933
|
const planPath = mission.active_plan;
|
|
60482
|
-
if (!
|
|
60934
|
+
if (!existsSync41(planPath))
|
|
60483
60935
|
return;
|
|
60484
60936
|
let todos = [];
|
|
60485
60937
|
try {
|
|
60486
60938
|
const taskDir = getTaskDir({}, directory);
|
|
60487
|
-
if (
|
|
60488
|
-
const files =
|
|
60939
|
+
if (existsSync41(taskDir)) {
|
|
60940
|
+
const files = readdirSync13(taskDir).filter((f) => f.startsWith("T-") && f.endsWith(".json"));
|
|
60489
60941
|
const tasks = [];
|
|
60490
60942
|
for (const f of files) {
|
|
60491
60943
|
const parsed = readJsonSafe(`${taskDir}/${f}`, TaskObjectSchema);
|
|
@@ -61811,568 +62263,9 @@ function createRtkBashRewriterHook(_ctx, config2) {
|
|
|
61811
62263
|
}
|
|
61812
62264
|
};
|
|
61813
62265
|
}
|
|
61814
|
-
// src/hooks/rules-injector/project-root-finder.ts
|
|
61815
|
-
import { existsSync as existsSync39, statSync as statSync5 } from "fs";
|
|
61816
|
-
import { dirname as dirname11, join as join47 } from "path";
|
|
61817
|
-
|
|
61818
|
-
// src/hooks/rules-injector/constants.ts
|
|
61819
|
-
import { join as join46 } from "path";
|
|
61820
|
-
var RULES_INJECTOR_STORAGE = join46(OPENCODE_STORAGE, "rules-injector");
|
|
61821
|
-
var PROJECT_MARKERS = [
|
|
61822
|
-
".git",
|
|
61823
|
-
"pyproject.toml",
|
|
61824
|
-
"package.json",
|
|
61825
|
-
"Cargo.toml",
|
|
61826
|
-
"go.mod",
|
|
61827
|
-
".venv"
|
|
61828
|
-
];
|
|
61829
|
-
var PROJECT_RULE_SUBDIRS = [
|
|
61830
|
-
[".github", "instructions"],
|
|
61831
|
-
[".cursor", "rules"],
|
|
61832
|
-
[".matrixx", "rules"]
|
|
61833
|
-
];
|
|
61834
|
-
var PROJECT_RULE_FILES = [
|
|
61835
|
-
".github/copilot-instructions.md"
|
|
61836
|
-
];
|
|
61837
|
-
var GITHUB_INSTRUCTIONS_PATTERN = /\.instructions\.md$/;
|
|
61838
|
-
var RULE_EXTENSIONS = [".md", ".mdc"];
|
|
61839
|
-
|
|
61840
|
-
// src/hooks/rules-injector/project-root-finder.ts
|
|
61841
|
-
function findProjectRoot(startPath) {
|
|
61842
|
-
let current;
|
|
61843
|
-
try {
|
|
61844
|
-
const stat = statSync5(startPath);
|
|
61845
|
-
current = stat.isDirectory() ? startPath : dirname11(startPath);
|
|
61846
|
-
} catch {
|
|
61847
|
-
current = dirname11(startPath);
|
|
61848
|
-
}
|
|
61849
|
-
while (true) {
|
|
61850
|
-
for (const marker of PROJECT_MARKERS) {
|
|
61851
|
-
const markerPath = join47(current, marker);
|
|
61852
|
-
if (existsSync39(markerPath)) {
|
|
61853
|
-
return current;
|
|
61854
|
-
}
|
|
61855
|
-
}
|
|
61856
|
-
const parent = dirname11(current);
|
|
61857
|
-
if (parent === current) {
|
|
61858
|
-
return null;
|
|
61859
|
-
}
|
|
61860
|
-
current = parent;
|
|
61861
|
-
}
|
|
61862
|
-
}
|
|
61863
|
-
// src/hooks/rules-injector/rule-file-finder.ts
|
|
61864
|
-
import { existsSync as existsSync41, statSync as statSync6 } from "fs";
|
|
61865
|
-
import { dirname as dirname12, join as join49 } from "path";
|
|
61866
|
-
|
|
61867
|
-
// src/hooks/rules-injector/rule-file-scanner.ts
|
|
61868
|
-
import { existsSync as existsSync40, readdirSync as readdirSync13, realpathSync as realpathSync2 } from "fs";
|
|
61869
|
-
import { join as join48 } from "path";
|
|
61870
|
-
function isGitHubInstructionsDir(dir) {
|
|
61871
|
-
return dir.includes(".github/instructions") || dir.endsWith(".github/instructions");
|
|
61872
|
-
}
|
|
61873
|
-
function isValidRuleFile(fileName, dir) {
|
|
61874
|
-
if (isGitHubInstructionsDir(dir)) {
|
|
61875
|
-
return GITHUB_INSTRUCTIONS_PATTERN.test(fileName);
|
|
61876
|
-
}
|
|
61877
|
-
return RULE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
|
|
61878
|
-
}
|
|
61879
|
-
function findRuleFilesRecursive(dir, results) {
|
|
61880
|
-
if (!existsSync40(dir))
|
|
61881
|
-
return;
|
|
61882
|
-
try {
|
|
61883
|
-
const entries = readdirSync13(dir, { withFileTypes: true });
|
|
61884
|
-
for (const entry of entries) {
|
|
61885
|
-
const fullPath = join48(dir, entry.name);
|
|
61886
|
-
if (entry.isDirectory()) {
|
|
61887
|
-
findRuleFilesRecursive(fullPath, results);
|
|
61888
|
-
} else if (entry.isFile()) {
|
|
61889
|
-
if (isValidRuleFile(entry.name, dir)) {
|
|
61890
|
-
results.push(fullPath);
|
|
61891
|
-
}
|
|
61892
|
-
}
|
|
61893
|
-
}
|
|
61894
|
-
} catch {}
|
|
61895
|
-
}
|
|
61896
|
-
function safeRealpathSync(filePath) {
|
|
61897
|
-
try {
|
|
61898
|
-
return realpathSync2(filePath);
|
|
61899
|
-
} catch {
|
|
61900
|
-
return filePath;
|
|
61901
|
-
}
|
|
61902
|
-
}
|
|
61903
|
-
|
|
61904
|
-
// src/hooks/rules-injector/rule-file-finder.ts
|
|
61905
|
-
function findRuleFiles(projectRoot, currentFile) {
|
|
61906
|
-
const candidates = [];
|
|
61907
|
-
const seenRealPaths = new Set;
|
|
61908
|
-
let currentDir = dirname12(currentFile);
|
|
61909
|
-
let distance = 0;
|
|
61910
|
-
while (true) {
|
|
61911
|
-
for (const [parent, subdir] of PROJECT_RULE_SUBDIRS) {
|
|
61912
|
-
const ruleDir = join49(currentDir, parent, subdir);
|
|
61913
|
-
const files = [];
|
|
61914
|
-
findRuleFilesRecursive(ruleDir, files);
|
|
61915
|
-
for (const filePath of files) {
|
|
61916
|
-
const realPath = safeRealpathSync(filePath);
|
|
61917
|
-
if (seenRealPaths.has(realPath))
|
|
61918
|
-
continue;
|
|
61919
|
-
seenRealPaths.add(realPath);
|
|
61920
|
-
candidates.push({
|
|
61921
|
-
path: filePath,
|
|
61922
|
-
realPath,
|
|
61923
|
-
isGlobal: false,
|
|
61924
|
-
distance
|
|
61925
|
-
});
|
|
61926
|
-
}
|
|
61927
|
-
}
|
|
61928
|
-
if (projectRoot && currentDir === projectRoot)
|
|
61929
|
-
break;
|
|
61930
|
-
const parentDir = dirname12(currentDir);
|
|
61931
|
-
if (parentDir === currentDir)
|
|
61932
|
-
break;
|
|
61933
|
-
currentDir = parentDir;
|
|
61934
|
-
distance++;
|
|
61935
|
-
}
|
|
61936
|
-
if (projectRoot) {
|
|
61937
|
-
for (const ruleFile of PROJECT_RULE_FILES) {
|
|
61938
|
-
const filePath = join49(projectRoot, ruleFile);
|
|
61939
|
-
if (existsSync41(filePath)) {
|
|
61940
|
-
try {
|
|
61941
|
-
const stat = statSync6(filePath);
|
|
61942
|
-
if (stat.isFile()) {
|
|
61943
|
-
const realPath = safeRealpathSync(filePath);
|
|
61944
|
-
if (!seenRealPaths.has(realPath)) {
|
|
61945
|
-
seenRealPaths.add(realPath);
|
|
61946
|
-
candidates.push({
|
|
61947
|
-
path: filePath,
|
|
61948
|
-
realPath,
|
|
61949
|
-
isGlobal: false,
|
|
61950
|
-
distance: 0,
|
|
61951
|
-
isSingleFile: true
|
|
61952
|
-
});
|
|
61953
|
-
}
|
|
61954
|
-
}
|
|
61955
|
-
} catch {}
|
|
61956
|
-
}
|
|
61957
|
-
}
|
|
61958
|
-
}
|
|
61959
|
-
candidates.sort((a, b) => {
|
|
61960
|
-
if (a.isGlobal !== b.isGlobal) {
|
|
61961
|
-
return a.isGlobal ? 1 : -1;
|
|
61962
|
-
}
|
|
61963
|
-
return a.distance - b.distance;
|
|
61964
|
-
});
|
|
61965
|
-
return candidates;
|
|
61966
|
-
}
|
|
61967
|
-
// src/hooks/rules-injector/storage.ts
|
|
61968
|
-
import {
|
|
61969
|
-
existsSync as existsSync42,
|
|
61970
|
-
mkdirSync as mkdirSync15,
|
|
61971
|
-
readFileSync as readFileSync31,
|
|
61972
|
-
unlinkSync as unlinkSync9,
|
|
61973
|
-
writeFileSync as writeFileSync17
|
|
61974
|
-
} from "fs";
|
|
61975
|
-
import { join as join50 } from "path";
|
|
61976
|
-
function getStoragePath3(sessionID) {
|
|
61977
|
-
return join50(RULES_INJECTOR_STORAGE, `${sessionID}.json`);
|
|
61978
|
-
}
|
|
61979
|
-
function loadInjectedRules(sessionID) {
|
|
61980
|
-
const filePath = getStoragePath3(sessionID);
|
|
61981
|
-
if (!existsSync42(filePath))
|
|
61982
|
-
return { contentHashes: new Set, realPaths: new Set };
|
|
61983
|
-
try {
|
|
61984
|
-
const content = readFileSync31(filePath, "utf-8");
|
|
61985
|
-
const data = JSON.parse(content);
|
|
61986
|
-
return {
|
|
61987
|
-
contentHashes: new Set(data.injectedHashes),
|
|
61988
|
-
realPaths: new Set(data.injectedRealPaths ?? [])
|
|
61989
|
-
};
|
|
61990
|
-
} catch {
|
|
61991
|
-
return { contentHashes: new Set, realPaths: new Set };
|
|
61992
|
-
}
|
|
61993
|
-
}
|
|
61994
|
-
function saveInjectedRules(sessionID, data) {
|
|
61995
|
-
if (!existsSync42(RULES_INJECTOR_STORAGE)) {
|
|
61996
|
-
mkdirSync15(RULES_INJECTOR_STORAGE, { recursive: true });
|
|
61997
|
-
}
|
|
61998
|
-
const storageData = {
|
|
61999
|
-
sessionID,
|
|
62000
|
-
injectedHashes: [...data.contentHashes],
|
|
62001
|
-
injectedRealPaths: [...data.realPaths],
|
|
62002
|
-
updatedAt: Date.now()
|
|
62003
|
-
};
|
|
62004
|
-
writeFileSync17(getStoragePath3(sessionID), JSON.stringify(storageData, null, 2));
|
|
62005
|
-
}
|
|
62006
|
-
function clearInjectedRules(sessionID) {
|
|
62007
|
-
const filePath = getStoragePath3(sessionID);
|
|
62008
|
-
if (existsSync42(filePath)) {
|
|
62009
|
-
unlinkSync9(filePath);
|
|
62010
|
-
}
|
|
62011
|
-
}
|
|
62012
|
-
|
|
62013
|
-
// src/hooks/rules-injector/cache.ts
|
|
62014
|
-
function createSessionCacheStore() {
|
|
62015
|
-
const sessionCaches = new Map;
|
|
62016
|
-
function getSessionCache2(sessionID) {
|
|
62017
|
-
if (!sessionCaches.has(sessionID)) {
|
|
62018
|
-
sessionCaches.set(sessionID, loadInjectedRules(sessionID));
|
|
62019
|
-
}
|
|
62020
|
-
return sessionCaches.get(sessionID);
|
|
62021
|
-
}
|
|
62022
|
-
function clearSessionCache(sessionID) {
|
|
62023
|
-
sessionCaches.delete(sessionID);
|
|
62024
|
-
clearInjectedRules(sessionID);
|
|
62025
|
-
}
|
|
62026
|
-
return { getSessionCache: getSessionCache2, clearSessionCache };
|
|
62027
|
-
}
|
|
62028
|
-
|
|
62029
|
-
// src/hooks/rules-injector/injector.ts
|
|
62030
|
-
import { readFileSync as readFileSync32, statSync as statSync7, watch } from "fs";
|
|
62031
|
-
import { relative as relative3, resolve as resolve5 } from "path";
|
|
62032
|
-
|
|
62033
|
-
// src/hooks/rules-injector/matcher.ts
|
|
62034
|
-
var import_picomatch2 = __toESM(require_picomatch2(), 1);
|
|
62035
|
-
import { createHash } from "crypto";
|
|
62036
|
-
import { relative as relative2 } from "path";
|
|
62037
|
-
function shouldApplyRule(metadata, currentFilePath, projectRoot) {
|
|
62038
|
-
if (metadata.alwaysApply === true) {
|
|
62039
|
-
return { applies: true, reason: "alwaysApply" };
|
|
62040
|
-
}
|
|
62041
|
-
const globs = metadata.globs;
|
|
62042
|
-
if (!globs) {
|
|
62043
|
-
return { applies: false };
|
|
62044
|
-
}
|
|
62045
|
-
const patterns2 = Array.isArray(globs) ? globs : [globs];
|
|
62046
|
-
if (patterns2.length === 0) {
|
|
62047
|
-
return { applies: false };
|
|
62048
|
-
}
|
|
62049
|
-
const relativePath = projectRoot ? relative2(projectRoot, currentFilePath) : currentFilePath;
|
|
62050
|
-
for (const pattern of patterns2) {
|
|
62051
|
-
if (import_picomatch2.default.isMatch(relativePath, pattern, { dot: true, bash: true })) {
|
|
62052
|
-
return { applies: true, reason: `glob: ${pattern}` };
|
|
62053
|
-
}
|
|
62054
|
-
}
|
|
62055
|
-
return { applies: false };
|
|
62056
|
-
}
|
|
62057
|
-
function isDuplicateByRealPath(realPath, cache2) {
|
|
62058
|
-
return cache2.has(realPath);
|
|
62059
|
-
}
|
|
62060
|
-
function createContentHash(content) {
|
|
62061
|
-
return createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
62062
|
-
}
|
|
62063
|
-
function isDuplicateByContentHash(hash2, cache2) {
|
|
62064
|
-
return cache2.has(hash2);
|
|
62065
|
-
}
|
|
62066
|
-
|
|
62067
|
-
// src/hooks/rules-injector/parser.ts
|
|
62068
|
-
function parseRuleFrontmatter(content) {
|
|
62069
|
-
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
62070
|
-
const match = content.match(frontmatterRegex);
|
|
62071
|
-
if (!match) {
|
|
62072
|
-
return { metadata: {}, body: content };
|
|
62073
|
-
}
|
|
62074
|
-
const yamlContent = match[1];
|
|
62075
|
-
const body = match[2];
|
|
62076
|
-
try {
|
|
62077
|
-
const metadata = parseYamlContent(yamlContent);
|
|
62078
|
-
return { metadata, body };
|
|
62079
|
-
} catch {
|
|
62080
|
-
return { metadata: {}, body: content };
|
|
62081
|
-
}
|
|
62082
|
-
}
|
|
62083
|
-
function parseYamlContent(yamlContent) {
|
|
62084
|
-
const lines = yamlContent.split(`
|
|
62085
|
-
`);
|
|
62086
|
-
const metadata = {};
|
|
62087
|
-
let i = 0;
|
|
62088
|
-
while (i < lines.length) {
|
|
62089
|
-
const line = lines[i];
|
|
62090
|
-
const colonIndex = line.indexOf(":");
|
|
62091
|
-
if (colonIndex === -1) {
|
|
62092
|
-
i++;
|
|
62093
|
-
continue;
|
|
62094
|
-
}
|
|
62095
|
-
const key = line.slice(0, colonIndex).trim();
|
|
62096
|
-
const rawValue = line.slice(colonIndex + 1).trim();
|
|
62097
|
-
if (key === "description") {
|
|
62098
|
-
metadata.description = parseStringValue(rawValue);
|
|
62099
|
-
} else if (key === "alwaysApply") {
|
|
62100
|
-
metadata.alwaysApply = rawValue === "true";
|
|
62101
|
-
} else if (key === "globs" || key === "applyTo") {
|
|
62102
|
-
const { value, consumed } = parseArrayOrStringValue(rawValue, lines, i);
|
|
62103
|
-
metadata.globs = mergeGlobs(metadata.globs, value);
|
|
62104
|
-
i += consumed;
|
|
62105
|
-
continue;
|
|
62106
|
-
}
|
|
62107
|
-
i++;
|
|
62108
|
-
}
|
|
62109
|
-
return metadata;
|
|
62110
|
-
}
|
|
62111
|
-
function parseStringValue(value) {
|
|
62112
|
-
if (!value)
|
|
62113
|
-
return "";
|
|
62114
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
62115
|
-
return value.slice(1, -1);
|
|
62116
|
-
}
|
|
62117
|
-
return value;
|
|
62118
|
-
}
|
|
62119
|
-
function parseArrayOrStringValue(rawValue, lines, currentIndex) {
|
|
62120
|
-
if (rawValue.startsWith("[")) {
|
|
62121
|
-
return { value: parseInlineArray(rawValue), consumed: 1 };
|
|
62122
|
-
}
|
|
62123
|
-
if (!rawValue || rawValue === "") {
|
|
62124
|
-
const arrayItems = [];
|
|
62125
|
-
let consumed = 1;
|
|
62126
|
-
for (let j = currentIndex + 1;j < lines.length; j++) {
|
|
62127
|
-
const nextLine = lines[j];
|
|
62128
|
-
const arrayMatch = nextLine.match(/^\s+-\s*(.*)$/);
|
|
62129
|
-
if (arrayMatch) {
|
|
62130
|
-
const itemValue = parseStringValue(arrayMatch[1].trim());
|
|
62131
|
-
if (itemValue) {
|
|
62132
|
-
arrayItems.push(itemValue);
|
|
62133
|
-
}
|
|
62134
|
-
consumed++;
|
|
62135
|
-
} else if (nextLine.trim() === "") {
|
|
62136
|
-
consumed++;
|
|
62137
|
-
} else {
|
|
62138
|
-
break;
|
|
62139
|
-
}
|
|
62140
|
-
}
|
|
62141
|
-
if (arrayItems.length > 0) {
|
|
62142
|
-
return { value: arrayItems, consumed };
|
|
62143
|
-
}
|
|
62144
|
-
}
|
|
62145
|
-
const stringValue = parseStringValue(rawValue);
|
|
62146
|
-
if (stringValue.includes(",")) {
|
|
62147
|
-
const items = stringValue.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
62148
|
-
return { value: items, consumed: 1 };
|
|
62149
|
-
}
|
|
62150
|
-
return { value: stringValue, consumed: 1 };
|
|
62151
|
-
}
|
|
62152
|
-
function parseInlineArray(value) {
|
|
62153
|
-
const content = value.slice(1, value.lastIndexOf("]")).trim();
|
|
62154
|
-
if (!content)
|
|
62155
|
-
return [];
|
|
62156
|
-
const items = [];
|
|
62157
|
-
let current = "";
|
|
62158
|
-
let inQuote = false;
|
|
62159
|
-
let quoteChar = "";
|
|
62160
|
-
for (let i = 0;i < content.length; i++) {
|
|
62161
|
-
const char = content[i];
|
|
62162
|
-
if (!inQuote && (char === '"' || char === "'")) {
|
|
62163
|
-
inQuote = true;
|
|
62164
|
-
quoteChar = char;
|
|
62165
|
-
} else if (inQuote && char === quoteChar) {
|
|
62166
|
-
inQuote = false;
|
|
62167
|
-
quoteChar = "";
|
|
62168
|
-
} else if (!inQuote && char === ",") {
|
|
62169
|
-
const trimmed2 = current.trim();
|
|
62170
|
-
if (trimmed2) {
|
|
62171
|
-
items.push(parseStringValue(trimmed2));
|
|
62172
|
-
}
|
|
62173
|
-
current = "";
|
|
62174
|
-
} else {
|
|
62175
|
-
current += char;
|
|
62176
|
-
}
|
|
62177
|
-
}
|
|
62178
|
-
const trimmed = current.trim();
|
|
62179
|
-
if (trimmed) {
|
|
62180
|
-
items.push(parseStringValue(trimmed));
|
|
62181
|
-
}
|
|
62182
|
-
return items;
|
|
62183
|
-
}
|
|
62184
|
-
function mergeGlobs(existing, newValue) {
|
|
62185
|
-
if (!existing)
|
|
62186
|
-
return newValue;
|
|
62187
|
-
const existingArray = Array.isArray(existing) ? existing : [existing];
|
|
62188
|
-
const newArray = Array.isArray(newValue) ? newValue : [newValue];
|
|
62189
|
-
return [...existingArray, ...newArray];
|
|
62190
|
-
}
|
|
62191
|
-
|
|
62192
|
-
// src/hooks/rules-injector/injector.ts
|
|
62193
|
-
var parsedRuleCache = new Map;
|
|
62194
|
-
var RULE_WATCHER_DEBOUNCE_MS = 1000;
|
|
62195
|
-
var ruleWatchers = new Map;
|
|
62196
|
-
var ruleWatcherDebounceTimers = new Map;
|
|
62197
|
-
function makeRuleCacheKey(realPath, cwd, mtimeMs) {
|
|
62198
|
-
return `${realPath}\x00${cwd}\x00${mtimeMs}`;
|
|
62199
|
-
}
|
|
62200
|
-
function invalidateRuleCacheForPath(realPath) {
|
|
62201
|
-
const prefix = `${realPath}\x00`;
|
|
62202
|
-
for (const key of parsedRuleCache.keys()) {
|
|
62203
|
-
if (key.startsWith(prefix)) {
|
|
62204
|
-
parsedRuleCache.delete(key);
|
|
62205
|
-
}
|
|
62206
|
-
}
|
|
62207
|
-
}
|
|
62208
|
-
function setupRuleWatcher(realPath) {
|
|
62209
|
-
if (ruleWatchers.has(realPath))
|
|
62210
|
-
return;
|
|
62211
|
-
try {
|
|
62212
|
-
const watcher = watch(realPath, () => {
|
|
62213
|
-
const existing = ruleWatcherDebounceTimers.get(realPath);
|
|
62214
|
-
if (existing)
|
|
62215
|
-
clearTimeout(existing);
|
|
62216
|
-
const timer = setTimeout(() => {
|
|
62217
|
-
ruleWatcherDebounceTimers.delete(realPath);
|
|
62218
|
-
invalidateRuleCacheForPath(realPath);
|
|
62219
|
-
}, RULE_WATCHER_DEBOUNCE_MS);
|
|
62220
|
-
if (typeof timer.unref === "function") {
|
|
62221
|
-
timer.unref?.();
|
|
62222
|
-
}
|
|
62223
|
-
ruleWatcherDebounceTimers.set(realPath, timer);
|
|
62224
|
-
});
|
|
62225
|
-
watcher.on("error", () => {
|
|
62226
|
-
ruleWatchers.delete(realPath);
|
|
62227
|
-
});
|
|
62228
|
-
ruleWatchers.set(realPath, watcher);
|
|
62229
|
-
} catch {}
|
|
62230
|
-
}
|
|
62231
|
-
function getCachedParsedRule(filePath, realPath, cwd) {
|
|
62232
|
-
try {
|
|
62233
|
-
const stat = statSync7(filePath);
|
|
62234
|
-
const key = makeRuleCacheKey(realPath, cwd, stat.mtimeMs);
|
|
62235
|
-
const cached2 = parsedRuleCache.get(key);
|
|
62236
|
-
if (cached2 && cached2.size === stat.size) {
|
|
62237
|
-
return { metadata: cached2.metadata, body: cached2.body };
|
|
62238
|
-
}
|
|
62239
|
-
const rawContent = readFileSync32(filePath, "utf-8");
|
|
62240
|
-
const { metadata, body } = parseRuleFrontmatter(rawContent);
|
|
62241
|
-
parsedRuleCache.set(key, {
|
|
62242
|
-
mtimeMs: stat.mtimeMs,
|
|
62243
|
-
size: stat.size,
|
|
62244
|
-
metadata,
|
|
62245
|
-
body
|
|
62246
|
-
});
|
|
62247
|
-
setupRuleWatcher(realPath);
|
|
62248
|
-
return { metadata, body };
|
|
62249
|
-
} catch {
|
|
62250
|
-
const rawContent = readFileSync32(filePath, "utf-8");
|
|
62251
|
-
return parseRuleFrontmatter(rawContent);
|
|
62252
|
-
}
|
|
62253
|
-
}
|
|
62254
|
-
function resolveFilePath3(workspaceDirectory, path12) {
|
|
62255
|
-
if (!path12)
|
|
62256
|
-
return null;
|
|
62257
|
-
if (path12.startsWith("/"))
|
|
62258
|
-
return path12;
|
|
62259
|
-
return resolve5(workspaceDirectory, path12);
|
|
62260
|
-
}
|
|
62261
|
-
function createRuleInjectionProcessor(deps) {
|
|
62262
|
-
const { workspaceDirectory, truncator, getSessionCache: getSessionCache2 } = deps;
|
|
62263
|
-
async function processFilePathForInjection(filePath, sessionID, output) {
|
|
62264
|
-
const resolved = resolveFilePath3(workspaceDirectory, filePath);
|
|
62265
|
-
if (!resolved)
|
|
62266
|
-
return;
|
|
62267
|
-
const projectRoot = findProjectRoot(resolved);
|
|
62268
|
-
const cache2 = getSessionCache2(sessionID);
|
|
62269
|
-
const ruleFileCandidates = findRuleFiles(projectRoot, resolved);
|
|
62270
|
-
const toInject = [];
|
|
62271
|
-
let dirty = false;
|
|
62272
|
-
for (const candidate of ruleFileCandidates) {
|
|
62273
|
-
if (isDuplicateByRealPath(candidate.realPath, cache2.realPaths))
|
|
62274
|
-
continue;
|
|
62275
|
-
try {
|
|
62276
|
-
const { metadata, body } = getCachedParsedRule(candidate.path, candidate.realPath, workspaceDirectory);
|
|
62277
|
-
let matchReason;
|
|
62278
|
-
if (candidate.isSingleFile) {
|
|
62279
|
-
matchReason = "copilot-instructions (always apply)";
|
|
62280
|
-
} else {
|
|
62281
|
-
const matchResult = shouldApplyRule(metadata, resolved, projectRoot);
|
|
62282
|
-
if (!matchResult.applies)
|
|
62283
|
-
continue;
|
|
62284
|
-
matchReason = matchResult.reason ?? "matched";
|
|
62285
|
-
}
|
|
62286
|
-
const contentHash = createContentHash(body);
|
|
62287
|
-
if (isDuplicateByContentHash(contentHash, cache2.contentHashes))
|
|
62288
|
-
continue;
|
|
62289
|
-
const relativePath = projectRoot ? relative3(projectRoot, candidate.path) : candidate.path;
|
|
62290
|
-
toInject.push({
|
|
62291
|
-
relativePath,
|
|
62292
|
-
matchReason,
|
|
62293
|
-
content: body,
|
|
62294
|
-
distance: candidate.distance
|
|
62295
|
-
});
|
|
62296
|
-
cache2.realPaths.add(candidate.realPath);
|
|
62297
|
-
cache2.contentHashes.add(contentHash);
|
|
62298
|
-
dirty = true;
|
|
62299
|
-
} catch {}
|
|
62300
|
-
}
|
|
62301
|
-
if (toInject.length === 0)
|
|
62302
|
-
return;
|
|
62303
|
-
toInject.sort((a, b) => a.distance - b.distance);
|
|
62304
|
-
for (const rule of toInject) {
|
|
62305
|
-
const { result, truncated } = await truncator.truncate(sessionID, rule.content);
|
|
62306
|
-
const truncationNotice = truncated ? `
|
|
62307
|
-
|
|
62308
|
-
[Note: Content was truncated to save context window space. For full context, please read the file directly: ${rule.relativePath}]` : "";
|
|
62309
|
-
output.output += `
|
|
62310
|
-
|
|
62311
|
-
[Rule: ${rule.relativePath}]
|
|
62312
|
-
[Match: ${rule.matchReason}]
|
|
62313
|
-
${result}${truncationNotice}`;
|
|
62314
|
-
}
|
|
62315
|
-
if (dirty) {
|
|
62316
|
-
saveInjectedRules(sessionID, cache2);
|
|
62317
|
-
}
|
|
62318
|
-
}
|
|
62319
|
-
return { processFilePathForInjection };
|
|
62320
|
-
}
|
|
62321
|
-
|
|
62322
|
-
// src/hooks/rules-injector/output-path.ts
|
|
62323
|
-
function getRuleInjectionFilePath(output) {
|
|
62324
|
-
const metadata = output.metadata;
|
|
62325
|
-
const metadataFilePath = metadata && typeof metadata === "object" ? metadata.filePath : undefined;
|
|
62326
|
-
if (typeof metadataFilePath === "string" && metadataFilePath.length > 0) {
|
|
62327
|
-
return metadataFilePath;
|
|
62328
|
-
}
|
|
62329
|
-
if (typeof output.title === "string" && output.title.length > 0) {
|
|
62330
|
-
return output.title;
|
|
62331
|
-
}
|
|
62332
|
-
return null;
|
|
62333
|
-
}
|
|
62334
|
-
|
|
62335
62266
|
// src/hooks/rules-injector/hook.ts
|
|
62336
|
-
var TRACKED_TOOLS = ["read", "write", "edit", "multiedit"];
|
|
62337
62267
|
function createRulesInjectorHook(ctx) {
|
|
62338
|
-
|
|
62339
|
-
const { getSessionCache: getSessionCache2, clearSessionCache } = createSessionCacheStore();
|
|
62340
|
-
const { processFilePathForInjection } = createRuleInjectionProcessor({
|
|
62341
|
-
workspaceDirectory: ctx.directory,
|
|
62342
|
-
truncator,
|
|
62343
|
-
getSessionCache: getSessionCache2
|
|
62344
|
-
});
|
|
62345
|
-
const toolExecuteAfter = async (input, output) => {
|
|
62346
|
-
const toolName = input.tool.toLowerCase();
|
|
62347
|
-
if (TRACKED_TOOLS.includes(toolName)) {
|
|
62348
|
-
const filePath = getRuleInjectionFilePath(output);
|
|
62349
|
-
if (!filePath)
|
|
62350
|
-
return;
|
|
62351
|
-
await processFilePathForInjection(filePath, input.sessionID, output);
|
|
62352
|
-
return;
|
|
62353
|
-
}
|
|
62354
|
-
};
|
|
62355
|
-
const toolExecuteBefore = async (input, output) => {};
|
|
62356
|
-
const eventHandler = async ({ event }) => {
|
|
62357
|
-
const props = event.properties;
|
|
62358
|
-
if (event.type === "session.deleted") {
|
|
62359
|
-
const sessionInfo = props?.info;
|
|
62360
|
-
if (sessionInfo?.id) {
|
|
62361
|
-
clearSessionCache(sessionInfo.id);
|
|
62362
|
-
}
|
|
62363
|
-
}
|
|
62364
|
-
if (event.type === "session.compacted") {
|
|
62365
|
-
const sessionID = props?.sessionID ?? props?.info?.id;
|
|
62366
|
-
if (sessionID) {
|
|
62367
|
-
clearSessionCache(sessionID);
|
|
62368
|
-
}
|
|
62369
|
-
}
|
|
62370
|
-
};
|
|
62371
|
-
return {
|
|
62372
|
-
"tool.execute.before": toolExecuteBefore,
|
|
62373
|
-
"tool.execute.after": toolExecuteAfter,
|
|
62374
|
-
event: eventHandler
|
|
62375
|
-
};
|
|
62268
|
+
return createPostReadInjectorHook(ctx, { trackedTools: ["read", "write", "edit", "multiedit"] });
|
|
62376
62269
|
}
|
|
62377
62270
|
// src/hooks/runtime-fallback/auto-retry.ts
|
|
62378
62271
|
init_logger();
|
|
@@ -64967,8 +64860,8 @@ var FAILURE_RESET_WINDOW_MS = 5 * 60 * 1000;
|
|
|
64967
64860
|
init_logger();
|
|
64968
64861
|
|
|
64969
64862
|
// src/hooks/task-continuation-enforcer/idle-event.ts
|
|
64970
|
-
import { existsSync as
|
|
64971
|
-
import { join as
|
|
64863
|
+
import { existsSync as existsSync43, readdirSync as readdirSync15 } from "fs";
|
|
64864
|
+
import { join as join52 } from "path";
|
|
64972
64865
|
init_logger();
|
|
64973
64866
|
|
|
64974
64867
|
// src/hooks/task-continuation-enforcer/abort-detection.ts
|
|
@@ -64991,12 +64884,12 @@ function isLastAssistantMessageAborted(messages) {
|
|
|
64991
64884
|
init_logger();
|
|
64992
64885
|
|
|
64993
64886
|
// src/hooks/task-continuation-enforcer/continuation-injection.ts
|
|
64994
|
-
import { existsSync as
|
|
64995
|
-
import { join as
|
|
64887
|
+
import { existsSync as existsSync42, readdirSync as readdirSync14 } from "fs";
|
|
64888
|
+
import { join as join51 } from "path";
|
|
64996
64889
|
init_logger();
|
|
64997
64890
|
// src/hooks/task-continuation-enforcer/staleness.ts
|
|
64998
|
-
import { statSync as
|
|
64999
|
-
import { join as
|
|
64891
|
+
import { statSync as statSync6 } from "fs";
|
|
64892
|
+
import { join as join50 } from "path";
|
|
65000
64893
|
var DEFAULT_STALE_AFTER_HOURS = 24;
|
|
65001
64894
|
var HOUR_MS = 60 * 60 * 1000;
|
|
65002
64895
|
function getStaleAfterMs(config2) {
|
|
@@ -65005,7 +64898,7 @@ function getStaleAfterMs(config2) {
|
|
|
65005
64898
|
}
|
|
65006
64899
|
function getTaskAgeMs(taskPath) {
|
|
65007
64900
|
try {
|
|
65008
|
-
const stat =
|
|
64901
|
+
const stat = statSync6(taskPath);
|
|
65009
64902
|
return Date.now() - stat.mtimeMs;
|
|
65010
64903
|
} catch {
|
|
65011
64904
|
return null;
|
|
@@ -65016,7 +64909,7 @@ function isTaskStale(taskPath, staleAfterMs) {
|
|
|
65016
64909
|
return ageMs !== null && ageMs > staleAfterMs;
|
|
65017
64910
|
}
|
|
65018
64911
|
function filterFreshIncompleteTasks(tasks, taskDir, staleAfterMs) {
|
|
65019
|
-
return tasks.filter((t) => !isTaskStale(
|
|
64912
|
+
return tasks.filter((t) => !isTaskStale(join50(taskDir, `${t.id}.json`), staleAfterMs));
|
|
65020
64913
|
}
|
|
65021
64914
|
function formatTaskAge(ageMs) {
|
|
65022
64915
|
const hours = Math.floor(ageMs / HOUR_MS);
|
|
@@ -65117,7 +65010,7 @@ async function injectContinuation(args) {
|
|
|
65117
65010
|
let taskDir = "";
|
|
65118
65011
|
try {
|
|
65119
65012
|
taskDir = getTaskDir(config2, ctx.directory);
|
|
65120
|
-
if (!
|
|
65013
|
+
if (!existsSync42(taskDir)) {
|
|
65121
65014
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65122
65015
|
if (hadNonExplorerBgTasks) {
|
|
65123
65016
|
log(`[${HOOK_NAME14}] Bootstrap injection: no task dir (hadNonExplorerBgTasks)`, { sessionID, taskDir });
|
|
@@ -65200,7 +65093,7 @@ async function injectContinuation(args) {
|
|
|
65200
65093
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65201
65094
|
const freshIncompleteTasks = filterFreshIncompleteTasks(incompleteTasks, taskDir, staleAfterMs);
|
|
65202
65095
|
const taskList = incompleteTasks.map((task) => {
|
|
65203
|
-
const ageMs = getTaskAgeMs(
|
|
65096
|
+
const ageMs = getTaskAgeMs(join51(taskDir, `${task.id}.json`));
|
|
65204
65097
|
const staleSuffix = ageMs !== null && ageMs > staleAfterMs ? ` (stale: ${formatTaskAge(ageMs)})` : "";
|
|
65205
65098
|
return `- [${task.status}] ${task.subject} (${task.id})${staleSuffix}`;
|
|
65206
65099
|
}).join(`
|
|
@@ -65382,7 +65275,7 @@ async function handleSessionIdle(args) {
|
|
|
65382
65275
|
let taskDir = "";
|
|
65383
65276
|
try {
|
|
65384
65277
|
taskDir = getTaskDir(config2, ctx.directory);
|
|
65385
|
-
if (!
|
|
65278
|
+
if (!existsSync43(taskDir)) {
|
|
65386
65279
|
const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
|
|
65387
65280
|
if (hadNonExplorerBgTasks) {
|
|
65388
65281
|
log(`[${HOOK_NAME14}] No task dir \u2014 bootstrap (hadBgTasks)`, { sessionID, taskDir });
|
|
@@ -65421,7 +65314,7 @@ async function handleSessionIdle(args) {
|
|
|
65421
65314
|
const incompleteTasks = getIncompleteTasks(filteredTasks);
|
|
65422
65315
|
incompleteCount = incompleteTasks.length;
|
|
65423
65316
|
const staleAfterMs = getStaleAfterMs(config2);
|
|
65424
|
-
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(
|
|
65317
|
+
const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(join52(taskDir, `${t.id}.json`), staleAfterMs)).length;
|
|
65425
65318
|
const activeIncompleteCount = incompleteCount - staleIncompleteCount;
|
|
65426
65319
|
if (activeIncompleteCount === 0 && staleIncompleteCount > 0) {
|
|
65427
65320
|
log(`[${HOOK_NAME14}] Skipped: only stale tasks remain`, {
|
|
@@ -65868,8 +65761,8 @@ var TASK_NOTEPAD_FRAGMENT = "task-notepad";
|
|
|
65868
65761
|
var TODO_FETCH_ERROR_FRAGMENT = "Failed to fetch todos for task-notepad";
|
|
65869
65762
|
// src/hooks/task-notepad/hook.ts
|
|
65870
65763
|
init_logger();
|
|
65871
|
-
import { existsSync as
|
|
65872
|
-
import { dirname as
|
|
65764
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync16, readdirSync as readdirSync16, readFileSync as readFileSync34, statSync as statSync7, writeFileSync as writeFileSync18 } from "fs";
|
|
65765
|
+
import { dirname as dirname11, join as join53 } from "path";
|
|
65873
65766
|
function createTaskNotepadHook(ctx) {
|
|
65874
65767
|
return {
|
|
65875
65768
|
"tool.execute.after": async (input, output) => {
|
|
@@ -65920,8 +65813,8 @@ async function fetchTodos(ctx, sessionID) {
|
|
|
65920
65813
|
}
|
|
65921
65814
|
}
|
|
65922
65815
|
function resolveActivePlan(directory) {
|
|
65923
|
-
const plansDir =
|
|
65924
|
-
if (!
|
|
65816
|
+
const plansDir = join53(directory, PLAN_DIR_NAME, PLANS_SUBDIR);
|
|
65817
|
+
if (!existsSync44(plansDir))
|
|
65925
65818
|
return null;
|
|
65926
65819
|
let entries;
|
|
65927
65820
|
try {
|
|
@@ -65933,9 +65826,9 @@ function resolveActivePlan(directory) {
|
|
|
65933
65826
|
for (const entry of entries) {
|
|
65934
65827
|
if (!entry.endsWith(PLAN_EXTENSION))
|
|
65935
65828
|
continue;
|
|
65936
|
-
const fullPath =
|
|
65829
|
+
const fullPath = join53(plansDir, entry);
|
|
65937
65830
|
try {
|
|
65938
|
-
const stats =
|
|
65831
|
+
const stats = statSync7(fullPath);
|
|
65939
65832
|
if (!stats.isFile())
|
|
65940
65833
|
continue;
|
|
65941
65834
|
const candidate = {
|
|
@@ -65952,18 +65845,18 @@ function resolveActivePlan(directory) {
|
|
|
65952
65845
|
}
|
|
65953
65846
|
function ensureNotepad(directory, planName, todo) {
|
|
65954
65847
|
const notepadPath = notepadPathFor(directory, planName, todo.id);
|
|
65955
|
-
if (
|
|
65848
|
+
if (existsSync44(notepadPath))
|
|
65956
65849
|
return;
|
|
65957
|
-
mkdirSync16(
|
|
65850
|
+
mkdirSync16(dirname11(notepadPath), { recursive: true });
|
|
65958
65851
|
const content = renderNotepad(todo);
|
|
65959
65852
|
writeFileSync18(notepadPath, content, "utf-8");
|
|
65960
65853
|
log(`[${HOOK_NAME16}] Created task notepad`, { plan: planName, taskID: todo.id });
|
|
65961
65854
|
}
|
|
65962
65855
|
function appendCompletion(directory, planName, todo) {
|
|
65963
65856
|
const notepadPath = notepadPathFor(directory, planName, todo.id);
|
|
65964
|
-
if (!
|
|
65857
|
+
if (!existsSync44(notepadPath))
|
|
65965
65858
|
return;
|
|
65966
|
-
const existing =
|
|
65859
|
+
const existing = readFileSync34(notepadPath, "utf-8");
|
|
65967
65860
|
if (existing.includes("**Status**: completed"))
|
|
65968
65861
|
return;
|
|
65969
65862
|
const stamp = new Date().toISOString();
|
|
@@ -65976,7 +65869,7 @@ function appendCompletion(directory, planName, todo) {
|
|
|
65976
65869
|
log(`[${HOOK_NAME16}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
|
|
65977
65870
|
}
|
|
65978
65871
|
function notepadPathFor(directory, planName, taskID) {
|
|
65979
|
-
return
|
|
65872
|
+
return join53(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
|
|
65980
65873
|
}
|
|
65981
65874
|
function renderNotepad(todo) {
|
|
65982
65875
|
const priority = todo.priority ?? "medium";
|
|
@@ -67185,6 +67078,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
|
|
|
67185
67078
|
// src/hooks/tool-output-truncator.ts
|
|
67186
67079
|
var DEFAULT_MAX_TOKENS = 50000;
|
|
67187
67080
|
var WEBFETCH_MAX_TOKENS = 1e4;
|
|
67081
|
+
var READ_MAX_TOKENS = 1e4;
|
|
67188
67082
|
var TRUNCATABLE_TOOLS = [
|
|
67189
67083
|
"grep",
|
|
67190
67084
|
"Grep",
|
|
@@ -67197,11 +67091,19 @@ var TRUNCATABLE_TOOLS = [
|
|
|
67197
67091
|
"interactive_bash",
|
|
67198
67092
|
"Interactive_bash",
|
|
67199
67093
|
"webfetch",
|
|
67200
|
-
"WebFetch"
|
|
67094
|
+
"WebFetch",
|
|
67095
|
+
"read",
|
|
67096
|
+
"Read",
|
|
67097
|
+
"lookup_type",
|
|
67098
|
+
"list_types"
|
|
67201
67099
|
];
|
|
67202
67100
|
var TOOL_SPECIFIC_MAX_TOKENS = {
|
|
67203
67101
|
webfetch: WEBFETCH_MAX_TOKENS,
|
|
67204
|
-
WebFetch: WEBFETCH_MAX_TOKENS
|
|
67102
|
+
WebFetch: WEBFETCH_MAX_TOKENS,
|
|
67103
|
+
read: READ_MAX_TOKENS,
|
|
67104
|
+
Read: READ_MAX_TOKENS,
|
|
67105
|
+
lookup_type: READ_MAX_TOKENS,
|
|
67106
|
+
list_types: READ_MAX_TOKENS
|
|
67205
67107
|
};
|
|
67206
67108
|
function createToolOutputTruncatorHook(ctx, options) {
|
|
67207
67109
|
const truncator = createDynamicTruncator(ctx);
|
|
@@ -67651,8 +67553,8 @@ function createWebFetchRedirectGuardHook(_ctx) {
|
|
|
67651
67553
|
};
|
|
67652
67554
|
}
|
|
67653
67555
|
// src/hooks/write-existing-file-guard/hook.ts
|
|
67654
|
-
import { existsSync as
|
|
67655
|
-
import { isAbsolute as isAbsolute4, join as
|
|
67556
|
+
import { existsSync as existsSync45 } from "fs";
|
|
67557
|
+
import { isAbsolute as isAbsolute4, join as join54, normalize as normalize2, resolve as resolve5, sep } from "path";
|
|
67656
67558
|
function createWriteExistingFileGuardHook(ctx) {
|
|
67657
67559
|
return {
|
|
67658
67560
|
"tool.execute.before": async (input, output) => {
|
|
@@ -67665,9 +67567,9 @@ function createWriteExistingFileGuardHook(ctx) {
|
|
|
67665
67567
|
if (!filePath) {
|
|
67666
67568
|
return;
|
|
67667
67569
|
}
|
|
67668
|
-
const resolvedPath =
|
|
67669
|
-
if (
|
|
67670
|
-
const matrixxRoot =
|
|
67570
|
+
const resolvedPath = normalize2(isAbsolute4(filePath) ? filePath : resolve5(ctx.directory, filePath));
|
|
67571
|
+
if (existsSync45(resolvedPath)) {
|
|
67572
|
+
const matrixxRoot = join54(ctx.directory, ".matrixx") + sep;
|
|
67671
67573
|
const isMatrixMarkdown = resolvedPath.startsWith(matrixxRoot) && resolvedPath.endsWith(".md");
|
|
67672
67574
|
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
67673
67575
|
const isPlansPath = normalizedPath.includes(".matrixx/plans");
|
|
@@ -67845,8 +67747,8 @@ function createAnthropicEffortHook() {
|
|
|
67845
67747
|
};
|
|
67846
67748
|
}
|
|
67847
67749
|
// src/tools/lsp/lsp-client.ts
|
|
67848
|
-
import { readFileSync as
|
|
67849
|
-
import { extname as extname2, resolve as
|
|
67750
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
67751
|
+
import { extname as extname2, resolve as resolve6 } from "path";
|
|
67850
67752
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
67851
67753
|
|
|
67852
67754
|
// src/tools/lsp/language-mappings.ts
|
|
@@ -68112,14 +68014,14 @@ function getLanguageId(ext) {
|
|
|
68112
68014
|
return EXT_TO_LANG[ext] || "plaintext";
|
|
68113
68015
|
}
|
|
68114
68016
|
// src/tools/lsp/server-installation.ts
|
|
68115
|
-
import { existsSync as
|
|
68116
|
-
import { join as
|
|
68017
|
+
import { existsSync as existsSync46 } from "fs";
|
|
68018
|
+
import { join as join55 } from "path";
|
|
68117
68019
|
function isServerInstalled(command) {
|
|
68118
68020
|
if (command.length === 0)
|
|
68119
68021
|
return false;
|
|
68120
68022
|
const cmd = command[0];
|
|
68121
68023
|
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
68122
|
-
if (
|
|
68024
|
+
if (existsSync46(cmd))
|
|
68123
68025
|
return true;
|
|
68124
68026
|
}
|
|
68125
68027
|
const isWindows = process.platform === "win32";
|
|
@@ -68141,23 +68043,23 @@ function isServerInstalled(command) {
|
|
|
68141
68043
|
const paths = pathEnv.split(pathSeparator);
|
|
68142
68044
|
for (const p of paths) {
|
|
68143
68045
|
for (const suffix of exts) {
|
|
68144
|
-
if (
|
|
68046
|
+
if (existsSync46(join55(p, cmd + suffix))) {
|
|
68145
68047
|
return true;
|
|
68146
68048
|
}
|
|
68147
68049
|
}
|
|
68148
68050
|
}
|
|
68149
68051
|
const cwd = process.cwd();
|
|
68150
68052
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68151
|
-
const dataDir =
|
|
68053
|
+
const dataDir = join55(getDataDir(), "opencode");
|
|
68152
68054
|
const additionalBases = [
|
|
68153
|
-
|
|
68154
|
-
|
|
68155
|
-
|
|
68156
|
-
|
|
68055
|
+
join55(cwd, "node_modules", ".bin"),
|
|
68056
|
+
join55(configDir, "bin"),
|
|
68057
|
+
join55(configDir, "node_modules", ".bin"),
|
|
68058
|
+
join55(dataDir, "bin")
|
|
68157
68059
|
];
|
|
68158
68060
|
for (const base of additionalBases) {
|
|
68159
68061
|
for (const suffix of exts) {
|
|
68160
|
-
if (
|
|
68062
|
+
if (existsSync46(join55(base, cmd + suffix))) {
|
|
68161
68063
|
return true;
|
|
68162
68064
|
}
|
|
68163
68065
|
}
|
|
@@ -68168,13 +68070,13 @@ function isServerInstalled(command) {
|
|
|
68168
68070
|
return false;
|
|
68169
68071
|
}
|
|
68170
68072
|
// src/tools/lsp/server-config-loader.ts
|
|
68171
|
-
import { existsSync as
|
|
68172
|
-
import { join as
|
|
68073
|
+
import { existsSync as existsSync47, readFileSync as readFileSync35 } from "fs";
|
|
68074
|
+
import { join as join56 } from "path";
|
|
68173
68075
|
function loadJsonFile(path12) {
|
|
68174
|
-
if (!
|
|
68076
|
+
if (!existsSync47(path12))
|
|
68175
68077
|
return null;
|
|
68176
68078
|
try {
|
|
68177
|
-
return parseJsonc(
|
|
68079
|
+
return parseJsonc(readFileSync35(path12, "utf-8"));
|
|
68178
68080
|
} catch {
|
|
68179
68081
|
return null;
|
|
68180
68082
|
}
|
|
@@ -68183,9 +68085,9 @@ function getConfigPaths3() {
|
|
|
68183
68085
|
const cwd = process.cwd();
|
|
68184
68086
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
68185
68087
|
return {
|
|
68186
|
-
project: detectConfigFile(
|
|
68187
|
-
user: detectConfigFile(
|
|
68188
|
-
opencode: detectConfigFile(
|
|
68088
|
+
project: detectConfigFile(join56(cwd, ".opencode", "matrixx")).path,
|
|
68089
|
+
user: detectConfigFile(join56(configDir, "matrixx")).path,
|
|
68090
|
+
opencode: detectConfigFile(join56(configDir, "opencode")).path
|
|
68189
68091
|
};
|
|
68190
68092
|
}
|
|
68191
68093
|
function loadAllConfigs() {
|
|
@@ -68303,17 +68205,17 @@ import { Readable, Writable } from "stream";
|
|
|
68303
68205
|
// src/tools/lsp/lsp-process.ts
|
|
68304
68206
|
init_logger();
|
|
68305
68207
|
import { spawn as nodeSpawn } from "child_process";
|
|
68306
|
-
import { existsSync as
|
|
68208
|
+
import { existsSync as existsSync48, statSync as statSync8 } from "fs";
|
|
68307
68209
|
var {spawn: bunSpawn } = globalThis.Bun;
|
|
68308
68210
|
function shouldUseNodeSpawn() {
|
|
68309
68211
|
return process.platform === "win32";
|
|
68310
68212
|
}
|
|
68311
68213
|
function validateCwd(cwd) {
|
|
68312
68214
|
try {
|
|
68313
|
-
if (!
|
|
68215
|
+
if (!existsSync48(cwd)) {
|
|
68314
68216
|
return { valid: false, error: `Working directory does not exist: ${cwd}` };
|
|
68315
68217
|
}
|
|
68316
|
-
const stats =
|
|
68218
|
+
const stats = statSync8(cwd);
|
|
68317
68219
|
if (!stats.isDirectory()) {
|
|
68318
68220
|
return { valid: false, error: `Path is not a directory: ${cwd}` };
|
|
68319
68221
|
}
|
|
@@ -68325,8 +68227,8 @@ function validateCwd(cwd) {
|
|
|
68325
68227
|
function wrapNodeProcess(proc) {
|
|
68326
68228
|
let resolveExited;
|
|
68327
68229
|
let exitCode = null;
|
|
68328
|
-
const exitedPromise = new Promise((
|
|
68329
|
-
resolveExited =
|
|
68230
|
+
const exitedPromise = new Promise((resolve6) => {
|
|
68231
|
+
resolveExited = resolve6;
|
|
68330
68232
|
});
|
|
68331
68233
|
proc.on("exit", (code) => {
|
|
68332
68234
|
exitCode = code ?? 1;
|
|
@@ -68346,9 +68248,9 @@ function wrapNodeProcess(proc) {
|
|
|
68346
68248
|
nodeStream.on("data", (chunk) => {
|
|
68347
68249
|
const uint8 = new Uint8Array(chunk);
|
|
68348
68250
|
if (waitingResolve) {
|
|
68349
|
-
const
|
|
68251
|
+
const resolve6 = waitingResolve;
|
|
68350
68252
|
waitingResolve = null;
|
|
68351
|
-
|
|
68253
|
+
resolve6({ done: false, value: uint8 });
|
|
68352
68254
|
} else {
|
|
68353
68255
|
chunks.push(uint8);
|
|
68354
68256
|
}
|
|
@@ -68356,17 +68258,17 @@ function wrapNodeProcess(proc) {
|
|
|
68356
68258
|
nodeStream.on("end", () => {
|
|
68357
68259
|
streamEnded = true;
|
|
68358
68260
|
if (waitingResolve) {
|
|
68359
|
-
const
|
|
68261
|
+
const resolve6 = waitingResolve;
|
|
68360
68262
|
waitingResolve = null;
|
|
68361
|
-
|
|
68263
|
+
resolve6({ done: true, value: undefined });
|
|
68362
68264
|
}
|
|
68363
68265
|
});
|
|
68364
68266
|
nodeStream.on("error", () => {
|
|
68365
68267
|
streamEnded = true;
|
|
68366
68268
|
if (waitingResolve) {
|
|
68367
|
-
const
|
|
68269
|
+
const resolve6 = waitingResolve;
|
|
68368
68270
|
waitingResolve = null;
|
|
68369
|
-
|
|
68271
|
+
resolve6({ done: true, value: undefined });
|
|
68370
68272
|
}
|
|
68371
68273
|
});
|
|
68372
68274
|
} else {
|
|
@@ -68374,13 +68276,13 @@ function wrapNodeProcess(proc) {
|
|
|
68374
68276
|
}
|
|
68375
68277
|
return {
|
|
68376
68278
|
read() {
|
|
68377
|
-
return new Promise((
|
|
68279
|
+
return new Promise((resolve6) => {
|
|
68378
68280
|
if (chunks.length > 0) {
|
|
68379
|
-
|
|
68281
|
+
resolve6({ done: false, value: chunks.shift() });
|
|
68380
68282
|
} else if (streamEnded) {
|
|
68381
|
-
|
|
68283
|
+
resolve6({ done: true, value: undefined });
|
|
68382
68284
|
} else {
|
|
68383
|
-
waitingResolve =
|
|
68285
|
+
waitingResolve = resolve6;
|
|
68384
68286
|
}
|
|
68385
68287
|
});
|
|
68386
68288
|
}
|
|
@@ -68468,7 +68370,7 @@ class LSPClientTransport {
|
|
|
68468
68370
|
throw new Error(`Failed to spawn LSP server: ${this.server.command.join(" ")}`);
|
|
68469
68371
|
}
|
|
68470
68372
|
this.startStderrReading();
|
|
68471
|
-
await new Promise((
|
|
68373
|
+
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
68472
68374
|
if (this.proc.exitCode !== null) {
|
|
68473
68375
|
const stderr = this.stderrBuffer.join(`
|
|
68474
68376
|
`);
|
|
@@ -68600,8 +68502,8 @@ recent stderr: ${stderr}` : ""}`));
|
|
|
68600
68502
|
try {
|
|
68601
68503
|
proc.kill();
|
|
68602
68504
|
let timeoutId;
|
|
68603
|
-
const timeoutPromise = new Promise((
|
|
68604
|
-
timeoutId = setTimeout(
|
|
68505
|
+
const timeoutPromise = new Promise((resolve6) => {
|
|
68506
|
+
timeoutId = setTimeout(resolve6, 5000);
|
|
68605
68507
|
});
|
|
68606
68508
|
await Promise.race([
|
|
68607
68509
|
proc.exited.then(() => {
|
|
@@ -68613,7 +68515,7 @@ recent stderr: ${stderr}` : ""}`));
|
|
|
68613
68515
|
log("[LSPClient] Process did not exit within timeout, escalating to SIGKILL");
|
|
68614
68516
|
try {
|
|
68615
68517
|
proc.kill("SIGKILL");
|
|
68616
|
-
await Promise.race([proc.exited, new Promise((
|
|
68518
|
+
await Promise.race([proc.exited, new Promise((resolve6) => setTimeout(resolve6, 1000))]);
|
|
68617
68519
|
} catch {}
|
|
68618
68520
|
}
|
|
68619
68521
|
} catch {}
|
|
@@ -68693,9 +68595,9 @@ class LSPClient extends LSPClientConnection {
|
|
|
68693
68595
|
documentVersions = new Map;
|
|
68694
68596
|
lastSyncedText = new Map;
|
|
68695
68597
|
async openFile(filePath) {
|
|
68696
|
-
const absPath =
|
|
68598
|
+
const absPath = resolve6(filePath);
|
|
68697
68599
|
const uri = pathToFileURL2(absPath).href;
|
|
68698
|
-
const text =
|
|
68600
|
+
const text = readFileSync36(absPath, "utf-8");
|
|
68699
68601
|
if (!this.openedFiles.has(absPath)) {
|
|
68700
68602
|
const ext = extname2(absPath);
|
|
68701
68603
|
const languageId = getLanguageId(ext);
|
|
@@ -68731,7 +68633,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68731
68633
|
});
|
|
68732
68634
|
}
|
|
68733
68635
|
async definition(filePath, line, character) {
|
|
68734
|
-
const absPath =
|
|
68636
|
+
const absPath = resolve6(filePath);
|
|
68735
68637
|
await this.openFile(absPath);
|
|
68736
68638
|
return this.sendRequest("textDocument/definition", {
|
|
68737
68639
|
textDocument: { uri: pathToFileURL2(absPath).href },
|
|
@@ -68739,7 +68641,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68739
68641
|
});
|
|
68740
68642
|
}
|
|
68741
68643
|
async references(filePath, line, character, includeDeclaration = true) {
|
|
68742
|
-
const absPath =
|
|
68644
|
+
const absPath = resolve6(filePath);
|
|
68743
68645
|
await this.openFile(absPath);
|
|
68744
68646
|
return this.sendRequest("textDocument/references", {
|
|
68745
68647
|
textDocument: { uri: pathToFileURL2(absPath).href },
|
|
@@ -68748,7 +68650,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68748
68650
|
});
|
|
68749
68651
|
}
|
|
68750
68652
|
async documentSymbols(filePath) {
|
|
68751
|
-
const absPath =
|
|
68653
|
+
const absPath = resolve6(filePath);
|
|
68752
68654
|
await this.openFile(absPath);
|
|
68753
68655
|
return this.sendRequest("textDocument/documentSymbol", {
|
|
68754
68656
|
textDocument: { uri: pathToFileURL2(absPath).href }
|
|
@@ -68758,7 +68660,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68758
68660
|
return this.sendRequest("workspace/symbol", { query });
|
|
68759
68661
|
}
|
|
68760
68662
|
async diagnostics(filePath) {
|
|
68761
|
-
const absPath =
|
|
68663
|
+
const absPath = resolve6(filePath);
|
|
68762
68664
|
const uri = pathToFileURL2(absPath).href;
|
|
68763
68665
|
await this.openFile(absPath);
|
|
68764
68666
|
await new Promise((r) => setTimeout(r, 500));
|
|
@@ -68773,7 +68675,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68773
68675
|
return { items: this.diagnosticsStore.get(uri) ?? [] };
|
|
68774
68676
|
}
|
|
68775
68677
|
async prepareRename(filePath, line, character) {
|
|
68776
|
-
const absPath =
|
|
68678
|
+
const absPath = resolve6(filePath);
|
|
68777
68679
|
await this.openFile(absPath);
|
|
68778
68680
|
return this.sendRequest("textDocument/prepareRename", {
|
|
68779
68681
|
textDocument: { uri: pathToFileURL2(absPath).href },
|
|
@@ -68781,7 +68683,7 @@ class LSPClient extends LSPClientConnection {
|
|
|
68781
68683
|
});
|
|
68782
68684
|
}
|
|
68783
68685
|
async rename(filePath, line, character, newName) {
|
|
68784
|
-
const absPath =
|
|
68686
|
+
const absPath = resolve6(filePath);
|
|
68785
68687
|
await this.openFile(absPath);
|
|
68786
68688
|
return this.sendRequest("textDocument/rename", {
|
|
68787
68689
|
textDocument: { uri: pathToFileURL2(absPath).href },
|
|
@@ -69033,31 +68935,31 @@ class LSPServerManager {
|
|
|
69033
68935
|
}
|
|
69034
68936
|
var lspManager = LSPServerManager.getInstance();
|
|
69035
68937
|
// src/tools/lsp/lsp-client-wrapper.ts
|
|
69036
|
-
import { existsSync as
|
|
69037
|
-
import { dirname as
|
|
68938
|
+
import { existsSync as existsSync49, statSync as statSync9 } from "fs";
|
|
68939
|
+
import { dirname as dirname12, extname as extname3, join as join57, resolve as resolve7 } from "path";
|
|
69038
68940
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
69039
68941
|
function findWorkspaceRoot(filePath) {
|
|
69040
|
-
let dir =
|
|
68942
|
+
let dir = resolve7(filePath);
|
|
69041
68943
|
try {
|
|
69042
|
-
const stat =
|
|
68944
|
+
const stat = statSync9(dir);
|
|
69043
68945
|
if (!stat.isDirectory()) {
|
|
69044
|
-
dir =
|
|
68946
|
+
dir = dirname12(dir);
|
|
69045
68947
|
}
|
|
69046
68948
|
} catch {
|
|
69047
|
-
dir =
|
|
68949
|
+
dir = dirname12(dir);
|
|
69048
68950
|
}
|
|
69049
68951
|
const markers = [".git", "package.json", "pyproject.toml", "Cargo.toml", "go.mod", "pom.xml", "build.gradle"];
|
|
69050
68952
|
let prevDir = "";
|
|
69051
68953
|
while (dir !== prevDir) {
|
|
69052
68954
|
for (const marker of markers) {
|
|
69053
|
-
if (
|
|
68955
|
+
if (existsSync49(join57(dir, marker))) {
|
|
69054
68956
|
return dir;
|
|
69055
68957
|
}
|
|
69056
68958
|
}
|
|
69057
68959
|
prevDir = dir;
|
|
69058
|
-
dir =
|
|
68960
|
+
dir = dirname12(dir);
|
|
69059
68961
|
}
|
|
69060
|
-
return
|
|
68962
|
+
return dirname12(resolve7(filePath));
|
|
69061
68963
|
}
|
|
69062
68964
|
function uriToPath(uri) {
|
|
69063
68965
|
return fileURLToPath3(uri);
|
|
@@ -69098,7 +69000,7 @@ function formatServerLookupError(result) {
|
|
|
69098
69000
|
`);
|
|
69099
69001
|
}
|
|
69100
69002
|
async function withLspClient(filePath, fn) {
|
|
69101
|
-
const absPath =
|
|
69003
|
+
const absPath = resolve7(filePath);
|
|
69102
69004
|
const ext = extname3(absPath);
|
|
69103
69005
|
const result = findServerForExtension(ext);
|
|
69104
69006
|
if (result.status !== "found") {
|
|
@@ -81652,10 +81554,10 @@ var lsp_goto_definition = tool({
|
|
|
81652
81554
|
}
|
|
81653
81555
|
});
|
|
81654
81556
|
// src/tools/lsp/workspace-edit.ts
|
|
81655
|
-
import { readFileSync as
|
|
81557
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync19 } from "fs";
|
|
81656
81558
|
function applyTextEditsToFile(filePath, edits) {
|
|
81657
81559
|
try {
|
|
81658
|
-
const content =
|
|
81560
|
+
const content = readFileSync37(filePath, "utf-8");
|
|
81659
81561
|
const lines = content.split(`
|
|
81660
81562
|
`);
|
|
81661
81563
|
const sortedEdits = [...edits].sort((a, b) => {
|
|
@@ -81721,7 +81623,7 @@ function applyWorkspaceEdit(edit) {
|
|
|
81721
81623
|
try {
|
|
81722
81624
|
const oldPath = uriToPath(change.oldUri);
|
|
81723
81625
|
const newPath = uriToPath(change.newUri);
|
|
81724
|
-
const content =
|
|
81626
|
+
const content = readFileSync37(oldPath, "utf-8");
|
|
81725
81627
|
writeFileSync19(newPath, content, "utf-8");
|
|
81726
81628
|
__require("fs").unlinkSync(oldPath);
|
|
81727
81629
|
result.filesModified.push(newPath);
|
|
@@ -81860,12 +81762,12 @@ var lsp_symbols = tool({
|
|
|
81860
81762
|
}
|
|
81861
81763
|
});
|
|
81862
81764
|
// src/tools/ast-grep/cli.ts
|
|
81863
|
-
import { existsSync as
|
|
81765
|
+
import { existsSync as existsSync53 } from "fs";
|
|
81864
81766
|
var {spawn: spawn9 } = globalThis.Bun;
|
|
81865
81767
|
|
|
81866
81768
|
// src/tools/ast-grep/cli-binary-path-resolution.ts
|
|
81867
81769
|
init_logger();
|
|
81868
|
-
import { existsSync as
|
|
81770
|
+
import { existsSync as existsSync52 } from "fs";
|
|
81869
81771
|
|
|
81870
81772
|
// src/tools/ast-grep/language-support.ts
|
|
81871
81773
|
var CLI_LANGUAGES = [
|
|
@@ -81900,15 +81802,15 @@ var DEFAULT_MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
|
81900
81802
|
var DEFAULT_MAX_MATCHES = 500;
|
|
81901
81803
|
|
|
81902
81804
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
81903
|
-
import { existsSync as
|
|
81805
|
+
import { existsSync as existsSync51, statSync as statSync10 } from "fs";
|
|
81904
81806
|
import { createRequire as createRequire4 } from "module";
|
|
81905
|
-
import { dirname as
|
|
81807
|
+
import { dirname as dirname13, join as join59 } from "path";
|
|
81906
81808
|
|
|
81907
81809
|
// src/tools/ast-grep/downloader.ts
|
|
81908
|
-
import { existsSync as
|
|
81810
|
+
import { existsSync as existsSync50 } from "fs";
|
|
81909
81811
|
import { createRequire as createRequire3 } from "module";
|
|
81910
81812
|
import { homedir as homedir9 } from "os";
|
|
81911
|
-
import { join as
|
|
81813
|
+
import { join as join58 } from "path";
|
|
81912
81814
|
init_logger();
|
|
81913
81815
|
var REPO2 = "ast-grep/ast-grep";
|
|
81914
81816
|
var DEFAULT_VERSION = "0.40.0";
|
|
@@ -81933,12 +81835,12 @@ var PLATFORM_MAP2 = {
|
|
|
81933
81835
|
function getCacheDir3() {
|
|
81934
81836
|
if (process.platform === "win32") {
|
|
81935
81837
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
81936
|
-
const base2 = localAppData ||
|
|
81937
|
-
return
|
|
81838
|
+
const base2 = localAppData || join58(homedir9(), "AppData", "Local");
|
|
81839
|
+
return join58(base2, "matrixx", "bin");
|
|
81938
81840
|
}
|
|
81939
81841
|
const xdgCache = process.env.XDG_CACHE_HOME;
|
|
81940
|
-
const base = xdgCache ||
|
|
81941
|
-
return
|
|
81842
|
+
const base = xdgCache || join58(homedir9(), ".cache");
|
|
81843
|
+
return join58(base, "matrixx", "bin");
|
|
81942
81844
|
}
|
|
81943
81845
|
function getBinaryName3() {
|
|
81944
81846
|
return process.platform === "win32" ? "sg.exe" : "sg";
|
|
@@ -81955,8 +81857,8 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81955
81857
|
}
|
|
81956
81858
|
const cacheDir = getCacheDir3();
|
|
81957
81859
|
const binaryName = getBinaryName3();
|
|
81958
|
-
const binaryPath =
|
|
81959
|
-
if (
|
|
81860
|
+
const binaryPath = join58(cacheDir, binaryName);
|
|
81861
|
+
if (existsSync50(binaryPath)) {
|
|
81960
81862
|
return binaryPath;
|
|
81961
81863
|
}
|
|
81962
81864
|
const { arch, os: os7 } = platformInfo;
|
|
@@ -81964,7 +81866,7 @@ async function downloadAstGrep(version3 = DEFAULT_VERSION) {
|
|
|
81964
81866
|
const downloadUrl = `https://github.com/${REPO2}/releases/download/${version3}/${assetName}`;
|
|
81965
81867
|
log(`[matrixx] Downloading ast-grep binary...`);
|
|
81966
81868
|
try {
|
|
81967
|
-
const archivePath =
|
|
81869
|
+
const archivePath = join58(cacheDir, assetName);
|
|
81968
81870
|
ensureCacheDir(cacheDir);
|
|
81969
81871
|
await downloadArchive(downloadUrl, archivePath);
|
|
81970
81872
|
await extractZipArchive(archivePath, cacheDir);
|
|
@@ -81989,7 +81891,7 @@ async function ensureAstGrepBinary() {
|
|
|
81989
81891
|
// src/tools/ast-grep/sg-cli-path.ts
|
|
81990
81892
|
function isValidBinary(filePath) {
|
|
81991
81893
|
try {
|
|
81992
|
-
return
|
|
81894
|
+
return statSync10(filePath).size > 1e4;
|
|
81993
81895
|
} catch {
|
|
81994
81896
|
return false;
|
|
81995
81897
|
}
|
|
@@ -82017,9 +81919,9 @@ function findSgCliPathSync() {
|
|
|
82017
81919
|
try {
|
|
82018
81920
|
const require2 = createRequire4(import.meta.url);
|
|
82019
81921
|
const cliPackageJsonPath = require2.resolve("@ast-grep/cli/package.json");
|
|
82020
|
-
const cliDirectory =
|
|
82021
|
-
const sgPath =
|
|
82022
|
-
if (
|
|
81922
|
+
const cliDirectory = dirname13(cliPackageJsonPath);
|
|
81923
|
+
const sgPath = join59(cliDirectory, binaryName);
|
|
81924
|
+
if (existsSync51(sgPath) && isValidBinary(sgPath)) {
|
|
82023
81925
|
return sgPath;
|
|
82024
81926
|
}
|
|
82025
81927
|
} catch {}
|
|
@@ -82028,10 +81930,10 @@ function findSgCliPathSync() {
|
|
|
82028
81930
|
try {
|
|
82029
81931
|
const require2 = createRequire4(import.meta.url);
|
|
82030
81932
|
const packageJsonPath = require2.resolve(`${platformPackage}/package.json`);
|
|
82031
|
-
const packageDirectory =
|
|
81933
|
+
const packageDirectory = dirname13(packageJsonPath);
|
|
82032
81934
|
const astGrepBinaryName = process.platform === "win32" ? "ast-grep.exe" : "ast-grep";
|
|
82033
|
-
const binaryPath =
|
|
82034
|
-
if (
|
|
81935
|
+
const binaryPath = join59(packageDirectory, astGrepBinaryName);
|
|
81936
|
+
if (existsSync51(binaryPath) && isValidBinary(binaryPath)) {
|
|
82035
81937
|
return binaryPath;
|
|
82036
81938
|
}
|
|
82037
81939
|
} catch {}
|
|
@@ -82039,7 +81941,7 @@ function findSgCliPathSync() {
|
|
|
82039
81941
|
if (process.platform === "darwin") {
|
|
82040
81942
|
const homebrewPaths = ["/opt/homebrew/bin/sg", "/usr/local/bin/sg"];
|
|
82041
81943
|
for (const path12 of homebrewPaths) {
|
|
82042
|
-
if (
|
|
81944
|
+
if (existsSync51(path12) && isValidBinary(path12)) {
|
|
82043
81945
|
return path12;
|
|
82044
81946
|
}
|
|
82045
81947
|
}
|
|
@@ -82065,7 +81967,7 @@ function setSgCliPath(path12) {
|
|
|
82065
81967
|
var resolvedCliPath3 = null;
|
|
82066
81968
|
var initPromise3 = null;
|
|
82067
81969
|
async function getAstGrepPath() {
|
|
82068
|
-
if (resolvedCliPath3 !== null &&
|
|
81970
|
+
if (resolvedCliPath3 !== null && existsSync52(resolvedCliPath3)) {
|
|
82069
81971
|
return resolvedCliPath3;
|
|
82070
81972
|
}
|
|
82071
81973
|
if (initPromise3) {
|
|
@@ -82073,7 +81975,7 @@ async function getAstGrepPath() {
|
|
|
82073
81975
|
}
|
|
82074
81976
|
initPromise3 = (async () => {
|
|
82075
81977
|
const syncPath = findSgCliPathSync();
|
|
82076
|
-
if (syncPath &&
|
|
81978
|
+
if (syncPath && existsSync52(syncPath)) {
|
|
82077
81979
|
resolvedCliPath3 = syncPath;
|
|
82078
81980
|
setSgCliPath(syncPath);
|
|
82079
81981
|
return syncPath;
|
|
@@ -82172,7 +82074,7 @@ async function runSg(options) {
|
|
|
82172
82074
|
const paths = options.paths && options.paths.length > 0 ? options.paths : ["."];
|
|
82173
82075
|
args.push(...paths);
|
|
82174
82076
|
let cliPath = getSgCliPath();
|
|
82175
|
-
if (!cliPath || !
|
|
82077
|
+
if (!cliPath || !existsSync53(cliPath)) {
|
|
82176
82078
|
const downloadedPath = await getAstGrepPath();
|
|
82177
82079
|
if (downloadedPath) {
|
|
82178
82080
|
cliPath = downloadedPath;
|
|
@@ -84460,18 +84362,18 @@ var {spawn: spawn10 } = globalThis.Bun;
|
|
|
84460
84362
|
|
|
84461
84363
|
// src/tools/grep/constants.ts
|
|
84462
84364
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
84463
|
-
import { existsSync as
|
|
84464
|
-
import { dirname as
|
|
84365
|
+
import { existsSync as existsSync58 } from "fs";
|
|
84366
|
+
import { dirname as dirname14, join as join61 } from "path";
|
|
84465
84367
|
|
|
84466
84368
|
// src/tools/grep/downloader.ts
|
|
84467
|
-
import { existsSync as
|
|
84468
|
-
import { join as
|
|
84369
|
+
import { existsSync as existsSync57, readdirSync as readdirSync18 } from "fs";
|
|
84370
|
+
import { join as join60 } from "path";
|
|
84469
84371
|
function findFileRecursive(dir, filename) {
|
|
84470
84372
|
try {
|
|
84471
84373
|
const entries = readdirSync18(dir, { withFileTypes: true, recursive: true });
|
|
84472
84374
|
for (const entry of entries) {
|
|
84473
84375
|
if (entry.isFile() && entry.name === filename) {
|
|
84474
|
-
return
|
|
84376
|
+
return join60(entry.parentPath ?? dir, entry.name);
|
|
84475
84377
|
}
|
|
84476
84378
|
}
|
|
84477
84379
|
} catch {
|
|
@@ -84492,11 +84394,11 @@ function getPlatformKey() {
|
|
|
84492
84394
|
}
|
|
84493
84395
|
function getInstallDir() {
|
|
84494
84396
|
const homeDir = process.env.HOME || process.env.USERPROFILE || ".";
|
|
84495
|
-
return
|
|
84397
|
+
return join60(homeDir, ".cache", "matrixx", "bin");
|
|
84496
84398
|
}
|
|
84497
84399
|
function getRgPath() {
|
|
84498
84400
|
const isWindows = process.platform === "win32";
|
|
84499
|
-
return
|
|
84401
|
+
return join60(getInstallDir(), isWindows ? "rg.exe" : "rg");
|
|
84500
84402
|
}
|
|
84501
84403
|
async function extractTarGz2(archivePath, destDir) {
|
|
84502
84404
|
const platformKey = getPlatformKey();
|
|
@@ -84513,7 +84415,7 @@ async function extractZip2(archivePath, destDir) {
|
|
|
84513
84415
|
const binaryName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
84514
84416
|
const foundPath = findFileRecursive(destDir, binaryName);
|
|
84515
84417
|
if (foundPath) {
|
|
84516
|
-
const destPath =
|
|
84418
|
+
const destPath = join60(destDir, binaryName);
|
|
84517
84419
|
if (foundPath !== destPath) {
|
|
84518
84420
|
const { renameSync: renameSync4 } = await import("fs");
|
|
84519
84421
|
renameSync4(foundPath, destPath);
|
|
@@ -84528,13 +84430,13 @@ async function downloadAndInstallRipgrep() {
|
|
|
84528
84430
|
}
|
|
84529
84431
|
const installDir = getInstallDir();
|
|
84530
84432
|
const rgPath = getRgPath();
|
|
84531
|
-
if (
|
|
84433
|
+
if (existsSync57(rgPath)) {
|
|
84532
84434
|
return rgPath;
|
|
84533
84435
|
}
|
|
84534
84436
|
ensureCacheDir(installDir);
|
|
84535
84437
|
const filename = `ripgrep-${RG_VERSION}-${config4.platform}.${config4.extension}`;
|
|
84536
84438
|
const url3 = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${filename}`;
|
|
84537
|
-
const archivePath =
|
|
84439
|
+
const archivePath = join60(installDir, filename);
|
|
84538
84440
|
try {
|
|
84539
84441
|
await downloadArchive(url3, archivePath);
|
|
84540
84442
|
if (config4.extension === "tar.gz") {
|
|
@@ -84543,7 +84445,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
84543
84445
|
await extractZip2(archivePath, installDir);
|
|
84544
84446
|
}
|
|
84545
84447
|
ensureExecutable(rgPath);
|
|
84546
|
-
if (!
|
|
84448
|
+
if (!existsSync57(rgPath)) {
|
|
84547
84449
|
throw new Error("ripgrep binary not found after extraction");
|
|
84548
84450
|
}
|
|
84549
84451
|
return rgPath;
|
|
@@ -84555,7 +84457,7 @@ async function downloadAndInstallRipgrep() {
|
|
|
84555
84457
|
}
|
|
84556
84458
|
function getInstalledRipgrepPath() {
|
|
84557
84459
|
const rgPath = getRgPath();
|
|
84558
|
-
return
|
|
84460
|
+
return existsSync57(rgPath) ? rgPath : null;
|
|
84559
84461
|
}
|
|
84560
84462
|
|
|
84561
84463
|
// src/tools/grep/constants.ts
|
|
@@ -84575,18 +84477,18 @@ function findExecutable(name) {
|
|
|
84575
84477
|
}
|
|
84576
84478
|
function getOpenCodeBundledRg() {
|
|
84577
84479
|
const execPath = process.execPath;
|
|
84578
|
-
const execDir =
|
|
84480
|
+
const execDir = dirname14(execPath);
|
|
84579
84481
|
const isWindows = process.platform === "win32";
|
|
84580
84482
|
const rgName = isWindows ? "rg.exe" : "rg";
|
|
84581
84483
|
const candidates = [
|
|
84582
|
-
|
|
84583
|
-
|
|
84584
|
-
|
|
84585
|
-
|
|
84586
|
-
|
|
84484
|
+
join61(getDataDir(), "opencode", "bin", rgName),
|
|
84485
|
+
join61(execDir, rgName),
|
|
84486
|
+
join61(execDir, "bin", rgName),
|
|
84487
|
+
join61(execDir, "..", "bin", rgName),
|
|
84488
|
+
join61(execDir, "..", "libexec", rgName)
|
|
84587
84489
|
];
|
|
84588
84490
|
for (const candidate of candidates) {
|
|
84589
|
-
if (
|
|
84491
|
+
if (existsSync58(candidate)) {
|
|
84590
84492
|
return candidate;
|
|
84591
84493
|
}
|
|
84592
84494
|
}
|
|
@@ -85090,17 +84992,17 @@ var HandoffSchema = exports_external.object({
|
|
|
85090
84992
|
sections: HandoffSectionSchema
|
|
85091
84993
|
});
|
|
85092
84994
|
// src/features/handoff/storage.ts
|
|
85093
|
-
import { existsSync as
|
|
85094
|
-
import { join as
|
|
84995
|
+
import { existsSync as existsSync59, mkdirSync as mkdirSync17, readFileSync as readFileSync40, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
|
|
84996
|
+
import { join as join62 } from "path";
|
|
85095
84997
|
function getHandoffFilePath(directory) {
|
|
85096
|
-
return
|
|
84998
|
+
return join62(directory, HANDOFF_FILE_PATH);
|
|
85097
84999
|
}
|
|
85098
85000
|
function getHandoffConsumedFilePath(directory) {
|
|
85099
|
-
return
|
|
85001
|
+
return join62(directory, HANDOFF_CONSUMED_FILE_PATH);
|
|
85100
85002
|
}
|
|
85101
85003
|
function handoffFileExists(directory) {
|
|
85102
85004
|
try {
|
|
85103
|
-
return
|
|
85005
|
+
return existsSync59(getHandoffFilePath(directory));
|
|
85104
85006
|
} catch {
|
|
85105
85007
|
return false;
|
|
85106
85008
|
}
|
|
@@ -85108,9 +85010,9 @@ function handoffFileExists(directory) {
|
|
|
85108
85010
|
function readHandoffFile(directory) {
|
|
85109
85011
|
const filePath = getHandoffFilePath(directory);
|
|
85110
85012
|
try {
|
|
85111
|
-
if (!
|
|
85013
|
+
if (!existsSync59(filePath))
|
|
85112
85014
|
return null;
|
|
85113
|
-
return
|
|
85015
|
+
return readFileSync40(filePath, "utf-8");
|
|
85114
85016
|
} catch {
|
|
85115
85017
|
return null;
|
|
85116
85018
|
}
|
|
@@ -85134,7 +85036,7 @@ function writeHandoffFile(directory, content) {
|
|
|
85134
85036
|
}
|
|
85135
85037
|
function ensureHandoffDir(directory) {
|
|
85136
85038
|
try {
|
|
85137
|
-
mkdirSync17(
|
|
85039
|
+
mkdirSync17(join62(directory, ".matrixx"), { recursive: true });
|
|
85138
85040
|
return true;
|
|
85139
85041
|
} catch {
|
|
85140
85042
|
return false;
|
|
@@ -85289,15 +85191,15 @@ ${body}
|
|
|
85289
85191
|
}
|
|
85290
85192
|
|
|
85291
85193
|
// src/tools/handoff/list-handler.ts
|
|
85292
|
-
import { existsSync as
|
|
85293
|
-
import { join as
|
|
85194
|
+
import { existsSync as existsSync60, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
|
|
85195
|
+
import { join as join63 } from "path";
|
|
85294
85196
|
var MATRIX_DIR = ".matrixx";
|
|
85295
85197
|
function isHandoffFile(name) {
|
|
85296
85198
|
return name === "handoff.md" || name.startsWith("handoff.consumed");
|
|
85297
85199
|
}
|
|
85298
85200
|
function handleList(directory) {
|
|
85299
|
-
const matrixxDir =
|
|
85300
|
-
if (!
|
|
85201
|
+
const matrixxDir = join63(directory, MATRIX_DIR);
|
|
85202
|
+
if (!existsSync60(matrixxDir)) {
|
|
85301
85203
|
return "No handoffs found in .matrixx/";
|
|
85302
85204
|
}
|
|
85303
85205
|
let entries;
|
|
@@ -85314,11 +85216,11 @@ function handleList(directory) {
|
|
|
85314
85216
|
lines.push(`Handoff files in ${matrixxDir}:`);
|
|
85315
85217
|
lines.push("");
|
|
85316
85218
|
for (const name of handoffs) {
|
|
85317
|
-
const fullPath =
|
|
85219
|
+
const fullPath = join63(matrixxDir, name);
|
|
85318
85220
|
let size = "?";
|
|
85319
85221
|
let mtime = "?";
|
|
85320
85222
|
try {
|
|
85321
|
-
const stat2 =
|
|
85223
|
+
const stat2 = statSync12(fullPath);
|
|
85322
85224
|
size = `${stat2.size}B`;
|
|
85323
85225
|
mtime = stat2.mtime.toISOString();
|
|
85324
85226
|
} catch {}
|
|
@@ -85538,9 +85440,9 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
|
|
|
85538
85440
|
}
|
|
85539
85441
|
});
|
|
85540
85442
|
// src/tools/pdf-extract-figures/tools.ts
|
|
85541
|
-
import { existsSync as
|
|
85443
|
+
import { existsSync as existsSync61, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
|
|
85542
85444
|
import { tmpdir as tmpdir5 } from "os";
|
|
85543
|
-
import { join as
|
|
85445
|
+
import { join as join64 } from "path";
|
|
85544
85446
|
var PYTHON_SCRIPT = `#!/usr/bin/env python3
|
|
85545
85447
|
import sys
|
|
85546
85448
|
import json
|
|
@@ -85642,7 +85544,7 @@ function validateArgs(args) {
|
|
|
85642
85544
|
if (!args.file_path) {
|
|
85643
85545
|
return "Error: 'file_path' is required.";
|
|
85644
85546
|
}
|
|
85645
|
-
if (!
|
|
85547
|
+
if (!existsSync61(args.file_path)) {
|
|
85646
85548
|
return `Error: File not found: ${args.file_path}`;
|
|
85647
85549
|
}
|
|
85648
85550
|
if (!args.file_path.toLowerCase().endsWith(".pdf")) {
|
|
@@ -85691,11 +85593,11 @@ async function runExtraction(args) {
|
|
|
85691
85593
|
}
|
|
85692
85594
|
let tmpDir;
|
|
85693
85595
|
try {
|
|
85694
|
-
tmpDir = mkdtempSync(
|
|
85596
|
+
tmpDir = mkdtempSync(join64(tmpdir5(), "matrixx-pdf-extract-"));
|
|
85695
85597
|
} catch {
|
|
85696
85598
|
return "Error: Could not create temporary directory.";
|
|
85697
85599
|
}
|
|
85698
|
-
const scriptPath =
|
|
85600
|
+
const scriptPath = join64(tmpDir, "extract_pdf_figures.py");
|
|
85699
85601
|
writeFileSync23(scriptPath, PYTHON_SCRIPT);
|
|
85700
85602
|
const payload = buildArgsPayload(args);
|
|
85701
85603
|
log("[pdf_extract_figures] Running extraction", { file_path: args.file_path, payload });
|
|
@@ -85743,11 +85645,11 @@ function createPdfExtractFiguresTool() {
|
|
|
85743
85645
|
return { pdf_extract_figures };
|
|
85744
85646
|
}
|
|
85745
85647
|
// src/tools/plan/plan-create.ts
|
|
85746
|
-
import { existsSync as
|
|
85648
|
+
import { existsSync as existsSync62 } from "fs";
|
|
85747
85649
|
import { basename as basename7 } from "path";
|
|
85748
85650
|
|
|
85749
85651
|
// src/tools/plan/types.ts
|
|
85750
|
-
import { basename as basename6, isAbsolute as isAbsolute5, join as
|
|
85652
|
+
import { basename as basename6, isAbsolute as isAbsolute5, join as join65, relative as relative4, resolve as resolve8 } from "path";
|
|
85751
85653
|
|
|
85752
85654
|
// src/tools/plan/constants.ts
|
|
85753
85655
|
var PLANS_DIR3 = ".matrixx/plans";
|
|
@@ -85773,8 +85675,8 @@ function validatePlanFilePath(filePath, directory) {
|
|
|
85773
85675
|
if (!PLAN_FILENAME_KEBAB_REGEX.test(base)) {
|
|
85774
85676
|
return { error: `fileName must match kebab-case ^[a-z0-9-]+\\.md$: ${base}` };
|
|
85775
85677
|
}
|
|
85776
|
-
const resolved = isAbsolute5(filePath) ?
|
|
85777
|
-
const plansDir =
|
|
85678
|
+
const resolved = isAbsolute5(filePath) ? resolve8(filePath) : resolve8(join65(directory, filePath));
|
|
85679
|
+
const plansDir = resolve8(join65(directory, PLANS_DIR3));
|
|
85778
85680
|
const relToPlans = relative4(plansDir, resolved);
|
|
85779
85681
|
if (relToPlans.startsWith("..") || isAbsolute5(relToPlans)) {
|
|
85780
85682
|
return { error: `filePath must be inside ${PLANS_DIR3}: ${filePath}` };
|
|
@@ -85817,7 +85719,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
|
|
|
85817
85719
|
}
|
|
85818
85720
|
const resolved = validation.resolved;
|
|
85819
85721
|
ensurePlanDir(directory);
|
|
85820
|
-
if (
|
|
85722
|
+
if (existsSync62(resolved)) {
|
|
85821
85723
|
return JSON.stringify({
|
|
85822
85724
|
error: "file_exists",
|
|
85823
85725
|
message: `Plan file already exists: ${resolved}. Use plan_read + plan_update to modify.`,
|
|
@@ -85848,7 +85750,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
|
|
|
85848
85750
|
});
|
|
85849
85751
|
}
|
|
85850
85752
|
// src/tools/plan/plan-delete.ts
|
|
85851
|
-
import { existsSync as
|
|
85753
|
+
import { existsSync as existsSync63, unlinkSync as unlinkSync10 } from "fs";
|
|
85852
85754
|
function createPlanDeleteTool(ctx) {
|
|
85853
85755
|
return tool({
|
|
85854
85756
|
description: `Delete a plan file under .matrixx/plans/*.md. Scoped unlink inside PLANS_DIR, refuses outside paths.`,
|
|
@@ -85864,7 +85766,7 @@ function createPlanDeleteTool(ctx) {
|
|
|
85864
85766
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
85865
85767
|
}
|
|
85866
85768
|
const resolved = validation.resolved;
|
|
85867
|
-
if (!
|
|
85769
|
+
if (!existsSync63(resolved)) {
|
|
85868
85770
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
85869
85771
|
}
|
|
85870
85772
|
unlinkSync10(resolved);
|
|
@@ -85877,8 +85779,8 @@ function createPlanDeleteTool(ctx) {
|
|
|
85877
85779
|
});
|
|
85878
85780
|
}
|
|
85879
85781
|
// src/tools/plan/plan-list.ts
|
|
85880
|
-
import { existsSync as
|
|
85881
|
-
import { join as
|
|
85782
|
+
import { existsSync as existsSync64, readdirSync as readdirSync20, statSync as statSync13 } from "fs";
|
|
85783
|
+
import { join as join66 } from "path";
|
|
85882
85784
|
function createPlanListTool(ctx) {
|
|
85883
85785
|
return tool({
|
|
85884
85786
|
description: `List plan files under .matrixx/plans/*.md. Filters *.md with kebab-case, sorts by mtime.`,
|
|
@@ -85886,15 +85788,15 @@ function createPlanListTool(ctx) {
|
|
|
85886
85788
|
execute: async (_args, context) => {
|
|
85887
85789
|
try {
|
|
85888
85790
|
const directory = resolveDirectory(context?.directory, ctx?.directory);
|
|
85889
|
-
const plansDir =
|
|
85890
|
-
if (!
|
|
85791
|
+
const plansDir = join66(directory, PLANS_DIR3);
|
|
85792
|
+
if (!existsSync64(plansDir)) {
|
|
85891
85793
|
return JSON.stringify({ plans: [] });
|
|
85892
85794
|
}
|
|
85893
85795
|
const entries = readdirSync20(plansDir).filter((f) => f.endsWith(".md") && PLAN_FILENAME_KEBAB_REGEX.test(f));
|
|
85894
85796
|
const plans = entries.map((fileName) => {
|
|
85895
|
-
const filePath =
|
|
85797
|
+
const filePath = join66(plansDir, fileName);
|
|
85896
85798
|
try {
|
|
85897
|
-
const stat2 =
|
|
85799
|
+
const stat2 = statSync13(filePath);
|
|
85898
85800
|
return {
|
|
85899
85801
|
fileName,
|
|
85900
85802
|
filePath,
|
|
@@ -85915,7 +85817,7 @@ function createPlanListTool(ctx) {
|
|
|
85915
85817
|
});
|
|
85916
85818
|
}
|
|
85917
85819
|
// src/tools/plan/plan-read.ts
|
|
85918
|
-
import { existsSync as
|
|
85820
|
+
import { existsSync as existsSync65, statSync as statSync14 } from "fs";
|
|
85919
85821
|
function createPlanReadTool(ctx) {
|
|
85920
85822
|
return tool({
|
|
85921
85823
|
description: `Read a plan file from .matrixx/plans/*.md with hashline-tagged output (1#AB|content) + lsp-style. Enforces scoping and ${MAX_PLAN_FILE_BYTES2} byte cap.`,
|
|
@@ -85931,11 +85833,11 @@ function createPlanReadTool(ctx) {
|
|
|
85931
85833
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
85932
85834
|
}
|
|
85933
85835
|
const resolved = validation.resolved;
|
|
85934
|
-
if (!
|
|
85836
|
+
if (!existsSync65(resolved)) {
|
|
85935
85837
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
85936
85838
|
}
|
|
85937
85839
|
try {
|
|
85938
|
-
const stat2 =
|
|
85840
|
+
const stat2 = statSync14(resolved);
|
|
85939
85841
|
if (stat2.size > MAX_PLAN_FILE_BYTES2) {
|
|
85940
85842
|
return JSON.stringify({
|
|
85941
85843
|
error: "file_too_large",
|
|
@@ -85963,7 +85865,7 @@ function createPlanReadTool(ctx) {
|
|
|
85963
85865
|
});
|
|
85964
85866
|
}
|
|
85965
85867
|
// src/tools/plan/plan-update.ts
|
|
85966
|
-
import { existsSync as
|
|
85868
|
+
import { existsSync as existsSync66 } from "fs";
|
|
85967
85869
|
|
|
85968
85870
|
// src/features/tool-metadata-store/store.ts
|
|
85969
85871
|
var pendingStore = new Map;
|
|
@@ -87026,7 +86928,7 @@ function createPlanUpdateTool(ctx) {
|
|
|
87026
86928
|
return JSON.stringify({ error: "invalid_file_path", message: validation.error });
|
|
87027
86929
|
}
|
|
87028
86930
|
const resolved = validation.resolved;
|
|
87029
|
-
if (!
|
|
86931
|
+
if (!existsSync66(resolved)) {
|
|
87030
86932
|
return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
|
|
87031
86933
|
}
|
|
87032
86934
|
const result = await executeHashlineEditTool({ filePath: resolved, edits }, context, ctx);
|
|
@@ -87039,9 +86941,9 @@ function createPlanUpdateTool(ctx) {
|
|
|
87039
86941
|
});
|
|
87040
86942
|
}
|
|
87041
86943
|
// src/tools/preset/tools.ts
|
|
87042
|
-
import { existsSync as
|
|
86944
|
+
import { existsSync as existsSync67, mkdirSync as mkdirSync18, readFileSync as readFileSync41, writeFileSync as writeFileSync24 } from "fs";
|
|
87043
86945
|
import { homedir as homedir10 } from "os";
|
|
87044
|
-
import { dirname as
|
|
86946
|
+
import { dirname as dirname15, join as join67 } from "path";
|
|
87045
86947
|
|
|
87046
86948
|
// src/features/preset-state/manager.ts
|
|
87047
86949
|
init_logger();
|
|
@@ -87060,15 +86962,15 @@ var ACTIONS = ["list", "show", "set"];
|
|
|
87060
86962
|
var SCOPES = ["global", "project"];
|
|
87061
86963
|
function resolveConfigPath(scope, directory) {
|
|
87062
86964
|
if (scope === "global") {
|
|
87063
|
-
return
|
|
86965
|
+
return join67(homedir10(), ".config", "opencode", "matrixx.jsonc");
|
|
87064
86966
|
}
|
|
87065
|
-
return
|
|
86967
|
+
return join67(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
|
|
87066
86968
|
}
|
|
87067
86969
|
function persistActivePreset(presetName, scope, directory) {
|
|
87068
86970
|
const configPath = resolveConfigPath(scope, directory);
|
|
87069
86971
|
let raw = {};
|
|
87070
|
-
if (
|
|
87071
|
-
const content =
|
|
86972
|
+
if (existsSync67(configPath)) {
|
|
86973
|
+
const content = readFileSync41(configPath, "utf-8");
|
|
87072
86974
|
const parsed = parseJsoncSafe(content);
|
|
87073
86975
|
if (parsed.errors.length > 0) {
|
|
87074
86976
|
return {
|
|
@@ -87078,7 +86980,7 @@ function persistActivePreset(presetName, scope, directory) {
|
|
|
87078
86980
|
}
|
|
87079
86981
|
raw = parsed.data ?? {};
|
|
87080
86982
|
} else {
|
|
87081
|
-
mkdirSync18(
|
|
86983
|
+
mkdirSync18(dirname15(configPath), { recursive: true });
|
|
87082
86984
|
}
|
|
87083
86985
|
raw.active_preset = presetName;
|
|
87084
86986
|
writeFileSync24(configPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -87181,9 +87083,9 @@ function createPresetTool(options) {
|
|
|
87181
87083
|
return { preset };
|
|
87182
87084
|
}
|
|
87183
87085
|
// src/tools/session-manager/constants.ts
|
|
87184
|
-
import { join as
|
|
87185
|
-
var TODO_DIR =
|
|
87186
|
-
var TRANSCRIPT_DIR2 =
|
|
87086
|
+
import { join as join68 } from "path";
|
|
87087
|
+
var TODO_DIR = join68(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
|
|
87088
|
+
var TRANSCRIPT_DIR2 = join68(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
|
|
87187
87089
|
var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
|
|
87188
87090
|
|
|
87189
87091
|
Returns a list of available session IDs with metadata including message count, date range, and agents used.
|
|
@@ -87255,9 +87157,9 @@ Agents Used: build, oracle, librarian
|
|
|
87255
87157
|
Has Todos: Yes (12 items, 8 completed)
|
|
87256
87158
|
Has Transcript: Yes (234 entries)`;
|
|
87257
87159
|
// src/tools/session-manager/storage.ts
|
|
87258
|
-
import { existsSync as
|
|
87160
|
+
import { existsSync as existsSync68 } from "fs";
|
|
87259
87161
|
import { readdir, readFile as readFile3 } from "fs/promises";
|
|
87260
|
-
import { join as
|
|
87162
|
+
import { join as join69 } from "path";
|
|
87261
87163
|
var sdkClient = null;
|
|
87262
87164
|
function setStorageClient(client2) {
|
|
87263
87165
|
sdkClient = client2;
|
|
@@ -87276,7 +87178,7 @@ async function getMainSessions(options) {
|
|
|
87276
87178
|
return [];
|
|
87277
87179
|
}
|
|
87278
87180
|
}
|
|
87279
|
-
if (!
|
|
87181
|
+
if (!existsSync68(SESSION_STORAGE))
|
|
87280
87182
|
return [];
|
|
87281
87183
|
const sessions = [];
|
|
87282
87184
|
try {
|
|
@@ -87284,13 +87186,13 @@ async function getMainSessions(options) {
|
|
|
87284
87186
|
for (const projectDir of projectDirs) {
|
|
87285
87187
|
if (!projectDir.isDirectory())
|
|
87286
87188
|
continue;
|
|
87287
|
-
const projectPath =
|
|
87189
|
+
const projectPath = join69(SESSION_STORAGE, projectDir.name);
|
|
87288
87190
|
const sessionFiles = await readdir(projectPath);
|
|
87289
87191
|
for (const file3 of sessionFiles) {
|
|
87290
87192
|
if (!file3.endsWith(".json"))
|
|
87291
87193
|
continue;
|
|
87292
87194
|
try {
|
|
87293
|
-
const content = await readFile3(
|
|
87195
|
+
const content = await readFile3(join69(projectPath, file3), "utf-8");
|
|
87294
87196
|
const meta3 = JSON.parse(content);
|
|
87295
87197
|
if (meta3.parentID)
|
|
87296
87198
|
continue;
|
|
@@ -87315,7 +87217,7 @@ async function getAllSessions() {
|
|
|
87315
87217
|
return [];
|
|
87316
87218
|
}
|
|
87317
87219
|
}
|
|
87318
|
-
if (!
|
|
87220
|
+
if (!existsSync68(MESSAGE_STORAGE))
|
|
87319
87221
|
return [];
|
|
87320
87222
|
const sessions = [];
|
|
87321
87223
|
async function scanDirectory(dir) {
|
|
@@ -87323,7 +87225,7 @@ async function getAllSessions() {
|
|
|
87323
87225
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
87324
87226
|
for (const entry of entries) {
|
|
87325
87227
|
if (entry.isDirectory()) {
|
|
87326
|
-
const sessionPath =
|
|
87228
|
+
const sessionPath = join69(dir, entry.name);
|
|
87327
87229
|
const files = await readdir(sessionPath);
|
|
87328
87230
|
if (files.some((f) => f.endsWith(".json"))) {
|
|
87329
87231
|
sessions.push(entry.name);
|
|
@@ -87384,7 +87286,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87384
87286
|
}
|
|
87385
87287
|
}
|
|
87386
87288
|
const messageDir = getMessageDir(sessionID);
|
|
87387
|
-
if (!messageDir || !
|
|
87289
|
+
if (!messageDir || !existsSync68(messageDir))
|
|
87388
87290
|
return [];
|
|
87389
87291
|
const messages2 = [];
|
|
87390
87292
|
try {
|
|
@@ -87393,7 +87295,7 @@ async function readSessionMessages(sessionID) {
|
|
|
87393
87295
|
if (!file3.endsWith(".json"))
|
|
87394
87296
|
continue;
|
|
87395
87297
|
try {
|
|
87396
|
-
const content = await readFile3(
|
|
87298
|
+
const content = await readFile3(join69(messageDir, file3), "utf-8");
|
|
87397
87299
|
const meta3 = JSON.parse(content);
|
|
87398
87300
|
const parts = await readParts2(meta3.id);
|
|
87399
87301
|
messages2.push({
|
|
@@ -87417,8 +87319,8 @@ async function readSessionMessages(sessionID) {
|
|
|
87417
87319
|
});
|
|
87418
87320
|
}
|
|
87419
87321
|
async function readParts2(messageID) {
|
|
87420
|
-
const partDir =
|
|
87421
|
-
if (!
|
|
87322
|
+
const partDir = join69(PART_STORAGE, messageID);
|
|
87323
|
+
if (!existsSync68(partDir))
|
|
87422
87324
|
return [];
|
|
87423
87325
|
const parts = [];
|
|
87424
87326
|
try {
|
|
@@ -87427,7 +87329,7 @@ async function readParts2(messageID) {
|
|
|
87427
87329
|
if (!file3.endsWith(".json"))
|
|
87428
87330
|
continue;
|
|
87429
87331
|
try {
|
|
87430
|
-
const content = await readFile3(
|
|
87332
|
+
const content = await readFile3(join69(partDir, file3), "utf-8");
|
|
87431
87333
|
parts.push(JSON.parse(content));
|
|
87432
87334
|
} catch {}
|
|
87433
87335
|
}
|
|
@@ -87451,14 +87353,14 @@ async function readSessionTodos(sessionID) {
|
|
|
87451
87353
|
return [];
|
|
87452
87354
|
}
|
|
87453
87355
|
}
|
|
87454
|
-
if (!
|
|
87356
|
+
if (!existsSync68(TODO_DIR))
|
|
87455
87357
|
return [];
|
|
87456
87358
|
try {
|
|
87457
87359
|
const allFiles = await readdir(TODO_DIR);
|
|
87458
87360
|
const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
|
|
87459
87361
|
for (const file3 of todoFiles) {
|
|
87460
87362
|
try {
|
|
87461
|
-
const content = await readFile3(
|
|
87363
|
+
const content = await readFile3(join69(TODO_DIR, file3), "utf-8");
|
|
87462
87364
|
const data = JSON.parse(content);
|
|
87463
87365
|
if (Array.isArray(data)) {
|
|
87464
87366
|
return data.map((item) => ({
|
|
@@ -87476,10 +87378,10 @@ async function readSessionTodos(sessionID) {
|
|
|
87476
87378
|
return [];
|
|
87477
87379
|
}
|
|
87478
87380
|
async function readSessionTranscript(sessionID) {
|
|
87479
|
-
if (!
|
|
87381
|
+
if (!existsSync68(TRANSCRIPT_DIR2))
|
|
87480
87382
|
return 0;
|
|
87481
|
-
const transcriptFile =
|
|
87482
|
-
if (!
|
|
87383
|
+
const transcriptFile = join69(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
|
|
87384
|
+
if (!existsSync68(transcriptFile))
|
|
87483
87385
|
return 0;
|
|
87484
87386
|
try {
|
|
87485
87387
|
const content = await readFile3(transcriptFile, "utf-8");
|
|
@@ -87979,20 +87881,20 @@ function createSkillTool(options = {}) {
|
|
|
87979
87881
|
}
|
|
87980
87882
|
var skill = createSkillTool();
|
|
87981
87883
|
// src/tools/slashcommand/command-discovery.ts
|
|
87982
|
-
import { existsSync as
|
|
87983
|
-
import { basename as basename8, join as
|
|
87884
|
+
import { existsSync as existsSync69, readdirSync as readdirSync21, readFileSync as readFileSync42 } from "fs";
|
|
87885
|
+
import { basename as basename8, join as join70 } from "path";
|
|
87984
87886
|
function discoverCommandsFromDir2(commandsDir, scope) {
|
|
87985
|
-
if (!
|
|
87887
|
+
if (!existsSync69(commandsDir))
|
|
87986
87888
|
return [];
|
|
87987
87889
|
const entries = readdirSync21(commandsDir, { withFileTypes: true });
|
|
87988
87890
|
const commands2 = [];
|
|
87989
87891
|
for (const entry of entries) {
|
|
87990
87892
|
if (!isMarkdownFile(entry))
|
|
87991
87893
|
continue;
|
|
87992
|
-
const commandPath =
|
|
87894
|
+
const commandPath = join70(commandsDir, entry.name);
|
|
87993
87895
|
const commandName = basename8(entry.name, ".md");
|
|
87994
87896
|
try {
|
|
87995
|
-
const content =
|
|
87897
|
+
const content = readFileSync42(commandPath, "utf-8");
|
|
87996
87898
|
const { data, body } = parseFrontmatter(content);
|
|
87997
87899
|
const isOpencodeSource = scope === "opencode" || scope === "opencode-project";
|
|
87998
87900
|
const metadata = {
|
|
@@ -88016,8 +87918,8 @@ function discoverCommandsFromDir2(commandsDir, scope) {
|
|
|
88016
87918
|
}
|
|
88017
87919
|
function discoverCommandsSync(directory) {
|
|
88018
87920
|
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
|
88019
|
-
const opencodeGlobalDir =
|
|
88020
|
-
const opencodeProjectDir =
|
|
87921
|
+
const opencodeGlobalDir = join70(configDir, "command");
|
|
87922
|
+
const opencodeProjectDir = join70(directory ?? process.cwd(), ".opencode", "command");
|
|
88021
87923
|
const opencodeGlobalCommands = discoverCommandsFromDir2(opencodeGlobalDir, "opencode");
|
|
88022
87924
|
const opencodeProjectCommands = discoverCommandsFromDir2(opencodeProjectDir, "opencode-project");
|
|
88023
87925
|
const builtinCommandsMap = loadBuiltinCommands();
|
|
@@ -88041,7 +87943,7 @@ function discoverCommandsSync(directory) {
|
|
|
88041
87943
|
];
|
|
88042
87944
|
}
|
|
88043
87945
|
// src/tools/slashcommand/command-output-formatter.ts
|
|
88044
|
-
import { dirname as
|
|
87946
|
+
import { dirname as dirname16 } from "path";
|
|
88045
87947
|
async function formatLoadedCommand(command, userMessage) {
|
|
88046
87948
|
const sections = [];
|
|
88047
87949
|
sections.push(`# /${command.name} Command
|
|
@@ -88080,7 +87982,7 @@ async function formatLoadedCommand(command, userMessage) {
|
|
|
88080
87982
|
if (!content && command.lazyContentLoader) {
|
|
88081
87983
|
content = await command.lazyContentLoader.load();
|
|
88082
87984
|
}
|
|
88083
|
-
const commandDir = command.path ?
|
|
87985
|
+
const commandDir = command.path ? dirname16(command.path) : process.cwd();
|
|
88084
87986
|
const withFileReferences = await resolveFileReferencesInText(content, commandDir);
|
|
88085
87987
|
const resolvedContent = await resolveCommandsInText(withFileReferences);
|
|
88086
87988
|
let finalContent = resolvedContent.trim();
|
|
@@ -88821,11 +88723,11 @@ function createBackgroundOutput(manager, client2) {
|
|
|
88821
88723
|
});
|
|
88822
88724
|
}
|
|
88823
88725
|
// src/tools/background-task/create-background-revive.ts
|
|
88824
|
-
import { existsSync as
|
|
88726
|
+
import { existsSync as existsSync71 } from "fs";
|
|
88825
88727
|
|
|
88826
88728
|
// src/features/background-agent/handle-index.ts
|
|
88827
|
-
import { existsSync as
|
|
88828
|
-
import { dirname as
|
|
88729
|
+
import { existsSync as existsSync70, mkdirSync as mkdirSync19, readdirSync as readdirSync22, readFileSync as readFileSync43, renameSync as renameSync5, unlinkSync as unlinkSync11, writeFileSync as writeFileSync25 } from "fs";
|
|
88730
|
+
import { dirname as dirname17, join as join71 } from "path";
|
|
88829
88731
|
|
|
88830
88732
|
// src/features/background-agent/constants.ts
|
|
88831
88733
|
var TASK_TTL_MS = 30 * 60 * 1000;
|
|
@@ -88876,13 +88778,13 @@ var BgHandleSchema = exports_external.object({
|
|
|
88876
88778
|
completedAt: exports_external.number().optional()
|
|
88877
88779
|
}).strict();
|
|
88878
88780
|
function getBgHandleDir(directory) {
|
|
88879
|
-
return
|
|
88781
|
+
return join71(directory, ".matrixx", BG_HANDLE_DIR_NAME);
|
|
88880
88782
|
}
|
|
88881
88783
|
function getHandlePath(directory, taskId) {
|
|
88882
|
-
return
|
|
88784
|
+
return join71(getBgHandleDir(directory), `${taskId}.json`);
|
|
88883
88785
|
}
|
|
88884
88786
|
function ensureDir3(dirPath) {
|
|
88885
|
-
if (!
|
|
88787
|
+
if (!existsSync70(dirPath)) {
|
|
88886
88788
|
mkdirSync19(dirPath, { recursive: true });
|
|
88887
88789
|
}
|
|
88888
88790
|
}
|
|
@@ -88906,14 +88808,14 @@ function toHandle(task) {
|
|
|
88906
88808
|
}
|
|
88907
88809
|
function writeHandle(directory, task) {
|
|
88908
88810
|
const filePath = getHandlePath(directory, task.id);
|
|
88909
|
-
ensureDir3(
|
|
88811
|
+
ensureDir3(dirname17(filePath));
|
|
88910
88812
|
const tempPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
88911
88813
|
try {
|
|
88912
88814
|
writeFileSync25(tempPath, JSON.stringify(toHandle(task), null, 2), "utf-8");
|
|
88913
88815
|
renameSync5(tempPath, filePath);
|
|
88914
88816
|
} catch (error95) {
|
|
88915
88817
|
try {
|
|
88916
|
-
if (
|
|
88818
|
+
if (existsSync70(tempPath)) {
|
|
88917
88819
|
unlinkSync11(tempPath);
|
|
88918
88820
|
}
|
|
88919
88821
|
} catch {}
|
|
@@ -88922,10 +88824,10 @@ function writeHandle(directory, task) {
|
|
|
88922
88824
|
}
|
|
88923
88825
|
function readHandleFile(filePath) {
|
|
88924
88826
|
try {
|
|
88925
|
-
if (!
|
|
88827
|
+
if (!existsSync70(filePath)) {
|
|
88926
88828
|
return null;
|
|
88927
88829
|
}
|
|
88928
|
-
const parsed = JSON.parse(
|
|
88830
|
+
const parsed = JSON.parse(readFileSync43(filePath, "utf-8"));
|
|
88929
88831
|
const result = BgHandleSchema.safeParse(parsed);
|
|
88930
88832
|
return result.success ? result.data : null;
|
|
88931
88833
|
} catch {
|
|
@@ -88934,7 +88836,7 @@ function readHandleFile(filePath) {
|
|
|
88934
88836
|
}
|
|
88935
88837
|
function readHandles(directory) {
|
|
88936
88838
|
const dir = getBgHandleDir(directory);
|
|
88937
|
-
if (!
|
|
88839
|
+
if (!existsSync70(dir)) {
|
|
88938
88840
|
return [];
|
|
88939
88841
|
}
|
|
88940
88842
|
const handles = [];
|
|
@@ -88942,7 +88844,7 @@ function readHandles(directory) {
|
|
|
88942
88844
|
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
88943
88845
|
continue;
|
|
88944
88846
|
}
|
|
88945
|
-
const handle = readHandleFile(
|
|
88847
|
+
const handle = readHandleFile(join71(dir, name));
|
|
88946
88848
|
if (handle) {
|
|
88947
88849
|
handles.push(handle);
|
|
88948
88850
|
}
|
|
@@ -88951,7 +88853,7 @@ function readHandles(directory) {
|
|
|
88951
88853
|
}
|
|
88952
88854
|
function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
|
|
88953
88855
|
const dir = getBgHandleDir(directory);
|
|
88954
|
-
if (!
|
|
88856
|
+
if (!existsSync70(dir)) {
|
|
88955
88857
|
return 0;
|
|
88956
88858
|
}
|
|
88957
88859
|
let removed = 0;
|
|
@@ -88959,7 +88861,7 @@ function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
|
|
|
88959
88861
|
if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
|
|
88960
88862
|
continue;
|
|
88961
88863
|
}
|
|
88962
|
-
const filePath =
|
|
88864
|
+
const filePath = join71(dir, name);
|
|
88963
88865
|
const handle = readHandleFile(filePath);
|
|
88964
88866
|
const timestamp = handle?.completedAt ?? handle?.startedAt ?? handle?.queuedAt;
|
|
88965
88867
|
if (handle && timestamp !== undefined && now - timestamp <= ttlMs) {
|
|
@@ -89027,7 +88929,7 @@ Revive one with background_revive(taskId="<id>", prompt="<new instruction>").`;
|
|
|
89027
88929
|
}
|
|
89028
88930
|
function resolveFailure(identifier, message, directory) {
|
|
89029
88931
|
if (message.includes("not found for revive")) {
|
|
89030
|
-
const handleOnDisk =
|
|
88932
|
+
const handleOnDisk = existsSync71(getHandlePath(directory, identifier));
|
|
89031
88933
|
return { reason: handleOnDisk ? "unknown-task" : "expired", detail: message };
|
|
89032
88934
|
}
|
|
89033
88935
|
if (message.includes("still active"))
|
|
@@ -89442,9 +89344,9 @@ function createAssemblyTool(options) {
|
|
|
89442
89344
|
});
|
|
89443
89345
|
}
|
|
89444
89346
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89445
|
-
import { existsSync as
|
|
89347
|
+
import { existsSync as existsSync72, writeFileSync as writeFileSync26 } from "fs";
|
|
89446
89348
|
import { homedir as homedir11 } from "os";
|
|
89447
|
-
import { join as
|
|
89349
|
+
import { join as join72 } from "path";
|
|
89448
89350
|
|
|
89449
89351
|
// src/config/schema/dcp.ts
|
|
89450
89352
|
var DcpCompressOverrideSchema = exports_external.object({
|
|
@@ -89644,11 +89546,11 @@ var DcpConfigSchema = exports_external.object({
|
|
|
89644
89546
|
});
|
|
89645
89547
|
|
|
89646
89548
|
// src/tools/dcp-switch-profile/tools.ts
|
|
89647
|
-
var DCP_PLUGIN_DIR =
|
|
89648
|
-
var DCP_SYMLINK =
|
|
89549
|
+
var DCP_PLUGIN_DIR = join72(homedir11(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
|
|
89550
|
+
var DCP_SYMLINK = join72(homedir11(), ".config", "opencode", "dcp.jsonc");
|
|
89649
89551
|
var VALID_PROFILES = ["economy", "balanced", "performance", "ultimate", "brutal"];
|
|
89650
89552
|
function checkDcpInstalled() {
|
|
89651
|
-
if (!
|
|
89553
|
+
if (!existsSync72(DCP_PLUGIN_DIR)) {
|
|
89652
89554
|
return `DCP is not installed at ${DCP_PLUGIN_DIR}. Install it with: npm install --prefix ~/.config/opencode @tarquinen/opencode-dcp`;
|
|
89653
89555
|
}
|
|
89654
89556
|
return null;
|
|
@@ -90571,7 +90473,7 @@ async function executeBackgroundTask(args, ctx, executorCtx, parentContext, agen
|
|
|
90571
90473
|
|
|
90572
90474
|
Task ID: ${task.id}`;
|
|
90573
90475
|
}
|
|
90574
|
-
await new Promise((
|
|
90476
|
+
await new Promise((resolve9) => setTimeout(resolve9, timing.WAIT_FOR_SESSION_INTERVAL_MS));
|
|
90575
90477
|
const updated = manager.getTask(task.id);
|
|
90576
90478
|
if (updated && isLaunchTerminalStatus(updated.status)) {
|
|
90577
90479
|
if (isQueueSaturated(updated.status, updated.terminalReason)) {
|
|
@@ -91106,10 +91008,11 @@ Create the work plan directly - that's your job as the planning agent.`
|
|
|
91106
91008
|
if (!matchedAgent) {
|
|
91107
91009
|
const isPrimaryAgent = agents.filter((a) => a.mode === "primary").find((agent) => agent.name.toLowerCase() === agentToUse.toLowerCase() || agent.name.toLowerCase() === resolvedDisplayName.toLowerCase());
|
|
91108
91010
|
if (isPrimaryAgent) {
|
|
91011
|
+
const architectHint = isPrimaryAgent.name.toLowerCase() === "architect" ? " Use /start-work to run Architect as session agent (issue #111 option b)." : "";
|
|
91109
91012
|
return {
|
|
91110
91013
|
agentToUse: "",
|
|
91111
91014
|
categoryModel: undefined,
|
|
91112
|
-
error: `Cannot call primary agent "${isPrimaryAgent.name}" via task. Primary agents are top-level orchestrators
|
|
91015
|
+
error: `Cannot call primary agent "${isPrimaryAgent.name}" via task. Primary agents are top-level orchestrators.${architectHint}`
|
|
91113
91016
|
};
|
|
91114
91017
|
}
|
|
91115
91018
|
const availableAgents = callableAgents.map((a) => a.name).sort().join(", ");
|
|
@@ -91536,7 +91439,7 @@ async function pollSyncSession(ctx, client2, input) {
|
|
|
91536
91439
|
|
|
91537
91440
|
Session ID: ${input.sessionID}`;
|
|
91538
91441
|
}
|
|
91539
|
-
await new Promise((
|
|
91442
|
+
await new Promise((resolve9) => setTimeout(resolve9, syncTiming.POLL_INTERVAL_MS));
|
|
91540
91443
|
pollCount++;
|
|
91541
91444
|
let statusResult;
|
|
91542
91445
|
try {
|
|
@@ -92083,7 +91986,7 @@ async function executeUnstableAgentTask(args, ctx, executorCtx, parentContext, a
|
|
|
92083
91986
|
|
|
92084
91987
|
Task ID: ${task.id}`;
|
|
92085
91988
|
}
|
|
92086
|
-
await new Promise((
|
|
91989
|
+
await new Promise((resolve9) => setTimeout(resolve9, timing.WAIT_FOR_SESSION_INTERVAL_MS));
|
|
92087
91990
|
const updated = manager.getTask(task.id);
|
|
92088
91991
|
sessionID = updated?.sessionID;
|
|
92089
91992
|
}
|
|
@@ -92124,7 +92027,7 @@ Task ID: ${task.id}`;
|
|
|
92124
92027
|
|
|
92125
92028
|
Session ID: ${sessionID}`;
|
|
92126
92029
|
}
|
|
92127
|
-
await new Promise((
|
|
92030
|
+
await new Promise((resolve9) => setTimeout(resolve9, timingCfg.POLL_INTERVAL_MS));
|
|
92128
92031
|
const currentTask = manager.getTask(task.id);
|
|
92129
92032
|
if (currentTask && (currentTask.status === "interrupt" || currentTask.status === "error" || currentTask.status === "cancelled")) {
|
|
92130
92033
|
terminalStatus = { status: currentTask.status, error: currentTask.error };
|
|
@@ -92768,8 +92671,8 @@ Original error: ${createResult.error}`;
|
|
|
92768
92671
|
});
|
|
92769
92672
|
}
|
|
92770
92673
|
// src/tools/task/task-cleanup.ts
|
|
92771
|
-
import { existsSync as
|
|
92772
|
-
import { join as
|
|
92674
|
+
import { existsSync as existsSync73, readdirSync as readdirSync23, readFileSync as readFileSync44, unlinkSync as unlinkSync12 } from "fs";
|
|
92675
|
+
import { join as join73 } from "path";
|
|
92773
92676
|
init_logger();
|
|
92774
92677
|
function parseOlderThan(value) {
|
|
92775
92678
|
const match = value.trim().match(/^(\d+)(d|h|m)$/);
|
|
@@ -92829,17 +92732,17 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92829
92732
|
}
|
|
92830
92733
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
92831
92734
|
const taskDir = getTaskDir(config4, directory);
|
|
92832
|
-
if (!
|
|
92735
|
+
if (!existsSync73(taskDir)) {
|
|
92833
92736
|
return JSON.stringify({ deleted: 0, remaining: 0, deletedIds: [] });
|
|
92834
92737
|
}
|
|
92835
92738
|
const files = readdirSync23(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
|
|
92836
92739
|
const total = files.length;
|
|
92837
92740
|
const deletedIds = [];
|
|
92838
92741
|
for (const file3 of files) {
|
|
92839
|
-
const filePath =
|
|
92742
|
+
const filePath = join73(taskDir, file3);
|
|
92840
92743
|
let raw;
|
|
92841
92744
|
try {
|
|
92842
|
-
const content =
|
|
92745
|
+
const content = readFileSync44(filePath, "utf-8");
|
|
92843
92746
|
raw = JSON.parse(content);
|
|
92844
92747
|
} catch {
|
|
92845
92748
|
continue;
|
|
@@ -92872,8 +92775,8 @@ Returns counts: {deleted, remaining, deletedIds}`,
|
|
|
92872
92775
|
});
|
|
92873
92776
|
}
|
|
92874
92777
|
// src/tools/task/task-create.ts
|
|
92875
|
-
import { readdirSync as readdirSync24, statSync as
|
|
92876
|
-
import { join as
|
|
92778
|
+
import { readdirSync as readdirSync24, statSync as statSync15 } from "fs";
|
|
92779
|
+
import { join as join74 } from "path";
|
|
92877
92780
|
init_logger();
|
|
92878
92781
|
function createTaskCreateTool(config4, ctx) {
|
|
92879
92782
|
return tool({
|
|
@@ -92942,7 +92845,7 @@ async function handleCreate2(args, config4, ctx, context) {
|
|
|
92942
92845
|
projectRoot: directory
|
|
92943
92846
|
};
|
|
92944
92847
|
const validatedTask = TaskObjectSchema.parse(task);
|
|
92945
|
-
writeJsonAtomic(
|
|
92848
|
+
writeJsonAtomic(join74(taskDir, `${taskId}.json`), validatedTask);
|
|
92946
92849
|
return JSON.stringify({
|
|
92947
92850
|
task: {
|
|
92948
92851
|
id: validatedTask.id,
|
|
@@ -92971,7 +92874,7 @@ function findDuplicateTask(taskDir, subject, directory) {
|
|
|
92971
92874
|
return null;
|
|
92972
92875
|
}
|
|
92973
92876
|
for (const file3 of files) {
|
|
92974
|
-
const filePath =
|
|
92877
|
+
const filePath = join74(taskDir, file3);
|
|
92975
92878
|
let existing;
|
|
92976
92879
|
try {
|
|
92977
92880
|
existing = readJsonSafe(filePath, TaskObjectSchema);
|
|
@@ -92990,7 +92893,7 @@ function findDuplicateTask(taskDir, subject, directory) {
|
|
|
92990
92893
|
continue;
|
|
92991
92894
|
let mtimeMs;
|
|
92992
92895
|
try {
|
|
92993
|
-
mtimeMs =
|
|
92896
|
+
mtimeMs = statSync15(filePath).mtimeMs;
|
|
92994
92897
|
} catch (error95) {
|
|
92995
92898
|
log(`[task-create] Failed to stat task file ${filePath} during dedup scan`, error95);
|
|
92996
92899
|
continue;
|
|
@@ -93002,7 +92905,7 @@ function findDuplicateTask(taskDir, subject, directory) {
|
|
|
93002
92905
|
return null;
|
|
93003
92906
|
}
|
|
93004
92907
|
// src/tools/task/task-get.ts
|
|
93005
|
-
import { join as
|
|
92908
|
+
import { join as join75 } from "path";
|
|
93006
92909
|
function parseTaskId(id) {
|
|
93007
92910
|
if (!TASK_ID_PATTERN.test(id))
|
|
93008
92911
|
return null;
|
|
@@ -93028,7 +92931,7 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
93028
92931
|
}
|
|
93029
92932
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
93030
92933
|
const taskDir = getTaskDir(config4, directory);
|
|
93031
|
-
const taskPath =
|
|
92934
|
+
const taskPath = join75(taskDir, `${taskId}.json`);
|
|
93032
92935
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
93033
92936
|
return JSON.stringify({ task: task ?? null });
|
|
93034
92937
|
} catch (error95) {
|
|
@@ -93041,8 +92944,8 @@ Returns null if the task does not exist or the file is invalid.`,
|
|
|
93041
92944
|
});
|
|
93042
92945
|
}
|
|
93043
92946
|
// src/tools/task/task-list.ts
|
|
93044
|
-
import { existsSync as
|
|
93045
|
-
import { join as
|
|
92947
|
+
import { existsSync as existsSync74, readdirSync as readdirSync25 } from "fs";
|
|
92948
|
+
import { join as join76 } from "path";
|
|
93046
92949
|
function createTaskList(config4, ctx) {
|
|
93047
92950
|
return tool({
|
|
93048
92951
|
description: `[TRACKING \u2014 local progress records only. Spawns nothing, executes nothing.]
|
|
@@ -93061,7 +92964,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
93061
92964
|
const parentID = args.parentID;
|
|
93062
92965
|
const directory = context?.directory ?? ctx?.directory ?? process.cwd();
|
|
93063
92966
|
const taskDir = getTaskDir(config4, directory);
|
|
93064
|
-
if (!
|
|
92967
|
+
if (!existsSync74(taskDir)) {
|
|
93065
92968
|
return JSON.stringify({ tasks: [] });
|
|
93066
92969
|
}
|
|
93067
92970
|
const files = readdirSync25(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
|
|
@@ -93070,7 +92973,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
93070
92973
|
}
|
|
93071
92974
|
const allTasks = [];
|
|
93072
92975
|
for (const fileId of files) {
|
|
93073
|
-
const task = readJsonSafe(
|
|
92976
|
+
const task = readJsonSafe(join76(taskDir, `${fileId}.json`), TaskObjectSchema);
|
|
93074
92977
|
if (task) {
|
|
93075
92978
|
allTasks.push(task);
|
|
93076
92979
|
}
|
|
@@ -93099,7 +93002,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
|
|
|
93099
93002
|
});
|
|
93100
93003
|
}
|
|
93101
93004
|
// src/tools/task/task-update.ts
|
|
93102
|
-
import { join as
|
|
93005
|
+
import { join as join77 } from "path";
|
|
93103
93006
|
function parseTaskId2(id) {
|
|
93104
93007
|
if (!TASK_ID_PATTERN.test(id))
|
|
93105
93008
|
return null;
|
|
@@ -93146,7 +93049,7 @@ async function handleUpdate(args, config4, ctx, context) {
|
|
|
93146
93049
|
return JSON.stringify({ error: "task_lock_unavailable" });
|
|
93147
93050
|
}
|
|
93148
93051
|
try {
|
|
93149
|
-
const taskPath =
|
|
93052
|
+
const taskPath = join77(taskDir, `${taskId}.json`);
|
|
93150
93053
|
const task = readJsonSafe(taskPath, TaskObjectSchema);
|
|
93151
93054
|
if (!task) {
|
|
93152
93055
|
return JSON.stringify({ error: "task_not_found" });
|
|
@@ -93682,7 +93585,7 @@ class ConcurrencyManager {
|
|
|
93682
93585
|
if (this.tryAcquireSlot(model)) {
|
|
93683
93586
|
return { granted: true };
|
|
93684
93587
|
}
|
|
93685
|
-
return new Promise((
|
|
93588
|
+
return new Promise((resolve9) => {
|
|
93686
93589
|
const queue = this.queues.get(model) ?? [];
|
|
93687
93590
|
let timer;
|
|
93688
93591
|
const entry = {
|
|
@@ -93694,7 +93597,7 @@ class ConcurrencyManager {
|
|
|
93694
93597
|
entry.settled = true;
|
|
93695
93598
|
if (timer !== undefined)
|
|
93696
93599
|
clearTimeout(timer);
|
|
93697
|
-
|
|
93600
|
+
resolve9(outcome);
|
|
93698
93601
|
}
|
|
93699
93602
|
};
|
|
93700
93603
|
queue.push(entry);
|
|
@@ -93711,7 +93614,7 @@ class ConcurrencyManager {
|
|
|
93711
93614
|
if (index !== -1)
|
|
93712
93615
|
pending.splice(index, 1);
|
|
93713
93616
|
}
|
|
93714
|
-
|
|
93617
|
+
resolve9({ granted: false, reason: "timeout", waitedMs: Date.now() - entry.enqueuedAt });
|
|
93715
93618
|
}, timeoutMs);
|
|
93716
93619
|
});
|
|
93717
93620
|
}
|
|
@@ -93910,19 +93813,19 @@ function detectRepetitiveToolUse(window2) {
|
|
|
93910
93813
|
}
|
|
93911
93814
|
|
|
93912
93815
|
// src/features/background-agent/message-dir.ts
|
|
93913
|
-
import { existsSync as
|
|
93914
|
-
import { join as
|
|
93816
|
+
import { existsSync as existsSync75, readdirSync as readdirSync26, readFileSync as readFileSync45 } from "fs";
|
|
93817
|
+
import { join as join78 } from "path";
|
|
93915
93818
|
var MESSAGE_DIR_CACHE_CAP = 1000;
|
|
93916
93819
|
var messageDirCache = new Map;
|
|
93917
93820
|
function computeMessageDir(sessionID) {
|
|
93918
|
-
if (!
|
|
93821
|
+
if (!existsSync75(MESSAGE_STORAGE))
|
|
93919
93822
|
return null;
|
|
93920
|
-
const directPath =
|
|
93921
|
-
if (
|
|
93823
|
+
const directPath = join78(MESSAGE_STORAGE, sessionID);
|
|
93824
|
+
if (existsSync75(directPath))
|
|
93922
93825
|
return directPath;
|
|
93923
93826
|
for (const dir of readdirSync26(MESSAGE_STORAGE)) {
|
|
93924
|
-
const sessionPath =
|
|
93925
|
-
if (
|
|
93827
|
+
const sessionPath = join78(MESSAGE_STORAGE, dir, sessionID);
|
|
93828
|
+
if (existsSync75(sessionPath))
|
|
93926
93829
|
return sessionPath;
|
|
93927
93830
|
}
|
|
93928
93831
|
return null;
|
|
@@ -93960,7 +93863,7 @@ function findNearestMessageExcludingCompaction(messageDir) {
|
|
|
93960
93863
|
const parsedByFile = new Map;
|
|
93961
93864
|
for (const file3 of files) {
|
|
93962
93865
|
try {
|
|
93963
|
-
const content =
|
|
93866
|
+
const content = readFileSync45(join78(messageDir, file3), "utf-8");
|
|
93964
93867
|
const parsed = JSON.parse(content);
|
|
93965
93868
|
parsedByFile.set(file3, parsed);
|
|
93966
93869
|
if (hasFullAgentAndModel(parsed)) {
|
|
@@ -96842,7 +96745,7 @@ class TmuxSessionManager {
|
|
|
96842
96745
|
} catch (err) {
|
|
96843
96746
|
log("[tmux-session-manager] session status check error", { error: String(err) });
|
|
96844
96747
|
}
|
|
96845
|
-
await new Promise((
|
|
96748
|
+
await new Promise((resolve9) => setTimeout(resolve9, SESSION_READY_POLL_INTERVAL_MS));
|
|
96846
96749
|
}
|
|
96847
96750
|
log("[tmux-session-manager] session ready timeout", {
|
|
96848
96751
|
sessionId,
|
|
@@ -97025,7 +96928,7 @@ class TmuxSessionManager {
|
|
|
97025
96928
|
var SESSION_TIMEOUT_MS3 = 10 * 60 * 1000;
|
|
97026
96929
|
var MIN_STABILITY_TIME_MS4 = 10 * 1000;
|
|
97027
96930
|
// src/agents/dynamic-agent-prompt-builder.ts
|
|
97028
|
-
import { readFileSync as
|
|
96931
|
+
import { readFileSync as readFileSync46 } from "fs";
|
|
97029
96932
|
init_logger();
|
|
97030
96933
|
function categorizeTools(toolNames) {
|
|
97031
96934
|
return toolNames.map((name) => {
|
|
@@ -97313,6 +97216,28 @@ Oracle creates plans, not code. Invoke oracle to decompose complex requests into
|
|
|
97313
97216
|
**Exception**: This is the ONLY case where you announce before acting. For all other work, start immediately without status updates.
|
|
97314
97217
|
</Oracle_Usage>`;
|
|
97315
97218
|
}
|
|
97219
|
+
function buildArchitectReferralSection() {
|
|
97220
|
+
return `<Architect_Usage>
|
|
97221
|
+
## Architect \u2014 Session Orchestrator (/start-work-only)
|
|
97222
|
+
|
|
97223
|
+
Architect is a /start-work-only session agent \u2014 it orchestrates work via a session until ALL tasks in a todo list are done. Architect is NOT callable via task().
|
|
97224
|
+
|
|
97225
|
+
### WHEN to Refer:
|
|
97226
|
+
|
|
97227
|
+
| Trigger | Action |
|
|
97228
|
+
| User provides a todo list path (.matrixx/plans/{name}.md) | /start-work FIRST, then Architect orchestrates |
|
|
97229
|
+
| Multiple tasks need to be completed in sequence or parallel | /start-work, NOT task() |
|
|
97230
|
+
| Work requires coordination across multiple specialized agents | /start-work, NOT task() |
|
|
97231
|
+
| Todo list path provided OR multiple tasks requiring multi-agent orchestration | /start-work |
|
|
97232
|
+
|
|
97233
|
+
### HARD ROUTING RULE \u2014 Architect Referral (NON-NEGOTIABLE):
|
|
97234
|
+
- ANY task that executes \`.matrixx/plans/*.md\` MUST use \`/start-work\` \u2014 NEVER \`task(subagent_type="architect")\`
|
|
97235
|
+
- Architect is excluded from the task-delegation list by design. Do NOT attempt \`task(subagent_type="architect")\` \u2014 it will be rejected. Use \`/start-work\` instead.
|
|
97236
|
+
|
|
97237
|
+
### Usage Pattern:
|
|
97238
|
+
Architect owns todo-list execution end to end. Invoke /start-work with the plan path so Architect can decompose and orchestrate the work across specialized agents.
|
|
97239
|
+
</Architect_Usage>`;
|
|
97240
|
+
}
|
|
97316
97241
|
function buildHardBlocksSection() {
|
|
97317
97242
|
const blocks = [
|
|
97318
97243
|
"| Type error suppression (`as any`, `@ts-ignore`) | Never |",
|
|
@@ -97388,11 +97313,11 @@ function readDisciplineFile() {
|
|
|
97388
97313
|
const p = resolveContextModeDisciplinePath();
|
|
97389
97314
|
if (!p)
|
|
97390
97315
|
throw new Error("discipline file not found");
|
|
97391
|
-
const raw =
|
|
97316
|
+
const raw = readFileSync46(p, "utf8");
|
|
97392
97317
|
let version4 = "1.0.169";
|
|
97393
97318
|
try {
|
|
97394
97319
|
const pkgResolved = __require.resolve("context-mode/package.json");
|
|
97395
|
-
const pkgRaw =
|
|
97320
|
+
const pkgRaw = readFileSync46(pkgResolved, "utf8");
|
|
97396
97321
|
const pkg = JSON.parse(pkgRaw);
|
|
97397
97322
|
if (pkg.version)
|
|
97398
97323
|
version4 = pkg.version;
|
|
@@ -98375,17 +98300,13 @@ function buildDynamicOrchestratorPrompt(ctx) {
|
|
|
98375
98300
|
return basePrompt.replace("{CATEGORY_SECTION}", categorySection).replace("{AGENT_SECTION}", agentSection).replace("{DECISION_MATRIX}", decisionMatrix).replace("{SKILLS_SECTION}", skillsSection).replace("{{CATEGORY_SKILLS_DELEGATION_GUIDE}}", categorySkillsGuide);
|
|
98376
98301
|
}
|
|
98377
98302
|
function createArchitectAgent(ctx) {
|
|
98378
|
-
const restrictions = createAgentToolRestrictions([
|
|
98379
|
-
"task"
|
|
98380
|
-
]);
|
|
98381
98303
|
const baseConfig = {
|
|
98382
98304
|
description: "Orchestrates work via task() to complete ALL tasks in a todo list until fully done. (Architect - Matrixx)",
|
|
98383
98305
|
mode: MODE,
|
|
98384
98306
|
...ctx.model ? { model: ctx.model } : {},
|
|
98385
98307
|
temperature: 0.1,
|
|
98386
98308
|
prompt: buildDynamicOrchestratorPrompt(ctx),
|
|
98387
|
-
color: "#10B981"
|
|
98388
|
-
...restrictions
|
|
98309
|
+
color: "#10B981"
|
|
98389
98310
|
};
|
|
98390
98311
|
return baseConfig;
|
|
98391
98312
|
}
|
|
@@ -98497,9 +98418,9 @@ function createBddContractAgent(model) {
|
|
|
98497
98418
|
createBddContractAgent.mode = MODE2;
|
|
98498
98419
|
|
|
98499
98420
|
// src/agents/builtin-agents/resolve-file-uri.ts
|
|
98500
|
-
import { existsSync as
|
|
98421
|
+
import { existsSync as existsSync76, readFileSync as readFileSync47 } from "fs";
|
|
98501
98422
|
import { homedir as homedir12 } from "os";
|
|
98502
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
98423
|
+
import { isAbsolute as isAbsolute6, resolve as resolve9 } from "path";
|
|
98503
98424
|
function resolvePromptAppend(promptAppend, configDir) {
|
|
98504
98425
|
if (!promptAppend.startsWith("file://"))
|
|
98505
98426
|
return promptAppend;
|
|
@@ -98508,15 +98429,15 @@ function resolvePromptAppend(promptAppend, configDir) {
|
|
|
98508
98429
|
try {
|
|
98509
98430
|
const decoded = decodeURIComponent(encoded);
|
|
98510
98431
|
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${homedir12()}/`) : decoded;
|
|
98511
|
-
filePath = isAbsolute6(expanded) ? expanded :
|
|
98432
|
+
filePath = isAbsolute6(expanded) ? expanded : resolve9(configDir ?? process.cwd(), expanded);
|
|
98512
98433
|
} catch {
|
|
98513
98434
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
|
|
98514
98435
|
}
|
|
98515
|
-
if (!
|
|
98436
|
+
if (!existsSync76(filePath)) {
|
|
98516
98437
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
|
|
98517
98438
|
}
|
|
98518
98439
|
try {
|
|
98519
|
-
return
|
|
98440
|
+
return readFileSync47(filePath, "utf8");
|
|
98520
98441
|
} catch {
|
|
98521
98442
|
return `[WARNING: Could not read file: ${promptAppend}]`;
|
|
98522
98443
|
}
|
|
@@ -99545,6 +99466,7 @@ function buildDynamicMorpheusPrompt(availableAgents, availableTools = [], availa
|
|
|
99545
99466
|
const categorySkillsGuide = buildCategorySkillsDelegationGuide(availableCategories, availableSkills);
|
|
99546
99467
|
const delegationTable = buildDelegationTable(availableAgents);
|
|
99547
99468
|
const oracleSection = buildOracleSection(availableAgents);
|
|
99469
|
+
const architectReferral = buildArchitectReferralSection();
|
|
99548
99470
|
const hardBlocks = buildHardBlocksSection();
|
|
99549
99471
|
const antiPatterns = buildAntiPatternsSection();
|
|
99550
99472
|
const hasContextMode = availableTools.some((t) => t.name.startsWith("ctx_"));
|
|
@@ -99841,6 +99763,8 @@ If verification fails:
|
|
|
99841
99763
|
|
|
99842
99764
|
${oracleSection}
|
|
99843
99765
|
|
|
99766
|
+
${architectReferral}
|
|
99767
|
+
|
|
99844
99768
|
${taskManagementSection}
|
|
99845
99769
|
|
|
99846
99770
|
<Tone_and_Style>
|
|
@@ -104309,10 +104233,12 @@ function applyToolConfig(params) {
|
|
|
104309
104233
|
}
|
|
104310
104234
|
const architect = agentByKey(params.agentResult, "architect");
|
|
104311
104235
|
if (architect) {
|
|
104312
|
-
|
|
104313
|
-
|
|
104314
|
-
|
|
104315
|
-
|
|
104236
|
+
const defaults = isTaskSystem ? { task: "allow", "task_*": "allow", teammate: "allow", ...denyTodoTools } : { todowrite: "allow", todoread: "allow", ...denyTaskTools };
|
|
104237
|
+
const permission = architect.permission ?? {};
|
|
104238
|
+
for (const [key, value] of Object.entries(defaults)) {
|
|
104239
|
+
permission[key] ??= value;
|
|
104240
|
+
}
|
|
104241
|
+
architect.permission = permission;
|
|
104316
104242
|
}
|
|
104317
104243
|
const morpheus = agentByKey(params.agentResult, "morpheus");
|
|
104318
104244
|
if (morpheus) {
|