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.
- package/CHANGELOG.md +84 -1
- package/CONTRIBUTING.md +15 -4
- package/README.de.md +187 -11
- package/README.es.md +187 -11
- package/README.fr.md +187 -11
- package/README.hi.md +187 -11
- package/README.ja.md +186 -10
- package/README.ko.md +331 -364
- package/README.md +200 -11
- package/README.ru.md +187 -11
- package/README.vi.md +188 -12
- package/README.zh-CN.md +186 -10
- package/bin/cli.js +183 -61
- package/bootstrap.sh +128 -21
- package/content-validator/index.js +131 -60
- package/health-checker/index.js +29 -23
- package/import-linter/index.js +14 -8
- package/manifest-generator/index.js +26 -20
- package/package.json +84 -75
- package/pass-json-validator/index.js +92 -70
- package/pass-prompts/templates/common/header.md +4 -4
- package/pass-prompts/templates/common/lang-instructions.json +27 -0
- package/pass-prompts/templates/common/pass3-footer.md +2 -3
- package/pass-prompts/templates/java-spring/pass1.md +84 -81
- package/pass-prompts/templates/java-spring/pass2.md +66 -66
- package/pass-prompts/templates/java-spring/pass3.md +60 -60
- package/pass-prompts/templates/kotlin-spring/pass1.md +172 -0
- package/pass-prompts/templates/kotlin-spring/pass2.md +109 -0
- package/pass-prompts/templates/kotlin-spring/pass3.md +98 -0
- package/pass-prompts/templates/node-express/pass1.md +73 -73
- package/pass-prompts/templates/node-express/pass2.md +66 -66
- package/pass-prompts/templates/node-express/pass3.md +53 -53
- package/pass-prompts/templates/node-nextjs/pass1.md +68 -68
- package/pass-prompts/templates/node-nextjs/pass2.md +61 -61
- package/pass-prompts/templates/node-nextjs/pass3.md +48 -48
- package/pass-prompts/templates/python-django/pass1.md +78 -78
- package/pass-prompts/templates/python-django/pass2.md +69 -69
- package/pass-prompts/templates/python-django/pass3.md +45 -45
- package/pass-prompts/templates/python-fastapi/pass1.md +76 -76
- package/pass-prompts/templates/python-fastapi/pass2.md +67 -67
- package/pass-prompts/templates/python-fastapi/pass3.md +45 -45
- package/plan-installer/index.js +623 -97
- package/plan-validator/index.js +54 -23
- package/sync-checker/index.js +25 -14
package/plan-validator/index.js
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* ClaudeOS-Core — Plan Validator
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* --check : drift/missing
|
|
9
|
-
* --refresh :
|
|
10
|
-
* --execute : Plan →
|
|
6
|
+
* Role: Validate and sync Master Plan (plan/) <file> blocks ↔ disk 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
|
-
*
|
|
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
|
-
//
|
|
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
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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(` ✅
|
|
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 exists — compare 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-
|
|
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
|
}
|
package/sync-checker/index.js
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* ClaudeOS-Core — Sync Checker
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Unregistered:
|
|
9
|
-
* - Orphaned:
|
|
6
|
+
* Role: Check disk ↔ Master 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
|
-
*
|
|
12
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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);
|