xwang 0.0.6 → 0.0.7

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.
@@ -5,6 +5,7 @@
5
5
  "xwang/scripts/xwang-env.sh",
6
6
  "xwang/scripts/xwang-state.sh",
7
7
  "xwang/scripts/xwang-guard.sh",
8
+ "xwang/scripts/xwang-doc-lang-check.sh",
8
9
  "xwang-open/SKILL.md",
9
10
  "xwang-open/scripts/xwang-open-check.sh",
10
11
  "xwang-design/SKILL.md",
@@ -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
@@ -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:执行归档
@@ -105,8 +105,18 @@ grilling 确认完成后、生成 PRD 前,**必须**执行 PRD 拆分预检并
105
105
 
106
106
  ### Step 2:生成 PRD
107
107
 
108
- - [ ] 用户确认后,调用 `/to-prd` 生成**中文** PRD(遵循 to-prd 的 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xwang",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "description": "xwang CLI",
5
5
  "keywords": [
6
6
  "xwang",