lee-spec-kit 0.1.8 → 0.2.0
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 +74 -48
- package/package.json +1 -1
- package/templates/en/{fullstack → common}/agents/git-workflow.md +26 -34
- package/templates/en/{single → common}/agents/issue-template.md +10 -5
- package/templates/en/{single → common}/agents/pr-template.md +9 -2
- package/templates/en/common/agents/skills/create-feature.md +53 -0
- package/templates/en/common/agents/skills/create-issue.md +52 -0
- package/templates/en/common/agents/skills/create-pr.md +97 -0
- package/templates/en/common/agents/skills/execute-task.md +86 -0
- package/templates/en/fullstack/agents/agents.md +11 -26
- package/templates/en/single/agents/agents.md +8 -21
- package/templates/ko/{fullstack → common}/agents/git-workflow.md +20 -73
- package/templates/ko/{fullstack → common}/agents/issue-template.md +10 -5
- package/templates/ko/{fullstack → common}/agents/pr-template.md +9 -2
- package/templates/ko/common/agents/skills/create-feature.md +53 -0
- package/templates/ko/common/agents/skills/create-issue.md +52 -0
- package/templates/ko/common/agents/skills/create-pr.md +97 -0
- package/templates/ko/common/agents/skills/execute-task.md +86 -0
- package/templates/ko/fullstack/agents/agents.md +11 -33
- package/templates/ko/single/agents/agents.md +13 -21
- package/templates/en/fullstack/agents/constitution.md +0 -80
- package/templates/en/fullstack/agents/issue-template.md +0 -110
- package/templates/en/fullstack/agents/pr-template.md +0 -96
- package/templates/en/single/agents/custom.md +0 -29
- package/templates/en/single/agents/git-workflow.md +0 -170
- package/templates/ko/single/agents/constitution.md +0 -80
- package/templates/ko/single/agents/custom.md +0 -29
- package/templates/ko/single/agents/git-workflow.md +0 -170
- package/templates/ko/single/agents/issue-template.md +0 -114
- package/templates/ko/single/agents/pr-template.md +0 -110
- /package/templates/en/{single → common}/agents/constitution.md +0 -0
- /package/templates/en/{fullstack → common}/agents/custom.md +0 -0
- /package/templates/ko/{fullstack → common}/agents/constitution.md +0 -0
- /package/templates/ko/{fullstack → common}/agents/custom.md +0 -0
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { program } from 'commander';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
|
-
import
|
|
5
|
+
import path6 from 'path';
|
|
6
6
|
import fs6 from 'fs-extra';
|
|
7
7
|
import { glob } from 'glob';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
@@ -32,10 +32,10 @@ async function replaceInFiles(dir, replacements) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
35
|
-
var __dirname2 =
|
|
35
|
+
var __dirname2 = path6.dirname(__filename2);
|
|
36
36
|
function getTemplatesDir() {
|
|
37
|
-
const rootDir =
|
|
38
|
-
return
|
|
37
|
+
const rootDir = path6.resolve(__dirname2, "..");
|
|
38
|
+
return path6.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 =
|
|
153
|
+
const defaultName = path6.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 =
|
|
157
|
+
const targetDir = path6.resolve(cwd, options.dir || "./docs");
|
|
158
158
|
if (!options.yes) {
|
|
159
159
|
const response = await prompts(
|
|
160
160
|
[
|
|
@@ -232,14 +232,20 @@ async function runInit(options) {
|
|
|
232
232
|
console.log(chalk.gray(` \uACBD\uB85C: ${targetDir}`));
|
|
233
233
|
console.log();
|
|
234
234
|
const templatesDir = getTemplatesDir();
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
235
|
+
const commonPath = path6.join(templatesDir, lang, "common");
|
|
236
|
+
const typePath = path6.join(templatesDir, lang, projectType);
|
|
237
|
+
if (await fs6.pathExists(commonPath)) {
|
|
238
|
+
await copyTemplates(commonPath, targetDir);
|
|
238
239
|
}
|
|
239
|
-
await
|
|
240
|
+
if (!await fs6.pathExists(typePath)) {
|
|
241
|
+
throw new Error(`\uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${typePath}`);
|
|
242
|
+
}
|
|
243
|
+
await copyTemplates(typePath, targetDir);
|
|
244
|
+
const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
|
|
240
245
|
const replacements = {
|
|
241
246
|
"{{projectName}}": projectName,
|
|
242
|
-
"{{date}}": (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
247
|
+
"{{date}}": (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
248
|
+
"{{featurePath}}": featurePath
|
|
243
249
|
};
|
|
244
250
|
await replaceInFiles(targetDir, replacements);
|
|
245
251
|
const config = {
|
|
@@ -248,7 +254,7 @@ async function runInit(options) {
|
|
|
248
254
|
lang,
|
|
249
255
|
createdAt: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
250
256
|
};
|
|
251
|
-
const configPath =
|
|
257
|
+
const configPath = path6.join(targetDir, ".lee-spec-kit.json");
|
|
252
258
|
await fs6.writeJson(configPath, config, { spaces: 2 });
|
|
253
259
|
console.log(chalk.green("\u2705 docs \uAD6C\uC870 \uC0DD\uC131 \uC644\uB8CC!"));
|
|
254
260
|
console.log();
|
|
@@ -273,7 +279,7 @@ async function initGit(cwd, targetDir) {
|
|
|
273
279
|
console.log(chalk.blue("\u{1F4E6} Git \uCD08\uAE30\uD654 \uC911..."));
|
|
274
280
|
execSync("git init", { cwd, stdio: "ignore" });
|
|
275
281
|
}
|
|
276
|
-
const relativePath =
|
|
282
|
+
const relativePath = path6.relative(cwd, targetDir);
|
|
277
283
|
execSync(`git add "${relativePath}"`, { cwd, stdio: "ignore" });
|
|
278
284
|
execSync('git commit -m "init: docs \uAD6C\uC870 \uCD08\uAE30\uD654 (lee-spec-kit)"', {
|
|
279
285
|
cwd,
|
|
@@ -290,12 +296,12 @@ async function initGit(cwd, targetDir) {
|
|
|
290
296
|
}
|
|
291
297
|
async function getConfig(cwd) {
|
|
292
298
|
const possibleDirs = [
|
|
293
|
-
|
|
299
|
+
path6.join(cwd, "docs"),
|
|
294
300
|
cwd
|
|
295
301
|
// 이미 docs 폴더 안에 있을 수 있음
|
|
296
302
|
];
|
|
297
303
|
for (const docsDir of possibleDirs) {
|
|
298
|
-
const configPath =
|
|
304
|
+
const configPath = path6.join(docsDir, ".lee-spec-kit.json");
|
|
299
305
|
if (await fs6.pathExists(configPath)) {
|
|
300
306
|
try {
|
|
301
307
|
const configFile = await fs6.readJson(configPath);
|
|
@@ -308,13 +314,13 @@ async function getConfig(cwd) {
|
|
|
308
314
|
} catch {
|
|
309
315
|
}
|
|
310
316
|
}
|
|
311
|
-
const agentsPath =
|
|
312
|
-
const featuresPath =
|
|
317
|
+
const agentsPath = path6.join(docsDir, "agents");
|
|
318
|
+
const featuresPath = path6.join(docsDir, "features");
|
|
313
319
|
if (await fs6.pathExists(agentsPath) && await fs6.pathExists(featuresPath)) {
|
|
314
|
-
const bePath =
|
|
315
|
-
const fePath =
|
|
320
|
+
const bePath = path6.join(featuresPath, "be");
|
|
321
|
+
const fePath = path6.join(featuresPath, "fe");
|
|
316
322
|
const projectType = await fs6.pathExists(bePath) || await fs6.pathExists(fePath) ? "fullstack" : "single";
|
|
317
|
-
const agentsMdPath =
|
|
323
|
+
const agentsMdPath = path6.join(agentsPath, "agents.md");
|
|
318
324
|
let lang = "ko";
|
|
319
325
|
if (await fs6.pathExists(agentsMdPath)) {
|
|
320
326
|
const content = await fs6.readFile(agentsMdPath, "utf-8");
|
|
@@ -386,17 +392,17 @@ async function runFeature(name, options) {
|
|
|
386
392
|
}
|
|
387
393
|
let featuresDir;
|
|
388
394
|
if (projectType === "fullstack" && repo) {
|
|
389
|
-
featuresDir =
|
|
395
|
+
featuresDir = path6.join(docsDir, "features", repo);
|
|
390
396
|
} else {
|
|
391
|
-
featuresDir =
|
|
397
|
+
featuresDir = path6.join(docsDir, "features");
|
|
392
398
|
}
|
|
393
399
|
const featureFolderName = `${featureId}-${name}`;
|
|
394
|
-
const featureDir =
|
|
400
|
+
const featureDir = path6.join(featuresDir, featureFolderName);
|
|
395
401
|
if (await fs6.pathExists(featureDir)) {
|
|
396
402
|
console.error(chalk.red(`\uC774\uBBF8 \uC874\uC7AC\uD558\uB294 \uD3F4\uB354\uC785\uB2C8\uB2E4: ${featureDir}`));
|
|
397
403
|
process.exit(1);
|
|
398
404
|
}
|
|
399
|
-
const featureBasePath =
|
|
405
|
+
const featureBasePath = path6.join(docsDir, "features", "feature-base");
|
|
400
406
|
if (!await fs6.pathExists(featureBasePath)) {
|
|
401
407
|
console.error(chalk.red("feature-base \uD15C\uD50C\uB9BF\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
402
408
|
process.exit(1);
|
|
@@ -432,12 +438,12 @@ async function runFeature(name, options) {
|
|
|
432
438
|
console.log();
|
|
433
439
|
}
|
|
434
440
|
async function getNextFeatureId(docsDir, projectType) {
|
|
435
|
-
const featuresDir =
|
|
441
|
+
const featuresDir = path6.join(docsDir, "features");
|
|
436
442
|
let max = 0;
|
|
437
443
|
const scanDirs = [];
|
|
438
444
|
if (projectType === "fullstack") {
|
|
439
|
-
scanDirs.push(
|
|
440
|
-
scanDirs.push(
|
|
445
|
+
scanDirs.push(path6.join(featuresDir, "be"));
|
|
446
|
+
scanDirs.push(path6.join(featuresDir, "fe"));
|
|
441
447
|
} else {
|
|
442
448
|
scanDirs.push(featuresDir);
|
|
443
449
|
}
|
|
@@ -477,20 +483,20 @@ async function runStatus(options) {
|
|
|
477
483
|
process.exit(1);
|
|
478
484
|
}
|
|
479
485
|
const { docsDir, projectType } = config;
|
|
480
|
-
const featuresDir =
|
|
486
|
+
const featuresDir = path6.join(docsDir, "features");
|
|
481
487
|
const features = [];
|
|
482
488
|
const idMap = /* @__PURE__ */ new Map();
|
|
483
489
|
const scopes = projectType === "fullstack" ? ["be", "fe"] : [""];
|
|
484
490
|
for (const scope of scopes) {
|
|
485
|
-
const scanDir = scope ?
|
|
491
|
+
const scanDir = scope ? path6.join(featuresDir, scope) : featuresDir;
|
|
486
492
|
if (!await fs6.pathExists(scanDir)) continue;
|
|
487
493
|
const entries = await fs6.readdir(scanDir, { withFileTypes: true });
|
|
488
494
|
for (const entry of entries) {
|
|
489
495
|
if (!entry.isDirectory()) continue;
|
|
490
496
|
if (entry.name === "feature-base") continue;
|
|
491
|
-
const featureDir =
|
|
492
|
-
const specPath =
|
|
493
|
-
const tasksPath =
|
|
497
|
+
const featureDir = path6.join(scanDir, entry.name);
|
|
498
|
+
const specPath = path6.join(featureDir, "spec.md");
|
|
499
|
+
const tasksPath = path6.join(featureDir, "tasks.md");
|
|
494
500
|
if (!await fs6.pathExists(specPath)) continue;
|
|
495
501
|
if (!await fs6.pathExists(tasksPath)) continue;
|
|
496
502
|
const specContent = await fs6.readFile(specPath, "utf-8");
|
|
@@ -499,7 +505,7 @@ async function runStatus(options) {
|
|
|
499
505
|
const name = extractSpecValue(specContent, "\uAE30\uB2A5\uBA85") || extractSpecValue(specContent, "Feature Name") || entry.name;
|
|
500
506
|
const repo = extractSpecValue(specContent, "\uB300\uC0C1 \uB808\uD3EC") || extractSpecValue(specContent, "Target Repo") || (scope ? `{{projectName}}-${scope}` : "{{projectName}}");
|
|
501
507
|
const issue = extractSpecValue(specContent, "\uC774\uC288 \uBC88\uD638") || extractSpecValue(specContent, "Issue Number") || "-";
|
|
502
|
-
const relPath =
|
|
508
|
+
const relPath = path6.relative(docsDir, featureDir);
|
|
503
509
|
if (!idMap.has(id)) {
|
|
504
510
|
idMap.set(id, []);
|
|
505
511
|
}
|
|
@@ -569,7 +575,7 @@ async function runStatus(options) {
|
|
|
569
575
|
}
|
|
570
576
|
console.log();
|
|
571
577
|
if (options.write) {
|
|
572
|
-
const outputPath =
|
|
578
|
+
const outputPath = path6.join(featuresDir, "status.md");
|
|
573
579
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
574
580
|
const content = [
|
|
575
581
|
"# Feature Status",
|
|
@@ -636,7 +642,7 @@ async function runUpdate(options) {
|
|
|
636
642
|
}
|
|
637
643
|
const { docsDir, projectType, lang } = config;
|
|
638
644
|
const templatesDir = getTemplatesDir();
|
|
639
|
-
const sourceDir =
|
|
645
|
+
const sourceDir = path6.join(templatesDir, lang, projectType);
|
|
640
646
|
const updateAgents = options.agents || !options.agents && !options.templates;
|
|
641
647
|
const updateTemplates = options.templates || !options.agents && !options.templates;
|
|
642
648
|
console.log(chalk.blue("\u{1F4E6} \uD15C\uD50C\uB9BF \uC5C5\uB370\uC774\uD2B8\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4..."));
|
|
@@ -646,22 +652,32 @@ async function runUpdate(options) {
|
|
|
646
652
|
let updatedCount = 0;
|
|
647
653
|
if (updateAgents) {
|
|
648
654
|
console.log(chalk.blue("\u{1F4C1} agents/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
|
|
655
|
+
const commonAgents = path6.join(templatesDir, lang, "common", "agents");
|
|
656
|
+
const typeAgents = path6.join(templatesDir, lang, projectType, "agents");
|
|
657
|
+
const targetAgents = path6.join(docsDir, "agents");
|
|
658
|
+
const featurePath = projectType === "fullstack" ? "docs/features/{be|fe}" : "docs/features";
|
|
659
|
+
const replacements = {
|
|
660
|
+
"{{featurePath}}": featurePath
|
|
661
|
+
};
|
|
662
|
+
if (await fs6.pathExists(commonAgents)) {
|
|
652
663
|
const count = await updateFolder(
|
|
653
|
-
|
|
664
|
+
commonAgents,
|
|
654
665
|
targetAgents,
|
|
655
|
-
options.force
|
|
666
|
+
options.force,
|
|
667
|
+
replacements
|
|
656
668
|
);
|
|
657
669
|
updatedCount += count;
|
|
658
|
-
console.log(chalk.green(` \u2705 ${count}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC`));
|
|
659
670
|
}
|
|
671
|
+
if (await fs6.pathExists(typeAgents)) {
|
|
672
|
+
const count = await updateFolder(typeAgents, targetAgents, options.force);
|
|
673
|
+
updatedCount += count;
|
|
674
|
+
}
|
|
675
|
+
console.log(chalk.green(` \u2705 agents/ \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC`));
|
|
660
676
|
}
|
|
661
677
|
if (updateTemplates) {
|
|
662
678
|
console.log(chalk.blue("\u{1F4C1} features/feature-base/ \uD3F4\uB354 \uC5C5\uB370\uC774\uD2B8 \uC911..."));
|
|
663
|
-
const sourceFeatureBase =
|
|
664
|
-
const targetFeatureBase =
|
|
679
|
+
const sourceFeatureBase = path6.join(sourceDir, "features", "feature-base");
|
|
680
|
+
const targetFeatureBase = path6.join(docsDir, "features", "feature-base");
|
|
665
681
|
if (await fs6.pathExists(sourceFeatureBase)) {
|
|
666
682
|
const count = await updateFolder(
|
|
667
683
|
sourceFeatureBase,
|
|
@@ -675,19 +691,24 @@ async function runUpdate(options) {
|
|
|
675
691
|
console.log();
|
|
676
692
|
console.log(chalk.green(`\u2705 \uCD1D ${updatedCount}\uAC1C \uD30C\uC77C \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!`));
|
|
677
693
|
}
|
|
678
|
-
async function updateFolder(sourceDir, targetDir, force) {
|
|
694
|
+
async function updateFolder(sourceDir, targetDir, force, replacements) {
|
|
679
695
|
await fs6.ensureDir(targetDir);
|
|
680
696
|
const files = await fs6.readdir(sourceDir);
|
|
681
697
|
let updatedCount = 0;
|
|
682
698
|
for (const file of files) {
|
|
683
|
-
const sourcePath =
|
|
684
|
-
const targetPath =
|
|
699
|
+
const sourcePath = path6.join(sourceDir, file);
|
|
700
|
+
const targetPath = path6.join(targetDir, file);
|
|
685
701
|
const stat = await fs6.stat(sourcePath);
|
|
686
702
|
if (stat.isFile()) {
|
|
687
703
|
if (file === "custom.md") {
|
|
688
704
|
continue;
|
|
689
705
|
}
|
|
690
|
-
|
|
706
|
+
let sourceContent = await fs6.readFile(sourcePath, "utf-8");
|
|
707
|
+
if (replacements) {
|
|
708
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
709
|
+
sourceContent = sourceContent.replaceAll(key, value);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
691
712
|
let shouldUpdate = true;
|
|
692
713
|
if (await fs6.pathExists(targetPath)) {
|
|
693
714
|
const targetContent = await fs6.readFile(targetPath, "utf-8");
|
|
@@ -707,7 +728,12 @@ async function updateFolder(sourceDir, targetDir, force) {
|
|
|
707
728
|
updatedCount++;
|
|
708
729
|
}
|
|
709
730
|
} else if (stat.isDirectory()) {
|
|
710
|
-
const subCount = await updateFolder(
|
|
731
|
+
const subCount = await updateFolder(
|
|
732
|
+
sourcePath,
|
|
733
|
+
targetPath,
|
|
734
|
+
force,
|
|
735
|
+
replacements
|
|
736
|
+
);
|
|
711
737
|
updatedCount += subCount;
|
|
712
738
|
}
|
|
713
739
|
}
|
package/package.json
CHANGED
|
@@ -37,6 +37,11 @@ main
|
|
|
37
37
|
| `refactor` | Refactoring |
|
|
38
38
|
| `docs` | Documentation |
|
|
39
39
|
|
|
40
|
+
**Examples:**
|
|
41
|
+
|
|
42
|
+
- `feat/123-user-auth`
|
|
43
|
+
- `fix/456-login-error`
|
|
44
|
+
|
|
40
45
|
---
|
|
41
46
|
|
|
42
47
|
## Commit Convention
|
|
@@ -65,49 +70,36 @@ main
|
|
|
65
70
|
|
|
66
71
|
## Automation Workflow
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
# 1. Create GitHub Issue (Feature = Issue)
|
|
72
|
-
# 2. Create branch
|
|
73
|
-
git checkout -b feat/{issue-number}-{feature-name}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 2. Document Commit (docs repo)
|
|
77
|
-
|
|
78
|
-
> 📌 The docs folder is managed as a separate git, so a separate commit strategy is used.
|
|
73
|
+
> 📖 Refer to `skills/` folder for step-by-step guides.
|
|
79
74
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
75
|
+
| Workflow | Guide |
|
|
76
|
+
| -------------- | -------------------------- |
|
|
77
|
+
| Feature Start | `skills/create-feature.md` |
|
|
78
|
+
| Issue Creation | `skills/create-issue.md` |
|
|
79
|
+
| Task Execution | `skills/execute-task.md` |
|
|
80
|
+
| PR Creation | `skills/create-pr.md` |
|
|
84
81
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
### 3. Auto Commit on Task Completion
|
|
82
|
+
### Branch Creation
|
|
88
83
|
|
|
89
84
|
```bash
|
|
90
|
-
git
|
|
91
|
-
git commit -m "{type}(#{issue}): {task-description}"
|
|
85
|
+
git checkout -b feat/{issue-number}-{feature-name}
|
|
92
86
|
```
|
|
93
87
|
|
|
94
|
-
###
|
|
88
|
+
### Document Commit Timing (docs repo)
|
|
95
89
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
--base main
|
|
101
|
-
```
|
|
90
|
+
| Commit Timing | Included Content | Commit Message Example |
|
|
91
|
+
| ------------------------------------------------- | ----------------------------------- | --------------------------------------------- |
|
|
92
|
+
| When planning complete (spec+plan+tasks approved) | `F{number}-{feature-name}/` folder | `docs(#{issue}): F{number} spec, plan, tasks` |
|
|
93
|
+
| When Feature complete (all tasks done) | `F{number}-{feature-name}/` changes | `docs(#{issue}): F{number} Feature complete` |
|
|
102
94
|
|
|
103
|
-
|
|
95
|
+
> ⚠️ Do not commit when creating Feature folder.
|
|
104
96
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
### Merge Strategy
|
|
98
|
+
|
|
99
|
+
| Situation | Merge Method |
|
|
100
|
+
| -------------- | ---------------- |
|
|
101
|
+
| Normal Feature | Squash and Merge |
|
|
102
|
+
| Urgent Hotfix | Squash and Merge |
|
|
111
103
|
|
|
112
104
|
---
|
|
113
105
|
|
|
@@ -36,12 +36,17 @@ In GitHub Issues, use different link formats **based on file location**:
|
|
|
36
36
|
[react-i18next](https://react.i18next.com/)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
3. **
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
3. **Local documents** (no URL available): **Path from project root**
|
|
40
|
+
|
|
41
|
+
> 📁 Local documents use paths **from project root**.
|
|
42
|
+
> Format: `- **{Label}**: \`{path}\``
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
- **Spec**: `{{featurePath}}/F001-feature-name/spec.md`
|
|
46
|
+
- **Tasks**: `{{featurePath}}/F001-feature-name/tasks.md`
|
|
42
47
|
```
|
|
43
48
|
|
|
44
|
-
> ⚠️ Local documents are not clickable on GitHub, so
|
|
49
|
+
> ⚠️ Local documents are not clickable on GitHub, so use **bold label + code block path** format instead of markdown links.
|
|
45
50
|
|
|
46
51
|
---
|
|
47
52
|
|
|
@@ -64,7 +69,7 @@ In GitHub Issues, use different link formats **based on file location**:
|
|
|
64
69
|
|
|
65
70
|
## Related Documents
|
|
66
71
|
|
|
67
|
-
- Spec
|
|
72
|
+
- **Spec**: `{{featurePath}}/F{number}-{feature-name}/spec.md`
|
|
68
73
|
|
|
69
74
|
## Labels
|
|
70
75
|
|
|
@@ -42,17 +42,24 @@ For file links within the repo in PR body, **always use current branch name**:
|
|
|
42
42
|
|
|
43
43
|
## Tests
|
|
44
44
|
|
|
45
|
+
> ⚠️ **Check only after running tests. Do NOT check items that were not executed.**
|
|
46
|
+
|
|
45
47
|
- [ ] Unit tests passed
|
|
46
48
|
- [ ] Integration tests completed
|
|
47
49
|
|
|
50
|
+
### Execution Results
|
|
51
|
+
|
|
52
|
+
- Command: `{test command executed}`
|
|
53
|
+
- Result: `{PASS/FAIL summary}`
|
|
54
|
+
|
|
48
55
|
## Screenshots (for UI changes)
|
|
49
56
|
|
|
50
57
|
{Attach if applicable}
|
|
51
58
|
|
|
52
59
|
## Related Documents
|
|
53
60
|
|
|
54
|
-
- Spec
|
|
55
|
-
- Tasks
|
|
61
|
+
- **Spec**: `{{featurePath}}/F{number}-{feature-name}/spec.md`
|
|
62
|
+
- **Tasks**: `{{featurePath}}/F{number}-{feature-name}/tasks.md`
|
|
56
63
|
|
|
57
64
|
Closes #{issue-number}
|
|
58
65
|
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# New Feature Creation Process
|
|
2
|
+
|
|
3
|
+
Step-by-step guide for adding a new Feature.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Steps
|
|
8
|
+
|
|
9
|
+
### 1. Create Feature Folder
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx lee-spec-kit feature <name> -d "<description>"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `<name>`: Feature name (lowercase, hyphens allowed)
|
|
16
|
+
- `-d`: Feature description (auto-filled in spec.md)
|
|
17
|
+
|
|
18
|
+
**Example:**
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx lee-spec-kit feature user-auth -d "User authentication and session management"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Write spec.md
|
|
25
|
+
|
|
26
|
+
- **What**: Clearly describe what the feature does
|
|
27
|
+
- **Why**: Explain why this feature is needed
|
|
28
|
+
- ❌ Do NOT include tech stack (covered in plan.md)
|
|
29
|
+
|
|
30
|
+
### 3. Request User Approval
|
|
31
|
+
|
|
32
|
+
> 🚨 **User Approval Required**
|
|
33
|
+
|
|
34
|
+
Share full spec.md content with user and wait for **explicit approval (OK)**
|
|
35
|
+
|
|
36
|
+
### 4. Create GitHub Issue
|
|
37
|
+
|
|
38
|
+
→ See `skills/create-issue.md`
|
|
39
|
+
|
|
40
|
+
### 5. Pre-Commit Checklist
|
|
41
|
+
|
|
42
|
+
> ⚠️ **Before committing, verify:**
|
|
43
|
+
|
|
44
|
+
- [ ] Issue number in spec.md (`- **Issue Number**: #{issue}`)
|
|
45
|
+
- [ ] Issue number in tasks.md (`- **Issue**: #{issue}`)
|
|
46
|
+
- [ ] Branch name in tasks.md (`feat/{issue-number}-{feature-name}`)
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Reference Documents
|
|
51
|
+
|
|
52
|
+
- **Feature Template**: `features/feature-base/`
|
|
53
|
+
- **Issue Creation Guide**: `skills/create-issue.md`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# GitHub Issue Creation Process
|
|
2
|
+
|
|
3
|
+
Guide for creating GitHub Issues.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- [ ] `spec.md` completed
|
|
10
|
+
- [ ] User approval received
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
### 1. Prepare Issue Content
|
|
17
|
+
|
|
18
|
+
> 📖 **Always refer to `issue-template.md`**
|
|
19
|
+
|
|
20
|
+
| Item | Format |
|
|
21
|
+
| -------- | ------------------------------------------- |
|
|
22
|
+
| Title | `F{number}: {feature-name} ({description})` |
|
|
23
|
+
| Body | Overview, Goals, Criteria, Related docs |
|
|
24
|
+
| Labels | `enhancement`, `bug`, `documentation`, etc. |
|
|
25
|
+
| Assignee | `@me` (default) |
|
|
26
|
+
|
|
27
|
+
### 2. Request User Approval
|
|
28
|
+
|
|
29
|
+
> 🚨 **User Approval Required**
|
|
30
|
+
|
|
31
|
+
Before creating issue, share and wait for approval:
|
|
32
|
+
|
|
33
|
+
- Title
|
|
34
|
+
- Body
|
|
35
|
+
- Labels
|
|
36
|
+
|
|
37
|
+
### 3. Create Issue
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
gh issue create \
|
|
41
|
+
--title "F{number}: {feature-name} ({description})" \
|
|
42
|
+
--body-file /tmp/issue-body.md \
|
|
43
|
+
--assignee @me \
|
|
44
|
+
--label enhancement
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Reference Documents
|
|
50
|
+
|
|
51
|
+
- **Issue Template**: `issue-template.md`
|
|
52
|
+
- **Link Format Rules**: `issue-template.md` > "Link Format" section
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Pull Request Creation Process
|
|
2
|
+
|
|
3
|
+
Guide for creating Pull Requests.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- [ ] All tasks in `[DONE]` state
|
|
10
|
+
- [ ] Changes committed
|
|
11
|
+
- [ ] Branch pushed
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
### 1. Prepare PR Content
|
|
18
|
+
|
|
19
|
+
> 📖 **Always refer to `pr-template.md`**
|
|
20
|
+
|
|
21
|
+
| Item | Format |
|
|
22
|
+
| -------- | ---------------------------------- |
|
|
23
|
+
| Title | `feat(#{issue-number}): {feature}` |
|
|
24
|
+
| Body | Overview, Changes, Tests, Docs |
|
|
25
|
+
| Labels | Appropriate labels |
|
|
26
|
+
| Assignee | `@me` (default) |
|
|
27
|
+
|
|
28
|
+
### 2. Test Verification
|
|
29
|
+
|
|
30
|
+
> 🚨 **Cannot create PR if tests fail**
|
|
31
|
+
|
|
32
|
+
1. Run related test commands (e.g., `npm test`, `pnpm test`)
|
|
33
|
+
2. Check results (PASS/FAIL)
|
|
34
|
+
3. Record **execution results** in PR body "Tests" section
|
|
35
|
+
4. Check boxes **only for items that actually passed**
|
|
36
|
+
|
|
37
|
+
### 3. Request User Approval
|
|
38
|
+
|
|
39
|
+
> 🚨 **User Approval Required**
|
|
40
|
+
|
|
41
|
+
Before creating PR, share and wait for approval:
|
|
42
|
+
|
|
43
|
+
- Title
|
|
44
|
+
- Body (including test results)
|
|
45
|
+
- Labels
|
|
46
|
+
|
|
47
|
+
### 4. Create PR
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gh pr create \
|
|
51
|
+
--title "feat(#{issue-number}): {feature}" \
|
|
52
|
+
--body-file /tmp/pr-body.md \
|
|
53
|
+
--assignee @me \
|
|
54
|
+
--base main
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Important Notes
|
|
60
|
+
|
|
61
|
+
### Link Format
|
|
62
|
+
|
|
63
|
+
Use **current branch name** for file links in PR body:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
[filename](https://github.com/{owner}/{repo}/blob/{branch-name}/path/to/file)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> ⚠️ `main` branch links will return 404 until merged!
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Code Review Modification Guidelines
|
|
74
|
+
|
|
75
|
+
> 📋 **Criteria for deciding whether to add a task when modifications are needed from review feedback**
|
|
76
|
+
|
|
77
|
+
### No task needed (Minor changes)
|
|
78
|
+
|
|
79
|
+
- Typo/code style fixes
|
|
80
|
+
- Variable/function name changes
|
|
81
|
+
- Comment additions/modifications
|
|
82
|
+
- Lint error fixes
|
|
83
|
+
|
|
84
|
+
### Task needed (Major changes)
|
|
85
|
+
|
|
86
|
+
- Logic/algorithm changes
|
|
87
|
+
- New file/function additions
|
|
88
|
+
- API signature changes
|
|
89
|
+
- Test case additions
|
|
90
|
+
- Requires changes to spec.md or plan.md
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Reference Documents
|
|
95
|
+
|
|
96
|
+
- **PR Template**: `pr-template.md`
|
|
97
|
+
- **Git Workflow**: `git-workflow.md`
|