docs(roadmap): split agent prompts into individual docs for one-liner delegation

New files in coding_agent_prompts/:
- TRACK_A_llm_lab_workspace_removal.md (learning_ai_local_llms)
- TRACK_B_ollama_client_package.md (learning_ai_common_plat)
- TRACK_C_fastify_sse_extend.md (learning_ai_common_plat)
- TRACK_D_react_hook_packages.md (learning_ai_common_plat)
- TRACK_E_localmemgpt_migrations.md (learning_ai_local_memory_gpt)
- README.md with dependency diagram + one-liner table

Roadmap delegation section now links to individual docs instead of
inline prompts. Point an agent at one file:
  Read and execute coding_agent_prompts/TRACK_A_*.md
This commit is contained in:
saravanakumardb1 2026-03-29 12:19:03 -07:00
parent 73a70c2f00
commit a8e0b7c3b0
7 changed files with 620 additions and 266 deletions

View File

@ -901,279 +901,35 @@ export function consumeNdjsonStream<T>(
> **5 tracks can run in parallel.** Tracks AD start immediately. Track E waits for A+B+C.
> Phase 3 is deferred until Phase 1+2 are done and you've evaluated what's missed.
>
> **Individual prompt docs:** [`coding_agent_prompts/`](./coding_agent_prompts/) — point an agent at one file.
### Dependency Diagram
```
┌─────────────────────────────────────────┐
START ────────►│ Track A: Phase 1 + 2.1 (LLM Lab) │──┐
│ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
├───────────►│ Track B: 4.1 create ollama-client pkg │──┤
│ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │ ┌──────────────────────────────────┐
├───────────►│ Track C: 4.2 extend fastify-sse │──┼──►│ Track E: Migrate LocalMemGPT │
│ └─────────────────────────────────────────┘ │ │ (2.2 + 4.1a + 4.2a) │
│ ┌─────────────────────────────────────────┐ │ └──────────────────────────────────┘
└───────────►│ Track D: 4.3 + 4.4 React hooks │──┘
└─────────────────────────────────────────┘
After Track E → Phase 3 (optional)
┌──────────────────────────────────────┐
START ────────►│ Track A: LLM Lab cleanup (2.5h) │──┐
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
├───────────►│ Track B: ollama-client pkg (1 day) │──┤
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │ ┌─────────────────────────────┐
├───────────►│ Track C: fastify-sse extend (2h) │──┼──►│ Track E: LocalMemGPT (3h) │
│ └──────────────────────────────────────┘ │ └─────────────────────────────┘
│ ┌──────────────────────────────────────┐ │
└───────────►│ Track D: React hooks (5h) │──┘
└──────────────────────────────────────┘
```
### Track A — Agent 1: LLM Lab Workspace Removal
### Track Summary
**Repo:** `learning_ai_local_llms`
**Can start:** Immediately
**Estimated time:** ~2.5 hours
<details>
<summary>Copy-pastable prompt for Agent 1</summary>
```
Read the consolidation roadmap at ../learning_ai_common_plat/docs/roadmaps/not-started/LOCAL_AI_CONSOLIDATION_ROADMAP.md — specifically the Execution Checklist and Safety Matrix sections.
Execute Phase 1 + Phase 2.1 in the learning_ai_local_llms repo. This removes the chat workspace from Local LLM Lab, making Local Memory GPT the single chat surface.
Steps (3 commits):
COMMIT 1 — refactor(dashboard): remove chat workspace
1. Run `cd dashboard && pnpm test && pnpm run build` to establish green baseline
2. Delete the entire `dashboard/src/app/(workspace)/` directory (22 files)
3. Delete `dashboard/src/app/api/ollama/title/` directory (1 workspace-only route)
4. Delete workspace lib files: db.ts, agents.ts, quick-actions.ts, scheduled-tasks.ts, cron.ts, migrate.ts
5. Trim types.ts: move Attachment (lines 119-126) and ModelDefaults (lines 163-169) into the shared section (after line 77), then delete everything from line 78 onward (the "// --- v4 Workspace Types ---" comment and below)
6. Delete workspace tests: __tests__/agents.test.ts, __tests__/cron.test.ts
7. Create dashboard/src/app/page.tsx that redirects to /mission-control:
import { redirect } from 'next/navigation';
export default function Home() { redirect('/mission-control'); }
8. Remove npm deps from dashboard/package.json: idb, cron-parser, fuse.js
9. Run pnpm install to update lockfile
10. Verify: pnpm run typecheck && pnpm test && pnpm run build
CRITICAL SAFETY RULES:
- DO NOT delete api/ollama/chat/, api/ollama/stream/, api/ollama/logs/, api/tts/, api/whisper/, api/whisper/transcribe/ — Mission Control uses all of these
- DO NOT delete lib/router.ts, lib/format.ts, lib/ollama-config.ts, lib/llm-router.ts, lib/product-config.ts, lib/use-theme.ts, lib/use-keyboard-shortcuts.ts — all shared
- DO NOT delete src/components/ — shared by Mission Control
- Verify: grep for any remaining imports of deleted files after cleanup
COMMIT 2 — test(e2e): update specs after workspace removal
1. Delete e2e/chat.spec.ts
2. Update e2e/health.spec.ts — remove /c/test-id test
3. Update e2e/accessibility.spec.ts — remove /compare, /tts, /whisper from routes array
4. Update e2e/visual-regression.spec.ts — remove workspace and compare from ROUTES
5. Update e2e/navigation.spec.ts — / now redirects to /mission-control
6. Update e2e/models.spec.ts — / now redirects
COMMIT 3 — docs: update agent docs after workspace removal
1. Update AGENTS.md — remove workspace references, update repo layout, adjust test count
2. Update README.md — remove workspace description, add note pointing to Local Memory GPT
3. Update CLAUDE.md, .windsurfrules, .cursorrules — align with new scope
PHASE 2.1 — feat(dashboard): add LocalMemGPT cross-link in Mission Control
1. Add a "Chat with your models →" card/banner in Mission Control linking to http://localhost:3070
2. Make URL configurable via LOCALMEMGPT_URL env var (default: http://localhost:3070)
3. Add LOCALMEMGPT_URL to .env.example
```
</details>
### Track B — Agent 2: Create `@bytelyst/ollama-client` Package
**Repo:** `learning_ai_common_plat`
**Can start:** Immediately
**Estimated time:** ~1 day
<details>
<summary>Copy-pastable prompt for Agent 2</summary>
```
Read the consolidation roadmap at docs/roadmaps/not-started/LOCAL_AI_CONSOLIDATION_ROADMAP.md — specifically sections 4.1, 4.5, 4.6, and 4.7.
Create a new @bytelyst/ollama-client package in packages/ollama-client/ in the learning_ai_common_plat repo. This is a shared Ollama API client that eliminates ~350 lines of duplicated code across learning_ai_local_llms and learning_ai_local_memory_gpt.
Package structure:
packages/ollama-client/
├── src/
│ ├── index.ts # Public exports
│ ├── config.ts # resolveOllamaUrl(env) — OLLAMA_URL/OLLAMA_HOST + WSL2 gateway detection
│ ├── client.ts # OllamaClient class: tags, ps, show, pull, load, unload, delete, version
│ ├── stream.ts # streamChat(), streamGenerate() — AsyncGenerator<OllamaStreamChunk>
│ ├── embed.ts # getEmbedding()
│ ├── health.ts # checkHealth() with timeout
│ ├── ndjson.ts # parseNdjsonStream() — reusable NDJSON async generator (server + client)
│ ├── types.ts # OllamaModel, OllamaChatMessage, OllamaStreamChunk, OllamaEmbedding
│ └── client-parsers.ts # consumeSSEStream(), consumeNdjsonStream() — browser-side stream consumers
├── package.json
└── tsconfig.json
Reference implementations to consolidate:
- learning_ai_local_memory_gpt/backend/src/lib/ollama.ts (117 lines) — listOllamaModels, checkOllamaHealth, streamChat, getEmbedding
- learning_ai_local_llms/dashboard/src/app/lib/ollama-config.ts (40 lines) — OLLAMA_URL config + WSL2 detection
- learning_ai_local_llms/dashboard/src/app/api/ollama/route.ts (128 lines) — fetchOllama wrapper
- NDJSON stream parsing pattern copy-pasted 5+ times across both repos
Also extract reusable utilities (section 4.6):
- formatBytes() — general purpose byte formatting
- estimateTokens() — LLM token estimation
- getModelContextWindow() — model context window lookup
- formatUptime() — uptime formatting
Canonical OllamaModel type (section 4.5) — match Ollama's actual API response, superset of both repos' definitions.
Follow existing @bytelyst/* package conventions:
- ESM ("type": "module"), TypeScript, exports field in package.json
- Extend ../../tsconfig.base.json
- Vitest for tests
- peerDependencies for heavy deps if any
DO NOT migrate consumer repos — that happens in Track E. Only create the package + tests + publish to Gitea registry.
COMMIT: feat(ollama-client): shared Ollama API client package
```
</details>
### Track C — Agent 3: Extend `@bytelyst/fastify-sse`
**Repo:** `learning_ai_common_plat`
**Can start:** Immediately
**Estimated time:** ~2 hours
<details>
<summary>Copy-pastable prompt for Agent 3</summary>
```
Read the consolidation roadmap at docs/roadmaps/not-started/LOCAL_AI_CONSOLIDATION_ROADMAP.md — specifically section 4.2.
Extend the existing @bytelyst/fastify-sse package (packages/fastify-sse/) with per-request SSE helpers. The package currently only has SSEHub (multi-client broadcast). We need single-request streaming helpers too.
Reference implementation to absorb:
- learning_ai_local_memory_gpt/backend/src/lib/sse-helpers.ts (26 lines):
- startSSE(reply) — sets headers, calls reply.hijack()
- sendSSEData(reply, data) — writes "data: JSON\n\n"
- sendSSEEvent(reply, event, data) — writes "event: name\ndata: JSON\n\n"
- endSSE(reply) — writes "[DONE]" and ends stream
Add a new file: packages/fastify-sse/src/per-request.ts
Export from index.ts alongside existing SSEHub + ssePlugin exports.
Add tests in packages/fastify-sse/src/per-request.test.ts.
Publish updated package to Gitea registry.
DO NOT migrate consumer repos — that happens in Track E.
COMMIT: feat(fastify-sse): add per-request SSE helpers
```
</details>
### Track D — Agent 4: Create React Hook Packages
**Repo:** `learning_ai_common_plat`
**Can start:** Immediately
**Estimated time:** ~5 hours
<details>
<summary>Copy-pastable prompt for Agent 4</summary>
```
Read the consolidation roadmap at docs/roadmaps/not-started/LOCAL_AI_CONSOLIDATION_ROADMAP.md — specifically sections 4.3 and 4.4.
Create TWO new @bytelyst/* packages:
PACKAGE 1: packages/use-theme/
A configurable React useTheme() hook. Currently duplicated across 6 web apps with only the storage key differing.
Reference implementations:
- learning_ai_local_memory_gpt/web/src/lib/use-theme.ts (49 lines, key: "lmg-theme")
- learning_ai_local_llms/dashboard/src/app/lib/use-theme.ts (48 lines, key: "llm-theme")
- Also in: ChronoMind, FlowMonk, NomGap, ActionTrail (all ~50 lines each)
API:
export function useTheme(options?: {
storageKey?: string; // default: 'theme'
attribute?: 'class' | 'data-theme'; // default: 'class'
}): { theme: 'light' | 'dark'; setTheme: (t: 'light' | 'dark') => void; toggleTheme: () => void }
COMMIT: feat(use-theme): shared React theme toggle hook
PACKAGE 2: packages/use-keyboard-shortcuts/
A data-driven React useKeyboardShortcuts() hook. Currently duplicated across 5+ web apps with hardcoded if/else chains.
Reference implementations:
- learning_ai_local_memory_gpt/web/src/lib/use-keyboard-shortcuts.ts (53 lines)
- learning_ai_local_llms/dashboard/src/app/lib/use-keyboard-shortcuts.ts (57 lines)
- Also in: ChronoMind, ActionTrail, NoteLett
API:
interface ShortcutDef {
key: string;
meta?: boolean;
shift?: boolean;
alt?: boolean;
handler: () => void;
allowInInput?: boolean;
description?: string;
}
export function useKeyboardShortcuts(shortcuts: ShortcutDef[]): void
COMMIT: feat(use-keyboard-shortcuts): shared React keyboard shortcuts hook
For both packages:
- ESM ("type": "module"), TypeScript, React as peerDependency
- Vitest + @testing-library/react for tests
- Extend ../../tsconfig.base.json
- Publish to Gitea registry
DO NOT migrate consumer repos yet — migrations can happen as a follow-up sweep.
```
</details>
### Track E — Agent 5: Migrate LocalMemGPT (after A+B+C complete)
**Repo:** `learning_ai_local_memory_gpt`
**Can start:** After Tracks A, B, and C are complete
**Estimated time:** ~3 hours
<details>
<summary>Copy-pastable prompt for Agent 5</summary>
```
Read the consolidation roadmap at ../learning_ai_common_plat/docs/roadmaps/not-started/LOCAL_AI_CONSOLIDATION_ROADMAP.md.
Execute 3 migrations in the learning_ai_local_memory_gpt repo:
TASK 1 — Phase 2.2: Add LLM Lab cross-link
1. Add a "Model Management →" link in the Settings panel (web/src/components/SettingsPanel.tsx)
2. Link to http://localhost:3000 (LLM Lab dashboard)
3. Make URL configurable via LOCALLLMLAB_URL env var (default: http://localhost:3000)
4. Add LOCALLLMLAB_URL to .env.example
COMMIT: feat(web): add LLM Lab cross-link in settings
TASK 2 — Migrate to @bytelyst/ollama-client
1. Add @bytelyst/ollama-client dependency (from Gitea registry)
2. Replace backend/src/lib/ollama.ts (117 lines) with imports from @bytelyst/ollama-client:
- listOllamaModels → import { listModels } from '@bytelyst/ollama-client'
- checkOllamaHealth → import { checkHealth } from '@bytelyst/ollama-client'
- streamChat → import { streamChat } from '@bytelyst/ollama-client'
- getEmbedding → import { getEmbedding } from '@bytelyst/ollama-client'
- OllamaModel type → import { OllamaModel } from '@bytelyst/ollama-client'
3. Update all consumers of lib/ollama.ts to use new imports
4. Run: cd backend && npm test && npm run typecheck && npm run build
COMMIT: refactor(backend): migrate to @bytelyst/ollama-client
TASK 3 — Migrate to @bytelyst/fastify-sse per-request helpers
1. Replace backend/src/lib/sse-helpers.ts (26 lines) with imports from @bytelyst/fastify-sse:
- import { startSSE, sendSSEData, sendSSEEvent, endSSE } from '@bytelyst/fastify-sse'
2. Update chat/routes.ts and compare/routes.ts to use new imports
3. Delete lib/sse-helpers.ts
4. Run: cd backend && npm test && npm run typecheck && npm run build
COMMIT: refactor(backend): migrate to @bytelyst/fastify-sse per-request helpers
Verify all ~104 tests still pass after each migration.
```
</details>
| Track | Prompt Doc | Repo | One-Liner |
| ----- | ----------------------------------------------------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------ |
| **A** | [`TRACK_A_llm_lab_workspace_removal.md`](./coding_agent_prompts/TRACK_A_llm_lab_workspace_removal.md) | `learning_ai_local_llms` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_A_llm_lab_workspace_removal.md` |
| **B** | [`TRACK_B_ollama_client_package.md`](./coding_agent_prompts/TRACK_B_ollama_client_package.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_B_ollama_client_package.md` |
| **C** | [`TRACK_C_fastify_sse_extend.md`](./coding_agent_prompts/TRACK_C_fastify_sse_extend.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_C_fastify_sse_extend.md` |
| **D** | [`TRACK_D_react_hook_packages.md`](./coding_agent_prompts/TRACK_D_react_hook_packages.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_D_react_hook_packages.md` |
| **E** | [`TRACK_E_localmemgpt_migrations.md`](./coding_agent_prompts/TRACK_E_localmemgpt_migrations.md) | `learning_ai_local_memory_gpt` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_E_localmemgpt_migrations.md` |
---

View File

@ -0,0 +1,34 @@
# Local AI Consolidation — Agent Prompts
> Individual prompt docs for the [Local AI Consolidation Roadmap](../LOCAL_AI_CONSOLIDATION_ROADMAP.md).
> Point an agent at one file with a one-liner like: `Read and execute <path-to-file>`
## Parallel Execution
```
Tracks AD start immediately (no dependencies).
Track E starts after A+B+C are done.
┌──────────────────────────────────────┐
START ────────►│ Track A: LLM Lab cleanup (2.5h) │──┐
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
├───────────►│ Track B: ollama-client pkg (1 day) │──┤
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │ ┌─────────────────────────────┐
├───────────►│ Track C: fastify-sse extend (2h) │──┼──►│ Track E: LocalMemGPT (3h) │
│ └──────────────────────────────────────┘ │ └─────────────────────────────┘
│ ┌──────────────────────────────────────┐ │
└───────────►│ Track D: React hooks (5h) │──┘
└──────────────────────────────────────┘
```
## Files
| File | Repo | One-Liner |
| ------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------ |
| [TRACK_A](./TRACK_A_llm_lab_workspace_removal.md) | `learning_ai_local_llms` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_A_llm_lab_workspace_removal.md` |
| [TRACK_B](./TRACK_B_ollama_client_package.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_B_ollama_client_package.md` |
| [TRACK_C](./TRACK_C_fastify_sse_extend.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_C_fastify_sse_extend.md` |
| [TRACK_D](./TRACK_D_react_hook_packages.md) | `learning_ai_common_plat` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_D_react_hook_packages.md` |
| [TRACK_E](./TRACK_E_localmemgpt_migrations.md) | `learning_ai_local_memory_gpt` | `Read and execute docs/roadmaps/not-started/coding_agent_prompts/TRACK_E_localmemgpt_migrations.md` |

View File

@ -0,0 +1,94 @@
# Track A — LLM Lab Workspace Removal
> **Repo:** `learning_ai_local_llms`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~2.5 hours
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — read the Execution Checklist and Safety Matrix sections first.
---
## Goal
Remove the chat workspace from Local LLM Lab, making Local Memory GPT the single chat surface. Then add a cross-link from Mission Control to LocalMemGPT.
---
## Steps (4 commits)
### COMMIT 1 — `refactor(dashboard): remove chat workspace — LocalMemGPT is the chat surface`
1. Run `cd dashboard && pnpm test && pnpm run build` to establish green baseline
2. Delete the entire `dashboard/src/app/(workspace)/` directory (22 files: 6 routes + 16 components)
3. Delete `dashboard/src/app/api/ollama/title/` directory (1 workspace-only route)
4. Delete workspace lib files:
- `dashboard/src/app/lib/db.ts`
- `dashboard/src/app/lib/agents.ts`
- `dashboard/src/app/lib/quick-actions.ts`
- `dashboard/src/app/lib/scheduled-tasks.ts`
- `dashboard/src/app/lib/cron.ts`
- `dashboard/src/app/lib/migrate.ts`
5. Trim `types.ts`:
- Copy `Attachment` (lines 119126) and `ModelDefaults` (lines 163169) into the shared section (after line 77)
- Delete everything from line 78 onward (the `// --- v4 Workspace Types ---` comment and below)
- **Why:** `router.ts` line 1 does `import type { Attachment, ModelDefaults } from './types'` → used by `api/ollama/chat/route.ts` (Mission Control)
6. Delete workspace tests:
- `dashboard/src/app/lib/__tests__/agents.test.ts`
- `dashboard/src/app/lib/__tests__/cron.test.ts`
7. Create `dashboard/src/app/page.tsx` that redirects to `/mission-control`:
```tsx
import { redirect } from 'next/navigation';
export default function Home() {
redirect('/mission-control');
}
```
8. Remove npm deps from `dashboard/package.json`: `idb`, `cron-parser`, `fuse.js`
9. Run `pnpm install` to update lockfile
10. Verify: `pnpm run typecheck && pnpm test && pnpm run build`
11. Grep for any remaining imports of deleted files:
```bash
grep -r 'from.*\(workspace\)\|from.*db\|from.*agents\|from.*quick-actions\|from.*scheduled-tasks\|from.*cron\|from.*migrate' dashboard/src/
```
### COMMIT 2 — `test(e2e): update specs after workspace removal`
1. Delete `e2e/chat.spec.ts` — tests workspace chat flow
2. Update `e2e/health.spec.ts` — remove `Conversation Routes` test that navigates to `/c/test-id`
3. Update `e2e/accessibility.spec.ts` — remove `/compare`, `/tts`, `/whisper` from routes array (keep `/`, `/mission-control`)
4. Update `e2e/visual-regression.spec.ts` — remove `workspace` and `compare` from ROUTES array (keep `mission-control`)
5. Update `e2e/navigation.spec.ts``/` now redirects to `/mission-control`
6. Update `e2e/models.spec.ts``/` now redirects
### COMMIT 3 — `docs: update agent docs after workspace removal`
1. Update `AGENTS.md` — remove workspace references, update repo layout, adjust test count
2. Update `README.md` — remove chat workspace description, add note pointing to Local Memory GPT for chat
3. Update `CLAUDE.md`, `.windsurfrules`, `.cursorrules` — align with new scope
### COMMIT 4 — `feat(dashboard): add LocalMemGPT cross-link in Mission Control`
1. Add a "Chat with your models →" card/banner in Mission Control dashboard
2. Link to `http://localhost:3070` (LocalMemGPT web default port)
3. Make URL configurable via `LOCALMEMGPT_URL` env var (default: `http://localhost:3070`)
4. Add `LOCALMEMGPT_URL` to `.env.example`
---
## CRITICAL SAFETY RULES
**DO NOT delete these API routes** — Mission Control uses all of them:
- `api/ollama/chat/` — model testing prompt (page.tsx line 719)
- `api/ollama/stream/` — model comparison + benchmarks (lines 331, 538)
- `api/ollama/logs/` — Ollama log viewer (line 297)
- `api/tts/` — TTS engine status panel (line 179)
- `api/whisper/` — Whisper status card (line 165)
- `api/whisper/transcribe/` — transcription test button (line 313)
**DO NOT delete these lib files** — all shared:
- `lib/router.ts`, `lib/format.ts`, `lib/ollama-config.ts`, `lib/llm-router.ts`
- `lib/product-config.ts`, `lib/use-theme.ts`, `lib/use-keyboard-shortcuts.ts`
**DO NOT delete** `src/components/` — all 6 components are shared by Mission Control.
**DO NOT delete** `globals.css` — all 319 lines are shared (no workspace-specific tokens).

View File

@ -0,0 +1,147 @@
# Track B — Create `@bytelyst/ollama-client` Package
> **Repo:** `learning_ai_common_plat`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~1 day
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — sections 4.1, 4.5, 4.6, and 4.7.
---
## Goal
Create a new `@bytelyst/ollama-client` package that eliminates ~350 lines of duplicated Ollama API code across `learning_ai_local_llms` and `learning_ai_local_memory_gpt`.
---
## Package Structure
```
packages/ollama-client/
├── src/
│ ├── index.ts # Public exports
│ ├── config.ts # resolveOllamaUrl(env) — OLLAMA_URL/OLLAMA_HOST + WSL2 gateway detection
│ ├── client.ts # OllamaClient class: tags, ps, show, pull, load, unload, delete, version
│ ├── stream.ts # streamChat(), streamGenerate() — AsyncGenerator<OllamaStreamChunk>
│ ├── embed.ts # getEmbedding()
│ ├── health.ts # checkHealth() with timeout
│ ├── ndjson.ts # parseNdjsonStream() — reusable NDJSON async generator (server + client)
│ ├── types.ts # OllamaModel, OllamaChatMessage, OllamaStreamChunk, OllamaEmbedding
│ └── client-parsers.ts # consumeSSEStream(), consumeNdjsonStream() — browser-side stream consumers
├── package.json
├── tsconfig.json
└── vitest.config.ts
```
---
## Reference Implementations to Consolidate
Read these files from sibling repos to understand the existing patterns:
1. **`../learning_ai_local_memory_gpt/backend/src/lib/ollama.ts`** (117 lines)
- `listOllamaModels()``GET /api/tags`
- `checkOllamaHealth()``GET /` with timeout
- `streamChat()``POST /api/chat` with NDJSON response parsing
- `getEmbedding()``POST /api/embed`
2. **`../learning_ai_local_llms/dashboard/src/app/lib/ollama-config.ts`** (40 lines)
- `OLLAMA_URL` env resolution
- WSL2 gateway detection (reads `/etc/resolv.conf` nameserver)
3. **`../learning_ai_local_llms/dashboard/src/app/api/ollama/route.ts`** (128 lines)
- `fetchOllama()` helper wrapper
- Model list, pull, delete, show, load, unload operations
4. **NDJSON stream parsing pattern** (copy-pasted 5+ times across both repos):
```ts
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() ?? '';
for (const line of lines) {
if (!line.trim()) continue;
try {
yield JSON.parse(line);
} catch {
/* skip */
}
}
}
```
---
## Types to Include (section 4.5)
Canonical `OllamaModel` type — superset of both repos' definitions, matching Ollama's actual API response:
| Repo | Definition |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| LocalMemGPT `ollama.ts` | `{ name, size, digest, modified_at, details?: { parameter_size, quantization_level, family } }` |
| LLM Lab `types.ts` | `{ name, model, modified_at, size, digest, details: { families, family, format, parameter_size, quantization_level } }` |
---
## Reusable Utilities to Include (section 4.6)
Extract these from `../learning_ai_local_llms/dashboard/src/app/lib/format.ts` (96 lines):
| Function | Scope |
| ------------------------- | ------------------------------- |
| `formatBytes()` | General purpose byte formatting |
| `estimateTokens()` | LLM token estimation |
| `getModelContextWindow()` | Model context window lookup |
| `formatUptime()` | Uptime formatting |
Leave LLM-Lab-specific functions (`estimateRam`, `checkMemoryFit`, `getModelBadges`) in LLM Lab.
---
## Client-Side Parsers to Include (section 4.7)
Browser-side stream consumers for Next.js API route clients:
```ts
// Client-side SSE stream consumer
export function consumeSSEStream(
response: Response,
onData: (data: Record<string, unknown>) => void,
onDone: () => void
): Promise<void>;
// Client-side NDJSON stream consumer
export function consumeNdjsonStream<T>(
response: Response,
onChunk: (chunk: T) => void
): Promise<void>;
```
---
## Conventions
Follow existing `@bytelyst/*` package patterns:
- ESM (`"type": "module"`), TypeScript, `exports` field in `package.json`
- Extend `../../tsconfig.base.json`
- Vitest for tests with `passWithNoTests: true`
- `peerDependencies` for heavy deps if any
- `workspace:*` for inter-package deps
- Build: `tsc``dist/`
---
## Scope Boundary
**DO NOT** migrate consumer repos — that happens in Track E. Only create the package, write tests, and publish to Gitea registry.
---
## Commit
`feat(ollama-client): shared Ollama API client package`

View File

@ -0,0 +1,81 @@
# Track C — Extend `@bytelyst/fastify-sse` with Per-Request Helpers
> **Repo:** `learning_ai_common_plat`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~2 hours
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — section 4.2.
---
## Goal
Extend the existing `@bytelyst/fastify-sse` package (`packages/fastify-sse/`) with per-request SSE helpers. The package currently only provides `SSEHub` (multi-client broadcast). We need single-request streaming helpers too.
---
## Context
| Existing | What it does |
| -------------------------------- | ------------------------------------------------------------------------------------ |
| `@bytelyst/fastify-sse` `SSEHub` | Multi-client broadcast pattern (hub/spoke) — one event pushed to N connected clients |
| LocalMemGPT `sse-helpers.ts` | Single-request streaming — one route handler streams SSE to one client |
The per-request pattern is used by chat streaming and model comparison endpoints.
---
## Reference Implementation
Read `../learning_ai_local_memory_gpt/backend/src/lib/sse-helpers.ts` (26 lines):
```ts
startSSE(reply); // Sets Content-Type: text/event-stream headers, calls reply.hijack()
sendSSEData(reply, data); // Writes "data: JSON.stringify(data)\n\n"
sendSSEEvent(reply, event, data); // Writes "event: name\ndata: JSON.stringify(data)\n\n"
endSSE(reply); // Writes "data: [DONE]\n\n" and ends the stream
```
---
## Implementation
1. Add a new file: `packages/fastify-sse/src/per-request.ts`
- `startSSE(reply: FastifyReply): void`
- `sendSSEData(reply: FastifyReply, data: unknown): void`
- `sendSSEEvent(reply: FastifyReply, event: string, data: unknown): void`
- `endSSE(reply: FastifyReply): void`
2. Export from `packages/fastify-sse/src/index.ts` alongside existing exports:
```ts
export { startSSE, sendSSEData, sendSSEEvent, endSSE } from './per-request.js';
```
3. Add tests: `packages/fastify-sse/src/per-request.test.ts`
- Test that `startSSE` sets correct headers and hijacks the reply
- Test that `sendSSEData` formats data correctly
- Test that `sendSSEEvent` formats named events correctly
- Test that `endSSE` sends the `[DONE]` sentinel and ends the stream
4. Publish updated package to Gitea registry
---
## Conventions
- ESM (`"type": "module"`), TypeScript
- Fastify 5 types (`FastifyReply`)
- Vitest for tests
- Don't break existing `SSEHub` / `ssePlugin` exports
---
## Scope Boundary
**DO NOT** migrate consumer repos — that happens in Track E. Only add the per-request helpers to the package.
---
## Commit
`feat(fastify-sse): add per-request SSE helpers`

View File

@ -0,0 +1,160 @@
# Track D — Create React Hook Packages (`use-theme` + `use-keyboard-shortcuts`)
> **Repo:** `learning_ai_common_plat`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~5 hours
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — sections 4.3 and 4.4.
---
## Goal
Create TWO new `@bytelyst/*` packages that eliminate ~550 lines of duplicated React hooks across the ecosystem.
---
## Package 1: `packages/use-theme/`
### Problem
Near-identical `useTheme()` hook duplicated across **6 web apps** — only the storage key and class-application strategy differ.
| Repo | File | Storage Key | Lines |
| ----------- | ------------------------------------ | ----------- | ----- |
| LocalMemGPT | `web/src/lib/use-theme.ts` | `lmg-theme` | 49 |
| LLM Lab | `dashboard/src/app/lib/use-theme.ts` | `llm-theme` | 48 |
| ChronoMind | `web/src/lib/use-theme.ts` | `cm-theme` | ~50 |
| FlowMonk | `web/src/lib/use-theme.ts` | `fm-theme` | ~50 |
| NomGap | `web/src/lib/use-theme.ts` | `ng-theme` | ~50 |
| ActionTrail | `web/src/lib/use-theme.ts` | `at-theme` | ~50 |
### Reference Implementations
Read these two files to understand the pattern:
- `../learning_ai_local_memory_gpt/web/src/lib/use-theme.ts` (49 lines)
- `../learning_ai_local_llms/dashboard/src/app/lib/use-theme.ts` (48 lines)
### API
```ts
export function useTheme(options?: {
storageKey?: string; // default: 'theme'
attribute?: 'class' | 'data-theme'; // default: 'class'
}): {
theme: 'light' | 'dark';
setTheme: (t: 'light' | 'dark') => void;
toggleTheme: () => void;
};
```
### Package Structure
```
packages/use-theme/
├── src/
│ ├── index.ts # Public export
│ └── use-theme.ts # Hook implementation
├── src/__tests__/
│ └── use-theme.test.ts # Vitest + @testing-library/react
├── package.json
├── tsconfig.json
└── vitest.config.ts
```
### Tests
- Toggles between light and dark
- Persists to localStorage with configurable key
- Applies class to `document.documentElement`
- Reads initial value from localStorage
- Falls back to `'dark'` when no stored value
- Supports `data-theme` attribute mode
### Commit
`feat(use-theme): shared React theme toggle hook`
---
## Package 2: `packages/use-keyboard-shortcuts/`
### Problem
Near-identical `useKeyboardShortcuts()` hook duplicated across **5+ web apps**, each with hardcoded if/else chains.
| Repo | File |
| ----------- | ------------------------------------------------------------ |
| LocalMemGPT | `web/src/lib/use-keyboard-shortcuts.ts` (53 lines) |
| LLM Lab | `dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines) |
| ChronoMind | `web/src/lib/use-keyboard-shortcuts.ts` |
| ActionTrail | `web/src/lib/use-keyboard-shortcuts.ts` |
| NoteLett | `web/src/lib/use-keyboard-shortcuts.ts` |
### Reference Implementations
Read these two files to understand the pattern:
- `../learning_ai_local_memory_gpt/web/src/lib/use-keyboard-shortcuts.ts` (53 lines)
- `../learning_ai_local_llms/dashboard/src/app/lib/use-keyboard-shortcuts.ts` (57 lines)
### API
```ts
export interface ShortcutDef {
key: string;
meta?: boolean;
shift?: boolean;
alt?: boolean;
handler: () => void;
allowInInput?: boolean; // default: false — skip if focus is in input/textarea
description?: string; // for help overlay / accessibility
}
export function useKeyboardShortcuts(shortcuts: ShortcutDef[]): void;
```
### Package Structure
```
packages/use-keyboard-shortcuts/
├── src/
│ ├── index.ts # Public export
│ └── use-keyboard-shortcuts.ts # Hook implementation
├── src/__tests__/
│ └── use-keyboard-shortcuts.test.ts
├── package.json
├── tsconfig.json
└── vitest.config.ts
```
### Tests
- Fires handler on matching key combo
- Respects `meta`, `shift`, `alt` modifiers
- Skips handler when focus is in `<input>` or `<textarea>` (unless `allowInInput: true`)
- Handles multiple shortcuts simultaneously
- Cleans up event listener on unmount
- Handles empty shortcuts array
### Commit
`feat(use-keyboard-shortcuts): shared React keyboard shortcuts hook`
---
## Conventions (both packages)
- ESM (`"type": "module"`), TypeScript
- React 19 as `peerDependency`
- Vitest + `@testing-library/react` + `jsdom` for tests
- Extend `../../tsconfig.base.json`
- `exports` field in `package.json`
- Build: `tsc``dist/`
- Publish to Gitea registry
---
## Scope Boundary
**DO NOT** migrate consumer repos yet — migrations can happen as a follow-up ecosystem sweep. Only create the packages, write tests, and publish.

View File

@ -0,0 +1,82 @@
# Track E — Migrate LocalMemGPT (Cross-Link + Package Migrations)
> **Repo:** `learning_ai_local_memory_gpt`
> **Can start:** After Tracks A, B, and C are complete
> **Estimated time:** ~3 hours
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md`
---
## Prerequisites
Before starting this track, confirm:
- [ ] **Track A** complete — LLM Lab workspace removed, cross-link added
- [ ] **Track B** complete — `@bytelyst/ollama-client` published to Gitea registry
- [ ] **Track C** complete — `@bytelyst/fastify-sse` updated with per-request helpers
---
## Task 1 — Add LLM Lab Cross-Link
Add a link from LocalMemGPT to LLM Lab for model management.
1. Add a "Model Management →" link in the Settings panel (`web/src/components/SettingsPanel.tsx`)
2. Link to `http://localhost:3000` (LLM Lab dashboard default port)
3. Make URL configurable via `LOCALLLMLAB_URL` env var (default: `http://localhost:3000`)
4. Add `LOCALLLMLAB_URL` to `web/.env.example`
**Commit:** `feat(web): add LLM Lab cross-link in settings`
---
## Task 2 — Migrate to `@bytelyst/ollama-client`
Replace the hand-rolled Ollama client with the shared package.
1. Add `@bytelyst/ollama-client` dependency (from Gitea registry)
2. Replace `backend/src/lib/ollama.ts` (117 lines) with imports from the package:
| Old (lib/ollama.ts) | New (@bytelyst/ollama-client) |
| --------------------- | ------------------------------------------------------------ |
| `listOllamaModels()` | `import { listModels } from '@bytelyst/ollama-client'` |
| `checkOllamaHealth()` | `import { checkHealth } from '@bytelyst/ollama-client'` |
| `streamChat()` | `import { streamChat } from '@bytelyst/ollama-client'` |
| `getEmbedding()` | `import { getEmbedding } from '@bytelyst/ollama-client'` |
| `OllamaModel` type | `import type { OllamaModel } from '@bytelyst/ollama-client'` |
3. Update all consumers of `lib/ollama.ts` to use new import paths
4. Delete `backend/src/lib/ollama.ts`
5. Verify: `cd backend && npm test && npm run typecheck && npm run build`
**Commit:** `refactor(backend): migrate to @bytelyst/ollama-client`
---
## Task 3 — Migrate to `@bytelyst/fastify-sse` Per-Request Helpers
Replace the hand-rolled SSE helpers with the shared package exports.
1. Update `@bytelyst/fastify-sse` dependency to latest version (with per-request helpers)
2. Replace `backend/src/lib/sse-helpers.ts` (26 lines) with imports:
```ts
import { startSSE, sendSSEData, sendSSEEvent, endSSE } from '@bytelyst/fastify-sse';
```
3. Update consumers:
- `backend/src/modules/chat/routes.ts`
- `backend/src/modules/compare/routes.ts`
4. Delete `backend/src/lib/sse-helpers.ts`
5. Verify: `cd backend && npm test && npm run typecheck && npm run build`
**Commit:** `refactor(backend): migrate to @bytelyst/fastify-sse per-request helpers`
---
## Verification
After all 3 tasks, run the full test suite and confirm all ~104 tests pass:
```bash
cd backend && npm test && npm run typecheck && npm run build
cd ../web && npm run typecheck && npm run build
```