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.
- 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 +226 -91
- 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.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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
6
|
+
"cliVersion": "1.1.0",
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
4
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
5
5
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6
|
-
import * as
|
|
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
|
|
33
|
-
import { dirname as dirname2, join as
|
|
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
|
|
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 =
|
|
265
|
-
var SKILLS_ROOT =
|
|
266
|
-
var MEMORY_ROOT =
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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(
|
|
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(
|
|
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 =
|
|
349
|
-
return
|
|
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 =
|
|
360
|
-
if (!isDir(dir) || !
|
|
361
|
-
const metaPath =
|
|
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
|
-
|
|
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 =
|
|
385
|
-
if (!isDir(dir) || !
|
|
503
|
+
const dir = join6(SKILLS_ROOT, name);
|
|
504
|
+
if (!isDir(dir) || !existsSync5(join6(dir, "SKILL.md"))) continue;
|
|
386
505
|
checked++;
|
|
387
|
-
const md =
|
|
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 =
|
|
395
|
-
if (!
|
|
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
|
-
|
|
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
|
|
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 (
|
|
435
|
-
|
|
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)
|
|
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
|
|
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 (
|
|
459
|
-
|
|
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
|
-
|
|
586
|
+
p3.log.warn("No installed agents detected; defaulting to all supported agents.");
|
|
468
587
|
}
|
|
469
588
|
if (chosenAgents.length === 0) {
|
|
470
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
504
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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 (
|
|
530
|
-
|
|
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,
|
|
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
|
-
|
|
705
|
+
p3.note(lines.join("\n"), "Nothing to do");
|
|
585
706
|
applyClaudeConfig();
|
|
707
|
+
applyBypassConfig();
|
|
586
708
|
await maybeOfferGitHooks(interactive, opts);
|
|
587
|
-
|
|
709
|
+
p3.log.success(`Everything up-to-date - ${nSkip} item(s) unchanged${nKept ? `, ${nKept} kept modified` : ""} (${scope}).`);
|
|
588
710
|
return;
|
|
589
711
|
}
|
|
590
|
-
|
|
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
|
|
599
|
-
if (
|
|
600
|
-
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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
|
-
|
|
617
|
-
|
|
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,
|
|
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,
|
|
625
|
-
cpSync2(m.src,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
801
|
-
import { join as
|
|
802
|
-
import { existsSync as
|
|
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" ?
|
|
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 =
|
|
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 =
|
|
834
|
-
if (!isDir(dir))
|
|
835
|
-
|
|
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(
|
|
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
|
-
|
|
1163
|
+
p4.intro("enigma - install agent skills");
|
|
1029
1164
|
await installSkills(opts, interactive);
|
|
1030
|
-
|
|
1165
|
+
p4.outro("Done.");
|
|
1031
1166
|
return;
|
|
1032
1167
|
}
|
|
1033
1168
|
if (opts.command === "security") {
|
|
1034
|
-
|
|
1169
|
+
p4.intro("enigma - git security hooks");
|
|
1035
1170
|
const done = await setupGitHooks(opts, interactive);
|
|
1036
|
-
|
|
1171
|
+
p4.outro(done ? "Git hooks configured." : "No changes made.");
|
|
1037
1172
|
return;
|
|
1038
1173
|
}
|
|
1039
|
-
|
|
1174
|
+
p4.intro("enigma");
|
|
1040
1175
|
let features;
|
|
1041
1176
|
if (interactive) {
|
|
1042
|
-
const r = await
|
|
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 (
|
|
1052
|
-
|
|
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
|
-
|
|
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.
|
|
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": {
|