claudeos-core 1.0.7 → 1.2.0

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.

Potentially problematic release.


This version of claudeos-core might be problematic. Click here for more details.

Files changed (44) hide show
  1. package/CHANGELOG.md +84 -1
  2. package/CONTRIBUTING.md +15 -4
  3. package/README.de.md +187 -11
  4. package/README.es.md +187 -11
  5. package/README.fr.md +187 -11
  6. package/README.hi.md +187 -11
  7. package/README.ja.md +186 -10
  8. package/README.ko.md +331 -364
  9. package/README.md +200 -11
  10. package/README.ru.md +187 -11
  11. package/README.vi.md +188 -12
  12. package/README.zh-CN.md +186 -10
  13. package/bin/cli.js +183 -61
  14. package/bootstrap.sh +128 -21
  15. package/content-validator/index.js +131 -60
  16. package/health-checker/index.js +29 -23
  17. package/import-linter/index.js +14 -8
  18. package/manifest-generator/index.js +26 -20
  19. package/package.json +84 -75
  20. package/pass-json-validator/index.js +92 -70
  21. package/pass-prompts/templates/common/header.md +4 -4
  22. package/pass-prompts/templates/common/lang-instructions.json +27 -0
  23. package/pass-prompts/templates/common/pass3-footer.md +2 -3
  24. package/pass-prompts/templates/java-spring/pass1.md +84 -81
  25. package/pass-prompts/templates/java-spring/pass2.md +66 -66
  26. package/pass-prompts/templates/java-spring/pass3.md +60 -60
  27. package/pass-prompts/templates/kotlin-spring/pass1.md +172 -0
  28. package/pass-prompts/templates/kotlin-spring/pass2.md +109 -0
  29. package/pass-prompts/templates/kotlin-spring/pass3.md +98 -0
  30. package/pass-prompts/templates/node-express/pass1.md +73 -73
  31. package/pass-prompts/templates/node-express/pass2.md +66 -66
  32. package/pass-prompts/templates/node-express/pass3.md +53 -53
  33. package/pass-prompts/templates/node-nextjs/pass1.md +68 -68
  34. package/pass-prompts/templates/node-nextjs/pass2.md +61 -61
  35. package/pass-prompts/templates/node-nextjs/pass3.md +48 -48
  36. package/pass-prompts/templates/python-django/pass1.md +78 -78
  37. package/pass-prompts/templates/python-django/pass2.md +69 -69
  38. package/pass-prompts/templates/python-django/pass3.md +45 -45
  39. package/pass-prompts/templates/python-fastapi/pass1.md +76 -76
  40. package/pass-prompts/templates/python-fastapi/pass2.md +67 -67
  41. package/pass-prompts/templates/python-fastapi/pass3.md +45 -45
  42. package/plan-installer/index.js +623 -97
  43. package/plan-validator/index.js +54 -23
  44. package/sync-checker/index.js +25 -14
@@ -3,13 +3,13 @@
3
3
  /**
4
4
  * ClaudeOS-Core — Plan Validator
5
5
  *
6
- * 역할: 마스터 플랜(plan/) <file> 블록디스크 파일 정합성 검증 및 동기화
7
- * 모드:
8
- * --check : drift/missing 감지만 (기본값, CI/CD)
9
- * --refresh : 디스크 → Plan 갱신 (파일 직접 수정 )
10
- * --execute : Plan → 디스크 복원 (파일이 깨졌을 )
6
+ * Role: Validate and sync Master Plan (plan/) <file> blocksdisk files
7
+ * Modes:
8
+ * --check : detect drift/missing only (default, for CI/CD)
9
+ * --refresh : sync disk → Plan (after manual file edits)
10
+ * --execute : restore Plan → disk (when files are corrupted)
11
11
  *
12
- * 실행: npx claudeos-core <cmd> 또는 node claudeos-core-tools/plan-validator/index.js [--check|--refresh|--execute]
12
+ * Usage: npx claudeos-core <cmd> or node claudeos-core-tools/plan-validator/index.js [--check|--refresh|--execute]
13
13
  */
14
14
 
15
15
  const fs = require("fs");
@@ -20,14 +20,14 @@ const ROOT = process.env.CLAUDEOS_ROOT || path.resolve(__dirname, "../..");
20
20
  const PLAN = path.join(ROOT, "claudeos-core/plan");
21
21
  const GEN = path.join(ROOT, "claudeos-core/generated");
22
22
 
23
- // 코드블록 방식을 사용하는 플랜 파일 목록 (나머지는 <file> 블록 방식)
23
+ // Plan files using code block format (the rest use <file> block format)
24
24
  const CODE_BLOCK_PLANS = ["21.sync-rules-master.md"];
25
25
 
26
26
  function rel(p) {
27
27
  return path.relative(ROOT, p).replace(/\\/g, "/");
28
28
  }
29
29
 
30
- // <file path="..."> ... </file> 블록 추출
30
+ // Extract <file path="..."> ... </file> blocks
31
31
  function extractFileBlocks(content) {
32
32
  const result = [];
33
33
  let m;
@@ -38,7 +38,7 @@ function extractFileBlocks(content) {
38
38
  return result;
39
39
  }
40
40
 
41
- // ## N. `path` \n```markdown ... ``` 블록 추출
41
+ // Extract ## N. `path` \n```markdown ... ``` blocks
42
42
  function extractCodeBlocks(content) {
43
43
  const result = [];
44
44
  let m;
@@ -49,7 +49,7 @@ function extractCodeBlocks(content) {
49
49
  return result;
50
50
  }
51
51
 
52
- // <file> 블록 내용을 내용으로 교체
52
+ // Replace <file> block content with new content
53
53
  function replaceFileBlock(content, filePath, newContent) {
54
54
  const escaped = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
55
55
  return content.replace(
@@ -58,13 +58,41 @@ function replaceFileBlock(content, filePath, newContent) {
58
58
  );
59
59
  }
60
60
 
61
- // 코드블록 내용을 내용으로 교체
61
+ // Replace code block content with new content
62
+ // Uses indexOf-based approach to avoid regex issues with nested code fences
62
63
  function replaceCodeBlock(content, filePath, newContent) {
63
- const escaped = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/`/g, "");
64
- return content.replace(
65
- new RegExp(`(##\\s+\\d+\\.\\s+\`?${escaped}\`?\\s*\\n+\`\`\`markdown\\n)[\\s\\S]*?(\`\`\`)`, "g"),
66
- `$1${newContent}\n$2`
67
- );
64
+ const cleanPath = filePath.replace(/`/g, "");
65
+ // Find the heading line containing the file path
66
+ const headingPattern = new RegExp(`^##\\s+\\d+\\.\\s+\`?${cleanPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\`?`, "m");
67
+ const headingMatch = headingPattern.exec(content);
68
+ if (!headingMatch) return content;
69
+ // Find the opening ```markdown after the heading
70
+ const afterHeading = content.indexOf("```markdown\n", headingMatch.index);
71
+ if (afterHeading < 0) return content;
72
+ const contentStart = afterHeading + "```markdown\n".length;
73
+ // Find the closing ``` that is on its own line (not nested)
74
+ // Track nesting depth to skip inner fenced blocks
75
+ let searchPos = contentStart;
76
+ let closingPos = -1;
77
+ let nestDepth = 0;
78
+ while (searchPos < content.length) {
79
+ const nextFence = content.indexOf("\n```", searchPos);
80
+ if (nextFence < 0) break;
81
+ const lineAfterFence = content.substring(nextFence + 4, content.indexOf("\n", nextFence + 4));
82
+ const isOpening = lineAfterFence.trim().length > 0 && !lineAfterFence.startsWith("\n");
83
+ if (isOpening) {
84
+ nestDepth++;
85
+ searchPos = nextFence + 4;
86
+ } else if (nestDepth > 0) {
87
+ nestDepth--;
88
+ searchPos = nextFence + 4;
89
+ } else {
90
+ closingPos = nextFence + 1; // position of the closing ```
91
+ break;
92
+ }
93
+ }
94
+ if (closingPos < 0) return content;
95
+ return content.substring(0, contentStart) + newContent + "\n" + content.substring(closingPos);
68
96
  }
69
97
 
70
98
  async function main() {
@@ -98,13 +126,13 @@ async function main() {
98
126
  total++;
99
127
  const abs = path.join(ROOT, b.path);
100
128
 
101
- // 파일이 없는 경우
129
+ // File does not exist
102
130
  if (!fs.existsSync(abs)) {
103
131
  if (mode === "--execute") {
104
132
  const dir = path.dirname(abs);
105
133
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
106
134
  fs.writeFileSync(abs, b.content + "\n");
107
- console.log(` ✅ RESTORED: ${b.path}`);
135
+ console.log(` ✅ CREATED: ${b.path}`);
108
136
  synced++;
109
137
  } else {
110
138
  console.log(` ❌ MISSING: ${b.path}`);
@@ -114,7 +142,7 @@ async function main() {
114
142
  continue;
115
143
  }
116
144
 
117
- // 파일이 있는 경우 내용 비교
145
+ // File existscompare content
118
146
  const diskContent = fs.readFileSync(abs, "utf-8").trimEnd();
119
147
  const planContent = b.content.trimEnd();
120
148
 
@@ -129,8 +157,11 @@ async function main() {
129
157
  console.log(` 🔄 REFRESHED: ${b.path}`);
130
158
  synced++;
131
159
  } else if (mode === "--execute") {
160
+ // Backup existing file before overwrite
161
+ const backupPath = abs + ".bak";
162
+ fs.copyFileSync(abs, backupPath);
132
163
  fs.writeFileSync(abs, b.content + "\n");
133
- console.log(` ✅ RESTORED: ${b.path}`);
164
+ console.log(` ✅ RESTORED: ${b.path} (backup: ${b.path}.bak)`);
134
165
  synced++;
135
166
  } else {
136
167
  console.log(` ⚠️ DRIFT: ${b.path}`);
@@ -146,14 +177,14 @@ async function main() {
146
177
  }
147
178
  }
148
179
 
149
- // ─── 결과 요약 ──────────────────────────────────────────
180
+ // ─── Results summary ──────────────────────────────────────────
150
181
  console.log(`\n Total: ${total} | Synced: ${synced} | Drift: ${drift} | Missing: ${missing}`);
151
182
  console.log(drift + missing === 0 ? " ✅ All plans in sync\n" : ` ⚠️ ${drift + missing} issues\n`);
152
183
 
153
- // ─── plan-manifest.json 갱신 ───────────────────────────
184
+ // ─── Update plan-sync-status.json (separate from manifest-generator's plan-manifest.json) ──
154
185
  if (fs.existsSync(GEN)) {
155
186
  fs.writeFileSync(
156
- path.join(GEN, "plan-manifest.json"),
187
+ path.join(GEN, "plan-sync-status.json"),
157
188
  JSON.stringify({ generatedAt: new Date().toISOString(), lastMode: mode, total, synced, drift, missing, issues: results }, null, 2)
158
189
  );
159
190
  }
@@ -3,13 +3,13 @@
3
3
  /**
4
4
  * ClaudeOS-Core — Sync Checker
5
5
  *
6
- * 역할: sync-map.json 기준으로 디스크 마스터 플랜 동기화 상태 확인
7
- * 감지 항목:
8
- * - Unregistered: 디스크에 존재하지만 플랜에 등록되지 않은 파일
9
- * - Orphaned: 플랜에 등록됐지만 디스크에 없는 파일
6
+ * Role: Check diskMaster Plan sync status based on sync-map.json
7
+ * Detection items:
8
+ * - Unregistered: file exists on disk but not registered in any plan
9
+ * - Orphaned: registered in plan but missing from disk
10
10
  *
11
- * 실행: npx claudeos-core <cmd> 또는 node claudeos-core-tools/sync-checker/index.js
12
- * 의존: manifest-generator 선행 실행 필요 (sync-map.json)
11
+ * Usage: npx claudeos-core <cmd> or node claudeos-core-tools/sync-checker/index.js
12
+ * Depends: manifest-generator must run first (sync-map.json)
13
13
  */
14
14
 
15
15
  const fs = require("fs");
@@ -43,11 +43,17 @@ async function main() {
43
43
  process.exit(1);
44
44
  }
45
45
 
46
- const sm = JSON.parse(fs.readFileSync(SMP, "utf-8"));
46
+ let sm;
47
+ try {
48
+ sm = JSON.parse(fs.readFileSync(SMP, "utf-8"));
49
+ } catch (e) {
50
+ console.log(` ❌ sync-map.json is malformed: ${e.message}\n`);
51
+ process.exit(1);
52
+ }
47
53
  const reg = new Set(sm.mappings.map((m) => m.sourcePath));
48
54
  const issues = { unreg: [], orphan: [] };
49
55
 
50
- // ─── [1/2] Disk → Plan: 등록되지 않은 파일 탐지 ───────
56
+ // ─── [1/2] Disk → Plan: detect unregistered files ───────
51
57
  console.log(" [1/2] Disk → Plan...");
52
58
  for (const t of TRACKED) {
53
59
  const abs = path.join(ROOT, t.dir);
@@ -62,12 +68,12 @@ async function main() {
62
68
  }
63
69
  }
64
70
 
65
- // CLAUDE.md 별도 확인
71
+ // Check CLAUDE.md separately
66
72
  if (fs.existsSync(path.join(ROOT, "CLAUDE.md")) && !reg.has("CLAUDE.md")) {
67
73
  issues.unreg.push({ path: "CLAUDE.md", domain: "root" });
68
74
  }
69
75
 
70
- // ─── [2/2] Plan → Disk: 고아 파일 탐지 ───────────────
76
+ // ─── [2/2] Plan → Disk: detect orphaned files ───────────────
71
77
  console.log(" [2/2] Plan → Disk...");
72
78
  for (const m of sm.mappings) {
73
79
  if (!fs.existsSync(path.join(ROOT, m.sourcePath))) {
@@ -75,7 +81,7 @@ async function main() {
75
81
  }
76
82
  }
77
83
 
78
- // ─── 결과 출력 ─────────────────────────────────────────
84
+ // ─── Output results ─────────────────────────────────────────
79
85
  if (issues.unreg.length) {
80
86
  console.log(`\n ⚠️ Unregistered (${issues.unreg.length}):`);
81
87
  issues.unreg.forEach((i) => console.log(` + ${i.path}`));
@@ -89,10 +95,13 @@ async function main() {
89
95
  console.log(`\n Registered: ${reg.size} | Unregistered: ${issues.unreg.length} | Orphaned: ${issues.orphan.length}`);
90
96
  console.log(total === 0 ? " ✅ All in sync\n" : ` ⚠️ ${total} issues\n`);
91
97
 
92
- // ─── stale-report.json 갱신 ────────────────────────────
98
+ // ─── Update stale-report.json ────────────────────────────
93
99
  if (fs.existsSync(GEN)) {
94
100
  const rp = path.join(GEN, "stale-report.json");
95
- const ex = fs.existsSync(rp) ? JSON.parse(fs.readFileSync(rp, "utf-8")) : {};
101
+ let ex = {};
102
+ if (fs.existsSync(rp)) {
103
+ try { ex = JSON.parse(fs.readFileSync(rp, "utf-8")); } catch { ex = {}; }
104
+ }
96
105
  ex.syncMisses = {
97
106
  checkedAt: new Date().toISOString(),
98
107
  unregistered: issues.unreg,
@@ -106,7 +115,9 @@ async function main() {
106
115
  fs.writeFileSync(rp, JSON.stringify(ex, null, 2));
107
116
  }
108
117
 
109
- process.exit(total > 0 ? 1 : 0);
118
+ // Exit 1 only for orphaned files (actual breakage), not for unregistered (informational)
119
+ const orphanCount = issues.orphan.length;
120
+ process.exit(orphanCount > 0 ? 1 : 0);
110
121
  }
111
122
 
112
123
  main().catch(console.error);