backthread 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-bundle/backthread.js +86 -0
- package/package.json +1 -1
|
@@ -7380,6 +7380,89 @@ function sessionTimestamp(records) {
|
|
|
7380
7380
|
}
|
|
7381
7381
|
return latestIso;
|
|
7382
7382
|
}
|
|
7383
|
+
function stripLeadingSlashes(p) {
|
|
7384
|
+
let i = 0;
|
|
7385
|
+
while (i < p.length && p[i] === "/") i += 1;
|
|
7386
|
+
return p.slice(i);
|
|
7387
|
+
}
|
|
7388
|
+
function isAbsolute(p) {
|
|
7389
|
+
return p.startsWith("/");
|
|
7390
|
+
}
|
|
7391
|
+
function isForeignRelativePath(p) {
|
|
7392
|
+
if (p.startsWith("~")) return true;
|
|
7393
|
+
if (p.startsWith("\\")) return true;
|
|
7394
|
+
if (/^[A-Za-z]:[\\/]/.test(p)) return true;
|
|
7395
|
+
const stripped = p.replace(/^(?:\.[\\/])+/, "");
|
|
7396
|
+
return /^\.\.(?:[\\/]|$)/.test(stripped);
|
|
7397
|
+
}
|
|
7398
|
+
function relativizeUnder(abs, root) {
|
|
7399
|
+
const trimmedRoot = root.replace(/\/+$/, "");
|
|
7400
|
+
if (trimmedRoot.length === 0) return null;
|
|
7401
|
+
if (abs === trimmedRoot) return "";
|
|
7402
|
+
const prefix = trimmedRoot + "/";
|
|
7403
|
+
if (!abs.startsWith(prefix)) return null;
|
|
7404
|
+
return stripLeadingSlashes(abs.slice(trimmedRoot.length));
|
|
7405
|
+
}
|
|
7406
|
+
function pathsFromRecord(rec) {
|
|
7407
|
+
if (!rec || typeof rec !== "object") return [];
|
|
7408
|
+
const out = [];
|
|
7409
|
+
const r = rec;
|
|
7410
|
+
const pushFromInput = (input) => {
|
|
7411
|
+
if (!input || typeof input !== "object") return;
|
|
7412
|
+
const i = input;
|
|
7413
|
+
for (const v of [i.file_path, i.path, i.notebook_path, i.cwd]) {
|
|
7414
|
+
if (typeof v === "string" && v.trim().length > 0) out.push(v.trim());
|
|
7415
|
+
}
|
|
7416
|
+
};
|
|
7417
|
+
const content = r.message?.content;
|
|
7418
|
+
if (Array.isArray(content)) {
|
|
7419
|
+
for (const raw of content) {
|
|
7420
|
+
if (!raw || typeof raw !== "object") continue;
|
|
7421
|
+
const block = raw;
|
|
7422
|
+
if (block.type === "tool_use") pushFromInput(block.input);
|
|
7423
|
+
}
|
|
7424
|
+
}
|
|
7425
|
+
if (r.payload && typeof r.payload === "object" && r.payload.type === "function_call") {
|
|
7426
|
+
const args = r.payload.arguments;
|
|
7427
|
+
if (typeof args === "string") {
|
|
7428
|
+
try {
|
|
7429
|
+
pushFromInput(JSON.parse(args));
|
|
7430
|
+
} catch {
|
|
7431
|
+
}
|
|
7432
|
+
} else {
|
|
7433
|
+
pushFromInput(args);
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
return out;
|
|
7437
|
+
}
|
|
7438
|
+
function codexSessionCwd(records) {
|
|
7439
|
+
for (const raw of records) {
|
|
7440
|
+
if (!raw || typeof raw !== "object") continue;
|
|
7441
|
+
const rec = raw;
|
|
7442
|
+
if (rec.type !== "session_meta") continue;
|
|
7443
|
+
const cwd = rec.payload?.cwd;
|
|
7444
|
+
if (typeof cwd === "string" && cwd.trim().length > 0) return cwd.trim();
|
|
7445
|
+
}
|
|
7446
|
+
return null;
|
|
7447
|
+
}
|
|
7448
|
+
function sessionPaths(records, repoRoot) {
|
|
7449
|
+
const root = (repoRoot && repoRoot.trim().length > 0 ? repoRoot.trim() : codexSessionCwd(records)) ?? null;
|
|
7450
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7451
|
+
for (const rec of records) {
|
|
7452
|
+
for (const p of pathsFromRecord(rec)) {
|
|
7453
|
+
if (isAbsolute(p)) {
|
|
7454
|
+
if (root === null) continue;
|
|
7455
|
+
const rel = relativizeUnder(p, root);
|
|
7456
|
+
if (rel === null || rel.length === 0) continue;
|
|
7457
|
+
seen.add(rel);
|
|
7458
|
+
} else if (!isForeignRelativePath(p)) {
|
|
7459
|
+
const rel = p.replace(/^(?:\.\/)+/, "");
|
|
7460
|
+
if (rel.length > 0) seen.add(rel);
|
|
7461
|
+
}
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
return Array.from(seen).sort();
|
|
7465
|
+
}
|
|
7383
7466
|
|
|
7384
7467
|
// src/repo.ts
|
|
7385
7468
|
import { execFileSync } from "node:child_process";
|
|
@@ -7459,6 +7542,7 @@ async function serverInfer(transcript, config2, opts = {}) {
|
|
|
7459
7542
|
body.persist = true;
|
|
7460
7543
|
body.repo = { owner: opts.repo.owner, name: opts.repo.name };
|
|
7461
7544
|
if (opts.decidedAt) body.decidedAt = opts.decidedAt;
|
|
7545
|
+
if (opts.filePaths && opts.filePaths.length > 0) body.filePaths = opts.filePaths;
|
|
7462
7546
|
}
|
|
7463
7547
|
let res;
|
|
7464
7548
|
try {
|
|
@@ -8263,6 +8347,7 @@ async function runCapture(input, deps = {}) {
|
|
|
8263
8347
|
const records = parseJsonl(rawTranscript);
|
|
8264
8348
|
const redacted = redactTranscript(records);
|
|
8265
8349
|
const decidedAt = sessionTimestamp(records) ?? void 0;
|
|
8350
|
+
const filePaths = sessionPaths(records, input.cwd);
|
|
8266
8351
|
const sessionId = redacted.sessionId ?? input.session_id ?? null;
|
|
8267
8352
|
if (redacted.turns.length === 0) {
|
|
8268
8353
|
return {
|
|
@@ -8281,6 +8366,7 @@ async function runCapture(input, deps = {}) {
|
|
|
8281
8366
|
env,
|
|
8282
8367
|
fetchImpl: deps.fetchImpl,
|
|
8283
8368
|
decidedAt,
|
|
8369
|
+
filePaths,
|
|
8284
8370
|
...repo ? { persist: true, repo } : {}
|
|
8285
8371
|
});
|
|
8286
8372
|
if (!result.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backthread",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Backthread CLI — capture the why behind your AI-coded changes from your Claude Code sessions, and ask how your codebase works without leaving the terminal. Source code and tool I/O are redacted locally before anything leaves your machine.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Backthread",
|