@hienlh/ppm 0.12.12 → 0.13.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/CHANGELOG.md +10 -1
- package/README.md +11 -0
- package/assets/skills/ppm/SKILL.md +74 -0
- package/assets/skills/ppm/references/cli-reference.md +728 -0
- package/assets/skills/ppm/references/common-tasks.md +139 -0
- package/assets/skills/ppm/references/http-api.md +204 -0
- package/dist/web/assets/ai-settings-section-QE6nBNgN.js +1 -0
- package/dist/web/assets/{api-settings-C3T95dWg.js → api-settings-DAk7D-NP.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-DvZbltvY.js +1 -0
- package/dist/web/assets/{audio-preview-BkbgGtDH.js → audio-preview-J5neETTY.js} +1 -1
- package/dist/web/assets/chat-tab-sVHRa1Fz.js +12 -0
- package/dist/web/assets/{code-editor-BtspASkW.js → code-editor-tMfcFaQ5.js} +4 -4
- package/dist/web/assets/{conflict-editor-Dgsu6fmj.js → conflict-editor-FydCxWTC.js} +1 -1
- package/dist/web/assets/{csv-preview-DcWCjQkZ.js → csv-preview-HMSavgBb.js} +1 -1
- package/dist/web/assets/{database-viewer-C85RxdMV.js → database-viewer-Celi1puH.js} +2 -2
- package/dist/web/assets/{diff-viewer-2pPy97Tl.js → diff-viewer-NgDJLTk9.js} +1 -1
- package/dist/web/assets/{esm-_CLpyLJ_.js → esm-K1XIK4vc.js} +1 -1
- package/dist/web/assets/{extension-store-BZDZ9QRc.js → extension-store-3yZYn07W.js} +1 -1
- package/dist/web/assets/{extension-webview-U1lMYZ0p.js → extension-webview-xWAdCj3q.js} +1 -1
- package/dist/web/assets/{file-store-4BpOJthN.js → file-store-BrbCNyLm.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BxhdxFgj.js +1 -0
- package/dist/web/assets/{image-preview-BcT1SbY2.js → image-preview-C6bFkdZD.js} +1 -1
- package/dist/web/assets/index-BMhiElt6.css +2 -0
- package/dist/web/assets/index-DtbAoxyy.js +23 -0
- package/dist/web/assets/info-3K5VOQVL-BwAZ2zd8.js +1 -0
- package/dist/web/assets/{input-2eDVjcRZ.js → input-Dk49gO8E.js} +1 -1
- package/dist/web/assets/{keybindings-store-BOG1yviy.js → keybindings-store-B-zET-0o.js} +1 -1
- package/dist/web/assets/keybindings-store-DaBV6qhz.js +1 -0
- package/dist/web/assets/{markdown-renderer-Dbam_-04.js → markdown-renderer-BAnnk1pI.js} +3 -3
- package/dist/web/assets/packet-RMMSAZCW-tx2n5Qry.js +1 -0
- package/dist/web/assets/{pdf-preview-BmHVGx32.js → pdf-preview-BNuFTSOL.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-D6S2MqVT.js +1 -0
- package/dist/web/assets/plus-51UQ45rf.js +1 -0
- package/dist/web/assets/{port-forwarding-tab-Dkq1upWC.js → port-forwarding-tab-BbDlGxAs.js} +1 -1
- package/dist/web/assets/{postgres-viewer-BgBJAJ9q.js → postgres-viewer-Cman1YRO.js} +3 -3
- package/dist/web/assets/radar-KQ55EAFF-BviZcL-b.js +1 -0
- package/dist/web/assets/{scroll-area-CdxNNnN-.js → scroll-area-BEllam7_.js} +1 -1
- package/dist/web/assets/{settings-store-CMAssqyb.js → settings-store-BLLR7ed8.js} +2 -2
- package/dist/web/assets/settings-tab-n5X_Dbu4.js +1 -0
- package/dist/web/assets/{sql-query-editor-b7zJ8XPp.js → sql-query-editor-CVAnRFbi.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-4lLAz1es.js → sqlite-viewer-D6JT11uu.js} +1 -1
- package/dist/web/assets/{tab-store-DNBsLdPn.js → tab-store-B3M9hjho.js} +1 -1
- package/dist/web/assets/{terminal-tab-BtnqkN1H.js → terminal-tab-B4kMthYo.js} +1 -1
- package/dist/web/assets/treemap-KZPCXAKY-CM54VdaB.js +1 -0
- package/dist/web/assets/{use-blob-url-QX-XajU8.js → use-blob-url-e9uTXjv5.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-D68oX3XU.js → use-monaco-theme-BkZDwoVd.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-sQS4C_iL.js → vendor-mermaid-Dx86tuVP.js} +2 -2
- package/dist/web/assets/{video-preview-CkOKvVLt.js → video-preview-BftQOOzF.js} +1 -1
- package/dist/web/index.html +18 -18
- package/dist/web/sw.js +1 -1
- package/docs/project-changelog.md +15 -1
- package/package.json +3 -3
- package/scripts/generate-ppm-skill.ts +23 -0
- package/scripts/lib/generate-cli-reference.ts +81 -0
- package/scripts/lib/generate-common-tasks.ts +14 -0
- package/scripts/lib/generate-http-api.ts +145 -0
- package/scripts/lib/generate-skill-md.ts +28 -0
- package/scripts/lib/write-output.ts +17 -0
- package/src/cli/commands/export-cmd.ts +85 -0
- package/src/index.ts +167 -153
- package/src/services/skill-export/backup-existing.ts +33 -0
- package/src/services/skill-export/copy-bundled-skill.ts +36 -0
- package/src/services/skill-export/generate-db-schema.ts +66 -0
- package/src/services/skill-export/index.ts +6 -0
- package/src/services/skill-export/resolve-assets-dir.ts +31 -0
- package/src/services/skill-export/resolve-target-dir.ts +17 -0
- package/src/web/components/chat/chat-tab.tsx +6 -1
- package/src/web/components/chat/message-list.tsx +96 -43
- package/src/web/hooks/use-chat.ts +37 -1
- package/src/web/lib/flatten-expansions.ts +36 -0
- package/templates/skill/SKILL.md.tmpl +74 -0
- package/templates/skill/common-tasks.md +139 -0
- package/assets/skills/ppm-guide/SKILL.md +0 -61
- package/dist/web/assets/ai-settings-section-NNWp6nw7.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-DDuzYaUV.js +0 -1
- package/dist/web/assets/chat-tab-BZlP1qjX.js +0 -12
- package/dist/web/assets/chevron-up-BWBvMZkp.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-BURAevTc.js +0 -1
- package/dist/web/assets/index-BWSRKVZn.js +0 -23
- package/dist/web/assets/index-b6tIZImC.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-tSD4Fpi3.js +0 -1
- package/dist/web/assets/keybindings-store-BvdUoEC7.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-DmDLZUrV.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-w03Pc9ZR.js +0 -1
- package/dist/web/assets/pre-compact-button-Dp7Hs49L.js +0 -1
- package/dist/web/assets/pre-compact-section-DnM5fGSR.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-C9XQvoey.js +0 -1
- package/dist/web/assets/settings-tab-zYWKTq5z.js +0 -1
- package/dist/web/assets/treemap-KZPCXAKY-lmftxSky.js +0 -1
- package/scripts/generate-ppm-guide.ts +0 -92
- package/src/web/components/chat/pre-compact-section.tsx +0 -69
- /package/dist/web/assets/{api-client-DIhJ5qVW.js → api-client-Dvzcc_EO.js} +0 -0
- /package/dist/web/assets/{csv-parser-B5QW8pZ6.js → csv-parser--2WJNgS7.js} +0 -0
- /package/dist/web/assets/{dist-GtkSekuX.js → dist-im4ynINo.js} +0 -0
- /package/dist/web/assets/{katex-C3cZrCvP.js → katex-CKoArbIw.js} +0 -0
- /package/dist/web/assets/{lib-Bu71-TFS.js → lib-DQHnkzGy.js} +0 -0
- /package/dist/web/assets/{react-DMIOAtcX.js → react-GqWghJ-L.js} +0 -0
- /package/dist/web/assets/{refresh-cw-BjrAbUJe.js → refresh-cw-LlbZDJpO.js} +0 -0
- /package/dist/web/assets/{sql-completion-provider-CULTsCqR.js → sql-completion-provider-C3cq9j99.js} +0 -0
- /package/dist/web/assets/{table-tf7pRkME.js → table-Dq575bPF.js} +0 -0
- /package/dist/web/assets/{text-wrap-BV-R4Vvy.js → text-wrap-Cn6BNQfq.js} +0 -0
- /package/dist/web/assets/{trash-2-DjQOpgUV.js → trash-2-CJYoLw7Q.js} +0 -0
- /package/dist/web/assets/{utils-CQux7CsO.js → utils-CTg5uAYR.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-K3_Xwigj.js → vendor-xterm-CU2c3f0A.js} +0 -0
|
@@ -5,10 +5,7 @@ import type { ChatMessage, ChatEvent } from "../../../types/chat";
|
|
|
5
5
|
import type { SessionPhase } from "../../../types/api";
|
|
6
6
|
import type { BashPartialEntry } from "../../hooks/use-chat";
|
|
7
7
|
import { ToolCard } from "./tool-cards";
|
|
8
|
-
import { extractJsonlPath } from "./pre-compact-button";
|
|
9
|
-
const PreCompactSection = lazy(() =>
|
|
10
|
-
import("./pre-compact-section").then((m) => ({ default: m.PreCompactSection }))
|
|
11
|
-
);
|
|
8
|
+
import { extractJsonlPath, PreCompactButton, type PreCompactStatus } from "./pre-compact-button";
|
|
12
9
|
const MarkdownRenderer = lazy(() =>
|
|
13
10
|
import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
|
|
14
11
|
);
|
|
@@ -59,6 +56,10 @@ interface MessageListProps {
|
|
|
59
56
|
onSelectSession?: (session: import("../../../types/chat").SessionInfo) => void;
|
|
60
57
|
/** Partial bash output ref from useChat for real-time streaming */
|
|
61
58
|
bashPartialOutput?: React.RefObject<Map<string, BashPartialEntry>>;
|
|
59
|
+
/** Fetches pre-compact transcript and prepends messages. Returns loaded count. */
|
|
60
|
+
onExpandCompact?: (compactMessageId: string, jsonlPath: string) => Promise<number>;
|
|
61
|
+
/** Whether a given compact message has already been expanded. */
|
|
62
|
+
isCompactExpanded?: (compactMessageId: string) => boolean;
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function MessageList({
|
|
@@ -75,6 +76,8 @@ export function MessageList({
|
|
|
75
76
|
projectName,
|
|
76
77
|
onFork,
|
|
77
78
|
bashPartialOutput,
|
|
79
|
+
onExpandCompact,
|
|
80
|
+
isCompactExpanded,
|
|
78
81
|
}: MessageListProps) {
|
|
79
82
|
// Scroll handled by StickToBottom wrapper — no manual scroll logic needed
|
|
80
83
|
|
|
@@ -106,6 +109,15 @@ export function MessageList({
|
|
|
106
109
|
onFork?.(msgContent, msgId);
|
|
107
110
|
}, [onFork]);
|
|
108
111
|
|
|
112
|
+
// Wrap expandCompact: bump visibleCount by loaded count so expansion is immediately visible
|
|
113
|
+
// in the paginated view (pre-compact messages land at top of flattened array, above pagination window).
|
|
114
|
+
const handleExpandCompact = useCallback(async (compactId: string, jsonlPath: string): Promise<number> => {
|
|
115
|
+
if (!onExpandCompact) throw new Error("Expansion not wired");
|
|
116
|
+
const count = await onExpandCompact(compactId, jsonlPath);
|
|
117
|
+
setVisibleCount((c) => c + count);
|
|
118
|
+
return count;
|
|
119
|
+
}, [onExpandCompact]);
|
|
120
|
+
|
|
109
121
|
if (messagesLoading) {
|
|
110
122
|
return (
|
|
111
123
|
<div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
|
|
@@ -126,8 +138,8 @@ export function MessageList({
|
|
|
126
138
|
|
|
127
139
|
return (
|
|
128
140
|
<div className="relative flex-1 overflow-hidden flex flex-col min-h-0">
|
|
129
|
-
<StickToBottom className="flex-1 overflow-y-auto overflow-x-hidden [contain:strict]" resize="smooth" initial="instant">
|
|
130
|
-
<StickToBottom.Content className="p-4 space-y-4 select-none">
|
|
141
|
+
<StickToBottom className="flex-1 overflow-y-auto overflow-x-hidden [contain:strict] [overflow-anchor:auto]" resize="smooth" initial="instant">
|
|
142
|
+
<StickToBottom.Content className="p-4 space-y-4 select-none [&>*]:[overflow-anchor:auto]">
|
|
131
143
|
{hasMore && (
|
|
132
144
|
<button onClick={() => setVisibleCount((c) => c + PAGE_SIZE)}
|
|
133
145
|
className="w-full py-2 text-xs text-text-secondary hover:text-text-primary bg-surface-elevated/50 hover:bg-surface-elevated rounded-md border border-border/50 transition-colors">
|
|
@@ -146,6 +158,8 @@ export function MessageList({
|
|
|
146
158
|
onFork={msg.role === "user" && onFork ? handleFork : undefined}
|
|
147
159
|
prevMsgId={prevMsg?.sdkUuid ?? prevMsg?.id}
|
|
148
160
|
bashPartialOutput={bashPartialOutput}
|
|
161
|
+
onExpandCompact={handleExpandCompact}
|
|
162
|
+
isCompactExpanded={isCompactExpanded}
|
|
149
163
|
/>
|
|
150
164
|
);
|
|
151
165
|
})}
|
|
@@ -180,16 +194,25 @@ function ScrollToBottomButton() {
|
|
|
180
194
|
);
|
|
181
195
|
}
|
|
182
196
|
|
|
183
|
-
const MessageBubble = memo(function MessageBubble({ message, isStreaming, projectName, onFork, prevMsgId, bashPartialOutput }: {
|
|
197
|
+
const MessageBubble = memo(function MessageBubble({ message, isStreaming, projectName, onFork, prevMsgId, bashPartialOutput, onExpandCompact, isCompactExpanded }: {
|
|
184
198
|
message: ChatMessage; isStreaming: boolean; projectName?: string;
|
|
185
199
|
onFork?: (content: string, messageId: string | undefined) => void;
|
|
186
200
|
prevMsgId?: string;
|
|
187
201
|
bashPartialOutput?: React.RefObject<Map<string, BashPartialEntry>>;
|
|
202
|
+
onExpandCompact?: (compactMessageId: string, jsonlPath: string) => Promise<number>;
|
|
203
|
+
isCompactExpanded?: (compactMessageId: string) => boolean;
|
|
188
204
|
}) {
|
|
189
205
|
if (message.role === "user") {
|
|
190
206
|
const handleFork = onFork ? () => onFork(message.content, prevMsgId) : undefined;
|
|
191
207
|
return (
|
|
192
|
-
<UserBubble
|
|
208
|
+
<UserBubble
|
|
209
|
+
content={message.content}
|
|
210
|
+
messageId={message.id}
|
|
211
|
+
projectName={projectName}
|
|
212
|
+
onFork={handleFork}
|
|
213
|
+
onExpandCompact={onExpandCompact}
|
|
214
|
+
isCompactExpanded={isCompactExpanded}
|
|
215
|
+
/>
|
|
193
216
|
);
|
|
194
217
|
}
|
|
195
218
|
|
|
@@ -206,7 +229,7 @@ const MessageBubble = memo(function MessageBubble({ message, isStreaming, projec
|
|
|
206
229
|
return (
|
|
207
230
|
<div className="flex flex-col gap-2">
|
|
208
231
|
{message.events && message.events.length > 0
|
|
209
|
-
? <InterleavedEvents events={message.events} isStreaming={isStreaming} projectName={projectName} bashPartialOutput={bashPartialOutput} />
|
|
232
|
+
? <InterleavedEvents events={message.events} isStreaming={isStreaming} projectName={projectName} bashPartialOutput={bashPartialOutput} messageId={message.id} onExpandCompact={onExpandCompact} isCompactExpanded={isCompactExpanded} />
|
|
210
233
|
: message.content && (
|
|
211
234
|
<div className="text-sm text-text-primary select-text">
|
|
212
235
|
<MarkdownContent content={message.content} projectName={projectName} />
|
|
@@ -313,7 +336,14 @@ function isPdfPath(path: string): boolean {
|
|
|
313
336
|
const SYSTEM_TAG_NAMES = new Set(["task-notification", "environment_details"]);
|
|
314
337
|
|
|
315
338
|
/** User message bubble — full width, collapsible, with system tag badges */
|
|
316
|
-
function UserBubble({ content,
|
|
339
|
+
function UserBubble({ content, messageId, projectName, onFork, onExpandCompact, isCompactExpanded }: {
|
|
340
|
+
content: string;
|
|
341
|
+
messageId?: string;
|
|
342
|
+
projectName?: string;
|
|
343
|
+
onFork?: () => void;
|
|
344
|
+
onExpandCompact?: (compactMessageId: string, jsonlPath: string) => Promise<number>;
|
|
345
|
+
isCompactExpanded?: (compactMessageId: string) => boolean;
|
|
346
|
+
}) {
|
|
317
347
|
const { files, text, tags, command, jsonlPath } = useMemo(() => {
|
|
318
348
|
const parsed = parseUserAttachments(content);
|
|
319
349
|
const { cleanText: noSysTags, tags } = extractSystemTags(parsed.text);
|
|
@@ -321,6 +351,23 @@ function UserBubble({ content, projectName, onFork }: { content: string; project
|
|
|
321
351
|
return { files: parsed.files, text: cleanText, tags, command, jsonlPath: extractJsonlPath(cleanText) };
|
|
322
352
|
}, [content]);
|
|
323
353
|
|
|
354
|
+
// Pre-compact expansion state — local per button instance
|
|
355
|
+
const [preCompactStatus, setPreCompactStatus] = useState<PreCompactStatus>(() =>
|
|
356
|
+
messageId && isCompactExpanded?.(messageId) ? "loaded" : "idle",
|
|
357
|
+
);
|
|
358
|
+
const [preCompactCount, setPreCompactCount] = useState<number | undefined>();
|
|
359
|
+
const handleExpand = useCallback(async () => {
|
|
360
|
+
if (!jsonlPath || !messageId || !onExpandCompact) return;
|
|
361
|
+
setPreCompactStatus("loading");
|
|
362
|
+
try {
|
|
363
|
+
const count = await onExpandCompact(messageId, jsonlPath);
|
|
364
|
+
setPreCompactCount(count);
|
|
365
|
+
setPreCompactStatus("loaded");
|
|
366
|
+
} catch {
|
|
367
|
+
setPreCompactStatus("error");
|
|
368
|
+
}
|
|
369
|
+
}, [jsonlPath, messageId, onExpandCompact]);
|
|
370
|
+
|
|
324
371
|
const isSystemContext = tags.some((t) => SYSTEM_TAG_NAMES.has(t.name));
|
|
325
372
|
|
|
326
373
|
const [expanded, setExpanded] = useState(false);
|
|
@@ -403,22 +450,14 @@ function UserBubble({ content, projectName, onFork }: { content: string; project
|
|
|
403
450
|
{expanded ? <><ChevronUp className="size-3" />Show less</> : <><ChevronDown className="size-3" />Show more</>}
|
|
404
451
|
</button>
|
|
405
452
|
)}
|
|
406
|
-
{/* Expand compacted conversation: detect JSONL path in compact summary user message
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
key={msg.id ?? `pc-${idx}`}
|
|
415
|
-
message={msg}
|
|
416
|
-
isStreaming={false}
|
|
417
|
-
projectName={projectName}
|
|
418
|
-
/>
|
|
419
|
-
)}
|
|
420
|
-
/>
|
|
421
|
-
</Suspense>
|
|
453
|
+
{/* Expand compacted conversation: detect JSONL path in compact summary user message.
|
|
454
|
+
Prepends pre-compact messages into main flattened list (see useChat.expandCompact). */}
|
|
455
|
+
{jsonlPath && messageId && onExpandCompact && (
|
|
456
|
+
<PreCompactButton
|
|
457
|
+
status={preCompactStatus}
|
|
458
|
+
onLoad={preCompactStatus === "idle" || preCompactStatus === "error" ? handleExpand : undefined}
|
|
459
|
+
count={preCompactCount}
|
|
460
|
+
/>
|
|
422
461
|
)}
|
|
423
462
|
{/* Fork/Rewind button — only for real user messages */}
|
|
424
463
|
{!isSystemContext && onFork && (
|
|
@@ -694,7 +733,31 @@ type EventGroup =
|
|
|
694
733
|
| { kind: "thinking"; content: string }
|
|
695
734
|
| { kind: "tool"; tool: ChatEvent; result?: ChatEvent; completed?: boolean };
|
|
696
735
|
|
|
697
|
-
function InterleavedEvents({ events, isStreaming, projectName, bashPartialOutput
|
|
736
|
+
function InterleavedEvents({ events, isStreaming, projectName, bashPartialOutput, messageId, onExpandCompact, isCompactExpanded }: {
|
|
737
|
+
events: ChatEvent[];
|
|
738
|
+
isStreaming: boolean;
|
|
739
|
+
projectName?: string;
|
|
740
|
+
bashPartialOutput?: React.RefObject<Map<string, BashPartialEntry>>;
|
|
741
|
+
messageId?: string;
|
|
742
|
+
onExpandCompact?: (compactMessageId: string, jsonlPath: string) => Promise<number>;
|
|
743
|
+
isCompactExpanded?: (compactMessageId: string) => boolean;
|
|
744
|
+
}) {
|
|
745
|
+
// Local state for the /compact slash-command path (assistant-authored summary)
|
|
746
|
+
const [preCompactStatus, setPreCompactStatus] = useState<PreCompactStatus>(() =>
|
|
747
|
+
messageId && isCompactExpanded?.(messageId) ? "loaded" : "idle",
|
|
748
|
+
);
|
|
749
|
+
const [preCompactCount, setPreCompactCount] = useState<number | undefined>();
|
|
750
|
+
const handleExpand = useCallback(async (jsonlPath: string) => {
|
|
751
|
+
if (!messageId || !onExpandCompact) return;
|
|
752
|
+
setPreCompactStatus("loading");
|
|
753
|
+
try {
|
|
754
|
+
const count = await onExpandCompact(messageId, jsonlPath);
|
|
755
|
+
setPreCompactCount(count);
|
|
756
|
+
setPreCompactStatus("loaded");
|
|
757
|
+
} catch {
|
|
758
|
+
setPreCompactStatus("error");
|
|
759
|
+
}
|
|
760
|
+
}, [messageId, onExpandCompact]);
|
|
698
761
|
// Group: consecutive text → merged text block; tool_use + tool_result paired by toolUseId
|
|
699
762
|
const groups: EventGroup[] = [];
|
|
700
763
|
let textBuffer = "";
|
|
@@ -813,22 +876,12 @@ function InterleavedEvents({ events, isStreaming, projectName, bashPartialOutput
|
|
|
813
876
|
return (
|
|
814
877
|
<div key={`text-${i}`} className="text-sm text-text-primary select-text">
|
|
815
878
|
<StreamingText content={group.content} animate={isLast} projectName={projectName} />
|
|
816
|
-
{jsonlPath && (
|
|
817
|
-
<
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
<MessageBubble
|
|
823
|
-
key={msg.id ?? `pc-${idx}`}
|
|
824
|
-
message={msg}
|
|
825
|
-
isStreaming={false}
|
|
826
|
-
projectName={projectName}
|
|
827
|
-
bashPartialOutput={bashPartialOutput}
|
|
828
|
-
/>
|
|
829
|
-
)}
|
|
830
|
-
/>
|
|
831
|
-
</Suspense>
|
|
879
|
+
{jsonlPath && messageId && onExpandCompact && (
|
|
880
|
+
<PreCompactButton
|
|
881
|
+
status={preCompactStatus}
|
|
882
|
+
onLoad={preCompactStatus === "idle" || preCompactStatus === "error" ? () => handleExpand(jsonlPath) : undefined}
|
|
883
|
+
count={preCompactCount}
|
|
884
|
+
/>
|
|
832
885
|
)}
|
|
833
886
|
</div>
|
|
834
887
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from "react";
|
|
1
|
+
import { useState, useCallback, useRef, useEffect, useMemo } from "react";
|
|
2
2
|
import { useWebSocket } from "./use-websocket";
|
|
3
3
|
import { api, getAuthToken, projectUrl } from "@/lib/api-client";
|
|
4
|
+
import { flattenWithExpansions, prefixPreCompactIds } from "@/lib/flatten-expansions";
|
|
4
5
|
import { useNotificationStore } from "@/stores/notification-store";
|
|
5
6
|
import { useStreamingStore } from "@/stores/streaming-store";
|
|
6
7
|
import { usePanelStore } from "@/stores/panel-store";
|
|
@@ -41,6 +42,12 @@ export interface BashPartialEntry {
|
|
|
41
42
|
|
|
42
43
|
interface UseChatReturn {
|
|
43
44
|
messages: ChatMessage[];
|
|
45
|
+
/** Messages flattened with pre-compact expansions prepended before their compact cards. */
|
|
46
|
+
renderedMessages: ChatMessage[];
|
|
47
|
+
/** Fetch pre-compact transcript and store expansion. Returns loaded message count. */
|
|
48
|
+
expandCompact: (compactMessageId: string, jsonlPath: string) => Promise<number>;
|
|
49
|
+
/** Whether a given compactMessageId has been expanded. */
|
|
50
|
+
isCompactExpanded: (compactMessageId: string) => boolean;
|
|
44
51
|
messagesLoading: boolean;
|
|
45
52
|
isStreaming: boolean;
|
|
46
53
|
phase: SessionPhase;
|
|
@@ -79,6 +86,8 @@ function isSessionTabActive(sid: string): boolean {
|
|
|
79
86
|
|
|
80
87
|
export function useChat(sessionId: string | null, providerId = "claude", projectName = ""): UseChatReturn {
|
|
81
88
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
|
89
|
+
/** Map of compactMessageId → pre-compact messages (already ID-prefixed). Ephemeral. */
|
|
90
|
+
const [expansions, setExpansions] = useState<Map<string, ChatMessage[]>>(new Map());
|
|
82
91
|
const [messagesLoading, setMessagesLoading] = useState(false);
|
|
83
92
|
const [phase, setPhase] = useState<SessionPhase>("idle");
|
|
84
93
|
const [isReconnecting, setIsReconnecting] = useState(false);
|
|
@@ -604,6 +613,8 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
604
613
|
setPendingApproval(null);
|
|
605
614
|
if (approvalToastRef.current != null) { toast.dismiss(approvalToastRef.current); approvalToastRef.current = null; }
|
|
606
615
|
setCompactStatus(null);
|
|
616
|
+
// Clear ephemeral pre-compact expansions on session change
|
|
617
|
+
setExpansions(new Map());
|
|
607
618
|
streamingContentRef.current = "";
|
|
608
619
|
streamingEventsRef.current = [];
|
|
609
620
|
bashOutputRef.current.clear();
|
|
@@ -796,8 +807,33 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
796
807
|
// Keep refetchRef in sync
|
|
797
808
|
refetchRef.current = refetchMessages;
|
|
798
809
|
|
|
810
|
+
/** Fetch pre-compact transcript. Idempotent: re-expanding same id replaces entry. */
|
|
811
|
+
const expandCompact = useCallback(async (compactMessageId: string, jsonlPath: string): Promise<number> => {
|
|
812
|
+
if (!projectName) throw new Error("No project context available");
|
|
813
|
+
const url = `${projectUrl(projectName)}/chat/pre-compact-messages?jsonlPath=${encodeURIComponent(jsonlPath)}`;
|
|
814
|
+
const loaded = await api.get<ChatMessage[]>(url);
|
|
815
|
+
const prefixed = prefixPreCompactIds(loaded, jsonlPath);
|
|
816
|
+
setExpansions((prev) => {
|
|
817
|
+
const next = new Map(prev);
|
|
818
|
+
next.set(compactMessageId, prefixed);
|
|
819
|
+
return next;
|
|
820
|
+
});
|
|
821
|
+
return prefixed.length;
|
|
822
|
+
}, [projectName]);
|
|
823
|
+
|
|
824
|
+
const isCompactExpanded = useCallback((id: string) => expansions.has(id), [expansions]);
|
|
825
|
+
|
|
826
|
+
/** Flattened view: expansions prepended before their compact cards. */
|
|
827
|
+
const renderedMessages = useMemo(
|
|
828
|
+
() => flattenWithExpansions(messages, expansions),
|
|
829
|
+
[messages, expansions],
|
|
830
|
+
);
|
|
831
|
+
|
|
799
832
|
return {
|
|
800
833
|
messages,
|
|
834
|
+
renderedMessages,
|
|
835
|
+
expandCompact,
|
|
836
|
+
isCompactExpanded,
|
|
801
837
|
messagesLoading,
|
|
802
838
|
isStreaming,
|
|
803
839
|
phase,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ChatMessage } from "../../types/chat";
|
|
2
|
+
|
|
3
|
+
/** Simple deterministic hash of a jsonlPath → short prefix (non-cryptographic). */
|
|
4
|
+
function hashPath(path: string): string {
|
|
5
|
+
let h = 0;
|
|
6
|
+
for (let i = 0; i < path.length; i++) h = ((h << 5) - h + path.charCodeAt(i)) | 0;
|
|
7
|
+
return Math.abs(h).toString(36);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Prefix pre-compact message IDs so they don't collide with current-session IDs.
|
|
12
|
+
* React keys rely on `id`; collisions cause render corruption.
|
|
13
|
+
*/
|
|
14
|
+
export function prefixPreCompactIds(msgs: ChatMessage[], jsonlPath: string): ChatMessage[] {
|
|
15
|
+
const prefix = `pc-${hashPath(jsonlPath)}-`;
|
|
16
|
+
return msgs.map((m) => (m.id ? { ...m, id: `${prefix}${m.id}` } : m));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Flatten messages with expansions prepended before their corresponding compact message.
|
|
21
|
+
* Key of `expansions` = compact message id. Values = already-prefixed ChatMessage[].
|
|
22
|
+
* Preserves reference equality when expansions is empty (zero-cost for non-compact chats).
|
|
23
|
+
*/
|
|
24
|
+
export function flattenWithExpansions(
|
|
25
|
+
messages: ChatMessage[],
|
|
26
|
+
expansions: Map<string, ChatMessage[]>,
|
|
27
|
+
): ChatMessage[] {
|
|
28
|
+
if (expansions.size === 0) return messages;
|
|
29
|
+
const out: ChatMessage[] = [];
|
|
30
|
+
for (const m of messages) {
|
|
31
|
+
const pre = m.id ? expansions.get(m.id) : undefined;
|
|
32
|
+
if (pre && pre.length > 0) out.push(...pre);
|
|
33
|
+
out.push(m);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ppm
|
|
3
|
+
description: Control PPM (Project & Process Manager) via its CLI, HTTP API, and SQLite config database. Use when the user wants to manage PPM projects, query database connections, start/stop the PPM server, view logs, or read/write PPM config.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PPM Skill
|
|
7
|
+
|
|
8
|
+
PPM is a local-first web IDE + project manager. This skill describes how to control PPM from the terminal, over HTTP, and by inspecting its SQLite config database.
|
|
9
|
+
|
|
10
|
+
## Quick Reference
|
|
11
|
+
|
|
12
|
+
| What | Where |
|
|
13
|
+
|---|---|
|
|
14
|
+
| CLI binary | `ppm` |
|
|
15
|
+
| Default server | `http://localhost:8080` |
|
|
16
|
+
| Config DB (prod) | `~/.ppm/ppm.db` (SQLite) |
|
|
17
|
+
| Config DB (dev) | `~/.ppm/ppm.dev.db` (port 8081) |
|
|
18
|
+
| Skill root (user) | `~/.claude/skills/` |
|
|
19
|
+
| Skill root (project) | `<project>/.claude/skills/` |
|
|
20
|
+
|
|
21
|
+
## When to Use This Skill
|
|
22
|
+
|
|
23
|
+
Invoke when the user asks to:
|
|
24
|
+
- Start, stop, restart, or check the PPM server
|
|
25
|
+
- Manage registered projects (`projects list/add/remove`)
|
|
26
|
+
- Manage database connections stored in PPM config (`db list/add/test/query`)
|
|
27
|
+
- Get or set PPM config values (`config get/set`)
|
|
28
|
+
- View PPM logs, status, or report a bug
|
|
29
|
+
- Install, upgrade, or inspect bundled skills / extensions
|
|
30
|
+
- Control the PPM daemon autostart registration
|
|
31
|
+
|
|
32
|
+
## Top Tasks
|
|
33
|
+
|
|
34
|
+
1. **Check if PPM is running** → `ppm status`
|
|
35
|
+
2. **Start server** → `ppm start` (use `--port <n>` to override, `--profile dev` for dev DB)
|
|
36
|
+
3. **List projects** → `ppm projects list`
|
|
37
|
+
4. **Add project** → `ppm projects add <path>`
|
|
38
|
+
5. **List DB connections** → `ppm db list`
|
|
39
|
+
6. **Query a saved DB connection** → `ppm db query <connection-name> "<sql>"`
|
|
40
|
+
7. **Read config** → `ppm config get <key>` (e.g. `port`, `auth.enabled`)
|
|
41
|
+
8. **Tail logs** → `ppm logs --tail 100` or `ppm logs -f` to follow
|
|
42
|
+
9. **Upgrade PPM** → `ppm upgrade` (or `--check` to only check)
|
|
43
|
+
10. **Open in browser** → `ppm open`
|
|
44
|
+
|
|
45
|
+
## Rules of Thumb
|
|
46
|
+
|
|
47
|
+
- Always run `ppm status` before assuming the server is up.
|
|
48
|
+
- Commands exit non-zero on failure and print to stderr. Capture both streams.
|
|
49
|
+
- Listing commands accept `--json` for structured output; prefer JSON when parsing.
|
|
50
|
+
- The config DB is **SQLite**. You may open `~/.ppm/ppm.db` read-only for inspection — see [references/db-schema.md](references/db-schema.md).
|
|
51
|
+
- Do NOT edit the config DB directly while the server is running; use `ppm config set` or the HTTP API.
|
|
52
|
+
|
|
53
|
+
## Links
|
|
54
|
+
|
|
55
|
+
- Complete CLI options, every subcommand → [references/cli-reference.md](references/cli-reference.md)
|
|
56
|
+
- HTTP API routes (25+) → [references/http-api.md](references/http-api.md)
|
|
57
|
+
- Config DB schema (runtime-generated from user's DB) → [references/db-schema.md](references/db-schema.md)
|
|
58
|
+
- Worked recipes for common tasks → [references/common-tasks.md](references/common-tasks.md)
|
|
59
|
+
|
|
60
|
+
## Error Handling
|
|
61
|
+
|
|
62
|
+
- Server not running → `ppm status` exits non-zero; start with `ppm start`.
|
|
63
|
+
- Port conflict → `ppm config set port <n>` then `ppm restart`.
|
|
64
|
+
- Missing config DB → `ppm init` creates defaults.
|
|
65
|
+
- Upgrade failure → re-run `ppm upgrade` or `npm i -g @hienlh/ppm@latest`.
|
|
66
|
+
|
|
67
|
+
## Scope Boundaries
|
|
68
|
+
|
|
69
|
+
This skill covers the `ppm` CLI, its HTTP API, and its config DB. It does **not** cover:
|
|
70
|
+
- Projects managed by PPM (those are user code, outside skill scope).
|
|
71
|
+
- Third-party extensions (inspect via `ppm ext list`).
|
|
72
|
+
- The Claude Agent SDK internals (separate skill).
|
|
73
|
+
|
|
74
|
+
<!-- AUTO:version_footer -->
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# PPM Common Tasks
|
|
2
|
+
|
|
3
|
+
Practical recipes for controlling PPM. Prefer these stable shell invocations over HTTP unless the user has specifically asked for HTTP.
|
|
4
|
+
|
|
5
|
+
## Task 1: Confirm PPM is running
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
ppm status --json
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Non-zero exit = server not running. Use `ppm status` (no flag) for a human-readable view.
|
|
12
|
+
|
|
13
|
+
## Task 2: Start the server
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Default port (from config, usually 8080)
|
|
17
|
+
ppm start
|
|
18
|
+
|
|
19
|
+
# Override port
|
|
20
|
+
ppm start --port 9090
|
|
21
|
+
|
|
22
|
+
# Use dev DB profile (port 8081, ~/.ppm/ppm.dev.db)
|
|
23
|
+
ppm start --profile dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Task 3: Register a project
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Add the current directory
|
|
30
|
+
ppm projects add "$PWD"
|
|
31
|
+
|
|
32
|
+
# List registered projects as JSON
|
|
33
|
+
ppm projects list --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Task 4: Add and test a database connection
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Interactive add
|
|
40
|
+
ppm db add
|
|
41
|
+
|
|
42
|
+
# Non-interactive (check `ppm db add --help` for full flag list)
|
|
43
|
+
ppm db add --name staging --type postgres --host db.example.com --port 5432 --user app --database myapp
|
|
44
|
+
|
|
45
|
+
# Test the connection
|
|
46
|
+
ppm db test staging
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Task 5: Query a saved DB connection
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Run a SQL query against a registered connection
|
|
53
|
+
ppm db query staging "SELECT count(*) AS total FROM users"
|
|
54
|
+
|
|
55
|
+
# Output as JSON for scripting
|
|
56
|
+
ppm db query staging "SELECT id, email FROM users LIMIT 10" --json
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Task 6: Read or change config
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Read a value
|
|
63
|
+
ppm config get port
|
|
64
|
+
ppm config get auth.enabled
|
|
65
|
+
|
|
66
|
+
# Set a value (takes effect on next restart)
|
|
67
|
+
ppm config set port 9090
|
|
68
|
+
ppm config set auth.enabled true
|
|
69
|
+
|
|
70
|
+
# List all keys
|
|
71
|
+
ppm config list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Task 7: Tail the daemon log
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Last 100 lines
|
|
78
|
+
ppm logs --tail 100
|
|
79
|
+
|
|
80
|
+
# Follow live
|
|
81
|
+
ppm logs -f
|
|
82
|
+
|
|
83
|
+
# Clear the log
|
|
84
|
+
ppm logs --clear
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Task 8: Upgrade PPM
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Check for a new version without installing
|
|
91
|
+
ppm upgrade --check
|
|
92
|
+
|
|
93
|
+
# Install the latest
|
|
94
|
+
ppm upgrade
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Task 9: Manage auto-start on boot
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
ppm autostart enable # register
|
|
101
|
+
ppm autostart status --json # inspect
|
|
102
|
+
ppm autostart disable # unregister
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Task 10: Inspect installed skills
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
ppm skills list
|
|
109
|
+
ppm skills list --json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## HTTP API Quick Calls
|
|
113
|
+
|
|
114
|
+
Assume `PPM_BASE=http://localhost:8080`. See [http-api.md](http-api.md) for full list.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# List projects over HTTP
|
|
118
|
+
curl -s $PPM_BASE/api/projects | jq
|
|
119
|
+
|
|
120
|
+
# List DB connections
|
|
121
|
+
curl -s $PPM_BASE/api/db/connections | jq
|
|
122
|
+
|
|
123
|
+
# Get server status (via CLI is easier; HTTP equivalent varies)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Troubleshooting
|
|
127
|
+
|
|
128
|
+
| Symptom | Check |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `ppm: command not found` | Ensure `npm i -g @hienlh/ppm` ran; check `$PATH` includes npm bin. |
|
|
131
|
+
| Server won't start | `ppm logs --tail 50`; check port conflict with `lsof -i :8080`. |
|
|
132
|
+
| Stale tunnel URL | `ppm restart --force`. |
|
|
133
|
+
| Auth prompts unexpected | `ppm config get auth.enabled`; toggle with `ppm config set auth.enabled false`. |
|
|
134
|
+
|
|
135
|
+
## Exit Code Convention
|
|
136
|
+
|
|
137
|
+
- `0` — success
|
|
138
|
+
- `1` — generic error (IO, validation, server not running)
|
|
139
|
+
- `2` — asset/setup missing (e.g. bundled skill assets)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ppm-guide
|
|
3
|
-
description: PPM project structure, commands, config, and development workflow reference
|
|
4
|
-
argument-hint: "[topic]"
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# PPM Guide
|
|
8
|
-
|
|
9
|
-
## Overview
|
|
10
|
-
# PPM Project Overview & Product Development Requirements
|
|
11
|
-
## Project Description
|
|
12
|
-
**PPM** (Personal Project Manager) is a full-stack, mobile-first web IDE designed for developers to manage code projects with AI-powered assistance. It combines a responsive web interface, real-time terminal access, AI chat with tool support, and Git integration into a cohesive development environment.
|
|
13
|
-
Built on the **Bun runtime** for performance, PPM enables developers to:
|
|
14
|
-
- Browse and edit project files with Monaco Editor syntax highlighting
|
|
15
|
-
- Execute commands via xterm.js terminal with full PTY support
|
|
16
|
-
- Chat with Claude AI with file attachments and slash commands
|
|
17
|
-
- View Git status, diffs, and commit graphs in real-time
|
|
18
|
-
|
|
19
|
-
## CLI Commands
|
|
20
|
-
```bash
|
|
21
|
-
bun dev:server # Start backend dev (port 8081, uses ~/.ppm/ppm.dev.db)
|
|
22
|
-
bun dev:web # Start Vite frontend (port 5173)
|
|
23
|
-
bun test # Run all tests
|
|
24
|
-
bun test tests/integration/ # Integration tests only
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Dev Config
|
|
28
|
-
Config is stored in **SQLite** (`~/.ppm/ppm.db`). Dev uses a separate DB:
|
|
29
|
-
|
|
30
|
-
- **Dev**: `~/.ppm/ppm.dev.db` — port **8081**
|
|
31
|
-
- **Production**: `~/.ppm/ppm.db` — port **8080**
|
|
32
|
-
|
|
33
|
-
`bun dev:server` automatically uses the dev database. On a new machine, run `ppm init` to create default config, then `ppm config set port 8081` for dev.
|
|
34
|
-
|
|
35
|
-
## Architecture
|
|
36
|
-
- `src/providers/claude-agent-sdk.ts` — SDK integration, tool execution, streaming
|
|
37
|
-
- `src/server/ws/chat.ts` — WebSocket chat handler
|
|
38
|
-
- `src/web/hooks/use-chat.ts` — Frontend chat state management
|
|
39
|
-
- `src/services/config.service.ts` — Config from SQLite (`~/.ppm/ppm.db`)
|
|
40
|
-
|
|
41
|
-
## Code Standards
|
|
42
|
-
# PPM Code Standards & Conventions
|
|
43
|
-
## File Naming
|
|
44
|
-
| File Type | Convention | Example | Purpose |
|
|
45
|
-
|-----------|-----------|---------|---------|
|
|
46
|
-
| CLI commands | kebab-case | `start-cmd.ts`, `init.ts` | Descriptive command names |
|
|
47
|
-
| Services | kebab-case | `chat.service.ts`, `file.service.ts` | `{feature}.service.ts` pattern |
|
|
48
|
-
| Providers | kebab-case | `claude-agent-sdk.ts`, `mock-provider.ts` | `{name}-provider.ts` or `{name}.ts` |
|
|
49
|
-
| Routes | kebab-case | `chat.ts`, `project-scoped.ts` | Describe HTTP route group |
|
|
50
|
-
| WebSocket | kebab-case | `chat.ts`, `terminal.ts` | Match feature area |
|
|
51
|
-
| Components | PascalCase | `ChatTab.tsx`, `FileTree.tsx` | React convention |
|
|
52
|
-
|
|
53
|
-
## Slash Commands
|
|
54
|
-
Use `/skills` to list all available skills and commands.
|
|
55
|
-
Use `/help` for session help, `/status` for context usage, `/compact` to reduce context.
|
|
56
|
-
|
|
57
|
-
## Dev Workflow
|
|
58
|
-
1. `bun dev:server` — Start backend (port 8081, dev DB)
|
|
59
|
-
2. `bun dev:web` — Start Vite frontend (port 5173)
|
|
60
|
-
3. `bun test` — Run all tests
|
|
61
|
-
4. `bun run typecheck` — TypeScript type checking
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{D as r,_ as i,b as a,c as o,d as s,f as c,g as l,h as u,m as d,p as f,s as p,u as m,v as h,x as g,y as _}from"./vendor-ui-B-89Uj8i.js";import{t as v}from"./createLucideIcon-BjHrJDVb.js";import{i as y}from"./dist-D7KGU7Vl.js";import{n as b,t as x}from"./chevron-up-BWBvMZkp.js";import{t as S}from"./input-2eDVjcRZ.js";import{t as C}from"./refresh-cw-BjrAbUJe.js";import{t as w}from"./trash-2-DjQOpgUV.js";import{i as T,t as E}from"./api-client-DIhJ5qVW.js";import{n as D}from"./utils-CQux7CsO.js";import{a as O,h as k}from"./api-settings-C3T95dWg.js";var A=v(`bell-off`,[[`path`,{d:`M10.268 21a2 2 0 0 0 3.464 0`,key:`vwvbt9`}],[`path`,{d:`M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742`,key:`178tsu`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05`,key:`1hqiys`}]]),j=v(`bot`,[[`path`,{d:`M12 8V4H8`,key:`hb8ula`}],[`rect`,{width:`16`,height:`12`,x:`4`,y:`8`,rx:`2`,key:`enze0r`}],[`path`,{d:`M2 14h2`,key:`vft8re`}],[`path`,{d:`M20 14h2`,key:`4cs60a`}],[`path`,{d:`M15 13v2`,key:`1xurst`}],[`path`,{d:`M9 13v2`,key:`rq6x2g`}]]),M=v(`bug`,[[`path`,{d:`M12 20v-9`,key:`1qisl0`}],[`path`,{d:`M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z`,key:`uouzyp`}],[`path`,{d:`M14.12 3.88 16 2`,key:`qol33r`}],[`path`,{d:`M21 21a4 4 0 0 0-3.81-4`,key:`1b0z45`}],[`path`,{d:`M21 5a4 4 0 0 1-3.55 3.97`,key:`5cxbf6`}],[`path`,{d:`M22 13h-4`,key:`1jl80f`}],[`path`,{d:`M3 21a4 4 0 0 1 3.81-4`,key:`1fjd4g`}],[`path`,{d:`M3 5a4 4 0 0 0 3.55 3.97`,key:`1d7oge`}],[`path`,{d:`M6 13H2`,key:`82j7cp`}],[`path`,{d:`m8 2 1.88 1.88`,key:`fmnt4t`}],[`path`,{d:`M9 7.13V6a3 3 0 1 1 6 0v1.13`,key:`1vgav8`}]]),N=v(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]),P=v(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),F=v(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),I=e(n(),1),L=t();function R({className:e,...t}){return(0,L.jsx)(r,{"data-slot":`label`,className:D(`flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50`,e),...t})}var z=I.forwardRef(({className:e,...t},n)=>(0,L.jsx)(p,{className:D(`peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input`,e),...t,ref:n,children:(0,L.jsx)(o,{className:D(`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0`)})}));z.displayName=p.displayName;function B({...e}){return(0,L.jsx)(l,{"data-slot":`select`,...e})}function V({...e}){return(0,L.jsx)(a,{"data-slot":`select-value`,...e})}function H({className:e,size:t=`default`,children:n,...r}){return(0,L.jsxs)(_,{"data-slot":`select-trigger`,"data-size":t,className:D(`flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground`,e),...r,children:[n,(0,L.jsx)(s,{asChild:!0,children:(0,L.jsx)(b,{className:`size-4 opacity-50`})})]})}function U({className:e,children:t,position:n=`item-aligned`,align:r=`center`,...i}){return(0,L.jsx)(u,{children:(0,L.jsxs)(m,{"data-slot":`select-content`,className:D(`relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95`,n===`popper`&&`data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1`,e),position:n,align:r,...i,children:[(0,L.jsx)(G,{}),(0,L.jsx)(g,{className:D(`p-1`,n===`popper`&&`h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1`),children:t}),(0,L.jsx)(K,{})]})})}function W({className:e,children:t,...n}){return(0,L.jsxs)(c,{"data-slot":`select-item`,className:D(`relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,e),...n,children:[(0,L.jsx)(`span`,{"data-slot":`select-item-indicator`,className:`absolute right-2 flex size-3.5 items-center justify-center`,children:(0,L.jsx)(f,{children:(0,L.jsx)(y,{className:`size-4`})})}),(0,L.jsx)(d,{children:t})]})}function G({className:e,...t}){return(0,L.jsx)(h,{"data-slot":`select-scroll-up-button`,className:D(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,L.jsx)(x,{className:`size-4`})})}function K({className:e,...t}){return(0,L.jsx)(i,{"data-slot":`select-scroll-down-button`,className:D(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,L.jsx)(b,{className:`size-4`})})}var q={claude:`C`,cursor:`▶`,codex:`◆`,gemini:`G`};function J({value:e,onChange:t,projectName:n}){let[r,i]=(0,I.useState)([]),[a,o]=(0,I.useState)(!1),s=(0,I.useRef)(null),c=(0,I.useRef)(0);(0,I.useEffect)(()=>{n&&E.get(`${T(n)}/chat/providers`).then(i).catch(()=>{})},[n]),(0,I.useEffect)(()=>{if(!a)return;let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[a]),(0,I.useEffect)(()=>{a&&(c.current=Math.max(0,r.findIndex(t=>t.id===e)))},[a,e,r]);let l=(0,I.useCallback)(e=>{if(e.key===`Escape`){o(!1);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`){e.preventDefault();let t=e.key===`ArrowDown`?1:-1;c.current=(c.current+t+r.length)%r.length,(s.current?.querySelector(`[data-idx="${c.current}"]`))?.focus()}if(e.key===`Enter`){e.preventDefault();let n=r[c.current];n&&(t(n.id),o(!1))}},[t,r]);if(r.length<=1)return null;let u=r.find(t=>t.id===e),d=q[e]||`?`;return(0,L.jsxs)(`div`,{className:`relative`,children:[(0,L.jsxs)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),o(e=>!e)},className:`inline-flex items-center gap-1 px-2 py-1 rounded-md text-[11px] text-text-subtle hover:text-text-primary hover:bg-surface-elevated transition-colors border border-transparent hover:border-border`,"aria-label":`AI Provider: ${u?.name??e}`,children:[(0,L.jsx)(`span`,{className:`inline-flex h-3.5 w-3.5 items-center justify-center rounded text-[9px] font-bold bg-surface-elevated shrink-0`,children:d}),(0,L.jsx)(`span`,{className:`max-w-[80px] truncate capitalize`,children:u?.name??e})]}),a&&(0,L.jsxs)(`div`,{ref:s,role:`listbox`,"aria-label":`AI Providers`,onKeyDown:l,onMouseDown:e=>e.stopPropagation(),onClick:e=>e.stopPropagation(),className:`absolute bottom-full left-0 mb-1 z-50 w-56 rounded-lg border border-border bg-surface shadow-lg`,children:[(0,L.jsx)(`div`,{className:`px-3 py-2 border-b border-border`,children:(0,L.jsx)(`span`,{className:`text-xs font-medium text-text-secondary`,children:`Provider`})}),(0,L.jsx)(`div`,{className:`py-1`,children:r.map((n,r)=>{let i=q[n.id]||`?`,a=n.id===e;return(0,L.jsxs)(`button`,{"data-idx":r,role:`option`,"aria-selected":a,tabIndex:0,onClick:()=>{t(n.id),o(!1)},className:`w-full flex items-center gap-3 px-3 py-2 text-left transition-colors hover:bg-surface-elevated focus:bg-surface-elevated focus:outline-none ${a?`bg-surface-elevated`:``}`,children:[(0,L.jsx)(`span`,{className:`inline-flex h-5 w-5 items-center justify-center rounded text-[11px] font-bold bg-surface-elevated text-text-subtle shrink-0`,children:i}),(0,L.jsx)(`span`,{className:`flex-1 text-sm font-medium text-text-primary capitalize`,children:n.name}),a&&(0,L.jsx)(y,{className:`size-4 shrink-0 text-primary`})]},n.id)})})]})]})}function Y({providerId:e}){return(0,L.jsx)(`span`,{className:`inline-flex h-4 w-4 items-center justify-center rounded text-[10px] font-bold bg-surface-elevated text-text-subtle shrink-0`,title:e,children:q[e]||`?`})}var X=[{value:`low`,label:`Low`},{value:`medium`,label:`Medium`},{value:`high`,label:`High`}],Z=[{value:`bypassPermissions`,label:`Bypass permissions (default)`},{value:`default`,label:`Ask before edits`},{value:`acceptEdits`,label:`Edit automatically`},{value:`plan`,label:`Plan mode`}],Q={claude:`Claude`,cursor:`Cursor`,codex:`Codex`,gemini:`Gemini`};function $({compact:e}={}){let[t,n]=(0,I.useState)(null),[r,i]=(0,I.useState)(``),[a,o]=(0,I.useState)([]),[s,c]=(0,I.useState)(!1),[l,u]=(0,I.useState)(!1),[d,f]=(0,I.useState)(null),[p,m]=(0,I.useState)(0);(0,I.useEffect)(()=>{O().then(e=>{n(e),i(e.default_provider??`claude`)}).catch(e=>f(e.message))},[]),(0,I.useEffect)(()=>{r&&(c(!0),E.get(`/api/settings/ai/providers/${r}/models`).then(o).catch(()=>o([])).finally(()=>c(!1)))},[r]);let h=t?Object.keys(t.providers).filter(e=>e!==`mock`).map(e=>({id:e,name:Q[e]??e})):[],g=t?.providers[r],_=g?.type===`agent-sdk`||!g?.type&&r===`claude`,v=async(e,i)=>{if(t){u(!0),f(null);try{n(await k({providers:{[r]:{[e]:i}}})),m(e=>e+1)}catch(e){f(e.message)}finally{u(!1)}}},y=e?`text-[11px]`:`text-sm`,b=e?`text-xs`:`text-sm`,x=e?`space-y-2`:`space-y-4`,C=e?`space-y-1.5`:`space-y-3`,w=e?`space-y-1`:`space-y-1.5`;if(!t)return(0,L.jsxs)(`div`,{className:C,children:[(0,L.jsx)(`h3`,{className:`${b} font-medium text-text-secondary`,children:`AI Settings`}),(0,L.jsx)(`p`,{className:`${y} text-text-subtle`,children:d?`Error: ${d}`:`Loading...`})]});let T=_?a:[{value:`__default__`,label:`Auto (default)`},...a];return(0,L.jsxs)(`div`,{className:x,children:[(0,L.jsx)(`h3`,{className:`${b} font-medium text-text-secondary`,children:`AI Settings`}),h.length>1&&(0,L.jsx)(`div`,{className:`flex gap-0.5 border-b border-border/50 -mx-1 px-1`,children:h.map(e=>(0,L.jsxs)(`button`,{onClick:()=>i(e.id),className:`flex items-center gap-1 px-2 py-1 text-[11px] rounded-t transition-colors ${r===e.id?`text-primary border-b-2 border-primary font-medium`:`text-text-subtle hover:text-text-secondary`}`,children:[(0,L.jsx)(Y,{providerId:e.id}),(0,L.jsx)(`span`,{className:`capitalize`,children:e.name})]},e.id))}),(0,L.jsxs)(`div`,{className:C,children:[a.length>0&&(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-model`,className:e?y:void 0,children:`Model`}),(0,L.jsxs)(B,{value:_?g?.model??a[0]?.value:g?.model||`__default__`,onValueChange:e=>v(`model`,e===`__default__`?void 0:e),disabled:s,children:[(0,L.jsx)(H,{id:`ai-model`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,L.jsx)(V,{placeholder:s?`Loading models...`:`Select model`})}),(0,L.jsx)(U,{className:`max-h-[300px]`,children:T.map(e=>(0,L.jsx)(W,{value:e.value,children:e.label},e.value))})]})]}),_&&(0,L.jsxs)(L.Fragment,{children:[(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-base-url`,className:e?y:void 0,children:`Base URL`}),(0,L.jsx)(S,{id:`ai-base-url`,type:`url`,defaultValue:g?.base_url??``,placeholder:`https://api.anthropic.com (default)`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{v(`base_url`,e.target.value.trim()||void 0)}},`baseurl-${r}-${p}`)]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-api-key`,className:e?y:void 0,children:`API Key / Token`}),(0,L.jsx)(S,{id:`ai-api-key`,type:`password`,defaultValue:g?.api_key??``,placeholder:`sk-ant-... (optional, overrides accounts)`,className:e?`h-7 text-[11px] font-mono`:`font-mono`,onBlur:e=>{let t=e.target.value.trim();t.startsWith(`••••`)||v(`api_key`,t||void 0)}},`apikey-${r}-${p}`),(0,L.jsx)(`p`,{className:`${e?`text-[9px]`:`text-[11px]`} text-muted-foreground`,children:`Direct API key or OAuth token. Leave empty to use connected accounts.`})]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-effort`,className:e?y:void 0,children:`Effort`}),(0,L.jsxs)(B,{value:g?.effort??`high`,onValueChange:e=>v(`effort`,e),children:[(0,L.jsx)(H,{id:`ai-effort`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,L.jsx)(V,{})}),(0,L.jsx)(U,{children:X.map(e=>(0,L.jsx)(W,{value:e.value,children:e.label},e.value))})]})]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-max-turns`,className:e?y:void 0,children:`Max Turns (1-500)`}),(0,L.jsx)(S,{id:`ai-max-turns`,type:`number`,min:1,max:500,defaultValue:g?.max_turns??100,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseInt(e.target.value);isNaN(t)||v(`max_turns`,t)}},`turns-${r}-${p}`)]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-budget`,className:e?y:void 0,children:`Max Budget (USD)`}),(0,L.jsx)(S,{id:`ai-budget`,type:`number`,step:.1,min:.01,max:50,defaultValue:g?.max_budget_usd??``,placeholder:`No limit`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseFloat(e.target.value);v(`max_budget_usd`,isNaN(t)?void 0:t)}},`budget-${r}-${p}`)]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-thinking`,className:e?y:void 0,children:`Thinking Budget (tokens)`}),(0,L.jsx)(S,{id:`ai-thinking`,type:`number`,min:0,defaultValue:g?.thinking_budget_tokens??``,placeholder:`Disabled`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseInt(e.target.value);v(`thinking_budget_tokens`,isNaN(t)?void 0:t)}},`thinking-${r}-${p}`)]}),(0,L.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,L.jsxs)(`div`,{children:[(0,L.jsx)(R,{htmlFor:`ai-agent-teams`,className:e?y:void 0,children:`Agent Teams`}),(0,L.jsx)(`p`,{className:`${e?`text-[9px]`:`text-[11px]`} text-muted-foreground`,children:`Experimental. Enables multi-agent collaboration with shared tasks and messaging. Uses ~7x more tokens.`})]}),(0,L.jsx)(z,{id:`ai-agent-teams`,checked:g?.agent_teams??!1,onCheckedChange:e=>v(`agent_teams`,e)})]}),g?.agent_teams&&(0,L.jsx)(ee,{compact:e})]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-permission-mode`,className:e?y:void 0,children:`Default Permission Mode`}),(0,L.jsxs)(B,{value:g?.permission_mode??`bypassPermissions`,onValueChange:e=>v(`permission_mode`,e),children:[(0,L.jsx)(H,{id:`ai-permission-mode`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,L.jsx)(V,{})}),(0,L.jsx)(U,{children:Z.map(e=>(0,L.jsx)(W,{value:e.value,children:e.label},e.value))})]})]}),(0,L.jsxs)(`div`,{className:w,children:[(0,L.jsx)(R,{htmlFor:`ai-system-prompt`,className:e?y:void 0,children:`Additional Instructions`}),(0,L.jsx)(`textarea`,{id:`ai-system-prompt`,rows:e?3:4,defaultValue:g?.system_prompt??``,placeholder:`Enter additional instructions for ${r}...`,className:`w-full rounded-md border border-input bg-background px-3 py-2 ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${e?`text-[11px]`:`text-sm`}`,onBlur:e=>{v(`system_prompt`,e.target.value.trim()||void 0)}},`sysprompt-${r}-${p}`)]})]}),l&&(0,L.jsx)(`p`,{className:`text-xs text-text-subtle`,children:`Saving...`}),d&&(0,L.jsx)(`p`,{className:`text-xs text-red-500`,children:d})]})}function ee({compact:e}){let[t,n]=(0,I.useState)([]),[r,i]=(0,I.useState)(!1),[a,o]=(0,I.useState)(null),s=(0,I.useCallback)(async()=>{i(!0);try{n(await E.get(`/api/teams`)??[])}catch{}i(!1)},[]);(0,I.useEffect)(()=>{s()},[s]);let c=async e=>{try{await E.del(`/api/teams/${encodeURIComponent(e)}`),n(t=>t.filter(t=>t.name!==e)),o(null)}catch{}};return t.length===0&&!r?null:(0,L.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,L.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,L.jsxs)(R,{className:e?`text-[11px]`:void 0,children:[`Teams (`,t.length,`)`]}),(0,L.jsx)(`button`,{onClick:s,className:`text-text-subtle hover:text-foreground p-1`,"aria-label":`Refresh teams`,children:(0,L.jsx)(C,{className:`size-3 ${r?`animate-spin`:``}`})})]}),t.map(e=>(0,L.jsxs)(`div`,{className:`flex items-center justify-between p-2 rounded bg-surface-elevated text-xs`,children:[(0,L.jsxs)(`div`,{className:`min-w-0`,children:[(0,L.jsx)(`div`,{className:`font-medium truncate`,children:e.name}),e.description&&(0,L.jsx)(`div`,{className:`text-text-subtle truncate`,children:e.description}),(0,L.jsxs)(`div`,{className:`text-text-subtle`,children:[e.members?.length??e.memberCount??0,` members`,e.createdAt?` · ${new Date(e.createdAt).toLocaleDateString()}`:``]})]}),a===e.name?(0,L.jsxs)(`div`,{className:`flex gap-1 shrink-0 ml-2`,children:[(0,L.jsx)(`button`,{onClick:()=>c(e.name),className:`px-2 py-1 bg-red-600 text-white rounded text-[10px]`,children:`Delete`}),(0,L.jsx)(`button`,{onClick:()=>o(null),className:`px-2 py-1 bg-zinc-600 text-white rounded text-[10px]`,children:`Cancel`})]}):(0,L.jsx)(`button`,{onClick:()=>o(e.name),className:`shrink-0 text-text-subtle hover:text-red-500 p-1 ml-2`,"aria-label":`Delete team ${e.name}`,children:(0,L.jsx)(w,{className:`size-3.5`})})]},e.name))]})}export{U as a,V as c,F as d,P as f,A as g,j as h,B as i,z as l,M as m,Y as n,W as o,N as p,J as r,H as s,$ as t,R as u};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{W as e}from"./vendor-mermaid-sQS4C_iL.js";export{e as createArchitectureServices};
|