enigma-cli 1.1.1 → 1.1.2
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/assets/skills/backend-policy/skill.json +1 -1
- package/assets/skills/ciphera-style-policy/skill.json +1 -1
- package/assets/skills/code-review-policy/skill.json +1 -1
- package/assets/skills/core-engineering-policy/skill.json +1 -1
- package/assets/skills/database-expert/skill.json +1 -1
- package/assets/skills/debugging-policy/skill.json +1 -1
- package/assets/skills/dependency-policy/skill.json +1 -1
- package/assets/skills/frontend-policy/skill.json +1 -1
- package/assets/skills/git-policy/skill.json +1 -1
- package/assets/skills/security-policy/skill.json +1 -1
- package/assets/skills/testing-policy/skill.json +1 -1
- package/assets/skills/validation-policy/skill.json +1 -1
- package/dist/enigma.js +329 -59
- package/package.json +1 -1
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Backend/API architecture: controller-service-repository layering, API and request optimization, server-side caching (Redis), and Zod boundary validation.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "c442bc9e39a7710cb709ef2abb8d15ecd8aa16ed4f5c8af92b7af6877401cba4"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.1.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Ciphera code style conventions (formatting, naming, imports, comments, code-level anti-patterns; TypeScript-first, language-agnostic).",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "f8602bb79fbbe063ab39fbd59d0b7844a22c3a1583fcd11c1b4f98a2fe8ddc86"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Pre-delivery self-review gate, prioritized review dimensions, and change-quality criteria.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "3d3bbe0602d5bbb4afe37648fe3c2fa39376b1bcbac5d8c441f01fad1e866ed0"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.4.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Core engineering execution policy and harness orchestration (highest-authority rules).",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "c9c69c59516794311cb7b306ed4d4ad971824de3689a39c2b86c7669c73f2e8b"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Senior database architecture policy: query optimization, anti-duplication/normalization, scalability, and RGPD/GDPR encryption.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "c4617ee8d1a57d9621c81bef3093e94de91f79eec0cc0ead41f6d18dd443e623"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Reproduce-isolate-fix debugging methodology with root-cause discipline and regression verification.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "14b0064c8b33a0dc85e51464b05005cf5801c756b1101789a6924b9548420f6b"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Dependency and supply-chain security: lockfiles and reproducible installs, version pinning, vulnerability auditing, vetting/minimizing packages, vendoring, and SBOM/provenance.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Frontend architecture: reusable components, abstraction thresholds, state management, and optimistic UI with rollback.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "b0355b0e15f9f528d32adf19f0722d2727cd64d6b3544307ecc7a3141338f023"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Application and AI-agent security: secrets, authn/authz (least privilege), OWASP Top 10, transport/crypto baseline, secure logging, and agent/MCP/tool-use safety.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "9971e9d9127397d0152e89d24aad3191e2935e55a8483db7fd15f5d4d7a60e7a"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Test strategy, coverage gates, deterministic tests, mocking discipline, and regression-first bug fixing.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "d19fa8ec7985ed231478be504d3c80360897f555d0bc0624bea19c091f459fb0"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"provider": "FJRG2007/enigma",
|
|
5
5
|
"description": "Strict frontend + backend schema validation, schema consistency, and safe client-facing error handling.",
|
|
6
|
-
"cliVersion": "1.1.
|
|
6
|
+
"cliVersion": "1.1.2",
|
|
7
7
|
"sha": "a33622a2f810ee4cea39824cb1a7ca34b355a917d4224025df50d77dd74f0b3a"
|
|
8
8
|
}
|
package/dist/enigma.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { dirname as dirname4, join as join10 } from "path";
|
|
5
5
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6
|
-
import * as
|
|
6
|
+
import * as p6 from "@clack/prompts";
|
|
7
7
|
|
|
8
8
|
// src/util.ts
|
|
9
9
|
import { existsSync, statSync, readFileSync } from "fs";
|
|
@@ -257,6 +257,54 @@ function disableClaudeAttribution(scope) {
|
|
|
257
257
|
writeFileSync2(path, JSON.stringify(next, null, 2) + "\n");
|
|
258
258
|
return true;
|
|
259
259
|
}
|
|
260
|
+
function getClaudeAttribution(scope) {
|
|
261
|
+
const current = readJson(claudeSettingsPath(scope)) || {};
|
|
262
|
+
const attribution = current.attribution;
|
|
263
|
+
const disabled = Boolean(attribution) && attribution.commit === "" && attribution.pr === "" && current.includeCoAuthoredBy === false;
|
|
264
|
+
return !disabled;
|
|
265
|
+
}
|
|
266
|
+
function setClaudeAttribution(scope, enabled) {
|
|
267
|
+
if (!enabled) return disableClaudeAttribution(scope);
|
|
268
|
+
const path = claudeSettingsPath(scope);
|
|
269
|
+
const current = readJson(path) || {};
|
|
270
|
+
const attribution = typeof current.attribution === "object" && current.attribution !== null ? { ...current.attribution } : {};
|
|
271
|
+
let changed = false;
|
|
272
|
+
if (attribution.commit === "") {
|
|
273
|
+
delete attribution.commit;
|
|
274
|
+
changed = true;
|
|
275
|
+
}
|
|
276
|
+
if (attribution.pr === "") {
|
|
277
|
+
delete attribution.pr;
|
|
278
|
+
changed = true;
|
|
279
|
+
}
|
|
280
|
+
if (current.includeCoAuthoredBy === false) changed = true;
|
|
281
|
+
if (!changed) return false;
|
|
282
|
+
const next = { ...current };
|
|
283
|
+
if (Object.keys(attribution).length) next.attribution = attribution;
|
|
284
|
+
else delete next.attribution;
|
|
285
|
+
delete next.includeCoAuthoredBy;
|
|
286
|
+
writeClaudeSettings(path, next);
|
|
287
|
+
return true;
|
|
288
|
+
}
|
|
289
|
+
function getClaudeBypass(scope) {
|
|
290
|
+
const current = readJson(claudeSettingsPath(scope)) || {};
|
|
291
|
+
const permissions = current.permissions;
|
|
292
|
+
return Boolean(permissions) && permissions.defaultMode === "bypassPermissions";
|
|
293
|
+
}
|
|
294
|
+
function setClaudeBypass(scope, on, dryRun) {
|
|
295
|
+
if (on) return enableClaudeBypass(scope, dryRun);
|
|
296
|
+
const path = claudeSettingsPath(scope);
|
|
297
|
+
const current = readJson(path) || {};
|
|
298
|
+
const permissions = typeof current.permissions === "object" && current.permissions !== null ? { ...current.permissions } : {};
|
|
299
|
+
if (permissions.defaultMode !== "bypassPermissions") return { path, changed: false };
|
|
300
|
+
if (dryRun) return { path, changed: true };
|
|
301
|
+
delete permissions.defaultMode;
|
|
302
|
+
const next = { ...current };
|
|
303
|
+
if (Object.keys(permissions).length) next.permissions = permissions;
|
|
304
|
+
else delete next.permissions;
|
|
305
|
+
writeClaudeSettings(path, next);
|
|
306
|
+
return { path, changed: true };
|
|
307
|
+
}
|
|
260
308
|
function enableClaudeBypass(scope, dryRun) {
|
|
261
309
|
const path = claudeSettingsPath(scope);
|
|
262
310
|
const current = readJson(path) || {};
|
|
@@ -264,10 +312,13 @@ function enableClaudeBypass(scope, dryRun) {
|
|
|
264
312
|
if (permissions.defaultMode === "bypassPermissions") return { path, changed: false };
|
|
265
313
|
if (dryRun) return { path, changed: true };
|
|
266
314
|
const next = { ...current, permissions: { ...permissions, defaultMode: "bypassPermissions" } };
|
|
315
|
+
writeClaudeSettings(path, next);
|
|
316
|
+
return { path, changed: true };
|
|
317
|
+
}
|
|
318
|
+
function writeClaudeSettings(path, data) {
|
|
267
319
|
const dir = join4(path, "..");
|
|
268
320
|
if (!isDir(dir)) mkdirSync2(dir, { recursive: true });
|
|
269
|
-
writeFileSync2(path, JSON.stringify(
|
|
270
|
-
return { path, changed: true };
|
|
321
|
+
writeFileSync2(path, JSON.stringify(data, null, 2) + "\n");
|
|
271
322
|
}
|
|
272
323
|
|
|
273
324
|
// src/permissions.ts
|
|
@@ -323,6 +374,74 @@ function enableFor(name, scope, dryRun) {
|
|
|
323
374
|
return null;
|
|
324
375
|
}
|
|
325
376
|
}
|
|
377
|
+
function getBypass(name, scope) {
|
|
378
|
+
switch (name) {
|
|
379
|
+
case "claude":
|
|
380
|
+
return getClaudeBypass(scope);
|
|
381
|
+
case "codex":
|
|
382
|
+
return getCodexBypass();
|
|
383
|
+
case "opencode":
|
|
384
|
+
return getOpencodeBypass(scope);
|
|
385
|
+
default:
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function setBypass(name, scope, on, dryRun) {
|
|
390
|
+
switch (name) {
|
|
391
|
+
case "claude":
|
|
392
|
+
return setClaudeBypass(scope, on, dryRun);
|
|
393
|
+
case "codex":
|
|
394
|
+
return on ? enableCodexBypass(dryRun) : disableCodexBypass(dryRun);
|
|
395
|
+
case "opencode":
|
|
396
|
+
return on ? enableOpencodeBypass(scope, dryRun) : disableOpencodeBypass(scope, dryRun);
|
|
397
|
+
default:
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
function getCodexBypass() {
|
|
402
|
+
const path = join5(homedir3(), ".codex", "config.toml");
|
|
403
|
+
const content = existsSync4(path) ? readFileSync2(path, "utf8") : "";
|
|
404
|
+
return getTomlTopLevelKey(content, "approval_policy") === '"never"';
|
|
405
|
+
}
|
|
406
|
+
function disableCodexBypass(dryRun) {
|
|
407
|
+
const path = join5(homedir3(), ".codex", "config.toml");
|
|
408
|
+
const before = existsSync4(path) ? readFileSync2(path, "utf8") : "";
|
|
409
|
+
let after = removeTomlTopLevelKey(before, "approval_policy");
|
|
410
|
+
after = removeTomlTopLevelKey(after, "sandbox_mode");
|
|
411
|
+
const changed = after !== before;
|
|
412
|
+
if (changed && !dryRun) writeFileSync3(path, after);
|
|
413
|
+
return { path, changed };
|
|
414
|
+
}
|
|
415
|
+
function getOpencodeBypass(scope) {
|
|
416
|
+
const path = opencodeConfigPath(scope);
|
|
417
|
+
const perm = (readJson(path) || {}).permission;
|
|
418
|
+
return perm === "allow" || typeof perm === "object" && perm !== null && perm["*"] === "allow";
|
|
419
|
+
}
|
|
420
|
+
function disableOpencodeBypass(scope, dryRun) {
|
|
421
|
+
const path = opencodeConfigPath(scope);
|
|
422
|
+
const current = readJson(path) || {};
|
|
423
|
+
const perm = current.permission;
|
|
424
|
+
if (!getOpencodeBypass(scope)) return { path, changed: false };
|
|
425
|
+
if (dryRun) return { path, changed: true };
|
|
426
|
+
const next = { ...current };
|
|
427
|
+
if (typeof perm === "object" && perm !== null) {
|
|
428
|
+
const rest = {};
|
|
429
|
+
for (const k of Object.keys(perm)) {
|
|
430
|
+
if (k !== "*") rest[k] = perm[k];
|
|
431
|
+
}
|
|
432
|
+
if (Object.keys(rest).length) next.permission = rest;
|
|
433
|
+
else delete next.permission;
|
|
434
|
+
} else {
|
|
435
|
+
delete next.permission;
|
|
436
|
+
}
|
|
437
|
+
const dir = join5(path, "..");
|
|
438
|
+
if (!isDir(dir)) mkdirSync3(dir, { recursive: true });
|
|
439
|
+
writeFileSync3(path, JSON.stringify(next, null, 2) + "\n");
|
|
440
|
+
return { path, changed: true };
|
|
441
|
+
}
|
|
442
|
+
function opencodeConfigPath(scope) {
|
|
443
|
+
return scope === "global" ? join5(homedir3(), ".config", "opencode", "opencode.json") : join5(process.cwd(), "opencode.json");
|
|
444
|
+
}
|
|
326
445
|
function enableCodexBypass(dryRun) {
|
|
327
446
|
const path = join5(homedir3(), ".codex", "config.toml");
|
|
328
447
|
const before = existsSync4(path) ? readFileSync2(path, "utf8") : "";
|
|
@@ -337,7 +456,7 @@ function enableCodexBypass(dryRun) {
|
|
|
337
456
|
return { path, changed };
|
|
338
457
|
}
|
|
339
458
|
function enableOpencodeBypass(scope, dryRun) {
|
|
340
|
-
const path = scope
|
|
459
|
+
const path = opencodeConfigPath(scope);
|
|
341
460
|
const current = readJson(path) || {};
|
|
342
461
|
const perm = current.permission;
|
|
343
462
|
const alreadyAllowAll = perm === "allow" || typeof perm === "object" && perm !== null && perm["*"] === "allow";
|
|
@@ -372,6 +491,27 @@ function setTomlTopLevelKey(content, key, tomlValue) {
|
|
|
372
491
|
lines.splice(insertAt, 0, ...followsTable ? [assign, ""] : [assign]);
|
|
373
492
|
return normalizeTrailingNewline(lines.join("\n"));
|
|
374
493
|
}
|
|
494
|
+
function getTomlTopLevelKey(content, key) {
|
|
495
|
+
const lines = content.split("\n");
|
|
496
|
+
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
497
|
+
const scanEnd = firstTable === -1 ? lines.length : firstTable;
|
|
498
|
+
const keyRe = new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`);
|
|
499
|
+
for (let i = 0; i < scanEnd; i++) {
|
|
500
|
+
const m = keyRe.exec(lines[i]);
|
|
501
|
+
if (m) return m[1];
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
function removeTomlTopLevelKey(content, key) {
|
|
506
|
+
if (content.trim() === "") return content;
|
|
507
|
+
const lines = content.split("\n");
|
|
508
|
+
const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
|
|
509
|
+
const scanEnd = firstTable === -1 ? lines.length : firstTable;
|
|
510
|
+
const keyRe = new RegExp(`^\\s*${key}\\s*=`);
|
|
511
|
+
const kept = lines.filter((l, i) => !(i < scanEnd && keyRe.test(l)));
|
|
512
|
+
if (kept.length === lines.length) return content;
|
|
513
|
+
return normalizeTrailingNewline(kept.join("\n"));
|
|
514
|
+
}
|
|
375
515
|
function normalizeTrailingNewline(s) {
|
|
376
516
|
return `${s.replace(/\s+$/, "")}
|
|
377
517
|
`;
|
|
@@ -920,14 +1060,15 @@ if (isGuardEntry && fileURLToPath3(import.meta.url) === guardEntry) {
|
|
|
920
1060
|
process.exit(runGuardCli(process.argv.includes("--all")));
|
|
921
1061
|
}
|
|
922
1062
|
|
|
1063
|
+
// src/settings.ts
|
|
1064
|
+
import * as p4 from "@clack/prompts";
|
|
1065
|
+
|
|
923
1066
|
// src/config.ts
|
|
924
1067
|
import { homedir as homedir4 } from "os";
|
|
925
1068
|
import { join as join8 } from "path";
|
|
926
1069
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
927
1070
|
var CONFIG_FILE = ".enigma.json";
|
|
928
|
-
var CONFIG_DEFAULTS = { commitEmoji: true };
|
|
929
|
-
var BOOLEAN_KEYS = ["commitEmoji"];
|
|
930
|
-
var CLI_KEYS = { "commit-emoji": "commitEmoji" };
|
|
1071
|
+
var CONFIG_DEFAULTS = { commitEmoji: true, updateNotifier: true };
|
|
931
1072
|
function configPath(scope) {
|
|
932
1073
|
return scope === "global" ? join8(homedir4(), CONFIG_FILE) : join8(process.cwd(), CONFIG_FILE);
|
|
933
1074
|
}
|
|
@@ -944,13 +1085,7 @@ function readConfig() {
|
|
|
944
1085
|
}
|
|
945
1086
|
return { config, sources };
|
|
946
1087
|
}
|
|
947
|
-
function
|
|
948
|
-
const v = value.toLowerCase();
|
|
949
|
-
if (["on", "true", "yes", "1", "enable", "enabled"].includes(v)) return true;
|
|
950
|
-
if (["off", "false", "no", "0", "disable", "disabled"].includes(v)) return false;
|
|
951
|
-
return null;
|
|
952
|
-
}
|
|
953
|
-
function setValue(scope, key, value) {
|
|
1088
|
+
function setEnigmaToggle(key, value, scope) {
|
|
954
1089
|
const path = configPath(scope);
|
|
955
1090
|
const current = readJson(path) || {};
|
|
956
1091
|
const next = { ...current, [key]: value };
|
|
@@ -959,26 +1094,156 @@ function setValue(scope, key, value) {
|
|
|
959
1094
|
writeFileSync5(path, JSON.stringify(next, null, 2) + "\n");
|
|
960
1095
|
return path;
|
|
961
1096
|
}
|
|
962
|
-
|
|
1097
|
+
|
|
1098
|
+
// src/settings.ts
|
|
1099
|
+
function enigmaToggle(key, field, label, hint) {
|
|
1100
|
+
return {
|
|
1101
|
+
key,
|
|
1102
|
+
label,
|
|
1103
|
+
hint,
|
|
1104
|
+
read: () => readConfig().config[field],
|
|
1105
|
+
write: (value, scope) => ({ path: setEnigmaToggle(field, value, scope), changed: true })
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
var CATEGORIES = [
|
|
1109
|
+
{
|
|
1110
|
+
title: "General",
|
|
1111
|
+
blurb: "enigma runtime toggles (.enigma.json)",
|
|
1112
|
+
settings: [
|
|
1113
|
+
enigmaToggle("commit-emoji", "commitEmoji", "Commit subject emoji", "leading gitmoji on commit subjects"),
|
|
1114
|
+
enigmaToggle("update-notifier", "updateNotifier", "Update notifications", "notify when a newer enigma-cli is published")
|
|
1115
|
+
]
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
title: "Git & attribution",
|
|
1119
|
+
blurb: "how the coding agent attributes its work in git",
|
|
1120
|
+
settings: [
|
|
1121
|
+
{
|
|
1122
|
+
key: "claude-attribution",
|
|
1123
|
+
label: "Claude commit attribution",
|
|
1124
|
+
hint: "let Claude Code commit as its own contributor (Co-Authored-By / PR footer); enigma default: off",
|
|
1125
|
+
read: (scope) => getClaudeAttribution(scope),
|
|
1126
|
+
write: (value, scope) => ({ changed: setClaudeAttribution(scope, value) })
|
|
1127
|
+
}
|
|
1128
|
+
]
|
|
1129
|
+
},
|
|
1130
|
+
{
|
|
1131
|
+
title: "Permissions",
|
|
1132
|
+
blurb: "approval-prompt bypass per agent (security trade-off)",
|
|
1133
|
+
settings: BYPASS_SUPPORTED.map((name) => ({
|
|
1134
|
+
key: `bypass-${name}`,
|
|
1135
|
+
label: `${AGENTS[name]?.label || name} approval bypass`,
|
|
1136
|
+
hint: name === "codex" ? "skip approval prompts (global ~/.codex only)" : "skip per-action approval prompts",
|
|
1137
|
+
globalOnly: name === "codex",
|
|
1138
|
+
read: (scope) => getBypass(name, scope),
|
|
1139
|
+
write: (value, scope) => setBypass(name, scope, value, false) || { changed: false }
|
|
1140
|
+
}))
|
|
1141
|
+
}
|
|
1142
|
+
];
|
|
1143
|
+
var ALL_SETTINGS = CATEGORIES.flatMap((c) => c.settings);
|
|
1144
|
+
function valueLabel(on) {
|
|
1145
|
+
return on ? "on" : "off";
|
|
1146
|
+
}
|
|
1147
|
+
function parseBool(value) {
|
|
1148
|
+
const v = value.toLowerCase();
|
|
1149
|
+
if (["on", "true", "yes", "1", "enable", "enabled"].includes(v)) return true;
|
|
1150
|
+
if (["off", "false", "no", "0", "disable", "disabled"].includes(v)) return false;
|
|
1151
|
+
return null;
|
|
1152
|
+
}
|
|
1153
|
+
async function runSettingsMenu() {
|
|
1154
|
+
for (; ; ) {
|
|
1155
|
+
const choice = await p4.select({
|
|
1156
|
+
message: "Settings - choose a category",
|
|
1157
|
+
options: [
|
|
1158
|
+
...CATEGORIES.map((c) => ({ value: c.title, label: c.title, hint: c.blurb })),
|
|
1159
|
+
{ value: "__back", label: "< Back" }
|
|
1160
|
+
]
|
|
1161
|
+
});
|
|
1162
|
+
if (p4.isCancel(choice) || choice === "__back") return;
|
|
1163
|
+
await runCategory(CATEGORIES.find((c) => c.title === choice));
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
async function runCategory(category) {
|
|
1167
|
+
for (; ; ) {
|
|
1168
|
+
const choice = await p4.select({
|
|
1169
|
+
message: `${category.title} - ${category.blurb}`,
|
|
1170
|
+
options: [
|
|
1171
|
+
...category.settings.map((s) => ({
|
|
1172
|
+
value: s.key,
|
|
1173
|
+
label: `${s.label}: ${valueLabel(s.read("global"))}`,
|
|
1174
|
+
hint: s.hint
|
|
1175
|
+
})),
|
|
1176
|
+
{ value: "__back", label: "< Back" }
|
|
1177
|
+
]
|
|
1178
|
+
});
|
|
1179
|
+
if (p4.isCancel(choice) || choice === "__back") return;
|
|
1180
|
+
await editSetting(category.settings.find((s) => s.key === choice));
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
async function editSetting(setting) {
|
|
1184
|
+
let scope = "global";
|
|
1185
|
+
if (!setting.globalOnly) {
|
|
1186
|
+
const picked2 = await p4.select({
|
|
1187
|
+
message: `Apply "${setting.label}" to which scope?`,
|
|
1188
|
+
options: [
|
|
1189
|
+
{ value: "global", label: "Global", hint: "all projects (~)" },
|
|
1190
|
+
{ value: "local", label: "This project", hint: "current directory" }
|
|
1191
|
+
],
|
|
1192
|
+
initialValue: "global"
|
|
1193
|
+
});
|
|
1194
|
+
if (p4.isCancel(picked2)) return;
|
|
1195
|
+
scope = picked2;
|
|
1196
|
+
}
|
|
1197
|
+
const current = setting.read(scope);
|
|
1198
|
+
const picked = await p4.select({
|
|
1199
|
+
message: `${setting.label} (${scope})`,
|
|
1200
|
+
options: [{ value: "on", label: "On" }, { value: "off", label: "Off" }],
|
|
1201
|
+
initialValue: current ? "on" : "off"
|
|
1202
|
+
});
|
|
1203
|
+
if (p4.isCancel(picked)) return;
|
|
1204
|
+
const value = picked === "on";
|
|
1205
|
+
if (value === current) {
|
|
1206
|
+
p4.log.info(`${setting.label}: unchanged (${valueLabel(current)}).`);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
const result = setting.write(value, scope);
|
|
1210
|
+
if (result.changed) {
|
|
1211
|
+
const where = result.path ? ` -> ${result.path}` : "";
|
|
1212
|
+
p4.log.success(`${setting.label}: ${valueLabel(value)} (${scope})${where}.`);
|
|
1213
|
+
} else {
|
|
1214
|
+
p4.log.info(`${setting.label}: already ${valueLabel(value)} (${scope}).`);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
function printEffective() {
|
|
1218
|
+
console.log("Effective enigma settings:\n");
|
|
1219
|
+
for (const category of CATEGORIES) {
|
|
1220
|
+
console.log(`${category.title}:`);
|
|
1221
|
+
for (const s of category.settings) console.log(` ${s.key}: ${valueLabel(s.read("global"))}`);
|
|
1222
|
+
console.log("");
|
|
1223
|
+
}
|
|
1224
|
+
const { sources } = readConfig();
|
|
1225
|
+
console.log(sources.length ? `.enigma.json sources: ${sources.join(", ")}` : ".enigma.json: built-in defaults (no file found)");
|
|
1226
|
+
console.log("Agent settings (attribution, bypass) reflect each agent's own config at the global scope.");
|
|
1227
|
+
}
|
|
1228
|
+
async function runConfigCli(positionals, scope, interactive) {
|
|
963
1229
|
const [rawKey, rawValue] = positionals;
|
|
964
1230
|
if (!rawKey) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1231
|
+
if (interactive) {
|
|
1232
|
+
p4.intro("enigma config");
|
|
1233
|
+
await runSettingsMenu();
|
|
1234
|
+
p4.outro("Done.");
|
|
1235
|
+
} else {
|
|
1236
|
+
printEffective();
|
|
970
1237
|
}
|
|
971
|
-
console.log(sources.length ? `
|
|
972
|
-
From: ${sources.join(", ")}` : "\nFrom: built-in defaults (no .enigma.json found)");
|
|
973
1238
|
return 0;
|
|
974
1239
|
}
|
|
975
|
-
const
|
|
976
|
-
if (!
|
|
977
|
-
console.error(`Unknown config key: ${rawKey}. Known keys: ${
|
|
1240
|
+
const setting = ALL_SETTINGS.find((s) => s.key === rawKey);
|
|
1241
|
+
if (!setting) {
|
|
1242
|
+
console.error(`Unknown config key: ${rawKey}. Known keys: ${ALL_SETTINGS.map((s) => s.key).join(", ")}.`);
|
|
978
1243
|
return 1;
|
|
979
1244
|
}
|
|
980
1245
|
if (rawValue === void 0) {
|
|
981
|
-
console.error(`Missing value for '${rawKey}'. Usage: enigma config ${rawKey} <on|off
|
|
1246
|
+
console.error(`Missing value for '${rawKey}'. Usage: enigma config ${rawKey} <on|off> [-g|-l]`);
|
|
982
1247
|
return 1;
|
|
983
1248
|
}
|
|
984
1249
|
const value = parseBool(rawValue);
|
|
@@ -986,9 +1251,10 @@ From: ${sources.join(", ")}` : "\nFrom: built-in defaults (no .enigma.json found
|
|
|
986
1251
|
console.error(`Invalid value '${rawValue}' for '${rawKey}'. Use on or off.`);
|
|
987
1252
|
return 1;
|
|
988
1253
|
}
|
|
989
|
-
const target = scope || "global";
|
|
990
|
-
const
|
|
991
|
-
|
|
1254
|
+
const target = setting.globalOnly ? "global" : scope || "global";
|
|
1255
|
+
const result = setting.write(value, target);
|
|
1256
|
+
const where = result.path ? ` in ${result.path}` : "";
|
|
1257
|
+
console.log(`Set ${rawKey} = ${valueLabel(value)} (${target})${where}.`);
|
|
992
1258
|
return 0;
|
|
993
1259
|
}
|
|
994
1260
|
|
|
@@ -997,7 +1263,7 @@ import { homedir as homedir5 } from "os";
|
|
|
997
1263
|
import { join as join9 } from "path";
|
|
998
1264
|
import { writeFileSync as writeFileSync6 } from "fs";
|
|
999
1265
|
import { spawn, spawnSync } from "child_process";
|
|
1000
|
-
import * as
|
|
1266
|
+
import * as p5 from "@clack/prompts";
|
|
1001
1267
|
var REGISTRY_URL = "https://registry.npmjs.org/enigma-cli/latest";
|
|
1002
1268
|
var UPDATE_COMMAND = "npm i -g enigma-cli@latest";
|
|
1003
1269
|
var CACHE_FILE = join9(homedir5(), ".enigma-update-check.json");
|
|
@@ -1073,6 +1339,7 @@ function runUpdate() {
|
|
|
1073
1339
|
async function notifyUpdate(current, interactive) {
|
|
1074
1340
|
if (!process.stdout.isTTY || process.env.CI) return;
|
|
1075
1341
|
try {
|
|
1342
|
+
if (!readConfig().config.updateNotifier) return;
|
|
1076
1343
|
scheduleUpdateCheck();
|
|
1077
1344
|
const cache = readCache();
|
|
1078
1345
|
const latest = cache?.latest ? String(cache.latest).replace(/[^\w.+-]/g, "") : "";
|
|
@@ -1081,8 +1348,8 @@ async function notifyUpdate(current, interactive) {
|
|
|
1081
1348
|
${renderUpdateBox(current, latest)}
|
|
1082
1349
|
`);
|
|
1083
1350
|
if (!interactive) return;
|
|
1084
|
-
const ok = await
|
|
1085
|
-
if (
|
|
1351
|
+
const ok = await p5.confirm({ message: `Update now with ${UPDATE_COMMAND}?`, initialValue: true });
|
|
1352
|
+
if (p5.isCancel(ok) || !ok) return;
|
|
1086
1353
|
runUpdate();
|
|
1087
1354
|
} catch {
|
|
1088
1355
|
}
|
|
@@ -1199,15 +1466,19 @@ Usage:
|
|
|
1199
1466
|
enigma [command] [options]
|
|
1200
1467
|
|
|
1201
1468
|
Commands:
|
|
1202
|
-
(none) Interactive
|
|
1469
|
+
(none) Interactive hub: configure settings or set up features
|
|
1203
1470
|
install Install/update agent skills (Claude Code, Codex, opencode)
|
|
1204
1471
|
security Set up git security hooks in the current repo
|
|
1205
1472
|
guard [--all] Run the commit guard (staged files, or --all for every tracked file)
|
|
1206
|
-
config [key val]
|
|
1473
|
+
config [key val] Configure settings: no args opens the interactive menu;
|
|
1474
|
+
'config <key> <on|off> [-g|-l]' sets one (e.g. config claude-attribution on)
|
|
1207
1475
|
seal Maintenance: (re)compute skill content hashes
|
|
1208
1476
|
check Integrity gate: verify skills are well-formed and sealed
|
|
1209
1477
|
help, version
|
|
1210
1478
|
|
|
1479
|
+
Config keys: commit-emoji, update-notifier, claude-attribution,
|
|
1480
|
+
bypass-claude, bypass-codex, bypass-opencode
|
|
1481
|
+
|
|
1211
1482
|
Install options:
|
|
1212
1483
|
-g, --global Install at user level
|
|
1213
1484
|
-l, --local Install into the current project
|
|
@@ -1256,45 +1527,44 @@ async function run(argv) {
|
|
|
1256
1527
|
process.exit(runGuardCli(opts.all));
|
|
1257
1528
|
}
|
|
1258
1529
|
if (opts.command === "config") {
|
|
1259
|
-
process.exit(runConfigCli(opts.positionals, opts.scope));
|
|
1530
|
+
process.exit(await runConfigCli(opts.positionals, opts.scope, interactive));
|
|
1260
1531
|
}
|
|
1261
1532
|
if (opts.command === "install") {
|
|
1262
|
-
|
|
1533
|
+
p6.intro("enigma - install agent skills");
|
|
1263
1534
|
await installSkills(opts, interactive);
|
|
1264
|
-
|
|
1535
|
+
p6.outro("Done.");
|
|
1265
1536
|
await notifyUpdate(version, interactive);
|
|
1266
1537
|
return;
|
|
1267
1538
|
}
|
|
1268
1539
|
if (opts.command === "security") {
|
|
1269
|
-
|
|
1540
|
+
p6.intro("enigma - git security hooks");
|
|
1270
1541
|
const done = await setupGitHooks(opts, interactive);
|
|
1271
|
-
|
|
1542
|
+
p6.outro(done ? "Git hooks configured." : "No changes made.");
|
|
1543
|
+
await notifyUpdate(version, interactive);
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
if (!interactive) {
|
|
1547
|
+
await installSkills(opts, interactive);
|
|
1272
1548
|
await notifyUpdate(version, interactive);
|
|
1273
1549
|
return;
|
|
1274
1550
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
message: "What do you want to set up?",
|
|
1551
|
+
p6.intro("enigma");
|
|
1552
|
+
for (; ; ) {
|
|
1553
|
+
const action = await p6.select({
|
|
1554
|
+
message: "What would you like to do?",
|
|
1280
1555
|
options: [
|
|
1281
|
-
{ value: "
|
|
1282
|
-
{ value: "
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1556
|
+
{ value: "config", label: "Configure settings", hint: "emoji, attribution, permission bypass, ..." },
|
|
1557
|
+
{ value: "skills", label: "Install agent skills", hint: "Claude Code, Codex, opencode" },
|
|
1558
|
+
{ value: "security", label: "Git security hooks", hint: "block secrets, .env, node_modules on commit" },
|
|
1559
|
+
{ value: "exit", label: "Exit" }
|
|
1560
|
+
]
|
|
1286
1561
|
});
|
|
1287
|
-
if (
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
features = r;
|
|
1292
|
-
} else {
|
|
1293
|
-
features = ["skills"];
|
|
1562
|
+
if (p6.isCancel(action) || action === "exit") break;
|
|
1563
|
+
if (action === "config") await runSettingsMenu();
|
|
1564
|
+
else if (action === "skills") await installSkills(opts, interactive);
|
|
1565
|
+
else if (action === "security") await setupGitHooks(opts, interactive);
|
|
1294
1566
|
}
|
|
1295
|
-
|
|
1296
|
-
if (features.includes("security")) await setupGitHooks(opts, interactive);
|
|
1297
|
-
p5.outro("Done.");
|
|
1567
|
+
p6.outro("Done.");
|
|
1298
1568
|
await notifyUpdate(version, interactive);
|
|
1299
1569
|
}
|
|
1300
1570
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enigma-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Everything you need to work with a coding agent: install shared policy skills for Claude Code, OpenAI Codex and opencode, and set up portable git security hooks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|