claudeos-core 1.5.0 → 1.6.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.
- package/CHANGELOG.md +38 -0
- package/README.de.md +54 -27
- package/README.es.md +54 -26
- package/README.fr.md +54 -26
- package/README.hi.md +54 -27
- package/README.ja.md +53 -26
- package/README.ko.md +56 -29
- package/README.md +53 -26
- package/README.ru.md +54 -27
- package/README.vi.md +54 -27
- package/README.zh-CN.md +54 -27
- package/bin/cli.js +6 -2
- package/bin/commands/init.js +48 -46
- package/bin/lib/cli-utils.js +6 -10
- package/bin/lib/lang-selector.js +10 -8
- package/bin/lib/resume-selector.js +10 -8
- package/content-validator/index.js +7 -12
- package/health-checker/index.js +7 -17
- package/lib/plan-parser.js +149 -0
- package/lib/safe-fs.js +2 -2
- package/lib/stale-report.js +36 -0
- package/manifest-generator/index.js +13 -36
- package/package.json +87 -87
- package/pass-json-validator/index.js +7 -12
- package/pass-prompts/templates/node-nestjs/pass1.md +132 -0
- package/pass-prompts/templates/node-nestjs/pass2.md +97 -0
- package/pass-prompts/templates/node-nestjs/pass3.md +121 -0
- package/pass-prompts/templates/vue-nuxt/pass1.md +119 -0
- package/pass-prompts/templates/vue-nuxt/pass2.md +83 -0
- package/pass-prompts/templates/vue-nuxt/pass3.md +100 -0
- package/plan-installer/domain-grouper.js +5 -2
- package/plan-installer/prompt-generator.js +1 -4
- package/plan-installer/scanners/scan-frontend.js +14 -7
- package/plan-installer/scanners/scan-java.js +18 -13
- package/plan-installer/scanners/scan-kotlin.js +47 -23
- package/plan-installer/scanners/scan-node.js +16 -2
- package/plan-installer/stack-detector.js +42 -1
- package/plan-validator/index.js +11 -121
- package/sync-checker/index.js +6 -19
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeOS-Core — Plan Parser (shared)
|
|
3
|
+
*
|
|
4
|
+
* Shared parsing logic for plan files used by both manifest-generator and plan-validator.
|
|
5
|
+
* Two block formats:
|
|
6
|
+
* - <file path="..."> ... </file> (standard plan format)
|
|
7
|
+
* - ## N. `path` \n```markdown ... ``` (code block format, e.g. sync-rules-master)
|
|
8
|
+
*
|
|
9
|
+
* Each function accepts a content string and returns parsed blocks.
|
|
10
|
+
* Replace functions modify content in-place and return the updated string.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ─── Extract <file path="..."> blocks ───────────────────────
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse <file path="..."> ... </file> blocks from plan content.
|
|
17
|
+
* @param {string} content - plan file content
|
|
18
|
+
* @param {{ includeContent?: boolean }} options
|
|
19
|
+
* @returns {Array<{ path: string, content?: string }>}
|
|
20
|
+
*/
|
|
21
|
+
function parseFileBlocks(content, { includeContent = false } = {}) {
|
|
22
|
+
const result = [];
|
|
23
|
+
if (includeContent) {
|
|
24
|
+
const re = /<file\s+path="([^"]+)">\s*\n([\s\S]*?)\n<\/file>/g;
|
|
25
|
+
let m;
|
|
26
|
+
while ((m = re.exec(content)) !== null) {
|
|
27
|
+
result.push({ path: m[1], content: m[2] });
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
const re = /<file\s+path="([^"]+)">/g;
|
|
31
|
+
let m;
|
|
32
|
+
while ((m = re.exec(content)) !== null) {
|
|
33
|
+
result.push({ path: m[1] });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// ─── Extract ## N. `path` ```markdown ... ``` blocks ────────
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse ## N. `path` + ```markdown ... ``` blocks from plan content.
|
|
43
|
+
* Uses indexOf-based parsing to correctly handle nested code fences.
|
|
44
|
+
* @param {string} content - plan file content
|
|
45
|
+
* @param {{ includeContent?: boolean }} options
|
|
46
|
+
* @returns {Array<{ path: string, content?: string }>}
|
|
47
|
+
*/
|
|
48
|
+
function parseCodeBlocks(content, { includeContent = false } = {}) {
|
|
49
|
+
const result = [];
|
|
50
|
+
const headingRe = includeContent
|
|
51
|
+
? /^##\s+\d+\.\s+([^\n]+)/gm
|
|
52
|
+
: /^##\s+\d+\.\s+`?([^`\n]+)`?/gm;
|
|
53
|
+
let headingMatch;
|
|
54
|
+
while ((headingMatch = headingRe.exec(content)) !== null) {
|
|
55
|
+
const filePath = headingMatch[1].replace(/`/g, "").trim();
|
|
56
|
+
|
|
57
|
+
if (!includeContent) {
|
|
58
|
+
// Path-only mode: just validate and collect
|
|
59
|
+
if (filePath && filePath.includes("/")) {
|
|
60
|
+
result.push({ path: filePath });
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Content mode: find opening ```markdown and matching closing ```
|
|
66
|
+
const openFence = content.indexOf("```markdown\n", headingMatch.index);
|
|
67
|
+
if (openFence < 0) continue;
|
|
68
|
+
const contentStart = openFence + "```markdown\n".length;
|
|
69
|
+
const closingPos = findClosingFence(content, contentStart);
|
|
70
|
+
if (closingPos < 0) continue;
|
|
71
|
+
const blockContent = content.substring(contentStart, closingPos).trimEnd();
|
|
72
|
+
result.push({ path: filePath, content: blockContent });
|
|
73
|
+
// Advance headingRe past this block to avoid re-matching inside content
|
|
74
|
+
headingRe.lastIndex = closingPos;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Replace <file> block content ───────────────────────────
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Replace content inside a <file path="..."> block.
|
|
83
|
+
* @param {string} content - full plan file content
|
|
84
|
+
* @param {string} filePath - path attribute to match
|
|
85
|
+
* @param {string} newContent - replacement content
|
|
86
|
+
* @returns {string} updated content
|
|
87
|
+
*/
|
|
88
|
+
function replaceFileBlock(content, filePath, newContent) {
|
|
89
|
+
const escaped = filePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
90
|
+
return content.replace(
|
|
91
|
+
new RegExp(`(<file\\s+path="${escaped}">\\s*\\n)[\\s\\S]*?(\\n</file>)`, "g"),
|
|
92
|
+
`$1${newContent}$2`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Replace code block content ─────────────────────────────
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Replace content inside a ## N. `path` ```markdown ... ``` block.
|
|
100
|
+
* Uses indexOf-based approach to handle nested code fences.
|
|
101
|
+
* @param {string} content - full plan file content
|
|
102
|
+
* @param {string} filePath - path to match in heading
|
|
103
|
+
* @param {string} newContent - replacement content
|
|
104
|
+
* @returns {string} updated content
|
|
105
|
+
*/
|
|
106
|
+
function replaceCodeBlock(content, filePath, newContent) {
|
|
107
|
+
const cleanPath = filePath.replace(/`/g, "");
|
|
108
|
+
const headingPattern = new RegExp(`^##\\s+\\d+\\.\\s+\`?${cleanPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\`?`, "m");
|
|
109
|
+
const headingMatch = headingPattern.exec(content);
|
|
110
|
+
if (!headingMatch) return content;
|
|
111
|
+
const afterHeading = content.indexOf("```markdown\n", headingMatch.index);
|
|
112
|
+
if (afterHeading < 0) return content;
|
|
113
|
+
const contentStart = afterHeading + "```markdown\n".length;
|
|
114
|
+
const closingPos = findClosingFence(content, contentStart);
|
|
115
|
+
if (closingPos < 0) return content;
|
|
116
|
+
return content.substring(0, contentStart) + newContent + "\n" + content.substring(closingPos);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── Internal: find closing ``` with nesting ────────────────
|
|
120
|
+
|
|
121
|
+
function findClosingFence(content, startPos) {
|
|
122
|
+
let searchPos = startPos;
|
|
123
|
+
let nestDepth = 0;
|
|
124
|
+
while (searchPos < content.length) {
|
|
125
|
+
const nextFence = content.indexOf("\n```", searchPos);
|
|
126
|
+
if (nextFence < 0) break;
|
|
127
|
+
const fenceLineStart = nextFence + 1;
|
|
128
|
+
const nextNewline = content.indexOf("\n", fenceLineStart + 3);
|
|
129
|
+
const restOfLine = nextNewline >= 0
|
|
130
|
+
? content.substring(fenceLineStart + 3, nextNewline)
|
|
131
|
+
: content.substring(fenceLineStart + 3);
|
|
132
|
+
const isOpening = /^[a-zA-Z]/.test(restOfLine.trim());
|
|
133
|
+
if (isOpening) {
|
|
134
|
+
nestDepth++;
|
|
135
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
136
|
+
} else if (nestDepth > 0) {
|
|
137
|
+
nestDepth--;
|
|
138
|
+
searchPos = nextNewline >= 0 ? nextNewline : fenceLineStart + 3;
|
|
139
|
+
} else {
|
|
140
|
+
return fenceLineStart;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return -1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Plan files that use code block format instead of <file> block format
|
|
147
|
+
const CODE_BLOCK_PLANS = ["21.sync-rules-master.md"];
|
|
148
|
+
|
|
149
|
+
module.exports = { parseFileBlocks, parseCodeBlocks, replaceFileBlock, replaceCodeBlock, CODE_BLOCK_PLANS };
|
package/lib/safe-fs.js
CHANGED
|
@@ -50,7 +50,7 @@ function readJsonSafe(filePath, fallback = null) {
|
|
|
50
50
|
function existsSafe(filePath) {
|
|
51
51
|
try {
|
|
52
52
|
return fs.existsSync(filePath);
|
|
53
|
-
} catch {
|
|
53
|
+
} catch (_e) {
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -95,7 +95,7 @@ function statSafe(filePath) {
|
|
|
95
95
|
bytes: s.size,
|
|
96
96
|
modified: s.mtime.toISOString().split("T")[0],
|
|
97
97
|
};
|
|
98
|
-
} catch {
|
|
98
|
+
} catch (_e) {
|
|
99
99
|
return null;
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeOS-Core — Stale Report Utility
|
|
3
|
+
*
|
|
4
|
+
* Shared helper for reading/updating claudeos-core/generated/stale-report.json.
|
|
5
|
+
* Used by: content-validator, sync-checker, plan-validator, health-checker,
|
|
6
|
+
* pass-json-validator, manifest-generator.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Update stale-report.json with a new key and optional summary patch.
|
|
14
|
+
* Reads existing file, merges data, writes back. Safe against missing/malformed files.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} genDir - path to claudeos-core/generated/
|
|
17
|
+
* @param {string} key - top-level key to set (e.g. "contentValidation", "syncMisses")
|
|
18
|
+
* @param {object} data - value for that key
|
|
19
|
+
* @param {object} [summaryPatch] - fields to merge into ex.summary
|
|
20
|
+
*/
|
|
21
|
+
function updateStaleReport(genDir, key, data, summaryPatch) {
|
|
22
|
+
if (!fs.existsSync(genDir)) return;
|
|
23
|
+
const rp = path.join(genDir, "stale-report.json");
|
|
24
|
+
let ex = {};
|
|
25
|
+
if (fs.existsSync(rp)) {
|
|
26
|
+
try { ex = JSON.parse(fs.readFileSync(rp, "utf-8")); } catch (_e) { ex = {}; }
|
|
27
|
+
}
|
|
28
|
+
ex[key] = data;
|
|
29
|
+
if (summaryPatch) {
|
|
30
|
+
if (!ex.summary) ex.summary = {};
|
|
31
|
+
Object.assign(ex.summary, summaryPatch);
|
|
32
|
+
}
|
|
33
|
+
fs.writeFileSync(rp, JSON.stringify(ex, null, 2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { updateStaleReport };
|
|
@@ -17,6 +17,8 @@ const fs = require("fs");
|
|
|
17
17
|
const path = require("path");
|
|
18
18
|
const matter = require("gray-matter");
|
|
19
19
|
const { glob } = require("glob");
|
|
20
|
+
const { parseFileBlocks, parseCodeBlocks, CODE_BLOCK_PLANS } = require("../lib/plan-parser");
|
|
21
|
+
const { updateStaleReport } = require("../lib/stale-report");
|
|
20
22
|
|
|
21
23
|
const ROOT = process.env.CLAUDEOS_ROOT || path.resolve(__dirname, "../..");
|
|
22
24
|
const GEN = path.join(ROOT, "claudeos-core/generated");
|
|
@@ -48,39 +50,23 @@ function stat(f) {
|
|
|
48
50
|
function frontmatter(f) {
|
|
49
51
|
try {
|
|
50
52
|
return matter(fs.readFileSync(f, "utf-8")).data || {};
|
|
51
|
-
} catch {
|
|
53
|
+
} catch (_e) {
|
|
52
54
|
return {};
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
//
|
|
58
|
-
function
|
|
59
|
+
// Wrappers: read file → parse → attach planFile metadata
|
|
60
|
+
function extractFileBlocksFromFile(f) {
|
|
59
61
|
if (!fs.existsSync(f)) return [];
|
|
60
62
|
const content = fs.readFileSync(f, "utf-8");
|
|
61
|
-
|
|
62
|
-
let m;
|
|
63
|
-
const re = /<file\s+path="([^"]+)">/g;
|
|
64
|
-
while ((m = re.exec(content)) !== null) {
|
|
65
|
-
result.push({ sourcePath: m[1], planFile: rel(f) });
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
63
|
+
return parseFileBlocks(content).map(b => ({ sourcePath: b.path, planFile: rel(f) }));
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
|
|
71
|
-
function extractCodeBlockPaths(f) {
|
|
66
|
+
function extractCodeBlockPathsFromFile(f) {
|
|
72
67
|
if (!fs.existsSync(f)) return [];
|
|
73
68
|
const content = fs.readFileSync(f, "utf-8");
|
|
74
|
-
|
|
75
|
-
const headingRe = /^##\s+\d+\.\s+`?([^`\n]+)`?/gm;
|
|
76
|
-
let headingMatch;
|
|
77
|
-
while ((headingMatch = headingRe.exec(content)) !== null) {
|
|
78
|
-
const filePath = headingMatch[1].trim();
|
|
79
|
-
if (filePath && filePath.includes("/")) {
|
|
80
|
-
result.push({ sourcePath: filePath, planFile: rel(f) });
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return result;
|
|
69
|
+
return parseCodeBlocks(content).map(b => ({ sourcePath: b.path, planFile: rel(f) }));
|
|
84
70
|
}
|
|
85
71
|
|
|
86
72
|
async function main() {
|
|
@@ -136,15 +122,15 @@ async function main() {
|
|
|
136
122
|
// import-graph.json removed — @import was never a Claude Code feature
|
|
137
123
|
|
|
138
124
|
// ─── sync-map.json ─────────────────────────────────────
|
|
139
|
-
|
|
125
|
+
// CODE_BLOCK_PLANS imported from lib/plan-parser.js
|
|
140
126
|
const sm = { generatedAt: new Date().toISOString(), mappings: [] };
|
|
141
127
|
if (fs.existsSync(DIRS.plan)) {
|
|
142
128
|
for (const p of await glob("*.md", { cwd: DIRS.plan, absolute: true })) {
|
|
143
129
|
const bn = path.basename(p);
|
|
144
130
|
if (CODE_BLOCK_PLANS.includes(bn)) {
|
|
145
|
-
sm.mappings.push(...
|
|
131
|
+
sm.mappings.push(...extractCodeBlockPathsFromFile(p));
|
|
146
132
|
} else {
|
|
147
|
-
sm.mappings.push(...
|
|
133
|
+
sm.mappings.push(...extractFileBlocksFromFile(p));
|
|
148
134
|
}
|
|
149
135
|
}
|
|
150
136
|
}
|
|
@@ -158,7 +144,7 @@ async function main() {
|
|
|
158
144
|
for (const p of await glob("*.md", { cwd: DIRS.plan, absolute: true })) {
|
|
159
145
|
const r = rel(p);
|
|
160
146
|
const s = stat(p);
|
|
161
|
-
const blocks =
|
|
147
|
+
const blocks = extractFileBlocksFromFile(p);
|
|
162
148
|
pm.plans.push({ path: r, ...s, fileBlocks: blocks.length, status: "ok" });
|
|
163
149
|
}
|
|
164
150
|
}
|
|
@@ -166,16 +152,7 @@ async function main() {
|
|
|
166
152
|
console.log(` ✅ plan-manifest.json — ${pm.plans.length} plans`);
|
|
167
153
|
|
|
168
154
|
// ─── Initialize stale-report.json (preserve existing sub-tool results) ──
|
|
169
|
-
|
|
170
|
-
let sr = {};
|
|
171
|
-
if (fs.existsSync(srPath)) {
|
|
172
|
-
try { sr = JSON.parse(fs.readFileSync(srPath, "utf-8")); } catch { sr = {}; }
|
|
173
|
-
}
|
|
174
|
-
sr.generatedAt = new Date().toISOString();
|
|
175
|
-
if (!sr.summary) sr.summary = {};
|
|
176
|
-
sr.summary.totalIssues = 0;
|
|
177
|
-
sr.summary.status = "initial";
|
|
178
|
-
fs.writeFileSync(srPath, JSON.stringify(sr, null, 2));
|
|
155
|
+
updateStaleReport(GEN, "generatedAt", new Date().toISOString(), { totalIssues: 0, status: "initial" });
|
|
179
156
|
console.log(" ✅ stale-report.json — initialized");
|
|
180
157
|
console.log("\n 📁 Output: claudeos-core/generated/ (4 files)\n");
|
|
181
158
|
}
|
package/package.json
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claudeos-core",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Auto-generate Claude Code documentation from your actual source code — Standards, Rules, Skills, and Guides tailored to your project",
|
|
5
|
-
"main": "bin/cli.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"claudeos-core": "bin/cli.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin/",
|
|
11
|
-
"lib/",
|
|
12
|
-
"content-validator/",
|
|
13
|
-
"health-checker/",
|
|
14
|
-
"manifest-generator/",
|
|
15
|
-
"pass-json-validator/",
|
|
16
|
-
"pass-prompts/",
|
|
17
|
-
"plan-installer/",
|
|
18
|
-
"plan-validator/",
|
|
19
|
-
"sync-checker/",
|
|
20
|
-
"bootstrap.sh",
|
|
21
|
-
"README.md",
|
|
22
|
-
"README.ko.md",
|
|
23
|
-
"LICENSE",
|
|
24
|
-
"CHANGELOG.md",
|
|
25
|
-
"CONTRIBUTING.md",
|
|
26
|
-
"README.zh-CN.md",
|
|
27
|
-
"README.ja.md",
|
|
28
|
-
"README.es.md",
|
|
29
|
-
"README.vi.md",
|
|
30
|
-
"README.hi.md",
|
|
31
|
-
"README.ru.md",
|
|
32
|
-
"README.fr.md",
|
|
33
|
-
"README.de.md"
|
|
34
|
-
],
|
|
35
|
-
"scripts": {
|
|
36
|
-
"init": "node bin/cli.js init",
|
|
37
|
-
"health": "node bin/cli.js health",
|
|
38
|
-
"validate": "node bin/cli.js validate",
|
|
39
|
-
"refresh": "node bin/cli.js refresh",
|
|
40
|
-
"restore": "node bin/cli.js restore",
|
|
41
|
-
"test": "node --test tests/*.test.js",
|
|
42
|
-
"test:health": "node health-checker/index.js"
|
|
43
|
-
},
|
|
44
|
-
"keywords": [
|
|
45
|
-
"claude-code",
|
|
46
|
-
"automation",
|
|
47
|
-
"code-analysis",
|
|
48
|
-
"CLAUDE.md",
|
|
49
|
-
"standards",
|
|
50
|
-
"rules",
|
|
51
|
-
"skills",
|
|
52
|
-
"scaffolding",
|
|
53
|
-
"i18n",
|
|
54
|
-
"multi-language",
|
|
55
|
-
"spring-boot",
|
|
56
|
-
"kotlin",
|
|
57
|
-
"exposed",
|
|
58
|
-
"jooq",
|
|
59
|
-
"cqrs",
|
|
60
|
-
"bff",
|
|
61
|
-
"multi-module",
|
|
62
|
-
"monorepo",
|
|
63
|
-
"nextjs",
|
|
64
|
-
"express",
|
|
65
|
-
"fastify",
|
|
66
|
-
"angular",
|
|
67
|
-
"django",
|
|
68
|
-
"fastapi"
|
|
69
|
-
],
|
|
70
|
-
"author": "claudeos-core <claudeoscore@gmail.com> (https://github.com/claudeos-core)",
|
|
71
|
-
"license": "ISC",
|
|
72
|
-
"repository": {
|
|
73
|
-
"type": "git",
|
|
74
|
-
"url": "git+https://github.com/claudeos-core/claudeos-core.git"
|
|
75
|
-
},
|
|
76
|
-
"homepage": "https://github.com/claudeos-core/claudeos-core#readme",
|
|
77
|
-
"bugs": {
|
|
78
|
-
"url": "https://github.com/claudeos-core/claudeos-core/issues"
|
|
79
|
-
},
|
|
80
|
-
"engines": {
|
|
81
|
-
"node": ">=18.0.0"
|
|
82
|
-
},
|
|
83
|
-
"dependencies": {
|
|
84
|
-
"glob": "^13.0.6",
|
|
85
|
-
"gray-matter": "^4.0.3"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "claudeos-core",
|
|
3
|
+
"version": "1.6.0",
|
|
4
|
+
"description": "Auto-generate Claude Code documentation from your actual source code — Standards, Rules, Skills, and Guides tailored to your project",
|
|
5
|
+
"main": "bin/cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claudeos-core": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"content-validator/",
|
|
13
|
+
"health-checker/",
|
|
14
|
+
"manifest-generator/",
|
|
15
|
+
"pass-json-validator/",
|
|
16
|
+
"pass-prompts/",
|
|
17
|
+
"plan-installer/",
|
|
18
|
+
"plan-validator/",
|
|
19
|
+
"sync-checker/",
|
|
20
|
+
"bootstrap.sh",
|
|
21
|
+
"README.md",
|
|
22
|
+
"README.ko.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"CHANGELOG.md",
|
|
25
|
+
"CONTRIBUTING.md",
|
|
26
|
+
"README.zh-CN.md",
|
|
27
|
+
"README.ja.md",
|
|
28
|
+
"README.es.md",
|
|
29
|
+
"README.vi.md",
|
|
30
|
+
"README.hi.md",
|
|
31
|
+
"README.ru.md",
|
|
32
|
+
"README.fr.md",
|
|
33
|
+
"README.de.md"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"init": "node bin/cli.js init",
|
|
37
|
+
"health": "node bin/cli.js health",
|
|
38
|
+
"validate": "node bin/cli.js validate",
|
|
39
|
+
"refresh": "node bin/cli.js refresh",
|
|
40
|
+
"restore": "node bin/cli.js restore",
|
|
41
|
+
"test": "node --test tests/*.test.js",
|
|
42
|
+
"test:health": "node health-checker/index.js"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"claude-code",
|
|
46
|
+
"automation",
|
|
47
|
+
"code-analysis",
|
|
48
|
+
"CLAUDE.md",
|
|
49
|
+
"standards",
|
|
50
|
+
"rules",
|
|
51
|
+
"skills",
|
|
52
|
+
"scaffolding",
|
|
53
|
+
"i18n",
|
|
54
|
+
"multi-language",
|
|
55
|
+
"spring-boot",
|
|
56
|
+
"kotlin",
|
|
57
|
+
"exposed",
|
|
58
|
+
"jooq",
|
|
59
|
+
"cqrs",
|
|
60
|
+
"bff",
|
|
61
|
+
"multi-module",
|
|
62
|
+
"monorepo",
|
|
63
|
+
"nextjs",
|
|
64
|
+
"express",
|
|
65
|
+
"fastify",
|
|
66
|
+
"angular",
|
|
67
|
+
"django",
|
|
68
|
+
"fastapi"
|
|
69
|
+
],
|
|
70
|
+
"author": "claudeos-core <claudeoscore@gmail.com> (https://github.com/claudeos-core)",
|
|
71
|
+
"license": "ISC",
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "git+https://github.com/claudeos-core/claudeos-core.git"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://github.com/claudeos-core/claudeos-core#readme",
|
|
77
|
+
"bugs": {
|
|
78
|
+
"url": "https://github.com/claudeos-core/claudeos-core/issues"
|
|
79
|
+
},
|
|
80
|
+
"engines": {
|
|
81
|
+
"node": ">=18.0.0"
|
|
82
|
+
},
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"glob": "^13.0.6",
|
|
85
|
+
"gray-matter": "^4.0.3"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
const fs = require("fs");
|
|
18
18
|
const path = require("path");
|
|
19
19
|
const { glob } = require("glob");
|
|
20
|
+
const { updateStaleReport } = require("../lib/stale-report");
|
|
20
21
|
|
|
21
22
|
const ROOT = process.env.CLAUDEOS_ROOT || path.resolve(__dirname, "../..");
|
|
22
23
|
const GEN_DIR = path.join(ROOT, "claudeos-core/generated");
|
|
@@ -197,7 +198,7 @@ async function main() {
|
|
|
197
198
|
isBackend = !frontend || ["express", "nestjs", "django", "fastapi", "spring-boot"].includes(framework);
|
|
198
199
|
isKotlin = language === "kotlin";
|
|
199
200
|
isKotlinCqrs = isKotlin && (architecture === "cqrs" || paData.stack?.multiModule);
|
|
200
|
-
} catch { /* If project-analysis parsing fails, conservatively assume backend */ }
|
|
201
|
+
} catch (_e) { /* If project-analysis parsing fails, conservatively assume backend */ }
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
const sectionsToCheck = [
|
|
@@ -282,23 +283,17 @@ async function main() {
|
|
|
282
283
|
}
|
|
283
284
|
|
|
284
285
|
// Record in stale-report
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
try { ex = JSON.parse(fs.readFileSync(rp, "utf-8")); } catch { ex = {}; }
|
|
290
|
-
}
|
|
291
|
-
ex.jsonValidation = { checkedAt: new Date().toISOString(), checked, errors: errors.length, warnings: warnings.length };
|
|
292
|
-
ex.summary = { ...ex.summary, jsonErrors: errors.length, jsonWarnings: warnings.length };
|
|
293
|
-
fs.writeFileSync(rp, JSON.stringify(ex, null, 2));
|
|
294
|
-
}
|
|
286
|
+
updateStaleReport(GEN_DIR, "jsonValidation",
|
|
287
|
+
{ checkedAt: new Date().toISOString(), checked, errors: errors.length, warnings: warnings.length },
|
|
288
|
+
{ jsonErrors: errors.length, jsonWarnings: warnings.length }
|
|
289
|
+
);
|
|
295
290
|
|
|
296
291
|
console.log(` Total: ${errors.length} errors, ${warnings.length} warnings\n`);
|
|
297
292
|
process.exit(errors.length > 0 ? 1 : 0);
|
|
298
293
|
}
|
|
299
294
|
|
|
300
295
|
if (require.main === module) {
|
|
301
|
-
main().catch(console.error);
|
|
296
|
+
main().catch(e => { console.error(`\n ❌ Unexpected error: ${e.message || e}`); process.exit(1); });
|
|
302
297
|
}
|
|
303
298
|
|
|
304
299
|
module.exports = { main };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Read claudeos-core/generated/project-analysis.json and
|
|
2
|
+
perform a deep analysis of the following domains only: {{DOMAIN_GROUP}}
|
|
3
|
+
|
|
4
|
+
For each domain, select one representative file per layer, read its code, and analyze it.
|
|
5
|
+
Prioritize files with the richest patterns.
|
|
6
|
+
|
|
7
|
+
Analysis items (per domain):
|
|
8
|
+
|
|
9
|
+
1. Module & Controller Patterns
|
|
10
|
+
- @Module() structure (imports, controllers, providers, exports)
|
|
11
|
+
- @Controller() decorator, route prefix conventions
|
|
12
|
+
- @Get/@Post/@Put/@Patch/@Delete decorators, parameter decorators (@Body, @Param, @Query, @Headers)
|
|
13
|
+
- Response handling (@Res() vs return value, interceptors, class-transformer serialization)
|
|
14
|
+
- If a custom response wrapper/interceptor exists, record its EXACT class name, method signatures, and import path
|
|
15
|
+
- **Response wrapping layer (CRITICAL)**: Which layer ACTUALLY calls the response wrapper?
|
|
16
|
+
Does Controller call it directly, or does a Service/UseCase layer return the wrapped response?
|
|
17
|
+
Record EXACTLY: "Controller wraps response" or "Service wraps response"
|
|
18
|
+
- If a dedicated orchestration layer exists between Controller and Service (e.g., UseCase, Facade, Aggregator),
|
|
19
|
+
record its name, responsibilities, and return type
|
|
20
|
+
- Error handling (HttpException hierarchy, ExceptionFilter, @Catch())
|
|
21
|
+
- @UseGuards() for authentication (JwtAuthGuard, RolesGuard, custom guards)
|
|
22
|
+
- API documentation (@ApiTags, @ApiOperation, @ApiResponse, @ApiBearerAuth)
|
|
23
|
+
- Pagination patterns (offset/limit, cursor, custom)
|
|
24
|
+
- @Version() API versioning
|
|
25
|
+
|
|
26
|
+
2. Provider & Service Patterns
|
|
27
|
+
- @Injectable() and dependency injection (constructor injection, custom providers, useFactory/useClass/useValue)
|
|
28
|
+
- Module scoping (DEFAULT, REQUEST, TRANSIENT)
|
|
29
|
+
- Transaction strategy (Prisma.$transaction, TypeORM QueryRunner, Sequelize.transaction)
|
|
30
|
+
- Business exception handling (custom exception hierarchy extending HttpException)
|
|
31
|
+
- Validation (@UsePipes, ValidationPipe, class-validator decorators)
|
|
32
|
+
- Event handling (@OnEvent, EventEmitter2)
|
|
33
|
+
- CQRS pattern usage (@nestjs/cqrs CommandBus/QueryBus/EventBus)
|
|
34
|
+
|
|
35
|
+
3. Data Access Patterns
|
|
36
|
+
- ORM/query builder (Prisma, TypeORM, Sequelize, MikroORM, Mongoose)
|
|
37
|
+
- Repository pattern (@InjectRepository, custom repository classes)
|
|
38
|
+
- Query optimization (relations/eager/lazy loading, N+1 prevention)
|
|
39
|
+
- Migration tools (Prisma Migrate, TypeORM Migration)
|
|
40
|
+
- Seed data management
|
|
41
|
+
- Connection management (TypeOrmModule.forRootAsync, PrismaModule)
|
|
42
|
+
|
|
43
|
+
4. DTO & Validation Patterns
|
|
44
|
+
- class-validator decorators (@IsString, @IsNotEmpty, @IsEmail, @ValidateNested)
|
|
45
|
+
- class-transformer decorators (@Expose, @Exclude, @Type, @Transform)
|
|
46
|
+
- Request/Response DTO separation
|
|
47
|
+
- Mapped types (PartialType, PickType, OmitType, IntersectionType)
|
|
48
|
+
- Custom validation decorators
|
|
49
|
+
- Enum/constants management
|
|
50
|
+
- File entry pattern: is the main file `index.ts` or named by module (e.g., `users.controller.ts`)? Record exact convention
|
|
51
|
+
- Import paths: record EXACT path aliases and import patterns (e.g., `@/modules/`, `@app/`, relative paths)
|
|
52
|
+
|
|
53
|
+
5. Middleware, Guard, Pipe, Interceptor Patterns
|
|
54
|
+
- @Injectable() middleware (NestMiddleware) vs Express-style middleware
|
|
55
|
+
- Guards (@CanActivate) — authentication, authorization, role-based
|
|
56
|
+
- Pipes (ValidationPipe, ParseIntPipe, custom pipes)
|
|
57
|
+
- Interceptors (logging, caching, timeout, response transformation)
|
|
58
|
+
- Execution order (middleware → guard → interceptor(pre) → pipe → handler → interceptor(post) → filter)
|
|
59
|
+
|
|
60
|
+
6. Configuration & Environment Patterns
|
|
61
|
+
- @nestjs/config (ConfigModule, ConfigService, registerAs)
|
|
62
|
+
- Environment validation (Joi schema, class-validator)
|
|
63
|
+
- Per-environment branching (development/staging/production)
|
|
64
|
+
- Custom configuration namespaces
|
|
65
|
+
|
|
66
|
+
7. Logging Patterns
|
|
67
|
+
- NestJS Logger vs external (winston, pino, nestjs-pino)
|
|
68
|
+
- Log level policy
|
|
69
|
+
- Structured logging (JSON format, correlation ID)
|
|
70
|
+
- Request/response logging (LoggingInterceptor)
|
|
71
|
+
|
|
72
|
+
8. Testing Patterns
|
|
73
|
+
- Test framework (Jest, Vitest)
|
|
74
|
+
- Test.createTestingModule() usage
|
|
75
|
+
- Mocking strategy (jest.mock, overrideProvider, custom TestModule)
|
|
76
|
+
- E2E testing (supertest, @nestjs/testing)
|
|
77
|
+
- Test data management (Factory, Fixture, Faker)
|
|
78
|
+
- DB test strategy (in-memory, test container, separate DB)
|
|
79
|
+
|
|
80
|
+
9. Domain-Specific Patterns
|
|
81
|
+
- File upload (@UseInterceptors(FileInterceptor), Multer, S3)
|
|
82
|
+
- WebSocket (@WebSocketGateway, Socket.io)
|
|
83
|
+
- Queue/job processing (@nestjs/bull, BullMQ)
|
|
84
|
+
- External API integration (HttpModule/HttpService, Axios)
|
|
85
|
+
- Caching (@nestjs/cache-manager, Redis, @CacheKey, @CacheTTL)
|
|
86
|
+
- Microservices (@nestjs/microservices, Transport)
|
|
87
|
+
- Scheduling (@nestjs/schedule, @Cron, @Interval)
|
|
88
|
+
- Health checks (@nestjs/terminus)
|
|
89
|
+
|
|
90
|
+
10. Anti-patterns / Inconsistencies
|
|
91
|
+
- Mixing NestJS patterns with raw Express patterns
|
|
92
|
+
- Business logic in controllers (should be in services)
|
|
93
|
+
- Circular dependencies
|
|
94
|
+
- Missing @Injectable() on providers
|
|
95
|
+
- Type safety issues (any abuse, missing types)
|
|
96
|
+
|
|
97
|
+
Do not create or modify source files. Analysis only.
|
|
98
|
+
Save results to claudeos-core/generated/pass1-{{PASS_NUM}}.json in the following format:
|
|
99
|
+
|
|
100
|
+
{
|
|
101
|
+
"analyzedAt": "ISO timestamp",
|
|
102
|
+
"passNum": {{PASS_NUM}},
|
|
103
|
+
"domains": ["users", "auth", "orders", "products"],
|
|
104
|
+
"analysisPerDomain": {
|
|
105
|
+
"users": {
|
|
106
|
+
"representativeFiles": {
|
|
107
|
+
"module": "users.module.ts",
|
|
108
|
+
"controller": "users.controller.ts",
|
|
109
|
+
"service": "users.service.ts",
|
|
110
|
+
"repository": "users.repository.ts",
|
|
111
|
+
"dto": "create-user.dto.ts"
|
|
112
|
+
},
|
|
113
|
+
"patterns": {
|
|
114
|
+
"module": { ... },
|
|
115
|
+
"controller": { ... },
|
|
116
|
+
"service": { ... },
|
|
117
|
+
"dataAccess": { ... },
|
|
118
|
+
"dto": { ... },
|
|
119
|
+
"middleware": { ... },
|
|
120
|
+
"config": { ... },
|
|
121
|
+
"logging": { ... },
|
|
122
|
+
"testing": { ... }
|
|
123
|
+
},
|
|
124
|
+
"specialPatterns": [],
|
|
125
|
+
"antiPatterns": []
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"crossDomainCommon": {
|
|
129
|
+
"description": "Patterns commonly used across domains in this group",
|
|
130
|
+
"patterns": []
|
|
131
|
+
}
|
|
132
|
+
}
|