claudeos-core 1.7.1 → 2.0.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 +121 -0
- package/CONTRIBUTING.md +92 -59
- package/README.de.md +465 -240
- package/README.es.md +446 -223
- package/README.fr.md +461 -238
- package/README.hi.md +485 -261
- package/README.ja.md +440 -235
- package/README.ko.md +244 -56
- package/README.md +215 -47
- package/README.ru.md +462 -238
- package/README.vi.md +454 -230
- package/README.zh-CN.md +476 -252
- package/bin/cli.js +144 -140
- package/bin/commands/init.js +549 -45
- package/bin/commands/memory.js +426 -0
- package/bin/lib/cli-utils.js +206 -143
- package/bootstrap.sh +81 -390
- package/content-validator/index.js +436 -340
- package/lib/expected-guides.js +23 -0
- package/lib/expected-outputs.js +91 -0
- package/lib/language-config.js +35 -0
- package/lib/memory-scaffold.js +1014 -0
- package/lib/plan-parser.js +153 -149
- package/lib/staged-rules.js +118 -0
- package/manifest-generator/index.js +176 -171
- package/package.json +1 -1
- package/pass-json-validator/index.js +337 -299
- package/pass-prompts/templates/common/pass3-footer.md +16 -0
- package/pass-prompts/templates/common/pass4.md +317 -0
- package/pass-prompts/templates/common/staging-override.md +26 -0
- package/plan-installer/prompt-generator.js +120 -96
- package/plan-installer/scanners/scan-frontend.js +216 -9
- package/sync-checker/index.js +133 -132
package/bin/lib/cli-utils.js
CHANGED
|
@@ -1,143 +1,206 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ClaudeOS-Core — CLI Utilities
|
|
3
|
-
*
|
|
4
|
-
* Shared constants, execution helpers, and filesystem utilities for the CLI.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { execSync } = require("child_process");
|
|
8
|
-
const fs = require("fs");
|
|
9
|
-
const path = require("path");
|
|
10
|
-
const { ensureDir, existsSafe } = require("../../lib/safe-fs");
|
|
11
|
-
|
|
12
|
-
// ─── Path configuration ──────────────────────────────────────────
|
|
13
|
-
const TOOLS_DIR = path.resolve(__dirname, "../..");
|
|
14
|
-
const PROJECT_ROOT = process.cwd();
|
|
15
|
-
const GENERATED_DIR = path.join(PROJECT_ROOT, "claudeos-core/generated");
|
|
16
|
-
|
|
17
|
-
// ─── Language configuration ──────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeOS-Core — CLI Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared constants, execution helpers, and filesystem utilities for the CLI.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync, spawn } = require("child_process");
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { ensureDir, existsSafe } = require("../../lib/safe-fs");
|
|
11
|
+
|
|
12
|
+
// ─── Path configuration ──────────────────────────────────────────
|
|
13
|
+
const TOOLS_DIR = path.resolve(__dirname, "../..");
|
|
14
|
+
const PROJECT_ROOT = process.cwd();
|
|
15
|
+
const GENERATED_DIR = path.join(PROJECT_ROOT, "claudeos-core/generated");
|
|
16
|
+
|
|
17
|
+
// ─── Language configuration ──────────────────────────────────────
|
|
18
|
+
// Single source of truth: lib/language-config.js. We re-export under the
|
|
19
|
+
// historical name SUPPORTED_LANGS so existing imports keep working.
|
|
20
|
+
const { LANGUAGES: SUPPORTED_LANGS, LANG_CODES, isValidLang } = require("../../lib/language-config");
|
|
21
|
+
|
|
22
|
+
// ─── Output ─────────────────────────────────────────────────────
|
|
23
|
+
function log(msg) {
|
|
24
|
+
console.log(msg);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function header(title) {
|
|
28
|
+
log("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
29
|
+
log(title);
|
|
30
|
+
log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ─── Execution ──────────────────────────────────────────────────
|
|
34
|
+
function run(cmd, options = {}) {
|
|
35
|
+
try {
|
|
36
|
+
execSync(cmd, {
|
|
37
|
+
cwd: options.cwd || PROJECT_ROOT,
|
|
38
|
+
stdio: options.silent ? ["pipe", "pipe", "pipe"] : "inherit",
|
|
39
|
+
encoding: "utf-8",
|
|
40
|
+
timeout: options.timeout || 0,
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
43
|
+
} catch (e) {
|
|
44
|
+
if (options.ignoreError) return false;
|
|
45
|
+
throw e;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Run claude -p: pass prompt via stdin (no shell pipe — avoids command injection)
|
|
50
|
+
function runClaudePrompt(prompt, options = {}) {
|
|
51
|
+
try {
|
|
52
|
+
execSync("claude -p --dangerously-skip-permissions", {
|
|
53
|
+
input: prompt,
|
|
54
|
+
cwd: options.cwd || PROJECT_ROOT,
|
|
55
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
56
|
+
encoding: "utf-8",
|
|
57
|
+
timeout: 0,
|
|
58
|
+
});
|
|
59
|
+
return true;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (options.ignoreError) return false;
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Async variant of runClaudePrompt using spawn — does NOT block the event loop,
|
|
67
|
+
// so the caller can run setInterval-based progress callbacks concurrently.
|
|
68
|
+
// Resolves to true on exit code 0, false otherwise (no throw; mirrors ignoreError:true).
|
|
69
|
+
// onTick is invoked every tickMs while claude is running; stopped on close.
|
|
70
|
+
// shell:true on Windows so that `claude.cmd`/`claude.ps1` shims resolve via PATH.
|
|
71
|
+
function runClaudePromptAsync(prompt, options = {}) {
|
|
72
|
+
return new Promise((resolve) => {
|
|
73
|
+
let child = null;
|
|
74
|
+
let tickTimer = null;
|
|
75
|
+
const cleanup = () => { if (tickTimer) { clearInterval(tickTimer); tickTimer = null; } };
|
|
76
|
+
const bail = () => {
|
|
77
|
+
cleanup();
|
|
78
|
+
// Best-effort kill so we don't leave orphaned claude processes when we
|
|
79
|
+
// fail to hand off the prompt or encounter an unexpected spawn error.
|
|
80
|
+
if (child && !child.killed) { try { child.kill(); } catch (_e) { /* ignore */ } }
|
|
81
|
+
resolve(false);
|
|
82
|
+
};
|
|
83
|
+
try {
|
|
84
|
+
// Node 18+ emits DEP0190 when mixing shell:true with an args array (the
|
|
85
|
+
// args aren't escaped — just concatenated). On Windows we need shell:true
|
|
86
|
+
// so `claude.cmd`/`claude.ps1` shims resolve via PATH, so we build the
|
|
87
|
+
// whole command as a single string there and pass an empty args array.
|
|
88
|
+
// The flags are hardcoded literals (no user input) so there's no
|
|
89
|
+
// injection surface either way; this just silences the warning.
|
|
90
|
+
const isWin = process.platform === "win32";
|
|
91
|
+
const spawnCmd = isWin ? "claude -p --dangerously-skip-permissions" : "claude";
|
|
92
|
+
const spawnArgs = isWin ? [] : ["-p", "--dangerously-skip-permissions"];
|
|
93
|
+
child = spawn(spawnCmd, spawnArgs, {
|
|
94
|
+
cwd: options.cwd || PROJECT_ROOT,
|
|
95
|
+
stdio: ["pipe", "inherit", "inherit"],
|
|
96
|
+
shell: isWin,
|
|
97
|
+
});
|
|
98
|
+
if (typeof options.onTick === "function" && options.tickMs > 0) {
|
|
99
|
+
// Fire once immediately so the user sees the progress line right away,
|
|
100
|
+
// instead of waiting a full tick interval for any feedback.
|
|
101
|
+
try { options.onTick(); } catch (_e) { /* swallow */ }
|
|
102
|
+
tickTimer = setInterval(() => {
|
|
103
|
+
try { options.onTick(); } catch (_e) { /* swallow — progress is best-effort */ }
|
|
104
|
+
}, options.tickMs);
|
|
105
|
+
}
|
|
106
|
+
child.on("close", (code) => { cleanup(); resolve(code === 0); });
|
|
107
|
+
child.on("error", () => { cleanup(); resolve(false); });
|
|
108
|
+
child.stdin.on("error", () => { bail(); });
|
|
109
|
+
child.stdin.write(prompt);
|
|
110
|
+
child.stdin.end();
|
|
111
|
+
} catch (_e) {
|
|
112
|
+
// Catches synchronous throws from spawn (e.g. ENAMETOOLONG on some
|
|
113
|
+
// platforms) or from the initial stdin.write before listeners were
|
|
114
|
+
// attached. Either way: no orphan child, no leaked interval.
|
|
115
|
+
bail();
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Run claude -p but CAPTURE stdout instead of inheriting it.
|
|
121
|
+
// Returns the captured stdout string on success, or null on failure.
|
|
122
|
+
// Used for short tasks where we need the response content (e.g. translation).
|
|
123
|
+
// maxBuffer default is 10MB — enough for document translation.
|
|
124
|
+
function runClaudeCapture(prompt, options = {}) {
|
|
125
|
+
try {
|
|
126
|
+
const out = execSync("claude -p --dangerously-skip-permissions", {
|
|
127
|
+
input: prompt,
|
|
128
|
+
cwd: options.cwd || PROJECT_ROOT,
|
|
129
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
130
|
+
encoding: "utf-8",
|
|
131
|
+
timeout: options.timeout || 0,
|
|
132
|
+
maxBuffer: options.maxBuffer || 10 * 1024 * 1024,
|
|
133
|
+
});
|
|
134
|
+
return out;
|
|
135
|
+
} catch (_e) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ─── Filesystem ─────────────────────────────────────────────────
|
|
141
|
+
// ensureDir: delegated to lib/safe-fs.js (single source of truth)
|
|
142
|
+
// fileExists: alias for existsSafe from lib/safe-fs.js
|
|
143
|
+
const fileExists = existsSafe;
|
|
144
|
+
|
|
145
|
+
// readFile: intentionally throws on error (CLI needs hard failure, unlike safe-fs fallback)
|
|
146
|
+
function readFile(p) {
|
|
147
|
+
return fs.readFileSync(p, "utf-8");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function injectProjectRoot(text) {
|
|
151
|
+
// Normalize to forward slashes for prompts (Claude interprets backslashes as escapes)
|
|
152
|
+
const normalizedRoot = PROJECT_ROOT.replace(/\\/g, "/");
|
|
153
|
+
// Use a replacement function so that `$`, `$1`, `$&`, `$$` in the
|
|
154
|
+
// project path (rare but possible on some systems) are preserved as
|
|
155
|
+
// literal characters rather than interpreted as regex specials.
|
|
156
|
+
return text.replace(/\{\{PROJECT_ROOT\}\}/g, () => normalizedRoot);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── Helpers ────────────────────────────────────────────────────
|
|
160
|
+
function pad(str, len) {
|
|
161
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function countFiles() {
|
|
165
|
+
try {
|
|
166
|
+
let count = 0;
|
|
167
|
+
const skipDirs = ["node_modules", "generated"];
|
|
168
|
+
const visited = new Set();
|
|
169
|
+
const scan = (dir) => {
|
|
170
|
+
if (!fs.existsSync(dir)) return;
|
|
171
|
+
let realDir;
|
|
172
|
+
try { realDir = fs.realpathSync(dir); } catch (_e) { realDir = dir; }
|
|
173
|
+
if (visited.has(realDir)) return;
|
|
174
|
+
visited.add(realDir);
|
|
175
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
176
|
+
if (skipDirs.includes(entry.name)) continue;
|
|
177
|
+
const full = path.join(dir, entry.name);
|
|
178
|
+
if (entry.isDirectory()) scan(full);
|
|
179
|
+
else count++;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
scan(path.join(PROJECT_ROOT, ".claude"));
|
|
183
|
+
scan(path.join(PROJECT_ROOT, "claudeos-core"));
|
|
184
|
+
return count;
|
|
185
|
+
} catch (e) {
|
|
186
|
+
return "?";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function countPass1Files() {
|
|
191
|
+
try {
|
|
192
|
+
return fs
|
|
193
|
+
.readdirSync(GENERATED_DIR)
|
|
194
|
+
.filter((f) => f.startsWith("pass1-") && f.endsWith(".json")).length;
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
TOOLS_DIR, PROJECT_ROOT, GENERATED_DIR,
|
|
202
|
+
SUPPORTED_LANGS, LANG_CODES, isValidLang,
|
|
203
|
+
log, header, run, runClaudePrompt, runClaudePromptAsync, runClaudeCapture,
|
|
204
|
+
ensureDir, fileExists, readFile, injectProjectRoot,
|
|
205
|
+
pad, countFiles, countPass1Files,
|
|
206
|
+
};
|