claudeos-core 1.2.3 → 1.3.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 +66 -1
- package/README.de.md +41 -6
- package/README.es.md +42 -6
- package/README.fr.md +42 -6
- package/README.hi.md +42 -6
- package/README.ja.md +43 -6
- package/README.ko.md +42 -6
- package/README.md +53 -11
- package/README.ru.md +42 -6
- package/README.vi.md +42 -6
- package/README.zh-CN.md +42 -6
- package/bin/cli.js +171 -36
- package/bootstrap.sh +72 -23
- package/content-validator/index.js +9 -4
- package/health-checker/index.js +4 -3
- package/lib/safe-fs.js +110 -0
- package/manifest-generator/index.js +16 -20
- package/package.json +4 -2
- package/pass-json-validator/index.js +3 -5
- package/pass-prompts/templates/java-spring/pass1.md +3 -0
- package/pass-prompts/templates/java-spring/pass2.md +3 -3
- package/pass-prompts/templates/java-spring/pass3.md +17 -0
- package/pass-prompts/templates/kotlin-spring/pass1.md +3 -0
- package/pass-prompts/templates/kotlin-spring/pass2.md +5 -5
- package/pass-prompts/templates/kotlin-spring/pass3.md +17 -0
- package/pass-prompts/templates/node-express/pass1.md +3 -0
- package/pass-prompts/templates/node-express/pass2.md +4 -1
- package/pass-prompts/templates/node-express/pass3.md +20 -1
- package/pass-prompts/templates/node-nextjs/pass1.md +13 -3
- package/pass-prompts/templates/node-nextjs/pass2.md +6 -4
- package/pass-prompts/templates/node-nextjs/pass3.md +20 -1
- package/pass-prompts/templates/python-django/pass1.md +3 -1
- package/pass-prompts/templates/python-django/pass2.md +4 -4
- package/pass-prompts/templates/python-django/pass3.md +18 -0
- package/pass-prompts/templates/python-fastapi/pass1.md +3 -0
- package/pass-prompts/templates/python-fastapi/pass2.md +4 -4
- package/pass-prompts/templates/python-fastapi/pass3.md +18 -0
- package/plan-installer/domain-grouper.js +74 -0
- package/plan-installer/index.js +43 -1303
- package/plan-installer/prompt-generator.js +99 -0
- package/plan-installer/stack-detector.js +326 -0
- package/plan-installer/structure-scanner.js +783 -0
- package/plan-validator/index.js +84 -20
- package/sync-checker/index.js +7 -3
package/plan-validator/index.js
CHANGED
|
@@ -39,12 +39,48 @@ function extractFileBlocks(content) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// Extract ## N. `path` \n```markdown ... ``` blocks
|
|
42
|
+
// Uses indexOf-based parsing to correctly handle nested code fences inside markdown content
|
|
42
43
|
function extractCodeBlocks(content) {
|
|
43
44
|
const result = [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
while ((
|
|
47
|
-
|
|
45
|
+
const headingRe = /^##\s+\d+\.\s+([^\n]+)/gm;
|
|
46
|
+
let headingMatch;
|
|
47
|
+
while ((headingMatch = headingRe.exec(content)) !== null) {
|
|
48
|
+
const filePath = headingMatch[1].replace(/`/g, "").trim();
|
|
49
|
+
// Find the opening ```markdown after the heading
|
|
50
|
+
const openFence = content.indexOf("```markdown\n", headingMatch.index);
|
|
51
|
+
if (openFence < 0) continue;
|
|
52
|
+
const contentStart = openFence + "```markdown\n".length;
|
|
53
|
+
// Find the matching closing ``` — track nesting depth to skip inner fenced blocks
|
|
54
|
+
let searchPos = contentStart;
|
|
55
|
+
let closingPos = -1;
|
|
56
|
+
let nestDepth = 0;
|
|
57
|
+
while (searchPos < content.length) {
|
|
58
|
+
const nextFence = content.indexOf("\n```", searchPos);
|
|
59
|
+
if (nextFence < 0) break;
|
|
60
|
+
const fenceLineStart = nextFence + 1; // position of ```
|
|
61
|
+
// Determine end of this ``` line
|
|
62
|
+
const nextNewline = content.indexOf("\n", fenceLineStart + 3);
|
|
63
|
+
const restOfLine = nextNewline >= 0
|
|
64
|
+
? content.substring(fenceLineStart + 3, nextNewline)
|
|
65
|
+
: content.substring(fenceLineStart + 3);
|
|
66
|
+
// Opening fence: ``` followed by a language tag (non-empty alphanumeric text)
|
|
67
|
+
const isOpening = /^[a-zA-Z]/.test(restOfLine.trim());
|
|
68
|
+
if (isOpening) {
|
|
69
|
+
nestDepth++;
|
|
70
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
71
|
+
} else if (nestDepth > 0) {
|
|
72
|
+
nestDepth--;
|
|
73
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
74
|
+
} else {
|
|
75
|
+
closingPos = fenceLineStart;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (closingPos < 0) continue;
|
|
80
|
+
const blockContent = content.substring(contentStart, closingPos).trimEnd();
|
|
81
|
+
result.push({ path: filePath, content: blockContent });
|
|
82
|
+
// Advance headingRe past this block to avoid re-matching inside content
|
|
83
|
+
headingRe.lastIndex = closingPos;
|
|
48
84
|
}
|
|
49
85
|
return result;
|
|
50
86
|
}
|
|
@@ -70,24 +106,29 @@ function replaceCodeBlock(content, filePath, newContent) {
|
|
|
70
106
|
const afterHeading = content.indexOf("```markdown\n", headingMatch.index);
|
|
71
107
|
if (afterHeading < 0) return content;
|
|
72
108
|
const contentStart = afterHeading + "```markdown\n".length;
|
|
73
|
-
// Find the closing ```
|
|
74
|
-
// Track nesting depth to skip inner fenced blocks
|
|
109
|
+
// Find the matching closing ``` — track nesting depth to skip inner fenced blocks
|
|
75
110
|
let searchPos = contentStart;
|
|
76
111
|
let closingPos = -1;
|
|
77
112
|
let nestDepth = 0;
|
|
78
113
|
while (searchPos < content.length) {
|
|
79
114
|
const nextFence = content.indexOf("\n```", searchPos);
|
|
80
115
|
if (nextFence < 0) break;
|
|
81
|
-
const
|
|
82
|
-
|
|
116
|
+
const fenceLineStart = nextFence + 1; // position of ```
|
|
117
|
+
// Determine end of this ``` line
|
|
118
|
+
const nextNewline = content.indexOf("\n", fenceLineStart + 3);
|
|
119
|
+
const restOfLine = nextNewline >= 0
|
|
120
|
+
? content.substring(fenceLineStart + 3, nextNewline)
|
|
121
|
+
: content.substring(fenceLineStart + 3);
|
|
122
|
+
// Opening fence: ``` followed by a language tag (non-empty alphanumeric text)
|
|
123
|
+
const isOpening = /^[a-zA-Z]/.test(restOfLine.trim());
|
|
83
124
|
if (isOpening) {
|
|
84
125
|
nestDepth++;
|
|
85
|
-
searchPos =
|
|
126
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
86
127
|
} else if (nestDepth > 0) {
|
|
87
128
|
nestDepth--;
|
|
88
|
-
searchPos =
|
|
129
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
89
130
|
} else {
|
|
90
|
-
closingPos =
|
|
131
|
+
closingPos = fenceLineStart;
|
|
91
132
|
break;
|
|
92
133
|
}
|
|
93
134
|
}
|
|
@@ -96,12 +137,18 @@ function replaceCodeBlock(content, filePath, newContent) {
|
|
|
96
137
|
}
|
|
97
138
|
|
|
98
139
|
async function main() {
|
|
140
|
+
const validModes = ["--check", "--refresh", "--execute"];
|
|
99
141
|
const mode = process.argv[2] || "--check";
|
|
142
|
+
if (!validModes.includes(mode)) {
|
|
143
|
+
console.log(` ❌ Unknown mode: ${mode}`);
|
|
144
|
+
console.log(` Valid modes: ${validModes.join(", ")}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
100
147
|
|
|
101
|
-
console.log("\n
|
|
148
|
+
console.log("\n╔═══════════════════════════════════════╗");
|
|
102
149
|
console.log("║ ClaudeOS-Core — Plan Validator ║");
|
|
103
|
-
console.log(`║ Mode: ${mode.padEnd(
|
|
104
|
-
console.log("
|
|
150
|
+
console.log(`║ Mode: ${mode.padEnd(31)}║`);
|
|
151
|
+
console.log("╚═══════════════════════════════════════╝\n");
|
|
105
152
|
|
|
106
153
|
if (!fs.existsSync(PLAN)) {
|
|
107
154
|
console.log(" ❌ Plan directory not found");
|
|
@@ -127,7 +174,7 @@ async function main() {
|
|
|
127
174
|
const abs = path.join(ROOT, b.path);
|
|
128
175
|
|
|
129
176
|
// Block path traversal attempts
|
|
130
|
-
if (!path.resolve(abs).startsWith(path.resolve(ROOT))) {
|
|
177
|
+
if (!path.resolve(abs).startsWith(path.resolve(ROOT) + path.sep)) {
|
|
131
178
|
console.log(` ⚠️ SKIPPED: ${b.path} (path traversal blocked)`);
|
|
132
179
|
continue;
|
|
133
180
|
}
|
|
@@ -148,9 +195,9 @@ async function main() {
|
|
|
148
195
|
continue;
|
|
149
196
|
}
|
|
150
197
|
|
|
151
|
-
// File exists — compare content
|
|
152
|
-
const diskContent = fs.readFileSync(abs, "utf-8").
|
|
153
|
-
const planContent = b.content.
|
|
198
|
+
// File exists — compare content (normalize trailing newlines only)
|
|
199
|
+
const diskContent = fs.readFileSync(abs, "utf-8").replace(/\n+$/, "");
|
|
200
|
+
const planContent = b.content.replace(/\n+$/, "");
|
|
154
201
|
|
|
155
202
|
if (diskContent === planContent) {
|
|
156
203
|
synced++;
|
|
@@ -187,15 +234,32 @@ async function main() {
|
|
|
187
234
|
console.log(`\n Total: ${total} | Synced: ${synced} | Drift: ${drift} | Missing: ${missing}`);
|
|
188
235
|
console.log(drift + missing === 0 ? " ✅ All plans in sync\n" : ` ⚠️ ${drift + missing} issues\n`);
|
|
189
236
|
|
|
190
|
-
// ─── Update plan-sync-status.json
|
|
237
|
+
// ─── Update plan-sync-status.json + stale-report.json ──
|
|
191
238
|
if (fs.existsSync(GEN)) {
|
|
192
239
|
fs.writeFileSync(
|
|
193
240
|
path.join(GEN, "plan-sync-status.json"),
|
|
194
241
|
JSON.stringify({ generatedAt: new Date().toISOString(), lastMode: mode, total, synced, drift, missing, issues: results }, null, 2)
|
|
195
242
|
);
|
|
243
|
+
|
|
244
|
+
// Also write to stale-report.json for consolidated health reporting
|
|
245
|
+
const rp = path.join(GEN, "stale-report.json");
|
|
246
|
+
let ex = {};
|
|
247
|
+
if (fs.existsSync(rp)) {
|
|
248
|
+
try { ex = JSON.parse(fs.readFileSync(rp, "utf-8")); } catch { ex = {}; }
|
|
249
|
+
}
|
|
250
|
+
ex.planValidation = { checkedAt: new Date().toISOString(), mode, total, synced, drift, missing };
|
|
251
|
+
if (!ex.summary) ex.summary = {};
|
|
252
|
+
ex.summary.planDrift = drift;
|
|
253
|
+
ex.summary.planMissing = missing;
|
|
254
|
+
fs.writeFileSync(rp, JSON.stringify(ex, null, 2));
|
|
196
255
|
}
|
|
197
256
|
|
|
198
257
|
process.exit(drift + missing > 0 ? 1 : 0);
|
|
199
258
|
}
|
|
200
259
|
|
|
201
|
-
main()
|
|
260
|
+
// Export for testing; run main() only when executed directly
|
|
261
|
+
if (require.main === module) {
|
|
262
|
+
main().catch(console.error);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = { extractFileBlocks, extractCodeBlocks, replaceFileBlock, replaceCodeBlock };
|
package/sync-checker/index.js
CHANGED
|
@@ -34,9 +34,9 @@ function rel(p) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async function main() {
|
|
37
|
-
console.log("\n
|
|
37
|
+
console.log("\n╔═══════════════════════════════════════╗");
|
|
38
38
|
console.log("║ ClaudeOS-Core — Sync Checker ║");
|
|
39
|
-
console.log("
|
|
39
|
+
console.log("╚═══════════════════════════════════════╝\n");
|
|
40
40
|
|
|
41
41
|
if (!fs.existsSync(SMP)) {
|
|
42
42
|
console.log(" ❌ sync-map.json not found. Run manifest-generator first.\n");
|
|
@@ -50,6 +50,10 @@ async function main() {
|
|
|
50
50
|
console.log(` ❌ sync-map.json is malformed: ${e.message}\n`);
|
|
51
51
|
process.exit(1);
|
|
52
52
|
}
|
|
53
|
+
if (!Array.isArray(sm.mappings)) {
|
|
54
|
+
console.log(" ❌ sync-map.json has no mappings array.\n");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
53
57
|
const reg = new Set(sm.mappings.map((m) => m.sourcePath));
|
|
54
58
|
const issues = { unreg: [], orphan: [] };
|
|
55
59
|
|
|
@@ -78,7 +82,7 @@ async function main() {
|
|
|
78
82
|
for (const m of sm.mappings) {
|
|
79
83
|
const abs = path.join(ROOT, m.sourcePath);
|
|
80
84
|
// Skip path traversal attempts
|
|
81
|
-
if (!path.resolve(abs).startsWith(path.resolve(ROOT))) continue;
|
|
85
|
+
if (!path.resolve(abs).startsWith(path.resolve(ROOT) + path.sep)) continue;
|
|
82
86
|
if (!fs.existsSync(abs)) {
|
|
83
87
|
issues.orphan.push({ path: m.sourcePath, plan: m.planFile });
|
|
84
88
|
}
|