first commit

This commit is contained in:
2026-07-17 21:48:37 +05:30
commit f85599dac5
124 changed files with 10775 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
# Onboarding — Messaging/Inbox/Support Web SDK
Welcome. This is a **static demo** of a Slack-grade messaging + inbox + support product,
shipped as a **React SDK** (`@lynkd/messaging-inbox-sdk`) consumed by a **Next.js demo app**
(`apps/web`), styled to match the LynkedUp Pro dashboard. Read this first; deep dives are in
[`../docs`](../docs).
## Run it
```bash
pnpm install # Node >= 20, pnpm >= 8
pnpm dev # http://localhost:4300
```
No DB, no env setup needed — mock data ships in code and every feature works.
## Mental model (read once, saves hours)
1. **UI is headless-first.** Product logic lives in SDK hooks (`useMessages`, `useInbox`,
`useTickets`…). Components in `apps/web/components` are just presentation.
2. **The UI never calls a backend directly.** It calls `BffClient` → the **BFF**
(`apps/web/app/api/bff/**`) → the **mock store** (`apps/web/lib/mock/store.ts`).
This is the one seam you replace to go real. This is the **BFF pattern**.
3. **Config is resolved once on the server** (`apps/web/lib/config.ts``getServerConfig()`)
from `NEXT_PUBLIC_*` env, then handed to the client provider as plain JSON. That's how
feature flags + theme reach the browser without dynamic `process.env` access.
4. **Theme = CSS variables.** `theme.ts` writes `--c-*`, `--r-*`, `--font-*` onto `<html>`.
Tailwind colors map to those vars (`tailwind.config.ts`). Light/dark + brand swap are
just different variable values.
## Where things are
| I want to… | Go to |
|---|---|
| Add / change a domain type | `packages/messaging-inbox-sdk/src/types.ts` |
| Add a feature flag | `packages/messaging-inbox-sdk/src/config.ts` (`FeatureFlags` + `defaultFlags`) |
| Change default theme | `packages/messaging-inbox-sdk/src/config.ts` (`darkTokens`/`lightTokens`/`defaultTheme`) |
| Add a data hook | `packages/messaging-inbox-sdk/src/hooks.ts` |
| Add a BFF endpoint | `apps/web/app/api/bff/<path>/route.ts` + a method on `store` + a method on `BffClient` |
| Change seed data | `apps/web/lib/mock/data.ts` |
| Edit the sidebar/header | `apps/web/components/nav/*` |
| Edit a message/composer/thread | `apps/web/components/message/*` |
| Edit the Support console | `apps/web/components/support/SupportView.tsx` |
| Add a page/route | `apps/web/app/(app)/<route>/page.tsx` |
## Add a feature end-to-end (worked example)
Say you want a `/pins` page listing pinned messages:
1. **Flag** (optional): add `pinsPage: boolean` to `FeatureFlags` + `defaultFlags`.
2. **BFF**: add `GET /api/bff/pins` returning `store.messages.filter(m => m.isPinned)`.
3. **Client**: add `listPinned()` to `BffClient`.
4. **Hook**: add `usePinned()` in `hooks.ts`.
5. **UI**: `apps/web/app/(app)/pins/page.tsx` renders a list, gated by `useFeature("pinsPage")`.
## Conventions
- **Client vs server components:** anything using SDK hooks/`useUi` needs `"use client"`.
Pages are thin server components that render a client view component.
- **Styling:** Tailwind utility classes + the semantic tokens (`bg-panel`, `text-muted`,
`border-border`, `rounded-card`, `bg-accent`). Don't hard-code hex — use tokens so
light/dark + theming keep working.
- **Icons:** `lucide-react`.
- **Time:** render relative/clock time with `lib/format.ts` (client-only, avoids SSR drift).
## Gotchas
- The Next.js version is **pinned for the demo** and has a known CVE — bump before real use.
- The mock store is **in-memory**: it resets on server restart (that's expected).
- Message timestamps are seeded relative to server start, so "just now / 4m" stays sensible.
## Next
- [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) — the full picture
- [`../docs/IIOS_INTEGRATION.md`](../docs/IIOS_INTEGRATION.md) — make it real
- [`./memory/`](./memory) — durable project decisions (also loaded by Claude Code)
+33
View File
@@ -0,0 +1,33 @@
# `.claude/` — project-local knowledge
This folder is **committed to the repo** on purpose. It holds knowledge that helps both human
developers and Claude Code sessions work in this codebase — kept **local to the project**, not
in anyone's global profile, so it travels with the code and everyone benefits.
## Contents
- **[`ONBOARDING.md`](ONBOARDING.md)** — start here. How to run it, the mental model, where
things live, and how to add a feature end-to-end.
- **[`memory/`](memory/)** — durable, one-fact-per-file notes about *why* things are the way
they are (decisions, the design system sampled from LynkedUp Pro, conventions, the IIOS
relationship). `memory/MEMORY.md` is the index.
- **`settings.json`** — project Claude Code settings (a permission allowlist for common dev
commands so you get fewer prompts).
## Conventions for memory notes
Each note in `memory/` is a single Markdown file with frontmatter:
```markdown
---
name: kebab-case-slug
description: one line used to judge relevance
metadata:
type: project | reference | feedback | user
---
The fact. Link related notes with [[their-slug]].
```
When you learn something non-obvious about this project, add or update a note and add a line to
`memory/MEMORY.md`. Prefer updating an existing note over creating a duplicate.
+12
View File
@@ -0,0 +1,12 @@
# Project memory index
Durable, project-local notes for this repo. Loaded by Claude Code sessions and useful for
any developer. One line per note; full content lives in the linked file.
- [project-overview.md](project-overview.md) — what this repo is and its non-obvious goals
- [architecture-decisions.md](architecture-decisions.md) — SDK+BFF+workspace choices and why
- [design-system.md](design-system.md) — LynkedUp Pro tokens sampled from the live site
- [conventions.md](conventions.md) — coding conventions and gotchas specific to this repo
- [iios-relationship.md](iios-relationship.md) — how this maps to the IIOS service at D:\iios
- [qa-and-fixes.md](qa-and-fixes.md) — the deep-QA pass and what was fixed (reactions, WYSIWYG composer, dead controls, responsive, backend seam)
- [features-and-modes.md](features-and-modes.md) — full feature set, mock/iios modes, how to run IIOS live (no Docker), seed script
+39
View File
@@ -0,0 +1,39 @@
---
name: architecture-decisions
description: Key architecture choices for the SDK + BFF + workspace and the reasons
metadata:
type: project
---
**pnpm workspace, two packages.** `packages/messaging-inbox-sdk` (the shareable SDK) and
`apps/web` (the demo). The app depends on the SDK via `workspace:*` and Next `transpilePackages`
transpiles the SDK's TS source directly (SDK `main`/`types` point at `src/index.ts`, no build
step). This mirrors the IIOS layout (packages + apps) and keeps the SDK genuinely reusable.
**BFF pattern (explicit client requirement).** The UI/SDK only ever calls `BffClient`, which
hits `/api/bff/**` (Next route handlers). Every route calls `getBackend()` (a formal `Backend`
interface in `apps/web/lib/backend/`), never the store directly. `BFF_BACKEND` env picks the impl:
`mock` (default, wraps `lib/mock/store.ts`) or `iios` (`lib/backend/iios.ts` → real IIOS via
`lib/iios/client.ts` + `map.ts`). Messages/inbox/tickets are wired to IIOS; bootstrap/channels/
reactions/search/notifications delegate to mock until IIOS exposes those endpoints. Flipping to
live IIOS is one env var (needs the IIOS service + Docker/Postgres running — see
`docs/IIOS_INTEGRATION.md`). This is the single swap seam; the SDK and components never change.
**Config resolved on the server.** `getServerConfig()` runs in the root layout (server
component), reads `NEXT_PUBLIC_*` from `process.env`, returns a plain `SdkConfig`, and passes
it to the client `<MessagingInboxProvider config=…>`. Avoids the Next.js gotcha that dynamic
`process.env[key]` doesn't work in the browser bundle. Theme vars are also inlined into
`<head>` on first paint to prevent a flash.
**Theme = CSS variables + Tailwind mapping.** All Tailwind colors resolve to `--c-*` vars set
on `<html>` at runtime by the provider. Light/dark and brand re-skin are just different
variable values; nothing is hard-coded. `darkMode: 'class'`.
**Feature flags are data, not conditionals scattered around.** One `FeatureFlags` interface;
`NEXT_PUBLIC_FEATURE_<UPPER_SNAKE>` overrides each. Components call `useFeature("x")`.
**Why not build the NestJS/DB backend?** The ask was a static demo with everything working.
A NestJS BFF + Postgres adds setup cost with no demo benefit; it's documented as the
production path instead (`docs/IIOS_INTEGRATION.md`, `docs/BFF.md`).
Related: [[design-system]], [[conventions]], [[iios-relationship]].
+23
View File
@@ -0,0 +1,23 @@
---
name: conventions
description: Repo-specific coding conventions and gotchas
metadata:
type: project
---
- **Client boundary:** any component using SDK hooks or `useUi` must start with `"use client"`.
Route `page.tsx` files stay thin server components that render a client view.
- **Never hard-code colors/radii.** Use semantic Tailwind tokens (`bg-panel`, `text-muted`,
`border-border`, `bg-accent`, `rounded-card`, `rounded-control`) so light/dark + live theming
keep working. Tokens are defined in `apps/web/tailwind.config.ts` → CSS vars.
- **Time formatting** is client-only via `apps/web/lib/format.ts` to avoid SSR hydration drift;
data (messages/tickets) is fetched client-side through the BFF, not server-rendered.
- **BFF params are async in Next 15:** handlers use `{ params }: { params: Promise<…> }` and
`await params`.
- **Next config** sets `typescript.ignoreBuildErrors` + `eslint.ignoreDuringBuilds` so the demo
runs even with strict-mode nits; run `pnpm typecheck` for a clean check.
- **No `packageManager` pin** in root `package.json` on purpose — a pin triggered corepack to
fetch a non-installed pnpm version and fail offline. Use the local pnpm.
- **Mock store is in-memory** and resets on restart; persistence isn't a goal for the demo.
- Adding a data feature touches 4 files in order: `store.ts``bff route``client.ts`
`hooks.ts` → then the component. See [[architecture-decisions]].
+28
View File
@@ -0,0 +1,28 @@
---
name: design-system
description: LynkedUp Pro design tokens sampled from the live dashboard (defaults for this app)
metadata:
type: reference
---
Tokens sampled live from `https://lynkeduppro-crmnew.vercel.app/dashboard` (computed styles),
used as the **default theme**. All are overridable via `NEXT_PUBLIC_THEME_*` or the in-app
Appearance panel. Source of truth in code: `packages/messaging-inbox-sdk/src/config.ts`.
**Accent:** `#FDA913` (golden amber). Solid usage ~0.92 opacity; soft tints at 0.12
(`rgba(253,169,19,0.12)`).
**Dark surfaces:** app bg `#060608`, sidebar `#08080b`, panel/card `#0e0e13`, elevated
`#15151c`. Borders `rgba(255,255,255,0.08)`. Text: ink `#ededf1`, muted `#8c8c94`, dim `#63636b`.
**Light surfaces:** app bg `#f6f7f9`, surface/panel `#ffffff`, borders `rgba(9,10,14,0.10)`,
ink `#14151a`, muted `#5c6069`. Accent nudged to `#e8920a` for contrast on white.
**Radii:** cards **20px**, controls/buttons **10px**, pills 999px.
**Font:** Inter (system-ui fallback). **Layout:** dark sectioned sidebar + user card footer;
top header with search, theme toggle, notifications, user menu (matches the reference exactly).
**Brand gradient:** `linear-gradient(135deg,#FDA913,#ff7a3d,#ff4d6d)`.
If the reference site restyles, re-sample with the browser devtools/`getComputedStyle` and
update the token blocks in `config.ts` + defaults in `app/globals.css`.
+76
View File
@@ -0,0 +1,76 @@
---
name: features-and-modes
description: The full Slack-grade feature set, the two backend modes, and how to run IIOS live
metadata:
type: project
---
**Two backend modes** (env `BFF_BACKEND` in `apps/web/.env.local`, read server-side):
- `mock` (default) — rich in-memory JSON workspace; every feature works & persists in-process.
Best for demos. Data in `apps/web/lib/mock/data.ts` + `store.ts`.
- `iios` — proxies the live IIOS NestJS service (real Postgres). Seeded via
`node scripts/seed-iios.mjs` which writes `apps/web/lib/iios/seed-manifest.json` (real thread
ids → channel directory). Bootstrap/channels come from the manifest; messages/inbox/tickets
from IIOS live. Verified: 5 channels + 6 users + messages render through the app BFF.
**Running IIOS live (no Docker):** native PostgreSQL 18, role `iios`/`iios`, db `iios` on 5432
(a separate 5434 cluster crashes under the sandbox's process limits). `packages/iios-service/.env`
`DATABASE_URL=postgresql://iios:iios@localhost:5432/iios?schema=public`, `IIOS_DEV_TOKENS=1`.
Build: allow prisma/esbuild in `D:\iios\pnpm-workspace.yaml`, `pnpm install && pnpm -r build`,
prisma `generate` + `migrate deploy`, `node packages/iios-service/dist/main.js`. Redis NOT needed
(in-memory socket adapter without `REDIS_URL`). Full steps: `docs/IIOS_INTEGRATION.md`.
**Slack-grade features (all working, verified via Playwright):** channels/DMs/threads;
WYSIWYG composer (bold/italic/code/lists); **@mention + #channel autocomplete** (typing pops a
picker; rendered as blue chips by `lib/rich-text.tsx`); **full searchable emoji picker** (168,
categorized, `lib/emoji.ts`) in composer + reactions + status; **`:shortcode:` → emoji** (`:tick:`
→ ✅); reactions (hover picker fixed); pins; **save** (+ Saved page); **message edit + delete**;
**attachment upload** (file → data-URL preview → send → image preview + Open/Download); scheduled
send; huddles; **status** (emoji picker + clear-after incl. custom datetime); **avatar change**
(color swatches + image upload); **copy message link + jump-to-message** (scroll); **thread "also
send to channel"**; command palette (⌘K, arrow-nav); notifications; sidebar collapse; inbox;
Support Center; light/dark + live theming.
**AI + translation (Gemini, server-side only):** `apps/web/lib/gemini.ts` calls Gemini
(`?key=` auth, model `gemini-2.0-flash`) and returns a discriminated result so 429/errors degrade
gracefully. Two BFF routes: `POST /api/bff/ai` (assistant) and `POST /api/bff/translate`. Client
methods `client.ai(prompt, context?)` / `client.translate(text, lang)` use `reqSoft` (does NOT
throw on non-2xx — returns the `{ok:false,error}` body so the UI shows a clean message). UI:
`AiPanel` right-drawer (open via `openAi({context})` from a message's ✨ toolbar button; quick
actions Summarize / What-should-I-reply / Improve / Explain + free prompt); per-message Translate
(`LanguageMenu` searchable dropdown → inline translated card w/ "Show original"); whole-chat
Translate (header ✈ button → banner + `autoTranslateTo` prop on every MessageItem); composer
translate-before-send (✈ toolbar btn → pick language → preview popover → "Use translation" replaces
draft). Flags `aiAssist` + `translation`. **KEY is server-side only** (`GEMINI_API_KEY`, no
`NEXT_PUBLIC_`; verified not in client bundle). The provided free-tier key is **quota-exhausted
(429)** — wiring is proven end-to-end; real output needs a fresh/paid key.
**More Slack features added:** presence toggle Active/Away/DND (ProfileDrawer → Availability →
`client.updateMe({presence})`); typing indicator moved to a fixed bar ABOVE the composer (incl.
DMs); channel create — **public adds the whole team automatically, private shows a multi-user
selector**; invite modal turns emails into **chips** on `,`/Enter; thread composer is the FULL
editor, thread replies have edit/delete; editor lists: Enter=new bullet (doesn't send), Tab/Shift-
Tab indent/outdent; cross-view live sync via `notifyChannelChanged` mutation bus (delete + also-
send reflect without refresh); `threadRootId` "Replied to a thread → open" chip; copy-message
toolbar button.
**Jump-to-message flash (working in a production build):** `pnpm --filter @lynkd/web build` +
`start`; StrictMode double-mount is gone so the WAAPI amber `animate()` on `[data-mid-body]` fires.
ChannelView reads `?msg` via `useSearchParams()` (`jumpMsg`) and the effect is keyed on
`[hasMessages, channelId, jumpMsg]` — **gate on the boolean `hasMessages = messages.length>0`, NOT
the raw count**, or every send/incoming message re-fires the jump and yanks the viewport back to
the old message. As soon as the target resolves (or after ~2.5s of not finding it) the effect
strips `?msg` with `router.replace(pathname,{scroll:false})` so it's one-shot AND re-clicking the
same result re-fires (identical-URL push is a no-op otherwise). **Search + notification results now
deep-link with `?msg=`** (CommandPalette.go, NotificationsPanel; Notification/mock data gained an
optional `messageId`; the Liam-mention notif/inbox point at `m_21` in `d_liam`, not `m_23` which is
`d_ava`). A **search hit on a thread reply** (msg with `parentId`) opens the thread panel
(`openThread`) instead of a `?msg=` jump that would never find a main-list node — `SearchResult`
gained `parentId`. In dev the flash is unreliable (node churn); the scroll always works.
**Editor lists:** inside a list BOTH Enter (native) and Shift+Enter (`execCommand insertParagraph`)
make the next bullet/number; Tab/Shift-Tab indent 3 levels. Outside a list Enter sends, Shift+Enter
soft-newlines. **Composer has a visible amber "✨ AI" button** (`flags.aiAssist`) → `openAi()`.
**Gotchas:** `reactStrictMode:false` in next.config. Bump `__lynkdStore_vN` in `store.ts` after
adding store methods (HMR singleton). See [[qa-and-fixes]], [[iios-relationship]], [[architecture-decisions]].
+32
View File
@@ -0,0 +1,32 @@
---
name: iios-relationship
description: How this demo relates to the IIOS service/SDKs at D:\iios (reference only)
metadata:
type: reference
---
The **IIOS** monorepo lives at `D:\iios` — a NestJS service (`packages/iios-service`, port
3200) plus React SDKs (`iios-message-web`, `iios-inbox-web`, `iios-support-web`,
`iios-community-web`, `iios-ai-web`, `iios-meeting-web`) over a low-level `iios-kernel-client`.
It treats every interaction (message, ticket, route decision, AI proposal, meeting) as one
governed `Interaction` behind fail-closed policy/consent gates.
**This repo is standalone and does NOT import or call IIOS.** IIOS was used only to model the
domain vocabulary and the surfaces to build. Deliberate parallels so a later swap is easy:
| This repo | IIOS analogue |
|---|---|
| `Message` / `Channel` / thread | `Message` / `Conversation`(`IiosThread`) / parent-child interactions |
| `InboxItem` (NEEDS_REPLY, MENTION…) | inbox projector items (`iios-inbox-web`) |
| `Ticket` (state/priority, escalate, callback) | `iios-support-web` tickets/escalate/callbacks |
| `BffClient``/api/bff/**` | `RestClient``iios-service` `/v1/**` |
| feature flags / theming | app-level concerns (IIOS is backend-governed) |
**Live wiring is built AND proven.** The BFF uses a `Backend` interface (`apps/web/lib/backend/`);
`BFF_BACKEND=iios` swaps in `iios.ts` → real `iios-service` via `lib/iios/client.ts` (dev-token
auth) + `map.ts` (shape mappers). This was run end-to-end: IIOS service on :3200 against a native
Postgres (role `iios`/`iios`, db `iios` on **:5432** — no Docker needed; a separate :5434 cluster
crashed under the sandbox's process/shared-memory limits, so the existing service on 5432 was used),
9 migrations applied, an ingested message read back through this app's BFF. Default stays mock
(rich demo data; fresh IIOS DB is empty and has no channel-directory endpoint). Exact steps:
`docs/IIOS_INTEGRATION.md`. Related: [[architecture-decisions]], [[qa-and-fixes]].
+31
View File
@@ -0,0 +1,31 @@
---
name: project-overview
description: What this repo is, who it's for, and the non-obvious constraints
metadata:
type: project
---
This repo (`message-inbox-web-frontend-sdk`) is a **static frontend demo** of a Slack-grade
team-messaging + inbox + support product, delivered as a reusable **React SDK**
(`@lynkd/messaging-inbox-sdk`) plus a **Next.js demo app** (`apps/web`).
**Non-obvious goals (from the requesting stakeholder):**
- It must **look like** the LynkedUp Pro CRM dashboard (dark + amber accent `#FDA913`), and
support **light AND dark** modes. The "Support Center" page especially should feel native
to that CRM.
- It must match **Slack's feature depth** (channels, DMs, threads, reactions, pins, mentions,
presence, huddles, rich composer, scheduled send, search, saved) — as a **static** demo
first (mock data, no real backend).
- **Every feature toggled by an env flag** (`NEXT_PUBLIC_FEATURE_*`), and the **UI fully
themeable** (accent/radius/gradient) via env or a live in-app panel, defaulting to the
LynkedUp look.
- Must use the **BFF pattern** (client requested this explicitly). Here the BFF = Next.js
route handlers under `app/api/bff/**`.
- Stack the stakeholder named: Next.js (frontend), NestJS (backend), Postgres/Supabase.
For the static demo we use Next.js + the Next route-handler BFF only; NestJS/DB are a
documented later swap, **not** built.
- The real IIOS SDK/service lives at `D:\iios` and was used **only as reference** — this app
is deliberately standalone and does not import or call it. See [[iios-relationship]].
Deadline context: originally requested as a ~2-hour demo. Prioritize a working, good-looking
demo over completeness. See [[architecture-decisions]] and [[design-system]].
+31
View File
@@ -0,0 +1,31 @@
---
name: qa-and-fixes
description: The deep-QA pass and what was fixed (reactions, composer, dead controls, responsive)
metadata:
type: project
---
A deep QA pass (a 5-agent static audit → 68 findings, plus live Playwright driving) fixed the
issues the stakeholder reported and more. All verified in-browser + typecheck clean.
**Headline fixes:**
- **Reactions** — the hover toolbar's emoji picker used to unmount on `mouseleave` (it floats in a
gap outside the row), so reacting was impossible for messages without existing reactions. Fixed:
toolbar stays while the picker/menu is open; picker also works in the thread panel (which had
no handlers wired). `useThread` now exposes react/pin/save.
- **Composer** — was a plain textarea inserting literal `**` markers. Now a **contentEditable
WYSIWYG** (`Composer.tsx`) where Bold/Italic/Strike/Code/lists format live and serialize to
markdown on send; `lib/rich-text.tsx` renders it (added `~~strike~~` + `[label](url)`).
- **Dead controls → real** — create-channel (real BFF create + navigate + appears in sidebar),
new-message, invite, add-workspace, callback, channel-details are modals (`overlays/Modals.tsx`
+ `ui/Modal.tsx`); huddle mute/video/share toggle + live timer; profile Message/Huddle; palette
person/file; workspace switcher; a toast system (`Toaster.tsx`, `useUi().toast`).
- **Layout/scroll "not fit"** — responsive down to ~400px: sidebar drawer closes on nav; thread
panel is a mobile overlay; Support master-detail stacks (list ↔ detail + back button); no
horizontal overflow.
- **Support state** — list + detail + stats now share one `useTickets` source, so Resolve/reply
update everywhere at once.
- **Theme flash** — pre-paint script in `layout.tsx` + light tokens under `html:not(.dark)` in
`globals.css`; ThemingPanel System mode works (matchMedia).
See [[architecture-decisions]] and [[conventions]].
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(pnpm install)",
"Bash(pnpm dev)",
"Bash(pnpm build)",
"Bash(pnpm typecheck)",
"Bash(pnpm --filter *)",
"Bash(pnpm exec next *)",
"Read",
"Grep",
"Glob",
"Bash(curl -s -o /dev/null -w \"home:%{http_code} \" http://localhost:4300/)",
"Bash(curl -s -o /dev/null -w \"channel:%{http_code} \" http://localhost:4300/c/c_general)",
"Bash(curl -s -o /dev/null -w \"inbox:%{http_code} \" http://localhost:4300/inbox)",
"Bash(curl -s -o /dev/null -w \"support:%{http_code} \" http://localhost:4300/support)",
"Bash(curl -s -o /dev/null -w \"bootstrap-api:%{http_code}\" http://localhost:4300/api/bff/bootstrap)",
"PowerShell($ps = \\(Get-NetTCPConnection -LocalPort 4300 -State Listen -ErrorAction SilentlyContinue\\).OwningProcess | Select-Object -Unique; foreach \\($procId in $ps\\) { Stop-Process -Id $procId -Force -ErrorAction SilentlyContinue }; Start-Sleep -Seconds 1; if \\(Get-NetTCPConnection -LocalPort 4300 -State Listen -ErrorAction SilentlyContinue\\) { \"still listening\" } else { \"4300 free\" })",
"Bash(curl -s -o /dev/null -w \"%{http_code}\" http://localhost:4300/c/c_general)",
"Bash(cd d:\\\\\\\\message-inbox-web-frontend-sdk)",
"Bash(rm -f .playwright-mcp/demo-*.png)"
]
}
}