lee-spec-kit 0.1.2 → 0.1.5

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.
Files changed (41) hide show
  1. package/README.md +5 -5
  2. package/dist/index.js +105 -53
  3. package/package.json +1 -1
  4. package/templates/en/fullstack/agents/agents.md +1 -1
  5. package/templates/en/fullstack/agents/constitution.md +5 -0
  6. package/templates/en/fullstack/agents/git-workflow.md +2 -0
  7. package/templates/en/fullstack/agents/issue-template.md +35 -1
  8. package/templates/en/fullstack/agents/pr-template.md +19 -1
  9. package/templates/en/fullstack/features/README.md +4 -4
  10. package/templates/en/fullstack/features/be/README.md +1 -1
  11. package/templates/en/fullstack/features/fe/README.md +1 -1
  12. package/templates/en/fullstack/features/feature-base/plan.md +2 -2
  13. package/templates/en/fullstack/features/feature-base/spec.md +4 -4
  14. package/templates/en/fullstack/features/feature-base/tasks.md +4 -4
  15. package/templates/en/fullstack/prd/README.md +5 -0
  16. package/templates/en/single/agents/agents.md +1 -1
  17. package/templates/en/single/agents/constitution.md +44 -39
  18. package/templates/en/single/agents/git-workflow.md +62 -60
  19. package/templates/en/single/agents/issue-template.md +66 -30
  20. package/templates/en/single/agents/pr-template.md +51 -31
  21. package/templates/en/single/features/README.md +2 -2
  22. package/templates/en/single/features/feature-base/plan.md +2 -2
  23. package/templates/en/single/features/feature-base/spec.md +4 -4
  24. package/templates/en/single/features/feature-base/tasks.md +4 -4
  25. package/templates/en/single/prd/README.md +14 -9
  26. package/templates/ko/fullstack/agents/agents.md +1 -1
  27. package/templates/ko/fullstack/agents/constitution.md +5 -0
  28. package/templates/ko/fullstack/agents/git-workflow.md +2 -0
  29. package/templates/ko/fullstack/agents/issue-template.md +36 -0
  30. package/templates/ko/fullstack/agents/pr-template.md +19 -1
  31. package/templates/ko/fullstack/features/README.md +4 -4
  32. package/templates/ko/fullstack/features/be/README.md +1 -1
  33. package/templates/ko/fullstack/features/fe/README.md +1 -1
  34. package/templates/ko/fullstack/prd/README.md +5 -0
  35. package/templates/ko/single/agents/agents.md +1 -1
  36. package/templates/ko/single/agents/constitution.md +5 -0
  37. package/templates/ko/single/agents/git-workflow.md +2 -0
  38. package/templates/ko/single/agents/issue-template.md +36 -0
  39. package/templates/ko/single/agents/pr-template.md +19 -1
  40. package/templates/ko/single/features/README.md +2 -2
  41. package/templates/ko/single/prd/README.md +5 -0
package/README.md CHANGED
@@ -41,21 +41,21 @@ npx lee-spec-kit init --name my-project --type fullstack --lang ko
41
41
 
42
42
  ```bash
43
43
  # Single 프로젝트
44
- lee-spec-kit feature user-auth
44
+ npx lee-spec-kit feature user-auth
45
45
 
46
46
  # Fullstack 프로젝트
47
- lee-spec-kit feature --repo be user-auth
48
- lee-spec-kit feature --repo fe user-profile
47
+ npx lee-spec-kit feature --repo be user-auth
48
+ npx lee-spec-kit feature --repo fe user-profile
49
49
  ```
50
50
 
51
51
  ### 상태 확인
52
52
 
53
53
  ```bash
54
54
  # 터미널에 출력
55
- lee-spec-kit status
55
+ npx lee-spec-kit status
56
56
 
57
57
  # 파일로 저장
58
- lee-spec-kit status --write
58
+ npx lee-spec-kit status --write
59
59
  ```
60
60
 
61
61
  ## 생성되는 구조
package/dist/index.js CHANGED
@@ -2,13 +2,13 @@
2
2
  import { program } from 'commander';
3
3
  import prompts from 'prompts';
4
4
  import chalk from 'chalk';
5
- import path4 from 'path';
6
- import fs5 from 'fs-extra';
5
+ import path3 from 'path';
6
+ import fs3 from 'fs-extra';
7
7
  import { glob } from 'glob';
8
8
  import { fileURLToPath } from 'url';
9
9
 
10
10
  async function copyTemplates(src, dest) {
11
- await fs5.copy(src, dest, {
11
+ await fs3.copy(src, dest, {
12
12
  overwrite: true,
13
13
  errorOnExist: false
14
14
  });
@@ -16,26 +16,26 @@ async function copyTemplates(src, dest) {
16
16
  async function replaceInFiles(dir, replacements) {
17
17
  const files = await glob("**/*.md", { cwd: dir, absolute: true });
18
18
  for (const file of files) {
19
- let content = await fs5.readFile(file, "utf-8");
19
+ let content = await fs3.readFile(file, "utf-8");
20
20
  for (const [search, replace] of Object.entries(replacements)) {
21
21
  content = content.replaceAll(search, replace);
22
22
  }
23
- await fs5.writeFile(file, content, "utf-8");
23
+ await fs3.writeFile(file, content, "utf-8");
24
24
  }
25
25
  const shFiles = await glob("**/*.sh", { cwd: dir, absolute: true });
26
26
  for (const file of shFiles) {
27
- let content = await fs5.readFile(file, "utf-8");
27
+ let content = await fs3.readFile(file, "utf-8");
28
28
  for (const [search, replace] of Object.entries(replacements)) {
29
29
  content = content.replaceAll(search, replace);
30
30
  }
31
- await fs5.writeFile(file, content, "utf-8");
31
+ await fs3.writeFile(file, content, "utf-8");
32
32
  }
33
33
  }
34
34
  var __filename2 = fileURLToPath(import.meta.url);
35
- var __dirname2 = path4.dirname(__filename2);
35
+ var __dirname2 = path3.dirname(__filename2);
36
36
  function getTemplatesDir() {
37
- const rootDir = path4.resolve(__dirname2, "..");
38
- return path4.join(rootDir, "templates");
37
+ const rootDir = path3.resolve(__dirname2, "..");
38
+ return path3.join(rootDir, "templates");
39
39
  }
40
40
 
41
41
  // src/utils/validation.ts
@@ -150,11 +150,11 @@ function initCommand(program2) {
150
150
  }
151
151
  async function runInit(options) {
152
152
  const cwd = process.cwd();
153
- const defaultName = path4.basename(cwd);
153
+ const defaultName = path3.basename(cwd);
154
154
  let projectName = options.name || defaultName;
155
155
  let projectType = options.type;
156
156
  let lang = options.lang || "ko";
157
- const targetDir = path4.resolve(cwd, options.dir || "./docs");
157
+ const targetDir = path3.resolve(cwd, options.dir || "./docs");
158
158
  if (!options.yes) {
159
159
  const response = await prompts(
160
160
  [
@@ -209,8 +209,8 @@ async function runInit(options) {
209
209
  assertValid(validateSafeName(projectName), "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984");
210
210
  assertValid(validateProjectType(projectType), "\uD504\uB85C\uC81D\uD2B8 \uD0C0\uC785");
211
211
  assertValid(validateLanguage(lang), "\uC5B8\uC5B4");
212
- if (await fs5.pathExists(targetDir)) {
213
- const files = await fs5.readdir(targetDir);
212
+ if (await fs3.pathExists(targetDir)) {
213
+ const files = await fs3.readdir(targetDir);
214
214
  if (files.length > 0) {
215
215
  const { overwrite } = await prompts({
216
216
  type: "confirm",
@@ -232,8 +232,8 @@ async function runInit(options) {
232
232
  console.log(chalk.gray(` \uACBD\uB85C: ${targetDir}`));
233
233
  console.log();
234
234
  const templatesDir = getTemplatesDir();
235
- const templatePath = path4.join(templatesDir, lang, projectType);
236
- if (!await fs5.pathExists(templatePath)) {
235
+ const templatePath = path3.join(templatesDir, lang, projectType);
236
+ if (!await fs3.pathExists(templatePath)) {
237
237
  throw new Error(`\uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${templatePath}`);
238
238
  }
239
239
  await copyTemplates(templatePath, targetDir);
@@ -242,30 +242,82 @@ async function runInit(options) {
242
242
  "{{date}}": (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
243
243
  };
244
244
  await replaceInFiles(targetDir, replacements);
245
+ const config = {
246
+ projectName,
247
+ projectType,
248
+ lang,
249
+ createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
250
+ };
251
+ const configPath = path3.join(targetDir, ".lee-spec-kit.json");
252
+ await fs3.writeJson(configPath, config, { spaces: 2 });
245
253
  console.log(chalk.green("\u2705 docs \uAD6C\uC870 \uC0DD\uC131 \uC644\uB8CC!"));
246
254
  console.log();
255
+ await initGit(cwd, targetDir);
247
256
  console.log(chalk.blue("\uB2E4\uC74C \uB2E8\uACC4:"));
248
257
  console.log(chalk.gray(` 1. ${targetDir}/prd/README.md \uC791\uC131`));
249
- console.log(chalk.gray(" 2. lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00"));
258
+ console.log(
259
+ chalk.gray(" 2. npx lee-spec-kit feature <name> \uC73C\uB85C \uAE30\uB2A5 \uCD94\uAC00")
260
+ );
250
261
  console.log();
251
262
  }
263
+ async function initGit(cwd, targetDir) {
264
+ const { execSync } = await import('child_process');
265
+ try {
266
+ try {
267
+ execSync("git rev-parse --is-inside-work-tree", {
268
+ cwd,
269
+ stdio: "ignore"
270
+ });
271
+ console.log(chalk.blue("\u{1F4E6} Git \uB808\uD3EC\uC9C0\uD1A0\uB9AC \uAC10\uC9C0, docs \uCEE4\uBC0B \uC911..."));
272
+ } catch {
273
+ console.log(chalk.blue("\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911..."));
274
+ execSync("git init", { cwd, stdio: "ignore" });
275
+ }
276
+ const relativePath = path3.relative(cwd, targetDir);
277
+ execSync(`git add "${relativePath}"`, { cwd, stdio: "ignore" });
278
+ execSync('git commit -m "init: docs \uAD6C\uC870 \uCD08\uAE30\uD654 (lee-spec-kit)"', {
279
+ cwd,
280
+ stdio: "ignore"
281
+ });
282
+ console.log(chalk.green("\u2705 Git \uCD08\uAE30 \uCEE4\uBC0B \uC644\uB8CC!"));
283
+ console.log();
284
+ } catch (error) {
285
+ console.log(
286
+ chalk.yellow("\u26A0\uFE0F Git \uCD08\uAE30\uD654\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4 (\uC218\uB3D9\uC73C\uB85C \uCEE4\uBC0B\uD574\uC8FC\uC138\uC694)")
287
+ );
288
+ console.log();
289
+ }
290
+ }
252
291
  async function getConfig(cwd) {
253
292
  const possibleDirs = [
254
- path4.join(cwd, "docs"),
293
+ path3.join(cwd, "docs"),
255
294
  cwd
256
295
  // 이미 docs 폴더 안에 있을 수 있음
257
296
  ];
258
297
  for (const docsDir of possibleDirs) {
259
- const agentsPath = path4.join(docsDir, "agents");
260
- const featuresPath = path4.join(docsDir, "features");
261
- if (await fs5.pathExists(agentsPath) && await fs5.pathExists(featuresPath)) {
262
- const bePath = path4.join(featuresPath, "be");
263
- const fePath = path4.join(featuresPath, "fe");
264
- const projectType = await fs5.pathExists(bePath) || await fs5.pathExists(fePath) ? "fullstack" : "single";
265
- const agentsMdPath = path4.join(agentsPath, "agents.md");
298
+ const configPath = path3.join(docsDir, ".lee-spec-kit.json");
299
+ if (await fs3.pathExists(configPath)) {
300
+ try {
301
+ const configFile = await fs3.readJson(configPath);
302
+ return {
303
+ docsDir,
304
+ projectName: configFile.projectName,
305
+ projectType: configFile.projectType,
306
+ lang: configFile.lang
307
+ };
308
+ } catch {
309
+ }
310
+ }
311
+ const agentsPath = path3.join(docsDir, "agents");
312
+ const featuresPath = path3.join(docsDir, "features");
313
+ if (await fs3.pathExists(agentsPath) && await fs3.pathExists(featuresPath)) {
314
+ const bePath = path3.join(featuresPath, "be");
315
+ const fePath = path3.join(featuresPath, "fe");
316
+ const projectType = await fs3.pathExists(bePath) || await fs3.pathExists(fePath) ? "fullstack" : "single";
317
+ const agentsMdPath = path3.join(agentsPath, "agents.md");
266
318
  let lang = "ko";
267
- if (await fs5.pathExists(agentsMdPath)) {
268
- const content = await fs5.readFile(agentsMdPath, "utf-8");
319
+ if (await fs3.pathExists(agentsMdPath)) {
320
+ const content = await fs3.readFile(agentsMdPath, "utf-8");
269
321
  if (!/[가-힣]/.test(content)) {
270
322
  lang = "en";
271
323
  }
@@ -334,22 +386,22 @@ async function runFeature(name, options) {
334
386
  }
335
387
  let featuresDir;
336
388
  if (projectType === "fullstack" && repo) {
337
- featuresDir = path4.join(docsDir, "features", repo);
389
+ featuresDir = path3.join(docsDir, "features", repo);
338
390
  } else {
339
- featuresDir = path4.join(docsDir, "features");
391
+ featuresDir = path3.join(docsDir, "features");
340
392
  }
341
393
  const featureFolderName = `${featureId}-${name}`;
342
- const featureDir = path4.join(featuresDir, featureFolderName);
343
- if (await fs5.pathExists(featureDir)) {
394
+ const featureDir = path3.join(featuresDir, featureFolderName);
395
+ if (await fs3.pathExists(featureDir)) {
344
396
  console.error(chalk.red(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: ${featureDir}`));
345
397
  process.exit(1);
346
398
  }
347
- const featureBasePath = path4.join(docsDir, "features", "feature-base");
348
- if (!await fs5.pathExists(featureBasePath)) {
399
+ const featureBasePath = path3.join(docsDir, "features", "feature-base");
400
+ if (!await fs3.pathExists(featureBasePath)) {
349
401
  console.error(chalk.red("feature-base \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
350
402
  process.exit(1);
351
403
  }
352
- await fs5.copy(featureBasePath, featureDir);
404
+ await fs3.copy(featureBasePath, featureDir);
353
405
  const idNumber = featureId.replace("F", "");
354
406
  const repoName = projectType === "fullstack" && repo ? `{{projectName}}-${repo}` : "{{projectName}}";
355
407
  const replacements = {
@@ -379,18 +431,18 @@ async function runFeature(name, options) {
379
431
  console.log();
380
432
  }
381
433
  async function getNextFeatureId(docsDir, projectType) {
382
- const featuresDir = path4.join(docsDir, "features");
434
+ const featuresDir = path3.join(docsDir, "features");
383
435
  let max = 0;
384
436
  const scanDirs = [];
385
437
  if (projectType === "fullstack") {
386
- scanDirs.push(path4.join(featuresDir, "be"));
387
- scanDirs.push(path4.join(featuresDir, "fe"));
438
+ scanDirs.push(path3.join(featuresDir, "be"));
439
+ scanDirs.push(path3.join(featuresDir, "fe"));
388
440
  } else {
389
441
  scanDirs.push(featuresDir);
390
442
  }
391
443
  for (const dir of scanDirs) {
392
- if (!await fs5.pathExists(dir)) continue;
393
- const entries = await fs5.readdir(dir, { withFileTypes: true });
444
+ if (!await fs3.pathExists(dir)) continue;
445
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
394
446
  for (const entry of entries) {
395
447
  if (!entry.isDirectory()) continue;
396
448
  const match = entry.name.match(/^F(\d+)-/);
@@ -424,29 +476,29 @@ async function runStatus(options) {
424
476
  process.exit(1);
425
477
  }
426
478
  const { docsDir, projectType } = config;
427
- const featuresDir = path4.join(docsDir, "features");
479
+ const featuresDir = path3.join(docsDir, "features");
428
480
  const features = [];
429
481
  const idMap = /* @__PURE__ */ new Map();
430
482
  const scopes = projectType === "fullstack" ? ["be", "fe"] : [""];
431
483
  for (const scope of scopes) {
432
- const scanDir = scope ? path4.join(featuresDir, scope) : featuresDir;
433
- if (!await fs5.pathExists(scanDir)) continue;
434
- const entries = await fs5.readdir(scanDir, { withFileTypes: true });
484
+ const scanDir = scope ? path3.join(featuresDir, scope) : featuresDir;
485
+ if (!await fs3.pathExists(scanDir)) continue;
486
+ const entries = await fs3.readdir(scanDir, { withFileTypes: true });
435
487
  for (const entry of entries) {
436
488
  if (!entry.isDirectory()) continue;
437
489
  if (entry.name === "feature-base") continue;
438
- const featureDir = path4.join(scanDir, entry.name);
439
- const specPath = path4.join(featureDir, "spec.md");
440
- const tasksPath = path4.join(featureDir, "tasks.md");
441
- if (!await fs5.pathExists(specPath)) continue;
442
- if (!await fs5.pathExists(tasksPath)) continue;
443
- const specContent = await fs5.readFile(specPath, "utf-8");
444
- const tasksContent = await fs5.readFile(tasksPath, "utf-8");
490
+ const featureDir = path3.join(scanDir, entry.name);
491
+ const specPath = path3.join(featureDir, "spec.md");
492
+ const tasksPath = path3.join(featureDir, "tasks.md");
493
+ if (!await fs3.pathExists(specPath)) continue;
494
+ if (!await fs3.pathExists(tasksPath)) continue;
495
+ const specContent = await fs3.readFile(specPath, "utf-8");
496
+ const tasksContent = await fs3.readFile(tasksPath, "utf-8");
445
497
  const id = extractSpecValue(specContent, "\uAE30\uB2A5 ID") || extractSpecValue(specContent, "Feature ID") || "UNKNOWN";
446
498
  const name = extractSpecValue(specContent, "\uAE30\uB2A5\uBA85") || extractSpecValue(specContent, "Feature Name") || entry.name;
447
499
  const repo = extractSpecValue(specContent, "\uB300\uC0C1 \uB808\uD3EC") || extractSpecValue(specContent, "Target Repo") || (scope ? `{{projectName}}-${scope}` : "{{projectName}}");
448
500
  const issue = extractSpecValue(specContent, "\uC774\uC288 \uBC88\uD638") || extractSpecValue(specContent, "Issue Number") || "-";
449
- const relPath = path4.relative(docsDir, featureDir);
501
+ const relPath = path3.relative(docsDir, featureDir);
450
502
  if (!idMap.has(id)) {
451
503
  idMap.set(id, []);
452
504
  }
@@ -516,7 +568,7 @@ async function runStatus(options) {
516
568
  }
517
569
  console.log();
518
570
  if (options.write) {
519
- const outputPath = path4.join(featuresDir, "status.md");
571
+ const outputPath = path3.join(featuresDir, "status.md");
520
572
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
521
573
  const content = [
522
574
  "# Feature Status",
@@ -531,7 +583,7 @@ async function runStatus(options) {
531
583
  ),
532
584
  ""
533
585
  ].join("\n");
534
- await fs5.writeFile(outputPath, content, "utf-8");
586
+ await fs3.writeFile(outputPath, content, "utf-8");
535
587
  console.log(chalk.green(`\u2705 ${outputPath} \uC0DD\uC131 \uC644\uB8CC`));
536
588
  }
537
589
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -50,7 +50,7 @@ docs/
50
50
  ### 1. New Feature Request
51
51
 
52
52
  1. Identify target repo (BE or FE)
53
- 2. Create feature folder: `lee-spec-kit feature <name>`
53
+ 2. Create feature folder: `npx lee-spec-kit feature <name>`
54
54
  3. Write `spec.md` - what and why (no tech stack)
55
55
  4. Request spec review from user
56
56
  5. Create GitHub Issue
@@ -3,6 +3,11 @@
3
3
  Core principles and technical decision guidelines for the project.
4
4
  All development decisions should be based on this document.
5
5
 
6
+ > **📌 Document Scope**
7
+ >
8
+ > - **This document**: Tech stack, architecture principles, code quality, security principles
9
+ > - **PRD**: Product requirements, business logic, user stories → `prd/*.md`
10
+
6
11
  ---
7
12
 
8
13
  ## Project Mission
@@ -41,6 +41,8 @@ main
41
41
 
42
42
  ## Commit Convention
43
43
 
44
+ > 📖 Type and Description follow [Udacity Git Commit Message Style Guide](https://udacity.github.io/git-styleguide/).
45
+
44
46
  ### Format
45
47
 
46
48
  ```
@@ -14,7 +14,32 @@ F{number}: {feature-name} ({short description})
14
14
 
15
15
  Example: `F001: user-auth (User authentication feature)`
16
16
 
17
- ---
17
+ ### Link Format (Important!)
18
+
19
+ In GitHub Issues, use different link formats **based on file location**:
20
+
21
+ 1. **Files within project repo**: Use full URL (clickable)
22
+ - **Merged documents/code**: Use `main` branch
23
+ ```markdown
24
+ [filename](https://github.com/{owner}/{repo}/blob/main/path/to/file)
25
+ ```
26
+ - **In-progress documents** (not merged yet): Use **Feature branch**
27
+ ```markdown
28
+ [filename](https://github.com/{owner}/{repo}/blob/{feat-branch}/path/to/file)
29
+ ```
30
+
31
+ 2. **External documents (with public URL)**: Use **absolute URL**
32
+
33
+ ```markdown
34
+ [react-i18next](https://react.i18next.com/)
35
+ ```
36
+
37
+ 3. **External/local documents** (no URL available): Use **relative path as text only**
38
+ ```text
39
+ ../docs/features/F001-feature-name/spec.md
40
+ ```
41
+
42
+ > ⚠️ Local documents are not clickable on GitHub, so provide path text only.
18
43
 
19
44
  ## Issue Body Template
20
45
 
@@ -56,3 +81,12 @@ Example: `F001: user-auth (User authentication feature)`
56
81
  | `backend` | BE related |
57
82
  | `frontend` | FE related |
58
83
  | `priority:high` | High priority |
84
+
85
+ ---
86
+
87
+ ## Body Input Rules (Shell Execution Prevention)
88
+
89
+ - Issue body should use **`--body-file` by default**.
90
+ - If the body contains backticks (`) or `$()`and is placed directly in`"..."`, it may be **interpreted by the shell**.
91
+ - For multi-line bodies, use **single-quoted heredoc** like `cat <<'EOF'`,
92
+ and handle variables via **placeholder → sed substitution**.
@@ -14,7 +14,16 @@ feat(#{issue-number}): {feature-name}
14
14
 
15
15
  Example: `feat(#1): Implement user authentication`
16
16
 
17
- ---
17
+ ### Link Format (Important!)
18
+
19
+ For file links within the repo in PR body, **always use current branch name**:
20
+
21
+ ```markdown
22
+ [filename](https://github.com/{owner}/{repo}/blob/{branch-name}/docs/path/to/file.md)
23
+ ```
24
+
25
+ > ⚠️ `main` branch links will return 404 until merged!
26
+ > Always use the **current feature branch name** (e.g., `feat/5-feature-name`).
18
27
 
19
28
  ## PR Body Template
20
29
 
@@ -55,3 +64,12 @@ Closes #{issue-number}
55
64
  | Normal Feature | Squash and Merge |
56
65
  | Urgent Hotfix | Merge or Rebase |
57
66
  | Documentation | Squash and Merge |
67
+
68
+ ---
69
+
70
+ ## Body Input Rules (Shell Execution Prevention)
71
+
72
+ - PR body should use **`--body-file` by default**.
73
+ - If the body contains backticks (`) or `$()`and is placed directly in`"..."`, it may be **interpreted by the shell**.
74
+ - For multi-line bodies, use **single-quoted heredoc** like `cat <<'EOF'`,
75
+ and handle variables via **placeholder → sed substitution**.
@@ -29,10 +29,10 @@ features/
29
29
 
30
30
  ```bash
31
31
  # Backend Feature
32
- lee-spec-kit feature --repo be user-auth
32
+ npx lee-spec-kit feature --repo be user-auth
33
33
 
34
34
  # Frontend Feature
35
- lee-spec-kit feature --repo fe user-profile
35
+ npx lee-spec-kit feature --repo fe user-profile
36
36
  ```
37
37
 
38
38
  > 💡 CLI copies templates from `feature-base/` and auto-assigns IDs.
@@ -55,13 +55,13 @@ lee-spec-kit feature --repo fe user-profile
55
55
  Check feature progress with CLI:
56
56
 
57
57
  ```bash
58
- lee-spec-kit status
58
+ npx lee-spec-kit status
59
59
  ```
60
60
 
61
61
  Save to file:
62
62
 
63
63
  ```bash
64
- lee-spec-kit status --write
64
+ npx lee-spec-kit status --write
65
65
  ```
66
66
 
67
67
  ---
@@ -2,4 +2,4 @@
2
2
 
3
3
  Backend Features will be created in this folder.
4
4
 
5
- Use `lee-spec-kit feature --repo be <name>` to create a new Feature.
5
+ Use `npx lee-spec-kit feature --repo be <name>` to create a new Feature.
@@ -2,4 +2,4 @@
2
2
 
3
3
  Frontend Features will be created in this folder.
4
4
 
5
- Use `lee-spec-kit feature --repo fe <name>` to create a new Feature.
5
+ Use `npx lee-spec-kit feature --repo fe <name>` to create a new Feature.
@@ -1,4 +1,4 @@
1
- # Implementation Plan: {기능명}
1
+ # Implementation Plan: {feature-name}
2
2
 
3
3
  > Write after spec is approved.
4
4
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Overview
8
8
 
9
- - **Feature ID**: F{번호}
9
+ - **Feature ID**: F{number}
10
10
  - **Target Repo**: {{projectName}}-{be|fe}
11
11
  - **Created**: YYYY-MM-DD
12
12
  - **Status**: Draft | Review | Approved
@@ -1,4 +1,4 @@
1
- # Feature Spec: {기능명}
1
+ # Feature Spec: {feature-name}
2
2
 
3
3
  > Tech stack is covered in plan.md.
4
4
 
@@ -6,10 +6,10 @@
6
6
 
7
7
  ## Overview
8
8
 
9
- - **Feature ID**: F{번호}
10
- - **Feature Name**: {기능명}
9
+ - **Feature ID**: F{number}
10
+ - **Feature Name**: {feature-name}
11
11
  - **Target Repo**: {{projectName}}-{be|fe}
12
- - **Issue Number**: #{이슈번호}
12
+ - **Issue Number**: #{issue-number}
13
13
  - **Created**: YYYY-MM-DD
14
14
  - **Status**: Draft | Review | Approved
15
15
 
@@ -1,4 +1,4 @@
1
- # Tasks: {기능명}
1
+ # Tasks: {feature-name}
2
2
 
3
3
  ## Task Rules
4
4
 
@@ -10,8 +10,8 @@
10
10
  ## GitHub Issue
11
11
 
12
12
  - **Repo**: {{projectName}}-{be|fe}
13
- - **Issue**: #{이슈번호}
14
- - **Branch**: `feat/{이슈번호}-{기능명}`
13
+ - **Issue**: #{issue-number}
14
+ - **Branch**: `feat/{issue-number}-{feature-name}`
15
15
 
16
16
  ---
17
17
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  ### Phase 1: {Phase Name}
21
21
 
22
- - [TODO][P1] T-F{번호}-01 {Task Title}
22
+ - [TODO][P1] T-F{number}-01 {Task Title}
23
23
  - Acceptance:
24
24
  - (verification condition)
25
25
  - Checklist:
@@ -2,6 +2,11 @@
2
2
 
3
3
  This folder contains product requirements documentation.
4
4
 
5
+ > **📌 Document Scope**
6
+ >
7
+ > - **This folder**: Product requirements, business logic, user stories
8
+ > - **Constitution**: Tech stack, architecture principles, code quality, security principles → `agents/constitution.md`
9
+
5
10
  ## Writing Guide
6
11
 
7
12
  1. Define project overview and goals
@@ -44,7 +44,7 @@ docs/
44
44
 
45
45
  ### 1. New Feature Request
46
46
 
47
- 1. Create feature folder: `lee-spec-kit feature <name>`
47
+ 1. Create feature folder: `npx lee-spec-kit feature <name>`
48
48
  2. Write `spec.md` - what and why
49
49
  3. Request spec review
50
50
  4. Create GitHub Issue