@nikitadmitrieff/feedback-chat 0.1.1 → 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 +72 -12
- 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
|
@@ -95,6 +95,19 @@ function cn(...inputs) {
|
|
|
95
95
|
|
|
96
96
|
// src/client/ui/tooltip.tsx
|
|
97
97
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
98
|
+
function TooltipProvider({
|
|
99
|
+
delayDuration = 0,
|
|
100
|
+
...props
|
|
101
|
+
}) {
|
|
102
|
+
return /* @__PURE__ */ jsx(
|
|
103
|
+
TooltipPrimitive.Provider,
|
|
104
|
+
{
|
|
105
|
+
"data-slot": "tooltip-provider",
|
|
106
|
+
delayDuration,
|
|
107
|
+
...props
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
}
|
|
98
111
|
function Tooltip({
|
|
99
112
|
...props
|
|
100
113
|
}) {
|
|
@@ -1687,6 +1700,19 @@ var STAGE_INDEX = {
|
|
|
1687
1700
|
failed: -1,
|
|
1688
1701
|
rejected: -1
|
|
1689
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
|
+
}
|
|
1690
1716
|
var TERMINAL_STAGES = ["deployed", "failed", "rejected"];
|
|
1691
1717
|
var POLL_INTERVAL_MS = 5e3;
|
|
1692
1718
|
var PREVIEW_POLL_INTERVAL_MS = 15e3;
|
|
@@ -1750,6 +1776,8 @@ function PipelineTracker({
|
|
|
1750
1776
|
const [changeComment, setChangeComment] = useState6("");
|
|
1751
1777
|
const [confirmReject, setConfirmReject] = useState6(false);
|
|
1752
1778
|
const previousStageRef = useRef3(null);
|
|
1779
|
+
const stageStartRef = useRef3(Date.now());
|
|
1780
|
+
const [elapsed, setElapsed] = useState6(0);
|
|
1753
1781
|
const fetchStatus = useCallback3(async () => {
|
|
1754
1782
|
try {
|
|
1755
1783
|
const res = await fetch(`${statusEndpoint}?issue=${issueNumber}`);
|
|
@@ -1768,6 +1796,17 @@ function PipelineTracker({
|
|
|
1768
1796
|
previousStageRef.current = status.stage;
|
|
1769
1797
|
}
|
|
1770
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]);
|
|
1771
1810
|
useEffect3(() => {
|
|
1772
1811
|
setPipelineActive(issueNumber, "created");
|
|
1773
1812
|
fetchStatus();
|
|
@@ -1815,18 +1854,39 @@ function PipelineTracker({
|
|
|
1815
1854
|
const currentIndex = STAGE_INDEX[status.stage];
|
|
1816
1855
|
const isFailed = status.stage === "failed";
|
|
1817
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);
|
|
1818
1861
|
return /* @__PURE__ */ jsxs10("div", { className: "rounded-2xl border border-border bg-card p-3 space-y-2", children: [
|
|
1819
|
-
/* @__PURE__ */
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
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
|
+
] }),
|
|
1830
1890
|
status.stage === "preview_ready" && status.previewUrl && /* @__PURE__ */ jsxs10("div", { className: "space-y-2.5 pt-2 border-t border-border", children: [
|
|
1831
1891
|
/* @__PURE__ */ jsxs10(
|
|
1832
1892
|
"a",
|
|
@@ -2061,7 +2121,7 @@ function FeedbackPanel({ isOpen, onToggle, apiUrl = "/api/feedback/chat" }) {
|
|
|
2061
2121
|
const clearPendingMessage = useCallback4(() => {
|
|
2062
2122
|
setPendingMessage("");
|
|
2063
2123
|
}, []);
|
|
2064
|
-
return /* @__PURE__ */ jsxs12(
|
|
2124
|
+
return /* @__PURE__ */ jsxs12(TooltipProvider, { children: [
|
|
2065
2125
|
/* @__PURE__ */ jsx16(
|
|
2066
2126
|
"div",
|
|
2067
2127
|
{
|
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",
|