From 118048028da15bae840ce26aaafb70ba2116641b Mon Sep 17 00:00:00 2001 From: dev-goraebap Date: Mon, 9 Nov 2026 14:59:02 +0900 Subject: [PATCH] =?UTF-8?q?Webcraft=200.8.0:=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=C2=B7=EC=83=81=EC=84=B8=20=ED=8C=A8=ED=84=B4?= =?UTF-8?q?=20=EC=A7=80=EC=B9=A8=EC=9D=84=20=EC=A0=95=EB=A6=AC=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EC=83=88=20=ED=8C=90=20=EC=95=8C=EB=A6=BC=EA=B3=BC?= =?UTF-8?q?=20=EC=9D=B4=EC=96=B4=EB=B0=9B=EA=B8=B0=EB=A5=BC=20=EB=8D=94?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 화면 이동에서 가리는 막을 빼고 SWR·미리 준비·카드 위 진행 표시·교차 페이드로 바꾼다. 화면 이동 점검(layout-shift)을 검증에 넣는다. - 지연 로딩 부품의 미리 받기, 열릴 때만 만드는 메뉴 내용, 사진 대표색·비율 자리, 리치 본문 16px·1.7을 지침에 넣는다. - 본문 상세의 속성 열 순서(목차·속성·정보), 둥근 편집 도구와 주소 있는 버전 기록, 바로 저장되는 속성의 되돌리기 토스트, 아이콘의 그려진 크기 맞추기를 정한다. - 지침 제안을 원본 저장소 이슈로 받고, 프로젝트 규칙 표에서 제안 표시를 뺀다. - Claude Code 배포본에 새 판 알림 세션 시작 훅을 넣고, 지침을 못 읽을 때의 이어받기 문서 절차를 더한다. - 가이드 사이트 해설(상태와 피드백, 콘텐츠 폭, 아이콘)을 맞춘다. Co-Authored-By: Claude Opus 5.5 --- .../webcraft/.claude-plugin/plugin.json | 2 +- .../dist/claude/plugins/webcraft/README.md | 23 +++++- .../claude/plugins/webcraft/hooks/hooks.json | 17 +++++ .../plugins/webcraft/hooks/update-check.mjs | 72 +++++++++++++++++++ .../dist/claude/plugins/webcraft/package.json | 2 +- .../plugins/webcraft/skills/api/SKILL.md | 2 +- .../references/composed-components.md | 4 +- .../design-system/references/data-states.md | 17 +++-- .../skills/design-system/references/icons.md | 2 + .../references/layout-patterns.md | 4 +- .../design-system/references/typography.md | 2 +- .../skills/frontend/references/navigation.md | 4 +- .../webcraft/skills/goraebap-mode/SKILL.md | 2 + .../skills/goraebap-mode/playbooks/evolve.md | 4 +- .../goraebap-mode/references/handoff.md | 30 ++++++++ .../goraebap-mode/references/verification.md | 1 + .../webcraft/skills/manage-workflow/SKILL.md | 11 +-- .../references/project-rules.md | 2 +- .../manage-workflow/references/proposals.md | 15 ++-- .../webcraft/.codex-plugin/plugin.json | 2 +- .../dist/codex/plugins/webcraft/README.md | 23 +++++- .../dist/codex/plugins/webcraft/package.json | 2 +- .../plugins/webcraft/skills/api/SKILL.md | 2 +- .../references/composed-components.md | 4 +- .../design-system/references/data-states.md | 17 +++-- .../skills/design-system/references/icons.md | 2 + .../references/layout-patterns.md | 4 +- .../design-system/references/typography.md | 2 +- .../skills/frontend/references/navigation.md | 4 +- .../webcraft/skills/goraebap-mode/SKILL.md | 2 + .../skills/goraebap-mode/playbooks/evolve.md | 4 +- .../goraebap-mode/references/handoff.md | 30 ++++++++ .../goraebap-mode/references/verification.md | 1 + .../webcraft/skills/manage-workflow/SKILL.md | 11 +-- .../references/project-rules.md | 2 +- .../manage-workflow/references/proposals.md | 15 ++-- plugins/webcraft/docs/plugin-usage.md | 23 +++++- plugins/webcraft/hooks/hooks.json | 17 +++++ plugins/webcraft/hooks/update-check.mjs | 72 +++++++++++++++++++ plugins/webcraft/package.json | 2 +- plugins/webcraft/scripts/package-plugins.mjs | 2 + plugins/webcraft/skills/api/SKILL.md | 2 +- .../references/composed-components.md | 4 +- .../design-system/references/data-states.md | 17 +++-- .../skills/design-system/references/icons.md | 2 + .../references/layout-patterns.md | 4 +- .../design-system/references/typography.md | 2 +- .../skills/frontend/references/navigation.md | 4 +- .../webcraft/skills/goraebap-mode/SKILL.md | 2 + .../skills/goraebap-mode/playbooks/evolve.md | 4 +- .../goraebap-mode/references/handoff.md | 30 ++++++++ .../goraebap-mode/references/verification.md | 1 + .../webcraft/skills/manage-workflow/SKILL.md | 11 +-- .../references/project-rules.md | 2 +- .../manage-workflow/references/proposals.md | 15 ++-- plugins/webcraft/tests/package.test.mjs | 26 ++++++- site/content/app-shell/content-width.mdx | 2 + .../content/app-shell/states-and-feedback.mdx | 5 +- site/content/common/icons.mdx | 1 + 59 files changed, 501 insertions(+), 92 deletions(-) create mode 100644 plugins/webcraft/dist/claude/plugins/webcraft/hooks/hooks.json create mode 100644 plugins/webcraft/dist/claude/plugins/webcraft/hooks/update-check.mjs create mode 100644 plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/handoff.md create mode 100644 plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/handoff.md create mode 100644 plugins/webcraft/hooks/hooks.json create mode 100644 plugins/webcraft/hooks/update-check.mjs create mode 100644 plugins/webcraft/skills/goraebap-mode/references/handoff.md diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/.claude-plugin/plugin.json b/plugins/webcraft/dist/claude/plugins/webcraft/.claude-plugin/plugin.json index 5cbf74c..71cfdc8 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/.claude-plugin/plugin.json +++ b/plugins/webcraft/dist/claude/plugins/webcraft/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "webcraft", - "version": "0.7.2", + "version": "0.8.0", "description": "웹 플랫폼 개발 지침(프론트·디자인 시스템·백엔드·API·DB·아키텍처)과 프로젝트 연결", "author": { "name": "dev-goraebap" diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/README.md b/plugins/webcraft/dist/claude/plugins/webcraft/README.md index 7b11414..3365b55 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/README.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/README.md @@ -26,10 +26,29 @@ codex plugin add webcraft@dev-goraebap ## 갱신 -플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있으면 먼저 갱신합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. +플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있거나 지침을 읽을 수 없으면 에이전트는 추측으로 진행하지 않고, 작업 상태를 `~/.goraebap/handoff/` 아래 이어받기 문서로 남긴 뒤 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 안내합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. + +Claude Code에서 자동 갱신은 공식 마켓플레이스가 아니면 기본값이 꺼짐입니다. 사용자가 `/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update로 켜거나, 조직 관리자가 managed settings의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true`를 넣습니다. 자동 갱신은 세션 시작 때가 아니라 첫 메시지 뒤 최대 10분 안에 돌며, 실행 중인 세션은 옛 판을 유지하고 `/reload-plugins`를 입력하거나 다음에 실행할 때 새 판이 적용됩니다. + +Claude Code 배포본에는 세션 시작 훅(`hooks/hooks.json`, `hooks/update-check.mjs`)이 들어갑니다. 새로 시작할 때만(`startup`) 돌며, 하루 한 번 원본 저장소의 최신 판을 받아 `~/.goraebap/cache/webcraft-latest.json`에 기억합니다. 설치된 판보다 높으면 `Webcraft 새 판이 나왔습니다(0.8.0 → 0.8.1). 에이전트에게 "Webcraft 업데이트해 줘"라고 하면 갱신합니다.` 한 줄을 보이고, 에이전트가 첫 요청 전에 한 번 갱신을 제안합니다. 동의하면 아래 수동 갱신 명령을 실행한 뒤 `/reload-plugins`를 안내하고, 거절하면 다시 묻지 않습니다. 네트워크 실패나 Node가 없는 경우에는 조용히 넘어갑니다. Codex 배포본에는 훅이 없습니다. + +직접 갱신할 때는 다음 명령을 실행하고, Claude Code에서는 `/reload-plugins`를 입력하거나 새 세션을 엽니다. + +Claude Code: + +```text +claude plugin marketplace update dev-goraebap +claude plugin update webcraft@dev-goraebap +``` + +Codex: + +```text +codex plugin marketplace upgrade dev-goraebap +``` ## 이전 이름에서 전환 이전 식별자는 `goraebap-workflow@goraebap-workflow`입니다. 새 이름은 `webcraft@dev-goraebap`이며 입구 스킬 이름은 `goraebap-mode`를 유지합니다. 기존 프로젝트는 새 플러그인 설치를 확인한 뒤 AGENTS.md 연결을 새 이름과 최소 판 `0.7.1`으로 바꿉니다. 기존 사본에서 바꾼 규칙과 제품 전용 스킬은 보존합니다. 옛 설치를 제거하는 작업은 새 설치 확인 후 사용자가 허용한 범위에서 수행합니다. -역할별 모델 설정 `~/.claude/rules/goraebap-models.md`와 로컬 제안함 `~/.goraebap/proposals/`는 기존 경로를 유지합니다. +지침 제안은 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 역할별 모델 설정 `~/.claude/rules/goraebap-models.md`는 기존 경로를 유지하며, `~/.goraebap/`에는 최신 판 확인 캐시(`cache/`), 이어받기 문서(`handoff/`), 직접 올리지 못한 이슈 문서(`issues/`)가 남습니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/hooks/hooks.json b/plugins/webcraft/dist/claude/plugins/webcraft/hooks/hooks.json new file mode 100644 index 0000000..90c0e27 --- /dev/null +++ b/plugins/webcraft/dist/claude/plugins/webcraft/hooks/hooks.json @@ -0,0 +1,17 @@ +{ + "description": "Webcraft 새 판이 있으면 세션을 시작할 때 알리고 갱신을 제안합니다(하루 한 번 확인).", + "hooks": { + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/update-check.mjs\"", + "timeout": 10 + } + ] + } + ] + } +} diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/hooks/update-check.mjs b/plugins/webcraft/dist/claude/plugins/webcraft/hooks/update-check.mjs new file mode 100644 index 0000000..9e84efc --- /dev/null +++ b/plugins/webcraft/dist/claude/plugins/webcraft/hooks/update-check.mjs @@ -0,0 +1,72 @@ +// Webcraft 새 판 알림(세션 시작 훅). 설치된 판과 원본 저장소의 최신 판을 하루 한 번 비교해, 낮으면 사용자에게 한 줄로 +// 알리고 에이전트에게는 갱신을 제안·실행하는 방법을 맥락으로 준다. 네트워크·파일 오류는 조용히 넘긴다: 알림이 실패해도 +// 세션 시작을 막거나 경고를 남기지 않는다. +import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const LATEST_URL = 'https://raw.githubusercontent.com/dev-goraebap/dev.goraebap/main/plugins/webcraft/package.json'; +const DAY = 24 * 60 * 60 * 1000; + +/** 점으로 나눈 숫자 판을 비교한다. a가 b보다 높으면 양수. 숫자가 아닌 꼬리(-rc 등)는 무시한다. */ +export function compareVersions(a, b) { + const parts = value => String(value).split('-')[0].split('.').map(part => Number.parseInt(part, 10) || 0); + const [x, y] = [parts(a), parts(b)]; + for (let i = 0; i < Math.max(x.length, y.length); i += 1) { + const diff = (x[i] ?? 0) - (y[i] ?? 0); + if (diff) return diff; + } + return 0; +} + +/** 새 판이 있으면 훅 출력(JSON)을, 없으면 null을 돌려준다. */ +export function notice(installed, latest) { + if (!installed || !latest || compareVersions(latest, installed) <= 0) return null; + return { + systemMessage: `Webcraft 새 판이 나왔습니다(${installed} → ${latest}). 에이전트에게 "Webcraft 업데이트해 줘"라고 하면 갱신합니다.`, + hookSpecificOutput: { + hookEventName: 'SessionStart', + additionalContext: [ + `Webcraft 플러그인의 새 판이 있습니다(설치된 판 ${installed}, 최신 판 ${latest}).`, + '사용자의 첫 요청을 처리하기 전에 한 번만 갱신을 제안합니다. 사용자가 동의하면 아래 두 명령을 차례로 실행합니다.', + '`claude plugin marketplace update dev-goraebap`', + '`claude plugin update webcraft@dev-goraebap`', + '성공하면 `/reload-plugins`를 입력하면 바로 적용되고, 아니면 다음 세션부터 적용된다고 안내합니다.', + '작업 중이라 지금 갱신하지 않기로 하면 다시 묻지 않고 하던 작업을 이어갑니다.', + ].join('\n'), + }, + }; +} + +async function readJson(file) { + return JSON.parse(await fs.readFile(file, 'utf8')); +} + +/** 하루 안에 확인한 최신 판이 캐시에 있으면 그것을, 없으면 원본에서 받아 캐시에 남긴다. 실패하면 null. */ +async function latestVersion(cacheFile, now = Date.now()) { + const cached = await readJson(cacheFile).catch(() => null); + if (cached?.latest && now - cached.checkedAt < DAY) return cached.latest; + try { + const response = await fetch(LATEST_URL, { signal: AbortSignal.timeout(3000) }); + if (!response.ok) return cached?.latest ?? null; + const { version } = await response.json(); + await fs.mkdir(path.dirname(cacheFile), { recursive: true }); + await fs.writeFile(cacheFile, JSON.stringify({ latest: version, checkedAt: now })); + return version; + } catch { + return cached?.latest ?? null; + } +} + +async function main() { + const root = process.env.CLAUDE_PLUGIN_ROOT ?? fileURLToPath(new URL('../', import.meta.url)); + const installed = (await readJson(path.join(root, '.claude-plugin', 'plugin.json')).catch(() => null))?.version; + const latest = await latestVersion(path.join(os.homedir(), '.goraebap', 'cache', 'webcraft-latest.json')); + const output = notice(installed, latest); + if (output) process.stdout.write(`${JSON.stringify(output)}\n`); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await main().catch(() => undefined); +} diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/package.json b/plugins/webcraft/dist/claude/plugins/webcraft/package.json index 323d44d..e72b43b 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/package.json +++ b/plugins/webcraft/dist/claude/plugins/webcraft/package.json @@ -1,6 +1,6 @@ { "name": "webcraft", - "version": "0.7.2", + "version": "0.8.0", "private": true, "type": "module" } diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/api/SKILL.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/api/SKILL.md index 6214cb7..29ab099 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/api/SKILL.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/api/SKILL.md @@ -14,7 +14,7 @@ description: 웹 플랫폼의 HTTP 계약, 오류 응답, 인증·권한, 첨부 - 인증된 사용자와 자원 접근 권한을 서버에서 검사합니다. 숨겨야 하는 대상의 존재 노출 정책을 프로젝트에서 정하고 REST·MCP·다운로드에 동일하게 적용합니다. - PATCH의 생략·null·값 전달 의미를 구분합니다. 업무 날짜·지역 시각·절대 시각을 구별하고 절대 시각은 UTC로 전달합니다. 업무 시간대는 프로젝트가 정합니다. - 목록은 서버 필터·안정된 정렬·페이지/커서 계약을 명시합니다. count와 목록이 같은 조건을 쓰는지 확인합니다. -- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. +- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. 사진 업로드가 선택 값인 대표색·가로·세로를 함께 보내면 파일과 함께 저장하고 첨부 정보로 돌려줍니다. - 변경은 버전 충돌과 명령 재시도 시 중복 효과를 검토합니다. 브라우저 캐시 무효화가 동시 편집 충돌을 해결한다고 가정하지 않습니다. 완료 시 성공 응답·실패 응답·권한·영향받는 호출자와 생성 타입을 확인합니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/composed-components.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/composed-components.md index 0da0d8a..249a89c 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/composed-components.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/composed-components.md @@ -22,6 +22,8 @@ 주소가 없는 시트는 브라우저의 닫기 요청(CloseWatcher와 같은 장치)으로 안드로이드 뒤로가기와 Esc에 닫힙니다. 네이티브 ``를 `showModal()`로 여는 시트(Astryx BottomSheet의 기본, 배경 막 있음)는 지원 브라우저에서 이 요청을 `cancel`로 이미 받으므로 따로 등록하지 않습니다. 겹쳐 등록하면 한 번에 두 번 닫힙니다. 직접 만든 시트나 배경 막 없는 시트만 CloseWatcher를 등록합니다. 외부 제어 API가 시트 열림을 주소 쿼리에 저장하라는 규칙은 아니며, 열림 상태를 쿼리에 두지 않습니다. 짧은 조작마다 방문 기록이 쌓이고 새로고침하면 시트가 다시 열리기 때문입니다. 닫기 요청을 지원하지 않는 브라우저(iOS 사파리 등)에서는 뒤로가기가 이전 페이지로 갈 수 있으므로, 시트마다 보이는 닫기 버튼을 두고 잃으면 아까운 입력은 시트에 담지 않습니다. 입력이 길면 주소가 있는 화면으로 엽니다. 시트는 쌓지 않습니다. +팝오버와 바텀시트는 열렸을 때만 내용을 만듭니다. 행마다 붙는 메뉴의 확인 대화상자는 그 동작을 고를 때만 만듭니다. 닫힌 메뉴 내용과 행마다 미리 만든 확인 대화상자(``)를 화면 이동 때 한꺼번에 정리하면 메인 스레드가 멈춥니다. 행마다 권한 판단에 필요한 데이터는 메뉴에 포인터나 초점이 갈 때 미리 읽습니다. + ## 선택지 목록 작은 고정 선택지는 그대로 나열하고, 8개를 넘으면 위에 검색 칸을 두어 초점을 줍니다. 검색 전에는 처음 8개와 이미 고른 항목, 검색하면 최대 50개, 나머지는 개수로 알립니다. 전부 렌더링하지 않습니다. 이 수치는 서버 한도와 별개이며 프로젝트가 조절합니다. 하나 고르기는 고른 뒤 닫고 끝에 체크, 여럿 고르기는 열린 채 앞에 상자를 둡니다. 휴대폰의 동작·선택·시간 목록은 최소 48px 행에 15px 글자·20px 아이콘을 씁니다. 보조 정보는 13px입니다. 메뉴를 시트로 옮기는 것만으로 데스크톱 밀도가 자동으로 바뀐다고 가정하지 않습니다. @@ -44,6 +46,6 @@ ## 본문 칸 -제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 본문 글자는 화면 글자(13px)보다 한 단계 큰 본문(15px, `--text-large-size`)이고 제목은 `--text-heading-2-size`입니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. +제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 앱 화면의 본문 토큰(13px, 15px `--text-large-size`)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 본문 칸과 상세 본문 글자는 데스크톱·휴대폰 모두 16px, 줄 높이 1.7이며 제목은 `--text-heading-2-size`입니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. 구현이 없는 조합을 메뉴에 빈 항목으로 만들지 않습니다. 제품 고유 조합(아이콘 표시 고르기, 전역 검색 팔레트, 프로필 패널)은 프로젝트가 소유합니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/data-states.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/data-states.md index 8bbfaff..f4f6b7b 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/data-states.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/data-states.md @@ -16,17 +16,21 @@ ## 화면을 바꾸는 동안 -캐시가 없는 화면의 데이터와 코드가 준비될 때까지 이전 화면을 유지합니다. 지연이 짧으면 표시를 생략하고, 길어지면 앱 셸을 그대로 둔 채 콘텐츠 영역에 불투명한 막을 표시합니다. 막과 새 본문을 함께 흐리게 만들지 않습니다. 새 화면의 그리기와 긴 작업이 끝났는지 확인한 뒤 잠깐 안정화 시간을 두고 막만 걷습니다. 숨은 탭이나 프레임이 멈춘 경우에도 끝나는 시간 제한과 취소 처리를 둡니다. +앱 첫 진입만 첫 방문 덮개(스플래시 화면)로 덮고, 그 뒤의 화면 이동에는 본문을 가리는 불투명한 막을 두지 않습니다. 화면 전체 스켈레톤은 모양이 바뀌는 본문과 높이가 맞지 않고, 영역별 덮개는 같은 판정이 화면마다 늘어나며, 콘텐츠 막은 상세 안 사이드바까지 덮고 재방문에서도 떠 쓰지 않습니다. -기본 시작값은 표시 지연 100ms, 한 번 표시한 뒤 최소 300ms, 그리기 확인 뒤 안정화 200ms, 막 해제 700ms입니다. 제품에 확정된 값이 있으면 그 값을 따릅니다. 최소 표시 시간만 지났다고 새 본문의 레이아웃이 안정되기 전에 막을 걷지 않습니다. +캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신합니다(stale-while-revalidate). 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 먼저 옮깁니다. -캐시가 있는 화면의 전환 효과는 콘텐츠에만 적용합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 붙인 콘텐츠만 캡처합니다. 로딩 막이 보이거나 걷히는 동안에는 겹치지 않습니다. 동작 줄이기 설정을 따르고, 빠른 연속 이동에서 진행 중인 전환을 겹쳐 실행하지 않습니다. +앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 따로 붙이지 않고 문서에 한 번 걸어, `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하며 같은 주소는 10초에 한 번만 부릅니다. + +캐시가 없어 기다릴 때는 이전 화면을 그대로 두고, 150ms가 지나면 콘텐츠 카드 위쪽 테두리에 테마 강조색이 흐르는 진행 표시만 띄웁니다. 진행 표시는 높이 2px로 카드 위에 겹쳐 그리고 카드 모서리에 맞춰 잘라 배치를 밀지 않습니다. 150ms 안에 끝나면 띄우지 않고, 한 번 띄웠으면 최소 300ms 둔 뒤 새 화면과 함께 바로 내립니다. 그리기 확인 후 천천히 걷는 대기를 진행 표시에 쓰면 이동이 끝난 뒤에도 남으므로 쓰지 않습니다. + +준비된 화면끼리는 콘텐츠 영역만 200ms 교차 페이드합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 콘텐츠 영역 하나에만 붙입니다. 앞뒤 화면에서 같은 부분(사이드바 등)은 같은 픽셀끼리 겹쳐 그대로 있는 듯 보이므로 화면별 예외를 두지 않습니다. TanStack Router는 loader가 끝난 뒤 View Transition을 시작하므로 준비하는 동안 진행 표시가 멈추지 않습니다. 동작 줄이기 설정, 로그인 오가기, 진행 중인 전환, 열린 대화상자에서는 전환하지 않습니다. ## 대화상자의 데이터 준비 준비 중에는 이름 있는 원형 Spinner를 보이고, 필요한 데이터가 준비되면 내용을 한 번에 표시합니다. 바깥 창과 준비 영역의 최소 크기를 유지해 작은 창에서 갑자기 커지지 않게 합니다. 준비 확인 Promise가 새로 생겼다는 이유만으로 캐시에 있는 내용을 가리지 않습니다. 필요한 조회가 실제 캐시에 남아 있는지 확인하며, 한 번 표시한 로딩에는 최소 표시 시간을 적용합니다. 실패는 무한 로딩 대신 실패 상태와 다시 시도로 끝납니다. -확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 애니메이션 호출 여부뿐 아니라 앱 셸·본문이 숨는 프레임과 로딩 막의 범위도 확인합니다. +확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 화면 전환·로딩·캐시·지연 로딩 부품을 바꾸면, 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동을 차례로 누르는 Playwright 점검을 돌립니다. 단계에는 같은 틀 안 이동, 3초 넘게 지난 페이지로 다시 방문, 목록 → 상세 → 뒤로 → 같은 상세를 넣고, 새 종류의 화면을 만들면 단계를 더합니다. 단계마다 누른 뒤의 레이아웃 이동 합(`layout-shift`, 입력 직후 제외)과 새 화면으로 교체가 끝날 때까지의 대기를 잽니다. 진행 표시는 데이터 속성으로 감지하고, 대기는 주소 변경 시각이 아니라 준비 중 표시(`aria-busy`)가 풀릴 때까지로 잽니다. 이동이 0.01을 넘거나, 캐시가 있어야 하는 다시 방문에서 진행 표시가 뜨면 실패입니다. ## 상태 화면 @@ -52,7 +56,9 @@ ## 캐시와 전환 -첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 180ms가 지나도 준비되지 않을 때만 진행 표시를 덮습니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. 이미지는 자리를 먼저 확보해 내용이 밀리지 않게 합니다. +첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 150ms가 지나도 준비되지 않을 때만 콘텐츠 카드 위쪽 테두리에 진행 표시를 띄웁니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. + +사진을 올릴 때는 브라우저가 대표색과 원본 가로·세로를 재어 업로드 요청에 함께 보냅니다(64px로 줄여 색을 묶어 세고, 가장 많은 묶음의 평균색). 측정은 한 번에 하나씩, 3초 제한이며, 실패하면 측정값 없이 올립니다. 서버는 파일과 함께 저장해 첨부 정보로 돌려주고, 리치 본문은 사진 블록 속성에도 넣습니다. 보여 줄 때는 원본 비율과 대표색으로 자리를 먼저 칠해 내용이 밀리지 않게 하고, 받아야 하는 사진만 0.4초에 걸쳐 나타냅니다. 이 페이지에서 이미 받았거나 브라우저 캐시에 있는 사진은 바로 보입니다. 화면 전환 효과는 준비된 내용 사이의 짧은 교차 페이드만 쓰고 동작 줄이기 설정에서는 끕니다. 스켈레톤은 실제 배치와 같을 때만 씁니다. 키트 예제는 대기 상태를 확인하기 위해 페이지 안의 진행 표시를 씁니다. @@ -64,4 +70,5 @@ | 양식 저장 | 양식 위 배너(`role="alert"`), 입력은 지킵니다 | | 페이지 전체(변경 실패·충돌·읽기 전용) | 머리 아래 배너 줄 | | 화면에 결과가 보이지 않는 짧은 완료(주소 복사, 보관, 백그라운드 작업 시작) | 토스트. 되돌릴 수 있으면 "되돌리기" 하나를 두고 충분히 오래 남깁니다. 오류·경고·되돌릴 수 없는 결과·다음 행동이 필요한 내용은 토스트에 두지 않습니다. 결과가 화면에 바로 보이면 알리지 않습니다 | +| 상세에서 저장 버튼 없이 바로 저장되는 속성 변경(상태·담당·날짜·라벨·우선순위 등) | 결과가 화면에 보여도 "되돌리기" 토스트를 5초 띄웁니다. 토스트 아래 2px 막대가 남은 시간만큼 줄어들고 포인터가 올라가 있으면 멈춥니다. 되돌리기는 서버 최신본을 다시 읽은 뒤 바꾼 속성만 이전 값으로 저장해, 그 사이 다른 사람의 변경을 덮지 않습니다 | | 화면을 그릴 수 없는 오류 | 라우트 오류 화면과 다시 시도 | diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/icons.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/icons.md index a7532cf..35fbd12 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/icons.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/icons.md @@ -12,6 +12,8 @@ 모바일에서 아이콘 버튼의 누르는 영역은 최소 44px입니다. 프로필 그림은 28~32px로 두고 그 바깥에서 같은 누르는 영역을 확보합니다. 누르는 영역을 넓히려고 그림을 44~48px로 키우지 않습니다. 반대로 버튼 상자만 넓히고 안의 아이콘을 데스크톱 크기에 두지도 않습니다. Astryx가 아이콘 칸을 고정하면 SVG가 줄어들지 않는지 실제 크기와 중심을 확인합니다. 머리 줄의 아이콘 규칙이 그 안에서 열리는 시트·메뉴까지 번지지 않도록 범위를 제한합니다. +속성 열·활동 기록처럼 아이콘이 세로로 늘어선 곳은 상자 크기뿐 아니라 24 격자 위에 그려진 범위도 비슷하게 맞춥니다(대부분 20칸 안팎). 범위가 눈에 띄게 작은 아이콘(16칸 정도)은 키우지 않고 같은 뜻의 다른 Solar 아이콘으로 바꿉니다. 키우면 선도 굵어지기 때문입니다. 확인은 화면에서 SVG의 `getBBox()`를 재서 합니다. 기본 상태의 줄에 Line Duotone처럼 일부 선이 흐린 형태를 섞지 않습니다. 같은 색이어도 흐리고 작게 보입니다. + ## 고르는 기준 | 상황 | 아이콘 | diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/layout-patterns.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/layout-patterns.md index a6a3745..1e1c8cf 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/layout-patterns.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/layout-patterns.md @@ -33,7 +33,9 @@ 본문이 있는 대상의 상세([DetailLayout](https://github.com/dev-goraebap/dev.goraebap/blob/main/site/src/shared/ui/layouts/detail/DetailLayout.tsx))는 최대 45rem 읽기 열에 소속·제목·본문·이어지는 것(하위 항목·활동)을 둡니다. 속성은 넓으면 오른쪽 16rem 열에 묶음 이름과 함께(스크롤해도 보입니다), 좁으면 제목과 본문 사이의 줄바꿈되는 알약으로 옮깁니다. 같은 요소를 옮기므로 속성을 두 벌 렌더링하지 않습니다. 창 폭이 아니라 컨테이너 폭(60rem)으로 판단해 대화상자·패널에서도 맞게 바뀝니다. 긴 제목·주소는 줄을 넘겨 가로로 넘치지 않습니다. 상세와 작성 면은 점 무늬 없이 평평합니다. -본문 편집 도구(편집·저장)는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. +본문이 있는 모든 상세는 속성 열을 같은 순서로 둡니다. 본문에 제목이 있으면 맨 위에 "목차" 묶음, 그 아래 대상별 속성, 맨 끝에 "정보" 묶음(작성자 아바타·작성일·수정일과 고친 사람·버전 수)을 두고, 모르는 값은 줄째 뺍니다. 목차와 버전 줄도 다른 속성과 같은 속성 행 부품으로 그려 아이콘·글자 자리를 맞춥니다. 누르면 목차는 테두리 있는 팝오버, 버전은 버전 기록 대화상자를 엽니다. 휴대폰은 목차를 오른쪽 아래 떠 있는 둥근 버튼과 바텀시트로 둡니다. 편집기 가장자리에 목차 표시를 붙이지 않습니다. 상세의 더 보기(…) 메뉴는 페이지 머리 줄 한 곳에 둡니다. + +본문 편집 도구는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. 데스크톱 머리 줄의 편집 전환·편집 마침·버전 기록은 필터·정렬과 같은 둥근 아이콘 도구(`variant="tool"`, 아이콘만, 툴팁에 이름)로 두며, "편집" 같은 글자 버튼을 머리 줄에 두지 않습니다. 저장은 상태가 바뀌는 동작이라 글자를 유지하고, 휴대폰은 바닥 줄의 저장·완료를 그대로 둡니다. 버전 기록 목록은 표준 목록 행(아이콘·시각·설명, 선택 표시)으로 그리고, 주소를 가진 대화상자(라우트 마스킹 `…/history`, 새로 고침하면 다시 열림)로 엽니다. ## 설정 diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/typography.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/typography.md index 1764370..fafbd92 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/typography.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/design-system/references/typography.md @@ -9,7 +9,7 @@ | 큰 본문·작은 제목 | 15px, 제목은 600 | 17px, 제목은 600 | `--text-large-size`, `--text-heading-3-*` | | 페이지 제목(상세·설정·작성 페이지) | 24px | 24px | `--text-heading-1-size` | -리치 본문(본문 칸·상세 본문)은 큰 본문을 따르므로 휴대폰에서 17px이 됩니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. +앱 화면의 본문 토큰(13px·15px)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 리치 본문(본문 칸·상세 본문)은 데스크톱·휴대폰 모두 16px, 줄 높이 1.7로 둡니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 리치 본문(16px), 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. ## 글꼴과 숫자 diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/frontend/references/navigation.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/frontend/references/navigation.md index 548ff19..34a170b 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/frontend/references/navigation.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/frontend/references/navigation.md @@ -1,8 +1,8 @@ # 화면 전환 -첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 바로 그리고, 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며 짧은 대기에는 진행 표시를 띄우지 않습니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. +첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신하며(stale-while-revalidate), 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 붙이지 않고 문서에 한 번 걸어 `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하고, 같은 주소는 10초에 한 번만 부릅니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며, 가리는 막을 두지 않고 150ms가 지나면 콘텐츠 카드 위쪽 테두리에만 진행 표시를 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 옮기고, 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. -지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면도 포함합니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. +지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면과 화면 안에서 지연 로딩하는 큰 부품(리치 에디터 등)도 같은 기준을 따릅니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있고, 본문 에디터가 뒤늦게 붙으면 아래 영역이 밀립니다. `React.lazy`는 코드가 이미 받아져 있어도 처음 그릴 때 한 번 멈추므로 `preload()`를 가진 래퍼로 감싸 받은 뒤에는 바로 그리게 하고, 그 부품을 쓰는 화면의 라우트 준비에서 `preload()`를 데이터와 함께 부릅니다. 배치에 영향을 주는 비동기 부품은 빈 채로 그렸다가 채우지 않습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. 본문뿐 아니라 첫 화면에 쓰는 목록·사람·선택지·연결 정보도 같은 조회 옵션으로 준비합니다. 하위 컴포넌트가 나중에 시작하는 조회와 URL 조건을 빠뜨리지 않습니다. 캐시를 채우는 loader의 라우터 캐시와 Query 캐시 수명을 따로 정해, loader가 생략된 뒤 빈 Query 캐시에서 화면이 다시 조회하지 않게 합니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/SKILL.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/SKILL.md index ff63064..46cf17e 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/SKILL.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/SKILL.md @@ -9,6 +9,8 @@ description: 웹 플랫폼의 기능 구현, 버그 수정, 구조 변경을 작 ## 시작 +시작할 때 프로젝트 AGENTS.md의 최소 판과 설치된 플러그인 판을 비교합니다. 설치된 판이 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없으면 추측으로 진행하지 않고 [이어받기 문서](references/handoff.md)를 따릅니다. + 사용자가 원하는 결과를 짧게 설명하고 기존 변경·staging 상태를 확인합니다. 제품 동작이 달라지면 관련 요구사항과 설계를 읽습니다. 기록 위치와 이슈 도구는 프로젝트가 정합니다. 연결되지 않은 도구를 필수로 가정하거나 조회만 하는 질문에 업무를 만들지 않습니다. 작업에 맞는 플레이북 하나를 읽고, 계약·스키마 변경 같은 의존 단계만 함께 수행합니다. 작은 수정은 필요한 단계만 적용합니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md index acdcad2..dfaeb41 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md @@ -4,5 +4,5 @@ 2. 다음 변경이 이 프로젝트만의 결정인지, 여러 프로젝트에 맞는 공통 개선인지 구분합니다. 당장 구분할 수 없으면 이 프로젝트의 결정으로 먼저 둡니다. 3. 이 프로젝트만의 결정은 프로젝트 AGENTS.md의 프로젝트 규칙 표에 이유와 함께 적습니다([프로젝트 규칙](../../manage-workflow/references/project-rules.md)). 경고를 늘리기 전에 자동 검사나 구조로 실수를 막을 수 있는지 검토합니다. 4. 같은 상황에서 결과가 달라지는지 확인합니다. 경미한 문구 수정에 불필요한 전체 앱 검사를 요구하지 않습니다. -5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서로 남기고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. 원본은 사용자가 요청할 때 플러그인 저장소에서 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. -6. 플러그인이 갱신되면 프로젝트 규칙 표의 `제안 중` 줄을 확인해, 원본에 반영됐으면 지우고 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 원본은 원본 저장소 세션에서 제안 이슈를 검토해 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. +6. 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/handoff.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/handoff.md new file mode 100644 index 0000000..36f0890 --- /dev/null +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/handoff.md @@ -0,0 +1,30 @@ +# 이어받기 문서 + +설치된 Webcraft 플러그인 판이 프로젝트 AGENTS.md의 최소 판보다 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없을 때는 추측으로 진행하지 않습니다. 지금까지의 작업 상태를 이어받기 문서로 남기고 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. + +세션 시작 훅이 새 판을 알려 첫 요청 전에 갱신을 제안할 때는, 진행 중인 작업이 없으면 사용자 동의를 받아 갱신 명령을 실행하고 `/reload-plugins`를 안내합니다. 작업을 시작한 뒤에 갱신이 필요해지면 이어받기 문서를 먼저 남깁니다. + +## 위치와 담는 내용 + +저장소를 더럽히지 않고 커밋에 섞이지 않도록 저장소 밖 `~/.goraebap/handoff/-<프로젝트 폴더 이름>-<짧은 영문 이름>.md`에 만듭니다. 비밀값과 접속 정보는 넣지 않습니다. + +| 항목 | 적는 내용 | +| --- | --- | +| 사용자가 원한 결과 | 요청의 목적과 완료 기준 | +| 결정과 기각한 안 | 사용자가 내린 결정, 검토했다가 기각한 안과 이유 | +| 바꾼 파일 | 지금까지 고치거나 만든 파일 경로와 커밋 여부 | +| 다음 단계 | 바로 이어서 해야 할 작업 순서 | +| 검증 상태 | 수행한 검증과 아직 못 한 검증 | +| 묻지 못한 질문 | 사용자 확인이 남은 질문 | + +## 사용자 안내와 갱신 + +문서를 남긴 뒤 사용자에게 할 일을 짧게 안내합니다. + +1. 플러그인 갱신 명령(Claude Code는 `claude plugin marketplace update dev-goraebap`과 `claude plugin update webcraft@dev-goraebap`, Codex는 `codex plugin marketplace upgrade dev-goraebap`)을 안내하거나, 사용자가 `"Webcraft 업데이트해 줘"`라고 요청하면 에이전트가 직접 실행합니다. +2. 새 판을 적용하는 `/reload-plugins` 입력 또는 새 세션 시작을 안내합니다. +3. 새 세션에 붙여 넣을 한 문장(`"~/.goraebap/handoff/<파일 이름>.md를 읽고 이어서 진행해 줘"`)을 함께 전달합니다. + +## 새 세션에서 이어받기 + +이어받기 문서를 전달받은 새 세션은 문서를 읽고 지금 저장소 상태(`git status`)와 대조한 뒤 다음 단계부터 이어서 진행합니다. 작업을 모두 이어받으면 이어받기 문서를 지워도 된다고 사용자에게 알립니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/verification.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/verification.md index da83bc7..f68af3d 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/verification.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/goraebap-mode/references/verification.md @@ -5,6 +5,7 @@ | 변경 | 확인할 것 | | --- | --- | | 화면 | 타입·빌드·프로젝트 검사, 바뀐 주 동작, 해당 화면의 로딩·빈 결과·오류 | +| 화면 전환·로딩·캐시·지연 로딩 | 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동(같은 틀 안 이동, 3초 넘게 지난 재방문, 목록 → 상세 → 뒤로 → 같은 상세)을 차례로 누르는 Playwright 점검(`layout-shift` 합 0.01 이하, 재방문 진행 표시 미노출, `aria-busy` 해제까지의 대기) | | 반응형·모양 | 데스크톱·휴대폰, 제공하는 밝게·어둡게 모드, 키보드 주 동작 | | 서버 로직 | 해당 유스케이스·권한·트랜잭션 실패, 프로젝트 정적 검사 | | API 계약 | 서버 명세와 생성 타입, 영향받는 호출자, 오류 응답 | diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/SKILL.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/SKILL.md index a45e59b..d721f3e 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/SKILL.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/SKILL.md @@ -15,7 +15,7 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | 프로젝트만 다르게 하는 규칙 추가·정리 | [프로젝트 규칙](references/project-rules.md) | | 예전 사본 방식(`.agents/skills`의 지침 사본·동기화 훅)에서 바꾸기 | [사본에서 전환](references/migrate-from-copies.md) | | 프로젝트 규칙 중 공통으로 올릴 것 찾기 | [회수](#회수) | -| 지침 제안 처리 | [지침 제안](references/proposals.md) | +| 지침 제안 올리기·검토 | [지침 제안](references/proposals.md) | ## 연결 @@ -23,12 +23,13 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 2. 예전 사본(접두사가 붙은 `*-mode`·`*-frontend` 같은 지침 폴더, `scripts/sync-skills.mjs`, 동기화 시작 훅)이 있으면 연결 대신 [사본에서 전환](references/migrate-from-copies.md)을 따릅니다. 3. AGENTS.md에 아래 "개발 지침" 블록을 넣습니다. 파일이 없으면 만들고, 있으면 기존 프로젝트 설명과 팀 규칙을 보존한 채 블록만 더합니다. 최소 판은 설치된 플러그인의 판(`plugin.json`의 `version`)으로 적습니다. 4. 기존 AGENTS.md에 지침과 다른 결정이 이미 있으면 [프로젝트 규칙](references/project-rules.md) 표로 옮깁니다. 확인할 수 없는 의도는 추정하지 말고 사용자에게 묻습니다. -5. 바꾼 파일과 남은 확인 사항을 보고합니다. +5. Claude Code 사용자에게는 이 마켓플레이스의 자동 갱신을 켤지 한 번 묻고 켜는 방법(`/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update, 또는 조직 관리 설정의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true` 추가)을 안내합니다. 사용자 설정 파일은 에이전트가 임의로 고치지 않습니다. +6. 바꾼 파일과 남은 확인 사항을 보고합니다. ```markdown ## 개발 지침 -이 프로젝트는 Webcraft 플러그인(0.7.1 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. +이 프로젝트는 Webcraft 플러그인(0.8.0 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. Webcraft 지침을 읽을 수 없거나 설치된 판이 위 판보다 낮으면 추측으로 진행하지 않습니다. 작업 상태를 이어받기 문서로 남기고, 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. ## 프로젝트 규칙 @@ -36,8 +37,8 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | --- | --- | --- | --- | ``` -표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. +플러그인이 설치되지 않았거나 꺼져 있으면 플러그인의 어떤 지침도 읽히지 않고 AGENTS.md만 읽히므로, 지침을 읽을 수 없을 때의 행동 문장까지 블록에 넣습니다. 표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. ## 회수 -프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서를 만들고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. +프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시를 붙이지 않고, 플러그인이 갱신되면 닫힌 이슈를 보고 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/project-rules.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/project-rules.md index 60b7cf8..a411420 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/project-rules.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/project-rules.md @@ -30,5 +30,5 @@ AGENTS.md의 "프로젝트 규칙" 표에 결정 하나를 한 줄로 적습니 - 지침과 같은 내용은 적지 않습니다. 같으면 지침이 이미 말하고 있습니다. - 지침에 없는 이 프로젝트만의 사실(제품 용어, 도메인 규칙, 실행 명령)은 표가 아니라 AGENTS.md의 프로젝트 설명에 씁니다. - 플러그인을 갱신한 뒤 지침이 프로젝트 규칙과 같아졌으면 그 줄을 지웁니다. 지침이 바뀌어 규칙의 "지침의 기준" 칸이 틀려졌으면 고칩니다. -- 이유 칸 끝의 `제안 중: <제안 파일 이름>`은 공통 지침 수정을 제안해 둔 항목이라는 뜻입니다. 플러그인을 갱신한 뒤 반영됐으면 그 줄을 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +- 공통 지침 수정을 제안한 항목도 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 플러그인을 갱신한 뒤 닫힌 이슈를 보고 반영됐으면 같은 줄을 지우고, 버려졌으면 예외로 남깁니다. - 다른 프로젝트에도 맞는 규칙은 [지침 제안](proposals.md) 대상입니다. diff --git a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/proposals.md b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/proposals.md index ebd7bf5..3982a3f 100644 --- a/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/proposals.md +++ b/plugins/webcraft/dist/claude/plugins/webcraft/skills/manage-workflow/references/proposals.md @@ -1,17 +1,18 @@ # 지침 제안 -프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 로컬 제안함(`~/.goraebap/proposals/`)으로 처리합니다. GitHub 이슈는 저장소가 공개라 회사 프로젝트 작업의 근거가 공개될 위험이 있어 쓰지 않고, 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않습니다. +프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 제안자가 누구든 받아들일지는 플러그인 관리자가 정합니다. 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않고, 로컬 제안함은 다른 사용자의 제안을 받을 수 없어 쓰지 않습니다. ## 흐름 1. 프로젝트 작업 중 요청이 지침과 부딪히면 에이전트가 그때만 한 번 묻습니다. 선택지는 "이 프로젝트만 예외", "지침을 고치자(제안)", "지침대로 하기"입니다. 지침과 부딪히지 않으면 묻지 않습니다. 2. "이 프로젝트만 예외"를 고르면 프로젝트 AGENTS.md의 [프로젝트 규칙](project-rules.md) 표에 한 줄로 적습니다. -3. "지침을 고치자(제안)"을 고르면 작업은 멈추지 않습니다. 프로젝트 규칙 표에 그 줄을 적되 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙이고, `~/.goraebap/proposals/`에 제안서 한 장을 만듭니다. -4. 제안서 파일은 `~/.goraebap/proposals/-<짧은 영문 이름>.md`로 만듭니다. 제안은 공개 저장소의 지침이 될 수 있으므로 회사 제품명·고객·업무 데이터·접속 정보·사내 주소는 일반 표현으로 바꿔 씁니다. -5. 제안 처리는 Webcraft 저장소 세션에서 사용자가 요청할 때 수행합니다. 제안함의 제안서를 하나씩 보이고 사용자가 받아들일지, 고쳐서 받을지, 버릴지 고릅니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. 처리한 제안서는 `~/.goraebap/proposals/done/`으로 옮기고 결과(반영한 판, 버린 이유)를 끝에 적습니다. -6. 프로젝트는 플러그인이 갱신되면 `제안 중` 줄을 확인합니다. 반영됐으면 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +3. "지침을 고치자(제안)"을 고르면 프로젝트 작업은 멈추지 않습니다. 프로젝트에는 프로젝트 규칙 표에 지속 규칙 한 줄만 남기고 이슈 번호나 제안 표시는 붙이지 않습니다. +4. 이슈 본문은 일반화합니다. 제품명·고객·사내 주소·업무 데이터를 쓰지 않고, 제안한 프로젝트 이름 대신 프로젝트 종류(예: 사내 업무 앱)만 씁니다. +5. 공개 게시라 되돌리기 어려우므로 에이전트는 이슈 본문 초안을 사용자에게 보이고 승인을 받은 뒤 올립니다. 에이전트가 이슈를 올릴 수 있으면(`gh`가 있고 로그인됨) 승인받아 `gh issue create --repo dev-goraebap/dev.goraebap`로 올립니다. 올릴 수 없으면 같은 형식의 이슈 문서를 `~/.goraebap/issues/-<짧은 영문 이름>.md`로 만들고, 새 이슈 페이지 주소(`https://github.com/dev-goraebap/dev.goraebap/issues/new`)와 함께 "이 내용을 붙여 넣어 올려 주세요"라고 안내합니다. +6. 검토와 반영은 원본 저장소 세션에서 합니다. 열린 제안 이슈를 하나씩 보고 받아들이기·고쳐서 받기·버리기를 정한 뒤, 결과(반영한 판, 버린 이유)를 이슈에 남기고 닫습니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. +7. 프로젝트는 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. -기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 이 제안함으로 제안서를 만드는 절차를 따릅니다. +기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 같은 절차로 이슈를 올립니다. ## 제안서 형식 @@ -21,5 +22,5 @@ | 지금 규칙 | 현재 지침이 정한 기준 한 문장 | | 바꿀 규칙 | 새로 바꿀 기준 한 문장 | | 이유와 근거 | 프로젝트에서 일어난 일과 바꿀 이유 | -| 제안한 프로젝트 이름 | 제안을 만든 프로젝트 이름 | +| 프로젝트 종류 | 제안을 만든 프로젝트의 종류(예: 사내 업무 앱) | | 날짜 | 제안한 날짜(`YYYY-MM-DD`) | diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/.codex-plugin/plugin.json b/plugins/webcraft/dist/codex/plugins/webcraft/.codex-plugin/plugin.json index 9cb8612..903f00d 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/.codex-plugin/plugin.json +++ b/plugins/webcraft/dist/codex/plugins/webcraft/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "webcraft", - "version": "0.7.2", + "version": "0.8.0", "description": "웹 플랫폼 개발 지침(프론트·디자인 시스템·백엔드·API·DB·아키텍처)과 프로젝트 연결", "author": { "name": "dev-goraebap" diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/README.md b/plugins/webcraft/dist/codex/plugins/webcraft/README.md index 7b11414..3365b55 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/README.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/README.md @@ -26,10 +26,29 @@ codex plugin add webcraft@dev-goraebap ## 갱신 -플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있으면 먼저 갱신합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. +플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있거나 지침을 읽을 수 없으면 에이전트는 추측으로 진행하지 않고, 작업 상태를 `~/.goraebap/handoff/` 아래 이어받기 문서로 남긴 뒤 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 안내합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. + +Claude Code에서 자동 갱신은 공식 마켓플레이스가 아니면 기본값이 꺼짐입니다. 사용자가 `/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update로 켜거나, 조직 관리자가 managed settings의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true`를 넣습니다. 자동 갱신은 세션 시작 때가 아니라 첫 메시지 뒤 최대 10분 안에 돌며, 실행 중인 세션은 옛 판을 유지하고 `/reload-plugins`를 입력하거나 다음에 실행할 때 새 판이 적용됩니다. + +Claude Code 배포본에는 세션 시작 훅(`hooks/hooks.json`, `hooks/update-check.mjs`)이 들어갑니다. 새로 시작할 때만(`startup`) 돌며, 하루 한 번 원본 저장소의 최신 판을 받아 `~/.goraebap/cache/webcraft-latest.json`에 기억합니다. 설치된 판보다 높으면 `Webcraft 새 판이 나왔습니다(0.8.0 → 0.8.1). 에이전트에게 "Webcraft 업데이트해 줘"라고 하면 갱신합니다.` 한 줄을 보이고, 에이전트가 첫 요청 전에 한 번 갱신을 제안합니다. 동의하면 아래 수동 갱신 명령을 실행한 뒤 `/reload-plugins`를 안내하고, 거절하면 다시 묻지 않습니다. 네트워크 실패나 Node가 없는 경우에는 조용히 넘어갑니다. Codex 배포본에는 훅이 없습니다. + +직접 갱신할 때는 다음 명령을 실행하고, Claude Code에서는 `/reload-plugins`를 입력하거나 새 세션을 엽니다. + +Claude Code: + +```text +claude plugin marketplace update dev-goraebap +claude plugin update webcraft@dev-goraebap +``` + +Codex: + +```text +codex plugin marketplace upgrade dev-goraebap +``` ## 이전 이름에서 전환 이전 식별자는 `goraebap-workflow@goraebap-workflow`입니다. 새 이름은 `webcraft@dev-goraebap`이며 입구 스킬 이름은 `goraebap-mode`를 유지합니다. 기존 프로젝트는 새 플러그인 설치를 확인한 뒤 AGENTS.md 연결을 새 이름과 최소 판 `0.7.1`으로 바꿉니다. 기존 사본에서 바꾼 규칙과 제품 전용 스킬은 보존합니다. 옛 설치를 제거하는 작업은 새 설치 확인 후 사용자가 허용한 범위에서 수행합니다. -역할별 모델 설정 `~/.claude/rules/goraebap-models.md`와 로컬 제안함 `~/.goraebap/proposals/`는 기존 경로를 유지합니다. +지침 제안은 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 역할별 모델 설정 `~/.claude/rules/goraebap-models.md`는 기존 경로를 유지하며, `~/.goraebap/`에는 최신 판 확인 캐시(`cache/`), 이어받기 문서(`handoff/`), 직접 올리지 못한 이슈 문서(`issues/`)가 남습니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/package.json b/plugins/webcraft/dist/codex/plugins/webcraft/package.json index 323d44d..e72b43b 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/package.json +++ b/plugins/webcraft/dist/codex/plugins/webcraft/package.json @@ -1,6 +1,6 @@ { "name": "webcraft", - "version": "0.7.2", + "version": "0.8.0", "private": true, "type": "module" } diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/api/SKILL.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/api/SKILL.md index c9a5163..9850efb 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/api/SKILL.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/api/SKILL.md @@ -13,7 +13,7 @@ description: 웹 플랫폼의 HTTP 계약, 오류 응답, 인증·권한, 첨부 - 인증된 사용자와 자원 접근 권한을 서버에서 검사합니다. 숨겨야 하는 대상의 존재 노출 정책을 프로젝트에서 정하고 REST·MCP·다운로드에 동일하게 적용합니다. - PATCH의 생략·null·값 전달 의미를 구분합니다. 업무 날짜·지역 시각·절대 시각을 구별하고 절대 시각은 UTC로 전달합니다. 업무 시간대는 프로젝트가 정합니다. - 목록은 서버 필터·안정된 정렬·페이지/커서 계약을 명시합니다. count와 목록이 같은 조건을 쓰는지 확인합니다. -- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. +- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. 사진 업로드가 선택 값인 대표색·가로·세로를 함께 보내면 파일과 함께 저장하고 첨부 정보로 돌려줍니다. - 변경은 버전 충돌과 명령 재시도 시 중복 효과를 검토합니다. 브라우저 캐시 무효화가 동시 편집 충돌을 해결한다고 가정하지 않습니다. 완료 시 성공 응답·실패 응답·권한·영향받는 호출자와 생성 타입을 확인합니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/composed-components.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/composed-components.md index 0da0d8a..249a89c 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/composed-components.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/composed-components.md @@ -22,6 +22,8 @@ 주소가 없는 시트는 브라우저의 닫기 요청(CloseWatcher와 같은 장치)으로 안드로이드 뒤로가기와 Esc에 닫힙니다. 네이티브 ``를 `showModal()`로 여는 시트(Astryx BottomSheet의 기본, 배경 막 있음)는 지원 브라우저에서 이 요청을 `cancel`로 이미 받으므로 따로 등록하지 않습니다. 겹쳐 등록하면 한 번에 두 번 닫힙니다. 직접 만든 시트나 배경 막 없는 시트만 CloseWatcher를 등록합니다. 외부 제어 API가 시트 열림을 주소 쿼리에 저장하라는 규칙은 아니며, 열림 상태를 쿼리에 두지 않습니다. 짧은 조작마다 방문 기록이 쌓이고 새로고침하면 시트가 다시 열리기 때문입니다. 닫기 요청을 지원하지 않는 브라우저(iOS 사파리 등)에서는 뒤로가기가 이전 페이지로 갈 수 있으므로, 시트마다 보이는 닫기 버튼을 두고 잃으면 아까운 입력은 시트에 담지 않습니다. 입력이 길면 주소가 있는 화면으로 엽니다. 시트는 쌓지 않습니다. +팝오버와 바텀시트는 열렸을 때만 내용을 만듭니다. 행마다 붙는 메뉴의 확인 대화상자는 그 동작을 고를 때만 만듭니다. 닫힌 메뉴 내용과 행마다 미리 만든 확인 대화상자(``)를 화면 이동 때 한꺼번에 정리하면 메인 스레드가 멈춥니다. 행마다 권한 판단에 필요한 데이터는 메뉴에 포인터나 초점이 갈 때 미리 읽습니다. + ## 선택지 목록 작은 고정 선택지는 그대로 나열하고, 8개를 넘으면 위에 검색 칸을 두어 초점을 줍니다. 검색 전에는 처음 8개와 이미 고른 항목, 검색하면 최대 50개, 나머지는 개수로 알립니다. 전부 렌더링하지 않습니다. 이 수치는 서버 한도와 별개이며 프로젝트가 조절합니다. 하나 고르기는 고른 뒤 닫고 끝에 체크, 여럿 고르기는 열린 채 앞에 상자를 둡니다. 휴대폰의 동작·선택·시간 목록은 최소 48px 행에 15px 글자·20px 아이콘을 씁니다. 보조 정보는 13px입니다. 메뉴를 시트로 옮기는 것만으로 데스크톱 밀도가 자동으로 바뀐다고 가정하지 않습니다. @@ -44,6 +46,6 @@ ## 본문 칸 -제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 본문 글자는 화면 글자(13px)보다 한 단계 큰 본문(15px, `--text-large-size`)이고 제목은 `--text-heading-2-size`입니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. +제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 앱 화면의 본문 토큰(13px, 15px `--text-large-size`)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 본문 칸과 상세 본문 글자는 데스크톱·휴대폰 모두 16px, 줄 높이 1.7이며 제목은 `--text-heading-2-size`입니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. 구현이 없는 조합을 메뉴에 빈 항목으로 만들지 않습니다. 제품 고유 조합(아이콘 표시 고르기, 전역 검색 팔레트, 프로필 패널)은 프로젝트가 소유합니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/data-states.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/data-states.md index 8bbfaff..f4f6b7b 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/data-states.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/data-states.md @@ -16,17 +16,21 @@ ## 화면을 바꾸는 동안 -캐시가 없는 화면의 데이터와 코드가 준비될 때까지 이전 화면을 유지합니다. 지연이 짧으면 표시를 생략하고, 길어지면 앱 셸을 그대로 둔 채 콘텐츠 영역에 불투명한 막을 표시합니다. 막과 새 본문을 함께 흐리게 만들지 않습니다. 새 화면의 그리기와 긴 작업이 끝났는지 확인한 뒤 잠깐 안정화 시간을 두고 막만 걷습니다. 숨은 탭이나 프레임이 멈춘 경우에도 끝나는 시간 제한과 취소 처리를 둡니다. +앱 첫 진입만 첫 방문 덮개(스플래시 화면)로 덮고, 그 뒤의 화면 이동에는 본문을 가리는 불투명한 막을 두지 않습니다. 화면 전체 스켈레톤은 모양이 바뀌는 본문과 높이가 맞지 않고, 영역별 덮개는 같은 판정이 화면마다 늘어나며, 콘텐츠 막은 상세 안 사이드바까지 덮고 재방문에서도 떠 쓰지 않습니다. -기본 시작값은 표시 지연 100ms, 한 번 표시한 뒤 최소 300ms, 그리기 확인 뒤 안정화 200ms, 막 해제 700ms입니다. 제품에 확정된 값이 있으면 그 값을 따릅니다. 최소 표시 시간만 지났다고 새 본문의 레이아웃이 안정되기 전에 막을 걷지 않습니다. +캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신합니다(stale-while-revalidate). 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 먼저 옮깁니다. -캐시가 있는 화면의 전환 효과는 콘텐츠에만 적용합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 붙인 콘텐츠만 캡처합니다. 로딩 막이 보이거나 걷히는 동안에는 겹치지 않습니다. 동작 줄이기 설정을 따르고, 빠른 연속 이동에서 진행 중인 전환을 겹쳐 실행하지 않습니다. +앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 따로 붙이지 않고 문서에 한 번 걸어, `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하며 같은 주소는 10초에 한 번만 부릅니다. + +캐시가 없어 기다릴 때는 이전 화면을 그대로 두고, 150ms가 지나면 콘텐츠 카드 위쪽 테두리에 테마 강조색이 흐르는 진행 표시만 띄웁니다. 진행 표시는 높이 2px로 카드 위에 겹쳐 그리고 카드 모서리에 맞춰 잘라 배치를 밀지 않습니다. 150ms 안에 끝나면 띄우지 않고, 한 번 띄웠으면 최소 300ms 둔 뒤 새 화면과 함께 바로 내립니다. 그리기 확인 후 천천히 걷는 대기를 진행 표시에 쓰면 이동이 끝난 뒤에도 남으므로 쓰지 않습니다. + +준비된 화면끼리는 콘텐츠 영역만 200ms 교차 페이드합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 콘텐츠 영역 하나에만 붙입니다. 앞뒤 화면에서 같은 부분(사이드바 등)은 같은 픽셀끼리 겹쳐 그대로 있는 듯 보이므로 화면별 예외를 두지 않습니다. TanStack Router는 loader가 끝난 뒤 View Transition을 시작하므로 준비하는 동안 진행 표시가 멈추지 않습니다. 동작 줄이기 설정, 로그인 오가기, 진행 중인 전환, 열린 대화상자에서는 전환하지 않습니다. ## 대화상자의 데이터 준비 준비 중에는 이름 있는 원형 Spinner를 보이고, 필요한 데이터가 준비되면 내용을 한 번에 표시합니다. 바깥 창과 준비 영역의 최소 크기를 유지해 작은 창에서 갑자기 커지지 않게 합니다. 준비 확인 Promise가 새로 생겼다는 이유만으로 캐시에 있는 내용을 가리지 않습니다. 필요한 조회가 실제 캐시에 남아 있는지 확인하며, 한 번 표시한 로딩에는 최소 표시 시간을 적용합니다. 실패는 무한 로딩 대신 실패 상태와 다시 시도로 끝납니다. -확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 애니메이션 호출 여부뿐 아니라 앱 셸·본문이 숨는 프레임과 로딩 막의 범위도 확인합니다. +확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 화면 전환·로딩·캐시·지연 로딩 부품을 바꾸면, 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동을 차례로 누르는 Playwright 점검을 돌립니다. 단계에는 같은 틀 안 이동, 3초 넘게 지난 페이지로 다시 방문, 목록 → 상세 → 뒤로 → 같은 상세를 넣고, 새 종류의 화면을 만들면 단계를 더합니다. 단계마다 누른 뒤의 레이아웃 이동 합(`layout-shift`, 입력 직후 제외)과 새 화면으로 교체가 끝날 때까지의 대기를 잽니다. 진행 표시는 데이터 속성으로 감지하고, 대기는 주소 변경 시각이 아니라 준비 중 표시(`aria-busy`)가 풀릴 때까지로 잽니다. 이동이 0.01을 넘거나, 캐시가 있어야 하는 다시 방문에서 진행 표시가 뜨면 실패입니다. ## 상태 화면 @@ -52,7 +56,9 @@ ## 캐시와 전환 -첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 180ms가 지나도 준비되지 않을 때만 진행 표시를 덮습니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. 이미지는 자리를 먼저 확보해 내용이 밀리지 않게 합니다. +첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 150ms가 지나도 준비되지 않을 때만 콘텐츠 카드 위쪽 테두리에 진행 표시를 띄웁니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. + +사진을 올릴 때는 브라우저가 대표색과 원본 가로·세로를 재어 업로드 요청에 함께 보냅니다(64px로 줄여 색을 묶어 세고, 가장 많은 묶음의 평균색). 측정은 한 번에 하나씩, 3초 제한이며, 실패하면 측정값 없이 올립니다. 서버는 파일과 함께 저장해 첨부 정보로 돌려주고, 리치 본문은 사진 블록 속성에도 넣습니다. 보여 줄 때는 원본 비율과 대표색으로 자리를 먼저 칠해 내용이 밀리지 않게 하고, 받아야 하는 사진만 0.4초에 걸쳐 나타냅니다. 이 페이지에서 이미 받았거나 브라우저 캐시에 있는 사진은 바로 보입니다. 화면 전환 효과는 준비된 내용 사이의 짧은 교차 페이드만 쓰고 동작 줄이기 설정에서는 끕니다. 스켈레톤은 실제 배치와 같을 때만 씁니다. 키트 예제는 대기 상태를 확인하기 위해 페이지 안의 진행 표시를 씁니다. @@ -64,4 +70,5 @@ | 양식 저장 | 양식 위 배너(`role="alert"`), 입력은 지킵니다 | | 페이지 전체(변경 실패·충돌·읽기 전용) | 머리 아래 배너 줄 | | 화면에 결과가 보이지 않는 짧은 완료(주소 복사, 보관, 백그라운드 작업 시작) | 토스트. 되돌릴 수 있으면 "되돌리기" 하나를 두고 충분히 오래 남깁니다. 오류·경고·되돌릴 수 없는 결과·다음 행동이 필요한 내용은 토스트에 두지 않습니다. 결과가 화면에 바로 보이면 알리지 않습니다 | +| 상세에서 저장 버튼 없이 바로 저장되는 속성 변경(상태·담당·날짜·라벨·우선순위 등) | 결과가 화면에 보여도 "되돌리기" 토스트를 5초 띄웁니다. 토스트 아래 2px 막대가 남은 시간만큼 줄어들고 포인터가 올라가 있으면 멈춥니다. 되돌리기는 서버 최신본을 다시 읽은 뒤 바꾼 속성만 이전 값으로 저장해, 그 사이 다른 사람의 변경을 덮지 않습니다 | | 화면을 그릴 수 없는 오류 | 라우트 오류 화면과 다시 시도 | diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/icons.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/icons.md index a7532cf..35fbd12 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/icons.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/icons.md @@ -12,6 +12,8 @@ 모바일에서 아이콘 버튼의 누르는 영역은 최소 44px입니다. 프로필 그림은 28~32px로 두고 그 바깥에서 같은 누르는 영역을 확보합니다. 누르는 영역을 넓히려고 그림을 44~48px로 키우지 않습니다. 반대로 버튼 상자만 넓히고 안의 아이콘을 데스크톱 크기에 두지도 않습니다. Astryx가 아이콘 칸을 고정하면 SVG가 줄어들지 않는지 실제 크기와 중심을 확인합니다. 머리 줄의 아이콘 규칙이 그 안에서 열리는 시트·메뉴까지 번지지 않도록 범위를 제한합니다. +속성 열·활동 기록처럼 아이콘이 세로로 늘어선 곳은 상자 크기뿐 아니라 24 격자 위에 그려진 범위도 비슷하게 맞춥니다(대부분 20칸 안팎). 범위가 눈에 띄게 작은 아이콘(16칸 정도)은 키우지 않고 같은 뜻의 다른 Solar 아이콘으로 바꿉니다. 키우면 선도 굵어지기 때문입니다. 확인은 화면에서 SVG의 `getBBox()`를 재서 합니다. 기본 상태의 줄에 Line Duotone처럼 일부 선이 흐린 형태를 섞지 않습니다. 같은 색이어도 흐리고 작게 보입니다. + ## 고르는 기준 | 상황 | 아이콘 | diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/layout-patterns.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/layout-patterns.md index a6a3745..1e1c8cf 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/layout-patterns.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/layout-patterns.md @@ -33,7 +33,9 @@ 본문이 있는 대상의 상세([DetailLayout](https://github.com/dev-goraebap/dev.goraebap/blob/main/site/src/shared/ui/layouts/detail/DetailLayout.tsx))는 최대 45rem 읽기 열에 소속·제목·본문·이어지는 것(하위 항목·활동)을 둡니다. 속성은 넓으면 오른쪽 16rem 열에 묶음 이름과 함께(스크롤해도 보입니다), 좁으면 제목과 본문 사이의 줄바꿈되는 알약으로 옮깁니다. 같은 요소를 옮기므로 속성을 두 벌 렌더링하지 않습니다. 창 폭이 아니라 컨테이너 폭(60rem)으로 판단해 대화상자·패널에서도 맞게 바뀝니다. 긴 제목·주소는 줄을 넘겨 가로로 넘치지 않습니다. 상세와 작성 면은 점 무늬 없이 평평합니다. -본문 편집 도구(편집·저장)는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. +본문이 있는 모든 상세는 속성 열을 같은 순서로 둡니다. 본문에 제목이 있으면 맨 위에 "목차" 묶음, 그 아래 대상별 속성, 맨 끝에 "정보" 묶음(작성자 아바타·작성일·수정일과 고친 사람·버전 수)을 두고, 모르는 값은 줄째 뺍니다. 목차와 버전 줄도 다른 속성과 같은 속성 행 부품으로 그려 아이콘·글자 자리를 맞춥니다. 누르면 목차는 테두리 있는 팝오버, 버전은 버전 기록 대화상자를 엽니다. 휴대폰은 목차를 오른쪽 아래 떠 있는 둥근 버튼과 바텀시트로 둡니다. 편집기 가장자리에 목차 표시를 붙이지 않습니다. 상세의 더 보기(…) 메뉴는 페이지 머리 줄 한 곳에 둡니다. + +본문 편집 도구는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. 데스크톱 머리 줄의 편집 전환·편집 마침·버전 기록은 필터·정렬과 같은 둥근 아이콘 도구(`variant="tool"`, 아이콘만, 툴팁에 이름)로 두며, "편집" 같은 글자 버튼을 머리 줄에 두지 않습니다. 저장은 상태가 바뀌는 동작이라 글자를 유지하고, 휴대폰은 바닥 줄의 저장·완료를 그대로 둡니다. 버전 기록 목록은 표준 목록 행(아이콘·시각·설명, 선택 표시)으로 그리고, 주소를 가진 대화상자(라우트 마스킹 `…/history`, 새로 고침하면 다시 열림)로 엽니다. ## 설정 diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/typography.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/typography.md index 1764370..fafbd92 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/typography.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/design-system/references/typography.md @@ -9,7 +9,7 @@ | 큰 본문·작은 제목 | 15px, 제목은 600 | 17px, 제목은 600 | `--text-large-size`, `--text-heading-3-*` | | 페이지 제목(상세·설정·작성 페이지) | 24px | 24px | `--text-heading-1-size` | -리치 본문(본문 칸·상세 본문)은 큰 본문을 따르므로 휴대폰에서 17px이 됩니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. +앱 화면의 본문 토큰(13px·15px)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 리치 본문(본문 칸·상세 본문)은 데스크톱·휴대폰 모두 16px, 줄 높이 1.7로 둡니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 리치 본문(16px), 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. ## 글꼴과 숫자 diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/frontend/references/navigation.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/frontend/references/navigation.md index 548ff19..34a170b 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/frontend/references/navigation.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/frontend/references/navigation.md @@ -1,8 +1,8 @@ # 화면 전환 -첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 바로 그리고, 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며 짧은 대기에는 진행 표시를 띄우지 않습니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. +첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신하며(stale-while-revalidate), 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 붙이지 않고 문서에 한 번 걸어 `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하고, 같은 주소는 10초에 한 번만 부릅니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며, 가리는 막을 두지 않고 150ms가 지나면 콘텐츠 카드 위쪽 테두리에만 진행 표시를 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 옮기고, 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. -지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면도 포함합니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. +지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면과 화면 안에서 지연 로딩하는 큰 부품(리치 에디터 등)도 같은 기준을 따릅니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있고, 본문 에디터가 뒤늦게 붙으면 아래 영역이 밀립니다. `React.lazy`는 코드가 이미 받아져 있어도 처음 그릴 때 한 번 멈추므로 `preload()`를 가진 래퍼로 감싸 받은 뒤에는 바로 그리게 하고, 그 부품을 쓰는 화면의 라우트 준비에서 `preload()`를 데이터와 함께 부릅니다. 배치에 영향을 주는 비동기 부품은 빈 채로 그렸다가 채우지 않습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. 본문뿐 아니라 첫 화면에 쓰는 목록·사람·선택지·연결 정보도 같은 조회 옵션으로 준비합니다. 하위 컴포넌트가 나중에 시작하는 조회와 URL 조건을 빠뜨리지 않습니다. 캐시를 채우는 loader의 라우터 캐시와 Query 캐시 수명을 따로 정해, loader가 생략된 뒤 빈 Query 캐시에서 화면이 다시 조회하지 않게 합니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/SKILL.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/SKILL.md index ff63064..46cf17e 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/SKILL.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/SKILL.md @@ -9,6 +9,8 @@ description: 웹 플랫폼의 기능 구현, 버그 수정, 구조 변경을 작 ## 시작 +시작할 때 프로젝트 AGENTS.md의 최소 판과 설치된 플러그인 판을 비교합니다. 설치된 판이 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없으면 추측으로 진행하지 않고 [이어받기 문서](references/handoff.md)를 따릅니다. + 사용자가 원하는 결과를 짧게 설명하고 기존 변경·staging 상태를 확인합니다. 제품 동작이 달라지면 관련 요구사항과 설계를 읽습니다. 기록 위치와 이슈 도구는 프로젝트가 정합니다. 연결되지 않은 도구를 필수로 가정하거나 조회만 하는 질문에 업무를 만들지 않습니다. 작업에 맞는 플레이북 하나를 읽고, 계약·스키마 변경 같은 의존 단계만 함께 수행합니다. 작은 수정은 필요한 단계만 적용합니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md index acdcad2..dfaeb41 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md @@ -4,5 +4,5 @@ 2. 다음 변경이 이 프로젝트만의 결정인지, 여러 프로젝트에 맞는 공통 개선인지 구분합니다. 당장 구분할 수 없으면 이 프로젝트의 결정으로 먼저 둡니다. 3. 이 프로젝트만의 결정은 프로젝트 AGENTS.md의 프로젝트 규칙 표에 이유와 함께 적습니다([프로젝트 규칙](../../manage-workflow/references/project-rules.md)). 경고를 늘리기 전에 자동 검사나 구조로 실수를 막을 수 있는지 검토합니다. 4. 같은 상황에서 결과가 달라지는지 확인합니다. 경미한 문구 수정에 불필요한 전체 앱 검사를 요구하지 않습니다. -5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서로 남기고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. 원본은 사용자가 요청할 때 플러그인 저장소에서 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. -6. 플러그인이 갱신되면 프로젝트 규칙 표의 `제안 중` 줄을 확인해, 원본에 반영됐으면 지우고 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 원본은 원본 저장소 세션에서 제안 이슈를 검토해 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. +6. 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/handoff.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/handoff.md new file mode 100644 index 0000000..36f0890 --- /dev/null +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/handoff.md @@ -0,0 +1,30 @@ +# 이어받기 문서 + +설치된 Webcraft 플러그인 판이 프로젝트 AGENTS.md의 최소 판보다 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없을 때는 추측으로 진행하지 않습니다. 지금까지의 작업 상태를 이어받기 문서로 남기고 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. + +세션 시작 훅이 새 판을 알려 첫 요청 전에 갱신을 제안할 때는, 진행 중인 작업이 없으면 사용자 동의를 받아 갱신 명령을 실행하고 `/reload-plugins`를 안내합니다. 작업을 시작한 뒤에 갱신이 필요해지면 이어받기 문서를 먼저 남깁니다. + +## 위치와 담는 내용 + +저장소를 더럽히지 않고 커밋에 섞이지 않도록 저장소 밖 `~/.goraebap/handoff/-<프로젝트 폴더 이름>-<짧은 영문 이름>.md`에 만듭니다. 비밀값과 접속 정보는 넣지 않습니다. + +| 항목 | 적는 내용 | +| --- | --- | +| 사용자가 원한 결과 | 요청의 목적과 완료 기준 | +| 결정과 기각한 안 | 사용자가 내린 결정, 검토했다가 기각한 안과 이유 | +| 바꾼 파일 | 지금까지 고치거나 만든 파일 경로와 커밋 여부 | +| 다음 단계 | 바로 이어서 해야 할 작업 순서 | +| 검증 상태 | 수행한 검증과 아직 못 한 검증 | +| 묻지 못한 질문 | 사용자 확인이 남은 질문 | + +## 사용자 안내와 갱신 + +문서를 남긴 뒤 사용자에게 할 일을 짧게 안내합니다. + +1. 플러그인 갱신 명령(Claude Code는 `claude plugin marketplace update dev-goraebap`과 `claude plugin update webcraft@dev-goraebap`, Codex는 `codex plugin marketplace upgrade dev-goraebap`)을 안내하거나, 사용자가 `"Webcraft 업데이트해 줘"`라고 요청하면 에이전트가 직접 실행합니다. +2. 새 판을 적용하는 `/reload-plugins` 입력 또는 새 세션 시작을 안내합니다. +3. 새 세션에 붙여 넣을 한 문장(`"~/.goraebap/handoff/<파일 이름>.md를 읽고 이어서 진행해 줘"`)을 함께 전달합니다. + +## 새 세션에서 이어받기 + +이어받기 문서를 전달받은 새 세션은 문서를 읽고 지금 저장소 상태(`git status`)와 대조한 뒤 다음 단계부터 이어서 진행합니다. 작업을 모두 이어받으면 이어받기 문서를 지워도 된다고 사용자에게 알립니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/verification.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/verification.md index da83bc7..f68af3d 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/verification.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/goraebap-mode/references/verification.md @@ -5,6 +5,7 @@ | 변경 | 확인할 것 | | --- | --- | | 화면 | 타입·빌드·프로젝트 검사, 바뀐 주 동작, 해당 화면의 로딩·빈 결과·오류 | +| 화면 전환·로딩·캐시·지연 로딩 | 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동(같은 틀 안 이동, 3초 넘게 지난 재방문, 목록 → 상세 → 뒤로 → 같은 상세)을 차례로 누르는 Playwright 점검(`layout-shift` 합 0.01 이하, 재방문 진행 표시 미노출, `aria-busy` 해제까지의 대기) | | 반응형·모양 | 데스크톱·휴대폰, 제공하는 밝게·어둡게 모드, 키보드 주 동작 | | 서버 로직 | 해당 유스케이스·권한·트랜잭션 실패, 프로젝트 정적 검사 | | API 계약 | 서버 명세와 생성 타입, 영향받는 호출자, 오류 응답 | diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/SKILL.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/SKILL.md index a45e59b..d721f3e 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/SKILL.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/SKILL.md @@ -15,7 +15,7 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | 프로젝트만 다르게 하는 규칙 추가·정리 | [프로젝트 규칙](references/project-rules.md) | | 예전 사본 방식(`.agents/skills`의 지침 사본·동기화 훅)에서 바꾸기 | [사본에서 전환](references/migrate-from-copies.md) | | 프로젝트 규칙 중 공통으로 올릴 것 찾기 | [회수](#회수) | -| 지침 제안 처리 | [지침 제안](references/proposals.md) | +| 지침 제안 올리기·검토 | [지침 제안](references/proposals.md) | ## 연결 @@ -23,12 +23,13 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 2. 예전 사본(접두사가 붙은 `*-mode`·`*-frontend` 같은 지침 폴더, `scripts/sync-skills.mjs`, 동기화 시작 훅)이 있으면 연결 대신 [사본에서 전환](references/migrate-from-copies.md)을 따릅니다. 3. AGENTS.md에 아래 "개발 지침" 블록을 넣습니다. 파일이 없으면 만들고, 있으면 기존 프로젝트 설명과 팀 규칙을 보존한 채 블록만 더합니다. 최소 판은 설치된 플러그인의 판(`plugin.json`의 `version`)으로 적습니다. 4. 기존 AGENTS.md에 지침과 다른 결정이 이미 있으면 [프로젝트 규칙](references/project-rules.md) 표로 옮깁니다. 확인할 수 없는 의도는 추정하지 말고 사용자에게 묻습니다. -5. 바꾼 파일과 남은 확인 사항을 보고합니다. +5. Claude Code 사용자에게는 이 마켓플레이스의 자동 갱신을 켤지 한 번 묻고 켜는 방법(`/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update, 또는 조직 관리 설정의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true` 추가)을 안내합니다. 사용자 설정 파일은 에이전트가 임의로 고치지 않습니다. +6. 바꾼 파일과 남은 확인 사항을 보고합니다. ```markdown ## 개발 지침 -이 프로젝트는 Webcraft 플러그인(0.7.1 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. +이 프로젝트는 Webcraft 플러그인(0.8.0 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. Webcraft 지침을 읽을 수 없거나 설치된 판이 위 판보다 낮으면 추측으로 진행하지 않습니다. 작업 상태를 이어받기 문서로 남기고, 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. ## 프로젝트 규칙 @@ -36,8 +37,8 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | --- | --- | --- | --- | ``` -표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. +플러그인이 설치되지 않았거나 꺼져 있으면 플러그인의 어떤 지침도 읽히지 않고 AGENTS.md만 읽히므로, 지침을 읽을 수 없을 때의 행동 문장까지 블록에 넣습니다. 표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. ## 회수 -프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서를 만들고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. +프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시를 붙이지 않고, 플러그인이 갱신되면 닫힌 이슈를 보고 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/project-rules.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/project-rules.md index 60b7cf8..a411420 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/project-rules.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/project-rules.md @@ -30,5 +30,5 @@ AGENTS.md의 "프로젝트 규칙" 표에 결정 하나를 한 줄로 적습니 - 지침과 같은 내용은 적지 않습니다. 같으면 지침이 이미 말하고 있습니다. - 지침에 없는 이 프로젝트만의 사실(제품 용어, 도메인 규칙, 실행 명령)은 표가 아니라 AGENTS.md의 프로젝트 설명에 씁니다. - 플러그인을 갱신한 뒤 지침이 프로젝트 규칙과 같아졌으면 그 줄을 지웁니다. 지침이 바뀌어 규칙의 "지침의 기준" 칸이 틀려졌으면 고칩니다. -- 이유 칸 끝의 `제안 중: <제안 파일 이름>`은 공통 지침 수정을 제안해 둔 항목이라는 뜻입니다. 플러그인을 갱신한 뒤 반영됐으면 그 줄을 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +- 공통 지침 수정을 제안한 항목도 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 플러그인을 갱신한 뒤 닫힌 이슈를 보고 반영됐으면 같은 줄을 지우고, 버려졌으면 예외로 남깁니다. - 다른 프로젝트에도 맞는 규칙은 [지침 제안](proposals.md) 대상입니다. diff --git a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/proposals.md b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/proposals.md index ebd7bf5..3982a3f 100644 --- a/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/proposals.md +++ b/plugins/webcraft/dist/codex/plugins/webcraft/skills/manage-workflow/references/proposals.md @@ -1,17 +1,18 @@ # 지침 제안 -프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 로컬 제안함(`~/.goraebap/proposals/`)으로 처리합니다. GitHub 이슈는 저장소가 공개라 회사 프로젝트 작업의 근거가 공개될 위험이 있어 쓰지 않고, 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않습니다. +프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 제안자가 누구든 받아들일지는 플러그인 관리자가 정합니다. 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않고, 로컬 제안함은 다른 사용자의 제안을 받을 수 없어 쓰지 않습니다. ## 흐름 1. 프로젝트 작업 중 요청이 지침과 부딪히면 에이전트가 그때만 한 번 묻습니다. 선택지는 "이 프로젝트만 예외", "지침을 고치자(제안)", "지침대로 하기"입니다. 지침과 부딪히지 않으면 묻지 않습니다. 2. "이 프로젝트만 예외"를 고르면 프로젝트 AGENTS.md의 [프로젝트 규칙](project-rules.md) 표에 한 줄로 적습니다. -3. "지침을 고치자(제안)"을 고르면 작업은 멈추지 않습니다. 프로젝트 규칙 표에 그 줄을 적되 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙이고, `~/.goraebap/proposals/`에 제안서 한 장을 만듭니다. -4. 제안서 파일은 `~/.goraebap/proposals/-<짧은 영문 이름>.md`로 만듭니다. 제안은 공개 저장소의 지침이 될 수 있으므로 회사 제품명·고객·업무 데이터·접속 정보·사내 주소는 일반 표현으로 바꿔 씁니다. -5. 제안 처리는 Webcraft 저장소 세션에서 사용자가 요청할 때 수행합니다. 제안함의 제안서를 하나씩 보이고 사용자가 받아들일지, 고쳐서 받을지, 버릴지 고릅니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. 처리한 제안서는 `~/.goraebap/proposals/done/`으로 옮기고 결과(반영한 판, 버린 이유)를 끝에 적습니다. -6. 프로젝트는 플러그인이 갱신되면 `제안 중` 줄을 확인합니다. 반영됐으면 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +3. "지침을 고치자(제안)"을 고르면 프로젝트 작업은 멈추지 않습니다. 프로젝트에는 프로젝트 규칙 표에 지속 규칙 한 줄만 남기고 이슈 번호나 제안 표시는 붙이지 않습니다. +4. 이슈 본문은 일반화합니다. 제품명·고객·사내 주소·업무 데이터를 쓰지 않고, 제안한 프로젝트 이름 대신 프로젝트 종류(예: 사내 업무 앱)만 씁니다. +5. 공개 게시라 되돌리기 어려우므로 에이전트는 이슈 본문 초안을 사용자에게 보이고 승인을 받은 뒤 올립니다. 에이전트가 이슈를 올릴 수 있으면(`gh`가 있고 로그인됨) 승인받아 `gh issue create --repo dev-goraebap/dev.goraebap`로 올립니다. 올릴 수 없으면 같은 형식의 이슈 문서를 `~/.goraebap/issues/-<짧은 영문 이름>.md`로 만들고, 새 이슈 페이지 주소(`https://github.com/dev-goraebap/dev.goraebap/issues/new`)와 함께 "이 내용을 붙여 넣어 올려 주세요"라고 안내합니다. +6. 검토와 반영은 원본 저장소 세션에서 합니다. 열린 제안 이슈를 하나씩 보고 받아들이기·고쳐서 받기·버리기를 정한 뒤, 결과(반영한 판, 버린 이유)를 이슈에 남기고 닫습니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. +7. 프로젝트는 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. -기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 이 제안함으로 제안서를 만드는 절차를 따릅니다. +기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 같은 절차로 이슈를 올립니다. ## 제안서 형식 @@ -21,5 +22,5 @@ | 지금 규칙 | 현재 지침이 정한 기준 한 문장 | | 바꿀 규칙 | 새로 바꿀 기준 한 문장 | | 이유와 근거 | 프로젝트에서 일어난 일과 바꿀 이유 | -| 제안한 프로젝트 이름 | 제안을 만든 프로젝트 이름 | +| 프로젝트 종류 | 제안을 만든 프로젝트의 종류(예: 사내 업무 앱) | | 날짜 | 제안한 날짜(`YYYY-MM-DD`) | diff --git a/plugins/webcraft/docs/plugin-usage.md b/plugins/webcraft/docs/plugin-usage.md index 7b11414..3365b55 100644 --- a/plugins/webcraft/docs/plugin-usage.md +++ b/plugins/webcraft/docs/plugin-usage.md @@ -26,10 +26,29 @@ codex plugin add webcraft@dev-goraebap ## 갱신 -플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있으면 먼저 갱신합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. +플러그인을 갱신하면 연결된 모든 프로젝트가 새 지침을 따릅니다. 프로젝트 AGENTS.md의 최소 판보다 낮은 플러그인이 설치돼 있거나 지침을 읽을 수 없으면 에이전트는 추측으로 진행하지 않고, 작업 상태를 `~/.goraebap/handoff/` 아래 이어받기 문서로 남긴 뒤 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 안내합니다. 커밋·푸시·외부 전송은 이 플러그인 설치만으로 허용되지 않습니다. + +Claude Code에서 자동 갱신은 공식 마켓플레이스가 아니면 기본값이 꺼짐입니다. 사용자가 `/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update로 켜거나, 조직 관리자가 managed settings의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true`를 넣습니다. 자동 갱신은 세션 시작 때가 아니라 첫 메시지 뒤 최대 10분 안에 돌며, 실행 중인 세션은 옛 판을 유지하고 `/reload-plugins`를 입력하거나 다음에 실행할 때 새 판이 적용됩니다. + +Claude Code 배포본에는 세션 시작 훅(`hooks/hooks.json`, `hooks/update-check.mjs`)이 들어갑니다. 새로 시작할 때만(`startup`) 돌며, 하루 한 번 원본 저장소의 최신 판을 받아 `~/.goraebap/cache/webcraft-latest.json`에 기억합니다. 설치된 판보다 높으면 `Webcraft 새 판이 나왔습니다(0.8.0 → 0.8.1). 에이전트에게 "Webcraft 업데이트해 줘"라고 하면 갱신합니다.` 한 줄을 보이고, 에이전트가 첫 요청 전에 한 번 갱신을 제안합니다. 동의하면 아래 수동 갱신 명령을 실행한 뒤 `/reload-plugins`를 안내하고, 거절하면 다시 묻지 않습니다. 네트워크 실패나 Node가 없는 경우에는 조용히 넘어갑니다. Codex 배포본에는 훅이 없습니다. + +직접 갱신할 때는 다음 명령을 실행하고, Claude Code에서는 `/reload-plugins`를 입력하거나 새 세션을 엽니다. + +Claude Code: + +```text +claude plugin marketplace update dev-goraebap +claude plugin update webcraft@dev-goraebap +``` + +Codex: + +```text +codex plugin marketplace upgrade dev-goraebap +``` ## 이전 이름에서 전환 이전 식별자는 `goraebap-workflow@goraebap-workflow`입니다. 새 이름은 `webcraft@dev-goraebap`이며 입구 스킬 이름은 `goraebap-mode`를 유지합니다. 기존 프로젝트는 새 플러그인 설치를 확인한 뒤 AGENTS.md 연결을 새 이름과 최소 판 `0.7.1`으로 바꿉니다. 기존 사본에서 바꾼 규칙과 제품 전용 스킬은 보존합니다. 옛 설치를 제거하는 작업은 새 설치 확인 후 사용자가 허용한 범위에서 수행합니다. -역할별 모델 설정 `~/.claude/rules/goraebap-models.md`와 로컬 제안함 `~/.goraebap/proposals/`는 기존 경로를 유지합니다. +지침 제안은 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 역할별 모델 설정 `~/.claude/rules/goraebap-models.md`는 기존 경로를 유지하며, `~/.goraebap/`에는 최신 판 확인 캐시(`cache/`), 이어받기 문서(`handoff/`), 직접 올리지 못한 이슈 문서(`issues/`)가 남습니다. diff --git a/plugins/webcraft/hooks/hooks.json b/plugins/webcraft/hooks/hooks.json new file mode 100644 index 0000000..90c0e27 --- /dev/null +++ b/plugins/webcraft/hooks/hooks.json @@ -0,0 +1,17 @@ +{ + "description": "Webcraft 새 판이 있으면 세션을 시작할 때 알리고 갱신을 제안합니다(하루 한 번 확인).", + "hooks": { + "SessionStart": [ + { + "matcher": "startup", + "hooks": [ + { + "type": "command", + "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/update-check.mjs\"", + "timeout": 10 + } + ] + } + ] + } +} diff --git a/plugins/webcraft/hooks/update-check.mjs b/plugins/webcraft/hooks/update-check.mjs new file mode 100644 index 0000000..9e84efc --- /dev/null +++ b/plugins/webcraft/hooks/update-check.mjs @@ -0,0 +1,72 @@ +// Webcraft 새 판 알림(세션 시작 훅). 설치된 판과 원본 저장소의 최신 판을 하루 한 번 비교해, 낮으면 사용자에게 한 줄로 +// 알리고 에이전트에게는 갱신을 제안·실행하는 방법을 맥락으로 준다. 네트워크·파일 오류는 조용히 넘긴다: 알림이 실패해도 +// 세션 시작을 막거나 경고를 남기지 않는다. +import fs from 'node:fs/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const LATEST_URL = 'https://raw.githubusercontent.com/dev-goraebap/dev.goraebap/main/plugins/webcraft/package.json'; +const DAY = 24 * 60 * 60 * 1000; + +/** 점으로 나눈 숫자 판을 비교한다. a가 b보다 높으면 양수. 숫자가 아닌 꼬리(-rc 등)는 무시한다. */ +export function compareVersions(a, b) { + const parts = value => String(value).split('-')[0].split('.').map(part => Number.parseInt(part, 10) || 0); + const [x, y] = [parts(a), parts(b)]; + for (let i = 0; i < Math.max(x.length, y.length); i += 1) { + const diff = (x[i] ?? 0) - (y[i] ?? 0); + if (diff) return diff; + } + return 0; +} + +/** 새 판이 있으면 훅 출력(JSON)을, 없으면 null을 돌려준다. */ +export function notice(installed, latest) { + if (!installed || !latest || compareVersions(latest, installed) <= 0) return null; + return { + systemMessage: `Webcraft 새 판이 나왔습니다(${installed} → ${latest}). 에이전트에게 "Webcraft 업데이트해 줘"라고 하면 갱신합니다.`, + hookSpecificOutput: { + hookEventName: 'SessionStart', + additionalContext: [ + `Webcraft 플러그인의 새 판이 있습니다(설치된 판 ${installed}, 최신 판 ${latest}).`, + '사용자의 첫 요청을 처리하기 전에 한 번만 갱신을 제안합니다. 사용자가 동의하면 아래 두 명령을 차례로 실행합니다.', + '`claude plugin marketplace update dev-goraebap`', + '`claude plugin update webcraft@dev-goraebap`', + '성공하면 `/reload-plugins`를 입력하면 바로 적용되고, 아니면 다음 세션부터 적용된다고 안내합니다.', + '작업 중이라 지금 갱신하지 않기로 하면 다시 묻지 않고 하던 작업을 이어갑니다.', + ].join('\n'), + }, + }; +} + +async function readJson(file) { + return JSON.parse(await fs.readFile(file, 'utf8')); +} + +/** 하루 안에 확인한 최신 판이 캐시에 있으면 그것을, 없으면 원본에서 받아 캐시에 남긴다. 실패하면 null. */ +async function latestVersion(cacheFile, now = Date.now()) { + const cached = await readJson(cacheFile).catch(() => null); + if (cached?.latest && now - cached.checkedAt < DAY) return cached.latest; + try { + const response = await fetch(LATEST_URL, { signal: AbortSignal.timeout(3000) }); + if (!response.ok) return cached?.latest ?? null; + const { version } = await response.json(); + await fs.mkdir(path.dirname(cacheFile), { recursive: true }); + await fs.writeFile(cacheFile, JSON.stringify({ latest: version, checkedAt: now })); + return version; + } catch { + return cached?.latest ?? null; + } +} + +async function main() { + const root = process.env.CLAUDE_PLUGIN_ROOT ?? fileURLToPath(new URL('../', import.meta.url)); + const installed = (await readJson(path.join(root, '.claude-plugin', 'plugin.json')).catch(() => null))?.version; + const latest = await latestVersion(path.join(os.homedir(), '.goraebap', 'cache', 'webcraft-latest.json')); + const output = notice(installed, latest); + if (output) process.stdout.write(`${JSON.stringify(output)}\n`); +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + await main().catch(() => undefined); +} diff --git a/plugins/webcraft/package.json b/plugins/webcraft/package.json index 7a6d541..8fd1dc0 100644 --- a/plugins/webcraft/package.json +++ b/plugins/webcraft/package.json @@ -1,6 +1,6 @@ { "name": "webcraft", - "version": "0.7.2", + "version": "0.8.0", "private": true, "type": "module", "engines": { diff --git a/plugins/webcraft/scripts/package-plugins.mjs b/plugins/webcraft/scripts/package-plugins.mjs index d4e1ce9..f6a923a 100644 --- a/plugins/webcraft/scripts/package-plugins.mjs +++ b/plugins/webcraft/scripts/package-plugins.mjs @@ -53,6 +53,8 @@ export async function packagePlugins() { await put(`${pluginPath}/skills/${name}/agents/openai.yaml`, 'policy:\n allow_implicit_invocation: false\n'); } } + // 새 판 알림 훅은 Claude Code만 담는다(Codex의 플러그인 훅은 확인하지 못했다). Codex는 입구의 판 확인을 따른다. + if (agent === 'claude') await copyFiles(path.join(root, 'hooks'), `${pluginPath}/hooks`, ['.json', '.mjs']); await put(`${pluginPath}/README.md`, await fs.readFile(path.join(root, 'docs/plugin-usage.md'))); const manifest = { name: pkg.name, version: pkg.version, description, author: { name: 'dev-goraebap' }, skills: './skills/' }; if (agent === 'codex') { diff --git a/plugins/webcraft/skills/api/SKILL.md b/plugins/webcraft/skills/api/SKILL.md index c9a5163..9850efb 100644 --- a/plugins/webcraft/skills/api/SKILL.md +++ b/plugins/webcraft/skills/api/SKILL.md @@ -13,7 +13,7 @@ description: 웹 플랫폼의 HTTP 계약, 오류 응답, 인증·권한, 첨부 - 인증된 사용자와 자원 접근 권한을 서버에서 검사합니다. 숨겨야 하는 대상의 존재 노출 정책을 프로젝트에서 정하고 REST·MCP·다운로드에 동일하게 적용합니다. - PATCH의 생략·null·값 전달 의미를 구분합니다. 업무 날짜·지역 시각·절대 시각을 구별하고 절대 시각은 UTC로 전달합니다. 업무 시간대는 프로젝트가 정합니다. - 목록은 서버 필터·안정된 정렬·페이지/커서 계약을 명시합니다. count와 목록이 같은 조건을 쓰는지 확인합니다. -- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. +- 업로드는 임시 파일과 업무 자원 연결의 소유권·만료·권한을 구분합니다. 파일명·확장자를 신뢰하지 않고 크기·타입·경로를 검증합니다. 파일을 읽는 경로에서도 권한을 확인합니다. 사진 업로드가 선택 값인 대표색·가로·세로를 함께 보내면 파일과 함께 저장하고 첨부 정보로 돌려줍니다. - 변경은 버전 충돌과 명령 재시도 시 중복 효과를 검토합니다. 브라우저 캐시 무효화가 동시 편집 충돌을 해결한다고 가정하지 않습니다. 완료 시 성공 응답·실패 응답·권한·영향받는 호출자와 생성 타입을 확인합니다. diff --git a/plugins/webcraft/skills/design-system/references/composed-components.md b/plugins/webcraft/skills/design-system/references/composed-components.md index 0da0d8a..249a89c 100644 --- a/plugins/webcraft/skills/design-system/references/composed-components.md +++ b/plugins/webcraft/skills/design-system/references/composed-components.md @@ -22,6 +22,8 @@ 주소가 없는 시트는 브라우저의 닫기 요청(CloseWatcher와 같은 장치)으로 안드로이드 뒤로가기와 Esc에 닫힙니다. 네이티브 ``를 `showModal()`로 여는 시트(Astryx BottomSheet의 기본, 배경 막 있음)는 지원 브라우저에서 이 요청을 `cancel`로 이미 받으므로 따로 등록하지 않습니다. 겹쳐 등록하면 한 번에 두 번 닫힙니다. 직접 만든 시트나 배경 막 없는 시트만 CloseWatcher를 등록합니다. 외부 제어 API가 시트 열림을 주소 쿼리에 저장하라는 규칙은 아니며, 열림 상태를 쿼리에 두지 않습니다. 짧은 조작마다 방문 기록이 쌓이고 새로고침하면 시트가 다시 열리기 때문입니다. 닫기 요청을 지원하지 않는 브라우저(iOS 사파리 등)에서는 뒤로가기가 이전 페이지로 갈 수 있으므로, 시트마다 보이는 닫기 버튼을 두고 잃으면 아까운 입력은 시트에 담지 않습니다. 입력이 길면 주소가 있는 화면으로 엽니다. 시트는 쌓지 않습니다. +팝오버와 바텀시트는 열렸을 때만 내용을 만듭니다. 행마다 붙는 메뉴의 확인 대화상자는 그 동작을 고를 때만 만듭니다. 닫힌 메뉴 내용과 행마다 미리 만든 확인 대화상자(``)를 화면 이동 때 한꺼번에 정리하면 메인 스레드가 멈춥니다. 행마다 권한 판단에 필요한 데이터는 메뉴에 포인터나 초점이 갈 때 미리 읽습니다. + ## 선택지 목록 작은 고정 선택지는 그대로 나열하고, 8개를 넘으면 위에 검색 칸을 두어 초점을 줍니다. 검색 전에는 처음 8개와 이미 고른 항목, 검색하면 최대 50개, 나머지는 개수로 알립니다. 전부 렌더링하지 않습니다. 이 수치는 서버 한도와 별개이며 프로젝트가 조절합니다. 하나 고르기는 고른 뒤 닫고 끝에 체크, 여럿 고르기는 열린 채 앞에 상자를 둡니다. 휴대폰의 동작·선택·시간 목록은 최소 48px 행에 15px 글자·20px 아이콘을 씁니다. 보조 정보는 13px입니다. 메뉴를 시트로 옮기는 것만으로 데스크톱 밀도가 자동으로 바뀐다고 가정하지 않습니다. @@ -44,6 +46,6 @@ ## 본문 칸 -제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 본문 글자는 화면 글자(13px)보다 한 단계 큰 본문(15px, `--text-large-size`)이고 제목은 `--text-heading-2-size`입니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. +제목·본문 양식의 본문은 프로젝트가 채택한 리치에디터를 테두리와 서식 도구 막대 없이 종이처럼 둡니다. 서식은 에디터의 단축키·입력 규칙·선택 도구로 합니다. 앱 화면의 본문 토큰(13px, 15px `--text-large-size`)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 본문 칸과 상세 본문 글자는 데스크톱·휴대폰 모두 16px, 줄 높이 1.7이며 제목은 `--text-heading-2-size`입니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 키트의 Tiptap 예시는 자리를 보이기 위한 것이며 특정 에디터를 기본 의존성으로 복사하지 않습니다. 구현이 없는 조합을 메뉴에 빈 항목으로 만들지 않습니다. 제품 고유 조합(아이콘 표시 고르기, 전역 검색 팔레트, 프로필 패널)은 프로젝트가 소유합니다. diff --git a/plugins/webcraft/skills/design-system/references/data-states.md b/plugins/webcraft/skills/design-system/references/data-states.md index 8bbfaff..f4f6b7b 100644 --- a/plugins/webcraft/skills/design-system/references/data-states.md +++ b/plugins/webcraft/skills/design-system/references/data-states.md @@ -16,17 +16,21 @@ ## 화면을 바꾸는 동안 -캐시가 없는 화면의 데이터와 코드가 준비될 때까지 이전 화면을 유지합니다. 지연이 짧으면 표시를 생략하고, 길어지면 앱 셸을 그대로 둔 채 콘텐츠 영역에 불투명한 막을 표시합니다. 막과 새 본문을 함께 흐리게 만들지 않습니다. 새 화면의 그리기와 긴 작업이 끝났는지 확인한 뒤 잠깐 안정화 시간을 두고 막만 걷습니다. 숨은 탭이나 프레임이 멈춘 경우에도 끝나는 시간 제한과 취소 처리를 둡니다. +앱 첫 진입만 첫 방문 덮개(스플래시 화면)로 덮고, 그 뒤의 화면 이동에는 본문을 가리는 불투명한 막을 두지 않습니다. 화면 전체 스켈레톤은 모양이 바뀌는 본문과 높이가 맞지 않고, 영역별 덮개는 같은 판정이 화면마다 늘어나며, 콘텐츠 막은 상세 안 사이드바까지 덮고 재방문에서도 떠 쓰지 않습니다. -기본 시작값은 표시 지연 100ms, 한 번 표시한 뒤 최소 300ms, 그리기 확인 뒤 안정화 200ms, 막 해제 700ms입니다. 제품에 확정된 값이 있으면 그 값을 따릅니다. 최소 표시 시간만 지났다고 새 본문의 레이아웃이 안정되기 전에 막을 걷지 않습니다. +캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신합니다(stale-while-revalidate). 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 먼저 옮깁니다. -캐시가 있는 화면의 전환 효과는 콘텐츠에만 적용합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 붙인 콘텐츠만 캡처합니다. 로딩 막이 보이거나 걷히는 동안에는 겹치지 않습니다. 동작 줄이기 설정을 따르고, 빠른 연속 이동에서 진행 중인 전환을 겹쳐 실행하지 않습니다. +앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 따로 붙이지 않고 문서에 한 번 걸어, `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하며 같은 주소는 10초에 한 번만 부릅니다. + +캐시가 없어 기다릴 때는 이전 화면을 그대로 두고, 150ms가 지나면 콘텐츠 카드 위쪽 테두리에 테마 강조색이 흐르는 진행 표시만 띄웁니다. 진행 표시는 높이 2px로 카드 위에 겹쳐 그리고 카드 모서리에 맞춰 잘라 배치를 밀지 않습니다. 150ms 안에 끝나면 띄우지 않고, 한 번 띄웠으면 최소 300ms 둔 뒤 새 화면과 함께 바로 내립니다. 그리기 확인 후 천천히 걷는 대기를 진행 표시에 쓰면 이동이 끝난 뒤에도 남으므로 쓰지 않습니다. + +준비된 화면끼리는 콘텐츠 영역만 200ms 교차 페이드합니다. View Transition을 쓰면 문서 전체의 기본 스냅샷을 제외하고 이름을 콘텐츠 영역 하나에만 붙입니다. 앞뒤 화면에서 같은 부분(사이드바 등)은 같은 픽셀끼리 겹쳐 그대로 있는 듯 보이므로 화면별 예외를 두지 않습니다. TanStack Router는 loader가 끝난 뒤 View Transition을 시작하므로 준비하는 동안 진행 표시가 멈추지 않습니다. 동작 줄이기 설정, 로그인 오가기, 진행 중인 전환, 열린 대화상자에서는 전환하지 않습니다. ## 대화상자의 데이터 준비 준비 중에는 이름 있는 원형 Spinner를 보이고, 필요한 데이터가 준비되면 내용을 한 번에 표시합니다. 바깥 창과 준비 영역의 최소 크기를 유지해 작은 창에서 갑자기 커지지 않게 합니다. 준비 확인 Promise가 새로 생겼다는 이유만으로 캐시에 있는 내용을 가리지 않습니다. 필요한 조회가 실제 캐시에 남아 있는지 확인하며, 한 번 표시한 로딩에는 최소 표시 시간을 적용합니다. 실패는 무한 로딩 대신 실패 상태와 다시 시도로 끝납니다. -확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 애니메이션 호출 여부뿐 아니라 앱 셸·본문이 숨는 프레임과 로딩 막의 범위도 확인합니다. +확인할 때는 첫 방문, 캐시가 있는 재방문, 느린 코드·데이터, 중첩 경로, 빠른 연속 이동과 동작 줄이기를 나눕니다. 화면 전환·로딩·캐시·지연 로딩 부품을 바꾸면, 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동을 차례로 누르는 Playwright 점검을 돌립니다. 단계에는 같은 틀 안 이동, 3초 넘게 지난 페이지로 다시 방문, 목록 → 상세 → 뒤로 → 같은 상세를 넣고, 새 종류의 화면을 만들면 단계를 더합니다. 단계마다 누른 뒤의 레이아웃 이동 합(`layout-shift`, 입력 직후 제외)과 새 화면으로 교체가 끝날 때까지의 대기를 잽니다. 진행 표시는 데이터 속성으로 감지하고, 대기는 주소 변경 시각이 아니라 준비 중 표시(`aria-busy`)가 풀릴 때까지로 잽니다. 이동이 0.01을 넘거나, 캐시가 있어야 하는 다시 방문에서 진행 표시가 뜨면 실패입니다. ## 상태 화면 @@ -52,7 +56,9 @@ ## 캐시와 전환 -첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 180ms가 지나도 준비되지 않을 때만 진행 표시를 덮습니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. 이미지는 자리를 먼저 확보해 내용이 밀리지 않게 합니다. +첫 화면에 필요한 조회는 라우트에서 준비하고 페이지와 같은 쿼리 옵션·캐시를 씁니다. 캐시가 있으면 바로 그리고 백그라운드에서 갱신합니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하고, 150ms가 지나도 준비되지 않을 때만 콘텐츠 카드 위쪽 테두리에 진행 표시를 띄웁니다. 짧은 대기에 깜빡이는 표시를 두지 않습니다. 목록 조건을 바꾸는 동안은 이전 결과를 유지하고(`placeholderData`) 진행 중임을 알립니다. + +사진을 올릴 때는 브라우저가 대표색과 원본 가로·세로를 재어 업로드 요청에 함께 보냅니다(64px로 줄여 색을 묶어 세고, 가장 많은 묶음의 평균색). 측정은 한 번에 하나씩, 3초 제한이며, 실패하면 측정값 없이 올립니다. 서버는 파일과 함께 저장해 첨부 정보로 돌려주고, 리치 본문은 사진 블록 속성에도 넣습니다. 보여 줄 때는 원본 비율과 대표색으로 자리를 먼저 칠해 내용이 밀리지 않게 하고, 받아야 하는 사진만 0.4초에 걸쳐 나타냅니다. 이 페이지에서 이미 받았거나 브라우저 캐시에 있는 사진은 바로 보입니다. 화면 전환 효과는 준비된 내용 사이의 짧은 교차 페이드만 쓰고 동작 줄이기 설정에서는 끕니다. 스켈레톤은 실제 배치와 같을 때만 씁니다. 키트 예제는 대기 상태를 확인하기 위해 페이지 안의 진행 표시를 씁니다. @@ -64,4 +70,5 @@ | 양식 저장 | 양식 위 배너(`role="alert"`), 입력은 지킵니다 | | 페이지 전체(변경 실패·충돌·읽기 전용) | 머리 아래 배너 줄 | | 화면에 결과가 보이지 않는 짧은 완료(주소 복사, 보관, 백그라운드 작업 시작) | 토스트. 되돌릴 수 있으면 "되돌리기" 하나를 두고 충분히 오래 남깁니다. 오류·경고·되돌릴 수 없는 결과·다음 행동이 필요한 내용은 토스트에 두지 않습니다. 결과가 화면에 바로 보이면 알리지 않습니다 | +| 상세에서 저장 버튼 없이 바로 저장되는 속성 변경(상태·담당·날짜·라벨·우선순위 등) | 결과가 화면에 보여도 "되돌리기" 토스트를 5초 띄웁니다. 토스트 아래 2px 막대가 남은 시간만큼 줄어들고 포인터가 올라가 있으면 멈춥니다. 되돌리기는 서버 최신본을 다시 읽은 뒤 바꾼 속성만 이전 값으로 저장해, 그 사이 다른 사람의 변경을 덮지 않습니다 | | 화면을 그릴 수 없는 오류 | 라우트 오류 화면과 다시 시도 | diff --git a/plugins/webcraft/skills/design-system/references/icons.md b/plugins/webcraft/skills/design-system/references/icons.md index a7532cf..35fbd12 100644 --- a/plugins/webcraft/skills/design-system/references/icons.md +++ b/plugins/webcraft/skills/design-system/references/icons.md @@ -12,6 +12,8 @@ 모바일에서 아이콘 버튼의 누르는 영역은 최소 44px입니다. 프로필 그림은 28~32px로 두고 그 바깥에서 같은 누르는 영역을 확보합니다. 누르는 영역을 넓히려고 그림을 44~48px로 키우지 않습니다. 반대로 버튼 상자만 넓히고 안의 아이콘을 데스크톱 크기에 두지도 않습니다. Astryx가 아이콘 칸을 고정하면 SVG가 줄어들지 않는지 실제 크기와 중심을 확인합니다. 머리 줄의 아이콘 규칙이 그 안에서 열리는 시트·메뉴까지 번지지 않도록 범위를 제한합니다. +속성 열·활동 기록처럼 아이콘이 세로로 늘어선 곳은 상자 크기뿐 아니라 24 격자 위에 그려진 범위도 비슷하게 맞춥니다(대부분 20칸 안팎). 범위가 눈에 띄게 작은 아이콘(16칸 정도)은 키우지 않고 같은 뜻의 다른 Solar 아이콘으로 바꿉니다. 키우면 선도 굵어지기 때문입니다. 확인은 화면에서 SVG의 `getBBox()`를 재서 합니다. 기본 상태의 줄에 Line Duotone처럼 일부 선이 흐린 형태를 섞지 않습니다. 같은 색이어도 흐리고 작게 보입니다. + ## 고르는 기준 | 상황 | 아이콘 | diff --git a/plugins/webcraft/skills/design-system/references/layout-patterns.md b/plugins/webcraft/skills/design-system/references/layout-patterns.md index a6a3745..1e1c8cf 100644 --- a/plugins/webcraft/skills/design-system/references/layout-patterns.md +++ b/plugins/webcraft/skills/design-system/references/layout-patterns.md @@ -33,7 +33,9 @@ 본문이 있는 대상의 상세([DetailLayout](https://github.com/dev-goraebap/dev.goraebap/blob/main/site/src/shared/ui/layouts/detail/DetailLayout.tsx))는 최대 45rem 읽기 열에 소속·제목·본문·이어지는 것(하위 항목·활동)을 둡니다. 속성은 넓으면 오른쪽 16rem 열에 묶음 이름과 함께(스크롤해도 보입니다), 좁으면 제목과 본문 사이의 줄바꿈되는 알약으로 옮깁니다. 같은 요소를 옮기므로 속성을 두 벌 렌더링하지 않습니다. 창 폭이 아니라 컨테이너 폭(60rem)으로 판단해 대화상자·패널에서도 맞게 바뀝니다. 긴 제목·주소는 줄을 넘겨 가로로 넘치지 않습니다. 상세와 작성 면은 점 무늬 없이 평평합니다. -본문 편집 도구(편집·저장)는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. +본문이 있는 모든 상세는 속성 열을 같은 순서로 둡니다. 본문에 제목이 있으면 맨 위에 "목차" 묶음, 그 아래 대상별 속성, 맨 끝에 "정보" 묶음(작성자 아바타·작성일·수정일과 고친 사람·버전 수)을 두고, 모르는 값은 줄째 뺍니다. 목차와 버전 줄도 다른 속성과 같은 속성 행 부품으로 그려 아이콘·글자 자리를 맞춥니다. 누르면 목차는 테두리 있는 팝오버, 버전은 버전 기록 대화상자를 엽니다. 휴대폰은 목차를 오른쪽 아래 떠 있는 둥근 버튼과 바텀시트로 둡니다. 편집기 가장자리에 목차 표시를 붙이지 않습니다. 상세의 더 보기(…) 메뉴는 페이지 머리 줄 한 곳에 둡니다. + +본문 편집 도구는 페이지 머리 줄의 공통 자리에 둡니다. 읽기에서 시작하고 명시적인 편집 전환을 둡니다. 데스크톱 머리 줄의 편집 전환·편집 마침·버전 기록은 필터·정렬과 같은 둥근 아이콘 도구(`variant="tool"`, 아이콘만, 툴팁에 이름)로 두며, "편집" 같은 글자 버튼을 머리 줄에 두지 않습니다. 저장은 상태가 바뀌는 동작이라 글자를 유지하고, 휴대폰은 바닥 줄의 저장·완료를 그대로 둡니다. 버전 기록 목록은 표준 목록 행(아이콘·시각·설명, 선택 표시)으로 그리고, 주소를 가진 대화상자(라우트 마스킹 `…/history`, 새로 고침하면 다시 열림)로 엽니다. ## 설정 diff --git a/plugins/webcraft/skills/design-system/references/typography.md b/plugins/webcraft/skills/design-system/references/typography.md index 1764370..fafbd92 100644 --- a/plugins/webcraft/skills/design-system/references/typography.md +++ b/plugins/webcraft/skills/design-system/references/typography.md @@ -9,7 +9,7 @@ | 큰 본문·작은 제목 | 15px, 제목은 600 | 17px, 제목은 600 | `--text-large-size`, `--text-heading-3-*` | | 페이지 제목(상세·설정·작성 페이지) | 24px | 24px | `--text-heading-1-size` | -리치 본문(본문 칸·상세 본문)은 큰 본문을 따르므로 휴대폰에서 17px이 됩니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. +앱 화면의 본문 토큰(13px·15px)은 목록·도구가 빽빽한 화면용으로 남기고, 긴 글을 읽는 리치 본문(본문 칸·상세 본문)은 데스크톱·휴대폰 모두 16px, 줄 높이 1.7로 둡니다. 테마가 문단에 글자 크기와 줄 높이를 직접 주므로 문단·목록·인용이 크기와 줄 높이를 함께 상속하게 합니다. 쓰는 칸과 그 글을 읽는 상세 본문은 같은 크기로 둡니다. 이 넷에 없는 크기는 이유가 분명할 때만 씁니다. 리치 본문(16px), 휴대폰 입력 칸 글자(16px, iOS 확대 방지), 하단 탭의 11px 이름, 상태 그림의 제목(18px), 작성 양식 대화상자의 제목(18px)이 그런 예입니다. 모듈 CSS에 rem 숫자를 흩어 두면 같은 역할이 화면마다 다른 크기가 됩니다. ## 글꼴과 숫자 diff --git a/plugins/webcraft/skills/frontend/references/navigation.md b/plugins/webcraft/skills/frontend/references/navigation.md index 548ff19..34a170b 100644 --- a/plugins/webcraft/skills/frontend/references/navigation.md +++ b/plugins/webcraft/skills/frontend/references/navigation.md @@ -1,8 +1,8 @@ # 화면 전환 -첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 바로 그리고, 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며 짧은 대기에는 진행 표시를 띄우지 않습니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. +첫 화면에 필요한 조회는 라우트 loader에서 준비하고 컴포넌트와 같은 queryOptions·캐시를 씁니다. 캐시가 있으면 오래됐어도 즉시 그리고 뒤에서 갱신하며(stale-while-revalidate), 캐시 신선도 조건으로 첫 그림을 거르지 않습니다. 앱 안 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다(intent prefetch). 화면마다 붙이지 않고 문서에 한 번 걸어 `pointerover`·`focusin`에서 앱 안 `a[href]`를 라우터의 preload로 준비하고, 같은 주소는 10초에 한 번만 부릅니다. 캐시가 없으면 다음 화면이 준비될 때까지 지금 화면을 유지하며, 가리는 막을 두지 않고 150ms가 지나면 콘텐츠 카드 위쪽 테두리에만 진행 표시를 띄웁니다. 누른 메뉴의 선택 표시는 응답 전에 옮기고, 캐시로 먼저 그린 본문이 서버 최신본과 다르면 읽는 글을 바로 바꾸지 않고 "최신 내용 보기" 안내만 띄웁니다. 메뉴·링크 진입, 뒤로 돌아오기, 검색 조건 변경을 구분해 로딩·스크롤·캐시 정책을 정합니다. 화면 표현은 디자인 시스템의 [데이터 상태](../../design-system/references/data-states.md)를 따릅니다. -지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면도 포함합니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. +지연 로딩하는 화면 코드는 데이터와 함께 전환 전에 준비합니다. 라우트 안에서 고르는 중첩 화면과 화면 안에서 지연 로딩하는 큰 부품(리치 에디터 등)도 같은 기준을 따릅니다. 바깥 라우트만 준비하고 안쪽 lazy 컴포넌트가 렌더 중 처음 로드되면 Suspense가 앱 셸까지 숨길 수 있고, 본문 에디터가 뒤늦게 붙으면 아래 영역이 밀립니다. `React.lazy`는 코드가 이미 받아져 있어도 처음 그릴 때 한 번 멈추므로 `preload()`를 가진 래퍼로 감싸 받은 뒤에는 바로 그리게 하고, 그 부품을 쓰는 화면의 라우트 준비에서 `preload()`를 데이터와 함께 부릅니다. 배치에 영향을 주는 비동기 부품은 빈 채로 그렸다가 채우지 않습니다. 새 경로의 일치 항목이 그려지는 시점과 `onResolved` 시점을 구분하고, 새 화면에 이전 주소를 전달해 본문을 다시 마운트하지 않습니다. 본문뿐 아니라 첫 화면에 쓰는 목록·사람·선택지·연결 정보도 같은 조회 옵션으로 준비합니다. 하위 컴포넌트가 나중에 시작하는 조회와 URL 조건을 빠뜨리지 않습니다. 캐시를 채우는 loader의 라우터 캐시와 Query 캐시 수명을 따로 정해, loader가 생략된 뒤 빈 Query 캐시에서 화면이 다시 조회하지 않게 합니다. diff --git a/plugins/webcraft/skills/goraebap-mode/SKILL.md b/plugins/webcraft/skills/goraebap-mode/SKILL.md index ff63064..46cf17e 100644 --- a/plugins/webcraft/skills/goraebap-mode/SKILL.md +++ b/plugins/webcraft/skills/goraebap-mode/SKILL.md @@ -9,6 +9,8 @@ description: 웹 플랫폼의 기능 구현, 버그 수정, 구조 변경을 작 ## 시작 +시작할 때 프로젝트 AGENTS.md의 최소 판과 설치된 플러그인 판을 비교합니다. 설치된 판이 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없으면 추측으로 진행하지 않고 [이어받기 문서](references/handoff.md)를 따릅니다. + 사용자가 원하는 결과를 짧게 설명하고 기존 변경·staging 상태를 확인합니다. 제품 동작이 달라지면 관련 요구사항과 설계를 읽습니다. 기록 위치와 이슈 도구는 프로젝트가 정합니다. 연결되지 않은 도구를 필수로 가정하거나 조회만 하는 질문에 업무를 만들지 않습니다. 작업에 맞는 플레이북 하나를 읽고, 계약·스키마 변경 같은 의존 단계만 함께 수행합니다. 작은 수정은 필요한 단계만 적용합니다. diff --git a/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md b/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md index acdcad2..dfaeb41 100644 --- a/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md +++ b/plugins/webcraft/skills/goraebap-mode/playbooks/evolve.md @@ -4,5 +4,5 @@ 2. 다음 변경이 이 프로젝트만의 결정인지, 여러 프로젝트에 맞는 공통 개선인지 구분합니다. 당장 구분할 수 없으면 이 프로젝트의 결정으로 먼저 둡니다. 3. 이 프로젝트만의 결정은 프로젝트 AGENTS.md의 프로젝트 규칙 표에 이유와 함께 적습니다([프로젝트 규칙](../../manage-workflow/references/project-rules.md)). 경고를 늘리기 전에 자동 검사나 구조로 실수를 막을 수 있는지 검토합니다. 4. 같은 상황에서 결과가 달라지는지 확인합니다. 경미한 문구 수정에 불필요한 전체 앱 검사를 요구하지 않습니다. -5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서로 남기고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. 원본은 사용자가 요청할 때 플러그인 저장소에서 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. -6. 플러그인이 갱신되면 프로젝트 규칙 표의 `제안 중` 줄을 확인해, 원본에 반영됐으면 지우고 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +5. 공통 개선은 [지침 제안](../../manage-workflow/references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 원본은 원본 저장소 세션에서 제안 이슈를 검토해 [글쓰기 기준](../references/writing.md)에 따라 고칩니다. +6. 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/skills/goraebap-mode/references/handoff.md b/plugins/webcraft/skills/goraebap-mode/references/handoff.md new file mode 100644 index 0000000..36f0890 --- /dev/null +++ b/plugins/webcraft/skills/goraebap-mode/references/handoff.md @@ -0,0 +1,30 @@ +# 이어받기 문서 + +설치된 Webcraft 플러그인 판이 프로젝트 AGENTS.md의 최소 판보다 낮거나, 세션 중에 플러그인이 갱신돼 지침이 맞지 않거나, 필요한 지침을 읽을 수 없을 때는 추측으로 진행하지 않습니다. 지금까지의 작업 상태를 이어받기 문서로 남기고 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. + +세션 시작 훅이 새 판을 알려 첫 요청 전에 갱신을 제안할 때는, 진행 중인 작업이 없으면 사용자 동의를 받아 갱신 명령을 실행하고 `/reload-plugins`를 안내합니다. 작업을 시작한 뒤에 갱신이 필요해지면 이어받기 문서를 먼저 남깁니다. + +## 위치와 담는 내용 + +저장소를 더럽히지 않고 커밋에 섞이지 않도록 저장소 밖 `~/.goraebap/handoff/-<프로젝트 폴더 이름>-<짧은 영문 이름>.md`에 만듭니다. 비밀값과 접속 정보는 넣지 않습니다. + +| 항목 | 적는 내용 | +| --- | --- | +| 사용자가 원한 결과 | 요청의 목적과 완료 기준 | +| 결정과 기각한 안 | 사용자가 내린 결정, 검토했다가 기각한 안과 이유 | +| 바꾼 파일 | 지금까지 고치거나 만든 파일 경로와 커밋 여부 | +| 다음 단계 | 바로 이어서 해야 할 작업 순서 | +| 검증 상태 | 수행한 검증과 아직 못 한 검증 | +| 묻지 못한 질문 | 사용자 확인이 남은 질문 | + +## 사용자 안내와 갱신 + +문서를 남긴 뒤 사용자에게 할 일을 짧게 안내합니다. + +1. 플러그인 갱신 명령(Claude Code는 `claude plugin marketplace update dev-goraebap`과 `claude plugin update webcraft@dev-goraebap`, Codex는 `codex plugin marketplace upgrade dev-goraebap`)을 안내하거나, 사용자가 `"Webcraft 업데이트해 줘"`라고 요청하면 에이전트가 직접 실행합니다. +2. 새 판을 적용하는 `/reload-plugins` 입력 또는 새 세션 시작을 안내합니다. +3. 새 세션에 붙여 넣을 한 문장(`"~/.goraebap/handoff/<파일 이름>.md를 읽고 이어서 진행해 줘"`)을 함께 전달합니다. + +## 새 세션에서 이어받기 + +이어받기 문서를 전달받은 새 세션은 문서를 읽고 지금 저장소 상태(`git status`)와 대조한 뒤 다음 단계부터 이어서 진행합니다. 작업을 모두 이어받으면 이어받기 문서를 지워도 된다고 사용자에게 알립니다. diff --git a/plugins/webcraft/skills/goraebap-mode/references/verification.md b/plugins/webcraft/skills/goraebap-mode/references/verification.md index da83bc7..f68af3d 100644 --- a/plugins/webcraft/skills/goraebap-mode/references/verification.md +++ b/plugins/webcraft/skills/goraebap-mode/references/verification.md @@ -5,6 +5,7 @@ | 변경 | 확인할 것 | | --- | --- | | 화면 | 타입·빌드·프로젝트 검사, 바뀐 주 동작, 해당 화면의 로딩·빈 결과·오류 | +| 화면 전환·로딩·캐시·지연 로딩 | 실행 중인 앱에 개발용 로그인으로 들어가 주요 이동(같은 틀 안 이동, 3초 넘게 지난 재방문, 목록 → 상세 → 뒤로 → 같은 상세)을 차례로 누르는 Playwright 점검(`layout-shift` 합 0.01 이하, 재방문 진행 표시 미노출, `aria-busy` 해제까지의 대기) | | 반응형·모양 | 데스크톱·휴대폰, 제공하는 밝게·어둡게 모드, 키보드 주 동작 | | 서버 로직 | 해당 유스케이스·권한·트랜잭션 실패, 프로젝트 정적 검사 | | API 계약 | 서버 명세와 생성 타입, 영향받는 호출자, 오류 응답 | diff --git a/plugins/webcraft/skills/manage-workflow/SKILL.md b/plugins/webcraft/skills/manage-workflow/SKILL.md index a45e59b..d721f3e 100644 --- a/plugins/webcraft/skills/manage-workflow/SKILL.md +++ b/plugins/webcraft/skills/manage-workflow/SKILL.md @@ -15,7 +15,7 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | 프로젝트만 다르게 하는 규칙 추가·정리 | [프로젝트 규칙](references/project-rules.md) | | 예전 사본 방식(`.agents/skills`의 지침 사본·동기화 훅)에서 바꾸기 | [사본에서 전환](references/migrate-from-copies.md) | | 프로젝트 규칙 중 공통으로 올릴 것 찾기 | [회수](#회수) | -| 지침 제안 처리 | [지침 제안](references/proposals.md) | +| 지침 제안 올리기·검토 | [지침 제안](references/proposals.md) | ## 연결 @@ -23,12 +23,13 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 2. 예전 사본(접두사가 붙은 `*-mode`·`*-frontend` 같은 지침 폴더, `scripts/sync-skills.mjs`, 동기화 시작 훅)이 있으면 연결 대신 [사본에서 전환](references/migrate-from-copies.md)을 따릅니다. 3. AGENTS.md에 아래 "개발 지침" 블록을 넣습니다. 파일이 없으면 만들고, 있으면 기존 프로젝트 설명과 팀 규칙을 보존한 채 블록만 더합니다. 최소 판은 설치된 플러그인의 판(`plugin.json`의 `version`)으로 적습니다. 4. 기존 AGENTS.md에 지침과 다른 결정이 이미 있으면 [프로젝트 규칙](references/project-rules.md) 표로 옮깁니다. 확인할 수 없는 의도는 추정하지 말고 사용자에게 묻습니다. -5. 바꾼 파일과 남은 확인 사항을 보고합니다. +5. Claude Code 사용자에게는 이 마켓플레이스의 자동 갱신을 켤지 한 번 묻고 켜는 방법(`/plugin` → Marketplaces → `dev-goraebap` → Enable auto-update, 또는 조직 관리 설정의 `extraKnownMarketplaces` 항목에 `"autoUpdate": true` 추가)을 안내합니다. 사용자 설정 파일은 에이전트가 임의로 고치지 않습니다. +6. 바꾼 파일과 남은 확인 사항을 보고합니다. ```markdown ## 개발 지침 -이 프로젝트는 Webcraft 플러그인(0.7.1 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. +이 프로젝트는 Webcraft 플러그인(0.8.0 이상)의 지침을 따릅니다. 작업을 시작할 때 goraebap-mode를 읽고, 바꾸는 영역의 지침(frontend·design-system·backend·api·database·architecture)을 함께 읽습니다. 아래 프로젝트 규칙이 지침과 다르면 프로젝트 규칙을 따릅니다. Webcraft 지침을 읽을 수 없거나 설치된 판이 위 판보다 낮으면 추측으로 진행하지 않습니다. 작업 상태를 이어받기 문서로 남기고, 플러그인 설치·갱신 방법과 새 세션에서 읽을 문서 경로를 사용자에게 알립니다. ## 프로젝트 규칙 @@ -36,8 +37,8 @@ description: 사용자가 Webcraft 연결·사본 전환·프로젝트 규칙 | --- | --- | --- | --- | ``` -표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. +플러그인이 설치되지 않았거나 꺼져 있으면 플러그인의 어떤 지침도 읽히지 않고 AGENTS.md만 읽히므로, 지침을 읽을 수 없을 때의 행동 문장까지 블록에 넣습니다. 표가 비어 있으면 표 대신 "지침과 다르게 하는 것은 없습니다."라고 적습니다. 개인 설치 경로나 플러그인 내부 파일 경로는 AGENTS.md에 넣지 않습니다. ## 회수 -프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 로컬 제안함(`~/.goraebap/proposals/`)에 제안서를 만들고, 프로젝트 규칙 표의 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙입니다. +프로젝트 규칙 중 다른 프로젝트에도 맞는 것을 찾습니다. 제품 고유의 업무 규칙, 회사 정보, 실험 중인 결정은 제외합니다. 후보를 사용자와 확인한 뒤 [지침 제안](references/proposals.md)에 따라 일반화한 이슈 본문 초안을 사용자에게 보이고 승인을 받아 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 올립니다. 프로젝트 규칙 표에는 이슈 번호나 제안 표시를 붙이지 않고, 플러그인이 갱신되면 닫힌 이슈를 보고 같은 줄을 지우거나 예외로 남깁니다. diff --git a/plugins/webcraft/skills/manage-workflow/references/project-rules.md b/plugins/webcraft/skills/manage-workflow/references/project-rules.md index 60b7cf8..a411420 100644 --- a/plugins/webcraft/skills/manage-workflow/references/project-rules.md +++ b/plugins/webcraft/skills/manage-workflow/references/project-rules.md @@ -30,5 +30,5 @@ AGENTS.md의 "프로젝트 규칙" 표에 결정 하나를 한 줄로 적습니 - 지침과 같은 내용은 적지 않습니다. 같으면 지침이 이미 말하고 있습니다. - 지침에 없는 이 프로젝트만의 사실(제품 용어, 도메인 규칙, 실행 명령)은 표가 아니라 AGENTS.md의 프로젝트 설명에 씁니다. - 플러그인을 갱신한 뒤 지침이 프로젝트 규칙과 같아졌으면 그 줄을 지웁니다. 지침이 바뀌어 규칙의 "지침의 기준" 칸이 틀려졌으면 고칩니다. -- 이유 칸 끝의 `제안 중: <제안 파일 이름>`은 공통 지침 수정을 제안해 둔 항목이라는 뜻입니다. 플러그인을 갱신한 뒤 반영됐으면 그 줄을 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +- 공통 지침 수정을 제안한 항목도 표에는 이슈 번호나 제안 표시 없이 지속 규칙 한 줄만 남깁니다. 플러그인을 갱신한 뒤 닫힌 이슈를 보고 반영됐으면 같은 줄을 지우고, 버려졌으면 예외로 남깁니다. - 다른 프로젝트에도 맞는 규칙은 [지침 제안](proposals.md) 대상입니다. diff --git a/plugins/webcraft/skills/manage-workflow/references/proposals.md b/plugins/webcraft/skills/manage-workflow/references/proposals.md index ebd7bf5..3982a3f 100644 --- a/plugins/webcraft/skills/manage-workflow/references/proposals.md +++ b/plugins/webcraft/skills/manage-workflow/references/proposals.md @@ -1,17 +1,18 @@ # 지침 제안 -프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 로컬 제안함(`~/.goraebap/proposals/`)으로 처리합니다. GitHub 이슈는 저장소가 공개라 회사 프로젝트 작업의 근거가 공개될 위험이 있어 쓰지 않고, 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않습니다. +프로젝트 작업 중 요청이 기존 지침과 다를 때, 이 프로젝트만의 예외로 둘지 Webcraft 지침을 고칠지 정하고 Webcraft 원본 저장소(`dev-goraebap/dev.goraebap`)의 이슈로 받습니다. 제안자가 누구든 받아들일지는 플러그인 관리자가 정합니다. 프로젝트 세션에서 바로 PR을 올리는 안은 작업 중 다른 저장소 체크아웃·글쓰기 역할 위임·판 올리기가 끼어 무거워 쓰지 않고, 로컬 제안함은 다른 사용자의 제안을 받을 수 없어 쓰지 않습니다. ## 흐름 1. 프로젝트 작업 중 요청이 지침과 부딪히면 에이전트가 그때만 한 번 묻습니다. 선택지는 "이 프로젝트만 예외", "지침을 고치자(제안)", "지침대로 하기"입니다. 지침과 부딪히지 않으면 묻지 않습니다. 2. "이 프로젝트만 예외"를 고르면 프로젝트 AGENTS.md의 [프로젝트 규칙](project-rules.md) 표에 한 줄로 적습니다. -3. "지침을 고치자(제안)"을 고르면 작업은 멈추지 않습니다. 프로젝트 규칙 표에 그 줄을 적되 이유 칸 끝에 `제안 중: <제안 파일 이름>`을 붙이고, `~/.goraebap/proposals/`에 제안서 한 장을 만듭니다. -4. 제안서 파일은 `~/.goraebap/proposals/-<짧은 영문 이름>.md`로 만듭니다. 제안은 공개 저장소의 지침이 될 수 있으므로 회사 제품명·고객·업무 데이터·접속 정보·사내 주소는 일반 표현으로 바꿔 씁니다. -5. 제안 처리는 Webcraft 저장소 세션에서 사용자가 요청할 때 수행합니다. 제안함의 제안서를 하나씩 보이고 사용자가 받아들일지, 고쳐서 받을지, 버릴지 고릅니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. 처리한 제안서는 `~/.goraebap/proposals/done/`으로 옮기고 결과(반영한 판, 버린 이유)를 끝에 적습니다. -6. 프로젝트는 플러그인이 갱신되면 `제안 중` 줄을 확인합니다. 반영됐으면 지우고, 버려졌으면 `제안 중` 표시만 떼고 예외로 남깁니다. +3. "지침을 고치자(제안)"을 고르면 프로젝트 작업은 멈추지 않습니다. 프로젝트에는 프로젝트 규칙 표에 지속 규칙 한 줄만 남기고 이슈 번호나 제안 표시는 붙이지 않습니다. +4. 이슈 본문은 일반화합니다. 제품명·고객·사내 주소·업무 데이터를 쓰지 않고, 제안한 프로젝트 이름 대신 프로젝트 종류(예: 사내 업무 앱)만 씁니다. +5. 공개 게시라 되돌리기 어려우므로 에이전트는 이슈 본문 초안을 사용자에게 보이고 승인을 받은 뒤 올립니다. 에이전트가 이슈를 올릴 수 있으면(`gh`가 있고 로그인됨) 승인받아 `gh issue create --repo dev-goraebap/dev.goraebap`로 올립니다. 올릴 수 없으면 같은 형식의 이슈 문서를 `~/.goraebap/issues/-<짧은 영문 이름>.md`로 만들고, 새 이슈 페이지 주소(`https://github.com/dev-goraebap/dev.goraebap/issues/new`)와 함께 "이 내용을 붙여 넣어 올려 주세요"라고 안내합니다. +6. 검토와 반영은 원본 저장소 세션에서 합니다. 열린 제안 이슈를 하나씩 보고 받아들이기·고쳐서 받기·버리기를 정한 뒤, 결과(반영한 판, 버린 이유)를 이슈에 남기고 닫습니다. 받아들인 것은 [글쓰기 맡기기](../../goraebap-mode/references/writer.md)로 스킬을 고치고 검사·판 올리기·배포를 합니다. +7. 프로젝트는 플러그인이 갱신되면 닫힌 이슈를 보고 프로젝트 규칙 표의 같은 줄을 지우거나 예외로 남깁니다. -기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 이 제안함으로 제안서를 만드는 절차를 따릅니다. +기존 회수(프로젝트 규칙 중 공통으로 올릴 것 찾기)도 같은 절차로 이슈를 올립니다. ## 제안서 형식 @@ -21,5 +22,5 @@ | 지금 규칙 | 현재 지침이 정한 기준 한 문장 | | 바꿀 규칙 | 새로 바꿀 기준 한 문장 | | 이유와 근거 | 프로젝트에서 일어난 일과 바꿀 이유 | -| 제안한 프로젝트 이름 | 제안을 만든 프로젝트 이름 | +| 프로젝트 종류 | 제안을 만든 프로젝트의 종류(예: 사내 업무 앱) | | 날짜 | 제안한 날짜(`YYYY-MM-DD`) | diff --git a/plugins/webcraft/tests/package.test.mjs b/plugins/webcraft/tests/package.test.mjs index 7b74116..09874da 100644 --- a/plugins/webcraft/tests/package.test.mjs +++ b/plugins/webcraft/tests/package.test.mjs @@ -4,6 +4,7 @@ import path from 'node:path'; import test from 'node:test'; import { fileURLToPath } from 'node:url'; import { explicitOnly, packagePlugins } from '../scripts/package-plugins.mjs'; +import { compareVersions, notice } from '../hooks/update-check.mjs'; const root = fileURLToPath(new URL('../', import.meta.url)); const pkg = JSON.parse(await fs.readFile(path.join(root, 'package.json'), 'utf8')); @@ -45,7 +46,8 @@ for (const agent of ['claude', 'codex']) { test(`${agent}: 사본·동기화 도구와 문서 밖의 파일을 담지 않는다`, async () => { for (const file of await files(plugin)) { - assert(['.md', '.json', '.yaml'].includes(path.extname(file)), file); + const isHook = agent === 'claude' && path.relative(plugin, file).startsWith(`hooks${path.sep}`); + assert(['.md', '.json', '.yaml'].includes(path.extname(file)) || (isHook && path.extname(file) === '.mjs'), file); assert(!/manager|project-source|sync-skills/.test(path.relative(plugin, file)), file); } }); @@ -71,3 +73,25 @@ for (const agent of ['claude', 'codex']) { assert(!(await files(plugin)).some(file => /(^|[/\\])(site|posts|samples)([/\\]|$)/.test(path.relative(plugin, file)))); }); } + +test('claude: 새 판 알림 훅은 시작할 때만 돌고 담긴 스크립트를 가리킨다', async () => { + const plugin = path.join(dist, 'claude/plugins/webcraft'); + const config = JSON.parse(await fs.readFile(path.join(plugin, 'hooks/hooks.json'), 'utf8')); + const [entry] = config.hooks.SessionStart; + assert.equal(entry.matcher, 'startup'); + const script = entry.hooks[0].command.match(/\$\{CLAUDE_PLUGIN_ROOT\}\/([^"]+)/)[1]; + await fs.access(path.join(plugin, script)); + await assert.rejects(fs.access(path.join(dist, 'codex/plugins/webcraft/hooks'))); +}); + +test('새 판 알림은 최신 판이 더 높을 때만 낸다', () => { + assert(compareVersions('0.10.0', '0.9.9') > 0); + assert.equal(compareVersions('0.8.0', '0.8.0-rc.1'), 0); + assert.equal(notice('0.8.0', '0.8.0'), null); + assert.equal(notice('0.8.1', '0.8.0'), null); + assert.equal(notice('0.8.0', null), null); + const output = notice('0.8.0', '0.8.1'); + assert.match(output.systemMessage, /0\.8\.1/); + assert.equal(output.hookSpecificOutput.hookEventName, 'SessionStart'); + assert.match(output.hookSpecificOutput.additionalContext, /claude plugin update webcraft@dev-goraebap/); +}); diff --git a/site/content/app-shell/content-width.mdx b/site/content/app-shell/content-width.mdx index bd5b670..61433c8 100644 --- a/site/content/app-shell/content-width.mdx +++ b/site/content/app-shell/content-width.mdx @@ -38,6 +38,8 @@ sidebar: 본문 글과 상태·담당자·마감일 같은 속성을 함께 다루는 상세 화면은 최대 약 `720px` 읽기 열 오른쪽에 약 `256px` 폭의 속성 열을 나란히 둡니다. 본문 영역이 좁아져 두 열을 나란히 놓기 어려우면 오른쪽 속성 열을 없애고, 같은 속성들을 본문 제목 아래의 알약 줄로 옮깁니다(이 배치 모양은 [선호 화면](/app-shell/preferred-screens/)의 기준이기도 합니다 `[선호]`). +`[고래밥, 2026-11-09 결정]` 본문이 있는 상세 화면의 속성 열은 본문에 제목이 있으면 맨 위에 "목차", 그 아래 대상별 속성, 맨 끝에 "정보"(작성자·작성일·수정일·버전 수) 순서로 둡니다. 목차와 버전도 다른 속성과 같은 속성 행으로 그려 아이콘과 글자 자리를 맞추고, 휴대폰은 목차를 오른쪽 아래 둥근 버튼과 바텀시트로 둡니다. 편집기 가장자리에는 목차 표시를 붙이지 않습니다. 가장자리의 짧은 선 목차는 무엇인지 알기 어려웠고, 화면마다 작성 정보 자리가 제각각이었기 때문입니다. + Linear와 Plane의 상세 화면이 본문 오른쪽에 속성 패널을 두는 구성을 씁니다. 다만 Linear 화면의 실제 픽셀 수치는 공개 자료에 없어 확인하지 못했습니다. [Android 권장 레이아웃(Canonical layouts)](https://developer.android.com/develop/ui/compose/layouts/adaptive/canonical-layouts)의 보조 패널(Supporting pane) 지침은 넓은 화면(`expanded`)에서 본문과 보조 패널을 70:30 비율로 나누고, 중간 화면(`medium`)에서는 반반으로 나누도록 안내합니다. diff --git a/site/content/app-shell/states-and-feedback.mdx b/site/content/app-shell/states-and-feedback.mdx index 2fe1819..d9bea3d 100644 --- a/site/content/app-shell/states-and-feedback.mdx +++ b/site/content/app-shell/states-and-feedback.mdx @@ -22,7 +22,7 @@ sidebar: | 10초 초과 | 긴 작업 | `[관례]` 진행률을 표시합니다 | - `[관례]` 스켈레톤은 실제 데이터 화면의 배치와 같을 때만 씁니다([Primer: Loading](https://primer.style/product/ui-patterns/loading/)). 실제 배치와 다른 스켈레톤은 내용이 나타날 때 화면이 다시 튀게 만듭니다. -- `[고래밥]` 캐시된 데이터가 있으면 바로 그리고 뒤에서 조용히 갱신합니다. 캐시가 없을 때도 곧바로 빈 화면이나 스켈레톤으로 덮지 않고 지금 화면을 유지하다가 응답이 늦어질 때만 대기 표시를 보입니다. 짧은 대기 때문에 화면이 깜빡이지 않게 하기 위해서입니다. +- `[고래밥]` 앱 첫 진입만 스플래시로 덮고, 그 뒤의 화면 이동에는 본문을 가리는 막을 두지 않습니다. 캐시가 있으면 오래됐어도 바로 그리고 뒤에서 조용히 갱신하며, 링크에 포인터나 초점이 오면 그 화면을 미리 준비합니다. 캐시가 없을 때는 이전 화면을 그대로 두고 150ms 뒤에 콘텐츠 카드 위 테두리에 흐르는 진행 표시만 띄우며, 준비된 화면끼리는 콘텐츠 영역만 200ms 교차 페이드합니다. 막은 상세 안 사이드바까지 덮고 다시 방문할 때도 떠서 답답했고, 바꾼 뒤 다시 방문의 대기가 사라지고 레이아웃 이동이 0이었습니다. @@ -57,7 +57,8 @@ sidebar: `[고래밥, 2026-11-07 결정]` 토스트는 화면의 변화만으로는 완료 여부를 알 수 없는 짧은 동작에만 씁니다. Primer는 토스트 자체를 권하지 않고 Atlassian도 자동으로 닫히는 알림에 중요한 내용을 금합니다. 화면 변화가 없는 짧은 완료까지 인라인이나 배너로 알리면 화면 자리만 차지하므로 그 경우에만 토스트를 허용하고, 오류와 중요 안내는 원위치에 고정해 놓치지 않게 합니다. - 주소 복사, 항목 보관, 백그라운드로 넘긴 작업의 시작처럼 완료 결과가 현재 화면에 바로 드러나지 않는 짧은 완료에만 토스트를 띄웁니다. -- 항목 추가나 상태 변경처럼 결과가 화면에 바로 보이면 토스트를 띄우지 않습니다. +- 항목 추가처럼 결과가 화면에 바로 보이면 아래의 바로 저장되는 속성 변경을 빼고는 토스트를 띄우지 않습니다. +- `[고래밥, 2026-11-09 결정]` 상세에서 저장 버튼 없이 바로 저장되는 속성 변경(상태·담당·날짜·라벨 등)은 결과가 화면에 보여도 5초 "되돌리기" 토스트를 띄우고, 남은 시간을 줄어드는 막대로 보이며 포인터가 올라가면 멈춥니다. 확인 단계가 없어 잘못 누르면 되돌릴 길이 필요하기 때문입니다. - 오류, 경고, 되돌릴 수 없는 결과, 사용자가 읽고 다음 행동을 해야 하는 내용은 토스트에 두지 않습니다. - 보관이나 삭제 취소처럼 되돌릴 수 있는 완료 알림에는 토스트 안에 `되돌리기` 동작 하나를 둘 수 있습니다. 이때는 사용자가 읽고 누를 수 있도록 충분히 오래 남깁니다. diff --git a/site/content/common/icons.mdx b/site/content/common/icons.mdx index 9730540..889026d 100644 --- a/site/content/common/icons.mdx +++ b/site/content/common/icons.mdx @@ -48,6 +48,7 @@ sidebar: | 알약(작은 칩·배지) 안 | 14px | 좁은 높이에 맞춰 본문 옆 아이콘보다 한 단계 줄입니다 **[고래밥]** | - **[고래밥]** 루트 글자 크기가 16px인 환경에서는 글자 옆 아이콘의 잉크 중심이 글자보다 약 0.5px 낮게 보여 반 픽셀 위로 올리고, 루트 글자 크기가 커지는 1920px 이상 화면에서는 보정을 멈춥니다. 이 정렬 보정은 공통 아이콘 규칙 한 곳에서만 처리하고, 개별 부품마다 따로 이동값을 누적하지 않습니다. +- **[고래밥]** 속성 열이나 활동 기록처럼 아이콘이 세로로 늘어선 곳은 상자 크기뿐 아니라 24 격자 위에 그려진 범위(대부분 20칸 안팎)도 맞춥니다. 범위가 눈에 띄게 작은 아이콘은 키우면 선도 굵어지므로 키우지 않고 같은 뜻의 다른 아이콘으로 바꾸며, SVG `getBBox()`로 확인합니다. 기본 상태 줄에 Line Duotone처럼 일부 선이 흐린 형태를 섞지 않습니다. ## 접근성과 라벨 표시