diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..1c98004cf2a122675db14eb7710d0a95e3f95c38 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +LLM_API_BASE_URL=https://api.openai.com/v1 +LLM_API_KEY= +LLM_MODEL=gpt-4.1-mini + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e56767793b458c5f89d6219f76308260f2324caa --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* +!.env.example + +# local tool/cache state +.npm-cache/ +.claude/ + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000000000000000000000000000000000..8e6f93f2a05a71aedf303779bea863cb63559139 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,29 @@ + +# This is NOT the Next.js you know + +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. + + +# Agentic PM Demo (Codex) + +## Product + +Build an independent web demo called **Agentic PM Demo** (not a Codex plugin). + +The UI is split: + +- Left: chat workspace +- Right: structured IoT product-development work packages + +## MVP Rules + +1. Seed work packages from `agentic_pm_demo_codex_plans/data/work-packages.seed.json`. +2. Support commands: + - `@WorkPackageName ask ...` + - `@WorkPackageName plan ...` + - `@WorkPackageName change ...` + - `@WorkPackageName execute ...` +3. All `execute` tasks are simulated. +4. Every simulated output must include this disclaimer verbatim: + +> This is a simulated execution result generated for demo purposes. No real external tool, engineering review, certification approval, user database, image generation service, patent search, or test system was executed. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..e9a7fcc29bfdd67656ab15fa7f00495ffe4590e3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +RUN npm run build + +EXPOSE 3000 + +ENV PORT=3000 +CMD ["npm", "start"] + diff --git a/README.md b/README.md index 53213b39bb84d8da94a0ecea603d754882afd2e5..df91ef0358cb2754d7342676feba54b2048a2008 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,36 @@ colorTo: gray sdk: docker pinned: false license: mit -short_description: work with Agent to co-develope Hardware products +short_description: Agentic PM demo for hardware product development --- -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference +# Agentic PM Demo + +Two-column demo app: + +- Left: chat with an AI assistant +- Right: structured IoT product-development work packages + +## Local run + +```bash +npm install +npm run dev +``` + +Open `http://localhost:3000`. + +## Environment + +Copy `.env.example` to `.env.local` and set: + +- `LLM_API_BASE_URL` (OpenAI-compatible) +- `LLM_API_KEY` (optional; app runs in mock mode without it) +- `LLM_MODEL` + +## Docker (Hugging Face Spaces) + +```bash +docker build -t agentic-pm-demo . +docker run --rm -p 3000:3000 agentic-pm-demo +``` diff --git a/agentic_pm_demo_codex_plans/.gitignore b/agentic_pm_demo_codex_plans/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..2c25c7498306f3e76117ce0a732a43d5c29c32eb --- /dev/null +++ b/agentic_pm_demo_codex_plans/.gitignore @@ -0,0 +1,6 @@ +.next +node_modules +out +.env*.local +.npm-cache +npm-debug.log* diff --git a/agentic_pm_demo_codex_plans/AGENTS.md b/agentic_pm_demo_codex_plans/AGENTS.md new file mode 100644 index 0000000000000000000000000000000000000000..47f8dbd8422d50177cea472fce8d83ccb007b821 --- /dev/null +++ b/agentic_pm_demo_codex_plans/AGENTS.md @@ -0,0 +1,57 @@ +# AGENTS.md — Codex Instructions + +## Project + +Build an independent web demo called **Agentic PM Demo**. + +The application demonstrates an AI-native product management workflow for IoT product development. + +## Development Principles + +1. Keep the MVP small and working. +2. Build UI first with mock data. +3. Add LLM integration only after the local UI works. +4. All tool execution must be simulated in MVP. +5. Never imply simulated execution is real. +6. Keep data models explicit and typed. +7. Use OpenAI-compatible API format. +8. Make the app deployable to Hugging Face Spaces with Docker. + +## Required App Features + +1. Two-column layout: left chat, right work package board. +2. Seed work packages from `data/work-packages.seed.json`. +3. Support commands: + - `@WorkPackageName ask ...` + - `@WorkPackageName plan ...` + - `@WorkPackageName change ...` + - `@WorkPackageName execute ...` +4. For `ask`, do not change the board. +5. For `plan`, update tasks or next steps. +6. For `change`, update work package fields. +7. For `execute`, generate simulated outputs and append them to the selected package. +8. Every simulated output must show the disclaimer. + +## Recommended Implementation Order + +1. Create Next.js project scaffold. +2. Add static two-column layout. +3. Add seed work package board. +4. Add chat input and message history. +5. Add command parser. +6. Add local mock agent fallback. +7. Add LLM API route. +8. Add prompt contract and structured JSON parsing. +9. Add Dockerfile for Hugging Face Spaces. +10. Polish UX. + +## Avoid in MVP + +- No login. +- No database. +- No real external tool execution. +- No real patent search. +- No real certification validation. +- No real user-data reading. +- No real image generation. +- No complex drag-and-drop kanban. diff --git a/agentic_pm_demo_codex_plans/README.md b/agentic_pm_demo_codex_plans/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5a3f37cdbf17caf52f1dde5824817bfa5773a1bd --- /dev/null +++ b/agentic_pm_demo_codex_plans/README.md @@ -0,0 +1,61 @@ +# Agentic PM Demo — Codex Development Pack + +This pack contains Codex-ready planning documents for an independent **Agentic PM Demo** web app. + +## Product Direction + +Build an independent web demo, not a Codex plugin. Codex is used as the development assistant; the product itself is a web app. + +## Demo Concept + +A user chats with an AI assistant on the left side. The right side shows structured product-development work packages. The user can reference a work package and ask the AI to `ask`, `plan`, `change`, or `execute` its contents. + +## MVP Rule + +All `execute` tasks are simulated. The app must clearly label all execution outputs as fake/simulated. + +Default disclaimer: + +> This is a simulated execution result generated for demo purposes. No real external tool, engineering review, certification approval, user database, image generation service, patent search, or test system was executed. + +## Recommended Stack + +- Next.js +- React +- TypeScript +- Tailwind CSS +- shadcn/ui +- OpenAI-compatible LLM API +- Docker for Hugging Face Spaces + +## Documents + +- `AGENTS.md` +- `plans/01-product-vision-and-scope.md` +- `plans/02-ux-layout-and-interaction.md` +- `plans/03-work-package-data-model.md` +- `plans/04-work-package-catalog.md` +- `plans/05-reference-command-and-execution.md` +- `plans/06-llm-api-and-prompt-contract.md` +- `plans/07-technical-architecture-and-file-structure.md` +- `plans/08-local-run-and-huggingface-deployment.md` +- `prompts/work-package-system-prompt.md` +- `data/work-packages.seed.json` + +## Local UI Prototype + +This folder now also contains a small Next.js demo UI for the Agentic PM workspace. + +```bash +npm install +npm run dev +``` + +Open `http://localhost:3000` and try: + +```text +@SRS plan Break this into verification tasks. +@Design FMEA execute Generate a risk table for a tightening-quality IoT product. +``` + +The UX pattern is intentionally Cursor/Codex-like: chat stays on the left, work packages stay on the right, and every package section can be cited into the composer with `@Package#Context` references. diff --git a/agentic_pm_demo_codex_plans/app/globals.css b/agentic_pm_demo_codex_plans/app/globals.css new file mode 100644 index 0000000000000000000000000000000000000000..3621c938ee511da67b01b4a7c65f45a5a145b518 --- /dev/null +++ b/agentic_pm_demo_codex_plans/app/globals.css @@ -0,0 +1,1007 @@ +:root { + --page-bg: #f6f9fd; + --page-tint: rgba(85, 132, 214, 0.08); + --surface: rgba(255, 255, 255, 0.96); + --surface-strong: #ffffff; + --surface-muted: #eef4fb; + --surface-accent: #f4f8fd; + --ink: #18202b; + --muted: #637182; + --line: rgba(24, 32, 43, 0.1); + --line-strong: rgba(24, 32, 43, 0.2); + --accent: #3d6fd6; + --accent-soft: rgba(61, 111, 214, 0.12); + --success: #2f7a56; + --warning: #b97b18; + --shadow: 0 16px 36px rgba(42, 73, 122, 0.08); + --font-sans: "Avenir Next", "IBM Plex Sans", "Segoe UI", sans-serif; + --font-display: "Iowan Old Style", "Palatino Linotype", "Book Antiqua", serif; +} + +* { + box-sizing: border-box; +} + +html, +body { + height: 100%; + min-height: 100%; + overflow: hidden; +} + +body { + margin: 0; + background: + radial-gradient(circle at top left, rgba(255, 255, 255, 0.9), transparent 28rem), + linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(246, 249, 253, 0.98)), + var(--page-bg); + color: var(--ink); + font-family: var(--font-sans); + overscroll-behavior: none; +} + +body::before { + background: + linear-gradient(120deg, transparent 0%, var(--page-tint) 42%, transparent 72%), + repeating-linear-gradient( + 90deg, + transparent 0, + transparent 23px, + rgba(31, 27, 22, 0.018) 23px, + rgba(31, 27, 22, 0.018) 24px + ); + content: ""; + inset: 0; + pointer-events: none; + position: fixed; +} + +button, +textarea { + font: inherit; +} + +button { + cursor: pointer; +} + +svg { + display: block; + flex: 0 0 auto; + height: 1rem; + width: 1rem; +} + +.pm-shell { + display: grid; + gap: 12px; + grid-template-columns: minmax(280px, 320px) minmax(0, 1fr); + height: 100vh; + min-height: 760px; + overflow: hidden; + padding: 12px; + position: relative; +} + +.chat-rail, +.workspace-canvas, +.agent-log-zone { + backdrop-filter: blur(14px); + background: var(--surface); + border: 1px solid var(--line); + box-shadow: var(--shadow); +} + +.chat-rail { + border-radius: 12px; + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + min-height: 0; + overflow: hidden; +} + +.project-strip { + align-items: center; + border-bottom: 1px solid var(--line); + display: grid; + gap: 12px; + grid-template-columns: 52px 1fr; + padding: 12px; +} + +.project-mark { + align-items: center; + background: linear-gradient(145deg, #ffffff, #eef4fb); + border: 1px solid var(--line); + border-radius: 8px; + color: var(--accent); + display: flex; + height: 52px; + justify-content: center; +} + +.project-meta p, +.workspace-title p, +.panel-kicker span, +.composer-topline span:last-child, +.agent-log-header p, +.phase-column header small, +.context-section-header span, +.stat-card span, +.agent-summary-card span, +.agent-summary-card small, +.empty-copy, +.chat-bubble strong { + color: var(--muted); +} + +.project-meta p { + font-size: 0.72rem; + letter-spacing: 0.12em; + margin: 0 0 4px; + text-transform: uppercase; +} + +.project-meta h1, +.workspace-title h2, +.detail-hero-copy h1, +.context-section h3, +.agent-log-header h2 { + font-family: var(--font-display); + font-weight: 600; + letter-spacing: -0.02em; + margin: 0; +} + +.project-meta h1 { + font-size: 1.1rem; +} + +.project-meta span { + color: var(--muted); + display: block; + font-size: 0.88rem; + margin-top: 2px; +} + +.chat-history { + display: flex; + flex-direction: column; + min-height: 0; +} + +.panel-kicker { + align-items: center; + border-bottom: 1px solid var(--line); + display: flex; + gap: 8px; + padding: 10px 14px; +} + +.panel-kicker span { + font-size: 0.82rem; + font-weight: 600; +} + +.message-stack { + display: flex; + flex: 1; + flex-direction: column; + gap: 10px; + overflow: auto; + padding: 12px; +} + +.chat-bubble { + background: rgba(255, 255, 255, 0.84); + border: 1px solid var(--line); + border-radius: 8px; + max-width: 92%; + padding: 10px 11px; +} + +.chat-bubble.user { + align-self: flex-end; + background: rgba(61, 111, 214, 0.08); + border-color: rgba(61, 111, 214, 0.22); +} + +.chat-bubble strong { + display: block; + font-size: 0.72rem; + margin-bottom: 4px; + text-transform: uppercase; +} + +.chat-bubble p { + font-size: 0.92rem; + line-height: 1.45; + margin: 0; + white-space: pre-wrap; +} + +.chat-input-zone { + border-top: 1px solid var(--line); + padding: 12px; +} + +.composer-topline, +.quick-action-row, +.reference-row, +.input-toolbar, +.workspace-tabs, +.workspace-title, +.package-card-topline, +.package-card-actions, +.floating-cite-bar, +.detail-hero-actions, +.simple-tags, +.simple-task, +.context-section-header, +.simple-output-header, +.agent-log-header, +.agent-log-content { + align-items: center; + display: flex; + gap: 8px; +} + +.composer-topline { + justify-content: space-between; + margin-bottom: 8px; +} + +.composer-topline span { + font-size: 0.8rem; +} + +.reference-row { + flex-wrap: wrap; + margin-bottom: 8px; +} + +.reference-chip { + background: var(--surface-strong); + border: 1px solid var(--line); + border-radius: 999px; + color: var(--ink); + font-size: 0.75rem; + padding: 6px 9px; +} + +.reference-chip.removable { + color: #8f5530; +} + +.quick-action-row { + flex-wrap: wrap; + margin-bottom: 10px; +} + +.quick-action-button, +.workspace-tabs button, +.phase-nav-button, +.context-section-header button, +.detail-hero-actions button, +.floating-cite-bar button, +.selection-cite button, +.back-button, +.send-button, +.ghost-button { + align-items: center; + background: var(--surface-strong); + border: 1px solid var(--line); + border-radius: 8px; + color: var(--ink); + display: inline-flex; + gap: 8px; + justify-content: center; + padding: 8px 12px; + transition: + transform 160ms ease, + border-color 160ms ease, + background-color 160ms ease; +} + +.quick-action-button:hover, +.workspace-tabs button:hover, +.phase-nav-button:hover, +.context-section-header button:hover, +.detail-hero-actions button:hover, +.floating-cite-bar button:hover, +.selection-cite button:hover, +.back-button:hover, +.send-button:hover, +.ghost-button:hover, +.package-card:hover { + border-color: rgba(61, 111, 214, 0.34); + transform: translateY(-1px); +} + +.quick-action-button { + font-size: 0.8rem; + padding: 7px 10px; +} + +.quick-action-button svg, +.detail-hero-actions button svg, +.context-section-header button svg, +.floating-cite-bar button svg, +.selection-cite button svg { + color: var(--accent); +} + +.composer-wrap { + position: relative; +} + +textarea { + background: rgba(255, 255, 255, 0.82); + border: 1px solid var(--line); + border-radius: 8px; + color: var(--ink); + min-height: 124px; + outline: 0; + padding: 12px 14px; + resize: none; + width: 100%; +} + +textarea::placeholder { + color: #8b9ab0; +} + +textarea:focus { + border-color: rgba(61, 111, 214, 0.32); + box-shadow: 0 0 0 4px rgba(61, 111, 214, 0.08); +} + +.mention-popover { + background: var(--surface-strong); + border: 1px solid var(--line); + border-radius: 8px; + bottom: calc(100% + 8px); + box-shadow: var(--shadow); + display: grid; + gap: 5px; + left: 0; + padding: 8px; + position: absolute; + width: 100%; + z-index: 3; +} + +.mention-popover button { + background: transparent; + border: 0; + border-radius: 6px; + display: grid; + gap: 2px; + padding: 8px; + text-align: left; +} + +.mention-popover button:hover { + background: var(--surface-muted); +} + +.mention-popover span { + font-weight: 700; +} + +.mention-popover small { + color: var(--muted); +} + +.input-toolbar { + justify-content: space-between; + margin-top: 10px; +} + +.ghost-button, +.send-button { + border-radius: 8px; + height: 38px; + padding: 0; + width: 38px; +} + +.ghost-button svg, +.send-button svg { + height: 0.95rem; + width: 0.95rem; +} + +.send-button { + background: var(--accent); + border-color: var(--accent); + color: #f8fbff; +} + +.main-workspace { + display: grid; + gap: 14px; + grid-template-rows: minmax(0, 1fr) 164px; + min-width: 0; +} + +.workspace-canvas { + border-radius: 12px; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + min-height: 0; + overflow: hidden; + position: relative; +} + +.workspace-toolbar { + align-items: center; + border-bottom: 1px solid var(--line); + display: flex; + justify-content: space-between; + padding: 12px 16px; +} + +.workspace-title svg, +.panel-kicker svg, +.agent-log-header svg { + color: var(--accent); +} + +.workspace-title h2, +.agent-log-header h2 { + font-size: 1.05rem; +} + +.workspace-title p, +.agent-log-header p { + font-size: 0.86rem; + margin: 2px 0 0; +} + +.workspace-tabs button { + font-size: 0.82rem; + padding: 8px 11px; +} + +.workspace-tabs button.active { + background: var(--accent-soft); + border-color: rgba(61, 111, 214, 0.2); + color: #315aa9; +} + +.overview-layout { + display: grid; + grid-template-columns: 170px minmax(0, 1fr); + min-height: 0; + overflow: hidden; +} + +.phase-rail { + border-right: 1px solid var(--line); + display: flex; + flex-direction: column; + gap: 8px; + overflow: hidden; + padding: 14px; +} + +.phase-rail p { + color: var(--muted); + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.08em; + margin: 0 0 2px; + text-transform: uppercase; +} + +.phase-nav-button { + justify-content: space-between; + text-align: left; +} + +.phase-nav-button span { + font-size: 0.88rem; +} + +.phase-nav-button small { + color: var(--muted); + font-size: 0.76rem; +} + +.overview-content { + display: flex; + flex-direction: column; + min-height: 0; + overflow: auto; + padding: 12px 14px 58px; +} + +.stat-row { + display: grid; + gap: 8px; + grid-template-columns: repeat(3, minmax(0, 1fr)); + margin-bottom: 14px; +} + +.stat-card { + background: linear-gradient(160deg, rgba(255, 255, 255, 0.96), rgba(244, 248, 253, 0.96)); + border: 1px solid var(--line); + border-radius: 8px; + padding: 10px 12px; +} + +.stat-card span { + display: block; + font-size: 0.8rem; + margin-bottom: 10px; +} + +.stat-card strong { + font-family: var(--font-display); + font-size: 1.55rem; +} + +.phase-map { + display: grid; + flex: 1 1 auto; + gap: 12px; +} + +.phase-column header { + align-items: baseline; + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} + +.phase-column header span { + font-size: 0.82rem; + font-weight: 700; + letter-spacing: 0.09em; + text-transform: uppercase; +} + +.package-card-grid { + display: grid; + gap: 10px; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); +} + +.package-card { + background: linear-gradient(160deg, rgba(255, 255, 255, 0.98), rgba(244, 248, 253, 0.96)); + border: 1px solid var(--line); + border-radius: 8px; + padding: 12px; + text-align: left; + transition: + transform 160ms ease, + border-color 160ms ease, + box-shadow 160ms ease; +} + +.package-card.selected { + border-color: rgba(61, 111, 214, 0.4); + box-shadow: inset 0 0 0 1px rgba(61, 111, 214, 0.16); +} + +.package-card-topline { + justify-content: space-between; + margin-bottom: 10px; +} + +.package-short { + color: #315aa9; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.status-pill { + border-radius: 999px; + font-size: 0.72rem; + padding: 5px 8px; +} + +.status-pill.todo { + background: #edf3f8; + color: #59697d; +} + +.status-pill.in_progress { + background: #f5e7c9; + color: var(--warning); +} + +.status-pill.done { + background: #deefe6; + color: var(--success); +} + +.package-card h3 { + font-size: 1rem; + margin: 0 0 8px; +} + +.package-card p { + color: var(--muted); + font-size: 0.84rem; + line-height: 1.35; + margin: 0 0 10px; +} + +.package-card-actions { + justify-content: space-between; +} + +.package-card-actions > span, +.package-card-actions button { + align-items: center; + display: inline-flex; + gap: 6px; +} + +.package-card-actions > span { + color: var(--muted); + font-size: 0.78rem; +} + +.package-card-actions button { + background: transparent; + border: 0; + border-radius: 999px; + color: var(--accent); + padding: 4px; +} + +.floating-cite-bar { + background: rgba(255, 255, 255, 0.96); + border-top: 1px solid var(--line); + inset: auto 0 0 0; + justify-content: space-between; + padding: 10px 14px; + position: absolute; +} + +.floating-cite-bar span { + color: var(--muted); + font-size: 0.85rem; +} + +.floating-cite-bar button { + padding: 8px 10px; +} + +.detail-layout { + min-height: 0; + overflow: auto; + padding: 14px; +} + +.detail-hero { + background: linear-gradient(140deg, rgba(255, 255, 255, 0.98), rgba(244, 248, 253, 0.96)); + border: 1px solid var(--line); + border-radius: 8px; + display: grid; + gap: 18px; + grid-template-columns: 170px minmax(0, 1fr) auto; + margin-bottom: 14px; + padding: 14px; +} + +.back-button { + align-self: start; + justify-self: start; +} + +.detail-hero-copy { + min-width: 0; +} + +.status-label { + color: #315aa9; + display: inline-block; + font-size: 0.76rem; + font-weight: 700; + letter-spacing: 0.08em; + margin-bottom: 10px; + text-transform: uppercase; +} + +.detail-hero-copy h1 { + font-size: clamp(1.6rem, 2.4vw, 2.4rem); + margin-bottom: 8px; +} + +.detail-hero-copy p { + color: var(--muted); + font-size: 1rem; + line-height: 1.55; + margin: 0; + max-width: 48rem; +} + +.detail-hero-actions { + align-items: flex-start; + flex-direction: column; +} + +.detail-hero-actions button { + justify-content: flex-start; + width: 132px; +} + +.detail-content-grid { + display: grid; + gap: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.context-section { + background: rgba(255, 255, 255, 0.78); + border: 1px solid var(--line); + border-radius: 8px; + padding: 14px; +} + +.context-section-header { + justify-content: space-between; + margin-bottom: 12px; +} + +.context-section h3 { + font-size: 1.02rem; + margin-bottom: 3px; +} + +.context-section-header span { + display: block; + font-size: 0.8rem; +} + +.context-section-header button { + padding: 8px 10px; +} + +.context-section p { + color: var(--muted); + font-size: 0.92rem; + line-height: 1.5; + margin: 0; +} + +.simple-tags { + flex-wrap: wrap; +} + +.simple-tags span { + background: var(--surface-strong); + border: 1px solid var(--line); + border-radius: 6px; + font-size: 0.8rem; + padding: 7px 10px; +} + +.simple-task-list { + display: grid; + gap: 12px; +} + +.simple-task { + align-items: flex-start; +} + +.task-status-dot { + background: #b8a895; + border-radius: 999px; + height: 10px; + margin-top: 4px; + width: 10px; +} + +.task-status-dot.in_progress { + background: var(--warning); +} + +.task-status-dot.done { + background: var(--success); +} + +.simple-task strong { + display: block; + margin-bottom: 3px; +} + +.simple-output { + background: var(--surface-strong); + border: 1px solid var(--line); + border-radius: 8px; + padding: 12px; +} + +.simple-output + .simple-output { + margin-top: 10px; +} + +.simple-output-header { + justify-content: space-between; + margin-bottom: 10px; +} + +.simple-output-header span { + color: #315aa9; + font-size: 0.76rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +pre { + color: var(--ink); + font-family: "IBM Plex Mono", "SFMono-Regular", monospace; + font-size: 0.83rem; + line-height: 1.5; + margin: 0 0 10px; + overflow: auto; + white-space: pre-wrap; +} + +.selection-cite { + background: rgba(255, 255, 255, 0.98); + border: 1px solid rgba(61, 111, 214, 0.28); + border-radius: 8px; + bottom: 0; + justify-content: space-between; + margin-top: 14px; + padding: 12px 14px; + position: sticky; +} + +.selection-cite span { + color: var(--muted); + font-size: 0.88rem; +} + +.selection-cite button { + padding: 8px 12px; +} + +.agent-log-zone { + border-radius: 12px; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + overflow: hidden; + padding: 12px 14px 14px; +} + +.agent-log-header { + justify-content: space-between; + margin-bottom: 12px; +} + +.agent-focus-pill { + background: var(--accent-soft); + border: 1px solid rgba(61, 111, 214, 0.18); + border-radius: 8px; + color: #315aa9; + font-size: 0.82rem; + padding: 7px 11px; +} + +.agent-log-content { + align-items: stretch; + gap: 12px; +} + +.agent-summary-card, +.agent-log-list { + background: rgba(255, 255, 255, 0.68); + border: 1px solid var(--line); + border-radius: 8px; +} + +.agent-summary-card { + display: flex; + flex-direction: column; + justify-content: center; + min-width: 240px; + padding: 12px 14px; +} + +.agent-summary-card strong { + font-size: 1rem; + margin: 6px 0 3px; +} + +.agent-log-list { + display: grid; + gap: 8px; + list-style: decimal; + margin: 0; + padding: 14px 18px 14px 34px; + width: 100%; +} + +.agent-log-list li { + color: var(--muted); + font-size: 0.88rem; + line-height: 1.45; +} + +@media (max-width: 1180px) { + .pm-shell { + grid-template-columns: 1fr; + height: auto; + min-height: 100vh; + overflow: visible; + } + + html, + body { + height: auto; + overflow: auto; + } + + .chat-rail { + min-height: 680px; + } + + .main-workspace { + grid-template-rows: minmax(640px, auto) auto; + } + + .overview-layout, + .detail-hero, + .detail-content-grid, + .agent-log-content { + grid-template-columns: 1fr; + } + + .phase-rail { + border-bottom: 1px solid var(--line); + border-right: 0; + } + + .phase-nav-button { + justify-content: flex-start; + } + + .detail-hero-actions { + flex-direction: row; + flex-wrap: wrap; + } + + .detail-hero-actions button { + width: auto; + } +} + +@media (max-width: 760px) { + .pm-shell { + padding: 10px; + } + + .workspace-toolbar, + .floating-cite-bar, + .selection-cite, + .agent-log-header, + .agent-log-content, + .context-section-header, + .composer-topline { + align-items: flex-start; + flex-direction: column; + } + + .stat-row { + grid-template-columns: 1fr; + } + + .workspace-tabs { + width: 100%; + } + + .detail-layout { + padding: 12px; + } +} diff --git a/agentic_pm_demo_codex_plans/app/layout.tsx b/agentic_pm_demo_codex_plans/app/layout.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4e3cf010b92ab7d42c1e2d892455e5b36e5abfa0 --- /dev/null +++ b/agentic_pm_demo_codex_plans/app/layout.tsx @@ -0,0 +1,19 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "Agentic PM Demo", + description: "A Cursor-like workspace for AI-native product management work packages.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/agentic_pm_demo_codex_plans/app/page.tsx b/agentic_pm_demo_codex_plans/app/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..dd8ca79b236940ea4401b1958764ea354a62ac29 --- /dev/null +++ b/agentic_pm_demo_codex_plans/app/page.tsx @@ -0,0 +1,5 @@ +import { AgenticPmWorkbench } from "@/components/AgenticPmWorkbench"; + +export default function Page() { + return ; +} diff --git a/agentic_pm_demo_codex_plans/components/AgenticPmWorkbench.tsx b/agentic_pm_demo_codex_plans/components/AgenticPmWorkbench.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3b0412f0f0520fad7f97245215215379fed88ac9 --- /dev/null +++ b/agentic_pm_demo_codex_plans/components/AgenticPmWorkbench.tsx @@ -0,0 +1,826 @@ +"use client"; + +import { useMemo, useRef, useState } from "react"; +import seedWorkPackages from "@/data/work-packages.seed.json"; +import { getPackageSuggestions } from "@/lib/command-parser"; +import { runMockAgentTurn } from "@/lib/mock-agent"; +import type { + ChatMessage, + CitationReference, + CommandMode, + WorkPackage, + WorkPackagePhase, + WorkPackageStatus, +} from "@/lib/work-package-types"; + +type WorkspaceMode = "overview" | "detail"; + +const phases: WorkPackagePhase[] = [ + "Stakeholder Needs", + "Specify Product", + "Design Product", + "Verify and Validate Product", +]; + +const statusLabels: Record = { + todo: "Todo", + in_progress: "In progress", + done: "Done", +}; + +const quickActions: Array<{ mode: CommandMode; label: string; icon: typeof SparkIcon }> = [ + { mode: "ask", label: "Ask", icon: SparkIcon }, + { mode: "plan", label: "Plan", icon: PlanIcon }, + { mode: "change", label: "Change", icon: TuneIcon }, + { mode: "execute", label: "Execute", icon: PlayIcon }, +]; + +export function AgenticPmWorkbench() { + const [workPackages, setWorkPackages] = useState(() => seedWorkPackages as WorkPackage[]); + const [selectedPackageId, setSelectedPackageId] = useState(workPackages[0]?.id ?? ""); + const [workspaceMode, setWorkspaceMode] = useState("overview"); + const [inputValue, setInputValue] = useState(""); + const [activeReferences, setActiveReferences] = useState([]); + const [selectedQuote, setSelectedQuote] = useState(""); + const idCounterRef = useRef(0); + const inputRef = useRef(null); + + const [messages, setMessages] = useState([ + { + id: "welcome", + role: "assistant", + createdAt: new Date().toISOString(), + content: + "Workspace ready. Pick a work package, cite the exact context you want, then ask the agent to plan, change, or execute a simulated deliverable.", + }, + ]); + + const [agentLogs, setAgentLogs] = useState([ + "System initialized with the seeded IPD package map.", + "Agent standby: ready for package-specific planning or execution.", + ]); + + const selectedPackage = + workPackages.find((workPackage) => workPackage.id === selectedPackageId) ?? workPackages[0]; + const suggestions = getPackageSuggestions(inputValue, workPackages); + + const groupedPackages = useMemo(() => { + return phases.map((phase) => ({ + phase, + workPackages: workPackages.filter((workPackage) => workPackage.phase === phase), + })); + }, [workPackages]); + + const boardStats = useMemo(() => { + const done = workPackages.filter((workPackage) => workPackage.status === "done").length; + const inProgress = workPackages.filter((workPackage) => workPackage.status === "in_progress").length; + + return { + total: workPackages.length, + done, + inProgress, + }; + }, [workPackages]); + + function openPackage(workPackage: WorkPackage) { + setSelectedPackageId(workPackage.id); + setWorkspaceMode("detail"); + } + + function handleQuickAction(workPackage: WorkPackage, mode: CommandMode) { + setSelectedPackageId(workPackage.id); + setInputValue(`@${workPackage.shortName} ${mode} `); + inputRef.current?.focus(); + } + + function addReference(workPackage: WorkPackage, label: string, quote?: string) { + const reference: CitationReference = { + id: `ref-${workPackage.id}-${idCounterRef.current++}`, + packageId: workPackage.id, + packageShortName: workPackage.shortName, + packageTitle: workPackage.title, + label, + quote, + }; + + setSelectedPackageId(workPackage.id); + setActiveReferences((references) => [...references, reference]); + setInputValue((currentValue) => { + const token = quote + ? `@${workPackage.shortName}:"${quote.slice(0, 72)}"` + : `@${workPackage.shortName}#${label.replace(/\s+/g, "-")}`; + + return currentValue.trim() ? `${currentValue.trimEnd()} ${token} ` : `${token} `; + }); + inputRef.current?.focus(); + } + + function citeSelectedQuote() { + if (!selectedPackage || !selectedQuote) { + return; + } + + addReference(selectedPackage, "Selected wording", selectedQuote); + setSelectedQuote(""); + } + + function captureSelectedText() { + const selection = window.getSelection()?.toString().replace(/\s+/g, " ").trim() ?? ""; + + if (selection.length > 2) { + setSelectedQuote(selection.slice(0, 180)); + } + } + + function applySuggestion(workPackage: WorkPackage) { + setInputValue((currentValue) => currentValue.replace(/@[A-Za-z\s-]*$/, `@${workPackage.shortName} `)); + setSelectedPackageId(workPackage.id); + inputRef.current?.focus(); + } + + function handleSend() { + const content = inputValue.trim(); + + if (!content) { + return; + } + + const userMessage: ChatMessage = { + id: `message-user-${idCounterRef.current++}`, + role: "user", + content, + createdAt: new Date().toISOString(), + references: activeReferences, + }; + const result = runMockAgentTurn(content, workPackages); + const assistantMessage: ChatMessage = { + id: `message-assistant-${idCounterRef.current++}`, + role: "assistant", + content: result.assistantMessage, + createdAt: new Date().toISOString(), + }; + const selectedAfterTurn = result.workPackages.find( + (workPackage) => workPackage.id === (result.selectedPackageId ?? selectedPackageId), + ); + + setMessages((currentMessages) => [...currentMessages, userMessage, assistantMessage]); + setWorkPackages(result.workPackages); + setSelectedPackageId(result.selectedPackageId ?? selectedPackageId); + setWorkspaceMode(result.selectedPackageId ? "detail" : workspaceMode); + setAgentLogs((logs) => [ + `Package ${selectedAfterTurn?.shortName ?? "General"} handled request: ${content.slice(0, 78)}.`, + `Board state is now ${selectedAfterTurn ? statusLabels[selectedAfterTurn.status].toLowerCase() : "unchanged"}.`, + ...logs, + ]); + setInputValue(""); + setActiveReferences([]); + } + + return ( +
+