@4pm/cli 1.20.0 → 1.21.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/dist/index.js +47 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42819,12 +42819,13 @@ function authedUrl2(url, token) {
|
|
|
42819
42819
|
if (url.startsWith("https://")) return url.replace("https://", `https://x-access-token:${token}@`);
|
|
42820
42820
|
return url;
|
|
42821
42821
|
}
|
|
42822
|
-
function buildPrompt4(tickets) {
|
|
42822
|
+
function buildPrompt4(tickets, customPrompt) {
|
|
42823
42823
|
const transcript = tickets.map((t2) => {
|
|
42824
42824
|
const thread = t2.messages.map((m2) => ` [${m2.author}] ${m2.body}`).join("\n");
|
|
42825
42825
|
return `### Ticket ${t2.id} \u2014 ${t2.subject} (${t2.category})
|
|
42826
42826
|
${thread}`;
|
|
42827
42827
|
}).join("\n\n");
|
|
42828
|
+
const ids = tickets.map((t2) => t2.id);
|
|
42828
42829
|
return [
|
|
42829
42830
|
"You are maintaining the 4PM product FAQ, stored as markdown in THIS git repository (your CWD).",
|
|
42830
42831
|
"From the resolved support tickets below, distil reusable, product-general FAQ entries and add or",
|
|
@@ -42839,11 +42840,45 @@ ${thread}`;
|
|
|
42839
42840
|
"- Only edit markdown documentation files; do not touch code, CI, or unrelated files.",
|
|
42840
42841
|
"- Do NOT run git commit/push or open a PR yourself \u2014 just leave the edited files in the working",
|
|
42841
42842
|
" tree; the surrounding tooling handles commit + PR.",
|
|
42843
|
+
...customPrompt?.trim() ? ["", "Additional instruction from the operator (follow it too):", customPrompt.trim()] : [],
|
|
42842
42844
|
"",
|
|
42843
42845
|
"===== RESOLVED TICKETS =====",
|
|
42844
|
-
transcript
|
|
42846
|
+
transcript,
|
|
42847
|
+
"",
|
|
42848
|
+
"===== REQUIRED FINAL OUTPUT =====",
|
|
42849
|
+
"After finishing all file edits, output \u2014 as the LAST thing, on its own \u2014 a single fenced JSON",
|
|
42850
|
+
"block summarizing what you did PER TICKET (created/updated which FAQ entry, or skipped + why):",
|
|
42851
|
+
"```json",
|
|
42852
|
+
`{"perTicket":[${ids.map((id) => `{"ticketId":"${id}","summary":"..."}`).join(",")}]}`,
|
|
42853
|
+
"```",
|
|
42854
|
+
"Include exactly one entry per ticket id above; keep each summary to one or two sentences."
|
|
42845
42855
|
].join("\n");
|
|
42846
42856
|
}
|
|
42857
|
+
function parsePerTicket(text) {
|
|
42858
|
+
const idx = text.lastIndexOf('"perTicket"');
|
|
42859
|
+
if (idx === -1) return [];
|
|
42860
|
+
const open = text.lastIndexOf("{", idx);
|
|
42861
|
+
if (open === -1) return [];
|
|
42862
|
+
let depth = 0;
|
|
42863
|
+
for (let i = open; i < text.length; i++) {
|
|
42864
|
+
if (text[i] === "{") depth++;
|
|
42865
|
+
else if (text[i] === "}") {
|
|
42866
|
+
depth--;
|
|
42867
|
+
if (depth === 0) {
|
|
42868
|
+
try {
|
|
42869
|
+
const obj = JSON.parse(text.slice(open, i + 1));
|
|
42870
|
+
if (Array.isArray(obj.perTicket)) {
|
|
42871
|
+
return obj.perTicket.filter((e) => !!e && typeof e.ticketId === "string").map((e) => ({ ticketId: e.ticketId, summary: String(e.summary ?? "") }));
|
|
42872
|
+
}
|
|
42873
|
+
} catch {
|
|
42874
|
+
return [];
|
|
42875
|
+
}
|
|
42876
|
+
return [];
|
|
42877
|
+
}
|
|
42878
|
+
}
|
|
42879
|
+
}
|
|
42880
|
+
return [];
|
|
42881
|
+
}
|
|
42847
42882
|
function runClaudeOnce2(cmd, profile, prompt, cwd2, extraEnv) {
|
|
42848
42883
|
return new Promise((resolve10, reject) => {
|
|
42849
42884
|
const isClaude = cmd.includes("claude");
|
|
@@ -42889,12 +42924,13 @@ async function runClaudeWithFailover2(prompt, cwd2, ai2) {
|
|
|
42889
42924
|
let lastReason = "no attempt";
|
|
42890
42925
|
for (let i = 0; i < attempts.length; i++) {
|
|
42891
42926
|
const { code, out, err, usage } = await runClaudeOnce2(ai2.cmd, attempts[i], prompt, cwd2, ai2.env);
|
|
42892
|
-
if (code === 0) return usage;
|
|
42927
|
+
if (code === 0) return { usage, text: out.trim() };
|
|
42893
42928
|
lastReason = `exited ${code}: ${(err || out).slice(0, 500)}`;
|
|
42894
42929
|
if (i === attempts.length - 1) break;
|
|
42895
42930
|
}
|
|
42896
42931
|
throw new Error(`claude ${lastReason}`);
|
|
42897
42932
|
}
|
|
42933
|
+
var MAX_OUTPUT_CHARS = 8e3;
|
|
42898
42934
|
async function runFaqCompose(req, ai2) {
|
|
42899
42935
|
if (!req.repo.token) return { prUrl: "", error: "no write token" };
|
|
42900
42936
|
const workDir = join24(tmpdir3(), "4pm-faq-sync", randomUUID3());
|
|
@@ -42906,10 +42942,14 @@ async function runFaqCompose(req, ai2) {
|
|
|
42906
42942
|
await execFileAsync2("git", ["-C", workDir, "config", "user.name", "4PM FAQ Bot"], { env: gitEnv });
|
|
42907
42943
|
await execFileAsync2("git", ["-C", workDir, "config", "user.email", "faq-bot@4pm.app"], { env: gitEnv });
|
|
42908
42944
|
await execFileAsync2("git", ["-C", workDir, "checkout", "-b", req.headBranch], { env: gitEnv });
|
|
42909
|
-
const usage = await runClaudeWithFailover2(buildPrompt4(req.tickets), workDir, ai2);
|
|
42945
|
+
const { usage, text } = await runClaudeWithFailover2(buildPrompt4(req.tickets, req.customPrompt), workDir, ai2);
|
|
42946
|
+
const output = text.slice(0, MAX_OUTPUT_CHARS);
|
|
42947
|
+
const perTicket = parsePerTicket(text);
|
|
42910
42948
|
await execFileAsync2("git", ["-C", workDir, "add", "-A"], { env: gitEnv });
|
|
42911
42949
|
const staged = await execFileAsync2("git", ["-C", workDir, "diff", "--cached", "--name-only"], { env: gitEnv });
|
|
42912
|
-
if (!staged.stdout.trim())
|
|
42950
|
+
if (!staged.stdout.trim()) {
|
|
42951
|
+
return { prUrl: "", output, perTicket, tokens: usage.tokens || estimateTokens2(text), finishedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
42952
|
+
}
|
|
42913
42953
|
await execFileAsync2("git", ["-C", workDir, "commit", "-m", "docs(faq): synthesize from support tickets (4PM)"], { env: gitEnv });
|
|
42914
42954
|
await execFileAsync2("git", ["-C", workDir, "push", "-u", "origin", req.headBranch], { env: gitEnv });
|
|
42915
42955
|
let prUrl = "";
|
|
@@ -42927,6 +42967,8 @@ async function runFaqCompose(req, ai2) {
|
|
|
42927
42967
|
return {
|
|
42928
42968
|
prUrl,
|
|
42929
42969
|
branch: req.headBranch,
|
|
42970
|
+
output,
|
|
42971
|
+
perTicket,
|
|
42930
42972
|
tokens: u2.tokens,
|
|
42931
42973
|
...usage.tokens > 0 ? { tokensBreakdown: { input: usage.input, output: usage.output, cacheRead: usage.cacheRead, cacheCreation: usage.cacheCreation } } : {},
|
|
42932
42974
|
finishedAt: (/* @__PURE__ */ new Date()).toISOString()
|