@gaberrb/polypus 0.4.10 → 0.4.11
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/index.js +202 -38
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -143,6 +143,7 @@ var en = {
|
|
|
143
143
|
"run.autocorrect": "\u21BB tool failed \u2014 auto-correcting with extra context",
|
|
144
144
|
"run.cancelled": "\u25A0 cancelled",
|
|
145
145
|
"compaction.done": "context compacted: ~{before} \u2192 ~{after} tokens",
|
|
146
|
+
"tools.customLoaded": "loaded custom tool(s): {names}",
|
|
146
147
|
"run.jsonNeedsTask": "--json requires a task argument (headless mode has no interactive REPL).",
|
|
147
148
|
"review.approveAll": "approve all",
|
|
148
149
|
"review.reject": "reject",
|
|
@@ -403,6 +404,7 @@ var ptBR = {
|
|
|
403
404
|
"run.autocorrect": "\u21BB tool falhou \u2014 autocorrigindo com contexto extra",
|
|
404
405
|
"run.cancelled": "\u25A0 cancelado",
|
|
405
406
|
"compaction.done": "contexto compactado: ~{before} \u2192 ~{after} tokens",
|
|
407
|
+
"tools.customLoaded": "tool(s) customizada(s) carregada(s): {names}",
|
|
406
408
|
"run.jsonNeedsTask": "--json exige um argumento de tarefa (o modo headless n\xE3o tem REPL interativo).",
|
|
407
409
|
"review.approveAll": "aprovar tudo",
|
|
408
410
|
"review.reject": "rejeitar",
|
|
@@ -2144,6 +2146,66 @@ ${summary.content.trim()}`
|
|
|
2144
2146
|
return system ? [system, summaryMessage, ...tail] : [summaryMessage, ...tail];
|
|
2145
2147
|
}
|
|
2146
2148
|
|
|
2149
|
+
// src/core/agent/hooks.ts
|
|
2150
|
+
import { exec as exec2 } from "child_process";
|
|
2151
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2152
|
+
import { join as join4 } from "path";
|
|
2153
|
+
import { promisify as promisify2 } from "util";
|
|
2154
|
+
import { z as z8 } from "zod";
|
|
2155
|
+
var execAsync2 = promisify2(exec2);
|
|
2156
|
+
var HOOK_TIMEOUT = 12e4;
|
|
2157
|
+
var HooksSchema = z8.object({
|
|
2158
|
+
/** Shell command run after a successful write_file. `{path}` is substituted. */
|
|
2159
|
+
afterWrite: z8.string().optional(),
|
|
2160
|
+
/** Shell command run after a successful edit_file. `{path}` is substituted. */
|
|
2161
|
+
afterEdit: z8.string().optional(),
|
|
2162
|
+
/** Shell command run after any successful mutating tool. `{tool}`/`{path}` substituted. */
|
|
2163
|
+
afterTool: z8.string().optional(),
|
|
2164
|
+
/** Block run_command when the command contains any of these substrings. */
|
|
2165
|
+
beforeCommand: z8.object({ deny: z8.array(z8.string()).default([]) }).optional()
|
|
2166
|
+
});
|
|
2167
|
+
async function loadHooks(workspace) {
|
|
2168
|
+
try {
|
|
2169
|
+
const raw = await readFile8(join4(workspace, ".poly", "hooks.json"), "utf8");
|
|
2170
|
+
const parsed = HooksSchema.safeParse(JSON.parse(raw));
|
|
2171
|
+
return parsed.success ? parsed.data : void 0;
|
|
2172
|
+
} catch {
|
|
2173
|
+
return void 0;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
function screenCommandHook(hooks, command) {
|
|
2177
|
+
const deny = hooks?.beforeCommand?.deny ?? [];
|
|
2178
|
+
for (const needle of deny) {
|
|
2179
|
+
if (needle && command.includes(needle)) {
|
|
2180
|
+
return { blocked: true, reason: `matches deny rule "${needle}"` };
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
return { blocked: false };
|
|
2184
|
+
}
|
|
2185
|
+
function substitute(template, call) {
|
|
2186
|
+
const path = typeof call.arguments.path === "string" ? call.arguments.path : "";
|
|
2187
|
+
return template.replace(/\{path\}/g, path).replace(/\{tool\}/g, call.name);
|
|
2188
|
+
}
|
|
2189
|
+
async function runAfterHook(hooks, call, workspace) {
|
|
2190
|
+
if (!hooks) return void 0;
|
|
2191
|
+
const commands = [];
|
|
2192
|
+
if (call.name === "write_file" && hooks.afterWrite) commands.push({ label: "afterWrite", cmd: hooks.afterWrite });
|
|
2193
|
+
if (call.name === "edit_file" && hooks.afterEdit) commands.push({ label: "afterEdit", cmd: hooks.afterEdit });
|
|
2194
|
+
if (hooks.afterTool) commands.push({ label: "afterTool", cmd: hooks.afterTool });
|
|
2195
|
+
if (commands.length === 0) return void 0;
|
|
2196
|
+
const notes = [];
|
|
2197
|
+
for (const { label, cmd } of commands) {
|
|
2198
|
+
const resolved = substitute(cmd, call);
|
|
2199
|
+
try {
|
|
2200
|
+
await execAsync2(resolved, { cwd: workspace, timeout: HOOK_TIMEOUT, windowsHide: true });
|
|
2201
|
+
notes.push(`\u21AA hook ${label} ok`);
|
|
2202
|
+
} catch (err) {
|
|
2203
|
+
notes.push(`\u21AA hook ${label} failed: ${err.message.split("\n")[0]}`);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return notes.join("\n");
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2147
2209
|
// src/core/agent/loop.ts
|
|
2148
2210
|
function looksLikeStall(text2) {
|
|
2149
2211
|
const lc = text2.toLowerCase();
|
|
@@ -2184,7 +2246,13 @@ async function runAgent(opts) {
|
|
|
2184
2246
|
const { agent, permissions, events } = opts;
|
|
2185
2247
|
const maxSteps = opts.maxSteps ?? 30;
|
|
2186
2248
|
const maxReprompts = opts.maxReprompts ?? 3;
|
|
2187
|
-
const
|
|
2249
|
+
const extra = opts.extraTools ?? [];
|
|
2250
|
+
const extraByName = new Map(extra.map((tl) => [tl.spec.name, tl]));
|
|
2251
|
+
const baseSpecs = toolSpecs();
|
|
2252
|
+
const finishSpec = baseSpecs[baseSpecs.length - 1];
|
|
2253
|
+
const allSpecs = [...baseSpecs.slice(0, -1), ...extra.map((tl) => tl.spec), finishSpec];
|
|
2254
|
+
const resolveTool = (name) => extraByName.get(name) ?? getTool(name);
|
|
2255
|
+
const driver = makeDriver(agent.toolMode, allSpecs);
|
|
2188
2256
|
const ctx = { workspace: opts.workspace, permissions };
|
|
2189
2257
|
const seeding = !(opts.history && opts.history.length > 0);
|
|
2190
2258
|
const promptContext = seeding && opts.promptContext.projectInstructions === void 0 ? { ...opts.promptContext, projectInstructions: await loadProjectInstructions(opts.workspace) } : opts.promptContext;
|
|
@@ -2265,8 +2333,21 @@ async function runAgent(opts) {
|
|
|
2265
2333
|
const summary = String(call.arguments.summary ?? "").trim();
|
|
2266
2334
|
return { finished: true, reason: "finished", summary, steps: step, messages, usage: usage2 };
|
|
2267
2335
|
}
|
|
2268
|
-
const tool =
|
|
2269
|
-
const
|
|
2336
|
+
const tool = resolveTool(call.name);
|
|
2337
|
+
const hookScreen = call.name === "run_command" ? screenCommandHook(opts.hooks, String(call.arguments.command ?? "")) : { blocked: false };
|
|
2338
|
+
let result;
|
|
2339
|
+
if (hookScreen.blocked) {
|
|
2340
|
+
result = { ok: false, output: `Command blocked by hook: ${hookScreen.reason}` };
|
|
2341
|
+
} else if (tool) {
|
|
2342
|
+
result = await tool.run(call.arguments, ctx);
|
|
2343
|
+
if (result.ok) {
|
|
2344
|
+
const note2 = await runAfterHook(opts.hooks, call, opts.workspace);
|
|
2345
|
+
if (note2) result = { ...result, output: `${result.output}
|
|
2346
|
+
${note2}` };
|
|
2347
|
+
}
|
|
2348
|
+
} else {
|
|
2349
|
+
result = { ok: false, output: `Unknown tool "${call.name}". Available: ${allSpecs.map((t2) => t2.name).join(", ")}` };
|
|
2350
|
+
}
|
|
2270
2351
|
events?.onToolResult?.(call, result);
|
|
2271
2352
|
const sig = `${call.name}:${JSON.stringify(call.arguments)}`;
|
|
2272
2353
|
let resultText = result.output;
|
|
@@ -2310,7 +2391,7 @@ ${guidance}`;
|
|
|
2310
2391
|
}
|
|
2311
2392
|
|
|
2312
2393
|
// src/core/context/mentions.ts
|
|
2313
|
-
import { readdir as readdir4, readFile as
|
|
2394
|
+
import { readdir as readdir4, readFile as readFile9, stat as stat2 } from "fs/promises";
|
|
2314
2395
|
import { resolve as resolve9 } from "path";
|
|
2315
2396
|
var MAX_FILE_CHARS = 1e4;
|
|
2316
2397
|
var MENTION_RE = /(?:^|\s)@([\w./-]+)/g;
|
|
@@ -2337,7 +2418,7 @@ ${t("mentions.notFound", { path: token })}`);
|
|
|
2337
2418
|
${listing || "(empty)"}`);
|
|
2338
2419
|
injected.push(decision.rel);
|
|
2339
2420
|
} else {
|
|
2340
|
-
const raw = await
|
|
2421
|
+
const raw = await readFile9(abs, "utf8");
|
|
2341
2422
|
const content = raw.length > MAX_FILE_CHARS ? raw.slice(0, MAX_FILE_CHARS) + "\n\u2026[truncated]" : raw;
|
|
2342
2423
|
blocks.push(`## @${decision.rel}
|
|
2343
2424
|
\`\`\`
|
|
@@ -2360,16 +2441,16 @@ ${blocks.join("\n\n")}`;
|
|
|
2360
2441
|
}
|
|
2361
2442
|
|
|
2362
2443
|
// src/core/agent/verify.ts
|
|
2363
|
-
import { exec as
|
|
2364
|
-
import { readFile as
|
|
2444
|
+
import { exec as exec3 } from "child_process";
|
|
2445
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
2365
2446
|
import { resolve as resolve10 } from "path";
|
|
2366
|
-
import { promisify as
|
|
2367
|
-
var
|
|
2447
|
+
import { promisify as promisify3 } from "util";
|
|
2448
|
+
var execAsync3 = promisify3(exec3);
|
|
2368
2449
|
var MAX_OUTPUT3 = 8e3;
|
|
2369
2450
|
var CHECK_SCRIPTS = ["typecheck", "build", "test"];
|
|
2370
2451
|
async function detectChecks(workspace) {
|
|
2371
2452
|
try {
|
|
2372
|
-
const raw = await
|
|
2453
|
+
const raw = await readFile10(resolve10(workspace, "package.json"), "utf8");
|
|
2373
2454
|
const scripts = JSON.parse(raw).scripts ?? {};
|
|
2374
2455
|
return CHECK_SCRIPTS.filter((s) => typeof scripts[s] === "string").map((s) => `npm run ${s}`);
|
|
2375
2456
|
} catch {
|
|
@@ -2380,7 +2461,7 @@ async function runChecks(workspace, commands) {
|
|
|
2380
2461
|
const results = [];
|
|
2381
2462
|
for (const command of commands) {
|
|
2382
2463
|
try {
|
|
2383
|
-
const { stdout: stdout2, stderr } = await
|
|
2464
|
+
const { stdout: stdout2, stderr } = await execAsync3(command, {
|
|
2384
2465
|
cwd: workspace,
|
|
2385
2466
|
timeout: 3e5,
|
|
2386
2467
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -2412,8 +2493,8 @@ function clamp2(s) {
|
|
|
2412
2493
|
}
|
|
2413
2494
|
|
|
2414
2495
|
// src/core/agent/usage.ts
|
|
2415
|
-
import { appendFile, mkdir as mkdir3, readFile as
|
|
2416
|
-
import { join as
|
|
2496
|
+
import { appendFile, mkdir as mkdir3, readFile as readFile11 } from "fs/promises";
|
|
2497
|
+
import { join as join5 } from "path";
|
|
2417
2498
|
|
|
2418
2499
|
// src/core/providers/openrouter.ts
|
|
2419
2500
|
var MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
@@ -2513,7 +2594,7 @@ function fmtUsd(n) {
|
|
|
2513
2594
|
return `US$${n.toFixed(2)}`;
|
|
2514
2595
|
}
|
|
2515
2596
|
function usagePath() {
|
|
2516
|
-
return
|
|
2597
|
+
return join5(configDir(), "usage.jsonl");
|
|
2517
2598
|
}
|
|
2518
2599
|
async function recordUsage(entry) {
|
|
2519
2600
|
try {
|
|
@@ -2525,7 +2606,7 @@ async function recordUsage(entry) {
|
|
|
2525
2606
|
async function aggregateUsage() {
|
|
2526
2607
|
let text2 = "";
|
|
2527
2608
|
try {
|
|
2528
|
-
text2 = await
|
|
2609
|
+
text2 = await readFile11(usagePath(), "utf8");
|
|
2529
2610
|
} catch {
|
|
2530
2611
|
return { days: [], total: emptyBucket("total") };
|
|
2531
2612
|
}
|
|
@@ -2559,10 +2640,10 @@ function accumulate(bucket, e) {
|
|
|
2559
2640
|
}
|
|
2560
2641
|
|
|
2561
2642
|
// src/core/agent/session-store.ts
|
|
2562
|
-
import { mkdir as mkdir4, readFile as
|
|
2563
|
-
import { join as
|
|
2643
|
+
import { mkdir as mkdir4, readFile as readFile12, readdir as readdir5, writeFile as writeFile4 } from "fs/promises";
|
|
2644
|
+
import { join as join6 } from "path";
|
|
2564
2645
|
function sessionsDir() {
|
|
2565
|
-
return
|
|
2646
|
+
return join6(configDir(), "sessions");
|
|
2566
2647
|
}
|
|
2567
2648
|
function newSessionId() {
|
|
2568
2649
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -2570,7 +2651,7 @@ function newSessionId() {
|
|
|
2570
2651
|
return `${stamp}-${rand}`;
|
|
2571
2652
|
}
|
|
2572
2653
|
function sessionPath(id) {
|
|
2573
|
-
return
|
|
2654
|
+
return join6(sessionsDir(), `${id}.json`);
|
|
2574
2655
|
}
|
|
2575
2656
|
async function saveSession(record) {
|
|
2576
2657
|
await mkdir4(sessionsDir(), { recursive: true });
|
|
@@ -2582,7 +2663,7 @@ async function saveSession(record) {
|
|
|
2582
2663
|
}
|
|
2583
2664
|
async function loadSession(id) {
|
|
2584
2665
|
try {
|
|
2585
|
-
return JSON.parse(await
|
|
2666
|
+
return JSON.parse(await readFile12(sessionPath(id), "utf8"));
|
|
2586
2667
|
} catch {
|
|
2587
2668
|
return void 0;
|
|
2588
2669
|
}
|
|
@@ -2597,7 +2678,7 @@ async function listSessions() {
|
|
|
2597
2678
|
const summaries = [];
|
|
2598
2679
|
for (const f of files) {
|
|
2599
2680
|
try {
|
|
2600
|
-
const r = JSON.parse(await
|
|
2681
|
+
const r = JSON.parse(await readFile12(join6(sessionsDir(), f), "utf8"));
|
|
2601
2682
|
summaries.push({
|
|
2602
2683
|
id: r.id,
|
|
2603
2684
|
updatedAt: r.updatedAt,
|
|
@@ -2621,6 +2702,83 @@ function deriveTitle(messages) {
|
|
|
2621
2702
|
return text2.length > 60 ? text2.slice(0, 60) + "\u2026" : text2 || "(untitled)";
|
|
2622
2703
|
}
|
|
2623
2704
|
|
|
2705
|
+
// src/core/tools/custom.ts
|
|
2706
|
+
import { exec as exec4 } from "child_process";
|
|
2707
|
+
import { readFile as readFile13, readdir as readdir6 } from "fs/promises";
|
|
2708
|
+
import { join as join7 } from "path";
|
|
2709
|
+
import { promisify as promisify4 } from "util";
|
|
2710
|
+
import { z as z9 } from "zod";
|
|
2711
|
+
var execAsync4 = promisify4(exec4);
|
|
2712
|
+
var MAX_OUTPUT4 = 2e4;
|
|
2713
|
+
var CustomToolSchema = z9.object({
|
|
2714
|
+
name: z9.string().min(1).regex(/^[a-z][a-z0-9_]*$/i, "tool name must be alphanumeric/underscore"),
|
|
2715
|
+
description: z9.string().min(1),
|
|
2716
|
+
/** JSON-schema object for the tool parameters (advertised to the model). */
|
|
2717
|
+
parameters: z9.record(z9.unknown()).optional(),
|
|
2718
|
+
/** Shell command template; `{argName}` placeholders are filled from the call arguments. */
|
|
2719
|
+
command: z9.string().min(1)
|
|
2720
|
+
});
|
|
2721
|
+
function makeCommandTool(def) {
|
|
2722
|
+
return {
|
|
2723
|
+
mutating: true,
|
|
2724
|
+
spec: {
|
|
2725
|
+
name: def.name,
|
|
2726
|
+
description: def.description,
|
|
2727
|
+
parameters: def.parameters ?? { type: "object", properties: {} }
|
|
2728
|
+
},
|
|
2729
|
+
async run(rawArgs, ctx) {
|
|
2730
|
+
const command = fillTemplate(def.command, rawArgs);
|
|
2731
|
+
const decision = await ctx.permissions.authorizeCommand(command);
|
|
2732
|
+
if (!decision.allowed) return { ok: false, output: `Command denied: ${decision.reason}` };
|
|
2733
|
+
try {
|
|
2734
|
+
const { stdout: stdout2, stderr } = await execAsync4(command, {
|
|
2735
|
+
cwd: ctx.workspace,
|
|
2736
|
+
timeout: 12e4,
|
|
2737
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
2738
|
+
windowsHide: true
|
|
2739
|
+
});
|
|
2740
|
+
return { ok: true, output: clamp3(`${stdout2}${stderr ? `
|
|
2741
|
+
[stderr]
|
|
2742
|
+
${stderr}` : ""}`.trim() || "(no output)") };
|
|
2743
|
+
} catch (err) {
|
|
2744
|
+
const e = err;
|
|
2745
|
+
return {
|
|
2746
|
+
ok: false,
|
|
2747
|
+
output: clamp3(`Command failed (exit ${e.code ?? "?"}): ${e.message}
|
|
2748
|
+
${e.stdout ?? ""}${e.stderr ?? ""}`)
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
async function loadCustomTools(workspace) {
|
|
2755
|
+
let files;
|
|
2756
|
+
try {
|
|
2757
|
+
files = (await readdir6(join7(workspace, ".poly", "tools"))).filter((f) => f.endsWith(".json"));
|
|
2758
|
+
} catch {
|
|
2759
|
+
return [];
|
|
2760
|
+
}
|
|
2761
|
+
const tools = [];
|
|
2762
|
+
for (const f of files) {
|
|
2763
|
+
try {
|
|
2764
|
+
const raw = await readFile13(join7(workspace, ".poly", "tools", f), "utf8");
|
|
2765
|
+
const parsed = CustomToolSchema.safeParse(JSON.parse(raw));
|
|
2766
|
+
if (parsed.success) tools.push(makeCommandTool(parsed.data));
|
|
2767
|
+
} catch {
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
return tools;
|
|
2771
|
+
}
|
|
2772
|
+
function fillTemplate(template, args) {
|
|
2773
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
2774
|
+
const v = args[key];
|
|
2775
|
+
return v === void 0 || v === null ? "" : String(v);
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
function clamp3(s) {
|
|
2779
|
+
return s.length > MAX_OUTPUT4 ? s.slice(0, MAX_OUTPUT4) + "\n\u2026[truncated]" : s;
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2624
2782
|
// src/cli/commands/json-output.ts
|
|
2625
2783
|
var OUTPUT_PREVIEW = 500;
|
|
2626
2784
|
function createJsonCollector() {
|
|
@@ -3419,7 +3577,7 @@ import pc7 from "picocolors";
|
|
|
3419
3577
|
// src/core/git/worktree.ts
|
|
3420
3578
|
import { mkdtemp } from "fs/promises";
|
|
3421
3579
|
import { tmpdir } from "os";
|
|
3422
|
-
import { join as
|
|
3580
|
+
import { join as join8 } from "path";
|
|
3423
3581
|
import { simpleGit } from "simple-git";
|
|
3424
3582
|
async function ensureRepo(workspace) {
|
|
3425
3583
|
const git = simpleGit(workspace);
|
|
@@ -3440,7 +3598,7 @@ async function identityArgs(git) {
|
|
|
3440
3598
|
}
|
|
3441
3599
|
async function createWorktree(git, label) {
|
|
3442
3600
|
const branch = `polypus/${label}-${Date.now().toString(36)}`;
|
|
3443
|
-
const path = await mkdtemp(
|
|
3601
|
+
const path = await mkdtemp(join8(tmpdir(), "polypus-wt-"));
|
|
3444
3602
|
await git.raw(["worktree", "add", "-b", branch, path, "HEAD"]);
|
|
3445
3603
|
return { path, branch };
|
|
3446
3604
|
}
|
|
@@ -3991,6 +4149,10 @@ async function executeTask(task, resolved, workspace, session, json = false, ver
|
|
|
3991
4149
|
return ok;
|
|
3992
4150
|
}
|
|
3993
4151
|
});
|
|
4152
|
+
const [extraTools, hooks] = await Promise.all([loadCustomTools(workspace), loadHooks(workspace)]);
|
|
4153
|
+
if (!json && extraTools.length > 0) {
|
|
4154
|
+
console.log(pc8.dim(t("tools.customLoaded", { names: extraTools.map((tl) => tl.spec.name).join(", ") })));
|
|
4155
|
+
}
|
|
3994
4156
|
const runOnce = (taskText) => runAgent({
|
|
3995
4157
|
task: taskText,
|
|
3996
4158
|
workspace,
|
|
@@ -4000,6 +4162,8 @@ async function executeTask(task, resolved, workspace, session, json = false, ver
|
|
|
4000
4162
|
history: session.history,
|
|
4001
4163
|
maxSteps: session.maxSteps,
|
|
4002
4164
|
compactThresholdTokens: compactionThreshold(),
|
|
4165
|
+
extraTools,
|
|
4166
|
+
hooks,
|
|
4003
4167
|
signal: controller.signal,
|
|
4004
4168
|
events
|
|
4005
4169
|
});
|
|
@@ -4204,7 +4368,7 @@ import pc9 from "picocolors";
|
|
|
4204
4368
|
|
|
4205
4369
|
// src/core/scaffold/init.ts
|
|
4206
4370
|
import { mkdir as mkdir5, writeFile as writeFile5, access } from "fs/promises";
|
|
4207
|
-
import { dirname as dirname3, join as
|
|
4371
|
+
import { dirname as dirname3, join as join9 } from "path";
|
|
4208
4372
|
|
|
4209
4373
|
// src/core/scaffold/templates.ts
|
|
4210
4374
|
function polyTemplates(locale) {
|
|
@@ -4447,7 +4611,7 @@ async function scaffoldPoly(workspace, opts) {
|
|
|
4447
4611
|
const skipped = [];
|
|
4448
4612
|
for (const [rel, content] of Object.entries(templates)) {
|
|
4449
4613
|
const display = `.poly/${rel}`;
|
|
4450
|
-
const abs =
|
|
4614
|
+
const abs = join9(workspace, ".poly", ...rel.split("/"));
|
|
4451
4615
|
if (!opts.force && await exists(abs)) {
|
|
4452
4616
|
skipped.push(display);
|
|
4453
4617
|
continue;
|
|
@@ -4587,9 +4751,9 @@ async function sessions() {
|
|
|
4587
4751
|
}
|
|
4588
4752
|
|
|
4589
4753
|
// src/cli/commands/prd.ts
|
|
4590
|
-
import { writeFile as writeFile6, readFile as
|
|
4754
|
+
import { writeFile as writeFile6, readFile as readFile14 } from "fs/promises";
|
|
4591
4755
|
import { execFile } from "child_process";
|
|
4592
|
-
import { promisify as
|
|
4756
|
+
import { promisify as promisify5 } from "util";
|
|
4593
4757
|
import pc13 from "picocolors";
|
|
4594
4758
|
|
|
4595
4759
|
// src/core/agent/prd.ts
|
|
@@ -4711,7 +4875,7 @@ function stripBom(s) {
|
|
|
4711
4875
|
}
|
|
4712
4876
|
|
|
4713
4877
|
// src/cli/commands/prd.ts
|
|
4714
|
-
var
|
|
4878
|
+
var exec5 = promisify5(execFile);
|
|
4715
4879
|
async function prd(issueRef, opts) {
|
|
4716
4880
|
const issue = await loadIssue(issueRef, opts.input);
|
|
4717
4881
|
const { provider } = resolveFreeProvider(opts.model ?? DEFAULT_PRD_MODEL);
|
|
@@ -4726,11 +4890,11 @@ async function prd(issueRef, opts) {
|
|
|
4726
4890
|
}
|
|
4727
4891
|
async function loadIssue(issueRef, input) {
|
|
4728
4892
|
if (input) {
|
|
4729
|
-
const raw = input === "-" ? await readStdin() : await
|
|
4893
|
+
const raw = input === "-" ? await readStdin() : await readFile14(input, "utf8");
|
|
4730
4894
|
return normalize2(JSON.parse(stripBom(raw)));
|
|
4731
4895
|
}
|
|
4732
4896
|
const num = numericRef(issueRef);
|
|
4733
|
-
const { stdout: stdout2 } = await
|
|
4897
|
+
const { stdout: stdout2 } = await exec5("gh", ["issue", "view", num, "--json", "number,title,body,comments"]);
|
|
4734
4898
|
const data = normalize2(JSON.parse(stdout2));
|
|
4735
4899
|
data.number ??= Number(num);
|
|
4736
4900
|
return data;
|
|
@@ -4745,9 +4909,9 @@ function normalize2(raw) {
|
|
|
4745
4909
|
}
|
|
4746
4910
|
|
|
4747
4911
|
// src/cli/commands/review.ts
|
|
4748
|
-
import { writeFile as writeFile7, readFile as
|
|
4912
|
+
import { writeFile as writeFile7, readFile as readFile15 } from "fs/promises";
|
|
4749
4913
|
import { execFile as execFile2 } from "child_process";
|
|
4750
|
-
import { promisify as
|
|
4914
|
+
import { promisify as promisify6 } from "util";
|
|
4751
4915
|
import pc14 from "picocolors";
|
|
4752
4916
|
|
|
4753
4917
|
// src/core/agent/review.ts
|
|
@@ -4805,7 +4969,7 @@ ${projectGuide}`
|
|
|
4805
4969
|
}
|
|
4806
4970
|
|
|
4807
4971
|
// src/cli/commands/review.ts
|
|
4808
|
-
var
|
|
4972
|
+
var exec6 = promisify6(execFile2);
|
|
4809
4973
|
async function review(prRef, opts) {
|
|
4810
4974
|
const num = opts.input ? prRef.replace(/^#/, "") : numericRef(prRef);
|
|
4811
4975
|
const diff = await loadDiff(num, opts.input);
|
|
@@ -4821,19 +4985,19 @@ async function review(prRef, opts) {
|
|
|
4821
4985
|
}
|
|
4822
4986
|
}
|
|
4823
4987
|
async function loadDiff(num, input) {
|
|
4824
|
-
if (input) return input === "-" ? readStdin() :
|
|
4825
|
-
const { stdout: stdout2 } = await
|
|
4988
|
+
if (input) return input === "-" ? readStdin() : readFile15(input, "utf8");
|
|
4989
|
+
const { stdout: stdout2 } = await exec6("gh", ["pr", "diff", num]);
|
|
4826
4990
|
return stdout2;
|
|
4827
4991
|
}
|
|
4828
4992
|
async function loadMeta(num, input) {
|
|
4829
4993
|
if (input) return { number: Number(num) || void 0, title: `PR ${num}`, body: "" };
|
|
4830
|
-
const { stdout: stdout2 } = await
|
|
4994
|
+
const { stdout: stdout2 } = await exec6("gh", ["pr", "view", num, "--json", "number,title,body"]);
|
|
4831
4995
|
const raw = JSON.parse(stdout2);
|
|
4832
4996
|
return { number: raw.number, title: raw.title ?? "", body: raw.body ?? "" };
|
|
4833
4997
|
}
|
|
4834
4998
|
|
|
4835
4999
|
// src/cli/index.ts
|
|
4836
|
-
import { join as
|
|
5000
|
+
import { join as join10 } from "path";
|
|
4837
5001
|
|
|
4838
5002
|
// src/core/config/dotenv.ts
|
|
4839
5003
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
@@ -4904,7 +5068,7 @@ function buildProgram() {
|
|
|
4904
5068
|
}
|
|
4905
5069
|
async function main() {
|
|
4906
5070
|
try {
|
|
4907
|
-
loadDotenv([
|
|
5071
|
+
loadDotenv([join10(configDir(), ".env"), join10(process.cwd(), ".env")]);
|
|
4908
5072
|
await resolveLocale();
|
|
4909
5073
|
await buildProgram().parseAsync(process.argv);
|
|
4910
5074
|
} catch (err) {
|