@bstage-sdk/core 0.8.5 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -174,7 +174,7 @@ var HttpTimeoutError = class extends Error {
174
174
  };
175
175
 
176
176
  // src/version.ts
177
- var VERSION = "0.8.5";
177
+ var VERSION = "0.8.7";
178
178
 
179
179
  // src/client/BstageClient.ts
180
180
  function resolveBaseUrl() {
@@ -630,6 +630,16 @@ var SLOT_CATALOG_V2 = {
630
630
  description: "\uCEE4\uBBA4\uB2C8\uD2F0 \uBCF4\uB4DC \uBAA9\uB85D \u2014 \uBAA9\uB85D \uC704",
631
631
  context: {}
632
632
  },
633
+ // 유저 — 홈 (`/home`. 루트 메뉴가 홈인 스페이스는 `/`도 여기로 온다. 어드민이 구성한 섹션 묶음을 SectionPage가 그리므로 샵 홈과 같은 주입 지점 패턴)
634
+ // 섹션 구성이 스페이스마다 달라 섹션 사이 자리는 특정할 수 없다 — 묶음 위·아래 두 자리만 둔다
635
+ "user.home.section:before": {
636
+ description: "\uD648 \u2014 \uC139\uC158 \uBB36\uC74C \uC704",
637
+ context: {}
638
+ },
639
+ "user.home.section:after": {
640
+ description: "\uD648 \u2014 \uC139\uC158 \uBB36\uC74C \uC544\uB798 (footer \uC704)",
641
+ context: {}
642
+ },
633
643
  // 유저 — 콘텐츠 홈 (결정-018에 1차 명시. curation은 신설, contents-section은 옛 contents.home.contents.before/after 이전)
634
644
  "user.contents-home.curation:after": {
635
645
  description: "\uCF58\uD150\uCE20 \uD648 \u2014 \uD050\uB808\uC774\uC158 \uC139\uC158 \uC544\uB798",
package/dist/index.d.cts CHANGED
@@ -779,6 +779,14 @@ declare const SLOT_CATALOG_V2: {
779
779
  readonly description: "커뮤니티 보드 목록 — 목록 위";
780
780
  readonly context: {};
781
781
  };
782
+ readonly 'user.home.section:before': {
783
+ readonly description: "홈 — 섹션 묶음 위";
784
+ readonly context: {};
785
+ };
786
+ readonly 'user.home.section:after': {
787
+ readonly description: "홈 — 섹션 묶음 아래 (footer 위)";
788
+ readonly context: {};
789
+ };
782
790
  readonly 'user.contents-home.curation:after': {
783
791
  readonly description: "콘텐츠 홈 — 큐레이션 섹션 아래";
784
792
  readonly context: {};
package/dist/index.d.ts CHANGED
@@ -779,6 +779,14 @@ declare const SLOT_CATALOG_V2: {
779
779
  readonly description: "커뮤니티 보드 목록 — 목록 위";
780
780
  readonly context: {};
781
781
  };
782
+ readonly 'user.home.section:before': {
783
+ readonly description: "홈 — 섹션 묶음 위";
784
+ readonly context: {};
785
+ };
786
+ readonly 'user.home.section:after': {
787
+ readonly description: "홈 — 섹션 묶음 아래 (footer 위)";
788
+ readonly context: {};
789
+ };
782
790
  readonly 'user.contents-home.curation:after': {
783
791
  readonly description: "콘텐츠 홈 — 큐레이션 섹션 아래";
784
792
  readonly context: {};
package/dist/index.js CHANGED
@@ -119,7 +119,7 @@ var HttpTimeoutError = class extends Error {
119
119
  };
120
120
 
121
121
  // src/version.ts
122
- var VERSION = "0.8.5";
122
+ var VERSION = "0.8.7";
123
123
 
124
124
  // src/client/BstageClient.ts
125
125
  function resolveBaseUrl() {
@@ -575,6 +575,16 @@ var SLOT_CATALOG_V2 = {
575
575
  description: "\uCEE4\uBBA4\uB2C8\uD2F0 \uBCF4\uB4DC \uBAA9\uB85D \u2014 \uBAA9\uB85D \uC704",
576
576
  context: {}
577
577
  },
578
+ // 유저 — 홈 (`/home`. 루트 메뉴가 홈인 스페이스는 `/`도 여기로 온다. 어드민이 구성한 섹션 묶음을 SectionPage가 그리므로 샵 홈과 같은 주입 지점 패턴)
579
+ // 섹션 구성이 스페이스마다 달라 섹션 사이 자리는 특정할 수 없다 — 묶음 위·아래 두 자리만 둔다
580
+ "user.home.section:before": {
581
+ description: "\uD648 \u2014 \uC139\uC158 \uBB36\uC74C \uC704",
582
+ context: {}
583
+ },
584
+ "user.home.section:after": {
585
+ description: "\uD648 \u2014 \uC139\uC158 \uBB36\uC74C \uC544\uB798 (footer \uC704)",
586
+ context: {}
587
+ },
578
588
  // 유저 — 콘텐츠 홈 (결정-018에 1차 명시. curation은 신설, contents-section은 옛 contents.home.contents.before/after 이전)
579
589
  "user.contents-home.curation:after": {
580
590
  description: "\uCF58\uD150\uCE20 \uD648 \u2014 \uD050\uB808\uC774\uC158 \uC139\uC158 \uC544\uB798",
@@ -59,10 +59,12 @@ liquid, liquid 파일이 없고 `src/pages|slots/**/template.tsx`가 있거나 S
59
59
  실패할 때의 보조 수단이며, 판정 결과와 어긋나면 덮어쓰지 않고 멈춥니다.
60
60
  - `bstage skills install`은 `bstage ai install`의 **deprecated 별칭**입니다. 경고를 출력하고 같은 일을
61
61
  하며 다음 릴리즈에서 제거됩니다.
62
- - `bstage doctor`는 진단에 더해 **SDK 소유 스킬만** 자동 동기화합니다. `package.json`과 `.husky`는
63
- 건드리지 않으므로, pre-commit 시크릿 가드는 `bstage ai install`로 설치하세요.
62
+ - `bstage doctor`는 진단에 더해 **스킬이 어긋났을 때만** `bstage ai install`과 같은 동기화를 자동으로
63
+ 합니다 — 스킬뿐 아니라 `AGENTS.md` 관리 영역 갱신과 없는 `AGENTS.md`·`CLAUDE.md` 생성까지 포함됩니다.
64
+ `--json`으로 실행하면 아무것도 쓰지 않습니다. `package.json`과 `.husky`는 건드리지 않으므로,
65
+ pre-commit 시크릿 가드는 `bstage ai install`로 설치하세요.
64
66
 
65
- `doctor`의 종료 코드는 전부 최신이면 0, 하나라도 어긋나면 2입니다. 상태 값은 `ok` · `stale` · `missing` ·
67
+ `bstage ai doctor`의 종료 코드는 전부 최신이면 0, 하나라도 어긋나면 2입니다(`bstage doctor`의 종료 코드 0·1과는 별개입니다). 상태 값은 `ok` · `stale` · `missing` ·
66
68
  `extra`이며, **`extra`(지금 kind에 속하지 않는 SDK 스킬이 남아 있음)는 경고일 뿐 종료 코드에 영향을 주지
67
69
  않습니다.** 혼합 레포에서는 어느 SDK 스킬도 `extra`가 아닙니다(두 세트가 모두 대상). 설치가 남의 파일을 지우지 않는 것이 설계라, 실패로 치면 사람이 손으로 지울 때까지 CI가 계속
68
70
  빨간불이 되기 때문입니다.
@@ -57,7 +57,7 @@ Vite 빌드라서 `import.meta.env.VITE_BSTAGE_*`는 **빌드 시점에 문자
57
57
 
58
58
  - **대상 판정** — 두 조건을 모두 만족할 때만 점검합니다.
59
59
  1. 번들에 BstageClient의 앱 ID 헤더(`X-BSTAGE-APP-ID`)가 있을 것. API를 쓰지 않는 UI 전용 템플릿은 `client.ts`를 스캐폴드만 하고 import하지 않으면 트리셰이킹으로 사라지므로 대상이 아닙니다.
60
- 2. 소스가 `import.meta.env.VITE_BSTAGE_*`를 참조할 것. cli 0.40.1 이전 스캐폴드처럼 `client.ts`에 키를 리터럴로 박은 프로젝트는 `.env`가 없는 게 정상이므로(그 전환은 마이그레이션에서 `선택`) 대상이 아닙니다. 참조한 변수만 검사합니다.
60
+ 2. 소스가 `import.meta.env.VITE_BSTAGE_*`를 참조할 것. 옛 스캐폴드처럼 `client.ts`에 키를 리터럴로 박은 프로젝트는 `.env`가 없는 게 정상이므로(그 전환은 마이그레이션에서 `선택`) 대상이 아닙니다. 참조한 변수만 검사합니다.
61
61
  - **검사 항목** — 값 누락, 자리표시자(`YOUR_APP_ID` 등) 잔존, appId·appKey 뒤바뀜, 접두사(`bsa_`/`bsp_`) 불일치. 앱 키는 `VITE_BSTAGE_APP_KEY`(정본)와 옛 이름 `VITE_BSTAGE_APP_SECRET`을 한 값으로 보며, 한쪽만 있으면 빌드가 다른 이름으로도 읽히게 이어 붙인다
62
62
  - **env 해석** — Vite의 `loadEnv`를 그대로 씁니다. `.env` 파일뿐 아니라 CI가 `process.env`로 주입한 값도 번들에 인라인되므로 파일만 읽으면 오탐이 납니다.
63
63
  - **빌드를 실패시키지 않습니다** — 값이 비어도 번들 자체는 정상적으로 나오고, 여기서 실패시키면 기존 소비자 CI가 깨집니다. 경고는 빌드 로그 맨 끝에 출력됩니다.
@@ -584,8 +584,9 @@ npx @bstage-sdk/cli@latest init --target admin
584
584
 
585
585
  - **키 발급**: 포털 `조직 › API 키`에서 키 타입 **MANAGEMENT**, 스테이지는 이 화면이 들어갈 **하나**, 허용 Origin은 **어드민 콘솔 도메인**(예: sandbox는 `https://{스테이지ID}.mysandstage.in`)으로 발급합니다. 허용 Origin에 자동으로 채워지는 값은 유저 도메인이라 그대로 두면 어드민에서 온 호출이 거부됩니다. 변수 이름은 유저 템플릿과 같고 값만 다릅니다 — `VITE_BSTAGE_APP_KEY=bsm_…`.
586
586
  - **Surface와 키 타입은 짝입니다** — USER ↔ PUBLIC(`bsp_`), ADMIN ↔ MANAGEMENT(`bsm_`). PUBLIC 키로 어드민 게이트웨이를 부르면 `401 KEY_TYPE_NOT_ALLOWED`, 어드민에 로그인하지 않았거나 어드민 도메인 밖에서 부르면 `401 MISSING_ADMIN_ACCESS_TOKEN`, 허용 Origin이 유저 도메인으로 남아 있으면 `403`입니다. 반대로 MANAGEMENT 키를 유저 템플릿에 넣으면 유저 게이트웨이가 거부하고 키만 노출됩니다 — 그래서 포털은 사용자(USER) 레포의 빌드 환경변수에 `bsm_` 값을 저장하지 않습니다(400). 게시한 뒤에는 `bstage doctor`가 키 타입 불일치(`appkey.type_surface`)를 짚어 줍니다.
587
- - **MANAGEMENT 키도 빌드 결과물에 평문으로 실립니다.** 어드민 화면을 열 수 있는 사람은 개발자 도구로 볼 수 있으므로, 허용 Origin과 스테이지 범위를 좁히는 발급 조건이 곧 방어선입니다. 의심되면 포털에서 회전(새 키 발급 + 옛 키 폐기)한 뒤 빌드 환경변수를 바꾸고 다시 빌드합니다. 서버용(SIGNING) 키는 어떤 템플릿에도 넣지 않습니다.
587
+ - **MANAGEMENT 키도 빌드 결과물에 평문으로 실립니다.** 어드민 화면을 열 수 있는 사람은 개발자 도구로 볼 수 있으므로, 허용 Origin과 스테이지 범위를 좁히는 발급 조건이 곧 방어선입니다. 의심되면 포털에서 회전(새 키 발급 + 옛 키 즉시 만료)한 뒤 빌드 환경변수를 바꾸고 다시 빌드합니다 — 회전하면 APP KEY뿐 아니라 **APP ID도 새 값**이므로 둘 다 바꿉니다. 서버용(SIGNING) 키는 어떤 템플릿에도 넣지 않습니다.
588
588
  - **호출할 수 있는 API**는 포털 개발자 문서의 **API 레퍼런스 › 운영자 탭**에 있는 것뿐입니다. 거기 없는 경로를 추측해 만들지 말고, 필요한 API가 목록에 없으면 b.stage에 요청하세요.
589
+ - **포털은 빌드 환경변수 저장 시점에 거부합니다**(400): 키 이름이 `^VITE_[A-Z0-9_]+$`가 아니거나 32개 초과·이름 128자 초과·값 2048자 초과, 앞뒤 공백을 뺀 값이 `bss_`로 시작(모든 레포)하거나 `bsm_`로 시작(사용자 레포), APP ID·APP KEY가 그 스테이지 키와 맞지 않음(공백 포함·없는 키·만료/폐기·스테이지 범위 밖·짝이 아닌 KEY — 파트너 조회가 실패하면 이 검사는 건너뜁니다).
589
590
  - **로컬·미리보기에서는 응답이 오지 않습니다.** `bstage dev`는 어드민 게이트웨이를 프록시하지 않고(어드민 모드에서 어드민 호스트로 가는 것은 로그인뿐), 포털 미리보기는 템플릿의 직접 게이트웨이 호출을 막습니다. 어드민 API 응답은 게시한 뒤 실제 어드민 화면에서 확인하세요.
590
591
 
591
592
  ---
@@ -631,7 +632,7 @@ CI에서는 `BSTAGE_TOKEN`·`BSTAGE_PORTAL_URL`(또는 `VITE_BSTAGE_PHASE`)·`BS
631
632
  | `rollback [buildId]` | 이전 성공 빌드 재적용 |
632
633
  | `publish on\|off` | 게시 토글 (라이브 버전은 유지) |
633
634
 
634
- 확인 프롬프트가 있는 명령(`deploy`·`rollback`·`publish`·`link`)에서 `--json`은 프롬프트와 섞일 수 없어 **`--yes`와 함께**만 쓸 수 있습니다(`list`·`logs`·`whoami`는 `--json`만으로 됩니다). 출력은 stdout에 객체 **하나**뿐입니다. `deploy`·`rollback`·`publish`는 같은 모양을 씁니다 — 명령마다 다르게 파싱하지 않아도 됩니다.
635
+ 확인 프롬프트가 있는 명령(`deploy`·`rollback`·`publish`·`link`·`placement create`)에서 `--json`은 프롬프트와 섞일 수 없어 **`--yes`와 함께**만 쓸 수 있습니다(`list`·`logs`·`whoami`는 `--json`만으로 됩니다). 출력은 stdout에 객체 **하나**뿐입니다. `deploy`·`rollback`·`publish`는 같은 모양을 씁니다 — 명령마다 다르게 파싱하지 않아도 됩니다.
635
636
 
636
637
  | 키 | 내용 |
637
638
  | ------------- | ----------------------------------------------------- |
@@ -647,7 +648,7 @@ CI에서는 `BSTAGE_TOKEN`·`BSTAGE_PORTAL_URL`(또는 `VITE_BSTAGE_PHASE`)·`BS
647
648
 
648
649
  위젯(슬롯) 배치를 만드는 일과 배치를 지우는 일은 포털 화면에서만 합니다.
649
650
 
650
- **배포했는데 페이지가 기대와 다르면 `bstage doctor`부터 실행하세요.** 로그인·링크된 디렉터리에서는 로컬 진단 뒤에 "포털 진단" 절이 붙어, 이 레포의 배치마다 포털이 판정한 문제(게시 꺼짐, 게시된 빌드 없음, 만료·불일치한 앱 키, 화면과 맞지 않는 키 타입, 다시 빌드하지 않은 환경변수, 반영 안 된 새 빌드 등)를 조치와 함께 보여 줍니다. 14일 안에 만료되는 키는 경고(`appkey.expiring_soon`)로 미리 알려 줍니다. 배치가 많으면 30개까지만 보고, `--placement <id|경로|슬롯 id>`로 하나만 볼 수 있습니다. `--json`은 `portal.placements[].findings[]`에 `{ id, severity, title, detail?, fix?, evidence? }`를 담고 `id`(예: `appkey.expired`)는 바뀌지 않는 식별자입니다. 진단에 `error` 항목이 있으면 종료 코드 1입니다(`warn`·`info`는 종료 코드를 바꾸지 않습니다). 로그인·링크가 없거나 포털이 아직 진단을 지원하지 않으면 그 절만 건너뜁니다(단 `--placement`를 줬으면 위 표대로 2·3으로 끝납니다).
651
+ **배포했는데 페이지가 기대와 다르면 `bstage doctor`부터 실행하세요.** 로그인·링크된 디렉터리에서는 로컬 진단 뒤에 "포털 진단" 절이 붙어, 이 레포의 배치마다 포털이 판정한 문제(게시 꺼짐, 게시된 빌드 없음, 만료·불일치한 앱 키, 화면과 맞지 않는 키 타입, 다시 빌드하지 않은 환경변수, 반영 안 된 새 빌드 등)를 조치와 함께 보여 줍니다. 14일 안에 만료되는 키는 경고(`appkey.expiring_soon`)로 미리 알려 줍니다. 배치가 많으면 30개까지만 보고, `--placement <id|경로|슬롯 id>`로 하나만 볼 수 있습니다. `--json`은 `portal.placements[].findings[]`에 `{ id, severity, title, detail?, fix?, evidence? }`를 담고 `id`(예: `appkey.expired`)는 바뀌지 않는 식별자입니다. 진단에 `error` 항목이 있거나 **포털 진단 자체가 실패하면**(`portal.status`가 `error` — 이유는 `portal.reason`·`portal.placements[].error`) 종료 코드 1입니다(`warn`·`info`는 종료 코드를 바꾸지 않습니다). 로그인·링크가 없거나 포털이 아직 진단을 지원하지 않으면 그 절만 건너뜁니다(단 `--placement`를 줬으면 위 표대로 링크 없음은 2, 토큰 없음은 3이고, 이 레포에 맞는 배치가 없어도 2로 끝납니다).
651
652
 
652
653
  로컬에서 산출물을 확인하고 싶으면 `npx bstage build`를 실행합니다. 페이지는 `dist/{경로}/template.js`, 위젯은 `dist/{슬롯 id}/template.js`로 나옵니다.
653
654
 
@@ -668,7 +669,7 @@ CI에서는 `BSTAGE_TOKEN`·`BSTAGE_PORTAL_URL`(또는 `VITE_BSTAGE_PHASE`)·`BS
668
669
 
669
670
  ### 9.2 배포 전 점검 — 증상으로 원인 되짚기
670
671
 
671
- 아래 항목은 **SDK가 감지할 수 없습니다.** 키 형식(`bsa_`/`bsp_`)은 ID·시크릿 구분일 뿐 어느 환경에서 발급했는지를 담지 않습니다. 나머지는 BE·콘솔 설정이라 코드에 흔적이 없습니다. 리얼 배포 후 아래 에러를 만나면 이 표로 되짚으세요.
672
+ 아래 항목은 **빌드 시점에는 감지할 수 없습니다.** 키 형식(`bsa_`/`bsp_`)은 ID·시크릿 구분일 뿐 어느 환경에서 발급했는지를 담지 않고, 나머지는 BE·콘솔 설정이라 코드에 흔적이 없습니다. 게시한 뒤에는 `bstage doctor`의 포털 진단이 일부(키 문제, 커스텀 템플릿 토글)를 짚어 줍니다. 리얼 배포 후 아래 에러를 만나면 이 표로 되짚으세요.
672
673
 
673
674
  | 증상 | 원인 | 조치 | 소관 |
674
675
  | ----------------------- | ---------------------------------------- | -------------------------------------------- | ------------ |
package/docs/MIGRATION.md CHANGED
@@ -7,7 +7,7 @@
7
7
  - **누가 쓰나**: SDK 작성자. 소비자 영향이 있는 변경을 릴리즈하는 **그 시점에** 항목을 추가한다. 마이그레이션할 때 만드는 게 아니라 미리 쌓아둔다.
8
8
  - **누가 읽나**: 소비자 프로젝트의 마이그레이션 도구(아래)와 사람.
9
9
  - **어디에 실리나**: `docs/`는 core 패키지 배포물에 포함되므로, 소비자는 `node_modules/@bstage-sdk/core/docs/MIGRATION.md`로 이 파일을 본다. cli도 빌드 시 `dist/MIGRATION.md`로 같은 파일을 복사해 `bstage doctor`가 동일 가이드를 읽는다.
10
- - **파일 배포 버전 ≠ 항목 헤더 버전**: 이 파일이 **실려 나가는** 버전(core)과 각 항목 헤더의 버전은 별개다. 패키지는 완전 독립 버전(cli·core·react·host·design이 서로 다른 버전 라인)이라, 항목 헤더는 그 변경을 **실제로 낸 패키지**의 버전을 가리킨다(대개 cli·react이며 core가 아닐 수 있다). 즉 core를 설치한 소비자도 헤더가 `cli 0.43.0`인 항목을 적용하려면 **cli**를 0.43.0 이상으로 올려야 한다.
10
+ - **버전 라인과 항목 헤더**: 공개 패키지(cli·core·react·host·design·ai-toolkit)는 **한 버전 라인을 함께 쓴다** — 릴리즈마다 모든 패키지가 같은 번호로 나간다. 항목 헤더는 그중 그 변경을 **실제로 낸 패키지**를 가리키므로(대개 cli·react·ai-toolkit이며 이 파일이 실려 나가는 core가 아닐 수 있다), 헤더가 `cli 0.8.5`인 항목을 적용하려면 **cli**를 0.8.5 이상으로 올려야 한다.
11
11
 
12
12
  ## 마이그레이션이 동작하는 방식
13
13
 
@@ -20,13 +20,13 @@
20
20
 
21
21
  ### 도구
22
22
 
23
- - **감지**: `bstage doctor` (`npx @bstage-sdk/cli@latest doctor`). 버전 드리프트·보일러플레이트 누락·적용 가능한 마이그레이션 항목을 read-only로 보고한다. `--json`으로 구조화 출력.
23
+ - **감지**: `bstage doctor` (`npx @bstage-sdk/cli@latest doctor`). 버전 드리프트·보일러플레이트 누락·적용 가능한 마이그레이션 항목을 보고한다. 진단 자체는 프로젝트 코드를 고치지 않지만, `--json` 없이 실행하면 SDK가 관리하는 스킬이 뒤처졌을 때 `ai install`과 같은 동기화(스킬·`AGENTS.md` 관리 영역, 없는 `AGENTS.md`·`CLAUDE.md` 생성)를 자동으로 한다. `--json`은 구조화 출력이며 아무것도 쓰지 않는다.
24
24
  - **변환**: `bstage-migrate` 스킬 (`.claude/skills/bstage-migrate/SKILL.md`). doctor 출력과 이 문서를 읽고 프로젝트에 맞춰 적용한다.
25
25
 
26
26
  ### 마이그레이션 스킬 부트스트랩
27
27
 
28
28
  - **새 프로젝트** (이 기능 도입 이후 `bstage init`): `.claude/skills/`에 스킬이 이미 들어 있다. 별도 작업 없음.
29
- - **옛 프로젝트**: `npx @bstage-sdk/cli@latest skills install`로 스킬을 설치한다(`.claude/skills/`에 기록). SDK 버전업 후 다시 실행하면 최신으로 동기화된다. 스킬이 없어도 `bstage doctor` 출력 + 이 문서만으로 AI가 마이그레이션을 진행할 수 있다.
29
+ - **옛 프로젝트**: `npx @bstage-sdk/cli@latest ai install`로 스킬을 설치한다(`.claude/skills/`에 기록). SDK 버전업 후 다시 실행하면 최신으로 동기화된다. 스킬이 없어도 `bstage doctor` 출력 + 이 문서만으로 AI가 마이그레이션을 진행할 수 있다.
30
30
 
31
31
  ## 항목 작성 규칙
32
32
 
@@ -43,14 +43,33 @@
43
43
  - **변경 내용** — 무엇이 왜 바뀌었는지 (배경은 한 줄, 상세는 관련 docs 링크)
44
44
  - **적용** — 단계. 자동 단계와 판단 단계를 구분해서 적는다.
45
45
 
46
- > **`적용` 단계는 옛 프로젝트에서 실제로 따라가 본 뒤 적는다.** 새 `bstage init` 프로젝트에서 되는 것과, 기존 프로젝트가 이 절차만 보고 도달하는 것은 다르다. 따라 했을 때 깨지는 선행 조건이 있으면 **그 조건까지 적는다** — `core 0.44.0` 항목이 그 사례다(스크립트만 넣으면 `@types/node` 부재로 `TS2688`이 난다. 스캐폴드 템플릿을 고쳐 새 프로젝트는 해결됐지만, 먼저 써둔 적용 절차를 되짚지 않아 기존 프로젝트용 지침이 깨진 채 남았다).
46
+ > **`적용` 단계는 옛 프로젝트에서 실제로 따라가 본 뒤 적는다.** 새 `bstage init` 프로젝트에서 되는 것과, 기존 프로젝트가 이 절차만 보고 도달하는 것은 다르다. 따라 했을 때 깨지는 선행 조건이 있으면 **그 조건까지 적는다** — 스크립트를 추가하던 항목이 그 사례다(스크립트만 넣으면 `@types/node` 부재로 `TS2688`이 난다. 스캐폴드 템플릿을 고쳐 새 프로젝트는 해결됐지만, 먼저 써둔 적용 절차를 되짚지 않아 기존 프로젝트용 지침이 깨진 채 남았다).
47
47
 
48
- > **헤더 규칙**: `## → {패키지} {버전}` 형식으로, 그 변경을 실제로 낸 패키지와 버전을 명시한다(예: `→ cli 0.3.0`, `→ core 0.3.0`). 한 변경이 여러 패키지를 함께 범프시켰으면 소비자가 올려야 할 패키지를 `·`로 나열한다(예: `→ core 0.3.0 · cli 0.3.0`). 소비자는 이 헤더의 패키지를 해당 버전 **이상**으로 올려야 한다. 단순 의존성 재핀(dependency bump)만 된 패키지는 헤더에 적지 않는다.
48
+ > **헤더 규칙**: `## → {패키지} {버전}` 형식으로, 그 변경을 실제로 낸 패키지와 버전을 명시한다(예: `→ cli 0.8.5`, `→ core 0.3.0`). 한 변경이 여러 패키지를 함께 범프시켰으면 소비자가 올려야 할 패키지를 `·`로 나열한다(예: `→ cli 0.8.5 · ai-toolkit 0.8.5`). 소비자는 이 헤더의 패키지를 해당 버전 **이상**으로 올려야 한다. 단순 의존성 재핀(dependency bump)만 된 패키지는 헤더에 적지 않는다.
49
49
  >
50
- > **정렬**: 릴리즈 **시점 최신이 위**. 패키지가 독립 버전이라 헤더 숫자만으로는 시간순 정렬이 되지 않으므로 릴리즈 순서를 기준으로 한다.
50
+ > **정렬**: 릴리즈 **시점 최신이 위**. 공개 패키지는 한 버전 라인을 쓰므로 헤더 숫자 순서가 곧 릴리즈 순서다.
51
51
 
52
52
  ---
53
53
 
54
+ ## → ai-toolkit 0.8.6
55
+
56
+ ### `bstage doctor` 종료 코드·동작 안내 정정(에이전트 스킬) — `자동`
57
+
58
+ **영향**
59
+
60
+ - 코드·빌드 동작은 바뀌지 않는다. 에이전트 스킬 본문만 바로잡았다.
61
+ - `bstage-deploy`·`bstage-migrate`·`bstage-onboarding` 스킬이 `bstage doctor`의 종료 코드 1을 "진단에 error 항목이 있을 때"로만 안내했고, doctor를 "파일을 고치지 않는다"고 적었다.
62
+
63
+ **변경 내용**
64
+
65
+ - `bstage-deploy`: 포털 진단 자체가 실패해도(`portal.status`가 `error`) 종료 코드 1이다. 이때 `reason`·`placements[].error`를 읽고, 권한 부족이면 계정 권한·CLI 토큰 등급부터 확인하도록 안내한다.
66
+ - `bstage-migrate`: `doctor --json`이 종료 코드 1이어도 JSON은 정상이며, 포털 진단 결과는 마이그레이션과 무관하다는 점을 적었다. "세 가지"로 적혀 있던 출력 항목 수를 네 가지로 바로잡았다.
67
+ - `bstage-onboarding`: doctor가 포털 진단을 함께 보고, `--json` 없이 실행하면 SDK 소유 스킬을 자동 동기화한다는 점을 적었다.
68
+
69
+ **적용**
70
+
71
+ 1. (자동) `npx @bstage-sdk/cli@latest ai update` — 스킬을 갱신한다.
72
+
54
73
  ## → cli 0.8.5 · ai-toolkit 0.8.5
55
74
 
56
75
  ### `bstage doctor`: 포털 진단이 실패해도 종료 코드 1 — `자동` · `선택`
@@ -81,6 +81,19 @@ export default createTemplate(ContentBadge, {
81
81
 
82
82
  ## 유저 (`target: user`)
83
83
 
84
+ ### 홈 — `/home`
85
+
86
+ 어드민이 구성한 섹션 묶음을 그리는 스페이스 홈. 루트 메뉴가 홈인 스페이스는 `/`도 이 페이지로 온다(루트가 샵·콘텐츠 등 다른 페이지면 `/home`에서만 보인다). 섹션 구성이 스페이스마다 달라 섹션 사이에는 자리를 두지 않고, 묶음 위·아래 두 자리만 연다.
87
+
88
+ 홈의 첫 섹션은 상단 바 아래로 당겨 그리는 브랜드 커버인 경우가 많다. `section:before`는 그 위에 놓이므로 흐름 높이를 차지하지 않는 위젯(팝업·모션)에 알맞고, 인라인 위젯은 아래쪽 여백을 넉넉히 둔다.
89
+
90
+ | 슬롯 키 | 위치 | context |
91
+ | -------------------------- | -------------------------- | ------- |
92
+ | `user.home.section:before` | 섹션 묶음 위 | — |
93
+ | `user.home.section:after` | 섹션 묶음 아래 (footer 위) | — |
94
+
95
+ ![홈 슬롯 배치](assets/slots/home.svg)
96
+
84
97
  ### 콘텐츠 홈 — `/contents`
85
98
 
86
99
  | 슬롯 키 | 위치 | context |
@@ -0,0 +1,67 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 951" width="720" height="951" role="img" aria-label="홈 페이지의 섹션 구성과 슬롯 자리">
2
+ <title>홈 — 슬롯 배치</title>
3
+ <rect x="0" y="0" width="720" height="951" rx="0" fill="#16191E"/>
4
+ <text x="8" y="941" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="10">PC · 모바일 섹션 순서 동일</text>
5
+ <text x="712" y="941" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="10" text-anchor="end">1440 × 약 1,850px · 50% 축소</text>
6
+ <rect x="0" y="0" width="720" height="36" rx="0" fill="#22272F"/>
7
+ <circle cx="224" cy="18" r="8" fill="#39414C"/>
8
+ <rect x="298" y="15" width="26" height="6" rx="3" fill="#5A6472"/>
9
+ <rect x="334" y="15" width="22" height="6" rx="3" fill="#39414C"/>
10
+ <rect x="366" y="15" width="18" height="6" rx="3" fill="#39414C"/>
11
+ <rect x="394" y="15" width="42" height="6" rx="3" fill="#39414C"/>
12
+ <rect x="446" y="15" width="24" height="6" rx="3" fill="#39414C"/>
13
+ <rect x="480" y="15" width="36" height="6" rx="3" fill="#39414C"/>
14
+ <circle cx="506" cy="18" r="6" fill="#39414C"/>
15
+ <circle cx="530" cy="18" r="6" fill="#39414C"/>
16
+ <circle cx="554" cy="18" r="6" fill="#39414C"/>
17
+ <circle cx="578" cy="18" r="6" fill="#39414C"/>
18
+ <circle cx="602" cy="18" r="6" fill="#39414C"/>
19
+ <rect x="2" y="44" width="716" height="55" rx="4" fill="#1E2536" stroke="#F0A030" stroke-width="1.6"/>
20
+ <text x="14" y="64" fill="#F0A030" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="8" font-weight="700" letter-spacing="1.1">▾ V2 SLOT</text>
21
+ <text x="14" y="84" fill="#EDF1F6" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="12.5">user.home.section:before</text>
22
+ <text x="706" y="84" fill="#616D7D" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="10" text-anchor="end">context 없음</text>
23
+ <rect x="87.5" y="106" width="541" height="155" rx="4" fill="#2A3038"/>
24
+ <text x="712" y="183.5" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="9" text-anchor="end">브랜드 커버</text>
25
+ <rect x="104" y="285" width="72" height="9" rx="3" fill="#5A6472"/>
26
+ <rect x="584" y="286.5" width="28" height="6" rx="3" fill="#39414C"/>
27
+ <rect x="104" y="305" width="118" height="115" rx="4" fill="#2A3038"/>
28
+ <rect x="234" y="305" width="118" height="115" rx="4" fill="#2A3038"/>
29
+ <rect x="364" y="305" width="118" height="115" rx="4" fill="#2A3038"/>
30
+ <rect x="494" y="305" width="118" height="115" rx="4" fill="#2A3038"/>
31
+ <rect x="104" y="429" width="52" height="6" rx="3" fill="#39414C"/>
32
+ <rect x="234" y="429" width="40" height="6" rx="3" fill="#39414C"/>
33
+ <rect x="364" y="429" width="66" height="6" rx="3" fill="#39414C"/>
34
+ <rect x="494" y="429" width="34" height="6" rx="3" fill="#39414C"/>
35
+ <text x="712" y="360" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="9" text-anchor="end">피드</text>
36
+ <rect x="2" y="455" width="716" height="85" rx="0" fill="#2A3038"/>
37
+ <rect x="278" y="455" width="164" height="85" rx="0" fill="#333B45"/>
38
+ <text x="712" y="497.5" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="9" text-anchor="end">배너</text>
39
+ <rect x="104" y="560" width="86" height="9" rx="3" fill="#5A6472"/>
40
+ <rect x="584" y="561.5" width="28" height="6" rx="3" fill="#39414C"/>
41
+ <rect x="104" y="580" width="118" height="67.5" rx="4" fill="#2A3038"/>
42
+ <rect x="234" y="580" width="118" height="67.5" rx="4" fill="#2A3038"/>
43
+ <rect x="364" y="580" width="118" height="67.5" rx="4" fill="#2A3038"/>
44
+ <rect x="494" y="580" width="118" height="67.5" rx="4" fill="#2A3038"/>
45
+ <rect x="104" y="656.5" width="52" height="6" rx="3" fill="#39414C"/>
46
+ <rect x="234" y="656.5" width="40" height="6" rx="3" fill="#39414C"/>
47
+ <rect x="364" y="656.5" width="66" height="6" rx="3" fill="#39414C"/>
48
+ <rect x="494" y="656.5" width="34" height="6" rx="3" fill="#39414C"/>
49
+ <text x="712" y="610" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="9" text-anchor="end">섹션 묶음</text>
50
+ <rect x="2" y="680" width="716" height="55" rx="4" fill="#1E2536" stroke="#F0A030" stroke-width="1.6"/>
51
+ <text x="14" y="700" fill="#F0A030" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="8" font-weight="700" letter-spacing="1.1">▾ V2 SLOT</text>
52
+ <text x="14" y="720" fill="#EDF1F6" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" font-size="12.5">user.home.section:after</text>
53
+ <text x="706" y="720" fill="#616D7D" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="10" text-anchor="end">context 없음</text>
54
+ <rect x="104" y="767" width="92" height="6" rx="3" fill="#39414C"/>
55
+ <rect x="104" y="779" width="80" height="6" rx="3" fill="#39414C"/>
56
+ <rect x="104" y="791" width="104" height="6" rx="3" fill="#39414C"/>
57
+ <rect x="104" y="803" width="70" height="6" rx="3" fill="#39414C"/>
58
+ <rect x="104" y="815" width="96" height="6" rx="3" fill="#39414C"/>
59
+ <rect x="104" y="827" width="58" height="6" rx="3" fill="#39414C"/>
60
+ <rect x="104" y="839" width="110" height="6" rx="3" fill="#39414C"/>
61
+ <line x1="92" y1="866" x2="624" y2="866" stroke="#2A3038" stroke-width="1"/>
62
+ <rect x="238" y="880" width="44" height="6" rx="3" fill="#4E5867"/>
63
+ <rect x="292" y="880" width="96" height="6" rx="3" fill="#39414C"/>
64
+ <rect x="398" y="880" width="22" height="6" rx="3" fill="#39414C"/>
65
+ <rect x="430" y="880" width="34" height="6" rx="3" fill="#39414C"/>
66
+ <text x="712" y="840" fill="#4E5867" font-family="ui-sans-serif, -apple-system, 'Segoe UI', Roboto, sans-serif" font-size="9" text-anchor="end">푸터</text>
67
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bstage-sdk/core",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "bstage SDK core — HTTP client for b.stage API",
5
5
  "type": "module",
6
6
  "publishConfig": {