lee-spec-kit 0.1.5 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3,12 +3,12 @@ import { program } from 'commander';
3
3
  import prompts from 'prompts';
4
4
  import chalk from 'chalk';
5
5
  import path3 from 'path';
6
- import fs3 from 'fs-extra';
6
+ import fs6 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 fs3.copy(src, dest, {
11
+ await fs6.copy(src, dest, {
12
12
  overwrite: true,
13
13
  errorOnExist: false
14
14
  });
@@ -16,19 +16,19 @@ 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 fs3.readFile(file, "utf-8");
19
+ let content = await fs6.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 fs3.writeFile(file, content, "utf-8");
23
+ await fs6.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 fs3.readFile(file, "utf-8");
27
+ let content = await fs6.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 fs3.writeFile(file, content, "utf-8");
31
+ await fs6.writeFile(file, content, "utf-8");
32
32
  }
33
33
  }
34
34
  var __filename2 = fileURLToPath(import.meta.url);
@@ -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 fs3.pathExists(targetDir)) {
213
- const files = await fs3.readdir(targetDir);
212
+ if (await fs6.pathExists(targetDir)) {
213
+ const files = await fs6.readdir(targetDir);
214
214
  if (files.length > 0) {
215
215
  const { overwrite } = await prompts({
216
216
  type: "confirm",
@@ -233,7 +233,7 @@ async function runInit(options) {
233
233
  console.log();
234
234
  const templatesDir = getTemplatesDir();
235
235
  const templatePath = path3.join(templatesDir, lang, projectType);
236
- if (!await fs3.pathExists(templatePath)) {
236
+ if (!await fs6.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);
@@ -249,7 +249,7 @@ async function runInit(options) {
249
249
  createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
250
250
  };
251
251
  const configPath = path3.join(targetDir, ".lee-spec-kit.json");
252
- await fs3.writeJson(configPath, config, { spaces: 2 });
252
+ await fs6.writeJson(configPath, config, { spaces: 2 });
253
253
  console.log(chalk.green("\u2705 docs \uAD6C\uC870 \uC0DD\uC131 \uC644\uB8CC!"));
254
254
  console.log();
255
255
  await initGit(cwd, targetDir);
@@ -296,9 +296,9 @@ async function getConfig(cwd) {
296
296
  ];
297
297
  for (const docsDir of possibleDirs) {
298
298
  const configPath = path3.join(docsDir, ".lee-spec-kit.json");
299
- if (await fs3.pathExists(configPath)) {
299
+ if (await fs6.pathExists(configPath)) {
300
300
  try {
301
- const configFile = await fs3.readJson(configPath);
301
+ const configFile = await fs6.readJson(configPath);
302
302
  return {
303
303
  docsDir,
304
304
  projectName: configFile.projectName,
@@ -310,14 +310,14 @@ async function getConfig(cwd) {
310
310
  }
311
311
  const agentsPath = path3.join(docsDir, "agents");
312
312
  const featuresPath = path3.join(docsDir, "features");
313
- if (await fs3.pathExists(agentsPath) && await fs3.pathExists(featuresPath)) {
313
+ if (await fs6.pathExists(agentsPath) && await fs6.pathExists(featuresPath)) {
314
314
  const bePath = path3.join(featuresPath, "be");
315
315
  const fePath = path3.join(featuresPath, "fe");
316
- const projectType = await fs3.pathExists(bePath) || await fs3.pathExists(fePath) ? "fullstack" : "single";
316
+ const projectType = await fs6.pathExists(bePath) || await fs6.pathExists(fePath) ? "fullstack" : "single";
317
317
  const agentsMdPath = path3.join(agentsPath, "agents.md");
318
318
  let lang = "ko";
319
- if (await fs3.pathExists(agentsMdPath)) {
320
- const content = await fs3.readFile(agentsMdPath, "utf-8");
319
+ if (await fs6.pathExists(agentsMdPath)) {
320
+ const content = await fs6.readFile(agentsMdPath, "utf-8");
321
321
  if (!/[가-힣]/.test(content)) {
322
322
  lang = "en";
323
323
  }
@@ -330,7 +330,7 @@ async function getConfig(cwd) {
330
330
 
331
331
  // src/commands/feature.ts
332
332
  function featureCommand(program2) {
333
- program2.command("feature <name>").description("Create a new feature folder").option("-r, --repo <repo>", "Repository type: be | fe (fullstack only)").option("--id <id>", "Feature ID (default: auto)").action(async (name, options) => {
333
+ program2.command("feature <name>").description("Create a new feature folder").option("-r, --repo <repo>", "Repository type: be | fe (fullstack only)").option("--id <id>", "Feature ID (default: auto)").option("-d, --desc <description>", "Feature description for spec.md").action(async (name, options) => {
334
334
  try {
335
335
  await runFeature(name, options);
336
336
  } catch (error) {
@@ -392,16 +392,16 @@ async function runFeature(name, options) {
392
392
  }
393
393
  const featureFolderName = `${featureId}-${name}`;
394
394
  const featureDir = path3.join(featuresDir, featureFolderName);
395
- if (await fs3.pathExists(featureDir)) {
395
+ if (await fs6.pathExists(featureDir)) {
396
396
  console.error(chalk.red(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: ${featureDir}`));
397
397
  process.exit(1);
398
398
  }
399
399
  const featureBasePath = path3.join(docsDir, "features", "feature-base");
400
- if (!await fs3.pathExists(featureBasePath)) {
400
+ if (!await fs6.pathExists(featureBasePath)) {
401
401
  console.error(chalk.red("feature-base \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
402
402
  process.exit(1);
403
403
  }
404
- await fs3.copy(featureBasePath, featureDir);
404
+ await fs6.copy(featureBasePath, featureDir);
405
405
  const idNumber = featureId.replace("F", "");
406
406
  const repoName = projectType === "fullstack" && repo ? `{{projectName}}-${repo}` : "{{projectName}}";
407
407
  const replacements = {
@@ -410,7 +410,8 @@ async function runFeature(name, options) {
410
410
  "YYYY-MM-DD": (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
411
411
  "{be|fe}": repo || "",
412
412
  "git-dungeon-{be|fe}": repoName,
413
- "{\uC774\uC288\uBC88\uD638}": ""
413
+ "{\uC774\uC288\uBC88\uD638}": "",
414
+ "{{description}}": options.desc || ""
414
415
  };
415
416
  if (lang === "en") {
416
417
  replacements["\uAE30\uB2A5 ID"] = "Feature ID";
@@ -441,8 +442,8 @@ async function getNextFeatureId(docsDir, projectType) {
441
442
  scanDirs.push(featuresDir);
442
443
  }
443
444
  for (const dir of scanDirs) {
444
- if (!await fs3.pathExists(dir)) continue;
445
- const entries = await fs3.readdir(dir, { withFileTypes: true });
445
+ if (!await fs6.pathExists(dir)) continue;
446
+ const entries = await fs6.readdir(dir, { withFileTypes: true });
446
447
  for (const entry of entries) {
447
448
  if (!entry.isDirectory()) continue;
448
449
  const match = entry.name.match(/^F(\d+)-/);
@@ -482,18 +483,18 @@ async function runStatus(options) {
482
483
  const scopes = projectType === "fullstack" ? ["be", "fe"] : [""];
483
484
  for (const scope of scopes) {
484
485
  const scanDir = scope ? path3.join(featuresDir, scope) : featuresDir;
485
- if (!await fs3.pathExists(scanDir)) continue;
486
- const entries = await fs3.readdir(scanDir, { withFileTypes: true });
486
+ if (!await fs6.pathExists(scanDir)) continue;
487
+ const entries = await fs6.readdir(scanDir, { withFileTypes: true });
487
488
  for (const entry of entries) {
488
489
  if (!entry.isDirectory()) continue;
489
490
  if (entry.name === "feature-base") continue;
490
491
  const featureDir = path3.join(scanDir, entry.name);
491
492
  const specPath = path3.join(featureDir, "spec.md");
492
493
  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");
494
+ if (!await fs6.pathExists(specPath)) continue;
495
+ if (!await fs6.pathExists(tasksPath)) continue;
496
+ const specContent = await fs6.readFile(specPath, "utf-8");
497
+ const tasksContent = await fs6.readFile(tasksPath, "utf-8");
497
498
  const id = extractSpecValue(specContent, "\uAE30\uB2A5 ID") || extractSpecValue(specContent, "Feature ID") || "UNKNOWN";
498
499
  const name = extractSpecValue(specContent, "\uAE30\uB2A5\uBA85") || extractSpecValue(specContent, "Feature Name") || entry.name;
499
500
  const repo = extractSpecValue(specContent, "\uB300\uC0C1 \uB808\uD3EC") || extractSpecValue(specContent, "Target Repo") || (scope ? `{{projectName}}-${scope}` : "{{projectName}}");
@@ -583,7 +584,7 @@ async function runStatus(options) {
583
584
  ),
584
585
  ""
585
586
  ].join("\n");
586
- await fs3.writeFile(outputPath, content, "utf-8");
587
+ await fs6.writeFile(outputPath, content, "utf-8");
587
588
  console.log(chalk.green(`\u2705 ${outputPath} \uC0DD\uC131 \uC644\uB8CC`));
588
589
  }
589
590
  }
@@ -610,6 +611,108 @@ function countTasks(content) {
610
611
  }
611
612
  return { total, done, doing, todo };
612
613
  }
614
+ function updateCommand(program2) {
615
+ program2.command("update").description("Update docs templates to the latest version").option("--agents", "Update agents/ folder only").option("--templates", "Update feature-base/ folder only").option("-f, --force", "Force overwrite without confirmation").action(async (options) => {
616
+ try {
617
+ await runUpdate(options);
618
+ } catch (error) {
619
+ if (error instanceof Error && error.message === "canceled") {
620
+ console.log(chalk.yellow("\n\uC791\uC5C5\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
621
+ process.exit(0);
622
+ }
623
+ console.error(chalk.red("\uC624\uB958:"), error);
624
+ process.exit(1);
625
+ }
626
+ });
627
+ }
628
+ async function runUpdate(options) {
629
+ const cwd = process.cwd();
630
+ const config = await getConfig(cwd);
631
+ if (!config) {
632
+ console.error(
633
+ chalk.red("docs \uD3F4\uB354\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 init\uC744 \uC2E4\uD589\uD558\uC138\uC694.")
634
+ );
635
+ process.exit(1);
636
+ }
637
+ const { docsDir, projectType, lang } = config;
638
+ const templatesDir = getTemplatesDir();
639
+ const sourceDir = path3.join(templatesDir, lang, projectType);
640
+ const updateAgents = options.agents || !options.agents && !options.templates;
641
+ const updateTemplates = options.templates || !options.agents && !options.templates;
642
+ console.log(chalk.blue("\u{1F4E6} \uD15C\uD50C\uB9BF \uC5C5\uB370\uC774\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4..."));
643
+ console.log(chalk.gray(` - \uC5B8\uC5B4: ${lang}`));
644
+ console.log(chalk.gray(` - \uD0C0\uC785: ${projectType}`));
645
+ console.log();
646
+ let updatedCount = 0;
647
+ if (updateAgents) {
648
+ console.log(chalk.blue("\u{1F4C1} agents/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
649
+ const sourceAgents = path3.join(sourceDir, "agents");
650
+ const targetAgents = path3.join(docsDir, "agents");
651
+ if (await fs6.pathExists(sourceAgents)) {
652
+ const count = await updateFolder(
653
+ sourceAgents,
654
+ targetAgents,
655
+ options.force
656
+ );
657
+ updatedCount += count;
658
+ console.log(chalk.green(` \u2705 ${count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC`));
659
+ }
660
+ }
661
+ if (updateTemplates) {
662
+ console.log(chalk.blue("\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
663
+ const sourceFeatureBase = path3.join(sourceDir, "features", "feature-base");
664
+ const targetFeatureBase = path3.join(docsDir, "features", "feature-base");
665
+ if (await fs6.pathExists(sourceFeatureBase)) {
666
+ const count = await updateFolder(
667
+ sourceFeatureBase,
668
+ targetFeatureBase,
669
+ options.force
670
+ );
671
+ updatedCount += count;
672
+ console.log(chalk.green(` \u2705 ${count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC`));
673
+ }
674
+ }
675
+ console.log();
676
+ console.log(chalk.green(`\u2705 \uCD1D ${updatedCount}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
677
+ }
678
+ async function updateFolder(sourceDir, targetDir, force) {
679
+ await fs6.ensureDir(targetDir);
680
+ const files = await fs6.readdir(sourceDir);
681
+ let updatedCount = 0;
682
+ for (const file of files) {
683
+ const sourcePath = path3.join(sourceDir, file);
684
+ const targetPath = path3.join(targetDir, file);
685
+ const stat = await fs6.stat(sourcePath);
686
+ if (stat.isFile()) {
687
+ if (file === "custom.md") {
688
+ continue;
689
+ }
690
+ const sourceContent = await fs6.readFile(sourcePath, "utf-8");
691
+ let shouldUpdate = true;
692
+ if (await fs6.pathExists(targetPath)) {
693
+ const targetContent = await fs6.readFile(targetPath, "utf-8");
694
+ if (sourceContent === targetContent) {
695
+ continue;
696
+ }
697
+ if (!force) {
698
+ console.log(
699
+ chalk.yellow(` \u26A0\uFE0F ${file} - \uBCC0\uACBD \uAC10\uC9C0 (--force\uB85C \uB36E\uC5B4\uC4F0\uAE30)`)
700
+ );
701
+ shouldUpdate = false;
702
+ }
703
+ }
704
+ if (shouldUpdate) {
705
+ await fs6.writeFile(targetPath, sourceContent);
706
+ console.log(chalk.gray(` \u{1F4C4} ${file} \uC5C5\uB370\uC774\uD2B8`));
707
+ updatedCount++;
708
+ }
709
+ } else if (stat.isDirectory()) {
710
+ const subCount = await updateFolder(sourcePath, targetPath, force);
711
+ updatedCount += subCount;
712
+ }
713
+ }
714
+ return updatedCount;
715
+ }
613
716
 
614
717
  // src/index.ts
615
718
  program.name("lee-spec-kit").description(
@@ -618,4 +721,5 @@ program.name("lee-spec-kit").description(
618
721
  initCommand(program);
619
722
  featureCommand(program);
620
723
  statusCommand(program);
724
+ updateCommand(program);
621
725
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lee-spec-kit",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Project documentation structure generator for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,13 @@
12
12
  "dist",
13
13
  "templates"
14
14
  ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "lint": "eslint src",
19
+ "format": "prettier --write .",
20
+ "prepublishOnly": "pnpm build"
21
+ },
15
22
  "keywords": [
16
23
  "docs",
17
24
  "template",
@@ -49,10 +56,5 @@
49
56
  "tsup": "^8.5.1",
50
57
  "typescript": "^5.9.3"
51
58
  },
52
- "scripts": {
53
- "build": "tsup",
54
- "dev": "tsup --watch",
55
- "lint": "eslint src",
56
- "format": "prettier --write ."
57
- }
58
- }
59
+ "packageManager": "pnpm@10.7.0"
60
+ }
@@ -4,10 +4,38 @@ Operating rules for AI code assistants to perform consistent code generation and
4
4
 
5
5
  ---
6
6
 
7
+ ## 🚨 User Approval Required (MUST)
8
+
9
+ > ⚠️ **The following actions require explicit user approval (OK) before proceeding.**
10
+ > **If approval is not given, stop immediately and request confirmation.**
11
+
12
+ | Action | When to Confirm | What to Share |
13
+ | --------------------- | ------------------------ | ------------------------- |
14
+ | Spec Writing | After writing `spec.md` | Full spec content |
15
+ | Task Execution | Before each task | Execution plan |
16
+ | Commit Creation | Before `git commit` | Commit message, file list |
17
+ | Issue Creation | Before `gh issue create` | Title, body, labels |
18
+ | PR Creation | Before `gh pr create` | Title, body, labels |
19
+ | Assignee Change | When assigning others | Target username |
20
+ | Remote Git Operations | Before `push`, `merge` | Branch, changes |
21
+
22
+ ### Approval Process
23
+
24
+ 1. **Share** action details with user first
25
+ 2. **Wait** for explicit user approval (OK)
26
+ 3. **Execute** only after approval
27
+
28
+ > 🚫 **Prohibited**: Proceeding without user response
29
+
30
+ ---
31
+
7
32
  ## Reference Documents
8
33
 
9
34
  ### Core Documents
10
35
 
36
+ > ⚠️ **Rules in `custom.md` take precedence over all other rules.**
37
+
38
+ - **🔴 Custom Rules (Highest Priority)**: `/docs/agents/custom.md`
11
39
  - **Project Principles**: `/docs/agents/constitution.md`
12
40
  - **Git Workflow**: `/docs/agents/git-workflow.md`
13
41
  - **Issue Template**: `/docs/agents/issue-template.md`
@@ -69,6 +97,14 @@ docs/
69
97
  3. Transition status: `[TODO]` → `[DOING]` → `[DONE]`
70
98
  4. Commit immediately after task completion
71
99
 
100
+ ### 4. Handling Requests Outside Tasks
101
+
102
+ > When user requests **work not in tasks.md**:
103
+
104
+ 1. Ask user if this should be **added to tasks.md**
105
+ 2. If approved: Add to tasks.md then execute
106
+ 3. If declined: Proceed as temporary work (still included in commit)
107
+
72
108
  ---
73
109
 
74
110
  ## 📋 ADR (Architecture Decision Records)
@@ -0,0 +1,29 @@
1
+ # Custom Rules
2
+
3
+ > ⚠️ This document contains **user-defined rules**.
4
+ > It is NOT affected by `npx lee-spec-kit update`.
5
+ > **Rules in this document take precedence over all other agent rules.**
6
+
7
+ ---
8
+
9
+ ## Project-Specific Rules
10
+
11
+ (Write your project-specific rules here)
12
+
13
+ ---
14
+
15
+ ## Additional Language/Code Rules
16
+
17
+ (Override default rules or add additional rules here)
18
+
19
+ ---
20
+
21
+ ## Custom Workflows
22
+
23
+ (Write project-specific workflows here)
24
+
25
+ ---
26
+
27
+ ## Other
28
+
29
+ (Write other rules here)
@@ -73,14 +73,25 @@ main
73
73
  git checkout -b feat/{issue-number}-{feature-name}
74
74
  ```
75
75
 
76
- ### 2. Auto Commit on Task Completion
76
+ ### 2. Document Commit (docs repo)
77
+
78
+ > 📌 The docs folder is managed as a separate git, so a separate commit strategy is used.
79
+
80
+ | # | Commit Timing | Included Documents | Commit Message Example |
81
+ | --- | -------------------------------------------------------- | ------------------------------- | ------------------------------- |
82
+ | 1 | **When planning is complete** (spec+plan+tasks approved) | spec.md, plan.md, tasks.md | `docs(#123): spec, plan, tasks` |
83
+ | 2 | **When Feature is complete** (all tasks done) | tasks.md (status), decisions.md | `docs(#123): Feature complete` |
84
+
85
+ > ⚠️ **Do not commit when creating Feature folder.**
86
+
87
+ ### 3. Auto Commit on Task Completion
77
88
 
78
89
  ```bash
79
90
  git add .
80
91
  git commit -m "{type}(#{issue}): {task-description}"
81
92
  ```
82
93
 
83
- ### 3. Create PR on Feature Completion
94
+ ### 4. Create PR on Feature Completion
84
95
 
85
96
  ```bash
86
97
  git push origin feat/{issue-number}-{feature-name}
@@ -89,7 +100,7 @@ gh pr create --title "feat(#{issue}): {feature-title}" \
89
100
  --base main
90
101
  ```
91
102
 
92
- ### 4. Merge
103
+ ### 5. Merge
93
104
 
94
105
  ```bash
95
106
  git checkout main
@@ -82,6 +82,24 @@ In GitHub Issues, use different link formats **based on file location**:
82
82
  | `frontend` | FE related |
83
83
  | `priority:high` | High priority |
84
84
 
85
+ > ⚠️ If a label does not exist, create it first:
86
+ >
87
+ > ```bash
88
+ > gh label create "label-name" --description "description" --color "color-code"
89
+ > ```
90
+
91
+ ---
92
+
93
+ ## Assignee Rules
94
+
95
+ - Default: Self-assign (`--assignee @me`)
96
+ - When assigning others, **confirm with user** first
97
+ - Examples:
98
+ ```bash
99
+ gh issue create --assignee @me ...
100
+ gh issue create --assignee username ...
101
+ ```
102
+
85
103
  ---
86
104
 
87
105
  ## Body Input Rules (Shell Execution Prevention)
@@ -67,6 +67,27 @@ Closes #{issue-number}
67
67
 
68
68
  ---
69
69
 
70
+ ## Label Rules
71
+
72
+ - Specify appropriate labels when creating PR (`--label`)
73
+ - If a label does not exist, create it first:
74
+ ```bash
75
+ gh label create "label-name" --description "description" --color "color-code"
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Assignee Rules
81
+
82
+ - Default: Self-assign (`--assignee @me`)
83
+ - Use `--reviewer` option to specify reviewers
84
+ - Examples:
85
+ ```bash
86
+ gh pr create --assignee @me --reviewer reviewer-username ...
87
+ ```
88
+
89
+ ---
90
+
70
91
  ## Body Input Rules (Shell Execution Prevention)
71
92
 
72
93
  - PR body should use **`--body-file` by default**.
@@ -17,6 +17,8 @@
17
17
 
18
18
  ## Purpose
19
19
 
20
+ {{description}}
21
+
20
22
  (Why is this feature needed? What problem does it solve?)
21
23
 
22
24
  ---
@@ -4,10 +4,38 @@ Operating rules for AI code assistants to perform consistent code generation and
4
4
 
5
5
  ---
6
6
 
7
+ ## 🚨 User Approval Required (MUST)
8
+
9
+ > ⚠️ **The following actions require explicit user approval (OK) before proceeding.**
10
+ > **If approval is not given, stop immediately and request confirmation.**
11
+
12
+ | Action | When to Confirm | What to Share |
13
+ | --------------------- | ------------------------ | ------------------------- |
14
+ | Spec Writing | After writing `spec.md` | Full spec content |
15
+ | Task Execution | Before each task | Execution plan |
16
+ | Commit Creation | Before `git commit` | Commit message, file list |
17
+ | Issue Creation | Before `gh issue create` | Title, body, labels |
18
+ | PR Creation | Before `gh pr create` | Title, body, labels |
19
+ | Assignee Change | When assigning others | Target username |
20
+ | Remote Git Operations | Before `push`, `merge` | Branch, changes |
21
+
22
+ ### Approval Process
23
+
24
+ 1. **Share** action details with user first
25
+ 2. **Wait** for explicit user approval (OK)
26
+ 3. **Execute** only after approval
27
+
28
+ > 🚫 **Prohibited**: Proceeding without user response
29
+
30
+ ---
31
+
7
32
  ## Reference Documents
8
33
 
9
34
  ### Core Documents
10
35
 
36
+ > ⚠️ **Rules in `custom.md` take precedence over all other rules.**
37
+
38
+ - **🔴 Custom Rules (Highest Priority)**: `/docs/agents/custom.md`
11
39
  - **Project Principles**: `/docs/agents/constitution.md`
12
40
  - **Git Workflow**: `/docs/agents/git-workflow.md`
13
41
  - **Issue Template**: `/docs/agents/issue-template.md`
@@ -56,6 +84,14 @@ docs/
56
84
  3. Status transition: `[TODO]` → `[DOING]` → `[DONE]`
57
85
  4. Commit on task completion
58
86
 
87
+ ### 3. Handling Requests Outside Tasks
88
+
89
+ > When user requests **work not in tasks.md**:
90
+
91
+ 1. Ask user if this should be **added to tasks.md**
92
+ 2. If approved: Add to tasks.md then execute
93
+ 3. If declined: Proceed as temporary work (still included in commit)
94
+
59
95
  ---
60
96
 
61
97
  ## 📋 ADR (Architecture Decision Records)
@@ -0,0 +1,29 @@
1
+ # Custom Rules
2
+
3
+ > ⚠️ This document contains **user-defined rules**.
4
+ > It is NOT affected by `npx lee-spec-kit update`.
5
+ > **Rules in this document take precedence over all other agent rules.**
6
+
7
+ ---
8
+
9
+ ## Project-Specific Rules
10
+
11
+ (Write your project-specific rules here)
12
+
13
+ ---
14
+
15
+ ## Additional Language/Code Rules
16
+
17
+ (Override default rules or add additional rules here)
18
+
19
+ ---
20
+
21
+ ## Custom Workflows
22
+
23
+ (Write project-specific workflows here)
24
+
25
+ ---
26
+
27
+ ## Other
28
+
29
+ (Write other rules here)
@@ -80,7 +80,18 @@ git checkout -b feat/{issue-number}-{feature-name}
80
80
 
81
81
  > When creating/modifying issues/PRs with `gh`, share the title/body/labels first and **wait for user confirmation (OK)** before proceeding.
82
82
 
83
- ### 2. Auto Commit on Task Completion
83
+ ### 2. Document Commit (docs repo)
84
+
85
+ > 📌 The docs folder is managed as a separate git, so a separate commit strategy is used.
86
+
87
+ | # | Commit Timing | Included Documents | Commit Message Example |
88
+ | --- | -------------------------------------------------------- | ------------------------------- | ------------------------------- |
89
+ | 1 | **When planning is complete** (spec+plan+tasks approved) | spec.md, plan.md, tasks.md | `docs(#123): spec, plan, tasks` |
90
+ | 2 | **When Feature is complete** (all tasks done) | tasks.md (status), decisions.md | `docs(#123): Feature complete` |
91
+
92
+ > ⚠️ **Do not commit when creating Feature folder.**
93
+
94
+ ### 3. Auto Commit on Task Completion
84
95
 
85
96
  When a task is completed:
86
97
 
@@ -91,7 +102,7 @@ git commit -m "{type}(#{issue}): {task-description}"
91
102
 
92
103
  > Before running `git commit`, share the commit message and file list first and **wait for user confirmation (OK)** before proceeding.
93
104
 
94
- ### 3. Create PR on Feature Completion
105
+ ### 4. Create PR on Feature Completion
95
106
 
96
107
  When all tasks are completed:
97
108
 
@@ -102,7 +113,7 @@ gh pr create --title "feat(#{issue}): {feature-title}" \
102
113
  --base main
103
114
  ```
104
115
 
105
- ### 4. Merge
116
+ ### 5. Merge
106
117
 
107
118
  When all reviews are resolved:
108
119
 
@@ -86,6 +86,24 @@ In GitHub Issues, use different link formats **based on file location**:
86
86
  | `frontend` | FE related |
87
87
  | `priority:high` | High priority |
88
88
 
89
+ > ⚠️ If a label does not exist, create it first:
90
+ >
91
+ > ```bash
92
+ > gh label create "label-name" --description "description" --color "color-code"
93
+ > ```
94
+
95
+ ---
96
+
97
+ ## Assignee Rules
98
+
99
+ - Default: Self-assign (`--assignee @me`)
100
+ - When assigning others, **confirm with user** first
101
+ - Examples:
102
+ ```bash
103
+ gh issue create --assignee @me ...
104
+ gh issue create --assignee username ...
105
+ ```
106
+
89
107
  ---
90
108
 
91
109
  ## Body Input Rules (Shell Execution Prevention)
@@ -94,3 +112,5 @@ In GitHub Issues, use different link formats **based on file location**:
94
112
  - If the body contains backticks (`) or `$()`and is placed directly in`"..."`, it may be **interpreted by the shell**.
95
113
  - For multi-line bodies, use **single-quoted heredoc** like `cat <<'EOF'`,
96
114
  and handle variables via **placeholder → sed substitution**.
115
+
116
+ and handle variables via **placeholder → sed substitution**.
@@ -83,6 +83,27 @@ gh pr create \
83
83
 
84
84
  ---
85
85
 
86
+ ## Label Rules
87
+
88
+ - Specify appropriate labels when creating PR (`--label`)
89
+ - If a label does not exist, create it first:
90
+ ```bash
91
+ gh label create "label-name" --description "description" --color "color-code"
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Assignee Rules
97
+
98
+ - Default: Self-assign (`--assignee @me`)
99
+ - Use `--reviewer` option to specify reviewers
100
+ - Examples:
101
+ ```bash
102
+ gh pr create --assignee @me --reviewer reviewer-username ...
103
+ ```
104
+
105
+ ---
106
+
86
107
  ## Body Input Rules (Shell Execution Prevention)
87
108
 
88
109
  - PR body should use **`--body-file` by default**.
@@ -17,6 +17,8 @@
17
17
 
18
18
  ## Purpose
19
19
 
20
+ {{description}}
21
+
20
22
  (Why is this feature needed? What problem does it solve?)
21
23
 
22
24
  ---
@@ -4,10 +4,38 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## 🚨 사용자 확인 필수 규칙 (MUST)
8
+
9
+ > ⚠️ **아래 작업은 반드시 사용자의 명시적 승인(OK)을 받은 후에만 진행합니다.**
10
+ > **확인 없이 진행 시 작업을 즉시 중단해야 합니다.**
11
+
12
+ | 작업 | 확인 시점 | 공유 내용 |
13
+ | ------------- | -------------------- | --------------------------- |
14
+ | 스펙 작성 | `spec.md` 작성 후 | 스펙 내용 전문 |
15
+ | 태스크 실행 | 각 태스크 시작 전 | 실행 계획 |
16
+ | 커밋 생성 | `git commit` 전 | 커밋 메시지, 포함 파일 목록 |
17
+ | 이슈 생성 | `gh issue create` 전 | 제목, 본문, 라벨 |
18
+ | PR 생성 | `gh pr create` 전 | 제목, 본문, 라벨 |
19
+ | Assignee 변경 | 본인 외 지정 시 | 대상 사용자명 |
20
+ | Git 원격 작업 | `push`, `merge` 전 | 브랜치, 변경 사항 |
21
+
22
+ ### 확인 프로세스
23
+
24
+ 1. 작업 내용을 사용자에게 **먼저 공유**
25
+ 2. 사용자의 **명시적 승인(OK)** 대기
26
+ 3. 승인 후에만 실행
27
+
28
+ > 🚫 **금지 사항**: 사용자 응답 없이 임의로 진행하는 것
29
+
30
+ ---
31
+
7
32
  ## 참조 문서
8
33
 
9
34
  ### 핵심 문서
10
35
 
36
+ > ⚠️ **`custom.md`의 규칙은 다른 모든 규칙보다 우선합니다.**
37
+
38
+ - **🔴 커스텀 규칙 (최우선)**: `/docs/agents/custom.md`
11
39
  - **프로젝트 원칙**: `/docs/agents/constitution.md`
12
40
  - **Git 워크플로우**: `/docs/agents/git-workflow.md`
13
41
  - **이슈 템플릿**: `/docs/agents/issue-template.md`
@@ -92,6 +120,14 @@ docs/
92
120
  1. 분석 리포트 작성 (현재 상태, 문제점, 제안, 영향)
93
121
  2. 변경 필요시 새 기능/태스크 생성 권장
94
122
 
123
+ ### 5. 태스크 외 요청 처리
124
+
125
+ > 사용자가 **tasks.md에 없는 작업**을 요청하면:
126
+
127
+ 1. 해당 작업을 **tasks.md에 반영할지** 사용자에게 확인
128
+ 2. 승인 시: tasks.md에 추가 후 실행
129
+ 3. 거부 시: 임시 작업으로 진행 (커밋에는 포함)
130
+
95
131
  ---
96
132
 
97
133
  ## 📋 ADR (Architecture Decision Records) 규칙
@@ -0,0 +1,29 @@
1
+ # Custom Rules
2
+
3
+ > ⚠️ 이 문서는 **사용자 정의 규칙**입니다.
4
+ > `npx lee-spec-kit update`의 영향을 받지 않습니다.
5
+ > **이 문서의 규칙은 다른 모든 agents 규칙보다 우선합니다.**
6
+
7
+ ---
8
+
9
+ ## 프로젝트 특화 규칙
10
+
11
+ (여기에 프로젝트만의 규칙을 작성하세요)
12
+
13
+ ---
14
+
15
+ ## 추가 언어/코드 규칙
16
+
17
+ (기본 규칙을 오버라이드하거나 추가 규칙을 작성하세요)
18
+
19
+ ---
20
+
21
+ ## 커스텀 워크플로우
22
+
23
+ (프로젝트만의 워크플로우가 있다면 작성하세요)
24
+
25
+ ---
26
+
27
+ ## 기타
28
+
29
+ (기타 규칙을 작성하세요)
@@ -80,7 +80,18 @@ git checkout -b feat/{issue-number}-{feature-name}
80
80
 
81
81
  > `gh`로 이슈/PR 생성·수정 시 작성할 제목/본문/라벨을 먼저 공유하고 **반드시** 사용자 확인(OK) 후 진행합니다.
82
82
 
83
- ### 2. 태스크 완료 자동 커밋
83
+ ### 2. 문서 커밋 (docs 레포)
84
+
85
+ > 📌 docs 폴더는 별도 git으로 관리되므로 프로젝트와 분리된 커밋 전략을 사용합니다.
86
+
87
+ | # | 커밋 시점 | 포함 문서 | 커밋 메시지 예시 |
88
+ | --- | ------------------------------------------ | ----------------------------- | ------------------------------------ |
89
+ | 1 | **계획 완료 시** (spec+plan+tasks 승인 후) | spec.md, plan.md, tasks.md | `docs(#123): spec, plan, tasks 작성` |
90
+ | 2 | **Feature 완료 시** (모든 태스크 완료 후) | tasks.md (상태), decisions.md | `docs(#123): Feature 완료` |
91
+
92
+ > ⚠️ **Feature 폴더 생성 시점**에는 커밋하지 않습니다.
93
+
94
+ ### 3. 태스크 완료 시 자동 커밋
84
95
 
85
96
  태스크 하나가 완료되면:
86
97
 
@@ -91,7 +102,7 @@ git commit -m "{type}(#{issue}): {task-description}"
91
102
 
92
103
  > `git commit` 실행 전 커밋 메시지와 포함될 파일 목록을 먼저 공유하고 **반드시** 사용자 확인(OK) 후 진행합니다.
93
104
 
94
- ### 3. Feature 완료 시 PR 생성
105
+ ### 4. Feature 완료 시 PR 생성
95
106
 
96
107
  모든 태스크 완료 시:
97
108
 
@@ -102,7 +113,7 @@ gh pr create --title "feat(#{issue}): {feature-title}" \
102
113
  --base main
103
114
  ```
104
115
 
105
- ### 4. 머지
116
+ ### 5. 머지
106
117
 
107
118
  모든 리뷰 해결 시:
108
119
 
@@ -86,6 +86,24 @@ GitHub Issue에서 링크는 **파일 위치에 따라** 다르게 작성:
86
86
  | `frontend` | FE 관련 |
87
87
  | `priority:high` | 높은 우선순위 |
88
88
 
89
+ > ⚠️ 라벨이 존재하지 않으면 먼저 생성합니다:
90
+ >
91
+ > ```bash
92
+ > gh label create "라벨명" --description "설명" --color "색상코드"
93
+ > ```
94
+
95
+ ---
96
+
97
+ ## Assignee 규칙
98
+
99
+ - 기본값: 본인 할당 (`--assignee @me`)
100
+ - 다른 담당자 지정 시 **사용자에게 확인** 후 진행
101
+ - 예시:
102
+ ```bash
103
+ gh issue create --assignee @me ...
104
+ gh issue create --assignee username ...
105
+ ```
106
+
89
107
  ---
90
108
 
91
109
  ## 본문 입력 규칙 (셸 실행 방지)
@@ -81,6 +81,27 @@ gh pr create \
81
81
 
82
82
  ---
83
83
 
84
+ ## 라벨 규칙
85
+
86
+ - PR 생성 시 적절한 라벨 지정 (`--label`)
87
+ - 라벨이 존재하지 않으면 먼저 생성:
88
+ ```bash
89
+ gh label create "라벨명" --description "설명" --color "색상코드"
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Assignee 규칙
95
+
96
+ - 기본값: 본인 할당 (`--assignee @me`)
97
+ - 리뷰어 지정 시 `--reviewer` 옵션 사용
98
+ - 예시:
99
+ ```bash
100
+ gh pr create --assignee @me --reviewer reviewer-username ...
101
+ ```
102
+
103
+ ---
104
+
84
105
  ## 본문 입력 규칙 (셸 실행 방지)
85
106
 
86
107
  - PR 본문은 **`--body-file` 사용을 기본**으로 한다.
@@ -17,6 +17,8 @@
17
17
 
18
18
  ## 목적
19
19
 
20
+ {{description}}
21
+
20
22
  (이 기능이 왜 필요한지, 어떤 문제를 해결하는지)
21
23
 
22
24
  ---
@@ -4,10 +4,38 @@
4
4
 
5
5
  ---
6
6
 
7
+ ## 🚨 사용자 확인 필수 규칙 (MUST)
8
+
9
+ > ⚠️ **아래 작업은 반드시 사용자의 명시적 승인(OK)을 받은 후에만 진행합니다.**
10
+ > **확인 없이 진행 시 작업을 즉시 중단해야 합니다.**
11
+
12
+ | 작업 | 확인 시점 | 공유 내용 |
13
+ | ------------- | -------------------- | --------------------------- |
14
+ | 스펙 작성 | `spec.md` 작성 후 | 스펙 내용 전문 |
15
+ | 태스크 실행 | 각 태스크 시작 전 | 실행 계획 |
16
+ | 커밋 생성 | `git commit` 전 | 커밋 메시지, 포함 파일 목록 |
17
+ | 이슈 생성 | `gh issue create` 전 | 제목, 본문, 라벨 |
18
+ | PR 생성 | `gh pr create` 전 | 제목, 본문, 라벨 |
19
+ | Assignee 변경 | 본인 외 지정 시 | 대상 사용자명 |
20
+ | Git 원격 작업 | `push`, `merge` 전 | 브랜치, 변경 사항 |
21
+
22
+ ### 확인 프로세스
23
+
24
+ 1. 작업 내용을 사용자에게 **먼저 공유**
25
+ 2. 사용자의 **명시적 승인(OK)** 대기
26
+ 3. 승인 후에만 실행
27
+
28
+ > 🚫 **금지 사항**: 사용자 응답 없이 임의로 진행하는 것
29
+
30
+ ---
31
+
7
32
  ## 참조 문서
8
33
 
9
34
  ### 핵심 문서
10
35
 
36
+ > ⚠️ **`custom.md`의 규칙은 다른 모든 규칙보다 우선합니다.**
37
+
38
+ - **🔴 커스텀 규칙 (최우선)**: `/docs/agents/custom.md`
11
39
  - **프로젝트 원칙**: `/docs/agents/constitution.md`
12
40
  - **Git 워크플로우**: `/docs/agents/git-workflow.md`
13
41
  - **이슈 템플릿**: `/docs/agents/issue-template.md`
@@ -69,6 +97,14 @@ docs/
69
97
  3. 상태 전환: `[TODO]` → `[DOING]` → `[DONE]`
70
98
  4. 태스크 완료 직후 커밋
71
99
 
100
+ ### 3. 태스크 외 요청 처리
101
+
102
+ > 사용자가 **tasks.md에 없는 작업**을 요청하면:
103
+
104
+ 1. 해당 작업을 **tasks.md에 반영할지** 사용자에게 확인
105
+ 2. 승인 시: tasks.md에 추가 후 실행
106
+ 3. 거부 시: 임시 작업으로 진행 (커밋에는 포함)
107
+
72
108
  ---
73
109
 
74
110
  ## 📋 ADR (Architecture Decision Records) 규칙
@@ -0,0 +1,29 @@
1
+ # Custom Rules
2
+
3
+ > ⚠️ 이 문서는 **사용자 정의 규칙**입니다.
4
+ > `npx lee-spec-kit update`의 영향을 받지 않습니다.
5
+ > **이 문서의 규칙은 다른 모든 agents 규칙보다 우선합니다.**
6
+
7
+ ---
8
+
9
+ ## 프로젝트 특화 규칙
10
+
11
+ (여기에 프로젝트만의 규칙을 작성하세요)
12
+
13
+ ---
14
+
15
+ ## 추가 언어/코드 규칙
16
+
17
+ (기본 규칙을 오버라이드하거나 추가 규칙을 작성하세요)
18
+
19
+ ---
20
+
21
+ ## 커스텀 워크플로우
22
+
23
+ (프로젝트만의 워크플로우가 있다면 작성하세요)
24
+
25
+ ---
26
+
27
+ ## 기타
28
+
29
+ (기타 규칙을 작성하세요)
@@ -80,7 +80,18 @@ git checkout -b feat/{issue-number}-{feature-name}
80
80
 
81
81
  > `gh`로 이슈/PR 생성·수정 시 작성할 제목/본문/라벨을 먼저 공유하고 **반드시** 사용자 확인(OK) 후 진행합니다.
82
82
 
83
- ### 2. 태스크 완료 자동 커밋
83
+ ### 2. 문서 커밋 (docs 레포)
84
+
85
+ > 📌 docs 폴더는 별도 git으로 관리되므로 프로젝트와 분리된 커밋 전략을 사용합니다.
86
+
87
+ | # | 커밋 시점 | 포함 문서 | 커밋 메시지 예시 |
88
+ | --- | ------------------------------------------ | ----------------------------- | ------------------------------------ |
89
+ | 1 | **계획 완료 시** (spec+plan+tasks 승인 후) | spec.md, plan.md, tasks.md | `docs(#123): spec, plan, tasks 작성` |
90
+ | 2 | **Feature 완료 시** (모든 태스크 완료 후) | tasks.md (상태), decisions.md | `docs(#123): Feature 완료` |
91
+
92
+ > ⚠️ **Feature 폴더 생성 시점**에는 커밋하지 않습니다.
93
+
94
+ ### 3. 태스크 완료 시 자동 커밋
84
95
 
85
96
  태스크 하나가 완료되면:
86
97
 
@@ -91,7 +102,7 @@ git commit -m "{type}(#{issue}): {task-description}"
91
102
 
92
103
  > `git commit` 실행 전 커밋 메시지와 포함될 파일 목록을 먼저 공유하고 **반드시** 사용자 확인(OK) 후 진행합니다.
93
104
 
94
- ### 3. Feature 완료 시 PR 생성
105
+ ### 4. Feature 완료 시 PR 생성
95
106
 
96
107
  모든 태스크 완료 시:
97
108
 
@@ -102,7 +113,7 @@ gh pr create --title "feat(#{issue}): {feature-title}" \
102
113
  --base main
103
114
  ```
104
115
 
105
- ### 4. 머지
116
+ ### 5. 머지
106
117
 
107
118
  모든 리뷰 해결 시:
108
119
 
@@ -86,6 +86,24 @@ GitHub Issue에서 링크는 **파일 위치에 따라** 다르게 작성:
86
86
  | `frontend` | FE 관련 |
87
87
  | `priority:high` | 높은 우선순위 |
88
88
 
89
+ > ⚠️ 라벨이 존재하지 않으면 먼저 생성합니다:
90
+ >
91
+ > ```bash
92
+ > gh label create "라벨명" --description "설명" --color "색상코드"
93
+ > ```
94
+
95
+ ---
96
+
97
+ ## Assignee 규칙
98
+
99
+ - 기본값: 본인 할당 (`--assignee @me`)
100
+ - 다른 담당자 지정 시 **사용자에게 확인** 후 진행
101
+ - 예시:
102
+ ```bash
103
+ gh issue create --assignee @me ...
104
+ gh issue create --assignee username ...
105
+ ```
106
+
89
107
  ---
90
108
 
91
109
  ## 본문 입력 규칙 (셸 실행 방지)
@@ -81,6 +81,27 @@ gh pr create \
81
81
 
82
82
  ---
83
83
 
84
+ ## 라벨 규칙
85
+
86
+ - PR 생성 시 적절한 라벨 지정 (`--label`)
87
+ - 라벨이 존재하지 않으면 먼저 생성:
88
+ ```bash
89
+ gh label create "라벨명" --description "설명" --color "색상코드"
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Assignee 규칙
95
+
96
+ - 기본값: 본인 할당 (`--assignee @me`)
97
+ - 리뷰어 지정 시 `--reviewer` 옵션 사용
98
+ - 예시:
99
+ ```bash
100
+ gh pr create --assignee @me --reviewer reviewer-username ...
101
+ ```
102
+
103
+ ---
104
+
84
105
  ## 본문 입력 규칙 (셸 실행 방지)
85
106
 
86
107
  - PR 본문은 **`--body-file` 사용을 기본**으로 한다.
@@ -17,6 +17,8 @@
17
17
 
18
18
  ## 목적
19
19
 
20
+ {{description}}
21
+
20
22
  (이 기능이 왜 필요한지, 어떤 문제를 해결하는지)
21
23
 
22
24
  ---