claudeos-core 2.1.1 → 2.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +1649 -481
  2. package/CONTRIBUTING.md +92 -92
  3. package/README.de.md +64 -5
  4. package/README.es.md +64 -5
  5. package/README.fr.md +64 -5
  6. package/README.hi.md +64 -5
  7. package/README.ja.md +64 -5
  8. package/README.ko.md +1018 -959
  9. package/README.md +1020 -960
  10. package/README.ru.md +66 -5
  11. package/README.vi.md +1019 -960
  12. package/README.zh-CN.md +64 -5
  13. package/bin/cli.js +152 -148
  14. package/bin/commands/init.js +1673 -1518
  15. package/bin/commands/lint.js +62 -0
  16. package/bin/commands/memory.js +438 -438
  17. package/bin/lib/cli-utils.js +206 -206
  18. package/claude-md-validator/index.js +184 -0
  19. package/claude-md-validator/reporter.js +66 -0
  20. package/claude-md-validator/structural-checks.js +528 -0
  21. package/content-validator/index.js +666 -436
  22. package/lib/env-parser.js +317 -0
  23. package/lib/expected-guides.js +23 -23
  24. package/lib/expected-outputs.js +90 -90
  25. package/lib/language-config.js +35 -35
  26. package/lib/memory-scaffold.js +1058 -1052
  27. package/lib/plan-parser.js +165 -165
  28. package/lib/staged-rules.js +118 -118
  29. package/manifest-generator/index.js +174 -174
  30. package/package.json +90 -87
  31. package/pass-json-validator/index.js +337 -337
  32. package/pass-prompts/templates/angular/pass3.md +28 -13
  33. package/pass-prompts/templates/common/claude-md-scaffold.md +686 -0
  34. package/pass-prompts/templates/common/pass3-footer.md +402 -39
  35. package/pass-prompts/templates/common/pass3b-core-header.md +43 -0
  36. package/pass-prompts/templates/common/pass4.md +375 -302
  37. package/pass-prompts/templates/common/staging-override.md +26 -26
  38. package/pass-prompts/templates/java-spring/pass3.md +31 -21
  39. package/pass-prompts/templates/kotlin-spring/pass3.md +34 -22
  40. package/pass-prompts/templates/node-express/pass3.md +30 -21
  41. package/pass-prompts/templates/node-fastify/pass3.md +28 -14
  42. package/pass-prompts/templates/node-nestjs/pass3.md +29 -14
  43. package/pass-prompts/templates/node-nextjs/pass3.md +34 -21
  44. package/pass-prompts/templates/node-vite/pass1.md +117 -117
  45. package/pass-prompts/templates/node-vite/pass2.md +78 -78
  46. package/pass-prompts/templates/node-vite/pass3.md +30 -13
  47. package/pass-prompts/templates/python-django/pass3.md +32 -21
  48. package/pass-prompts/templates/python-fastapi/pass3.md +33 -21
  49. package/pass-prompts/templates/python-flask/pass1.md +119 -119
  50. package/pass-prompts/templates/python-flask/pass2.md +85 -85
  51. package/pass-prompts/templates/python-flask/pass3.md +31 -13
  52. package/pass-prompts/templates/vue-nuxt/pass3.md +32 -13
  53. package/plan-installer/domain-grouper.js +76 -76
  54. package/plan-installer/index.js +137 -129
  55. package/plan-installer/prompt-generator.js +188 -128
  56. package/plan-installer/scanners/scan-frontend.js +505 -473
  57. package/plan-installer/scanners/scan-java.js +226 -226
  58. package/plan-installer/scanners/scan-node.js +57 -57
  59. package/plan-installer/scanners/scan-python.js +85 -85
  60. package/plan-installer/stack-detector.js +482 -466
  61. package/plan-installer/structure-scanner.js +65 -65
  62. package/sync-checker/index.js +177 -177
@@ -1,128 +1,188 @@
1
- /**
2
- * ClaudeOS-Core — Prompt Generator
3
- *
4
- * Generates dynamic prompts for Pass 1/2/3 based on detected stack and language.
5
- * Supports multi-stack combined prompts (backend + frontend merged in Pass 3).
6
- */
7
-
8
- const path = require("path");
9
- const { readFileSafe, readJsonSafe, existsSafe, writeFileSafe } = require("../lib/safe-fs");
10
-
11
- /**
12
- * Generate pass prompts from templates.
13
- * @param {object} templates - { backend: string|null, frontend: string|null }
14
- * @param {string} lang - output language code (e.g. "ko", "en")
15
- * @param {string} templatesDir - path to pass-prompts/templates/
16
- * @param {string} generatedDir - path to claudeos-core/generated/
17
- */
18
- function generatePrompts(templates, lang, templatesDir, generatedDir) {
19
- const commonDir = path.join(templatesDir, "common");
20
- const headerPath = path.join(commonDir, "header.md");
21
- const footerPath = path.join(commonDir, "pass3-footer.md");
22
- const langPath = path.join(commonDir, "lang-instructions.json");
23
- const stagingOverridePath = path.join(commonDir, "staging-override.md");
24
-
25
- const header = existsSafe(headerPath) ? readFileSafe(headerPath) : "";
26
- const footer = existsSafe(footerPath) ? readFileSafe(footerPath) : "";
27
- // Injected into pass3/pass4 prompts — redirects .claude/rules/* writes to
28
- // claudeos-core/generated/.staged-rules/* to bypass Claude Code's sensitive-
29
- // path block. The Node.js orchestrator moves the staged files after each pass.
30
- const stagingOverride = existsSafe(stagingOverridePath) ? readFileSafe(stagingOverridePath) + "\n" : "";
31
- // v2.1: Phase 1 "Read Once, Extract Facts" block prepended to every Pass 3
32
- // prompt. Teaches Claude to read pass2-merged.json exactly once into a
33
- // compact in-context fact table and reference that table for all subsequent
34
- // file generation — fixes the `Prompt is too long` failure on large projects
35
- // caused by 10-20× re-reads of pass2-merged.json. Also includes idempotent
36
- // skip rules (Rule B) so interrupted Pass 3 runs can resume safely.
37
- const phase1Path = path.join(commonDir, "pass3-phase1.md");
38
- const phase1 = existsSafe(phase1Path) ? readFileSafe(phase1Path) + "\n" : "";
39
-
40
- let langInstruction = "";
41
- if (lang && lang !== "en" && existsSafe(langPath)) {
42
- const langData = readJsonSafe(langPath);
43
- if (langData && langData.instructions && langData.instructions[lang]) {
44
- langInstruction = langData.instructions[lang];
45
- const label = (langData.labels && langData.labels[lang]) || lang;
46
- console.log(` 🌐 Language: ${label} (Pass 3 output)`);
47
- }
48
- }
49
-
50
- function readTemplate(templateName, passName) {
51
- const src = path.join(templatesDir, templateName, `${passName}.md`);
52
- if (!existsSafe(src)) return null;
53
- return readFileSafe(src);
54
- }
55
-
56
- const activeTemplates = [...new Set([templates.backend, templates.frontend].filter(Boolean))];
57
- const primaryTemplate = templates.backend || templates.frontend;
58
-
59
- for (let ti = 0; ti < activeTemplates.length; ti++) {
60
- const tmpl = activeTemplates[ti];
61
- const type = (tmpl === templates.frontend && tmpl !== templates.backend) ? "frontend"
62
- : (ti === 1 && templates.frontend) ? "frontend" : "backend";
63
- const body = readTemplate(tmpl, "pass1");
64
- if (body) {
65
- writeFileSafe(path.join(generatedDir, `pass1-${type}-prompt.md`), header + body);
66
- console.log(` ✅ pass1-${type}-prompt.md (${tmpl})`);
67
- }
68
- }
69
-
70
- if (primaryTemplate) {
71
- const body = readTemplate(primaryTemplate, "pass2");
72
- if (body) {
73
- writeFileSafe(path.join(generatedDir, "pass2-prompt.md"), header + body);
74
- console.log(` ✅ pass2-prompt.md (${primaryTemplate})`);
75
- }
76
- }
77
-
78
- if (primaryTemplate) {
79
- const primaryBody = readTemplate(primaryTemplate, "pass3");
80
- if (!primaryBody) {
81
- console.log(` ⚠️ pass3 template not found for ${primaryTemplate}, skipping`);
82
- return;
83
- }
84
- let combinedBody = primaryBody;
85
-
86
- if (templates.backend && templates.frontend && templates.backend !== templates.frontend) {
87
- const frontendBody = readTemplate(templates.frontend, "pass3");
88
- if (frontendBody) {
89
- combinedBody += "\n\n---\n\n";
90
- combinedBody += "# Additional: Frontend generation targets (auto-detected)\n\n";
91
- combinedBody += "In addition to the backend standards above, also generate the following frontend standards.\n";
92
- combinedBody += "Reference the frontend analysis results in pass2-merged.json.\n\n";
93
- const frontendSections = frontendBody
94
- .split(/\n(?=\d+\.\s)/)
95
- .filter(s => /frontend|component|page|routing|data[.\-]fetch|state|styling/i.test(s))
96
- .join("\n");
97
- if (frontendSections.trim()) combinedBody += frontendSections;
98
- }
99
- }
100
-
101
- writeFileSafe(
102
- path.join(generatedDir, "pass3-prompt.md"),
103
- header + langInstruction + stagingOverride + phase1 + combinedBody.trimEnd() + "\n" + footer
104
- );
105
- console.log(` ✅ pass3-prompt.md${templates.frontend && templates.backend ? " (multi-stack combined)" : ""}`);
106
- }
107
-
108
- // ─── Pass 4 (L4 memory + rules + CLAUDE.md append) ───
109
- const pass4Path = path.join(commonDir, "pass4.md");
110
- if (existsSafe(pass4Path)) {
111
- const langPath2 = path.join(commonDir, "lang-instructions.json");
112
- const langData2 = existsSafe(langPath2) ? readJsonSafe(langPath2) : null;
113
- const langLabel = (langData2 && langData2.labels && langData2.labels[lang]) || "English";
114
- let pass4Body = readFileSafe(pass4Path);
115
- // Replace {{LANG_NAME}} with the resolved language label.
116
- // Use a replacement function to be consistent with other placeholder
117
- // substitutions and to be safe against future labels that might contain
118
- // `$` characters (which would otherwise be interpreted as back-refs).
119
- pass4Body = pass4Body.replace(/\{\{LANG_NAME\}\}/g, () => langLabel);
120
- writeFileSafe(
121
- path.join(generatedDir, "pass4-prompt.md"),
122
- header + langInstruction + stagingOverride + pass4Body
123
- );
124
- console.log(` ✅ pass4-prompt.md (memory + rules, lang: ${langLabel})`);
125
- }
126
- }
127
-
128
- module.exports = { generatePrompts };
1
+ /**
2
+ * ClaudeOS-Core — Prompt Generator
3
+ *
4
+ * Generates dynamic prompts for Pass 1/2/3 based on detected stack and language.
5
+ * Supports multi-stack combined prompts (backend + frontend merged in Pass 3).
6
+ */
7
+
8
+ const path = require("path");
9
+ const { readFileSafe, readJsonSafe, existsSafe, writeFileSafe } = require("../lib/safe-fs");
10
+
11
+ /**
12
+ * Generate pass prompts from templates.
13
+ * @param {object} templates - { backend: string|null, frontend: string|null }
14
+ * @param {string} lang - output language code (e.g. "ko", "en")
15
+ * @param {string} templatesDir - path to pass-prompts/templates/
16
+ * @param {string} generatedDir - path to claudeos-core/generated/
17
+ */
18
+ function generatePrompts(templates, lang, templatesDir, generatedDir) {
19
+ const commonDir = path.join(templatesDir, "common");
20
+ const headerPath = path.join(commonDir, "header.md");
21
+ const footerPath = path.join(commonDir, "pass3-footer.md");
22
+ const langPath = path.join(commonDir, "lang-instructions.json");
23
+ const stagingOverridePath = path.join(commonDir, "staging-override.md");
24
+
25
+ const header = existsSafe(headerPath) ? readFileSafe(headerPath) : "";
26
+ const footer = existsSafe(footerPath) ? readFileSafe(footerPath) : "";
27
+ // Injected into pass3/pass4 prompts — redirects .claude/rules/* writes to
28
+ // claudeos-core/generated/.staged-rules/* to bypass Claude Code's sensitive-
29
+ // path block. The Node.js orchestrator moves the staged files after each pass.
30
+ const stagingOverride = existsSafe(stagingOverridePath) ? readFileSafe(stagingOverridePath) + "\n" : "";
31
+ // v2.1: Phase 1 "Read Once, Extract Facts" block prepended to every Pass 3
32
+ // prompt. Teaches Claude to read pass2-merged.json exactly once into a
33
+ // compact in-context fact table and reference that table for all subsequent
34
+ // file generation — fixes the `Prompt is too long` failure on large projects
35
+ // caused by 10-20× re-reads of pass2-merged.json. Also includes idempotent
36
+ // skip rules (Rule B) so interrupted Pass 3 runs can resume safely.
37
+ const phase1Path = path.join(commonDir, "pass3-phase1.md");
38
+ const phase1 = existsSafe(phase1Path) ? readFileSafe(phase1Path) + "\n" : "";
39
+
40
+ // v2.2: CLAUDE.md Scaffold — the 8-section deterministic template for CLAUDE.md.
41
+ // Embedded inline (not referenced by path) because the prompt runs in the user's
42
+ // project directory where the scaffold file does not exist. Stack-specific pass3
43
+ // templates and pass3-footer both reference "pass-prompts/templates/common/
44
+ // claude-md-scaffold.md" in their instructions, and this embed makes that
45
+ // reference resolvable via in-context content. Wrapped in explicit delimiters
46
+ // so the LLM can reliably locate the scaffold block.
47
+ // v2.3.0: Demote scaffold meta-section `##` headers to `###` before
48
+ // embedding. Inside the embedded scaffold, the only `##`-level headings
49
+ // visible to the LLM should be the 8 canonical CLAUDE.md sections inside
50
+ // the Template structure code block (`## 1. Role Definition` ... `## 8.`).
51
+ // Scaffold meta-sections like `## Why this scaffold exists`, `## Hard
52
+ // constraints`, `## Per-section generation rules`, `## Validation checks`,
53
+ // `## Examples`, `## Usage from pass3 prompts` used to share the same
54
+ // `##` level, producing 40+ `##` lines in the prompt and creating an
55
+ // unintended pattern bias ("this prompt has many `##` sections → my
56
+ // output should too"). Demotion is code-block-aware: `##` lines inside
57
+ // ``` or ~~~ fenced blocks are preserved so the Template structure
58
+ // example remains intact.
59
+ function demoteScaffoldMetaHeaders(scaffoldContent) {
60
+ const lines = scaffoldContent.split(/\r?\n/);
61
+ let inFence = false;
62
+ let fenceMarker = null;
63
+ return lines.map(line => {
64
+ const trimmed = line.trimStart();
65
+ const fenceMatch = trimmed.match(/^(```+|~~~+)/);
66
+ if (fenceMatch) {
67
+ if (!inFence) {
68
+ inFence = true;
69
+ fenceMarker = fenceMatch[1][0];
70
+ } else if (trimmed.startsWith(fenceMarker)) {
71
+ inFence = false;
72
+ fenceMarker = null;
73
+ }
74
+ return line;
75
+ }
76
+ // Only demote outside fenced code blocks.
77
+ // Also preserve the top-level `# CLAUDE.md Scaffold Template ...`
78
+ // single `#` — it's the scaffold doc title, not a section.
79
+ if (!inFence && /^## (?!#)/.test(line)) {
80
+ return line.replace(/^## /, "### ");
81
+ }
82
+ return line;
83
+ }).join("\n");
84
+ }
85
+
86
+ const scaffoldPath = path.join(commonDir, "claude-md-scaffold.md");
87
+ const scaffold = existsSafe(scaffoldPath)
88
+ ? "\n---\n\n# === EMBEDDED: claude-md-scaffold.md ===\n\n"
89
+ + "The content below is the scaffold referenced by stack-specific sections\n"
90
+ + "and the Pass 3 footer. Treat this embedded block as the authoritative\n"
91
+ + "source when instructions mention `pass-prompts/templates/common/claude-md-scaffold.md`.\n\n"
92
+ + "NOTE: Scaffold meta-section headers have been demoted from `##` to `###`\n"
93
+ + "when embedded here. The ONLY `##` headings visible in this block are the\n"
94
+ + "8 canonical CLAUDE.md sections inside the Template structure example —\n"
95
+ + "those 8 are the count your generated CLAUDE.md must match exactly.\n\n"
96
+ + demoteScaffoldMetaHeaders(readFileSafe(scaffoldPath))
97
+ + "\n\n# === END EMBEDDED: claude-md-scaffold.md ===\n\n---\n\n"
98
+ : "";
99
+
100
+ let langInstruction = "";
101
+ if (lang && lang !== "en" && existsSafe(langPath)) {
102
+ const langData = readJsonSafe(langPath);
103
+ if (langData && langData.instructions && langData.instructions[lang]) {
104
+ langInstruction = langData.instructions[lang];
105
+ const label = (langData.labels && langData.labels[lang]) || lang;
106
+ console.log(` 🌐 Language: ${label} (Pass 3 output)`);
107
+ }
108
+ }
109
+
110
+ function readTemplate(templateName, passName) {
111
+ const src = path.join(templatesDir, templateName, `${passName}.md`);
112
+ if (!existsSafe(src)) return null;
113
+ return readFileSafe(src);
114
+ }
115
+
116
+ const activeTemplates = [...new Set([templates.backend, templates.frontend].filter(Boolean))];
117
+ const primaryTemplate = templates.backend || templates.frontend;
118
+
119
+ for (let ti = 0; ti < activeTemplates.length; ti++) {
120
+ const tmpl = activeTemplates[ti];
121
+ const type = (tmpl === templates.frontend && tmpl !== templates.backend) ? "frontend"
122
+ : (ti === 1 && templates.frontend) ? "frontend" : "backend";
123
+ const body = readTemplate(tmpl, "pass1");
124
+ if (body) {
125
+ writeFileSafe(path.join(generatedDir, `pass1-${type}-prompt.md`), header + body);
126
+ console.log(` ✅ pass1-${type}-prompt.md (${tmpl})`);
127
+ }
128
+ }
129
+
130
+ if (primaryTemplate) {
131
+ const body = readTemplate(primaryTemplate, "pass2");
132
+ if (body) {
133
+ writeFileSafe(path.join(generatedDir, "pass2-prompt.md"), header + body);
134
+ console.log(` ✅ pass2-prompt.md (${primaryTemplate})`);
135
+ }
136
+ }
137
+
138
+ if (primaryTemplate) {
139
+ const primaryBody = readTemplate(primaryTemplate, "pass3");
140
+ if (!primaryBody) {
141
+ console.log(` ⚠️ pass3 template not found for ${primaryTemplate}, skipping`);
142
+ return;
143
+ }
144
+ let combinedBody = primaryBody;
145
+
146
+ if (templates.backend && templates.frontend && templates.backend !== templates.frontend) {
147
+ const frontendBody = readTemplate(templates.frontend, "pass3");
148
+ if (frontendBody) {
149
+ combinedBody += "\n\n---\n\n";
150
+ combinedBody += "# Additional: Frontend generation targets (auto-detected)\n\n";
151
+ combinedBody += "In addition to the backend standards above, also generate the following frontend standards.\n";
152
+ combinedBody += "Reference the frontend analysis results in pass2-merged.json.\n\n";
153
+ const frontendSections = frontendBody
154
+ .split(/\n(?=\d+\.\s)/)
155
+ .filter(s => /frontend|component|page|routing|data[.\-]fetch|state|styling/i.test(s))
156
+ .join("\n");
157
+ if (frontendSections.trim()) combinedBody += frontendSections;
158
+ }
159
+ }
160
+
161
+ writeFileSafe(
162
+ path.join(generatedDir, "pass3-prompt.md"),
163
+ header + langInstruction + stagingOverride + phase1 + scaffold + combinedBody.trimEnd() + "\n" + footer
164
+ );
165
+ console.log(` ✅ pass3-prompt.md${templates.frontend && templates.backend ? " (multi-stack combined)" : ""}`);
166
+ }
167
+
168
+ // ─── Pass 4 (L4 memory + rules + CLAUDE.md append) ───
169
+ const pass4Path = path.join(commonDir, "pass4.md");
170
+ if (existsSafe(pass4Path)) {
171
+ const langPath2 = path.join(commonDir, "lang-instructions.json");
172
+ const langData2 = existsSafe(langPath2) ? readJsonSafe(langPath2) : null;
173
+ const langLabel = (langData2 && langData2.labels && langData2.labels[lang]) || "English";
174
+ let pass4Body = readFileSafe(pass4Path);
175
+ // Replace {{LANG_NAME}} with the resolved language label.
176
+ // Use a replacement function to be consistent with other placeholder
177
+ // substitutions and to be safe against future labels that might contain
178
+ // `$` characters (which would otherwise be interpreted as back-refs).
179
+ pass4Body = pass4Body.replace(/\{\{LANG_NAME\}\}/g, () => langLabel);
180
+ writeFileSafe(
181
+ path.join(generatedDir, "pass4-prompt.md"),
182
+ header + langInstruction + stagingOverride + pass4Body
183
+ );
184
+ console.log(` ✅ pass4-prompt.md (memory + rules, lang: ${langLabel})`);
185
+ }
186
+ }
187
+
188
+ module.exports = { generatePrompts };