enigma-cli 1.1.4 → 1.2.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/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 +302 -138
- 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.
|
|
6
|
+
"cliVersion": "1.2.0",
|
|
7
7
|
"sha": "c442bc9e39a7710cb709ef2abb8d15ecd8aa16ed4f5c8af92b7af6877401cba4"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.1.1",
|
|
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.
|
|
6
|
+
"cliVersion": "1.2.0",
|
|
7
7
|
"sha": "74f638aec13e8c93257fe1ad604c28b07e9a7c456796a4ceefcc99217d9e7039"
|
|
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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.0",
|
|
7
7
|
"sha": "6375d835c2aef2c9bd31ce116444dc3d796f510f9970a213aa3ac4696d7e21b9"
|
|
8
8
|
}
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"version": "1.1.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.
|
|
6
|
+
"cliVersion": "1.2.0",
|
|
7
7
|
"sha": "33fa1e9f667ef26203a3d6c892121efe12b0cddb706c195492fa97e080fba115"
|
|
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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.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.
|
|
6
|
+
"cliVersion": "1.2.0",
|
|
7
7
|
"sha": "a33622a2f810ee4cea39824cb1a7ca34b355a917d4224025df50d77dd74f0b3a"
|
|
8
8
|
}
|
package/dist/enigma.js
CHANGED
|
@@ -106,7 +106,7 @@ var init_agents = __esm({
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
opencode: {
|
|
109
|
-
label: "
|
|
109
|
+
label: "OpenCode",
|
|
110
110
|
memoryFile: "AGENTS.md",
|
|
111
111
|
// opencode reads AGENTS.md from ~/.config/opencode (global) or the project
|
|
112
112
|
// root (local); skills from ~/.config/opencode/skills and .opencode/skills.
|
|
@@ -453,7 +453,7 @@ var init_config = __esm({
|
|
|
453
453
|
"use strict";
|
|
454
454
|
init_util();
|
|
455
455
|
CONFIG_FILE = ".enigma.json";
|
|
456
|
-
CONFIG_DEFAULTS = { commitEmoji: true, updateNotifier: true };
|
|
456
|
+
CONFIG_DEFAULTS = { commitEmoji: true, updateNotifier: true, fullscreen: true };
|
|
457
457
|
}
|
|
458
458
|
});
|
|
459
459
|
|
|
@@ -490,7 +490,8 @@ var init_settings_registry = __esm({
|
|
|
490
490
|
blurb: "enigma runtime toggles (.enigma.json)",
|
|
491
491
|
settings: [
|
|
492
492
|
enigmaToggle("commit-emoji", "commitEmoji", "Commit subject emoji", "leading gitmoji on commit subjects"),
|
|
493
|
-
enigmaToggle("update-notifier", "updateNotifier", "Update notifications", "notify when a newer enigma-cli is published")
|
|
493
|
+
enigmaToggle("update-notifier", "updateNotifier", "Update notifications", "notify when a newer enigma-cli is published"),
|
|
494
|
+
enigmaToggle("fullscreen", "fullscreen", "Full-screen TUI", "clear the screen for a clean TUI view; off renders inline among existing output")
|
|
494
495
|
]
|
|
495
496
|
},
|
|
496
497
|
{
|
|
@@ -530,63 +531,81 @@ __export(settings_exports, {
|
|
|
530
531
|
runHomeTui: () => runHomeTui,
|
|
531
532
|
runSettingsTui: () => runSettingsTui
|
|
532
533
|
});
|
|
533
|
-
async function runHomeTui(
|
|
534
|
-
await runTui(
|
|
534
|
+
async function runHomeTui(hub) {
|
|
535
|
+
await runTui({ showActions: true, hub });
|
|
535
536
|
}
|
|
536
537
|
async function runSettingsTui() {
|
|
537
|
-
await runTui(
|
|
538
|
+
await runTui({ showActions: false });
|
|
538
539
|
}
|
|
539
|
-
async function runTui(
|
|
540
|
+
async function runTui(opts) {
|
|
540
541
|
if (!process.stdout.isTTY) return;
|
|
542
|
+
const fullscreen = readConfig().config.fullscreen;
|
|
541
543
|
const React = (await import("react")).default;
|
|
542
544
|
const ink = await import("ink");
|
|
543
545
|
const { render } = ink;
|
|
544
546
|
const h = React.createElement;
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
547
|
+
const agents = opts.hub?.agents ?? [];
|
|
548
|
+
const protections = opts.hub?.protections ?? [];
|
|
549
|
+
const clear = () => {
|
|
550
|
+
if (fullscreen) try {
|
|
551
|
+
process.stdout.write(CLEAR_SCREEN);
|
|
548
552
|
} catch {
|
|
549
553
|
}
|
|
550
554
|
};
|
|
551
555
|
for (; ; ) {
|
|
552
|
-
const state = {
|
|
553
|
-
const App = buildApp(React, ink, {
|
|
554
|
-
state.
|
|
556
|
+
const state = { intent: { action: "quit" } };
|
|
557
|
+
const App = buildApp(React, ink, { showActions: opts.showActions, fullscreen, agents, protections, onLeave: (i) => {
|
|
558
|
+
state.intent = i;
|
|
555
559
|
} });
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
if (runAction && (state.leave === "skills" || state.leave === "security")) {
|
|
566
|
-
await runAction(state.leave);
|
|
560
|
+
clear();
|
|
561
|
+
const app = render(h(App), { exitOnCtrlC: true });
|
|
562
|
+
await app.waitUntilExit();
|
|
563
|
+
const { intent } = state;
|
|
564
|
+
if (opts.hub && (intent.action === "skills" || intent.action === "security")) {
|
|
565
|
+
clear();
|
|
566
|
+
await opts.hub.runAction(intent);
|
|
567
|
+
await waitForEnter();
|
|
567
568
|
continue;
|
|
568
569
|
}
|
|
569
570
|
break;
|
|
570
571
|
}
|
|
571
572
|
}
|
|
573
|
+
function waitForEnter() {
|
|
574
|
+
return new Promise((resolve3) => {
|
|
575
|
+
process.stdout.write("\nPress Enter to return to the menu...\n");
|
|
576
|
+
const stdin = process.stdin;
|
|
577
|
+
stdin.resume();
|
|
578
|
+
stdin.once("data", () => {
|
|
579
|
+
stdin.pause();
|
|
580
|
+
resolve3();
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
}
|
|
572
584
|
function buildApp(React, ink, opts) {
|
|
573
585
|
const { useApp, useInput, useStdout } = ink;
|
|
574
586
|
const Box = ink.Box;
|
|
575
587
|
const Text = ink.Text;
|
|
576
588
|
const { useState, useEffect } = React;
|
|
577
589
|
const h = React.createElement;
|
|
578
|
-
const
|
|
590
|
+
const fill = opts.fullscreen;
|
|
591
|
+
const sideItems = [
|
|
592
|
+
...CATEGORIES.map((c, i) => ({ kind: "category", catIndex: i, title: c.title })),
|
|
593
|
+
...opts.showActions ? ACTION_ITEMS.map((a) => ({ kind: "action", ...a })) : []
|
|
594
|
+
];
|
|
579
595
|
return function App() {
|
|
580
596
|
const { exit } = useApp();
|
|
581
597
|
const { stdout } = useStdout();
|
|
582
598
|
const [size, setSize] = useState({ columns: stdout.columns || 80, rows: stdout.rows || 24 });
|
|
583
|
-
const [
|
|
584
|
-
const [homeIndex, setHomeIndex] = useState(0);
|
|
599
|
+
const [mode, setMode] = useState("menu");
|
|
585
600
|
const [scope, setScope] = useState("global");
|
|
586
|
-
const [
|
|
587
|
-
const [
|
|
601
|
+
const [sideIndex, setSideIndex] = useState(0);
|
|
602
|
+
const [focusRight, setFocusRight] = useState(false);
|
|
588
603
|
const [setIndex, setSetIndex] = useState(0);
|
|
589
|
-
const [,
|
|
604
|
+
const [pending, setPending] = useState({});
|
|
605
|
+
const [confirm4, setConfirm] = useState(null);
|
|
606
|
+
const [actCursor, setActCursor] = useState(0);
|
|
607
|
+
const [actChecked, setActChecked] = useState({});
|
|
608
|
+
const [actScope, setActScope] = useState("global");
|
|
590
609
|
useEffect(() => {
|
|
591
610
|
const onResize = () => setSize({ columns: stdout.columns || 80, rows: stdout.rows || 24 });
|
|
592
611
|
stdout.on("resize", onResize);
|
|
@@ -594,45 +613,112 @@ function buildApp(React, ink, opts) {
|
|
|
594
613
|
stdout.off("resize", onResize);
|
|
595
614
|
};
|
|
596
615
|
}, [stdout]);
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
616
|
+
const current = sideItems[sideIndex];
|
|
617
|
+
const category = current.kind === "category" ? CATEGORIES[current.catIndex] : null;
|
|
618
|
+
const valueOf = (setting, sc) => {
|
|
619
|
+
const k = stageKey(setting.key, sc);
|
|
620
|
+
return k in pending ? pending[k] : setting.read(sc);
|
|
621
|
+
};
|
|
622
|
+
const isModified = (setting, sc) => {
|
|
623
|
+
const k = stageKey(setting.key, sc);
|
|
624
|
+
return k in pending && pending[k] !== setting.read(sc);
|
|
625
|
+
};
|
|
626
|
+
const dirty = Object.entries(pending).some(([k, v]) => {
|
|
627
|
+
const { key, scope: sc } = parseStageKey(k);
|
|
628
|
+
return SETTING_BY_KEY.get(key)?.read(sc) !== v;
|
|
629
|
+
});
|
|
630
|
+
const persistPending = () => {
|
|
631
|
+
for (const [k, v] of Object.entries(pending)) {
|
|
632
|
+
const { key, scope: sc } = parseStageKey(k);
|
|
633
|
+
const setting = SETTING_BY_KEY.get(key);
|
|
634
|
+
if (setting && setting.read(sc) !== v) setting.write(v, sc);
|
|
602
635
|
}
|
|
603
636
|
};
|
|
637
|
+
const leave = (intent) => {
|
|
638
|
+
opts.onLeave(intent);
|
|
639
|
+
exit();
|
|
640
|
+
};
|
|
641
|
+
const openAction = (action) => {
|
|
642
|
+
setActCursor(0);
|
|
643
|
+
if (action === "security") {
|
|
644
|
+
setActChecked(Object.fromEntries(opts.protections.map((p6) => [p6.value, true])));
|
|
645
|
+
} else {
|
|
646
|
+
const detected = opts.agents.filter((a) => a.installed);
|
|
647
|
+
const preselect = detected.length ? detected : opts.agents;
|
|
648
|
+
setActChecked(Object.fromEntries(opts.agents.map((a) => [a.name, preselect.some((d) => d.name === a.name)])));
|
|
649
|
+
setActScope("global");
|
|
650
|
+
}
|
|
651
|
+
setMode(action);
|
|
652
|
+
};
|
|
604
653
|
useInput((input, key) => {
|
|
605
|
-
if (
|
|
606
|
-
if (
|
|
607
|
-
|
|
608
|
-
exit();
|
|
654
|
+
if (confirm4) {
|
|
655
|
+
if (key.escape) {
|
|
656
|
+
setConfirm(null);
|
|
609
657
|
return;
|
|
610
658
|
}
|
|
611
659
|
if (key.upArrow || input === "k") {
|
|
612
|
-
|
|
660
|
+
setConfirm((c) => c && { index: Math.max(0, c.index - 1) });
|
|
613
661
|
return;
|
|
614
662
|
}
|
|
615
663
|
if (key.downArrow || input === "j") {
|
|
616
|
-
|
|
664
|
+
setConfirm((c) => c && { index: Math.min(EXIT_OPTIONS.length - 1, c.index + 1) });
|
|
617
665
|
return;
|
|
618
666
|
}
|
|
619
667
|
if (key.return || input === " ") {
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
668
|
+
const index = confirm4.index;
|
|
669
|
+
setConfirm(null);
|
|
670
|
+
if (index === 2) return;
|
|
671
|
+
if (index === 0) persistPending();
|
|
672
|
+
setPending({});
|
|
673
|
+
leave({ action: "quit" });
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
if (mode !== "menu") {
|
|
678
|
+
const items = mode === "security" ? opts.protections.map((p6) => p6.value) : opts.agents.map((a) => a.name);
|
|
679
|
+
if (key.escape || input === "q") {
|
|
680
|
+
setMode("menu");
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (mode === "skills" && input === "g") {
|
|
684
|
+
setActScope((s) => s === "global" ? "local" : "global");
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (key.upArrow || input === "k") {
|
|
688
|
+
setActCursor((i) => Math.max(0, i - 1));
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (key.downArrow || input === "j") {
|
|
692
|
+
setActCursor((i) => Math.min(items.length - 1, i + 1));
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (input === " ") {
|
|
696
|
+
const k = items[actCursor];
|
|
697
|
+
setActChecked((c) => ({ ...c, [k]: !c[k] }));
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (key.return) {
|
|
701
|
+
const chosen = items.filter((k) => actChecked[k]);
|
|
702
|
+
persistPending();
|
|
703
|
+
setPending({});
|
|
704
|
+
if (mode === "security") leave({ action: "security", protections: chosen });
|
|
705
|
+
else leave({ action: "skills", scope: actScope, agents: chosen });
|
|
631
706
|
}
|
|
632
707
|
return;
|
|
633
708
|
}
|
|
634
709
|
if (input === "q" || key.escape) {
|
|
635
|
-
|
|
710
|
+
dirty ? setConfirm({ index: 0 }) : leave({ action: "quit" });
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
if (input === "s") {
|
|
714
|
+
persistPending();
|
|
715
|
+
setPending({});
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (input === "x") {
|
|
719
|
+
persistPending();
|
|
720
|
+
setPending({});
|
|
721
|
+
leave({ action: "quit" });
|
|
636
722
|
return;
|
|
637
723
|
}
|
|
638
724
|
if (input === "g") {
|
|
@@ -640,147 +726,218 @@ function buildApp(React, ink, opts) {
|
|
|
640
726
|
return;
|
|
641
727
|
}
|
|
642
728
|
if (key.tab) {
|
|
643
|
-
|
|
729
|
+
if (category) setFocusRight((f) => !f);
|
|
644
730
|
return;
|
|
645
731
|
}
|
|
646
732
|
if (key.leftArrow || input === "h") {
|
|
647
|
-
|
|
733
|
+
setFocusRight(false);
|
|
648
734
|
return;
|
|
649
735
|
}
|
|
650
736
|
if (key.rightArrow || input === "l") {
|
|
651
|
-
|
|
737
|
+
if (category) setFocusRight(true);
|
|
652
738
|
return;
|
|
653
739
|
}
|
|
654
740
|
if (key.upArrow || input === "k") {
|
|
655
|
-
if (
|
|
741
|
+
if (focusRight && category) setSetIndex((i) => Math.max(0, i - 1));
|
|
656
742
|
else {
|
|
657
|
-
|
|
743
|
+
setSideIndex((i) => Math.max(0, i - 1));
|
|
658
744
|
setSetIndex(0);
|
|
745
|
+
setFocusRight(false);
|
|
659
746
|
}
|
|
660
747
|
return;
|
|
661
748
|
}
|
|
662
749
|
if (key.downArrow || input === "j") {
|
|
663
|
-
if (
|
|
750
|
+
if (focusRight && category) setSetIndex((i) => Math.min(category.settings.length - 1, i + 1));
|
|
664
751
|
else {
|
|
665
|
-
|
|
752
|
+
setSideIndex((i) => Math.min(sideItems.length - 1, i + 1));
|
|
666
753
|
setSetIndex(0);
|
|
754
|
+
setFocusRight(false);
|
|
667
755
|
}
|
|
668
756
|
return;
|
|
669
757
|
}
|
|
670
758
|
if (key.return || input === " ") {
|
|
671
|
-
if (
|
|
672
|
-
|
|
759
|
+
if (current.kind === "action") {
|
|
760
|
+
openAction(current.action);
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
if (!focusRight) {
|
|
764
|
+
setFocusRight(true);
|
|
673
765
|
return;
|
|
674
766
|
}
|
|
675
|
-
const setting =
|
|
676
|
-
|
|
677
|
-
redraw((n) => n + 1);
|
|
767
|
+
const setting = category.settings[setIndex];
|
|
768
|
+
setPending((p6) => ({ ...p6, [stageKey(setting.key, scope)]: !valueOf(setting, scope) }));
|
|
678
769
|
}
|
|
679
770
|
});
|
|
680
|
-
const
|
|
681
|
-
Box,
|
|
682
|
-
{ width: size.columns, paddingX: 1, justifyContent: "space-between" },
|
|
683
|
-
h(Text, { bold: true, color: "cyan" }, "enigma"),
|
|
684
|
-
right2
|
|
685
|
-
);
|
|
686
|
-
const body = view === "home" ? renderHome(h, Box, Text, homeIndex) : renderSettings(h, Box, Text, { size, scope, focusSettings, catIndex, setIndex });
|
|
687
|
-
const footer = h(Box, { width: size.columns, paddingX: 1 }, h(
|
|
688
|
-
Text,
|
|
689
|
-
{ dimColor: true },
|
|
690
|
-
view === "home" ? "up/down move enter select q quit" : `up/down move tab/left/right switch enter toggle g scope ${canGoHome ? "esc back" : "q quit"}`
|
|
691
|
-
));
|
|
692
|
-
const right = view === "home" ? h(Text, { dimColor: true }, "main menu") : h(
|
|
771
|
+
const headerRight = confirm4 ? h(Text, { color: "yellow" }, "unsaved changes") : mode === "menu" ? h(
|
|
693
772
|
Box,
|
|
694
773
|
{},
|
|
695
774
|
h(Text, { dimColor: true }, "scope "),
|
|
696
775
|
h(Text, { bold: true, color: scope === "global" ? "green" : "yellow" }, scope),
|
|
697
|
-
h(Text, { dimColor: true }, "
|
|
776
|
+
h(Text, { dimColor: true }, " (g)"),
|
|
777
|
+
dirty ? h(Text, { color: "yellow" }, " * unsaved") : null
|
|
778
|
+
) : h(Text, { dimColor: true }, mode === "skills" ? "install" : "security");
|
|
779
|
+
const titleBar = h(
|
|
780
|
+
Box,
|
|
781
|
+
{ width: size.columns, paddingX: 1, justifyContent: "space-between" },
|
|
782
|
+
h(Text, { bold: true, color: "cyan" }, "enigma"),
|
|
783
|
+
headerRight
|
|
698
784
|
);
|
|
785
|
+
let content;
|
|
786
|
+
if (confirm4) {
|
|
787
|
+
content = renderConfirm(h, Box, Text, confirm4.index, fill);
|
|
788
|
+
} else if (mode === "security") {
|
|
789
|
+
content = renderChecklist(h, Box, Text, {
|
|
790
|
+
title: "Git security hooks",
|
|
791
|
+
blurb: "Choose what the commit guard enforces, then press enter.",
|
|
792
|
+
items: opts.protections.map((p6) => ({ key: p6.value, label: p6.label, hint: p6.hint })),
|
|
793
|
+
cursor: actCursor,
|
|
794
|
+
checked: actChecked,
|
|
795
|
+
fill
|
|
796
|
+
});
|
|
797
|
+
} else if (mode === "skills") {
|
|
798
|
+
content = renderChecklist(h, Box, Text, {
|
|
799
|
+
title: "Install agent skills",
|
|
800
|
+
blurb: `Scope ${actScope} (g to change). Choose agents, then press enter.`,
|
|
801
|
+
items: opts.agents.map((a) => ({ key: a.name, label: a.label, hint: a.installed ? "detected" : "not detected" })),
|
|
802
|
+
cursor: actCursor,
|
|
803
|
+
checked: actChecked,
|
|
804
|
+
fill
|
|
805
|
+
});
|
|
806
|
+
} else {
|
|
807
|
+
const sidebarWidth = Math.min(28, Math.max(20, Math.floor(size.columns * 0.3)));
|
|
808
|
+
const panel = category ? renderCategoryPanel(h, Box, Text, { category, scope, focusRight, setIndex, valueOf, isModified, fill }) : renderActionPanel(h, Box, Text, current);
|
|
809
|
+
content = h(Box, fill ? { flexGrow: 1 } : {}, renderSidebar(h, Box, Text, sideItems, sideIndex, focusRight, sidebarWidth), panel);
|
|
810
|
+
}
|
|
811
|
+
const footerText = confirm4 ? "up/down move enter select esc cancel" : mode === "security" ? "up/down move space toggle enter apply esc back" : mode === "skills" ? "up/down move space toggle g scope enter install esc back" : `up/down move tab switch ${category ? "enter toggle g scope " : "enter open "}s save x save & exit q quit`;
|
|
812
|
+
const footer = h(Box, { width: size.columns, paddingX: 1 }, h(Text, { dimColor: true }, footerText));
|
|
699
813
|
return h(
|
|
700
814
|
Box,
|
|
701
|
-
{ width: size.columns, height: size.rows, flexDirection: "column" },
|
|
702
|
-
titleBar
|
|
703
|
-
|
|
815
|
+
{ width: size.columns, ...fill ? { height: size.rows } : {}, flexDirection: "column" },
|
|
816
|
+
titleBar,
|
|
817
|
+
content,
|
|
704
818
|
footer
|
|
705
819
|
);
|
|
706
820
|
};
|
|
707
821
|
}
|
|
708
|
-
function
|
|
709
|
-
const items = HOME_ITEMS.map((item, i) => h(
|
|
710
|
-
Box,
|
|
711
|
-
{ key: item.key, flexDirection: "column" },
|
|
712
|
-
h(Text, { inverse: i === homeIndex, bold: i === homeIndex }, ` ${item.label} `),
|
|
713
|
-
i === homeIndex && item.hint ? h(Text, { key: "h", dimColor: true }, ` ${item.hint}`) : null
|
|
714
|
-
));
|
|
822
|
+
function renderSidebar(h, Box, Text, items, index, focusRight, width) {
|
|
715
823
|
return h(Box, {
|
|
716
824
|
flexDirection: "column",
|
|
717
825
|
borderStyle: "round",
|
|
718
|
-
borderColor: "cyan",
|
|
826
|
+
borderColor: focusRight ? "gray" : "cyan",
|
|
719
827
|
paddingX: 1,
|
|
720
|
-
|
|
828
|
+
width,
|
|
829
|
+
marginRight: 1
|
|
721
830
|
}, [
|
|
722
|
-
h(Text, { key: "__t", bold: true,
|
|
723
|
-
|
|
831
|
+
h(Text, { key: "__t", bold: true, dimColor: true }, "MENU"),
|
|
832
|
+
...items.map((it, i) => h(Text, {
|
|
833
|
+
key: String(i),
|
|
834
|
+
inverse: !focusRight && i === index,
|
|
835
|
+
color: i === index ? "cyan" : void 0
|
|
836
|
+
}, ` ${it.title} `))
|
|
724
837
|
]);
|
|
725
838
|
}
|
|
726
|
-
function
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
839
|
+
function renderConfirm(h, Box, Text, index, fill) {
|
|
840
|
+
return h(
|
|
841
|
+
Box,
|
|
842
|
+
{ justifyContent: "center", ...fill ? { flexGrow: 1, alignItems: "center" } : {} },
|
|
843
|
+
h(Box, {
|
|
844
|
+
flexDirection: "column",
|
|
845
|
+
borderStyle: "round",
|
|
846
|
+
borderColor: "yellow",
|
|
847
|
+
paddingX: 2,
|
|
848
|
+
paddingY: 1
|
|
849
|
+
}, [
|
|
850
|
+
h(Text, { key: "__t", bold: true, color: "yellow" }, "You have unsaved changes"),
|
|
851
|
+
h(
|
|
852
|
+
Box,
|
|
853
|
+
{ key: "__o", marginTop: 1, flexDirection: "column" },
|
|
854
|
+
EXIT_OPTIONS.map((o, i) => h(Text, { key: o, inverse: i === index, bold: i === index }, ` ${o} `))
|
|
855
|
+
)
|
|
856
|
+
])
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
function renderChecklist(h, Box, Text, s) {
|
|
860
|
+
const rows = s.items.map((it, i) => {
|
|
861
|
+
const on = !!s.checked[it.key];
|
|
862
|
+
const selected = i === s.cursor;
|
|
863
|
+
return h(
|
|
864
|
+
Box,
|
|
865
|
+
{ key: it.key, justifyContent: "space-between" },
|
|
866
|
+
h(Text, { inverse: selected, bold: selected }, ` ${on ? "[x]" : "[ ]"} ${it.label} `),
|
|
867
|
+
h(Text, { dimColor: true }, `${it.hint} `)
|
|
868
|
+
);
|
|
869
|
+
});
|
|
870
|
+
return h(Box, {
|
|
732
871
|
flexDirection: "column",
|
|
733
872
|
borderStyle: "round",
|
|
734
|
-
borderColor:
|
|
873
|
+
borderColor: "cyan",
|
|
735
874
|
paddingX: 1,
|
|
736
|
-
|
|
737
|
-
marginRight: 1
|
|
875
|
+
...s.fill ? { flexGrow: 1 } : {}
|
|
738
876
|
}, [
|
|
739
|
-
h(Text, { key: "__t", bold: true,
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
color: i === s.catIndex ? "cyan" : void 0
|
|
744
|
-
}, ` ${c.title} `))
|
|
877
|
+
h(Text, { key: "__t", bold: true, color: "cyan" }, s.title),
|
|
878
|
+
h(Text, { key: "__bl", dimColor: true }, s.blurb),
|
|
879
|
+
h(Box, { key: "__rows", marginTop: 1, flexDirection: "column" }, rows),
|
|
880
|
+
...s.fill ? [h(Box, { key: "__grow", flexGrow: 1 })] : []
|
|
745
881
|
]);
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
882
|
+
}
|
|
883
|
+
function renderCategoryPanel(h, Box, Text, s) {
|
|
884
|
+
const focused = s.category.settings[s.setIndex];
|
|
885
|
+
const rows = s.category.settings.map((setting, i) => {
|
|
886
|
+
const on = s.valueOf(setting, s.scope);
|
|
887
|
+
const modified = s.isModified(setting, s.scope);
|
|
888
|
+
const selected = s.focusRight && i === s.setIndex;
|
|
749
889
|
return h(
|
|
750
890
|
Box,
|
|
751
891
|
{ key: setting.key, justifyContent: "space-between" },
|
|
752
892
|
h(Text, { inverse: selected, bold: selected }, ` ${setting.label}${setting.globalOnly ? " (global)" : ""} `),
|
|
753
|
-
h(Text, { bold: true, color: on ? "green" : "gray" }, `${valueLabel(on)} `)
|
|
893
|
+
h(Text, { bold: true, color: modified ? "yellow" : on ? "green" : "gray" }, `${valueLabel(on)}${modified ? " *" : ""} `)
|
|
754
894
|
);
|
|
755
895
|
});
|
|
756
|
-
|
|
896
|
+
return h(Box, {
|
|
757
897
|
flexDirection: "column",
|
|
758
898
|
borderStyle: "round",
|
|
759
|
-
borderColor: s.
|
|
899
|
+
borderColor: s.focusRight ? "cyan" : "gray",
|
|
760
900
|
paddingX: 1,
|
|
761
901
|
flexGrow: 1
|
|
762
902
|
}, [
|
|
763
|
-
h(Text, { key: "__b", bold: true, color: "cyan" }, category.title),
|
|
764
|
-
h(Text, { key: "__bl", dimColor: true }, category.blurb),
|
|
903
|
+
h(Text, { key: "__b", bold: true, color: "cyan" }, s.category.title),
|
|
904
|
+
h(Text, { key: "__bl", dimColor: true }, s.category.blurb),
|
|
765
905
|
h(Box, { key: "__rows", marginTop: 1, flexDirection: "column" }, rows),
|
|
766
|
-
h(Box, { key: "__grow", flexGrow: 1 }),
|
|
767
|
-
h(Text, { key: "__hint", dimColor: true, wrap: "truncate-end" }, focused.hint)
|
|
906
|
+
...s.fill ? [h(Box, { key: "__grow", flexGrow: 1 })] : [],
|
|
907
|
+
h(Text, { key: "__hint", marginTop: 1, dimColor: true, wrap: "truncate-end" }, focused.hint)
|
|
768
908
|
]);
|
|
769
|
-
return h(Box, { flexGrow: 1 }, sidebar, panel);
|
|
770
909
|
}
|
|
771
|
-
|
|
910
|
+
function renderActionPanel(h, Box, Text, item) {
|
|
911
|
+
return h(Box, {
|
|
912
|
+
flexDirection: "column",
|
|
913
|
+
borderStyle: "round",
|
|
914
|
+
borderColor: "gray",
|
|
915
|
+
paddingX: 1,
|
|
916
|
+
flexGrow: 1
|
|
917
|
+
}, [
|
|
918
|
+
h(Text, { key: "__t", bold: true, color: "cyan" }, item.title),
|
|
919
|
+
h(Text, { key: "__bl", dimColor: true }, item.blurb),
|
|
920
|
+
h(Text, { key: "__h", marginTop: 1, dimColor: true }, "Press enter to open")
|
|
921
|
+
]);
|
|
922
|
+
}
|
|
923
|
+
var CLEAR_SCREEN, SETTING_BY_KEY, stageKey, parseStageKey, ACTION_ITEMS, EXIT_OPTIONS;
|
|
772
924
|
var init_settings = __esm({
|
|
773
925
|
"src/tui/settings.ts"() {
|
|
774
926
|
"use strict";
|
|
927
|
+
init_config();
|
|
775
928
|
init_settings_registry();
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
{
|
|
782
|
-
|
|
929
|
+
CLEAR_SCREEN = "\x1B[2J\x1B[H";
|
|
930
|
+
SETTING_BY_KEY = new Map(ALL_SETTINGS.map((s) => [s.key, s]));
|
|
931
|
+
stageKey = (key, scope) => `${scope}/${key}`;
|
|
932
|
+
parseStageKey = (composite) => {
|
|
933
|
+
const i = composite.indexOf("/");
|
|
934
|
+
return { scope: composite.slice(0, i), key: composite.slice(i + 1) };
|
|
935
|
+
};
|
|
936
|
+
ACTION_ITEMS = [
|
|
937
|
+
{ action: "skills", title: "Install agent skills", blurb: "Claude Code, Codex, OpenCode" },
|
|
938
|
+
{ action: "security", title: "Git security hooks", blurb: "block secrets, .env, node_modules on commit" }
|
|
783
939
|
];
|
|
940
|
+
EXIT_OPTIONS = ["Save & exit", "Exit without saving", "Cancel"];
|
|
784
941
|
}
|
|
785
942
|
});
|
|
786
943
|
|
|
@@ -1320,6 +1477,9 @@ async function installSkills(opts, interactive) {
|
|
|
1320
1477
|
}
|
|
1321
1478
|
}
|
|
1322
1479
|
|
|
1480
|
+
// src/cli.ts
|
|
1481
|
+
init_agents();
|
|
1482
|
+
|
|
1323
1483
|
// src/guard.ts
|
|
1324
1484
|
import { readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
1325
1485
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -1725,7 +1885,7 @@ Usage:
|
|
|
1725
1885
|
|
|
1726
1886
|
Commands:
|
|
1727
1887
|
(none) Interactive hub: configure settings or set up features
|
|
1728
|
-
install Install/update agent skills (Claude Code, Codex,
|
|
1888
|
+
install Install/update agent skills (Claude Code, Codex, OpenCode)
|
|
1729
1889
|
security Set up git security hooks in the current repo
|
|
1730
1890
|
guard [--all] Run the commit guard (staged files, or --all for every tracked file)
|
|
1731
1891
|
config [key val] Configure settings: no args opens the interactive menu;
|
|
@@ -1734,7 +1894,7 @@ Commands:
|
|
|
1734
1894
|
check Integrity gate: verify skills are well-formed and sealed
|
|
1735
1895
|
help, version
|
|
1736
1896
|
|
|
1737
|
-
Config keys: commit-emoji, update-notifier, claude-attribution,
|
|
1897
|
+
Config keys: commit-emoji, update-notifier, fullscreen, claude-attribution,
|
|
1738
1898
|
bypass-claude, bypass-codex, bypass-opencode
|
|
1739
1899
|
|
|
1740
1900
|
Install options:
|
|
@@ -1807,15 +1967,19 @@ async function run(argv) {
|
|
|
1807
1967
|
return;
|
|
1808
1968
|
}
|
|
1809
1969
|
const { runHomeTui: runHomeTui2 } = await Promise.resolve().then(() => (init_settings(), settings_exports));
|
|
1810
|
-
await runHomeTui2(
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1970
|
+
await runHomeTui2({
|
|
1971
|
+
agents: discoverAgents().map((a) => ({ name: a.name, label: a.label, installed: a.installed })),
|
|
1972
|
+
protections: GUARD_PROTECTIONS,
|
|
1973
|
+
runAction: async (intent) => {
|
|
1974
|
+
if (intent.action === "skills") {
|
|
1975
|
+
p5.intro("enigma - install agent skills");
|
|
1976
|
+
await installSkills({ ...opts, scope: intent.scope ?? opts.scope, agents: intent.agents ?? [], allAgents: !(intent.agents && intent.agents.length) }, false);
|
|
1977
|
+
p5.outro("Done.");
|
|
1978
|
+
} else if (intent.action === "security") {
|
|
1979
|
+
p5.intro("enigma - git security hooks");
|
|
1980
|
+
const done = await setupGitHooks({ ...opts, protections: intent.protections, force: true }, false);
|
|
1981
|
+
p5.outro(done ? "Git hooks configured." : "No changes made.");
|
|
1982
|
+
}
|
|
1819
1983
|
}
|
|
1820
1984
|
});
|
|
1821
1985
|
await notifyUpdate(version, interactive);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "enigma-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.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": {
|