sdtk-kit 0.3.9 → 1.0.1
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/LICENSE +21 -0
- package/README.md +135 -177
- package/bin/sdtk-code.js +6 -0
- package/bin/sdtk-design.js +6 -0
- package/bin/sdtk-ops.js +6 -0
- package/bin/sdtk-spec.js +12 -0
- package/bin/sdtk-wiki.js +6 -0
- package/package.json +60 -46
- package/scripts/postinstall.js +40 -0
- package/assets/manifest/toolkit-bundle.manifest.json +0 -473
- package/assets/manifest/toolkit-bundle.sha256.txt +0 -93
- package/assets/toolkit/toolkit/AGENTS.md +0 -131
- package/assets/toolkit/toolkit/install.ps1 +0 -310
- package/assets/toolkit/toolkit/runtimes/claude/CLAUDE_TEMPLATE.md +0 -54
- package/assets/toolkit/toolkit/runtimes/codex/CODEX_TEMPLATE.md +0 -32
- package/assets/toolkit/toolkit/scripts/init-feature.ps1 +0 -261
- package/assets/toolkit/toolkit/scripts/install-claude-skills.ps1 +0 -169
- package/assets/toolkit/toolkit/scripts/install-codex-skills.ps1 +0 -189
- package/assets/toolkit/toolkit/scripts/uninstall-claude-skills.ps1 +0 -139
- package/assets/toolkit/toolkit/scripts/uninstall-codex-skills.ps1 +0 -116
- package/assets/toolkit/toolkit/sdtk.config.json +0 -28
- package/assets/toolkit/toolkit/sdtk.config.profiles.example.json +0 -50
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/SKILL.md +0 -84
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-api-design-spec/scripts/generate_api_design_detail.py +0 -732
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/SKILL.md +0 -43
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-api-doc/references/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/skills/sdtk-arch/SKILL.md +0 -83
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -220
- package/assets/toolkit/toolkit/skills/sdtk-arch/references/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/skills/sdtk-ba/SKILL.md +0 -29
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/SKILL.md +0 -52
- package/assets/toolkit/toolkit/skills/sdtk-design-layout/scripts/render_design_layout_images.py +0 -246
- package/assets/toolkit/toolkit/skills/sdtk-dev/SKILL.md +0 -90
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/code-quality-reviewer.md +0 -35
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/implementer.md +0 -61
- package/assets/toolkit/toolkit/skills/sdtk-dev/prompts/spec-reviewer.md +0 -42
- package/assets/toolkit/toolkit/skills/sdtk-dev-backend/SKILL.md +0 -21
- package/assets/toolkit/toolkit/skills/sdtk-dev-frontend/SKILL.md +0 -19
- package/assets/toolkit/toolkit/skills/sdtk-orchestrator/SKILL.md +0 -80
- package/assets/toolkit/toolkit/skills/sdtk-pm/SKILL.md +0 -30
- package/assets/toolkit/toolkit/skills/sdtk-qa/SKILL.md +0 -53
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/SKILL.md +0 -86
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -220
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/excel-image-export.md +0 -51
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/figma-mcp.md +0 -54
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/references/numbering-rules.md +0 -28
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/scripts/renumber_flow_action_spec_global.py +0 -136
- package/assets/toolkit/toolkit/skills/sdtk-screen-design-spec/scripts/validate_flow_action_spec_numbering.py +0 -414
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/SKILL.md +0 -74
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/references/TEST_CASE_CREATION_RULES.md +0 -129
- package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/scripts/validate_test_case_spec.py +0 -97
- package/assets/toolkit/toolkit/skills/skills.catalog.yaml +0 -302
- package/assets/toolkit/toolkit/skills-claude/api-design-spec/SKILL.md +0 -90
- package/assets/toolkit/toolkit/skills-claude/api-doc/SKILL.md +0 -47
- package/assets/toolkit/toolkit/skills-claude/arch/SKILL.md +0 -59
- package/assets/toolkit/toolkit/skills-claude/ba/SKILL.md +0 -50
- package/assets/toolkit/toolkit/skills-claude/design-layout/SKILL.md +0 -57
- package/assets/toolkit/toolkit/skills-claude/dev/SKILL.md +0 -45
- package/assets/toolkit/toolkit/skills-claude/dev-backend/SKILL.md +0 -20
- package/assets/toolkit/toolkit/skills-claude/dev-frontend/SKILL.md +0 -18
- package/assets/toolkit/toolkit/skills-claude/orchestrator/SKILL.md +0 -63
- package/assets/toolkit/toolkit/skills-claude/pm/SKILL.md +0 -52
- package/assets/toolkit/toolkit/skills-claude/qa/SKILL.md +0 -48
- package/assets/toolkit/toolkit/skills-claude/screen-design-spec/SKILL.md +0 -90
- package/assets/toolkit/toolkit/skills-claude/test-case-spec/SKILL.md +0 -61
- package/assets/toolkit/toolkit/templates/QUALITY_CHECKLIST.md +0 -124
- package/assets/toolkit/toolkit/templates/README.md +0 -63
- package/assets/toolkit/toolkit/templates/SHARED_PLANNING.md +0 -80
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_CREATION_RULES.md +0 -22
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_DETAIL_TEMPLATE.md +0 -67
- package/assets/toolkit/toolkit/templates/docs/api/API_DESIGN_FLOWCHART_CREATION_RULES.md +0 -468
- package/assets/toolkit/toolkit/templates/docs/api/API_ENDPOINTS_TEMPLATE.md +0 -229
- package/assets/toolkit/toolkit/templates/docs/api/FEATURE_API_TEMPLATE.yaml +0 -20
- package/assets/toolkit/toolkit/templates/docs/api/FLOWCHART_CREATION_RULES.md +0 -20
- package/assets/toolkit/toolkit/templates/docs/api/YAML_CREATION_RULES.md +0 -128
- package/assets/toolkit/toolkit/templates/docs/api/feature_api_flow_list_TEMPLATE.txt +0 -12
- package/assets/toolkit/toolkit/templates/docs/architecture/ARCH_DESIGN_TEMPLATE.md +0 -109
- package/assets/toolkit/toolkit/templates/docs/database/DATABASE_SPEC_TEMPLATE.md +0 -175
- package/assets/toolkit/toolkit/templates/docs/design/DESIGN_LAYOUT_TEMPLATE.md +0 -60
- package/assets/toolkit/toolkit/templates/docs/dev/FEATURE_IMPL_PLAN_TEMPLATE.md +0 -73
- package/assets/toolkit/toolkit/templates/docs/product/BACKLOG_TEMPLATE.md +0 -50
- package/assets/toolkit/toolkit/templates/docs/product/PRD_TEMPLATE.md +0 -66
- package/assets/toolkit/toolkit/templates/docs/product/PROJECT_INITIATION_TEMPLATE.md +0 -98
- package/assets/toolkit/toolkit/templates/docs/qa/QA_RELEASE_REPORT_TEMPLATE.md +0 -61
- package/assets/toolkit/toolkit/templates/docs/qa/TEST_CASE_CREATION_RULES.md +0 -129
- package/assets/toolkit/toolkit/templates/docs/qa/TEST_CASE_TEMPLATE.md +0 -104
- package/assets/toolkit/toolkit/templates/docs/specs/BA_SPEC_TEMPLATE.md +0 -139
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_CREATION_RULES.md +0 -220
- package/assets/toolkit/toolkit/templates/docs/specs/FLOW_ACTION_SPEC_TEMPLATE.md +0 -197
- package/assets/toolkit/toolkit/templates/handoffs/ARCH_TO_DEV.md +0 -31
- package/assets/toolkit/toolkit/templates/handoffs/BA_TO_ARCH.md +0 -28
- package/assets/toolkit/toolkit/templates/handoffs/DEV_STAGE1_SPEC_REVIEW.md +0 -26
- package/assets/toolkit/toolkit/templates/handoffs/DEV_STAGE2_CODE_QUALITY_REVIEW.md +0 -20
- package/assets/toolkit/toolkit/templates/handoffs/DEV_TO_QA.md +0 -23
- package/assets/toolkit/toolkit/templates/handoffs/PM_TO_BA.md +0 -26
- package/assets/toolkit/toolkit/templates/handoffs/QA_RELEASE_DECISION.md +0 -21
- package/bin/sdtk.js +0 -15
- package/src/commands/auth.js +0 -85
- package/src/commands/generate.js +0 -177
- package/src/commands/help.js +0 -101
- package/src/commands/init.js +0 -97
- package/src/commands/runtime.js +0 -217
- package/src/index.js +0 -59
- package/src/lib/args.js +0 -116
- package/src/lib/errors.js +0 -41
- package/src/lib/github-access.js +0 -68
- package/src/lib/powershell.js +0 -85
- package/src/lib/scope.js +0 -68
- package/src/lib/state.js +0 -83
- package/src/lib/toolkit-payload.js +0 -99
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import argparse
|
|
4
|
-
import re
|
|
5
|
-
import sys
|
|
6
|
-
from dataclasses import dataclass
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
TABLE_ROW_RE = re.compile(r"^\|.*\|\s*$")
|
|
11
|
-
TABLE_SEP_RE = re.compile(r"^\|\s*:?-+:?\s*(\|\s*:?-+:?\s*)+\|\s*$")
|
|
12
|
-
HEADING_RE = re.compile(r"^(#{1,6})\s+(.*\S)\s*$")
|
|
13
|
-
SCREEN_SECTION_HEADING_RE = re.compile(r"^###\s+(.*\S)\s*$", re.MULTILINE)
|
|
14
|
-
VI_DIACRITIC_RE = re.compile(r"[À-ỹ]")
|
|
15
|
-
VI_PHRASE_RE = re.compile(
|
|
16
|
-
r"\b("
|
|
17
|
-
r"thuc thi|hien thi|cap nhat|bo sung|can xac nhan|trong anh|"
|
|
18
|
-
r"duoc tra kem|tao lich|theo user|muc global|thay the cho|bao gom"
|
|
19
|
-
r")\b",
|
|
20
|
-
re.IGNORECASE,
|
|
21
|
-
)
|
|
22
|
-
INLINE_HEADING_RE = re.compile(r".+\s#{2,}\s+\S")
|
|
23
|
-
IMAGE_LINK_RE = re.compile(r"!\[[^\]]*\]\(([^)]+)\)")
|
|
24
|
-
PLANTUML_FENCE_START_RE = re.compile(r"^\s*```plantuml\s*$", re.IGNORECASE)
|
|
25
|
-
NEW_STYLE_ACTIVITY_LINE_RE = re.compile(
|
|
26
|
-
r"^\s*(start|stop|partition\s+\"|if\s*\(|fork(?:\s+again)?\b|end\s+fork\b|note\s+(?:right|left|top|bottom)\b|:[^;\n]+;)",
|
|
27
|
-
re.IGNORECASE,
|
|
28
|
-
)
|
|
29
|
-
LEGACY_ACTIVITY_START_RE = re.compile(r"\(\*\)")
|
|
30
|
-
LEGACY_EDGE_LABEL_RE = re.compile(r"-->\s*\[")
|
|
31
|
-
MIXED_ACTIVITY_ARROW_RE = re.compile(r":[^;\n]+;\s*-->")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass(frozen=True)
|
|
35
|
-
class Occurrence:
|
|
36
|
-
number: int
|
|
37
|
-
section: str
|
|
38
|
-
line_no: int
|
|
39
|
-
raw_line: str
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _split_table_row(line: str) -> list[str]:
|
|
43
|
-
# Keep it simple: markdown tables in our spec are plain and don't escape pipes.
|
|
44
|
-
parts = [p.strip() for p in line.strip().strip("|").split("|")]
|
|
45
|
-
return parts
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _read_text(path: Path) -> str:
|
|
49
|
-
# Prefer UTF-8; fallback to cp1252-ish if user saved without UTF-8.
|
|
50
|
-
for enc in ("utf-8", "utf-8-sig", "cp1252"):
|
|
51
|
-
try:
|
|
52
|
-
return path.read_text(encoding=enc)
|
|
53
|
-
except UnicodeDecodeError:
|
|
54
|
-
continue
|
|
55
|
-
return path.read_text(errors="replace")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def collect_hygiene_issues(md_text: str, *, en_check: bool) -> dict[str, list[tuple[int, str]]]:
|
|
59
|
-
issues: dict[str, list[tuple[int, str]]] = {
|
|
60
|
-
"encoding": [],
|
|
61
|
-
"vi_diacritic": [],
|
|
62
|
-
"vi_phrase": [],
|
|
63
|
-
"inline_heading": [],
|
|
64
|
-
}
|
|
65
|
-
lines = md_text.splitlines()
|
|
66
|
-
in_code_block = False
|
|
67
|
-
|
|
68
|
-
for idx, line in enumerate(lines, start=1):
|
|
69
|
-
stripped = line.strip()
|
|
70
|
-
if stripped.startswith("```"):
|
|
71
|
-
in_code_block = not in_code_block
|
|
72
|
-
continue
|
|
73
|
-
if in_code_block:
|
|
74
|
-
continue
|
|
75
|
-
|
|
76
|
-
if "�" in line or "ↁE" in line:
|
|
77
|
-
issues["encoding"].append((idx, line))
|
|
78
|
-
|
|
79
|
-
# A common corruption symptom: merged headings inside normal text lines.
|
|
80
|
-
if INLINE_HEADING_RE.search(line) and not stripped.startswith("#"):
|
|
81
|
-
issues["inline_heading"].append((idx, line))
|
|
82
|
-
|
|
83
|
-
if en_check:
|
|
84
|
-
lowered = stripped.lower()
|
|
85
|
-
if "original text" in lowered:
|
|
86
|
-
continue
|
|
87
|
-
if VI_DIACRITIC_RE.search(line):
|
|
88
|
-
issues["vi_diacritic"].append((idx, line))
|
|
89
|
-
elif VI_PHRASE_RE.search(line):
|
|
90
|
-
issues["vi_phrase"].append((idx, line))
|
|
91
|
-
|
|
92
|
-
return issues
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def _is_root_relative_docs_href(href: str) -> bool:
|
|
96
|
-
normalized = href.strip().replace("\\", "/").lower()
|
|
97
|
-
if not normalized:
|
|
98
|
-
return False
|
|
99
|
-
if re.match(r"^[a-z][a-z0-9+.-]*://", normalized):
|
|
100
|
-
return False
|
|
101
|
-
if normalized.startswith(("mailto:", "tel:", "data:", "#")):
|
|
102
|
-
return False
|
|
103
|
-
return normalized.startswith(("docs/", "./docs/", "/docs/"))
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def collect_screen_image_path_issues(md_text: str) -> list[tuple[int, str]]:
|
|
107
|
-
issues: list[tuple[int, str]] = []
|
|
108
|
-
lines = md_text.splitlines()
|
|
109
|
-
in_code_block = False
|
|
110
|
-
expecting_image_line = False
|
|
111
|
-
|
|
112
|
-
for idx, line in enumerate(lines, start=1):
|
|
113
|
-
stripped = line.strip()
|
|
114
|
-
if stripped.startswith("```"):
|
|
115
|
-
in_code_block = not in_code_block
|
|
116
|
-
expecting_image_line = False
|
|
117
|
-
continue
|
|
118
|
-
if in_code_block:
|
|
119
|
-
continue
|
|
120
|
-
if not stripped:
|
|
121
|
-
continue
|
|
122
|
-
|
|
123
|
-
if stripped.lower().startswith("screen image:"):
|
|
124
|
-
expecting_image_line = stripped.lower() == "screen image:"
|
|
125
|
-
hrefs = IMAGE_LINK_RE.findall(stripped)
|
|
126
|
-
elif expecting_image_line and stripped.startswith("!["):
|
|
127
|
-
expecting_image_line = False
|
|
128
|
-
hrefs = IMAGE_LINK_RE.findall(stripped)
|
|
129
|
-
else:
|
|
130
|
-
hrefs = []
|
|
131
|
-
if not stripped.startswith("<!--"):
|
|
132
|
-
expecting_image_line = False
|
|
133
|
-
|
|
134
|
-
for href in hrefs:
|
|
135
|
-
if _is_root_relative_docs_href(href):
|
|
136
|
-
issues.append((idx, href))
|
|
137
|
-
|
|
138
|
-
return issues
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def collect_plantuml_activity_syntax_issues(md_text: str) -> list[tuple[int, str]]:
|
|
142
|
-
issues: list[tuple[int, str]] = []
|
|
143
|
-
lines = md_text.splitlines()
|
|
144
|
-
in_plantuml_block = False
|
|
145
|
-
block_start_line = 0
|
|
146
|
-
block_lines: list[str] = []
|
|
147
|
-
|
|
148
|
-
def flush_block() -> None:
|
|
149
|
-
nonlocal issues, block_lines, block_start_line
|
|
150
|
-
if not block_lines:
|
|
151
|
-
return
|
|
152
|
-
has_new_style = any(NEW_STYLE_ACTIVITY_LINE_RE.search(line) for line in block_lines)
|
|
153
|
-
if not has_new_style:
|
|
154
|
-
block_lines = []
|
|
155
|
-
block_start_line = 0
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
for offset, line in enumerate(block_lines, start=0):
|
|
159
|
-
line_no = block_start_line + offset
|
|
160
|
-
if LEGACY_ACTIVITY_START_RE.search(line):
|
|
161
|
-
issues.append(
|
|
162
|
-
(line_no, "Legacy activity start marker `(*)` is not allowed in new-style activity diagrams.")
|
|
163
|
-
)
|
|
164
|
-
if MIXED_ACTIVITY_ARROW_RE.search(line):
|
|
165
|
-
issues.append(
|
|
166
|
-
(line_no, "Do not append legacy `-->` transitions after a new-style `:Activity;` action line.")
|
|
167
|
-
)
|
|
168
|
-
elif LEGACY_EDGE_LABEL_RE.search(line):
|
|
169
|
-
issues.append(
|
|
170
|
-
(line_no, "Legacy edge labels like `--> [label]` are not allowed in new-style activity diagrams.")
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
block_lines = []
|
|
174
|
-
block_start_line = 0
|
|
175
|
-
|
|
176
|
-
for idx, line in enumerate(lines, start=1):
|
|
177
|
-
if not in_plantuml_block and PLANTUML_FENCE_START_RE.match(line):
|
|
178
|
-
in_plantuml_block = True
|
|
179
|
-
block_start_line = idx + 1
|
|
180
|
-
block_lines = []
|
|
181
|
-
continue
|
|
182
|
-
|
|
183
|
-
if in_plantuml_block and line.strip().startswith("```"):
|
|
184
|
-
flush_block()
|
|
185
|
-
in_plantuml_block = False
|
|
186
|
-
continue
|
|
187
|
-
|
|
188
|
-
if in_plantuml_block:
|
|
189
|
-
block_lines.append(line)
|
|
190
|
-
|
|
191
|
-
if in_plantuml_block:
|
|
192
|
-
flush_block()
|
|
193
|
-
|
|
194
|
-
return issues
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def collect_wireframe_mapping_issues(md_text: str) -> list[tuple[int, str]]:
|
|
198
|
-
issues: list[tuple[int, str]] = []
|
|
199
|
-
sections = list(SCREEN_SECTION_HEADING_RE.finditer(md_text))
|
|
200
|
-
for idx, match in enumerate(sections):
|
|
201
|
-
start = match.start()
|
|
202
|
-
end = sections[idx + 1].start() if idx + 1 < len(sections) else len(md_text)
|
|
203
|
-
section_text = md_text[start:end]
|
|
204
|
-
|
|
205
|
-
if "Design Source Type: generated-draft" not in section_text:
|
|
206
|
-
continue
|
|
207
|
-
if "> Screen image not rendered in this environment." in section_text:
|
|
208
|
-
continue
|
|
209
|
-
if "Screen image:" not in section_text or "![" not in section_text:
|
|
210
|
-
continue
|
|
211
|
-
if "#### Wireframe Marker Mapping" in section_text:
|
|
212
|
-
continue
|
|
213
|
-
|
|
214
|
-
line_no = md_text.count("\n", 0, start) + 1
|
|
215
|
-
section_name = match.group(1).strip()
|
|
216
|
-
issues.append(
|
|
217
|
-
(line_no, f"{section_name}: missing `Wireframe Marker Mapping` table for generated-draft screen.")
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
return issues
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def parse_action_tables(md_text: str) -> list[Occurrence]:
|
|
224
|
-
occurrences: list[Occurrence] = []
|
|
225
|
-
current_section = ""
|
|
226
|
-
lines = md_text.splitlines()
|
|
227
|
-
|
|
228
|
-
i = 0
|
|
229
|
-
while i < len(lines):
|
|
230
|
-
line = lines[i]
|
|
231
|
-
|
|
232
|
-
m = HEADING_RE.match(line)
|
|
233
|
-
if m:
|
|
234
|
-
current_section = m.group(2).strip()
|
|
235
|
-
|
|
236
|
-
# Detect table header
|
|
237
|
-
if i + 1 < len(lines) and TABLE_ROW_RE.match(line) and TABLE_ROW_RE.match(lines[i + 1]):
|
|
238
|
-
header = _split_table_row(line)
|
|
239
|
-
sep = lines[i + 1]
|
|
240
|
-
|
|
241
|
-
if not TABLE_SEP_RE.match(sep):
|
|
242
|
-
i += 1
|
|
243
|
-
continue
|
|
244
|
-
|
|
245
|
-
# Only validate action tables: must contain No + Action + Description
|
|
246
|
-
lowered = [h.lower() for h in header]
|
|
247
|
-
if "no" not in lowered and "no." not in lowered:
|
|
248
|
-
i += 1
|
|
249
|
-
continue
|
|
250
|
-
if "action" not in lowered or "description" not in lowered:
|
|
251
|
-
i += 1
|
|
252
|
-
continue
|
|
253
|
-
|
|
254
|
-
try:
|
|
255
|
-
no_index = lowered.index("no")
|
|
256
|
-
except ValueError:
|
|
257
|
-
no_index = lowered.index("no.")
|
|
258
|
-
|
|
259
|
-
# Consume rows until table ends
|
|
260
|
-
j = i + 2
|
|
261
|
-
while j < len(lines) and TABLE_ROW_RE.match(lines[j]):
|
|
262
|
-
row = _split_table_row(lines[j])
|
|
263
|
-
if len(row) <= no_index:
|
|
264
|
-
j += 1
|
|
265
|
-
continue
|
|
266
|
-
raw_no = row[no_index]
|
|
267
|
-
try:
|
|
268
|
-
number = int(raw_no)
|
|
269
|
-
except ValueError:
|
|
270
|
-
j += 1
|
|
271
|
-
continue
|
|
272
|
-
occurrences.append(
|
|
273
|
-
Occurrence(
|
|
274
|
-
number=number,
|
|
275
|
-
section=current_section or "(unknown section)",
|
|
276
|
-
line_no=j + 1, # 1-based
|
|
277
|
-
raw_line=lines[j],
|
|
278
|
-
)
|
|
279
|
-
)
|
|
280
|
-
j += 1
|
|
281
|
-
|
|
282
|
-
i = j
|
|
283
|
-
continue
|
|
284
|
-
|
|
285
|
-
i += 1
|
|
286
|
-
|
|
287
|
-
return occurrences
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
def main() -> int:
|
|
291
|
-
parser = argparse.ArgumentParser(description="Validate numbering in flow-action spec tables.")
|
|
292
|
-
parser.add_argument("--spec", required=True, help="Path to *_FLOW_ACTION_SPEC.md")
|
|
293
|
-
parser.add_argument(
|
|
294
|
-
"--en-check",
|
|
295
|
-
action="store_true",
|
|
296
|
-
help="Enable EN artifact hygiene checks (Vietnamese leftovers + encoding corruption).",
|
|
297
|
-
)
|
|
298
|
-
args = parser.parse_args()
|
|
299
|
-
|
|
300
|
-
try:
|
|
301
|
-
sys.stdout.reconfigure(encoding="utf-8") # type: ignore[attr-defined]
|
|
302
|
-
sys.stderr.reconfigure(encoding="utf-8") # type: ignore[attr-defined]
|
|
303
|
-
except Exception:
|
|
304
|
-
pass
|
|
305
|
-
|
|
306
|
-
spec_path = Path(args.spec)
|
|
307
|
-
if not spec_path.exists():
|
|
308
|
-
print(f"[ERROR] Not found: {spec_path}", file=sys.stderr)
|
|
309
|
-
return 2
|
|
310
|
-
|
|
311
|
-
text = _read_text(spec_path)
|
|
312
|
-
occ = parse_action_tables(text)
|
|
313
|
-
|
|
314
|
-
by_number: dict[int, list[Occurrence]] = {}
|
|
315
|
-
for o in occ:
|
|
316
|
-
by_number.setdefault(o.number, []).append(o)
|
|
317
|
-
|
|
318
|
-
duplicates = {n: os for n, os in by_number.items() if len(os) > 1}
|
|
319
|
-
|
|
320
|
-
max_seen = -1
|
|
321
|
-
decreases: list[tuple[Occurrence, int]] = []
|
|
322
|
-
for o in occ:
|
|
323
|
-
if o.number < max_seen:
|
|
324
|
-
decreases.append((o, max_seen))
|
|
325
|
-
max_seen = max(max_seen, o.number)
|
|
326
|
-
|
|
327
|
-
has_issues = bool(duplicates or decreases)
|
|
328
|
-
hygiene = collect_hygiene_issues(text, en_check=args.en_check)
|
|
329
|
-
image_path_issues = collect_screen_image_path_issues(text)
|
|
330
|
-
plantuml_activity_issues = collect_plantuml_activity_syntax_issues(text)
|
|
331
|
-
wireframe_mapping_issues = collect_wireframe_mapping_issues(text)
|
|
332
|
-
if hygiene["encoding"] or hygiene["inline_heading"]:
|
|
333
|
-
has_issues = True
|
|
334
|
-
if args.en_check and (hygiene["vi_diacritic"] or hygiene["vi_phrase"]):
|
|
335
|
-
has_issues = True
|
|
336
|
-
if image_path_issues:
|
|
337
|
-
has_issues = True
|
|
338
|
-
if plantuml_activity_issues:
|
|
339
|
-
has_issues = True
|
|
340
|
-
if wireframe_mapping_issues:
|
|
341
|
-
has_issues = True
|
|
342
|
-
|
|
343
|
-
print(f"Checked: {spec_path}")
|
|
344
|
-
print(f"- Total numbered rows: {len(occ)}")
|
|
345
|
-
print(f"- Unique numbers: {len(by_number)}")
|
|
346
|
-
print(f"- EN hygiene check enabled: {args.en_check}")
|
|
347
|
-
|
|
348
|
-
if not occ:
|
|
349
|
-
print("[WARN] No action tables detected (| No | Action | Description | ... |).")
|
|
350
|
-
|
|
351
|
-
if duplicates:
|
|
352
|
-
print("\n[FAIL] Duplicate numbers found:")
|
|
353
|
-
for n in sorted(duplicates.keys()):
|
|
354
|
-
print(f"- No {n}:")
|
|
355
|
-
for o in duplicates[n]:
|
|
356
|
-
print(f" - {o.section} (line {o.line_no})")
|
|
357
|
-
|
|
358
|
-
if decreases:
|
|
359
|
-
print("\n[FAIL] Numbering decreases/resets detected (global numbering policy):")
|
|
360
|
-
for o, prev_max in decreases:
|
|
361
|
-
print(f"- No {o.number} after max {prev_max}: {o.section} (line {o.line_no})")
|
|
362
|
-
|
|
363
|
-
if hygiene["encoding"]:
|
|
364
|
-
print("\n[FAIL] Encoding/mojibake markers detected:")
|
|
365
|
-
for line_no, line in hygiene["encoding"][:20]:
|
|
366
|
-
print(f"- line {line_no}: {line.strip()}")
|
|
367
|
-
|
|
368
|
-
if hygiene["inline_heading"]:
|
|
369
|
-
print("\n[FAIL] Inline/merged heading patterns detected:")
|
|
370
|
-
for line_no, line in hygiene["inline_heading"][:20]:
|
|
371
|
-
print(f"- line {line_no}: {line.strip()}")
|
|
372
|
-
|
|
373
|
-
if args.en_check and hygiene["vi_diacritic"]:
|
|
374
|
-
print("\n[FAIL] Vietnamese diacritic text detected in EN artifact:")
|
|
375
|
-
for line_no, line in hygiene["vi_diacritic"][:20]:
|
|
376
|
-
print(f"- line {line_no}: {line.strip()}")
|
|
377
|
-
|
|
378
|
-
if args.en_check and hygiene["vi_phrase"]:
|
|
379
|
-
print("\n[FAIL] Vietnamese phrase patterns detected in EN artifact:")
|
|
380
|
-
for line_no, line in hygiene["vi_phrase"][:20]:
|
|
381
|
-
print(f"- line {line_no}: {line.strip()}")
|
|
382
|
-
|
|
383
|
-
if image_path_issues:
|
|
384
|
-
print("\n[FAIL] Screen image markdown paths must be file-relative, not docs-root-relative:")
|
|
385
|
-
for line_no, href in image_path_issues[:20]:
|
|
386
|
-
print(f"- line {line_no}: {href}")
|
|
387
|
-
|
|
388
|
-
if plantuml_activity_issues:
|
|
389
|
-
print("\n[FAIL] Mixed or legacy PlantUML activity syntax detected in a new-style screen-flow block:")
|
|
390
|
-
for line_no, message in plantuml_activity_issues[:20]:
|
|
391
|
-
print(f"- line {line_no}: {message}")
|
|
392
|
-
|
|
393
|
-
if wireframe_mapping_issues:
|
|
394
|
-
print("\n[FAIL] Missing wireframe-marker mapping tables for generated-draft screens:")
|
|
395
|
-
for line_no, message in wireframe_mapping_issues[:20]:
|
|
396
|
-
print(f"- line {line_no}: {message}")
|
|
397
|
-
|
|
398
|
-
if not has_issues:
|
|
399
|
-
print("\n[OK] Numbering and text hygiene checks passed.")
|
|
400
|
-
return 0
|
|
401
|
-
|
|
402
|
-
print("\nHint: Use global numbering across the full document.")
|
|
403
|
-
print("- Do not repeat No values in any action table.")
|
|
404
|
-
print("- Do not reset numbering per screen/dialog.")
|
|
405
|
-
print("- Use file-relative screen image paths such as assets/<feature>/screens/<screen>.svg.")
|
|
406
|
-
print("- Use new-style PlantUML activity syntax only for screen-flow diagrams.")
|
|
407
|
-
print("- Add a `Wireframe Marker Mapping` table when a generated-draft screen embeds a wireframe image.")
|
|
408
|
-
print("- Keep EN artifacts free of Vietnamese leftovers and encoding corruption.")
|
|
409
|
-
print("- Keep headings/sections on separate lines (avoid merged markdown blocks).")
|
|
410
|
-
return 1
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
if __name__ == "__main__":
|
|
414
|
-
raise SystemExit(main())
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sdtk-test-case-spec
|
|
3
|
-
description: Generate screen-based QA test-case markdown (`[FEATURE_KEY]_TEST_CASE.md`) in Excel-aligned layout (Statistic + per-screen UTC/ITC worksheets). Use when QA needs reusable test design artifacts before or during execution.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# SDTK Test Case Spec
|
|
7
|
-
|
|
8
|
-
## Critical Constraints
|
|
9
|
-
- I do not invent test coverage beyond BA, flow-action, and API sources.
|
|
10
|
-
- I do not let test totals drift away from worksheet rows or the QA baseline.
|
|
11
|
-
|
|
12
|
-
## Outputs
|
|
13
|
-
- `docs/qa/[FEATURE_KEY]_TEST_CASE.md`
|
|
14
|
-
- Optional project variant:
|
|
15
|
-
- `docs/en/qa/[FEATURE_KEY]_TEST_CASE.md` (only when project uses `docs/en/**`)
|
|
16
|
-
|
|
17
|
-
## Required Inputs
|
|
18
|
-
- Feature key (`FEATURE_KEY`)
|
|
19
|
-
- Screen/flow references:
|
|
20
|
-
- `docs/specs/[FEATURE_KEY]_FLOW_ACTION_SPEC.md`
|
|
21
|
-
- `docs/specs/BA_SPEC_[FEATURE_KEY].md`
|
|
22
|
-
- API reference:
|
|
23
|
-
- `docs/api/[FEATURE_KEY]_ENDPOINTS.md`
|
|
24
|
-
- (optional) `docs/api/[FeaturePascal]_API.yaml`
|
|
25
|
-
- Clarification source:
|
|
26
|
-
- `docs/specs/Q&A.md` or `docs/en/specs/Q&A.md` when available
|
|
27
|
-
|
|
28
|
-
## Core Rules
|
|
29
|
-
1. Apply `./references/TEST_CASE_CREATION_RULES.md` first.
|
|
30
|
-
2. Keep section order fixed (Statistic -> Abbreviations -> Scope -> References -> Environment -> Coverage -> Screen-based UTC/ITC -> OQ -> STC/UAT note).
|
|
31
|
-
3. Use screen-first layout to mirror worksheet structure.
|
|
32
|
-
4. Keep one unified 18-column schema for UTC/ITC rows.
|
|
33
|
-
5. Keep stable case IDs; do not renumber only because of regrouping.
|
|
34
|
-
6. Track unresolved decisions via `OQ-xx` and keep benchmark-expected OQs explicitly OPEN per `governance/ai/core/SDTK_BENCHMARK_OQ_POLICY.md`.
|
|
35
|
-
7. Quote exact BA, API, or flow-action requirement text when a testcase or coverage claim depends on that requirement.
|
|
36
|
-
|
|
37
|
-
## Procedure
|
|
38
|
-
1. Resolve output path:
|
|
39
|
-
- default `docs/qa/[FEATURE_KEY]_TEST_CASE.md`
|
|
40
|
-
- use `docs/en/qa/...` only if the repo already follows `docs/en/**`.
|
|
41
|
-
2. Build/refresh `Statistic Summary (Excel-aligned)` with UT total, IT total, and grand total.
|
|
42
|
-
3. Build `Feature Coverage Matrix` from flow/action and API docs.
|
|
43
|
-
4. Split UTC/ITC by screen sections (`screen-first`), not by test type only.
|
|
44
|
-
5. Fill conflict/permission/error-path cases from Q&A decisions and API contracts.
|
|
45
|
-
6. Record unresolved items in section `Open Questions`.
|
|
46
|
-
7. Validate:
|
|
47
|
-
- counts in summary match table rows
|
|
48
|
-
- structured TEST_CASE total stays aligned with the QA release-report baseline; if E2E is tracked separately, label it separately instead of inflating grand total
|
|
49
|
-
- no placeholder tokens like `??`
|
|
50
|
-
- out-of-scope boundaries are explicit
|
|
51
|
-
|
|
52
|
-
## Specification Quoting
|
|
53
|
-
When a testcase, coverage note, or defect check depends on a requirement, quote the exact source text before stating coverage or mismatch.
|
|
54
|
-
|
|
55
|
-
Use this format:
|
|
56
|
-
- `Spec says: "[exact quote]" -> Evidence: [covered/not covered + file reference]`
|
|
57
|
-
|
|
58
|
-
## Role Integration
|
|
59
|
-
- Primary owner: `/qa`.
|
|
60
|
-
- `/qa` uses this skill to design/update reusable test-case specs.
|
|
61
|
-
- `sdtk-qa` still owns release decision artifact (`QA_RELEASE_REPORT_*`).
|
|
62
|
-
|
|
63
|
-
## Notes
|
|
64
|
-
- This skill is for test-case specification artifacts, not test execution logs.
|
|
65
|
-
- For release approval and defect triage, continue with `sdtk-qa`.
|
|
66
|
-
|
|
67
|
-
## Validator Script
|
|
68
|
-
- `scripts/validate_test_case_spec.py`
|
|
69
|
-
|
|
70
|
-
### Typical command
|
|
71
|
-
```powershell
|
|
72
|
-
python "toolkit/skills/sdtk-test-case-spec/scripts/validate_test_case_spec.py" `
|
|
73
|
-
--file "docs/qa/[FEATURE_KEY]_TEST_CASE.md"
|
|
74
|
-
```
|
package/assets/toolkit/toolkit/skills/sdtk-test-case-spec/references/TEST_CASE_CREATION_RULES.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# TEST CASE CREATION RULES
|
|
2
|
-
## Canonical rules for `[FEATURE_KEY]_TEST_CASE.md`
|
|
3
|
-
|
|
4
|
-
**Version:** 1.0.0
|
|
5
|
-
**Last Updated:** 2026-02-25
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1. Purpose
|
|
10
|
-
- Standardize creation of feature test-case documents in markdown.
|
|
11
|
-
- Mirror Excel-style worksheet layout while keeping output reviewable in `.md`.
|
|
12
|
-
- Keep QA artifacts reusable across projects, domains, and screen sets.
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 2. Output Contract
|
|
17
|
-
- Primary output file:
|
|
18
|
-
- `docs/qa/[FEATURE_KEY]_TEST_CASE.md`
|
|
19
|
-
- Optional language/project variant:
|
|
20
|
-
- `docs/en/qa/[FEATURE_KEY]_TEST_CASE.md` (only when the project explicitly uses `docs/en/**`).
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## 3. Required Section Order
|
|
25
|
-
1. Document metadata (`Document ID`, `Version`, `Date`, `Author`, `Status`)
|
|
26
|
-
2. `Statistic Summary (Excel-aligned)`
|
|
27
|
-
3. `Abbreviations`
|
|
28
|
-
4. `1. Scope`
|
|
29
|
-
5. `2. References`
|
|
30
|
-
6. `3. Test Environment and Common Data`
|
|
31
|
-
7. `4. Feature Coverage Matrix`
|
|
32
|
-
8. `5. Screen-based Test Cases (Excel-aligned)`
|
|
33
|
-
9. `6. Open Questions (for final freeze)`
|
|
34
|
-
10. `7. STC/UAT Note`
|
|
35
|
-
|
|
36
|
-
Do not reorder these sections unless the user requests a different contract.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 4. Statistic Summary Rules
|
|
41
|
-
- Place summary near the top of the file (before detailed UTC/ITC tables).
|
|
42
|
-
- Keep columns aligned with Excel `Statistic` logic:
|
|
43
|
-
- `Num of Case`
|
|
44
|
-
- `OK`
|
|
45
|
-
- `NG`
|
|
46
|
-
- `Not tested yet`
|
|
47
|
-
- `Done (%)`
|
|
48
|
-
- `Updated Date`
|
|
49
|
-
- Compute:
|
|
50
|
-
- `Not tested yet = Num of Case - (OK + NG)`
|
|
51
|
-
- `Done (%) = (OK + NG) / Num of Case`
|
|
52
|
-
- Include subtotal rows at minimum:
|
|
53
|
-
- `Unit Test Total`
|
|
54
|
-
- `Integration Test Total`
|
|
55
|
-
- `Grand Total`
|
|
56
|
-
|
|
57
|
-
---
|
|
58
|
-
|
|
59
|
-
## 5. Screen-Based Worksheet Mapping
|
|
60
|
-
- Use `screen-first` layout to mirror Excel tabs:
|
|
61
|
-
- Each functional screen has a section (for example `SCH01`, `SCH02`, ...).
|
|
62
|
-
- Each screen section can have 2 sub-sections:
|
|
63
|
-
- `UTC` worksheet (name pattern: `[SCREEN_KEY]_UTC`)
|
|
64
|
-
- `ITC` worksheet (name pattern: `[SCREEN_KEY]_ITC`)
|
|
65
|
-
- Add one mapping table in section 5 that lists:
|
|
66
|
-
- `Screen`
|
|
67
|
-
- `Suggested Worksheet Pair`
|
|
68
|
-
- `UTC Cases`
|
|
69
|
-
- `ITC Cases`
|
|
70
|
-
- `Notes`
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 6. Test Case Table Schema (Mandatory)
|
|
75
|
-
- Use one unified 18-column schema for both UTC and ITC tables:
|
|
76
|
-
- `No`
|
|
77
|
-
- `Test Type`
|
|
78
|
-
- `Test Perspective`
|
|
79
|
-
- `Test Item`
|
|
80
|
-
- `Precondition`
|
|
81
|
-
- `Test Steps`
|
|
82
|
-
- `Expected Result`
|
|
83
|
-
- `Browser`
|
|
84
|
-
- `Test Execution Result`
|
|
85
|
-
- `Remarks`
|
|
86
|
-
- `Reviewer`
|
|
87
|
-
- `Review Date`
|
|
88
|
-
- `OK/NG`
|
|
89
|
-
- `Cause`
|
|
90
|
-
- `Countermeasure`
|
|
91
|
-
- `Owner`
|
|
92
|
-
- `Completion Date`
|
|
93
|
-
- `Confirmation`
|
|
94
|
-
|
|
95
|
-
---
|
|
96
|
-
|
|
97
|
-
## 7. Numbering and ID Policy
|
|
98
|
-
- Keep a stable `No` for each test case row.
|
|
99
|
-
- Do not renumber existing case IDs only for visual regrouping.
|
|
100
|
-
- In screen-split sections, case IDs may be non-contiguous; this is allowed.
|
|
101
|
-
- Summary/helper tables must keep contiguous `No` (`1,2,3,...`).
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## 8. Open Questions (OQ) Policy
|
|
106
|
-
- If expected behavior is unclear, create `OQ-xx` entries in section 6.
|
|
107
|
-
- Do not assume behavior for:
|
|
108
|
-
- permission matrix
|
|
109
|
-
- conflict dialog/action semantics
|
|
110
|
-
- API contract ambiguities
|
|
111
|
-
- release-scope boundary
|
|
112
|
-
- When resolved, keep question row and set status/remarks to resolved.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## 9. Language and Encoding
|
|
117
|
-
- Default artifact language: English.
|
|
118
|
-
- Keep original JP/VI labels when required by business/UI traceability (for example button labels).
|
|
119
|
-
- File encoding must be UTF-8.
|
|
120
|
-
- Avoid mojibake and placeholder tokens (`??`, `?????`) in final output.
|
|
121
|
-
|
|
122
|
-
---
|
|
123
|
-
|
|
124
|
-
## 10. Final Validation Checklist
|
|
125
|
-
- Required sections exist and are in correct order.
|
|
126
|
-
- Statistic totals equal sum of UTC/ITC rows.
|
|
127
|
-
- Screen mapping table case counts match actual UTC/ITC table counts.
|
|
128
|
-
- Scope explicitly states in-scope and out-of-scope boundaries.
|
|
129
|
-
- Open questions are explicitly tracked with owner and impact.
|