your-cto-jev 0.1.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/LICENSE +21 -0
- package/README.md +198 -0
- package/dist/agents.js +90 -0
- package/dist/api.js +123 -0
- package/dist/brain.js +100 -0
- package/dist/diff.js +66 -0
- package/dist/hooks.js +174 -0
- package/dist/i18n.js +130 -0
- package/dist/index.js +102 -0
- package/dist/masker.js +35 -0
- package/dist/setup.js +216 -0
- package/dist/types.js +1 -0
- package/dist/wizard.js +86 -0
- package/package.json +50 -0
package/dist/wizard.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { agentNames, agents } from './agents.js';
|
|
3
|
+
import { probeProvider, providers } from './api.js';
|
|
4
|
+
import { credentialsPath, loadCredentials, saveCredentials } from './brain.js';
|
|
5
|
+
import { t } from './i18n.js';
|
|
6
|
+
/** Terminal prompt; hidden input echoes nothing (for keys). */
|
|
7
|
+
export function terminalAsk() {
|
|
8
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
9
|
+
const anyRl = rl;
|
|
10
|
+
const write = anyRl._writeToOutput.bind(rl);
|
|
11
|
+
let muted = false;
|
|
12
|
+
anyRl._writeToOutput = (s) => { if (!muted)
|
|
13
|
+
write(s); };
|
|
14
|
+
const ask = async (q, hidden = false) => {
|
|
15
|
+
if (!hidden)
|
|
16
|
+
return (await rl.question(q)).trim();
|
|
17
|
+
process.stdout.write(q);
|
|
18
|
+
muted = true;
|
|
19
|
+
try {
|
|
20
|
+
return (await rl.question('')).trim();
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
muted = false;
|
|
24
|
+
process.stdout.write('\n');
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
return { ask, close: () => rl.close() };
|
|
28
|
+
}
|
|
29
|
+
const yes = (ans, def) => (ans ? /^y/i.test(ans) : def);
|
|
30
|
+
/**
|
|
31
|
+
* Interactive setup: pick agents (detected ones default to yes), then enter and verify keys.
|
|
32
|
+
* Keys already present in env are kept as-is unless forceKeys.
|
|
33
|
+
*/
|
|
34
|
+
export async function wizard(ask, lang, detected, log, forceKeys = false) {
|
|
35
|
+
log(t(lang, 'wiz_agents'));
|
|
36
|
+
const chosen = [];
|
|
37
|
+
for (const name of agentNames) {
|
|
38
|
+
const def = detected.includes(name);
|
|
39
|
+
const ans = await ask(t(lang, 'wiz_agent_q', { label: agents[name].label, hint: def ? 'Y/n' : 'y/N', found: def ? t(lang, 'wiz_found') : '' }));
|
|
40
|
+
if (yes(ans, def))
|
|
41
|
+
chosen.push(name);
|
|
42
|
+
}
|
|
43
|
+
const env = process.env;
|
|
44
|
+
const hasKey = !!(env.OPENROUTER_API_KEY || (env.CLOUDFLARE_API_TOKEN && env.CLOUDFLARE_ACCOUNT_ID));
|
|
45
|
+
if (hasKey && !forceKeys) {
|
|
46
|
+
log(t(lang, 'wiz_keys_present'));
|
|
47
|
+
return chosen;
|
|
48
|
+
}
|
|
49
|
+
log(t(lang, 'wiz_keys', { path: credentialsPath() }));
|
|
50
|
+
const creds = { ...loadCredentials() };
|
|
51
|
+
const tryKey = async (provider, set) => {
|
|
52
|
+
const saved = { ...env };
|
|
53
|
+
Object.assign(env, set);
|
|
54
|
+
log(t(lang, 'wiz_checking', { name: providers[provider].label }));
|
|
55
|
+
const r = await probeProvider(provider);
|
|
56
|
+
if (r.ok) {
|
|
57
|
+
log(t(lang, 'wiz_ok', { name: providers[provider].label }));
|
|
58
|
+
Object.assign(creds, set);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
log(t(lang, 'wiz_fail', { name: providers[provider].label, status: r.status, msg: r.message ?? '' }));
|
|
62
|
+
if (yes(await ask(t(lang, 'wiz_save_anyway')), false))
|
|
63
|
+
Object.assign(creds, set);
|
|
64
|
+
else
|
|
65
|
+
for (const k of Object.keys(set)) {
|
|
66
|
+
if (saved[k] === undefined)
|
|
67
|
+
delete env[k];
|
|
68
|
+
else
|
|
69
|
+
env[k] = saved[k];
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const or = await ask(t(lang, 'wiz_or_key'), true);
|
|
73
|
+
if (or)
|
|
74
|
+
await tryKey('openrouter', { OPENROUTER_API_KEY: or });
|
|
75
|
+
const acc = await ask(t(lang, 'wiz_cf_account'));
|
|
76
|
+
if (acc) {
|
|
77
|
+
const tok = await ask(t(lang, 'wiz_cf_token'), true);
|
|
78
|
+
if (tok)
|
|
79
|
+
await tryKey('cloudflare', { CLOUDFLARE_ACCOUNT_ID: acc, CLOUDFLARE_API_TOKEN: tok });
|
|
80
|
+
}
|
|
81
|
+
if (Object.keys(creds).length)
|
|
82
|
+
log(t(lang, 'wiz_saved', { path: saveCredentials(creds) }));
|
|
83
|
+
else
|
|
84
|
+
log(t(lang, 'wiz_no_keys'));
|
|
85
|
+
return chosen;
|
|
86
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "your-cto-jev",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A sharp-tongued CTO gatekeeper for AI coding agents: blocks leaked secrets in git commits and destructive shell commands in Claude Code, Cursor, Gemini CLI and Codex, using TypeSafe Jev.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"cursor",
|
|
8
|
+
"gemini-cli",
|
|
9
|
+
"codex",
|
|
10
|
+
"git-hooks",
|
|
11
|
+
"pre-commit",
|
|
12
|
+
"secrets",
|
|
13
|
+
"ai-agents",
|
|
14
|
+
"guardrails",
|
|
15
|
+
"jev"
|
|
16
|
+
],
|
|
17
|
+
"author": "rainday",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/rainday/your-cto-jev.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/rainday/your-cto-jev#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/rainday/your-cto-jev/issues"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"bin": {
|
|
29
|
+
"cto": "dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"!dist/*.test.js"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc",
|
|
40
|
+
"test": "tsc && node --test \"dist/*.test.js\"",
|
|
41
|
+
"prepublishOnly": "npm run build && node --test \"dist/*.test.js\""
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^26.6.3",
|
|
45
|
+
"typescript": "^7.0.2"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|