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
package/bin/cli.js CHANGED
@@ -5,12 +5,13 @@
5
5
  *
6
6
  * Node.js replacement for bootstrap.sh with cross-platform support.
7
7
  * Usage:
8
- * npx claudeos-core init ← Run 3-Pass pipeline
9
- * npx claudeos-core health Run health checker
10
- * npx claudeos-core validate ← Run plan validator (--check)
11
- * npx claudeos-core restore Restore from Master Plan
12
- * npx claudeos-core refresh Sync disk Plan
13
- * npx claudeos-core --help Show help
8
+ * npx claudeos-core init --lang ko ← Run 3-Pass pipeline (Korean output)
9
+ * npx claudeos-core init Interactive language selection
10
+ * npx claudeos-core health ← Run health checker
11
+ * npx claudeos-core validate Run plan validator (--check)
12
+ * npx claudeos-core restore Restore from Master Plan
13
+ * npx claudeos-core refresh Sync disk → Plan
14
+ * npx claudeos-core --help ← Show help
14
15
  *
15
16
  * Also works when cloned directly:
16
17
  * node claudeos-core-tools/bin/cli.js init
@@ -20,16 +21,75 @@ const { execSync } = require("child_process");
20
21
  const fs = require("fs");
21
22
  const path = require("path");
22
23
 
23
- // ─── 경로 설정 ──────────────────────────────────────────
24
+ // ─── Path configuration ──────────────────────────────────────────
24
25
  const TOOLS_DIR = path.resolve(__dirname, "..");
25
26
  const PROJECT_ROOT = process.cwd();
26
27
  const GENERATED_DIR = path.join(PROJECT_ROOT, "claudeos-core/generated");
27
28
 
28
- // 하위 도구(plan-installer ) 프로젝트 루트를 올바르게 인식하도록 환경변수 설정
29
- // (npm/npx 실행 __dirname 기반 계산이 틀리므로 필수)
29
+ // Set env var so sub-tools (plan-installer, etc.) correctly resolve the project root
30
+ // (Required because __dirname-based calculation is incorrect when run via npm/npx)
30
31
  process.env.CLAUDEOS_ROOT = PROJECT_ROOT;
31
32
 
32
- // ─── 유틸리티 ───────────────────────────────────────────
33
+ // ─── Language configuration ──────────────────────────────────────
34
+ const SUPPORTED_LANGS = {
35
+ en: "English",
36
+ ko: "한국어 (Korean)",
37
+ "zh-CN": "简体中文 (Chinese Simplified)",
38
+ ja: "日本語 (Japanese)",
39
+ es: "Español (Spanish)",
40
+ vi: "Tiếng Việt (Vietnamese)",
41
+ hi: "हिन्दी (Hindi)",
42
+ ru: "Русский (Russian)",
43
+ fr: "Français (French)",
44
+ de: "Deutsch (German)",
45
+ };
46
+ const LANG_CODES = Object.keys(SUPPORTED_LANGS);
47
+
48
+ function isValidLang(lang) {
49
+ return LANG_CODES.includes(lang);
50
+ }
51
+
52
+ // Interactive language selection (stdin prompt)
53
+ function selectLangInteractive() {
54
+ return new Promise((resolve) => {
55
+ const readline = require("readline");
56
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
57
+
58
+ log("");
59
+ log("╔══════════════════════════════════════════════════╗");
60
+ log("║ Select output language (required) ║");
61
+ log("╚══════════════════════════════════════════════════╝");
62
+ log("");
63
+ LANG_CODES.forEach((code, i) => {
64
+ log(` ${String(i + 1).padStart(2)}. ${code.padEnd(6)} — ${SUPPORTED_LANGS[code]}`);
65
+ });
66
+ log("");
67
+
68
+ rl.question(` Enter number (1-${LANG_CODES.length}) or language code: `, (answer) => {
69
+ rl.close();
70
+ const trimmed = answer.trim();
71
+
72
+ // Accept number
73
+ const num = parseInt(trimmed);
74
+ if (num >= 1 && num <= LANG_CODES.length) {
75
+ resolve(LANG_CODES[num - 1]);
76
+ return;
77
+ }
78
+
79
+ // Accept language code
80
+ if (isValidLang(trimmed)) {
81
+ resolve(trimmed);
82
+ return;
83
+ }
84
+
85
+ log(`\n ❌ Invalid selection: "${trimmed}"`);
86
+ log(` Supported: ${LANG_CODES.join(", ")}\n`);
87
+ process.exit(1);
88
+ });
89
+ });
90
+ }
91
+
92
+ // ─── Utilities ───────────────────────────────────────────
33
93
  function log(msg) {
34
94
  console.log(msg);
35
95
  }
@@ -46,6 +106,7 @@ function run(cmd, options = {}) {
46
106
  cwd: options.cwd || PROJECT_ROOT,
47
107
  stdio: options.silent ? ["pipe", "pipe", "pipe"] : "inherit",
48
108
  encoding: "utf-8",
109
+ timeout: options.timeout || 0, // no timeout (Pass 1-3 can take 10+ minutes)
49
110
  });
50
111
  return true;
51
112
  } catch (e) {
@@ -54,26 +115,20 @@ function run(cmd, options = {}) {
54
115
  }
55
116
  }
56
117
 
57
- // claude -p 실행: 프롬프트를 파일에 저장 파이프로 전달 (Windows 8191자 제한 우회)
118
+ // Run claude -p: pass prompt via stdin (no shell pipe avoids command injection)
58
119
  function runClaudePrompt(prompt, options = {}) {
59
- const tmpFile = path.join(GENERATED_DIR, "_tmp_prompt.md");
60
- fs.writeFileSync(tmpFile, prompt, "utf-8");
61
120
  try {
62
- const catCmd = process.platform === "win32" ? "type" : "cat";
63
- const escaped = escapeShellArg(tmpFile);
64
- const cmd = `${catCmd} ${escaped} | claude -p --dangerously-skip-permissions`;
65
- execSync(cmd, {
121
+ execSync("claude -p --dangerously-skip-permissions", {
122
+ input: prompt,
66
123
  cwd: options.cwd || PROJECT_ROOT,
67
- stdio: "inherit",
124
+ stdio: ["pipe", "inherit", "inherit"],
68
125
  encoding: "utf-8",
69
- shell: true,
126
+ timeout: 0, // no timeout (Claude analysis can be long-running)
70
127
  });
71
128
  return true;
72
129
  } catch (e) {
73
130
  if (options.ignoreError) return false;
74
131
  throw e;
75
- } finally {
76
- try { fs.unlinkSync(tmpFile); } catch {}
77
132
  }
78
133
  }
79
134
 
@@ -95,9 +150,19 @@ function injectProjectRoot(text) {
95
150
  return text.replace(/\{\{PROJECT_ROOT\}\}/g, PROJECT_ROOT);
96
151
  }
97
152
 
98
- // ─── 명령어: init ───────────────────────────────────────
153
+ // ─── Command: init ───────────────────────────────────────
99
154
  async function cmdInit() {
100
- // ─── 사전 조건 체크 ───────────────────────────────────
155
+ // ─── Prerequisites check ───────────────────────────────────
156
+ // Validate PROJECT_ROOT is a plausible project directory
157
+ const hasProjectMarker = [".git", "package.json", "build.gradle", "build.gradle.kts", "pom.xml", "pyproject.toml", "requirements.txt"].some(
158
+ m => fs.existsSync(path.join(PROJECT_ROOT, m))
159
+ );
160
+ if (!hasProjectMarker) {
161
+ log(`\n ⚠️ Warning: ${PROJECT_ROOT} does not look like a project root.`);
162
+ log(" No .git, package.json, build.gradle, or pom.xml found.");
163
+ log(" Run this command from your project directory.\n");
164
+ }
165
+
101
166
  const nodeVersion = parseInt(process.versions.node.split(".")[0]);
102
167
  if (nodeVersion < 18) {
103
168
  log(`\n ❌ Node.js v18+ required (current: v${process.versions.node})`);
@@ -113,21 +178,44 @@ async function cmdInit() {
113
178
  process.exit(1);
114
179
  }
115
180
 
181
+ // Verify Claude is authenticated (quick prompt test)
182
+ const claudeAuth = run('claude -p "echo ok" --max-tokens 10', { silent: true, ignoreError: true });
183
+ if (!claudeAuth) {
184
+ log("\n ⚠️ Claude Code may not be authenticated.");
185
+ log(" Run: claude (and complete authentication)");
186
+ log(" Then retry: npx claudeos-core init\n");
187
+ process.exit(1);
188
+ }
189
+
190
+ // ─── Language selection (required) ────────────────────────────
191
+ let lang = parsedArgs.lang;
192
+ if (!lang) {
193
+ // Interactive selection if --lang not provided
194
+ lang = await selectLangInteractive();
195
+ }
196
+ if (!isValidLang(lang)) {
197
+ log(`\n ❌ Unsupported language: "${lang}"`);
198
+ log(` Supported: ${LANG_CODES.join(", ")}\n`);
199
+ process.exit(1);
200
+ }
201
+ process.env.CLAUDEOS_LANG = lang;
202
+
116
203
  log("");
117
204
  log("╔════════════════════════════════════════════════════╗");
118
205
  log("║ ClaudeOS-Core — Bootstrap (3-Pass) ║");
119
206
  log("╚════════════════════════════════════════════════════╝");
120
207
  log(` Project root: ${PROJECT_ROOT}`);
208
+ log(` Language: ${SUPPORTED_LANGS[lang]} (${lang})`);
121
209
  log("");
122
210
 
123
- // ─── [1] 의존성 설치 ────────────────────────────────
211
+ // ─── [1] Install dependencies ────────────────────────────────
124
212
  header("[1] Installing dependencies...");
125
213
  if (!fileExists(path.join(TOOLS_DIR, "node_modules"))) {
126
214
  run("npm install --silent", { cwd: TOOLS_DIR });
127
215
  }
128
216
  log(" ✅ Done\n");
129
217
 
130
- // ─── [2] 디렉토리 구조 생성 ─────────────────────────
218
+ // ─── [2] Create directory structure ─────────────────────────
131
219
  header("[2] Creating directory structure...");
132
220
  const dirs = [
133
221
  ".claude/rules/00.core",
@@ -162,20 +250,27 @@ async function cmdInit() {
162
250
  }
163
251
  log(" ✅ Done\n");
164
252
 
165
- // ─── [3] plan-installer 실행 ─────────────────────────
253
+ // ─── [3] Run plan-installer ─────────────────────────
166
254
  header("[3] Analyzing project (plan-installer)...");
167
255
  run(`node "${path.join(TOOLS_DIR, "plan-installer/index.js")}"`);
168
256
  log("");
169
257
 
170
- // ─── [4] Pass 1: 도메인 그룹별 분석 ──────────────────
258
+ // ─── [4] Pass 1: Deep analysis per domain group ──────────────────
171
259
  header("[4] Pass 1 — Deep analysis per domain group...");
172
260
 
173
- const domainGroups = JSON.parse(
174
- readFile(path.join(GENERATED_DIR, "domain-groups.json"))
175
- );
261
+ let domainGroups;
262
+ try {
263
+ domainGroups = JSON.parse(
264
+ readFile(path.join(GENERATED_DIR, "domain-groups.json"))
265
+ );
266
+ } catch (e) {
267
+ log(` ❌ domain-groups.json is missing or malformed: ${e.message}`);
268
+ log(" Re-run plan-installer or check claudeos-core/generated/");
269
+ process.exit(1);
270
+ }
176
271
  const totalGroups = domainGroups.totalGroups;
177
272
 
178
- // 타입별 pass1 프롬프트 로드
273
+ // Load pass1 prompts by type
179
274
  const pass1Prompts = {};
180
275
  for (const type of ["backend", "frontend"]) {
181
276
  const promptFile = path.join(GENERATED_DIR, `pass1-${type}-prompt.md`);
@@ -183,12 +278,16 @@ async function cmdInit() {
183
278
  pass1Prompts[type] = readFile(promptFile);
184
279
  }
185
280
  }
186
- // 단일스택 하위 호환
281
+ // Single-stack backward compatibility
187
282
  if (Object.keys(pass1Prompts).length === 0) {
188
283
  const fallback = path.join(GENERATED_DIR, "pass1-prompt.md");
189
284
  if (fileExists(fallback)) pass1Prompts["backend"] = readFile(fallback);
190
285
  }
191
286
 
287
+ if (!domainGroups.groups || totalGroups !== domainGroups.groups.length) {
288
+ log(` ❌ domain-groups.json is malformed: expected ${totalGroups} groups, found ${domainGroups.groups ? domainGroups.groups.length : 0}`);
289
+ process.exit(1);
290
+ }
192
291
  for (let i = 1; i <= totalGroups; i++) {
193
292
  const group = domainGroups.groups[i - 1];
194
293
  const domainList = group.domains.join(", ");
@@ -207,14 +306,14 @@ async function cmdInit() {
207
306
  continue;
208
307
  }
209
308
 
210
- // 해당 타입의 프롬프트 선택
309
+ // Select prompt for this type
211
310
  const template = pass1Prompts[groupType] || pass1Prompts["backend"];
212
311
  if (!template) {
213
312
  log(` ❌ No pass1 prompt found for type: ${groupType}. Aborting.`);
214
313
  process.exit(1);
215
314
  }
216
315
 
217
- // 플레이스홀더 치환
316
+ // Placeholder substitution
218
317
  let prompt = template
219
318
  .replace(/\{\{DOMAIN_GROUP\}\}/g, domainList)
220
319
  .replace(/\{\{PASS_NUM\}\}/g, String(i));
@@ -236,7 +335,7 @@ async function cmdInit() {
236
335
  }
237
336
  log("");
238
337
 
239
- // ─── [5] Pass 2: 분석 결과 통합 ──────────────────────
338
+ // ─── [5] Pass 2: Merge analysis results ──────────────────────
240
339
  header("[5] Pass 2 — Merging analysis results...");
241
340
 
242
341
  const pass2Json = path.join(GENERATED_DIR, "pass2-merged.json");
@@ -263,7 +362,7 @@ async function cmdInit() {
263
362
  }
264
363
  log("");
265
364
 
266
- // ─── [6] Pass 3: 생성 + 검증 ─────────────────────────
365
+ // ─── [6] Pass 3: Generate + verify ─────────────────────────
267
366
  header("[6] Pass 3 — Generating all files...");
268
367
 
269
368
  let prompt = injectProjectRoot(
@@ -283,7 +382,7 @@ async function cmdInit() {
283
382
  }
284
383
  log("");
285
384
 
286
- // ─── [7] 검증 도구 실행 ───────────────────────────────
385
+ // ─── [7] Run verification tools ───────────────────────────────
287
386
  header("[7] Running verification tools...");
288
387
 
289
388
  const verifyTools = [
@@ -303,7 +402,7 @@ async function cmdInit() {
303
402
  }
304
403
  log("");
305
404
 
306
- // ─── 완료 ─────────────────────────────────────────────
405
+ // ─── Complete ─────────────────────────────────────────────
307
406
  const totalFiles = countFiles();
308
407
  const pass1Files = countPass1Files();
309
408
 
@@ -314,6 +413,7 @@ async function cmdInit() {
314
413
  log(`║ Files created: ${pad(String(totalFiles), 29)}║`);
315
414
  log(`║ Domains analyzed: ${pad(totalGroups + " groups", 29)}║`);
316
415
  log(`║ Analysis passes: ${pad(pass1Files + " pass1 files", 29)}║`);
416
+ log(`║ Output language: ${pad(SUPPORTED_LANGS[lang] || lang, 29)}║`);
317
417
  log("║ ║");
318
418
  log("║ Verify anytime: ║");
319
419
  log("║ npx claudeos-core health ║");
@@ -324,35 +424,27 @@ async function cmdInit() {
324
424
  log("");
325
425
  }
326
426
 
327
- // ─── 명령어: health ─────────────────────────────────────
427
+ // ─── Command: health ─────────────────────────────────────
328
428
  function cmdHealth() {
329
429
  run(`node "${path.join(TOOLS_DIR, "health-checker/index.js")}"`);
330
430
  }
331
431
 
332
- // ─── 명령어: validate ───────────────────────────────────
432
+ // ─── Command: validate ───────────────────────────────────
333
433
  function cmdValidate() {
334
434
  run(`node "${path.join(TOOLS_DIR, "plan-validator/index.js")}" --check`);
335
435
  }
336
436
 
337
- // ─── 명령어: restore ────────────────────────────────────
437
+ // ─── Command: restore ────────────────────────────────────
338
438
  function cmdRestore() {
339
439
  run(`node "${path.join(TOOLS_DIR, "plan-validator/index.js")}" --execute`);
340
440
  }
341
441
 
342
- // ─── 명령어: refresh ────────────────────────────────────
442
+ // ─── Command: refresh ────────────────────────────────────
343
443
  function cmdRefresh() {
344
444
  run(`node "${path.join(TOOLS_DIR, "plan-validator/index.js")}" --refresh`);
345
445
  }
346
446
 
347
- // ─── 헬퍼 ───────────────────────────────────────────────
348
- function escapeShellArg(arg) {
349
- // Cross-platform shell argument escaping
350
- if (process.platform === "win32") {
351
- return `"${arg.replace(/"/g, '\\"')}"`;
352
- }
353
- return `'${arg.replace(/'/g, "'\\''")}'`;
354
- }
355
-
447
+ // ─── Helpers ───────────────────────────────────────────────
356
448
  function pad(str, len) {
357
449
  return str.length >= len ? str : str + " ".repeat(len - str.length);
358
450
  }
@@ -360,10 +452,11 @@ function pad(str, len) {
360
452
  function countFiles() {
361
453
  try {
362
454
  let count = 0;
455
+ const skipDirs = ["node_modules", "generated"];
363
456
  const scan = (dir) => {
364
457
  if (!fs.existsSync(dir)) return;
365
458
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
366
- if (entry.name === "node_modules") continue;
459
+ if (skipDirs.includes(entry.name)) continue;
367
460
  const full = path.join(dir, entry.name);
368
461
  if (entry.isDirectory()) scan(full);
369
462
  else count++;
@@ -403,19 +496,44 @@ Commands:
403
496
  restore Restore all files from Master Plan
404
497
 
405
498
  Options:
499
+ --lang CODE Output language for generated files (required for init)
500
+ Supported: en, ko, zh-CN, ja, es, vi, hi, ru, fr, de
501
+ If omitted, interactive selection is shown.
406
502
  --help Show this help message
407
503
  --version Show version
408
504
 
409
505
  Examples:
410
- npx claudeos-core init # First-time setup
411
- npx claudeos-core health # Check everything is consistent
412
- npx claudeos-core restore # Recover from corrupted docs
506
+ npx claudeos-core init --lang ko # Generate in Korean
507
+ npx claudeos-core init --lang ja # Generate in Japanese
508
+ npx claudeos-core init # Interactive language selection
509
+ npx claudeos-core health # Check everything is consistent
510
+ npx claudeos-core restore # Recover from corrupted docs
413
511
  `);
414
512
  }
415
513
 
416
- // ─── 메인 ───────────────────────────────────────────────
514
+ // ─── Argument parser ────────────────────────────────────────
515
+ function parseArgs(argv) {
516
+ const result = { command: null, lang: null };
517
+ for (let i = 0; i < argv.length; i++) {
518
+ if (argv[i] === "--lang" && i + 1 < argv.length) {
519
+ result.lang = argv[++i];
520
+ } else if (argv[i].startsWith("--lang=")) {
521
+ result.lang = argv[i].split("=")[1];
522
+ } else if (argv[i] === "--help" || argv[i] === "-h") {
523
+ result.command = "--help";
524
+ } else if (argv[i] === "--version" || argv[i] === "-v") {
525
+ result.command = "--version";
526
+ } else if (!argv[i].startsWith("-") && !result.command) {
527
+ result.command = argv[i];
528
+ }
529
+ }
530
+ return result;
531
+ }
532
+
533
+ // ─── Main ───────────────────────────────────────────────
417
534
  const args = process.argv.slice(2);
418
- const command = args[0];
535
+ const parsedArgs = parseArgs(args);
536
+ const command = parsedArgs.command;
419
537
 
420
538
  if (!command || command === "--help" || command === "-h") {
421
539
  showHelp();
@@ -423,10 +541,14 @@ if (!command || command === "--help" || command === "-h") {
423
541
  }
424
542
 
425
543
  if (command === "--version" || command === "-v") {
426
- const pkg = JSON.parse(
427
- readFile(path.join(TOOLS_DIR, "package.json"))
428
- );
429
- log(`claudeos-core v${pkg.version}`);
544
+ try {
545
+ const pkg = JSON.parse(
546
+ readFile(path.join(TOOLS_DIR, "package.json"))
547
+ );
548
+ log(`claudeos-core v${pkg.version}`);
549
+ } catch {
550
+ log("claudeos-core (version unknown)");
551
+ }
430
552
  process.exit(0);
431
553
  }
432
554