@callstack/apex 0.2.2 → 0.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.
- package/README.md +210 -27
- package/bin/apex.js +11 -2
- package/dist/cli.js +4859 -0
- package/package.json +25 -8
- package/src/assistants/aiSdk.js +0 -54
- package/src/assistants/claudeCode.js +0 -37
- package/src/assistants/codex.js +0 -56
- package/src/assistants/cursor.js +0 -30
- package/src/assistants/index.js +0 -11
- package/src/assistants/opencode.js +0 -103
- package/src/assistants/pi.js +0 -55
- package/src/assistants/vscodeCopilot.js +0 -34
- package/src/cli.js +0 -89
- package/src/init.js +0 -91
- package/src/util/detect.js +0 -49
- package/src/util/fsHelpers.js +0 -57
- package/src/util/prompt.js +0 -74
- package/src/util/toml.js +0 -35
package/src/util/fsHelpers.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('node:fs');
|
|
4
|
-
const path = require('node:path');
|
|
5
|
-
|
|
6
|
-
const backedUpThisRun = new Set();
|
|
7
|
-
|
|
8
|
-
function pathExists(p) {
|
|
9
|
-
try {
|
|
10
|
-
fs.accessSync(p);
|
|
11
|
-
return true;
|
|
12
|
-
} catch {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function readJsonIfExists(filePath) {
|
|
18
|
-
if (!pathExists(filePath)) return null;
|
|
19
|
-
const raw = fs.readFileSync(filePath, 'utf8');
|
|
20
|
-
if (!raw.trim()) return {};
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(raw);
|
|
23
|
-
} catch (err) {
|
|
24
|
-
throw new Error(`${filePath} exists but is not valid JSON: ${err.message}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function readTextIfExists(filePath) {
|
|
29
|
-
return pathExists(filePath) ? fs.readFileSync(filePath, 'utf8') : null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function backupFile(filePath) {
|
|
33
|
-
if (!pathExists(filePath) || backedUpThisRun.has(filePath)) return null;
|
|
34
|
-
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
35
|
-
const backupPath = `${filePath}.bak-${stamp}`;
|
|
36
|
-
fs.copyFileSync(filePath, backupPath);
|
|
37
|
-
backedUpThisRun.add(filePath);
|
|
38
|
-
return backupPath;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function writeFileEnsuringDir(filePath, contents) {
|
|
42
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
43
|
-
fs.writeFileSync(filePath, contents);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function writeJson(filePath, data) {
|
|
47
|
-
writeFileEnsuringDir(filePath, `${JSON.stringify(data, null, 2)}\n`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = {
|
|
51
|
-
pathExists,
|
|
52
|
-
readJsonIfExists,
|
|
53
|
-
readTextIfExists,
|
|
54
|
-
backupFile,
|
|
55
|
-
writeFileEnsuringDir,
|
|
56
|
-
writeJson,
|
|
57
|
-
};
|
package/src/util/prompt.js
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const readline = require('node:readline');
|
|
4
|
-
|
|
5
|
-
function ask(question) {
|
|
6
|
-
return new Promise((resolve) => {
|
|
7
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
-
rl.question(question, (answer) => {
|
|
9
|
-
rl.close();
|
|
10
|
-
resolve(answer.trim());
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function confirm(question, defaultYes = true) {
|
|
16
|
-
if (!process.stdin.isTTY) return defaultYes;
|
|
17
|
-
const hint = defaultYes ? 'Y/n' : 'y/N';
|
|
18
|
-
const answer = (await ask(`${question} (${hint}) `)).toLowerCase();
|
|
19
|
-
if (!answer) return defaultYes;
|
|
20
|
-
return answer === 'y' || answer === 'yes';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Char codes, spelled out numerically to avoid embedding raw control bytes in source.
|
|
24
|
-
const CODE_LF = 10;
|
|
25
|
-
const CODE_CR = 13;
|
|
26
|
-
const CODE_CTRL_C = 3;
|
|
27
|
-
const CODE_CTRL_D = 4;
|
|
28
|
-
const CODE_BACKSPACE = 8;
|
|
29
|
-
const CODE_DEL = 127;
|
|
30
|
-
|
|
31
|
-
function askHidden(question) {
|
|
32
|
-
if (!process.stdin.isTTY) return ask(question);
|
|
33
|
-
return new Promise((resolve) => {
|
|
34
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
35
|
-
const stdin = process.stdin;
|
|
36
|
-
process.stdout.write(question);
|
|
37
|
-
|
|
38
|
-
let value = '';
|
|
39
|
-
stdin.setRawMode(true);
|
|
40
|
-
stdin.resume();
|
|
41
|
-
stdin.setEncoding('utf8');
|
|
42
|
-
|
|
43
|
-
const onData = (chunk) => {
|
|
44
|
-
const char = chunk.toString();
|
|
45
|
-
const code = char.charCodeAt(0);
|
|
46
|
-
|
|
47
|
-
if (code === CODE_LF || code === CODE_CR) {
|
|
48
|
-
stdin.setRawMode(false);
|
|
49
|
-
stdin.removeListener('data', onData);
|
|
50
|
-
process.stdout.write('\n');
|
|
51
|
-
rl.close();
|
|
52
|
-
resolve(value.trim());
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (code === CODE_CTRL_C || code === CODE_CTRL_D) {
|
|
57
|
-
stdin.setRawMode(false);
|
|
58
|
-
process.stdout.write('\n');
|
|
59
|
-
process.exit(130);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (code === CODE_BACKSPACE || code === CODE_DEL) {
|
|
64
|
-
value = value.slice(0, -1);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
value += char;
|
|
69
|
-
};
|
|
70
|
-
stdin.on('data', onData);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
module.exports = { ask, askHidden, confirm };
|
package/src/util/toml.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Minimal helper to idempotently add/replace one top-level [section] block in a
|
|
4
|
-
// TOML file's raw text, without pulling in a TOML parser dependency. Good enough
|
|
5
|
-
// for the narrow, known-shape config files this CLI writes into.
|
|
6
|
-
function upsertTomlSection(existingText, sectionHeader, sectionBody) {
|
|
7
|
-
const block = `[${sectionHeader}]\n${sectionBody}`;
|
|
8
|
-
if (!existingText) return block.endsWith('\n') ? block : `${block}\n`;
|
|
9
|
-
|
|
10
|
-
const headerLine = `[${sectionHeader}]`;
|
|
11
|
-
const lines = existingText.split('\n');
|
|
12
|
-
const startIdx = lines.findIndex((line) => line.trim() === headerLine);
|
|
13
|
-
|
|
14
|
-
if (startIdx === -1) {
|
|
15
|
-
const needsBlankLine = existingText.length > 0 && !existingText.endsWith('\n\n');
|
|
16
|
-
const separator = existingText.endsWith('\n') ? (needsBlankLine ? '\n' : '') : '\n\n';
|
|
17
|
-
const suffix = block.endsWith('\n') ? block : `${block}\n`;
|
|
18
|
-
return `${existingText}${separator}${suffix}`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let endIdx = lines.length;
|
|
22
|
-
for (let i = startIdx + 1; i < lines.length; i += 1) {
|
|
23
|
-
if (lines[i].trim().startsWith('[')) {
|
|
24
|
-
endIdx = i;
|
|
25
|
-
break;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const before = lines.slice(0, startIdx);
|
|
30
|
-
const after = lines.slice(endIdx);
|
|
31
|
-
const newLines = [...before, ...block.split('\n').slice(0, -1), ...after];
|
|
32
|
-
return `${newLines.join('\n').replace(/\n+$/, '\n')}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
module.exports = { upsertTomlSection };
|