feat: workbench shell 및 로드맵 업데이트

- workbench shell 앱 구조 추가 (nomadcode_workbench_shell.dart)
- 워크스페이스 홈 페이지 업데이트
- 클라이언트 앱 멀티앱 구조 지원
- Pubspec 의존성 업데이트
- 로드맵 마일스톤 및 페이즈 문서 업데이트
- agent-ops 도메인 rule 갱신 (core, mobile)
This commit is contained in:
toki 2026-05-31 19:25:36 +09:00
parent 092be9dad9
commit f769dd5f3c
14 changed files with 579 additions and 61 deletions

View file

@ -7,7 +7,7 @@ NomadCode는 IOP를 사용하는 개발 워크플로우 셸입니다. 사용자
## 현재 상태
- `services/core`는 health/readiness endpoint, task API, PostgreSQL persistence, goose migration, sqlc query 생성 구조, River job, IOP OpenAI-compatible Responses client 경로, 향후 A2A agent delegation scaffolding, Plane/Mattermost adapter stub을 포함합니다.
- `apps/client`는 NomadCode 제품 UI의 기준 Flutter 앱이며 모바일/데스크톱/선택적 web target, Firebase Messaging, local notifications, HTTP client 의존성을 포함합니다.
- `apps/client`는 NomadCode 제품 UI의 기준 Flutter 앱이며 모바일/데스크톱/선택적 web target, Firebase Messaging, local notifications, HTTP client 의존성을 포함합니다. 현재 top titlebar, 우측 activity rail, 우측 Agent dock panel을 가진 workbench shell scaffold가 들어가 있고, Agent dock은 공통 `agent_shell` package를 사용합니다.
- `apps/web`은 이전 React/Vite 운영 콘솔 scaffold였으며, Flutter-first 통합에 따라 완전히 폐기 및 제거되었습니다.
- `packages/contracts`는 향후 OpenAPI, protobuf, generated client, fixture, compatibility asset을 담을 공유 계약 공간입니다.
- 활성 계획과 Milestone 상태는 `agent-roadmap/`에서 관리합니다. README에는 현재 작업 상태를 복사하지 않고 진입 문서만 연결합니다.
@ -20,6 +20,8 @@ NomadCode는 제품과 워크플로우 계층을 소유합니다.
- Plane/Jira 스타일 work item intake, provider projection, comment, status update
- 사용자에게 노출되는 file change, diff, branch, commit, PR 흐름
- 모바일/데스크톱/선택적 web target의 Flutter project management surface
- top titlebar, 우측 activity rail, 중앙 content 전환, 우측 Agent dock으로 구성되는 workbench shell
- IOP 관리 page/tab에 외부 IOP console package 또는 widget을 선택적으로 mount하는 제품 조립 경계
- IOP 호출에 넘길 task metadata 구성
IOP는 실행과 최적화 계층을 소유합니다.
@ -80,7 +82,7 @@ flutter run
| 경로 | 역할 |
|------|------|
| `services/core/` | Go backend. orchestration, persistence, scheduling, HTTP API, 외부 service adapter를 담당합니다. |
| `apps/client/` | Flutter 기반 제품 UI source of truth, 모바일/데스크톱/선택적 web 클라이언트와 notification 연동 표면입니다. |
| `apps/client/` | Flutter 기반 제품 UI source of truth, workbench shell, 우측 Agent dock, 모바일/데스크톱/선택적 web 클라이언트와 notification 연동 표면입니다. |
| `apps/web/` | [Legacy/Retired] React/Vite 운영 콘솔 scaffold입니다. Flutter-first 클라이언트 통합에 따라 완전히 폐기 및 제거되었습니다. |
| `packages/contracts/` | 서비스와 클라이언트가 공유할 API/schema 계약 공간입니다. |
| `docs/` | 아키텍처와 운영 문서입니다. |
@ -106,6 +108,8 @@ README 같은 workspace-level 파일을 다룰 때는 아래 문서를 함께
IOP 경계가 걸린 작업은 같은 workspace에 sibling IOP repository가 있을 때 함께 확인합니다. NomadCode roadmap은 제품/워크플로우 범위의 기준 문서이고, IOP roadmap은 실행/runtime 범위의 기준 문서입니다.
`agent_shell`은 제품 표면이 아니라 공통 chat/agent shell package입니다. NomadCode는 이 package를 우측 Agent dock UI에 사용하고, IOP 자체 기능은 NomadCode 코드에 직접 복제하지 않고 별도 IOP console package/widget 경계로 조립하는 방향을 유지합니다.
## 개발 흐름
- 전체 workspace 작업은 가능한 한 root `bin/test`, `bin/lint`, `bin/build`, `bin/dev` entrypoint를 우선 사용합니다.

View file

@ -1,7 +1,7 @@
---
domain: core
last_rule_review_commit: cc00ac25fb862e6ff2d4269f0c78cf1278e10e69
last_rule_updated_at: 2026-05-23
last_rule_review_commit: 092be9dad94f22e497cf4c083681581ab52ffdc1
last_rule_updated_at: 2026-05-31
---
# core
@ -55,13 +55,15 @@ NomadCode의 백엔드 오케스트레이션 도메인이다. workflow, scheduli
- 원격 SSH의 기본 shell에서는 `docker`가 PATH에 없을 수 있으므로 `zsh -lc`로 실행한다. 예: `cd ~/docker/services/plane/compose && docker compose ps`.
- compose service 이름은 `plane-api`, `plane-worker`, `plane-beat`, `plane-frontend`, `postgres`, `redis`, `rabbitmq`, `minio`다. 실제 컨테이너 이름은 `plane-api`, `plane-worker`, `plane-beat`, `plane-frontend`, `plane-postgres`, `plane-redis`, `plane-rabbitmq`, `plane-minio`다.
- NomadCode dev Plane workspace slug는 `general`, workspace id는 `dadf050e-cd1e-4590-bc33-672511630841`, project id는 `a6beb42f-7a8a-410c-b50f-ea3ca94828f3`, project identifier는 `NOMAD`다.
- NomadCode project state id: Backlog `62d4c50c-0cea-4a76-a0ed-ec97498b2d5f`, Todo `45ba7449-f684-4381-af6d-5854747c5e8d`, In Progress `c6ac1a6b-74d5-47fb-8b36-646d2bf0284d`, Done `ea2e5b48-8bf1-4723-b749-de7723be41e9`, Cancelled `f29c06c2-d70c-4b56-a83c-fccc4db60ae4`.
- NomadCode project state id: Backlog `62d4c50c-0cea-4a76-a0ed-ec97498b2d5f`, Todo `45ba7449-f684-4381-af6d-5854747c5e8d`, In Progress `c6ac1a6b-74d5-47fb-8b36-646d2bf0284d`, User Review `dc4fb920-f763-4ae3-9569-3c5e2d500391`, Done `ea2e5b48-8bf1-4723-b749-de7723be41e9`, Cancelled `f29c06c2-d70c-4b56-a83c-fccc4db60ae4`.
- Plane API 호출은 repo root의 ignored local secret file `.env.plane.local`을 우선 사용한다. 이 파일은 `PLANE_BASE_URL`, `PLANE_TOKEN`, `PLANE_WORKSPACE_SLUG`, `PLANE_PROJECT_ID`를 담고 있으며 `.gitignore`로 제외된다. shell 작업에서는 `set -a; source .env.plane.local; set +a`로 로드한다.
- `.env.plane.local`의 현재 Plane token 계정은 NomadCode project에서 Admin role로 설정되어 있다.
- Plane API token이나 서버 `.env`의 secret 값은 domain rule에 직접 기록하지 않는다. 토큰 파일이 없거나 API 권한이 부족할 때만 서버 내부 조작이 명시적으로 요청되면 `plane-api`의 Django shell/ORM을 사용한다.
- Plane custom property는 work item types 기반 기능이며, 현재 NomadCode dev project의 `is_issue_type_enabled``False`다. 무료 self-hosted/free tier에서는 이 기능을 전제로 설계하지 않는다.
- Plane work item 샘플 `NOMAD-13``In Progress` state에 있으며, board state와 agent 내부 실행 상태를 분리하는 예시다. external source/id는 `nomadcode` / `sample-agent-in-progress-state`이고, 라벨 `agent:waiting-user`, `phase:planning`으로 내부 상태를 보드에서 보이게 한다.
- provider-neutral 상태 설계는 board state를 `backlog`, `todo`, `in_progress`, `testing`, `complete`, `cancel`로 두고, canonical agent 실행 상태는 core task metadata에 저장한다. provider에는 labels를 우선 투영하고, 자세한 사유는 comment로 남긴다.
- Plane control flow의 board state는 `backlog`, `todo`, `in_progress`, `user_review`, `done`, `cancelled`로 둔다. canonical agent 실행 상태는 core task metadata에 저장하고, provider에는 labels를 우선 투영하며, 자세한 사유는 comment로 남긴다.
- Plane `Todo + AGENT assignee`는 Milestone 초안 생성/갱신 gate이며 자동 실행 gate가 아니다. 사용자가 상위 티켓을 `In Progress`로 옮길 때 Milestone 하위 작업 생성과 실행을 시작한다.
- Milestone에서 만들어진 Plane 하위 티켓은 정상 PASS 시 기본적으로 `Done`으로 이동한다. 사용자 결정, 외부 환경, 범위 충돌처럼 자동 진행이 막힐 때만 `User Review`로 이동한다.
- Plane과 Jira 모두 provider workflow status는 가볍게 유지한다. agent 내부 실행 상태는 우선 labels로 투영하고, Jira에서는 필요 시 issue property/custom field를 보조 저장소로 쓸 수 있게 adapter 경계를 둔다.
## 유지할 패턴
@ -72,7 +74,7 @@ NomadCode의 백엔드 오케스트레이션 도메인이다. workflow, scheduli
- 환경 변수 기본값과 alias는 `internal/config`에서 관리하고 README의 실행 예시와 어긋나지 않게 유지한다.
- scheduler 변경은 가능한 한 job 단위 테스트를 추가한다.
- HTTP API 변경은 Flutter 앱 영향과 contracts 반영 필요성을 함께 판단한다.
- Plane 관련 작업에서는 board state와 agent 내부 실행 상태를 같은 workflow state로 섞지 않는다. `testing`은 agent가 자체 검증과 리뷰 루프를 끝낸 뒤 사용자 테스트를 기다리는 상태로 사용한다.
- Plane 관련 작업에서는 board state와 agent 내부 실행 상태를 같은 workflow state로 섞지 않는다. `User Review`는 사용자 판단이 필요한 provider board state이고, 세부 사유는 `agent_run_state`, `wait_type`, label, comment로 표현한다.
- Plane/Jira work item 본문(description)은 작업 요구사항과 맥락의 원본으로 취급하고, agent 실행 상태를 매번 갱신하는 저장소로 사용하지 않는다.
## 다른 도메인과의 경계

View file

@ -1,7 +1,7 @@
---
domain: mobile
last_rule_review_commit: e3b5dae725db62dd7a47c563abcce557e0557551
last_rule_updated_at: 2026-05-21
last_rule_review_commit: 092be9dad94f22e497cf4c083681581ab52ffdc1
last_rule_updated_at: 2026-05-31
---
# mobile
@ -13,7 +13,9 @@ NomadCode의 Flutter-first 제품 UI 도메인이다. 모바일/데스크톱/선
## 포함 경로
- `apps/client/lib/` — Flutter application code.
- `apps/client/lib/services/` — auth, notification, background handlers.
- `apps/client/lib/src/app/` — app host, bootstrap, workbench shell.
- `apps/client/lib/src/features/` — product feature UI and local feature state.
- `apps/client/lib/src/integrations/` — Mattermost, proto-socket, workspace launcher 같은 외부 경계 adapter.
- `apps/client/test/` — Flutter widget/service tests.
- `apps/client/android/`, `apps/client/ios/`, `apps/client/macos/`, `apps/client/linux/`, `apps/client/windows/`, `apps/client/web/` — platform-specific runner/config.
- `apps/client/pubspec.yaml`, `apps/client/pubspec.lock`, `apps/client/analysis_options.yaml` — Dart/Flutter dependencies and analysis.
@ -28,12 +30,15 @@ NomadCode의 Flutter-first 제품 UI 도메인이다. 모바일/데스크톱/선
## 주요 구성 요소
- `lib/main.dart` — Flutter app entrypoint.
- `lib/services/mattermost_auth_service.dart` — Mattermost auth flow.
- `lib/services/push_notification_service.dart` — foreground/background notification setup.
- `lib/services/push_notification_background.dart` — background message handler.
- `android/app/src/main/java/com/tokilabs/mattermost/MattermostFirebaseMessagingService.kt` — Android FCM 수신과 Flutter EventChannel bridge.
- `android/app/src/main/java/com/tokilabs/mattermost/NotificationReplyBroadcastReceiver.java` — Android inline reply 처리.
- `android/app/src/main/java/com/tokilabs/mattermost/helpers/` — Android notification, DB, network helper.
- `lib/src/app/nomadcode_client_app.dart` — MaterialApp host와 integration lifecycle wiring.
- `lib/src/app/nomadcode_workbench_shell.dart` — top titlebar, 우측 activity rail, 중앙 content 전환, 우측 Agent dock panel을 가진 workbench shell.
- `lib/src/features/workspaces/presentation/workspace_home_page.dart` — workspace project/task UI. workbench mount 시 `showAppBar: false`로 내부 app bar를 숨길 수 있다.
- `lib/src/integrations/mattermost/mattermost_auth_service.dart` — Mattermost auth flow.
- `lib/src/integrations/mattermost/mattermost_push_host_integration.dart` — Mattermost push host initialization, auth handoff, notification navigation.
- `lib/src/integrations/proto_socket/proto_socket_lifecycle.dart` — proto-socket connection lifecycle facade.
- `lib/src/integrations/proto_socket/proto_socket_task_service.dart` — workspace task loading over proto-socket transport.
- `lib/src/integrations/workspace/workspace_launcher.dart` — workspace/code-server launch boundary.
- `android/app/src/main/java/com/tokilabs/mattermost/MainActivity.kt` — Android Flutter activity host.
- `test/widget_test.dart` — baseline widget test.
## 유지할 패턴
@ -43,6 +48,8 @@ NomadCode의 Flutter-first 제품 UI 도메인이다. 모바일/데스크톱/선
- Android native push 변경은 Kotlin/Java receiver/service, manifest, Dart bridge 영향을 함께 확인한다.
- public API 호출을 추가하면 contracts/core 영향 여부를 확인한다.
- project/task/agent/workspace 제품 UI는 Flutter 앱을 기준 구현으로 둔다.
- NomadCode app shell은 top titlebar와 우측 activity rail을 가진 workbench 구조를 기준으로 한다. 우측 Agent dock은 rail에 붙는 panel로 열고 닫으며, 중앙 영역은 workspace/workflow/IOP/web context/settings 같은 product section을 전환한다.
- 공통 agent interaction UI는 sibling `agent_shell` package를 사용한다. NomadCode-specific workflow/web context capability는 NomadCode mobile domain이 소유하고, IOP-specific 운영 화면은 향후 IOP-owned package/widget을 선택적으로 mount한다.
- `flutter test`를 기본 검증으로 사용한다.
- 현재 baseline의 `avoid_print` info는 별도 cleanup 작업으로 다루고, 기능 변경과 섞지 않는다.
@ -50,6 +57,8 @@ NomadCode의 Flutter-first 제품 UI 도메인이다. 모바일/데스크톱/선
- **core**: mobile은 core API/notification 계약에 의존하되 core 내부 구현을 알지 않는다.
- **contracts**: mobile이 사용하는 API/event shape는 contracts 후보로 올린다.
- **agent-shell**: common chat/agent shell widget과 message model만 제공하는 sibling package다. Product routing, workbench section, web context 조작, IOP mount 결정은 mobile domain에서 조립한다.
- **IOP**: IOP는 runtime/operation/agent capability와 IOP console widget/package 경계를 소유한다. mobile은 IOP 기능을 직접 복제하지 않고 선택적 section으로 mount한다.
- **web**: [Legacy/Retired] React web 도메인은 완전히 제거되었습니다.
- **workspace-ops**: root scripts와 agent rules는 mobile 내부에 복제하지 않는다.
@ -58,5 +67,7 @@ NomadCode의 Flutter-first 제품 UI 도메인이다. 모바일/데스크톱/선
- Android/iOS/macOS 권한 변경을 Dart service 변경 없이 단독으로 넣지 않는다.
- Android native notification helper 변경을 FCM service와 reply/dismiss receiver 영향 검토 없이 단독으로 넣지 않는다.
- Firebase 초기화/handler 변경을 테스트나 수동 검증 메모 없이 마무리하지 않는다.
- IOP client UI나 runtime/operation semantics를 NomadCode mobile code에 직접 복제하지 않는다.
- `agent_shell` package에 NomadCode 전용 workflow/web context/IOP product logic을 밀어 넣지 않는다.
- generated/platform build output을 의도 없이 커밋하지 않는다.
- mobile 내부에 별도 `agent-ops` scaffold를 다시 만들지 않는다.

View file

@ -6,6 +6,8 @@
- 경로: `agent-roadmap/phase/workflow-core/PHASE.md`
- [진행중] External Integration
- 경로: `agent-roadmap/phase/external-integration/PHASE.md`
- [계획] Project Workspace Management UX
- 경로: `agent-roadmap/phase/project-workspace-management-ux/PHASE.md`
## 활성 Milestone
@ -21,6 +23,9 @@
- [계획] External Integration
- Phase: `agent-roadmap/phase/external-integration/PHASE.md`
- 경로: `agent-roadmap/phase/external-integration/milestones/external-integration.md`
- [계획] Workbench Shell and Optional IOP Composition
- Phase: `agent-roadmap/phase/project-workspace-management-ux/PHASE.md`
- 경로: `agent-roadmap/phase/project-workspace-management-ux/milestones/workbench-shell-optional-iop-composition.md`
## 선택 규칙

View file

@ -23,7 +23,8 @@ Work Item Provider Pipeline Design과 workflow core 이후 남은 Plane/Jira 확
## 범위
- Plane issue 생성 / comment / status update 확장
- Plane work item 생성 / comment / status update 확장
- Roadmap Driven Agent-Ops Automation에서 정한 Plane 상위 티켓/Milestone, 하위 티켓/Task 제어 흐름을 실제 Plane adapter 경계로 확장
- Jira issue 조회 / comment / status transition adapter 구현
- Mattermost 메시지 발송 구현
- Agent Integrator 호출 구조 추가
@ -37,7 +38,8 @@ Work Item Provider Pipeline Design과 workflow core 이후 남은 Plane/Jira 확
외부 work item provider, 협업 도구, 실행 표면을 adapter 경계 안에서 실제 통합 흐름으로 확장한다.
- [ ] [plane-adapter-expand] Plane issue 생성, comment, status update adapter 확장. 검증: core가 Plane에 issue, comment, status update를 요청할 수 있다.
- [ ] [plane-adapter-expand] Plane work item 생성, comment, status update adapter 확장. 검증: core가 Plane에 work item, comment, status update를 요청할 수 있다.
- [ ] [plane-roadmap-control] Plane 상위 티켓을 Milestone, 하위 티켓을 Milestone 기능 Task로 투영하는 control flow adapter를 구현한다. 검증: `Todo + AGENT assignee`는 Milestone 초안 생성/갱신, `In Progress`는 하위 티켓 생성과 실행 시작, 하위 티켓 PASS는 기본 `Done`, blocker는 `User Review`, 상위 티켓 `Done`은 Milestone 완료/archive로 연결된다.
- [ ] [jira-adapter] Jira issue 조회, comment, status transition adapter 구현. 검증: core가 Jira에 issue 조회, comment, status transition을 요청할 수 있다.
- [ ] [mattermost-adapter] Mattermost 메시지 발송 adapter 구현과 `../nexo/packages/messaging_flutter` host notification boundary 정합성 유지. 검증: core가 Mattermost에 메시지를 발송하고, server-generated signed push smoke가 `agent-test/local/mattermost-server-generated-push-smoke.md` 기준으로 FCM/ACK/opened/reply/dismiss evidence를 남긴다.
- [ ] [agent-integrator] Agent Integrator 호출 경계 정의. 검증: Agent Integrator 또는 그 대체 연결 지점이 명확히 정의되어 있다.
@ -76,6 +78,7 @@ Work Item Provider Pipeline Design과 workflow core 이후 남은 Plane/Jira 확
- Mattermost 책임 경계: core는 Mattermost REST 메시지 발송과 task notification 발행을 담당하고, `../nexo/packages/messaging_flutter`는 client-side FCM 수신, signature 검증, ACK, notification display, opened-routing, inline reply, dismiss를 담당한다.
- Nexo host 정합성: NomadCode host는 Firebase 설정, Mattermost credential handoff, signing key, optional server identifier, navigation callback만 `MattermostPushClient` 경계로 전달한다. native push 처리 로직은 `apps/client/android`에 복제하지 않는다.
- 선행 순서: Mattermost 메시지/알림 경계 정합성은 `agent-roadmap/phase/external-integration/milestones/mattermost-nexo-messaging-alignment.md`에서 먼저 닫고, 이후 `[mattermost-adapter]`는 core의 Mattermost REST 메시지 발송 구현에 집중한다.
- Plane 제어 선행 기준: `Roadmap Driven Agent-Ops Automation`의 Plane control flow 계약이 먼저 닫힌 뒤 `[plane-roadmap-control]`에서 실제 Plane webhook/polling, 상위/하위 work item 생성, comment/status projection을 구현한다.
- 현재 반영 근거:
- `services/core/internal/adapters/openai/client.go`는 non-streaming `POST /v1/responses` 호출 경로를 사용한다.
- `services/core/cmd/server/main.go``MODEL_BASE_URL`, `MODEL_API_KEY`, `MODEL_NAME`, `MODEL_CONTEXT_SIZE`, `MODEL_TIMEOUT_SEC` 설정으로 OpenAI-compatible model client를 구성해 scheduler에 연결한다.

View file

@ -21,10 +21,15 @@ Flutter-first 클라이언트 구조 위에서 `apps/client` 모듈, web/mobile
- 경로: `agent-roadmap/phase/project-workspace-management-ux/milestones/project-workspace-management-ux.md`
- 요약: 프로젝트 단위 제어 UX의 desktop/mobile 역할, parity 기준, IOP/task 결과 표시 기준을 정리한다.
- [계획] Workbench Shell and Optional IOP Composition
- 경로: `agent-roadmap/phase/project-workspace-management-ux/milestones/workbench-shell-optional-iop-composition.md`
- 요약: 상단 titlebar와 우측 icon rail 기반 workbench를 만들고, IOP 관리 UI를 선택적 Flutter package composition으로 중앙 content에 삽입하거나 빼도 빌드 가능한 구조로 정리한다.
## Phase 경계
- 기존 Project/Session/Workspace 방향을 대체하지 않고 보강한다.
- 실제 앱 UX 구현과 상세 화면 작업은 공통 모듈, workflow/core 안정화, 외부 통합 기준이 먼저 정리된 뒤 후순위로 진행한다.
- IOP 내부 모델 라우팅, 외부 agent 추가 흐름, 상세 화면/API/DB schema 확정은 이 Phase의 결정 전 범위에서 제외한다.
- IOP 관리 화면 자체는 IOP package가 소유하고, NomadCode는 우측 rail host와 optional composition adapter만 소유한다.
- UX 책임 경계와 우선순위는 사용자 결정이 필요한 잠금 항목으로 유지한다.
- `../iop``../alt`로 복제할 내용은 제품 feature 전체가 아니라 client skeleton, integration boundary, bootstrap convention, 문서화된 handoff 기준으로 제한한다.

View file

@ -0,0 +1,94 @@
# Milestone: Workbench Shell and Optional IOP Composition
## 위치
- Roadmap: `agent-roadmap/ROADMAP.md`
- Phase: `agent-roadmap/phase/project-workspace-management-ux/PHASE.md`
## 목표
NomadCode Flutter 앱을 상단 titlebar와 우측 icon rail이 만드는 ㄱ자 workbench shell로 정리하고, rail item 선택에 따라 중앙 content가 바뀌는 구조를 만든다. IOP 관리는 optional feature composition으로 두어 포함 빌드에서는 IOP의 native Flutter 관리 UI가 그대로 중앙 content에 들어오고, 기본 빌드에서는 IOP 의존성 없이 NomadCode가 독립적으로 빌드되어야 한다.
## 상태
[계획]
## 구현 잠금
- 상태: 해제
- 결정 필요: 없음
## 범위
- NomadCode desktop/workbench shell의 상단 titlebar + 우측 icon rail + center content 구조
- Agent, Workflow, IOP 관리, Web Context, Settings 같은 도메인 rail item 선택과 중앙 content 전환
- 공통 agent shell package를 NomadCode agent와 IOP agent capability가 함께 사용할 수 있는 조립 경계
- IOP 포함 빌드와 IOP 제외 기본 빌드를 분리하는 optional composition package/app 경계
- IOP 관리 페이지가 IOP repository의 embeddable console package를 native Flutter widget으로 그대로 삽입하는 구조
- NomadCode가 독립 제품으로 움직일 때 IOP feature import 없이 빌드 가능한 구조
## 기능
### Epic: [workbench-shell] NomadCode right-rail workbench
NomadCode는 여러 도메인 관리 화면을 우측 icon rail과 중앙 content host로 전환하는 제품 shell을 소유한다.
- [ ] [titlebar] NomadCode shell은 상단 titlebar 영역을 제공하고, 앱/워크스페이스/현재 컨텍스트 상태를 이 영역에서 표현한다.
- [ ] [right-rail] NomadCode shell은 VS Code activity bar처럼 아이콘만 있는 우측 rail을 제공한다.
- [ ] [center-content] 우측 rail item 선택에 따라 중앙 content가 Agent, Workflow, IOP 관리, Web Context, Settings 등으로 전환된다.
- [ ] [layout-shape] 상단 titlebar와 우측 rail이 ㄱ자 구조를 만들고, 중앙 content는 host 도메인 화면을 침범 없이 표시한다. 검증: narrow/wide layout에서 titlebar, rail, center content가 겹치지 않는다.
### Epic: [agent-surface] Shared agent shell composition
채팅형 agent shell은 NomadCode와 IOP가 함께 쓰는 공통 package로 두고, 각 제품은 capability pack만 등록한다.
- [ ] [agent-shell-package] 메시지, tool call, 승인 UI, capability registry를 포함한 공통 agent shell package를 NomadCode 제품 shell에서 사용할 수 있다.
- [ ] [nomad-agent] NomadCode agent capability는 WebView 화면 관찰/조작, workflow/task/workspace, project context 같은 NomadCode 책임 범위만 제공한다.
- [ ] [iop-agent-slot] IOP 포함 빌드는 같은 agent shell registry에 IOP agent capability pack을 추가 등록할 수 있다.
### Epic: [optional-iop] Optional IOP feature composition
IOP 관리는 NomadCode default app에 직접 박지 않고 별도 composition layer로 격리한다.
- [ ] [default-build] 기본 `apps/client` 빌드는 IOP package를 import하지 않고 NomadCode 기능만으로 빌드된다. 검증: IOP package가 없는 환경에서도 기본 client build/analyze가 실패하지 않는다.
- [ ] [iop-build] IOP 포함 빌드는 별도 app/flavor/composition package에서 IOP console package를 import하고 우측 rail의 IOP item에 연결한다.
- [ ] [feature-adapter] `nomadcode_iop_feature` 또는 동등한 adapter는 NomadCode feature registry와 IOP console public API 사이만 연결한다.
- [ ] [dependency-boundary] IOP 의존 import는 optional composition layer에만 존재하고 NomadCode core shell/domain package로 새지 않는다. 검증: default app dependency graph에서 IOP package가 제외된다.
### Epic: [iop-embed] Native Flutter IOP management embed
NomadCode의 IOP 관리 탭은 IOP에서 사용하는 화면을 webview나 별도 앱이 아니라 native Flutter widget으로 중앙 content에 삽입한다.
- [ ] [iop-center-page] 우측 rail의 IOP item 선택 시 중앙 content에 IOP embeddable console이 표시된다.
- [ ] [same-ui] NomadCode에 포함된 IOP 관리 화면과 IOP standalone 앱의 관리 화면은 같은 IOP console package를 사용한다.
- [ ] [host-config] NomadCode는 IOP endpoint/auth/token reference/theme/navigation callback을 주입하고 IOP package 내부 state와 NomadCode 제품 state를 느슨하게 연결한다.
- [ ] [remove-cleanly] IOP feature를 제거해도 NomadCode Agent, Workflow, Web Context, Settings rail item과 중앙 content 전환은 유지된다.
## 완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 모든 기능 Task와 Task 안에 명시된 검증이 아직 충족되지 않았다.
- 리뷰 필요:
- [ ] 사용자가 완료 결과를 확인했다
- [ ] archive 이동을 승인했다
- 리뷰 코멘트: 없음
## 범위 제외
- IOP 관리 UI 자체 구현과 IOP Control Plane/Edge/Node 운영 책임
- IOP 내부 모델 라우팅, adapter/target 실행 정책, runtime 최적화 구현
- NomadCode default app에 IOP package를 직접 의존시키는 구조
- 런타임 동적 plugin framework 도입
- WebView를 통해 IOP 화면을 띄우는 대체 구현
## 작업 컨텍스트
- 관련 경로: `apps/client/`, `packages/contracts/`, `services/core/`
- 외부 관련 경로: `../iop/apps/client/`, `../iop/packages/`
- 표준선(선택): NomadCode shell은 상단 titlebar + 우측 icon rail + center content 구조를 소유한다.
- 표준선(선택): IOP 관리는 optional composition layer에서만 import하고, default build는 IOP 없이 빌드 가능해야 한다.
- 표준선(선택): Agent shell은 공통 package, NomadCode/IOP는 각각 capability pack으로 조립한다.
- 선행 작업: Client Integration Standardization, Project Workspace Management UX, IOP Embeddable Console Workbench
- 후속 작업: 실제 desktop/mobile project workspace 화면 구현, workflow/iop 관리 탭별 세부 content 구현
- 확인 필요: 후속 구현 계획에서 app/flavor/package 중 어떤 optional composition 방식을 택할지 확정한다.

View file

@ -7,7 +7,7 @@
## 목표
NomadCode가 로드맵을 중심으로 사용자 입력, 실행 상태, 출력 검토, 완료 승인까지 한 흐름에서 운영하도록 Roadmap Operations Control Plane의 방향을 정한다. NomadCode Core는 roadmap/workflow/action 상태 전이를 소유하고, 외부 에이전트는 MCP-first 제어 표면을 통해 Core를 호출하며, agent-ops 스킬은 문서 작성, 의미 해석, 변경 제안, MCP 호출 준비 계층으로 낮춘다.
NomadCode가 로드맵을 중심으로 사용자 입력, 실행 상태, 출력 검토, 완료 승인까지 한 흐름에서 운영하도록 Roadmap Operations Control Plane의 방향을 정한다. Plane은 사용자가 work item 상태를 움직이는 primary control UI이며, NomadCode Core는 Plane command를 검증해 roadmap/workflow/action side effect를 실행하고, agent-roadmap은 장기 원장으로 유지한다. 외부 에이전트는 MCP-first 제어 표면을 통해 Core를 호출하며, agent-ops 스킬은 문서 작성, 의미 해석, 변경 제안, MCP 호출 준비 계층으로 낮춘다.
## 상태
@ -28,6 +28,9 @@ NomadCode가 로드맵을 중심으로 사용자 입력, 실행 상태, 출력
- MCP-first 외부 agent 제어 표면 방향 정의
- HTTP API와 MCP의 역할 분리
- 사용자 입력, 출력 검토, 승인, 보완, archive 확인을 NomadCode 라인에서 처리하는 흐름 정의
- Plane 상위 티켓 1개를 Milestone 1개로 보고, Plane 하위 티켓을 Milestone 기능 Task로 투영하는 사용자 플로우 계약 정의
- Plane 상태 `Backlog`, `Todo`, `In Progress`, `User Review`, `Done`, `Cancelled`를 roadmap/agent-task lifecycle command로 해석하는 기준 정의
- `Todo + AGENT assignee`에서 Milestone 초안을 작성하고, `In Progress`에서 실제 실행과 하위 티켓 생성을 시작하는 gate 정의
- agent-ops roadmap skills를 작성/제안/MCP 호출 준비 계층으로 낮추는 방향 정의
- completion event, `USER_REVIEW.md`, 완료 리뷰, dependency lock을 Core action으로 다루는 기준 정의
@ -42,6 +45,12 @@ NomadCode 내부 core logic이 roadmap 기반 작업 운영을 소유하고, 외
- [ ] [mcp-tools] 외부 agent용 MCP tool 표면을 Core action wrapper로 정의하고 `dry_run`, `expected_revision`, `idempotency_key`, `actor`, `reason` 입력 원칙을 정한다.
- [ ] [http-role] HTTP API는 Flutter UI, webhook, internal integration용 표면으로 유지하고, 외부 agent 제어는 MCP-first로 여는 기준을 정한다.
- [ ] [review-gates] 사용자 입력, 출력 검토, `USER_REVIEW.md`, 완료 승인, archive 승인을 NomadCode workflow 안의 review gate로 처리하는 흐름을 정한다.
- [ ] [plane-control-flow] Plane을 primary control UI로 쓰는 lifecycle을 정리한다. 검증: `Backlog -> Todo -> In Progress -> User Review -> Done` 상태가 Milestone 초안, 실행 시작, 사용자 검토, 완료 archive로 어떻게 연결되는지 문서에서 일관되게 읽힌다.
- [ ] [plane-identity] Plane 상위 티켓을 Milestone, 하위 티켓을 Milestone 기능 Task로 매핑하는 id 계약을 정한다. 검증: Milestone id는 파일명으로 유지하고, Plane work item id는 외부 provider id로 보존하는 기준이 명확하다.
- [ ] [todo-draft-gate] `Todo + AGENT assignee`를 Milestone 초안 생성/갱신 gate로 정의한다. 검증: Todo 진입이 자동 실행이 아니라 사용자 검토용 Milestone 작성 단계임이 명확하다.
- [ ] [in-progress-exec-gate] 사용자가 Plane 상위 티켓을 `In Progress`로 옮길 때 실제 실행과 하위 티켓 전환을 시작하는 기준을 정한다. 검증: Milestone Task가 Plane 하위 티켓으로 생성되고 plan/code-review 루프로 들어가는 시점이 명확하다.
- [ ] [child-task-review-policy] Milestone에서 만들어진 하위 작업은 막혔을 때만 `User Review`로 보내고, 정상 PASS 시 기본적으로 `Done`으로 보내는 정책을 정한다. 검증: 하위 작업의 기본 완료 흐름과 blocker review 흐름이 구분된다.
- [ ] [milestone-review-loop] 모든 하위 작업이 완료되면 상위 Milestone 티켓을 `User Review`로 보내고, 사용자가 `Done`으로 옮기면 완료/archive, `Todo`로 되돌리면 보완 루프로 처리하는 기준을 정한다.
- [ ] [skill-adapter] agent-ops roadmap skills가 action owner가 아니라 semantic authoring/proposal layer와 MCP call preparation layer가 되도록 축소 기준을 정한다.
- [ ] [first-slice] 첫 구현 단위를 `roadmap.validate`, `roadmap.get_position`, `roadmap.ingest_completion_event` 중 어떤 순서로 자를지 정한다.
@ -59,16 +68,25 @@ NomadCode 내부 core logic이 roadmap 기반 작업 운영을 소유하고, 외
- IOP 내부 모델 라우팅, MCP/tool policy, output validation, RAG, context compression 구현
- 전체 Flutter UI 구현
- Plane/Jira/Mattermost provider projection 세부 구현
- Plane/Jira/Mattermost provider projection 세부 구현, Plane webhook/하위 티켓 생성 adapter 실제 구현
- 사용자 승인 없는 자동 archive 이동
- agentic-framework 공통 스킬의 실제 개편
## 작업 컨텍스트
- 관련 경로: `services/core/internal/workflow/`, `services/core/internal/scheduler/`, `services/core/internal/http/`, `packages/contracts/`, `agent-roadmap/`, `agent-ops/skills/common/update-roadmap/SKILL.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-ops/skills/common/code-review/SKILL.md`
- 표준선(선택): NomadCode Core는 roadmap/action 상태 전이의 source of truth이고, MCP는 Core를 외부 에이전트에게 여는 adapter다. agent-ops 스킬은 사용자의 자연어와 문서 초안을 Core/MCP 호출 입력으로 정리한다.
- 표준선(선택): Plane은 사용자의 primary control UI이고, Plane 상태 변화는 NomadCode Core가 검증해 실행하는 command로 본다. NomadCode Core는 roadmap/action side effect와 idempotency/revision 검증을 소유하고, agent-roadmap은 장기 원장으로 유지한다. agent-ops 스킬은 사용자의 자연어와 문서 초안을 Core/MCP 호출 입력으로 정리한다.
- 로컬 제어 표면 후보: `http://127.0.0.1:8080/v1`
- 선행 작업: Work Item Provider Pipeline Design, 로드맵 스킬 운영 복잡도 평가, Core/MCP-first 방향성 합의
- 후속 작업: Workflow Core, Roadmap Operations Control Plane 첫 구현 slice, MCP tool contract 작성
- 현재 지점: Mattermost Nexo Messaging Alignment가 완료되어 Workflow Core 재개 전 선행 정합성 작업이 닫혔다. 다음 실행 단위는 roadmap/action core 책임과 MCP-first 제어 표면을 구현 가능한 Core action 후보로 정리하는 것이다.
- 용어 경계: Plane `User Review`는 provider board state이고, agent-task `USER_REVIEW.md`는 자동 plan/code-review 루프를 멈추는 내부 stop artifact다. 둘은 모두 사용자 판단 신호지만 저장 위치와 트리거가 다르다.
- Plane 제어 플로우:
- `Backlog`: 아이디어/후보이며 아직 Milestone 확정이 아니다.
- `Todo + AGENT assignee`: NomadCode가 Plane 티켓 내용을 바탕으로 Milestone 초안을 생성하거나 갱신한다. 원본 사용자 요청은 Plane comment 히스토리로 보존하고, Milestone 본문에는 정제된 목표/범위/기능/작업 컨텍스트를 둔다.
- `In Progress`: 사용자가 Milestone 초안을 검토한 뒤 실행을 승인한 상태다. NomadCode는 Milestone 기능 Task를 Plane 하위 티켓으로 전환하고 `agent-task/m-<milestone-id>` plan/code-review 루프를 시작한다.
- 하위 티켓: Milestone에서 만들어진 작업은 정상 PASS 시 기본적으로 `Done`으로 이동한다. 작업 중 사용자 결정, 외부 환경, 범위 충돌처럼 자동 진행이 막힐 때만 `User Review`로 이동한다.
- `User Review`: 상위 Milestone 티켓은 모든 하위 작업이 완료되었거나 사용자 판단이 필요한 경우 이동한다. 사용자가 `Done`으로 옮기면 `[완료]`와 archive를 수행하고, `Todo`로 되돌리면 내부 Milestone을 `[진행중]`/보완 필요로 되돌려 추가 요구사항을 반영하는 루프로 처리한다.
- `Done`: 사용자 최종 완료 승인이다.
- `Cancelled`: 폐기 또는 중단 루트다.
- 확인 필요: 없음

View file

@ -7,7 +7,7 @@ The Flutter-first NomadCode client application.
The Flutter host application in this monorepo is standardized under `apps/client/`. This directory serves as the reference architecture for other projects (such as `../iop` and `../alt`) to replicate.
### Directory Layout
- **`lib/src/app/`**: Root application routing, bootstrap settings (`bootstrap.dart`), global configurations, and state setup.
- **`lib/src/app/`**: Root application routing, workbench shell, bootstrap settings (`bootstrap.dart`), global configurations, and state setup.
- **`lib/src/features/`**: Feature-specific UI surfaces, business logic, and local UI state. Keep layout decoupled from shared integration protocols.
- **`lib/src/integrations/`**: Adapters and lifecycle hooks for external system boundaries. Any connection initialization or platform-specific service logic should reside here under strict interfaces.
@ -17,6 +17,16 @@ The Flutter host application in this monorepo is standardized under `apps/client
---
## Workbench Shell
`lib/src/app/nomadcode_workbench_shell.dart` owns the current NomadCode client shell. It provides a top titlebar, a right-side activity rail, center content switching, and an Agent dock panel attached to the right rail.
The Agent dock uses the sibling `agent_shell` package as a product-neutral chat/agent surface. Workflow, Web Context, and optional IOP management areas are mounted as center content sections. IOP-specific UI should be composed through an IOP-owned package or widget boundary rather than copied into NomadCode client code.
`WorkspaceHomePage` can hide its own app bar through `showAppBar: false` when it is mounted inside the workbench shell.
---
## Bootstrap
NomadCode features a standardized, platform-safe bootstrap layer in `lib/src/app/bootstrap.dart`:

View file

@ -9,6 +9,7 @@ import '../integrations/proto_socket/proto_socket_lifecycle.dart';
import '../integrations/proto_socket/proto_socket_task_service.dart';
import '../integrations/workspace/workspace_launcher.dart';
import 'bootstrap.dart' show applyFullscreenMode;
import 'nomadcode_workbench_shell.dart';
class NomadCodeClientApp extends StatelessWidget {
final MattermostPushHostIntegration? mattermostHost;
@ -141,10 +142,13 @@ class _NomadCodeClientHomeState extends State<_NomadCodeClientHome> {
@override
Widget build(BuildContext context) {
return WorkspaceHomePage(
return NomadCodeWorkbenchShell(
workspace: WorkspaceHomePage(
showAppBar: false,
onLaunchCodeServer: (project) => _launcher.openCodeServer(project),
loadTasks: _taskLoader,
protoSocketLifecycle: widget.protoSocketLifecycle,
),
);
}
}

View file

@ -0,0 +1,343 @@
import 'package:agent_shell/agent_shell.dart';
import 'package:flutter/material.dart';
enum NomadWorkbenchSection { workspace, workflow, iop, webContext, settings }
class NomadCodeWorkbenchShell extends StatefulWidget {
final Widget workspace;
const NomadCodeWorkbenchShell({super.key, required this.workspace});
@override
State<NomadCodeWorkbenchShell> createState() =>
_NomadCodeWorkbenchShellState();
}
class _NomadCodeWorkbenchShellState extends State<NomadCodeWorkbenchShell> {
NomadWorkbenchSection _section = NomadWorkbenchSection.workspace;
bool _agentOpen = false;
void _select(NomadWorkbenchSection section) {
setState(() {
_section = section;
});
}
void _toggleAgent() {
setState(() {
_agentOpen = !_agentOpen;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF0F0F1A),
body: SafeArea(
child: Column(
children: [
_WorkbenchTitleBar(section: _section),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(child: _buildCenterContent()),
if (_agentOpen)
SizedBox(
width: _agentPanelWidth(context),
child: const _AgentDockPanel(),
),
_WorkbenchActivityRail(
selected: _section,
agentOpen: _agentOpen,
onSelect: _select,
onToggleAgent: _toggleAgent,
),
],
),
),
],
),
),
);
}
Widget _buildCenterContent() {
return switch (_section) {
NomadWorkbenchSection.workspace => widget.workspace,
NomadWorkbenchSection.workflow => const _WorkbenchPlaceholder(
icon: Icons.account_tree_outlined,
title: 'Workflow',
subtitle: 'Workflow management surface will mount here.',
),
NomadWorkbenchSection.iop => const _WorkbenchPlaceholder(
icon: Icons.hub_outlined,
title: 'IOP',
subtitle: 'Optional IOP console package will mount here when included.',
),
NomadWorkbenchSection.webContext => const _WorkbenchPlaceholder(
icon: Icons.web_asset_outlined,
title: 'Web Context',
subtitle: 'WebView observation and control context will mount here.',
),
NomadWorkbenchSection.settings => const _WorkbenchPlaceholder(
icon: Icons.tune,
title: 'Settings',
subtitle: 'Workspace and integration settings will mount here.',
),
};
}
double _agentPanelWidth(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
if (width < 760) return width - 64;
if (width < 1120) return 360;
return 420;
}
}
class _WorkbenchTitleBar extends StatelessWidget {
final NomadWorkbenchSection section;
const _WorkbenchTitleBar({required this.section});
@override
Widget build(BuildContext context) {
return Container(
height: 52,
decoration: const BoxDecoration(
color: Color(0xFF16162A),
border: Border(bottom: BorderSide(color: Color(0xFF252548))),
),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Row(
children: [
Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: Colors.deepPurpleAccent.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(8),
),
child: const Icon(
Icons.blur_on,
color: Colors.deepPurpleAccent,
size: 18,
),
),
const SizedBox(width: 12),
const Text(
'NomadCode Console',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 16,
),
),
const SizedBox(width: 12),
Container(width: 1, height: 22, color: const Color(0xFF303050)),
const SizedBox(width: 12),
Expanded(
child: Text(
_sectionLabel(section),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(color: Colors.white70, fontSize: 13),
),
),
],
),
);
}
}
class _WorkbenchActivityRail extends StatelessWidget {
final NomadWorkbenchSection selected;
final bool agentOpen;
final ValueChanged<NomadWorkbenchSection> onSelect;
final VoidCallback onToggleAgent;
const _WorkbenchActivityRail({
required this.selected,
required this.agentOpen,
required this.onSelect,
required this.onToggleAgent,
});
@override
Widget build(BuildContext context) {
return Container(
width: 60,
decoration: const BoxDecoration(
color: Color(0xFF111122),
border: Border(left: BorderSide(color: Color(0xFF252548))),
),
child: Column(
children: [
const SizedBox(height: 8),
_RailButton(
tooltip: 'Agent',
icon: Icons.smart_toy_outlined,
selected: agentOpen,
onPressed: onToggleAgent,
),
const Divider(color: Color(0xFF252548), height: 16),
_RailButton(
tooltip: 'Workspace',
icon: Icons.dashboard_outlined,
selected: selected == NomadWorkbenchSection.workspace,
onPressed: () => onSelect(NomadWorkbenchSection.workspace),
),
_RailButton(
tooltip: 'Workflow',
icon: Icons.account_tree_outlined,
selected: selected == NomadWorkbenchSection.workflow,
onPressed: () => onSelect(NomadWorkbenchSection.workflow),
),
_RailButton(
tooltip: 'IOP',
icon: Icons.hub_outlined,
selected: selected == NomadWorkbenchSection.iop,
onPressed: () => onSelect(NomadWorkbenchSection.iop),
),
_RailButton(
tooltip: 'Web Context',
icon: Icons.web_asset_outlined,
selected: selected == NomadWorkbenchSection.webContext,
onPressed: () => onSelect(NomadWorkbenchSection.webContext),
),
const Spacer(),
_RailButton(
tooltip: 'Settings',
icon: Icons.tune,
selected: selected == NomadWorkbenchSection.settings,
onPressed: () => onSelect(NomadWorkbenchSection.settings),
),
const SizedBox(height: 8),
],
),
);
}
}
class _RailButton extends StatelessWidget {
final String tooltip;
final IconData icon;
final bool selected;
final VoidCallback onPressed;
const _RailButton({
required this.tooltip,
required this.icon,
required this.selected,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
final color = selected ? Colors.cyanAccent : Colors.white60;
return Tooltip(
message: tooltip,
waitDuration: const Duration(milliseconds: 500),
child: SizedBox(
width: 56,
height: 48,
child: IconButton(
onPressed: onPressed,
icon: Icon(icon, color: color),
style: IconButton.styleFrom(
backgroundColor: selected
? Colors.cyanAccent.withValues(alpha: 0.08)
: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
);
}
}
class _AgentDockPanel extends StatelessWidget {
const _AgentDockPanel();
static const _messages = <AgentMessage>[
AgentMessage(
id: 'nomad-agent-intro',
role: AgentMessageRole.assistant,
text:
'NomadCode agent is docked to the right rail. Workflow, Web Context, and optional IOP capabilities can be registered here.',
),
];
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
color: Color(0xFF141426),
border: Border(left: BorderSide(color: Color(0xFF252548))),
),
child: const AgentShell(
messages: _messages,
placeholder: 'Ask the workspace agent',
),
);
}
}
class _WorkbenchPlaceholder extends StatelessWidget {
final IconData icon;
final String title;
final String subtitle;
const _WorkbenchPlaceholder({
required this.icon,
required this.title,
required this.subtitle,
});
@override
Widget build(BuildContext context) {
return Container(
color: const Color(0xFF0F0F1A),
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 460),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: Colors.cyanAccent, size: 40),
const SizedBox(height: 16),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white,
fontSize: 22,
fontWeight: FontWeight.w800,
),
),
const SizedBox(height: 8),
Text(
subtitle,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white54, fontSize: 14),
),
],
),
),
),
);
}
}
String _sectionLabel(NomadWorkbenchSection section) {
return switch (section) {
NomadWorkbenchSection.workspace => 'Workspace',
NomadWorkbenchSection.workflow => 'Workflow',
NomadWorkbenchSection.iop => 'IOP Management',
NomadWorkbenchSection.webContext => 'Web Context',
NomadWorkbenchSection.settings => 'Settings',
};
}

View file

@ -15,12 +15,14 @@ class WorkspaceHomePage extends StatefulWidget {
final Future<bool> Function(ProjectWorkspace)? onLaunchCodeServer;
final WorkspaceTaskLoader? loadTasks;
final ProtoSocketLifecycle? protoSocketLifecycle;
final bool showAppBar;
const WorkspaceHomePage({
super.key,
this.onLaunchCodeServer,
this.loadTasks,
this.protoSocketLifecycle,
this.showAppBar = true,
});
@override
@ -68,9 +70,19 @@ class _WorkspaceHomePageState extends State<WorkspaceHomePage> {
final theme = Theme.of(context);
final isLargeScreen = MediaQuery.of(context).size.width > 600;
final body = isLargeScreen
? _buildDualPaneLayout(theme)
: _buildSinglePaneLayout(theme);
return Scaffold(
backgroundColor: const Color(0xFF0F0F1A), // Sleek deep dark background
appBar: AppBar(
appBar: widget.showAppBar ? _buildAppBar() : null,
body: body,
);
}
PreferredSizeWidget _buildAppBar() {
return AppBar(
title: Row(
children: [
Container(
@ -106,10 +118,6 @@ class _WorkspaceHomePageState extends State<WorkspaceHomePage> {
},
),
],
),
body: isLargeScreen
? _buildDualPaneLayout(theme)
: _buildSinglePaneLayout(theme),
);
}

View file

@ -9,6 +9,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.59"
agent_shell:
dependency: "direct main"
description:
path: "../../../agent-shell"
relative: true
source: path
version: "0.1.0"
async:
dependency: transitive
description:

View file

@ -43,6 +43,10 @@ dependencies:
nexo_messaging:
path: ../../../nexo/packages/messaging_flutter
# Shared embeddable agent interaction shell.
agent_shell:
path: ../../../agent-shell
# proto-socket: shared protocol/transport library (local path)
proto_socket:
path: ../../../proto-socket/dart