@whittlelabs/sifter 0.14.0 → 0.15.0
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 +40 -70
- package/bin.js.map +3 -3
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -3538,7 +3538,7 @@ var require_init = __commonJS({
|
|
|
3538
3538
|
var store_1 = require_store();
|
|
3539
3539
|
var spend_store_1 = require_spend_store();
|
|
3540
3540
|
function buildInitCommand(brand) {
|
|
3541
|
-
return new commander_1.Command("init").description(`Pair this ${brand.product.title} with Keep and seed local state`).option("--pairing-code <code>", "Pairing code from the product UI").option("--keep-url <url>", "Override the Keep API URL from BrandConfig").option("--jobs-url <url>", "Override the Jobs API URL from BrandConfig").option("--executor <type>", `Executor to write into ${brand.product.id}.yaml (e.g. claude-code). Must be in the brand's executor allowlist.`).option("--poll-interval <ms>", "Run-loop poll interval in milliseconds").option("--daily-cap <tokens>", "Daily token cap; overrides the brand default").option("--monthly-cap <tokens>", "Monthly token cap; overrides the brand default").option("--
|
|
3541
|
+
return new commander_1.Command("init").description(`Pair this ${brand.product.title} with Keep and seed local state`).option("--pairing-code <code>", "Pairing code from the product UI").option("--keep-url <url>", "Override the Keep API URL from BrandConfig").option("--jobs-url <url>", "Override the Jobs API URL from BrandConfig").option("--executor <type>", `Executor to write into ${brand.product.id}.yaml (e.g. claude-code). Must be in the brand's executor allowlist.`).option("--poll-interval <ms>", "Run-loop poll interval in milliseconds").option("--daily-cap <tokens>", "Daily token cap; overrides the brand default").option("--monthly-cap <tokens>", "Monthly token cap; overrides the brand default").option("--tmp-dir <path>", "Base directory for each job's throwaway checkout (defaults to the OS temp dir; set only when that is unsuitable)").option("-p, --profile <name>", 'Config profile to pair into (isolates this pairing under its own dir; default "default")').action(async (options) => {
|
|
3542
3542
|
try {
|
|
3543
3543
|
const profile = (0, apply_1.resolveProfile)(brand, options.profile);
|
|
3544
3544
|
const setupOptions = parseSetupOptions(brand, options);
|
|
@@ -3621,8 +3621,8 @@ var require_init = __commonJS({
|
|
|
3621
3621
|
if (raw.monthlyCap !== void 0) {
|
|
3622
3622
|
out.monthlyCap = requireNonNegativeInt("--monthly-cap", raw.monthlyCap);
|
|
3623
3623
|
}
|
|
3624
|
-
if (raw.
|
|
3625
|
-
out.
|
|
3624
|
+
if (raw.tmpDir !== void 0) {
|
|
3625
|
+
out.tmpDir = raw.tmpDir;
|
|
3626
3626
|
}
|
|
3627
3627
|
return out;
|
|
3628
3628
|
}
|
|
@@ -18896,10 +18896,11 @@ var require_workspace = __commonJS({
|
|
|
18896
18896
|
var path_1 = __importDefault(require("path"));
|
|
18897
18897
|
var DEFAULT_GIT_TIMEOUT_MS = 12e4;
|
|
18898
18898
|
var DEFAULT_GIT_PROTOCOLS = "https:ssh:git";
|
|
18899
|
-
async function prepareWorkspace(
|
|
18899
|
+
async function prepareWorkspace(workspace, options = {}) {
|
|
18900
|
+
const tmpBase = options.tmpDir ?? os_1.default.tmpdir();
|
|
18900
18901
|
const resolved = readGitWorkspace(workspace);
|
|
18901
18902
|
if ("warning" in resolved) {
|
|
18902
|
-
return degradedWorkspace(resolved.warning);
|
|
18903
|
+
return degradedWorkspace(resolved.warning, tmpBase);
|
|
18903
18904
|
}
|
|
18904
18905
|
const gitWorkspace = resolved.git;
|
|
18905
18906
|
const timeoutMs = options.commandTimeoutMs ?? DEFAULT_GIT_TIMEOUT_MS;
|
|
@@ -18907,7 +18908,6 @@ var require_workspace = __commonJS({
|
|
|
18907
18908
|
const signal = options.signal;
|
|
18908
18909
|
const authEnv = options.gitToken !== void 0 ? buildGitAuthEnv(gitWorkspace.remote, options.gitToken) : void 0;
|
|
18909
18910
|
const git = (args, cwd) => runGit(args, cwd, { timeoutMs, signal, allowedProtocols, extraEnv: authEnv });
|
|
18910
|
-
const gitCleanup = (args, cwd) => runGit(args, cwd, { timeoutMs, allowedProtocols });
|
|
18911
18911
|
const hasCommit = (dir) => git(["cat-file", "-e", `${gitWorkspace.commit}^{commit}`], dir).then((r) => r.code === 0);
|
|
18912
18912
|
const fetchCommit = async (dir, opts) => {
|
|
18913
18913
|
if (await hasCommit(dir))
|
|
@@ -18915,9 +18915,6 @@ var require_workspace = __commonJS({
|
|
|
18915
18915
|
const depth = opts.shallow ? ["--depth=1"] : [];
|
|
18916
18916
|
const attempts = [];
|
|
18917
18917
|
for (const ref of gitWorkspace.fetchRefs ?? []) {
|
|
18918
|
-
if (opts.includeOrigin) {
|
|
18919
|
-
attempts.push({ source: `origin ${ref}`, args: ["fetch", "--no-tags", ...depth, "origin", ref] });
|
|
18920
|
-
}
|
|
18921
18918
|
attempts.push({
|
|
18922
18919
|
source: `${gitWorkspace.remote} ${ref}`,
|
|
18923
18920
|
args: ["fetch", "--no-tags", ...depth, gitWorkspace.remote, ref]
|
|
@@ -18941,31 +18938,7 @@ var require_workspace = __commonJS({
|
|
|
18941
18938
|
return failures2;
|
|
18942
18939
|
};
|
|
18943
18940
|
const notMaterialized = (failures2) => `could not materialize ${gitWorkspace.commit} from ${gitWorkspace.remote}` + (failures2.length > 0 ? ` (${failures2.join("; ")})` : "");
|
|
18944
|
-
const
|
|
18945
|
-
if (isRepo) {
|
|
18946
|
-
const failures2 = await fetchCommit(repoDir, { includeOrigin: true, shallow: false });
|
|
18947
|
-
if (!await hasCommit(repoDir))
|
|
18948
|
-
return degradedWorkspace(notMaterialized(failures2));
|
|
18949
|
-
const tempRoot2 = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-ws-"));
|
|
18950
|
-
const worktreeDir = path_1.default.join(tempRoot2, "checkout");
|
|
18951
|
-
const add = await git(["worktree", "add", "--detach", worktreeDir, gitWorkspace.commit], repoDir);
|
|
18952
|
-
if (add.code !== 0) {
|
|
18953
|
-
await fs_1.promises.rm(tempRoot2, { recursive: true, force: true }).catch(() => {
|
|
18954
|
-
});
|
|
18955
|
-
return degradedWorkspace(`git worktree add failed: ${firstLine(add.stderr)}`);
|
|
18956
|
-
}
|
|
18957
|
-
return {
|
|
18958
|
-
cwd: worktreeDir,
|
|
18959
|
-
materialized: true,
|
|
18960
|
-
cleanup: async () => {
|
|
18961
|
-
await gitCleanup(["worktree", "remove", "--force", worktreeDir], repoDir);
|
|
18962
|
-
await gitCleanup(["worktree", "prune"], repoDir);
|
|
18963
|
-
await fs_1.promises.rm(tempRoot2, { recursive: true, force: true }).catch(() => {
|
|
18964
|
-
});
|
|
18965
|
-
}
|
|
18966
|
-
};
|
|
18967
|
-
}
|
|
18968
|
-
const tempRoot = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), "shuttle-ws-"));
|
|
18941
|
+
const tempRoot = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-ws-"));
|
|
18969
18942
|
const checkoutDir = path_1.default.join(tempRoot, "checkout");
|
|
18970
18943
|
const removeTempRoot = async () => {
|
|
18971
18944
|
await fs_1.promises.rm(tempRoot, { recursive: true, force: true }).catch(() => {
|
|
@@ -18975,17 +18948,17 @@ var require_workspace = __commonJS({
|
|
|
18975
18948
|
const init = await git(["init", "-q"], checkoutDir);
|
|
18976
18949
|
if (init.code !== 0) {
|
|
18977
18950
|
await removeTempRoot();
|
|
18978
|
-
return degradedWorkspace(`git init failed: ${firstLine(init.stderr)}
|
|
18951
|
+
return degradedWorkspace(`git init failed: ${firstLine(init.stderr)}`, tmpBase);
|
|
18979
18952
|
}
|
|
18980
|
-
const failures = await fetchCommit(checkoutDir, {
|
|
18953
|
+
const failures = await fetchCommit(checkoutDir, { shallow: true });
|
|
18981
18954
|
if (!await hasCommit(checkoutDir)) {
|
|
18982
18955
|
await removeTempRoot();
|
|
18983
|
-
return degradedWorkspace(notMaterialized(failures));
|
|
18956
|
+
return degradedWorkspace(notMaterialized(failures), tmpBase);
|
|
18984
18957
|
}
|
|
18985
18958
|
const checkout = await git(["checkout", "--detach", gitWorkspace.commit], checkoutDir);
|
|
18986
18959
|
if (checkout.code !== 0) {
|
|
18987
18960
|
await removeTempRoot();
|
|
18988
|
-
return degradedWorkspace(`git checkout failed: ${firstLine(checkout.stderr)}
|
|
18961
|
+
return degradedWorkspace(`git checkout failed: ${firstLine(checkout.stderr)}`, tmpBase);
|
|
18989
18962
|
}
|
|
18990
18963
|
return { cwd: checkoutDir, materialized: true, cleanup: removeTempRoot };
|
|
18991
18964
|
}
|
|
@@ -19033,8 +19006,8 @@ var require_workspace = __commonJS({
|
|
|
19033
19006
|
}
|
|
19034
19007
|
return { git };
|
|
19035
19008
|
}
|
|
19036
|
-
async function degradedWorkspace(warning) {
|
|
19037
|
-
const scratchDir = await fs_1.promises.mkdtemp(path_1.default.join(
|
|
19009
|
+
async function degradedWorkspace(warning, tmpBase = os_1.default.tmpdir()) {
|
|
19010
|
+
const scratchDir = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-ws-degraded-"));
|
|
19038
19011
|
return {
|
|
19039
19012
|
cwd: scratchDir,
|
|
19040
19013
|
materialized: false,
|
|
@@ -19329,12 +19302,15 @@ var require_claude_code = __commonJS({
|
|
|
19329
19302
|
configDir = path_1.default.join(jobScratch, "claude-config");
|
|
19330
19303
|
await fs_1.promises.mkdir(configDir);
|
|
19331
19304
|
}
|
|
19305
|
+
const tmpBase = this.config.tmpDir ? (0, apply_1.expandHome)(this.config.tmpDir) : os_1.default.tmpdir();
|
|
19332
19306
|
let workspace = null;
|
|
19307
|
+
let promptOnlyScratch = null;
|
|
19333
19308
|
let overlayResult = null;
|
|
19334
19309
|
try {
|
|
19335
19310
|
if (spec.workspace) {
|
|
19336
|
-
workspace = await (0, workspace_1.prepareWorkspace)(
|
|
19311
|
+
workspace = await (0, workspace_1.prepareWorkspace)(spec.workspace, {
|
|
19337
19312
|
signal,
|
|
19313
|
+
tmpDir: tmpBase,
|
|
19338
19314
|
...credentials?.githubToken ? { gitToken: credentials.githubToken } : {}
|
|
19339
19315
|
});
|
|
19340
19316
|
if (spec.workspace.overlay) {
|
|
@@ -19347,7 +19323,10 @@ var require_claude_code = __commonJS({
|
|
|
19347
19323
|
const childEnv = buildChildEnv(credentials, configDir);
|
|
19348
19324
|
const claudeHints = (0, loom_1.pickProviderHints)(spec, "claude-code");
|
|
19349
19325
|
const hintedModel = typeof claudeHints.model === "string" && claudeHints.model.length > 0 ? claudeHints.model : void 0;
|
|
19350
|
-
|
|
19326
|
+
if (!workspace) {
|
|
19327
|
+
promptOnlyScratch = await fs_1.promises.mkdtemp(path_1.default.join(tmpBase, "shuttle-cc-run-"));
|
|
19328
|
+
}
|
|
19329
|
+
const runCwd = workspace?.cwd ?? promptOnlyScratch;
|
|
19351
19330
|
const { response, readPaths } = await this.runClaude(prompt, spec, runCwd, childEnv, signal, hintedModel);
|
|
19352
19331
|
if (workspace) {
|
|
19353
19332
|
response.outputs.push({
|
|
@@ -19372,6 +19351,9 @@ var require_claude_code = __commonJS({
|
|
|
19372
19351
|
} finally {
|
|
19373
19352
|
if (workspace)
|
|
19374
19353
|
await workspace.cleanup();
|
|
19354
|
+
if (promptOnlyScratch)
|
|
19355
|
+
await fs_1.promises.rm(promptOnlyScratch, { recursive: true, force: true }).catch(() => {
|
|
19356
|
+
});
|
|
19375
19357
|
if (jobScratch)
|
|
19376
19358
|
await fs_1.promises.rm(jobScratch, { recursive: true, force: true }).catch(() => {
|
|
19377
19359
|
});
|
|
@@ -19477,12 +19459,13 @@ var require_claude_code = __commonJS({
|
|
|
19477
19459
|
}
|
|
19478
19460
|
};
|
|
19479
19461
|
function createClaudeCodeExecutor(config, capabilityId = "claude-code", deps = {}) {
|
|
19480
|
-
const
|
|
19481
|
-
if (
|
|
19482
|
-
|
|
19462
|
+
const validated = { capabilityId };
|
|
19463
|
+
if (config.tmpDir !== void 0) {
|
|
19464
|
+
if (typeof config.tmpDir !== "string" || config.tmpDir.length === 0) {
|
|
19465
|
+
throw new Error('"tmpDir" must be a non-empty string');
|
|
19466
|
+
}
|
|
19467
|
+
validated.tmpDir = config.tmpDir;
|
|
19483
19468
|
}
|
|
19484
|
-
const cwd = (0, apply_1.expandHome)(cwdRaw);
|
|
19485
|
-
const validated = { cwd, capabilityId };
|
|
19486
19469
|
if (config.model !== void 0) {
|
|
19487
19470
|
if (typeof config.model !== "string")
|
|
19488
19471
|
throw new Error('"model" must be a string');
|
|
@@ -21542,14 +21525,13 @@ var import_shuttle2 = __toESM(require_dist5());
|
|
|
21542
21525
|
// src/brand.ts
|
|
21543
21526
|
var import_path = require("path");
|
|
21544
21527
|
var import_promises = require("fs/promises");
|
|
21545
|
-
var import_readline = require("readline");
|
|
21546
21528
|
var import_yaml = __toESM(require_dist());
|
|
21547
21529
|
var import_shuttle = __toESM(require_dist5());
|
|
21548
21530
|
|
|
21549
21531
|
// package.json
|
|
21550
21532
|
var package_default = {
|
|
21551
21533
|
name: "@whittlelabs/sifter",
|
|
21552
|
-
version: "0.
|
|
21534
|
+
version: "0.15.0",
|
|
21553
21535
|
description: "Whittle Sifter: paired AI reviewer for Whittle Sift job pools.",
|
|
21554
21536
|
bin: {
|
|
21555
21537
|
"whittle-sifter": "./dist/bin.js"
|
|
@@ -21646,12 +21628,11 @@ var sifterBrand = {
|
|
|
21646
21628
|
// A re-pair must refresh the pairing-derived fields (Jobs URL, service
|
|
21647
21629
|
// identity, pools) even when sifter.yaml already exists — otherwise a
|
|
21648
21630
|
// stale file silently shadows the new pairing (exactly how a leftover
|
|
21649
|
-
// `localhost:3005`
|
|
21650
|
-
//
|
|
21651
|
-
//
|
|
21652
|
-
//
|
|
21653
|
-
//
|
|
21654
|
-
// isn't one to preserve.
|
|
21631
|
+
// `localhost:3005` once survived a fresh pair). So we merge rather than
|
|
21632
|
+
// skip: rebuild the pairing fields from `pairing`, carry over the user's
|
|
21633
|
+
// executor block (e.g. a claude-code `tmpDir`) and polling unless a fresh
|
|
21634
|
+
// flag overrides them, and log what happened. We only (re)scaffold an
|
|
21635
|
+
// executor block when there isn't one to preserve.
|
|
21655
21636
|
run: async (ctx) => {
|
|
21656
21637
|
const yamlPath = (0, import_path.join)(ctx.configDir, "sifter.yaml");
|
|
21657
21638
|
const pairing = new import_shuttle.PairingConfigStore((0, import_path.join)(ctx.configDir, "config.json")).read();
|
|
@@ -21706,8 +21687,10 @@ var sifterBrand = {
|
|
|
21706
21687
|
async function buildExecutorBlock(executor, ctx) {
|
|
21707
21688
|
switch (executor) {
|
|
21708
21689
|
case "claude-code": {
|
|
21709
|
-
|
|
21710
|
-
|
|
21690
|
+
return {
|
|
21691
|
+
type: "claude-code",
|
|
21692
|
+
...ctx.setupOptions.tmpDir ? { tmpDir: ctx.setupOptions.tmpDir } : {}
|
|
21693
|
+
};
|
|
21711
21694
|
}
|
|
21712
21695
|
case "anthropic-api": {
|
|
21713
21696
|
return {
|
|
@@ -21719,19 +21702,6 @@ async function buildExecutorBlock(executor, ctx) {
|
|
|
21719
21702
|
return { type: executor };
|
|
21720
21703
|
}
|
|
21721
21704
|
}
|
|
21722
|
-
async function promptForCwd(prompt, fallback) {
|
|
21723
|
-
if (!process.stdin.isTTY) return fallback;
|
|
21724
|
-
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
21725
|
-
try {
|
|
21726
|
-
const answer = await new Promise((resolve) => {
|
|
21727
|
-
rl.question(prompt, (input) => resolve(input));
|
|
21728
|
-
});
|
|
21729
|
-
const trimmed = answer.trim();
|
|
21730
|
-
return trimmed.length === 0 ? fallback : trimmed;
|
|
21731
|
-
} finally {
|
|
21732
|
-
rl.close();
|
|
21733
|
-
}
|
|
21734
|
-
}
|
|
21735
21705
|
|
|
21736
21706
|
// src/bin.ts
|
|
21737
21707
|
(0, import_shuttle2.createCli)(sifterBrand).parseAsync(process.argv).catch((err) => {
|