@whittlelabs/sifter 0.30.0 → 0.30.1
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/bin.js +139 -17
- package/bin.js.map +2 -2
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -10008,6 +10008,8 @@ var require_claude_code = __commonJS({
|
|
|
10008
10008
|
exports2.createClaudeCodeExecutor = createClaudeCodeExecutor;
|
|
10009
10009
|
exports2.parseStreamJson = parseStreamJson;
|
|
10010
10010
|
exports2.relativeWorkspaceReads = relativeWorkspaceReads;
|
|
10011
|
+
exports2.globToRegExp = globToRegExp;
|
|
10012
|
+
exports2.creditOverlayReads = creditOverlayReads;
|
|
10011
10013
|
exports2.extractJsonObject = extractJsonObject;
|
|
10012
10014
|
var child_process_1 = require("child_process");
|
|
10013
10015
|
var fs_1 = require("fs");
|
|
@@ -10092,12 +10094,11 @@ var require_claude_code = __commonJS({
|
|
|
10092
10094
|
promptOnlyScratch = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-cc-run-"));
|
|
10093
10095
|
}
|
|
10094
10096
|
const runCwd = workspace?.cwd ?? promptOnlyScratch;
|
|
10095
|
-
const { response, readPaths,
|
|
10097
|
+
const { response, readPaths, toolInputs } = await this.runClaude(prompt, spec, runCwd, childEnv, signal, hintedModel, reporter, void 0, dispatch.job.id);
|
|
10096
10098
|
if (workspace) {
|
|
10097
10099
|
const reads = [...readPaths];
|
|
10098
|
-
for (const rel of workspace.overlay?.writtenPaths ?? []) {
|
|
10099
|
-
|
|
10100
|
-
reads.push(path_1.default.join(runCwd, rel));
|
|
10100
|
+
for (const rel of creditOverlayReads(toolInputs, workspace.overlay?.writtenPaths ?? [])) {
|
|
10101
|
+
reads.push(path_1.default.join(runCwd, rel));
|
|
10101
10102
|
}
|
|
10102
10103
|
response.outputs.push({
|
|
10103
10104
|
label: "workspace_reads",
|
|
@@ -10155,14 +10156,14 @@ var require_claude_code = __commonJS({
|
|
|
10155
10156
|
outputLabel: handle.outputLabel,
|
|
10156
10157
|
outputSchema: handle.outputSchema
|
|
10157
10158
|
};
|
|
10158
|
-
const { response } = await this.runClaude(followUp, spec, scratch, handle.childEnv, signal, handle.model, null, handle.sessionId);
|
|
10159
|
+
const { response } = await this.runClaude(followUp, spec, scratch, handle.childEnv, signal, handle.model, null, handle.sessionId, `${handle.sessionId}-repair`);
|
|
10159
10160
|
return response;
|
|
10160
10161
|
} finally {
|
|
10161
10162
|
await fs_1.promises.rm(scratch, { recursive: true, force: true }).catch(() => {
|
|
10162
10163
|
});
|
|
10163
10164
|
}
|
|
10164
10165
|
}
|
|
10165
|
-
runClaude(prompt, spec, cwd, childEnv, signal, modelOverride, reporter, resumeSessionId) {
|
|
10166
|
+
runClaude(prompt, spec, cwd, childEnv, signal, modelOverride, reporter, resumeSessionId, transcriptName) {
|
|
10166
10167
|
const outputLabel = spec.outputLabel;
|
|
10167
10168
|
const model = modelOverride ?? this.config.model;
|
|
10168
10169
|
return new Promise((resolve, reject) => {
|
|
@@ -10251,6 +10252,9 @@ var require_claude_code = __commonJS({
|
|
|
10251
10252
|
tap.end();
|
|
10252
10253
|
}
|
|
10253
10254
|
const stdout = Buffer.concat(stdoutChunks).toString("utf-8");
|
|
10255
|
+
if (this.config.keepTranscripts && transcriptName) {
|
|
10256
|
+
void keepTranscript((0, apply_1.expandHome)(this.config.keepTranscripts), transcriptName, stdout);
|
|
10257
|
+
}
|
|
10254
10258
|
if (code === 0) {
|
|
10255
10259
|
try {
|
|
10256
10260
|
const stream = parseStreamJson(stdout);
|
|
@@ -10286,7 +10290,7 @@ var require_claude_code = __commonJS({
|
|
|
10286
10290
|
} : {}
|
|
10287
10291
|
},
|
|
10288
10292
|
readPaths: stream.readPaths,
|
|
10289
|
-
|
|
10293
|
+
toolInputs: stream.toolInputs
|
|
10290
10294
|
}));
|
|
10291
10295
|
} catch (err) {
|
|
10292
10296
|
settle(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
@@ -10334,6 +10338,12 @@ var require_claude_code = __commonJS({
|
|
|
10334
10338
|
}
|
|
10335
10339
|
validated.timeout = config.timeout;
|
|
10336
10340
|
}
|
|
10341
|
+
if (config.keepTranscripts !== void 0) {
|
|
10342
|
+
if (typeof config.keepTranscripts !== "string" || config.keepTranscripts.length === 0) {
|
|
10343
|
+
throw new Error('"keepTranscripts" must be a non-empty directory path');
|
|
10344
|
+
}
|
|
10345
|
+
validated.keepTranscripts = config.keepTranscripts;
|
|
10346
|
+
}
|
|
10337
10347
|
if (config.overlayAllowedHosts !== void 0) {
|
|
10338
10348
|
if (!Array.isArray(config.overlayAllowedHosts) || !config.overlayAllowedHosts.every((h) => typeof h === "string")) {
|
|
10339
10349
|
throw new Error('"overlayAllowedHosts" must be an array of strings');
|
|
@@ -10358,7 +10368,7 @@ var require_claude_code = __commonJS({
|
|
|
10358
10368
|
let resultMeta = null;
|
|
10359
10369
|
let structuredOutput = null;
|
|
10360
10370
|
let sessionId = null;
|
|
10361
|
-
|
|
10371
|
+
const toolInputs = [];
|
|
10362
10372
|
for (const line of stdout.split("\n")) {
|
|
10363
10373
|
const trimmed = line.trim();
|
|
10364
10374
|
if (!trimmed.startsWith("{"))
|
|
@@ -10384,7 +10394,7 @@ var require_claude_code = __commonJS({
|
|
|
10384
10394
|
readPaths.push(fp);
|
|
10385
10395
|
}
|
|
10386
10396
|
if (b.input && typeof b.input === "object")
|
|
10387
|
-
|
|
10397
|
+
toolInputs.push(JSON.stringify(b.input));
|
|
10388
10398
|
}
|
|
10389
10399
|
} else if (event.type === "result") {
|
|
10390
10400
|
if (typeof event.result === "string" && event.result.trim().length > 0) {
|
|
@@ -10412,24 +10422,131 @@ var require_claude_code = __commonJS({
|
|
|
10412
10422
|
};
|
|
10413
10423
|
}
|
|
10414
10424
|
}
|
|
10415
|
-
return { finalText, readPaths, usage, resultMeta, structuredOutput, sessionId,
|
|
10425
|
+
return { finalText, readPaths, usage, resultMeta, structuredOutput, sessionId, toolInputs };
|
|
10416
10426
|
}
|
|
10417
10427
|
var MAX_REPORTED_READS = 2e3;
|
|
10418
10428
|
function relativeWorkspaceReads(readPaths, cwd) {
|
|
10419
10429
|
const root = path_1.default.resolve(cwd);
|
|
10430
|
+
const roots = [root];
|
|
10431
|
+
try {
|
|
10432
|
+
const real = (0, fs_1.realpathSync)(root);
|
|
10433
|
+
if (real !== root)
|
|
10434
|
+
roots.push(real);
|
|
10435
|
+
} catch {
|
|
10436
|
+
}
|
|
10420
10437
|
const out = /* @__PURE__ */ new Set();
|
|
10421
10438
|
for (const p of readPaths) {
|
|
10422
10439
|
const resolved = path_1.default.resolve(root, p);
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10440
|
+
for (const r of roots) {
|
|
10441
|
+
if (resolved === r)
|
|
10442
|
+
break;
|
|
10443
|
+
if (!resolved.startsWith(r + path_1.default.sep))
|
|
10444
|
+
continue;
|
|
10445
|
+
out.add(resolved.slice(r.length + 1));
|
|
10446
|
+
break;
|
|
10447
|
+
}
|
|
10428
10448
|
if (out.size >= MAX_REPORTED_READS)
|
|
10429
10449
|
break;
|
|
10430
10450
|
}
|
|
10431
10451
|
return [...out].sort();
|
|
10432
10452
|
}
|
|
10453
|
+
var GLOB_CHARS = /[*?[{]/;
|
|
10454
|
+
function escapeRegExp(s) {
|
|
10455
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10456
|
+
}
|
|
10457
|
+
function globToRegExp(glob) {
|
|
10458
|
+
let re = "^";
|
|
10459
|
+
for (let i = 0; i < glob.length; i++) {
|
|
10460
|
+
const ch = glob[i];
|
|
10461
|
+
if (ch === "*") {
|
|
10462
|
+
if (glob[i + 1] === "*") {
|
|
10463
|
+
if (glob[i + 2] === "/") {
|
|
10464
|
+
re += "(?:.*/)?";
|
|
10465
|
+
i += 2;
|
|
10466
|
+
} else {
|
|
10467
|
+
re += ".*";
|
|
10468
|
+
i += 1;
|
|
10469
|
+
}
|
|
10470
|
+
} else {
|
|
10471
|
+
re += "[^/]*";
|
|
10472
|
+
}
|
|
10473
|
+
} else if (ch === "?") {
|
|
10474
|
+
re += "[^/]";
|
|
10475
|
+
} else if (ch === "[") {
|
|
10476
|
+
const end = glob.indexOf("]", i + 1);
|
|
10477
|
+
if (end === -1)
|
|
10478
|
+
return null;
|
|
10479
|
+
let cls = glob.slice(i + 1, end);
|
|
10480
|
+
if (cls.startsWith("!"))
|
|
10481
|
+
cls = `^${cls.slice(1)}`;
|
|
10482
|
+
re += `[${cls.replace(/\\/g, "\\\\")}]`;
|
|
10483
|
+
i = end;
|
|
10484
|
+
} else if (ch === "{") {
|
|
10485
|
+
const end = glob.indexOf("}", i + 1);
|
|
10486
|
+
if (end === -1)
|
|
10487
|
+
return null;
|
|
10488
|
+
re += `(?:${glob.slice(i + 1, end).split(",").map(escapeRegExp).join("|")})`;
|
|
10489
|
+
i = end;
|
|
10490
|
+
} else {
|
|
10491
|
+
re += escapeRegExp(ch);
|
|
10492
|
+
}
|
|
10493
|
+
}
|
|
10494
|
+
return new RegExp(`${re}$`);
|
|
10495
|
+
}
|
|
10496
|
+
function creditOverlayReads(toolInputs, writtenPaths) {
|
|
10497
|
+
if (writtenPaths.length === 0 || toolInputs.length === 0)
|
|
10498
|
+
return [];
|
|
10499
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
10500
|
+
for (const rel of writtenPaths) {
|
|
10501
|
+
const parts = rel.split("/");
|
|
10502
|
+
for (let i = 1; i < parts.length; i++)
|
|
10503
|
+
dirs.add(`${parts.slice(0, i).join("/")}/`);
|
|
10504
|
+
}
|
|
10505
|
+
const dirAlt = [...dirs].map(escapeRegExp).join("|");
|
|
10506
|
+
const wordRe = dirs.size > 0 ? new RegExp(`(?:${dirAlt})[^\\s"'\`;|&<>(),\\\\]*`, "g") : null;
|
|
10507
|
+
const findRe = dirs.size > 0 ? new RegExp(`\\bfind\\s+(?:\\./)?(${[...dirs].map((d) => escapeRegExp(d.slice(0, -1))).join("|")})/?(?=\\s|$)`, "g") : null;
|
|
10508
|
+
const readerRe = /(?:^|[\s"])(?:-exec|-execdir|xargs)\b[^;|&]*\b(?:cat|head|tail|sed|awk|less|more|bat)\b/;
|
|
10509
|
+
const credited = /* @__PURE__ */ new Set();
|
|
10510
|
+
for (const input of toolInputs) {
|
|
10511
|
+
for (const rel of writtenPaths) {
|
|
10512
|
+
if (input.includes(rel))
|
|
10513
|
+
credited.add(rel);
|
|
10514
|
+
}
|
|
10515
|
+
if (!wordRe)
|
|
10516
|
+
continue;
|
|
10517
|
+
for (const match of input.matchAll(wordRe)) {
|
|
10518
|
+
const word = match[0];
|
|
10519
|
+
if (!GLOB_CHARS.test(word))
|
|
10520
|
+
continue;
|
|
10521
|
+
const re = globToRegExp(word);
|
|
10522
|
+
if (!re)
|
|
10523
|
+
continue;
|
|
10524
|
+
for (const rel of writtenPaths) {
|
|
10525
|
+
if (re.test(rel))
|
|
10526
|
+
credited.add(rel);
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
if (findRe && readerRe.test(input)) {
|
|
10530
|
+
for (const match of input.matchAll(findRe)) {
|
|
10531
|
+
const dir = `${match[1]}/`;
|
|
10532
|
+
for (const rel of writtenPaths) {
|
|
10533
|
+
if (rel.startsWith(dir))
|
|
10534
|
+
credited.add(rel);
|
|
10535
|
+
}
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
}
|
|
10539
|
+
return [...credited].sort();
|
|
10540
|
+
}
|
|
10541
|
+
async function keepTranscript(dir, name, stdout) {
|
|
10542
|
+
try {
|
|
10543
|
+
await fs_1.promises.mkdir(dir, { recursive: true });
|
|
10544
|
+
const safe = name.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
10545
|
+
await fs_1.promises.writeFile(path_1.default.join(dir, `${safe}.jsonl`), stdout);
|
|
10546
|
+
} catch (err) {
|
|
10547
|
+
console.warn(`[shuttle] could not keep the transcript for ${name} under ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
10548
|
+
}
|
|
10549
|
+
}
|
|
10433
10550
|
function extractJsonObject(s) {
|
|
10434
10551
|
const exact = tryParseObject(s);
|
|
10435
10552
|
if (exact)
|
|
@@ -25422,7 +25539,7 @@ var import_path = require("path");
|
|
|
25422
25539
|
var import_promises = require("fs/promises");
|
|
25423
25540
|
var import_yaml = __toESM(require_dist4());
|
|
25424
25541
|
var import_shuttle = __toESM(require_dist5());
|
|
25425
|
-
var buildVersion = true ? "0.30.
|
|
25542
|
+
var buildVersion = true ? "0.30.1" : pkg.version;
|
|
25426
25543
|
var sifterBrand = {
|
|
25427
25544
|
product: {
|
|
25428
25545
|
id: "sifter",
|
|
@@ -25467,7 +25584,12 @@ var sifterBrand = {
|
|
|
25467
25584
|
"https://api.sift.whittlelabs.com",
|
|
25468
25585
|
"https://api.sift.stage.whittlelabs.com",
|
|
25469
25586
|
"http://localhost:3008"
|
|
25470
|
-
]
|
|
25587
|
+
],
|
|
25588
|
+
// Transcript retention is a diagnostic switch a person flips for one
|
|
25589
|
+
// session, so it rides the environment rather than a file they would
|
|
25590
|
+
// have to remember to unset. The shared package reads no env of its
|
|
25591
|
+
// own (BE-005); this brand is where the variable becomes config.
|
|
25592
|
+
...process.env.SHUTTLE_KEEP_TRANSCRIPTS ? { keepTranscripts: process.env.SHUTTLE_KEEP_TRANSCRIPTS } : {}
|
|
25471
25593
|
}
|
|
25472
25594
|
},
|
|
25473
25595
|
// The environments Whittle Labs operates, keyed by the profile name that
|