sdd-tool 1.3.0 → 1.3.2

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.md CHANGED
@@ -345,11 +345,17 @@ sdd list # 목록 조회
345
345
  ### 기능 개발
346
346
 
347
347
  ```bash
348
- sdd new <name> # 새 기능 생성
348
+ sdd new <name> # 새 기능 생성 (common 도메인)
349
+ sdd new <name> -d <domain> # 도메인 지정 생성 (v1.3.0)
349
350
  sdd new <name> --all # spec + plan + tasks 모두 생성
350
351
  sdd prepare <name> # 서브에이전트/스킬 점검
351
352
  ```
352
353
 
354
+ **v1.3.0 도메인 기반 구조:**
355
+ - 도메인 미지정 시 `common` 폴더에 생성
356
+ - 경로: `.sdd/specs/<domain>/<feature>/spec.md`
357
+ - 예: `sdd new login -d auth` → `.sdd/specs/auth/login/spec.md`
358
+
353
359
  ### 변경 관리
354
360
 
355
361
  ```bash
@@ -458,17 +464,20 @@ your-project/
458
464
  │ ├── AGENTS.md # AI 워크플로우 가이드
459
465
  │ ├── domains.yml # 도메인 정의 (v1.2.0)
460
466
  │ ├── .context.json # 현재 컨텍스트 (v1.2.0)
461
- │ ├── specs/ # 기능 명세
462
- │ │ ├── feature-name/ # 단순 기능
463
- │ │ │ ├── spec.md
464
- │ │ │ ├── plan.md
465
- │ │ │ └── tasks.md
466
- │ │ └── domain-name/ # 도메인별 그룹 (v1.2.0)
467
- │ │ └── feature/
468
- │ │ └── spec.md
467
+ │ ├── specs/ # 기능 명세 (v1.3.0: 도메인 기반 구조)
468
+ │ │ ├── common/ # 기본 도메인 (도메인 미지정 시)
469
+ │ │ │ └── feature-name/
470
+ │ │ │ ├── spec.md
471
+ │ │ │ ├── plan.md
472
+ │ │ └── tasks.md
473
+ │ │ └── auth/ # 도메인별 그룹
474
+ │ │ └── login/
475
+ │ │ ├── spec.md
476
+ │ │ ├── plan.md
477
+ │ │ └── tasks.md
469
478
  │ ├── changes/ # 변경 제안
470
479
  │ ├── archive/ # 완료된 변경
471
- │ └── drafts/ # 역추출 임시 스펙 (v1.2.0)
480
+ │ └── .reverse-drafts/ # 역추출 임시 스펙 (v1.2.0)
472
481
 
473
482
  └── .claude/
474
483
  ├── commands/ # 슬래시 커맨드 (29개)
package/dist/cli/index.js CHANGED
@@ -1954,8 +1954,8 @@ var validateCommand = {
1954
1954
  # \uC804\uCCB4 \uC2A4\uD399 \uAC80\uC99D
1955
1955
  sdd validate
1956
1956
 
1957
- # \uD2B9\uC815 \uD30C\uC77C \uAC80\uC99D
1958
- sdd validate .sdd/specs/user-auth/spec.md
1957
+ # \uD2B9\uC815 \uD30C\uC77C \uAC80\uC99D (\uB3C4\uBA54\uC778 \uAE30\uBC18 \uACBD\uB85C)
1958
+ sdd validate .sdd/specs/auth/user-auth/spec.md
1959
1959
 
1960
1960
  # \uC5C4\uACA9 \uBAA8\uB4DC (\uACBD\uACE0\uB3C4 \uC5D0\uB7EC\uB85C \uCC98\uB9AC)
1961
1961
  sdd validate --strict
@@ -2080,7 +2080,7 @@ created: YYYY-MM-DD
2080
2080
 
2081
2081
  ## \uC601\uD5A5 \uBC94\uC704
2082
2082
  ### \uC601\uD5A5\uBC1B\uB294 \uC2A4\uD399
2083
- - \`specs/user-auth/spec.md\`
2083
+ - \`.sdd/specs/auth/user-auth/spec.md\`
2084
2084
 
2085
2085
  ### \uBCC0\uACBD \uC720\uD615
2086
2086
  - [x] \uC218\uC815 (MODIFIED)
@@ -2705,9 +2705,23 @@ sdd reverse finalize -d auth # \uD2B9\uC815 \uB3C4\uBA54\uC778 \uD655\uC815
2705
2705
 
2706
2706
  **\uC218\uD589 \uC791\uC5C5:**
2707
2707
  - \`.sdd/.reverse-drafts/\`\uC5D0\uC11C \uC2B9\uC778\uB41C \uC2A4\uD399 \uC77D\uAE30
2708
- - \`.sdd/specs/<feature-id>/spec.md\` \uC0DD\uC131
2708
+ - \`.sdd/specs/<domain>/<feature-id>/spec.md\` \uC0DD\uC131 (\`/sdd.new\`\uC640 \uB3D9\uC77C\uD55C \uD615\uC2DD)
2709
2709
  - \uCD08\uC548 \uD30C\uC77C \uC0AD\uC81C
2710
2710
 
2711
+ **\uC0DD\uC131\uB418\uB294 \uC2A4\uD399 \uD615\uC2DD:**
2712
+
2713
+ finalize\uB85C \uC0DD\uC131\uB418\uB294 \uC2A4\uD399\uC740 \`/sdd.new\`\uC640 **\uB3D9\uC77C\uD55C \uD615\uC2DD**\uC785\uB2C8\uB2E4:
2714
+
2715
+ - YAML frontmatter (id, title, status, domain, depends, ...)
2716
+ - \`## \uC694\uAD6C\uC0AC\uD56D\` + REQ-ID + RFC 2119 \uD0A4\uC6CC\uB4DC (SHALL)
2717
+ - \`## \uC2DC\uB098\uB9AC\uC624\` + \`- **GIVEN/WHEN/THEN**\` \uD615\uC2DD
2718
+ - \`## \uBE44\uAE30\uB2A5 \uC694\uAD6C\uC0AC\uD56D\`, \`## \uC81C\uC57D\uC0AC\uD56D\`, \`## \uC6A9\uC5B4 \uC815\uC758\`
2719
+
2720
+ \uCD94\uAC00 \uBA54\uD0C0\uB370\uC774\uD130 (\uC5ED\uCD94\uCD9C \uC804\uC6A9):
2721
+ - \`extracted_from: reverse-extraction\`
2722
+ - \`confidence: <\uC2E0\uB8B0\uB3C4 \uC810\uC218>\`
2723
+ - \`source_files: [\uC6D0\uBCF8 \uD30C\uC77C \uBAA9\uB85D]\`
2724
+
2711
2725
  **\uC644\uB8CC \uD6C4 \uC548\uB0B4:** "\uC2A4\uD399 \uD655\uC815\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \`/sdd.validate\`\uB85C \uC2A4\uD399\uC744 \uAC80\uC99D\uD558\uAC70\uB098 \`/sdd.new\`\uB85C \uC0C8 \uAE30\uB2A5\uC744 \uCD94\uAC00\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
2712
2726
 
2713
2727
  ## \uCD9C\uB825 \uD30C\uC77C
@@ -21302,12 +21316,14 @@ function formatSpecAsMarkdown(spec) {
21302
21316
  if (spec.scenarios.length > 0) {
21303
21317
  lines.push("## \uC2DC\uB098\uB9AC\uC624");
21304
21318
  lines.push("");
21305
- for (const scenario of spec.scenarios) {
21306
- lines.push(`### ${scenario.name}${scenario.inferred ? " *(\uCD94\uB860\uB428)*" : ""}`);
21319
+ for (let i = 0; i < spec.scenarios.length; i++) {
21320
+ const scenario = spec.scenarios[i];
21321
+ const inferredTag = scenario.inferred ? " *(\uCD94\uB860\uB428)*" : "";
21322
+ lines.push(`### Scenario ${i + 1}: ${scenario.name}${inferredTag}`);
21307
21323
  lines.push("");
21308
- lines.push(`**Given** ${scenario.given}`);
21309
- lines.push(`**When** ${scenario.when}`);
21310
- lines.push(`**Then** ${scenario.then}`);
21324
+ lines.push(`- **GIVEN** ${scenario.given}`);
21325
+ lines.push(`- **WHEN** ${scenario.when}`);
21326
+ lines.push(`- **THEN** ${scenario.then}`);
21311
21327
  lines.push("");
21312
21328
  }
21313
21329
  }
@@ -21759,88 +21775,128 @@ import path52 from "path";
21759
21775
  import { promises as fs34 } from "fs";
21760
21776
  import chalk7 from "chalk";
21761
21777
  function convertToSddSpec(spec) {
21762
- const lines = [];
21763
- lines.push(`# ${spec.name}`);
21764
- lines.push("");
21765
- lines.push(`> \uB3C4\uBA54\uC778: \`${spec.domain}\``);
21766
- lines.push(`> \uBC84\uC804: ${spec.metadata.version}`);
21767
- lines.push(`> \uC0C1\uD0DC: ACTIVE`);
21768
- lines.push("");
21769
- lines.push("## \uAC1C\uC694");
21770
- lines.push("");
21771
- lines.push(spec.description);
21772
- lines.push("");
21773
- lines.push("## \uC2DC\uB098\uB9AC\uC624");
21774
- lines.push("");
21775
- for (const scenario of spec.scenarios) {
21776
- lines.push(`### ${scenario.name}`);
21777
- lines.push("");
21778
- lines.push("```gherkin");
21779
- lines.push(`Given ${scenario.given}`);
21780
- lines.push(`When ${scenario.when}`);
21781
- lines.push(`Then ${scenario.then}`);
21782
- lines.push("```");
21783
- lines.push("");
21784
- }
21778
+ const extractedAt = spec.metadata.extractedAt instanceof Date ? spec.metadata.extractedAt.toISOString().split("T")[0] : String(spec.metadata.extractedAt).split("T")[0];
21779
+ const featureId = spec.id.includes("/") ? spec.id.split("/").pop() : spec.id;
21780
+ const sourceFilesYaml = spec.metadata.sourceFiles.length > 0 ? spec.metadata.sourceFiles.map((f) => ` - ${f}`).join("\n") : " - (none)";
21781
+ let content = `---
21782
+ id: ${featureId}
21783
+ title: "${spec.name}"
21784
+ status: draft
21785
+ created: ${extractedAt}
21786
+ domain: ${spec.domain}
21787
+ depends: null
21788
+ extracted_from: reverse-extraction
21789
+ confidence: ${spec.confidence.score}
21790
+ source_files:
21791
+ ${sourceFilesYaml}
21792
+ ---
21793
+
21794
+ # ${spec.name}
21795
+
21796
+ > ${spec.description}
21797
+
21798
+ ---
21799
+
21800
+ ## \uAC1C\uC694
21801
+
21802
+ ${spec.description}
21803
+
21804
+ ---
21805
+
21806
+ ## \uC694\uAD6C\uC0AC\uD56D
21807
+
21808
+ `;
21785
21809
  if (spec.contracts.length > 0) {
21786
- lines.push("## \uACC4\uC57D");
21787
- lines.push("");
21788
- const inputContracts = spec.contracts.filter((c) => c.type === "input");
21789
- const outputContracts = spec.contracts.filter((c) => c.type === "output");
21790
- if (inputContracts.length > 0) {
21791
- lines.push("### \uC785\uB825");
21792
- lines.push("");
21793
- for (const contract of inputContracts) {
21794
- lines.push(`- ${contract.description}`);
21795
- if (contract.signature) {
21796
- lines.push(` \`\`\`typescript`);
21797
- lines.push(` ${contract.signature}`);
21798
- lines.push(` \`\`\``);
21799
- }
21810
+ let reqIndex = 1;
21811
+ for (const contract of spec.contracts) {
21812
+ const reqId = `REQ-${String(reqIndex++).padStart(2, "0")}`;
21813
+ const reqTitle = contract.description.split("\uC758")[0] || contract.description;
21814
+ content += `### ${reqId}: ${reqTitle}
21815
+
21816
+ \uC2DC\uC2A4\uD15C\uC740 ${contract.description.toLowerCase()}\uC744(\uB97C) \uC81C\uACF5\uD574\uC57C \uD55C\uB2E4(SHALL).
21817
+
21818
+ `;
21819
+ if (contract.signature) {
21820
+ content += `\`\`\`typescript
21821
+ ${contract.signature}
21822
+ \`\`\`
21823
+
21824
+ `;
21800
21825
  }
21801
- lines.push("");
21802
21826
  }
21803
- if (outputContracts.length > 0) {
21804
- lines.push("### \uCD9C\uB825");
21805
- lines.push("");
21806
- for (const contract of outputContracts) {
21807
- lines.push(`- ${contract.description}`);
21808
- if (contract.signature) {
21809
- lines.push(` \`\`\`typescript`);
21810
- lines.push(` ${contract.signature}`);
21811
- lines.push(` \`\`\``);
21812
- }
21813
- }
21814
- lines.push("");
21827
+ } else {
21828
+ content += `### REQ-01: [\uC694\uAD6C\uC0AC\uD56D \uC81C\uBAA9]
21829
+
21830
+ [\uC694\uAD6C\uC0AC\uD56D \uC0C1\uC138 \uC124\uBA85]
21831
+ - \uC2DC\uC2A4\uD15C\uC740 [\uAE30\uB2A5]\uC744 \uC9C0\uC6D0\uD574\uC57C \uD55C\uB2E4(SHALL)
21832
+
21833
+ `;
21834
+ }
21835
+ content += `---
21836
+
21837
+ ## \uC2DC\uB098\uB9AC\uC624
21838
+
21839
+ `;
21840
+ if (spec.scenarios.length > 0) {
21841
+ for (let i = 0; i < spec.scenarios.length; i++) {
21842
+ const scenario = spec.scenarios[i];
21843
+ content += `### Scenario ${i + 1}: ${scenario.name}
21844
+
21845
+ - **GIVEN** ${scenario.given}
21846
+ - **WHEN** ${scenario.when}
21847
+ - **THEN** ${scenario.then}
21848
+
21849
+ `;
21815
21850
  }
21851
+ } else {
21852
+ content += `### Scenario 1: [\uC2DC\uB098\uB9AC\uC624\uBA85]
21853
+
21854
+ - **GIVEN** [\uC804\uC81C \uC870\uAC74]
21855
+ - **WHEN** [\uD589\uB3D9/\uD2B8\uB9AC\uAC70]
21856
+ - **THEN** [\uC608\uC0C1 \uACB0\uACFC]
21857
+
21858
+ `;
21816
21859
  }
21860
+ content += `---
21861
+
21862
+ ## \uBE44\uAE30\uB2A5 \uC694\uAD6C\uC0AC\uD56D
21863
+
21864
+ ### \uC131\uB2A5
21865
+
21866
+ - \uC751\uB2F5 \uC2DC\uAC04: [N]ms \uC774\uB0B4 (SHOULD)
21867
+
21868
+ ### \uBCF4\uC548
21869
+
21870
+ - [\uBCF4\uC548 \uC694\uAD6C\uC0AC\uD56D] (SHALL)
21871
+
21872
+ ---
21873
+
21874
+ ## \uC81C\uC57D\uC0AC\uD56D
21875
+
21876
+ - \uC6D0\uBCF8 \uD30C\uC77C: ${spec.metadata.sourceFiles.join(", ") || "(\uC5C6\uC74C)"}
21877
+ - \uC5ED\uCD94\uCD9C \uC2E0\uB8B0\uB3C4: ${spec.confidence.grade} (${spec.confidence.score}%)
21878
+
21879
+ ---
21880
+
21881
+ ## \uC6A9\uC5B4 \uC815\uC758
21882
+
21883
+ | \uC6A9\uC5B4 | \uC815\uC758 |
21884
+ |------|------|
21885
+ | [\uC6A9\uC5B41] | [\uC815\uC7581] |
21886
+ `;
21817
21887
  if (spec.relatedSpecs.length > 0) {
21818
- lines.push("## \uAD00\uB828 \uC2A4\uD399");
21819
- lines.push("");
21888
+ content += `
21889
+ ---
21890
+
21891
+ ## \uAD00\uB828 \uC2A4\uD399
21892
+
21893
+ `;
21820
21894
  for (const related of spec.relatedSpecs) {
21821
- lines.push(`- [[${related}]]`);
21895
+ content += `- [[${related}]]
21896
+ `;
21822
21897
  }
21823
- lines.push("");
21824
21898
  }
21825
- lines.push("---");
21826
- lines.push("");
21827
- lines.push("## \uBA54\uD0C0\uB370\uC774\uD130");
21828
- lines.push("");
21829
- lines.push("```yaml");
21830
- lines.push(`id: ${spec.id}`);
21831
- lines.push(`domain: ${spec.domain}`);
21832
- lines.push(`version: ${spec.metadata.version}`);
21833
- lines.push(`source: reverse-extraction`);
21834
- lines.push(`extracted_at: ${spec.metadata.extractedAt.toISOString()}`);
21835
- lines.push(`finalized_at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
21836
- lines.push(`confidence: ${spec.confidence.score}`);
21837
- lines.push(`source_files:`);
21838
- for (const file of spec.metadata.sourceFiles) {
21839
- lines.push(` - ${file}`);
21840
- }
21841
- lines.push("```");
21842
- lines.push("");
21843
- return lines.join("\n");
21899
+ return content;
21844
21900
  }
21845
21901
  async function finalizeSpec(sddRoot, spec) {
21846
21902
  try {