@hienlh/ppm 0.1.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/.claude/agent-memory/tester/MEMORY.md +3 -0
- package/.claude/agent-memory/tester/project-ppm-test-conventions.md +32 -0
- package/.env.example +1 -0
- package/.github/workflows/release.yml +46 -0
- package/README.md +349 -0
- package/bun.lock +1217 -0
- package/components.json +21 -0
- package/docs/code-standards.md +574 -0
- package/docs/codebase-summary.md +294 -0
- package/docs/deployment-guide.md +631 -0
- package/docs/design-guidelines.md +661 -0
- package/docs/project-overview-pdr.md +142 -0
- package/docs/project-roadmap.md +400 -0
- package/docs/system-architecture.md +459 -0
- package/package.json +68 -0
- package/plans/260314-2009-ppm-implementation/phase-01-project-skeleton.md +81 -0
- package/plans/260314-2009-ppm-implementation/phase-02-backend-core.md +148 -0
- package/plans/260314-2009-ppm-implementation/phase-03-frontend-shell.md +256 -0
- package/plans/260314-2009-ppm-implementation/phase-04-file-explorer-editor.md +120 -0
- package/plans/260314-2009-ppm-implementation/phase-05-web-terminal.md +174 -0
- package/plans/260314-2009-ppm-implementation/phase-06-git-integration.md +244 -0
- package/plans/260314-2009-ppm-implementation/phase-07-ai-chat.md +242 -0
- package/plans/260314-2009-ppm-implementation/phase-08-cli-commands.md +143 -0
- package/plans/260314-2009-ppm-implementation/phase-09-pwa-build-deploy.md +209 -0
- package/plans/260314-2009-ppm-implementation/phase-10-testing.md +311 -0
- package/plans/260314-2009-ppm-implementation/plan.md +202 -0
- package/plans/260315-0356-project-scoped-api-refactor/phase-01-backend-project-router.md +145 -0
- package/plans/260315-0356-project-scoped-api-refactor/phase-02-frontend-api-migration.md +107 -0
- package/plans/260315-0356-project-scoped-api-refactor/phase-03-per-project-tabs.md +100 -0
- package/plans/260315-0356-project-scoped-api-refactor/phase-04-websocket-migration.md +66 -0
- package/plans/260315-0356-project-scoped-api-refactor/plan.md +87 -0
- package/plans/reports/brainstorm-260314-1938-final-techstack.md +342 -0
- package/plans/reports/docs-manager-260315-1314-documentation-creation.md +386 -0
- package/plans/reports/fullstack-developer-260314-2252-phase-02-backend-core.md +57 -0
- package/plans/reports/fullstack-developer-260314-2253-phase-03-frontend-shell.md +70 -0
- package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-api-terminal-ws.md +49 -0
- package/plans/reports/fullstack-developer-260314-2300-phase-04-05-file-explorer-editor-terminal.md +52 -0
- package/plans/reports/fullstack-developer-260314-2307-ai-chat-phase7.md +58 -0
- package/plans/reports/fullstack-developer-260314-2307-phase-06-git-integration.md +33 -0
- package/plans/reports/research-260314-1911-ppm-tech-stack.md +318 -0
- package/plans/reports/research-260314-1930-claude-code-integration.md +293 -0
- package/plans/reports/researcher-260314-2232-node-pty-bun-crash-analysis.md +305 -0
- package/plans/reports/researcher-260314-2232-ui-style.md +942 -0
- package/plans/reports/researcher-260315-0300-opcode-claude-interaction.md +745 -0
- package/plans/reports/researcher-260315-0303-opcode-deep-analysis.md +742 -0
- package/plans/reports/researcher-260315-0305-claude-agent-sdk-github-research.md +423 -0
- package/plans/reports/tester-260314-2053-initial-test-suite.md +81 -0
- package/ppm.example.yaml +14 -0
- package/repomix-output.xml +23745 -0
- package/scripts/build.ts +13 -0
- package/src/cli/commands/chat-cmd.ts +259 -0
- package/src/cli/commands/config-cmd.ts +121 -0
- package/src/cli/commands/git-cmd.ts +315 -0
- package/src/cli/commands/init.ts +57 -0
- package/src/cli/commands/open.ts +19 -0
- package/src/cli/commands/projects.ts +100 -0
- package/src/cli/commands/start.ts +3 -0
- package/src/cli/commands/stop.ts +33 -0
- package/src/cli/utils/project-resolver.ts +27 -0
- package/src/index.ts +59 -0
- package/src/providers/claude-agent-sdk.ts +499 -0
- package/src/providers/claude-binary-finder.ts +256 -0
- package/src/providers/claude-code-cli.ts +413 -0
- package/src/providers/claude-process-registry.ts +106 -0
- package/src/providers/mock-provider.ts +171 -0
- package/src/providers/provider.interface.ts +10 -0
- package/src/providers/registry.ts +45 -0
- package/src/server/helpers/resolve-project.ts +22 -0
- package/src/server/index.ts +181 -0
- package/src/server/middleware/auth.ts +30 -0
- package/src/server/routes/chat.ts +153 -0
- package/src/server/routes/files.ts +168 -0
- package/src/server/routes/git.ts +261 -0
- package/src/server/routes/project-scoped.ts +27 -0
- package/src/server/routes/projects.ts +57 -0
- package/src/server/routes/static.ts +26 -0
- package/src/server/ws/chat.ts +130 -0
- package/src/server/ws/terminal.ts +89 -0
- package/src/services/chat.service.ts +110 -0
- package/src/services/claude-usage.service.ts +113 -0
- package/src/services/config.service.ts +90 -0
- package/src/services/file.service.ts +261 -0
- package/src/services/git-dirs.service.ts +112 -0
- package/src/services/git.service.ts +372 -0
- package/src/services/project.service.ts +107 -0
- package/src/services/slash-items.service.ts +184 -0
- package/src/services/terminal.service.ts +212 -0
- package/src/types/api.ts +37 -0
- package/src/types/chat.ts +92 -0
- package/src/types/config.ts +41 -0
- package/src/types/git.ts +50 -0
- package/src/types/project.ts +18 -0
- package/src/types/terminal.ts +20 -0
- package/src/web/app.tsx +168 -0
- package/src/web/components/auth/login-screen.tsx +88 -0
- package/src/web/components/chat/attachment-chips.tsx +55 -0
- package/src/web/components/chat/chat-placeholder.tsx +10 -0
- package/src/web/components/chat/chat-tab.tsx +301 -0
- package/src/web/components/chat/file-picker.tsx +126 -0
- package/src/web/components/chat/message-input.tsx +420 -0
- package/src/web/components/chat/message-list.tsx +838 -0
- package/src/web/components/chat/session-picker.tsx +139 -0
- package/src/web/components/chat/slash-command-picker.tsx +135 -0
- package/src/web/components/chat/usage-badge.tsx +186 -0
- package/src/web/components/editor/code-editor.tsx +329 -0
- package/src/web/components/editor/diff-viewer.tsx +276 -0
- package/src/web/components/editor/editor-placeholder.tsx +10 -0
- package/src/web/components/explorer/file-actions.tsx +191 -0
- package/src/web/components/explorer/file-tree.tsx +298 -0
- package/src/web/components/git/git-graph.tsx +727 -0
- package/src/web/components/git/git-placeholder.tsx +55 -0
- package/src/web/components/git/git-status-panel.tsx +850 -0
- package/src/web/components/layout/mobile-drawer.tsx +137 -0
- package/src/web/components/layout/mobile-nav.tsx +103 -0
- package/src/web/components/layout/sidebar.tsx +90 -0
- package/src/web/components/layout/tab-bar.tsx +152 -0
- package/src/web/components/layout/tab-content.tsx +85 -0
- package/src/web/components/projects/dir-suggest.tsx +152 -0
- package/src/web/components/projects/project-list.tsx +187 -0
- package/src/web/components/settings/settings-tab.tsx +57 -0
- package/src/web/components/terminal/terminal-placeholder.tsx +10 -0
- package/src/web/components/terminal/terminal-tab.tsx +133 -0
- package/src/web/components/ui/button.tsx +64 -0
- package/src/web/components/ui/context-menu.tsx +250 -0
- package/src/web/components/ui/dialog.tsx +156 -0
- package/src/web/components/ui/dropdown-menu.tsx +257 -0
- package/src/web/components/ui/input.tsx +21 -0
- package/src/web/components/ui/scroll-area.tsx +56 -0
- package/src/web/components/ui/separator.tsx +26 -0
- package/src/web/components/ui/sonner.tsx +40 -0
- package/src/web/components/ui/tabs.tsx +91 -0
- package/src/web/components/ui/tooltip.tsx +57 -0
- package/src/web/hooks/use-chat.ts +420 -0
- package/src/web/hooks/use-terminal.ts +182 -0
- package/src/web/hooks/use-url-sync.ts +66 -0
- package/src/web/hooks/use-websocket.ts +48 -0
- package/src/web/index.html +16 -0
- package/src/web/lib/api-client.ts +90 -0
- package/src/web/lib/file-support.ts +68 -0
- package/src/web/lib/utils.ts +6 -0
- package/src/web/lib/ws-client.ts +100 -0
- package/src/web/main.tsx +10 -0
- package/src/web/public/icon-192.svg +5 -0
- package/src/web/public/icon-512.svg +5 -0
- package/src/web/stores/file-store.ts +81 -0
- package/src/web/stores/project-store.ts +50 -0
- package/src/web/stores/settings-store.ts +65 -0
- package/src/web/stores/tab-store.ts +187 -0
- package/src/web/styles/globals.css +227 -0
- package/src/web/vite-env.d.ts +1 -0
- package/tests/integration/api/chat-routes.test.ts +95 -0
- package/tests/integration/claude-agent-sdk-integration.test.ts +228 -0
- package/tests/integration/ws/chat-websocket.test.ts +312 -0
- package/tests/test-setup.ts +5 -0
- package/tests/unit/providers/claude-agent-sdk.test.ts +339 -0
- package/tests/unit/providers/mock-provider.test.ts +143 -0
- package/tests/unit/services/chat-service.test.ts +100 -0
- package/tsconfig.json +32 -0
- package/vite.config.ts +62 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Project-scoped API routes + per-project tab storage"
|
|
3
|
+
description: "Refactor all APIs under /api/project/:projectName/... and store tabs per-project"
|
|
4
|
+
status: pending
|
|
5
|
+
priority: P1
|
|
6
|
+
effort: 6h
|
|
7
|
+
branch: v2-fresh-start
|
|
8
|
+
tags: [refactor, api, tabs, project-scope]
|
|
9
|
+
created: 2026-03-15
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Project-Scoped API Refactor
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Two changes: (1) move all API routes under `/api/project/:projectName/...` prefix, (2) persist tabs per-project in Zustand so switching projects restores different tab sets.
|
|
17
|
+
|
|
18
|
+
## Current State Analysis
|
|
19
|
+
|
|
20
|
+
### Backend Route Structure
|
|
21
|
+
| Module | Current Mount | Project Param |
|
|
22
|
+
|--------|--------------|---------------|
|
|
23
|
+
| `chat.ts` | `/api/chat/...` | body/query (`projectName`, `dir`) |
|
|
24
|
+
| `git.ts` | `/api/git/...` | mixed: URL param (`:project`) for GET, body (`project`) for POST |
|
|
25
|
+
| `files.ts` | `/api/files/...` | URL param (`:project`) |
|
|
26
|
+
| `projects.ts` | `/api/projects` | N/A (lists all) |
|
|
27
|
+
| terminal WS | `/ws/terminal/:id?project=` | query param |
|
|
28
|
+
| chat WS | `/ws/chat/:sessionId` | none |
|
|
29
|
+
|
|
30
|
+
### Frontend API Calls (all files)
|
|
31
|
+
- `api-client.ts` -- generic fetch wrapper, no project prefix
|
|
32
|
+
- `chat-tab.tsx` -- `/api/chat/sessions` POST
|
|
33
|
+
- `session-picker.tsx` -- `/api/chat/sessions` GET, DELETE
|
|
34
|
+
- `use-chat.ts` -- `/api/chat/sessions/:id/messages` GET, `/ws/chat/:id` WS
|
|
35
|
+
- `git-graph.tsx` -- `/api/git/graph/:project`, `/api/git/*` POST with body.project
|
|
36
|
+
- `git-status-panel.tsx` -- `/api/git/status/:project`, POST with body.project
|
|
37
|
+
- `file-store.ts` -- `/api/files/tree/:project`
|
|
38
|
+
- `code-editor.tsx` -- `/api/files/read/:project`, `/api/files/write/:project`
|
|
39
|
+
- `file-actions.tsx` -- `/api/files/create/:project`, etc.
|
|
40
|
+
- `diff-viewer.tsx` -- `/api/files/compare/:project`, `/api/git/file-diff/:project`
|
|
41
|
+
- `use-terminal.ts` -- `/ws/terminal/:id?project=`
|
|
42
|
+
|
|
43
|
+
### Tab Store
|
|
44
|
+
- Single global `ppm-tabs` key in localStorage
|
|
45
|
+
- No project awareness; all tabs shared across projects
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Target Architecture
|
|
50
|
+
|
|
51
|
+
### New URL Pattern
|
|
52
|
+
```
|
|
53
|
+
/api/project/:projectName/chat/...
|
|
54
|
+
/api/project/:projectName/git/...
|
|
55
|
+
/api/project/:projectName/files/...
|
|
56
|
+
/ws/project/:projectName/terminal/:id
|
|
57
|
+
/ws/project/:projectName/chat/:sessionId
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`/api/projects` and `/api/auth/*` and `/api/health` stay global (no project scope).
|
|
61
|
+
|
|
62
|
+
### Key Design Decisions
|
|
63
|
+
|
|
64
|
+
1. **Hono middleware extracts `:projectName`** -- single `projectRouter` sub-app with middleware that resolves project path once, sets it on context. All child routes read from context instead of calling `resolveProjectPath` themselves.
|
|
65
|
+
2. **Git POST routes drop `project` from body** -- project comes from URL; body only carries action-specific data.
|
|
66
|
+
3. **Chat routes get project from URL** -- no more `dir` query or `projectName` body field.
|
|
67
|
+
4. **Tab store keyed by project name** -- `ppm-tabs-{projectName}` in localStorage. On project switch, store swaps persisted state.
|
|
68
|
+
5. **ApiClient gets `projectUrl(name)` helper** -- returns `/api/project/${encodeURIComponent(name)}` prefix; all feature calls use it.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Phases
|
|
73
|
+
|
|
74
|
+
- [Phase 1: Backend project-scoped router](./phase-01-backend-project-router.md) -- 2h
|
|
75
|
+
- [Phase 2: Frontend API client + calls](./phase-02-frontend-api-migration.md) -- 2h
|
|
76
|
+
- [Phase 3: Per-project tab storage](./phase-03-per-project-tabs.md) -- 1.5h
|
|
77
|
+
- [Phase 4: WebSocket URL migration](./phase-04-websocket-migration.md) -- 0.5h
|
|
78
|
+
|
|
79
|
+
## Dependencies
|
|
80
|
+
- Phase 2 depends on Phase 1 (new routes must exist)
|
|
81
|
+
- Phase 3 is independent (can parallel with Phase 2)
|
|
82
|
+
- Phase 4 depends on Phase 1 (WS upgrade paths change in server index)
|
|
83
|
+
|
|
84
|
+
## Risk Assessment
|
|
85
|
+
- **Breaking change**: All frontend API calls change. No backward compat needed since this is a fresh v2 branch.
|
|
86
|
+
- **Chat sessions are currently global**: After refactor, sessions filter by project via URL. Existing sessions in storage may lack project association -- migration not needed since v2-fresh-start has no prod data.
|
|
87
|
+
- **Terminal WS path change**: Must update both foreground and daemon `Bun.serve()` blocks in `src/server/index.ts`.
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# PPM Final Tech Stack Decision
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-03-14
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Project Overview
|
|
9
|
+
|
|
10
|
+
PPM (Personal Project Manager) — mobile-first web-based tool to manage code projects from phone/browser. Runs as CLI, serves web UI, deployable to VPS or accessible via Tailscale.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Requirements
|
|
15
|
+
|
|
16
|
+
### Core
|
|
17
|
+
|
|
18
|
+
- **Mobile-first UI:** Ưu tiên mobile UX, có thể làm việc hoàn toàn từ điện thoại
|
|
19
|
+
- **CLI gateway:** Chạy background như daemon, serve web UI (giống openclaw pattern)
|
|
20
|
+
- **Tab system:** Mọi thứ mở trong tab giống VSCode — terminal, chat, editor, git graph, git diff, settings
|
|
21
|
+
- **PWA:** Installable trên điện thoại, offline UI shell caching
|
|
22
|
+
|
|
23
|
+
### Project Management
|
|
24
|
+
|
|
25
|
+
- **Project list:** Xem các folder project đã add vào
|
|
26
|
+
- **Auto-scan:** Khi `ppm init`, quét folder có `.git` và gợi ý add vào
|
|
27
|
+
- **File explorer:** Duyệt file tree trong project
|
|
28
|
+
- CRUD: tạo, xóa, rename, move files/folders
|
|
29
|
+
- Select 2 files để compare (diff) giống VSCode
|
|
30
|
+
- Drag & drop support (nice-to-have)
|
|
31
|
+
|
|
32
|
+
### Code Editor
|
|
33
|
+
|
|
34
|
+
- **Syntax highlighting:** Xem và sửa code trong browser
|
|
35
|
+
- **Autocomplete:** Không bắt buộc nhưng dùng CM6 built-in autocomplete nếu có sẵn
|
|
36
|
+
- **File compare:** Mở diff view khi select 2 files từ explorer
|
|
37
|
+
|
|
38
|
+
### Git Integration
|
|
39
|
+
|
|
40
|
+
- **Git graph:** Visualize commit history — giống [vscode-git-graph](https://github.com/mhutchie/vscode-git-graph)
|
|
41
|
+
- **Rendering:** SVG-based (same approach as vscode-git-graph: SVG paths for branch lines, SVG circles for commit nodes, color-coded lanes)
|
|
42
|
+
- **Data:** `git log --format=<custom>` + `git branch` + `git for-each-ref` (parsed server-side, sent as JSON)
|
|
43
|
+
- **Context menu actions on commits:** Checkout, Cherry Pick, Revert, Create Branch, Create Tag, Copy Hash
|
|
44
|
+
- **Context menu actions on branches:** Checkout, Merge into current, Delete, Rename, Push, Pull, Rebase
|
|
45
|
+
- **PR:** Mở browser URL tới GitHub/GitLab create PR page (không dùng `gh` CLI)
|
|
46
|
+
- **Branch visualization:** Lane allocation algorithm — mỗi branch 1 lane, color rotation, merge/fork lines
|
|
47
|
+
- **Git diff:** Xem thay đổi giữa commits/branches (CodeMirror 6 merge view)
|
|
48
|
+
- **Git status panel:**
|
|
49
|
+
- Xem files changed (modified, added, deleted)
|
|
50
|
+
- Xem files staged vs unstaged
|
|
51
|
+
- Stage/unstage individual files
|
|
52
|
+
- Commit input box + commit button
|
|
53
|
+
- Push/pull buttons
|
|
54
|
+
- **Git blame:** (nice-to-have) inline blame annotations
|
|
55
|
+
|
|
56
|
+
### CLI (AI/Automation-friendly)
|
|
57
|
+
|
|
58
|
+
- **Mục đích:** CLI cho AI agents và automation điều khiển PPM, không cần CLI cho mọi feature
|
|
59
|
+
- **Project resolution:** CWD auto-detect (nếu trong registered project) + `-p <name>` flag override
|
|
60
|
+
```bash
|
|
61
|
+
ppm git status # auto-detect từ CWD
|
|
62
|
+
ppm git status -p my-app # explicit project
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**CLI commands (chỉ những gì AI cần):**
|
|
66
|
+
- `ppm init` — onboarding wizard
|
|
67
|
+
- `ppm start [-d] [-c config.yaml]` / `ppm stop` — server lifecycle
|
|
68
|
+
- `ppm open` — mở browser tới web UI
|
|
69
|
+
- `ppm projects list|add|remove`
|
|
70
|
+
- `ppm config get|set`
|
|
71
|
+
- `ppm git status|log|diff` — đọc git state
|
|
72
|
+
- `ppm git stage|unstage|commit|push|pull` — git write actions
|
|
73
|
+
- `ppm git branch create|checkout|delete|merge`
|
|
74
|
+
- `ppm chat create|list|resume|delete|send` — AI-to-AI orchestration
|
|
75
|
+
|
|
76
|
+
**Không cần CLI (web-only hoặc đã có native CLI):**
|
|
77
|
+
- File operations → AI dùng `ls`, `cat`, `mv`, `rm` trực tiếp
|
|
78
|
+
- Terminal management → web-only feature
|
|
79
|
+
- Git graph visual → web-only
|
|
80
|
+
- Cherry-pick, revert, tag, rebase → AI dùng `git` CLI trực tiếp
|
|
81
|
+
|
|
82
|
+
### Terminal
|
|
83
|
+
|
|
84
|
+
- **Web terminal:** Mở terminal thật trong tab, tương tác đầy đủ với shell
|
|
85
|
+
|
|
86
|
+
### AI Chat
|
|
87
|
+
|
|
88
|
+
- **Claude Code integration:** Tương tác giống hệt Claude Code extension trên VSCode
|
|
89
|
+
- **Multi-provider:** Generic interface, bắt đầu với Claude, mở rộng sang provider khác sau
|
|
90
|
+
- **Session management:** CRUD sessions — tạo, xem, resume, xóa
|
|
91
|
+
- **Multi-tab chat:** Mở nhiều chat sessions cùng lúc trên nhiều tab
|
|
92
|
+
- **Tool approvals:** Hiển thị và xử lý tool permission requests trong UI
|
|
93
|
+
|
|
94
|
+
### Deployment
|
|
95
|
+
|
|
96
|
+
- **Local:** `ppm init` → onboarding wizard → tạo config → `ppm start`
|
|
97
|
+
- **VPS:** Copy binary + config file → `ppm start -c config.yaml` (không cần onboarding)
|
|
98
|
+
- **Tailscale:** Chạy local, truy cập từ xa qua Tailscale IP
|
|
99
|
+
- **Single binary:** Deploy dễ dàng, không cần runtime
|
|
100
|
+
|
|
101
|
+
### Open Source
|
|
102
|
+
|
|
103
|
+
- **OSS-friendly:** MIT/Apache 2.0 license
|
|
104
|
+
- **Cross-platform:** Build binaries cho Linux, macOS (ARM + x64)
|
|
105
|
+
- **Extensible:** Community có thể contribute AI providers, themes, plugins
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Tech Stack
|
|
110
|
+
|
|
111
|
+
### Backend
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
| Component | Technology | Version | Why |
|
|
115
|
+
| -------------- | ---------------------------------- | ------- | ------------------------------------------------------------------------------------------ |
|
|
116
|
+
| Runtime | **Bun** | 1.2+ | Single binary (`bun build --compile`), native TS, fast startup (~30ms), built-in WebSocket |
|
|
117
|
+
| HTTP Framework | **Hono** | 4.x | 14KB, fastest TS framework, runtime-agnostic |
|
|
118
|
+
| CLI | **Commander.js** | 13.x | Simple, proven CLI framework |
|
|
119
|
+
| Config | **js-yaml** | 4.x | Parse ppm.yaml config |
|
|
120
|
+
| PTY (terminal) | **node-pty** | 1.x | Spawn real shell processes, battle-tested |
|
|
121
|
+
| Git | **simple-git** | 3.x | Wrapper over git binary, simple API |
|
|
122
|
+
| AI Chat | **@anthropic-ai/claude-agent-sdk** | latest | Claude Code as library — first provider |
|
|
123
|
+
| WebSocket | **Bun built-in** | — | No extra dependency needed |
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
### Frontend
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
| Component | Technology | Version | Why |
|
|
130
|
+
| ---------- | -------------------------- | ------- | -------------------------------------------- |
|
|
131
|
+
| Framework | **React** | 19.x | Largest ecosystem, Agent SDK demos use React |
|
|
132
|
+
| Build | **Vite** | 6.x | Fast HMR, tree-shaking, code splitting |
|
|
133
|
+
| Styling | **Tailwind CSS** | 4.x | Mobile-first utility classes |
|
|
134
|
+
| Components | **shadcn/ui** | latest | Accessible, customizable, Radix UI based |
|
|
135
|
+
| State | **zustand** | 5.x | Lightweight (~1KB), simple API |
|
|
136
|
+
| Editor | **CodeMirror 6** | 6.x | Mobile-first (300KB), modular, touch support |
|
|
137
|
+
| Terminal | **xterm.js** | 5.x | Industry standard (VSCode uses it) |
|
|
138
|
+
| Git Diff | **diff2html** | 3.x | Parse unified diff → HTML |
|
|
139
|
+
| Git Graph | **Custom SVG** | — | No good maintained lib, build with basic SVG |
|
|
140
|
+
| PWA | **vite-plugin-pwa** | latest | Service worker, offline shell, installable |
|
|
141
|
+
| Panels | **react-resizable-panels** | latest | Split panes for tab layout |
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
### Infrastructure
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
| Component | Technology | Why |
|
|
148
|
+
| ----------------- | ------------------------- | ------------------------------------------------- |
|
|
149
|
+
| Deploy (binary) | `bun build --compile` | Single executable, copy to VPS |
|
|
150
|
+
| Deploy (fallback) | Docker | If native addon (node-pty) fails with bun compile |
|
|
151
|
+
| Background daemon | Built-in (detach process) | `ppm start -d` |
|
|
152
|
+
| Remote access | Tailscale / direct VPS | Zero config networking |
|
|
153
|
+
| Auth | Simple token header | Extensible to OAuth later |
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Architecture
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
ppm (single binary)
|
|
162
|
+
│
|
|
163
|
+
├── CLI Layer (Commander.js) — calls same Service Layer as API
|
|
164
|
+
│ │ Project resolution: CWD auto-detect + `-p <name>` override
|
|
165
|
+
│ ├── ppm init / start / stop / open
|
|
166
|
+
│ ├── ppm projects list|add|remove
|
|
167
|
+
│ ├── ppm config get|set
|
|
168
|
+
│ ├── ppm git status|log|diff|stage|unstage|commit|push|pull
|
|
169
|
+
│ ├── ppm git branch create|checkout|delete|merge
|
|
170
|
+
│ └── ppm chat create|list|resume|delete|send
|
|
171
|
+
│
|
|
172
|
+
├── Server Layer (Hono + Bun) — thin wrapper over Service Layer
|
|
173
|
+
│ ├── GET / # Serve React SPA (embedded)
|
|
174
|
+
│ ├── GET /api/projects # List managed projects
|
|
175
|
+
│ ├── CRUD /api/files/* # File tree + read/write/create/delete/rename/move
|
|
176
|
+
│ ├── GET /api/git/* # Git status, log, diff, graph
|
|
177
|
+
│ ├── POST /api/git/* # Stage, unstage, commit, push, pull, branch ops
|
|
178
|
+
│ ├── WS /ws/terminal/:id # PTY sessions (xterm.js ↔ node-pty)
|
|
179
|
+
│ └── WS /ws/chat/:id # AI chat sessions (streaming)
|
|
180
|
+
│
|
|
181
|
+
├── Service Layer (shared business logic)
|
|
182
|
+
│ ├── project.service.ts # Project CRUD
|
|
183
|
+
│ ├── file.service.ts # File operations
|
|
184
|
+
│ ├── git.service.ts # All git operations (simple-git)
|
|
185
|
+
│ ├── terminal.service.ts # PTY management
|
|
186
|
+
│ └── chat.service.ts # AI provider sessions
|
|
187
|
+
│
|
|
188
|
+
├── AI Provider Layer (generic)
|
|
189
|
+
│ ├── provider.interface.ts # AIProvider interface
|
|
190
|
+
│ ├── claude-agent-sdk.ts # Claude adapter (first)
|
|
191
|
+
│ ├── cli-subprocess.ts # Generic CLI adapter (future: Gemini, Aider...)
|
|
192
|
+
│ └── registry.ts # Provider registry + factory
|
|
193
|
+
│
|
|
194
|
+
├── Terminal Manager
|
|
195
|
+
│ └── node-pty pool ↔ WebSocket bridge
|
|
196
|
+
│
|
|
197
|
+
├── Git Service (simple-git)
|
|
198
|
+
│ └── status, log, diff, graph data extraction
|
|
199
|
+
│
|
|
200
|
+
└── Config (js-yaml)
|
|
201
|
+
└── ppm.yaml
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### AI Provider Interface (generic, multi-provider ready)
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface AIProvider {
|
|
208
|
+
id: string;
|
|
209
|
+
name: string;
|
|
210
|
+
createSession(config: SessionConfig): Promise<Session>;
|
|
211
|
+
resumeSession(sessionId: string): Promise<Session>;
|
|
212
|
+
listSessions(): Promise<SessionInfo[]>;
|
|
213
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
214
|
+
sendMessage(sessionId: string, message: string): AsyncIterable<ChatEvent>;
|
|
215
|
+
onToolApproval?: (callback: ToolApprovalHandler) => void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
type ChatEvent =
|
|
219
|
+
| { type: 'text'; content: string }
|
|
220
|
+
| { type: 'tool_use'; tool: string; input: any }
|
|
221
|
+
| { type: 'tool_result'; output: string }
|
|
222
|
+
| { type: 'approval_request'; tool: string; input: any }
|
|
223
|
+
| { type: 'error'; message: string }
|
|
224
|
+
| { type: 'done'; sessionId: string }
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Frontend Tab System
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
┌─────────────────────────────────────────────┐
|
|
231
|
+
│ [Projects] [Terminal 1] [Chat: main] [app.ts]│
|
|
232
|
+
├─────────────────────────────────────────────┤
|
|
233
|
+
│ Active Tab Content │
|
|
234
|
+
│ (lazy-loaded per tab type) │
|
|
235
|
+
└─────────────────────────────────────────────┘
|
|
236
|
+
|
|
237
|
+
Tab types: projects | terminal | chat | editor | git-graph | git-diff | settings
|
|
238
|
+
Each tab = { id, type, title, metadata, component }
|
|
239
|
+
State managed by zustand
|
|
240
|
+
Mobile: scrollable tab bar, swipe gestures
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## Config File (ppm.yaml)
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
port: 8080
|
|
249
|
+
host: 0.0.0.0
|
|
250
|
+
auth:
|
|
251
|
+
enabled: true
|
|
252
|
+
token: "your-secret-token"
|
|
253
|
+
projects:
|
|
254
|
+
- path: /home/user/project-a
|
|
255
|
+
name: Project A
|
|
256
|
+
- path: /home/user/project-b
|
|
257
|
+
name: Project B
|
|
258
|
+
ai:
|
|
259
|
+
default_provider: claude
|
|
260
|
+
providers:
|
|
261
|
+
claude:
|
|
262
|
+
type: agent-sdk
|
|
263
|
+
api_key_env: ANTHROPIC_API_KEY # Read from env var
|
|
264
|
+
# Future:
|
|
265
|
+
# gemini:
|
|
266
|
+
# type: cli
|
|
267
|
+
# command: gemini
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Key Design Decisions
|
|
273
|
+
|
|
274
|
+
1. **Bun over Go:** Agent SDK is TypeScript-only → single runtime, no sidecar complexity
|
|
275
|
+
2. **Bun over Node.js:** `bun build --compile` for single binary, 3-5x faster startup
|
|
276
|
+
3. **Hono over Express:** 14KB vs 200KB+, faster, modern API
|
|
277
|
+
4. **CodeMirror 6 over Monaco:** Mobile-first (300KB vs 5-10MB), touch native
|
|
278
|
+
5. **simple-git over go-git:** Same runtime, proven wrapper, basic ops sufficient
|
|
279
|
+
6. **Provider adapter pattern:** Start with Claude, extend to any AI tool later
|
|
280
|
+
7. **zustand over Redux:** 1KB, zero boilerplate, perfect for tab state
|
|
281
|
+
8. **PWA:** Installable on phone, offline UI shell caching
|
|
282
|
+
9. **WebSocket only (no Socket.IO, no SSE):** Terminal requires bidirectional binary; chat needs bidirectional for tool approvals; Bun has WS built-in = zero deps, one protocol for everything
|
|
283
|
+
10. **Open-source:** MIT/Apache 2.0 license, CI/CD for cross-platform binaries, contributor-friendly stack
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Risk Register
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
| Risk | Impact | Mitigation |
|
|
291
|
+
| ----------------------------------------- | ------------------------ | ---------------------------------------------------------------- |
|
|
292
|
+
| node-pty fails with `bun build --compile` | Can't ship single binary | Fallback: Docker container or Node.js runtime |
|
|
293
|
+
| Agent SDK incompatible with Bun | Chat feature broken | Test early (Phase 1). Fallback: run SDK in Node.js child process |
|
|
294
|
+
| CodeMirror 6 keyboard issues on iOS | Bad mobile editing UX | Test early on real device. CM6 known to work well on mobile |
|
|
295
|
+
| WebSocket drops on mobile network | Lost terminal/chat state | Auto-reconnect logic + session resume |
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Realtime Communication
|
|
301
|
+
|
|
302
|
+
**Decision: WebSocket only** (no Socket.IO, no SSE)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
| Connection | Direction | Protocol |
|
|
306
|
+
| ------------------------------------ | --------------------- | --------------------- |
|
|
307
|
+
| Terminal (xterm.js ↔ PTY) | Bidirectional, binary | WS `/ws/terminal/:id` |
|
|
308
|
+
| AI Chat (streaming + tool approvals) | Bidirectional | WS `/ws/chat/:id` |
|
|
309
|
+
| File/git watcher (optional) | Server→Client | WS `/ws/events` |
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
**Why not Socket.IO:** +45KB client bundle, rooms/namespaces not needed, overkill.
|
|
313
|
+
**Why not SSE:** Terminal & chat both need bidirectional. Mixing SSE+HTTP POST = more complexity than single WS.
|
|
314
|
+
**Why WebSocket:** Bun built-in (zero deps), one protocol for all realtime, binary frame support for terminal.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Open Source Considerations
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
| Item | Detail |
|
|
322
|
+
| -------------- | ----------------------------------------------------------------------- |
|
|
323
|
+
| License | MIT or Apache 2.0 |
|
|
324
|
+
| Config | `.env.example` + `ppm.yaml` schema validation |
|
|
325
|
+
| CI/CD | GitHub Actions — build binaries for linux-x64, darwin-arm64, darwin-x64 |
|
|
326
|
+
| Cross-platform | `bun build --compile --target=bun-{platform}-{arch}` |
|
|
327
|
+
| Plugin docs | Guide for contributing new AI providers |
|
|
328
|
+
| node-pty | Prebuilt binaries in CI for each platform |
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## References
|
|
334
|
+
|
|
335
|
+
- [Tech stack research](../reports/research-260314-1911-ppm-tech-stack.md)
|
|
336
|
+
- [Claude Code integration research](../reports/research-260314-1930-claude-code-integration.md)
|
|
337
|
+
- [Claude Agent SDK docs](https://platform.claude.com/docs/en/agent-sdk/overview)
|
|
338
|
+
- [Hono](https://hono.dev)
|
|
339
|
+
- [CodeMirror 6](https://codemirror.net)
|
|
340
|
+
- [xterm.js](https://xtermjs.org)
|
|
341
|
+
- [simple-git](https://github.com/steveukx/git-js)
|
|
342
|
+
|