kcg-code 0.1.0 → 0.2.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/README.md +2 -0
- package/package.json +2 -1
- package/src/cli/__tests__/daemon.test.ts +84 -0
- package/src/cli/__tests__/dashboard.test.ts +170 -8
- package/src/cli/__tests__/keep-awake.test.ts +44 -0
- package/src/cli/__tests__/port.test.ts +86 -0
- package/src/cli/args.ts +69 -6
- package/src/cli/attach.ts +154 -0
- package/src/cli/commands.ts +174 -21
- package/src/cli/daemon.ts +275 -0
- package/src/cli/dashboard.tsx +876 -159
- package/src/cli/index.ts +10 -0
- package/src/cli/keep-awake.ts +148 -0
- package/src/cli/port.ts +92 -0
- package/src/cli/theme.ts +6 -6
- package/src/components/layout/AppShell.tsx +8 -3
- package/src/components/projects/HomeChat.tsx +85 -48
- package/src/components/projects/HomeRecentSessions.tsx +90 -0
- package/src/components/projects/ProjectInstructions.tsx +27 -38
- package/src/components/projects/ProjectSidePanel.tsx +382 -316
- package/src/components/sessions/AgentLogo.tsx +42 -0
- package/src/components/sessions/AgentPicker.tsx +13 -4
- package/src/components/sessions/ConfirmSessionDeleteDialog.tsx +27 -7
- package/src/components/sessions/ContextPanel.tsx +159 -59
- package/src/components/sessions/ModelPicker.tsx +65 -27
- package/src/components/sessions/ModelSearchList.tsx +137 -57
- package/src/components/sessions/NewSessionDialog.tsx +100 -117
- package/src/components/sessions/SessionComposer.tsx +2 -1
- package/src/components/sessions/SessionHeader.tsx +1 -0
- package/src/components/sessions/SessionList.tsx +173 -66
- package/src/components/sessions/SessionRow.tsx +176 -76
- package/src/components/sessions/SessionView.tsx +17 -13
- package/src/components/sessions/__tests__/ContextPanel.test.tsx +36 -0
- package/src/components/settings/LhrRemoteAccess.tsx +20 -25
- package/src/components/settings/RemoteAccessGroup.tsx +242 -267
- package/src/components/settings/prefs.tsx +69 -1
- package/src/components/skills/InstallNotifications.tsx +4 -3
- package/src/components/skills/InstallSkillDialog.tsx +256 -99
- package/src/components/skills/SkillCards.tsx +52 -8
- package/src/components/ui/adw-dialog.tsx +114 -0
- package/src/components/ui/adw-toggle-group.tsx +75 -0
- package/src/components/ui/search-entry.tsx +70 -0
- package/src/config.ts +32 -3
- package/src/hooks/useAgentPicker.ts +6 -2
- package/src/hooks/useSessionList.ts +95 -2
- package/src/lib/__tests__/project-extensions.test.ts +32 -1
- package/src/lib/__tests__/project-overview.test.ts +15 -0
- package/src/lib/__tests__/routes.test.ts +2 -2
- package/src/lib/__tests__/session-selection.test.ts +41 -0
- package/src/lib/__tests__/skill-installs.test.ts +1 -0
- package/src/lib/__tests__/skills-catalog.test.ts +36 -8
- package/src/lib/agent-types.ts +49 -0
- package/src/lib/project-extensions.ts +66 -4
- package/src/lib/project-overview.ts +14 -8
- package/src/lib/routes.ts +13 -4
- package/src/lib/session-selection.ts +40 -0
- package/src/lib/skill-installs.ts +3 -1
- package/src/lib/skills-catalog.ts +56 -17
- package/src/pages/projects/ProjectDetailPage.tsx +13 -12
- package/src/pages/settings/SettingsPage.tsx +212 -145
- package/src/pages/settings/__tests__/SettingsPage.test.ts +11 -4
- package/src/pages/skills/SkillsPage.tsx +89 -45
- package/src/server/__tests__/app.test.ts +15 -2
- package/src/server/app.ts +13 -1
- package/src/server/routes/projects.routes.ts +14 -4
- package/src/server/routes/sessions.routes.ts +1 -1
- package/src/server/routes/skills.routes.ts +24 -5
- package/src/server/services/__tests__/claude-code.test.ts +609 -0
- package/src/server/services/__tests__/claude-mcp.test.ts +90 -0
- package/src/server/services/__tests__/claude-skills.test.ts +63 -0
- package/src/server/services/__tests__/session-manager.test.ts +40 -0
- package/src/server/services/__tests__/skill-install-jobs.test.ts +22 -3
- package/src/server/services/__tests__/skills-registry.test.ts +6 -0
- package/src/server/services/claude-code-client.ts +1093 -0
- package/src/server/services/claude-code-translate.ts +370 -0
- package/src/server/services/claude-mcp.ts +122 -0
- package/src/server/services/claude-skills.ts +102 -0
- package/src/server/services/session-manager.ts +117 -52
- package/src/server/services/skill-install-jobs.ts +21 -8
- package/src/server/services/skills-registry.ts +18 -6
- package/src/types/message.ts +1 -1
- package/src/components/projects/PanelSection.tsx +0 -102
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* `SessionComposer` di folder yang sama.
|
|
19
19
|
* - Drag & drop file: `FileDropOverlay` (portal, menutupi seluruh viewport)
|
|
20
20
|
* meneruskan file ke composer (`SessionComposerHandle.addFiles`).
|
|
21
|
-
* - Kartu konteks kanan (`ContextPanel`): Environment /
|
|
22
|
-
* Reference
|
|
23
|
-
*
|
|
21
|
+
* - Kartu konteks kanan (`ContextPanel`): Environment / Task list / MCP /
|
|
22
|
+
* Reference. Desktop: kolom sendiri di kanan (berdampingan) — kolom chat
|
|
23
|
+
* menyempit & percakapan tetap terpusat di sisa ruang, tidak tertutup
|
|
24
|
+
* kartu. Di bawah `lg` (HP & tablet) tampil sebagai bottom sheet.
|
|
24
25
|
*/
|
|
25
26
|
import { useMemo, useRef, useState } from "react";
|
|
26
27
|
import { ConfirmSessionDeleteDialog } from "@/components/sessions/ConfirmSessionDeleteDialog";
|
|
@@ -37,7 +38,7 @@ import {
|
|
|
37
38
|
SessionComposerSkeleton,
|
|
38
39
|
SessionTimelineSkeleton,
|
|
39
40
|
} from "@/components/sessions/SessionTimelineSkeleton";
|
|
40
|
-
import {
|
|
41
|
+
import { useMediaQuery } from "@/hooks/useMediaQuery";
|
|
41
42
|
import { useSessionChat } from "@/hooks/useSessionChat";
|
|
42
43
|
import { useSessionContext } from "@/hooks/useSessionContext";
|
|
43
44
|
import { CONVERSATION_MAX_W } from "@/lib/layout";
|
|
@@ -46,6 +47,9 @@ import { cn } from "@/lib/utils";
|
|
|
46
47
|
import type { Session } from "@/types";
|
|
47
48
|
import { PromptPanel } from "./PromptPanel";
|
|
48
49
|
|
|
50
|
+
/** Lebar minimum untuk kartu konteks sebagai kolom samping (`lg`). */
|
|
51
|
+
const SIDE_PANEL_QUERY = "(min-width: 1024px)";
|
|
52
|
+
|
|
49
53
|
export interface SessionViewProps {
|
|
50
54
|
session: Session;
|
|
51
55
|
onBack: () => void;
|
|
@@ -70,8 +74,9 @@ export function SessionView({ session, onBack, onDeleted }: SessionViewProps) {
|
|
|
70
74
|
const composerRef = useRef<SessionComposerHandle>(null);
|
|
71
75
|
/** Panel konteks kanan; default tertutup agar area chat tetap lega. */
|
|
72
76
|
const [contextOpen, setContextOpen] = useState(false);
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
// Kolom konteks berdampingan butuh ruang: di bawah `lg` kolom chat jadi
|
|
78
|
+
// terlalu sempit, jadi kartu tampil sebagai bottom sheet (seperti HP).
|
|
79
|
+
const sidePanel = useMediaQuery(SIDE_PANEL_QUERY);
|
|
75
80
|
const sessionCtx = useSessionContext(session.id, contextOpen, chat.generating);
|
|
76
81
|
/**
|
|
77
82
|
* Reference diturunkan dari parts pesan yang sudah ada di timeline
|
|
@@ -187,12 +192,11 @@ export function SessionView({ session, onBack, onDeleted }: SessionViewProps) {
|
|
|
187
192
|
)}
|
|
188
193
|
</div>
|
|
189
194
|
|
|
190
|
-
{/* Tertutup: hanya tombol hamburger. Terbuka: tombol hilang
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
// Mobile: bottom sheet. Tombol hamburger tetap terlihat — sheet
|
|
195
|
+
{/* Tertutup: hanya tombol hamburger. Terbuka (desktop): tombol hilang
|
|
196
|
+
& kartu jadi kolom kanan selebar 17rem yang IKUT tata letak — kolom
|
|
197
|
+
chat (flex-1) menyempit, jadi pesan & composer tidak tertutup. */}
|
|
198
|
+
{!sidePanel ? (
|
|
199
|
+
// HP & tablet: bottom sheet. Tombol hamburger tetap terlihat — sheet
|
|
196
200
|
// menutupi layar & punya penutupnya sendiri (tap luar / geser / Esc).
|
|
197
201
|
<>
|
|
198
202
|
<ContextPanelTrigger onOpen={() => setContextOpen(true)} />
|
|
@@ -205,7 +209,7 @@ export function SessionView({ session, onBack, onDeleted }: SessionViewProps) {
|
|
|
205
209
|
/>
|
|
206
210
|
</>
|
|
207
211
|
) : contextOpen ? (
|
|
208
|
-
<div className="
|
|
212
|
+
<div className="flex w-[17rem] shrink-0 flex-col pt-2 pe-3 pb-3 sm:pe-4">
|
|
209
213
|
<ContextPanel
|
|
210
214
|
context={sessionCtx.context}
|
|
211
215
|
references={references}
|
|
@@ -97,6 +97,42 @@ test("MCP: server bermasalah ditampilkan paling atas", () => {
|
|
|
97
97
|
expect(out.indexOf("zzz-broken")).toBeLessThan(out.indexOf("aaa-ok"));
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
test("MCP: banyak yang perlu login dilipat jadi satu baris, yang tersambung tetap tampil", () => {
|
|
101
|
+
const out = html({
|
|
102
|
+
...base,
|
|
103
|
+
mcp: [
|
|
104
|
+
{ name: "firecrawl", status: "connected", error: null },
|
|
105
|
+
{ name: "claude.ai Asana", status: "needs_auth", error: null },
|
|
106
|
+
{ name: "claude.ai Box", status: "needs_auth", error: null },
|
|
107
|
+
{ name: "claude.ai Canva", status: "needs_client_registration", error: null },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
expect(out).toContain("firecrawl");
|
|
111
|
+
expect(out).toContain("3 need sign-in");
|
|
112
|
+
// Nama di dalam kelompok baru tampil saat dibuka.
|
|
113
|
+
expect(out).not.toContain("claude.ai Asana");
|
|
114
|
+
expect(out).toContain('aria-expanded="false"');
|
|
115
|
+
// Belum login itu normal — ringkasan tidak diberi warna peringatan.
|
|
116
|
+
expect(out).not.toContain("text-amber-700");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("MCP: satu server perlu login tetap tampil sebagai baris biasa", () => {
|
|
120
|
+
const out = html({
|
|
121
|
+
...base,
|
|
122
|
+
mcp: [
|
|
123
|
+
{ name: "firecrawl", status: "connected", error: null },
|
|
124
|
+
{ name: "github", status: "needs_auth", error: null },
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
expect(out).toContain("github");
|
|
128
|
+
expect(out).not.toContain("need sign-in");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("MCP: ada yang gagal -> ringkasan diberi warna peringatan", () => {
|
|
132
|
+
const out = html({ ...base, mcp: [{ name: "x", status: "failed", error: null }] });
|
|
133
|
+
expect(out).toContain("text-amber-700");
|
|
134
|
+
});
|
|
135
|
+
|
|
100
136
|
test("MCP kosong: keterangan menyesuaikan status Session", () => {
|
|
101
137
|
expect(html(base)).toContain("No MCP servers configured");
|
|
102
138
|
expect(html({ ...base, live: false })).toContain("Start the session to see MCP servers");
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Panel Remote access via localhost.run — tanpa akun, lewat `ssh` bawaan OS.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Susunan sama dengan penyedia bawaan (satu hal per grup):
|
|
5
5
|
* - AdwSwitchRow "Remote access" (status satu frasa di subjudul),
|
|
6
|
-
* -
|
|
6
|
+
* - grup "Your link" saat online (komponen bersama penyedia bawaan),
|
|
7
|
+
* - grup "Connection method" (dari pembungkus),
|
|
7
8
|
* - baris expander "Connection log".
|
|
8
|
-
* Catatan alamat yang berubah + tautan ke KCG Code link jadi footer grup.
|
|
9
9
|
*/
|
|
10
10
|
import { ChevronDownIcon, GlobeIcon, LockIcon, TerminalSquareIcon } from "lucide-react";
|
|
11
11
|
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
|
@@ -33,8 +33,8 @@ export interface LhrRemoteAccessProps {
|
|
|
33
33
|
LogPanel: (p: { endpoint?: string }) => ReactNode;
|
|
34
34
|
/** Buka konfirmasi mematikan (halaman bisa terputus bila dibuka lewat link ini). */
|
|
35
35
|
onRequestStop: (stop: () => Promise<void>, viaThisLink: boolean) => void;
|
|
36
|
-
/**
|
|
37
|
-
|
|
36
|
+
/** Grup "Connection method" — dirender sebelum log. */
|
|
37
|
+
methodPicker: ReactNode;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function LhrRemoteAccess({
|
|
@@ -42,7 +42,7 @@ export function LhrRemoteAccess({
|
|
|
42
42
|
LiveAddress,
|
|
43
43
|
LogPanel,
|
|
44
44
|
onRequestStop,
|
|
45
|
-
|
|
45
|
+
methodPicker,
|
|
46
46
|
}: LhrRemoteAccessProps) {
|
|
47
47
|
const { navigate } = useRouter();
|
|
48
48
|
const [s, setS] = useState<LhrStatus | null>(null);
|
|
@@ -109,10 +109,10 @@ export function LhrRemoteAccess({
|
|
|
109
109
|
|
|
110
110
|
// Subjudul: satu kalimat pendek yang menjelaskan keadaan + apa artinya.
|
|
111
111
|
let tone: RowTone = "muted";
|
|
112
|
-
let subtitle: ReactNode = "
|
|
112
|
+
let subtitle: ReactNode = "Off";
|
|
113
113
|
if (!protectedApp) {
|
|
114
114
|
tone = "warning";
|
|
115
|
-
subtitle = "
|
|
115
|
+
subtitle = "Needs a PIN or password first";
|
|
116
116
|
} else if (s.phase === "error") {
|
|
117
117
|
tone = "warning";
|
|
118
118
|
subtitle = (
|
|
@@ -124,7 +124,7 @@ export function LhrRemoteAccess({
|
|
|
124
124
|
);
|
|
125
125
|
} else if (online) {
|
|
126
126
|
tone = "success";
|
|
127
|
-
subtitle = "On ·
|
|
127
|
+
subtitle = "On · PIN still required";
|
|
128
128
|
} else if (connecting) {
|
|
129
129
|
tone = "busy";
|
|
130
130
|
subtitle =
|
|
@@ -139,7 +139,7 @@ export function LhrRemoteAccess({
|
|
|
139
139
|
};
|
|
140
140
|
|
|
141
141
|
return (
|
|
142
|
-
|
|
142
|
+
<>
|
|
143
143
|
<PrefsGroup id="remote">
|
|
144
144
|
{/* AdwSwitchRow: judul + status singkat, switch di kanan. */}
|
|
145
145
|
<li>
|
|
@@ -181,9 +181,17 @@ export function LhrRemoteAccess({
|
|
|
181
181
|
onActivate={() => navigate(settingsPath("security"))}
|
|
182
182
|
/>
|
|
183
183
|
)}
|
|
184
|
+
</PrefsGroup>
|
|
185
|
+
|
|
186
|
+
{online && s.url && (
|
|
187
|
+
<PrefsGroup title="Your link">
|
|
188
|
+
<LiveAddress url={s.url} />
|
|
189
|
+
</PrefsGroup>
|
|
190
|
+
)}
|
|
184
191
|
|
|
185
|
-
|
|
192
|
+
{methodPicker}
|
|
186
193
|
|
|
194
|
+
<PrefsGroup>
|
|
187
195
|
{/* Log teknis dilipat di baris expander (AdwExpanderRow). */}
|
|
188
196
|
<Collapsible asChild open={showLog} onOpenChange={setShowLog}>
|
|
189
197
|
<li>
|
|
@@ -216,20 +224,7 @@ export function LhrRemoteAccess({
|
|
|
216
224
|
</li>
|
|
217
225
|
</Collapsible>
|
|
218
226
|
</PrefsGroup>
|
|
219
|
-
|
|
220
|
-
{/* Footer grup: catatan singkat + aksi inline. */}
|
|
221
|
-
<p className="px-1 text-[12.5px] leading-snug text-muted-foreground">
|
|
222
|
-
Free addresses change every few hours and after reconnecting.{" "}
|
|
223
|
-
<button
|
|
224
|
-
type="button"
|
|
225
|
-
onClick={onUseRecommended}
|
|
226
|
-
className="rounded-sm font-medium text-foreground underline-offset-2 outline-none hover:underline focus-visible:underline focus-visible:ring-[3px] focus-visible:ring-ring/40"
|
|
227
|
-
>
|
|
228
|
-
Use KCG Code link
|
|
229
|
-
</button>{" "}
|
|
230
|
-
for one that stays the same.
|
|
231
|
-
</p>
|
|
232
|
-
</section>
|
|
227
|
+
</>
|
|
233
228
|
);
|
|
234
229
|
}
|
|
235
230
|
|