brainjar 0.3.685 → 0.3.691
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/brainjar.js +1108 -178
- package/package.json +1 -1
package/brainjar.js
CHANGED
|
@@ -107,7 +107,7 @@ var CLI_VERSION, CACHE, DAY;
|
|
|
107
107
|
var init_version_check = __esm({
|
|
108
108
|
"packages/cli/src/version-check.ts"() {
|
|
109
109
|
"use strict";
|
|
110
|
-
CLI_VERSION = "0.3.
|
|
110
|
+
CLI_VERSION = "0.3.691";
|
|
111
111
|
CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
|
|
112
112
|
DAY = 864e5;
|
|
113
113
|
}
|
|
@@ -238,8 +238,8 @@ var init_github = __esm({
|
|
|
238
238
|
if (this.opts.token) h.Authorization = `Bearer ${this.opts.token}`;
|
|
239
239
|
return h;
|
|
240
240
|
}
|
|
241
|
-
async *paginate(
|
|
242
|
-
let url = this.buildUrl(
|
|
241
|
+
async *paginate(path19, params) {
|
|
242
|
+
let url = this.buildUrl(path19, params);
|
|
243
243
|
while (url) {
|
|
244
244
|
const res = await this.fetchImpl(url, { headers: this.headers() });
|
|
245
245
|
if (!res.ok) {
|
|
@@ -250,9 +250,9 @@ var init_github = __esm({
|
|
|
250
250
|
url = parseNextLink(res.headers.get("link"));
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
|
-
buildUrl(
|
|
253
|
+
buildUrl(path19, params) {
|
|
254
254
|
const url = new URL(
|
|
255
|
-
`${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${
|
|
255
|
+
`${this.baseUrl}/repos/${this.opts.owner}/${this.opts.repo}${path19}`
|
|
256
256
|
);
|
|
257
257
|
url.searchParams.set("per_page", String(this.perPage));
|
|
258
258
|
for (const [k, v] of Object.entries(params)) {
|
|
@@ -3735,7 +3735,7 @@ var require_split2 = __commonJS({
|
|
|
3735
3735
|
var require_helper = __commonJS({
|
|
3736
3736
|
"node_modules/pgpass/lib/helper.js"(exports, module) {
|
|
3737
3737
|
"use strict";
|
|
3738
|
-
var
|
|
3738
|
+
var path19 = __require("path");
|
|
3739
3739
|
var Stream2 = __require("stream").Stream;
|
|
3740
3740
|
var split = require_split2();
|
|
3741
3741
|
var util = __require("util");
|
|
@@ -3774,7 +3774,7 @@ var require_helper = __commonJS({
|
|
|
3774
3774
|
};
|
|
3775
3775
|
module.exports.getFileName = function(rawEnv) {
|
|
3776
3776
|
var env = rawEnv || process.env;
|
|
3777
|
-
var file = env.PGPASSFILE || (isWin ?
|
|
3777
|
+
var file = env.PGPASSFILE || (isWin ? path19.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path19.join(env.HOME || "./", ".pgpass"));
|
|
3778
3778
|
return file;
|
|
3779
3779
|
};
|
|
3780
3780
|
module.exports.usePgPass = function(stats, fname) {
|
|
@@ -3906,7 +3906,7 @@ var require_helper = __commonJS({
|
|
|
3906
3906
|
var require_lib = __commonJS({
|
|
3907
3907
|
"node_modules/pgpass/lib/index.js"(exports, module) {
|
|
3908
3908
|
"use strict";
|
|
3909
|
-
var
|
|
3909
|
+
var path19 = __require("path");
|
|
3910
3910
|
var fs = __require("fs");
|
|
3911
3911
|
var helper = require_helper();
|
|
3912
3912
|
module.exports = function(connInfo, cb) {
|
|
@@ -8850,12 +8850,12 @@ var init_path = __esm({
|
|
|
8850
8850
|
"node_modules/@anthropic-ai/sdk/internal/utils/path.mjs"() {
|
|
8851
8851
|
init_error();
|
|
8852
8852
|
EMPTY = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
8853
|
-
createPathTagFunction = (pathEncoder = encodeURIPath) => function
|
|
8853
|
+
createPathTagFunction = (pathEncoder = encodeURIPath) => function path19(statics, ...params) {
|
|
8854
8854
|
if (statics.length === 1)
|
|
8855
8855
|
return statics[0];
|
|
8856
8856
|
let postPath = false;
|
|
8857
8857
|
const invalidSegments = [];
|
|
8858
|
-
const
|
|
8858
|
+
const path20 = statics.reduce((previousValue, currentValue, index) => {
|
|
8859
8859
|
if (/[?#]/.test(currentValue)) {
|
|
8860
8860
|
postPath = true;
|
|
8861
8861
|
}
|
|
@@ -8872,7 +8872,7 @@ var init_path = __esm({
|
|
|
8872
8872
|
}
|
|
8873
8873
|
return previousValue + currentValue + (index === params.length ? "" : encoded);
|
|
8874
8874
|
}, "");
|
|
8875
|
-
const pathOnly =
|
|
8875
|
+
const pathOnly = path20.split(/[?#]/, 1)[0];
|
|
8876
8876
|
const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
|
|
8877
8877
|
let match;
|
|
8878
8878
|
while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
|
|
@@ -8893,10 +8893,10 @@ var init_path = __esm({
|
|
|
8893
8893
|
}, "");
|
|
8894
8894
|
throw new AnthropicError(`Path parameters result in path with invalid segments:
|
|
8895
8895
|
${invalidSegments.map((e) => e.error).join("\n")}
|
|
8896
|
-
${
|
|
8896
|
+
${path20}
|
|
8897
8897
|
${underline}`);
|
|
8898
8898
|
}
|
|
8899
|
-
return
|
|
8899
|
+
return path20;
|
|
8900
8900
|
};
|
|
8901
8901
|
path3 = /* @__PURE__ */ createPathTagFunction(encodeURIPath);
|
|
8902
8902
|
}
|
|
@@ -11882,9 +11882,9 @@ var init_client = __esm({
|
|
|
11882
11882
|
makeStatusError(status, error, message, headers) {
|
|
11883
11883
|
return APIError.generate(status, error, message, headers);
|
|
11884
11884
|
}
|
|
11885
|
-
buildURL(
|
|
11885
|
+
buildURL(path19, query, defaultBaseURL) {
|
|
11886
11886
|
const baseURL = !__classPrivateFieldGet(this, _BaseAnthropic_instances, "m", _BaseAnthropic_baseURLOverridden).call(this) && defaultBaseURL || this.baseURL;
|
|
11887
|
-
const url = isAbsoluteURL(
|
|
11887
|
+
const url = isAbsoluteURL(path19) ? new URL(path19) : new URL(baseURL + (baseURL.endsWith("/") && path19.startsWith("/") ? path19.slice(1) : path19));
|
|
11888
11888
|
const defaultQuery = this.defaultQuery();
|
|
11889
11889
|
if (!isEmptyObj(defaultQuery)) {
|
|
11890
11890
|
query = { ...defaultQuery, ...query };
|
|
@@ -11915,24 +11915,24 @@ var init_client = __esm({
|
|
|
11915
11915
|
*/
|
|
11916
11916
|
async prepareRequest(request, { url, options }) {
|
|
11917
11917
|
}
|
|
11918
|
-
get(
|
|
11919
|
-
return this.methodRequest("get",
|
|
11918
|
+
get(path19, opts) {
|
|
11919
|
+
return this.methodRequest("get", path19, opts);
|
|
11920
11920
|
}
|
|
11921
|
-
post(
|
|
11922
|
-
return this.methodRequest("post",
|
|
11921
|
+
post(path19, opts) {
|
|
11922
|
+
return this.methodRequest("post", path19, opts);
|
|
11923
11923
|
}
|
|
11924
|
-
patch(
|
|
11925
|
-
return this.methodRequest("patch",
|
|
11924
|
+
patch(path19, opts) {
|
|
11925
|
+
return this.methodRequest("patch", path19, opts);
|
|
11926
11926
|
}
|
|
11927
|
-
put(
|
|
11928
|
-
return this.methodRequest("put",
|
|
11927
|
+
put(path19, opts) {
|
|
11928
|
+
return this.methodRequest("put", path19, opts);
|
|
11929
11929
|
}
|
|
11930
|
-
delete(
|
|
11931
|
-
return this.methodRequest("delete",
|
|
11930
|
+
delete(path19, opts) {
|
|
11931
|
+
return this.methodRequest("delete", path19, opts);
|
|
11932
11932
|
}
|
|
11933
|
-
methodRequest(method,
|
|
11933
|
+
methodRequest(method, path19, opts) {
|
|
11934
11934
|
return this.request(Promise.resolve(opts).then((opts2) => {
|
|
11935
|
-
return { method, path:
|
|
11935
|
+
return { method, path: path19, ...opts2 };
|
|
11936
11936
|
}));
|
|
11937
11937
|
}
|
|
11938
11938
|
request(options, remainingRetries = null) {
|
|
@@ -12036,8 +12036,8 @@ var init_client = __esm({
|
|
|
12036
12036
|
}));
|
|
12037
12037
|
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
|
|
12038
12038
|
}
|
|
12039
|
-
getAPIList(
|
|
12040
|
-
return this.requestAPIList(Page2, { method: "get", path:
|
|
12039
|
+
getAPIList(path19, Page2, opts) {
|
|
12040
|
+
return this.requestAPIList(Page2, { method: "get", path: path19, ...opts });
|
|
12041
12041
|
}
|
|
12042
12042
|
requestAPIList(Page2, options) {
|
|
12043
12043
|
const request = this.makeRequest(options, null, void 0);
|
|
@@ -12124,8 +12124,8 @@ var init_client = __esm({
|
|
|
12124
12124
|
}
|
|
12125
12125
|
async buildRequest(inputOptions, { retryCount = 0 } = {}) {
|
|
12126
12126
|
const options = { ...inputOptions };
|
|
12127
|
-
const { method, path:
|
|
12128
|
-
const url = this.buildURL(
|
|
12127
|
+
const { method, path: path19, query, defaultBaseURL } = options;
|
|
12128
|
+
const url = this.buildURL(path19, query, defaultBaseURL);
|
|
12129
12129
|
if ("timeout" in options)
|
|
12130
12130
|
validatePositiveInteger("timeout", options.timeout);
|
|
12131
12131
|
options.timeout = options.timeout ?? this.timeout;
|
|
@@ -12704,22 +12704,22 @@ async function gitHooksDir(root) {
|
|
|
12704
12704
|
return path5.join(root, ".git", "hooks");
|
|
12705
12705
|
}
|
|
12706
12706
|
async function installGitPostCommitHook(root) {
|
|
12707
|
-
const
|
|
12708
|
-
const file = path5.join(
|
|
12707
|
+
const hooksDir2 = await gitHooksDir(root);
|
|
12708
|
+
const file = path5.join(hooksDir2, "post-commit");
|
|
12709
12709
|
const existing = existsSync2(file) ? await readFile2(file, "utf8") : null;
|
|
12710
12710
|
if (existing?.includes(GIT_HOOK_MARKER)) return false;
|
|
12711
12711
|
const content = existing === null ? `#!/bin/sh
|
|
12712
12712
|
${gitHookBlock()}
|
|
12713
12713
|
` : existing.replace(/\n*$/, "\n") + gitHookBlock() + "\n";
|
|
12714
|
-
await mkdir(
|
|
12714
|
+
await mkdir(hooksDir2, { recursive: true });
|
|
12715
12715
|
await writeFile(file, content, { mode: 493 });
|
|
12716
12716
|
await chmod(file, 493).catch(() => {
|
|
12717
12717
|
});
|
|
12718
12718
|
return true;
|
|
12719
12719
|
}
|
|
12720
12720
|
async function removeGitPostCommitHook(root) {
|
|
12721
|
-
const
|
|
12722
|
-
const file = path5.join(
|
|
12721
|
+
const hooksDir2 = await gitHooksDir(root);
|
|
12722
|
+
const file = path5.join(hooksDir2, "post-commit");
|
|
12723
12723
|
if (!existsSync2(file)) return false;
|
|
12724
12724
|
const content = await readFile2(file, "utf8");
|
|
12725
12725
|
if (!content.includes(GIT_HOOK_MARKER)) return false;
|
|
@@ -13115,14 +13115,367 @@ var init_copilot_hooks = __esm({
|
|
|
13115
13115
|
}
|
|
13116
13116
|
});
|
|
13117
13117
|
|
|
13118
|
+
// packages/ingest/src/gemini-hooks.ts
|
|
13119
|
+
import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir5 } from "node:fs/promises";
|
|
13120
|
+
import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
|
|
13121
|
+
import path9 from "node:path";
|
|
13122
|
+
async function installGeminiHooks(root) {
|
|
13123
|
+
const file = settingsFile(root);
|
|
13124
|
+
let settings = {};
|
|
13125
|
+
if (existsSync6(file)) {
|
|
13126
|
+
try {
|
|
13127
|
+
settings = JSON.parse(await readFile6(file, "utf8"));
|
|
13128
|
+
} catch {
|
|
13129
|
+
settings = {};
|
|
13130
|
+
}
|
|
13131
|
+
}
|
|
13132
|
+
const hooks = settings.hooks ??= {};
|
|
13133
|
+
let changed = false;
|
|
13134
|
+
for (const { event, subcommand, probe, timeout } of EVENTS4) {
|
|
13135
|
+
const groups = hooks[event] ??= [];
|
|
13136
|
+
if (groups.some((g) => g.hooks?.some((h) => h.command?.includes(probe))))
|
|
13137
|
+
continue;
|
|
13138
|
+
groups.push({
|
|
13139
|
+
hooks: [
|
|
13140
|
+
{
|
|
13141
|
+
type: "command",
|
|
13142
|
+
command: captureHookCommand(subcommand),
|
|
13143
|
+
name: "brainjar",
|
|
13144
|
+
timeout
|
|
13145
|
+
}
|
|
13146
|
+
]
|
|
13147
|
+
});
|
|
13148
|
+
changed = true;
|
|
13149
|
+
}
|
|
13150
|
+
if (!changed) return false;
|
|
13151
|
+
await mkdir5(path9.dirname(file), { recursive: true });
|
|
13152
|
+
await writeFile5(file, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
13153
|
+
return true;
|
|
13154
|
+
}
|
|
13155
|
+
function geminiHooksInstalled(root) {
|
|
13156
|
+
const file = settingsFile(root);
|
|
13157
|
+
if (!existsSync6(file)) return false;
|
|
13158
|
+
try {
|
|
13159
|
+
return readFileSync7(file, "utf8").includes(PROBE3);
|
|
13160
|
+
} catch {
|
|
13161
|
+
return false;
|
|
13162
|
+
}
|
|
13163
|
+
}
|
|
13164
|
+
async function removeGeminiHooks(root) {
|
|
13165
|
+
const file = settingsFile(root);
|
|
13166
|
+
if (!existsSync6(file)) return false;
|
|
13167
|
+
let settings;
|
|
13168
|
+
try {
|
|
13169
|
+
settings = JSON.parse(await readFile6(file, "utf8"));
|
|
13170
|
+
} catch {
|
|
13171
|
+
return false;
|
|
13172
|
+
}
|
|
13173
|
+
const hooks = settings.hooks;
|
|
13174
|
+
if (!hooks) return false;
|
|
13175
|
+
let changed = false;
|
|
13176
|
+
for (const event of Object.keys(hooks)) {
|
|
13177
|
+
const groups = hooks[event];
|
|
13178
|
+
if (!Array.isArray(groups)) continue;
|
|
13179
|
+
const kept = groups.filter((g) => !g.hooks?.some(ours2));
|
|
13180
|
+
if (kept.length !== groups.length) {
|
|
13181
|
+
changed = true;
|
|
13182
|
+
if (kept.length === 0) delete hooks[event];
|
|
13183
|
+
else hooks[event] = kept;
|
|
13184
|
+
}
|
|
13185
|
+
}
|
|
13186
|
+
if (!changed) return false;
|
|
13187
|
+
await writeFile5(file, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
13188
|
+
return true;
|
|
13189
|
+
}
|
|
13190
|
+
var PROBE3, EVENTS4, PROBES2, settingsFile, ours2;
|
|
13191
|
+
var init_gemini_hooks = __esm({
|
|
13192
|
+
"packages/ingest/src/gemini-hooks.ts"() {
|
|
13193
|
+
"use strict";
|
|
13194
|
+
init_bootstrap();
|
|
13195
|
+
PROBE3 = "hook gemini";
|
|
13196
|
+
EVENTS4 = [
|
|
13197
|
+
{
|
|
13198
|
+
event: "SessionStart",
|
|
13199
|
+
subcommand: "constraints",
|
|
13200
|
+
probe: "constraints --hook",
|
|
13201
|
+
timeout: 15e3
|
|
13202
|
+
},
|
|
13203
|
+
{
|
|
13204
|
+
event: "BeforeAgent",
|
|
13205
|
+
subcommand: "hook gemini",
|
|
13206
|
+
probe: PROBE3,
|
|
13207
|
+
timeout: 3e4
|
|
13208
|
+
},
|
|
13209
|
+
{
|
|
13210
|
+
event: "AfterTool",
|
|
13211
|
+
subcommand: "hook gemini",
|
|
13212
|
+
probe: PROBE3,
|
|
13213
|
+
timeout: 3e4
|
|
13214
|
+
},
|
|
13215
|
+
{
|
|
13216
|
+
event: "AfterAgent",
|
|
13217
|
+
subcommand: "hook gemini",
|
|
13218
|
+
probe: PROBE3,
|
|
13219
|
+
timeout: 12e4
|
|
13220
|
+
},
|
|
13221
|
+
{
|
|
13222
|
+
event: "SessionEnd",
|
|
13223
|
+
subcommand: "hook gemini",
|
|
13224
|
+
probe: PROBE3,
|
|
13225
|
+
timeout: 1e4
|
|
13226
|
+
}
|
|
13227
|
+
];
|
|
13228
|
+
PROBES2 = EVENTS4.map((e) => e.probe);
|
|
13229
|
+
settingsFile = (root) => path9.join(root, ".gemini", "settings.json");
|
|
13230
|
+
ours2 = (h) => PROBES2.some((p) => h.command?.includes(p) ?? false);
|
|
13231
|
+
}
|
|
13232
|
+
});
|
|
13233
|
+
|
|
13234
|
+
// packages/ingest/src/kiro-hooks.ts
|
|
13235
|
+
import { readFile as readFile7, writeFile as writeFile6, mkdir as mkdir6, rm as rm3 } from "node:fs/promises";
|
|
13236
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
|
|
13237
|
+
import path10 from "node:path";
|
|
13238
|
+
function render2() {
|
|
13239
|
+
const hook = (name, trigger, command, timeout) => ({ name, trigger, action: { type: "command", command }, timeout });
|
|
13240
|
+
return JSON.stringify(
|
|
13241
|
+
{
|
|
13242
|
+
version: "v1",
|
|
13243
|
+
hooks: [
|
|
13244
|
+
hook(
|
|
13245
|
+
"brainjar-context",
|
|
13246
|
+
"SessionStart",
|
|
13247
|
+
`${captureHookCommand("constraints")} --plain`,
|
|
13248
|
+
15
|
|
13249
|
+
),
|
|
13250
|
+
hook(
|
|
13251
|
+
"brainjar-prompt",
|
|
13252
|
+
"UserPromptSubmit",
|
|
13253
|
+
captureHookCommand("hook kiro"),
|
|
13254
|
+
30
|
|
13255
|
+
),
|
|
13256
|
+
hook(
|
|
13257
|
+
"brainjar-tool",
|
|
13258
|
+
"PostToolUse",
|
|
13259
|
+
captureHookCommand("hook kiro"),
|
|
13260
|
+
30
|
|
13261
|
+
),
|
|
13262
|
+
hook("brainjar-stop", "Stop", captureHookCommand("hook kiro"), 120)
|
|
13263
|
+
]
|
|
13264
|
+
},
|
|
13265
|
+
null,
|
|
13266
|
+
2
|
|
13267
|
+
) + "\n";
|
|
13268
|
+
}
|
|
13269
|
+
async function installKiroHooks(root) {
|
|
13270
|
+
const file = hooksFile4(root);
|
|
13271
|
+
const next = render2();
|
|
13272
|
+
if (existsSync7(file) && await readFile7(file, "utf8") === next) return false;
|
|
13273
|
+
await mkdir6(path10.dirname(file), { recursive: true });
|
|
13274
|
+
await writeFile6(file, next, "utf8");
|
|
13275
|
+
return true;
|
|
13276
|
+
}
|
|
13277
|
+
function kiroHooksInstalled(root) {
|
|
13278
|
+
const file = hooksFile4(root);
|
|
13279
|
+
if (!existsSync7(file)) return false;
|
|
13280
|
+
try {
|
|
13281
|
+
return readFileSync8(file, "utf8").includes(PROBE4);
|
|
13282
|
+
} catch {
|
|
13283
|
+
return false;
|
|
13284
|
+
}
|
|
13285
|
+
}
|
|
13286
|
+
async function removeKiroHooks(root) {
|
|
13287
|
+
if (!kiroHooksInstalled(root)) return false;
|
|
13288
|
+
await rm3(hooksFile4(root), { force: true });
|
|
13289
|
+
return true;
|
|
13290
|
+
}
|
|
13291
|
+
var PROBE4, hooksFile4;
|
|
13292
|
+
var init_kiro_hooks = __esm({
|
|
13293
|
+
"packages/ingest/src/kiro-hooks.ts"() {
|
|
13294
|
+
"use strict";
|
|
13295
|
+
init_bootstrap();
|
|
13296
|
+
PROBE4 = "hook kiro";
|
|
13297
|
+
hooksFile4 = (root) => path10.join(root, ".kiro", "hooks", "brainjar.json");
|
|
13298
|
+
}
|
|
13299
|
+
});
|
|
13300
|
+
|
|
13301
|
+
// packages/ingest/src/opencode-plugin.ts
|
|
13302
|
+
import { readFile as readFile8, writeFile as writeFile7, mkdir as mkdir7, rm as rm4 } from "node:fs/promises";
|
|
13303
|
+
import { existsSync as existsSync8, readFileSync as readFileSync9 } from "node:fs";
|
|
13304
|
+
import path11 from "node:path";
|
|
13305
|
+
function render3() {
|
|
13306
|
+
const cmd = captureHookCommand("hook opencode");
|
|
13307
|
+
return `// BrainJar capture plugin, written by \`brainjar init\` (rewritten on every init; do not edit).
|
|
13308
|
+
// Ships prompts, tool calls, failures, and touched files of every OpenCode session to your Brain.
|
|
13309
|
+
import { spawn } from "node:child_process";
|
|
13310
|
+
|
|
13311
|
+
export const BrainJar = async ({ directory }) => {
|
|
13312
|
+
let session = "";
|
|
13313
|
+
const seen = new Set();
|
|
13314
|
+
const send = (payload) => {
|
|
13315
|
+
try {
|
|
13316
|
+
const child = spawn(${JSON.stringify(cmd)}, {
|
|
13317
|
+
shell: true,
|
|
13318
|
+
cwd: directory,
|
|
13319
|
+
stdio: ["pipe", "ignore", "ignore"],
|
|
13320
|
+
});
|
|
13321
|
+
child.on("error", () => {});
|
|
13322
|
+
child.stdin.end(
|
|
13323
|
+
JSON.stringify({ source: "opencode", cwd: directory, session_id: session, ...payload }),
|
|
13324
|
+
);
|
|
13325
|
+
} catch {}
|
|
13326
|
+
};
|
|
13327
|
+
const target = (args) => {
|
|
13328
|
+
if (!args || typeof args !== "object") return "";
|
|
13329
|
+
for (const k of ["command", "filePath", "path"]) if (typeof args[k] === "string") return args[k];
|
|
13330
|
+
return "";
|
|
13331
|
+
};
|
|
13332
|
+
return {
|
|
13333
|
+
event: async ({ event }) => {
|
|
13334
|
+
const p = event.properties ?? {};
|
|
13335
|
+
switch (event.type) {
|
|
13336
|
+
case "session.created":
|
|
13337
|
+
session = p.info?.id ?? session;
|
|
13338
|
+
send({ kind: "session_started" });
|
|
13339
|
+
break;
|
|
13340
|
+
case "session.idle":
|
|
13341
|
+
if (p.sessionID) session = p.sessionID;
|
|
13342
|
+
send({ kind: "turn_ended" });
|
|
13343
|
+
break;
|
|
13344
|
+
case "file.edited":
|
|
13345
|
+
send({ kind: "file_changed", path: p.file });
|
|
13346
|
+
break;
|
|
13347
|
+
case "message.part.updated": {
|
|
13348
|
+
const part = p.part;
|
|
13349
|
+
if (part?.type === "tool" && part.state?.status === "error" && !seen.has(part.callID)) {
|
|
13350
|
+
seen.add(part.callID);
|
|
13351
|
+
session = part.sessionID ?? session;
|
|
13352
|
+
send({ kind: "tool_failed", tool_name: part.tool, target: target(part.state.input), error: String(part.state.error ?? "") });
|
|
13353
|
+
}
|
|
13354
|
+
break;
|
|
13355
|
+
}
|
|
13356
|
+
}
|
|
13357
|
+
},
|
|
13358
|
+
"chat.message": async (input, output) => {
|
|
13359
|
+
session = input.sessionID ?? session;
|
|
13360
|
+
const text = (output.parts ?? []).filter((x) => x.type === "text").map((x) => x.text).join("\\n");
|
|
13361
|
+
if (text.trim()) send({ kind: "prompt", text });
|
|
13362
|
+
},
|
|
13363
|
+
"tool.execute.after": async (input, output) => {
|
|
13364
|
+
session = input.sessionID ?? session;
|
|
13365
|
+
send({ kind: "tool_used", tool_name: input.tool, target: target(input.args), output: String(output.output ?? "").slice(0, 4000) });
|
|
13366
|
+
},
|
|
13367
|
+
};
|
|
13368
|
+
};
|
|
13369
|
+
`;
|
|
13370
|
+
}
|
|
13371
|
+
async function installOpencodePlugin(root) {
|
|
13372
|
+
const file = pluginFile(root);
|
|
13373
|
+
const next = render3();
|
|
13374
|
+
if (existsSync8(file) && await readFile8(file, "utf8") === next) return false;
|
|
13375
|
+
await mkdir7(path11.dirname(file), { recursive: true });
|
|
13376
|
+
await writeFile7(file, next, "utf8");
|
|
13377
|
+
return true;
|
|
13378
|
+
}
|
|
13379
|
+
function opencodePluginInstalled(root) {
|
|
13380
|
+
const file = pluginFile(root);
|
|
13381
|
+
if (!existsSync8(file)) return false;
|
|
13382
|
+
try {
|
|
13383
|
+
return readFileSync9(file, "utf8").includes(PROBE5);
|
|
13384
|
+
} catch {
|
|
13385
|
+
return false;
|
|
13386
|
+
}
|
|
13387
|
+
}
|
|
13388
|
+
async function removeOpencodePlugin(root) {
|
|
13389
|
+
if (!opencodePluginInstalled(root)) return false;
|
|
13390
|
+
await rm4(pluginFile(root), { force: true });
|
|
13391
|
+
return true;
|
|
13392
|
+
}
|
|
13393
|
+
var PROBE5, pluginFile;
|
|
13394
|
+
var init_opencode_plugin = __esm({
|
|
13395
|
+
"packages/ingest/src/opencode-plugin.ts"() {
|
|
13396
|
+
"use strict";
|
|
13397
|
+
init_bootstrap();
|
|
13398
|
+
PROBE5 = "hook opencode";
|
|
13399
|
+
pluginFile = (root) => path11.join(root, ".opencode", "plugins", "brainjar.ts");
|
|
13400
|
+
}
|
|
13401
|
+
});
|
|
13402
|
+
|
|
13403
|
+
// packages/ingest/src/cline-hooks.ts
|
|
13404
|
+
import { readFile as readFile9, writeFile as writeFile8, mkdir as mkdir8, rm as rm5, chmod as chmod2 } from "node:fs/promises";
|
|
13405
|
+
import { existsSync as existsSync9, readFileSync as readFileSync10 } from "node:fs";
|
|
13406
|
+
import path12 from "node:path";
|
|
13407
|
+
async function installClineHooks(root) {
|
|
13408
|
+
const dir = hooksDir(root);
|
|
13409
|
+
let changed = false;
|
|
13410
|
+
for (const { name, subcommand, flag } of SCRIPTS) {
|
|
13411
|
+
const file = path12.join(dir, name);
|
|
13412
|
+
const next = script(subcommand, flag);
|
|
13413
|
+
if (existsSync9(file)) {
|
|
13414
|
+
const current = await readFile9(file, "utf8");
|
|
13415
|
+
if (current === next) continue;
|
|
13416
|
+
if (!current.includes(MARK)) continue;
|
|
13417
|
+
}
|
|
13418
|
+
await mkdir8(dir, { recursive: true });
|
|
13419
|
+
await writeFile8(file, next, { mode: 493 });
|
|
13420
|
+
await chmod2(file, 493).catch(() => {
|
|
13421
|
+
});
|
|
13422
|
+
changed = true;
|
|
13423
|
+
}
|
|
13424
|
+
return changed;
|
|
13425
|
+
}
|
|
13426
|
+
function clineHooksInstalled(root) {
|
|
13427
|
+
const file = path12.join(hooksDir(root), "TaskComplete");
|
|
13428
|
+
if (!existsSync9(file)) return false;
|
|
13429
|
+
try {
|
|
13430
|
+
return readFileSync10(file, "utf8").includes("hook cline");
|
|
13431
|
+
} catch {
|
|
13432
|
+
return false;
|
|
13433
|
+
}
|
|
13434
|
+
}
|
|
13435
|
+
async function removeClineHooks(root) {
|
|
13436
|
+
let changed = false;
|
|
13437
|
+
for (const { name } of SCRIPTS) {
|
|
13438
|
+
const file = path12.join(hooksDir(root), name);
|
|
13439
|
+
if (!existsSync9(file)) continue;
|
|
13440
|
+
try {
|
|
13441
|
+
if (!readFileSync10(file, "utf8").includes(MARK)) continue;
|
|
13442
|
+
} catch {
|
|
13443
|
+
continue;
|
|
13444
|
+
}
|
|
13445
|
+
await rm5(file, { force: true });
|
|
13446
|
+
changed = true;
|
|
13447
|
+
}
|
|
13448
|
+
return changed;
|
|
13449
|
+
}
|
|
13450
|
+
var MARK, SCRIPTS, hooksDir, script;
|
|
13451
|
+
var init_cline_hooks = __esm({
|
|
13452
|
+
"packages/ingest/src/cline-hooks.ts"() {
|
|
13453
|
+
"use strict";
|
|
13454
|
+
init_bootstrap();
|
|
13455
|
+
MARK = "# brainjar-capture";
|
|
13456
|
+
SCRIPTS = [
|
|
13457
|
+
{ name: "TaskStart", subcommand: "constraints", flag: " --cline" },
|
|
13458
|
+
{ name: "UserPromptSubmit", subcommand: "hook cline", flag: "" },
|
|
13459
|
+
{ name: "PostToolUse", subcommand: "hook cline", flag: "" },
|
|
13460
|
+
{ name: "TaskComplete", subcommand: "hook cline", flag: "" },
|
|
13461
|
+
{ name: "TaskCancel", subcommand: "hook cline", flag: "" }
|
|
13462
|
+
];
|
|
13463
|
+
hooksDir = (root) => path12.join(root, ".clinerules", "hooks");
|
|
13464
|
+
script = (subcommand, flag) => `#!/bin/sh
|
|
13465
|
+
${MARK}
|
|
13466
|
+
exec ${captureHookCommand(subcommand)}${flag}
|
|
13467
|
+
`;
|
|
13468
|
+
}
|
|
13469
|
+
});
|
|
13470
|
+
|
|
13118
13471
|
// packages/ingest/src/tools.ts
|
|
13119
|
-
import { existsSync as
|
|
13472
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
13120
13473
|
import os4 from "node:os";
|
|
13121
|
-
import
|
|
13474
|
+
import path13 from "node:path";
|
|
13122
13475
|
function detectedTools(root) {
|
|
13123
13476
|
return CAPTURE_TOOLS.filter((t) => t.detect(root));
|
|
13124
13477
|
}
|
|
13125
|
-
var claudeCode, cursor, codex, copilot, CAPTURE_TOOLS;
|
|
13478
|
+
var claudeCode, cursor, codex, copilot, gemini, kiro, opencode, cline, CAPTURE_TOOLS;
|
|
13126
13479
|
var init_tools = __esm({
|
|
13127
13480
|
"packages/ingest/src/tools.ts"() {
|
|
13128
13481
|
"use strict";
|
|
@@ -13130,10 +13483,14 @@ var init_tools = __esm({
|
|
|
13130
13483
|
init_cursor_hooks();
|
|
13131
13484
|
init_codex_hooks();
|
|
13132
13485
|
init_copilot_hooks();
|
|
13486
|
+
init_gemini_hooks();
|
|
13487
|
+
init_kiro_hooks();
|
|
13488
|
+
init_opencode_plugin();
|
|
13489
|
+
init_cline_hooks();
|
|
13133
13490
|
claudeCode = {
|
|
13134
13491
|
id: "claude-code",
|
|
13135
13492
|
name: "Claude Code",
|
|
13136
|
-
detect: (root) =>
|
|
13493
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".claude")) || existsSync10(path13.join(root, ".claude")),
|
|
13137
13494
|
install: installCaptureHook,
|
|
13138
13495
|
installed: captureHookInstalled,
|
|
13139
13496
|
remove: removeCaptureHook
|
|
@@ -13141,7 +13498,7 @@ var init_tools = __esm({
|
|
|
13141
13498
|
cursor = {
|
|
13142
13499
|
id: "cursor",
|
|
13143
13500
|
name: "Cursor",
|
|
13144
|
-
detect: (root) =>
|
|
13501
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".cursor")) || existsSync10(path13.join(root, ".cursor")),
|
|
13145
13502
|
install: installCursorHooks,
|
|
13146
13503
|
installed: cursorHooksInstalled,
|
|
13147
13504
|
remove: removeCursorHooks
|
|
@@ -13149,7 +13506,7 @@ var init_tools = __esm({
|
|
|
13149
13506
|
codex = {
|
|
13150
13507
|
id: "codex",
|
|
13151
13508
|
name: "Codex",
|
|
13152
|
-
detect: (root) =>
|
|
13509
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".codex")) || existsSync10(path13.join(root, ".codex")),
|
|
13153
13510
|
install: installCodexHooks,
|
|
13154
13511
|
installed: codexHooksInstalled,
|
|
13155
13512
|
remove: removeCodexHooks
|
|
@@ -13157,16 +13514,52 @@ var init_tools = __esm({
|
|
|
13157
13514
|
copilot = {
|
|
13158
13515
|
id: "copilot",
|
|
13159
13516
|
name: "GitHub Copilot",
|
|
13160
|
-
detect: (root) =>
|
|
13517
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".copilot")) || existsSync10(path13.join(root, ".github", "hooks")),
|
|
13161
13518
|
install: installCopilotHooks,
|
|
13162
13519
|
installed: copilotHooksInstalled,
|
|
13163
13520
|
remove: removeCopilotHooks
|
|
13164
13521
|
};
|
|
13522
|
+
gemini = {
|
|
13523
|
+
id: "gemini",
|
|
13524
|
+
name: "Gemini CLI",
|
|
13525
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".gemini")) || existsSync10(path13.join(root, ".gemini")),
|
|
13526
|
+
install: installGeminiHooks,
|
|
13527
|
+
installed: geminiHooksInstalled,
|
|
13528
|
+
remove: removeGeminiHooks
|
|
13529
|
+
};
|
|
13530
|
+
kiro = {
|
|
13531
|
+
id: "kiro",
|
|
13532
|
+
name: "Kiro",
|
|
13533
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".kiro")) || existsSync10(path13.join(root, ".kiro")),
|
|
13534
|
+
install: installKiroHooks,
|
|
13535
|
+
installed: kiroHooksInstalled,
|
|
13536
|
+
remove: removeKiroHooks
|
|
13537
|
+
};
|
|
13538
|
+
opencode = {
|
|
13539
|
+
id: "opencode",
|
|
13540
|
+
name: "OpenCode",
|
|
13541
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), ".config", "opencode")) || existsSync10(path13.join(root, ".opencode")),
|
|
13542
|
+
install: installOpencodePlugin,
|
|
13543
|
+
installed: opencodePluginInstalled,
|
|
13544
|
+
remove: removeOpencodePlugin
|
|
13545
|
+
};
|
|
13546
|
+
cline = {
|
|
13547
|
+
id: "cline",
|
|
13548
|
+
name: "Cline",
|
|
13549
|
+
detect: (root) => existsSync10(path13.join(os4.homedir(), "Documents", "Cline")) || existsSync10(path13.join(os4.homedir(), ".cline")) || existsSync10(path13.join(root, ".clinerules")),
|
|
13550
|
+
install: installClineHooks,
|
|
13551
|
+
installed: clineHooksInstalled,
|
|
13552
|
+
remove: removeClineHooks
|
|
13553
|
+
};
|
|
13165
13554
|
CAPTURE_TOOLS = [
|
|
13166
13555
|
claudeCode,
|
|
13167
13556
|
cursor,
|
|
13168
13557
|
codex,
|
|
13169
|
-
copilot
|
|
13558
|
+
copilot,
|
|
13559
|
+
gemini,
|
|
13560
|
+
kiro,
|
|
13561
|
+
opencode,
|
|
13562
|
+
cline
|
|
13170
13563
|
];
|
|
13171
13564
|
}
|
|
13172
13565
|
});
|
|
@@ -13643,8 +14036,8 @@ var init_doku_model = __esm({
|
|
|
13643
14036
|
|
|
13644
14037
|
// packages/ingest/src/doku.ts
|
|
13645
14038
|
import { createHash as createHash2 } from "node:crypto";
|
|
13646
|
-
import { mkdir as
|
|
13647
|
-
import
|
|
14039
|
+
import { mkdir as mkdir9, readFile as readFile10, rm as rm6, stat, writeFile as writeFile9 } from "node:fs/promises";
|
|
14040
|
+
import path14 from "node:path";
|
|
13648
14041
|
function bodyLocations(o) {
|
|
13649
14042
|
return Array.isArray(o.body.location) ? o.body.location.map(String) : [];
|
|
13650
14043
|
}
|
|
@@ -13710,7 +14103,7 @@ async function loadDokuData(brainId, root) {
|
|
|
13710
14103
|
if (root) {
|
|
13711
14104
|
try {
|
|
13712
14105
|
const pkg = JSON.parse(
|
|
13713
|
-
await
|
|
14106
|
+
await readFile10(path14.join(root, "package.json"), "utf8")
|
|
13714
14107
|
);
|
|
13715
14108
|
description = pkg.description ?? null;
|
|
13716
14109
|
scripts = pkg.scripts ?? null;
|
|
@@ -14293,11 +14686,11 @@ function dokuFilePath(kind, slug) {
|
|
|
14293
14686
|
return `${DOKU_DIR}/${slug}.md`;
|
|
14294
14687
|
}
|
|
14295
14688
|
async function writeDokuTree(root, files) {
|
|
14296
|
-
const dir =
|
|
14297
|
-
const manifestPath =
|
|
14689
|
+
const dir = path14.join(root, DOKU_DIR);
|
|
14690
|
+
const manifestPath = path14.join(dir, MANIFEST);
|
|
14298
14691
|
let old = { files: {} };
|
|
14299
14692
|
try {
|
|
14300
|
-
old = JSON.parse(await
|
|
14693
|
+
old = JSON.parse(await readFile10(manifestPath, "utf8"));
|
|
14301
14694
|
if (typeof old.files !== "object" || old.files === null)
|
|
14302
14695
|
old = { files: {} };
|
|
14303
14696
|
} catch {
|
|
@@ -14307,21 +14700,21 @@ async function writeDokuTree(root, files) {
|
|
|
14307
14700
|
for (const file of files) {
|
|
14308
14701
|
const hash = sha1(file.markdown);
|
|
14309
14702
|
next[file.relPath] = { hash };
|
|
14310
|
-
const abs =
|
|
14703
|
+
const abs = path14.join(root, file.relPath);
|
|
14311
14704
|
if (old.files[file.relPath]?.hash === hash && await exists(abs)) continue;
|
|
14312
|
-
await
|
|
14313
|
-
await
|
|
14705
|
+
await mkdir9(path14.dirname(abs), { recursive: true });
|
|
14706
|
+
await writeFile9(abs, file.markdown, "utf8");
|
|
14314
14707
|
written++;
|
|
14315
14708
|
}
|
|
14316
14709
|
let deleted = 0;
|
|
14317
14710
|
for (const rel of Object.keys(old.files)) {
|
|
14318
14711
|
if (next[rel]) continue;
|
|
14319
14712
|
if (rel !== DOKU_FILENAME && !rel.startsWith(`${DOKU_DIR}/`)) continue;
|
|
14320
|
-
await
|
|
14713
|
+
await rm6(path14.join(root, rel), { force: true });
|
|
14321
14714
|
deleted++;
|
|
14322
14715
|
}
|
|
14323
|
-
await
|
|
14324
|
-
await
|
|
14716
|
+
await mkdir9(dir, { recursive: true });
|
|
14717
|
+
await writeFile9(
|
|
14325
14718
|
manifestPath,
|
|
14326
14719
|
JSON.stringify({ files: next }, null, 2),
|
|
14327
14720
|
"utf8"
|
|
@@ -14474,8 +14867,8 @@ var init_ponytail_brief = __esm({
|
|
|
14474
14867
|
});
|
|
14475
14868
|
|
|
14476
14869
|
// packages/ingest/src/ponytail-debt.ts
|
|
14477
|
-
function isDebtScannable(
|
|
14478
|
-
return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(
|
|
14870
|
+
function isDebtScannable(path19) {
|
|
14871
|
+
return !/\.(md|mdx|markdown|txt|jsonl|log|lock)$/i.test(path19);
|
|
14479
14872
|
}
|
|
14480
14873
|
function scanPonytailComments(files) {
|
|
14481
14874
|
const out = [];
|
|
@@ -14642,6 +15035,10 @@ var init_src3 = __esm({
|
|
|
14642
15035
|
init_cursor_hooks();
|
|
14643
15036
|
init_codex_hooks();
|
|
14644
15037
|
init_copilot_hooks();
|
|
15038
|
+
init_gemini_hooks();
|
|
15039
|
+
init_kiro_hooks();
|
|
15040
|
+
init_opencode_plugin();
|
|
15041
|
+
init_cline_hooks();
|
|
14645
15042
|
init_commit_links();
|
|
14646
15043
|
init_tool_failure();
|
|
14647
15044
|
init_bootstrap();
|
|
@@ -14727,8 +15124,8 @@ __export(brief_exports, {
|
|
|
14727
15124
|
refreshBrief: () => refreshBrief,
|
|
14728
15125
|
upsertBriefBlock: () => upsertBriefBlock
|
|
14729
15126
|
});
|
|
14730
|
-
import { readFileSync as
|
|
14731
|
-
import
|
|
15127
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync3, existsSync as existsSync11 } from "node:fs";
|
|
15128
|
+
import path15 from "node:path";
|
|
14732
15129
|
function claudeMdWithImport(existing) {
|
|
14733
15130
|
let text = existing;
|
|
14734
15131
|
const start = text.indexOf(BRIEF_START);
|
|
@@ -14782,12 +15179,12 @@ async function refreshBrief(cwd) {
|
|
|
14782
15179
|
`/capture/brief?brain=${project.brainId}`
|
|
14783
15180
|
);
|
|
14784
15181
|
if (!text) return "skipped";
|
|
14785
|
-
const file =
|
|
14786
|
-
const existing =
|
|
15182
|
+
const file = path15.join(root, BRIEF_FILE);
|
|
15183
|
+
const existing = existsSync11(file) ? readFileSync11(file, "utf8") : "";
|
|
14787
15184
|
const next = upsertBriefBlock(existing, text);
|
|
14788
15185
|
if (next !== null) writeFileSync3(file, next);
|
|
14789
|
-
const claudeFile =
|
|
14790
|
-
const claude =
|
|
15186
|
+
const claudeFile = path15.join(root, "CLAUDE.md");
|
|
15187
|
+
const claude = existsSync11(claudeFile) ? readFileSync11(claudeFile, "utf8") : "";
|
|
14791
15188
|
const claudeNext = claudeMdWithImport(claude);
|
|
14792
15189
|
if (claudeNext !== null) writeFileSync3(claudeFile, claudeNext);
|
|
14793
15190
|
return next !== null || claudeNext !== null ? "updated" : "unchanged";
|
|
@@ -14824,9 +15221,9 @@ __export(debt_exports, {
|
|
|
14824
15221
|
scanDebtInRepo: () => scanDebtInRepo
|
|
14825
15222
|
});
|
|
14826
15223
|
import { execFile as execFile3 } from "node:child_process";
|
|
14827
|
-
import { readFile as
|
|
15224
|
+
import { readFile as readFile11 } from "node:fs/promises";
|
|
14828
15225
|
import { promisify as promisify3 } from "node:util";
|
|
14829
|
-
import
|
|
15226
|
+
import path16 from "node:path";
|
|
14830
15227
|
async function scanDebtInRepo(root) {
|
|
14831
15228
|
const { stdout } = await run3("git", ["-C", root, "ls-files"], {
|
|
14832
15229
|
maxBuffer: 16 * 1048576
|
|
@@ -14836,7 +15233,7 @@ async function scanDebtInRepo(root) {
|
|
|
14836
15233
|
const p = rel.trim();
|
|
14837
15234
|
if (!p || !isDebtScannable(p)) continue;
|
|
14838
15235
|
try {
|
|
14839
|
-
const buf = await
|
|
15236
|
+
const buf = await readFile11(path16.join(root, p));
|
|
14840
15237
|
if (buf.length > 512 * 1024 || buf.includes(0)) continue;
|
|
14841
15238
|
files.push({ path: p, content: buf.toString("utf8") });
|
|
14842
15239
|
} catch {
|
|
@@ -15262,11 +15659,11 @@ var init_codex = __esm({
|
|
|
15262
15659
|
...commits.length ? { commits } : {}
|
|
15263
15660
|
});
|
|
15264
15661
|
files.forEach(
|
|
15265
|
-
(
|
|
15662
|
+
(path19, i) => events.push({
|
|
15266
15663
|
...base,
|
|
15267
15664
|
event_id: `file:${id}:${i}`,
|
|
15268
15665
|
kind: "file_changed",
|
|
15269
|
-
path:
|
|
15666
|
+
path: path19
|
|
15270
15667
|
})
|
|
15271
15668
|
);
|
|
15272
15669
|
}
|
|
@@ -15436,106 +15833,637 @@ var init_copilot = __esm({
|
|
|
15436
15833
|
}
|
|
15437
15834
|
});
|
|
15438
15835
|
|
|
15439
|
-
// packages/cli/src/adapters/
|
|
15440
|
-
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
init_cursor();
|
|
15446
|
-
init_codex();
|
|
15447
|
-
init_copilot();
|
|
15448
|
-
ADAPTERS = {
|
|
15449
|
-
[claudeCode2.id]: claudeCode2,
|
|
15450
|
-
[cursor2.id]: cursor2,
|
|
15451
|
-
[codex2.id]: codex2,
|
|
15452
|
-
[copilot2.id]: copilot2
|
|
15453
|
-
};
|
|
15454
|
-
}
|
|
15455
|
-
});
|
|
15456
|
-
|
|
15457
|
-
// packages/cli/src/hook.ts
|
|
15458
|
-
var hook_exports = {};
|
|
15459
|
-
__export(hook_exports, {
|
|
15460
|
-
hookMain: () => hookMain,
|
|
15461
|
-
readSpool: () => readSpool,
|
|
15462
|
-
spoolPath: () => spoolPath
|
|
15463
|
-
});
|
|
15464
|
-
import {
|
|
15465
|
-
appendFileSync,
|
|
15466
|
-
existsSync as existsSync8,
|
|
15467
|
-
mkdirSync as mkdirSync3,
|
|
15468
|
-
readdirSync,
|
|
15469
|
-
readFileSync as readFileSync8,
|
|
15470
|
-
rmSync,
|
|
15471
|
-
statSync as statSync2
|
|
15472
|
-
} from "node:fs";
|
|
15473
|
-
import os5 from "node:os";
|
|
15474
|
-
import path13 from "node:path";
|
|
15475
|
-
function spoolPath(tool, sessionId) {
|
|
15476
|
-
return path13.join(
|
|
15477
|
-
os5.homedir(),
|
|
15478
|
-
".brainjar",
|
|
15479
|
-
"spool",
|
|
15480
|
-
tool,
|
|
15481
|
-
`${sessionId.replace(/[^A-Za-z0-9._-]/g, "_")}.jsonl`
|
|
15482
|
-
);
|
|
15483
|
-
}
|
|
15484
|
-
function readSpool(file) {
|
|
15485
|
-
const out = [];
|
|
15486
|
-
for (const line of readFileSync8(file, "utf8").split("\n")) {
|
|
15487
|
-
if (!line.trim()) continue;
|
|
15488
|
-
try {
|
|
15489
|
-
out.push(clampCaptureEvent(JSON.parse(line)));
|
|
15490
|
-
} catch {
|
|
15491
|
-
}
|
|
15836
|
+
// packages/cli/src/adapters/gemini.ts
|
|
15837
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
15838
|
+
function targetOf4(input) {
|
|
15839
|
+
const i = input ?? {};
|
|
15840
|
+
for (const k of ["command", "file_path"]) {
|
|
15841
|
+
if (typeof i[k] === "string") return i[k];
|
|
15492
15842
|
}
|
|
15493
|
-
return
|
|
15843
|
+
return "";
|
|
15494
15844
|
}
|
|
15495
|
-
function
|
|
15496
|
-
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
if (Date.now() - statSync2(file).mtimeMs > maxAgeMs)
|
|
15501
|
-
rmSync(file, { force: true });
|
|
15502
|
-
}
|
|
15503
|
-
} catch {
|
|
15845
|
+
function commitShas5(v) {
|
|
15846
|
+
const text = typeof v === "string" ? v : JSON.stringify(v ?? "");
|
|
15847
|
+
const out = /* @__PURE__ */ new Set();
|
|
15848
|
+
for (const m of text.matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
15849
|
+
out.add(m[1].toLowerCase());
|
|
15504
15850
|
}
|
|
15851
|
+
return [...out];
|
|
15505
15852
|
}
|
|
15506
|
-
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
)
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15853
|
+
var EDIT_TOOLS3, short6, gemini2;
|
|
15854
|
+
var init_gemini = __esm({
|
|
15855
|
+
"packages/cli/src/adapters/gemini.ts"() {
|
|
15856
|
+
"use strict";
|
|
15857
|
+
init_src();
|
|
15858
|
+
EDIT_TOOLS3 = /* @__PURE__ */ new Set(["write_file", "replace"]);
|
|
15859
|
+
short6 = (s) => createHash7("sha256").update(s).digest("hex").slice(0, 16);
|
|
15860
|
+
gemini2 = {
|
|
15861
|
+
id: "gemini",
|
|
15862
|
+
map(payload, now) {
|
|
15863
|
+
const p = payload;
|
|
15864
|
+
if (!p || typeof p !== "object" || typeof p.session_id !== "string") {
|
|
15865
|
+
return null;
|
|
15866
|
+
}
|
|
15867
|
+
const at = typeof p.timestamp === "string" && !Number.isNaN(Date.parse(p.timestamp)) ? new Date(p.timestamp).toISOString() : now;
|
|
15868
|
+
const base = {
|
|
15869
|
+
v: CAPTURE_EVENT_VERSION,
|
|
15870
|
+
tool: "gemini",
|
|
15871
|
+
session_id: p.session_id,
|
|
15872
|
+
at,
|
|
15873
|
+
cwd: p.cwd ?? null
|
|
15874
|
+
};
|
|
15875
|
+
const events = [];
|
|
15876
|
+
let flush = false;
|
|
15877
|
+
let end = false;
|
|
15878
|
+
switch (p.hook_event_name) {
|
|
15879
|
+
case "SessionStart":
|
|
15880
|
+
events.push({
|
|
15881
|
+
...base,
|
|
15882
|
+
event_id: `start:${p.session_id}`,
|
|
15883
|
+
kind: "session_started"
|
|
15884
|
+
});
|
|
15885
|
+
break;
|
|
15886
|
+
case "BeforeAgent":
|
|
15887
|
+
if (typeof p.prompt === "string" && p.prompt.trim()) {
|
|
15888
|
+
events.push({
|
|
15889
|
+
...base,
|
|
15890
|
+
event_id: `prompt:${short6(at + p.prompt)}`,
|
|
15891
|
+
kind: "prompt",
|
|
15892
|
+
text: p.prompt
|
|
15893
|
+
});
|
|
15894
|
+
}
|
|
15895
|
+
break;
|
|
15896
|
+
case "AfterTool": {
|
|
15897
|
+
const tool = p.tool_name ?? "tool";
|
|
15898
|
+
const target = targetOf4(p.tool_input);
|
|
15899
|
+
const id = short6(at + tool + target);
|
|
15900
|
+
const err = p.tool_response?.error;
|
|
15901
|
+
if (err && typeof err === "object") {
|
|
15902
|
+
const error = String(err.message ?? err.type ?? "tool error");
|
|
15903
|
+
events.push({
|
|
15904
|
+
...base,
|
|
15905
|
+
event_id: `fail:${id}`,
|
|
15906
|
+
kind: "tool_failed",
|
|
15907
|
+
tool_name: tool,
|
|
15908
|
+
...target ? { target } : {},
|
|
15909
|
+
error
|
|
15910
|
+
});
|
|
15911
|
+
break;
|
|
15912
|
+
}
|
|
15913
|
+
const commits = commitShas5(p.tool_response?.llmContent);
|
|
15914
|
+
events.push({
|
|
15915
|
+
...base,
|
|
15916
|
+
event_id: `use:${id}`,
|
|
15917
|
+
kind: "tool_used",
|
|
15918
|
+
tool_name: tool,
|
|
15919
|
+
...target ? { target } : {},
|
|
15920
|
+
...commits.length ? { commits } : {}
|
|
15921
|
+
});
|
|
15922
|
+
if (EDIT_TOOLS3.has(tool) && target) {
|
|
15923
|
+
events.push({
|
|
15924
|
+
...base,
|
|
15925
|
+
event_id: `file:${id}`,
|
|
15926
|
+
kind: "file_changed",
|
|
15927
|
+
path: target
|
|
15928
|
+
});
|
|
15929
|
+
}
|
|
15930
|
+
break;
|
|
15931
|
+
}
|
|
15932
|
+
case "AfterAgent":
|
|
15933
|
+
flush = true;
|
|
15934
|
+
break;
|
|
15935
|
+
case "SessionEnd":
|
|
15936
|
+
events.push({
|
|
15937
|
+
...base,
|
|
15938
|
+
event_id: `end:${p.session_id}`,
|
|
15939
|
+
kind: "session_ended",
|
|
15940
|
+
...p.reason ? { reason: p.reason } : {}
|
|
15941
|
+
});
|
|
15942
|
+
flush = true;
|
|
15943
|
+
end = true;
|
|
15944
|
+
break;
|
|
15945
|
+
default:
|
|
15946
|
+
return null;
|
|
15947
|
+
}
|
|
15948
|
+
return { sessionId: p.session_id, cwd: p.cwd ?? null, events, flush, end };
|
|
15949
|
+
}
|
|
15950
|
+
};
|
|
15951
|
+
}
|
|
15952
|
+
});
|
|
15953
|
+
|
|
15954
|
+
// packages/cli/src/adapters/kiro.ts
|
|
15955
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
15956
|
+
function targetOf5(input) {
|
|
15957
|
+
const i = input ?? {};
|
|
15958
|
+
for (const k of ["command", "cmd", "path", "file_path", "filePath"]) {
|
|
15959
|
+
if (typeof i[k] === "string") return i[k];
|
|
15960
|
+
}
|
|
15961
|
+
return "";
|
|
15962
|
+
}
|
|
15963
|
+
function commitShas6(v) {
|
|
15964
|
+
const text = typeof v === "string" ? v : JSON.stringify(v ?? "");
|
|
15965
|
+
const out = /* @__PURE__ */ new Set();
|
|
15966
|
+
for (const m of text.matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
15967
|
+
out.add(m[1].toLowerCase());
|
|
15968
|
+
}
|
|
15969
|
+
return [...out];
|
|
15970
|
+
}
|
|
15971
|
+
function failureOf2(response) {
|
|
15972
|
+
if (!response || typeof response !== "object") return null;
|
|
15973
|
+
const r = response;
|
|
15974
|
+
if (r.success !== false) return null;
|
|
15975
|
+
const text = [r.error, r.result, r.message].find(
|
|
15976
|
+
(t) => typeof t === "string" && t.trim()
|
|
15977
|
+
);
|
|
15978
|
+
return (typeof text === "string" ? text : JSON.stringify(r)).trim();
|
|
15979
|
+
}
|
|
15980
|
+
var EDIT_TOOLS4, short7, EVENTS5, kiro2;
|
|
15981
|
+
var init_kiro = __esm({
|
|
15982
|
+
"packages/cli/src/adapters/kiro.ts"() {
|
|
15983
|
+
"use strict";
|
|
15984
|
+
init_src();
|
|
15985
|
+
EDIT_TOOLS4 = /* @__PURE__ */ new Set(["fs_write", "write", "str_replace", "fs_append"]);
|
|
15986
|
+
short7 = (s) => createHash8("sha256").update(s).digest("hex").slice(0, 16);
|
|
15987
|
+
EVENTS5 = {
|
|
15988
|
+
agentSpawn: "start",
|
|
15989
|
+
sessionStart: "start",
|
|
15990
|
+
SessionStart: "start",
|
|
15991
|
+
userPromptSubmit: "prompt",
|
|
15992
|
+
UserPromptSubmit: "prompt",
|
|
15993
|
+
promptSubmit: "prompt",
|
|
15994
|
+
postToolUse: "tool",
|
|
15995
|
+
PostToolUse: "tool",
|
|
15996
|
+
postFileSave: "file",
|
|
15997
|
+
PostFileSave: "file",
|
|
15998
|
+
postFileCreate: "file",
|
|
15999
|
+
PostFileCreate: "file",
|
|
16000
|
+
stop: "stop",
|
|
16001
|
+
Stop: "stop",
|
|
16002
|
+
agentStop: "stop"
|
|
16003
|
+
};
|
|
16004
|
+
kiro2 = {
|
|
16005
|
+
id: "kiro",
|
|
16006
|
+
map(payload, now) {
|
|
16007
|
+
const p = payload;
|
|
16008
|
+
if (!p || typeof p !== "object" || typeof p.session_id !== "string") {
|
|
16009
|
+
return null;
|
|
16010
|
+
}
|
|
16011
|
+
const base = {
|
|
16012
|
+
v: CAPTURE_EVENT_VERSION,
|
|
16013
|
+
tool: "kiro",
|
|
16014
|
+
session_id: p.session_id,
|
|
16015
|
+
at: now,
|
|
16016
|
+
cwd: p.cwd ?? null
|
|
16017
|
+
};
|
|
16018
|
+
const events = [];
|
|
16019
|
+
let flush = false;
|
|
16020
|
+
switch (EVENTS5[p.hook_event_name ?? ""]) {
|
|
16021
|
+
case "start":
|
|
16022
|
+
events.push({
|
|
16023
|
+
...base,
|
|
16024
|
+
event_id: `start:${p.session_id}`,
|
|
16025
|
+
kind: "session_started"
|
|
16026
|
+
});
|
|
16027
|
+
break;
|
|
16028
|
+
case "prompt":
|
|
16029
|
+
if (typeof p.prompt === "string" && p.prompt.trim()) {
|
|
16030
|
+
events.push({
|
|
16031
|
+
...base,
|
|
16032
|
+
event_id: `prompt:${short7(now + p.prompt)}`,
|
|
16033
|
+
kind: "prompt",
|
|
16034
|
+
text: p.prompt
|
|
16035
|
+
});
|
|
16036
|
+
}
|
|
16037
|
+
break;
|
|
16038
|
+
case "tool": {
|
|
16039
|
+
const tool = p.tool_name ?? "tool";
|
|
16040
|
+
const target = targetOf5(p.tool_input);
|
|
16041
|
+
const id = short7(now + tool + target);
|
|
16042
|
+
const error = failureOf2(p.tool_response);
|
|
16043
|
+
if (error) {
|
|
16044
|
+
events.push({
|
|
16045
|
+
...base,
|
|
16046
|
+
event_id: `fail:${id}`,
|
|
16047
|
+
kind: "tool_failed",
|
|
16048
|
+
tool_name: tool,
|
|
16049
|
+
...target ? { target } : {},
|
|
16050
|
+
error
|
|
16051
|
+
});
|
|
16052
|
+
break;
|
|
16053
|
+
}
|
|
16054
|
+
const commits = commitShas6(p.tool_response);
|
|
16055
|
+
events.push({
|
|
16056
|
+
...base,
|
|
16057
|
+
event_id: `use:${id}`,
|
|
16058
|
+
kind: "tool_used",
|
|
16059
|
+
tool_name: tool,
|
|
16060
|
+
...target ? { target } : {},
|
|
16061
|
+
...commits.length ? { commits } : {}
|
|
16062
|
+
});
|
|
16063
|
+
if (EDIT_TOOLS4.has(tool) && target) {
|
|
16064
|
+
events.push({
|
|
16065
|
+
...base,
|
|
16066
|
+
event_id: `file:${id}`,
|
|
16067
|
+
kind: "file_changed",
|
|
16068
|
+
path: target
|
|
16069
|
+
});
|
|
16070
|
+
}
|
|
16071
|
+
break;
|
|
16072
|
+
}
|
|
16073
|
+
case "file":
|
|
16074
|
+
if (typeof p.file_path === "string" && p.file_path) {
|
|
16075
|
+
events.push({
|
|
16076
|
+
...base,
|
|
16077
|
+
event_id: `file:${short7(now + p.file_path)}`,
|
|
16078
|
+
kind: "file_changed",
|
|
16079
|
+
path: p.file_path
|
|
16080
|
+
});
|
|
16081
|
+
}
|
|
16082
|
+
break;
|
|
16083
|
+
case "stop":
|
|
16084
|
+
flush = true;
|
|
16085
|
+
break;
|
|
16086
|
+
default:
|
|
16087
|
+
return null;
|
|
16088
|
+
}
|
|
16089
|
+
return {
|
|
16090
|
+
sessionId: p.session_id,
|
|
16091
|
+
cwd: p.cwd ?? null,
|
|
16092
|
+
events,
|
|
16093
|
+
flush,
|
|
16094
|
+
end: false
|
|
16095
|
+
};
|
|
16096
|
+
}
|
|
16097
|
+
};
|
|
16098
|
+
}
|
|
16099
|
+
});
|
|
16100
|
+
|
|
16101
|
+
// packages/cli/src/adapters/opencode.ts
|
|
16102
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
16103
|
+
function commitShas7(text) {
|
|
16104
|
+
const out = /* @__PURE__ */ new Set();
|
|
16105
|
+
for (const m of (text ?? "").matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
16106
|
+
out.add(m[1].toLowerCase());
|
|
16107
|
+
}
|
|
16108
|
+
return [...out];
|
|
16109
|
+
}
|
|
16110
|
+
var EDIT_TOOLS5, short8, opencode2;
|
|
16111
|
+
var init_opencode = __esm({
|
|
16112
|
+
"packages/cli/src/adapters/opencode.ts"() {
|
|
16113
|
+
"use strict";
|
|
16114
|
+
init_src();
|
|
16115
|
+
EDIT_TOOLS5 = /* @__PURE__ */ new Set(["edit", "write"]);
|
|
16116
|
+
short8 = (s) => createHash9("sha256").update(s).digest("hex").slice(0, 16);
|
|
16117
|
+
opencode2 = {
|
|
16118
|
+
id: "opencode",
|
|
16119
|
+
map(payload, now) {
|
|
16120
|
+
const p = payload;
|
|
16121
|
+
if (!p || typeof p !== "object" || p.source !== "opencode" || typeof p.session_id !== "string" || !p.session_id) {
|
|
16122
|
+
return null;
|
|
16123
|
+
}
|
|
16124
|
+
const base = {
|
|
16125
|
+
v: CAPTURE_EVENT_VERSION,
|
|
16126
|
+
tool: "opencode",
|
|
16127
|
+
session_id: p.session_id,
|
|
16128
|
+
at: now,
|
|
16129
|
+
cwd: p.cwd ?? null
|
|
16130
|
+
};
|
|
16131
|
+
const events = [];
|
|
16132
|
+
let flush = false;
|
|
16133
|
+
const target = p.target?.trim() ?? "";
|
|
16134
|
+
const tool = p.tool_name ?? "tool";
|
|
16135
|
+
switch (p.kind) {
|
|
16136
|
+
case "session_started":
|
|
16137
|
+
events.push({
|
|
16138
|
+
...base,
|
|
16139
|
+
event_id: `start:${p.session_id}`,
|
|
16140
|
+
kind: "session_started"
|
|
16141
|
+
});
|
|
16142
|
+
break;
|
|
16143
|
+
case "prompt":
|
|
16144
|
+
if (typeof p.text === "string" && p.text.trim()) {
|
|
16145
|
+
events.push({
|
|
16146
|
+
...base,
|
|
16147
|
+
event_id: `prompt:${short8(now + p.text)}`,
|
|
16148
|
+
kind: "prompt",
|
|
16149
|
+
text: p.text
|
|
16150
|
+
});
|
|
16151
|
+
}
|
|
16152
|
+
break;
|
|
16153
|
+
case "tool_used": {
|
|
16154
|
+
const id = short8(now + tool + target);
|
|
16155
|
+
const commits = commitShas7(p.output);
|
|
16156
|
+
events.push({
|
|
16157
|
+
...base,
|
|
16158
|
+
event_id: `use:${id}`,
|
|
16159
|
+
kind: "tool_used",
|
|
16160
|
+
tool_name: tool,
|
|
16161
|
+
...target ? { target } : {},
|
|
16162
|
+
...commits.length ? { commits } : {}
|
|
16163
|
+
});
|
|
16164
|
+
if (EDIT_TOOLS5.has(tool) && target) {
|
|
16165
|
+
events.push({
|
|
16166
|
+
...base,
|
|
16167
|
+
event_id: `file:${id}`,
|
|
16168
|
+
kind: "file_changed",
|
|
16169
|
+
path: target
|
|
16170
|
+
});
|
|
16171
|
+
}
|
|
16172
|
+
break;
|
|
16173
|
+
}
|
|
16174
|
+
case "tool_failed": {
|
|
16175
|
+
const error = (p.error ?? "").trim();
|
|
16176
|
+
if (!error) break;
|
|
16177
|
+
events.push({
|
|
16178
|
+
...base,
|
|
16179
|
+
event_id: `fail:${short8(now + tool + target + error)}`,
|
|
16180
|
+
kind: "tool_failed",
|
|
16181
|
+
tool_name: tool,
|
|
16182
|
+
...target ? { target } : {},
|
|
16183
|
+
error
|
|
16184
|
+
});
|
|
16185
|
+
break;
|
|
16186
|
+
}
|
|
16187
|
+
case "file_changed":
|
|
16188
|
+
if (typeof p.path === "string" && p.path) {
|
|
16189
|
+
events.push({
|
|
16190
|
+
...base,
|
|
16191
|
+
event_id: `file:${short8(now + p.path)}`,
|
|
16192
|
+
kind: "file_changed",
|
|
16193
|
+
path: p.path
|
|
16194
|
+
});
|
|
16195
|
+
}
|
|
16196
|
+
break;
|
|
16197
|
+
case "turn_ended":
|
|
16198
|
+
flush = true;
|
|
16199
|
+
break;
|
|
16200
|
+
default:
|
|
16201
|
+
return null;
|
|
16202
|
+
}
|
|
16203
|
+
return {
|
|
16204
|
+
sessionId: p.session_id,
|
|
16205
|
+
cwd: p.cwd ?? null,
|
|
16206
|
+
events,
|
|
16207
|
+
flush,
|
|
16208
|
+
end: false
|
|
16209
|
+
};
|
|
16210
|
+
}
|
|
16211
|
+
};
|
|
16212
|
+
}
|
|
16213
|
+
});
|
|
16214
|
+
|
|
16215
|
+
// packages/cli/src/adapters/cline.ts
|
|
16216
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
16217
|
+
function targetOf6(params) {
|
|
16218
|
+
const i = params ?? {};
|
|
16219
|
+
for (const k of ["command", "path"]) {
|
|
16220
|
+
if (typeof i[k] === "string") return i[k];
|
|
16221
|
+
}
|
|
16222
|
+
return "";
|
|
16223
|
+
}
|
|
16224
|
+
function commitShas8(text) {
|
|
16225
|
+
const out = /* @__PURE__ */ new Set();
|
|
16226
|
+
for (const m of (text ?? "").matchAll(/\[[^\]\n]* ([0-9a-f]{7,40})\]/g)) {
|
|
16227
|
+
out.add(m[1].toLowerCase());
|
|
16228
|
+
}
|
|
16229
|
+
return [...out];
|
|
16230
|
+
}
|
|
16231
|
+
function stamp2(ts, now) {
|
|
16232
|
+
const n = typeof ts === "string" && /^\d+$/.test(ts) ? Number(ts) : ts;
|
|
16233
|
+
if (typeof n === "number" && Number.isFinite(n))
|
|
16234
|
+
return new Date(n).toISOString();
|
|
16235
|
+
if (typeof ts === "string" && !Number.isNaN(Date.parse(ts)))
|
|
16236
|
+
return new Date(ts).toISOString();
|
|
16237
|
+
return now;
|
|
16238
|
+
}
|
|
16239
|
+
function clineContextEnvelope(text) {
|
|
16240
|
+
return JSON.stringify({ cancel: false, contextModification: text });
|
|
16241
|
+
}
|
|
16242
|
+
var EDIT_TOOLS6, short9, CLINE_REPLY, cline2;
|
|
16243
|
+
var init_cline = __esm({
|
|
16244
|
+
"packages/cli/src/adapters/cline.ts"() {
|
|
16245
|
+
"use strict";
|
|
16246
|
+
init_src();
|
|
16247
|
+
EDIT_TOOLS6 = /* @__PURE__ */ new Set(["write_to_file", "replace_in_file", "apply_patch"]);
|
|
16248
|
+
short9 = (s) => createHash10("sha256").update(s).digest("hex").slice(0, 16);
|
|
16249
|
+
CLINE_REPLY = JSON.stringify({ cancel: false });
|
|
16250
|
+
cline2 = {
|
|
16251
|
+
id: "cline",
|
|
16252
|
+
map(payload, now) {
|
|
16253
|
+
const p = payload;
|
|
16254
|
+
if (!p || typeof p !== "object" || typeof p.taskId !== "string") {
|
|
16255
|
+
return null;
|
|
16256
|
+
}
|
|
16257
|
+
const at = stamp2(p.timestamp, now);
|
|
16258
|
+
const cwd = p.workspaceRoots?.[0] ?? null;
|
|
16259
|
+
const base = {
|
|
16260
|
+
v: CAPTURE_EVENT_VERSION,
|
|
16261
|
+
tool: "cline",
|
|
16262
|
+
session_id: p.taskId,
|
|
16263
|
+
at,
|
|
16264
|
+
cwd
|
|
16265
|
+
};
|
|
16266
|
+
const events = [];
|
|
16267
|
+
let flush = false;
|
|
16268
|
+
let end = false;
|
|
16269
|
+
switch (p.hookName) {
|
|
16270
|
+
case "TaskStart":
|
|
16271
|
+
case "TaskResume":
|
|
16272
|
+
events.push({
|
|
16273
|
+
...base,
|
|
16274
|
+
event_id: `start:${p.taskId}`,
|
|
16275
|
+
kind: "session_started"
|
|
16276
|
+
});
|
|
16277
|
+
break;
|
|
16278
|
+
case "UserPromptSubmit": {
|
|
16279
|
+
const prompt = p.userPromptSubmit?.prompt;
|
|
16280
|
+
if (typeof prompt === "string" && prompt.trim()) {
|
|
16281
|
+
events.push({
|
|
16282
|
+
...base,
|
|
16283
|
+
event_id: `prompt:${short9(at + prompt)}`,
|
|
16284
|
+
kind: "prompt",
|
|
16285
|
+
text: prompt
|
|
16286
|
+
});
|
|
16287
|
+
}
|
|
16288
|
+
break;
|
|
16289
|
+
}
|
|
16290
|
+
case "PostToolUse": {
|
|
16291
|
+
const t = p.postToolUse ?? {};
|
|
16292
|
+
const tool = t.toolName ?? "tool";
|
|
16293
|
+
const target = targetOf6(t.parameters);
|
|
16294
|
+
const id = short9(at + tool + target);
|
|
16295
|
+
if (t.success !== true) {
|
|
16296
|
+
const error = (t.result ?? "").trim() || "tool failed";
|
|
16297
|
+
events.push({
|
|
16298
|
+
...base,
|
|
16299
|
+
event_id: `fail:${id}`,
|
|
16300
|
+
kind: "tool_failed",
|
|
16301
|
+
tool_name: tool,
|
|
16302
|
+
...target ? { target } : {},
|
|
16303
|
+
error
|
|
16304
|
+
});
|
|
16305
|
+
break;
|
|
16306
|
+
}
|
|
16307
|
+
const commits = commitShas8(t.result);
|
|
16308
|
+
events.push({
|
|
16309
|
+
...base,
|
|
16310
|
+
event_id: `use:${id}`,
|
|
16311
|
+
kind: "tool_used",
|
|
16312
|
+
tool_name: tool,
|
|
16313
|
+
...target ? { target } : {},
|
|
16314
|
+
...commits.length ? { commits } : {}
|
|
16315
|
+
});
|
|
16316
|
+
if (EDIT_TOOLS6.has(tool) && target) {
|
|
16317
|
+
events.push({
|
|
16318
|
+
...base,
|
|
16319
|
+
event_id: `file:${id}`,
|
|
16320
|
+
kind: "file_changed",
|
|
16321
|
+
path: target
|
|
16322
|
+
});
|
|
16323
|
+
}
|
|
16324
|
+
break;
|
|
16325
|
+
}
|
|
16326
|
+
case "TaskComplete":
|
|
16327
|
+
case "TaskCancel":
|
|
16328
|
+
events.push({
|
|
16329
|
+
...base,
|
|
16330
|
+
event_id: `end:${p.taskId}`,
|
|
16331
|
+
kind: "session_ended",
|
|
16332
|
+
reason: p.hookName === "TaskCancel" ? "cancelled" : "completed"
|
|
16333
|
+
});
|
|
16334
|
+
flush = true;
|
|
16335
|
+
end = true;
|
|
16336
|
+
break;
|
|
16337
|
+
default:
|
|
16338
|
+
return null;
|
|
16339
|
+
}
|
|
16340
|
+
return {
|
|
16341
|
+
sessionId: p.taskId,
|
|
16342
|
+
cwd,
|
|
16343
|
+
events,
|
|
16344
|
+
flush,
|
|
16345
|
+
end,
|
|
16346
|
+
reply: CLINE_REPLY
|
|
16347
|
+
};
|
|
16348
|
+
}
|
|
16349
|
+
};
|
|
16350
|
+
}
|
|
16351
|
+
});
|
|
16352
|
+
|
|
16353
|
+
// packages/cli/src/adapters/index.ts
|
|
16354
|
+
var ADAPTERS;
|
|
16355
|
+
var init_adapters = __esm({
|
|
16356
|
+
"packages/cli/src/adapters/index.ts"() {
|
|
16357
|
+
"use strict";
|
|
16358
|
+
init_claude_code();
|
|
16359
|
+
init_cursor();
|
|
16360
|
+
init_codex();
|
|
16361
|
+
init_copilot();
|
|
16362
|
+
init_gemini();
|
|
16363
|
+
init_kiro();
|
|
16364
|
+
init_opencode();
|
|
16365
|
+
init_cline();
|
|
16366
|
+
ADAPTERS = {
|
|
16367
|
+
[claudeCode2.id]: claudeCode2,
|
|
16368
|
+
[cursor2.id]: cursor2,
|
|
16369
|
+
[codex2.id]: codex2,
|
|
16370
|
+
[copilot2.id]: copilot2,
|
|
16371
|
+
[gemini2.id]: gemini2,
|
|
16372
|
+
[kiro2.id]: kiro2,
|
|
16373
|
+
[opencode2.id]: opencode2,
|
|
16374
|
+
[cline2.id]: cline2
|
|
16375
|
+
};
|
|
16376
|
+
}
|
|
16377
|
+
});
|
|
16378
|
+
|
|
16379
|
+
// packages/cli/src/hook.ts
|
|
16380
|
+
var hook_exports = {};
|
|
16381
|
+
__export(hook_exports, {
|
|
16382
|
+
hookMain: () => hookMain,
|
|
16383
|
+
parseHookArgs: () => parseHookArgs,
|
|
16384
|
+
readSpool: () => readSpool,
|
|
16385
|
+
spoolPath: () => spoolPath
|
|
16386
|
+
});
|
|
16387
|
+
import {
|
|
16388
|
+
appendFileSync,
|
|
16389
|
+
existsSync as existsSync12,
|
|
16390
|
+
mkdirSync as mkdirSync3,
|
|
16391
|
+
readdirSync,
|
|
16392
|
+
readFileSync as readFileSync12,
|
|
16393
|
+
rmSync,
|
|
16394
|
+
statSync as statSync2
|
|
16395
|
+
} from "node:fs";
|
|
16396
|
+
import os5 from "node:os";
|
|
16397
|
+
import path17 from "node:path";
|
|
16398
|
+
function spoolPath(tool, sessionId) {
|
|
16399
|
+
return path17.join(
|
|
16400
|
+
os5.homedir(),
|
|
16401
|
+
".brainjar",
|
|
16402
|
+
"spool",
|
|
16403
|
+
tool,
|
|
16404
|
+
`${sessionId.replace(/[^A-Za-z0-9._-]/g, "_")}.jsonl`
|
|
16405
|
+
);
|
|
16406
|
+
}
|
|
16407
|
+
function readSpool(file) {
|
|
16408
|
+
const out = [];
|
|
16409
|
+
for (const line of readFileSync12(file, "utf8").split("\n")) {
|
|
16410
|
+
if (!line.trim()) continue;
|
|
16411
|
+
try {
|
|
16412
|
+
out.push(clampCaptureEvent(JSON.parse(line)));
|
|
16413
|
+
} catch {
|
|
16414
|
+
}
|
|
16415
|
+
}
|
|
16416
|
+
return out;
|
|
16417
|
+
}
|
|
16418
|
+
function pruneSpool(dir, keep, maxAgeMs = 7 * 864e5) {
|
|
16419
|
+
try {
|
|
16420
|
+
for (const f of readdirSync(dir)) {
|
|
16421
|
+
const file = path17.join(dir, f);
|
|
16422
|
+
if (file === keep) continue;
|
|
16423
|
+
if (Date.now() - statSync2(file).mtimeMs > maxAgeMs)
|
|
16424
|
+
rmSync(file, { force: true });
|
|
16425
|
+
}
|
|
16426
|
+
} catch {
|
|
16427
|
+
}
|
|
16428
|
+
}
|
|
16429
|
+
function parseHookArgs(argv) {
|
|
16430
|
+
const eventFlag = argv.indexOf("--event");
|
|
16431
|
+
const eventIndex = eventFlag >= 0 ? eventFlag + 1 : -1;
|
|
16432
|
+
const event = eventIndex >= 0 ? argv[eventIndex] : void 0;
|
|
16433
|
+
const tool = argv.find((a, i) => !a.startsWith("--") && i !== eventIndex);
|
|
16434
|
+
return { tool, event };
|
|
16435
|
+
}
|
|
16436
|
+
async function hookMain(argv) {
|
|
16437
|
+
const { tool, event } = parseHookArgs(argv);
|
|
16438
|
+
const adapter = tool ? ADAPTERS[tool] : void 0;
|
|
16439
|
+
try {
|
|
16440
|
+
if (!adapter) {
|
|
16441
|
+
console.error(
|
|
16442
|
+
`brainjar hook: unknown tool "${tool ?? ""}". Known: ${Object.keys(ADAPTERS).join(", ")}`
|
|
16443
|
+
);
|
|
16444
|
+
return;
|
|
16445
|
+
}
|
|
16446
|
+
let payload = null;
|
|
16447
|
+
try {
|
|
16448
|
+
payload = JSON.parse(readFileSync12(0, "utf8"));
|
|
16449
|
+
} catch {
|
|
16450
|
+
return;
|
|
16451
|
+
}
|
|
16452
|
+
const out = adapter.map(payload, (/* @__PURE__ */ new Date()).toISOString(), { event });
|
|
16453
|
+
if (!out) return;
|
|
16454
|
+
if (out.reply) process.stdout.write(out.reply);
|
|
16455
|
+
const file = spoolPath(adapter.id, out.sessionId);
|
|
16456
|
+
if (out.events.length) {
|
|
16457
|
+
mkdirSync3(path17.dirname(file), { recursive: true });
|
|
16458
|
+
appendFileSync(
|
|
16459
|
+
file,
|
|
16460
|
+
out.events.map((e) => JSON.stringify(clampCaptureEvent(e))).join("\n") + "\n"
|
|
16461
|
+
);
|
|
16462
|
+
}
|
|
16463
|
+
if (!out.flush) return;
|
|
16464
|
+
const root = await repoRoot(out.cwd ?? process.cwd());
|
|
16465
|
+
const events = existsSync12(file) ? readSpool(file) : [];
|
|
16466
|
+
if (events.length) {
|
|
15539
16467
|
const r = await captureApi("/capture/events", {
|
|
15540
16468
|
method: "POST",
|
|
15541
16469
|
body: {
|
|
@@ -15555,7 +16483,7 @@ async function hookMain(argv) {
|
|
|
15555
16483
|
}
|
|
15556
16484
|
}
|
|
15557
16485
|
if (out.end) rmSync(file, { force: true });
|
|
15558
|
-
pruneSpool(
|
|
16486
|
+
pruneSpool(path17.dirname(file), file);
|
|
15559
16487
|
await syncRepo(root);
|
|
15560
16488
|
} catch (e) {
|
|
15561
16489
|
console.error("brainjar hook:", e instanceof Error ? e.message : e);
|
|
@@ -15678,7 +16606,7 @@ __export(init_exports, {
|
|
|
15678
16606
|
init: () => init,
|
|
15679
16607
|
initMain: () => initMain
|
|
15680
16608
|
});
|
|
15681
|
-
import { readFile as
|
|
16609
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
15682
16610
|
async function init(argv) {
|
|
15683
16611
|
const skipTranscripts = argv.includes("--no-transcripts");
|
|
15684
16612
|
const flag = (f) => argv.includes(f) ? argv[argv.indexOf(f) + 1] ?? null : null;
|
|
@@ -15763,7 +16691,7 @@ async function init(argv) {
|
|
|
15763
16691
|
let skipped = 0;
|
|
15764
16692
|
for (const f of files) {
|
|
15765
16693
|
try {
|
|
15766
|
-
const { text, truncated } = capTranscript(await
|
|
16694
|
+
const { text, truncated } = capTranscript(await readFile12(f, "utf8"));
|
|
15767
16695
|
if (truncated) truncatedCount++;
|
|
15768
16696
|
const r = await captureApi("/capture/transcript", {
|
|
15769
16697
|
method: "POST",
|
|
@@ -15786,7 +16714,7 @@ async function init(argv) {
|
|
|
15786
16714
|
const tools = detectedTools(root);
|
|
15787
16715
|
if (tools.length === 0) {
|
|
15788
16716
|
ok(
|
|
15789
|
-
"no coding agent detected (looked for Claude Code, Cursor, Codex, GitHub Copilot); rerun init after installing one"
|
|
16717
|
+
"no coding agent detected (looked for Claude Code, Cursor, Codex, GitHub Copilot, Gemini CLI, Kiro, OpenCode, Cline); rerun init after installing one"
|
|
15790
16718
|
);
|
|
15791
16719
|
}
|
|
15792
16720
|
for (const tool of tools) {
|
|
@@ -15853,7 +16781,7 @@ __export(constraints_exports, {
|
|
|
15853
16781
|
constraintsMain: () => constraintsMain,
|
|
15854
16782
|
sessionStartEnvelope: () => sessionStartEnvelope
|
|
15855
16783
|
});
|
|
15856
|
-
import { readFileSync as
|
|
16784
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
15857
16785
|
function sessionStartEnvelope(text) {
|
|
15858
16786
|
return JSON.stringify({
|
|
15859
16787
|
hookSpecificOutput: {
|
|
@@ -15871,8 +16799,8 @@ async function constraintsMain(argv) {
|
|
|
15871
16799
|
if (hook) {
|
|
15872
16800
|
let cwd;
|
|
15873
16801
|
try {
|
|
15874
|
-
const payload = JSON.parse(
|
|
15875
|
-
cwd = payload.cwd;
|
|
16802
|
+
const payload = JSON.parse(readFileSync13(0, "utf8"));
|
|
16803
|
+
cwd = payload.cwd ?? payload.workspace_roots?.[0] ?? payload.workspaceRoots?.[0];
|
|
15876
16804
|
} catch {
|
|
15877
16805
|
}
|
|
15878
16806
|
root = await repoRoot(cwd ?? process.cwd());
|
|
@@ -15893,8 +16821,9 @@ async function constraintsMain(argv) {
|
|
|
15893
16821
|
}
|
|
15894
16822
|
return;
|
|
15895
16823
|
}
|
|
15896
|
-
const envelope = argv.includes("--copilot") ? copilotContextEnvelope : sessionStartEnvelope;
|
|
16824
|
+
const envelope = argv.includes("--copilot") ? copilotContextEnvelope : argv.includes("--cline") ? clineContextEnvelope : argv.includes("--plain") ? (text) => text : sessionStartEnvelope;
|
|
15897
16825
|
if (r.text) console.log(hook ? envelope(r.text) : r.text);
|
|
16826
|
+
else if (hook && argv.includes("--cline")) console.log(CLINE_REPLY);
|
|
15898
16827
|
else if (!hook)
|
|
15899
16828
|
console.log("No recorded failed approaches or rules for this Brain.");
|
|
15900
16829
|
if (hook) {
|
|
@@ -15915,6 +16844,7 @@ var init_constraints = __esm({
|
|
|
15915
16844
|
init_src3();
|
|
15916
16845
|
init_api_client();
|
|
15917
16846
|
init_copilot();
|
|
16847
|
+
init_cline();
|
|
15918
16848
|
}
|
|
15919
16849
|
});
|
|
15920
16850
|
|
|
@@ -16066,8 +16996,8 @@ __export(deepscan_exports, {
|
|
|
16066
16996
|
deepscanMain: () => deepscanMain,
|
|
16067
16997
|
selectDeepScanFiles: () => selectDeepScanFiles
|
|
16068
16998
|
});
|
|
16069
|
-
import { readdir as readdir3, readFile as
|
|
16070
|
-
import
|
|
16999
|
+
import { readdir as readdir3, readFile as readFile13, stat as stat2 } from "node:fs/promises";
|
|
17000
|
+
import path18 from "node:path";
|
|
16071
17001
|
function selectDeepScanFiles(candidates, graph, maxFiles = MAX_FILES) {
|
|
16072
17002
|
const picked = [];
|
|
16073
17003
|
const has = /* @__PURE__ */ new Set();
|
|
@@ -16104,7 +17034,7 @@ async function walkCandidates(root) {
|
|
|
16104
17034
|
for (const e of entries) {
|
|
16105
17035
|
if (!e.isFile()) continue;
|
|
16106
17036
|
const parent = e.parentPath ?? e.path ?? root;
|
|
16107
|
-
const rel =
|
|
17037
|
+
const rel = path18.relative(root, path18.join(parent, e.name)).split(path18.sep).join("/");
|
|
16108
17038
|
if (rel.split("/").some((seg) => SKIP_DIRS.has(seg))) continue;
|
|
16109
17039
|
out.push(rel);
|
|
16110
17040
|
}
|
|
@@ -16129,10 +17059,10 @@ async function deepscanMain() {
|
|
|
16129
17059
|
const artifacts = [];
|
|
16130
17060
|
for (const rel of files) {
|
|
16131
17061
|
try {
|
|
16132
|
-
const abs =
|
|
17062
|
+
const abs = path18.join(root, rel);
|
|
16133
17063
|
const info = await stat2(abs);
|
|
16134
17064
|
if (info.size > RAW_SKIP_BYTES) continue;
|
|
16135
|
-
const content = await
|
|
17065
|
+
const content = await readFile13(abs, "utf8");
|
|
16136
17066
|
artifacts.push({
|
|
16137
17067
|
source: "deepscan",
|
|
16138
17068
|
kind: "source_file",
|