@nikitadmitrieff/feedback-chat 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/client/index.d.ts +6 -1
- package/dist/client/index.js +58 -11
- package/dist/server/index.d.ts +6 -1
- package/dist/server/index.js +24 -9
- package/package.json +2 -2
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
3
|
type Stage = 'created' | 'queued' | 'running' | 'validating' | 'preview_ready' | 'deployed' | 'failed' | 'rejected';
|
|
4
|
+
type ActivityEntry = {
|
|
5
|
+
message: string;
|
|
6
|
+
time: string;
|
|
7
|
+
};
|
|
4
8
|
type StatusResponse = {
|
|
5
9
|
stage: Stage;
|
|
6
10
|
issueNumber: number;
|
|
@@ -9,6 +13,7 @@ type StatusResponse = {
|
|
|
9
13
|
previewUrl?: string;
|
|
10
14
|
prNumber?: number;
|
|
11
15
|
prUrl?: string;
|
|
16
|
+
activity?: ActivityEntry[];
|
|
12
17
|
};
|
|
13
18
|
type Conversation = {
|
|
14
19
|
id: string;
|
|
@@ -40,4 +45,4 @@ type PipelineTrackerProps = {
|
|
|
40
45
|
};
|
|
41
46
|
declare function PipelineTracker({ issueUrl, statusEndpoint, }: PipelineTrackerProps): react_jsx_runtime.JSX.Element;
|
|
42
47
|
|
|
43
|
-
export { type Conversation, FeedbackPanel, type FeedbackPanelProps, PipelineTracker, type Stage, type StatusResponse, useConversations };
|
|
48
|
+
export { type ActivityEntry, type Conversation, FeedbackPanel, type FeedbackPanelProps, PipelineTracker, type Stage, type StatusResponse, useConversations };
|
package/dist/client/index.js
CHANGED
|
@@ -1700,6 +1700,19 @@ var STAGE_INDEX = {
|
|
|
1700
1700
|
failed: -1,
|
|
1701
1701
|
rejected: -1
|
|
1702
1702
|
};
|
|
1703
|
+
var STAGE_MESSAGE = {
|
|
1704
|
+
created: "Creating issue\u2026",
|
|
1705
|
+
queued: "Waiting for agent\u2026",
|
|
1706
|
+
running: "Running\u2026",
|
|
1707
|
+
validating: "Checking build\u2026",
|
|
1708
|
+
preview_ready: "Deploying preview\u2026"
|
|
1709
|
+
};
|
|
1710
|
+
function formatElapsed(ms) {
|
|
1711
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
1712
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1713
|
+
const seconds = totalSeconds % 60;
|
|
1714
|
+
return `${minutes}:${seconds.toString().padStart(2, "0")}`;
|
|
1715
|
+
}
|
|
1703
1716
|
var TERMINAL_STAGES = ["deployed", "failed", "rejected"];
|
|
1704
1717
|
var POLL_INTERVAL_MS = 5e3;
|
|
1705
1718
|
var PREVIEW_POLL_INTERVAL_MS = 15e3;
|
|
@@ -1763,6 +1776,8 @@ function PipelineTracker({
|
|
|
1763
1776
|
const [changeComment, setChangeComment] = useState6("");
|
|
1764
1777
|
const [confirmReject, setConfirmReject] = useState6(false);
|
|
1765
1778
|
const previousStageRef = useRef3(null);
|
|
1779
|
+
const stageStartRef = useRef3(Date.now());
|
|
1780
|
+
const [elapsed, setElapsed] = useState6(0);
|
|
1766
1781
|
const fetchStatus = useCallback3(async () => {
|
|
1767
1782
|
try {
|
|
1768
1783
|
const res = await fetch(`${statusEndpoint}?issue=${issueNumber}`);
|
|
@@ -1781,6 +1796,17 @@ function PipelineTracker({
|
|
|
1781
1796
|
previousStageRef.current = status.stage;
|
|
1782
1797
|
}
|
|
1783
1798
|
}, [status.stage]);
|
|
1799
|
+
useEffect3(() => {
|
|
1800
|
+
stageStartRef.current = Date.now();
|
|
1801
|
+
setElapsed(0);
|
|
1802
|
+
}, [status.stage]);
|
|
1803
|
+
useEffect3(() => {
|
|
1804
|
+
if (TERMINAL_STAGES.includes(status.stage)) return;
|
|
1805
|
+
const interval = setInterval(() => {
|
|
1806
|
+
setElapsed(Date.now() - stageStartRef.current);
|
|
1807
|
+
}, 1e3);
|
|
1808
|
+
return () => clearInterval(interval);
|
|
1809
|
+
}, [status.stage]);
|
|
1784
1810
|
useEffect3(() => {
|
|
1785
1811
|
setPipelineActive(issueNumber, "created");
|
|
1786
1812
|
fetchStatus();
|
|
@@ -1828,18 +1854,39 @@ function PipelineTracker({
|
|
|
1828
1854
|
const currentIndex = STAGE_INDEX[status.stage];
|
|
1829
1855
|
const isFailed = status.stage === "failed";
|
|
1830
1856
|
const failedAtIndex = isFailed ? getFailedStepIndex(previousStageRef.current) : -1;
|
|
1857
|
+
const progressIndex = isFailed ? failedAtIndex : currentIndex;
|
|
1858
|
+
const latestActivity = status.activity?.at(-1)?.message;
|
|
1859
|
+
const activityMessage = latestActivity || STAGE_MESSAGE[status.stage] || "";
|
|
1860
|
+
const isActive = !TERMINAL_STAGES.includes(status.stage);
|
|
1831
1861
|
return /* @__PURE__ */ jsxs10("div", { className: "rounded-2xl border border-border bg-card p-3 space-y-2", children: [
|
|
1832
|
-
/* @__PURE__ */
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1862
|
+
/* @__PURE__ */ jsxs10("div", { className: "relative", children: [
|
|
1863
|
+
/* @__PURE__ */ jsx14("div", { className: "absolute left-[7.5px] top-3 bottom-3 w-px bg-muted-foreground/15" }),
|
|
1864
|
+
progressIndex > 0 && /* @__PURE__ */ jsx14(
|
|
1865
|
+
"div",
|
|
1866
|
+
{
|
|
1867
|
+
className: `absolute left-[7.5px] top-3 w-px transition-all duration-700 ease-out ${isFailed ? "bg-destructive/50" : "bg-emerald-500/50"}`,
|
|
1868
|
+
style: { height: `${progressIndex * 24}px` }
|
|
1869
|
+
}
|
|
1870
|
+
),
|
|
1871
|
+
STEPS.map((step, i) => {
|
|
1872
|
+
const state = deriveStepState(i, currentIndex, failedAtIndex, status.stage);
|
|
1873
|
+
return /* @__PURE__ */ jsxs10("div", { children: [
|
|
1874
|
+
/* @__PURE__ */ jsxs10("div", { className: "relative flex items-center gap-2 h-6", children: [
|
|
1875
|
+
/* @__PURE__ */ jsx14(StepDot, { state }),
|
|
1876
|
+
/* @__PURE__ */ jsx14("span", { className: `text-xs truncate ${STEP_LABEL_CLASS[state]}`, children: step.label }),
|
|
1877
|
+
i === 0 && /* @__PURE__ */ jsxs10("span", { className: "ml-auto text-[11px] text-muted-foreground/60 shrink-0", children: [
|
|
1878
|
+
"#",
|
|
1879
|
+
issueNumber
|
|
1880
|
+
] })
|
|
1881
|
+
] }),
|
|
1882
|
+
state === "failed" && status.failReason && /* @__PURE__ */ jsx14("div", { className: "pl-6 pb-0.5", children: /* @__PURE__ */ jsx14("span", { className: "text-[10px] text-destructive/80 line-clamp-2", children: status.failReason }) }),
|
|
1883
|
+
state === "active" && isActive && activityMessage && /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 pl-6 h-5", children: [
|
|
1884
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[10px] text-muted-foreground truncate flex-1", children: activityMessage }),
|
|
1885
|
+
/* @__PURE__ */ jsx14("span", { className: "text-[10px] text-muted-foreground/60 tabular-nums shrink-0", children: formatElapsed(elapsed) })
|
|
1886
|
+
] })
|
|
1887
|
+
] }, step.stage);
|
|
1888
|
+
})
|
|
1889
|
+
] }),
|
|
1843
1890
|
status.stage === "preview_ready" && status.previewUrl && /* @__PURE__ */ jsxs10("div", { className: "space-y-2.5 pt-2 border-t border-border", children: [
|
|
1844
1891
|
/* @__PURE__ */ jsxs10(
|
|
1845
1892
|
"a",
|
package/dist/server/index.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ type StatusHandlerConfig = {
|
|
|
37
37
|
agentUrl?: string;
|
|
38
38
|
};
|
|
39
39
|
type Stage = 'created' | 'queued' | 'running' | 'validating' | 'preview_ready' | 'deployed' | 'failed' | 'rejected';
|
|
40
|
+
type ActivityEntry = {
|
|
41
|
+
message: string;
|
|
42
|
+
time: string;
|
|
43
|
+
};
|
|
40
44
|
type StatusResponse = {
|
|
41
45
|
stage: Stage;
|
|
42
46
|
issueNumber: number;
|
|
@@ -45,6 +49,7 @@ type StatusResponse = {
|
|
|
45
49
|
previewUrl?: string;
|
|
46
50
|
prNumber?: number;
|
|
47
51
|
prUrl?: string;
|
|
52
|
+
activity?: ActivityEntry[];
|
|
48
53
|
};
|
|
49
54
|
/**
|
|
50
55
|
* Creates Next.js App Router GET and POST handlers for the feedback status endpoint.
|
|
@@ -112,4 +117,4 @@ declare function createGitHubIssue({ title, body, labels, }: {
|
|
|
112
117
|
labels?: string[];
|
|
113
118
|
}): Promise<string | null>;
|
|
114
119
|
|
|
115
|
-
export { type FeedbackHandlerConfig, type GitHubIssueCreator, type Stage, type StatusHandlerConfig, type StatusResponse, buildDefaultPrompt, createFeedbackHandler, createGitHubIssue, createStatusHandler, createTools };
|
|
120
|
+
export { type ActivityEntry, type FeedbackHandlerConfig, type GitHubIssueCreator, type Stage, type StatusHandlerConfig, type StatusResponse, buildDefaultPrompt, createFeedbackHandler, createGitHubIssue, createStatusHandler, createTools };
|
package/dist/server/index.js
CHANGED
|
@@ -284,17 +284,22 @@ async function getPreviewUrl(config, sha) {
|
|
|
284
284
|
}
|
|
285
285
|
return null;
|
|
286
286
|
}
|
|
287
|
-
async function
|
|
287
|
+
async function getActivity(config, issueNumber) {
|
|
288
288
|
const res = await fetch(
|
|
289
289
|
`${issueEndpoint(config, issueNumber)}/comments?per_page=5&direction=desc`,
|
|
290
290
|
{ headers: githubHeaders(config.token), cache: "no-store" }
|
|
291
291
|
);
|
|
292
|
-
if (!res.ok) return
|
|
292
|
+
if (!res.ok) return [];
|
|
293
293
|
const comments = await res.json();
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
294
|
+
if (!Array.isArray(comments)) return [];
|
|
295
|
+
return comments.slice(0, 3).map((c) => {
|
|
296
|
+
const raw = c.body ?? "";
|
|
297
|
+
const clean = raw.replace(/<[^>]*>/g, "").replace(/```[\s\S]*?```/g, "").replace(/`[^`]*`/g, "").replace(/[#*_~>\-|]/g, "").replace(/\s+/g, " ").trim().slice(0, 120);
|
|
298
|
+
return {
|
|
299
|
+
message: clean,
|
|
300
|
+
time: c.created_at ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
301
|
+
};
|
|
302
|
+
}).reverse();
|
|
298
303
|
}
|
|
299
304
|
async function isAgentRunning(agentUrl, issueNumber) {
|
|
300
305
|
if (!agentUrl) return false;
|
|
@@ -313,8 +318,13 @@ async function deriveStage(config, issueNumber, agentUrl) {
|
|
|
313
318
|
const labels = (issue.labels ?? []).map((l) => l.name);
|
|
314
319
|
const issueUrl = issue.html_url;
|
|
315
320
|
if (labels.includes("agent-failed")) {
|
|
316
|
-
const
|
|
317
|
-
|
|
321
|
+
const activity = await getActivity(config, issueNumber);
|
|
322
|
+
const failComment = activity.find((a) => a.message.startsWith("Agent failed:"));
|
|
323
|
+
let failReason = failComment?.message.replace("Agent failed:", "").trim();
|
|
324
|
+
if (failReason) {
|
|
325
|
+
failReason = failReason.replace(/<[^>]*>/g, "").replace(/```[\s\S]*?```/g, "").replace(/`[^`]*`/g, "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
326
|
+
}
|
|
327
|
+
return { stage: "failed", issueUrl, failReason, activity };
|
|
318
328
|
}
|
|
319
329
|
if (labels.includes("rejected")) {
|
|
320
330
|
return { stage: "rejected", issueUrl };
|
|
@@ -454,7 +464,8 @@ async function handleRequestChanges(config, issueNumber, comment) {
|
|
|
454
464
|
await fetch(`${url}/comments`, {
|
|
455
465
|
method: "POST",
|
|
456
466
|
headers,
|
|
457
|
-
|
|
467
|
+
// Must match the string the agent's retry detection looks for
|
|
468
|
+
body: JSON.stringify({ body: `**Modifications demand\xE9es :**
|
|
458
469
|
|
|
459
470
|
${comment}` })
|
|
460
471
|
});
|
|
@@ -501,6 +512,10 @@ function createStatusHandler(config) {
|
|
|
501
512
|
if (!result) {
|
|
502
513
|
return Response.json({ error: "Issue not found" }, { status: 404 });
|
|
503
514
|
}
|
|
515
|
+
const terminal = ["deployed", "rejected"];
|
|
516
|
+
if (!terminal.includes(result.stage) && !result.activity) {
|
|
517
|
+
result.activity = await getActivity(ghConfig, issueNumber);
|
|
518
|
+
}
|
|
504
519
|
const response = { issueNumber, ...result };
|
|
505
520
|
return Response.json(response);
|
|
506
521
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nikitadmitrieff/feedback-chat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/client/index.js",
|
|
6
6
|
"types": "./dist/client/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"bin": {
|
|
24
24
|
"feedback-chat": "./dist/cli/init.js"
|
|
25
25
|
},
|
|
26
|
-
"files": ["dist"],
|
|
26
|
+
"files": ["dist", "README.md"],
|
|
27
27
|
"scripts": {
|
|
28
28
|
"build": "tsup && cp src/client/styles.css dist/styles.css",
|
|
29
29
|
"dev": "tsup --watch",
|