claudeos-core 2.3.1 → 2.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.
@@ -409,7 +409,7 @@ async function main() {
409
409
  }
410
410
 
411
411
  // ─── 10. Path-claim verification ──────────────────────────
412
- // Catches two dogfood-surfaced failure classes:
412
+ // Catches two failure classes:
413
413
  // (a) Pass 3 hallucinations: rules/standard files reference
414
414
  // src/... paths the LLM fabricated from directory context
415
415
  // (e.g., `src/feature/routers/featureRoutePath.ts` when the actual
@@ -427,9 +427,62 @@ async function main() {
427
427
  // inline code already fenced. We still strip fenced blocks first so
428
428
  // example blocks inside ```...``` don't produce false positives.
429
429
  const SRC_PATH_RE = /\bsrc\/[\w\-./]+\.(?:ts|tsx|js|jsx)\b/g;
430
- // Placeholder paths like `src/{domain}/...` are scaffold templates,
431
- // not real path claims. Skip them.
432
- const hasPlaceholder = (p) => /\{[^}]+\}/.test(p);
430
+
431
+ // Placeholder paths are scaffold templates / teaching examples, not
432
+ // real path claims. We skip them. Three patterns qualify:
433
+ // 1. Curly-brace placeholder — `src/{domain}/api/{Entity}.ts`
434
+ // (the original v2.3.0 form).
435
+ // 2. Xxx-style placeholder — `src/hooks/useXxx.ts` or
436
+ // `src/pages/PageXxx.tsx` (LLMs commonly use `Xxx`/`XXX` as
437
+ // "insert-your-name-here"; this form has been observed in
438
+ // `naming-conventions-rules.md`). We match any segment
439
+ // containing `Xxx` or `XXX` as a distinctive token.
440
+ // 3. Glob-star placeholder — `src/test/*.setup.ts` or
441
+ // `src/*/mocks/handlers.ts` (conventional glob syntax that an
442
+ // LLM may use to describe a *class* of files rather than one
443
+ // specific file).
444
+ // Literal paths that happen to contain `x`/`X` as part of a real
445
+ // identifier (e.g., `src/utils/xyzParser.ts`) do NOT match these
446
+ // patterns — the placeholder regex requires the `Xxx` pattern
447
+ // (capital-lower-lower, a distinctive convention) OR three or more
448
+ // consecutive uppercase X's. The uppercase-XXX rule has NO word-
449
+ // boundary anchor because placeholder tokens commonly appear in
450
+ // the middle of a compound identifier (`useXXX`, `useXXX_CONFIG`,
451
+ // `nameXXXvalue`, `XXXParser`) — requiring a boundary would skip
452
+ // those cases. Three consecutive uppercase X's in a row is a
453
+ // distinctive signal that essentially never appears in ordinary
454
+ // identifiers (lowercase `xxx` CAN appear in words like `taxXxxRate`,
455
+ // but three uppercase X's do not occur outside placeholder convention).
456
+ const hasPlaceholder = (p) =>
457
+ /\{[^}]+\}/.test(p) || // {domain} style
458
+ /X{3,}/.test(p) || /Xxx/.test(p) || // XXX+ anywhere, or Xxx token
459
+ /\*/.test(p); // glob star
460
+
461
+ // File-level exclusion: some generated rule files are DESIGNED to cite
462
+ // convention-trap paths as teaching examples — they tell the reader
463
+ // "don't invent paths like these". `content-validator`'s path-claim
464
+ // check is content-blind, so literal example paths inside such a file
465
+ // would be flagged as STALE_PATH even though the author intentionally
466
+ // listed them as cautionary illustrations.
467
+ //
468
+ // The exclusion is strictly opt-in, named by relative path, and
469
+ // limited to files whose purpose is educational-about-paths. Adding a
470
+ // file here is a deliberate design choice — the alternative is for
471
+ // the LLM to rewrite those examples as placeholders (Xxx / glob /
472
+ // prose), which the prompt now nudges toward but cannot strictly
473
+ // enforce.
474
+ //
475
+ // Current exclusions:
476
+ // - 00.core/52.ai-work-rules.md — the canonical "AI work rules"
477
+ // file, which by design lists convention-trap paths as warnings
478
+ // to future AI sessions. This file has been observed to
479
+ // accumulate STALE_PATH false positives when a prompt-level
480
+ // denylist primed the LLM to cite those exact paths as
481
+ // educational examples (the denylist has since been removed;
482
+ // this exclusion is the validator-side defense-in-depth).
483
+ const PATH_CLAIM_EXCLUDE_FILES = new Set([
484
+ "00.core/52.ai-work-rules.md",
485
+ ]);
433
486
 
434
487
  // Strip fenced code blocks (``` and ~~~) so examples inside code
435
488
  // blocks don't trigger the check — they're illustrations, not claims.
@@ -459,10 +512,19 @@ async function main() {
459
512
  ];
460
513
  let pathClaimsChecked = 0;
461
514
  let pathClaimErrors = 0;
515
+ let pathClaimFilesExcluded = 0;
462
516
  for (const target of pathClaimTargets) {
463
517
  if (!fs.existsSync(target.dir)) continue;
464
518
  const files = await glob(target.glob, { cwd: target.dir, absolute: true });
465
519
  for (const file of files) {
520
+ // File-level exclusion: the path is relative to the target dir
521
+ // (e.g., "00.core/52.ai-work-rules.md" inside .claude/rules/).
522
+ // Normalize to forward slashes for cross-platform match.
523
+ const relToTargetDir = path.relative(target.dir, file).split(path.sep).join("/");
524
+ if (PATH_CLAIM_EXCLUDE_FILES.has(relToTargetDir)) {
525
+ pathClaimFilesExcluded++;
526
+ continue;
527
+ }
466
528
  const raw = fs.readFileSync(file, "utf-8");
467
529
  const stripped = stripFences(raw);
468
530
  const seen = new Set(); // dedupe within a single file
@@ -488,7 +550,8 @@ async function main() {
488
550
  }
489
551
  }
490
552
  }
491
- console.log(` ${pathClaimsChecked} path claim(s) checked, ${pathClaimErrors} stale`);
553
+ console.log(` ${pathClaimsChecked} path claim(s) checked, ${pathClaimErrors} stale` +
554
+ (pathClaimFilesExcluded > 0 ? ` (${pathClaimFilesExcluded} file(s) excluded by design)` : ""));
492
555
 
493
556
  // MANIFEST ↔ CLAUDE.md §6 Skills drift check.
494
557
  // MANIFEST registers skills in a 4-column table; each row's second
@@ -634,14 +697,26 @@ async function main() {
634
697
  }
635
698
 
636
699
  // ─── Output results ─────────────────────────────────────────
700
+ //
701
+ // Terminology note (v2.3.3): the internal arrays stay named `errors` and
702
+ // `warnings` because they encode *severity* for programmatic consumers
703
+ // (health-checker's pass/fail gate, stale-report.json's schema, CI
704
+ // pipelines). The user-visible labels, however, are "advisories" and
705
+ // "notes" — because these are quality observations about LLM-generated
706
+ // documents, not test failures. A STALE_PATH doesn't mean `init` crashed;
707
+ // it means "AI guessed a filename that doesn't exist on disk — worth
708
+ // reviewing". Calling that an "error" made users think generation had
709
+ // actually failed. The exit code behavior is unchanged — this tool still
710
+ // returns 1 when advisories exist so `npx claudeos-core health` remains
711
+ // a real gate for CI.
637
712
  console.log(`\n Checked ${checked} files\n`);
638
713
  if (errors.length) {
639
- console.log(` ERRORS (${errors.length}):`);
714
+ console.log(` ℹ️ ADVISORIES (${errors.length}):`);
640
715
  errors.forEach(e => console.log(` [${e.type}] ${e.file}: ${e.msg}`));
641
716
  console.log();
642
717
  }
643
718
  if (warnings.length) {
644
- console.log(` ⚠️ WARNINGS (${warnings.length}):`);
719
+ console.log(` ⚠️ NOTES (${warnings.length}):`);
645
720
  warnings.forEach(w => console.log(` [${w.type}] ${w.file}: ${w.msg}`));
646
721
  console.log();
647
722
  }
@@ -649,13 +724,18 @@ async function main() {
649
724
  console.log(" ✅ All content validation passed\n");
650
725
  }
651
726
 
652
- // Record in stale-report
727
+ // Record in stale-report. Field names (`contentErrors`, `contentWarnings`)
728
+ // stay stable because they are part of stale-report.json's public schema
729
+ // that health-checker and external CI consumers read.
653
730
  updateStaleReport(GEN_DIR, "contentValidation",
654
731
  { checkedAt: new Date().toISOString(), checked, errors: errors.length, warnings: warnings.length, details: { errors, warnings } },
655
732
  { contentErrors: errors.length, contentWarnings: warnings.length }
656
733
  );
657
734
 
658
- console.log(` Total: ${errors.length} errors, ${warnings.length} warnings\n`);
735
+ console.log(` Total: ${errors.length} advisories, ${warnings.length} notes\n`);
736
+ // Exit code preserved (advisories → 1) so health-checker can still gate
737
+ // on this tool. The `init` orchestrator displays the result as a soft
738
+ // advisory regardless (see runContentValidator in bin/commands/init.js).
659
739
  process.exit(errors.length > 0 ? 1 : 0);
660
740
  }
661
741
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudeos-core",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "Auto-generate Claude Code documentation from your actual source code — Standards, Rules, Skills, and Guides tailored to your project",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {
@@ -52,38 +52,73 @@ DO NOT:
52
52
  The 8 canonical section headings below (`## 1. Role Definition` through
53
53
  `## 8. Common Rules & Memory (L4)`) use English as their canonical form.
54
54
  When generating CLAUDE.md in a non-English output language, the English
55
- canonical heading MUST remain the primary heading text. A native-language
56
- translation MAY be appended in parentheses, but is optional.
57
-
58
- This rule exists because multiple projects in the same organization will
59
- have their CLAUDE.md files consumed together (multi-repo grep, cross-repo
60
- navigation, side-by-side review). When every project uses a different
61
- native-language translation for the same section, even when the structure
62
- is otherwise identical, discoverability breaks: `grep "## 7. DO NOT Read"`
63
- no longer matches all siblings.
55
+ canonical heading MUST remain the primary heading text AND a
56
+ native-language translation MUST be appended in parentheses.
57
+
58
+ This dual requirement exists for two reasons. First, multiple projects
59
+ in the same organization will have their CLAUDE.md files consumed
60
+ together (multi-repo grep, cross-repo navigation, side-by-side review).
61
+ When every project uses a different native-language translation for the
62
+ same section, even when the structure is otherwise identical,
63
+ discoverability breaks: `grep "## 7. DO NOT Read"` no longer matches
64
+ all siblings. Second, when the CONTENT below the heading is written in
65
+ the target language (e.g., Korean body), a Korean reader scanning the
66
+ document benefits from seeing a Korean gloss next to the English
67
+ canonical token — the heading reads both as a stable grep target AND as
68
+ an intelligible label. Run-to-run consistency matters too: gloss
69
+ inclusion must not vary between generations of the same project.
64
70
 
65
71
  Format rule:
66
- - Primary (required): English canonical heading exactly as listed.
67
- - Parenthetical (optional): native-language translation, added at the end.
72
+ - Primary (REQUIRED): English canonical heading exactly as listed.
73
+ - Parenthetical (REQUIRED when `{OUTPUT_LANG}` != `en`, OMITTED when
74
+ `{OUTPUT_LANG}` == `en`): native-language translation, added at the
75
+ end in parentheses.
76
+
77
+ Canonical translations — use these verbatim when `{OUTPUT_LANG}` matches:
78
+
79
+ | Code | §1 | §2 | §3 | §4 | §5 | §6 | §7 | §8 |
80
+ |-------|----|----|----|----|----|----|----|----|
81
+ | `en` | (no gloss — English is canonical) |
82
+ | `ko` | (역할 정의) | (프로젝트 개요) | (빌드 및 실행 명령어) | (핵심 아키텍처) | (디렉터리 구조) | (Standard / Rules / Skills 참조) | (직접 읽지 말아야 할 파일) | (공통 규칙 및 메모리 (L4)) |
83
+ | `zh-CN` | (角色定义) | (项目概述) | (构建和运行命令) | (核心架构) | (目录结构) | (Standard / Rules / Skills 参考) | (请勿直接读取的文件) | (通用规则与内存 (L4)) |
84
+ | `ja` | (役割定義) | (プロジェクト概要) | (ビルド&実行コマンド) | (コアアーキテクチャ) | (ディレクトリ構造) | (Standard / Rules / Skills 参照) | (直接読まないファイル) | (共通ルール&メモリ (L4)) |
85
+ | `es` | (Definición del rol) | (Resumen del proyecto) | (Comandos de compilación y ejecución) | (Arquitectura central) | (Estructura de directorios) | (Referencia Standard / Rules / Skills) | (Archivos que NO se deben leer) | (Reglas comunes y memoria (L4)) |
86
+ | `vi` | (Định nghĩa vai trò) | (Tổng quan dự án) | (Lệnh build và chạy) | (Kiến trúc cốt lõi) | (Cấu trúc thư mục) | (Tham chiếu Standard / Rules / Skills) | (Tệp KHÔNG được đọc) | (Quy tắc chung & bộ nhớ (L4)) |
87
+ | `hi` | (भूमिका परिभाषा) | (परियोजना अवलोकन) | (बिल्ड और रन कमांड) | (मुख्य आर्किटेक्चर) | (निर्देशिका संरचना) | (Standard / Rules / Skills संदर्भ) | (न पढ़ने योग्य फ़ाइलें) | (सामान्य नियम और मेमोरी (L4)) |
88
+ | `ru` | (Определение роли) | (Обзор проекта) | (Команды сборки и запуска) | (Основная архитектура) | (Структура каталогов) | (Справочник Standard / Rules / Skills) | (Файлы, которые НЕ следует читать) | (Общие правила и память (L4)) |
89
+ | `fr` | (Définition du rôle) | (Aperçu du projet) | (Commandes de build et d'exécution) | (Architecture principale) | (Structure des répertoires) | (Référence Standard / Rules / Skills) | (Fichiers à NE PAS lire) | (Règles communes & mémoire (L4)) |
90
+ | `de` | (Rollendefinition) | (Projektübersicht) | (Build- und Ausführungsbefehle) | (Kernarchitektur) | (Verzeichnisstruktur) | (Standard / Rules / Skills-Referenz) | (Nicht zu lesende Dateien) | (Gemeinsame Regeln & Speicher (L4)) |
68
91
 
69
92
  Examples (ko output):
70
93
 
71
- ✅ `## 7. DO NOT Read`
94
+ ✅ `## 1. Role Definition (역할 정의)`
72
95
  ✅ `## 7. DO NOT Read (직접 읽지 말아야 할 파일)`
96
+ ❌ `## 1. Role Definition`
97
+ — gloss is required when OUTPUT_LANG != en
73
98
  ❌ `## 7. 읽지 말 것 (Files Not to Be Read Directly)`
74
99
  — English must be primary, not parenthetical
75
100
  ❌ `## 7. 읽지 말 것`
76
- — English canonical must appear
101
+ — English canonical must appear as primary
77
102
 
78
103
  Examples (ja output):
79
104
 
80
105
  ✅ `## 7. DO NOT Read (直接読まないファイル)`
106
+ ❌ `## 7. DO NOT Read`
107
+ — gloss is required when OUTPUT_LANG != en
81
108
  ❌ `## 7. 直接読まないファイル (DO NOT Read)`
109
+ — English must be primary, not parenthetical
110
+
111
+ Examples (en output):
112
+
113
+ ✅ `## 7. DO NOT Read`
114
+ ❌ `## 7. DO NOT Read (Files Not to Be Read Directly)`
115
+ — gloss must be OMITTED when OUTPUT_LANG == en
116
+ (the English canonical already serves as the label)
82
117
 
83
- The same rule applies to all 8 sections. When in doubt, emit only the
84
- English canonical heading; the parenthetical translation is a courtesy,
85
- not a requirement. The CONTENT below the heading is still written
86
- entirely in the target language.
118
+ For `{OUTPUT_LANG}` codes not in the table above, translate each
119
+ canonical heading semantically into the target language and append in
120
+ parentheses following the same pattern. The CONTENT below the heading
121
+ is written entirely in the target language regardless of the gloss.
87
122
 
88
123
  DO:
89
124
  - Adapt content within each section to project facts from pass2-merged.json
@@ -101,8 +136,20 @@ Use the following structure EXACTLY:
101
136
 
102
137
  > {1-2 line project intro}
103
138
 
139
+ {!-- SECTION HEADING RULE (applies to all 8 ## headings below):
140
+ The heading format "## N. English Canonical" shown in this scaffold
141
+ is the en (English) form. For non-English OUTPUT_LANG, APPEND the
142
+ canonical gloss in parentheses per the "Section heading format"
143
+ table earlier in this scaffold.
144
+ Example (ko): `## 1. Role Definition (역할 정의)`
145
+ Example (en): `## 1. Role Definition` (no gloss)
146
+ --}
147
+
104
148
  ## 1. Role Definition
105
149
 
150
+ {!-- Line 1 below is in English for reference only. When OUTPUT_LANG != en,
151
+ REPLACE with the canonical translation from "Section 1: Role Definition"
152
+ rules further down in this scaffold. See the 10-language canonical list. --}
106
153
  As the senior developer for this repository, you are responsible for writing, modifying, and reviewing code. Responses must be written in {OUTPUT_LANG}.
107
154
  {PROJECT_CONTEXT}.
108
155
 
@@ -242,11 +289,45 @@ Unlike rules that auto-load via `paths` glob, this layer is referenced **on-dema
242
289
 
243
290
  **Structure**: EXACTLY 2 lines.
244
291
 
245
- **Line 1** (fixed text with `{OUTPUT_LANG}` substitution; emit in the target output language):
292
+ **Line 1** (fixed meaning, EMIT IN THE TARGET OUTPUT LANGUAGE — do NOT copy the English reference verbatim unless `{OUTPUT_LANG}` is English):
293
+
294
+ The sentence below is the **semantic reference in English**. Translate it
295
+ fully into `{OUTPUT_LANG}`. The English text is NOT a literal template —
296
+ copying it verbatim for a non-English target produces the ironic bug of
297
+ writing "Responses must be written in Korean" in English.
298
+
299
+ English reference (semantic, for `en` output only):
246
300
  ```
247
301
  As the senior developer for this repository, you are responsible for writing, modifying, and reviewing code. Responses must be written in {OUTPUT_LANG}.
248
302
  ```
249
303
 
304
+ **Canonical translations** — emit these VERBATIM when `{OUTPUT_LANG}` matches
305
+ the target language. If `{OUTPUT_LANG}` is some other language not listed
306
+ here, translate the English reference following the same semantic
307
+ structure (senior developer + write/modify/review code + respond in target
308
+ language):
309
+
310
+ - `en` →
311
+ `As the senior developer for this repository, you are responsible for writing, modifying, and reviewing code. Responses must be written in English.`
312
+ - `ko` (한국어) →
313
+ `이 리포지토리의 시니어 개발자로서 코드 작성, 수정, 리뷰를 책임진다. 응답은 한국어로 작성해야 한다.`
314
+ - `zh-CN` (简体中文) →
315
+ `作为此仓库的高级开发者,负责代码的编写、修改和审查。回答必须使用简体中文。`
316
+ - `ja` (日本語) →
317
+ `このリポジトリのシニア開発者として、コードの記述、修正、レビューを担当する。回答は日本語で記述すること。`
318
+ - `es` (Español) →
319
+ `Como desarrollador senior de este repositorio, eres responsable de escribir, modificar y revisar código. Las respuestas deben estar escritas en español.`
320
+ - `vi` (Tiếng Việt) →
321
+ `Với tư cách là lập trình viên cấp cao của kho lưu trữ này, bạn chịu trách nhiệm viết, sửa đổi và đánh giá mã. Các câu trả lời phải được viết bằng tiếng Việt.`
322
+ - `hi` (हिन्दी) →
323
+ `इस रिपॉजिटरी के वरिष्ठ डेवलपर के रूप में, आप कोड लिखने, संशोधित करने और समीक्षा करने के लिए ज़िम्मेदार हैं। उत्तर हिन्दी में लिखे जाने चाहिए।`
324
+ - `ru` (Русский) →
325
+ `Как старший разработчик этого репозитория, вы отвечаете за написание, изменение и проверку кода. Ответы должны быть написаны на русском языке.`
326
+ - `fr` (Français) →
327
+ `En tant que développeur senior de ce dépôt, vous êtes responsable de l'écriture, de la modification et de la révision du code. Les réponses doivent être rédigées en français.`
328
+ - `de` (Deutsch) →
329
+ `Als Senior-Entwickler dieses Repositorys sind Sie für das Schreiben, Ändern und Überprüfen von Code verantwortlich. Antworten müssen auf Deutsch verfasst werden.`
330
+
250
331
  **Line 2** (`{PROJECT_CONTEXT}` — dynamically generated, Level 2 abstraction):
251
332
 
252
333
  Generate `{PROJECT_CONTEXT}` as a single sentence combining:
@@ -624,10 +705,27 @@ Before finalizing, verify:
624
705
  Structure, Standard / Rules / Skills Reference, DO NOT Read,
625
706
  Common Rules & Memory (L4)) — rendered in the target output
626
707
  language with equivalent meaning and order
708
+ - [ ] **Section heading gloss rule:**
709
+ If `{OUTPUT_LANG}` != `en`, EVERY one of the 8 `##` headings MUST
710
+ carry the parenthetical native-language gloss from the canonical
711
+ table in "Section heading format". Example (ko):
712
+ `## 1. Role Definition (역할 정의)`, `## 7. DO NOT Read (직접 읽지 말아야 할 파일)`.
713
+ If any heading is missing its gloss while `{OUTPUT_LANG}` != `en`,
714
+ the output is NON-COMPLIANT — add the gloss before finalizing.
715
+ - [ ] **English gloss-absence rule:**
716
+ If `{OUTPUT_LANG}` == `en`, headings MUST NOT carry a parenthetical
717
+ gloss (the English canonical already serves as the label).
718
+ `## 7. DO NOT Read` is correct; `## 7. DO NOT Read (Files Not to Be
719
+ Read Directly)` is incorrect for `en` output.
627
720
  - [ ] Section order is correct
628
721
  - [ ] No sections renamed, merged, or split
629
722
  - [ ] No sections added beyond the 8
630
723
  - [ ] Section 1 is 2 lines
724
+ - [ ] Section 1 Line 1 is in `{OUTPUT_LANG}` — matches the canonical
725
+ translation in "Section 1: Role Definition" rules (if
726
+ `{OUTPUT_LANG}` is one of the 10 canonical codes). If Line 1
727
+ contains the English phrase "As the senior developer" while
728
+ `{OUTPUT_LANG}` is NOT `en`, the translation was skipped — fix it.
631
729
  - [ ] Section 2 is 8-12 rows table
632
730
  - [ ] Section 5 has at most 3 emphasis bullets below tree
633
731
  - [ ] Section 6 has EXACTLY 3 sub-sections (Standard / Rules / Skills)
@@ -648,8 +746,12 @@ Before finalizing, verify:
648
746
 
649
747
  ### Example: Section 1 for different stacks
650
748
 
651
- Examples below are shown in English. Emit the final output in the
652
- target output language; the semantic content should match.
749
+ ⚠️ **Language note:** The English examples below show the SEMANTIC
750
+ structure only. Line 1 of Section 1 MUST be emitted in `{OUTPUT_LANG}` —
751
+ use the canonical translations from "Section 1: Role Definition" rules
752
+ above. Line 2 (PROJECT_CONTEXT) should also be written in
753
+ `{OUTPUT_LANG}`, keeping technical identifiers (framework names, pattern
754
+ names, stack labels) in their standard English form where idiomatic.
653
755
 
654
756
  **Java Spring Boot backend**:
655
757
  ```