lee-spec-kit 0.4.7 → 0.4.9

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 ADDED
@@ -0,0 +1,191 @@
1
+ <h1 align="center">
2
+ <strong>lee-spec-kit</strong>
3
+ </h1>
4
+
5
+ <div align="center">
6
+ <img src="./assets/logo.png" alt="lee-spec-kit logo" width="620" />
7
+ </div>
8
+
9
+ <p align="center">
10
+ <strong>CLI to generate a project docs structure for AI-assisted development</strong>
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://www.npmjs.com/package/lee-spec-kit"><img src="https://img.shields.io/npm/v/lee-spec-kit.svg" alt="npm version"></a>
15
+ <img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node.js">
16
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <a href="#quick-start">Quick Start</a> •
21
+ <a href="#features">Features</a> •
22
+ <a href="#usage">Usage</a> •
23
+ <a href="#generated-structure">Generated Structure</a>
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="./README.en.md">
28
+ <img src="https://img.shields.io/badge/lang-en-red.svg" alt="English">
29
+ </a>
30
+ <a href="./README.md">
31
+ <img src="https://img.shields.io/badge/lang-ko-blue.svg" alt="한국어">
32
+ </a>
33
+ </p>
34
+
35
+ ---
36
+
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ # 1) Initialize docs structure
41
+ npx lee-spec-kit init
42
+
43
+ # 2) Create a feature
44
+ npx lee-spec-kit feature user-auth
45
+
46
+ # 3) Show next steps (for agents)
47
+ npx lee-spec-kit context
48
+
49
+ # 4) Show overall status
50
+ npx lee-spec-kit status
51
+
52
+ # 5) Validate docs / feature metadata
53
+ npx lee-spec-kit doctor
54
+ ```
55
+
56
+ ## Features
57
+
58
+ ### 📁 Project initialization
59
+
60
+ - Interactive init or CLI options
61
+ - Supports `single` and `fullstack` (FE/BE split)
62
+ - Korean/English templates
63
+
64
+ ### 🚀 Feature creation
65
+
66
+ - Generates `spec.md`, `plan.md`, `tasks.md`, `decisions.md`
67
+ - Fullstack mode supports FE/BE separation
68
+ - Integrates Issue/PR templates (docs side)
69
+
70
+ ### 📊 Status management
71
+
72
+ - View feature progress at a glance
73
+ - Print to terminal or write a Markdown report
74
+
75
+ ### 🩺 Doctor
76
+
77
+ - Checks docs structure and feature metadata (missing status, duplicate IDs, placeholders, etc.)
78
+ - `--json` output for automation/agents
79
+
80
+ ### 🔄 Template updates
81
+
82
+ - Updates docs templates to the latest version
83
+
84
+ ## Usage
85
+
86
+ ### Init
87
+
88
+ ```bash
89
+ npx lee-spec-kit init
90
+ npx lee-spec-kit init --name my-project --type fullstack
91
+ ```
92
+
93
+ **Options:**
94
+
95
+ | Option | Description | Default |
96
+ | ------------------- | ----------------------------------------- | ------------- |
97
+ | `-n, --name <name>` | Project name | current folder |
98
+ | `-t, --type <type>` | `single` or `fullstack` | interactive |
99
+ | `-l, --lang <lang>` | `ko` or `en` | `en` |
100
+ | `-d, --dir <dir>` | Install directory | `./docs` |
101
+ | `-y, --yes` | Skip interactive prompts | - |
102
+
103
+ ### Create a feature
104
+
105
+ ```bash
106
+ # Single
107
+ npx lee-spec-kit feature user-auth
108
+
109
+ # Fullstack
110
+ npx lee-spec-kit feature --repo be user-auth
111
+ npx lee-spec-kit feature --repo fe user-profile
112
+ ```
113
+
114
+ ### Context (agent guide)
115
+
116
+ ```bash
117
+ # Auto-detect (based on git branch)
118
+ npx lee-spec-kit context
119
+
120
+ # Specify a feature
121
+ npx lee-spec-kit context user-auth
122
+
123
+ # Selector: Feature ID / folder name
124
+ npx lee-spec-kit context F001
125
+ npx lee-spec-kit context F001-user-auth
126
+
127
+ # JSON output (for agents)
128
+ npx lee-spec-kit context --json
129
+ ```
130
+
131
+ ### Status
132
+
133
+ ```bash
134
+ npx lee-spec-kit status
135
+ npx lee-spec-kit status --write
136
+ ```
137
+
138
+ ### Doctor
139
+
140
+ ```bash
141
+ npx lee-spec-kit doctor
142
+ npx lee-spec-kit doctor --strict
143
+ npx lee-spec-kit doctor --json
144
+ ```
145
+
146
+ ### Update templates
147
+
148
+ By default, `update` runs only when the `docs/` working tree is clean; in that case it overwrites changed files without prompting.
149
+ If you want to update while you have uncommitted changes, use `--force`.
150
+
151
+ ```bash
152
+ npx lee-spec-kit update
153
+ npx lee-spec-kit update --agents
154
+ npx lee-spec-kit update --skills
155
+ npx lee-spec-kit update --templates
156
+ npx lee-spec-kit update --force
157
+ ```
158
+
159
+ ## Configuration
160
+
161
+ ### `.lee-spec-kit.json`
162
+
163
+ Running `init` creates `.lee-spec-kit.json` in your docs root (default: `docs/`).
164
+
165
+ ```json
166
+ {
167
+ "projectName": "my-project",
168
+ "projectType": "single",
169
+ "lang": "en",
170
+ "createdAt": "YYYY-MM-DD",
171
+ "docsRepo": "embedded"
172
+ }
173
+ ```
174
+
175
+ | Field | Description |
176
+ | ------------- | ------------------------------------------------ |
177
+ | `projectName` | Project name |
178
+ | `projectType` | `single` or `fullstack` |
179
+ | `lang` | `ko` or `en` |
180
+ | `createdAt` | Creation date |
181
+ | `docsRepo` | `embedded` or `standalone` |
182
+ | `pushDocs` | (standalone only) whether to manage/push docs repo as a separate git repo |
183
+ | `docsRemote` | (standalone + pushDocs) docs repo remote URL |
184
+ | `projectRoot` | (standalone only) project repo path (single: string, fullstack: {fe, be}) |
185
+
186
+ > In standalone mode, `init` can add `pushDocs`, `docsRemote`, and `projectRoot` to this config.
187
+ > If you run the CLI outside the docs repo in standalone mode, set `LEE_SPEC_KIT_DOCS_DIR` to the docs repo path.
188
+
189
+ ## Generated Structure
190
+
191
+ See the Korean README for the full tree examples and workflow details: `README.md`.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
  <strong>lee-spec-kit</strong>
3
3
  </h1>
4
4
 
5
+ <div align="center">
6
+ <img src="./assets/logo.png" alt="lee-spec-kit logo" width="620" />
7
+ </div>
8
+
5
9
  <p align="center">
6
10
  <strong>AI 에이전트 기반 개발을 위한 프로젝트 문서 구조 생성 CLI</strong>
7
11
  </p>
@@ -173,6 +177,9 @@ npx lee-spec-kit doctor --json
173
177
 
174
178
  ### 템플릿 업데이트
175
179
 
180
+ 기본 동작은 `docs/` 작업트리에 변경사항이 없을 때만 업데이트를 진행하며, 이 경우 변경된 파일은 확인 없이 덮어씁니다.
181
+ 변경사항이 있는 상태에서 업데이트하려면 `--force`를 사용하세요.
182
+
176
183
  ```bash
177
184
  # 전체 업데이트
178
185
  npx lee-spec-kit update
@@ -186,7 +193,7 @@ npx lee-spec-kit update --skills
186
193
  # feature-base/ 폴더만 업데이트
187
194
  npx lee-spec-kit update --templates
188
195
 
189
- # 확인 없이 강제 덮어쓰기
196
+ # 변경사항이 있어도 강제 덮어쓰기
190
197
  npx lee-spec-kit update --force
191
198
  ```
192
199
 
@@ -213,12 +220,14 @@ npx lee-spec-kit update --force
213
220
  | `lang` | `ko` 또는 `en` |
214
221
  | `createdAt` | 생성 날짜 |
215
222
  | `docsRepo` | `embedded` 또는 `standalone` |
216
- | `projectRoot` | (standalone만) 프로젝트 레포지토리 경로 |
223
+ | `pushDocs` | (standalone만) docs 레포를 별도 Git으로 관리/푸시할지 여부 |
224
+ | `docsRemote` | (standalone+pushDocs) docs 레포 remote URL |
225
+ | `projectRoot` | (standalone만) 프로젝트 레포지토리 경로 (single: string, fullstack: {fe, be}) |
217
226
 
218
227
  > `docsRepo: "standalone"`을 선택하면 `pushDocs`, `docsRemote`, `projectRoot`가 추가됩니다.
219
228
 
220
229
  > 어디서 실행하든 설정을 찾을 수 있도록, CLI는 현재 디렉토리에서 상위로 올라가며 `.lee-spec-kit.json` 또는 `docs/.lee-spec-kit.json`을 탐색합니다.
221
- > standalone 환경에서 docs 레포 바깥(예: 프로젝트 레포)에서 실행해야 한다면 `LEE_SPEC_KIT_DOCS_DIR`(또는 `LSK_DOCS_DIR`)에 docs 레포 경로를 지정할 수 있습니다.
230
+ > standalone 환경에서 docs 레포 바깥(예: 프로젝트 레포)에서 실행해야 한다면 `LEE_SPEC_KIT_DOCS_DIR`에 docs 레포 경로를 지정할 수 있습니다.
222
231
 
223
232
  ### Standalone 프로젝트 설정 예시
224
233
 
@@ -228,7 +237,10 @@ npx lee-spec-kit update --force
228
237
  {
229
238
  "projectName": "my-project",
230
239
  "projectType": "single",
240
+ "lang": "ko",
241
+ "createdAt": "YYYY-MM-DD",
231
242
  "docsRepo": "standalone",
243
+ "pushDocs": false,
232
244
  "projectRoot": "/path/to/my-project"
233
245
  }
234
246
  ```
@@ -239,7 +251,10 @@ npx lee-spec-kit update --force
239
251
  {
240
252
  "projectName": "my-project",
241
253
  "projectType": "fullstack",
254
+ "lang": "ko",
255
+ "createdAt": "YYYY-MM-DD",
242
256
  "docsRepo": "standalone",
257
+ "pushDocs": false,
243
258
  "projectRoot": {
244
259
  "fe": "/path/to/frontend",
245
260
  "be": "/path/to/backend"
Binary file
package/dist/index.js CHANGED
@@ -18,21 +18,25 @@ async function copyTemplates(src, dest) {
18
18
  errorOnExist: false
19
19
  });
20
20
  }
21
+ function applyReplacements(content, replacements) {
22
+ const keys = Object.keys(replacements).sort((a, b) => b.length - a.length);
23
+ let next = content;
24
+ for (const key of keys) {
25
+ next = next.replaceAll(key, replacements[key]);
26
+ }
27
+ return next;
28
+ }
21
29
  async function replaceInFiles(dir, replacements) {
22
30
  const files = await glob("**/*.md", { cwd: dir, absolute: true });
23
31
  for (const file of files) {
24
32
  let content = await fs8.readFile(file, "utf-8");
25
- for (const [search, replace] of Object.entries(replacements)) {
26
- content = content.replaceAll(search, replace);
27
- }
33
+ content = applyReplacements(content, replacements);
28
34
  await fs8.writeFile(file, content, "utf-8");
29
35
  }
30
36
  const shFiles = await glob("**/*.sh", { cwd: dir, absolute: true });
31
37
  for (const file of shFiles) {
32
38
  let content = await fs8.readFile(file, "utf-8");
33
- for (const [search, replace] of Object.entries(replacements)) {
34
- content = content.replaceAll(search, replace);
35
- }
39
+ content = applyReplacements(content, replacements);
36
40
  await fs8.writeFile(file, content, "utf-8");
37
41
  }
38
42
  }
@@ -93,6 +97,8 @@ var I18N = {
93
97
  "update.updatedTotal": "\uCD1D {count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!",
94
98
  "update.changeDetected": "\uBCC0\uACBD \uAC10\uC9C0 (--force\uB85C \uB36E\uC5B4\uC4F0\uAE30)",
95
99
  "update.fileUpdated": "{file} \uC5C5\uB370\uC774\uD2B8",
100
+ "update.gitStatusUnavailable": "git \uC0C1\uD0DC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (git repo\uAC00 \uC544\uB2C8\uAC70\uB098 git \uC2E4\uD589 \uBD88\uAC00) --force\uB85C \uAC15\uC81C \uB36E\uC5B4\uC4F0\uAE30\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.",
101
+ "update.docsWorktreeDirty": "docs \uC791\uC5C5\uD2B8\uB9AC\uC5D0 \uBCC0\uACBD\uC0AC\uD56D\uC774 \uC788\uC5B4 update\uB97C \uC9C4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBCC0\uACBD\uC0AC\uD56D\uC744 \uCEE4\uBC0B/\uC2A4\uD0DC\uC2DC \uD6C4 \uB2E4\uC2DC \uC2E4\uD589\uD558\uAC70\uB098 --force\uB85C \uB36E\uC5B4\uC4F0\uC138\uC694.",
96
102
  "doctor.title": "\u{1F50E} \uBB38\uC11C \uC9C4\uB2E8",
97
103
  "doctor.envWarnings": "\u26A0\uFE0F \uD658\uACBD \uACBD\uACE0:",
98
104
  "doctor.noIssues": "\u2705 \uBB38\uC81C\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
@@ -266,6 +272,8 @@ var I18N = {
266
272
  "update.updatedTotal": "Updated {count} files!",
267
273
  "update.changeDetected": "changes detected (use --force to overwrite)",
268
274
  "update.fileUpdated": "{file} updated",
275
+ "update.gitStatusUnavailable": "Cannot determine git status (not a git repo or git unavailable). Use --force to overwrite.",
276
+ "update.docsWorktreeDirty": "Docs working tree has changes. Commit/stash your changes, or run with --force to overwrite.",
269
277
  "doctor.title": "\u{1F50E} Docs Doctor",
270
278
  "doctor.envWarnings": "\u26A0\uFE0F Environment warnings:",
271
279
  "doctor.noIssues": "\u2705 No issues found.",
@@ -901,7 +909,7 @@ function getAncestorDirs(startDir) {
901
909
  return dirs;
902
910
  }
903
911
  async function getConfig(cwd) {
904
- const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || process.env.LSK_DOCS_DIR || "").trim();
912
+ const explicitDocsDir = (process.env.LEE_SPEC_KIT_DOCS_DIR || "").trim();
905
913
  const baseDirs = [
906
914
  ...explicitDocsDir ? [path4.resolve(explicitDocsDir)] : [],
907
915
  ...getAncestorDirs(cwd)
@@ -983,6 +991,7 @@ async function runFeature(name, options) {
983
991
  process.exit(1);
984
992
  }
985
993
  const { docsDir, projectType, lang } = config;
994
+ const projectName = config.projectName;
986
995
  assertValid(validateSafeName(name), "\uAE30\uB2A5 \uC774\uB984");
987
996
  let repo = options.repo;
988
997
  if (projectType === "fullstack" && !repo) {
@@ -1043,6 +1052,7 @@ async function runFeature(name, options) {
1043
1052
  const idNumber = featureId.replace("F", "");
1044
1053
  const repoName = projectType === "fullstack" && repo ? `{{projectName}}-${repo}` : "{{projectName}}";
1045
1054
  const replacements = {
1055
+ "{{projectName}}": projectName ?? "{{projectName}}",
1046
1056
  // ko placeholders
1047
1057
  "{\uAE30\uB2A5\uBA85}": name,
1048
1058
  "{\uBC88\uD638}": idNumber,
@@ -2065,18 +2075,28 @@ async function getFeatureNameFromSpec(featureDir, fallbackSlug, fallbackFolderNa
2065
2075
  return fallbackSlug || fallbackFolderName;
2066
2076
  }
2067
2077
  function updateCommand(program2) {
2068
- program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "Update agents/skills folder only").option("--templates", "Update feature-base/ folder only").option("-f, --force", "Force overwrite without confirmation").action(async (options) => {
2078
+ program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--skills", "Update agents/skills folder only").option("--templates", "Update feature-base/ folder only").option(
2079
+ "-f, --force",
2080
+ "Force overwrite even if docs has uncommitted changes"
2081
+ ).action(async (options) => {
2069
2082
  try {
2070
2083
  await runUpdate(options);
2071
2084
  } catch (error) {
2085
+ const config = await getConfig(process.cwd());
2086
+ const lang = config?.lang ?? DEFAULT_LANG;
2072
2087
  if (error instanceof Error && error.message === "canceled") {
2073
- const config = await getConfig(process.cwd());
2074
- const lang = config?.lang ?? DEFAULT_LANG;
2075
2088
  console.log(chalk6.yellow(`
2076
2089
  ${tr(lang, "cli", "common.canceled")}`));
2077
2090
  process.exit(0);
2078
2091
  }
2079
- console.error(chalk6.red(tr(DEFAULT_LANG, "cli", "common.errorLabel")), error);
2092
+ if (error instanceof Error) {
2093
+ console.error(
2094
+ chalk6.red(tr(lang, "cli", "common.errorLabel")),
2095
+ chalk6.red(error.message)
2096
+ );
2097
+ } else {
2098
+ console.error(chalk6.red(tr(lang, "cli", "common.errorLabel")), error);
2099
+ }
2080
2100
  process.exit(1);
2081
2101
  }
2082
2102
  });
@@ -2096,6 +2116,7 @@ async function runUpdate(options) {
2096
2116
  const { docsDir, projectType, lang } = config;
2097
2117
  const templatesDir = getTemplatesDir();
2098
2118
  const sourceDir = path4.join(templatesDir, lang, projectType);
2119
+ const forceOverwrite = !!options.force || await isDocsWorktreeCleanOrThrow(docsDir, lang);
2099
2120
  const hasExplicitSelection = !!(options.agents || options.skills || options.templates);
2100
2121
  const updateAgents = options.agents || options.skills || !hasExplicitSelection;
2101
2122
  const updateTemplates = options.templates || !hasExplicitSelection;
@@ -2120,15 +2141,20 @@ async function runUpdate(options) {
2120
2141
  const typeAgents = agentsMode === "skills" ? path4.join(typeAgentsBase, "skills") : typeAgentsBase;
2121
2142
  const targetAgents = agentsMode === "skills" ? path4.join(targetAgentsBase, "skills") : targetAgentsBase;
2122
2143
  const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
2123
- const replacements = {
2144
+ const projectName = config.projectName ?? "{{projectName}}";
2145
+ const commonReplacements = {
2146
+ "{{projectName}}": projectName,
2124
2147
  "{{featurePath}}": featurePath
2125
2148
  };
2149
+ const typeReplacements = {
2150
+ "{{projectName}}": projectName
2151
+ };
2126
2152
  if (await fs8.pathExists(commonAgents)) {
2127
2153
  const count = await updateFolder(
2128
2154
  commonAgents,
2129
2155
  targetAgents,
2130
- options.force,
2131
- replacements,
2156
+ forceOverwrite,
2157
+ commonReplacements,
2132
2158
  lang
2133
2159
  );
2134
2160
  updatedCount += count;
@@ -2137,8 +2163,8 @@ async function runUpdate(options) {
2137
2163
  const count = await updateFolder(
2138
2164
  typeAgents,
2139
2165
  targetAgents,
2140
- options.force,
2141
- void 0,
2166
+ forceOverwrite,
2167
+ typeReplacements,
2142
2168
  lang
2143
2169
  );
2144
2170
  updatedCount += count;
@@ -2154,11 +2180,14 @@ async function runUpdate(options) {
2154
2180
  const sourceFeatureBase = path4.join(sourceDir, "features", "feature-base");
2155
2181
  const targetFeatureBase = path4.join(docsDir, "features", "feature-base");
2156
2182
  if (await fs8.pathExists(sourceFeatureBase)) {
2183
+ const replacements = {
2184
+ "{{projectName}}": config.projectName ?? "{{projectName}}"
2185
+ };
2157
2186
  const count = await updateFolder(
2158
2187
  sourceFeatureBase,
2159
2188
  targetFeatureBase,
2160
- options.force,
2161
- void 0,
2189
+ forceOverwrite,
2190
+ replacements,
2162
2191
  lang
2163
2192
  );
2164
2193
  updatedCount += count;
@@ -2191,9 +2220,7 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
2191
2220
  }
2192
2221
  let sourceContent = await fs8.readFile(sourcePath, "utf-8");
2193
2222
  if (replacements) {
2194
- for (const [key, value] of Object.entries(replacements)) {
2195
- sourceContent = sourceContent.replaceAll(key, value);
2196
- }
2223
+ sourceContent = applyReplacements(sourceContent, replacements);
2197
2224
  }
2198
2225
  let shouldUpdate = true;
2199
2226
  if (await fs8.pathExists(targetPath)) {
@@ -2230,6 +2257,42 @@ async function updateFolder(sourceDir, targetDir, force, replacements, lang = DE
2230
2257
  }
2231
2258
  return updatedCount;
2232
2259
  }
2260
+ function getGitTopLevel2(cwd) {
2261
+ try {
2262
+ const out = execFileSync("git", ["rev-parse", "--show-toplevel"], {
2263
+ cwd,
2264
+ encoding: "utf-8",
2265
+ stdio: ["ignore", "pipe", "ignore"]
2266
+ }).trim();
2267
+ return out || null;
2268
+ } catch {
2269
+ return null;
2270
+ }
2271
+ }
2272
+ function getDocsPorcelainStatus(docsDir) {
2273
+ const top = getGitTopLevel2(docsDir);
2274
+ if (!top) return null;
2275
+ const rel = path4.relative(top, docsDir) || ".";
2276
+ try {
2277
+ return execFileSync("git", ["status", "--porcelain=v1", "--", rel], {
2278
+ cwd: top,
2279
+ encoding: "utf-8",
2280
+ stdio: ["ignore", "pipe", "ignore"]
2281
+ });
2282
+ } catch {
2283
+ return null;
2284
+ }
2285
+ }
2286
+ async function isDocsWorktreeCleanOrThrow(docsDir, lang) {
2287
+ const status = getDocsPorcelainStatus(docsDir);
2288
+ if (status === null) {
2289
+ throw new Error(tr(lang, "cli", "update.gitStatusUnavailable"));
2290
+ }
2291
+ if (status.trim().length > 0) {
2292
+ throw new Error(tr(lang, "cli", "update.docsWorktreeDirty"));
2293
+ }
2294
+ return true;
2295
+ }
2233
2296
  function configCommand(program2) {
2234
2297
  program2.command("config").description("View or modify project configuration").option("--project-root <path>", "Set project root path").option("--repo <repo>", "Repository type for fullstack: fe | be").action(async (options) => {
2235
2298
  try {
@@ -2930,6 +2993,49 @@ function doctorCommand(program2) {
2930
2993
  process.exit(exitCode);
2931
2994
  });
2932
2995
  }
2996
+ function isBannerDisabled() {
2997
+ const v = (process.env.LEE_SPEC_KIT_NO_BANNER || "").trim();
2998
+ return v === "1";
2999
+ }
3000
+ function getBanner(opts) {
3001
+ if (isBannerDisabled()) return "";
3002
+ const lee = `
3003
+ \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
3004
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3005
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3006
+ \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
3007
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3008
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3009
+ \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
3010
+ `;
3011
+ const spec = `
3012
+ \u2591\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588
3013
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3014
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3015
+ \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588
3016
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3017
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3018
+ \u2591\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588
3019
+ `;
3020
+ const kit = `
3021
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588
3022
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3023
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3024
+ \u2591\u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3025
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3026
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588
3027
+ \u2591\u2588\u2588 \u2591\u2588\u2588 \u2591\u2588\u2588\u2588\u2588\u2588\u2588 \u2591\u2588\u2588
3028
+ `;
3029
+ const ascii = `${lee}${spec}${kit}`;
3030
+ const version = opts?.version ? `v${opts.version}` : "";
3031
+ const footer = version ? `
3032
+ ${version}
3033
+ ` : "\n";
3034
+ if (process.stdout.isTTY) {
3035
+ return `${chalk6.cyan(ascii)}${chalk6.gray(footer)}`;
3036
+ }
3037
+ return `${ascii}${footer}`;
3038
+ }
2933
3039
  var CACHE_FILE = path4.join(os.homedir(), ".lee-spec-kit-version-cache.json");
2934
3040
  var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
2935
3041
  function getCurrentVersion() {
@@ -3014,7 +3120,7 @@ function shouldCheckForUpdates() {
3014
3120
  const argv = process.argv.slice(2);
3015
3121
  const hasJsonFlag = argv.includes("--json");
3016
3122
  const isHelpOrVersion = argv.includes("--help") || argv.includes("-h") || argv.includes("--version") || argv.includes("-V");
3017
- const disabledByEnv = (process.env.LSK_NO_UPDATE_CHECK || "").trim() === "1" || (process.env.LEE_SPEC_KIT_NO_UPDATE_CHECK || "").trim() === "1";
3123
+ const disabledByEnv = (process.env.LEE_SPEC_KIT_NO_UPDATE_CHECK || "").trim() === "1";
3018
3124
  if (hasJsonFlag) return false;
3019
3125
  if (!process.stdout.isTTY) return false;
3020
3126
  if (isHelpOrVersion) return false;
@@ -3033,9 +3139,10 @@ function getCliVersion() {
3033
3139
  }
3034
3140
  return "0.0.0";
3035
3141
  }
3142
+ var cliVersion = getCliVersion();
3036
3143
  program.name("lee-spec-kit").description(
3037
3144
  "Project documentation structure generator for AI-assisted development"
3038
- ).version(getCliVersion());
3145
+ ).version(cliVersion).addHelpText("beforeAll", getBanner({ version: cliVersion }));
3039
3146
  initCommand(program);
3040
3147
  featureCommand(program);
3041
3148
  statusCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,8 @@
10
10
  "main": "./dist/index.js",
11
11
  "files": [
12
12
  "dist",
13
- "templates"
13
+ "templates",
14
+ "assets"
14
15
  ],
15
16
  "keywords": [
16
17
  "docs",