lee-spec-kit 0.6.16 → 0.6.17

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/README.en.md CHANGED
@@ -40,26 +40,29 @@
40
40
  # 1) Initialize docs structure
41
41
  npx lee-spec-kit init
42
42
 
43
- # 2) Create a feature
43
+ # 2) Run initial onboarding checks
44
+ npx lee-spec-kit onboard --strict
45
+
46
+ # 3) Create a feature
44
47
  npx lee-spec-kit feature user-auth
45
48
 
46
- # 3) Show next steps (for agents)
49
+ # 4) Show next steps (for agents)
47
50
  npx lee-spec-kit context
48
51
 
49
- # 4) Show workflow dashboard
52
+ # 5) Show workflow dashboard
50
53
  npx lee-spec-kit view
51
54
 
52
- # 5) Show overall status
55
+ # 6) Show overall status
53
56
  npx lee-spec-kit status
54
57
 
55
- # 6) Validate docs / feature metadata
58
+ # 7) Validate docs / feature metadata
56
59
  npx lee-spec-kit doctor
57
60
  ```
58
61
 
59
62
  ## New Project Start Order
60
63
 
61
64
  For a brand-new project, scaffold the **codebase first**, then initialize docs.
62
- This sequence assumes the default `docsRepo: embedded` mode.
65
+ For most users (default: embedded), running `npx lee-spec-kit init` in project root is enough.
63
66
 
64
67
  ```bash
65
68
  # 0) Create/init the code project first (example: Next.js)
@@ -69,17 +72,32 @@ cd my-app
69
72
  # 1) Initialize docs structure
70
73
  npx lee-spec-kit init
71
74
 
72
- # 2) Detect project (agent entrypoint)
75
+ # 2) Run initial onboarding checks
76
+ npx lee-spec-kit onboard --strict
77
+
78
+ # 3) Detect project (agent entrypoint)
73
79
  npx lee-spec-kit detect --json
74
80
 
75
- # 3) Create feature and start workflow
81
+ # 4) Create feature and start workflow
76
82
  npx lee-spec-kit feature user-auth
77
- npx lee-spec-kit context --json
83
+ npx lee-spec-kit context --json-compact
78
84
  ```
79
85
 
80
86
  - Apply lee-spec-kit workflow only when `detect --json` returns `isLeeSpecKitProject: true`.
81
87
  - If `isLeeSpecKitProject: false`, continue with normal non-lee-spec-kit workflow.
82
- - If you use `standalone` docs, run commands against the docs repo location (`--dir` or `LEE_SPEC_KIT_DOCS_DIR`).
88
+
89
+ For teams that keep docs separate from the code repo (standalone), the recommended start point is the **parent workspace folder**.
90
+
91
+ ```bash
92
+ # Recommended layout:
93
+ # workspace/
94
+ # ├─ docs/ (lee-spec-kit docs)
95
+ # └─ project/ (actual code repo)
96
+ #
97
+ # Run from workspace root
98
+ npx lee-spec-kit init --docs-repo standalone --dir ./docs --project-root ./project
99
+ npx lee-spec-kit detect --json
100
+ ```
83
101
 
84
102
  ## Agent Kickoff Prompt
85
103
 
@@ -88,7 +106,7 @@ You can paste the following as an agent session-start instruction.
88
106
  ```text
89
107
  Start procedure:
90
108
  1) Run npx lee-spec-kit detect --json
91
- 2) If isLeeSpecKitProject === true, run npx lee-spec-kit context --json
109
+ 2) If isLeeSpecKitProject === true, run npx lee-spec-kit context --json-compact (use --json only when full detail is needed)
92
110
  3) If actionOptions exist, show approvalPrompt and finalPrompt exactly as provided, then wait for user approval (<LABEL> or <LABEL> OK)
93
111
  4) Do not execute before approval; execute requiresUserCheck=true actions only after approval
94
112
  5) If isLeeSpecKitProject === false, skip lee-spec-kit-specific flow and continue with normal workflow
@@ -99,7 +117,7 @@ Start procedure:
99
117
  ### 📁 Project initialization
100
118
 
101
119
  - Interactive init or CLI options
102
- - Supports `single` and `multi` (`fullstack` remains as a backward-compatible alias)
120
+ - Default is `multi`; `single` remains supported for simple single-repo and backward-compatibility scenarios (`fullstack` is a backward-compatible alias of `multi`)
103
121
  - Korean/English templates
104
122
 
105
123
  ### 🚀 Feature creation
@@ -178,6 +196,21 @@ npx lee-spec-kit detect --dir /path/to/workspace
178
196
 
179
197
  The `--json` payload includes `isLeeSpecKitProject`, `reasonCode` (`PROJECT_DETECTED` | `PROJECT_NOT_DETECTED`), `docsDir`, `configPath`, and `detectionSource` (`config` | `heuristic`).
180
198
 
199
+ ### Onboarding checks
200
+
201
+ Validate initial setup readiness (Constitution/PRD/git remotes, etc.).
202
+
203
+ ```bash
204
+ # human-readable output
205
+ npx lee-spec-kit onboard
206
+
207
+ # JSON output for agents/automation
208
+ npx lee-spec-kit onboard --json
209
+
210
+ # exit code 1 when WARN/BLOCK exists
211
+ npx lee-spec-kit onboard --strict
212
+ ```
213
+
181
214
  ### Create a feature
182
215
 
183
216
  ```bash
@@ -214,6 +247,7 @@ npx lee-spec-kit context
214
247
  # recommended: one feature + labels
215
248
  npx lee-spec-kit context F001
216
249
  npx lee-spec-kit context F001 --json
250
+ npx lee-spec-kit context F001 --json-compact
217
251
 
218
252
  # approve + execute (common path)
219
253
  npx lee-spec-kit context F001 --approve A --execute
@@ -232,6 +266,7 @@ Use advanced selectors (`--component`, `--all`, `--done`) only when you need mul
232
266
  | Option | Description |
233
267
  | -------------- | ----------------------------------------------- |
234
268
  | `--json` | JSON output for agents |
269
+ | `--json-compact` | Compact JSON for agents (implies `--json`, minimizes duplicated fields) |
235
270
  | `--component <id>` | Select target component in multi mode (e.g. `app`, `api`, `worker`) |
236
271
  | `--all` | Include completed features when auto-detecting |
237
272
  | `--done` | Show completed (workflow-done) features only |
@@ -246,7 +281,8 @@ Use advanced selectors (`--component`, `--all`, `--done`) only when you need mul
246
281
  - `--ticket` is required for `--execute` only when the selected action has `requiresUserCheck=true`.
247
282
  - It is short-lived (5 minutes by default) and cannot be reused after one execution.
248
283
 
249
- `context --json` is organized into `actions` (atomic actions), `actionOptions` (label mapping), and top-level metadata.
284
+ `context --json-compact` is the default recommended format, providing a reduced and deduplicated decision state.
285
+ Use `context --json` only when full-detail debugging fields are required.
250
286
 
251
287
  **Core fields (recommended for normal agent flows)**
252
288
 
@@ -475,8 +511,8 @@ Running `init` creates `.lee-spec-kit.json` in your docs root (default: `docs/`)
475
511
  `approval` only affects the following values produced by `context`:
476
512
 
477
513
  - the `[CHECK required]` tag in text output
478
- - `actions[].requiresUserCheck` in `context --json`
479
- - `checkPolicy.token` (`context --json`): approval token format (`<LABEL>`)
514
+ - `actionOptions[].requiresUserCheck` in `context --json-compact` (`actions[].requiresUserCheck` in `--json`)
515
+ - `checkPolicy.token` (`context --json-compact`/`--json`): approval token format (`<LABEL>`)
480
516
  - `checkPolicy.acceptedTokens`: accepted reply templates (e.g. `["<LABEL>", "<LABEL> OK", "<LABEL> ...", "... <LABEL> ..."]`)
481
517
  - `checkPolicy.tokenPattern`: input validation regex for approval replies
482
518
  - `checkPolicy.validLabels`: currently selectable labels (`A`, `B`, `C`...)
@@ -536,7 +572,7 @@ Example:
536
572
  #### Modes
537
573
 
538
574
  - `builtin` (default): keep built-in `requiresUserCheck` in steps/actions
539
- - `category` (recommended): control CHECK policy by `actions[].category`
575
+ - `category` (recommended): control CHECK policy by `actionOptions[].category` (`actions[].category` in `--json`)
540
576
  - `steps`: control by step numbers (not recommended; fragile)
541
577
 
542
578
  #### Fields
@@ -564,7 +600,7 @@ Example:
564
600
  }
565
601
  ```
566
602
 
567
- > To discover available `category` values, check `actions[].category` in `context --json`.
603
+ > To discover available `category` values, check `actionOptions[].category` in `context --json-compact` first, and use `actions[].category` in `context --json` when needed.
568
604
 
569
605
  ### pr (PR artifacts policy)
570
606
 
package/README.md CHANGED
@@ -55,26 +55,29 @@
55
55
  # 1. 프로젝트 문서 구조 생성
56
56
  npx lee-spec-kit init
57
57
 
58
- # 2. 기능 생성
58
+ # 2. 초기 온보딩 점검
59
+ npx lee-spec-kit onboard --strict
60
+
61
+ # 3. 새 기능 생성
59
62
  npx lee-spec-kit feature user-auth
60
63
 
61
- # 3. 진행 상황 및 다음 단계 확인 (AI 에이전트용)
64
+ # 4. 진행 상황 및 다음 단계 확인 (AI 에이전트용)
62
65
  npx lee-spec-kit context
63
66
 
64
- # 4. 워크플로우 대시보드 확인
67
+ # 5. 워크플로우 대시보드 확인
65
68
  npx lee-spec-kit view
66
69
 
67
- # 5. 전체 상태 확인
70
+ # 6. 전체 상태 확인
68
71
  npx lee-spec-kit status
69
72
 
70
- # 6. 문서/Feature 진단
73
+ # 7. 문서/Feature 진단
71
74
  npx lee-spec-kit doctor
72
75
  ```
73
76
 
74
77
  ## 신규 프로젝트 시작 순서
75
78
 
76
79
  신규 프로젝트에서는 **코드 스캐폴딩을 먼저** 하고, 그 다음 docs를 초기화하세요.
77
- 순서는 기본값인 `docsRepo: embedded` 기준입니다.
80
+ 대부분의 사용자(기본값: embedded)는 프로젝트 루트에서 `npx lee-spec-kit init`만 실행하면 됩니다.
78
81
 
79
82
  ```bash
80
83
  # 0. 코드 프로젝트 생성/초기화 (예: Next.js)
@@ -84,17 +87,32 @@ cd my-app
84
87
  # 1. docs 구조 초기화
85
88
  npx lee-spec-kit init
86
89
 
87
- # 2. 감지 확인 (에이전트 시작점)
90
+ # 2. 초기 온보딩 점검
91
+ npx lee-spec-kit onboard --strict
92
+
93
+ # 3. 감지 확인 (에이전트 시작점)
88
94
  npx lee-spec-kit detect --json
89
95
 
90
- # 3. Feature 생성 후 작업 시작
96
+ # 4. Feature 생성 후 작업 시작
91
97
  npx lee-spec-kit feature user-auth
92
- npx lee-spec-kit context --json
98
+ npx lee-spec-kit context --json-compact
93
99
  ```
94
100
 
95
101
  - `detect --json` 결과가 `isLeeSpecKitProject: true`일 때 lee-spec-kit 워크플로우를 적용하세요.
96
102
  - `isLeeSpecKitProject: false`면 일반 프로젝트 워크플로우로 진행하세요.
97
- - `standalone`으로 docs를 분리한 경우에는 docs 레포 위치(`--dir` 또는 `LEE_SPEC_KIT_DOCS_DIR`) 기준으로 실행하세요.
103
+
104
+ docs를 코드 저장소와 분리해 운영하는 팀(standalone)은 **상위 워크스페이스 폴더**를 기준으로 시작하는 것을 권장합니다.
105
+
106
+ ```bash
107
+ # 권장 레이아웃:
108
+ # workspace/
109
+ # ├─ docs/ (lee-spec-kit 문서)
110
+ # └─ project/ (실제 코드 저장소)
111
+ #
112
+ # workspace 루트에서 실행
113
+ npx lee-spec-kit init --docs-repo standalone --dir ./docs --project-root ./project
114
+ npx lee-spec-kit detect --json
115
+ ```
98
116
 
99
117
  ## 에이전트 킥오프 프롬프트
100
118
 
@@ -103,7 +121,7 @@ npx lee-spec-kit context --json
103
121
  ```text
104
122
  작업 시작 절차:
105
123
  1) npx lee-spec-kit detect --json
106
- 2) isLeeSpecKitProject === true 이면 npx lee-spec-kit context --json 실행
124
+ 2) isLeeSpecKitProject === true 이면 npx lee-spec-kit context --json-compact 실행 (필요 시 --json으로 상세 조회)
107
125
  3) actionOptions가 있으면 approvalPrompt와 finalPrompt를 그대로 사용자에게 제시하고 승인(<LABEL> 또는 <LABEL> OK) 대기
108
126
  4) 승인 전에는 실행하지 말고, requiresUserCheck=true 액션은 승인 후에만 실행
109
127
  5) isLeeSpecKitProject === false 이면 lee-spec-kit 전용 절차를 건너뛰고 일반 워크플로우로 진행
@@ -114,7 +132,7 @@ npx lee-spec-kit context --json
114
132
  ### 📁 프로젝트 초기화
115
133
 
116
134
  - 대화형 모드 또는 CLI 옵션으로 프로젝트 설정
117
- - Single(단일 레포) / Multi(멀티 컴포넌트) 프로젝트 타입 지원 (`fullstack`는 하위호환 alias)
135
+ - 기본값은 Multi이며, Single 단순 단일 레포/기존 호환 시나리오를 위해 지원합니다. (`fullstack`는 `multi` 하위호환 alias)
118
136
  - 한국어/영어 템플릿 선택
119
137
 
120
138
  ### 🚀 Feature 생성
@@ -196,6 +214,21 @@ npx lee-spec-kit detect --dir /path/to/workspace
196
214
 
197
215
  `--json` 출력은 `isLeeSpecKitProject`, `reasonCode`(`PROJECT_DETECTED` | `PROJECT_NOT_DETECTED`), `docsDir`, `configPath`, `detectionSource`(`config` | `heuristic`)를 포함합니다.
198
216
 
217
+ ### 온보딩 점검
218
+
219
+ 초기 설정(Constitution/PRD/Git remote 등) 준비 상태를 점검합니다.
220
+
221
+ ```bash
222
+ # 사람 읽기 쉬운 출력
223
+ npx lee-spec-kit onboard
224
+
225
+ # 에이전트/자동화용 JSON
226
+ npx lee-spec-kit onboard --json
227
+
228
+ # WARN/BLOCK이 있으면 종료 코드 1
229
+ npx lee-spec-kit onboard --strict
230
+ ```
231
+
199
232
  ### 새 기능 생성
200
233
 
201
234
  ```bash
@@ -233,6 +266,7 @@ npx lee-spec-kit context
233
266
  # 특정 Feature 상태 + 라벨 확인 (에이전트 권장)
234
267
  npx lee-spec-kit context F001
235
268
  npx lee-spec-kit context F001 --json
269
+ npx lee-spec-kit context F001 --json-compact
236
270
 
237
271
  # 승인 + 실행 (일반 케이스)
238
272
  npx lee-spec-kit context F001 --approve A --execute
@@ -251,6 +285,7 @@ npx lee-spec-kit context F001 --approve A --execute --ticket <TICKET> --execute-
251
285
  | 옵션 | 설명 |
252
286
  | --------------- | ----------------------------------------------- |
253
287
  | `--json` | 에이전트용 JSON 출력 |
288
+ | `--json-compact` | 에이전트용 압축 JSON 출력 (`--json` 포함, 중복 필드 최소화) |
254
289
  | `--component <id>` | multi에서 대상 컴포넌트 지정 (예: `app`, `api`, `worker`) |
255
290
  | `--all` | 자동 감지 실패 시 완료된 Feature까지 포함해서 표시 |
256
291
  | `--done` | 완료(workflow-done) Feature만 표시 |
@@ -265,7 +300,8 @@ npx lee-spec-kit context F001 --approve A --execute --ticket <TICKET> --execute-
265
300
  - 선택한 액션이 `requiresUserCheck=true`인 경우에만 `--execute`에서 `--ticket`이 필요합니다.
266
301
  - 발급 후 짧은 시간(기본 5분)만 유효하며, 한 번 사용하면 재사용할 수 없습니다.
267
302
 
268
- `context --json` 출력은 크게 `actions`(원자 액션), `actionOptions`(라벨 매핑), 상위 메타데이터로 구성됩니다.
303
+ 기본 권장 포맷은 `context --json-compact`이며, 같은 판단 정보를 중복 없이 축약해 전달합니다.
304
+ `context --json`은 디버깅/세부 필드 확인이 필요할 때만 사용하세요.
269
305
 
270
306
  **핵심 필드 (실사용 권장)**
271
307
 
@@ -526,8 +562,8 @@ npx lee-spec-kit update --force
526
562
  `approval`은 `context`가 출력하는 다음 값에만 영향을 줍니다:
527
563
 
528
564
  - 텍스트 출력의 `[확인 필요]` 표시
529
- - `context --json`의 `actions[].requiresUserCheck`
530
- - `checkPolicy.token` (`context --json`): 승인 토큰 형식 (`<LABEL>`)
565
+ - `context --json-compact`의 `actionOptions[].requiresUserCheck` (`--json`에서는 `actions[].requiresUserCheck`)
566
+ - `checkPolicy.token` (`context --json-compact`/`--json`): 승인 토큰 형식 (`<LABEL>`)
531
567
  - `checkPolicy.acceptedTokens`: 허용되는 승인 응답 템플릿 (예: `["<LABEL>", "<LABEL> OK", "<LABEL> ...", "... <LABEL> ..."]`)
532
568
  - `checkPolicy.tokenPattern`: 승인 응답 검증용 정규식
533
569
  - `checkPolicy.validLabels`: 현재 선택 가능한 라벨 목록 (`A`, `B`, `C`...)
@@ -584,7 +620,7 @@ npx lee-spec-kit update --force
584
620
  #### 모드
585
621
 
586
622
  - `builtin` (기본): 코드에 내장된 `requiresUserCheck`를 그대로 사용
587
- - `category` (권장): `actions[].category` 기준으로 확인 정책을 제어
623
+ - `category` (권장): `actionOptions[].category` 기준으로 확인 정책을 제어 (`--json`에서는 `actions[].category`)
588
624
  - `steps`: step 번호 기준(변경에 취약하므로 권장하지 않음)
589
625
 
590
626
  #### 설정 필드
@@ -612,7 +648,7 @@ npx lee-spec-kit update --force
612
648
  }
613
649
  ```
614
650
 
615
- > 사용 가능한 `category` 값은 `context --json`의 `actions[].category`로 확인하는 것을 권장합니다.
651
+ > 사용 가능한 `category` 값은 `context --json-compact`의 `actionOptions[].category`에서 우선 확인하고, 필요 시 `context --json`의 `actions[].category`를 참고하세요.
616
652
 
617
653
  ### pr (PR 결과물 정책)
618
654
 
package/dist/index.js CHANGED
@@ -139,6 +139,7 @@ var I18N = {
139
139
  "context.suggestionCommandHint": "\uB77C\uBCA8 \uCC38\uACE0 \uBA85\uB839: {command}",
140
140
  "context.suggestionFinalPrompt": "\uD604\uC7AC \uCD94\uCC9C \uB77C\uBCA8: {labels}. \uC751\uB2F5\uC740 \uB77C\uBCA8 \uD1A0\uD070 \uD3EC\uD568 \uD615\uC2DD\uC73C\uB85C \uD574\uC8FC\uC138\uC694. (\uC608: {example}, `A \uC9C4\uD589\uD574`)",
141
141
  "context.suggestion.createFeature": "\uC0C8 Feature\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4",
142
+ "context.suggestion.runOnboard": "\uCD08\uAE30 \uC124\uC815 \uC810\uAC80(onboard)\uC744 \uC2E4\uD589\uD569\uB2C8\uB2E4",
142
143
  "context.suggestion.showDone": "\uC644\uB8CC\uB41C Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
143
144
  "context.suggestion.showAll": "\uC804\uCCB4 Feature \uBAA9\uB85D\uC744 \uD655\uC778\uD569\uB2C8\uB2E4",
144
145
  "context.suggestion.selectFeature": "\uC9C4\uD589\uD560 Feature\uB97C \uC120\uD0DD\uD574 \uC0C1\uC138 \uCEE8\uD14D\uC2A4\uD2B8\uB97C \uC5FD\uB2C8\uB2E4",
@@ -202,6 +203,7 @@ var I18N = {
202
203
  "init.log.nextStepsTitle": "\uB2E4\uC74C \uB2E8\uACC4:",
203
204
  "init.log.nextSteps1": " 1. {docsDir}/prd/README.md \uC791\uC131",
204
205
  "init.log.nextSteps2": " 2. npx lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00",
206
+ "init.log.nextSteps3": " 3. npx lee-spec-kit onboard --strict \uB85C \uCD08\uAE30 \uC124\uC815 \uC810\uAC80",
205
207
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0, docs \uCEE4\uBC0B \uC911...",
206
208
  "init.log.gitInit": "\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911...",
207
209
  "init.warn.stagedChangesSkip": '\u26A0\uFE0F \uD604\uC7AC Git index\uC5D0 \uC774\uBBF8 stage\uB41C \uBCC0\uACBD\uC774 \uC788\uC2B5\uB2C8\uB2E4. (--dir "." \uC778 \uACBD\uC6B0 \uCEE4\uBC0B \uBC94\uC704\uB97C \uC548\uC804\uD558\uAC8C \uC81C\uD55C\uD560 \uC218 \uC5C6\uC5B4 \uC790\uB3D9 \uCEE4\uBC0B\uC744 \uAC74\uB108\uB701\uB2C8\uB2E4)',
@@ -589,6 +591,7 @@ var I18N = {
589
591
  "context.suggestionCommandHint": "Reference command: {command}",
590
592
  "context.suggestionFinalPrompt": "Recommended labels now: {labels}. Please reply with a format that includes a label token. (e.g. {example}, `A proceed`)",
591
593
  "context.suggestion.createFeature": "Create a new feature",
594
+ "context.suggestion.runOnboard": "Run onboarding checks",
592
595
  "context.suggestion.showDone": "Show completed features",
593
596
  "context.suggestion.showAll": "Show all features",
594
597
  "context.suggestion.selectFeature": "Select a feature and open detailed context",
@@ -652,6 +655,7 @@ var I18N = {
652
655
  "init.log.nextStepsTitle": "Next steps:",
653
656
  "init.log.nextSteps1": " 1. Write {docsDir}/prd/README.md",
654
657
  "init.log.nextSteps2": " 2. Add a feature with: npx lee-spec-kit feature <name>",
658
+ "init.log.nextSteps3": " 3. Run setup checks: npx lee-spec-kit onboard --strict",
655
659
  "init.log.gitRepoDetectedCommit": "\u{1F4E6} Git repo detected, committing docs...",
656
660
  "init.log.gitInit": "\u{1F4E6} Initializing Git...",
657
661
  "init.warn.stagedChangesSkip": '\u26A0\uFE0F There are already staged changes in the Git index. (With --dir ".", commit scope cannot be safely restricted, so auto-commit is skipped.)',
@@ -2152,6 +2156,7 @@ async function runInit(options) {
2152
2156
  chalk6.gray(tr(lang, "cli", "init.log.nextSteps1", { docsDir: targetDir }))
2153
2157
  );
2154
2158
  console.log(chalk6.gray(tr(lang, "cli", "init.log.nextSteps2")));
2159
+ console.log(chalk6.gray(tr(lang, "cli", "init.log.nextSteps3")));
2155
2160
  console.log();
2156
2161
  },
2157
2162
  { owner: "init" }
@@ -2159,7 +2164,8 @@ async function runInit(options) {
2159
2164
  }
2160
2165
  async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2161
2166
  try {
2162
- const runGit = (args, workdir) => {
2167
+ const gitWorkdir = docsRepo === "standalone" ? targetDir : cwd;
2168
+ const runGit2 = (args, workdir) => {
2163
2169
  execFileSync("git", args, { cwd: workdir, stdio: "ignore" });
2164
2170
  };
2165
2171
  const getCachedStagedFiles = (workdir) => {
@@ -2205,14 +2211,14 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2205
2211
  }
2206
2212
  };
2207
2213
  try {
2208
- runGit(["rev-parse", "--is-inside-work-tree"], cwd);
2214
+ runGit2(["rev-parse", "--is-inside-work-tree"], gitWorkdir);
2209
2215
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitRepoDetectedCommit")));
2210
2216
  } catch {
2211
2217
  console.log(chalk6.blue(tr(lang, "cli", "init.log.gitInit")));
2212
- runGit(["init"], cwd);
2218
+ runGit2(["init"], gitWorkdir);
2213
2219
  }
2214
- const relativePath = path19.relative(cwd, targetDir);
2215
- const stagedBeforeAdd = getCachedStagedFiles(cwd);
2220
+ const relativePath = docsRepo === "standalone" ? "." : path19.relative(cwd, targetDir);
2221
+ const stagedBeforeAdd = getCachedStagedFiles(gitWorkdir);
2216
2222
  if (relativePath === "." && stagedBeforeAdd && stagedBeforeAdd.length > 0) {
2217
2223
  console.log(
2218
2224
  chalk6.yellow(
@@ -2223,8 +2229,8 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2223
2229
  console.log();
2224
2230
  return;
2225
2231
  }
2226
- if (relativePath !== "." && isPathIgnored(cwd, relativePath)) {
2227
- const repoRelativePath = toRepoRelativePath(cwd, relativePath);
2232
+ if (relativePath !== "." && isPathIgnored(gitWorkdir, relativePath)) {
2233
+ const repoRelativePath = toRepoRelativePath(gitWorkdir, relativePath);
2228
2234
  console.log(
2229
2235
  chalk6.yellow(
2230
2236
  tr(lang, "cli", "init.warn.docsPathIgnoredSkipCommit", {
@@ -2242,14 +2248,14 @@ async function initGit(cwd, targetDir, docsRepo, lang, pushDocs, docsRemote) {
2242
2248
  console.log();
2243
2249
  return;
2244
2250
  }
2245
- runGit(["add", relativePath], cwd);
2246
- runGit(
2251
+ runGit2(["add", relativePath], gitWorkdir);
2252
+ runGit2(
2247
2253
  ["commit", "-m", "init: docs \uAD6C\uC870 \uCD08\uAE30\uD654 (lee-spec-kit)", "--", relativePath],
2248
- cwd
2254
+ gitWorkdir
2249
2255
  );
2250
2256
  if (docsRepo === "standalone" && pushDocs && docsRemote) {
2251
2257
  try {
2252
- runGit(["remote", "add", "origin", docsRemote], cwd);
2258
+ runGit2(["remote", "add", "origin", docsRemote], gitWorkdir);
2253
2259
  console.log(
2254
2260
  chalk6.green(tr(lang, "cli", "init.log.gitRemoteSet", { remote: docsRemote }))
2255
2261
  );
@@ -6376,9 +6382,14 @@ function buildSuggestionOptions(lang, state, projectType, selectedComponent) {
6376
6382
  const showDoneCommand = `npx lee-spec-kit context --done${componentArg}`;
6377
6383
  const showAllCommand = `npx lee-spec-kit context --all${componentArg}`;
6378
6384
  const showOpenCommand = `npx lee-spec-kit context${componentArg}`;
6385
+ const runOnboardCommand = "npx lee-spec-kit onboard --strict";
6379
6386
  const rawSuggestions = [];
6380
6387
  switch (state.status) {
6381
6388
  case "no_features":
6389
+ rawSuggestions.push({
6390
+ summary: tr(lang, "cli", "context.suggestion.runOnboard"),
6391
+ command: runOnboardCommand
6392
+ });
6382
6393
  rawSuggestions.push({
6383
6394
  summary: tr(lang, "cli", "context.suggestion.createFeature"),
6384
6395
  command: createFeatureCommand
@@ -6487,7 +6498,10 @@ function getCommandExecutionLockPath(action, config) {
6487
6498
  return getProjectExecutionLockPath(action.cwd);
6488
6499
  }
6489
6500
  function contextCommand(program2) {
6490
- program2.command("context [feature-name]").description("Show current feature context and next actions").option("--json", "Output in JSON format for agents").option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option(
6501
+ program2.command("context [feature-name]").description("Show current feature context and next actions").option("--json", "Output in JSON format for agents").option(
6502
+ "--json-compact",
6503
+ "Output compact JSON for agents (implies --json, reduced duplication)"
6504
+ ).option("--component <component>", "Component name for multi projects").option("--all", "Include completed features when auto-detecting").option("--done", "Show completed (workflow-done) features only").option(
6491
6505
  "--approve <reply>",
6492
6506
  "Approve one labeled option (examples: A, A OK, A proceed, A \uC9C4\uD589\uD574)"
6493
6507
  ).option(
@@ -6508,7 +6522,7 @@ function contextCommand(program2) {
6508
6522
  const lang = config?.lang ?? DEFAULT_LANG;
6509
6523
  const cliError = toCliError(error);
6510
6524
  const suggestions = getCliErrorSuggestions(cliError.code, lang);
6511
- if (options.json) {
6525
+ if (options.json || options.jsonCompact) {
6512
6526
  console.log(
6513
6527
  JSON.stringify({
6514
6528
  status: "error",
@@ -6600,6 +6614,111 @@ function getMultipleFeaturesRecommendation(projectType, selectedComponent) {
6600
6614
  }
6601
6615
  return "Multiple features detected across components. Please specify feature name (slug | F001 | F001-slug) or use --component.";
6602
6616
  }
6617
+ function getFeatureRef(feature) {
6618
+ return feature.folderName || `${feature.type}:${feature.slug}`;
6619
+ }
6620
+ function toCompactFeature(feature) {
6621
+ if (!feature) return null;
6622
+ return {
6623
+ ref: getFeatureRef(feature),
6624
+ id: feature.id ?? null,
6625
+ slug: feature.slug,
6626
+ folderName: feature.folderName,
6627
+ type: feature.type,
6628
+ path: feature.path,
6629
+ currentStep: feature.currentStep,
6630
+ nextAction: feature.nextAction,
6631
+ completion: feature.completion,
6632
+ specStatus: feature.specStatus,
6633
+ planStatus: feature.planStatus,
6634
+ tasks: feature.tasks,
6635
+ prePrReview: {
6636
+ status: feature.prePrReview.status,
6637
+ findings: feature.prePrReview.findings,
6638
+ evidenceProvided: feature.prePrReview.evidenceProvided
6639
+ },
6640
+ prReview: {
6641
+ findings: feature.prReview.findings,
6642
+ evidenceProvided: feature.prReview.evidenceProvided
6643
+ },
6644
+ pr: {
6645
+ link: feature.pr.link,
6646
+ status: feature.pr.status,
6647
+ remote: feature.pr.remote
6648
+ },
6649
+ git: {
6650
+ docsBranch: feature.git.docsBranch,
6651
+ projectBranch: feature.git.projectBranch,
6652
+ projectBranchAvailable: feature.git.projectBranchAvailable,
6653
+ onExpectedBranch: feature.git.onExpectedBranch,
6654
+ docsEverCommitted: feature.git.docsEverCommitted,
6655
+ docsHasUncommittedChanges: feature.git.docsHasUncommittedChanges,
6656
+ projectHasUncommittedChanges: feature.git.projectHasUncommittedChanges,
6657
+ docsPathIgnored: feature.git.docsPathIgnored
6658
+ },
6659
+ docs: {
6660
+ specExists: feature.docs.specExists,
6661
+ planExists: feature.docs.planExists,
6662
+ tasksExists: feature.docs.tasksExists,
6663
+ issueDocIssueFieldExists: feature.docs.issueDocIssueFieldExists,
6664
+ prDocPrFieldExists: feature.docs.prDocPrFieldExists,
6665
+ prDocReviewStatusFieldExists: feature.docs.prDocReviewStatusFieldExists,
6666
+ prFieldExists: feature.docs.prFieldExists,
6667
+ prStatusFieldExists: feature.docs.prStatusFieldExists,
6668
+ prePrReviewFieldExists: feature.docs.prePrReviewFieldExists,
6669
+ prePrFindingsFieldExists: feature.docs.prePrFindingsFieldExists,
6670
+ prePrEvidenceFieldExists: feature.docs.prePrEvidenceFieldExists,
6671
+ prReviewFindingsFieldExists: feature.docs.prReviewFindingsFieldExists,
6672
+ prReviewEvidenceFieldExists: feature.docs.prReviewEvidenceFieldExists
6673
+ },
6674
+ warnings: feature.warnings
6675
+ };
6676
+ }
6677
+ function toCompactActionOption(option) {
6678
+ const base = {
6679
+ label: option.label,
6680
+ summary: option.summary,
6681
+ detail: option.detail,
6682
+ approvalPrompt: option.approvalPrompt,
6683
+ actionType: option.action.type,
6684
+ category: option.action.category,
6685
+ operationType: option.action.operationType,
6686
+ requiresUserCheck: !!option.action.requiresUserCheck
6687
+ };
6688
+ if (option.action.type === "command") {
6689
+ base.scope = option.action.scope;
6690
+ base.cwd = option.action.cwd;
6691
+ base.cmd = option.action.cmd;
6692
+ return base;
6693
+ }
6694
+ base.message = option.action.message;
6695
+ return base;
6696
+ }
6697
+ function toCompactSuggestionOption(option) {
6698
+ return {
6699
+ label: option.label,
6700
+ summary: option.summary,
6701
+ command: option.command
6702
+ };
6703
+ }
6704
+ function resolveContextRecommendation(state, projectType, selectedComponent) {
6705
+ if (state.status === "multiple_active") {
6706
+ return getMultipleFeaturesRecommendation(projectType, selectedComponent);
6707
+ }
6708
+ if (state.status === "no_features") {
6709
+ return "No features found. Run onboarding checks first, then create a feature.";
6710
+ }
6711
+ if (state.status === "no_open") {
6712
+ return "No open features found. Use `context --done` to inspect completed features.";
6713
+ }
6714
+ if (state.status === "no_match") {
6715
+ return "No features found.";
6716
+ }
6717
+ if (state.targetFeatures.length === 1) {
6718
+ return state.targetFeatures[0].nextAction;
6719
+ }
6720
+ return "No matched feature.";
6721
+ }
6603
6722
  async function runContext(featureName, options) {
6604
6723
  const cwd = process.cwd();
6605
6724
  const config = await getConfig(cwd);
@@ -6659,7 +6778,8 @@ async function runContext(featureName, options) {
6659
6778
  );
6660
6779
  return;
6661
6780
  }
6662
- if (options.json) {
6781
+ const jsonMode = !!options.json || !!options.jsonCompact;
6782
+ if (jsonMode) {
6663
6783
  const primaryAction = state.actionOptions[0] ?? null;
6664
6784
  const finalApprovalPrompt = buildFinalApprovalPrompt(lang, state.actionOptions);
6665
6785
  const approveCommand = buildApprovalCommand(
@@ -6674,6 +6794,76 @@ async function runContext(featureName, options) {
6674
6794
  selectedComponent,
6675
6795
  true
6676
6796
  );
6797
+ const recommendation = resolveContextRecommendation(
6798
+ state,
6799
+ config.projectType,
6800
+ selectedComponent
6801
+ );
6802
+ if (options.jsonCompact) {
6803
+ const compactResult = {
6804
+ schema: "context.v2.compact",
6805
+ status: state.status,
6806
+ reasonCode: toReasonCode(state.status),
6807
+ selectionMode: state.selectionMode,
6808
+ selectionFallback: state.selectionFallback,
6809
+ branches: state.branches,
6810
+ warnings: state.warnings,
6811
+ contextVersion: state.contextVersion,
6812
+ matchedFeature: toCompactFeature(state.matchedFeature),
6813
+ candidateRefs: state.targetFeatures.length > 1 ? state.targetFeatures.map((feature) => getFeatureRef(feature)) : [],
6814
+ completedCandidateRefs: state.selectionMode === "open" ? state.doneFeatures.map((feature) => getFeatureRef(feature)) : [],
6815
+ openCandidateRefs: state.selectionMode === "open" ? state.openFeatures.map((feature) => getFeatureRef(feature)) : [],
6816
+ inProgressCandidateRefs: state.selectionMode === "open" ? state.inProgressFeatures.map((feature) => getFeatureRef(feature)) : [],
6817
+ readyToCloseCandidateRefs: state.selectionMode === "open" ? state.readyToCloseFeatures.map((feature) => getFeatureRef(feature)) : [],
6818
+ actionOptions: state.actionOptions.map((option) => toCompactActionOption(option)),
6819
+ suggestionOptions: suggestionOptions.map(
6820
+ (option) => toCompactSuggestionOption(option)
6821
+ ),
6822
+ primaryActionLabel: primaryAction?.label ?? null,
6823
+ workflowPolicy,
6824
+ taskCommitGatePolicy,
6825
+ prePrReviewPolicy,
6826
+ checkPolicy: {
6827
+ docPath: "builtin://agents/policy",
6828
+ token: "<LABEL>",
6829
+ acceptedTokens: ["<LABEL>", "<LABEL> OK", "<LABEL> ...", "... <LABEL> ..."],
6830
+ tokenPattern: "^.*\\b([A-Z]+)\\b.*$",
6831
+ validLabels: state.actionOptions.map((o) => o.label),
6832
+ oneApprovalPerAction: true,
6833
+ requireFreshContext: true,
6834
+ contextVersion: state.contextVersion,
6835
+ config: config.approval ?? { mode: "builtin" }
6836
+ },
6837
+ approvalRequest: {
6838
+ finalPrompt: finalApprovalPrompt,
6839
+ userFacingLines: [
6840
+ ...state.actionOptions.map((o) => o.approvalPrompt),
6841
+ finalApprovalPrompt
6842
+ ].filter((line) => line.length > 0),
6843
+ labels: state.actionOptions.map((o) => o.label),
6844
+ approveCommand,
6845
+ executeCommand,
6846
+ executeRequiresTicket: !!state.actionOptions[0]?.action?.requiresUserCheck
6847
+ },
6848
+ suggestionRequest: {
6849
+ finalPrompt: suggestionFinalPrompt,
6850
+ userFacingLines: [
6851
+ ...suggestionOptions.map((o) => `${o.label}: ${o.summary}`),
6852
+ suggestionFinalPrompt
6853
+ ].filter((line) => line.length > 0),
6854
+ labels: suggestionOptions.map((o) => o.label)
6855
+ },
6856
+ prPolicy: {
6857
+ screenshots: {
6858
+ upload: config.pr?.screenshots?.upload ?? false
6859
+ }
6860
+ },
6861
+ requiredDocs,
6862
+ recommendation
6863
+ };
6864
+ console.log(JSON.stringify(compactResult, null, 2));
6865
+ return;
6866
+ }
6677
6867
  const result = {
6678
6868
  status: state.status,
6679
6869
  reasonCode: toReasonCode(state.status),
@@ -6765,24 +6955,8 @@ async function runContext(featureName, options) {
6765
6955
  }
6766
6956
  },
6767
6957
  requiredDocs,
6768
- recommendation: ""
6958
+ recommendation
6769
6959
  };
6770
- if (result.status === "multiple_active") {
6771
- result.recommendation = getMultipleFeaturesRecommendation(
6772
- config.projectType,
6773
- selectedComponent
6774
- );
6775
- } else if (result.status === "no_features") {
6776
- result.recommendation = "No features found. Create a feature first.";
6777
- } else if (result.status === "no_open") {
6778
- result.recommendation = "No open features found. Use `context --done` to inspect completed features.";
6779
- } else if (result.status === "no_match") {
6780
- result.recommendation = "No features found.";
6781
- } else if (state.targetFeatures.length === 1) {
6782
- result.recommendation = state.targetFeatures[0].nextAction;
6783
- } else {
6784
- result.recommendation = "No matched feature.";
6785
- }
6786
6960
  console.log(JSON.stringify(result, null, 2));
6787
6961
  return;
6788
6962
  }
@@ -7055,6 +7229,7 @@ async function runContext(featureName, options) {
7055
7229
  async function runApprovedOption(state, config, lang, featureName, selectionOptions, options) {
7056
7230
  const approval = options.approve || "";
7057
7231
  const ticketToken = (options.ticket || "").trim();
7232
+ const jsonMode = !!options.json || !!options.jsonCompact;
7058
7233
  let parsedLabel = null;
7059
7234
  if (state.status !== "single_matched" || !state.matchedFeature) {
7060
7235
  throw createCliError(
@@ -7115,7 +7290,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
7115
7290
  label: parsedLabel,
7116
7291
  featureRef
7117
7292
  }) : null;
7118
- if (options.json) {
7293
+ if (jsonMode) {
7119
7294
  console.log(
7120
7295
  JSON.stringify(
7121
7296
  {
@@ -7194,7 +7369,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
7194
7369
  `Approved label "${parsedLabel}" is instruction-only. Re-run without \`--execute\` or pick a command option.`
7195
7370
  );
7196
7371
  }
7197
- if (options.json) {
7372
+ if (jsonMode) {
7198
7373
  console.log(
7199
7374
  JSON.stringify(
7200
7375
  {
@@ -7219,7 +7394,7 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
7219
7394
  console.log();
7220
7395
  return;
7221
7396
  }
7222
- if (!options.json) {
7397
+ if (!jsonMode) {
7223
7398
  console.log();
7224
7399
  console.log(chalk6.blue(`\u25B6 Executing option ${parsedLabel}...`));
7225
7400
  console.log(chalk6.gray(` ${selectedAction.cmd}`));
@@ -7231,12 +7406,12 @@ async function runApprovedOption(state, config, lang, featureName, selectionOpti
7231
7406
  lockPath,
7232
7407
  async () => executeCommandAction(
7233
7408
  selectedAction.cmd,
7234
- !!options.json,
7409
+ jsonMode,
7235
7410
  selectedAction.cwd
7236
7411
  ),
7237
7412
  { owner: `context-execute:${selectedAction.scope}` }
7238
7413
  );
7239
- if (options.json) {
7414
+ if (jsonMode) {
7240
7415
  console.log(
7241
7416
  JSON.stringify(
7242
7417
  {
@@ -10054,6 +10229,459 @@ async function runDetect(options) {
10054
10229
  }
10055
10230
  console.log();
10056
10231
  }
10232
+ function t(lang, ko, en) {
10233
+ return lang === "ko" ? ko : en;
10234
+ }
10235
+ function quotePath(value) {
10236
+ return `"${value.replace(/"/g, '\\"')}"`;
10237
+ }
10238
+ function toSlug(value) {
10239
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
10240
+ }
10241
+ function runGit(args, cwd) {
10242
+ try {
10243
+ return execFileSync("git", args, {
10244
+ cwd,
10245
+ encoding: "utf-8",
10246
+ stdio: ["ignore", "pipe", "pipe"]
10247
+ }).trim();
10248
+ } catch {
10249
+ return void 0;
10250
+ }
10251
+ }
10252
+ function isGitRepo(cwd) {
10253
+ return runGit(["rev-parse", "--is-inside-work-tree"], cwd) === "true";
10254
+ }
10255
+ function hasHeadCommit(cwd) {
10256
+ return !!runGit(["rev-parse", "--verify", "HEAD"], cwd);
10257
+ }
10258
+ function getOriginUrl(cwd) {
10259
+ const out = runGit(["remote", "get-url", "origin"], cwd);
10260
+ return out || void 0;
10261
+ }
10262
+ function hasTemplateMarkers(content) {
10263
+ const patterns = [
10264
+ /\{\{projectName\}\}/,
10265
+ /\{\{date\}\}/,
10266
+ /\(Write your project mission here\)/,
10267
+ /\(Write project-specific architecture principles here/i,
10268
+ /\(Write project code quality standards here/i,
10269
+ /\(Write project security principles here/i,
10270
+ /\(Write your project-specific rules here\)/,
10271
+ /\(Override default rules or add additional rules here\)/,
10272
+ /\(Write project-specific workflows here\)/,
10273
+ /\(Write other rules here\)/,
10274
+ /\(프로젝트의 미션을 작성하세요\)/,
10275
+ /\(프로젝트별 아키텍처 원칙을 작성하세요/,
10276
+ /\(프로젝트의 코드 품질 기준을 작성하세요/,
10277
+ /\(프로젝트의 보안 원칙을 작성하세요/,
10278
+ /\(여기에 프로젝트만의 규칙을 작성하세요\)/,
10279
+ /\(기본 규칙을 오버라이드하거나 추가 규칙을 작성하세요\)/,
10280
+ /\(프로젝트만의 워크플로우가 있다면 작성하세요\)/,
10281
+ /\(기타 규칙을 작성하세요\)/
10282
+ ];
10283
+ return patterns.some((pattern) => pattern.test(content));
10284
+ }
10285
+ async function countFeatureDirs(docsDir, projectType) {
10286
+ const pattern = projectType === "single" ? "features/*/" : "features/*/*/";
10287
+ const dirs = await glob(pattern, {
10288
+ cwd: docsDir,
10289
+ absolute: false,
10290
+ ignore: ["**/feature-base/**"]
10291
+ });
10292
+ return dirs.map((value) => value.replace(/\\/g, "/").replace(/\/+$/, "")).filter((value) => {
10293
+ const base = path19.posix.basename(value);
10294
+ return !!base && base !== "feature-base";
10295
+ }).length;
10296
+ }
10297
+ async function hasUserPrdFile(prdDir) {
10298
+ if (!await fs15.pathExists(prdDir)) return false;
10299
+ const files = await glob("**/*.md", {
10300
+ cwd: prdDir,
10301
+ nodir: true,
10302
+ absolute: false,
10303
+ ignore: ["**/node_modules/**"]
10304
+ });
10305
+ return files.some((relativePath) => path19.basename(relativePath).toLowerCase() !== "readme.md");
10306
+ }
10307
+ function finalizeChecks(checks) {
10308
+ const summary = checks.reduce(
10309
+ (acc, check) => {
10310
+ acc[check.status] += 1;
10311
+ return acc;
10312
+ },
10313
+ { ok: 0, warn: 0, block: 0 }
10314
+ );
10315
+ const status = summary.block > 0 ? "blocked" : summary.warn > 0 ? "needs_action" : "ready";
10316
+ return { checks, summary, status };
10317
+ }
10318
+ function printOnboardResult(lang, result) {
10319
+ console.log();
10320
+ console.log(chalk6.bold(t(lang, "\u{1F9ED} Onboarding \uC810\uAC80", "\u{1F9ED} Onboarding Checks")));
10321
+ for (const check of result.checks) {
10322
+ const mark = check.status === "ok" ? chalk6.green("\u2705") : check.status === "warn" ? chalk6.yellow("\u26A0\uFE0F") : chalk6.red("\u274C");
10323
+ const level = check.status === "ok" ? chalk6.green("OK") : check.status === "warn" ? chalk6.yellow("WARN") : chalk6.red("BLOCK");
10324
+ console.log(`${mark} [${level}] ${check.title}`);
10325
+ console.log(` ${check.message}`);
10326
+ if (check.path) console.log(chalk6.gray(` path: ${check.path}`));
10327
+ if (check.suggestedCommand) {
10328
+ console.log(chalk6.gray(` ${t(lang, "\uB2E4\uC74C \uBA85\uB839", "next")}: ${check.suggestedCommand}`));
10329
+ }
10330
+ }
10331
+ console.log();
10332
+ console.log(
10333
+ chalk6.bold(
10334
+ t(
10335
+ lang,
10336
+ `\uC694\uC57D: OK ${result.summary.ok}, WARN ${result.summary.warn}, BLOCK ${result.summary.block}`,
10337
+ `Summary: OK ${result.summary.ok}, WARN ${result.summary.warn}, BLOCK ${result.summary.block}`
10338
+ )
10339
+ )
10340
+ );
10341
+ if (result.status === "ready") {
10342
+ console.log(chalk6.green(t(lang, "\uC628\uBCF4\uB529 \uC900\uBE44\uAC00 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Onboarding checks passed.")));
10343
+ } else if (result.status === "needs_action") {
10344
+ console.log(chalk6.yellow(t(lang, "\uCD94\uAC00 \uC815\uB9AC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.", "Some onboarding actions are required.")));
10345
+ } else {
10346
+ console.log(chalk6.red(t(lang, "\uC628\uBCF4\uB529 \uC120\uD589 \uC791\uC5C5\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", "Onboarding is blocked by required setup.")));
10347
+ }
10348
+ console.log();
10349
+ }
10350
+ async function runOnboardChecks(config) {
10351
+ const lang = config.lang;
10352
+ const checks = [];
10353
+ const docsDir = config.docsDir;
10354
+ const docsGitReady = isGitRepo(docsDir);
10355
+ if (!docsGitReady) {
10356
+ checks.push({
10357
+ id: "docs_git_repo",
10358
+ status: "block",
10359
+ title: t(lang, "docs Git \uB808\uD3EC \uCD08\uAE30\uD654", "Docs git repository initialized"),
10360
+ message: t(
10361
+ lang,
10362
+ "docs \uACBD\uB85C\uAC00 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
10363
+ "Docs directory is not a git repository."
10364
+ ),
10365
+ path: docsDir,
10366
+ suggestedCommand: `git -C ${quotePath(docsDir)} init`
10367
+ });
10368
+ } else {
10369
+ checks.push({
10370
+ id: "docs_git_repo",
10371
+ status: "ok",
10372
+ title: t(lang, "docs Git \uB808\uD3EC \uCD08\uAE30\uD654", "Docs git repository initialized"),
10373
+ message: t(lang, "docs Git \uB808\uD3EC\uAC00 \uD655\uC778\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Docs git repository is available."),
10374
+ path: docsDir
10375
+ });
10376
+ if (!hasHeadCommit(docsDir)) {
10377
+ checks.push({
10378
+ id: "docs_initial_commit",
10379
+ status: "warn",
10380
+ title: t(lang, "docs \uCD08\uAE30 \uCEE4\uBC0B", "Docs initial commit"),
10381
+ message: t(
10382
+ lang,
10383
+ "docs \uCCAB \uCEE4\uBC0B\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uCD08\uAE30 \uC124\uC815 \uCEE4\uBC0B\uC744 \uBA3C\uC800 \uC0DD\uC131\uD558\uC138\uC694.",
10384
+ "No initial commit found in docs repo. Create an initial setup commit first."
10385
+ ),
10386
+ path: docsDir,
10387
+ suggestedCommand: `git -C ${quotePath(docsDir)} add . && git -C ${quotePath(docsDir)} commit -m "docs: onboard setup"`
10388
+ });
10389
+ } else {
10390
+ checks.push({
10391
+ id: "docs_initial_commit",
10392
+ status: "ok",
10393
+ title: t(lang, "docs \uCD08\uAE30 \uCEE4\uBC0B", "Docs initial commit"),
10394
+ message: t(lang, "docs \uCD08\uAE30 \uCEE4\uBC0B\uC774 \uC874\uC7AC\uD569\uB2C8\uB2E4.", "Initial commit exists in docs repo."),
10395
+ path: docsDir
10396
+ });
10397
+ }
10398
+ const docsDirty = runGit(["status", "--porcelain=v1"], docsDir);
10399
+ if (docsDirty === void 0) {
10400
+ checks.push({
10401
+ id: "docs_worktree",
10402
+ status: "warn",
10403
+ title: t(lang, "docs \uC791\uC5C5 \uD2B8\uB9AC \uC0C1\uD0DC", "Docs worktree status"),
10404
+ message: t(
10405
+ lang,
10406
+ "docs \uBCC0\uACBD \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
10407
+ "Unable to read docs worktree status."
10408
+ ),
10409
+ path: docsDir
10410
+ });
10411
+ } else if (docsDirty.trim().length > 0) {
10412
+ checks.push({
10413
+ id: "docs_worktree",
10414
+ status: "warn",
10415
+ title: t(lang, "docs \uC791\uC5C5 \uD2B8\uB9AC \uC0C1\uD0DC", "Docs worktree status"),
10416
+ message: t(
10417
+ lang,
10418
+ "\uCEE4\uBC0B\uB418\uC9C0 \uC54A\uC740 docs \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC2B5\uB2C8\uB2E4.",
10419
+ "Uncommitted docs changes were found."
10420
+ ),
10421
+ path: docsDir,
10422
+ suggestedCommand: `git -C ${quotePath(docsDir)} add . && git -C ${quotePath(docsDir)} commit -m "docs: onboard updates"`
10423
+ });
10424
+ } else {
10425
+ checks.push({
10426
+ id: "docs_worktree",
10427
+ status: "ok",
10428
+ title: t(lang, "docs \uC791\uC5C5 \uD2B8\uB9AC \uC0C1\uD0DC", "Docs worktree status"),
10429
+ message: t(lang, "docs \uC791\uC5C5 \uD2B8\uB9AC\uAC00 \uAE68\uB057\uD569\uB2C8\uB2E4.", "Docs worktree is clean."),
10430
+ path: docsDir
10431
+ });
10432
+ }
10433
+ }
10434
+ const constitutionPath = path19.join(docsDir, "agents", "constitution.md");
10435
+ if (!await fs15.pathExists(constitutionPath)) {
10436
+ checks.push({
10437
+ id: "constitution_exists",
10438
+ status: "block",
10439
+ title: t(lang, "Constitution \uC791\uC131", "Constitution setup"),
10440
+ message: t(
10441
+ lang,
10442
+ "`agents/constitution.md` \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
10443
+ "`agents/constitution.md` is missing."
10444
+ ),
10445
+ path: constitutionPath,
10446
+ suggestedCommand: `npx lee-spec-kit update --agents`
10447
+ });
10448
+ } else {
10449
+ const content = await fs15.readFile(constitutionPath, "utf-8");
10450
+ if (hasTemplateMarkers(content)) {
10451
+ checks.push({
10452
+ id: "constitution_filled",
10453
+ status: "block",
10454
+ title: t(lang, "Constitution \uC791\uC131", "Constitution setup"),
10455
+ message: t(
10456
+ lang,
10457
+ "Constitution\uC5D0 \uD15C\uD50C\uB9BF placeholder\uAC00 \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4. \uD504\uB85C\uC81D\uD2B8 \uAE30\uC900\uC73C\uB85C \uBA3C\uC800 \uC791\uC131\uD558\uC138\uC694.",
10458
+ "Constitution still contains template placeholders. Fill project-specific content first."
10459
+ ),
10460
+ path: constitutionPath
10461
+ });
10462
+ } else {
10463
+ checks.push({
10464
+ id: "constitution_filled",
10465
+ status: "ok",
10466
+ title: t(lang, "Constitution \uC791\uC131", "Constitution setup"),
10467
+ message: t(lang, "Constitution\uC774 \uC791\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Constitution looks filled."),
10468
+ path: constitutionPath
10469
+ });
10470
+ }
10471
+ }
10472
+ const customPath = path19.join(docsDir, "agents", "custom.md");
10473
+ if (await fs15.pathExists(customPath)) {
10474
+ const content = await fs15.readFile(customPath, "utf-8");
10475
+ if (hasTemplateMarkers(content)) {
10476
+ checks.push({
10477
+ id: "custom_optional",
10478
+ status: "warn",
10479
+ title: t(lang, "Custom \uADDC\uCE59 \uBB38\uC11C", "Custom rules doc"),
10480
+ message: t(
10481
+ lang,
10482
+ "`agents/custom.md`\uB294 \uC120\uD0DD \uD56D\uBAA9\uC774\uC9C0\uB9CC, \uD604\uC7AC \uD15C\uD50C\uB9BF \uC0C1\uD0DC\uC785\uB2C8\uB2E4. \uD544\uC694\uD558\uBA74 \uADDC\uCE59\uC744 \uC791\uC131\uD558\uC138\uC694.",
10483
+ "`agents/custom.md` is optional, but it still looks like template content. Fill it if your project needs custom rules."
10484
+ ),
10485
+ path: customPath
10486
+ });
10487
+ } else {
10488
+ checks.push({
10489
+ id: "custom_optional",
10490
+ status: "ok",
10491
+ title: t(lang, "Custom \uADDC\uCE59 \uBB38\uC11C", "Custom rules doc"),
10492
+ message: t(lang, "Custom \uADDC\uCE59 \uBB38\uC11C\uAC00 \uAD6C\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "Custom rules doc looks configured."),
10493
+ path: customPath
10494
+ });
10495
+ }
10496
+ }
10497
+ const prdDir = path19.join(docsDir, "prd");
10498
+ const featureCount = await countFeatureDirs(docsDir, config.projectType);
10499
+ const prdReady = await hasUserPrdFile(prdDir);
10500
+ if (!prdReady) {
10501
+ checks.push({
10502
+ id: "prd_ready",
10503
+ status: featureCount === 0 ? "block" : "warn",
10504
+ title: t(lang, "PRD \uC900\uBE44 \uC0C1\uD0DC", "PRD readiness"),
10505
+ message: featureCount === 0 ? t(
10506
+ lang,
10507
+ "PRD \uBB38\uC11C\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. Feature \uC0DD\uC131 \uC804\uC5D0 PRD\uBD80\uD130 \uC791\uC131\uD558\uC138\uC694.",
10508
+ "PRD is empty. Write PRD first before creating features."
10509
+ ) : t(
10510
+ lang,
10511
+ "PRD \uBB38\uC11C\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uC774\uBBF8 Feature\uAC00 \uC788\uB2E4\uBA74 PRD\uB97C \uBCF4\uAC15\uD558\uC138\uC694.",
10512
+ "PRD is empty. If features already exist, fill PRD as soon as possible."
10513
+ ),
10514
+ path: prdDir,
10515
+ suggestedCommand: `touch ${quotePath(path19.join(prdDir, `${toSlug(config.projectName || "project")}-prd.md`))}`
10516
+ });
10517
+ } else {
10518
+ checks.push({
10519
+ id: "prd_ready",
10520
+ status: "ok",
10521
+ title: t(lang, "PRD \uC900\uBE44 \uC0C1\uD0DC", "PRD readiness"),
10522
+ message: t(lang, "PRD \uBB38\uC11C\uAC00 \uD655\uC778\uB418\uC5C8\uC2B5\uB2C8\uB2E4.", "PRD document is present."),
10523
+ path: prdDir
10524
+ });
10525
+ }
10526
+ const workflowPolicy = resolveWorkflowPolicy(config.workflow);
10527
+ if (workflowPolicy.mode === "github") {
10528
+ const projectKeys = config.projectType === "multi" ? resolveProjectComponents(config.projectType, config.components) : ["single"];
10529
+ for (const key of projectKeys) {
10530
+ const resolved = resolveProjectGitCwd(config, key, lang);
10531
+ const title = t(
10532
+ lang,
10533
+ config.projectType === "multi" ? `\uD504\uB85C\uC81D\uD2B8 Git \uC5F0\uACB0 (${key})` : "\uD504\uB85C\uC81D\uD2B8 Git \uC5F0\uACB0",
10534
+ config.projectType === "multi" ? `Project git connectivity (${key})` : "Project git connectivity"
10535
+ );
10536
+ if (resolved.warning || !resolved.cwd) {
10537
+ checks.push({
10538
+ id: `project_git_${key}`,
10539
+ status: "block",
10540
+ title,
10541
+ message: resolved.warning || t(
10542
+ lang,
10543
+ "\uD504\uB85C\uC81D\uD2B8 \uB808\uD3EC \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
10544
+ "Project repository path could not be resolved."
10545
+ )
10546
+ });
10547
+ continue;
10548
+ }
10549
+ if (!isGitRepo(resolved.cwd)) {
10550
+ checks.push({
10551
+ id: `project_git_${key}`,
10552
+ status: "block",
10553
+ title,
10554
+ message: t(
10555
+ lang,
10556
+ "\uD504\uB85C\uC81D\uD2B8 \uACBD\uB85C\uAC00 Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
10557
+ "Project path is not a git repository."
10558
+ ),
10559
+ path: resolved.cwd,
10560
+ suggestedCommand: `git -C ${quotePath(resolved.cwd)} init`
10561
+ });
10562
+ continue;
10563
+ }
10564
+ const origin = getOriginUrl(resolved.cwd);
10565
+ if (!origin) {
10566
+ checks.push({
10567
+ id: `project_origin_${key}`,
10568
+ status: "block",
10569
+ title: t(
10570
+ lang,
10571
+ config.projectType === "multi" ? `\uD504\uB85C\uC81D\uD2B8 origin \uC124\uC815 (${key})` : "\uD504\uB85C\uC81D\uD2B8 origin \uC124\uC815",
10572
+ config.projectType === "multi" ? `Project origin configured (${key})` : "Project origin configured"
10573
+ ),
10574
+ message: t(
10575
+ lang,
10576
+ "GitHub \uC6CC\uD06C\uD50C\uB85C\uC6B0\uB97C \uC704\uD574 \uD504\uB85C\uC81D\uD2B8 \uB808\uD3EC\uC758 origin remote\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
10577
+ "Project repo origin remote is required for github workflow."
10578
+ ),
10579
+ path: resolved.cwd,
10580
+ suggestedCommand: `git -C ${quotePath(resolved.cwd)} remote add origin <git-url>`
10581
+ });
10582
+ } else {
10583
+ checks.push({
10584
+ id: `project_origin_${key}`,
10585
+ status: "ok",
10586
+ title: t(
10587
+ lang,
10588
+ config.projectType === "multi" ? `\uD504\uB85C\uC81D\uD2B8 origin \uC124\uC815 (${key})` : "\uD504\uB85C\uC81D\uD2B8 origin \uC124\uC815",
10589
+ config.projectType === "multi" ? `Project origin configured (${key})` : "Project origin configured"
10590
+ ),
10591
+ message: t(
10592
+ lang,
10593
+ `origin\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: ${origin}`,
10594
+ `origin is configured: ${origin}`
10595
+ ),
10596
+ path: resolved.cwd
10597
+ });
10598
+ }
10599
+ }
10600
+ }
10601
+ if (config.docsRepo === "standalone" && config.pushDocs) {
10602
+ const origin = getOriginUrl(docsDir);
10603
+ if (!origin) {
10604
+ checks.push({
10605
+ id: "docs_origin",
10606
+ status: "block",
10607
+ title: t(lang, "docs origin \uC124\uC815", "Docs origin configured"),
10608
+ message: t(
10609
+ lang,
10610
+ "standalone + pushDocs=true \uC124\uC815\uC5D0\uC11C\uB294 docs origin remote\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
10611
+ "docs origin remote is required when standalone + pushDocs=true."
10612
+ ),
10613
+ path: docsDir,
10614
+ suggestedCommand: `git -C ${quotePath(docsDir)} remote add origin <docs-git-url>`
10615
+ });
10616
+ } else {
10617
+ checks.push({
10618
+ id: "docs_origin",
10619
+ status: "ok",
10620
+ title: t(lang, "docs origin \uC124\uC815", "Docs origin configured"),
10621
+ message: t(lang, `origin\uC774 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: ${origin}`, `origin is configured: ${origin}`),
10622
+ path: docsDir
10623
+ });
10624
+ }
10625
+ }
10626
+ return finalizeChecks(checks);
10627
+ }
10628
+ function onboardCommand(program2) {
10629
+ program2.command("onboard").description("Run onboarding checks for initial setup").option("--json", "Output in JSON format for agents").option("--strict", "Exit with code 1 when WARN/BLOCK exists").action(async (options) => {
10630
+ try {
10631
+ await runOnboard(options);
10632
+ } catch (error) {
10633
+ const config = await getConfig(process.cwd());
10634
+ const lang = config?.lang ?? DEFAULT_LANG;
10635
+ const cliError = toCliError(error);
10636
+ const suggestions = getCliErrorSuggestions(cliError.code, lang);
10637
+ if (options.json) {
10638
+ console.log(
10639
+ JSON.stringify({
10640
+ status: "error",
10641
+ reasonCode: cliError.code,
10642
+ error: cliError.message,
10643
+ suggestions
10644
+ })
10645
+ );
10646
+ } else {
10647
+ console.error(
10648
+ chalk6.red(tr(lang, "cli", "common.errorLabel")),
10649
+ chalk6.red(`[${cliError.code}] ${cliError.message}`)
10650
+ );
10651
+ printCliErrorSuggestions(suggestions, lang);
10652
+ }
10653
+ process.exit(1);
10654
+ }
10655
+ });
10656
+ }
10657
+ async function runOnboard(options) {
10658
+ const config = await getConfig(process.cwd());
10659
+ if (!config) {
10660
+ throw createCliError(
10661
+ "CONFIG_NOT_FOUND",
10662
+ tr(DEFAULT_LANG, "cli", "common.configNotFound")
10663
+ );
10664
+ }
10665
+ const lang = config.lang;
10666
+ const result = await runOnboardChecks(config);
10667
+ if (options.json) {
10668
+ const payload = {
10669
+ status: "ok",
10670
+ reasonCode: result.status === "ready" ? "ONBOARD_READY" : result.status === "needs_action" ? "ONBOARD_NEEDS_ACTION" : "ONBOARD_BLOCKED",
10671
+ docsDir: config.docsDir,
10672
+ docsRepo: config.docsRepo || "embedded",
10673
+ workflow: resolveWorkflowPolicy(config.workflow),
10674
+ summary: result.summary,
10675
+ checks: result.checks
10676
+ };
10677
+ console.log(JSON.stringify(payload, null, 2));
10678
+ } else {
10679
+ printOnboardResult(lang, result);
10680
+ }
10681
+ if (options.strict && (result.summary.warn > 0 || result.summary.block > 0)) {
10682
+ process.exitCode = 1;
10683
+ }
10684
+ }
10057
10685
  function isBannerDisabled() {
10058
10686
  const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
10059
10687
  return v === "1";
@@ -10233,4 +10861,5 @@ flowCommand(program);
10233
10861
  githubCommand(program);
10234
10862
  docsCommand(program);
10235
10863
  detectCommand(program);
10864
+ onboardCommand(program);
10236
10865
  await program.parseAsync();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.6.16",
3
+ "version": "0.6.17",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,11 +5,14 @@ This documentation is organized by feature to help agents quickly understand the
5
5
  ## Agent Session Start Checklist
6
6
 
7
7
  ```bash
8
+ # (recommended once at start) run onboarding checks
9
+ npx lee-spec-kit onboard --strict
10
+
8
11
  # 1) Detect project
9
12
  npx lee-spec-kit detect --json
10
13
 
11
14
  # 2) If detected, read context first
12
- npx lee-spec-kit context --json
15
+ npx lee-spec-kit context --json-compact
13
16
  ```
14
17
 
15
18
  - Apply lee-spec-kit workflow only when `isLeeSpecKitProject: true`.
@@ -20,7 +23,8 @@ npx lee-spec-kit context --json
20
23
 
21
24
  - Scaffold the code project first (for example Next.js/NestJS), then run `lee-spec-kit init`.
22
25
  - After that, verify detection with `detect --json`, then continue with `feature` and `context`.
23
- - This sequence assumes default `docsRepo: embedded`. For `standalone`, run against the docs repo path (`--dir` or `LEE_SPEC_KIT_DOCS_DIR`).
26
+ - In most cases (default: embedded), the steps above are all you need.
27
+ - Choose standalone only when docs are managed separately from the code repo. In that case, prefer running init from a parent workspace folder (for example `workspace/docs`, `workspace/project`) and set both docs/project paths together. (e.g. `npx lee-spec-kit init --docs-repo standalone --dir ./docs --project-root ./project`)
24
28
 
25
29
  ## Directory Structure
26
30
 
@@ -33,7 +33,7 @@ Prohibited:
33
33
  ## 🧾 Label Response Contract (SSOT)
34
34
 
35
35
  - End **every user-facing reply** with current status + available labels.
36
- - Use the latest `npx lee-spec-kit context --json` (or `flow --json`) as source.
36
+ - Use the latest `npx lee-spec-kit context --json-compact` as the default source (fallback: `context --json` or `flow --json` when full detail is required).
37
37
  - Use `actionOptions[].detail` or command `cmd` **verbatim**. Do not paraphrase.
38
38
  - Even when the user asks something else, append the same label block at the end if executable labels exist.
39
39
  - If no executable labels exist, print `Available labels: none` and guide re-check with `npx lee-spec-kit context`.
@@ -22,12 +22,8 @@
22
22
  - [ ] (verifiable criterion 1)
23
23
  - [ ] (verifiable criterion 2)
24
24
 
25
- ## Out of Scope
26
-
27
- - (what this issue does not cover)
28
-
29
25
  ## Related Docs
30
26
 
31
- - Spec: `./spec.md`
32
- - Plan: `./plan.md`
33
- - Tasks: `./tasks.md`
27
+ - Spec: `docs/.../spec.md`
28
+ - Plan: `docs/.../plan.md`
29
+ - Tasks: `docs/.../tasks.md`
@@ -27,6 +27,6 @@
27
27
 
28
28
  ## Related Docs
29
29
 
30
- - Spec: `./spec.md`
31
- - Tasks: `./tasks.md`
32
- - Decisions: `./decisions.md`
30
+ - Spec: `docs/.../spec.md`
31
+ - Tasks: `docs/.../tasks.md`
32
+ - Decisions: `docs/.../decisions.md`
@@ -5,11 +5,14 @@
5
5
  ## 에이전트 세션 시작 체크리스트
6
6
 
7
7
  ```bash
8
+ # (최초 1회 권장) 초기 온보딩 점검
9
+ npx lee-spec-kit onboard --strict
10
+
8
11
  # 1) 프로젝트 감지
9
12
  npx lee-spec-kit detect --json
10
13
 
11
14
  # 2) 감지 성공 시 컨텍스트 조회
12
- npx lee-spec-kit context --json
15
+ npx lee-spec-kit context --json-compact
13
16
  ```
14
17
 
15
18
  - `isLeeSpecKitProject: true`일 때만 lee-spec-kit 워크플로우를 적용합니다.
@@ -20,7 +23,8 @@ npx lee-spec-kit context --json
20
23
 
21
24
  - 코드 프로젝트 스캐폴딩(예: Next.js/NestJS) 후 `lee-spec-kit init`을 실행하세요.
22
25
  - 그 다음 `detect --json`으로 감지 결과를 확인하고, `feature`/`context` 순서로 진행하세요.
23
- - 순서는 기본값인 `docsRepo: embedded` 기준입니다. `standalone`이면 docs 레포 경로 기준(`--dir` 또는 `LEE_SPEC_KIT_DOCS_DIR`)으로 실행하세요.
26
+ - 대부분의 경우(기본값: embedded) 순서만 따르면 됩니다.
27
+ - docs를 코드 저장소와 분리해 운영할 때만 standalone을 선택하세요. 이때는 상위 워크스페이스 폴더(예: `workspace/docs`, `workspace/project`)에서 `init`을 실행해 docs/project 경로를 함께 지정하는 방식을 권장합니다. (예: `npx lee-spec-kit init --docs-repo standalone --dir ./docs --project-root ./project`)
24
28
 
25
29
  ## 상위 구조 요약
26
30
 
@@ -33,7 +33,7 @@
33
33
  ## 🧾 라벨 응답 계약 (SSOT)
34
34
 
35
35
  - 사용자에게 보내는 **모든 응답의 마지막**에 현재 상태 + 선택 가능한 라벨을 표시합니다.
36
- - 기준 데이터는 최신 `npx lee-spec-kit context --json`(또는 `flow --json`)입니다.
36
+ - 기준 데이터는 최신 `npx lee-spec-kit context --json-compact`를 기본으로 사용하고, 상세 필드가 필요할 때만 `context --json`(또는 `flow --json`)을 사용합니다.
37
37
  - 라벨 설명은 `actionOptions[].detail` 또는 command `cmd`를 **원문 그대로** 사용합니다. (요약/의역 금지)
38
38
  - 사용자가 다른 질문을 하더라도, 실행 가능한 라벨이 있으면 응답 마지막에 동일 블록을 다시 표시합니다.
39
39
  - 실행 가능한 라벨이 없으면 `선택 가능: 없음` + `npx lee-spec-kit context` 재확인을 안내합니다.
@@ -22,12 +22,8 @@
22
22
  - [ ] (검증 가능한 완료 기준 1)
23
23
  - [ ] (검증 가능한 완료 기준 2)
24
24
 
25
- ## 제외 범위
26
-
27
- - (이번 이슈에서 다루지 않는 내용)
28
-
29
25
  ## 관련 문서
30
26
 
31
- - Spec: `./spec.md`
32
- - Plan: `./plan.md`
33
- - Tasks: `./tasks.md`
27
+ - Spec: `docs/.../spec.md`
28
+ - Plan: `docs/.../plan.md`
29
+ - Tasks: `docs/.../tasks.md`
@@ -27,6 +27,6 @@
27
27
 
28
28
  ## 관련 문서
29
29
 
30
- - Spec: `./spec.md`
31
- - Tasks: `./tasks.md`
32
- - Decisions: `./decisions.md`
30
+ - Spec: `docs/.../spec.md`
31
+ - Tasks: `docs/.../tasks.md`
32
+ - Decisions: `docs/.../decisions.md`