enigma-cli 1.0.0 → 1.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.
@@ -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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
7
7
  "sha": "b0355b0e15f9f528d32adf19f0722d2727cd64d6b3544307ecc7a3141338f023"
8
8
  }
@@ -3,6 +3,6 @@
3
3
  "version": "1.2.0",
4
4
  "provider": "FJRG2007/enigma",
5
5
  "description": "Git & contribution policy (senior engineering standards).",
6
- "cliVersion": "1.0.0",
6
+ "cliVersion": "1.1.0",
7
7
  "sha": "ada4b7eb5bb7e013429e23703c271c0f34b0d76327c059efa148ea2794f96178"
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
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.0.0",
6
+ "cliVersion": "1.1.0",
7
7
  "sha": "a33622a2f810ee4cea39824cb1a7ca34b355a917d4224025df50d77dd74f0b3a"
8
8
  }
package/dist/enigma.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { dirname as dirname4, join as join8 } from "path";
4
+ import { dirname as dirname4, join as join9 } from "path";
5
5
  import { fileURLToPath as fileURLToPath4 } from "url";
6
- import * as p3 from "@clack/prompts";
6
+ import * as p4 from "@clack/prompts";
7
7
 
8
8
  // src/util.ts
9
9
  import { existsSync, statSync, readFileSync } from "fs";
@@ -17,7 +17,7 @@ function isDir(pth) {
17
17
  }
18
18
  function readJson(file) {
19
19
  try {
20
- return JSON.parse(readFileSync(file, "utf8"));
20
+ return JSON.parse(readFileSync(file, "utf8").replace(/^\uFEFF/, ""));
21
21
  } catch {
22
22
  return null;
23
23
  }
@@ -29,11 +29,11 @@ function isOnPath(bin) {
29
29
  }
30
30
 
31
31
  // src/skills.ts
32
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync3, cpSync as cpSync2, mkdirSync as mkdirSync3, rmSync } from "fs";
33
- import { dirname as dirname2, join as join5, resolve as resolve2, relative as relative2, sep as sep2 } from "path";
32
+ import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync4, cpSync as cpSync2, mkdirSync as mkdirSync4, rmSync } from "fs";
33
+ import { dirname as dirname2, join as join6, resolve as resolve2, relative as relative2, sep as sep2 } from "path";
34
34
  import { fileURLToPath as fileURLToPath2 } from "url";
35
35
  import { createHash } from "crypto";
36
- import * as p2 from "@clack/prompts";
36
+ import * as p3 from "@clack/prompts";
37
37
 
38
38
  // src/agents.ts
39
39
  import { homedir } from "os";
@@ -257,15 +257,134 @@ function disableClaudeAttribution(scope) {
257
257
  writeFileSync2(path, JSON.stringify(next, null, 2) + "\n");
258
258
  return true;
259
259
  }
260
+ function enableClaudeBypass(scope, dryRun) {
261
+ const path = claudeSettingsPath(scope);
262
+ const current = readJson(path) || {};
263
+ const permissions = typeof current.permissions === "object" && current.permissions !== null ? current.permissions : {};
264
+ if (permissions.defaultMode === "bypassPermissions") return { path, changed: false };
265
+ if (dryRun) return { path, changed: true };
266
+ const next = { ...current, permissions: { ...permissions, defaultMode: "bypassPermissions" } };
267
+ const dir = join4(path, "..");
268
+ if (!isDir(dir)) mkdirSync2(dir, { recursive: true });
269
+ writeFileSync2(path, JSON.stringify(next, null, 2) + "\n");
270
+ return { path, changed: true };
271
+ }
272
+
273
+ // src/permissions.ts
274
+ import { homedir as homedir3 } from "os";
275
+ import { join as join5 } from "path";
276
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
277
+ import * as p2 from "@clack/prompts";
278
+ var BYPASS_SUPPORTED = ["claude", "codex", "opencode"];
279
+ var BYPASS_DEFAULT_ON = /* @__PURE__ */ new Set(["claude", "codex"]);
280
+ async function resolveBypassSelection(candidates, opts, interactive) {
281
+ const supported = candidates.filter((a) => BYPASS_SUPPORTED.includes(a.name));
282
+ if (!supported.length || opts.noBypass) return [];
283
+ const names = supported.map((a) => a.name);
284
+ if (opts.bypass !== null) {
285
+ const req = opts.bypass.map((s) => s.trim().toLowerCase());
286
+ if (req.includes("none")) return [];
287
+ if (req.includes("all")) return names;
288
+ return names.filter((n) => req.includes(n));
289
+ }
290
+ if (!interactive) return [];
291
+ const r = await p2.multiselect({
292
+ message: "Bypass approval prompts for which agents? (security trade-off: the agent stops asking before acting)",
293
+ options: supported.map((a) => ({
294
+ value: a.name,
295
+ label: a.label,
296
+ hint: BYPASS_DEFAULT_ON.has(a.name) ? "recommended" : "less reliable - off by default"
297
+ })),
298
+ initialValues: names.filter((n) => BYPASS_DEFAULT_ON.has(n)),
299
+ required: false
300
+ });
301
+ if (p2.isCancel(r)) return [];
302
+ return r;
303
+ }
304
+ function applyBypass(agentNames, scope, dryRun) {
305
+ for (const name of agentNames) {
306
+ const res = enableFor(name, scope, dryRun);
307
+ if (!res) continue;
308
+ const label = AGENTS[name]?.label || name;
309
+ if (dryRun) p2.log.info(`Bypass (dry run): would enable for ${label} -> ${res.path}.`);
310
+ else if (res.changed) p2.log.warn(`Bypass: enabled for ${label} (${res.path}) - approval prompts are now OFF.`);
311
+ else p2.log.info(`Bypass: already enabled for ${label}.`);
312
+ }
313
+ }
314
+ function enableFor(name, scope, dryRun) {
315
+ switch (name) {
316
+ case "claude":
317
+ return enableClaudeBypass(scope, dryRun);
318
+ case "codex":
319
+ return enableCodexBypass(dryRun);
320
+ case "opencode":
321
+ return enableOpencodeBypass(scope, dryRun);
322
+ default:
323
+ return null;
324
+ }
325
+ }
326
+ function enableCodexBypass(dryRun) {
327
+ const path = join5(homedir3(), ".codex", "config.toml");
328
+ const before = existsSync4(path) ? readFileSync2(path, "utf8") : "";
329
+ let after = setTomlTopLevelKey(before, "approval_policy", '"never"');
330
+ after = setTomlTopLevelKey(after, "sandbox_mode", '"danger-full-access"');
331
+ const changed = after !== before;
332
+ if (changed && !dryRun) {
333
+ const dir = join5(path, "..");
334
+ if (!isDir(dir)) mkdirSync3(dir, { recursive: true });
335
+ writeFileSync3(path, after);
336
+ }
337
+ return { path, changed };
338
+ }
339
+ function enableOpencodeBypass(scope, dryRun) {
340
+ const path = scope === "global" ? join5(homedir3(), ".config", "opencode", "opencode.json") : join5(process.cwd(), "opencode.json");
341
+ const current = readJson(path) || {};
342
+ const perm = current.permission;
343
+ const alreadyAllowAll = perm === "allow" || typeof perm === "object" && perm !== null && perm["*"] === "allow";
344
+ if (alreadyAllowAll) return { path, changed: false };
345
+ if (dryRun) return { path, changed: true };
346
+ const existing = typeof perm === "object" && perm !== null ? perm : {};
347
+ const rest = {};
348
+ for (const k of Object.keys(existing)) if (k !== "*") rest[k] = existing[k];
349
+ const next = { ...current, permission: { "*": "allow", ...rest } };
350
+ const dir = join5(path, "..");
351
+ if (!isDir(dir)) mkdirSync3(dir, { recursive: true });
352
+ writeFileSync3(path, JSON.stringify(next, null, 2) + "\n");
353
+ return { path, changed: true };
354
+ }
355
+ function setTomlTopLevelKey(content, key, tomlValue) {
356
+ const assign = `${key} = ${tomlValue}`;
357
+ if (content.trim() === "") return `${assign}
358
+ `;
359
+ const lines = content.split("\n");
360
+ const firstTable = lines.findIndex((l) => /^\s*\[/.test(l));
361
+ const scanEnd = firstTable === -1 ? lines.length : firstTable;
362
+ const keyRe = new RegExp(`^\\s*${key}\\s*=`);
363
+ for (let i = 0; i < scanEnd; i++) {
364
+ if (!keyRe.test(lines[i])) continue;
365
+ if (lines[i].trim() === assign) return content;
366
+ lines[i] = assign;
367
+ return normalizeTrailingNewline(lines.join("\n"));
368
+ }
369
+ let insertAt = scanEnd;
370
+ while (insertAt > 0 && lines[insertAt - 1].trim() === "") insertAt--;
371
+ const followsTable = insertAt < lines.length && /^\s*\[/.test(lines[insertAt]);
372
+ lines.splice(insertAt, 0, ...followsTable ? [assign, ""] : [assign]);
373
+ return normalizeTrailingNewline(lines.join("\n"));
374
+ }
375
+ function normalizeTrailingNewline(s) {
376
+ return `${s.replace(/\s+$/, "")}
377
+ `;
378
+ }
260
379
 
261
380
  // src/skills.ts
262
381
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
263
382
  var PKG_ROOT = resolve2(__dirname2, "..");
264
- var ASSETS = join5(PKG_ROOT, "assets");
265
- var SKILLS_ROOT = join5(ASSETS, "skills");
266
- var MEMORY_ROOT = join5(ASSETS, "memory");
383
+ var ASSETS = join6(PKG_ROOT, "assets");
384
+ var SKILLS_ROOT = join6(ASSETS, "skills");
385
+ var MEMORY_ROOT = join6(ASSETS, "memory");
267
386
  function cliVersion() {
268
- return (readJson(join5(PKG_ROOT, "package.json")) || {}).version || "0.0.0";
387
+ return (readJson(join6(PKG_ROOT, "package.json")) || {}).version || "0.0.0";
269
388
  }
270
389
  function serializeMeta(meta) {
271
390
  const ordered = {};
@@ -276,12 +395,12 @@ function serializeMeta(meta) {
276
395
  return JSON.stringify(ordered, null, 2) + "\n";
277
396
  }
278
397
  function readSkillMeta(skillDir) {
279
- return readJson(join5(skillDir, "skill.json")) || {};
398
+ return readJson(join6(skillDir, "skill.json")) || {};
280
399
  }
281
400
  function listFilesRel(dir, base = dir) {
282
401
  const out = [];
283
402
  for (const e of readdirSync(dir)) {
284
- const full = join5(dir, e);
403
+ const full = join6(dir, e);
285
404
  if (isDir(full)) out.push(...listFilesRel(full, base));
286
405
  else out.push(relative2(base, full).split(sep2).join("/"));
287
406
  }
@@ -293,13 +412,13 @@ function computeContentSha(dir) {
293
412
  for (const f of files) {
294
413
  h.update(f);
295
414
  h.update("\0");
296
- h.update(readFileSync2(join5(dir, f)));
415
+ h.update(readFileSync3(join6(dir, f)));
297
416
  h.update("\0");
298
417
  }
299
418
  return h.digest("hex");
300
419
  }
301
420
  function skillStatus(destDir, srcMeta) {
302
- if (!existsSync4(destDir)) return { kind: "install", from: null, to: srcMeta.version || null };
421
+ if (!existsSync5(destDir)) return { kind: "install", from: null, to: srcMeta.version || null };
303
422
  const destMeta = readSkillMeta(destDir);
304
423
  const from = destMeta.version || null;
305
424
  const to = srcMeta.version || null;
@@ -326,27 +445,27 @@ function statusLabel(st) {
326
445
  }
327
446
  function filesEqual(a, b) {
328
447
  try {
329
- return readFileSync2(a).equals(readFileSync2(b));
448
+ return readFileSync3(a).equals(readFileSync3(b));
330
449
  } catch {
331
450
  return false;
332
451
  }
333
452
  }
334
453
  function memoryStatus(srcFile, destFile) {
335
- if (!existsSync4(destFile)) return "install";
454
+ if (!existsSync5(destFile)) return "install";
336
455
  return filesEqual(srcFile, destFile) ? "identical" : "overwrite";
337
456
  }
338
457
  function computePrune(destSkillsDir, sourceNames) {
339
458
  if (!isDir(destSkillsDir)) return [];
340
- return readdirSync(destSkillsDir).filter((e) => isDir(join5(destSkillsDir, e)) && existsSync4(join5(destSkillsDir, e, "SKILL.md"))).filter((e) => !sourceNames.includes(e)).map((e) => ({ name: e, dir: join5(destSkillsDir, e), meta: readSkillMeta(join5(destSkillsDir, e)) })).filter((s) => isManagedProvider(s.meta.provider));
459
+ return readdirSync(destSkillsDir).filter((e) => isDir(join6(destSkillsDir, e)) && existsSync5(join6(destSkillsDir, e, "SKILL.md"))).filter((e) => !sourceNames.includes(e)).map((e) => ({ name: e, dir: join6(destSkillsDir, e), meta: readSkillMeta(join6(destSkillsDir, e)) })).filter((s) => isManagedProvider(s.meta.provider));
341
460
  }
342
461
  function inspectSkills() {
343
462
  if (!isDir(SKILLS_ROOT)) return [];
344
- return readdirSync(SKILLS_ROOT).filter((e) => isDir(join5(SKILLS_ROOT, e)) && existsSync4(join5(SKILLS_ROOT, e, "SKILL.md"))).map((e) => ({ name: e, src: join5(SKILLS_ROOT, e), meta: readSkillMeta(join5(SKILLS_ROOT, e)) }));
463
+ return readdirSync(SKILLS_ROOT).filter((e) => isDir(join6(SKILLS_ROOT, e)) && existsSync5(join6(SKILLS_ROOT, e, "SKILL.md"))).map((e) => ({ name: e, src: join6(SKILLS_ROOT, e), meta: readSkillMeta(join6(SKILLS_ROOT, e)) }));
345
464
  }
346
465
  function inspectMemory(agent) {
347
466
  if (!agent.memoryFile) return [];
348
- const src = join5(MEMORY_ROOT, agent.memoryFile);
349
- return existsSync4(src) ? [{ name: agent.memoryFile, src }] : [];
467
+ const src = join6(MEMORY_ROOT, agent.memoryFile);
468
+ return existsSync5(src) ? [{ name: agent.memoryFile, src }] : [];
350
469
  }
351
470
  function sealSources() {
352
471
  if (!isDir(SKILLS_ROOT)) {
@@ -356,16 +475,16 @@ function sealSources() {
356
475
  const cli = cliVersion();
357
476
  let sealed = 0;
358
477
  for (const name of readdirSync(SKILLS_ROOT)) {
359
- const dir = join5(SKILLS_ROOT, name);
360
- if (!isDir(dir) || !existsSync4(join5(dir, "SKILL.md"))) continue;
361
- const metaPath = join5(dir, "skill.json");
478
+ const dir = join6(SKILLS_ROOT, name);
479
+ if (!isDir(dir) || !existsSync5(join6(dir, "SKILL.md"))) continue;
480
+ const metaPath = join6(dir, "skill.json");
362
481
  const meta = readJson(metaPath) || { name };
363
482
  const before = JSON.stringify(meta);
364
483
  meta.provider = MANAGED_PROVIDER;
365
484
  meta.cliVersion = cli;
366
485
  meta.sha = computeContentSha(dir);
367
486
  const changed = JSON.stringify(meta) !== before;
368
- writeFileSync3(metaPath, serializeMeta(meta));
487
+ writeFileSync4(metaPath, serializeMeta(meta));
369
488
  console.log(`${changed ? "updated" : "ok "} ${name} cli=${cli} sha=${meta.sha.slice(0, 12)}`);
370
489
  sealed++;
371
490
  }
@@ -381,18 +500,18 @@ function checkSources() {
381
500
  const problems = [];
382
501
  let checked = 0;
383
502
  for (const name of readdirSync(SKILLS_ROOT)) {
384
- const dir = join5(SKILLS_ROOT, name);
385
- if (!isDir(dir) || !existsSync4(join5(dir, "SKILL.md"))) continue;
503
+ const dir = join6(SKILLS_ROOT, name);
504
+ if (!isDir(dir) || !existsSync5(join6(dir, "SKILL.md"))) continue;
386
505
  checked++;
387
- const md = readFileSync2(join5(dir, "SKILL.md"), "utf8");
506
+ const md = readFileSync3(join6(dir, "SKILL.md"), "utf8");
388
507
  const fm = md.match(/^---\n([\s\S]*?)\n---/);
389
508
  if (!fm) problems.push(`${name}: SKILL.md is missing YAML frontmatter`);
390
509
  else {
391
510
  if (!/^name:\s*\S/m.test(fm[1])) problems.push(`${name}: frontmatter missing 'name'`);
392
511
  if (!/^description:\s*\S/m.test(fm[1])) problems.push(`${name}: frontmatter missing 'description'`);
393
512
  }
394
- const metaPath = join5(dir, "skill.json");
395
- if (!existsSync4(metaPath)) {
513
+ const metaPath = join6(dir, "skill.json");
514
+ if (!existsSync5(metaPath)) {
396
515
  problems.push(`${name}: missing skill.json`);
397
516
  continue;
398
517
  }
@@ -417,22 +536,22 @@ function checkSources() {
417
536
  async function installSkills(opts, interactive) {
418
537
  const available = discoverAgents();
419
538
  if (available.length === 0) {
420
- p2.cancel("No installable agents known.");
539
+ p3.cancel("No installable agents known.");
421
540
  process.exit(1);
422
541
  }
423
542
  let scope;
424
543
  if (opts.scope) {
425
544
  scope = opts.scope;
426
545
  } else if (interactive) {
427
- const r = await p2.select({
546
+ const r = await p3.select({
428
547
  message: "Where should skills be installed?",
429
548
  options: [
430
549
  { value: "global", label: "Global (user)", hint: "~/.claude, ~/.codex, ~/.config/opencode" },
431
550
  { value: "local", label: "Local (this project)", hint: process.cwd() }
432
551
  ]
433
552
  });
434
- if (p2.isCancel(r)) {
435
- p2.cancel("Aborted.");
553
+ if (p3.isCancel(r)) {
554
+ p3.cancel("Aborted.");
436
555
  return;
437
556
  }
438
557
  scope = r;
@@ -444,19 +563,19 @@ async function installSkills(opts, interactive) {
444
563
  if (opts.agents.length) {
445
564
  chosenAgents = available.filter((a) => opts.agents.includes(a.name));
446
565
  const unknown = opts.agents.filter((n) => !available.some((a) => a.name === n));
447
- if (unknown.length) p2.log.warn(`Skipping unknown/absent agents: ${unknown.join(", ")}`);
566
+ if (unknown.length) p3.log.warn(`Skipping unknown/absent agents: ${unknown.join(", ")}`);
448
567
  } else if (opts.allAgents) {
449
568
  chosenAgents = available;
450
569
  } else if (interactive && available.length > 1) {
451
570
  const preselect = (detected.length ? detected : available).map((a) => a.name);
452
- const r = await p2.multiselect({
571
+ const r = await p3.multiselect({
453
572
  message: "Which agents? (detected on this system are preselected)",
454
573
  options: available.map((a) => ({ value: a.name, label: a.label, hint: a.installed ? "detected" : "not detected" })),
455
574
  initialValues: preselect,
456
575
  required: true
457
576
  });
458
- if (p2.isCancel(r)) {
459
- p2.cancel("Aborted.");
577
+ if (p3.isCancel(r)) {
578
+ p3.cancel("Aborted.");
460
579
  return;
461
580
  }
462
581
  chosenAgents = available.filter((a) => r.includes(a.name));
@@ -464,24 +583,26 @@ async function installSkills(opts, interactive) {
464
583
  chosenAgents = detected;
465
584
  } else {
466
585
  chosenAgents = available;
467
- p2.log.warn("No installed agents detected; defaulting to all supported agents.");
586
+ p3.log.warn("No installed agents detected; defaulting to all supported agents.");
468
587
  }
469
588
  if (chosenAgents.length === 0) {
470
- p2.cancel("No matching agents selected.");
589
+ p3.cancel("No matching agents selected.");
471
590
  process.exit(1);
472
591
  }
473
592
  const claudeScope = chosenAgents.some((a) => a.name === "claude") ? scope : null;
474
593
  const applyClaudeConfig = () => {
475
594
  if (!claudeScope || opts.dryRun) return;
476
595
  if (disableClaudeAttribution(claudeScope)) {
477
- p2.log.info("Claude Code: disabled Co-Authored-By and PR attribution in settings.json.");
596
+ p3.log.info("Claude Code: disabled Co-Authored-By and PR attribution in settings.json.");
478
597
  }
479
598
  };
599
+ const bypassAgents = await resolveBypassSelection(chosenAgents, opts, interactive);
600
+ const applyBypassConfig = () => applyBypass(bypassAgents, scope, opts.dryRun);
480
601
  const plan = [];
481
602
  for (const agent of chosenAgents) {
482
603
  const target = agent.targets[scope];
483
604
  if (!target) {
484
- p2.log.warn(`${agent.label} has no '${scope}' target - skipping.`);
605
+ p3.log.warn(`${agent.label} has no '${scope}' target - skipping.`);
485
606
  continue;
486
607
  }
487
608
  const skills = inspectSkills();
@@ -490,25 +611,25 @@ async function installSkills(opts, interactive) {
490
611
  if (!opts.memoryOnly && opts.skills.length) {
491
612
  chosenSkills = skills.filter((s2) => opts.skills.includes(s2.name));
492
613
  } else if (!opts.memoryOnly && interactive && skills.length > 1) {
493
- const r = await p2.multiselect({
614
+ const r = await p3.multiselect({
494
615
  message: `Skills for ${agent.label} - all selected; deselect any you don't want`,
495
616
  options: skills.map((s2) => {
496
- const st = skillStatus(join5(target.skills, s2.name), s2.meta);
617
+ const st = skillStatus(join6(target.skills, s2.name), s2.meta);
497
618
  const prov = s2.meta.provider ? ` ${s2.meta.provider}` : "";
498
619
  return { value: s2.name, label: s2.name, hint: `${statusLabel(st)}${prov}` };
499
620
  }),
500
621
  initialValues: skills.map((s2) => s2.name),
501
622
  required: false
502
623
  });
503
- if (p2.isCancel(r)) {
504
- p2.cancel("Aborted.");
624
+ if (p3.isCancel(r)) {
625
+ p3.cancel("Aborted.");
505
626
  return;
506
627
  }
507
628
  chosenSkills = skills.filter((s2) => r.includes(s2.name));
508
629
  }
509
630
  const skillsWithStatus = (opts.memoryOnly ? [] : chosenSkills).map((s2) => ({
510
631
  ...s2,
511
- status: skillStatus(join5(target.skills, s2.name), s2.meta),
632
+ status: skillStatus(join6(target.skills, s2.name), s2.meta),
512
633
  overwrite: true
513
634
  }));
514
635
  const prune = opts.prune && !opts.memoryOnly ? computePrune(target.skills, skills.map((s2) => s2.name)) : [];
@@ -518,16 +639,16 @@ async function installSkills(opts, interactive) {
518
639
  if (tampered.length) {
519
640
  if (opts.keepModified) {
520
641
  for (const s2 of tampered) s2.overwrite = false;
521
- p2.log.warn(`${tampered.length} locally-modified skill(s) will be kept (--keep-modified).`);
642
+ p3.log.warn(`${tampered.length} locally-modified skill(s) will be kept (--keep-modified).`);
522
643
  } else if (interactive && !opts.dryRun) {
523
- const sel = await p2.multiselect({
644
+ const sel = await p3.multiselect({
524
645
  message: `${tampered.length} skill(s) were modified locally since install. Select which to OVERWRITE`,
525
646
  options: tampered.map((s2, i) => ({ value: i, label: s2.name, hint: s2.meta.version ? `v${s2.meta.version}` : "modified" })),
526
647
  initialValues: tampered.map((_, i) => i),
527
648
  required: false
528
649
  });
529
- if (p2.isCancel(sel)) {
530
- p2.cancel("Aborted.");
650
+ if (p3.isCancel(sel)) {
651
+ p3.cancel("Aborted.");
531
652
  return;
532
653
  }
533
654
  tampered.forEach((s2, i) => {
@@ -562,7 +683,7 @@ async function installSkills(opts, interactive) {
562
683
  lines.push(` ${label.padEnd(26)} skill ${s2.name}${prov}`);
563
684
  }
564
685
  for (const m of x.memory) {
565
- const ms = memoryStatus(m.src, join5(x.target.memory, m.name));
686
+ const ms = memoryStatus(m.src, join6(x.target.memory, m.name));
566
687
  if (ms === "identical") {
567
688
  nSkip++;
568
689
  lines.push(` ${"up-to-date (skip)".padEnd(26)} memory ${m.name}`);
@@ -581,13 +702,14 @@ async function installSkills(opts, interactive) {
581
702
  }
582
703
  }
583
704
  if (nInstall + nUpdate + nRemove === 0) {
584
- p2.note(lines.join("\n"), "Nothing to do");
705
+ p3.note(lines.join("\n"), "Nothing to do");
585
706
  applyClaudeConfig();
707
+ applyBypassConfig();
586
708
  await maybeOfferGitHooks(interactive, opts);
587
- p2.log.success(`Everything up-to-date - ${nSkip} item(s) unchanged${nKept ? `, ${nKept} kept modified` : ""} (${scope}).`);
709
+ p3.log.success(`Everything up-to-date - ${nSkip} item(s) unchanged${nKept ? `, ${nKept} kept modified` : ""} (${scope}).`);
588
710
  return;
589
711
  }
590
- p2.note(lines.join("\n"), opts.dryRun ? "Dry run - planned changes" : "Planned changes");
712
+ p3.note(lines.join("\n"), opts.dryRun ? "Dry run - planned changes" : "Planned changes");
591
713
  if (interactive && !opts.dryRun) {
592
714
  const summary = [
593
715
  nInstall && `${nInstall} to install`,
@@ -595,63 +717,65 @@ async function installSkills(opts, interactive) {
595
717
  nRemove && `${nRemove} to remove`,
596
718
  nSkip && `${nSkip} unchanged`
597
719
  ].filter(Boolean).join(", ");
598
- const ok = await p2.confirm({ message: `Apply: ${summary}?` });
599
- if (p2.isCancel(ok) || !ok) {
600
- p2.cancel("Aborted.");
720
+ const ok = await p3.confirm({ message: `Apply: ${summary}?` });
721
+ if (p3.isCancel(ok) || !ok) {
722
+ p3.cancel("Aborted.");
601
723
  return;
602
724
  }
603
725
  }
604
726
  if (opts.dryRun) {
605
- p2.log.info("Dry run complete - no files written.");
727
+ applyBypassConfig();
728
+ p3.log.info("Dry run complete - no files written.");
606
729
  return;
607
730
  }
608
731
  const changedAgents = plan.filter(
609
- (x) => x.skills.some(willCopy) || x.memory.some((m) => memoryStatus(m.src, join5(x.target.memory, m.name)) !== "identical") || x.prune.length > 0
732
+ (x) => x.skills.some(willCopy) || x.memory.some((m) => memoryStatus(m.src, join6(x.target.memory, m.name)) !== "identical") || x.prune.length > 0
610
733
  );
611
- const s = p2.spinner();
734
+ const s = p3.spinner();
612
735
  s.start("Installing...");
613
736
  let copied = 0;
614
737
  try {
615
738
  for (const x of plan) {
616
- mkdirSync3(x.target.skills, { recursive: true });
617
- mkdirSync3(x.target.memory, { recursive: true });
739
+ mkdirSync4(x.target.skills, { recursive: true });
740
+ mkdirSync4(x.target.memory, { recursive: true });
618
741
  for (const sk of x.skills) {
619
742
  if (!willCopy(sk)) continue;
620
- cpSync2(sk.src, join5(x.target.skills, sk.name), { recursive: true, force: true });
743
+ cpSync2(sk.src, join6(x.target.skills, sk.name), { recursive: true, force: true });
621
744
  copied++;
622
745
  }
623
746
  for (const m of x.memory) {
624
- if (memoryStatus(m.src, join5(x.target.memory, m.name)) === "identical") continue;
625
- cpSync2(m.src, join5(x.target.memory, m.name), { force: true });
747
+ if (memoryStatus(m.src, join6(x.target.memory, m.name)) === "identical") continue;
748
+ cpSync2(m.src, join6(x.target.memory, m.name), { force: true });
626
749
  copied++;
627
750
  }
628
751
  for (const pr of x.prune) rmSync(pr.dir, { recursive: true, force: true });
629
752
  }
630
753
  } catch (err) {
631
754
  s.stop("Failed.");
632
- p2.cancel(`Error while installing: ${err.message}`);
755
+ p3.cancel(`Error while installing: ${err.message}`);
633
756
  process.exit(1);
634
757
  }
635
758
  s.stop(`Wrote ${copied} item(s)${nRemove ? `, removed ${nRemove}` : ""}.`);
636
759
  applyClaudeConfig();
760
+ applyBypassConfig();
637
761
  await maybeOfferGitHooks(interactive, opts);
638
- p2.log.success(`${nInstall} installed, ${nUpdate} updated/overwritten` + (nRemove ? `, ${nRemove} removed` : "") + (nSkip ? `, ${nSkip} unchanged` : "") + (nKept ? `, ${nKept} kept modified` : "") + ` (${scope}).`);
762
+ p3.log.success(`${nInstall} installed, ${nUpdate} updated/overwritten` + (nRemove ? `, ${nRemove} removed` : "") + (nSkip ? `, ${nSkip} unchanged` : "") + (nKept ? `, ${nKept} kept modified` : "") + ` (${scope}).`);
639
763
  if (changedAgents.length) {
640
764
  const { known, running } = runningStatus(changedAgents.map((x) => x.agent));
641
765
  if (running.size) {
642
766
  const names = changedAgents.filter((x) => running.has(x.agent.name)).map((x) => x.agent.label);
643
- p2.log.warn(`Restart ${names.join(", ")} to apply the changes (running now).`);
767
+ p3.log.warn(`Restart ${names.join(", ")} to apply the changes (running now).`);
644
768
  } else if (!known) {
645
769
  const names = changedAgents.map((x) => x.agent.label);
646
- p2.log.info(`If any of these agents are running, restart them to apply the changes: ${names.join(", ")}.`);
770
+ p3.log.info(`If any of these agents are running, restart them to apply the changes: ${names.join(", ")}.`);
647
771
  }
648
772
  }
649
773
  }
650
774
 
651
775
  // src/guard.ts
652
- import { readFileSync as readFileSync3, statSync as statSync2 } from "fs";
776
+ import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
653
777
  import { execFileSync as execFileSync3 } from "child_process";
654
- import { basename, extname, dirname as dirname3, join as join6 } from "path";
778
+ import { basename, extname, dirname as dirname3, join as join7 } from "path";
655
779
  import { fileURLToPath as fileURLToPath3 } from "url";
656
780
  var LARGE_FILE_BYTES = 5 * 1024 * 1024;
657
781
  var ENV_ALLOWED = /(example|sample|template)/i;
@@ -712,7 +836,7 @@ var SELF_DIR = dirname3(fileURLToPath3(import.meta.url));
712
836
  function loadConfig() {
713
837
  const defaults = { secrets: true, envFiles: true, depDirs: true, generatedDirs: true, junkFiles: true, largeFiles: true };
714
838
  try {
715
- const raw = JSON.parse(readFileSync3(join6(SELF_DIR, "enigma-guard.json"), "utf8"));
839
+ const raw = JSON.parse(readFileSync4(join7(SELF_DIR, "enigma-guard.json"), "utf8"));
716
840
  return { ...defaults, ...raw };
717
841
  } catch {
718
842
  return defaults;
@@ -727,7 +851,7 @@ function scanSecrets(file, blocks) {
727
851
  if (SECRET_SKIP_EXT.has(extname(file).toLowerCase())) return;
728
852
  let text;
729
853
  try {
730
- text = readFileSync3(file, "utf8");
854
+ text = readFileSync4(file, "utf8");
731
855
  } catch {
732
856
  return;
733
857
  }
@@ -797,22 +921,22 @@ if (isGuardEntry && fileURLToPath3(import.meta.url) === guardEntry) {
797
921
  }
798
922
 
799
923
  // src/config.ts
800
- import { homedir as homedir3 } from "os";
801
- import { join as join7 } from "path";
802
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
924
+ import { homedir as homedir4 } from "os";
925
+ import { join as join8 } from "path";
926
+ import { existsSync as existsSync6, mkdirSync as mkdirSync5, writeFileSync as writeFileSync5 } from "fs";
803
927
  var CONFIG_FILE = ".enigma.json";
804
928
  var CONFIG_DEFAULTS = { commitEmoji: true };
805
929
  var BOOLEAN_KEYS = ["commitEmoji"];
806
930
  var CLI_KEYS = { "commit-emoji": "commitEmoji" };
807
931
  function configPath(scope) {
808
- return scope === "global" ? join7(homedir3(), CONFIG_FILE) : join7(process.cwd(), CONFIG_FILE);
932
+ return scope === "global" ? join8(homedir4(), CONFIG_FILE) : join8(process.cwd(), CONFIG_FILE);
809
933
  }
810
934
  function readConfig() {
811
935
  const sources = [];
812
936
  let config = { ...CONFIG_DEFAULTS };
813
937
  for (const scope of ["global", "local"]) {
814
938
  const path = configPath(scope);
815
- const raw = existsSync5(path) ? readJson(path) : null;
939
+ const raw = existsSync6(path) ? readJson(path) : null;
816
940
  if (raw) {
817
941
  config = { ...config, ...raw };
818
942
  sources.push(path);
@@ -830,9 +954,9 @@ function setValue(scope, key, value) {
830
954
  const path = configPath(scope);
831
955
  const current = readJson(path) || {};
832
956
  const next = { ...current, [key]: value };
833
- const dir = join7(path, "..");
834
- if (!isDir(dir)) mkdirSync4(dir, { recursive: true });
835
- writeFileSync4(path, JSON.stringify(next, null, 2) + "\n");
957
+ const dir = join8(path, "..");
958
+ if (!isDir(dir)) mkdirSync5(dir, { recursive: true });
959
+ writeFileSync5(path, JSON.stringify(next, null, 2) + "\n");
836
960
  return path;
837
961
  }
838
962
  function runConfigCli(positionals, scope) {
@@ -870,7 +994,7 @@ From: ${sources.join(", ")}` : "\nFrom: built-in defaults (no .enigma.json found
870
994
 
871
995
  // src/cli.ts
872
996
  var __dirname3 = dirname4(fileURLToPath4(import.meta.url));
873
- var PKG = readJson(join8(__dirname3, "..", "package.json")) || {};
997
+ var PKG = readJson(join9(__dirname3, "..", "package.json")) || {};
874
998
  var COMMANDS = /* @__PURE__ */ new Set(["install", "security", "guard", "seal", "check", "config", "help", "version"]);
875
999
  function parseArgs(argv) {
876
1000
  const opts = {
@@ -884,6 +1008,8 @@ function parseArgs(argv) {
884
1008
  memoryOnly: false,
885
1009
  prune: true,
886
1010
  keepModified: false,
1011
+ bypass: null,
1012
+ noBypass: false,
887
1013
  force: false,
888
1014
  all: false,
889
1015
  yes: false,
@@ -931,6 +1057,12 @@ function parseArgs(argv) {
931
1057
  case "--keep-modified":
932
1058
  opts.keepModified = true;
933
1059
  break;
1060
+ case "--bypass":
1061
+ opts.bypass = (opts.bypass || []).concat(next().split(","));
1062
+ break;
1063
+ case "--no-bypass":
1064
+ opts.noBypass = true;
1065
+ break;
934
1066
  case "--force":
935
1067
  opts.force = true;
936
1068
  break;
@@ -988,6 +1120,8 @@ Install options:
988
1120
  --all Target every supported agent, ignoring detection
989
1121
  --skills-only Only skill folders --memory-only Only memory files
990
1122
  --no-prune Keep orphaned skills --keep-modified Don't overwrite local edits
1123
+ --bypass <names> Disable approval prompts for agents (claude,codex,opencode | all | none)
1124
+ --no-bypass Never configure permission bypass (skip the prompt)
991
1125
  --dry-run Show the plan without writing
992
1126
 
993
1127
  Security options:
@@ -1000,6 +1134,7 @@ Examples:
1000
1134
  enigma # interactive
1001
1135
  enigma install --global # skills for detected agents, user level
1002
1136
  enigma install --all -y # every supported agent, non-interactive
1137
+ enigma install -y --bypass claude,codex # also disable approval prompts (unattended)
1003
1138
  enigma security # configure git hooks (choose protections)
1004
1139
  enigma config # show effective runtime config
1005
1140
  enigma config commit-emoji off # opt out of commit-message emojis (global)
@@ -1025,21 +1160,21 @@ async function run(argv) {
1025
1160
  }
1026
1161
  const interactive = Boolean(process.stdout.isTTY) && !opts.yes;
1027
1162
  if (opts.command === "install") {
1028
- p3.intro("enigma - install agent skills");
1163
+ p4.intro("enigma - install agent skills");
1029
1164
  await installSkills(opts, interactive);
1030
- p3.outro("Done.");
1165
+ p4.outro("Done.");
1031
1166
  return;
1032
1167
  }
1033
1168
  if (opts.command === "security") {
1034
- p3.intro("enigma - git security hooks");
1169
+ p4.intro("enigma - git security hooks");
1035
1170
  const done = await setupGitHooks(opts, interactive);
1036
- p3.outro(done ? "Git hooks configured." : "No changes made.");
1171
+ p4.outro(done ? "Git hooks configured." : "No changes made.");
1037
1172
  return;
1038
1173
  }
1039
- p3.intro("enigma");
1174
+ p4.intro("enigma");
1040
1175
  let features;
1041
1176
  if (interactive) {
1042
- const r = await p3.multiselect({
1177
+ const r = await p4.multiselect({
1043
1178
  message: "What do you want to set up?",
1044
1179
  options: [
1045
1180
  { value: "skills", label: "Agent skills", hint: "Claude Code, Codex, opencode" },
@@ -1048,8 +1183,8 @@ async function run(argv) {
1048
1183
  initialValues: ["skills"],
1049
1184
  required: true
1050
1185
  });
1051
- if (p3.isCancel(r)) {
1052
- p3.cancel("Aborted.");
1186
+ if (p4.isCancel(r)) {
1187
+ p4.cancel("Aborted.");
1053
1188
  return;
1054
1189
  }
1055
1190
  features = r;
@@ -1058,7 +1193,7 @@ async function run(argv) {
1058
1193
  }
1059
1194
  if (features.includes("skills")) await installSkills(opts, interactive);
1060
1195
  if (features.includes("security")) await setupGitHooks(opts, interactive);
1061
- p3.outro("Done.");
1196
+ p4.outro("Done.");
1062
1197
  }
1063
1198
 
1064
1199
  // src/bin/enigma.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enigma-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
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": {