xwang 0.0.6 → 0.0.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/assets/manifest.json +1 -0
- package/assets/skills/xwang/scripts/xwang-doc-lang-check.sh +176 -0
- package/assets/skills/xwang/scripts/xwang-env.sh +15 -0
- package/assets/skills/xwang/scripts/xwang-guard.sh +112 -5
- package/assets/skills/xwang-archive/SKILL.md +10 -1
- package/assets/skills/xwang-design/SKILL.md +11 -1
- package/package.json +1 -1
package/assets/manifest.json
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# xwang-doc-lang-check — validate language consistency across xwang-generated documents
|
|
3
|
+
# Usage: xwang-doc-lang-check.sh [file-or-dir ...]
|
|
4
|
+
# Exits 0 if all documents are consistent, 1 if any are mixed.
|
|
5
|
+
# On mixed output, stdout contains a correction prompt in the primary language.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
# --- Language detection helpers ---
|
|
10
|
+
|
|
11
|
+
# Count CJK unified ideographs in text
|
|
12
|
+
count_cjk() {
|
|
13
|
+
local text="$1"
|
|
14
|
+
printf '%s' "$text" | perl -CS -ne 'BEGIN { $n=0 } while (/[\x{4e00}-\x{9fff}]/g) { $n++ } END { print $n }'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Count alphabetic ASCII words
|
|
18
|
+
count_words() {
|
|
19
|
+
local text="$1"
|
|
20
|
+
printf '%s' "$text" | grep -oE '[a-zA-Z]+' | wc -l | tr -d ' '
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# Detect dominant language: "zh" or "en"
|
|
24
|
+
detect_language() {
|
|
25
|
+
local text="$1"
|
|
26
|
+
local cjk words
|
|
27
|
+
cjk=$(count_cjk "$text")
|
|
28
|
+
words=$(count_words "$text")
|
|
29
|
+
if [ "$cjk" -gt "$words" ]; then
|
|
30
|
+
echo "zh"
|
|
31
|
+
else
|
|
32
|
+
echo "en"
|
|
33
|
+
fi
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# --- Markdown helpers ---
|
|
37
|
+
|
|
38
|
+
is_markdown() {
|
|
39
|
+
local file="$1"
|
|
40
|
+
case "$file" in
|
|
41
|
+
*.md|*.mdx) return 0 ;;
|
|
42
|
+
*) return 1 ;;
|
|
43
|
+
esac
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Check a single markdown file for language consistency.
|
|
47
|
+
# Outputs issues to stderr and a correction prompt to stdout when inconsistent.
|
|
48
|
+
check_file() {
|
|
49
|
+
local file="$1"
|
|
50
|
+
local title=""
|
|
51
|
+
local lead=""
|
|
52
|
+
local primary=""
|
|
53
|
+
local section_name=""
|
|
54
|
+
local section_body=""
|
|
55
|
+
local issues=()
|
|
56
|
+
|
|
57
|
+
flush_section() {
|
|
58
|
+
if [ -z "$section_name" ]; then
|
|
59
|
+
return
|
|
60
|
+
fi
|
|
61
|
+
local full_section="${section_name}"$'\n'"${section_body}"
|
|
62
|
+
if [ -z "$primary" ]; then
|
|
63
|
+
lead="${lead}${full_section}"
|
|
64
|
+
primary=$(detect_language "$lead")
|
|
65
|
+
else
|
|
66
|
+
local section_lang
|
|
67
|
+
section_lang=$(detect_language "$full_section")
|
|
68
|
+
if [ "$section_lang" != "$primary" ]; then
|
|
69
|
+
issues+=("section \"$section_name\": $section_lang, expected $primary")
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
section_name=""
|
|
73
|
+
section_body=""
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
77
|
+
if [[ "$line" =~ ^#[[:space:]] ]]; then
|
|
78
|
+
# Level-1 heading: treat as title / lead context
|
|
79
|
+
title="${line#*# }"
|
|
80
|
+
lead="${lead}${line}"$'\n'
|
|
81
|
+
continue
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if [[ "$line" =~ ^##[[:space:]] ]]; then
|
|
85
|
+
flush_section
|
|
86
|
+
section_name="${line##*## }"
|
|
87
|
+
section_body=""
|
|
88
|
+
continue
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [ -n "$section_name" ]; then
|
|
92
|
+
section_body="${section_body}${line}"$'\n'
|
|
93
|
+
else
|
|
94
|
+
# Content before the first section contributes to the lead
|
|
95
|
+
lead="${lead}${line}"$'\n'
|
|
96
|
+
fi
|
|
97
|
+
done < "$file"
|
|
98
|
+
|
|
99
|
+
flush_section
|
|
100
|
+
|
|
101
|
+
# If no sections at all, treat the whole file as one document
|
|
102
|
+
if [ -z "$primary" ]; then
|
|
103
|
+
primary=$(detect_language "$lead")
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if [ ${#issues[@]} -eq 0 ]; then
|
|
107
|
+
echo "OK: $file language is consistent ($primary)"
|
|
108
|
+
return 0
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
echo "MIXED-LANGUAGE: $file (${#issues[@]} issue(s))" >&2
|
|
112
|
+
for issue in "${issues[@]}"; do
|
|
113
|
+
echo " - $issue" >&2
|
|
114
|
+
done
|
|
115
|
+
|
|
116
|
+
if [ "$primary" = "zh" ]; then
|
|
117
|
+
cat <<'EOF'
|
|
118
|
+
上述文档存在中英文混用问题。项目上下文为中文,请将整份文档统一改写为中文,保持所有章节(包括但不限于标题、Problem Statement、Solution、User Stories、Implementation Decisions、Testing Decisions、Out of Scope、Further Notes、Why、What Changes、Capabilities、Impact 等)语言一致。
|
|
119
|
+
EOF
|
|
120
|
+
else
|
|
121
|
+
cat <<'EOF'
|
|
122
|
+
The document above has mixed languages. The project context is English. Please rewrite the entire document in English, keeping all sections (including but not limited to title, Problem Statement, Solution, User Stories, Implementation Decisions, Testing Decisions, Out of Scope, Further Notes, Why, What Changes, Capabilities, Impact, etc.) consistent in language.
|
|
123
|
+
EOF
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
return 1
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# --- Argument collection ---
|
|
130
|
+
|
|
131
|
+
declare -a FILES=()
|
|
132
|
+
|
|
133
|
+
if [ $# -eq 0 ]; then
|
|
134
|
+
echo "Usage: $0 [file-or-dir ...]" >&2
|
|
135
|
+
exit 2
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
for arg in "$@"; do
|
|
139
|
+
if [ -f "$arg" ]; then
|
|
140
|
+
FILES+=("$arg")
|
|
141
|
+
elif [ -d "$arg" ]; then
|
|
142
|
+
while IFS= read -r f; do
|
|
143
|
+
if is_markdown "$f"; then
|
|
144
|
+
FILES+=("$f")
|
|
145
|
+
fi
|
|
146
|
+
done < <(find "$arg" -type f \( -name '*.md' -o -name '*.mdx' \) \
|
|
147
|
+
! -path '*/node_modules/*' \
|
|
148
|
+
! -path '*/.git/*' \
|
|
149
|
+
! -path '*/dist/*' \
|
|
150
|
+
! -path '*/.claude/worktrees/*' \
|
|
151
|
+
2>/dev/null || true)
|
|
152
|
+
else
|
|
153
|
+
echo "WARNING: not found: $arg" >&2
|
|
154
|
+
fi
|
|
155
|
+
done
|
|
156
|
+
|
|
157
|
+
if [ ${#FILES[@]} -eq 0 ]; then
|
|
158
|
+
echo "No markdown documents found to check." >&2
|
|
159
|
+
exit 0
|
|
160
|
+
fi
|
|
161
|
+
|
|
162
|
+
mixed=0
|
|
163
|
+
for file in "${FILES[@]}"; do
|
|
164
|
+
if ! check_file "$file"; then
|
|
165
|
+
mixed=$((mixed + 1))
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
|
|
169
|
+
if [ "$mixed" -eq 0 ]; then
|
|
170
|
+
echo "OK: all ${#FILES[@]} document(s) have consistent language"
|
|
171
|
+
exit 0
|
|
172
|
+
else
|
|
173
|
+
echo "" >&2
|
|
174
|
+
echo "Total: $mixed / ${#FILES[@]} document(s) have mixed languages." >&2
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
@@ -65,6 +65,17 @@ if [ -z "${XWANG_GUARD:-}" ]; then
|
|
|
65
65
|
fi
|
|
66
66
|
export XWANG_GUARD
|
|
67
67
|
|
|
68
|
+
# Locate xwang-doc-lang-check.sh using the same approach.
|
|
69
|
+
if [ -z "${XWANG_DOC_LANG_CHECK:-}" ]; then
|
|
70
|
+
XWANG_DOC_LANG_CHECK_SIBLING="${XWANG_SCRIPT_DIR}/xwang-doc-lang-check.sh"
|
|
71
|
+
if [ -f "$XWANG_DOC_LANG_CHECK_SIBLING" ]; then
|
|
72
|
+
XWANG_DOC_LANG_CHECK="$(cd "$(dirname "$XWANG_DOC_LANG_CHECK_SIBLING")" && pwd)/$(basename "$XWANG_DOC_LANG_CHECK_SIBLING")"
|
|
73
|
+
else
|
|
74
|
+
XWANG_DOC_LANG_CHECK="$(locate_by_glob '*/xwang/scripts/xwang-doc-lang-check.sh' "${SEARCH_DIRS[@]}" 2>/dev/null || true)"
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
export XWANG_DOC_LANG_CHECK
|
|
78
|
+
|
|
68
79
|
if [ -z "$XWANG_OPEN_CHECK" ] || [ ! -f "$XWANG_OPEN_CHECK" ]; then
|
|
69
80
|
echo "WARNING: xwang-open-check.sh not found" >&2
|
|
70
81
|
fi
|
|
@@ -72,3 +83,7 @@ fi
|
|
|
72
83
|
if [ -z "$XWANG_GUARD" ] || [ ! -f "$XWANG_GUARD" ]; then
|
|
73
84
|
echo "WARNING: xwang-guard.sh not found" >&2
|
|
74
85
|
fi
|
|
86
|
+
|
|
87
|
+
if [ -z "$XWANG_DOC_LANG_CHECK" ] || [ ! -f "$XWANG_DOC_LANG_CHECK" ]; then
|
|
88
|
+
echo "WARNING: xwang-doc-lang-check.sh not found" >&2
|
|
89
|
+
fi
|
|
@@ -13,6 +13,35 @@ red() { echo -e "\033[31m$1\033[0m" >&2; }
|
|
|
13
13
|
green() { echo -e "\033[32m$1\033[0m" >&2; }
|
|
14
14
|
warn() { echo -e "\033[33m$1\033[0m" >&2; }
|
|
15
15
|
|
|
16
|
+
# --- Project root resolution ---
|
|
17
|
+
|
|
18
|
+
find_project_root() {
|
|
19
|
+
if [ -n "${XWANG_PROJECT_ROOT:-}" ]; then
|
|
20
|
+
printf '%s' "$XWANG_PROJECT_ROOT"
|
|
21
|
+
return 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
local git_root
|
|
25
|
+
git_root=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
|
26
|
+
if [ -n "$git_root" ]; then
|
|
27
|
+
printf '%s' "$git_root"
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
local dir="$PWD"
|
|
32
|
+
while [ "$dir" != "/" ]; do
|
|
33
|
+
if [ -d "$dir/openspec/changes" ]; then
|
|
34
|
+
printf '%s' "$dir"
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
dir=$(dirname "$dir")
|
|
38
|
+
done
|
|
39
|
+
|
|
40
|
+
printf '%s' "$PWD"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
PROJECT_ROOT=$(find_project_root)
|
|
44
|
+
|
|
16
45
|
# --- Input validation ---
|
|
17
46
|
|
|
18
47
|
validate_change_name() {
|
|
@@ -48,9 +77,16 @@ else
|
|
|
48
77
|
if [[ "${3:-}" == "--apply" ]]; then
|
|
49
78
|
APPLY=1
|
|
50
79
|
fi
|
|
51
|
-
CHANGE_DIR="openspec/changes/$CHANGE"
|
|
52
|
-
if [ "$PHASE" = "archive" ] && [ ! -d "$CHANGE_DIR" ]
|
|
53
|
-
|
|
80
|
+
CHANGE_DIR="$PROJECT_ROOT/openspec/changes/$CHANGE"
|
|
81
|
+
if [ "$PHASE" = "archive" ] && [ ! -d "$CHANGE_DIR" ]; then
|
|
82
|
+
if [ -d "$PROJECT_ROOT/openspec/changes/archive/$CHANGE" ]; then
|
|
83
|
+
CHANGE_DIR="$PROJECT_ROOT/openspec/changes/archive/$CHANGE"
|
|
84
|
+
elif [ -d "$PROJECT_ROOT/openspec/changes/archive" ]; then
|
|
85
|
+
archived=$(find "$PROJECT_ROOT/openspec/changes/archive" -maxdepth 1 -type d -name "*-$CHANGE" -print -quit 2>/dev/null || true)
|
|
86
|
+
if [ -n "$archived" ]; then
|
|
87
|
+
CHANGE_DIR="$archived"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
54
90
|
fi
|
|
55
91
|
fi
|
|
56
92
|
|
|
@@ -306,7 +342,7 @@ branch_status_handled() {
|
|
|
306
342
|
design_doc_recorded() {
|
|
307
343
|
local design_doc
|
|
308
344
|
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
309
|
-
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ] && [ -f "$design_doc" ]; then
|
|
345
|
+
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ] && { [ -f "$PROJECT_ROOT/$design_doc" ] || [ -f "$design_doc" ]; }; then
|
|
310
346
|
return 0
|
|
311
347
|
fi
|
|
312
348
|
echo "design_doc must point to an existing PRD for full workflow before leaving design." >&2
|
|
@@ -410,6 +446,7 @@ guard_design() {
|
|
|
410
446
|
check "design.md exists and non-empty" file_nonempty "$CHANGE_DIR/design.md"
|
|
411
447
|
check "tasks.md exists and non-empty" file_nonempty "$CHANGE_DIR/tasks.md"
|
|
412
448
|
check "tasks.md has at least one task" tasks_has_any
|
|
449
|
+
check "OpenSpec artifacts language consistency" openspec_artifacts_language_consistent
|
|
413
450
|
|
|
414
451
|
if [ "$workflow" = "full" ]; then
|
|
415
452
|
check "design_doc is recorded for full workflow" design_doc_recorded
|
|
@@ -418,7 +455,10 @@ guard_design() {
|
|
|
418
455
|
local design_doc
|
|
419
456
|
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
420
457
|
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ]; then
|
|
421
|
-
|
|
458
|
+
local doc_path="$design_doc"
|
|
459
|
+
[ -f "$PROJECT_ROOT/$design_doc" ] && doc_path="$PROJECT_ROOT/$design_doc"
|
|
460
|
+
check "PRD file ($design_doc) exists" file_nonempty "$doc_path"
|
|
461
|
+
check "PRD language consistency" design_doc_language_consistent
|
|
422
462
|
elif [ "$workflow" != "full" ]; then
|
|
423
463
|
warn " [WARN] No design_doc recorded in .xwang.yaml (optional for quick/tweak)"
|
|
424
464
|
fi
|
|
@@ -454,6 +494,13 @@ guard_archive() {
|
|
|
454
494
|
check "proposal.md exists" file_nonempty "$CHANGE_DIR/proposal.md"
|
|
455
495
|
check "design.md exists" file_nonempty "$CHANGE_DIR/design.md"
|
|
456
496
|
check "tasks.md all tasks checked" tasks_all_done
|
|
497
|
+
check "OpenSpec artifacts language consistency" openspec_artifacts_language_consistent
|
|
498
|
+
|
|
499
|
+
local design_doc
|
|
500
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
501
|
+
if [ -n "$design_doc" ] && [ "$design_doc" != "null" ]; then
|
|
502
|
+
check "PRD language consistency" design_doc_language_consistent
|
|
503
|
+
fi
|
|
457
504
|
}
|
|
458
505
|
|
|
459
506
|
locate_state_script() {
|
|
@@ -470,6 +517,66 @@ locate_state_script() {
|
|
|
470
517
|
return 1
|
|
471
518
|
}
|
|
472
519
|
|
|
520
|
+
locate_doc_lang_check() {
|
|
521
|
+
local check_sh="$SCRIPT_DIR/xwang-doc-lang-check.sh"
|
|
522
|
+
if [ -f "$check_sh" ]; then
|
|
523
|
+
echo "$check_sh"
|
|
524
|
+
return 0
|
|
525
|
+
fi
|
|
526
|
+
check_sh="$(find "$SCRIPT_DIR" "$SCRIPT_DIR/.." "$HOME" -path '*/xwang/scripts/xwang-doc-lang-check.sh' -type f -print -quit 2>/dev/null || true)"
|
|
527
|
+
if [ -n "$check_sh" ] && [ -f "$check_sh" ]; then
|
|
528
|
+
echo "$check_sh"
|
|
529
|
+
return 0
|
|
530
|
+
fi
|
|
531
|
+
return 1
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
design_doc_language_consistent() {
|
|
535
|
+
local design_doc
|
|
536
|
+
design_doc=$(yaml_field_value "design_doc" 2>/dev/null || true)
|
|
537
|
+
if [ -z "$design_doc" ] || [ "$design_doc" = "null" ]; then
|
|
538
|
+
return 0
|
|
539
|
+
fi
|
|
540
|
+
|
|
541
|
+
local target=""
|
|
542
|
+
if [ -f "$PROJECT_ROOT/$design_doc" ]; then
|
|
543
|
+
target="$PROJECT_ROOT/$design_doc"
|
|
544
|
+
elif [ -f "$design_doc" ]; then
|
|
545
|
+
target="$design_doc"
|
|
546
|
+
elif [ -f "$CHANGE_DIR/design.md" ]; then
|
|
547
|
+
# design_doc may be stale after archiving; fall back to design.md in the change dir
|
|
548
|
+
target="$CHANGE_DIR/design.md"
|
|
549
|
+
fi
|
|
550
|
+
|
|
551
|
+
if [ -z "$target" ]; then
|
|
552
|
+
echo "design_doc file not found: $design_doc" >&2
|
|
553
|
+
return 1
|
|
554
|
+
fi
|
|
555
|
+
|
|
556
|
+
local check_sh
|
|
557
|
+
if ! check_sh=$(locate_doc_lang_check); then
|
|
558
|
+
echo "xwang-doc-lang-check.sh not found; skipping language check" >&2
|
|
559
|
+
return 0
|
|
560
|
+
fi
|
|
561
|
+
"$XWANG_BASH" "$check_sh" "$target" >/dev/null 2>&1
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
openspec_artifacts_language_consistent() {
|
|
565
|
+
local check_sh
|
|
566
|
+
if ! check_sh=$(locate_doc_lang_check); then
|
|
567
|
+
echo "xwang-doc-lang-check.sh not found; skipping language check" >&2
|
|
568
|
+
return 0
|
|
569
|
+
fi
|
|
570
|
+
local files=()
|
|
571
|
+
for f in "$CHANGE_DIR/proposal.md" "$CHANGE_DIR/design.md" "$CHANGE_DIR/tasks.md"; do
|
|
572
|
+
[ -f "$f" ] && files+=("$f")
|
|
573
|
+
done
|
|
574
|
+
if [ ${#files[@]} -eq 0 ]; then
|
|
575
|
+
return 0
|
|
576
|
+
fi
|
|
577
|
+
"$XWANG_BASH" "$check_sh" "${files[@]}" >/dev/null 2>&1
|
|
578
|
+
}
|
|
579
|
+
|
|
473
580
|
apply_state_update() {
|
|
474
581
|
local state_sh
|
|
475
582
|
if ! state_sh=$(locate_state_script); then
|
|
@@ -58,10 +58,18 @@ fi
|
|
|
58
58
|
- **暂不归档**:不执行归档,保留当前 `phase: archive` 状态,等待用户稍后再次调用 `/xwang-archive`
|
|
59
59
|
- [ ] 只有用户选择「确认归档」后才继续 Step 3。
|
|
60
60
|
|
|
61
|
-
### Step 3
|
|
61
|
+
### Step 3:标记归档状态、校验完整性与 PRD 语言一致性
|
|
62
62
|
|
|
63
63
|
- [ ] 调用 `"$XWANG_STATE" set <name> archived true`,在 change 仍在 `openspec/changes/<name>/` 时把 `.xwang.yaml` 标记为已归档。
|
|
64
64
|
- [ ] **归档完整性守护**:调用 `"$XWANG_GUARD" <name> archive` 校验归档退出条件(`archived: true`、必要文档存在且 tasks 已勾选)。
|
|
65
|
+
- [ ] **PRD 语言一致性守护**:定位当前 change 对应的 PRD 文件(常见路径如 `docs/mattpocock/<name>-prd.md`、`docs/<name>-prd.md` 或 `<name>-prd.md`)。如存在,运行:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
"$XWANG_DOC_LANG_CHECK" <prd-file>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
若脚本报告 `MIXED-LANGUAGE`,将其 stdout 中的修正提示词展示给用户,**停止归档**,等待用户修正 PRD 后重新触发 `/xwang-archive`。只有脚本退出 `0` 才继续下一步。
|
|
72
|
+
|
|
65
73
|
- [ ] 如需,在 PRD 和 plan 文件前置元数据中标注 `archived-with: <name>` 和 `status: archived`。
|
|
66
74
|
|
|
67
75
|
### Step 4:执行归档
|
|
@@ -69,6 +77,7 @@ fi
|
|
|
69
77
|
- [ ] 调用 `/openspec-archive-change <name>`(或等价的 OpenSpec archive 命令)按 delta 语义合并主 spec 并移动 change 到归档目录。
|
|
70
78
|
- [ ] 如归档命令返回非零退出码,报告错误并停止。
|
|
71
79
|
- [ ] 归档成功后,确认 change 目录已移动到 `openspec/changes/archive/YYYY-MM-DD-<name>/`(或 OpenSpec 默认归档路径)。
|
|
80
|
+
- [ ] **同步 `.xwang.yaml` 中的文件路径**:如果 `design_doc`、`plan`、`verification_report` 等字段指向的是 `openspec/changes/<name>/` 下的旧路径,更新为归档目录 `openspec/changes/archive/YYYY-MM-DD-<name>/` 下的相对路径,避免后续 guard 或读取时路径失效。
|
|
72
81
|
- [ ] 向用户汇报归档完成、新目录路径与生命周期闭环状态。
|
|
73
82
|
|
|
74
83
|
## 退出条件
|
|
@@ -105,8 +105,18 @@ grilling 确认完成后、生成 PRD 前,**必须**执行 PRD 拆分预检并
|
|
|
105
105
|
|
|
106
106
|
### Step 2:生成 PRD
|
|
107
107
|
|
|
108
|
-
- [ ] 用户确认后,调用 `/to-prd`
|
|
108
|
+
- [ ] 用户确认后,调用 `/to-prd` 生成 PRD(遵循 to-prd 的 PRD 模板),无需额外用户确认。
|
|
109
109
|
- [ ] 将 PRD 写入 `docs/mattpocock/<name>-prd.md`;如果 grilling 过程中名称有调整,使用最终生成的名称。
|
|
110
|
+
- [ ] **PRD 语言一致性守护**:写入后运行语言检测脚本:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
XWANG_ENV="${XWANG_ENV:-$(find . "$HOME"/.*/skills "$HOME/.config" -path '*/xwang/scripts/xwang-env.sh' -type f -print -quit 2>/dev/null)}"
|
|
114
|
+
. "$XWANG_ENV"
|
|
115
|
+
"$XWANG_DOC_LANG_CHECK" docs/mattpocock/<name>-prd.md
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
若脚本报告 `MIXED-LANGUAGE`,将其 stdout 中的修正提示词追加到下一次 `/to-prd` 请求中,重新生成并写入 PRD,重复检测直到脚本退出 `0`。
|
|
119
|
+
|
|
110
120
|
- [ ] **如果 Step 1a 结论为「保持为一个 change」,在 PRD 中显式写入“不拆分原因”章节。**
|
|
111
121
|
|
|
112
122
|
### Step 3:创建 OpenSpec change
|